aboutsummaryrefslogtreecommitdiff
path: root/src/lib
diff options
context:
space:
mode:
Diffstat (limited to 'src/lib')
-rw-r--r--src/lib/Makefile.am14
-rw-r--r--src/lib/block/Makefile.am31
-rw-r--r--src/lib/block/bg_bf.c286
-rw-r--r--src/lib/block/block.c378
-rw-r--r--src/lib/block/meson.build24
-rw-r--r--src/lib/curl/Makefile.am38
-rw-r--r--src/lib/curl/curl.c907
-rw-r--r--src/lib/curl/curl_reschedule.c181
-rw-r--r--src/lib/curl/meson.build13
-rw-r--r--src/lib/gnsrecord/.gitignore6
-rw-r--r--src/lib/gnsrecord/Makefile.am111
-rw-r--r--src/lib/gnsrecord/gnsrecord.c267
-rw-r--r--src/lib/gnsrecord/gnsrecord_crypto.c1091
-rw-r--r--src/lib/gnsrecord/gnsrecord_crypto.h94
-rw-r--r--src/lib/gnsrecord/gnsrecord_misc.c566
-rw-r--r--src/lib/gnsrecord/gnsrecord_pow.c462
-rw-r--r--src/lib/gnsrecord/gnsrecord_serialization.c302
-rw-r--r--src/lib/gnsrecord/gnunet-gnsrecord-tvg.c539
-rw-r--r--src/lib/gnsrecord/json_gnsrecord.c397
-rw-r--r--src/lib/gnsrecord/meson.build86
-rw-r--r--src/lib/gnsrecord/perf_gnsrecord_crypto.c139
-rw-r--r--src/lib/gnsrecord/test_gnsrecord_block_expiration.c118
-rw-r--r--src/lib/gnsrecord/test_gnsrecord_crypto.c211
-rw-r--r--src/lib/gnsrecord/test_gnsrecord_serialization.c156
-rw-r--r--src/lib/gnsrecord/test_gnsrecord_testvectors.c716
-rw-r--r--src/lib/hello/.gitignore5
-rw-r--r--src/lib/hello/Makefile.am33
-rw-r--r--src/lib/hello/gnunet-hello.c426
-rw-r--r--src/lib/hello/hello-ng.c198
-rw-r--r--src/lib/hello/hello-uri.c1045
-rw-r--r--src/lib/hello/meson.build27
-rw-r--r--src/lib/hello/test_hello-uri.c215
-rw-r--r--src/lib/json/.gitignore2
-rw-r--r--src/lib/json/Makefile.am57
-rw-r--r--src/lib/json/json.c161
-rw-r--r--src/lib/json/json_generator.c207
-rw-r--r--src/lib/json/json_helper.c1611
-rw-r--r--src/lib/json/json_mhd.c379
-rw-r--r--src/lib/json/json_pack.c480
-rw-r--r--src/lib/json/meson.build44
-rw-r--r--src/lib/json/test_json.c247
-rw-r--r--src/lib/json/test_json_mhd.c193
-rw-r--r--src/lib/meson.build9
-rw-r--r--src/lib/pq/.gitignore1
-rw-r--r--src/lib/pq/Makefile.am51
-rw-r--r--src/lib/pq/meson.build32
-rw-r--r--src/lib/pq/pq.c207
-rw-r--r--src/lib/pq/pq.h172
-rw-r--r--src/lib/pq/pq_connect.c705
-rw-r--r--src/lib/pq/pq_eval.c248
-rw-r--r--src/lib/pq/pq_event.c584
-rw-r--r--src/lib/pq/pq_exec.c95
-rw-r--r--src/lib/pq/pq_prepare.c125
-rw-r--r--src/lib/pq/pq_query_helper.c1585
-rw-r--r--src/lib/pq/pq_result_helper.c2067
-rw-r--r--src/lib/pq/test_pq.c630
-rw-r--r--src/lib/pq/versioning.sql298
-rw-r--r--src/lib/sq/.gitignore1
-rw-r--r--src/lib/sq/Makefile.am38
-rw-r--r--src/lib/sq/meson.build30
-rw-r--r--src/lib/sq/sq.c131
-rw-r--r--src/lib/sq/sq_exec.c113
-rw-r--r--src/lib/sq/sq_prepare.c77
-rw-r--r--src/lib/sq/sq_query_helper.c510
-rw-r--r--src/lib/sq/sq_result_helper.c785
-rw-r--r--src/lib/sq/test_sq.c291
-rw-r--r--src/lib/testing/.gitignore9
-rw-r--r--src/lib/testing/Makefile.am43
-rw-r--r--src/lib/testing/barrier.c51
-rw-r--r--src/lib/testing/barrier.h77
-rw-r--r--src/lib/testing/gnunet-cmds-helper.c527
-rw-r--r--src/lib/testing/meson.build62
-rw-r--r--src/lib/testing/netjail.c1085
-rw-r--r--src/lib/testing/netjail.h358
-rw-r--r--src/lib/testing/testing.conf11
-rw-r--r--src/lib/testing/testing_api_cmd_barrier.c106
-rw-r--r--src/lib/testing/testing_api_cmd_barrier_reached.c182
-rw-r--r--src/lib/testing/testing_api_cmd_batch.c210
-rw-r--r--src/lib/testing/testing_api_cmd_batch.h61
-rw-r--r--src/lib/testing/testing_api_cmd_exec.c180
-rw-r--r--src/lib/testing/testing_api_cmd_finish.c215
-rw-r--r--src/lib/testing/testing_api_cmd_get_topo_from_file.c161
-rw-r--r--src/lib/testing/testing_api_cmd_get_topo_from_string.c119
-rw-r--r--src/lib/testing/testing_api_cmd_netjail_start.c223
-rw-r--r--src/lib/testing/testing_api_cmd_netjail_start_cmds_helper.c555
-rw-r--r--src/lib/testing/testing_api_loop.c901
-rw-r--r--src/lib/testing/testing_api_loop.h141
-rw-r--r--src/lib/testing/testing_api_traits.c87
-rw-r--r--src/lib/testing/testing_cmds.h106
-rw-r--r--src/lib/util/.gitignore82
-rw-r--r--src/lib/util/Makefile.am608
-rw-r--r--src/lib/util/bandwidth.c515
-rw-r--r--src/lib/util/benchmark.c294
-rw-r--r--src/lib/util/benchmark.h174
-rw-r--r--src/lib/util/bio.c1380
-rw-r--r--src/lib/util/buffer.c283
-rw-r--r--src/lib/util/child_management.c240
-rwxr-xr-xsrc/lib/util/child_management_test.sh2
-rw-r--r--src/lib/util/client.c1112
-rw-r--r--src/lib/util/common_allocation.c437
-rw-r--r--src/lib/util/common_endian.c95
-rw-r--r--src/lib/util/common_logging.c1544
-rw-r--r--src/lib/util/compress.c91
-rw-r--r--src/lib/util/configuration.c2576
-rw-r--r--src/lib/util/configuration_helper.c303
-rw-r--r--src/lib/util/consttime_memcmp.c280
-rw-r--r--src/lib/util/container_bloomfilter.c806
-rw-r--r--src/lib/util/container_heap.c501
-rw-r--r--src/lib/util/container_multihashmap.c974
-rw-r--r--src/lib/util/container_multihashmap32.c606
-rw-r--r--src/lib/util/container_multipeermap.c895
-rw-r--r--src/lib/util/container_multishortmap.c904
-rw-r--r--src/lib/util/container_multiuuidmap.c902
-rw-r--r--src/lib/util/crypto_blind_sign.c712
-rw-r--r--src/lib/util/crypto_crc.c173
-rw-r--r--src/lib/util/crypto_cs.c366
-rw-r--r--src/lib/util/crypto_ecc.c971
-rw-r--r--src/lib/util/crypto_ecc_dlog.c336
-rw-r--r--src/lib/util/crypto_ecc_gnsrecord.c451
-rw-r--r--src/lib/util/crypto_ecc_setup.c351
-rw-r--r--src/lib/util/crypto_edx25519.c354
-rw-r--r--src/lib/util/crypto_elligator.c674
-rw-r--r--src/lib/util/crypto_hash.c415
-rw-r--r--src/lib/util/crypto_hash_file.c236
-rw-r--r--src/lib/util/crypto_hkdf.c369
-rw-r--r--src/lib/util/crypto_kdf.c145
-rw-r--r--src/lib/util/crypto_mpi.c178
-rw-r--r--src/lib/util/crypto_paillier.c463
-rw-r--r--src/lib/util/crypto_pkey.c639
-rw-r--r--src/lib/util/crypto_pow.c60
-rw-r--r--src/lib/util/crypto_random.c412
-rw-r--r--src/lib/util/crypto_rsa.c1290
-rw-r--r--src/lib/util/crypto_symmetric.c254
-rw-r--r--src/lib/util/disk.c1689
-rw-r--r--src/lib/util/disk.h44
-rw-r--r--src/lib/util/dnsparser.c1535
-rw-r--r--src/lib/util/dnsstub.c721
-rw-r--r--src/lib/util/getopt.c1015
-rw-r--r--src/lib/util/getopt_helpers.c1017
-rw-r--r--src/lib/util/gnunet_error_codes.c262
-rw-r--r--src/lib/util/helper.c672
-rw-r--r--src/lib/util/load.c257
-rw-r--r--src/lib/util/meson.build669
-rw-r--r--src/lib/util/mq.c1030
-rw-r--r--src/lib/util/mst.c411
-rw-r--r--src/lib/util/nc.c228
-rw-r--r--src/lib/util/network.c1311
-rw-r--r--src/lib/util/nt.c438
-rw-r--r--src/lib/util/op.c335
-rw-r--r--src/lib/util/os_installation.c829
-rw-r--r--src/lib/util/os_network.c444
-rw-r--r--src/lib/util/os_priority.c1063
-rw-r--r--src/lib/util/peer.c242
-rw-r--r--src/lib/util/perf_crypto_asymmetric.c133
-rw-r--r--src/lib/util/perf_crypto_cs.c184
-rw-r--r--src/lib/util/perf_crypto_ecc_dlog.c184
-rw-r--r--src/lib/util/perf_crypto_hash.c115
-rw-r--r--src/lib/util/perf_crypto_paillier.c96
-rw-r--r--src/lib/util/perf_crypto_rsa.c211
-rw-r--r--src/lib/util/perf_crypto_symmetric.c78
-rw-r--r--src/lib/util/perf_malloc.c98
-rw-r--r--src/lib/util/perf_mq.c301
-rw-r--r--src/lib/util/perf_scheduler.c104
-rw-r--r--src/lib/util/plugin.c403
-rw-r--r--src/lib/util/proc_compat.c50
-rw-r--r--src/lib/util/program.c454
-rw-r--r--src/lib/util/regex.c804
-rw-r--r--src/lib/util/resolver.conf20
-rw-r--r--src/lib/util/resolver.h93
-rw-r--r--src/lib/util/resolver_api.c1295
-rw-r--r--src/lib/util/scheduler.c2583
-rw-r--r--src/lib/util/service.c2572
-rw-r--r--src/lib/util/signal.c110
-rw-r--r--src/lib/util/socks.c690
-rw-r--r--src/lib/util/speedup.c114
-rw-r--r--src/lib/util/speedup.h45
-rw-r--r--src/lib/util/strings.c2077
-rw-r--r--src/lib/util/test_bio.c383
-rw-r--r--src/lib/util/test_child_management.c178
-rw-r--r--src/lib/util/test_client.c197
-rw-r--r--src/lib/util/test_client_data.conf3
-rw-r--r--src/lib/util/test_client_unix.conf6
-rw-r--r--src/lib/util/test_common_allocation.c179
-rw-r--r--src/lib/util/test_common_endian.c44
-rw-r--r--src/lib/util/test_common_logging.c100
-rw-r--r--src/lib/util/test_common_logging_dummy.c123
-rw-r--r--src/lib/util/test_common_logging_runtime_loglevels.c457
-rw-r--r--src/lib/util/test_configuration.c581
-rw-r--r--src/lib/util/test_configuration_data.conf30
-rw-r--r--src/lib/util/test_container_bloomfilter.c302
-rw-r--r--src/lib/util/test_container_dll.c114
-rw-r--r--src/lib/util/test_container_heap.c294
-rw-r--r--src/lib/util/test_container_multihashmap.c140
-rw-r--r--src/lib/util/test_container_multihashmap32.c110
-rw-r--r--src/lib/util/test_container_multipeermap.c140
-rw-r--r--src/lib/util/test_crypto_blind.c87
-rw-r--r--src/lib/util/test_crypto_crc.c221
-rw-r--r--src/lib/util/test_crypto_cs.c621
-rw-r--r--src/lib/util/test_crypto_ecc_dlog.c219
-rw-r--r--src/lib/util/test_crypto_ecdh_ecdsa.c95
-rw-r--r--src/lib/util/test_crypto_ecdh_eddsa.c96
-rw-r--r--src/lib/util/test_crypto_ecdhe.c71
-rw-r--r--src/lib/util/test_crypto_ecdsa.c282
-rw-r--r--src/lib/util/test_crypto_eddsa.c319
-rw-r--r--src/lib/util/test_crypto_edx25519.c327
-rw-r--r--src/lib/util/test_crypto_elligator.c302
-rw-r--r--src/lib/util/test_crypto_hash.c218
-rw-r--r--src/lib/util/test_crypto_hash_context.c50
-rw-r--r--src/lib/util/test_crypto_hkdf.c339
-rw-r--r--src/lib/util/test_crypto_kdf.c72
-rw-r--r--src/lib/util/test_crypto_paillier.c248
-rw-r--r--src/lib/util/test_crypto_random.c74
-rw-r--r--src/lib/util/test_crypto_rsa.c171
-rw-r--r--src/lib/util/test_crypto_symmetric.c176
-rw-r--r--src/lib/util/test_disk.c291
-rw-r--r--src/lib/util/test_getopt.c183
-rw-r--r--src/lib/util/test_hexcoder.c56
-rw-r--r--src/lib/util/test_mq.c342
-rw-r--r--src/lib/util/test_os_network.c93
-rw-r--r--src/lib/util/test_os_start_process.c290
-rw-r--r--src/lib/util/test_peer.c140
-rw-r--r--src/lib/util/test_plugin.c88
-rw-r--r--src/lib/util/test_plugin_plug.c46
-rw-r--r--src/lib/util/test_program.c139
-rw-r--r--src/lib/util/test_program_data.conf2
-rw-r--r--src/lib/util/test_regex.c194
-rw-r--r--src/lib/util/test_scheduler.c294
-rw-r--r--src/lib/util/test_scheduler_delay.c96
-rw-r--r--src/lib/util/test_scheduler_hogging_cancel.c51
-rw-r--r--src/lib/util/test_scheduler_hogging_priority.c55
-rw-r--r--src/lib/util/test_service.c239
-rw-r--r--src/lib/util/test_service_data.conf28
-rw-r--r--src/lib/util/test_socks.c258
-rw-r--r--src/lib/util/test_speedup.c127
-rw-r--r--src/lib/util/test_speedup_data.conf3
-rw-r--r--src/lib/util/test_strings.c174
-rw-r--r--src/lib/util/test_strings_to_data.c66
-rw-r--r--src/lib/util/test_time.c265
-rw-r--r--src/lib/util/test_tun.c77
-rw-r--r--src/lib/util/test_uri.c838
-rw-r--r--src/lib/util/time.c991
-rw-r--r--src/lib/util/tun.c281
-rw-r--r--src/lib/util/uri.c345
-rw-r--r--src/lib/util/util.conf79
-rw-r--r--src/lib/util/util.supp11
245 files changed, 91708 insertions, 0 deletions
diff --git a/src/lib/Makefile.am b/src/lib/Makefile.am
new file mode 100644
index 000000000..89a34b299
--- /dev/null
+++ b/src/lib/Makefile.am
@@ -0,0 +1,14 @@
1if HAVE_POSTGRESQL
2 POSTGRES_DIR = pq
3endif
4
5SUBDIRS = \
6 util \
7 curl \
8 json \
9 sq \
10 $(POSTGRES_DIR) \
11 hello \
12 block \
13 gnsrecord \
14 testing
diff --git a/src/lib/block/Makefile.am b/src/lib/block/Makefile.am
new file mode 100644
index 000000000..f9a5a0347
--- /dev/null
+++ b/src/lib/block/Makefile.am
@@ -0,0 +1,31 @@
1# This Makefile.am is in the public domain
2AM_CPPFLAGS = -I$(top_srcdir)/src/include
3
4plugindir = $(libdir)/gnunet
5
6if USE_COVERAGE
7 AM_CFLAGS = --coverage
8endif
9
10lib_LTLIBRARIES = \
11 libgnunetblock.la \
12 libgnunetblockgroup.la
13
14libgnunetblock_la_SOURCES = \
15 block.c
16libgnunetblock_la_LIBADD = \
17 $(top_builddir)/src/lib/util/libgnunetutil.la
18libgnunetblock_la_LDFLAGS = \
19 $(GN_LIB_LDFLAGS) \
20 $(GN_LIBINTL) \
21 -version-info 0:0:0
22
23libgnunetblockgroup_la_SOURCES = \
24 bg_bf.c
25libgnunetblockgroup_la_LIBADD = \
26 libgnunetblock.la \
27 $(top_builddir)/src/lib/util/libgnunetutil.la
28libgnunetblockgroup_la_LDFLAGS = \
29 $(GN_LIB_LDFLAGS) \
30 $(GN_LIBINTL) \
31 -version-info 0:0:0
diff --git a/src/lib/block/bg_bf.c b/src/lib/block/bg_bf.c
new file mode 100644
index 000000000..c8269498e
--- /dev/null
+++ b/src/lib/block/bg_bf.c
@@ -0,0 +1,286 @@
1/*
2 This file is part of GNUnet
3 Copyright (C) 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 block/bg_bf.c
22 * @brief implementation of a block group using a Bloom filter
23 * to drop duplicate blocks
24 * @author Christian Grothoff
25 */
26#include "platform.h"
27#include "gnunet_util_lib.h"
28#include "gnunet_block_group_lib.h"
29#include "gnunet_block_plugin.h"
30
31
32/**
33 * Internal data structure for a block group.
34 */
35struct BfGroupInternals
36{
37 /**
38 * A Bloom filter to weed out duplicate replies probabilistically.
39 */
40 struct GNUNET_CONTAINER_BloomFilter *bf;
41
42 /**
43 * Set from the nonce to mingle the hashes before going into the @e bf.
44 */
45 uint32_t bf_mutator;
46
47 /**
48 * Size of @a bf.
49 */
50 uint32_t bf_size;
51};
52
53
54/**
55 * Serialize state of a block group.
56 *
57 * @param bg group to serialize
58 * @param[out] raw_data set to the serialized state
59 * @param[out] raw_data_size set to the number of bytes in @a raw_data
60 * @return #GNUNET_OK on success, #GNUNET_NO if serialization is not
61 * supported, #GNUNET_SYSERR on error
62 */
63static enum GNUNET_GenericReturnValue
64bf_group_serialize_cb (struct GNUNET_BLOCK_Group *bg,
65 void **raw_data,
66 size_t *raw_data_size)
67{
68 struct BfGroupInternals *gi = bg->internal_cls;
69 void *raw;
70
71 raw = GNUNET_malloc (gi->bf_size + sizeof (uint32_t));
72 if (GNUNET_OK !=
73 GNUNET_CONTAINER_bloomfilter_get_raw_data (gi->bf,
74 raw + sizeof (uint32_t),
75 gi->bf_size))
76 {
77 GNUNET_free (raw);
78 GNUNET_break (0);
79 return GNUNET_SYSERR;
80 }
81 memcpy (raw,
82 &gi->bf_mutator,
83 sizeof (uint32_t));
84 *raw_data = raw;
85 *raw_data_size = gi->bf_size + sizeof (uint32_t);
86 return GNUNET_OK;
87}
88
89
90/**
91 * Mark elements as "seen" using a hash of the element. Not supported
92 * by all block plugins.
93 *
94 * @param bg group to update
95 * @param seen_results results already seen
96 * @param seen_results_count number of entries in @a seen_results
97 */
98static void
99bf_group_mark_seen_cb (struct GNUNET_BLOCK_Group *bg,
100 const struct GNUNET_HashCode *seen_results,
101 unsigned int seen_results_count)
102{
103 struct BfGroupInternals *gi = bg->internal_cls;
104
105 for (unsigned int i = 0; i < seen_results_count; i++)
106 {
107 struct GNUNET_HashCode mhash;
108
109 GNUNET_BLOCK_mingle_hash (&seen_results[i],
110 gi->bf_mutator,
111 &mhash);
112 GNUNET_CONTAINER_bloomfilter_add (gi->bf,
113 &mhash);
114 }
115}
116
117
118/**
119 * Merge two groups, if possible. Not supported by all block plugins,
120 * can also fail if the nonces were different.
121 *
122 * @param bg1 group to update
123 * @param bg2 group to merge into @a bg1
124 * @return #GNUNET_OK on success, #GNUNET_NO if the nonces were different and thus
125 * we failed.
126 */
127static enum GNUNET_GenericReturnValue
128bf_group_merge_cb (struct GNUNET_BLOCK_Group *bg1,
129 const struct GNUNET_BLOCK_Group *bg2)
130{
131 struct BfGroupInternals *gi1 = bg1->internal_cls;
132 struct BfGroupInternals *gi2 = bg2->internal_cls;
133
134 if (gi1->bf_mutator != gi2->bf_mutator)
135 return GNUNET_NO;
136 if (gi1->bf_size != gi2->bf_size)
137 return GNUNET_NO;
138 GNUNET_CONTAINER_bloomfilter_or2 (gi1->bf,
139 gi2->bf);
140 return GNUNET_OK;
141}
142
143
144/**
145 * Destroy resources used by a block group.
146 *
147 * @param bg group to destroy, NULL is allowed
148 */
149static void
150bf_group_destroy_cb (struct GNUNET_BLOCK_Group *bg)
151{
152 struct BfGroupInternals *gi = bg->internal_cls;
153
154 GNUNET_CONTAINER_bloomfilter_free (gi->bf);
155 GNUNET_free (gi);
156 GNUNET_free (bg);
157}
158
159
160/**
161 * Create a new block group that filters duplicates using a Bloom filter.
162 *
163 * @param ctx block context in which the block group is created
164 * @param bf_size size of the Bloom filter
165 * @param bf_k K-value for the Bloom filter
166 * @param type block type
167 * @param raw_data optional serialized prior state of the group, NULL if unavailable/fresh
168 * @param raw_data_size number of bytes in @a raw_data, 0 if unavailable/fresh
169 * @return block group handle, NULL if block groups are not supported
170 * by this @a type of block (this is not an error)
171 */
172struct GNUNET_BLOCK_Group *
173GNUNET_BLOCK_GROUP_bf_create (void *cls,
174 size_t bf_size,
175 unsigned int bf_k,
176 enum GNUNET_BLOCK_Type type,
177 const void *raw_data,
178 size_t raw_data_size)
179{
180 struct BfGroupInternals *gi;
181 struct GNUNET_BLOCK_Group *bg;
182 uint32_t nonce;
183
184 if ( (NULL != raw_data) &&
185 (raw_data_size < sizeof (nonce)) )
186 {
187 GNUNET_break_op (0);
188 return NULL;
189 }
190 if (NULL != raw_data)
191 {
192 memcpy (&nonce,
193 raw_data,
194 sizeof (nonce));
195 raw_data += sizeof (nonce);
196 raw_data_size -= sizeof (nonce);
197 }
198 else
199 {
200 nonce = GNUNET_CRYPTO_random_u32 (GNUNET_CRYPTO_QUALITY_NONCE,
201 UINT32_MAX);
202 }
203 gi = GNUNET_new (struct BfGroupInternals);
204 gi->bf = GNUNET_CONTAINER_bloomfilter_init ((bf_size != raw_data_size) ?
205 NULL : raw_data,
206 bf_size,
207 bf_k);
208 gi->bf_mutator = nonce;
209 gi->bf_size = bf_size;
210 bg = GNUNET_new (struct GNUNET_BLOCK_Group);
211 bg->type = type;
212 bg->serialize_cb = &bf_group_serialize_cb;
213 bg->mark_seen_cb = &bf_group_mark_seen_cb;
214 bg->merge_cb = &bf_group_merge_cb;
215 bg->destroy_cb = &bf_group_destroy_cb;
216 bg->internal_cls = gi;
217 return bg;
218}
219
220
221/**
222 * Test if @a hc is contained in the Bloom filter of @a bg. If so,
223 * return #GNUNET_YES. If not, add @a hc to the Bloom filter and
224 * return #GNUNET_NO.
225 *
226 * @param bg block group to use for testing
227 * @param hc hash of element to evaluate
228 * @return #GNUNET_YES if @a hc is (likely) a duplicate
229 * #GNUNET_NO if @a hc was definitively not in @a bg (but now is)
230 */
231enum GNUNET_GenericReturnValue
232GNUNET_BLOCK_GROUP_bf_test_and_set (struct GNUNET_BLOCK_Group *bg,
233 const struct GNUNET_HashCode *hc)
234{
235 struct BfGroupInternals *gi;
236 struct GNUNET_HashCode mhash;
237
238 if (NULL == bg)
239 return GNUNET_NO;
240 gi = bg->internal_cls;
241 GNUNET_BLOCK_mingle_hash (hc,
242 gi->bf_mutator,
243 &mhash);
244 if (GNUNET_YES ==
245 GNUNET_CONTAINER_bloomfilter_test (gi->bf,
246 &mhash))
247 return GNUNET_YES;
248 GNUNET_CONTAINER_bloomfilter_add (gi->bf,
249 &mhash);
250 return GNUNET_NO;
251}
252
253
254/**
255 * How many bytes should a bloomfilter be if we have already seen
256 * entry_count responses? Sized so that do not have to
257 * re-size the filter too often (to keep it cheap).
258 *
259 * Since other peers will also add entries but not resize the filter,
260 * we should generally pick a slightly larger size than what the
261 * strict math would suggest.
262 *
263 * @param entry_count expected number of entries in the Bloom filter
264 * @param k number of bits set per entry
265 * @return must be a power of two and smaller or equal to 2^15.
266 */
267size_t
268GNUNET_BLOCK_GROUP_compute_bloomfilter_size (unsigned int entry_count,
269 unsigned int k)
270{
271 size_t size;
272 unsigned int ideal = (entry_count * k) / 4;
273 uint16_t max = 1 << 15;
274
275 if (entry_count > max)
276 return max;
277 size = 8;
278 while ((size < max) && (size < ideal))
279 size *= 2;
280 if (size > max)
281 return max;
282 return size;
283}
284
285
286/* end of bg_bf.c */
diff --git a/src/lib/block/block.c b/src/lib/block/block.c
new file mode 100644
index 000000000..9edc7ce5e
--- /dev/null
+++ b/src/lib/block/block.c
@@ -0,0 +1,378 @@
1/*
2 This file is part of GNUnet.
3 Copyright (C) 2010, 2017, 2021, 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 * @file block/block.c
23 * @brief library for data block manipulation
24 * @author Christian Grothoff
25 */
26#include "platform.h"
27#include "gnunet_util_lib.h"
28#include "gnunet_constants.h"
29#include "gnunet_signatures.h"
30#include "gnunet_block_lib.h"
31#include "gnunet_block_plugin.h"
32
33
34/**
35 * Handle for a plugin.
36 */
37struct Plugin
38{
39 /**
40 * Name of the shared library.
41 */
42 char *library_name;
43
44 /**
45 * Plugin API.
46 */
47 struct GNUNET_BLOCK_PluginFunctions *api;
48};
49
50
51/**
52 * Handle to an initialized block library.
53 */
54struct GNUNET_BLOCK_Context
55{
56 /**
57 * Array of our plugins.
58 */
59 struct Plugin **plugins;
60
61 /**
62 * Size of the 'plugins' array.
63 */
64 unsigned int num_plugins;
65
66 /**
67 * Our configuration.
68 */
69 const struct GNUNET_CONFIGURATION_Handle *cfg;
70};
71
72
73GNUNET_NETWORK_STRUCT_BEGIN
74
75
76/**
77 * Serialization to use in #GNUNET_BLOCK_mingle_hash.
78 */
79struct MinglePacker
80{
81 /**
82 * Original hash.
83 */
84 struct GNUNET_HashCode in;
85
86 /**
87 * Mingle value.
88 */
89 uint32_t mingle GNUNET_PACKED;
90};
91
92GNUNET_NETWORK_STRUCT_END
93
94
95void
96GNUNET_BLOCK_mingle_hash (const struct GNUNET_HashCode *in,
97 uint32_t mingle_number,
98 struct GNUNET_HashCode *hc)
99{
100 struct MinglePacker mp = {
101 .in = *in,
102 .mingle = mingle_number
103 };
104
105 GNUNET_CRYPTO_hash (&mp,
106 sizeof(mp),
107 hc);
108}
109
110
111/**
112 * Add a plugin to the list managed by the block library.
113 *
114 * @param cls the block context
115 * @param library_name name of the plugin
116 * @param lib_ret the plugin API
117 */
118static void
119add_plugin (void *cls,
120 const char *library_name,
121 void *lib_ret)
122{
123 struct GNUNET_BLOCK_Context *ctx = cls;
124 struct GNUNET_BLOCK_PluginFunctions *api = lib_ret;
125 struct Plugin *plugin;
126
127 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
128 "Loading block plugin `%s'\n",
129 library_name);
130 plugin = GNUNET_new (struct Plugin);
131 plugin->api = api;
132 plugin->library_name = GNUNET_strdup (library_name);
133 GNUNET_array_append (ctx->plugins,
134 ctx->num_plugins,
135 plugin);
136}
137
138
139struct GNUNET_BLOCK_Context *
140GNUNET_BLOCK_context_create (const struct GNUNET_CONFIGURATION_Handle *cfg)
141{
142 struct GNUNET_BLOCK_Context *ctx;
143
144 ctx = GNUNET_new (struct GNUNET_BLOCK_Context);
145 ctx->cfg = cfg;
146 GNUNET_PLUGIN_load_all_in_context (GNUNET_OS_project_data_default (),
147 "libgnunet_plugin_block_",
148 (void *) cfg,
149 &add_plugin,
150 ctx);
151 return ctx;
152}
153
154
155void
156GNUNET_BLOCK_context_destroy (struct GNUNET_BLOCK_Context *ctx)
157{
158 struct Plugin *plugin;
159
160 for (unsigned int i = 0; i < ctx->num_plugins; i++)
161 {
162 plugin = ctx->plugins[i];
163 GNUNET_break (NULL ==
164 GNUNET_PLUGIN_unload (plugin->library_name,
165 plugin->api));
166 GNUNET_free (plugin->library_name);
167 GNUNET_free (plugin);
168 }
169 GNUNET_free (ctx->plugins);
170 GNUNET_free (ctx);
171}
172
173
174enum GNUNET_GenericReturnValue
175GNUNET_BLOCK_group_serialize (struct GNUNET_BLOCK_Group *bg,
176 void **raw_data,
177 size_t *raw_data_size)
178{
179 *raw_data = NULL;
180 *raw_data_size = 0;
181 if (NULL == bg)
182 return GNUNET_NO;
183 if (NULL == bg->serialize_cb)
184 return GNUNET_NO;
185 return bg->serialize_cb (bg,
186 raw_data,
187 raw_data_size);
188}
189
190
191void
192GNUNET_BLOCK_group_destroy (struct GNUNET_BLOCK_Group *bg)
193{
194 if (NULL == bg)
195 return;
196 bg->destroy_cb (bg);
197}
198
199
200enum GNUNET_GenericReturnValue
201GNUNET_BLOCK_group_merge (struct GNUNET_BLOCK_Group *bg1,
202 struct GNUNET_BLOCK_Group *bg2)
203{
204 enum GNUNET_GenericReturnValue ret;
205
206 if (NULL == bg2)
207 return GNUNET_OK;
208 if (NULL == bg1)
209 {
210 bg2->destroy_cb (bg2);
211 return GNUNET_OK;
212 }
213 if (NULL == bg1->merge_cb)
214 return GNUNET_SYSERR;
215 GNUNET_assert (bg1->merge_cb == bg1->merge_cb);
216 ret = bg1->merge_cb (bg1,
217 bg2);
218 bg2->destroy_cb (bg2);
219 return ret;
220}
221
222
223/**
224 * Find a plugin for the given type.
225 *
226 * @param ctx context to search
227 * @param type type to look for
228 * @return NULL if no matching plugin exists
229 */
230static struct GNUNET_BLOCK_PluginFunctions *
231find_plugin (struct GNUNET_BLOCK_Context *ctx,
232 enum GNUNET_BLOCK_Type type)
233{
234 for (unsigned i = 0; i < ctx->num_plugins; i++)
235 {
236 struct Plugin *plugin = ctx->plugins[i];
237
238 for (unsigned int j = 0; 0 != plugin->api->types[j]; j++)
239 if (type == plugin->api->types[j])
240 return plugin->api;
241 }
242 return NULL;
243}
244
245
246struct GNUNET_BLOCK_Group *
247GNUNET_BLOCK_group_create (struct GNUNET_BLOCK_Context *ctx,
248 enum GNUNET_BLOCK_Type type,
249 const void *raw_data,
250 size_t raw_data_size,
251 ...)
252{
253 struct GNUNET_BLOCK_PluginFunctions *plugin;
254 struct GNUNET_BLOCK_Group *bg;
255 va_list ap;
256
257 plugin = find_plugin (ctx,
258 type);
259 if (NULL == plugin)
260 return NULL;
261 if (NULL == plugin->create_group)
262 return NULL;
263 va_start (ap,
264 raw_data_size);
265 bg = plugin->create_group (plugin->cls,
266 type,
267 raw_data,
268 raw_data_size,
269 ap);
270 va_end (ap);
271 return bg;
272}
273
274
275enum GNUNET_GenericReturnValue
276GNUNET_BLOCK_get_key (struct GNUNET_BLOCK_Context *ctx,
277 enum GNUNET_BLOCK_Type type,
278 const void *block,
279 size_t block_size,
280 struct GNUNET_HashCode *key)
281{
282 struct GNUNET_BLOCK_PluginFunctions *plugin = find_plugin (ctx,
283 type);
284
285 if (NULL == plugin)
286 return GNUNET_SYSERR;
287 return plugin->get_key (plugin->cls,
288 type,
289 block,
290 block_size,
291 key);
292}
293
294
295enum GNUNET_GenericReturnValue
296GNUNET_BLOCK_check_query (struct GNUNET_BLOCK_Context *ctx,
297 enum GNUNET_BLOCK_Type type,
298 const struct GNUNET_HashCode *query,
299 const void *xquery,
300 size_t xquery_size)
301{
302 struct GNUNET_BLOCK_PluginFunctions *plugin;
303
304 if (GNUNET_BLOCK_TYPE_ANY == type)
305 return GNUNET_SYSERR; /* no checks */
306 plugin = find_plugin (ctx,
307 type);
308 if (NULL == plugin)
309 return GNUNET_SYSERR;
310 return plugin->check_query (plugin->cls,
311 type,
312 query,
313 xquery,
314 xquery_size);
315}
316
317
318enum GNUNET_GenericReturnValue
319GNUNET_BLOCK_check_block (struct GNUNET_BLOCK_Context *ctx,
320 enum GNUNET_BLOCK_Type type,
321 const void *block,
322 size_t block_size)
323{
324 struct GNUNET_BLOCK_PluginFunctions *plugin = find_plugin (ctx,
325 type);
326
327 if (NULL == plugin)
328 return GNUNET_SYSERR;
329 return plugin->check_block (plugin->cls,
330 type,
331 block,
332 block_size);
333}
334
335
336enum GNUNET_BLOCK_ReplyEvaluationResult
337GNUNET_BLOCK_check_reply (struct GNUNET_BLOCK_Context *ctx,
338 enum GNUNET_BLOCK_Type type,
339 struct GNUNET_BLOCK_Group *group,
340 const struct GNUNET_HashCode *query,
341 const void *xquery,
342 size_t xquery_size,
343 const void *reply_block,
344 size_t reply_block_size)
345{
346 struct GNUNET_BLOCK_PluginFunctions *plugin = find_plugin (ctx,
347 type);
348
349 if (NULL == plugin)
350 return GNUNET_BLOCK_REPLY_TYPE_NOT_SUPPORTED;
351 return plugin->check_reply (plugin->cls,
352 type,
353 group,
354 query,
355 xquery,
356 xquery_size,
357 reply_block,
358 reply_block_size);
359}
360
361
362enum GNUNET_GenericReturnValue
363GNUNET_BLOCK_group_set_seen (struct GNUNET_BLOCK_Group *bg,
364 const struct GNUNET_HashCode *seen_results,
365 unsigned int seen_results_count)
366{
367 if (NULL == bg)
368 return GNUNET_OK;
369 if (NULL == bg->mark_seen_cb)
370 return GNUNET_SYSERR;
371 bg->mark_seen_cb (bg,
372 seen_results,
373 seen_results_count);
374 return GNUNET_OK;
375}
376
377
378/* end of block.c */
diff --git a/src/lib/block/meson.build b/src/lib/block/meson.build
new file mode 100644
index 000000000..999391c27
--- /dev/null
+++ b/src/lib/block/meson.build
@@ -0,0 +1,24 @@
1libgnunetblock_src = ['block.c']
2libgnunetblockgroup_src = ['bg_bf.c']
3
4libgnunetblock = library('gnunetblock',
5 libgnunetblock_src,
6 dependencies: libgnunetutil_dep,
7 include_directories: [incdir, configuration_inc],
8 install: true,
9 version: '0.0.0',
10 soversion: '0',
11 install_dir: get_option('libdir'))
12libgnunetblock_dep = declare_dependency(link_with : libgnunetblock)
13pkg.generate(libgnunetblock, url: 'https://www.gnunet.org',
14 description : 'Library for data block manipulation')
15
16libgnunetblockgroup = library('gnunetblockgroup',
17 libgnunetblockgroup_src,
18 dependencies: [libgnunetutil_dep, libgnunetblock_dep],
19 include_directories: [incdir, configuration_inc],
20 install: true,
21 version: '0.0.0',
22 soversion: '0',
23 install_dir: get_option('libdir'))
24libgnunetblockgroup_dep = declare_dependency(link_with : libgnunetblockgroup)
diff --git a/src/lib/curl/Makefile.am b/src/lib/curl/Makefile.am
new file mode 100644
index 000000000..78eae4b97
--- /dev/null
+++ b/src/lib/curl/Makefile.am
@@ -0,0 +1,38 @@
1# This Makefile.am is in the public domain
2AM_CPPFLAGS = -I$(top_srcdir)/src/include
3
4if USE_COVERAGE
5 AM_CFLAGS = --coverage -O0
6 XLIB = -lgcov
7endif
8
9lib_LTLIBRARIES = \
10 libgnunetcurl.la
11
12libgnunetcurl_la_LDFLAGS = \
13 $(GN_LIBINTL) \
14 -version-info 0:0:0 \
15 -no-undefined
16libgnunetcurl_la_SOURCES = \
17 curl.c \
18 curl_reschedule.c
19libgnunetcurl_la_LIBADD = \
20 $(top_builddir)/src/lib/util/libgnunetutil.la \
21 -ljansson \
22 @LIBCURL@ \
23 $(XLIB)
24libgnunetcurl_la_CPPFLAGS = \
25 @LIBCURL_CPPFLAGS@ $(AM_CPPFLAGS) $(MHD_CFLAGS)
26
27#check_PROGRAMS = \
28# test_curl
29
30#TESTS = \
31# $(check_PROGRAMS)
32
33#test_curl_SOURCES = \
34# test_curl.c
35#test_curl_LDADD = \
36# libgnunetcurl.la \
37# $(top_builddir)/src/lib/util/libgnunetutil.la \
38# -ljansson -lcurl
diff --git a/src/lib/curl/curl.c b/src/lib/curl/curl.c
new file mode 100644
index 000000000..648c9a14e
--- /dev/null
+++ b/src/lib/curl/curl.c
@@ -0,0 +1,907 @@
1/*
2 This file is part of GNUnet
3 Copyright (C) 2014, 2015, 2016, 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 curl/curl.c
22 * @brief API for downloading JSON via CURL
23 * @author Sree Harsha Totakura <sreeharsha@totakura.in>
24 * @author Christian Grothoff
25 */
26#include "platform.h"
27#include <jansson.h>
28#include <microhttpd.h>
29#include "gnunet_curl_lib.h"
30
31#if ENABLE_BENCHMARK
32#include "../util/benchmark.h"
33#endif
34
35/**
36 * Set to 1 for extra debug logging.
37 */
38#define DEBUG 0
39
40/**
41 * Log error related to CURL operations.
42 *
43 * @param type log level
44 * @param function which function failed to run
45 * @param code what was the curl error code
46 */
47#define CURL_STRERROR(type, function, code) \
48 GNUNET_log (type, \
49 "Curl function `%s' has failed at `%s:%d' with error: %s\n", \
50 function, \
51 __FILE__, \
52 __LINE__, \
53 curl_easy_strerror (code));
54
55/**
56 * Print JSON parsing related error information
57 */
58#define JSON_WARN(error) \
59 GNUNET_log (GNUNET_ERROR_TYPE_WARNING, \
60 "JSON parsing failed at %s:%u: %s (%s)\n", \
61 __FILE__, \
62 __LINE__, \
63 error.text, \
64 error.source)
65
66
67/**
68 * Failsafe flag. Raised if our constructor fails to initialize
69 * the Curl library.
70 */
71static int curl_fail;
72
73/**
74 * Jobs are CURL requests running within a `struct GNUNET_CURL_Context`.
75 */
76struct GNUNET_CURL_Job
77{
78 /**
79 * We keep jobs in a DLL.
80 */
81 struct GNUNET_CURL_Job *next;
82
83 /**
84 * We keep jobs in a DLL.
85 */
86 struct GNUNET_CURL_Job *prev;
87
88 /**
89 * Easy handle of the job.
90 */
91 CURL *easy_handle;
92
93 /**
94 * Context this job runs in.
95 */
96 struct GNUNET_CURL_Context *ctx;
97
98 /**
99 * Function to call upon completion.
100 */
101 GNUNET_CURL_JobCompletionCallback jcc;
102
103 /**
104 * Closure for @e jcc.
105 */
106 void *jcc_cls;
107
108 /**
109 * Function to call upon completion.
110 */
111 GNUNET_CURL_RawJobCompletionCallback jcc_raw;
112
113 /**
114 * Closure for @e jcc_raw.
115 */
116 void *jcc_raw_cls;
117
118 /**
119 * Buffer for response received from CURL.
120 */
121 struct GNUNET_CURL_DownloadBuffer db;
122
123 /**
124 * Headers used for this job, the list needs to be freed
125 * after the job has finished.
126 */
127 struct curl_slist *job_headers;
128
129 /**
130 * When did we start the job?
131 */
132 struct GNUNET_TIME_Absolute start_time;
133};
134
135
136/**
137 * Context
138 */
139struct GNUNET_CURL_Context
140{
141 /**
142 * Curl multi handle
143 */
144 CURLM *multi;
145
146 /**
147 * Curl share handle
148 */
149 CURLSH *share;
150
151 /**
152 * We keep jobs in a DLL.
153 */
154 struct GNUNET_CURL_Job *jobs_head;
155
156 /**
157 * We keep jobs in a DLL.
158 */
159 struct GNUNET_CURL_Job *jobs_tail;
160
161 /**
162 * Headers common for all requests in the context.
163 */
164 struct curl_slist *common_headers;
165
166 /**
167 * If non-NULL, the async scope ID is sent in a request
168 * header of this name.
169 */
170 const char *async_scope_id_header;
171
172 /**
173 * Function we need to call whenever the event loop's
174 * socket set changed.
175 */
176 GNUNET_CURL_RescheduleCallback cb;
177
178 /**
179 * Closure for @e cb.
180 */
181 void *cb_cls;
182
183 /**
184 * USERNAME:PASSWORD to use for client-authentication
185 * with all requests of this context, or NULL.
186 */
187 char *userpass;
188
189 /**
190 * Type of the TLS client certificate used, or NULL.
191 */
192 char *certtype;
193
194 /**
195 * File with the TLS client certificate, or NULL.
196 */
197 char *certfile;
198
199 /**
200 * File with the private key to authenticate the
201 * TLS client, or NULL.
202 */
203 char *keyfile;
204
205 /**
206 * Passphrase to decrypt @e keyfile, or NULL.
207 */
208 char *keypass;
209
210};
211
212
213void
214GNUNET_CURL_set_userpass (struct GNUNET_CURL_Context *ctx,
215 const char *userpass)
216{
217 GNUNET_free (ctx->userpass);
218 if (NULL != userpass)
219 ctx->userpass = GNUNET_strdup (userpass);
220}
221
222
223void
224GNUNET_CURL_set_tlscert (struct GNUNET_CURL_Context *ctx,
225 const char *certtype,
226 const char *certfile,
227 const char *keyfile,
228 const char *keypass)
229{
230 GNUNET_free (ctx->certtype);
231 GNUNET_free (ctx->certfile);
232 GNUNET_free (ctx->keyfile);
233 GNUNET_free (ctx->keypass);
234 if (NULL != certtype)
235 ctx->certtype = GNUNET_strdup (certtype);
236 if (NULL != certfile)
237 ctx->certfile = GNUNET_strdup (certfile);
238 if (NULL != keyfile)
239 ctx->certtype = GNUNET_strdup (keyfile);
240 if (NULL != keypass)
241 ctx->certtype = GNUNET_strdup (keypass);
242}
243
244
245struct GNUNET_CURL_Context *
246GNUNET_CURL_init (GNUNET_CURL_RescheduleCallback cb,
247 void *cb_cls)
248{
249 struct GNUNET_CURL_Context *ctx;
250 CURLM *multi;
251 CURLSH *share;
252
253 if (curl_fail)
254 {
255 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
256 "Curl was not initialised properly\n");
257 return NULL;
258 }
259 if (NULL == (multi = curl_multi_init ()))
260 {
261 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
262 "Failed to create a Curl multi handle\n");
263 return NULL;
264 }
265 if (NULL == (share = curl_share_init ()))
266 {
267 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
268 "Failed to create a Curl share handle\n");
269 return NULL;
270 }
271 ctx = GNUNET_new (struct GNUNET_CURL_Context);
272 ctx->cb = cb;
273 ctx->cb_cls = cb_cls;
274 ctx->multi = multi;
275 ctx->share = share;
276 return ctx;
277}
278
279
280void
281GNUNET_CURL_enable_async_scope_header (struct GNUNET_CURL_Context *ctx,
282 const char *header_name)
283{
284 ctx->async_scope_id_header = header_name;
285}
286
287
288enum GNUNET_GenericReturnValue
289GNUNET_CURL_is_valid_scope_id (const char *scope_id)
290{
291 if (strlen (scope_id) >= 64)
292 return GNUNET_NO;
293 for (size_t i = 0; i < strlen (scope_id); i++)
294 if (! (isalnum (scope_id[i]) || (scope_id[i] == '-')))
295 return GNUNET_NO;
296 return GNUNET_YES;
297}
298
299
300/**
301 * Callback used when downloading the reply to an HTTP request.
302 * Just appends all of the data to the `buf` in the
303 * `struct DownloadBuffer` for further processing. The size of
304 * the download is limited to #GNUNET_MAX_MALLOC_CHECKED, if
305 * the download exceeds this size, we abort with an error.
306 *
307 * @param bufptr data downloaded via HTTP
308 * @param size size of an item in @a bufptr
309 * @param nitems number of items in @a bufptr
310 * @param cls the `struct DownloadBuffer`
311 * @return number of bytes processed from @a bufptr
312 */
313static size_t
314download_cb (char *bufptr,
315 size_t size,
316 size_t nitems,
317 void *cls)
318{
319 struct GNUNET_CURL_DownloadBuffer *db = cls;
320 size_t msize;
321 void *buf;
322
323 if (0 == size * nitems)
324 {
325 /* Nothing (left) to do */
326 return 0;
327 }
328 msize = size * nitems;
329 if ((msize + db->buf_size) >= GNUNET_MAX_MALLOC_CHECKED)
330 {
331 db->eno = ENOMEM;
332 return 0; /* signals an error to curl */
333 }
334 db->buf = GNUNET_realloc (db->buf,
335 db->buf_size + msize);
336 buf = db->buf + db->buf_size;
337 GNUNET_memcpy (buf, bufptr, msize);
338 db->buf_size += msize;
339 return msize;
340}
341
342
343/**
344 * Create the HTTP headers for the request
345 *
346 * @param ctx context we run in
347 * @param job_headers job-specific headers
348 * @return all headers to use
349 */
350static struct curl_slist *
351setup_job_headers (struct GNUNET_CURL_Context *ctx,
352 const struct curl_slist *job_headers)
353{
354 struct curl_slist *all_headers = NULL;
355
356 for (const struct curl_slist *curr = job_headers;
357 NULL != curr;
358 curr = curr->next)
359 {
360 GNUNET_assert (NULL !=
361 (all_headers = curl_slist_append (all_headers,
362 curr->data)));
363 }
364
365 for (const struct curl_slist *curr = ctx->common_headers;
366 NULL != curr;
367 curr = curr->next)
368 {
369 GNUNET_assert (NULL !=
370 (all_headers = curl_slist_append (all_headers,
371 curr->data)));
372 }
373
374 if (NULL != ctx->async_scope_id_header)
375 {
376 struct GNUNET_AsyncScopeSave scope;
377
378 GNUNET_async_scope_get (&scope);
379 if (GNUNET_YES == scope.have_scope)
380 {
381 char *aid_header;
382
383 aid_header =
384 GNUNET_STRINGS_data_to_string_alloc (
385 &scope.scope_id,
386 sizeof(struct GNUNET_AsyncScopeId));
387 GNUNET_assert (NULL != aid_header);
388 GNUNET_assert (NULL != curl_slist_append (all_headers,
389 aid_header));
390 GNUNET_free (aid_header);
391 }
392 }
393 return all_headers;
394}
395
396
397/**
398 * Create a job.
399 *
400 * @param eh easy handle to use
401 * @param ctx context to run the job in
402 * @param all_headers HTTP client headers to use (free'd)
403 * @return NULL on error
404 */
405static struct GNUNET_CURL_Job *
406setup_job (CURL *eh,
407 struct GNUNET_CURL_Context *ctx,
408 struct curl_slist *all_headers)
409{
410 struct GNUNET_CURL_Job *job;
411
412 if (CURLE_OK !=
413 curl_easy_setopt (eh,
414 CURLOPT_HTTPHEADER,
415 all_headers))
416 {
417 GNUNET_break (0);
418 curl_slist_free_all (all_headers);
419 curl_easy_cleanup (eh);
420 return NULL;
421 }
422 job = GNUNET_new (struct GNUNET_CURL_Job);
423 job->start_time = GNUNET_TIME_absolute_get ();
424 job->job_headers = all_headers;
425
426 if ( (CURLE_OK !=
427 curl_easy_setopt (eh,
428 CURLOPT_PRIVATE,
429 job)) ||
430 (CURLE_OK !=
431 curl_easy_setopt (eh,
432 CURLOPT_WRITEFUNCTION,
433 &download_cb)) ||
434 (CURLE_OK !=
435 curl_easy_setopt (eh,
436 CURLOPT_WRITEDATA,
437 &job->db)) ||
438 (CURLE_OK !=
439 curl_easy_setopt (eh,
440 CURLOPT_SHARE,
441 ctx->share)) )
442 {
443 GNUNET_break (0);
444 GNUNET_free (job);
445 curl_easy_cleanup (eh);
446 return NULL;
447 }
448 if ( (CURLM_OK !=
449 curl_multi_add_handle (ctx->multi,
450 eh)) )
451 {
452 GNUNET_break (0);
453 GNUNET_free (job);
454 curl_easy_cleanup (eh);
455 return NULL;
456 }
457 job->easy_handle = eh;
458 job->ctx = ctx;
459 GNUNET_CONTAINER_DLL_insert (ctx->jobs_head,
460 ctx->jobs_tail,
461 job);
462 return job;
463}
464
465
466void
467GNUNET_CURL_extend_headers (struct GNUNET_CURL_Job *job,
468 const struct curl_slist *extra_headers)
469{
470 struct curl_slist *all_headers = job->job_headers;
471
472 for (const struct curl_slist *curr = extra_headers;
473 NULL != curr;
474 curr = curr->next)
475 {
476 GNUNET_assert (NULL !=
477 (all_headers = curl_slist_append (all_headers,
478 curr->data)));
479 }
480 job->job_headers = all_headers;
481}
482
483
484struct GNUNET_CURL_Job *
485GNUNET_CURL_job_add_raw (struct GNUNET_CURL_Context *ctx,
486 CURL *eh,
487 const struct curl_slist *job_headers,
488 GNUNET_CURL_RawJobCompletionCallback jcc,
489 void *jcc_cls)
490{
491 struct GNUNET_CURL_Job *job;
492 struct curl_slist *all_headers;
493
494 GNUNET_assert (NULL != jcc);
495 all_headers = setup_job_headers (ctx,
496 job_headers);
497 if (NULL == (job = setup_job (eh,
498 ctx,
499 all_headers)))
500 return NULL;
501 job->jcc_raw = jcc;
502 job->jcc_raw_cls = jcc_cls;
503 ctx->cb (ctx->cb_cls);
504 return job;
505}
506
507
508struct GNUNET_CURL_Job *
509GNUNET_CURL_job_add2 (struct GNUNET_CURL_Context *ctx,
510 CURL *eh,
511 const struct curl_slist *job_headers,
512 GNUNET_CURL_JobCompletionCallback jcc,
513 void *jcc_cls)
514{
515 struct GNUNET_CURL_Job *job;
516 struct curl_slist *all_headers;
517
518 GNUNET_assert (NULL != jcc);
519 if ( (NULL != ctx->userpass) &&
520 (0 != curl_easy_setopt (eh,
521 CURLOPT_USERPWD,
522 ctx->userpass)) )
523 return NULL;
524 if ( (NULL != ctx->certfile) &&
525 (0 != curl_easy_setopt (eh,
526 CURLOPT_SSLCERT,
527 ctx->certfile)) )
528 return NULL;
529 if ( (NULL != ctx->certtype) &&
530 (0 != curl_easy_setopt (eh,
531 CURLOPT_SSLCERTTYPE,
532 ctx->certtype)) )
533 return NULL;
534 if ( (NULL != ctx->keyfile) &&
535 (0 != curl_easy_setopt (eh,
536 CURLOPT_SSLKEY,
537 ctx->keyfile)) )
538 return NULL;
539 if ( (NULL != ctx->keypass) &&
540 (0 != curl_easy_setopt (eh,
541 CURLOPT_KEYPASSWD,
542 ctx->keypass)) )
543 return NULL;
544
545 all_headers = setup_job_headers (ctx,
546 job_headers);
547 if (NULL == (job = setup_job (eh,
548 ctx,
549 all_headers)))
550 return NULL;
551
552 job->jcc = jcc;
553 job->jcc_cls = jcc_cls;
554 ctx->cb (ctx->cb_cls);
555 return job;
556}
557
558
559struct GNUNET_CURL_Job *
560GNUNET_CURL_job_add_with_ct_json (struct GNUNET_CURL_Context *ctx,
561 CURL *eh,
562 GNUNET_CURL_JobCompletionCallback jcc,
563 void *jcc_cls)
564{
565 struct GNUNET_CURL_Job *job;
566 struct curl_slist *job_headers = NULL;
567
568 GNUNET_assert (NULL != (job_headers =
569 curl_slist_append (NULL,
570 "Content-Type: application/json")));
571 job = GNUNET_CURL_job_add2 (ctx,
572 eh,
573 job_headers,
574 jcc,
575 jcc_cls);
576 curl_slist_free_all (job_headers);
577 return job;
578}
579
580
581struct GNUNET_CURL_Job *
582GNUNET_CURL_job_add (struct GNUNET_CURL_Context *ctx,
583 CURL *eh,
584 GNUNET_CURL_JobCompletionCallback jcc,
585 void *jcc_cls)
586{
587 return GNUNET_CURL_job_add2 (ctx,
588 eh,
589 NULL,
590 jcc,
591 jcc_cls);
592}
593
594
595void
596GNUNET_CURL_job_cancel (struct GNUNET_CURL_Job *job)
597{
598 struct GNUNET_CURL_Context *ctx = job->ctx;
599
600 GNUNET_CONTAINER_DLL_remove (ctx->jobs_head,
601 ctx->jobs_tail,
602 job);
603 GNUNET_break (CURLM_OK ==
604 curl_multi_remove_handle (ctx->multi,
605 job->easy_handle));
606 curl_easy_cleanup (job->easy_handle);
607 GNUNET_free (job->db.buf);
608 curl_slist_free_all (job->job_headers);
609 ctx->cb (ctx->cb_cls);
610 GNUNET_free (job);
611}
612
613
614/**
615 * Test if the given content type @a ct is JSON
616 *
617 * @param ct a content type, e.g. "application/json; charset=UTF-8"
618 * @return true if @a ct denotes JSON
619 */
620static bool
621is_json (const char *ct)
622{
623 const char *semi;
624
625 /* check for "application/json" exact match */
626 if (0 == strcasecmp (ct,
627 "application/json"))
628 return true;
629 /* check for "application/json;[ANYTHING]" */
630 semi = strchr (ct,
631 ';');
632 /* also allow "application/json [ANYTHING]" (note the space!) */
633 if (NULL == semi)
634 semi = strchr (ct,
635 ' ');
636 if (NULL == semi)
637 return false; /* no delimiter we accept, forget it */
638 if (semi - ct != strlen ("application/json"))
639 return false; /* delimiter past desired length, forget it */
640 if (0 == strncasecmp (ct,
641 "application/json",
642 strlen ("application/json")))
643 return true; /* OK */
644 return false;
645}
646
647
648void *
649GNUNET_CURL_download_get_result_ (struct GNUNET_CURL_DownloadBuffer *db,
650 CURL *eh,
651 long *response_code)
652{
653 json_t *json;
654 json_error_t error;
655 char *ct;
656
657#if DEBUG
658 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
659 "Downloaded body: %.*s\n",
660 (int) db->buf_size,
661 (char *) db->buf);
662#endif
663 if (CURLE_OK !=
664 curl_easy_getinfo (eh,
665 CURLINFO_RESPONSE_CODE,
666 response_code))
667 {
668 /* unexpected error... */
669 GNUNET_break (0);
670 *response_code = 0;
671 }
672 if (MHD_HTTP_NO_CONTENT == *response_code)
673 return NULL;
674 if ((CURLE_OK !=
675 curl_easy_getinfo (eh,
676 CURLINFO_CONTENT_TYPE,
677 &ct)) ||
678 (NULL == ct) ||
679 (! is_json (ct)))
680 {
681 /* No content type or explicitly not JSON, refuse to parse
682 (but keep response code) */
683 if (0 != db->buf_size)
684 {
685 char *url;
686
687 if (CURLE_OK !=
688 curl_easy_getinfo (eh,
689 CURLINFO_EFFECTIVE_URL,
690 &url))
691 url = "<unknown URL>";
692 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
693 "Request to `%s' was expected to return a body of type `application/json', got `%s'\n",
694 url,
695 ct);
696 }
697 return NULL;
698 }
699 if (0 == *response_code)
700 {
701 char *url;
702
703 if (CURLE_OK !=
704 curl_easy_getinfo (eh,
705 CURLINFO_EFFECTIVE_URL,
706 &url))
707 url = "<unknown URL>";
708 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
709 "Failed to download response from `%s': \n",
710 url);
711 return NULL;
712 }
713 json = NULL;
714 if (0 == db->eno)
715 {
716 json = json_loadb (db->buf,
717 db->buf_size,
718 JSON_REJECT_DUPLICATES | JSON_DISABLE_EOF_CHECK,
719 &error);
720 if (NULL == json)
721 {
722 JSON_WARN (error);
723 *response_code = 0;
724 }
725 }
726 GNUNET_free (db->buf);
727 db->buf = NULL;
728 db->buf_size = 0;
729 return json;
730}
731
732
733enum GNUNET_GenericReturnValue
734GNUNET_CURL_append_header (struct GNUNET_CURL_Context *ctx,
735 const char *header)
736{
737 ctx->common_headers = curl_slist_append (ctx->common_headers,
738 header);
739 if (NULL == ctx->common_headers)
740 return GNUNET_SYSERR;
741
742 return GNUNET_OK;
743}
744
745
746void
747GNUNET_CURL_perform2 (struct GNUNET_CURL_Context *ctx,
748 GNUNET_CURL_RawParser rp,
749 GNUNET_CURL_ResponseCleaner rc)
750{
751 CURLMsg *cmsg;
752 int n_running;
753 int n_completed;
754
755 (void) curl_multi_perform (ctx->multi,
756 &n_running);
757 while (NULL != (cmsg = curl_multi_info_read (ctx->multi,
758 &n_completed)))
759 {
760 struct GNUNET_CURL_Job *job;
761 struct GNUNET_TIME_Relative duration;
762 long response_code;
763 void *response;
764
765 /* Only documented return value is CURLMSG_DONE */
766 GNUNET_break (CURLMSG_DONE == cmsg->msg);
767 GNUNET_assert (CURLE_OK ==
768 curl_easy_getinfo (cmsg->easy_handle,
769 CURLINFO_PRIVATE,
770 (char **) &job));
771 GNUNET_assert (job->ctx == ctx);
772 response_code = 0;
773 duration = GNUNET_TIME_absolute_get_duration (job->start_time);
774 if (NULL != job->jcc_raw)
775 {
776 /* RAW mode, no parsing */
777 GNUNET_break (CURLE_OK ==
778 curl_easy_getinfo (job->easy_handle,
779 CURLINFO_RESPONSE_CODE,
780 &response_code));
781 job->jcc_raw (job->jcc_raw_cls,
782 response_code,
783 job->db.buf,
784 job->db.buf_size);
785 }
786 else
787 {
788 /* to be parsed via 'rp' */
789 response = rp (&job->db,
790 job->easy_handle,
791 &response_code);
792 job->jcc (job->jcc_cls,
793 response_code,
794 response);
795 rc (response);
796 }
797 {
798 char *url = NULL;
799
800 if (CURLE_UNKNOWN_OPTION ==
801 curl_easy_getinfo (job->easy_handle,
802 CURLINFO_EFFECTIVE_URL,
803 &url))
804 url = "<unknown>";
805 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
806 "HTTP request for `%s' finished with %u after %s\n",
807 url,
808 (unsigned int) response_code,
809 GNUNET_TIME_relative2s (duration,
810 true));
811 /* Note: we MUST NOT free 'url' here */
812 }
813 GNUNET_CURL_job_cancel (job);
814 }
815}
816
817
818void
819GNUNET_CURL_perform (struct GNUNET_CURL_Context *ctx)
820{
821 GNUNET_CURL_perform2 (ctx,
822 &GNUNET_CURL_download_get_result_,
823 (GNUNET_CURL_ResponseCleaner) & json_decref);
824}
825
826
827void
828GNUNET_CURL_get_select_info (struct GNUNET_CURL_Context *ctx,
829 fd_set *read_fd_set,
830 fd_set *write_fd_set,
831 fd_set *except_fd_set,
832 int *max_fd,
833 long *timeout)
834{
835 long to;
836 int m;
837
838 m = -1;
839 GNUNET_assert (CURLM_OK ==
840 curl_multi_fdset (ctx->multi,
841 read_fd_set,
842 write_fd_set,
843 except_fd_set,
844 &m));
845 to = *timeout;
846 *max_fd = GNUNET_MAX (m, *max_fd);
847 GNUNET_assert (CURLM_OK ==
848 curl_multi_timeout (ctx->multi,
849 &to));
850
851 /* Only if what we got back from curl is smaller than what we
852 already had (-1 == infinity!), then update timeout */
853 if ((to < *timeout) && (-1 != to))
854 *timeout = to;
855 if ((-1 == (*timeout)) && (NULL != ctx->jobs_head))
856 *timeout = to;
857}
858
859
860void
861GNUNET_CURL_fini (struct GNUNET_CURL_Context *ctx)
862{
863 /* all jobs must have been cancelled at this time, assert this */
864 GNUNET_assert (NULL == ctx->jobs_head);
865 curl_share_cleanup (ctx->share);
866 curl_multi_cleanup (ctx->multi);
867 curl_slist_free_all (ctx->common_headers);
868 GNUNET_free (ctx->userpass);
869 GNUNET_free (ctx->certtype);
870 GNUNET_free (ctx->certfile);
871 GNUNET_free (ctx->keyfile);
872 GNUNET_free (ctx->keypass);
873 GNUNET_free (ctx);
874}
875
876
877/**
878 * Initial global setup logic, specifically runs the Curl setup.
879 */
880__attribute__ ((constructor)) void
881GNUNET_CURL_constructor__ (void)
882{
883 CURLcode ret;
884
885 if (CURLE_OK != (ret = curl_global_init (CURL_GLOBAL_DEFAULT)))
886 {
887 CURL_STRERROR (GNUNET_ERROR_TYPE_ERROR,
888 "curl_global_init",
889 ret);
890 curl_fail = 1;
891 }
892}
893
894
895/**
896 * Cleans up after us, specifically runs the Curl cleanup.
897 */
898__attribute__ ((destructor)) void
899GNUNET_CURL_destructor__ (void)
900{
901 if (curl_fail)
902 return;
903 curl_global_cleanup ();
904}
905
906
907/* end of curl.c */
diff --git a/src/lib/curl/curl_reschedule.c b/src/lib/curl/curl_reschedule.c
new file mode 100644
index 000000000..0c19bd171
--- /dev/null
+++ b/src/lib/curl/curl_reschedule.c
@@ -0,0 +1,181 @@
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 * @file curl/curl_reschedule.c
22 * @brief API for event loop integration with GNUnet SCHEDULER.
23 * @author Christian Grothoff
24 */
25#include "platform.h"
26#include <jansson.h>
27#include "gnunet_curl_lib.h"
28#include "gnunet_util_lib.h"
29
30extern void *
31GNUNET_CURL_download_get_result_ (struct GNUNET_CURL_DownloadBuffer *db,
32 CURL *eh,
33 long *response_code);
34
35/**
36 * Closure for #GNUNET_CURL_gnunet_scheduler_reschedule().
37 */
38struct GNUNET_CURL_RescheduleContext
39{
40 /**
41 * Just the task.
42 */
43 struct GNUNET_SCHEDULER_Task *task;
44
45 /**
46 * Context we manage.
47 */
48 struct GNUNET_CURL_Context *ctx;
49
50 /**
51 * Parser of the raw response.
52 */
53 GNUNET_CURL_RawParser parser;
54
55 /**
56 * Deallocate the response object.
57 */
58 GNUNET_CURL_ResponseCleaner cleaner;
59};
60
61
62struct GNUNET_CURL_RescheduleContext *
63GNUNET_CURL_gnunet_rc_create_with_parser (struct GNUNET_CURL_Context *ctx,
64 GNUNET_CURL_RawParser rp,
65 GNUNET_CURL_ResponseCleaner rc)
66{
67 struct GNUNET_CURL_RescheduleContext *rctx;
68
69 rctx = GNUNET_new (struct GNUNET_CURL_RescheduleContext);
70 rctx->ctx = ctx;
71 rctx->parser = rp;
72 rctx->cleaner = rc;
73
74 return rctx;
75}
76
77
78/**
79 * Just a wrapper to avoid casting of function pointers.
80 *
81 * @param response the (JSON) response to clean.
82 */
83static void
84clean_result (void *response)
85{
86 json_decref (response);
87}
88
89
90struct GNUNET_CURL_RescheduleContext *
91GNUNET_CURL_gnunet_rc_create (struct GNUNET_CURL_Context *ctx)
92{
93 struct GNUNET_CURL_RescheduleContext *rc;
94
95 rc = GNUNET_new (struct GNUNET_CURL_RescheduleContext);
96 rc->ctx = ctx;
97 rc->parser = &GNUNET_CURL_download_get_result_;
98 rc->cleaner = &clean_result;
99 return rc;
100}
101
102
103void
104GNUNET_CURL_gnunet_rc_destroy (struct GNUNET_CURL_RescheduleContext *rc)
105{
106 if (NULL != rc->task)
107 GNUNET_SCHEDULER_cancel (rc->task);
108 GNUNET_free (rc);
109}
110
111
112/**
113 * Task that runs the context's event loop with the GNUnet scheduler.
114 *
115 * @param cls a `struct GNUNET_CURL_RescheduleContext *`
116 */
117static void
118context_task (void *cls)
119{
120 struct GNUNET_CURL_RescheduleContext *rc = cls;
121 long timeout;
122 int max_fd;
123 fd_set read_fd_set;
124 fd_set write_fd_set;
125 fd_set except_fd_set;
126 struct GNUNET_NETWORK_FDSet *rs;
127 struct GNUNET_NETWORK_FDSet *ws;
128 struct GNUNET_TIME_Relative delay;
129
130 rc->task = NULL;
131 GNUNET_CURL_perform2 (rc->ctx, rc->parser, rc->cleaner);
132 max_fd = -1;
133 timeout = -1;
134 FD_ZERO (&read_fd_set);
135 FD_ZERO (&write_fd_set);
136 FD_ZERO (&except_fd_set);
137 GNUNET_CURL_get_select_info (rc->ctx,
138 &read_fd_set,
139 &write_fd_set,
140 &except_fd_set,
141 &max_fd,
142 &timeout);
143 if (timeout >= 0)
144 delay =
145 GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_MILLISECONDS,
146 timeout);
147 else
148 delay = GNUNET_TIME_UNIT_FOREVER_REL;
149 rs = GNUNET_NETWORK_fdset_create ();
150 GNUNET_NETWORK_fdset_copy_native (rs,
151 &read_fd_set,
152 max_fd + 1);
153 ws = GNUNET_NETWORK_fdset_create ();
154 GNUNET_NETWORK_fdset_copy_native (ws,
155 &write_fd_set,
156 max_fd + 1);
157 if (NULL == rc->task)
158 rc->task = GNUNET_SCHEDULER_add_select (GNUNET_SCHEDULER_PRIORITY_DEFAULT,
159 delay,
160 rs,
161 ws,
162 &context_task,
163 rc);
164 GNUNET_NETWORK_fdset_destroy (rs);
165 GNUNET_NETWORK_fdset_destroy (ws);
166}
167
168
169void
170GNUNET_CURL_gnunet_scheduler_reschedule (void *cls)
171{
172 struct GNUNET_CURL_RescheduleContext *rc = *(void **) cls;
173
174 if (NULL != rc->task)
175 GNUNET_SCHEDULER_cancel (rc->task);
176 rc->task = GNUNET_SCHEDULER_add_now (&context_task,
177 rc);
178}
179
180
181/* end of curl_reschedule.c */
diff --git a/src/lib/curl/meson.build b/src/lib/curl/meson.build
new file mode 100644
index 000000000..77594d6c1
--- /dev/null
+++ b/src/lib/curl/meson.build
@@ -0,0 +1,13 @@
1libgnunetcurl_src = ['curl.c',
2 'curl_reschedule.c']
3
4libgnunetcurl = library('gnunetcurl',
5 libgnunetcurl_src,
6 soversion: '0',
7 version: '0.0.0',
8 dependencies: [libgnunetutil_dep, curl_dep, json_dep],
9 include_directories: [incdir, configuration_inc],
10 install: true,
11 install_dir: get_option('libdir'))
12libgnunetcurl_dep = declare_dependency(link_with : libgnunetcurl)
13
diff --git a/src/lib/gnsrecord/.gitignore b/src/lib/gnsrecord/.gitignore
new file mode 100644
index 000000000..dca3bd309
--- /dev/null
+++ b/src/lib/gnsrecord/.gitignore
@@ -0,0 +1,6 @@
1test_gnsrecord_block_expiration
2test_gnsrecord_crypto
3test_gnsrecord_serialization
4zonefiles
5perf_gnsrecord_crypto
6gnunet-gnsrecord-tvg
diff --git a/src/lib/gnsrecord/Makefile.am b/src/lib/gnsrecord/Makefile.am
new file mode 100644
index 000000000..ab79de900
--- /dev/null
+++ b/src/lib/gnsrecord/Makefile.am
@@ -0,0 +1,111 @@
1# This Makefile.am is in the public domain
2AM_CPPFLAGS = -I$(top_srcdir)/src/include ${MHD_CFLAGS}
3
4plugindir = $(libdir)/gnunet
5
6pkgcfgdir= $(pkgdatadir)/config.d/
7
8libexecdir= $(pkglibdir)/libexec/
9
10if USE_COVERAGE
11 AM_CFLAGS = --coverage -O0
12 XLIBS = -lgcov
13endif
14
15noinst_PROGRAMS = \
16 gnunet-gnsrecord-tvg
17
18
19check_PROGRAMS = \
20 test_gnsrecord_crypto \
21 test_gnsrecord_serialization \
22 test_gnsrecord_lsd0001testvectors \
23 test_gnsrecord_block_expiration \
24 perf_gnsrecord_crypto
25
26if ENABLE_TEST_RUN
27AM_TESTS_ENVIRONMENT=export GNUNET_PREFIX=$${GNUNET_PREFIX:-@libdir@};export PATH=$${GNUNET_PREFIX:-@prefix@}/bin:$$PATH;unset XDG_DATA_HOME;unset XDG_CONFIG_HOME;
28TESTS = \
29 $(check_PROGRAMS) \
30 $(check_SCRIPTS)
31endif
32
33lib_LTLIBRARIES = \
34 libgnunetgnsrecord.la \
35 libgnunetgnsrecordjson.la
36
37gnunet_gnsrecord_tvg_SOURCES = \
38 gnunet-gnsrecord-tvg.c \
39 gnsrecord_crypto.h
40gnunet_gnsrecord_tvg_LDADD = \
41 $(top_builddir)/src/lib/util/libgnunetutil.la \
42 libgnunetgnsrecord.la \
43 $(GN_LIBINTL)
44
45
46libgnunetgnsrecord_la_SOURCES = \
47 gnsrecord.c \
48 gnsrecord_serialization.c \
49 gnsrecord_crypto.c \
50 gnsrecord_pow.c \
51 gnsrecord_misc.c
52libgnunetgnsrecord_la_LIBADD = \
53 $(top_builddir)/src/lib/util/libgnunetutil.la \
54 $(LIBGCRYPT_LIBS) \
55 -lsodium \
56 $(GN_LIBINTL)
57libgnunetgnsrecord_la_LDFLAGS = \
58 $(GN_LIB_LDFLAGS) \
59 -version-info 0:0:0
60
61libgnunetgnsrecordjson_la_SOURCES = \
62 json_gnsrecord.c
63libgnunetgnsrecordjson_la_LIBADD = \
64 $(top_builddir)/src/lib/util/libgnunetutil.la \
65 libgnunetgnsrecord.la \
66 -ljansson \
67 $(GN_LIBINTL)
68libgnunetgnsrecordjson_la_LDFLAGS = \
69 $(GN_LIB_LDFLAGS) \
70 -version-info 0:0:0
71
72EXTRA_DIST = \
73 $(check_SCRIPTS)
74
75test_gnsrecord_lsd0001testvectors_SOURCES = \
76 test_gnsrecord_testvectors.c
77test_gnsrecord_lsd0001testvectors_LDADD = \
78 $(top_builddir)/src/service/testing/libgnunettesting.la \
79 libgnunetgnsrecord.la \
80 $(top_builddir)/src/lib/util/libgnunetutil.la
81
82
83test_gnsrecord_serialization_SOURCES = \
84 test_gnsrecord_serialization.c
85test_gnsrecord_serialization_LDADD = \
86 $(top_builddir)/src/service/testing/libgnunettesting.la \
87 libgnunetgnsrecord.la \
88 $(top_builddir)/src/lib/util/libgnunetutil.la
89
90test_gnsrecord_block_expiration_SOURCES = \
91 test_gnsrecord_block_expiration.c
92test_gnsrecord_block_expiration_LDADD = \
93 $(top_builddir)/src/service/testing/libgnunettesting.la \
94 libgnunetgnsrecord.la \
95 $(top_builddir)/src/lib/util/libgnunetutil.la
96
97
98test_gnsrecord_crypto_SOURCES = \
99 test_gnsrecord_crypto.c
100test_gnsrecord_crypto_LDADD = \
101 $(top_builddir)/src/service/testing/libgnunettesting.la \
102 libgnunetgnsrecord.la \
103 $(top_builddir)/src/lib/util/libgnunetutil.la
104
105
106perf_gnsrecord_crypto_SOURCES = \
107 perf_gnsrecord_crypto.c
108perf_gnsrecord_crypto_LDADD = \
109 $(top_builddir)/src/service/testing/libgnunettesting.la \
110 libgnunetgnsrecord.la \
111 $(top_builddir)/src/lib/util/libgnunetutil.la
diff --git a/src/lib/gnsrecord/gnsrecord.c b/src/lib/gnsrecord/gnsrecord.c
new file mode 100644
index 000000000..c71dc1708
--- /dev/null
+++ b/src/lib/gnsrecord/gnsrecord.c
@@ -0,0 +1,267 @@
1/*
2 This file is part of GNUnet.
3 Copyright (C) 2009-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 gnsrecord/gnsrecord.c
23 * @brief API to access GNS record data
24 * @author Martin Schanzenbach
25 * @author Matthias Wachs
26 * @author Christian Grothoff
27 */
28#include "platform.h"
29#include "gnunet_util_lib.h"
30#include "gnunet_constants.h"
31#include "gnunet_gnsrecord_lib.h"
32#include "gnunet_gnsrecord_plugin.h"
33
34#define LOG(kind, ...) GNUNET_log_from (kind, "gnsrecord", __VA_ARGS__)
35
36
37/**
38 * Handle for a plugin.
39 */
40struct Plugin
41{
42 /**
43 * Name of the shared library.
44 */
45 char *library_name;
46
47 /**
48 * Plugin API.
49 */
50 struct GNUNET_GNSRECORD_PluginFunctions *api;
51};
52
53
54/**
55 * Array of our plugins.
56 */
57static struct Plugin **gns_plugins;
58
59/**
60 * Size of the 'plugins' array.
61 */
62static unsigned int num_plugins;
63
64/**
65 * Global to mark if we've run the initialization.
66 */
67static int once;
68
69
70/**
71 * Add a plugin to the list managed by the block library.
72 *
73 * @param cls NULL
74 * @param library_name name of the plugin
75 * @param lib_ret the plugin API
76 */
77static void
78add_plugin (void *cls,
79 const char *library_name,
80 void *lib_ret)
81{
82 struct GNUNET_GNSRECORD_PluginFunctions *api = lib_ret;
83 struct Plugin *plugin;
84
85 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
86 "Loading block plugin `%s'\n",
87 library_name);
88 plugin = GNUNET_new (struct Plugin);
89 plugin->api = api;
90 plugin->library_name = GNUNET_strdup (library_name);
91 GNUNET_array_append (gns_plugins, num_plugins, plugin);
92}
93
94
95/**
96 * Loads all plugins (lazy initialization).
97 */
98static void
99init ()
100{
101 if (1 == once)
102 return;
103 once = 1;
104
105 GNUNET_PLUGIN_load_all_in_context (GNUNET_OS_project_data_default (),
106 "libgnunet_plugin_gnsrecord_",
107 NULL,
108 &add_plugin,
109 NULL);
110}
111
112
113/**
114 * Dual function to #init().
115 */
116void __attribute__ ((destructor))
117GNSRECORD_fini ()
118{
119 struct Plugin *plugin;
120 const struct GNUNET_OS_ProjectData *pd = GNUNET_OS_project_data_get ();
121 const struct GNUNET_OS_ProjectData *dpd = GNUNET_OS_project_data_default ();
122
123 if (pd != dpd)
124 GNUNET_OS_init (dpd);
125
126 for (unsigned int i = 0; i < num_plugins; i++)
127 {
128 plugin = gns_plugins[i];
129 GNUNET_break (NULL ==
130 GNUNET_PLUGIN_unload (plugin->library_name,
131 plugin->api));
132 GNUNET_free (plugin->library_name);
133 GNUNET_free (plugin);
134 }
135 GNUNET_free (gns_plugins);
136
137 if (pd != dpd)
138 GNUNET_OS_init (pd);
139
140 gns_plugins = NULL;
141 once = 0;
142 num_plugins = 0;
143}
144
145
146/**
147 * Convert the 'value' of a record to a string.
148 *
149 * @param type type of the record
150 * @param data value in binary encoding
151 * @param data_size number of bytes in @a data
152 * @return NULL on error, otherwise human-readable representation of the value
153 */
154char *
155GNUNET_GNSRECORD_value_to_string (uint32_t type,
156 const void *data,
157 size_t data_size)
158{
159 struct Plugin *plugin;
160 char *ret;
161
162 init ();
163 for (unsigned int i = 0; i < num_plugins; i++)
164 {
165 plugin = gns_plugins[i];
166 if (NULL != (ret = plugin->api->value_to_string (plugin->api->cls,
167 type,
168 data,
169 data_size)))
170 return ret;
171 }
172 return NULL;
173}
174
175
176int
177GNUNET_GNSRECORD_string_to_value (uint32_t type,
178 const char *s,
179 void **data,
180 size_t *data_size)
181{
182 struct Plugin *plugin;
183
184 init ();
185 for (unsigned int i = 0; i < num_plugins; i++)
186 {
187 plugin = gns_plugins[i];
188 if (GNUNET_OK == plugin->api->string_to_value (plugin->api->cls,
189 type,
190 s,
191 data,
192 data_size))
193 return GNUNET_OK;
194 }
195 return GNUNET_SYSERR;
196}
197
198
199uint32_t
200GNUNET_GNSRECORD_typename_to_number (const char *dns_typename)
201{
202 struct Plugin *plugin;
203 uint32_t ret;
204
205 if (0 == strcasecmp (dns_typename,
206 "ANY"))
207 return GNUNET_GNSRECORD_TYPE_ANY;
208 init ();
209 for (unsigned int i = 0; i < num_plugins; i++)
210 {
211 plugin = gns_plugins[i];
212 if (UINT32_MAX != (ret = plugin->api->typename_to_number (plugin->api->cls,
213 dns_typename)))
214 return ret;
215 }
216 return UINT32_MAX;
217}
218
219
220/**
221 * Convert a type number to the corresponding type string (e.g. 1 to "A")
222 *
223 * @param type number of a type to convert
224 * @return corresponding typestring, NULL on error
225 */
226const char *
227GNUNET_GNSRECORD_number_to_typename (uint32_t type)
228{
229 struct Plugin *plugin;
230 const char *ret;
231
232 if (GNUNET_GNSRECORD_TYPE_ANY == type)
233 return "ANY";
234 init ();
235 for (unsigned int i = 0; i < num_plugins; i++)
236 {
237 plugin = gns_plugins[i];
238 if (NULL != (ret = plugin->api->number_to_typename (plugin->api->cls,
239 type)))
240 return ret;
241 }
242 return NULL;
243}
244
245
246enum GNUNET_GenericReturnValue
247GNUNET_GNSRECORD_is_critical (uint32_t type)
248{
249 struct Plugin *plugin;
250
251 if (GNUNET_GNSRECORD_TYPE_ANY == type)
252 return GNUNET_NO;
253 init ();
254 for (unsigned int i = 0; i < num_plugins; i++)
255 {
256 plugin = gns_plugins[i];
257 if (NULL == plugin->api->is_critical)
258 continue;
259 if (GNUNET_NO == plugin->api->is_critical (plugin->api->cls, type))
260 continue;
261 return GNUNET_YES;
262 }
263 return GNUNET_NO;
264}
265
266
267/* end of gnsrecord.c */
diff --git a/src/lib/gnsrecord/gnsrecord_crypto.c b/src/lib/gnsrecord/gnsrecord_crypto.c
new file mode 100644
index 000000000..de0fa3ffd
--- /dev/null
+++ b/src/lib/gnsrecord/gnsrecord_crypto.c
@@ -0,0 +1,1091 @@
1/*
2 This file is part of GNUnet.
3 Copyright (C) 2009-2013, 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/**
22 * @file gnsrecord/gnsrecord_crypto.c
23 * @brief API for GNS record-related crypto
24 * @author Martin Schanzenbach
25 * @author Matthias Wachs
26 * @author Christian Grothoff
27 */
28#include "platform.h"
29#include "gnsrecord_crypto.h"
30
31#define LOG(kind, ...) GNUNET_log_from (kind, "gnsrecord", __VA_ARGS__)
32
33ssize_t
34ecdsa_symmetric_decrypt (
35 const void *block,
36 size_t size,
37 const unsigned char *key,
38 const unsigned char *ctr,
39 void *result)
40{
41 gcry_cipher_hd_t handle;
42 int rc;
43
44 GNUNET_assert (0 == gcry_cipher_open (&handle, GCRY_CIPHER_AES256,
45 GCRY_CIPHER_MODE_CTR, 0));
46 rc = gcry_cipher_setkey (handle,
47 key,
48 GNUNET_CRYPTO_AES_KEY_LENGTH);
49 GNUNET_assert ((0 == rc) || ((char) rc == GPG_ERR_WEAK_KEY));
50 rc = gcry_cipher_setctr (handle,
51 ctr,
52 GNUNET_CRYPTO_AES_KEY_LENGTH / 2);
53 GNUNET_assert ((0 == rc) || ((char) rc == GPG_ERR_WEAK_KEY));
54 GNUNET_assert (0 == gcry_cipher_decrypt (handle, result, size, block, size));
55 gcry_cipher_close (handle);
56 return size;
57}
58
59
60ssize_t
61ecdsa_symmetric_encrypt (
62 const void *block,
63 size_t size,
64 const unsigned char *key,
65 const unsigned char *ctr,
66 void *result)
67{
68 gcry_cipher_hd_t handle;
69 int rc;
70
71 GNUNET_assert (0 == gcry_cipher_open (&handle, GCRY_CIPHER_AES256,
72 GCRY_CIPHER_MODE_CTR, 0));
73 rc = gcry_cipher_setkey (handle,
74 key,
75 GNUNET_CRYPTO_AES_KEY_LENGTH);
76 GNUNET_assert ((0 == rc) || ((char) rc == GPG_ERR_WEAK_KEY));
77 rc = gcry_cipher_setctr (handle,
78 ctr,
79 GNUNET_CRYPTO_AES_KEY_LENGTH / 2);
80 GNUNET_assert ((0 == rc) || ((char) rc == GPG_ERR_WEAK_KEY));
81 GNUNET_assert (0 == gcry_cipher_encrypt (handle, result, size, block, size));
82 gcry_cipher_close (handle);
83 return size;
84}
85
86
87enum GNUNET_GenericReturnValue
88eddsa_symmetric_decrypt (
89 const void *block,
90 size_t size,
91 const unsigned char *key,
92 const unsigned char *nonce,
93 void *result)
94{
95 ssize_t ctlen = size - crypto_secretbox_MACBYTES;
96 if (ctlen < 0)
97 return GNUNET_SYSERR;
98 if (0 != crypto_secretbox_open_detached (result,
99 ((unsigned char*) block)
100 + crypto_secretbox_MACBYTES, // Ciphertext
101 block, // Tag
102 ctlen,
103 nonce, key))
104 {
105 return GNUNET_SYSERR;
106 }
107 return GNUNET_OK;
108}
109
110
111enum GNUNET_GenericReturnValue
112eddsa_symmetric_encrypt (
113 const void *block,
114 size_t size,
115 const unsigned char *key,
116 const unsigned char *nonce,
117 void *result)
118{
119 if (size > crypto_secretbox_MESSAGEBYTES_MAX)
120 return GNUNET_SYSERR;
121 crypto_secretbox_detached (result + crypto_secretbox_MACBYTES, // Ciphertext
122 result, // TAG
123 block, size, nonce, key);
124 return GNUNET_OK;
125}
126
127
128void
129GNR_derive_block_aes_key (unsigned char *ctr,
130 unsigned char *key,
131 const char *label,
132 uint64_t exp,
133 const struct GNUNET_CRYPTO_EcdsaPublicKey *pub)
134{
135 static const char ctx_key[] = "gns-aes-ctx-key";
136 static const char ctx_iv[] = "gns-aes-ctx-iv";
137
138 GNUNET_CRYPTO_kdf (key, GNUNET_CRYPTO_AES_KEY_LENGTH,
139 ctx_key, strlen (ctx_key),
140 pub, sizeof(struct GNUNET_CRYPTO_EcdsaPublicKey),
141 label, strlen (label),
142 NULL, 0);
143 memset (ctr, 0, GNUNET_CRYPTO_AES_KEY_LENGTH / 2);
144 /** 4 byte nonce **/
145 GNUNET_CRYPTO_kdf (ctr, 4,
146 ctx_iv, strlen (ctx_iv),
147 pub, sizeof(struct GNUNET_CRYPTO_EcdsaPublicKey),
148 label, strlen (label),
149 NULL, 0);
150 /** Expiration time 64 bit. **/
151 memcpy (ctr + 4, &exp, sizeof (exp));
152 /** Set counter part to 1 **/
153 ctr[15] |= 0x01;
154}
155
156
157void
158GNR_derive_block_xsalsa_key (unsigned char *nonce,
159 unsigned char *key,
160 const char *label,
161 uint64_t exp,
162 const struct GNUNET_CRYPTO_EddsaPublicKey *pub)
163{
164 static const char ctx_key[] = "gns-xsalsa-ctx-key";
165 static const char ctx_iv[] = "gns-xsalsa-ctx-iv";
166
167 GNUNET_CRYPTO_kdf (key, crypto_secretbox_KEYBYTES,
168 ctx_key, strlen (ctx_key),
169 pub, sizeof(struct GNUNET_CRYPTO_EddsaPublicKey),
170 label, strlen (label),
171 NULL, 0);
172 memset (nonce, 0, crypto_secretbox_NONCEBYTES);
173 /** 16 byte nonce **/
174 GNUNET_CRYPTO_kdf (nonce, (crypto_secretbox_NONCEBYTES - sizeof (exp)),
175 ctx_iv, strlen (ctx_iv),
176 pub, sizeof(struct GNUNET_CRYPTO_EddsaPublicKey),
177 label, strlen (label),
178 NULL, 0);
179 /** Expiration time 64 bit. **/
180 memcpy (nonce + (crypto_secretbox_NONCEBYTES - sizeof (exp)),
181 &exp, sizeof (exp));
182}
183
184
185static ssize_t
186block_get_size_ecdsa (const struct GNUNET_GNSRECORD_Data *rd,
187 unsigned int rd_count)
188{
189 ssize_t len;
190
191 len = GNUNET_GNSRECORD_records_get_size (rd_count, rd);
192 if (len < 0)
193 return -1;
194 len += sizeof(struct GNUNET_GNSRECORD_Block);
195 return len;
196}
197
198
199enum GNUNET_GenericReturnValue
200block_sign_ecdsa (const struct
201 GNUNET_CRYPTO_EcdsaPrivateKey *key,
202 const struct
203 GNUNET_CRYPTO_EcdsaPublicKey *pkey,
204 const char *label,
205 struct GNUNET_GNSRECORD_Block *block)
206{
207 struct GNRBlockPS *gnr_block;
208 struct GNUNET_GNSRECORD_EcdsaBlock *ecblock;
209 size_t size = ntohl (block->size) - sizeof (*block) + sizeof (*gnr_block);
210
211 gnr_block = GNUNET_malloc (size);
212 ecblock = &(block)->ecdsa_block;
213 gnr_block->purpose.size = htonl (size);
214 gnr_block->purpose.purpose =
215 htonl (GNUNET_SIGNATURE_PURPOSE_GNS_RECORD_SIGN);
216 gnr_block->expiration_time = ecblock->expiration_time;
217 /* encrypt and sign */
218 GNUNET_memcpy (&gnr_block[1], &ecblock[1],
219 size - sizeof (*gnr_block));
220 GNUNET_CRYPTO_ecdsa_public_key_derive (pkey,
221 label,
222 "gns",
223 &ecblock->derived_key);
224 if (GNUNET_OK !=
225 GNUNET_CRYPTO_ecdsa_sign_derived (key,
226 label,
227 "gns",
228 &gnr_block->purpose,
229 &ecblock->signature))
230 {
231 GNUNET_break (0);
232 GNUNET_free (gnr_block);
233 return GNUNET_SYSERR;
234 }
235 GNUNET_free (gnr_block);
236 return GNUNET_OK;
237}
238
239
240enum GNUNET_GenericReturnValue
241block_sign_eddsa (const struct
242 GNUNET_CRYPTO_EddsaPrivateKey *key,
243 const struct
244 GNUNET_CRYPTO_EddsaPublicKey *pkey,
245 const char *label,
246 struct GNUNET_GNSRECORD_Block *block)
247{
248 struct GNRBlockPS *gnr_block;
249 struct GNUNET_GNSRECORD_EddsaBlock *edblock;
250 size_t size = ntohl (block->size) - sizeof (*block) + sizeof (*gnr_block);
251 gnr_block = GNUNET_malloc (size);
252 edblock = &(block)->eddsa_block;
253 gnr_block->purpose.size = htonl (size);
254 gnr_block->purpose.purpose =
255 htonl (GNUNET_SIGNATURE_PURPOSE_GNS_RECORD_SIGN);
256 gnr_block->expiration_time = edblock->expiration_time;
257 GNUNET_memcpy (&gnr_block[1], &edblock[1],
258 size - sizeof (*gnr_block));
259 /* encrypt and sign */
260 GNUNET_CRYPTO_eddsa_public_key_derive (pkey,
261 label,
262 "gns",
263 &edblock->derived_key);
264 GNUNET_CRYPTO_eddsa_sign_derived (key,
265 label,
266 "gns",
267 &gnr_block->purpose,
268 &edblock->signature);
269 GNUNET_free (gnr_block);
270 return GNUNET_OK;
271}
272
273
274enum GNUNET_GenericReturnValue
275GNUNET_GNSRECORD_block_sign (const struct
276 GNUNET_CRYPTO_PrivateKey *key,
277 const char *label,
278 struct GNUNET_GNSRECORD_Block *block)
279{
280 struct GNUNET_CRYPTO_PublicKey pkey;
281 enum GNUNET_GenericReturnValue res = GNUNET_SYSERR;
282 char *norm_label;
283
284 GNUNET_CRYPTO_key_get_public (key,
285 &pkey);
286 norm_label = GNUNET_GNSRECORD_string_normalize (label);
287
288 switch (ntohl (key->type))
289 {
290 case GNUNET_GNSRECORD_TYPE_PKEY:
291 res = block_sign_ecdsa (&key->ecdsa_key,
292 &pkey.ecdsa_key,
293 norm_label,
294 block);
295 break;
296 case GNUNET_GNSRECORD_TYPE_EDKEY:
297 res = block_sign_eddsa (&key->eddsa_key,
298 &pkey.eddsa_key,
299 norm_label,
300 block);
301 break;
302 default:
303 GNUNET_assert (0);
304 }
305 GNUNET_free (norm_label);
306 return res;
307}
308
309
310/**
311 * Sign name and records
312 *
313 * @param key the private key
314 * @param pkey associated public key
315 * @param expire block expiration
316 * @param label the name for the records
317 * @param rd record data
318 * @param rd_count number of records
319 * @param block the block result. Must be allocated sufficiently.
320 * @param sign sign the block GNUNET_NO if block will be signed later.
321 * @return GNUNET_SYSERR on error (otherwise GNUNET_OK)
322 */
323static enum GNUNET_GenericReturnValue
324block_create_ecdsa (const struct GNUNET_CRYPTO_EcdsaPrivateKey *key,
325 const struct GNUNET_CRYPTO_EcdsaPublicKey *pkey,
326 struct GNUNET_TIME_Absolute expire,
327 const char *label,
328 const struct GNUNET_GNSRECORD_Data *rd,
329 unsigned int rd_count,
330 struct GNUNET_GNSRECORD_Block **block,
331 int sign)
332{
333 ssize_t payload_len = GNUNET_GNSRECORD_records_get_size (rd_count,
334 rd);
335 struct GNUNET_GNSRECORD_EcdsaBlock *ecblock;
336 unsigned char ctr[GNUNET_CRYPTO_AES_KEY_LENGTH / 2];
337 unsigned char skey[GNUNET_CRYPTO_AES_KEY_LENGTH];
338 struct GNUNET_GNSRECORD_Data rdc[GNUNET_NZL (rd_count)];
339 struct GNUNET_TIME_Absolute now;
340
341 if (payload_len < 0)
342 {
343 GNUNET_break (0);
344 return GNUNET_SYSERR;
345 }
346 if (payload_len > GNUNET_GNSRECORD_MAX_BLOCK_SIZE)
347 {
348 GNUNET_break (0);
349 return GNUNET_SYSERR;
350 }
351 /* convert relative to absolute times */
352 now = GNUNET_TIME_absolute_get ();
353 for (unsigned int i = 0; i < rd_count; i++)
354 {
355 rdc[i] = rd[i];
356 if (0 != (rd[i].flags & GNUNET_GNSRECORD_RF_RELATIVE_EXPIRATION))
357 {
358 struct GNUNET_TIME_Relative t;
359
360 /* encrypted blocks must never have relative expiration times, convert! */
361 rdc[i].flags &= ~GNUNET_GNSRECORD_RF_RELATIVE_EXPIRATION;
362 t.rel_value_us = rdc[i].expiration_time;
363 rdc[i].expiration_time = GNUNET_TIME_absolute_add (now, t).abs_value_us;
364 }
365 }
366 /* serialize */
367 *block = GNUNET_malloc (sizeof (struct GNUNET_GNSRECORD_Block) + payload_len);
368 (*block)->size = htonl (sizeof (struct GNUNET_GNSRECORD_Block) + payload_len);
369 {
370 char payload[payload_len];
371
372 GNUNET_assert (payload_len ==
373 GNUNET_GNSRECORD_records_serialize (rd_count,
374 rdc,
375 payload_len,
376 payload));
377 ecblock = &(*block)->ecdsa_block;
378 (*block)->type = htonl (GNUNET_GNSRECORD_TYPE_PKEY);
379 ecblock->expiration_time = GNUNET_TIME_absolute_hton (expire);
380 GNR_derive_block_aes_key (ctr,
381 skey,
382 label,
383 ecblock->expiration_time.abs_value_us__,
384 pkey);
385 GNUNET_assert (payload_len ==
386 ecdsa_symmetric_encrypt (payload,
387 payload_len,
388 skey,
389 ctr,
390 &ecblock[1]));
391 }
392 if (GNUNET_YES != sign)
393 return GNUNET_OK;
394 if (GNUNET_OK !=
395 block_sign_ecdsa (key, pkey, label, *block))
396 {
397 GNUNET_break (0);
398 GNUNET_free (*block);
399 return GNUNET_SYSERR;
400 }
401 return GNUNET_OK;
402}
403
404
405static ssize_t
406block_get_size_eddsa (const struct GNUNET_GNSRECORD_Data *rd,
407 unsigned int rd_count)
408{
409 ssize_t len;
410
411 len = GNUNET_GNSRECORD_records_get_size (rd_count, rd);
412 if (len < 0)
413 return -1;
414 len += sizeof(struct GNUNET_GNSRECORD_Block);
415 len += crypto_secretbox_MACBYTES;
416 return len;
417}
418
419
420/**
421 * Sign name and records (EDDSA version)
422 *
423 * @param key the private key
424 * @param pkey associated public key
425 * @param expire block expiration
426 * @param label the name for the records
427 * @param rd record data
428 * @param rd_count number of records
429 * @param block where to store the block. Must be allocated sufficiently.
430 * @param sign GNUNET_YES if block shall be signed as well
431 * @return GNUNET_SYSERR on error (otherwise GNUNET_OK)
432 */
433enum GNUNET_GenericReturnValue
434block_create_eddsa (const struct GNUNET_CRYPTO_EddsaPrivateKey *key,
435 const struct GNUNET_CRYPTO_EddsaPublicKey *pkey,
436 struct GNUNET_TIME_Absolute expire,
437 const char *label,
438 const struct GNUNET_GNSRECORD_Data *rd,
439 unsigned int rd_count,
440 struct GNUNET_GNSRECORD_Block **block,
441 int sign)
442{
443 ssize_t payload_len = GNUNET_GNSRECORD_records_get_size (rd_count,
444 rd);
445 struct GNUNET_GNSRECORD_EddsaBlock *edblock;
446 unsigned char nonce[crypto_secretbox_NONCEBYTES];
447 unsigned char skey[crypto_secretbox_KEYBYTES];
448 struct GNUNET_GNSRECORD_Data rdc[GNUNET_NZL (rd_count)];
449 struct GNUNET_TIME_Absolute now;
450
451 if (payload_len < 0)
452 {
453 GNUNET_break (0);
454 return GNUNET_SYSERR;
455 }
456 if (payload_len > GNUNET_GNSRECORD_MAX_BLOCK_SIZE)
457 {
458 GNUNET_break (0);
459 return GNUNET_SYSERR;
460 }
461 /* convert relative to absolute times */
462 now = GNUNET_TIME_absolute_get ();
463 for (unsigned int i = 0; i < rd_count; i++)
464 {
465 rdc[i] = rd[i];
466 if (0 != (rd[i].flags & GNUNET_GNSRECORD_RF_RELATIVE_EXPIRATION))
467 {
468 struct GNUNET_TIME_Relative t;
469
470 /* encrypted blocks must never have relative expiration times, convert! */
471 rdc[i].flags &= ~GNUNET_GNSRECORD_RF_RELATIVE_EXPIRATION;
472 t.rel_value_us = rdc[i].expiration_time;
473 rdc[i].expiration_time = GNUNET_TIME_absolute_add (now, t).abs_value_us;
474 }
475 }
476 /* serialize */
477 *block = GNUNET_malloc (sizeof (struct GNUNET_GNSRECORD_Block)
478 + payload_len + crypto_secretbox_MACBYTES);
479 (*block)->size = htonl (sizeof (struct GNUNET_GNSRECORD_Block)
480 + payload_len + crypto_secretbox_MACBYTES);
481 {
482 char payload[payload_len];
483
484 GNUNET_assert (payload_len ==
485 GNUNET_GNSRECORD_records_serialize (rd_count,
486 rdc,
487 payload_len,
488 payload));
489 edblock = &(*block)->eddsa_block;
490 (*block)->type = htonl (GNUNET_GNSRECORD_TYPE_EDKEY);
491 edblock->expiration_time = GNUNET_TIME_absolute_hton (expire);
492 GNR_derive_block_xsalsa_key (nonce,
493 skey,
494 label,
495 edblock->expiration_time.abs_value_us__,
496 pkey);
497 GNUNET_assert (GNUNET_OK ==
498 eddsa_symmetric_encrypt (payload,
499 payload_len,
500 skey,
501 nonce,
502 &edblock[1]));
503 if (GNUNET_YES != sign)
504 return GNUNET_OK;
505 block_sign_eddsa (key, pkey, label, *block);
506 }
507 return GNUNET_OK;
508}
509
510
511ssize_t
512GNUNET_GNSRECORD_block_calculate_size (const struct
513 GNUNET_CRYPTO_PrivateKey *key,
514 const struct GNUNET_GNSRECORD_Data *rd,
515 unsigned int rd_count)
516{
517 struct GNUNET_CRYPTO_PublicKey pkey;
518 ssize_t res = -1;
519
520 GNUNET_CRYPTO_key_get_public (key,
521 &pkey);
522 switch (ntohl (key->type))
523 {
524 case GNUNET_GNSRECORD_TYPE_PKEY:
525 res = block_get_size_ecdsa (rd, rd_count);
526 break;
527 case GNUNET_GNSRECORD_TYPE_EDKEY:
528 res = block_get_size_eddsa (rd, rd_count);
529 break;
530 default:
531 GNUNET_assert (0);
532 }
533 return res;
534
535}
536
537
538enum GNUNET_GenericReturnValue
539GNUNET_GNSRECORD_block_create (const struct GNUNET_CRYPTO_PrivateKey *key,
540 struct GNUNET_TIME_Absolute expire,
541 const char *label,
542 const struct GNUNET_GNSRECORD_Data *rd,
543 unsigned int rd_count,
544 struct GNUNET_GNSRECORD_Block **result)
545{
546 struct GNUNET_CRYPTO_PublicKey pkey;
547 enum GNUNET_GenericReturnValue res = GNUNET_SYSERR;
548 char *norm_label;
549
550 GNUNET_CRYPTO_key_get_public (key,
551 &pkey);
552 norm_label = GNUNET_GNSRECORD_string_normalize (label);
553
554 switch (ntohl (key->type))
555 {
556 case GNUNET_GNSRECORD_TYPE_PKEY:
557 res = block_create_ecdsa (&key->ecdsa_key,
558 &pkey.ecdsa_key,
559 expire,
560 norm_label,
561 rd,
562 rd_count,
563 result,
564 GNUNET_YES);
565 break;
566 case GNUNET_GNSRECORD_TYPE_EDKEY:
567 res = block_create_eddsa (&key->eddsa_key,
568 &pkey.eddsa_key,
569 expire,
570 norm_label,
571 rd,
572 rd_count,
573 result,
574 GNUNET_YES);
575 break;
576 default:
577 GNUNET_assert (0);
578 }
579 GNUNET_free (norm_label);
580 return res;
581}
582
583
584/**
585 * Line in cache mapping private keys to public keys.
586 */
587struct KeyCacheLine
588{
589 /**
590 * A private key.
591 */
592 struct GNUNET_CRYPTO_EcdsaPrivateKey key;
593
594 /**
595 * Associated public key.
596 */
597 struct GNUNET_CRYPTO_EcdsaPublicKey pkey;
598};
599
600
601static enum GNUNET_GenericReturnValue
602block_create2 (const struct GNUNET_CRYPTO_PrivateKey *pkey,
603 struct GNUNET_TIME_Absolute expire,
604 const char *label,
605 const struct GNUNET_GNSRECORD_Data *rd,
606 unsigned int rd_count,
607 struct GNUNET_GNSRECORD_Block **result,
608 int sign)
609{
610 const struct GNUNET_CRYPTO_EcdsaPrivateKey *key;
611 struct GNUNET_CRYPTO_EddsaPublicKey edpubkey;
612 enum GNUNET_GenericReturnValue res = GNUNET_SYSERR;
613 char *norm_label;
614
615 norm_label = GNUNET_GNSRECORD_string_normalize (label);
616
617 if (GNUNET_PUBLIC_KEY_TYPE_ECDSA == ntohl (pkey->type))
618 {
619 key = &pkey->ecdsa_key;
620#define CSIZE 64
621 static struct KeyCacheLine cache[CSIZE];
622 struct KeyCacheLine *line;
623
624 line = &cache[(*(unsigned int *) key) % CSIZE];
625 if (0 != memcmp (&line->key,
626 key,
627 sizeof(*key)))
628 {
629 /* cache miss, recompute */
630 line->key = *key;
631 GNUNET_CRYPTO_ecdsa_key_get_public (key,
632 &line->pkey);
633 }
634#undef CSIZE
635 res = block_create_ecdsa (key,
636 &line->pkey,
637 expire,
638 norm_label,
639 rd,
640 rd_count,
641 result,
642 sign);
643 }
644 else if (GNUNET_PUBLIC_KEY_TYPE_EDDSA == ntohl (pkey->type))
645 {
646 GNUNET_CRYPTO_eddsa_key_get_public (&pkey->eddsa_key,
647 &edpubkey);
648 res = block_create_eddsa (&pkey->eddsa_key,
649 &edpubkey,
650 expire,
651 norm_label,
652 rd,
653 rd_count,
654 result,
655 sign);
656 }
657 GNUNET_free (norm_label);
658 return res;
659}
660
661
662enum GNUNET_GenericReturnValue
663GNUNET_GNSRECORD_block_create_unsigned (const struct
664 GNUNET_CRYPTO_PrivateKey *pkey,
665 struct GNUNET_TIME_Absolute expire,
666 const char *label,
667 const struct GNUNET_GNSRECORD_Data *rd,
668 unsigned int rd_count,
669 struct GNUNET_GNSRECORD_Block **result)
670{
671 return block_create2 (pkey, expire, label, rd, rd_count, result, GNUNET_NO);
672}
673
674
675enum GNUNET_GenericReturnValue
676GNUNET_GNSRECORD_block_create2 (const struct GNUNET_CRYPTO_PrivateKey *pkey,
677 struct GNUNET_TIME_Absolute expire,
678 const char *label,
679 const struct GNUNET_GNSRECORD_Data *rd,
680 unsigned int rd_count,
681 struct GNUNET_GNSRECORD_Block **result)
682{
683 return block_create2 (pkey, expire, label, rd, rd_count, result, GNUNET_YES);
684}
685
686
687/**
688 * Check if a signature is valid. This API is used by the GNS Block
689 * to validate signatures received from the network.
690 *
691 * @param block block to verify
692 * @return #GNUNET_OK if the signature is valid
693 */
694enum GNUNET_GenericReturnValue
695GNUNET_GNSRECORD_block_verify (const struct GNUNET_GNSRECORD_Block *block)
696{
697 struct GNRBlockPS *purp;
698 size_t payload_len = ntohl (block->size)
699 - sizeof (struct GNUNET_GNSRECORD_Block);
700 enum GNUNET_GenericReturnValue res = GNUNET_NO;
701 purp = GNUNET_malloc (sizeof (struct GNRBlockPS) + payload_len);
702 purp->purpose.size = htonl (sizeof (struct GNRBlockPS) + payload_len);
703 purp->purpose.purpose = htonl (GNUNET_SIGNATURE_PURPOSE_GNS_RECORD_SIGN);
704 GNUNET_memcpy (&purp[1],
705 &block[1],
706 payload_len);
707 switch (ntohl (block->type))
708 {
709 case GNUNET_GNSRECORD_TYPE_PKEY:
710 purp->expiration_time = block->ecdsa_block.expiration_time;
711 res = GNUNET_CRYPTO_ecdsa_verify_ (
712 GNUNET_SIGNATURE_PURPOSE_GNS_RECORD_SIGN,
713 &purp->purpose,
714 &block->ecdsa_block.signature,
715 &block->ecdsa_block.derived_key);
716 break;
717 case GNUNET_GNSRECORD_TYPE_EDKEY:
718 purp->expiration_time = block->eddsa_block.expiration_time;
719 res = GNUNET_CRYPTO_eddsa_verify_ (
720 GNUNET_SIGNATURE_PURPOSE_GNS_RECORD_SIGN,
721 &purp->purpose,
722 &block->eddsa_block.signature,
723 &block->eddsa_block.derived_key);
724 break;
725 default:
726 res = GNUNET_NO;
727 }
728 GNUNET_free (purp);
729 return res;
730}
731
732
733enum GNUNET_GenericReturnValue
734block_decrypt_ecdsa (const struct GNUNET_GNSRECORD_Block *block,
735 const struct
736 GNUNET_CRYPTO_EcdsaPublicKey *zone_key,
737 const char *label,
738 GNUNET_GNSRECORD_RecordCallback proc,
739 void *proc_cls)
740{
741 size_t payload_len = ntohl (block->size) - sizeof (struct
742 GNUNET_GNSRECORD_Block);
743 unsigned char ctr[GNUNET_CRYPTO_AES_KEY_LENGTH / 2];
744 unsigned char key[GNUNET_CRYPTO_AES_KEY_LENGTH];
745
746 if (ntohl (block->size) <
747 sizeof(struct GNUNET_CRYPTO_EccSignaturePurpose)
748 + sizeof(struct GNUNET_TIME_AbsoluteNBO))
749 {
750 GNUNET_break_op (0);
751 return GNUNET_SYSERR;
752 }
753 GNR_derive_block_aes_key (ctr,
754 key,
755 label,
756 block->ecdsa_block.expiration_time.abs_value_us__,
757 zone_key);
758 {
759 char payload[payload_len];
760 unsigned int rd_count;
761
762 GNUNET_assert (payload_len ==
763 ecdsa_symmetric_decrypt (&block[1], payload_len,
764 key, ctr,
765 payload));
766 rd_count = GNUNET_GNSRECORD_records_deserialize_get_size (payload_len,
767 payload);
768 if (rd_count > 2048)
769 {
770 /* limit to sane value */
771 GNUNET_break_op (0);
772 return GNUNET_SYSERR;
773 }
774 {
775 struct GNUNET_GNSRECORD_Data rd[GNUNET_NZL (rd_count)];
776 unsigned int j;
777 struct GNUNET_TIME_Absolute now;
778
779 if (GNUNET_OK !=
780 GNUNET_GNSRECORD_records_deserialize (payload_len,
781 payload,
782 rd_count,
783 rd))
784 {
785 GNUNET_break_op (0);
786 return GNUNET_SYSERR;
787 }
788 /* hide expired records */
789 now = GNUNET_TIME_absolute_get ();
790 j = 0;
791 for (unsigned int i = 0; i < rd_count; i++)
792 {
793 if (0 != (rd[i].flags & GNUNET_GNSRECORD_RF_RELATIVE_EXPIRATION))
794 {
795 /* encrypted blocks must never have relative expiration times, skip! */
796 GNUNET_break_op (0);
797 continue;
798 }
799
800 if (0 != (rd[i].flags & GNUNET_GNSRECORD_RF_SHADOW))
801 {
802 int include_record = GNUNET_YES;
803 /* Shadow record, figure out if we have a not expired active record */
804 for (unsigned int k = 0; k < rd_count; k++)
805 {
806 if (k == i)
807 continue;
808 if (rd[i].expiration_time < now.abs_value_us)
809 include_record = GNUNET_NO; /* Shadow record is expired */
810 if ((rd[k].record_type == rd[i].record_type) &&
811 (rd[k].expiration_time >= now.abs_value_us) &&
812 (0 == (rd[k].flags & GNUNET_GNSRECORD_RF_SHADOW)))
813 {
814 include_record = GNUNET_NO; /* We have a non-expired, non-shadow record of the same type */
815 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
816 "Ignoring shadow record\n");
817 break;
818 }
819 }
820 if (GNUNET_YES == include_record)
821 {
822 rd[i].flags ^= GNUNET_GNSRECORD_RF_SHADOW; /* Remove Flag */
823 if (j != i)
824 rd[j] = rd[i];
825 j++;
826 }
827 }
828 else if (rd[i].expiration_time >= now.abs_value_us)
829 {
830 /* Include this record */
831 if (j != i)
832 rd[j] = rd[i];
833 j++;
834 }
835 else
836 {
837 struct GNUNET_TIME_Absolute at;
838
839 at.abs_value_us = rd[i].expiration_time;
840 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
841 "Excluding record that expired %s (%llu ago)\n",
842 GNUNET_STRINGS_absolute_time_to_string (at),
843 (unsigned long long) rd[i].expiration_time
844 - now.abs_value_us);
845 }
846 }
847 rd_count = j;
848 if (NULL != proc)
849 proc (proc_cls,
850 rd_count,
851 (0 != rd_count) ? rd : NULL);
852 }
853 }
854 return GNUNET_OK;
855}
856
857
858enum GNUNET_GenericReturnValue
859block_decrypt_eddsa (const struct GNUNET_GNSRECORD_Block *block,
860 const struct
861 GNUNET_CRYPTO_EddsaPublicKey *zone_key,
862 const char *label,
863 GNUNET_GNSRECORD_RecordCallback proc,
864 void *proc_cls)
865{
866 size_t payload_len = ntohl (block->size) - sizeof (struct
867 GNUNET_GNSRECORD_Block);
868 unsigned char nonce[crypto_secretbox_NONCEBYTES];
869 unsigned char key[crypto_secretbox_KEYBYTES];
870
871 if (ntohl (block->size) <
872 sizeof(struct GNUNET_CRYPTO_EccSignaturePurpose)
873 + sizeof(struct GNUNET_TIME_AbsoluteNBO))
874 {
875 GNUNET_break_op (0);
876 return GNUNET_SYSERR;
877 }
878 GNR_derive_block_xsalsa_key (nonce,
879 key,
880 label,
881 block->eddsa_block.expiration_time.abs_value_us__,
882 zone_key);
883 {
884 char payload[payload_len];
885 unsigned int rd_count;
886
887 GNUNET_assert (GNUNET_OK ==
888 eddsa_symmetric_decrypt (&block[1], payload_len,
889 key, nonce,
890 payload));
891 payload_len -= crypto_secretbox_MACBYTES;
892 rd_count = GNUNET_GNSRECORD_records_deserialize_get_size (payload_len,
893 payload);
894 if (rd_count > 2048)
895 {
896 /* limit to sane value */
897 GNUNET_break_op (0);
898 return GNUNET_SYSERR;
899 }
900 {
901 struct GNUNET_GNSRECORD_Data rd[GNUNET_NZL (rd_count)];
902 unsigned int j;
903 struct GNUNET_TIME_Absolute now;
904
905 if (GNUNET_OK !=
906 GNUNET_GNSRECORD_records_deserialize (payload_len,
907 payload,
908 rd_count,
909 rd))
910 {
911 GNUNET_break_op (0);
912 return GNUNET_SYSERR;
913 }
914 /* hide expired records */
915 now = GNUNET_TIME_absolute_get ();
916 j = 0;
917 for (unsigned int i = 0; i < rd_count; i++)
918 {
919 if (0 != (rd[i].flags & GNUNET_GNSRECORD_RF_RELATIVE_EXPIRATION))
920 {
921 /* encrypted blocks must never have relative expiration times, skip! */
922 GNUNET_break_op (0);
923 continue;
924 }
925
926 if (0 != (rd[i].flags & GNUNET_GNSRECORD_RF_SHADOW))
927 {
928 int include_record = GNUNET_YES;
929 /* Shadow record, figure out if we have a not expired active record */
930 for (unsigned int k = 0; k < rd_count; k++)
931 {
932 if (k == i)
933 continue;
934 if (rd[i].expiration_time < now.abs_value_us)
935 include_record = GNUNET_NO; /* Shadow record is expired */
936 if ((rd[k].record_type == rd[i].record_type) &&
937 (rd[k].expiration_time >= now.abs_value_us) &&
938 (0 == (rd[k].flags & GNUNET_GNSRECORD_RF_SHADOW)))
939 {
940 include_record = GNUNET_NO; /* We have a non-expired, non-shadow record of the same type */
941 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
942 "Ignoring shadow record\n");
943 break;
944 }
945 }
946 if (GNUNET_YES == include_record)
947 {
948 rd[i].flags ^= GNUNET_GNSRECORD_RF_SHADOW; /* Remove Flag */
949 if (j != i)
950 rd[j] = rd[i];
951 j++;
952 }
953 }
954 else if (rd[i].expiration_time >= now.abs_value_us)
955 {
956 /* Include this record */
957 if (j != i)
958 rd[j] = rd[i];
959 j++;
960 }
961 else
962 {
963 struct GNUNET_TIME_Absolute at;
964
965 at.abs_value_us = rd[i].expiration_time;
966 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
967 "Excluding record that expired %s (%llu ago)\n",
968 GNUNET_STRINGS_absolute_time_to_string (at),
969 (unsigned long long) rd[i].expiration_time
970 - now.abs_value_us);
971 }
972 }
973 rd_count = j;
974 if (NULL != proc)
975 proc (proc_cls,
976 rd_count,
977 (0 != rd_count) ? rd : NULL);
978 }
979 }
980 return GNUNET_OK;
981}
982
983
984enum GNUNET_GenericReturnValue
985GNUNET_GNSRECORD_block_decrypt (const struct GNUNET_GNSRECORD_Block *block,
986 const struct
987 GNUNET_CRYPTO_PublicKey *zone_key,
988 const char *label,
989 GNUNET_GNSRECORD_RecordCallback proc,
990 void *proc_cls)
991{
992 enum GNUNET_GenericReturnValue res = GNUNET_SYSERR;
993 char *norm_label;
994
995 norm_label = GNUNET_GNSRECORD_string_normalize (label);
996 switch (ntohl (zone_key->type))
997 {
998 case GNUNET_PUBLIC_KEY_TYPE_ECDSA:
999 res = block_decrypt_ecdsa (block,
1000 &zone_key->ecdsa_key, norm_label, proc,
1001 proc_cls);
1002 break;
1003 case GNUNET_PUBLIC_KEY_TYPE_EDDSA:
1004 res = block_decrypt_eddsa (block,
1005 &zone_key->eddsa_key, norm_label, proc,
1006 proc_cls);
1007 break;
1008 default:
1009 res = GNUNET_SYSERR;
1010 }
1011 GNUNET_free (norm_label);
1012 return res;
1013}
1014
1015
1016/**
1017 * Calculate the DHT query for a given @a label in a given @a zone.
1018 *
1019 * @param zone private key of the zone
1020 * @param label label of the record
1021 * @param query hash to use for the query
1022 */
1023void
1024GNUNET_GNSRECORD_query_from_private_key (const struct
1025 GNUNET_CRYPTO_PrivateKey *zone,
1026 const char *label,
1027 struct GNUNET_HashCode *query)
1028{
1029 char *norm_label;
1030 struct GNUNET_CRYPTO_PublicKey pub;
1031
1032 norm_label = GNUNET_GNSRECORD_string_normalize (label);
1033 switch (ntohl (zone->type))
1034 {
1035 case GNUNET_GNSRECORD_TYPE_PKEY:
1036 case GNUNET_GNSRECORD_TYPE_EDKEY:
1037
1038 GNUNET_CRYPTO_key_get_public (zone,
1039 &pub);
1040 GNUNET_GNSRECORD_query_from_public_key (&pub,
1041 norm_label,
1042 query);
1043 break;
1044 default:
1045 GNUNET_assert (0);
1046 }
1047 GNUNET_free (norm_label);
1048}
1049
1050
1051void
1052GNUNET_GNSRECORD_query_from_public_key (const struct
1053 GNUNET_CRYPTO_PublicKey *pub,
1054 const char *label,
1055 struct GNUNET_HashCode *query)
1056{
1057 char *norm_label;
1058 struct GNUNET_CRYPTO_PublicKey pd;
1059
1060 norm_label = GNUNET_GNSRECORD_string_normalize (label);
1061
1062 switch (ntohl (pub->type))
1063 {
1064 case GNUNET_GNSRECORD_TYPE_PKEY:
1065 pd.type = pub->type;
1066 GNUNET_CRYPTO_ecdsa_public_key_derive (&pub->ecdsa_key,
1067 norm_label,
1068 "gns",
1069 &pd.ecdsa_key);
1070 GNUNET_CRYPTO_hash (&pd.ecdsa_key,
1071 sizeof (pd.ecdsa_key),
1072 query);
1073 break;
1074 case GNUNET_GNSRECORD_TYPE_EDKEY:
1075 pd.type = pub->type;
1076 GNUNET_CRYPTO_eddsa_public_key_derive (&pub->eddsa_key,
1077 norm_label,
1078 "gns",
1079 &(pd.eddsa_key));
1080 GNUNET_CRYPTO_hash (&pd.eddsa_key,
1081 sizeof (pd.eddsa_key),
1082 query);
1083 break;
1084 default:
1085 GNUNET_assert (0);
1086 }
1087 GNUNET_free (norm_label);
1088}
1089
1090
1091/* end of gnsrecord_crypto.c */
diff --git a/src/lib/gnsrecord/gnsrecord_crypto.h b/src/lib/gnsrecord/gnsrecord_crypto.h
new file mode 100644
index 000000000..85f2258ab
--- /dev/null
+++ b/src/lib/gnsrecord/gnsrecord_crypto.h
@@ -0,0 +1,94 @@
1/*
2 This file is part of GNUnet.
3 Copyright (C) 2009-2013, 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/**
22 * @file gnsrecord/gnsrecord_crypto.h
23 * @brief API for GNS record-related crypto
24 * @author Martin Schanzenbach
25 * @author Matthias Wachs
26 * @author Christian Grothoff
27 */
28#include "platform.h"
29#include "gnunet_util_lib.h"
30#include "gnunet_constants.h"
31#include "gnunet_signatures.h"
32#include "gnunet_arm_service.h"
33#include "gnunet_gnsrecord_lib.h"
34
35/**
36 * Information we have in an encrypted block with record data (i.e. in the DHT).
37 */
38struct GNRBlockPS
39{
40 /**
41 * Number of bytes signed; also specifies the number of bytes
42 * of encrypted data that follow.
43 */
44 struct GNUNET_CRYPTO_EccSignaturePurpose purpose;
45
46 /**
47 * Expiration time of the block.
48 */
49 struct GNUNET_TIME_AbsoluteNBO expiration_time;
50
51 /* followed by encrypted data */
52};
53
54
55/**
56 * Derive session key and iv from label and public key.
57 *
58 * @param iv initialization vector to initialize
59 * @param skey session key to initialize
60 * @param label label to use for KDF
61 * @param pub public key to use for KDF
62 */
63void
64GNR_derive_block_aes_key (unsigned char *ctr,
65 unsigned char *key,
66 const char *label,
67 uint64_t exp,
68 const struct GNUNET_CRYPTO_EcdsaPublicKey *pub);
69
70
71/**
72 * Derive session key and iv from label and public key.
73 *
74 * @param nonce initialization vector to initialize
75 * @param skey session key to initialize
76 * @param label label to use for KDF
77 * @param pub public key to use for KDF
78 */
79void
80GNR_derive_block_xsalsa_key (unsigned char *nonce,
81 unsigned char *key,
82 const char *label,
83 uint64_t exp,
84 const struct GNUNET_CRYPTO_EddsaPublicKey *pub);
85
86/**
87 * Create the revocation metadata to sign for a revocation message
88 *
89 * @param pow the PoW to sign
90 * @return the signature purpose
91 */
92struct GNUNET_GNSRECORD_SignaturePurposePS *
93GNR_create_signature_message (const struct GNUNET_GNSRECORD_PowP *pow);
94
diff --git a/src/lib/gnsrecord/gnsrecord_misc.c b/src/lib/gnsrecord/gnsrecord_misc.c
new file mode 100644
index 000000000..ede3e23e4
--- /dev/null
+++ b/src/lib/gnsrecord/gnsrecord_misc.c
@@ -0,0 +1,566 @@
1/*
2 This file is part of GNUnet.
3 Copyright (C) 2009-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 gnsrecord/gnsrecord_misc.c
23 * @brief MISC functions related to GNS records
24 * @author Martin Schanzenbach
25 * @author Matthias Wachs
26 * @author Christian Grothoff
27 */
28#include "platform.h"
29#include "gnunet_util_lib.h"
30#include "gnunet_constants.h"
31#include "gnunet_signatures.h"
32#include "gnunet_arm_service.h"
33#include "gnunet_gnsrecord_lib.h"
34
35
36#define LOG(kind, ...) GNUNET_log_from (kind, "gnsrecord", __VA_ARGS__)
37
38char *
39GNUNET_GNSRECORD_string_normalize (const char *src)
40{
41 /*FIXME: We may want to follow RFC5890/RFC5891 */
42 return GNUNET_STRINGS_utf8_normalize (src);
43}
44
45
46enum GNUNET_GenericReturnValue
47GNUNET_GNSRECORD_label_check (const char*label, char **emsg)
48{
49 if (NULL == label)
50 {
51 *emsg = GNUNET_strdup (_ ("Label is NULL which is not allowed\n"));
52 return GNUNET_NO;
53 }
54 if (0 != strchr (label, '.'))
55 {
56 *emsg = GNUNET_strdup (_ ("Label contains `.' which is not allowed\n"));
57 return GNUNET_NO;
58 }
59 return GNUNET_OK;
60}
61
62
63const char *
64GNUNET_GNSRECORD_z2s (const struct GNUNET_CRYPTO_PublicKey *z)
65{
66 static char buf[sizeof(struct GNUNET_CRYPTO_PublicKey) * 8];
67 char *end;
68
69 end = GNUNET_STRINGS_data_to_string ((const unsigned char *) z,
70 sizeof(struct
71 GNUNET_CRYPTO_PublicKey),
72 buf, sizeof(buf));
73 if (NULL == end)
74 {
75 GNUNET_break (0);
76 return NULL;
77 }
78 *end = '\0';
79 return buf;
80}
81
82
83/**
84 * Compares if two records are equal (ignoring flags such
85 * as authority, private and pending, but not relative vs.
86 * absolute expiration time).
87 *
88 * @param a record
89 * @param b record
90 * @return #GNUNET_YES if the records are equal or #GNUNET_NO if they are not
91 */
92enum GNUNET_GenericReturnValue
93GNUNET_GNSRECORD_records_cmp (const struct GNUNET_GNSRECORD_Data *a,
94 const struct GNUNET_GNSRECORD_Data *b)
95{
96 LOG (GNUNET_ERROR_TYPE_DEBUG,
97 "Comparing records\n");
98 if (a->record_type != b->record_type)
99 {
100 LOG (GNUNET_ERROR_TYPE_DEBUG,
101 "Record type %u != %u\n", a->record_type, b->record_type);
102 return GNUNET_NO;
103 }
104 if ((a->expiration_time != b->expiration_time) &&
105 ((a->expiration_time != 0) && (b->expiration_time != 0)))
106 {
107 LOG (GNUNET_ERROR_TYPE_DEBUG,
108 "Expiration time %llu != %llu\n",
109 (unsigned long long) a->expiration_time,
110 (unsigned long long) b->expiration_time);
111 return GNUNET_NO;
112 }
113 if ((a->flags & GNUNET_GNSRECORD_RF_RCMP_FLAGS)
114 != (b->flags & GNUNET_GNSRECORD_RF_RCMP_FLAGS))
115 {
116 LOG (GNUNET_ERROR_TYPE_DEBUG,
117 "Flags %u (%u) != %u (%u)\n", a->flags,
118 a->flags & GNUNET_GNSRECORD_RF_RCMP_FLAGS, b->flags,
119 b->flags & GNUNET_GNSRECORD_RF_RCMP_FLAGS);
120 return GNUNET_NO;
121 }
122 if (a->data_size != b->data_size)
123 {
124 LOG (GNUNET_ERROR_TYPE_DEBUG,
125 "Data size %llu != %llu\n",
126 (unsigned long long) a->data_size,
127 (unsigned long long) b->data_size);
128 return GNUNET_NO;
129 }
130 if (0 != memcmp (a->data, b->data, a->data_size))
131 {
132 LOG (GNUNET_ERROR_TYPE_DEBUG,
133 "Data contents do not match\n");
134 return GNUNET_NO;
135 }
136 LOG (GNUNET_ERROR_TYPE_DEBUG,
137 "Records are equal\n");
138 return GNUNET_YES;
139}
140
141
142struct GNUNET_TIME_Absolute
143GNUNET_GNSRECORD_record_get_expiration_time (unsigned int rd_count,
144 const struct
145 GNUNET_GNSRECORD_Data *rd,
146 struct GNUNET_TIME_Absolute min)
147{
148 struct GNUNET_TIME_Absolute expire;
149 struct GNUNET_TIME_Absolute at;
150 struct GNUNET_TIME_Relative rt;
151 struct GNUNET_TIME_Absolute at_shadow;
152 struct GNUNET_TIME_Relative rt_shadow;
153
154 if (0 == rd_count)
155 return GNUNET_TIME_absolute_max (GNUNET_TIME_UNIT_ZERO_ABS, min);
156 expire = GNUNET_TIME_UNIT_FOREVER_ABS;
157 for (unsigned int c = 0; c < rd_count; c++)
158 {
159 if (0 != (rd[c].flags & GNUNET_GNSRECORD_RF_RELATIVE_EXPIRATION))
160 {
161 rt.rel_value_us = rd[c].expiration_time;
162 at = GNUNET_TIME_relative_to_absolute (rt);
163 }
164 else
165 {
166 at.abs_value_us = rd[c].expiration_time;
167 }
168
169 for (unsigned int c2 = 0; c2 < rd_count; c2++)
170 {
171 /* Check for shadow record */
172 if ((c == c2) ||
173 (rd[c].record_type != rd[c2].record_type) ||
174 (0 == (rd[c2].flags & GNUNET_GNSRECORD_RF_SHADOW)))
175 continue;
176 /* We have a shadow record */
177 if (0 != (rd[c2].flags & GNUNET_GNSRECORD_RF_RELATIVE_EXPIRATION))
178 {
179 rt_shadow.rel_value_us = rd[c2].expiration_time;
180 at_shadow = GNUNET_TIME_relative_to_absolute (rt_shadow);
181 }
182 else
183 {
184 at_shadow.abs_value_us = rd[c2].expiration_time;
185 }
186 at = GNUNET_TIME_absolute_max (at,
187 at_shadow);
188 }
189 expire = GNUNET_TIME_absolute_min (at,
190 expire);
191 }
192 expire = GNUNET_TIME_absolute_max (expire, min);
193 LOG (GNUNET_ERROR_TYPE_DEBUG,
194 "Determined expiration time for block with %u records to be %s\n",
195 rd_count,
196 GNUNET_STRINGS_absolute_time_to_string (expire));
197 return expire;
198}
199
200
201/**
202 * Test if a given record is expired.
203 *
204 * @return #GNUNET_YES if the record is expired,
205 * #GNUNET_NO if not
206 */
207int
208GNUNET_GNSRECORD_is_expired (const struct GNUNET_GNSRECORD_Data *rd)
209{
210 struct GNUNET_TIME_Absolute at;
211
212 if (0 != (rd->flags & GNUNET_GNSRECORD_RF_RELATIVE_EXPIRATION))
213 return GNUNET_NO;
214 at.abs_value_us = rd->expiration_time;
215 return (0 == GNUNET_TIME_absolute_get_remaining (at).rel_value_us) ?
216 GNUNET_YES : GNUNET_NO;
217}
218
219
220/**
221 * Convert public key to the respective absolute domain name in the
222 * ".zkey" pTLD.
223 * This is one of the very few calls in the entire API that is
224 * NOT reentrant!
225 *
226 * @param pkey a public key with a point on the eliptic curve
227 * @return string "X.zkey" where X is the public
228 * key in an encoding suitable for DNS labels.
229 */
230const char *
231GNUNET_GNSRECORD_pkey_to_zkey (const struct GNUNET_CRYPTO_PublicKey *pkey)
232{
233 static char ret[128];
234 char *pkeys;
235
236 pkeys = GNUNET_CRYPTO_public_key_to_string (pkey);
237 GNUNET_snprintf (ret,
238 sizeof(ret),
239 "%s",
240 pkeys);
241 GNUNET_free (pkeys);
242 return ret;
243}
244
245
246/**
247 * Convert an absolute domain name to the
248 * respective public key.
249 *
250 * @param zkey string encoding the coordinates of the public
251 * key in an encoding suitable for DNS labels.
252 * @param pkey set to a public key on the eliptic curve
253 * @return #GNUNET_SYSERR if @a zkey has the wrong syntax
254 */
255int
256GNUNET_GNSRECORD_zkey_to_pkey (const char *zkey,
257 struct GNUNET_CRYPTO_PublicKey *pkey)
258{
259 if (GNUNET_OK !=
260 GNUNET_CRYPTO_public_key_from_string (zkey,
261 pkey))
262 return GNUNET_SYSERR;
263 return GNUNET_OK;
264}
265
266
267enum GNUNET_GenericReturnValue
268GNUNET_GNSRECORD_identity_from_data (const char *data,
269 size_t data_size,
270 uint32_t type,
271 struct GNUNET_CRYPTO_PublicKey *key)
272{
273 if (GNUNET_NO == GNUNET_GNSRECORD_is_zonekey_type (type))
274 return GNUNET_SYSERR;
275 switch (type)
276 {
277 case GNUNET_GNSRECORD_TYPE_PKEY:
278 if (data_size > sizeof (struct GNUNET_CRYPTO_EcdsaPublicKey))
279 return GNUNET_SYSERR;
280 memcpy (&key->ecdsa_key, data, data_size);
281 break;
282 case GNUNET_GNSRECORD_TYPE_EDKEY:
283 if (data_size > sizeof (struct GNUNET_CRYPTO_EddsaPublicKey))
284 return GNUNET_SYSERR;
285 memcpy (&key->eddsa_key, data, data_size);
286 break;
287 default:
288 return GNUNET_NO;
289 }
290 key->type = htonl (type);
291
292 return GNUNET_YES;
293}
294
295
296enum GNUNET_GenericReturnValue
297GNUNET_GNSRECORD_data_from_identity (const struct
298 GNUNET_CRYPTO_PublicKey *key,
299 char **data,
300 size_t *data_size,
301 uint32_t *type)
302{
303 char *tmp;
304 *type = ntohl (key->type);
305 *data_size = GNUNET_CRYPTO_public_key_get_length (key) - sizeof (key->type);
306 if (0 == *data_size)
307 return GNUNET_SYSERR;
308 tmp = GNUNET_malloc (*data_size);
309 memcpy (tmp, ((char*) key) + sizeof (key->type), *data_size);
310 *data = tmp;
311 return GNUNET_OK;
312}
313
314
315enum GNUNET_GenericReturnValue
316GNUNET_GNSRECORD_is_zonekey_type (uint32_t type)
317{
318 switch (type)
319 {
320 case GNUNET_GNSRECORD_TYPE_PKEY:
321 case GNUNET_GNSRECORD_TYPE_EDKEY:
322 return GNUNET_YES;
323 default:
324 return GNUNET_NO;
325 }
326}
327
328
329size_t
330GNUNET_GNSRECORD_block_get_size (const struct GNUNET_GNSRECORD_Block *block)
331{
332 return ntohl (block->size);
333}
334
335
336struct GNUNET_TIME_Absolute
337GNUNET_GNSRECORD_block_get_expiration (const struct
338 GNUNET_GNSRECORD_Block *block)
339{
340
341 switch (ntohl (block->type))
342 {
343 case GNUNET_GNSRECORD_TYPE_PKEY:
344 return GNUNET_TIME_absolute_ntoh (block->ecdsa_block.expiration_time);
345 case GNUNET_GNSRECORD_TYPE_EDKEY:
346 return GNUNET_TIME_absolute_ntoh (block->eddsa_block.expiration_time);
347 default:
348 GNUNET_break (0); /* Hopefully we never get here, but we might */
349 }
350 return GNUNET_TIME_absolute_get_zero_ ();
351
352}
353
354
355enum GNUNET_GenericReturnValue
356GNUNET_GNSRECORD_query_from_block (const struct GNUNET_GNSRECORD_Block *block,
357 struct GNUNET_HashCode *query)
358{
359 switch (ntohl (block->type))
360 {
361 case GNUNET_GNSRECORD_TYPE_PKEY:
362 GNUNET_CRYPTO_hash (&(block->ecdsa_block.derived_key),
363 sizeof (block->ecdsa_block.derived_key),
364 query);
365 return GNUNET_OK;
366 case GNUNET_GNSRECORD_TYPE_EDKEY:
367 GNUNET_CRYPTO_hash (&block->eddsa_block.derived_key,
368 sizeof (block->eddsa_block.derived_key),
369 query);
370 return GNUNET_OK;
371 default:
372 return GNUNET_SYSERR;
373 }
374 return GNUNET_SYSERR;
375
376}
377
378
379enum GNUNET_GenericReturnValue
380GNUNET_GNSRECORD_record_to_identity_key (const struct GNUNET_GNSRECORD_Data *rd,
381 struct GNUNET_CRYPTO_PublicKey *key)
382{
383 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
384 "Got record of type %u\n",
385 rd->record_type);
386 switch (rd->record_type)
387 {
388 case GNUNET_GNSRECORD_TYPE_PKEY:
389 key->type = htonl (rd->record_type);
390 memcpy (&key->ecdsa_key, rd->data, sizeof (key->ecdsa_key));
391 return GNUNET_OK;
392 case GNUNET_GNSRECORD_TYPE_EDKEY:
393 key->type = htonl (rd->record_type);
394 memcpy (&key->eddsa_key, rd->data, sizeof (key->eddsa_key));
395 return GNUNET_OK;
396 default:
397 return GNUNET_SYSERR;
398 }
399 return GNUNET_SYSERR;
400
401
402}
403
404
405enum GNUNET_GenericReturnValue
406GNUNET_GNSRECORD_normalize_record_set (const char *label,
407 const struct
408 GNUNET_GNSRECORD_Data *rd,
409 unsigned int rd_count,
410 struct GNUNET_GNSRECORD_Data *
411 rd_public,
412 unsigned int *rd_count_public,
413 struct GNUNET_TIME_Absolute *expiry,
414 enum GNUNET_GNSRECORD_Filter filter,
415 char **emsg)
416{
417 struct GNUNET_TIME_Absolute now;
418 struct GNUNET_TIME_Absolute minimum_expiration;
419 int have_zone_delegation = GNUNET_NO;
420 int have_gns2dns = GNUNET_NO;
421 int have_other = GNUNET_NO;
422 int have_redirect = GNUNET_NO;
423 int have_empty_label = (0 == strcmp (GNUNET_GNS_EMPTY_LABEL_AT, label));
424 unsigned int rd_count_tmp;
425
426 minimum_expiration = GNUNET_TIME_UNIT_ZERO_ABS;
427 now = GNUNET_TIME_absolute_get ();
428 rd_count_tmp = 0;
429 for (unsigned int i = 0; i < rd_count; i++)
430 {
431 /* Ignore private records for public record set */
432 if ((0 != (filter & GNUNET_GNSRECORD_FILTER_OMIT_PRIVATE)) &&
433 (0 != (rd[i].flags & GNUNET_GNSRECORD_RF_PRIVATE)))
434 {
435 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
436 "Filtering private record filter=%u...\n", filter);
437 continue;
438 }
439 /* Skip expired records */
440 if ((0 == (rd[i].flags & GNUNET_GNSRECORD_RF_RELATIVE_EXPIRATION)) &&
441 (rd[i].expiration_time < now.abs_value_us))
442 {
443 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
444 "Filtering expired record...\n");
445 continue; /* record already expired, skip it */
446 }
447 /* Ignore the maintenance record (e.g. tombstone) unless filter permits explicitly.
448 * Remember expiration time. */
449 if (0 != (rd[i].flags & GNUNET_GNSRECORD_RF_MAINTENANCE))
450 {
451 if (0 != (filter & GNUNET_GNSRECORD_FILTER_INCLUDE_MAINTENANCE))
452 {
453 rd_public[rd_count_tmp] = rd[i];
454 rd_count_tmp++;
455 }
456 continue;
457 }
458 /* No NICK records unless empty label */
459 if (have_empty_label &&
460 (GNUNET_GNSRECORD_TYPE_NICK == rd[i].record_type))
461 continue;
462
463 /**
464 * Check for delegation and redirect consistency.
465 * Note that we check for consistency BEFORE we filter for
466 * private records ON PURPOSE.
467 * We also want consistent record sets in our local zone(s).
468 * The only exception is the tombstone (above) which we ignore
469 * for the consistency check(s).
470 * FIXME: What about shadow records? Should we ignore them?
471 */
472 if (GNUNET_YES == GNUNET_GNSRECORD_is_zonekey_type (rd[i].record_type))
473 {
474 /* No delegation records under empty label*/
475 if (have_empty_label)
476 {
477 *emsg = GNUNET_strdup (_ (
478 "Zone delegation record not allowed in apex."));
479 return GNUNET_SYSERR;
480 }
481 if ((GNUNET_YES == have_other) ||
482 (GNUNET_YES == have_redirect) ||
483 (GNUNET_YES == have_gns2dns))
484 {
485 *emsg = GNUNET_strdup (_ (
486 "Zone delegation record set contains mutually exclusive records."));
487 return GNUNET_SYSERR;
488 }
489 have_zone_delegation = GNUNET_YES;
490 }
491 else if (GNUNET_GNSRECORD_TYPE_REDIRECT == rd[i].record_type)
492 {
493 if (GNUNET_YES == have_redirect)
494 {
495 *emsg = GNUNET_strdup (_ (
496 "Multiple REDIRECT records."));
497 return GNUNET_SYSERR;
498
499 }
500 if ((GNUNET_YES == have_other) ||
501 (GNUNET_YES == have_zone_delegation) ||
502 (GNUNET_YES == have_gns2dns))
503 {
504 *emsg = GNUNET_strdup (_ (
505 "Redirection record set contains mutually exclusive records."));
506 return GNUNET_SYSERR;
507 }
508 /* No redirection records under empty label*/
509 if (have_empty_label)
510 {
511 *emsg = GNUNET_strdup (_ (
512 "Redirection records not allowed in apex."));
513 return GNUNET_SYSERR;
514 }
515 have_redirect = GNUNET_YES;
516 }
517 else if (GNUNET_GNSRECORD_TYPE_GNS2DNS == rd[i].record_type)
518 {
519 /* No gns2dns records under empty label*/
520 if (have_empty_label)
521 {
522 *emsg = GNUNET_strdup (_ (
523 "Redirection records not allowed in apex.."));
524 return GNUNET_SYSERR;
525 }
526 if ((GNUNET_YES == have_other) ||
527 (GNUNET_YES == have_redirect) ||
528 (GNUNET_YES == have_zone_delegation))
529 {
530 *emsg = GNUNET_strdup (_ (
531 "Redirection record set contains mutually exclusive records."));
532 return GNUNET_SYSERR;
533 }
534 have_gns2dns = GNUNET_YES;
535 }
536 else
537 {
538 /* Some other record.
539 * Not allowed for zone delegations or redirections */
540 if ((GNUNET_YES == have_zone_delegation) ||
541 (GNUNET_YES == have_redirect) ||
542 (GNUNET_YES == have_gns2dns))
543 {
544 *emsg = GNUNET_strdup (_ (
545 "Mutually exclusive records."));
546 return GNUNET_SYSERR;
547 }
548 have_other = GNUNET_YES;
549 }
550
551 rd_public[rd_count_tmp] = rd[i];
552 /* Make sure critical record types are marked as such */
553 if (GNUNET_YES == GNUNET_GNSRECORD_is_critical (rd[i].record_type))
554 rd_public[rd_count_tmp].flags |= GNUNET_GNSRECORD_RF_CRITICAL;
555 rd_count_tmp++;
556 }
557
558 *expiry = GNUNET_GNSRECORD_record_get_expiration_time (rd_count_tmp,
559 rd_public,
560 minimum_expiration);
561 *rd_count_public = rd_count_tmp;
562 return GNUNET_OK;
563}
564
565
566/* end of gnsrecord_misc.c */
diff --git a/src/lib/gnsrecord/gnsrecord_pow.c b/src/lib/gnsrecord/gnsrecord_pow.c
new file mode 100644
index 000000000..a40dd7802
--- /dev/null
+++ b/src/lib/gnsrecord/gnsrecord_pow.c
@@ -0,0 +1,462 @@
1/*
2 This file is part of GNUnet
3 Copyright (C) 2013, 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/**
23 * @brief API for proof of work
24 * @author Martin Schanzenbach
25 */
26#include "platform.h"
27#include "gnunet_util_lib.h"
28#include "gnunet_gnsrecord_lib.h"
29#include "gnunet_signatures.h"
30#include "gnunet_protocols.h"
31#include <inttypes.h>
32
33/**
34 * Helper struct that holds a found pow nonce
35 * and the corresponding number of leading zeros.
36 */
37struct BestPow
38{
39 /**
40 * PoW nonce
41 */
42 uint64_t pow;
43
44 /**
45 * Corresponding zero bits in hash
46 */
47 unsigned int bits;
48};
49
50
51/**
52 * The handle to a PoW calculation.
53 * Used in iterative PoW rounds.
54 */
55struct GNUNET_GNSRECORD_PowCalculationHandle
56{
57 /**
58 * Current set of found PoWs
59 */
60 struct BestPow best[POW_COUNT];
61
62 /**
63 * The final PoW result data structure.
64 */
65 struct GNUNET_GNSRECORD_PowP *pow;
66
67 /**
68 * The current nonce to try
69 */
70 uint64_t current_pow;
71
72 /**
73 * Epochs how long the PoW should be valid.
74 * This is added on top of the difficulty in the PoW.
75 */
76 unsigned int epochs;
77
78 /**
79 * The difficulty (leading zeros) to achieve.
80 */
81 unsigned int difficulty;
82
83};
84
85static struct GNUNET_CRYPTO_PowSalt salt = { "GnsRevocationPow" };
86
87/**
88 * Calculate the average zeros in the pows.
89 *
90 * @param ph the PowHandle
91 * @return the average number of zeros.
92 */
93static unsigned int
94calculate_score (const struct GNUNET_GNSRECORD_PowCalculationHandle *ph)
95{
96 double sum = 0.0;
97 for (unsigned int j = 0; j<POW_COUNT; j++)
98 sum += ph->best[j].bits;
99 double avg = sum / POW_COUNT;
100 return avg;
101}
102
103
104struct GNUNET_GNSRECORD_SignaturePurposePS *
105GNR_create_signature_message (const struct GNUNET_GNSRECORD_PowP *pow)
106{
107 struct GNUNET_GNSRECORD_SignaturePurposePS *spurp;
108 const struct GNUNET_CRYPTO_PublicKey *pk;
109 size_t ksize;
110
111 pk = (const struct GNUNET_CRYPTO_PublicKey *) &pow[1];
112 ksize = GNUNET_CRYPTO_public_key_get_length (pk);
113 spurp = GNUNET_malloc (sizeof (*spurp) + ksize);
114 spurp->timestamp = pow->timestamp;
115 spurp->purpose.purpose = htonl (GNUNET_SIGNATURE_PURPOSE_GNS_REVOCATION);
116 spurp->purpose.size = htonl (sizeof(*spurp) + ksize);
117 GNUNET_CRYPTO_write_public_key_to_buffer (pk,
118 (char*) &spurp[1],
119 ksize);
120 return spurp;
121}
122
123
124enum GNUNET_GenericReturnValue
125check_signature_identity (const struct GNUNET_GNSRECORD_PowP *pow,
126 const struct GNUNET_CRYPTO_PublicKey *key)
127{
128 struct GNUNET_GNSRECORD_SignaturePurposePS *spurp;
129 unsigned char *sig;
130 size_t ksize;
131 int ret;
132
133 ksize = GNUNET_CRYPTO_public_key_get_length (key);
134 spurp = GNR_create_signature_message (pow);
135 sig = ((unsigned char*) &pow[1] + ksize);
136 ret =
137 GNUNET_CRYPTO_signature_verify_raw_ (
138 GNUNET_SIGNATURE_PURPOSE_GNS_REVOCATION,
139 &spurp->purpose,
140 sig,
141 key);
142 GNUNET_free (spurp);
143 return ret == GNUNET_OK ? GNUNET_OK : GNUNET_SYSERR;
144}
145
146
147enum GNUNET_GenericReturnValue
148check_signature (const struct GNUNET_GNSRECORD_PowP *pow)
149{
150 const struct GNUNET_CRYPTO_PublicKey *pk;
151
152 pk = (const struct GNUNET_CRYPTO_PublicKey *) &pow[1];
153 return check_signature_identity (pow, pk);
154}
155
156
157/**
158 * Check if the given proof-of-work is valid.
159 *
160 * @param pow proof of work
161 * @param difficulty how many bits must match (configuration) LSD0001: D
162 * @param epoch_duration length of single epoch in configuration
163 * @return #GNUNET_YES if the @a pow is acceptable, #GNUNET_NO if not
164 */
165enum GNUNET_GenericReturnValue
166GNUNET_GNSRECORD_check_pow (const struct GNUNET_GNSRECORD_PowP *pow,
167 unsigned int difficulty,
168 struct GNUNET_TIME_Relative epoch_duration)
169{
170 char buf[sizeof(struct GNUNET_CRYPTO_PublicKey)
171 + sizeof (struct GNUNET_TIME_AbsoluteNBO)
172 + sizeof (uint64_t)] GNUNET_ALIGN;
173 struct GNUNET_HashCode result;
174 struct GNUNET_TIME_Absolute ts;
175 struct GNUNET_TIME_Absolute exp;
176 struct GNUNET_TIME_Relative ttl;
177 struct GNUNET_TIME_Relative buffer;
178 /* LSD0001: D' */
179 unsigned int score = 0;
180 unsigned int tmp_score = 0;
181 unsigned int epochs;
182 uint64_t pow_val;
183 ssize_t pklen;
184 const struct GNUNET_CRYPTO_PublicKey *pk;
185
186 pk = (const struct GNUNET_CRYPTO_PublicKey *) &pow[1];
187
188 /**
189 * Check if signature valid
190 */
191 if (GNUNET_OK != check_signature (pow))
192 {
193 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
194 "Proof of work signature invalid!\n");
195 return GNUNET_SYSERR;
196 }
197
198 /**
199 * First, check if PoW set is strictly monotically increasing
200 */
201 for (unsigned int i = 0; i < POW_COUNT - 1; i++)
202 {
203 if (GNUNET_ntohll (pow->pow[i]) >= GNUNET_ntohll (pow->pow[i + 1]))
204 return GNUNET_NO;
205 }
206 GNUNET_memcpy (&buf[sizeof(uint64_t)],
207 &pow->timestamp,
208 sizeof (uint64_t));
209 pklen = GNUNET_CRYPTO_public_key_get_length (pk);
210 if (0 > pklen)
211 {
212 GNUNET_break (0);
213 return GNUNET_NO;
214 }
215 GNUNET_memcpy (&buf[sizeof(uint64_t) * 2],
216 pk,
217 pklen);
218 for (unsigned int i = 0; i < POW_COUNT; i++)
219 {
220 pow_val = GNUNET_ntohll (pow->pow[i]);
221 GNUNET_memcpy (buf, &pow->pow[i], sizeof(uint64_t));
222 GNUNET_CRYPTO_pow_hash (&salt,
223 buf,
224 sizeof(buf),
225 &result);
226 tmp_score = GNUNET_CRYPTO_hash_count_leading_zeros (&result);
227 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
228 "Score %u with %" PRIu64 " (#%u)\n",
229 tmp_score, pow_val, i);
230
231 score += tmp_score;
232
233 }
234 score = score / POW_COUNT;
235 if (score < difficulty)
236 return GNUNET_NO;
237 /* LSD0001: (D'-D+1) */
238 epochs = score - difficulty + 1;
239
240 /**
241 * Check expiration
242 */
243 ts = GNUNET_TIME_absolute_ntoh (pow->timestamp);
244 ttl = GNUNET_TIME_relative_multiply (epoch_duration,
245 epochs);
246 /**
247 * Extend by 10% for unsynchronized clocks
248 */
249 buffer = GNUNET_TIME_relative_divide (epoch_duration,
250 10);
251 exp = GNUNET_TIME_absolute_add (ts, ttl);
252 exp = GNUNET_TIME_absolute_add (exp,
253 buffer);
254
255 if (0 != GNUNET_TIME_absolute_get_remaining (ts).rel_value_us)
256 return GNUNET_NO; /* Not yet valid. */
257 /* Revert to actual start time */
258 ts = GNUNET_TIME_absolute_add (ts,
259 buffer);
260
261 if (0 == GNUNET_TIME_absolute_get_remaining (exp).rel_value_us)
262 return GNUNET_NO; /* expired */
263 return GNUNET_YES;
264}
265
266
267enum GNUNET_GenericReturnValue
268sign_pow_identity (const struct GNUNET_CRYPTO_PrivateKey *key,
269 struct GNUNET_GNSRECORD_PowP *pow)
270{
271 struct GNUNET_TIME_Absolute ts = GNUNET_TIME_absolute_get ();
272 struct GNUNET_GNSRECORD_SignaturePurposePS *rp;
273 const struct GNUNET_CRYPTO_PublicKey *pk;
274 size_t ksize;
275 char *sig;
276
277 /**
278 * Predate the validity period to prevent rejections due to
279 * unsynchronized clocks
280 */
281 ts = GNUNET_TIME_absolute_subtract (ts,
282 GNUNET_TIME_UNIT_WEEKS);
283 pk = (const struct GNUNET_CRYPTO_PublicKey *) &pow[1];
284 ksize = GNUNET_CRYPTO_public_key_get_length (pk);
285 pow->timestamp = GNUNET_TIME_absolute_hton (ts);
286 rp = GNR_create_signature_message (pow);
287 sig = ((char*) &pow[1]) + ksize;
288 int result = GNUNET_CRYPTO_sign_raw_ (key,
289 &rp->purpose,
290 (void*) sig);
291 GNUNET_free (rp);
292 if (result == GNUNET_SYSERR)
293 return GNUNET_NO;
294 else
295 return result;
296}
297
298
299enum GNUNET_GenericReturnValue
300sign_pow (const struct GNUNET_CRYPTO_PrivateKey *key,
301 struct GNUNET_GNSRECORD_PowP *pow)
302{
303 struct GNUNET_CRYPTO_PublicKey *pk;
304
305 pk = (struct GNUNET_CRYPTO_PublicKey *) &pow[1];
306 GNUNET_CRYPTO_key_get_public (key, pk);
307 return sign_pow_identity (key, pow);
308}
309
310
311/**
312 * Initializes a fresh PoW computation.
313 *
314 * @param key the key to calculate the PoW for.
315 * @param[out] pow starting point for PoW calculation (not yet valid)
316 */
317void
318GNUNET_GNSRECORD_pow_init (const struct GNUNET_CRYPTO_PrivateKey *key,
319 struct GNUNET_GNSRECORD_PowP *pow)
320{
321 GNUNET_assert (GNUNET_OK == sign_pow (key, pow));
322}
323
324
325struct GNUNET_GNSRECORD_PowCalculationHandle*
326GNUNET_GNSRECORD_pow_start (struct GNUNET_GNSRECORD_PowP *pow,
327 int epochs,
328 unsigned int difficulty)
329{
330 struct GNUNET_GNSRECORD_PowCalculationHandle *pc;
331 struct GNUNET_TIME_Relative ttl;
332
333
334 pc = GNUNET_new (struct GNUNET_GNSRECORD_PowCalculationHandle);
335 pc->pow = pow;
336 ttl = GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_YEARS,
337 epochs);
338 pc->pow->ttl = GNUNET_TIME_relative_hton (ttl);
339 pc->current_pow = GNUNET_CRYPTO_random_u64 (GNUNET_CRYPTO_QUALITY_WEAK,
340 UINT64_MAX);
341 pc->difficulty = difficulty;
342 pc->epochs = epochs;
343 return pc;
344}
345
346
347/**
348 * Comparison function for quicksort
349 *
350 * @param a left element
351 * @param b right element
352 * @return a-b
353 */
354static int
355cmp_pow_value (const void *a, const void *b)
356{
357 return (GNUNET_ntohll (*(uint64_t*) a) - GNUNET_ntohll (*(uint64_t*) b));
358}
359
360
361/**
362 * Calculate a key revocation valid for broadcasting for a number
363 * of epochs.
364 *
365 * @param pc handle to the PoW, initially called with NULL.
366 * @param epochs number of epochs for which the revocation must be valid.
367 * @param pow current pow value to try
368 * @param difficulty current base difficulty to achieve
369 * @return #GNUNET_YES if the @a pow is acceptable, #GNUNET_NO if not
370 */
371enum GNUNET_GenericReturnValue
372GNUNET_GNSRECORD_pow_round (struct GNUNET_GNSRECORD_PowCalculationHandle *pc)
373{
374 char buf[sizeof(struct GNUNET_CRYPTO_PublicKey)
375 + sizeof (uint64_t)
376 + sizeof (uint64_t)] GNUNET_ALIGN;
377 struct GNUNET_HashCode result;
378 const struct GNUNET_CRYPTO_PublicKey *pk;
379 unsigned int zeros;
380 int ret;
381 uint64_t pow_nbo;
382 ssize_t ksize;
383
384 pc->current_pow++;
385 pk = (const struct GNUNET_CRYPTO_PublicKey *) &(pc->pow[1]);
386
387 /**
388 * Do not try duplicates
389 */
390 for (unsigned int i = 0; i < POW_COUNT; i++)
391 if (pc->current_pow == pc->best[i].pow)
392 return GNUNET_NO;
393 pow_nbo = GNUNET_htonll (pc->current_pow);
394 GNUNET_memcpy (buf, &pow_nbo, sizeof(uint64_t));
395 GNUNET_memcpy (&buf[sizeof(uint64_t)],
396 &pc->pow->timestamp,
397 sizeof (uint64_t));
398 ksize = GNUNET_CRYPTO_public_key_get_length (pk);
399 GNUNET_assert (0 < ksize);
400 GNUNET_memcpy (&buf[sizeof(uint64_t) * 2],
401 pk,
402 ksize);
403 GNUNET_CRYPTO_pow_hash (&salt,
404 buf,
405 sizeof(buf),
406 &result);
407 zeros = GNUNET_CRYPTO_hash_count_leading_zeros (&result);
408 for (unsigned int i = 0; i < POW_COUNT; i++)
409 {
410 if (pc->best[i].bits < zeros)
411 {
412 pc->best[i].bits = zeros;
413 pc->best[i].pow = pc->current_pow;
414 pc->pow->pow[i] = pow_nbo;
415 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
416 "New best score %u with %" PRIu64 " (#%u)\n",
417 zeros, pc->current_pow, i);
418
419 break;
420 }
421 }
422 ret = calculate_score (pc) >= pc->difficulty + pc->epochs ? GNUNET_YES :
423 GNUNET_NO;
424 if (GNUNET_YES == ret)
425 {
426 /* Sort POWs) */
427 qsort (pc->pow->pow, POW_COUNT, sizeof (uint64_t), &cmp_pow_value);
428 }
429 return ret;
430}
431
432
433size_t
434GNUNET_GNSRECORD_proof_get_size (const struct GNUNET_GNSRECORD_PowP *pow)
435{
436 size_t size;
437 size_t ksize;
438 const struct GNUNET_CRYPTO_PublicKey *pk;
439
440 size = sizeof (struct GNUNET_GNSRECORD_PowP);
441 pk = (const struct GNUNET_CRYPTO_PublicKey *) &pow[1];
442 ksize = GNUNET_CRYPTO_public_key_get_length (pk);
443 size += ksize;
444 size += GNUNET_CRYPTO_signature_get_raw_length_by_type (pk->type);
445 return size;
446}
447
448
449/**
450 * Stop a PoW calculation
451 *
452 * @param pc the calculation to clean up
453 * @return #GNUNET_YES if pow valid, #GNUNET_NO if pow was set but is not
454 * valid
455 */
456void
457GNUNET_GNSRECORD_pow_stop (struct GNUNET_GNSRECORD_PowCalculationHandle *pc)
458{
459 GNUNET_free (pc);
460}
461
462
diff --git a/src/lib/gnsrecord/gnsrecord_serialization.c b/src/lib/gnsrecord/gnsrecord_serialization.c
new file mode 100644
index 000000000..053edfd33
--- /dev/null
+++ b/src/lib/gnsrecord/gnsrecord_serialization.c
@@ -0,0 +1,302 @@
1/*
2 This file is part of GNUnet.
3 Copyright (C) 2009-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 gnsrecord/gnsrecord_serialization.c
23 * @brief API to serialize and deserialize GNS records
24 * @author Martin Schanzenbach
25 * @author Matthias Wachs
26 * @author Christian Grothoff
27 */
28#include "platform.h"
29#include "gnunet_util_lib.h"
30#include "gnunet_constants.h"
31#include "gnunet_signatures.h"
32#include "gnunet_arm_service.h"
33#include "gnunet_gnsrecord_lib.h"
34
35#define LOG(kind, ...) GNUNET_log_from (kind, "gnsrecord", __VA_ARGS__)
36
37/**
38 * Set to 1 to check that all records are well-formed (can be converted
39 * to string) during serialization/deserialization.
40 */
41#define DEBUG_GNSRECORDS 0
42
43GNUNET_NETWORK_STRUCT_BEGIN
44
45
46/**
47 * Internal format of a record in the serialized form.
48 */
49struct NetworkRecord
50{
51 /**
52 * Expiration time for the DNS record; relative or absolute depends
53 * on @e flags, network byte order.
54 */
55 uint64_t expiration_time GNUNET_PACKED;
56
57 /**
58 * Number of bytes in 'data', network byte order.
59 */
60 uint16_t data_size GNUNET_PACKED;
61
62 /**
63 * Flags for the record, network byte order.
64 */
65 uint16_t flags GNUNET_PACKED;
66
67 /**
68 * Type of the GNS/DNS record, network byte order.
69 */
70 uint32_t record_type GNUNET_PACKED;
71
72};
73
74GNUNET_NETWORK_STRUCT_END
75
76
77ssize_t
78GNUNET_GNSRECORD_records_get_size (unsigned int rd_count,
79 const struct GNUNET_GNSRECORD_Data *rd)
80{
81 size_t ret;
82
83 if (0 == rd_count)
84 return 0;
85
86 ret = sizeof(struct NetworkRecord) * rd_count;
87 for (unsigned int i = 0; i < rd_count; i++)
88 {
89 if ((ret + rd[i].data_size) < ret)
90 {
91 GNUNET_break (0);
92 return -1;
93 }
94 ret += rd[i].data_size;
95#if DEBUG_GNSRECORDS
96 {
97 char *str;
98
99 str = GNUNET_GNSRECORD_value_to_string (rd[i].record_type,
100 rd[i].data,
101 rd[i].data_size);
102 if (NULL == str)
103 {
104 GNUNET_break_op (0);
105 return -1;
106 }
107 GNUNET_free (str);
108 }
109#endif
110 }
111 if (ret > SSIZE_MAX)
112 {
113 GNUNET_break (0);
114 return -1;
115 }
116 // Do not pad PKEY
117 if ((GNUNET_GNSRECORD_TYPE_PKEY == rd->record_type) ||
118 (GNUNET_GNSRECORD_TYPE_EDKEY == rd->record_type))
119 return ret;
120 /**
121 * Efficiently round up to the next
122 * power of 2 for padding
123 * https://graphics.stanford.edu/~seander/bithacks.html#RoundUpPowerOf2
124 */ret--;
125 ret |= ret >> 1;
126 ret |= ret >> 2;
127 ret |= ret >> 4;
128 ret |= ret >> 8;
129 ret |= ret >> 16;
130 ret++;
131 return (ssize_t) ret;
132}
133
134
135ssize_t
136GNUNET_GNSRECORD_records_serialize (unsigned int rd_count,
137 const struct GNUNET_GNSRECORD_Data *rd,
138 size_t dest_size,
139 char *dest)
140{
141 struct NetworkRecord rec;
142 size_t off;
143
144 off = 0;
145 for (unsigned int i = 0; i < rd_count; i++)
146 {
147 LOG (GNUNET_ERROR_TYPE_DEBUG,
148 "Serializing record %u with flags %d and expiration time %llu\n",
149 i,
150 rd[i].flags,
151 (unsigned long long) rd[i].expiration_time);
152 rec.expiration_time = GNUNET_htonll (rd[i].expiration_time);
153 rec.data_size = htons ((uint16_t) rd[i].data_size);
154 rec.record_type = htonl (rd[i].record_type);
155 rec.flags = htons (rd[i].flags);
156 if ((off + sizeof(rec) > dest_size) ||
157 (off + sizeof(rec) < off))
158 {
159 GNUNET_break (0);
160 return -1;
161 }
162 GNUNET_memcpy (&dest[off],
163 &rec,
164 sizeof(rec));
165 off += sizeof(rec);
166 if ((off + rd[i].data_size > dest_size) ||
167 (off + rd[i].data_size < off))
168 {
169 GNUNET_break (0);
170 return -1;
171 }
172 GNUNET_memcpy (&dest[off],
173 rd[i].data,
174 rd[i].data_size);
175 off += rd[i].data_size;
176#if DEBUG_GNSRECORDS
177 {
178 char *str;
179
180 str = GNUNET_GNSRECORD_value_to_string (rd[i].record_type,
181 rd[i].data,
182 rd[i].data_size);
183 if (NULL == str)
184 {
185 GNUNET_break_op (0);
186 return -1;
187 }
188 GNUNET_free (str);
189 }
190#endif
191 }
192 memset (&dest[off],
193 0,
194 dest_size - off);
195 return dest_size;
196}
197
198unsigned int
199GNUNET_GNSRECORD_records_deserialize_get_size (size_t len,
200 const char *src)
201{
202 struct NetworkRecord rec;
203 struct NetworkRecord rec_zero;
204 size_t off;
205 unsigned int rd_count = 0;
206
207 memset (&rec_zero, 0, sizeof (rec_zero));
208
209 off = 0;
210 for (off = 0; (off + sizeof(rec) <= len) && (off + sizeof(rec) >= off);)
211 {
212 GNUNET_memcpy (&rec,
213 &src[off],
214 sizeof(rec));
215 /*
216 * If we have found a byte string of zeroes, we have reached
217 * the padding
218 */
219 if (0 == GNUNET_memcmp (&rec, &rec_zero))
220 break;
221 off += sizeof(rec);
222 if ((off + ntohs ((uint16_t) rec.data_size) > len) ||
223 (off + ntohs ((uint16_t) rec.data_size) < off))
224 {
225 GNUNET_break_op (0);
226 return 0;
227 }
228 off += ntohs ((uint16_t) rec.data_size);
229 rd_count++;
230 }
231 return rd_count;
232}
233
234/**
235 * Deserialize the given records to the given destination.
236 *
237 * @param len size of the serialized record data
238 * @param src the serialized record data
239 * @param rd_count number of records parsed
240 * @param dest where to put the data
241 * @return #GNUNET_OK on success, #GNUNET_SYSERR on error
242 */
243int
244GNUNET_GNSRECORD_records_deserialize (size_t len,
245 const char *src,
246 unsigned int rd_count,
247 struct GNUNET_GNSRECORD_Data *dest)
248{
249 struct NetworkRecord rec;
250 size_t off;
251
252 off = 0;
253 for (unsigned int i = 0; i < rd_count; i++)
254 {
255 if ((off + sizeof(rec) > len) ||
256 (off + sizeof(rec) < off))
257 {
258 GNUNET_break_op (0);
259 return GNUNET_SYSERR;
260 }
261 GNUNET_memcpy (&rec,
262 &src[off],
263 sizeof(rec));
264 dest[i].expiration_time = GNUNET_ntohll (rec.expiration_time);
265 dest[i].data_size = ntohs ((uint16_t) rec.data_size);
266 dest[i].record_type = ntohl (rec.record_type);
267 dest[i].flags = ntohs (rec.flags);
268 off += sizeof(rec);
269 if ((off + dest[i].data_size > len) ||
270 (off + dest[i].data_size < off))
271 {
272 GNUNET_break_op (0);
273 return GNUNET_SYSERR;
274 }
275 dest[i].data = &src[off];
276 off += dest[i].data_size;
277#if GNUNET_EXTRA_LOGGING
278 {
279 char *str;
280
281 str = GNUNET_GNSRECORD_value_to_string (dest[i].record_type,
282 dest[i].data,
283 dest[i].data_size);
284 if (NULL == str)
285 {
286 GNUNET_break_op (0);
287 return GNUNET_SYSERR;
288 }
289 GNUNET_free (str);
290 }
291#endif
292 LOG (GNUNET_ERROR_TYPE_DEBUG,
293 "Deserialized record %u with flags %d and expiration time %llu\n",
294 i,
295 dest[i].flags,
296 (unsigned long long) dest[i].expiration_time);
297 }
298 return GNUNET_OK;
299}
300
301
302/* end of gnsrecord_serialization.c */
diff --git a/src/lib/gnsrecord/gnunet-gnsrecord-tvg.c b/src/lib/gnsrecord/gnunet-gnsrecord-tvg.c
new file mode 100644
index 000000000..746e95c32
--- /dev/null
+++ b/src/lib/gnsrecord/gnunet-gnsrecord-tvg.c
@@ -0,0 +1,539 @@
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-gns-tvg.c
23 * @brief Generate test vectors for GNS.
24 * @author Martin Schanzenbach
25 */
26#include "platform.h"
27#include "gnunet_util_lib.h"
28#include "gnunet_signatures.h"
29#include "gnunet_gns_service.h"
30#include "gnunet_gnsrecord_lib.h"
31#include "gnunet_testing_lib.h"
32#include "gnsrecord_crypto.h"
33#include <inttypes.h>
34
35
36static char *d_pkey =
37 "50d7b652a4efeadff37396909785e5952171a02178c8e7d450fa907925fafd98";
38
39static char *d_edkey =
40 "5af7020ee19160328832352bbc6a68a8d71a7cbe1b929969a7c66d415a0d8f65";
41
42
43static int
44parsehex (char *src, char *dst, size_t dstlen, int invert)
45{
46 char *line = src;
47 char *data = line;
48 int off;
49 int read_byte;
50 int data_len = 0;
51
52 while (sscanf (data, " %02x%n", &read_byte, &off) == 1)
53 {
54 if (invert)
55 dst[dstlen - 1 - data_len++] = read_byte;
56 else
57 dst[data_len++] = read_byte;
58 data += off;
59 }
60 return data_len;
61}
62
63
64static void
65print_bytes_ (void *buf,
66 size_t buf_len,
67 int fold,
68 int in_be)
69{
70 int i;
71
72 for (i = 0; i < buf_len; i++)
73 {
74 if (0 != i)
75 {
76 if ((0 != fold) && (i % fold == 0))
77 printf ("\n ");
78 else
79 printf (" ");
80 }
81 else
82 {
83 printf (" ");
84 }
85 if (in_be)
86 printf ("%02x", ((unsigned char*) buf)[buf_len - 1 - i]);
87 else
88 printf ("%02x", ((unsigned char*) buf)[i]);
89 }
90 printf ("\n");
91}
92
93
94static void
95print_bytes (void *buf,
96 size_t buf_len,
97 int fold)
98{
99 print_bytes_ (buf, buf_len, fold, 0);
100}
101
102
103static void
104print_record (const struct GNUNET_GNSRECORD_Data *rd)
105{
106 struct GNUNET_TIME_Relative rt;
107 struct GNUNET_TIME_Absolute at;
108 uint16_t flags = htons (rd->flags);
109 uint64_t abs_nbo = GNUNET_htonll (rd->expiration_time);
110 uint16_t size_nbo = htons (rd->data_size);
111 uint32_t type_nbo = htonl (rd->record_type);
112 at.abs_value_us = GNUNET_ntohll (abs_nbo);
113 if (0 != (rd->flags & GNUNET_GNSRECORD_RF_RELATIVE_EXPIRATION))
114 {
115 rt.rel_value_us = rd->expiration_time;
116 at = GNUNET_TIME_relative_to_absolute (rt);
117 abs_nbo = GNUNET_htonll (at.abs_value_us);
118 }
119 printf (" EXPIRATION: %" PRIu64 " us\n", rd->expiration_time);
120 print_bytes (&abs_nbo, sizeof (abs_nbo), 8);
121 printf ("\n DATA_SIZE:\n");
122 print_bytes (&size_nbo, sizeof (size_nbo), 8);
123 printf ("\n TYPE:\n");
124 print_bytes (&type_nbo, sizeof (type_nbo), 8);
125 printf ("\n FLAGS: ");
126 print_bytes ((void*) &flags, sizeof (flags), 8);
127 printf ("\n");
128 fprintf (stdout,
129 " DATA:\n");
130 print_bytes ((char*) rd->data, rd->data_size, 8);
131 printf ("\n");
132}
133
134
135/**
136 * Main function that will be run.
137 *
138 * @param cls closure
139 * @param args remaining command-line arguments
140 * @param cfgfile name of the configuration file used (for saving, can be NULL!)
141 * @param cfg configuration
142 */
143static void
144run_pkey (struct GNUNET_GNSRECORD_Data *rd, int rd_count, const char *label)
145{
146 struct GNUNET_TIME_Absolute expire;
147 struct GNUNET_GNSRECORD_Block *rrblock;
148 char *bdata;
149 struct GNUNET_CRYPTO_PrivateKey id_priv;
150 struct GNUNET_CRYPTO_PublicKey id_pub;
151 struct GNUNET_CRYPTO_PrivateKey pkey_data_p;
152 struct GNUNET_CRYPTO_PublicKey pkey_data;
153 struct GNUNET_HashCode query;
154 char *rdata;
155 char *conv_lbl;
156 size_t rdata_size;
157 char ztld[128];
158 unsigned char ctr[GNUNET_CRYPTO_AES_KEY_LENGTH / 2];
159 unsigned char skey[GNUNET_CRYPTO_AES_KEY_LENGTH];
160
161 id_priv.type = htonl (GNUNET_GNSRECORD_TYPE_PKEY);
162 GNUNET_CRYPTO_ecdsa_key_create (&id_priv.ecdsa_key);
163 parsehex (d_pkey,
164 (char*) &id_priv.ecdsa_key,
165 sizeof (id_priv.ecdsa_key), 1);
166
167 GNUNET_CRYPTO_key_get_public (&id_priv,
168 &id_pub);
169 printf ("Zone private key (d, big-endian):\n");
170 print_bytes_ (&id_priv.ecdsa_key,
171 sizeof (struct GNUNET_CRYPTO_EcdsaPrivateKey), 8, 1);
172 printf ("\n");
173 printf ("Zone identifier (ztype|zkey):\n");
174 GNUNET_assert (0 < GNUNET_CRYPTO_public_key_get_length (&id_pub));
175 print_bytes (&id_pub, GNUNET_CRYPTO_public_key_get_length (&id_pub), 8);
176 GNUNET_STRINGS_data_to_string (&id_pub,
177 GNUNET_CRYPTO_public_key_get_length (
178 &id_pub),
179 ztld,
180 sizeof (ztld));
181 printf ("\n");
182 printf ("zTLD:\n");
183 printf ("%s\n", ztld);
184 printf ("\n");
185
186 pkey_data_p.type = htonl (GNUNET_GNSRECORD_TYPE_PKEY);
187 GNUNET_CRYPTO_ecdsa_key_create (&pkey_data_p.ecdsa_key);
188 GNUNET_CRYPTO_key_get_public (&pkey_data_p,
189 &pkey_data);
190 conv_lbl = GNUNET_GNSRECORD_string_normalize (label);
191 printf ("Label:\n");
192 print_bytes (conv_lbl, strlen (conv_lbl), 8);
193 GNUNET_free (conv_lbl);
194 printf ("\nNumber of records (integer): %d\n\n", rd_count);
195
196 for (int i = 0; i < rd_count; i++)
197 {
198 printf ("Record #%d := (\n", i);
199 print_record (&rd[i]);
200 printf (")\n\n");
201 }
202
203 rdata_size = GNUNET_GNSRECORD_records_get_size (rd_count,
204 rd);
205 rdata = GNUNET_malloc (rdata_size);
206 GNUNET_GNSRECORD_records_serialize (rd_count,
207 rd,
208 (size_t) rdata_size,
209 rdata);
210 printf ("RDATA:\n");
211 print_bytes (rdata,
212 (size_t) rdata_size,
213 8);
214 printf ("\n");
215 expire = GNUNET_GNSRECORD_record_get_expiration_time (rd_count, rd,
216 GNUNET_TIME_UNIT_ZERO_ABS);
217 GNR_derive_block_aes_key (ctr,
218 skey,
219 label,
220 GNUNET_TIME_absolute_hton (
221 expire).abs_value_us__,
222 &id_pub.ecdsa_key);
223
224 printf ("Encryption NONCE|EXPIRATION|BLOCK COUNTER:\n");
225 print_bytes (ctr, sizeof (ctr), 8);
226 printf ("\n");
227 printf ("Encryption key (K):\n");
228 print_bytes (skey, sizeof (skey), 8);
229 printf ("\n");
230 GNUNET_GNSRECORD_query_from_public_key (&id_pub,
231 label,
232 &query);
233 printf ("Storage key (q):\n");
234 print_bytes (&query, sizeof (query), 8);
235 printf ("\n");
236 GNUNET_assert (GNUNET_OK == GNUNET_GNSRECORD_block_create (&id_priv,
237 expire,
238 label,
239 rd,
240 rd_count,
241 &rrblock));
242 struct GNUNET_CRYPTO_EcdsaPublicKey derived_key;
243 struct GNUNET_CRYPTO_EcdsaPrivateKey *derived_privkey;
244
245 GNUNET_CRYPTO_ecdsa_public_key_derive (&id_pub.ecdsa_key,
246 label,
247 "gns",
248 &derived_key);
249 derived_privkey = GNUNET_CRYPTO_ecdsa_private_key_derive (&id_priv.ecdsa_key,
250 label,
251 "gns");
252 printf ("ZKDF(zkey):\n");
253 print_bytes (&derived_key, sizeof (derived_key), 8);
254 printf ("\n");
255 printf ("Derived private key (d', big-endian):\n");
256 print_bytes_ (derived_privkey, sizeof (*derived_privkey), 8, 1);
257 printf ("\n");
258 size_t bdata_size = ntohl (rrblock->size) - sizeof (struct
259 GNUNET_GNSRECORD_Block);
260
261 GNUNET_free (derived_privkey);
262
263 bdata = (char*) &(&rrblock->ecdsa_block)[1];
264 printf ("BDATA:\n");
265 print_bytes (bdata, bdata_size, 8);
266 printf ("\n");
267 printf ("RRBLOCK:\n");
268 print_bytes (rrblock, ntohl (rrblock->size), 8);
269 printf ("\n");
270 GNUNET_free (rdata);
271}
272
273
274/**
275 * Main function that will be run.
276 *
277 * @param cls closure
278 * @param args remaining command-line arguments
279 * @param cfgfile name of the configuration file used (for saving, can be NULL!)
280 * @param cfg configuration
281 */
282static void
283run_edkey (struct GNUNET_GNSRECORD_Data *rd, int rd_count, const char*label)
284{
285 struct GNUNET_TIME_Absolute expire;
286 struct GNUNET_GNSRECORD_Block *rrblock;
287 char *bdata;
288 struct GNUNET_CRYPTO_PrivateKey id_priv;
289 struct GNUNET_CRYPTO_PublicKey id_pub;
290 struct GNUNET_CRYPTO_PrivateKey pkey_data_p;
291 struct GNUNET_CRYPTO_PublicKey pkey_data;
292 struct GNUNET_HashCode query;
293 char *rdata;
294 char *conv_lbl;
295 size_t rdata_size;
296
297 char ztld[128];
298 unsigned char nonce[crypto_secretbox_NONCEBYTES];
299 unsigned char skey[crypto_secretbox_KEYBYTES];
300
301 id_priv.type = htonl (GNUNET_GNSRECORD_TYPE_PKEY);
302 GNUNET_CRYPTO_ecdsa_key_create (&id_priv.ecdsa_key);
303 GNUNET_CRYPTO_key_get_public (&id_priv,
304 &id_pub);
305
306 id_priv.type = htonl (GNUNET_PUBLIC_KEY_TYPE_EDDSA);
307 GNUNET_CRYPTO_eddsa_key_create (&id_priv.eddsa_key);
308 parsehex (d_edkey,
309 (char*) &id_priv.eddsa_key,
310 sizeof (id_priv.eddsa_key), 0);
311 GNUNET_CRYPTO_key_get_public (&id_priv,
312 &id_pub);
313 fprintf (stdout,
314 "Zone private key (d):\n");
315 print_bytes (&id_priv.eddsa_key, sizeof (struct
316 GNUNET_CRYPTO_EddsaPrivateKey), 8);
317 printf ("\n");
318 printf ("Zone identifier (ztype|zkey):\n");
319 GNUNET_assert (0 < GNUNET_CRYPTO_public_key_get_length (&id_pub));
320 print_bytes (&id_pub, GNUNET_CRYPTO_public_key_get_length (&id_pub), 8);
321 GNUNET_STRINGS_data_to_string (&id_pub,
322 GNUNET_CRYPTO_public_key_get_length (
323 &id_pub),
324 ztld,
325 sizeof (ztld));
326 printf ("\n");
327 printf ("zTLD:\n");
328 printf ("%s\n", ztld);
329 printf ("\n");
330
331 pkey_data_p.type = htonl (GNUNET_GNSRECORD_TYPE_EDKEY);
332 GNUNET_CRYPTO_eddsa_key_create (&pkey_data_p.eddsa_key);
333 GNUNET_CRYPTO_key_get_public (&pkey_data_p,
334 &pkey_data);
335 conv_lbl = GNUNET_GNSRECORD_string_normalize (label);
336 printf ("Label:\n");
337 print_bytes (conv_lbl, strlen (conv_lbl), 8);
338 GNUNET_free (conv_lbl);
339 fprintf (stdout,
340 "\nNumber of records (integer): %d\n\n", rd_count);
341
342 for (int i = 0; i < rd_count; i++)
343 {
344 printf ("Record #%d := (\n", i);
345 print_record (&rd[i]);
346 printf (")\n\n");
347 }
348
349 rdata_size = GNUNET_GNSRECORD_records_get_size (rd_count,
350 rd);
351 expire = GNUNET_GNSRECORD_record_get_expiration_time (rd_count,
352 rd,
353 GNUNET_TIME_UNIT_ZERO_ABS);
354 GNUNET_assert (0 < rdata_size);
355 rdata = GNUNET_malloc ((size_t) rdata_size);
356 GNUNET_GNSRECORD_records_serialize (rd_count,
357 rd,
358 (size_t) rdata_size,
359 rdata);
360 printf ("RDATA:\n");
361 print_bytes (rdata,
362 (size_t) rdata_size,
363 8);
364 printf ("\n");
365 GNR_derive_block_xsalsa_key (nonce,
366 skey,
367 label,
368 GNUNET_TIME_absolute_hton (
369 expire).abs_value_us__,
370 &id_pub.eddsa_key);
371 printf ("Encryption NONCE|EXPIRATION:\n");
372 print_bytes (nonce, sizeof (nonce), 8);
373 printf ("\n");
374 printf ("Encryption key (K):\n");
375 print_bytes (skey, sizeof (skey), 8);
376 printf ("\n");
377 GNUNET_GNSRECORD_query_from_public_key (&id_pub,
378 label,
379 &query);
380 printf ("Storage key (q):\n");
381 print_bytes (&query, sizeof (query), 8);
382 printf ("\n");
383
384 GNUNET_assert (GNUNET_OK == GNUNET_GNSRECORD_block_create (&id_priv,
385 expire,
386 label,
387 rd,
388 rd_count,
389 &rrblock));
390
391 struct GNUNET_CRYPTO_EddsaPublicKey derived_key;
392 struct GNUNET_CRYPTO_EddsaPrivateScalar derived_privkey;
393 GNUNET_CRYPTO_eddsa_public_key_derive (&id_pub.eddsa_key,
394 label,
395 "gns",
396 &derived_key);
397 GNUNET_CRYPTO_eddsa_private_key_derive (&id_priv.eddsa_key,
398 label,
399 "gns", &derived_privkey);
400 printf ("ZKDF(zkey):\n");
401 print_bytes (&derived_key, sizeof (derived_key), 8);
402 printf ("\n");
403 printf ("nonce := SHA-256 (dh[32..63] || h):\n");
404 print_bytes (derived_privkey.s + 32, 32, 8);
405 printf ("\n");
406 char derived_privkeyNBO[32];
407 /* Convert from little endian */
408 for (size_t i = 0; i < 32; i++)
409 derived_privkeyNBO[i] = derived_privkey.s[31 - i];
410 printf ("Derived private key (d', big-endian):\n");
411 print_bytes (derived_privkeyNBO, sizeof (derived_privkeyNBO), 8);
412 printf ("\n");
413 size_t bdata_size = ntohl (rrblock->size) - sizeof (struct
414 GNUNET_GNSRECORD_Block);
415
416
417 bdata = (char*) &(&rrblock->eddsa_block)[1];
418 printf ("BDATA:\n");
419 print_bytes (bdata, bdata_size, 8);
420 printf ("\n");
421 printf ("RRBLOCK:\n");
422 print_bytes (rrblock, ntohl (rrblock->size), 8);
423 printf ("\n");
424 GNUNET_free (rdata);
425}
426
427
428/**
429 * Main function that will be run.
430 *
431 * @param cls closure
432 * @param args remaining command-line arguments
433 * @param cfgfile name of the configuration file used (for saving, can be NULL!)
434 * @param cfg configuration
435 */
436static void
437run (void *cls,
438 char *const *args,
439 const char *cfgfile,
440 const struct GNUNET_CONFIGURATION_Handle *cfg)
441{
442 struct GNUNET_GNSRECORD_Data rd_pkey;
443 struct GNUNET_GNSRECORD_Data rd[3];
444 struct GNUNET_TIME_Absolute exp1;
445 struct GNUNET_TIME_Absolute exp2;
446 struct GNUNET_TIME_Absolute exp3;
447 struct GNUNET_TIME_AbsoluteNBO exp1nbo;
448 struct GNUNET_TIME_AbsoluteNBO exp2nbo;
449 struct GNUNET_TIME_AbsoluteNBO exp3nbo;
450 size_t pkey_data_size;
451 size_t ip_data_size;
452 char *pkey_data;
453 char *ip_data;
454
455 /*
456 * Make different expiration times
457 */
458 parsehex ("001cee8c10e25980", (char*) &exp1nbo, sizeof (exp1nbo), 0);
459 parsehex ("003ff2aa5408db40", (char*) &exp2nbo, sizeof (exp2nbo), 0);
460 parsehex ("0028bb13ff371940", (char*) &exp3nbo, sizeof (exp3nbo), 0);
461 exp1 = GNUNET_TIME_absolute_ntoh (exp1nbo);
462 exp2 = GNUNET_TIME_absolute_ntoh (exp2nbo);
463 exp3 = GNUNET_TIME_absolute_ntoh (exp3nbo);
464
465 memset (&rd_pkey, 0, sizeof (struct GNUNET_GNSRECORD_Data));
466 GNUNET_assert (GNUNET_OK == GNUNET_GNSRECORD_string_to_value (
467 GNUNET_GNSRECORD_TYPE_PKEY,
468 "000G0011WESGZY9VRV9NNJ66W3GKNZFZF56BFD2BQF3MHMJST2G2GKDYGG",
469 (void**) &pkey_data,
470 &pkey_data_size));
471 rd_pkey.data = pkey_data;
472 rd_pkey.data_size = pkey_data_size;
473 rd_pkey.expiration_time = exp1.abs_value_us;
474 rd_pkey.record_type = GNUNET_GNSRECORD_TYPE_PKEY;
475 rd_pkey.flags = GNUNET_GNSRECORD_RF_CRITICAL;
476 GNUNET_assert (GNUNET_OK == GNUNET_GNSRECORD_string_to_value (
477 GNUNET_DNSPARSER_TYPE_AAAA,
478 "::dead:beef",
479 (void**) &ip_data,
480 &ip_data_size));
481
482 rd[0].data = ip_data;
483 rd[0].data_size = ip_data_size;
484 rd[0].expiration_time = exp1.abs_value_us;
485 rd[0].record_type = GNUNET_DNSPARSER_TYPE_AAAA;
486 rd[0].flags = GNUNET_GNSRECORD_RF_NONE;
487
488 rd[1].data = "\u611b\u79f0";
489 rd[1].data_size = strlen (rd[1].data);
490 rd[1].expiration_time = exp2.abs_value_us;
491 rd[1].record_type = GNUNET_GNSRECORD_TYPE_NICK;
492 rd[1].flags = GNUNET_GNSRECORD_RF_NONE;
493
494 rd[2].data = "Hello World";
495 rd[2].data_size = strlen (rd[2].data);
496 rd[2].expiration_time = exp3.abs_value_us;
497 rd[2].record_type = GNUNET_DNSPARSER_TYPE_TXT;
498 rd[2].flags = GNUNET_GNSRECORD_RF_SUPPLEMENTAL;
499
500 run_pkey (&rd_pkey, 1, "testdelegation");
501 run_pkey (rd, 3, "\u5929\u4e0b\u7121\u6575");
502 run_edkey (&rd_pkey, 1, "testdelegation");
503 run_edkey (rd, 3, "\u5929\u4e0b\u7121\u6575");
504}
505
506
507/**
508 * The main function of the test vector generation tool.
509 *
510 * @param argc number of arguments from the command line
511 * @param argv command line arguments
512 * @return 0 ok, 1 on error
513 */
514int
515main (int argc,
516 char *const *argv)
517{
518 const struct GNUNET_GETOPT_CommandLineOption options[] = {
519 GNUNET_GETOPT_OPTION_END
520 };
521
522 GNUNET_assert (GNUNET_OK ==
523 GNUNET_log_setup ("gnunet-gns-tvg",
524 "INFO",
525 NULL));
526 // gcry_control (GCRYCTL_SET_DEBUG_FLAGS, 1u, 0);
527 // gcry_control (GCRYCTL_SET_VERBOSITY, 99);
528 if (GNUNET_OK !=
529 GNUNET_PROGRAM_run (argc, argv,
530 "gnunet-gns-tvg",
531 "Generate test vectors for GNS",
532 options,
533 &run, NULL))
534 return 1;
535 return 0;
536}
537
538
539/* end of gnunet-gns-tvg.c */
diff --git a/src/lib/gnsrecord/json_gnsrecord.c b/src/lib/gnsrecord/json_gnsrecord.c
new file mode 100644
index 000000000..9a08e0157
--- /dev/null
+++ b/src/lib/gnsrecord/json_gnsrecord.c
@@ -0,0 +1,397 @@
1/*
2 This file is part of GNUnet.
3 Copyright (C) 2009-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 json/json_gnsrecord.c
23 * @brief JSON handling of GNS record data
24 * @author Philippe Buschmann
25 */
26#include "platform.h"
27#include "gnunet_util_lib.h"
28#include "gnunet_json_lib.h"
29#include "gnunet_gnsrecord_lib.h"
30
31#define GNUNET_JSON_GNSRECORD_VALUE "value"
32#define GNUNET_JSON_GNSRECORD_RECORD_DATA "data"
33#define GNUNET_JSON_GNSRECORD_TYPE "record_type"
34#define GNUNET_JSON_GNSRECORD_RELATIVE_EXPIRATION_TIME "relative_expiration"
35#define GNUNET_JSON_GNSRECORD_ABSOLUTE_EXPIRATION_TIME "absolute_expiration"
36#define GNUNET_JSON_GNSRECORD_FLAG_MAINTENANCE "is_maintenance"
37#define GNUNET_JSON_GNSRECORD_FLAG_PRIVATE "is_private"
38#define GNUNET_JSON_GNSRECORD_FLAG_SUPPLEMENTAL "is_supplemental"
39#define GNUNET_JSON_GNSRECORD_FLAG_RELATIVE "is_relative_expiration"
40#define GNUNET_JSON_GNSRECORD_FLAG_SHADOW "is_shadow"
41#define GNUNET_JSON_GNSRECORD_RECORD_NAME "record_name"
42
43struct GnsRecordInfo
44{
45 char **name;
46
47 unsigned int *rd_count;
48
49 struct GNUNET_GNSRECORD_Data **rd;
50};
51
52
53static void
54cleanup_recordinfo (struct GnsRecordInfo *gnsrecord_info)
55{
56 char *tmp;
57
58 if (NULL != *(gnsrecord_info->rd))
59 {
60 for (int i = 0; i < *(gnsrecord_info->rd_count); i++)
61 {
62 tmp = (char*) (*(gnsrecord_info->rd))[i].data;
63 if (NULL != tmp)
64 GNUNET_free (tmp);
65 }
66 GNUNET_free (*(gnsrecord_info->rd));
67 *(gnsrecord_info->rd) = NULL;
68 }
69 if (NULL != *(gnsrecord_info->name))
70 GNUNET_free (*(gnsrecord_info->name));
71 *(gnsrecord_info->name) = NULL;
72}
73
74
75/**
76 * Parse given JSON object to gns record
77 *
78 * @param cls closure, NULL
79 * @param root the json object representing data
80 * @param spec where to write the data
81 * @return #GNUNET_OK upon successful parsing; #GNUNET_SYSERR upon error
82 */
83static int
84parse_record (json_t *data, struct GNUNET_GNSRECORD_Data *rd)
85{
86 struct GNUNET_TIME_Absolute abs_exp;
87 struct GNUNET_TIME_Relative rel_exp;
88 const char *value;
89 const char *record_type;
90 int maintenance;
91 int private;
92 int supplemental;
93 int is_rel_exp;
94 int shadow;
95 int unpack_state = 0;
96 json_error_t err;
97
98 // interpret single gns record
99 unpack_state = json_unpack_ex (data,
100 &err,
101 0,
102 "{s:s, s:s, s:I, s:b, s:b, s:b, s:b}",
103 GNUNET_JSON_GNSRECORD_VALUE,
104 &value,
105 GNUNET_JSON_GNSRECORD_TYPE,
106 &record_type,
107 GNUNET_JSON_GNSRECORD_RELATIVE_EXPIRATION_TIME,
108 &rel_exp.rel_value_us,
109 GNUNET_JSON_GNSRECORD_FLAG_MAINTENANCE,
110 &maintenance,
111 GNUNET_JSON_GNSRECORD_FLAG_PRIVATE,
112 &private,
113 GNUNET_JSON_GNSRECORD_FLAG_SUPPLEMENTAL,
114 &supplemental,
115 GNUNET_JSON_GNSRECORD_FLAG_RELATIVE,
116 &is_rel_exp,
117 GNUNET_JSON_GNSRECORD_FLAG_SHADOW,
118 &shadow);
119 if (0 != unpack_state)
120 {
121 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
122 "Error gnsdata object has a wrong format: `%s'!\n",
123 err.text);
124 unpack_state = json_unpack_ex (data,
125 &err,
126 0,
127 "{s:s, s:s, s:I, s:b, s:b, s:b, s:b}",
128 GNUNET_JSON_GNSRECORD_VALUE,
129 &value,
130 GNUNET_JSON_GNSRECORD_TYPE,
131 &record_type,
132 GNUNET_JSON_GNSRECORD_ABSOLUTE_EXPIRATION_TIME,
133 &abs_exp.abs_value_us,
134 GNUNET_JSON_GNSRECORD_FLAG_MAINTENANCE,
135 &maintenance,
136 GNUNET_JSON_GNSRECORD_FLAG_PRIVATE,
137 &private,
138 GNUNET_JSON_GNSRECORD_FLAG_SUPPLEMENTAL,
139 &supplemental,
140 GNUNET_JSON_GNSRECORD_FLAG_RELATIVE,
141 &is_rel_exp,
142 GNUNET_JSON_GNSRECORD_FLAG_SHADOW,
143 &shadow);
144 if ((0 != unpack_state) || (is_rel_exp))
145 {
146 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
147 "Error gnsdata object has a wrong format: `%s'!\n",
148 (is_rel_exp) ? "No relative expiration given" : err.text);
149 return GNUNET_SYSERR;
150 }
151 rd->expiration_time = abs_exp.abs_value_us;
152 }
153 else
154 {
155 rd->expiration_time = rel_exp.rel_value_us;
156 }
157 rd->record_type = GNUNET_GNSRECORD_typename_to_number (record_type);
158 if (UINT32_MAX == rd->record_type)
159 {
160 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Unsupported type\n");
161 return GNUNET_SYSERR;
162 }
163 if (GNUNET_OK != GNUNET_GNSRECORD_string_to_value (rd->record_type,
164 value,
165 (void **) &rd->data,
166 &rd->data_size))
167 {
168 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Value invalid for record type\n");
169 return GNUNET_SYSERR;
170 }
171
172 if (is_rel_exp)
173 rd->flags |= GNUNET_GNSRECORD_RF_RELATIVE_EXPIRATION;
174 if (1 == maintenance)
175 rd->flags |= GNUNET_GNSRECORD_RF_MAINTENANCE;
176 if (1 == private)
177 rd->flags |= GNUNET_GNSRECORD_RF_PRIVATE;
178 if (1 == supplemental)
179 rd->flags |= GNUNET_GNSRECORD_RF_SUPPLEMENTAL;
180 if (1 == shadow)
181 rd->flags |= GNUNET_GNSRECORD_RF_SHADOW;
182 return GNUNET_OK;
183}
184
185
186/**
187 * Parse given JSON object to gns record
188 *
189 * @param cls closure, NULL
190 * @param root the json object representing data
191 * @param spec where to write the data
192 * @return #GNUNET_OK upon successful parsing; #GNUNET_SYSERR upon error
193 */
194static int
195parse_record_data (struct GnsRecordInfo *gnsrecord_info, json_t *data)
196{
197 GNUNET_assert (NULL != data);
198 if (! json_is_array (data))
199 {
200 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
201 "Error gns record data JSON is not an array!\n");
202 return GNUNET_SYSERR;
203 }
204 *(gnsrecord_info->rd_count) = json_array_size (data);
205 *(gnsrecord_info->rd) = GNUNET_malloc (sizeof(struct GNUNET_GNSRECORD_Data)
206 * json_array_size (data));
207 size_t index;
208 json_t *value;
209 json_array_foreach (data, index, value)
210 {
211 if (GNUNET_OK != parse_record (value, &(*(gnsrecord_info->rd))[index]))
212 return GNUNET_SYSERR;
213 }
214 return GNUNET_OK;
215}
216
217
218static int
219parse_gnsrecordobject (void *cls,
220 json_t *root,
221 struct GNUNET_JSON_Specification *spec)
222{
223 struct GnsRecordInfo *gnsrecord_info;
224 int unpack_state = 0;
225 const char *name;
226 json_t *data;
227
228 GNUNET_assert (NULL != root);
229 if (! json_is_object (root))
230 {
231 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
232 "Error record JSON is not an object!\n");
233 return GNUNET_SYSERR;
234 }
235 // interpret single gns record
236 unpack_state = json_unpack (root,
237 "{s:s, s:o!}",
238 GNUNET_JSON_GNSRECORD_RECORD_NAME,
239 &name,
240 GNUNET_JSON_GNSRECORD_RECORD_DATA,
241 &data);
242 if (0 != unpack_state)
243 {
244 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
245 "Error namestore records object has a wrong format!\n");
246 return GNUNET_SYSERR;
247 }
248 gnsrecord_info = (struct GnsRecordInfo *) spec->ptr;
249 *(gnsrecord_info->name) = GNUNET_strdup (name);
250 if (GNUNET_OK != parse_record_data (gnsrecord_info, data))
251 {
252 cleanup_recordinfo (gnsrecord_info);
253 return GNUNET_SYSERR;
254 }
255 return GNUNET_OK;
256}
257
258
259/**
260 * Cleanup data left from parsing the record.
261 *
262 * @param cls closure, NULL
263 * @param[out] spec where to free the data
264 */
265static void
266clean_gnsrecordobject (void *cls, struct GNUNET_JSON_Specification *spec)
267{
268 struct GnsRecordInfo *gnsrecord_info = (struct GnsRecordInfo *) spec->ptr;
269
270 GNUNET_free (gnsrecord_info);
271}
272
273
274/**
275 * JSON Specification for GNS Records.
276 *
277 * @param gnsrecord_object struct of GNUNET_GNSRECORD_Data to fill
278 * @return JSON Specification
279 */
280struct GNUNET_JSON_Specification
281GNUNET_GNSRECORD_JSON_spec_gnsrecord (struct GNUNET_GNSRECORD_Data **rd,
282 unsigned int *rd_count,
283 char **name)
284{
285 struct GnsRecordInfo *gnsrecord_info = GNUNET_new (struct GnsRecordInfo);
286
287 gnsrecord_info->rd = rd;
288 gnsrecord_info->name = name;
289 gnsrecord_info->rd_count = rd_count;
290 struct GNUNET_JSON_Specification ret = { .parser = &parse_gnsrecordobject,
291 .cleaner = &clean_gnsrecordobject,
292 .cls = NULL,
293 .field = NULL,
294 .ptr = (struct GnsRecordInfo *)
295 gnsrecord_info,
296 .ptr_size = 0,
297 .size_ptr = NULL };
298 return ret;
299}
300
301
302/**
303 * Convert GNS record to JSON.
304 *
305 * @param rname name of record
306 * @param rd record data
307 * @return corresponding JSON encoding
308 */
309json_t *
310GNUNET_GNSRECORD_JSON_from_gnsrecord (const char*rname,
311 const struct GNUNET_GNSRECORD_Data *rd,
312 unsigned int rd_count)
313{
314 const char *record_type_str;
315 char *value_str;
316 json_t *data;
317 json_t *record;
318 json_t *records;
319
320 data = json_object ();
321 if (NULL == data)
322 {
323 GNUNET_break (0);
324 return NULL;
325 }
326 if (0 !=
327 json_object_set_new (data,
328 "record_name",
329 json_string (rname)))
330 {
331 GNUNET_break (0);
332 json_decref (data);
333 return NULL;
334 }
335 records = json_array ();
336 if (NULL == records)
337 {
338 GNUNET_break (0);
339 json_decref (data);
340 return NULL;
341 }
342 for (int i = 0; i < rd_count; i++)
343 {
344 value_str = GNUNET_GNSRECORD_value_to_string (rd[i].record_type,
345 rd[i].data,
346 rd[i].data_size);
347 record_type_str = GNUNET_GNSRECORD_number_to_typename (rd[i].record_type);
348 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
349 "Packing %s %s %" PRIu64 " %d\n",
350 value_str, record_type_str, rd[i].expiration_time, rd[i].flags);
351 record = json_pack (
352 "{s:s,s:s,s:I,s:b,s:b,s:b,s:b, s:b}",
353 GNUNET_JSON_GNSRECORD_VALUE, value_str,
354 GNUNET_JSON_GNSRECORD_TYPE, record_type_str,
355 (rd[i].flags & GNUNET_GNSRECORD_RF_RELATIVE_EXPIRATION)
356 ? GNUNET_JSON_GNSRECORD_RELATIVE_EXPIRATION_TIME
357 : GNUNET_JSON_GNSRECORD_ABSOLUTE_EXPIRATION_TIME,
358 rd[i].expiration_time,
359 GNUNET_JSON_GNSRECORD_FLAG_MAINTENANCE,
360 rd[i].flags & GNUNET_GNSRECORD_RF_MAINTENANCE,
361 GNUNET_JSON_GNSRECORD_FLAG_PRIVATE,
362 rd[i].flags & GNUNET_GNSRECORD_RF_PRIVATE,
363 GNUNET_JSON_GNSRECORD_FLAG_RELATIVE,
364 rd[i].flags & GNUNET_GNSRECORD_RF_RELATIVE_EXPIRATION,
365 GNUNET_JSON_GNSRECORD_FLAG_SUPPLEMENTAL,
366 rd[i].flags & GNUNET_GNSRECORD_RF_SUPPLEMENTAL,
367 GNUNET_JSON_GNSRECORD_FLAG_SUPPLEMENTAL,
368 rd[i].flags & GNUNET_GNSRECORD_RF_SHADOW);
369 GNUNET_free (value_str);
370 if (NULL == record)
371 {
372 GNUNET_break (0);
373 json_decref (records);
374 json_decref (data);
375 return NULL;
376 }
377 if (0 !=
378 json_array_append_new (records,
379 record))
380 {
381 GNUNET_break (0);
382 json_decref (records);
383 json_decref (data);
384 return NULL;
385 }
386 }
387 if (0 !=
388 json_object_set_new (data,
389 "data",
390 records))
391 {
392 GNUNET_break (0);
393 json_decref (data);
394 return NULL;
395 }
396 return data;
397}
diff --git a/src/lib/gnsrecord/meson.build b/src/lib/gnsrecord/meson.build
new file mode 100644
index 000000000..dd71695ca
--- /dev/null
+++ b/src/lib/gnsrecord/meson.build
@@ -0,0 +1,86 @@
1libgnunetgnsrecord_src = ['gnsrecord.c',
2 'gnsrecord_serialization.c',
3 'gnsrecord_crypto.c',
4 'gnsrecord_pow.c',
5 'gnsrecord_misc.c']
6libgnunetgnsrecordjson_src = ['json_gnsrecord.c']
7
8libgnunetgnsrecord = library('gnunetgnsrecord',
9 libgnunetgnsrecord_src,
10 soversion: '0',
11 version: '0.0.0',
12 dependencies: [libgnunetutil_dep,
13 sodium_dep,
14 gcrypt_dep],
15 include_directories: [incdir, configuration_inc],
16 install: true,
17 install_dir: get_option('libdir'))
18libgnunetgnsrecord_dep = declare_dependency(link_with : libgnunetgnsrecord)
19pkg.generate(libgnunetgnsrecord, url: 'https://www.gnunet.org',
20 description : 'Provides API for manipulating GNS records')
21
22
23libgnunetgnsrecordjson = library('gnunetgnsrecordjson',
24 libgnunetgnsrecordjson_src,
25 soversion: '0',
26 version: '0.0.0',
27 dependencies: [libgnunetutil_dep, libgnunetgnsrecord_dep, json_dep],
28 include_directories: [incdir, configuration_inc],
29 install: true,
30 install_dir: get_option('libdir'))
31libgnunetgnsrecordjson_dep = declare_dependency(link_with : libgnunetgnsrecordjson)
32
33testgnsrecrd_perf_crypto = executable ('perf_gnsrecord_crypto',
34 ['perf_gnsrecord_crypto.c'],
35 dependencies: [libgnunetutil_dep,
36 libgnunetgnsrecord_dep],
37 include_directories: [incdir, configuration_inc],
38 build_by_default: false,
39 install: false)
40testgnsrecrd_test_crypto = executable ('test_gnsrecord_crypto',
41 ['test_gnsrecord_crypto.c'],
42 dependencies: [libgnunetutil_dep,
43 libgnunetgnsrecord_dep],
44 include_directories: [incdir, configuration_inc],
45 build_by_default: false,
46 install: false)
47testgnsrecrd_test_serialization = executable ('test_gnsrecord_serialization',
48 ['test_gnsrecord_serialization.c'],
49 dependencies: [libgnunetutil_dep,
50 libgnunetgnsrecord_dep],
51 include_directories: [incdir, configuration_inc],
52 build_by_default: false,
53 install: false)
54testgnsrecrd_test_tvs = executable ('test_gnsrecord_testvectors',
55 ['test_gnsrecord_testvectors.c'],
56 dependencies: [libgnunetutil_dep,
57 libgnunetgnsrecord_dep],
58 include_directories: [incdir, configuration_inc],
59 build_by_default: false,
60 install: false)
61testgnsrecrd_test_exp = executable ('test_gnsrecord_block_expiration',
62 ['test_gnsrecord_block_expiration.c'],
63 dependencies: [libgnunetutil_dep,
64 libgnunetgnsrecord_dep],
65 include_directories: [incdir, configuration_inc],
66 build_by_default: false,
67 install: false)
68
69test('perf_gnsrecord_crypto', testgnsrecrd_perf_crypto,
70 workdir: meson.current_build_dir(),
71 suite: ['gnsrecord', 'perf'])
72test('test_gnsrecord_crypto', testgnsrecrd_test_crypto,
73 workdir: meson.current_build_dir(),
74 suite: ['gnsrecord'])
75test('test_gnsrecord_serialization', testgnsrecrd_test_serialization,
76 workdir: meson.current_build_dir(),
77 suite: ['gnsrecord'])
78test('test_gnsrecord_block_expiration', testgnsrecrd_test_exp,
79 workdir: meson.current_build_dir(),
80 suite: ['gnsrecord'])
81test('test_gnsrecord_serialization', testgnsrecrd_test_serialization,
82 workdir: meson.current_build_dir(),
83 suite: ['gnsrecord'])
84test('test_gnsrecord_testvectors', testgnsrecrd_test_tvs,
85 workdir: meson.current_build_dir(),
86 suite: ['gnsrecord'])
diff --git a/src/lib/gnsrecord/perf_gnsrecord_crypto.c b/src/lib/gnsrecord/perf_gnsrecord_crypto.c
new file mode 100644
index 000000000..1f9a3d4d4
--- /dev/null
+++ b/src/lib/gnsrecord/perf_gnsrecord_crypto.c
@@ -0,0 +1,139 @@
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 gnsrecord/test_gnsrecord_crypto.c
22 * @brief testcase for block creation, verification and decryption
23 */
24#include "platform.h"
25#include "gnunet_util_lib.h"
26#include "gnunet_gnsrecord_lib.h"
27
28#define ROUNDS 1000
29
30#define RECORDS 5
31
32#define TEST_RECORD_TYPE 1234
33
34#define TEST_RECORD_DATALEN 123
35
36#define TEST_RECORD_DATA 'a'
37
38#define TEST_REMOVE_RECORD_TYPE 4321
39
40#define TEST_REMOVE_RECORD_DATALEN 255
41
42#define TEST_REMOVE_RECORD_DATA 'b'
43
44
45static struct GNUNET_GNSRECORD_Data *
46create_record (int count)
47{
48 struct GNUNET_GNSRECORD_Data *rd;
49
50 rd = GNUNET_new_array (count,
51 struct GNUNET_GNSRECORD_Data);
52 for (unsigned int c = 0; c < count; c++)
53 {
54 rd[c].expiration_time = GNUNET_TIME_absolute_get ().abs_value_us
55 + 1000000000;
56 rd[c].record_type = TEST_RECORD_TYPE;
57 rd[c].data_size = TEST_RECORD_DATALEN;
58 rd[c].data = GNUNET_malloc (TEST_RECORD_DATALEN);
59 memset ((char *) rd[c].data, TEST_RECORD_DATA, TEST_RECORD_DATALEN);
60 }
61 return rd;
62}
63
64
65static void
66run (void *cls,
67 char *const *args,
68 const char *cfgfile,
69 const struct GNUNET_CONFIGURATION_Handle *cfg)
70{
71 struct GNUNET_GNSRECORD_Block *block;
72 struct GNUNET_HashCode query;
73 struct GNUNET_GNSRECORD_Data *s_rd;
74 const char *s_name;
75 struct GNUNET_TIME_Absolute start_time;
76 struct GNUNET_CRYPTO_PrivateKey privkey;
77 struct GNUNET_TIME_Absolute expire;
78
79 (void) cls;
80 (void) args;
81 (void) cfgfile;
82 (void) cfg;
83 expire = GNUNET_TIME_absolute_get ();
84 privkey.type = htonl (GNUNET_GNSRECORD_TYPE_PKEY);
85 GNUNET_CRYPTO_ecdsa_key_create (&privkey.ecdsa_key);
86
87 /* test block creation */
88 s_name = "DUMMY.dummy.gnunet";
89 s_rd = create_record (RECORDS);
90 start_time = GNUNET_TIME_absolute_get ();
91 for (unsigned int i = 0; i < ROUNDS; i++)
92 {
93 GNUNET_assert (GNUNET_OK == GNUNET_GNSRECORD_block_create2 (&privkey,
94 expire,
95 s_name,
96 s_rd,
97 RECORDS,
98 &block));
99 GNUNET_GNSRECORD_query_from_private_key (&privkey,
100 s_name,
101 &query);
102 GNUNET_free (block);
103 }
104 fprintf (stderr,
105 "Took %s to produce %u GNS blocks for the DHT\n",
106 GNUNET_STRINGS_relative_time_to_string (
107 GNUNET_TIME_absolute_get_duration (start_time),
108 GNUNET_YES),
109 ROUNDS);
110 for (unsigned int i = 0; i < RECORDS; i++)
111 GNUNET_free_nz ((void *) s_rd[i].data);
112 GNUNET_free (s_rd);
113}
114
115
116int
117main (int argc, char *argv[])
118{
119 static char *const argvx[] = {
120 "perf-gnsrecord-crypto",
121 NULL
122 };
123 static struct GNUNET_GETOPT_CommandLineOption options[] = {
124 GNUNET_GETOPT_OPTION_END
125 };
126
127 if (GNUNET_OK !=
128 GNUNET_PROGRAM_run ((sizeof(argvx) / sizeof(char *)) - 1,
129 argvx,
130 "perf-gnsrecord-crypto",
131 "nohelp", options,
132 &run,
133 NULL))
134 return 1;
135 return 0;
136}
137
138
139/* end of test_gnsrecord_crypto.c */
diff --git a/src/lib/gnsrecord/test_gnsrecord_block_expiration.c b/src/lib/gnsrecord/test_gnsrecord_block_expiration.c
new file mode 100644
index 000000000..14b2acd97
--- /dev/null
+++ b/src/lib/gnsrecord/test_gnsrecord_block_expiration.c
@@ -0,0 +1,118 @@
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 * @file gnsrecord/test_gnsrecord_crypto.c
22 * @brief testcase for block creation, verification and decryption
23 */
24#include "platform.h"
25#include "gnunet_util_lib.h"
26#include "gnunet_gnsrecord_lib.h"
27
28#define RECORDS 5
29
30#define TEST_RECORD_TYPE 1234
31
32#define TEST_RECORD_DATALEN 123
33
34#define TEST_RECORD_DATA 'a'
35
36#define TEST_REMOVE_RECORD_TYPE 4321
37
38#define TEST_REMOVE_RECORD_DATALEN 255
39
40#define TEST_REMOVE_RECORD_DATA 'b'
41
42static int res;
43
44
45static void
46run (void *cls, char *const *args, const char *cfgfile,
47 const struct GNUNET_CONFIGURATION_Handle *cfg)
48{
49 struct GNUNET_GNSRECORD_Data rd[2];
50 struct GNUNET_TIME_Absolute expiration_abs;
51 struct GNUNET_TIME_Absolute expiration_abs_shadow;
52 char *tmp_data0;
53 char *tmp_data1;
54
55 expiration_abs.abs_value_us = GNUNET_TIME_absolute_get ().abs_value_us
56 + GNUNET_TIME_UNIT_SECONDS.rel_value_us;
57 expiration_abs_shadow.abs_value_us = GNUNET_TIME_absolute_get ().abs_value_us
58 + GNUNET_TIME_UNIT_MINUTES.rel_value_us;
59
60 /* create record */
61 rd[0].expiration_time = expiration_abs.abs_value_us;
62 rd[0].record_type = TEST_RECORD_TYPE;
63 rd[0].data_size = TEST_RECORD_DATALEN;
64 tmp_data0 = GNUNET_malloc (TEST_RECORD_DATALEN);
65 rd[0].data = tmp_data0;
66 rd[0].flags = GNUNET_GNSRECORD_RF_NONE;
67 memset ((char *) rd[0].data, TEST_RECORD_DATA, TEST_RECORD_DATALEN);
68
69 rd[1].expiration_time = expiration_abs.abs_value_us;
70 rd[1].record_type = TEST_RECORD_TYPE;
71 rd[1].data_size = TEST_RECORD_DATALEN;
72 tmp_data1 = GNUNET_malloc (TEST_RECORD_DATALEN);
73 rd[1].data = tmp_data1;
74 rd[1].flags = GNUNET_GNSRECORD_RF_NONE;
75 memset ((char *) rd[1].data, TEST_RECORD_DATA, TEST_RECORD_DATALEN);
76
77 GNUNET_assert (expiration_abs.abs_value_us ==
78 GNUNET_GNSRECORD_record_get_expiration_time (2,
79 rd,
80 GNUNET_TIME_UNIT_ZERO_ABS).abs_value_us);
81
82 rd[1].expiration_time = expiration_abs_shadow.abs_value_us;
83 rd[1].record_type = TEST_RECORD_TYPE;
84 rd[1].data_size = TEST_RECORD_DATALEN;
85 GNUNET_free (tmp_data1);
86 tmp_data1 = GNUNET_malloc (TEST_RECORD_DATALEN);
87 rd[1].data = tmp_data1;
88 rd[1].flags = GNUNET_GNSRECORD_RF_SHADOW;
89 memset ((char *) rd[1].data, TEST_RECORD_DATA, TEST_RECORD_DATALEN);
90
91 GNUNET_assert (expiration_abs_shadow.abs_value_us ==
92 GNUNET_GNSRECORD_record_get_expiration_time (2,
93 rd,
94 GNUNET_TIME_UNIT_ZERO_ABS).abs_value_us);
95 GNUNET_free (tmp_data0);
96 GNUNET_free (tmp_data1);
97 res = 0;
98}
99
100
101int
102main (int argc, char *argv[])
103{
104 static char *const argvx[] = { "test-gnsrecord-crypto",
105 NULL };
106 static struct GNUNET_GETOPT_CommandLineOption options[] = {
107 GNUNET_GETOPT_OPTION_END
108 };
109
110 res = 1;
111 GNUNET_PROGRAM_run ((sizeof(argvx) / sizeof(char *)) - 1, argvx,
112 "test-namestore-api",
113 "nohelp", options, &run, &res);
114 return res;
115}
116
117
118/* end of test_gnsrecord_crypto.c */
diff --git a/src/lib/gnsrecord/test_gnsrecord_crypto.c b/src/lib/gnsrecord/test_gnsrecord_crypto.c
new file mode 100644
index 000000000..34f5f35e5
--- /dev/null
+++ b/src/lib/gnsrecord/test_gnsrecord_crypto.c
@@ -0,0 +1,211 @@
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 * @file gnsrecord/test_gnsrecord_crypto.c
22 * @brief testcase for block creation, verification and decryption
23 */
24#include "platform.h"
25#include "gnunet_util_lib.h"
26#include "gnunet_gnsrecord_lib.h"
27
28#define RECORDS 5
29
30#define TEST_RECORD_TYPE GNUNET_DNSPARSER_TYPE_TXT
31
32#define TEST_RECORD_DATALEN 123
33
34#define TEST_RECORD_DATA 'a'
35
36#define TEST_REMOVE_RECORD_TYPE 4321
37
38#define TEST_REMOVE_RECORD_DATALEN 255
39
40#define TEST_REMOVE_RECORD_DATA 'b'
41
42
43static struct GNUNET_GNSRECORD_Data *s_rd;
44
45static char *s_name;
46
47static int res;
48
49
50static struct GNUNET_GNSRECORD_Data *
51create_record (int count)
52{
53 struct GNUNET_GNSRECORD_Data *rd;
54
55 rd = GNUNET_new_array (count, struct GNUNET_GNSRECORD_Data);
56 for (unsigned int c = 0; c < count; c++)
57 {
58 rd[c].expiration_time = GNUNET_TIME_absolute_get ().abs_value_us
59 + 1000000000;
60 rd[c].record_type = TEST_RECORD_TYPE;
61 rd[c].data_size = TEST_RECORD_DATALEN;
62 rd[c].data = GNUNET_malloc (TEST_RECORD_DATALEN);
63 memset ((char *) rd[c].data, TEST_RECORD_DATA, TEST_RECORD_DATALEN);
64 }
65 return rd;
66}
67
68
69static void
70rd_decrypt_cb (void *cls,
71 unsigned int rd_count,
72 const struct GNUNET_GNSRECORD_Data *rd)
73{
74 char rd_cmp_data[TEST_RECORD_DATALEN];
75
76 GNUNET_assert (RECORDS == rd_count);
77 GNUNET_assert (NULL != rd);
78 memset (rd_cmp_data,
79 'a',
80 TEST_RECORD_DATALEN);
81 for (unsigned int c = 0; c < rd_count; c++)
82 {
83 GNUNET_assert (TEST_RECORD_TYPE == rd[c].record_type);
84 GNUNET_assert (TEST_RECORD_DATALEN == rd[c].data_size);
85 GNUNET_assert (0 == memcmp (&rd_cmp_data,
86 rd[c].data,
87 TEST_RECORD_DATALEN));
88 }
89 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
90 "Block was decrypted successfully \n");
91 res = 0;
92}
93
94
95static void
96test_with_type (struct GNUNET_CRYPTO_PrivateKey *privkey)
97{
98 struct GNUNET_GNSRECORD_Block *block;
99 struct GNUNET_CRYPTO_PublicKey pubkey;
100 struct GNUNET_HashCode query_pub;
101 struct GNUNET_HashCode query_priv;
102 struct GNUNET_HashCode query_block;
103 struct GNUNET_TIME_Absolute expire = GNUNET_TIME_UNIT_FOREVER_ABS;
104 char* tmp_data;
105
106 /* get public key */
107 GNUNET_CRYPTO_key_get_public (privkey,
108 &pubkey);
109
110 /* test query derivation */
111 GNUNET_GNSRECORD_query_from_private_key (privkey,
112 "testlabel",
113 &query_priv);
114 GNUNET_GNSRECORD_query_from_public_key (&pubkey,
115 "testlabel",
116 &query_pub);
117 GNUNET_assert (0 == memcmp (&query_priv,
118 &query_pub,
119 sizeof(struct GNUNET_HashCode)));
120 /* create record */
121 s_name = "testlabel";
122 s_rd = create_record (RECORDS);
123
124 /* Create block */
125 GNUNET_assert (GNUNET_OK == GNUNET_GNSRECORD_block_create (privkey,
126 expire,
127 s_name,
128 s_rd,
129 RECORDS,
130 &block));
131 GNUNET_assert (GNUNET_OK ==
132 GNUNET_GNSRECORD_query_from_block (block,
133 &query_block));
134 GNUNET_assert (0 == memcmp (&query_pub,
135 &query_block,
136 sizeof(struct GNUNET_HashCode)));
137
138 GNUNET_assert (GNUNET_OK ==
139 GNUNET_GNSRECORD_block_verify (block));
140 GNUNET_assert (GNUNET_OK ==
141 GNUNET_GNSRECORD_block_decrypt (block,
142 &pubkey,
143 s_name,
144 &rd_decrypt_cb,
145 NULL));
146 for (int i = 0; i < RECORDS; i++)
147 {
148 tmp_data = (char*) s_rd[i].data;
149 GNUNET_free(tmp_data);
150 }
151 GNUNET_free (s_rd);
152 GNUNET_free (block);
153}
154
155
156static void
157run (void *cls,
158 char *const *args,
159 const char *cfgfile,
160 const struct GNUNET_CONFIGURATION_Handle *cfg)
161{
162 struct GNUNET_CRYPTO_PrivateKey privkey;
163 struct GNUNET_CRYPTO_PrivateKey privkey_ed;
164 struct GNUNET_TIME_Absolute start;
165 struct GNUNET_TIME_Absolute end;
166
167
168 privkey.type = htonl (GNUNET_GNSRECORD_TYPE_PKEY);
169 GNUNET_CRYPTO_ecdsa_key_create (&privkey.ecdsa_key);
170 start = GNUNET_TIME_absolute_get ();
171 test_with_type (&privkey);
172 end = GNUNET_TIME_absolute_get ();
173 printf ("Time: %llu ms\n", (unsigned long long)
174 GNUNET_TIME_absolute_get_difference (start,
175 end).rel_value_us);
176
177 privkey_ed.type = htonl (GNUNET_GNSRECORD_TYPE_EDKEY);
178 GNUNET_CRYPTO_eddsa_key_create (&privkey_ed.eddsa_key);
179 start = GNUNET_TIME_absolute_get ();
180 test_with_type (&privkey_ed);
181 end = GNUNET_TIME_absolute_get ();
182 printf ("Time: %llu ms\n", (unsigned long long)
183 GNUNET_TIME_absolute_get_difference (start,
184 end).rel_value_us);
185
186
187}
188
189
190int
191main (int argc, char *argv[])
192{
193 static char *const argvx[] = {
194 "test-gnsrecord-crypto",
195 NULL
196 };
197 static struct GNUNET_GETOPT_CommandLineOption options[] = {
198 GNUNET_GETOPT_OPTION_END
199 };
200
201 res = 1;
202 GNUNET_PROGRAM_run ((sizeof(argvx) / sizeof(char *)) - 1,
203 argvx,
204 "test-gnsrecord-crypto",
205 "nohelp", options,
206 &run, &res);
207 return res;
208}
209
210
211/* end of test_gnsrecord_crypto.c */
diff --git a/src/lib/gnsrecord/test_gnsrecord_serialization.c b/src/lib/gnsrecord/test_gnsrecord_serialization.c
new file mode 100644
index 000000000..b06b3a0fe
--- /dev/null
+++ b/src/lib/gnsrecord/test_gnsrecord_serialization.c
@@ -0,0 +1,156 @@
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 * @file gnsrecord/test_gnsrecord_serialization.c
22 * @brief testcase for gnsrecord_serialization.c
23 */
24#include "platform.h"
25#include "gnunet_util_lib.h"
26#include "gnunet_gnsrecord_lib.h"
27
28#define TIMEOUT GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS, 100)
29
30static int res;
31
32
33static void
34run (void *cls,
35 char *const *args,
36 const char *cfgfile,
37 const struct GNUNET_CONFIGURATION_Handle *cfg)
38{
39 size_t len;
40 int c;
41
42 int rd_count = 3;
43 size_t data_len;
44 struct GNUNET_GNSRECORD_Data src[rd_count];
45
46 memset (src, '\0', rd_count * sizeof(struct GNUNET_GNSRECORD_Data));
47
48 data_len = 0;
49 for (c = 0; c < rd_count; c++)
50 {
51 src[c].record_type = GNUNET_DNSPARSER_TYPE_TXT;
52 src[c].data_size = data_len;
53 src[c].data = GNUNET_malloc (data_len);
54
55 /* Setting data to data_len * record_type */
56 memset ((char *) src[c].data, 'a', data_len);
57 data_len += 10;
58 }
59 res = 0;
60
61 len = GNUNET_GNSRECORD_records_get_size (rd_count, src);
62 char rd_ser[len];
63 GNUNET_assert (len ==
64 GNUNET_GNSRECORD_records_serialize (rd_count,
65 src,
66 len,
67 rd_ser));
68
69 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
70 "Serialized data len: %u\n",
71 (unsigned int) len);
72
73 GNUNET_assert (rd_ser != NULL);
74 {
75 struct GNUNET_GNSRECORD_Data dst[rd_count];
76 GNUNET_assert (GNUNET_OK ==
77 GNUNET_GNSRECORD_records_deserialize (len,
78 rd_ser,
79 rd_count,
80 dst));
81
82 GNUNET_assert (dst != NULL);
83
84 for (c = 0; c < rd_count; c++)
85 {
86 if (src[c].data_size != dst[c].data_size)
87 {
88 GNUNET_break (0);
89 res = 1;
90 }
91 if (src[c].expiration_time != dst[c].expiration_time)
92 {
93 GNUNET_break (0);
94 res = 1;
95 }
96 if (src[c].flags != dst[c].flags)
97 {
98 GNUNET_break (0);
99 res = 1;
100 }
101 if (src[c].record_type != dst[c].record_type)
102 {
103 GNUNET_break (0);
104 res = 1;
105 }
106
107 {
108 size_t data_size = src[c].data_size;
109 char data[data_size];
110
111 memset (data, 'a', data_size);
112 if (0 != memcmp (data, dst[c].data, data_size))
113 {
114 GNUNET_break (0);
115 res = 1;
116 }
117 if (0 != memcmp (data, src[c].data, data_size))
118 {
119 GNUNET_break (0);
120 res = 1;
121 }
122 if (0 != memcmp (src[c].data, dst[c].data, src[c].data_size))
123 {
124 GNUNET_break (0);
125 res = 1;
126 }
127 }
128 }
129 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Element [%i]: EQUAL\n", c);
130 }
131
132 for (c = 0; c < rd_count; c++)
133 {
134 GNUNET_free_nz ((void *) src[c].data);
135 }
136}
137
138
139int
140main (int argcx, char *argvx[])
141{
142 static char *const argv[] = { "test_gnsrecord_serialization",
143 NULL };
144 static struct GNUNET_GETOPT_CommandLineOption options[] = {
145 GNUNET_GETOPT_OPTION_END
146 };
147
148 res = 1;
149 GNUNET_PROGRAM_run ((sizeof(argv) / sizeof(char *)) - 1, argv,
150 "test_namestore_record_serialization",
151 "nohelp", options, &run, &res);
152 return res;
153}
154
155
156/* end of test_gnsrecord_serialization.c */
diff --git a/src/lib/gnsrecord/test_gnsrecord_testvectors.c b/src/lib/gnsrecord/test_gnsrecord_testvectors.c
new file mode 100644
index 000000000..12b9efe56
--- /dev/null
+++ b/src/lib/gnsrecord/test_gnsrecord_testvectors.c
@@ -0,0 +1,716 @@
1#include "platform.h"
2#include "gnunet_util_lib.h"
3#include "gnunet_gns_service.h"
4#include "gnunet_gnsrecord_lib.h"
5#include <inttypes.h>
6#include "gnsrecord_crypto.h"
7
8int res;
9
10struct GnsTv
11{
12 uint32_t expected_rd_count;
13 struct GNUNET_GNSRECORD_Data expected_rd[2048];
14 char *d;
15 char *zid;
16 char *ztld;
17 char *label;
18 char *q;
19 char *rdata;
20 char *rrblock;
21 char *k;
22 char *nonce;
23};
24
25struct RevocationTv
26{
27 char *d;
28 char *zid;
29 char *ztld;
30 char *m;
31 char *proof;
32 int diff;
33 int epochs;
34};
35
36struct RevocationTv rtvs[] = {
37 {
38 .d =
39 "70 ed 98 b9 07 8c 47 f7"
40 "d5 78 3b 26 cc f9 8b 7d"
41 "d5 5f 60 88 d1 53 95 97"
42 "fa 8b f5 5a c0 32 ea 6f",
43 .zid =
44 "00 01 00 00 2c a2 23 e8"
45 "79 ec c4 bb de b5 da 17"
46 "31 92 81 d6 3b 2e 3b 69"
47 "55 f1 c3 77 5c 80 4a 98"
48 "d5 f8 dd aa",
49 .ztld =
50 "000G001CM8HYGYFCRJXXXDET2WRS50EP7CQ3PTANY71QEQ409ACDBY6XN8",
51 .m =
52 "00 00 00 34 00 00 00 03"
53 "00 05 fe b4 6d 86 5c 1c"
54 "00 01 00 00 2c a2 23 e8"
55 "79 ec c4 bb de b5 da 17"
56 "31 92 81 d6 3b 2e 3b 69"
57 "55 f1 c3 77 5c 80 4a 98"
58 "d5 f8 dd aa",
59 .proof =
60 "00 05 fe b4 6d 86 5c 1c"
61 "00 00 39 5d 18 27 c0 00"
62 "e6 6a 57 0b cc d4 b3 93"
63 "e6 6a 57 0b cc d4 b3 ea"
64 "e6 6a 57 0b cc d4 b5 36"
65 "e6 6a 57 0b cc d4 b5 42"
66 "e6 6a 57 0b cc d4 b6 13"
67 "e6 6a 57 0b cc d4 b6 5f"
68 "e6 6a 57 0b cc d4 b6 72"
69 "e6 6a 57 0b cc d4 b7 0a"
70 "e6 6a 57 0b cc d4 b7 1a"
71 "e6 6a 57 0b cc d4 b7 23"
72 "e6 6a 57 0b cc d4 b7 47"
73 "e6 6a 57 0b cc d4 b7 77"
74 "e6 6a 57 0b cc d4 b7 85"
75 "e6 6a 57 0b cc d4 b7 89"
76 "e6 6a 57 0b cc d4 b7 cf"
77 "e6 6a 57 0b cc d4 b7 dc"
78 "e6 6a 57 0b cc d4 b9 3a"
79 "e6 6a 57 0b cc d4 b9 56"
80 "e6 6a 57 0b cc d4 ba 4a"
81 "e6 6a 57 0b cc d4 ba 9d"
82 "e6 6a 57 0b cc d4 bb 28"
83 "e6 6a 57 0b cc d4 bb 5a"
84 "e6 6a 57 0b cc d4 bb 92"
85 "e6 6a 57 0b cc d4 bb a2"
86 "e6 6a 57 0b cc d4 bb d8"
87 "e6 6a 57 0b cc d4 bb e2"
88 "e6 6a 57 0b cc d4 bc 93"
89 "e6 6a 57 0b cc d4 bc 94"
90 "e6 6a 57 0b cc d4 bd 0f"
91 "e6 6a 57 0b cc d4 bd ce"
92 "e6 6a 57 0b cc d4 be 6a"
93 "e6 6a 57 0b cc d4 be 73"
94 "00 01 00 00 2c a2 23 e8"
95 "79 ec c4 bb de b5 da 17"
96 "31 92 81 d6 3b 2e 3b 69"
97 "55 f1 c3 77 5c 80 4a 98"
98 "d5 f8 dd aa 04 4a 87 8a"
99 "15 8b 40 f0 c8 41 d9 f9"
100 "78 cb 13 72 ea ee 51 99"
101 "a3 d8 7e 5e 2b db c7 2a"
102 "6c 8c 73 d0 00 18 1d fc"
103 "39 c3 aa a4 81 66 7b 16"
104 "5b 58 44 e4 50 71 3d 8a"
105 "b6 a3 b2 ba 8f ef 44 7b"
106 "65 07 6a 0f",
107 .diff = 5,
108 .epochs = 2
109 }
110};
111
112struct GnsTv tvs[] = {
113 { .d =
114 "50 d7 b6 52 a4 ef ea df"
115 "f3 73 96 90 97 85 e5 95"
116 "21 71 a0 21 78 c8 e7 d4"
117 "50 fa 90 79 25 fa fd 98",
118 .zid =
119 "00 01 00 00 67 7c 47 7d"
120 "2d 93 09 7c 85 b1 95 c6"
121 "f9 6d 84 ff 61 f5 98 2c"
122 "2c 4f e0 2d 5a 11 fe df"
123 "b0 c2 90 1f",
124 .ztld = "000G0037FH3QTBCK15Y8BCCNRVWPV17ZC7TSGB1C9ZG2TPGHZVFV1GMG3W",
125 .label = "74 65 73 74 64 65 6c 65"
126 "67 61 74 69 6f 6e",
127 .q =
128 "4a dc 67 c5 ec ee 9f 76"
129 "98 6a bd 71 c2 22 4a 3d"
130 "ce 2e 91 70 26 c9 a0 9d"
131 "fd 44 ce f3 d2 0f 55 a2"
132 "73 32 72 5a 6c 8a fb bb"
133 "b0 f7 ec 9a f1 cc 42 64"
134 "12 99 40 6b 04 fd 9b 5b"
135 "57 91 f8 6c 4b 08 d5 f4",
136 .nonce =
137 "e9 0a 00 61 00 1c ee 8c"
138 "10 e2 59 80 00 00 00 01",
139 .k =
140 "86 4e 71 38 ea e7 fd 91"
141 "a3 01 36 89 9c 13 2b 23"
142 "ac eb db 2c ef 43 cb 19"
143 "f6 bf 55 b6 7d b9 b3 b3",
144 .rdata =
145 "00 1c ee 8c 10 e2 59 80"
146 "00 20 00 01 00 01 00 00"
147 "21 e3 b3 0f f9 3b c6 d3"
148 "5a c8 c6 e0 e1 3a fd ff"
149 "79 4c b7 b4 4b bb c7 48"
150 "d2 59 d0 a0 28 4d be 84",
151 .rrblock =
152 "00 00 00 a0 00 01 00 00"
153 "18 2b b6 36 ed a7 9f 79"
154 "57 11 bc 27 08 ad bb 24"
155 "2a 60 44 6a d3 c3 08 03"
156 "12 1d 03 d3 48 b7 ce b6"
157 "0a d1 0b c1 3b 40 3b 5b"
158 "25 61 26 b2 14 5a 6f 60"
159 "c5 14 f9 51 ff a7 66 f7"
160 "a3 fd 4b ac 4a 4e 19 90"
161 "05 5c b8 7e 8d 1b fd 19"
162 "aa 09 a4 29 f7 29 e9 f5"
163 "c6 ee c2 47 0a ce e2 22"
164 "07 59 e9 e3 6c 88 6f 35"
165 "00 1c ee 8c 10 e2 59 80"
166 "0c 1e da 5c c0 94 a1 c7"
167 "a8 88 64 9d 25 fa ee bd"
168 "60 da e6 07 3d 57 d8 ae"
169 "8d 45 5f 4f 13 92 c0 74"
170 "e2 6a c6 69 bd ee c2 34"
171 "62 b9 62 95 2c c6 e9 eb"},
172 { .d =
173 "50 d7 b6 52 a4 ef ea df"
174 "f3 73 96 90 97 85 e5 95"
175 "21 71 a0 21 78 c8 e7 d4"
176 "50 fa 90 79 25 fa fd 98",
177 .zid =
178 "00 01 00 00 67 7c 47 7d"
179 "2d 93 09 7c 85 b1 95 c6"
180 "f9 6d 84 ff 61 f5 98 2c"
181 "2c 4f e0 2d 5a 11 fe df"
182 "b0 c2 90 1f",
183 .ztld = "000G0037FH3QTBCK15Y8BCCNRVWPV17ZC7TSGB1C9ZG2TPGHZVFV1GMG3W",
184 .label =
185 "e5 a4 a9 e4 b8 8b e7 84"
186 "a1 e6 95 b5",
187 .nonce =
188 "ee 96 33 c1 00 1c ee 8c"
189 "10 e2 59 80 00 00 00 01",
190 .k =
191 "fb 3a b5 de 23 bd da e1"
192 "99 7a af 7b 92 c2 d2 71"
193 "51 40 8b 77 af 7a 41 ac"
194 "79 05 7c 4d f5 38 3d 01",
195 .q =
196 "af f0 ad 6a 44 09 73 68"
197 "42 9a c4 76 df a1 f3 4b"
198 "ee 4c 36 e7 47 6d 07 aa"
199 "64 63 ff 20 91 5b 10 05"
200 "c0 99 1d ef 91 fc 3e 10"
201 "90 9f 87 02 c0 be 40 43"
202 "67 78 c7 11 f2 ca 47 d5"
203 "5c f0 b5 4d 23 5d a9 77",
204 .rdata =
205 "00 1c ee 8c 10 e2 59 80"
206 "00 10 00 00 00 00 00 1c"
207 "00 00 00 00 00 00 00 00"
208 "00 00 00 00 de ad be ef"
209 "00 3f f2 aa 54 08 db 40"
210 "00 06 00 00 00 01 00 01"
211 "e6 84 9b e7 a7 b0 00 28"
212 "bb 13 ff 37 19 40 00 0b"
213 "00 04 00 00 00 10 48 65"
214 "6c 6c 6f 20 57 6f 72 6c"
215 "64 00 00 00 00 00 00 00"
216 "00 00 00 00 00 00 00 00"
217 "00 00 00 00 00 00 00 00"
218 "00 00 00 00 00 00 00 00"
219 "00 00 00 00 00 00 00 00"
220 "00 00 00 00 00 00 00 00",
221 .rrblock =
222 "00 00 00 f0 00 01 00 00"
223 "a5 12 96 df 75 7e e2 75"
224 "ca 11 8d 4f 07 fa 7a ae"
225 "55 08 bc f5 12 aa 41 12"
226 "14 29 d4 a0 de 9d 05 7e"
227 "08 5b d6 5f d4 85 10 51"
228 "ba ce 2a 45 2a fc 8a 7e"
229 "4f 6b 2c 1f 74 f0 20 35"
230 "d9 64 1a cd ba a4 66 e0"
231 "00 ce d6 f2 d2 3b 63 1c"
232 "8e 8a 0b 38 e2 ba e7 9a"
233 "22 ca d8 1d 4c 50 d2 25"
234 "35 8e bc 17 ac 0f 89 9e"
235 "00 1c ee 8c 10 e2 59 80"
236 "d8 c2 8d 2f d6 96 7d 1a"
237 "b7 22 53 f2 10 98 b8 14"
238 "a4 10 be 1f 59 98 de 03"
239 "f5 8f 7e 7c db 7f 08 a6"
240 "16 51 be 4d 0b 6f 8a 61"
241 "df 15 30 44 0b d7 47 dc"
242 "f0 d7 10 4f 6b 8d 24 c2"
243 "ac 9b c1 3d 9c 6f e8 29"
244 "05 25 d2 a6 d0 f8 84 42"
245 "67 a1 57 0e 8e 29 4d c9"
246 "3a 31 9f cf c0 3e a2 70"
247 "17 d6 fd a3 47 b4 a7 94"
248 "97 d7 f6 b1 42 2d 4e dd"
249 "82 1c 19 93 4e 96 c1 aa"
250 "87 76 57 25 d4 94 c7 64"
251 "b1 55 dc 6d 13 26 91 74"},
252 { .d =
253 "5a f7 02 0e e1 91 60 32"
254 "88 32 35 2b bc 6a 68 a8"
255 "d7 1a 7c be 1b 92 99 69"
256 "a7 c6 6d 41 5a 0d 8f 65",
257 .zid =
258 "00 01 00 14 3c f4 b9 24"
259 "03 20 22 f0 dc 50 58 14"
260 "53 b8 5d 93 b0 47 b6 3d"
261 "44 6c 58 45 cb 48 44 5d"
262 "db 96 68 8f",
263 .ztld = "000G051WYJWJ80S04BRDRM2R2H9VGQCKP13VCFA4DHC4BJT88HEXQ5K8HW",
264 .label =
265 "74 65 73 74 64 65 6c 65"
266 "67 61 74 69 6f 6e",
267 .nonce =
268 "98 13 2e a8 68 59 d3 5c"
269 "88 bf d3 17 fa 99 1b cb"
270 "00 1c ee 8c 10 e2 59 80",
271 .k =
272 "85 c4 29 a9 56 7a a6 33"
273 "41 1a 96 91 e9 09 4c 45"
274 "28 16 72 be 58 60 34 aa"
275 "e4 a2 a2 cc 71 61 59 e2",
276 .q =
277 "ab aa ba c0 e1 24 94 59"
278 "75 98 83 95 aa c0 24 1e"
279 "55 59 c4 1c 40 74 e2 55"
280 "7b 9f e6 d1 54 b6 14 fb"
281 "cd d4 7f c7 f5 1d 78 6d"
282 "c2 e0 b1 ec e7 60 37 c0"
283 "a1 57 8c 38 4e c6 1d 44"
284 "56 36 a9 4e 88 03 29 e9",
285 .rdata =
286 "00 1c ee 8c 10 e2 59 80"
287 "00 20 00 01 00 01 00 00"
288 "21 e3 b3 0f f9 3b c6 d3"
289 "5a c8 c6 e0 e1 3a fd ff"
290 "79 4c b7 b4 4b bb c7 48"
291 "d2 59 d0 a0 28 4d be 84",
292 .rrblock =
293 "00 00 00 b0 00 01 00 14"
294 "9b f2 33 19 8c 6d 53 bb"
295 "db ac 49 5c ab d9 10 49"
296 "a6 84 af 3f 40 51 ba ca"
297 "b0 dc f2 1c 8c f2 7a 1a"
298 "9f 56 a8 86 ea 73 9d 59"
299 "17 50 8f 9b 75 56 39 f3"
300 "a9 ac fa ed ed ca 7f bf"
301 "a7 94 b1 92 e0 8b f9 ed"
302 "4c 7e c8 59 4c 9f 7b 4e"
303 "19 77 4f f8 38 ec 38 7a"
304 "8f 34 23 da ac 44 9f 59"
305 "db 4e 83 94 3f 90 72 00"
306 "00 1c ee 8c 10 e2 59 80"
307 "57 7c c6 c9 5a 14 e7 04"
308 "09 f2 0b 01 67 e6 36 d0"
309 "10 80 7c 4f 00 37 2d 69"
310 "8c 82 6b d9 2b c2 2b d6"
311 "bb 45 e5 27 7c 01 88 1d"
312 "6a 43 60 68 e4 dd f1 c6"
313 "b7 d1 41 6f af a6 69 7c"
314 "25 ed d9 ea e9 91 67 c3"},
315 { .d =
316 "5a f7 02 0e e1 91 60 32"
317 "88 32 35 2b bc 6a 68 a8"
318 "d7 1a 7c be 1b 92 99 69"
319 "a7 c6 6d 41 5a 0d 8f 65",
320 .zid =
321 "00 01 00 14 3c f4 b9 24"
322 "03 20 22 f0 dc 50 58 14"
323 "53 b8 5d 93 b0 47 b6 3d"
324 "44 6c 58 45 cb 48 44 5d"
325 "db 96 68 8f",
326 .ztld = "000G051WYJWJ80S04BRDRM2R2H9VGQCKP13VCFA4DHC4BJT88HEXQ5K8HW",
327 .label =
328 "e5 a4 a9 e4 b8 8b e7 84"
329 "a1 e6 95 b5",
330 .nonce =
331 "bb 0d 3f 0f bd 22 42 77"
332 "50 da 5d 69 12 16 e6 c9"
333 "00 1c ee 8c 10 e2 59 80",
334 .k =
335 "3d f8 05 bd 66 87 aa 14"
336 "20 96 28 c2 44 b1 11 91"
337 "88 c3 92 56 37 a4 1e 5d"
338 "76 49 6c 29 45 dc 37 7b",
339 .q =
340 "ba f8 21 77 ee c0 81 e0"
341 "74 a7 da 47 ff c6 48 77"
342 "58 fb 0d f0 1a 6c 7f bb"
343 "52 fc 8a 31 be f0 29 af"
344 "74 aa 0d c1 5a b8 e2 fa"
345 "7a 54 b4 f5 f6 37 f6 15"
346 "8f a7 f0 3c 3f ce be 78"
347 "d3 f9 d6 40 aa c0 d1 ed",
348 .rdata =
349 "00 1c ee 8c 10 e2 59 80"
350 "00 10 00 00 00 00 00 1c"
351 "00 00 00 00 00 00 00 00"
352 "00 00 00 00 de ad be ef"
353 "00 3f f2 aa 54 08 db 40"
354 "00 06 00 00 00 01 00 01"
355 "e6 84 9b e7 a7 b0 00 28"
356 "bb 13 ff 37 19 40 00 0b"
357 "00 04 00 00 00 10 48 65"
358 "6c 6c 6f 20 57 6f 72 6c"
359 "64 00 00 00 00 00 00 00"
360 "00 00 00 00 00 00 00 00"
361 "00 00 00 00 00 00 00 00"
362 "00 00 00 00 00 00 00 00"
363 "00 00 00 00 00 00 00 00"
364 "00 00 00 00 00 00 00 00",
365 .rrblock =
366 "00 00 01 00 00 01 00 14"
367 "74 f9 00 68 f1 67 69 53"
368 "52 a8 a6 c2 eb 98 48 98"
369 "c5 3a cc a0 98 04 70 c6"
370 "c8 12 64 cb dd 78 ad 11"
371 "75 6d 2c 15 7a d2 ea 4f"
372 "c0 b1 b9 1c 08 03 79 44"
373 "61 d3 de f2 0d d1 63 6c"
374 "fe dc 03 89 c5 49 d1 43"
375 "6c c3 5b 4e 1b f8 89 5a"
376 "64 6b d9 a6 f4 6b 83 48"
377 "1d 9c 0e 91 d4 e1 be bb"
378 "6a 83 52 6f b7 25 2a 06"
379 "00 1c ee 8c 10 e2 59 80"
380 "4e b3 5a 50 d4 0f e1 a4"
381 "29 c7 f4 b2 67 a0 59 de"
382 "4e 2c 8a 89 a5 ed 53 d3"
383 "d4 92 58 59 d2 94 9f 7f"
384 "30 d8 a2 0c aa 96 f8 81"
385 "45 05 2d 1c da 04 12 49"
386 "8f f2 5f f2 81 6e f0 ce"
387 "61 fe 69 9b fa c7 2c 15"
388 "dc 83 0e a9 b0 36 17 1c"
389 "cf ca bb dd a8 de 3c 86"
390 "ed e2 95 70 d0 17 4b 82"
391 "82 09 48 a9 28 b7 f0 0e"
392 "fb 40 1c 10 fe 80 bb bb"
393 "02 76 33 1b f7 f5 1b 8d"
394 "74 57 9c 14 14 f2 2d 50"
395 "1a d2 5a e2 49 f5 bb f2"
396 "a6 c3 72 59 d1 75 e4 40"
397 "b2 94 39 c6 05 19 cb b1"},
398 {.d = NULL}
399};
400
401static void
402print_bytes_ (void *buf,
403 size_t buf_len,
404 int fold,
405 int in_be)
406{
407 int i;
408
409 for (i = 0; i < buf_len; i++)
410 {
411 if (0 != i)
412 {
413 if ((0 != fold) && (i % fold == 0))
414 printf ("\n ");
415 else
416 printf (" ");
417 }
418 else
419 {
420 printf (" ");
421 }
422 if (in_be)
423 printf ("%02x", ((unsigned char*) buf)[buf_len - 1 - i]);
424 else
425 printf ("%02x", ((unsigned char*) buf)[i]);
426 }
427 printf ("\n");
428}
429
430
431static void
432print_bytes (void *buf,
433 size_t buf_len,
434 int fold)
435{
436 print_bytes_ (buf, buf_len, fold, 0);
437}
438
439
440int
441parsehex (char *src, char *dst, size_t dstlen, int invert)
442{
443 int off;
444 int read_byte;
445 int data_len = 0;
446 char data[strlen (src) + 1];
447 char *pos = data;
448 int i = 0;
449 int j = 0;
450 memset (data, 0, strlen (src) + 1);
451
452 for (i = 0; i < strlen (src); i++)
453 {
454 if ((src[i] == ' ') || (src[i] == '\n'))
455 continue;
456 data[j++] = src[i];
457 }
458
459 while (sscanf (pos, " %02x%n", &read_byte, &off) == 1)
460 {
461 if (invert)
462 dst[dstlen - 1 - data_len++] = read_byte;
463 else
464 dst[data_len++] = read_byte;
465 pos += off;
466 }
467 return data_len;
468}
469
470
471void
472res_checker (void *cls,
473 unsigned int rd_count, const struct GNUNET_GNSRECORD_Data *rd)
474{
475 struct GnsTv *tv = cls;
476 if (rd_count != tv->expected_rd_count)
477 {
478 printf ("FAIL: Record count expected: %u, was: %u\n", tv->expected_rd_count,
479 rd_count);
480 res = 1;
481 return;
482 }
483 for (int i = 0; i < rd_count; i++)
484 {
485 if (rd[i].record_type != tv->expected_rd[i].record_type)
486 {
487 printf ("FAIL: Record type expected: %u, was: %u\n",
488 tv->expected_rd[i].record_type,
489 rd[i].record_type);
490 res = 1;
491 return;
492 }
493 if (rd[i].expiration_time != tv->expected_rd[i].expiration_time)
494 {
495 printf ("FAIL: Expiration expected: %" PRIu64 ", was: %" PRIu64 "\n",
496 tv->expected_rd[i].expiration_time,
497 rd[i].expiration_time);
498 res = 1;
499 return;
500 }
501 if (rd[i].flags != tv->expected_rd[i].flags)
502 {
503 printf ("FAIL: Record flags expected: %u, was: %u\n",
504 tv->expected_rd[i].flags,
505 rd[i].flags);
506 res = 1;
507 return;
508 }
509 if (rd[i].data_size != tv->expected_rd[i].data_size)
510 {
511 printf ("FAIL: Record data size expected: %lu, was: %lu\n",
512 tv->expected_rd[i].data_size,
513 rd[i].data_size);
514 res = 1;
515 return;
516 }
517 if (0 != memcmp (rd[i].data, tv->expected_rd[i].data,
518 rd[i].data_size))
519 {
520 printf ("FAIL: Record data does not match\n");
521 res = 1;
522 return;
523 }
524 }
525}
526
527
528enum GNUNET_GenericReturnValue
529check_derivations_edkey (const char*label,
530 struct GNUNET_TIME_Absolute expire,
531 struct GNUNET_CRYPTO_PublicKey *pub,
532 struct GnsTv *tv)
533{
534 unsigned char nonce[crypto_secretbox_NONCEBYTES];
535 unsigned char skey[crypto_secretbox_KEYBYTES];
536 unsigned char nonce_expected[crypto_secretbox_NONCEBYTES];
537 unsigned char skey_expected[crypto_secretbox_KEYBYTES];
538
539
540 parsehex (tv->nonce,(char*) nonce_expected, crypto_secretbox_NONCEBYTES, 0);
541 parsehex (tv->k,(char*) skey_expected, crypto_secretbox_KEYBYTES, 0);
542 GNR_derive_block_xsalsa_key (nonce,
543 skey,
544 label,
545 GNUNET_TIME_absolute_hton (
546 expire).abs_value_us__,
547 &pub->eddsa_key);
548 /* Ignore random 128-bit nonce, can't check this here. Will be checked on
549 * decryption. */
550 if (0 != memcmp (nonce + 16, nonce_expected + 16, sizeof (nonce) - 16))
551 {
552 printf ("FAIL: Failed to derive nonce:\n");
553 print_bytes (nonce, sizeof (nonce), 8);
554 print_bytes (nonce_expected, sizeof (nonce), 8);
555 return GNUNET_NO;
556 }
557 if (0 != memcmp (skey, skey_expected, sizeof (skey)))
558 {
559 printf ("FAIL: Failed to derive secret key\n");
560 return GNUNET_NO;
561 }
562 return GNUNET_OK;
563}
564
565
566enum GNUNET_GenericReturnValue
567check_derivations_pkey (const char*label,
568 struct GNUNET_TIME_Absolute expire,
569 struct GNUNET_CRYPTO_PublicKey *pub,
570 struct GnsTv *tv)
571{
572 unsigned char ctr[GNUNET_CRYPTO_AES_KEY_LENGTH / 2];
573 unsigned char ctr_expected[GNUNET_CRYPTO_AES_KEY_LENGTH / 2];
574 unsigned char skey[GNUNET_CRYPTO_AES_KEY_LENGTH];
575 unsigned char skey_expected[GNUNET_CRYPTO_AES_KEY_LENGTH];
576
577 parsehex (tv->nonce,(char*) ctr_expected, sizeof (ctr), 0);
578 parsehex (tv->k,(char*) skey_expected, sizeof (skey), 0);
579 GNR_derive_block_aes_key (ctr,
580 skey,
581 label,
582 GNUNET_TIME_absolute_hton (
583 expire).abs_value_us__,
584 &pub->ecdsa_key);
585
586 /* Ignore random 32-bit nonce, can't check this here. Will be checked on
587 * decryption. */
588 if (0 != memcmp (ctr + 4, ctr_expected + 4, sizeof (ctr) - 4))
589 {
590 printf ("FAIL: Failed to derive nonce\n");
591 return GNUNET_NO;
592 }
593 if (0 != memcmp (skey, skey_expected, sizeof (skey)))
594 {
595 printf ("FAIL: Failed to derive secret key\n");
596 return GNUNET_NO;
597 }
598 return GNUNET_OK;
599}
600
601
602int
603main ()
604{
605 struct GNUNET_CRYPTO_PrivateKey priv;
606 struct GNUNET_CRYPTO_PublicKey pub;
607 struct GNUNET_CRYPTO_PublicKey pub_parsed;
608 struct GNUNET_GNSRECORD_Block *rrblock;
609 struct GNUNET_HashCode query;
610 struct GNUNET_HashCode expected_query;
611 struct GNUNET_TIME_Absolute expire;
612 char label[128];
613 char rdata[8096];
614 char ztld[128];
615 res = 0;
616
617 for (int i = 0; NULL != tvs[i].d; i++)
618 {
619 printf ("Test vector #%d\n", i);
620 memset (label, 0, sizeof (label));
621 parsehex (tvs[i].zid,(char*) &pub_parsed, 36, 0);
622 parsehex (tvs[i].d,(char*) &priv.ecdsa_key, sizeof (priv.ecdsa_key),
623 (GNUNET_GNSRECORD_TYPE_PKEY == ntohl (pub_parsed.type)) ? 1 : 0);
624 priv.type = pub_parsed.type;
625 GNUNET_CRYPTO_key_get_public (&priv, &pub);
626 if (0 != memcmp (&pub, &pub_parsed, GNUNET_CRYPTO_public_key_get_length (
627 &pub)))
628 {
629 printf ("Wrong pubkey.\n");
630 print_bytes (&pub, 36, 8);
631 print_bytes (&pub_parsed, 36, 8);
632 res = 1;
633 break;
634 }
635 GNUNET_STRINGS_data_to_string (&pub,
636 GNUNET_CRYPTO_public_key_get_length (
637 &pub),
638 ztld,
639 sizeof (ztld));
640 if (0 != strcmp (ztld, tvs[i].ztld))
641 {
642 printf ("Wrong zTLD: expected %s, got %s\n", tvs[i].ztld, ztld);
643 res = 1;
644 break;
645 }
646 rrblock = GNUNET_malloc (strlen (tvs[i].rrblock));
647 parsehex (tvs[i].rrblock, (char*) rrblock, 0, 0);
648 parsehex (tvs[i].label, (char*) label, 0, 0);
649 parsehex (tvs[i].q, (char*) &query, 0, 0);
650 GNUNET_GNSRECORD_query_from_public_key (&pub_parsed,
651 label,
652 &expected_query);
653 if (0 != GNUNET_memcmp (&query, &expected_query))
654 {
655 printf ("FAIL: query does not match:");
656 printf (" expected: %s", GNUNET_h2s (&expected_query));
657 printf (", was: %s\n", GNUNET_h2s (&query));
658 GNUNET_free (rrblock);
659 res = 1;
660 break;
661 }
662 int len = parsehex (tvs[i].rdata, (char*) rdata, 0, 0);
663 tvs[i].expected_rd_count =
664 GNUNET_GNSRECORD_records_deserialize_get_size (len,
665 rdata);
666 GNUNET_assert (tvs[i].expected_rd_count < 2048);
667 if (GNUNET_OK !=
668 GNUNET_GNSRECORD_records_deserialize (len,
669 rdata,
670 tvs[i].expected_rd_count,
671 tvs[i].expected_rd))
672 {
673 printf ("FAIL: Deserialization of RDATA failed\n");
674 res = 1;
675 GNUNET_free (rrblock);
676 break;
677 }
678 expire = GNUNET_GNSRECORD_record_get_expiration_time (
679 tvs[i].expected_rd_count,
680 tvs[i].expected_rd,
681 GNUNET_TIME_UNIT_ZERO_ABS);
682 if ((GNUNET_GNSRECORD_TYPE_PKEY == ntohl (pub.type)) &&
683 (GNUNET_OK != check_derivations_pkey (label, expire, &pub, &tvs[i])))
684 {
685 res = 1;
686 GNUNET_free (rrblock);
687 break;
688 }
689 else if ((GNUNET_GNSRECORD_TYPE_EDKEY == ntohl (pub.type)) &&
690 (GNUNET_OK != check_derivations_edkey (label, expire, &pub,
691 &tvs[i])))
692 {
693 res = 1;
694 GNUNET_free (rrblock);
695 break;
696 }
697 if (GNUNET_OK != GNUNET_GNSRECORD_block_decrypt (rrblock,
698 &pub_parsed,
699 label,
700 &res_checker,
701 &tvs[i]))
702 {
703 printf ("FAIL: Decryption of RRBLOCK failed\n");
704 res = 1;
705 GNUNET_free (rrblock);
706 break;
707 }
708 if (0 != res)
709 {
710 GNUNET_free (rrblock);
711 break;
712 }
713 printf ("Good.\n");
714 }
715 return res;
716}
diff --git a/src/lib/hello/.gitignore b/src/lib/hello/.gitignore
new file mode 100644
index 000000000..d175d148e
--- /dev/null
+++ b/src/lib/hello/.gitignore
@@ -0,0 +1,5 @@
1gnunet-hello
2test_friend_hello
3test_hello
4test_hello-uri
5test_hello-ng
diff --git a/src/lib/hello/Makefile.am b/src/lib/hello/Makefile.am
new file mode 100644
index 000000000..c7b3e4a05
--- /dev/null
+++ b/src/lib/hello/Makefile.am
@@ -0,0 +1,33 @@
1# This Makefile.am is in the public domain
2AM_CPPFLAGS = -I$(top_srcdir)/src/include
3
4if USE_COVERAGE
5 AM_CFLAGS = --coverage -O0
6 XLIB = -lgcov
7endif
8
9lib_LTLIBRARIES = libgnunethello.la
10
11libgnunethello_la_SOURCES = \
12 hello-uri.c
13libgnunethello_la_LIBADD = \
14 $(top_builddir)/src/lib/util/libgnunetutil.la $(XLIB) \
15 $(LTLIBINTL)
16libgnunethello_la_LDFLAGS = \
17 $(GN_LIB_LDFLAGS) \
18 -version-info 1:0:1
19
20check_PROGRAMS = \
21 test_hello-uri
22
23if ENABLE_TEST_RUN
24AM_TESTS_ENVIRONMENT=export GNUNET_PREFIX=$${GNUNET_PREFIX:-@libdir@};export PATH=$${GNUNET_PREFIX:-@prefix@}/bin:$$PATH;unset XDG_DATA_HOME;unset XDG_CONFIG_HOME;
25TESTS = $(check_PROGRAMS)
26endif
27
28test_hello_uri_SOURCES = \
29 test_hello-uri.c
30test_hello_uri_LDADD = \
31 libgnunethello.la \
32 $(top_builddir)/src/lib/util/libgnunetutil.la \
33 -lgcrypt
diff --git a/src/lib/hello/gnunet-hello.c b/src/lib/hello/gnunet-hello.c
new file mode 100644
index 000000000..aaa4b5005
--- /dev/null
+++ b/src/lib/hello/gnunet-hello.c
@@ -0,0 +1,426 @@
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 hello/gnunet-hello.c
22 * @brief change HELLO files to never expire
23 * @author Christian Grothoff
24 */
25#include "platform.h"
26#include "gnunet_protocols.h"
27#include "gnunet_hello_uri_lib.h"
28#include "gnunet_transport_plugin.h"
29
30/**
31 * Closure for #add_to_buf().
32 */
33struct AddContext
34{
35 /**
36 * Where to add.
37 */
38 char *buf;
39
40 /**
41 * Maximum number of bytes left
42 */
43 size_t max;
44
45 /**
46 * Number of bytes added so far.
47 */
48 size_t ret;
49
50 struct GNUNET_HELLO_Builder *builder;
51};
52
53/**
54 * Entry in doubly-linked list of all of our plugins.
55 */
56struct TransportPlugin
57{
58 /**
59 * This is a doubly-linked list.
60 */
61 struct TransportPlugin *next;
62
63 /**
64 * This is a doubly-linked list.
65 */
66 struct TransportPlugin *prev;
67
68 /**
69 * API of the transport as returned by the plugin's
70 * initialization function.
71 */
72 struct GNUNET_TRANSPORT_PluginFunctions *api;
73
74 /**
75 * Short name for the plugin (e.g. "tcp").
76 */
77 char *short_name;
78
79 /**
80 * Name of the library (e.g. "gnunet_plugin_transport_tcp").
81 */
82 char *lib_name;
83
84 /**
85 * Environment this transport service is using
86 * for this plugin.
87 */
88 struct GNUNET_TRANSPORT_PluginEnvironment env;
89};
90
91static int address_count;
92
93/**
94 * Our private key.
95 */
96static struct GNUNET_CRYPTO_EddsaPrivateKey *my_private_key;
97
98/**
99 * Local peer own ID.
100 */
101struct GNUNET_PeerIdentity my_full_id;
102
103/**
104 * The file with hello in old style which we like to replace with the new one.
105 */
106static char *hello_file;
107
108/**
109 * Head of DLL of all loaded plugins.
110 */
111static struct TransportPlugin *plugins_head;
112
113/**
114 * Head of DLL of all loaded plugins.
115 */
116static struct TransportPlugin *plugins_tail;
117
118static void
119plugins_load (const struct GNUNET_CONFIGURATION_Handle *cfg)
120{
121 struct TransportPlugin *plug;
122 struct TransportPlugin *next;
123 char *libname;
124 char *plugs;
125 char *pos;
126
127 if (NULL != plugins_head)
128 return; /* already loaded */
129 if (GNUNET_OK !=
130 GNUNET_CONFIGURATION_get_value_string (cfg, "TRANSPORT", "PLUGINS",
131 &plugs))
132 return;
133 fprintf (stdout,"Starting transport plugins `%s'\n",
134 plugs);
135 for (pos = strtok (plugs, " "); pos != NULL; pos = strtok (NULL, " "))
136 {
137 fprintf (stdout,"Loading `%s' transport plugin\n",
138 pos);
139 GNUNET_asprintf (&libname, "libgnunet_plugin_transport_%s", pos);
140 plug = GNUNET_new (struct TransportPlugin);
141 plug->short_name = GNUNET_strdup (pos);
142 plug->lib_name = libname;
143 plug->env.cfg = cfg;
144 plug->env.cls = plug->short_name;
145 GNUNET_CONTAINER_DLL_insert (plugins_head, plugins_tail, plug);
146 }
147 GNUNET_free (plugs);
148 next = plugins_head;
149 while (next != NULL)
150 {
151 plug = next;
152 next = plug->next;
153 plug->api = GNUNET_PLUGIN_load (plug->lib_name, &plug->env);
154 if (plug->api == NULL)
155 {
156 fprintf (stdout,"Failed to load transport plugin for `%s'\n",
157 plug->lib_name);
158 GNUNET_CONTAINER_DLL_remove (plugins_head, plugins_tail, plug);
159 GNUNET_free (plug->short_name);
160 GNUNET_free (plug->lib_name);
161 GNUNET_free (plug);
162 }
163 }
164}
165
166
167static int
168add_to_builder (void *cls,
169 const struct GNUNET_HELLO_Address *address,
170 struct GNUNET_TIME_Absolute expiration)
171{
172 struct GNUNET_HELLO_Builder *builder= cls;
173 struct TransportPlugin *pos = plugins_head;
174 const char *addr;
175 char *uri;
176
177 while (NULL != pos)
178 {
179 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
180 "short_name: %s transport_name: %s\n",
181 pos->short_name,
182 address->transport_name);
183 if (0 == strcmp (address->transport_name, pos->short_name))
184 {
185 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
186 "short_name: %s transport_name: %s are the same\n",
187 pos->short_name,
188 address->transport_name);
189 addr = strchr (strchr (pos->api->address_to_string (pos, address, address->address_length), '.')+1, '.') + 1;
190 }
191 pos = pos->next;
192 }
193
194 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
195 "Hello address string: %s\n",
196 addr);
197 GNUNET_asprintf (&uri, "%s://%s", address->transport_name, addr);
198 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
199 "Hello address uri string: %s\n",
200 uri);
201 GNUNET_HELLO_builder_add_address (builder,
202 uri);
203}
204
205
206/**
207 * Add the given address with infinite expiration to the buffer.
208 *
209 * @param cls closure
210 * @param address address to add
211 * @param expiration old expiration
212 * @return #GNUNET_OK keep iterating
213 */
214static int
215add_to_buf (void *cls,
216 const struct GNUNET_HELLO_Address *address,
217 struct GNUNET_TIME_Absolute expiration)
218{
219 struct AddContext *ac = cls;
220 size_t ret;
221
222 ret = GNUNET_HELLO_add_address (address,
223 GNUNET_TIME_UNIT_FOREVER_ABS,
224 ac->buf,
225 ac->max);
226
227 ac->buf += ret;
228 ac->max -= ret;
229 ac->ret += ret;
230 address_count++;
231 return GNUNET_OK;
232}
233
234
235/**
236 * Add addresses from the address list to the HELLO.
237 *
238 * @param cls the HELLO with the addresses to add
239 * @param max maximum space available
240 * @param buf where to add the addresses
241 * @return number of bytes added, 0 to terminate
242 */
243static ssize_t
244add_from_hello (void *cls, size_t max, void *buf)
245{
246 struct GNUNET_HELLO_Message **orig = cls;
247 struct AddContext ac;
248
249 if (NULL == *orig)
250 return GNUNET_SYSERR; /* already done */
251 ac.buf = buf;
252 ac.max = max;
253 ac.ret = 0;
254 GNUNET_assert (
255 NULL ==
256 GNUNET_HELLO_iterate_addresses (*orig, GNUNET_NO, &add_to_buf, &ac));
257 *orig = NULL;
258 return ac.ret;
259}
260
261
262/**
263 * Main function that will be run without the scheduler.
264 *
265 * @param cls closure
266 * @param args remaining command-line arguments
267 * @param cfgfile name of the configuration file used (for saving, can be NULL!)
268 * @param c configuration
269 */
270static void
271run (void *cls,
272 char *const *args,
273 const char *cfgfile,
274 const struct GNUNET_CONFIGURATION_Handle *c)
275{
276 struct GNUNET_DISK_FileHandle *fh;
277 struct GNUNET_HELLO_Message *orig;
278 struct GNUNET_HELLO_Message *result;
279 struct GNUNET_PeerIdentity pid;
280 uint64_t fsize;
281 ssize_t size_written;
282 struct GNUNET_HELLO_Builder *builder;
283 char *url;
284 const struct GNUNET_MessageHeader *msg;
285 struct GNUNET_MQ_Envelope *env;
286
287 plugins_load (c);
288 address_count = 0;
289
290 my_private_key =
291 GNUNET_CRYPTO_eddsa_key_create_from_configuration (c);
292 GNUNET_CRYPTO_eddsa_key_get_public (my_private_key,
293 &my_full_id.public_key);
294 fprintf (stdout,"We are peer %s\n", GNUNET_i2s (&my_full_id));
295
296 GNUNET_log_setup ("gnunet-hello", "DEBUG", NULL);
297
298 if (GNUNET_OK !=
299 GNUNET_DISK_file_size (hello_file, &fsize, GNUNET_YES, GNUNET_YES))
300 {
301 fprintf (stderr,
302 _ ("Error accessing file `%s': %s\n"),
303 hello_file,
304 strerror (errno));
305 return;
306 }
307 if (fsize > 65536)
308 {
309 fprintf (stderr, _ ("File `%s' is too big to be a HELLO\n"), hello_file);
310 return;
311 }
312 if (fsize < sizeof(struct GNUNET_MessageHeader))
313 {
314 fprintf (stderr, _ ("File `%s' is too small to be a HELLO\n"), hello_file);
315 return;
316 }
317 fh = GNUNET_DISK_file_open (hello_file,
318 GNUNET_DISK_OPEN_READ,
319 GNUNET_DISK_PERM_USER_READ);
320 if (NULL == fh)
321 {
322 fprintf (stderr,
323 _ ("Error opening file `%s': %s\n"),
324 hello_file,
325 strerror (errno));
326 return;
327 }
328 {
329 char buf[fsize] GNUNET_ALIGN;
330
331 GNUNET_assert (fsize == GNUNET_DISK_file_read (fh, buf, fsize));
332 GNUNET_assert (GNUNET_OK == GNUNET_DISK_file_close (fh));
333 orig = (struct GNUNET_HELLO_Message *) buf;
334 if ((fsize < GNUNET_HELLO_size (orig)) ||
335 (GNUNET_OK != GNUNET_HELLO_get_id (orig, &pid)))
336 {
337 fprintf (stderr,
338 _ ("Did not find well-formed HELLO in file `%s'\n"),
339 hello_file);
340 return;
341 }
342 {
343 char *pids;
344
345 pids = GNUNET_CRYPTO_eddsa_public_key_to_string (&my_full_id.public_key);
346 fprintf (stdout, "Processing HELLO for peer `%s'\n", pids);
347 GNUNET_free (pids);
348 }
349 /* result = GNUNET_HELLO_create (&pid.public_key, */
350 /* &add_from_hello, */
351 /* &orig, */
352 /* GNUNET_HELLO_is_friend_only (orig)); */
353
354 builder = GNUNET_HELLO_builder_new (&my_full_id);
355 GNUNET_assert (
356 NULL ==
357 GNUNET_HELLO_iterate_addresses ((const struct GNUNET_HELLO_Message *) orig, GNUNET_NO, &add_to_builder, builder));
358 url = GNUNET_HELLO_builder_to_url (builder, my_private_key);
359 fprintf (stdout,"url: %s\n", url);
360 env = GNUNET_HELLO_builder_to_env (builder,
361 my_private_key,
362 GNUNET_TIME_UNIT_FOREVER_REL);
363 msg = GNUNET_MQ_env_get_msg (env);
364 //GNUNET_assert (NULL != result);
365 GNUNET_assert (NULL != msg);
366 fh =
367 GNUNET_DISK_file_open (hello_file,
368 GNUNET_DISK_OPEN_WRITE | GNUNET_DISK_OPEN_TRUNCATE,
369 GNUNET_DISK_PERM_USER_READ
370 | GNUNET_DISK_PERM_USER_WRITE);
371 if (NULL == fh)
372 {
373 fprintf (stderr,
374 _ ("Error opening file `%s': %s\n"),
375 hello_file,
376 strerror (errno));
377 GNUNET_free (result);
378 return;
379 }
380 //fsize = GNUNET_HELLO_size (result);
381 size_written = GNUNET_DISK_file_write (fh, msg, ntohs (msg->size));
382 if (ntohs (msg->size) != size_written)
383 {
384 fprintf (stderr,
385 _ ("Error writing HELLO to file `%s': %s expected size %u size written %u\n"),
386 hello_file,
387 strerror (errno));
388 (void) GNUNET_DISK_file_close (fh);
389 return;
390 }
391 GNUNET_assert (GNUNET_OK == GNUNET_DISK_file_close (fh));
392 }
393 fprintf (stderr,
394 _ ("Modified %u addresses, wrote %u bytes\n"),
395 address_count,
396 (unsigned int) ntohs (msg->size));
397 GNUNET_HELLO_builder_free (builder);
398}
399
400
401int
402main (int argc, char *argv[])
403{
404 struct GNUNET_GETOPT_CommandLineOption options[] =
405 { GNUNET_GETOPT_option_string ('h',
406 "hello-file",
407 "HELLO_FILE",
408 gettext_noop ("Hello file to read"),
409 &hello_file),
410 GNUNET_GETOPT_OPTION_END };
411 int ret;
412
413 ret = (GNUNET_OK ==
414 GNUNET_PROGRAM_run2 (argc,
415 argv,
416 "gnunet-peerinfo",
417 gettext_noop ("Print information about peers."),
418 options,
419 &run,
420 NULL,
421 GNUNET_YES));
422 return ret;
423}
424
425
426/* end of gnunet-hello.c */
diff --git a/src/lib/hello/hello-ng.c b/src/lib/hello/hello-ng.c
new file mode 100644
index 000000000..9c255b361
--- /dev/null
+++ b/src/lib/hello/hello-ng.c
@@ -0,0 +1,198 @@
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/**
22 * @file hello/hello-ng.c
23 * @brief helper library for handling HELLOs
24 * @author Christian Grothoff
25 */
26#include "platform.h"
27#include "gnunet_signatures.h"
28#include "gnunet_hello_lib.h"
29#include "gnunet_protocols.h"
30#include "gnunet_util_lib.h"
31
32GNUNET_NETWORK_STRUCT_BEGIN
33/**
34 * Binary block we sign when we sign an address.
35 */
36struct SignedAddress
37{
38 /**
39 * Purpose must be #GNUNET_SIGNATURE_PURPOSE_TRANSPORT_ADDRESS
40 */
41 struct GNUNET_CRYPTO_EccSignaturePurpose purpose;
42
43 /**
44 * When was the address generated.
45 */
46 struct GNUNET_TIME_AbsoluteNBO mono_time;
47
48 /**
49 * Hash of the address.
50 */
51 struct GNUNET_HashCode addr_hash GNUNET_PACKED;
52};
53GNUNET_NETWORK_STRUCT_END
54
55/**
56 * Build address record by signing raw information with private key.
57 *
58 * @param address text address at @a communicator to sign
59 * @param nt network type of @a address
60 * @param mono_time monotonic time at which @a address was valid
61 * @param private_key signing key to use
62 * @param[out] result where to write address record (allocated)
63 * @param[out] result_size set to size of @a result
64 */
65void
66GNUNET_HELLO_sign_address (
67 const char *address,
68 enum GNUNET_NetworkType nt,
69 struct GNUNET_TIME_Absolute mono_time,
70 const struct GNUNET_CRYPTO_EddsaPrivateKey *private_key,
71 void **result,
72 size_t *result_size)
73{
74 struct SignedAddress sa;
75 struct GNUNET_CRYPTO_EddsaSignature sig;
76 char *sig_str;
77
78 sa.purpose.purpose = htonl (GNUNET_SIGNATURE_PURPOSE_TRANSPORT_ADDRESS);
79 sa.purpose.size = htonl (sizeof(sa));
80 sa.mono_time = GNUNET_TIME_absolute_hton (mono_time);
81 GNUNET_CRYPTO_hash (address, strlen (address), &sa.addr_hash);
82 GNUNET_CRYPTO_eddsa_sign (private_key, &sa, &sig);
83 sig_str = NULL;
84 (void) GNUNET_STRINGS_base64_encode (&sig, sizeof(sig), &sig_str);
85 *result_size =
86 1 + GNUNET_asprintf ((char **) result,
87 "%s;%llu;%u;%s",
88 sig_str,
89 (unsigned long long) mono_time.abs_value_us,
90 (unsigned int) nt,
91 address);
92 GNUNET_free (sig_str);
93}
94
95
96/**
97 * Check signature and extract address record.
98 *
99 * @param raw raw signed address
100 * @param raw_size size of @a raw
101 * @param pid public key to use for signature verification
102 * @param nt[out] set to network type
103 * @param mono_time[out] when was the address generated
104 * @return NULL on error, otherwise the address
105 */
106char *
107GNUNET_HELLO_extract_address (const void *raw,
108 size_t raw_size,
109 const struct GNUNET_PeerIdentity *pid,
110 enum GNUNET_NetworkType *nt,
111 struct GNUNET_TIME_Absolute *mono_time)
112{
113 const struct GNUNET_CRYPTO_EddsaPublicKey *public_key = &pid->public_key;
114 const char *raws = raw;
115 unsigned long long raw_us = 0;
116 unsigned int raw_nt = 0;
117 const char *sc;
118 const char *sc2;
119 const char *sc3;
120 const char *raw_addr;
121 char *data = NULL;
122 struct GNUNET_TIME_Absolute raw_mono_time;
123 struct SignedAddress sa;
124 struct GNUNET_CRYPTO_EddsaSignature *sig;
125
126 if ('\0' != raws[raw_size-1])
127 {
128 GNUNET_break_op (0);
129 return NULL;
130 }
131 if (NULL == (sc = strchr (raws, ';')))
132 {
133 GNUNET_break_op (0);
134 return NULL;
135 }
136 if (NULL == (sc2 = strchr (sc + 1, ';')))
137 {
138 GNUNET_break_op (0);
139 return NULL;
140 }
141 if (NULL == (sc3 = strchr (sc2 + 1, ';')))
142 {
143 GNUNET_break_op (0);
144 return NULL;
145 }
146 if (2 != sscanf (sc + 1, "%llu;%u;%*s", &raw_us, &raw_nt))
147 {
148 GNUNET_break_op (0);
149 return NULL;
150 }
151 raw_addr = sc3 + 1;
152 raw_mono_time.abs_value_us = raw_us;
153 if (sizeof(struct GNUNET_CRYPTO_EddsaSignature) !=
154 GNUNET_STRINGS_base64_decode (raws, sc - raws, (void **) &data))
155 {
156 GNUNET_break_op (0);
157 GNUNET_free (data);
158 return NULL;
159 }
160 sig = (struct GNUNET_CRYPTO_EddsaSignature*) data;
161 sa.purpose.purpose = htonl (GNUNET_SIGNATURE_PURPOSE_TRANSPORT_ADDRESS);
162 sa.purpose.size = htonl (sizeof(sa));
163 sa.mono_time = GNUNET_TIME_absolute_hton (raw_mono_time);
164 GNUNET_CRYPTO_hash (raw_addr, strlen (raw_addr), &sa.addr_hash);
165 if (GNUNET_YES !=
166 GNUNET_CRYPTO_eddsa_verify (GNUNET_SIGNATURE_PURPOSE_TRANSPORT_ADDRESS,
167 &sa,
168 sig,
169 public_key))
170 {
171 GNUNET_free (data);
172 GNUNET_break_op (0);
173 return NULL;
174 }
175 GNUNET_free (data);
176 *mono_time = raw_mono_time;
177 *nt = raw_nt;
178 return GNUNET_strdup (raw_addr);
179}
180
181
182/**
183 * Given an address as a string, extract the prefix that identifies
184 * the communicator offering transmissions to that address.
185 *
186 * @param address a peer's address
187 * @return NULL if the address is mal-formed, otherwise the prefix
188 */
189char *
190GNUNET_HELLO_address_to_prefix (const char *address)
191{
192 const char *dash;
193
194 dash = strchr (address, '-');
195 if (NULL == dash)
196 return NULL;
197 return GNUNET_strndup (address, dash - address);
198}
diff --git a/src/lib/hello/hello-uri.c b/src/lib/hello/hello-uri.c
new file mode 100644
index 000000000..49acaf7a9
--- /dev/null
+++ b/src/lib/hello/hello-uri.c
@@ -0,0 +1,1045 @@
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/**
22 * @file hello/hello-uri.c
23 * @brief helper library for handling URI-based HELLOs
24 * @author Christian Grothoff
25 *
26 * Note:
27 * - Current API does not support deserializing HELLO of
28 * another peer and then serializing it into another
29 * format (we always require the private key).
30 * Not sure if we need this, but if we do, we need
31 * to extend the builder and the API.
32 * - Current API does not allow overriding the default
33 * HELLO expiration time. We may want to add a function
34 * that does this to create bootstrap HELLOs shipped with
35 * the TGZ.
36 */
37#include "platform.h"
38#include "gnunet_signatures.h"
39#include "gnunet_hello_uri_lib.h"
40#include "gnunet_protocols.h"
41#include "gnunet_util_lib.h"
42
43
44GNUNET_NETWORK_STRUCT_BEGIN
45
46/**
47 * Binary block we sign when we sign an address.
48 */
49struct SignedAddress
50{
51 /**
52 * Purpose must be #GNUNET_SIGNATURE_PURPOSE_TRANSPORT_ADDRESS
53 */
54 struct GNUNET_CRYPTO_EccSignaturePurpose purpose;
55
56 /**
57 * When was the address generated.
58 */
59 struct GNUNET_TIME_AbsoluteNBO mono_time;
60
61 /**
62 * Hash of the address.
63 */
64 struct GNUNET_HashCode addr_hash GNUNET_PACKED;
65};
66
67/**
68 * Message signed as part of a HELLO block/URL.
69 */
70struct HelloSignaturePurpose
71{
72 /**
73 * Purpose must be #GNUNET_SIGNATURE_PURPOSE_HELLO
74 */
75 struct GNUNET_CRYPTO_EccSignaturePurpose purpose;
76
77 /**
78 * When does the signature expire?
79 */
80 struct GNUNET_TIME_AbsoluteNBO expiration_time;
81
82 /**
83 * Hash over all addresses.
84 */
85 struct GNUNET_HashCode h_addrs;
86
87};
88
89/**
90 * Message used when gossiping HELLOs between peers.
91 */
92struct HelloUriMessage
93{
94 /**
95 * Type must be #GNUNET_MESSAGE_TYPE_HELLO_URI
96 */
97 struct GNUNET_MessageHeader header;
98
99 /**
100 * Reserved. 0.
101 */
102 uint16_t reserved GNUNET_PACKED;
103
104 /**
105 * Number of URLs encoded after the end of the struct, in NBO.
106 */
107 uint16_t url_counter GNUNET_PACKED;
108
109 /* followed by a 'block' */
110};
111
112
113/**
114 * Start of a 'block'.
115 */
116struct BlockHeader
117{
118 /**
119 * Public key of the peer.
120 */
121 struct GNUNET_PeerIdentity pid;
122
123 /**
124 * Signature over the block, of purpose #GNUNET_SIGNATURE_PURPOSE_HELLO.
125 */
126 struct GNUNET_CRYPTO_EddsaSignature sig;
127
128 /**
129 * When does the HELLO expire?
130 */
131 struct GNUNET_TIME_AbsoluteNBO expiration_time;
132
133};
134
135
136/**
137 * Message used when a DHT provides its HELLO to direct
138 * neighbours.
139 */
140struct DhtHelloMessage
141{
142 /**
143 * Type must be #GNUNET_MESSAGE_TYPE_DHT_P2P_HELLO
144 */
145 struct GNUNET_MessageHeader header;
146
147 /**
148 * Reserved. 0.
149 */
150 uint16_t reserved GNUNET_PACKED;
151
152 /**
153 * Number of URLs encoded after the end of the struct, in NBO.
154 */
155 uint16_t url_counter GNUNET_PACKED;
156
157 /**
158 * Signature over the block, of purpose #GNUNET_SIGNATURE_PURPOSE_HELLO.
159 */
160 struct GNUNET_CRYPTO_EddsaSignature sig;
161
162 /**
163 * When does the HELLO expire?
164 */
165 struct GNUNET_TIME_AbsoluteNBO expiration_time;
166
167 /* followed by the serialized addresses of the 'block' */
168};
169
170
171GNUNET_NETWORK_STRUCT_END
172
173
174/**
175 * Address of a peer.
176 */
177struct Address
178{
179 /**
180 * Kept in a DLL.
181 */
182 struct Address *next;
183
184 /**
185 * Kept in a DLL.
186 */
187 struct Address *prev;
188
189 /**
190 * Actual URI, allocated at the end of this struct.
191 */
192 const char *uri;
193
194 /**
195 * Length of @a uri including 0-terminator.
196 */
197 size_t uri_len;
198};
199
200
201/**
202 * Context for building (or parsing) HELLO URIs.
203 */
204struct GNUNET_HELLO_Builder
205{
206 /**
207 * Public key of the peer.
208 */
209 struct GNUNET_PeerIdentity pid;
210
211 /**
212 * Head of the addresses DLL.
213 */
214 struct Address *a_head;
215
216 /**
217 * Tail of the addresses DLL.
218 */
219 struct Address *a_tail;
220
221 /**
222 * Length of the @a a_head DLL.
223 */
224 unsigned int a_length;
225
226};
227
228/**
229 * Struct to wrap data to do the merge of to hello uris.
230 */
231struct AddressUriMergeResult
232{
233 /**
234 * The builder of the hello uri we merge with.
235 */
236 struct GNUNET_HELLO_Builder *builder;
237
238 /**
239 * The actual address to check, if it is allready in the hello uri we merge with.
240 */
241 const char *address_uri;
242
243 /**
244 * Did we found the actual address to check.
245 */
246 unsigned int found;
247
248 /**
249 * Did we found at least one address to merge.
250 */
251 unsigned int merged;
252};
253
254
255/**
256 * Compute @a hash over addresses in @a builder.
257 *
258 * @param builder the builder to hash addresses of
259 * @param[out] hash where to write the hash
260 */
261static void
262hash_addresses (const struct GNUNET_HELLO_Builder *builder,
263 struct GNUNET_HashCode *hash)
264{
265 struct GNUNET_HashContext *hc;
266
267 hc = GNUNET_CRYPTO_hash_context_start ();
268 for (struct Address *a = builder->a_head;
269 NULL != a;
270 a = a->next)
271 {
272 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
273 "Hashing over %.*s\n",
274 (int) a->uri_len,
275 a->uri);
276 GNUNET_CRYPTO_hash_context_read (hc,
277 a->uri,
278 a->uri_len);
279 }
280 GNUNET_CRYPTO_hash_context_finish (hc,
281 hash);
282
283}
284
285
286/**
287 * Create HELLO signature.
288 *
289 * @param builder the builder to use
290 * @param et expiration time to sign
291 * @param priv key to sign with
292 * @param[out] sig where to write the signature
293 */
294static void
295sign_hello (const struct GNUNET_HELLO_Builder *builder,
296 struct GNUNET_TIME_Timestamp et,
297 const struct GNUNET_CRYPTO_EddsaPrivateKey *priv,
298 struct GNUNET_CRYPTO_EddsaSignature *sig)
299{
300 struct HelloSignaturePurpose hsp = {
301 .purpose.size = htonl (sizeof (hsp)),
302 .purpose.purpose = htonl (GNUNET_SIGNATURE_PURPOSE_HELLO),
303 .expiration_time = GNUNET_TIME_absolute_hton (et.abs_time)
304 };
305
306 hash_addresses (builder,
307 &hsp.h_addrs);
308 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
309 "Address hash is %s\n",
310 GNUNET_h2s_full (&hsp.h_addrs));
311 GNUNET_CRYPTO_eddsa_sign (priv,
312 &hsp,
313 sig);
314}
315
316
317/**
318 * Verify HELLO signature.
319 *
320 * @param builder the builder to use
321 * @param et expiration time to verify
322 * @param sig signature to verify
323 * @return #GNUNET_OK if everything is ok, #GNUNET_NO if the
324 * HELLO expired, #GNUNET_SYSERR if the signature is wrong
325 */
326static enum GNUNET_GenericReturnValue
327verify_hello (const struct GNUNET_HELLO_Builder *builder,
328 struct GNUNET_TIME_Absolute et,
329 const struct GNUNET_CRYPTO_EddsaSignature *sig)
330{
331 struct HelloSignaturePurpose hsp = {
332 .purpose.size = htonl (sizeof (hsp)),
333 .purpose.purpose = htonl (GNUNET_SIGNATURE_PURPOSE_HELLO),
334 .expiration_time = GNUNET_TIME_absolute_hton (et)
335 };
336
337 hash_addresses (builder,
338 &hsp.h_addrs);
339 if (GNUNET_OK !=
340 GNUNET_CRYPTO_eddsa_verify (GNUNET_SIGNATURE_PURPOSE_HELLO,
341 &hsp,
342 sig,
343 &builder->pid.public_key))
344 {
345 GNUNET_break_op (0);
346 return GNUNET_SYSERR;
347 }
348 if (GNUNET_TIME_absolute_is_past (et))
349 return GNUNET_NO;
350 return GNUNET_OK;
351}
352
353
354struct GNUNET_HELLO_Builder *
355GNUNET_HELLO_builder_new (const struct GNUNET_PeerIdentity *pid)
356{
357 struct GNUNET_HELLO_Builder *builder;
358
359 builder = GNUNET_new (struct GNUNET_HELLO_Builder);
360 builder->pid = *pid;
361 return builder;
362}
363
364
365const struct GNUNET_PeerIdentity *
366GNUNET_HELLO_builder_get_id (const struct GNUNET_HELLO_Builder *builder)
367{
368 return &builder->pid;
369}
370
371
372void
373GNUNET_HELLO_builder_free (struct GNUNET_HELLO_Builder *builder)
374{
375 struct Address *a;
376
377 while (NULL != (a = builder->a_head))
378 {
379 GNUNET_CONTAINER_DLL_remove (builder->a_head,
380 builder->a_tail,
381 a);
382 builder->a_length--;
383 GNUNET_free (a);
384 }
385 GNUNET_assert (0 == builder->a_length);
386 GNUNET_free (builder);
387}
388
389
390struct GNUNET_HELLO_Builder *
391GNUNET_HELLO_builder_from_msg (const struct GNUNET_MessageHeader *msg)
392{
393 const struct HelloUriMessage *h;
394 uint16_t size = ntohs (msg->size);
395
396 if (GNUNET_MESSAGE_TYPE_HELLO_URI != ntohs (msg->type))
397 {
398 GNUNET_break (0);
399 return NULL;
400 }
401 if (sizeof (struct HelloUriMessage) > size)
402 {
403 GNUNET_break_op (0);
404 return NULL;
405 }
406 h = (const struct HelloUriMessage *) msg;
407 size -= sizeof (*h);
408 return GNUNET_HELLO_builder_from_block (&h[1],
409 size);
410}
411
412
413struct GNUNET_HELLO_Builder *
414GNUNET_HELLO_builder_from_block (const void *block,
415 size_t block_size)
416{
417 const struct BlockHeader *bh = block;
418 struct GNUNET_HELLO_Builder *b;
419
420 if (block_size < sizeof (*bh))
421 {
422 GNUNET_break_op (0);
423 return NULL;
424 }
425 b = GNUNET_HELLO_builder_new (&bh->pid);
426 block += sizeof (*bh);
427 block_size -= sizeof (*bh);
428 while (block_size > 0)
429 {
430 const void *end = memchr (block,
431 '\0',
432 block_size);
433
434 if (NULL == end)
435 {
436 GNUNET_break_op (0);
437 GNUNET_HELLO_builder_free (b);
438 return NULL;
439 }
440 if (GNUNET_OK !=
441 GNUNET_HELLO_builder_add_address (b,
442 block))
443 {
444 GNUNET_break_op (0);
445 GNUNET_HELLO_builder_free (b);
446 return NULL;
447 }
448 end++;
449 block_size -= (end - block);
450 block = end;
451 }
452 {
453 enum GNUNET_GenericReturnValue ret;
454
455 ret = verify_hello (b,
456 GNUNET_TIME_absolute_ntoh (bh->expiration_time),
457 &bh->sig);
458 GNUNET_break (GNUNET_SYSERR != ret);
459 if (GNUNET_OK != ret)
460 {
461 GNUNET_HELLO_builder_free (b);
462 return NULL;
463 }
464 }
465 return b;
466}
467
468
469struct GNUNET_TIME_Absolute
470GNUNET_HELLO_builder_get_expiration_time (const struct
471 GNUNET_MessageHeader *msg)
472{
473 if (GNUNET_MESSAGE_TYPE_HELLO_URI == ntohs (msg->type))
474 {
475 const struct HelloUriMessage *h = (struct HelloUriMessage *) msg;
476 const struct BlockHeader *bh = (const struct BlockHeader *) &h[1];
477
478 return GNUNET_TIME_absolute_ntoh (bh->expiration_time);
479 }
480 else if (GNUNET_MESSAGE_TYPE_DHT_P2P_HELLO == ntohs (msg->type))
481 {
482 const struct DhtHelloMessage *dht_hello = (struct DhtHelloMessage *) msg;
483
484 return GNUNET_TIME_absolute_ntoh (dht_hello->expiration_time);
485 }
486 else
487 GNUNET_break (0);
488 return GNUNET_TIME_UNIT_ZERO_ABS;
489}
490
491
492struct GNUNET_HELLO_Builder *
493GNUNET_HELLO_builder_from_url (const char *url)
494{
495 const char *q;
496 const char *s1;
497 const char *s2;
498 struct GNUNET_PeerIdentity pid;
499 struct GNUNET_CRYPTO_EddsaSignature sig;
500 struct GNUNET_TIME_Absolute et;
501 size_t len;
502 struct GNUNET_HELLO_Builder *b;
503
504 if (0 != strncasecmp (url,
505 "gnunet://hello/",
506 strlen ("gnunet://hello/")))
507 return NULL;
508 url += strlen ("gnunet://hello/");
509 s1 = strchr (url, '/');
510 if (NULL == s1)
511 {
512 GNUNET_break_op (0);
513 return NULL;
514 }
515 s2 = strchr (s1 + 1, '/');
516 if (NULL == s1)
517 {
518 GNUNET_break_op (0);
519 return NULL;
520 }
521 q = strchr (url, '?');
522 if (NULL == q)
523 q = url + strlen (url);
524 if (GNUNET_OK !=
525 GNUNET_STRINGS_string_to_data (url,
526 s1 - url,
527 &pid,
528 sizeof(pid)))
529 {
530 GNUNET_break_op (0);
531 return NULL;
532 }
533 if (GNUNET_OK !=
534 GNUNET_STRINGS_string_to_data (s1 + 1,
535 s2 - (s1 + 1),
536 &sig,
537 sizeof(sig)))
538 {
539 GNUNET_break_op (0);
540 return NULL;
541 }
542 {
543 unsigned long long sec;
544 char dummy = '?';
545
546 if ( (0 == sscanf (s2 + 1,
547 "%llu%c",
548 &sec,
549 &dummy)) ||
550 ('?' != dummy) )
551 {
552 GNUNET_break_op (0);
553 return NULL;
554 }
555 et = GNUNET_TIME_absolute_from_s (sec);
556 }
557
558 b = GNUNET_HELLO_builder_new (&pid);
559 len = strlen (q);
560 while (len > 0)
561 {
562 const char *eq;
563 const char *amp;
564 char *addr = NULL;
565 char *uri;
566
567 /* skip ?/& separator */
568 len--;
569 q++;
570 eq = strchr (q, '=');
571 if ( (eq == q) ||
572 (NULL == eq) )
573 {
574 GNUNET_break_op (0);
575 GNUNET_HELLO_builder_free (b);
576 return NULL;
577 }
578 amp = strchr (eq, '&');
579 if (NULL == amp)
580 amp = &q[len];
581 GNUNET_STRINGS_urldecode (eq + 1,
582 amp - (eq + 1),
583 &addr);
584 if ( (NULL == addr) ||
585 (0 == strlen (addr)) )
586 {
587 GNUNET_free (addr);
588 GNUNET_break_op (0);
589 GNUNET_HELLO_builder_free (b);
590 return NULL;
591 }
592 GNUNET_asprintf (&uri,
593 "%.*s://%s",
594 (int) (eq - q),
595 q,
596 addr);
597 GNUNET_free (addr);
598 if (GNUNET_OK !=
599 GNUNET_HELLO_builder_add_address (b,
600 uri))
601 {
602 GNUNET_break_op (0);
603 GNUNET_free (uri);
604 GNUNET_HELLO_builder_free (b);
605 return NULL;
606 }
607 GNUNET_free (uri);
608 /* move to next URL */
609 len -= (amp - q);
610 q = amp;
611 }
612
613 {
614 enum GNUNET_GenericReturnValue ret;
615
616 ret = verify_hello (b,
617 et,
618 &sig);
619 GNUNET_break (GNUNET_SYSERR != ret);
620 if (GNUNET_OK != ret)
621 {
622 GNUNET_HELLO_builder_free (b);
623 return NULL;
624 }
625 }
626 return b;
627}
628
629
630struct GNUNET_MQ_Envelope *
631GNUNET_HELLO_builder_to_env (const struct GNUNET_HELLO_Builder *builder,
632 const struct GNUNET_CRYPTO_EddsaPrivateKey *priv,
633 struct GNUNET_TIME_Relative expiration_time)
634{
635 struct GNUNET_MQ_Envelope *env;
636 struct HelloUriMessage *msg;
637 size_t blen;
638
639 if (builder->a_length > UINT16_MAX)
640 {
641 GNUNET_break (0);
642 return NULL;
643 }
644 blen = 0;
645 GNUNET_assert (GNUNET_NO ==
646 GNUNET_HELLO_builder_to_block (builder,
647 priv,
648 NULL,
649 &blen,
650 expiration_time));
651 env = GNUNET_MQ_msg_extra (msg,
652 blen,
653 GNUNET_MESSAGE_TYPE_HELLO_URI);
654 msg->url_counter = htons ((uint16_t) builder->a_length);
655 GNUNET_assert (GNUNET_OK ==
656 GNUNET_HELLO_builder_to_block (builder,
657 priv,
658 &msg[1],
659 &blen,
660 expiration_time));
661 return env;
662}
663
664
665struct GNUNET_MessageHeader *
666GNUNET_HELLO_builder_to_dht_hello_msg (
667 const struct GNUNET_HELLO_Builder *builder,
668 const struct GNUNET_CRYPTO_EddsaPrivateKey *priv,
669 struct GNUNET_TIME_Relative expiration_time)
670{
671 struct DhtHelloMessage *msg;
672 size_t blen;
673
674 if (builder->a_length > UINT16_MAX)
675 {
676 GNUNET_break (0);
677 return NULL;
678 }
679 blen = 0;
680 GNUNET_assert (GNUNET_NO ==
681 GNUNET_HELLO_builder_to_block (builder,
682 priv,
683 NULL,
684 &blen,
685 expiration_time));
686 GNUNET_assert (blen < UINT16_MAX);
687 GNUNET_assert (blen >= sizeof (struct BlockHeader));
688 {
689 char buf[blen] GNUNET_ALIGN;
690 const struct BlockHeader *block = (const struct BlockHeader *) buf;
691
692 GNUNET_assert (GNUNET_OK ==
693 GNUNET_HELLO_builder_to_block (builder,
694 priv,
695 buf,
696 &blen,
697 expiration_time));
698 msg = GNUNET_malloc (sizeof (*msg)
699 + blen
700 - sizeof (*block));
701 msg->header.type = htons (GNUNET_MESSAGE_TYPE_DHT_P2P_HELLO);
702 msg->header.size = htons (sizeof (*msg)
703 + blen
704 - sizeof (*block));
705 memcpy (&msg[1],
706 &block[1],
707 blen - sizeof (*block));
708 msg->sig = block->sig;
709 msg->expiration_time = block->expiration_time;
710 }
711 msg->url_counter = htons ((uint16_t) builder->a_length);
712 return &msg->header;
713}
714
715
716char *
717GNUNET_HELLO_builder_to_url (const struct GNUNET_HELLO_Builder *builder,
718 const struct GNUNET_CRYPTO_EddsaPrivateKey *priv)
719{
720 struct GNUNET_CRYPTO_EddsaSignature sig;
721 struct GNUNET_TIME_Timestamp et;
722 char *result;
723 char *pids;
724 char *sigs;
725 const char *sep = "?";
726
727 et = GNUNET_TIME_relative_to_timestamp (GNUNET_HELLO_ADDRESS_EXPIRATION);
728 sign_hello (builder,
729 et,
730 priv,
731 &sig);
732 pids = GNUNET_STRINGS_data_to_string_alloc (&builder->pid,
733 sizeof (builder->pid));
734 sigs = GNUNET_STRINGS_data_to_string_alloc (&sig,
735 sizeof (sig));
736 GNUNET_asprintf (&result,
737 "gnunet://hello/%s/%s/%llu",
738 pids,
739 sigs,
740 (unsigned long long) GNUNET_TIME_timestamp_to_s (et));
741 GNUNET_free (sigs);
742 GNUNET_free (pids);
743 for (struct Address *a = builder->a_head;
744 NULL != a;
745 a = a->next)
746 {
747 char *ue;
748 char *tmp;
749 int pfx_len;
750 const char *eou;
751
752 eou = strstr (a->uri,
753 "://");
754 if (NULL == eou)
755 {
756 GNUNET_break (0);
757 GNUNET_free (result);
758 return NULL;
759 }
760 pfx_len = eou - a->uri;
761 eou += 3;
762 GNUNET_STRINGS_urlencode (a->uri_len - 4 - pfx_len,
763 eou,
764 &ue);
765 GNUNET_asprintf (&tmp,
766 "%s%s%.*s=%s",
767 result,
768 sep,
769 pfx_len,
770 a->uri,
771 ue);
772 GNUNET_free (ue);
773 GNUNET_free (result);
774 result = tmp;
775 sep = "&";
776 }
777 return result;
778}
779
780
781enum GNUNET_GenericReturnValue
782GNUNET_HELLO_builder_to_block (const struct GNUNET_HELLO_Builder *builder,
783 const struct GNUNET_CRYPTO_EddsaPrivateKey *priv,
784 void *block,
785 size_t *block_size,
786 struct GNUNET_TIME_Relative expiration_time)
787{
788 struct BlockHeader bh;
789 size_t needed = sizeof (bh);
790 char *pos;
791 struct GNUNET_TIME_Timestamp et;
792
793 for (struct Address *a = builder->a_head;
794 NULL != a;
795 a = a->next)
796 {
797 GNUNET_assert (needed + a->uri_len > needed);
798 needed += a->uri_len;
799 }
800 if ( (NULL == block) ||
801 (needed < *block_size) )
802 {
803 *block_size = needed;
804 return GNUNET_NO;
805 }
806 bh.pid = builder->pid;
807 if (GNUNET_TIME_UNIT_ZERO.rel_value_us == expiration_time.rel_value_us)
808 et = GNUNET_TIME_relative_to_timestamp (GNUNET_HELLO_ADDRESS_EXPIRATION);
809 else
810 et = GNUNET_TIME_relative_to_timestamp (expiration_time);
811 bh.expiration_time = GNUNET_TIME_absolute_hton (et.abs_time);
812 sign_hello (builder,
813 et,
814 priv,
815 &bh.sig);
816 memcpy (block,
817 &bh,
818 sizeof (bh));
819 pos = block + sizeof (bh);
820 for (struct Address *a = builder->a_head;
821 NULL != a;
822 a = a->next)
823 {
824 memcpy (pos,
825 a->uri,
826 a->uri_len);
827 pos += a->uri_len;
828 }
829 *block_size = needed;
830 return GNUNET_OK;
831}
832
833
834enum GNUNET_GenericReturnValue
835GNUNET_HELLO_builder_add_address (struct GNUNET_HELLO_Builder *builder,
836 const char *address)
837{
838 size_t alen = strlen (address) + 1;
839 struct Address *a;
840 const char *e;
841
842 if (NULL == (e = strstr (address,
843 "://")))
844 {
845 GNUNET_break_op (0);
846 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
847 "Invalid address `%s'\n",
848 address);
849 return GNUNET_SYSERR;
850 }
851 if (e == address)
852 {
853 GNUNET_break_op (0);
854 return GNUNET_SYSERR;
855 }
856 for (const char *p = address; p != e; p++)
857 if ( (! isalpha ((unsigned char) *p)) &&
858 ('+' != *p) )
859 {
860 GNUNET_break_op (0);
861 return GNUNET_SYSERR;
862 }
863 /* check for duplicates */
864 for (a = builder->a_head;
865 NULL != a;
866 a = a->next)
867 if (0 == strcmp (address,
868 a->uri))
869 return GNUNET_NO;
870 a = GNUNET_malloc (sizeof (struct Address) + alen);
871 a->uri_len = alen;
872 memcpy (&a[1],
873 address,
874 alen);
875 a->uri = (const char *) &a[1];
876 GNUNET_CONTAINER_DLL_insert_tail (builder->a_head,
877 builder->a_tail,
878 a);
879 builder->a_length++;
880 return GNUNET_OK;
881}
882
883
884enum GNUNET_GenericReturnValue
885GNUNET_HELLO_builder_del_address (struct GNUNET_HELLO_Builder *builder,
886 const char *address)
887{
888 struct Address *a;
889
890 /* check for duplicates */
891 for (a = builder->a_head;
892 NULL != a;
893 a = a->next)
894 if (0 == strcmp (address,
895 a->uri))
896 break;
897 if (NULL == a)
898 return GNUNET_NO;
899 GNUNET_CONTAINER_DLL_remove (builder->a_head,
900 builder->a_tail,
901 a);
902 builder->a_length--;
903 GNUNET_free (a);
904 return GNUNET_OK;
905}
906
907
908const struct GNUNET_PeerIdentity*
909GNUNET_HELLO_builder_iterate (const struct GNUNET_HELLO_Builder *builder,
910 GNUNET_HELLO_UriCallback uc,
911 void *uc_cls)
912{
913 struct Address *nxt;
914
915 if (NULL == uc)
916 return &builder->pid;
917 for (struct Address *a = builder->a_head;
918 NULL != a;
919 a = nxt)
920 {
921 nxt = a->next;
922 uc (uc_cls,
923 &builder->pid,
924 a->uri);
925 }
926 return &builder->pid;
927}
928
929
930enum GNUNET_GenericReturnValue
931GNUNET_HELLO_dht_msg_to_block (const struct GNUNET_MessageHeader *hello,
932 const struct GNUNET_PeerIdentity *pid,
933 void **block,
934 size_t *block_size,
935 struct GNUNET_TIME_Absolute *block_expiration)
936{
937 const struct DhtHelloMessage *msg
938 = (const struct DhtHelloMessage *) hello;
939 uint16_t len = ntohs (hello->size);
940 struct BlockHeader *bh;
941 struct GNUNET_HELLO_Builder *b;
942 enum GNUNET_GenericReturnValue ret;
943
944 if (GNUNET_MESSAGE_TYPE_DHT_P2P_HELLO != ntohs (hello->type))
945 {
946 GNUNET_break (0);
947 return GNUNET_SYSERR;
948 }
949 if (len < sizeof (*msg))
950 {
951 GNUNET_break_op (0);
952 return GNUNET_SYSERR;
953 }
954 len -= sizeof (*msg);
955 *block_size = len + sizeof (*bh);
956 *block = GNUNET_malloc (*block_size);
957 bh = *block;
958 bh->pid = *pid;
959 bh->sig = msg->sig;
960 bh->expiration_time = msg->expiration_time;
961 *block_expiration = GNUNET_TIME_absolute_ntoh (msg->expiration_time);
962 memcpy (&bh[1],
963 &msg[1],
964 len);
965 b = GNUNET_HELLO_builder_from_block (*block,
966 *block_size);
967 if (NULL == b)
968 {
969 GNUNET_break_op (0);
970 GNUNET_free (*block);
971 *block_size = 0;
972 return GNUNET_SYSERR;
973 }
974 ret = verify_hello (b,
975 *block_expiration,
976 &msg->sig);
977 GNUNET_HELLO_builder_free (b);
978 if (GNUNET_SYSERR == ret)
979 {
980 GNUNET_free (*block);
981 *block_size = 0;
982 return GNUNET_SYSERR;
983 }
984 return ret;
985}
986
987
988/**
989 * Given an address as a string, extract the prefix that identifies
990 * the communicator offering transmissions to that address.
991 *
992 * @param address a peer's address
993 * @return NULL if the address is mal-formed, otherwise the prefix
994 */
995char *
996GNUNET_HELLO_address_to_prefix (const char *address)
997{
998 const char *dash;
999
1000 dash = strchr (address, '-');
1001 if (NULL == dash)
1002 return NULL;
1003 return GNUNET_strndup (address, dash - address);
1004}
1005
1006
1007/**
1008 * Build address record by signing raw information with private key.
1009 *
1010 * @param address text address at @a communicator to sign
1011 * @param nt network type of @a address
1012 * @param mono_time monotonic time at which @a address was valid
1013 * @param private_key signing key to use
1014 * @param[out] result where to write address record (allocated)
1015 * @param[out] result_size set to size of @a result
1016 */
1017void
1018GNUNET_HELLO_sign_address (
1019 const char *address,
1020 enum GNUNET_NetworkType nt,
1021 struct GNUNET_TIME_Absolute mono_time,
1022 const struct GNUNET_CRYPTO_EddsaPrivateKey *private_key,
1023 void **result,
1024 size_t *result_size)
1025{
1026 struct SignedAddress sa;
1027 struct GNUNET_CRYPTO_EddsaSignature sig;
1028 char *sig_str;
1029
1030 sa.purpose.purpose = htonl (GNUNET_SIGNATURE_PURPOSE_TRANSPORT_ADDRESS);
1031 sa.purpose.size = htonl (sizeof(sa));
1032 sa.mono_time = GNUNET_TIME_absolute_hton (mono_time);
1033 GNUNET_CRYPTO_hash (address, strlen (address), &sa.addr_hash);
1034 GNUNET_CRYPTO_eddsa_sign (private_key, &sa, &sig);
1035 sig_str = NULL;
1036 (void) GNUNET_STRINGS_base64_encode (&sig, sizeof(sig), &sig_str);
1037 *result_size =
1038 1 + GNUNET_asprintf ((char **) result,
1039 "%s;%llu;%u;%s",
1040 sig_str,
1041 (unsigned long long) mono_time.abs_value_us,
1042 (unsigned int) nt,
1043 address);
1044 GNUNET_free (sig_str);
1045}
diff --git a/src/lib/hello/meson.build b/src/lib/hello/meson.build
new file mode 100644
index 000000000..caf70e4a7
--- /dev/null
+++ b/src/lib/hello/meson.build
@@ -0,0 +1,27 @@
1libgnunethello_src = ['hello-uri.c']
2
3libgnunethello = library('gnunethello',
4 libgnunethello_src,
5 soversion: '0',
6 version: '0.1.0',
7 dependencies: libgnunetutil_dep,
8 include_directories: [incdir, configuration_inc],
9 install: true,
10 install_dir: get_option('libdir'))
11libgnunethello_dep = declare_dependency(link_with : libgnunethello)
12pkg.generate(libgnunethello, url: 'https://www.gnunet.org',
13 description : 'Helper library for handling GNUnet HELLO messages')
14
15
16test_hello_uri = executable ('test_hello_uri',
17 ['test_hello-uri.c'],
18 dependencies: [libgnunethello_dep,
19 libgnunetutil_dep,
20 gcrypt_dep],
21 include_directories: [incdir, configuration_inc],
22 build_by_default: false,
23 install: false)
24
25test('test_hello_uri', test_hello_uri,
26 workdir: meson.current_build_dir(),
27 suite: ['hello'])
diff --git a/src/lib/hello/test_hello-uri.c b/src/lib/hello/test_hello-uri.c
new file mode 100644
index 000000000..9f3179270
--- /dev/null
+++ b/src/lib/hello/test_hello-uri.c
@@ -0,0 +1,215 @@
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 hello/test_hello-uri.c
22 * @brief test for helper library for handling URI-based HELLOs
23 * @author Christian Grothoff
24 */
25#include "platform.h"
26#include "gnunet_signatures.h"
27#include "gnunet_util_lib.h"
28#include "gnunet_hello_uri_lib.h"
29
30
31/**
32 * Check for expected URIs.
33 *
34 * @param cls a `unsigned int*`, bitmask set to found URIs
35 * @param uri URI to check for
36 */
37static void
38check_uris (void *cls,
39 const struct GNUNET_PeerIdentity *pid,
40 const char *uri)
41{
42 unsigned int *found = cls;
43
44 if (0 == strcmp (uri,
45 "test://address"))
46 *found |= 1;
47 else if (0 == strcmp (uri,
48 "test://more"))
49 *found |= 2;
50 else
51 *found = (unsigned int) -1;
52}
53
54
55int
56main (int argc,
57 char *argv[])
58{
59 struct GNUNET_PeerIdentity pid;
60 struct GNUNET_HELLO_Builder *b;
61 struct GNUNET_CRYPTO_EddsaPrivateKey priv;
62
63 GNUNET_log_setup ("test-hell-uri",
64 "WARNING",
65 NULL);
66 GNUNET_CRYPTO_eddsa_key_create (&priv);
67 GNUNET_CRYPTO_eddsa_key_get_public (&priv,
68 &pid.public_key);
69 b = GNUNET_HELLO_builder_new (&pid);
70 GNUNET_assert (GNUNET_SYSERR ==
71 GNUNET_HELLO_builder_add_address (b,
72 "invalid"));
73 GNUNET_assert (GNUNET_SYSERR ==
74 GNUNET_HELLO_builder_add_address (b,
75 "i%v://bla"));
76 GNUNET_assert (GNUNET_SYSERR ==
77 GNUNET_HELLO_builder_add_address (b,
78 "://empty"));
79 GNUNET_assert (GNUNET_OK ==
80 GNUNET_HELLO_builder_add_address (b,
81 "test://address"));
82 GNUNET_assert (GNUNET_NO ==
83 GNUNET_HELLO_builder_add_address (b,
84 "test://address"));
85 GNUNET_assert (GNUNET_OK ==
86 GNUNET_HELLO_builder_add_address (b,
87 "test://more"));
88 {
89 void *block;
90 size_t block_size = 0;
91 struct GNUNET_HELLO_Builder *b2;
92 const struct GNUNET_PeerIdentity *p2;
93 unsigned int found;
94
95 GNUNET_assert (GNUNET_NO ==
96 GNUNET_HELLO_builder_to_block (b,
97 &priv,
98 NULL,
99 &block_size,
100 GNUNET_TIME_UNIT_FOREVER_REL));
101 GNUNET_assert (GNUNET_NO ==
102 GNUNET_HELLO_builder_to_block (b,
103 &priv,
104 NULL,
105 &block_size,
106 GNUNET_TIME_UNIT_FOREVER_REL));
107 GNUNET_assert (0 != block_size);
108 block = GNUNET_malloc (block_size);
109 GNUNET_assert (GNUNET_OK ==
110 GNUNET_HELLO_builder_to_block (b,
111 &priv,
112 block,
113 &block_size,
114 GNUNET_TIME_UNIT_FOREVER_REL));
115 b2 = GNUNET_HELLO_builder_from_block (block,
116 block_size);
117 GNUNET_free (block);
118 GNUNET_assert (NULL != b2);
119 found = 0;
120 p2 = GNUNET_HELLO_builder_iterate (b2,
121 &check_uris,
122 &found);
123 GNUNET_assert (3 == found);
124 GNUNET_assert (0 ==
125 GNUNET_memcmp (p2,
126 &pid));
127 GNUNET_HELLO_builder_free (b2);
128 }
129
130 {
131 char *url;
132 struct GNUNET_HELLO_Builder *b2;
133 const struct GNUNET_PeerIdentity *p2;
134 unsigned int found;
135
136 url = GNUNET_HELLO_builder_to_url (b,
137 &priv);
138 b2 = GNUNET_HELLO_builder_from_url (url);
139 GNUNET_free (url);
140 GNUNET_assert (NULL != b2);
141 found = 0;
142 p2 = GNUNET_HELLO_builder_iterate (b2,
143 &check_uris,
144 &found);
145 GNUNET_assert (3 == found);
146 GNUNET_assert (0 ==
147 GNUNET_memcmp (p2,
148 &pid));
149 GNUNET_HELLO_builder_free (b2);
150 }
151
152 {
153 struct GNUNET_MQ_Envelope *env;
154 struct GNUNET_HELLO_Builder *b2;
155 const struct GNUNET_PeerIdentity *p2;
156 unsigned int found;
157
158 env = GNUNET_HELLO_builder_to_env (b,
159 &priv,
160 GNUNET_TIME_UNIT_FOREVER_REL);
161 b2 = GNUNET_HELLO_builder_from_msg (GNUNET_MQ_env_get_msg (env));
162 GNUNET_free (env);
163 GNUNET_assert (NULL != b2);
164 found = 0;
165 p2 = GNUNET_HELLO_builder_iterate (b2,
166 &check_uris,
167 &found);
168 GNUNET_assert (3 == found);
169 GNUNET_assert (0 ==
170 GNUNET_memcmp (p2,
171 &pid));
172 GNUNET_HELLO_builder_free (b2);
173 }
174
175 GNUNET_HELLO_builder_free (b);
176
177 GNUNET_CRYPTO_mpi_print_unsigned (priv.d,
178 sizeof (priv.d),
179 GCRYMPI_CONST_ONE);
180 priv.d[0] &= 248;
181 priv.d[31] &= 127;
182 priv.d[31] |= 64;
183 {
184 char *buf;
185
186 buf = GNUNET_STRINGS_data_to_string_alloc (&priv,
187 sizeof (priv));
188 fprintf (stderr,
189 "PK: %s\n",
190 buf);
191 GNUNET_free (buf);
192 }
193 GNUNET_CRYPTO_eddsa_key_get_public (&priv,
194 &pid.public_key);
195 b = GNUNET_HELLO_builder_new (&pid);
196 GNUNET_assert (GNUNET_OK ==
197 GNUNET_HELLO_builder_add_address (b,
198 "a://first"));
199 GNUNET_assert (GNUNET_OK ==
200 GNUNET_HELLO_builder_add_address (b,
201 "b://second"));
202 {
203 char *url;
204
205 url = GNUNET_HELLO_builder_to_url (b,
206 &priv);
207 fprintf (stderr,
208 "TV: %s\n",
209 url);
210 GNUNET_free (url);
211 }
212 GNUNET_HELLO_builder_free (b);
213
214 return 0;
215}
diff --git a/src/lib/json/.gitignore b/src/lib/json/.gitignore
new file mode 100644
index 000000000..347bffd7b
--- /dev/null
+++ b/src/lib/json/.gitignore
@@ -0,0 +1,2 @@
1test_json
2test_json_mhd
diff --git a/src/lib/json/Makefile.am b/src/lib/json/Makefile.am
new file mode 100644
index 000000000..c67824ee4
--- /dev/null
+++ b/src/lib/json/Makefile.am
@@ -0,0 +1,57 @@
1# This Makefile.am is in the public domain
2AM_CPPFLAGS = -I$(top_srcdir)/src/include
3
4if USE_COVERAGE
5 AM_CFLAGS = --coverage -O0
6 XLIB = -lgcov
7endif
8
9lib_LTLIBRARIES = \
10 libgnunetjson.la
11
12libgnunetjson_la_LDFLAGS = \
13 $(GN_LIBINTL) \
14 -version-info 0:0:0 \
15 -no-undefined
16libgnunetjson_la_CFLAGS = \
17 $(MHD_CFLAGS) \
18 $(AM_CFLAGS)
19libgnunetjson_la_SOURCES = \
20 json.c \
21 json_generator.c \
22 json_helper.c \
23 json_mhd.c \
24 json_pack.c
25libgnunetjson_la_LIBADD = \
26 $(top_builddir)/src/lib/util/libgnunetutil.la \
27 -ljansson \
28 $(MHD_LIBS) \
29 $(XLIB) \
30 $(Z_LIBS)
31
32check_PROGRAMS = \
33 test_json \
34 test_json_mhd
35
36TESTS = \
37 $(check_PROGRAMS)
38
39test_json_SOURCES = \
40 test_json.c
41test_json_CFLAGS = $(MHD_CFLAGS) $(AM_CFLAGS)
42test_json_LDADD = \
43 libgnunetjson.la \
44 $(top_builddir)/src/lib/util/libgnunetutil.la \
45 -ljansson
46
47
48test_json_mhd_SOURCES = \
49 test_json_mhd.c
50test_json_mhd_LDADD = \
51 libgnunetjson.la \
52 $(top_builddir)/src/lib/util/libgnunetutil.la \
53 -ljansson \
54 $(MHD_LIBS) \
55 $(Z_LIBS) \
56 @LIBCURL@
57test_json_mhd_CFLAGS = $(MHD_CFLAGS) @LIBCURL_CPPFLAGS@ $(AM_CFLAGS)
diff --git a/src/lib/json/json.c b/src/lib/json/json.c
new file mode 100644
index 000000000..07ec158be
--- /dev/null
+++ b/src/lib/json/json.c
@@ -0,0 +1,161 @@
1/*
2 This file is part of GNUnet
3 Copyright (C) 2014-2017, 2021, 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 json/json.c
22 * @brief functions to parse JSON snippets
23 * @author Florian Dold
24 * @author Benedikt Mueller
25 * @author Christian Grothoff
26 */
27#include "platform.h"
28#include "gnunet_json_lib.h"
29
30
31enum GNUNET_GenericReturnValue
32GNUNET_JSON_parse (const json_t *root,
33 struct GNUNET_JSON_Specification *spec,
34 const char **error_json_name,
35 unsigned int *error_line)
36{
37 if (NULL == root)
38 return GNUNET_SYSERR;
39 for (unsigned int i = 0; NULL != spec[i].parser; i++)
40 {
41 json_t *pos;
42
43 if (NULL == spec[i].field)
44 pos = (json_t *) root;
45 else
46 pos = json_object_get (root,
47 spec[i].field);
48 if ( ( (NULL == pos) ||
49 (json_is_null (pos) ) ) &&
50 (spec[i].is_optional) )
51 {
52 if (NULL != spec[i].missing)
53 *spec[i].missing = true;
54 continue;
55 }
56 if ( (NULL == pos) ||
57 (GNUNET_OK !=
58 spec[i].parser (spec[i].cls,
59 pos,
60 &spec[i])) )
61 {
62 if (NULL != error_json_name)
63 *error_json_name = spec[i].field;
64 else
65 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
66 "Parsing failed for field `%s:%u`\n",
67 spec[i].field,
68 i);
69 if (NULL != error_line)
70 *error_line = i;
71 GNUNET_JSON_parse_free (spec);
72 return GNUNET_SYSERR;
73 }
74 if (NULL != spec[i].missing)
75 *spec[i].missing = false;
76 }
77 return GNUNET_OK; /* all OK! */
78}
79
80
81struct GNUNET_JSON_Specification
82GNUNET_JSON_spec_mark_optional (struct GNUNET_JSON_Specification spec,
83 bool *missing)
84{
85 struct GNUNET_JSON_Specification ret = spec;
86
87 ret.is_optional = true;
88 ret.missing = missing;
89 return ret;
90}
91
92
93void
94GNUNET_JSON_parse_free (struct GNUNET_JSON_Specification *spec)
95{
96 for (unsigned int i = 0; NULL != spec[i].parser; i++)
97 if (NULL != spec[i].cleaner)
98 spec[i].cleaner (spec[i].cls,
99 &spec[i]);
100}
101
102
103/**
104 * Set an option with a JSON value from the command line.
105 * A pointer to this function should be passed as part of the
106 * 'struct GNUNET_GETOPT_CommandLineOption' array to initialize options
107 * of this type.
108 *
109 * @param ctx command line processing context
110 * @param scls additional closure (will point to the 'json_t *')
111 * @param option name of the option
112 * @param value actual value of the option as a string.
113 * @return #GNUNET_OK if parsing the value worked
114 */
115static enum GNUNET_GenericReturnValue
116set_json (struct GNUNET_GETOPT_CommandLineProcessorContext *ctx,
117 void *scls,
118 const char *option,
119 const char *value)
120{
121 json_t **json = scls;
122 json_error_t error;
123
124 *json = json_loads (value,
125 JSON_REJECT_DUPLICATES,
126 &error);
127 if (NULL == *json)
128 {
129 fprintf (stderr,
130 _ ("Failed to parse JSON in option `%s': %s (%s)\n"),
131 option,
132 error.text,
133 error.source);
134 return GNUNET_SYSERR;
135 }
136 return GNUNET_OK;
137}
138
139
140struct GNUNET_GETOPT_CommandLineOption
141GNUNET_JSON_getopt (char shortName,
142 const char *name,
143 const char *argumentHelp,
144 const char *description,
145 json_t **json)
146{
147 struct GNUNET_GETOPT_CommandLineOption clo = {
148 .shortName = shortName,
149 .name = name,
150 .argumentHelp = argumentHelp,
151 .description = description,
152 .require_argument = 1,
153 .processor = &set_json,
154 .scls = (void *) json
155 };
156
157 return clo;
158}
159
160
161/* end of json.c */
diff --git a/src/lib/json/json_generator.c b/src/lib/json/json_generator.c
new file mode 100644
index 000000000..43b72ba57
--- /dev/null
+++ b/src/lib/json/json_generator.c
@@ -0,0 +1,207 @@
1/*
2 This file is part of GNUnet
3 Copyright (C) 2014, 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 * @file json/json_generator.c
22 * @brief helper functions for generating JSON from GNUnet data structures
23 * @author Sree Harsha Totakura <sreeharsha@totakura.in>
24 */
25#include "platform.h"
26#include "gnunet_util_lib.h"
27#include "gnunet_json_lib.h"
28
29
30json_t *
31GNUNET_JSON_from_data (const void *data,
32 size_t size)
33{
34 char *buf;
35 json_t *json;
36
37 if (size >= ( (GNUNET_MAX_MALLOC_CHECKED - 1) * 5) - 4 / 8)
38 {
39 GNUNET_break (0);
40 return NULL;
41 }
42 buf = GNUNET_STRINGS_data_to_string_alloc (data,
43 size);
44 json = json_string (buf);
45 GNUNET_free (buf);
46 GNUNET_break (NULL != json);
47 return json;
48}
49
50
51json_t *
52GNUNET_JSON_from_data64 (const void *data,
53 size_t size)
54{
55 char *buf = NULL;
56 json_t *json;
57 size_t len;
58
59 if (size >= ( ( (GNUNET_MAX_MALLOC_CHECKED - 1) * 6) - 5) / 8)
60 {
61 GNUNET_break (0);
62 return NULL;
63 }
64 len = GNUNET_STRINGS_base64_encode (data,
65 size,
66 &buf);
67 if (NULL == buf)
68 {
69 GNUNET_break (0);
70 return NULL;
71 }
72 json = json_stringn (buf,
73 len);
74 GNUNET_free (buf);
75 GNUNET_break (NULL != json);
76 return json;
77}
78
79
80json_t *
81GNUNET_JSON_from_timestamp (struct GNUNET_TIME_Timestamp stamp)
82{
83 json_t *j;
84
85 j = json_object ();
86 if (NULL == j)
87 {
88 GNUNET_break (0);
89 return NULL;
90 }
91 if (GNUNET_TIME_absolute_is_never (stamp.abs_time))
92 {
93 if (0 !=
94 json_object_set_new (j,
95 "t_s",
96 json_string ("never")))
97 {
98 GNUNET_break (0);
99 json_decref (j);
100 return NULL;
101 }
102 return j;
103 }
104 GNUNET_assert (
105 0 ==
106 (stamp.abs_time.abs_value_us
107 % GNUNET_TIME_UNIT_SECONDS.rel_value_us));
108 if (0 !=
109 json_object_set_new (
110 j,
111 "t_s",
112 json_integer (
113 (json_int_t) (stamp.abs_time.abs_value_us
114 / GNUNET_TIME_UNIT_SECONDS.rel_value_us))))
115 {
116 GNUNET_break (0);
117 json_decref (j);
118 return NULL;
119 }
120 return j;
121}
122
123
124json_t *
125GNUNET_JSON_from_timestamp_nbo (struct GNUNET_TIME_TimestampNBO stamp)
126{
127 return GNUNET_JSON_from_timestamp (GNUNET_TIME_timestamp_ntoh (stamp));
128}
129
130
131json_t *
132GNUNET_JSON_from_time_rel (struct GNUNET_TIME_Relative stamp)
133{
134 json_t *j;
135
136 j = json_object ();
137 if (NULL == j)
138 {
139 GNUNET_break (0);
140 return NULL;
141 }
142 if (GNUNET_TIME_relative_is_forever (stamp))
143 {
144 if (0 !=
145 json_object_set_new (j,
146 "d_us",
147 json_string ("forever")))
148 {
149 GNUNET_break (0);
150 json_decref (j);
151 return NULL;
152 }
153 return j;
154 }
155 if (stamp.rel_value_us >= (1LLU << 53))
156 {
157 /* value is larger than allowed */
158 GNUNET_break (0);
159 return NULL;
160 }
161 if (0 !=
162 json_object_set_new (
163 j,
164 "d_us",
165 json_integer ((json_int_t) stamp.rel_value_us)))
166 {
167 GNUNET_break (0);
168 json_decref (j);
169 return NULL;
170 }
171 return j;
172}
173
174
175json_t *
176GNUNET_JSON_from_rsa_public_key (const struct GNUNET_CRYPTO_RsaPublicKey *pk)
177{
178 void *buf;
179 size_t buf_len;
180 json_t *ret;
181
182 buf_len = GNUNET_CRYPTO_rsa_public_key_encode (pk,
183 &buf);
184 ret = GNUNET_JSON_from_data (buf,
185 buf_len);
186 GNUNET_free (buf);
187 return ret;
188}
189
190
191json_t *
192GNUNET_JSON_from_rsa_signature (const struct GNUNET_CRYPTO_RsaSignature *sig)
193{
194 void *buf;
195 size_t buf_len;
196 json_t *ret;
197
198 buf_len = GNUNET_CRYPTO_rsa_signature_encode (sig,
199 &buf);
200 ret = GNUNET_JSON_from_data (buf,
201 buf_len);
202 GNUNET_free (buf);
203 return ret;
204}
205
206
207/* End of json/json_generator.c */
diff --git a/src/lib/json/json_helper.c b/src/lib/json/json_helper.c
new file mode 100644
index 000000000..5c2f8ae05
--- /dev/null
+++ b/src/lib/json/json_helper.c
@@ -0,0 +1,1611 @@
1/*
2 This file is part of GNUnet
3 Copyright (C) 2014-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 json/json_helper.c
22 * @brief functions to generate specifciations for JSON parsing
23 * @author Florian Dold
24 * @author Benedikt Mueller
25 * @author Christian Grothoff
26 */
27#include "platform.h"
28#include "gnunet_json_lib.h"
29#include "gnunet_common.h"
30
31
32struct GNUNET_JSON_Specification
33GNUNET_JSON_spec_end ()
34{
35 struct GNUNET_JSON_Specification ret = {
36 .parser = NULL,
37 .cleaner = NULL,
38 .cls = NULL
39 };
40
41 return ret;
42}
43
44
45/**
46 * Convert string value to numeric cipher value.
47 *
48 * @param cipher_s input string
49 * @return numeric cipher value
50 */
51static enum GNUNET_CRYPTO_BlindSignatureAlgorithm
52string_to_cipher (const char *cipher_s)
53{
54 if ((0 == strcasecmp (cipher_s,
55 "RSA")) ||
56 (0 == strcasecmp (cipher_s,
57 "RSA+age_restricted")))
58 return GNUNET_CRYPTO_BSA_RSA;
59 if ((0 == strcasecmp (cipher_s,
60 "CS")) ||
61 (0 == strcasecmp (cipher_s,
62 "CS+age_restricted")))
63 return GNUNET_CRYPTO_BSA_CS;
64 return GNUNET_CRYPTO_BSA_INVALID;
65}
66
67
68/**
69 * Parse given JSON object to fixed size data
70 *
71 * @param cls closure, NULL
72 * @param root the json object representing data
73 * @param[out] spec where to write the data
74 * @return #GNUNET_OK upon successful parsing; #GNUNET_SYSERR upon error
75 */
76static enum GNUNET_GenericReturnValue
77parse_fixed_data (void *cls,
78 json_t *root,
79 struct GNUNET_JSON_Specification *spec)
80{
81 const char *enc;
82 size_t len;
83
84 if (NULL == (enc = json_string_value (root)))
85 {
86 GNUNET_break_op (0);
87 return GNUNET_SYSERR;
88 }
89 len = strlen (enc);
90 if (len >= SIZE_MAX / 5)
91 {
92 GNUNET_break_op (0);
93 return GNUNET_SYSERR;
94 }
95 if (((len * 5) / 8) != spec->ptr_size)
96 {
97 GNUNET_break_op (0);
98 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
99 "Field `%s' has wrong length\n",
100 spec->field);
101 return GNUNET_SYSERR;
102 }
103 if (GNUNET_OK !=
104 GNUNET_STRINGS_string_to_data (enc,
105 len,
106 spec->ptr,
107 spec->ptr_size))
108 {
109 GNUNET_break_op (0);
110 return GNUNET_SYSERR;
111 }
112 return GNUNET_OK;
113}
114
115
116struct GNUNET_JSON_Specification
117GNUNET_JSON_spec_fixed (const char *name,
118 void *obj,
119 size_t size)
120{
121 struct GNUNET_JSON_Specification ret = {
122 .parser = &parse_fixed_data,
123 .cleaner = NULL,
124 .cls = NULL,
125 .field = name,
126 .ptr = obj,
127 .ptr_size = size,
128 .size_ptr = NULL
129 };
130
131 return ret;
132}
133
134
135/**
136 * Parse given JSON object to fixed size data
137 *
138 * @param cls closure, NULL
139 * @param root the json object representing data
140 * @param[out] spec where to write the data
141 * @return #GNUNET_OK upon successful parsing; #GNUNET_SYSERR upon error
142 */
143static enum GNUNET_GenericReturnValue
144parse_fixed64_data (void *cls,
145 json_t *root,
146 struct GNUNET_JSON_Specification *spec)
147{
148 const char *enc;
149 unsigned int len;
150 void *output;
151 size_t olen;
152
153 if (NULL == (enc = json_string_value (root)))
154 {
155 GNUNET_break_op (0);
156 return GNUNET_SYSERR;
157 }
158 len = strlen (enc);
159 output = NULL;
160 olen = GNUNET_STRINGS_base64_decode (enc,
161 len,
162 &output);
163 if (olen != spec->ptr_size)
164 {
165 GNUNET_break_op (0);
166 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
167 "Field `%s' has wrong length\n",
168 spec->field);
169 GNUNET_free (output);
170 return GNUNET_SYSERR;
171 }
172 memcpy (spec->ptr,
173 output,
174 olen);
175 GNUNET_free (output);
176 return GNUNET_OK;
177}
178
179
180struct GNUNET_JSON_Specification
181GNUNET_JSON_spec_fixed64 (const char *name,
182 void *obj,
183 size_t size)
184{
185 struct GNUNET_JSON_Specification ret = {
186 .parser = &parse_fixed64_data,
187 .cleaner = NULL,
188 .cls = NULL,
189 .field = name,
190 .ptr = obj,
191 .ptr_size = size,
192 .size_ptr = NULL
193 };
194
195 return ret;
196}
197
198
199/**
200 * Parse given JSON object to variable size data
201 *
202 * @param cls closure, NULL
203 * @param root the json object representing data
204 * @param[out] spec where to write the data
205 * @return #GNUNET_OK upon successful parsing; #GNUNET_SYSERR upon error
206 */
207static enum GNUNET_GenericReturnValue
208parse_variable_data (void *cls,
209 json_t *root,
210 struct GNUNET_JSON_Specification *spec)
211{
212 const char *str;
213 size_t size;
214 void *data;
215
216 str = json_string_value (root);
217 if (NULL == str)
218 {
219 GNUNET_break_op (0);
220 return GNUNET_SYSERR;
221 }
222 if (GNUNET_OK !=
223 GNUNET_STRINGS_string_to_data_alloc (str,
224 strlen (str),
225 &data,
226 &size))
227 {
228 GNUNET_break_op (0);
229 return GNUNET_SYSERR;
230 }
231 *(void **) spec->ptr = data;
232 *spec->size_ptr = size;
233 return GNUNET_OK;
234}
235
236
237/**
238 * Cleanup data left from parsing variable size data
239 *
240 * @param cls closure, NULL
241 * @param[out] spec where to free the data
242 */
243static void
244clean_variable_data (void *cls,
245 struct GNUNET_JSON_Specification *spec)
246{
247 (void) cls;
248 if (0 != *spec->size_ptr)
249 {
250 GNUNET_free (*(void **) spec->ptr);
251 *(void **) spec->ptr = NULL;
252 *spec->size_ptr = 0;
253 }
254}
255
256
257struct GNUNET_JSON_Specification
258GNUNET_JSON_spec_varsize (const char *name,
259 void **obj,
260 size_t *size)
261{
262 struct GNUNET_JSON_Specification ret = {
263 .parser = &parse_variable_data,
264 .cleaner = &clean_variable_data,
265 .cls = NULL,
266 .field = name,
267 .ptr = obj,
268 .ptr_size = 0,
269 .size_ptr = size
270 };
271
272 *obj = NULL;
273 *size = 0;
274 return ret;
275}
276
277
278/**
279 * Parse given JSON object to string.
280 *
281 * @param cls closure, NULL
282 * @param root the json object representing data
283 * @param[out] spec where to write the data
284 * @return #GNUNET_OK upon successful parsing; #GNUNET_SYSERR upon error
285 */
286static enum GNUNET_GenericReturnValue
287parse_string (void *cls,
288 json_t *root,
289 struct GNUNET_JSON_Specification *spec)
290{
291 const char *str;
292
293 (void) cls;
294 str = json_string_value (root);
295 if (NULL == str)
296 {
297 GNUNET_break_op (0);
298 return GNUNET_SYSERR;
299 }
300 *(const char **) spec->ptr = str;
301 return GNUNET_OK;
302}
303
304
305struct GNUNET_JSON_Specification
306GNUNET_JSON_spec_string (const char *name,
307 const char **strptr)
308{
309 struct GNUNET_JSON_Specification ret = {
310 .parser = &parse_string,
311 .field = name,
312 .ptr = strptr
313 };
314
315 *strptr = NULL;
316 return ret;
317}
318
319
320/**
321 * Parse given JSON object to a JSON object. (Yes, trivial.)
322 *
323 * @param cls closure, NULL
324 * @param root the json object representing data
325 * @param[out] spec where to write the data
326 * @return #GNUNET_OK upon successful parsing; #GNUNET_SYSERR upon error
327 */
328static enum GNUNET_GenericReturnValue
329parse_json (void *cls,
330 json_t *root,
331 struct GNUNET_JSON_Specification *spec)
332{
333 if (! (json_is_object (root) || json_is_array (root)))
334 {
335 GNUNET_break_op (0);
336 return GNUNET_SYSERR;
337 }
338 json_incref (root);
339 *(json_t **) spec->ptr = root;
340 return GNUNET_OK;
341}
342
343
344/**
345 * Cleanup data left from parsing JSON object.
346 *
347 * @param cls closure, NULL
348 * @param[out] spec where to free the data
349 */
350static void
351clean_json (void *cls,
352 struct GNUNET_JSON_Specification *spec)
353{
354 json_t **ptr = (json_t **) spec->ptr;
355
356 if (NULL != *ptr)
357 {
358 json_decref (*ptr);
359 *ptr = NULL;
360 }
361}
362
363
364struct GNUNET_JSON_Specification
365GNUNET_JSON_spec_json (const char *name,
366 json_t **jsonp)
367{
368 struct GNUNET_JSON_Specification ret = {
369 .parser = &parse_json,
370 .cleaner = &clean_json,
371 .cls = NULL,
372 .field = name,
373 .ptr = jsonp,
374 .ptr_size = 0,
375 .size_ptr = NULL
376 };
377
378 *jsonp = NULL;
379 return ret;
380}
381
382
383/**
384 * Parse given JSON object to a JSON object.
385 *
386 * @param cls closure, NULL
387 * @param root the json object representing data
388 * @param[out] spec where to write the data
389 * @return #GNUNET_OK upon successful parsing; #GNUNET_SYSERR upon error
390 */
391static enum GNUNET_GenericReturnValue
392parse_object_const (void *cls,
393 json_t *root,
394 struct GNUNET_JSON_Specification *spec)
395{
396 if (NULL == root)
397 return GNUNET_OK;
398 if (! json_is_object (root))
399 {
400 GNUNET_break_op (0);
401 return GNUNET_SYSERR;
402 }
403 *(const json_t **) spec->ptr = (const json_t *) root;
404 return GNUNET_OK;
405}
406
407
408struct GNUNET_JSON_Specification
409GNUNET_JSON_spec_object_const (const char *name,
410 const json_t **jsonp)
411{
412 struct GNUNET_JSON_Specification ret = {
413 .parser = &parse_object_const,
414 .cls = NULL,
415 .field = name,
416 .ptr = jsonp,
417 .ptr_size = 0,
418 .size_ptr = NULL
419 };
420
421 *jsonp = NULL;
422 return ret;
423}
424
425
426/**
427 * Parse given JSON to a JSON array.
428 *
429 * @param cls closure, NULL
430 * @param root the json object representing data
431 * @param[out] spec where to write the data
432 * @return #GNUNET_OK upon successful parsing; #GNUNET_SYSERR upon error
433 */
434static enum GNUNET_GenericReturnValue
435parse_array_const (void *cls,
436 json_t *root,
437 struct GNUNET_JSON_Specification *spec)
438{
439 if (NULL == root)
440 return GNUNET_OK;
441 if (! json_is_array (root))
442 {
443 GNUNET_break_op (0);
444 return GNUNET_SYSERR;
445 }
446 *(const json_t **) spec->ptr = (const json_t *) root;
447 return GNUNET_OK;
448}
449
450
451struct GNUNET_JSON_Specification
452GNUNET_JSON_spec_array_const (const char *name,
453 const json_t **jsonp)
454{
455 struct GNUNET_JSON_Specification ret = {
456 .parser = &parse_array_const,
457 .cls = NULL,
458 .field = name,
459 .ptr = jsonp,
460 .ptr_size = 0,
461 .size_ptr = NULL
462 };
463
464 *jsonp = NULL;
465 return ret;
466}
467
468
469/**
470 * Parse given JSON object to a bool.
471 *
472 * @param cls closure, NULL
473 * @param root the json object representing data
474 * @param[out] spec where to write the data
475 * @return #GNUNET_OK upon successful parsing; #GNUNET_SYSERR upon error
476 */
477static enum GNUNET_GenericReturnValue
478parse_bool (void *cls,
479 json_t *root,
480 struct GNUNET_JSON_Specification *spec)
481{
482 bool *b = spec->ptr;
483
484 if (json_true () == root)
485 {
486 *b = true;
487 return GNUNET_OK;
488 }
489 if (json_false () == root)
490 {
491 *b = false;
492 return GNUNET_OK;
493 }
494 GNUNET_break_op (0);
495 return GNUNET_SYSERR;
496}
497
498
499struct GNUNET_JSON_Specification
500GNUNET_JSON_spec_bool (const char *name,
501 bool *b)
502{
503 struct GNUNET_JSON_Specification ret = {
504 .parser = &parse_bool,
505 .cleaner = NULL,
506 .cls = NULL,
507 .field = name,
508 .ptr = b,
509 .ptr_size = sizeof(bool),
510 .size_ptr = NULL
511 };
512
513 return ret;
514}
515
516
517/**
518 * Parse given JSON object to a double.
519 *
520 * @param cls closure, NULL
521 * @param root the json object representing data
522 * @param[out] spec where to write the data
523 * @return #GNUNET_OK upon successful parsing; #GNUNET_SYSERR upon error
524 */
525static enum GNUNET_GenericReturnValue
526parse_double (void *cls,
527 json_t *root,
528 struct GNUNET_JSON_Specification *spec)
529{
530 double *f = spec->ptr;
531
532 if (! json_is_real (root))
533 {
534 GNUNET_break_op (0);
535 return GNUNET_SYSERR;
536 }
537 *f = json_real_value (root);
538 return GNUNET_OK;
539}
540
541
542struct GNUNET_JSON_Specification
543GNUNET_JSON_spec_double (const char *name,
544 double *f)
545{
546 struct GNUNET_JSON_Specification ret = {
547 .parser = &parse_double,
548 .cleaner = NULL,
549 .cls = NULL,
550 .field = name,
551 .ptr = f,
552 .ptr_size = sizeof(double),
553 .size_ptr = NULL
554 };
555
556 return ret;
557}
558
559
560/**
561 * Parse given JSON object to a uint8_t.
562 *
563 * @param cls closure, NULL
564 * @param root the json object representing data
565 * @param[out] spec where to write the data
566 * @return #GNUNET_OK upon successful parsing; #GNUNET_SYSERR upon error
567 */
568static enum GNUNET_GenericReturnValue
569parse_u8 (void *cls,
570 json_t *root,
571 struct GNUNET_JSON_Specification *spec)
572{
573 json_int_t val;
574 uint8_t *up = spec->ptr;
575
576 if (! json_is_integer (root))
577 {
578 GNUNET_break_op (0);
579 return GNUNET_SYSERR;
580 }
581 val = json_integer_value (root);
582 if ((0 > val) || (val > UINT8_MAX))
583 {
584 GNUNET_break_op (0);
585 return GNUNET_SYSERR;
586 }
587 *up = (uint8_t) val;
588 return GNUNET_OK;
589}
590
591
592struct GNUNET_JSON_Specification
593GNUNET_JSON_spec_uint8 (const char *name,
594 uint8_t *u8)
595{
596 struct GNUNET_JSON_Specification ret = {
597 .parser = &parse_u8,
598 .cleaner = NULL,
599 .cls = NULL,
600 .field = name,
601 .ptr = u8,
602 .ptr_size = sizeof(uint8_t),
603 .size_ptr = NULL
604 };
605
606 return ret;
607}
608
609
610/**
611 * Parse given JSON object to a uint16_t.
612 *
613 * @param cls closure, NULL
614 * @param root the json object representing data
615 * @param[out] spec where to write the data
616 * @return #GNUNET_OK upon successful parsing; #GNUNET_SYSERR upon error
617 */
618static enum GNUNET_GenericReturnValue
619parse_u16 (void *cls,
620 json_t *root,
621 struct GNUNET_JSON_Specification *spec)
622{
623 json_int_t val;
624 uint16_t *up = spec->ptr;
625
626 if (! json_is_integer (root))
627 {
628 GNUNET_break_op (0);
629 return GNUNET_SYSERR;
630 }
631 val = json_integer_value (root);
632 if ((0 > val) || (val > UINT16_MAX))
633 {
634 GNUNET_break_op (0);
635 return GNUNET_SYSERR;
636 }
637 *up = (uint16_t) val;
638 return GNUNET_OK;
639}
640
641
642struct GNUNET_JSON_Specification
643GNUNET_JSON_spec_uint16 (const char *name,
644 uint16_t *u16)
645{
646 struct GNUNET_JSON_Specification ret = {
647 .parser = &parse_u16,
648 .cleaner = NULL,
649 .cls = NULL,
650 .field = name,
651 .ptr = u16,
652 .ptr_size = sizeof(uint16_t),
653 .size_ptr = NULL
654 };
655
656 return ret;
657}
658
659
660/**
661 * Parse given JSON object to a uint32_t.
662 *
663 * @param cls closure, NULL
664 * @param root the json object representing data
665 * @param[out] spec where to write the data
666 * @return #GNUNET_OK upon successful parsing; #GNUNET_SYSERR upon error
667 */
668static enum GNUNET_GenericReturnValue
669parse_u32 (void *cls,
670 json_t *root,
671 struct GNUNET_JSON_Specification *spec)
672{
673 json_int_t val;
674 uint32_t *up = spec->ptr;
675
676 if (! json_is_integer (root))
677 {
678 GNUNET_break_op (0);
679 return GNUNET_SYSERR;
680 }
681 val = json_integer_value (root);
682 if ((0 > val) || (val > UINT32_MAX))
683 {
684 GNUNET_break_op (0);
685 return GNUNET_SYSERR;
686 }
687 *up = (uint32_t) val;
688 return GNUNET_OK;
689}
690
691
692struct GNUNET_JSON_Specification
693GNUNET_JSON_spec_uint32 (const char *name,
694 uint32_t *u32)
695{
696 struct GNUNET_JSON_Specification ret = {
697 .parser = &parse_u32,
698 .cleaner = NULL,
699 .cls = NULL,
700 .field = name,
701 .ptr = u32,
702 .ptr_size = sizeof(uint32_t),
703 .size_ptr = NULL
704 };
705
706 return ret;
707}
708
709
710/**
711 * Parse given JSON object to a uint64_t.
712 *
713 * @param cls closure, NULL
714 * @param root the json object representing data
715 * @param[out] spec where to write the data
716 * @return #GNUNET_OK upon successful parsing; #GNUNET_SYSERR upon error
717 */
718static enum GNUNET_GenericReturnValue
719parse_u64 (void *cls,
720 json_t *root,
721 struct GNUNET_JSON_Specification *spec)
722{
723 json_int_t val;
724 uint64_t *up = spec->ptr;
725
726 if (! json_is_integer (root))
727 {
728 GNUNET_break_op (0);
729 return GNUNET_SYSERR;
730 }
731 val = json_integer_value (root);
732 *up = (uint64_t) val;
733 return GNUNET_OK;
734}
735
736
737struct GNUNET_JSON_Specification
738GNUNET_JSON_spec_uint64 (const char *name,
739 uint64_t *u64)
740{
741 struct GNUNET_JSON_Specification ret = {
742 .parser = &parse_u64,
743 .cleaner = NULL,
744 .cls = NULL,
745 .field = name,
746 .ptr = u64,
747 .ptr_size = sizeof(uint64_t),
748 .size_ptr = NULL
749 };
750
751 return ret;
752}
753
754
755/**
756 * Parse given JSON object to a int64_t.
757 *
758 * @param cls closure, NULL
759 * @param root the json object representing data
760 * @param[out] spec where to write the data
761 * @return #GNUNET_OK upon successful parsing; #GNUNET_SYSERR upon error
762 */
763static enum GNUNET_GenericReturnValue
764parse_i64 (void *cls,
765 json_t *root,
766 struct GNUNET_JSON_Specification *spec)
767{
768 json_int_t val;
769 int64_t *up = spec->ptr;
770
771 if (! json_is_integer (root))
772 {
773 GNUNET_break_op (0);
774 return GNUNET_SYSERR;
775 }
776 val = json_integer_value (root);
777 *up = (int64_t) val;
778 return GNUNET_OK;
779}
780
781
782struct GNUNET_JSON_Specification
783GNUNET_JSON_spec_int64 (const char *name,
784 int64_t *i64)
785{
786 struct GNUNET_JSON_Specification ret = {
787 .parser = &parse_i64,
788 .cleaner = NULL,
789 .cls = NULL,
790 .field = name,
791 .ptr = i64,
792 .ptr_size = sizeof(int64_t),
793 .size_ptr = NULL
794 };
795
796 return ret;
797}
798
799
800/* ************ GNUnet-specific parser specifications ******************* */
801
802/**
803 * Parse given JSON object to a timestamp.
804 *
805 * @param cls closure, NULL
806 * @param root the json object representing data
807 * @param[out] spec where to write the data
808 * @return #GNUNET_OK upon successful parsing; #GNUNET_SYSERR upon error
809 */
810static enum GNUNET_GenericReturnValue
811parse_timestamp (void *cls,
812 json_t *root,
813 struct GNUNET_JSON_Specification *spec)
814{
815 struct GNUNET_TIME_Timestamp *ts = spec->ptr;
816 json_t *json_t_s;
817 unsigned long long int tval;
818
819 if (! json_is_object (root))
820 {
821 GNUNET_break_op (0);
822 return GNUNET_SYSERR;
823 }
824 json_t_s = json_object_get (root,
825 "t_s");
826 if (json_is_integer (json_t_s))
827 {
828 tval = json_integer_value (json_t_s);
829 /* Time is in seconds in JSON, but in microseconds in GNUNET_TIME_Absolute */
830 ts->abs_time.abs_value_us
831 = tval * GNUNET_TIME_UNIT_SECONDS.rel_value_us;
832 if (ts->abs_time.abs_value_us
833 / GNUNET_TIME_UNIT_SECONDS.rel_value_us
834 != tval)
835 {
836 /* Integer overflow */
837 GNUNET_break_op (0);
838 return GNUNET_SYSERR;
839 }
840 return GNUNET_OK;
841 }
842 if (json_is_string (json_t_s))
843 {
844 const char *val;
845
846 val = json_string_value (json_t_s);
847 if ((0 == strcasecmp (val,
848 "never")))
849 {
850 ts->abs_time = GNUNET_TIME_UNIT_FOREVER_ABS;
851 return GNUNET_OK;
852 }
853 GNUNET_break_op (0);
854 return GNUNET_SYSERR;
855 }
856 GNUNET_break_op (0);
857 return GNUNET_SYSERR;
858}
859
860
861struct GNUNET_JSON_Specification
862GNUNET_JSON_spec_timestamp (const char *name,
863 struct GNUNET_TIME_Timestamp *t)
864{
865 struct GNUNET_JSON_Specification ret = {
866 .parser = &parse_timestamp,
867 .field = name,
868 .ptr = t,
869 .ptr_size = sizeof(struct GNUNET_TIME_Timestamp)
870 };
871
872 return ret;
873}
874
875
876/**
877 * Parse given JSON object to absolute time.
878 *
879 * @param cls closure, NULL
880 * @param root the json object representing data
881 * @param[out] spec where to write the data
882 * @return #GNUNET_OK upon successful parsing; #GNUNET_SYSERR upon error
883 */
884static enum GNUNET_GenericReturnValue
885parse_timestamp_nbo (void *cls,
886 json_t *root,
887 struct GNUNET_JSON_Specification *spec)
888{
889 struct GNUNET_TIME_TimestampNBO *ts = spec->ptr;
890 struct GNUNET_TIME_Timestamp a;
891 struct GNUNET_JSON_Specification ispec;
892
893 ispec = *spec;
894 ispec.parser = &parse_timestamp;
895 ispec.ptr = &a;
896 if (GNUNET_OK !=
897 parse_timestamp (NULL,
898 root,
899 &ispec))
900 return GNUNET_SYSERR;
901 *ts = GNUNET_TIME_timestamp_hton (a);
902 return GNUNET_OK;
903}
904
905
906struct GNUNET_JSON_Specification
907GNUNET_JSON_spec_timestamp_nbo (const char *name,
908 struct GNUNET_TIME_TimestampNBO *at)
909{
910 struct GNUNET_JSON_Specification ret = {
911 .parser = &parse_timestamp_nbo,
912 .field = name,
913 .ptr = at,
914 .ptr_size = sizeof(struct GNUNET_TIME_TimestampNBO)
915 };
916
917 return ret;
918}
919
920
921/**
922 * Parse given JSON object to relative time.
923 *
924 * @param cls closure, NULL
925 * @param root the json object representing data
926 * @param[out] spec where to write the data
927 * @return #GNUNET_OK upon successful parsing; #GNUNET_SYSERR upon error
928 */
929static enum GNUNET_GenericReturnValue
930parse_rel_time (void *cls,
931 json_t *root,
932 struct GNUNET_JSON_Specification *spec)
933{
934 struct GNUNET_TIME_Relative *rel = spec->ptr;
935 json_t *json_d_us;
936 unsigned long long int tval;
937
938 if (! json_is_object (root))
939 {
940 GNUNET_break_op (0);
941 return GNUNET_SYSERR;
942 }
943 json_d_us = json_object_get (root,
944 "d_us");
945 if (json_is_integer (json_d_us))
946 {
947 tval = json_integer_value (json_d_us);
948 if (tval >= (1LLU << 53))
949 {
950 /* value is larger than allowed */
951 GNUNET_break_op (0);
952 return GNUNET_SYSERR;
953 }
954 rel->rel_value_us = tval;
955 return GNUNET_OK;
956 }
957 if (json_is_string (json_d_us))
958 {
959 const char *val;
960
961 val = json_string_value (json_d_us);
962 if ((0 == strcasecmp (val,
963 "forever")))
964 {
965 *rel = GNUNET_TIME_UNIT_FOREVER_REL;
966 return GNUNET_OK;
967 }
968 GNUNET_break_op (0);
969 return GNUNET_SYSERR;
970 }
971 GNUNET_break_op (0);
972 return GNUNET_SYSERR;
973}
974
975
976struct GNUNET_JSON_Specification
977GNUNET_JSON_spec_relative_time (const char *name,
978 struct GNUNET_TIME_Relative *rt)
979{
980 struct GNUNET_JSON_Specification ret = {
981 .parser = &parse_rel_time,
982 .field = name,
983 .ptr = rt,
984 .ptr_size = sizeof(struct GNUNET_TIME_Relative)
985 };
986
987 return ret;
988}
989
990
991/**
992 * Parse given JSON object to RSA public key.
993 *
994 * @param cls closure, NULL
995 * @param root the json object representing data
996 * @param[out] spec where to write the data
997 * @return #GNUNET_OK upon successful parsing; #GNUNET_SYSERR upon error
998 */
999static enum GNUNET_GenericReturnValue
1000parse_rsa_public_key (void *cls,
1001 json_t *root,
1002 struct GNUNET_JSON_Specification *spec)
1003{
1004 struct GNUNET_CRYPTO_RsaPublicKey **pk = spec->ptr;
1005 const char *enc;
1006 char *buf;
1007 size_t len;
1008 size_t buf_len;
1009
1010 if (NULL == (enc = json_string_value (root)))
1011 {
1012 GNUNET_break_op (0);
1013 return GNUNET_SYSERR;
1014 }
1015 len = strlen (enc);
1016 buf_len = (len * 5) / 8;
1017 buf = GNUNET_malloc (buf_len);
1018 if (GNUNET_OK !=
1019 GNUNET_STRINGS_string_to_data (enc,
1020 len,
1021 buf,
1022 buf_len))
1023 {
1024 GNUNET_break_op (0);
1025 GNUNET_free (buf);
1026 return GNUNET_SYSERR;
1027 }
1028 if (NULL == (*pk = GNUNET_CRYPTO_rsa_public_key_decode (buf,
1029 buf_len)))
1030 {
1031 GNUNET_break_op (0);
1032 GNUNET_free (buf);
1033 return GNUNET_SYSERR;
1034 }
1035 GNUNET_free (buf);
1036 return GNUNET_OK;
1037}
1038
1039
1040/**
1041 * Cleanup data left from parsing RSA public key.
1042 *
1043 * @param cls closure, NULL
1044 * @param[out] spec where to free the data
1045 */
1046static void
1047clean_rsa_public_key (void *cls,
1048 struct GNUNET_JSON_Specification *spec)
1049{
1050 struct GNUNET_CRYPTO_RsaPublicKey **pk = spec->ptr;
1051
1052 if (NULL != *pk)
1053 {
1054 GNUNET_CRYPTO_rsa_public_key_free (*pk);
1055 *pk = NULL;
1056 }
1057}
1058
1059
1060struct GNUNET_JSON_Specification
1061GNUNET_JSON_spec_rsa_public_key (const char *name,
1062 struct GNUNET_CRYPTO_RsaPublicKey **pk)
1063{
1064 struct GNUNET_JSON_Specification ret = {
1065 .parser = &parse_rsa_public_key,
1066 .cleaner = &clean_rsa_public_key,
1067 .field = name,
1068 .ptr = pk
1069 };
1070
1071 *pk = NULL;
1072 return ret;
1073}
1074
1075
1076/**
1077 * Parse given JSON object to RSA signature.
1078 *
1079 * @param cls closure, NULL
1080 * @param root the json object representing data
1081 * @param[out] spec where to write the data
1082 * @return #GNUNET_OK upon successful parsing; #GNUNET_SYSERR upon error
1083 */
1084static enum GNUNET_GenericReturnValue
1085parse_rsa_signature (void *cls,
1086 json_t *root,
1087 struct GNUNET_JSON_Specification *spec)
1088{
1089 struct GNUNET_CRYPTO_RsaSignature **sig = spec->ptr;
1090 size_t size;
1091 const char *str;
1092 int res;
1093 void *buf;
1094
1095 str = json_string_value (root);
1096 if (NULL == str)
1097 {
1098 GNUNET_break_op (0);
1099 return GNUNET_SYSERR;
1100 }
1101 size = (strlen (str) * 5) / 8;
1102 buf = GNUNET_malloc (size);
1103 res = GNUNET_STRINGS_string_to_data (str,
1104 strlen (str),
1105 buf,
1106 size);
1107 if (GNUNET_OK != res)
1108 {
1109 GNUNET_free (buf);
1110 GNUNET_break_op (0);
1111 return GNUNET_SYSERR;
1112 }
1113 if (NULL == (*sig = GNUNET_CRYPTO_rsa_signature_decode (buf,
1114 size)))
1115 {
1116 GNUNET_break_op (0);
1117 GNUNET_free (buf);
1118 return GNUNET_SYSERR;
1119 }
1120 GNUNET_free (buf);
1121 return GNUNET_OK;
1122}
1123
1124
1125/**
1126 * Cleanup data left from parsing RSA signature.
1127 *
1128 * @param cls closure, NULL
1129 * @param[out] spec where to free the data
1130 */
1131static void
1132clean_rsa_signature (void *cls,
1133 struct GNUNET_JSON_Specification *spec)
1134{
1135 struct GNUNET_CRYPTO_RsaSignature **sig = spec->ptr;
1136
1137 if (NULL != *sig)
1138 {
1139 GNUNET_CRYPTO_rsa_signature_free (*sig);
1140 *sig = NULL;
1141 }
1142}
1143
1144
1145struct GNUNET_JSON_Specification
1146GNUNET_JSON_spec_rsa_signature (const char *name,
1147 struct GNUNET_CRYPTO_RsaSignature **sig)
1148{
1149 struct GNUNET_JSON_Specification ret = {
1150 .parser = &parse_rsa_signature,
1151 .cleaner = &clean_rsa_signature,
1152 .cls = NULL,
1153 .field = name,
1154 .ptr = sig,
1155 .ptr_size = 0,
1156 .size_ptr = NULL
1157 };
1158
1159 *sig = NULL;
1160 return ret;
1161}
1162
1163
1164/**
1165 * Parse given JSON object to an int as a boolean.
1166 *
1167 * @param cls closure, NULL
1168 * @param root the json object representing data
1169 * @param[out] spec where to write the data
1170 * @return #GNUNET_OK upon successful parsing; #GNUNET_SYSERR upon error
1171 */
1172static enum GNUNET_GenericReturnValue
1173parse_boolean (void *cls,
1174 json_t *root,
1175 struct GNUNET_JSON_Specification *spec)
1176{
1177 int *bp = spec->ptr;
1178
1179 if (! json_is_boolean (root))
1180 {
1181 GNUNET_break_op (0);
1182 return GNUNET_SYSERR;
1183 }
1184 *bp = json_boolean_value (root) ? GNUNET_YES : GNUNET_NO;
1185 return GNUNET_OK;
1186}
1187
1188
1189struct GNUNET_JSON_Specification
1190GNUNET_JSON_spec_boolean (const char *name,
1191 int *boolean)
1192{
1193 struct GNUNET_JSON_Specification ret = {
1194 .parser = &parse_boolean,
1195 .cleaner = NULL,
1196 .cls = NULL,
1197 .field = name,
1198 .ptr = boolean,
1199 .ptr_size = sizeof(int),
1200 .size_ptr = NULL
1201 };
1202
1203 return ret;
1204}
1205
1206
1207/**
1208 * Parse given JSON object to a blinded message.
1209 *
1210 * @param cls closure, NULL
1211 * @param root the json object representing data
1212 * @param[out] spec where to write the data
1213 * @return #GNUNET_OK upon successful parsing; #GNUNET_SYSERR upon error
1214 */
1215static enum GNUNET_GenericReturnValue
1216parse_blinded_message (void *cls,
1217 json_t *root,
1218 struct GNUNET_JSON_Specification *spec)
1219{
1220 struct GNUNET_CRYPTO_BlindedMessage **target = spec->ptr;
1221 struct GNUNET_CRYPTO_BlindedMessage *blinded_message;
1222 const char *cipher;
1223 struct GNUNET_JSON_Specification dspec[] = {
1224 GNUNET_JSON_spec_string ("cipher",
1225 &cipher),
1226 GNUNET_JSON_spec_end ()
1227 };
1228 const char *emsg;
1229 unsigned int eline;
1230
1231 (void) cls;
1232 if (GNUNET_OK !=
1233 GNUNET_JSON_parse (root,
1234 dspec,
1235 &emsg,
1236 &eline))
1237 {
1238 GNUNET_break_op (0);
1239 return GNUNET_SYSERR;
1240 }
1241 blinded_message = GNUNET_new (struct GNUNET_CRYPTO_BlindedMessage);
1242 blinded_message->rc = 1;
1243 blinded_message->cipher = string_to_cipher (cipher);
1244 switch (blinded_message->cipher)
1245 {
1246 case GNUNET_CRYPTO_BSA_INVALID:
1247 break;
1248 case GNUNET_CRYPTO_BSA_RSA:
1249 {
1250 struct GNUNET_JSON_Specification ispec[] = {
1251 GNUNET_JSON_spec_varsize (
1252 /* TODO: Change this field name to something
1253 more generic / pass in as argument. */
1254 "rsa_blinded_planchet",
1255 &blinded_message->details.rsa_blinded_message.blinded_msg,
1256 &blinded_message->details.rsa_blinded_message.blinded_msg_size),
1257 GNUNET_JSON_spec_end ()
1258 };
1259
1260 if (GNUNET_OK !=
1261 GNUNET_JSON_parse (root,
1262 ispec,
1263 &emsg,
1264 &eline))
1265 {
1266 GNUNET_break_op (0);
1267 GNUNET_free (blinded_message);
1268 return GNUNET_SYSERR;
1269 }
1270 *target = blinded_message;
1271 return GNUNET_OK;
1272 }
1273 case GNUNET_CRYPTO_BSA_CS:
1274 {
1275 struct GNUNET_JSON_Specification ispec[] = {
1276 GNUNET_JSON_spec_fixed_auto (
1277 "cs_nonce",
1278 &blinded_message->details.cs_blinded_message.nonce),
1279 GNUNET_JSON_spec_fixed_auto (
1280 "cs_blinded_c0",
1281 &blinded_message->details.cs_blinded_message.c[0]),
1282 GNUNET_JSON_spec_fixed_auto (
1283 "cs_blinded_c1",
1284 &blinded_message->details.cs_blinded_message.c[1]),
1285 GNUNET_JSON_spec_end ()
1286 };
1287
1288 if (GNUNET_OK !=
1289 GNUNET_JSON_parse (root,
1290 ispec,
1291 &emsg,
1292 &eline))
1293 {
1294 GNUNET_break_op (0);
1295 GNUNET_free (blinded_message);
1296 return GNUNET_SYSERR;
1297 }
1298 *target = blinded_message;
1299 return GNUNET_OK;
1300 }
1301 }
1302 GNUNET_break_op (0);
1303 GNUNET_free (blinded_message);
1304 return GNUNET_SYSERR;
1305}
1306
1307/**
1308 * Cleanup data left from parsing blinded message.
1309 *
1310 * @param cls closure, NULL
1311 * @param[out] spec where to free the data
1312 */
1313static void
1314clean_blinded_message (void *cls,
1315 struct GNUNET_JSON_Specification *spec)
1316{
1317 struct GNUNET_CRYPTO_BlindedMessage **blinded_message = spec->ptr;
1318
1319 (void) cls;
1320 if (NULL != blinded_message)
1321 {
1322 GNUNET_CRYPTO_blinded_message_decref (*blinded_message);
1323 *blinded_message = NULL;
1324 }
1325}
1326
1327
1328struct GNUNET_JSON_Specification
1329GNUNET_JSON_spec_blinded_message (const char *name,
1330 struct GNUNET_CRYPTO_BlindedMessage **msg)
1331{
1332 struct GNUNET_JSON_Specification ret = {
1333 .parser = &parse_blinded_message,
1334 .cleaner = &clean_blinded_message,
1335 .cls = NULL,
1336 .field = name,
1337 .ptr = msg,
1338 .ptr_size = 0,
1339 .size_ptr = NULL
1340 };
1341
1342 *msg = NULL;
1343 return ret;
1344}
1345
1346
1347/**
1348 * Parse given JSON object to a blinded signature.
1349 *
1350 * @param cls closure, NULL
1351 * @param root the json object representing data
1352 * @param[out] spec where to write the data
1353 * @return #GNUNET_OK upon successful parsing; #GNUNET_SYSERR upon error
1354 */
1355static enum GNUNET_GenericReturnValue
1356parse_blinded_sig (void *cls,
1357 json_t *root,
1358 struct GNUNET_JSON_Specification *spec)
1359{
1360 struct GNUNET_CRYPTO_BlindedSignature **target = spec->ptr;
1361 struct GNUNET_CRYPTO_BlindedSignature *blinded_sig;
1362 const char *cipher;
1363 struct GNUNET_JSON_Specification dspec[] = {
1364 GNUNET_JSON_spec_string ("cipher",
1365 &cipher),
1366 GNUNET_JSON_spec_end ()
1367 };
1368 const char *emsg;
1369 unsigned int eline;
1370
1371 (void) cls;
1372 if (GNUNET_OK !=
1373 GNUNET_JSON_parse (root,
1374 dspec,
1375 &emsg,
1376 &eline))
1377 {
1378 GNUNET_break_op (0);
1379 return GNUNET_SYSERR;
1380 }
1381 blinded_sig = GNUNET_new (struct GNUNET_CRYPTO_BlindedSignature);
1382 blinded_sig->cipher = string_to_cipher (cipher);
1383 blinded_sig->rc = 1;
1384 switch (blinded_sig->cipher)
1385 {
1386 case GNUNET_CRYPTO_BSA_INVALID:
1387 break;
1388 case GNUNET_CRYPTO_BSA_RSA:
1389 {
1390 struct GNUNET_JSON_Specification ispec[] = {
1391 GNUNET_JSON_spec_rsa_signature (
1392 "blinded_rsa_signature",
1393 &blinded_sig->details.blinded_rsa_signature),
1394 GNUNET_JSON_spec_end ()
1395 };
1396
1397 if (GNUNET_OK !=
1398 GNUNET_JSON_parse (root,
1399 ispec,
1400 &emsg,
1401 &eline))
1402 {
1403 GNUNET_break_op (0);
1404 GNUNET_free (blinded_sig);
1405 return GNUNET_SYSERR;
1406 }
1407 *target = blinded_sig;
1408 return GNUNET_OK;
1409 }
1410 case GNUNET_CRYPTO_BSA_CS:
1411 {
1412 struct GNUNET_JSON_Specification ispec[] = {
1413 GNUNET_JSON_spec_uint32 ("b",
1414 &blinded_sig->details.blinded_cs_answer.b),
1415 GNUNET_JSON_spec_fixed_auto ("s",
1416 &blinded_sig->details.blinded_cs_answer.
1417 s_scalar),
1418 GNUNET_JSON_spec_end ()
1419 };
1420
1421 if (GNUNET_OK !=
1422 GNUNET_JSON_parse (root,
1423 ispec,
1424 &emsg,
1425 &eline))
1426 {
1427 GNUNET_break_op (0);
1428 GNUNET_free (blinded_sig);
1429 return GNUNET_SYSERR;
1430 }
1431 *target = blinded_sig;
1432 return GNUNET_OK;
1433 }
1434 }
1435 GNUNET_break_op (0);
1436 GNUNET_free (blinded_sig);
1437 return GNUNET_SYSERR;
1438}
1439
1440
1441/**
1442 * Cleanup data left from parsing blinded sig.
1443 *
1444 * @param cls closure, NULL
1445 * @param[out] spec where to free the data
1446 */
1447static void
1448clean_blinded_sig (void *cls,
1449 struct GNUNET_JSON_Specification *spec)
1450{
1451 struct GNUNET_CRYPTO_BlindedSignature **b_sig = spec->ptr;
1452
1453 (void) cls;
1454
1455 if (NULL != *b_sig)
1456 {
1457 GNUNET_CRYPTO_blinded_sig_decref (*b_sig);
1458 *b_sig = NULL;
1459 }
1460}
1461
1462
1463struct GNUNET_JSON_Specification
1464GNUNET_JSON_spec_blinded_signature (const char *field,
1465 struct GNUNET_CRYPTO_BlindedSignature **b_sig)
1466{
1467 struct GNUNET_JSON_Specification ret = {
1468 .parser = &parse_blinded_sig,
1469 .cleaner = &clean_blinded_sig,
1470 .field = field,
1471 .ptr = b_sig
1472 };
1473
1474 *b_sig = NULL;
1475 return ret;
1476}
1477
1478/**
1479 * Parse given JSON object to unblinded signature.
1480 *
1481 * @param cls closure, NULL
1482 * @param root the json object representing data
1483 * @param[out] spec where to write the data
1484 * @return #GNUNET_OK upon successful parsing; #GNUNET_SYSERR upon error
1485 */
1486static enum GNUNET_GenericReturnValue
1487parse_unblinded_sig (void *cls,
1488 json_t *root,
1489 struct GNUNET_JSON_Specification *spec)
1490{
1491 struct GNUNET_CRYPTO_UnblindedSignature **target = spec->ptr;
1492 struct GNUNET_CRYPTO_UnblindedSignature *unblinded_sig;
1493 const char *cipher;
1494 struct GNUNET_JSON_Specification dspec[] = {
1495 GNUNET_JSON_spec_string ("cipher",
1496 &cipher),
1497 GNUNET_JSON_spec_end ()
1498 };
1499 const char *emsg;
1500 unsigned int eline;
1501
1502 (void) cls;
1503 if (GNUNET_OK !=
1504 GNUNET_JSON_parse (root,
1505 dspec,
1506 &emsg,
1507 &eline))
1508 {
1509 GNUNET_break_op (0);
1510 return GNUNET_SYSERR;
1511 }
1512 unblinded_sig = GNUNET_new (struct GNUNET_CRYPTO_UnblindedSignature);
1513 unblinded_sig->cipher = string_to_cipher (cipher);
1514 unblinded_sig->rc = 1;
1515 switch (unblinded_sig->cipher)
1516 {
1517 case GNUNET_CRYPTO_BSA_INVALID:
1518 break;
1519 case GNUNET_CRYPTO_BSA_RSA:
1520 {
1521 struct GNUNET_JSON_Specification ispec[] = {
1522 GNUNET_JSON_spec_rsa_signature (
1523 "rsa_signature",
1524 &unblinded_sig->details.rsa_signature),
1525 GNUNET_JSON_spec_end ()
1526 };
1527
1528 if (GNUNET_OK !=
1529 GNUNET_JSON_parse (root,
1530 ispec,
1531 &emsg,
1532 &eline))
1533 {
1534 GNUNET_break_op (0);
1535 GNUNET_free (unblinded_sig);
1536 return GNUNET_SYSERR;
1537 }
1538 *target = unblinded_sig;
1539 return GNUNET_OK;
1540 }
1541 case GNUNET_CRYPTO_BSA_CS:
1542 {
1543 struct GNUNET_JSON_Specification ispec[] = {
1544 GNUNET_JSON_spec_fixed_auto ("cs_signature_r",
1545 &unblinded_sig->details.cs_signature.
1546 r_point),
1547 GNUNET_JSON_spec_fixed_auto ("cs_signature_s",
1548 &unblinded_sig->details.cs_signature.
1549 s_scalar),
1550 GNUNET_JSON_spec_end ()
1551 };
1552
1553 if (GNUNET_OK !=
1554 GNUNET_JSON_parse (root,
1555 ispec,
1556 &emsg,
1557 &eline))
1558 {
1559 GNUNET_break_op (0);
1560 GNUNET_free (unblinded_sig);
1561 return GNUNET_SYSERR;
1562 }
1563 *target = unblinded_sig;
1564 return GNUNET_OK;
1565 }
1566 }
1567 GNUNET_break_op (0);
1568 GNUNET_free (unblinded_sig);
1569 return GNUNET_SYSERR;
1570}
1571
1572
1573/**
1574 * Cleanup data left from parsing unblinded signature.
1575 *
1576 * @param cls closure, NULL
1577 * @param[out] spec where to free the data
1578 */
1579static void
1580clean_unblinded_sig (void *cls,
1581 struct GNUNET_JSON_Specification *spec)
1582{
1583 struct GNUNET_CRYPTO_UnblindedSignature **ub_sig = spec->ptr;
1584
1585 (void) cls;
1586 if (NULL != *ub_sig)
1587 {
1588 GNUNET_CRYPTO_unblinded_sig_decref (*ub_sig);
1589 *ub_sig = NULL;
1590 }
1591}
1592
1593
1594struct GNUNET_JSON_Specification
1595GNUNET_JSON_spec_unblinded_signature (const char *field,
1596 struct GNUNET_CRYPTO_UnblindedSignature **ub_sig)
1597{
1598 struct GNUNET_JSON_Specification ret = {
1599 .parser = &parse_unblinded_sig,
1600 .cleaner = &clean_unblinded_sig,
1601 .field = field,
1602 .ptr = ub_sig
1603 };
1604
1605 *ub_sig = NULL;
1606 return ret;
1607}
1608
1609
1610
1611/* end of json_helper.c */
diff --git a/src/lib/json/json_mhd.c b/src/lib/json/json_mhd.c
new file mode 100644
index 000000000..0b0fa0538
--- /dev/null
+++ b/src/lib/json/json_mhd.c
@@ -0,0 +1,379 @@
1/*
2 This file is part of GNUnet
3 Copyright (C) 2014, 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 * @file json/json_mhd.c
22 * @brief functions to parse JSON snippets we receive via MHD
23 * @author Florian Dold
24 * @author Benedikt Mueller
25 * @author Christian Grothoff
26 */
27#include "platform.h"
28#include "gnunet_json_lib.h"
29#include <zlib.h>
30
31
32/**
33 * Initial size for POST request buffers. Should be big enough to
34 * usually not require a reallocation, but not so big that it hurts in
35 * terms of memory use.
36 */
37#define REQUEST_BUFFER_INITIAL (2 * 1024)
38
39
40/**
41 * Buffer for POST requests.
42 */
43struct Buffer
44{
45 /**
46 * Allocated memory
47 */
48 char *data;
49
50 /**
51 * Number of valid bytes in buffer.
52 */
53 size_t fill;
54
55 /**
56 * Number of allocated bytes in buffer.
57 */
58 size_t alloc;
59
60 /**
61 * Maximum buffer size allowed.
62 */
63 size_t max;
64};
65
66
67/**
68 * Initialize a buffer.
69 *
70 * @param buf the buffer to initialize
71 * @param data the initial data
72 * @param data_size size of the initial data
73 * @param alloc_size size of the buffer
74 * @param max_size maximum size that the buffer can grow to
75 * @return a GNUnet result code
76 */
77static int
78buffer_init (struct Buffer *buf,
79 const void *data,
80 size_t data_size,
81 size_t alloc_size,
82 size_t max_size)
83{
84 if ((data_size > max_size) || (alloc_size > max_size))
85 return GNUNET_SYSERR;
86 if (data_size > alloc_size)
87 alloc_size = data_size;
88 buf->data = GNUNET_malloc (alloc_size);
89 buf->alloc = alloc_size;
90 GNUNET_memcpy (buf->data, data, data_size);
91 buf->fill = data_size;
92 buf->max = max_size;
93 return GNUNET_OK;
94}
95
96
97/**
98 * Free the data in a buffer. Does *not* free
99 * the buffer object itself.
100 *
101 * @param buf buffer to de-initialize
102 */
103static void
104buffer_deinit (struct Buffer *buf)
105{
106 GNUNET_free (buf->data);
107 buf->data = NULL;
108}
109
110
111/**
112 * Append data to a buffer, growing the buffer if necessary.
113 *
114 * @param buf the buffer to append to
115 * @param data the data to append
116 * @param data_size the size of @a data
117 * @param max_size maximum size that the buffer can grow to
118 * @return #GNUNET_OK on success,
119 * #GNUNET_NO if the buffer can't accommodate for the new data
120 */
121static int
122buffer_append (struct Buffer *buf,
123 const void *data,
124 size_t data_size,
125 size_t max_size)
126{
127 if (buf->fill + data_size > max_size)
128 return GNUNET_NO;
129 if (buf->fill + data_size > buf->alloc)
130 {
131 char *new_buf;
132 size_t new_size = buf->alloc;
133 while (new_size < buf->fill + data_size)
134 new_size += 2;
135 if (new_size > max_size)
136 return GNUNET_NO;
137 new_buf = GNUNET_malloc (new_size);
138 GNUNET_memcpy (new_buf, buf->data, buf->fill);
139 GNUNET_free (buf->data);
140 buf->data = new_buf;
141 buf->alloc = new_size;
142 }
143 GNUNET_memcpy (buf->data + buf->fill, data, data_size);
144 buf->fill += data_size;
145 return GNUNET_OK;
146}
147
148
149/**
150 * Decompress data in @a buf.
151 *
152 * @param buf input data to inflate
153 * @return result code indicating the status of the operation
154 */
155static enum GNUNET_JSON_PostResult
156inflate_data (struct Buffer *buf)
157{
158 z_stream z;
159 char *tmp;
160 size_t tmp_size;
161 int ret;
162
163 memset (&z, 0, sizeof(z));
164 z.next_in = (Bytef *) buf->data;
165 z.avail_in = buf->fill;
166 tmp_size = GNUNET_MIN (buf->max, buf->fill * 4);
167 tmp = GNUNET_malloc (tmp_size);
168 z.next_out = (Bytef *) tmp;
169 z.avail_out = tmp_size;
170 ret = inflateInit (&z);
171 switch (ret)
172 {
173 case Z_MEM_ERROR:
174 GNUNET_break (0);
175 return GNUNET_JSON_PR_OUT_OF_MEMORY;
176
177 case Z_STREAM_ERROR:
178 GNUNET_break_op (0);
179 return GNUNET_JSON_PR_JSON_INVALID;
180
181 case Z_OK:
182 break;
183 }
184 while (1)
185 {
186 ret = inflate (&z, 0);
187 switch (ret)
188 {
189 case Z_BUF_ERROR:
190 GNUNET_break_op (0);
191 GNUNET_break (Z_OK == inflateEnd (&z));
192 GNUNET_free (tmp);
193 return GNUNET_JSON_PR_JSON_INVALID;
194 case Z_MEM_ERROR:
195 GNUNET_break (0);
196 GNUNET_break (Z_OK == inflateEnd (&z));
197 GNUNET_free (tmp);
198 return GNUNET_JSON_PR_OUT_OF_MEMORY;
199 case Z_DATA_ERROR:
200 GNUNET_break_op (0);
201 GNUNET_break (Z_OK == inflateEnd (&z));
202 GNUNET_free (tmp);
203 return GNUNET_JSON_PR_JSON_INVALID;
204 case Z_NEED_DICT:
205 GNUNET_break_op (0);
206 GNUNET_break (Z_OK == inflateEnd (&z));
207 GNUNET_free (tmp);
208 return GNUNET_JSON_PR_JSON_INVALID;
209 case Z_OK:
210 if ((0 < z.avail_out) && (0 == z.avail_in))
211 {
212 /* truncated input stream */
213 GNUNET_break (0);
214 GNUNET_break (Z_OK == inflateEnd (&z));
215 GNUNET_free (tmp);
216 return GNUNET_JSON_PR_JSON_INVALID;
217 }
218 if (0 < z.avail_out)
219 continue; /* just call it again */
220 /* output buffer full, can we grow it? */
221 if (tmp_size == buf->max)
222 {
223 /* already at max */
224 GNUNET_break (0);
225 GNUNET_break (Z_OK == inflateEnd (&z));
226 GNUNET_free (tmp);
227 return GNUNET_JSON_PR_OUT_OF_MEMORY;
228 }
229 if (tmp_size * 2 < tmp_size)
230 tmp_size = buf->max;
231 else
232 tmp_size = GNUNET_MIN (buf->max, tmp_size * 2);
233 tmp = GNUNET_realloc (tmp, tmp_size);
234 z.next_out = (Bytef *) &tmp[z.total_out];
235 continue;
236 case Z_STREAM_END:
237 /* decompression successful, make 'tmp' the new 'data' */
238 GNUNET_free (buf->data);
239 buf->data = tmp;
240 buf->alloc = tmp_size;
241 buf->fill = z.total_out;
242 GNUNET_break (Z_OK == inflateEnd (&z));
243 return GNUNET_JSON_PR_SUCCESS; /* at least for now */
244 }
245 } /* while (1) */
246}
247
248
249/**
250 * Process a POST request containing a JSON object. This function
251 * realizes an MHD POST processor that will (incrementally) process
252 * JSON data uploaded to the HTTP server. It will store the required
253 * state in the @a con_cls, which must be cleaned up using
254 * #GNUNET_JSON_post_parser_callback().
255 *
256 * @param buffer_max maximum allowed size for the buffer
257 * @param connection MHD connection handle (for meta data about the upload)
258 * @param con_cls the closure (will point to a `struct Buffer *`)
259 * @param upload_data the POST data
260 * @param upload_data_size number of bytes in @a upload_data
261 * @param json the JSON object for a completed request
262 * @return result code indicating the status of the operation
263 */
264enum GNUNET_JSON_PostResult
265GNUNET_JSON_post_parser (size_t buffer_max,
266 struct MHD_Connection *connection,
267 void **con_cls,
268 const char *upload_data,
269 size_t *upload_data_size,
270 json_t **json)
271{
272 struct Buffer *r = *con_cls;
273 const char *ce;
274 int ret;
275
276 *json = NULL;
277 if (NULL == *con_cls)
278 {
279 /* We are seeing a fresh POST request. */
280 r = GNUNET_new (struct Buffer);
281 if (GNUNET_OK != buffer_init (r,
282 upload_data,
283 *upload_data_size,
284 REQUEST_BUFFER_INITIAL,
285 buffer_max))
286 {
287 *con_cls = NULL;
288 buffer_deinit (r);
289 GNUNET_free (r);
290 return GNUNET_JSON_PR_OUT_OF_MEMORY;
291 }
292 /* everything OK, wait for more POST data */
293 *upload_data_size = 0;
294 *con_cls = r;
295 return GNUNET_JSON_PR_CONTINUE;
296 }
297 if (0 != *upload_data_size)
298 {
299 /* We are seeing an old request with more data available. */
300
301 if (GNUNET_OK !=
302 buffer_append (r, upload_data, *upload_data_size, buffer_max))
303 {
304 /* Request too long */
305 *con_cls = NULL;
306 buffer_deinit (r);
307 GNUNET_free (r);
308 return GNUNET_JSON_PR_REQUEST_TOO_LARGE;
309 }
310 /* everything OK, wait for more POST data */
311 *upload_data_size = 0;
312 return GNUNET_JSON_PR_CONTINUE;
313 }
314
315 /* We have seen the whole request. */
316 ce = MHD_lookup_connection_value (connection,
317 MHD_HEADER_KIND,
318 MHD_HTTP_HEADER_CONTENT_ENCODING);
319 if ((NULL != ce) && (0 == strcasecmp ("deflate", ce)))
320 {
321 ret = inflate_data (r);
322 if (GNUNET_JSON_PR_SUCCESS != ret)
323 {
324 buffer_deinit (r);
325 GNUNET_free (r);
326 *con_cls = NULL;
327 return ret;
328 }
329 }
330
331 {
332 json_error_t err;
333
334 *json = json_loadb (r->data,
335 r->fill,
336 0,
337 &err);
338 if (NULL == *json)
339 {
340 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
341 "Failed to parse JSON request body of %u byte at offset %d: %s\n",
342 (unsigned int) r->fill,
343 err.position,
344 err.text);
345 buffer_deinit (r);
346 GNUNET_free (r);
347 *con_cls = NULL;
348 return GNUNET_JSON_PR_JSON_INVALID;
349 }
350 }
351 buffer_deinit (r);
352 GNUNET_free (r);
353 *con_cls = NULL;
354
355 return GNUNET_JSON_PR_SUCCESS;
356}
357
358
359/**
360 * Function called whenever we are done with a request
361 * to clean up our state.
362 *
363 * @param con_cls value as it was left by
364 * #GNUNET_JSON_post_parser(), to be cleaned up
365 */
366void
367GNUNET_JSON_post_parser_cleanup (void *con_cls)
368{
369 struct Buffer *r = con_cls;
370
371 if (NULL != r)
372 {
373 buffer_deinit (r);
374 GNUNET_free (r);
375 }
376}
377
378
379/* end of mhd_json.c */
diff --git a/src/lib/json/json_pack.c b/src/lib/json/json_pack.c
new file mode 100644
index 000000000..d298e6efe
--- /dev/null
+++ b/src/lib/json/json_pack.c
@@ -0,0 +1,480 @@
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 * @file json/json_pack.c
22 * @brief functions to pack JSON objects
23 * @author Christian Grothoff
24 */
25#include "platform.h"
26#include "gnunet_json_lib.h"
27
28json_t *
29GNUNET_JSON_pack_ (struct GNUNET_JSON_PackSpec spec[])
30{
31 json_t *ret;
32
33 if (NULL == spec[0].field_name)
34 {
35 ret = spec[0].object;
36 spec[0].object = NULL;
37 return ret;
38 }
39 ret = json_object ();
40 GNUNET_assert (NULL != ret);
41 for (unsigned int i = 0;
42 NULL != spec[i].field_name;
43 i++)
44 {
45 if (NULL == spec[i].object)
46 {
47 if (! spec[i].allow_null)
48 {
49 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
50 "NULL not allowed for `%s'\n",
51 spec[i].field_name);
52 GNUNET_assert (0);
53 }
54 }
55 else
56 {
57 GNUNET_assert (0 ==
58 json_object_set_new (ret,
59 spec[i].field_name,
60 spec[i].object));
61 spec[i].object = NULL;
62 }
63 }
64 return ret;
65}
66
67
68struct GNUNET_JSON_PackSpec
69GNUNET_JSON_pack_end_ (void)
70{
71 struct GNUNET_JSON_PackSpec ps = {
72 .field_name = NULL
73 };
74
75 return ps;
76}
77
78
79struct GNUNET_JSON_PackSpec
80GNUNET_JSON_pack_allow_null (struct GNUNET_JSON_PackSpec in)
81{
82 in.allow_null = true;
83 return in;
84}
85
86
87struct GNUNET_JSON_PackSpec
88GNUNET_JSON_pack_bool (const char *name,
89 bool b)
90{
91 struct GNUNET_JSON_PackSpec ps = {
92 .field_name = name,
93 .object = json_boolean (b)
94 };
95
96 return ps;
97}
98
99
100struct GNUNET_JSON_PackSpec
101GNUNET_JSON_pack_double (const char *name,
102 double f)
103{
104 struct GNUNET_JSON_PackSpec ps = {
105 .field_name = name,
106 .object = json_real (f)
107 };
108
109 return ps;
110}
111
112
113struct GNUNET_JSON_PackSpec
114GNUNET_JSON_pack_string (const char *name,
115 const char *s)
116{
117 struct GNUNET_JSON_PackSpec ps = {
118 .field_name = name,
119 .object = json_string (s)
120 };
121
122 return ps;
123}
124
125
126struct GNUNET_JSON_PackSpec
127GNUNET_JSON_pack_uint64 (const char *name,
128 uint64_t num)
129{
130 struct GNUNET_JSON_PackSpec ps = {
131 .field_name = name,
132 .object = json_integer ((json_int_t) num)
133 };
134
135#if JSON_INTEGER_IS_LONG_LONG
136 GNUNET_assert (num <= LLONG_MAX);
137#else
138 GNUNET_assert (num <= LONG_MAX);
139#endif
140 return ps;
141}
142
143
144struct GNUNET_JSON_PackSpec
145GNUNET_JSON_pack_int64 (const char *name,
146 int64_t num)
147{
148 struct GNUNET_JSON_PackSpec ps = {
149 .field_name = name,
150 .object = json_integer ((json_int_t) num)
151 };
152
153#if JSON_INTEGER_IS_LONG_LONG
154 GNUNET_assert (num <= LLONG_MAX);
155 GNUNET_assert (num >= LLONG_MIN);
156#else
157 GNUNET_assert (num <= LONG_MAX);
158 GNUNET_assert (num >= LONG_MIN);
159#endif
160 return ps;
161}
162
163
164struct GNUNET_JSON_PackSpec
165GNUNET_JSON_pack_object_steal (const char *name,
166 json_t *o)
167{
168 struct GNUNET_JSON_PackSpec ps = {
169 .field_name = name,
170 .object = o
171 };
172
173 if (NULL == o)
174 return ps;
175 if (! json_is_object (o))
176 {
177 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
178 "Expected JSON object for field `%s'\n",
179 name);
180 GNUNET_assert (0);
181 }
182 return ps;
183}
184
185
186struct GNUNET_JSON_PackSpec
187GNUNET_JSON_pack_object_incref (const char *name,
188 json_t *o)
189{
190 struct GNUNET_JSON_PackSpec ps = {
191 .field_name = name,
192 .object = o
193 };
194
195 if (NULL == o)
196 return ps;
197 (void) json_incref (o);
198 if (! json_is_object (o))
199 {
200 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
201 "Expected JSON object for field `%s'\n",
202 name);
203 GNUNET_assert (0);
204 }
205 return ps;
206}
207
208
209struct GNUNET_JSON_PackSpec
210GNUNET_JSON_pack_array_steal (const char *name,
211 json_t *a)
212{
213 struct GNUNET_JSON_PackSpec ps = {
214 .field_name = name,
215 .object = a
216 };
217
218 if (NULL == a)
219 return ps;
220 if (! json_is_array (a))
221 {
222 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
223 "Expected JSON array for field `%s'\n",
224 name);
225 GNUNET_assert (0);
226 }
227 return ps;
228}
229
230
231struct GNUNET_JSON_PackSpec
232GNUNET_JSON_pack_array_incref (const char *name,
233 json_t *a)
234{
235 struct GNUNET_JSON_PackSpec ps = {
236 .field_name = name,
237 .object = a
238 };
239
240 if (NULL == a)
241 return ps;
242 (void) json_incref (a);
243 if (! json_is_array (a))
244 {
245 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
246 "Expected JSON array for field `%s'\n",
247 name);
248 GNUNET_assert (0);
249 }
250 return ps;
251}
252
253
254struct GNUNET_JSON_PackSpec
255GNUNET_JSON_pack_data_varsize (const char *name,
256 const void *blob,
257 size_t blob_size)
258{
259 struct GNUNET_JSON_PackSpec ps = {
260 .field_name = name,
261 .object = (NULL != blob)
262 ? GNUNET_JSON_from_data (blob,
263 blob_size)
264 : NULL
265 };
266
267 return ps;
268}
269
270
271struct GNUNET_JSON_PackSpec
272GNUNET_JSON_pack_data64_varsize (const char *name,
273 const void *blob,
274 size_t blob_size)
275{
276 struct GNUNET_JSON_PackSpec ps = {
277 .field_name = name,
278 .object = (NULL != blob)
279 ? GNUNET_JSON_from_data64 (blob,
280 blob_size)
281 : NULL
282 };
283
284 return ps;
285}
286
287
288struct GNUNET_JSON_PackSpec
289GNUNET_JSON_pack_timestamp (const char *name,
290 struct GNUNET_TIME_Timestamp t)
291{
292 struct GNUNET_JSON_PackSpec ps = {
293 .field_name = name
294 };
295
296 if (! GNUNET_TIME_absolute_is_zero (t.abs_time))
297 {
298 ps.object = GNUNET_JSON_from_timestamp (t);
299 GNUNET_assert (NULL != ps.object);
300 }
301 else
302 {
303 ps.object = NULL;
304 }
305 return ps;
306}
307
308
309struct GNUNET_JSON_PackSpec
310GNUNET_JSON_pack_timestamp_nbo (const char *name,
311 struct GNUNET_TIME_TimestampNBO at)
312{
313 return GNUNET_JSON_pack_timestamp (name,
314 GNUNET_TIME_timestamp_ntoh (at));
315}
316
317
318struct GNUNET_JSON_PackSpec
319GNUNET_JSON_pack_time_rel (const char *name,
320 struct GNUNET_TIME_Relative rt)
321{
322 json_t *json;
323
324 json = GNUNET_JSON_from_time_rel (rt);
325 GNUNET_assert (NULL != json);
326 return GNUNET_JSON_pack_object_steal (name,
327 json);
328}
329
330
331struct GNUNET_JSON_PackSpec
332GNUNET_JSON_pack_time_rel_nbo (const char *name,
333 struct GNUNET_TIME_RelativeNBO rt)
334{
335 return GNUNET_JSON_pack_time_rel (name,
336 GNUNET_TIME_relative_ntoh (rt));
337}
338
339
340struct GNUNET_JSON_PackSpec
341GNUNET_JSON_pack_rsa_public_key (const char *name,
342 const struct GNUNET_CRYPTO_RsaPublicKey *pk)
343{
344 struct GNUNET_JSON_PackSpec ps = {
345 .field_name = name,
346 .object = GNUNET_JSON_from_rsa_public_key (pk)
347 };
348
349 return ps;
350}
351
352
353struct GNUNET_JSON_PackSpec
354GNUNET_JSON_pack_rsa_signature (const char *name,
355 const struct GNUNET_CRYPTO_RsaSignature *sig)
356{
357 struct GNUNET_JSON_PackSpec ps = {
358 .field_name = name,
359 .object = GNUNET_JSON_from_rsa_signature (sig)
360 };
361
362 return ps;
363}
364
365
366struct GNUNET_JSON_PackSpec
367GNUNET_JSON_pack_unblinded_signature (const char *name,
368 const struct GNUNET_CRYPTO_UnblindedSignature *sig)
369{
370 struct GNUNET_JSON_PackSpec ps = {
371 .field_name = name
372 };
373
374 if (NULL == sig)
375 return ps;
376
377 switch (sig->cipher)
378 {
379 case GNUNET_CRYPTO_BSA_INVALID:
380 break;
381 case GNUNET_CRYPTO_BSA_RSA:
382 ps.object = GNUNET_JSON_PACK (
383 GNUNET_JSON_pack_string ("cipher",
384 "RSA"),
385 GNUNET_JSON_pack_rsa_signature ("rsa_signature",
386 sig->details.rsa_signature));
387 return ps;
388 case GNUNET_CRYPTO_BSA_CS:
389 ps.object = GNUNET_JSON_PACK (
390 GNUNET_JSON_pack_string ("cipher",
391 "CS"),
392 GNUNET_JSON_pack_data_auto ("cs_signature_r",
393 &sig->details.cs_signature.r_point),
394 GNUNET_JSON_pack_data_auto ("cs_signature_s",
395 &sig->details.cs_signature.s_scalar));
396 return ps;
397 }
398 GNUNET_assert (0);
399 return ps;
400}
401
402
403struct GNUNET_JSON_PackSpec
404GNUNET_JSON_pack_blinded_message (const char *name,
405 const struct GNUNET_CRYPTO_BlindedMessage *msg)
406{
407 struct GNUNET_JSON_PackSpec ps = {
408 .field_name = name,
409 };
410
411 switch (msg->cipher)
412 {
413 case GNUNET_CRYPTO_BSA_INVALID:
414 break;
415 case GNUNET_CRYPTO_BSA_RSA:
416 ps.object = GNUNET_JSON_PACK (
417 GNUNET_JSON_pack_string ("cipher",
418 "RSA"),
419 GNUNET_JSON_pack_data_varsize (
420 "rsa_blinded_planchet",
421 msg->details.rsa_blinded_message.blinded_msg,
422 msg->details.rsa_blinded_message.blinded_msg_size));
423 return ps;
424 case GNUNET_CRYPTO_BSA_CS:
425 ps.object = GNUNET_JSON_PACK (
426 GNUNET_JSON_pack_string ("cipher",
427 "CS"),
428 GNUNET_JSON_pack_data_auto (
429 "cs_nonce",
430 &msg->details.cs_blinded_message.nonce),
431 GNUNET_JSON_pack_data_auto (
432 "cs_blinded_c0",
433 &msg->details.cs_blinded_message.c[0]),
434 GNUNET_JSON_pack_data_auto (
435 "cs_blinded_c1",
436 &msg->details.cs_blinded_message.c[1]));
437 return ps;
438 }
439 GNUNET_assert (0);
440 return ps;
441}
442
443
444struct GNUNET_JSON_PackSpec
445GNUNET_JSON_pack_blinded_sig (
446 const char *name,
447 const struct GNUNET_CRYPTO_BlindedSignature *sig)
448{
449 struct GNUNET_JSON_PackSpec ps = {
450 .field_name = name,
451 };
452
453 if (NULL == sig)
454 return ps;
455 switch (sig->cipher)
456 {
457 case GNUNET_CRYPTO_BSA_INVALID:
458 break;
459 case GNUNET_CRYPTO_BSA_RSA:
460 ps.object = GNUNET_JSON_PACK (
461 GNUNET_JSON_pack_string ("cipher",
462 "RSA"),
463 GNUNET_JSON_pack_rsa_signature ("blinded_rsa_signature",
464 sig->details.blinded_rsa_signature));
465 return ps;
466 case GNUNET_CRYPTO_BSA_CS:
467 ps.object = GNUNET_JSON_PACK (
468 GNUNET_JSON_pack_string ("cipher",
469 "CS"),
470 GNUNET_JSON_pack_uint64 ("b",
471 sig->details.blinded_cs_answer.b),
472 GNUNET_JSON_pack_data_auto ("s",
473 &sig->details.blinded_cs_answer.s_scalar));
474 return ps;
475 }
476 GNUNET_assert (0);
477 return ps;
478}
479
480/* end of json_pack.c */
diff --git a/src/lib/json/meson.build b/src/lib/json/meson.build
new file mode 100644
index 000000000..d4c3b4528
--- /dev/null
+++ b/src/lib/json/meson.build
@@ -0,0 +1,44 @@
1libgnunetjson_src = ['json.c',
2 'json_generator.c',
3 'json_helper.c',
4 'json_mhd.c',
5 'json_pack.c']
6
7libgnunetjson = library('gnunetjson',
8 libgnunetjson_src,
9 soversion: '0',
10 version: '0.0.0',
11 dependencies: [libgnunetutil_dep, json_dep, mhd_dep, zlib_dep],
12 include_directories: [incdir, configuration_inc],
13 install: true,
14 install_dir: get_option('libdir'))
15libgnunetjson_dep = declare_dependency(link_with : libgnunetjson)
16pkg.generate(libgnunetjson, url: 'https://www.gnunet.org',
17 description : 'Library for JSON de/serialization')
18
19testjson = executable ('test_json',
20 ['test_json.c'],
21 dependencies: [libgnunetutil_dep,
22 json_dep,
23 libgnunetjson_dep],
24 include_directories: [incdir, configuration_inc],
25 build_by_default: false,
26 install: false)
27testjson_mhd = executable ('test_json_mhd',
28 ['test_json_mhd.c'],
29 dependencies: [libgnunetutil_dep,
30 json_dep,
31 mhd_dep,
32 curl_dep,
33 zlib_dep,
34 libgnunetjson_dep],
35 include_directories: [incdir, configuration_inc],
36 build_by_default: false,
37 install: false)
38test('test_json', testjson,
39 workdir: meson.current_build_dir(),
40 suite: ['json'])
41test('test_json_mhd', testjson_mhd,
42 workdir: meson.current_build_dir(),
43 suite: ['json'])
44
diff --git a/src/lib/json/test_json.c b/src/lib/json/test_json.c
new file mode 100644
index 000000000..1d27518b2
--- /dev/null
+++ b/src/lib/json/test_json.c
@@ -0,0 +1,247 @@
1/*
2 This file is part of GNUnet
3 (C) 2015, 2016, 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 json/test_json.c
23 * @brief Tests for JSON conversion functions
24 * @author Christian Grothoff <christian@grothoff.org>
25 */
26#include "platform.h"
27#include "gnunet_util_lib.h"
28#include "gnunet_json_lib.h"
29
30
31/**
32 * Test absolute time conversion from/to JSON.
33 *
34 * @return 0 on success
35 */
36static int
37test_timestamp (void)
38{
39 json_t *j;
40 struct GNUNET_TIME_Absolute a1;
41 struct GNUNET_TIME_Timestamp t1;
42 struct GNUNET_TIME_Timestamp t2;
43 struct GNUNET_JSON_Specification s1[] = {
44 GNUNET_JSON_spec_timestamp (NULL,
45 &t2),
46 GNUNET_JSON_spec_end ()
47 };
48 struct GNUNET_JSON_Specification s2[] = {
49 GNUNET_JSON_spec_timestamp (NULL,
50 &t2),
51 GNUNET_JSON_spec_end ()
52 };
53
54 a1 = GNUNET_TIME_absolute_get ();
55 t1 = GNUNET_TIME_absolute_to_timestamp (a1);
56 j = GNUNET_JSON_from_timestamp (t1);
57 GNUNET_assert (NULL != j);
58 GNUNET_assert (GNUNET_OK ==
59 GNUNET_JSON_parse (j,
60 s1,
61 NULL,
62 NULL));
63 GNUNET_assert (GNUNET_TIME_timestamp_cmp (t1, ==, t2));
64 json_decref (j);
65
66 a1 = GNUNET_TIME_UNIT_FOREVER_ABS;
67 j = GNUNET_JSON_from_timestamp (t1);
68 GNUNET_assert (NULL != j);
69 GNUNET_assert (GNUNET_OK ==
70 GNUNET_JSON_parse (j,
71 s2,
72 NULL,
73 NULL));
74 GNUNET_assert (GNUNET_TIME_timestamp_cmp (t1, ==, t2));
75 json_decref (j);
76 return 0;
77}
78
79
80/**
81 * Test relative time conversion from/to JSON.
82 *
83 * @return 0 on success
84 */
85static int
86test_rel_time (void)
87{
88 json_t *j;
89 struct GNUNET_TIME_Relative r1;
90 struct GNUNET_TIME_Relative r2;
91 struct GNUNET_JSON_Specification s1[] = {
92 GNUNET_JSON_spec_relative_time (NULL,
93 &r2),
94 GNUNET_JSON_spec_end ()
95 };
96 struct GNUNET_JSON_Specification s2[] = {
97 GNUNET_JSON_spec_relative_time (NULL,
98 &r2),
99 GNUNET_JSON_spec_end ()
100 };
101
102 r1 = GNUNET_TIME_UNIT_SECONDS;
103 j = GNUNET_JSON_from_time_rel (r1);
104 GNUNET_assert (NULL != j);
105 GNUNET_assert (GNUNET_OK == GNUNET_JSON_parse (j, s1, NULL, NULL));
106 GNUNET_assert (r1.rel_value_us == r2.rel_value_us);
107 json_decref (j);
108
109 r1 = GNUNET_TIME_UNIT_FOREVER_REL;
110 j = GNUNET_JSON_from_time_rel (r1);
111 GNUNET_assert (NULL != j);
112 GNUNET_assert (GNUNET_OK == GNUNET_JSON_parse (j, s2, NULL, NULL));
113 GNUNET_assert (r1.rel_value_us == r2.rel_value_us);
114 json_decref (j);
115 return 0;
116}
117
118
119/**
120 * Test raw (binary) conversion from/to JSON.
121 *
122 * @return 0 on success
123 */
124static int
125test_raw ()
126{
127 char blob[256];
128 unsigned int i;
129 json_t *j;
130
131 for (i = 0; i <= 256; i++)
132 {
133 char blob2[256];
134 struct GNUNET_JSON_Specification spec[] = { GNUNET_JSON_spec_fixed (NULL,
135 blob2,
136 i),
137 GNUNET_JSON_spec_end () };
138
139 memset (blob, i, i);
140 j = GNUNET_JSON_from_data (blob, i);
141 GNUNET_assert (NULL != j);
142 GNUNET_assert (GNUNET_OK == GNUNET_JSON_parse (j, spec, NULL, NULL));
143 GNUNET_assert (0 == memcmp (blob, blob2, i));
144 json_decref (j);
145 }
146 return 0;
147}
148
149
150/**
151 * Test rsa conversions from/to JSON.
152 *
153 * @return 0 on success
154 */
155static int
156test_rsa ()
157{
158 struct GNUNET_CRYPTO_RsaPublicKey *pub;
159 struct GNUNET_CRYPTO_RsaPublicKey *pub2;
160 struct GNUNET_JSON_Specification pspec[] =
161 { GNUNET_JSON_spec_rsa_public_key (NULL, &pub2), GNUNET_JSON_spec_end () };
162 struct GNUNET_CRYPTO_RsaSignature *sig;
163 struct GNUNET_CRYPTO_RsaSignature *sig2;
164 struct GNUNET_JSON_Specification sspec[] =
165 { GNUNET_JSON_spec_rsa_signature (NULL, &sig2), GNUNET_JSON_spec_end () };
166 struct GNUNET_CRYPTO_RsaPrivateKey *priv;
167 struct GNUNET_HashCode msg;
168 json_t *jp;
169 json_t *js;
170
171 priv = GNUNET_CRYPTO_rsa_private_key_create (1024);
172 pub = GNUNET_CRYPTO_rsa_private_key_get_public (priv);
173 memset (&msg, 42, sizeof(msg));
174 sig = GNUNET_CRYPTO_rsa_sign_fdh (priv, &msg, sizeof (msg));
175 GNUNET_assert (NULL != (jp = GNUNET_JSON_from_rsa_public_key (pub)));
176 GNUNET_assert (NULL != (js = GNUNET_JSON_from_rsa_signature (sig)));
177 GNUNET_assert (GNUNET_OK == GNUNET_JSON_parse (jp, pspec, NULL, NULL));
178 GNUNET_assert (GNUNET_OK == GNUNET_JSON_parse (js, sspec, NULL, NULL));
179 GNUNET_break (0 == GNUNET_CRYPTO_rsa_signature_cmp (sig, sig2));
180 GNUNET_break (0 == GNUNET_CRYPTO_rsa_public_key_cmp (pub, pub2));
181 json_decref (jp);
182 json_decref (js);
183 GNUNET_CRYPTO_rsa_signature_free (sig);
184 GNUNET_CRYPTO_rsa_signature_free (sig2);
185 GNUNET_CRYPTO_rsa_private_key_free (priv);
186 GNUNET_CRYPTO_rsa_public_key_free (pub);
187 GNUNET_CRYPTO_rsa_public_key_free (pub2);
188 return 0;
189}
190
191
192/**
193 * Test rsa conversions from/to JSON.
194 *
195 * @return 0 on success
196 */
197static int
198test_boolean ()
199{
200 int b1;
201 int b2;
202 json_t *json;
203 struct GNUNET_JSON_Specification pspec[] = { GNUNET_JSON_spec_boolean ("b1",
204 &b1),
205 GNUNET_JSON_spec_boolean ("b2",
206 &b2),
207 GNUNET_JSON_spec_end () };
208
209 json = json_object ();
210 json_object_set_new (json, "b1", json_true ());
211 json_object_set_new (json, "b2", json_false ());
212
213 GNUNET_assert (GNUNET_OK == GNUNET_JSON_parse (json, pspec, NULL, NULL));
214
215 GNUNET_assert (GNUNET_YES == b1);
216 GNUNET_assert (GNUNET_NO == b2);
217
218 json_object_set_new (json, "b1", json_integer (42));
219
220 GNUNET_assert (GNUNET_OK != GNUNET_JSON_parse (json, pspec, NULL, NULL));
221
222 json_decref (json);
223
224 return 0;
225}
226
227
228int
229main (int argc, const char *const argv[])
230{
231 GNUNET_log_setup ("test-json", "WARNING", NULL);
232 if (0 != test_timestamp ())
233 return 1;
234 if (0 != test_rel_time ())
235 return 1;
236 if (0 != test_raw ())
237 return 1;
238 if (0 != test_rsa ())
239 return 1;
240 if (0 != test_boolean ())
241 return 1;
242 /* FIXME: test EdDSA signature conversion... */
243 return 0;
244}
245
246
247/* end of test_json.c */
diff --git a/src/lib/json/test_json_mhd.c b/src/lib/json/test_json_mhd.c
new file mode 100644
index 000000000..642715f25
--- /dev/null
+++ b/src/lib/json/test_json_mhd.c
@@ -0,0 +1,193 @@
1/*
2 This file is part of GNUnet
3 (C) 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 json/test_json_mhd.c
23 * @brief Tests for JSON MHD integration functions
24 * @author Christian Grothoff <christian@grothoff.org>
25 */
26#include "platform.h"
27#include "gnunet_util_lib.h"
28#include "gnunet_json_lib.h"
29#include "gnunet_curl_lib.h"
30#include "gnunet_mhd_compat.h"
31#include <zlib.h>
32
33#define MAX_SIZE 1024 * 1024
34
35static json_t *bigj;
36
37static int global_ret;
38
39
40static MHD_RESULT
41access_handler_cb (void *cls,
42 struct MHD_Connection *connection,
43 const char *url,
44 const char *method,
45 const char *version,
46 const char *upload_data,
47 size_t *upload_data_size,
48 void **con_cls)
49{
50 int ret;
51 json_t *json;
52 struct MHD_Response *resp;
53
54 json = NULL;
55 ret = GNUNET_JSON_post_parser (MAX_SIZE,
56 connection,
57 con_cls,
58 upload_data,
59 upload_data_size,
60 &json);
61 switch (ret)
62 {
63 case GNUNET_JSON_PR_SUCCESS:
64 if (json_equal (bigj, json))
65 {
66 global_ret = 0;
67 }
68 else
69 {
70 GNUNET_break (0);
71 global_ret = 6;
72 }
73 json_decref (json);
74 resp = MHD_create_response_from_buffer (3, "OK\n", MHD_RESPMEM_PERSISTENT);
75 ret = MHD_queue_response (connection, MHD_HTTP_OK, resp);
76 MHD_destroy_response (resp);
77 return ret;
78
79 case GNUNET_JSON_PR_CONTINUE:
80 return MHD_YES;
81
82 case GNUNET_JSON_PR_OUT_OF_MEMORY:
83 GNUNET_break (0);
84 global_ret = 3;
85 break;
86
87 case GNUNET_JSON_PR_REQUEST_TOO_LARGE:
88 GNUNET_break (0);
89 global_ret = 4;
90 break;
91
92 case GNUNET_JSON_PR_JSON_INVALID:
93 GNUNET_break (0);
94 global_ret = 5;
95 break;
96 }
97 GNUNET_break (0);
98 return MHD_NO;
99}
100
101
102int
103main (int argc, const char *const argv[])
104{
105 struct MHD_Daemon *daemon;
106 uint16_t port;
107 CURL *easy;
108 char *url;
109 char *str;
110 size_t slen;
111 long post_data_size;
112 void *post_data;
113 uLongf dlen;
114 struct curl_slist *json_header;
115
116 GNUNET_log_setup ("test-json-mhd", "WARNING", NULL);
117 global_ret = 2;
118 daemon = MHD_start_daemon (MHD_USE_DUAL_STACK | MHD_USE_AUTO_INTERNAL_THREAD,
119 0,
120 NULL,
121 NULL,
122 &access_handler_cb,
123 NULL,
124 MHD_OPTION_END);
125 if (NULL == daemon)
126 return 77;
127 bigj = json_object ();
128 json_object_set_new (bigj, "test", json_string ("value"));
129 for (unsigned int i = 0; i < 1000; i++)
130 {
131 char tmp[5];
132
133 GNUNET_snprintf (tmp, sizeof(tmp), "%u", i);
134 json_object_set_new (bigj, tmp, json_string (tmp));
135 }
136 str = json_dumps (bigj, JSON_INDENT (2));
137 slen = strlen (str);
138
139#ifdef compressBound
140 dlen = compressBound (slen);
141#else
142 dlen = slen + slen / 100 + 20;
143 /* documentation says 100.1% oldSize + 12 bytes, but we
144 * should be able to overshoot by more to be safe */
145#endif
146 post_data = GNUNET_malloc (dlen);
147 if (Z_OK !=
148 compress2 ((Bytef *) post_data, &dlen, (const Bytef *) str, slen, 9))
149 {
150 GNUNET_break (0);
151 MHD_stop_daemon (daemon);
152 json_decref (bigj);
153 GNUNET_free (post_data);
154 GNUNET_free (str);
155 return 1;
156 }
157 post_data_size = (long) dlen;
158 port = MHD_get_daemon_info (daemon, MHD_DAEMON_INFO_BIND_PORT)->port;
159 easy = curl_easy_init ();
160 GNUNET_asprintf (&url, "http://localhost:%u/", (unsigned int) port);
161 curl_easy_setopt (easy, CURLOPT_VERBOSE, 0);
162 curl_easy_setopt (easy, CURLOPT_URL, url);
163 curl_easy_setopt (easy, CURLOPT_POST, 1);
164 curl_easy_setopt (easy, CURLOPT_POSTFIELDS, post_data);
165 curl_easy_setopt (easy, CURLOPT_POSTFIELDSIZE, post_data_size);
166
167 json_header = curl_slist_append (NULL, "Content-Type: application/json");
168 json_header = curl_slist_append (json_header, "Content-Encoding: deflate");
169 curl_easy_setopt (easy, CURLOPT_HTTPHEADER, json_header);
170 if (0 != curl_easy_perform (easy))
171 {
172 GNUNET_break (0);
173 MHD_stop_daemon (daemon);
174 GNUNET_free (url);
175 json_decref (bigj);
176 GNUNET_free (post_data);
177 GNUNET_free (str);
178 curl_slist_free_all (json_header);
179 curl_easy_cleanup (easy);
180 return 1;
181 }
182 MHD_stop_daemon (daemon);
183 GNUNET_free (url);
184 json_decref (bigj);
185 GNUNET_free (post_data);
186 GNUNET_free (str);
187 curl_slist_free_all (json_header);
188 curl_easy_cleanup (easy);
189 return global_ret;
190}
191
192
193/* end of test_json_mhd.c */
diff --git a/src/lib/meson.build b/src/lib/meson.build
new file mode 100644
index 000000000..93dbf5c4d
--- /dev/null
+++ b/src/lib/meson.build
@@ -0,0 +1,9 @@
1subdir('util')
2subdir('hello')
3subdir('block')
4subdir('json', if_found : json_dep)
5subdir('curl', if_found : [curl_dep])
6subdir('sq', if_found : [sqlite_dep])
7subdir('pq', if_found : [pq_dep])
8subdir('gnsrecord')
9subdir('testing')
diff --git a/src/lib/pq/.gitignore b/src/lib/pq/.gitignore
new file mode 100644
index 000000000..8de68ddc9
--- /dev/null
+++ b/src/lib/pq/.gitignore
@@ -0,0 +1 @@
test_pq
diff --git a/src/lib/pq/Makefile.am b/src/lib/pq/Makefile.am
new file mode 100644
index 000000000..91014dbfe
--- /dev/null
+++ b/src/lib/pq/Makefile.am
@@ -0,0 +1,51 @@
1# This Makefile.am is in the public domain
2AM_CPPFLAGS = -I$(top_srcdir)/src/include $(POSTGRESQL_CPPFLAGS)
3
4if USE_COVERAGE
5 AM_CFLAGS = --coverage
6endif
7
8sqldir = $(prefix)/share/gnunet/sql/
9
10sql_DATA = \
11 versioning.sql
12
13EXTRA_DIST = \
14 $(sql_DATA)
15
16if HAVE_POSTGRESQL
17lib_LTLIBRARIES = libgnunetpq.la
18endif
19
20libgnunetpq_la_SOURCES = \
21 pq.c \
22 pq.h \
23 pq_connect.c \
24 pq_eval.c \
25 pq_event.c \
26 pq_exec.c \
27 pq_prepare.c \
28 pq_query_helper.c \
29 pq_result_helper.c
30libgnunetpq_la_LIBADD = -lpq \
31 $(top_builddir)/src/lib/util/libgnunetutil.la
32libgnunetpq_la_LDFLAGS = \
33 $(POSTGRESQL_LDFLAGS) \
34 $(GN_LIB_LDFLAGS) \
35 -version-info 7:0:2
36
37if ENABLE_TEST_RUN
38AM_TESTS_ENVIRONMENT=export GNUNET_PREFIX=$${GNUNET_PREFIX:-@libdir@};export PATH=$${GNUNET_PREFIX:-@prefix@}/bin:$$PATH;unset XDG_DATA_HOME;unset XDG_CONFIG_HOME;
39TESTS = \
40 test_pq
41endif
42
43check_PROGRAMS= \
44 test_pq
45
46test_pq_SOURCES = \
47 test_pq.c
48test_pq_LDADD = \
49 libgnunetpq.la \
50 $(top_builddir)/src/lib/util/libgnunetutil.la \
51 -lpq $(XLIB)
diff --git a/src/lib/pq/meson.build b/src/lib/pq/meson.build
new file mode 100644
index 000000000..ea73f45a1
--- /dev/null
+++ b/src/lib/pq/meson.build
@@ -0,0 +1,32 @@
1libgnunetpq_src = ['pq.c',
2 'pq_connect.c',
3 'pq_eval.c',
4 'pq_event.c',
5 'pq_exec.c',
6 'pq_prepare.c',
7 'pq_query_helper.c',
8 'pq_result_helper.c']
9
10libgnunetpq = library('gnunetpq',
11 libgnunetpq_src,
12 soversion: '5',
13 version: '5.0.0',
14 dependencies: [libgnunetutil_dep, pq_dep],
15 include_directories: [incdir, configuration_inc],
16 install: true,
17 install_dir: get_option('libdir'))
18libgnunetpq_dep = declare_dependency(link_with : libgnunetpq)
19
20testpq = executable ('test_pq',
21 ['test_pq.c'],
22 dependencies: [libgnunetutil_dep,
23 pq_dep,
24 libgnunetpq_dep],
25 include_directories: [incdir, configuration_inc],
26 build_by_default: false,
27 install: false)
28test('test_pq', testpq,
29 workdir: meson.current_build_dir(),
30 suite: ['pq'])
31
32
diff --git a/src/lib/pq/pq.c b/src/lib/pq/pq.c
new file mode 100644
index 000000000..65e051bfc
--- /dev/null
+++ b/src/lib/pq/pq.c
@@ -0,0 +1,207 @@
1/*
2 This file is part of GNUnet
3 Copyright (C) 2014, 2015, 2016, 2017, 2019, 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 * @file pq/pq.c
22 * @brief helper functions for libpq (PostGres) interactions
23 * @author Sree Harsha Totakura <sreeharsha@totakura.in>
24 * @author Florian Dold
25 * @author Christian Grothoff
26 */
27#include "platform.h"
28#include "gnunet_pq_lib.h"
29#include "pq.h"
30
31
32PGresult *
33GNUNET_PQ_exec_prepared (struct GNUNET_PQ_Context *db,
34 const char *name,
35 const struct GNUNET_PQ_QueryParam *params)
36{
37 unsigned int len;
38
39 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
40 "Running prepared statement `%s' on %p\n",
41 name,
42 db);
43 /* count the number of parameters */
44 len = 0;
45 for (unsigned int i = 0; 0 != params[i].num_params; i++)
46 len += params[i].num_params;
47
48 /* new scope to allow stack allocation without alloca */
49 {
50 /* Scratch buffer for temporary storage */
51 void *scratch[GNUNET_NZL (len)];
52 /* Parameter array we are building for the query */
53 void *param_values[GNUNET_NZL (len)];
54 int param_lengths[GNUNET_NZL (len)];
55 int param_formats[GNUNET_NZL (len)];
56 unsigned int off;
57 /* How many entries in the scratch buffer are in use? */
58 unsigned int soff;
59 PGresult *res;
60 int ret;
61 ConnStatusType status;
62
63 off = 0;
64 soff = 0;
65 for (unsigned int i = 0; 0 != params[i].num_params; i++)
66 {
67 const struct GNUNET_PQ_QueryParam *x = &params[i];
68
69 ret = x->conv (x->conv_cls,
70 x->data,
71 x->size,
72 &param_values[off],
73 &param_lengths[off],
74 &param_formats[off],
75 x->num_params,
76 &scratch[soff],
77 len - soff);
78 if (ret < 0)
79 {
80 for (off = 0; off < soff; off++)
81 GNUNET_free (scratch[off]);
82 return NULL;
83 }
84 soff += ret;
85 off += x->num_params;
86 }
87 GNUNET_assert (off == len);
88 GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG,
89 "pq",
90 "Executing prepared SQL statement `%s'\n",
91 name);
92 res = PQexecPrepared (db->conn,
93 name,
94 len,
95 (const char **) param_values,
96 param_lengths,
97 param_formats,
98 1);
99 GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG,
100 "pq",
101 "Execution of prepared SQL statement `%s' finished (%s)\n",
102 name,
103 PQresStatus (PQresultStatus (res)));
104 if ( (PGRES_COMMAND_OK != PQresultStatus (res)) &&
105 (CONNECTION_OK != (status = PQstatus (db->conn))) )
106 {
107 GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG,
108 "pq",
109 "Database disconnected on SQL statement `%s' (reconnecting)\n",
110 name);
111 GNUNET_PQ_reconnect (db);
112 res = NULL;
113 }
114
115 for (off = 0; off < soff; off++)
116 GNUNET_free (scratch[off]);
117 return res;
118 }
119}
120
121
122void
123GNUNET_PQ_cleanup_query_params_closures (
124 const struct GNUNET_PQ_QueryParam *params)
125{
126 for (unsigned int i = 0; 0 != params[i].num_params; i++)
127 {
128 const struct GNUNET_PQ_QueryParam *x = &params[i];
129
130 if ((NULL != x->conv_cls) &&
131 (NULL != x->conv_cls_cleanup))
132 x->conv_cls_cleanup (x->conv_cls);
133 }
134
135}
136
137
138void
139GNUNET_PQ_cleanup_result (struct GNUNET_PQ_ResultSpec *rs)
140{
141 for (unsigned int i = 0; NULL != rs[i].conv; i++)
142 if (NULL != rs[i].cleaner)
143 rs[i].cleaner (rs[i].cls,
144 rs[i].dst);
145}
146
147
148enum GNUNET_GenericReturnValue
149GNUNET_PQ_extract_result (PGresult *result,
150 struct GNUNET_PQ_ResultSpec *rs,
151 int row)
152{
153 unsigned int i;
154
155 if (NULL == result)
156 return GNUNET_SYSERR;
157 for (i = 0; NULL != rs[i].conv; i++)
158 {
159 struct GNUNET_PQ_ResultSpec *spec;
160 enum GNUNET_GenericReturnValue ret;
161
162 spec = &rs[i];
163 ret = spec->conv (spec->cls,
164 result,
165 row,
166 spec->fname,
167 &spec->dst_size,
168 spec->dst);
169 switch (ret)
170 {
171 case GNUNET_OK:
172 /* canonical case, continue below */
173 if (NULL != spec->is_null)
174 *spec->is_null = false;
175 break;
176 case GNUNET_NO:
177 if (spec->is_nullable)
178 {
179 if (NULL != spec->is_null)
180 *spec->is_null = true;
181 continue;
182 }
183 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
184 "NULL field encountered for `%s' where non-NULL was required\n",
185 spec->fname);
186 goto cleanup;
187 case GNUNET_SYSERR:
188 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
189 "Failed to extract field `%s'\n",
190 spec->fname);
191 GNUNET_break (0);
192 goto cleanup;
193 }
194 if (NULL != spec->result_size)
195 *spec->result_size = spec->dst_size;
196 }
197 return GNUNET_OK;
198cleanup:
199 for (unsigned int j = 0; j < i; j++)
200 if (NULL != rs[j].cleaner)
201 rs[j].cleaner (rs[j].cls,
202 rs[j].dst);
203 return GNUNET_SYSERR;
204}
205
206
207/* end of pq/pq.c */
diff --git a/src/lib/pq/pq.h b/src/lib/pq/pq.h
new file mode 100644
index 000000000..65b72cedd
--- /dev/null
+++ b/src/lib/pq/pq.h
@@ -0,0 +1,172 @@
1/*
2 This file is part of GNUnet
3 Copyright (C) 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 * @file pq/pq.h
22 * @brief shared internal data structures of libgnunetpq
23 * @author Christian Grothoff
24 */
25#ifndef PQ_H
26#define PQ_H
27
28#include "gnunet_util_lib.h"
29#include "gnunet_pq_lib.h"
30
31
32/**
33 * Handle to Postgres database.
34 */
35struct GNUNET_PQ_Context
36{
37 /**
38 * Actual connection.
39 */
40 PGconn *conn;
41
42 /**
43 * Statements to execute upon connection.
44 */
45 struct GNUNET_PQ_ExecuteStatement *es;
46
47 /**
48 * Prepared statements.
49 */
50 struct GNUNET_PQ_PreparedStatement *ps;
51
52 /**
53 * Length of the @e ps array.
54 */
55 unsigned int ps_len;
56
57 /**
58 * Last used offset in the @e ps array.
59 */
60 unsigned int ps_off;
61
62 /**
63 * Configuration to use to connect to the DB.
64 */
65 char *config_str;
66
67 /**
68 * Path to load SQL files from.
69 */
70 char *load_path;
71
72 /**
73 * Suffix to append to path to load on startup.
74 */
75 char *auto_suffix;
76
77 /**
78 * Map managing event subscriptions.
79 */
80 struct GNUNET_CONTAINER_MultiShortmap *channel_map;
81
82 /**
83 * Task responsible for processing events.
84 */
85 struct GNUNET_SCHEDULER_Task *event_task;
86
87 /**
88 * File descriptor wrapper for @e event_task.
89 */
90 struct GNUNET_NETWORK_Handle *rfd;
91
92 /**
93 * How fast should we resubscribe again?
94 */
95 struct GNUNET_TIME_Relative resubscribe_backoff;
96
97 /**
98 * Flags controlling the connection.
99 */
100 enum GNUNET_PQ_Options flags;
101
102 /**
103 * Mapping between array types and Oid's, pre-filled at reconnect.
104 * More entries are captured in via GNUNET_PQ_get_oid_by_name.
105 */
106 struct
107 {
108 /* Allocated number of elements array the table */
109 unsigned int cap;
110
111 /* Number of entries in the table */
112 unsigned int num;
113
114 /* The table of (name, oid) pairs.
115 * Note that the names are 'const char *' and the pointers should be point
116 * to the same string throughout the lifetime of the program.*/
117 struct name2oid
118 {
119 const char *name;
120 Oid oid;
121 } *table;
122
123 } oids;
124};
125
126
127/**
128 * Internal types that are supported as array types.
129 */
130
131enum array_types
132{
133 array_of_bool,
134 array_of_uint16,
135 array_of_uint32,
136 array_of_uint64,
137 array_of_byte, /* buffers of (char *), (void *), ... */
138 array_of_string, /* NULL-terminated (char *) */
139 array_of_abs_time,
140 array_of_rel_time,
141 array_of_timestamp,
142 array_of_MAX, /* must be last */
143};
144
145/**
146 * the header for a postgresql array in binary format. note that this a
147 * simplified special case of the general structure (which contains pointers),
148 * as we only support one-dimensional arrays.
149 */
150struct pq_array_header
151{
152 uint32_t ndim; /* number of dimensions. we only support ndim = 1 */
153 uint32_t has_null;
154 uint32_t oid;
155 uint32_t dim; /* size of the array */
156 uint32_t lbound; /* index value of first element in the db (default: 1). */
157} __attribute__((packed));
158
159
160/**
161 * Internal API. Reconnect should re-register notifications
162 * after a disconnect.
163 *
164 * @param db the DB handle
165 * @param fd socket to listen on
166 */
167void
168GNUNET_PQ_event_reconnect_ (struct GNUNET_PQ_Context *db,
169 int fd);
170
171
172#endif
diff --git a/src/lib/pq/pq_connect.c b/src/lib/pq/pq_connect.c
new file mode 100644
index 000000000..4ad88ef53
--- /dev/null
+++ b/src/lib/pq/pq_connect.c
@@ -0,0 +1,705 @@
1/*
2 This file is part of GNUnet
3 Copyright (C) 2017, 2019, 2020, 2021, 2023 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 pq/pq_connect.c
22 * @brief functions to connect to libpq (PostGres)
23 * @author Christian Grothoff
24 * @author Özgür Kesim
25 */
26#include "platform.h"
27#include "pq.h"
28#include <pthread.h>
29
30
31/**
32 * Function called by libpq whenever it wants to log something.
33 * We already log whenever we care, so this function does nothing
34 * and merely exists to silence the libpq logging.
35 *
36 * @param arg the SQL connection that was used
37 * @param res information about some libpq event
38 */
39static void
40pq_notice_receiver_cb (void *arg,
41 const PGresult *res)
42{
43 /* do nothing, intentionally */
44 (void) arg;
45 (void) res;
46}
47
48
49/**
50 * Function called by libpq whenever it wants to log something.
51 * We log those using the GNUnet logger.
52 *
53 * @param arg the SQL connection that was used
54 * @param message information about some libpq event
55 */
56static void
57pq_notice_processor_cb (void *arg,
58 const char *message)
59{
60 (void) arg;
61 GNUNET_log_from (GNUNET_ERROR_TYPE_INFO,
62 "pq",
63 "%s",
64 message);
65}
66
67
68struct GNUNET_PQ_Context *
69GNUNET_PQ_connect (const char *config_str,
70 const char *load_path,
71 const struct GNUNET_PQ_ExecuteStatement *es,
72 const struct GNUNET_PQ_PreparedStatement *ps)
73{
74 return GNUNET_PQ_connect2 (config_str,
75 load_path,
76 NULL == load_path
77 ? NULL
78 : "",
79 es,
80 ps,
81 GNUNET_PQ_FLAG_NONE);
82}
83
84
85struct GNUNET_PQ_Context *
86GNUNET_PQ_connect2 (const char *config_str,
87 const char *load_path,
88 const char *auto_suffix,
89 const struct GNUNET_PQ_ExecuteStatement *es,
90 const struct GNUNET_PQ_PreparedStatement *ps,
91 enum GNUNET_PQ_Options flags)
92{
93 struct GNUNET_PQ_Context *db;
94 unsigned int elen = 0;
95 unsigned int plen = 0;
96
97 if (NULL != es)
98 while (NULL != es[elen].sql)
99 elen++;
100 if (NULL != ps)
101 while (NULL != ps[plen].name)
102 plen++;
103
104 db = GNUNET_new (struct GNUNET_PQ_Context);
105 db->flags = flags;
106 db->config_str = GNUNET_strdup (config_str);
107 if (NULL != load_path)
108 db->load_path = GNUNET_strdup (load_path);
109 if (NULL != auto_suffix)
110 db->auto_suffix = GNUNET_strdup (auto_suffix);
111 if (0 != elen)
112 {
113 db->es = GNUNET_new_array (elen + 1,
114 struct GNUNET_PQ_ExecuteStatement);
115 memcpy (db->es,
116 es,
117 elen * sizeof (struct GNUNET_PQ_ExecuteStatement));
118 }
119 if (0 != plen)
120 {
121 db->ps = GNUNET_new_array (plen + 1,
122 struct GNUNET_PQ_PreparedStatement);
123 memcpy (db->ps,
124 ps,
125 plen * sizeof (struct GNUNET_PQ_PreparedStatement));
126 }
127 db->channel_map = GNUNET_CONTAINER_multishortmap_create (16,
128 GNUNET_YES);
129 GNUNET_PQ_reconnect (db);
130 if (NULL == db->conn)
131 {
132 GNUNET_CONTAINER_multishortmap_destroy (db->channel_map);
133 GNUNET_free (db->load_path);
134 GNUNET_free (db->auto_suffix);
135 GNUNET_free (db->config_str);
136 GNUNET_free (db);
137 return NULL;
138 }
139 return db;
140}
141
142
143enum GNUNET_GenericReturnValue
144GNUNET_PQ_exec_sql (struct GNUNET_PQ_Context *db,
145 const char *buf)
146{
147 struct GNUNET_OS_Process *psql;
148 enum GNUNET_OS_ProcessStatusType type;
149 unsigned long code;
150 enum GNUNET_GenericReturnValue ret;
151 char *fn;
152
153 GNUNET_asprintf (&fn,
154 "%s%s.sql",
155 db->load_path,
156 buf);
157 if (GNUNET_YES !=
158 GNUNET_DISK_file_test (fn))
159 {
160 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
161 "SQL resource `%s' does not exist\n",
162 fn);
163 GNUNET_free (fn);
164 return GNUNET_NO;
165 }
166 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
167 "Applying SQL file `%s' on database %s\n",
168 fn,
169 db->config_str);
170 psql = GNUNET_OS_start_process (GNUNET_OS_INHERIT_STD_NONE,
171 NULL,
172 NULL,
173 NULL,
174 "psql",
175 "psql",
176 db->config_str,
177 "-f",
178 fn,
179 "-q",
180 "--set",
181 "ON_ERROR_STOP=1",
182 NULL);
183 if (NULL == psql)
184 {
185 GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_ERROR,
186 "exec",
187 "psql");
188 GNUNET_free (fn);
189 return GNUNET_SYSERR;
190 }
191 ret = GNUNET_OS_process_wait_status (psql,
192 &type,
193 &code);
194 if (GNUNET_OK != ret)
195 {
196 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
197 "psql on file %s did not finish, killed it!\n",
198 fn);
199 /* can happen if we got a signal, like CTRL-C, before
200 psql was complete */
201 (void) GNUNET_OS_process_kill (psql,
202 SIGKILL);
203 GNUNET_OS_process_destroy (psql);
204 GNUNET_free (fn);
205 return GNUNET_SYSERR;
206 }
207 GNUNET_OS_process_destroy (psql);
208 if ( (GNUNET_OS_PROCESS_EXITED != type) ||
209 (0 != code) )
210 {
211 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
212 "Could not run PSQL on file %s: psql exit code was %d\n",
213 fn,
214 (int) code);
215 GNUNET_free (fn);
216 return GNUNET_SYSERR;
217 }
218 GNUNET_free (fn);
219 return GNUNET_OK;
220}
221
222
223enum GNUNET_GenericReturnValue
224GNUNET_PQ_run_sql (struct GNUNET_PQ_Context *db,
225 const char *load_path)
226{
227 const char *load_path_suffix;
228 size_t slen = strlen (load_path) + 10;
229
230 load_path_suffix = strrchr (load_path, '/');
231 if (NULL == load_path_suffix)
232 load_path_suffix = load_path;
233 else
234 load_path_suffix++; /* skip '/' */
235 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
236 "Loading SQL resources from `%s'\n",
237 load_path);
238 for (unsigned int i = 1; i<10000; i++)
239 {
240 char patch_name[slen];
241 enum GNUNET_DB_QueryStatus qs;
242
243 /* Check with DB versioning schema if this patch was already applied,
244 if so, skip it. */
245 GNUNET_snprintf (patch_name,
246 sizeof (patch_name),
247 "%s%04u",
248 load_path_suffix,
249 i);
250 {
251 char *applied_by;
252 struct GNUNET_PQ_QueryParam params[] = {
253 GNUNET_PQ_query_param_string (patch_name),
254 GNUNET_PQ_query_param_end
255 };
256 struct GNUNET_PQ_ResultSpec rs[] = {
257 GNUNET_PQ_result_spec_string ("applied_by",
258 &applied_by),
259 GNUNET_PQ_result_spec_end
260 };
261
262 qs = GNUNET_PQ_eval_prepared_singleton_select (db,
263 "gnunet_pq_check_patch",
264 params,
265 rs);
266 if (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT == qs)
267 {
268 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
269 "Database version %s already applied by %s, skipping\n",
270 patch_name,
271 applied_by);
272 GNUNET_PQ_cleanup_result (rs);
273 }
274 if (GNUNET_DB_STATUS_HARD_ERROR == qs)
275 {
276 GNUNET_break (0);
277 return GNUNET_SYSERR;
278 }
279 }
280 if (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT == qs)
281 continue; /* patch already applied, skip it */
282
283 if (0 != (GNUNET_PQ_FLAG_CHECK_CURRENT & db->flags))
284 {
285 /* We are only checking, found unapplied patch, bad! */
286 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
287 "Database outdated, patch %s missing. Aborting!\n",
288 patch_name);
289 return GNUNET_SYSERR;
290 }
291 else
292 {
293 /* patch not yet applied, run it! */
294 enum GNUNET_GenericReturnValue ret;
295
296 GNUNET_snprintf (patch_name,
297 sizeof (patch_name),
298 "%s%04u",
299 load_path,
300 i);
301 ret = GNUNET_PQ_exec_sql (db,
302 patch_name);
303 if (GNUNET_NO == ret)
304 break;
305 if (GNUNET_SYSERR == ret)
306 return GNUNET_SYSERR;
307 }
308 }
309 return GNUNET_OK;
310}
311
312
313void
314GNUNET_PQ_reconnect_if_down (struct GNUNET_PQ_Context *db)
315{
316 if (1 ==
317 PQconsumeInput (db->conn))
318 return;
319 if (CONNECTION_BAD != PQstatus (db->conn))
320 return;
321 GNUNET_PQ_reconnect (db);
322}
323
324
325enum GNUNET_GenericReturnValue
326GNUNET_PQ_get_oid_by_name (
327 struct GNUNET_PQ_Context *db,
328 const char *name,
329 Oid *oid)
330{
331 /* Check if the entry is in the cache already */
332 for (unsigned int i = 0; i < db->oids.num; i++)
333 {
334 /* Pointer comparison */
335 if (name == db->oids.table[i].name)
336 {
337 *oid = db->oids.table[i].oid;
338 return GNUNET_OK;
339 }
340 }
341
342 /* No entry found in cache, ask database */
343 {
344 enum GNUNET_DB_QueryStatus qs;
345 struct GNUNET_PQ_QueryParam params[] = {
346 GNUNET_PQ_query_param_string (name),
347 GNUNET_PQ_query_param_end
348 };
349 struct GNUNET_PQ_ResultSpec spec[] = {
350 GNUNET_PQ_result_spec_uint32 ("oid",
351 oid),
352 GNUNET_PQ_result_spec_end
353 };
354
355 GNUNET_assert (NULL != db);
356
357 qs = GNUNET_PQ_eval_prepared_singleton_select (db,
358 "gnunet_pq_get_oid_by_name",
359 params,
360 spec);
361 if (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT != qs)
362 return GNUNET_SYSERR;
363 }
364
365 /* Add the entry to the cache */
366 if (NULL == db->oids.table)
367 {
368 db->oids.table = GNUNET_new_array (8,
369 typeof(*db->oids.table));
370 db->oids.cap = 8;
371 db->oids.num = 0;
372 }
373
374 if (db->oids.cap <= db->oids.num)
375 GNUNET_array_grow (db->oids.table,
376 db->oids.cap,
377 db->oids.cap + 8);
378
379 db->oids.table[db->oids.num].name = name;
380 db->oids.table[db->oids.num].oid = *oid;
381 db->oids.num++;
382
383 return GNUNET_OK;
384}
385
386
387/**
388 * Load the initial set of OIDs for the supported
389 * array-datatypes
390 *
391 * @param db The database context
392 * @return GNUNET_OK on success, GNUNET_SYSERR if any of the types couldn't be found
393 */
394static
395enum GNUNET_GenericReturnValue
396load_initial_oids (struct GNUNET_PQ_Context *db)
397{
398 static const char *typnames[] = {
399 "bool",
400 "int2",
401 "int4",
402 "int8",
403 "bytea",
404 "varchar"
405 };
406 Oid oid;
407
408 for (size_t i = 0; i< sizeof(typnames) / sizeof(*typnames); i++)
409 {
410 if (GNUNET_OK !=
411 GNUNET_PQ_get_oid_by_name (db,
412 typnames[i],
413 &oid))
414 {
415 GNUNET_log_from (GNUNET_ERROR_TYPE_ERROR,
416 "pq",
417 "Couldn't retrieve OID for type %s\n",
418 typnames[i]);
419 return GNUNET_SYSERR;
420 }
421 }
422 return GNUNET_OK;
423}
424
425
426void
427GNUNET_PQ_reconnect (struct GNUNET_PQ_Context *db)
428{
429 GNUNET_PQ_event_reconnect_ (db,
430 -1);
431 if (NULL != db->conn)
432 PQfinish (db->conn);
433 db->conn = PQconnectdb (db->config_str);
434 if ( (NULL == db->conn) ||
435 (CONNECTION_OK != PQstatus (db->conn)) )
436 {
437 GNUNET_log_from (GNUNET_ERROR_TYPE_ERROR,
438 "pq",
439 "Database connection to '%s' failed: %s\n",
440 db->config_str,
441 (NULL != db->conn)
442 ? PQerrorMessage (db->conn)
443 : "PQconnectdb returned NULL");
444 if (NULL != db->conn)
445 {
446 PQfinish (db->conn);
447 db->conn = NULL;
448 }
449 return;
450 }
451 PQsetNoticeReceiver (db->conn,
452 &pq_notice_receiver_cb,
453 db);
454 PQsetNoticeProcessor (db->conn,
455 &pq_notice_processor_cb,
456 db);
457 if ( (NULL != db->load_path) &&
458 (NULL != db->auto_suffix) )
459 {
460 PGresult *res;
461 ExecStatusType est;
462
463 res = PQexec (db->conn,
464 "SELECT"
465 " schema_name"
466 " FROM information_schema.schemata"
467 " WHERE schema_name='_v';");
468 est = PQresultStatus (res);
469 if ( (PGRES_COMMAND_OK != est) &&
470 (PGRES_TUPLES_OK != est) )
471 {
472 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
473 "Failed to run statement to check versioning schema. Bad!\n");
474 PQclear (res);
475 PQfinish (db->conn);
476 db->conn = NULL;
477 return;
478 }
479 if (0 == PQntuples (res))
480 {
481 enum GNUNET_GenericReturnValue ret;
482
483 PQclear (res);
484 if (0 != (db->flags & GNUNET_PQ_FLAG_DROP))
485 {
486 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
487 "Versioning schema does not exist yet. Not attempting drop!\n");
488 PQfinish (db->conn);
489 db->conn = NULL;
490 return;
491 }
492 ret = GNUNET_PQ_exec_sql (db,
493 "versioning");
494 if (GNUNET_NO == ret)
495 {
496 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
497 "Failed to find SQL file to load database versioning logic\n");
498 PQfinish (db->conn);
499 db->conn = NULL;
500 return;
501 }
502 if (GNUNET_SYSERR == ret)
503 {
504 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
505 "Failed to run SQL logic to setup database versioning logic\n");
506 PQfinish (db->conn);
507 db->conn = NULL;
508 return;
509 }
510 }
511 else
512 {
513 PQclear (res);
514 }
515 }
516
517 /* Prepare statement for OID lookup by name */
518 {
519 PGresult *res;
520
521 res = PQprepare (db->conn,
522 "gnunet_pq_get_oid_by_name",
523 "SELECT"
524 " typname, oid"
525 " FROM pg_type"
526 " WHERE typname = $1"
527 " LIMIT 1",
528 1,
529 NULL);
530 if (PGRES_COMMAND_OK != PQresultStatus (res))
531 {
532 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
533 "Failed to run SQL statement prepare OID lookups: %s/%s\n",
534 PQresultErrorMessage (res),
535 PQerrorMessage (db->conn));
536 PQclear (res);
537 PQfinish (db->conn);
538 db->conn = NULL;
539 return;
540 }
541 PQclear (res);
542 }
543
544 /* Reset the OID-cache and retrieve the OIDs for the supported Array types */
545 db->oids.num = 0;
546 if (GNUNET_SYSERR == load_initial_oids (db))
547 {
548 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
549 "Failed to retrieve OID information for array types!\n");
550 PQfinish (db->conn);
551 db->conn = NULL;
552 return;
553 }
554
555 if (NULL != db->auto_suffix)
556 {
557 PGresult *res;
558
559 GNUNET_assert (NULL != db->load_path);
560 res = PQprepare (db->conn,
561 "gnunet_pq_check_patch",
562 "SELECT"
563 " applied_by"
564 " FROM _v.patches"
565 " WHERE patch_name = $1"
566 " LIMIT 1",
567 1,
568 NULL);
569 if (PGRES_COMMAND_OK != PQresultStatus (res))
570 {
571 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
572 "Failed to run SQL logic to setup database versioning logic: %s/%s\n",
573 PQresultErrorMessage (res),
574 PQerrorMessage (db->conn));
575 PQclear (res);
576 PQfinish (db->conn);
577 db->conn = NULL;
578 return;
579 }
580 PQclear (res);
581
582 if (GNUNET_SYSERR ==
583 GNUNET_PQ_run_sql (db,
584 db->auto_suffix))
585 {
586 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
587 "Failed to load SQL statements from `%s*'\n",
588 db->auto_suffix);
589 PQfinish (db->conn);
590 db->conn = NULL;
591 return;
592 }
593 }
594
595 if ( (NULL != db->es) &&
596 (GNUNET_OK !=
597 GNUNET_PQ_exec_statements (db,
598 db->es)) )
599 {
600 PQfinish (db->conn);
601 db->conn = NULL;
602 return;
603 }
604 if ( (NULL != db->ps) &&
605 (GNUNET_OK !=
606 GNUNET_PQ_prepare_statements (db,
607 db->ps)) )
608 {
609 PQfinish (db->conn);
610 db->conn = NULL;
611 return;
612 }
613 GNUNET_PQ_event_reconnect_ (db,
614 PQsocket (db->conn));
615}
616
617
618struct GNUNET_PQ_Context *
619GNUNET_PQ_connect_with_cfg (const struct GNUNET_CONFIGURATION_Handle *cfg,
620 const char *section,
621 const char *load_path_suffix,
622 const struct GNUNET_PQ_ExecuteStatement *es,
623 const struct GNUNET_PQ_PreparedStatement *ps)
624{
625 return GNUNET_PQ_connect_with_cfg2 (cfg,
626 section,
627 load_path_suffix,
628 es,
629 ps,
630 GNUNET_PQ_FLAG_NONE);
631}
632
633
634struct GNUNET_PQ_Context *
635GNUNET_PQ_connect_with_cfg2 (const struct GNUNET_CONFIGURATION_Handle *cfg,
636 const char *section,
637 const char *load_path_suffix,
638 const struct GNUNET_PQ_ExecuteStatement *es,
639 const struct GNUNET_PQ_PreparedStatement *ps,
640 enum GNUNET_PQ_Options flags)
641{
642 struct GNUNET_PQ_Context *db;
643 char *conninfo;
644 char *load_path;
645
646 if (GNUNET_OK !=
647 GNUNET_CONFIGURATION_get_value_string (cfg,
648 section,
649 "CONFIG",
650 &conninfo))
651 conninfo = NULL;
652 load_path = NULL;
653 if (GNUNET_OK !=
654 GNUNET_CONFIGURATION_get_value_filename (cfg,
655 section,
656 "SQL_DIR",
657 &load_path))
658 {
659 GNUNET_log_config_missing (GNUNET_ERROR_TYPE_INFO,
660 section,
661 "SQL_DIR");
662 }
663 if ( (NULL != load_path_suffix) &&
664 (NULL == load_path) )
665 {
666 GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR,
667 section,
668 "SQL_DIR");
669 return NULL;
670 }
671 db = GNUNET_PQ_connect2 (conninfo == NULL ? "" : conninfo,
672 load_path,
673 load_path_suffix,
674 es,
675 ps,
676 flags);
677 GNUNET_free (load_path);
678 GNUNET_free (conninfo);
679 return db;
680}
681
682
683void
684GNUNET_PQ_disconnect (struct GNUNET_PQ_Context *db)
685{
686 if (NULL == db)
687 return;
688 GNUNET_assert (0 ==
689 GNUNET_CONTAINER_multishortmap_size (db->channel_map));
690 GNUNET_CONTAINER_multishortmap_destroy (db->channel_map);
691 GNUNET_free (db->es);
692 GNUNET_free (db->ps);
693 GNUNET_free (db->load_path);
694 GNUNET_free (db->auto_suffix);
695 GNUNET_free (db->config_str);
696 GNUNET_free (db->oids.table);
697 db->oids.table = NULL;
698 db->oids.num = 0;
699 db->oids.cap = 0;
700 PQfinish (db->conn);
701 GNUNET_free (db);
702}
703
704
705/* end of pq/pq_connect.c */
diff --git a/src/lib/pq/pq_eval.c b/src/lib/pq/pq_eval.c
new file mode 100644
index 000000000..e31475e13
--- /dev/null
+++ b/src/lib/pq/pq_eval.c
@@ -0,0 +1,248 @@
1/*
2 This file is part of GNUnet
3 Copyright (C) 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 * @file pq/pq_eval.c
22 * @brief functions to execute SQL statements with arguments and/or results (PostGres)
23 * @author Christian Grothoff
24 */
25#include "platform.h"
26#include "pq.h"
27
28
29/**
30 * Error code returned by Postgres for deadlock.
31 */
32#define PQ_DIAG_SQLSTATE_DEADLOCK "40P01"
33
34/**
35 * Error code returned by Postgres for uniqueness violation.
36 */
37#define PQ_DIAG_SQLSTATE_UNIQUE_VIOLATION "23505"
38
39/**
40 * Error code returned by Postgres on serialization failure.
41 */
42#define PQ_DIAG_SQLSTATE_SERIALIZATION_FAILURE "40001"
43
44
45enum GNUNET_DB_QueryStatus
46GNUNET_PQ_eval_result (struct GNUNET_PQ_Context *db,
47 const char *statement_name,
48 PGresult *result)
49{
50 ExecStatusType est;
51
52 if (NULL == result)
53 return GNUNET_DB_STATUS_SOFT_ERROR;
54 est = PQresultStatus (result);
55 if ((PGRES_COMMAND_OK != est) &&
56 (PGRES_TUPLES_OK != est))
57 {
58 const char *sqlstate;
59 ConnStatusType status;
60
61 if (CONNECTION_OK != (status = PQstatus (db->conn)))
62 {
63 GNUNET_log_from (GNUNET_ERROR_TYPE_INFO,
64 "pq",
65 "Database connection failed during query `%s': %d (reconnecting)\n",
66 statement_name,
67 status);
68 GNUNET_PQ_reconnect (db);
69 return GNUNET_DB_STATUS_SOFT_ERROR;
70 }
71
72 sqlstate = PQresultErrorField (result,
73 PG_DIAG_SQLSTATE);
74 if (NULL == sqlstate)
75 {
76 /* very unexpected... */
77 GNUNET_break (0);
78 return GNUNET_DB_STATUS_HARD_ERROR;
79 }
80 if ((0 == strcmp (sqlstate,
81 PQ_DIAG_SQLSTATE_DEADLOCK)) ||
82 (0 == strcmp (sqlstate,
83 PQ_DIAG_SQLSTATE_SERIALIZATION_FAILURE)))
84 {
85 /* These two can be retried and have a fair chance of working
86 the next time */
87 GNUNET_log_from (GNUNET_ERROR_TYPE_INFO,
88 "pq",
89 "Query `%s' failed with result: %s/%s/%s/%s/%s\n",
90 statement_name,
91 PQresultErrorField (result,
92 PG_DIAG_MESSAGE_PRIMARY),
93 PQresultErrorField (result,
94 PG_DIAG_MESSAGE_DETAIL),
95 PQresultErrorMessage (result),
96 PQresStatus (PQresultStatus (result)),
97 PQerrorMessage (db->conn));
98 return GNUNET_DB_STATUS_SOFT_ERROR;
99 }
100 if (0 == strcmp (sqlstate,
101 PQ_DIAG_SQLSTATE_UNIQUE_VIOLATION))
102 {
103 /* Likely no need to retry, INSERT of "same" data. */
104 GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG,
105 "pq",
106 "Query `%s' failed with unique violation: %s/%s/%s/%s/%s\n",
107 statement_name,
108 PQresultErrorField (result,
109 PG_DIAG_MESSAGE_PRIMARY),
110 PQresultErrorField (result,
111 PG_DIAG_MESSAGE_DETAIL),
112 PQresultErrorMessage (result),
113 PQresStatus (PQresultStatus (result)),
114 PQerrorMessage (db->conn));
115 return GNUNET_DB_STATUS_SUCCESS_NO_RESULTS;
116 }
117 GNUNET_log_from (GNUNET_ERROR_TYPE_ERROR,
118 "pq",
119 "Query `%s' failed with result: %s/%s/%s/%s/%s\n",
120 statement_name,
121 PQresultErrorField (result,
122 PG_DIAG_MESSAGE_PRIMARY),
123 PQresultErrorField (result,
124 PG_DIAG_MESSAGE_DETAIL),
125 PQresultErrorMessage (result),
126 PQresStatus (PQresultStatus (result)),
127 PQerrorMessage (db->conn));
128 return GNUNET_DB_STATUS_HARD_ERROR;
129 }
130 return GNUNET_DB_STATUS_SUCCESS_NO_RESULTS;
131}
132
133
134enum GNUNET_DB_QueryStatus
135GNUNET_PQ_eval_prepared_non_select (struct GNUNET_PQ_Context *db,
136 const char *statement_name,
137 const struct GNUNET_PQ_QueryParam *params)
138{
139 PGresult *result;
140 enum GNUNET_DB_QueryStatus qs;
141
142 result = GNUNET_PQ_exec_prepared (db,
143 statement_name,
144 params);
145 if (NULL == result)
146 return GNUNET_DB_STATUS_SOFT_ERROR;
147 qs = GNUNET_PQ_eval_result (db,
148 statement_name,
149 result);
150 if (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS == qs)
151 {
152 const char *tuples;
153
154 /* What an awful API, this function really does return a string */
155 tuples = PQcmdTuples (result);
156 if (NULL != tuples)
157 qs = strtol (tuples, NULL, 10);
158 }
159 PQclear (result);
160 return qs;
161}
162
163
164enum GNUNET_DB_QueryStatus
165GNUNET_PQ_eval_prepared_multi_select (struct GNUNET_PQ_Context *db,
166 const char *statement_name,
167 const struct GNUNET_PQ_QueryParam *params,
168 GNUNET_PQ_PostgresResultHandler rh,
169 void *rh_cls)
170{
171 PGresult *result;
172 enum GNUNET_DB_QueryStatus qs;
173 unsigned int ret;
174
175 result = GNUNET_PQ_exec_prepared (db,
176 statement_name,
177 params);
178 if (NULL == result)
179 return GNUNET_DB_STATUS_SOFT_ERROR;
180 qs = GNUNET_PQ_eval_result (db,
181 statement_name,
182 result);
183 if (qs < 0)
184 {
185 PQclear (result);
186 return qs;
187 }
188 ret = PQntuples (result);
189 if (NULL != rh)
190 rh (rh_cls,
191 result,
192 ret);
193 PQclear (result);
194 return ret;
195}
196
197
198enum GNUNET_DB_QueryStatus
199GNUNET_PQ_eval_prepared_singleton_select (
200 struct GNUNET_PQ_Context *db,
201 const char *statement_name,
202 const struct GNUNET_PQ_QueryParam *params,
203 struct GNUNET_PQ_ResultSpec *rs)
204{
205 PGresult *result;
206 enum GNUNET_DB_QueryStatus qs;
207 int ntuples;
208
209 result = GNUNET_PQ_exec_prepared (db,
210 statement_name,
211 params);
212 if (NULL == result)
213 return GNUNET_DB_STATUS_SOFT_ERROR;
214 qs = GNUNET_PQ_eval_result (db,
215 statement_name,
216 result);
217 if (qs < 0)
218 {
219 PQclear (result);
220 return qs;
221 }
222 ntuples = PQntuples (result);
223 if (0 == ntuples)
224 {
225 PQclear (result);
226 return GNUNET_DB_STATUS_SUCCESS_NO_RESULTS;
227 }
228 if (1 != ntuples)
229 {
230 /* more than one result, but there must be at most one */
231 GNUNET_break (0);
232 PQclear (result);
233 return GNUNET_DB_STATUS_HARD_ERROR;
234 }
235 if (GNUNET_OK !=
236 GNUNET_PQ_extract_result (result,
237 rs,
238 0))
239 {
240 PQclear (result);
241 return GNUNET_DB_STATUS_HARD_ERROR;
242 }
243 PQclear (result);
244 return GNUNET_DB_STATUS_SUCCESS_ONE_RESULT;
245}
246
247
248/* end of pq/pq_eval.c */
diff --git a/src/lib/pq/pq_event.c b/src/lib/pq/pq_event.c
new file mode 100644
index 000000000..7506ff2f0
--- /dev/null
+++ b/src/lib/pq/pq_event.c
@@ -0,0 +1,584 @@
1/*
2 This file is part of GNUnet
3 Copyright (C) 2021, 2023 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 pq/pq_event.c
22 * @brief event notifications via Postgres
23 * @author Christian Grothoff
24 */
25#include "platform.h"
26#include "pq.h"
27#include <pthread.h>
28
29
30/**
31 * Handle for an active LISTENer to the database.
32 */
33struct GNUNET_DB_EventHandler
34{
35 /**
36 * Channel name.
37 */
38 struct GNUNET_ShortHashCode sh;
39
40 /**
41 * Function to call on events.
42 */
43 GNUNET_DB_EventCallback cb;
44
45 /**
46 * Closure for @e cb.
47 */
48 void *cb_cls;
49
50 /**
51 * Database context this event handler is with.
52 */
53 struct GNUNET_PQ_Context *db;
54
55 /**
56 * Task to run on timeout.
57 */
58 struct GNUNET_SCHEDULER_Task *timeout_task;
59};
60
61
62/**
63 * Convert @a es to a short hash.
64 *
65 * @param es spec to hash to an identifier
66 * @param[out] sh short hash to set
67 */
68static void
69es_to_sh (const struct GNUNET_DB_EventHeaderP *es,
70 struct GNUNET_ShortHashCode *sh)
71{
72 struct GNUNET_HashCode h_channel;
73
74 GNUNET_CRYPTO_hash (es,
75 ntohs (es->size),
76 &h_channel);
77 GNUNET_static_assert (sizeof (*sh) <= sizeof (h_channel));
78 memcpy (sh,
79 &h_channel,
80 sizeof (*sh));
81}
82
83
84/**
85 * Convert @a sh to a Postgres identifier.
86 *
87 * @param sh short hash to convert to an identifier
88 * @param[out] identifier by default, Postgres supports
89 * NAMEDATALEN=64 character identifiers
90 * @return end position of the identifier
91 */
92static char *
93sh_to_channel (struct GNUNET_ShortHashCode *sh,
94 char identifier[64])
95{
96 char *end;
97
98 end = GNUNET_STRINGS_data_to_string (sh,
99 sizeof (*sh),
100 identifier,
101 63);
102 GNUNET_assert (NULL != end);
103 *end = '\0';
104 return end;
105}
106
107
108/**
109 * Convert @a sh to a Postgres identifier.
110 *
111 * @param identifier to convert
112 * @param[out] sh set to short hash
113 * @return #GNUNET_OK on success
114 */
115static enum GNUNET_GenericReturnValue
116channel_to_sh (const char *identifier,
117 struct GNUNET_ShortHashCode *sh)
118{
119 return GNUNET_STRINGS_string_to_data (identifier,
120 strlen (identifier),
121 sh,
122 sizeof (*sh));
123}
124
125
126/**
127 * Convert @a es to a Postgres identifier.
128 *
129 * @param es spec to hash to an identifier
130 * @param[out] identifier by default, Postgres supports
131 * NAMEDATALEN=64 character identifiers
132 * @return end position of the identifier
133 */
134static char *
135es_to_channel (const struct GNUNET_DB_EventHeaderP *es,
136 char identifier[64])
137{
138 struct GNUNET_ShortHashCode sh;
139
140 es_to_sh (es,
141 &sh);
142 return sh_to_channel (&sh,
143 identifier);
144}
145
146
147/**
148 * Closure for #do_notify().
149 */
150struct NotifyContext
151{
152 /**
153 * Extra argument of the notification, or NULL.
154 */
155 void *extra;
156
157 /**
158 * Number of bytes in @e extra.
159 */
160 size_t extra_size;
161};
162
163
164/**
165 * Function called on every event handler that
166 * needs to be triggered.
167 *
168 * @param cls a `struct NotifyContext`
169 * @param sh channel name
170 * @param value a `struct GNUNET_DB_EventHandler`
171 * @return #GNUNET_OK continue to iterate
172 */
173static enum GNUNET_GenericReturnValue
174do_notify (void *cls,
175 const struct GNUNET_ShortHashCode *sh,
176 void *value)
177{
178 struct NotifyContext *ctx = cls;
179 struct GNUNET_DB_EventHandler *eh = value;
180
181 eh->cb (eh->cb_cls,
182 ctx->extra,
183 ctx->extra_size);
184 return GNUNET_OK;
185}
186
187
188void
189GNUNET_PQ_event_do_poll (struct GNUNET_PQ_Context *db)
190{
191 PGnotify *n;
192 unsigned int cnt = 0;
193
194 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
195 "PG poll job active\n");
196 if (1 !=
197 PQconsumeInput (db->conn))
198 {
199 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
200 "Failed to read from Postgres: %s\n",
201 PQerrorMessage (db->conn));
202 if (CONNECTION_BAD != PQstatus (db->conn))
203 return;
204 GNUNET_PQ_reconnect (db);
205 return;
206 }
207 while (NULL != (n = PQnotifies (db->conn)))
208 {
209 struct GNUNET_ShortHashCode sh;
210 struct NotifyContext ctx = {
211 .extra = NULL
212 };
213
214 cnt++;
215 if ('X' != toupper ((int) n->relname[0]))
216 {
217 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
218 "Ignoring notification for unsupported channel identifier `%s'\n",
219 n->relname);
220 PQfreemem (n);
221 continue;
222 }
223 if (GNUNET_OK !=
224 channel_to_sh (&n->relname[1],
225 &sh))
226 {
227 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
228 "Ignoring notification for unsupported channel identifier `%s'\n",
229 n->relname);
230 PQfreemem (n);
231 continue;
232 }
233 if ( (NULL != n->extra) &&
234 (GNUNET_OK !=
235 GNUNET_STRINGS_string_to_data_alloc (n->extra,
236 strlen (n->extra),
237 &ctx.extra,
238 &ctx.extra_size)))
239 {
240 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
241 "Ignoring notification for unsupported extra data `%s' on channel `%s'\n",
242 n->extra,
243 n->relname);
244 PQfreemem (n);
245 continue;
246 }
247 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
248 "Received notification %s with extra data `%.*s'\n",
249 n->relname,
250 (int) ctx.extra_size,
251 (const char *) ctx.extra);
252 GNUNET_CONTAINER_multishortmap_get_multiple (db->channel_map,
253 &sh,
254 &do_notify,
255 &ctx);
256 GNUNET_free (ctx.extra);
257 PQfreemem (n);
258 }
259 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
260 "PG poll job finishes after %u events\n",
261 cnt);
262}
263
264
265/**
266 * The GNUnet scheduler notifies us that we need to
267 * trigger the DB event poller.
268 *
269 * @param cls a `struct GNUNET_PQ_Context *`
270 */
271static void
272do_scheduler_notify (void *cls)
273{
274 struct GNUNET_PQ_Context *db = cls;
275
276 db->event_task = NULL;
277 if (NULL == db->rfd)
278 GNUNET_PQ_reconnect (db);
279 GNUNET_PQ_event_do_poll (db);
280 if (NULL != db->event_task)
281 return;
282 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
283 "Resubscribing\n");
284 if (NULL == db->rfd)
285 {
286 db->resubscribe_backoff
287 = GNUNET_TIME_relative_max (db->resubscribe_backoff,
288 GNUNET_TIME_UNIT_SECONDS);
289 db->resubscribe_backoff
290 = GNUNET_TIME_STD_BACKOFF (db->resubscribe_backoff);
291 db->event_task = GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_UNIT_SECONDS,
292 &do_scheduler_notify,
293 db);
294 return;
295 }
296 db->resubscribe_backoff = GNUNET_TIME_UNIT_SECONDS;
297 db->event_task
298 = GNUNET_SCHEDULER_add_read_net (GNUNET_TIME_UNIT_FOREVER_REL,
299 db->rfd,
300 &do_scheduler_notify,
301 db);
302}
303
304
305/**
306 * Function called when the Postgres FD changes and we need
307 * to update the scheduler event loop task.
308 *
309 * @param cls a `struct GNUNET_PQ_Context *`
310 * @param fd the file descriptor, possibly -1
311 */
312static void
313scheduler_fd_cb (void *cls,
314 int fd)
315{
316 struct GNUNET_PQ_Context *db = cls;
317
318 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
319 "New poll FD is %d\n",
320 fd);
321 if (NULL != db->event_task)
322 {
323 GNUNET_SCHEDULER_cancel (db->event_task);
324 db->event_task = NULL;
325 }
326 GNUNET_free (db->rfd);
327 if (-1 == fd)
328 return;
329 if (0 == GNUNET_CONTAINER_multishortmap_size (db->channel_map))
330 return;
331 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
332 "Activating poll job on %d\n",
333 fd);
334 db->rfd = GNUNET_NETWORK_socket_box_native (fd);
335 db->event_task
336 = GNUNET_SCHEDULER_add_read_net (GNUNET_TIME_UNIT_ZERO,
337 db->rfd,
338 &do_scheduler_notify,
339 db);
340}
341
342
343/**
344 * Helper function to trigger an SQL @a cmd on @a db
345 *
346 * @param db database to send command to
347 * @param cmd prefix of the command to send
348 * @param eh details about the event
349 */
350static void
351manage_subscribe (struct GNUNET_PQ_Context *db,
352 const char *cmd,
353 struct GNUNET_DB_EventHandler *eh)
354{
355 char sql[16 + 64];
356 char *end;
357 PGresult *result;
358
359 if (NULL == db->conn)
360 return;
361 end = stpcpy (sql,
362 cmd);
363 end = sh_to_channel (&eh->sh,
364 end);
365 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
366 "Executing PQ command `%s'\n",
367 sql);
368 result = PQexec (db->conn,
369 sql);
370 if (PGRES_COMMAND_OK != PQresultStatus (result))
371 {
372 GNUNET_log_from (GNUNET_ERROR_TYPE_ERROR,
373 "pq",
374 "Failed to execute `%s': %s/%s/%s/%s/%s",
375 sql,
376 PQresultErrorField (result,
377 PG_DIAG_MESSAGE_PRIMARY),
378 PQresultErrorField (result,
379 PG_DIAG_MESSAGE_DETAIL),
380 PQresultErrorMessage (result),
381 PQresStatus (PQresultStatus (result)),
382 PQerrorMessage (db->conn));
383 }
384 PQclear (result);
385}
386
387
388/**
389 * Re-subscribe to notifications after disconnect.
390 *
391 * @param cls the DB context
392 * @param sh the short hash of the channel
393 * @param value the event handler
394 * @return #GNUNET_OK to continue to iterate
395 */
396static enum GNUNET_GenericReturnValue
397register_notify (void *cls,
398 const struct GNUNET_ShortHashCode *sh,
399 void *value)
400{
401 struct GNUNET_PQ_Context *db = cls;
402 struct GNUNET_DB_EventHandler *eh = value;
403
404 manage_subscribe (db,
405 "LISTEN X",
406 eh);
407 return GNUNET_OK;
408}
409
410
411void
412GNUNET_PQ_event_reconnect_ (struct GNUNET_PQ_Context *db,
413 int fd)
414{
415 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
416 "Change in PQ event FD to %d\n",
417 fd);
418 scheduler_fd_cb (db,
419 fd);
420 GNUNET_CONTAINER_multishortmap_iterate (db->channel_map,
421 &register_notify,
422 db);
423}
424
425
426/**
427 * Function run on timeout for an event. Triggers
428 * the notification, but does NOT clear the handler.
429 *
430 * @param cls a `struct GNUNET_DB_EventHandler *`
431 */
432static void
433event_timeout (void *cls)
434{
435 struct GNUNET_DB_EventHandler *eh = cls;
436
437 eh->timeout_task = NULL;
438 eh->cb (eh->cb_cls,
439 NULL,
440 0);
441}
442
443
444struct GNUNET_DB_EventHandler *
445GNUNET_PQ_event_listen (struct GNUNET_PQ_Context *db,
446 const struct GNUNET_DB_EventHeaderP *es,
447 struct GNUNET_TIME_Relative timeout,
448 GNUNET_DB_EventCallback cb,
449 void *cb_cls)
450{
451 struct GNUNET_DB_EventHandler *eh;
452 bool sub;
453
454 eh = GNUNET_new (struct GNUNET_DB_EventHandler);
455 eh->db = db;
456 es_to_sh (es,
457 &eh->sh);
458 eh->cb = cb;
459 eh->cb_cls = cb_cls;
460 sub = (NULL ==
461 GNUNET_CONTAINER_multishortmap_get (db->channel_map,
462 &eh->sh));
463 GNUNET_assert (GNUNET_OK ==
464 GNUNET_CONTAINER_multishortmap_put (db->channel_map,
465 &eh->sh,
466 eh,
467 GNUNET_CONTAINER_MULTIHASHMAPOPTION_MULTIPLE));
468 if (NULL == db->event_task)
469 {
470 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
471 "Starting event scheduler\n");
472 scheduler_fd_cb (db,
473 PQsocket (db->conn));
474 }
475 if (sub)
476 manage_subscribe (db,
477 "LISTEN X",
478 eh);
479 eh->timeout_task = GNUNET_SCHEDULER_add_delayed (timeout,
480 &event_timeout,
481 eh);
482 return eh;
483}
484
485
486void
487GNUNET_PQ_event_listen_cancel (struct GNUNET_DB_EventHandler *eh)
488{
489 struct GNUNET_PQ_Context *db = eh->db;
490
491 GNUNET_assert (GNUNET_OK ==
492 GNUNET_CONTAINER_multishortmap_remove (db->channel_map,
493 &eh->sh,
494 eh));
495 if (NULL ==
496 GNUNET_CONTAINER_multishortmap_get (db->channel_map,
497 &eh->sh))
498 manage_subscribe (db,
499 "UNLISTEN X",
500 eh);
501 if (0 == GNUNET_CONTAINER_multishortmap_size (db->channel_map))
502 {
503 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
504 "Stopping PQ event scheduler job\n");
505 GNUNET_free (db->rfd);
506 if (NULL != db->event_task)
507 {
508 GNUNET_SCHEDULER_cancel (db->event_task);
509 db->event_task = NULL;
510 }
511 }
512 if (NULL != eh->timeout_task)
513 {
514 GNUNET_SCHEDULER_cancel (eh->timeout_task);
515 eh->timeout_task = NULL;
516 }
517 GNUNET_free (eh);
518}
519
520
521char *
522GNUNET_PQ_get_event_notify_channel (const struct GNUNET_DB_EventHeaderP *es)
523{
524 char sql[16 + 64 + 8];
525 char *end;
526
527 end = stpcpy (sql,
528 "X");
529 end = es_to_channel (es,
530 end);
531 GNUNET_assert (NULL != end);
532 return GNUNET_strdup (sql);
533}
534
535
536void
537GNUNET_PQ_event_notify (struct GNUNET_PQ_Context *db,
538 const struct GNUNET_DB_EventHeaderP *es,
539 const void *extra,
540 size_t extra_size)
541{
542 char sql[16 + 64 + extra_size * 8 / 5 + 8];
543 char *end;
544 PGresult *result;
545
546 end = stpcpy (sql,
547 "NOTIFY X");
548 end = es_to_channel (es,
549 end);
550 end = stpcpy (end,
551 ", '");
552 end = GNUNET_STRINGS_data_to_string (extra,
553 extra_size,
554 end,
555 sizeof (sql) - (end - sql) - 1);
556 GNUNET_assert (NULL != end);
557 *end = '\0';
558 end = stpcpy (end,
559 "'");
560 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
561 "Executing command `%s'\n",
562 sql);
563 result = PQexec (db->conn,
564 sql);
565 if (PGRES_COMMAND_OK != PQresultStatus (result))
566 {
567 GNUNET_log_from (GNUNET_ERROR_TYPE_ERROR,
568 "pq",
569 "Failed to execute `%s': %s/%s/%s/%s/%s",
570 sql,
571 PQresultErrorField (result,
572 PG_DIAG_MESSAGE_PRIMARY),
573 PQresultErrorField (result,
574 PG_DIAG_MESSAGE_DETAIL),
575 PQresultErrorMessage (result),
576 PQresStatus (PQresultStatus (result)),
577 PQerrorMessage (db->conn));
578 }
579 PQclear (result);
580 GNUNET_PQ_event_do_poll (db);
581}
582
583
584/* end of pq_event.c */
diff --git a/src/lib/pq/pq_exec.c b/src/lib/pq/pq_exec.c
new file mode 100644
index 000000000..1fd8c5068
--- /dev/null
+++ b/src/lib/pq/pq_exec.c
@@ -0,0 +1,95 @@
1/*
2 This file is part of GNUnet
3 Copyright (C) 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 * @file pq/pq_exec.c
22 * @brief functions to execute plain SQL statements (PostGres)
23 * @author Christian Grothoff
24 */
25#include "platform.h"
26#include "pq.h"
27
28
29struct GNUNET_PQ_ExecuteStatement
30GNUNET_PQ_make_execute (const char *sql)
31{
32 struct GNUNET_PQ_ExecuteStatement es = {
33 .sql = sql,
34 .ignore_errors = GNUNET_NO
35 };
36
37 return es;
38}
39
40
41struct GNUNET_PQ_ExecuteStatement
42GNUNET_PQ_make_try_execute (const char *sql)
43{
44 struct GNUNET_PQ_ExecuteStatement es = {
45 .sql = sql,
46 .ignore_errors = GNUNET_YES
47 };
48
49 return es;
50}
51
52
53enum GNUNET_GenericReturnValue
54GNUNET_PQ_exec_statements (struct GNUNET_PQ_Context *db,
55 const struct GNUNET_PQ_ExecuteStatement *es)
56{
57 for (unsigned int i = 0; NULL != es[i].sql; i++)
58 {
59 PGresult *result;
60
61 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
62 "Running statement `%s' on %p\n",
63 es[i].sql,
64 db);
65 result = PQexec (db->conn,
66 es[i].sql);
67 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
68 "Running statement `%s' on %p finished (%s)\n",
69 es[i].sql,
70 db,
71 PQresStatus (PQresultStatus (result)));
72 if ((GNUNET_NO == es[i].ignore_errors) &&
73 (PGRES_COMMAND_OK != PQresultStatus (result)))
74 {
75 GNUNET_log_from (GNUNET_ERROR_TYPE_ERROR,
76 "pq",
77 "Failed to execute `%s': %s/%s/%s/%s/%s",
78 es[i].sql,
79 PQresultErrorField (result,
80 PG_DIAG_MESSAGE_PRIMARY),
81 PQresultErrorField (result,
82 PG_DIAG_MESSAGE_DETAIL),
83 PQresultErrorMessage (result),
84 PQresStatus (PQresultStatus (result)),
85 PQerrorMessage (db->conn));
86 PQclear (result);
87 return GNUNET_SYSERR;
88 }
89 PQclear (result);
90 }
91 return GNUNET_OK;
92}
93
94
95/* end of pq/pq_exec.c */
diff --git a/src/lib/pq/pq_prepare.c b/src/lib/pq/pq_prepare.c
new file mode 100644
index 000000000..b4292dea3
--- /dev/null
+++ b/src/lib/pq/pq_prepare.c
@@ -0,0 +1,125 @@
1/*
2 This file is part of GNUnet
3 Copyright (C) 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 * @file pq/pq_prepare.c
22 * @brief functions to connect to libpq (PostGres)
23 * @author Christian Grothoff
24 */
25#include "platform.h"
26#include "pq.h"
27
28
29struct GNUNET_PQ_PreparedStatement
30GNUNET_PQ_make_prepare (const char *name,
31 const char *sql)
32{
33 struct GNUNET_PQ_PreparedStatement ps = {
34 .name = name,
35 .sql = sql
36 };
37
38 return ps;
39}
40
41
42enum GNUNET_GenericReturnValue
43GNUNET_PQ_prepare_once (struct GNUNET_PQ_Context *db,
44 const struct GNUNET_PQ_PreparedStatement *ps)
45{
46 for (unsigned int i = 0; NULL != ps[i].name; i++)
47 {
48 PGresult *ret;
49
50 GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG,
51 "pq",
52 "Preparing SQL statement `%s' as `%s'\n",
53 ps[i].sql,
54 ps[i].name);
55 ret = PQprepare (db->conn,
56 ps[i].name,
57 ps[i].sql,
58 0,
59 NULL);
60 if (PGRES_COMMAND_OK != PQresultStatus (ret))
61 {
62 GNUNET_log_from (GNUNET_ERROR_TYPE_ERROR | GNUNET_ERROR_TYPE_BULK,
63 "pq",
64 "PQprepare (`%s' as `%s') failed with error: %s\n",
65 ps[i].sql,
66 ps[i].name,
67 PQerrorMessage (db->conn));
68 PQclear (ret);
69 ret = PQdescribePrepared (db->conn,
70 ps[i].name);
71 if (PGRES_COMMAND_OK != PQresultStatus (ret))
72 {
73 PQclear (ret);
74 return GNUNET_SYSERR;
75 }
76 GNUNET_log_from (GNUNET_ERROR_TYPE_ERROR,
77 "pq",
78 "Statement `%s' already known. Ignoring the issue in the hope that you are using connection pooling...\n",
79 ps[i].name);
80 }
81 PQclear (ret);
82 }
83 return GNUNET_OK;
84}
85
86
87enum GNUNET_GenericReturnValue
88GNUNET_PQ_prepare_statements (struct GNUNET_PQ_Context *db,
89 const struct GNUNET_PQ_PreparedStatement *ps)
90{
91 if (db->ps != ps)
92 {
93 /* add 'ps' to list db->ps of prepared statements to run on reconnect! */
94 unsigned int nlen = 0; /* length of 'ps' array */
95 unsigned int xlen;
96 struct GNUNET_PQ_PreparedStatement *rps; /* combined array */
97
98 while (NULL != ps[nlen].name)
99 nlen++;
100 xlen = nlen + db->ps_off;
101 if (xlen > db->ps_len)
102 {
103 xlen = 2 * xlen + 1;
104 rps = GNUNET_new_array (xlen,
105 struct GNUNET_PQ_PreparedStatement);
106 if (NULL != db->ps)
107 memcpy (rps,
108 db->ps,
109 db->ps_off * sizeof (struct GNUNET_PQ_PreparedStatement));
110 GNUNET_free (db->ps);
111 db->ps_len = xlen;
112 db->ps = rps;
113 }
114 memcpy (&db->ps[db->ps_off],
115 ps,
116 nlen * sizeof (struct GNUNET_PQ_PreparedStatement));
117 db->ps_off += nlen;
118 }
119
120 return GNUNET_PQ_prepare_once (db,
121 ps);
122}
123
124
125/* end of pq/pq_prepare.c */
diff --git a/src/lib/pq/pq_query_helper.c b/src/lib/pq/pq_query_helper.c
new file mode 100644
index 000000000..913ce9235
--- /dev/null
+++ b/src/lib/pq/pq_query_helper.c
@@ -0,0 +1,1585 @@
1/*
2 This file is part of GNUnet
3 Copyright (C) 2014, 2015, 2016, 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 * @file pq/pq_query_helper.c
22 * @brief functions to initialize parameter arrays
23 * @author Christian Grothoff
24 */
25#include "platform.h"
26#include "gnunet_common.h"
27#include "gnunet_pq_lib.h"
28#include "gnunet_time_lib.h"
29#include "pq.h"
30
31
32/**
33 * Function called to convert input argument into SQL parameters.
34 *
35 * @param cls closure
36 * @param data pointer to input argument
37 * @param data_len number of bytes in @a data (if applicable)
38 * @param[out] param_values SQL data to set
39 * @param[out] param_lengths SQL length data to set
40 * @param[out] param_formats SQL format data to set
41 * @param param_length number of entries available in the @a param_values, @a param_lengths and @a param_formats arrays
42 * @param[out] scratch buffer for dynamic allocations (to be done via #GNUNET_malloc()
43 * @param scratch_length number of entries left in @a scratch
44 * @return -1 on error, number of offsets used in @a scratch otherwise
45 */
46static int
47qconv_null (void *cls,
48 const void *data,
49 size_t data_len,
50 void *param_values[],
51 int param_lengths[],
52 int param_formats[],
53 unsigned int param_length,
54 void *scratch[],
55 unsigned int scratch_length)
56{
57 (void) scratch;
58 (void) scratch_length;
59 (void) data;
60 (void) data_len;
61 GNUNET_break (NULL == cls);
62 if (1 != param_length)
63 return -1;
64 param_values[0] = NULL;
65 param_lengths[0] = 0;
66 param_formats[0] = 1;
67 return 0;
68}
69
70
71struct GNUNET_PQ_QueryParam
72GNUNET_PQ_query_param_null (void)
73{
74 struct GNUNET_PQ_QueryParam res = {
75 .conv = &qconv_null,
76 .num_params = 1
77 };
78
79 return res;
80}
81
82
83/**
84 * Function called to convert input argument into SQL parameters.
85 *
86 * @param cls closure
87 * @param data pointer to input argument
88 * @param data_len number of bytes in @a data (if applicable)
89 * @param[out] param_values SQL data to set
90 * @param[out] param_lengths SQL length data to set
91 * @param[out] param_formats SQL format data to set
92 * @param param_length number of entries available in the @a param_values, @a param_lengths and @a param_formats arrays
93 * @param[out] scratch buffer for dynamic allocations (to be done via #GNUNET_malloc()
94 * @param scratch_length number of entries left in @a scratch
95 * @return -1 on error, number of offsets used in @a scratch otherwise
96 */
97static int
98qconv_fixed (void *cls,
99 const void *data,
100 size_t data_len,
101 void *param_values[],
102 int param_lengths[],
103 int param_formats[],
104 unsigned int param_length,
105 void *scratch[],
106 unsigned int scratch_length)
107{
108 (void) scratch;
109 (void) scratch_length;
110 GNUNET_break (NULL == cls);
111 if (1 != param_length)
112 return -1;
113 param_values[0] = (void *) data;
114 param_lengths[0] = data_len;
115 param_formats[0] = 1;
116 return 0;
117}
118
119
120struct GNUNET_PQ_QueryParam
121GNUNET_PQ_query_param_fixed_size (const void *ptr,
122 size_t ptr_size)
123{
124 struct GNUNET_PQ_QueryParam res = {
125 .conv = &qconv_fixed,
126 .conv_cls = NULL,
127 .data = ptr,
128 .size = ptr_size,
129 .num_params = 1
130 };
131
132 return res;
133}
134
135
136struct GNUNET_PQ_QueryParam
137GNUNET_PQ_query_param_string (const char *ptr)
138{
139 return GNUNET_PQ_query_param_fixed_size (ptr,
140 strlen (ptr));
141}
142
143
144struct GNUNET_PQ_QueryParam
145GNUNET_PQ_query_param_bool (bool b)
146{
147 static uint8_t bt = 1;
148 static uint8_t bf = 0;
149
150 return GNUNET_PQ_query_param_fixed_size (b ? &bt : &bf,
151 sizeof (uint8_t));
152}
153
154
155/**
156 * Function called to convert input argument into SQL parameters.
157 *
158 * @param cls closure
159 * @param data pointer to input argument
160 * @param data_len number of bytes in @a data (if applicable)
161 * @param[out] param_values SQL data to set
162 * @param[out] param_lengths SQL length data to set
163 * @param[out] param_formats SQL format data to set
164 * @param param_length number of entries available in the @a param_values, @a param_lengths and @a param_formats arrays
165 * @param[out] scratch buffer for dynamic allocations (to be done via #GNUNET_malloc()
166 * @param scratch_length number of entries left in @a scratch
167 * @return -1 on error, number of offsets used in @a scratch otherwise
168 */
169static int
170qconv_uint16 (void *cls,
171 const void *data,
172 size_t data_len,
173 void *param_values[],
174 int param_lengths[],
175 int param_formats[],
176 unsigned int param_length,
177 void *scratch[],
178 unsigned int scratch_length)
179{
180 const uint16_t *u_hbo = data;
181 uint16_t *u_nbo;
182
183 (void) scratch;
184 (void) scratch_length;
185 GNUNET_break (NULL == cls);
186 if (1 != param_length)
187 return -1;
188 u_nbo = GNUNET_new (uint16_t);
189 scratch[0] = u_nbo;
190 *u_nbo = htons (*u_hbo);
191 param_values[0] = (void *) u_nbo;
192 param_lengths[0] = sizeof(uint16_t);
193 param_formats[0] = 1;
194 return 1;
195}
196
197
198struct GNUNET_PQ_QueryParam
199GNUNET_PQ_query_param_uint16 (const uint16_t *x)
200{
201 struct GNUNET_PQ_QueryParam res = {
202 .conv = &qconv_uint16,
203 .data = x,
204 .size = sizeof(*x),
205 .num_params = 1
206 };
207
208 return res;
209}
210
211
212/**
213 * Function called to convert input argument into SQL parameters.
214 *
215 * @param cls closure
216 * @param data pointer to input argument
217 * @param data_len number of bytes in @a data (if applicable)
218 * @param[out] param_values SQL data to set
219 * @param[out] param_lengths SQL length data to set
220 * @param[out] param_formats SQL format data to set
221 * @param param_length number of entries available in the @a param_values, @a param_lengths and @a param_formats arrays
222 * @param[out] scratch buffer for dynamic allocations (to be done via #GNUNET_malloc()
223 * @param scratch_length number of entries left in @a scratch
224 * @return -1 on error, number of offsets used in @a scratch otherwise
225 */
226static int
227qconv_uint32 (void *cls,
228 const void *data,
229 size_t data_len,
230 void *param_values[],
231 int param_lengths[],
232 int param_formats[],
233 unsigned int param_length,
234 void *scratch[],
235 unsigned int scratch_length)
236{
237 const uint32_t *u_hbo = data;
238 uint32_t *u_nbo;
239
240 (void) scratch;
241 (void) scratch_length;
242 GNUNET_break (NULL == cls);
243 if (1 != param_length)
244 return -1;
245 u_nbo = GNUNET_new (uint32_t);
246 scratch[0] = u_nbo;
247 *u_nbo = htonl (*u_hbo);
248 param_values[0] = (void *) u_nbo;
249 param_lengths[0] = sizeof(uint32_t);
250 param_formats[0] = 1;
251 return 1;
252}
253
254
255struct GNUNET_PQ_QueryParam
256GNUNET_PQ_query_param_uint32 (const uint32_t *x)
257{
258 struct GNUNET_PQ_QueryParam res = {
259 .conv = &qconv_uint32,
260 .data = x,
261 .size = sizeof(*x),
262 .num_params = 1
263 };
264
265 return res;
266}
267
268
269/**
270 * Function called to convert input argument into SQL parameters.
271 *
272 * @param cls closure
273 * @param data pointer to input argument
274 * @param data_len number of bytes in @a data (if applicable)
275 * @param[out] param_values SQL data to set
276 * @param[out] param_lengths SQL length data to set
277 * @param[out] param_formats SQL format data to set
278 * @param param_length number of entries available in the @a param_values, @a param_lengths and @a param_formats arrays
279 * @param[out] scratch buffer for dynamic allocations (to be done via #GNUNET_malloc()
280 * @param scratch_length number of entries left in @a scratch
281 * @return -1 on error, number of offsets used in @a scratch otherwise
282 */
283static int
284qconv_uint64 (void *cls,
285 const void *data,
286 size_t data_len,
287 void *param_values[],
288 int param_lengths[],
289 int param_formats[],
290 unsigned int param_length,
291 void *scratch[],
292 unsigned int scratch_length)
293{
294 const uint64_t *u_hbo = data;
295 uint64_t *u_nbo;
296
297 (void) scratch;
298 (void) scratch_length;
299 GNUNET_break (NULL == cls);
300 if (1 != param_length)
301 return -1;
302 u_nbo = GNUNET_new (uint64_t);
303 scratch[0] = u_nbo;
304 *u_nbo = GNUNET_htonll (*u_hbo);
305 param_values[0] = (void *) u_nbo;
306 param_lengths[0] = sizeof(uint64_t);
307 param_formats[0] = 1;
308 return 1;
309}
310
311
312struct GNUNET_PQ_QueryParam
313GNUNET_PQ_query_param_uint64 (const uint64_t *x)
314{
315 struct GNUNET_PQ_QueryParam res = {
316 .conv = &qconv_uint64,
317 .data = x,
318 .size = sizeof(*x),
319 .num_params = 1
320 };
321
322 return res;
323}
324
325
326/**
327 * Function called to convert input argument into SQL parameters.
328 *
329 * @param cls closure
330 * @param data pointer to input argument
331 * @param data_len number of bytes in @a data (if applicable)
332 * @param[out] param_values SQL data to set
333 * @param[out] param_lengths SQL length data to set
334 * @param[out] param_formats SQL format data to set
335 * @param param_length number of entries available in the @a param_values, @a param_lengths and @a param_formats arrays
336 * @param[out] scratch buffer for dynamic allocations (to be done via #GNUNET_malloc()
337 * @param scratch_length number of entries left in @a scratch
338 * @return -1 on error, number of offsets used in @a scratch otherwise
339 */
340static int
341qconv_int64 (void *cls,
342 const void *data,
343 size_t data_len,
344 void *param_values[],
345 int param_lengths[],
346 int param_formats[],
347 unsigned int param_length,
348 void *scratch[],
349 unsigned int scratch_length)
350{
351 const int64_t *u_hbo = data;
352 int64_t *u_nbo;
353
354 (void) scratch;
355 (void) scratch_length;
356 GNUNET_break (NULL == cls);
357 if (1 != param_length)
358 return -1;
359 u_nbo = GNUNET_new (int64_t);
360 scratch[0] = u_nbo;
361 *u_nbo = GNUNET_htonll (*u_hbo);
362 param_values[0] = (void *) u_nbo;
363 param_lengths[0] = sizeof(int64_t);
364 param_formats[0] = 1;
365 return 1;
366}
367
368
369struct GNUNET_PQ_QueryParam
370GNUNET_PQ_query_param_int64 (const int64_t *x)
371{
372 struct GNUNET_PQ_QueryParam res = {
373 .conv = &qconv_int64,
374 .data = x,
375 .size = sizeof(*x),
376 .num_params = 1
377 };
378
379 return res;
380}
381
382
383/**
384 * Function called to convert input argument into SQL parameters.
385 *
386 * @param cls closure
387 * @param data pointer to input argument
388 * @param data_len number of bytes in @a data (if applicable)
389 * @param[out] param_values SQL data to set
390 * @param[out] param_lengths SQL length data to set
391 * @param[out] param_formats SQL format data to set
392 * @param param_length number of entries available in the @a param_values, @a param_lengths and @a param_formats arrays
393 * @param[out] scratch buffer for dynamic allocations (to be done via #GNUNET_malloc()
394 * @param scratch_length number of entries left in @a scratch
395 * @return -1 on error, number of offsets used in @a scratch otherwise
396 */
397static int
398qconv_rsa_public_key (void *cls,
399 const void *data,
400 size_t data_len,
401 void *param_values[],
402 int param_lengths[],
403 int param_formats[],
404 unsigned int param_length,
405 void *scratch[],
406 unsigned int scratch_length)
407{
408 const struct GNUNET_CRYPTO_RsaPublicKey *rsa = data;
409 void *buf;
410 size_t buf_size;
411
412 GNUNET_break (NULL == cls);
413 if (1 != param_length)
414 return -1;
415 buf_size = GNUNET_CRYPTO_rsa_public_key_encode (rsa,
416 &buf);
417 scratch[0] = buf;
418 param_values[0] = (void *) buf;
419 param_lengths[0] = buf_size;
420 param_formats[0] = 1;
421 return 1;
422}
423
424
425struct GNUNET_PQ_QueryParam
426GNUNET_PQ_query_param_rsa_public_key (
427 const struct GNUNET_CRYPTO_RsaPublicKey *x)
428{
429 struct GNUNET_PQ_QueryParam res = {
430 .conv = &qconv_rsa_public_key,
431 .data = x,
432 .num_params = 1
433 };
434
435 return res;
436}
437
438
439/**
440 * Function called to convert input argument into SQL parameters.
441 *
442 * @param cls closure
443 * @param data pointer to input argument
444 * @param data_len number of bytes in @a data (if applicable)
445 * @param[out] param_values SQL data to set
446 * @param[out] param_lengths SQL length data to set
447 * @param[out] param_formats SQL format data to set
448 * @param param_length number of entries available in the @a param_values, @a param_lengths and @a param_formats arrays
449 * @param[out] scratch buffer for dynamic allocations (to be done via #GNUNET_malloc()
450 * @param scratch_length number of entries left in @a scratch
451 * @return -1 on error, number of offsets used in @a scratch otherwise
452 */
453static int
454qconv_rsa_signature (void *cls,
455 const void *data,
456 size_t data_len,
457 void *param_values[],
458 int param_lengths[],
459 int param_formats[],
460 unsigned int param_length,
461 void *scratch[],
462 unsigned int scratch_length)
463{
464 const struct GNUNET_CRYPTO_RsaSignature *sig = data;
465 void *buf;
466 size_t buf_size;
467
468 GNUNET_break (NULL == cls);
469 if (1 != param_length)
470 return -1;
471 buf_size = GNUNET_CRYPTO_rsa_signature_encode (sig,
472 &buf);
473 scratch[0] = buf;
474 param_values[0] = (void *) buf;
475 param_lengths[0] = buf_size;
476 param_formats[0] = 1;
477 return 1;
478}
479
480
481struct GNUNET_PQ_QueryParam
482GNUNET_PQ_query_param_rsa_signature (const struct GNUNET_CRYPTO_RsaSignature *x)
483{
484 struct GNUNET_PQ_QueryParam res = {
485 .conv = &qconv_rsa_signature,
486 .data = x,
487 .num_params = 1
488 };
489
490 return res;
491}
492
493
494/**
495 * Function called to convert input argument into SQL parameters.
496 *
497 * @param cls closure
498 * @param data pointer to input argument
499 * @param data_len number of bytes in @a data (if applicable)
500 * @param[out] param_values SQL data to set
501 * @param[out] param_lengths SQL length data to set
502 * @param[out] param_formats SQL format data to set
503 * @param param_length number of entries available in the @a param_values, @a param_lengths and @a param_formats arrays
504 * @param[out] scratch buffer for dynamic allocations (to be done via #GNUNET_malloc()
505 * @param scratch_length number of entries left in @a scratch
506 * @return -1 on error, number of offsets used in @a scratch otherwise
507 */
508static int
509qconv_rel_time (void *cls,
510 const void *data,
511 size_t data_len,
512 void *param_values[],
513 int param_lengths[],
514 int param_formats[],
515 unsigned int param_length,
516 void *scratch[],
517 unsigned int scratch_length)
518{
519 const struct GNUNET_TIME_Relative *u = data;
520 struct GNUNET_TIME_Relative rel;
521 uint64_t *u_nbo;
522
523 GNUNET_break (NULL == cls);
524 if (1 != param_length)
525 return -1;
526 rel = *u;
527 if (rel.rel_value_us > INT64_MAX)
528 rel.rel_value_us = INT64_MAX;
529 u_nbo = GNUNET_new (uint64_t);
530 scratch[0] = u_nbo;
531 *u_nbo = GNUNET_htonll (rel.rel_value_us);
532 param_values[0] = (void *) u_nbo;
533 param_lengths[0] = sizeof(uint64_t);
534 param_formats[0] = 1;
535 return 1;
536}
537
538
539struct GNUNET_PQ_QueryParam
540GNUNET_PQ_query_param_relative_time (const struct GNUNET_TIME_Relative *x)
541{
542 struct GNUNET_PQ_QueryParam res = {
543 .conv = &qconv_rel_time,
544 .data = x,
545 .size = sizeof(*x),
546 .num_params = 1
547 };
548
549 return res;
550}
551
552
553/**
554 * Function called to convert input argument into SQL parameters.
555 *
556 * @param cls closure
557 * @param data pointer to input argument
558 * @param data_len number of bytes in @a data (if applicable)
559 * @param[out] param_values SQL data to set
560 * @param[out] param_lengths SQL length data to set
561 * @param[out] param_formats SQL format data to set
562 * @param param_length number of entries available in the @a param_values, @a param_lengths and @a param_formats arrays
563 * @param[out] scratch buffer for dynamic allocations (to be done via #GNUNET_malloc()
564 * @param scratch_length number of entries left in @a scratch
565 * @return -1 on error, number of offsets used in @a scratch otherwise
566 */
567static int
568qconv_abs_time (void *cls,
569 const void *data,
570 size_t data_len,
571 void *param_values[],
572 int param_lengths[],
573 int param_formats[],
574 unsigned int param_length,
575 void *scratch[],
576 unsigned int scratch_length)
577{
578 const struct GNUNET_TIME_Absolute *u = data;
579 struct GNUNET_TIME_Absolute abs;
580 uint64_t *u_nbo;
581
582 GNUNET_break (NULL == cls);
583 if (1 != param_length)
584 return -1;
585 abs = *u;
586 if (abs.abs_value_us > INT64_MAX)
587 abs.abs_value_us = INT64_MAX;
588 u_nbo = GNUNET_new (uint64_t);
589 scratch[0] = u_nbo;
590 *u_nbo = GNUNET_htonll (abs.abs_value_us);
591 param_values[0] = (void *) u_nbo;
592 param_lengths[0] = sizeof(uint64_t);
593 param_formats[0] = 1;
594 return 1;
595}
596
597
598struct GNUNET_PQ_QueryParam
599GNUNET_PQ_query_param_absolute_time (const struct GNUNET_TIME_Absolute *x)
600{
601 struct GNUNET_PQ_QueryParam res = {
602 .conv = &qconv_abs_time,
603 .data = x,
604 .size = sizeof(*x),
605 .num_params = 1
606 };
607
608 return res;
609}
610
611
612struct GNUNET_PQ_QueryParam
613GNUNET_PQ_query_param_absolute_time_nbo (
614 const struct GNUNET_TIME_AbsoluteNBO *x)
615{
616 return GNUNET_PQ_query_param_auto_from_type (&x->abs_value_us__);
617}
618
619
620struct GNUNET_PQ_QueryParam
621GNUNET_PQ_query_param_timestamp (const struct GNUNET_TIME_Timestamp *x)
622{
623 return GNUNET_PQ_query_param_absolute_time (&x->abs_time);
624}
625
626
627struct GNUNET_PQ_QueryParam
628GNUNET_PQ_query_param_timestamp_nbo (
629 const struct GNUNET_TIME_TimestampNBO *x)
630{
631 return GNUNET_PQ_query_param_absolute_time_nbo (&x->abs_time_nbo);
632}
633
634
635/**
636 * Closure for the array type handlers.
637 *
638 * May contain sizes information for the data, given (and handled) by the
639 * caller.
640 */
641struct qconv_array_cls
642{
643 /**
644 * If not null, contains the array of sizes (the size of the array is the
645 * .size field in the ambient GNUNET_PQ_QueryParam struct). We do not free
646 * this memory.
647 *
648 * If not null, this value has precedence over @a sizes, which MUST be NULL */
649 const size_t *sizes;
650
651 /**
652 * If @a size and @a c_sizes are NULL, this field defines the same size
653 * for each element in the array.
654 */
655 size_t same_size;
656
657 /**
658 * If true, the array parameter to the data pointer to the qconv_array is a
659 * continuous byte array of data, either with @a same_size each or sizes
660 * provided bytes by @a sizes;
661 */
662 bool continuous;
663
664 /**
665 * Type of the array elements
666 */
667 enum array_types typ;
668
669 /**
670 * Oid of the array elements
671 */
672 Oid oid;
673};
674
675/**
676 * Callback to cleanup a qconv_array_cls to be used during
677 * GNUNET_PQ_cleanup_query_params_closures
678 */
679static void
680qconv_array_cls_cleanup (void *cls)
681{
682 GNUNET_free (cls);
683}
684
685
686/**
687 * Function called to convert input argument into SQL parameters for arrays
688 *
689 * Note: the format for the encoding of arrays for libpq is not very well
690 * documented. We peeked into various sources (postgresql and libpqtypes) for
691 * guidance.
692 *
693 * @param cls Closure of type struct qconv_array_cls*
694 * @param data Pointer to first element in the array
695 * @param data_len Number of _elements_ in array @a data (if applicable)
696 * @param[out] param_values SQL data to set
697 * @param[out] param_lengths SQL length data to set
698 * @param[out] param_formats SQL format data to set
699 * @param param_length number of entries available in the @a param_values, @a param_lengths and @a param_formats arrays
700 * @param[out] scratch buffer for dynamic allocations (to be done via #GNUNET_malloc()
701 * @param scratch_length number of entries left in @a scratch
702 * @return -1 on error, number of offsets used in @a scratch otherwise
703 */
704static int
705qconv_array (
706 void *cls,
707 const void *data,
708 size_t data_len,
709 void *param_values[],
710 int param_lengths[],
711 int param_formats[],
712 unsigned int param_length,
713 void *scratch[],
714 unsigned int scratch_length)
715{
716 struct qconv_array_cls *meta = cls;
717 size_t num = data_len;
718 size_t total_size;
719 const size_t *sizes;
720 bool same_sized;
721 size_t *string_lengths = NULL;
722 void *elements = NULL;
723 bool noerror = true;
724
725 (void) (param_length);
726 (void) (scratch_length);
727
728 GNUNET_assert (NULL != meta);
729 GNUNET_assert (num < INT_MAX);
730
731 sizes = meta->sizes;
732 same_sized = (0 != meta->same_size);
733
734#define RETURN_UNLESS(cond) \
735 do { \
736 if (! (cond)) \
737 { \
738 GNUNET_break ((cond)); \
739 noerror = false; \
740 goto DONE; \
741 } \
742 } while (0)
743
744 /* Calculate sizes and check bounds */
745 {
746 /* num * length-field */
747 size_t x = sizeof(uint32_t);
748 size_t y = x * num;
749 RETURN_UNLESS ((0 == num) || (y / num == x));
750
751 /* size of header */
752 total_size = x = sizeof(struct pq_array_header);
753 total_size += y;
754 RETURN_UNLESS (total_size >= x);
755
756 /* sizes of elements */
757 if (same_sized)
758 {
759 x = num * meta->same_size;
760 RETURN_UNLESS ((0 == num) || (x / num == meta->same_size));
761
762 y = total_size;
763 total_size += x;
764 RETURN_UNLESS (total_size >= y);
765 }
766 else /* sizes are different per element */
767 {
768 /* for an array of strings we need to get their length's first */
769 if (array_of_string == meta->typ)
770 {
771 string_lengths = GNUNET_new_array (num, size_t);
772
773 if (meta->continuous)
774 {
775 const char *ptr = data;
776 for (unsigned int i = 0; i < num; i++)
777 {
778 size_t len = strlen (ptr);
779 string_lengths[i] = len;
780 ptr += len + 1;
781 }
782 }
783 else
784 {
785 const char **str = (const char **) data;
786 for (unsigned int i = 0; i < num; i++)
787 string_lengths[i] = strlen (str[i]);
788 }
789
790 sizes = string_lengths;
791 }
792
793 for (unsigned int i = 0; i < num; i++)
794 {
795 x = total_size;
796 total_size += sizes[i];
797 RETURN_UNLESS (total_size >= x);
798 }
799 }
800
801 RETURN_UNLESS (total_size < INT_MAX);
802
803 elements = GNUNET_malloc (total_size);
804 }
805
806 /* Write data */
807 {
808 char *in = (char *) data;
809 char *out = elements;
810 size_t nullbyte = (array_of_string == meta->typ) ? 1 : 0;
811 struct pq_array_header h = {
812 .ndim = htonl (1), /* We only support one-dimensional arrays */
813 .has_null = htonl (0), /* We do not support NULL entries in arrays */
814 .lbound = htonl (1), /* Default start index value */
815 .dim = htonl (num),
816 .oid = htonl (meta->oid),
817 };
818
819 /* Write header */
820 GNUNET_memcpy (out, &h, sizeof(h));
821 out += sizeof(h);
822
823
824 /* Write elements */
825 for (unsigned int i = 0; i < num; i++)
826 {
827 size_t sz = same_sized ? meta->same_size : sizes[i];
828 size_t hsz = htonl (sz);
829
830 GNUNET_memcpy (out,
831 &hsz,
832 sizeof(hsz));
833 out += sizeof(uint32_t);
834
835 switch (meta->typ)
836 {
837 case array_of_bool:
838 {
839 GNUNET_assert (sizeof(bool) == sz);
840 *(bool *) out = (*(bool *) in);
841 in += sz;
842 break;
843 }
844 case array_of_uint16:
845 {
846 GNUNET_assert (sizeof(uint16_t) == sz);
847 *(uint16_t *) out = htons (*(uint16_t *) in);
848 in += sz;
849 break;
850 }
851 case array_of_uint32:
852 {
853 uint32_t v;
854 GNUNET_assert (sizeof(uint32_t) == sz);
855
856 v = htonl (*(uint32_t *) in);
857 GNUNET_memcpy (out,
858 &v,
859 sizeof(v));
860 in += sz;
861 break;
862 }
863 case array_of_uint64:
864 {
865 uint64_t tmp;
866 GNUNET_assert (sizeof(uint64_t) == sz);
867
868 tmp = GNUNET_htonll (*(uint64_t *) in);
869 GNUNET_memcpy (out,
870 &tmp,
871 sizeof(tmp));
872 in += sz;
873 break;
874 }
875 case array_of_byte:
876 case array_of_string:
877 {
878 const void *ptr;
879
880 if (meta->continuous)
881 {
882 ptr = in;
883 in += sz + nullbyte;
884 }
885 else
886 ptr = ((const void **) data)[i];
887
888 GNUNET_memcpy (out,
889 ptr,
890 sz);
891 break;
892 }
893 case array_of_abs_time:
894 case array_of_rel_time:
895 case array_of_timestamp:
896 {
897 uint64_t val;
898
899 switch (meta->typ)
900 {
901 case array_of_abs_time:
902 {
903 const struct GNUNET_TIME_Absolute *abs =
904 (const struct GNUNET_TIME_Absolute *) in;
905
906 GNUNET_assert (sizeof(struct GNUNET_TIME_Absolute) == sz);
907
908 if (! meta->continuous)
909 abs = ((const struct GNUNET_TIME_Absolute **) data)[i];
910
911 val = abs->abs_value_us;
912 break;
913 }
914 case array_of_rel_time:
915 {
916 const struct GNUNET_TIME_Relative *rel =
917 (const struct GNUNET_TIME_Relative *) in;
918
919 GNUNET_assert (sizeof(struct GNUNET_TIME_Relative) == sz);
920
921 if (! meta->continuous)
922 rel = ((const struct GNUNET_TIME_Relative **) data)[i];
923
924 val = rel->rel_value_us;
925 break;
926 }
927 case array_of_timestamp:
928 {
929 const struct GNUNET_TIME_Timestamp *ts =
930 (const struct GNUNET_TIME_Timestamp *) in;
931
932 GNUNET_assert (sizeof(struct GNUNET_TIME_Timestamp) == sz);
933
934 if (! meta->continuous)
935 ts = ((const struct GNUNET_TIME_Timestamp **) data)[i];
936
937 val = ts->abs_time.abs_value_us;
938 break;
939 }
940 default:
941 {
942 GNUNET_assert (0);
943 }
944 }
945
946 if (val > INT64_MAX)
947 val = INT64_MAX;
948
949 val = GNUNET_htonll (val);
950 GNUNET_memcpy (out,
951 &val,
952 sizeof(val));
953
954 if (meta->continuous)
955 in += sz;
956
957 break;
958 }
959 default:
960 {
961 GNUNET_assert (0);
962 break;
963 }
964 }
965 out += sz;
966 }
967 }
968
969 param_values[0] = elements;
970 param_lengths[0] = total_size;
971 param_formats[0] = 1;
972 scratch[0] = elements;
973
974DONE:
975 GNUNET_free (string_lengths);
976
977 if (noerror)
978 return 1;
979
980 return -1;
981}
982
983
984/**
985 * Function to genreate a typ specific query parameter and corresponding closure
986 *
987 * @param num Number of elements in @a elements
988 * @param continuous If true, @a elements is an continuous array of data
989 * @param elements Array of @a num elements, either continuous or pointers
990 * @param sizes Array of @a num sizes, one per element, may be NULL
991 * @param same_size If not 0, all elements in @a elements have this size
992 * @param typ Supported internal type of each element in @a elements
993 * @param oid Oid of the type to be used in Postgres
994 * @return Query parameter
995 */
996static struct GNUNET_PQ_QueryParam
997query_param_array_generic (
998 unsigned int num,
999 bool continuous,
1000 const void *elements,
1001 const size_t *sizes,
1002 size_t same_size,
1003 enum array_types typ,
1004 Oid oid)
1005{
1006 struct qconv_array_cls *meta = GNUNET_new (struct qconv_array_cls);
1007
1008 meta->typ = typ;
1009 meta->oid = oid;
1010 meta->sizes = sizes;
1011 meta->same_size = same_size;
1012 meta->continuous = continuous;
1013
1014 {
1015 struct GNUNET_PQ_QueryParam res = {
1016 .conv = qconv_array,
1017 .conv_cls = meta,
1018 .conv_cls_cleanup = &qconv_array_cls_cleanup,
1019 .data = elements,
1020 .size = num,
1021 .num_params = 1,
1022 };
1023
1024 return res;
1025 }
1026}
1027
1028
1029struct GNUNET_PQ_QueryParam
1030GNUNET_PQ_query_param_array_bool (
1031 unsigned int num,
1032 const bool *elements,
1033 struct GNUNET_PQ_Context *db)
1034{
1035 Oid oid;
1036
1037 GNUNET_assert (GNUNET_OK ==
1038 GNUNET_PQ_get_oid_by_name (db,
1039 "bool",
1040 &oid));
1041 return query_param_array_generic (num,
1042 true,
1043 elements,
1044 NULL,
1045 sizeof(bool),
1046 array_of_bool,
1047 oid);
1048}
1049
1050
1051struct GNUNET_PQ_QueryParam
1052GNUNET_PQ_query_param_array_uint16 (
1053 unsigned int num,
1054 const uint16_t *elements,
1055 struct GNUNET_PQ_Context *db)
1056{
1057 Oid oid;
1058
1059 GNUNET_assert (GNUNET_OK ==
1060 GNUNET_PQ_get_oid_by_name (db,
1061 "int2",
1062 &oid));
1063 return query_param_array_generic (num,
1064 true,
1065 elements,
1066 NULL,
1067 sizeof(uint16_t),
1068 array_of_uint16,
1069 oid);
1070}
1071
1072
1073struct GNUNET_PQ_QueryParam
1074GNUNET_PQ_query_param_array_uint32 (
1075 unsigned int num,
1076 const uint32_t *elements,
1077 struct GNUNET_PQ_Context *db)
1078{
1079 Oid oid;
1080
1081 GNUNET_assert (GNUNET_OK ==
1082 GNUNET_PQ_get_oid_by_name (db,
1083 "int4",
1084 &oid));
1085 return query_param_array_generic (num,
1086 true,
1087 elements,
1088 NULL,
1089 sizeof(uint32_t),
1090 array_of_uint32,
1091 oid);
1092}
1093
1094
1095struct GNUNET_PQ_QueryParam
1096GNUNET_PQ_query_param_array_uint64 (
1097 unsigned int num,
1098 const uint64_t *elements,
1099 struct GNUNET_PQ_Context *db)
1100{
1101 Oid oid;
1102
1103 GNUNET_assert (GNUNET_OK ==
1104 GNUNET_PQ_get_oid_by_name (db,
1105 "int8",
1106 &oid));
1107 return query_param_array_generic (num,
1108 true,
1109 elements,
1110 NULL,
1111 sizeof(uint64_t),
1112 array_of_uint64,
1113 oid);
1114}
1115
1116
1117struct GNUNET_PQ_QueryParam
1118GNUNET_PQ_query_param_array_bytes (
1119 unsigned int num,
1120 const void *elements,
1121 const size_t *sizes,
1122 struct GNUNET_PQ_Context *db)
1123{
1124 Oid oid;
1125
1126 GNUNET_assert (GNUNET_OK ==
1127 GNUNET_PQ_get_oid_by_name (db,
1128 "bytea",
1129 &oid));
1130 return query_param_array_generic (num,
1131 true,
1132 elements,
1133 sizes,
1134 0,
1135 array_of_byte,
1136 oid);
1137}
1138
1139
1140struct GNUNET_PQ_QueryParam
1141GNUNET_PQ_query_param_array_ptrs_bytes (
1142 unsigned int num,
1143 const void *elements[static num],
1144 const size_t *sizes,
1145 struct GNUNET_PQ_Context *db)
1146{
1147 Oid oid;
1148
1149 GNUNET_assert (GNUNET_OK ==
1150 GNUNET_PQ_get_oid_by_name (db,
1151 "bytea",
1152 &oid));
1153 return query_param_array_generic (num,
1154 false,
1155 elements,
1156 sizes,
1157 0,
1158 array_of_byte,
1159 oid);
1160}
1161
1162
1163struct GNUNET_PQ_QueryParam
1164GNUNET_PQ_query_param_array_bytes_same_size (
1165 unsigned int num,
1166 const void *elements,
1167 size_t same_size,
1168 struct GNUNET_PQ_Context *db)
1169{
1170 Oid oid;
1171
1172 GNUNET_assert (GNUNET_OK ==
1173 GNUNET_PQ_get_oid_by_name (db,
1174 "bytea",
1175 &oid));
1176 return query_param_array_generic (num,
1177 true,
1178 elements,
1179 NULL,
1180 same_size,
1181 array_of_byte,
1182 oid);
1183}
1184
1185
1186struct GNUNET_PQ_QueryParam
1187GNUNET_PQ_query_param_array_ptrs_bytes_same_size (
1188 unsigned int num,
1189 const void *elements[static num],
1190 size_t same_size,
1191 struct GNUNET_PQ_Context *db)
1192{
1193 Oid oid;
1194
1195 GNUNET_assert (GNUNET_OK ==
1196 GNUNET_PQ_get_oid_by_name (db,
1197 "bytea",
1198 &oid));
1199 return query_param_array_generic (num,
1200 false,
1201 elements,
1202 NULL,
1203 same_size,
1204 array_of_byte,
1205 oid);
1206}
1207
1208
1209struct GNUNET_PQ_QueryParam
1210GNUNET_PQ_query_param_array_string (
1211 unsigned int num,
1212 const char *elements,
1213 struct GNUNET_PQ_Context *db)
1214{
1215 Oid oid;
1216
1217 GNUNET_assert (GNUNET_OK ==
1218 GNUNET_PQ_get_oid_by_name (db,
1219 "text",
1220 &oid));
1221 return query_param_array_generic (num,
1222 true,
1223 elements,
1224 NULL,
1225 0,
1226 array_of_string,
1227 oid);
1228}
1229
1230
1231struct GNUNET_PQ_QueryParam
1232GNUNET_PQ_query_param_array_ptrs_string (
1233 unsigned int num,
1234 const char *elements[static num],
1235 struct GNUNET_PQ_Context *db)
1236{
1237 Oid oid;
1238
1239 GNUNET_assert (GNUNET_OK ==
1240 GNUNET_PQ_get_oid_by_name (db,
1241 "text",
1242 &oid));
1243 return query_param_array_generic (num,
1244 false,
1245 elements,
1246 NULL,
1247 0,
1248 array_of_string,
1249 oid);
1250}
1251
1252
1253struct GNUNET_PQ_QueryParam
1254GNUNET_PQ_query_param_array_abs_time (
1255 unsigned int num,
1256 const struct GNUNET_TIME_Absolute *elements,
1257 struct GNUNET_PQ_Context *db)
1258{
1259 Oid oid;
1260
1261 GNUNET_assert (GNUNET_OK ==
1262 GNUNET_PQ_get_oid_by_name (db,
1263 "int8",
1264 &oid));
1265 return query_param_array_generic (num,
1266 true,
1267 elements,
1268 NULL,
1269 sizeof(struct GNUNET_TIME_Absolute),
1270 array_of_abs_time,
1271 oid);
1272}
1273
1274
1275struct GNUNET_PQ_QueryParam
1276GNUNET_PQ_query_param_array_ptrs_abs_time (
1277 unsigned int num,
1278 const struct GNUNET_TIME_Absolute *elements[],
1279 struct GNUNET_PQ_Context *db)
1280{
1281 Oid oid;
1282
1283 GNUNET_assert (GNUNET_OK ==
1284 GNUNET_PQ_get_oid_by_name (db,
1285 "int8",
1286 &oid));
1287 return query_param_array_generic (num,
1288 false,
1289 elements,
1290 NULL,
1291 sizeof(struct GNUNET_TIME_Absolute),
1292 array_of_abs_time,
1293 oid);
1294}
1295
1296
1297struct GNUNET_PQ_QueryParam
1298GNUNET_PQ_query_param_array_rel_time (
1299 unsigned int num,
1300 const struct GNUNET_TIME_Relative *elements,
1301 struct GNUNET_PQ_Context *db)
1302{
1303 Oid oid;
1304
1305 GNUNET_assert (GNUNET_OK ==
1306 GNUNET_PQ_get_oid_by_name (db,
1307 "int8",
1308 &oid));
1309 return query_param_array_generic (num,
1310 true,
1311 elements,
1312 NULL,
1313 sizeof(struct GNUNET_TIME_Relative),
1314 array_of_abs_time,
1315 oid);
1316}
1317
1318
1319struct GNUNET_PQ_QueryParam
1320GNUNET_PQ_query_param_array_ptrs_rel_time (
1321 unsigned int num,
1322 const struct GNUNET_TIME_Relative *elements[],
1323 struct GNUNET_PQ_Context *db)
1324{
1325 Oid oid;
1326
1327 GNUNET_assert (GNUNET_OK ==
1328 GNUNET_PQ_get_oid_by_name (db,
1329 "int8",
1330 &oid));
1331 return query_param_array_generic (num,
1332 false,
1333 elements,
1334 NULL,
1335 sizeof(struct GNUNET_TIME_Relative),
1336 array_of_abs_time,
1337 oid);
1338}
1339
1340
1341struct GNUNET_PQ_QueryParam
1342GNUNET_PQ_query_param_array_timestamp (
1343 unsigned int num,
1344 const struct GNUNET_TIME_Timestamp *elements,
1345 struct GNUNET_PQ_Context *db)
1346{
1347 Oid oid;
1348
1349 GNUNET_assert (GNUNET_OK ==
1350 GNUNET_PQ_get_oid_by_name (db,
1351 "int8",
1352 &oid));
1353 return query_param_array_generic (num,
1354 true,
1355 elements,
1356 NULL,
1357 sizeof(struct GNUNET_TIME_Timestamp),
1358 array_of_timestamp,
1359 oid);
1360}
1361
1362
1363struct GNUNET_PQ_QueryParam
1364GNUNET_PQ_query_param_array_ptrs_timestamp (
1365 unsigned int num,
1366 const struct GNUNET_TIME_Timestamp *elements[],
1367 struct GNUNET_PQ_Context *db)
1368{
1369 Oid oid;
1370
1371 GNUNET_assert (GNUNET_OK ==
1372 GNUNET_PQ_get_oid_by_name (db,
1373 "int8",
1374 &oid));
1375 return query_param_array_generic (num,
1376 false,
1377 elements,
1378 NULL,
1379 sizeof(struct GNUNET_TIME_Timestamp),
1380 array_of_timestamp,
1381 oid);
1382}
1383
1384
1385/**
1386 * Function called to convert input argument into SQL parameters.
1387 *
1388 * @param cls closure
1389 * @param data pointer to input argument
1390 * @param data_len number of bytes in @a data (if applicable)
1391 * @param[out] param_values SQL data to set
1392 * @param[out] param_lengths SQL length data to set
1393 * @param[out] param_formats SQL format data to set
1394 * @param param_length number of entries available in the @a param_values, @a param_lengths and @a param_formats arrays
1395 * @param[out] scratch buffer for dynamic allocations (to be done via #GNUNET_malloc()
1396 * @param scratch_length number of entries left in @a scratch
1397 * @return -1 on error, number of offsets used in @a scratch otherwise
1398 */
1399static int
1400qconv_blind_sign_pub (void *cls,
1401 const void *data,
1402 size_t data_len,
1403 void *param_values[],
1404 int param_lengths[],
1405 int param_formats[],
1406 unsigned int param_length,
1407 void *scratch[],
1408 unsigned int scratch_length)
1409{
1410 const struct GNUNET_CRYPTO_BlindSignPublicKey *public_key = data;
1411 size_t tlen;
1412 size_t len;
1413 uint32_t be;
1414 char *buf;
1415 void *tbuf;
1416
1417 (void) cls;
1418 (void) data_len;
1419 GNUNET_assert (1 == param_length);
1420 GNUNET_assert (scratch_length > 0);
1421 GNUNET_break (NULL == cls);
1422 be = htonl ((uint32_t) public_key->cipher);
1423 switch (public_key->cipher)
1424 {
1425 case GNUNET_CRYPTO_BSA_RSA:
1426 tlen = GNUNET_CRYPTO_rsa_public_key_encode (
1427 public_key->details.rsa_public_key,
1428 &tbuf);
1429 break;
1430 case GNUNET_CRYPTO_BSA_CS:
1431 tlen = sizeof (public_key->details.cs_public_key);
1432 break;
1433 default:
1434 GNUNET_assert (0);
1435 }
1436 len = tlen + sizeof (be);
1437 buf = GNUNET_malloc (len);
1438 GNUNET_memcpy (buf,
1439 &be,
1440 sizeof (be));
1441 switch (public_key->cipher)
1442 {
1443 case GNUNET_CRYPTO_BSA_RSA:
1444 GNUNET_memcpy (&buf[sizeof (be)],
1445 tbuf,
1446 tlen);
1447 GNUNET_free (tbuf);
1448 break;
1449 case GNUNET_CRYPTO_BSA_CS:
1450 GNUNET_memcpy (&buf[sizeof (be)],
1451 &public_key->details.cs_public_key,
1452 tlen);
1453 break;
1454 default:
1455 GNUNET_assert (0);
1456 }
1457
1458 scratch[0] = buf;
1459 param_values[0] = (void *) buf;
1460 param_lengths[0] = len;
1461 param_formats[0] = 1;
1462 return 1;
1463}
1464
1465
1466/**
1467 * Generate query parameter for a blind sign public key of variable size.
1468 *
1469 * @param pub pointer to the query parameter to pass
1470 */
1471struct GNUNET_PQ_QueryParam
1472GNUNET_PQ_query_param_blind_sign_pub (
1473 const struct GNUNET_CRYPTO_BlindSignPublicKey *pub)
1474{
1475 struct GNUNET_PQ_QueryParam res = {
1476 .conv = &qconv_blind_sign_pub,
1477 .data = pub,
1478 .num_params = 1
1479 };
1480
1481 return res;
1482}
1483
1484
1485/**
1486 * Function called to convert input argument into SQL parameters.
1487 *
1488 * @param cls closure
1489 * @param data pointer to input argument
1490 * @param data_len number of bytes in @a data (if applicable)
1491 * @param[out] param_values SQL data to set
1492 * @param[out] param_lengths SQL length data to set
1493 * @param[out] param_formats SQL format data to set
1494 * @param param_length number of entries available in the @a param_values, @a param_lengths and @a param_formats arrays
1495 * @param[out] scratch buffer for dynamic allocations (to be done via #GNUNET_malloc()
1496 * @param scratch_length number of entries left in @a scratch
1497 * @return -1 on error, number of offsets used in @a scratch otherwise
1498 */
1499static int
1500qconv_blind_sign_priv (void *cls,
1501 const void *data,
1502 size_t data_len,
1503 void *param_values[],
1504 int param_lengths[],
1505 int param_formats[],
1506 unsigned int param_length,
1507 void *scratch[],
1508 unsigned int scratch_length)
1509{
1510 const struct GNUNET_CRYPTO_BlindSignPrivateKey *private_key = data;
1511 size_t tlen;
1512 size_t len;
1513 uint32_t be;
1514 char *buf;
1515 void *tbuf;
1516
1517 (void) cls;
1518 (void) data_len;
1519 GNUNET_assert (1 == param_length);
1520 GNUNET_assert (scratch_length > 0);
1521 GNUNET_break (NULL == cls);
1522 be = htonl ((uint32_t) private_key->cipher);
1523 switch (private_key->cipher)
1524 {
1525 case GNUNET_CRYPTO_BSA_RSA:
1526 tlen = GNUNET_CRYPTO_rsa_private_key_encode (
1527 private_key->details.rsa_private_key,
1528 &tbuf);
1529 break;
1530 case GNUNET_CRYPTO_BSA_CS:
1531 tlen = sizeof (private_key->details.cs_private_key);
1532 break;
1533 default:
1534 GNUNET_assert (0);
1535 }
1536 len = tlen + sizeof (be);
1537 buf = GNUNET_malloc (len);
1538 GNUNET_memcpy (buf,
1539 &be,
1540 sizeof (be));
1541 switch (private_key->cipher)
1542 {
1543 case GNUNET_CRYPTO_BSA_RSA:
1544 GNUNET_memcpy (&buf[sizeof (be)],
1545 tbuf,
1546 tlen);
1547 GNUNET_free (tbuf);
1548 break;
1549 case GNUNET_CRYPTO_BSA_CS:
1550 GNUNET_memcpy (&buf[sizeof (be)],
1551 &private_key->details.cs_private_key,
1552 tlen);
1553 break;
1554 default:
1555 GNUNET_assert (0);
1556 }
1557
1558 scratch[0] = buf;
1559 param_values[0] = (void *) buf;
1560 param_lengths[0] = len;
1561 param_formats[0] = 1;
1562 return 1;
1563}
1564
1565
1566/**
1567 * Generate query parameter for a blind sign private key of variable size.
1568 *
1569 * @param priv pointer to the query parameter to pass
1570 */
1571struct GNUNET_PQ_QueryParam
1572GNUNET_PQ_query_param_blind_sign_priv (
1573 const struct GNUNET_CRYPTO_BlindSignPrivateKey *priv)
1574{
1575 struct GNUNET_PQ_QueryParam res = {
1576 .conv = &qconv_blind_sign_priv,
1577 .data = priv,
1578 .num_params = 1
1579 };
1580
1581 return res;
1582}
1583
1584
1585/* end of pq_query_helper.c */
diff --git a/src/lib/pq/pq_result_helper.c b/src/lib/pq/pq_result_helper.c
new file mode 100644
index 000000000..cbb1e8e8e
--- /dev/null
+++ b/src/lib/pq/pq_result_helper.c
@@ -0,0 +1,2067 @@
1/*
2 This file is part of GNUnet
3 Copyright (C) 2014-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 * @file pq/pq_result_helper.c
22 * @brief functions to extract result values
23 * @author Christian Grothoff
24 * @author Özgür Kesim
25 */
26#include "platform.h"
27#include "gnunet_time_lib.h"
28#include "gnunet_common.h"
29#include "gnunet_util_lib.h"
30#include "gnunet_pq_lib.h"
31#include "pq.h"
32
33
34struct GNUNET_PQ_ResultSpec
35GNUNET_PQ_result_spec_allow_null (struct GNUNET_PQ_ResultSpec rs,
36 bool *is_null)
37{
38 struct GNUNET_PQ_ResultSpec rsr;
39
40 rsr = rs;
41 rsr.is_nullable = true;
42 rsr.is_null = is_null;
43 return rsr;
44}
45
46
47/**
48 * Function called to clean up memory allocated
49 * by a #GNUNET_PQ_ResultConverter.
50 *
51 * @param cls closure
52 * @param rd result data to clean up
53 */
54static void
55clean_varsize_blob (void *cls,
56 void *rd)
57{
58 void **dst = rd;
59
60 (void) cls;
61 if (NULL != *dst)
62 {
63 GNUNET_free (*dst);
64 *dst = NULL;
65 }
66}
67
68
69/**
70 * Extract data from a Postgres database @a result at row @a row.
71 *
72 * @param cls closure
73 * @param result where to extract data from
74 * @param row row to extract data from
75 * @param fname name (or prefix) of the fields to extract from
76 * @param[in,out] dst_size where to store size of result, may be NULL
77 * @param[out] dst where to store the result
78 * @return
79 * #GNUNET_YES if all results could be extracted
80 * #GNUNET_SYSERR if a result was invalid (non-existing field)
81 */
82static enum GNUNET_GenericReturnValue
83extract_varsize_blob (void *cls,
84 PGresult *result,
85 int row,
86 const char *fname,
87 size_t *dst_size,
88 void *dst)
89{
90 size_t len;
91 const char *res;
92 void *idst;
93 int fnum;
94
95 (void) cls;
96 *dst_size = 0;
97 *((void **) dst) = NULL;
98
99 fnum = PQfnumber (result,
100 fname);
101 if (fnum < 0)
102 {
103 GNUNET_break (0);
104 return GNUNET_SYSERR;
105 }
106 if (PQgetisnull (result,
107 row,
108 fnum))
109 return GNUNET_NO;
110 /* if a field is null, continue but
111 * remember that we now return a different result */
112 len = PQgetlength (result,
113 row,
114 fnum);
115 res = PQgetvalue (result,
116 row,
117 fnum);
118 GNUNET_assert (NULL != res);
119 *dst_size = len;
120 idst = GNUNET_malloc (len);
121 *((void **) dst) = idst;
122 GNUNET_memcpy (idst,
123 res,
124 len);
125 return GNUNET_OK;
126}
127
128
129struct GNUNET_PQ_ResultSpec
130GNUNET_PQ_result_spec_variable_size (const char *name,
131 void **dst,
132 size_t *sptr)
133{
134 struct GNUNET_PQ_ResultSpec res = {
135 .conv = &extract_varsize_blob,
136 .cleaner = &clean_varsize_blob,
137 .dst = (void *) (dst),
138 .fname = name,
139 .result_size = sptr
140 };
141
142 return res;
143}
144
145
146/**
147 * Extract data from a Postgres database @a result at row @a row.
148 *
149 * @param cls closure
150 * @param result where to extract data from
151 * @param row row to extract data from
152 * @param fname name (or prefix) of the fields to extract from
153 * @param[in] dst_size desired size, never NULL
154 * @param[out] dst where to store the result
155 * @return
156 * #GNUNET_YES if all results could be extracted
157 * #GNUNET_SYSERR if a result was invalid (non-existing field or NULL)
158 */
159static enum GNUNET_GenericReturnValue
160extract_fixed_blob (void *cls,
161 PGresult *result,
162 int row,
163 const char *fname,
164 size_t *dst_size,
165 void *dst)
166{
167 size_t len;
168 const char *res;
169 int fnum;
170
171 (void) cls;
172 fnum = PQfnumber (result,
173 fname);
174 if (fnum < 0)
175 {
176 GNUNET_break (0);
177 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
178 "Result does not have field %s\n",
179 fname);
180 return GNUNET_SYSERR;
181 }
182 if (PQgetisnull (result,
183 row,
184 fnum))
185 return GNUNET_NO;
186 /* if a field is null, continue but
187 * remember that we now return a different result */
188 len = PQgetlength (result,
189 row,
190 fnum);
191 if (*dst_size != len)
192 {
193 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
194 "Expected %u bytes for field `%s', got %u\n",
195 (unsigned int) *dst_size,
196 fname,
197 (unsigned int) len);
198 GNUNET_break (0);
199 return GNUNET_SYSERR;
200 }
201 res = PQgetvalue (result,
202 row,
203 fnum);
204 GNUNET_assert (NULL != res);
205 GNUNET_memcpy (dst,
206 res,
207 len);
208 return GNUNET_OK;
209}
210
211
212struct GNUNET_PQ_ResultSpec
213GNUNET_PQ_result_spec_fixed_size (const char *name,
214 void *dst,
215 size_t dst_size)
216{
217 struct GNUNET_PQ_ResultSpec res = {
218 .conv = &extract_fixed_blob,
219 .dst = (dst),
220 .dst_size = dst_size,
221 .fname = name
222 };
223
224 return res;
225}
226
227
228/**
229 * Extract data from a Postgres database @a result at row @a row.
230 *
231 * @param cls closure
232 * @param result where to extract data from
233 * @param row row to extract data from
234 * @param fname name (or prefix) of the fields to extract from
235 * @param[in,out] dst_size where to store size of result, may be NULL
236 * @param[out] dst where to store the result
237 * @return
238 * #GNUNET_YES if all results could be extracted
239 * #GNUNET_SYSERR if a result was invalid (non-existing field or NULL)
240 */
241static enum GNUNET_GenericReturnValue
242extract_rsa_public_key (void *cls,
243 PGresult *result,
244 int row,
245 const char *fname,
246 size_t *dst_size,
247 void *dst)
248{
249 struct GNUNET_CRYPTO_RsaPublicKey **pk = dst;
250 size_t len;
251 const char *res;
252 int fnum;
253
254 (void) cls;
255 *pk = NULL;
256 fnum = PQfnumber (result,
257 fname);
258 if (fnum < 0)
259 {
260 GNUNET_break (0);
261 return GNUNET_SYSERR;
262 }
263 if (PQgetisnull (result,
264 row,
265 fnum))
266 return GNUNET_NO;
267
268 /* if a field is null, continue but
269 * remember that we now return a different result */
270 len = PQgetlength (result,
271 row,
272 fnum);
273 res = PQgetvalue (result,
274 row,
275 fnum);
276 *pk = GNUNET_CRYPTO_rsa_public_key_decode (res,
277 len);
278 if (NULL == *pk)
279 {
280 GNUNET_break (0);
281 return GNUNET_SYSERR;
282 }
283 return GNUNET_OK;
284}
285
286
287/**
288 * Function called to clean up memory allocated
289 * by a #GNUNET_PQ_ResultConverter.
290 *
291 * @param cls closure
292 * @param rd result data to clean up
293 */
294static void
295clean_rsa_public_key (void *cls,
296 void *rd)
297{
298 struct GNUNET_CRYPTO_RsaPublicKey **pk = rd;
299
300 (void) cls;
301 if (NULL != *pk)
302 {
303 GNUNET_CRYPTO_rsa_public_key_free (*pk);
304 *pk = NULL;
305 }
306}
307
308
309struct GNUNET_PQ_ResultSpec
310GNUNET_PQ_result_spec_rsa_public_key (const char *name,
311 struct GNUNET_CRYPTO_RsaPublicKey **rsa)
312{
313 struct GNUNET_PQ_ResultSpec res = {
314 .conv = &extract_rsa_public_key,
315 .cleaner = &clean_rsa_public_key,
316 .dst = (void *) rsa,
317 .fname = name
318 };
319
320 return res;
321}
322
323
324/**
325 * Extract data from a Postgres database @a result at row @a row.
326 *
327 * @param cls closure
328 * @param result where to extract data from
329 * @param row row to extract data from
330 * @param fname name (or prefix) of the fields to extract from
331 * @param[in,out] dst_size where to store size of result, may be NULL
332 * @param[out] dst where to store the result
333 * @return
334 * #GNUNET_YES if all results could be extracted
335 * #GNUNET_SYSERR if a result was invalid (non-existing field or NULL)
336 */
337static enum GNUNET_GenericReturnValue
338extract_rsa_signature (void *cls,
339 PGresult *result,
340 int row,
341 const char *fname,
342 size_t *dst_size,
343 void *dst)
344{
345 struct GNUNET_CRYPTO_RsaSignature **sig = dst;
346 size_t len;
347 const void *res;
348 int fnum;
349
350 (void) cls;
351 *sig = NULL;
352 fnum = PQfnumber (result,
353 fname);
354 if (fnum < 0)
355 {
356 GNUNET_break (0);
357 return GNUNET_SYSERR;
358 }
359 if (PQgetisnull (result,
360 row,
361 fnum))
362 return GNUNET_NO;
363 /* if a field is null, continue but
364 * remember that we now return a different result */
365 len = PQgetlength (result,
366 row,
367 fnum);
368 res = PQgetvalue (result,
369 row,
370 fnum);
371 *sig = GNUNET_CRYPTO_rsa_signature_decode (res,
372 len);
373 if (NULL == *sig)
374 {
375 GNUNET_break (0);
376 return GNUNET_SYSERR;
377 }
378 return GNUNET_OK;
379}
380
381
382/**
383 * Function called to clean up memory allocated
384 * by a #GNUNET_PQ_ResultConverter.
385 *
386 * @param cls closure
387 * @param rd result data to clean up
388 */
389static void
390clean_rsa_signature (void *cls,
391 void *rd)
392{
393 struct GNUNET_CRYPTO_RsaSignature **sig = rd;
394
395 (void) cls;
396 if (NULL != *sig)
397 {
398 GNUNET_CRYPTO_rsa_signature_free (*sig);
399 *sig = NULL;
400 }
401}
402
403
404struct GNUNET_PQ_ResultSpec
405GNUNET_PQ_result_spec_rsa_signature (const char *name,
406 struct GNUNET_CRYPTO_RsaSignature **sig)
407{
408 struct GNUNET_PQ_ResultSpec res = {
409 .conv = &extract_rsa_signature,
410 .cleaner = &clean_rsa_signature,
411 .dst = (void *) sig,
412 .fname = name
413 };
414
415 return res;
416}
417
418
419/**
420 * Extract data from a Postgres database @a result at row @a row.
421 *
422 * @param cls closure
423 * @param result where to extract data from
424 * @param row row to extract data from
425 * @param fname name (or prefix) of the fields to extract from
426 * @param[in,out] dst_size where to store size of result, may be NULL
427 * @param[out] dst where to store the result
428 * @return
429 * #GNUNET_YES if all results could be extracted
430 * #GNUNET_SYSERR if a result was invalid (non-existing field or NULL)
431 */
432static enum GNUNET_GenericReturnValue
433extract_string (void *cls,
434 PGresult *result,
435 int row,
436 const char *fname,
437 size_t *dst_size,
438 void *dst)
439{
440 char **str = dst;
441 size_t len;
442 const char *res;
443 int fnum;
444
445 (void) cls;
446 *str = NULL;
447 fnum = PQfnumber (result,
448 fname);
449 if (fnum < 0)
450 {
451 GNUNET_break (0);
452 return GNUNET_SYSERR;
453 }
454 if (PQgetisnull (result,
455 row,
456 fnum))
457 return GNUNET_NO;
458 /* if a field is null, continue but
459 * remember that we now return a different result */
460 len = PQgetlength (result,
461 row,
462 fnum);
463 res = PQgetvalue (result,
464 row,
465 fnum);
466 *str = GNUNET_strndup (res,
467 len);
468 if (NULL == *str)
469 {
470 GNUNET_break (0);
471 return GNUNET_SYSERR;
472 }
473 return GNUNET_OK;
474}
475
476
477/**
478 * Function called to clean up memory allocated
479 * by a #GNUNET_PQ_ResultConverter.
480 *
481 * @param cls closure
482 * @param rd result data to clean up
483 */
484static void
485clean_string (void *cls,
486 void *rd)
487{
488 char **str = rd;
489
490 (void) cls;
491 if (NULL != *str)
492 {
493 GNUNET_free (*str);
494 *str = NULL;
495 }
496}
497
498
499struct GNUNET_PQ_ResultSpec
500GNUNET_PQ_result_spec_string (const char *name,
501 char **dst)
502{
503 struct GNUNET_PQ_ResultSpec res = {
504 .conv = &extract_string,
505 .cleaner = &clean_string,
506 .dst = (void *) dst,
507 .fname = (name)
508 };
509
510 return res;
511}
512
513
514/**
515 * Extract data from a Postgres database @a result at row @a row.
516 *
517 * @param cls closure
518 * @param result where to extract data from
519 * @param row row to extract data from
520 * @param fname name (or prefix) of the fields to extract from
521 * @param[in,out] dst_size where to store size of result, may be NULL
522 * @param[out] dst where to store the result
523 * @return
524 * #GNUNET_YES if all results could be extracted
525 * #GNUNET_SYSERR if a result was invalid (non-existing field or NULL)
526 */
527static enum GNUNET_GenericReturnValue
528extract_bool (void *cls,
529 PGresult *result,
530 int row,
531 const char *fname,
532 size_t *dst_size,
533 void *dst)
534{
535 bool *b = dst;
536 const uint8_t *res;
537 int fnum;
538 size_t len;
539
540 (void) cls;
541 fnum = PQfnumber (result,
542 fname);
543 if (fnum < 0)
544 {
545 GNUNET_break (0);
546 return GNUNET_SYSERR;
547 }
548 if (PQgetisnull (result,
549 row,
550 fnum))
551 return GNUNET_NO;
552 /* if a field is null, continue but
553 * remember that we now return a different result */
554 len = PQgetlength (result,
555 row,
556 fnum);
557 if (sizeof (uint8_t) != len)
558 {
559 GNUNET_break (0);
560 return GNUNET_SYSERR;
561 }
562 res = (const uint8_t *) PQgetvalue (result,
563 row,
564 fnum);
565 *b = (0 != *res);
566 return GNUNET_OK;
567}
568
569
570struct GNUNET_PQ_ResultSpec
571GNUNET_PQ_result_spec_bool (const char *name,
572 bool *dst)
573{
574 struct GNUNET_PQ_ResultSpec res = {
575 .conv = &extract_bool,
576 .dst = (void *) dst,
577 .fname = name
578 };
579
580 return res;
581}
582
583
584/**
585 * Extract data from a Postgres database @a result at row @a row.
586 *
587 * @param cls closure
588 * @param result where to extract data from
589 * @param row row to extract data from
590 * @param fname name (or prefix) of the fields to extract from
591 * @param[in,out] dst_size where to store size of result, may be NULL
592 * @param[out] dst where to store the result
593 * @return
594 * #GNUNET_YES if all results could be extracted
595 * #GNUNET_SYSERR if a result was invalid (non-existing field or NULL)
596 */
597static enum GNUNET_GenericReturnValue
598extract_rel_time (void *cls,
599 PGresult *result,
600 int row,
601 const char *fname,
602 size_t *dst_size,
603 void *dst)
604{
605 struct GNUNET_TIME_Relative *udst = dst;
606 const int64_t *res;
607 int fnum;
608
609 (void) cls;
610 fnum = PQfnumber (result,
611 fname);
612 if (fnum < 0)
613 {
614 GNUNET_break (0);
615 return GNUNET_SYSERR;
616 }
617 if (PQgetisnull (result,
618 row,
619 fnum))
620 return GNUNET_NO;
621 GNUNET_assert (NULL != dst);
622 if (sizeof(struct GNUNET_TIME_Relative) != *dst_size)
623 {
624 GNUNET_break (0);
625 return GNUNET_SYSERR;
626 }
627 if (sizeof(int64_t) !=
628 PQgetlength (result,
629 row,
630 fnum))
631 {
632 GNUNET_break (0);
633 return GNUNET_SYSERR;
634 }
635 res = (int64_t *) PQgetvalue (result,
636 row,
637 fnum);
638 if (INT64_MAX == GNUNET_ntohll ((uint64_t) *res))
639 *udst = GNUNET_TIME_UNIT_FOREVER_REL;
640 else
641 udst->rel_value_us = GNUNET_ntohll ((uint64_t) *res);
642 return GNUNET_OK;
643}
644
645
646struct GNUNET_PQ_ResultSpec
647GNUNET_PQ_result_spec_relative_time (const char *name,
648 struct GNUNET_TIME_Relative *rt)
649{
650 struct GNUNET_PQ_ResultSpec res = {
651 .conv = &extract_rel_time,
652 .dst = (void *) rt,
653 .dst_size = sizeof(*rt),
654 .fname = name
655 };
656
657 return res;
658}
659
660
661/**
662 * Extract data from a Postgres database @a result at row @a row.
663 *
664 * @param cls closure
665 * @param result where to extract data from
666 * @param row row to extract data from
667 * @param fname name (or prefix) of the fields to extract from
668 * @param[in,out] dst_size where to store size of result, may be NULL
669 * @param[out] dst where to store the result
670 * @return
671 * #GNUNET_YES if all results could be extracted
672 * #GNUNET_SYSERR if a result was invalid (non-existing field or NULL)
673 */
674static enum GNUNET_GenericReturnValue
675extract_abs_time (void *cls,
676 PGresult *result,
677 int row,
678 const char *fname,
679 size_t *dst_size,
680 void *dst)
681{
682 struct GNUNET_TIME_Absolute *udst = dst;
683 const int64_t *res;
684 int fnum;
685
686 (void) cls;
687 fnum = PQfnumber (result,
688 fname);
689 if (fnum < 0)
690 {
691 GNUNET_break (0);
692 return GNUNET_SYSERR;
693 }
694 if (PQgetisnull (result,
695 row,
696 fnum))
697 return GNUNET_NO;
698 GNUNET_assert (NULL != dst);
699 if (sizeof(struct GNUNET_TIME_Absolute) != *dst_size)
700 {
701 GNUNET_break (0);
702 return GNUNET_SYSERR;
703 }
704 if (sizeof(int64_t) !=
705 PQgetlength (result,
706 row,
707 fnum))
708 {
709 GNUNET_break (0);
710 return GNUNET_SYSERR;
711 }
712 res = (int64_t *) PQgetvalue (result,
713 row,
714 fnum);
715 if (INT64_MAX == GNUNET_ntohll ((uint64_t) *res))
716 *udst = GNUNET_TIME_UNIT_FOREVER_ABS;
717 else
718 udst->abs_value_us = GNUNET_ntohll ((uint64_t) *res);
719 return GNUNET_OK;
720}
721
722
723struct GNUNET_PQ_ResultSpec
724GNUNET_PQ_result_spec_absolute_time (const char *name,
725 struct GNUNET_TIME_Absolute *at)
726{
727 struct GNUNET_PQ_ResultSpec res = {
728 .conv = &extract_abs_time,
729 .dst = (void *) at,
730 .dst_size = sizeof(*at),
731 .fname = name
732 };
733
734 return res;
735}
736
737
738struct GNUNET_PQ_ResultSpec
739GNUNET_PQ_result_spec_absolute_time_nbo (const char *name,
740 struct GNUNET_TIME_AbsoluteNBO *at)
741{
742 struct GNUNET_PQ_ResultSpec res =
743 GNUNET_PQ_result_spec_auto_from_type (name,
744 &at->abs_value_us__);
745
746 return res;
747}
748
749
750/**
751 * Extract data from a Postgres database @a result at row @a row.
752 *
753 * @param cls closure
754 * @param result where to extract data from
755 * @param row row to extract data from
756 * @param fname name (or prefix) of the fields to extract from
757 * @param[in,out] dst_size where to store size of result, may be NULL
758 * @param[out] dst where to store the result
759 * @return
760 * #GNUNET_YES if all results could be extracted
761 * #GNUNET_SYSERR if a result was invalid (non-existing field or NULL)
762 */
763static enum GNUNET_GenericReturnValue
764extract_timestamp (void *cls,
765 PGresult *result,
766 int row,
767 const char *fname,
768 size_t *dst_size,
769 void *dst)
770{
771 struct GNUNET_TIME_Timestamp *udst = dst;
772 struct GNUNET_TIME_Absolute abs;
773 const int64_t *res;
774 int fnum;
775
776 (void) cls;
777 fnum = PQfnumber (result,
778 fname);
779 if (fnum < 0)
780 {
781 GNUNET_break (0);
782 return GNUNET_SYSERR;
783 }
784 if (PQgetisnull (result,
785 row,
786 fnum))
787 return GNUNET_NO;
788 GNUNET_assert (NULL != dst);
789 if (sizeof(struct GNUNET_TIME_Absolute) != *dst_size)
790 {
791 GNUNET_break (0);
792 return GNUNET_SYSERR;
793 }
794 if (sizeof(int64_t) !=
795 PQgetlength (result,
796 row,
797 fnum))
798 {
799 GNUNET_break (0);
800 return GNUNET_SYSERR;
801 }
802 res = (int64_t *) PQgetvalue (result,
803 row,
804 fnum);
805 if (INT64_MAX == GNUNET_ntohll ((uint64_t) *res))
806 {
807 abs = GNUNET_TIME_UNIT_FOREVER_ABS;
808 }
809 else
810 {
811 abs.abs_value_us = GNUNET_ntohll ((uint64_t) *res);
812 if (0 != abs.abs_value_us % GNUNET_TIME_UNIT_SECONDS.rel_value_us)
813 {
814 /* timestamps must be multiple of seconds! */
815 GNUNET_break (0);
816 return GNUNET_SYSERR;
817 }
818 }
819 udst->abs_time = abs;
820 return GNUNET_OK;
821}
822
823
824struct GNUNET_PQ_ResultSpec
825GNUNET_PQ_result_spec_timestamp (const char *name,
826 struct GNUNET_TIME_Timestamp *at)
827{
828 struct GNUNET_PQ_ResultSpec res = {
829 .conv = &extract_timestamp,
830 .dst = (void *) at,
831 .dst_size = sizeof(*at),
832 .fname = name
833 };
834
835 return res;
836}
837
838
839/**
840 * Extract data from a Postgres database @a result at row @a row.
841 *
842 * @param cls closure
843 * @param result where to extract data from
844 * @param row row to extract data from
845 * @param fname name (or prefix) of the fields to extract from
846 * @param[in,out] dst_size where to store size of result, may be NULL
847 * @param[out] dst where to store the result
848 * @return
849 * #GNUNET_YES if all results could be extracted
850 * #GNUNET_SYSERR if a result was invalid (non-existing field or NULL)
851 */
852static enum GNUNET_GenericReturnValue
853extract_timestamp_nbo (void *cls,
854 PGresult *result,
855 int row,
856 const char *fname,
857 size_t *dst_size,
858 void *dst)
859{
860 struct GNUNET_TIME_TimestampNBO *udst = dst;
861 struct GNUNET_TIME_Timestamp t;
862 enum GNUNET_GenericReturnValue r;
863
864 r = extract_timestamp (NULL,
865 result,
866 row,
867 fname,
868 dst_size,
869 &t);
870 if (GNUNET_OK != r)
871 return r;
872 *udst = GNUNET_TIME_timestamp_hton (t);
873 return r;
874}
875
876
877struct GNUNET_PQ_ResultSpec
878GNUNET_PQ_result_spec_timestamp_nbo (const char *name,
879 struct GNUNET_TIME_TimestampNBO *at)
880{
881 struct GNUNET_PQ_ResultSpec res = {
882 .conv = &extract_timestamp_nbo,
883 .dst = (void *) at,
884 .dst_size = sizeof(*at),
885 .fname = name
886 };
887
888 return res;
889}
890
891
892/**
893 * Extract data from a Postgres database @a result at row @a row.
894 *
895 * @param cls closure
896 * @param result where to extract data from
897 * @param row row to extract data from
898 * @param fname name (or prefix) of the fields to extract from
899 * @param[in,out] dst_size where to store size of result, may be NULL
900 * @param[out] dst where to store the result
901 * @return
902 * #GNUNET_YES if all results could be extracted
903 * #GNUNET_SYSERR if a result was invalid (non-existing field or NULL)
904 */
905static enum GNUNET_GenericReturnValue
906extract_uint16 (void *cls,
907 PGresult *result,
908 int row,
909 const char *fname,
910 size_t *dst_size,
911 void *dst)
912{
913 uint16_t *udst = dst;
914 const uint16_t *res;
915 int fnum;
916
917 (void) cls;
918 fnum = PQfnumber (result,
919 fname);
920 if (fnum < 0)
921 {
922 GNUNET_break (0);
923 return GNUNET_SYSERR;
924 }
925 if (PQgetisnull (result,
926 row,
927 fnum))
928 return GNUNET_NO;
929 GNUNET_assert (NULL != dst);
930 if (sizeof(uint16_t) != *dst_size)
931 {
932 GNUNET_break (0);
933 return GNUNET_SYSERR;
934 }
935 if (sizeof(uint16_t) !=
936 PQgetlength (result,
937 row,
938 fnum))
939 {
940 GNUNET_break (0);
941 return GNUNET_SYSERR;
942 }
943 res = (uint16_t *) PQgetvalue (result,
944 row,
945 fnum);
946 *udst = ntohs (*res);
947 return GNUNET_OK;
948}
949
950
951struct GNUNET_PQ_ResultSpec
952GNUNET_PQ_result_spec_uint16 (const char *name,
953 uint16_t *u16)
954{
955 struct GNUNET_PQ_ResultSpec res = {
956 .conv = &extract_uint16,
957 .dst = (void *) u16,
958 .dst_size = sizeof(*u16),
959 .fname = name
960 };
961
962 return res;
963}
964
965
966/**
967 * Extract data from a Postgres database @a result at row @a row.
968 *
969 * @param cls closure
970 * @param result where to extract data from
971 * @param row row to extract data from
972 * @param fname name (or prefix) of the fields to extract from
973 * @param[in,out] dst_size where to store size of result, may be NULL
974 * @param[out] dst where to store the result
975 * @return
976 * #GNUNET_YES if all results could be extracted
977 * #GNUNET_SYSERR if a result was invalid (non-existing field or NULL)
978 */
979static enum GNUNET_GenericReturnValue
980extract_uint32 (void *cls,
981 PGresult *result,
982 int row,
983 const char *fname,
984 size_t *dst_size,
985 void *dst)
986{
987 uint32_t *udst = dst;
988 const uint32_t *res;
989 int fnum;
990
991 (void) cls;
992 fnum = PQfnumber (result,
993 fname);
994 if (fnum < 0)
995 {
996 GNUNET_break (0);
997 return GNUNET_SYSERR;
998 }
999 if (PQgetisnull (result,
1000 row,
1001 fnum))
1002 return GNUNET_NO;
1003 GNUNET_assert (NULL != dst);
1004 if (sizeof(uint32_t) != *dst_size)
1005 {
1006 GNUNET_break (0);
1007 return GNUNET_SYSERR;
1008 }
1009 if (sizeof(uint32_t) !=
1010 PQgetlength (result,
1011 row,
1012 fnum))
1013 {
1014 GNUNET_break (0);
1015 return GNUNET_SYSERR;
1016 }
1017 res = (uint32_t *) PQgetvalue (result,
1018 row,
1019 fnum);
1020 *udst = ntohl (*res);
1021 return GNUNET_OK;
1022}
1023
1024
1025struct GNUNET_PQ_ResultSpec
1026GNUNET_PQ_result_spec_uint32 (const char *name,
1027 uint32_t *u32)
1028{
1029 struct GNUNET_PQ_ResultSpec res = {
1030 .conv = &extract_uint32,
1031 .dst = (void *) u32,
1032 .dst_size = sizeof(*u32),
1033 .fname = name
1034 };
1035
1036 return res;
1037}
1038
1039
1040/**
1041 * Extract data from a Postgres database @a result at row @a row.
1042 *
1043 * @param cls closure
1044 * @param result where to extract data from
1045 * @param row row to extract data from
1046 * @param fname name (or prefix) of the fields to extract from
1047 * @param[in,out] dst_size where to store size of result, may be NULL
1048 * @param[out] dst where to store the result
1049 * @return
1050 * #GNUNET_YES if all results could be extracted
1051 * #GNUNET_SYSERR if a result was invalid (non-existing field or NULL)
1052 */
1053static enum GNUNET_GenericReturnValue
1054extract_uint64 (void *cls,
1055 PGresult *result,
1056 int row,
1057 const char *fname,
1058 size_t *dst_size,
1059 void *dst)
1060{
1061 uint64_t *udst = dst;
1062 const uint64_t *res;
1063 int fnum;
1064
1065 (void) cls;
1066 fnum = PQfnumber (result,
1067 fname);
1068 if (fnum < 0)
1069 {
1070 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
1071 "Field %s missing in result\n",
1072 fname);
1073 GNUNET_break (0);
1074 return GNUNET_SYSERR;
1075 }
1076 if (PQgetisnull (result,
1077 row,
1078 fnum))
1079 return GNUNET_NO;
1080
1081 GNUNET_assert (NULL != dst);
1082 if (sizeof(uint64_t) != *dst_size)
1083 {
1084 GNUNET_break (0);
1085 return GNUNET_SYSERR;
1086 }
1087 if (sizeof(uint64_t) !=
1088 PQgetlength (result,
1089 row,
1090 fnum))
1091 {
1092 GNUNET_break (0);
1093 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
1094 "Got length %u for field `%s'\n",
1095 PQgetlength (result,
1096 row,
1097 fnum),
1098 fname);
1099 return GNUNET_SYSERR;
1100 }
1101 res = (uint64_t *) PQgetvalue (result,
1102 row,
1103 fnum);
1104 *udst = GNUNET_ntohll (*res);
1105 return GNUNET_OK;
1106}
1107
1108
1109struct GNUNET_PQ_ResultSpec
1110GNUNET_PQ_result_spec_uint64 (const char *name,
1111 uint64_t *u64)
1112{
1113 struct GNUNET_PQ_ResultSpec res = {
1114 .conv = &extract_uint64,
1115 .dst = (void *) u64,
1116 .dst_size = sizeof(*u64),
1117 .fname = name
1118 };
1119
1120 return res;
1121}
1122
1123
1124/**
1125 * Extract data from a Postgres database @a result at row @a row.
1126 *
1127 * @param cls closure
1128 * @param result where to extract data from
1129 * @param row row to extract data from
1130 * @param fname name (or prefix) of the fields to extract from
1131 * @param[in,out] dst_size where to store size of result, may be NULL
1132 * @param[out] dst where to store the result
1133 * @return
1134 * #GNUNET_YES if all results could be extracted
1135 * #GNUNET_SYSERR if a result was invalid (non-existing field or NULL)
1136 */
1137static enum GNUNET_GenericReturnValue
1138extract_int64 (void *cls,
1139 PGresult *result,
1140 int row,
1141 const char *fname,
1142 size_t *dst_size,
1143 void *dst)
1144{
1145 int64_t *udst = dst;
1146 const int64_t *res;
1147 int fnum;
1148
1149 (void) cls;
1150 fnum = PQfnumber (result,
1151 fname);
1152 if (fnum < 0)
1153 {
1154 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
1155 "Field %s missing in result\n",
1156 fname);
1157 GNUNET_break (0);
1158 return GNUNET_SYSERR;
1159 }
1160 if (PQgetisnull (result,
1161 row,
1162 fnum))
1163 return GNUNET_NO;
1164
1165 GNUNET_assert (NULL != dst);
1166 if (sizeof(int64_t) != *dst_size)
1167 {
1168 GNUNET_break (0);
1169 return GNUNET_SYSERR;
1170 }
1171 if (sizeof(int64_t) !=
1172 PQgetlength (result,
1173 row,
1174 fnum))
1175 {
1176 GNUNET_break (0);
1177 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
1178 "Got length %u for field `%s'\n",
1179 PQgetlength (result,
1180 row,
1181 fnum),
1182 fname);
1183 return GNUNET_SYSERR;
1184 }
1185 res = (int64_t *) PQgetvalue (result,
1186 row,
1187 fnum);
1188 *udst = GNUNET_ntohll (*res);
1189 return GNUNET_OK;
1190}
1191
1192
1193struct GNUNET_PQ_ResultSpec
1194GNUNET_PQ_result_spec_int64 (const char *name,
1195 int64_t *i64)
1196{
1197 struct GNUNET_PQ_ResultSpec res = {
1198 .conv = &extract_int64,
1199 .dst = (void *) i64,
1200 .dst_size = sizeof(*i64),
1201 .fname = name
1202 };
1203
1204 return res;
1205}
1206
1207
1208/**
1209 * Closure for the array result specifications. Contains type information
1210 * for the generic parser extract_array_generic and out-pointers for the results.
1211 */
1212struct array_result_cls
1213{
1214 /* Oid of the expected type, must match the oid in the header of the PQResult struct */
1215 Oid oid;
1216
1217 /* Target type */
1218 enum array_types typ;
1219
1220 /* If not 0, defines the expected size of each entry */
1221 size_t same_size;
1222
1223 /* Out-pointer to write the number of elements in the array */
1224 size_t *num;
1225
1226 /* Out-pointer. If @a typ is array_of_byte and @a same_size is 0,
1227 * allocate and put the array of @a num sizes here. NULL otherwise */
1228 size_t **sizes;
1229};
1230
1231
1232/**
1233 * Extract data from a Postgres database @a result as array of a specific type
1234 * from row @a row. The type information and optionally additional
1235 * out-parameters are given in @a cls which is of type array_result_cls.
1236 *
1237 * @param cls closure of type array_result_cls
1238 * @param result where to extract data from
1239 * @param row row to extract data from
1240 * @param fname name (or prefix) of the fields to extract from
1241 * @param[in,out] dst_size where to store size of result, may be NULL
1242 * @param[out] dst where to store the result
1243 * @return
1244 * #GNUNET_YES if all results could be extracted
1245 * #GNUNET_SYSERR if a result was invalid (non-existing field or NULL)
1246 */
1247static enum GNUNET_GenericReturnValue
1248extract_array_generic (
1249 void *cls,
1250 PGresult *result,
1251 int row,
1252 const char *fname,
1253 size_t *dst_size,
1254 void *dst)
1255{
1256 const struct array_result_cls *info = cls;
1257 int data_sz;
1258 char *data;
1259 void *out = NULL;
1260 struct pq_array_header header;
1261 int col_num;
1262
1263 GNUNET_assert (NULL != dst);
1264 *((void **) dst) = NULL;
1265
1266 #define FAIL_IF(cond) \
1267 do { \
1268 if ((cond)) \
1269 { \
1270 GNUNET_break (! (cond)); \
1271 goto FAIL; \
1272 } \
1273 } while (0)
1274
1275 col_num = PQfnumber (result, fname);
1276 FAIL_IF (0 > col_num);
1277
1278 data_sz = PQgetlength (result, row, col_num);
1279 FAIL_IF (0 > data_sz);
1280 FAIL_IF (sizeof(header) > (size_t) data_sz);
1281
1282 data = PQgetvalue (result, row, col_num);
1283 FAIL_IF (NULL == data);
1284
1285 {
1286 struct pq_array_header *h =
1287 (struct pq_array_header *) data;
1288
1289 header.ndim = ntohl (h->ndim);
1290 header.has_null = ntohl (h->has_null);
1291 header.oid = ntohl (h->oid);
1292 header.dim = ntohl (h->dim);
1293 header.lbound = ntohl (h->lbound);
1294
1295 FAIL_IF (1 != header.ndim);
1296 FAIL_IF (INT_MAX <= header.dim);
1297 FAIL_IF (0 != header.has_null);
1298 FAIL_IF (1 != header.lbound);
1299 FAIL_IF (info->oid != header.oid);
1300 }
1301
1302 if (NULL != info->num)
1303 *info->num = header.dim;
1304
1305 {
1306 char *in = data + sizeof(header);
1307
1308 switch (info->typ)
1309 {
1310 case array_of_bool:
1311 if (NULL != dst_size)
1312 *dst_size = sizeof(bool) * (header.dim);
1313 out = GNUNET_new_array (header.dim, bool);
1314 *((void **) dst) = out;
1315 for (uint32_t i = 0; i < header.dim; i++)
1316 {
1317 size_t sz = ntohl (*(uint32_t *) in);
1318 FAIL_IF (sz != sizeof(bool));
1319 in += sizeof(uint32_t);
1320 *(bool *) out = *(bool *) in;
1321 in += sz;
1322 out += sz;
1323 }
1324 break;
1325
1326 case array_of_uint16:
1327 if (NULL != dst_size)
1328 *dst_size = sizeof(uint16_t) * (header.dim);
1329 out = GNUNET_new_array (header.dim, uint16_t);
1330 *((void **) dst) = out;
1331 for (uint32_t i = 0; i < header.dim; i++)
1332 {
1333 size_t sz = ntohl (*(uint32_t *) in);
1334 FAIL_IF (sz != sizeof(uint16_t));
1335 in += sizeof(uint32_t);
1336 *(uint16_t *) out = ntohs (*(uint16_t *) in);
1337 in += sz;
1338 out += sz;
1339 }
1340 break;
1341
1342 case array_of_uint32:
1343 if (NULL != dst_size)
1344 *dst_size = sizeof(uint32_t) * (header.dim);
1345 out = GNUNET_new_array (header.dim, uint32_t);
1346 *((void **) dst) = out;
1347 for (uint32_t i = 0; i < header.dim; i++)
1348 {
1349 size_t sz = ntohl (*(uint32_t *) in);
1350 FAIL_IF (sz != sizeof(uint32_t));
1351 in += sizeof(uint32_t);
1352 *(uint32_t *) out = ntohl (*(uint32_t *) in);
1353 in += sz;
1354 out += sz;
1355 }
1356 break;
1357
1358 case array_of_uint64:
1359 if (NULL != dst_size)
1360 *dst_size = sizeof(uint64_t) * (header.dim);
1361 out = GNUNET_new_array (header.dim, uint64_t);
1362 *((void **) dst) = out;
1363 for (uint32_t i = 0; i < header.dim; i++)
1364 {
1365 size_t sz = ntohl (*(uint32_t *) in);
1366 FAIL_IF (sz != sizeof(uint64_t));
1367 in += sizeof(uint32_t);
1368 *(uint64_t *) out = GNUNET_ntohll (*(uint64_t *) in);
1369 in += sz;
1370 out += sz;
1371 }
1372 break;
1373
1374 case array_of_abs_time:
1375 if (NULL != dst_size)
1376 *dst_size = sizeof(struct GNUNET_TIME_Absolute) * (header.dim);
1377 out = GNUNET_new_array (header.dim, struct GNUNET_TIME_Absolute);
1378 *((void **) dst) = out;
1379 for (uint32_t i = 0; i < header.dim; i++)
1380 {
1381 size_t sz = ntohl (*(uint32_t *) in);
1382 FAIL_IF (sz != sizeof(uint64_t));
1383 in += sizeof(uint32_t);
1384 ((struct GNUNET_TIME_Absolute *) out)->abs_value_us =
1385 GNUNET_ntohll (*(uint64_t *) in);
1386 in += sz;
1387 out += sz;
1388 }
1389 break;
1390
1391 case array_of_rel_time:
1392 if (NULL != dst_size)
1393 *dst_size = sizeof(struct GNUNET_TIME_Relative) * (header.dim);
1394 out = GNUNET_new_array (header.dim, struct GNUNET_TIME_Relative);
1395 *((void **) dst) = out;
1396 for (uint32_t i = 0; i < header.dim; i++)
1397 {
1398 size_t sz = ntohl (*(uint32_t *) in);
1399 FAIL_IF (sz != sizeof(uint64_t));
1400 in += sizeof(uint32_t);
1401 ((struct GNUNET_TIME_Relative *) out)->rel_value_us =
1402 GNUNET_ntohll (*(uint64_t *) in);
1403 in += sz;
1404 out += sz;
1405 }
1406 break;
1407
1408 case array_of_timestamp:
1409 if (NULL != dst_size)
1410 *dst_size = sizeof(struct GNUNET_TIME_Timestamp) * (header.dim);
1411 out = GNUNET_new_array (header.dim, struct GNUNET_TIME_Timestamp);
1412 *((void **) dst) = out;
1413 for (uint32_t i = 0; i < header.dim; i++)
1414 {
1415 size_t sz = ntohl (*(uint32_t *) in);
1416 FAIL_IF (sz != sizeof(uint64_t));
1417 in += sizeof(uint32_t);
1418 ((struct GNUNET_TIME_Timestamp *) out)->abs_time.abs_value_us =
1419 GNUNET_ntohll (*(uint64_t *) in);
1420 in += sz;
1421 out += sz;
1422 }
1423 break;
1424
1425 case array_of_byte:
1426 if (0 == info->same_size)
1427 *info->sizes = GNUNET_new_array (header.dim, size_t);
1428 /* fallthrough */
1429 case array_of_string:
1430 {
1431 size_t total = 0;
1432 bool is_string = (array_of_string == info->typ);
1433
1434 /* first, calculate total size required for allocation */
1435 {
1436 char *ptr = data + sizeof(header);
1437 for (uint32_t i = 0; i < header.dim; i++)
1438 {
1439 uint32_t sz;
1440
1441 sz = ntohl (*(uint32_t *) ptr);
1442 sz += is_string ? 1 : 0;
1443 total += sz;
1444 ptr += sizeof(uint32_t);
1445 ptr += sz;
1446
1447 if ((! is_string) &&
1448 (0 == info->same_size))
1449 (*info->sizes)[i] = sz;
1450
1451 FAIL_IF ((0 != info->same_size) &&
1452 (sz != info->same_size));
1453 FAIL_IF (total < sz);
1454 }
1455 }
1456
1457 if (NULL != dst_size)
1458 *dst_size = total;
1459
1460 FAIL_IF (0 == total);
1461 out = GNUNET_malloc (total);
1462
1463 *((void **) dst) = out;
1464
1465 /* copy data */
1466 for (uint32_t i = 0; i < header.dim; i++)
1467 {
1468 size_t sz = ntohl (*(uint32_t *) in);
1469 in += sizeof(uint32_t);
1470 GNUNET_memcpy (out, in, sz);
1471
1472 in += sz;
1473 out += sz;
1474 out += (array_of_string == info->typ) ? 1 : 0;
1475 }
1476 break;
1477 }
1478 default:
1479 FAIL_IF (1 != 0);
1480 }
1481 }
1482
1483 return GNUNET_OK;
1484
1485FAIL:
1486 GNUNET_free (*(void **) dst);
1487 return GNUNET_SYSERR;
1488 #undef FAIL_IF
1489}
1490
1491
1492/**
1493 * Cleanup of the data and closure of an array spec.
1494 */
1495static void
1496array_cleanup (void *cls,
1497 void *rd)
1498{
1499
1500 struct array_result_cls *info = cls;
1501 void **dst = rd;
1502
1503 if ((array_of_byte == info->typ) &&
1504 (0 == info->same_size) &&
1505 (NULL != info->sizes))
1506 GNUNET_free (*(info->sizes));
1507
1508 GNUNET_free (cls);
1509 GNUNET_free (*dst);
1510 *dst = NULL;
1511}
1512
1513
1514struct GNUNET_PQ_ResultSpec
1515GNUNET_PQ_result_spec_array_bool (
1516 struct GNUNET_PQ_Context *db,
1517 const char *name,
1518 size_t *num,
1519 bool **dst)
1520{
1521 struct array_result_cls *info =
1522 GNUNET_new (struct array_result_cls);
1523
1524 info->num = num;
1525 info->typ = array_of_bool;
1526 GNUNET_assert (GNUNET_OK ==
1527 GNUNET_PQ_get_oid_by_name (db,
1528 "bool",
1529 &info->oid));
1530
1531 struct GNUNET_PQ_ResultSpec res = {
1532 .conv = extract_array_generic,
1533 .cleaner = array_cleanup,
1534 .dst = (void *) dst,
1535 .fname = name,
1536 .cls = info
1537 };
1538 return res;
1539}
1540
1541
1542struct GNUNET_PQ_ResultSpec
1543GNUNET_PQ_result_spec_array_uint16 (
1544 struct GNUNET_PQ_Context *db,
1545 const char *name,
1546 size_t *num,
1547 uint16_t **dst)
1548{
1549 struct array_result_cls *info =
1550 GNUNET_new (struct array_result_cls);
1551
1552 info->num = num;
1553 info->typ = array_of_uint16;
1554 GNUNET_assert (GNUNET_OK ==
1555 GNUNET_PQ_get_oid_by_name (db,
1556 "int2",
1557 &info->oid));
1558
1559 struct GNUNET_PQ_ResultSpec res = {
1560 .conv = extract_array_generic,
1561 .cleaner = array_cleanup,
1562 .dst = (void *) dst,
1563 .fname = name,
1564 .cls = info
1565 };
1566 return res;
1567}
1568
1569
1570struct GNUNET_PQ_ResultSpec
1571GNUNET_PQ_result_spec_array_uint32 (
1572 struct GNUNET_PQ_Context *db,
1573 const char *name,
1574 size_t *num,
1575 uint32_t **dst)
1576{
1577 struct array_result_cls *info =
1578 GNUNET_new (struct array_result_cls);
1579
1580 info->num = num;
1581 info->typ = array_of_uint32;
1582 GNUNET_assert (GNUNET_OK ==
1583 GNUNET_PQ_get_oid_by_name (db,
1584 "int4",
1585 &info->oid));
1586
1587 struct GNUNET_PQ_ResultSpec res = {
1588 .conv = extract_array_generic,
1589 .cleaner = array_cleanup,
1590 .dst = (void *) dst,
1591 .fname = name,
1592 .cls = info
1593 };
1594 return res;
1595}
1596
1597
1598struct GNUNET_PQ_ResultSpec
1599GNUNET_PQ_result_spec_array_uint64 (
1600 struct GNUNET_PQ_Context *db,
1601 const char *name,
1602 size_t *num,
1603 uint64_t **dst)
1604{
1605 struct array_result_cls *info =
1606 GNUNET_new (struct array_result_cls);
1607
1608 info->num = num;
1609 info->typ = array_of_uint64;
1610 GNUNET_assert (GNUNET_OK ==
1611 GNUNET_PQ_get_oid_by_name (db,
1612 "int8",
1613 &info->oid));
1614
1615 struct GNUNET_PQ_ResultSpec res = {
1616 .conv = extract_array_generic,
1617 .cleaner = array_cleanup,
1618 .dst = (void *) dst,
1619 .fname = name,
1620 .cls = info
1621 };
1622 return res;
1623}
1624
1625
1626struct GNUNET_PQ_ResultSpec
1627GNUNET_PQ_result_spec_array_abs_time (
1628 struct GNUNET_PQ_Context *db,
1629 const char *name,
1630 size_t *num,
1631 struct GNUNET_TIME_Absolute **dst)
1632{
1633 struct array_result_cls *info =
1634 GNUNET_new (struct array_result_cls);
1635
1636 info->num = num;
1637 info->typ = array_of_abs_time;
1638 GNUNET_assert (GNUNET_OK ==
1639 GNUNET_PQ_get_oid_by_name (db,
1640 "int8",
1641 &info->oid));
1642
1643 struct GNUNET_PQ_ResultSpec res = {
1644 .conv = extract_array_generic,
1645 .cleaner = array_cleanup,
1646 .dst = (void *) dst,
1647 .fname = name,
1648 .cls = info
1649 };
1650 return res;
1651}
1652
1653
1654struct GNUNET_PQ_ResultSpec
1655GNUNET_PQ_result_spec_array_rel_time (
1656 struct GNUNET_PQ_Context *db,
1657 const char *name,
1658 size_t *num,
1659 struct GNUNET_TIME_Relative **dst)
1660{
1661 struct array_result_cls *info =
1662 GNUNET_new (struct array_result_cls);
1663
1664 info->num = num;
1665 info->typ = array_of_rel_time;
1666 GNUNET_assert (GNUNET_OK ==
1667 GNUNET_PQ_get_oid_by_name (db,
1668 "int8",
1669 &info->oid));
1670
1671 struct GNUNET_PQ_ResultSpec res = {
1672 .conv = extract_array_generic,
1673 .cleaner = array_cleanup,
1674 .dst = (void *) dst,
1675 .fname = name,
1676 .cls = info
1677 };
1678 return res;
1679}
1680
1681
1682struct GNUNET_PQ_ResultSpec
1683GNUNET_PQ_result_spec_array_timestamp (
1684 struct GNUNET_PQ_Context *db,
1685 const char *name,
1686 size_t *num,
1687 struct GNUNET_TIME_Timestamp **dst)
1688{
1689 struct array_result_cls *info =
1690 GNUNET_new (struct array_result_cls);
1691
1692 info->num = num;
1693 info->typ = array_of_timestamp;
1694 GNUNET_assert (GNUNET_OK ==
1695 GNUNET_PQ_get_oid_by_name (db,
1696 "int8",
1697 &info->oid));
1698
1699 struct GNUNET_PQ_ResultSpec res = {
1700 .conv = extract_array_generic,
1701 .cleaner = array_cleanup,
1702 .dst = (void *) dst,
1703 .fname = name,
1704 .cls = info
1705 };
1706 return res;
1707}
1708
1709
1710struct GNUNET_PQ_ResultSpec
1711GNUNET_PQ_result_spec_array_variable_size (
1712 struct GNUNET_PQ_Context *db,
1713 const char *name,
1714 size_t *num,
1715 size_t **sizes,
1716 void **dst)
1717{
1718 struct array_result_cls *info =
1719 GNUNET_new (struct array_result_cls);
1720
1721 info->num = num;
1722 info->sizes = sizes;
1723 info->typ = array_of_byte;
1724 GNUNET_assert (GNUNET_OK ==
1725 GNUNET_PQ_get_oid_by_name (db,
1726 "bytea",
1727 &info->oid));
1728
1729 struct GNUNET_PQ_ResultSpec res = {
1730 .conv = extract_array_generic,
1731 .cleaner = array_cleanup,
1732 .dst = (void *) dst,
1733 .fname = name,
1734 .cls = info
1735 };
1736 return res;
1737}
1738
1739
1740struct GNUNET_PQ_ResultSpec
1741GNUNET_PQ_result_spec_array_fixed_size (
1742 struct GNUNET_PQ_Context *db,
1743 const char *name,
1744 size_t size,
1745 size_t *num,
1746 void **dst)
1747{
1748 struct array_result_cls *info =
1749 GNUNET_new (struct array_result_cls);
1750
1751 info->num = num;
1752 info->same_size = size;
1753 info->typ = array_of_byte;
1754 GNUNET_assert (GNUNET_OK ==
1755 GNUNET_PQ_get_oid_by_name (db,
1756 "bytea",
1757 &info->oid));
1758
1759 struct GNUNET_PQ_ResultSpec res = {
1760 .conv = extract_array_generic,
1761 .cleaner = array_cleanup,
1762 .dst = (void *) dst,
1763 .fname = name,
1764 .cls = info
1765 };
1766 return res;
1767}
1768
1769
1770struct GNUNET_PQ_ResultSpec
1771GNUNET_PQ_result_spec_array_string (
1772 struct GNUNET_PQ_Context *db,
1773 const char *name,
1774 size_t *num,
1775 char **dst)
1776{
1777 struct array_result_cls *info =
1778 GNUNET_new (struct array_result_cls);
1779
1780 info->num = num;
1781 info->typ = array_of_string;
1782 GNUNET_assert (GNUNET_OK ==
1783 GNUNET_PQ_get_oid_by_name (db,
1784 "text",
1785 &info->oid));
1786
1787 struct GNUNET_PQ_ResultSpec res = {
1788 .conv = extract_array_generic,
1789 .cleaner = array_cleanup,
1790 .dst = (void *) dst,
1791 .fname = name,
1792 .cls = info
1793 };
1794 return res;
1795}
1796
1797
1798/**
1799 * Extract data from a Postgres database @a result at row @a row.
1800 *
1801 * @param cls closure
1802 * @param result where to extract data from
1803 * @param row the row to extract data from
1804 * @param fname name (or prefix) of the fields to extract from
1805 * @param[in,out] dst_size where to store size of result, may be NULL
1806 * @param[out] dst where to store the result
1807 * @return
1808 * #GNUNET_YES if all results could be extracted
1809 * #GNUNET_SYSERR if a result was invalid (non-existing field or NULL)
1810 */
1811static enum GNUNET_GenericReturnValue
1812extract_blind_sign_pub (void *cls,
1813 PGresult *result,
1814 int row,
1815 const char *fname,
1816 size_t *dst_size,
1817 void *dst)
1818{
1819 struct GNUNET_CRYPTO_BlindSignPublicKey **bpk = dst;
1820 struct GNUNET_CRYPTO_BlindSignPublicKey *tmp;
1821 size_t len;
1822 const char *res;
1823 int fnum;
1824 uint32_t be;
1825
1826 (void) cls;
1827 (void) dst_size;
1828 fnum = PQfnumber (result,
1829 fname);
1830 if (fnum < 0)
1831 {
1832 GNUNET_break (0);
1833 return GNUNET_SYSERR;
1834 }
1835 if (PQgetisnull (result,
1836 row,
1837 fnum))
1838 return GNUNET_NO;
1839
1840 /* if a field is null, continue but
1841 * remember that we now return a different result */
1842 len = PQgetlength (result,
1843 row,
1844 fnum);
1845 res = PQgetvalue (result,
1846 row,
1847 fnum);
1848 if (len < sizeof (be))
1849 {
1850 GNUNET_break (0);
1851 return GNUNET_SYSERR;
1852 }
1853 GNUNET_memcpy (&be,
1854 res,
1855 sizeof (be));
1856 res += sizeof (be);
1857 len -= sizeof (be);
1858 tmp = GNUNET_new (struct GNUNET_CRYPTO_BlindSignPublicKey);
1859 tmp->cipher = ntohl (be);
1860 tmp->rc = 1;
1861 switch (tmp->cipher)
1862 {
1863 case GNUNET_CRYPTO_BSA_INVALID:
1864 break;
1865 case GNUNET_CRYPTO_BSA_RSA:
1866 tmp->details.rsa_public_key
1867 = GNUNET_CRYPTO_rsa_public_key_decode (res,
1868 len);
1869 if (NULL == tmp->details.rsa_public_key)
1870 {
1871 GNUNET_break (0);
1872 GNUNET_free (tmp);
1873 return GNUNET_SYSERR;
1874 }
1875 GNUNET_CRYPTO_hash (res,
1876 len,
1877 &tmp->pub_key_hash);
1878 *bpk = tmp;
1879 return GNUNET_OK;
1880 case GNUNET_CRYPTO_BSA_CS:
1881 if (sizeof (tmp->details.cs_public_key) != len)
1882 {
1883 GNUNET_break (0);
1884 GNUNET_free (tmp);
1885 return GNUNET_SYSERR;
1886 }
1887 GNUNET_memcpy (&tmp->details.cs_public_key,
1888 res,
1889 len);
1890 GNUNET_CRYPTO_hash (res,
1891 len,
1892 &tmp->pub_key_hash);
1893 *bpk = tmp;
1894 return GNUNET_OK;
1895 }
1896 GNUNET_break (0);
1897 GNUNET_free (tmp);
1898 return GNUNET_SYSERR;
1899}
1900
1901
1902/**
1903 * Function called to clean up memory allocated
1904 * by a #GNUNET_PQ_ResultConverter.
1905 *
1906 * @param cls closure
1907 * @param rd result data to clean up
1908 */
1909static void
1910clean_blind_sign_pub (void *cls,
1911 void *rd)
1912{
1913 struct GNUNET_CRYPTO_BlindSignPublicKey **pub = rd;
1914
1915 (void) cls;
1916 GNUNET_CRYPTO_blind_sign_pub_decref (*pub);
1917 *pub = NULL;
1918}
1919
1920
1921struct GNUNET_PQ_ResultSpec
1922GNUNET_PQ_result_spec_blind_sign_pub (const char *name,
1923 struct GNUNET_CRYPTO_BlindSignPublicKey **pub)
1924{
1925 struct GNUNET_PQ_ResultSpec res = {
1926 .conv = &extract_blind_sign_pub,
1927 .cleaner = &clean_blind_sign_pub,
1928 .dst = (void *) pub,
1929 .fname = name
1930 };
1931
1932 return res;
1933}
1934
1935
1936/**
1937 * Extract data from a Postgres database @a result at row @a row.
1938 *
1939 * @param cls closure
1940 * @param result where to extract data from
1941 * @param row the row to extract data from
1942 * @param fname name (or prefix) of the fields to extract from
1943 * @param[in,out] dst_size where to store size of result, may be NULL
1944 * @param[out] dst where to store the result
1945 * @return
1946 * #GNUNET_YES if all results could be extracted
1947 * #GNUNET_SYSERR if a result was invalid (non-existing field or NULL)
1948 */
1949static enum GNUNET_GenericReturnValue
1950extract_blind_sign_priv (void *cls,
1951 PGresult *result,
1952 int row,
1953 const char *fname,
1954 size_t *dst_size,
1955 void *dst)
1956{
1957 struct GNUNET_CRYPTO_BlindSignPrivateKey **bpk = dst;
1958 struct GNUNET_CRYPTO_BlindSignPrivateKey *tmp;
1959 size_t len;
1960 const char *res;
1961 int fnum;
1962 uint32_t be;
1963
1964 (void) cls;
1965 (void) dst_size;
1966 fnum = PQfnumber (result,
1967 fname);
1968 if (fnum < 0)
1969 {
1970 GNUNET_break (0);
1971 return GNUNET_SYSERR;
1972 }
1973 if (PQgetisnull (result,
1974 row,
1975 fnum))
1976 return GNUNET_NO;
1977
1978 /* if a field is null, continue but
1979 * remember that we now return a different result */
1980 len = PQgetlength (result,
1981 row,
1982 fnum);
1983 res = PQgetvalue (result,
1984 row,
1985 fnum);
1986 if (len < sizeof (be))
1987 {
1988 GNUNET_break (0);
1989 return GNUNET_SYSERR;
1990 }
1991 GNUNET_memcpy (&be,
1992 res,
1993 sizeof (be));
1994 res += sizeof (be);
1995 len -= sizeof (be);
1996 tmp = GNUNET_new (struct GNUNET_CRYPTO_BlindSignPrivateKey);
1997 tmp->cipher = ntohl (be);
1998 tmp->rc = 1;
1999 switch (tmp->cipher)
2000 {
2001 case GNUNET_CRYPTO_BSA_INVALID:
2002 break;
2003 case GNUNET_CRYPTO_BSA_RSA:
2004 tmp->details.rsa_private_key
2005 = GNUNET_CRYPTO_rsa_private_key_decode (res,
2006 len);
2007 if (NULL == tmp->details.rsa_private_key)
2008 {
2009 GNUNET_break (0);
2010 GNUNET_free (bpk);
2011 return GNUNET_SYSERR;
2012 }
2013 *bpk = tmp;
2014 return GNUNET_OK;
2015 case GNUNET_CRYPTO_BSA_CS:
2016 if (sizeof (tmp->details.cs_private_key) != len)
2017 {
2018 GNUNET_break (0);
2019 GNUNET_free (tmp);
2020 return GNUNET_SYSERR;
2021 }
2022 GNUNET_memcpy (&tmp->details.cs_private_key,
2023 res,
2024 len);
2025 *bpk = tmp;
2026 return GNUNET_OK;
2027 }
2028 GNUNET_break (0);
2029 GNUNET_free (tmp);
2030 return GNUNET_SYSERR;
2031}
2032
2033
2034/**
2035 * Function called to clean up memory allocated
2036 * by a #GNUNET_PQ_ResultConverter.
2037 *
2038 * @param cls closure
2039 * @param rd result data to clean up
2040 */
2041static void
2042clean_blind_sign_priv (void *cls,
2043 void *rd)
2044{
2045 struct GNUNET_CRYPTO_BlindSignPrivateKey **priv = rd;
2046
2047 (void) cls;
2048 GNUNET_CRYPTO_blind_sign_priv_decref (*priv);
2049 *priv = NULL;
2050}
2051
2052
2053struct GNUNET_PQ_ResultSpec
2054GNUNET_PQ_result_spec_blind_sign_priv (const char *name,
2055 struct GNUNET_CRYPTO_BlindSignPrivateKey **priv)
2056{
2057 struct GNUNET_PQ_ResultSpec res = {
2058 .conv = &extract_blind_sign_priv,
2059 .cleaner = &clean_blind_sign_priv,
2060 .dst = (void *) priv,
2061 .fname = name
2062 };
2063
2064 return res;
2065}
2066
2067/* end of pq_result_helper.c */
diff --git a/src/lib/pq/test_pq.c b/src/lib/pq/test_pq.c
new file mode 100644
index 000000000..813c4a019
--- /dev/null
+++ b/src/lib/pq/test_pq.c
@@ -0,0 +1,630 @@
1/*
2 This file is part of GNUnet
3 (C) 2015, 2016, 2019, 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 * @file pq/test_pq.c
22 * @brief Tests for Postgres convenience API
23 * @author Christian Grothoff <christian@grothoff.org>
24 */
25#include "platform.h"
26#include "gnunet_common.h"
27#include "gnunet_pq_lib.h"
28#include "gnunet_time_lib.h"
29#include "pq.h"
30
31/**
32 * Database handle.
33 */
34static struct GNUNET_PQ_Context *db;
35
36/**
37 * Global return value, 0 on success.
38 */
39static int ret;
40
41/**
42 * An event handler.
43 */
44static struct GNUNET_DB_EventHandler *eh;
45
46/**
47 * Timeout task.
48 */
49static struct GNUNET_SCHEDULER_Task *tt;
50
51
52/**
53 * Setup prepared statements.
54 *
55 * @param db database handle to initialize
56 * @return #GNUNET_OK on success, #GNUNET_SYSERR on failure
57 */
58static int
59postgres_prepare (struct GNUNET_PQ_Context *db)
60{
61 struct GNUNET_PQ_PreparedStatement ps[] = {
62 GNUNET_PQ_make_prepare ("test_insert",
63 "INSERT INTO test_pq ("
64 " pub"
65 ",sig"
66 ",abs_time"
67 ",forever"
68 ",hash"
69 ",vsize"
70 ",u16"
71 ",u32"
72 ",u64"
73 ",unn"
74 ",arr_bool"
75 ",arr_int2"
76 ",arr_int4"
77 ",arr_int8"
78 ",arr_bytea"
79 ",arr_text"
80 ",arr_abs_time"
81 ",arr_rel_time"
82 ",arr_timestamp"
83 ") VALUES "
84 "($1, $2, $3, $4, $5, $6,"
85 "$7, $8, $9, $10,"
86 "$11, $12, $13, $14, $15, $16,"
87 "$17, $18, $19);"),
88 GNUNET_PQ_make_prepare ("test_select",
89 "SELECT"
90 " pub"
91 ",sig"
92 ",abs_time"
93 ",forever"
94 ",hash"
95 ",vsize"
96 ",u16"
97 ",u32"
98 ",u64"
99 ",unn"
100 ",arr_bool"
101 ",arr_int2"
102 ",arr_int4"
103 ",arr_int8"
104 ",arr_bytea"
105 ",arr_text"
106 ",arr_abs_time"
107 ",arr_rel_time"
108 ",arr_timestamp"
109 " FROM test_pq"
110 " ORDER BY abs_time DESC "
111 " LIMIT 1;"),
112 GNUNET_PQ_PREPARED_STATEMENT_END
113 };
114
115 return GNUNET_PQ_prepare_statements (db,
116 ps);
117}
118
119
120/**
121 * Run actual test queries.
122 *
123 * @param db database handle
124 * @return 0 on success
125 */
126static int
127run_queries (struct GNUNET_PQ_Context *db)
128{
129 struct GNUNET_CRYPTO_RsaPublicKey *pub;
130 struct GNUNET_CRYPTO_RsaPublicKey *pub2 = NULL;
131 struct GNUNET_CRYPTO_RsaSignature *sig;
132 struct GNUNET_CRYPTO_RsaSignature *sig2 = NULL;
133 struct GNUNET_TIME_Absolute abs_time = GNUNET_TIME_absolute_get ();
134 struct GNUNET_TIME_Absolute abs_time2;
135 struct GNUNET_TIME_Absolute forever = GNUNET_TIME_UNIT_FOREVER_ABS;
136 struct GNUNET_TIME_Absolute forever2;
137 struct GNUNET_HashCode hc;
138 struct GNUNET_HashCode hc2;
139 PGresult *result;
140 int ret;
141 struct GNUNET_CRYPTO_RsaPrivateKey *priv;
142 const char msg[] = "hello";
143 void *msg2;
144 struct GNUNET_HashCode hmsg;
145 size_t msg2_len;
146 uint16_t u16;
147 uint16_t u162;
148 uint32_t u32;
149 uint32_t u322;
150 uint64_t u64;
151 uint64_t u642;
152 uint64_t uzzz = 42;
153 struct GNUNET_HashCode ahc[3] = {};
154 bool ab[5] = {true, false, false, true, false};
155 uint16_t ai2[3] = {42, 0x0001, 0xFFFF};
156 uint32_t ai4[3] = {42, 0x00010000, 0xFFFFFFFF};
157 uint64_t ai8[3] = {42, 0x0001000000000000, 0xFFFFFFFFFFFFFFFF};
158 const char *as[] = {"foo", "bar", "buzz"};
159 const struct GNUNET_TIME_Absolute ata[2] = {GNUNET_TIME_absolute_get (),
160 GNUNET_TIME_absolute_get ()};
161 const struct GNUNET_TIME_Relative atr[2] = {GNUNET_TIME_relative_get_hour_ (),
162 GNUNET_TIME_relative_get_minute_ ()};
163 const struct GNUNET_TIME_Timestamp ats[2] = {GNUNET_TIME_timestamp_get (),
164 GNUNET_TIME_timestamp_get ()};
165
166
167 priv = GNUNET_CRYPTO_rsa_private_key_create (1024);
168 pub = GNUNET_CRYPTO_rsa_private_key_get_public (priv);
169 memset (&hmsg, 42, sizeof(hmsg));
170 sig = GNUNET_CRYPTO_rsa_sign_fdh (priv,
171 &hmsg,
172 sizeof (hmsg));
173 u16 = 16;
174 u32 = 32;
175 u64 = 64;
176 for (int i = 0; i < 3; i++)
177 GNUNET_CRYPTO_random_block (GNUNET_CRYPTO_QUALITY_WEAK,
178 &ahc[i],
179 sizeof(ahc[i]));
180
181 /* FIXME: test GNUNET_PQ_result_spec_variable_size */
182 {
183 struct GNUNET_PQ_QueryParam params_insert[] = {
184 GNUNET_PQ_query_param_rsa_public_key (pub),
185 GNUNET_PQ_query_param_rsa_signature (sig),
186 GNUNET_PQ_query_param_absolute_time (&abs_time),
187 GNUNET_PQ_query_param_absolute_time (&forever),
188 GNUNET_PQ_query_param_auto_from_type (&hc),
189 GNUNET_PQ_query_param_string (msg),
190 GNUNET_PQ_query_param_uint16 (&u16),
191 GNUNET_PQ_query_param_uint32 (&u32),
192 GNUNET_PQ_query_param_uint64 (&u64),
193 GNUNET_PQ_query_param_null (),
194 GNUNET_PQ_query_param_array_bool (5, ab, db),
195 GNUNET_PQ_query_param_array_uint16 (3, ai2, db),
196 GNUNET_PQ_query_param_array_uint32 (3, ai4, db),
197 GNUNET_PQ_query_param_array_uint64 (3, ai8, db),
198 GNUNET_PQ_query_param_array_bytes_same_size (3,
199 ahc,
200 sizeof(ahc[0]),
201 db),
202 GNUNET_PQ_query_param_array_ptrs_string (3, as, db),
203 GNUNET_PQ_query_param_array_abs_time (2, ata, db),
204 GNUNET_PQ_query_param_array_rel_time (2, atr, db),
205 GNUNET_PQ_query_param_array_timestamp (2, ats, db),
206 GNUNET_PQ_query_param_end
207 };
208 struct GNUNET_PQ_QueryParam params_select[] = {
209 GNUNET_PQ_query_param_end
210 };
211 bool got_null = false;
212 size_t num_bool;
213 bool *arr_bools;
214 size_t num_u16;
215 uint16_t *arr_u16;
216 size_t num_u32;
217 uint32_t *arr_u32;
218 size_t num_u64;
219 uint64_t *arr_u64;
220 size_t num_abs;
221 struct GNUNET_TIME_Absolute *arr_abs;
222 size_t num_rel;
223 struct GNUNET_TIME_Relative *arr_rel;
224 size_t num_tstmp;
225 struct GNUNET_TIME_Timestamp *arr_tstmp;
226 size_t num_str;
227 char *arr_str;
228 size_t num_hash;
229 struct GNUNET_HashCode *arr_hash;
230 size_t num_buf;
231 void *arr_buf;
232 size_t *sz_buf;
233 struct GNUNET_PQ_ResultSpec results_select[] = {
234 GNUNET_PQ_result_spec_rsa_public_key ("pub", &pub2),
235 GNUNET_PQ_result_spec_rsa_signature ("sig", &sig2),
236 GNUNET_PQ_result_spec_absolute_time ("abs_time", &abs_time2),
237 GNUNET_PQ_result_spec_absolute_time ("forever", &forever2),
238 GNUNET_PQ_result_spec_auto_from_type ("hash", &hc2),
239 GNUNET_PQ_result_spec_variable_size ("vsize", &msg2, &msg2_len),
240 GNUNET_PQ_result_spec_uint16 ("u16", &u162),
241 GNUNET_PQ_result_spec_uint32 ("u32", &u322),
242 GNUNET_PQ_result_spec_uint64 ("u64", &u642),
243 GNUNET_PQ_result_spec_allow_null (
244 GNUNET_PQ_result_spec_uint64 ("unn", &uzzz),
245 &got_null),
246 GNUNET_PQ_result_spec_array_bool (db,
247 "arr_bool",
248 &num_bool,
249 &arr_bools),
250 GNUNET_PQ_result_spec_array_uint16 (db,
251 "arr_int2",
252 &num_u16,
253 &arr_u16),
254 GNUNET_PQ_result_spec_array_uint32 (db,
255 "arr_int4",
256 &num_u32,
257 &arr_u32),
258 GNUNET_PQ_result_spec_array_uint64 (db,
259 "arr_int8",
260 &num_u64,
261 &arr_u64),
262 GNUNET_PQ_result_spec_array_abs_time (db,
263 "arr_abs_time",
264 &num_abs,
265 &arr_abs),
266 GNUNET_PQ_result_spec_array_rel_time (db,
267 "arr_rel_time",
268 &num_rel,
269 &arr_rel),
270 GNUNET_PQ_result_spec_array_timestamp (db,
271 "arr_timestamp",
272 &num_tstmp,
273 &arr_tstmp),
274 GNUNET_PQ_result_spec_auto_array_from_type (db,
275 "arr_bytea",
276 &num_hash,
277 arr_hash),
278 GNUNET_PQ_result_spec_array_variable_size (db,
279 "arr_bytea",
280 &num_buf,
281 &sz_buf,
282 &arr_buf),
283 GNUNET_PQ_result_spec_array_string (db,
284 "arr_text",
285 &num_str,
286 &arr_str),
287 GNUNET_PQ_result_spec_end
288 };
289
290 result = GNUNET_PQ_exec_prepared (db,
291 "test_insert",
292 params_insert);
293 if (PGRES_COMMAND_OK != PQresultStatus (result))
294 {
295 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
296 "Database failure: %s\n",
297 PQresultErrorMessage (result));
298 PQclear (result);
299 GNUNET_CRYPTO_rsa_signature_free (sig);
300 GNUNET_CRYPTO_rsa_private_key_free (priv);
301 GNUNET_CRYPTO_rsa_public_key_free (pub);
302 return 1;
303 }
304
305 PQclear (result);
306 result = GNUNET_PQ_exec_prepared (db,
307 "test_select",
308 params_select);
309 if (1 !=
310 PQntuples (result))
311 {
312 GNUNET_break (0);
313 PQclear (result);
314 GNUNET_CRYPTO_rsa_signature_free (sig);
315 GNUNET_CRYPTO_rsa_private_key_free (priv);
316 GNUNET_CRYPTO_rsa_public_key_free (pub);
317 return 1;
318 }
319 ret = GNUNET_PQ_extract_result (result,
320 results_select,
321 0);
322 GNUNET_break (GNUNET_YES == ret);
323 GNUNET_break (abs_time.abs_value_us == abs_time2.abs_value_us);
324 GNUNET_break (forever.abs_value_us == forever2.abs_value_us);
325 GNUNET_break (0 ==
326 GNUNET_memcmp (&hc,
327 &hc2));
328 GNUNET_break (0 ==
329 GNUNET_CRYPTO_rsa_signature_cmp (sig,
330 sig2));
331 GNUNET_break (0 ==
332 GNUNET_CRYPTO_rsa_public_key_cmp (pub,
333 pub2));
334 GNUNET_break (strlen (msg) == msg2_len);
335 GNUNET_break (0 ==
336 strncmp (msg,
337 msg2,
338 msg2_len));
339 GNUNET_break (16 == u162);
340 GNUNET_break (32 == u322);
341 GNUNET_break (64 == u642);
342 GNUNET_break (42 == uzzz);
343 GNUNET_break (got_null);
344
345 /* Check arrays */
346 GNUNET_break (num_bool == 5);
347 for (size_t i = 0; i < num_bool; i++)
348 GNUNET_break (arr_bools[i] == ab[i]);
349
350 GNUNET_break (num_u16 == 3);
351 for (size_t i = 0; i < num_u16; i++)
352 GNUNET_break (arr_u16[i] == ai2[i]);
353
354 GNUNET_break (num_u32 == 3);
355 for (size_t i = 0; i < num_u32; i++)
356 GNUNET_break (arr_u32[i] == ai4[i]);
357
358 GNUNET_break (num_u64 == 3);
359 for (size_t i = 0; i < num_u64; i++)
360 GNUNET_break (arr_u64[i] == ai8[i]);
361
362 GNUNET_break (num_abs == 2);
363 for (size_t i = 0; i < num_abs; i++)
364 GNUNET_break (arr_abs[i].abs_value_us == ata[i].abs_value_us);
365
366 GNUNET_break (num_rel == 2);
367 for (size_t i = 0; i < num_rel; i++)
368 GNUNET_break (arr_rel[i].rel_value_us == atr[i].rel_value_us);
369
370 GNUNET_break (num_tstmp == 2);
371 for (size_t i = 0; i < num_tstmp; i++)
372 GNUNET_break (arr_tstmp[i].abs_time.abs_value_us ==
373 ats[i].abs_time.abs_value_us);
374
375 GNUNET_break (num_str == 3);
376 GNUNET_break (0 == strcmp (arr_str, as[0]));
377 GNUNET_break (0 == strcmp (arr_str + 4, as[1]));
378 GNUNET_break (0 == strcmp (arr_str + 8, as[2]));
379
380 GNUNET_break (num_hash == 3);
381 for (size_t i = 0; i < num_hash; i++)
382 GNUNET_break (0 == GNUNET_memcmp (&arr_hash[i], &ahc[i]));
383
384 GNUNET_break (num_buf == 3);
385 for (size_t i = 0; i < num_buf; i++)
386 {
387 GNUNET_break (0 == memcmp (((char *) arr_buf) + i * sizeof(ahc[i]),
388 &ahc[i],
389 sizeof(ahc[i])));
390 }
391
392 GNUNET_PQ_cleanup_result (results_select);
393 PQclear (result);
394
395 GNUNET_PQ_cleanup_query_params_closures (params_insert);
396 }
397
398 GNUNET_CRYPTO_rsa_signature_free (sig);
399 GNUNET_CRYPTO_rsa_private_key_free (priv);
400 GNUNET_CRYPTO_rsa_public_key_free (pub);
401 if (GNUNET_OK != ret)
402 return 1;
403
404 return 0;
405}
406
407
408/**
409 * Task called on shutdown.
410 *
411 * @param cls NULL
412 */
413static void
414event_end (void *cls)
415{
416 GNUNET_PQ_event_listen_cancel (eh);
417 eh = NULL;
418 if (NULL != tt)
419 {
420 GNUNET_SCHEDULER_cancel (tt);
421 tt = NULL;
422 }
423}
424
425
426/**
427 * Task called on timeout. Should not happen, means
428 * we did not get the expected event.
429 *
430 * @param cls NULL
431 */
432static void
433timeout_cb (void *cls)
434{
435 ret = 2;
436 GNUNET_break (0);
437 tt = NULL;
438 GNUNET_SCHEDULER_shutdown ();
439}
440
441
442/**
443 * Task called on expected event
444 *
445 * @param cls NULL
446 */
447static void
448event_sched_cb (void *cls,
449 const void *extra,
450 size_t extra_size)
451{
452 GNUNET_assert (5 == extra_size);
453 GNUNET_assert (0 ==
454 memcmp ("hello",
455 extra,
456 5));
457 GNUNET_SCHEDULER_shutdown ();
458}
459
460
461/**
462 * Run tests that need a scheduler.
463 *
464 * @param cls NULL
465 */
466static void
467sched_tests (void *cls)
468{
469 struct GNUNET_DB_EventHeaderP es = {
470 .size = htons (sizeof (es)),
471 .type = htons (42)
472 };
473
474
475 tt = GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_UNIT_SECONDS,
476 &timeout_cb,
477 NULL);
478 eh = GNUNET_PQ_event_listen (db,
479 &es,
480 GNUNET_TIME_UNIT_FOREVER_REL,
481 &event_sched_cb,
482 NULL);
483 GNUNET_PQ_reconnect (db);
484 GNUNET_SCHEDULER_add_shutdown (&event_end,
485 NULL);
486 GNUNET_PQ_event_notify (db,
487 &es,
488 "hello",
489 5);
490}
491
492
493int
494main (int argc,
495 const char *const argv[])
496{
497 struct GNUNET_PQ_ExecuteStatement es[] = {
498 GNUNET_PQ_make_execute ("CREATE TEMPORARY TABLE IF NOT EXISTS test_pq ("
499 " pub BYTEA NOT NULL"
500 ",sig BYTEA NOT NULL"
501 ",abs_time INT8 NOT NULL"
502 ",forever INT8 NOT NULL"
503 ",hash BYTEA NOT NULL CHECK(LENGTH(hash)=64)"
504 ",vsize VARCHAR NOT NULL"
505 ",u16 INT2 NOT NULL"
506 ",u32 INT4 NOT NULL"
507 ",u64 INT8 NOT NULL"
508 ",unn INT8"
509 ",arr_bool BOOL[]"
510 ",arr_int2 INT2[]"
511 ",arr_int4 INT4[]"
512 ",arr_int8 INT8[]"
513 ",arr_bytea BYTEA[]"
514 ",arr_text TEXT[]"
515 ",arr_abs_time INT8[]"
516 ",arr_rel_time INT8[]"
517 ",arr_timestamp INT8[]"
518 ")"),
519 GNUNET_PQ_EXECUTE_STATEMENT_END
520 };
521
522 GNUNET_log_setup ("test-pq",
523 "INFO",
524 NULL);
525 db = GNUNET_PQ_connect ("postgres:///gnunetcheck",
526 NULL,
527 es,
528 NULL);
529 if (NULL == db)
530 {
531 fprintf (stderr,
532 "Cannot run test, database connection failed\n");
533 return 77;
534 }
535 if (CONNECTION_OK != PQstatus (db->conn))
536 {
537 fprintf (stderr,
538 "Cannot run test, database connection failed: %s\n",
539 PQerrorMessage (db->conn));
540 GNUNET_break (0);
541 GNUNET_PQ_disconnect (db);
542 return 77; /* signal test was skipped */
543 }
544 if (GNUNET_OK !=
545 postgres_prepare (db))
546 {
547 GNUNET_break (0);
548 GNUNET_PQ_disconnect (db);
549 return 1;
550 }
551 ret = run_queries (db);
552 if (0 != ret)
553 {
554 GNUNET_break (0);
555 GNUNET_PQ_disconnect (db);
556 return ret;
557 }
558
559 /* ensure oid lookup works */
560 {
561 enum GNUNET_GenericReturnValue ret;
562 Oid oid;
563
564 ret = GNUNET_PQ_get_oid_by_name (db, "int8", &oid);
565
566 if (GNUNET_OK != ret)
567 {
568 fprintf (stderr,
569 "Cannot lookup oid for int8: %s\n",
570 PQerrorMessage (db->conn));
571 GNUNET_break (0);
572 GNUNET_PQ_disconnect (db);
573 return 77; /* signal test was skipped */
574 }
575
576 PQexec (db->conn, "CREATE TYPE foo AS (foo int, bar int);");
577
578 ret = GNUNET_PQ_get_oid_by_name (db, "foo", &oid);
579 if (GNUNET_OK != ret)
580 {
581 fprintf (stderr,
582 "Cannot lookup oid for foo: %s\n",
583 PQerrorMessage (db->conn));
584 GNUNET_break (0);
585 GNUNET_PQ_disconnect (db);
586 return 77; /* signal test was skipped */
587 }
588
589 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
590 "got oid %d for type foo\n", oid);
591 }
592
593 GNUNET_SCHEDULER_run (&sched_tests,
594 NULL);
595 if (0 != ret)
596 {
597 GNUNET_break (0);
598 GNUNET_PQ_disconnect (db);
599 return ret;
600 }
601#if TEST_RESTART
602 fprintf (stderr, "Please restart Postgres database now!\n");
603 sleep (60);
604 ret |= run_queries (db);
605 fprintf (stderr, "Result: %d (expect: 1 -- if you restarted the DB)\n", ret);
606 ret |= run_queries (db);
607 fprintf (stderr, "Result: %d (expect: 0)\n", ret);
608#endif
609 {
610 struct GNUNET_PQ_ExecuteStatement es[] = {
611 GNUNET_PQ_make_execute ("DROP TABLE test_pq"),
612 GNUNET_PQ_EXECUTE_STATEMENT_END
613 };
614
615 if (GNUNET_OK !=
616 GNUNET_PQ_exec_statements (db,
617 es))
618 {
619 fprintf (stderr,
620 "Failed to drop table\n");
621 GNUNET_PQ_disconnect (db);
622 return 1;
623 }
624 }
625 GNUNET_PQ_disconnect (db);
626 return ret;
627}
628
629
630/* end of test_pq.c */
diff --git a/src/lib/pq/versioning.sql b/src/lib/pq/versioning.sql
new file mode 100644
index 000000000..c7fa81213
--- /dev/null
+++ b/src/lib/pq/versioning.sql
@@ -0,0 +1,298 @@
1-- LICENSE AND COPYRIGHT
2--
3-- Copyright (C) 2010 Hubert depesz Lubaczewski
4--
5-- This program is distributed under the (Revised) BSD License:
6-- L<http://www.opensource.org/licenses/bsd-license.php>
7--
8-- Redistribution and use in source and binary forms, with or without
9-- modification, are permitted provided that the following conditions
10-- are met:
11--
12-- * Redistributions of source code must retain the above copyright
13-- notice, this list of conditions and the following disclaimer.
14--
15-- * Redistributions in binary form must reproduce the above copyright
16-- notice, this list of conditions and the following disclaimer in the
17-- documentation and/or other materials provided with the distribution.
18--
19-- * Neither the name of Hubert depesz Lubaczewski's Organization
20-- nor the names of its contributors may be used to endorse or
21-- promote products derived from this software without specific
22-- prior written permission.
23--
24-- THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
25-- AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
26-- IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
27-- DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE
28-- FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
29-- DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
30-- SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
31-- CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
32-- OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
33-- OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
34--
35-- Code origin: https://gitlab.com/depesz/Versioning/blob/master/install.versioning.sql
36--
37--
38-- # NAME
39--
40-- **Versioning** - simplistic take on tracking and applying changes to databases.
41--
42-- # DESCRIPTION
43--
44-- This project strives to provide simple way to manage changes to
45-- database.
46--
47-- Instead of making changes on development server, then finding
48-- differences between production and development, deciding which ones
49-- should be installed on production, and finding a way to install them -
50-- you start with writing diffs themselves!
51--
52-- # INSTALLATION
53--
54-- To install versioning simply run install.versioning.sql in your database
55-- (all of them: production, stage, test, devel, ...).
56--
57-- # USAGE
58--
59-- In your files with patches to database, put whole logic in single
60-- transaction, and use \_v.\* functions - usually \_v.register_patch() at
61-- least to make sure everything is OK.
62--
63-- For example. Let's assume you have patch files:
64--
65-- ## 0001.sql:
66--
67-- ```
68-- create table users (id serial primary key, username text);
69-- ```
70--
71-- ## 0002.sql:
72--
73-- ```
74-- insert into users (username) values ('depesz');
75-- ```
76-- To change it to use versioning you would change the files, to this
77-- state:
78--
79-- 0000.sql:
80--
81-- ```
82-- BEGIN;
83-- select _v.register_patch('000-base', NULL, NULL);
84-- create table users (id serial primary key, username text);
85-- COMMIT;
86-- ```
87--
88-- ## 0002.sql:
89--
90-- ```
91-- BEGIN;
92-- select _v.register_patch('001-users', ARRAY['000-base'], NULL);
93-- insert into users (username) values ('depesz');
94-- COMMIT;
95-- ```
96--
97-- This will make sure that patch 001-users can only be applied after
98-- 000-base.
99--
100-- # AVAILABLE FUNCTIONS
101--
102-- ## \_v.register_patch( TEXT )
103--
104-- Registers named patch, or dies if it is already registered.
105--
106-- Returns integer which is id of patch in \_v.patches table - only if it
107-- succeeded.
108--
109-- ## \_v.register_patch( TEXT, TEXT[] )
110--
111-- Same as \_v.register_patch( TEXT ), but checks is all given patches (given as
112-- array in second argument) are already registered.
113--
114-- ## \_v.register_patch( TEXT, TEXT[], TEXT[] )
115--
116-- Same as \_v.register_patch( TEXT, TEXT[] ), but also checks if there are no conflicts with preexisting patches.
117--
118-- Third argument is array of names of patches that conflict with current one. So
119-- if any of them is installed - register_patch will error out.
120--
121-- ## \_v.unregister_patch( TEXT )
122--
123-- Removes information about given patch from the versioning data.
124--
125-- It doesn't remove objects that were created by this patch - just removes
126-- metainformation.
127--
128-- ## \_v.assert_user_is_superuser()
129--
130-- Make sure that current patch is being loaded by superuser.
131--
132-- If it's not - it will raise exception, and break transaction.
133--
134-- ## \_v.assert_user_is_not_superuser()
135--
136-- Make sure that current patch is not being loaded by superuser.
137--
138-- If it is - it will raise exception, and break transaction.
139--
140-- ## \_v.assert_user_is_one_of(TEXT, TEXT, ... )
141--
142-- Make sure that current patch is being loaded by one of listed users.
143--
144-- If ```current_user``` is not listed as one of arguments - function will raise
145-- exception and break the transaction.
146
147BEGIN;
148
149-- Added by Christian Grothoff to support concurrency, see
150-- https://stackoverflow.com/questions/29900845/create-schema-if-not-exists-raises-duplicate-key-error?rq=4
151LOCK TABLE pg_catalog.pg_namespace;
152
153
154-- This file adds versioning support to database it will be loaded to.
155-- It requires that PL/pgSQL is already loaded - will raise exception otherwise.
156-- All versioning "stuff" (tables, functions) is in "_v" schema.
157
158-- All functions are defined as 'RETURNS SETOF INT4' to be able to make them to RETURN literally nothing (0 rows).
159-- >> RETURNS VOID<< IS similar, but it still outputs "empty line" in psql when calling
160CREATE SCHEMA IF NOT EXISTS _v;
161COMMENT ON SCHEMA _v IS 'Schema for versioning data and functionality.';
162
163CREATE TABLE IF NOT EXISTS _v.patches (
164 patch_name TEXT PRIMARY KEY,
165 applied_tsz TIMESTAMPTZ NOT NULL DEFAULT now(),
166 applied_by TEXT NOT NULL,
167 requires TEXT[],
168 conflicts TEXT[]
169);
170COMMENT ON TABLE _v.patches IS 'Contains information about what patches are currently applied on database.';
171COMMENT ON COLUMN _v.patches.patch_name IS 'Name of patch, has to be unique for every patch.';
172COMMENT ON COLUMN _v.patches.applied_tsz IS 'When the patch was applied.';
173COMMENT ON COLUMN _v.patches.applied_by IS 'Who applied this patch (PostgreSQL username)';
174COMMENT ON COLUMN _v.patches.requires IS 'List of patches that are required for given patch.';
175COMMENT ON COLUMN _v.patches.conflicts IS 'List of patches that conflict with given patch.';
176
177CREATE OR REPLACE FUNCTION _v.register_patch( IN in_patch_name TEXT, IN in_requirements TEXT[], in_conflicts TEXT[], OUT versioning INT4 ) RETURNS setof INT4 AS $$
178DECLARE
179 t_text TEXT;
180 t_text_a TEXT[];
181 i INT4;
182BEGIN
183 -- Thanks to this we know only one patch will be applied at a time
184 LOCK TABLE _v.patches IN EXCLUSIVE MODE;
185
186 SELECT patch_name INTO t_text FROM _v.patches WHERE patch_name = in_patch_name;
187 IF FOUND THEN
188 RAISE EXCEPTION 'Patch % is already applied!', in_patch_name;
189 END IF;
190
191 t_text_a := ARRAY( SELECT patch_name FROM _v.patches WHERE patch_name = any( in_conflicts ) );
192 IF array_upper( t_text_a, 1 ) IS NOT NULL THEN
193 RAISE EXCEPTION 'Versioning patches conflict. Conflicting patche(s) installed: %.', array_to_string( t_text_a, ', ' );
194 END IF;
195
196 IF array_upper( in_requirements, 1 ) IS NOT NULL THEN
197 t_text_a := '{}';
198 FOR i IN array_lower( in_requirements, 1 ) .. array_upper( in_requirements, 1 ) LOOP
199 SELECT patch_name INTO t_text FROM _v.patches WHERE patch_name = in_requirements[i];
200 IF NOT FOUND THEN
201 t_text_a := t_text_a || in_requirements[i];
202 END IF;
203 END LOOP;
204 IF array_upper( t_text_a, 1 ) IS NOT NULL THEN
205 RAISE EXCEPTION 'Missing prerequisite(s): %.', array_to_string( t_text_a, ', ' );
206 END IF;
207 END IF;
208
209 INSERT INTO _v.patches (patch_name, applied_tsz, applied_by, requires, conflicts ) VALUES ( in_patch_name, now(), current_user, coalesce( in_requirements, '{}' ), coalesce( in_conflicts, '{}' ) );
210 RETURN;
211END;
212$$ language plpgsql;
213COMMENT ON FUNCTION _v.register_patch( TEXT, TEXT[], TEXT[] ) IS 'Function to register patches in database. Raises exception if there are conflicts, prerequisites are not installed or the migration has already been installed.';
214
215CREATE OR REPLACE FUNCTION _v.register_patch( TEXT, TEXT[] ) RETURNS setof INT4 AS $$
216 SELECT _v.register_patch( $1, $2, NULL );
217$$ language sql;
218COMMENT ON FUNCTION _v.register_patch( TEXT, TEXT[] ) IS 'Wrapper to allow registration of patches without conflicts.';
219CREATE OR REPLACE FUNCTION _v.register_patch( TEXT ) RETURNS setof INT4 AS $$
220 SELECT _v.register_patch( $1, NULL, NULL );
221$$ language sql;
222COMMENT ON FUNCTION _v.register_patch( TEXT ) IS 'Wrapper to allow registration of patches without requirements and conflicts.';
223
224CREATE OR REPLACE FUNCTION _v.unregister_patch( IN in_patch_name TEXT, OUT versioning INT4 ) RETURNS setof INT4 AS $$
225DECLARE
226 i INT4;
227 t_text_a TEXT[];
228BEGIN
229 -- Thanks to this we know only one patch will be applied at a time
230 LOCK TABLE _v.patches IN EXCLUSIVE MODE;
231
232 t_text_a := ARRAY( SELECT patch_name FROM _v.patches WHERE in_patch_name = ANY( requires ) );
233 IF array_upper( t_text_a, 1 ) IS NOT NULL THEN
234 RAISE EXCEPTION 'Cannot uninstall %, as it is required by: %.', in_patch_name, array_to_string( t_text_a, ', ' );
235 END IF;
236
237 DELETE FROM _v.patches WHERE patch_name = in_patch_name;
238 GET DIAGNOSTICS i = ROW_COUNT;
239 IF i < 1 THEN
240 RAISE EXCEPTION 'Patch % is not installed, so it can''t be uninstalled!', in_patch_name;
241 END IF;
242
243 RETURN;
244END;
245$$ language plpgsql;
246COMMENT ON FUNCTION _v.unregister_patch( TEXT ) IS 'Function to unregister patches in database. Dies if the patch is not registered, or if unregistering it would break dependencies.';
247
248CREATE OR REPLACE FUNCTION _v.assert_patch_is_applied( IN in_patch_name TEXT ) RETURNS TEXT as $$
249DECLARE
250 t_text TEXT;
251BEGIN
252 SELECT patch_name INTO t_text FROM _v.patches WHERE patch_name = in_patch_name;
253 IF NOT FOUND THEN
254 RAISE EXCEPTION 'Patch % is not applied!', in_patch_name;
255 END IF;
256 RETURN format('Patch %s is applied.', in_patch_name);
257END;
258$$ language plpgsql;
259COMMENT ON FUNCTION _v.assert_patch_is_applied( TEXT ) IS 'Function that can be used to make sure that patch has been applied.';
260
261CREATE OR REPLACE FUNCTION _v.assert_user_is_superuser() RETURNS TEXT as $$
262DECLARE
263 v_super bool;
264BEGIN
265 SELECT usesuper INTO v_super FROM pg_user WHERE usename = current_user;
266 IF v_super THEN
267 RETURN 'assert_user_is_superuser: OK';
268 END IF;
269 RAISE EXCEPTION 'Current user is not superuser - cannot continue.';
270END;
271$$ language plpgsql;
272COMMENT ON FUNCTION _v.assert_user_is_superuser() IS 'Function that can be used to make sure that patch is being applied using superuser account.';
273
274CREATE OR REPLACE FUNCTION _v.assert_user_is_not_superuser() RETURNS TEXT as $$
275DECLARE
276 v_super bool;
277BEGIN
278 SELECT usesuper INTO v_super FROM pg_user WHERE usename = current_user;
279 IF v_super THEN
280 RAISE EXCEPTION 'Current user is superuser - cannot continue.';
281 END IF;
282 RETURN 'assert_user_is_not_superuser: OK';
283END;
284$$ language plpgsql;
285COMMENT ON FUNCTION _v.assert_user_is_not_superuser() IS 'Function that can be used to make sure that patch is being applied using normal (not superuser) account.';
286
287CREATE OR REPLACE FUNCTION _v.assert_user_is_one_of(VARIADIC p_acceptable_users TEXT[] ) RETURNS TEXT as $$
288DECLARE
289BEGIN
290 IF current_user = any( p_acceptable_users ) THEN
291 RETURN 'assert_user_is_one_of: OK';
292 END IF;
293 RAISE EXCEPTION 'User is not one of: % - cannot continue.', p_acceptable_users;
294END;
295$$ language plpgsql;
296COMMENT ON FUNCTION _v.assert_user_is_one_of(TEXT[]) IS 'Function that can be used to make sure that patch is being applied by one of defined users.';
297
298COMMIT;
diff --git a/src/lib/sq/.gitignore b/src/lib/sq/.gitignore
new file mode 100644
index 000000000..951587047
--- /dev/null
+++ b/src/lib/sq/.gitignore
@@ -0,0 +1 @@
test_sq
diff --git a/src/lib/sq/Makefile.am b/src/lib/sq/Makefile.am
new file mode 100644
index 000000000..a77a380af
--- /dev/null
+++ b/src/lib/sq/Makefile.am
@@ -0,0 +1,38 @@
1# This Makefile.am is in the public domain
2AM_CPPFLAGS = -I$(top_srcdir)/src/include
3
4if USE_COVERAGE
5 AM_CFLAGS = --coverage
6endif
7
8if HAVE_SQLITE
9lib_LTLIBRARIES = libgnunetsq.la
10endif
11
12libgnunetsq_la_SOURCES = \
13 sq.c \
14 sq_exec.c \
15 sq_prepare.c \
16 sq_query_helper.c \
17 sq_result_helper.c
18libgnunetsq_la_LIBADD = -lsqlite3 \
19 $(top_builddir)/src/lib/util/libgnunetutil.la
20libgnunetsq_la_LDFLAGS = \
21 $(GN_LIBINTL) \
22 $(GN_LIB_LDFLAGS) \
23 -version-info 0:0:0
24
25if ENABLE_TEST_RUN
26TESTS = \
27 test_sq
28endif
29
30check_PROGRAMS= \
31 test_sq
32
33test_sq_SOURCES = \
34 test_sq.c
35test_sq_LDADD = \
36 libgnunetsq.la \
37 $(top_builddir)/src/lib/util/libgnunetutil.la \
38 -lsqlite3 $(XLIB)
diff --git a/src/lib/sq/meson.build b/src/lib/sq/meson.build
new file mode 100644
index 000000000..69d372cac
--- /dev/null
+++ b/src/lib/sq/meson.build
@@ -0,0 +1,30 @@
1libgnunetsq_src = ['sq.c',
2 'sq_exec.c',
3 'sq_prepare.c',
4 'sq_query_helper.c',
5 'sq_result_helper.c']
6
7libgnunetsq = library('gnunetsq',
8 libgnunetsq_src,
9 soversion: '0',
10 version: '0.0.0',
11 dependencies: [libgnunetutil_dep, sqlite_dep],
12 include_directories: [incdir, configuration_inc],
13 install: true,
14 install_dir: get_option('libdir'))
15pkg.generate(libgnunetsq, url: 'https://www.gnunet.org',
16 description : 'Provides API for accessing the SQ service')
17libgnunetsq_dep = declare_dependency(link_with : libgnunetsq)
18testsq = executable ('test_sq',
19 ['test_sq.c'],
20 dependencies: [libgnunetutil_dep,
21 sqlite_dep,
22 libgnunetsq_dep],
23 include_directories: [incdir, configuration_inc],
24 build_by_default: false,
25 install: false)
26test('test_sq', testsq,
27 workdir: meson.current_build_dir(),
28 suite: ['sq'])
29
30
diff --git a/src/lib/sq/sq.c b/src/lib/sq/sq.c
new file mode 100644
index 000000000..96d1d333d
--- /dev/null
+++ b/src/lib/sq/sq.c
@@ -0,0 +1,131 @@
1/*
2 This file is part of GNUnet
3 Copyright (C) 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 sq/sq.c
22 * @brief helper functions for Sqlite3 DB interactions
23 * @author Christian Grothoff
24 */
25#include "platform.h"
26#include "gnunet_sq_lib.h"
27
28
29enum GNUNET_GenericReturnValue
30GNUNET_SQ_bind (sqlite3_stmt *stmt,
31 const struct GNUNET_SQ_QueryParam *params)
32{
33 unsigned int j;
34
35 j = 1;
36 for (unsigned int i = 0; NULL != params[i].conv; i++)
37 {
38 if (GNUNET_OK !=
39 params[i].conv (params[i].conv_cls,
40 params[i].data,
41 params[i].size,
42 stmt,
43 j))
44 {
45 GNUNET_log_from (GNUNET_ERROR_TYPE_WARNING,
46 "sq",
47 _ ("Failure to bind %u-th SQL parameter\n"),
48 i);
49 if (SQLITE_OK !=
50 sqlite3_reset (stmt))
51 {
52 GNUNET_log_from (GNUNET_ERROR_TYPE_WARNING,
53 "sq",
54 _ ("Failure in sqlite3_reset (!)\n"));
55 return GNUNET_SYSERR;
56 }
57 }
58 GNUNET_assert (0 != params[i].num_params);
59 j += params[i].num_params;
60 }
61 return GNUNET_OK;
62}
63
64
65/**
66 * Extract results from a query result according to the given specification.
67 *
68 * @param result result to process
69 * @param[in,out] rs result specification to extract for
70 * @return
71 * #GNUNET_OK if all results could be extracted
72 * #GNUNET_SYSERR if a result was invalid (non-existing field)
73 */
74enum GNUNET_GenericReturnValue
75GNUNET_SQ_extract_result (sqlite3_stmt *result,
76 struct GNUNET_SQ_ResultSpec *rs)
77{
78 unsigned int j = 0;
79
80 for (unsigned int i = 0; NULL != rs[i].conv; i++)
81 {
82 if (NULL == rs[i].result_size)
83 rs[i].result_size = &rs[i].dst_size;
84 if (GNUNET_OK !=
85 rs[i].conv (rs[i].cls,
86 result,
87 j,
88 rs[i].result_size,
89 rs[i].dst))
90 {
91 for (unsigned int k = 0; k < i; k++)
92 if (NULL != rs[k].cleaner)
93 rs[k].cleaner (rs[k].cls);
94 return GNUNET_SYSERR;
95 }
96 GNUNET_assert (0 != rs[i].num_params);
97 j += rs[i].num_params;
98 }
99 return GNUNET_OK;
100}
101
102
103void
104GNUNET_SQ_cleanup_result (struct GNUNET_SQ_ResultSpec *rs)
105{
106 for (unsigned int i = 0; NULL != rs[i].conv; i++)
107 if (NULL != rs[i].cleaner)
108 rs[i].cleaner (rs[i].cls);
109}
110
111
112/**
113 * Reset @a stmt and log error.
114 *
115 * @param dbh database handle
116 * @param stmt statement to reset
117 */
118void
119GNUNET_SQ_reset (sqlite3 *dbh,
120 sqlite3_stmt *stmt)
121{
122 if (SQLITE_OK !=
123 sqlite3_reset (stmt))
124 GNUNET_log_from (GNUNET_ERROR_TYPE_ERROR | GNUNET_ERROR_TYPE_BULK,
125 "sqlite",
126 _ ("Failed to reset sqlite statement with error: %s\n"),
127 sqlite3_errmsg (dbh));
128}
129
130
131/* end of sq.c */
diff --git a/src/lib/sq/sq_exec.c b/src/lib/sq/sq_exec.c
new file mode 100644
index 000000000..d4690ee99
--- /dev/null
+++ b/src/lib/sq/sq_exec.c
@@ -0,0 +1,113 @@
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 sq/sq_exec.c
22 * @brief helper functions for executing SQL statements
23 * @author Christian Grothoff
24 */
25#include "platform.h"
26#include "gnunet_sq_lib.h"
27
28
29/**
30 * Create a `struct GNUNET_SQ_ExecuteStatement` where errors are fatal.
31 *
32 * @param sql actual SQL statement
33 * @return initialized struct
34 */
35struct GNUNET_SQ_ExecuteStatement
36GNUNET_SQ_make_execute (const char *sql)
37{
38 struct GNUNET_SQ_ExecuteStatement es = {
39 .sql = sql,
40 .ignore_errors = GNUNET_NO
41 };
42
43 return es;
44}
45
46
47/**
48 * Create a `struct GNUNET_SQ_ExecuteStatement` where errors should
49 * be tolerated.
50 *
51 * @param sql actual SQL statement
52 * @return initialized struct
53 */
54struct GNUNET_SQ_ExecuteStatement
55GNUNET_SQ_make_try_execute (const char *sql)
56{
57 struct GNUNET_SQ_ExecuteStatement es = {
58 .sql = sql,
59 .ignore_errors = GNUNET_YES
60 };
61
62 return es;
63}
64
65
66/**
67 * Request execution of an array of statements @a es from Postgres.
68 *
69 * @param dbh database to execute the statements over
70 * @param es #GNUNET_PQ_PREPARED_STATEMENT_END-terminated array of prepared
71 * statements.
72 * @return #GNUNET_OK on success (modulo statements where errors can be ignored)
73 * #GNUNET_SYSERR on error
74 */
75enum GNUNET_GenericReturnValue
76GNUNET_SQ_exec_statements (sqlite3 *dbh,
77 const struct GNUNET_SQ_ExecuteStatement *es)
78{
79 for (unsigned int i = 0; NULL != es[i].sql; i++)
80 {
81 char *emsg = NULL;
82
83 if (SQLITE_OK !=
84 sqlite3_exec (dbh,
85 es[i].sql,
86 NULL,
87 NULL,
88 &emsg))
89 {
90 if (es[i].ignore_errors)
91 {
92 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
93 "Failed to run SQL `%s': %s\n",
94 es[i].sql,
95 emsg);
96 }
97 else
98 {
99 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
100 "Failed to run SQL `%s': %s\n",
101 es[i].sql,
102 emsg);
103 sqlite3_free (emsg);
104 return GNUNET_SYSERR;
105 }
106 sqlite3_free (emsg);
107 }
108 }
109 return GNUNET_OK;
110}
111
112
113/* end of sq_exec */
diff --git a/src/lib/sq/sq_prepare.c b/src/lib/sq/sq_prepare.c
new file mode 100644
index 000000000..d1ca0e8bd
--- /dev/null
+++ b/src/lib/sq/sq_prepare.c
@@ -0,0 +1,77 @@
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 sq/sq_prepare.c
22 * @brief helper functions for executing SQL statements
23 * @author Christian Grothoff
24 */
25#include "platform.h"
26#include "gnunet_sq_lib.h"
27
28
29/**
30 * Create a `struct GNUNET_SQ_PrepareStatement`
31 *
32 * @param sql actual SQL statement
33 * @param pstmt where to store the handle
34 * @return initialized struct
35 */
36struct GNUNET_SQ_PrepareStatement
37GNUNET_SQ_make_prepare (const char *sql,
38 sqlite3_stmt **pstmt)
39{
40 struct GNUNET_SQ_PrepareStatement ps = {
41 .sql = sql,
42 .pstmt = pstmt
43 };
44
45 return ps;
46}
47
48
49enum GNUNET_GenericReturnValue
50GNUNET_SQ_prepare (sqlite3 *dbh,
51 const struct GNUNET_SQ_PrepareStatement *ps)
52{
53 for (unsigned int i = 0; NULL != ps[i].sql; i++)
54 {
55 const char *epos = NULL;
56 int ret;
57
58 if (SQLITE_OK !=
59 (ret = sqlite3_prepare_v2 (dbh,
60 ps[i].sql,
61 strlen (ps[i].sql),
62 ps[i].pstmt,
63 &epos)))
64 {
65 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
66 "Failed to prepare SQL `%s': error %d at %s\n",
67 ps[i].sql,
68 ret,
69 epos);
70 return GNUNET_SYSERR;
71 }
72 }
73 return GNUNET_OK;
74}
75
76
77/* end of sq_prepare.c */
diff --git a/src/lib/sq/sq_query_helper.c b/src/lib/sq/sq_query_helper.c
new file mode 100644
index 000000000..ead1b5bdd
--- /dev/null
+++ b/src/lib/sq/sq_query_helper.c
@@ -0,0 +1,510 @@
1/*
2 This file is part of GNUnet
3 Copyright (C) 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 sq/sq_query_helper.c
22 * @brief helper functions for queries
23 * @author Christian Grothoff
24 */
25#include "platform.h"
26#include "gnunet_sq_lib.h"
27
28
29/**
30 * Function called to convert input argument into SQL parameters.
31 *
32 * @param cls closure
33 * @param data pointer to input argument
34 * @param data_len number of bytes in @a data (if applicable)
35 * @param stmt sqlite statement to bind parameters for
36 * @param off offset of the argument to bind in @a stmt, numbered from 1,
37 * so immediately suitable for passing to `sqlite3_bind`-functions.
38 * @return #GNUNET_SYSERR on error, #GNUNET_OK on success
39 */
40static int
41bind_fixed_blob (void *cls,
42 const void *data,
43 size_t data_len,
44 sqlite3_stmt *stmt,
45 unsigned int off)
46{
47 if (SQLITE_OK !=
48 sqlite3_bind_blob64 (stmt,
49 (int) off,
50 data,
51 (sqlite3_uint64) data_len,
52 SQLITE_TRANSIENT))
53 return GNUNET_SYSERR;
54 return GNUNET_OK;
55}
56
57
58/**
59 * Generate query parameter for a buffer @a ptr of
60 * @a ptr_size bytes.
61 *
62 * @param ptr pointer to the query parameter to pass
63 * @param ptr_size number of bytes in @a ptr
64 */
65struct GNUNET_SQ_QueryParam
66GNUNET_SQ_query_param_fixed_size (const void *ptr,
67 size_t ptr_size)
68{
69 struct GNUNET_SQ_QueryParam qp = {
70 .conv = &bind_fixed_blob,
71 .data = ptr,
72 .size = ptr_size,
73 .num_params = 1
74 };
75
76 return qp;
77}
78
79
80/**
81 * Function called to convert input argument into SQL parameters.
82 *
83 * @param cls closure
84 * @param data pointer to input argument
85 * @param data_len number of bytes in @a data (if applicable)
86 * @param stmt sqlite statement to bind parameters for
87 * @param off offset of the argument to bind in @a stmt, numbered from 1,
88 * so immediately suitable for passing to `sqlite3_bind`-functions.
89 * @return #GNUNET_SYSERR on error, #GNUNET_OK on success
90 */
91static int
92bind_string (void *cls,
93 const void *data,
94 size_t data_len,
95 sqlite3_stmt *stmt,
96 unsigned int off)
97{
98 if (NULL == data)
99 {
100 if (SQLITE_OK !=
101 sqlite3_bind_null (stmt,
102 (int) off))
103 return GNUNET_SYSERR;
104 return GNUNET_OK;
105 }
106 if (SQLITE_OK !=
107 sqlite3_bind_text (stmt,
108 (int) off,
109 (const char *) data,
110 -1,
111 SQLITE_TRANSIENT))
112 return GNUNET_SYSERR;
113 return GNUNET_OK;
114}
115
116
117/**
118 * Generate query parameter for a string.
119 *
120 * @param ptr pointer to the string query parameter to pass
121 */
122struct GNUNET_SQ_QueryParam
123GNUNET_SQ_query_param_string (const char *ptr)
124{
125 struct GNUNET_SQ_QueryParam qp = {
126 .conv = &bind_string,
127 .data = ptr,
128 .num_params = 1
129 };
130
131 return qp;
132}
133
134
135/**
136 * Function called to convert input argument into SQL parameters.
137 *
138 * @param cls closure
139 * @param data pointer to input argument
140 * @param data_len number of bytes in @a data (if applicable)
141 * @param stmt sqlite statement to bind parameters for
142 * @param off offset of the argument to bind in @a stmt, numbered from 1,
143 * so immediately suitable for passing to `sqlite3_bind`-functions.
144 * @return #GNUNET_SYSERR on error, #GNUNET_OK on success
145 */
146static int
147bind_rsa_pub (void *cls,
148 const void *data,
149 size_t data_len,
150 sqlite3_stmt *stmt,
151 unsigned int off)
152{
153 const struct GNUNET_CRYPTO_RsaPublicKey *rsa = data;
154 void *buf;
155 size_t buf_size;
156
157 GNUNET_break (NULL == cls);
158 buf_size = GNUNET_CRYPTO_rsa_public_key_encode (rsa,
159 &buf);
160 if (SQLITE_OK !=
161 sqlite3_bind_blob64 (stmt,
162 (int) off,
163 buf,
164 (sqlite3_uint64) buf_size,
165 SQLITE_TRANSIENT))
166 {
167 GNUNET_free (buf);
168 return GNUNET_SYSERR;
169 }
170 GNUNET_free (buf);
171 return GNUNET_OK;
172}
173
174
175/**
176 * Generate query parameter for an RSA public key. The
177 * database must contain a BLOB type in the respective position.
178 *
179 * @param x the query parameter to pass.
180 */
181struct GNUNET_SQ_QueryParam
182GNUNET_SQ_query_param_rsa_public_key (const struct
183 GNUNET_CRYPTO_RsaPublicKey *x)
184{
185 struct GNUNET_SQ_QueryParam qp = {
186 .conv = &bind_rsa_pub,
187 .data = x,
188 .num_params = 1
189 };
190
191 return qp;
192}
193
194
195/**
196 * Function called to convert input argument into SQL parameters.
197 *
198 * @param cls closure
199 * @param data pointer to input argument
200 * @param data_len number of bytes in @a data (if applicable)
201 * @param stmt sqlite statement to bind parameters for
202 * @param off offset of the argument to bind in @a stmt, numbered from 1,
203 * so immediately suitable for passing to `sqlite3_bind`-functions.
204 * @return #GNUNET_SYSERR on error, #GNUNET_OK on success
205 */
206static int
207bind_rsa_sig (void *cls,
208 const void *data,
209 size_t data_len,
210 sqlite3_stmt *stmt,
211 unsigned int off)
212{
213 const struct GNUNET_CRYPTO_RsaSignature *sig = data;
214 void *buf;
215 size_t buf_size;
216
217 GNUNET_break (NULL == cls);
218 buf_size = GNUNET_CRYPTO_rsa_signature_encode (sig,
219 &buf);
220 if (SQLITE_OK !=
221 sqlite3_bind_blob64 (stmt,
222 (int) off,
223 buf,
224 (sqlite3_uint64) buf_size,
225 SQLITE_TRANSIENT))
226 {
227 GNUNET_free (buf);
228 return GNUNET_SYSERR;
229 }
230 GNUNET_free (buf);
231 return GNUNET_OK;
232}
233
234
235/**
236 * Generate query parameter for an RSA signature. The
237 * database must contain a BLOB type in the respective position.
238 *
239 * @param x the query parameter to pass
240 */
241struct GNUNET_SQ_QueryParam
242GNUNET_SQ_query_param_rsa_signature (const struct GNUNET_CRYPTO_RsaSignature *x)
243{
244 struct GNUNET_SQ_QueryParam qp = {
245 .conv = &bind_rsa_sig,
246 .data = x,
247 .num_params = 1
248 };
249
250 return qp;
251}
252
253
254/**
255 * Function called to convert input argument into SQL parameters.
256 *
257 * @param cls closure
258 * @param data pointer to input argument
259 * @param data_len number of bytes in @a data (if applicable)
260 * @param stmt sqlite statement to bind parameters for
261 * @param off offset of the argument to bind in @a stmt, numbered from 1,
262 * so immediately suitable for passing to `sqlite3_bind`-functions.
263 * @return #GNUNET_SYSERR on error, #GNUNET_OK on success
264 */
265static int
266bind_abstime (void *cls,
267 const void *data,
268 size_t data_len,
269 sqlite3_stmt *stmt,
270 unsigned int off)
271{
272 const struct GNUNET_TIME_Absolute *u = data;
273 struct GNUNET_TIME_Absolute abs;
274
275 abs = *u;
276 if (abs.abs_value_us > INT64_MAX)
277 abs.abs_value_us = INT64_MAX;
278 GNUNET_assert (sizeof(uint64_t) == data_len);
279 if (SQLITE_OK !=
280 sqlite3_bind_int64 (stmt,
281 (int) off,
282 (sqlite3_int64) abs.abs_value_us))
283 return GNUNET_SYSERR;
284 return GNUNET_OK;
285}
286
287
288/**
289 * Generate query parameter for an absolute time value.
290 * The database must store a 64-bit integer.
291 *
292 * @param x pointer to the query parameter to pass
293 */
294struct GNUNET_SQ_QueryParam
295GNUNET_SQ_query_param_absolute_time (const struct GNUNET_TIME_Absolute *x)
296{
297 struct GNUNET_SQ_QueryParam qp = {
298 .conv = &bind_abstime,
299 .data = x,
300 .size = sizeof(struct GNUNET_TIME_Absolute),
301 .num_params = 1
302 };
303
304 return qp;
305}
306
307
308/**
309 * Function called to convert input argument into SQL parameters.
310 *
311 * @param cls closure
312 * @param data pointer to input argument
313 * @param data_len number of bytes in @a data (if applicable)
314 * @param stmt sqlite statement to bind parameters for
315 * @param off offset of the argument to bind in @a stmt, numbered from 1,
316 * so immediately suitable for passing to `sqlite3_bind`-functions.
317 * @return #GNUNET_SYSERR on error, #GNUNET_OK on success
318 */
319static int
320bind_nbotime (void *cls,
321 const void *data,
322 size_t data_len,
323 sqlite3_stmt *stmt,
324 unsigned int off)
325{
326 const struct GNUNET_TIME_AbsoluteNBO *u = data;
327 struct GNUNET_TIME_Absolute abs;
328
329 abs = GNUNET_TIME_absolute_ntoh (*u);
330 if (abs.abs_value_us > INT64_MAX)
331 abs.abs_value_us = INT64_MAX;
332 GNUNET_assert (sizeof(uint64_t) == data_len);
333 if (SQLITE_OK !=
334 sqlite3_bind_int64 (stmt,
335 (int) off,
336 (sqlite3_int64) abs.abs_value_us))
337 return GNUNET_SYSERR;
338 return GNUNET_OK;
339}
340
341
342/**
343 * Generate query parameter for an absolute time value.
344 * The database must store a 64-bit integer.
345 *
346 * @param x pointer to the query parameter to pass
347 */
348struct GNUNET_SQ_QueryParam
349GNUNET_SQ_query_param_absolute_time_nbo (const struct
350 GNUNET_TIME_AbsoluteNBO *x)
351{
352 struct GNUNET_SQ_QueryParam qp = {
353 .conv = &bind_nbotime,
354 .data = x,
355 .size = sizeof(struct GNUNET_TIME_AbsoluteNBO),
356 .num_params = 1
357 };
358
359 return qp;
360}
361
362
363/**
364 * Function called to convert input argument into SQL parameters.
365 *
366 * @param cls closure
367 * @param data pointer to input argument
368 * @param data_len number of bytes in @a data (if applicable)
369 * @param stmt sqlite statement to bind parameters for
370 * @param off offset of the argument to bind in @a stmt, numbered from 1,
371 * so immediately suitable for passing to `sqlite3_bind`-functions.
372 * @return #GNUNET_SYSERR on error, #GNUNET_OK on success
373 */
374static int
375bind_u16 (void *cls,
376 const void *data,
377 size_t data_len,
378 sqlite3_stmt *stmt,
379 unsigned int off)
380{
381 const uint16_t *u = data;
382
383 GNUNET_assert (sizeof(uint16_t) == data_len);
384 if (SQLITE_OK !=
385 sqlite3_bind_int (stmt,
386 (int) off,
387 (int) *u))
388 return GNUNET_SYSERR;
389 return GNUNET_OK;
390}
391
392
393/**
394 * Generate query parameter for an uint16_t in host byte order.
395 *
396 * @param x pointer to the query parameter to pass
397 */
398struct GNUNET_SQ_QueryParam
399GNUNET_SQ_query_param_uint16 (const uint16_t *x)
400{
401 struct GNUNET_SQ_QueryParam qp = {
402 .conv = &bind_u16,
403 .data = x,
404 .size = sizeof(uint16_t),
405 .num_params = 1
406 };
407
408 return qp;
409}
410
411
412/**
413 * Function called to convert input argument into SQL parameters.
414 *
415 * @param cls closure
416 * @param data pointer to input argument
417 * @param data_len number of bytes in @a data (if applicable)
418 * @param stmt sqlite statement to bind parameters for
419 * @param off offset of the argument to bind in @a stmt, numbered from 1,
420 * so immediately suitable for passing to `sqlite3_bind`-functions.
421 * @return #GNUNET_SYSERR on error, #GNUNET_OK on success
422 */
423static int
424bind_u32 (void *cls,
425 const void *data,
426 size_t data_len,
427 sqlite3_stmt *stmt,
428 unsigned int off)
429{
430 const uint32_t *u = data;
431
432 GNUNET_assert (sizeof(uint32_t) == data_len);
433 if (SQLITE_OK !=
434 sqlite3_bind_int64 (stmt,
435 (int) off,
436 (sqlite3_int64) * u))
437 return GNUNET_SYSERR;
438 return GNUNET_OK;
439}
440
441
442/**
443 * Generate query parameter for an uint32_t in host byte order.
444 *
445 * @param x pointer to the query parameter to pass
446 */
447struct GNUNET_SQ_QueryParam
448GNUNET_SQ_query_param_uint32 (const uint32_t *x)
449{
450 struct GNUNET_SQ_QueryParam qp = {
451 .conv = &bind_u32,
452 .data = x,
453 .size = sizeof(uint32_t),
454 .num_params = 1
455 };
456
457 return qp;
458}
459
460
461/**
462 * Function called to convert input argument into SQL parameters.
463 *
464 * @param cls closure
465 * @param data pointer to input argument
466 * @param data_len number of bytes in @a data (if applicable)
467 * @param stmt sqlite statement to bind parameters for
468 * @param off offset of the argument to bind in @a stmt, numbered from 1,
469 * so immediately suitable for passing to `sqlite3_bind`-functions.
470 * @return #GNUNET_SYSERR on error, #GNUNET_OK on success
471 */
472static int
473bind_u64 (void *cls,
474 const void *data,
475 size_t data_len,
476 sqlite3_stmt *stmt,
477 unsigned int off)
478{
479 const uint64_t *u = data;
480
481 GNUNET_assert (sizeof(uint64_t) == data_len);
482 if (SQLITE_OK !=
483 sqlite3_bind_int64 (stmt,
484 (int) off,
485 (sqlite3_int64) * u))
486 return GNUNET_SYSERR;
487 return GNUNET_OK;
488}
489
490
491/**
492 * Generate query parameter for an uint16_t in host byte order.
493 *
494 * @param x pointer to the query parameter to pass
495 */
496struct GNUNET_SQ_QueryParam
497GNUNET_SQ_query_param_uint64 (const uint64_t *x)
498{
499 struct GNUNET_SQ_QueryParam qp = {
500 .conv = &bind_u64,
501 .data = x,
502 .size = sizeof(uint64_t),
503 .num_params = 1
504 };
505
506 return qp;
507}
508
509
510/* end of sq_query_helper.c */
diff --git a/src/lib/sq/sq_result_helper.c b/src/lib/sq/sq_result_helper.c
new file mode 100644
index 000000000..5ea3f1e56
--- /dev/null
+++ b/src/lib/sq/sq_result_helper.c
@@ -0,0 +1,785 @@
1/*
2 This file is part of GNUnet
3 Copyright (C) 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 sq/sq_result_helper.c
22 * @brief helper functions for queries
23 * @author Christian Grothoff
24 */
25#include "platform.h"
26#include "gnunet_sq_lib.h"
27
28
29/**
30 * Extract variable-sized binary data from a Postgres database @a result at row @a row.
31 *
32 * @param cls closure
33 * @param result where to extract data from
34 * @param column column to extract data from
35 * @param[in,out] dst_size where to store size of result, may be NULL
36 * @param[out] dst where to store the result (actually a `void **`)
37 * @return
38 * #GNUNET_YES if all results could be extracted
39 * #GNUNET_SYSERR if a result was invalid (non-existing field or NULL)
40 */
41static int
42extract_var_blob (void *cls,
43 sqlite3_stmt *result,
44 unsigned int column,
45 size_t *dst_size,
46 void *dst)
47{
48 int have;
49 const void *ret;
50 void **rdst = (void **) dst;
51
52 if (SQLITE_NULL ==
53 sqlite3_column_type (result,
54 column))
55 {
56 *rdst = NULL;
57 *dst_size = 0;
58 return GNUNET_YES;
59 }
60
61 if (SQLITE_BLOB !=
62 sqlite3_column_type (result,
63 column))
64 {
65 GNUNET_break (0);
66 return GNUNET_SYSERR;
67 }
68 /* sqlite manual says to invoke 'sqlite3_column_blob()'
69 before calling sqlite3_column_bytes() */
70 ret = sqlite3_column_blob (result,
71 column);
72 have = sqlite3_column_bytes (result,
73 column);
74 if (have < 0)
75 {
76 GNUNET_break (0);
77 return GNUNET_SYSERR;
78 }
79 *dst_size = have;
80 if (0 == have)
81 {
82 *rdst = NULL;
83 return GNUNET_OK;
84 }
85 *rdst = GNUNET_malloc (have);
86 GNUNET_memcpy (*rdst,
87 ret,
88 have);
89 return GNUNET_OK;
90}
91
92
93/**
94 * Cleanup memory allocated by #extract_var_blob().
95 *
96 * @param cls pointer to pointer of allocation
97 */
98static void
99clean_var_blob (void *cls)
100{
101 void **dptr = (void **) cls;
102
103 if (NULL != *dptr)
104 {
105 GNUNET_free (*dptr);
106 *dptr = NULL;
107 }
108}
109
110
111/**
112 * Variable-size result expected.
113 *
114 * @param[out] dst where to store the result, allocated
115 * @param[out] sptr where to store the size of @a dst
116 * @return array entry for the result specification to use
117 */
118struct GNUNET_SQ_ResultSpec
119GNUNET_SQ_result_spec_variable_size (void **dst,
120 size_t *sptr)
121{
122 struct GNUNET_SQ_ResultSpec rs = {
123 .conv = &extract_var_blob,
124 .cleaner = &clean_var_blob,
125 .dst = dst,
126 .cls = dst,
127 .result_size = sptr,
128 .num_params = 1
129 };
130
131 return rs;
132}
133
134
135/**
136 * Extract fixed-sized binary data from a Postgres database @a result at row @a row.
137 *
138 * @param cls closure
139 * @param result where to extract data from
140 * @param column column to extract data from
141 * @param[in,out] dst_size where to store size of result, may be NULL
142 * @param[out] dst where to store the result
143 * @return
144 * #GNUNET_YES if all results could be extracted
145 * #GNUNET_SYSERR if a result was invalid (non-existing field or NULL)
146 */
147static int
148extract_fixed_blob (void *cls,
149 sqlite3_stmt *result,
150 unsigned int column,
151 size_t *dst_size,
152 void *dst)
153{
154 int have;
155 const void *ret;
156
157 if ((0 == *dst_size) &&
158 (SQLITE_NULL ==
159 sqlite3_column_type (result,
160 column)))
161 {
162 return GNUNET_YES;
163 }
164
165 if (SQLITE_BLOB !=
166 sqlite3_column_type (result,
167 column))
168 {
169 GNUNET_break (0);
170 return GNUNET_SYSERR;
171 }
172 /* sqlite manual says to invoke 'sqlite3_column_blob()'
173 before calling sqlite3_column_bytes() */
174 ret = sqlite3_column_blob (result,
175 column);
176 have = sqlite3_column_bytes (result,
177 column);
178 if (*dst_size != have)
179 {
180 GNUNET_break (0);
181 return GNUNET_SYSERR;
182 }
183 GNUNET_memcpy (dst,
184 ret,
185 have);
186 return GNUNET_OK;
187}
188
189
190/**
191 * Fixed-size result expected.
192 *
193 * @param[out] dst where to store the result
194 * @param dst_size number of bytes in @a dst
195 * @return array entry for the result specification to use
196 */
197struct GNUNET_SQ_ResultSpec
198GNUNET_SQ_result_spec_fixed_size (void *dst,
199 size_t dst_size)
200{
201 struct GNUNET_SQ_ResultSpec rs = {
202 .conv = &extract_fixed_blob,
203 .dst = dst,
204 .dst_size = dst_size,
205 .num_params = 1
206 };
207
208 return rs;
209}
210
211
212/**
213 * Extract fixed-sized binary data from a Postgres database @a result at row @a row.
214 *
215 * @param cls closure
216 * @param result where to extract data from
217 * @param column column to extract data from
218 * @param[in,out] dst_size where to store size of result, may be NULL
219 * @param[out] dst where to store the result
220 * @return
221 * #GNUNET_YES if all results could be extracted
222 * #GNUNET_SYSERR if a result was invalid (non-existing field or NULL)
223 */
224static int
225extract_utf8_string (void *cls,
226 sqlite3_stmt *result,
227 unsigned int column,
228 size_t *dst_size,
229 void *dst)
230{
231 const char *text;
232 char **rdst = dst;
233
234 if (SQLITE_NULL ==
235 sqlite3_column_type (result,
236 column))
237 {
238 *rdst = NULL;
239 return GNUNET_OK;
240 }
241 if (SQLITE_TEXT !=
242 sqlite3_column_type (result,
243 column))
244 {
245 GNUNET_break (0);
246 return GNUNET_SYSERR;
247 }
248 /* sqlite manual guarantees that 'sqlite3_column_text()'
249 is 0-terminated */
250 text = (const char *) sqlite3_column_text (result,
251 column);
252 if (NULL == text)
253 {
254 GNUNET_break (0);
255 return GNUNET_SYSERR;
256 }
257 *dst_size = strlen (text) + 1;
258 *rdst = GNUNET_strdup (text);
259 return GNUNET_OK;
260}
261
262
263/**
264 * Cleanup memory allocated by #extract_var_blob().
265 *
266 * @param cls pointer to pointer of allocation
267 */
268static void
269clean_utf8_string (void *cls)
270{
271 char **dptr = (char **) cls;
272
273 if (NULL != *dptr)
274 {
275 GNUNET_free (*dptr);
276 *dptr = NULL;
277 }
278}
279
280
281/**
282 * 0-terminated string expected.
283 *
284 * @param[out] dst where to store the result, allocated
285 * @return array entry for the result specification to use
286 */
287struct GNUNET_SQ_ResultSpec
288GNUNET_SQ_result_spec_string (char **dst)
289{
290 struct GNUNET_SQ_ResultSpec rs = {
291 .conv = &extract_utf8_string,
292 .cleaner = &clean_utf8_string,
293 .cls = dst,
294 .dst = dst,
295 .num_params = 1
296 };
297
298 return rs;
299}
300
301
302/**
303 * Extract data from a Postgres database @a result at row @a row.
304 *
305 * @param cls closure
306 * @param result where to extract data from
307 * @param column column to extract data from
308 * @param[in,out] dst_size where to store size of result, may be NULL
309 * @param[out] dst where to store the result
310 * @return
311 * #GNUNET_YES if all results could be extracted
312 * #GNUNET_SYSERR if a result was invalid (non-existing field or NULL)
313 */
314static int
315extract_rsa_pub (void *cls,
316 sqlite3_stmt *result,
317 unsigned int column,
318 size_t *dst_size,
319 void *dst)
320{
321 struct GNUNET_CRYPTO_RsaPublicKey **pk = dst;
322 int have;
323 const void *ret;
324
325 if (SQLITE_BLOB !=
326 sqlite3_column_type (result,
327 column))
328 {
329 GNUNET_break (0);
330 return GNUNET_SYSERR;
331 }
332 /* sqlite manual says to invoke 'sqlite3_column_blob()'
333 before calling sqlite3_column_bytes() */
334 ret = sqlite3_column_blob (result,
335 column);
336 have = sqlite3_column_bytes (result,
337 column);
338 if (have < 0)
339 {
340 GNUNET_break (0);
341 return GNUNET_SYSERR;
342 }
343
344 *pk = GNUNET_CRYPTO_rsa_public_key_decode (ret,
345 have);
346 if (NULL == *pk)
347 {
348 GNUNET_break (0);
349 return GNUNET_SYSERR;
350 }
351 return GNUNET_OK;
352}
353
354
355/**
356 * Function called to clean up memory allocated
357 * by a #GNUNET_PQ_ResultConverter.
358 *
359 * @param cls closure
360 */
361static void
362clean_rsa_pub (void *cls)
363{
364 struct GNUNET_CRYPTO_RsaPublicKey **pk = cls;
365
366 if (NULL != *pk)
367 {
368 GNUNET_CRYPTO_rsa_public_key_free (*pk);
369 *pk = NULL;
370 }
371}
372
373
374/**
375 * RSA public key expected.
376 *
377 * @param[out] rsa where to store the result
378 * @return array entry for the result specification to use
379 */
380struct GNUNET_SQ_ResultSpec
381GNUNET_SQ_result_spec_rsa_public_key (struct GNUNET_CRYPTO_RsaPublicKey **rsa)
382{
383 struct GNUNET_SQ_ResultSpec rs = {
384 .conv = &extract_rsa_pub,
385 .cleaner = &clean_rsa_pub,
386 .dst = rsa,
387 .cls = rsa,
388 .num_params = 1
389 };
390
391 return rs;
392}
393
394
395/**
396 * Extract data from a Postgres database @a result at row @a row.
397 *
398 * @param cls closure
399 * @param result where to extract data from
400 * @param column column to extract data from
401 * @param[in,out] dst_size where to store size of result, may be NULL
402 * @param[out] dst where to store the result
403 * @return
404 * #GNUNET_YES if all results could be extracted
405 * #GNUNET_SYSERR if a result was invalid (non-existing field or NULL)
406 */
407static int
408extract_rsa_sig (void *cls,
409 sqlite3_stmt *result,
410 unsigned int column,
411 size_t *dst_size,
412 void *dst)
413{
414 struct GNUNET_CRYPTO_RsaSignature **sig = dst;
415 int have;
416 const void *ret;
417
418 if (SQLITE_BLOB !=
419 sqlite3_column_type (result,
420 column))
421 {
422 GNUNET_break (0);
423 return GNUNET_SYSERR;
424 }
425 /* sqlite manual says to invoke 'sqlite3_column_blob()'
426 before calling sqlite3_column_bytes() */
427 ret = sqlite3_column_blob (result,
428 column);
429 have = sqlite3_column_bytes (result,
430 column);
431 if (have < 0)
432 {
433 GNUNET_break (0);
434 return GNUNET_SYSERR;
435 }
436
437 *sig = GNUNET_CRYPTO_rsa_signature_decode (ret,
438 have);
439 if (NULL == *sig)
440 {
441 GNUNET_break (0);
442 return GNUNET_SYSERR;
443 }
444 return GNUNET_OK;
445}
446
447
448/**
449 * Function called to clean up memory allocated
450 * by a #GNUNET_PQ_ResultConverter.
451 *
452 * @param cls result data to clean up
453 */
454static void
455clean_rsa_sig (void *cls)
456{
457 struct GNUNET_CRYPTO_RsaSignature **sig = cls;
458
459 if (NULL != *sig)
460 {
461 GNUNET_CRYPTO_rsa_signature_free (*sig);
462 *sig = NULL;
463 }
464}
465
466
467/**
468 * RSA signature expected.
469 *
470 * @param[out] sig where to store the result;
471 * @return array entry for the result specification to use
472 */
473struct GNUNET_SQ_ResultSpec
474GNUNET_SQ_result_spec_rsa_signature (struct GNUNET_CRYPTO_RsaSignature **sig)
475{
476 struct GNUNET_SQ_ResultSpec rs = {
477 .conv = &extract_rsa_sig,
478 .cleaner = &clean_rsa_sig,
479 .dst = sig,
480 .cls = sig,
481 .num_params = 1
482 };
483
484 return rs;
485}
486
487
488/**
489 * Extract absolute time value from a Postgres database @a result at row @a row.
490 *
491 * @param cls closure
492 * @param result where to extract data from
493 * @param column column to extract data from
494 * @param[in,out] dst_size where to store size of result, may be NULL
495 * @param[out] dst where to store the result
496 * @return
497 * #GNUNET_YES if all results could be extracted
498 * #GNUNET_SYSERR if a result was invalid (non-existing field or NULL)
499 */
500static int
501extract_abs_time (void *cls,
502 sqlite3_stmt *result,
503 unsigned int column,
504 size_t *dst_size,
505 void *dst)
506{
507 struct GNUNET_TIME_Absolute *u = dst;
508 struct GNUNET_TIME_Absolute t;
509
510 GNUNET_assert (sizeof(uint64_t) == *dst_size);
511 if (SQLITE_INTEGER !=
512 sqlite3_column_type (result,
513 column))
514 {
515 GNUNET_break (0);
516 return GNUNET_SYSERR;
517 }
518 t.abs_value_us = (uint64_t) sqlite3_column_int64 (result,
519 column);
520 if (INT64_MAX == t.abs_value_us)
521 t = GNUNET_TIME_UNIT_FOREVER_ABS;
522 *u = t;
523 return GNUNET_OK;
524}
525
526
527/**
528 * Absolute time expected.
529 *
530 * @param[out] at where to store the result
531 * @return array entry for the result specification to use
532 */
533struct GNUNET_SQ_ResultSpec
534GNUNET_SQ_result_spec_absolute_time (struct GNUNET_TIME_Absolute *at)
535{
536 struct GNUNET_SQ_ResultSpec rs = {
537 .conv = &extract_abs_time,
538 .dst = at,
539 .dst_size = sizeof(struct GNUNET_TIME_Absolute),
540 .num_params = 1
541 };
542
543 return rs;
544}
545
546
547/**
548 * Extract absolute time value in NBO from a Postgres database @a result at row @a row.
549 *
550 * @param cls closure
551 * @param result where to extract data from
552 * @param column column to extract data from
553 * @param[in,out] dst_size where to store size of result, may be NULL
554 * @param[out] dst where to store the result
555 * @return
556 * #GNUNET_YES if all results could be extracted
557 * #GNUNET_SYSERR if a result was invalid (non-existing field or NULL)
558 */
559static int
560extract_abs_time_nbo (void *cls,
561 sqlite3_stmt *result,
562 unsigned int column,
563 size_t *dst_size,
564 void *dst)
565{
566 struct GNUNET_TIME_AbsoluteNBO *u = dst;
567 struct GNUNET_TIME_Absolute t;
568
569 GNUNET_assert (sizeof(uint64_t) == *dst_size);
570 if (SQLITE_INTEGER !=
571 sqlite3_column_type (result,
572 column))
573 {
574 GNUNET_break (0);
575 return GNUNET_SYSERR;
576 }
577 t.abs_value_us = (uint64_t) sqlite3_column_int64 (result,
578 column);
579 if (INT64_MAX == t.abs_value_us)
580 t = GNUNET_TIME_UNIT_FOREVER_ABS;
581 *u = GNUNET_TIME_absolute_hton (t);
582 return GNUNET_OK;
583}
584
585
586/**
587 * Absolute time expected.
588 *
589 * @param[out] at where to store the result
590 * @return array entry for the result specification to use
591 */
592struct GNUNET_SQ_ResultSpec
593GNUNET_SQ_result_spec_absolute_time_nbo (struct GNUNET_TIME_AbsoluteNBO *at)
594{
595 struct GNUNET_SQ_ResultSpec rs = {
596 .conv = &extract_abs_time_nbo,
597 .dst = at,
598 .dst_size = sizeof(struct GNUNET_TIME_AbsoluteNBO),
599 .num_params = 1
600 };
601
602 return rs;
603}
604
605
606/**
607 * Extract 16-bit integer from a Postgres database @a result at row @a row.
608 *
609 * @param cls closure
610 * @param result where to extract data from
611 * @param column column to extract data from
612 * @param[in,out] dst_size where to store size of result, may be NULL
613 * @param[out] dst where to store the result
614 * @return
615 * #GNUNET_YES if all results could be extracted
616 * #GNUNET_SYSERR if a result was invalid (non-existing field or NULL)
617 */
618static int
619extract_uint16 (void *cls,
620 sqlite3_stmt *result,
621 unsigned int column,
622 size_t *dst_size,
623 void *dst)
624{
625 uint64_t v;
626 uint16_t *u = dst;
627
628 GNUNET_assert (sizeof(uint16_t) == *dst_size);
629 if (SQLITE_INTEGER !=
630 sqlite3_column_type (result,
631 column))
632 {
633 GNUNET_break (0);
634 return GNUNET_SYSERR;
635 }
636 v = (uint64_t) sqlite3_column_int64 (result,
637 column);
638 if (v > UINT16_MAX)
639 {
640 GNUNET_break (0);
641 return GNUNET_SYSERR;
642 }
643 *u = (uint16_t) v;
644 return GNUNET_OK;
645}
646
647
648/**
649 * uint16_t expected.
650 *
651 * @param[out] u16 where to store the result
652 * @return array entry for the result specification to use
653 */
654struct GNUNET_SQ_ResultSpec
655GNUNET_SQ_result_spec_uint16 (uint16_t *u16)
656{
657 struct GNUNET_SQ_ResultSpec rs = {
658 .conv = &extract_uint16,
659 .dst = u16,
660 .dst_size = sizeof(uint16_t),
661 .num_params = 1
662 };
663
664 return rs;
665}
666
667
668/**
669 * Extract 32-bit integer from a Postgres database @a result at row @a row.
670 *
671 * @param cls closure
672 * @param result where to extract data from
673 * @param column column to extract data from
674 * @param[in,out] dst_size where to store size of result, may be NULL
675 * @param[out] dst where to store the result
676 * @return
677 * #GNUNET_YES if all results could be extracted
678 * #GNUNET_SYSERR if a result was invalid (non-existing field or NULL)
679 */
680static int
681extract_uint32 (void *cls,
682 sqlite3_stmt *result,
683 unsigned int column,
684 size_t *dst_size,
685 void *dst)
686{
687 uint64_t v;
688 uint32_t *u = dst;
689
690 GNUNET_assert (sizeof(uint32_t) == *dst_size);
691 if (SQLITE_INTEGER !=
692 sqlite3_column_type (result,
693 column))
694 {
695 GNUNET_break (0);
696 return GNUNET_SYSERR;
697 }
698 v = (uint64_t) sqlite3_column_int64 (result,
699 column);
700 if (v > UINT32_MAX)
701 {
702 GNUNET_break (0);
703 return GNUNET_SYSERR;
704 }
705 *u = (uint32_t) v;
706 return GNUNET_OK;
707}
708
709
710/**
711 * uint32_t expected.
712 *
713 * @param[out] u32 where to store the result
714 * @return array entry for the result specification to use
715 */
716struct GNUNET_SQ_ResultSpec
717GNUNET_SQ_result_spec_uint32 (uint32_t *u32)
718{
719 struct GNUNET_SQ_ResultSpec rs = {
720 .conv = &extract_uint32,
721 .dst = u32,
722 .dst_size = sizeof(uint32_t),
723 .num_params = 1
724 };
725
726 return rs;
727}
728
729
730/**
731 * Extract 64-bit integer from a Postgres database @a result at row @a row.
732 *
733 * @param cls closure
734 * @param result where to extract data from
735 * @param column column to extract data from
736 * @param[in,out] dst_size where to store size of result, may be NULL
737 * @param[out] dst where to store the result
738 * @return
739 * #GNUNET_YES if all results could be extracted
740 * #GNUNET_SYSERR if a result was invalid (non-existing field or NULL)
741 */
742static int
743extract_uint64 (void *cls,
744 sqlite3_stmt *result,
745 unsigned int column,
746 size_t *dst_size,
747 void *dst)
748{
749 uint64_t *u = dst;
750
751 GNUNET_assert (sizeof(uint64_t) == *dst_size);
752 if (SQLITE_INTEGER !=
753 sqlite3_column_type (result,
754 column))
755 {
756 GNUNET_break (0);
757 return GNUNET_SYSERR;
758 }
759 *u = (uint64_t) sqlite3_column_int64 (result,
760 column);
761 return GNUNET_OK;
762}
763
764
765/**
766 * uint64_t expected.
767 *
768 * @param[out] u64 where to store the result
769 * @return array entry for the result specification to use
770 */
771struct GNUNET_SQ_ResultSpec
772GNUNET_SQ_result_spec_uint64 (uint64_t *u64)
773{
774 struct GNUNET_SQ_ResultSpec rs = {
775 .conv = &extract_uint64,
776 .dst = u64,
777 .dst_size = sizeof(uint64_t),
778 .num_params = 1
779 };
780
781 return rs;
782}
783
784
785/* end of sq_result_helper.c */
diff --git a/src/lib/sq/test_sq.c b/src/lib/sq/test_sq.c
new file mode 100644
index 000000000..292de5f34
--- /dev/null
+++ b/src/lib/sq/test_sq.c
@@ -0,0 +1,291 @@
1/*
2 This file is part of GNUnet
3 (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 * @file sq/test_sq.c
22 * @brief Tests for sqlite3 convenience API
23 * @author Christian Grothoff
24 */
25#include "platform.h"
26#include "gnunet_util_lib.h"
27#include "gnunet_sq_lib.h"
28
29
30/**
31 * @brief Prepare a SQL statement
32 *
33 * @param dbh handle to the database
34 * @param zSql SQL statement, UTF-8 encoded
35 * @param[out] ppStmt set to the prepared statement
36 * @return 0 on success
37 */
38static int
39sq_prepare (sqlite3 *dbh,
40 const char *zSql,
41 sqlite3_stmt **ppStmt)
42{
43 char *dummy;
44 int result;
45
46 result = sqlite3_prepare_v2 (dbh,
47 zSql,
48 strlen (zSql),
49 ppStmt,
50 (const char **) &dummy);
51 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
52 "Prepared `%s' / %p: %d\n",
53 zSql,
54 *ppStmt,
55 result);
56 return result;
57}
58
59
60/**
61 * Run actual test queries.
62 *
63 * @return 0 on success
64 */
65static int
66run_queries (sqlite3 *dbh)
67{
68 struct GNUNET_CRYPTO_RsaPublicKey *pub;
69 struct GNUNET_CRYPTO_RsaPublicKey *pub2 = NULL;
70 struct GNUNET_CRYPTO_RsaSignature *sig;
71 struct GNUNET_CRYPTO_RsaSignature *sig2 = NULL;
72 struct GNUNET_TIME_Absolute abs_time = GNUNET_TIME_absolute_get ();
73 struct GNUNET_TIME_Absolute abs_time2;
74 struct GNUNET_TIME_Absolute forever = GNUNET_TIME_UNIT_FOREVER_ABS;
75 struct GNUNET_TIME_Absolute forever2;
76 struct GNUNET_HashCode hc;
77 struct GNUNET_HashCode hc2;
78 sqlite3_stmt *stmt;
79 struct GNUNET_CRYPTO_RsaPrivateKey *priv;
80 const char msg[] = "hello";
81 void *msg2;
82 struct GNUNET_HashCode hmsg;
83 size_t msg2_len;
84 uint16_t u16;
85 uint16_t u162;
86 uint32_t u32;
87 uint32_t u322;
88 uint64_t u64;
89 uint64_t u642;
90
91 priv = GNUNET_CRYPTO_rsa_private_key_create (1024);
92 pub = GNUNET_CRYPTO_rsa_private_key_get_public (priv);
93 memset (&hmsg, 42, sizeof(hmsg));
94 sig = GNUNET_CRYPTO_rsa_sign_fdh (priv,
95 &hmsg,
96 sizeof (hmsg));
97 u16 = 16;
98 u32 = 32;
99 u64 = 64;
100 /* FIXME: test GNUNET_SQ_result_spec_variable_size */
101
102 sq_prepare (dbh,
103 "INSERT INTO test_sq ("
104 " pub"
105 ",sig"
106 ",abs_time"
107 ",forever"
108 ",hash"
109 ",vsize"
110 ",u16"
111 ",u32"
112 ",u64"
113 ") VALUES "
114 "($1, $2, $3, $4, $5, $6,"
115 "$7, $8, $9);",
116 &stmt);
117 {
118 struct GNUNET_SQ_QueryParam params_insert[] = {
119 GNUNET_SQ_query_param_rsa_public_key (pub),
120 GNUNET_SQ_query_param_rsa_signature (sig),
121 GNUNET_SQ_query_param_absolute_time (&abs_time),
122 GNUNET_SQ_query_param_absolute_time (&forever),
123 GNUNET_SQ_query_param_auto_from_type (&hc),
124 GNUNET_SQ_query_param_fixed_size (msg, strlen (msg)),
125 GNUNET_SQ_query_param_uint16 (&u16),
126 GNUNET_SQ_query_param_uint32 (&u32),
127 GNUNET_SQ_query_param_uint64 (&u64),
128 GNUNET_SQ_query_param_end
129 };
130
131 GNUNET_assert (GNUNET_OK ==
132 GNUNET_SQ_bind (stmt,
133 params_insert));
134 if (SQLITE_DONE !=
135 sqlite3_step (stmt))
136 {
137 GNUNET_CRYPTO_rsa_signature_free (sig);
138 GNUNET_CRYPTO_rsa_private_key_free (priv);
139 GNUNET_CRYPTO_rsa_public_key_free (pub);
140 return 1;
141 }
142 sqlite3_reset (stmt);
143 }
144 sqlite3_finalize (stmt);
145
146 sq_prepare (dbh,
147 "SELECT"
148 " pub"
149 ",sig"
150 ",abs_time"
151 ",forever"
152 ",hash"
153 ",vsize"
154 ",u16"
155 ",u32"
156 ",u64"
157 " FROM test_sq"
158 " ORDER BY abs_time DESC "
159 " LIMIT 1;",
160 &stmt);
161 {
162 struct GNUNET_SQ_QueryParam params_select[] = {
163 GNUNET_SQ_query_param_end
164 };
165 struct GNUNET_SQ_ResultSpec results_select[] = {
166 GNUNET_SQ_result_spec_rsa_public_key (&pub2),
167 GNUNET_SQ_result_spec_rsa_signature (&sig2),
168 GNUNET_SQ_result_spec_absolute_time (&abs_time2),
169 GNUNET_SQ_result_spec_absolute_time (&forever2),
170 GNUNET_SQ_result_spec_auto_from_type (&hc2),
171 GNUNET_SQ_result_spec_variable_size (&msg2, &msg2_len),
172 GNUNET_SQ_result_spec_uint16 (&u162),
173 GNUNET_SQ_result_spec_uint32 (&u322),
174 GNUNET_SQ_result_spec_uint64 (&u642),
175 GNUNET_SQ_result_spec_end
176 };
177
178 GNUNET_assert (GNUNET_OK ==
179 GNUNET_SQ_bind (stmt,
180 params_select));
181 if (SQLITE_ROW !=
182 sqlite3_step (stmt))
183 {
184 GNUNET_break (0);
185 sqlite3_finalize (stmt);
186 GNUNET_CRYPTO_rsa_signature_free (sig);
187 GNUNET_CRYPTO_rsa_private_key_free (priv);
188 GNUNET_CRYPTO_rsa_public_key_free (pub);
189 return 1;
190 }
191 GNUNET_assert (GNUNET_OK ==
192 GNUNET_SQ_extract_result (stmt,
193 results_select));
194 GNUNET_break (abs_time.abs_value_us == abs_time2.abs_value_us);
195 GNUNET_break (forever.abs_value_us == forever2.abs_value_us);
196 GNUNET_break (0 ==
197 GNUNET_memcmp (&hc,
198 &hc2));
199 GNUNET_break (0 ==
200 GNUNET_CRYPTO_rsa_signature_cmp (sig,
201 sig2));
202 GNUNET_break (0 ==
203 GNUNET_CRYPTO_rsa_public_key_cmp (pub,
204 pub2));
205 GNUNET_break (strlen (msg) == msg2_len);
206 GNUNET_break (0 ==
207 strncmp (msg,
208 msg2,
209 msg2_len));
210 GNUNET_break (16 == u162);
211 GNUNET_break (32 == u322);
212 GNUNET_break (64 == u642);
213 GNUNET_SQ_cleanup_result (results_select);
214 sqlite3_reset (stmt);
215 }
216 sqlite3_finalize (stmt);
217
218 GNUNET_CRYPTO_rsa_signature_free (sig);
219 GNUNET_CRYPTO_rsa_private_key_free (priv);
220 GNUNET_CRYPTO_rsa_public_key_free (pub);
221 return 0;
222}
223
224
225int
226main (int argc,
227 const char *const argv[])
228{
229 sqlite3 *dbh;
230 int ret;
231
232 GNUNET_log_setup ("test-sq",
233 "WARNING",
234 NULL);
235 if (SQLITE_OK !=
236 sqlite3_open ("test.db",
237 &dbh))
238 {
239 fprintf (stderr,
240 "Cannot run test, sqlite3 initialization failed\n");
241 GNUNET_break (0);
242 return 77; /* Signal test was skipped... */
243 }
244
245 if (SQLITE_OK !=
246 sqlite3_exec (dbh,
247 "CREATE TEMPORARY TABLE IF NOT EXISTS test_sq ("
248 " pub BYTEA NOT NULL"
249 ",sig BYTEA NOT NULL"
250 ",abs_time INT8 NOT NULL"
251 ",forever INT8 NOT NULL"
252 ",hash BYTEA NOT NULL"
253 ",vsize VARCHAR NOT NULL"
254 ",u16 INT2 NOT NULL"
255 ",u32 INT4 NOT NULL"
256 ",u64 INT8 NOT NULL"
257 ")",
258 NULL, NULL, NULL))
259 {
260 fprintf (stderr,
261 "Failed to create table\n");
262 GNUNET_break (SQLITE_OK ==
263 sqlite3_close (dbh));
264 if (0 != unlink ("test.db"))
265 GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_ERROR,
266 "unlink",
267 "test.db");
268 return 1;
269 }
270
271 ret = run_queries (dbh);
272 if (SQLITE_OK !=
273 sqlite3_exec (dbh,
274 "DROP TABLE test_sq",
275 NULL, NULL, NULL))
276 {
277 fprintf (stderr,
278 "Failed to drop table\n");
279 ret = 1;
280 }
281 GNUNET_break (SQLITE_OK ==
282 sqlite3_close (dbh));
283 if (0 != unlink ("test.db"))
284 GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_ERROR,
285 "unlink",
286 "test.db");
287 return ret;
288}
289
290
291/* end of test_sq.c */
diff --git a/src/lib/testing/.gitignore b/src/lib/testing/.gitignore
new file mode 100644
index 000000000..a1e77a8e4
--- /dev/null
+++ b/src/lib/testing/.gitignore
@@ -0,0 +1,9 @@
1test_testing_start_with_config
2list-keys
3gnunet-testing
4test_testing_peerstartup
5test_testing_peerstartup2
6test_testing_portreservation
7test_testing_servicestartup
8test_testing_sharedservices
9gnunet-cmds-helper
diff --git a/src/lib/testing/Makefile.am b/src/lib/testing/Makefile.am
new file mode 100644
index 000000000..cd53d7237
--- /dev/null
+++ b/src/lib/testing/Makefile.am
@@ -0,0 +1,43 @@
1# This Makefile.am is in the public domain
2AM_CPPFLAGS = -I$(top_srcdir)/src/include
3
4if USE_COVERAGE
5 AM_CFLAGS = --coverage -O0
6 XLIB = -lgcov
7endif
8
9libexecdir= $(pkglibdir)/libexec/
10
11libexec_PROGRAMS = \
12 gnunet-cmds-helper
13
14plugindir = $(libdir)/gnunet
15
16lib_LTLIBRARIES = \
17 libgnunettesting.la
18
19gnunet_cmds_helper_SOURCES = \
20 gnunet-cmds-helper.c
21gnunet_cmds_helper_LDADD = $(XLIB) \
22 libgnunettesting.la \
23 $(top_builddir)/src/lib/util/libgnunetutil.la \
24 $(LTLIBINTL) $(Z_LIBS)
25
26libgnunettesting_la_SOURCES = \
27 testing_api_cmd_barrier.c \
28 testing_api_cmd_barrier_reached.c \
29 testing_api_cmd_exec.c \
30 testing_api_cmd_finish.c \
31 testing_api_loop.c testing_api_loop.h \
32 testing_api_traits.c \
33 testing_api_cmd_netjail_start.c \
34 testing_api_cmd_netjail_start_cmds_helper.c \
35 netjail.c netjail.h \
36 barrier.c barrier.h \
37 testing_api_cmd_batch.c testing_api_cmd_batch.h
38libgnunettesting_la_LIBADD = \
39 $(top_builddir)/src/lib/util/libgnunetutil.la \
40 $(LTLIBINTL)
41libgnunettesting_la_LDFLAGS = \
42 $(GN_LIB_LDFLAGS) \
43 -version-info 3:0:0
diff --git a/src/lib/testing/barrier.c b/src/lib/testing/barrier.c
new file mode 100644
index 000000000..fba350035
--- /dev/null
+++ b/src/lib/testing/barrier.c
@@ -0,0 +1,51 @@
1/*
2 This file is part of GNUnet
3 Copyright (C) 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/**
22 * @file testing/barrier.c
23 * @brief convenience API for writing testcases for GNUnet
24 * Many testcases need to start and stop a peer/service
25 * and this library is supposed to make that easier
26 * for TESTCASES. Normal programs should always
27 * use functions from gnunet_{util,arm}_lib.h. This API is
28 * ONLY for writing testcases (or internal use of the testbed).
29 * @author Christian Grothoff
30 *
31 */
32#include "platform.h"
33#include "gnunet_util_lib.h"
34#include "gnunet_testing_lib.h"
35#include "barrier.h"
36
37
38void
39GNUNET_TESTING_barrier_name_hash_ (
40 const char *barrier_name,
41 struct GNUNET_ShortHashCode *bkey)
42{
43 struct GNUNET_HashCode hc;
44
45 GNUNET_CRYPTO_hash (barrier_name,
46 strlen (barrier_name),
47 &hc);
48 memcpy (bkey,
49 &hc,
50 sizeof (*bkey));
51}
diff --git a/src/lib/testing/barrier.h b/src/lib/testing/barrier.h
new file mode 100644
index 000000000..bf777d9cf
--- /dev/null
+++ b/src/lib/testing/barrier.h
@@ -0,0 +1,77 @@
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/**
22 * @file barrier.h
23 * @brief API to manage barriers.
24 * @author t3sserakt
25 */
26
27#ifndef BARRIER_H
28#define BARRIER_H
29
30/**
31 * An entry for a barrier list
32 */
33struct GNUNET_TESTING_Barrier
34{
35
36 struct GNUNET_ShortHashCode barrier_id;
37
38 /**
39 * Context of a barrier reached command of our local interpreter that is
40 * currently blocked on this barrier, or NULL if
41 * no command is currently blocked on this barrier.
42 */
43 struct GNUNET_TESTING_AsyncContext *cmd_ac;
44
45 /**
46 * Number of total commands expected to be reached by the barrier.
47 */
48 unsigned int expected_reaches;
49
50 /**
51 * Number of times the barrier has been reached.
52 * Only used if @e inherited is false.
53 */
54 unsigned int reached;
55
56 /**
57 * Did we inherit the barrier from our parent loop?
58 */
59 bool inherited;
60
61 /**
62 * Did we reach @e expected_reaches? Used in particular if
63 * @e inherited is true and we cannot compute locally.
64 */
65 bool satisfied;
66
67};
68
69
70void
71GNUNET_TESTING_barrier_name_hash_ (
72 const char *barrier_name,
73 struct GNUNET_ShortHashCode *bkey);
74
75
76#endif
77/* end of barrier.h */
diff --git a/src/lib/testing/gnunet-cmds-helper.c b/src/lib/testing/gnunet-cmds-helper.c
new file mode 100644
index 000000000..4fccdc68b
--- /dev/null
+++ b/src/lib/testing/gnunet-cmds-helper.c
@@ -0,0 +1,527 @@
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 testbed/gnunet-cmds-helper.c
23 * @brief Helper binary that is started from a remote interpreter loop to start
24 * a local interpreter loop.
25 *
26 * This helper monitors for three termination events. They are: (1)The
27 * stdin of the helper is closed for reading; (2)the helper received
28 * SIGTERM/SIGINT; (3)the local loop crashed. In case of events 1 and 2
29 * the helper kills the interpreter loop. When the interpreter loop
30 * crashed (event 3), the helper should send a SIGTERM to its own process
31 * group; this behaviour will help terminate any child processes the loop
32 * has started and prevents them from leaking and running forever.
33 *
34 * @author t3sserakt
35 * @author Sree Harsha Totakura <sreeharsha@totakura.in>
36 */
37#include "platform.h"
38#include "gnunet_util_lib.h"
39#include "gnunet_testing_lib.h"
40#include "testing_api_loop.h"
41#include "testing_cmds.h"
42#include "netjail.h"
43
44/**
45 * Generic logging shortcut
46 */
47#define LOG(kind, ...) GNUNET_log (kind, __VA_ARGS__)
48
49/**
50 * Debug logging shorthand
51 */
52#define LOG_DEBUG(...) LOG (GNUNET_ERROR_TYPE_DEBUG, __VA_ARGS__)
53
54/**
55 * Context for a single write on a chunk of memory
56 */
57struct WriteContext
58{
59
60 struct WriteContext *next;
61
62 struct WriteContext *prev;
63
64 /**
65 * The data to write
66 */
67 void *data;
68
69 /**
70 * The length of the data
71 */
72 size_t length;
73
74 /**
75 * The current position from where the write operation should begin
76 */
77 size_t pos;
78};
79
80
81static struct WriteContext *wc_head;
82
83static struct WriteContext *wc_tail;
84
85static struct GNUNET_TESTING_Interpreter *is;
86
87static const char *my_node_id;
88
89/**
90 * Plugin to dynamically load a test case.
91 */
92static struct GNUNET_TESTING_PluginFunctions *plugin;
93
94/**
95 * Name of our plugin.
96 */
97static char *plugin_name;
98
99/**
100 * Our message stream tokenizer
101 */
102static struct GNUNET_MessageStreamTokenizer *tokenizer;
103
104/**
105 * Disk handle from stdin
106 */
107static struct GNUNET_DISK_FileHandle *stdin_fd;
108
109/**
110 * Disk handle for stdout
111 */
112static struct GNUNET_DISK_FileHandle *stdout_fd;
113
114/**
115 * Task identifier for the read task
116 */
117static struct GNUNET_SCHEDULER_Task *read_task_id;
118
119/**
120 * Task identifier for the write task
121 */
122static struct GNUNET_SCHEDULER_Task *write_task_id;
123
124/**
125 * Result to return in case we fail
126 */
127static int global_ret;
128
129/**
130 * Set to true once we are finished and should exit
131 * after sending our final message to the parent.
132 */
133static bool finished;
134
135
136/**
137 * Task to shut down cleanly
138 *
139 * @param cls NULL
140 */
141static void
142do_shutdown (void *cls)
143{
144 struct WriteContext *wc;
145
146 if (NULL != read_task_id)
147 {
148 GNUNET_SCHEDULER_cancel (read_task_id);
149 read_task_id = NULL;
150 }
151 if (NULL != write_task_id)
152 {
153 GNUNET_SCHEDULER_cancel (write_task_id);
154 write_task_id = NULL;
155 }
156 while (NULL != (wc = wc_head))
157 {
158 GNUNET_CONTAINER_DLL_remove (wc_head,
159 wc_tail,
160 wc);
161 GNUNET_free (wc->data);
162 GNUNET_free (wc);
163 }
164 if (NULL != tokenizer)
165 {
166 GNUNET_MST_destroy (tokenizer);
167 tokenizer = NULL;
168 }
169 if (NULL != plugin)
170 {
171 GNUNET_PLUGIN_unload (plugin_name,
172 plugin);
173 GNUNET_free (plugin_name);
174 }
175}
176
177
178/**
179 * Task to write to the standard out
180 *
181 * @param cls the WriteContext
182 */
183static void
184write_task (void *cls)
185{
186 struct WriteContext *wc = wc_head;
187 ssize_t bytes_wrote;
188
189 write_task_id = NULL;
190 if (NULL == wc)
191 {
192 if (finished)
193 GNUNET_SCHEDULER_shutdown ();
194 return;
195 }
196 bytes_wrote
197 = GNUNET_DISK_file_write (stdout_fd,
198 wc->data + wc->pos,
199 wc->length - wc->pos);
200 if (GNUNET_SYSERR == bytes_wrote)
201 {
202 GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING,
203 "write");
204 GNUNET_free (wc->data);
205 GNUNET_free (wc);
206 global_ret = EXIT_FAILURE;
207 GNUNET_SCHEDULER_shutdown ();
208 return;
209 }
210 wc->pos += bytes_wrote;
211 if (wc->pos == wc->length)
212 {
213 GNUNET_CONTAINER_DLL_remove (wc_head,
214 wc_tail,
215 wc);
216 GNUNET_free (wc->data);
217 GNUNET_free (wc);
218 }
219 write_task_id
220 = GNUNET_SCHEDULER_add_write_file (GNUNET_TIME_UNIT_FOREVER_REL,
221 stdout_fd,
222 &write_task,
223 NULL);
224}
225
226
227/**
228 * Callback to write a message to the parent process.
229 *
230 */
231static void
232write_message (const struct GNUNET_MessageHeader *message)
233{
234 struct WriteContext *wc;
235 size_t msg_length = ntohl (message->size);
236
237 wc = GNUNET_new (struct WriteContext);
238 wc->length = msg_length;
239 wc->data = GNUNET_memdup (message,
240 msg_length);
241 GNUNET_CONTAINER_DLL_insert_tail (wc_head,
242 wc_tail,
243 wc);
244 if (NULL == write_task_id)
245 {
246 GNUNET_assert (wc_head == wc);
247 write_task_id
248 = GNUNET_SCHEDULER_add_write_file (
249 GNUNET_TIME_UNIT_FOREVER_REL,
250 stdout_fd,
251 &write_task,
252 NULL);
253 }
254}
255
256
257static void
258finished_cb (enum GNUNET_GenericReturnValue rv)
259{
260 struct GNUNET_TESTING_CommandLocalFinished reply = {
261 .header.type = htons (GNUNET_MESSAGE_TYPE_CMDS_HELPER_LOCAL_FINISHED),
262 .header.size = htons (sizeof (reply)),
263 .rv = htonl ((uint32_t) rv)
264 };
265
266 finished = true;
267 write_message (&reply.header);
268}
269
270
271static enum GNUNET_GenericReturnValue
272check_helper_init (
273 void *cls,
274 const struct GNUNET_TESTING_CommandHelperInit *msg)
275{
276 uint16_t msize = htons (msg->header.size);
277 uint32_t barrier_count = htonl (msg->barrier_count);
278 size_t bs = barrier_count * sizeof (struct GNUNET_ShortHashCode);
279 size_t left = msize - bs - sizeof (*msg);
280 const struct GNUNET_ShortHashCode *bd
281 = (const struct GNUNET_ShortHashCode *) &msg[1];
282 const char *topo = (const char *) &bd[barrier_count];
283
284 if (msize < bs + sizeof (*msg))
285 {
286 GNUNET_break_op (0);
287 return GNUNET_SYSERR;
288 }
289 if ('\0' != topo[left - 1])
290 {
291 GNUNET_break_op (0);
292 return GNUNET_SYSERR;
293 }
294 return GNUNET_OK;
295}
296
297
298static void
299handle_helper_init (
300 void *cls,
301 const struct GNUNET_TESTING_CommandHelperInit *msg)
302{
303 uint16_t msize = htons (msg->header.size);
304 uint32_t barrier_count = htonl (msg->barrier_count);
305 size_t bs = barrier_count * sizeof (struct GNUNET_ShortHashCode);
306 size_t left = msize - bs - sizeof (*msg);
307 const struct GNUNET_ShortHashCode *bd
308 = (const struct GNUNET_ShortHashCode *) &msg[1];
309 const char *topo = (const char *) &bd[barrier_count];
310 struct GNUNET_TESTING_NetjailTopology *njt;
311
312 GNUNET_assert ('\0' == topo[left - 1]);
313 njt = GNUNET_TESTING_get_topo_from_string (topo);
314 if (NULL == njt)
315 {
316 GNUNET_break_op (0);
317 global_ret = EXIT_FAILURE;
318 GNUNET_SCHEDULER_shutdown ();
319 return;
320 }
321 plugin_name = GNUNET_TESTING_get_plugin_from_topo (njt,
322 my_node_id);
323 GNUNET_TESTING_free_toplogy (njt);
324 plugin = GNUNET_PLUGIN_load (plugin_name,
325 (void *) my_node_id);
326 if (NULL == plugin)
327 {
328 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
329 "Plugin `%s' not found!\n",
330 plugin_name);
331 global_ret = EXIT_FAILURE;
332 GNUNET_SCHEDULER_shutdown ();
333 return;
334 }
335 is = plugin->start_testcase (plugin->cls,
336 topo,
337 barrier_count,
338 bd,
339 &write_message,
340 &finished_cb);
341}
342
343
344static void
345handle_helper_barrier_crossable (
346 void *cls,
347 const struct GNUNET_TESTING_CommandBarrierSatisfied *cbs)
348{
349 struct GNUNET_TESTING_Barrier *barrier;
350
351 if (NULL == is)
352 {
353 /* Barrier satisfied *before* helper_init?! */
354 GNUNET_break_op (0);
355 global_ret = EXIT_FAILURE;
356 GNUNET_SCHEDULER_shutdown ();
357 return;
358 }
359 barrier = GNUNET_TESTING_get_barrier2_ (is,
360 &cbs->barrier_key);
361 if (barrier->satisfied)
362 {
363 /* Barrier satisfied *twice* is strange... */
364 GNUNET_break_op (0);
365 global_ret = EXIT_FAILURE;
366 GNUNET_SCHEDULER_shutdown ();
367 return;
368 }
369 barrier->satisfied = true;
370 GNUNET_TESTING_loop_notify_children_ (is,
371 &cbs->header);
372 if (NULL != barrier->cmd_ac)
373 {
374 GNUNET_TESTING_async_finish (barrier->cmd_ac);
375 barrier->cmd_ac = NULL;
376 }
377}
378
379
380/**
381 * Functions with this signature are called whenever a
382 * complete message is received by the tokenizer.
383 *
384 * Do not call #GNUNET_mst_destroy() in this callback
385 *
386 * @param cls identification of the client
387 * @param message the actual message
388 * @return #GNUNET_OK on success,
389 * #GNUNET_NO to stop further processing (no error)
390 * #GNUNET_SYSERR to stop further processing with error
391 */
392static enum GNUNET_GenericReturnValue
393tokenizer_cb (void *cls,
394 const struct GNUNET_MessageHeader *message)
395{
396 struct GNUNET_MQ_MessageHandler handlers[] = {
397 GNUNET_MQ_hd_var_size (
398 helper_init,
399 GNUNET_MESSAGE_TYPE_CMDS_HELPER_INIT,
400 struct GNUNET_TESTING_CommandHelperInit,
401 NULL),
402 GNUNET_MQ_hd_fixed_size (
403 helper_barrier_crossable,
404 GNUNET_MESSAGE_TYPE_CMDS_HELPER_BARRIER_CROSSABLE,
405 struct GNUNET_TESTING_CommandBarrierSatisfied,
406 NULL),
407 GNUNET_MQ_handler_end ()
408 };
409
410 return GNUNET_MQ_handle_message (handlers,
411 message);
412}
413
414
415/**
416 * Task to read from stdin
417 *
418 * @param cls NULL
419 */
420static void
421read_task (void *cls)
422{
423 char buf[GNUNET_MAX_MESSAGE_SIZE];
424 ssize_t sread;
425
426 read_task_id = NULL;
427 sread = GNUNET_DISK_file_read (stdin_fd,
428 buf,
429 sizeof(buf));
430 if (GNUNET_SYSERR == sread)
431 {
432 GNUNET_break (0);
433 global_ret = EXIT_FAILURE;
434 GNUNET_SCHEDULER_shutdown ();
435 return;
436 }
437 if (0 == sread)
438 {
439 LOG_DEBUG ("STDIN eof\n");
440 GNUNET_SCHEDULER_shutdown ();
441 return;
442 }
443 if (GNUNET_OK !=
444 GNUNET_MST_from_buffer (tokenizer,
445 buf,
446 sread,
447 GNUNET_NO,
448 GNUNET_NO))
449 {
450 GNUNET_break (0);
451 global_ret = EXIT_FAILURE;
452 GNUNET_SCHEDULER_shutdown ();
453 return;
454 }
455 read_task_id
456 = GNUNET_SCHEDULER_add_read_file (GNUNET_TIME_UNIT_FOREVER_REL,
457 stdin_fd,
458 &read_task,
459 NULL);
460}
461
462
463/**
464 * Main function that will be run.
465 *
466 * @param cls closure
467 * @param args remaining command-line arguments
468 * @param cfgfile name of the configuration file used (for saving, can be NULL!)
469 * @param cfg configuration
470 */
471static void
472run (void *cls,
473 char *const *args,
474 const char *cfgfile,
475 const struct GNUNET_CONFIGURATION_Handle *cfg)
476{
477 if (NULL == args[0])
478 {
479 /* must be called with our node ID as 1st argument */
480 GNUNET_break_op (0);
481 global_ret = EXIT_INVALIDARGUMENT;
482 return;
483 }
484 my_node_id = args[0];
485 tokenizer = GNUNET_MST_create (&tokenizer_cb,
486 NULL);
487 stdin_fd = GNUNET_DISK_get_handle_from_native (stdin);
488 stdout_fd = GNUNET_DISK_get_handle_from_native (stdout);
489 read_task_id = GNUNET_SCHEDULER_add_read_file (GNUNET_TIME_UNIT_FOREVER_REL,
490 stdin_fd,
491 &read_task,
492 NULL);
493 GNUNET_SCHEDULER_add_shutdown (&do_shutdown,
494 NULL);
495}
496
497
498/**
499 * Main function
500 *
501 * @param argc the number of command line arguments
502 * @param argv command line arg array
503 * @return return code
504 */
505int
506main (int argc,
507 char **argv)
508{
509 struct GNUNET_GETOPT_CommandLineOption options[] = {
510 GNUNET_GETOPT_OPTION_END
511 };
512 enum GNUNET_GenericReturnValue ret;
513
514 ret = GNUNET_PROGRAM_run (argc,
515 argv,
516 "gnunet-cmds-helper",
517 "Helper for starting a local interpreter loop",
518 options,
519 &run,
520 NULL);
521 if (GNUNET_OK != ret)
522 return 1;
523 return global_ret;
524}
525
526
527/* end of gnunet-cmds-helper.c */
diff --git a/src/lib/testing/meson.build b/src/lib/testing/meson.build
new file mode 100644
index 000000000..ea8956c1b
--- /dev/null
+++ b/src/lib/testing/meson.build
@@ -0,0 +1,62 @@
1libgnunettesting_src = [
2 'testing_api_cmd_exec_bash_script.c',
3 'testing_api_cmd_barrier.c',
4 'testing_api_cmd_barrier_reached.c',
5 'testing_api_cmd_finish.c',
6 'testing_api_cmd_local_test_prepared.c',
7 'testing_api_cmd_send_peer_ready.c',
8 'testing_api_cmd_block_until_external_trigger.c',
9 'testing_api_cmd_netjail_start.c',
10 'testing_api_cmd_netjail_start_cmds_helper.c',
11 'testing_api_cmd_netjail_stop_cmds_helper.c',
12 'testing_api_cmd_netjail_stop.c',
13 'testing.c',
14 'testing_api_cmd_system_create.c',
15 'testing_api_cmd_system_destroy.c',
16 'testing_api_cmd_batch.c',
17 'testing_api_loop.c',
18 'testing_api_traits.c'
19 ]
20
21gnunettesting_src = ['gnunet-testing.c']
22gnunetservicetesting_src = ['gnunet-service-testing.c']
23
24configure_file(input : 'testing.conf',
25 output : 'testing.conf',
26 configuration : cdata,
27 install: true,
28 install_dir: pkgcfgdir)
29
30
31libgnunettesting = library('gnunettesting',
32 libgnunettesting_src,
33 soversion: '1',
34 version: '1.1.0',
35 dependencies: [libgnunetutil_dep,
36 m_dep,
37 libgnunetarm_dep],
38 include_directories: [incdir, configuration_inc],
39 install: true,
40 install_dir: get_option('libdir'))
41libgnunettesting_dep = declare_dependency(link_with : libgnunettesting)
42pkg.generate(libgnunettesting, url: 'https://www.gnunet.org',
43 description : 'Provides API for gnunet testing')
44
45executable ('gnunet-testing',
46 gnunettesting_src,
47 dependencies: [libgnunettesting_dep,
48 libgnunetutil_dep,
49 ],
50 include_directories: [incdir, configuration_inc],
51 install: true,
52 install_dir: get_option('bindir'))
53
54executable ('gnunet-cmds-helper',
55 ['gnunet-cmds-helper.c'],
56 dependencies: [libgnunettesting_dep,
57 libgnunetutil_dep,
58 ],
59 include_directories: [incdir, configuration_inc],
60 install: true,
61 install_dir: get_option('libdir')/'gnunet'/'libexec')
62
diff --git a/src/lib/testing/netjail.c b/src/lib/testing/netjail.c
new file mode 100644
index 000000000..f3b670311
--- /dev/null
+++ b/src/lib/testing/netjail.c
@@ -0,0 +1,1085 @@
1/*
2 This file is part of GNUnet
3 Copyright (C) 2008, 2009, 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 testing/testing.c
23 * @brief convenience API for writing testcases for GNUnet
24 * Many testcases need to start and stop a peer/service
25 * and this library is supposed to make that easier
26 * for TESTCASES. Normal programs should always
27 * use functions from gnunet_{util,arm}_lib.h. This API is
28 * ONLY for writing testcases (or internal use of the testbed).
29 * @author Christian Grothoff
30 *
31 */
32#include "platform.h"
33#include "gnunet_util_lib.h"
34#include "gnunet_testing_lib.h"
35#include "netjail.h"
36#include "testing_cmds.h"
37
38#define LOG(kind, ...) GNUNET_log_from (kind, "testing-api", __VA_ARGS__)
39
40#define CONNECT_ADDRESS_TEMPLATE "%s-192.168.15.%u"
41
42#define ROUTER_CONNECT_ADDRESS_TEMPLATE "%s-92.68.150.%u"
43
44#define KNOWN_CONNECT_ADDRESS_TEMPLATE "%s-92.68.151.%u"
45
46#define PREFIX_TCP "tcp"
47
48#define PREFIX_UDP "udp"
49
50#define PREFIX_TCP_NATTED "tcp_natted"
51
52#define PREFIX_UDP_NATTED "udp_natted"
53
54
55/**
56 * Every line in the topology configuration starts with a string indicating which
57 * kind of information will be configured with this line. Configuration values following
58 * this string are seperated by special sequences of characters. An integer value seperated
59 * by ':' is returned by this function.
60 *
61 * @param line The line of configuration.
62 * @return An integer value.
63 */
64static unsigned int
65get_first_value (const char *line)
66{
67 char *copy;
68 size_t slen;
69 char *token;
70 unsigned int ret;
71 char *rest = NULL;
72
73 slen = strlen (line) + 1;
74 copy = GNUNET_malloc (slen);
75 memcpy (copy, line, slen);
76 token = strtok_r (copy, ":", &rest);
77 token = strtok_r (NULL, ":", &rest);
78 GNUNET_assert (1 == sscanf (token, "%u", &ret));
79 GNUNET_free (copy);
80 return ret;
81}
82
83
84/**
85 * Every line in the topology configuration starts with a string indicating which
86 * kind of information will be configured with this line. This string is returned by this function.
87 *
88 * @param line The line of configuration.
89 * @return The leading string of this configuration line.
90 */
91static char *
92get_key (const char *line)
93{
94 char *copy;
95 size_t slen;
96 size_t tlen;
97 char *token;
98 char *ret;
99 char *rest = NULL;
100
101 slen = strlen (line) + 1;
102 copy = GNUNET_malloc (slen);
103 memcpy (copy, line, slen);
104 token = strtok_r (copy, ":", &rest);
105 tlen = strlen (token) + 1;
106 ret = GNUNET_malloc (tlen);
107 memcpy (ret, token, tlen);
108 GNUNET_free (copy);
109 return ret;
110}
111
112
113/**
114 * Every line in the topology configuration starts with a string indicating which
115 * kind of information will be configured with this line. Configuration values following
116 * this string are seperated by special sequences of characters. A string value seperated
117 * by ':' is returned by this function.
118 *
119 * @param line The line of configuration.
120 * @return A string value.
121 */
122static char *
123get_first_string_value (const char *line)
124{
125 char *copy;
126 size_t slen, slen_token;
127 char *token;
128 char *ret;
129 char *rest = NULL;
130
131 slen = strlen (line) + 1;
132 copy = GNUNET_malloc (slen);
133 memcpy (copy, line, slen);
134 token = strtok_r (copy, ":", &rest);
135 token = strtok_r (NULL, ":", &rest);
136 LOG (GNUNET_ERROR_TYPE_DEBUG,
137 "first token %s\n",
138 token);
139 slen_token = strlen (token);
140 ret = GNUNET_malloc (slen_token + 1);
141 memcpy (ret, token, slen_token + 1);
142 GNUNET_free (copy);
143 return ret;
144}
145
146
147/**
148 * Every line in the topology configuration starts with a string indicating which
149 * kind of information will be configured with this line. Configuration values following
150 * this string are seperated by special sequences of characters. A second integer value
151 * seperated by ':' from a first value is returned by this function.
152 *
153 * @param line The line of configuration.
154 * @return An integer value.
155 */
156static unsigned int
157get_second_value (const char *line)
158{
159 char *copy;
160 char *token;
161 unsigned int ret;
162 char *rest = NULL;
163
164 copy = GNUNET_strdup (line);
165 token = strtok_r (copy, ":", &rest);
166 token = strtok_r (NULL, ":", &rest);
167 token = strtok_r (NULL, ":", &rest);
168 LOG (GNUNET_ERROR_TYPE_ERROR,
169 "Format error in configuration line: %s\n",
170 line);
171 GNUNET_assert (1 == sscanf (token, "%u", &ret));
172 GNUNET_free (copy);
173 return ret;
174}
175
176
177/**
178 * Every line in the topology configuration starts with a string indicating which
179 * kind of information will be configured with this line. Configuration values following
180 * this string are seperated by special sequences of characters. A value might be
181 * a key value pair.
182 * This function returns the value for a specific key in a configuration line.
183 *
184 * @param key The key of the key value pair.
185 * @return The value of the key value pair.
186 */
187static char *
188get_value (const char *key, const char *line)
189{
190 char copy[strlen (line) + 1];
191 size_t slen;
192 char *token;
193 char *token2;
194 char *temp;
195 char *rest = NULL;
196
197 slen = strlen (line) + 1;
198 memcpy (copy, line, slen);
199 temp = strstr (copy, key);
200 if (NULL == temp)
201 return NULL;
202 token = strtok_r (temp, ":", &rest);
203 if (NULL == token)
204 return NULL;
205 token = strtok_r (NULL, ":", &rest);
206 if (NULL == token)
207 return NULL;
208 token2 = strtok_r (token, "}", &rest);
209 if (NULL == token2)
210 return NULL;
211 return GNUNET_strdup (token2);
212}
213
214
215/**
216 * Every line in the topology configuration starts with a string indicating which
217 * kind of information will be configured with this line. Configuration values following
218 * this string are seperated by special sequences of characters. A value might be
219 * a key value pair. A special key is the 'connect' key which can appear more than once.
220 * The value is the information about a connection via some protocol to some other node.
221 * This function returns the struct GNUNET_TESTING_NodeConnection which holds the information
222 * of the connect value.
223 *
224 * @param value The value of the connect key value pair.
225 * @return The struct GNUNET_TESTING_NodeConnection.
226 */
227static struct GNUNET_TESTING_NodeConnection *
228get_connect_value (const char *line,
229 struct GNUNET_TESTING_NetjailNode *node)
230{
231 struct GNUNET_TESTING_NodeConnection *node_connection;
232 char *copy;
233 char *token;
234 char *token2;
235 unsigned int node_n;
236 unsigned int namespace_n;
237 char *rest = NULL;
238 char *rest2 = NULL;
239 struct GNUNET_TESTING_AddressPrefix *prefix;
240 unsigned int sscanf_ret;
241
242 node_connection = GNUNET_new (struct GNUNET_TESTING_NodeConnection);
243 node_connection->node = node;
244
245 copy = GNUNET_strdup (line);
246 token = strtok_r (copy, ":", &rest);
247 if (0 == strcmp ("{K", token))
248 {
249 node_connection->node_type = GNUNET_TESTING_GLOBAL_NODE;
250 token = strtok_r (NULL, ":", &rest);
251 GNUNET_assert (1 == sscanf (token, "%u", &node_n));
252 LOG (GNUNET_ERROR_TYPE_DEBUG,
253 "node_n %u\n",
254 node_n);
255 node_connection->node_n = node_n;
256 node_connection->namespace_n = 0;
257 }
258 else if (0 == strcmp ("{P", token))
259 {
260 node_connection->node_type = GNUNET_TESTING_SUBNET_NODE;
261 token = strtok_r (NULL, ":", &rest);
262 errno = 0;
263 sscanf_ret = sscanf (token, "%u", &namespace_n);
264 if (errno != 0)
265 {
266 GNUNET_log_strerror (GNUNET_ERROR_TYPE_ERROR, "sscanf");
267 }
268 GNUNET_assert (0 < sscanf_ret);
269 node_connection->namespace_n = namespace_n;
270 token = strtok_r (NULL, ":", &rest);
271 errno = 0;
272 sscanf_ret = sscanf (token, "%u", &node_n);
273 if (errno != 0)
274 {
275 GNUNET_log_strerror (GNUNET_ERROR_TYPE_ERROR, "sscanf");
276 }
277 GNUNET_assert (0 < sscanf_ret);
278 node_connection->node_n = node_n;
279 LOG (GNUNET_ERROR_TYPE_DEBUG,
280 "node_n %u namespace_n %u node->node_n %u node->namespace_n %u\n",
281 node_n,
282 namespace_n,
283 node->node_n,
284 node->namespace_n);
285 }
286 else
287 {
288 GNUNET_break (0);
289 GNUNET_free (node_connection);
290 GNUNET_free (copy);
291 return NULL;
292 }
293
294 while (NULL != (token = strtok_r (NULL, ":", &rest)))
295 {
296 prefix = GNUNET_new (struct GNUNET_TESTING_AddressPrefix);
297 token2 = strtok_r (token, "}", &rest2);
298 if (NULL != token2)
299 prefix->address_prefix = GNUNET_strdup (token2);
300 else
301 prefix->address_prefix = GNUNET_strdup (token);
302
303 LOG (GNUNET_ERROR_TYPE_DEBUG,
304 "address_prefix %s\n",
305 prefix->address_prefix);
306
307 GNUNET_CONTAINER_DLL_insert (node_connection->address_prefixes_head,
308 node_connection->address_prefixes_tail,
309 prefix);
310 LOG (GNUNET_ERROR_TYPE_DEBUG,
311 "address_prefix %s\n",
312 prefix->address_prefix);
313 }
314
315 GNUNET_free (copy);
316 return node_connection;
317}
318
319
320/**
321 * Every line in the topology configuration starts with a string indicating which
322 * kind of information will be configured with this line. Configuration values following
323 * this string are seperated by special sequences of characters. A value might be
324 * a key value pair. A special key is the 'connect' key.
325 * The value is the information about a connections via some protocol to other nodes.
326 * Each connection itself is a key value pair separated by the character '|' and
327 * surrounded by the characters '{' and '}'.
328 * The struct GNUNET_TESTING_NodeConnection holds the information of each connection value.
329 * This function extracts the values of each connection into a DLL of
330 * struct GNUNET_TESTING_NodeConnection which will be added to a node.
331 *
332 * @param line The line of configuration.
333 * @param node The struct GNUNET_TESTING_NetjailNode to which the DLL of
334 * struct GNUNET_TESTING_NodeConnection will be added.
335 */
336static void
337node_connections (const char *line, struct GNUNET_TESTING_NetjailNode *node)
338{
339 char *value, *value2;
340 char *temp;
341 char *copy;
342 char *rest = NULL;
343 char *rest2 = NULL;
344 struct GNUNET_TESTING_NodeConnection *node_connection;
345
346
347 temp = strstr (line, "connect");
348 if (NULL != temp)
349 {
350 copy = GNUNET_strdup (temp);
351 strtok_r (copy, ":", &rest);
352 value = strtok_r (rest, "|", &rest2);
353
354 while (NULL != value)
355 {
356 LOG (GNUNET_ERROR_TYPE_DEBUG,
357 "node_connections value %s\n",
358 value);
359 node_connection = get_connect_value (value, node);
360 if (NULL == node_connection)
361 {
362 LOG (GNUNET_ERROR_TYPE_WARNING,
363 "connect key was not expected in this configuration line: %s\n",
364 line);
365 break;
366 }
367 GNUNET_CONTAINER_DLL_insert (node->node_connections_head,
368 node->node_connections_tail,
369 node_connection);
370 value2 = strstr (value, "}}");
371 if (NULL != value2)
372 break;
373 value = strtok_r (NULL, "|", &rest2);
374
375 }
376 GNUNET_free (copy);
377 }
378}
379
380
381/**
382 * A helper function to log information about individual nodes.
383 *
384 * @param cls This is not used actually.
385 * @param id The key of this value in the map.
386 * @param value A struct GNUNET_TESTING_NetjailNode which holds information about a node.
387 * return GNUNET_YES to continue with iterating, GNUNET_NO otherwise.
388 */
389static int
390log_nodes (void *cls, const struct GNUNET_ShortHashCode *id, void *value)
391{
392 struct GNUNET_TESTING_NetjailNode *node = value;
393 struct GNUNET_TESTING_NodeConnection *pos_connection;
394 struct GNUNET_TESTING_AddressPrefix *pos_prefix;
395
396 LOG (GNUNET_ERROR_TYPE_DEBUG,
397 "plugin: %s space: %u node: %u global: %u\n",
398 node->plugin,
399 node->namespace_n,
400 node->node_n,
401 node->is_global);
402
403 for (pos_connection = node->node_connections_head; NULL != pos_connection;
404 pos_connection = pos_connection->next)
405 {
406
407 LOG (GNUNET_ERROR_TYPE_DEBUG,
408 "namespace_n: %u node_n: %u node_type: %u\n",
409 pos_connection->namespace_n,
410 pos_connection->node_n,
411 pos_connection->node_type);
412
413 for (pos_prefix = pos_connection->address_prefixes_head; NULL != pos_prefix;
414 pos_prefix =
415 pos_prefix->next)
416 {
417 LOG (GNUNET_ERROR_TYPE_DEBUG,
418 "prefix: %s\n",
419 pos_prefix->address_prefix);
420 }
421 }
422 return GNUNET_YES;
423}
424
425
426/**
427 * Helper function to log information about namespaces.
428 *
429 * @param cls This is not used actually.
430 * @param id The key of this value in the map.
431 * @param value A struct GNUNET_TESTING_NetjailNamespace which holds information about a subnet.
432 * return GNUNET_YES to continue with iterating, GNUNET_NO otherwise.
433 */
434static int
435log_namespaces (void *cls, const struct GNUNET_ShortHashCode *id, void *value)
436{
437 struct GNUNET_TESTING_NetjailNamespace *namespace = value;
438
439 GNUNET_CONTAINER_multishortmap_iterate (namespace->nodes, &log_nodes, NULL);
440 return GNUNET_YES;
441}
442
443
444/**
445 * Helper function to log the configuration in case of a problem with configuration.
446 *
447 * @param topology The struct GNUNET_TESTING_NetjailTopology holding the configuration information.
448 */
449static int
450log_topo (const struct GNUNET_TESTING_NetjailTopology *topology)
451{
452 LOG (GNUNET_ERROR_TYPE_DEBUG,
453 "plugin: %s spaces: %u nodes: %u known: %u\n",
454 topology->plugin,
455 topology->namespaces_n,
456 topology->nodes_m,
457 topology->nodes_x);
458
459 GNUNET_CONTAINER_multishortmap_iterate (topology->map_namespaces,
460 log_namespaces, NULL);
461 GNUNET_CONTAINER_multishortmap_iterate (topology->map_globals, &log_nodes,
462 NULL);
463 return GNUNET_YES;
464}
465
466
467/**
468 * This function extracts information about a specific node from the topology.
469 *
470 * @param num The global index number of the node.
471 * @param[out] node_ex A struct GNUNET_TESTING_NetjailNode with information about the node.
472 * @param[out] namespace_ex A struct GNUNET_TESTING_NetjailNamespace with information about the namespace
473 the node is in or NULL, if the node is a global node.
474 * @param[out] node_connections_ex A struct GNUNET_TESTING_NodeConnection with information about the connection
475 of this node to other nodes.
476*/
477static void
478get_node_info (unsigned int num,
479 const struct GNUNET_TESTING_NetjailTopology *topology,
480 struct GNUNET_TESTING_NetjailNode **node_ex,
481 struct GNUNET_TESTING_NetjailNamespace **namespace_ex,
482 struct GNUNET_TESTING_NodeConnection **node_connections_ex)
483{
484 struct GNUNET_ShortHashCode hkey;
485 struct GNUNET_HashCode hc;
486 unsigned int namespace_n;
487 unsigned int node_m;
488 struct GNUNET_TESTING_NetjailNode *node;
489 struct GNUNET_TESTING_NetjailNamespace *namespace;
490 struct GNUNET_TESTING_NodeConnection *node_connections = NULL;
491
492 log_topo (topology);
493 LOG (GNUNET_ERROR_TYPE_DEBUG,
494 "num: %u \n",
495 num);
496 if (topology->nodes_x >= num)
497 {
498
499 GNUNET_CRYPTO_hash (&num, sizeof(num), &hc);
500 memcpy (&hkey,
501 &hc,
502 sizeof (hkey));
503 node = GNUNET_CONTAINER_multishortmap_get (topology->map_globals,
504 &hkey);
505 if (NULL != node)
506 {
507 *node_ex = node;
508 *node_connections_ex = node->node_connections_head;
509 }
510 }
511 else
512 {
513 namespace_n = (unsigned int) ceil ((double) (num - topology->nodes_x)
514 / topology->nodes_m);
515 LOG (GNUNET_ERROR_TYPE_DEBUG,
516 "ceil num: %u nodes_x: %u nodes_m: %u namespace_n: %u\n",
517 num,
518 topology->nodes_x,
519 topology->nodes_m,
520 namespace_n);
521 GNUNET_CRYPTO_hash (&namespace_n, sizeof(namespace_n), &hc);
522 memcpy (&hkey,
523 &hc,
524 sizeof (hkey));
525 namespace = GNUNET_CONTAINER_multishortmap_get (topology->map_namespaces,
526 &hkey);
527 if (NULL != namespace)
528 {
529 node_m = num - topology->nodes_x - topology->nodes_m * (namespace_n - 1);
530 GNUNET_CRYPTO_hash (&node_m, sizeof(node_m), &hc);
531 memcpy (&hkey,
532 &hc,
533 sizeof (hkey));
534 node = GNUNET_CONTAINER_multishortmap_get (namespace->nodes,
535 &hkey);
536 if (NULL != node)
537 {
538 LOG (GNUNET_ERROR_TYPE_DEBUG,
539 "node additional_connects: %u %p\n",
540 node->additional_connects,
541 node);
542 node_connections = node->node_connections_head;
543 }
544 *node_ex = node;
545 *namespace_ex = namespace;
546 *node_connections_ex = node_connections;
547 }
548 }
549}
550
551
552/**
553 * Get a node from the topology.
554 *
555 * @param num The specific node we want the connections for.
556 * @param topology The topology we get the connections from.
557 * @return The connections of the node.
558 */
559struct GNUNET_TESTING_NetjailNode *
560GNUNET_TESTING_get_node (unsigned int num,
561 struct GNUNET_TESTING_NetjailTopology *topology)
562{
563 struct GNUNET_TESTING_NetjailNode *node;
564 struct GNUNET_TESTING_NetjailNamespace *namespace;
565 struct GNUNET_TESTING_NodeConnection *node_connections;
566
567 get_node_info (num, topology, &node, &namespace, &node_connections);
568
569 return node;
570
571}
572
573
574/**
575 * Get the connections to other nodes for a specific node.
576 *
577 * @param num The specific node we want the connections for.
578 * @param topology The topology we get the connections from.
579 * @return The connections of the node.
580 */
581struct GNUNET_TESTING_NodeConnection *
582GNUNET_TESTING_get_connections (unsigned int num,
583 const struct
584 GNUNET_TESTING_NetjailTopology *topology)
585{
586 struct GNUNET_TESTING_NetjailNode *node;
587 struct GNUNET_TESTING_NetjailNamespace *namespace;
588 struct GNUNET_TESTING_NodeConnection *node_connections;
589
590 LOG (GNUNET_ERROR_TYPE_DEBUG,
591 "get_connections\n");
592
593 get_node_info (num, topology, &node, &namespace, &node_connections);
594
595 return node_connections;
596}
597
598
599int
600free_nodes_cb (void *cls,
601 const struct GNUNET_ShortHashCode *key,
602 void *value)
603{
604 (void) cls;
605 struct GNUNET_TESTING_NetjailNode *node = value;
606 struct GNUNET_TESTING_NodeConnection *pos_connection;
607 struct GNUNET_TESTING_AddressPrefix *pos_prefix;
608
609 while (NULL != (pos_connection = node->node_connections_head))
610 {
611 while (NULL != (pos_prefix = pos_connection->address_prefixes_head))
612 {
613 GNUNET_CONTAINER_DLL_remove (pos_connection->address_prefixes_head,
614 pos_connection->address_prefixes_tail,
615 pos_prefix);
616 GNUNET_free (pos_prefix->address_prefix);
617 GNUNET_free (pos_prefix);
618 }
619 GNUNET_CONTAINER_DLL_remove (node->node_connections_head,
620 node->node_connections_tail,
621 pos_connection);
622 GNUNET_free (pos_connection);
623 }
624
625 GNUNET_free (node->plugin);
626 GNUNET_free (node);
627 return GNUNET_OK;
628}
629
630
631int
632free_namespaces_cb (void *cls,
633 const struct GNUNET_ShortHashCode *key,
634 void *value)
635{
636 (void) cls;
637 struct GNUNET_TESTING_NetjailNamespace *namespace = value;
638
639 GNUNET_free (namespace->router);
640 GNUNET_CONTAINER_multishortmap_iterate (namespace->nodes, free_nodes_cb,
641 namespace->nodes);
642 return GNUNET_OK;
643
644}
645
646
647/**
648 * Deallocate memory of the struct GNUNET_TESTING_NetjailTopology.
649 *
650 * @param topology The GNUNET_TESTING_NetjailTopology to be deallocated.
651 */
652void
653GNUNET_TESTING_free_topology (struct GNUNET_TESTING_NetjailTopology *topology)
654{
655 GNUNET_CONTAINER_multishortmap_iterate (topology->map_namespaces,
656 free_namespaces_cb, NULL);
657 GNUNET_CONTAINER_multishortmap_destroy (topology->map_namespaces);
658 GNUNET_CONTAINER_multishortmap_iterate (topology->map_globals, free_nodes_cb,
659 NULL);
660 GNUNET_CONTAINER_multishortmap_destroy (topology->map_globals);
661 GNUNET_free (topology->plugin);
662 GNUNET_free (topology);
663}
664
665
666unsigned int
667GNUNET_TESTING_calculate_num (
668 struct GNUNET_TESTING_NodeConnection *node_connection,
669 struct GNUNET_TESTING_NetjailTopology *topology)
670{
671 unsigned int n, m, num;
672
673 n = node_connection->namespace_n;
674 m = node_connection->node_n;
675
676 if (0 == n)
677 num = m;
678 else
679 num = (n - 1) * topology->nodes_m + m + topology->nodes_x;
680
681 return num;
682}
683
684
685/**
686 * Get the address for a specific communicator from a connection.
687 *
688 * @param connection The connection we like to have the address from.
689 * @param prefix The communicator protocol prefix.
690 * @return The address of the communicator.
691 */
692char *
693GNUNET_TESTING_get_address (struct GNUNET_TESTING_NodeConnection *connection,
694 const char *prefix)
695{
696 struct GNUNET_TESTING_NetjailNode *node;
697 char *addr;
698 char *template;
699 unsigned int node_n;
700
701 LOG (GNUNET_ERROR_TYPE_DEBUG,
702 "get address prefix: %s node_n: %u\n",
703 prefix,
704 connection->node_n);
705
706 node = connection->node;
707 if (connection->namespace_n == node->namespace_n)
708 {
709 template = CONNECT_ADDRESS_TEMPLATE;
710 node_n = connection->node_n;
711 }
712 else if (0 == connection->namespace_n)
713 {
714 template = KNOWN_CONNECT_ADDRESS_TEMPLATE;
715 node_n = connection->node_n;
716 }
717 else if (1 == connection->node_n)
718 {
719 template = ROUTER_CONNECT_ADDRESS_TEMPLATE;
720 node_n = connection->namespace_n;
721 }
722 else
723 {
724 return NULL;
725 }
726
727 if (0 == strcmp (PREFIX_TCP, prefix) ||
728 0 == strcmp (PREFIX_UDP, prefix) ||
729 0 == strcmp (PREFIX_UDP_NATTED, prefix) ||
730 0 == strcmp (PREFIX_TCP_NATTED, prefix))
731 {
732 GNUNET_asprintf (&addr,
733 template,
734 prefix,
735 node_n);
736 }
737 else
738 {
739 GNUNET_assert (0);
740 }
741
742 return addr;
743}
744
745
746/**
747 * Get the number of unintentional additional connections the node waits for.
748 *
749 * @param num The specific node we want the additional connects for.
750 * @return The number of additional connects
751 */
752unsigned int
753GNUNET_TESTING_get_additional_connects (unsigned int num,
754 struct GNUNET_TESTING_NetjailTopology *
755 topology)
756{
757 struct GNUNET_TESTING_NetjailNode *node;
758 struct GNUNET_TESTING_NetjailNamespace *namespace;
759 struct GNUNET_TESTING_NodeConnection *node_connections;
760
761 LOG (GNUNET_ERROR_TYPE_DEBUG,
762 "get_additional_connects\n");
763
764 get_node_info (num, topology, &node, &namespace, &node_connections);
765
766 if (NULL == node)
767 {
768 LOG (GNUNET_ERROR_TYPE_WARNING,
769 "No info found for node %d\n", num);
770 return 0;
771 }
772 LOG (GNUNET_ERROR_TYPE_DEBUG,
773 "node additional_connects for node %p\n",
774 node);
775 LOG (GNUNET_ERROR_TYPE_DEBUG,
776 "node additional_connects: %u\n",
777 node->additional_connects);
778
779 return node->additional_connects;
780}
781
782
783static void
784parse_ac (struct GNUNET_TESTING_NetjailNode *p_node, const char *token)
785{
786 char *ac_value;
787 int ret;
788
789 ac_value = get_value ("AC", token);
790 if (NULL != ac_value)
791 {
792 LOG (GNUNET_ERROR_TYPE_DEBUG,
793 "ac value: %s\n",
794 ac_value);
795 errno = 0;
796 ret = sscanf (ac_value, "%u", &p_node->additional_connects);
797 if (errno != 0)
798 {
799 GNUNET_log_strerror (GNUNET_ERROR_TYPE_ERROR, "sscanf");
800 }
801 GNUNET_assert (0 < ret);
802 LOG (GNUNET_ERROR_TYPE_DEBUG,
803 "AC %u\n",
804 p_node->additional_connects);
805 }
806 else
807 {
808 p_node->additional_connects = 0;
809 }
810 GNUNET_free (ac_value);
811}
812
813
814/**
815 * Parse the topology data.
816 *
817 * @param data The topology data.
818 * @return The GNUNET_TESTING_NetjailTopology
819 */
820struct GNUNET_TESTING_NetjailTopology *
821GNUNET_TESTING_get_topo_from_string_ (const char *input)
822{
823 char *token;
824 char *key = NULL;
825 unsigned int out;
826 char *rest = NULL;
827 char *value = NULL;
828 char *value2;
829 char *data;
830 int ret;
831 struct GNUNET_TESTING_NetjailTopology *topo;
832 struct GNUNET_TESTING_NetjailRouter *router;
833 struct GNUNET_TESTING_NetjailNamespace *namespace;
834 struct GNUNET_HashCode hc;
835
836 data = GNUNET_strdup (input);
837 token = strtok_r (data, "\n", &rest);
838 topo = GNUNET_new (struct GNUNET_TESTING_NetjailTopology);
839 topo->map_namespaces =
840 GNUNET_CONTAINER_multishortmap_create (1,GNUNET_NO);
841 topo->map_globals =
842 GNUNET_CONTAINER_multishortmap_create (1,GNUNET_NO);
843
844 while (NULL != token)
845 {
846 if (NULL != key)
847 GNUNET_free (key);
848 key = get_key (token);
849 LOG (GNUNET_ERROR_TYPE_DEBUG,
850 "In the loop with token: %s beginning with %s\n",
851 token,
852 key);
853 if (0 == strcmp (key, "M"))
854 {
855 LOG (GNUNET_ERROR_TYPE_DEBUG,
856 "Get first Value for M.\n");
857 out = get_first_value (token);
858 LOG (GNUNET_ERROR_TYPE_DEBUG,
859 "M: %u\n",
860 out);
861 topo->nodes_m = out;
862 }
863 else if (0 == strcmp (key, "N"))
864 {
865 LOG (GNUNET_ERROR_TYPE_DEBUG,
866 "Get first Value for N.\n");
867 out = get_first_value (token);
868 LOG (GNUNET_ERROR_TYPE_DEBUG,
869 "N: %u\n",
870 out);
871 topo->namespaces_n = out;
872 }
873 else if (0 == strcmp (key, "X"))
874 {
875 LOG (GNUNET_ERROR_TYPE_DEBUG,
876 "Get first Value for X.\n");
877 out = get_first_value (token);
878 LOG (GNUNET_ERROR_TYPE_DEBUG,
879 "X: %u\n",
880 out);
881 topo->nodes_x = out;
882 }
883 else if (0 == strcmp (key, "AC"))
884 {
885 LOG (GNUNET_ERROR_TYPE_DEBUG,
886 "Get first Value for AC.\n");
887 out = get_first_value (token);
888 LOG (GNUNET_ERROR_TYPE_DEBUG,
889 "AC: %u\n",
890 out);
891 topo->additional_connects = out;
892 }
893 else if (0 == strcmp (key, "T"))
894 {
895 LOG (GNUNET_ERROR_TYPE_DEBUG,
896 "Get first string value for T.\n");
897 value = get_first_string_value (token);
898 LOG (GNUNET_ERROR_TYPE_DEBUG,
899 "value: %s\n",
900 value);
901 topo->plugin = value;
902 }
903 else if (0 == strcmp (key, "K"))
904 {
905 struct GNUNET_ShortHashCode hkey_k;
906 struct GNUNET_TESTING_NetjailNode *k_node = GNUNET_new (struct
907 GNUNET_TESTING_NetjailNode);
908
909 LOG (GNUNET_ERROR_TYPE_DEBUG,
910 "Get first Value for K.\n");
911 out = get_first_value (token);
912 LOG (GNUNET_ERROR_TYPE_DEBUG,
913 "K: %u\n",
914 out);
915 k_node->node_n = out;
916 GNUNET_CRYPTO_hash (&out, sizeof(out), &hc);
917 memcpy (&hkey_k,
918 &hc,
919 sizeof (hkey_k));
920 k_node->is_global = GNUNET_YES;
921
922 if (GNUNET_YES == GNUNET_CONTAINER_multishortmap_contains (
923 topo->map_globals,
924 &hkey_k))
925 GNUNET_break (0);
926 else
927 GNUNET_CONTAINER_multishortmap_put (topo->map_globals,
928 &hkey_k,
929 k_node,
930 GNUNET_CONTAINER_MULTIHASHMAPOPTION_MULTIPLE);
931 LOG (GNUNET_ERROR_TYPE_DEBUG,
932 "Get value for key value on K.\n");
933 value = get_value ("plugin", token);
934 LOG (GNUNET_ERROR_TYPE_DEBUG,
935 "value: %s\n",
936 value);
937 k_node->plugin = value;
938 parse_ac (k_node, token);
939 node_connections (token, k_node);
940 GNUNET_free (value);
941 }
942 else if (0 == strcmp (key, "R"))
943 {
944 struct GNUNET_ShortHashCode hkey_r;
945 router = GNUNET_new (struct GNUNET_TESTING_NetjailRouter);
946
947 LOG (GNUNET_ERROR_TYPE_DEBUG,
948 "Get first Value for R.\n");
949 out = get_first_value (token);
950 LOG (GNUNET_ERROR_TYPE_DEBUG,
951 "R: %u\n",
952 out);
953 GNUNET_CRYPTO_hash (&out, sizeof(out), &hc);
954 memcpy (&hkey_r,
955 &hc,
956 sizeof (hkey_r));
957 LOG (GNUNET_ERROR_TYPE_DEBUG,
958 "Get value for key tcp_port on R.\n");
959 value = get_value ("tcp_port", token);
960 LOG (GNUNET_ERROR_TYPE_DEBUG,
961 "tcp_port: %s\n",
962 value);
963 ret = sscanf (value, "%u", &(router->tcp_port));
964 GNUNET_free (value);
965 GNUNET_break (0 != ret && 1 >= router->tcp_port);
966
967 LOG (GNUNET_ERROR_TYPE_DEBUG,
968 "Get value for key udp_port on R.\n");
969 value2 = get_value ("udp_port", token);
970 ret = sscanf (value2, "%u", &(router->udp_port));
971 GNUNET_free (value2);
972 GNUNET_break (0 != ret && 1 >= router->udp_port);
973 LOG (GNUNET_ERROR_TYPE_DEBUG,
974 "udp_port: %s\n",
975 value2);
976 GNUNET_free (value2);
977 if (GNUNET_YES == GNUNET_CONTAINER_multishortmap_contains (
978 topo->map_namespaces,
979 &hkey_r))
980 {
981 namespace = GNUNET_CONTAINER_multishortmap_get (topo->map_namespaces,
982 &hkey_r);
983 }
984 else
985 {
986 namespace = GNUNET_new (struct GNUNET_TESTING_NetjailNamespace);
987 namespace->namespace_n = out;
988 namespace->nodes = GNUNET_CONTAINER_multishortmap_create (1,GNUNET_NO);
989 GNUNET_CONTAINER_multishortmap_put (topo->map_namespaces,
990 &hkey_r,
991 namespace,
992 GNUNET_CONTAINER_MULTIHASHMAPOPTION_MULTIPLE);
993 }
994 namespace->router = router;
995
996 }
997 else if (0 == strcmp (key, "P"))
998 {
999 struct GNUNET_TESTING_NetjailNode *p_node = GNUNET_new (struct
1000 GNUNET_TESTING_NetjailNode);
1001 struct GNUNET_ShortHashCode hkey_p;
1002
1003 LOG (GNUNET_ERROR_TYPE_DEBUG,
1004 "Get first Value for P.\n");
1005 out = get_first_value (token);
1006 LOG (GNUNET_ERROR_TYPE_DEBUG,
1007 "P: %u\n",
1008 out);
1009 GNUNET_CRYPTO_hash (&out, sizeof(out), &hc);
1010 memcpy (&hkey_p,
1011 &hc,
1012 sizeof (hkey_p));
1013
1014 if (GNUNET_YES == GNUNET_CONTAINER_multishortmap_contains (
1015 topo->map_namespaces,
1016 &hkey_p))
1017 {
1018 namespace = GNUNET_CONTAINER_multishortmap_get (topo->map_namespaces,
1019 &hkey_p);
1020 }
1021 else
1022 {
1023 namespace = GNUNET_new (struct GNUNET_TESTING_NetjailNamespace);
1024 namespace->nodes = GNUNET_CONTAINER_multishortmap_create (1,GNUNET_NO);
1025 namespace->namespace_n = out;
1026 GNUNET_CONTAINER_multishortmap_put (topo->map_namespaces,
1027 &hkey_p,
1028 namespace,
1029 GNUNET_CONTAINER_MULTIHASHMAPOPTION_MULTIPLE);
1030 }
1031 LOG (GNUNET_ERROR_TYPE_DEBUG,
1032 "Get second Value for P.\n");
1033 out = get_second_value (token);
1034 LOG (GNUNET_ERROR_TYPE_DEBUG,
1035 "P: %u\n",
1036 out);
1037 GNUNET_CRYPTO_hash (&out, sizeof(out), &hc);
1038 memcpy (&hkey_p,
1039 &hc,
1040 sizeof (hkey_p));
1041 if (GNUNET_YES == GNUNET_CONTAINER_multishortmap_contains (
1042 namespace->nodes,
1043 &hkey_p))
1044 {
1045 GNUNET_break (0);
1046 }
1047 else
1048 {
1049
1050 GNUNET_CONTAINER_multishortmap_put (namespace->nodes,
1051 &hkey_p,
1052 p_node,
1053 GNUNET_CONTAINER_MULTIHASHMAPOPTION_MULTIPLE);
1054 LOG (GNUNET_ERROR_TYPE_DEBUG,
1055 "Get value for key plugin on P.\n");
1056 value = get_value ("plugin", token);
1057 if (NULL != value)
1058 {
1059 LOG (GNUNET_ERROR_TYPE_DEBUG,
1060 "plugin: %s\n",
1061 value);
1062 p_node->plugin = value;
1063 }
1064 p_node->node_n = out;
1065 p_node->namespace_n = namespace->namespace_n;
1066 }
1067 LOG (GNUNET_ERROR_TYPE_DEBUG,
1068 "Get AC Value for P.\n");
1069 parse_ac (p_node, token);
1070 node_connections (token, p_node);
1071 }
1072 token = strtok_r (NULL, "\n", &rest);
1073 if (NULL != token)
1074 LOG (GNUNET_ERROR_TYPE_DEBUG,
1075 "Next token %s\n",
1076 token);
1077 }
1078 if (NULL != key)
1079 GNUNET_free (key);
1080 GNUNET_free (data);
1081 return topo;
1082}
1083
1084
1085/* end of testing.c */
diff --git a/src/lib/testing/netjail.h b/src/lib/testing/netjail.h
new file mode 100644
index 000000000..f8445a89f
--- /dev/null
+++ b/src/lib/testing/netjail.h
@@ -0,0 +1,358 @@
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 * @brief API for writing an interpreter to test GNUnet components
23 * @author Christian Grothoff <christian@grothoff.org>
24 * @author Marcello Stanisci
25 * @author t3sserakt
26 */
27#ifndef NETJAIL_H
28#define NETJAIL_H
29
30#include "gnunet_util_lib.h"
31#include "gnunet_testing_lib.h"
32
33
34/**
35 * Router of a netjail subnet.
36 */
37struct GNUNET_TESTING_NetjailRouter
38{
39 /**
40 * Will tcp be forwarded?
41 */
42 unsigned int tcp_port;
43
44 /**
45 * Will udp be forwarded?
46 */
47 unsigned int udp_port;
48};
49
50
51/**
52 * Enum for the different types of nodes.
53 */
54enum GNUNET_TESTING_NodeType
55{
56 /**
57 * Node in a subnet.
58 */
59 GNUNET_TESTING_SUBNET_NODE,
60
61 /**
62 * Global known node.
63 */
64 GNUNET_TESTING_GLOBAL_NODE
65};
66
67/**
68 * Protocol address prefix für a connection between nodes.
69 */
70struct GNUNET_TESTING_AddressPrefix
71{
72 /**
73 * Pointer to the previous prefix in the DLL.
74 */
75 struct GNUNET_TESTING_AddressPrefix *prev;
76
77 /**
78 * Pointer to the next prefix in the DLL.
79 */
80 struct GNUNET_TESTING_AddressPrefix *next;
81
82 /**
83 * The address prefix.
84 */
85 char *address_prefix;
86};
87
88
89/**
90 * Node in a netjail topology.
91 */
92struct GNUNET_TESTING_NetjailNode;
93
94/**
95 * Connection to another node.
96 */
97struct GNUNET_TESTING_NodeConnection
98{
99 /**
100 * Pointer to the previous connection in the DLL.
101 */
102 struct GNUNET_TESTING_NodeConnection *prev;
103
104 /**
105 * Pointer to the next connection in the DLL.
106 */
107 struct GNUNET_TESTING_NodeConnection *next;
108
109 /**
110 * The number of the subnet of the node this connection points to. This is 0,
111 * if the node is a global known node.
112 */
113 unsigned int namespace_n;
114
115 /**
116 * The number of the node this connection points to.
117 */
118 unsigned int node_n;
119
120 /**
121 * The type of the node this connection points to.
122 */
123 enum GNUNET_TESTING_NodeType node_type;
124
125 /**
126 * The node which establish the connection
127 */
128 struct GNUNET_TESTING_NetjailNode *node;
129
130 /**
131 * Head of the DLL with the address prefixes for the protocolls this node is reachable.
132 */
133 struct GNUNET_TESTING_AddressPrefix *address_prefixes_head;
134
135 /**
136 * Tail of the DLL with the address prefixes for the protocolls this node is reachable.
137 */
138 struct GNUNET_TESTING_AddressPrefix *address_prefixes_tail;
139};
140
141/**
142 * Node in the netjail topology.
143 */
144struct GNUNET_TESTING_NetjailNode
145{
146 /**
147 * Head of the DLL with the connections which shall be established to other nodes.
148 */
149 struct GNUNET_TESTING_NodeConnection *node_connections_head;
150
151 /**
152 * Tail of the DLL with the connections which shall be established to other nodes.
153 */
154 struct GNUNET_TESTING_NodeConnection *node_connections_tail;
155
156 /**
157 * Plugin for the test case to be run on this node.
158 */
159 char *plugin;
160
161 /**
162 * Flag indicating if this node is a global known node.
163 */
164 unsigned int is_global;
165
166 /**
167 * The number of the subnet this node is running in.
168 */
169 unsigned int namespace_n;
170
171 /**
172 * The number of this node in the subnet.
173 */
174 unsigned int node_n;
175
176 /**
177 * The overall number of the node in the whole test system.
178 */
179 unsigned int node_number;
180
181 /**
182 * The number of unintentional additional connections this node waits for. This overwrites the global additional_connects value.
183 */
184 unsigned int additional_connects;
185
186 /**
187 * The number of cmds waiting for a specific barrier.
188 */
189 unsigned int expected_reaches;
190};
191
192
193/**
194 * Subnet in a topology.
195 */
196struct GNUNET_TESTING_NetjailNamespace
197{
198 /**
199 * The number of the subnet.
200 */
201 unsigned int namespace_n;
202
203 /**
204 * Router of the subnet.
205 */
206 struct GNUNET_TESTING_NetjailRouter *router;
207
208 /**
209 * Hash map containing the nodes in this subnet.
210 */
211 struct GNUNET_CONTAINER_MultiShortmap *nodes;
212};
213
214/**
215 * Toplogy of our netjail setup.
216 */
217struct GNUNET_TESTING_NetjailTopology
218{
219
220 /**
221 * Default plugin for the test case to be run on nodes.
222 */
223 char *plugin;
224
225 /**
226 * Total number of namespaces in the topology;
227 * numbered starting from 1 (!).
228 */
229 unsigned int total;
230
231 /**
232 * Number of subnets.
233 */
234 unsigned int namespaces_n;
235
236 /**
237 * Number of nodes per subnet.
238 */
239 unsigned int nodes_m;
240
241 /**
242 * Number of global known nodes.
243 */
244 unsigned int nodes_x;
245
246 /**
247 * Hash map containing the subnets (for natted nodes) of the topology.
248 */
249 struct GNUNET_CONTAINER_MultiShortmap *map_namespaces;
250
251 /**
252 * Hash map containing the global known nodes which are not natted.
253 */
254 struct GNUNET_CONTAINER_MultiShortmap *map_globals;
255
256 /**
257 * Additional connects we do expect, beside the connects which are configured in the topology.
258 */
259 unsigned int additional_connects;
260};
261
262
263/**
264 * Parse the topology data.
265 *
266 * @param data The topology data.
267 * @return The GNUNET_TESTING_NetjailTopology
268 */
269struct GNUNET_TESTING_NetjailTopology *
270GNUNET_TESTING_get_topo_from_string_ (const char *data);
271
272
273/**
274 * Get the number of unintentional additional connections the node waits for.
275 *
276 * @param num The specific node we want the additional connects for.
277 * @return The number of additional connects
278 */
279unsigned int
280GNUNET_TESTING_get_additional_connects (
281 unsigned int num,
282 struct GNUNET_TESTING_NetjailTopology *topology);
283
284
285/**
286 * Get a node from the topology.
287 *
288 * @param num The specific node we want the connections for.
289 * @param topology The topology we get the connections from.
290 * @return The connections of the node.
291 */
292struct GNUNET_TESTING_NetjailNode *
293GNUNET_TESTING_get_node (unsigned int num,
294 struct GNUNET_TESTING_NetjailTopology *topology);
295
296
297/**
298 * Get the connections to other nodes for a specific node.
299 *
300 * @param num The specific node we want the connections for.
301 * @param topology The topology we get the connections from.
302 * @return The connections of the node.
303 */
304struct GNUNET_TESTING_NodeConnection *
305GNUNET_TESTING_get_connections (
306 unsigned int num,
307 const struct GNUNET_TESTING_NetjailTopology *topology);
308
309
310/**
311 * Get the address for a specific communicator from a connection.
312 *
313 * @param connection The connection we like to have the address from.
314 * @param prefix The communicator protocol prefix.
315 * @return The address of the communicator.
316 */
317char *
318GNUNET_TESTING_get_address (
319 struct GNUNET_TESTING_NodeConnection *connection,
320 const char *prefix);
321
322
323/**
324 * Deallocate memory of the struct GNUNET_TESTING_NetjailTopology.
325 *
326 * @param[in] topology The GNUNET_TESTING_NetjailTopology to be deallocated.
327 */
328void
329GNUNET_TESTING_free_topology (struct GNUNET_TESTING_NetjailTopology *topology);
330
331
332/**
333 * Calculate the unique id identifying a node from a given connection.
334 *
335 * @param node_connection The connection we calculate the id from.
336 * @param topology The topology we get all needed information from.
337 * @return The unique id of the node from the connection.
338 */
339unsigned int
340GNUNET_TESTING_calculate_num (
341 struct GNUNET_TESTING_NodeConnection *node_connection,
342 struct GNUNET_TESTING_NetjailTopology *topology);
343
344
345/**
346 * Call #op on all simple traits.
347 */
348#define GNUNET_TESTING_SIMPLE_NETJAIL_TRAITS(op, prefix) \
349 op (prefix, get_topology, const struct GNUNET_TESTING_NetjailTopology) \
350 op (prefix, get_topology_string, char) \
351 op (prefix, async_context, struct GNUNET_TESTING_AsyncContext) \
352 op (prefix, helper_handles, const struct GNUNET_HELPER_Handle *)
353
354GNUNET_TESTING_SIMPLE_NETJAIL_TRAITS (GNUNET_TESTING_MAKE_DECL_SIMPLE_TRAIT,
355 GNUNET_TESTING)
356
357
358#endif
diff --git a/src/lib/testing/testing.conf b/src/lib/testing/testing.conf
new file mode 100644
index 000000000..7e25f8c13
--- /dev/null
+++ b/src/lib/testing/testing.conf
@@ -0,0 +1,11 @@
1[TESTING]
2# How long before failing a connection?
3CONNECT_TIMEOUT = 30 s
4# How many connect attempts should we make?
5CONNECT_ATTEMPTS = 3
6# How many connections can happen simultaneously?
7MAX_OUTSTANDING_CONNECTIONS = 50
8
9# Should we clean up the files on peer group shutdown?
10DELETE_FILES = YES
11
diff --git a/src/lib/testing/testing_api_cmd_barrier.c b/src/lib/testing/testing_api_cmd_barrier.c
new file mode 100644
index 000000000..582df792d
--- /dev/null
+++ b/src/lib/testing/testing_api_cmd_barrier.c
@@ -0,0 +1,106 @@
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/**
22 * @file testing/testing_api_cmd_barrier.c
23 * @brief Barrier functionality.
24 * @author t3sserakt
25 */
26#include "platform.h"
27#include "gnunet_testing_lib.h"
28#include "barrier.h"
29#include "testing_api_loop.h"
30
31
32/**
33 * Offer internal data from a "barrier" CMD, to other commands.
34 *
35 * @param cls closure.
36 * @param[out] ret result.
37 * @param trait name of the trait.
38 * @param index index number of the object to offer.
39 * @return #GNUNET_OK on success.
40 */
41static enum GNUNET_GenericReturnValue
42barrier_traits (void *cls,
43 const void **ret,
44 const char *trait,
45 unsigned int index)
46{
47 struct GNUNET_TESTING_Trait traits[] = {
48 GNUNET_TESTING_trait_end ()
49 };
50
51 return GNUNET_TESTING_get_trait (traits,
52 ret,
53 trait,
54 index);
55}
56
57
58/**
59 * Cleanup the state from a "barrier" CMD, and possibly
60 * cancel a pending operation thereof.
61 *
62 * @param cls closure.
63 */
64static void
65barrier_cleanup (void *cls)
66{
67 struct GNUNET_TESTING_Barrier *barrier = cls;
68
69 GNUNET_free (barrier);
70}
71
72
73/**
74 * Run the command.
75 *
76 * @param cls closure.
77 * @param is the interpreter state.
78 */
79static void
80barrier_run (void *cls,
81 struct GNUNET_TESTING_Interpreter *is)
82{
83 struct GNUNET_TESTING_Barrier *barrier = cls;
84
85 GNUNET_TESTING_add_barrier_ (is,
86 barrier);
87}
88
89
90struct GNUNET_TESTING_Command
91GNUNET_TESTING_cmd_barrier_create (
92 const char *label,
93 unsigned int number_to_be_reached)
94{
95 struct GNUNET_TESTING_Barrier *barrier;
96
97 barrier = GNUNET_new (struct GNUNET_TESTING_Barrier);
98 GNUNET_TESTING_barrier_name_hash_ (label,
99 &barrier->barrier_id);
100 barrier->expected_reaches = number_to_be_reached;
101 return GNUNET_TESTING_command_new (barrier,
102 label,
103 &barrier_run,
104 &barrier_cleanup,
105 &barrier_traits);
106}
diff --git a/src/lib/testing/testing_api_cmd_barrier_reached.c b/src/lib/testing/testing_api_cmd_barrier_reached.c
new file mode 100644
index 000000000..dac0705cf
--- /dev/null
+++ b/src/lib/testing/testing_api_cmd_barrier_reached.c
@@ -0,0 +1,182 @@
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/**
22 * @file testing/testing_api_cmd_barrier_reached.c
23 * @brief Command to signal barrier was reached.
24 * @author t3sserakt
25 */
26#include "platform.h"
27#include "gnunet_testing_lib.h"
28#include "testing_api_loop.h"
29#include "testing_cmds.h"
30
31/**
32 * Generic logging shortcut
33 */
34#define LOG(kind, ...) GNUNET_log (kind, __VA_ARGS__)
35
36/**
37 * Struct with information for callbacks.
38 *
39 */
40struct BarrierReachedState
41{
42 /**
43 * Context for our asynchronous completion.
44 */
45 struct GNUNET_TESTING_AsyncContext ac;
46
47 /**
48 * The label of this command.
49 */
50 const char *label;
51
52 /**
53 * The name of the barrier this commands wait (if finishing asynchronous) for or/and reaches.
54 */
55 const char *barrier_name;
56
57};
58
59
60/**
61 * Run the command.
62 *
63 * @param cls closure.
64 * @param is the interpreter state.
65 */
66static void
67barrier_reached_run (void *cls,
68 struct GNUNET_TESTING_Interpreter *is)
69{
70 struct BarrierReachedState *brs = cls;
71 struct GNUNET_TESTING_Barrier *barrier;
72
73 barrier = GNUNET_TESTING_get_barrier_ (is,
74 brs->barrier_name);
75 if (NULL == barrier)
76 {
77 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
78 "No barrier `%s'\n",
79 brs->barrier_name);
80 GNUNET_TESTING_async_fail (&brs->ac);
81 return;
82 }
83 if (barrier->satisfied)
84 {
85 GNUNET_TESTING_async_finish (&brs->ac);
86 return;
87 }
88 if (barrier->inherited)
89 {
90 struct GNUNET_TESTING_CommandBarrierReached cbr = {
91 .header.size = htons (sizeof (cbr)),
92 .header.type = htons (GNUNET_MESSAGE_TYPE_CMDS_HELPER_BARRIER_REACHED)
93 };
94
95 GNUNET_TESTING_barrier_name_hash_ (brs->barrier_name,
96 &cbr.barrier_key);
97 GNUNET_TESTING_loop_notify_parent_ (is,
98 &cbr.header);
99 return;
100 }
101 barrier->reached++;
102 if (barrier->reached == barrier->expected_reaches)
103 {
104 struct GNUNET_TESTING_CommandBarrierSatisfied cbs = {
105 .header.size = htons (sizeof (cbs)),
106 .header.type = htons (GNUNET_MESSAGE_TYPE_CMDS_HELPER_BARRIER_CROSSABLE)
107 };
108
109 GNUNET_TESTING_barrier_name_hash_ (brs->barrier_name,
110 &cbs.barrier_key);
111 barrier->satisfied = true;
112 GNUNET_TESTING_loop_notify_children_ (is,
113 &cbs.header);
114 }
115 if (barrier->satisfied)
116 {
117 GNUNET_TESTING_async_finish (&brs->ac);
118 return;
119 }
120 barrier->cmd_ac = &brs->ac;
121}
122
123
124/**
125 * Cleanup the state from a "barrier reached" CMD, and possibly
126 * cancel a pending operation thereof.
127 *
128 * @param cls closure.
129 */
130static void
131barrier_reached_cleanup (void *cls)
132{
133 struct BarrierReachedState *brs = cls;
134
135 GNUNET_free (brs);
136}
137
138
139/**
140 * Offer internal data from a "batch" CMD, to other commands.
141 *
142 * @param cls closure.
143 * @param[out] ret result.
144 * @param trait name of the trait.
145 * @param index index number of the object to offer.
146 * @return #GNUNET_OK on success.
147 */
148static enum GNUNET_GenericReturnValue
149barrier_reached_traits (void *cls,
150 const void **ret,
151 const char *trait,
152 unsigned int index)
153{
154 struct GNUNET_TESTING_Trait traits[] = {
155 GNUNET_TESTING_trait_end ()
156 };
157
158 return GNUNET_TESTING_get_trait (traits,
159 ret,
160 trait,
161 index);
162}
163
164
165struct GNUNET_TESTING_Command
166GNUNET_TESTING_cmd_barrier_reached (
167 const char *label,
168 const char *barrier_label)
169{
170 struct BarrierReachedState *brs;
171
172 brs = GNUNET_new (struct BarrierReachedState);
173 brs->label = label;
174 brs->barrier_name = barrier_label;
175 return GNUNET_TESTING_command_new_ac (
176 brs,
177 label,
178 &barrier_reached_run,
179 &barrier_reached_cleanup,
180 &barrier_reached_traits,
181 &brs->ac);
182}
diff --git a/src/lib/testing/testing_api_cmd_batch.c b/src/lib/testing/testing_api_cmd_batch.c
new file mode 100644
index 000000000..3aeaa5bfb
--- /dev/null
+++ b/src/lib/testing/testing_api_cmd_batch.c
@@ -0,0 +1,210 @@
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 testing/testing_api_cmd_batch.c
23 * @brief Implement batch-execution of CMDs.
24 * @author Marcello Stanisci (GNU Taler testing)
25 * @author t3sserakt
26 */
27#include "platform.h"
28#include "gnunet_testing_ng_lib.h"
29#include "testing.h"
30
31/**
32 * State for a "batch" CMD.
33 */
34struct BatchState
35{
36 /**
37 * CMDs batch.
38 */
39 struct GNUNET_TESTING_Command *batch;
40
41 /**
42 * Our label.
43 */
44 struct GNUNET_TESTING_CommandLabel label;
45
46 /**
47 * Internal command pointer.
48 */
49 unsigned int batch_ip;
50};
51
52
53/**
54 * Run the command.
55 *
56 * @param cls closure.
57 * @param is the interpreter state.
58 */
59static void
60batch_run (void *cls,
61 struct GNUNET_TESTING_Interpreter *is)
62{
63 struct BatchState *bs = cls;
64
65 if (NULL != bs->batch[bs->batch_ip].run)
66 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
67 "Running batched command: %s\n",
68 bs->batch[bs->batch_ip].label.value);
69
70 /* hit end command, leap to next top-level command. */
71 if (NULL == bs->batch[bs->batch_ip].run)
72 {
73 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
74 "Exiting from batch: %s\n",
75 bs->label.value);
76 return;
77 }
78 bs->batch[bs->batch_ip].start_time
79 = bs->batch[bs->batch_ip].last_req_time
80 = GNUNET_TIME_absolute_get ();
81 bs->batch[bs->batch_ip].num_tries = 1;
82 bs->batch[bs->batch_ip].run (bs->batch[bs->batch_ip].cls,
83 is);
84}
85
86
87/**
88 * Cleanup the state from a "reserve status" CMD, and possibly
89 * cancel a pending operation thereof.
90 *
91 * @param cls closure.
92 */
93static void
94batch_cleanup (void *cls)
95{
96 struct BatchState *bs = cls;
97
98 for (unsigned int i = 0;
99 NULL != bs->batch[i].run;
100 i++)
101 bs->batch[i].cleanup (bs->batch[i].cls);
102 GNUNET_free (bs->batch);
103 GNUNET_free (bs);
104}
105
106
107/**
108 * Offer internal data from a "batch" CMD, to other commands.
109 *
110 * @param cls closure.
111 * @param[out] ret result.
112 * @param trait name of the trait.
113 * @param index index number of the object to offer.
114 * @return #GNUNET_OK on success.
115 */
116static enum GNUNET_GenericReturnValue
117batch_traits (void *cls,
118 const void **ret,
119 const char *trait,
120 unsigned int index)
121{
122 struct BatchState *bs = cls;
123 // FIXME: these constants should be more global!
124#define CURRENT_CMD_INDEX 0
125#define BATCH_INDEX 1
126 struct GNUNET_TESTING_Trait traits[] = {
127 GNUNET_TESTING_make_trait_cmd (CURRENT_CMD_INDEX,
128 &bs->batch[bs->batch_ip]),
129 GNUNET_TESTING_make_trait_cmd (BATCH_INDEX,
130 bs->batch),
131 GNUNET_TESTING_trait_end ()
132 };
133
134 /* Always return current command. */
135 return GNUNET_TESTING_get_trait (traits,
136 ret,
137 trait,
138 index);
139}
140
141
142/**
143 * Create a "batch" command. Such command takes a
144 * end_CMD-terminated array of CMDs and executed them.
145 * Once it hits the end CMD, it passes the control
146 * to the next top-level CMD, regardless of it being
147 * another batch or ordinary CMD.
148 *
149 * @param label the command label.
150 * @param batch array of CMDs to execute.
151 *
152 * @return the command.
153 */
154struct GNUNET_TESTING_Command
155GNUNET_TESTING_cmd_batch (const char *label,
156 struct GNUNET_TESTING_Command *batch)
157{
158 struct BatchState *bs;
159 unsigned int i;
160
161 bs = GNUNET_new (struct BatchState);
162 GNUNET_TESTING_set_label (&bs->label,
163 label);
164 /* Get number of commands. */
165 for (i = 0; NULL != batch[i].run; i++)
166 /* noop */
167 ;
168
169 bs->batch = GNUNET_new_array (i + 1,
170 struct GNUNET_TESTING_Command);
171 memcpy (bs->batch,
172 batch,
173 sizeof (struct GNUNET_TESTING_Command) * i);
174 return GNUNET_TESTING_command_new (bs,
175 label,
176 &batch_run,
177 &batch_cleanup,
178 &batch_traits);
179}
180
181
182bool
183GNUNET_TESTING_cmd_batch_next_ (void *cls)
184{
185 struct BatchState *bs = cls;
186
187 if (NULL == bs->batch[bs->batch_ip].run)
188 return false;
189 bs->batch[bs->batch_ip].finish_time
190 = GNUNET_TIME_absolute_get ();
191 bs->batch_ip++;
192 return true;
193}
194
195
196bool
197GNUNET_TESTING_cmd_is_batch_ (const struct GNUNET_TESTING_Command *cmd)
198{
199 return cmd->run == &batch_run;
200}
201
202
203struct GNUNET_TESTING_Command *
204GNUNET_TESTING_cmd_batch_get_current_ (const struct GNUNET_TESTING_Command *cmd)
205{
206 struct BatchState *bs = cmd->cls;
207
208 GNUNET_assert (GNUNET_TESTING_cmd_is_batch_ (cmd));
209 return &bs->batch[bs->batch_ip];
210}
diff --git a/src/lib/testing/testing_api_cmd_batch.h b/src/lib/testing/testing_api_cmd_batch.h
new file mode 100644
index 000000000..4b4d041d7
--- /dev/null
+++ b/src/lib/testing/testing_api_cmd_batch.h
@@ -0,0 +1,61 @@
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/**
22 * @file testing_api_cmd_batch.h
23 * @brief
24 * @author t3sserakt
25 */
26#ifndef TESTING_API_CMD_BATCH_H
27#define TESTING_API_CMD_BATCH_H
28
29
30/**
31 * Test if this command is a batch command.
32 *
33 * @return false if not, true if it is a batch command
34 */
35bool
36GNUNET_TESTING_cmd_is_batch_ (
37 const struct GNUNET_TESTING_Command *cmd);
38
39
40/**
41 * Advance internal pointer to next command.
42 *
43 * @param cls batch internal state
44 * @return true if we could advance, false if the batch
45 * has completed and cannot advance anymore
46 */
47bool
48GNUNET_TESTING_cmd_batch_next_ (void *cls);
49
50
51/**
52 * Obtain what command the batch is at.
53 *
54 * @return cmd current batch command
55 */
56struct GNUNET_TESTING_Command *
57GNUNET_TESTING_cmd_batch_get_current_ (
58 const struct GNUNET_TESTING_Command *cmd);
59
60
61#endif
diff --git a/src/lib/testing/testing_api_cmd_exec.c b/src/lib/testing/testing_api_cmd_exec.c
new file mode 100644
index 000000000..e452ce4b3
--- /dev/null
+++ b/src/lib/testing/testing_api_cmd_exec.c
@@ -0,0 +1,180 @@
1/*
2 This file is part of GNUnet
3 Copyright (C) 2023 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 testing_api_cmd_exec.c
23 * @brief cmd to block the interpreter loop until all peers started.
24 * @author t3sserakt
25 */
26#include "platform.h"
27#include "gnunet_util_lib.h"
28#include "gnunet_testing_lib.h"
29
30#define LOG(kind, ...) GNUNET_log (kind, __VA_ARGS__)
31
32struct BashScriptState
33{
34 /**
35 * Context for our asynchronous completion.
36 */
37 struct GNUNET_TESTING_AsyncContext ac;
38
39 /**
40 * Callback handed over to the command, which should
41 * be called upon death or completion of the script.
42 */
43 GNUNET_ChildCompletedCallback cb;
44
45 /**
46 * Wait for death of @e start_proc.
47 */
48 struct GNUNET_ChildWaitHandle *cwh;
49
50 /**
51 * The process id of the script.
52 */
53 struct GNUNET_OS_Process *start_proc;
54
55 /**
56 * Arguments for the script
57 */
58 char *const*script_argv;
59
60 /**
61 *
62 */
63 enum GNUNET_OS_ProcessStatusType expected_type;
64
65 /**
66 *
67 */
68 unsigned long int expected_exit_code;
69
70};
71
72/**
73 * The cleanup function of this cmd frees resources the cmd allocated.
74 *
75 */
76static void
77exec_bash_script_cleanup (void *cls)
78{
79 struct BashScriptState *bss = cls;
80
81 if (NULL != bss->cwh)
82 {
83 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
84 "Cancel child\n");
85 GNUNET_wait_child_cancel (bss->cwh);
86 bss->cwh = NULL;
87 }
88 if (NULL != bss->start_proc)
89 {
90 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
91 "Kill process\n");
92 GNUNET_assert (0 ==
93 GNUNET_OS_process_kill (bss->start_proc,
94 SIGKILL));
95 GNUNET_assert (GNUNET_OK ==
96 GNUNET_OS_process_wait (bss->start_proc));
97 GNUNET_OS_process_destroy (bss->start_proc);
98 bss->start_proc = NULL;
99 }
100 GNUNET_free (bss);
101}
102
103
104/**
105 * Callback which will be called if the setup script finished.
106 *
107 */
108static void
109child_completed_callback (void *cls,
110 enum GNUNET_OS_ProcessStatusType type,
111 long unsigned int exit_code)
112{
113 struct BashScriptState *bss = cls;
114
115 GNUNET_OS_process_destroy (bss->start_proc);
116 bss->start_proc = NULL;
117 bss->cwh = NULL;
118 if ( (bss->expected_type != type) ||
119 (bss->expected_exit_code != exit_code) )
120 {
121 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
122 "Child failed with error %lu!\n",
123 exit_code);
124 GNUNET_TESTING_async_fail (&bss->ac);
125 return;
126 }
127 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
128 "Child succeeded!\n");
129 GNUNET_TESTING_async_finish (&bss->ac);
130}
131
132
133/**
134 * Run method of the command created by the interpreter to wait for another
135 * command to finish.
136 *
137 */
138static void
139exec_bash_script_run (void *cls,
140 struct GNUNET_TESTING_Interpreter *is)
141{
142 struct BashScriptState *bss = cls;
143
144 bss->start_proc
145 = GNUNET_OS_start_process_vap (
146 GNUNET_OS_INHERIT_STD_ERR,
147 NULL,
148 NULL,
149 NULL,
150 bss->script_argv[0],
151 bss->script_argv);
152 bss->cwh = GNUNET_wait_child (bss->start_proc,
153 &child_completed_callback,
154 bss);
155 GNUNET_break (NULL != bss->cwh);
156}
157
158
159// FIXME: support variadic style...
160const struct GNUNET_TESTING_Command
161GNUNET_TESTING_cmd_exec (
162 const char *label,
163 enum GNUNET_OS_ProcessStatusType expected_type,
164 unsigned long int expected_exit_code,
165 char *const script_argv[])
166{
167 struct BashScriptState *bss;
168
169 bss = GNUNET_new (struct BashScriptState);
170 bss->script_argv = script_argv; // FIXME: make copy?
171 bss->expected_type = expected_type;
172 bss->expected_exit_code = expected_exit_code;
173 return GNUNET_TESTING_command_new_ac (
174 bss,
175 label,
176 &exec_bash_script_run,
177 &exec_bash_script_cleanup,
178 NULL,
179 &bss->ac);
180}
diff --git a/src/lib/testing/testing_api_cmd_finish.c b/src/lib/testing/testing_api_cmd_finish.c
new file mode 100644
index 000000000..7abcb5bb9
--- /dev/null
+++ b/src/lib/testing/testing_api_cmd_finish.c
@@ -0,0 +1,215 @@
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 * @file testing/testing_api_cmd_finish.c
22 * @brief command to wait for completion of async command
23 * @author Christian Grothoff
24 */
25#include "platform.h"
26#include "gnunet_util_lib.h"
27#include "gnunet_testing_lib.h"
28
29
30/**
31 * Struct to use for command-specific context information closure of a command waiting
32 * for another command.
33 */
34struct FinishState
35{
36 /**
37 * Closure for all commands with command-specific context information.
38 */
39 void *cls;
40
41 /**
42 * Label of the asynchronous command the synchronous command of this closure
43 * waits for.
44 */
45 const char *async_label;
46
47 /**
48 * Function to call when async operation is done.
49 */
50 GNUNET_SCHEDULER_TaskCallback old_notify;
51
52 /**
53 * Closure for @e notify_finished.
54 */
55 void *old_notify_cls;
56
57 /**
58 * Task for running the finish method of the asynchronous task the command
59 * is waiting for.
60 */
61 struct GNUNET_SCHEDULER_Task *finish_task;
62
63 /**
64 * Function to call when done.
65 */
66 struct GNUNET_TESTING_AsyncContext ac;
67
68 /**
69 * How long to wait until finish fails hard?
70 */
71 struct GNUNET_TIME_Relative timeout;
72
73};
74
75
76/**
77 * Function called when the command we are waiting on
78 * is finished. Hence we are finished, too.
79 *
80 * @param cls a `struct FinishState` being notified
81 */
82static void
83done_finish (void *cls)
84{
85 struct FinishState *finish_state = cls;
86
87 GNUNET_SCHEDULER_cancel (finish_state->finish_task);
88 finish_state->finish_task = NULL;
89 if (NULL != finish_state->old_notify)
90 {
91 finish_state->old_notify (finish_state->old_notify_cls);
92 finish_state->old_notify = NULL;
93 }
94 GNUNET_TESTING_async_finish (&finish_state->ac);
95}
96
97
98/**
99 * Function triggered if the command we are waiting
100 * for did not complete on time.
101 *
102 * @param cls our `struct FinishState`
103 */
104static void
105timeout_finish (void *cls)
106{
107 struct FinishState *finish_state = cls;
108
109 finish_state->finish_task = NULL;
110 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
111 "Timeout waiting for command `%s' to finish\n",
112 finish_state->async_label);
113 GNUNET_TESTING_async_fail (&finish_state->ac);
114}
115
116
117/**
118 * Run method of the command created by the interpreter to wait for another
119 * command to finish.
120 *
121 */
122static void
123run_finish (void *cls,
124 struct GNUNET_TESTING_Interpreter *is)
125{
126 struct FinishState *finish_state = cls;
127 const struct GNUNET_TESTING_Command *async_cmd;
128 struct GNUNET_TESTING_AsyncContext *aac;
129
130 async_cmd
131 = GNUNET_TESTING_interpreter_lookup_command (is,
132 finish_state->async_label);
133 if (NULL == async_cmd)
134 {
135 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
136 "Did not find command `%s'\n",
137 finish_state->async_label);
138 GNUNET_TESTING_interpreter_fail (is);
139 return;
140 }
141 if ( (NULL == (aac = async_cmd->ac)) ||
142 (! async_cmd->asynchronous_finish) )
143 {
144 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
145 "Cannot finish `%s': not asynchronous\n",
146 finish_state->async_label);
147 GNUNET_TESTING_interpreter_fail (is);
148 return;
149 }
150 if (GNUNET_NO != aac->finished)
151 {
152 /* Command is already finished, so are we! */
153 GNUNET_TESTING_async_finish (&finish_state->ac);
154 return;
155 }
156 /* add timeout */
157 finish_state->finish_task
158 = GNUNET_SCHEDULER_add_delayed (finish_state->timeout,
159 &timeout_finish,
160 finish_state);
161 /* back up old notification that we will override */
162 finish_state->old_notify = aac->notify_finished;
163 finish_state->old_notify_cls = aac->notify_finished_cls;
164 aac->notify_finished = &done_finish;
165 aac->notify_finished_cls = finish_state;
166}
167
168
169/**
170 * Cleanup state of a finish command.
171 *
172 * @param cls a `struct FinishState` to clean up
173 */
174static void
175cleanup_finish (void *cls)
176{
177 struct FinishState *finish_state = cls;
178
179 if (NULL != finish_state->finish_task)
180 {
181 GNUNET_SCHEDULER_cancel (finish_state->finish_task);
182 finish_state->finish_task = NULL;
183 }
184 GNUNET_free (finish_state);
185}
186
187
188const struct GNUNET_TESTING_Command
189GNUNET_TESTING_cmd_finish (const char *finish_label,
190 const char *cmd_ref,
191 struct GNUNET_TIME_Relative timeout)
192{
193 struct FinishState *finish_state;
194
195 finish_state = GNUNET_new (struct FinishState);
196 finish_state->async_label = cmd_ref;
197 finish_state->timeout = timeout;
198 return GNUNET_TESTING_command_new_ac (finish_state,
199 finish_label,
200 &run_finish,
201 &cleanup_finish,
202 NULL,
203 &finish_state->ac);
204}
205
206
207struct GNUNET_TESTING_Command
208GNUNET_TESTING_cmd_make_unblocking (struct GNUNET_TESTING_Command cmd)
209{
210 /* do not permit this function to be used on
211 a finish command! */
212 GNUNET_assert (cmd.run != &run_finish);
213 cmd.asynchronous_finish = true;
214 return cmd;
215}
diff --git a/src/lib/testing/testing_api_cmd_get_topo_from_file.c b/src/lib/testing/testing_api_cmd_get_topo_from_file.c
new file mode 100644
index 000000000..e38b81f24
--- /dev/null
+++ b/src/lib/testing/testing_api_cmd_get_topo_from_file.c
@@ -0,0 +1,161 @@
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 testing/testing_api_cmd_netjail_start.c
23 * @brief Command to start the netjail script.
24 * @author t3sserakt
25 */
26#include "platform.h"
27#include "gnunet_util_lib.h"
28
29struct TopologyState
30{
31 /**
32 * The label of the command.
33 */
34 char *label;
35
36 /**
37 * The topology we parsed.
38 */
39 struct GNUNET_TESTING_NetjailTopology *topology;
40
41 /**
42 * A string with the name of the topology file, if @e read_file is true,
43 * otherwise a string containing the topology data.
44 */
45 char *topology_string;
46
47 /**
48 * A string with the name of the topology file.
49 */
50 char *file_name;
51}
52
53/**
54 * The cleanup function of this cmd frees resources the cmd allocated.
55 *
56 */
57static void
58cleanup (void *cls)
59{
60 struct NetJailState *ts = cls;
61
62 GNUNET_free (ts);
63}
64
65/**
66 * This function prepares an array with traits.
67 */
68static enum GNUNET_GenericReturnValue
69traits (void *cls,
70 const void **ret,
71 const char *trait,
72 unsigned int index)
73{
74 struct NetJailState *ts = cls;
75 struct GNUNET_TESTING_Trait traits[] = {
76 GNUNET_TESTING_make_trait_get_topology ((const void *) ts->topology),
77 GNUNET_TESTING_make_trait_get_topology_string ((const void *) ts->topology_string),
78 GNUNET_TESTING_trait_end ()
79 };
80
81 return GNUNET_TESTING_get_trait (traits,
82 ret,
83 trait,
84 index);
85}
86
87static char *
88get_topo_string_from_file (char *topology_data_file)
89{
90 uint64_t fs;
91 char *data;
92
93 if (GNUNET_YES !=
94 GNUNET_DISK_file_test (topology_data_file))
95 {
96 LOG (GNUNET_ERROR_TYPE_ERROR,
97 "Topology file %s not found\n",
98 topology_data_file);
99 GNUNET_assert (0);
100 }
101 if (GNUNET_OK !=
102 GNUNET_DISK_file_size (topology_data_file,
103 &fs,
104 GNUNET_YES,
105 GNUNET_YES))
106 {
107 LOG (GNUNET_ERROR_TYPE_ERROR,
108 "Could not determine size of topology file %s\n",
109 topology_data_file);
110 GNUNET_assert (0);
111 }
112 data = GNUNET_malloc_large (fs + 1);
113 GNUNET_assert (NULL != data);
114 if (fs !=
115 GNUNET_DISK_fn_read (topology_data_file,
116 data,
117 fs))
118 {
119 LOG (GNUNET_ERROR_TYPE_ERROR,
120 "Topology file %s cannot be read\n",
121 topology_data_file);
122 GNUNET_free (data);
123 return NULL;
124 }
125 return data;
126}
127
128/**
129* The run method starts the script which setup the network namespaces.
130*
131* @param cls closure.
132* @param is interpreter state.
133*/
134static void
135run (void *cls,
136 struct GNUNET_TESTING_Interpreter *is)
137{
138 struct TopologyState *ts = cls;
139
140 ts->topology_string = get_topo_string_from_file (ts->file_name);
141 ts->topology = GNUNET_TESTING_get_topo_from_string (ts->topology_string);
142}
143
144struct GNUNET_TESTING_Command
145GNUNET_TESTING_cmd_get_topo_from_file (
146 const char *label,
147 char *file_name)
148{
149 struct TopologyState *ts;
150
151 ts = GNUNET_new (struct TopologyState);
152 ts->label = label;
153 ts->file_name = file_name;
154 return GNUNET_TESTING_command_new_ac (
155 ts,
156 label,
157 &run,
158 &cleanup,
159 traits,
160 &ts->ac);
161}
diff --git a/src/lib/testing/testing_api_cmd_get_topo_from_string.c b/src/lib/testing/testing_api_cmd_get_topo_from_string.c
new file mode 100644
index 000000000..777e8ae2a
--- /dev/null
+++ b/src/lib/testing/testing_api_cmd_get_topo_from_string.c
@@ -0,0 +1,119 @@
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 testing/testing_api_cmd_netjail_start.c
23 * @brief Command to start the netjail script.
24 * @author t3sserakt
25 */
26#include "platform.h"
27#include "gnunet_util_lib.h"
28
29struct TopologyState
30{
31 /**
32 * The label of the command.
33 */
34 char *label;
35
36 /**
37 * The topology we parsed.
38 */
39 struct GNUNET_TESTING_NetjailTopology *topology;
40
41 /**
42 * A string with the name of the topology file, if @e read_file is true,
43 * otherwise a string containing the topology data.
44 */
45 char *topology_string;
46
47 /**
48 * Are we reading from file, or did we get a string with the topology data?
49 */
50 bool read_file;
51}
52
53/**
54 * The cleanup function of this cmd frees resources the cmd allocated.
55 *
56 */
57static void
58netjail_topology_cleanup (void *cls)
59{
60 struct NetJailState *ts = cls;
61
62 GNUNET_free (ts);
63}
64
65/**
66 * This function prepares an array with traits.
67 */
68static enum GNUNET_GenericReturnValue
69netjail_topology_traits (void *cls,
70 const void **ret,
71 const char *trait,
72 unsigned int index)
73{
74 struct NetJailState *ts = cls;
75 struct GNUNET_TESTING_Trait traits[] = {
76 GNUNET_TESTING_make_trait_get_topology ((const void *) ts->topology),
77 GNUNET_TESTING_make_trait_get_topology_string ((const void *) ts->topology_string),
78 GNUNET_TESTING_trait_end ()
79 };
80
81 return GNUNET_TESTING_get_trait (traits,
82 ret,
83 trait,
84 index);
85}
86
87/**
88* The run method starts the script which setup the network namespaces.
89*
90* @param cls closure.
91* @param is interpreter state.
92*/
93static void
94netjail_topology_run (void *cls,
95 struct GNUNET_TESTING_Interpreter *is)
96{
97 struct TopologyState *ts = cls;
98
99 ts->topology = GNUNET_TESTING_get_topo_from_string (topology_string);
100}
101
102struct GNUNET_TESTING_Command
103GNUNET_TESTING_cmd_get_topo_from_string (
104 const char *label,
105 char *topology_string)
106{
107 struct TopologyState *ts;
108
109 ts = GNUNET_new (struct TopologyState);
110 ts->label = label;
111 ts->topology_string = topology_string;
112 return GNUNET_TESTING_command_new_ac (
113 ts,
114 label,
115 &run,
116 &cleanup,
117 traits,
118 &ts->ac);
119}
diff --git a/src/lib/testing/testing_api_cmd_netjail_start.c b/src/lib/testing/testing_api_cmd_netjail_start.c
new file mode 100644
index 000000000..e81622133
--- /dev/null
+++ b/src/lib/testing/testing_api_cmd_netjail_start.c
@@ -0,0 +1,223 @@
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 testing/testing_api_cmd_netjail_start.c
23 * @brief Command to start the netjail script.
24 * @author t3sserakt
25 */
26#include "platform.h"
27#include "gnunet_util_lib.h"
28#include "gnunet_testing_lib.h"
29
30
31#define LOG(kind, ...) GNUNET_log (kind, __VA_ARGS__)
32
33/**
34 * Struct to hold information for callbacks.
35 *
36 */
37struct NetJailState
38{
39 /**
40 * Context for our asynchronous completion.
41 */
42 struct GNUNET_TESTING_AsyncContext ac;
43
44 struct GNUNET_ChildWaitHandle *cwh;
45
46 /**
47 * The process id of the start script.
48 */
49 struct GNUNET_OS_Process *start_proc;
50
51 /**
52 * Configuration file for the test topology.
53 */
54 const char *topology_cmd_label;
55
56 /**
57 * Start or stop?
58 */
59 const char *script;
60
61};
62
63
64/**
65 * The cleanup function of this cmd frees resources the cmd allocated.
66 *
67 */
68static void
69netjail_start_cleanup (void *cls)
70{
71 struct NetJailState *ns = cls;
72
73 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
74 "netjail_start_cleanup!\n");
75 if (NULL != ns->cwh)
76 {
77 GNUNET_wait_child_cancel (ns->cwh);
78 ns->cwh = NULL;
79 }
80 if (NULL != ns->start_proc)
81 {
82 GNUNET_assert (0 ==
83 GNUNET_OS_process_kill (ns->start_proc,
84 SIGKILL));
85 GNUNET_assert (GNUNET_OK ==
86 GNUNET_OS_process_wait (ns->start_proc));
87 GNUNET_OS_process_destroy (ns->start_proc);
88 ns->start_proc = NULL;
89 }
90 GNUNET_free (ns);
91}
92
93
94/**
95 * Callback which will be called if the setup script finished.
96 */
97static void
98child_completed_callback (void *cls,
99 enum GNUNET_OS_ProcessStatusType type,
100 unsigned long int exit_code)
101{
102 struct NetJailState *ns = cls;
103
104 GNUNET_OS_process_destroy (ns->start_proc);
105 ns->start_proc = NULL;
106 ns->cwh = NULL;
107 if ( (GNUNET_OS_PROCESS_EXITED != type) ||
108 (0 != exit_code) )
109 {
110 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
111 "Child failed with error %lu!\n",
112 exit_code);
113 GNUNET_TESTING_async_fail (&ns->ac);
114 return;
115 }
116 GNUNET_TESTING_async_finish (&ns->ac);
117}
118
119
120/**
121* The run method starts the script which setup the network namespaces.
122*
123* @param cls closure.
124* @param is interpreter state.
125*/
126static void
127netjail_start_run (void *cls,
128 struct GNUNET_TESTING_Interpreter *is)
129{
130 struct NetJailState *ns = cls;
131 const struct GNUNET_TESTING_Command *topo_cmd;
132 char pid[15];
133 enum GNUNET_GenericReturnValue helper_check;
134 char *data_dir;
135 char *script_name;
136 const char *topology_data;
137
138 topo_cmd = GNUNET_TESTING_interpreter_lookup_command (
139 is,
140 ns->topology_cmd_label);
141 if (NULL == topo_cmd)
142 {
143 GNUNET_break (0);
144 GNUNET_TESTING_interpreter_fail (is);
145 return;
146 }
147 // FIXME: get topology_data from topo_cmd trait!
148 data_dir = GNUNET_OS_installation_get_path (GNUNET_OS_IPK_DATADIR);
149 GNUNET_asprintf (&script_name,
150 "%s%s",
151 data_dir,
152 ns->script);
153 helper_check = GNUNET_OS_check_helper_binary (
154 script_name,
155 GNUNET_YES,
156 NULL);
157 if (GNUNET_NO == helper_check)
158 {
159 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
160 "No SUID for %s!\n",
161 script_name);
162 GNUNET_TESTING_interpreter_fail (is);
163 return;
164 }
165 if (GNUNET_SYSERR == helper_check)
166 {
167 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
168 "%s not found!\n",
169 script_name);
170 GNUNET_TESTING_interpreter_fail (is);
171 return;
172 }
173
174 GNUNET_snprintf (pid,
175 sizeof (pid),
176 "%u",
177 getpid ());
178 {
179 char *const script_argv[] = {
180 script_name,
181 (char *) topology_data,
182 pid, // FIXME: use $PPID!
183 "0",
184 NULL
185 };
186
187 ns->start_proc
188 = GNUNET_OS_start_process_vap (GNUNET_OS_INHERIT_STD_ERR,
189 NULL,
190 NULL,
191 NULL,
192 script_name,
193 script_argv);
194
195 }
196 ns->cwh = GNUNET_wait_child (ns->start_proc,
197 &child_completed_callback,
198 ns);
199 GNUNET_break (NULL != ns->cwh);
200 GNUNET_free (script_name);
201 GNUNET_free (data_dir);
202}
203
204
205struct GNUNET_TESTING_Command
206GNUNET_TESTING_cmd_netjail_setup (
207 const char *label,
208 const char *script,
209 const char *topology_cmd_label)
210{
211 struct NetJailState *ns;
212
213 ns = GNUNET_new (struct NetJailState);
214 ns->script = script;
215 ns->topology_cmd_label = topology_cmd_label;
216 return GNUNET_TESTING_command_new_ac (
217 ns,
218 label,
219 &netjail_start_run,
220 &netjail_start_cleanup,
221 NULL,
222 &ns->ac);
223}
diff --git a/src/lib/testing/testing_api_cmd_netjail_start_cmds_helper.c b/src/lib/testing/testing_api_cmd_netjail_start_cmds_helper.c
new file mode 100644
index 000000000..4236f0da2
--- /dev/null
+++ b/src/lib/testing/testing_api_cmd_netjail_start_cmds_helper.c
@@ -0,0 +1,555 @@
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 * @file testing/testing_api_cmd_netjail_start_cmds_helper.c
22 * @brief Command to start the netjail peers.
23 * @author t3sserakt
24 */
25#include "platform.h"
26#include "gnunet_util_lib.h"
27#include "gnunet_testing_lib.h"
28#include "barrier.h"
29#include "netjail.h"
30#include "testing_api_loop.h"
31#include "testing_cmds.h"
32#include "netjail.h"
33
34
35/**
36 * Generic logging shortcut
37 */
38#define LOG(kind, ...) GNUNET_log (kind, __VA_ARGS__)
39
40
41/**
42 * Struct containing the number of the netjail node and the NetJailState which
43 * will be handed to callbacks specific to a test environment.
44 */
45struct TestingSystemCount;
46
47
48/**
49 * Struct to store information handed over to callbacks.
50 */
51struct NetJailState
52{
53 /**
54 * Global state of the interpreter, used by a command
55 * to access information about other commands.
56 */
57 struct GNUNET_TESTING_Interpreter *is;
58
59 /**
60 * Context for our asynchronous completion.
61 */
62 struct GNUNET_TESTING_AsyncContext ac;
63
64 /**
65 * Command with topology data.
66 */
67 const char *topology_cmd_label;
68
69 /**
70 * Array with handles of helper processes.
71 */
72 struct GNUNET_HELPER_Handle **helpers;
73
74 /**
75 * Time after this cmd has to finish.
76 */
77 struct GNUNET_TIME_Relative timeout;
78
79 /**
80 * Timeout task.
81 */
82 struct GNUNET_SCHEDULER_Task *timeout_task;
83
84 /**
85 * Kept in a DLL.
86 */
87 struct TestingSystemCount *tbc_head;
88
89 /**
90 * Kept in a DLL.
91 */
92 struct TestingSystemCount *tbc_tail;
93
94 char *topology_data;
95
96 /**
97 * Size of the array @e helpers.
98 */
99 unsigned int n_helpers;
100
101 /**
102 * Counts number of helpers that finished.
103 */
104 unsigned int n_finished;
105
106 /**
107 * Set to true if we already failed the command.
108 */
109 bool failed;
110};
111
112/**
113 * Struct containing the number of the netjail node and the NetJailState which
114 * will be handed to callbacks specific to a test environment.
115 */
116struct TestingSystemCount
117{
118
119 /**
120 * Kept in a DLL.
121 */
122 struct TestingSystemCount *next;
123
124 /**
125 * Kept in a DLL.
126 */
127 struct TestingSystemCount *prev;
128
129 /**
130 * The send handle for the helper
131 */
132 struct GNUNET_HELPER_SendHandle *shandle;
133
134 /**
135 * Struct to store information handed over to callbacks.
136 */
137 struct NetJailState *ns;
138
139
140};
141
142
143/**
144 * Continuation function from GNUNET_HELPER_send()
145 *
146 * @param cls closure
147 * @param result #GNUNET_OK on success,
148 * #GNUNET_NO if helper process died
149 * #GNUNET_SYSERR during GNUNET_HELPER_stop
150 */
151static void
152clear_msg (void *cls,
153 enum GNUNET_GenericReturnValue result)
154{
155 struct TestingSystemCount *tbc = cls;
156 struct NetJailState *ns = tbc->ns;
157
158 GNUNET_assert (NULL != tbc->shandle);
159 tbc->shandle = NULL;
160 GNUNET_CONTAINER_DLL_remove (ns->tbc_head,
161 ns->tbc_tail,
162 tbc);
163 GNUNET_free (tbc);
164 if ( (! ns->failed) &&
165 (GNUNET_OK != result) )
166 {
167 ns->failed = true;
168 GNUNET_TESTING_interpreter_fail (ns->is);
169 }
170}
171
172
173static void
174handle_helper_barrier_reached (
175 void *cls,
176 const struct GNUNET_TESTING_CommandBarrierReached *rm)
177{
178 struct NetJailState *ns = cls;
179 struct GNUNET_TESTING_Barrier *barrier;
180
181 barrier = GNUNET_TESTING_get_barrier2_ (ns->is,
182 &rm->barrier_key);
183 if (NULL == barrier)
184 {
185 if (! ns->failed)
186 {
187 ns->failed = true;
188 GNUNET_TESTING_async_fail (&ns->ac);
189 }
190 return;
191 }
192 if (barrier->inherited)
193 {
194 /* pass on to parent */
195 GNUNET_TESTING_loop_notify_parent_ (ns->is,
196 &rm->header);
197 }
198 else
199 {
200 barrier->reached++;
201 if (barrier->reached == barrier->expected_reaches)
202 {
203 struct GNUNET_TESTING_CommandBarrierSatisfied cbs = {
204 .header.size
205 = htons (sizeof (cbs)),
206 .header.type
207 = htons (GNUNET_MESSAGE_TYPE_CMDS_HELPER_BARRIER_CROSSABLE),
208 .barrier_key
209 = rm->barrier_key
210 };
211
212 GNUNET_assert (! barrier->satisfied);
213 barrier->satisfied = true;
214 /* unblock children */
215 GNUNET_TESTING_loop_notify_children_ (ns->is,
216 &cbs.header);
217 /* unblock self */
218 if (NULL != barrier->cmd_ac)
219 GNUNET_TESTING_async_finish (barrier->cmd_ac);
220 }
221 }
222}
223
224
225static void
226handle_helper_local_finished (
227 void *cls,
228 const struct GNUNET_TESTING_CommandLocalFinished *lf)
229{
230 struct NetJailState *ns = cls;
231
232 ns->n_finished++;
233 if ( (! ns->failed) &&
234 (GNUNET_OK != ntohl (lf->rv)) )
235 {
236 ns->failed = true;
237 GNUNET_TESTING_async_fail (&ns->ac);
238 return;
239 }
240 if (ns->n_finished == ns->n_helpers)
241 {
242 GNUNET_SCHEDULER_cancel (ns->timeout_task);
243 ns->timeout_task = NULL;
244 GNUNET_TESTING_async_finish (&ns->ac);
245 }
246}
247
248
249/**
250 * Functions with this signature are called whenever a
251 * complete message is received by the tokenizer.
252 *
253 * Do not call GNUNET_SERVER_mst_destroy in callback
254 *
255 * @param cls closure
256 * @param message the actual message
257 * @return #GNUNET_OK on success, #GNUNET_SYSERR to stop further processing
258 */
259static enum GNUNET_GenericReturnValue
260helper_mst (void *cls,
261 const struct GNUNET_MessageHeader *message)
262{
263 struct NetJailState *ns = cls;
264 struct GNUNET_MQ_MessageHandler handlers[] = {
265 GNUNET_MQ_hd_fixed_size (
266 helper_barrier_reached,
267 GNUNET_MESSAGE_TYPE_CMDS_HELPER_BARRIER_REACHED,
268 struct GNUNET_TESTING_CommandBarrierReached,
269 ns),
270 GNUNET_MQ_hd_fixed_size (
271 helper_local_finished,
272 GNUNET_MESSAGE_TYPE_CMDS_HELPER_LOCAL_FINISHED,
273 struct GNUNET_TESTING_CommandLocalFinished,
274 ns),
275 GNUNET_MQ_handler_end ()
276 };
277 enum GNUNET_GenericReturnValue ret;
278
279 ret = GNUNET_MQ_handle_message (handlers,
280 message);
281 if (GNUNET_OK != ret)
282 {
283 GNUNET_break (0);
284 if (! ns->failed)
285 {
286 ns->failed = true;
287 GNUNET_TESTING_async_fail (&ns->ac);
288 }
289 }
290 return ret;
291}
292
293
294/**
295 * Callback called if there was an exception during execution of the helper.
296 */
297static void
298exp_cb (void *cls)
299{
300 struct NetJailState *ns = cls;
301
302 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
303 "Called exp_cb.\n");
304 if (NULL != ns->timeout_task)
305 {
306 GNUNET_SCHEDULER_cancel (ns->timeout_task);
307 ns->timeout_task = NULL;
308 }
309 if (! ns->failed)
310 GNUNET_TESTING_async_fail (&ns->ac);
311}
312
313
314/**
315 * @return true on success
316 */
317static bool
318send_start_messages (struct NetJailState *ns,
319 struct GNUNET_HELPER_Handle *helper)
320{
321 struct GNUNET_TESTING_Interpreter *is = ns->is;
322 const struct GNUNET_TESTING_Command *topo_cmd;
323 struct GNUNET_TESTING_CommandHelperInit *msg;
324 struct TestingSystemCount *tbc;
325 struct GNUNET_ShortHashCode *bar;
326 unsigned int num_barriers = 0;
327 size_t topo_length;
328 size_t msg_len;
329
330 topo_cmd = GNUNET_TESTING_interpreter_lookup_command (is,
331 ns->topology_cmd_label);
332 GNUNET_TESTING_get_trait_get_topology_string (topo_cmd,
333 &ns->topology_data);
334 topo_length = strlen (ns->topology_data) + 1;
335 msg_len = sizeof (*msg) + topo_length
336 + num_barriers * sizeof (struct GNUNET_ShortHashCode);
337 // FIXME: check for integer arithmetic overflow in the above code; theoretically.
338 if (msg_len > UINT16_MAX)
339 {
340 /* ask a wizzard to enhance the protocol;
341 start with gzip topology_data? multiple
342 init messages for barriers + topo data,
343 etc.*/
344 GNUNET_break (0);
345 return false;
346 }
347 msg = GNUNET_malloc (msg_len);
348 msg->header.size = htons ((uint16_t) msg_len);
349 msg->header.type = htons (GNUNET_MESSAGE_TYPE_CMDS_HELPER_INIT);
350 bar = (struct GNUNET_ShortHashCode *) &msg[1];
351 // FIXME: iterate over barriers...
352 memcpy (&bar[num_barriers],
353 ns->topology_data,
354 topo_length);
355 tbc = GNUNET_new (struct TestingSystemCount);
356 tbc->ns = ns;
357 tbc->shandle = GNUNET_HELPER_send (
358 helper,
359 &msg->header,
360 GNUNET_NO,
361 &clear_msg,
362 tbc);
363 GNUNET_free (msg);
364 if (NULL == tbc->shandle)
365 {
366 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
367 "Send handle is NULL!\n");
368 GNUNET_free (tbc);
369 return false;
370 }
371 GNUNET_CONTAINER_DLL_insert (ns->tbc_head,
372 ns->tbc_tail,
373 tbc);
374 return true;
375}
376
377
378/**
379 * Function which start a single helper process.
380 * @return true on success
381 */
382static bool
383start_helper (struct NetJailState *ns,
384 unsigned int script_num)
385{
386 char *gnunet_cmds_helper
387 = GNUNET_OS_get_libexec_binary_path (HELPER_CMDS_BINARY);
388 char node_id[32];
389 char *const script_argv[] = {
390 "ip",
391 "netns",
392 "exec",
393 node_id,
394 gnunet_cmds_helper,
395 node_id,
396 NULL
397 };
398 struct GNUNET_HELPER_Handle *helper;
399
400 GNUNET_snprintf (node_id,
401 sizeof (node_id),
402 "if%06x-%06x\n",
403 (unsigned int) getpid (),
404 script_num);
405 helper = GNUNET_HELPER_start (
406 GNUNET_YES, /* with control pipe */
407 script_argv[0],
408 script_argv,
409 &helper_mst,
410 &exp_cb,
411 ns);
412 GNUNET_free (gnunet_cmds_helper);
413 if (NULL == helper)
414 {
415 GNUNET_break (0);
416 return false;
417 }
418 GNUNET_array_append (ns->helpers,
419 ns->n_helpers,
420 helper);
421 GNUNET_TESTING_add_netjail_helper_ (ns->is,
422 helper);
423 return send_start_messages (ns,
424 helper);
425}
426
427
428/**
429 * Function run when the cmd terminates (good or bad) with timeout.
430 *
431 * @param cls the interpreter state
432 */
433static void
434do_timeout (void *cls)
435{
436 struct NetJailState *ns = cls;
437
438 ns->timeout_task = NULL;
439 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
440 "Terminating cmd due to global timeout\n");
441 GNUNET_TESTING_async_finish (&ns->ac);
442}
443
444
445/**
446 * This function starts a helper process for each node.
447 *
448 * @param cls closure.
449 * @param cmd CMD being run.
450 * @param is interpreter state.
451 */
452static void
453netjail_exec_run (void *cls,
454 struct GNUNET_TESTING_Interpreter *is)
455{
456 struct NetJailState *ns = cls;
457 struct GNUNET_TESTING_NetjailTopology *topology;
458 bool failed = false;
459
460 ns->is = is;
461 topology
462 = GNUNET_TESTING_get_topo_from_string_ (ns->topology_data);
463 for (unsigned int i = 1; i <= topology->total; i++)
464 {
465 if (! start_helper (ns,
466 i))
467 {
468 failed = true;
469 break;
470 }
471 }
472 GNUNET_TESTING_free_topology (topology);
473 if (failed)
474 {
475 ns->failed = true;
476 GNUNET_TESTING_interpreter_fail (ns->is);
477 return;
478 }
479 ns->timeout_task
480 = GNUNET_SCHEDULER_add_delayed (ns->timeout,
481 &do_timeout,
482 ns);
483}
484
485
486/**
487 * Code to clean up resource this cmd used.
488 *
489 * @param cls closure
490 */
491static void
492netjail_exec_cleanup (void *cls)
493{
494 struct NetJailState *ns = cls;
495
496 if (NULL != ns->timeout_task)
497 {
498 GNUNET_SCHEDULER_cancel (ns->timeout_task);
499 ns->timeout_task = NULL;
500 }
501 for (unsigned int i = 0; i<ns->n_helpers; i++)
502 GNUNET_HELPER_stop (ns->helpers[i],
503 GNUNET_YES);
504 GNUNET_free (ns);
505}
506
507
508/**
509 * This function prepares an array with traits.
510 */
511static enum GNUNET_GenericReturnValue
512netjail_exec_traits (void *cls,
513 const void **ret,
514 const char *trait,
515 unsigned int index)
516{
517 struct NetJailState *ns = cls;
518 struct GNUNET_TESTING_Trait traits[] = {
519 GNUNET_TESTING_trait_end ()
520 };
521
522 (void) ns;
523 return GNUNET_TESTING_get_trait (traits,
524 ret,
525 trait,
526 index);
527}
528
529
530/**
531 * Create command.
532 *
533 * @param label Name for the command.
534 * @param topology_data topology data
535 * @param timeout Before this timeout is reached this cmd MUST finish.
536 * @return command.
537 */
538struct GNUNET_TESTING_Command
539GNUNET_TESTING_cmd_netjail_start_helpers (
540 const char *label,
541 const char *topology_cmd_label,
542 struct GNUNET_TIME_Relative timeout)
543{
544 struct NetJailState *ns;
545
546 ns = GNUNET_new (struct NetJailState);
547 ns->topology_cmd_label = topology_cmd_label;
548 ns->timeout = timeout;
549 return GNUNET_TESTING_command_new_ac (ns,
550 label,
551 &netjail_exec_run,
552 &netjail_exec_cleanup,
553 &netjail_exec_traits,
554 &ns->ac);
555}
diff --git a/src/lib/testing/testing_api_loop.c b/src/lib/testing/testing_api_loop.c
new file mode 100644
index 000000000..a22e0c3c9
--- /dev/null
+++ b/src/lib/testing/testing_api_loop.c
@@ -0,0 +1,901 @@
1/*
2 This file is part of GNUnet
3 Copyright (C) 2021-2023 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 testing/testing_api_loop.c
23 * @brief main interpreter loop for testcases
24 * @author Christian Grothoff (GNU Taler testing)
25 * @author Marcello Stanisci (GNU Taler testing)
26 * @author t3sserakt
27 */
28#include "platform.h"
29#include "gnunet_util_lib.h"
30#include "gnunet_testing_lib.h"
31#include "testing_api_loop.h"
32#include "testing_api_cmd_batch.h"
33#include "netjail.h"
34#include "testing_cmds.h"
35
36
37struct SendContext
38{
39 struct SendContext *next;
40 struct SendContext *prev;
41
42 /**
43 * Handle to a send op
44 */
45 struct GNUNET_HELPER_SendHandle *send_handle;
46
47 struct GNUNET_TESTING_Interpreter *is;
48};
49
50/**
51 * Global state of the interpreter, used by a command
52 * to access information about other commands.
53 */
54struct GNUNET_TESTING_Interpreter
55{
56 /**
57 * Array with handles of helper processes for communication with netjails.
58 */
59 struct GNUNET_HELPER_Handle **helpers;
60
61 /**
62 * Function to call with the test result.
63 */
64 GNUNET_TESTING_ResultCallback rc;
65
66 /**
67 * Closure for @e rc.
68 */
69 void *rc_cls;
70
71 /**
72 * Commands the interpreter will run.
73 */
74 struct GNUNET_TESTING_Command *commands;
75
76 /**
77 * Map with barriers for this loop.
78 */
79 struct GNUNET_CONTAINER_MultiShortmap *barriers;
80
81 /**
82 * Interpreter task (if one is scheduled).
83 */
84 struct GNUNET_SCHEDULER_Task *task;
85
86 /**
87 * Final task that returns the result.
88 */
89 struct GNUNET_SCHEDULER_Task *final_task;
90
91 /**
92 * Task run on timeout.
93 */
94 struct GNUNET_SCHEDULER_Task *timeout_task;
95
96 /**
97 * Hash map mapping variable names to commands.
98 */
99 struct GNUNET_CONTAINER_MultiHashMap *vars;
100
101 struct SendContext *sender_head;
102 struct SendContext *sender_tail;
103
104 /**
105 * Function to call to send messages to our parent.
106 */
107 GNUNET_TESTING_cmd_helper_write_cb parent_writer;
108
109 /**
110 * Number of GNUNET_TESTING_Command in @e commands.
111 */
112 unsigned int cmds_n;
113
114 /**
115 * Size of the array @e helpers.
116 */
117 unsigned int n_helpers;
118
119 /**
120 * Instruction pointer. Tells #interpreter_run() which instruction to run
121 * next. Need (signed) int because it gets -1 when rewinding the
122 * interpreter to the first CMD.
123 */
124 int ip;
125
126 /**
127 * Result of the testcases, #GNUNET_OK on success
128 */
129 enum GNUNET_GenericReturnValue result;
130
131 /**
132 * Is the interpreter finishing?
133 */
134 bool finishing;
135
136};
137
138
139/**
140 * Lookup command by label.
141 *
142 * @param is interpreter to lookup command in
143 * @param label label of the command to lookup.
144 * @param future true to look into the future, false to look into the past
145 * @return the command, if it is found, or NULL.
146 */
147static const struct GNUNET_TESTING_Command *
148get_command (struct GNUNET_TESTING_Interpreter *is,
149 const char *label,
150 bool future)
151{
152 int start_i = future ? is->cmds_n - 1 : is->ip;
153 int end_i = future ? is->ip + 1 : 0;
154
155 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
156 "start_i: %u end_i: %u\n",
157 start_i,
158 end_i);
159 if (NULL == label)
160 {
161 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
162 "Attempt to lookup command for empty label\n");
163 return NULL;
164 }
165 for (int i = start_i; i >= end_i; i--)
166 {
167 const struct GNUNET_TESTING_Command *cmd = &is->commands[i];
168
169 if (NULL != cmd->run)
170 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
171 "label to compare %s\n",
172 cmd->label.value);
173 /* Give precedence to top-level commands. */
174 if ( (NULL != cmd->run) &&
175 (0 == strcmp (cmd->label.value,
176 label)) )
177 return cmd;
178
179 if (GNUNET_TESTING_cmd_is_batch_ (cmd))
180 {
181 struct GNUNET_TESTING_Command **batch;
182 struct GNUNET_TESTING_Command *current;
183 const struct GNUNET_TESTING_Command *icmd;
184 const struct GNUNET_TESTING_Command *match;
185
186 current = GNUNET_TESTING_cmd_batch_get_current_ (cmd);
187 GNUNET_assert (GNUNET_OK ==
188 GNUNET_TESTING_get_trait_batch_cmds (cmd,
189 &batch));
190 /* We must do the loop forward, but we can find the last match */
191 match = NULL;
192 for (unsigned int j = 0;
193 NULL != (icmd = &(*batch)[j])->run;
194 j++)
195 {
196 if (current == icmd)
197 break; /* do not go past current command */
198 if ( (NULL != icmd->run) &&
199 (0 == strcmp (icmd->label.value,
200 label)) )
201 match = icmd;
202 }
203 if (NULL != match)
204 return match;
205 }
206 }
207 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
208 "Command `%s' not found\n",
209 label);
210 return NULL;
211}
212
213
214const struct GNUNET_TESTING_Command *
215GNUNET_TESTING_interpreter_lookup_future_command (
216 struct GNUNET_TESTING_Interpreter *is,
217 const char *label)
218{
219 return get_command (is,
220 label,
221 true);
222}
223
224
225const struct GNUNET_TESTING_Command *
226GNUNET_TESTING_interpreter_lookup_command (
227 struct GNUNET_TESTING_Interpreter *is,
228 const char *label)
229{
230 return get_command (is,
231 label,
232 false);
233}
234
235
236const struct GNUNET_TESTING_Command *
237GNUNET_TESTING_interpreter_lookup_command_all (
238 struct GNUNET_TESTING_Interpreter *is,
239 const char *label)
240{
241 const struct GNUNET_TESTING_Command *cmd;
242
243 cmd = get_command (is,
244 label,
245 false);
246 if (NULL == cmd)
247 cmd = get_command (is,
248 label,
249 true);
250 return cmd;
251}
252
253
254const struct GNUNET_TESTING_Command *
255GNUNET_TESTING_interpreter_get_command (
256 struct GNUNET_TESTING_Interpreter *is,
257 const char *name)
258{
259 const struct GNUNET_TESTING_Command *cmd;
260 struct GNUNET_HashCode h_name;
261
262 GNUNET_CRYPTO_hash (name,
263 strlen (name),
264 &h_name);
265 cmd = GNUNET_CONTAINER_multihashmap_get (is->vars,
266 &h_name);
267 if (NULL == cmd)
268 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
269 "Command not found by name: %s\n",
270 name);
271 return cmd;
272}
273
274
275struct GNUNET_TESTING_Command
276GNUNET_TESTING_cmd_set_var (const char *name,
277 struct GNUNET_TESTING_Command cmd)
278{
279 cmd.name = name;
280 return cmd;
281}
282
283
284static void
285send_finished (void *cls,
286 enum GNUNET_GenericReturnValue result)
287{
288 struct SendContext *sctx = cls;
289 struct GNUNET_TESTING_Interpreter *is = sctx->is;
290
291 GNUNET_break (GNUNET_OK == result);
292 GNUNET_CONTAINER_DLL_remove (is->sender_head,
293 is->sender_tail,
294 sctx);
295 GNUNET_free (sctx);
296}
297
298
299void
300GNUNET_TESTING_loop_notify_children_ (struct GNUNET_TESTING_Interpreter *is,
301 const struct GNUNET_MessageHeader *hdr)
302{
303 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
304 "Send notification to children of type %u\n",
305 (unsigned int) ntohs (hdr->type));
306 for (unsigned int i = 0; i<is->n_helpers; i++)
307 {
308 struct SendContext *sctx;
309
310 sctx = GNUNET_new (struct SendContext);
311 sctx->is = is;
312 GNUNET_CONTAINER_DLL_insert (is->sender_head,
313 is->sender_tail,
314 sctx);
315 sctx->send_handle
316 = GNUNET_HELPER_send (is->helpers[i],
317 hdr,
318 false, /* never drop */
319 &send_finished,
320 sctx);
321 }
322}
323
324
325void
326GNUNET_TESTING_loop_notify_parent_ (struct GNUNET_TESTING_Interpreter *is,
327 const struct GNUNET_MessageHeader *hdr)
328{
329 if (NULL == is->parent_writer)
330 {
331 /* We have no parent, this is impossible! */
332 GNUNET_break (0);
333 GNUNET_TESTING_interpreter_fail (is);
334 return;
335 }
336 is->parent_writer (hdr);
337}
338
339
340/**
341 * Finish the test run, return the final result.
342 *
343 * @param cls the `struct GNUNET_TESTING_Interpreter`
344 */
345static void
346finish_test (void *cls)
347{
348 struct GNUNET_TESTING_Interpreter *is = cls;
349 struct GNUNET_TESTING_Command *cmd;
350 const char *label;
351
352 is->finishing = true;
353 is->final_task = NULL;
354 label = is->commands[is->ip].label.value;
355 if (NULL == is->commands[is->ip].run)
356 label = "END";
357 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
358 "Interpreter finishes at `%s' with status %d\n",
359 label,
360 is->result);
361 for (unsigned int j = 0;
362 NULL != (cmd = &is->commands[j])->run;
363 j++)
364 {
365 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
366 "Cleaning up cmd %s\n",
367 cmd->label.value);
368 cmd->cleanup (cmd->cls);
369 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
370 "Cleaned up cmd %s\n",
371 cmd->label.value);
372 }
373 if (NULL != is->task)
374 {
375 GNUNET_SCHEDULER_cancel (is->task);
376 is->task = NULL;
377 }
378 if (NULL != is->timeout_task)
379 {
380 GNUNET_SCHEDULER_cancel (is->timeout_task);
381 is->timeout_task = NULL;
382 }
383 {
384 struct SendContext *sctx;
385
386 while (NULL != (sctx = is->sender_head))
387 {
388 GNUNET_CONTAINER_DLL_remove (is->sender_head,
389 is->sender_tail,
390 sctx);
391 GNUNET_HELPER_send_cancel (sctx->send_handle);
392 GNUNET_free (sctx);
393 }
394 }
395 GNUNET_free (is->commands);
396 is->rc (is->rc_cls,
397 is->result);
398 if (NULL != is->barriers)
399 {
400 GNUNET_CONTAINER_multishortmap_destroy (is->barriers);
401 is->barriers = NULL;
402 }
403 if (NULL != is->vars)
404 {
405 GNUNET_CONTAINER_multihashmap_destroy (is->vars);
406 is->vars = NULL;
407 }
408 GNUNET_free (is->helpers);
409 GNUNET_free (is);
410}
411
412
413/**
414 * Run the main interpreter loop that performs exchange operations.
415 *
416 * @param cls contains the `struct InterpreterState`
417 */
418static void
419interpreter_run (void *cls);
420
421
422/**
423 * Current command is done, run the next one.
424 */
425static void
426interpreter_next (void *cls)
427{
428 struct GNUNET_TESTING_Interpreter *is = cls;
429 static unsigned long long ipc;
430 static struct GNUNET_TIME_Absolute last_report;
431 struct GNUNET_TESTING_Command *cmd = &is->commands[is->ip];
432
433 if (GNUNET_SYSERR == is->result)
434 return; /* ignore, we already failed! */
435 cmd->finish_time = GNUNET_TIME_absolute_get ();
436 if ( (! GNUNET_TESTING_cmd_is_batch_ (cmd)) ||
437 (! GNUNET_TESTING_cmd_batch_next_ (cmd->cls)) )
438 is->ip++;
439 if (0 == (ipc % 1000))
440 {
441 if (0 != ipc)
442 GNUNET_log (GNUNET_ERROR_TYPE_MESSAGE,
443 "Interpreter executed 1000 instructions in %s\n",
444 GNUNET_STRINGS_relative_time_to_string (
445 GNUNET_TIME_absolute_get_duration (last_report),
446 true));
447 last_report = GNUNET_TIME_absolute_get ();
448 }
449 ipc++;
450 is->task = GNUNET_SCHEDULER_add_now (&interpreter_run,
451 is);
452}
453
454
455void
456GNUNET_TESTING_interpreter_fail (struct GNUNET_TESTING_Interpreter *is)
457{
458 struct GNUNET_TESTING_Command *cmd
459 = &is->commands[is->ip];
460
461 if (GNUNET_SYSERR == is->result)
462 {
463 GNUNET_break (0);
464 return; /* ignore, we already failed! */
465 }
466 if (NULL != cmd)
467 {
468 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
469 "Failed during command `%s'\n",
470 cmd->label.value);
471 while (GNUNET_TESTING_cmd_is_batch_ (cmd))
472 {
473 cmd = GNUNET_TESTING_cmd_batch_get_current_ (cmd);
474 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
475 "Failed in batch during command `%s'\n",
476 cmd->label.value);
477 }
478 }
479 else
480 {
481 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
482 "Failed with CMD being NULL!\n");
483 }
484 is->result = GNUNET_SYSERR;
485 GNUNET_assert (NULL == is->final_task);
486 is->final_task = GNUNET_SCHEDULER_add_now (&finish_test,
487 is);
488}
489
490
491void
492GNUNET_TESTING_async_fail (struct GNUNET_TESTING_AsyncContext *ac)
493{
494 GNUNET_assert (GNUNET_NO == ac->finished);
495 ac->finished = GNUNET_SYSERR;
496 GNUNET_TESTING_interpreter_fail (ac->is);
497}
498
499
500void
501GNUNET_TESTING_async_finish (struct GNUNET_TESTING_AsyncContext *ac)
502{
503 GNUNET_assert (GNUNET_NO == ac->finished);
504 ac->finished = GNUNET_OK;
505 if (NULL != ac->notify_finished)
506 {
507 ac->notify_finished (ac->notify_finished_cls);
508 ac->notify_finished = NULL;
509 }
510 if (! ac->next_called)
511 {
512 ac->next_called = true;
513 interpreter_next (ac->is);
514 }
515}
516
517
518/**
519 * Run the main interpreter loop.
520 *
521 * @param cls contains the `struct GNUNET_TESTING_Interpreter`
522 */
523static void
524interpreter_run (void *cls)
525{
526 struct GNUNET_TESTING_Interpreter *is = cls;
527 struct GNUNET_TESTING_Command *cmd = &is->commands[is->ip];
528
529 is->task = NULL;
530 if (NULL == cmd->run)
531 {
532 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
533 "Running command END\n");
534 is->result = GNUNET_OK;
535 finish_test (is);
536 return;
537 }
538 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
539 "Running command `%s'\n",
540 cmd->label.value);
541 cmd->last_req_time
542 = GNUNET_TIME_absolute_get ();
543 if (0 == cmd->num_tries)
544 cmd->start_time = cmd->last_req_time;
545 cmd->num_tries = 1;
546 if (NULL != cmd->name)
547 {
548 struct GNUNET_HashCode h_name;
549
550 GNUNET_CRYPTO_hash (cmd->name,
551 strlen (cmd->name),
552 &h_name);
553 (void) GNUNET_CONTAINER_multihashmap_put (
554 is->vars,
555 &h_name,
556 cmd,
557 GNUNET_CONTAINER_MULTIHASHMAPOPTION_REPLACE);
558 }
559 if (NULL != cmd->ac)
560 {
561 cmd->ac->is = is;
562 cmd->ac->finished = GNUNET_NO;
563 }
564 cmd->run (cmd->cls,
565 is);
566 if ( (NULL == cmd->ac) ||
567 (cmd->asynchronous_finish) )
568 {
569 if (NULL != cmd->ac)
570 cmd->ac->next_called = true;
571 interpreter_next (is);
572 }
573}
574
575
576/**
577 * Function run when the test terminates (good or bad) with timeout.
578 *
579 * @param cls the interpreter state
580 */
581static void
582do_timeout (void *cls)
583{
584 struct GNUNET_TESTING_Interpreter *is = cls;
585
586 is->timeout_task = NULL;
587 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
588 "Terminating test due to global timeout\n");
589 is->result = GNUNET_SYSERR;
590 finish_test (is);
591}
592
593
594static void
595setup_is (struct GNUNET_TESTING_Interpreter *is,
596 const struct GNUNET_TESTING_Command *bcommand,
597 const struct GNUNET_TESTING_Command *commands)
598{
599 unsigned int i;
600
601 is->vars = GNUNET_CONTAINER_multihashmap_create (1024,
602 false);
603 /* get the number of commands */
604 for (i = 0; NULL != commands[i].run; i++)
605 ;
606 if (NULL != bcommand)
607 i++;
608 is->cmds_n = i + 1;
609 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
610 "Got %u commands\n",
611 i);
612 is->commands = GNUNET_malloc_large (
613 (i + 1)
614 * sizeof (struct GNUNET_TESTING_Command));
615 GNUNET_assert (NULL != is->commands);
616 if (NULL == bcommand)
617 {
618 memcpy (is->commands,
619 commands,
620 sizeof (struct GNUNET_TESTING_Command) * i);
621 }
622 else
623 {
624 is->commands[0] = *bcommand;
625 memcpy (&is->commands[1],
626 commands,
627 sizeof (struct GNUNET_TESTING_Command) * i);
628 }
629 is->task = GNUNET_SCHEDULER_add_now (&interpreter_run,
630 is);
631}
632
633
634struct GNUNET_TESTING_Interpreter *
635GNUNET_TESTING_run (const struct GNUNET_TESTING_Command *commands,
636 struct GNUNET_TIME_Relative timeout,
637 GNUNET_TESTING_ResultCallback rc,
638 void *rc_cls)
639{
640 struct GNUNET_TESTING_Interpreter *is;
641
642 is = GNUNET_new (struct GNUNET_TESTING_Interpreter);
643 is->timeout_task
644 = GNUNET_SCHEDULER_add_delayed (timeout,
645 &do_timeout,
646 is);
647 is->rc = rc;
648 is->rc_cls = rc_cls;
649 setup_is (is,
650 NULL,
651 commands);
652 return is;
653}
654
655
656static struct GNUNET_TESTING_Interpreter *
657start_testcase (
658 void *cls,
659 const char *topology_data,
660 uint32_t inherited_barrier_count,
661 const struct GNUNET_ShortHashCode *inherited_barriers,
662 GNUNET_TESTING_cmd_helper_write_cb parent_writer,
663 GNUNET_TESTING_cmd_helper_finish_cb finish_cb)
664{
665 const struct GNUNET_TESTING_Command *commands = cls;
666 struct GNUNET_TESTING_Interpreter *is;
667
668 is = GNUNET_new (struct GNUNET_TESTING_Interpreter);
669 if (0 != inherited_barrier_count)
670 {
671 is->barriers
672 = GNUNET_CONTAINER_multishortmap_create (inherited_barrier_count * 4 / 3,
673 true);
674 for (unsigned int j = 0; j<inherited_barrier_count; j++)
675 {
676 struct GNUNET_TESTING_Barrier *barrier;
677
678 barrier = GNUNET_new (struct GNUNET_TESTING_Barrier);
679 barrier->barrier_id = inherited_barriers[j];
680 barrier->inherited = true;
681 (void) GNUNET_CONTAINER_multishortmap_put (
682 is->barriers,
683 &barrier->barrier_id,
684 barrier,
685 GNUNET_CONTAINER_MULTIHASHMAPOPTION_REPLACE);
686 }
687 }
688 is->parent_writer = parent_writer;
689 is->rc = finish_cb;
690 is->rc_cls = NULL;
691 {
692 struct GNUNET_TESTING_Command bcmd;
693
694 bcmd = GNUNET_TESTING_cmd_set_var (
695 "topology",
696 GNUNET_TESTING_cmd_netjail_topology_from_data (
697 "_boot_",
698 topology_data));
699 setup_is (is,
700 &bcmd,
701 commands);
702 }
703 return is;
704
705}
706
707
708struct GNUNET_TESTING_PluginFunctions *
709GNUNET_TESTING_make_plugin (
710 const struct GNUNET_TESTING_Command *commands)
711{
712 struct GNUNET_TESTING_PluginFunctions *api;
713
714 api = GNUNET_new (struct GNUNET_TESTING_PluginFunctions);
715 api->cls = (void *) commands;
716 api->start_testcase = &start_testcase;
717 return api;
718}
719
720
721struct GNUNET_TESTING_Command
722GNUNET_TESTING_command_new_ac (
723 void *cls,
724 const char *label,
725 GNUNET_TESTING_CommandRunRoutine run,
726 GNUNET_TESTING_CommandCleanupRoutine cleanup,
727 GNUNET_TESTING_CommandGetTraits traits,
728 struct GNUNET_TESTING_AsyncContext *ac)
729{
730 struct GNUNET_TESTING_Command cmd = {
731 .cls = cls,
732 .run = run,
733 .ac = ac,
734 .cleanup = cleanup,
735 .traits = traits
736 };
737
738 GNUNET_assert (NULL != run);
739 if (NULL != label)
740 GNUNET_TESTING_set_label (&cmd.label,
741 label);
742 return cmd;
743}
744
745
746void
747GNUNET_TESTING_set_label (struct GNUNET_TESTING_CommandLabel *label,
748 const char *value)
749{
750 size_t len;
751
752 len = strlen (value);
753 GNUNET_assert (len <=
754 GNUNET_TESTING_CMD_MAX_LABEL_LENGTH);
755 memcpy (label->value,
756 value,
757 len + 1);
758}
759
760
761struct GNUNET_TESTING_Command
762GNUNET_TESTING_cmd_end (void)
763{
764 struct GNUNET_TESTING_Command cmd = {
765 .run = NULL
766 };
767
768 return cmd;
769}
770
771
772/**
773 * Closure for #loop_run().
774 */
775struct MainParams
776{
777
778 /**
779 * NULL-label terminated array of commands.
780 */
781 struct GNUNET_TESTING_Command *commands;
782
783 /**
784 * Global timeout for the test.
785 */
786 struct GNUNET_TIME_Relative timeout;
787
788 /**
789 * Set to #EXIT_FAILURE on error.
790 */
791 int rv;
792};
793
794
795/**
796 * Function called with the final result of the test.
797 *
798 * @param cls the `struct MainParams`
799 * @param rv #GNUNET_OK if the test passed
800 */
801static void
802handle_result (void *cls,
803 enum GNUNET_GenericReturnValue rv)
804{
805 struct MainParams *mp = cls;
806
807 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
808 "Test exits with status %d\n",
809 rv);
810 if (GNUNET_OK != rv)
811 mp->rv = EXIT_FAILURE;
812 GNUNET_SCHEDULER_shutdown ();
813}
814
815
816/**
817 * Main function to run the test cases.
818 *
819 * @param cls a `struct MainParams *`
820 */
821static void
822loop_run (void *cls)
823{
824 struct MainParams *mp = cls;
825
826 GNUNET_TESTING_run (mp->commands,
827 mp->timeout,
828 &handle_result,
829 mp);
830}
831
832
833int
834GNUNET_TESTING_main (struct GNUNET_TESTING_Command *commands,
835 struct GNUNET_TIME_Relative timeout)
836{
837 struct MainParams mp = {
838 .commands = commands,
839 .timeout = timeout,
840 .rv = EXIT_SUCCESS
841 };
842
843 GNUNET_SCHEDULER_run (&loop_run,
844 &mp);
845 return mp.rv;
846}
847
848
849void
850GNUNET_TESTING_add_netjail_helper_ (struct GNUNET_TESTING_Interpreter *is,
851 struct GNUNET_HELPER_Handle *helper)
852{
853 GNUNET_array_append (is->helpers,
854 is->n_helpers,
855 helper);
856}
857
858
859struct GNUNET_TESTING_Barrier *
860GNUNET_TESTING_get_barrier2_ (struct GNUNET_TESTING_Interpreter *is,
861 const struct GNUNET_ShortHashCode *create_key)
862{
863 return GNUNET_CONTAINER_multishortmap_get (is->barriers,
864 create_key);
865}
866
867
868struct GNUNET_TESTING_Barrier *
869GNUNET_TESTING_get_barrier_ (struct GNUNET_TESTING_Interpreter *is,
870 const char *barrier_name)
871{
872 struct GNUNET_ShortHashCode create_key;
873
874 if (NULL == is->barriers)
875 return NULL;
876 GNUNET_TESTING_barrier_name_hash_ (barrier_name,
877 &create_key);
878 return GNUNET_TESTING_get_barrier2_ (is,
879 &create_key);
880}
881
882
883void
884GNUNET_TESTING_add_barrier_ (struct GNUNET_TESTING_Interpreter *is,
885 struct GNUNET_TESTING_Barrier *barrier)
886{
887 if (NULL == is->barriers)
888 is->barriers
889 = GNUNET_CONTAINER_multishortmap_create (1,
890 true);
891 /* We always use the barrier we encountered
892 most recently under a given label, thus replace */
893 (void) GNUNET_CONTAINER_multishortmap_put (
894 is->barriers,
895 &barrier->barrier_id,
896 barrier,
897 GNUNET_CONTAINER_MULTIHASHMAPOPTION_REPLACE);
898}
899
900
901/* end of testing_api_loop.c */
diff --git a/src/lib/testing/testing_api_loop.h b/src/lib/testing/testing_api_loop.h
new file mode 100644
index 000000000..2d57cea97
--- /dev/null
+++ b/src/lib/testing/testing_api_loop.h
@@ -0,0 +1,141 @@
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/**
22 * @file testing_api_loop.h
23 * @brief
24 * @author t3sserakt
25 */
26#ifndef TESTING_API_LOOP_H
27#define TESTING_API_LOOP_H
28
29
30#include "barrier.h"
31
32
33/**
34 * Callback function to write messages from the helper process running on a netjail node to the master process.
35 *
36 * @param message The message to write.
37 */
38typedef void
39(*GNUNET_TESTING_cmd_helper_write_cb) (
40 const struct GNUNET_MessageHeader *message);
41
42/**
43 * Callback function which writes a message from the helper process running on a netjail node to the master process * signaling that the test case running on the netjail node finished.
44 */
45typedef void
46(*GNUNET_TESTING_cmd_helper_finish_cb) (
47 enum GNUNET_GenericReturnValue status);
48
49
50/**
51 * The plugin API every test case plugin has to implement.
52 */
53struct GNUNET_TESTING_PluginFunctions
54{
55
56 /**
57 * Closure to pass to start_testcase.
58 */
59 void *cls;
60
61 /**
62 * Function to be implemented for each test case plugin which starts the test case on a netjail node.
63 *
64 * @param topology_data A file name for the file containing the topology configuration, or a string containing
65 * the topology configuration.
66 * @param barrier_count length of the @a barriers array
67 * @param barriers inherited barriers
68 * @param write_message Callback function to write messages from the helper process running on a
69 * netjail node to the master process.
70 * @param finish_cb Callback function which writes a message from the helper process running on a netjail
71 * node to the master process * signaling that the test case running on the netjail node finished.
72 * @return Returns The struct GNUNET_TESTING_Interpreter of the command loop running on this netjail node.
73 */
74 struct GNUNET_TESTING_Interpreter *
75 (*start_testcase) (
76 void *cls,
77 const char *topology_data,
78 uint32_t barrier_count,
79 const struct GNUNET_ShortHashCode *barriers,
80 GNUNET_TESTING_cmd_helper_write_cb write_message,
81 GNUNET_TESTING_cmd_helper_finish_cb finish_cb);
82
83};
84
85
86/**
87 * Send message to our parent. Fails very hard if
88 * we do not have one.
89 *
90 * @param is The interpreter loop.
91 */
92void
93GNUNET_TESTING_loop_notify_parent_ (struct GNUNET_TESTING_Interpreter *is,
94 const struct GNUNET_MessageHeader *hdr);
95
96
97/**
98 * Send message to all netjail children (if there
99 * are any).
100 *
101 * @param is The interpreter loop.
102 */
103void
104GNUNET_TESTING_loop_notify_children_ (struct GNUNET_TESTING_Interpreter *is,
105 const struct GNUNET_MessageHeader *hdr);
106
107
108/**
109 * Adding a helper handle to the interpreter.
110 *
111 * @param is The interpreter.
112 * @param helper The helper handle.
113 */
114void
115GNUNET_TESTING_add_netjail_helper_ (struct GNUNET_TESTING_Interpreter *is,
116 struct GNUNET_HELPER_Handle *helper);
117
118
119/**
120 * Add a barrier to the interpreter to share it with
121 * all children as an inherited barrier.
122 *
123 * @param is The interpreter.
124 * @param barrier The barrier to add.
125 */
126void
127GNUNET_TESTING_add_barrier_ (struct GNUNET_TESTING_Interpreter *is,
128 struct GNUNET_TESTING_Barrier *barrier);
129
130
131struct GNUNET_TESTING_Barrier *
132GNUNET_TESTING_get_barrier2_ (struct GNUNET_TESTING_Interpreter *is,
133 const struct GNUNET_ShortHashCode *create_key);
134
135
136struct GNUNET_TESTING_Barrier *
137GNUNET_TESTING_get_barrier_ (struct GNUNET_TESTING_Interpreter *is,
138 const char *barrier_name);
139
140
141#endif
diff --git a/src/lib/testing/testing_api_traits.c b/src/lib/testing/testing_api_traits.c
new file mode 100644
index 000000000..0b20b6359
--- /dev/null
+++ b/src/lib/testing/testing_api_traits.c
@@ -0,0 +1,87 @@
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 testing/testing_api_traits.c
23 * @brief loop for trait resolution
24 * @author Christian Grothoff (GNU Taler testing)
25 * @author Marcello Stanisci (GNU Taler testing)
26 * @author t3sserakt
27 */
28#include "platform.h"
29#include "gnunet_testing_lib.h"
30
31
32GNUNET_TESTING_LOOP_SIMPLE_TRAITS (GNUNET_TESTING_MAKE_IMPL_SIMPLE_TRAIT,
33 GNUNET_TESTING)
34
35GNUNET_TESTING_LOOP_INDEXED_TRAITS (GNUNET_TESTING_MAKE_IMPL_INDEXED_TRAIT,
36 GNUNET_TESTING)
37
38/**
39 * End a trait array. Usually, commands offer several traits,
40 * and put them in arrays.
41 */
42struct GNUNET_TESTING_Trait
43GNUNET_TESTING_trait_end ()
44{
45 struct GNUNET_TESTING_Trait end = {
46 .index = 0,
47 .trait_name = NULL,
48 .ptr = NULL
49 };
50
51 return end;
52}
53
54
55/**
56 * Pick the chosen trait from the traits array.
57 *
58 * @param traits the traits array.
59 * @param ret where to store the result.
60 * @param trait type of the trait to extract.
61 * @param index index number of the object to extract.
62 * @return #GNUNET_OK if no error occurred, #GNUNET_SYSERR otherwise.
63 */
64enum GNUNET_GenericReturnValue
65GNUNET_TESTING_get_trait (const struct GNUNET_TESTING_Trait *traits,
66 const void **ret,
67 const char *trait,
68 unsigned int index)
69{
70 for (unsigned int i = 0; NULL != traits[i].trait_name; i++)
71 {
72 if ( (0 == strcmp (trait, traits[i].trait_name)) &&
73 (index == traits[i].index) )
74 {
75 *ret = (void *) traits[i].ptr;
76 return GNUNET_OK;
77 }
78 }
79 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
80 "Trait %s/%u not found.\n",
81 trait, index);
82
83 return GNUNET_SYSERR;
84}
85
86
87/* end of testing_api_traits.c */
diff --git a/src/lib/testing/testing_cmds.h b/src/lib/testing/testing_cmds.h
new file mode 100644
index 000000000..3493dbbf9
--- /dev/null
+++ b/src/lib/testing/testing_cmds.h
@@ -0,0 +1,106 @@
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 testing/testing_cmds.h
23 * @brief Message formats for communication between testing cmds helper and testcase plugins.
24 * @author t3sserakt
25 */
26
27#ifndef TESTING_CMDS_H
28#define TESTING_CMDS_H
29
30#define HELPER_CMDS_BINARY "gnunet-cmds-helper"
31#include "gnunet_common.h"
32
33GNUNET_NETWORK_STRUCT_BEGIN
34
35/**
36 * Initialization message for gnunet-cmds-testbed to start cmd binary.
37 */
38struct GNUNET_TESTING_CommandHelperInit
39{
40 /**
41 * Type is #GNUNET_MESSAGE_TYPE_CMDS_HELPER_INIT
42 */
43 struct GNUNET_MessageHeader header;
44
45 /**
46 * Number of barriers the helper inherits.
47 */
48 uint32_t barrier_count GNUNET_PACKED;
49
50 /* Followed by barrier_count barrier hashes */
51
52 /* Followed by topology data */
53};
54
55
56struct GNUNET_TESTING_CommandLocalFinished
57{
58 /**
59 * Type is GNUNET_MESSAGE_TYPE_CMDS_HELPER_LOCAL_FINISHED
60 */
61 struct GNUNET_MessageHeader header;
62
63 /**
64 * The exit status local test return with in NBO.
65 */
66 uint32_t rv GNUNET_PACKED;
67};
68
69
70/**
71 * Child to parent: I reached the given barrier,
72 * increment the counter (or pass to grandparent).
73 */
74struct GNUNET_TESTING_CommandBarrierReached
75{
76 struct GNUNET_MessageHeader header;
77 struct GNUNET_ShortHashCode barrier_key;
78};
79
80
81/**
82 * Parent to child: you're inheriting a barrier.
83 * If the barrier was already satisfied, the parent
84 * must sent a separate barrier satisfied message.
85 */
86struct GNUNET_TESTING_CommandBarrierInherited
87{
88 struct GNUNET_MessageHeader header;
89 struct GNUNET_ShortHashCode barrier_key;
90};
91
92
93/**
94 * Parent to child: this barrier was satisfied.
95 */
96struct GNUNET_TESTING_CommandBarrierSatisfied
97{
98 struct GNUNET_MessageHeader header;
99 struct GNUNET_ShortHashCode barrier_key;
100};
101
102
103GNUNET_NETWORK_STRUCT_END
104
105#endif
106/* end of testing_cmds.h */
diff --git a/src/lib/util/.gitignore b/src/lib/util/.gitignore
new file mode 100644
index 000000000..c31c9ec28
--- /dev/null
+++ b/src/lib/util/.gitignore
@@ -0,0 +1,82 @@
1test_common_logging_dummy
2test_bio
3test_client.nc
4test_client_unix.nc
5test_common_allocation
6test_common_endian
7test_common_logging
8test_common_logging_runtime_loglevels
9test_configuration
10test_connection.nc
11test_connection_addressing.nc
12test_connection_receive_cancel.nc
13test_connection_timeout.nc
14test_connection_timeout_no_connect.nc
15test_connection_transmit_cancel.nc
16test_container_bloomfilter
17test_container_dll
18test_container_heap
19test_container_meta_data
20test_container_multihashmap
21test_container_multihashmap32
22test_container_multipeermap
23test_crypto_blind
24test_crypto_crc
25test_crypto_ecc_dlog
26test_crypto_ecdh_ecdsa
27test_crypto_ecdh_eddsa
28test_crypto_ecdhe
29test_crypto_ecdsa
30test_crypto_eddsa
31test_crypto_edx25519
32test_crypto_elligator
33test_crypto_hash
34test_crypto_hash_context
35test_crypto_hkdf
36test_crypto_kdf
37test_crypto_paillier
38test_crypto_random
39test_crypto_rsa
40test_crypto_cs
41test_crypto_symmetric
42test_disk
43test_getopt
44test_mq
45test_os_network
46test_os_start_process
47test_peer
48test_plugin
49test_program
50test_scheduler
51test_scheduler_delay
52test_server.nc
53test_server_disconnect.nc
54test_server_mst_interrupt.nc
55test_server_with_client.nc
56test_server_with_client_unix
57test_service
58test_speedup
59test_strings
60test_strings_to_data
61test_time
62test_socks.nc
63perf_crypto_asymmetric
64perf_crypto_hash
65perf_crypto_symmetric
66perf_crypto_rsa
67perf_crypto_cs
68perf_crypto_ecc_dlog
69perf_crypto_paillier
70test_hexcoder
71test_regex
72test_tun
73test_uri
74python27_location
75perf_malloc
76perf_mq
77perf_scheduler
78test_crypto_cs
79test_crypto_ecc
80test_child_management
81test_scheduler_hogging_cancel
82test_scheduler_hogging_priority
diff --git a/src/lib/util/Makefile.am b/src/lib/util/Makefile.am
new file mode 100644
index 000000000..4d052c57b
--- /dev/null
+++ b/src/lib/util/Makefile.am
@@ -0,0 +1,608 @@
1# This Makefile.am is in the public domain
2AM_CPPFLAGS = -I$(top_srcdir)/src/include
3
4plugindir = $(libdir)/gnunet
5
6libexecdir= $(pkglibdir)/libexec/
7
8pkgcfgdir= $(pkgdatadir)/config.d/
9
10dist_pkgcfg_DATA = \
11 util.conf
12
13
14TEST_CLIENT_UNIX_NC = test_client_unix.nc
15
16if USE_COVERAGE
17 AM_CFLAGS = --coverage -O0
18 XLIB = -lgcov
19endif
20
21if ENABLE_BENCHMARK
22 BENCHMARK = benchmark.c benchmark.h
23 PTHREAD = -lpthread
24endif
25
26DLOG = crypto_ecc_dlog.c
27DLOG_TEST = test_crypto_ecc_dlog
28
29test_common_logging_dummy_SOURCES = \
30 test_common_logging_dummy.c
31test_common_logging_dummy_LDADD = \
32 libgnunetutil.la
33
34libgnunetutil_la_SOURCES = \
35 bandwidth.c \
36 $(BENCHMARK) \
37 bio.c \
38 buffer.c \
39 child_management.c \
40 client.c \
41 common_allocation.c \
42 common_endian.c \
43 common_logging.c \
44 compress.c \
45 configuration.c \
46 configuration_helper.c \
47 consttime_memcmp.c \
48 container_bloomfilter.c \
49 container_heap.c \
50 container_multihashmap.c \
51 container_multishortmap.c \
52 container_multiuuidmap.c \
53 container_multipeermap.c \
54 container_multihashmap32.c \
55 crypto_blind_sign.c \
56 crypto_crc.c \
57 crypto_cs.c \
58 crypto_ecc.c \
59 crypto_ecc_gnsrecord.c \
60 $(DLOG) \
61 crypto_ecc_setup.c \
62 crypto_edx25519.c \
63 crypto_elligator.c \
64 crypto_hash.c \
65 crypto_hash_file.c \
66 crypto_hkdf.c \
67 crypto_kdf.c \
68 crypto_mpi.c \
69 crypto_paillier.c \
70 crypto_pkey.c \
71 crypto_pow.c \
72 crypto_random.c \
73 crypto_rsa.c \
74 crypto_symmetric.c \
75 disk.c \
76 disk.h \
77 dnsparser.c \
78 dnsstub.c \
79 getopt.c \
80 getopt_helpers.c \
81 helper.c \
82 load.c \
83 mst.c \
84 mq.c \
85 nc.c \
86 network.c \
87 nt.c \
88 op.c \
89 os_installation.c \
90 os_network.c \
91 os_priority.c \
92 peer.c \
93 plugin.c \
94 program.c \
95 regex.c \
96 resolver_api.c resolver.h \
97 scheduler.c \
98 service.c \
99 signal.c \
100 strings.c \
101 time.c \
102 tun.c \
103 uri.c \
104 speedup.c speedup.h \
105 proc_compat.c \
106 gnunet_error_codes.c
107
108if HAVE_LIBATOMIC
109if DARWIN
110 LIBATOMIC=
111else
112 LIBATOMIC= -latomic
113endif
114else
115 LIBATOMIC=
116endif
117
118if HAVE_LIBIDN
119 LIBIDN= -lidn
120else
121 LIBIDN=
122endif
123
124if HAVE_LIBIDN2
125 LIBIDN2= -lidn2
126else
127 LIBIDN2=
128endif
129
130
131libgnunetutil_la_CFLAGS = \
132 ${MHD_CFLAGS}
133libgnunetutil_la_LIBADD = \
134 $(GCLIBADD) $(WINLIB) \
135 $(LIBATOMIC) \
136 $(LIBGCRYPT_LIBS) \
137 $(LTLIBICONV) \
138 $(LTLIBINTL) \
139 -lltdl \
140 $(LIBIDN) $(LIBIDN2) \
141 $(Z_LIBS) \
142 -lunistring \
143 -lsodium \
144 -lgmp \
145 $(XLIB) \
146 $(PTHREAD)
147
148libgnunetutil_la_LDFLAGS = \
149 $(GN_LIB_LDFLAGS) \
150 -version-info 16:1:0
151
152lib_LTLIBRARIES = libgnunetutil.la
153
154noinst_PROGRAMS = \
155 test_common_logging_dummy
156
157if ENABLE_TEST_RUN
158AM_TESTS_ENVIRONMENT=export GNUNET_PREFIX=$${GNUNET_PREFIX:-@libdir@};export PATH=$${GNUNET_PREFIX:-@prefix@}/bin:$$PATH;unset XDG_DATA_HOME;unset XDG_CONFIG_HOME;
159TESTS = $(check_PROGRAMS) $(check_SCRIPTS)
160endif
161
162plugin_LTLIBRARIES = \
163 libgnunet_plugin_utiltest.la
164
165libgnunet_plugin_utiltest_la_SOURCES = \
166 test_plugin_plug.c
167libgnunet_plugin_utiltest_la_LDFLAGS = \
168 $(GN_PLUGIN_LDFLAGS)
169
170if HAVE_BENCHMARKS
171 BENCHMARKS = \
172 perf_crypto_cs \
173 perf_crypto_hash \
174 perf_crypto_rsa \
175 perf_crypto_paillier \
176 perf_crypto_symmetric \
177 perf_crypto_asymmetric \
178 perf_malloc \
179 perf_mq \
180 perf_scheduler \
181 perf_crypto_ecc_dlog
182endif
183
184if HAVE_SSH_KEY
185# SSH_USING_TESTS = test_socks.nc
186endif
187
188check_PROGRAMS = \
189 test_bio \
190 test_child_management \
191 test_client.nc \
192 $(TEST_CLIENT_UNIX_NC) \
193 test_common_allocation \
194 test_common_endian \
195 test_common_logging \
196 test_configuration \
197 test_container_bloomfilter \
198 test_container_dll \
199 test_container_multihashmap \
200 test_container_multihashmap32 \
201 test_container_multipeermap \
202 test_container_heap \
203 test_crypto_blind \
204 test_crypto_crc \
205 test_crypto_cs \
206 test_crypto_ecdsa \
207 test_crypto_eddsa \
208 test_crypto_ecdhe \
209 test_crypto_ecdh_eddsa \
210 test_crypto_ecdh_ecdsa \
211 test_crypto_edx25519 \
212 test_crypto_elligator \
213 $(DLOG_TEST) \
214 test_crypto_hash \
215 test_crypto_hash_context \
216 test_crypto_hkdf \
217 test_crypto_kdf \
218 test_crypto_paillier \
219 test_crypto_random \
220 test_crypto_rsa \
221 test_crypto_symmetric \
222 test_disk \
223 test_getopt \
224 test_hexcoder \
225 test_mq \
226 test_os_network \
227 test_peer \
228 test_plugin \
229 test_program \
230 test_regex \
231 test_scheduler \
232 test_scheduler_delay \
233 test_scheduler_hogging_cancel \
234 test_scheduler_hogging_priority \
235 test_service \
236 test_strings \
237 test_strings_to_data \
238 test_speedup \
239 test_time \
240 test_tun \
241 test_uri \
242 $(BENCHMARKS) \
243 test_os_start_process \
244 test_common_logging_runtime_loglevels
245
246test_child_management_SOURCES = \
247 test_child_management.c
248test_child_management_LDADD = \
249 libgnunetutil.la \
250 $(XLIB)
251
252
253
254# Declare .nc (NO-CONCURRENCY) as a test extension so that we can impart
255# sequential execution order for them
256TEST_EXTENSIONS = .nc
257test_test_client_unix.log: test_client.log
258
259test_bio_SOURCES = \
260 test_bio.c
261test_bio_LDADD = \
262 libgnunetutil.la
263
264test_hexcoder_SOURCES = \
265 test_hexcoder.c
266test_hexcoder_LDADD = \
267 libgnunetutil.la
268
269test_tun_SOURCES = \
270 test_tun.c
271test_tun_LDADD = \
272 libgnunetutil.la
273
274test_regex_SOURCES = \
275 test_regex.c
276test_regex_LDADD = \
277 libgnunetutil.la
278
279test_os_start_process_SOURCES = \
280 test_os_start_process.c
281test_os_start_process_LDADD = \
282 libgnunetutil.la
283
284test_client_nc_SOURCES = \
285 test_client.c
286test_client_nc_LDADD = \
287 libgnunetutil.la
288
289test_client_unix_nc_SOURCES = \
290 test_client.c
291test_client_unix_nc_LDADD = \
292 libgnunetutil.la
293
294#test_socks_nc_SOURCES = \
295# test_socks.c
296#test_socks_nc_LDADD = \
297# libgnunetutil.la
298
299test_common_allocation_SOURCES = \
300 test_common_allocation.c
301test_common_allocation_LDADD = \
302 libgnunetutil.la
303
304test_common_endian_SOURCES = \
305 test_common_endian.c
306test_common_endian_LDADD = \
307 libgnunetutil.la
308
309test_common_logging_SOURCES = \
310 test_common_logging.c
311test_common_logging_LDADD = \
312 libgnunetutil.la
313
314test_common_logging_runtime_loglevels_SOURCES = \
315 test_common_logging_runtime_loglevels.c
316test_common_logging_runtime_loglevels_LDADD = \
317 libgnunetutil.la
318
319test_configuration_SOURCES = \
320 test_configuration.c
321test_configuration_LDADD = \
322 libgnunetutil.la
323
324test_container_bloomfilter_SOURCES = \
325 test_container_bloomfilter.c
326test_container_bloomfilter_LDADD = \
327 libgnunetutil.la
328
329test_container_dll_SOURCES = \
330 test_container_dll.c
331test_container_dll_LDADD = \
332 libgnunetutil.la
333
334test_container_multihashmap_SOURCES = \
335 test_container_multihashmap.c
336test_container_multihashmap_LDADD = \
337 libgnunetutil.la
338
339test_container_multihashmap32_SOURCES = \
340 test_container_multihashmap32.c
341test_container_multihashmap32_LDADD = \
342 libgnunetutil.la
343
344test_container_multipeermap_SOURCES = \
345 test_container_multipeermap.c
346test_container_multipeermap_LDADD = \
347 libgnunetutil.la
348
349test_container_heap_SOURCES = \
350 test_container_heap.c
351test_container_heap_LDADD = \
352 libgnunetutil.la
353
354test_crypto_blind_SOURCES = \
355 test_crypto_blind.c
356test_crypto_blind_LDADD = \
357 libgnunetutil.la
358
359test_crypto_symmetric_SOURCES = \
360 test_crypto_symmetric.c
361test_crypto_symmetric_LDADD = \
362 libgnunetutil.la
363
364test_crypto_crc_SOURCES = \
365 test_crypto_crc.c
366test_crypto_crc_LDADD = \
367 libgnunetutil.la
368
369test_crypto_cs_SOURCES = \
370 test_crypto_cs.c
371test_crypto_cs_LDADD = \
372 libgnunetutil.la \
373 -lsodium
374
375test_crypto_ecdsa_SOURCES = \
376 test_crypto_ecdsa.c
377test_crypto_ecdsa_LDADD = \
378 libgnunetutil.la \
379 $(LIBGCRYPT_LIBS)
380
381test_crypto_eddsa_SOURCES = \
382 test_crypto_eddsa.c
383test_crypto_eddsa_LDADD = \
384 libgnunetutil.la \
385 $(LIBGCRYPT_LIBS)
386
387test_crypto_edx25519_SOURCES = \
388 test_crypto_edx25519.c
389test_crypto_edx25519_LDADD = \
390 libgnunetutil.la \
391 $(LIBGCRYPT_LIBS)
392
393 test_crypto_elligator_SOURCES = \
394 test_crypto_elligator.c
395test_crypto_elligator_LDADD = \
396 libgnunetutil.la \
397 -lsodium \
398 $(LIBGCRYPT_LIBS)
399
400test_crypto_ecc_dlog_SOURCES = \
401 test_crypto_ecc_dlog.c
402test_crypto_ecc_dlog_LDADD = \
403 -lsodium \
404 libgnunetutil.la \
405 $(LIBGCRYPT_LIBS)
406
407test_crypto_ecdhe_SOURCES = \
408 test_crypto_ecdhe.c
409test_crypto_ecdhe_LDADD = \
410 libgnunetutil.la \
411 $(LIBGCRYPT_LIBS)
412
413test_crypto_ecdh_eddsa_SOURCES = \
414 test_crypto_ecdh_eddsa.c
415test_crypto_ecdh_eddsa_LDADD = \
416 libgnunetutil.la \
417 $(LIBGCRYPT_LIBS)
418
419test_crypto_ecdh_ecdsa_SOURCES = \
420 test_crypto_ecdh_ecdsa.c
421test_crypto_ecdh_ecdsa_LDADD = \
422 libgnunetutil.la \
423 $(LIBGCRYPT_LIBS)
424
425
426test_crypto_hash_SOURCES = \
427 test_crypto_hash.c
428test_crypto_hash_LDADD = \
429 libgnunetutil.la
430
431test_crypto_hash_context_SOURCES = \
432 test_crypto_hash_context.c
433test_crypto_hash_context_LDADD = \
434 libgnunetutil.la
435
436test_crypto_hkdf_SOURCES = \
437 test_crypto_hkdf.c
438test_crypto_hkdf_LDADD = \
439 libgnunetutil.la
440
441test_crypto_kdf_SOURCES = \
442 test_crypto_kdf.c
443test_crypto_kdf_LDADD = \
444 libgnunetutil.la -lgcrypt
445
446test_crypto_paillier_SOURCES = \
447 test_crypto_paillier.c
448test_crypto_paillier_LDADD = \
449 $(LIBGCRYPT_LIBS) \
450 libgnunetutil.la
451
452test_crypto_random_SOURCES = \
453 test_crypto_random.c
454test_crypto_random_LDADD = \
455 libgnunetutil.la
456
457test_crypto_rsa_SOURCES = \
458 test_crypto_rsa.c
459test_crypto_rsa_LDADD = \
460 libgnunetutil.la -lgcrypt
461
462test_disk_SOURCES = \
463 test_disk.c
464test_disk_LDADD = \
465 libgnunetutil.la
466
467test_getopt_SOURCES = \
468 test_getopt.c
469test_getopt_LDADD = \
470 libgnunetutil.la
471
472test_mq_SOURCES = \
473 test_mq.c
474test_mq_LDADD = \
475 libgnunetutil.la
476
477test_os_network_SOURCES = \
478 test_os_network.c
479test_os_network_LDADD = \
480 libgnunetutil.la
481
482test_peer_SOURCES = \
483 test_peer.c
484test_peer_LDADD = \
485 libgnunetutil.la -lgcrypt
486
487test_plugin_SOURCES = \
488 test_plugin.c
489test_plugin_LDADD = \
490 libgnunetutil.la
491
492test_program_SOURCES = \
493 test_program.c
494test_program_LDADD = \
495 libgnunetutil.la
496
497test_scheduler_SOURCES = \
498 test_scheduler.c
499test_scheduler_LDADD = \
500 libgnunetutil.la
501
502test_scheduler_delay_SOURCES = \
503 test_scheduler_delay.c
504test_scheduler_delay_LDADD = \
505 libgnunetutil.la
506
507test_scheduler_hogging_cancel_SOURCES = \
508 test_scheduler_hogging_cancel.c
509test_scheduler_hogging_cancel_LDADD = \
510 libgnunetutil.la
511
512test_scheduler_hogging_priority_SOURCES = \
513 test_scheduler_hogging_priority.c
514test_scheduler_hogging_priority_LDADD = \
515 libgnunetutil.la
516
517test_service_SOURCES = \
518 test_service.c
519test_service_LDADD = \
520 libgnunetutil.la
521
522test_strings_SOURCES = \
523 test_strings.c
524test_strings_LDADD = \
525 libgnunetutil.la
526
527test_strings_to_data_SOURCES = \
528 test_strings_to_data.c
529test_strings_to_data_LDADD = \
530 libgnunetutil.la
531
532
533test_time_SOURCES = \
534 test_time.c
535test_time_LDADD = \
536 libgnunetutil.la
537
538test_speedup_SOURCES = \
539 test_speedup.c
540test_speedup_LDADD = \
541 libgnunetutil.la
542
543test_uri_SOURCES = \
544 test_uri.c
545test_uri_LDADD = \
546 libgnunetutil.la
547
548perf_crypto_cs_SOURCES = \
549 perf_crypto_cs.c
550perf_crypto_cs_LDADD = \
551 libgnunetutil.la
552
553perf_crypto_hash_SOURCES = \
554 perf_crypto_hash.c
555perf_crypto_hash_LDADD = \
556 libgnunetutil.la
557
558perf_crypto_ecc_dlog_SOURCES = \
559 perf_crypto_ecc_dlog.c
560perf_crypto_ecc_dlog_LDADD = \
561 libgnunetutil.la \
562 -lsodium
563
564perf_crypto_rsa_SOURCES = \
565 perf_crypto_rsa.c
566perf_crypto_rsa_LDADD = \
567 libgnunetutil.la
568
569perf_crypto_symmetric_SOURCES = \
570 perf_crypto_symmetric.c
571perf_crypto_symmetric_LDADD = \
572 libgnunetutil.la
573
574perf_crypto_asymmetric_SOURCES = \
575 perf_crypto_asymmetric.c
576perf_crypto_asymmetric_LDADD = \
577 libgnunetutil.la
578
579perf_crypto_paillier_SOURCES = \
580 perf_crypto_paillier.c
581perf_crypto_paillier_LDADD = \
582 libgnunetutil.la \
583 -lgcrypt
584
585perf_malloc_SOURCES = \
586 perf_malloc.c
587perf_malloc_LDADD = \
588 libgnunetutil.la
589
590perf_mq_SOURCES = \
591 perf_mq.c
592perf_mq_LDADD = \
593 libgnunetutil.la
594
595perf_scheduler_SOURCES = \
596 perf_scheduler.c
597perf_scheduler_LDADD = \
598 libgnunetutil.la
599
600
601EXTRA_DIST = \
602 test_client_data.conf \
603 test_client_unix.conf \
604 test_configuration_data.conf \
605 test_program_data.conf \
606 test_service_data.conf \
607 test_speedup_data.conf \
608 child_management_test.sh
diff --git a/src/lib/util/bandwidth.c b/src/lib/util/bandwidth.c
new file mode 100644
index 000000000..8411c12ee
--- /dev/null
+++ b/src/lib/util/bandwidth.c
@@ -0,0 +1,515 @@
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 util/bandwidth.c
23 * @brief functions related to bandwidth (unit)
24 * @author Christian Grothoff
25 */
26
27#include "platform.h"
28#include "gnunet_util_lib.h"
29
30
31#define LOG(kind, ...) GNUNET_log_from (kind, "util-bandwidth", __VA_ARGS__)
32
33/**
34 * Create a new bandwidth value.
35 *
36 * @param bytes_per_second value to create
37 * @return the new bandwidth value
38 */
39struct GNUNET_BANDWIDTH_Value32NBO
40GNUNET_BANDWIDTH_value_init (uint32_t bytes_per_second)
41{
42 struct GNUNET_BANDWIDTH_Value32NBO ret;
43
44 ret.value__ = htonl (bytes_per_second);
45 return ret;
46}
47
48
49/**
50 * Compute the MIN of two bandwidth values.
51 *
52 * @param b1 first value
53 * @param b2 second value
54 * @return the min of b1 and b2
55 */
56struct GNUNET_BANDWIDTH_Value32NBO
57GNUNET_BANDWIDTH_value_min (struct GNUNET_BANDWIDTH_Value32NBO b1,
58 struct GNUNET_BANDWIDTH_Value32NBO b2)
59{
60 return GNUNET_BANDWIDTH_value_init (
61 GNUNET_MIN (ntohl (b1.value__), ntohl (b2.value__)));
62}
63
64
65/**
66 * Compute the MAX of two bandwidth values.
67 *
68 * @param b1 first value
69 * @param b2 second value
70 * @return the min of b1 and b2
71 */
72struct GNUNET_BANDWIDTH_Value32NBO
73GNUNET_BANDWIDTH_value_max (struct GNUNET_BANDWIDTH_Value32NBO b1,
74 struct GNUNET_BANDWIDTH_Value32NBO b2)
75{
76 return GNUNET_BANDWIDTH_value_init (
77 GNUNET_MAX (ntohl (b1.value__), ntohl (b2.value__)));
78}
79
80
81/**
82 * Compute the SUM of two bandwidth values.
83 *
84 * @param b1 first value
85 * @param b2 second value
86 * @return the sum of b1 and b2
87 */
88struct GNUNET_BANDWIDTH_Value32NBO
89GNUNET_BANDWIDTH_value_sum (struct GNUNET_BANDWIDTH_Value32NBO b1,
90 struct GNUNET_BANDWIDTH_Value32NBO b2)
91{
92 return GNUNET_BANDWIDTH_value_init (ntohl (b1.value__) + ntohl (b2.value__));
93}
94
95
96uint64_t
97GNUNET_BANDWIDTH_value_get_available_until (
98 struct GNUNET_BANDWIDTH_Value32NBO bps,
99 struct GNUNET_TIME_Relative deadline)
100{
101 uint64_t b;
102
103 b = ntohl (bps.value__);
104 LOG (GNUNET_ERROR_TYPE_DEBUG,
105 "Bandwidth has %llu bytes available until deadline in %s\n",
106 (unsigned long long) ((b * deadline.rel_value_us + 500000LL)
107 / 1000000LL),
108 GNUNET_STRINGS_relative_time_to_string (deadline, GNUNET_YES));
109 return (b * deadline.rel_value_us + 500000LL) / 1000000LL;
110}
111
112
113/**
114 * At the given bandwidth, calculate how long it would take for
115 * @a size bytes to be transmitted.
116 *
117 * @param bps bandwidth
118 * @param size number of bytes we want to have available
119 * @return how long it would take
120 */
121struct GNUNET_TIME_Relative
122GNUNET_BANDWIDTH_value_get_delay_for (struct GNUNET_BANDWIDTH_Value32NBO bps,
123 uint64_t size)
124{
125 uint64_t b;
126 struct GNUNET_TIME_Relative ret;
127
128 b = ntohl (bps.value__);
129 if (0 == b)
130 {
131 LOG (GNUNET_ERROR_TYPE_DEBUG,
132 "Bandwidth suggests delay of infinity (zero bandwidth)\n");
133 return GNUNET_TIME_UNIT_FOREVER_REL;
134 }
135 ret.rel_value_us = size * 1000LL * 1000LL / b;
136 LOG (GNUNET_ERROR_TYPE_DEBUG,
137 "Bandwidth suggests delay of %s for %llu bytes of traffic\n",
138 GNUNET_STRINGS_relative_time_to_string (ret, GNUNET_YES),
139 (unsigned long long) size);
140 return ret;
141}
142
143
144/**
145 * Task run whenever we hit the bandwidth limit for a tracker.
146 *
147 * @param cls the `struct GNUNET_BANDWIDTH_Tracker`
148 */
149static void
150excess_trigger (void *cls)
151{
152 struct GNUNET_BANDWIDTH_Tracker *av = cls;
153
154 av->excess_task = NULL;
155 if (NULL != av->excess_cb)
156 {
157 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
158 "Notifying application about excess bandwidth\n");
159 av->excess_cb (av->excess_cb_cls);
160 }
161}
162
163
164/**
165 * Recalculate when we might need to call the excess callback.
166 */
167static void
168update_excess (struct GNUNET_BANDWIDTH_Tracker *av)
169{
170 struct GNUNET_TIME_Relative delay;
171 struct GNUNET_TIME_Absolute now;
172 uint64_t delta_time;
173 uint64_t delta_avail;
174 int64_t left_bytes;
175 uint64_t max_carry;
176 int64_t current_consumption;
177
178 if (NULL == av->excess_cb)
179 return; /* nothing to do */
180 now = GNUNET_TIME_absolute_get ();
181 delta_time = now.abs_value_us - av->last_update__.abs_value_us;
182 delta_avail =
183 (delta_time * ((unsigned long long) av->available_bytes_per_s__)
184 + 500000LL)
185 / 1000000LL;
186 current_consumption = av->consumption_since_last_update__ - delta_avail;
187 if (current_consumption > av->consumption_since_last_update__)
188 {
189 /* integer underflow, cap! */
190 current_consumption = INT64_MIN;
191 }
192 /* negative current_consumption means that we have savings */
193 max_carry = ((uint64_t) av->available_bytes_per_s__) * av->max_carry_s__;
194 if (max_carry < GNUNET_MAX_MESSAGE_SIZE)
195 max_carry = GNUNET_MAX_MESSAGE_SIZE;
196 if (max_carry > INT64_MAX)
197 max_carry = INT64_MAX;
198 left_bytes = current_consumption + max_carry;
199 if (left_bytes < current_consumption)
200 {
201 /* integer overflow, cap! */
202 left_bytes = INT64_MAX;
203 }
204 /* left_bytes now contains the number of bytes needed until
205 we have more savings than allowed */
206 if (left_bytes < 0)
207 {
208 /* having excess already */
209 delay = GNUNET_TIME_UNIT_ZERO;
210 }
211 else
212 {
213 double factor = 1.0 * left_bytes / (double) av->available_bytes_per_s__;
214 delay =
215 GNUNET_TIME_relative_saturating_multiply (GNUNET_TIME_UNIT_SECONDS,
216 (unsigned long long) factor);
217 }
218 GNUNET_log (
219 GNUNET_ERROR_TYPE_DEBUG,
220 "At %llu bps it will take us %s for %lld bytes to reach excess threshold\n",
221 (unsigned long long) av->available_bytes_per_s__,
222 GNUNET_STRINGS_relative_time_to_string (delay, GNUNET_NO),
223 (long long) left_bytes);
224 if (NULL != av->excess_task)
225 GNUNET_SCHEDULER_cancel (av->excess_task);
226 av->excess_task = GNUNET_SCHEDULER_add_delayed (delay, &excess_trigger, av);
227}
228
229
230/**
231 * Initialize bandwidth tracker. Note that in addition to the
232 * 'max_carry_s' limit, we also always allow at least
233 * #GNUNET_MAX_MESSAGE_SIZE to accumulate. So if the
234 * bytes-per-second limit is so small that within 'max_carry_s' not
235 * even #GNUNET_MAX_MESSAGE_SIZE is allowed to accumulate, it is
236 * ignored and replaced by #GNUNET_MAX_MESSAGE_SIZE (which is in
237 * bytes).
238 *
239 * To stop notifications about updates and excess callbacks use
240 * #GNUNET_BANDWIDTH_tracker_notification_stop().
241 *
242 * @param av tracker to initialize
243 * @param update_cb callback to notify a client about the tracker being updated
244 * @param update_cb_cls cls for the callback
245 * @param bytes_per_second_limit initial limit to assume
246 * @param max_carry_s maximum number of seconds unused bandwidth
247 * may accumulate before it expires
248 * @param excess_cb callback to notify if we have excess bandwidth
249 * @param excess_cb_cls closure for @a excess_cb
250 */
251void
252GNUNET_BANDWIDTH_tracker_init2 (
253 struct GNUNET_BANDWIDTH_Tracker *av,
254 GNUNET_BANDWIDTH_TrackerUpdateCallback update_cb,
255 void *update_cb_cls,
256 struct GNUNET_BANDWIDTH_Value32NBO bytes_per_second_limit,
257 uint32_t max_carry_s,
258 GNUNET_BANDWIDTH_ExcessNotificationCallback excess_cb,
259 void *excess_cb_cls)
260{
261 av->update_cb = update_cb;
262 av->update_cb_cls = update_cb_cls;
263 av->consumption_since_last_update__ = 0;
264 av->last_update__ = GNUNET_TIME_absolute_get ();
265 av->available_bytes_per_s__ = ntohl (bytes_per_second_limit.value__);
266 av->max_carry_s__ = max_carry_s;
267 av->excess_cb = excess_cb;
268 av->excess_cb_cls = excess_cb_cls;
269 LOG (GNUNET_ERROR_TYPE_DEBUG,
270 "Tracker %p initialized with %u Bps and max carry %u\n",
271 av,
272 (unsigned int) av->available_bytes_per_s__,
273 (unsigned int) max_carry_s);
274 update_excess (av);
275}
276
277
278void
279GNUNET_BANDWIDTH_tracker_init (
280 struct GNUNET_BANDWIDTH_Tracker *av,
281 GNUNET_BANDWIDTH_TrackerUpdateCallback update_cb,
282 void *update_cb_cls,
283 struct GNUNET_BANDWIDTH_Value32NBO bytes_per_second_limit,
284 uint32_t max_carry_s)
285{
286 GNUNET_BANDWIDTH_tracker_init2 (av,
287 update_cb,
288 update_cb_cls,
289 bytes_per_second_limit,
290 max_carry_s,
291 NULL,
292 NULL);
293}
294
295
296/**
297 * Stop notifying about tracker updates and excess notifications
298 *
299 * @param av the respective trackers
300 */
301void
302GNUNET_BANDWIDTH_tracker_notification_stop (struct GNUNET_BANDWIDTH_Tracker *av)
303{
304 if (NULL != av->excess_task)
305 GNUNET_SCHEDULER_cancel (av->excess_task);
306 av->excess_task = NULL;
307 av->excess_cb = NULL;
308 av->excess_cb_cls = NULL;
309 av->update_cb = NULL;
310 av->update_cb_cls = NULL;
311}
312
313
314/**
315 * Update the tracker, looking at the current time and
316 * bandwidth consumption data.
317 *
318 * @param av tracker to update
319 */
320static void
321update_tracker (struct GNUNET_BANDWIDTH_Tracker *av)
322{
323 struct GNUNET_TIME_Absolute now;
324 uint64_t delta_time;
325 uint64_t delta_avail;
326 uint64_t left_bytes;
327 uint64_t max_carry;
328
329 now = GNUNET_TIME_absolute_get ();
330 delta_time = now.abs_value_us - av->last_update__.abs_value_us;
331 delta_avail =
332 (delta_time * ((unsigned long long) av->available_bytes_per_s__)
333 + 500000LL)
334 / 1000000LL;
335 av->consumption_since_last_update__ -= delta_avail;
336 av->last_update__ = now;
337 if (av->consumption_since_last_update__ < 0)
338 {
339 left_bytes = -av->consumption_since_last_update__;
340 max_carry =
341 ((unsigned long long) av->available_bytes_per_s__) * av->max_carry_s__;
342 if (max_carry < GNUNET_MAX_MESSAGE_SIZE)
343 max_carry = GNUNET_MAX_MESSAGE_SIZE;
344 if (max_carry > INT64_MAX)
345 max_carry = INT64_MAX;
346 if (max_carry > left_bytes)
347 av->consumption_since_last_update__ = -left_bytes;
348 else
349 av->consumption_since_last_update__ = -max_carry;
350 }
351#if ! defined(GNUNET_CULL_LOGGING)
352 {
353 struct GNUNET_TIME_Relative delta;
354
355 delta.rel_value_us = delta_time;
356 LOG (GNUNET_ERROR_TYPE_DEBUG,
357 "Tracker %p updated, consumption at %lld at %u Bps, last update was %s ago\n",
358 av,
359 (long long) av->consumption_since_last_update__,
360 (unsigned int) av->available_bytes_per_s__,
361 GNUNET_STRINGS_relative_time_to_string (delta, GNUNET_YES));
362 }
363#endif
364}
365
366
367int
368GNUNET_BANDWIDTH_tracker_consume (struct GNUNET_BANDWIDTH_Tracker *av,
369 ssize_t size)
370{
371 int64_t nc;
372
373 LOG (GNUNET_ERROR_TYPE_DEBUG,
374 "Tracker %p consumes %d bytes\n",
375 av,
376 (int) size);
377 if (size > 0)
378 {
379 nc = av->consumption_since_last_update__ + size;
380 if (nc < av->consumption_since_last_update__)
381 {
382 /* integer overflow, very bad */
383 GNUNET_break (0);
384 return GNUNET_SYSERR;
385 }
386 av->consumption_since_last_update__ = nc;
387 update_tracker (av);
388 update_excess (av);
389 if (av->consumption_since_last_update__ > 0)
390 {
391 LOG (GNUNET_ERROR_TYPE_DEBUG,
392 "Tracker %p consumption %llu bytes above limit\n",
393 av,
394 (unsigned long long) av->consumption_since_last_update__);
395 return GNUNET_YES;
396 }
397 }
398 else
399 {
400 nc = av->consumption_since_last_update__ + size;
401 if (nc > av->consumption_since_last_update__)
402 {
403 /* integer underflow, very bad */
404 GNUNET_break (0);
405 return GNUNET_SYSERR;
406 }
407 av->consumption_since_last_update__ = nc;
408 update_excess (av);
409 }
410 return GNUNET_NO;
411}
412
413
414/**
415 * Compute how long we should wait until consuming 'size'
416 * bytes of bandwidth in order to stay within the given
417 * quota.
418 *
419 * @param av tracker to query
420 * @param size number of bytes we would like to consume
421 * @return time in ms to wait for consumption to be OK
422 */
423struct GNUNET_TIME_Relative
424GNUNET_BANDWIDTH_tracker_get_delay (struct GNUNET_BANDWIDTH_Tracker *av,
425 size_t size)
426{
427 struct GNUNET_TIME_Relative ret;
428 int64_t bytes_needed;
429
430 if (0 == av->available_bytes_per_s__)
431 {
432 LOG (GNUNET_ERROR_TYPE_DEBUG, "Tracker %p delay is infinity\n", av);
433 return GNUNET_TIME_UNIT_FOREVER_REL;
434 }
435 update_tracker (av);
436 bytes_needed = size + av->consumption_since_last_update__;
437 if (bytes_needed <= 0)
438 {
439 LOG (GNUNET_ERROR_TYPE_DEBUG,
440 "Tracker %p delay for %u bytes is zero\n",
441 av,
442 (unsigned int) size);
443 return GNUNET_TIME_UNIT_ZERO;
444 }
445 ret.rel_value_us = (1000LL * 1000LL * bytes_needed)
446 / (unsigned long long) av->available_bytes_per_s__;
447 LOG (GNUNET_ERROR_TYPE_DEBUG,
448 "Tracker %p delay for %u bytes is %s\n",
449 av,
450 (unsigned int) size,
451 GNUNET_STRINGS_relative_time_to_string (ret, GNUNET_YES));
452 return ret;
453}
454
455
456/**
457 * Compute how many bytes are available for consumption right now.
458 * quota.
459 *
460 * @param av tracker to query
461 * @return number of bytes available for consumption right now
462 */
463int64_t
464GNUNET_BANDWIDTH_tracker_get_available (struct GNUNET_BANDWIDTH_Tracker *av)
465{
466 struct GNUNET_BANDWIDTH_Value32NBO bps;
467 uint64_t avail;
468 int64_t used;
469
470 update_tracker (av);
471 bps = GNUNET_BANDWIDTH_value_init (av->available_bytes_per_s__);
472 avail =
473 GNUNET_BANDWIDTH_value_get_available_until (bps,
474 GNUNET_TIME_absolute_get_duration (
475 av->last_update__));
476 used = av->consumption_since_last_update__;
477 LOG (GNUNET_ERROR_TYPE_DEBUG,
478 "Tracker %p available bandwidth is %lld bytes\n",
479 av,
480 (long long) (int64_t) (avail - used));
481 return (int64_t) (avail - used);
482}
483
484
485/**
486 * Update quota of bandwidth tracker.
487 *
488 * @param av tracker to initialize
489 * @param bytes_per_second_limit new limit to assume
490 */
491void
492GNUNET_BANDWIDTH_tracker_update_quota (
493 struct GNUNET_BANDWIDTH_Tracker *av,
494 struct GNUNET_BANDWIDTH_Value32NBO bytes_per_second_limit)
495{
496 uint32_t old_limit;
497 uint32_t new_limit;
498
499 new_limit = ntohl (bytes_per_second_limit.value__);
500 LOG (GNUNET_ERROR_TYPE_DEBUG,
501 "Tracker %p bandwidth changed to %u Bps\n",
502 av,
503 (unsigned int) new_limit);
504 update_tracker (av);
505 old_limit = av->available_bytes_per_s__;
506 av->available_bytes_per_s__ = new_limit;
507 if (NULL != av->update_cb)
508 av->update_cb (av->update_cb_cls);
509 if (old_limit > new_limit)
510 update_tracker (av); /* maximum excess might be less now */
511 update_excess (av);
512}
513
514
515/* end of bandwidth.c */
diff --git a/src/lib/util/benchmark.c b/src/lib/util/benchmark.c
new file mode 100644
index 000000000..c9fc8842e
--- /dev/null
+++ b/src/lib/util/benchmark.c
@@ -0,0 +1,294 @@
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/**
22 * @file util/benchmark.c
23 * @brief benchmarking for various operations
24 * @author Florian Dold <flo@dold.me>
25 */
26
27
28#include "platform.h"
29#include "gnunet_util_lib.h"
30#include "benchmark.h"
31#include <pthread.h>
32#include <sys/syscall.h>
33
34/**
35 * Thread-local storage key for the benchmark data.
36 */
37static pthread_key_t key;
38
39/**
40 * One-time initialization marker for key.
41 */
42static pthread_once_t key_once = PTHREAD_ONCE_INIT;
43
44
45/**
46 * Write benchmark data to a file.
47 *
48 * @param bd the benchmark data
49 */
50static void
51write_benchmark_data (struct BenchmarkData *bd)
52{
53 struct GNUNET_DISK_FileHandle *fh;
54 pid_t pid = getpid ();
55 pid_t tid = syscall (SYS_gettid);
56 char *benchmark_dir;
57 char *s;
58
59 benchmark_dir = getenv ("GNUNET_BENCHMARK_DIR");
60
61 if (NULL == benchmark_dir)
62 return;
63
64 if (GNUNET_OK != GNUNET_DISK_directory_create (benchmark_dir))
65 {
66 GNUNET_break (0);
67 return;
68 }
69
70 GNUNET_asprintf (&s, "%s/gnunet-benchmark-ops-%s-%llu-%llu.txt",
71 benchmark_dir,
72 (pid == tid) ? "main" : "thread",
73 (unsigned long long) pid,
74 (unsigned long long) tid);
75
76 fh = GNUNET_DISK_file_open (s,
77 (GNUNET_DISK_OPEN_WRITE
78 | GNUNET_DISK_OPEN_TRUNCATE
79 | GNUNET_DISK_OPEN_CREATE),
80 (GNUNET_DISK_PERM_USER_READ
81 | GNUNET_DISK_PERM_USER_WRITE));
82 GNUNET_assert (NULL != fh);
83 GNUNET_free (s);
84
85#define WRITE_BENCHMARK_OP(opname) do { \
86 GNUNET_asprintf (&s, "op " #opname " count %llu time_us %llu\n", \
87 (unsigned long long) bd->opname ## _count, \
88 (unsigned long long) bd->opname ## _time.rel_value_us); \
89 GNUNET_assert (GNUNET_SYSERR != GNUNET_DISK_file_write_blocking (fh, s, \
90 strlen ( \
91 s))); \
92 GNUNET_free (s); \
93} while (0)
94
95 WRITE_BENCHMARK_OP (ecc_ecdh);
96 WRITE_BENCHMARK_OP (ecdh_eddsa);
97 WRITE_BENCHMARK_OP (ecdhe_key_create);
98 WRITE_BENCHMARK_OP (ecdhe_key_get_public);
99 WRITE_BENCHMARK_OP (ecdsa_ecdh);
100 WRITE_BENCHMARK_OP (ecdsa_key_create);
101 WRITE_BENCHMARK_OP (ecdsa_key_get_public);
102 WRITE_BENCHMARK_OP (ecdsa_sign);
103 WRITE_BENCHMARK_OP (ecdsa_verify);
104 WRITE_BENCHMARK_OP (eddsa_ecdh);
105 WRITE_BENCHMARK_OP (eddsa_key_create);
106 WRITE_BENCHMARK_OP (eddsa_key_get_public);
107 WRITE_BENCHMARK_OP (eddsa_sign);
108 WRITE_BENCHMARK_OP (eddsa_verify);
109 WRITE_BENCHMARK_OP (hash);
110 WRITE_BENCHMARK_OP (hash_context_finish);
111 WRITE_BENCHMARK_OP (hash_context_read);
112 WRITE_BENCHMARK_OP (hash_context_start);
113 WRITE_BENCHMARK_OP (hkdf);
114 WRITE_BENCHMARK_OP (rsa_blind);
115 WRITE_BENCHMARK_OP (rsa_private_key_create);
116 WRITE_BENCHMARK_OP (rsa_private_key_get_public);
117 WRITE_BENCHMARK_OP (rsa_sign_blinded);
118 WRITE_BENCHMARK_OP (rsa_unblind);
119 WRITE_BENCHMARK_OP (rsa_verify);
120
121#undef WRITE_BENCHMARK_OP
122
123 GNUNET_assert (GNUNET_OK == GNUNET_DISK_file_close (fh));
124
125 GNUNET_asprintf (&s, "%s/gnunet-benchmark-urls-%s-%llu-%llu.txt",
126 benchmark_dir,
127 (pid == tid) ? "main" : "thread",
128 (unsigned long long) pid,
129 (unsigned long long) tid);
130
131 fh = GNUNET_DISK_file_open (s,
132 (GNUNET_DISK_OPEN_WRITE
133 | GNUNET_DISK_OPEN_TRUNCATE
134 | GNUNET_DISK_OPEN_CREATE),
135 (GNUNET_DISK_PERM_USER_READ
136 | GNUNET_DISK_PERM_USER_WRITE));
137 GNUNET_assert (NULL != fh);
138 GNUNET_free (s);
139
140 for (unsigned int i = 0; i < bd->urd_len; i++)
141 {
142 struct UrlRequestData *urd = &bd->urd[i];
143 GNUNET_asprintf (&s,
144 "url %s status %u count %llu time_us %llu time_us_max %llu bytes_sent %llu bytes_received %llu\n",
145 urd->request_url,
146 urd->status,
147 (unsigned long long) urd->count,
148 (unsigned long long) urd->time.rel_value_us,
149 (unsigned long long) urd->time_max.rel_value_us,
150 (unsigned long long) urd->bytes_sent,
151 (unsigned long long) urd->bytes_received);
152 GNUNET_assert (GNUNET_SYSERR != GNUNET_DISK_file_write_blocking (fh, s,
153 strlen (
154 s)));
155 GNUNET_free (s);
156 }
157
158 GNUNET_assert (GNUNET_OK == GNUNET_DISK_file_close (fh));
159}
160
161
162/**
163 * Called when the main thread exits and benchmark data for it was created.
164 */
165static void
166main_thread_destructor ()
167{
168 struct BenchmarkData *bd;
169
170 bd = pthread_getspecific (key);
171 if (NULL != bd)
172 write_benchmark_data (bd);
173}
174
175
176/**
177 * Called when a thread exits and benchmark data for it was created.
178 *
179 * @param cls closure
180 */
181static void
182thread_destructor (void *cls)
183{
184 struct BenchmarkData *bd = cls;
185
186 // main thread will be handled by atexit
187 if (getpid () == (pid_t) syscall (SYS_gettid))
188 return;
189
190 GNUNET_assert (NULL != bd);
191 write_benchmark_data (bd);
192}
193
194
195/**
196 * Initialize the thread-local variable key for benchmark data.
197 */
198static void
199make_key ()
200{
201 (void) pthread_key_create (&key, &thread_destructor);
202}
203
204
205/**
206 * Acquire the benchmark data for the current thread, allocate if necessary.
207 * Installs handler to collect the benchmark data on thread termination.
208 *
209 * @return benchmark data for the current thread
210 */
211struct BenchmarkData *
212get_benchmark_data (void)
213{
214 struct BenchmarkData *bd;
215
216 (void) pthread_once (&key_once, &make_key);
217
218 if (NULL == (bd = pthread_getspecific (key)))
219 {
220 bd = GNUNET_new (struct BenchmarkData);
221 (void) pthread_setspecific (key, bd);
222 if (getpid () == (pid_t) syscall (SYS_gettid))
223 {
224 // We're the main thread!
225 atexit (main_thread_destructor);
226 }
227 }
228 return bd;
229}
230
231
232/**
233 * Get benchmark data for a URL. If the URL is too long, it's truncated
234 * before looking up the corresponding benchmark data.
235 *
236 * Statistics are bucketed by URL and status code.
237 *
238 * @param url url to get request data for
239 * @param status http status code
240 */
241struct UrlRequestData *
242get_url_benchmark_data (char *url, unsigned int status)
243{
244 char trunc[MAX_BENCHMARK_URL_LEN];
245 struct BenchmarkData *bd;
246
247 if (NULL == url)
248 {
249 /* Should not happen unless curl barfs */
250 GNUNET_break (0);
251 url = "<empty>";
252 }
253
254 memcpy (trunc, url, MAX_BENCHMARK_URL_LEN);
255 trunc[MAX_BENCHMARK_URL_LEN - 1] = 0;
256
257 /* We're not interested in what's after the query string */
258 for (size_t i = 0; i < strlen (trunc); i++)
259 {
260 if (trunc[i] == '?')
261 {
262 trunc[i] = 0;
263 break;
264 }
265 }
266
267 bd = get_benchmark_data ();
268
269 GNUNET_assert (bd->urd_len <= bd->urd_capacity);
270
271 for (unsigned int i = 0; i < bd->urd_len; i++)
272 {
273 if ((0 == strcmp (trunc, bd->urd[i].request_url)) &&
274 (bd->urd[i].status == status))
275 return &bd->urd[i];
276 }
277
278 {
279 struct UrlRequestData urd = { 0 };
280
281 memcpy (&urd.request_url, trunc, MAX_BENCHMARK_URL_LEN);
282 urd.status = status;
283
284 if (bd->urd_len == bd->urd_capacity)
285 {
286 bd->urd_capacity = 2 * (bd->urd_capacity + 1);
287 bd->urd = GNUNET_realloc (bd->urd, bd->urd_capacity * sizeof(struct
288 UrlRequestData));
289 }
290
291 bd->urd[bd->urd_len++] = urd;
292 return &bd->urd[bd->urd_len - 1];
293 }
294}
diff --git a/src/lib/util/benchmark.h b/src/lib/util/benchmark.h
new file mode 100644
index 000000000..e35fa50bd
--- /dev/null
+++ b/src/lib/util/benchmark.h
@@ -0,0 +1,174 @@
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/**
22 * @file util/benchmark.h
23 * @brief benchmarking for various operations
24 * @author Florian Dold <flo@dold.me>
25 */
26
27#ifndef BENCHMARK_H_
28#define BENCHMARK_H_
29
30#include "gnunet_time_lib.h"
31
32/**
33 * Maximum length of URLs considered for benchmarking.
34 * Shorter URLs are simply truncated.
35 */
36#define MAX_BENCHMARK_URL_LEN 128
37
38#if ENABLE_BENCHMARK
39#define BENCHMARK_START(opname) \
40 struct GNUNET_TIME_Absolute _benchmark_ ## opname ## _start = \
41 GNUNET_TIME_absolute_get ()
42#define BENCHMARK_END(opname) do { \
43 { \
44 struct GNUNET_TIME_Absolute _benchmark_ ## opname ## _end = \
45 GNUNET_TIME_absolute_get (); \
46 struct BenchmarkData *bd = get_benchmark_data (); \
47 bd->opname ## _count++; \
48 bd->opname ## _time = \
49 GNUNET_TIME_relative_add (bd->opname ## _time, \
50 GNUNET_TIME_absolute_get_difference ( \
51 _benchmark_ ## opname ## _start, \
52 _benchmark_ \
53 ## opname ## _end)); \
54 } \
55} while (0)
56#else
57#define BENCHMARK_START(opname) do { } while (0)
58#define BENCHMARK_END(opname) do { } while (0)
59#endif
60
61
62/**
63 * Struct for benchmark data for one URL.
64 */
65struct UrlRequestData
66{
67 /**
68 * Request URL, truncated (but 0-terminated).
69 */
70 char request_url[MAX_BENCHMARK_URL_LEN];
71
72 /**
73 * HTTP status code.
74 */
75 unsigned int status;
76
77 /**
78 * How often was the URL requested?
79 */
80 uint64_t count;
81
82 /**
83 * How many bytes were sent in total to request the URL.
84 */
85 uint64_t bytes_sent;
86
87 /**
88 * How many bytes were received in total as response to requesting this URL.
89 */
90 uint64_t bytes_received;
91
92 /**
93 * Total time spent requesting this URL.
94 */
95 struct GNUNET_TIME_Relative time;
96
97 /**
98 * Slowest time to response.
99 */
100 struct GNUNET_TIME_Relative time_max;
101
102 /**
103 * Fastest time to response.
104 */
105 struct GNUNET_TIME_Relative time_min;
106};
107
108#define GNUNET_DECLARE_BENCHMARK_OP(opname) \
109 uint64_t opname ## _count; \
110 struct GNUNET_TIME_Relative opname ## _time
111
112/**
113 * Thread-local struct for benchmarking data.
114 */
115struct BenchmarkData
116{
117 GNUNET_DECLARE_BENCHMARK_OP (ecc_ecdh);
118 GNUNET_DECLARE_BENCHMARK_OP (ecdh_eddsa);
119 GNUNET_DECLARE_BENCHMARK_OP (ecdhe_key_create);
120 GNUNET_DECLARE_BENCHMARK_OP (ecdhe_key_get_public);
121 GNUNET_DECLARE_BENCHMARK_OP (ecdsa_ecdh);
122 GNUNET_DECLARE_BENCHMARK_OP (ecdsa_key_create);
123 GNUNET_DECLARE_BENCHMARK_OP (ecdsa_key_get_public);
124 GNUNET_DECLARE_BENCHMARK_OP (ecdsa_sign);
125 GNUNET_DECLARE_BENCHMARK_OP (ecdsa_verify);
126 GNUNET_DECLARE_BENCHMARK_OP (eddsa_ecdh);
127 GNUNET_DECLARE_BENCHMARK_OP (eddsa_key_create);
128 GNUNET_DECLARE_BENCHMARK_OP (eddsa_key_get_public);
129 GNUNET_DECLARE_BENCHMARK_OP (eddsa_sign);
130 GNUNET_DECLARE_BENCHMARK_OP (eddsa_verify);
131 GNUNET_DECLARE_BENCHMARK_OP (hash);
132 GNUNET_DECLARE_BENCHMARK_OP (hash_context_finish);
133 GNUNET_DECLARE_BENCHMARK_OP (hash_context_read);
134 GNUNET_DECLARE_BENCHMARK_OP (hash_context_start);
135 GNUNET_DECLARE_BENCHMARK_OP (hkdf);
136 GNUNET_DECLARE_BENCHMARK_OP (rsa_blind);
137 GNUNET_DECLARE_BENCHMARK_OP (rsa_private_key_create);
138 GNUNET_DECLARE_BENCHMARK_OP (rsa_private_key_get_public);
139 GNUNET_DECLARE_BENCHMARK_OP (rsa_sign_blinded);
140 GNUNET_DECLARE_BENCHMARK_OP (rsa_unblind);
141 GNUNET_DECLARE_BENCHMARK_OP (rsa_verify);
142
143 struct UrlRequestData *urd;
144
145 unsigned int urd_len;
146
147 unsigned int urd_capacity;
148};
149
150#undef GNUNET_DECLARE_BENCHMARK_OP
151
152
153/**
154 * Acquire the benchmark data for the current thread, allocate if necessary.
155 * Installs handler to collect the benchmark data on thread termination.
156 *
157 * @return benchmark data for the current thread
158 */
159struct BenchmarkData *
160get_benchmark_data (void);
161
162/**
163 * Get benchmark data for a URL. If the URL is too long, it's truncated
164 * before looking up the corresponding benchmark data.
165 *
166 * Statistics are bucketed by URL and status code.
167 *
168 * @param url url to get request data for
169 * @param status http status code
170 */
171struct UrlRequestData *
172get_url_benchmark_data (char *url, unsigned int status);
173
174#endif /* BENCHMARK_H_ */
diff --git a/src/lib/util/bio.c b/src/lib/util/bio.c
new file mode 100644
index 000000000..caf533108
--- /dev/null
+++ b/src/lib/util/bio.c
@@ -0,0 +1,1380 @@
1/*
2 This file is part of GNUnet.
3 Copyright (C) 2006, 2009, 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 util/bio.c
22 * @brief functions for buffering IO
23 * @author Christian Grothoff
24 */
25
26#include "platform.h"
27#include "gnunet_util_lib.h"
28
29#define LOG(kind, ...) GNUNET_log_from (kind, "util-bio", __VA_ARGS__)
30
31#ifndef PATH_MAX
32/**
33 * Assumed maximum path length (for source file names).
34 */
35#define PATH_MAX 4096
36#endif
37
38
39/**
40 * Size for I/O buffers.
41 */
42#define BIO_BUFFER_SIZE 65536
43
44
45/**
46 * Enum used internally to know how buffering is handled.
47 *
48 * The idea is that by using an enum, BIO can be extended to support other
49 * kinds of "backend" for buffering (or just formatted I/O.)
50 */
51enum IOType
52{
53 /**
54 * The handle uses a file to read/write data.
55 */
56 IO_FILE = 0,
57
58 /**
59 * The data is stored entirely in memory.
60 */
61 IO_BUFFER,
62};
63
64
65/**
66 * Handle for buffered reading.
67 */
68struct GNUNET_BIO_ReadHandle
69{
70 /**
71 * The "backend" type.
72 */
73 enum IOType type;
74
75 /**
76 * Handle to a file on disk, if @e type is #IO_FILE.
77 */
78 struct GNUNET_DISK_FileHandle *fd;
79
80 /**
81 * Error message, NULL if there were no errors.
82 */
83 char *emsg;
84
85 /**
86 * I/O buffer. Do @b not free!
87 */
88 char *buffer;
89
90 /**
91 * Number of bytes available in @e buffer.
92 */
93 size_t have;
94
95 /**
96 * Total size of @e buffer.
97 */
98 size_t size;
99
100 /**
101 * Current read offset in @e buffer.
102 */
103 off_t pos;
104};
105
106
107/**
108 * Open a file for reading.
109 *
110 * @param fn file name to be opened
111 * @return IO handle on success, NULL on error
112 */
113struct GNUNET_BIO_ReadHandle *
114GNUNET_BIO_read_open_file (const char *fn)
115{
116 struct GNUNET_DISK_FileHandle *fd;
117 struct GNUNET_BIO_ReadHandle *h;
118
119 fd = GNUNET_DISK_file_open (fn, GNUNET_DISK_OPEN_READ, GNUNET_DISK_PERM_NONE);
120 if (NULL == fd)
121 return NULL;
122 h = GNUNET_malloc (sizeof(struct GNUNET_BIO_ReadHandle) + BIO_BUFFER_SIZE);
123 h->type = IO_FILE;
124 h->buffer = (char *) &h[1];
125 h->size = BIO_BUFFER_SIZE;
126 h->fd = fd;
127 return h;
128}
129
130
131/**
132 * Create a handle from an existing allocated buffer.
133 *
134 * @param buffer the buffer to use as source
135 * @param size the total size in bytes of the buffer
136 * @return IO handle on success, NULL on error
137 */
138struct GNUNET_BIO_ReadHandle *
139GNUNET_BIO_read_open_buffer (void *buffer, size_t size)
140{
141 struct GNUNET_BIO_ReadHandle *h;
142
143 h = GNUNET_new (struct GNUNET_BIO_ReadHandle);
144 h->type = IO_BUFFER;
145 h->buffer = buffer;
146 h->size = size;
147 return h;
148}
149
150
151/**
152 * Close an open handle. Reports if any errors reading
153 * from the file were encountered.
154 *
155 * @param h file handle
156 * @param emsg set to the (allocated) error message
157 * if the handle has an error message, the return
158 * value is #GNUNET_SYSERR
159 * @return #GNUNET_OK on success, #GNUNET_SYSERR otherwise
160 */
161enum GNUNET_GenericReturnValue
162GNUNET_BIO_read_close (struct GNUNET_BIO_ReadHandle *h, char **emsg)
163{
164 int err;
165
166 err = (NULL == h->emsg) ? GNUNET_OK : GNUNET_SYSERR;
167 if (NULL != emsg)
168 *emsg = h->emsg;
169 else
170 GNUNET_free (h->emsg);
171 switch (h->type)
172 {
173 case IO_FILE:
174 GNUNET_DISK_file_close (h->fd);
175 break;
176 case IO_BUFFER:
177 break;
178 default:
179 break;
180 }
181 GNUNET_free (h);
182 return err;
183}
184
185
186void
187GNUNET_BIO_read_set_error (struct GNUNET_BIO_ReadHandle *h, const char*emsg)
188{
189 GNUNET_assert (NULL == h->emsg);
190 h->emsg = GNUNET_strdup (emsg);
191}
192
193
194/**
195 * Function used internally to read the contents of a file into a buffer.
196 *
197 * @param h the IO handle to read from
198 * @param what describes what is being read (for error message creation)
199 * @param result the buffer to write the data to
200 * @param len the number of bytes to read
201 * @return #GNUNET_OK on success, #GNUNET_SYSERR otherwise
202 */
203static int
204read_from_file (struct GNUNET_BIO_ReadHandle *h,
205 const char *what,
206 char *result,
207 size_t len)
208{
209 size_t pos = 0;
210 size_t min;
211 ssize_t ret;
212
213 do
214 {
215 min = h->have - h->pos;
216 if (0 < min)
217 {
218 if (len - pos < min)
219 min = len - pos;
220 GNUNET_memcpy (&result[pos], &h->buffer[h->pos], min);
221 h->pos += min;
222 pos += min;
223 }
224 if (len == pos)
225 return GNUNET_OK;
226 GNUNET_assert (((off_t) h->have) == h->pos);
227 ret = GNUNET_DISK_file_read (h->fd, h->buffer, h->size);
228 if (-1 == ret)
229 {
230 GNUNET_asprintf (&h->emsg,
231 _ ("Error reading `%s' from file: %s"),
232 what,
233 strerror (errno));
234 return GNUNET_SYSERR;
235 }
236 if (0 == ret)
237 {
238 GNUNET_asprintf (&h->emsg,
239 _ ("Error reading `%s' from file: %s"),
240 what,
241 _ ("End of file"));
242 return GNUNET_SYSERR;
243 }
244 h->pos = 0;
245 h->have = ret;
246 }
247 while (pos < len);
248 return GNUNET_OK;
249}
250
251
252/**
253 * Function used internally to read the content of a buffer into a buffer.
254 *
255 * @param h the IO handle to read from
256 * @param what describes what is being read (for error message creation)
257 * @param result the buffer to write the result to
258 * @param len the number of bytes to read
259 * @return #GNUNET_OK on success, #GNUNET_SYSERR otherwise
260 */
261static int
262read_from_buffer (struct GNUNET_BIO_ReadHandle *h,
263 const char *what,
264 char *result,
265 size_t len)
266{
267 if ((h->size < len) || (h->size - h->pos < len))
268 {
269 GNUNET_asprintf (&h->emsg,
270 _ ("Error while reading `%s' from buffer: %s"),
271 what,
272 _ ("Not enough data left"));
273 return GNUNET_SYSERR;
274 }
275 GNUNET_memcpy (result, h->buffer + h->pos, len);
276 h->pos += len;
277 return GNUNET_OK;
278}
279
280
281/**
282 * Read some contents into a buffer.
283 *
284 * @param h the IO handle to read from
285 * @param what describes what is being read (for error message creation)
286 * @param result the buffer to write the result to
287 * @param len the number of bytes to read
288 * @return #GNUNET_OK on success, #GNUNET_SYSERR on failure
289 */
290enum GNUNET_GenericReturnValue
291GNUNET_BIO_read (struct GNUNET_BIO_ReadHandle *h,
292 const char *what,
293 void *result,
294 size_t len)
295{
296 char *dst = result;
297
298 if (NULL != h->emsg)
299 return GNUNET_SYSERR;
300
301 if (0 == len)
302 return GNUNET_OK;
303
304 switch (h->type)
305 {
306 case IO_FILE:
307 return read_from_file (h, what, dst, len);
308 case IO_BUFFER:
309 return read_from_buffer (h, what, dst, len);
310 default:
311 GNUNET_asprintf (&h->emsg,
312 _ ("Invalid handle type while reading `%s'"),
313 what);
314 return GNUNET_SYSERR;
315 }
316}
317
318
319/**
320 * Read 0-terminated string.
321 *
322 * @param h the IO handle to read from
323 * @param what describes what is being read (for error message creation)
324 * @param result where to store the pointer to the (allocated) string
325 * (note that *result could be set to NULL as well)
326 * @param max_length maximum allowed length for the string
327 * @return #GNUNET_OK on success, #GNUNET_SYSERR on failure
328 */
329enum GNUNET_GenericReturnValue
330GNUNET_BIO_read_string (struct GNUNET_BIO_ReadHandle *h,
331 const char *what,
332 char **result,
333 size_t max_length)
334{
335 char *buf;
336 uint32_t big;
337
338 if (GNUNET_OK != GNUNET_BIO_read_int32 (h,
339 _ ("string length"),
340 (int32_t *) &big))
341 {
342 char *tmp = h->emsg;
343 if (NULL != tmp)
344 GNUNET_asprintf (&h->emsg,
345 _ ("%s (while reading `%s')"),
346 tmp,
347 what);
348 else
349 GNUNET_asprintf (&h->emsg,
350 _ ("Error reading length of string `%s'"),
351 what);
352 GNUNET_free (tmp);
353 return GNUNET_SYSERR;
354 }
355 if (0 == big)
356 {
357 *result = NULL;
358 return GNUNET_OK;
359 }
360 if (big > max_length)
361 {
362 GNUNET_asprintf (&h->emsg,
363 _ ("String `%s' longer than allowed (%u > %lu)"),
364 what,
365 big,
366 (unsigned long) max_length);
367 return GNUNET_SYSERR;
368 }
369 buf = GNUNET_malloc (big);
370 *result = buf;
371 buf[--big] = '\0';
372 if (0 == big)
373 return GNUNET_OK;
374 if (GNUNET_OK != GNUNET_BIO_read (h, what, buf, big))
375 {
376 GNUNET_free (buf);
377 *result = NULL;
378 return GNUNET_SYSERR;
379 }
380 return GNUNET_OK;
381}
382
383
384/**
385 * Read a float.
386 *
387 * @param h the IO handle to read from
388 * @param what describes what is being read (for error message creation)
389 * @param f address of float to read
390 */
391enum GNUNET_GenericReturnValue
392GNUNET_BIO_read_float (struct GNUNET_BIO_ReadHandle *h,
393 const char *what,
394 float *f)
395{
396 int32_t *i = (int32_t *) f;
397 return GNUNET_BIO_read_int32 (h, what, i);
398}
399
400
401/**
402 * Read a double.
403 *
404 * @param h the IO handle to read from
405 * @param what describes what is being read (for error message creation)
406 * @param f address of double to read
407 */
408enum GNUNET_GenericReturnValue
409GNUNET_BIO_read_double (struct GNUNET_BIO_ReadHandle *h,
410 const char *what,
411 double *f)
412{
413 int64_t *i = (int64_t *) f;
414 return GNUNET_BIO_read_int64 (h, what, i);
415}
416
417
418/**
419 * Read an (u)int32_t.
420 *
421 * @param h the IO handle to read from
422 * @param what describes what is being read (for error message creation)
423 * @param i where to store the data
424 * @return #GNUNET_OK on success, #GNUNET_SYSERR on error
425 */
426enum GNUNET_GenericReturnValue
427GNUNET_BIO_read_int32 (struct GNUNET_BIO_ReadHandle *h,
428 const char *what,
429 int32_t *i)
430{
431 int32_t big;
432
433 if (GNUNET_OK != GNUNET_BIO_read (h, what, &big, sizeof(int32_t)))
434 return GNUNET_SYSERR;
435 *i = ntohl (big);
436 return GNUNET_OK;
437}
438
439
440/**
441 * Read an (u)int64_t.
442 *
443 * @param h the IO handle to read from
444 * @param what describes what is being read (for error message creation)
445 * @param i where to store the data
446 * @return #GNUNET_OK on success, #GNUNET_SYSERR on error
447 */
448enum GNUNET_GenericReturnValue
449GNUNET_BIO_read_int64 (struct GNUNET_BIO_ReadHandle *h,
450 const char *what,
451 int64_t *i)
452{
453 int64_t big;
454
455 if (GNUNET_OK != GNUNET_BIO_read (h, what, &big, sizeof(int64_t)))
456 return GNUNET_SYSERR;
457 *i = GNUNET_ntohll (big);
458 return GNUNET_OK;
459}
460
461
462/**
463 * Handle for buffered writing.
464 */
465struct GNUNET_BIO_WriteHandle
466{
467 /**
468 * The "backend" type.
469 */
470 enum IOType type;
471
472 /**
473 * Handle to a file on disk, if @e type is #IO_FILE.
474 */
475 struct GNUNET_DISK_FileHandle *fd;
476
477 /**
478 * Error message, NULL if there were no errors.
479 */
480 char *emsg;
481
482 /**
483 * I/O buffer.
484 * This field is a void * because it is used to hold pointers to allocated
485 * structures or arrays and will be casted to the appropriate type.
486 */
487 void *buffer;
488
489 /**
490 * Number of bytes available in @e buffer.
491 */
492 size_t have;
493
494 /**
495 * Total size of @e buffer.
496 */
497 size_t size;
498};
499
500
501/**
502 * Open a file for writing.
503 *
504 * @param fn name of the file to be opened
505 * @return IO handle on success, NULL on error
506 */
507struct GNUNET_BIO_WriteHandle *
508GNUNET_BIO_write_open_file (const char *fn)
509{
510 struct GNUNET_DISK_FileHandle *fd;
511 struct GNUNET_BIO_WriteHandle *h;
512
513 fd = GNUNET_DISK_file_open (fn,
514 GNUNET_DISK_OPEN_WRITE
515 | GNUNET_DISK_OPEN_TRUNCATE
516 | GNUNET_DISK_OPEN_CREATE,
517 GNUNET_DISK_PERM_USER_READ
518 | GNUNET_DISK_PERM_USER_WRITE);
519 if (NULL == fd)
520 return NULL;
521 h = GNUNET_malloc (sizeof(struct GNUNET_BIO_WriteHandle) + BIO_BUFFER_SIZE);
522 h->buffer = &h[1];
523 h->size = BIO_BUFFER_SIZE;
524 h->fd = fd;
525 return h;
526}
527
528
529/**
530 * Create a handle backed by an in-memory buffer.
531 *
532 * @return IO handle on success, NULL on error
533 */
534struct GNUNET_BIO_WriteHandle *
535GNUNET_BIO_write_open_buffer (void)
536{
537 struct GNUNET_BIO_WriteHandle *h;
538
539 h = GNUNET_new (struct GNUNET_BIO_WriteHandle);
540 h->type = IO_BUFFER;
541 h->buffer = (void *) GNUNET_malloc (sizeof (struct GNUNET_Buffer));
542 return h;
543}
544
545
546/**
547 * Close an IO handle.
548 * If the handle was using a file, the file will be closed.
549 *
550 * @param h file handle
551 * @param emsg set to the (allocated) error message
552 * if the handle has an error message, the return value is #GNUNET_SYSERR
553 * @return #GNUNET_OK on success, #GNUNET_SYSERR otherwise
554 */
555enum GNUNET_GenericReturnValue
556GNUNET_BIO_write_close (struct GNUNET_BIO_WriteHandle *h,
557 char **emsg)
558{
559 int err;
560
561 err = (NULL == h->emsg) ? GNUNET_OK : GNUNET_SYSERR;
562 if (NULL != emsg)
563 *emsg = h->emsg;
564 else
565 GNUNET_free (h->emsg);
566 switch (h->type)
567 {
568 case IO_FILE:
569 if (NULL == h->fd)
570 {
571 err = GNUNET_SYSERR;
572 break;
573 }
574 if (GNUNET_OK != GNUNET_BIO_flush (h))
575 {
576 if (NULL != emsg)
577 *emsg = h->emsg;
578 else
579 GNUNET_free (h->emsg);
580 err = GNUNET_SYSERR;
581 }
582 else
583 {
584 GNUNET_DISK_file_close (h->fd);
585 }
586 break;
587 case IO_BUFFER:
588 GNUNET_buffer_clear ((struct GNUNET_Buffer *) h->buffer);
589 GNUNET_free (h->buffer);
590 break;
591 }
592 GNUNET_free (h);
593 return err;
594}
595
596
597/**
598 * Force a file-based buffered writer to flush its buffer.
599 * If the handle does not use a file, this function returns #GNUNET_OK
600 * without doing anything.
601 *
602 * @param h the IO handle
603 * @return #GNUNET_OK upon success. Upon failure #GNUNET_SYSERR is returned
604 * and the file is closed
605 */
606enum GNUNET_GenericReturnValue
607GNUNET_BIO_flush (struct GNUNET_BIO_WriteHandle *h)
608{
609 ssize_t ret;
610
611 if (IO_FILE != h->type)
612 return GNUNET_OK;
613 ret = GNUNET_DISK_file_write (h->fd,
614 h->buffer,
615 h->have);
616 if (ret != (ssize_t) h->have)
617 {
618 GNUNET_DISK_file_close (h->fd);
619 h->fd = NULL;
620 GNUNET_free (h->emsg);
621 GNUNET_asprintf (&h->emsg,
622 "Unable to flush buffer to file");
623 return GNUNET_SYSERR;
624 }
625 h->have = 0;
626 return GNUNET_OK;
627}
628
629
630/**
631 * Get the IO handle's contents.
632 * If the handle doesn't use an in-memory buffer, this function returns
633 * #GNUNET_SYSERR.
634 *
635 * @param h the IO handle
636 * @param emsg set to the (allocated) error message
637 * if the handle has an error message the return value is #GNUNET_SYSERR
638 * @param contents where to store the pointer to the handle's contents
639 * @param size where to store the size of @e contents
640 * @return #GNUNET_OK on success, #GNUNET_SYSERR otherwise
641 */
642enum GNUNET_GenericReturnValue
643GNUNET_BIO_get_buffer_contents (struct GNUNET_BIO_WriteHandle *h,
644 char **emsg,
645 void **contents,
646 size_t *size)
647{
648 if (IO_BUFFER != h->type)
649 return GNUNET_SYSERR;
650 if ((NULL == contents) || (NULL == size))
651 return GNUNET_SYSERR;
652 enum GNUNET_GenericReturnValue ret
653 = (NULL != h->emsg)
654 ? GNUNET_SYSERR
655 : GNUNET_OK;
656 if (NULL != emsg)
657 *emsg = h->emsg;
658 else
659 GNUNET_free (h->emsg);
660 *contents = GNUNET_buffer_reap ((struct GNUNET_Buffer *) h->buffer, size);
661 return ret;
662}
663
664
665/**
666 * Function used internally to write the contents of a buffer into a file.
667 *
668 * @param h the IO handle to write to
669 * @param what describes what is being written (for error message creation)
670 * @param source the buffer to write
671 * @param len the number of bytes to write
672 * @return #GNUNET_OK on success, #GNUNET_SYSERR otherwise
673 */
674static enum GNUNET_GenericReturnValue
675write_to_file (struct GNUNET_BIO_WriteHandle *h,
676 const char *what,
677 const char *source,
678 size_t len)
679{
680 size_t min;
681 size_t pos = 0;
682 char *buffer = (char *) h->buffer;
683
684 if (NULL == h->fd)
685 {
686 GNUNET_asprintf (&h->emsg,
687 _ ("Error while writing `%s' to file: %s"),
688 what,
689 _ ("No associated file"));
690 return GNUNET_SYSERR;
691 }
692
693 do
694 {
695 min = h->size - h->have;
696 if (len - pos < min)
697 min = len - pos;
698 GNUNET_memcpy (&buffer[h->have], &source[pos], min);
699 pos += min;
700 h->have += min;
701 if (len == pos)
702 return GNUNET_OK;
703 GNUNET_assert (h->have == h->size);
704 if (GNUNET_OK != GNUNET_BIO_flush (h))
705 {
706 char *tmp = h->emsg;
707 GNUNET_asprintf (&h->emsg,
708 _ ("Error while writing `%s' to file: %s"),
709 what,
710 tmp);
711 GNUNET_free (tmp);
712 return GNUNET_SYSERR;
713 }
714 }
715 while (pos < len);
716 GNUNET_break (0);
717 return GNUNET_OK;
718}
719
720
721/**
722 * Function used internally to write the contents of a buffer to another buffer.
723 *
724 * @param h the IO handle to write to
725 * @param what describes what is being written (for error message creation)
726 * @param source the buffer to write
727 * @param len the number of bytes to write
728 * @return #GNUNET_OK on success, #GNUNET_SYSERR otherwise
729 */
730static int
731write_to_buffer (struct GNUNET_BIO_WriteHandle *h,
732 const char *what,
733 const char *source,
734 size_t len)
735{
736 GNUNET_buffer_write ((struct GNUNET_Buffer *) h->buffer, source, len);
737 h->have += len;
738 return GNUNET_OK;
739}
740
741
742/**
743 * Write a buffer to a handle.
744 *
745 * @param h the IO handle to write to
746 * @param what what is being written (for error message creation)
747 * @param buffer the data to write
748 * @param n number of bytes to write
749 * @return #GNUNET_OK on success, #GNUNET_SYSERR on error
750 */
751enum GNUNET_GenericReturnValue
752GNUNET_BIO_write (struct GNUNET_BIO_WriteHandle *h,
753 const char *what,
754 const void *buffer,
755 size_t n)
756{
757 const char *src = buffer;
758
759 if (NULL != h->emsg)
760 return GNUNET_SYSERR;
761
762 if (0 == n)
763 return GNUNET_OK;
764
765 switch (h->type)
766 {
767 case IO_FILE:
768 return write_to_file (h, what, src, n);
769 case IO_BUFFER:
770 return write_to_buffer (h, what, src, n);
771 default:
772 GNUNET_asprintf (&h->emsg,
773 _ ("Invalid handle type while writing `%s'"),
774 what);
775 return GNUNET_SYSERR;
776 }
777}
778
779
780/**
781 * Write a 0-terminated string.
782 *
783 * @param h the IO handle to write to
784 * @param what what is being written (for error message creation)
785 * @param s string to write (can be NULL)
786 * @return #GNUNET_OK on success, #GNUNET_SYSERR on error
787 */
788enum GNUNET_GenericReturnValue
789GNUNET_BIO_write_string (struct GNUNET_BIO_WriteHandle *h,
790 const char *what,
791 const char *s)
792{
793 uint32_t slen;
794
795 slen = (uint32_t) ((s == NULL) ? 0 : strlen (s) + 1);
796 if (GNUNET_OK != GNUNET_BIO_write_int32 (h, _ ("string length"), slen))
797 return GNUNET_SYSERR;
798 if (0 != slen)
799 return GNUNET_BIO_write (h, what, s, slen - 1);
800 return GNUNET_OK;
801}
802
803
804/**
805 * Write a float.
806 *
807 * @param h the IO handle to write to
808 * @param what what is being written (for error message creation)
809 * @param f float to write
810 */
811enum GNUNET_GenericReturnValue
812GNUNET_BIO_write_float (struct GNUNET_BIO_WriteHandle *h,
813 const char *what,
814 float f)
815{
816 int32_t i = f;
817 return GNUNET_BIO_write_int32 (h, what, i);
818}
819
820
821/**
822 * Write a double.
823 *
824 * @param h the IO handle to write to
825 * @param what what is being written (for error message creation)
826 * @param f double to write
827 */
828enum GNUNET_GenericReturnValue
829GNUNET_BIO_write_double (struct GNUNET_BIO_WriteHandle *h,
830 const char *what,
831 double f)
832{
833 int64_t i = f;
834 return GNUNET_BIO_write_int64 (h, what, i);
835}
836
837
838/**
839 * Write an (u)int32_t.
840 *
841 * @param h the IO handle to write to
842 * @param what what is being written (for error message creation)
843 * @param i 32-bit integer to write
844 * @return #GNUNET_OK on success, #GNUNET_SYSERR on error
845 */
846enum GNUNET_GenericReturnValue
847GNUNET_BIO_write_int32 (struct GNUNET_BIO_WriteHandle *h,
848 const char *what,
849 int32_t i)
850{
851 int32_t big;
852
853 big = htonl (i);
854 return GNUNET_BIO_write (h, what, &big, sizeof(int32_t));
855}
856
857
858/**
859 * Write an (u)int64_t.
860 *
861 * @param h the IO handle to write to
862 * @param what what is being written (for error message creation)
863 * @param i 64-bit integer to write
864 * @return #GNUNET_OK on success, #GNUNET_SYSERR on error
865 */
866enum GNUNET_GenericReturnValue
867GNUNET_BIO_write_int64 (struct GNUNET_BIO_WriteHandle *h,
868 const char *what,
869 int64_t i)
870{
871 int64_t big;
872
873 big = GNUNET_htonll (i);
874 return GNUNET_BIO_write (h, what, &big, sizeof(int64_t));
875}
876
877
878/**
879 * Function used internally to read some bytes from within a read spec.
880 *
881 * @param cls ignored, always NULL
882 * @param h the IO handle to read from
883 * @param what what is being read (for error message creation)
884 * @param target where to store the data
885 * @param target_size how many bytes to read
886 * @return #GNUNET_OK on success, #GNUNET_SYSERR otherwise
887 */
888static int
889read_spec_handler_object (void *cls,
890 struct GNUNET_BIO_ReadHandle *h,
891 const char *what,
892 void *target,
893 size_t target_size)
894{
895 return GNUNET_BIO_read (h, what, target, target_size);
896}
897
898
899/**
900 * Create the specification to read a certain amount of bytes.
901 *
902 * @param what describes what is being read (for error message creation)
903 * @param result the buffer to write the result to
904 * @param len the number of bytes to read
905 * @return the read spec
906 */
907struct GNUNET_BIO_ReadSpec
908GNUNET_BIO_read_spec_object (const char *what,
909 void *result,
910 size_t len)
911{
912 struct GNUNET_BIO_ReadSpec rs = {
913 .rh = &read_spec_handler_object,
914 .cls = NULL,
915 .what = what,
916 .target = result,
917 .size = len,
918 };
919
920 return rs;
921}
922
923
924/**
925 * Function used internally to read a string from within a read spec.
926 *
927 * @param cls ignored, always NULL
928 * @param h the IO handle to read from
929 * @param what what is being read (for error message creation)
930 * @param target where to store the data
931 * @param target_size how many bytes to read
932 * @return #GNUNET_OK on success, #GNUNET_SYSERR otherwise
933 */
934static int
935read_spec_handler_string (void *cls,
936 struct GNUNET_BIO_ReadHandle *h,
937 const char *what,
938 void *target,
939 size_t target_size)
940{
941 char **result = target;
942 return GNUNET_BIO_read_string (h, what, result, target_size);
943}
944
945
946/**
947 * Create the specification to read a 0-terminated string.
948 *
949 * @param what describes what is being read (for error message creation)
950 * @param result where to store the pointer to the (allocated) string
951 * (note that *result could be set to NULL as well)
952 * @param max_length maximum allowed length for the string
953 * @return the read spec
954 */
955struct GNUNET_BIO_ReadSpec
956GNUNET_BIO_read_spec_string (const char *what,
957 char **result,
958 size_t max_length)
959{
960 struct GNUNET_BIO_ReadSpec rs = {
961 .rh = &read_spec_handler_string,
962 .cls = NULL,
963 .target = result,
964 .size = max_length,
965 };
966
967 return rs;
968}
969
970
971/**
972 * Function used internally to read an (u)int32_t from within a read spec.
973 *
974 * @param cls ignored, always NULL
975 * @param h the IO handle to read from
976 * @param what what is being read (for error message creation)
977 * @param target where to store the data
978 * @param target_size ignored
979 * @return #GNUNET_OK on success, #GNUNET_SYSERR otherwise
980 */
981static int
982read_spec_handler_int32 (void *cls,
983 struct GNUNET_BIO_ReadHandle *h,
984 const char *what,
985 void *target,
986 size_t target_size)
987{
988 int32_t *result = target;
989 return GNUNET_BIO_read_int32 (h, what, result);
990}
991
992
993/**
994 * Create the specification to read an (u)int32_t.
995 *
996 * @param what describes what is being read (for error message creation)
997 * @param i where to store the data
998 * @return the read spec
999 */
1000struct GNUNET_BIO_ReadSpec
1001GNUNET_BIO_read_spec_int32 (const char *what,
1002 int32_t *i)
1003{
1004 struct GNUNET_BIO_ReadSpec rs = {
1005 .rh = &read_spec_handler_int32,
1006 .cls = NULL,
1007 .target = i,
1008 .size = 0,
1009 };
1010
1011 return rs;
1012}
1013
1014
1015/**
1016 * Function used internally to read an (u)int64_t from within a read spec.
1017 *
1018 * @param cls ignored, always NULL
1019 * @param h the IO handle to read from
1020 * @param what what is being read (for error message creation)
1021 * @param target where to store the data
1022 * @param target_size ignored
1023 * @return #GNUNET_OK on success, #GNUNET_SYSERR otherwise
1024 */
1025static int
1026read_spec_handler_int64 (void *cls,
1027 struct GNUNET_BIO_ReadHandle *h,
1028 const char *what,
1029 void *target,
1030 size_t target_size)
1031{
1032 int64_t *result = target;
1033 return GNUNET_BIO_read_int64 (h, what, result);
1034}
1035
1036
1037/**
1038 * Create the specification to read an (u)int64_t.
1039 *
1040 * @param what describes what is being read (for error message creation)
1041 * @param i where to store the data
1042 * @return the read spec
1043 */
1044struct GNUNET_BIO_ReadSpec
1045GNUNET_BIO_read_spec_int64 (const char *what,
1046 int64_t *i)
1047{
1048 struct GNUNET_BIO_ReadSpec rs = {
1049 .rh = &read_spec_handler_int64,
1050 .cls = NULL,
1051 .target = i,
1052 .size = 0,
1053 };
1054
1055 return rs;
1056}
1057
1058
1059/**
1060 * Create the specification to read a float.
1061 *
1062 * @param what describes what is being read (for error message creation)
1063 * @param f address of float to read
1064 */
1065struct GNUNET_BIO_ReadSpec
1066GNUNET_BIO_read_spec_float (const char *what, float *f)
1067{
1068 struct GNUNET_BIO_ReadSpec rs = {
1069 .rh = &read_spec_handler_int32,
1070 .cls = NULL,
1071 .target = (int32_t *) f,
1072 .size = 0,
1073 };
1074
1075 return rs;
1076}
1077
1078
1079/**
1080 * Create the specification to read a double.
1081 *
1082 * @param what describes what is being read (for error message creation)
1083 * @param f address of double to read
1084 */
1085struct GNUNET_BIO_ReadSpec
1086GNUNET_BIO_read_spec_double (const char *what, double *f)
1087{
1088 struct GNUNET_BIO_ReadSpec rs = {
1089 .rh = &read_spec_handler_int64,
1090 .cls = NULL,
1091 .target = (int64_t *) f,
1092 .size = 0,
1093 };
1094
1095 return rs;
1096}
1097
1098
1099/**
1100 * Execute the read specifications in order.
1101 *
1102 * @param h the IO handle to read from
1103 * @param rs array of read specs
1104 * the last element must be #GNUNET_BIO_read_spec_end
1105 * @return #GNUNET_OK on success, #GNUNET_SYSERR otherwise
1106 */
1107enum GNUNET_GenericReturnValue
1108GNUNET_BIO_read_spec_commit (struct GNUNET_BIO_ReadHandle *h,
1109 struct GNUNET_BIO_ReadSpec *rs)
1110{
1111 int ret = GNUNET_OK;
1112
1113 for (size_t i = 0; NULL!=rs[i].rh; ++i)
1114 {
1115 ret = rs[i].rh (rs[i].cls, h, rs[i].what, rs[i].target, rs[i].size);
1116 if (GNUNET_OK != ret)
1117 return ret;
1118 }
1119
1120 return ret;
1121}
1122
1123
1124/**
1125 * Function used internally to write some bytes from within a write spec.
1126 *
1127 * @param cls ignored, always NULL
1128 * @param h the IO handle to write to
1129 * @param what what is being written (for error message creation)
1130 * @param source the data to write
1131 * @param source_size how many bytes to write
1132 * @return #GNUNET_OK on success, #GNUNET_SYSERR otherwise
1133 */
1134static int
1135write_spec_handler_object (void *cls,
1136 struct GNUNET_BIO_WriteHandle *h,
1137 const char *what,
1138 void *source,
1139 size_t source_size)
1140{
1141 return GNUNET_BIO_write (h, what, source, source_size);
1142}
1143
1144
1145/**
1146 * Create the specification to read some bytes.
1147 *
1148 * @param what describes what is being written (for error message creation)
1149 * @param source the data to write
1150 * @param size how many bytes should be written
1151 * @return the write spec
1152 */
1153struct GNUNET_BIO_WriteSpec
1154GNUNET_BIO_write_spec_object (const char *what,
1155 void *source,
1156 size_t size)
1157{
1158 struct GNUNET_BIO_WriteSpec ws = {
1159 .wh = &write_spec_handler_object,
1160 .cls = NULL,
1161 .what = what,
1162 .source = source,
1163 .source_size = size,
1164 };
1165
1166 return ws;
1167}
1168
1169
1170/**
1171 * Function used internally to write a 0-terminated string from within a write
1172 * spec.
1173 *
1174 * @param cls ignored, always NULL
1175 * @param h the IO handle to write to
1176 * @param what what is being written (for error message creation)
1177 * @param source the data to write
1178 * @param source_size ignored
1179 * @return #GNUNET_OK on success, #GNUNET_SYSERR otherwise
1180 */
1181static int
1182write_spec_handler_string (void *cls,
1183 struct GNUNET_BIO_WriteHandle *h,
1184 const char *what,
1185 void *source,
1186 size_t source_size)
1187{
1188 const char *s = source;
1189 return GNUNET_BIO_write_string (h, what, s);
1190}
1191
1192
1193/**
1194 * Create the specification to write a 0-terminated string.
1195 *
1196 * @param what describes what is being read (for error message creation)
1197 * @param s string to write (can be NULL)
1198 * @return the read spec
1199 */
1200struct GNUNET_BIO_WriteSpec
1201GNUNET_BIO_write_spec_string (const char *what,
1202 const char *s)
1203{
1204 struct GNUNET_BIO_WriteSpec ws = {
1205 .wh = &write_spec_handler_string,
1206 .cls = NULL,
1207 .what = what,
1208 .source = (void *) s,
1209 .source_size = 0,
1210 };
1211
1212 return ws;
1213}
1214
1215
1216/**
1217 * Function used internally to write an (u)int32_t from within a write spec.
1218 *
1219 * @param cls ignored, always NULL
1220 * @param h the IO handle to write to
1221 * @param what what is being written (for error message creation)
1222 * @param source the data to write
1223 * @param source_size ignored
1224 * @return #GNUNET_OK on success, #GNUNET_SYSERR otherwise
1225 */
1226static int
1227write_spec_handler_int32 (void *cls,
1228 struct GNUNET_BIO_WriteHandle *h,
1229 const char *what,
1230 void *source,
1231 size_t source_size)
1232{
1233 int32_t i = *(int32_t *) source;
1234 return GNUNET_BIO_write_int32 (h, what, i);
1235}
1236
1237
1238/**
1239 * Create the specification to write an (u)int32_t.
1240 *
1241 * @param what describes what is being written (for error message creation)
1242 * @param i pointer to a 32-bit integer
1243 * @return the write spec
1244 */
1245struct GNUNET_BIO_WriteSpec
1246GNUNET_BIO_write_spec_int32 (const char *what,
1247 int32_t *i)
1248{
1249 struct GNUNET_BIO_WriteSpec ws = {
1250 .wh = &write_spec_handler_int32,
1251 .cls = NULL,
1252 .what = what,
1253 .source = i,
1254 .source_size = 0,
1255 };
1256
1257 return ws;
1258}
1259
1260
1261/**
1262 * Function used internally to write an (u)int64_t from within a write spec.
1263 *
1264 * @param cls ignored, always NULL
1265 * @param h the IO handle to write to
1266 * @param what what is being written (for error message creation)
1267 * @param source the data to write
1268 * @param source_size ignored
1269 * @return #GNUNET_OK on success, #GNUNET_SYSERR otherwise
1270 */
1271static int
1272write_spec_handler_int64 (void *cls,
1273 struct GNUNET_BIO_WriteHandle *h,
1274 const char *what,
1275 void *source,
1276 size_t source_size)
1277{
1278 int64_t i = *(int64_t *) source;
1279 return GNUNET_BIO_write_int64 (h, what, i);
1280}
1281
1282
1283/**
1284 * Create the specification to write an (u)int64_t.
1285 *
1286 * @param what describes what is being written (for error message creation)
1287 * @param i pointer to a 64-bit integer
1288 * @return the write spec
1289 */
1290struct GNUNET_BIO_WriteSpec
1291GNUNET_BIO_write_spec_int64 (const char *what,
1292 int64_t *i)
1293{
1294 struct GNUNET_BIO_WriteSpec ws = {
1295 .wh = &write_spec_handler_int64,
1296 .cls = NULL,
1297 .what = what,
1298 .source = i,
1299 .source_size = 0,
1300 };
1301
1302 return ws;
1303}
1304
1305
1306/**
1307 * Create the specification to write a float.
1308 *
1309 * @param what describes what is being written (for error message creation)
1310 * @param f pointer to a float
1311 * @return the write spec
1312 */
1313struct GNUNET_BIO_WriteSpec
1314GNUNET_BIO_write_spec_float (const char *what, float *f)
1315{
1316 struct GNUNET_BIO_WriteSpec ws = {
1317 .wh = &write_spec_handler_int32,
1318 .cls = NULL,
1319 .what = what,
1320 .source = (int32_t *) f,
1321 .source_size = 0,
1322 };
1323
1324 return ws;
1325}
1326
1327
1328/**
1329 * Create the specification to write an double.
1330 *
1331 * @param what describes what is being written (for error message creation)
1332 * @param f pointer to a double
1333 * @return the write spec
1334 */
1335struct GNUNET_BIO_WriteSpec
1336GNUNET_BIO_write_spec_double (const char *what, double *f)
1337{
1338 struct GNUNET_BIO_WriteSpec ws = {
1339 .wh = &write_spec_handler_int64,
1340 .cls = NULL,
1341 .what = what,
1342 .source = (int64_t *) f,
1343 .source_size = 0,
1344 };
1345
1346 return ws;
1347}
1348
1349
1350/**
1351 * Execute the write specifications in order.
1352 *
1353 * @param h the IO handle to write to
1354 * @param ws array of write specs
1355 * the last element must be #GNUNET_BIO_write_spec_end
1356 * @return #GNUNET_OK on success, #GNUNET_SYSERR otherwise
1357 */
1358enum GNUNET_GenericReturnValue
1359GNUNET_BIO_write_spec_commit (struct GNUNET_BIO_WriteHandle *h,
1360 struct GNUNET_BIO_WriteSpec *ws)
1361{
1362 int ret = GNUNET_OK;
1363
1364 for (size_t i = 0; NULL!=ws[i].wh; ++i)
1365 {
1366 ret = ws[i].wh (ws[i].cls, h, ws[i].what, ws[i].source, ws[i].source_size);
1367 if (GNUNET_OK != ret)
1368 return ret;
1369 }
1370
1371 /* If it's a file-based handle, the flush makes sure that the data in the
1372 buffer is actually written to the disk. */
1373 if (IO_FILE == h->type)
1374 ret = GNUNET_BIO_flush (h);
1375
1376 return ret;
1377}
1378
1379
1380/* end of bio.c */
diff --git a/src/lib/util/buffer.c b/src/lib/util/buffer.c
new file mode 100644
index 000000000..f88c56849
--- /dev/null
+++ b/src/lib/util/buffer.c
@@ -0,0 +1,283 @@
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 under the
6 terms of the GNU Affero General Public License as published by the Free Software
7 Foundation; either version 3, or (at your option) any later version.
8
9 GNUnet is distributed in the hope that it will be useful, but WITHOUT ANY
10 WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
11 A PARTICULAR PURPOSE. See the GNU Affero General Public License for more details.
12
13 You should have received a copy of the GNU Affero General Public License along with
14 GNUnet; see the file COPYING. If not, see <http://www.gnu.org/licenses/>
15*/
16/**
17 * @file buffer.c
18 * @brief Common buffer management functions.
19 * @author Florian Dold
20 */
21
22#include "platform.h"
23#include "gnunet_util_lib.h"
24
25/**
26 * Initialize a buffer with the given capacity.
27 *
28 * When a buffer is allocated with this function, a warning is logged
29 * when the buffer exceeds the initial capacity.
30 *
31 * @param buf the buffer to initialize
32 * @param capacity the capacity (in bytes) to allocate for @a buf
33 */
34void
35GNUNET_buffer_prealloc (struct GNUNET_Buffer *buf,
36 size_t capacity)
37{
38 /* Buffer should be zero-initialized */
39 GNUNET_assert (0 == buf->mem);
40 GNUNET_assert (0 == buf->capacity);
41 GNUNET_assert (0 == buf->position);
42 buf->mem = GNUNET_malloc (capacity);
43 buf->capacity = capacity;
44 buf->warn_grow = GNUNET_YES;
45}
46
47
48/**
49 * Make sure that at least @a n bytes remaining in the buffer.
50 *
51 * @param buf buffer to potentially grow
52 * @param n number of bytes that should be available to write
53 */
54void
55GNUNET_buffer_ensure_remaining (struct GNUNET_Buffer *buf,
56 size_t n)
57{
58 size_t new_capacity = buf->position + n;
59
60 /* guard against overflow */
61 GNUNET_assert (new_capacity >= buf->position);
62 if (new_capacity <= buf->capacity)
63 return;
64 /* warn if calculation of expected size was wrong */
65 GNUNET_break (GNUNET_YES != buf->warn_grow);
66 if (new_capacity < buf->capacity * 2)
67 new_capacity = buf->capacity * 2;
68 buf->capacity = new_capacity;
69 if (NULL != buf->mem)
70 buf->mem = GNUNET_realloc (buf->mem, new_capacity);
71 else
72 buf->mem = GNUNET_malloc (new_capacity);
73}
74
75
76/**
77 * Write bytes to the buffer.
78 *
79 * Grows the buffer if necessary.
80 *
81 * @param buf buffer to write to
82 * @param data data to read from
83 * @param len number of bytes to copy from @a data to @a buf
84 */
85void
86GNUNET_buffer_write (struct GNUNET_Buffer *buf,
87 const char *data,
88 size_t len)
89{
90 GNUNET_buffer_ensure_remaining (buf, len);
91 memcpy (buf->mem + buf->position, data, len);
92 buf->position += len;
93}
94
95
96/**
97 * Write a 0-terminated string to a buffer, excluding the 0-terminator.
98 *
99 * @param buf the buffer to write to
100 * @param str the string to write to @a buf
101 */
102void
103GNUNET_buffer_write_str (struct GNUNET_Buffer *buf,
104 const char *str)
105{
106 size_t len = strlen (str);
107
108 GNUNET_buffer_write (buf, str, len);
109}
110
111
112/**
113 * Clear the buffer and return the string it contained.
114 * The caller is responsible to eventually #GNUNET_free
115 * the returned string.
116 *
117 * The returned string is always 0-terminated.
118 *
119 * @param buf the buffer to reap the string from
120 * @returns the buffer contained in the string
121 */
122char *
123GNUNET_buffer_reap_str (struct GNUNET_Buffer *buf)
124{
125 char *res;
126
127 /* ensure 0-termination */
128 if ( (0 == buf->position) || ('\0' != buf->mem[buf->position - 1]))
129 {
130 GNUNET_buffer_ensure_remaining (buf, 1);
131 buf->mem[buf->position++] = '\0';
132 }
133 res = buf->mem;
134 memset (buf, 0, sizeof (struct GNUNET_Buffer));
135 return res;
136}
137
138
139/**
140 * Clear the buffer and return its contents.
141 * The caller is responsible to eventually #GNUNET_free
142 * the returned data.
143 *
144 * @param buf the buffer to reap the contents from
145 * @param size where to store the size of the returned data
146 * @returns the data contained in the string
147 */
148void *
149GNUNET_buffer_reap (struct GNUNET_Buffer *buf, size_t *size)
150{
151 *size = buf->position;
152 void *res = buf->mem;
153 memset (buf, 0, sizeof (struct GNUNET_Buffer));
154 return res;
155}
156
157
158/**
159 * Free the backing memory of the given buffer.
160 * Does not free the memory of the buffer control structure,
161 * which is typically stack-allocated.
162 */
163void
164GNUNET_buffer_clear (struct GNUNET_Buffer *buf)
165{
166 GNUNET_free (buf->mem);
167 memset (buf, 0, sizeof (struct GNUNET_Buffer));
168}
169
170
171/**
172 * Write a path component to a buffer, ensuring that
173 * there is exactly one slash between the previous contents
174 * of the buffer and the new string.
175 *
176 * @param buf buffer to write to
177 * @param str string containing the new path component
178 */
179void
180GNUNET_buffer_write_path (struct GNUNET_Buffer *buf, const char *str)
181{
182 size_t len = strlen (str);
183
184 while ( (0 != len) && ('/' == str[0]) )
185 {
186 str++;
187 len--;
188 }
189 if ( (0 == buf->position) || ('/' != buf->mem[buf->position - 1]) )
190 {
191 GNUNET_buffer_ensure_remaining (buf, 1);
192 buf->mem[buf->position++] = '/';
193 }
194 GNUNET_buffer_write (buf, str, len);
195}
196
197
198/**
199 * Write a 0-terminated formatted string to a buffer, excluding the
200 * 0-terminator.
201 *
202 * Grows the buffer if necessary.
203 *
204 * @param buf the buffer to write to
205 * @param fmt format string
206 * @param ... format arguments
207 */
208void
209GNUNET_buffer_write_fstr (struct GNUNET_Buffer *buf, const char *fmt, ...)
210{
211 va_list args;
212
213 va_start (args, fmt);
214 GNUNET_buffer_write_vfstr (buf, fmt, args);
215 va_end (args);
216}
217
218
219/**
220 * Write a 0-terminated formatted string to a buffer, excluding the
221 * 0-terminator.
222 *
223 * Grows the buffer if necessary.
224 *
225 * @param buf the buffer to write to
226 * @param fmt format string
227 * @param args format argument list
228 */
229void
230GNUNET_buffer_write_vfstr (struct GNUNET_Buffer *buf,
231 const char *fmt,
232 va_list args)
233{
234 int res;
235 va_list args2;
236
237 va_copy (args2, args);
238 res = vsnprintf (NULL, 0, fmt, args2);
239 va_end (args2);
240
241 GNUNET_assert (res >= 0);
242 GNUNET_buffer_ensure_remaining (buf, res + 1);
243
244 va_copy (args2, args);
245 res = vsnprintf (buf->mem + buf->position, res + 1, fmt, args2);
246 va_end (args2);
247
248 GNUNET_assert (res >= 0);
249 buf->position += res;
250 GNUNET_assert (buf->position <= buf->capacity);
251}
252
253
254/**
255 * Write data encoded via #GNUNET_STRINGS_data_to_string to the buffer.
256 *
257 * Grows the buffer if necessary.
258 *
259 * @param buf buffer to write to
260 * @param data data to read from
261 * @param data_len number of bytes to copy from @a data to @a buf
262 */
263void
264GNUNET_buffer_write_data_encoded (struct GNUNET_Buffer *buf,
265 const void *data,
266 size_t data_len)
267{
268 size_t outlen = data_len * 8;
269
270 if (outlen % 5 > 0)
271 outlen += 5 - outlen % 5;
272 outlen /= 5;
273 GNUNET_buffer_ensure_remaining (buf,
274 outlen);
275 GNUNET_assert (NULL !=
276 GNUNET_STRINGS_data_to_string (data,
277 data_len,
278 (buf->mem
279 + buf->position),
280 outlen));
281 buf->position += outlen;
282 GNUNET_assert (buf->position <= buf->capacity);
283}
diff --git a/src/lib/util/child_management.c b/src/lib/util/child_management.c
new file mode 100644
index 000000000..e78ebac9f
--- /dev/null
+++ b/src/lib/util/child_management.c
@@ -0,0 +1,240 @@
1/*
2 This file is part of GNUnet
3 Copyright (C) 2021-2023 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/child_management.c
23 * @brief Handling of child processes in GNUnet.
24 * @author Christian Grothoff (ANASTASIS)
25 * @author Dominik Meister (ANASTASIS)
26 * @author t3sserakt
27 */
28
29#include "platform.h"
30#include "gnunet_util_lib.h"
31
32/**
33 * Generic logging shortcut
34 */
35#define LOG(kind, ...) GNUNET_log (kind, __VA_ARGS__)
36
37
38/**
39 * Struct which defines a Child Wait handle
40 */
41struct GNUNET_ChildWaitHandle
42{
43 /**
44 * Linked list to the next child
45 */
46 struct GNUNET_ChildWaitHandle *next;
47 /**
48 * Linked list to the previous child
49 */
50 struct GNUNET_ChildWaitHandle *prev;
51 /**
52 * Child process which is managed
53 */
54 struct GNUNET_OS_Process *proc;
55 /**
56 * Callback which is called upon completion/death of the child task
57 */
58 GNUNET_ChildCompletedCallback cb;
59 /**
60 * Closure for the handle
61 */
62 void *cb_cls;
63};
64
65
66/**
67 * Pipe used to communicate shutdown via signal.
68 */
69static struct GNUNET_DISK_PipeHandle *sigpipe;
70
71static struct GNUNET_SIGNAL_Context *shc_chld;
72
73static struct GNUNET_SCHEDULER_Task *sig_task;
74
75static struct GNUNET_ChildWaitHandle *cwh_head;
76
77static struct GNUNET_ChildWaitHandle *cwh_tail;
78
79/**
80 * Task triggered whenever we receive a SIGCHLD (child
81 * process died) or when user presses CTRL-C.
82 *
83 * @param cls closure, NULL
84 */
85static void
86maint_child_death (void *cls)
87{
88 char buf[16];
89 const struct GNUNET_DISK_FileHandle *pr;
90 struct GNUNET_ChildWaitHandle *nxt;
91
92 (void) cls;
93 sig_task = NULL;
94 /* drain pipe */
95 pr = GNUNET_DISK_pipe_handle (sigpipe,
96 GNUNET_DISK_PIPE_END_READ);
97 GNUNET_assert (! GNUNET_DISK_handle_invalid (pr));
98 (void) GNUNET_DISK_file_read (pr,
99 buf,
100 sizeof(buf));
101
102 /* find applicable processes that exited */
103 for (struct GNUNET_ChildWaitHandle *cwh = cwh_head;
104 NULL != cwh;
105 cwh = nxt)
106 {
107 enum GNUNET_OS_ProcessStatusType type;
108 long unsigned int exit_code = 0;
109
110 nxt = cwh->next;
111 if (GNUNET_OK ==
112 GNUNET_OS_process_status (cwh->proc,
113 &type,
114 &exit_code))
115 {
116 GNUNET_CONTAINER_DLL_remove (cwh_head,
117 cwh_tail,
118 cwh);
119 cwh->cb (cwh->cb_cls,
120 type,
121 exit_code);
122 GNUNET_free (cwh);
123 }
124 }
125 if (NULL == cwh_head)
126 return;
127 /* wait for more */
128 sig_task = GNUNET_SCHEDULER_add_read_file (
129 GNUNET_TIME_UNIT_FOREVER_REL,
130 GNUNET_DISK_pipe_handle (sigpipe,
131 GNUNET_DISK_PIPE_END_READ),
132 &maint_child_death,
133 NULL);
134}
135
136
137/**
138 * Signal handler called for SIGCHLD. Triggers the
139 * respective handler by writing to the trigger pipe.
140 */
141static void
142sighandler_child_death (void)
143{
144 static char c;
145 int old_errno = errno; /* back-up errno */
146
147 GNUNET_break (
148 1 ==
149 GNUNET_DISK_file_write (GNUNET_DISK_pipe_handle (sigpipe,
150 GNUNET_DISK_PIPE_END_WRITE),
151 &c,
152 sizeof(c)));
153 errno = old_errno; /* restore errno */
154}
155
156
157/**
158 * Initializing the signal pipe for child handling.
159 */
160static void
161child_management_start (void)
162{
163 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
164 "Trying to start child management.\n");
165 if (NULL != sigpipe)
166 return; /* already initialized */
167 sigpipe = GNUNET_DISK_pipe (GNUNET_DISK_PF_NONE);
168 GNUNET_assert (sigpipe != NULL);
169 shc_chld =
170 GNUNET_SIGNAL_handler_install (GNUNET_SIGCHLD,
171 &sighandler_child_death);
172 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
173 "Child management started.\n");
174}
175
176
177/**
178 * Clean up.
179 */
180static void
181child_management_done (void)
182{
183 if (NULL != sig_task)
184 {
185 GNUNET_SCHEDULER_cancel (sig_task);
186 sig_task = NULL;
187 }
188 GNUNET_SIGNAL_handler_uninstall (shc_chld);
189 shc_chld = NULL;
190 GNUNET_DISK_pipe_close (sigpipe);
191 sigpipe = NULL;
192 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
193 "Child management stopped.\n");
194}
195
196
197struct GNUNET_ChildWaitHandle *
198GNUNET_wait_child (struct GNUNET_OS_Process *proc,
199 GNUNET_ChildCompletedCallback cb,
200 void *cb_cls)
201{
202 struct GNUNET_ChildWaitHandle *cwh;
203 bool may_race = (NULL == sigpipe);
204
205 child_management_start ();
206 cwh = GNUNET_new (struct GNUNET_ChildWaitHandle);
207 cwh->proc = proc;
208 cwh->cb = cb;
209 cwh->cb_cls = cb_cls;
210 GNUNET_CONTAINER_DLL_insert (cwh_head,
211 cwh_tail,
212 cwh);
213 if (NULL == sig_task)
214 {
215 sig_task = GNUNET_SCHEDULER_add_read_file (
216 GNUNET_TIME_UNIT_FOREVER_REL,
217 GNUNET_DISK_pipe_handle (sigpipe,
218 GNUNET_DISK_PIPE_END_READ),
219 &maint_child_death,
220 NULL);
221 }
222 /* Handle race-condition case where the child terminated just before we
223 installed the signal handler and thus we missed the signal. */
224 if (may_race)
225 sighandler_child_death ();
226 return cwh;
227}
228
229
230void
231GNUNET_wait_child_cancel (struct GNUNET_ChildWaitHandle *cwh)
232{
233 GNUNET_CONTAINER_DLL_remove (cwh_head,
234 cwh_tail,
235 cwh);
236 GNUNET_free (cwh);
237 if (NULL != cwh_head)
238 return;
239 child_management_done ();
240}
diff --git a/src/lib/util/child_management_test.sh b/src/lib/util/child_management_test.sh
new file mode 100755
index 000000000..a35b865f3
--- /dev/null
+++ b/src/lib/util/child_management_test.sh
@@ -0,0 +1,2 @@
1#!/usr/bin/env bash
2echo "$1$2" >> child_management_test.txt
diff --git a/src/lib/util/client.c b/src/lib/util/client.c
new file mode 100644
index 000000000..fb2120ee8
--- /dev/null
+++ b/src/lib/util/client.c
@@ -0,0 +1,1112 @@
1/*
2 This file is part of GNUnet.
3 Copyright (C) 2001-2016, 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 util/client.c
23 * @brief code for access to services
24 * @author Christian Grothoff
25 *
26 * Generic TCP code for reliable, record-oriented TCP
27 * connections between clients and service providers.
28 */
29
30#include "platform.h"
31#include "gnunet_protocols.h"
32#include "gnunet_util_lib.h"
33#include "gnunet_resolver_service.h"
34#include "gnunet_socks.h"
35
36
37#define LOG(kind, ...) GNUNET_log_from (kind, "util-client", __VA_ARGS__)
38
39/**
40 * Timeout we use on TCP connect before trying another
41 * result from the DNS resolver. Actual value used
42 * is this value divided by the number of address families.
43 * Default is 5s.
44 */
45#define CONNECT_RETRY_TIMEOUT GNUNET_TIME_relative_multiply ( \
46 GNUNET_TIME_UNIT_SECONDS, 5)
47
48
49/**
50 * Internal state for a client connected to a GNUnet service.
51 */
52struct ClientState;
53
54
55/**
56 * During connect, we try multiple possible IP addresses
57 * to find out which one might work.
58 */
59struct AddressProbe
60{
61 /**
62 * This is a linked list.
63 */
64 struct AddressProbe *next;
65
66 /**
67 * This is a doubly-linked list.
68 */
69 struct AddressProbe *prev;
70
71 /**
72 * The address; do not free (allocated at the end of this struct).
73 */
74 const struct sockaddr *addr;
75
76 /**
77 * Underlying OS's socket.
78 */
79 struct GNUNET_NETWORK_Handle *sock;
80
81 /**
82 * Connection for which we are probing.
83 */
84 struct ClientState *cstate;
85
86 /**
87 * Length of addr.
88 */
89 socklen_t addrlen;
90
91 /**
92 * Task waiting for the connection to finish connecting.
93 */
94 struct GNUNET_SCHEDULER_Task *task;
95};
96
97
98/**
99 * Internal state for a client connected to a GNUnet service.
100 */
101struct ClientState
102{
103 /**
104 * The connection handle, NULL if not live
105 */
106 struct GNUNET_NETWORK_Handle *sock;
107
108 /**
109 * Handle to a pending DNS lookup request, NULL if DNS is finished.
110 */
111 struct GNUNET_RESOLVER_RequestHandle *dns_active;
112
113 /**
114 * Our configuration.
115 */
116 const struct GNUNET_CONFIGURATION_Handle *cfg;
117
118 /**
119 * Linked list of sockets we are currently trying out
120 * (during connect).
121 */
122 struct AddressProbe *ap_head;
123
124 /**
125 * Linked list of sockets we are currently trying out
126 * (during connect).
127 */
128 struct AddressProbe *ap_tail;
129
130 /**
131 * Name of the service we interact with.
132 */
133 char *service_name;
134
135 /**
136 * Hostname, if any.
137 */
138 char *hostname;
139
140 /**
141 * Next message to transmit to the service. NULL for none.
142 */
143 const struct GNUNET_MessageHeader *msg;
144
145 /**
146 * Task for trying to connect to the service.
147 */
148 struct GNUNET_SCHEDULER_Task *retry_task;
149
150 /**
151 * Task for sending messages to the service.
152 */
153 struct GNUNET_SCHEDULER_Task *send_task;
154
155 /**
156 * Task for sending messages to the service.
157 */
158 struct GNUNET_SCHEDULER_Task *recv_task;
159
160 /**
161 * Tokenizer for inbound messages.
162 */
163 struct GNUNET_MessageStreamTokenizer *mst;
164
165 /**
166 * Message queue under our control.
167 */
168 struct GNUNET_MQ_Handle *mq;
169
170 /**
171 * Timeout for receiving a response (absolute time).
172 */
173 struct GNUNET_TIME_Absolute receive_timeout;
174
175 /**
176 * Current value for our incremental back-off (for
177 * connect re-tries).
178 */
179 struct GNUNET_TIME_Relative back_off;
180
181 /**
182 * TCP port (0 for disabled).
183 */
184 unsigned long long port;
185
186 /**
187 * Offset in the message where we are for transmission.
188 */
189 size_t msg_off;
190
191 /**
192 * How often have we tried to connect?
193 */
194 unsigned int attempts;
195
196 /**
197 * Are we supposed to die? #GNUNET_SYSERR if destruction must be
198 * deferred, #GNUNET_NO by default, #GNUNET_YES if destruction was
199 * deferred.
200 */
201 int in_destroy;
202};
203
204
205/**
206 * Try to connect to the service.
207 *
208 * @param cls the `struct ClientState` to try to connect to the service
209 */
210static void
211start_connect (void *cls);
212
213
214/**
215 * We've failed for good to establish a connection (timeout or
216 * no more addresses to try).
217 *
218 * @param cstate the connection we tried to establish
219 */
220static void
221connect_fail_continuation (struct ClientState *cstate)
222{
223 GNUNET_break (NULL == cstate->ap_head);
224 GNUNET_break (NULL == cstate->ap_tail);
225 GNUNET_break (NULL == cstate->dns_active);
226 GNUNET_break (NULL == cstate->sock);
227 GNUNET_assert (NULL == cstate->send_task);
228 GNUNET_assert (NULL == cstate->recv_task);
229 // GNUNET_assert (NULL == cstate->proxy_handshake);
230
231 cstate->back_off = GNUNET_TIME_STD_BACKOFF (cstate->back_off);
232 LOG (GNUNET_ERROR_TYPE_DEBUG,
233 "Failed to establish connection to `%s', no further addresses to try, will try again in %s.\n",
234 cstate->service_name,
235 GNUNET_STRINGS_relative_time_to_string (cstate->back_off,
236 GNUNET_YES));
237 cstate->retry_task
238 = GNUNET_SCHEDULER_add_delayed (cstate->back_off,
239 &start_connect,
240 cstate);
241}
242
243
244/**
245 * We are ready to send a message to the service.
246 *
247 * @param cls the `struct ClientState` with the `msg` to transmit
248 */
249static void
250transmit_ready (void *cls)
251{
252 struct ClientState *cstate = cls;
253 ssize_t ret;
254 size_t len;
255 const char *pos;
256 int notify_in_flight;
257
258 cstate->send_task = NULL;
259 if (GNUNET_YES == cstate->in_destroy)
260 return;
261 pos = (const char *) cstate->msg;
262 len = ntohs (cstate->msg->size);
263 GNUNET_assert (cstate->msg_off < len);
264 LOG (GNUNET_ERROR_TYPE_DEBUG,
265 "message of type %u and size %u trying to send with socket %p (MQ: %p\n",
266 ntohs (cstate->msg->type),
267 ntohs (cstate->msg->size),
268 cstate->sock,
269 cstate->mq);
270
271RETRY:
272 ret = GNUNET_NETWORK_socket_send (cstate->sock,
273 &pos[cstate->msg_off],
274 len - cstate->msg_off);
275 if ( (-1 == ret) &&
276 ( (EAGAIN == errno) ||
277 (EINTR == errno) ) )
278 {
279 /* ignore */
280 ret = 0;
281 }
282 if (-1 == ret)
283 {
284 LOG (GNUNET_ERROR_TYPE_WARNING,
285 "Error during sending message of type %u: %s\n",
286 ntohs (cstate->msg->type),
287 strerror (errno));
288 if (EINTR == errno)
289 {
290 LOG (GNUNET_ERROR_TYPE_DEBUG,
291 "Retrying message of type %u\n",
292 ntohs (cstate->msg->type));
293 goto RETRY;
294 }
295 GNUNET_MQ_inject_error (cstate->mq,
296 GNUNET_MQ_ERROR_WRITE);
297 return;
298 }
299 notify_in_flight = (0 == cstate->msg_off);
300 cstate->msg_off += ret;
301 if (cstate->msg_off < len)
302 {
303 LOG (GNUNET_ERROR_TYPE_DEBUG,
304 "rescheduling message of type %u\n",
305 ntohs (cstate->msg->type));
306 cstate->send_task
307 = GNUNET_SCHEDULER_add_write_net (GNUNET_TIME_UNIT_FOREVER_REL,
308 cstate->sock,
309 &transmit_ready,
310 cstate);
311 if (notify_in_flight)
312 GNUNET_MQ_impl_send_in_flight (cstate->mq);
313 return;
314 }
315 LOG (GNUNET_ERROR_TYPE_DEBUG,
316 "sending message of type %u and size %u successful\n",
317 ntohs (cstate->msg->type),
318 ntohs (cstate->msg->size));
319 cstate->msg = NULL;
320 GNUNET_MQ_impl_send_continue (cstate->mq);
321}
322
323
324/**
325 * We have received a full message, pass to the MQ dispatcher.
326 * Called by the tokenizer via #receive_ready().
327 *
328 * @param cls the `struct ClientState`
329 * @param msg message we received.
330 * @return #GNUNET_OK on success,
331 * #GNUNET_NO to stop further processing due to disconnect (no error)
332 * #GNUNET_SYSERR to stop further processing due to error
333 */
334static enum GNUNET_GenericReturnValue
335recv_message (void *cls,
336 const struct GNUNET_MessageHeader *msg)
337{
338 struct ClientState *cstate = cls;
339
340 if (GNUNET_YES == cstate->in_destroy)
341 return GNUNET_NO;
342 LOG (GNUNET_ERROR_TYPE_DEBUG,
343 "Received message of type %u and size %u from %s\n",
344 ntohs (msg->type),
345 ntohs (msg->size),
346 cstate->service_name);
347 GNUNET_MQ_inject_message (cstate->mq,
348 msg);
349 if (GNUNET_YES == cstate->in_destroy)
350 return GNUNET_NO;
351 return GNUNET_OK;
352}
353
354
355/**
356 * Cancel all remaining connect attempts
357 *
358 * @param cstate handle of the client state to process
359 */
360static void
361cancel_aps (struct ClientState *cstate)
362{
363 struct AddressProbe *pos;
364
365 while (NULL != (pos = cstate->ap_head))
366 {
367 GNUNET_break (GNUNET_OK ==
368 GNUNET_NETWORK_socket_close (pos->sock));
369 GNUNET_SCHEDULER_cancel (pos->task);
370 GNUNET_CONTAINER_DLL_remove (cstate->ap_head,
371 cstate->ap_tail,
372 pos);
373 GNUNET_free (pos);
374 }
375}
376
377
378/**
379 * Implement the destruction of a message queue. Implementations must
380 * not free @a mq, but should take care of @a impl_state.
381 *
382 * @param mq the message queue to destroy
383 * @param impl_state our `struct ClientState`
384 */
385static void
386connection_client_destroy_impl (struct GNUNET_MQ_Handle *mq,
387 void *impl_state)
388{
389 struct ClientState *cstate = impl_state;
390
391 (void) mq;
392 if (NULL != cstate->dns_active)
393 {
394 GNUNET_RESOLVER_request_cancel (cstate->dns_active);
395 cstate->dns_active = NULL;
396 }
397 if (NULL != cstate->send_task)
398 {
399 GNUNET_SCHEDULER_cancel (cstate->send_task);
400 cstate->send_task = NULL;
401 }
402 if (NULL != cstate->retry_task)
403 {
404 GNUNET_SCHEDULER_cancel (cstate->retry_task);
405 cstate->retry_task = NULL;
406 }
407 if (GNUNET_SYSERR == cstate->in_destroy)
408 {
409 /* defer destruction */
410 cstate->in_destroy = GNUNET_YES;
411 cstate->mq = NULL;
412 return;
413 }
414 if (NULL != cstate->recv_task)
415 {
416 GNUNET_SCHEDULER_cancel (cstate->recv_task);
417 cstate->recv_task = NULL;
418 }
419 if (NULL != cstate->sock)
420 {
421 LOG (GNUNET_ERROR_TYPE_DEBUG,
422 "destroying socket: %p\n",
423 cstate->sock);
424 GNUNET_NETWORK_socket_close (cstate->sock);
425 }
426 cancel_aps (cstate);
427 GNUNET_free (cstate->service_name);
428 GNUNET_free (cstate->hostname);
429 GNUNET_MST_destroy (cstate->mst);
430 GNUNET_free (cstate);
431}
432
433
434/**
435 * This function is called once we have data ready to read.
436 *
437 * @param cls `struct ClientState` with connection to read from
438 */
439static void
440receive_ready (void *cls)
441{
442 struct ClientState *cstate = cls;
443 enum GNUNET_GenericReturnValue ret;
444
445 cstate->recv_task = NULL;
446 cstate->in_destroy = GNUNET_SYSERR;
447 ret = GNUNET_MST_read (cstate->mst,
448 cstate->sock,
449 GNUNET_NO,
450 GNUNET_NO);
451 if (GNUNET_SYSERR == ret)
452 {
453 if (NULL != cstate->mq)
454 GNUNET_MQ_inject_error (cstate->mq,
455 GNUNET_MQ_ERROR_READ);
456 if (GNUNET_YES == cstate->in_destroy)
457 connection_client_destroy_impl (cstate->mq,
458 cstate);
459 return;
460 }
461 if (GNUNET_YES == cstate->in_destroy)
462 {
463 connection_client_destroy_impl (cstate->mq,
464 cstate);
465 return;
466 }
467 cstate->in_destroy = GNUNET_NO;
468 GNUNET_assert (NULL == cstate->recv_task);
469 cstate->recv_task
470 = GNUNET_SCHEDULER_add_read_net (GNUNET_TIME_UNIT_FOREVER_REL,
471 cstate->sock,
472 &receive_ready,
473 cstate);
474}
475
476
477/**
478 * We've succeeded in establishing a connection.
479 *
480 * @param cstate the connection we tried to establish
481 */
482static void
483connect_success_continuation (struct ClientState *cstate)
484{
485 GNUNET_assert (NULL == cstate->recv_task);
486 cstate->recv_task
487 = GNUNET_SCHEDULER_add_read_net (GNUNET_TIME_UNIT_FOREVER_REL,
488 cstate->sock,
489 &receive_ready,
490 cstate);
491 if (NULL != cstate->msg)
492 {
493 GNUNET_assert (NULL == cstate->send_task);
494 cstate->send_task
495 = GNUNET_SCHEDULER_add_write_net (GNUNET_TIME_UNIT_FOREVER_REL,
496 cstate->sock,
497 &transmit_ready,
498 cstate);
499 }
500}
501
502
503/**
504 * Try connecting to the server using UNIX domain sockets.
505 *
506 * @param service_name name of service to connect to
507 * @param cfg configuration to use
508 * @return NULL on error, socket connected to UNIX otherwise
509 */
510static struct GNUNET_NETWORK_Handle *
511try_unixpath (const char *service_name,
512 const struct GNUNET_CONFIGURATION_Handle *cfg)
513{
514#if AF_UNIX
515 struct GNUNET_NETWORK_Handle *sock;
516 char *unixpath;
517 struct sockaddr_un s_un;
518
519 unixpath = NULL;
520 if ((GNUNET_OK ==
521 GNUNET_CONFIGURATION_get_value_filename (cfg,
522 service_name,
523 "UNIXPATH",
524 &unixpath)) &&
525 (0 < strlen (unixpath)))
526 {
527 /* We have a non-NULL unixpath, need to validate it */
528 if (strlen (unixpath) >= sizeof(s_un.sun_path))
529 {
530 LOG (GNUNET_ERROR_TYPE_WARNING,
531 _ ("UNIXPATH `%s' too long, maximum length is %llu\n"),
532 unixpath,
533 (unsigned long long) sizeof(s_un.sun_path));
534 unixpath = GNUNET_NETWORK_shorten_unixpath (unixpath);
535 LOG (GNUNET_ERROR_TYPE_INFO,
536 _ ("Using `%s' instead\n"),
537 unixpath);
538 if (NULL == unixpath)
539 return NULL;
540 }
541 memset (&s_un,
542 0,
543 sizeof(s_un));
544 s_un.sun_family = AF_UNIX;
545 GNUNET_strlcpy (s_un.sun_path,
546 unixpath,
547 sizeof(s_un.sun_path));
548#if HAVE_SOCKADDR_UN_SUN_LEN
549 s_un.sun_len = (u_char) sizeof(struct sockaddr_un);
550#endif
551 sock = GNUNET_NETWORK_socket_create (AF_UNIX,
552 SOCK_STREAM,
553 0);
554 if ((NULL != sock) &&
555 ((GNUNET_OK ==
556 GNUNET_NETWORK_socket_connect (sock,
557 (struct sockaddr *) &s_un,
558 sizeof(s_un))) ||
559 (EINPROGRESS == errno)))
560 {
561 LOG (GNUNET_ERROR_TYPE_DEBUG,
562 "Successfully connected to unixpath `%s'!\n",
563 unixpath);
564 GNUNET_free (unixpath);
565 return sock;
566 }
567 if (NULL != sock)
568 GNUNET_NETWORK_socket_close (sock);
569 }
570 GNUNET_free (unixpath);
571#endif
572 return NULL;
573}
574
575
576/**
577 * Scheduler let us know that we're either ready to write on the
578 * socket OR connect timed out. Do the right thing.
579 *
580 * @param cls the `struct AddressProbe *` with the address that we are probing
581 */
582static void
583connect_probe_continuation (void *cls)
584{
585 struct AddressProbe *ap = cls;
586 struct ClientState *cstate = ap->cstate;
587 const struct GNUNET_SCHEDULER_TaskContext *tc;
588 int error;
589 socklen_t len;
590
591 ap->task = NULL;
592 GNUNET_assert (NULL != ap->sock);
593 GNUNET_CONTAINER_DLL_remove (cstate->ap_head,
594 cstate->ap_tail,
595 ap);
596 len = sizeof(error);
597 error = 0;
598 tc = GNUNET_SCHEDULER_get_task_context ();
599 if ((0 == (tc->reason & GNUNET_SCHEDULER_REASON_WRITE_READY)) ||
600 (GNUNET_OK !=
601 GNUNET_NETWORK_socket_getsockopt (ap->sock,
602 SOL_SOCKET,
603 SO_ERROR,
604 &error,
605 &len)) ||
606 (0 != error))
607 {
608 GNUNET_break (GNUNET_OK ==
609 GNUNET_NETWORK_socket_close (ap->sock));
610 GNUNET_free (ap);
611 if ((NULL == cstate->ap_head) &&
612 // (NULL == cstate->proxy_handshake) &&
613 (NULL == cstate->dns_active))
614 connect_fail_continuation (cstate);
615 return;
616 }
617 LOG (GNUNET_ERROR_TYPE_DEBUG,
618 "Connection to `%s' succeeded!\n",
619 cstate->service_name);
620 /* trigger jobs that waited for the connection */
621 GNUNET_assert (NULL == cstate->sock);
622 cstate->sock = ap->sock;
623 GNUNET_free (ap);
624 cancel_aps (cstate);
625 connect_success_continuation (cstate);
626}
627
628
629/**
630 * Try to establish a connection given the specified address.
631 * This function is called by the resolver once we have a DNS reply.
632 *
633 * @param cls our `struct ClientState *`
634 * @param addr address to try, NULL for "last call"
635 * @param addrlen length of @a addr
636 */
637static void
638try_connect_using_address (void *cls,
639 const struct sockaddr *addr,
640 socklen_t addrlen)
641{
642 struct ClientState *cstate = cls;
643 struct AddressProbe *ap;
644
645 if (NULL == addr)
646 {
647 cstate->dns_active = NULL;
648 if ((NULL == cstate->ap_head) &&
649 // (NULL == cstate->proxy_handshake) &&
650 (NULL == cstate->sock))
651 connect_fail_continuation (cstate);
652 return;
653 }
654 if (NULL != cstate->sock)
655 return; /* already connected */
656 /* try to connect */
657 LOG (GNUNET_ERROR_TYPE_DEBUG,
658 "Trying to connect using address `%s:%u'\n",
659 GNUNET_a2s (addr,
660 addrlen),
661 (unsigned int) cstate->port);
662 ap = GNUNET_malloc (sizeof(struct AddressProbe) + addrlen);
663 ap->addr = (const struct sockaddr *) &ap[1];
664 GNUNET_memcpy (&ap[1],
665 addr,
666 addrlen);
667 ap->addrlen = addrlen;
668 ap->cstate = cstate;
669
670 switch (ap->addr->sa_family)
671 {
672 case AF_INET:
673 ((struct sockaddr_in *) ap->addr)->sin_port = htons (cstate->port);
674 break;
675
676 case AF_INET6:
677 ((struct sockaddr_in6 *) ap->addr)->sin6_port = htons (cstate->port);
678 break;
679
680 default:
681 GNUNET_break (0);
682 GNUNET_free (ap);
683 return; /* not supported by us */
684 }
685 ap->sock = GNUNET_NETWORK_socket_create (ap->addr->sa_family,
686 SOCK_STREAM,
687 0);
688 if (NULL == ap->sock)
689 {
690 GNUNET_free (ap);
691 return; /* not supported by OS */
692 }
693 if ((GNUNET_OK !=
694 GNUNET_NETWORK_socket_connect (ap->sock,
695 ap->addr,
696 ap->addrlen)) &&
697 (EINPROGRESS != errno))
698 {
699 /* maybe refused / unsupported address, try next */
700 GNUNET_log_strerror (GNUNET_ERROR_TYPE_INFO,
701 "connect");
702 GNUNET_break (GNUNET_OK ==
703 GNUNET_NETWORK_socket_close (ap->sock));
704 GNUNET_free (ap);
705 return;
706 }
707 GNUNET_CONTAINER_DLL_insert (cstate->ap_head,
708 cstate->ap_tail,
709 ap);
710 ap->task = GNUNET_SCHEDULER_add_write_net (CONNECT_RETRY_TIMEOUT,
711 ap->sock,
712 &connect_probe_continuation,
713 ap);
714}
715
716
717/**
718 * Test whether the configuration has proper values for connection
719 * (UNIXPATH || (PORT && HOSTNAME)).
720 *
721 * @param service_name name of service to connect to
722 * @param cfg configuration to use
723 * @return #GNUNET_OK if the configuration is valid, #GNUNET_SYSERR if not
724 */
725static int
726test_service_configuration (const char *service_name,
727 const struct GNUNET_CONFIGURATION_Handle *cfg)
728{
729 int ret = GNUNET_SYSERR;
730 char *hostname = NULL;
731 unsigned long long port;
732
733#if AF_UNIX
734 char *unixpath = NULL;
735
736 if ((GNUNET_OK ==
737 GNUNET_CONFIGURATION_get_value_filename (cfg,
738 service_name,
739 "UNIXPATH",
740 &unixpath)) &&
741 (0 < strlen (unixpath)))
742 ret = GNUNET_OK;
743 else if ((GNUNET_OK ==
744 GNUNET_CONFIGURATION_have_value (cfg,
745 service_name,
746 "UNIXPATH")))
747 {
748 GNUNET_log_config_invalid (GNUNET_ERROR_TYPE_ERROR,
749 service_name,
750 "UNIXPATH",
751 _ ("not a valid filename"));
752 GNUNET_free (unixpath);
753 return GNUNET_SYSERR; /* UNIXPATH specified but invalid! */
754 }
755 GNUNET_free (unixpath);
756#endif
757
758 if ((GNUNET_YES ==
759 GNUNET_CONFIGURATION_have_value (cfg,
760 service_name,
761 "PORT")) &&
762 (GNUNET_OK ==
763 GNUNET_CONFIGURATION_get_value_number (cfg,
764 service_name,
765 "PORT",
766 &port)) &&
767 (port <= 65535) &&
768 (0 != port) &&
769 (GNUNET_OK ==
770 GNUNET_CONFIGURATION_get_value_string (cfg,
771 service_name,
772 "HOSTNAME",
773 &hostname)) &&
774 (0 != strlen (hostname)))
775 ret = GNUNET_OK;
776 GNUNET_free (hostname);
777 return ret;
778}
779
780
781/**
782 * Try to connect to the service.
783 *
784 * @param cls the `struct ClientState` to try to connect to the service
785 */
786static void
787start_connect (void *cls)
788{
789 struct ClientState *cstate = cls;
790
791 cstate->retry_task = NULL;
792#if 0
793 /* Never use a local source if a proxy is configured */
794 if (GNUNET_YES ==
795 GNUNET_SOCKS_check_service (cstate->service_name,
796 cstate->cfg))
797 {
798 socks_connect (cstate);
799 return;
800 }
801#endif
802
803 if ((0 == (cstate->attempts++ % 2)) ||
804 (0 == cstate->port) ||
805 (NULL == cstate->hostname))
806 {
807 /* on even rounds, try UNIX first, or always
808 if we do not have a DNS name and TCP port. */
809 cstate->sock = try_unixpath (cstate->service_name,
810 cstate->cfg);
811 if (NULL != cstate->sock)
812 {
813 connect_success_continuation (cstate);
814 return;
815 }
816 }
817 if ((NULL == cstate->hostname) ||
818 (0 == cstate->port))
819 {
820 /* All options failed. Boo! */
821 connect_fail_continuation (cstate);
822 return;
823 }
824 cstate->dns_active
825 = GNUNET_RESOLVER_ip_get (cstate->hostname,
826 AF_UNSPEC,
827 CONNECT_RETRY_TIMEOUT,
828 &try_connect_using_address,
829 cstate);
830}
831
832
833/**
834 * Implements the transmission functionality of a message queue.
835 *
836 * @param mq the message queue
837 * @param msg the message to send
838 * @param impl_state our `struct ClientState`
839 */
840static void
841connection_client_send_impl (struct GNUNET_MQ_Handle *mq,
842 const struct GNUNET_MessageHeader *msg,
843 void *impl_state)
844{
845 struct ClientState *cstate = impl_state;
846
847 (void) mq;
848 /* only one message at a time allowed */
849 GNUNET_assert (NULL == cstate->msg);
850 GNUNET_assert (NULL == cstate->send_task);
851 cstate->msg = msg;
852 cstate->msg_off = 0;
853 if (NULL == cstate->sock)
854 {
855 LOG (GNUNET_ERROR_TYPE_DEBUG,
856 "message of type %u waiting for socket\n",
857 ntohs (msg->type));
858 return; /* still waiting for connection */
859 }
860 cstate->send_task
861 = GNUNET_SCHEDULER_add_now (&transmit_ready,
862 cstate);
863}
864
865
866/**
867 * Cancel the currently sent message.
868 *
869 * @param mq message queue
870 * @param impl_state our `struct ClientState`
871 */
872static void
873connection_client_cancel_impl (struct GNUNET_MQ_Handle *mq,
874 void *impl_state)
875{
876 struct ClientState *cstate = impl_state;
877
878 (void) mq;
879 GNUNET_assert (NULL != cstate->msg);
880 GNUNET_assert (0 == cstate->msg_off);
881 cstate->msg = NULL;
882 if (NULL != cstate->send_task)
883 {
884 GNUNET_SCHEDULER_cancel (cstate->send_task);
885 cstate->send_task = NULL;
886 }
887}
888
889
890/**
891 * Test if the port or UNIXPATH of the given @a service_name
892 * is in use and thus (most likely) the respective service is up.
893 *
894 * @param cfg our configuration
895 * @param service_name name of the service to connect to
896 * @return #GNUNET_YES if the service is (likely) up,
897 * #GNUNET_NO if the service is (definitively) down,
898 * #GNUNET_SYSERR if the configuration does not give us
899 * the necessary information about the service, or if
900 * we could not check (e.g. socket() failed)
901 */
902int
903GNUNET_CLIENT_test (const struct GNUNET_CONFIGURATION_Handle *cfg,
904 const char *service_name)
905{
906 char *hostname = NULL;
907 unsigned long long port;
908 int ret;
909
910#if AF_UNIX
911 {
912 char *unixpath = NULL;
913
914 if (GNUNET_OK ==
915 GNUNET_CONFIGURATION_get_value_filename (cfg,
916 service_name,
917 "UNIXPATH",
918 &unixpath))
919 {
920 if (0 == strlen (unixpath))
921 {
922 GNUNET_free (unixpath);
923 return GNUNET_SYSERR; /* empty string not OK */
924 }
925 if (0 == access (unixpath,
926 F_OK))
927 {
928 GNUNET_free (unixpath);
929 return GNUNET_OK; /* file exists, we assume service is running */
930 }
931 GNUNET_free (unixpath);
932 }
933 else if (GNUNET_OK ==
934 GNUNET_CONFIGURATION_have_value (cfg,
935 service_name,
936 "UNIXPATH"))
937 {
938 /* UNIXPATH specified but not a valid path! */
939 GNUNET_log_config_invalid (GNUNET_ERROR_TYPE_ERROR,
940 service_name,
941 "UNIXPATH",
942 _ ("not a valid filename"));
943 return GNUNET_SYSERR;
944 }
945 }
946#endif
947
948 if ( (GNUNET_OK !=
949 GNUNET_CONFIGURATION_get_value_number (cfg,
950 service_name,
951 "PORT",
952 &port)) ||
953 (port > 65535) ||
954 (0 == port) )
955 {
956 return GNUNET_SYSERR;
957 }
958 if (GNUNET_OK ==
959 GNUNET_CONFIGURATION_get_value_string (cfg,
960 service_name,
961 "HOSTNAME",
962 &hostname))
963 {
964 /* We always assume remotes are up */
965 ret = GNUNET_YES;
966 }
967 else
968 {
969 /* We look for evidence the service is up */
970 ret = GNUNET_NO;
971 }
972 if ( (NULL == hostname) ||
973 (0 == strcasecmp (hostname,
974 "localhost")) ||
975 (0 == strcasecmp (hostname,
976 "ip6-localnet")) )
977 {
978 /* service runs on loopback */
979 struct sockaddr_in v4;
980 struct sockaddr_in6 v6;
981 int sock;
982
983 memset (&v4, 0, sizeof (v4));
984 memset (&v6, 0, sizeof (v6));
985 v4.sin_family = AF_INET;
986 v4.sin_port = htons ((uint16_t) port);
987#if HAVE_SOCKADDR_IN_SUN_LEN
988 v4.sin_len = (u_char) sizeof(struct sockaddr_in);
989#endif
990 GNUNET_assert (1 == inet_pton (AF_INET,
991 "127.0.0.1",
992 &v4.sin_addr));
993 ret = GNUNET_NO;
994 sock = socket (AF_INET,
995 SOCK_STREAM,
996 0);
997 if (-1 != sock)
998 {
999 if (0 != bind (sock,
1000 (struct sockaddr *) &v4,
1001 sizeof (v4)))
1002 {
1003 /* bind failed, so someone is listening! */
1004 ret = GNUNET_YES;
1005 }
1006 (void) close (sock);
1007 }
1008 else
1009 {
1010 GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING,
1011 "socket");
1012 if (GNUNET_NO == ret)
1013 ret = GNUNET_SYSERR;
1014 }
1015 v6.sin6_family = AF_INET6;
1016 v6.sin6_port = htons ((uint16_t) port);
1017#if HAVE_SOCKADDR_IN_SUN_LEN
1018 v6.sin6_len = (u_char) sizeof(struct sockaddr_in6);
1019#endif
1020 inet_pton (AF_INET6,
1021 "::1",
1022 &v6.sin6_addr);
1023 sock = socket (AF_INET6,
1024 SOCK_STREAM,
1025 0);
1026 if (-1 != sock)
1027 {
1028 if (0 != bind (sock,
1029 (struct sockaddr *) &v6,
1030 sizeof (v6)))
1031 {
1032 /* bind failed, so someone is listening! */
1033 ret = GNUNET_YES;
1034 }
1035 (void) close (sock);
1036 }
1037 else
1038 {
1039 GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING,
1040 "socket");
1041 /* not changing 'ret' intentionally here, as
1042 v4 succeeding and v6 failing just means we
1043 should use v4 */
1044 }
1045 }
1046 else
1047 {
1048 /* service running remotely */
1049 ret = GNUNET_OK;
1050 }
1051 GNUNET_free (hostname);
1052 return ret;
1053}
1054
1055
1056struct GNUNET_MQ_Handle *
1057GNUNET_CLIENT_connect (const struct GNUNET_CONFIGURATION_Handle *cfg,
1058 const char *service_name,
1059 const struct GNUNET_MQ_MessageHandler *handlers,
1060 GNUNET_MQ_ErrorHandler error_handler,
1061 void *error_handler_cls)
1062{
1063 struct ClientState *cstate;
1064
1065 if (GNUNET_OK !=
1066 test_service_configuration (service_name,
1067 cfg))
1068 return NULL;
1069 cstate = GNUNET_new (struct ClientState);
1070 cstate->service_name = GNUNET_strdup (service_name);
1071 cstate->cfg = cfg;
1072 cstate->retry_task = GNUNET_SCHEDULER_add_now (&start_connect,
1073 cstate);
1074 cstate->mst = GNUNET_MST_create (&recv_message,
1075 cstate);
1076 if (GNUNET_YES ==
1077 GNUNET_CONFIGURATION_have_value (cfg,
1078 service_name,
1079 "PORT"))
1080 {
1081 if (! ((GNUNET_OK !=
1082 GNUNET_CONFIGURATION_get_value_number (cfg,
1083 service_name,
1084 "PORT",
1085 &cstate->port)) ||
1086 (cstate->port > 65535) ||
1087 (GNUNET_OK !=
1088 GNUNET_CONFIGURATION_get_value_string (cfg,
1089 service_name,
1090 "HOSTNAME",
1091 &cstate->hostname))) &&
1092 (0 == strlen (cstate->hostname)))
1093 {
1094 GNUNET_free (cstate->hostname);
1095 cstate->hostname = NULL;
1096 LOG (GNUNET_ERROR_TYPE_WARNING,
1097 _ ("Need a non-empty hostname for service `%s'.\n"),
1098 service_name);
1099 }
1100 }
1101 cstate->mq = GNUNET_MQ_queue_for_callbacks (&connection_client_send_impl,
1102 &connection_client_destroy_impl,
1103 &connection_client_cancel_impl,
1104 cstate,
1105 handlers,
1106 error_handler,
1107 error_handler_cls);
1108 return cstate->mq;
1109}
1110
1111
1112/* end of client.c */
diff --git a/src/lib/util/common_allocation.c b/src/lib/util/common_allocation.c
new file mode 100644
index 000000000..afd701720
--- /dev/null
+++ b/src/lib/util/common_allocation.c
@@ -0,0 +1,437 @@
1/*
2 This file is part of GNUnet.
3 Copyright (C) 2001, 2002, 2003, 2005, 2006, 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/**
22 * @file util/common_allocation.c
23 * @brief wrapper around malloc/free
24 * @author Christian Grothoff
25 */
26
27#include "platform.h"
28#include "gnunet_util_lib.h"
29#if HAVE_MALLOC_H
30#include <malloc.h>
31#endif
32#if HAVE_MALLOC_MALLOC_H
33#include <malloc/malloc.h>
34#endif
35
36#define LOG(kind, ...) \
37 GNUNET_log_from (kind, "util-common-allocation", __VA_ARGS__)
38
39#define LOG_STRERROR(kind, syscall) \
40 GNUNET_log_from_strerror (kind, "util-common-allocation", syscall)
41
42#ifndef INT_MAX
43#define INT_MAX 0x7FFFFFFF
44#endif
45
46
47void *
48GNUNET_xmalloc_ (size_t size,
49 const char *filename,
50 int linenumber)
51{
52 void *ret;
53
54 /* As a security precaution, we generally do not allow very large
55 * allocations using the default 'GNUNET_malloc()' macro */
56 GNUNET_assert_at (size <= GNUNET_MAX_MALLOC_CHECKED,
57 filename,
58 linenumber);
59 ret = GNUNET_xmalloc_unchecked_ (size,
60 filename,
61 linenumber);
62 if (NULL == ret)
63 {
64 LOG_STRERROR (GNUNET_ERROR_TYPE_ERROR,
65 "malloc");
66 GNUNET_assert (0);
67 }
68 return ret;
69}
70
71
72void **
73GNUNET_xnew_array_2d_ (size_t n,
74 size_t m,
75 size_t elementSize,
76 const char *filename,
77 int linenumber)
78{
79 /* use char pointer internally to avoid void pointer arithmetic warnings */
80 char **ret = GNUNET_xmalloc_ (n * sizeof(void *) /* 1. dim header */
81 + n * m * elementSize, /* element data */
82 filename,
83 linenumber);
84
85 for (size_t i = 0; i < n; i++)
86 ret[i] = (char *) ret /* base address */
87 + n * sizeof(void *) /* skip 1. dim header */
88 + i * m * elementSize; /* skip to 2. dim row header */
89 return (void **) ret;
90}
91
92
93void ***
94GNUNET_xnew_array_3d_ (size_t n,
95 size_t m,
96 size_t o,
97 size_t elementSize,
98 const char *filename,
99 int linenumber)
100{
101 /* use char pointer internally to avoid void pointer arithmetic warnings */
102 char ***ret = GNUNET_xmalloc_ (n * sizeof(void **) /* 1. dim header */
103 + n * m * sizeof(void *) /* 2. dim header */
104 + n * m * o * elementSize, /* element data */
105 filename,
106 linenumber);
107
108 for (size_t i = 0; i < n; i++)
109 {
110 /* need to cast to (char *) temporarily for byte level accuracy */
111 ret[i] = (char **) ((char *) ret /* base address */
112 + n * sizeof(void **) /* skip 1. dim header */
113 + i * m * sizeof(void *)); /* skip to 2. dim header */
114 for (size_t j = 0; j < m; j++)
115 ret[i][j] = (char *) ret /* base address */
116 + n * sizeof(void **) /* skip 1. dim header */
117 + n * m * sizeof(void *) /* skip 2. dim header */
118 + i * m * o * elementSize /* skip to 2. dim part */
119 + j * o * elementSize; /* skip to 3. dim row data */
120 }
121 return (void ***) ret;
122}
123
124
125void *
126GNUNET_xmemdup_ (const void *buf,
127 size_t size,
128 const char *filename,
129 int linenumber)
130{
131 void *ret;
132
133 /* As a security precaution, we generally do not allow very large
134 * allocations here */
135 GNUNET_assert_at (size <= GNUNET_MAX_MALLOC_CHECKED,
136 filename,
137 linenumber);
138 GNUNET_assert_at (size < INT_MAX,
139 filename,
140 linenumber);
141 ret = malloc (size);
142 if (NULL == ret)
143 {
144 LOG_STRERROR (GNUNET_ERROR_TYPE_ERROR,
145 "malloc");
146 GNUNET_assert (0);
147 }
148 GNUNET_memcpy (ret,
149 buf,
150 size);
151 return ret;
152}
153
154
155void *
156GNUNET_xmalloc_unchecked_ (size_t size,
157 const char *filename,
158 int linenumber)
159{
160 void *result;
161
162 (void) filename;
163 (void) linenumber;
164 result = malloc (size);
165 if (NULL == result)
166 return NULL;
167 memset (result,
168 0,
169 size);
170 return result;
171}
172
173
174void *
175GNUNET_xrealloc_ (void *ptr,
176 size_t n,
177 const char *filename,
178 int linenumber)
179{
180 (void) filename;
181 (void) linenumber;
182
183#if defined(M_SIZE)
184#if ENABLE_POISONING
185 {
186 uint64_t *base = ptr;
187 size_t s = M_SIZE (ptr);
188
189 if (s > n)
190 {
191 const uint64_t baadfood = GNUNET_ntohll (0xBAADF00DBAADF00DLL);
192 char *cbase = ptr;
193
194 GNUNET_memcpy (&cbase[n],
195 &baadfood,
196 GNUNET_MIN (8 - (n % 8),
197 s - n));
198 for (size_t i = 1 + (n + 7) / 8; i < s / 8; i++)
199 base[i] = baadfood;
200 GNUNET_memcpy (&base[s / 8],
201 &baadfood,
202 s % 8);
203 }
204 }
205#endif
206#endif
207 ptr = realloc (ptr, n);
208 if ((NULL == ptr) && (n > 0))
209 {
210 LOG_STRERROR (GNUNET_ERROR_TYPE_ERROR,
211 "realloc");
212 GNUNET_assert (0);
213 }
214 return ptr;
215}
216
217
218#if __BYTE_ORDER == __LITTLE_ENDIAN
219#define BAADFOOD_STR "\x0D\xF0\xAD\xBA"
220#endif
221#if __BYTE_ORDER == __BIG_ENDIAN
222#define BAADFOOD_STR "\xBA\xAD\xF0\x0D"
223#endif
224
225#if HAVE_MALLOC_NP_H
226#include <malloc_np.h>
227#endif
228#if HAVE_MALLOC_USABLE_SIZE
229#define M_SIZE(p) malloc_usable_size (p)
230#elif HAVE_MALLOC_SIZE
231#define M_SIZE(p) malloc_size (p)
232#endif
233
234void
235GNUNET_xfree_ (void *ptr,
236 const char *filename,
237 int linenumber)
238{
239 if (NULL == ptr)
240 return;
241#if defined(M_SIZE)
242#if ENABLE_POISONING
243 {
244 const uint64_t baadfood = GNUNET_ntohll (0xBAADF00DBAADF00DLL);
245 uint64_t *base = ptr;
246 size_t s = M_SIZE (ptr);
247
248 for (size_t i = 0; i < s / 8; i++)
249 base[i] = baadfood;
250 GNUNET_memcpy (&base[s / 8], &baadfood, s % 8);
251 }
252#endif
253#endif
254 free (ptr);
255}
256
257
258char *
259GNUNET_xstrdup_ (const char *str,
260 const char *filename,
261 int linenumber)
262{
263 size_t slen = strlen (str) + 1;
264 char *res;
265
266 GNUNET_assert_at (str != NULL,
267 filename,
268 linenumber);
269 res = GNUNET_xmalloc_ (slen,
270 filename,
271 linenumber);
272 GNUNET_memcpy (res,
273 str,
274 slen);
275 return res;
276}
277
278
279#if ! HAVE_STRNLEN
280static size_t
281strnlen (const char *s,
282 size_t n)
283{
284 const char *e;
285
286 e = memchr (s,
287 '\0',
288 n);
289 if (NULL == e)
290 return n;
291 return e - s;
292}
293
294
295#endif
296
297
298char *
299GNUNET_xstrndup_ (const char *str,
300 size_t len,
301 const char *filename,
302 int linenumber)
303{
304 char *res;
305
306 if (0 == len)
307 return GNUNET_strdup ("");
308 GNUNET_assert_at (NULL != str,
309 filename,
310 linenumber);
311 len = strnlen (str, len);
312 res = GNUNET_xmalloc_ (len + 1,
313 filename,
314 linenumber);
315 GNUNET_memcpy (res, str, len);
316 /* res[len] = '\0'; 'malloc' zeros out anyway */
317 return res;
318}
319
320
321void
322GNUNET_xgrow_ (void **old,
323 size_t elementSize,
324 unsigned int *oldCount,
325 unsigned int newCount,
326 const char *filename,
327 int linenumber)
328{
329 void *tmp;
330 size_t size;
331
332 GNUNET_assert_at (INT_MAX / elementSize > newCount, filename, linenumber);
333 size = newCount * elementSize;
334 if (0 == size)
335 {
336 tmp = NULL;
337 }
338 else
339 {
340 tmp = GNUNET_xmalloc_ (size,
341 filename,
342 linenumber);
343 if (NULL != *old)
344 {
345 GNUNET_memcpy (tmp,
346 *old,
347 elementSize * GNUNET_MIN (*oldCount,
348 newCount));
349 }
350 }
351
352 if (NULL != *old)
353 {
354 GNUNET_xfree_ (*old,
355 filename,
356 linenumber);
357 }
358 *old = tmp;
359 *oldCount = newCount;
360}
361
362
363int
364GNUNET_asprintf (char **buf,
365 const char *format,
366 ...)
367{
368 int ret;
369 va_list args;
370
371 va_start (args,
372 format);
373 ret = vsnprintf (NULL,
374 0,
375 format,
376 args);
377 va_end (args);
378 GNUNET_assert (ret >= 0);
379 *buf = GNUNET_malloc (ret + 1);
380 va_start (args, format);
381 ret = vsprintf (*buf,
382 format,
383 args);
384 va_end (args);
385 return ret;
386}
387
388
389int
390GNUNET_snprintf (char *buf,
391 size_t size,
392 const char *format,
393 ...)
394{
395 int ret;
396 va_list args;
397
398 va_start (args,
399 format);
400 ret = vsnprintf (buf,
401 size,
402 format,
403 args);
404 va_end (args);
405 GNUNET_assert ((ret >= 0) && (((size_t) ret) < size));
406 return ret;
407}
408
409
410struct GNUNET_MessageHeader *
411GNUNET_copy_message (const struct GNUNET_MessageHeader *msg)
412{
413 struct GNUNET_MessageHeader *ret;
414 uint16_t msize;
415
416 msize = ntohs (msg->size);
417 GNUNET_assert (msize >= sizeof(struct GNUNET_MessageHeader));
418 ret = GNUNET_malloc (msize);
419 GNUNET_memcpy (ret, msg, msize);
420 return ret;
421}
422
423
424bool
425GNUNET_is_zero_ (const void *a,
426 size_t n)
427{
428 const char *b = a;
429
430 for (size_t i = 0; i < n; i++)
431 if (b[i])
432 return false;
433 return true;
434}
435
436
437/* end of common_allocation.c */
diff --git a/src/lib/util/common_endian.c b/src/lib/util/common_endian.c
new file mode 100644
index 000000000..59c15326d
--- /dev/null
+++ b/src/lib/util/common_endian.c
@@ -0,0 +1,95 @@
1/*
2 This file is part of GNUnet.
3 Copyright (C) 2001, 2002, 2003, 2004, 2006, 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/common_endian.c
23 * @brief endian conversion helpers
24 * @author Christian Grothoff
25 * @author Gabor X Toth
26 */
27
28
29#include "platform.h"
30#include "gnunet_util_lib.h"
31
32#define LOG(kind, ...) GNUNET_log_from (kind, "util-common-endian", __VA_ARGS__)
33
34
35#ifndef htonbe64
36uint64_t
37GNUNET_htonll (uint64_t n)
38{
39#if __BYTE_ORDER == __BIG_ENDIAN
40 return n;
41#elif __BYTE_ORDER == __LITTLE_ENDIAN
42 return (((uint64_t) htonl (n)) << 32) + htonl (n >> 32);
43#else
44 #error byteorder undefined
45#endif
46}
47
48
49#endif
50
51
52#ifndef be64toh
53uint64_t
54GNUNET_ntohll (uint64_t n)
55{
56#if __BYTE_ORDER == __BIG_ENDIAN
57 return n;
58#elif __BYTE_ORDER == __LITTLE_ENDIAN
59 return (((uint64_t) ntohl (n)) << 32) + ntohl (n >> 32);
60#else
61 #error byteorder undefined
62#endif
63}
64
65
66#endif
67
68
69double
70GNUNET_hton_double (double d)
71{
72 double res;
73 uint64_t *in = (uint64_t *) &d;
74 uint64_t *out = (uint64_t *) &res;
75
76 out[0] = GNUNET_htonll (in[0]);
77
78 return res;
79}
80
81
82double
83GNUNET_ntoh_double (double d)
84{
85 double res;
86 uint64_t *in = (uint64_t *) &d;
87 uint64_t *out = (uint64_t *) &res;
88
89 out[0] = GNUNET_ntohll (in[0]);
90
91 return res;
92}
93
94
95/* end of common_endian.c */
diff --git a/src/lib/util/common_logging.c b/src/lib/util/common_logging.c
new file mode 100644
index 000000000..37c6064b8
--- /dev/null
+++ b/src/lib/util/common_logging.c
@@ -0,0 +1,1544 @@
1/*
2 This file is part of GNUnet.
3 Copyright (C) 2006-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/common_logging.c
23 * @brief error handling API
24 * @author Christian Grothoff
25 */
26
27#include "platform.h"
28#include "gnunet_util_lib.h"
29#include <regex.h>
30
31
32/**
33 * After how many milliseconds do we always print
34 * that "message X was repeated N times"? Use 12h.
35 */
36#define BULK_DELAY_THRESHOLD (12 * 60 * 60 * 1000LL * 1000LL)
37
38/**
39 * After how many repetitions do we always print
40 * that "message X was repeated N times"? (even if
41 * we have not yet reached the delay threshold)
42 */
43#define BULK_REPEAT_THRESHOLD 1000
44
45/**
46 * How many characters do we use for matching of
47 * bulk messages?
48 */
49#define BULK_TRACK_SIZE 256
50
51/**
52 * How many characters do we use for matching of
53 * bulk components?
54 */
55#define COMP_TRACK_SIZE 32
56
57/**
58 * How many characters can a date/time string
59 * be at most?
60 */
61#define DATE_STR_SIZE 64
62
63/**
64 * How many log files to keep?
65 */
66#define ROTATION_KEEP 3
67
68#ifndef PATH_MAX
69/**
70 * Assumed maximum path length (for the log file name).
71 */
72#define PATH_MAX 4096
73#endif
74
75
76/**
77 * Linked list of active loggers.
78 */
79struct CustomLogger
80{
81 /**
82 * This is a linked list.
83 */
84 struct CustomLogger *next;
85
86 /**
87 * Log function.
88 */
89 GNUNET_Logger logger;
90
91 /**
92 * Closure for logger.
93 */
94 void *logger_cls;
95};
96
97
98/**
99 * Asynchronous scope of the current thread, or NULL if we have not
100 * entered an async scope yet.
101 */
102static GNUNET_THREAD_LOCAL struct GNUNET_AsyncScopeSave current_async_scope;
103
104/**
105 * The last "bulk" error message that we have been logging.
106 * Note that this message maybe truncated to the first BULK_TRACK_SIZE
107 * characters, in which case it is NOT 0-terminated!
108 */
109static GNUNET_THREAD_LOCAL char last_bulk[BULK_TRACK_SIZE] __nonstring;
110
111/**
112 * Type of the last bulk message.
113 */
114static GNUNET_THREAD_LOCAL enum GNUNET_ErrorType last_bulk_kind;
115
116/**
117 * Time of the last bulk error message (0 for none)
118 */
119static GNUNET_THREAD_LOCAL struct GNUNET_TIME_Absolute last_bulk_time;
120
121/**
122 * Number of times that bulk message has been repeated since.
123 */
124static GNUNET_THREAD_LOCAL unsigned int last_bulk_repeat;
125
126/**
127 * Component when the last bulk was logged. Will be 0-terminated.
128 */
129static GNUNET_THREAD_LOCAL char last_bulk_comp[COMP_TRACK_SIZE + 1];
130
131/**
132 * Running component.
133 */
134static char *component;
135
136/**
137 * Running component (without pid).
138 */
139static char *component_nopid;
140
141/**
142 * Format string describing the name of the log file.
143 */
144static char *log_file_name;
145
146/**
147 * Minimum log level.
148 */
149static enum GNUNET_ErrorType min_level;
150
151/**
152 * Linked list of our custom loggres.
153 */
154static struct CustomLogger *loggers;
155
156/**
157 * Number of log calls to ignore.
158 */
159static GNUNET_THREAD_LOCAL int skip_log = 0;
160
161/**
162 * File descriptor to use for "stderr", or NULL for none.
163 */
164static FILE *GNUNET_stderr;
165
166/**
167 * Represents a single logging definition
168 */
169struct LogDef
170{
171 /**
172 * Component name regex
173 */
174 regex_t component_regex;
175
176 /**
177 * File name regex
178 */
179 regex_t file_regex;
180
181 /**
182 * Function name regex
183 */
184 regex_t function_regex;
185
186 /**
187 * Lowest line at which this definition matches.
188 * Defaults to 0. Must be <= to_line.
189 */
190 int from_line;
191
192 /**
193 * Highest line at which this definition matches.
194 * Defaults to INT_MAX. Must be >= from_line.
195 */
196 int to_line;
197
198 /**
199 * Maximal log level allowed for calls that match this definition.
200 * Calls with higher log level will be disabled.
201 * Must be >= 0
202 */
203 int level;
204
205 /**
206 * 1 if this definition comes from GNUNET_FORCE_LOG, which means that it
207 * overrides any configuration options. 0 otherwise.
208 */
209 int force;
210};
211
212
213#if ! defined(GNUNET_CULL_LOGGING)
214/**
215 * Dynamic array of logging definitions
216 */
217static struct LogDef *logdefs;
218
219/**
220 * Allocated size of logdefs array (in units)
221 */
222static int logdefs_size;
223
224/**
225 * The number of units used in logdefs array.
226 */
227static int logdefs_len;
228
229/**
230 * #GNUNET_YES if GNUNET_LOG environment variable is already parsed.
231 */
232static int gnunet_log_parsed;
233
234/**
235 * #GNUNET_YES if GNUNET_FORCE_LOG environment variable is already parsed.
236 */
237static int gnunet_force_log_parsed;
238
239/**
240 * #GNUNET_YES if at least one definition with forced == 1 is available.
241 */
242static int gnunet_force_log_present;
243#endif
244
245
246/**
247 * Convert a textual description of a loglevel
248 * to the respective enumeration type.
249 *
250 * @param log loglevel to parse
251 * @return GNUNET_ERROR_TYPE_INVALID if log does not parse
252 */
253static enum GNUNET_ErrorType
254get_type (const char *log)
255{
256 if (NULL == log)
257 return GNUNET_ERROR_TYPE_UNSPECIFIED;
258 if (0 == strcasecmp (log, "DEBUG"))
259 return GNUNET_ERROR_TYPE_DEBUG;
260 if (0 == strcasecmp (log, "INFO"))
261 return GNUNET_ERROR_TYPE_INFO;
262 if (0 == strcasecmp (log, "MESSAGE"))
263 return GNUNET_ERROR_TYPE_MESSAGE;
264 if (0 == strcasecmp (log, "WARNING"))
265 return GNUNET_ERROR_TYPE_WARNING;
266 if (0 == strcasecmp (log, "ERROR"))
267 return GNUNET_ERROR_TYPE_ERROR;
268 if (0 == strcasecmp (log, "NONE"))
269 return GNUNET_ERROR_TYPE_NONE;
270 return GNUNET_ERROR_TYPE_INVALID;
271}
272
273
274/**
275 * Abort the process, generate a core dump if possible.
276 */
277void
278GNUNET_abort_ ()
279{
280 abort ();
281}
282
283
284#if ! defined(GNUNET_CULL_LOGGING)
285/**
286 * Utility function - reallocates logdefs array to be twice as large.
287 */
288static void
289resize_logdefs (void)
290{
291 logdefs_size = (logdefs_size + 1) * 2;
292 logdefs = GNUNET_realloc (logdefs, logdefs_size * sizeof(struct LogDef));
293}
294
295
296/**
297 * Rotate logs, deleting the oldest log.
298 *
299 * @param new_name new name to add to the rotation
300 */
301static void
302log_rotate (const char *new_name)
303{
304 static char *rotation[ROTATION_KEEP];
305 static unsigned int rotation_off;
306 char *discard;
307
308 if ('\0' == *new_name)
309 return; /* not a real log file name */
310 discard = rotation[rotation_off % ROTATION_KEEP];
311 if (NULL != discard)
312 {
313 /* Note: can't log errors during logging (recursion!), so this
314 operation MUST silently fail... */
315 (void) unlink (discard);
316 GNUNET_free (discard);
317 }
318 rotation[rotation_off % ROTATION_KEEP] = GNUNET_strdup (new_name);
319 rotation_off++;
320}
321
322
323const char *
324GNUNET_b2s (const void *buf,
325 size_t buf_size)
326{
327 static GNUNET_THREAD_LOCAL char ret[9];
328 struct GNUNET_HashCode hc;
329 char *tmp;
330
331 GNUNET_CRYPTO_hash (buf,
332 buf_size,
333 &hc);
334 tmp = GNUNET_STRINGS_data_to_string_alloc (&hc,
335 sizeof (hc));
336 memcpy (ret,
337 tmp,
338 8);
339 GNUNET_free (tmp);
340 ret[8] = '\0';
341 return ret;
342}
343
344
345/**
346 * Setup the log file.
347 *
348 * @param tm timestamp for which we should setup logging
349 * @return #GNUNET_OK on success, #GNUNET_SYSERR on error
350 */
351static enum GNUNET_GenericReturnValue
352setup_log_file (const struct tm *tm)
353{
354 static char last_fn[PATH_MAX + 1];
355 char fn[PATH_MAX + 1];
356 int altlog_fd;
357 int dup_return;
358 FILE *altlog;
359 char *leftsquare;
360
361 if (NULL == log_file_name)
362 return GNUNET_SYSERR;
363 if (0 == strftime (fn, sizeof(fn), log_file_name, tm))
364 return GNUNET_SYSERR;
365 leftsquare = strrchr (fn, '[');
366 if ((NULL != leftsquare) && (']' == leftsquare[1]))
367 {
368 char *logfile_copy = GNUNET_strdup (fn);
369
370 logfile_copy[leftsquare - fn] = '\0';
371 logfile_copy[leftsquare - fn + 1] = '\0';
372 snprintf (fn,
373 PATH_MAX,
374 "%s%d%s",
375 logfile_copy,
376 getpid (),
377 &logfile_copy[leftsquare - fn + 2]);
378 GNUNET_free (logfile_copy);
379 }
380 if (0 == strcmp (fn, last_fn))
381 return GNUNET_OK; /* no change */
382 log_rotate (last_fn);
383 strcpy (last_fn, fn);
384 if (GNUNET_SYSERR == GNUNET_DISK_directory_create_for_file (fn))
385 {
386 fprintf (stderr,
387 "Failed to create directory for `%s': %s\n",
388 fn,
389 strerror (errno));
390 return GNUNET_SYSERR;
391 }
392 altlog_fd = open (fn,
393 O_APPEND | O_WRONLY | O_CREAT,
394 S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH);
395
396 if (-1 != altlog_fd)
397 {
398 if (NULL != GNUNET_stderr)
399 fclose (GNUNET_stderr);
400 dup_return = dup2 (altlog_fd, 2);
401 (void) close (altlog_fd);
402 if (-1 != dup_return)
403 {
404 altlog = fdopen (2, "ab");
405 if (NULL == altlog)
406 {
407 (void) close (2);
408 altlog_fd = -1;
409 }
410 }
411 else
412 {
413 altlog_fd = -1;
414 }
415 }
416 if (-1 == altlog_fd)
417 {
418 GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_ERROR, "open", fn);
419 return GNUNET_SYSERR;
420 }
421 GNUNET_stderr = altlog;
422 return GNUNET_OK;
423}
424
425
426/**
427 * Utility function - adds a parsed definition to logdefs array.
428 *
429 * @param component see struct LogDef, can't be NULL
430 * @param file see struct LogDef, can't be NULL
431 * @param function see struct LogDef, can't be NULL
432 * @param from_line see struct LogDef
433 * @param to_line see struct LogDef
434 * @param level see struct LogDef, must be >= 0
435 * @param force see struct LogDef
436 * @return 0 on success, regex-specific error otherwise
437 */
438static int
439add_definition (const char *component,
440 const char *file,
441 const char *function,
442 int from_line,
443 int to_line,
444 int level,
445 int force)
446{
447 struct LogDef n;
448 int r;
449
450 if (logdefs_size == logdefs_len)
451 resize_logdefs ();
452 memset (&n, 0, sizeof(n));
453 if (0 == strlen (component))
454 component = (char *) ".*";
455 r = regcomp (&n.component_regex, (const char *) component, REG_NOSUB);
456 if (0 != r)
457 {
458 return r;
459 }
460 if (0 == strlen (file))
461 file = (char *) ".*";
462 r = regcomp (&n.file_regex, (const char *) file, REG_NOSUB);
463 if (0 != r)
464 {
465 regfree (&n.component_regex);
466 return r;
467 }
468 if ((NULL == function) || (0 == strlen (function)))
469 function = (char *) ".*";
470 r = regcomp (&n.function_regex, (const char *) function, REG_NOSUB);
471 if (0 != r)
472 {
473 regfree (&n.component_regex);
474 regfree (&n.file_regex);
475 return r;
476 }
477 n.from_line = from_line;
478 n.to_line = to_line;
479 n.level = level;
480 n.force = force;
481 logdefs[logdefs_len++] = n;
482 return 0;
483}
484
485
486/**
487 * Decides whether a particular logging call should or should not be allowed
488 * to be made. Used internally by GNUNET_log*()
489 *
490 * @param caller_level loglevel the caller wants to use
491 * @param comp component name the caller uses (NULL means that global
492 * component name is used)
493 * @param file file name containing the logging call, usually __FILE__
494 * @param function function which tries to make a logging call,
495 * usually __FUNCTION__
496 * @param line line at which the call is made, usually __LINE__
497 * @return 0 to disallow the call, 1 to allow it
498 */
499int
500GNUNET_get_log_call_status (int caller_level,
501 const char *comp,
502 const char *file,
503 const char *function,
504 int line)
505{
506 struct LogDef *ld;
507 int i;
508 int force_only;
509
510 if (NULL == comp)
511 /* Use default component */
512 comp = component_nopid;
513
514 /* We have no definitions to override globally configured log level,
515 * so just use it right away.
516 */
517 if ((min_level >= 0) && (GNUNET_NO == gnunet_force_log_present))
518 return caller_level <= min_level;
519
520 /* Only look for forced definitions? */
521 force_only = min_level >= 0;
522 for (i = 0; i < logdefs_len; i++)
523 {
524 ld = &logdefs[i];
525 if (((! force_only) || ld->force) &&
526 ((line >= ld->from_line) && (line <= ld->to_line) ) &&
527 (0 == regexec (&ld->component_regex, comp, 0, NULL, 0)) &&
528 (0 == regexec (&ld->file_regex, file, 0, NULL, 0)) &&
529 (0 == regexec (&ld->function_regex, function, 0, NULL, 0)))
530 {
531 /* We're finished */
532 return caller_level <= ld->level;
533 }
534 }
535 /* No matches - use global level, if defined */
536 if (min_level >= 0)
537 return caller_level <= min_level;
538 /* All programs/services previously defaulted to WARNING.
539 * Now *we* default to WARNING, and THEY default to NULL.
540 * Or rather we default to MESSAGE, since things aren't always bad.
541 */
542 return caller_level <= GNUNET_ERROR_TYPE_MESSAGE;
543}
544
545
546/**
547 * Utility function - parses a definition
548 *
549 * Definition format:
550 * component;file;function;from_line-to_line;level[/component...]
551 * All entries are mandatory, but may be empty.
552 * Empty entries for component, file and function are treated as
553 * "matches anything".
554 * Empty line entry is treated as "from 0 to INT_MAX"
555 * Line entry with only one line is treated as "this line only"
556 * Entry for level MUST NOT be empty.
557 * Entries for component, file and function that consist of a
558 * single character "*" are treated (at the moment) the same way
559 * empty entries are treated (wildcard matching is not implemented (yet?)).
560 * file entry is matched to the end of __FILE__. That is, it might be
561 * a base name, or a base name with leading directory names (some compilers
562 * define __FILE__ to absolute file path).
563 *
564 * @param constname name of the environment variable from which to get the
565 * string to be parsed
566 * @param force 1 if definitions found in constname are to be forced
567 * @return number of added definitions
568 */
569static int
570parse_definitions (const char *constname, int force)
571{
572 char *def;
573 const char *tmp;
574 char *comp = NULL;
575 char *file = NULL;
576 char *function = NULL;
577 char *p;
578 char *start;
579 char *t;
580 short state;
581 int level;
582 int from_line, to_line;
583 int counter = 0;
584 int keep_looking = 1;
585
586 tmp = getenv (constname);
587 if (NULL == tmp)
588 return 0;
589 def = GNUNET_strdup (tmp);
590 from_line = 0;
591 to_line = INT_MAX;
592 for (p = def, state = 0, start = def; keep_looking; p++)
593 {
594 switch (p[0])
595 {
596 case ';': /* found a field separator */
597 p[0] = '\0';
598 switch (state)
599 {
600 case 0: /* within a component name */
601 comp = start;
602 break;
603
604 case 1: /* within a file name */
605 file = start;
606 break;
607
608 case 2: /* within a function name */
609 /* after a file name there must be a function name */
610 function = start;
611 break;
612
613 case 3: /* within a from-to line range */
614 if (strlen (start) > 0)
615 {
616 errno = 0;
617 from_line = strtol (start, &t, 10);
618 if ((0 != errno) || (from_line < 0))
619 {
620 GNUNET_free (def);
621 return counter;
622 }
623 if ((t < p) && ('-' == t[0]))
624 {
625 errno = 0;
626 start = t + 1;
627 to_line = strtol (start, &t, 10);
628 if ((0 != errno) || (to_line < 0) || (t != p))
629 {
630 GNUNET_free (def);
631 return counter;
632 }
633 }
634 else /* one number means "match this line only" */
635 to_line = from_line;
636 }
637 else /* default to 0-max */
638 {
639 from_line = 0;
640 to_line = INT_MAX;
641 }
642 break;
643
644 default:
645 fprintf (
646 stderr,
647 _ ("ERROR: Unable to parse log definition: Syntax error at `%s'.\n"),
648 p);
649 break;
650 }
651 start = p + 1;
652 state++;
653 break;
654
655 case '\0': /* found EOL */
656 keep_looking = 0;
657
658 /* fall through to '/' */
659 case '/': /* found a definition separator */
660 switch (state)
661 {
662 case 4: /* within a log level */
663 p[0] = '\0';
664 state = 0;
665 level = get_type ((const char *) start);
666 if ((GNUNET_ERROR_TYPE_INVALID == level) ||
667 (GNUNET_ERROR_TYPE_UNSPECIFIED == level) ||
668 (0 != add_definition (comp,
669 file,
670 function,
671 from_line,
672 to_line,
673 level,
674 force)))
675 {
676 GNUNET_free (def);
677 return counter;
678 }
679 counter++;
680 start = p + 1;
681 break;
682
683 default:
684 fprintf (
685 stderr,
686 _ ("ERROR: Unable to parse log definition: Syntax error at `%s'.\n"),
687 p);
688 break;
689 }
690
691 default:
692 break;
693 }
694 }
695 GNUNET_free (def);
696 return counter;
697}
698
699
700/**
701 * Utility function - parses GNUNET_LOG and GNUNET_FORCE_LOG.
702 */
703static void
704parse_all_definitions ()
705{
706 if (GNUNET_NO == gnunet_force_log_parsed)
707 gnunet_force_log_present =
708 parse_definitions ("GNUNET_FORCE_LOG", 1) > 0 ? GNUNET_YES : GNUNET_NO;
709 gnunet_force_log_parsed = GNUNET_YES;
710
711 if (GNUNET_NO == gnunet_log_parsed)
712 parse_definitions ("GNUNET_LOG", 0);
713 gnunet_log_parsed = GNUNET_YES;
714}
715
716
717#endif
718
719
720/**
721 * Setup logging.
722 *
723 * @param comp default component to use
724 * @param loglevel what types of messages should be logged
725 * @param logfile which file to write log messages to (can be NULL)
726 * @return #GNUNET_OK on success
727 */
728enum GNUNET_GenericReturnValue
729GNUNET_log_setup (const char *comp,
730 const char *loglevel,
731 const char *logfile)
732{
733 const char *env_logfile;
734
735 min_level = get_type (loglevel);
736#if ! defined(GNUNET_CULL_LOGGING)
737 parse_all_definitions ();
738#endif
739 GNUNET_free (component);
740 GNUNET_asprintf (&component,
741 "%s-%d",
742 comp,
743 getpid ());
744 GNUNET_free (component_nopid);
745 component_nopid = GNUNET_strdup (comp);
746
747 env_logfile = getenv ("GNUNET_FORCE_LOGFILE");
748 if ((NULL != env_logfile) && (strlen (env_logfile) > 0))
749 logfile = env_logfile;
750 if (NULL == logfile)
751 return GNUNET_OK;
752 GNUNET_free (log_file_name);
753 log_file_name = GNUNET_STRINGS_filename_expand (logfile);
754 if (NULL == log_file_name)
755 return GNUNET_SYSERR;
756#if defined(GNUNET_CULL_LOGGING)
757 /* log file option not allowed for wallet logic */
758 GNUNET_assert (NULL == logfile);
759 return GNUNET_OK;
760#else
761 {
762 time_t t;
763 const struct tm *tm;
764
765 t = time (NULL);
766 tm = gmtime (&t);
767 return setup_log_file (tm);
768 }
769#endif
770}
771
772
773void
774GNUNET_logger_add (GNUNET_Logger logger,
775 void *logger_cls)
776{
777 struct CustomLogger *entry;
778
779 entry = GNUNET_new (struct CustomLogger);
780 entry->logger = logger;
781 entry->logger_cls = logger_cls;
782 entry->next = loggers;
783 loggers = entry;
784}
785
786
787void
788GNUNET_logger_remove (GNUNET_Logger logger,
789 void *logger_cls)
790{
791 struct CustomLogger *pos;
792 struct CustomLogger *prev;
793
794 prev = NULL;
795 pos = loggers;
796 while ((NULL != pos) &&
797 ((pos->logger != logger) || (pos->logger_cls != logger_cls)))
798 {
799 prev = pos;
800 pos = pos->next;
801 }
802 GNUNET_assert (NULL != pos);
803 if (NULL == prev)
804 loggers = pos->next;
805 else
806 prev->next = pos->next;
807 GNUNET_free (pos);
808}
809
810
811/**
812 * Actually output the log message.
813 *
814 * @param kind how severe was the issue
815 * @param comp component responsible
816 * @param datestr current date/time
817 * @param msg the actual message
818 */
819static void
820output_message (enum GNUNET_ErrorType kind,
821 const char *comp,
822 const char *datestr,
823 const char *msg)
824{
825 static int have_journald = -1;
826 struct CustomLogger *pos;
827
828 if (-1 == have_journald)
829 {
830 /* systemd after version 231 sets this environment
831 variable if we are logging to journald. In this
832 case, skip outputting our component name, PID
833 and timestamp as journald already adds those. (#8032) */
834 if (NULL != getenv ("JOURNAL_STREAM"))
835 have_journald = 1;
836 else
837 have_journald = 0;
838 }
839
840 /* only use the standard logger if no custom loggers are present */
841 if ((NULL != GNUNET_stderr) && (NULL == loggers))
842 {
843 if (kind == GNUNET_ERROR_TYPE_MESSAGE)
844 {
845 /* The idea here is to produce "normal" output messages
846 * for end users while still having the power of the
847 * logging engine for developer needs. So ideally this
848 * is what it should look like when CLI tools are used
849 * interactively, yet the same message shouldn't look
850 * this way if the output is going to logfiles or robots
851 * instead.
852 */
853 fprintf (GNUNET_stderr, "* %s", msg);
854 }
855 else if (GNUNET_YES == current_async_scope.have_scope)
856 {
857 static GNUNET_THREAD_LOCAL char id_buf[27];
858 char *end;
859
860 /* We're logging, so skip_log must be currently 0. */
861 skip_log = 100;
862 end = GNUNET_STRINGS_data_to_string (&current_async_scope.scope_id,
863 sizeof(struct GNUNET_AsyncScopeId),
864 id_buf,
865 sizeof(id_buf) - 1);
866 GNUNET_assert (NULL != end);
867 *end = '\0';
868 skip_log = 0;
869 if (have_journald)
870 fprintf (GNUNET_stderr,
871 "(%s) %s %s",
872 id_buf,
873 GNUNET_error_type_to_string (kind),
874 msg);
875 else
876 fprintf (GNUNET_stderr,
877 "%s %s(%s) %s %s",
878 datestr,
879 comp,
880 id_buf,
881 GNUNET_error_type_to_string (kind),
882 msg);
883 }
884 else
885 {
886 if (have_journald)
887 fprintf (GNUNET_stderr,
888 "%s %s",
889 GNUNET_error_type_to_string (kind),
890 msg);
891 else
892 fprintf (GNUNET_stderr,
893 "%s %s %s %s",
894 datestr,
895 comp,
896 GNUNET_error_type_to_string (kind),
897 msg);
898 }
899 fflush (GNUNET_stderr);
900 }
901 pos = loggers;
902 while (NULL != pos)
903 {
904 pos->logger (pos->logger_cls, kind, comp, datestr, msg);
905 pos = pos->next;
906 }
907}
908
909
910/**
911 * Flush an existing bulk report to the output.
912 *
913 * @param datestr our current timestamp
914 */
915static void
916flush_bulk (const char *datestr)
917{
918 char msg[DATE_STR_SIZE + BULK_TRACK_SIZE + 256];
919 int rev;
920 char *last;
921 const char *ft;
922
923 if ((0 == last_bulk_time.abs_value_us) || (0 == last_bulk_repeat))
924 return;
925 rev = 0;
926 last = memchr (last_bulk, '\0', BULK_TRACK_SIZE);
927 if (last == NULL)
928 last = &last_bulk[BULK_TRACK_SIZE - 1];
929 else if (last != last_bulk)
930 last--;
931 if (last[0] == '\n')
932 {
933 rev = 1;
934 last[0] = '\0';
935 }
936 ft =
937 GNUNET_STRINGS_relative_time_to_string (GNUNET_TIME_absolute_get_duration (
938 last_bulk_time),
939 GNUNET_YES);
940 snprintf (msg,
941 sizeof(msg),
942 _ ("Message `%.*s' repeated %u times in the last %s\n"),
943 BULK_TRACK_SIZE,
944 last_bulk,
945 last_bulk_repeat,
946 ft);
947 if (rev == 1)
948 last[0] = '\n';
949 output_message (last_bulk_kind, last_bulk_comp, datestr, msg);
950 last_bulk_time = GNUNET_TIME_absolute_get ();
951 last_bulk_repeat = 0;
952}
953
954
955void
956GNUNET_log_skip (int n, int check_reset)
957{
958 int ok;
959
960 if (0 == n)
961 {
962 ok = (0 == skip_log);
963 skip_log = 0;
964 if (check_reset)
965 GNUNET_break (ok);
966 }
967 else
968 {
969 skip_log += n;
970 }
971}
972
973
974/**
975 * Get the number of log calls that are going to be skipped
976 *
977 * @return number of log calls to be ignored
978 */
979int
980GNUNET_get_log_skip ()
981{
982 return skip_log;
983}
984
985
986/**
987 * Output a log message using the default mechanism.
988 *
989 * @param kind how severe was the issue
990 * @param comp component responsible
991 * @param message the actual message
992 * @param va arguments to the format string "message"
993 */
994static void
995mylog (enum GNUNET_ErrorType kind,
996 const char *comp,
997 const char *message,
998 va_list va)
999{
1000 char date[DATE_STR_SIZE];
1001 char date2[DATE_STR_SIZE];
1002 struct tm *tmptr;
1003 size_t size;
1004 va_list vacp;
1005
1006 va_copy (vacp, va);
1007 size = vsnprintf (NULL, 0, message, vacp) + 1;
1008 GNUNET_assert (0 != size);
1009 va_end (vacp);
1010 memset (date, 0, DATE_STR_SIZE);
1011 {
1012 char buf[size];
1013 long long offset;
1014
1015 struct timeval timeofday;
1016
1017 gettimeofday (&timeofday, NULL);
1018 offset = GNUNET_TIME_get_offset ();
1019 if (offset > 0)
1020 {
1021 timeofday.tv_sec += offset / 1000LL;
1022 timeofday.tv_usec += (offset % 1000LL) * 1000LL;
1023 if (timeofday.tv_usec > 1000000LL)
1024 {
1025 timeofday.tv_usec -= 1000000LL;
1026 timeofday.tv_sec++;
1027 }
1028 }
1029 else
1030 {
1031 timeofday.tv_sec += offset / 1000LL;
1032 if (timeofday.tv_usec > -(offset % 1000LL) * 1000LL)
1033 {
1034 timeofday.tv_usec += (offset % 1000LL) * 1000LL;
1035 }
1036 else
1037 {
1038 timeofday.tv_usec += 1000000LL + (offset % 1000LL) * 1000LL;
1039 timeofday.tv_sec--;
1040 }
1041 }
1042 tmptr = localtime (&timeofday.tv_sec);
1043 if (NULL == tmptr)
1044 {
1045 strcpy (date, "localtime error");
1046 }
1047 else
1048 {
1049 /* RFC 3339 timestamp, with snprintf placeholder for microseconds */
1050 if (0 == strftime (date2, DATE_STR_SIZE, "%Y-%m-%dT%H:%M:%S.%%06u%z",
1051 tmptr))
1052 abort ();
1053 /* Fill in microseconds */
1054 if (0 > snprintf (date, sizeof(date), date2, timeofday.tv_usec))
1055 abort ();
1056 }
1057
1058 vsnprintf (buf, size, message, va);
1059#if ! defined(GNUNET_CULL_LOGGING)
1060 if (NULL != tmptr)
1061 (void) setup_log_file (tmptr);
1062#endif
1063 if ((0 != (kind & GNUNET_ERROR_TYPE_BULK)) &&
1064 (0 != last_bulk_time.abs_value_us) &&
1065 (0 == strncmp (buf, last_bulk, sizeof(last_bulk))))
1066 {
1067 last_bulk_repeat++;
1068 if ((GNUNET_TIME_absolute_get_duration (last_bulk_time).rel_value_us >
1069 BULK_DELAY_THRESHOLD) ||
1070 (last_bulk_repeat > BULK_REPEAT_THRESHOLD))
1071 flush_bulk (date);
1072 return;
1073 }
1074 flush_bulk (date);
1075 GNUNET_strlcpy (last_bulk, buf, sizeof(last_bulk));
1076 last_bulk_repeat = 0;
1077 last_bulk_kind = kind;
1078 last_bulk_time = GNUNET_TIME_absolute_get ();
1079 GNUNET_strlcpy (last_bulk_comp, comp, sizeof(last_bulk_comp));
1080 output_message (kind, comp, date, buf);
1081 }
1082}
1083
1084
1085/**
1086 * Main log function.
1087 *
1088 * @param kind how serious is the error?
1089 * @param message what is the message (format string)
1090 * @param ... arguments for format string
1091 */
1092void
1093GNUNET_log_nocheck (enum GNUNET_ErrorType kind, const char *message, ...)
1094{
1095 va_list va;
1096
1097 va_start (va, message);
1098 mylog (kind, component, message, va);
1099 va_end (va);
1100}
1101
1102
1103/**
1104 * Log function that specifies an alternative component.
1105 * This function should be used by plugins.
1106 *
1107 * @param kind how serious is the error?
1108 * @param comp component responsible for generating the message
1109 * @param message what is the message (format string)
1110 * @param ... arguments for format string
1111 */
1112void
1113GNUNET_log_from_nocheck (enum GNUNET_ErrorType kind,
1114 const char *comp,
1115 const char *message,
1116 ...)
1117{
1118 va_list va;
1119 char comp_w_pid[128];
1120
1121 if (comp == NULL)
1122 comp = component_nopid;
1123
1124 va_start (va, message);
1125 GNUNET_snprintf (comp_w_pid, sizeof(comp_w_pid), "%s-%d", comp, getpid ());
1126 mylog (kind, comp_w_pid, message, va);
1127 va_end (va);
1128}
1129
1130
1131const char *
1132GNUNET_error_type_to_string (enum GNUNET_ErrorType kind)
1133{
1134 if ((kind & GNUNET_ERROR_TYPE_ERROR) > 0)
1135 return _ ("ERROR");
1136 if ((kind & GNUNET_ERROR_TYPE_WARNING) > 0)
1137 return _ ("WARNING");
1138 if ((kind & GNUNET_ERROR_TYPE_MESSAGE) > 0)
1139 return _ ("MESSAGE");
1140 if ((kind & GNUNET_ERROR_TYPE_INFO) > 0)
1141 return _ ("INFO");
1142 if ((kind & GNUNET_ERROR_TYPE_DEBUG) > 0)
1143 return _ ("DEBUG");
1144 if ((kind & ~GNUNET_ERROR_TYPE_BULK) == 0)
1145 return _ ("NONE");
1146 return _ ("INVALID");
1147}
1148
1149
1150/**
1151 * Convert a hash to a string (for printing debug messages).
1152 *
1153 * @param hc the hash code
1154 * @return string form; will be overwritten by next call to GNUNET_h2s.
1155 */
1156const char *
1157GNUNET_h2s (const struct GNUNET_HashCode *hc)
1158{
1159 static GNUNET_THREAD_LOCAL struct GNUNET_CRYPTO_HashAsciiEncoded ret;
1160
1161 GNUNET_CRYPTO_hash_to_enc (hc, &ret);
1162 ret.encoding[8] = '\0';
1163 return (const char *) ret.encoding;
1164}
1165
1166
1167/**
1168 * Convert a hash to a string (for printing debug messages).
1169 * This is one of the very few calls in the entire API that is
1170 * NOT reentrant! Identical to #GNUNET_h2s(), except that another
1171 * buffer is used so both #GNUNET_h2s() and #GNUNET_h2s2() can be
1172 * used within the same log statement.
1173 *
1174 * @param hc the hash code
1175 * @return string form; will be overwritten by next call to GNUNET_h2s.
1176 */
1177const char *
1178GNUNET_h2s2 (const struct GNUNET_HashCode *hc)
1179{
1180 static GNUNET_THREAD_LOCAL struct GNUNET_CRYPTO_HashAsciiEncoded ret;
1181
1182 GNUNET_CRYPTO_hash_to_enc (hc, &ret);
1183 ret.encoding[8] = '\0';
1184 return (const char *) ret.encoding;
1185}
1186
1187
1188const char *
1189GNUNET_p2s (const struct GNUNET_CRYPTO_EddsaPublicKey *p)
1190{
1191 static GNUNET_THREAD_LOCAL struct GNUNET_CRYPTO_HashAsciiEncoded ret;
1192 struct GNUNET_HashCode hc;
1193
1194 GNUNET_CRYPTO_hash (p, sizeof(*p), &hc);
1195 GNUNET_CRYPTO_hash_to_enc (&hc, &ret);
1196 ret.encoding[6] = '\0';
1197 return (const char *) ret.encoding;
1198}
1199
1200
1201const char *
1202GNUNET_p2s2 (const struct GNUNET_CRYPTO_EddsaPublicKey *p)
1203{
1204 static GNUNET_THREAD_LOCAL struct GNUNET_CRYPTO_HashAsciiEncoded ret;
1205 struct GNUNET_HashCode hc;
1206
1207 GNUNET_CRYPTO_hash (p, sizeof(*p), &hc);
1208 GNUNET_CRYPTO_hash_to_enc (&hc, &ret);
1209 ret.encoding[6] = '\0';
1210 return (const char *) ret.encoding;
1211}
1212
1213
1214const char *
1215GNUNET_e2s (const struct GNUNET_CRYPTO_EcdhePublicKey *p)
1216{
1217 static GNUNET_THREAD_LOCAL struct GNUNET_CRYPTO_HashAsciiEncoded ret;
1218 struct GNUNET_HashCode hc;
1219
1220 GNUNET_CRYPTO_hash (p, sizeof(*p), &hc);
1221 GNUNET_CRYPTO_hash_to_enc (&hc, &ret);
1222 ret.encoding[6] = '\0';
1223 return (const char *) ret.encoding;
1224}
1225
1226
1227const char *
1228GNUNET_e2s2 (const struct GNUNET_CRYPTO_EcdhePublicKey *p)
1229{
1230 static GNUNET_THREAD_LOCAL struct GNUNET_CRYPTO_HashAsciiEncoded ret;
1231 struct GNUNET_HashCode hc;
1232
1233 GNUNET_CRYPTO_hash (p, sizeof(*p), &hc);
1234 GNUNET_CRYPTO_hash_to_enc (&hc, &ret);
1235 ret.encoding[6] = '\0';
1236 return (const char *) ret.encoding;
1237}
1238
1239
1240/**
1241 * @ingroup logging
1242 * Convert a short hash value to a string (for printing debug messages).
1243 * This is one of the very few calls in the entire API that is
1244 * NOT reentrant!
1245 *
1246 * @param shc the hash code
1247 * @return string
1248 */
1249const char *
1250GNUNET_sh2s (const struct GNUNET_ShortHashCode *shc)
1251{
1252 static GNUNET_THREAD_LOCAL char buf[64];
1253
1254 GNUNET_STRINGS_data_to_string (shc, sizeof(*shc), buf, sizeof(buf));
1255 buf[6] = '\0';
1256 return (const char *) buf;
1257}
1258
1259
1260/**
1261 * @ingroup logging
1262 * Convert a UUID to a string (for printing debug messages).
1263 * This is one of the very few calls in the entire API that is
1264 * NOT reentrant!
1265 *
1266 * @param uuid the UUID
1267 * @return string
1268 */
1269const char *
1270GNUNET_uuid2s (const struct GNUNET_Uuid *uuid)
1271{
1272 static GNUNET_THREAD_LOCAL char buf[32];
1273
1274 GNUNET_STRINGS_data_to_string (uuid, sizeof(*uuid), buf, sizeof(buf));
1275 buf[6] = '\0';
1276 return (const char *) buf;
1277}
1278
1279
1280/**
1281 * Convert a hash to a string (for printing debug messages).
1282 * This is one of the very few calls in the entire API that is
1283 * NOT reentrant!
1284 *
1285 * @param hc the hash code
1286 * @return string form; will be overwritten by next call to GNUNET_h2s_full.
1287 */
1288const char *
1289GNUNET_h2s_full (const struct GNUNET_HashCode *hc)
1290{
1291 static GNUNET_THREAD_LOCAL struct GNUNET_CRYPTO_HashAsciiEncoded ret;
1292
1293 GNUNET_CRYPTO_hash_to_enc (hc, &ret);
1294 ret.encoding[sizeof(ret) - 1] = '\0';
1295 return (const char *) ret.encoding;
1296}
1297
1298
1299/**
1300 * Convert a peer identity to a string (for printing debug messages).
1301 *
1302 * @param pid the peer identity
1303 * @return string form of the pid; will be overwritten by next
1304 * call to #GNUNET_i2s.
1305 */
1306const char *
1307GNUNET_i2s (const struct GNUNET_PeerIdentity *pid)
1308{
1309 static GNUNET_THREAD_LOCAL char buf[5];
1310 char *ret;
1311
1312 if (NULL == pid)
1313 return "NULL";
1314 ret = GNUNET_CRYPTO_eddsa_public_key_to_string (&pid->public_key);
1315 GNUNET_strlcpy (buf, ret, sizeof(buf));
1316 GNUNET_free (ret);
1317 return buf;
1318}
1319
1320
1321/**
1322 * Convert a peer identity to a string (for printing debug messages).
1323 * Identical to #GNUNET_i2s(), except that another
1324 * buffer is used so both #GNUNET_i2s() and #GNUNET_i2s2() can be
1325 * used within the same log statement.
1326 *
1327 * @param pid the peer identity
1328 * @return string form of the pid; will be overwritten by next
1329 * call to #GNUNET_i2s.
1330 */
1331const char *
1332GNUNET_i2s2 (const struct GNUNET_PeerIdentity *pid)
1333{
1334 static GNUNET_THREAD_LOCAL char buf[5];
1335 char *ret;
1336
1337 if (NULL == pid)
1338 return "NULL";
1339 ret = GNUNET_CRYPTO_eddsa_public_key_to_string (&pid->public_key);
1340 GNUNET_strlcpy (buf, ret, sizeof(buf));
1341 GNUNET_free (ret);
1342 return buf;
1343}
1344
1345
1346/**
1347 * Convert a peer identity to a string (for printing debug messages).
1348 *
1349 * @param pid the peer identity
1350 * @return string form of the pid; will be overwritten by next
1351 * call to #GNUNET_i2s_full.
1352 */
1353const char *
1354GNUNET_i2s_full (const struct GNUNET_PeerIdentity *pid)
1355{
1356 static GNUNET_THREAD_LOCAL char buf[256];
1357 char *ret;
1358
1359 ret = GNUNET_CRYPTO_eddsa_public_key_to_string (&pid->public_key);
1360 strcpy (buf, ret);
1361 GNUNET_free (ret);
1362 return buf;
1363}
1364
1365
1366/**
1367 * Convert a "struct sockaddr*" (IPv4 or IPv6 address) to a string
1368 * (for printing debug messages). This is one of the very few calls
1369 * in the entire API that is NOT reentrant!
1370 *
1371 * @param addr the address
1372 * @param addrlen the length of the address in @a addr
1373 * @return nicely formatted string for the address
1374 * will be overwritten by next call to #GNUNET_a2s.
1375 */
1376const char *
1377GNUNET_a2s (const struct sockaddr *addr, socklen_t addrlen)
1378{
1379#define LEN \
1380 GNUNET_MAX ((INET6_ADDRSTRLEN + 8), \
1381 (1 + sizeof(struct sockaddr_un) - sizeof(sa_family_t)))
1382 static GNUNET_THREAD_LOCAL char buf[LEN];
1383#undef LEN
1384 static GNUNET_THREAD_LOCAL char b2[6];
1385 const struct sockaddr_in *v4;
1386 const struct sockaddr_un *un;
1387 const struct sockaddr_in6 *v6;
1388 unsigned int off;
1389
1390 if (addr == NULL)
1391 return _ ("unknown address");
1392 switch (addr->sa_family)
1393 {
1394 case AF_INET:
1395 if (addrlen != sizeof(struct sockaddr_in))
1396 return "<invalid v4 address>";
1397 v4 = (const struct sockaddr_in *) addr;
1398 inet_ntop (AF_INET, &v4->sin_addr, buf, INET_ADDRSTRLEN);
1399 if (0 == ntohs (v4->sin_port))
1400 return buf;
1401 strcat (buf, ":");
1402 GNUNET_snprintf (b2, sizeof(b2), "%u", ntohs (v4->sin_port));
1403 strcat (buf, b2);
1404 return buf;
1405
1406 case AF_INET6:
1407 if (addrlen != sizeof(struct sockaddr_in6))
1408 return "<invalid v6 address>";
1409 v6 = (const struct sockaddr_in6 *) addr;
1410 buf[0] = '[';
1411 inet_ntop (AF_INET6, &v6->sin6_addr, &buf[1], INET6_ADDRSTRLEN);
1412 if (0 == ntohs (v6->sin6_port))
1413 return &buf[1];
1414 strcat (buf, "]:");
1415 GNUNET_snprintf (b2, sizeof(b2), "%u", ntohs (v6->sin6_port));
1416 strcat (buf, b2);
1417 return buf;
1418
1419 case AF_UNIX:
1420 if (addrlen <= sizeof(sa_family_t))
1421 return "<unbound UNIX client>";
1422 un = (const struct sockaddr_un *) addr;
1423 off = 0;
1424 if ('\0' == un->sun_path[0])
1425 off++;
1426 memset (buf, 0, sizeof(buf));
1427 GNUNET_snprintf (buf,
1428 sizeof(buf),
1429 "%s%.*s",
1430 (1 == off) ? "@" : "",
1431 (int) (addrlen - sizeof(sa_family_t) - off),
1432 &un->sun_path[off]);
1433 return buf;
1434
1435 default:
1436 return _ ("invalid address");
1437 }
1438}
1439
1440
1441void
1442GNUNET_log_config_missing (enum GNUNET_ErrorType kind,
1443 const char *section,
1444 const char *option)
1445{
1446 GNUNET_log (kind,
1447 _ (
1448 "Configuration fails to specify option `%s' in section `%s'!\n"),
1449 option,
1450 section);
1451}
1452
1453
1454void
1455GNUNET_log_config_invalid (enum GNUNET_ErrorType kind,
1456 const char *section,
1457 const char *option,
1458 const char *required)
1459{
1460 GNUNET_log (
1461 kind,
1462 _ (
1463 "Configuration specifies invalid value for option `%s' in section `%s': %s\n"),
1464 option,
1465 section,
1466 required);
1467}
1468
1469
1470/**
1471 * Set the async scope for the current thread.
1472 *
1473 * @param aid the async scope identifier
1474 * @param[out] old_scope location to save the old scope
1475 */
1476void
1477GNUNET_async_scope_enter (const struct GNUNET_AsyncScopeId *aid,
1478 struct GNUNET_AsyncScopeSave *old_scope)
1479{
1480 *old_scope = current_async_scope;
1481 current_async_scope.have_scope = GNUNET_YES;
1482 current_async_scope.scope_id = *aid;
1483}
1484
1485
1486/**
1487 * Clear the current thread's async scope.
1488 *
1489 * @param old_scope scope to restore
1490 */
1491void
1492GNUNET_async_scope_restore (struct GNUNET_AsyncScopeSave *old_scope)
1493{
1494 current_async_scope = *old_scope;
1495}
1496
1497
1498/**
1499 * Generate a fresh async scope identifier.
1500 *
1501 * @param[out] aid_ret pointer to where the result is stored
1502 */
1503void
1504GNUNET_async_scope_fresh (struct GNUNET_AsyncScopeId *aid_ret)
1505{
1506 GNUNET_CRYPTO_random_block (GNUNET_CRYPTO_QUALITY_WEAK,
1507 aid_ret,
1508 sizeof(struct GNUNET_AsyncScopeId));
1509}
1510
1511
1512/**
1513 * Get the current async scope.
1514 *
1515 * @param[out] scope_ret pointer to where the result is stored
1516 */
1517void
1518GNUNET_async_scope_get (struct GNUNET_AsyncScopeSave *scope_ret)
1519{
1520 *scope_ret = current_async_scope;
1521}
1522
1523
1524/**
1525 * Initializer
1526 */
1527void __attribute__ ((constructor))
1528GNUNET_util_cl_init ()
1529{
1530 GNUNET_stderr = stderr;
1531}
1532
1533
1534/**
1535 * Destructor
1536 */
1537void __attribute__ ((destructor))
1538GNUNET_util_cl_fini ()
1539{
1540
1541}
1542
1543
1544/* end of common_logging.c */
diff --git a/src/lib/util/compress.c b/src/lib/util/compress.c
new file mode 100644
index 000000000..73fa25bd9
--- /dev/null
+++ b/src/lib/util/compress.c
@@ -0,0 +1,91 @@
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/**
22 * @file util/compress.c
23 * @brief Simple (de)compression logic
24 * @author Philipp Toelke
25 * @author Martin Schanzenbach
26 */
27
28#include "platform.h"
29#include "gnunet_util_lib.h"
30#include <zlib.h>
31
32int
33GNUNET_try_compression (const char *data,
34 size_t old_size,
35 char **result,
36 size_t *new_size)
37{
38 char *tmp;
39 uLongf dlen;
40
41 *result = NULL;
42 *new_size = 0;
43#ifdef compressBound
44 dlen = compressBound (old_size);
45#else
46 dlen = old_size + (old_size / 100) + 20;
47 /* documentation says 100.1% oldSize + 12 bytes, but we
48 * should be able to overshoot by more to be safe */
49#endif
50 tmp = GNUNET_malloc (dlen);
51 if (Z_OK ==
52 compress2 ((Bytef *) tmp,
53 &dlen,
54 (const Bytef *) data,
55 old_size, 9))
56 {
57 if (dlen < old_size)
58 {
59 *result = tmp;
60 *new_size = dlen;
61 return GNUNET_YES;
62 }
63 }
64 GNUNET_free (tmp);
65 return GNUNET_NO;
66}
67
68
69char *
70GNUNET_decompress (const char *input,
71 size_t input_size,
72 size_t output_size)
73{
74 char *output;
75 uLongf olen;
76
77 olen = output_size;
78 output = GNUNET_malloc (olen);
79 if (Z_OK ==
80 uncompress ((Bytef *) output,
81 &olen,
82 (const Bytef *) input,
83 input_size))
84 return output;
85 GNUNET_free (output);
86 return NULL;
87}
88
89
90
91/* end of compress.c */
diff --git a/src/lib/util/configuration.c b/src/lib/util/configuration.c
new file mode 100644
index 000000000..17ba253ff
--- /dev/null
+++ b/src/lib/util/configuration.c
@@ -0,0 +1,2576 @@
1/*
2 This file is part of GNUnet.
3 Copyright (C) 2006, 2007, 2008, 2009, 2013, 2020, 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 * @file src/util/configuration.c
22 * @brief configuration management
23 * @author Christian Grothoff
24 */
25
26#include "platform.h"
27#include "gnunet_util_lib.h"
28#include "gnunet_configuration_lib.h"
29
30#define LOG(kind, ...) GNUNET_log_from (kind, "util", __VA_ARGS__)
31
32#define LOG_STRERROR_FILE(kind, syscall, filename) \
33 GNUNET_log_from_strerror_file (kind, "util", syscall, filename)
34
35/**
36 * @brief configuration entry
37 */
38struct ConfigEntry
39{
40 /**
41 * This is a linked list.
42 */
43 struct ConfigEntry *next;
44
45 /**
46 * key for this entry
47 */
48 char *key;
49
50 /**
51 * current, committed value
52 */
53 char *val;
54
55 /**
56 * Diagnostics information for the filename.
57 */
58 char *hint_filename;
59
60 /**
61 * Diagnostics information for the line number.
62 */
63 unsigned int hint_lineno;
64};
65
66
67/**
68 * @brief configuration section
69 */
70struct ConfigSection
71{
72 /**
73 * This is a linked list.
74 */
75 struct ConfigSection *next;
76
77 /**
78 * entries in the section
79 */
80 struct ConfigEntry *entries;
81
82 /**
83 * name of the section
84 */
85 char *name;
86
87 /**
88 * Is the configuration section marked as inaccessible?
89 *
90 * This can happen if the section name is used in an \@inline-secret\@
91 * directive, but the referenced file can't be found or accessed.
92 */
93 bool inaccessible;
94
95 /**
96 * Diagnostics hint for the secret file.
97 */
98 char *hint_secret_filename;
99
100 /**
101 * Extra information regarding permissions of the secret file.
102 */
103 char *hint_secret_stat;
104
105 /**
106 * For secret sections: Where was this inlined from?
107 */
108 char *hint_inlined_from_filename;
109
110 /**
111 * For secret sections: Where was this inlined from?
112 */
113 unsigned int hint_inlined_from_line;
114};
115
116struct ConfigFile
117{
118 /**
119 * Source filename.
120 */
121 char *source_filename;
122
123 /**
124 * Level in the tree of loaded config files.
125 */
126 unsigned int level;
127
128 struct ConfigFile *prev;
129
130 struct ConfigFile *next;
131
132 /**
133 * Was this configuration file parsed via
134 * \@inline-secret\@?
135 */
136 char *hint_restrict_section;
137
138 /**
139 * Was this configuration file inaccessible?
140 */
141 bool hint_inaccessible;
142};
143
144
145/**
146 * @brief configuration data
147 */
148struct GNUNET_CONFIGURATION_Handle
149{
150 /**
151 * Configuration sections.
152 */
153 struct ConfigSection *sections;
154
155 /**
156 * Linked list of loaded files.
157 */
158 struct ConfigFile *loaded_files_head;
159
160 /**
161 * Linked list of loaded files.
162 */
163 struct ConfigFile *loaded_files_tail;
164
165 /**
166 * Current nesting level of file loading.
167 */
168 unsigned int current_nest_level;
169
170 /**
171 * Enable diagnostics.
172 */
173 bool diagnostics;
174
175 /**
176 * Modification indication since last save
177 * #GNUNET_NO if clean, #GNUNET_YES if dirty,
178 * #GNUNET_SYSERR on error (i.e. last save failed)
179 */
180 enum GNUNET_GenericReturnValue dirty;
181
182 /**
183 * Was the configuration ever loaded via GNUNET_CONFIGURATION_load?
184 */
185 bool load_called;
186
187 /**
188 * Name of the entry point configuration file.
189 */
190 char *main_filename;
191
192 /**
193 * When parsing into this configuration, and this value
194 * is non-NULL, only parse sections of the same name,
195 * and ban import statements.
196 */
197 const char *restrict_section;
198};
199
200
201/**
202 * Used for diffing a configuration object against
203 * the default one
204 */
205struct DiffHandle
206{
207 const struct GNUNET_CONFIGURATION_Handle *cfg_default;
208
209 struct GNUNET_CONFIGURATION_Handle *cfgDiff;
210};
211
212
213void
214GNUNET_CONFIGURATION_enable_diagnostics (struct
215 GNUNET_CONFIGURATION_Handle *cfg)
216{
217 cfg->diagnostics = true;
218}
219
220
221struct GNUNET_CONFIGURATION_Handle *
222GNUNET_CONFIGURATION_create ()
223{
224 struct GNUNET_CONFIGURATION_Handle *cfg;
225 char *p;
226
227 cfg = GNUNET_new (struct GNUNET_CONFIGURATION_Handle);
228 /* make certain values from the project data available
229 as PATHS */
230 p = GNUNET_OS_installation_get_path (GNUNET_OS_IPK_DATADIR);
231 if (NULL != p)
232 {
233 GNUNET_CONFIGURATION_set_value_string (cfg,
234 "PATHS",
235 "DATADIR",
236 p);
237 GNUNET_free (p);
238 }
239 p = GNUNET_OS_installation_get_path (GNUNET_OS_IPK_LIBDIR);
240 if (NULL != p)
241 {
242 GNUNET_CONFIGURATION_set_value_string (cfg,
243 "PATHS",
244 "LIBDIR",
245 p);
246 GNUNET_free (p);
247 }
248 p = GNUNET_OS_installation_get_path (GNUNET_OS_IPK_BINDIR);
249 if (NULL != p)
250 {
251 GNUNET_CONFIGURATION_set_value_string (cfg,
252 "PATHS",
253 "BINDIR",
254 p);
255 GNUNET_free (p);
256 }
257 p = GNUNET_OS_installation_get_path (GNUNET_OS_IPK_PREFIX);
258 if (NULL != p)
259 {
260 GNUNET_CONFIGURATION_set_value_string (cfg,
261 "PATHS",
262 "PREFIX",
263 p);
264 GNUNET_free (p);
265 }
266 p = GNUNET_OS_installation_get_path (GNUNET_OS_IPK_LOCALEDIR);
267 if (NULL != p)
268 {
269 GNUNET_CONFIGURATION_set_value_string (cfg,
270 "PATHS",
271 "LOCALEDIR",
272 p);
273 GNUNET_free (p);
274 }
275 p = GNUNET_OS_installation_get_path (GNUNET_OS_IPK_ICONDIR);
276 if (NULL != p)
277 {
278 GNUNET_CONFIGURATION_set_value_string (cfg,
279 "PATHS",
280 "ICONDIR",
281 p);
282 GNUNET_free (p);
283 }
284 p = GNUNET_OS_installation_get_path (GNUNET_OS_IPK_DOCDIR);
285 if (NULL != p)
286 {
287 GNUNET_CONFIGURATION_set_value_string (cfg,
288 "PATHS",
289 "DOCDIR",
290 p);
291 GNUNET_free (p);
292 }
293 p = GNUNET_OS_installation_get_path (GNUNET_OS_IPK_LIBEXECDIR);
294 if (NULL != p)
295 {
296 GNUNET_CONFIGURATION_set_value_string (cfg,
297 "PATHS",
298 "LIBEXECDIR",
299 p);
300 GNUNET_free (p);
301 }
302 return cfg;
303}
304
305
306void
307GNUNET_CONFIGURATION_destroy (struct GNUNET_CONFIGURATION_Handle *cfg)
308{
309 struct ConfigSection *sec;
310 struct ConfigFile *cf;
311
312 while (NULL != (sec = cfg->sections))
313 GNUNET_CONFIGURATION_remove_section (cfg, sec->name);
314 while (NULL != (cf = cfg->loaded_files_head))
315 {
316 GNUNET_free (cf->hint_restrict_section);
317 GNUNET_free (cf->source_filename);
318 GNUNET_CONTAINER_DLL_remove (cfg->loaded_files_head,
319 cfg->loaded_files_tail,
320 cf);
321 GNUNET_free (cf);
322 }
323 GNUNET_free (cfg->main_filename);
324 GNUNET_free (cfg);
325}
326
327
328enum GNUNET_GenericReturnValue
329GNUNET_CONFIGURATION_parse_and_run (const char *filename,
330 GNUNET_CONFIGURATION_Callback cb,
331 void *cb_cls)
332{
333 struct GNUNET_CONFIGURATION_Handle *cfg;
334 enum GNUNET_GenericReturnValue ret;
335
336 cfg = GNUNET_CONFIGURATION_create ();
337 if (GNUNET_OK !=
338 GNUNET_CONFIGURATION_load (cfg,
339 filename))
340 {
341 GNUNET_break (0);
342 GNUNET_CONFIGURATION_destroy (cfg);
343 return GNUNET_SYSERR;
344 }
345 ret = cb (cb_cls, cfg);
346 GNUNET_CONFIGURATION_destroy (cfg);
347 return ret;
348}
349
350
351/**
352 * Closure to collect_files_cb.
353 */
354struct CollectFilesContext
355{
356 /**
357 * Collected files from globbing.
358 */
359 char **files;
360
361 /**
362 * Size of the files array.
363 */
364 unsigned int files_length;
365};
366
367
368/**
369 * Function called with a filename.
370 *
371 * @param cls closure
372 * @param filename complete filename (absolute path)
373 * @return #GNUNET_OK to continue to iterate,
374 * #GNUNET_NO to stop iteration with no error,
375 * #GNUNET_SYSERR to abort iteration with error!
376 */
377static enum GNUNET_GenericReturnValue
378collect_files_cb (void *cls,
379 const char *filename)
380{
381 struct CollectFilesContext *igc = cls;
382
383 GNUNET_array_append (igc->files,
384 igc->files_length,
385 GNUNET_strdup (filename));
386 return GNUNET_OK;
387}
388
389
390/**
391 * Find a section entry from a configuration.
392 *
393 * @param cfg configuration to search in
394 * @param section name of the section to look for
395 * @return matching entry, NULL if not found
396 */
397static struct ConfigSection *
398find_section (const struct GNUNET_CONFIGURATION_Handle *cfg,
399 const char *section)
400{
401 struct ConfigSection *pos;
402
403 pos = cfg->sections;
404 while ((pos != NULL) && (0 != strcasecmp (section, pos->name)))
405 pos = pos->next;
406 return pos;
407}
408
409
410static int
411pstrcmp (const void *a, const void *b)
412{
413 return strcmp (*((const char **) a), *((const char **) b));
414}
415
416
417/**
418 * Handle an inline directive.
419 *
420 * @returns #GNUNET_SYSERR on error, #GNUNET_OK otherwise
421 */
422enum GNUNET_GenericReturnValue
423handle_inline (struct GNUNET_CONFIGURATION_Handle *cfg,
424 const char *path_or_glob,
425 bool path_is_glob,
426 const char *restrict_section,
427 const char *source_filename,
428 unsigned int source_lineno)
429{
430 char *inline_path = NULL;
431 struct GNUNET_CONFIGURATION_Handle *other_cfg = NULL;
432 struct CollectFilesContext igc = {
433 .files = NULL,
434 .files_length = 0,
435 };
436 enum GNUNET_GenericReturnValue fun_ret;
437 unsigned int old_nest_level = cfg->current_nest_level++;
438
439 /* We support the section restriction only for non-globs */
440 GNUNET_assert (! (path_is_glob && (NULL != restrict_section)));
441
442 if (NULL == source_filename)
443 {
444 LOG (GNUNET_ERROR_TYPE_DEBUG,
445 "Refusing to parse inline configurations, "
446 "not allowed without source filename!\n");
447 fun_ret = GNUNET_SYSERR;
448 goto cleanup;
449 }
450
451 if ('/' == *path_or_glob)
452 inline_path = GNUNET_strdup (path_or_glob);
453 else
454 {
455 /* We compute the canonical, absolute path first,
456 so that relative imports resolve properly with symlinked
457 config files. */
458 char *source_realpath;
459 char *endsep;
460
461 source_realpath = realpath (source_filename,
462 NULL);
463 if (NULL == source_realpath)
464 {
465 /* Couldn't even resolve path of base dir. */
466 GNUNET_break (0);
467 /* failed to parse included config */
468 fun_ret = GNUNET_SYSERR;
469 goto cleanup;
470 }
471 endsep = strrchr (source_realpath, '/');
472 GNUNET_assert (NULL != endsep);
473 *endsep = '\0';
474 GNUNET_asprintf (&inline_path,
475 "%s/%s",
476 source_realpath,
477 path_or_glob);
478 free (source_realpath);
479 }
480
481 if (path_is_glob)
482 {
483 int nret;
484
485 LOG (GNUNET_ERROR_TYPE_DEBUG,
486 "processing config glob '%s'\n",
487 inline_path);
488
489 nret = GNUNET_DISK_glob (inline_path, collect_files_cb, &igc);
490 if (-1 == nret)
491 {
492 fun_ret = GNUNET_SYSERR;
493 goto cleanup;
494 }
495 GNUNET_assert (nret == igc.files_length);
496 qsort (igc.files, igc.files_length, sizeof (char *), pstrcmp);
497 for (int i = 0; i < nret; i++)
498 {
499 if (GNUNET_OK !=
500 GNUNET_CONFIGURATION_parse (cfg,
501 igc.files[i]))
502 {
503 fun_ret = GNUNET_SYSERR;
504 goto cleanup;
505 }
506 }
507 fun_ret = GNUNET_OK;
508 }
509 else if (NULL != restrict_section)
510 {
511 enum GNUNET_GenericReturnValue inner_ret;
512 struct ConfigSection *cs;
513 struct ConfigFile *cf = GNUNET_new (struct ConfigFile);
514
515 inner_ret = GNUNET_DISK_file_test_read (inline_path);
516
517 cs = find_section (cfg, restrict_section);
518
519 if (NULL == cs)
520 {
521 cs = GNUNET_new (struct ConfigSection);
522 cs->name = GNUNET_strdup (restrict_section);
523 cs->next = cfg->sections;
524 cfg->sections = cs;
525 cs->entries = NULL;
526 }
527 if (cfg->diagnostics)
528 {
529 char *sfn = GNUNET_STRINGS_filename_expand (inline_path);
530 struct stat istat;
531
532 cs->hint_secret_filename = sfn;
533 if (0 == stat (sfn, &istat))
534 {
535 struct passwd *pw = getpwuid (istat.st_uid);
536 struct group *gr = getgrgid (istat.st_gid);
537 char *pwname = (NULL == pw) ? "<unknown>" : pw->pw_name;
538 char *grname = (NULL == gr) ? "<unknown>" : gr->gr_name;
539
540 GNUNET_asprintf (&cs->hint_secret_stat,
541 "%s:%s %o",
542 pwname,
543 grname,
544 istat.st_mode);
545 }
546 else
547 {
548 cs->hint_secret_stat = GNUNET_strdup ("<can't stat file>");
549 }
550 if (source_filename)
551 {
552 /* Possible that this secret section has been inlined before */
553 GNUNET_free (cs->hint_inlined_from_filename);
554 cs->hint_inlined_from_filename = GNUNET_strdup (source_filename);
555 cs->hint_inlined_from_line = source_lineno;
556 }
557 }
558
559 /* Put file in the load list for diagnostics, even if we can't access it. */
560 {
561 cf->level = cfg->current_nest_level;
562 cf->source_filename = GNUNET_strdup (inline_path);
563 cf->hint_restrict_section = GNUNET_strdup (restrict_section);
564 GNUNET_CONTAINER_DLL_insert_tail (cfg->loaded_files_head,
565 cfg->loaded_files_tail,
566 cf);
567 }
568
569 if (GNUNET_OK != inner_ret)
570 {
571 cs->inaccessible = true;
572 cf->hint_inaccessible = true;
573 /* File can't be accessed, but that's okay. */
574 fun_ret = GNUNET_OK;
575 goto cleanup;
576 }
577
578 other_cfg = GNUNET_CONFIGURATION_create ();
579 other_cfg->restrict_section = restrict_section;
580 inner_ret = GNUNET_CONFIGURATION_parse (other_cfg,
581 inline_path);
582 if (GNUNET_OK != inner_ret)
583 {
584 cf->hint_inaccessible = true;
585 fun_ret = inner_ret;
586 goto cleanup;
587 }
588
589 cs = find_section (other_cfg, restrict_section);
590 if (NULL == cs)
591 {
592 LOG (GNUNET_ERROR_TYPE_INFO,
593 "Configuration file '%s' loaded with @inline-secret@ "
594 "does not contain section '%s'.\n",
595 inline_path,
596 restrict_section);
597 /* Inlined onfiguration is accessible but doesn't contain any values.
598 We treat this as if the inlined section was empty, and do not
599 consider it an error. */
600 fun_ret = GNUNET_OK;
601 goto cleanup;
602 }
603 for (struct ConfigEntry *ce = cs->entries;
604 NULL != ce;
605 ce = ce->next)
606 GNUNET_CONFIGURATION_set_value_string (cfg,
607 restrict_section,
608 ce->key,
609 ce->val);
610 fun_ret = GNUNET_OK;
611 }
612 else if (GNUNET_OK !=
613 GNUNET_CONFIGURATION_parse (cfg,
614 inline_path))
615 {
616 fun_ret = GNUNET_SYSERR;
617 goto cleanup;
618 }
619 else
620 {
621 fun_ret = GNUNET_OK;
622 }
623cleanup:
624 cfg->current_nest_level = old_nest_level;
625 if (NULL != other_cfg)
626 GNUNET_CONFIGURATION_destroy (other_cfg);
627 GNUNET_free (inline_path);
628 if (igc.files_length > 0)
629 {
630 for (size_t i = 0; i < igc.files_length; i++)
631 GNUNET_free (igc.files[i]);
632 GNUNET_array_grow (igc.files, igc.files_length, 0);
633 }
634 return fun_ret;
635}
636
637
638/**
639 * Find an entry from a configuration.
640 *
641 * @param cfg handle to the configuration
642 * @param section section the option is in
643 * @param key the option
644 * @return matching entry, NULL if not found
645 */
646static struct ConfigEntry *
647find_entry (const struct GNUNET_CONFIGURATION_Handle *cfg,
648 const char *section,
649 const char *key)
650{
651 struct ConfigSection *sec;
652 struct ConfigEntry *pos;
653
654 if (NULL == (sec = find_section (cfg, section)))
655 return NULL;
656 if (sec->inaccessible)
657 {
658 LOG (GNUNET_ERROR_TYPE_WARNING,
659 "Section '%s' is marked as inaccessible, because the configuration "
660 " file that contains the section can't be read. Attempts to use "
661 "option '%s' will fail.\n",
662 section,
663 key);
664 return NULL;
665 }
666 pos = sec->entries;
667 while ((pos != NULL) && (0 != strcasecmp (key, pos->key)))
668 pos = pos->next;
669 return pos;
670}
671
672
673/**
674 * Set a configuration hint.
675 *
676 * @param cfg configuration handle
677 * @param section section
678 * @param option config option
679 * @param hint_filename
680 * @param hint_line
681 */
682static void
683set_entry_hint (struct GNUNET_CONFIGURATION_Handle *cfg,
684 const char *section,
685 const char *option,
686 const char *hint_filename,
687 unsigned int hint_line)
688{
689 struct ConfigEntry *e = find_entry (cfg, section, option);
690 if (! cfg->diagnostics)
691 return;
692 if (! e)
693 return;
694 e->hint_filename = GNUNET_strdup (hint_filename);
695 e->hint_lineno = hint_line;
696}
697
698
699enum GNUNET_GenericReturnValue
700GNUNET_CONFIGURATION_deserialize (struct GNUNET_CONFIGURATION_Handle *cfg,
701 const char *mem,
702 size_t size,
703 const char *source_filename)
704{
705 size_t line_size;
706 unsigned int nr;
707 size_t r_bytes;
708 size_t to_read;
709 enum GNUNET_GenericReturnValue ret;
710 char *section;
711 char *eq;
712 char *tag;
713 char *value;
714 char *line_orig = NULL;
715
716 ret = GNUNET_OK;
717 section = NULL;
718 nr = 0;
719 r_bytes = 0;
720 while (r_bytes < size)
721 {
722 char *pos;
723 char *line;
724 bool emptyline;
725
726 GNUNET_free (line_orig);
727 /* fgets-like behaviour on buffer */
728 to_read = size - r_bytes;
729 pos = memchr (&mem[r_bytes], '\n', to_read);
730 if (NULL == pos)
731 {
732 line_orig = GNUNET_strndup (&mem[r_bytes],
733 line_size = to_read);
734 r_bytes += line_size;
735 }
736 else
737 {
738 line_orig = GNUNET_strndup (&mem[r_bytes],
739 line_size = (pos - &mem[r_bytes]));
740 r_bytes += line_size + 1;
741 }
742 line = line_orig;
743 /* increment line number */
744 nr++;
745 /* tabs and '\r' are whitespace */
746 emptyline = GNUNET_YES;
747 for (size_t i = 0; i < line_size; i++)
748 {
749 if (line[i] == '\t')
750 line[i] = ' ';
751 if (line[i] == '\r')
752 line[i] = ' ';
753 if (' ' != line[i])
754 emptyline = GNUNET_NO;
755 }
756 /* ignore empty lines */
757 if (GNUNET_YES == emptyline)
758 continue;
759
760 /* remove tailing whitespace */
761 for (size_t i = line_size - 1;
762 (i >= 1) && (isspace ((unsigned char) line[i]));
763 i--)
764 line[i] = '\0';
765
766 /* remove leading whitespace */
767 for (; line[0] != '\0' && (isspace ((unsigned char) line[0])); line++)
768 ;
769
770 /* ignore comments */
771 if ( ('#' == line[0]) ||
772 ('%' == line[0]) )
773 continue;
774
775 /* Handle special directives. */
776 if ('@' == line[0])
777 {
778 char *end = strchr (line + 1, '@');
779 char *directive;
780 enum GNUNET_GenericReturnValue directive_ret;
781
782 if (NULL != cfg->restrict_section)
783 {
784 LOG (GNUNET_ERROR_TYPE_WARNING,
785 "Illegal directive in line %u (parsing restricted section %s)\n",
786 nr,
787 cfg->restrict_section);
788 ret = GNUNET_SYSERR;
789 break;
790 }
791
792 if (NULL == end)
793 {
794 LOG (GNUNET_ERROR_TYPE_WARNING,
795 "Bad directive in line %u\n",
796 nr);
797 ret = GNUNET_SYSERR;
798 break;
799 }
800 *end = '\0';
801 directive = line + 1;
802
803 if (0 == strcasecmp (directive,
804 "INLINE"))
805 {
806 const char *path = end + 1;
807
808 /* Skip space before path */
809 for (; isspace (*path); path++)
810 ;
811
812 directive_ret = handle_inline (cfg,
813 path,
814 false,
815 NULL,
816 source_filename,
817 nr);
818 }
819 else if (0 == strcasecmp (directive,
820 "INLINE-MATCHING"))
821 {
822 const char *path = end + 1;
823
824 /* Skip space before path */
825 for (; isspace (*path); path++)
826 ;
827
828 directive_ret = handle_inline (cfg,
829 path,
830 true,
831 NULL,
832 source_filename,
833 nr);
834 }
835 else if (0 == strcasecmp (directive,
836 "INLINE-SECRET"))
837 {
838 char *secname = end + 1;
839 char *secname_end;
840 const char *path;
841
842 /* Skip space before secname */
843 for (; isspace (*secname); secname++)
844 ;
845
846 secname_end = strchr (secname, ' ');
847
848 if (NULL == secname_end)
849 {
850 LOG (GNUNET_ERROR_TYPE_WARNING,
851 "Bad inline-secret directive in line %u\n",
852 nr);
853 ret = GNUNET_SYSERR;
854 break;
855 }
856 *secname_end = '\0';
857 path = secname_end + 1;
858
859 /* Skip space before path */
860 for (; isspace (*path); path++)
861 ;
862
863 directive_ret = handle_inline (cfg,
864 path,
865 false,
866 secname,
867 source_filename,
868 nr);
869 }
870 else
871 {
872 LOG (GNUNET_ERROR_TYPE_WARNING,
873 "Unknown or malformed directive '%s' in line %u\n",
874 directive,
875 nr);
876 ret = GNUNET_SYSERR;
877 break;
878 }
879 if (GNUNET_OK != directive_ret)
880 {
881 ret = directive_ret;
882 break;
883 }
884 continue;
885 }
886 if ( ('[' == line[0]) &&
887 (']' == line[line_size - 1]) )
888 {
889 /* [value] */
890 line[line_size - 1] = '\0';
891 value = &line[1];
892 GNUNET_free (section);
893 section = GNUNET_strdup (value);
894 continue;
895 }
896 if (NULL != (eq = strchr (line, '=')))
897 {
898 size_t i;
899
900 if (NULL == section)
901 {
902 LOG (GNUNET_ERROR_TYPE_WARNING,
903 "Syntax error while deserializing in line %u (option without section)\n",
904 nr);
905 ret = GNUNET_SYSERR;
906 break;
907 }
908
909 /* tag = value */
910 tag = GNUNET_strndup (line, eq - line);
911 /* remove tailing whitespace */
912 for (i = strlen (tag) - 1;
913 (i >= 1) && (isspace ((unsigned char) tag[i]));
914 i--)
915 tag[i] = '\0';
916
917 /* Strip whitespace */
918 value = eq + 1;
919 while (isspace ((unsigned char) value[0]))
920 value++;
921 for (i = strlen (value) - 1;
922 (i >= 1) && (isspace ((unsigned char) value[i]));
923 i--)
924 value[i] = '\0';
925
926 /* remove quotes */
927 i = 0;
928 if ( ('"' == value[0]) &&
929 ('"' == value[strlen (value) - 1]) )
930 {
931 value[strlen (value) - 1] = '\0';
932 value++;
933 }
934 GNUNET_CONFIGURATION_set_value_string (cfg,
935 section,
936 tag,
937 &value[i]);
938 if (cfg->diagnostics)
939 {
940 set_entry_hint (cfg,
941 section,
942 tag,
943 source_filename
944 ? source_filename
945 : "<input>",
946 nr);
947 }
948 GNUNET_free (tag);
949 continue;
950 }
951 /* parse error */
952 LOG (GNUNET_ERROR_TYPE_WARNING,
953 "Syntax error while deserializing in line %u\n",
954 nr);
955 ret = GNUNET_SYSERR;
956 break;
957 }
958 GNUNET_free (line_orig);
959 GNUNET_free (section);
960 GNUNET_assert ( (GNUNET_OK != ret) ||
961 (r_bytes == size) );
962 return ret;
963}
964
965
966enum GNUNET_GenericReturnValue
967GNUNET_CONFIGURATION_parse (struct GNUNET_CONFIGURATION_Handle *cfg,
968 const char *filename)
969{
970 uint64_t fs64;
971 size_t fs;
972 char *fn;
973 char *mem;
974 int dirty;
975 enum GNUNET_GenericReturnValue ret;
976 ssize_t sret;
977
978 fn = GNUNET_STRINGS_filename_expand (filename);
979 LOG (GNUNET_ERROR_TYPE_DEBUG, "Asked to parse config file `%s'\n", fn);
980 if (NULL == fn)
981 return GNUNET_SYSERR;
982
983
984 /* Check for cycles */
985 {
986 unsigned int lvl = cfg->current_nest_level;
987 struct ConfigFile *cf = cfg->loaded_files_tail;
988 struct ConfigFile *parent = NULL;
989
990
991 for (; NULL != cf; parent = cf, cf = cf->prev)
992 {
993 /* Check parents based on level, skipping children of siblings. */
994 if (cf->level >= lvl)
995 continue;
996 lvl = cf->level;
997 if ( (NULL == cf->source_filename) || (NULL == filename))
998 continue;
999 if (0 == strcmp (cf->source_filename, filename))
1000 {
1001 if (NULL == parent)
1002 {
1003 LOG (GNUNET_ERROR_TYPE_ERROR,
1004 "Forbidden direct cyclic configuration import (%s -> %s)\n",
1005 cf->source_filename,
1006 filename);
1007 }
1008 else
1009 LOG (GNUNET_ERROR_TYPE_ERROR,
1010 "Forbidden indirect cyclic configuration import (%s -> ... -> %s -> %s)\n",
1011 cf->source_filename,
1012 parent->source_filename,
1013 filename);
1014 GNUNET_free (fn);
1015 return GNUNET_SYSERR;
1016 }
1017 }
1018
1019 }
1020
1021 /* Keep track of loaded files.*/
1022 {
1023 struct ConfigFile *cf = GNUNET_new (struct ConfigFile);
1024
1025 cf->level = cfg->current_nest_level;
1026 cf->source_filename = GNUNET_strdup (filename ? filename : "<input>");
1027 GNUNET_CONTAINER_DLL_insert_tail (cfg->loaded_files_head,
1028 cfg->loaded_files_tail,
1029 cf);
1030 }
1031
1032 dirty = cfg->dirty; /* back up value! */
1033 if (GNUNET_SYSERR ==
1034 GNUNET_DISK_file_size (fn,
1035 &fs64,
1036 GNUNET_YES,
1037 GNUNET_YES))
1038 {
1039 LOG (GNUNET_ERROR_TYPE_WARNING,
1040 "Error while determining the file size of `%s'\n",
1041 fn);
1042 GNUNET_free (fn);
1043 return GNUNET_SYSERR;
1044 }
1045 if (fs64 > SIZE_MAX)
1046 {
1047 GNUNET_break (0); /* File size is more than the heap size */
1048 GNUNET_free (fn);
1049 return GNUNET_SYSERR;
1050 }
1051 fs = fs64;
1052 mem = GNUNET_malloc (fs);
1053 sret = GNUNET_DISK_fn_read (fn, mem, fs);
1054 if ((sret < 0) || (fs != (size_t) sret))
1055 {
1056 LOG (GNUNET_ERROR_TYPE_WARNING,
1057 "Error while reading file `%s'\n",
1058 fn);
1059 GNUNET_free (fn);
1060 GNUNET_free (mem);
1061 return GNUNET_SYSERR;
1062 }
1063 LOG (GNUNET_ERROR_TYPE_DEBUG,
1064 "Deserializing contents of file `%s'\n",
1065 fn);
1066 ret = GNUNET_CONFIGURATION_deserialize (cfg,
1067 mem,
1068 fs,
1069 fn);
1070 if (GNUNET_SYSERR == ret)
1071 {
1072 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
1073 _ ("Failed to parse configuration file `%s'\n"),
1074 fn);
1075 }
1076 GNUNET_free (fn);
1077 GNUNET_free (mem);
1078 /* restore dirty flag - anything we set in the meantime
1079 * came from disk */
1080 cfg->dirty = dirty;
1081 return ret;
1082}
1083
1084
1085enum GNUNET_GenericReturnValue
1086GNUNET_CONFIGURATION_is_dirty (const struct GNUNET_CONFIGURATION_Handle *cfg)
1087{
1088 return cfg->dirty;
1089}
1090
1091
1092/**
1093 * Should we skip this configuration entry when serializing?
1094 *
1095 * @param sec section name
1096 * @param key key
1097 * @return true if we should skip it
1098 */
1099static bool
1100do_skip (const char *sec,
1101 const char *key)
1102{
1103 if (0 != strcasecmp ("PATHS",
1104 sec))
1105 return false;
1106 return ( (0 == strcasecmp ("DATADIR",
1107 key)) ||
1108 (0 == strcasecmp ("LIBDIR",
1109 key)) ||
1110 (0 == strcasecmp ("BINDIR",
1111 key)) ||
1112 (0 == strcasecmp ("PREFIX",
1113 key)) ||
1114 (0 == strcasecmp ("LOCALEDIR",
1115 key)) ||
1116 (0 == strcasecmp ("ICONDIR",
1117 key)) ||
1118 (0 == strcasecmp ("DOCDIR",
1119 key)) ||
1120 (0 == strcasecmp ("DEFAULTCONFIG",
1121 key)) ||
1122 (0 == strcasecmp ("LIBEXECDIR",
1123 key)) );
1124}
1125
1126
1127char *
1128GNUNET_CONFIGURATION_serialize (const struct GNUNET_CONFIGURATION_Handle *cfg,
1129 size_t *size)
1130{
1131 char *mem;
1132 char *cbuf;
1133 char *val;
1134 char *pos;
1135 size_t m_size;
1136 size_t c_size;
1137
1138 /* Pass1 : calculate the buffer size required */
1139 m_size = 0;
1140 for (struct ConfigSection *sec = cfg->sections;
1141 NULL != sec;
1142 sec = sec->next)
1143 {
1144 if (sec->inaccessible)
1145 continue;
1146 /* For each section we need to add 3 characters: {'[',']','\n'} */
1147 m_size += strlen (sec->name) + 3;
1148 for (struct ConfigEntry *ent = sec->entries;
1149 NULL != ent;
1150 ent = ent->next)
1151 {
1152 if (do_skip (sec->name,
1153 ent->key))
1154 continue;
1155 if (NULL != ent->val)
1156 {
1157 /* if val has any '\n' then they occupy +1 character as '\n'->'\\','n' */
1158 pos = ent->val;
1159 while (NULL != (pos = strstr (pos, "\n")))
1160 {
1161 m_size++;
1162 pos++;
1163 }
1164 /* For each key = value pair we need to add 4 characters (2
1165 spaces and 1 equal-to character and 1 new line) */
1166 m_size += strlen (ent->key) + strlen (ent->val) + 4;
1167 }
1168 }
1169 /* A new line after section end */
1170 m_size++;
1171 }
1172
1173 /* Pass2: Allocate memory and write the configuration to it */
1174 mem = GNUNET_malloc (m_size);
1175 c_size = 0;
1176 *size = c_size;
1177 for (struct ConfigSection *sec = cfg->sections;
1178 NULL != sec;
1179 sec = sec->next)
1180 {
1181 int len;
1182
1183 len = GNUNET_asprintf (&cbuf,
1184 "[%s]\n",
1185 sec->name);
1186 GNUNET_assert (0 < len);
1187 GNUNET_memcpy (mem + c_size,
1188 cbuf,
1189 len);
1190 c_size += len;
1191 GNUNET_free (cbuf);
1192 for (struct ConfigEntry *ent = sec->entries;
1193 NULL != ent;
1194 ent = ent->next)
1195 {
1196 if (do_skip (sec->name,
1197 ent->key))
1198 continue;
1199 if (NULL != ent->val)
1200 {
1201 val = GNUNET_malloc (strlen (ent->val) * 2 + 1);
1202 strcpy (val, ent->val);
1203 while (NULL != (pos = strstr (val, "\n")))
1204 {
1205 memmove (&pos[2], &pos[1], strlen (&pos[1]));
1206 pos[0] = '\\';
1207 pos[1] = 'n';
1208 }
1209 len = GNUNET_asprintf (&cbuf, "%s = %s\n", ent->key, val);
1210 GNUNET_free (val);
1211 GNUNET_memcpy (mem + c_size, cbuf, len);
1212 c_size += len;
1213 GNUNET_free (cbuf);
1214 }
1215 }
1216 GNUNET_memcpy (mem + c_size, "\n", 1);
1217 c_size++;
1218 }
1219 GNUNET_assert (c_size == m_size);
1220 *size = c_size;
1221 return mem;
1222}
1223
1224
1225char *
1226GNUNET_CONFIGURATION_serialize_diagnostics (const struct
1227 GNUNET_CONFIGURATION_Handle *cfg)
1228{
1229 struct GNUNET_Buffer buf = { 0 };
1230
1231 GNUNET_buffer_write_fstr (&buf,
1232 "#\n# Configuration file diagnostics\n#\n");
1233 GNUNET_buffer_write_fstr (&buf,
1234 "# Entry point: %s\n",
1235 cfg->main_filename ? cfg->main_filename :
1236 "<none>");
1237 GNUNET_buffer_write_fstr (&buf,
1238 "#\n# Files Loaded:\n");
1239
1240 for (struct ConfigFile *cfil = cfg->loaded_files_head;
1241 NULL != cfil;
1242 cfil = cfil->next)
1243 {
1244 GNUNET_buffer_write_fstr (&buf,
1245 "# ");
1246 for (unsigned int i = 0; i < cfil->level; i++)
1247 GNUNET_buffer_write_fstr (&buf,
1248 "+");
1249 if (0 != cfil->level)
1250 GNUNET_buffer_write_fstr (&buf,
1251 " ");
1252
1253 GNUNET_buffer_write_fstr (&buf,
1254 "%s",
1255 cfil->source_filename);
1256
1257 if (NULL != cfil->hint_restrict_section)
1258 GNUNET_buffer_write_fstr (&buf,
1259 " (%s secret section %s)",
1260 cfil->hint_inaccessible
1261 ? "inaccessible"
1262 : "loaded",
1263 cfil->hint_restrict_section);
1264
1265 GNUNET_buffer_write_str (&buf,
1266 "\n");
1267 }
1268
1269 GNUNET_buffer_write_fstr (&buf,
1270 "#\n\n");
1271
1272 for (struct ConfigSection *sec = cfg->sections;
1273 NULL != sec;
1274 sec = sec->next)
1275 {
1276 if (sec->hint_secret_filename)
1277 GNUNET_buffer_write_fstr (&buf,
1278 "# secret section from %s\n# secret file stat %s\n",
1279 sec->hint_secret_filename,
1280 sec->hint_secret_stat);
1281 if (sec->hint_inlined_from_filename)
1282 {
1283 GNUNET_buffer_write_fstr (&buf,
1284 "# inlined from %s:%u\n",
1285 sec->hint_inlined_from_filename,
1286 sec->hint_inlined_from_line);
1287 }
1288 GNUNET_buffer_write_fstr (&buf,
1289 "[%s]\n\n",
1290 sec->name);
1291 if (sec->inaccessible)
1292 {
1293 GNUNET_buffer_write_fstr (&buf,
1294 "# <section contents inaccessible>\n\n\n");
1295 continue;
1296 }
1297 for (struct ConfigEntry *ent = sec->entries;
1298 NULL != ent;
1299 ent = ent->next)
1300 {
1301 if (do_skip (sec->name,
1302 ent->key))
1303 continue;
1304 if (NULL != ent->val)
1305 {
1306 char *pos;
1307 char *val = GNUNET_malloc (strlen (ent->val) * 2 + 1);
1308 strcpy (val, ent->val);
1309 while (NULL != (pos = strstr (val, "\n")))
1310 {
1311 memmove (&pos[2], &pos[1], strlen (&pos[1]));
1312 pos[0] = '\\';
1313 pos[1] = 'n';
1314 }
1315 if (NULL != ent->hint_filename)
1316 {
1317 GNUNET_buffer_write_fstr (&buf,
1318 "# %s:%u\n",
1319 ent->hint_filename,
1320 ent->hint_lineno);
1321 }
1322 GNUNET_buffer_write_fstr (&buf,
1323 "%s = %s\n",
1324 ent->key,
1325 val);
1326 GNUNET_free (val);
1327 }
1328 GNUNET_buffer_write_str (&buf, "\n");
1329 }
1330 GNUNET_buffer_write_str (&buf, "\n");
1331 }
1332 return GNUNET_buffer_reap_str (&buf);
1333}
1334
1335
1336enum GNUNET_GenericReturnValue
1337GNUNET_CONFIGURATION_write (struct GNUNET_CONFIGURATION_Handle *cfg,
1338 const char *filename)
1339{
1340 char *fn;
1341 char *cfg_buf;
1342 size_t size;
1343
1344 fn = GNUNET_STRINGS_filename_expand (filename);
1345 if (fn == NULL)
1346 return GNUNET_SYSERR;
1347 if (GNUNET_OK != GNUNET_DISK_directory_create_for_file (fn))
1348 {
1349 GNUNET_free (fn);
1350 return GNUNET_SYSERR;
1351 }
1352 cfg_buf = GNUNET_CONFIGURATION_serialize (cfg,
1353 &size);
1354 {
1355 struct GNUNET_DISK_FileHandle *h;
1356
1357 h = GNUNET_DISK_file_open (fn,
1358 GNUNET_DISK_OPEN_WRITE
1359 | GNUNET_DISK_OPEN_TRUNCATE
1360 | GNUNET_DISK_OPEN_CREATE,
1361 GNUNET_DISK_PERM_USER_READ
1362 | GNUNET_DISK_PERM_USER_WRITE
1363 | GNUNET_DISK_PERM_GROUP_READ
1364 | GNUNET_DISK_PERM_GROUP_WRITE);
1365 if (NULL == h)
1366 {
1367 GNUNET_free (fn);
1368 GNUNET_free (cfg_buf);
1369 return GNUNET_SYSERR;
1370 }
1371 if (((ssize_t) size) !=
1372 GNUNET_DISK_file_write (h,
1373 cfg_buf,
1374 size))
1375 {
1376 GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING,
1377 "write",
1378 fn);
1379 GNUNET_DISK_file_close (h);
1380 (void) GNUNET_DISK_directory_remove (fn);
1381 GNUNET_free (fn);
1382 GNUNET_free (cfg_buf);
1383 cfg->dirty = GNUNET_SYSERR; /* last write failed */
1384 return GNUNET_SYSERR;
1385 }
1386 GNUNET_assert (GNUNET_OK ==
1387 GNUNET_DISK_file_close (h));
1388 }
1389 GNUNET_free (fn);
1390 GNUNET_free (cfg_buf);
1391 cfg->dirty = GNUNET_NO; /* last write succeeded */
1392 return GNUNET_OK;
1393}
1394
1395
1396void
1397GNUNET_CONFIGURATION_iterate (const struct GNUNET_CONFIGURATION_Handle *cfg,
1398 GNUNET_CONFIGURATION_Iterator iter,
1399 void *iter_cls)
1400{
1401 for (struct ConfigSection *spos = cfg->sections;
1402 NULL != spos;
1403 spos = spos->next)
1404 for (struct ConfigEntry *epos = spos->entries;
1405 NULL != epos;
1406 epos = epos->next)
1407 if (NULL != epos->val)
1408 iter (iter_cls,
1409 spos->name,
1410 epos->key,
1411 epos->val);
1412}
1413
1414
1415void
1416GNUNET_CONFIGURATION_iterate_section_values (
1417 const struct GNUNET_CONFIGURATION_Handle *cfg,
1418 const char *section,
1419 GNUNET_CONFIGURATION_Iterator iter,
1420 void *iter_cls)
1421{
1422 struct ConfigSection *spos;
1423 struct ConfigEntry *epos;
1424
1425 spos = cfg->sections;
1426 while ((spos != NULL) && (0 != strcasecmp (spos->name, section)))
1427 spos = spos->next;
1428 if (NULL == spos)
1429 return;
1430 if (spos->inaccessible)
1431 {
1432 LOG (GNUNET_ERROR_TYPE_WARNING,
1433 "Section '%s' is marked as inaccessible, because the configuration "
1434 " file that contains the section can't be read.\n",
1435 section);
1436 return;
1437 }
1438 for (epos = spos->entries; NULL != epos; epos = epos->next)
1439 if (NULL != epos->val)
1440 iter (iter_cls, spos->name, epos->key, epos->val);
1441}
1442
1443
1444void
1445GNUNET_CONFIGURATION_iterate_sections (
1446 const struct GNUNET_CONFIGURATION_Handle *cfg,
1447 GNUNET_CONFIGURATION_SectionIterator iter,
1448 void *iter_cls)
1449{
1450 struct ConfigSection *spos;
1451 struct ConfigSection *next;
1452
1453 next = cfg->sections;
1454 while (next != NULL)
1455 {
1456 spos = next;
1457 next = spos->next;
1458 iter (iter_cls, spos->name);
1459 }
1460}
1461
1462
1463void
1464GNUNET_CONFIGURATION_remove_section (struct GNUNET_CONFIGURATION_Handle *cfg,
1465 const char *section)
1466{
1467 struct ConfigSection *spos;
1468 struct ConfigSection *prev;
1469 struct ConfigEntry *ent;
1470
1471 prev = NULL;
1472 spos = cfg->sections;
1473 while (NULL != spos)
1474 {
1475 if (0 == strcasecmp (section, spos->name))
1476 {
1477 if (NULL == prev)
1478 cfg->sections = spos->next;
1479 else
1480 prev->next = spos->next;
1481 while (NULL != (ent = spos->entries))
1482 {
1483 spos->entries = ent->next;
1484 GNUNET_free (ent->key);
1485 GNUNET_free (ent->val);
1486 GNUNET_free (ent->hint_filename);
1487 GNUNET_free (ent);
1488 cfg->dirty = GNUNET_YES;
1489 }
1490 GNUNET_free (spos->name);
1491 GNUNET_free (spos->hint_secret_filename);
1492 GNUNET_free (spos->hint_secret_stat);
1493 GNUNET_free (spos->hint_inlined_from_filename);
1494 GNUNET_free (spos);
1495 return;
1496 }
1497 prev = spos;
1498 spos = spos->next;
1499 }
1500}
1501
1502
1503/**
1504 * Copy a configuration value to the given target configuration.
1505 * Overwrites existing entries.
1506 *
1507 * @param cls the destination configuration (`struct GNUNET_CONFIGURATION_Handle *`)
1508 * @param section section for the value
1509 * @param option option name of the value
1510 * @param value value to copy
1511 */
1512static void
1513copy_entry (void *cls,
1514 const char *section,
1515 const char *option,
1516 const char *value)
1517{
1518 struct GNUNET_CONFIGURATION_Handle *dst = cls;
1519
1520 GNUNET_CONFIGURATION_set_value_string (dst,
1521 section,
1522 option,
1523 value);
1524}
1525
1526
1527struct GNUNET_CONFIGURATION_Handle *
1528GNUNET_CONFIGURATION_dup (const struct GNUNET_CONFIGURATION_Handle *cfg)
1529{
1530 struct GNUNET_CONFIGURATION_Handle *ret;
1531
1532 ret = GNUNET_CONFIGURATION_create ();
1533 GNUNET_CONFIGURATION_iterate (cfg, &copy_entry, ret);
1534 return ret;
1535}
1536
1537
1538/**
1539 * A callback function, compares entries from two configurations
1540 * (default against a new configuration) and write the diffs in a
1541 * diff-configuration object (the callback object).
1542 *
1543 * @param cls the diff configuration (`struct DiffHandle *`)
1544 * @param section section for the value (of the default conf.)
1545 * @param option option name of the value (of the default conf.)
1546 * @param value value to copy (of the default conf.)
1547 */
1548static void
1549compare_entries (void *cls,
1550 const char *section,
1551 const char *option,
1552 const char *value)
1553{
1554 struct DiffHandle *dh = cls;
1555 struct ConfigEntry *entNew;
1556
1557 entNew = find_entry (dh->cfg_default, section, option);
1558 if ((NULL != entNew) && (NULL != entNew->val) &&
1559 (0 == strcmp (entNew->val, value)))
1560 return;
1561 GNUNET_CONFIGURATION_set_value_string (dh->cfgDiff,
1562 section,
1563 option,
1564 value);
1565}
1566
1567
1568struct GNUNET_CONFIGURATION_Handle *
1569GNUNET_CONFIGURATION_get_diff (
1570 const struct GNUNET_CONFIGURATION_Handle *cfg_default,
1571 const struct GNUNET_CONFIGURATION_Handle *cfg_new)
1572{
1573 struct DiffHandle diffHandle;
1574
1575 diffHandle.cfgDiff = GNUNET_CONFIGURATION_create ();
1576 diffHandle.cfg_default = cfg_default;
1577 GNUNET_CONFIGURATION_iterate (cfg_new, &compare_entries, &diffHandle);
1578 return diffHandle.cfgDiff;
1579}
1580
1581
1582enum GNUNET_GenericReturnValue
1583GNUNET_CONFIGURATION_write_diffs (
1584 const struct GNUNET_CONFIGURATION_Handle *cfg_default,
1585 const struct GNUNET_CONFIGURATION_Handle *cfg_new,
1586 const char *filename)
1587{
1588 int ret;
1589 struct GNUNET_CONFIGURATION_Handle *diff;
1590
1591 diff = GNUNET_CONFIGURATION_get_diff (cfg_default, cfg_new);
1592 ret = GNUNET_CONFIGURATION_write (diff, filename);
1593 GNUNET_CONFIGURATION_destroy (diff);
1594 return ret;
1595}
1596
1597
1598void
1599GNUNET_CONFIGURATION_set_value_string (struct GNUNET_CONFIGURATION_Handle *cfg,
1600 const char *section,
1601 const char *option,
1602 const char *value)
1603{
1604 struct ConfigSection *sec;
1605 struct ConfigEntry *e;
1606 char *nv;
1607
1608 e = find_entry (cfg, section, option);
1609 if (NULL != e)
1610 {
1611 if (NULL == value)
1612 {
1613 GNUNET_free (e->val);
1614 e->val = NULL;
1615 }
1616 else
1617 {
1618 nv = GNUNET_strdup (value);
1619 GNUNET_free (e->val);
1620 e->val = nv;
1621 }
1622 return;
1623 }
1624 sec = find_section (cfg, section);
1625 if (sec == NULL)
1626 {
1627 sec = GNUNET_new (struct ConfigSection);
1628 sec->name = GNUNET_strdup (section);
1629 sec->next = cfg->sections;
1630 cfg->sections = sec;
1631 }
1632 e = GNUNET_new (struct ConfigEntry);
1633 e->key = GNUNET_strdup (option);
1634 e->val = GNUNET_strdup (value);
1635 e->next = sec->entries;
1636 sec->entries = e;
1637}
1638
1639
1640void
1641GNUNET_CONFIGURATION_set_value_number (struct GNUNET_CONFIGURATION_Handle *cfg,
1642 const char *section,
1643 const char *option,
1644 unsigned long long number)
1645{
1646 char s[64];
1647
1648 GNUNET_snprintf (s,
1649 64,
1650 "%llu",
1651 number);
1652 GNUNET_CONFIGURATION_set_value_string (cfg,
1653 section,
1654 option,
1655 s);
1656}
1657
1658
1659enum GNUNET_GenericReturnValue
1660GNUNET_CONFIGURATION_get_value_number (
1661 const struct GNUNET_CONFIGURATION_Handle *cfg,
1662 const char *section,
1663 const char *option,
1664 unsigned long long *number)
1665{
1666 struct ConfigEntry *e;
1667 char dummy[2];
1668
1669 if (NULL == (e = find_entry (cfg, section, option)))
1670 return GNUNET_SYSERR;
1671 if (NULL == e->val)
1672 return GNUNET_SYSERR;
1673 if (1 != sscanf (e->val, "%llu%1s", number, dummy))
1674 return GNUNET_SYSERR;
1675 return GNUNET_OK;
1676}
1677
1678
1679enum GNUNET_GenericReturnValue
1680GNUNET_CONFIGURATION_get_value_float (
1681 const struct GNUNET_CONFIGURATION_Handle *cfg,
1682 const char *section,
1683 const char *option,
1684 float *number)
1685{
1686 struct ConfigEntry *e;
1687 char dummy[2];
1688
1689 if (NULL == (e = find_entry (cfg, section, option)))
1690 return GNUNET_SYSERR;
1691 if (NULL == e->val)
1692 return GNUNET_SYSERR;
1693 if (1 != sscanf (e->val, "%f%1s", number, dummy))
1694 return GNUNET_SYSERR;
1695 return GNUNET_OK;
1696}
1697
1698
1699enum GNUNET_GenericReturnValue
1700GNUNET_CONFIGURATION_get_value_time (
1701 const struct GNUNET_CONFIGURATION_Handle *cfg,
1702 const char *section,
1703 const char *option,
1704 struct GNUNET_TIME_Relative *time)
1705{
1706 struct ConfigEntry *e;
1707 int ret;
1708
1709 if (NULL == (e = find_entry (cfg, section, option)))
1710 return GNUNET_SYSERR;
1711 if (NULL == e->val)
1712 return GNUNET_SYSERR;
1713 ret = GNUNET_STRINGS_fancy_time_to_relative (e->val, time);
1714 if (GNUNET_OK != ret)
1715 GNUNET_log_config_invalid (GNUNET_ERROR_TYPE_ERROR,
1716 section,
1717 option,
1718 _ ("Not a valid relative time specification"));
1719 return ret;
1720}
1721
1722
1723enum GNUNET_GenericReturnValue
1724GNUNET_CONFIGURATION_get_value_size (
1725 const struct GNUNET_CONFIGURATION_Handle *cfg,
1726 const char *section,
1727 const char *option,
1728 unsigned long long *size)
1729{
1730 struct ConfigEntry *e;
1731
1732 if (NULL == (e = find_entry (cfg, section, option)))
1733 return GNUNET_SYSERR;
1734 if (NULL == e->val)
1735 return GNUNET_SYSERR;
1736 return GNUNET_STRINGS_fancy_size_to_bytes (e->val, size);
1737}
1738
1739
1740/**
1741 * Get a configuration value that should be a string.
1742 *
1743 * @param cfg configuration to inspect
1744 * @param section section of interest
1745 * @param option option of interest
1746 * @param value will be set to a freshly allocated configuration
1747 * value, or NULL if option is not specified
1748 * @return #GNUNET_OK on success, #GNUNET_SYSERR on error
1749 */
1750enum GNUNET_GenericReturnValue
1751GNUNET_CONFIGURATION_get_value_string (
1752 const struct GNUNET_CONFIGURATION_Handle *cfg,
1753 const char *section,
1754 const char *option,
1755 char **value)
1756{
1757 struct ConfigEntry *e;
1758
1759 if ((NULL == (e = find_entry (cfg, section, option))) || (NULL == e->val))
1760 {
1761 *value = NULL;
1762 return GNUNET_SYSERR;
1763 }
1764 *value = GNUNET_strdup (e->val);
1765 return GNUNET_OK;
1766}
1767
1768
1769enum GNUNET_GenericReturnValue
1770GNUNET_CONFIGURATION_get_value_choice (
1771 const struct GNUNET_CONFIGURATION_Handle *cfg,
1772 const char *section,
1773 const char *option,
1774 const char *const *choices,
1775 const char **value)
1776{
1777 struct ConfigEntry *e;
1778 unsigned int i;
1779
1780 if (NULL == (e = find_entry (cfg, section, option)))
1781 return GNUNET_SYSERR;
1782 for (i = 0; NULL != choices[i]; i++)
1783 if (0 == strcasecmp (choices[i], e->val))
1784 break;
1785 if (NULL == choices[i])
1786 {
1787 LOG (GNUNET_ERROR_TYPE_ERROR,
1788 _ ("Configuration value '%s' for '%s'"
1789 " in section '%s' is not in set of legal choices\n"),
1790 e->val,
1791 option,
1792 section);
1793 return GNUNET_SYSERR;
1794 }
1795 *value = choices[i];
1796 return GNUNET_OK;
1797}
1798
1799
1800enum GNUNET_GenericReturnValue
1801GNUNET_CONFIGURATION_get_data (const struct GNUNET_CONFIGURATION_Handle *cfg,
1802 const char *section,
1803 const char *option,
1804 void *buf,
1805 size_t buf_size)
1806{
1807 char *enc;
1808 int res;
1809 size_t data_size;
1810
1811 if (GNUNET_OK !=
1812 (res =
1813 GNUNET_CONFIGURATION_get_value_string (cfg, section, option, &enc)))
1814 return res;
1815 data_size = (strlen (enc) * 5) / 8;
1816 if (data_size != buf_size)
1817 {
1818 GNUNET_free (enc);
1819 return GNUNET_SYSERR;
1820 }
1821 if (GNUNET_OK !=
1822 GNUNET_STRINGS_string_to_data (enc, strlen (enc), buf, buf_size))
1823 {
1824 GNUNET_free (enc);
1825 return GNUNET_SYSERR;
1826 }
1827 GNUNET_free (enc);
1828 return GNUNET_OK;
1829}
1830
1831
1832enum GNUNET_GenericReturnValue
1833GNUNET_CONFIGURATION_have_value (const struct GNUNET_CONFIGURATION_Handle *cfg,
1834 const char *section,
1835 const char *option)
1836{
1837 struct ConfigEntry *e;
1838
1839 if ((NULL == (e = find_entry (cfg, section, option))) || (NULL == e->val))
1840 return GNUNET_NO;
1841 return GNUNET_YES;
1842}
1843
1844
1845/**
1846 * Expand an expression of the form "$FOO/BAR" to "DIRECTORY/BAR"
1847 * where either in the "PATHS" section or the environment "FOO" is
1848 * set to "DIRECTORY". We also support default expansion,
1849 * i.e. ${VARIABLE:-default} will expand to $VARIABLE if VARIABLE is
1850 * set in PATHS or the environment, and otherwise to "default". Note
1851 * that "default" itself can also be a $-expression, thus
1852 * "${VAR1:-{$VAR2}}" will expand to VAR1 and if that is not defined
1853 * to VAR2.
1854 *
1855 * @param cfg configuration to use for path expansion
1856 * @param orig string to $-expand (will be freed!)
1857 * @param depth recursion depth, used to detect recursive expansions
1858 * @return $-expanded string, never NULL unless @a orig was NULL
1859 */
1860static char *
1861expand_dollar (const struct GNUNET_CONFIGURATION_Handle *cfg,
1862 char *orig,
1863 unsigned int depth)
1864{
1865 char *prefix;
1866 char *result;
1867 char *start;
1868 const char *post;
1869 const char *env;
1870 char *def;
1871 char *end;
1872 unsigned int lopen;
1873 char erased_char;
1874 char *erased_pos;
1875 size_t len;
1876
1877 if (NULL == orig)
1878 return NULL;
1879 if (depth > 128)
1880 {
1881 LOG (GNUNET_ERROR_TYPE_WARNING,
1882 "Recursive expansion suspected, aborting $-expansion for term `%s'\n",
1883 orig);
1884 return orig;
1885 }
1886 LOG (GNUNET_ERROR_TYPE_DEBUG,
1887 "Asked to $-expand %s\n",
1888 orig);
1889 if ('$' != orig[0])
1890 {
1891 LOG (GNUNET_ERROR_TYPE_DEBUG,
1892 "Doesn't start with $ - not expanding\n");
1893 return orig;
1894 }
1895 erased_char = 0;
1896 erased_pos = NULL;
1897 if ('{' == orig[1])
1898 {
1899 start = &orig[2];
1900 lopen = 1;
1901 end = &orig[1];
1902 while (lopen > 0)
1903 {
1904 end++;
1905 switch (*end)
1906 {
1907 case '}':
1908 lopen--;
1909 break;
1910
1911 case '{':
1912 lopen++;
1913 break;
1914
1915 case '\0':
1916 LOG (GNUNET_ERROR_TYPE_WARNING,
1917 "Missing closing `}' in option `%s'\n",
1918 orig);
1919 return orig;
1920
1921 default:
1922 break;
1923 }
1924 }
1925 erased_char = *end;
1926 erased_pos = end;
1927 *end = '\0';
1928 post = end + 1;
1929 def = strchr (orig, ':');
1930 if (NULL != def)
1931 {
1932 *def = '\0';
1933 def++;
1934 if (('-' == *def) || ('=' == *def))
1935 def++;
1936 def = GNUNET_strdup (def);
1937 }
1938 }
1939 else
1940 {
1941 int i;
1942
1943 start = &orig[1];
1944 def = NULL;
1945 i = 0;
1946 while ( (orig[i] != '/') &&
1947 (orig[i] != '\\') &&
1948 (orig[i] != '\0') &&
1949 (orig[i] != ' ') )
1950 i++;
1951 if (orig[i] == '\0')
1952 {
1953 post = "";
1954 }
1955 else
1956 {
1957 erased_char = orig[i];
1958 erased_pos = &orig[i];
1959 orig[i] = '\0';
1960 post = &orig[i + 1];
1961 }
1962 }
1963 LOG (GNUNET_ERROR_TYPE_DEBUG,
1964 "Split into `%s' and `%s' with default %s\n",
1965 start,
1966 post,
1967 def);
1968 if (GNUNET_OK !=
1969 GNUNET_CONFIGURATION_get_value_string (cfg,
1970 "PATHS",
1971 start,
1972 &prefix))
1973 {
1974 if (NULL == (env = getenv (start)))
1975 {
1976 /* try default */
1977 def = expand_dollar (cfg,
1978 def,
1979 depth + 1);
1980 env = def;
1981 }
1982 if (NULL == env)
1983 {
1984 start = GNUNET_strdup (start);
1985 if (erased_pos)
1986 *erased_pos = erased_char;
1987 LOG (GNUNET_ERROR_TYPE_WARNING,
1988 "Failed to expand `%s' in `%s' as it is neither found in [PATHS] nor defined as an environmental variable\n",
1989 start,
1990 orig);
1991 GNUNET_free (start);
1992 return orig;
1993 }
1994 prefix = GNUNET_strdup (env);
1995 }
1996 prefix = GNUNET_CONFIGURATION_expand_dollar (cfg,
1997 prefix);
1998 if ((erased_pos) && ('}' != erased_char))
1999 {
2000 len = strlen (prefix) + 1;
2001 prefix = GNUNET_realloc (prefix, len + 1);
2002 prefix[len - 1] = erased_char;
2003 prefix[len] = '\0';
2004 }
2005 result = GNUNET_malloc (strlen (prefix) + strlen (post) + 1);
2006 strcpy (result, prefix);
2007 strcat (result, post);
2008 GNUNET_free (def);
2009 GNUNET_free (prefix);
2010 GNUNET_free (orig);
2011 return result;
2012}
2013
2014
2015char *
2016GNUNET_CONFIGURATION_expand_dollar (
2017 const struct GNUNET_CONFIGURATION_Handle *cfg,
2018 char *orig)
2019{
2020 char *dup;
2021 size_t i;
2022 size_t len;
2023
2024 for (i = 0; '\0' != orig[i]; i++)
2025 {
2026 if ('$' != orig[i])
2027 continue;
2028 dup = GNUNET_strdup (orig + i);
2029 dup = expand_dollar (cfg, dup, 0);
2030 GNUNET_assert (NULL != dup); /* make compiler happy */
2031 len = strlen (dup) + 1;
2032 orig = GNUNET_realloc (orig, i + len);
2033 GNUNET_memcpy (orig + i, dup, len);
2034 GNUNET_free (dup);
2035 }
2036 return orig;
2037}
2038
2039
2040enum GNUNET_GenericReturnValue
2041GNUNET_CONFIGURATION_get_value_filename (
2042 const struct GNUNET_CONFIGURATION_Handle *cfg,
2043 const char *section,
2044 const char *option,
2045 char **value)
2046{
2047 char *tmp;
2048
2049 if (GNUNET_OK !=
2050 GNUNET_CONFIGURATION_get_value_string (cfg, section, option, &tmp))
2051 {
2052 LOG (GNUNET_ERROR_TYPE_DEBUG, "Failed to retrieve filename\n");
2053 *value = NULL;
2054 return GNUNET_SYSERR;
2055 }
2056 tmp = GNUNET_CONFIGURATION_expand_dollar (cfg, tmp);
2057 *value = GNUNET_STRINGS_filename_expand (tmp);
2058 GNUNET_free (tmp);
2059 if (*value == NULL)
2060 return GNUNET_SYSERR;
2061 return GNUNET_OK;
2062}
2063
2064
2065enum GNUNET_GenericReturnValue
2066GNUNET_CONFIGURATION_get_value_yesno (
2067 const struct GNUNET_CONFIGURATION_Handle *cfg,
2068 const char *section,
2069 const char *option)
2070{
2071 static const char *yesno[] = { "YES", "NO", NULL };
2072 const char *val;
2073 int ret;
2074
2075 ret =
2076 GNUNET_CONFIGURATION_get_value_choice (cfg, section, option, yesno, &val);
2077 if (ret == GNUNET_SYSERR)
2078 return ret;
2079 if (val == yesno[0])
2080 return GNUNET_YES;
2081 return GNUNET_NO;
2082}
2083
2084
2085int
2086GNUNET_CONFIGURATION_iterate_value_filenames (
2087 const struct GNUNET_CONFIGURATION_Handle *cfg,
2088 const char *section,
2089 const char *option,
2090 GNUNET_FileNameCallback cb,
2091 void *cb_cls)
2092{
2093 char *list;
2094 char *pos;
2095 char *end;
2096 char old;
2097 int ret;
2098
2099 if (GNUNET_OK !=
2100 GNUNET_CONFIGURATION_get_value_string (cfg, section, option, &list))
2101 return 0;
2102 GNUNET_assert (list != NULL);
2103 ret = 0;
2104 pos = list;
2105 while (1)
2106 {
2107 while (pos[0] == ' ')
2108 pos++;
2109 if (strlen (pos) == 0)
2110 break;
2111 end = pos + 1;
2112 while ((end[0] != ' ') && (end[0] != '\0'))
2113 {
2114 if (end[0] == '\\')
2115 {
2116 switch (end[1])
2117 {
2118 case '\\':
2119 case ' ':
2120 memmove (end, &end[1], strlen (&end[1]) + 1);
2121
2122 case '\0':
2123 /* illegal, but just keep it */
2124 break;
2125
2126 default:
2127 /* illegal, but just ignore that there was a '/' */
2128 break;
2129 }
2130 }
2131 end++;
2132 }
2133 old = end[0];
2134 end[0] = '\0';
2135 if (strlen (pos) > 0)
2136 {
2137 ret++;
2138 if ((cb != NULL) && (GNUNET_OK != cb (cb_cls, pos)))
2139 {
2140 ret = GNUNET_SYSERR;
2141 break;
2142 }
2143 }
2144 if (old == '\0')
2145 break;
2146 pos = end + 1;
2147 }
2148 GNUNET_free (list);
2149 return ret;
2150}
2151
2152
2153/**
2154 * FIXME.
2155 *
2156 * @param value FIXME
2157 * @return FIXME
2158 */
2159static char *
2160escape_name (const char *value)
2161{
2162 char *escaped;
2163 const char *rpos;
2164 char *wpos;
2165
2166 escaped = GNUNET_malloc (strlen (value) * 2 + 1);
2167 memset (escaped, 0, strlen (value) * 2 + 1);
2168 rpos = value;
2169 wpos = escaped;
2170 while (rpos[0] != '\0')
2171 {
2172 switch (rpos[0])
2173 {
2174 case '\\':
2175 case ' ':
2176 wpos[0] = '\\';
2177 wpos[1] = rpos[0];
2178 wpos += 2;
2179 break;
2180
2181 default:
2182 wpos[0] = rpos[0];
2183 wpos++;
2184 }
2185 rpos++;
2186 }
2187 return escaped;
2188}
2189
2190
2191/**
2192 * FIXME.
2193 *
2194 * @param cls string we compare with (const char*)
2195 * @param fn filename we are currently looking at
2196 * @return #GNUNET_OK if the names do not match, #GNUNET_SYSERR if they do
2197 */
2198static enum GNUNET_GenericReturnValue
2199test_match (void *cls, const char *fn)
2200{
2201 const char *of = cls;
2202
2203 return (0 == strcmp (of, fn)) ? GNUNET_SYSERR : GNUNET_OK;
2204}
2205
2206
2207enum GNUNET_GenericReturnValue
2208GNUNET_CONFIGURATION_append_value_filename (
2209 struct GNUNET_CONFIGURATION_Handle *cfg,
2210 const char *section,
2211 const char *option,
2212 const char *value)
2213{
2214 char *escaped;
2215 char *old;
2216 char *nw;
2217
2218 if (GNUNET_SYSERR ==
2219 GNUNET_CONFIGURATION_iterate_value_filenames (cfg,
2220 section,
2221 option,
2222 &test_match,
2223 (void *) value))
2224 return GNUNET_NO; /* already exists */
2225 if (GNUNET_OK !=
2226 GNUNET_CONFIGURATION_get_value_string (cfg, section, option, &old))
2227 old = GNUNET_strdup ("");
2228 escaped = escape_name (value);
2229 nw = GNUNET_malloc (strlen (old) + strlen (escaped) + 2);
2230 strcpy (nw, old);
2231 if (strlen (old) > 0)
2232 strcat (nw, " ");
2233 strcat (nw, escaped);
2234 GNUNET_CONFIGURATION_set_value_string (cfg,
2235 section,
2236 option,
2237 nw);
2238 GNUNET_free (old);
2239 GNUNET_free (nw);
2240 GNUNET_free (escaped);
2241 return GNUNET_OK;
2242}
2243
2244
2245enum GNUNET_GenericReturnValue
2246GNUNET_CONFIGURATION_remove_value_filename (
2247 struct GNUNET_CONFIGURATION_Handle *cfg,
2248 const char *section,
2249 const char *option,
2250 const char *value)
2251{
2252 char *list;
2253 char *pos;
2254 char *end;
2255 char *match;
2256 char old;
2257
2258 if (GNUNET_OK !=
2259 GNUNET_CONFIGURATION_get_value_string (cfg,
2260 section,
2261 option,
2262 &list))
2263 return GNUNET_NO;
2264 match = escape_name (value);
2265 pos = list;
2266 while (1)
2267 {
2268 while (pos[0] == ' ')
2269 pos++;
2270 if (strlen (pos) == 0)
2271 break;
2272 end = pos + 1;
2273 while ((end[0] != ' ') && (end[0] != '\0'))
2274 {
2275 if (end[0] == '\\')
2276 {
2277 switch (end[1])
2278 {
2279 case '\\':
2280 case ' ':
2281 end++;
2282 break;
2283
2284 case '\0':
2285 /* illegal, but just keep it */
2286 break;
2287
2288 default:
2289 /* illegal, but just ignore that there was a '/' */
2290 break;
2291 }
2292 }
2293 end++;
2294 }
2295 old = end[0];
2296 end[0] = '\0';
2297 if (0 == strcmp (pos, match))
2298 {
2299 if (old != '\0')
2300 memmove (pos,
2301 &end[1],
2302 strlen (&end[1]) + 1);
2303 else
2304 {
2305 if (pos != list)
2306 pos[-1] = '\0';
2307 else
2308 pos[0] = '\0';
2309 }
2310 GNUNET_CONFIGURATION_set_value_string (cfg,
2311 section,
2312 option,
2313 list);
2314 GNUNET_free (list);
2315 GNUNET_free (match);
2316 return GNUNET_OK;
2317 }
2318 if (old == '\0')
2319 break;
2320 end[0] = old;
2321 pos = end + 1;
2322 }
2323 GNUNET_free (list);
2324 GNUNET_free (match);
2325 return GNUNET_NO;
2326}
2327
2328
2329enum GNUNET_GenericReturnValue
2330GNUNET_CONFIGURATION_load_from (struct GNUNET_CONFIGURATION_Handle *cfg,
2331 const char *defaults_d)
2332{
2333 struct CollectFilesContext files_context = {
2334 .files = NULL,
2335 .files_length = 0,
2336 };
2337 enum GNUNET_GenericReturnValue fun_ret;
2338
2339 if (GNUNET_SYSERR ==
2340 GNUNET_DISK_directory_scan (defaults_d, &collect_files_cb,
2341 &files_context))
2342 return GNUNET_SYSERR; /* no configuration at all found */
2343 qsort (files_context.files,
2344 files_context.files_length,
2345 sizeof (char *),
2346 pstrcmp);
2347 for (unsigned int i = 0; i < files_context.files_length; i++)
2348 {
2349 char *ext;
2350 const char *filename = files_context.files[i];
2351
2352 /* Examine file extension */
2353 ext = strrchr (filename, '.');
2354 if ((NULL == ext) || (0 != strcmp (ext, ".conf")))
2355 {
2356 GNUNET_log (GNUNET_ERROR_TYPE_WARNING, "Skipping file `%s'\n", filename);
2357 fun_ret = GNUNET_OK;
2358 goto cleanup;
2359 }
2360 fun_ret = GNUNET_CONFIGURATION_parse (cfg, filename);
2361 if (fun_ret != GNUNET_OK)
2362 break;
2363 }
2364cleanup:
2365 if (files_context.files_length > 0)
2366 {
2367 for (size_t i = 0; i < files_context.files_length; i++)
2368 GNUNET_free (files_context.files[i]);
2369 GNUNET_array_grow (files_context.files,
2370 files_context.files_length,
2371 0);
2372 }
2373 return fun_ret;
2374}
2375
2376
2377char *
2378GNUNET_CONFIGURATION_default_filename (void)
2379{
2380 char *cfg_fn;
2381 const struct GNUNET_OS_ProjectData *pd = GNUNET_OS_project_data_get ();
2382 const char *xdg = getenv ("XDG_CONFIG_HOME");
2383
2384 if (NULL != xdg)
2385 GNUNET_asprintf (&cfg_fn,
2386 "%s%s%s",
2387 xdg,
2388 DIR_SEPARATOR_STR,
2389 pd->config_file);
2390 else
2391 cfg_fn = GNUNET_strdup (pd->user_config_file);
2392
2393 if (GNUNET_OK == GNUNET_DISK_file_test_read (cfg_fn))
2394 return cfg_fn;
2395 GNUNET_free (cfg_fn);
2396
2397 /* Fall back to /etc/ for the default configuration.
2398 Should be okay to use forward slashes here. */
2399
2400 GNUNET_asprintf (&cfg_fn,
2401 "/etc/%s",
2402 pd->config_file);
2403 if (GNUNET_OK == GNUNET_DISK_file_test_read (cfg_fn))
2404 return cfg_fn;
2405 GNUNET_free (cfg_fn);
2406
2407 GNUNET_asprintf (&cfg_fn,
2408 "/etc/%s/%s",
2409 pd->project_dirname,
2410 pd->config_file);
2411 if (GNUNET_OK == GNUNET_DISK_file_test_read (cfg_fn))
2412 return cfg_fn;
2413
2414 GNUNET_free (cfg_fn);
2415 return NULL;
2416}
2417
2418
2419struct GNUNET_CONFIGURATION_Handle *
2420GNUNET_CONFIGURATION_default (void)
2421{
2422 const struct GNUNET_OS_ProjectData *pd = GNUNET_OS_project_data_get ();
2423 const struct GNUNET_OS_ProjectData *dpd = GNUNET_OS_project_data_default ();
2424 const char *xdg = getenv ("XDG_CONFIG_HOME");
2425 char *cfgname = NULL;
2426 struct GNUNET_CONFIGURATION_Handle *cfg;
2427
2428 /* Makes sure function implicitly looking at the installation directory (for
2429 example GNUNET_CONFIGURATION_load further down) use GNUnet's environment
2430 instead of the caller's. It's done at the start to make sure as many
2431 functions as possible are directed to the proper paths. */
2432 GNUNET_OS_init (dpd);
2433
2434 cfg = GNUNET_CONFIGURATION_create ();
2435
2436 /* First, try user configuration. */
2437 if (NULL != xdg)
2438 GNUNET_asprintf (&cfgname, "%s/%s", xdg, dpd->config_file);
2439 else
2440 cfgname = GNUNET_strdup (dpd->user_config_file);
2441
2442 /* If user config doesn't exist, try in
2443 /etc/<projdir>/<cfgfile> and /etc/<cfgfile> */
2444 if (GNUNET_OK != GNUNET_DISK_file_test (cfgname))
2445 {
2446 GNUNET_free (cfgname);
2447 GNUNET_asprintf (&cfgname, "/etc/%s", dpd->config_file);
2448 }
2449 if (GNUNET_OK != GNUNET_DISK_file_test (cfgname))
2450 {
2451 GNUNET_free (cfgname);
2452 GNUNET_asprintf (&cfgname,
2453 "/etc/%s/%s",
2454 dpd->project_dirname,
2455 dpd->config_file);
2456 }
2457 if (GNUNET_OK != GNUNET_DISK_file_test (cfgname))
2458 {
2459 LOG (GNUNET_ERROR_TYPE_ERROR,
2460 "Unable to top-level configuration file.\n");
2461 GNUNET_OS_init (pd);
2462 GNUNET_CONFIGURATION_destroy (cfg);
2463 GNUNET_free (cfgname);
2464 return NULL;
2465 }
2466
2467 /* We found a configuration file that looks good, try to load it. */
2468
2469 LOG (GNUNET_ERROR_TYPE_DEBUG,
2470 "Loading top-level configuration from '%s'\n",
2471 cfgname);
2472 if (GNUNET_OK !=
2473 GNUNET_CONFIGURATION_load (cfg, cfgname))
2474 {
2475 GNUNET_OS_init (pd);
2476 GNUNET_CONFIGURATION_destroy (cfg);
2477 GNUNET_free (cfgname);
2478 return NULL;
2479 }
2480 GNUNET_free (cfgname);
2481 GNUNET_OS_init (pd);
2482 return cfg;
2483}
2484
2485
2486/**
2487 * Load configuration (starts with defaults, then loads
2488 * system-specific configuration).
2489 *
2490 * @param cfg configuration to update
2491 * @param filename name of the configuration file, NULL to load defaults
2492 * @return #GNUNET_OK on success, #GNUNET_SYSERR on error
2493 */
2494enum GNUNET_GenericReturnValue
2495GNUNET_CONFIGURATION_load (struct GNUNET_CONFIGURATION_Handle *cfg,
2496 const char *filename)
2497{
2498 char *baseconfig;
2499 const char *base_config_varname;
2500
2501 if (cfg->load_called)
2502 {
2503 /* FIXME: Make this a GNUNET_assert later */
2504 GNUNET_break (0);
2505 GNUNET_free (cfg->main_filename);
2506 }
2507 cfg->load_called = true;
2508 if (NULL != filename)
2509 {
2510 GNUNET_free (cfg->main_filename);
2511 cfg->main_filename = GNUNET_strdup (filename);
2512 }
2513
2514 base_config_varname = GNUNET_OS_project_data_get ()->base_config_varname;
2515
2516 if ((NULL != base_config_varname)
2517 && (NULL != (baseconfig = getenv (base_config_varname))))
2518 {
2519 baseconfig = GNUNET_strdup (baseconfig);
2520 }
2521 else
2522 {
2523 char *ipath;
2524
2525 ipath = GNUNET_OS_installation_get_path (GNUNET_OS_IPK_DATADIR);
2526 if (NULL == ipath)
2527 {
2528 GNUNET_break (0);
2529 return GNUNET_SYSERR;
2530 }
2531 GNUNET_asprintf (&baseconfig, "%s%s", ipath, "config.d");
2532 GNUNET_free (ipath);
2533 }
2534
2535 char *dname = GNUNET_STRINGS_filename_expand (baseconfig);
2536 GNUNET_free (baseconfig);
2537
2538 if ((GNUNET_YES ==
2539 GNUNET_DISK_directory_test (dname,
2540 GNUNET_YES)) &&
2541 (GNUNET_SYSERR ==
2542 GNUNET_CONFIGURATION_load_from (cfg,
2543 dname)))
2544 {
2545 LOG (GNUNET_ERROR_TYPE_WARNING,
2546 "Failed to load base configuration from '%s'\n",
2547 filename);
2548 GNUNET_free (dname);
2549 return GNUNET_SYSERR; /* no configuration at all found */
2550 }
2551 GNUNET_free (dname);
2552 if ((NULL != filename) &&
2553 (GNUNET_OK !=
2554 GNUNET_CONFIGURATION_parse (cfg,
2555 filename)))
2556 {
2557 /* specified configuration not found */
2558 LOG (GNUNET_ERROR_TYPE_WARNING,
2559 "Failed to load configuration from file '%s'\n",
2560 filename);
2561 return GNUNET_SYSERR;
2562 }
2563 if (((GNUNET_YES !=
2564 GNUNET_CONFIGURATION_have_value (cfg,
2565 "PATHS",
2566 "DEFAULTCONFIG"))) &&
2567 (filename != NULL))
2568 GNUNET_CONFIGURATION_set_value_string (cfg,
2569 "PATHS",
2570 "DEFAULTCONFIG",
2571 filename);
2572 return GNUNET_OK;
2573}
2574
2575
2576/* end of configuration.c */
diff --git a/src/lib/util/configuration_helper.c b/src/lib/util/configuration_helper.c
new file mode 100644
index 000000000..d4d5fc732
--- /dev/null
+++ b/src/lib/util/configuration_helper.c
@@ -0,0 +1,303 @@
1/*
2 This file is part of GNUnet.
3 Copyright (C) 2006, 2007, 2008, 2009, 2013, 2020, 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 * @file src/util/configuration_helper.c
22 * @brief helper logic for gnunet-config
23 * @author Christian Grothoff
24 */
25
26#include "platform.h"
27#include "gnunet_util_lib.h"
28
29/**
30 * Print each option in a given section as a filename.
31 *
32 * @param cls closure
33 * @param section name of the section
34 * @param option name of the option
35 * @param value value of the option
36 */
37static void
38print_filename_option (void *cls,
39 const char *section,
40 const char *option,
41 const char *value)
42{
43 const struct GNUNET_CONFIGURATION_Handle *cfg = cls;
44
45 char *value_fn;
46 char *fn;
47
48 GNUNET_assert (GNUNET_OK ==
49 GNUNET_CONFIGURATION_get_value_filename (cfg,
50 section,
51 option,
52 &value_fn));
53 fn = GNUNET_STRINGS_filename_expand (value_fn);
54 if (NULL == fn)
55 fn = value_fn;
56 else
57 GNUNET_free (value_fn);
58 fprintf (stdout,
59 "%s = %s\n",
60 option,
61 fn);
62 GNUNET_free (fn);
63}
64
65
66/**
67 * Print each option in a given section.
68 *
69 * @param cls closure
70 * @param section name of the section
71 * @param option name of the option
72 * @param value value of the option
73 */
74static void
75print_option (void *cls,
76 const char *section,
77 const char *option,
78 const char *value)
79{
80 (void) cls;
81 (void) section;
82
83 fprintf (stdout,
84 "%s = %s\n",
85 option,
86 value);
87}
88
89
90/**
91 * Print out given section name.
92 *
93 * @param cls unused
94 * @param section a section in the configuration file
95 */
96static void
97print_section_name (void *cls,
98 const char *section)
99{
100 (void) cls;
101 fprintf (stdout,
102 "%s\n",
103 section);
104}
105
106
107void
108GNUNET_CONFIGURATION_config_tool_run (
109 void *cls,
110 char *const *args,
111 const char *cfgfile,
112 const struct GNUNET_CONFIGURATION_Handle *cfg)
113{
114 struct GNUNET_CONFIGURATION_ConfigSettings *cs = cls;
115 struct GNUNET_CONFIGURATION_Handle *out = NULL;
116 struct GNUNET_CONFIGURATION_Handle *ncfg = NULL;
117
118 (void) args;
119 if (cs->diagnostics)
120 {
121 /* Re-parse the configuration with diagnostics enabled. */
122 ncfg = GNUNET_CONFIGURATION_create ();
123 GNUNET_CONFIGURATION_enable_diagnostics (ncfg);
124 GNUNET_CONFIGURATION_load (ncfg,
125 cfgfile);
126 cfg = ncfg;
127 }
128
129 if (cs->full)
130 cs->rewrite = GNUNET_YES;
131 if (cs->list_sections)
132 {
133 fprintf (stderr,
134 _ ("The following sections are available:\n"));
135 GNUNET_CONFIGURATION_iterate_sections (cfg,
136 &print_section_name,
137 NULL);
138 return;
139 }
140 if ( (! cs->rewrite) &&
141 (NULL == cs->section) )
142 {
143 char *serialization;
144
145 if (! cs->diagnostics)
146 {
147 fprintf (stderr,
148 _ ("%s, %s or %s argument is required\n"),
149 "--section",
150 "--list-sections",
151 "--diagnostics");
152 cs->global_ret = EXIT_INVALIDARGUMENT;
153 return;
154 }
155 serialization = GNUNET_CONFIGURATION_serialize_diagnostics (cfg);
156 fprintf (stdout,
157 "%s",
158 serialization);
159 GNUNET_free (serialization);
160 }
161 else if ( (NULL != cs->section) &&
162 (NULL == cs->value) )
163 {
164 if (NULL == cs->option)
165 {
166 GNUNET_CONFIGURATION_iterate_section_values (
167 cfg,
168 cs->section,
169 cs->is_filename
170 ? &print_filename_option
171 : &print_option,
172 (void *) cfg);
173 }
174 else
175 {
176 char *value;
177
178 if (cs->is_filename)
179 {
180 if (GNUNET_OK !=
181 GNUNET_CONFIGURATION_get_value_filename (cfg,
182 cs->section,
183 cs->option,
184 &value))
185 {
186 GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR,
187 cs->section,
188 cs->option);
189 cs->global_ret = EXIT_NOTCONFIGURED;
190 return;
191 }
192 }
193 else
194 {
195 if (GNUNET_OK !=
196 GNUNET_CONFIGURATION_get_value_string (cfg,
197 cs->section,
198 cs->option,
199 &value))
200 {
201 GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR,
202 cs->section,
203 cs->option);
204 cs->global_ret = EXIT_NOTCONFIGURED;
205 return;
206 }
207 }
208 fprintf (stdout,
209 "%s\n",
210 value);
211 GNUNET_free (value);
212 }
213 }
214 else if (NULL != cs->section)
215 {
216 if (NULL == cs->option)
217 {
218 fprintf (stderr,
219 _ ("--option argument required to set value\n"));
220 cs->global_ret = EXIT_INVALIDARGUMENT;
221 return;
222 }
223 out = GNUNET_CONFIGURATION_dup (cfg);
224 GNUNET_CONFIGURATION_set_value_string (out,
225 cs->section,
226 cs->option,
227 cs->value);
228 cs->rewrite = GNUNET_YES;
229 }
230 if (cs->rewrite)
231 {
232 char *cfg_fn = NULL;
233
234 if (NULL == out)
235 out = GNUNET_CONFIGURATION_dup (cfg);
236
237 if (NULL == cfgfile)
238 {
239 const char *xdg = getenv ("XDG_CONFIG_HOME");
240
241 if (NULL != xdg)
242 GNUNET_asprintf (&cfg_fn,
243 "%s%s%s",
244 xdg,
245 DIR_SEPARATOR_STR,
246 GNUNET_OS_project_data_get ()->config_file);
247 else
248 cfg_fn = GNUNET_strdup (
249 GNUNET_OS_project_data_get ()->user_config_file);
250 cfgfile = cfg_fn;
251 }
252
253 if (! cs->full)
254 {
255 struct GNUNET_CONFIGURATION_Handle *def;
256
257 def = GNUNET_CONFIGURATION_create ();
258 if (GNUNET_OK !=
259 GNUNET_CONFIGURATION_load (def,
260 NULL))
261 {
262 fprintf (stderr,
263 _ ("failed to load configuration defaults"));
264 cs->global_ret = 1;
265 GNUNET_CONFIGURATION_destroy (def);
266 GNUNET_CONFIGURATION_destroy (out);
267 GNUNET_free (cfg_fn);
268 return;
269 }
270 if (GNUNET_OK !=
271 GNUNET_CONFIGURATION_write_diffs (def,
272 out,
273 cfgfile))
274 cs->global_ret = 2;
275 GNUNET_CONFIGURATION_destroy (def);
276 }
277 else
278 {
279 if (GNUNET_OK !=
280 GNUNET_CONFIGURATION_write (out,
281 cfgfile))
282 cs->global_ret = 2;
283 }
284 GNUNET_free (cfg_fn);
285 }
286 if (NULL != out)
287 GNUNET_CONFIGURATION_destroy (out);
288 if (NULL != ncfg)
289 GNUNET_CONFIGURATION_destroy (ncfg);
290}
291
292
293void
294GNUNET_CONFIGURATION_config_settings_free (
295 struct GNUNET_CONFIGURATION_ConfigSettings *cs)
296{
297 GNUNET_free (cs->option);
298 GNUNET_free (cs->section);
299 GNUNET_free (cs->value);
300}
301
302
303/* end of configuration_helper.c */
diff --git a/src/lib/util/consttime_memcmp.c b/src/lib/util/consttime_memcmp.c
new file mode 100644
index 000000000..d4fa45a5b
--- /dev/null
+++ b/src/lib/util/consttime_memcmp.c
@@ -0,0 +1,280 @@
1/*
2The MIT License (MIT)
3
4Copyright (c) 2015 Christophe Meessen
5
6Permission is hereby granted, free of charge, to any person obtaining a copy
7of this software and associated documentation files (the "Software"), to deal
8in the Software without restriction, including without limitation the rights
9to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10copies of the Software, and to permit persons to whom the Software is
11furnished to do so, subject to the following conditions:
12
13The above copyright notice and this permission notice shall be included in all
14copies or substantial portions of the Software.
15
16THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
22SOFTWARE.
23*/
24
25/* Minimally modified for libgnunetutil: added license header
26 (from https://github.com/chmike/cst_time_memcmp, LICENSE file), and
27 renamed the exported symbol: */
28#define consttime_memcmp GNUNET_memcmp_ct_
29/* Rest of the file is 'original' */
30
31
32#include "platform.h"
33#include <stddef.h>
34#include <inttypes.h>
35
36/*
37 * "constant time" memcmp. Time taken depends on the buffer length, of
38 * course, but not on the content of the buffers.
39 *
40 * Just like the ordinary memcmp function, the return value is
41 * tri-state: <0, 0, or >0. However, applications that need a
42 * constant-time memory comparison function usually need only a
43 * two-state result, signalling only whether the inputs were identical
44 * or different, but not signalling which of the inputs was larger.
45 * This code could be made significantly faster and simpler if the
46 * requirement for a tri-state result were removed.
47 *
48 * In order to protect against adversaries who can observe timing,
49 * cache hits or misses, page faults, etc., and who can use such
50 * observations to learn something about the relationship between the
51 * contents of the two buffers, we have to perform exactly the same
52 * instructions and memory accesses regardless of the contents of the
53 * buffers. We can't stop as soon as we find a difference, we can't
54 * take different conditional branches depending on the data, and we
55 * can't use different pointers or array indexes depending on the data.
56 *
57 * Further reading:
58 *
59 * .Rs
60 * .%A Paul C. Kocher
61 * .%T Timing Attacks on Implementations of Diffie-Hellman, RSA, DSS, and Other Systems
62 * .%D 1996
63 * .%J CRYPTO 1996
64 * .%P 104-113
65 * .%U http://www.cryptography.com/timingattack/paper.html
66 * .%U http://www.webcitation.org/query?url=http%3A%2F%2Fwww.cryptography.com%2Ftimingattack%2Fpaper.html&date=2012-10-17
67 * .Re
68 *
69 * .Rs
70 * .%A D. Boneh
71 * .%A D. Brumley
72 * .%T Remote timing attacks are practical
73 * .%D August 2003
74 * .%J Proceedings of the 12th Usenix Security Symposium, 2003
75 * .%U https://crypto.stanford.edu/~dabo/abstracts/ssl-timing.html
76 * .%U http://www.webcitation.org/query?url=https%3A%2F%2Fcrypto.stanford.edu%2F%7Edabo%2Fabstracts%2Fssl-timing.html&date=2012-10-17
77 * .%U http://www.webcitation.org/query?url=http%3A%2F%2Fcrypto.stanford.edu%2F%7Edabo%2Fpubs%2Fpapers%2Fssl-timing.pdf&date=2012-10-17
78 * .Es
79 *
80 * .Rs
81 * .%A Coda Hale
82 * .%T A Lesson In Timing Attacks (or, Don't use MessageDigest.isEquals)
83 * .%D 13 Aug 2009
84 * .%U http://codahale.com/a-lesson-in-timing-attacks/
85 * .%U http://www.webcitation.org/query?url=http%3A%2F%2Fcodahale.com%2Fa-lesson-in-timing-attacks%2F&date=2012-10-17
86 * .Re
87 *
88 */
89
90/*
91 * A note on portability:
92 *
93 * We assume that char is exactly 8 bits, the same as uint8_t, and that
94 * integer types with exactly 16 bits and exactly 32 bits exist. (If
95 * there is ever a need to change this, then the actual requirement is
96 * that we need a type that is at least two bits wider than char, and
97 * another type that is at least two bits wider than that, or we need to
98 * fake it somehow.)
99 *
100 * We do not assume any particular size for the plain "int" type, except
101 * that it is at least 16 bits, as is guaranteed by the C language
102 * standard.
103 *
104 * We do not assume that signed integer overflow is harmless. We
105 * ensure that signed integer overflow does not occur, so that
106 * implementation-defined overflow behaviour is not invoked.
107 *
108 * We rely on the C standard's guarantees regarding the wraparound
109 * behaviour of unsigned integer arithmetic, and on the analogous
110 * guarantees regarding conversions from signed types to narrower
111 * unsigned types.
112 *
113 * We do not assume that the platform uses two's complement arithmetic.
114 */
115
116/*
117 * How hard do we have to try to prevent unwanted compiler optimisations?
118 *
119 * Try compiling with "#define USE_VOLATILE_TEMPORARY 0", and examine
120 * the compiler output. If the only conditional tests in the entire
121 * function are to test whether len is zero, then all is well, but try
122 * again with different optimisation flags to be sure. If the compiler
123 * emitted code with conditional tests that do anything other than
124 * testing whether len is zero, then that's a problem, so try again with
125 * "#define USE_VOLATILE_TEMPORARY 1". If it's still bad, then you are
126 * out of luck.
127 */
128#define USE_VOLATILE_TEMPORARY 0
129
130int
131consttime_memcmp (const void *b1, const void *b2, size_t len)
132{
133 const uint8_t *c1, *c2;
134 uint16_t d, r, m;
135
136#if USE_VOLATILE_TEMPORARY
137 volatile uint16_t v;
138#else
139 uint16_t v;
140#endif
141
142 c1 = b1;
143 c2 = b2;
144
145 r = 0;
146 while (len)
147 {
148 /*
149 * Take the low 8 bits of r (in the range 0x00 to 0xff,
150 * or 0 to 255);
151 * As explained elsewhere, the low 8 bits of r will be zero
152 * if and only if all bytes compared so far were identical;
153 * Zero-extend to a 16-bit type (in the range 0x0000 to
154 * 0x00ff);
155 * Add 255, yielding a result in the range 255 to 510;
156 * Save that in a volatile variable to prevent
157 * the compiler from trying any shortcuts (the
158 * use of a volatile variable depends on "#ifdef
159 * USE_VOLATILE_TEMPORARY", and most compilers won't
160 * need it);
161 * Divide by 256 yielding a result of 1 if the original
162 * value of r was non-zero, or 0 if r was zero;
163 * Subtract 1, yielding 0 if r was non-zero, or -1 if r
164 * was zero;
165 * Convert to uint16_t, yielding 0x0000 if r was
166 * non-zero, or 0xffff if r was zero;
167 * Save in m.
168 */v = ((uint16_t) (uint8_t) r) + 255;
169 m = v / 256 - 1;
170
171 /*
172 * Get the values from *c1 and *c2 as uint8_t (each will
173 * be in the range 0 to 255, or 0x00 to 0xff);
174 * Convert them to signed int values (still in the
175 * range 0 to 255);
176 * Subtract them using signed arithmetic, yielding a
177 * result in the range -255 to +255;
178 * Convert to uint16_t, yielding a result in the range
179 * 0xff01 to 0xffff (for what was previously -255 to
180 * -1), or 0, or in the range 0x0001 to 0x00ff (for what
181 * was previously +1 to +255).
182 */d = (uint16_t) ((int) *c1 - (int) *c2);
183
184 /*
185 * If the low 8 bits of r were previously 0, then m
186 * is now 0xffff, so (d & m) is the same as d, so we
187 * effectively copy d to r;
188 * Otherwise, if r was previously non-zero, then m is
189 * now 0, so (d & m) is zero, so leave r unchanged.
190 * Note that the low 8 bits of d will be zero if and
191 * only if d == 0, which happens when *c1 == *c2.
192 * The low 8 bits of r are thus zero if and only if the
193 * entirety of r is zero, which happens if and only if
194 * all bytes compared so far were equal. As soon as a
195 * non-zero value is stored in r, it remains unchanged
196 * for the remainder of the loop.
197 */r |= (d & m);
198
199 /*
200 * Increment pointers, decrement length, and loop.
201 */
202 ++c1;
203 ++c2;
204 --len;
205 }
206
207 /*
208 * At this point, r is an unsigned value, which will be 0 if the
209 * final result should be zero, or in the range 0x0001 to 0x00ff
210 * (1 to 255) if the final result should be positive, or in the
211 * range 0xff01 to 0xffff (65281 to 65535) if the final result
212 * should be negative.
213 *
214 * We want to convert the unsigned values in the range 0xff01
215 * to 0xffff to signed values in the range -255 to -1, while
216 * converting the other unsigned values to equivalent signed
217 * values (0, or +1 to +255).
218 *
219 * On a machine with two's complement arithmetic, simply copying
220 * the underlying bits (with sign extension if int is wider than
221 * 16 bits) would do the job, so something like this might work:
222 *
223 * return (int16_t)r;
224 *
225 * However, that invokes implementation-defined behaviour,
226 * because values larger than 32767 can't fit in a signed 16-bit
227 * integer without overflow.
228 *
229 * To avoid any implementation-defined behaviour, we go through
230 * these contortions:
231 *
232 * a. Calculate ((uint32_t)r + 0x8000). The cast to uint32_t
233 * it to prevent problems on platforms where int is narrower
234 * than 32 bits. If int is a larger than 32-bits, then the
235 * usual arithmetic conversions cause this addition to be
236 * done in unsigned int arithmetic. If int is 32 bits
237 * or narrower, then this addition is done in uint32_t
238 * arithmetic. In either case, no overflow or wraparound
239 * occurs, and the result from this step has a value that
240 * will be one of 0x00008000 (32768), or in the range
241 * 0x00008001 to 0x000080ff (32769 to 33023), or in the range
242 * 0x00017f01 to 0x00017fff (98049 to 98303).
243 *
244 * b. Cast the result from (a) to uint16_t. This effectively
245 * discards the high bits of the result, in a way that is
246 * well defined by the C language. The result from this step
247 * will be of type uint16_t, and its value will be one of
248 * 0x8000 (32768), or in the range 0x8001 to 0x80ff (32769 to
249 * 33023), or in the range 0x7f01 to 0x7fff (32513 to
250 * 32767).
251 *
252 * c. Cast the result from (b) to int32_t. We use int32_t
253 * instead of int because we need a type that's strictly
254 * larger than 16 bits, and the C standard allows
255 * implementations where int is only 16 bits. The result
256 * from this step will be of type int32_t, and its value will
257 * be one of 0x00008000 (32768), or in the range 0x00008001
258 * to 0x000080ff (32769 to 33023), or in the range 0x00007f01
259 * to 0x00007fff (32513 to 32767).
260 *
261 * d. Take the result from (c) and subtract 0x8000 (32768) using
262 * signed int32_t arithmetic. The result from this step will
263 * be of type int32_t and the value will be one of
264 * 0x00000000 (0), or in the range 0x00000001 to 0x000000ff
265 * (+1 to +255), or in the range 0xffffff01 to 0xffffffff
266 * (-255 to -1).
267 *
268 * e. Cast the result from (d) to int. This does nothing
269 * interesting, except to make explicit what would have been
270 * implicit in the return statement. The final result is an
271 * int in the range -255 to +255.
272 *
273 * Unfortunately, compilers don't seem to be good at figuring
274 * out that most of this can be optimised away by careful choice
275 * of register width and sign extension.
276 *
277 */return (/*e*/ int) (/*d*/
278 (/*c*/ int32_t) (/*b*/ uint16_t) (/*a*/ (uint32_t) r + 0x8000)
279 - 0x8000);
280}
diff --git a/src/lib/util/container_bloomfilter.c b/src/lib/util/container_bloomfilter.c
new file mode 100644
index 000000000..7e4faaf3f
--- /dev/null
+++ b/src/lib/util/container_bloomfilter.c
@@ -0,0 +1,806 @@
1/*
2 This file is part of GNUnet.
3 Copyright (C) 2001, 2002, 2003, 2004, 2006, 2008, 2011, 2012, 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 util/container_bloomfilter.c
22 * @brief data structure used to reduce disk accesses.
23 *
24 * The idea basically: Create a signature for each element in the
25 * database. Add those signatures to a bit array. When doing a lookup,
26 * check if the bit array matches the signature of the requested
27 * element. If yes, address the disk, otherwise return 'not found'.
28 *
29 * A property of the bloom filter is that sometimes we will have
30 * a match even if the element is not on the disk (then we do
31 * an unnecessary disk access), but what's most important is that
32 * we never get a single "false negative".
33 *
34 * To be able to delete entries from the bloom filter, we maintain
35 * a 4 bit counter in the file on the drive (we still use only one
36 * bit in memory).
37 *
38 * @author Igor Wronsky
39 * @author Christian Grothoff
40 */
41
42
43#include "platform.h"
44#include "gnunet_util_lib.h"
45
46#define LOG(kind, ...) \
47 GNUNET_log_from (kind, "util-container-bloomfilter", __VA_ARGS__)
48
49#define LOG_STRERROR(kind, syscall) \
50 GNUNET_log_from_strerror (kind, "util-container-bloomfilter", syscall)
51
52#define LOG_STRERROR_FILE(kind, syscall, filename) \
53 GNUNET_log_from_strerror_file (kind, \
54 "util-container-bloomfilter", \
55 syscall, \
56 filename)
57
58struct GNUNET_CONTAINER_BloomFilter
59{
60 /**
61 * The actual bloomfilter bit array
62 */
63 char *bitArray;
64
65 /**
66 * Filename of the filter
67 */
68 char *filename;
69
70 /**
71 * The bit counter file on disk
72 */
73 struct GNUNET_DISK_FileHandle *fh;
74
75 /**
76 * How many bits we set for each stored element
77 */
78 unsigned int addressesPerElement;
79
80 /**
81 * Size of bitArray in bytes
82 */
83 size_t bitArraySize;
84};
85
86
87size_t
88GNUNET_CONTAINER_bloomfilter_get_element_addresses (
89 const struct GNUNET_CONTAINER_BloomFilter *bf)
90{
91 if (bf == NULL)
92 return 0;
93 return bf->addressesPerElement;
94}
95
96
97size_t
98GNUNET_CONTAINER_bloomfilter_get_size (
99 const struct GNUNET_CONTAINER_BloomFilter *bf)
100{
101 if (bf == NULL)
102 return 0;
103 return bf->bitArraySize;
104}
105
106
107struct GNUNET_CONTAINER_BloomFilter *
108GNUNET_CONTAINER_bloomfilter_copy (
109 const struct GNUNET_CONTAINER_BloomFilter *bf)
110{
111 return GNUNET_CONTAINER_bloomfilter_init (bf->bitArray,
112 bf->bitArraySize,
113 bf->addressesPerElement);
114}
115
116
117/**
118 * Sets a bit active in the bitArray. Increment bit-specific
119 * usage counter on disk only if below 4bit max (==15).
120 *
121 * @param bitArray memory area to set the bit in
122 * @param bitIdx which bit to set
123 */
124static void
125setBit (char *bitArray,
126 unsigned int bitIdx)
127{
128 size_t arraySlot;
129 unsigned int targetBit;
130
131 arraySlot = bitIdx / 8;
132 targetBit = (1L << (bitIdx % 8));
133 bitArray[arraySlot] |= targetBit;
134}
135
136
137/**
138 * Clears a bit from bitArray. Bit is cleared from the array
139 * only if the respective usage counter on the disk hits/is zero.
140 *
141 * @param bitArray memory area to set the bit in
142 * @param bitIdx which bit to unset
143 */
144static void
145clearBit (char *bitArray, unsigned int bitIdx)
146{
147 size_t slot;
148 unsigned int targetBit;
149
150 slot = bitIdx / 8;
151 targetBit = (1L << (bitIdx % 8));
152 bitArray[slot] = bitArray[slot] & (~targetBit);
153}
154
155
156/**
157 * Checks if a bit is active in the bitArray
158 *
159 * @param bitArray memory area to set the bit in
160 * @param bitIdx which bit to test
161 * @return true if the bit is set, false if not.
162 */
163static bool
164testBit (char *bitArray,
165 unsigned int bitIdx)
166{
167 size_t slot;
168 unsigned int targetBit;
169
170 slot = bitIdx / 8;
171 targetBit = (1L << (bitIdx % 8));
172 if (bitArray[slot] & targetBit)
173 return true;
174 return false;
175}
176
177
178/**
179 * Sets a bit active in the bitArray and increments
180 * bit-specific usage counter on disk (but only if
181 * the counter was below 4 bit max (==15)).
182 *
183 * @param bitArray memory area to set the bit in
184 * @param bitIdx which bit to test
185 * @param fh A file to keep the 4 bit address usage counters in
186 */
187static void
188incrementBit (char *bitArray,
189 unsigned int bitIdx,
190 const struct GNUNET_DISK_FileHandle *fh)
191{
192 off_t fileSlot;
193 unsigned char value;
194 unsigned int high;
195 unsigned int low;
196 unsigned int targetLoc;
197
198 setBit (bitArray,
199 bitIdx);
200 if (GNUNET_DISK_handle_invalid (fh))
201 return;
202 /* Update the counter file on disk */
203 fileSlot = bitIdx / 2;
204 targetLoc = bitIdx % 2;
205
206 GNUNET_assert (fileSlot ==
207 GNUNET_DISK_file_seek (fh, fileSlot, GNUNET_DISK_SEEK_SET));
208 if (1 != GNUNET_DISK_file_read (fh, &value, 1))
209 value = 0;
210 low = value & 0xF;
211 high = (value & (~0xF)) >> 4;
212
213 if (targetLoc == 0)
214 {
215 if (low < 0xF)
216 low++;
217 }
218 else
219 {
220 if (high < 0xF)
221 high++;
222 }
223 value = ((high << 4) | low);
224 GNUNET_assert (fileSlot ==
225 GNUNET_DISK_file_seek (fh, fileSlot, GNUNET_DISK_SEEK_SET));
226 GNUNET_assert (1 == GNUNET_DISK_file_write (fh, &value, 1));
227}
228
229
230/**
231 * Clears a bit from bitArray if the respective usage
232 * counter on the disk hits/is zero.
233 *
234 * @param bitArray memory area to set the bit in
235 * @param bitIdx which bit to test
236 * @param fh A file to keep the 4bit address usage counters in
237 */
238static void
239decrementBit (char *bitArray,
240 unsigned int bitIdx,
241 const struct GNUNET_DISK_FileHandle *fh)
242{
243 off_t fileslot;
244 unsigned char value;
245 unsigned int high;
246 unsigned int low;
247 unsigned int targetLoc;
248
249 if (GNUNET_DISK_handle_invalid (fh))
250 return; /* cannot decrement! */
251 /* Each char slot in the counter file holds two 4 bit counters */
252 fileslot = bitIdx / 2;
253 targetLoc = bitIdx % 2;
254 if (GNUNET_SYSERR ==
255 GNUNET_DISK_file_seek (fh, fileslot, GNUNET_DISK_SEEK_SET))
256 {
257 GNUNET_log_strerror (GNUNET_ERROR_TYPE_ERROR, "seek");
258 return;
259 }
260 if (1 != GNUNET_DISK_file_read (fh, &value, 1))
261 value = 0;
262 low = value & 0xF;
263 high = (value & 0xF0) >> 4;
264
265 /* decrement, but once we have reached the max, never go back! */
266 if (targetLoc == 0)
267 {
268 if ((low > 0) && (low < 0xF))
269 low--;
270 if (low == 0)
271 {
272 clearBit (bitArray, bitIdx);
273 }
274 }
275 else
276 {
277 if ((high > 0) && (high < 0xF))
278 high--;
279 if (high == 0)
280 {
281 clearBit (bitArray, bitIdx);
282 }
283 }
284 value = ((high << 4) | low);
285 if (GNUNET_SYSERR ==
286 GNUNET_DISK_file_seek (fh, fileslot, GNUNET_DISK_SEEK_SET))
287 {
288 GNUNET_log_strerror (GNUNET_ERROR_TYPE_ERROR, "seek");
289 return;
290 }
291 GNUNET_assert (1 == GNUNET_DISK_file_write (fh, &value, 1));
292}
293
294
295#define BUFFSIZE 65536
296
297/**
298 * Creates a file filled with zeroes
299 *
300 * @param fh the file handle
301 * @param size the size of the file
302 * @return #GNUNET_OK if created ok, #GNUNET_SYSERR otherwise
303 */
304static enum GNUNET_GenericReturnValue
305make_empty_file (const struct GNUNET_DISK_FileHandle *fh,
306 size_t size)
307{
308 char buffer[BUFFSIZE];
309 size_t bytesleft = size;
310 int res = 0;
311
312 if (GNUNET_DISK_handle_invalid (fh))
313 return GNUNET_SYSERR;
314 memset (buffer, 0, sizeof(buffer));
315 GNUNET_DISK_file_seek (fh, 0, GNUNET_DISK_SEEK_SET);
316 while (bytesleft > 0)
317 {
318 if (bytesleft > sizeof(buffer))
319 {
320 res = GNUNET_DISK_file_write (fh, buffer, sizeof(buffer));
321 if (res >= 0)
322 bytesleft -= res;
323 }
324 else
325 {
326 res = GNUNET_DISK_file_write (fh, buffer, bytesleft);
327 if (res >= 0)
328 bytesleft -= res;
329 }
330 if (GNUNET_SYSERR == res)
331 return GNUNET_SYSERR;
332 }
333 return GNUNET_OK;
334}
335
336
337/* ************** GNUNET_CONTAINER_BloomFilter iterator ********* */
338
339/**
340 * Iterator (callback) method to be called by the
341 * bloomfilter iterator on each bit that is to be
342 * set or tested for the key.
343 *
344 * @param cls closure
345 * @param bf the filter to manipulate
346 * @param bit the current bit
347 * @return #GNUNET_YES to continue, #GNUNET_NO to stop early
348 */
349typedef enum GNUNET_GenericReturnValue
350(*BitIterator)(void *cls,
351 const struct GNUNET_CONTAINER_BloomFilter *bf,
352 unsigned int bit);
353
354
355/**
356 * Call an iterator for each bit that the bloomfilter
357 * must test or set for this element.
358 *
359 * @param bf the filter
360 * @param callback the method to call
361 * @param arg extra argument to callback
362 * @param key the key for which we iterate over the BF bits
363 */
364static void
365iterateBits (const struct GNUNET_CONTAINER_BloomFilter *bf,
366 BitIterator callback,
367 void *arg,
368 const struct GNUNET_HashCode *key)
369{
370 struct GNUNET_HashCode tmp = *key;
371 int bitCount;
372 unsigned int slot = 0;
373
374 bitCount = bf->addressesPerElement;
375 GNUNET_assert (bf->bitArraySize > 0);
376 GNUNET_assert (bf->bitArraySize * 8LL > bf->bitArraySize);
377 while (bitCount > 0)
378 {
379 while ( (0 != bitCount) &&
380 (slot < (sizeof(struct GNUNET_HashCode) / sizeof(uint32_t))) )
381 {
382 if (GNUNET_YES !=
383 callback (arg,
384 bf,
385 ntohl ((((uint32_t *) &tmp)[slot]))
386 % ((bf->bitArraySize * 8LL))))
387 return;
388 slot++;
389 bitCount--;
390 }
391 if (0 == bitCount)
392 break;
393 GNUNET_CRYPTO_hash (&tmp,
394 sizeof(tmp),
395 &tmp);
396 slot = 0;
397 }
398}
399
400
401/**
402 * Callback: increment bit
403 *
404 * @param cls pointer to writeable form of bf
405 * @param bf the filter to manipulate
406 * @param bit the bit to increment
407 * @return #GNUNET_YES
408 */
409static enum GNUNET_GenericReturnValue
410incrementBitCallback (void *cls,
411 const struct GNUNET_CONTAINER_BloomFilter *bf,
412 unsigned int bit)
413{
414 struct GNUNET_CONTAINER_BloomFilter *b = cls;
415
416 incrementBit (b->bitArray,
417 bit,
418 bf->fh);
419 return GNUNET_YES;
420}
421
422
423/**
424 * Callback: decrement bit
425 *
426 * @param cls pointer to writeable form of bf
427 * @param bf the filter to manipulate
428 * @param bit the bit to decrement
429 * @return #GNUNET_YES
430 */
431static enum GNUNET_GenericReturnValue
432decrementBitCallback (void *cls,
433 const struct GNUNET_CONTAINER_BloomFilter *bf,
434 unsigned int bit)
435{
436 struct GNUNET_CONTAINER_BloomFilter *b = cls;
437
438 decrementBit (b->bitArray,
439 bit,
440 bf->fh);
441 return GNUNET_YES;
442}
443
444
445/**
446 * Callback: test if all bits are set
447 *
448 * @param cls pointer set to false if bit is not set
449 * @param bf the filter
450 * @param bit the bit to test
451 * @return #GNUNET_YES if the bit is set, #GNUNET_NO if not
452 */
453static enum GNUNET_GenericReturnValue
454testBitCallback (void *cls,
455 const struct GNUNET_CONTAINER_BloomFilter *bf,
456 unsigned int bit)
457{
458 bool *arg = cls;
459
460 if (! testBit (bf->bitArray, bit))
461 {
462 *arg = false;
463 return GNUNET_NO;
464 }
465 return GNUNET_YES;
466}
467
468
469/* *********************** INTERFACE **************** */
470
471struct GNUNET_CONTAINER_BloomFilter *
472GNUNET_CONTAINER_bloomfilter_load (const char *filename,
473 size_t size,
474 unsigned int k)
475{
476 struct GNUNET_CONTAINER_BloomFilter *bf;
477 char *rbuff;
478 off_t pos;
479 int i;
480 size_t ui;
481 off_t fsize;
482 int must_read;
483
484 GNUNET_assert (NULL != filename);
485 if ((k == 0) || (size == 0))
486 return NULL;
487 if (size < BUFFSIZE)
488 size = BUFFSIZE;
489 ui = 1;
490 while ((ui < size) && (ui * 2 > ui))
491 ui *= 2;
492 size = ui; /* make sure it's a power of 2 */
493
494 bf = GNUNET_new (struct GNUNET_CONTAINER_BloomFilter);
495 /* Try to open a bloomfilter file */
496 if (GNUNET_YES == GNUNET_DISK_file_test (filename))
497 bf->fh = GNUNET_DISK_file_open (filename,
498 GNUNET_DISK_OPEN_READWRITE,
499 GNUNET_DISK_PERM_USER_READ
500 | GNUNET_DISK_PERM_USER_WRITE);
501 if (NULL != bf->fh)
502 {
503 /* file existed, try to read it! */
504 must_read = GNUNET_YES;
505 if (GNUNET_OK !=
506 GNUNET_DISK_file_handle_size (bf->fh,
507 &fsize))
508 {
509 GNUNET_DISK_file_close (bf->fh);
510 GNUNET_free (bf);
511 return NULL;
512 }
513 if (0 == fsize)
514 {
515 /* found existing empty file, just overwrite */
516 if (GNUNET_OK !=
517 make_empty_file (bf->fh,
518 size * 4LL))
519 {
520 GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING,
521 "write");
522 GNUNET_DISK_file_close (bf->fh);
523 GNUNET_free (bf);
524 return NULL;
525 }
526 }
527 else if (fsize != ((off_t) size) * 4LL)
528 {
529 GNUNET_log (
530 GNUNET_ERROR_TYPE_ERROR,
531 _ (
532 "Size of file on disk is incorrect for this Bloom filter (want %llu, have %llu)\n"),
533 (unsigned long long) (size * 4LL),
534 (unsigned long long) fsize);
535 GNUNET_DISK_file_close (bf->fh);
536 GNUNET_free (bf);
537 return NULL;
538 }
539 }
540 else
541 {
542 /* file did not exist, don't read, just create */
543 must_read = GNUNET_NO;
544 bf->fh = GNUNET_DISK_file_open (filename,
545 GNUNET_DISK_OPEN_CREATE
546 | GNUNET_DISK_OPEN_READWRITE,
547 GNUNET_DISK_PERM_USER_READ
548 | GNUNET_DISK_PERM_USER_WRITE);
549 if (NULL == bf->fh)
550 {
551 GNUNET_free (bf);
552 return NULL;
553 }
554 if (GNUNET_OK != make_empty_file (bf->fh, size * 4LL))
555 {
556 GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING, "write");
557 GNUNET_DISK_file_close (bf->fh);
558 GNUNET_free (bf);
559 return NULL;
560 }
561 }
562 bf->filename = GNUNET_strdup (filename);
563 /* Alloc block */
564 bf->bitArray = GNUNET_malloc_large (size);
565 if (NULL == bf->bitArray)
566 {
567 if (NULL != bf->fh)
568 GNUNET_DISK_file_close (bf->fh);
569 GNUNET_free (bf->filename);
570 GNUNET_free (bf);
571 return NULL;
572 }
573 bf->bitArraySize = size;
574 bf->addressesPerElement = k;
575 if (GNUNET_YES != must_read)
576 return bf; /* already done! */
577 /* Read from the file what bits we can */
578 rbuff = GNUNET_malloc (BUFFSIZE);
579 pos = 0;
580 while (pos < ((off_t) size) * 8LL)
581 {
582 int res;
583
584 res = GNUNET_DISK_file_read (bf->fh, rbuff, BUFFSIZE);
585 if (res == -1)
586 {
587 LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_WARNING, "read", bf->filename);
588 GNUNET_free (rbuff);
589 GNUNET_free (bf->filename);
590 GNUNET_DISK_file_close (bf->fh);
591 GNUNET_free (bf);
592 return NULL;
593 }
594 if (res == 0)
595 break; /* is ok! we just did not use that many bits yet */
596 for (i = 0; i < res; i++)
597 {
598 if ((rbuff[i] & 0x0F) != 0)
599 setBit (bf->bitArray, pos + i * 2);
600 if ((rbuff[i] & 0xF0) != 0)
601 setBit (bf->bitArray, pos + i * 2 + 1);
602 }
603 if (res < BUFFSIZE)
604 break;
605 pos += BUFFSIZE * 2; /* 2 bits per byte in the buffer */
606 }
607 GNUNET_free (rbuff);
608 return bf;
609}
610
611
612struct GNUNET_CONTAINER_BloomFilter *
613GNUNET_CONTAINER_bloomfilter_init (const char *data,
614 size_t size,
615 unsigned int k)
616{
617 struct GNUNET_CONTAINER_BloomFilter *bf;
618
619 if ((0 == k) || (0 == size))
620 return NULL;
621 bf = GNUNET_new (struct GNUNET_CONTAINER_BloomFilter);
622 bf->filename = NULL;
623 bf->fh = NULL;
624 bf->bitArray = GNUNET_malloc_large (size);
625 if (NULL == bf->bitArray)
626 {
627 GNUNET_free (bf);
628 return NULL;
629 }
630 bf->bitArraySize = size;
631 bf->addressesPerElement = k;
632 if (NULL != data)
633 GNUNET_memcpy (bf->bitArray, data, size);
634 return bf;
635}
636
637
638enum GNUNET_GenericReturnValue
639GNUNET_CONTAINER_bloomfilter_get_raw_data (
640 const struct GNUNET_CONTAINER_BloomFilter *bf,
641 char *data,
642 size_t size)
643{
644 if (NULL == bf)
645 return GNUNET_SYSERR;
646 if (bf->bitArraySize != size)
647 return GNUNET_SYSERR;
648 GNUNET_memcpy (data, bf->bitArray, size);
649 return GNUNET_OK;
650}
651
652
653void
654GNUNET_CONTAINER_bloomfilter_free (struct GNUNET_CONTAINER_BloomFilter *bf)
655{
656 if (NULL == bf)
657 return;
658 if (bf->fh != NULL)
659 GNUNET_DISK_file_close (bf->fh);
660 GNUNET_free (bf->filename);
661 GNUNET_free (bf->bitArray);
662 GNUNET_free (bf);
663}
664
665
666void
667GNUNET_CONTAINER_bloomfilter_clear (struct GNUNET_CONTAINER_BloomFilter *bf)
668{
669 if (NULL == bf)
670 return;
671
672 memset (bf->bitArray, 0, bf->bitArraySize);
673 if (bf->filename != NULL)
674 make_empty_file (bf->fh, bf->bitArraySize * 4LL);
675}
676
677
678bool
679GNUNET_CONTAINER_bloomfilter_test (
680 const struct GNUNET_CONTAINER_BloomFilter *bf,
681 const struct GNUNET_HashCode *e)
682{
683 bool res;
684
685 if (NULL == bf)
686 return true;
687 res = true;
688 iterateBits (bf,
689 &testBitCallback,
690 &res,
691 e);
692 return res;
693}
694
695
696void
697GNUNET_CONTAINER_bloomfilter_add (struct GNUNET_CONTAINER_BloomFilter *bf,
698 const struct GNUNET_HashCode *e)
699{
700 if (NULL == bf)
701 return;
702 iterateBits (bf,
703 &incrementBitCallback,
704 bf,
705 e);
706}
707
708
709enum GNUNET_GenericReturnValue
710GNUNET_CONTAINER_bloomfilter_or (struct GNUNET_CONTAINER_BloomFilter *bf,
711 const char *data,
712 size_t size)
713{
714 unsigned int i;
715 unsigned int n;
716 unsigned long long *fc;
717 const unsigned long long *dc;
718
719 if (NULL == bf)
720 return GNUNET_YES;
721 if (bf->bitArraySize != size)
722 return GNUNET_SYSERR;
723 fc = (unsigned long long *) bf->bitArray;
724 dc = (const unsigned long long *) data;
725 n = size / sizeof(unsigned long long);
726
727 for (i = 0; i < n; i++)
728 fc[i] |= dc[i];
729 for (i = n * sizeof(unsigned long long); i < size; i++)
730 bf->bitArray[i] |= data[i];
731 return GNUNET_OK;
732}
733
734
735enum GNUNET_GenericReturnValue
736GNUNET_CONTAINER_bloomfilter_or2 (
737 struct GNUNET_CONTAINER_BloomFilter *bf,
738 const struct GNUNET_CONTAINER_BloomFilter *to_or)
739{
740 unsigned int i;
741 unsigned int n;
742 unsigned long long *fc;
743 const unsigned long long *dc;
744 size_t size;
745
746 if (NULL == bf)
747 return GNUNET_OK;
748 if (bf->bitArraySize != to_or->bitArraySize)
749 {
750 GNUNET_break (0);
751 return GNUNET_SYSERR;
752 }
753 size = bf->bitArraySize;
754 fc = (unsigned long long *) bf->bitArray;
755 dc = (const unsigned long long *) to_or->bitArray;
756 n = size / sizeof(unsigned long long);
757
758 for (i = 0; i < n; i++)
759 fc[i] |= dc[i];
760 for (i = n * sizeof(unsigned long long); i < size; i++)
761 bf->bitArray[i] |= to_or->bitArray[i];
762 return GNUNET_OK;
763}
764
765
766void
767GNUNET_CONTAINER_bloomfilter_remove (struct GNUNET_CONTAINER_BloomFilter *bf,
768 const struct GNUNET_HashCode *e)
769{
770 if (NULL == bf)
771 return;
772 if (NULL == bf->filename)
773 return;
774 iterateBits (bf,
775 &decrementBitCallback,
776 bf,
777 e);
778}
779
780
781void
782GNUNET_CONTAINER_bloomfilter_resize (struct GNUNET_CONTAINER_BloomFilter *bf,
783 GNUNET_CONTAINER_HashCodeIterator iterator,
784 void *iterator_cls,
785 size_t size,
786 unsigned int k)
787{
788 struct GNUNET_HashCode hc;
789 unsigned int i;
790
791 GNUNET_free (bf->bitArray);
792 i = 1;
793 while (i < size)
794 i *= 2;
795 size = i; /* make sure it's a power of 2 */
796 bf->addressesPerElement = k;
797 bf->bitArraySize = size;
798 bf->bitArray = GNUNET_malloc (size);
799 if (NULL != bf->filename)
800 make_empty_file (bf->fh, bf->bitArraySize * 4LL);
801 while (GNUNET_YES == iterator (iterator_cls, &hc))
802 GNUNET_CONTAINER_bloomfilter_add (bf, &hc);
803}
804
805
806/* end of container_bloomfilter.c */
diff --git a/src/lib/util/container_heap.c b/src/lib/util/container_heap.c
new file mode 100644
index 000000000..232e66bf9
--- /dev/null
+++ b/src/lib/util/container_heap.c
@@ -0,0 +1,501 @@
1/*
2 This file is part of GNUnet.
3 Copyright (C) 2008, 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/**
22 * @file util/container_heap.c
23 * @brief Implementation of a heap
24 * @author Nathan Evans
25 * @author Christian Grothoff
26 */
27
28
29#include "platform.h"
30#include "gnunet_util_lib.h"
31
32#define LOG(kind, ...) GNUNET_log_from (kind, "util-container-heap", \
33 __VA_ARGS__)
34
35#define EXTRA_CHECKS 0
36
37/**
38 * Node in the heap.
39 */
40struct GNUNET_CONTAINER_HeapNode
41{
42 /**
43 * Heap this node belongs to.
44 */
45 struct GNUNET_CONTAINER_Heap *heap;
46
47 /**
48 * Parent node.
49 */
50 struct GNUNET_CONTAINER_HeapNode *parent;
51
52 /**
53 * Left child.
54 */
55 struct GNUNET_CONTAINER_HeapNode *left_child;
56
57 /**
58 * Right child.
59 */
60 struct GNUNET_CONTAINER_HeapNode *right_child;
61
62 /**
63 * Our element.
64 */
65 void *element;
66
67 /**
68 * Cost for this element.
69 */
70 GNUNET_CONTAINER_HeapCostType cost;
71
72 /**
73 * Number of elements below this node in the heap
74 * (excluding this node itself).
75 */
76 unsigned int tree_size;
77};
78
79/**
80 * Handle to a node in a heap.
81 */
82struct GNUNET_CONTAINER_Heap
83{
84 /**
85 * Root of the heap.
86 */
87 struct GNUNET_CONTAINER_HeapNode *root;
88
89 /**
90 * Current position of our random walk.
91 */
92 struct GNUNET_CONTAINER_HeapNode *walk_pos;
93
94 /**
95 * Number of elements in the heap.
96 */
97 unsigned int size;
98
99 /**
100 * How is the heap sorted?
101 */
102 enum GNUNET_CONTAINER_HeapOrder order;
103};
104
105
106#if EXTRA_CHECKS
107/**
108 * Check if internal invariants hold for the given node.
109 *
110 * @param node subtree to check
111 */
112static void
113check (const struct GNUNET_CONTAINER_HeapNode *node)
114{
115 if (NULL == node)
116 return;
117 GNUNET_assert (node->tree_size ==
118 ((node->left_child ==
119 NULL) ? 0 : 1 + node->left_child->tree_size)
120 + ((node->right_child ==
121 NULL) ? 0 : 1 + node->right_child->tree_size));
122 check (node->left_child);
123 check (node->right_child);
124}
125
126
127#define CHECK(n) check (n)
128#else
129#define CHECK(n) do {} while (0)
130#endif
131
132
133struct GNUNET_CONTAINER_Heap *
134GNUNET_CONTAINER_heap_create (enum GNUNET_CONTAINER_HeapOrder order)
135{
136 struct GNUNET_CONTAINER_Heap *heap;
137
138 heap = GNUNET_new (struct GNUNET_CONTAINER_Heap);
139 heap->order = order;
140 return heap;
141}
142
143
144void
145GNUNET_CONTAINER_heap_destroy (struct GNUNET_CONTAINER_Heap *heap)
146{
147 GNUNET_break (heap->size == 0);
148 GNUNET_free (heap);
149}
150
151
152void *
153GNUNET_CONTAINER_heap_peek (const struct GNUNET_CONTAINER_Heap *heap)
154{
155 if (heap->root == NULL)
156 return NULL;
157 return heap->root->element;
158}
159
160
161/**
162 * Get @a element and @a cost stored at the root of @a heap.
163 *
164 * @param[in] heap Heap to inspect.
165 * @param[out] element Root element is returned here.
166 * @param[out] cost Cost of @a element is returned here.
167 * @return #GNUNET_YES if an element is returned,
168 * #GNUNET_NO if the heap is empty.
169 */
170enum GNUNET_GenericReturnValue
171GNUNET_CONTAINER_heap_peek2 (const struct GNUNET_CONTAINER_Heap *heap,
172 void **element,
173 GNUNET_CONTAINER_HeapCostType *cost)
174{
175 if (NULL == heap->root)
176 return GNUNET_NO;
177 if (NULL != element)
178 *element = heap->root->element;
179 if (NULL != cost)
180 *cost = heap->root->cost;
181 return GNUNET_YES;
182}
183
184
185unsigned int
186GNUNET_CONTAINER_heap_get_size (const struct GNUNET_CONTAINER_Heap *heap)
187{
188 return heap->size;
189}
190
191
192GNUNET_CONTAINER_HeapCostType
193GNUNET_CONTAINER_heap_node_get_cost (const struct GNUNET_CONTAINER_HeapNode
194 *node)
195{
196 return node->cost;
197}
198
199
200/**
201 * Iterate over the children of the given node.
202 *
203 * @param heap argument to give to iterator
204 * @param node node to iterate over
205 * @param iterator function to call on each node
206 * @param iterator_cls closure for iterator
207 * @return GNUNET_YES to continue to iterate
208 */
209static int
210node_iterator (const struct GNUNET_CONTAINER_Heap *heap,
211 struct GNUNET_CONTAINER_HeapNode *node,
212 GNUNET_CONTAINER_HeapIterator iterator, void *iterator_cls)
213{
214 if (node == NULL)
215 return GNUNET_YES;
216 if (GNUNET_YES !=
217 node_iterator (heap, node->left_child, iterator, iterator_cls))
218 return GNUNET_NO;
219 if (GNUNET_YES !=
220 node_iterator (heap, node->right_child, iterator, iterator_cls))
221 return GNUNET_NO;
222 return iterator (iterator_cls, node, node->element, node->cost);
223}
224
225
226void
227GNUNET_CONTAINER_heap_iterate (const struct GNUNET_CONTAINER_Heap *heap,
228 GNUNET_CONTAINER_HeapIterator iterator,
229 void *iterator_cls)
230{
231 (void) node_iterator (heap, heap->root, iterator, iterator_cls);
232}
233
234
235void *
236GNUNET_CONTAINER_heap_walk_get_next (struct GNUNET_CONTAINER_Heap *heap)
237{
238 struct GNUNET_CONTAINER_HeapNode *pos;
239 void *element;
240
241 if (heap->root == NULL)
242 return NULL;
243 pos = heap->walk_pos;
244 if (pos == NULL)
245 pos = heap->root;
246 element = pos->element;
247 heap->walk_pos =
248 (0 ==
249 GNUNET_CRYPTO_random_u32 (GNUNET_CRYPTO_QUALITY_WEAK,
250 2)) ? pos->right_child : pos->left_child;
251 return element;
252}
253
254
255/**
256 * Insert the given node 'node' into the subtree starting
257 * at 'pos' (while keeping the tree somewhat balanced).
258 *
259 * @param heap heap to modify
260 * @param pos existing tree
261 * @param node node to insert (which may be a subtree itself)
262 */
263static void
264insert_node (struct GNUNET_CONTAINER_Heap *heap,
265 struct GNUNET_CONTAINER_HeapNode *pos,
266 struct GNUNET_CONTAINER_HeapNode *node)
267{
268 struct GNUNET_CONTAINER_HeapNode *parent;
269
270 GNUNET_assert (node->parent == NULL);
271 while ((heap->order == GNUNET_CONTAINER_HEAP_ORDER_MAX) ? (pos->cost >=
272 node->cost)
273 : (pos->cost <= node->cost))
274 {
275 /* node is descendent of pos */
276 pos->tree_size += (1 + node->tree_size);
277 if (pos->left_child == NULL)
278 {
279 pos->left_child = node;
280 node->parent = pos;
281 return;
282 }
283 if (pos->right_child == NULL)
284 {
285 pos->right_child = node;
286 node->parent = pos;
287 return;
288 }
289 /* keep it balanced by descending into smaller subtree */
290 if (pos->left_child->tree_size < pos->right_child->tree_size)
291 pos = pos->left_child;
292 else
293 pos = pos->right_child;
294 }
295 /* make 'node' parent of 'pos' */
296 parent = pos->parent;
297 pos->parent = NULL;
298 node->parent = parent;
299 if (NULL == parent)
300 {
301 heap->root = node;
302 }
303 else
304 {
305 if (parent->left_child == pos)
306 parent->left_child = node;
307 else
308 parent->right_child = node;
309 }
310 /* insert 'pos' below 'node' */
311 insert_node (heap, node, pos);
312 CHECK (pos);
313}
314
315
316struct GNUNET_CONTAINER_HeapNode *
317GNUNET_CONTAINER_heap_insert (struct GNUNET_CONTAINER_Heap *heap, void *element,
318 GNUNET_CONTAINER_HeapCostType cost)
319{
320 struct GNUNET_CONTAINER_HeapNode *node;
321
322 node = GNUNET_new (struct GNUNET_CONTAINER_HeapNode);
323 node->heap = heap;
324 node->element = element;
325 node->cost = cost;
326 heap->size++;
327 if (NULL == heap->root)
328 heap->root = node;
329 else
330 insert_node (heap, heap->root, node);
331 GNUNET_assert (heap->size == heap->root->tree_size + 1);
332 CHECK (heap->root);
333 return node;
334}
335
336
337/**
338 * Remove root of the heap.
339 *
340 * @param heap heap to modify
341 * @return element data stored at the root node, NULL if heap is empty
342 */
343void *
344GNUNET_CONTAINER_heap_remove_root (struct GNUNET_CONTAINER_Heap *heap)
345{
346 void *ret;
347 struct GNUNET_CONTAINER_HeapNode *root;
348
349 if (NULL == (root = heap->root))
350 return NULL;
351 heap->size--;
352 ret = root->element;
353 if (root->left_child == NULL)
354 {
355 heap->root = root->right_child;
356 if (root->right_child != NULL)
357 root->right_child->parent = NULL;
358 }
359 else if (root->right_child == NULL)
360 {
361 heap->root = root->left_child;
362 root->left_child->parent = NULL;
363 }
364 else
365 {
366 root->left_child->parent = NULL;
367 root->right_child->parent = NULL;
368 heap->root = root->left_child;
369 insert_node (heap, heap->root, root->right_child);
370 }
371 if (heap->walk_pos == root)
372 heap->walk_pos = heap->root;
373 GNUNET_free (root);
374#if EXTRA_CHECKS
375 GNUNET_assert (((heap->size == 0) && (heap->root == NULL)) ||
376 (heap->size == heap->root->tree_size + 1));
377 CHECK (heap->root);
378#endif
379 return ret;
380}
381
382
383/**
384 * Remove the given node 'node' from the tree and update
385 * the 'tree_size' fields accordingly. Preserves the
386 * children of 'node' and does NOT change the overall
387 * 'size' field of the tree.
388 */
389static void
390remove_node (struct GNUNET_CONTAINER_HeapNode *node)
391{
392 struct GNUNET_CONTAINER_HeapNode *ancestor;
393 struct GNUNET_CONTAINER_Heap *heap = node->heap;
394
395 /* update 'size' of the ancestors */
396 ancestor = node;
397 while (NULL != (ancestor = ancestor->parent))
398 ancestor->tree_size--;
399
400 /* update 'size' of node itself */
401 if (node->left_child != NULL)
402 node->tree_size -= (1 + node->left_child->tree_size);
403 if (node->right_child != NULL)
404 node->tree_size -= (1 + node->right_child->tree_size);
405
406 /* unlink 'node' itself and insert children in its place */
407 if (node->parent == NULL)
408 {
409 if (node->left_child != NULL)
410 {
411 heap->root = node->left_child;
412 node->left_child->parent = NULL;
413 if (node->right_child != NULL)
414 {
415 node->right_child->parent = NULL;
416 insert_node (heap, heap->root, node->right_child);
417 }
418 }
419 else
420 {
421 heap->root = node->right_child;
422 if (node->right_child != NULL)
423 node->right_child->parent = NULL;
424 }
425 }
426 else
427 {
428 if (node->parent->left_child == node)
429 node->parent->left_child = NULL;
430 else
431 node->parent->right_child = NULL;
432 if (node->left_child != NULL)
433 {
434 node->left_child->parent = NULL;
435 node->parent->tree_size -= (1 + node->left_child->tree_size);
436 insert_node (heap, node->parent, node->left_child);
437 }
438 if (node->right_child != NULL)
439 {
440 node->right_child->parent = NULL;
441 node->parent->tree_size -= (1 + node->right_child->tree_size);
442 insert_node (heap, node->parent, node->right_child);
443 }
444 }
445 node->parent = NULL;
446 node->left_child = NULL;
447 node->right_child = NULL;
448 GNUNET_assert (node->tree_size == 0);
449 CHECK (heap->root);
450}
451
452
453/**
454 * Removes a node from the heap.
455 *
456 * @param node node to remove
457 * @return element data stored at the node
458 */
459void *
460GNUNET_CONTAINER_heap_remove_node (struct GNUNET_CONTAINER_HeapNode *node)
461{
462 void *ret;
463 struct GNUNET_CONTAINER_Heap *heap;
464
465 heap = node->heap;
466 CHECK (heap->root);
467 if (heap->walk_pos == node)
468 (void) GNUNET_CONTAINER_heap_walk_get_next (heap);
469 remove_node (node);
470 heap->size--;
471 ret = node->element;
472 if (heap->walk_pos == node)
473 heap->walk_pos = NULL;
474 GNUNET_free (node);
475#if EXTRA_CHECKS
476 CHECK (heap->root);
477 GNUNET_assert (((heap->size == 0) && (heap->root == NULL)) ||
478 (heap->size == heap->root->tree_size + 1));
479#endif
480 return ret;
481}
482
483
484void
485GNUNET_CONTAINER_heap_update_cost (struct GNUNET_CONTAINER_HeapNode *node,
486 GNUNET_CONTAINER_HeapCostType new_cost)
487{
488 struct GNUNET_CONTAINER_Heap *heap = node->heap;
489
490 remove_node (node);
491 node->cost = new_cost;
492 if (NULL == heap->root)
493 heap->root = node;
494 else
495 insert_node (heap,
496 heap->root,
497 node);
498}
499
500
501/* end of heap.c */
diff --git a/src/lib/util/container_multihashmap.c b/src/lib/util/container_multihashmap.c
new file mode 100644
index 000000000..d403cd220
--- /dev/null
+++ b/src/lib/util/container_multihashmap.c
@@ -0,0 +1,974 @@
1/*
2 This file is part of GNUnet.
3 Copyright (C) 2008, 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 util/container_multihashmap.c
22 * @brief hash map where the same key may be present multiple times
23 * @author Christian Grothoff
24 */
25
26
27#include "platform.h"
28#include "gnunet_util_lib.h"
29
30#define LOG(kind, ...) \
31 GNUNET_log_from (kind, "util-container-multihashmap", __VA_ARGS__)
32
33/**
34 * Maximum recursion depth for callbacks of
35 * #GNUNET_CONTAINER_multihashmap_get_multiple() themselves s
36 * again calling #GNUNET_CONTAINER_multihashmap_get_multiple().
37 * Should be totally excessive, but if violated we die.
38 */
39#define NEXT_CACHE_SIZE 16
40
41
42/**
43 * An entry in the hash map with the full key.
44 */
45struct BigMapEntry
46{
47 /**
48 * Value of the entry.
49 */
50 void *value;
51
52 /**
53 * If there is a hash collision, we create a linked list.
54 */
55 struct BigMapEntry *next;
56
57 /**
58 * Key for the entry.
59 */
60 struct GNUNET_HashCode key;
61};
62
63
64/**
65 * An entry in the hash map with just a pointer to the key.
66 */
67struct SmallMapEntry
68{
69 /**
70 * Value of the entry.
71 */
72 void *value;
73
74 /**
75 * If there is a hash collision, we create a linked list.
76 */
77 struct SmallMapEntry *next;
78
79 /**
80 * Key for the entry.
81 */
82 const struct GNUNET_HashCode *key;
83};
84
85
86/**
87 * Entry in the map.
88 */
89union MapEntry
90{
91 /**
92 * Variant used if map entries only contain a pointer to the key.
93 */
94 struct SmallMapEntry *sme;
95
96 /**
97 * Variant used if map entries contain the full key.
98 */
99 struct BigMapEntry *bme;
100};
101
102
103/**
104 * Internal representation of the hash map.
105 */
106struct GNUNET_CONTAINER_MultiHashMap
107{
108 /**
109 * All of our buckets.
110 */
111 union MapEntry *map;
112
113 /**
114 * Number of entries in the map.
115 */
116 unsigned int size;
117
118 /**
119 * Length of the "map" array.
120 */
121 unsigned int map_length;
122
123 /**
124 * #GNUNET_NO if the map entries are of type 'struct BigMapEntry',
125 * #GNUNET_YES if the map entries are of type 'struct SmallMapEntry'.
126 */
127 int use_small_entries;
128
129 /**
130 * Counts the destructive modifications (grow, remove)
131 * to the map, so that iterators can check if they are still valid.
132 */
133 unsigned int modification_counter;
134
135 /**
136 * Map entries indicating iteration positions currently
137 * in use by #GNUNET_CONTAINER_multihashmap_get_multiple().
138 * Only used up to @e next_cache_off.
139 */
140 union MapEntry next_cache[NEXT_CACHE_SIZE];
141
142 /**
143 * Offset of @e next_cache entries in use, must be smaller
144 * than #NEXT_CACHE_SIZE.
145 */
146 unsigned int next_cache_off;
147};
148
149
150/**
151 * Cursor into a multihashmap.
152 * Allows to enumerate elements asynchronously.
153 */
154struct GNUNET_CONTAINER_MultiHashMapIterator
155{
156 /**
157 * Position in the bucket @e idx
158 */
159 union MapEntry me;
160
161 /**
162 * Current bucket index.
163 */
164 unsigned int idx;
165
166 /**
167 * Modification counter as observed on the map when the iterator
168 * was created.
169 */
170 unsigned int modification_counter;
171
172 /**
173 * Map that we are iterating over.
174 */
175 const struct GNUNET_CONTAINER_MultiHashMap *map;
176};
177
178
179struct GNUNET_CONTAINER_MultiHashMap *
180GNUNET_CONTAINER_multihashmap_create (unsigned int len, int do_not_copy_keys)
181{
182 struct GNUNET_CONTAINER_MultiHashMap *hm;
183
184 GNUNET_assert (len > 0);
185 hm = GNUNET_new (struct GNUNET_CONTAINER_MultiHashMap);
186 if (len * sizeof(union MapEntry) > GNUNET_MAX_MALLOC_CHECKED)
187 {
188 size_t s;
189 /* application *explicitly* requested very large map, hopefully
190 it checks the return value... */
191 s = len * sizeof(union MapEntry);
192 if ((s / sizeof(union MapEntry)) != len)
193 return NULL; /* integer overflow on multiplication */
194 if (NULL == (hm->map = GNUNET_malloc_large (s)))
195 {
196 /* out of memory */
197 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
198 "Out of memory allocating large hash map (%u entries)\n",
199 len);
200 GNUNET_free (hm);
201 return NULL;
202 }
203 }
204 else
205 {
206 hm->map = GNUNET_new_array (len, union MapEntry);
207 }
208 hm->map_length = len;
209 hm->use_small_entries = do_not_copy_keys;
210 return hm;
211}
212
213
214void
215GNUNET_CONTAINER_multihashmap_destroy (
216 struct GNUNET_CONTAINER_MultiHashMap *map)
217{
218 GNUNET_assert (0 == map->next_cache_off);
219 for (unsigned int i = 0; i < map->map_length; i++)
220 {
221 union MapEntry me;
222
223 me = map->map[i];
224 if (map->use_small_entries)
225 {
226 struct SmallMapEntry *sme;
227 struct SmallMapEntry *nxt;
228
229 nxt = me.sme;
230 while (NULL != (sme = nxt))
231 {
232 nxt = sme->next;
233 GNUNET_free (sme);
234 }
235 me.sme = NULL;
236 }
237 else
238 {
239 struct BigMapEntry *bme;
240 struct BigMapEntry *nxt;
241
242 nxt = me.bme;
243 while (NULL != (bme = nxt))
244 {
245 nxt = bme->next;
246 GNUNET_free (bme);
247 }
248 me.bme = NULL;
249 }
250 }
251 GNUNET_free (map->map);
252 GNUNET_free (map);
253}
254
255
256/**
257 * Compute the index of the bucket for the given key.
258 *
259 * @param map hash map for which to compute the index
260 * @param key what key should the index be computed for
261 * @return offset into the "map" array of "map"
262 */
263static unsigned int
264idx_of (const struct GNUNET_CONTAINER_MultiHashMap *map,
265 const struct GNUNET_HashCode *key)
266{
267 GNUNET_assert (map != NULL);
268 return (*(unsigned int *) key) % map->map_length;
269}
270
271
272unsigned int
273GNUNET_CONTAINER_multihashmap_size (
274 const struct GNUNET_CONTAINER_MultiHashMap *map)
275{
276 return map->size;
277}
278
279
280void *
281GNUNET_CONTAINER_multihashmap_get (
282 const struct GNUNET_CONTAINER_MultiHashMap *map,
283 const struct GNUNET_HashCode *key)
284{
285 union MapEntry me;
286
287 me = map->map[idx_of (map, key)];
288 if (map->use_small_entries)
289 {
290 struct SmallMapEntry *sme;
291
292 for (sme = me.sme; NULL != sme; sme = sme->next)
293 if (0 == GNUNET_memcmp (key, sme->key))
294 return sme->value;
295 }
296 else
297 {
298 struct BigMapEntry *bme;
299
300 for (bme = me.bme; NULL != bme; bme = bme->next)
301 if (0 == GNUNET_memcmp (key, &bme->key))
302 return bme->value;
303 }
304 return NULL;
305}
306
307
308int
309GNUNET_CONTAINER_multihashmap_iterate (
310 struct GNUNET_CONTAINER_MultiHashMap *map,
311 GNUNET_CONTAINER_MultiHashMapIteratorCallback it,
312 void *it_cls)
313{
314 int count;
315 union MapEntry me;
316 union MapEntry *ce;
317 struct GNUNET_HashCode kc;
318
319 GNUNET_assert (NULL != map);
320 ce = &map->next_cache[map->next_cache_off];
321 GNUNET_assert (++map->next_cache_off < NEXT_CACHE_SIZE);
322 count = 0;
323 for (unsigned i = 0; i < map->map_length; i++)
324 {
325 me = map->map[i];
326 if (map->use_small_entries)
327 {
328 struct SmallMapEntry *sme;
329
330 ce->sme = me.sme;
331 while (NULL != (sme = ce->sme))
332 {
333 ce->sme = sme->next;
334 if (NULL != it)
335 {
336 if (GNUNET_OK != it (it_cls, sme->key, sme->value))
337 {
338 GNUNET_assert (--map->next_cache_off < NEXT_CACHE_SIZE);
339 return GNUNET_SYSERR;
340 }
341 }
342 count++;
343 }
344 }
345 else
346 {
347 struct BigMapEntry *bme;
348
349 ce->bme = me.bme;
350 while (NULL != (bme = ce->bme))
351 {
352 ce->bme = bme->next;
353 if (NULL != it)
354 {
355 kc = bme->key;
356 if (GNUNET_OK != it (it_cls, &kc, bme->value))
357 {
358 GNUNET_assert (--map->next_cache_off < NEXT_CACHE_SIZE);
359 return GNUNET_SYSERR;
360 }
361 }
362 count++;
363 }
364 }
365 }
366 GNUNET_assert (--map->next_cache_off < NEXT_CACHE_SIZE);
367 return count;
368}
369
370
371/**
372 * We are about to free() the @a bme, make sure it is not in
373 * the list of next values for any iterator in the @a map's next_cache.
374 *
375 * @param map the map to check
376 * @param bme the entry that is about to be free'd
377 */
378static void
379update_next_cache_bme (struct GNUNET_CONTAINER_MultiHashMap *map,
380 const struct BigMapEntry *bme)
381{
382 for (unsigned int i = 0; i < map->next_cache_off; i++)
383 if (map->next_cache[i].bme == bme)
384 map->next_cache[i].bme = bme->next;
385}
386
387
388/**
389 * We are about to free() the @a sme, make sure it is not in
390 * the list of next values for any iterator in the @a map's next_cache.
391 *
392 * @param map the map to check
393 * @param sme the entry that is about to be free'd
394 */
395static void
396update_next_cache_sme (struct GNUNET_CONTAINER_MultiHashMap *map,
397 const struct SmallMapEntry *sme)
398{
399 for (unsigned int i = 0; i < map->next_cache_off; i++)
400 if (map->next_cache[i].sme == sme)
401 map->next_cache[i].sme = sme->next;
402}
403
404
405enum GNUNET_GenericReturnValue
406GNUNET_CONTAINER_multihashmap_remove (struct GNUNET_CONTAINER_MultiHashMap *map,
407 const struct GNUNET_HashCode *key,
408 const void *value)
409{
410 union MapEntry me;
411 unsigned int i;
412
413 map->modification_counter++;
414
415 i = idx_of (map, key);
416 me = map->map[i];
417 if (map->use_small_entries)
418 {
419 struct SmallMapEntry *p;
420
421 p = NULL;
422 for (struct SmallMapEntry *sme = me.sme; NULL != sme; sme = sme->next)
423 {
424 if ((0 == GNUNET_memcmp (key, sme->key)) && (value == sme->value))
425 {
426 if (NULL == p)
427 map->map[i].sme = sme->next;
428 else
429 p->next = sme->next;
430 update_next_cache_sme (map, sme);
431 GNUNET_free (sme);
432 map->size--;
433 return GNUNET_YES;
434 }
435 p = sme;
436 }
437 }
438 else
439 {
440 struct BigMapEntry *p;
441
442 p = NULL;
443 for (struct BigMapEntry *bme = me.bme; NULL != bme; bme = bme->next)
444 {
445 if ((0 == GNUNET_memcmp (key, &bme->key)) && (value == bme->value))
446 {
447 if (NULL == p)
448 map->map[i].bme = bme->next;
449 else
450 p->next = bme->next;
451 update_next_cache_bme (map, bme);
452 GNUNET_free (bme);
453 map->size--;
454 return GNUNET_YES;
455 }
456 p = bme;
457 }
458 }
459 return GNUNET_NO;
460}
461
462
463int
464GNUNET_CONTAINER_multihashmap_remove_all (
465 struct GNUNET_CONTAINER_MultiHashMap *map,
466 const struct GNUNET_HashCode *key)
467{
468 union MapEntry me;
469 unsigned int i;
470 int ret;
471
472 map->modification_counter++;
473
474 ret = 0;
475 i = idx_of (map, key);
476 me = map->map[i];
477 if (map->use_small_entries)
478 {
479 struct SmallMapEntry *sme;
480 struct SmallMapEntry *p;
481
482 p = NULL;
483 sme = me.sme;
484 while (NULL != sme)
485 {
486 if (0 == GNUNET_memcmp (key, sme->key))
487 {
488 if (NULL == p)
489 map->map[i].sme = sme->next;
490 else
491 p->next = sme->next;
492 update_next_cache_sme (map, sme);
493 GNUNET_free (sme);
494 map->size--;
495 if (NULL == p)
496 sme = map->map[i].sme;
497 else
498 sme = p->next;
499 ret++;
500 }
501 else
502 {
503 p = sme;
504 sme = sme->next;
505 }
506 }
507 }
508 else
509 {
510 struct BigMapEntry *bme;
511 struct BigMapEntry *p;
512
513 p = NULL;
514 bme = me.bme;
515 while (NULL != bme)
516 {
517 if (0 == GNUNET_memcmp (key, &bme->key))
518 {
519 if (NULL == p)
520 map->map[i].bme = bme->next;
521 else
522 p->next = bme->next;
523 update_next_cache_bme (map, bme);
524 GNUNET_free (bme);
525 map->size--;
526 if (NULL == p)
527 bme = map->map[i].bme;
528 else
529 bme = p->next;
530 ret++;
531 }
532 else
533 {
534 p = bme;
535 bme = bme->next;
536 }
537 }
538 }
539 return ret;
540}
541
542
543/**
544 * Callback used to remove all entries from the map.
545 *
546 * @param cls the `struct GNUNET_CONTAINER_MultiHashMap`
547 * @param key the key
548 * @param value the value
549 * @return #GNUNET_OK (continue to iterate)
550 */
551static int
552remove_all (void *cls, const struct GNUNET_HashCode *key, void *value)
553{
554 struct GNUNET_CONTAINER_MultiHashMap *map = cls;
555
556 GNUNET_assert (GNUNET_YES ==
557 GNUNET_CONTAINER_multihashmap_remove (map, key, value));
558 return GNUNET_OK;
559}
560
561
562/**
563 * @ingroup hashmap
564 * Remove all entries from the map.
565 * Note that the values would not be "freed".
566 *
567 * @param map the map
568 * @return number of values removed
569 */
570unsigned int
571GNUNET_CONTAINER_multihashmap_clear (struct GNUNET_CONTAINER_MultiHashMap *map)
572{
573 unsigned int ret;
574
575 ret = map->size;
576 GNUNET_CONTAINER_multihashmap_iterate (map, &remove_all, map);
577 return ret;
578}
579
580
581enum GNUNET_GenericReturnValue
582GNUNET_CONTAINER_multihashmap_contains (
583 const struct GNUNET_CONTAINER_MultiHashMap *map,
584 const struct GNUNET_HashCode *key)
585{
586 union MapEntry me;
587
588 me = map->map[idx_of (map, key)];
589 if (map->use_small_entries)
590 {
591 struct SmallMapEntry *sme;
592
593 for (sme = me.sme; NULL != sme; sme = sme->next)
594 if (0 == GNUNET_memcmp (key, sme->key))
595 return GNUNET_YES;
596 }
597 else
598 {
599 struct BigMapEntry *bme;
600
601 for (bme = me.bme; NULL != bme; bme = bme->next)
602 if (0 == GNUNET_memcmp (key, &bme->key))
603 return GNUNET_YES;
604 }
605 return GNUNET_NO;
606}
607
608
609enum GNUNET_GenericReturnValue
610GNUNET_CONTAINER_multihashmap_contains_value (
611 const struct GNUNET_CONTAINER_MultiHashMap *map,
612 const struct GNUNET_HashCode *key,
613 const void *value)
614{
615 union MapEntry me;
616
617 me = map->map[idx_of (map, key)];
618 if (map->use_small_entries)
619 {
620 struct SmallMapEntry *sme;
621
622 for (sme = me.sme; NULL != sme; sme = sme->next)
623 if ((0 == GNUNET_memcmp (key, sme->key)) && (sme->value == value))
624 return GNUNET_YES;
625 }
626 else
627 {
628 struct BigMapEntry *bme;
629
630 for (bme = me.bme; NULL != bme; bme = bme->next)
631 if ((0 == GNUNET_memcmp (key, &bme->key)) && (bme->value == value))
632 return GNUNET_YES;
633 }
634 return GNUNET_NO;
635}
636
637
638/**
639 * Grow the given map to a more appropriate size.
640 *
641 * @param map the hash map to grow
642 */
643static void
644grow (struct GNUNET_CONTAINER_MultiHashMap *map)
645{
646 union MapEntry *old_map;
647 union MapEntry *new_map;
648 unsigned int old_len;
649 unsigned int new_len;
650 unsigned int idx;
651
652 old_map = map->map;
653 old_len = map->map_length;
654 GNUNET_assert (0 != old_len);
655 new_len = old_len * 2;
656 if (0 == new_len) /* 2^31 * 2 == 0 */
657 new_len = old_len; /* never use 0 */
658 if (new_len == old_len)
659 return; /* nothing changed */
660 new_map = GNUNET_malloc_large (new_len * sizeof(union MapEntry));
661 if (NULL == new_map)
662 return; /* grow not possible */
663 map->modification_counter++;
664 map->map_length = new_len;
665 map->map = new_map;
666 for (unsigned int i = 0; i < old_len; i++)
667 {
668 if (map->use_small_entries)
669 {
670 struct SmallMapEntry *sme;
671
672 while (NULL != (sme = old_map[i].sme))
673 {
674 old_map[i].sme = sme->next;
675 idx = idx_of (map, sme->key);
676 sme->next = new_map[idx].sme;
677 new_map[idx].sme = sme;
678 }
679 }
680 else
681 {
682 struct BigMapEntry *bme;
683
684 while (NULL != (bme = old_map[i].bme))
685 {
686 old_map[i].bme = bme->next;
687 idx = idx_of (map, &bme->key);
688 bme->next = new_map[idx].bme;
689 new_map[idx].bme = bme;
690 }
691 }
692 }
693 GNUNET_free (old_map);
694}
695
696
697/**
698 * Store a key-value pair in the map.
699 *
700 * @param map the map
701 * @param key key to use
702 * @param value value to use
703 * @param opt options for put
704 * @return #GNUNET_OK on success,
705 * #GNUNET_NO if a value was replaced (with REPLACE)
706 * #GNUNET_SYSERR if UNIQUE_ONLY was the option and the
707 * value already exists
708 */
709enum GNUNET_GenericReturnValue
710GNUNET_CONTAINER_multihashmap_put (struct GNUNET_CONTAINER_MultiHashMap *map,
711 const struct GNUNET_HashCode *key,
712 void *value,
713 enum GNUNET_CONTAINER_MultiHashMapOption opt)
714{
715 union MapEntry me;
716 unsigned int i;
717
718 i = idx_of (map, key);
719 if ((opt != GNUNET_CONTAINER_MULTIHASHMAPOPTION_MULTIPLE) &&
720 (opt != GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_FAST))
721 {
722 me = map->map[i];
723 if (map->use_small_entries)
724 {
725 struct SmallMapEntry *sme;
726
727 for (sme = me.sme; NULL != sme; sme = sme->next)
728 if (0 == GNUNET_memcmp (key, sme->key))
729 {
730 if (opt == GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_ONLY)
731 return GNUNET_SYSERR;
732 sme->value = value;
733 return GNUNET_NO;
734 }
735 }
736 else
737 {
738 struct BigMapEntry *bme;
739
740 for (bme = me.bme; NULL != bme; bme = bme->next)
741 if (0 == GNUNET_memcmp (key, &bme->key))
742 {
743 if (opt == GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_ONLY)
744 return GNUNET_SYSERR;
745 bme->value = value;
746 return GNUNET_NO;
747 }
748 }
749 }
750 if (map->size / 3 >= map->map_length / 4)
751 {
752 grow (map);
753 i = idx_of (map, key);
754 }
755 if (map->use_small_entries)
756 {
757 struct SmallMapEntry *sme;
758
759 sme = GNUNET_new (struct SmallMapEntry);
760 sme->key = key;
761 sme->value = value;
762 sme->next = map->map[i].sme;
763 map->map[i].sme = sme;
764 }
765 else
766 {
767 struct BigMapEntry *bme;
768
769 bme = GNUNET_new (struct BigMapEntry);
770 bme->key = *key;
771 bme->value = value;
772 bme->next = map->map[i].bme;
773 map->map[i].bme = bme;
774 }
775 map->size++;
776 return GNUNET_OK;
777}
778
779
780enum GNUNET_GenericReturnValue
781GNUNET_CONTAINER_multihashmap_get_multiple (
782 struct GNUNET_CONTAINER_MultiHashMap *map,
783 const struct GNUNET_HashCode *key,
784 GNUNET_CONTAINER_MultiHashMapIteratorCallback it,
785 void *it_cls)
786{
787 int count;
788 union MapEntry *me;
789 union MapEntry *ce;
790
791 ce = &map->next_cache[map->next_cache_off];
792 GNUNET_assert (++map->next_cache_off < NEXT_CACHE_SIZE);
793 count = 0;
794 me = &map->map[idx_of (map, key)];
795 if (map->use_small_entries)
796 {
797 struct SmallMapEntry *sme;
798
799 ce->sme = me->sme;
800 while (NULL != (sme = ce->sme))
801 {
802 ce->sme = sme->next;
803 if (0 != GNUNET_memcmp (key, sme->key))
804 continue;
805 if ((NULL != it) && (GNUNET_OK != it (it_cls, key, sme->value)))
806 {
807 GNUNET_assert (--map->next_cache_off < NEXT_CACHE_SIZE);
808 return GNUNET_SYSERR;
809 }
810 count++;
811 }
812 }
813 else
814 {
815 struct BigMapEntry *bme;
816
817 ce->bme = me->bme;
818 while (NULL != (bme = ce->bme))
819 {
820 ce->bme = bme->next;
821 if (0 != GNUNET_memcmp (key, &bme->key))
822 continue;
823 if ((NULL != it) && (GNUNET_OK != it (it_cls, key, bme->value)))
824 {
825 GNUNET_assert (--map->next_cache_off < NEXT_CACHE_SIZE);
826 return GNUNET_SYSERR;
827 }
828 count++;
829 }
830 }
831 GNUNET_assert (--map->next_cache_off < NEXT_CACHE_SIZE);
832 return count;
833}
834
835
836/**
837 * @ingroup hashmap
838 * Call @a it on a random value from the map, or not at all
839 * if the map is empty. Note that this function has linear
840 * complexity (in the size of the map).
841 *
842 * @param map the map
843 * @param it function to call on a random entry
844 * @param it_cls extra argument to @a it
845 * @return the number of key value pairs processed, zero or one.
846 */
847unsigned int
848GNUNET_CONTAINER_multihashmap_get_random (
849 const struct GNUNET_CONTAINER_MultiHashMap *map,
850 GNUNET_CONTAINER_MultiHashMapIteratorCallback it,
851 void *it_cls)
852{
853 unsigned int off;
854 unsigned int idx;
855 union MapEntry me;
856
857 if (0 == map->size)
858 return 0;
859 if (NULL == it)
860 return 1;
861 off = GNUNET_CRYPTO_random_u32 (GNUNET_CRYPTO_QUALITY_NONCE, map->size);
862 for (idx = 0; idx < map->map_length; idx++)
863 {
864 me = map->map[idx];
865 if (map->use_small_entries)
866 {
867 struct SmallMapEntry *sme;
868 struct SmallMapEntry *nxt;
869
870 nxt = me.sme;
871 while (NULL != (sme = nxt))
872 {
873 nxt = sme->next;
874 if (0 == off)
875 {
876 if (GNUNET_OK != it (it_cls, sme->key, sme->value))
877 return GNUNET_SYSERR;
878 return 1;
879 }
880 off--;
881 }
882 }
883 else
884 {
885 struct BigMapEntry *bme;
886 struct BigMapEntry *nxt;
887
888 nxt = me.bme;
889 while (NULL != (bme = nxt))
890 {
891 nxt = bme->next;
892 if (0 == off)
893 {
894 if (GNUNET_OK != it (it_cls, &bme->key, bme->value))
895 return GNUNET_SYSERR;
896 return 1;
897 }
898 off--;
899 }
900 }
901 }
902 GNUNET_break (0);
903 return GNUNET_SYSERR;
904}
905
906
907struct GNUNET_CONTAINER_MultiHashMapIterator *
908GNUNET_CONTAINER_multihashmap_iterator_create (
909 const struct GNUNET_CONTAINER_MultiHashMap *map)
910{
911 struct GNUNET_CONTAINER_MultiHashMapIterator *iter;
912
913 iter = GNUNET_new (struct GNUNET_CONTAINER_MultiHashMapIterator);
914 iter->map = map;
915 iter->modification_counter = map->modification_counter;
916 iter->me = map->map[0];
917 return iter;
918}
919
920
921enum GNUNET_GenericReturnValue
922GNUNET_CONTAINER_multihashmap_iterator_next (
923 struct GNUNET_CONTAINER_MultiHashMapIterator *iter,
924 struct GNUNET_HashCode *key,
925 const void **value)
926{
927 /* make sure the map has not been modified */
928 GNUNET_assert (iter->modification_counter == iter->map->modification_counter);
929
930 /* look for the next entry, skipping empty buckets */
931 while (1)
932 {
933 if (iter->idx >= iter->map->map_length)
934 return GNUNET_NO;
935 if (GNUNET_YES == iter->map->use_small_entries)
936 {
937 if (NULL != iter->me.sme)
938 {
939 if (NULL != key)
940 *key = *iter->me.sme->key;
941 if (NULL != value)
942 *value = iter->me.sme->value;
943 iter->me.sme = iter->me.sme->next;
944 return GNUNET_YES;
945 }
946 }
947 else
948 {
949 if (NULL != iter->me.bme)
950 {
951 if (NULL != key)
952 *key = iter->me.bme->key;
953 if (NULL != value)
954 *value = iter->me.bme->value;
955 iter->me.bme = iter->me.bme->next;
956 return GNUNET_YES;
957 }
958 }
959 iter->idx += 1;
960 if (iter->idx < iter->map->map_length)
961 iter->me = iter->map->map[iter->idx];
962 }
963}
964
965
966void
967GNUNET_CONTAINER_multihashmap_iterator_destroy (
968 struct GNUNET_CONTAINER_MultiHashMapIterator *iter)
969{
970 GNUNET_free (iter);
971}
972
973
974/* end of container_multihashmap.c */
diff --git a/src/lib/util/container_multihashmap32.c b/src/lib/util/container_multihashmap32.c
new file mode 100644
index 000000000..3b4c92426
--- /dev/null
+++ b/src/lib/util/container_multihashmap32.c
@@ -0,0 +1,606 @@
1/*
2 This file is part of GNUnet.
3 Copyright (C) 2008 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/container_multihashmap32.c
22 * @brief a version of hash map implemented in container_multihashmap.c but with
23 * uint32_t as keys
24 * @author Christian Grothoff
25 * @author Sree Harsha Totakura
26 */
27
28
29#include "platform.h"
30#include "gnunet_util_lib.h"
31
32#define LOG(kind, ...) \
33 GNUNET_log_from (kind, "util-container-multihashmap32", __VA_ARGS__)
34
35
36/**
37 * Maximum recursion depth for callbacks of
38 * #GNUNET_CONTAINER_multihashmap_get_multiple() themselves
39 * again calling #GNUNET_CONTAINER_multihashmap_get_multiple().
40 * Should be totally excessive, but if violated we die.
41 */
42#define NEXT_CACHE_SIZE 16
43
44/**
45 * An entry in the hash map.
46 */
47struct MapEntry
48{
49 /**
50 * Key for the entry.
51 */
52 uint32_t key;
53
54 /**
55 * Value of the entry.
56 */
57 void *value;
58
59 /**
60 * If there is a hash collision, we create a linked list.
61 */
62 struct MapEntry *next;
63};
64
65/**
66 * Internal representation of the hash map.
67 */
68struct GNUNET_CONTAINER_MultiHashMap32
69{
70 /**
71 * All of our buckets.
72 */
73 struct MapEntry **map;
74
75 /**
76 * Number of entries in the map.
77 */
78 unsigned int size;
79
80 /**
81 * Length of the @e map array.
82 */
83 unsigned int map_length;
84
85 /**
86 * Counts the destructive modifications (grow, remove)
87 * to the map, so that iterators can check if they are still valid.
88 */
89 unsigned int modification_counter;
90
91 /**
92 * Map entries indicating iteration positions currently
93 * in use by #GNUNET_CONTAINER_multihashmap_get_multiple().
94 * Only used up to @e next_cache_off.
95 */
96 struct MapEntry *next_cache[NEXT_CACHE_SIZE];
97
98 /**
99 * Offset of @e next_cache entries in use, must be smaller
100 * than #NEXT_CACHE_SIZE.
101 */
102 unsigned int next_cache_off;
103};
104
105
106/**
107 * Cursor into a multihashmap.
108 * Allows to enumerate elements asynchronously.
109 */
110struct GNUNET_CONTAINER_MultiHashMap32Iterator
111{
112 /**
113 * Position in the bucket @e idx
114 */
115 struct MapEntry *me;
116
117 /**
118 * Current bucket index.
119 */
120 unsigned int idx;
121
122 /**
123 * Modification counter as observed on the map when the iterator
124 * was created.
125 */
126 unsigned int modification_counter;
127
128 /**
129 * Map that we are iterating over.
130 */
131 const struct GNUNET_CONTAINER_MultiHashMap32 *map;
132};
133
134
135/**
136 * Create a multi hash map.
137 *
138 * @param len initial size (map will grow as needed)
139 * @return NULL on error
140 */
141struct GNUNET_CONTAINER_MultiHashMap32 *
142GNUNET_CONTAINER_multihashmap32_create (unsigned int len)
143{
144 struct GNUNET_CONTAINER_MultiHashMap32 *ret;
145
146 GNUNET_assert (len > 0);
147 ret = GNUNET_new (struct GNUNET_CONTAINER_MultiHashMap32);
148 ret->map = GNUNET_malloc_large (len * sizeof(struct MapEntry *));
149 if (NULL == ret->map)
150 {
151 GNUNET_free (ret);
152 return NULL;
153 }
154 ret->map_length = len;
155 return ret;
156}
157
158
159/**
160 * Destroy a hash map. Will not free any values
161 * stored in the hash map!
162 *
163 * @param map the map
164 */
165void
166GNUNET_CONTAINER_multihashmap32_destroy (
167 struct GNUNET_CONTAINER_MultiHashMap32 *map)
168{
169 struct MapEntry *e;
170
171 for (unsigned int i = 0; i < map->map_length; i++)
172 {
173 while (NULL != (e = map->map[i]))
174 {
175 map->map[i] = e->next;
176 GNUNET_free (e);
177 }
178 }
179 GNUNET_free (map->map);
180 GNUNET_free (map);
181}
182
183
184/**
185 * Compute the index of the bucket for the given key.
186 *
187 * @param m hash map for which to compute the index
188 * @param key what key should the index be computed for
189 * @return offset into the "map" array of "m"
190 */
191static unsigned int
192idx_of (const struct GNUNET_CONTAINER_MultiHashMap32 *m, const uint32_t key)
193{
194 GNUNET_assert (NULL != m);
195 return ((unsigned int) key) % m->map_length;
196}
197
198
199unsigned int
200GNUNET_CONTAINER_multihashmap32_size (
201 const struct GNUNET_CONTAINER_MultiHashMap32 *map)
202{
203 return map->size;
204}
205
206
207void *
208GNUNET_CONTAINER_multihashmap32_get (
209 const struct GNUNET_CONTAINER_MultiHashMap32 *map,
210 uint32_t key)
211{
212 struct MapEntry *e;
213
214 e = map->map[idx_of (map, key)];
215 while (NULL != e)
216 {
217 if (key == e->key)
218 return e->value;
219 e = e->next;
220 }
221 return NULL;
222}
223
224
225int
226GNUNET_CONTAINER_multihashmap32_iterate (
227 struct GNUNET_CONTAINER_MultiHashMap32 *map,
228 GNUNET_CONTAINER_MultiHashMapIterator32Callback it,
229 void *it_cls)
230{
231 int count;
232 struct MapEntry **ce;
233
234 count = 0;
235 GNUNET_assert (NULL != map);
236 ce = &map->next_cache[map->next_cache_off];
237 GNUNET_assert (++map->next_cache_off < NEXT_CACHE_SIZE);
238 for (unsigned int i = 0; i < map->map_length; i++)
239 {
240 struct MapEntry *e;
241
242 *ce = map->map[i];
243 while (NULL != (e = *ce))
244 {
245 *ce = e->next;
246 if (NULL != it)
247 {
248 if (GNUNET_OK != it (it_cls, e->key, e->value))
249 {
250 GNUNET_assert (--map->next_cache_off < NEXT_CACHE_SIZE);
251 return GNUNET_SYSERR;
252 }
253 }
254 count++;
255 }
256 }
257 GNUNET_assert (--map->next_cache_off < NEXT_CACHE_SIZE);
258 return count;
259}
260
261
262/**
263 * We are about to free() the @a me, make sure it is not in
264 * the list of next values for any iterator in the @a map's next_cache.
265 *
266 * @param map the map to check
267 * @param me the entry that is about to be free'd
268 */
269static void
270update_next_cache (struct GNUNET_CONTAINER_MultiHashMap32 *map,
271 const struct MapEntry *me)
272{
273 for (unsigned int i = 0; i < map->next_cache_off; i++)
274 if (map->next_cache[i] == me)
275 map->next_cache[i] = me->next;
276}
277
278
279enum GNUNET_GenericReturnValue
280GNUNET_CONTAINER_multihashmap32_remove (
281 struct GNUNET_CONTAINER_MultiHashMap32 *map,
282 uint32_t key,
283 const void *value)
284{
285 struct MapEntry *e;
286 struct MapEntry *p;
287 unsigned int i;
288
289 map->modification_counter++;
290
291 i = idx_of (map, key);
292 p = NULL;
293 e = map->map[i];
294 while (e != NULL)
295 {
296 if ((key == e->key) && (value == e->value))
297 {
298 if (p == NULL)
299 map->map[i] = e->next;
300 else
301 p->next = e->next;
302 update_next_cache (map, e);
303 GNUNET_free (e);
304 map->size--;
305 return GNUNET_YES;
306 }
307 p = e;
308 e = e->next;
309 }
310 return GNUNET_NO;
311}
312
313
314int
315GNUNET_CONTAINER_multihashmap32_remove_all (
316 struct GNUNET_CONTAINER_MultiHashMap32 *map,
317 uint32_t key)
318{
319 struct MapEntry *e;
320 struct MapEntry *p;
321 unsigned int i;
322 int ret;
323
324 map->modification_counter++;
325
326 ret = 0;
327 i = idx_of (map, key);
328 p = NULL;
329 e = map->map[i];
330 while (e != NULL)
331 {
332 if (key == e->key)
333 {
334 if (p == NULL)
335 map->map[i] = e->next;
336 else
337 p->next = e->next;
338 update_next_cache (map, e);
339 GNUNET_free (e);
340 map->size--;
341 if (p == NULL)
342 e = map->map[i];
343 else
344 e = p->next;
345 ret++;
346 }
347 else
348 {
349 p = e;
350 e = e->next;
351 }
352 }
353 return ret;
354}
355
356
357enum GNUNET_GenericReturnValue
358GNUNET_CONTAINER_multihashmap32_contains (
359 const struct GNUNET_CONTAINER_MultiHashMap32 *map,
360 uint32_t key)
361{
362 struct MapEntry *e;
363
364 e = map->map[idx_of (map, key)];
365 while (e != NULL)
366 {
367 if (key == e->key)
368 return GNUNET_YES;
369 e = e->next;
370 }
371 return GNUNET_NO;
372}
373
374
375enum GNUNET_GenericReturnValue
376GNUNET_CONTAINER_multihashmap32_contains_value (
377 const struct GNUNET_CONTAINER_MultiHashMap32 *map,
378 uint32_t key,
379 const void *value)
380{
381 struct MapEntry *e;
382
383 e = map->map[idx_of (map, key)];
384 while (e != NULL)
385 {
386 if ((key == e->key) && (e->value == value))
387 return GNUNET_YES;
388 e = e->next;
389 }
390 return GNUNET_NO;
391}
392
393
394/**
395 * Grow the given map to a more appropriate size.
396 *
397 * @param map the hash map to grow
398 */
399static void
400grow (struct GNUNET_CONTAINER_MultiHashMap32 *map)
401{
402 struct MapEntry **old_map;
403 struct MapEntry **new_map;
404 struct MapEntry *e;
405 unsigned int old_len;
406 unsigned int new_len;
407 unsigned int idx;
408
409 old_map = map->map;
410 old_len = map->map_length;
411 new_len = old_len * 2;
412 if (0 == new_len) /* 2^31 * 2 == 0 */
413 new_len = old_len; /* never use 0 */
414 if (new_len == old_len)
415 return; /* nothing changed */
416 new_map = GNUNET_malloc_large (new_len * sizeof(struct MapEntry *));
417 if (NULL == new_map)
418 return; /* grow not possible */
419 map->modification_counter++;
420 map->map_length = new_len;
421 map->map = new_map;
422 for (unsigned int i = 0; i < old_len; i++)
423 {
424 while (NULL != (e = old_map[i]))
425 {
426 old_map[i] = e->next;
427 idx = idx_of (map, e->key);
428 e->next = new_map[idx];
429 new_map[idx] = e;
430 }
431 }
432 GNUNET_free (old_map);
433}
434
435
436/**
437 * Store a key-value pair in the map.
438 *
439 * @param map the map
440 * @param key key to use
441 * @param value value to use
442 * @param opt options for put
443 * @return #GNUNET_OK on success,
444 * #GNUNET_NO if a value was replaced (with REPLACE)
445 * #GNUNET_SYSERR if UNIQUE_ONLY was the option and the
446 * value already exists
447 */
448enum GNUNET_GenericReturnValue
449GNUNET_CONTAINER_multihashmap32_put (
450 struct GNUNET_CONTAINER_MultiHashMap32 *map,
451 uint32_t key,
452 void *value,
453 enum GNUNET_CONTAINER_MultiHashMapOption opt)
454{
455 struct MapEntry *e;
456 unsigned int i;
457
458 i = idx_of (map, key);
459 if ((opt != GNUNET_CONTAINER_MULTIHASHMAPOPTION_MULTIPLE) &&
460 (opt != GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_FAST))
461 {
462 e = map->map[i];
463 while (e != NULL)
464 {
465 if (key == e->key)
466 {
467 if (opt == GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_ONLY)
468 return GNUNET_SYSERR;
469 e->value = value;
470 return GNUNET_NO;
471 }
472 e = e->next;
473 }
474 }
475 if (map->size / 3 >= map->map_length / 4)
476 {
477 grow (map);
478 i = idx_of (map, key);
479 }
480 e = GNUNET_new (struct MapEntry);
481 e->key = key;
482 e->value = value;
483 e->next = map->map[i];
484 map->map[i] = e;
485 map->size++;
486 return GNUNET_OK;
487}
488
489
490int
491GNUNET_CONTAINER_multihashmap32_get_multiple (
492 struct GNUNET_CONTAINER_MultiHashMap32 *map,
493 uint32_t key,
494 GNUNET_CONTAINER_MultiHashMapIterator32Callback it,
495 void *it_cls)
496{
497 int count;
498 struct MapEntry *e;
499 struct MapEntry **ce;
500
501 count = 0;
502 ce = &map->next_cache[map->next_cache_off];
503 GNUNET_assert (++map->next_cache_off < NEXT_CACHE_SIZE);
504
505 *ce = map->map[idx_of (map, key)];
506 while (NULL != (e = *ce))
507 {
508 *ce = e->next;
509 if (key != e->key)
510 continue;
511 if ((NULL != it) && (GNUNET_OK != it (it_cls, key, e->value)))
512 {
513 GNUNET_assert (--map->next_cache_off < NEXT_CACHE_SIZE);
514 return GNUNET_SYSERR;
515 }
516 count++;
517 }
518 GNUNET_assert (--map->next_cache_off < NEXT_CACHE_SIZE);
519 return count;
520}
521
522
523/**
524 * Create an iterator for a multihashmap.
525 * The iterator can be used to retrieve all the elements in the multihashmap
526 * one by one, without having to handle all elements at once (in contrast to
527 * GNUNET_CONTAINER_multihashmap_iterate()). Note that the iterator can not be
528 * used anymore if elements have been removed from 'map' after the creation of
529 * the iterator, or 'map' has been destroyed. Adding elements to 'map' may
530 * result in skipped or repeated elements.
531 *
532 * @param map the map to create an iterator for
533 * @return an iterator over the given multihashmap @a map
534 */
535struct GNUNET_CONTAINER_MultiHashMap32Iterator *
536GNUNET_CONTAINER_multihashmap32_iterator_create (
537 const struct GNUNET_CONTAINER_MultiHashMap32 *map)
538{
539 struct GNUNET_CONTAINER_MultiHashMap32Iterator *iter;
540
541 iter = GNUNET_new (struct GNUNET_CONTAINER_MultiHashMap32Iterator);
542 iter->map = map;
543 iter->modification_counter = map->modification_counter;
544 iter->me = map->map[0];
545 return iter;
546}
547
548
549/**
550 * Retrieve the next element from the hash map at the iterator's position.
551 * If there are no elements left, GNUNET_NO is returned, and 'key' and 'value'
552 * are not modified.
553 * This operation is only allowed if no elements have been removed from the
554 * multihashmap since the creation of 'iter', and the map has not been destroyed.
555 * Adding elements may result in repeating or skipping elements.
556 *
557 * @param iter the iterator to get the next element from
558 * @param key pointer to store the key in, can be NULL
559 * @param value pointer to store the value in, can be NULL
560 * @return #GNUNET_YES we returned an element,
561 * #GNUNET_NO if we are out of elements
562 */
563enum GNUNET_GenericReturnValue
564GNUNET_CONTAINER_multihashmap32_iterator_next (
565 struct GNUNET_CONTAINER_MultiHashMap32Iterator *iter,
566 uint32_t *key,
567 const void **value)
568{
569 /* make sure the map has not been modified */
570 GNUNET_assert (iter->modification_counter == iter->map->modification_counter);
571
572 /* look for the next entry, skipping empty buckets */
573 while (1)
574 {
575 if (iter->idx >= iter->map->map_length)
576 return GNUNET_NO;
577 if (NULL != iter->me)
578 {
579 if (NULL != key)
580 *key = iter->me->key;
581 if (NULL != value)
582 *value = iter->me->value;
583 iter->me = iter->me->next;
584 return GNUNET_YES;
585 }
586 iter->idx += 1;
587 if (iter->idx < iter->map->map_length)
588 iter->me = iter->map->map[iter->idx];
589 }
590}
591
592
593/**
594 * Destroy a multihashmap iterator.
595 *
596 * @param iter the iterator to destroy
597 */
598void
599GNUNET_CONTAINER_multihashmap32_iterator_destroy (
600 struct GNUNET_CONTAINER_MultiHashMapIterator *iter)
601{
602 GNUNET_free (iter);
603}
604
605
606/* end of container_multihashmap.c */
diff --git a/src/lib/util/container_multipeermap.c b/src/lib/util/container_multipeermap.c
new file mode 100644
index 000000000..7ccfb62c8
--- /dev/null
+++ b/src/lib/util/container_multipeermap.c
@@ -0,0 +1,895 @@
1/*
2 This file is part of GNUnet.
3 Copyright (C) 2008, 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 util/container_multipeermap.c
22 * @brief hash map where the same key may be present multiple times
23 * @author Christian Grothoff
24 */
25
26
27#include "platform.h"
28#include "gnunet_util_lib.h"
29
30#define LOG(kind, ...) \
31 GNUNET_log_from (kind, "util-container-multipeermap", __VA_ARGS__)
32
33/**
34 * Maximum recursion depth for callbacks of
35 * #GNUNET_CONTAINER_multihashmap_get_multiple() themselves s
36 * again calling #GNUNET_CONTAINER_multihashmap_get_multiple().
37 * Should be totally excessive, but if violated we die.
38 */
39#define NEXT_CACHE_SIZE 16
40
41/**
42 * An entry in the hash map with the full key.
43 */
44struct BigMapEntry
45{
46 /**
47 * Value of the entry.
48 */
49 void *value;
50
51 /**
52 * If there is a hash collision, we create a linked list.
53 */
54 struct BigMapEntry *next;
55
56 /**
57 * Key for the entry.
58 */
59 struct GNUNET_PeerIdentity key;
60};
61
62
63/**
64 * An entry in the hash map with just a pointer to the key.
65 */
66struct SmallMapEntry
67{
68 /**
69 * Value of the entry.
70 */
71 void *value;
72
73 /**
74 * If there is a hash collision, we create a linked list.
75 */
76 struct SmallMapEntry *next;
77
78 /**
79 * Key for the entry.
80 */
81 const struct GNUNET_PeerIdentity *key;
82};
83
84
85/**
86 * Entry in the map.
87 */
88union MapEntry
89{
90 /**
91 * Variant used if map entries only contain a pointer to the key.
92 */
93 struct SmallMapEntry *sme;
94
95 /**
96 * Variant used if map entries contain the full key.
97 */
98 struct BigMapEntry *bme;
99};
100
101
102/**
103 * Internal representation of the hash map.
104 */
105struct GNUNET_CONTAINER_MultiPeerMap
106{
107 /**
108 * All of our buckets.
109 */
110 union MapEntry *map;
111
112 /**
113 * Number of entries in the map.
114 */
115 unsigned int size;
116
117 /**
118 * Length of the "map" array.
119 */
120 unsigned int map_length;
121
122 /**
123 * #GNUNET_NO if the map entries are of type 'struct BigMapEntry',
124 * #GNUNET_YES if the map entries are of type 'struct SmallMapEntry'.
125 */
126 int use_small_entries;
127
128 /**
129 * Counts the destructive modifications (grow, remove)
130 * to the map, so that iterators can check if they are still valid.
131 */
132 unsigned int modification_counter;
133
134 /**
135 * Map entries indicating iteration positions currently
136 * in use by #GNUNET_CONTAINER_multihashmap_get_multiple().
137 * Only used up to @e next_cache_off.
138 */
139 union MapEntry next_cache[NEXT_CACHE_SIZE];
140
141 /**
142 * Offset of @e next_cache entries in use, must be smaller
143 * than #NEXT_CACHE_SIZE.
144 */
145 unsigned int next_cache_off;
146};
147
148
149/**
150 * Cursor into a multipeermap.
151 * Allows to enumerate elements asynchronously.
152 */
153struct GNUNET_CONTAINER_MultiPeerMapIterator
154{
155 /**
156 * Position in the bucket 'idx'
157 */
158 union MapEntry me;
159
160 /**
161 * Current bucket index.
162 */
163 unsigned int idx;
164
165 /**
166 * Modification counter as observed on the map when the iterator
167 * was created.
168 */
169 unsigned int modification_counter;
170
171 /**
172 * Map that we are iterating over.
173 */
174 const struct GNUNET_CONTAINER_MultiPeerMap *map;
175};
176
177
178struct GNUNET_CONTAINER_MultiPeerMap *
179GNUNET_CONTAINER_multipeermap_create (unsigned int len,
180 int do_not_copy_keys)
181{
182 struct GNUNET_CONTAINER_MultiPeerMap *map;
183
184 GNUNET_assert (len > 0);
185 map = GNUNET_new (struct GNUNET_CONTAINER_MultiPeerMap);
186 map->map = GNUNET_malloc_large (len * sizeof(union MapEntry));
187 if (NULL == map->map)
188 {
189 GNUNET_free (map);
190 return NULL;
191 }
192 map->map_length = len;
193 map->use_small_entries = do_not_copy_keys;
194 return map;
195}
196
197
198void
199GNUNET_CONTAINER_multipeermap_destroy (
200 struct GNUNET_CONTAINER_MultiPeerMap *map)
201{
202 GNUNET_assert (0 == map->next_cache_off);
203 for (unsigned int i = 0; i < map->map_length; i++)
204 {
205 union MapEntry me;
206
207 me = map->map[i];
208 if (map->use_small_entries)
209 {
210 struct SmallMapEntry *sme;
211 struct SmallMapEntry *nxt;
212
213 nxt = me.sme;
214 while (NULL != (sme = nxt))
215 {
216 nxt = sme->next;
217 GNUNET_free (sme);
218 }
219 me.sme = NULL;
220 }
221 else
222 {
223 struct BigMapEntry *bme;
224 struct BigMapEntry *nxt;
225
226 nxt = me.bme;
227 while (NULL != (bme = nxt))
228 {
229 nxt = bme->next;
230 GNUNET_free (bme);
231 }
232 me.bme = NULL;
233 }
234 }
235 GNUNET_free (map->map);
236 GNUNET_free (map);
237}
238
239
240/**
241 * Compute the index of the bucket for the given key.
242 *
243 * @param map hash map for which to compute the index
244 * @param key what key should the index be computed for
245 * @return offset into the "map" array of "map"
246 */
247static unsigned int
248idx_of (const struct GNUNET_CONTAINER_MultiPeerMap *map,
249 const struct GNUNET_PeerIdentity *key)
250{
251 unsigned int kx;
252
253 GNUNET_assert (NULL != map);
254 GNUNET_memcpy (&kx, key, sizeof(kx));
255 return kx % map->map_length;
256}
257
258
259unsigned int
260GNUNET_CONTAINER_multipeermap_size (
261 const struct GNUNET_CONTAINER_MultiPeerMap *map)
262{
263 return map->size;
264}
265
266
267void *
268GNUNET_CONTAINER_multipeermap_get (
269 const struct GNUNET_CONTAINER_MultiPeerMap *map,
270 const struct GNUNET_PeerIdentity *key)
271{
272 union MapEntry me;
273
274 me = map->map[idx_of (map, key)];
275 if (map->use_small_entries)
276 {
277 for (struct SmallMapEntry *sme = me.sme; NULL != sme; sme = sme->next)
278 if (0 == GNUNET_memcmp (key, sme->key))
279 return sme->value;
280 }
281 else
282 {
283 for (struct BigMapEntry *bme = me.bme; NULL != bme; bme = bme->next)
284 if (0 == GNUNET_memcmp (key, &bme->key))
285 return bme->value;
286 }
287 return NULL;
288}
289
290
291int
292GNUNET_CONTAINER_multipeermap_iterate (
293 struct GNUNET_CONTAINER_MultiPeerMap *map,
294 GNUNET_CONTAINER_PeerMapIterator it,
295 void *it_cls)
296{
297 int count;
298 union MapEntry me;
299 union MapEntry *ce;
300 struct GNUNET_PeerIdentity kc;
301
302 count = 0;
303 GNUNET_assert (NULL != map);
304 ce = &map->next_cache[map->next_cache_off];
305 GNUNET_assert (++map->next_cache_off < NEXT_CACHE_SIZE);
306 for (unsigned int i = 0; i < map->map_length; i++)
307 {
308 me = map->map[i];
309 if (map->use_small_entries)
310 {
311 struct SmallMapEntry *sme;
312
313 ce->sme = me.sme;
314 while (NULL != (sme = ce->sme))
315 {
316 ce->sme = sme->next;
317 if (NULL != it)
318 {
319 if (GNUNET_OK != it (it_cls, sme->key, sme->value))
320 {
321 GNUNET_assert (--map->next_cache_off < NEXT_CACHE_SIZE);
322 return GNUNET_SYSERR;
323 }
324 }
325 count++;
326 }
327 }
328 else
329 {
330 struct BigMapEntry *bme;
331
332 ce->bme = me.bme;
333 while (NULL != (bme = ce->bme))
334 {
335 ce->bme = bme->next;
336 if (NULL != it)
337 {
338 kc = bme->key;
339 if (GNUNET_OK != it (it_cls, &kc, bme->value))
340 {
341 GNUNET_assert (--map->next_cache_off < NEXT_CACHE_SIZE);
342 return GNUNET_SYSERR;
343 }
344 }
345 count++;
346 }
347 }
348 }
349 GNUNET_assert (--map->next_cache_off < NEXT_CACHE_SIZE);
350 return count;
351}
352
353
354/**
355 * We are about to free() the @a bme, make sure it is not in
356 * the list of next values for any iterator in the @a map's next_cache.
357 *
358 * @param map the map to check
359 * @param bme the entry that is about to be free'd
360 */
361static void
362update_next_cache_bme (struct GNUNET_CONTAINER_MultiPeerMap *map,
363 const struct BigMapEntry *bme)
364{
365 for (unsigned int i = 0; i < map->next_cache_off; i++)
366 if (map->next_cache[i].bme == bme)
367 map->next_cache[i].bme = bme->next;
368}
369
370
371/**
372 * We are about to free() the @a sme, make sure it is not in
373 * the list of next values for any iterator in the @a map's next_cache.
374 *
375 * @param map the map to check
376 * @param sme the entry that is about to be free'd
377 */
378static void
379update_next_cache_sme (struct GNUNET_CONTAINER_MultiPeerMap *map,
380 const struct SmallMapEntry *sme)
381{
382 for (unsigned int i = 0; i < map->next_cache_off; i++)
383 if (map->next_cache[i].sme == sme)
384 map->next_cache[i].sme = sme->next;
385}
386
387
388enum GNUNET_GenericReturnValue
389GNUNET_CONTAINER_multipeermap_remove (struct GNUNET_CONTAINER_MultiPeerMap *map,
390 const struct GNUNET_PeerIdentity *key,
391 const void *value)
392{
393 union MapEntry me;
394 unsigned int i;
395
396 map->modification_counter++;
397 i = idx_of (map, key);
398 me = map->map[i];
399 if (map->use_small_entries)
400 {
401 struct SmallMapEntry *p = NULL;
402
403 for (struct SmallMapEntry *sme = me.sme; NULL != sme; sme = sme->next)
404 {
405 if ((0 == GNUNET_memcmp (key, sme->key)) && (value == sme->value))
406 {
407 if (NULL == p)
408 map->map[i].sme = sme->next;
409 else
410 p->next = sme->next;
411 update_next_cache_sme (map, sme);
412 GNUNET_free (sme);
413 map->size--;
414 return GNUNET_YES;
415 }
416 p = sme;
417 }
418 }
419 else
420 {
421 struct BigMapEntry *p = NULL;
422
423 for (struct BigMapEntry *bme = me.bme; NULL != bme; bme = bme->next)
424 {
425 if ((0 == GNUNET_memcmp (key, &bme->key)) && (value == bme->value))
426 {
427 if (NULL == p)
428 map->map[i].bme = bme->next;
429 else
430 p->next = bme->next;
431 update_next_cache_bme (map, bme);
432 GNUNET_free (bme);
433 map->size--;
434 return GNUNET_YES;
435 }
436 p = bme;
437 }
438 }
439 return GNUNET_NO;
440}
441
442
443int
444GNUNET_CONTAINER_multipeermap_remove_all (
445 struct GNUNET_CONTAINER_MultiPeerMap *map,
446 const struct GNUNET_PeerIdentity *key)
447{
448 union MapEntry me;
449 unsigned int i;
450 int ret;
451
452 map->modification_counter++;
453
454 ret = 0;
455 i = idx_of (map, key);
456 me = map->map[i];
457 if (map->use_small_entries)
458 {
459 struct SmallMapEntry *sme;
460 struct SmallMapEntry *p;
461
462 p = NULL;
463 sme = me.sme;
464 while (NULL != sme)
465 {
466 if (0 == GNUNET_memcmp (key, sme->key))
467 {
468 if (NULL == p)
469 map->map[i].sme = sme->next;
470 else
471 p->next = sme->next;
472 update_next_cache_sme (map, sme);
473 GNUNET_free (sme);
474 map->size--;
475 if (NULL == p)
476 sme = map->map[i].sme;
477 else
478 sme = p->next;
479 ret++;
480 }
481 else
482 {
483 p = sme;
484 sme = sme->next;
485 }
486 }
487 }
488 else
489 {
490 struct BigMapEntry *bme;
491 struct BigMapEntry *p;
492
493 p = NULL;
494 bme = me.bme;
495 while (NULL != bme)
496 {
497 if (0 == GNUNET_memcmp (key, &bme->key))
498 {
499 if (NULL == p)
500 map->map[i].bme = bme->next;
501 else
502 p->next = bme->next;
503 update_next_cache_bme (map, bme);
504 GNUNET_free (bme);
505 map->size--;
506 if (NULL == p)
507 bme = map->map[i].bme;
508 else
509 bme = p->next;
510 ret++;
511 }
512 else
513 {
514 p = bme;
515 bme = bme->next;
516 }
517 }
518 }
519 return ret;
520}
521
522
523enum GNUNET_GenericReturnValue
524GNUNET_CONTAINER_multipeermap_contains (
525 const struct GNUNET_CONTAINER_MultiPeerMap *map,
526 const struct GNUNET_PeerIdentity *key)
527{
528 union MapEntry me;
529
530 me = map->map[idx_of (map, key)];
531 if (map->use_small_entries)
532 {
533 for (struct SmallMapEntry *sme = me.sme; NULL != sme; sme = sme->next)
534 if (0 == GNUNET_memcmp (key, sme->key))
535 return GNUNET_YES;
536 }
537 else
538 {
539 for (struct BigMapEntry *bme = me.bme; NULL != bme; bme = bme->next)
540 if (0 == GNUNET_memcmp (key, &bme->key))
541 return GNUNET_YES;
542 }
543 return GNUNET_NO;
544}
545
546
547enum GNUNET_GenericReturnValue
548GNUNET_CONTAINER_multipeermap_contains_value (
549 const struct GNUNET_CONTAINER_MultiPeerMap *map,
550 const struct GNUNET_PeerIdentity *key,
551 const void *value)
552{
553 union MapEntry me;
554
555 me = map->map[idx_of (map, key)];
556 if (map->use_small_entries)
557 {
558 for (struct SmallMapEntry *sme = me.sme; NULL != sme; sme = sme->next)
559 if ((0 == GNUNET_memcmp (key, sme->key)) && (sme->value == value))
560 return GNUNET_YES;
561 }
562 else
563 {
564 for (struct BigMapEntry *bme = me.bme; NULL != bme; bme = bme->next)
565 if ((0 == GNUNET_memcmp (key, &bme->key)) && (bme->value == value))
566 return GNUNET_YES;
567 }
568 return GNUNET_NO;
569}
570
571
572/**
573 * Grow the given map to a more appropriate size.
574 *
575 * @param map the hash map to grow
576 */
577static void
578grow (struct GNUNET_CONTAINER_MultiPeerMap *map)
579{
580 union MapEntry *old_map;
581 union MapEntry *new_map;
582 unsigned int old_len;
583 unsigned int new_len;
584 unsigned int idx;
585
586 old_map = map->map;
587 old_len = map->map_length;
588 GNUNET_assert (0 != old_len);
589 new_len = old_len * 2;
590 if (0 == new_len) /* 2^31 * 2 == 0 */
591 new_len = old_len; /* never use 0 */
592 if (new_len == old_len)
593 return; /* nothing changed */
594 new_map = GNUNET_malloc_large (new_len * sizeof(union MapEntry));
595 if (NULL == new_map)
596 return; /* grow not possible */
597 map->modification_counter++;
598 map->map_length = new_len;
599 map->map = new_map;
600 for (unsigned int i = 0; i < old_len; i++)
601 {
602 if (map->use_small_entries)
603 {
604 struct SmallMapEntry *sme;
605
606 while (NULL != (sme = old_map[i].sme))
607 {
608 old_map[i].sme = sme->next;
609 idx = idx_of (map, sme->key);
610 sme->next = new_map[idx].sme;
611 new_map[idx].sme = sme;
612 }
613 }
614 else
615 {
616 struct BigMapEntry *bme;
617
618 while (NULL != (bme = old_map[i].bme))
619 {
620 old_map[i].bme = bme->next;
621 idx = idx_of (map, &bme->key);
622 bme->next = new_map[idx].bme;
623 new_map[idx].bme = bme;
624 }
625 }
626 }
627 GNUNET_free (old_map);
628}
629
630
631int
632GNUNET_CONTAINER_multipeermap_put (struct GNUNET_CONTAINER_MultiPeerMap *map,
633 const struct GNUNET_PeerIdentity *key,
634 void *value,
635 enum GNUNET_CONTAINER_MultiHashMapOption opt)
636{
637 union MapEntry me;
638 unsigned int i;
639
640 i = idx_of (map, key);
641 if ((opt != GNUNET_CONTAINER_MULTIHASHMAPOPTION_MULTIPLE) &&
642 (opt != GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_FAST))
643 {
644 me = map->map[i];
645 if (map->use_small_entries)
646 {
647 struct SmallMapEntry *sme;
648
649 for (sme = me.sme; NULL != sme; sme = sme->next)
650 if (0 == GNUNET_memcmp (key, sme->key))
651 {
652 if (opt == GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_ONLY)
653 return GNUNET_SYSERR;
654 sme->value = value;
655 return GNUNET_NO;
656 }
657 }
658 else
659 {
660 struct BigMapEntry *bme;
661
662 for (bme = me.bme; NULL != bme; bme = bme->next)
663 if (0 == GNUNET_memcmp (key, &bme->key))
664 {
665 if (opt == GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_ONLY)
666 return GNUNET_SYSERR;
667 bme->value = value;
668 return GNUNET_NO;
669 }
670 }
671 }
672 if (map->size / 3 >= map->map_length / 4)
673 {
674 grow (map);
675 i = idx_of (map, key);
676 }
677 if (map->use_small_entries)
678 {
679 struct SmallMapEntry *sme;
680
681 sme = GNUNET_new (struct SmallMapEntry);
682 sme->key = key;
683 sme->value = value;
684 sme->next = map->map[i].sme;
685 map->map[i].sme = sme;
686 }
687 else
688 {
689 struct BigMapEntry *bme;
690
691 bme = GNUNET_new (struct BigMapEntry);
692 bme->key = *key;
693 bme->value = value;
694 bme->next = map->map[i].bme;
695 map->map[i].bme = bme;
696 }
697 map->size++;
698 return GNUNET_OK;
699}
700
701
702int
703GNUNET_CONTAINER_multipeermap_get_multiple (
704 struct GNUNET_CONTAINER_MultiPeerMap *map,
705 const struct GNUNET_PeerIdentity *key,
706 GNUNET_CONTAINER_PeerMapIterator it,
707 void *it_cls)
708{
709 int count;
710 union MapEntry me;
711 union MapEntry *ce;
712
713 ce = &map->next_cache[map->next_cache_off];
714 GNUNET_assert (++map->next_cache_off < NEXT_CACHE_SIZE);
715 count = 0;
716 me = map->map[idx_of (map, key)];
717 if (map->use_small_entries)
718 {
719 struct SmallMapEntry *sme;
720
721 ce->sme = me.sme;
722 while (NULL != (sme = ce->sme))
723 {
724 ce->sme = sme->next;
725 if (0 != GNUNET_memcmp (key, sme->key))
726 continue;
727 if ((NULL != it) && (GNUNET_OK != it (it_cls, key, sme->value)))
728 {
729 GNUNET_assert (--map->next_cache_off < NEXT_CACHE_SIZE);
730 return GNUNET_SYSERR;
731 }
732 count++;
733 }
734 }
735 else
736 {
737 struct BigMapEntry *bme;
738
739 ce->bme = me.bme;
740 while (NULL != (bme = ce->bme))
741 {
742 ce->bme = bme->next;
743 if (0 != GNUNET_memcmp (key, &bme->key))
744 continue;
745 if ((NULL != it) && (GNUNET_OK != it (it_cls, key, bme->value)))
746 {
747 GNUNET_assert (--map->next_cache_off < NEXT_CACHE_SIZE);
748 return GNUNET_SYSERR;
749 }
750 count++;
751 }
752 }
753 GNUNET_assert (--map->next_cache_off < NEXT_CACHE_SIZE);
754 return count;
755}
756
757
758/**
759 * @ingroup hashmap
760 * Call @a it on a random value from the map, or not at all
761 * if the map is empty. Note that this function has linear
762 * complexity (in the size of the map).
763 *
764 * @param map the map
765 * @param it function to call on a random entry
766 * @param it_cls extra argument to @a it
767 * @return the number of key value pairs processed, zero or one.
768 */
769unsigned int
770GNUNET_CONTAINER_multipeermap_get_random (
771 const struct GNUNET_CONTAINER_MultiPeerMap *map,
772 GNUNET_CONTAINER_PeerMapIterator it,
773 void *it_cls)
774{
775 unsigned int off;
776 union MapEntry me;
777
778 if (0 == map->size)
779 return 0;
780 if (NULL == it)
781 return 1;
782 off = GNUNET_CRYPTO_random_u32 (GNUNET_CRYPTO_QUALITY_NONCE, map->size);
783 for (unsigned int idx = 0; idx < map->map_length; idx++)
784 {
785 me = map->map[idx];
786 if (map->use_small_entries)
787 {
788 struct SmallMapEntry *sme;
789 struct SmallMapEntry *nxt;
790
791 nxt = me.sme;
792 while (NULL != (sme = nxt))
793 {
794 nxt = sme->next;
795 if (0 == off)
796 {
797 if (GNUNET_OK != it (it_cls, sme->key, sme->value))
798 return GNUNET_SYSERR;
799 return 1;
800 }
801 off--;
802 }
803 }
804 else
805 {
806 struct BigMapEntry *bme;
807 struct BigMapEntry *nxt;
808
809 nxt = me.bme;
810 while (NULL != (bme = nxt))
811 {
812 nxt = bme->next;
813 if (0 == off)
814 {
815 if (GNUNET_OK != it (it_cls, &bme->key, bme->value))
816 return GNUNET_SYSERR;
817 return 1;
818 }
819 off--;
820 }
821 }
822 }
823 GNUNET_break (0);
824 return GNUNET_SYSERR;
825}
826
827
828struct GNUNET_CONTAINER_MultiPeerMapIterator *
829GNUNET_CONTAINER_multipeermap_iterator_create (
830 const struct GNUNET_CONTAINER_MultiPeerMap *map)
831{
832 struct GNUNET_CONTAINER_MultiPeerMapIterator *iter;
833
834 iter = GNUNET_new (struct GNUNET_CONTAINER_MultiPeerMapIterator);
835 iter->map = map;
836 iter->modification_counter = map->modification_counter;
837 iter->me = map->map[0];
838 return iter;
839}
840
841
842enum GNUNET_GenericReturnValue
843GNUNET_CONTAINER_multipeermap_iterator_next (
844 struct GNUNET_CONTAINER_MultiPeerMapIterator *iter,
845 struct GNUNET_PeerIdentity *key,
846 const void **value)
847{
848 /* make sure the map has not been modified */
849 GNUNET_assert (iter->modification_counter == iter->map->modification_counter);
850
851 /* look for the next entry, skipping empty buckets */
852 while (1)
853 {
854 if (iter->idx >= iter->map->map_length)
855 return GNUNET_NO;
856 if (GNUNET_YES == iter->map->use_small_entries)
857 {
858 if (NULL != iter->me.sme)
859 {
860 if (NULL != key)
861 *key = *iter->me.sme->key;
862 if (NULL != value)
863 *value = iter->me.sme->value;
864 iter->me.sme = iter->me.sme->next;
865 return GNUNET_YES;
866 }
867 }
868 else
869 {
870 if (NULL != iter->me.bme)
871 {
872 if (NULL != key)
873 *key = iter->me.bme->key;
874 if (NULL != value)
875 *value = iter->me.bme->value;
876 iter->me.bme = iter->me.bme->next;
877 return GNUNET_YES;
878 }
879 }
880 iter->idx += 1;
881 if (iter->idx < iter->map->map_length)
882 iter->me = iter->map->map[iter->idx];
883 }
884}
885
886
887void
888GNUNET_CONTAINER_multipeermap_iterator_destroy (
889 struct GNUNET_CONTAINER_MultiPeerMapIterator *iter)
890{
891 GNUNET_free (iter);
892}
893
894
895/* end of container_multipeermap.c */
diff --git a/src/lib/util/container_multishortmap.c b/src/lib/util/container_multishortmap.c
new file mode 100644
index 000000000..c626b55e5
--- /dev/null
+++ b/src/lib/util/container_multishortmap.c
@@ -0,0 +1,904 @@
1/*
2 This file is part of GNUnet.
3 Copyright (C) 2008, 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 util/container_multishortmap.c
22 * @brief hash map where the same key may be present multiple times
23 * @author Christian Grothoff
24 */
25
26
27#include "platform.h"
28#include "gnunet_util_lib.h"
29
30#define LOG(kind, ...) \
31 GNUNET_log_from (kind, "util-container-multishortmap", __VA_ARGS__)
32
33/**
34 * Maximum recursion depth for callbacks of
35 * #GNUNET_CONTAINER_multihashmap_get_multiple() themselves s
36 * again calling #GNUNET_CONTAINER_multihashmap_get_multiple().
37 * Should be totally excessive, but if violated we die.
38 */
39#define NEXT_CACHE_SIZE 16
40
41
42/**
43 * An entry in the hash map with the full key.
44 */
45struct BigMapEntry
46{
47 /**
48 * Value of the entry.
49 */
50 void *value;
51
52 /**
53 * If there is a hash collision, we create a linked list.
54 */
55 struct BigMapEntry *next;
56
57 /**
58 * Key for the entry.
59 */
60 struct GNUNET_ShortHashCode key;
61};
62
63
64/**
65 * An entry in the hash map with just a pointer to the key.
66 */
67struct SmallMapEntry
68{
69 /**
70 * Value of the entry.
71 */
72 void *value;
73
74 /**
75 * If there is a hash collision, we create a linked list.
76 */
77 struct SmallMapEntry *next;
78
79 /**
80 * Key for the entry.
81 */
82 const struct GNUNET_ShortHashCode *key;
83};
84
85
86/**
87 * Entry in the map.
88 */
89union MapEntry
90{
91 /**
92 * Variant used if map entries only contain a pointer to the key.
93 */
94 struct SmallMapEntry *sme;
95
96 /**
97 * Variant used if map entries contain the full key.
98 */
99 struct BigMapEntry *bme;
100};
101
102
103/**
104 * Internal representation of the hash map.
105 */
106struct GNUNET_CONTAINER_MultiShortmap
107{
108 /**
109 * All of our buckets.
110 */
111 union MapEntry *map;
112
113 /**
114 * Number of entries in the map.
115 */
116 unsigned int size;
117
118 /**
119 * Length of the "map" array.
120 */
121 unsigned int map_length;
122
123 /**
124 * #GNUNET_NO if the map entries are of type 'struct BigMapEntry',
125 * #GNUNET_YES if the map entries are of type 'struct SmallMapEntry'.
126 */
127 int use_small_entries;
128
129 /**
130 * Counts the destructive modifications (grow, remove)
131 * to the map, so that iterators can check if they are still valid.
132 */
133 unsigned int modification_counter;
134
135 /**
136 * Map entries indicating iteration positions currently
137 * in use by #GNUNET_CONTAINER_multihashmap_get_multiple().
138 * Only used up to @e next_cache_off.
139 */
140 union MapEntry next_cache[NEXT_CACHE_SIZE];
141
142 /**
143 * Offset of @e next_cache entries in use, must be smaller
144 * than #NEXT_CACHE_SIZE.
145 */
146 unsigned int next_cache_off;
147};
148
149
150/**
151 * Cursor into a multishortmap.
152 * Allows to enumerate elements asynchronously.
153 */
154struct GNUNET_CONTAINER_MultiShortmapIterator
155{
156 /**
157 * Position in the bucket 'idx'
158 */
159 union MapEntry me;
160
161 /**
162 * Current bucket index.
163 */
164 unsigned int idx;
165
166 /**
167 * Modification counter as observed on the map when the iterator
168 * was created.
169 */
170 unsigned int modification_counter;
171
172 /**
173 * Map that we are iterating over.
174 */
175 const struct GNUNET_CONTAINER_MultiShortmap *map;
176};
177
178
179/**
180 * Create a multi hash map.
181 *
182 * @param len initial size (map will grow as needed)
183 * @param do_not_copy_keys #GNUNET_NO is always safe and should be used by default;
184 * #GNUNET_YES means that on 'put', the 'key' does not have
185 * to be copied as the destination of the pointer is
186 * guaranteed to be life as long as the value is stored in
187 * the hashmap. This can significantly reduce memory
188 * consumption, but of course is also a recipe for
189 * heap corruption if the assumption is not true. Only
190 * use this if (1) memory use is important in this case and
191 * (2) you have triple-checked that the invariant holds
192 * @return NULL on error
193 */
194struct GNUNET_CONTAINER_MultiShortmap *
195GNUNET_CONTAINER_multishortmap_create (unsigned int len, int do_not_copy_keys)
196{
197 struct GNUNET_CONTAINER_MultiShortmap *map;
198
199 GNUNET_assert (len > 0);
200 map = GNUNET_new (struct GNUNET_CONTAINER_MultiShortmap);
201 map->map = GNUNET_malloc_large (len * sizeof(union MapEntry));
202 if (NULL == map->map)
203 {
204 GNUNET_free (map);
205 return NULL;
206 }
207 map->map_length = len;
208 map->use_small_entries = do_not_copy_keys;
209 return map;
210}
211
212
213void
214GNUNET_CONTAINER_multishortmap_destroy (
215 struct GNUNET_CONTAINER_MultiShortmap *map)
216{
217 GNUNET_assert (0 == map->next_cache_off);
218 for (unsigned int i = 0; i < map->map_length; i++)
219 {
220 union MapEntry me;
221
222 me = map->map[i];
223 if (map->use_small_entries)
224 {
225 struct SmallMapEntry *sme;
226 struct SmallMapEntry *nxt;
227
228 nxt = me.sme;
229 while (NULL != (sme = nxt))
230 {
231 nxt = sme->next;
232 GNUNET_free (sme);
233 }
234 me.sme = NULL;
235 }
236 else
237 {
238 struct BigMapEntry *bme;
239 struct BigMapEntry *nxt;
240
241 nxt = me.bme;
242 while (NULL != (bme = nxt))
243 {
244 nxt = bme->next;
245 GNUNET_free (bme);
246 }
247 me.bme = NULL;
248 }
249 }
250 GNUNET_free (map->map);
251 GNUNET_free (map);
252}
253
254
255/**
256 * Compute the index of the bucket for the given key.
257 *
258 * @param map hash map for which to compute the index
259 * @param key what key should the index be computed for
260 * @return offset into the "map" array of "map"
261 */
262static unsigned int
263idx_of (const struct GNUNET_CONTAINER_MultiShortmap *map,
264 const struct GNUNET_ShortHashCode *key)
265{
266 unsigned int kx;
267
268 GNUNET_assert (NULL != map);
269 GNUNET_memcpy (&kx, key, sizeof(kx));
270 return kx % map->map_length;
271}
272
273
274unsigned int
275GNUNET_CONTAINER_multishortmap_size (
276 const struct GNUNET_CONTAINER_MultiShortmap *map)
277{
278 return map->size;
279}
280
281
282void *
283GNUNET_CONTAINER_multishortmap_get (
284 const struct GNUNET_CONTAINER_MultiShortmap *map,
285 const struct GNUNET_ShortHashCode *key)
286{
287 union MapEntry me;
288
289 me = map->map[idx_of (map, key)];
290 if (map->use_small_entries)
291 {
292 for (struct SmallMapEntry *sme = me.sme; NULL != sme; sme = sme->next)
293 if (0 == GNUNET_memcmp (key, sme->key))
294 return sme->value;
295 }
296 else
297 {
298 for (struct BigMapEntry *bme = me.bme; NULL != bme; bme = bme->next)
299 if (0 == GNUNET_memcmp (key, &bme->key))
300 return bme->value;
301 }
302 return NULL;
303}
304
305
306int
307GNUNET_CONTAINER_multishortmap_iterate (
308 struct GNUNET_CONTAINER_MultiShortmap *map,
309 GNUNET_CONTAINER_ShortmapIterator it,
310 void *it_cls)
311{
312 int count;
313 union MapEntry me;
314 union MapEntry *ce;
315 struct GNUNET_ShortHashCode kc;
316
317 count = 0;
318 GNUNET_assert (NULL != map);
319 ce = &map->next_cache[map->next_cache_off];
320 GNUNET_assert (++map->next_cache_off < NEXT_CACHE_SIZE);
321 for (unsigned int i = 0; i < map->map_length; i++)
322 {
323 me = map->map[i];
324 if (map->use_small_entries)
325 {
326 struct SmallMapEntry *sme;
327
328 ce->sme = me.sme;
329 while (NULL != (sme = ce->sme))
330 {
331 ce->sme = sme->next;
332 if ((NULL != it) && (GNUNET_OK != it (it_cls, sme->key, sme->value)))
333 {
334 GNUNET_assert (--map->next_cache_off < NEXT_CACHE_SIZE);
335 return GNUNET_SYSERR;
336 }
337 count++;
338 }
339 }
340 else
341 {
342 struct BigMapEntry *bme;
343
344 ce->bme = me.bme;
345 while (NULL != (bme = ce->bme))
346 {
347 ce->bme = bme->next;
348 if (NULL != it)
349 {
350 kc = bme->key;
351 if (GNUNET_OK != it (it_cls, &kc, bme->value))
352 {
353 GNUNET_assert (--map->next_cache_off < NEXT_CACHE_SIZE);
354 return GNUNET_SYSERR;
355 }
356 }
357 count++;
358 }
359 }
360 }
361 GNUNET_assert (--map->next_cache_off < NEXT_CACHE_SIZE);
362 return count;
363}
364
365
366/**
367 * We are about to free() the @a bme, make sure it is not in
368 * the list of next values for any iterator in the @a map's next_cache.
369 *
370 * @param map the map to check
371 * @param bme the entry that is about to be free'd
372 */
373static void
374update_next_cache_bme (struct GNUNET_CONTAINER_MultiShortmap *map,
375 const struct BigMapEntry *bme)
376{
377 for (unsigned int i = 0; i < map->next_cache_off; i++)
378 if (map->next_cache[i].bme == bme)
379 map->next_cache[i].bme = bme->next;
380}
381
382
383/**
384 * We are about to free() the @a sme, make sure it is not in
385 * the list of next values for any iterator in the @a map's next_cache.
386 *
387 * @param map the map to check
388 * @param sme the entry that is about to be free'd
389 */
390static void
391update_next_cache_sme (struct GNUNET_CONTAINER_MultiShortmap *map,
392 const struct SmallMapEntry *sme)
393{
394 for (unsigned int i = 0; i < map->next_cache_off; i++)
395 if (map->next_cache[i].sme == sme)
396 map->next_cache[i].sme = sme->next;
397}
398
399
400int
401GNUNET_CONTAINER_multishortmap_remove (
402 struct GNUNET_CONTAINER_MultiShortmap *map,
403 const struct GNUNET_ShortHashCode *key,
404 const void *value)
405{
406 union MapEntry me;
407 unsigned int i;
408
409 map->modification_counter++;
410 i = idx_of (map, key);
411 me = map->map[i];
412 if (map->use_small_entries)
413 {
414 struct SmallMapEntry *p = NULL;
415
416 for (struct SmallMapEntry *sme = me.sme; NULL != sme; sme = sme->next)
417 {
418 if ((0 == GNUNET_memcmp (key, sme->key)) && (value == sme->value))
419 {
420 if (NULL == p)
421 map->map[i].sme = sme->next;
422 else
423 p->next = sme->next;
424 update_next_cache_sme (map, sme);
425 GNUNET_free (sme);
426 map->size--;
427 return GNUNET_YES;
428 }
429 p = sme;
430 }
431 }
432 else
433 {
434 struct BigMapEntry *p = NULL;
435
436 for (struct BigMapEntry *bme = me.bme; NULL != bme; bme = bme->next)
437 {
438 if ((0 == GNUNET_memcmp (key, &bme->key)) && (value == bme->value))
439 {
440 if (NULL == p)
441 map->map[i].bme = bme->next;
442 else
443 p->next = bme->next;
444 update_next_cache_bme (map, bme);
445 GNUNET_free (bme);
446 map->size--;
447 return GNUNET_YES;
448 }
449 p = bme;
450 }
451 }
452 return GNUNET_NO;
453}
454
455
456int
457GNUNET_CONTAINER_multishortmap_remove_all (
458 struct GNUNET_CONTAINER_MultiShortmap *map,
459 const struct GNUNET_ShortHashCode *key)
460{
461 union MapEntry me;
462 unsigned int i;
463 int ret;
464
465 map->modification_counter++;
466
467 ret = 0;
468 i = idx_of (map, key);
469 me = map->map[i];
470 if (map->use_small_entries)
471 {
472 struct SmallMapEntry *sme;
473 struct SmallMapEntry *p;
474
475 p = NULL;
476 sme = me.sme;
477 while (NULL != sme)
478 {
479 if (0 == GNUNET_memcmp (key, sme->key))
480 {
481 if (NULL == p)
482 map->map[i].sme = sme->next;
483 else
484 p->next = sme->next;
485 update_next_cache_sme (map, sme);
486 GNUNET_free (sme);
487 map->size--;
488 if (NULL == p)
489 sme = map->map[i].sme;
490 else
491 sme = p->next;
492 ret++;
493 }
494 else
495 {
496 p = sme;
497 sme = sme->next;
498 }
499 }
500 }
501 else
502 {
503 struct BigMapEntry *bme;
504 struct BigMapEntry *p;
505
506 p = NULL;
507 bme = me.bme;
508 while (NULL != bme)
509 {
510 if (0 == GNUNET_memcmp (key, &bme->key))
511 {
512 if (NULL == p)
513 map->map[i].bme = bme->next;
514 else
515 p->next = bme->next;
516 update_next_cache_bme (map, bme);
517 GNUNET_free (bme);
518 map->size--;
519 if (NULL == p)
520 bme = map->map[i].bme;
521 else
522 bme = p->next;
523 ret++;
524 }
525 else
526 {
527 p = bme;
528 bme = bme->next;
529 }
530 }
531 }
532 return ret;
533}
534
535
536int
537GNUNET_CONTAINER_multishortmap_contains (
538 const struct GNUNET_CONTAINER_MultiShortmap *map,
539 const struct GNUNET_ShortHashCode *key)
540{
541 union MapEntry me;
542
543 me = map->map[idx_of (map, key)];
544 if (map->use_small_entries)
545 {
546 for (struct SmallMapEntry *sme = me.sme; NULL != sme; sme = sme->next)
547 if (0 == GNUNET_memcmp (key, sme->key))
548 return GNUNET_YES;
549 }
550 else
551 {
552 for (struct BigMapEntry *bme = me.bme; NULL != bme; bme = bme->next)
553 if (0 == GNUNET_memcmp (key, &bme->key))
554 return GNUNET_YES;
555 }
556 return GNUNET_NO;
557}
558
559
560int
561GNUNET_CONTAINER_multishortmap_contains_value (
562 const struct GNUNET_CONTAINER_MultiShortmap *map,
563 const struct GNUNET_ShortHashCode *key,
564 const void *value)
565{
566 union MapEntry me;
567
568 me = map->map[idx_of (map, key)];
569 if (map->use_small_entries)
570 {
571 for (struct SmallMapEntry *sme = me.sme; NULL != sme; sme = sme->next)
572 if ((0 == GNUNET_memcmp (key, sme->key)) && (sme->value == value))
573 return GNUNET_YES;
574 }
575 else
576 {
577 for (struct BigMapEntry *bme = me.bme; NULL != bme; bme = bme->next)
578 if ((0 == GNUNET_memcmp (key, &bme->key)) && (bme->value == value))
579 return GNUNET_YES;
580 }
581 return GNUNET_NO;
582}
583
584
585/**
586 * Grow the given map to a more appropriate size.
587 *
588 * @param map the hash map to grow
589 */
590static void
591grow (struct GNUNET_CONTAINER_MultiShortmap *map)
592{
593 union MapEntry *old_map;
594 union MapEntry *new_map;
595 unsigned int old_len;
596 unsigned int new_len;
597 unsigned int idx;
598
599 old_map = map->map;
600 old_len = map->map_length;
601 new_len = old_len * 2;
602 if (0 == new_len) /* 2^31 * 2 == 0 */
603 new_len = old_len; /* never use 0 */
604 if (new_len == old_len)
605 return; /* nothing changed */
606 new_map = GNUNET_malloc_large (new_len * sizeof(union MapEntry));
607 if (NULL == new_map)
608 return; /* grow not possible */
609 map->modification_counter++;
610 map->map_length = new_len;
611 map->map = new_map;
612 for (unsigned int i = 0; i < old_len; i++)
613 {
614 if (map->use_small_entries)
615 {
616 struct SmallMapEntry *sme;
617
618 while (NULL != (sme = old_map[i].sme))
619 {
620 old_map[i].sme = sme->next;
621 idx = idx_of (map, sme->key);
622 sme->next = new_map[idx].sme;
623 new_map[idx].sme = sme;
624 }
625 }
626 else
627 {
628 struct BigMapEntry *bme;
629
630 while (NULL != (bme = old_map[i].bme))
631 {
632 old_map[i].bme = bme->next;
633 idx = idx_of (map, &bme->key);
634 bme->next = new_map[idx].bme;
635 new_map[idx].bme = bme;
636 }
637 }
638 }
639 GNUNET_free (old_map);
640}
641
642
643enum GNUNET_GenericReturnValue
644GNUNET_CONTAINER_multishortmap_put (
645 struct GNUNET_CONTAINER_MultiShortmap *map,
646 const struct GNUNET_ShortHashCode *key,
647 void *value,
648 enum GNUNET_CONTAINER_MultiHashMapOption opt)
649{
650 union MapEntry me;
651 unsigned int i;
652
653 i = idx_of (map, key);
654 if ((opt != GNUNET_CONTAINER_MULTIHASHMAPOPTION_MULTIPLE) &&
655 (opt != GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_FAST))
656 {
657 me = map->map[i];
658 if (map->use_small_entries)
659 {
660 for (struct SmallMapEntry *sme = me.sme; NULL != sme; sme = sme->next)
661 if (0 == GNUNET_memcmp (key, sme->key))
662 {
663 if (opt == GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_ONLY)
664 return GNUNET_SYSERR;
665 sme->value = value;
666 return GNUNET_NO;
667 }
668 }
669 else
670 {
671 for (struct BigMapEntry *bme = me.bme; NULL != bme; bme = bme->next)
672 if (0 == GNUNET_memcmp (key, &bme->key))
673 {
674 if (opt == GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_ONLY)
675 return GNUNET_SYSERR;
676 bme->value = value;
677 return GNUNET_NO;
678 }
679 }
680 }
681 if (map->size / 3 >= map->map_length / 4)
682 {
683 grow (map);
684 i = idx_of (map, key);
685 }
686 if (map->use_small_entries)
687 {
688 struct SmallMapEntry *sme;
689
690 sme = GNUNET_new (struct SmallMapEntry);
691 sme->key = key;
692 sme->value = value;
693 sme->next = map->map[i].sme;
694 map->map[i].sme = sme;
695 }
696 else
697 {
698 struct BigMapEntry *bme;
699
700 bme = GNUNET_new (struct BigMapEntry);
701 bme->key = *key;
702 bme->value = value;
703 bme->next = map->map[i].bme;
704 map->map[i].bme = bme;
705 }
706 map->size++;
707 return GNUNET_OK;
708}
709
710
711/**
712 * Iterate over all entries in the map that match a particular key.
713 *
714 * @param map the map
715 * @param key key that the entries must correspond to
716 * @param it function to call on each entry
717 * @param it_cls extra argument to @a it
718 * @return the number of key value pairs processed,
719 * #GNUNET_SYSERR if it aborted iteration
720 */
721int
722GNUNET_CONTAINER_multishortmap_get_multiple (
723 struct GNUNET_CONTAINER_MultiShortmap *map,
724 const struct GNUNET_ShortHashCode *key,
725 GNUNET_CONTAINER_ShortmapIterator it,
726 void *it_cls)
727{
728 int count;
729 union MapEntry me;
730 union MapEntry *ce;
731
732 ce = &map->next_cache[map->next_cache_off];
733 GNUNET_assert (++map->next_cache_off < NEXT_CACHE_SIZE);
734 count = 0;
735 me = map->map[idx_of (map, key)];
736 if (map->use_small_entries)
737 {
738 struct SmallMapEntry *sme;
739
740 ce->sme = me.sme;
741 while (NULL != (sme = ce->sme))
742 {
743 ce->sme = sme->next;
744 if (0 != GNUNET_memcmp (key, sme->key))
745 continue;
746 if ((NULL != it) && (GNUNET_OK != it (it_cls, key, sme->value)))
747 {
748 GNUNET_assert (--map->next_cache_off < NEXT_CACHE_SIZE);
749 return GNUNET_SYSERR;
750 }
751 count++;
752 }
753 }
754 else
755 {
756 struct BigMapEntry *bme;
757
758 ce->bme = me.bme;
759 while (NULL != (bme = ce->bme))
760 {
761 ce->bme = bme->next;
762 if (0 != GNUNET_memcmp (key, &bme->key))
763 continue;
764 if ((NULL != it) && (GNUNET_OK != it (it_cls, key, bme->value)))
765 {
766 GNUNET_assert (--map->next_cache_off < NEXT_CACHE_SIZE);
767 return GNUNET_SYSERR;
768 }
769 count++;
770 }
771 }
772 GNUNET_assert (--map->next_cache_off < NEXT_CACHE_SIZE);
773 return count;
774}
775
776
777/**
778 * @ingroup hashmap
779 * Call @a it on a random value from the map, or not at all
780 * if the map is empty. Note that this function has linear
781 * complexity (in the size of the map).
782 *
783 * @param map the map
784 * @param it function to call on a random entry
785 * @param it_cls extra argument to @a it
786 * @return the number of key value pairs processed, zero or one.
787 */
788unsigned int
789GNUNET_CONTAINER_multishortmap_get_random (
790 const struct GNUNET_CONTAINER_MultiShortmap *map,
791 GNUNET_CONTAINER_ShortmapIterator it,
792 void *it_cls)
793{
794 unsigned int off;
795 union MapEntry me;
796
797 if (0 == map->size)
798 return 0;
799 if (NULL == it)
800 return 1;
801 off = GNUNET_CRYPTO_random_u32 (GNUNET_CRYPTO_QUALITY_NONCE, map->size);
802 for (unsigned int idx = 0; idx < map->map_length; idx++)
803 {
804 me = map->map[idx];
805 if (map->use_small_entries)
806 {
807 for (struct SmallMapEntry *sme = me.sme; NULL != sme; sme = sme->next)
808 {
809 if (0 == off)
810 {
811 if (GNUNET_OK != it (it_cls, sme->key, sme->value))
812 return GNUNET_SYSERR;
813 return 1;
814 }
815 off--;
816 }
817 }
818 else
819 {
820 for (struct BigMapEntry *bme = me.bme; NULL != bme; bme = bme->next)
821 {
822 if (0 == off)
823 {
824 if (GNUNET_OK != it (it_cls, &bme->key, bme->value))
825 return GNUNET_SYSERR;
826 return 1;
827 }
828 off--;
829 }
830 }
831 }
832 GNUNET_break (0);
833 return GNUNET_SYSERR;
834}
835
836
837struct GNUNET_CONTAINER_MultiShortmapIterator *
838GNUNET_CONTAINER_multishortmap_iterator_create (
839 const struct GNUNET_CONTAINER_MultiShortmap *map)
840{
841 struct GNUNET_CONTAINER_MultiShortmapIterator *iter;
842
843 iter = GNUNET_new (struct GNUNET_CONTAINER_MultiShortmapIterator);
844 iter->map = map;
845 iter->modification_counter = map->modification_counter;
846 iter->me = map->map[0];
847 return iter;
848}
849
850
851enum GNUNET_GenericReturnValue
852GNUNET_CONTAINER_multishortmap_iterator_next (
853 struct GNUNET_CONTAINER_MultiShortmapIterator *iter,
854 struct GNUNET_ShortHashCode *key,
855 const void **value)
856{
857 /* make sure the map has not been modified */
858 GNUNET_assert (iter->modification_counter == iter->map->modification_counter);
859
860 /* look for the next entry, skipping empty buckets */
861 while (1)
862 {
863 if (iter->idx >= iter->map->map_length)
864 return GNUNET_NO;
865 if (GNUNET_YES == iter->map->use_small_entries)
866 {
867 if (NULL != iter->me.sme)
868 {
869 if (NULL != key)
870 *key = *iter->me.sme->key;
871 if (NULL != value)
872 *value = iter->me.sme->value;
873 iter->me.sme = iter->me.sme->next;
874 return GNUNET_YES;
875 }
876 }
877 else
878 {
879 if (NULL != iter->me.bme)
880 {
881 if (NULL != key)
882 *key = iter->me.bme->key;
883 if (NULL != value)
884 *value = iter->me.bme->value;
885 iter->me.bme = iter->me.bme->next;
886 return GNUNET_YES;
887 }
888 }
889 iter->idx += 1;
890 if (iter->idx < iter->map->map_length)
891 iter->me = iter->map->map[iter->idx];
892 }
893}
894
895
896void
897GNUNET_CONTAINER_multishortmap_iterator_destroy (
898 struct GNUNET_CONTAINER_MultiShortmapIterator *iter)
899{
900 GNUNET_free (iter);
901}
902
903
904/* end of container_multishortmap.c */
diff --git a/src/lib/util/container_multiuuidmap.c b/src/lib/util/container_multiuuidmap.c
new file mode 100644
index 000000000..3e957d47b
--- /dev/null
+++ b/src/lib/util/container_multiuuidmap.c
@@ -0,0 +1,902 @@
1/*
2 This file is part of GNUnet.
3 Copyright (C) 2008, 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 util/container_multiuuidmap.c
22 * @brief hash map for UUIDs where the same key may be present multiple times
23 * @author Christian Grothoff
24 */
25
26
27#include "platform.h"
28#include "gnunet_util_lib.h"
29
30#define LOG(kind, ...) \
31 GNUNET_log_from (kind, "util-container-multiuuidmap", __VA_ARGS__)
32
33/**
34 * Maximum recursion depth for callbacks of
35 * #GNUNET_CONTAINER_multihashmap_get_multiple() themselves s
36 * again calling #GNUNET_CONTAINER_multihashmap_get_multiple().
37 * Should be totally excessive, but if violated we die.
38 */
39#define NEXT_CACHE_SIZE 16
40
41
42/**
43 * An entry in the hash map with the full key.
44 */
45struct BigMapEntry
46{
47 /**
48 * Value of the entry.
49 */
50 void *value;
51
52 /**
53 * If there is a hash collision, we create a linked list.
54 */
55 struct BigMapEntry *next;
56
57 /**
58 * Key for the entry.
59 */
60 struct GNUNET_Uuid key;
61};
62
63
64/**
65 * An entry in the hash map with just a pointer to the key.
66 */
67struct SmallMapEntry
68{
69 /**
70 * Value of the entry.
71 */
72 void *value;
73
74 /**
75 * If there is a hash collision, we create a linked list.
76 */
77 struct SmallMapEntry *next;
78
79 /**
80 * Key for the entry.
81 */
82 const struct GNUNET_Uuid *key;
83};
84
85
86/**
87 * Entry in the map.
88 */
89union MapEntry
90{
91 /**
92 * Variant used if map entries only contain a pointer to the key.
93 */
94 struct SmallMapEntry *sme;
95
96 /**
97 * Variant used if map entries contain the full key.
98 */
99 struct BigMapEntry *bme;
100};
101
102
103/**
104 * Internal representation of the hash map.
105 */
106struct GNUNET_CONTAINER_MultiUuidmap
107{
108 /**
109 * All of our buckets.
110 */
111 union MapEntry *map;
112
113 /**
114 * Number of entries in the map.
115 */
116 unsigned int size;
117
118 /**
119 * Length of the "map" array.
120 */
121 unsigned int map_length;
122
123 /**
124 * #GNUNET_NO if the map entries are of type 'struct BigMapEntry',
125 * #GNUNET_YES if the map entries are of type 'struct SmallMapEntry'.
126 */
127 int use_small_entries;
128
129 /**
130 * Counts the destructive modifications (grow, remove)
131 * to the map, so that iterators can check if they are still valid.
132 */
133 unsigned int modification_counter;
134
135 /**
136 * Map entries indicating iteration positions currently
137 * in use by #GNUNET_CONTAINER_multihashmap_get_multiple().
138 * Only used up to @e next_cache_off.
139 */
140 union MapEntry next_cache[NEXT_CACHE_SIZE];
141
142 /**
143 * Offset of @e next_cache entries in use, must be smaller
144 * than #NEXT_CACHE_SIZE.
145 */
146 unsigned int next_cache_off;
147};
148
149
150/**
151 * Cursor into a multiuuidmap.
152 * Allows to enumerate elements asynchronously.
153 */
154struct GNUNET_CONTAINER_MultiUuidmapIterator
155{
156 /**
157 * Position in the bucket 'idx'
158 */
159 union MapEntry me;
160
161 /**
162 * Current bucket index.
163 */
164 unsigned int idx;
165
166 /**
167 * Modification counter as observed on the map when the iterator
168 * was created.
169 */
170 unsigned int modification_counter;
171
172 /**
173 * Map that we are iterating over.
174 */
175 const struct GNUNET_CONTAINER_MultiUuidmap *map;
176};
177
178
179/**
180 * Create a multi hash map.
181 *
182 * @param len initial size (map will grow as needed)
183 * @param do_not_copy_keys #GNUNET_NO is always safe and should be used by default;
184 * #GNUNET_YES means that on 'put', the 'key' does not have
185 * to be copied as the destination of the pointer is
186 * guaranteed to be life as long as the value is stored in
187 * the hashmap. This can significantly reduce memory
188 * consumption, but of course is also a recipe for
189 * heap corruption if the assumption is not true. Only
190 * use this if (1) memory use is important in this case and
191 * (2) you have triple-checked that the invariant holds
192 * @return NULL on error
193 */
194struct GNUNET_CONTAINER_MultiUuidmap *
195GNUNET_CONTAINER_multiuuidmap_create (unsigned int len, int do_not_copy_keys)
196{
197 struct GNUNET_CONTAINER_MultiUuidmap *map;
198
199 GNUNET_assert (len > 0);
200 map = GNUNET_new (struct GNUNET_CONTAINER_MultiUuidmap);
201 map->map = GNUNET_malloc_large (len * sizeof(union MapEntry));
202 if (NULL == map->map)
203 {
204 GNUNET_free (map);
205 return NULL;
206 }
207 map->map_length = len;
208 map->use_small_entries = do_not_copy_keys;
209 return map;
210}
211
212
213void
214GNUNET_CONTAINER_multiuuidmap_destroy (
215 struct GNUNET_CONTAINER_MultiUuidmap *map)
216{
217 GNUNET_assert (0 == map->next_cache_off);
218 for (unsigned int i = 0; i < map->map_length; i++)
219 {
220 union MapEntry me;
221
222 me = map->map[i];
223 if (map->use_small_entries)
224 {
225 struct SmallMapEntry *sme;
226 struct SmallMapEntry *nxt;
227
228 nxt = me.sme;
229 while (NULL != (sme = nxt))
230 {
231 nxt = sme->next;
232 GNUNET_free (sme);
233 }
234 me.sme = NULL;
235 }
236 else
237 {
238 struct BigMapEntry *bme;
239 struct BigMapEntry *nxt;
240
241 nxt = me.bme;
242 while (NULL != (bme = nxt))
243 {
244 nxt = bme->next;
245 GNUNET_free (bme);
246 }
247 me.bme = NULL;
248 }
249 }
250 GNUNET_free (map->map);
251 GNUNET_free (map);
252}
253
254
255/**
256 * Compute the index of the bucket for the given key.
257 *
258 * @param map hash map for which to compute the index
259 * @param key what key should the index be computed for
260 * @return offset into the "map" array of "map"
261 */
262static unsigned int
263idx_of (const struct GNUNET_CONTAINER_MultiUuidmap *map,
264 const struct GNUNET_Uuid *key)
265{
266 unsigned int kx;
267
268 GNUNET_assert (NULL != map);
269 GNUNET_memcpy (&kx, key, sizeof(kx));
270 return kx % map->map_length;
271}
272
273
274unsigned int
275GNUNET_CONTAINER_multiuuidmap_size (
276 const struct GNUNET_CONTAINER_MultiUuidmap *map)
277{
278 return map->size;
279}
280
281
282void *
283GNUNET_CONTAINER_multiuuidmap_get (
284 const struct GNUNET_CONTAINER_MultiUuidmap *map,
285 const struct GNUNET_Uuid *key)
286{
287 union MapEntry me;
288
289 me = map->map[idx_of (map, key)];
290 if (map->use_small_entries)
291 {
292 for (struct SmallMapEntry *sme = me.sme; NULL != sme; sme = sme->next)
293 if (0 == GNUNET_memcmp (key, sme->key))
294 return sme->value;
295 }
296 else
297 {
298 for (struct BigMapEntry *bme = me.bme; NULL != bme; bme = bme->next)
299 if (0 == GNUNET_memcmp (key, &bme->key))
300 return bme->value;
301 }
302 return NULL;
303}
304
305
306enum GNUNET_GenericReturnValue
307GNUNET_CONTAINER_multiuuidmap_iterate (
308 struct GNUNET_CONTAINER_MultiUuidmap *map,
309 GNUNET_CONTAINER_MultiUuidmapIteratorCallback it,
310 void *it_cls)
311{
312 int count;
313 union MapEntry me;
314 union MapEntry *ce;
315 struct GNUNET_Uuid kc;
316
317 count = 0;
318 GNUNET_assert (NULL != map);
319 ce = &map->next_cache[map->next_cache_off];
320 GNUNET_assert (++map->next_cache_off < NEXT_CACHE_SIZE);
321 for (unsigned int i = 0; i < map->map_length; i++)
322 {
323 me = map->map[i];
324 if (map->use_small_entries)
325 {
326 struct SmallMapEntry *sme;
327
328 ce->sme = me.sme;
329 while (NULL != (sme = ce->sme))
330 {
331 ce->sme = sme->next;
332 if ((NULL != it) && (GNUNET_OK != it (it_cls, sme->key, sme->value)))
333 {
334 GNUNET_assert (--map->next_cache_off < NEXT_CACHE_SIZE);
335 return GNUNET_SYSERR;
336 }
337 count++;
338 }
339 }
340 else
341 {
342 struct BigMapEntry *bme;
343
344 ce->bme = me.bme;
345 while (NULL != (bme = ce->bme))
346 {
347 ce->bme = bme->next;
348 if (NULL != it)
349 {
350 kc = bme->key;
351 if (GNUNET_OK != it (it_cls, &kc, bme->value))
352 {
353 GNUNET_assert (--map->next_cache_off < NEXT_CACHE_SIZE);
354 return GNUNET_SYSERR;
355 }
356 }
357 count++;
358 }
359 }
360 }
361 GNUNET_assert (--map->next_cache_off < NEXT_CACHE_SIZE);
362 return count;
363}
364
365
366/**
367 * We are about to free() the @a bme, make sure it is not in
368 * the list of next values for any iterator in the @a map's next_cache.
369 *
370 * @param map the map to check
371 * @param bme the entry that is about to be free'd
372 */
373static void
374update_next_cache_bme (struct GNUNET_CONTAINER_MultiUuidmap *map,
375 const struct BigMapEntry *bme)
376{
377 for (unsigned int i = 0; i < map->next_cache_off; i++)
378 if (map->next_cache[i].bme == bme)
379 map->next_cache[i].bme = bme->next;
380}
381
382
383/**
384 * We are about to free() the @a sme, make sure it is not in
385 * the list of next values for any iterator in the @a map's next_cache.
386 *
387 * @param map the map to check
388 * @param sme the entry that is about to be free'd
389 */
390static void
391update_next_cache_sme (struct GNUNET_CONTAINER_MultiUuidmap *map,
392 const struct SmallMapEntry *sme)
393{
394 for (unsigned int i = 0; i < map->next_cache_off; i++)
395 if (map->next_cache[i].sme == sme)
396 map->next_cache[i].sme = sme->next;
397}
398
399
400enum GNUNET_GenericReturnValue
401GNUNET_CONTAINER_multiuuidmap_remove (struct GNUNET_CONTAINER_MultiUuidmap *map,
402 const struct GNUNET_Uuid *key,
403 const void *value)
404{
405 union MapEntry me;
406 unsigned int i;
407
408 map->modification_counter++;
409 i = idx_of (map, key);
410 me = map->map[i];
411 if (map->use_small_entries)
412 {
413 struct SmallMapEntry *p = NULL;
414
415 for (struct SmallMapEntry *sme = me.sme; NULL != sme; sme = sme->next)
416 {
417 if ((0 == GNUNET_memcmp (key, sme->key)) && (value == sme->value))
418 {
419 if (NULL == p)
420 map->map[i].sme = sme->next;
421 else
422 p->next = sme->next;
423 update_next_cache_sme (map, sme);
424 GNUNET_free (sme);
425 map->size--;
426 return GNUNET_YES;
427 }
428 p = sme;
429 }
430 }
431 else
432 {
433 struct BigMapEntry *p = NULL;
434
435 for (struct BigMapEntry *bme = me.bme; NULL != bme; bme = bme->next)
436 {
437 if ((0 == GNUNET_memcmp (key, &bme->key)) && (value == bme->value))
438 {
439 if (NULL == p)
440 map->map[i].bme = bme->next;
441 else
442 p->next = bme->next;
443 update_next_cache_bme (map, bme);
444 GNUNET_free (bme);
445 map->size--;
446 return GNUNET_YES;
447 }
448 p = bme;
449 }
450 }
451 return GNUNET_NO;
452}
453
454
455int
456GNUNET_CONTAINER_multiuuidmap_remove_all (
457 struct GNUNET_CONTAINER_MultiUuidmap *map,
458 const struct GNUNET_Uuid *key)
459{
460 union MapEntry me;
461 unsigned int i;
462 int ret;
463
464 map->modification_counter++;
465
466 ret = 0;
467 i = idx_of (map, key);
468 me = map->map[i];
469 if (map->use_small_entries)
470 {
471 struct SmallMapEntry *sme;
472 struct SmallMapEntry *p;
473
474 p = NULL;
475 sme = me.sme;
476 while (NULL != sme)
477 {
478 if (0 == GNUNET_memcmp (key, sme->key))
479 {
480 if (NULL == p)
481 map->map[i].sme = sme->next;
482 else
483 p->next = sme->next;
484 update_next_cache_sme (map, sme);
485 GNUNET_free (sme);
486 map->size--;
487 if (NULL == p)
488 sme = map->map[i].sme;
489 else
490 sme = p->next;
491 ret++;
492 }
493 else
494 {
495 p = sme;
496 sme = sme->next;
497 }
498 }
499 }
500 else
501 {
502 struct BigMapEntry *bme;
503 struct BigMapEntry *p;
504
505 p = NULL;
506 bme = me.bme;
507 while (NULL != bme)
508 {
509 if (0 == GNUNET_memcmp (key, &bme->key))
510 {
511 if (NULL == p)
512 map->map[i].bme = bme->next;
513 else
514 p->next = bme->next;
515 update_next_cache_bme (map, bme);
516 GNUNET_free (bme);
517 map->size--;
518 if (NULL == p)
519 bme = map->map[i].bme;
520 else
521 bme = p->next;
522 ret++;
523 }
524 else
525 {
526 p = bme;
527 bme = bme->next;
528 }
529 }
530 }
531 return ret;
532}
533
534
535enum GNUNET_GenericReturnValue
536GNUNET_CONTAINER_multiuuidmap_contains (
537 const struct GNUNET_CONTAINER_MultiUuidmap *map,
538 const struct GNUNET_Uuid *key)
539{
540 union MapEntry me;
541
542 me = map->map[idx_of (map, key)];
543 if (map->use_small_entries)
544 {
545 for (struct SmallMapEntry *sme = me.sme; NULL != sme; sme = sme->next)
546 if (0 == GNUNET_memcmp (key, sme->key))
547 return GNUNET_YES;
548 }
549 else
550 {
551 for (struct BigMapEntry *bme = me.bme; NULL != bme; bme = bme->next)
552 if (0 == GNUNET_memcmp (key, &bme->key))
553 return GNUNET_YES;
554 }
555 return GNUNET_NO;
556}
557
558
559enum GNUNET_GenericReturnValue
560GNUNET_CONTAINER_multiuuidmap_contains_value (
561 const struct GNUNET_CONTAINER_MultiUuidmap *map,
562 const struct GNUNET_Uuid *key,
563 const void *value)
564{
565 union MapEntry me;
566
567 me = map->map[idx_of (map, key)];
568 if (map->use_small_entries)
569 {
570 for (struct SmallMapEntry *sme = me.sme; NULL != sme; sme = sme->next)
571 if ((0 == GNUNET_memcmp (key, sme->key)) && (sme->value == value))
572 return GNUNET_YES;
573 }
574 else
575 {
576 for (struct BigMapEntry *bme = me.bme; NULL != bme; bme = bme->next)
577 if ((0 == GNUNET_memcmp (key, &bme->key)) && (bme->value == value))
578 return GNUNET_YES;
579 }
580 return GNUNET_NO;
581}
582
583
584/**
585 * Grow the given map to a more appropriate size.
586 *
587 * @param map the hash map to grow
588 */
589static void
590grow (struct GNUNET_CONTAINER_MultiUuidmap *map)
591{
592 union MapEntry *old_map;
593 union MapEntry *new_map;
594 unsigned int old_len;
595 unsigned int new_len;
596 unsigned int idx;
597
598 old_map = map->map;
599 old_len = map->map_length;
600 new_len = old_len * 2;
601 if (0 == new_len) /* 2^31 * 2 == 0 */
602 new_len = old_len; /* never use 0 */
603 if (new_len == old_len)
604 return; /* nothing changed */
605 new_map = GNUNET_malloc_large (new_len * sizeof(union MapEntry));
606 if (NULL == new_map)
607 return; /* grow not possible */
608 map->modification_counter++;
609 map->map_length = new_len;
610 map->map = new_map;
611 for (unsigned int i = 0; i < old_len; i++)
612 {
613 if (map->use_small_entries)
614 {
615 struct SmallMapEntry *sme;
616
617 while (NULL != (sme = old_map[i].sme))
618 {
619 old_map[i].sme = sme->next;
620 idx = idx_of (map, sme->key);
621 sme->next = new_map[idx].sme;
622 new_map[idx].sme = sme;
623 }
624 }
625 else
626 {
627 struct BigMapEntry *bme;
628
629 while (NULL != (bme = old_map[i].bme))
630 {
631 old_map[i].bme = bme->next;
632 idx = idx_of (map, &bme->key);
633 bme->next = new_map[idx].bme;
634 new_map[idx].bme = bme;
635 }
636 }
637 }
638 GNUNET_free (old_map);
639}
640
641
642enum GNUNET_GenericReturnValue
643GNUNET_CONTAINER_multiuuidmap_put (struct GNUNET_CONTAINER_MultiUuidmap *map,
644 const struct GNUNET_Uuid *key,
645 void *value,
646 enum GNUNET_CONTAINER_MultiHashMapOption opt)
647{
648 union MapEntry me;
649 unsigned int i;
650
651 i = idx_of (map, key);
652 if ((opt != GNUNET_CONTAINER_MULTIHASHMAPOPTION_MULTIPLE) &&
653 (opt != GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_FAST))
654 {
655 me = map->map[i];
656 if (map->use_small_entries)
657 {
658 for (struct SmallMapEntry *sme = me.sme; NULL != sme; sme = sme->next)
659 if (0 == GNUNET_memcmp (key, sme->key))
660 {
661 if (opt == GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_ONLY)
662 return GNUNET_SYSERR;
663 sme->value = value;
664 return GNUNET_NO;
665 }
666 }
667 else
668 {
669 for (struct BigMapEntry *bme = me.bme; NULL != bme; bme = bme->next)
670 if (0 == GNUNET_memcmp (key, &bme->key))
671 {
672 if (opt == GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_ONLY)
673 return GNUNET_SYSERR;
674 bme->value = value;
675 return GNUNET_NO;
676 }
677 }
678 }
679 if (map->size / 3 >= map->map_length / 4)
680 {
681 grow (map);
682 i = idx_of (map, key);
683 }
684 if (map->use_small_entries)
685 {
686 struct SmallMapEntry *sme;
687
688 sme = GNUNET_new (struct SmallMapEntry);
689 sme->key = key;
690 sme->value = value;
691 sme->next = map->map[i].sme;
692 map->map[i].sme = sme;
693 }
694 else
695 {
696 struct BigMapEntry *bme;
697
698 bme = GNUNET_new (struct BigMapEntry);
699 bme->key = *key;
700 bme->value = value;
701 bme->next = map->map[i].bme;
702 map->map[i].bme = bme;
703 }
704 map->size++;
705 return GNUNET_OK;
706}
707
708
709/**
710 * Iterate over all entries in the map that match a particular key.
711 *
712 * @param map the map
713 * @param key key that the entries must correspond to
714 * @param it function to call on each entry
715 * @param it_cls extra argument to @a it
716 * @return the number of key value pairs processed,
717 * #GNUNET_SYSERR if it aborted iteration
718 */
719int
720GNUNET_CONTAINER_multiuuidmap_get_multiple (
721 struct GNUNET_CONTAINER_MultiUuidmap *map,
722 const struct GNUNET_Uuid *key,
723 GNUNET_CONTAINER_MultiUuidmapIteratorCallback it,
724 void *it_cls)
725{
726 int count;
727 union MapEntry me;
728 union MapEntry *ce;
729
730 ce = &map->next_cache[map->next_cache_off];
731 GNUNET_assert (++map->next_cache_off < NEXT_CACHE_SIZE);
732 count = 0;
733 me = map->map[idx_of (map, key)];
734 if (map->use_small_entries)
735 {
736 struct SmallMapEntry *sme;
737
738 ce->sme = me.sme;
739 while (NULL != (sme = ce->sme))
740 {
741 ce->sme = sme->next;
742 if (0 != GNUNET_memcmp (key, sme->key))
743 continue;
744 if ((NULL != it) && (GNUNET_OK != it (it_cls, key, sme->value)))
745 {
746 GNUNET_assert (--map->next_cache_off < NEXT_CACHE_SIZE);
747 return GNUNET_SYSERR;
748 }
749 count++;
750 }
751 }
752 else
753 {
754 struct BigMapEntry *bme;
755
756 ce->bme = me.bme;
757 while (NULL != (bme = ce->bme))
758 {
759 ce->bme = bme->next;
760 if (0 != GNUNET_memcmp (key, &bme->key))
761 continue;
762 if ((NULL != it) && (GNUNET_OK != it (it_cls, key, bme->value)))
763 {
764 GNUNET_assert (--map->next_cache_off < NEXT_CACHE_SIZE);
765 return GNUNET_SYSERR;
766 }
767 count++;
768 }
769 }
770 GNUNET_assert (--map->next_cache_off < NEXT_CACHE_SIZE);
771 return count;
772}
773
774
775/**
776 * @ingroup hashmap
777 * Call @a it on a random value from the map, or not at all
778 * if the map is empty. Note that this function has linear
779 * complexity (in the size of the map).
780 *
781 * @param map the map
782 * @param it function to call on a random entry
783 * @param it_cls extra argument to @a it
784 * @return the number of key value pairs processed, zero or one.
785 */
786unsigned int
787GNUNET_CONTAINER_multiuuidmap_get_random (
788 const struct GNUNET_CONTAINER_MultiUuidmap *map,
789 GNUNET_CONTAINER_MultiUuidmapIteratorCallback it,
790 void *it_cls)
791{
792 unsigned int off;
793 union MapEntry me;
794
795 if (0 == map->size)
796 return 0;
797 if (NULL == it)
798 return 1;
799 off = GNUNET_CRYPTO_random_u32 (GNUNET_CRYPTO_QUALITY_NONCE, map->size);
800 for (unsigned int idx = 0; idx < map->map_length; idx++)
801 {
802 me = map->map[idx];
803 if (map->use_small_entries)
804 {
805 for (struct SmallMapEntry *sme = me.sme; NULL != sme; sme = sme->next)
806 {
807 if (0 == off)
808 {
809 if (GNUNET_OK != it (it_cls, sme->key, sme->value))
810 return GNUNET_SYSERR;
811 return 1;
812 }
813 off--;
814 }
815 }
816 else
817 {
818 for (struct BigMapEntry *bme = me.bme; NULL != bme; bme = bme->next)
819 {
820 if (0 == off)
821 {
822 if (GNUNET_OK != it (it_cls, &bme->key, bme->value))
823 return GNUNET_SYSERR;
824 return 1;
825 }
826 off--;
827 }
828 }
829 }
830 GNUNET_break (0);
831 return GNUNET_SYSERR;
832}
833
834
835struct GNUNET_CONTAINER_MultiUuidmapIterator *
836GNUNET_CONTAINER_multiuuidmap_iterator_create (
837 const struct GNUNET_CONTAINER_MultiUuidmap *map)
838{
839 struct GNUNET_CONTAINER_MultiUuidmapIterator *iter;
840
841 iter = GNUNET_new (struct GNUNET_CONTAINER_MultiUuidmapIterator);
842 iter->map = map;
843 iter->modification_counter = map->modification_counter;
844 iter->me = map->map[0];
845 return iter;
846}
847
848
849enum GNUNET_GenericReturnValue
850GNUNET_CONTAINER_multiuuidmap_iterator_next (
851 struct GNUNET_CONTAINER_MultiUuidmapIterator *iter,
852 struct GNUNET_Uuid *key,
853 const void **value)
854{
855 /* make sure the map has not been modified */
856 GNUNET_assert (iter->modification_counter == iter->map->modification_counter);
857
858 /* look for the next entry, skipping empty buckets */
859 while (1)
860 {
861 if (iter->idx >= iter->map->map_length)
862 return GNUNET_NO;
863 if (GNUNET_YES == iter->map->use_small_entries)
864 {
865 if (NULL != iter->me.sme)
866 {
867 if (NULL != key)
868 *key = *iter->me.sme->key;
869 if (NULL != value)
870 *value = iter->me.sme->value;
871 iter->me.sme = iter->me.sme->next;
872 return GNUNET_YES;
873 }
874 }
875 else
876 {
877 if (NULL != iter->me.bme)
878 {
879 if (NULL != key)
880 *key = iter->me.bme->key;
881 if (NULL != value)
882 *value = iter->me.bme->value;
883 iter->me.bme = iter->me.bme->next;
884 return GNUNET_YES;
885 }
886 }
887 iter->idx += 1;
888 if (iter->idx < iter->map->map_length)
889 iter->me = iter->map->map[iter->idx];
890 }
891}
892
893
894void
895GNUNET_CONTAINER_multiuuidmap_iterator_destroy (
896 struct GNUNET_CONTAINER_MultiUuidmapIterator *iter)
897{
898 GNUNET_free (iter);
899}
900
901
902/* end of container_multiuuidmap.c */
diff --git a/src/lib/util/crypto_blind_sign.c b/src/lib/util/crypto_blind_sign.c
new file mode 100644
index 000000000..33a587acd
--- /dev/null
+++ b/src/lib/util/crypto_blind_sign.c
@@ -0,0 +1,712 @@
1/*
2 This file is part of GNUNET
3 Copyright (C) 2021, 2022, 2023 GNUnet e.V.
4
5 GNUNET is free software; you can redistribute it and/or modify it under the
6 terms of the GNU General Public License as published by the Free Software
7 Foundation; either version 3, or (at your option) any later version.
8
9 GNUNET is distributed in the hope that it will be useful, but WITHOUT ANY
10 WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
11 A PARTICULAR PURPOSE. See the GNU General Public License for more details.
12
13 You should have received a copy of the GNU General Public License along with
14 GNUNET; see the file COPYING. If not, see <http://www.gnu.org/licenses/>
15*/
16/**
17 * @file crypto_blind_sign.c
18 * @brief blind signatures (abstraction over RSA or CS)
19 * @author Christian Grothoff
20 */
21#include "platform.h"
22#include "gnunet_util_lib.h"
23
24
25void
26GNUNET_CRYPTO_blinding_input_values_decref (
27 struct GNUNET_CRYPTO_BlindingInputValues *bm)
28{
29 GNUNET_assert (bm->rc > 0);
30 bm->rc--;
31 if (0 != bm->rc)
32 return;
33 switch (bm->cipher)
34 {
35 case GNUNET_CRYPTO_BSA_INVALID:
36 GNUNET_break (0);
37 break;
38 case GNUNET_CRYPTO_BSA_RSA:
39 bm->cipher = GNUNET_CRYPTO_BSA_INVALID;
40 break;
41 case GNUNET_CRYPTO_BSA_CS:
42 bm->cipher = GNUNET_CRYPTO_BSA_INVALID;
43 break;
44 }
45 GNUNET_free (bm);
46}
47
48
49void
50GNUNET_CRYPTO_blind_sign_priv_decref (
51 struct GNUNET_CRYPTO_BlindSignPrivateKey *bsign_priv)
52{
53 GNUNET_assert (bsign_priv->rc > 0);
54 bsign_priv->rc--;
55 if (0 != bsign_priv->rc)
56 return;
57 switch (bsign_priv->cipher)
58 {
59 case GNUNET_CRYPTO_BSA_INVALID:
60 GNUNET_break (0);
61 break;
62 case GNUNET_CRYPTO_BSA_RSA:
63 if (NULL != bsign_priv->details.rsa_private_key)
64 {
65 GNUNET_CRYPTO_rsa_private_key_free (bsign_priv->details.rsa_private_key);
66 bsign_priv->details.rsa_private_key = NULL;
67 }
68 bsign_priv->cipher = GNUNET_CRYPTO_BSA_INVALID;
69 break;
70 case GNUNET_CRYPTO_BSA_CS:
71 bsign_priv->cipher = GNUNET_CRYPTO_BSA_INVALID;
72 break;
73 }
74 GNUNET_free (bsign_priv);
75}
76
77
78void
79GNUNET_CRYPTO_blind_sign_pub_decref (struct
80 GNUNET_CRYPTO_BlindSignPublicKey *bsign_pub)
81{
82 GNUNET_assert (bsign_pub->rc > 0);
83 bsign_pub->rc--;
84 if (0 != bsign_pub->rc)
85 return;
86 switch (bsign_pub->cipher)
87 {
88 case GNUNET_CRYPTO_BSA_INVALID:
89 GNUNET_break (0);
90 break;
91 case GNUNET_CRYPTO_BSA_RSA:
92 if (NULL != bsign_pub->details.rsa_public_key)
93 {
94 GNUNET_CRYPTO_rsa_public_key_free (bsign_pub->details.rsa_public_key);
95 bsign_pub->details.rsa_public_key = NULL;
96 }
97 bsign_pub->cipher = GNUNET_CRYPTO_BSA_INVALID;
98 break;
99 case GNUNET_CRYPTO_BSA_CS:
100 break;
101 }
102 GNUNET_free (bsign_pub);
103}
104
105
106void
107GNUNET_CRYPTO_unblinded_sig_decref (struct
108 GNUNET_CRYPTO_UnblindedSignature *ub_sig)
109{
110 GNUNET_assert (ub_sig->rc > 0);
111 ub_sig->rc--;
112 if (0 != ub_sig->rc)
113 return;
114 switch (ub_sig->cipher)
115 {
116 case GNUNET_CRYPTO_BSA_INVALID:
117 GNUNET_break (0);
118 break;
119 case GNUNET_CRYPTO_BSA_RSA:
120 if (NULL != ub_sig->details.rsa_signature)
121 {
122 GNUNET_CRYPTO_rsa_signature_free (ub_sig->details.rsa_signature);
123 ub_sig->details.rsa_signature = NULL;
124 }
125 ub_sig->cipher = GNUNET_CRYPTO_BSA_INVALID;
126 break;
127 case GNUNET_CRYPTO_BSA_CS:
128 ub_sig->cipher = GNUNET_CRYPTO_BSA_INVALID;
129 break;
130 }
131 GNUNET_free (ub_sig);
132}
133
134
135void
136GNUNET_CRYPTO_blinded_sig_decref (
137 struct GNUNET_CRYPTO_BlindedSignature *blind_sig)
138{
139 GNUNET_assert (blind_sig->rc > 0);
140 blind_sig->rc--;
141 if (0 != blind_sig->rc)
142 return;
143 switch (blind_sig->cipher)
144 {
145 case GNUNET_CRYPTO_BSA_INVALID:
146 GNUNET_break (0);
147 break;
148 case GNUNET_CRYPTO_BSA_RSA:
149 if (NULL != blind_sig->details.blinded_rsa_signature)
150 {
151 GNUNET_CRYPTO_rsa_signature_free (
152 blind_sig->details.blinded_rsa_signature);
153 blind_sig->details.blinded_rsa_signature = NULL;
154 }
155 blind_sig->cipher = GNUNET_CRYPTO_BSA_INVALID;
156 break;
157 case GNUNET_CRYPTO_BSA_CS:
158 blind_sig->cipher = GNUNET_CRYPTO_BSA_INVALID;
159 break;
160 }
161 GNUNET_free (blind_sig);
162}
163
164
165void
166GNUNET_CRYPTO_blinded_message_decref (
167 struct GNUNET_CRYPTO_BlindedMessage *bm)
168{
169 GNUNET_assert (bm->rc > 0);
170 bm->rc--;
171 if (0 != bm->rc)
172 return;
173 switch (bm->cipher)
174 {
175 case GNUNET_CRYPTO_BSA_INVALID:
176 GNUNET_break (0);
177 break;
178 case GNUNET_CRYPTO_BSA_RSA:
179 GNUNET_free (bm->details.rsa_blinded_message.blinded_msg);
180 break;
181 case GNUNET_CRYPTO_BSA_CS:
182 break;
183 }
184 GNUNET_free (bm);
185}
186
187
188struct GNUNET_CRYPTO_BlindedMessage *
189GNUNET_CRYPTO_blinded_message_incref (
190 struct GNUNET_CRYPTO_BlindedMessage *bm)
191{
192 bm->rc++;
193 return bm;
194}
195
196
197struct GNUNET_CRYPTO_BlindingInputValues *
198GNUNET_CRYPTO_blinding_input_values_incref (
199 struct GNUNET_CRYPTO_BlindingInputValues *bm)
200{
201 bm->rc++;
202 return bm;
203}
204
205
206struct GNUNET_CRYPTO_BlindSignPublicKey *
207GNUNET_CRYPTO_bsign_pub_incref (
208 struct GNUNET_CRYPTO_BlindSignPublicKey *bsign_pub)
209{
210 bsign_pub->rc++;
211 return bsign_pub;
212}
213
214
215struct GNUNET_CRYPTO_BlindSignPrivateKey *
216GNUNET_CRYPTO_bsign_priv_incref (
217 struct GNUNET_CRYPTO_BlindSignPrivateKey *bsign_priv)
218{
219 bsign_priv->rc++;
220 return bsign_priv;
221}
222
223
224struct GNUNET_CRYPTO_UnblindedSignature *
225GNUNET_CRYPTO_ub_sig_incref (struct GNUNET_CRYPTO_UnblindedSignature *ub_sig)
226{
227 ub_sig->rc++;
228 return ub_sig;
229}
230
231
232struct GNUNET_CRYPTO_BlindedSignature *
233GNUNET_CRYPTO_blind_sig_incref (
234 struct GNUNET_CRYPTO_BlindedSignature *blind_sig)
235{
236 blind_sig->rc++;
237 return blind_sig;
238}
239
240
241int
242GNUNET_CRYPTO_bsign_pub_cmp (
243 const struct GNUNET_CRYPTO_BlindSignPublicKey *bp1,
244 const struct GNUNET_CRYPTO_BlindSignPublicKey *bp2)
245{
246 if (bp1->cipher != bp2->cipher)
247 return (bp1->cipher > bp2->cipher) ? 1 : -1;
248 switch (bp1->cipher)
249 {
250 case GNUNET_CRYPTO_BSA_INVALID:
251 GNUNET_break (0);
252 return 0;
253 case GNUNET_CRYPTO_BSA_RSA:
254 return GNUNET_memcmp (&bp1->pub_key_hash,
255 &bp2->pub_key_hash);
256 case GNUNET_CRYPTO_BSA_CS:
257 return GNUNET_memcmp (&bp1->pub_key_hash,
258 &bp2->pub_key_hash);
259 }
260 GNUNET_assert (0);
261 return -2;
262}
263
264
265int
266GNUNET_CRYPTO_ub_sig_cmp (
267 const struct GNUNET_CRYPTO_UnblindedSignature *sig1,
268 const struct GNUNET_CRYPTO_UnblindedSignature *sig2)
269{
270 if (sig1->cipher != sig2->cipher)
271 return (sig1->cipher > sig2->cipher) ? 1 : -1;
272 switch (sig1->cipher)
273 {
274 case GNUNET_CRYPTO_BSA_INVALID:
275 GNUNET_break (0);
276 return 0;
277 case GNUNET_CRYPTO_BSA_RSA:
278 return GNUNET_CRYPTO_rsa_signature_cmp (sig1->details.rsa_signature,
279 sig2->details.rsa_signature);
280 case GNUNET_CRYPTO_BSA_CS:
281 return GNUNET_memcmp (&sig1->details.cs_signature,
282 &sig2->details.cs_signature);
283 }
284 GNUNET_assert (0);
285 return -2;
286}
287
288
289int
290GNUNET_CRYPTO_blind_sig_cmp (
291 const struct GNUNET_CRYPTO_BlindedSignature *sig1,
292 const struct GNUNET_CRYPTO_BlindedSignature *sig2)
293{
294 if (sig1->cipher != sig2->cipher)
295 return (sig1->cipher > sig2->cipher) ? 1 : -1;
296 switch (sig1->cipher)
297 {
298 case GNUNET_CRYPTO_BSA_INVALID:
299 GNUNET_break (0);
300 return 0;
301 case GNUNET_CRYPTO_BSA_RSA:
302 return GNUNET_CRYPTO_rsa_signature_cmp (sig1->details.blinded_rsa_signature,
303 sig2->details.blinded_rsa_signature);
304 case GNUNET_CRYPTO_BSA_CS:
305 return GNUNET_memcmp (&sig1->details.blinded_cs_answer,
306 &sig2->details.blinded_cs_answer);
307 }
308 GNUNET_assert (0);
309 return -2;
310}
311
312
313int
314GNUNET_CRYPTO_blinded_message_cmp (
315 const struct GNUNET_CRYPTO_BlindedMessage *bp1,
316 const struct GNUNET_CRYPTO_BlindedMessage *bp2)
317{
318 if (bp1->cipher != bp2->cipher)
319 return (bp1->cipher > bp2->cipher) ? 1 : -1;
320 switch (bp1->cipher)
321 {
322 case GNUNET_CRYPTO_BSA_INVALID:
323 GNUNET_break (0);
324 return 0;
325 case GNUNET_CRYPTO_BSA_RSA:
326 if (bp1->details.rsa_blinded_message.blinded_msg_size !=
327 bp2->details.rsa_blinded_message.blinded_msg_size)
328 return (bp1->details.rsa_blinded_message.blinded_msg_size >
329 bp2->details.rsa_blinded_message.blinded_msg_size) ? 1 : -1;
330 return memcmp (bp1->details.rsa_blinded_message.blinded_msg,
331 bp2->details.rsa_blinded_message.blinded_msg,
332 bp1->details.rsa_blinded_message.blinded_msg_size);
333 case GNUNET_CRYPTO_BSA_CS:
334 return GNUNET_memcmp (&bp1->details.cs_blinded_message,
335 &bp2->details.cs_blinded_message);
336 }
337 GNUNET_assert (0);
338 return -2;
339}
340
341
342enum GNUNET_GenericReturnValue
343GNUNET_CRYPTO_blind_sign_keys_create (
344 struct GNUNET_CRYPTO_BlindSignPrivateKey **bsign_priv,
345 struct GNUNET_CRYPTO_BlindSignPublicKey **bsign_pub,
346 enum GNUNET_CRYPTO_BlindSignatureAlgorithm cipher,
347 ...)
348{
349 enum GNUNET_GenericReturnValue ret;
350 va_list ap;
351
352 va_start (ap,
353 cipher);
354 ret = GNUNET_CRYPTO_blind_sign_keys_create_va (bsign_priv,
355 bsign_pub,
356 cipher,
357 ap);
358 va_end (ap);
359 return ret;
360}
361
362
363enum GNUNET_GenericReturnValue
364GNUNET_CRYPTO_blind_sign_keys_create_va (
365 struct GNUNET_CRYPTO_BlindSignPrivateKey **bsign_priv,
366 struct GNUNET_CRYPTO_BlindSignPublicKey **bsign_pub,
367 enum GNUNET_CRYPTO_BlindSignatureAlgorithm cipher,
368 va_list ap)
369{
370 struct GNUNET_CRYPTO_BlindSignPrivateKey *priv;
371 struct GNUNET_CRYPTO_BlindSignPublicKey *pub;
372
373 priv = GNUNET_new (struct GNUNET_CRYPTO_BlindSignPrivateKey);
374 priv->rc = 1;
375 priv->cipher = cipher;
376 *bsign_priv = priv;
377 pub = GNUNET_new (struct GNUNET_CRYPTO_BlindSignPublicKey);
378 pub->rc = 1;
379 pub->cipher = cipher;
380 *bsign_pub = pub;
381 switch (cipher)
382 {
383 case GNUNET_CRYPTO_BSA_INVALID:
384 GNUNET_break (0);
385 break;
386 case GNUNET_CRYPTO_BSA_RSA:
387 {
388 unsigned int bits;
389
390 bits = va_arg (ap,
391 unsigned int);
392 if (bits < 512)
393 {
394 GNUNET_break (0);
395 break;
396 }
397 priv->details.rsa_private_key
398 = GNUNET_CRYPTO_rsa_private_key_create (bits);
399 }
400 if (NULL == priv->details.rsa_private_key)
401 {
402 GNUNET_break (0);
403 break;
404 }
405 pub->details.rsa_public_key
406 = GNUNET_CRYPTO_rsa_private_key_get_public (
407 priv->details.rsa_private_key);
408 GNUNET_CRYPTO_rsa_public_key_hash (pub->details.rsa_public_key,
409 &pub->pub_key_hash);
410 return GNUNET_OK;
411 case GNUNET_CRYPTO_BSA_CS:
412 GNUNET_CRYPTO_cs_private_key_generate (&priv->details.cs_private_key);
413 GNUNET_CRYPTO_cs_private_key_get_public (
414 &priv->details.cs_private_key,
415 &pub->details.cs_public_key);
416 GNUNET_CRYPTO_hash (&pub->details.cs_public_key,
417 sizeof(pub->details.cs_public_key),
418 &pub->pub_key_hash);
419 return GNUNET_OK;
420 }
421 GNUNET_free (priv);
422 GNUNET_free (pub);
423 *bsign_priv = NULL;
424 *bsign_pub = NULL;
425 return GNUNET_SYSERR;
426}
427
428
429struct GNUNET_CRYPTO_BlindingInputValues *
430GNUNET_CRYPTO_get_blinding_input_values (
431 const struct GNUNET_CRYPTO_BlindSignPrivateKey *bsign_priv,
432 const union GNUNET_CRYPTO_BlindSessionNonce *nonce,
433 const char *salt)
434{
435 struct GNUNET_CRYPTO_BlindingInputValues *biv;
436
437 biv = GNUNET_new (struct GNUNET_CRYPTO_BlindingInputValues);
438 biv->cipher = bsign_priv->cipher;
439 biv->rc = 1;
440 switch (bsign_priv->cipher)
441 {
442 case GNUNET_CRYPTO_BSA_INVALID:
443 GNUNET_break (0);
444 GNUNET_free (biv);
445 return NULL;
446 case GNUNET_CRYPTO_BSA_RSA:
447 return biv;
448 case GNUNET_CRYPTO_BSA_CS:
449 {
450 struct GNUNET_CRYPTO_CsRSecret cspriv[2];
451
452 GNUNET_CRYPTO_cs_r_derive (&nonce->cs_nonce,
453 salt,
454 &bsign_priv->details.cs_private_key,
455 cspriv);
456 GNUNET_CRYPTO_cs_r_get_public (&cspriv[0],
457 &biv->details.cs_values.r_pub[0]);
458 GNUNET_CRYPTO_cs_r_get_public (&cspriv[1],
459 &biv->details.cs_values.r_pub[1]);
460 return biv;
461 }
462 }
463 GNUNET_break (0);
464 GNUNET_free (biv);
465 return NULL;
466}
467
468
469struct GNUNET_CRYPTO_BlindedMessage *
470GNUNET_CRYPTO_message_blind_to_sign (
471 const struct GNUNET_CRYPTO_BlindSignPublicKey *bsign_pub,
472 const union GNUNET_CRYPTO_BlindingSecretP *bks,
473 const union GNUNET_CRYPTO_BlindSessionNonce *nonce,
474 const void *message,
475 size_t message_size,
476 const struct GNUNET_CRYPTO_BlindingInputValues *alg_values)
477{
478 struct GNUNET_CRYPTO_BlindedMessage *bm;
479
480 bm = GNUNET_new (struct GNUNET_CRYPTO_BlindedMessage);
481 bm->cipher = bsign_pub->cipher;
482 bm->rc = 1;
483 switch (bsign_pub->cipher)
484 {
485 case GNUNET_CRYPTO_BSA_INVALID:
486 GNUNET_break (0);
487 GNUNET_free (bm);
488 return NULL;
489 case GNUNET_CRYPTO_BSA_RSA:
490 if (GNUNET_YES !=
491 GNUNET_CRYPTO_rsa_blind (
492 message,
493 message_size,
494 &bks->rsa_bks,
495 bsign_pub->details.rsa_public_key,
496 &bm->details.rsa_blinded_message))
497 {
498 GNUNET_break (0);
499 GNUNET_free (bm);
500 return NULL;
501 }
502 return bm;
503 case GNUNET_CRYPTO_BSA_CS:
504 {
505 struct GNUNET_CRYPTO_CSPublicRPairP blinded_r_pub;
506 struct GNUNET_CRYPTO_CsBlindingSecret bs[2];
507
508 if (NULL == nonce)
509 {
510 GNUNET_break_op (0);
511 GNUNET_free (bm);
512 return NULL;
513 }
514 GNUNET_CRYPTO_cs_blinding_secrets_derive (&bks->nonce,
515 bs);
516 GNUNET_CRYPTO_cs_calc_blinded_c (
517 bs,
518 alg_values->details.cs_values.r_pub,
519 &bsign_pub->details.cs_public_key,
520 message,
521 message_size,
522 bm->details.cs_blinded_message.c,
523 &blinded_r_pub);
524 bm->details.cs_blinded_message.nonce = nonce->cs_nonce;
525 (void) blinded_r_pub;
526 return bm;
527 }
528 }
529 GNUNET_break (0);
530 return NULL;
531}
532
533
534struct GNUNET_CRYPTO_BlindedSignature *
535GNUNET_CRYPTO_blind_sign (
536 const struct GNUNET_CRYPTO_BlindSignPrivateKey *bsign_priv,
537 const char *salt,
538 const struct GNUNET_CRYPTO_BlindedMessage *blinded_message)
539{
540 struct GNUNET_CRYPTO_BlindedSignature *blind_sig;
541
542 if (blinded_message->cipher != bsign_priv->cipher)
543 {
544 GNUNET_break (0);
545 return NULL;
546 }
547
548 blind_sig = GNUNET_new (struct GNUNET_CRYPTO_BlindedSignature);
549 blind_sig->cipher = bsign_priv->cipher;
550 blind_sig->rc = 1;
551 switch (bsign_priv->cipher)
552 {
553 case GNUNET_CRYPTO_BSA_INVALID:
554 GNUNET_break (0);
555 GNUNET_free (blind_sig);
556 return NULL;
557 case GNUNET_CRYPTO_BSA_RSA:
558 blind_sig->details.blinded_rsa_signature
559 = GNUNET_CRYPTO_rsa_sign_blinded (
560 bsign_priv->details.rsa_private_key,
561 &blinded_message->details.rsa_blinded_message);
562 if (NULL == blind_sig->details.blinded_rsa_signature)
563 {
564 GNUNET_break (0);
565 GNUNET_free (blind_sig);
566 return NULL;
567 }
568 return blind_sig;
569 case GNUNET_CRYPTO_BSA_CS:
570 {
571 struct GNUNET_CRYPTO_CsRSecret r[2];
572
573 GNUNET_CRYPTO_cs_r_derive (
574 &blinded_message->details.cs_blinded_message.nonce,
575 salt,
576 &bsign_priv->details.cs_private_key,
577 r);
578 GNUNET_CRYPTO_cs_sign_derive (
579 &bsign_priv->details.cs_private_key,
580 r,
581 &blinded_message->details.cs_blinded_message,
582 &blind_sig->details.blinded_cs_answer);
583 }
584 return blind_sig;
585 }
586 GNUNET_break (0);
587 return NULL;
588}
589
590
591struct GNUNET_CRYPTO_UnblindedSignature *
592GNUNET_CRYPTO_blind_sig_unblind (
593 const struct GNUNET_CRYPTO_BlindedSignature *blinded_sig,
594 const union GNUNET_CRYPTO_BlindingSecretP *bks,
595 const void *message,
596 size_t message_size,
597 const struct GNUNET_CRYPTO_BlindingInputValues *alg_values,
598 const struct GNUNET_CRYPTO_BlindSignPublicKey *bsign_pub)
599{
600 struct GNUNET_CRYPTO_UnblindedSignature *ub_sig;
601
602 if (blinded_sig->cipher != bsign_pub->cipher)
603 {
604 GNUNET_break (0);
605 return NULL;
606 }
607 if (blinded_sig->cipher != alg_values->cipher)
608 {
609 GNUNET_break (0);
610 return NULL;
611 }
612 ub_sig = GNUNET_new (struct GNUNET_CRYPTO_UnblindedSignature);
613 ub_sig->cipher = blinded_sig->cipher;
614 ub_sig->rc = 1;
615 switch (bsign_pub->cipher)
616 {
617 case GNUNET_CRYPTO_BSA_INVALID:
618 GNUNET_break (0);
619 GNUNET_free (ub_sig);
620 return NULL;
621 case GNUNET_CRYPTO_BSA_RSA:
622 ub_sig->details.rsa_signature
623 = GNUNET_CRYPTO_rsa_unblind (
624 blinded_sig->details.blinded_rsa_signature,
625 &bks->rsa_bks,
626 bsign_pub->details.rsa_public_key);
627 if (NULL == ub_sig->details.rsa_signature)
628 {
629 GNUNET_break (0);
630 GNUNET_free (ub_sig);
631 return NULL;
632 }
633 return ub_sig;
634 case GNUNET_CRYPTO_BSA_CS:
635 {
636 struct GNUNET_CRYPTO_CsBlindingSecret bs[2];
637 struct GNUNET_CRYPTO_CsC c[2];
638 struct GNUNET_CRYPTO_CSPublicRPairP r_pub_blind;
639 unsigned int b;
640
641 GNUNET_CRYPTO_cs_blinding_secrets_derive (&bks->nonce,
642 bs);
643 GNUNET_CRYPTO_cs_calc_blinded_c (
644 bs,
645 alg_values->details.cs_values.r_pub,
646 &bsign_pub->details.cs_public_key,
647 message,
648 message_size,
649 c,
650 &r_pub_blind);
651 b = blinded_sig->details.blinded_cs_answer.b;
652 ub_sig->details.cs_signature.r_point
653 = r_pub_blind.r_pub[b];
654 GNUNET_CRYPTO_cs_unblind (
655 &blinded_sig->details.blinded_cs_answer.s_scalar,
656 &bs[b],
657 &ub_sig->details.cs_signature.s_scalar);
658 return ub_sig;
659 }
660 }
661 GNUNET_break (0);
662 GNUNET_free (ub_sig);
663 return NULL;
664}
665
666
667enum GNUNET_GenericReturnValue
668GNUNET_CRYPTO_blind_sig_verify (
669 const struct GNUNET_CRYPTO_BlindSignPublicKey *bsign_pub,
670 const struct GNUNET_CRYPTO_UnblindedSignature *ub_sig,
671 const void *message,
672 size_t message_size)
673{
674 if (bsign_pub->cipher != ub_sig->cipher)
675 {
676 GNUNET_break (0);
677 return GNUNET_SYSERR;
678 }
679 switch (bsign_pub->cipher)
680 {
681 case GNUNET_CRYPTO_BSA_INVALID:
682 GNUNET_break (0);
683 return GNUNET_NO;
684 case GNUNET_CRYPTO_BSA_RSA:
685 if (GNUNET_OK !=
686 GNUNET_CRYPTO_rsa_verify (message,
687 message_size,
688 ub_sig->details.rsa_signature,
689 bsign_pub->details.rsa_public_key))
690 {
691 GNUNET_break_op (0);
692 return GNUNET_NO;
693 }
694 return GNUNET_YES;
695 case GNUNET_CRYPTO_BSA_CS:
696 if (GNUNET_OK !=
697 GNUNET_CRYPTO_cs_verify (&ub_sig->details.cs_signature,
698 &bsign_pub->details.cs_public_key,
699 message,
700 message_size))
701 {
702 GNUNET_break_op (0);
703 return GNUNET_NO;
704 }
705 return GNUNET_YES;
706 }
707 GNUNET_break (0);
708 return GNUNET_NO;
709}
710
711
712/* end of crypto_blind_sign.c */
diff --git a/src/lib/util/crypto_crc.c b/src/lib/util/crypto_crc.c
new file mode 100644
index 000000000..f93b5b0b3
--- /dev/null
+++ b/src/lib/util/crypto_crc.c
@@ -0,0 +1,173 @@
1/*
2 This file is part of GNUnet.
3 Copyright (C) 2001, 2002, 2003, 2004, 2006 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 For the actual CRC-32 code:
21 Copyright abandoned; this code is in the public domain.
22 Provided to GNUnet by peter@horizon.com
23 */
24
25/**
26 * @file util/crypto_crc.c
27 * @brief implementation of CRC16 and CRC32
28 * @author Christian Grothoff
29 */
30
31#include "platform.h"
32#include "gnunet_util_lib.h"
33
34#define LOG(kind, ...) GNUNET_log_from (kind, "util-crypto-crc", __VA_ARGS__)
35
36/* Avoid wasting space on 8-byte longs. */
37#if UINT_MAX >= 0xffffffff
38typedef unsigned int GNUNET_uLong;
39#elif ULONG_MAX >= 0xffffffff
40typedef unsigned long GNUNET_uLong;
41#else
42#error This compiler is not ANSI-compliant!
43#endif
44
45#define Z_NULL 0
46
47
48#define POLYNOMIAL (GNUNET_uLong) 0xedb88320
49static GNUNET_uLong crc_table[256];
50
51/*
52 * This routine writes each crc_table entry exactly once,
53 * with the correct final value. Thus, it is safe to call
54 * even on a table that someone else is using concurrently.
55 */
56static void
57crc_init ()
58{
59 static int once;
60 GNUNET_uLong h = 1;
61
62 if (once)
63 return;
64 once = 1;
65 crc_table[0] = 0;
66 for (unsigned int i = 128; i; i >>= 1)
67 {
68 h = (h >> 1) ^ ((h & 1) ? POLYNOMIAL : 0);
69 /* h is now crc_table[i] */
70 for (unsigned int j = 0; j < 256; j += 2 * i)
71 crc_table[i + j] = crc_table[j] ^ h;
72 }
73}
74
75
76/*
77 * This computes the standard preset and inverted CRC, as used
78 * by most networking standards. Start by passing in an initial
79 * chaining value of 0, and then pass in the return value from the
80 * previous crc32() call. The final return value is the CRC.
81 * Note that this is a little-endian CRC, which is best used with
82 * data transmitted lsbit-first, and it should, itself, be appended
83 * to data in little-endian byte and bit order to preserve the
84 * property of detecting all burst errors of length 32 bits or less.
85 */
86static GNUNET_uLong
87gn_crc32 (GNUNET_uLong crc, const char *buf, size_t len)
88{
89 crc_init ();
90 GNUNET_assert (crc_table[255] != 0);
91 crc ^= 0xffffffff;
92 while (len--)
93 crc = (crc >> 8) ^ crc_table[(crc ^ *buf++) & 0xff];
94 return crc ^ 0xffffffff;
95}
96
97
98int32_t
99GNUNET_CRYPTO_crc32_n (const void *buf, size_t len)
100{
101 GNUNET_uLong crc;
102
103 crc = gn_crc32 (0L, Z_NULL, 0);
104 crc = gn_crc32 (crc, (char *) buf, len);
105 return crc;
106}
107
108
109uint32_t
110GNUNET_CRYPTO_crc16_step (uint32_t sum, const void *buf, size_t len)
111{
112 const uint16_t *hdr = buf;
113
114 for (; len >= 2; len -= 2)
115 sum += *(hdr++);
116 if (len == 1)
117 sum += ntohs(*((uint8_t *)hdr) << 8);
118 return sum;
119}
120
121
122uint16_t
123GNUNET_CRYPTO_crc16_finish (uint32_t sum)
124{
125 sum = (sum >> 16) + (sum & 0xFFFF);
126 sum += (sum >> 16);
127
128 return ~sum;
129}
130
131
132uint16_t
133GNUNET_CRYPTO_crc16_n (const void *buf, size_t len)
134{
135 const uint16_t *hdr = buf;
136 uint32_t sum = GNUNET_CRYPTO_crc16_step (0, hdr, len);
137
138 return GNUNET_CRYPTO_crc16_finish (sum);
139}
140
141
142/**
143 * @ingroup hash
144 * Calculate the checksum of a buffer in one step.
145 *
146 * @param buf buffer to calculate CRC over
147 * @param len number of bytes in @a buf
148 * @return crc8 value
149 */
150uint8_t
151GNUNET_CRYPTO_crc8_n (const void *buf,
152 size_t len)
153{
154 const uint8_t *data = buf;
155 unsigned int crc = 0;
156 int i;
157 int j;
158
159 for (j = len; 0 != j; j--)
160 {
161 crc ^= (*data++ << 8);
162 for (i = 8; 0 != i; i--)
163 {
164 if (0 != (crc & 0x8000))
165 crc ^= (0x1070 << 3);
166 crc <<= 1;
167 }
168 }
169 return (uint8_t) (crc >> 8);
170}
171
172
173/* end of crypto_crc.c */
diff --git a/src/lib/util/crypto_cs.c b/src/lib/util/crypto_cs.c
new file mode 100644
index 000000000..049f63062
--- /dev/null
+++ b/src/lib/util/crypto_cs.c
@@ -0,0 +1,366 @@
1/*
2 This file is part of GNUnet
3 Copyright (C) 2014,2016,2019, 2023 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/crypto_cs.c
23 * @brief Clause Blind Schnorr signatures using Curve25519
24 * @author Lucien Heuzeveldt <lucienclaude.heuzeveldt@students.bfh.ch>
25 * @author Gian Demarmels <gian@demarmels.org>
26 */
27
28#include "platform.h"
29#include "gnunet_util_lib.h"
30#include <sodium.h>
31#include <gcrypt.h>
32
33/**
34 * IMPLEMENTATION NOTICE:
35 *
36 * This is an implementation of the Clause Blind Schnorr Signature Scheme using Curve25519.
37 * Further details about the Clause Blind Schnorr Signature Scheme can be found here:
38 * https://eprint.iacr.org/2019/877.pdf
39 *
40 * We use libsodium wherever possible.
41 */
42
43
44void
45GNUNET_CRYPTO_cs_private_key_generate (struct GNUNET_CRYPTO_CsPrivateKey *priv)
46{
47 crypto_core_ed25519_scalar_random (priv->scalar.d);
48}
49
50
51void
52GNUNET_CRYPTO_cs_private_key_get_public (
53 const struct GNUNET_CRYPTO_CsPrivateKey *priv,
54 struct GNUNET_CRYPTO_CsPublicKey *pub)
55{
56 GNUNET_assert (0 ==
57 crypto_scalarmult_ed25519_base_noclamp (pub->point.y,
58 priv->scalar.d));
59}
60
61
62/**
63 * Maps 32 random bytes to a scalar. This is necessary because libsodium
64 * expects scalar to be in the prime order subgroup.
65 *
66 * @param[in,out] scalar containing 32 byte char array, is modified to be in prime order subgroup
67 */
68static void
69map_to_scalar_subgroup (struct GNUNET_CRYPTO_Cs25519Scalar *scalar)
70{
71 /* perform clamping as described in RFC7748 */
72 scalar->d[0] &= 248;
73 scalar->d[31] &= 127;
74 scalar->d[31] |= 64;
75}
76
77
78void
79GNUNET_CRYPTO_cs_r_derive (const struct GNUNET_CRYPTO_CsSessionNonce *nonce,
80 const char *seed,
81 const struct GNUNET_CRYPTO_CsPrivateKey *lts,
82 struct GNUNET_CRYPTO_CsRSecret r[2])
83{
84 GNUNET_assert (
85 GNUNET_YES ==
86 GNUNET_CRYPTO_kdf (
87 r, sizeof (struct GNUNET_CRYPTO_CsRSecret) * 2,
88 seed, strlen (seed),
89 lts, sizeof (*lts),
90 nonce, sizeof (*nonce),
91 NULL, 0));
92 map_to_scalar_subgroup (&r[0].scalar);
93 map_to_scalar_subgroup (&r[1].scalar);
94}
95
96
97void
98GNUNET_CRYPTO_cs_r_get_public (const struct GNUNET_CRYPTO_CsRSecret *r_priv,
99 struct GNUNET_CRYPTO_CsRPublic *r_pub)
100{
101 GNUNET_assert (0 ==
102 crypto_scalarmult_ed25519_base_noclamp (r_pub->point.y,
103 r_priv->scalar.d));
104}
105
106
107void
108GNUNET_CRYPTO_cs_blinding_secrets_derive (
109 const struct GNUNET_CRYPTO_CsBlindingNonce *blind_seed,
110 struct GNUNET_CRYPTO_CsBlindingSecret bs[2])
111{
112 GNUNET_assert (
113 GNUNET_YES ==
114 GNUNET_CRYPTO_hkdf (bs,
115 sizeof (struct GNUNET_CRYPTO_CsBlindingSecret) * 2,
116 GCRY_MD_SHA512,
117 GCRY_MD_SHA256,
118 "alphabeta",
119 strlen ("alphabeta"),
120 blind_seed,
121 sizeof(*blind_seed),
122 NULL,
123 0));
124 map_to_scalar_subgroup (&bs[0].alpha);
125 map_to_scalar_subgroup (&bs[0].beta);
126 map_to_scalar_subgroup (&bs[1].alpha);
127 map_to_scalar_subgroup (&bs[1].beta);
128}
129
130
131/*
132order of subgroup required for scalars by libsodium
1332^252 + 27742317777372353535851937790883648493
134copied from https://github.com/jedisct1/libsodium/blob/master/src/libsodium/crypto_core/ed25519/ref10/ed25519_ref10.c
135and converted to big endian
136*/
137static const unsigned char L_BIG_ENDIAN[32] = {
138 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
139 0x00, 0x00, 0x00, 0x00, 0x00, 0x14, 0xde, 0xf9, 0xde, 0xa2, 0xf7,
140 0x9c, 0xd6, 0x58, 0x12, 0x63, 0x1a, 0x5c, 0xf5, 0xd3, 0xed
141};
142
143
144/**
145 * Computes a Hash of (R', m) mapped to a Curve25519 scalar
146 *
147 * @param hash initial hash of the message to be signed
148 * @param pub denomination public key (used as salt)
149 * @param[out] c C containing scalar
150 */
151static void
152cs_full_domain_hash (const struct GNUNET_CRYPTO_CsRPublic *r_dash,
153 const void *msg,
154 size_t msg_len,
155 const struct GNUNET_CRYPTO_CsPublicKey *pub,
156 struct GNUNET_CRYPTO_CsC *c)
157{
158 // SHA-512 hash of R' and message
159 size_t r_m_concat_len = sizeof(struct GNUNET_CRYPTO_CsRPublic) + msg_len;
160 char r_m_concat[r_m_concat_len];
161 memcpy (r_m_concat,
162 r_dash,
163 sizeof(struct GNUNET_CRYPTO_CsRPublic));
164 memcpy (r_m_concat + sizeof(struct GNUNET_CRYPTO_CsRPublic),
165 msg,
166 msg_len);
167 struct GNUNET_HashCode prehash;
168
169 GNUNET_CRYPTO_hash (r_m_concat,
170 r_m_concat_len,
171 &prehash);
172
173 // modulus converted to MPI representation
174 gcry_mpi_t l_mpi;
175 GNUNET_CRYPTO_mpi_scan_unsigned (&l_mpi,
176 L_BIG_ENDIAN,
177 sizeof(L_BIG_ENDIAN));
178
179 // calculate full domain hash
180 gcry_mpi_t c_mpi;
181 GNUNET_CRYPTO_kdf_mod_mpi (&c_mpi,
182 l_mpi,
183 pub,
184 sizeof(struct GNUNET_CRYPTO_CsPublicKey),
185 &prehash,
186 sizeof(struct GNUNET_HashCode),
187 "Curve25519FDH");
188 gcry_mpi_release (l_mpi);
189
190 // convert c from mpi
191 unsigned char c_big_endian[256 / 8];
192 GNUNET_CRYPTO_mpi_print_unsigned (c_big_endian,
193 sizeof(c_big_endian),
194 c_mpi);
195 gcry_mpi_release (c_mpi);
196 for (size_t i = 0; i<32; i++)
197 c->scalar.d[i] = c_big_endian[31 - i];
198}
199
200
201/**
202 * calculate R'
203 *
204 * @param bs blinding secret
205 * @param r_pub R
206 * @param pub public key
207 * @param[out] blinded_r_pub R'
208 */
209static void
210calc_r_dash (const struct GNUNET_CRYPTO_CsBlindingSecret *bs,
211 const struct GNUNET_CRYPTO_CsRPublic *r_pub,
212 const struct GNUNET_CRYPTO_CsPublicKey *pub,
213 struct GNUNET_CRYPTO_CsRPublic *blinded_r_pub)
214{
215 // R'i = Ri + alpha i*G + beta i*pub
216 struct GNUNET_CRYPTO_Cs25519Point alpha_mul_base;
217 GNUNET_assert (0 ==
218 crypto_scalarmult_ed25519_base_noclamp (
219 alpha_mul_base.y,
220 bs->alpha.d));
221 struct GNUNET_CRYPTO_Cs25519Point beta_mul_pub;
222 GNUNET_assert (0 ==
223 crypto_scalarmult_ed25519_noclamp (
224 beta_mul_pub.y,
225 bs->beta.d,
226 pub->point.y));
227 struct GNUNET_CRYPTO_Cs25519Point alpha_mul_base_plus_beta_mul_pub;
228 GNUNET_assert (0 == crypto_core_ed25519_add (
229 alpha_mul_base_plus_beta_mul_pub.y,
230 alpha_mul_base.y,
231 beta_mul_pub.y));
232 GNUNET_assert (0 ==
233 crypto_core_ed25519_add (
234 blinded_r_pub->point.y,
235 r_pub->point.y,
236 alpha_mul_base_plus_beta_mul_pub.y));
237}
238
239
240void
241GNUNET_CRYPTO_cs_calc_blinded_c (
242 const struct GNUNET_CRYPTO_CsBlindingSecret bs[2],
243 const struct GNUNET_CRYPTO_CsRPublic r_pub[2],
244 const struct GNUNET_CRYPTO_CsPublicKey *pub,
245 const void *msg,
246 size_t msg_len,
247 struct GNUNET_CRYPTO_CsC blinded_c[2],
248 struct GNUNET_CRYPTO_CSPublicRPairP *r_pub_blind)
249{
250 /* for i 0/1: R'i = Ri + alpha i*G + beta i*pub */
251 calc_r_dash (&bs[0],
252 &r_pub[0],
253 pub,
254 &r_pub_blind->r_pub[0]);
255 calc_r_dash (&bs[1],
256 &r_pub[1],
257 pub,
258 &r_pub_blind->r_pub[1]);
259
260 /* for i 0/1: c'i = H(R'i, msg) */
261 struct GNUNET_CRYPTO_CsC c_dash_0;
262 struct GNUNET_CRYPTO_CsC c_dash_1;
263 cs_full_domain_hash (&r_pub_blind->r_pub[0],
264 msg,
265 msg_len,
266 pub,
267 &c_dash_0);
268 cs_full_domain_hash (&r_pub_blind->r_pub[1],
269 msg,
270 msg_len,
271 pub,
272 &c_dash_1);
273
274 /* for i 0/1: ci = c'i + beta i mod p */
275 crypto_core_ed25519_scalar_add (blinded_c[0].scalar.d,
276 c_dash_0.scalar.d,
277 bs[0].beta.d);
278 crypto_core_ed25519_scalar_add (blinded_c[1].scalar.d,
279 c_dash_1.scalar.d,
280 bs[1].beta.d);
281}
282
283
284void
285GNUNET_CRYPTO_cs_sign_derive (
286 const struct GNUNET_CRYPTO_CsPrivateKey *priv,
287 const struct GNUNET_CRYPTO_CsRSecret r[2],
288 const struct GNUNET_CRYPTO_CsBlindedMessage *bm,
289 struct GNUNET_CRYPTO_CsBlindSignature *cs_blind_sig)
290{
291 struct GNUNET_CRYPTO_Cs25519Scalar c_b_mul_priv;
292 uint32_t hkdf_out;
293
294 /* derive clause session identifier b (random bit) */
295 GNUNET_assert (GNUNET_YES ==
296 GNUNET_CRYPTO_hkdf (&hkdf_out,
297 sizeof (hkdf_out),
298 GCRY_MD_SHA512,
299 GCRY_MD_SHA256,
300 "b",
301 strlen ("b"),
302 priv,
303 sizeof (*priv),
304 &bm->nonce,
305 sizeof (bm->nonce),
306 NULL,
307 0));
308 cs_blind_sig->b = hkdf_out % 2;
309
310 /* s = r_b + c_b * priv */
311 crypto_core_ed25519_scalar_mul (c_b_mul_priv.d,
312 bm->c[cs_blind_sig->b].scalar.d,
313 priv->scalar.d);
314 crypto_core_ed25519_scalar_add (cs_blind_sig->s_scalar.scalar.d,
315 r[cs_blind_sig->b].scalar.d,
316 c_b_mul_priv.d);
317}
318
319
320void
321GNUNET_CRYPTO_cs_unblind (
322 const struct GNUNET_CRYPTO_CsBlindS *blinded_signature_scalar,
323 const struct GNUNET_CRYPTO_CsBlindingSecret *bs,
324 struct GNUNET_CRYPTO_CsS *signature_scalar)
325{
326 crypto_core_ed25519_scalar_add (signature_scalar->scalar.d,
327 blinded_signature_scalar->scalar.d,
328 bs->alpha.d);
329}
330
331
332enum GNUNET_GenericReturnValue
333GNUNET_CRYPTO_cs_verify (const struct GNUNET_CRYPTO_CsSignature *sig,
334 const struct GNUNET_CRYPTO_CsPublicKey *pub,
335 const void *msg,
336 size_t msg_len)
337{
338 // calculate c' = H(R, m)
339 struct GNUNET_CRYPTO_CsC c_dash;
340
341 cs_full_domain_hash (&sig->r_point,
342 msg,
343 msg_len,
344 pub,
345 &c_dash);
346
347 // s'G ?= R' + c' pub
348 struct GNUNET_CRYPTO_Cs25519Point sig_scal_mul_base;
349 GNUNET_assert (0 ==
350 crypto_scalarmult_ed25519_base_noclamp (
351 sig_scal_mul_base.y,
352 sig->s_scalar.scalar.d));
353 struct GNUNET_CRYPTO_Cs25519Point c_dash_mul_pub;
354 GNUNET_assert (0 == crypto_scalarmult_ed25519_noclamp (c_dash_mul_pub.y,
355 c_dash.scalar.d,
356 pub->point.y));
357 struct GNUNET_CRYPTO_Cs25519Point R_add_c_dash_mul_pub;
358 GNUNET_assert (0 == crypto_core_ed25519_add (R_add_c_dash_mul_pub.y,
359 sig->r_point.point.y,
360 c_dash_mul_pub.y));
361
362 return 0 == GNUNET_memcmp (&sig_scal_mul_base,
363 &R_add_c_dash_mul_pub)
364 ? GNUNET_OK
365 : GNUNET_SYSERR;
366}
diff --git a/src/lib/util/crypto_ecc.c b/src/lib/util/crypto_ecc.c
new file mode 100644
index 000000000..8ea17fda0
--- /dev/null
+++ b/src/lib/util/crypto_ecc.c
@@ -0,0 +1,971 @@
1/*
2 This file is part of GNUnet.
3 Copyright (C) 2012, 2013, 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/**
22 * @file util/crypto_ecc.c
23 * @brief public key cryptography (ECC) with libgcrypt
24 * @author Christian Grothoff
25 * @author Florian Dold
26 */
27
28#include "platform.h"
29#include <gcrypt.h>
30#include <sodium.h>
31#include "gnunet_util_lib.h"
32#include "benchmark.h"
33
34#define EXTRA_CHECKS 0
35
36/**
37 * IMPLEMENTATION NOTICE:
38 *
39 * ECDSA: We use a non-standard curve for ECDSA: Ed25519.
40 * For performance reasons, we use cryptographic operations from
41 * libsodium wherever we can get away with it, even though libsodium
42 * itself does not support ECDSA.
43 * This is why the sign and verifiy functionality from libgcrypt is
44 * required and used.
45 *
46 * EdDSA: We use a standard EdDSA construction.
47 * (We still use libgcrypt for hashing and RNG, but not EC)
48 *
49 * ECDHE: For both EdDSA and ECDSA keys, we use libsodium for
50 * ECDHE due to performance benefits over libgcrypt.
51 */
52
53/**
54 * Name of the curve we are using. Note that we have hard-coded
55 * structs that use 256 bits, so using a bigger curve will require
56 * changes that break stuff badly. The name of the curve given here
57 * must be agreed by all peers and be supported by libgcrypt.
58 */
59#define CURVE "Ed25519"
60
61#define LOG(kind, ...) GNUNET_log_from (kind, "util-crypto-ecc", __VA_ARGS__)
62
63#define LOG_STRERROR(kind, syscall) \
64 GNUNET_log_from_strerror (kind, "util-crypto-ecc", syscall)
65
66#define LOG_STRERROR_FILE(kind, syscall, filename) \
67 GNUNET_log_from_strerror_file (kind, "util-crypto-ecc", syscall, \
68 filename)
69
70/**
71 * Log an error message at log-level 'level' that indicates
72 * a failure of the command 'cmd' with the message given
73 * by gcry_strerror(rc).
74 */
75#define LOG_GCRY(level, cmd, rc) \
76 do \
77 { \
78 LOG (level, \
79 _ ("`%s' failed at %s:%d with error: %s\n"), \
80 cmd, \
81 __FILE__, \
82 __LINE__, \
83 gcry_strerror (rc)); \
84 } while (0)
85
86
87/**
88 * Extract values from an S-expression.
89 *
90 * @param array where to store the result(s)
91 * @param sexp S-expression to parse
92 * @param topname top-level name in the S-expression that is of interest
93 * @param elems names of the elements to extract
94 * @return 0 on success
95 */
96static int
97key_from_sexp (gcry_mpi_t *array,
98 gcry_sexp_t sexp,
99 const char *topname,
100 const char *elems)
101{
102 gcry_sexp_t list;
103 gcry_sexp_t l2;
104 unsigned int idx;
105
106 list = gcry_sexp_find_token (sexp, topname, 0);
107 if (! list)
108 return 1;
109 l2 = gcry_sexp_cadr (list);
110 gcry_sexp_release (list);
111 list = l2;
112 if (! list)
113 return 2;
114
115 idx = 0;
116 for (const char *s = elems; *s; s++, idx++)
117 {
118 l2 = gcry_sexp_find_token (list, s, 1);
119 if (! l2)
120 {
121 for (unsigned int i = 0; i < idx; i++)
122 {
123 gcry_free (array[i]);
124 array[i] = NULL;
125 }
126 gcry_sexp_release (list);
127 return 3; /* required parameter not found */
128 }
129 array[idx] = gcry_sexp_nth_mpi (l2, 1, GCRYMPI_FMT_USG);
130 gcry_sexp_release (l2);
131 if (! array[idx])
132 {
133 for (unsigned int i = 0; i < idx; i++)
134 {
135 gcry_free (array[i]);
136 array[i] = NULL;
137 }
138 gcry_sexp_release (list);
139 return 4; /* required parameter is invalid */
140 }
141 }
142 gcry_sexp_release (list);
143 return 0;
144}
145
146
147/**
148 * Convert the given private key from the network format to the
149 * S-expression that can be used by libgcrypt.
150 *
151 * @param priv private key to decode
152 * @return NULL on error
153 */
154static gcry_sexp_t
155decode_private_ecdsa_key (const struct GNUNET_CRYPTO_EcdsaPrivateKey *priv)
156{
157 gcry_sexp_t result;
158 int rc;
159 uint8_t d[32];
160
161 for (size_t i = 0; i<32; i++)
162 d[i] = priv->d[31 - i];
163
164 rc = gcry_sexp_build (&result,
165 NULL,
166 "(private-key(ecc(curve \"" CURVE "\")"
167 "(d %b)))",
168 32,
169 d);
170 if (0 != rc)
171 {
172 LOG_GCRY (GNUNET_ERROR_TYPE_ERROR, "gcry_sexp_build", rc);
173 GNUNET_assert (0);
174 }
175#if EXTRA_CHECKS
176 if (0 != (rc = gcry_pk_testkey (result)))
177 {
178 LOG_GCRY (GNUNET_ERROR_TYPE_ERROR, "gcry_pk_testkey", rc);
179 GNUNET_assert (0);
180 }
181#endif
182 return result;
183}
184
185
186void
187GNUNET_CRYPTO_ecdsa_key_get_public (
188 const struct GNUNET_CRYPTO_EcdsaPrivateKey *priv,
189 struct GNUNET_CRYPTO_EcdsaPublicKey *pub)
190{
191 BENCHMARK_START (ecdsa_key_get_public);
192 crypto_scalarmult_ed25519_base_noclamp (pub->q_y, priv->d);
193 BENCHMARK_END (ecdsa_key_get_public);
194}
195
196
197void
198GNUNET_CRYPTO_eddsa_key_get_public (
199 const struct GNUNET_CRYPTO_EddsaPrivateKey *priv,
200 struct GNUNET_CRYPTO_EddsaPublicKey *pub)
201{
202 unsigned char pk[crypto_sign_PUBLICKEYBYTES];
203 unsigned char sk[crypto_sign_SECRETKEYBYTES];
204
205 BENCHMARK_START (eddsa_key_get_public);
206 GNUNET_assert (0 == crypto_sign_seed_keypair (pk, sk, priv->d));
207 GNUNET_memcpy (pub->q_y, pk, crypto_sign_PUBLICKEYBYTES);
208 sodium_memzero (sk, crypto_sign_SECRETKEYBYTES);
209 BENCHMARK_END (eddsa_key_get_public);
210}
211
212
213void
214GNUNET_CRYPTO_ecdhe_key_get_public (
215 const struct GNUNET_CRYPTO_EcdhePrivateKey *priv,
216 struct GNUNET_CRYPTO_EcdhePublicKey *pub)
217{
218 BENCHMARK_START (ecdhe_key_get_public);
219 GNUNET_assert (0 == crypto_scalarmult_base (pub->q_y, priv->d));
220 BENCHMARK_END (ecdhe_key_get_public);
221}
222
223
224char *
225GNUNET_CRYPTO_ecdsa_public_key_to_string (
226 const struct GNUNET_CRYPTO_EcdsaPublicKey *pub)
227{
228 char *pubkeybuf;
229 size_t keylen = (sizeof(struct GNUNET_CRYPTO_EcdsaPublicKey)) * 8;
230 char *end;
231
232 if (keylen % 5 > 0)
233 keylen += 5 - keylen % 5;
234 keylen /= 5;
235 pubkeybuf = GNUNET_malloc (keylen + 1);
236 end =
237 GNUNET_STRINGS_data_to_string ((unsigned char *) pub,
238 sizeof(struct GNUNET_CRYPTO_EcdsaPublicKey),
239 pubkeybuf,
240 keylen);
241 if (NULL == end)
242 {
243 GNUNET_free (pubkeybuf);
244 return NULL;
245 }
246 *end = '\0';
247 return pubkeybuf;
248}
249
250
251char *
252GNUNET_CRYPTO_eddsa_public_key_to_string (
253 const struct GNUNET_CRYPTO_EddsaPublicKey *pub)
254{
255 char *pubkeybuf;
256 size_t keylen = (sizeof(struct GNUNET_CRYPTO_EddsaPublicKey)) * 8;
257 char *end;
258
259 if (keylen % 5 > 0)
260 keylen += 5 - keylen % 5;
261 keylen /= 5;
262 pubkeybuf = GNUNET_malloc (keylen + 1);
263 end =
264 GNUNET_STRINGS_data_to_string ((unsigned char *) pub,
265 sizeof(struct GNUNET_CRYPTO_EddsaPublicKey),
266 pubkeybuf,
267 keylen);
268 if (NULL == end)
269 {
270 GNUNET_free (pubkeybuf);
271 return NULL;
272 }
273 *end = '\0';
274 return pubkeybuf;
275}
276
277
278char *
279GNUNET_CRYPTO_eddsa_private_key_to_string (
280 const struct GNUNET_CRYPTO_EddsaPrivateKey *priv)
281{
282 char *privkeybuf;
283 size_t keylen = (sizeof(struct GNUNET_CRYPTO_EddsaPrivateKey)) * 8;
284 char *end;
285
286 if (keylen % 5 > 0)
287 keylen += 5 - keylen % 5;
288 keylen /= 5;
289 privkeybuf = GNUNET_malloc (keylen + 1);
290 end = GNUNET_STRINGS_data_to_string ((unsigned char *) priv,
291 sizeof(
292 struct GNUNET_CRYPTO_EddsaPrivateKey),
293 privkeybuf,
294 keylen);
295 if (NULL == end)
296 {
297 GNUNET_free (privkeybuf);
298 return NULL;
299 }
300 *end = '\0';
301 return privkeybuf;
302}
303
304
305char *
306GNUNET_CRYPTO_ecdsa_private_key_to_string (
307 const struct GNUNET_CRYPTO_EcdsaPrivateKey *priv)
308{
309 char *privkeybuf;
310 size_t keylen = (sizeof(struct GNUNET_CRYPTO_EcdsaPrivateKey)) * 8;
311 char *end;
312
313 if (keylen % 5 > 0)
314 keylen += 5 - keylen % 5;
315 keylen /= 5;
316 privkeybuf = GNUNET_malloc (keylen + 1);
317 end = GNUNET_STRINGS_data_to_string ((unsigned char *) priv,
318 sizeof(
319 struct GNUNET_CRYPTO_EcdsaPrivateKey),
320 privkeybuf,
321 keylen);
322 if (NULL == end)
323 {
324 GNUNET_free (privkeybuf);
325 return NULL;
326 }
327 *end = '\0';
328 return privkeybuf;
329}
330
331
332enum GNUNET_GenericReturnValue
333GNUNET_CRYPTO_ecdsa_public_key_from_string (
334 const char *enc,
335 size_t enclen,
336 struct GNUNET_CRYPTO_EcdsaPublicKey *pub)
337{
338 size_t keylen = (sizeof(struct GNUNET_CRYPTO_EcdsaPublicKey)) * 8;
339
340 if (keylen % 5 > 0)
341 keylen += 5 - keylen % 5;
342 keylen /= 5;
343 if (enclen != keylen)
344 return GNUNET_SYSERR;
345
346 if (GNUNET_OK !=
347 GNUNET_STRINGS_string_to_data (enc,
348 enclen,
349 pub,
350 sizeof(
351 struct GNUNET_CRYPTO_EcdsaPublicKey)))
352 return GNUNET_SYSERR;
353 return GNUNET_OK;
354}
355
356
357enum GNUNET_GenericReturnValue
358GNUNET_CRYPTO_eddsa_public_key_from_string (
359 const char *enc,
360 size_t enclen,
361 struct GNUNET_CRYPTO_EddsaPublicKey *pub)
362{
363 size_t keylen = (sizeof(struct GNUNET_CRYPTO_EddsaPublicKey)) * 8;
364
365 if (keylen % 5 > 0)
366 keylen += 5 - keylen % 5;
367 keylen /= 5;
368 if (enclen != keylen)
369 return GNUNET_SYSERR;
370
371 if (GNUNET_OK !=
372 GNUNET_STRINGS_string_to_data (enc,
373 enclen,
374 pub,
375 sizeof(
376 struct GNUNET_CRYPTO_EddsaPublicKey)))
377 return GNUNET_SYSERR;
378 return GNUNET_OK;
379}
380
381
382enum GNUNET_GenericReturnValue
383GNUNET_CRYPTO_eddsa_private_key_from_string (
384 const char *enc,
385 size_t enclen,
386 struct GNUNET_CRYPTO_EddsaPrivateKey *priv)
387{
388 size_t keylen = (sizeof(struct GNUNET_CRYPTO_EddsaPrivateKey)) * 8;
389
390 if (keylen % 5 > 0)
391 keylen += 5 - keylen % 5;
392 keylen /= 5;
393 if (enclen != keylen)
394 return GNUNET_SYSERR;
395
396 if (GNUNET_OK !=
397 GNUNET_STRINGS_string_to_data (enc,
398 enclen,
399 priv,
400 sizeof(
401 struct GNUNET_CRYPTO_EddsaPrivateKey)))
402 return GNUNET_SYSERR;
403#if CRYPTO_BUG
404 if (GNUNET_OK != check_eddsa_key (priv))
405 {
406 GNUNET_break (0);
407 return GNUNET_OK;
408 }
409#endif
410 return GNUNET_OK;
411}
412
413
414static void
415buffer_clear (void *buf, size_t len)
416{
417#if HAVE_MEMSET_S
418 memset_s (buf, len, 0, len);
419#elif HAVE_EXPLICIT_BZERO
420 explicit_bzero (buf, len);
421#else
422 volatile unsigned char *p = buf;
423 while (len--)
424 *p++ = 0;
425#endif
426}
427
428
429void
430GNUNET_CRYPTO_ecdhe_key_clear (struct GNUNET_CRYPTO_EcdhePrivateKey *pk)
431{
432 buffer_clear (pk, sizeof(struct GNUNET_CRYPTO_EcdhePrivateKey));
433}
434
435
436void
437GNUNET_CRYPTO_ecdsa_key_clear (struct GNUNET_CRYPTO_EcdsaPrivateKey *pk)
438{
439 buffer_clear (pk, sizeof(struct GNUNET_CRYPTO_EcdsaPrivateKey));
440}
441
442
443void
444GNUNET_CRYPTO_eddsa_key_clear (struct GNUNET_CRYPTO_EddsaPrivateKey *pk)
445{
446 buffer_clear (pk, sizeof(struct GNUNET_CRYPTO_EddsaPrivateKey));
447}
448
449
450void
451GNUNET_CRYPTO_ecdhe_key_create (struct GNUNET_CRYPTO_EcdhePrivateKey *pk)
452{
453 BENCHMARK_START (ecdhe_key_create);
454 GNUNET_CRYPTO_random_block (GNUNET_CRYPTO_QUALITY_NONCE,
455 pk,
456 sizeof (struct GNUNET_CRYPTO_EcdhePrivateKey));
457 BENCHMARK_END (ecdhe_key_create);
458}
459
460
461void
462GNUNET_CRYPTO_ecdsa_key_create (struct GNUNET_CRYPTO_EcdsaPrivateKey *pk)
463{
464 BENCHMARK_START (ecdsa_key_create);
465 GNUNET_CRYPTO_random_block (GNUNET_CRYPTO_QUALITY_NONCE,
466 pk,
467 sizeof (struct GNUNET_CRYPTO_EcdsaPrivateKey));
468 pk->d[0] &= 248;
469 pk->d[31] &= 127;
470 pk->d[31] |= 64;
471
472 BENCHMARK_END (ecdsa_key_create);
473}
474
475
476void
477GNUNET_CRYPTO_eddsa_key_create (struct GNUNET_CRYPTO_EddsaPrivateKey *pk)
478{
479 BENCHMARK_START (eddsa_key_create);
480 /*
481 * We do not clamp for EdDSA, since all functions that use the private key do
482 * their own clamping (just like in libsodium). What we call "private key"
483 * here, actually corresponds to the seed in libsodium.
484 *
485 * (Contrast this to ECDSA, where functions using the private key can't clamp
486 * due to properties needed for GNS. That is a worse/unsafer API, but
487 * required for the GNS constructions to work.)
488 */
489 GNUNET_CRYPTO_random_block (GNUNET_CRYPTO_QUALITY_NONCE,
490 pk,
491 sizeof (struct GNUNET_CRYPTO_EddsaPrivateKey));
492 BENCHMARK_END (eddsa_key_create);
493}
494
495
496const struct GNUNET_CRYPTO_EcdsaPrivateKey *
497GNUNET_CRYPTO_ecdsa_key_get_anonymous ()
498{
499 /**
500 * 'anonymous' pseudonym (global static, d=1, public key = G
501 * (generator).
502 */
503 static struct GNUNET_CRYPTO_EcdsaPrivateKey anonymous;
504 static int once;
505
506 if (once)
507 return &anonymous;
508 GNUNET_CRYPTO_mpi_print_unsigned (anonymous.d,
509 sizeof(anonymous.d),
510 GCRYMPI_CONST_ONE);
511 anonymous.d[0] &= 248;
512 anonymous.d[31] &= 127;
513 anonymous.d[31] |= 64;
514
515 once = 1;
516 return &anonymous;
517}
518
519
520/**
521 * Convert the data specified in the given purpose argument to an
522 * S-expression suitable for signature operations.
523 *
524 * @param purpose data to convert
525 * @return converted s-expression
526 */
527static gcry_sexp_t
528data_to_ecdsa_value (const struct GNUNET_CRYPTO_EccSignaturePurpose *purpose)
529{
530 gcry_sexp_t data;
531 int rc;
532 /* Unlike EdDSA, libgcrypt expects a hash for ECDSA. */
533 struct GNUNET_HashCode hc;
534
535 GNUNET_CRYPTO_hash (purpose, ntohl (purpose->size), &hc);
536 if (0 != (rc = gcry_sexp_build (&data,
537 NULL,
538 "(data(flags rfc6979)(hash %s %b))",
539 "sha512",
540 (int) sizeof(hc),
541 &hc)))
542 {
543 LOG_GCRY (GNUNET_ERROR_TYPE_ERROR, "gcry_sexp_build", rc);
544 return NULL;
545 }
546 return data;
547}
548
549
550enum GNUNET_GenericReturnValue
551GNUNET_CRYPTO_ecdsa_sign_ (
552 const struct GNUNET_CRYPTO_EcdsaPrivateKey *priv,
553 const struct GNUNET_CRYPTO_EccSignaturePurpose *purpose,
554 struct GNUNET_CRYPTO_EcdsaSignature *sig)
555{
556 gcry_sexp_t priv_sexp;
557 gcry_sexp_t sig_sexp;
558 gcry_sexp_t data;
559 int rc;
560 gcry_mpi_t rs[2];
561
562 BENCHMARK_START (ecdsa_sign);
563
564 priv_sexp = decode_private_ecdsa_key (priv);
565 data = data_to_ecdsa_value (purpose);
566 if (0 != (rc = gcry_pk_sign (&sig_sexp, data, priv_sexp)))
567 {
568 LOG (GNUNET_ERROR_TYPE_WARNING,
569 _ ("ECC signing failed at %s:%d: %s\n"),
570 __FILE__,
571 __LINE__,
572 gcry_strerror (rc));
573 gcry_sexp_release (data);
574 gcry_sexp_release (priv_sexp);
575 return GNUNET_SYSERR;
576 }
577 gcry_sexp_release (priv_sexp);
578 gcry_sexp_release (data);
579
580 /* extract 'r' and 's' values from sexpression 'sig_sexp' and store in
581 'signature' */
582 if (0 != (rc = key_from_sexp (rs, sig_sexp, "sig-val", "rs")))
583 {
584 GNUNET_break (0);
585 gcry_sexp_release (sig_sexp);
586 return GNUNET_SYSERR;
587 }
588 gcry_sexp_release (sig_sexp);
589 GNUNET_CRYPTO_mpi_print_unsigned (sig->r, sizeof(sig->r), rs[0]);
590 GNUNET_CRYPTO_mpi_print_unsigned (sig->s, sizeof(sig->s), rs[1]);
591 gcry_mpi_release (rs[0]);
592 gcry_mpi_release (rs[1]);
593
594 BENCHMARK_END (ecdsa_sign);
595
596 return GNUNET_OK;
597}
598
599
600enum GNUNET_GenericReturnValue
601GNUNET_CRYPTO_eddsa_sign_raw (
602 const struct GNUNET_CRYPTO_EddsaPrivateKey *priv,
603 void *data,
604 size_t size,
605 struct GNUNET_CRYPTO_EddsaSignature *sig)
606{
607 unsigned char sk[crypto_sign_SECRETKEYBYTES];
608 unsigned char pk[crypto_sign_PUBLICKEYBYTES];
609 int res;
610
611 GNUNET_assert (0 == crypto_sign_seed_keypair (pk, sk, priv->d));
612 res = crypto_sign_detached ((uint8_t *) sig,
613 NULL,
614 (uint8_t *) data,
615 size,
616 sk);
617 return (res == 0) ? GNUNET_OK : GNUNET_SYSERR;
618}
619
620
621enum GNUNET_GenericReturnValue
622GNUNET_CRYPTO_eddsa_sign_ (
623 const struct GNUNET_CRYPTO_EddsaPrivateKey *priv,
624 const struct GNUNET_CRYPTO_EccSignaturePurpose *purpose,
625 struct GNUNET_CRYPTO_EddsaSignature *sig)
626{
627
628 size_t mlen = ntohl (purpose->size);
629 unsigned char sk[crypto_sign_SECRETKEYBYTES];
630 unsigned char pk[crypto_sign_PUBLICKEYBYTES];
631 int res;
632
633 BENCHMARK_START (eddsa_sign);
634 GNUNET_assert (0 == crypto_sign_seed_keypair (pk, sk, priv->d));
635 res = crypto_sign_detached ((uint8_t *) sig,
636 NULL,
637 (uint8_t *) purpose,
638 mlen,
639 sk);
640 BENCHMARK_END (eddsa_sign);
641 return (res == 0) ? GNUNET_OK : GNUNET_SYSERR;
642}
643
644
645enum GNUNET_GenericReturnValue
646GNUNET_CRYPTO_ecdsa_verify_ (
647 uint32_t purpose,
648 const struct GNUNET_CRYPTO_EccSignaturePurpose *validate,
649 const struct GNUNET_CRYPTO_EcdsaSignature *sig,
650 const struct GNUNET_CRYPTO_EcdsaPublicKey *pub)
651{
652 gcry_sexp_t data;
653 gcry_sexp_t sig_sexpr;
654 gcry_sexp_t pub_sexpr;
655 int rc;
656
657 BENCHMARK_START (ecdsa_verify);
658
659 if (purpose != ntohl (validate->purpose))
660 return GNUNET_SYSERR; /* purpose mismatch */
661
662 /* build s-expression for signature */
663 if (0 != (rc = gcry_sexp_build (&sig_sexpr,
664 NULL,
665 "(sig-val(ecdsa(r %b)(s %b)))",
666 (int) sizeof(sig->r),
667 sig->r,
668 (int) sizeof(sig->s),
669 sig->s)))
670 {
671 LOG_GCRY (GNUNET_ERROR_TYPE_ERROR, "gcry_sexp_build", rc);
672 return GNUNET_SYSERR;
673 }
674 data = data_to_ecdsa_value (validate);
675 if (0 != (rc = gcry_sexp_build (&pub_sexpr,
676 NULL,
677 "(public-key(ecc(curve " CURVE ")(q %b)))",
678 (int) sizeof(pub->q_y),
679 pub->q_y)))
680 {
681 gcry_sexp_release (data);
682 gcry_sexp_release (sig_sexpr);
683 return GNUNET_SYSERR;
684 }
685 rc = gcry_pk_verify (sig_sexpr, data, pub_sexpr);
686 gcry_sexp_release (pub_sexpr);
687 gcry_sexp_release (data);
688 gcry_sexp_release (sig_sexpr);
689 if (0 != rc)
690 {
691 LOG (GNUNET_ERROR_TYPE_INFO,
692 _ ("ECDSA signature verification failed at %s:%d: %s\n"),
693 __FILE__,
694 __LINE__,
695 gcry_strerror (rc));
696 BENCHMARK_END (ecdsa_verify);
697 return GNUNET_SYSERR;
698 }
699 BENCHMARK_END (ecdsa_verify);
700 return GNUNET_OK;
701}
702
703
704enum GNUNET_GenericReturnValue
705GNUNET_CRYPTO_eddsa_verify_ (
706 uint32_t purpose,
707 const struct GNUNET_CRYPTO_EccSignaturePurpose *validate,
708 const struct GNUNET_CRYPTO_EddsaSignature *sig,
709 const struct GNUNET_CRYPTO_EddsaPublicKey *pub)
710{
711 const unsigned char *m = (const void *) validate;
712 size_t mlen = ntohl (validate->size);
713 const unsigned char *s = (const void *) sig;
714
715 int res;
716
717 if (purpose != ntohl (validate->purpose))
718 return GNUNET_SYSERR; /* purpose mismatch */
719
720 BENCHMARK_START (eddsa_verify);
721
722 res = crypto_sign_verify_detached (s, m, mlen, pub->q_y);
723 BENCHMARK_END (eddsa_verify);
724 return (res == 0) ? GNUNET_OK : GNUNET_SYSERR;
725}
726
727
728enum GNUNET_GenericReturnValue
729GNUNET_CRYPTO_ecc_ecdh (const struct GNUNET_CRYPTO_EcdhePrivateKey *priv,
730 const struct GNUNET_CRYPTO_EcdhePublicKey *pub,
731 struct GNUNET_HashCode *key_material)
732{
733 uint8_t p[crypto_scalarmult_BYTES];
734 if (0 != crypto_scalarmult (p, priv->d, pub->q_y))
735 return GNUNET_SYSERR;
736 GNUNET_CRYPTO_hash (p, crypto_scalarmult_BYTES, key_material);
737 return GNUNET_OK;
738}
739
740
741enum GNUNET_GenericReturnValue
742GNUNET_CRYPTO_eddsa_ecdh (const struct GNUNET_CRYPTO_EddsaPrivateKey *priv,
743 const struct GNUNET_CRYPTO_EcdhePublicKey *pub,
744 struct GNUNET_HashCode *key_material)
745{
746 struct GNUNET_HashCode hc;
747 uint8_t a[crypto_scalarmult_SCALARBYTES];
748 uint8_t p[crypto_scalarmult_BYTES];
749
750 GNUNET_CRYPTO_hash (priv,
751 sizeof (struct GNUNET_CRYPTO_EcdsaPrivateKey),
752 &hc);
753 memcpy (a, &hc, sizeof (struct GNUNET_CRYPTO_EcdhePrivateKey));
754 if (0 != crypto_scalarmult (p, a, pub->q_y))
755 return GNUNET_SYSERR;
756 GNUNET_CRYPTO_hash (p,
757 crypto_scalarmult_BYTES,
758 key_material);
759 return GNUNET_OK;
760}
761
762
763enum GNUNET_GenericReturnValue
764GNUNET_CRYPTO_eddsa_kem_decaps (const struct
765 GNUNET_CRYPTO_EddsaPrivateKey *priv,
766 const struct GNUNET_CRYPTO_EcdhePublicKey *c,
767 struct GNUNET_HashCode *key_material)
768{
769 return GNUNET_CRYPTO_eddsa_ecdh (priv, c, key_material);
770}
771
772
773enum GNUNET_GenericReturnValue
774GNUNET_CRYPTO_ecdsa_ecdh (const struct GNUNET_CRYPTO_EcdsaPrivateKey *priv,
775 const struct GNUNET_CRYPTO_EcdhePublicKey *pub,
776 struct GNUNET_HashCode *key_material)
777{
778 uint8_t p[crypto_scalarmult_BYTES];
779
780 BENCHMARK_START (ecdsa_ecdh);
781 if (0 != crypto_scalarmult (p, priv->d, pub->q_y))
782 return GNUNET_SYSERR;
783 GNUNET_CRYPTO_hash (p,
784 crypto_scalarmult_BYTES,
785 key_material);
786 BENCHMARK_END (ecdsa_ecdh);
787 return GNUNET_OK;
788}
789
790
791enum GNUNET_GenericReturnValue
792GNUNET_CRYPTO_ecdh_eddsa (const struct GNUNET_CRYPTO_EcdhePrivateKey *priv,
793 const struct GNUNET_CRYPTO_EddsaPublicKey *pub,
794 struct GNUNET_HashCode *key_material)
795{
796 uint8_t p[crypto_scalarmult_BYTES];
797 uint8_t curve25510_pk[crypto_scalarmult_BYTES];
798
799 if (0 != crypto_sign_ed25519_pk_to_curve25519 (curve25510_pk, pub->q_y))
800 return GNUNET_SYSERR;
801 if (0 != crypto_scalarmult (p, priv->d, curve25510_pk))
802 return GNUNET_SYSERR;
803 GNUNET_CRYPTO_hash (p, crypto_scalarmult_BYTES, key_material);
804 return GNUNET_OK;
805}
806
807
808enum GNUNET_GenericReturnValue
809GNUNET_CRYPTO_eddsa_kem_encaps (const struct GNUNET_CRYPTO_EddsaPublicKey *pub,
810 struct GNUNET_CRYPTO_EcdhePublicKey *c,
811 struct GNUNET_HashCode *key_material)
812{
813 struct GNUNET_CRYPTO_EcdhePrivateKey sk;
814
815 GNUNET_CRYPTO_ecdhe_key_create (&sk);
816 GNUNET_CRYPTO_ecdhe_key_get_public (&sk, c);
817 return GNUNET_CRYPTO_ecdh_eddsa (&sk, pub, key_material);
818}
819
820
821enum GNUNET_GenericReturnValue
822GNUNET_CRYPTO_ecdsa_fo_kem_encaps (const struct
823 GNUNET_CRYPTO_EcdsaPublicKey *pub,
824 struct GNUNET_CRYPTO_FoKemC *c,
825 struct GNUNET_HashCode *key_material)
826{
827 struct GNUNET_HashCode x;
828 struct GNUNET_HashCode ux;
829 struct GNUNET_HashCode w;
830 struct GNUNET_CRYPTO_EcdhePrivateKey sk;
831
832 // This is the input to the FO OWTF
833 GNUNET_CRYPTO_random_block (GNUNET_CRYPTO_QUALITY_NONCE, &x, sizeof(x));
834
835 // We build our OWTF using a FO-transformation of ElGamal:
836 // U(x)
837 GNUNET_CRYPTO_hash (&x, sizeof (x), &ux);
838 GNUNET_memcpy (&sk, &ux, sizeof (sk));
839
840 // B := g^U(x)
841 GNUNET_CRYPTO_ecdhe_key_get_public (&sk, &c->pub);
842
843 if (GNUNET_SYSERR == GNUNET_CRYPTO_ecdh_ecdsa (&sk, pub, &w))
844 return -1;
845 // w xor x (one-time pad)
846 GNUNET_CRYPTO_hash_xor (&w, &x, &c->y);
847
848 // k := H(x) FIXME: U and H must be different?
849 GNUNET_memcpy (key_material, &ux, sizeof (ux));
850 return GNUNET_OK;
851}
852
853
854enum GNUNET_GenericReturnValue
855GNUNET_CRYPTO_eddsa_fo_kem_encaps (const struct
856 GNUNET_CRYPTO_EddsaPublicKey *pub,
857 struct GNUNET_CRYPTO_FoKemC *c,
858 struct GNUNET_HashCode *key_material)
859{
860 struct GNUNET_HashCode x;
861 struct GNUNET_HashCode ux;
862 struct GNUNET_HashCode w;
863 struct GNUNET_CRYPTO_EcdhePrivateKey sk;
864 enum GNUNET_GenericReturnValue ret;
865
866 // This is the input to the FO OWTF
867 GNUNET_CRYPTO_random_block (GNUNET_CRYPTO_QUALITY_NONCE, &x, sizeof(x));
868
869 // We build our OWTF using a FO-transformation of ElGamal:
870 // U(x)
871 GNUNET_CRYPTO_hash (&x, sizeof (x), &ux);
872 GNUNET_memcpy (&sk, &ux, sizeof (sk));
873
874 // B := g^U(x)
875 GNUNET_CRYPTO_ecdhe_key_get_public (&sk, &c->pub);
876
877 ret = GNUNET_CRYPTO_ecdh_eddsa (&sk, pub, &w);
878 if (GNUNET_OK != ret)
879 return ret;
880 // w xor x (one-time pad)
881 GNUNET_CRYPTO_hash_xor (&w, &x, &c->y);
882
883 // k := H(x) FIXME: U and H must be different?
884 GNUNET_memcpy (key_material, &ux, sizeof (ux));
885 return GNUNET_OK;
886}
887
888
889static enum GNUNET_GenericReturnValue
890fo_kem_decaps (const struct GNUNET_HashCode *w,
891 const struct GNUNET_CRYPTO_FoKemC *c,
892 struct GNUNET_HashCode *key_material)
893{
894 struct GNUNET_HashCode x;
895 struct GNUNET_HashCode ux;
896 struct GNUNET_CRYPTO_EcdhePrivateKey sk;
897 struct GNUNET_CRYPTO_EcdhePublicKey pub_test;
898
899 // w xor x (one-time pad)
900 GNUNET_CRYPTO_hash_xor (w, &c->y, &x);
901
902 // We build our OWTF using a FO-transformation of ElGamal:
903 // U(x)
904 GNUNET_CRYPTO_hash (&x, sizeof (x), &ux);
905 GNUNET_memcpy (&sk, &ux, sizeof (sk));
906
907 // B := g^U(x)
908 GNUNET_CRYPTO_ecdhe_key_get_public (&sk, &pub_test);
909
910 if (0 != memcmp (&pub_test, &c->pub, sizeof (c->pub)))
911 return GNUNET_SYSERR; // Reject
912
913 // k := H(x) FIXME: U and H must be different?
914 GNUNET_memcpy (key_material, &ux, sizeof (ux));
915 return GNUNET_OK;
916}
917
918
919/**
920 * This implementation is not testes/publicly exposed yet
921 */
922enum GNUNET_GenericReturnValue
923GNUNET_CRYPTO_eddsa_fo_kem_decaps (const struct
924 GNUNET_CRYPTO_EddsaPrivateKey *priv,
925 const struct GNUNET_CRYPTO_FoKemC *c,
926 struct GNUNET_HashCode *key_material)
927{
928 struct GNUNET_HashCode w;
929 enum GNUNET_GenericReturnValue ret;
930
931 ret = GNUNET_CRYPTO_eddsa_ecdh (priv, &c->pub, &w);
932 if (GNUNET_OK != ret)
933 return ret;
934 return fo_kem_decaps (&w, c, key_material);
935}
936
937
938enum GNUNET_GenericReturnValue
939GNUNET_CRYPTO_ecdsa_fo_kem_decaps (const struct
940 GNUNET_CRYPTO_EcdsaPrivateKey *priv,
941 struct GNUNET_CRYPTO_FoKemC *c,
942 struct GNUNET_HashCode *key_material)
943{
944 struct GNUNET_HashCode w;
945 enum GNUNET_GenericReturnValue ret;
946
947 ret = GNUNET_CRYPTO_ecdsa_ecdh (priv, &c->pub, &w);
948 if (GNUNET_OK != ret)
949 return ret;
950 return fo_kem_decaps (&w, c, key_material);
951}
952
953
954enum GNUNET_GenericReturnValue
955GNUNET_CRYPTO_ecdh_ecdsa (const struct GNUNET_CRYPTO_EcdhePrivateKey *priv,
956 const struct GNUNET_CRYPTO_EcdsaPublicKey *pub,
957 struct GNUNET_HashCode *key_material)
958{
959 uint8_t p[crypto_scalarmult_BYTES];
960 uint8_t curve25510_pk[crypto_scalarmult_BYTES];
961
962 if (0 != crypto_sign_ed25519_pk_to_curve25519 (curve25510_pk, pub->q_y))
963 return GNUNET_SYSERR;
964 if (0 != crypto_scalarmult (p, priv->d, curve25510_pk))
965 return GNUNET_SYSERR;
966 GNUNET_CRYPTO_hash (p, crypto_scalarmult_BYTES, key_material);
967 return GNUNET_OK;
968}
969
970
971/* end of crypto_ecc.c */
diff --git a/src/lib/util/crypto_ecc_dlog.c b/src/lib/util/crypto_ecc_dlog.c
new file mode 100644
index 000000000..6e333686f
--- /dev/null
+++ b/src/lib/util/crypto_ecc_dlog.c
@@ -0,0 +1,336 @@
1/*
2 This file is part of GNUnet.
3 Copyright (C) 2012, 2013, 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/**
22 * @file util/crypto_ecc_dlog.c
23 * @brief ECC addition and discreate logarithm for small values.
24 * Allows us to use ECC for computations as long as the
25 * result is relativey small.
26 * @author Christian Grothoff
27 */
28
29#include "platform.h"
30#include <gcrypt.h>
31#include "gnunet_util_lib.h"
32
33
34/**
35 * Internal structure used to cache pre-calculated values for DLOG calculation.
36 */
37struct GNUNET_CRYPTO_EccDlogContext
38{
39 /**
40 * Maximum absolute value the calculation supports.
41 */
42 unsigned int max;
43
44 /**
45 * How much memory should we use (relates to the number of entries in the map).
46 */
47 unsigned int mem;
48
49 /**
50 * Map mapping points (here "interpreted" as EdDSA public keys) to
51 * a "void * = long" which corresponds to the numeric value of the
52 * point. As NULL is used to represent "unknown", the actual value
53 * represented by the entry in the map is the "long" minus @e max.
54 */
55 struct GNUNET_CONTAINER_MultiPeerMap *map;
56
57 /**
58 * Context to use for operations on the elliptic curve.
59 */
60 gcry_ctx_t ctx;
61};
62
63
64struct GNUNET_CRYPTO_EccDlogContext *
65GNUNET_CRYPTO_ecc_dlog_prepare (unsigned int max,
66 unsigned int mem)
67{
68 struct GNUNET_CRYPTO_EccDlogContext *edc;
69 int K = ((max + (mem - 1)) / mem);
70
71 GNUNET_assert (max < INT32_MAX);
72 edc = GNUNET_new (struct GNUNET_CRYPTO_EccDlogContext);
73 edc->max = max;
74 edc->mem = mem;
75 edc->map = GNUNET_CONTAINER_multipeermap_create (mem * 2,
76 GNUNET_NO);
77 for (int i = -(int) mem; i <= (int) mem; i++)
78 {
79 struct GNUNET_CRYPTO_EccScalar Ki;
80 struct GNUNET_PeerIdentity key;
81
82 GNUNET_CRYPTO_ecc_scalar_from_int (K * i,
83 &Ki);
84 if (0 == i) /* libsodium does not like to multiply with zero */
85 GNUNET_assert (
86 0 ==
87 crypto_core_ed25519_sub ((unsigned char *) &key,
88 (unsigned char *) &key,
89 (unsigned char *) &key));
90 else
91 GNUNET_assert (
92 0 ==
93 crypto_scalarmult_ed25519_base_noclamp ((unsigned char*) &key,
94 Ki.v));
95 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
96 "K*i: %d (mem=%u, i=%d) => %s\n",
97 K * i,
98 mem,
99 i,
100 GNUNET_i2s (&key));
101 GNUNET_assert (GNUNET_OK ==
102 GNUNET_CONTAINER_multipeermap_put (edc->map,
103 &key,
104 (void *) (long) i + max,
105 GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_ONLY));
106 }
107 return edc;
108}
109
110
111int
112GNUNET_CRYPTO_ecc_dlog (struct GNUNET_CRYPTO_EccDlogContext *edc,
113 const struct GNUNET_CRYPTO_EccPoint *input)
114{
115 unsigned int K = ((edc->max + (edc->mem - 1)) / edc->mem);
116 int res;
117 struct GNUNET_CRYPTO_EccPoint g;
118 struct GNUNET_CRYPTO_EccPoint q;
119 struct GNUNET_CRYPTO_EccPoint nq;
120
121 {
122 struct GNUNET_CRYPTO_EccScalar fact;
123
124 memset (&fact,
125 0,
126 sizeof (fact));
127 sodium_increment (fact.v,
128 sizeof (fact.v));
129 GNUNET_assert (0 ==
130 crypto_scalarmult_ed25519_base_noclamp (g.v,
131 fact.v));
132 }
133 /* make compiler happy: initialize q and nq, technically not needed! */
134 memset (&q,
135 0,
136 sizeof (q));
137 memset (&nq,
138 0,
139 sizeof (nq));
140 res = INT_MAX;
141 for (unsigned int i = 0; i <= edc->max / edc->mem; i++)
142 {
143 struct GNUNET_PeerIdentity key;
144 void *retp;
145
146 GNUNET_assert (sizeof (key) == crypto_scalarmult_BYTES);
147 if (0 == i)
148 {
149 memcpy (&key,
150 input,
151 sizeof (key));
152 }
153 else
154 {
155 memcpy (&key,
156 &q,
157 sizeof (key));
158 }
159 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
160 "Trying offset i=%u): %s\n",
161 i,
162 GNUNET_i2s (&key));
163 retp = GNUNET_CONTAINER_multipeermap_get (edc->map,
164 &key);
165 if (NULL != retp)
166 {
167 res = (((long) retp) - edc->max) * K - i;
168 /* we continue the loop here to make the implementation
169 "constant-time". If we do not care about this, we could just
170 'break' here and do fewer operations... */
171 }
172 if (i == edc->max / edc->mem)
173 break;
174 /* q = q + g */
175 if (0 == i)
176 {
177 GNUNET_assert (0 ==
178 crypto_core_ed25519_add (q.v,
179 input->v,
180 g.v));
181 }
182 else
183 {
184 GNUNET_assert (0 ==
185 crypto_core_ed25519_add (q.v,
186 q.v,
187 g.v));
188 }
189 }
190 return res;
191}
192
193
194void
195GNUNET_CRYPTO_ecc_random_mod_n (struct GNUNET_CRYPTO_EccScalar *r)
196{
197 crypto_core_ed25519_scalar_random (r->v);
198}
199
200
201void
202GNUNET_CRYPTO_ecc_dlog_release (struct GNUNET_CRYPTO_EccDlogContext *edc)
203{
204 GNUNET_CONTAINER_multipeermap_destroy (edc->map);
205 GNUNET_free (edc);
206}
207
208
209void
210GNUNET_CRYPTO_ecc_dexp (int val,
211 struct GNUNET_CRYPTO_EccPoint *r)
212{
213 struct GNUNET_CRYPTO_EccScalar fact;
214
215 GNUNET_CRYPTO_ecc_scalar_from_int (val,
216 &fact);
217 crypto_scalarmult_ed25519_base_noclamp (r->v,
218 fact.v);
219}
220
221
222enum GNUNET_GenericReturnValue
223GNUNET_CRYPTO_ecc_dexp_mpi (const struct GNUNET_CRYPTO_EccScalar *val,
224 struct GNUNET_CRYPTO_EccPoint *r)
225{
226 if (0 ==
227 crypto_scalarmult_ed25519_base_noclamp (r->v,
228 val->v))
229 return GNUNET_OK;
230 return GNUNET_SYSERR;
231}
232
233
234enum GNUNET_GenericReturnValue
235GNUNET_CRYPTO_ecc_add (const struct GNUNET_CRYPTO_EccPoint *a,
236 const struct GNUNET_CRYPTO_EccPoint *b,
237 struct GNUNET_CRYPTO_EccPoint *r)
238{
239 if (0 ==
240 crypto_core_ed25519_add (r->v,
241 a->v,
242 b->v))
243 return GNUNET_OK;
244 return GNUNET_SYSERR;
245}
246
247
248enum GNUNET_GenericReturnValue
249GNUNET_CRYPTO_ecc_pmul_mpi (const struct GNUNET_CRYPTO_EccPoint *p,
250 const struct GNUNET_CRYPTO_EccScalar *val,
251 struct GNUNET_CRYPTO_EccPoint *r)
252{
253 if (0 ==
254 crypto_scalarmult_ed25519_noclamp (r->v,
255 val->v,
256 p->v))
257 return GNUNET_OK;
258 return GNUNET_SYSERR;
259}
260
261
262enum GNUNET_GenericReturnValue
263GNUNET_CRYPTO_ecc_rnd (struct GNUNET_CRYPTO_EccPoint *r,
264 struct GNUNET_CRYPTO_EccPoint *r_inv)
265{
266 struct GNUNET_CRYPTO_EccScalar s;
267 unsigned char inv_s[crypto_scalarmult_ed25519_SCALARBYTES];
268
269 GNUNET_CRYPTO_ecc_random_mod_n (&s);
270 if (0 !=
271 crypto_scalarmult_ed25519_base_noclamp (r->v,
272 s.v))
273 return GNUNET_SYSERR;
274 crypto_core_ed25519_scalar_negate (inv_s,
275 s.v);
276 if (0 !=
277 crypto_scalarmult_ed25519_base_noclamp (r_inv->v,
278 inv_s))
279 return GNUNET_SYSERR;
280 return GNUNET_OK;
281}
282
283
284void
285GNUNET_CRYPTO_ecc_rnd_mpi (struct GNUNET_CRYPTO_EccScalar *r,
286 struct GNUNET_CRYPTO_EccScalar *r_neg)
287{
288 GNUNET_CRYPTO_ecc_random_mod_n (r);
289 crypto_core_ed25519_scalar_negate (r_neg->v,
290 r->v);
291}
292
293
294void
295GNUNET_CRYPTO_ecc_scalar_from_int (int64_t val,
296 struct GNUNET_CRYPTO_EccScalar *r)
297{
298 unsigned char fact[crypto_scalarmult_ed25519_SCALARBYTES];
299 uint64_t valBe;
300
301 GNUNET_assert (sizeof (*r) == sizeof (fact));
302 if (val < 0)
303 {
304 if (INT64_MIN == val)
305 valBe = GNUNET_htonll ((uint64_t) INT64_MAX);
306 else
307 valBe = GNUNET_htonll ((uint64_t) (-val));
308 }
309 else
310 {
311 valBe = GNUNET_htonll ((uint64_t) val);
312 }
313 memset (fact,
314 0,
315 sizeof (fact));
316 for (unsigned int i = 0; i < sizeof (val); i++)
317 fact[i] = ((unsigned char*) &valBe)[sizeof (val) - 1 - i];
318 if (val < 0)
319 {
320 if (INT64_MIN == val)
321 /* See above: fact is one too small, increment now that we can */
322 sodium_increment (fact,
323 sizeof (fact));
324 crypto_core_ed25519_scalar_negate (r->v,
325 fact);
326 }
327 else
328 {
329 memcpy (r,
330 fact,
331 sizeof (fact));
332 }
333}
334
335
336/* end of crypto_ecc_dlog.c */
diff --git a/src/lib/util/crypto_ecc_gnsrecord.c b/src/lib/util/crypto_ecc_gnsrecord.c
new file mode 100644
index 000000000..fb8ba3ac9
--- /dev/null
+++ b/src/lib/util/crypto_ecc_gnsrecord.c
@@ -0,0 +1,451 @@
1/*
2 This file is part of GNUnet.
3 Copyright (C) 2012, 2013, 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/**
22 * @file util/crypto_ecc_gnsrecord.c
23 * @brief public key cryptography (ECC) for GNS records (LSD0001)
24 * @author Christian Grothoff
25 * @author Florian Dold
26 * @author Martin Schanzenbach
27 */
28
29#include "platform.h"
30#include <gcrypt.h>
31#include <sodium.h>
32#include "gnunet_util_lib.h"
33
34#define CURVE "Ed25519"
35
36/**
37 * Derive the 'h' value for key derivation, where
38 * 'h = H(l,P)'.
39 *
40 * @param pub public key for deriviation
41 * @param pubsize the size of the public key
42 * @param label label for deriviation
43 * @param context additional context to use for HKDF of 'h';
44 * typically the name of the subsystem/application
45 * @param hc where to write the result
46 */
47void
48derive_h (const void *pub,
49 size_t pubsize,
50 const char *label,
51 const char *context,
52 struct GNUNET_HashCode *hc)
53{
54 static const char *const salt = "key-derivation";
55
56 GNUNET_CRYPTO_kdf (hc,
57 sizeof(*hc),
58 salt,
59 strlen (salt),
60 pub,
61 pubsize,
62 label,
63 strlen (label),
64 context,
65 strlen (context),
66 NULL,
67 0);
68}
69
70
71enum GNUNET_GenericReturnValue
72GNUNET_CRYPTO_eddsa_sign_derived (
73 const struct GNUNET_CRYPTO_EddsaPrivateKey *pkey,
74 const char *label,
75 const char *context,
76 const struct GNUNET_CRYPTO_EccSignaturePurpose *purpose,
77 struct GNUNET_CRYPTO_EddsaSignature *sig)
78{
79 struct GNUNET_CRYPTO_EddsaPrivateScalar priv;
80 crypto_hash_sha512_state hs;
81 unsigned char sk[64];
82 unsigned char r[64];
83 unsigned char hram[64];
84 unsigned char R[32];
85 unsigned char zk[32];
86 unsigned char tmp[32];
87
88 /**
89 * Derive the private key
90 */
91 GNUNET_CRYPTO_eddsa_private_key_derive (pkey,
92 label,
93 context,
94 &priv);
95
96 crypto_hash_sha512_init (&hs);
97
98 /**
99 * Instead of expanding the private here, we already
100 * have the secret scalar as input. Use it.
101 * Note that sk is not plain SHA512 (d).
102 * sk[0..31] contains the derived private scalar
103 * sk[0..31] = h * SHA512 (d)[0..31]
104 * sk[32..63] = SHA512 (d)[32..63]
105 */
106 memcpy (sk, priv.s, 64);
107
108 /**
109 * Calculate the derived zone key zk' from the
110 * derived private scalar.
111 */
112 crypto_scalarmult_ed25519_base_noclamp (zk,
113 sk);
114
115 /**
116 * Calculate r:
117 * r = SHA512 (sk[32..63] | M)
118 * where M is our message (purpose).
119 * Note that sk[32..63] is the other half of the
120 * expansion from the original, non-derived private key
121 * "d".
122 */
123 crypto_hash_sha512_update (&hs, sk + 32, 32);
124 crypto_hash_sha512_update (&hs, (uint8_t*) purpose, ntohl (purpose->size));
125 crypto_hash_sha512_final (&hs, r);
126
127 /**
128 * Temporarily put zk into S
129 */
130 memcpy (sig->s, zk, 32);
131
132 /**
133 * Reduce the scalar value r
134 */
135 unsigned char r_mod[64];
136 crypto_core_ed25519_scalar_reduce (r_mod, r);
137
138 /**
139 * Calculate R := r * G of the signature
140 */
141 crypto_scalarmult_ed25519_base_noclamp (R, r_mod);
142 memcpy (sig->r, R, sizeof (R));
143
144 /**
145 * Calculate
146 * hram := SHA512 (R | zk' | M)
147 */
148 crypto_hash_sha512_init (&hs);
149 crypto_hash_sha512_update (&hs, (uint8_t*) sig, 64);
150 crypto_hash_sha512_update (&hs, (uint8_t*) purpose,
151 ntohl (purpose->size));
152 crypto_hash_sha512_final (&hs, hram);
153
154 /**
155 * Reduce the resulting scalar value
156 */
157 unsigned char hram_mod[64];
158 crypto_core_ed25519_scalar_reduce (hram_mod, hram);
159
160 /**
161 * Calculate
162 * S := r + hram * s mod L
163 */
164 crypto_core_ed25519_scalar_mul (tmp, hram_mod, sk);
165 crypto_core_ed25519_scalar_add (sig->s, tmp, r_mod);
166
167 sodium_memzero (sk, sizeof (sk));
168 sodium_memzero (r, sizeof (r));
169 sodium_memzero (r_mod, sizeof (r_mod));
170 return GNUNET_OK;
171}
172
173
174enum GNUNET_GenericReturnValue
175GNUNET_CRYPTO_ecdsa_sign_derived (
176 const struct GNUNET_CRYPTO_EcdsaPrivateKey *priv,
177 const char *label,
178 const char *context,
179 const struct GNUNET_CRYPTO_EccSignaturePurpose *purpose,
180 struct GNUNET_CRYPTO_EcdsaSignature *sig)
181{
182 struct GNUNET_CRYPTO_EcdsaPrivateKey *key;
183 enum GNUNET_GenericReturnValue res;
184 key = GNUNET_CRYPTO_ecdsa_private_key_derive (priv,
185 label,
186 context);
187 res = GNUNET_CRYPTO_ecdsa_sign_ (key,
188 purpose,
189 sig);
190 GNUNET_free (key);
191 return res;
192}
193
194
195struct GNUNET_CRYPTO_EcdsaPrivateKey *
196GNUNET_CRYPTO_ecdsa_private_key_derive (
197 const struct GNUNET_CRYPTO_EcdsaPrivateKey *priv,
198 const char *label,
199 const char *context)
200{
201 struct GNUNET_CRYPTO_EcdsaPublicKey pub;
202 struct GNUNET_CRYPTO_EcdsaPrivateKey *ret;
203 struct GNUNET_HashCode hc;
204 uint8_t dc[32];
205 gcry_mpi_t h;
206 gcry_mpi_t x;
207 gcry_mpi_t d;
208 gcry_mpi_t n;
209 gcry_ctx_t ctx;
210
211 GNUNET_assert (0 == gcry_mpi_ec_new (&ctx, NULL, CURVE));
212
213 n = gcry_mpi_ec_get_mpi ("n", ctx, 1);
214 GNUNET_CRYPTO_ecdsa_key_get_public (priv, &pub);
215
216 derive_h (&pub, sizeof (pub), label, context, &hc);
217 GNUNET_CRYPTO_mpi_scan_unsigned (&h, (unsigned char *) &hc, sizeof(hc));
218
219 /* Convert to big endian for libgcrypt */
220 for (size_t i = 0; i < 32; i++)
221 dc[i] = priv->d[31 - i];
222 GNUNET_CRYPTO_mpi_scan_unsigned (&x, dc, sizeof(dc));
223 d = gcry_mpi_new (256);
224 gcry_mpi_mulm (d, h, x, n);
225 gcry_mpi_release (h);
226 gcry_mpi_release (x);
227 gcry_mpi_release (n);
228 gcry_ctx_release (ctx);
229 ret = GNUNET_new (struct GNUNET_CRYPTO_EcdsaPrivateKey);
230 GNUNET_CRYPTO_mpi_print_unsigned (dc, sizeof(dc), d);
231 /* Convert to big endian for libgcrypt */
232 for (size_t i = 0; i < 32; i++)
233 ret->d[i] = dc[31 - i];
234 sodium_memzero (dc, sizeof(dc));
235 gcry_mpi_release (d);
236 return ret;
237}
238
239
240void
241GNUNET_CRYPTO_ecdsa_public_key_derive (
242 const struct GNUNET_CRYPTO_EcdsaPublicKey *pub,
243 const char *label,
244 const char *context,
245 struct GNUNET_CRYPTO_EcdsaPublicKey *result)
246{
247 struct GNUNET_HashCode hc;
248 gcry_ctx_t ctx;
249 gcry_mpi_t q_y;
250 gcry_mpi_t h;
251 gcry_mpi_t n;
252 gcry_mpi_t h_mod_n;
253 gcry_mpi_point_t q;
254 gcry_mpi_point_t v;
255
256 GNUNET_assert (0 == gcry_mpi_ec_new (&ctx, NULL, CURVE));
257
258 /* obtain point 'q' from original public key. The provided 'q' is
259 compressed thus we first store it in the context and then get it
260 back as a (decompresssed) point. */
261 q_y = gcry_mpi_set_opaque_copy (NULL, pub->q_y, 8 * sizeof(pub->q_y));
262 GNUNET_assert (NULL != q_y);
263 GNUNET_assert (0 == gcry_mpi_ec_set_mpi ("q", q_y, ctx));
264 gcry_mpi_release (q_y);
265 q = gcry_mpi_ec_get_point ("q", ctx, 0);
266 GNUNET_assert (q);
267
268 /* calculate h_mod_n = h % n */
269 derive_h (pub, sizeof (*pub), label, context, &hc);
270 GNUNET_CRYPTO_mpi_scan_unsigned (&h, (unsigned char *) &hc, sizeof(hc));
271 n = gcry_mpi_ec_get_mpi ("n", ctx, 1);
272 h_mod_n = gcry_mpi_new (256);
273 gcry_mpi_mod (h_mod_n, h, n);
274 /* calculate v = h_mod_n * q */
275 v = gcry_mpi_point_new (0);
276 gcry_mpi_ec_mul (v, h_mod_n, q, ctx);
277 gcry_mpi_release (h_mod_n);
278 gcry_mpi_release (h);
279 gcry_mpi_release (n);
280 gcry_mpi_point_release (q);
281
282 /* convert point 'v' to public key that we return */
283 GNUNET_assert (0 == gcry_mpi_ec_set_point ("q", v, ctx));
284 gcry_mpi_point_release (v);
285 q_y = gcry_mpi_ec_get_mpi ("q@eddsa", ctx, 0);
286 GNUNET_assert (q_y);
287 GNUNET_CRYPTO_mpi_print_unsigned (result->q_y, sizeof(result->q_y), q_y);
288 gcry_mpi_release (q_y);
289 gcry_ctx_release (ctx);
290}
291
292
293void
294GNUNET_CRYPTO_eddsa_private_key_derive (
295 const struct GNUNET_CRYPTO_EddsaPrivateKey *priv,
296 const char *label,
297 const char *context,
298 struct GNUNET_CRYPTO_EddsaPrivateScalar *result)
299{
300 struct GNUNET_CRYPTO_EddsaPublicKey pub;
301 struct GNUNET_HashCode hc;
302 uint8_t dc[32];
303 unsigned char sk[64];
304 gcry_mpi_t h;
305 gcry_mpi_t h_mod_L;
306 gcry_mpi_t a;
307 gcry_mpi_t d;
308 gcry_mpi_t L;
309 gcry_ctx_t ctx;
310
311 /**
312 * Libsodium does not offer an API with arbitrary arithmetic.
313 * Hence we have to use libgcrypt here.
314 */
315 GNUNET_assert (0 == gcry_mpi_ec_new (&ctx, NULL, "Ed25519"));
316
317 /**
318 * Get our modulo L
319 */
320 L = gcry_mpi_ec_get_mpi ("n", ctx, 1);
321 GNUNET_CRYPTO_eddsa_key_get_public (priv, &pub);
322
323 /**
324 * This is the standard private key expansion in Ed25519.
325 * The first 32 octets are used as a little-endian private
326 * scalar.
327 * We derive this scalar using our "h".
328 */
329 crypto_hash_sha512 (sk, priv->d, 32);
330 sk[0] &= 248;
331 sk[31] &= 127;
332 sk[31] |= 64;
333
334 /**
335 * Get h mod L
336 */
337 derive_h (&pub, sizeof (pub), label, context, &hc);
338 GNUNET_CRYPTO_mpi_scan_unsigned (&h, (unsigned char *) &hc, sizeof(hc));
339 h_mod_L = gcry_mpi_new (256);
340 gcry_mpi_mod (h_mod_L, h, L);
341 /* Convert scalar to big endian for libgcrypt */
342 for (size_t i = 0; i < 32; i++)
343 dc[i] = sk[31 - i];
344
345 /**
346 * dc now contains the private scalar "a".
347 * We calculate:
348 * d' := h * a mod L
349 */
350 GNUNET_CRYPTO_mpi_scan_unsigned (&a, dc, sizeof(dc)); // a
351 d = gcry_mpi_new (256);
352 gcry_mpi_mulm (d, h_mod_L, a, L); // d := h * a mod L
353 gcry_mpi_release (h);
354 gcry_mpi_release (a);
355 gcry_mpi_release (L);
356 gcry_mpi_release (h_mod_L);
357 gcry_ctx_release (ctx);
358 GNUNET_CRYPTO_mpi_print_unsigned (dc, sizeof(dc), d);
359 /**
360 * We hash the derived "h" parameter with the
361 * other half of the expanded private key. This ensures
362 * that for signature generation, the "R" is derived from
363 * the same derivation path as "h" and is not reused.
364 */
365 crypto_hash_sha256_state hs;
366 crypto_hash_sha256_init (&hs);
367 crypto_hash_sha256_update (&hs, sk + 32, 32);
368 crypto_hash_sha256_update (&hs, (unsigned char*) &hc, sizeof (hc));
369 crypto_hash_sha256_final (&hs, result->s + 32);
370 // memcpy (result->s, sk, sizeof (sk));
371 /* Convert to little endian for libsodium */
372 for (size_t i = 0; i < 32; i++)
373 result->s[i] = dc[31 - i];
374
375 sodium_memzero (dc, sizeof(dc));
376 gcry_mpi_release (d);
377}
378
379
380void
381GNUNET_CRYPTO_eddsa_public_key_derive (
382 const struct GNUNET_CRYPTO_EddsaPublicKey *pub,
383 const char *label,
384 const char *context,
385 struct GNUNET_CRYPTO_EddsaPublicKey *result)
386{
387 struct GNUNET_HashCode hc;
388 gcry_ctx_t ctx;
389 gcry_mpi_t q_y;
390 gcry_mpi_t h;
391 gcry_mpi_t n;
392 gcry_mpi_t h_mod_n;
393 gcry_mpi_point_t q;
394 gcry_mpi_point_t v;
395
396 GNUNET_assert (0 == gcry_mpi_ec_new (&ctx, NULL, "Ed25519"));
397
398 /* obtain point 'q' from original public key. The provided 'q' is
399 compressed thus we first store it in the context and then get it
400 back as a (decompresssed) point. */
401 q_y = gcry_mpi_set_opaque_copy (NULL, pub->q_y, 8 * sizeof(pub->q_y));
402 GNUNET_assert (NULL != q_y);
403 GNUNET_assert (0 == gcry_mpi_ec_set_mpi ("q", q_y, ctx));
404 gcry_mpi_release (q_y);
405 q = gcry_mpi_ec_get_point ("q", ctx, 0);
406 GNUNET_assert (q);
407
408 /* calculate h_mod_n = h % n */
409 derive_h (pub, sizeof (*pub), label, context, &hc);
410 GNUNET_CRYPTO_mpi_scan_unsigned (&h, (unsigned char *) &hc, sizeof(hc));
411
412 n = gcry_mpi_ec_get_mpi ("n", ctx, 1);
413 h_mod_n = gcry_mpi_new (256);
414 gcry_mpi_mod (h_mod_n, h, n);
415
416 /* calculate v = h_mod_n * q */
417 v = gcry_mpi_point_new (0);
418 gcry_mpi_ec_mul (v, h_mod_n, q, ctx);
419 gcry_mpi_release (h_mod_n);
420 gcry_mpi_release (h);
421 gcry_mpi_release (n);
422 gcry_mpi_point_release (q);
423
424 /* convert point 'v' to public key that we return */
425 GNUNET_assert (0 == gcry_mpi_ec_set_point ("q", v, ctx));
426 gcry_mpi_point_release (v);
427 q_y = gcry_mpi_ec_get_mpi ("q@eddsa", ctx, 0);
428 GNUNET_assert (q_y);
429 GNUNET_CRYPTO_mpi_print_unsigned (result->q_y, sizeof(result->q_y), q_y);
430 gcry_mpi_release (q_y);
431 gcry_ctx_release (ctx);
432
433}
434
435
436void
437GNUNET_CRYPTO_eddsa_key_get_public_from_scalar (
438 const struct GNUNET_CRYPTO_EddsaPrivateScalar *priv,
439 struct GNUNET_CRYPTO_EddsaPublicKey *pkey)
440{
441 unsigned char sk[32];
442
443 memcpy (sk, priv->s, 32);
444
445 /**
446 * Calculate the derived zone key zk' from the
447 * derived private scalar.
448 */
449 crypto_scalarmult_ed25519_base_noclamp (pkey->q_y,
450 sk);
451}
diff --git a/src/lib/util/crypto_ecc_setup.c b/src/lib/util/crypto_ecc_setup.c
new file mode 100644
index 000000000..07e28f89d
--- /dev/null
+++ b/src/lib/util/crypto_ecc_setup.c
@@ -0,0 +1,351 @@
1/*
2 This file is part of GNUnet.
3 Copyright (C) 2012, 2013, 2015, 2020, 2023 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/crypto_ecc_setup.c
23 * @brief helper function for easy EdDSA key setup
24 * @author Christian Grothoff
25 */
26
27#include "platform.h"
28#include <gcrypt.h>
29#include "gnunet_util_lib.h"
30
31#define LOG(kind, ...) GNUNET_log_from (kind, "util-crypto-ecc", __VA_ARGS__)
32
33#define LOG_STRERROR(kind, syscall) \
34 GNUNET_log_from_strerror (kind, "util-crypto-ecc", syscall)
35
36#define LOG_STRERROR_FILE(kind, syscall, filename) \
37 GNUNET_log_from_strerror_file (kind, "util-crypto-ecc", syscall, filename)
38
39/**
40 * Log an error message at log-level 'level' that indicates
41 * a failure of the command 'cmd' with the message given
42 * by gcry_strerror(rc).
43 */
44#define LOG_GCRY(level, cmd, rc) \
45 do \
46 { \
47 LOG (level, \
48 _ ("`%s' failed at %s:%d with error: %s\n"), \
49 cmd, \
50 __FILE__, \
51 __LINE__, \
52 gcry_strerror (rc)); \
53 } while (0)
54
55
56/**
57 * Read file to @a buf. Fails if the file does not exist or
58 * does not have precisely @a buf_size bytes.
59 *
60 * @param filename file to read
61 * @param[out] buf where to write the file contents
62 * @param buf_size number of bytes in @a buf
63 * @return #GNUNET_OK on success
64 */
65static enum GNUNET_GenericReturnValue
66read_from_file (const char *filename,
67 void *buf,
68 size_t buf_size)
69{
70 int fd;
71 struct stat sb;
72
73 fd = open (filename,
74 O_RDONLY);
75 if (-1 == fd)
76 {
77 memset (buf,
78 0,
79 buf_size);
80 return GNUNET_SYSERR;
81 }
82 if (0 != fstat (fd,
83 &sb))
84 {
85 GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING,
86 "stat",
87 filename);
88 GNUNET_assert (0 == close (fd));
89 memset (buf,
90 0,
91 buf_size);
92 return GNUNET_SYSERR;
93 }
94 if (sb.st_size != buf_size)
95 {
96 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
97 "File `%s' has wrong size (%llu), expected %llu bytes\n",
98 filename,
99 (unsigned long long) sb.st_size,
100 (unsigned long long) buf_size);
101 GNUNET_assert (0 == close (fd));
102 memset (buf,
103 0,
104 buf_size);
105 return GNUNET_SYSERR;
106 }
107 if (buf_size !=
108 read (fd,
109 buf,
110 buf_size))
111 {
112 GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING,
113 "read",
114 filename);
115 GNUNET_assert (0 == close (fd));
116 memset (buf,
117 0,
118 buf_size);
119 return GNUNET_SYSERR;
120 }
121 GNUNET_assert (0 == close (fd));
122 return GNUNET_OK;
123}
124
125
126/**
127 * @ingroup crypto
128 * @brief Create a new private key by reading it from a file.
129 *
130 * If the files does not exist and @a do_create is set, creates a new key and
131 * write it to the file.
132 *
133 * If the contents of the file are invalid, an error is returned.
134 *
135 * @param filename name of file to use to store the key
136 * @param do_create should a file be created?
137 * @param[out] pkey set to the private key from @a filename on success
138 * @return - #GNUNET_OK on success,
139 * - #GNUNET_NO if @a do_create was set but we found an existing file,
140 * - #GNUNET_SYSERR on failure _or_ if the file didn't exist and @a
141 * do_create was not set
142 */
143enum GNUNET_GenericReturnValue
144GNUNET_CRYPTO_eddsa_key_from_file (const char *filename,
145 int do_create,
146 struct GNUNET_CRYPTO_EddsaPrivateKey *pkey)
147{
148 enum GNUNET_GenericReturnValue ret;
149
150 if (GNUNET_OK ==
151 read_from_file (filename,
152 pkey,
153 sizeof (*pkey)))
154 {
155 /* file existed, report that we didn't create it... */
156 return (do_create) ? GNUNET_NO : GNUNET_OK;
157 }
158 else if (! do_create)
159 {
160 return GNUNET_SYSERR;
161 }
162
163 GNUNET_CRYPTO_eddsa_key_create (pkey);
164 ret = GNUNET_DISK_fn_write (filename,
165 pkey,
166 sizeof (*pkey),
167 GNUNET_DISK_PERM_USER_READ);
168 if ( (GNUNET_OK == ret) ||
169 (GNUNET_SYSERR == ret) )
170 return ret;
171 /* maybe another process succeeded in the meantime, try reading one more time */
172 if (GNUNET_OK ==
173 read_from_file (filename,
174 pkey,
175 sizeof (*pkey)))
176 {
177 /* file existed, report that *we* didn't create it... */
178 return GNUNET_NO;
179 }
180 /* give up */
181 return GNUNET_SYSERR;
182}
183
184
185/**
186 * @ingroup crypto
187 * @brief Create a new private key by reading it from a file.
188 *
189 * If the files does not exist and @a do_create is set, creates a new key and
190 * write it to the file.
191 *
192 * If the contents of the file are invalid, an error is returned.
193 *
194 * @param filename name of file to use to store the key
195 * @param do_create should a file be created?
196 * @param[out] pkey set to the private key from @a filename on success
197 * @return #GNUNET_OK on success, #GNUNET_NO if @a do_create was set but
198 * we found an existing file, #GNUNET_SYSERR on failure
199 */
200enum GNUNET_GenericReturnValue
201GNUNET_CRYPTO_ecdsa_key_from_file (const char *filename,
202 int do_create,
203 struct GNUNET_CRYPTO_EcdsaPrivateKey *pkey)
204{
205 if (GNUNET_OK ==
206 read_from_file (filename,
207 pkey,
208 sizeof (*pkey)))
209 {
210 /* file existed, report that we didn't create it... */
211 return (do_create) ? GNUNET_NO : GNUNET_OK;
212 }
213 else if (! do_create)
214 {
215 return GNUNET_SYSERR;
216 }
217 GNUNET_CRYPTO_ecdsa_key_create (pkey);
218 if (GNUNET_OK ==
219 GNUNET_DISK_fn_write (filename,
220 pkey,
221 sizeof (*pkey),
222 GNUNET_DISK_PERM_USER_READ))
223 return GNUNET_OK;
224 /* maybe another process succeeded in the meantime, try reading one more time */
225 if (GNUNET_OK ==
226 read_from_file (filename,
227 pkey,
228 sizeof (*pkey)))
229 {
230 /* file existed, report that *we* didn't create it... */
231 return GNUNET_NO;
232 }
233 /* give up */
234 return GNUNET_SYSERR;
235}
236
237
238/**
239 * Create a new private key by reading our peer's key from
240 * the file specified in the configuration.
241 *
242 * @param cfg the configuration to use
243 * @return new private key, NULL on error (for example,
244 * permission denied)
245 */
246struct GNUNET_CRYPTO_EddsaPrivateKey *
247GNUNET_CRYPTO_eddsa_key_create_from_configuration (
248 const struct GNUNET_CONFIGURATION_Handle *cfg)
249{
250 struct GNUNET_CRYPTO_EddsaPrivateKey *priv;
251 char *fn;
252
253 if (GNUNET_OK !=
254 GNUNET_CONFIGURATION_get_value_filename (cfg,
255 "PEER",
256 "PRIVATE_KEY",
257 &fn))
258 return NULL;
259 priv = GNUNET_new (struct GNUNET_CRYPTO_EddsaPrivateKey);
260 if (GNUNET_SYSERR == GNUNET_CRYPTO_eddsa_key_from_file (fn,
261 GNUNET_YES,
262 priv))
263 {
264 GNUNET_free (fn);
265 GNUNET_free (priv);
266 return NULL;
267 }
268 GNUNET_free (fn);
269 return priv;
270}
271
272
273enum GNUNET_GenericReturnValue
274GNUNET_CRYPTO_get_peer_identity (const struct GNUNET_CONFIGURATION_Handle *cfg,
275 struct GNUNET_PeerIdentity *dst)
276{
277 struct GNUNET_CRYPTO_EddsaPrivateKey *priv;
278
279 if (NULL == (priv = GNUNET_CRYPTO_eddsa_key_create_from_configuration (cfg)))
280 {
281 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
282 _ ("Could not load peer's private key\n"));
283 return GNUNET_SYSERR;
284 }
285 GNUNET_CRYPTO_eddsa_key_get_public (priv,
286 &dst->public_key);
287 GNUNET_free (priv);
288 return GNUNET_OK;
289}
290
291
292enum GNUNET_GenericReturnValue
293GNUNET_CRYPTO_sign_by_peer_identity (const struct
294 GNUNET_CONFIGURATION_Handle *cfg,
295 const struct
296 GNUNET_CRYPTO_EccSignaturePurpose *purpose,
297 struct GNUNET_CRYPTO_EddsaSignature *sig)
298{
299 struct GNUNET_CRYPTO_EddsaPrivateKey *priv;
300 enum GNUNET_GenericReturnValue result;
301
302 if (NULL == (priv = GNUNET_CRYPTO_eddsa_key_create_from_configuration (cfg)))
303 {
304 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
305 _ ("Could not load peer's private key\n"));
306 return GNUNET_SYSERR;
307 }
308
309 result = GNUNET_CRYPTO_eddsa_sign_ (priv, purpose, sig);
310 GNUNET_CRYPTO_eddsa_key_clear (priv);
311 return result;
312}
313
314
315enum GNUNET_GenericReturnValue
316GNUNET_CRYPTO_verify_peer_identity (uint32_t purpose,
317 const struct
318 GNUNET_CRYPTO_EccSignaturePurpose *validate,
319 const struct
320 GNUNET_CRYPTO_EddsaSignature *sig,
321 const struct GNUNET_PeerIdentity *identity)
322{
323 return GNUNET_CRYPTO_eddsa_verify_ (purpose, validate, sig,
324 &identity->public_key);
325}
326
327
328/**
329 * Setup a key file for a peer given the name of the
330 * configuration file (!). This function is used so that
331 * at a later point code can be certain that reading a
332 * key is fast (for example in time-dependent testcases).
333 *
334 * @param cfg_name name of the configuration file to use
335 */
336void
337GNUNET_CRYPTO_eddsa_setup_key (const char *cfg_name)
338{
339 struct GNUNET_CONFIGURATION_Handle *cfg;
340 struct GNUNET_CRYPTO_EddsaPrivateKey *priv;
341
342 cfg = GNUNET_CONFIGURATION_create ();
343 (void) GNUNET_CONFIGURATION_load (cfg, cfg_name);
344 priv = GNUNET_CRYPTO_eddsa_key_create_from_configuration (cfg);
345 if (NULL != priv)
346 GNUNET_free (priv);
347 GNUNET_CONFIGURATION_destroy (cfg);
348}
349
350
351/* end of crypto_ecc_setup.c */
diff --git a/src/lib/util/crypto_edx25519.c b/src/lib/util/crypto_edx25519.c
new file mode 100644
index 000000000..2d9a76aa3
--- /dev/null
+++ b/src/lib/util/crypto_edx25519.c
@@ -0,0 +1,354 @@
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/**
22 * @file util/crypto_edx25519.c
23 * @brief An variant of EdDSA which allows for iterative derivation of key pairs.
24 * @author Özgür Kesim
25 * @author Christian Grothoff
26 * @author Florian Dold
27 * @author Martin Schanzenbach
28 */
29
30#include "platform.h"
31#include <gcrypt.h>
32#include <sodium.h>
33#include "gnunet_util_lib.h"
34
35#define CURVE "Ed25519"
36
37void
38GNUNET_CRYPTO_edx25519_key_clear (struct GNUNET_CRYPTO_Edx25519PrivateKey *pk)
39{
40 memset (pk, 0, sizeof(struct GNUNET_CRYPTO_Edx25519PrivateKey));
41}
42
43
44void
45GNUNET_CRYPTO_edx25519_key_create_from_seed (
46 const void *seed,
47 size_t seedsize,
48 struct GNUNET_CRYPTO_Edx25519PrivateKey *pk)
49{
50
51 GNUNET_static_assert (sizeof(*pk) == sizeof(struct GNUNET_HashCode));
52 GNUNET_CRYPTO_hash (seed,
53 seedsize,
54 (struct GNUNET_HashCode *) pk);
55
56 /* Clamp the first half of the key. The second half is used in the signature
57 * process. */
58 pk->a[0] &= 248;
59 pk->a[31] &= 127;
60 pk->a[31] |= 64;
61}
62
63
64void
65GNUNET_CRYPTO_edx25519_key_create (
66 struct GNUNET_CRYPTO_Edx25519PrivateKey *pk)
67{
68 char seed[256 / 8];
69 GNUNET_CRYPTO_random_block (GNUNET_CRYPTO_QUALITY_NONCE,
70 seed,
71 sizeof (seed));
72 GNUNET_CRYPTO_edx25519_key_create_from_seed (seed,
73 sizeof(seed),
74 pk);
75}
76
77
78void
79GNUNET_CRYPTO_edx25519_key_get_public (
80 const struct GNUNET_CRYPTO_Edx25519PrivateKey *priv,
81 struct GNUNET_CRYPTO_Edx25519PublicKey *pub)
82{
83 crypto_scalarmult_ed25519_base_noclamp (pub->q_y,
84 priv->a);
85}
86
87
88/**
89 * This function operates the basically same way as the signature function for
90 * EdDSA. But instead of expanding a private seed (which is usually the case
91 * for crypto APIs) and using the resulting scalars, it takes the scalars
92 * directly from Edx25519PrivateKey. We require this functionality in order to
93 * use derived private keys for signatures.
94 *
95 * The resulting signature is a standard EdDSA signature
96 * which can be verified using the usual APIs.
97 *
98 * @param priv the private key (containing two scalars .a and .b)
99 * @param purp the signature purpose
100 * @param sig the resulting signature
101 */
102enum GNUNET_GenericReturnValue
103GNUNET_CRYPTO_edx25519_sign_ (
104 const struct GNUNET_CRYPTO_Edx25519PrivateKey *priv,
105 const struct GNUNET_CRYPTO_EccSignaturePurpose *purpose,
106 struct GNUNET_CRYPTO_Edx25519Signature *sig)
107{
108
109 crypto_hash_sha512_state hs;
110 unsigned char r[64];
111 unsigned char hram[64];
112 unsigned char P[32];
113 unsigned char r_mod[64];
114 unsigned char R[32];
115 unsigned char tmp[32];
116
117 crypto_hash_sha512_init (&hs);
118
119 /**
120 * Calculate the public key P from the private scalar in the key.
121 */
122 crypto_scalarmult_ed25519_base_noclamp (P,
123 priv->a);
124
125 /**
126 * Calculate r:
127 * r = SHA512 (b ∥ M)
128 * where M is our message (purpose).
129 */
130 crypto_hash_sha512_update (&hs,
131 priv->b,
132 sizeof(priv->b));
133 crypto_hash_sha512_update (&hs,
134 (uint8_t*) purpose,
135 ntohl (purpose->size));
136 crypto_hash_sha512_final (&hs,
137 r);
138
139 /**
140 * Temporarily put P into S
141 */
142 memcpy (sig->s, P, 32);
143
144 /**
145 * Reduce the scalar value r
146 */
147 crypto_core_ed25519_scalar_reduce (r_mod, r);
148
149 /**
150 * Calculate R := r * G of the signature
151 */
152 crypto_scalarmult_ed25519_base_noclamp (R, r_mod);
153 memcpy (sig->r, R, sizeof (R));
154
155 /**
156 * Calculate
157 * hram := SHA512 (R ∥ P ∥ M)
158 */
159 crypto_hash_sha512_init (&hs);
160 crypto_hash_sha512_update (&hs, (uint8_t*) sig, 64);
161 crypto_hash_sha512_update (&hs, (uint8_t*) purpose,
162 ntohl (purpose->size));
163 crypto_hash_sha512_final (&hs, hram);
164
165 /**
166 * Reduce the resulting scalar value
167 */
168 unsigned char hram_mod[64];
169 crypto_core_ed25519_scalar_reduce (hram_mod, hram);
170
171 /**
172 * Calculate
173 * S := r + hram * s mod L
174 */
175 crypto_core_ed25519_scalar_mul (tmp, hram_mod, priv->a);
176 crypto_core_ed25519_scalar_add (sig->s, tmp, r_mod);
177
178 sodium_memzero (r, sizeof (r));
179 sodium_memzero (r_mod, sizeof (r_mod));
180
181 return GNUNET_OK;
182}
183
184
185enum GNUNET_GenericReturnValue
186GNUNET_CRYPTO_edx25519_verify_ (
187 uint32_t purpose,
188 const struct GNUNET_CRYPTO_EccSignaturePurpose *validate,
189 const struct GNUNET_CRYPTO_Edx25519Signature *sig,
190 const struct GNUNET_CRYPTO_Edx25519PublicKey *pub)
191{
192 const unsigned char *m = (const void *) validate;
193 size_t mlen = ntohl (validate->size);
194 const unsigned char *s = (const void *) sig;
195
196 int res;
197
198 if (purpose != ntohl (validate->purpose))
199 return GNUNET_SYSERR; /* purpose mismatch */
200
201 res = crypto_sign_verify_detached (s, m, mlen, pub->q_y);
202 return (res == 0) ? GNUNET_OK : GNUNET_SYSERR;
203}
204
205
206/**
207 * Derive the 'h' value for key derivation, where
208 * 'h = H(P ∥ seed) mod n' and 'n' is the size of the cyclic subroup.
209 *
210 * @param pub public key for deriviation
211 * @param seed seed for key the deriviation
212 * @param seedsize the size of the seed
213 * @param[out] phc if not NULL, the output of H() will be written into
214 * return h_mod_n (allocated by this function)
215 */
216static void
217derive_h (
218 const struct GNUNET_CRYPTO_Edx25519PublicKey *pub,
219 const void *seed,
220 size_t seedsize,
221 struct GNUNET_HashCode *phc)
222{
223 static const char *const salt = "edx25519-derivation";
224
225 GNUNET_CRYPTO_kdf (/* output*/
226 phc, sizeof(*phc),
227 /* salt */
228 seed, seedsize,
229 /* ikm */
230 pub, sizeof(*pub),
231 /* ctx chunks*/
232 salt, strlen (salt),
233 NULL, 0);
234
235}
236
237
238void
239GNUNET_CRYPTO_edx25519_private_key_derive (
240 const struct GNUNET_CRYPTO_Edx25519PrivateKey *priv,
241 const void *seed,
242 size_t seedsize,
243 struct GNUNET_CRYPTO_Edx25519PrivateKey *result)
244{
245 struct GNUNET_CRYPTO_Edx25519PublicKey pub;
246 struct GNUNET_HashCode hc;
247 uint8_t a[32];
248 uint8_t eight[32] = { 8 };
249 uint8_t eight_inv[32];
250 uint8_t h[64] = { 0 };
251
252 GNUNET_CRYPTO_edx25519_key_get_public (priv, &pub);
253
254 /* Get h mod n */
255 derive_h (&pub,
256 seed,
257 seedsize,
258 &hc);
259
260 memcpy (h, &hc, 64);
261 crypto_core_ed25519_scalar_reduce (h,
262 h);
263#ifdef CHECK_RARE_CASES
264 /**
265 * Note that the following cases would be problematic:
266 * 1.) h == 0 mod n
267 * 2.) h == 1 mod n
268 * 3.) [h] * P == E
269 * We assume that the probalities for these cases to occur are neglegible.
270 */
271 {
272 char zero[32] = { 0 };
273 char one[32] = { 1 };
274
275 GNUNET_assert (0 != memcmp (zero, h, 32));
276 GNUNET_assert (0 != memcmp (one, h, 32));
277 }
278#endif
279
280 /**
281 * dc now contains the private scalar "a".
282 * We carefully remove the clamping and derive a'.
283 * Calculate:
284 * a1 := a / 8
285 * a2 := h * a1 mod n
286 * a' := a2 * 8 mod n
287 */
288
289 GNUNET_assert (0 == crypto_core_ed25519_scalar_invert (eight_inv,
290 eight));
291
292 crypto_core_ed25519_scalar_mul (a, priv->a, eight_inv);
293 crypto_core_ed25519_scalar_mul (a, a, h);
294 crypto_core_ed25519_scalar_mul (a, a, eight);
295
296#ifdef CHECK_RARE_CASES
297 /* The likelihood for a' == 0 or a' == 1 is neglegible */
298 {
299 char zero[32] = { 0 };
300 char one[32] = { 1 };
301
302 GNUNET_assert (0 != memcmp (zero, a, 32));
303 GNUNET_assert (0 != memcmp (one, a, 32));
304 }
305#endif
306
307 /* We hash the derived "h" parameter with the other half of the expanded
308 * private key (that is: priv->b). This ensures that for signature
309 * generation, the "R" is derived from the same derivation path as "h" and is
310 * not reused. */
311 {
312 struct GNUNET_HashCode hcb;
313 struct GNUNET_HashContext *hctx;
314
315 hctx = GNUNET_CRYPTO_hash_context_start ();
316 GNUNET_CRYPTO_hash_context_read (hctx, priv->b, sizeof(priv->b));
317 GNUNET_CRYPTO_hash_context_read (hctx, (unsigned char*) &hc, sizeof (hc));
318 GNUNET_CRYPTO_hash_context_finish (hctx, &hcb);
319
320 /* Truncate result, effectively doing SHA512/256 */
321 for (size_t i = 0; i < 32; i++)
322 result->b[i] = ((unsigned char *) &hcb)[i];
323 }
324
325 for (size_t i = 0; i < 32; i++)
326 result->a[i] = a[i];
327
328 sodium_memzero (a, sizeof(a));
329}
330
331
332void
333GNUNET_CRYPTO_edx25519_public_key_derive (
334 const struct GNUNET_CRYPTO_Edx25519PublicKey *pub,
335 const void *seed,
336 size_t seedsize,
337 struct GNUNET_CRYPTO_Edx25519PublicKey *result)
338{
339 struct GNUNET_HashCode hc;
340 uint8_t h[64] = { 0 };
341
342 derive_h (pub,
343 seed,
344 seedsize,
345 &hc);
346 memcpy (h,
347 &hc,
348 64);
349 crypto_core_ed25519_scalar_reduce (h,
350 h);
351 GNUNET_assert (0 == crypto_scalarmult_ed25519_noclamp (result->q_y,
352 h,
353 pub->q_y));
354}
diff --git a/src/lib/util/crypto_elligator.c b/src/lib/util/crypto_elligator.c
new file mode 100644
index 000000000..91db517b0
--- /dev/null
+++ b/src/lib/util/crypto_elligator.c
@@ -0,0 +1,674 @@
1/*
2 This file is part of GNUnet.
3 Copyright (C) 2002-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 Portions of this code are derived from the Elligator-2 project,
22 which is licensed under the Creative Commons CC0 1.0 Universal Public Domain Dedication.
23 The Elligator-2 project can be found at: https://github.com/Kleshni/Elligator-2
24
25 Note that gmp is already a dependency of GnuTLS
26
27*/
28
29#include "platform.h"
30#include <gcrypt.h>
31#include <sodium.h>
32#include "gnunet_util_lib.h"
33#include "benchmark.h"
34
35#include <stdint.h>
36#include <stdbool.h>
37#include <string.h>
38#include <gmp.h>
39
40// Ed25519 subgroup of points with a low order
41static const uint8_t lookupTable[8][crypto_scalarmult_SCALARBYTES] = {
42 {
43 0x26, 0xE8, 0x95, 0x8F, 0xC2, 0xB2, 0x27, 0xB0,
44 0x45, 0xC3, 0xF4, 0x89, 0xF2, 0xEF, 0x98, 0xF0,
45 0xD5, 0xDF, 0xAC, 0x05, 0xD3, 0xC6, 0x33, 0x39,
46 0xB1, 0x38, 0x02, 0x88, 0x6D, 0x53, 0xFC, 0x05
47 },
48 {
49 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
50 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
51 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
52 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
53 },
54 {
55 0xC7, 0x17, 0x6A, 0x70, 0x3D, 0x4D, 0xD8, 0x4F,
56 0xBA, 0x3C, 0x0B, 0x76, 0x0D, 0x10, 0x67, 0x0F,
57 0x2A, 0x20, 0x53, 0xFA, 0x2C, 0x39, 0xCC, 0xC6,
58 0x4E, 0xC7, 0xFD, 0x77, 0x92, 0xAC, 0x03, 0x7A
59 },
60 {
61 0xEC, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
62 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
63 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
64 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x7F
65 }, {
66 0xC7, 0x17, 0x6A, 0x70, 0x3D, 0x4D, 0xD8, 0x4F,
67 0xBA, 0x3C, 0x0B, 0x76, 0x0D, 0x10, 0x67, 0x0F,
68 0x2A, 0x20, 0x53, 0xFA, 0x2C, 0x39, 0xCC, 0xC6,
69 0x4E, 0xC7, 0xFD, 0x77, 0x92, 0xAC, 0x03, 0xFA
70 }, {
71 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
72 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
73 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
74 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80
75 }, {
76 0x26, 0xE8, 0x95, 0x8F, 0xC2, 0xB2, 0x27, 0xB0,
77 0x45, 0xC3, 0xF4, 0x89, 0xF2, 0xEF, 0x98, 0xF0,
78 0xD5, 0xDF, 0xAC, 0x05, 0xD3, 0xC6, 0x33, 0x39,
79 0xB1, 0x38, 0x02, 0x88, 0x6D, 0x53, 0xFC, 0x85
80 },{
81 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
82 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
83 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
84 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
85 }
86};
87
88// main.h from Kleshnis's elligator implementation
89#include <limits.h>
90
91#define P_BITS (256) // 255 significant bits + 1 for carry
92#define P_BYTES ((P_BITS + CHAR_BIT - 1) / CHAR_BIT)
93#define P_LIMBS ((P_BITS + GMP_NUMB_BITS - 1) / GMP_NUMB_BITS)
94
95
96// main.c from Kleshnis's elligator implementation
97static const unsigned char p_bytes[P_BYTES] = {
98 0xed, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
99 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
100 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
101 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x7f
102};
103
104static const unsigned char negative_1_bytes[P_BYTES] = {
105 0xec, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
106 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
107 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
108 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x7f
109};
110
111static const unsigned char negative_2_bytes[P_BYTES] = {
112 0xeb, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
113 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
114 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
115 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x7f
116};
117
118static const unsigned char divide_negative_1_2_bytes[P_BYTES] = {
119 0xf6, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
120 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
121 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
122 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x3f
123};
124
125static const unsigned char divide_plus_p_3_8_bytes[P_BYTES] = {
126 0xfe, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
127 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
128 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
129 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x0f
130};
131
132static const unsigned char divide_minus_p_1_2_bytes[P_BYTES] = {
133 0xf6, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
134 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
135 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
136 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x3f
137};
138
139static const unsigned char square_root_negative_1_bytes[P_BYTES] = {
140 0xb0, 0xa0, 0x0e, 0x4a, 0x27, 0x1b, 0xee, 0xc4,
141 0x78, 0xe4, 0x2f, 0xad, 0x06, 0x18, 0x43, 0x2f,
142 0xa7, 0xd7, 0xfb, 0x3d, 0x99, 0x00, 0x4d, 0x2b,
143 0x0b, 0xdf, 0xc1, 0x4f, 0x80, 0x24, 0x83, 0x2b
144};
145
146static const unsigned char A_bytes[P_BYTES] = {
147 0x06, 0x6d, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00,
148 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
149 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
150 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
151};
152
153static const unsigned char negative_A_bytes[P_BYTES] = {
154 0xe7, 0x92, 0xf8, 0xff, 0xff, 0xff, 0xff, 0xff,
155 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
156 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
157 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x7f
158};
159
160static const unsigned char u_bytes[P_BYTES] = {
161 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
162 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
163 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
164 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
165};
166
167static const unsigned char inverted_u_bytes[P_BYTES] = {
168 0xf7, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
169 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
170 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
171 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x3f
172};
173
174static const unsigned char d_bytes[P_BYTES] = {
175 0xa3, 0x78, 0x59, 0x13, 0xca, 0x4d, 0xeb, 0x75,
176 0xab, 0xd8, 0x41, 0x41, 0x4d, 0x0a, 0x70, 0x00,
177 0x98, 0xe8, 0x79, 0x77, 0x79, 0x40, 0xc7, 0x8c,
178 0x73, 0xfe, 0x6f, 0x2b, 0xee, 0x6c, 0x03, 0x52
179};
180
181static mp_limb_t p[P_LIMBS];
182static mp_limb_t negative_1[P_LIMBS];
183static mp_limb_t negative_2[P_LIMBS];
184static mp_limb_t divide_negative_1_2[P_LIMBS];
185static mp_limb_t divide_plus_p_3_8[P_LIMBS];
186static mp_limb_t divide_minus_p_1_2[P_LIMBS];
187static mp_limb_t square_root_negative_1[P_LIMBS];
188static mp_limb_t A[P_LIMBS];
189static mp_limb_t negative_A[P_LIMBS];
190static mp_limb_t u[P_LIMBS];
191static mp_limb_t inverted_u[P_LIMBS];
192static mp_limb_t d[P_LIMBS];
193
194static mp_size_t scratch_space_length;
195
196// TODO
197static void
198decode_bytes (mp_limb_t *number, const uint8_t *bytes)
199{
200 mp_limb_t scratch_space[1];
201
202 for (size_t i = 0; i < P_BYTES; ++i)
203 {
204 mpn_lshift (number, number, P_LIMBS, 8);
205 mpn_sec_add_1 (number, number, 1, bytes[P_BYTES - i - 1], scratch_space);
206 }
207}
208
209
210// TODO
211static void
212encode_bytes (uint8_t *bytes, mp_limb_t *number)
213{
214 for (size_t i = 0; i < P_BYTES; ++i)
215 {
216 bytes[P_BYTES - i - 1] = mpn_lshift (number, number, P_LIMBS, 8);
217 }
218}
219
220
221/**
222 * Initialize elligator scratch space.
223*/
224void __attribute__ ((constructor))
225GNUNET_CRYPTO_ecdhe_elligator_initialize ()
226{
227 static bool initialized = false;
228
229 if (initialized)
230 {
231 return;
232 }
233
234 decode_bytes (p, p_bytes);
235 decode_bytes (negative_1, negative_1_bytes);
236 decode_bytes (negative_2, negative_2_bytes);
237 decode_bytes (divide_negative_1_2, divide_negative_1_2_bytes);
238 decode_bytes (divide_plus_p_3_8, divide_plus_p_3_8_bytes);
239 decode_bytes (divide_minus_p_1_2, divide_minus_p_1_2_bytes);
240 decode_bytes (square_root_negative_1, square_root_negative_1_bytes);
241 decode_bytes (A, A_bytes);
242 decode_bytes (negative_A, negative_A_bytes);
243 decode_bytes (u, u_bytes);
244 decode_bytes (inverted_u, inverted_u_bytes);
245 decode_bytes (d, d_bytes);
246
247 mp_size_t scratch_space_lengths[] = {
248 // For least_square_root
249
250 mpn_sec_powm_itch (P_LIMBS, P_BITS - 1, P_LIMBS),
251 mpn_sec_sqr_itch (P_LIMBS),
252 mpn_sec_div_r_itch (P_LIMBS + P_LIMBS, P_LIMBS),
253 mpn_sec_sub_1_itch (P_LIMBS),
254 mpn_sec_mul_itch (P_LIMBS, P_LIMBS),
255
256 // For Elligator_2_Curve25519_encode
257
258 mpn_sec_powm_itch (P_LIMBS, P_BITS - 1, P_LIMBS),
259 mpn_sec_mul_itch (P_LIMBS, P_LIMBS),
260 mpn_sec_div_r_itch (P_LIMBS + P_LIMBS, P_LIMBS),
261 mpn_sec_sqr_itch (P_LIMBS),
262 mpn_sec_sub_1_itch (P_LIMBS),
263
264 // For Elligator_2_Curve25519_decode
265
266 mpn_sec_sqr_itch (P_LIMBS),
267 mpn_sec_div_r_itch (P_LIMBS + P_LIMBS, P_LIMBS),
268 mpn_sec_div_r_itch (P_LIMBS, P_LIMBS),
269 mpn_sec_mul_itch (P_LIMBS, P_LIMBS),
270 mpn_sec_add_1_itch (P_LIMBS),
271 mpn_sec_powm_itch (P_LIMBS, P_BITS - 1, P_LIMBS),
272
273 // For Elligator_2_Curve25519_convert_from_Ed25519
274 mpn_sec_sqr_itch (P_LIMBS),
275 mpn_sec_div_r_itch (P_LIMBS + P_LIMBS, P_LIMBS),
276 mpn_sec_mul_itch (P_LIMBS, P_LIMBS),
277 mpn_sec_add_1_itch (P_LIMBS),
278 mpn_sec_powm_itch (P_LIMBS, P_BITS - 1, P_LIMBS),
279 mpn_sec_sub_1_itch (P_LIMBS)
280 };
281
282 for (size_t i = 0; i < sizeof scratch_space_lengths
283 / sizeof *scratch_space_lengths; ++i)
284 {
285 if (scratch_space_lengths[i] > scratch_space_length)
286 {
287 scratch_space_length = scratch_space_lengths[i];
288 }
289 }
290
291 initialized = true;
292}
293
294
295/**
296 * Calculates the root of a given number.
297 * Returns trash if the number is a quadratic non-residue.
298 *
299 * @param root storage for calculated root
300 * @param number value for which the root is calculated
301 * @param scratch_space buffer for calculation
302 */
303static void
304least_square_root (mp_limb_t *root,
305 const mp_limb_t *number,
306 mp_limb_t *scratch_space)
307{
308 mp_limb_t a[P_LIMBS + P_LIMBS];
309 mp_limb_t b[P_LIMBS];
310
311 // root := number ^ ((p + 3) / 8)
312
313 mpn_add_n (b, number, p, P_LIMBS); // The next function requires a nonzero input
314 mpn_sec_powm (root, b, P_LIMBS, divide_plus_p_3_8, P_BITS - 1, p, P_LIMBS,
315 scratch_space);
316
317 // If root ^ 2 != number, root := root * square_root(-1)
318
319 mpn_sec_sqr (a, root, P_LIMBS, scratch_space);
320 mpn_sec_div_r (a, P_LIMBS + P_LIMBS, p, P_LIMBS, scratch_space);
321 mpn_sub_n (b, a, number, P_LIMBS);
322
323 mp_limb_t condition = mpn_sec_sub_1 (b, b, P_LIMBS, 1, scratch_space) ^ 1;
324
325 mpn_sec_mul (a, root, P_LIMBS, square_root_negative_1, P_LIMBS,
326 scratch_space);
327 mpn_sec_div_r (a, P_LIMBS + P_LIMBS, p, P_LIMBS, scratch_space);
328
329 mpn_cnd_swap (condition, root, a, P_LIMBS);
330
331 // If root > (p - 1) / 2, root := -root
332
333 condition = mpn_sub_n (a, divide_minus_p_1_2, root, P_LIMBS);
334
335 mpn_sub_n (a, p, root, P_LIMBS); // If root = 0, a := p
336
337 mpn_cnd_swap (condition, root, a, P_LIMBS);
338}
339
340
341bool
342GNUNET_CRYPTO_ecdhe_elligator_encoding (
343 struct GNUNET_CRYPTO_ElligatorRepresentative *r,
344 const struct GNUNET_CRYPTO_EcdhePublicKey *pub,
345 bool high_y)
346{
347 uint8_t *representative = (uint8_t *) r->r;
348 uint8_t *point = (uint8_t *) pub->q_y;
349
350 mp_limb_t scratch_space[scratch_space_length];
351
352 mp_limb_t a[P_LIMBS + P_LIMBS];
353 mp_limb_t b[P_LIMBS + P_LIMBS];
354 mp_limb_t c[P_LIMBS + P_LIMBS];
355
356 // a := point
357
358 decode_bytes (a, point);
359
360 // b := -a / (a + A), or b := p if a = 0
361
362 mpn_add_n (b, a, A, P_LIMBS);
363 mpn_sec_powm (c, b, P_LIMBS, negative_2, P_BITS - 1, p, P_LIMBS,
364 scratch_space);
365 mpn_sec_mul (b, c, P_LIMBS, a, P_LIMBS, scratch_space);
366 mpn_sec_div_r (b, P_LIMBS + P_LIMBS, p, P_LIMBS, scratch_space);
367 mpn_sub_n (b, p, b, P_LIMBS);
368
369 // If high_y = true, b := 1 / b or b := 0 if it was = p
370
371 mpn_sec_powm (c, b, P_LIMBS, negative_2, P_BITS - 1, p, P_LIMBS,
372 scratch_space);
373 mpn_cnd_swap (high_y, b, c, P_LIMBS);
374
375 // c := b / u
376
377 mpn_sec_mul (c, b, P_LIMBS, inverted_u, P_LIMBS, scratch_space);
378 mpn_sec_div_r (c, P_LIMBS + P_LIMBS, p, P_LIMBS, scratch_space);
379
380 // If c is a square modulo p, b := least_square_root(c)
381
382 least_square_root (b, c, scratch_space);
383
384 // Determine, whether b ^ 2 = c
385
386 mpn_sec_sqr (a, b, P_LIMBS, scratch_space);
387 mpn_sec_div_r (a, P_LIMBS + P_LIMBS, p, P_LIMBS, scratch_space);
388 mpn_sub_n (a, a, c, P_LIMBS);
389
390 bool result = mpn_sec_sub_1 (a, a, P_LIMBS, 1, scratch_space);
391
392 encode_bytes (representative, b);
393
394 return result;
395}
396
397
398/**
399 * Takes a number of the underlying finite field of Curve25519 and projects it into a valid point on that curve.
400 * This function works deterministically.
401 * This step is also known as elligators "decoding" step.
402 * Taken from https://github.com/Kleshni/Elligator-2/blob/master/main.c.
403 *
404 * @param point storage for calculated point on Curve25519
405 * @param high_y The 'high_y' argument of the corresponding GNUNET_CRYPTO_ecdhe_elligator_encoding call
406 * @param representative Given representative
407 * @return 'false' if extra step during direct map calculation is needed, otherwise 'true'
408 */
409static bool
410elligator_direct_map (uint8_t *point,
411 bool *high_y,
412 uint8_t *representative)
413{
414 mp_limb_t scratch_space[scratch_space_length];
415
416 mp_limb_t a[P_LIMBS + P_LIMBS];
417 mp_limb_t b[P_LIMBS + P_LIMBS];
418 mp_limb_t c[P_LIMBS];
419 mp_limb_t e[P_LIMBS + P_LIMBS];
420
421 // a := representative
422
423 decode_bytes (a, representative);
424
425 // Determine whether a < (p - 1) / 2
426
427 bool result = mpn_sub_n (b, divide_minus_p_1_2, a, P_LIMBS) ^ 1;
428
429 // b := -A / (1 + u * a ^ 2)
430
431 mpn_sec_sqr (b, a, P_LIMBS, scratch_space);
432 mpn_sec_div_r (b, P_LIMBS + P_LIMBS, p, P_LIMBS, scratch_space);
433 mpn_sec_mul (a, u, P_LIMBS, b, P_LIMBS, scratch_space);
434 mpn_sec_div_r (a, P_LIMBS + P_LIMBS, p, P_LIMBS, scratch_space);
435 mpn_sec_add_1 (b, a, P_LIMBS, 1, scratch_space);
436 mpn_sec_powm (a, b, P_LIMBS, negative_2, P_BITS - 1, p, P_LIMBS,
437 scratch_space);
438 mpn_sec_mul (b, a, P_LIMBS, negative_A, P_LIMBS, scratch_space);
439 mpn_sec_div_r (b, P_LIMBS + P_LIMBS, p, P_LIMBS, scratch_space);
440
441 // a := b ^ 3 + A * b ^ 2 + b (with 1-bit overflow)
442
443 mpn_sec_sqr (a, b, P_LIMBS, scratch_space);
444 mpn_sec_div_r (a, P_LIMBS + P_LIMBS, p, P_LIMBS, scratch_space);
445 mpn_add_n (c, b, A, P_LIMBS);
446 mpn_sec_mul (e, c, P_LIMBS, a, P_LIMBS, scratch_space);
447 mpn_sec_div_r (e, P_LIMBS + P_LIMBS, p, P_LIMBS, scratch_space);
448 mpn_add_n (a, e, b, P_LIMBS);
449
450 // If a is a quadratic residue modulo p, point := b and high_y := 1
451 // Otherwise point := -b - A and high_y := 0
452
453 mpn_sub_n (c, p, b, P_LIMBS);
454 mpn_add_n (c, c, negative_A, P_LIMBS);
455 mpn_sec_div_r (c, P_LIMBS, p, P_LIMBS, scratch_space);
456
457 mpn_sec_powm (e, a, P_LIMBS, divide_minus_p_1_2, P_BITS - 1, p, P_LIMBS,
458 scratch_space);
459 *high_y = mpn_sub_n (e, e, divide_minus_p_1_2, P_LIMBS);
460
461 mpn_cnd_swap (*high_y, b, c, P_LIMBS);
462
463 encode_bytes (point, c);
464
465 return result;
466}
467
468
469void
470GNUNET_CRYPTO_ecdhe_elligator_decoding (
471 struct GNUNET_CRYPTO_EcdhePublicKey *point,
472 bool *high_y,
473 const struct GNUNET_CRYPTO_ElligatorRepresentative *representative)
474{
475 // if sign of direct map transformation not needed throw it away
476 bool high_y_local;
477 bool *high_y_ptr;
478 if (NULL == high_y)
479 high_y_ptr = &high_y_local;
480 else
481 high_y_ptr = high_y;
482
483 struct GNUNET_CRYPTO_ElligatorRepresentative r_tmp;
484 memcpy (&r_tmp.r, &representative->r, sizeof(r_tmp.r));
485 r_tmp.r[31] &= 63;
486 // GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,"Print high_y\n");
487 elligator_direct_map ((uint8_t *) point->q_y,
488 high_y_ptr,
489 (uint8_t *) r_tmp.r);
490}
491
492
493/**
494 * Takes a number of the underlying finite field of Curve25519 and projects it into a valid point on that curve.
495 * This function works deterministically.
496 * This step is also known as elligators "decoding" step.
497 * Taken from https://github.com/Kleshni/Elligator-2/blob/master/main.c.
498 *
499 * @param point storage for calculated point on Curve25519
500 * @param source Ed25519 curve point
501 * @return 'false' if source is not a valid Ed25519 point. In this case the 'point' array will be undefined but dependend on source.
502 */
503static bool
504convert_from_ed_to_curve (uint8_t *point,
505 const uint8_t *source)
506{
507 mp_limb_t scratch_space[scratch_space_length];
508
509 mp_limb_t y[P_LIMBS];
510 mp_limb_t a[P_LIMBS + P_LIMBS];
511 mp_limb_t b[P_LIMBS + P_LIMBS];
512 mp_limb_t c[P_LIMBS + P_LIMBS];
513
514 uint8_t y_bytes[P_BYTES];
515
516 memcpy (y_bytes, source, 31);
517
518 y_bytes[31] = source[31] & 0x7f;
519
520 decode_bytes (y, y_bytes);
521
522 // Check if y < p
523
524 bool result = mpn_sub_n (a, y, p, P_LIMBS);
525
526 // a := (y ^ 2 - 1) / (1 + d * y ^ 2)
527
528 mpn_sec_sqr (a, y, P_LIMBS, scratch_space);
529 mpn_sec_div_r (a, P_LIMBS + P_LIMBS, p, P_LIMBS, scratch_space);
530 mpn_sec_mul (b, a, P_LIMBS, d, P_LIMBS, scratch_space);
531 mpn_sec_add_1 (b, b, P_LIMBS, 1, scratch_space);
532 mpn_sec_div_r (b, P_LIMBS + P_LIMBS, p, P_LIMBS, scratch_space);
533 mpn_sec_powm (c, b, P_LIMBS, negative_2, P_BITS - 1, p, P_LIMBS,
534 scratch_space);
535 mpn_add_n (b, a, negative_1, P_LIMBS);
536 mpn_sec_mul (a, b, P_LIMBS, c, P_LIMBS, scratch_space);
537 mpn_sec_div_r (a, P_LIMBS + P_LIMBS, p, P_LIMBS, scratch_space);
538
539 // Check, whether a is a square modulo p (including a = 0)
540
541 mpn_add_n (a, a, p, P_LIMBS);
542 mpn_sec_powm (b, a, P_LIMBS, divide_negative_1_2, P_BITS - 1, p, P_LIMBS,
543 scratch_space);
544
545 result &= mpn_sub_n (c, b, divide_minus_p_1_2, P_LIMBS);
546
547 // If a = p, the parity bit must be 0
548
549 mpn_sub_n (a, a, p, P_LIMBS);
550
551 result ^= mpn_sec_sub_1 (a, a, P_LIMBS, 1, scratch_space) & source[31] >> 7;
552
553 // If y != 1, c := (1 + y) / (1 - y), otherwise c := 0
554
555 mpn_sub_n (a, p, y, P_LIMBS);
556 mpn_sec_add_1 (a, a, P_LIMBS, 1, scratch_space);
557 mpn_sec_powm (b, a, P_LIMBS, negative_2, P_BITS - 1, p, P_LIMBS,
558 scratch_space);
559 mpn_sec_add_1 (a, y, P_LIMBS, 1, scratch_space);
560 mpn_sec_mul (c, a, P_LIMBS, b, P_LIMBS, scratch_space);
561 mpn_sec_div_r (c, P_LIMBS + P_LIMBS, p, P_LIMBS, scratch_space);
562
563 encode_bytes (point, c);
564
565 return result;
566}
567
568
569enum GNUNET_GenericReturnValue
570GNUNET_CRYPTO_ecdhe_elligator_generate_public_key (
571 struct GNUNET_CRYPTO_EcdhePublicKey *pub,
572 struct GNUNET_CRYPTO_EcdhePrivateKey *pk)
573{
574 // eHigh
575 // crypto_scalarmult_ed25519_base clamps the scalar pk->d and return only 0 if pk->d is zero
576 unsigned char eHigh[crypto_scalarmult_SCALARBYTES] = {0};
577 GNUNET_assert (0 == crypto_scalarmult_ed25519_base (eHigh, pk->d));
578
579 // eLow: choose a random point of low order
580 int sLow = (pk->d)[0] % 8;
581 unsigned char eLow[crypto_scalarmult_SCALARBYTES] = {0};
582 memcpy (eLow, lookupTable[sLow], crypto_scalarmult_SCALARBYTES);
583
584 // eHigh + eLow
585 unsigned char edPub[crypto_scalarmult_SCALARBYTES] = {0};
586 if (crypto_core_ed25519_add (edPub, eLow, eHigh) == -1)
587 {
588 return GNUNET_SYSERR;
589 }
590
591 if (convert_from_ed_to_curve (pub->q_y, edPub) == false)
592 {
593 return GNUNET_SYSERR;
594 }
595 return GNUNET_OK;
596}
597
598
599void
600GNUNET_CRYPTO_ecdhe_elligator_key_create (
601 struct GNUNET_CRYPTO_ElligatorRepresentative *repr,
602 struct GNUNET_CRYPTO_EcdhePrivateKey *pk)
603{
604 // inverse map can fail for some public keys generated by GNUNET_CRYPTO_ecdhe_elligator_generate_public_key
605 bool validKey = 0;
606 struct GNUNET_CRYPTO_EcdhePublicKey pub = {0};
607 int8_t random_tweak;
608 bool high_y;
609 bool msb_set;
610 bool smsb_set;
611
612 while (! validKey)
613 {
614 GNUNET_CRYPTO_random_block (GNUNET_CRYPTO_QUALITY_NONCE,
615 pk,
616 sizeof (struct GNUNET_CRYPTO_EcdhePrivateKey));
617
618 // Continue if generate_public_key fails
619 if (GNUNET_SYSERR ==
620 GNUNET_CRYPTO_ecdhe_elligator_generate_public_key (&pub, pk))
621 {
622 continue;
623 }
624
625 GNUNET_CRYPTO_random_block (GNUNET_CRYPTO_QUALITY_NONCE,
626 &random_tweak,
627 sizeof(int8_t));
628 high_y = random_tweak & 1;
629
630 validKey = GNUNET_CRYPTO_ecdhe_elligator_encoding (repr,
631 &pub,
632 high_y ?
633 GNUNET_YES :
634 GNUNET_NO);
635 }
636
637 // Setting most significant bit and second most significant bit randomly
638 msb_set = (random_tweak >> 1) & 1;
639 smsb_set = (random_tweak >> 2) & 1;
640 if (msb_set)
641 {
642 repr->r[31] |= 128;
643 }
644 if (smsb_set)
645 {
646 repr->r[31] |= 64;
647 }
648}
649
650
651enum GNUNET_GenericReturnValue
652GNUNET_CRYPTO_eddsa_elligator_kem_encaps (
653 const struct GNUNET_CRYPTO_EddsaPublicKey *pub,
654 struct GNUNET_CRYPTO_ElligatorRepresentative *r,
655 struct GNUNET_HashCode *key_material)
656{
657 struct GNUNET_CRYPTO_EcdhePrivateKey sk;
658
659 GNUNET_CRYPTO_ecdhe_elligator_key_create (r, &sk);
660
661 return GNUNET_CRYPTO_ecdh_eddsa (&sk, pub, key_material);
662}
663
664
665enum GNUNET_GenericReturnValue
666GNUNET_CRYPTO_eddsa_elligator_kem_decaps (
667 const struct GNUNET_CRYPTO_EddsaPrivateKey *priv,
668 const struct GNUNET_CRYPTO_ElligatorRepresentative *r,
669 struct GNUNET_HashCode *key_material)
670{
671 struct GNUNET_CRYPTO_EcdhePublicKey pub;
672 GNUNET_CRYPTO_ecdhe_elligator_decoding (&pub, NULL, r);
673 return GNUNET_CRYPTO_eddsa_ecdh (priv, &pub, key_material);
674}
diff --git a/src/lib/util/crypto_hash.c b/src/lib/util/crypto_hash.c
new file mode 100644
index 000000000..4f3acde77
--- /dev/null
+++ b/src/lib/util/crypto_hash.c
@@ -0,0 +1,415 @@
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/**
22 * @file util/crypto_hash.c
23 * @brief SHA-512 #GNUNET_CRYPTO_hash() related functions
24 * @author Christian Grothoff
25 */
26
27#include "platform.h"
28#include "gnunet_util_lib.h"
29#include "benchmark.h"
30#include <gcrypt.h>
31
32#define LOG(kind, ...) GNUNET_log_from (kind, "util-crypto-hash", __VA_ARGS__)
33
34#define LOG_STRERROR_FILE(kind, syscall, \
35 filename) GNUNET_log_from_strerror_file (kind, \
36 "util-crypto-hash", \
37 syscall, \
38 filename)
39
40void
41GNUNET_CRYPTO_hash (const void *block,
42 size_t size,
43 struct GNUNET_HashCode *ret)
44{
45 BENCHMARK_START (hash);
46 gcry_md_hash_buffer (GCRY_MD_SHA512, ret, block, size);
47 BENCHMARK_END (hash);
48}
49
50
51/* ***************** binary-ASCII encoding *************** */
52
53
54void
55GNUNET_CRYPTO_hash_to_enc (const struct GNUNET_HashCode *block,
56 struct GNUNET_CRYPTO_HashAsciiEncoded *result)
57{
58 char *np;
59
60 np = GNUNET_STRINGS_data_to_string ((const unsigned char *) block,
61 sizeof(struct GNUNET_HashCode),
62 (char *) result,
63 sizeof(struct
64 GNUNET_CRYPTO_HashAsciiEncoded)
65 - 1);
66 GNUNET_assert (NULL != np);
67 *np = '\0';
68}
69
70
71enum GNUNET_GenericReturnValue
72GNUNET_CRYPTO_hash_from_string2 (const char *enc,
73 size_t enclen,
74 struct GNUNET_HashCode *result)
75{
76 char upper_enc[enclen + 1];
77 char *up_ptr = upper_enc;
78
79 if (GNUNET_OK != GNUNET_STRINGS_utf8_toupper (enc, up_ptr))
80 return GNUNET_SYSERR;
81
82 return GNUNET_STRINGS_string_to_data (upper_enc, enclen,
83 (unsigned char *) result,
84 sizeof(struct GNUNET_HashCode));
85}
86
87
88unsigned int
89GNUNET_CRYPTO_hash_distance_u32 (const struct GNUNET_HashCode *a,
90 const struct GNUNET_HashCode *b)
91{
92 unsigned int x1 = (a->bits[1] - b->bits[1]) >> 16;
93 unsigned int x2 = (b->bits[1] - a->bits[1]) >> 16;
94
95 return(x1 * x2);
96}
97
98
99void
100GNUNET_CRYPTO_hash_create_random (enum GNUNET_CRYPTO_Quality mode,
101 struct GNUNET_HashCode *result)
102{
103 for (ssize_t i = (sizeof(struct GNUNET_HashCode) / sizeof(uint32_t)) - 1;
104 i >= 0;
105 i--)
106 result->bits[i] = GNUNET_CRYPTO_random_u32 (mode, UINT32_MAX);
107}
108
109
110void
111GNUNET_CRYPTO_hash_difference (const struct GNUNET_HashCode *a,
112 const struct GNUNET_HashCode *b,
113 struct GNUNET_HashCode *result)
114{
115 for (ssize_t i = (sizeof(struct GNUNET_HashCode) / sizeof(unsigned int)) - 1;
116 i >= 0;
117 i--)
118 result->bits[i] = b->bits[i] - a->bits[i];
119}
120
121
122void
123GNUNET_CRYPTO_hash_sum (const struct GNUNET_HashCode *a,
124 const struct GNUNET_HashCode *delta, struct
125 GNUNET_HashCode *result)
126{
127 for (ssize_t i = (sizeof(struct GNUNET_HashCode) / sizeof(unsigned int)) - 1;
128 i >= 0;
129 i--)
130 result->bits[i] = delta->bits[i] + a->bits[i];
131}
132
133
134void
135GNUNET_CRYPTO_hash_xor (const struct GNUNET_HashCode *a,
136 const struct GNUNET_HashCode *b,
137 struct GNUNET_HashCode *result)
138{
139 const unsigned long long *lla = (const unsigned long long *) a;
140 const unsigned long long *llb = (const unsigned long long *) b;
141 unsigned long long *llr = (unsigned long long *) result;
142
143 GNUNET_static_assert (8 == sizeof (unsigned long long));
144 GNUNET_static_assert (0 == sizeof (*a) % sizeof (unsigned long long));
145
146 for (int i = sizeof (*result) / sizeof (*llr) - 1; i>=0; i--)
147 llr[i] = lla[i] ^ llb[i];
148}
149
150
151void
152GNUNET_CRYPTO_hash_to_aes_key (
153 const struct GNUNET_HashCode *hc,
154 struct GNUNET_CRYPTO_SymmetricSessionKey *skey,
155 struct GNUNET_CRYPTO_SymmetricInitializationVector *iv)
156{
157 GNUNET_assert (GNUNET_YES ==
158 GNUNET_CRYPTO_kdf (
159 skey,
160 sizeof(*skey),
161 "Hash key derivation",
162 strlen ("Hash key derivation"),
163 hc, sizeof(*hc),
164 NULL, 0));
165 GNUNET_assert (GNUNET_YES ==
166 GNUNET_CRYPTO_kdf (
167 iv,
168 sizeof(*iv),
169 "Initialization vector derivation",
170 strlen ("Initialization vector derivation"),
171 hc, sizeof(*hc),
172 NULL, 0));
173}
174
175
176unsigned int
177GNUNET_CRYPTO_hash_count_leading_zeros (const struct GNUNET_HashCode *h)
178{
179 const unsigned long long *llp = (const unsigned long long *) h;
180 unsigned int ret = 0;
181 unsigned int i;
182
183 GNUNET_static_assert (8 == sizeof (unsigned long long));
184 GNUNET_static_assert (0 == sizeof (*h) % sizeof (unsigned long long));
185 for (i = 0; i<sizeof (*h) / sizeof (*llp); i++)
186 {
187 if (0LLU != llp[i])
188 break;
189 ret += sizeof (*llp) * 8;
190 }
191 if (ret == 8 * sizeof (*h))
192 return ret;
193 ret += __builtin_clzll (GNUNET_ntohll ((uint64_t) llp[i]));
194 return ret;
195}
196
197
198unsigned int
199GNUNET_CRYPTO_hash_count_tailing_zeros (const struct GNUNET_HashCode *h)
200{
201 const unsigned long long *llp = (const unsigned long long *) h;
202 unsigned int ret = 0;
203 int i;
204
205 GNUNET_static_assert (8 == sizeof (unsigned long long));
206 GNUNET_static_assert (0 == sizeof (*h) % sizeof (unsigned long long));
207 for (i = sizeof (*h) / sizeof (*llp) - 1; i>=0; i--)
208 {
209 if (0LLU != llp[i])
210 break;
211 ret += sizeof (*llp) * 8;
212 }
213 if (ret == 8 * sizeof (*h))
214 return ret;
215 ret += __builtin_ctzll (GNUNET_ntohll ((uint64_t) llp[i]));
216 return ret;
217}
218
219
220int
221GNUNET_CRYPTO_hash_cmp (const struct GNUNET_HashCode *h1,
222 const struct GNUNET_HashCode *h2)
223{
224 unsigned int *i1;
225 unsigned int *i2;
226
227 i1 = (unsigned int *) h1;
228 i2 = (unsigned int *) h2;
229 for (ssize_t i = (sizeof(struct GNUNET_HashCode) / sizeof(unsigned int)) - 1;
230 i >= 0;
231 i--)
232 {
233 if (i1[i] > i2[i])
234 return 1;
235 if (i1[i] < i2[i])
236 return -1;
237 }
238 return 0;
239}
240
241
242int
243GNUNET_CRYPTO_hash_xorcmp (const struct GNUNET_HashCode *h1,
244 const struct GNUNET_HashCode *h2,
245 const struct GNUNET_HashCode *target)
246{
247 const unsigned long long *l1 = (const unsigned long long *) h1;
248 const unsigned long long *l2 = (const unsigned long long *) h2;
249 const unsigned long long *t = (const unsigned long long *) target;
250
251 GNUNET_static_assert (0 == sizeof (*h1) % sizeof (*l1));
252 for (size_t i = 0; i < sizeof(*h1) / sizeof(*l1); i++)
253 {
254 unsigned long long x1 = l1[i] ^ t[i];
255 unsigned long long x2 = l2[i] ^ t[i];
256
257 if (x1 > x2)
258 return 1;
259 if (x1 < x2)
260 return -1;
261 }
262 return 0;
263}
264
265
266void
267GNUNET_CRYPTO_hmac_derive_key (
268 struct GNUNET_CRYPTO_AuthKey *key,
269 const struct GNUNET_CRYPTO_SymmetricSessionKey *rkey,
270 const void *salt, size_t salt_len,
271 ...)
272{
273 va_list argp;
274
275 va_start (argp,
276 salt_len);
277 GNUNET_CRYPTO_hmac_derive_key_v (key,
278 rkey,
279 salt, salt_len,
280 argp);
281 va_end (argp);
282}
283
284
285void
286GNUNET_CRYPTO_hmac_derive_key_v (
287 struct GNUNET_CRYPTO_AuthKey *key,
288 const struct GNUNET_CRYPTO_SymmetricSessionKey *rkey,
289 const void *salt, size_t salt_len,
290 va_list argp)
291{
292 GNUNET_CRYPTO_kdf_v (key->key, sizeof(key->key),
293 salt, salt_len,
294 rkey, sizeof(struct GNUNET_CRYPTO_SymmetricSessionKey),
295 argp);
296}
297
298
299void
300GNUNET_CRYPTO_hmac_raw (const void *key, size_t key_len,
301 const void *plaintext, size_t plaintext_len,
302 struct GNUNET_HashCode *hmac)
303{
304 static int once;
305 static gcry_md_hd_t md;
306 const unsigned char *mc;
307
308 if (! once)
309 {
310 once = 1;
311 GNUNET_assert (GPG_ERR_NO_ERROR ==
312 gcry_md_open (&md,
313 GCRY_MD_SHA512,
314 GCRY_MD_FLAG_HMAC));
315 }
316 else
317 {
318 gcry_md_reset (md);
319 }
320 GNUNET_assert (GPG_ERR_NO_ERROR ==
321 gcry_md_setkey (md, key, key_len));
322 gcry_md_write (md, plaintext, plaintext_len);
323 mc = gcry_md_read (md, GCRY_MD_SHA512);
324 GNUNET_assert (NULL != mc);
325 GNUNET_memcpy (hmac->bits, mc, sizeof(hmac->bits));
326}
327
328
329void
330GNUNET_CRYPTO_hmac (const struct GNUNET_CRYPTO_AuthKey *key,
331 const void *plaintext, size_t plaintext_len,
332 struct GNUNET_HashCode *hmac)
333{
334 GNUNET_CRYPTO_hmac_raw ((void *) key->key, sizeof(key->key),
335 plaintext, plaintext_len,
336 hmac);
337}
338
339
340struct GNUNET_HashContext
341{
342 /**
343 * Internal state of the hash function.
344 */
345 gcry_md_hd_t hd;
346};
347
348
349struct GNUNET_HashContext *
350GNUNET_CRYPTO_hash_context_start ()
351{
352 struct GNUNET_HashContext *hc;
353
354 BENCHMARK_START (hash_context_start);
355 hc = GNUNET_new (struct GNUNET_HashContext);
356 GNUNET_assert (0 ==
357 gcry_md_open (&hc->hd,
358 GCRY_MD_SHA512,
359 0));
360 BENCHMARK_END (hash_context_start);
361 return hc;
362}
363
364
365void
366GNUNET_CRYPTO_hash_context_read (struct GNUNET_HashContext *hc,
367 const void *buf,
368 size_t size)
369{
370 BENCHMARK_START (hash_context_read);
371 gcry_md_write (hc->hd, buf, size);
372 BENCHMARK_END (hash_context_read);
373}
374
375
376struct GNUNET_HashContext *
377GNUNET_CRYPTO_hash_context_copy (const struct GNUNET_HashContext *hc)
378{
379 struct GNUNET_HashContext *cp;
380
381 cp = GNUNET_new (struct GNUNET_HashContext);
382 GNUNET_assert (0 ==
383 gcry_md_copy (&cp->hd,
384 hc->hd));
385 return cp;
386}
387
388
389void
390GNUNET_CRYPTO_hash_context_finish (struct GNUNET_HashContext *hc,
391 struct GNUNET_HashCode *r_hash)
392{
393 const void *res = gcry_md_read (hc->hd, 0);
394
395 BENCHMARK_START (hash_context_finish);
396
397 GNUNET_assert (NULL != res);
398 if (NULL != r_hash)
399 GNUNET_memcpy (r_hash,
400 res,
401 sizeof(struct GNUNET_HashCode));
402 GNUNET_CRYPTO_hash_context_abort (hc);
403 BENCHMARK_END (hash_context_finish);
404}
405
406
407void
408GNUNET_CRYPTO_hash_context_abort (struct GNUNET_HashContext *hc)
409{
410 gcry_md_close (hc->hd);
411 GNUNET_free (hc);
412}
413
414
415/* end of crypto_hash.c */
diff --git a/src/lib/util/crypto_hash_file.c b/src/lib/util/crypto_hash_file.c
new file mode 100644
index 000000000..96d364d2b
--- /dev/null
+++ b/src/lib/util/crypto_hash_file.c
@@ -0,0 +1,236 @@
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/**
22 * @file util/crypto_hash_file.c
23 * @brief incremental hashing of files
24 * @author Christian Grothoff
25 */
26
27#include "platform.h"
28#include "gnunet_util_lib.h"
29#include <gcrypt.h>
30
31#define LOG(kind, ...) GNUNET_log_from (kind, "util-crypto-hash-file", \
32 __VA_ARGS__)
33
34#define LOG_STRERROR_FILE(kind, syscall, \
35 filename) GNUNET_log_from_strerror_file (kind, \
36 "util-crypto-hash-file", \
37 syscall, \
38 filename)
39
40
41/**
42 * Context used when hashing a file.
43 */
44struct GNUNET_CRYPTO_FileHashContext
45{
46 /**
47 * Function to call upon completion.
48 */
49 GNUNET_CRYPTO_HashCompletedCallback callback;
50
51 /**
52 * Closure for callback.
53 */
54 void *callback_cls;
55
56 /**
57 * IO buffer.
58 */
59 unsigned char *buffer;
60
61 /**
62 * Name of the file we are hashing.
63 */
64 char *filename;
65
66 /**
67 * File descriptor.
68 */
69 struct GNUNET_DISK_FileHandle *fh;
70
71 /**
72 * Cummulated hash.
73 */
74 gcry_md_hd_t md;
75
76 /**
77 * Size of the file.
78 */
79 uint64_t fsize;
80
81 /**
82 * Current offset.
83 */
84 uint64_t offset;
85
86 /**
87 * Current task for hashing.
88 */
89 struct GNUNET_SCHEDULER_Task *task;
90
91 /**
92 * Priority we use.
93 */
94 enum GNUNET_SCHEDULER_Priority priority;
95
96 /**
97 * Blocksize.
98 */
99 size_t bsize;
100};
101
102
103/**
104 * Report result of hash computation to callback
105 * and free associated resources.
106 */
107static void
108file_hash_finish (struct GNUNET_CRYPTO_FileHashContext *fhc,
109 const struct GNUNET_HashCode *res)
110{
111 fhc->callback (fhc->callback_cls, res);
112 GNUNET_free (fhc->filename);
113 if (! GNUNET_DISK_handle_invalid (fhc->fh))
114 GNUNET_break (GNUNET_OK == GNUNET_DISK_file_close (fhc->fh));
115 gcry_md_close (fhc->md);
116 GNUNET_free (fhc); /* also frees fhc->buffer */
117}
118
119
120/**
121 * File hashing task.
122 *
123 * @param cls closure
124 */
125static void
126file_hash_task (void *cls)
127{
128 struct GNUNET_CRYPTO_FileHashContext *fhc = cls;
129 struct GNUNET_HashCode *res;
130 size_t delta;
131 ssize_t sret;
132
133 fhc->task = NULL;
134 GNUNET_assert (fhc->offset <= fhc->fsize);
135 delta = fhc->bsize;
136 if (fhc->fsize - fhc->offset < delta)
137 delta = fhc->fsize - fhc->offset;
138 sret = GNUNET_DISK_file_read (fhc->fh,
139 fhc->buffer,
140 delta);
141 if ((sret < 0) ||
142 (delta != (size_t) sret))
143 {
144 LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_WARNING,
145 "read",
146 fhc->filename);
147 file_hash_finish (fhc,
148 NULL);
149 return;
150 }
151 gcry_md_write (fhc->md,
152 fhc->buffer,
153 delta);
154 fhc->offset += delta;
155 if (fhc->offset == fhc->fsize)
156 {
157 res = (struct GNUNET_HashCode *) gcry_md_read (fhc->md,
158 GCRY_MD_SHA512);
159 file_hash_finish (fhc, res);
160 return;
161 }
162 fhc->task = GNUNET_SCHEDULER_add_with_priority (fhc->priority,
163 &file_hash_task,
164 fhc);
165}
166
167
168struct GNUNET_CRYPTO_FileHashContext *
169GNUNET_CRYPTO_hash_file (enum GNUNET_SCHEDULER_Priority priority,
170 const char *filename,
171 size_t blocksize,
172 GNUNET_CRYPTO_HashCompletedCallback callback,
173 void *callback_cls)
174{
175 struct GNUNET_CRYPTO_FileHashContext *fhc;
176
177 GNUNET_assert (blocksize > 0);
178 fhc =
179 GNUNET_malloc (sizeof(struct GNUNET_CRYPTO_FileHashContext) + blocksize);
180 fhc->callback = callback;
181 fhc->callback_cls = callback_cls;
182 fhc->buffer = (unsigned char *) &fhc[1];
183 fhc->filename = GNUNET_strdup (filename);
184 if (GPG_ERR_NO_ERROR != gcry_md_open (&fhc->md, GCRY_MD_SHA512, 0))
185 {
186 GNUNET_break (0);
187 GNUNET_free (fhc->filename);
188 GNUNET_free (fhc);
189 return NULL;
190 }
191 fhc->bsize = blocksize;
192 if (GNUNET_OK !=
193 GNUNET_DISK_file_size (filename,
194 &fhc->fsize,
195 GNUNET_NO,
196 GNUNET_YES))
197 {
198 GNUNET_free (fhc->filename);
199 GNUNET_free (fhc);
200 return NULL;
201 }
202 fhc->fh = GNUNET_DISK_file_open (filename,
203 GNUNET_DISK_OPEN_READ,
204 GNUNET_DISK_PERM_NONE);
205 if (! fhc->fh)
206 {
207 GNUNET_free (fhc->filename);
208 GNUNET_free (fhc);
209 return NULL;
210 }
211 fhc->priority = priority;
212 fhc->task = GNUNET_SCHEDULER_add_with_priority (priority,
213 &file_hash_task,
214 fhc);
215 return fhc;
216}
217
218
219/**
220 * Cancel a file hashing operation.
221 *
222 * @param fhc operation to cancel (callback must not yet have been invoked)
223 */
224void
225GNUNET_CRYPTO_hash_file_cancel (struct GNUNET_CRYPTO_FileHashContext *fhc)
226{
227 GNUNET_SCHEDULER_cancel (fhc->task);
228 GNUNET_free (fhc->filename);
229 GNUNET_break (GNUNET_OK ==
230 GNUNET_DISK_file_close (fhc->fh));
231 gcry_md_close (fhc->md);
232 GNUNET_free (fhc);
233}
234
235
236/* end of crypto_hash_file.c */
diff --git a/src/lib/util/crypto_hkdf.c b/src/lib/util/crypto_hkdf.c
new file mode 100644
index 000000000..e724bc9ba
--- /dev/null
+++ b/src/lib/util/crypto_hkdf.c
@@ -0,0 +1,369 @@
1/*
2 Copyright (c) 2010 Nils Durner
3
4 Permission is hereby granted, free of charge, to any person obtaining a copy
5 of this software and associated documentation files (the "Software"), to deal
6 in the Software without restriction, including without limitation the rights
7 to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
8 copies of the Software, and to permit persons to whom the Software is
9 furnished to do so, subject to the following conditions:
10
11 The above copyright notice and this permission notice shall be included in
12 all copies or substantial portions of the Software.
13
14 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16 FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17 AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18 LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
19 OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
20 THE SOFTWARE.
21 */
22
23/**
24 * @file src/util/crypto_hkdf.c
25 * @brief Hash-based KDF as defined in RFC 5869
26 * @see http://www.rfc-editor.org/rfc/rfc5869.txt
27 * @todo remove GNUNET references
28 * @author Nils Durner
29 *
30 * The following list of people have reviewed this code and considered
31 * it correct on the date given (if you reviewed it, please
32 * have your name added to the list):
33 *
34 * - Christian Grothoff (08.10.2010)
35 * - Nathan Evans (08.10.2010)
36 * - Matthias Wachs (08.10.2010)
37 */
38
39#define LOG(kind, ...) GNUNET_log_from (kind, "util-crypto-hkdf", __VA_ARGS__)
40
41/**
42 * Set this to 0 if you compile this code outside of GNUnet.
43 */
44#define GNUNET_BUILD 1
45
46/**
47 * Enable debugging.
48 */
49#define DEBUG_HKDF 0
50
51
52#if GNUNET_BUILD
53
54#include "platform.h"
55#include "gnunet_util_lib.h"
56#include "benchmark.h"
57#else
58#define GNUNET_NO 0
59#define GNUNET_YES 1
60#define GNUNET_SYSERR -1
61#include <stdlib.h>
62#endif
63
64#include <gcrypt.h>
65
66
67/**
68 * @brief Compute the HMAC
69 * @todo use chunked buffers
70 * @param mac gcrypt MAC handle
71 * @param key HMAC key
72 * @param key_len length of key
73 * @param buf message to be processed
74 * @param buf_len length of buf
75 * @return HMAC, freed by caller via gcry_md_close/_reset
76 */
77static const void *
78doHMAC (gcry_md_hd_t mac,
79 const void *key,
80 size_t key_len,
81 const void *buf,
82 size_t buf_len)
83{
84 if (GPG_ERR_NO_ERROR !=
85 gcry_md_setkey (mac, key, key_len))
86 {
87 GNUNET_break (0);
88 return NULL;
89 }
90 gcry_md_write (mac,
91 buf,
92 buf_len);
93 return (const void *) gcry_md_read (mac, 0);
94}
95
96
97/**
98 * @brief Generate pseudo-random key
99 * @param mac gcrypt HMAC handle
100 * @param xts salt
101 * @param xts_len length of the @a xts salt
102 * @param skm source key material
103 * @param skm_len length of @a skm
104 * @param prk result buffer (allocated by caller; at least gcry_md_dlen() bytes)
105 * @return #GNUNET_YES on success
106 */
107static enum GNUNET_GenericReturnValue
108getPRK (gcry_md_hd_t mac,
109 const void *xts,
110 size_t xts_len,
111 const void *skm,
112 size_t skm_len,
113 void *prk)
114{
115 const void *ret;
116 size_t dlen;
117
118 dlen = gcry_md_get_algo_dlen (gcry_md_get_algo (mac));
119
120 /* sanity check to bound stack allocation */
121 GNUNET_assert (dlen <= 512);
122
123 /* From RFC 5869:
124 * salt - optional salt value (a non-secret random value);
125 * if not provided, it is set to a string of HashLen zeros. */
126
127 if (0 == xts_len)
128 {
129 char zero_salt[dlen];
130
131 memset (zero_salt, 0, dlen);
132 ret = doHMAC (mac, zero_salt, dlen, skm, skm_len);
133 }
134 else
135 {
136 ret = doHMAC (mac, xts, xts_len, skm, skm_len);
137 }
138 if (NULL == ret)
139 return GNUNET_SYSERR;
140 GNUNET_memcpy (prk,
141 ret,
142 dlen);
143 return GNUNET_YES;
144}
145
146
147#if DEBUG_HKDF
148static void
149dump (const char *src,
150 const void *p,
151 unsigned int l)
152{
153 printf ("\n%s: ", src);
154 for (unsigned int i = 0; i < l; i++)
155 {
156 printf ("%2x", (int) ((const unsigned char *) p)[i]);
157 }
158 printf ("\n");
159}
160
161
162#endif
163
164
165enum GNUNET_GenericReturnValue
166GNUNET_CRYPTO_hkdf_v (void *result,
167 size_t out_len,
168 int xtr_algo,
169 int prf_algo,
170 const void *xts,
171 size_t xts_len,
172 const void *skm,
173 size_t skm_len,
174 va_list argp)
175{
176 gcry_md_hd_t xtr;
177 gcry_md_hd_t prf;
178 const void *hc;
179 unsigned long i;
180 unsigned long t;
181 unsigned long d;
182 unsigned int k = gcry_md_get_algo_dlen (prf_algo);
183 unsigned int xtr_len = gcry_md_get_algo_dlen (xtr_algo);
184 char prk[xtr_len];
185 int ret;
186 size_t ctx_len;
187 va_list args;
188
189 BENCHMARK_START (hkdf);
190
191 if (0 == k)
192 return GNUNET_SYSERR;
193 if (GPG_ERR_NO_ERROR !=
194 gcry_md_open (&xtr,
195 xtr_algo,
196 GCRY_MD_FLAG_HMAC))
197 return GNUNET_SYSERR;
198 if (GPG_ERR_NO_ERROR !=
199 gcry_md_open (&prf,
200 prf_algo,
201 GCRY_MD_FLAG_HMAC))
202 {
203 gcry_md_close (xtr);
204 return GNUNET_SYSERR;
205 }
206 va_copy (args, argp);
207
208 ctx_len = 0;
209 while (NULL != va_arg (args, void *))
210 {
211 size_t nxt = va_arg (args, size_t);
212 if (nxt + ctx_len < nxt)
213 {
214 /* integer overflow */
215 GNUNET_break (0);
216 va_end (args);
217 goto hkdf_error;
218 }
219 ctx_len += nxt;
220 }
221
222 va_end (args);
223
224 if ( (k + ctx_len < ctx_len) ||
225 (k + ctx_len + 1 < ctx_len) )
226 {
227 /* integer overflow */
228 GNUNET_break (0);
229 goto hkdf_error;
230 }
231
232 memset (result, 0, out_len);
233 if (GNUNET_YES !=
234 getPRK (xtr, xts, xts_len, skm, skm_len, prk))
235 goto hkdf_error;
236#if DEBUG_HKDF
237 dump ("PRK", prk, xtr_len);
238#endif
239
240 t = out_len / k;
241 d = out_len % k;
242
243 /* K(1) */
244 {
245 size_t plain_len = k + ctx_len + 1;
246 char *plain;
247 const void *ctx;
248 char *dst;
249
250 plain = GNUNET_malloc (plain_len);
251 dst = plain + k;
252 va_copy (args, argp);
253 while ((ctx = va_arg (args, void *)))
254 {
255 size_t len;
256
257 len = va_arg (args, size_t);
258 GNUNET_memcpy (dst, ctx, len);
259 dst += len;
260 }
261 va_end (args);
262
263 if (t > 0)
264 {
265 plain[k + ctx_len] = (char) 1;
266#if DEBUG_HKDF
267 dump ("K(1)", plain, plain_len);
268#endif
269 hc = doHMAC (prf, prk, xtr_len, &plain[k], ctx_len + 1);
270 if (hc == NULL)
271 {
272 GNUNET_free (plain);
273 goto hkdf_error;
274 }
275 GNUNET_memcpy (result, hc, k);
276 result += k;
277 }
278
279 /* K(i+1) */
280 for (i = 1; i < t; i++)
281 {
282 GNUNET_memcpy (plain, result - k, k);
283 plain[k + ctx_len] = (char) (i + 1);
284 gcry_md_reset (prf);
285#if DEBUG_HKDF
286 dump ("K(i+1)", plain, plain_len);
287#endif
288 hc = doHMAC (prf, prk, xtr_len, plain, plain_len);
289 if (NULL == hc)
290 {
291 GNUNET_free (plain);
292 goto hkdf_error;
293 }
294 GNUNET_memcpy (result, hc, k);
295 result += k;
296 }
297
298 /* K(t):d */
299 if (d > 0)
300 {
301 if (t > 0)
302 {
303 GNUNET_memcpy (plain, result - k, k);
304 i++;
305 }
306 plain[k + ctx_len] = (char) i;
307 gcry_md_reset (prf);
308#if DEBUG_HKDF
309 dump ("K(t):d", plain, plain_len);
310#endif
311 if (t > 0)
312 hc = doHMAC (prf, prk, xtr_len, plain, plain_len);
313 else
314 hc = doHMAC (prf, prk, xtr_len, plain + k, plain_len - k);
315 if (hc == NULL)
316 {
317 GNUNET_free (plain);
318 goto hkdf_error;
319 }
320 GNUNET_memcpy (result, hc, d);
321 }
322#if DEBUG_HKDF
323 dump ("result", result - k, out_len);
324#endif
325
326 ret = GNUNET_YES;
327 GNUNET_free (plain);
328 goto hkdf_ok;
329 }
330hkdf_error:
331 ret = GNUNET_SYSERR;
332hkdf_ok:
333 gcry_md_close (xtr);
334 gcry_md_close (prf);
335 BENCHMARK_END (hkdf);
336 return ret;
337}
338
339
340enum GNUNET_GenericReturnValue
341GNUNET_CRYPTO_hkdf (void *result,
342 size_t out_len,
343 int xtr_algo,
344 int prf_algo,
345 const void *xts,
346 size_t xts_len,
347 const void *skm,
348 size_t skm_len, ...)
349{
350 va_list argp;
351 enum GNUNET_GenericReturnValue ret;
352
353 va_start (argp, skm_len);
354 ret =
355 GNUNET_CRYPTO_hkdf_v (result,
356 out_len,
357 xtr_algo,
358 prf_algo,
359 xts,
360 xts_len,
361 skm,
362 skm_len,
363 argp);
364 va_end (argp);
365 return ret;
366}
367
368
369/* end of crypto_hkdf.c */
diff --git a/src/lib/util/crypto_kdf.c b/src/lib/util/crypto_kdf.c
new file mode 100644
index 000000000..7cfd8fea4
--- /dev/null
+++ b/src/lib/util/crypto_kdf.c
@@ -0,0 +1,145 @@
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 src/util/crypto_kdf.c
23 * @brief Key derivation
24 * @author Nils Durner
25 * @author Jeffrey Burdges <burdges@gnunet.org>
26 */
27
28#include "platform.h"
29#include <gcrypt.h>
30
31
32#include "gnunet_util_lib.h"
33
34#define LOG(kind, ...) GNUNET_log_from (kind, "util-crypto-kdf", __VA_ARGS__)
35
36
37enum GNUNET_GenericReturnValue
38GNUNET_CRYPTO_kdf_v (void *result,
39 size_t out_len,
40 const void *xts,
41 size_t xts_len,
42 const void *skm,
43 size_t skm_len,
44 va_list argp)
45{
46 /*
47 * "Finally, we point out to a particularly advantageous instantiation using
48 * HMAC-SHA512 as XTR and HMAC-SHA256 in PRF* (in which case the output from SHA-512 is
49 * truncated to 256 bits). This makes sense in two ways: First, the extraction part is where we need a
50 * stronger hash function due to the unconventional demand from the hash function in the extraction
51 * setting. Second, as shown in Section 6, using HMAC with a truncated output as an extractor
52 * allows to prove the security of HKDF under considerably weaker assumptions on the underlying
53 * hash function."
54 *
55 * http://eprint.iacr.org/2010/264
56 */
57 return GNUNET_CRYPTO_hkdf_v (result,
58 out_len,
59 GCRY_MD_SHA512,
60 GCRY_MD_SHA256,
61 xts,
62 xts_len,
63 skm,
64 skm_len,
65 argp);
66}
67
68
69enum GNUNET_GenericReturnValue
70GNUNET_CRYPTO_kdf (void *result,
71 size_t out_len,
72 const void *xts,
73 size_t xts_len,
74 const void *skm,
75 size_t skm_len, ...)
76{
77 va_list argp;
78 int ret;
79
80 va_start (argp, skm_len);
81 ret = GNUNET_CRYPTO_kdf_v (result,
82 out_len,
83 xts,
84 xts_len,
85 skm,
86 skm_len,
87 argp);
88 va_end (argp);
89
90 return ret;
91}
92
93
94void
95GNUNET_CRYPTO_kdf_mod_mpi (gcry_mpi_t *r,
96 gcry_mpi_t n,
97 const void *xts, size_t xts_len,
98 const void *skm, size_t skm_len,
99 const char *ctx)
100{
101 gcry_error_t rc;
102 unsigned int nbits;
103 size_t rsize;
104 uint16_t ctr;
105
106 nbits = gcry_mpi_get_nbits (n);
107 /* GNUNET_assert (nbits > 512); */
108 ctr = 0;
109 while (1)
110 {
111 /* Ain't clear if n is always divisible by 8 */
112 size_t bsize = (nbits - 1) / 8 + 1;
113 uint8_t buf[bsize];
114 uint16_t ctr_nbo = htons (ctr);
115
116 rc = GNUNET_CRYPTO_kdf (buf,
117 bsize,
118 xts, xts_len,
119 skm, skm_len,
120 ctx, strlen (ctx),
121 &ctr_nbo, sizeof(ctr_nbo),
122 NULL, 0);
123 GNUNET_assert (GNUNET_YES == rc);
124 rc = gcry_mpi_scan (r,
125 GCRYMPI_FMT_USG,
126 (const unsigned char *) buf,
127 bsize,
128 &rsize);
129 GNUNET_assert (GPG_ERR_NO_ERROR == rc); /* Allocation error? */
130 GNUNET_assert (rsize == bsize);
131 gcry_mpi_clear_highbit (*r,
132 nbits);
133 GNUNET_assert (0 ==
134 gcry_mpi_test_bit (*r,
135 nbits));
136 ++ctr;
137 /* We reject this FDH if either *r > n and retry with another ctr */
138 if (0 > gcry_mpi_cmp (*r, n))
139 break;
140 gcry_mpi_release (*r);
141 }
142}
143
144
145/* end of crypto_kdf.c */
diff --git a/src/lib/util/crypto_mpi.c b/src/lib/util/crypto_mpi.c
new file mode 100644
index 000000000..06f2fd786
--- /dev/null
+++ b/src/lib/util/crypto_mpi.c
@@ -0,0 +1,178 @@
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/crypto_mpi.c
23 * @brief Helper functions for libgcrypt MPIs
24 * @author Christian Grothoff
25 * @author Florian Dold
26 */
27
28#include "platform.h"
29#include <gcrypt.h>
30#include "gnunet_util_lib.h"
31
32
33#define LOG(kind, ...) GNUNET_log_from (kind, "util-crypto-mpi", __VA_ARGS__)
34
35/**
36 * Log an error message at log-level 'level' that indicates
37 * a failure of the command 'cmd' with the message given
38 * by gcry_strerror(rc).
39 */
40#define LOG_GCRY(level, cmd, rc) do { LOG (level, _ ( \
41 "`%s' failed at %s:%d with error: %s\n"), \
42 cmd, __FILE__, __LINE__, \
43 gcry_strerror (rc)); } while (0)
44
45
46/**
47 * If target != size, move @a target bytes to the end of the size-sized
48 * buffer and zero out the first @a target - @a size bytes.
49 *
50 * @param buf original buffer
51 * @param size number of bytes in @a buf
52 * @param target target size of the buffer
53 */
54static void
55adjust (void *buf,
56 size_t size,
57 size_t target)
58{
59 char *p = buf;
60
61 if (size < target)
62 {
63 memmove (&p[target - size], buf, size);
64 memset (buf, 0, target - size);
65 }
66}
67
68
69/**
70 * Output the given MPI value to the given buffer in
71 * network byte order.
72 * The MPI @a val may not be negative.
73 *
74 * @param buf where to output to
75 * @param size number of bytes in @a buf
76 * @param val value to write to @a buf
77 */
78void
79GNUNET_CRYPTO_mpi_print_unsigned (void *buf,
80 size_t size,
81 gcry_mpi_t val)
82{
83 size_t rsize;
84 int rc;
85
86 if (gcry_mpi_get_flag (val, GCRYMPI_FLAG_OPAQUE))
87 {
88 /* Store opaque MPIs left aligned into the buffer. */
89 unsigned int nbits;
90 const void *p;
91
92 p = gcry_mpi_get_opaque (val, &nbits);
93 GNUNET_assert (p);
94 rsize = (nbits + 7) / 8;
95 if (rsize > size)
96 rsize = size;
97 GNUNET_memcpy (buf, p, rsize);
98 if (rsize < size)
99 memset (buf + rsize, 0, size - rsize);
100 }
101 else
102 {
103 /* Store regular MPIs as unsigned integers right aligned into
104 the buffer. */
105 rsize = size;
106 if (0 !=
107 (rc = gcry_mpi_print (GCRYMPI_FMT_USG,
108 buf,
109 rsize, &rsize,
110 val)))
111 {
112 LOG_GCRY (GNUNET_ERROR_TYPE_ERROR,
113 "gcry_mpi_print",
114 rc);
115 GNUNET_assert (0);
116 }
117 adjust (buf, rsize, size);
118 }
119}
120
121
122/**
123 * Convert data buffer into MPI value.
124 * The buffer is interpreted as network
125 * byte order, unsigned integer.
126 *
127 * @param result where to store MPI value (allocated)
128 * @param data raw data (GCRYMPI_FMT_USG)
129 * @param size number of bytes in @a data
130 */
131void
132GNUNET_CRYPTO_mpi_scan_unsigned (gcry_mpi_t *result,
133 const void *data,
134 size_t size)
135{
136 int rc;
137
138 if (0 != (rc = gcry_mpi_scan (result,
139 GCRYMPI_FMT_USG,
140 data, size, &size)))
141 {
142 LOG_GCRY (GNUNET_ERROR_TYPE_ERROR,
143 "gcry_mpi_scan",
144 rc);
145 GNUNET_assert (0);
146 }
147}
148
149
150/**
151 * Convert little endian data buffer into MPI value.
152 * The buffer is interpreted as network
153 * byte order, unsigned integer.
154 *
155 * @param result where to store MPI value (allocated)
156 * @param data raw data (GCRYMPI_FMT_USG)
157 * @param size number of bytes in @a data
158 */
159void
160GNUNET_CRYPTO_mpi_scan_unsigned_le (gcry_mpi_t *result,
161 const void *data,
162 size_t size)
163{
164 int rc;
165
166 if (0 != (rc = gcry_mpi_scan (result,
167 GCRYMPI_FMT_USG,
168 data, size, &size)))
169 {
170 LOG_GCRY (GNUNET_ERROR_TYPE_ERROR,
171 "gcry_mpi_scan",
172 rc);
173 GNUNET_assert (0);
174 }
175}
176
177
178/* end of crypto_mpi.c */
diff --git a/src/lib/util/crypto_paillier.c b/src/lib/util/crypto_paillier.c
new file mode 100644
index 000000000..169d1e49e
--- /dev/null
+++ b/src/lib/util/crypto_paillier.c
@@ -0,0 +1,463 @@
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/**
22 * @file util/crypto_paillier.c
23 * @brief implementation of the paillier crypto system with libgcrypt
24 * @author Florian Dold
25 * @author Christian Fuchs
26 */
27
28#include "platform.h"
29#include <gcrypt.h>
30#include "gnunet_util_lib.h"
31
32
33/**
34 * Create a freshly generated paillier public key.
35 *
36 * @param[out] public_key Where to store the public key?
37 * @param[out] private_key Where to store the private key?
38 */
39void
40GNUNET_CRYPTO_paillier_create (struct
41 GNUNET_CRYPTO_PaillierPublicKey *public_key,
42 struct GNUNET_CRYPTO_PaillierPrivateKey *
43 private_key)
44{
45 gcry_mpi_t p;
46 gcry_mpi_t q;
47 gcry_mpi_t phi;
48 gcry_mpi_t mu;
49 gcry_mpi_t n;
50
51 /* Generate two distinct primes. The probability that the loop body
52 is executed more than once is very very low... */
53 p = NULL;
54 q = NULL;
55 do
56 {
57 if (NULL != p)
58 gcry_mpi_release (p);
59 if (NULL != q)
60 gcry_mpi_release (q);
61 GNUNET_assert (0 ==
62 gcry_prime_generate (&p,
63 GNUNET_CRYPTO_PAILLIER_BITS / 2,
64 0, NULL, NULL, NULL,
65 GCRY_STRONG_RANDOM, 0));
66 GNUNET_assert (0 ==
67 gcry_prime_generate (&q,
68 GNUNET_CRYPTO_PAILLIER_BITS / 2,
69 0, NULL, NULL, NULL,
70 GCRY_STRONG_RANDOM, 0));
71 }
72 while (0 == gcry_mpi_cmp (p, q));
73 /* n = p * q */
74 GNUNET_assert (NULL != (n = gcry_mpi_new (0)));
75 gcry_mpi_mul (n,
76 p,
77 q);
78 GNUNET_CRYPTO_mpi_print_unsigned (public_key,
79 sizeof(struct
80 GNUNET_CRYPTO_PaillierPublicKey),
81 n);
82
83 /* compute phi(n) = (p-1)(q-1) */
84 GNUNET_assert (NULL != (phi = gcry_mpi_new (0)));
85 gcry_mpi_sub_ui (p, p, 1);
86 gcry_mpi_sub_ui (q, q, 1);
87 gcry_mpi_mul (phi, p, q);
88 gcry_mpi_release (p);
89 gcry_mpi_release (q);
90
91 /* lambda equals phi(n) in the simplified key generation */
92 GNUNET_CRYPTO_mpi_print_unsigned (private_key->lambda,
93 GNUNET_CRYPTO_PAILLIER_BITS / 8,
94 phi);
95 /* mu = phi^{-1} mod n, as we use g = n + 1 */
96 GNUNET_assert (NULL != (mu = gcry_mpi_new (0)));
97 GNUNET_assert (0 != gcry_mpi_invm (mu,
98 phi,
99 n));
100 gcry_mpi_release (phi);
101 gcry_mpi_release (n);
102 GNUNET_CRYPTO_mpi_print_unsigned (private_key->mu,
103 GNUNET_CRYPTO_PAILLIER_BITS / 8,
104 mu);
105 gcry_mpi_release (mu);
106}
107
108
109/**
110 * Encrypt a plaintext with a paillier public key.
111 *
112 * @param public_key Public key to use.
113 * @param m Plaintext to encrypt.
114 * @param desired_ops How many homomorphic ops the caller intends to use
115 * @param[out] ciphertext Encryption of @a plaintext with @a public_key.
116 * @return guaranteed number of supported homomorphic operations >= 1,
117 * or desired_ops, in case that is lower,
118 * or -1 if less than one homomorphic operation is possible
119 */
120int
121GNUNET_CRYPTO_paillier_encrypt1 (const struct
122 GNUNET_CRYPTO_PaillierPublicKey *public_key,
123 const gcry_mpi_t m,
124 int desired_ops,
125 struct GNUNET_CRYPTO_PaillierCiphertext *
126 ciphertext)
127{
128 int possible_opts;
129 gcry_mpi_t n_square;
130 gcry_mpi_t r;
131 gcry_mpi_t c;
132 gcry_mpi_t n;
133 gcry_mpi_t tmp1;
134 gcry_mpi_t tmp2;
135 unsigned int highbit;
136
137 /* determine how many operations we could allow, if the other number
138 has the same length. */
139 GNUNET_assert (NULL != (tmp1 = gcry_mpi_set_ui (NULL, 1)));
140 GNUNET_assert (NULL != (tmp2 = gcry_mpi_set_ui (NULL, 2)));
141 gcry_mpi_mul_2exp (tmp1, tmp1, GNUNET_CRYPTO_PAILLIER_BITS);
142
143 /* count number of possible operations
144 this would be nicer with gcry_mpi_get_nbits, however it does not return
145 the BITLENGTH of the given MPI's value, but the bits required
146 to represent the number as MPI. */
147 for (possible_opts = -2; gcry_mpi_cmp (tmp1, m) > 0; possible_opts++)
148 gcry_mpi_div (tmp1, NULL, tmp1, tmp2, 0);
149 gcry_mpi_release (tmp1);
150 gcry_mpi_release (tmp2);
151
152 if (possible_opts < 1)
153 possible_opts = 0;
154 /* soft-cap by caller */
155 possible_opts = (desired_ops < possible_opts) ? desired_ops : possible_opts;
156
157 ciphertext->remaining_ops = htonl (possible_opts);
158
159 GNUNET_CRYPTO_mpi_scan_unsigned (&n,
160 public_key,
161 sizeof(struct
162 GNUNET_CRYPTO_PaillierPublicKey));
163 highbit = GNUNET_CRYPTO_PAILLIER_BITS - 1;
164 while ((! gcry_mpi_test_bit (n, highbit)) &&
165 (0 != highbit))
166 highbit--;
167 if (0 == highbit)
168 {
169 /* invalid public key */
170 GNUNET_break_op (0);
171 gcry_mpi_release (n);
172 return GNUNET_SYSERR;
173 }
174 GNUNET_assert (0 != (n_square = gcry_mpi_new (0)));
175 GNUNET_assert (0 != (r = gcry_mpi_new (0)));
176 GNUNET_assert (0 != (c = gcry_mpi_new (0)));
177 gcry_mpi_mul (n_square, n, n);
178
179 /* generate r < n (without bias) */
180 do
181 {
182 gcry_mpi_randomize (r, highbit + 1, GCRY_STRONG_RANDOM);
183 }
184 while (gcry_mpi_cmp (r, n) >= 0);
185
186 /* c = (n+1)^m mod n^2 */
187 /* c = n + 1 */
188 gcry_mpi_add_ui (c, n, 1);
189 /* c = (n+1)^m mod n^2 */
190 gcry_mpi_powm (c, c, m, n_square);
191 /* r <- r^n mod n^2 */
192 gcry_mpi_powm (r, r, n, n_square);
193 /* c <- r*c mod n^2 */
194 gcry_mpi_mulm (c, r, c, n_square);
195
196 GNUNET_CRYPTO_mpi_print_unsigned (ciphertext->bits,
197 sizeof ciphertext->bits,
198 c);
199
200 gcry_mpi_release (n_square);
201 gcry_mpi_release (n);
202 gcry_mpi_release (r);
203 gcry_mpi_release (c);
204
205 return possible_opts;
206}
207
208
209/**
210 * Encrypt a plaintext with a paillier public key.
211 *
212 * @param public_key Public key to use.
213 * @param m Plaintext to encrypt.
214 * @param desired_ops How many homomorphic ops the caller intends to use
215 * @param[out] ciphertext Encryption of @a plaintext with @a public_key.
216 * @return guaranteed number of supported homomorphic operations >= 1,
217 * or desired_ops, in case that is lower,
218 * or -1 if less than one homomorphic operation is possible
219 */
220int
221GNUNET_CRYPTO_paillier_encrypt (const struct
222 GNUNET_CRYPTO_PaillierPublicKey *public_key,
223 const gcry_mpi_t m,
224 int desired_ops,
225 struct GNUNET_CRYPTO_PaillierCiphertext *
226 ciphertext)
227{
228 int possible_opts;
229 gcry_mpi_t n_square;
230 gcry_mpi_t r;
231 gcry_mpi_t rn;
232 gcry_mpi_t g;
233 gcry_mpi_t gm;
234 gcry_mpi_t c;
235 gcry_mpi_t n;
236 gcry_mpi_t max_num;
237 unsigned int highbit;
238
239 /* set max_num = 2^{GNUNET_CRYPTO_PAILLIER_BITS}, the largest
240 number we can have as a result */
241 GNUNET_assert (NULL != (max_num = gcry_mpi_set_ui (NULL, 1)));
242 gcry_mpi_mul_2exp (max_num,
243 max_num,
244 GNUNET_CRYPTO_PAILLIER_BITS);
245
246 /* Determine how many operations we could allow, assuming the other
247 number has the same length (or is smaller), by counting the
248 number of possible operations. We essentially divide max_num by
249 2 until the result is no longer larger than 'm', incrementing the
250 maximum number of operations in each round, starting at -2 */for (possible_opts = -2; gcry_mpi_cmp (max_num, m) > 0; possible_opts++)
251 gcry_mpi_div (max_num,
252 NULL,
253 max_num,
254 GCRYMPI_CONST_TWO,
255 0);
256 gcry_mpi_release (max_num);
257
258 if (possible_opts < 1)
259 possible_opts = 0;
260 /* Enforce soft-cap by caller */
261 possible_opts = GNUNET_MIN (desired_ops, possible_opts);
262 ciphertext->remaining_ops = htonl (possible_opts);
263
264 GNUNET_CRYPTO_mpi_scan_unsigned (&n,
265 public_key,
266 sizeof(struct
267 GNUNET_CRYPTO_PaillierPublicKey));
268
269 /* check public key for number of bits, bail out if key is all zeros */
270 highbit = GNUNET_CRYPTO_PAILLIER_BITS - 1;
271 while ((! gcry_mpi_test_bit (n, highbit)) &&
272 (0 != highbit))
273 highbit--;
274 if (0 == highbit)
275 {
276 /* invalid public key */
277 GNUNET_break_op (0);
278 gcry_mpi_release (n);
279 return GNUNET_SYSERR;
280 }
281
282 /* generate r < n (without bias) */
283 GNUNET_assert (NULL != (r = gcry_mpi_new (0)));
284 do
285 {
286 gcry_mpi_randomize (r, highbit + 1, GCRY_STRONG_RANDOM);
287 }
288 while (gcry_mpi_cmp (r, n) >= 0);
289
290 /* g = n + 1 */
291 GNUNET_assert (0 != (g = gcry_mpi_new (0)));
292 gcry_mpi_add_ui (g, n, 1);
293
294 /* n_square = n^2 */
295 GNUNET_assert (0 != (n_square = gcry_mpi_new (0)));
296 gcry_mpi_mul (n_square,
297 n,
298 n);
299
300 /* gm = g^m mod n^2 */
301 GNUNET_assert (0 != (gm = gcry_mpi_new (0)));
302 gcry_mpi_powm (gm, g, m, n_square);
303 gcry_mpi_release (g);
304
305 /* rn <- r^n mod n^2 */
306 GNUNET_assert (0 != (rn = gcry_mpi_new (0)));
307 gcry_mpi_powm (rn, r, n, n_square);
308 gcry_mpi_release (r);
309 gcry_mpi_release (n);
310
311 /* c <- rn * gm mod n^2 */
312 GNUNET_assert (0 != (c = gcry_mpi_new (0)));
313 gcry_mpi_mulm (c, rn, gm, n_square);
314 gcry_mpi_release (n_square);
315 gcry_mpi_release (gm);
316 gcry_mpi_release (rn);
317
318 GNUNET_CRYPTO_mpi_print_unsigned (ciphertext->bits,
319 sizeof(ciphertext->bits),
320 c);
321 gcry_mpi_release (c);
322
323 return possible_opts;
324}
325
326
327void
328GNUNET_CRYPTO_paillier_decrypt (const struct
329 GNUNET_CRYPTO_PaillierPrivateKey *private_key,
330 const struct
331 GNUNET_CRYPTO_PaillierPublicKey *public_key,
332 const struct
333 GNUNET_CRYPTO_PaillierCiphertext *ciphertext,
334 gcry_mpi_t m)
335{
336 gcry_mpi_t mu;
337 gcry_mpi_t lambda;
338 gcry_mpi_t n;
339 gcry_mpi_t n_square;
340 gcry_mpi_t c;
341 gcry_mpi_t cmu;
342 gcry_mpi_t cmum1;
343 gcry_mpi_t mod;
344
345 GNUNET_CRYPTO_mpi_scan_unsigned (&lambda,
346 private_key->lambda,
347 sizeof(private_key->lambda));
348 GNUNET_CRYPTO_mpi_scan_unsigned (&mu,
349 private_key->mu,
350 sizeof(private_key->mu));
351 GNUNET_CRYPTO_mpi_scan_unsigned (&n,
352 public_key,
353 sizeof(struct
354 GNUNET_CRYPTO_PaillierPublicKey));
355 GNUNET_CRYPTO_mpi_scan_unsigned (&c,
356 ciphertext->bits,
357 sizeof(ciphertext->bits));
358
359 /* n_square = n * n */
360 GNUNET_assert (0 != (n_square = gcry_mpi_new (0)));
361 gcry_mpi_mul (n_square, n, n);
362
363 /* cmu = c^lambda mod n^2 */
364 GNUNET_assert (0 != (cmu = gcry_mpi_new (0)));
365 gcry_mpi_powm (cmu,
366 c,
367 lambda,
368 n_square);
369 gcry_mpi_release (n_square);
370 gcry_mpi_release (lambda);
371 gcry_mpi_release (c);
372
373 /* cmum1 = cmu - 1 */
374 GNUNET_assert (0 != (cmum1 = gcry_mpi_new (0)));
375 gcry_mpi_sub_ui (cmum1, cmu, 1);
376 gcry_mpi_release (cmu);
377
378 /* mod = cmum1 / n (mod n) */
379 GNUNET_assert (0 != (mod = gcry_mpi_new (0)));
380 gcry_mpi_div (mod, NULL, cmum1, n, 0);
381 gcry_mpi_release (cmum1);
382
383 /* m = mod * mu mod n */
384 gcry_mpi_mulm (m, mod, mu, n);
385 gcry_mpi_release (mod);
386 gcry_mpi_release (mu);
387 gcry_mpi_release (n);
388}
389
390
391int
392GNUNET_CRYPTO_paillier_hom_add (const struct
393 GNUNET_CRYPTO_PaillierPublicKey *public_key,
394 const struct
395 GNUNET_CRYPTO_PaillierCiphertext *c1,
396 const struct
397 GNUNET_CRYPTO_PaillierCiphertext *c2,
398 struct GNUNET_CRYPTO_PaillierCiphertext *result)
399{
400 gcry_mpi_t a;
401 gcry_mpi_t b;
402 gcry_mpi_t c;
403 gcry_mpi_t n;
404 gcry_mpi_t n_square;
405 int32_t o1;
406 int32_t o2;
407
408 o1 = (int32_t) ntohl (c1->remaining_ops);
409 o2 = (int32_t) ntohl (c2->remaining_ops);
410 if ((0 >= o1) || (0 >= o2))
411 {
412 GNUNET_break_op (0);
413 return GNUNET_SYSERR;
414 }
415
416 GNUNET_CRYPTO_mpi_scan_unsigned (&a,
417 c1->bits,
418 sizeof(c1->bits));
419 GNUNET_CRYPTO_mpi_scan_unsigned (&b,
420 c2->bits,
421 sizeof(c2->bits));
422 GNUNET_CRYPTO_mpi_scan_unsigned (&n,
423 public_key,
424 sizeof(struct
425 GNUNET_CRYPTO_PaillierPublicKey));
426
427 /* n_square = n * n */
428 GNUNET_assert (0 != (n_square = gcry_mpi_new (0)));
429 gcry_mpi_mul (n_square, n, n);
430 gcry_mpi_release (n);
431
432 /* c = a * b mod n_square */
433 GNUNET_assert (0 != (c = gcry_mpi_new (0)));
434 gcry_mpi_mulm (c, a, b, n_square);
435 gcry_mpi_release (n_square);
436 gcry_mpi_release (a);
437 gcry_mpi_release (b);
438
439 result->remaining_ops = htonl (GNUNET_MIN (o1, o2) - 1);
440 GNUNET_CRYPTO_mpi_print_unsigned (result->bits,
441 sizeof(result->bits),
442 c);
443 gcry_mpi_release (c);
444 return ntohl (result->remaining_ops);
445}
446
447
448/**
449 * Get the number of remaining supported homomorphic operations.
450 *
451 * @param c Paillier cipher text.
452 * @return the number of remaining homomorphic operations
453 */
454int
455GNUNET_CRYPTO_paillier_hom_get_remaining (const struct
456 GNUNET_CRYPTO_PaillierCiphertext *c)
457{
458 GNUNET_assert (NULL != c);
459 return ntohl (c->remaining_ops);
460}
461
462
463/* end of crypto_paillier.c */
diff --git a/src/lib/util/crypto_pkey.c b/src/lib/util/crypto_pkey.c
new file mode 100644
index 000000000..92e0fba36
--- /dev/null
+++ b/src/lib/util/crypto_pkey.c
@@ -0,0 +1,639 @@
1/*
2 This file is part of GNUnet.
3 Copyright (C) 2013, 2016, 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/crypto_pkey.c
23 * @brief api to interact handle generic public keys
24 * @author Martin Schanzenbach
25 */
26
27#include "platform.h"
28#include "gnunet_util_lib.h"
29
30
31static enum GNUNET_GenericReturnValue
32check_key_type (uint32_t type)
33{
34 switch (type)
35 {
36 case GNUNET_PUBLIC_KEY_TYPE_ECDSA:
37 case GNUNET_PUBLIC_KEY_TYPE_EDDSA:
38 return GNUNET_OK;
39 default:
40 return GNUNET_SYSERR;
41 }
42 return GNUNET_SYSERR;
43}
44
45
46void
47GNUNET_CRYPTO_private_key_clear (struct GNUNET_CRYPTO_PrivateKey *key)
48{
49 switch (ntohl (key->type))
50 {
51 case GNUNET_PUBLIC_KEY_TYPE_ECDSA:
52 GNUNET_CRYPTO_ecdsa_key_clear (&key->ecdsa_key);
53 break;
54 case GNUNET_PUBLIC_KEY_TYPE_EDDSA:
55 GNUNET_CRYPTO_eddsa_key_clear (&key->eddsa_key);
56 break;
57 default:
58 GNUNET_break (0);
59 }
60}
61
62
63ssize_t
64GNUNET_CRYPTO_private_key_get_length (const struct
65 GNUNET_CRYPTO_PrivateKey *key)
66{
67 switch (ntohl (key->type))
68 {
69 case GNUNET_PUBLIC_KEY_TYPE_ECDSA:
70 return sizeof (key->type) + sizeof (key->ecdsa_key);
71 break;
72 case GNUNET_PUBLIC_KEY_TYPE_EDDSA:
73 return sizeof (key->type) + sizeof (key->eddsa_key);
74 break;
75 default:
76 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
77 "Got key type %u\n", ntohl (key->type));
78 GNUNET_break (0);
79 }
80 return -1;
81}
82
83
84ssize_t
85GNUNET_CRYPTO_public_key_get_length (const struct
86 GNUNET_CRYPTO_PublicKey *key)
87{
88 switch (ntohl (key->type))
89 {
90 case GNUNET_PUBLIC_KEY_TYPE_ECDSA:
91 return sizeof (key->type) + sizeof (key->ecdsa_key);
92 case GNUNET_PUBLIC_KEY_TYPE_EDDSA:
93 return sizeof (key->type) + sizeof (key->eddsa_key);
94 default:
95 GNUNET_break (0);
96 }
97 return -1;
98}
99
100
101ssize_t
102GNUNET_CRYPTO_private_key_length_by_type (enum GNUNET_CRYPTO_KeyType kt)
103{
104 switch (kt)
105 {
106 case GNUNET_PUBLIC_KEY_TYPE_ECDSA:
107 return sizeof (struct GNUNET_CRYPTO_EcdsaPrivateKey);
108 break;
109 case GNUNET_PUBLIC_KEY_TYPE_EDDSA:
110 return sizeof (struct GNUNET_CRYPTO_EcdsaPrivateKey);
111 break;
112 default:
113 GNUNET_break (0);
114 }
115 return -1;
116}
117
118
119enum GNUNET_GenericReturnValue
120GNUNET_CRYPTO_read_public_key_from_buffer (const void *buffer,
121 size_t len,
122 struct GNUNET_CRYPTO_PublicKey *
123 key,
124 size_t *kb_read)
125{
126 if (len < sizeof (key->type))
127 return GNUNET_SYSERR;
128 GNUNET_memcpy (&key->type,
129 buffer,
130 sizeof (key->type));
131 ssize_t length = GNUNET_CRYPTO_public_key_get_length (key);
132 if (len < length)
133 return GNUNET_SYSERR;
134 if (length < 0)
135 return GNUNET_SYSERR;
136 GNUNET_memcpy (&key->ecdsa_key,
137 buffer + sizeof (key->type),
138 length - sizeof (key->type));
139 *kb_read = length;
140 return GNUNET_OK;
141}
142
143
144ssize_t
145GNUNET_CRYPTO_write_public_key_to_buffer (const struct
146 GNUNET_CRYPTO_PublicKey *key,
147 void*buffer,
148 size_t len)
149{
150 const ssize_t length = GNUNET_CRYPTO_public_key_get_length (key);
151 if (len < length)
152 return -1;
153 if (length < 0)
154 return -2;
155 GNUNET_memcpy (buffer, &(key->type), sizeof (key->type));
156 GNUNET_memcpy (buffer + sizeof (key->type), &(key->ecdsa_key), length
157 - sizeof (key->type));
158 return length;
159}
160
161
162enum GNUNET_GenericReturnValue
163GNUNET_CRYPTO_read_private_key_from_buffer (const void *buffer,
164 size_t len,
165 struct
166 GNUNET_CRYPTO_PrivateKey *key,
167 size_t *kb_read)
168{
169 if (len < sizeof (key->type))
170 return GNUNET_SYSERR;
171 GNUNET_memcpy (&key->type,
172 buffer,
173 sizeof (key->type));
174 ssize_t length = GNUNET_CRYPTO_private_key_get_length (key);
175 if (len < length)
176 return GNUNET_SYSERR;
177 if (length < 0)
178 return GNUNET_SYSERR;
179 GNUNET_memcpy (&key->ecdsa_key,
180 buffer + sizeof (key->type),
181 length - sizeof (key->type));
182 *kb_read = length;
183 return GNUNET_OK;
184}
185
186
187ssize_t
188GNUNET_CRYPTO_write_private_key_to_buffer (const struct
189 GNUNET_CRYPTO_PrivateKey *key,
190 void *buffer,
191 size_t len)
192{
193 const ssize_t length = GNUNET_CRYPTO_private_key_get_length (key);
194 if (len < length)
195 return -1;
196 if (length < 0)
197 return -2;
198 GNUNET_memcpy (buffer, &(key->type), sizeof (key->type));
199 GNUNET_memcpy (buffer + sizeof (key->type), &(key->ecdsa_key), length
200 - sizeof (key->type));
201 return length;
202}
203
204
205ssize_t
206GNUNET_CRYPTO_signature_get_length (const struct
207 GNUNET_CRYPTO_Signature *sig)
208{
209 switch (ntohl (sig->type))
210 {
211 case GNUNET_PUBLIC_KEY_TYPE_ECDSA:
212 return sizeof (sig->type) + sizeof (sig->ecdsa_signature);
213 break;
214 case GNUNET_PUBLIC_KEY_TYPE_EDDSA:
215 return sizeof (sig->type) + sizeof (sig->eddsa_signature);
216 break;
217 default:
218 GNUNET_break (0);
219 }
220 return -1;
221}
222
223
224ssize_t
225GNUNET_CRYPTO_signature_get_raw_length_by_type (uint32_t type)
226{
227 switch (ntohl (type))
228 {
229 case GNUNET_PUBLIC_KEY_TYPE_ECDSA:
230 return sizeof (struct GNUNET_CRYPTO_EcdsaSignature);
231 break;
232 case GNUNET_PUBLIC_KEY_TYPE_EDDSA:
233 return sizeof (struct GNUNET_CRYPTO_EddsaSignature);
234 break;
235 default:
236 GNUNET_break (0);
237 }
238 return -1;
239}
240
241
242ssize_t
243GNUNET_CRYPTO_read_signature_from_buffer (struct
244 GNUNET_CRYPTO_Signature *sig,
245 const void*buffer,
246 size_t len)
247{
248 if (len < sizeof (sig->type))
249 return -1;
250 GNUNET_memcpy (&(sig->type), buffer, sizeof (sig->type));
251 const ssize_t length = GNUNET_CRYPTO_signature_get_length (sig);
252 if (len < length)
253 return -1;
254 if (length < 0)
255 return -2;
256 GNUNET_memcpy (&(sig->ecdsa_signature), buffer + sizeof (sig->type), length
257 - sizeof (sig->type));
258 return length;
259}
260
261
262ssize_t
263GNUNET_CRYPTO_write_signature_to_buffer (const struct
264 GNUNET_CRYPTO_Signature *sig,
265 void*buffer,
266 size_t len)
267{
268 const ssize_t length = GNUNET_CRYPTO_signature_get_length (sig);
269 if (len < length)
270 return -1;
271 if (length < 0)
272 return -2;
273 GNUNET_memcpy (buffer, &(sig->type), sizeof (sig->type));
274 GNUNET_memcpy (buffer + sizeof (sig->type), &(sig->ecdsa_signature), length
275 - sizeof (sig->type));
276 return length;
277}
278
279
280enum GNUNET_GenericReturnValue
281GNUNET_CRYPTO_sign_raw_ (const struct
282 GNUNET_CRYPTO_PrivateKey *priv,
283 const struct
284 GNUNET_CRYPTO_EccSignaturePurpose *purpose,
285 unsigned char *sig)
286{
287 switch (ntohl (priv->type))
288 {
289 case GNUNET_PUBLIC_KEY_TYPE_ECDSA:
290 return GNUNET_CRYPTO_ecdsa_sign_ (&(priv->ecdsa_key), purpose,
291 (struct
292 GNUNET_CRYPTO_EcdsaSignature*) sig);
293 break;
294 case GNUNET_PUBLIC_KEY_TYPE_EDDSA:
295 return GNUNET_CRYPTO_eddsa_sign_ (&(priv->eddsa_key), purpose,
296 (struct
297 GNUNET_CRYPTO_EddsaSignature*) sig);
298 break;
299 default:
300 GNUNET_break (0);
301 }
302
303 return GNUNET_SYSERR;
304}
305
306
307enum GNUNET_GenericReturnValue
308GNUNET_CRYPTO_sign_ (const struct
309 GNUNET_CRYPTO_PrivateKey *priv,
310 const struct
311 GNUNET_CRYPTO_EccSignaturePurpose *purpose,
312 struct GNUNET_CRYPTO_Signature *sig)
313{
314 sig->type = priv->type;
315 switch (ntohl (priv->type))
316 {
317 case GNUNET_PUBLIC_KEY_TYPE_ECDSA:
318 return GNUNET_CRYPTO_ecdsa_sign_ (&(priv->ecdsa_key), purpose,
319 &(sig->ecdsa_signature));
320 break;
321 case GNUNET_PUBLIC_KEY_TYPE_EDDSA:
322 return GNUNET_CRYPTO_eddsa_sign_ (&(priv->eddsa_key), purpose,
323 &(sig->eddsa_signature));
324 break;
325 default:
326 GNUNET_break (0);
327 }
328
329 return GNUNET_SYSERR;
330}
331
332
333enum GNUNET_GenericReturnValue
334GNUNET_CRYPTO_signature_verify_ (uint32_t purpose,
335 const struct
336 GNUNET_CRYPTO_EccSignaturePurpose *validate,
337 const struct GNUNET_CRYPTO_Signature *sig,
338 const struct GNUNET_CRYPTO_PublicKey *pub)
339{
340 /* check type matching of 'sig' and 'pub' */
341 GNUNET_assert (ntohl (pub->type) == ntohl (sig->type));
342 switch (ntohl (pub->type))
343 {
344 case GNUNET_PUBLIC_KEY_TYPE_ECDSA:
345 return GNUNET_CRYPTO_ecdsa_verify_ (purpose, validate,
346 &(sig->ecdsa_signature),
347 &(pub->ecdsa_key));
348 break;
349 case GNUNET_PUBLIC_KEY_TYPE_EDDSA:
350 return GNUNET_CRYPTO_eddsa_verify_ (purpose, validate,
351 &(sig->eddsa_signature),
352 &(pub->eddsa_key));
353 break;
354 default:
355 GNUNET_break (0);
356 }
357
358 return GNUNET_SYSERR;
359}
360
361
362enum GNUNET_GenericReturnValue
363GNUNET_CRYPTO_signature_verify_raw_ (uint32_t purpose,
364 const struct
365 GNUNET_CRYPTO_EccSignaturePurpose *
366 validate,
367 const unsigned char *sig,
368 const struct
369 GNUNET_CRYPTO_PublicKey *pub)
370{
371 switch (ntohl (pub->type))
372 {
373 case GNUNET_PUBLIC_KEY_TYPE_ECDSA:
374 return GNUNET_CRYPTO_ecdsa_verify_ (purpose, validate,
375 (struct
376 GNUNET_CRYPTO_EcdsaSignature*) sig,
377 &(pub->ecdsa_key));
378 break;
379 case GNUNET_PUBLIC_KEY_TYPE_EDDSA:
380 return GNUNET_CRYPTO_eddsa_verify_ (purpose, validate,
381 (struct
382 GNUNET_CRYPTO_EddsaSignature*) sig,
383 &(pub->eddsa_key));
384 break;
385 default:
386 GNUNET_break (0);
387 }
388
389 return GNUNET_SYSERR;
390}
391
392
393ssize_t
394GNUNET_CRYPTO_encrypt_old (const void *block,
395 size_t size,
396 const struct GNUNET_CRYPTO_PublicKey *pub,
397 struct GNUNET_CRYPTO_EcdhePublicKey *ecc,
398 void *result)
399{
400 struct GNUNET_CRYPTO_EcdhePrivateKey pk;
401 GNUNET_CRYPTO_ecdhe_key_create (&pk);
402 struct GNUNET_HashCode hash;
403 switch (ntohl (pub->type))
404 {
405 case GNUNET_PUBLIC_KEY_TYPE_ECDSA:
406 if (GNUNET_SYSERR == GNUNET_CRYPTO_ecdh_ecdsa (&pk, &(pub->ecdsa_key),
407 &hash))
408 return -1;
409 break;
410 case GNUNET_PUBLIC_KEY_TYPE_EDDSA:
411 if (GNUNET_SYSERR == GNUNET_CRYPTO_ecdh_eddsa (&pk, &(pub->eddsa_key),
412 &hash))
413 return -1;
414 break;
415 default:
416 return -1;
417 }
418 GNUNET_CRYPTO_ecdhe_key_get_public (&pk, ecc);
419 GNUNET_CRYPTO_ecdhe_key_clear (&pk);
420 struct GNUNET_CRYPTO_SymmetricSessionKey key;
421 struct GNUNET_CRYPTO_SymmetricInitializationVector iv;
422 GNUNET_CRYPTO_hash_to_aes_key (&hash, &key, &iv);
423 GNUNET_CRYPTO_zero_keys (&hash, sizeof(hash));
424 const ssize_t encrypted = GNUNET_CRYPTO_symmetric_encrypt (block, size, &key,
425 &iv, result);
426 GNUNET_CRYPTO_zero_keys (&key, sizeof(key));
427 GNUNET_CRYPTO_zero_keys (&iv, sizeof(iv));
428 return encrypted;
429}
430
431
432enum GNUNET_GenericReturnValue
433GNUNET_CRYPTO_encrypt (const void *pt,
434 size_t pt_size,
435 const struct GNUNET_CRYPTO_PublicKey *pub,
436 void *ct_buf,
437 size_t ct_size)
438{
439 struct GNUNET_HashCode k;
440 struct GNUNET_CRYPTO_FoKemC kemc;
441 struct GNUNET_CRYPTO_FoKemC *kemc_buf = (struct GNUNET_CRYPTO_FoKemC*) ct_buf;
442 unsigned char *encrypted_data = (unsigned char*) &kemc_buf[1];
443 unsigned char nonce[crypto_secretbox_NONCEBYTES];
444 unsigned char key[crypto_secretbox_KEYBYTES];
445
446 if (ct_size < pt_size + GNUNET_CRYPTO_ENCRYPT_OVERHEAD_BYTES)
447 {
448 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
449 "Output buffer size for ciphertext too small: Got %llu, want >= %llu\n",
450 (unsigned long long) ct_size,
451 (unsigned long long) (pt_size
452 + GNUNET_CRYPTO_ENCRYPT_OVERHEAD_BYTES));
453 return GNUNET_SYSERR;
454 }
455 switch (ntohl (pub->type))
456 {
457 case GNUNET_PUBLIC_KEY_TYPE_ECDSA:
458 if (GNUNET_SYSERR == GNUNET_CRYPTO_ecdsa_fo_kem_encaps (&(pub->ecdsa_key),
459 &kemc,
460 &k))
461 return GNUNET_SYSERR;
462 break;
463 case GNUNET_PUBLIC_KEY_TYPE_EDDSA:
464 if (GNUNET_SYSERR == GNUNET_CRYPTO_eddsa_fo_kem_encaps (&pub->eddsa_key,
465 &kemc,
466 &k))
467 return GNUNET_SYSERR;
468 break;
469 default:
470 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Unsupported key type\n");
471 return GNUNET_SYSERR;
472 }
473 memcpy (key, &k, crypto_secretbox_KEYBYTES);
474 memcpy (nonce, ((char* ) &k) + crypto_secretbox_KEYBYTES,
475 crypto_secretbox_NONCEBYTES);
476 if (crypto_secretbox_easy (encrypted_data, pt, pt_size, nonce, key))
477 return GNUNET_SYSERR;
478 memcpy (kemc_buf, &kemc, sizeof (kemc));
479 return GNUNET_OK;
480}
481
482
483enum GNUNET_GenericReturnValue
484GNUNET_CRYPTO_decrypt (const void *ct_buf,
485 size_t ct_size,
486 const struct GNUNET_CRYPTO_PrivateKey *priv,
487 void *pt,
488 size_t pt_size)
489{
490 struct GNUNET_HashCode k;
491 struct GNUNET_CRYPTO_FoKemC *kemc = (struct GNUNET_CRYPTO_FoKemC*) ct_buf;
492 unsigned char *encrypted_data = (unsigned char*) &kemc[1];
493 unsigned char nonce[crypto_secretbox_NONCEBYTES];
494 unsigned char key[crypto_secretbox_KEYBYTES];
495 size_t expected_pt_len = ct_size - GNUNET_CRYPTO_ENCRYPT_OVERHEAD_BYTES;
496
497 if (pt_size < expected_pt_len)
498 {
499 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
500 "Output buffer size for plaintext too small: Got %llu, want >= %llu\n",
501 (unsigned long long) pt_size,
502 (unsigned long long) expected_pt_len);
503 return GNUNET_SYSERR;
504 }
505 switch (ntohl (priv->type))
506 {
507 case GNUNET_PUBLIC_KEY_TYPE_ECDSA:
508 if (GNUNET_SYSERR == GNUNET_CRYPTO_ecdsa_fo_kem_decaps (&(priv->ecdsa_key),
509 kemc,
510 &k))
511 return GNUNET_SYSERR;
512 break;
513 case GNUNET_PUBLIC_KEY_TYPE_EDDSA:
514 if (GNUNET_SYSERR == GNUNET_CRYPTO_eddsa_fo_kem_decaps (&(priv->eddsa_key),
515 kemc,
516 &k))
517 return GNUNET_SYSERR;
518 break;
519 default:
520 return GNUNET_SYSERR;
521 }
522 memcpy (key, &k, crypto_secretbox_KEYBYTES);
523 memcpy (nonce, ((char* ) &k) + crypto_secretbox_KEYBYTES,
524 crypto_secretbox_NONCEBYTES);
525 if (crypto_secretbox_open_easy (pt, encrypted_data, ct_size - sizeof (*kemc),
526 nonce, key))
527 return GNUNET_SYSERR;
528 return GNUNET_OK;
529}
530
531
532ssize_t
533GNUNET_CRYPTO_decrypt_old (const void *block,
534 size_t size,
535 const struct GNUNET_CRYPTO_PrivateKey *priv,
536 const struct GNUNET_CRYPTO_EcdhePublicKey *ecc,
537 void *result)
538{
539 struct GNUNET_HashCode hash;
540 switch (ntohl (priv->type))
541 {
542 case GNUNET_PUBLIC_KEY_TYPE_ECDSA:
543 if (GNUNET_SYSERR == GNUNET_CRYPTO_ecdsa_ecdh (&(priv->ecdsa_key), ecc,
544 &hash))
545 return -1;
546 break;
547 case GNUNET_PUBLIC_KEY_TYPE_EDDSA:
548 if (GNUNET_SYSERR == GNUNET_CRYPTO_eddsa_ecdh (&(priv->eddsa_key), ecc,
549 &hash))
550 return -1;
551 break;
552 default:
553 return -1;
554 }
555 struct GNUNET_CRYPTO_SymmetricSessionKey key;
556 struct GNUNET_CRYPTO_SymmetricInitializationVector iv;
557 GNUNET_CRYPTO_hash_to_aes_key (&hash, &key, &iv);
558 GNUNET_CRYPTO_zero_keys (&hash, sizeof(hash));
559 const ssize_t decrypted = GNUNET_CRYPTO_symmetric_decrypt (block, size, &key,
560 &iv, result);
561 GNUNET_CRYPTO_zero_keys (&key, sizeof(key));
562 GNUNET_CRYPTO_zero_keys (&iv, sizeof(iv));
563 return decrypted;
564}
565
566
567char *
568GNUNET_CRYPTO_public_key_to_string (const struct
569 GNUNET_CRYPTO_PublicKey *key)
570{
571 size_t size = GNUNET_CRYPTO_public_key_get_length (key);
572 return GNUNET_STRINGS_data_to_string_alloc (key,
573 size);
574}
575
576
577char *
578GNUNET_CRYPTO_private_key_to_string (const struct
579 GNUNET_CRYPTO_PrivateKey *key)
580{
581 size_t size = GNUNET_CRYPTO_private_key_get_length (key);
582 return GNUNET_STRINGS_data_to_string_alloc (key,
583 size);
584}
585
586
587enum GNUNET_GenericReturnValue
588GNUNET_CRYPTO_public_key_from_string (const char *str,
589 struct GNUNET_CRYPTO_PublicKey *key)
590{
591 enum GNUNET_GenericReturnValue ret;
592 ret = GNUNET_STRINGS_string_to_data (str,
593 strlen (str),
594 key,
595 sizeof (*key));
596 if (GNUNET_OK != ret)
597 return GNUNET_SYSERR;
598 return check_key_type (ntohl (key->type));
599
600}
601
602
603enum GNUNET_GenericReturnValue
604GNUNET_CRYPTO_private_key_from_string (const char *str,
605 struct GNUNET_CRYPTO_PrivateKey *key)
606{
607 enum GNUNET_GenericReturnValue ret;
608 ret = GNUNET_STRINGS_string_to_data (str,
609 strlen (str),
610 key,
611 sizeof (*key));
612 if (GNUNET_OK != ret)
613 return GNUNET_SYSERR;
614 return check_key_type (ntohl (key->type));
615}
616
617
618enum GNUNET_GenericReturnValue
619GNUNET_CRYPTO_key_get_public (const struct
620 GNUNET_CRYPTO_PrivateKey *privkey,
621 struct GNUNET_CRYPTO_PublicKey *key)
622{
623 key->type = privkey->type;
624 switch (ntohl (privkey->type))
625 {
626 case GNUNET_PUBLIC_KEY_TYPE_ECDSA:
627 GNUNET_CRYPTO_ecdsa_key_get_public (&privkey->ecdsa_key,
628 &key->ecdsa_key);
629 break;
630 case GNUNET_PUBLIC_KEY_TYPE_EDDSA:
631 GNUNET_CRYPTO_eddsa_key_get_public (&privkey->eddsa_key,
632 &key->eddsa_key);
633 break;
634 default:
635 GNUNET_break (0);
636 return GNUNET_SYSERR;
637 }
638 return GNUNET_OK;
639}
diff --git a/src/lib/util/crypto_pow.c b/src/lib/util/crypto_pow.c
new file mode 100644
index 000000000..bfaaf555d
--- /dev/null
+++ b/src/lib/util/crypto_pow.c
@@ -0,0 +1,60 @@
1/*
2 This file is part of GNUnet.
3 Copyright (C) 2012, 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/crypto_pow.c
22 * @brief proof-of-work hashing
23 * @author Christian Grothoff
24 * @author Bart Polot
25 */
26
27#include "platform.h"
28#include "gnunet_util_lib.h"
29#include <sodium.h>
30
31/**
32 * Calculate the 'proof-of-work' hash (an expensive hash).
33 * We're using a non-standard formula to avoid issues with
34 * ASICs appearing (see #3795).
35 *
36 * @param salt salt for the hash. Must be crypto_pwhash_argon2id_SALTBYTES long.
37 * @param buf data to hash
38 * @param buf_len number of bytes in @a buf
39 * @param result where to write the resulting hash
40 */
41void
42GNUNET_CRYPTO_pow_hash (const struct GNUNET_CRYPTO_PowSalt *salt,
43 const void *buf,
44 size_t buf_len,
45 struct GNUNET_HashCode *result)
46{
47 /* Threads hardcoded at 1 in libsodium */
48 GNUNET_break (0 ==
49 crypto_pwhash_argon2id ((unsigned char *) result,
50 sizeof (struct GNUNET_HashCode),
51 buf,
52 buf_len,
53 (unsigned char*) salt,
54 3, /* iterations */
55 1024 * 1024, /* memory (1 MiB) */
56 crypto_pwhash_argon2id_ALG_ARGON2ID13));
57}
58
59
60/* end of crypto_pow.c */
diff --git a/src/lib/util/crypto_random.c b/src/lib/util/crypto_random.c
new file mode 100644
index 000000000..72474d04b
--- /dev/null
+++ b/src/lib/util/crypto_random.c
@@ -0,0 +1,412 @@
1/*
2 This file is part of GNUnet. Copyright (C) 2001-2014 Christian Grothoff
3 (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 */
21
22/**
23 * @file util/crypto_random.c
24 * @brief functions to gather random numbers
25 * @author Christian Grothoff
26 */
27
28#include "platform.h"
29#include "gnunet_util_lib.h"
30#include <gcrypt.h>
31
32#define LOG(kind, ...) GNUNET_log_from (kind, "util-crypto-random", __VA_ARGS__)
33
34#define LOG_STRERROR(kind, syscall) \
35 GNUNET_log_from_strerror (kind, "util-crypto-random", syscall)
36
37
38/* TODO: ndurner, move this to plibc? */
39/* The code is derived from glibc, obviously */
40#if ! HAVE_RANDOM || ! HAVE_SRANDOM
41#ifdef RANDOM
42#undef RANDOM
43#endif
44#ifdef SRANDOM
45#undef SRANDOM
46#endif
47#define RANDOM() glibc_weak_rand32 ()
48#define SRANDOM(s) glibc_weak_srand32 (s)
49#if defined(RAND_MAX)
50#undef RAND_MAX
51#endif
52#define RAND_MAX 0x7fffffff /* Hopefully this is correct */
53
54
55static int32_t glibc_weak_rand32_state = 1;
56
57
58void
59glibc_weak_srand32 (int32_t s)
60{
61 glibc_weak_rand32_state = s;
62}
63
64
65int32_t
66glibc_weak_rand32 ()
67{
68 int32_t val = glibc_weak_rand32_state;
69
70 val = ((glibc_weak_rand32_state * 1103515245) + 12345) & 0x7fffffff;
71 glibc_weak_rand32_state = val;
72 return val;
73}
74
75
76#endif
77
78/**
79 * Create a cryptographically weak pseudo-random number in the interval of 0 to 1.
80 *
81 * @return number between 0 and 1.
82 */
83static double
84get_weak_random (void)
85{
86 return((double) random () / RAND_MAX);
87}
88
89
90void
91GNUNET_CRYPTO_seed_weak_random (int32_t seed)
92{
93#ifdef OPENBSD
94 srandom_deterministic (seed);
95#else
96 srandom (seed);
97#endif
98}
99
100
101/**
102 * @ingroup crypto
103 * Zero out @a buffer, securely against compiler optimizations.
104 * Used to delete key material.
105 *
106 * @param buffer the buffer to zap
107 * @param length buffer length
108 */
109void
110GNUNET_CRYPTO_zero_keys (void *buffer, size_t length)
111{
112#if HAVE_MEMSET_S
113 memset_s (buffer, length, 0, length);
114#elif HAVE_EXPLICIT_BZERO
115 explicit_bzero (buffer, length);
116#else
117 volatile unsigned char *p = buffer;
118 while (length--)
119 *p++ = 0;
120#endif
121}
122
123
124/**
125 * @ingroup crypto
126 * Fill block with a random values.
127 *
128 * @param mode desired quality of the random number
129 * @param buffer the buffer to fill
130 * @param length buffer length
131 */
132void
133GNUNET_CRYPTO_random_block (enum GNUNET_CRYPTO_Quality mode,
134 void *buffer,
135 size_t length)
136{
137#ifdef gcry_fast_random_poll
138 static unsigned int invokeCount;
139#endif
140 switch (mode)
141 {
142 case GNUNET_CRYPTO_QUALITY_STRONG:
143 /* see http://lists.gnupg.org/pipermail/gcrypt-devel/2004-May/000613.html */
144#ifdef gcry_fast_random_poll
145 if ((invokeCount++ % 256) == 0)
146 gcry_fast_random_poll ();
147#endif
148 gcry_randomize (buffer, length, GCRY_STRONG_RANDOM);
149 return;
150
151 case GNUNET_CRYPTO_QUALITY_NONCE:
152 gcry_create_nonce (buffer, length);
153 return;
154
155 case GNUNET_CRYPTO_QUALITY_WEAK:
156 /* see http://lists.gnupg.org/pipermail/gcrypt-devel/2004-May/000613.html */
157#ifdef gcry_fast_random_poll
158 if ((invokeCount++ % 256) == 0)
159 gcry_fast_random_poll ();
160#endif
161 gcry_randomize (buffer, length, GCRY_WEAK_RANDOM);
162 return;
163
164 default:
165 GNUNET_assert (0);
166 }
167}
168
169
170/**
171 * Produce a random unsigned 32-bit number modulo @a i.
172 *
173 * @param mode desired quality of the random number
174 * @param i the upper limit (exclusive) for the random number
175 * @return a random value in the interval [0,i[.
176 */
177uint32_t
178GNUNET_CRYPTO_random_u32 (enum GNUNET_CRYPTO_Quality mode,
179 uint32_t i)
180{
181#ifdef gcry_fast_random_poll
182 static unsigned int invokeCount;
183#endif
184 uint32_t ret;
185 uint32_t ul;
186
187 GNUNET_assert (i > 0);
188
189 switch (mode)
190 {
191 case GNUNET_CRYPTO_QUALITY_STRONG:
192 /* see http://lists.gnupg.org/pipermail/gcrypt-devel/2004-May/000613.html */
193#ifdef gcry_fast_random_poll
194 if ((invokeCount++ % 256) == 0)
195 gcry_fast_random_poll ();
196#endif
197 ul = UINT32_MAX - (UINT32_MAX % i);
198 do
199 {
200 gcry_randomize ((unsigned char *) &ret,
201 sizeof(uint32_t),
202 GCRY_STRONG_RANDOM);
203 }
204 while (ret >= ul);
205 return ret % i;
206
207 case GNUNET_CRYPTO_QUALITY_NONCE:
208 ul = UINT32_MAX - (UINT32_MAX % i);
209 do
210 {
211 gcry_create_nonce (&ret, sizeof(ret));
212 }
213 while (ret >= ul);
214 return ret % i;
215
216 case GNUNET_CRYPTO_QUALITY_WEAK:
217 ret = i * get_weak_random ();
218 if (ret >= i)
219 ret = i - 1;
220 return ret;
221
222 default:
223 GNUNET_assert (0);
224 }
225 return 0;
226}
227
228
229/**
230 * Get an array with a random permutation of the
231 * numbers 0...n-1.
232 * @param mode #GNUNET_RANDOM_QUALITY_STRONG if the strong (but expensive)
233 * PRNG should be used, #GNUNET_RANDOM_QUALITY_WEAK otherwise
234 * @param n the size of the array
235 * @return the permutation array (allocated from heap)
236 */
237unsigned int *
238GNUNET_CRYPTO_random_permute (enum GNUNET_CRYPTO_Quality mode,
239 unsigned int n)
240{
241 unsigned int *ret;
242 unsigned int i;
243 unsigned int tmp;
244 uint32_t x;
245
246 GNUNET_assert (n > 0);
247 ret = GNUNET_malloc (n * sizeof(unsigned int));
248 for (i = 0; i < n; i++)
249 ret[i] = i;
250 for (i = n - 1; i > 0; i--)
251 {
252 x = GNUNET_CRYPTO_random_u32 (mode, i + 1);
253 tmp = ret[x];
254 ret[x] = ret[i];
255 ret[i] = tmp;
256 }
257 return ret;
258}
259
260
261uint64_t
262GNUNET_CRYPTO_random_u64 (enum GNUNET_CRYPTO_Quality mode,
263 uint64_t max)
264{
265 uint64_t ret;
266 uint64_t ul;
267
268 GNUNET_assert (max > 0);
269 switch (mode)
270 {
271 case GNUNET_CRYPTO_QUALITY_STRONG:
272 ul = UINT64_MAX - (UINT64_MAX % max);
273 do
274 {
275 gcry_randomize ((unsigned char *) &ret,
276 sizeof(uint64_t),
277 GCRY_STRONG_RANDOM);
278 }
279 while (ret >= ul);
280 return ret % max;
281
282 case GNUNET_CRYPTO_QUALITY_NONCE:
283 ul = UINT64_MAX - (UINT64_MAX % max);
284 do
285 {
286 gcry_create_nonce (&ret, sizeof(ret));
287 }
288 while (ret >= ul);
289
290 return ret % max;
291
292 case GNUNET_CRYPTO_QUALITY_WEAK:
293 ret = max * get_weak_random ();
294 if (ret >= max)
295 ret = max - 1;
296 return ret;
297
298 default:
299 GNUNET_assert (0);
300 }
301 return 0;
302}
303
304
305/**
306 * @ingroup crypto
307 * Fill UUID with a timeflake pseudo-random value. Note that
308 * timeflakes use only 80 bits of randomness and 48 bits
309 * to encode a timestamp in milliseconds. So what we return
310 * here is not a completely random number.
311 *
312 * @param mode desired quality of the random number
313 * @param uuid the value to fill
314 */
315void
316GNUNET_CRYPTO_random_timeflake (enum GNUNET_CRYPTO_Quality mode,
317 struct GNUNET_Uuid *uuid)
318{
319 struct GNUNET_TIME_Absolute now;
320 uint64_t ms;
321 uint64_t be;
322 char *base;
323
324 GNUNET_CRYPTO_random_block (mode,
325 uuid,
326 sizeof (struct GNUNET_Uuid));
327 now = GNUNET_TIME_absolute_get ();
328 ms = now.abs_value_us / GNUNET_TIME_UNIT_MILLISECONDS.rel_value_us;
329 be = GNUNET_htonll (ms);
330 base = (char *) &be;
331 memcpy (uuid,
332 base + 2,
333 sizeof (be) - 2);
334}
335
336
337/**
338 * Allocation wrapper for libgcrypt, used to avoid bad locking
339 * strategy of libgcrypt implementation.
340 */
341static void *
342w_malloc (size_t n)
343{
344 return calloc (n, 1);
345}
346
347
348/**
349 * Allocation wrapper for libgcrypt, used to avoid bad locking
350 * strategy of libgcrypt implementation.
351 */
352static int
353w_check (const void *p)
354{
355 (void) p;
356 return 0; /* not secure memory */
357}
358
359
360/**
361 * Initialize libgcrypt.
362 */
363void __attribute__ ((constructor))
364GNUNET_CRYPTO_random_init ()
365{
366 gcry_error_t rc;
367
368 if (! gcry_check_version (NEED_LIBGCRYPT_VERSION))
369 {
370 fprintf (
371 stderr,
372 _ ("libgcrypt has not the expected version (version %s is required).\n"),
373 NEED_LIBGCRYPT_VERSION);
374 GNUNET_assert (0);
375 }
376 /* set custom allocators */
377 gcry_set_allocation_handler (&w_malloc, &w_malloc, &w_check, &realloc, &free);
378 /* Disable use of secure memory */
379 if ((rc = gcry_control (GCRYCTL_DISABLE_SECMEM, 0)))
380 fprintf (stderr,
381 "Failed to set libgcrypt option %s: %s\n",
382 "DISABLE_SECMEM",
383 gcry_strerror (rc));
384 /* Otherwise gnunet-ecc takes forever to complete, besides
385 we are fine with "just" using GCRY_STRONG_RANDOM */
386 if ((rc = gcry_control (GCRYCTL_ENABLE_QUICK_RANDOM, 0)))
387 fprintf (stderr,
388 "Failed to set libgcrypt option %s: %s\n",
389 "ENABLE_QUICK_RANDOM",
390 gcry_strerror (rc));
391 gcry_control (GCRYCTL_INITIALIZATION_FINISHED, 0);
392 gcry_fast_random_poll ();
393 GNUNET_CRYPTO_seed_weak_random (
394 time (NULL)
395 ^ GNUNET_CRYPTO_random_u32 (GNUNET_CRYPTO_QUALITY_NONCE, UINT32_MAX));
396}
397
398
399/**
400 * Nicely shut down libgcrypt.
401 */
402void __attribute__ ((destructor))
403GNUNET_CRYPTO_random_fini ()
404{
405 gcry_set_progress_handler (NULL, NULL);
406#ifdef GCRYCTL_CLOSE_RANDOM_DEVICE
407 (void) gcry_control (GCRYCTL_CLOSE_RANDOM_DEVICE, 0);
408#endif
409}
410
411
412/* end of crypto_random.c */
diff --git a/src/lib/util/crypto_rsa.c b/src/lib/util/crypto_rsa.c
new file mode 100644
index 000000000..aeae3de8f
--- /dev/null
+++ b/src/lib/util/crypto_rsa.c
@@ -0,0 +1,1290 @@
1/*
2 This file is part of GNUnet
3 Copyright (C) 2014,2016,2019,2023 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/crypto_rsa.c
23 * @brief Chaum-style Blind signatures based on RSA
24 * @author Sree Harsha Totakura <sreeharsha@totakura.in>
25 * @author Christian Grothoff
26 * @author Jeffrey Burdges <burdges@gnunet.org>
27 */
28
29#include "platform.h"
30#include <gcrypt.h>
31#include "gnunet_util_lib.h"
32#include "benchmark.h"
33
34#define LOG(kind, ...) GNUNET_log_from (kind, "util-crypto-rsa", __VA_ARGS__)
35
36
37/**
38 * The private information of an RSA key pair.
39 */
40struct GNUNET_CRYPTO_RsaPrivateKey
41{
42 /**
43 * Libgcrypt S-expression for the RSA private key.
44 */
45 gcry_sexp_t sexp;
46};
47
48
49/**
50 * The public information of an RSA key pair.
51 */
52struct GNUNET_CRYPTO_RsaPublicKey
53{
54 /**
55 * Libgcrypt S-expression for the RSA public key.
56 */
57 gcry_sexp_t sexp;
58};
59
60
61/**
62 * @brief an RSA signature
63 */
64struct GNUNET_CRYPTO_RsaSignature
65{
66 /**
67 * Libgcrypt S-expression for the RSA signature.
68 */
69 gcry_sexp_t sexp;
70};
71
72
73/**
74 * @brief RSA blinding key
75 */
76struct RsaBlindingKey
77{
78 /**
79 * Random value used for blinding.
80 */
81 gcry_mpi_t r;
82};
83
84
85/**
86 * Extract values from an S-expression.
87 *
88 * @param array where to store the result(s)
89 * @param sexp S-expression to parse
90 * @param topname top-level name in the S-expression that is of interest
91 * @param elems names of the elements to extract
92 * @return 0 on success
93 */
94static int
95key_from_sexp (gcry_mpi_t *array,
96 gcry_sexp_t sexp,
97 const char *topname,
98 const char *elems)
99{
100 gcry_sexp_t list;
101 gcry_sexp_t l2;
102 const char *s;
103 unsigned int idx;
104
105 if (! (list = gcry_sexp_find_token (sexp, topname, 0)))
106 return 1;
107 l2 = gcry_sexp_cadr (list);
108 gcry_sexp_release (list);
109 list = l2;
110 if (! list)
111 return 2;
112 idx = 0;
113 for (s = elems; *s; s++, idx++)
114 {
115 if (! (l2 = gcry_sexp_find_token (list, s, 1)))
116 {
117 for (unsigned int i = 0; i < idx; i++)
118 {
119 gcry_free (array[i]);
120 array[i] = NULL;
121 }
122 gcry_sexp_release (list);
123 return 3; /* required parameter not found */
124 }
125 array[idx] = gcry_sexp_nth_mpi (l2, 1, GCRYMPI_FMT_USG);
126 gcry_sexp_release (l2);
127 if (! array[idx])
128 {
129 for (unsigned int i = 0; i < idx; i++)
130 {
131 gcry_free (array[i]);
132 array[i] = NULL;
133 }
134 gcry_sexp_release (list);
135 return 4; /* required parameter is invalid */
136 }
137 }
138 gcry_sexp_release (list);
139 return 0;
140}
141
142
143struct GNUNET_CRYPTO_RsaPrivateKey *
144GNUNET_CRYPTO_rsa_private_key_create (unsigned int len)
145{
146 struct GNUNET_CRYPTO_RsaPrivateKey *ret;
147 gcry_sexp_t s_key;
148 gcry_sexp_t s_keyparam;
149
150 BENCHMARK_START (rsa_private_key_create);
151
152 GNUNET_assert (0 ==
153 gcry_sexp_build (&s_keyparam,
154 NULL,
155 "(genkey(rsa(nbits %d)))",
156 len));
157 GNUNET_assert (0 ==
158 gcry_pk_genkey (&s_key,
159 s_keyparam));
160 gcry_sexp_release (s_keyparam);
161#if EXTRA_CHECKS
162 GNUNET_assert (0 ==
163 gcry_pk_testkey (s_key));
164#endif
165 ret = GNUNET_new (struct GNUNET_CRYPTO_RsaPrivateKey);
166 ret->sexp = s_key;
167 BENCHMARK_END (rsa_private_key_create);
168 return ret;
169}
170
171
172void
173GNUNET_CRYPTO_rsa_private_key_free (struct GNUNET_CRYPTO_RsaPrivateKey *key)
174{
175 gcry_sexp_release (key->sexp);
176 GNUNET_free (key);
177}
178
179
180size_t
181GNUNET_CRYPTO_rsa_private_key_encode (
182 const struct GNUNET_CRYPTO_RsaPrivateKey *key,
183 void **buffer)
184{
185 size_t n;
186 char *b;
187
188 n = gcry_sexp_sprint (key->sexp,
189 GCRYSEXP_FMT_DEFAULT,
190 NULL,
191 0);
192 b = GNUNET_malloc (n);
193 GNUNET_assert ((n - 1) == /* since the last byte is \0 */
194 gcry_sexp_sprint (key->sexp,
195 GCRYSEXP_FMT_DEFAULT,
196 b,
197 n));
198 *buffer = b;
199 return n;
200}
201
202
203struct GNUNET_CRYPTO_RsaPrivateKey *
204GNUNET_CRYPTO_rsa_private_key_decode (const void *buf,
205 size_t buf_size)
206{
207 struct GNUNET_CRYPTO_RsaPrivateKey *key;
208
209 key = GNUNET_new (struct GNUNET_CRYPTO_RsaPrivateKey);
210 if (0 !=
211 gcry_sexp_new (&key->sexp,
212 buf,
213 buf_size,
214 0))
215 {
216 LOG (GNUNET_ERROR_TYPE_WARNING,
217 "Decoded private key is not valid\n");
218 GNUNET_free (key);
219 return NULL;
220 }
221 if (0 != gcry_pk_testkey (key->sexp))
222 {
223 LOG (GNUNET_ERROR_TYPE_WARNING,
224 "Decoded private key is not valid\n");
225 GNUNET_CRYPTO_rsa_private_key_free (key);
226 return NULL;
227 }
228 return key;
229}
230
231
232struct GNUNET_CRYPTO_RsaPublicKey *
233GNUNET_CRYPTO_rsa_private_key_get_public (
234 const struct GNUNET_CRYPTO_RsaPrivateKey *priv)
235{
236 struct GNUNET_CRYPTO_RsaPublicKey *pub;
237 gcry_mpi_t ne[2];
238 int rc;
239 gcry_sexp_t result;
240
241 BENCHMARK_START (rsa_private_key_get_public);
242
243 rc = key_from_sexp (ne, priv->sexp, "public-key", "ne");
244 if (0 != rc)
245 rc = key_from_sexp (ne, priv->sexp, "private-key", "ne");
246 if (0 != rc)
247 rc = key_from_sexp (ne, priv->sexp, "rsa", "ne");
248 if (0 != rc)
249 {
250 GNUNET_break_op (0);
251 return NULL;
252 }
253 rc = gcry_sexp_build (&result,
254 NULL,
255 "(public-key(rsa(n %m)(e %m)))",
256 ne[0],
257 ne[1]);
258 gcry_mpi_release (ne[0]);
259 gcry_mpi_release (ne[1]);
260 pub = GNUNET_new (struct GNUNET_CRYPTO_RsaPublicKey);
261 pub->sexp = result;
262 BENCHMARK_END (rsa_private_key_get_public);
263 return pub;
264}
265
266
267void
268GNUNET_CRYPTO_rsa_public_key_free (struct GNUNET_CRYPTO_RsaPublicKey *key)
269{
270 gcry_sexp_release (key->sexp);
271 GNUNET_free (key);
272}
273
274
275GNUNET_NETWORK_STRUCT_BEGIN
276
277/**
278 * Format of the header of a serialized RSA public key.
279 */
280struct GNUNET_CRYPTO_RsaPublicKeyHeaderP
281{
282 /**
283 * length of modulus 'n' in bytes, in NBO
284 */
285 uint16_t modulus_length GNUNET_PACKED;
286
287 /**
288 * length of exponent in bytes, in NBO
289 */
290 uint16_t public_exponent_length GNUNET_PACKED;
291
292 /* followed by variable-size modulus and
293 public exponent follows as big-endian encoded
294 integers */
295};
296
297GNUNET_NETWORK_STRUCT_END
298
299
300bool
301GNUNET_CRYPTO_rsa_public_key_check (
302 const struct GNUNET_CRYPTO_RsaPublicKey *key)
303{
304 gcry_mpi_t ne[2];
305 int ret;
306
307 ret = key_from_sexp (ne,
308 key->sexp,
309 "public-key",
310 "ne");
311 if (0 != ret)
312 ret = key_from_sexp (ne,
313 key->sexp,
314 "rsa",
315 "ne");
316 if (0 != ret)
317 return false;
318 gcry_mpi_release (ne[0]);
319 gcry_mpi_release (ne[1]);
320 return true;
321}
322
323
324size_t
325GNUNET_CRYPTO_rsa_public_key_encode (
326 const struct GNUNET_CRYPTO_RsaPublicKey *key,
327 void **buffer)
328{
329 gcry_mpi_t ne[2];
330 size_t n_size;
331 size_t e_size;
332 size_t rsize;
333 size_t buf_size;
334 char *buf;
335 struct GNUNET_CRYPTO_RsaPublicKeyHeaderP hdr;
336 int ret;
337
338 ret = key_from_sexp (ne,
339 key->sexp,
340 "public-key",
341 "ne");
342 if (0 != ret)
343 ret = key_from_sexp (ne,
344 key->sexp,
345 "rsa",
346 "ne");
347 if (0 != ret)
348 {
349 GNUNET_break (0);
350 *buffer = NULL;
351 return 0;
352 }
353 gcry_mpi_print (GCRYMPI_FMT_USG,
354 NULL,
355 0,
356 &n_size,
357 ne[0]);
358 gcry_mpi_print (GCRYMPI_FMT_USG,
359 NULL,
360 0,
361 &e_size,
362 ne[1]);
363 if ( (e_size > UINT16_MAX) ||
364 (n_size > UINT16_MAX) )
365 {
366 GNUNET_break (0);
367 if (NULL != buffer)
368 *buffer = NULL;
369 gcry_mpi_release (ne[0]);
370 gcry_mpi_release (ne[1]);
371 return 0;
372 }
373 buf_size = n_size + e_size + sizeof (hdr);
374 if (NULL == buffer)
375 {
376 gcry_mpi_release (ne[0]);
377 gcry_mpi_release (ne[1]);
378 return buf_size;
379 }
380 buf = GNUNET_malloc (buf_size);
381 hdr.modulus_length = htons ((uint16_t) n_size);
382 hdr.public_exponent_length = htons ((uint16_t) e_size);
383 memcpy (buf,
384 &hdr,
385 sizeof (hdr));
386 GNUNET_assert (0 ==
387 gcry_mpi_print (GCRYMPI_FMT_USG,
388 (unsigned char *) &buf[sizeof (hdr)],
389 n_size,
390 &rsize,
391 ne[0]));
392
393 GNUNET_assert (0 ==
394 gcry_mpi_print (GCRYMPI_FMT_USG,
395 (unsigned char *) &buf[sizeof (hdr) + n_size],
396 e_size,
397 &rsize,
398 ne[1]));
399 *buffer = buf;
400 gcry_mpi_release (ne[0]);
401 gcry_mpi_release (ne[1]);
402 return buf_size;
403}
404
405
406void
407GNUNET_CRYPTO_rsa_public_key_hash (const struct GNUNET_CRYPTO_RsaPublicKey *key,
408 struct GNUNET_HashCode *hc)
409{
410 void *buf;
411 size_t buf_size;
412
413 buf_size = GNUNET_CRYPTO_rsa_public_key_encode (key,
414 &buf);
415 GNUNET_CRYPTO_hash (buf,
416 buf_size,
417 hc);
418 GNUNET_free (buf);
419}
420
421
422struct GNUNET_CRYPTO_RsaPublicKey *
423GNUNET_CRYPTO_rsa_public_key_decode (const char *buf,
424 size_t len)
425{
426 struct GNUNET_CRYPTO_RsaPublicKey *key;
427 struct GNUNET_CRYPTO_RsaPublicKeyHeaderP hdr;
428 size_t e_size;
429 size_t n_size;
430 gcry_mpi_t n;
431 gcry_mpi_t e;
432 gcry_sexp_t data;
433
434 if (len < sizeof (hdr))
435 {
436 GNUNET_break_op (0);
437 return NULL;
438 }
439 memcpy (&hdr, buf, sizeof (hdr));
440 n_size = ntohs (hdr.modulus_length);
441 e_size = ntohs (hdr.public_exponent_length);
442 if (len != sizeof (hdr) + e_size + n_size)
443 {
444 GNUNET_break_op (0);
445 return NULL;
446 }
447 if (0 !=
448 gcry_mpi_scan (&n,
449 GCRYMPI_FMT_USG,
450 &buf[sizeof (hdr)],
451 n_size,
452 NULL))
453 {
454 GNUNET_break_op (0);
455 return NULL;
456 }
457 if (0 !=
458 gcry_mpi_scan (&e,
459 GCRYMPI_FMT_USG,
460 &buf[sizeof (hdr) + n_size],
461 e_size,
462 NULL))
463 {
464 GNUNET_break_op (0);
465 gcry_mpi_release (n);
466 return NULL;
467 }
468
469 if (0 !=
470 gcry_sexp_build (&data,
471 NULL,
472 "(public-key(rsa(n %m)(e %m)))",
473 n,
474 e))
475 {
476 GNUNET_break (0);
477 gcry_mpi_release (n);
478 gcry_mpi_release (e);
479 return NULL;
480 }
481 gcry_mpi_release (n);
482 gcry_mpi_release (e);
483 key = GNUNET_new (struct GNUNET_CRYPTO_RsaPublicKey);
484 key->sexp = data;
485 return key;
486}
487
488
489/**
490 * Test for malicious RSA key.
491 *
492 * Assuming n is an RSA modulous and r is generated using a call to
493 * GNUNET_CRYPTO_kdf_mod_mpi, if gcd(r,n) != 1 then n must be a
494 * malicious RSA key designed to deanomize the user.
495 *
496 * @param r KDF result
497 * @param n RSA modulus
498 * @return True if gcd(r,n) = 1, False means RSA key is malicious
499 */
500static int
501rsa_gcd_validate (gcry_mpi_t r,
502 gcry_mpi_t n)
503{
504 gcry_mpi_t g;
505 int t;
506
507 g = gcry_mpi_new (0);
508 t = gcry_mpi_gcd (g, r, n);
509 gcry_mpi_release (g);
510 return t;
511}
512
513
514/**
515 * Create a blinding key
516 *
517 * @param len length of the key in bits (e.g. 2048)
518 * @param bks pre-secret to use to derive the blinding key
519 * @return the newly created blinding key, NULL if RSA key is malicious
520 */
521static struct RsaBlindingKey *
522rsa_blinding_key_derive (const struct GNUNET_CRYPTO_RsaPublicKey *pkey,
523 const struct GNUNET_CRYPTO_RsaBlindingKeySecret *bks)
524{
525 const char *xts = "Blinding KDF extractor HMAC key"; /* Trusts bks' randomness more */
526 struct RsaBlindingKey *blind;
527 gcry_mpi_t n;
528
529 blind = GNUNET_new (struct RsaBlindingKey);
530
531 /* Extract the composite n from the RSA public key */
532 GNUNET_assert (0 ==
533 key_from_sexp (&n,
534 pkey->sexp,
535 "rsa",
536 "n"));
537 /* Assert that it at least looks like an RSA key */
538 GNUNET_assert (0 ==
539 gcry_mpi_get_flag (n,
540 GCRYMPI_FLAG_OPAQUE));
541 GNUNET_CRYPTO_kdf_mod_mpi (&blind->r,
542 n,
543 xts, strlen (xts),
544 bks, sizeof(*bks),
545 "Blinding KDF");
546 if (0 == rsa_gcd_validate (blind->r,
547 n))
548 {
549 gcry_mpi_release (blind->r);
550 GNUNET_free (blind);
551 blind = NULL;
552 }
553 gcry_mpi_release (n);
554 return blind;
555}
556
557
558/*
559 We originally added GNUNET_CRYPTO_kdf_mod_mpi for the benefit of the
560 previous routine.
561
562 There was previously a call to GNUNET_CRYPTO_kdf in
563 bkey = rsa_blinding_key_derive (len, bks);
564 that gives exactly len bits where
565 len = GNUNET_CRYPTO_rsa_public_key_len (pkey);
566
567 Now r = 2^(len-1)/pkey.n is the probability that a set high bit being
568 okay, meaning bkey < pkey.n. It follows that (1-r)/2 of the time bkey >
569 pkey.n making the effective bkey be
570 bkey mod pkey.n = bkey - pkey.n
571 so the effective bkey has its high bit set with probability r/2.
572
573 We expect r to be close to 1/2 if the exchange is honest, but the
574 exchange can choose r otherwise.
575
576 In blind signing, the exchange sees
577 B = bkey * S mod pkey.n
578 On deposit, the exchange sees S so they can compute bkey' = B/S mod
579 pkey.n for all B they recorded to see if bkey' has it's high bit set.
580 Also, note the exchange can compute 1/S efficiently since they know the
581 factors of pkey.n.
582
583 I suppose that happens with probability r/(1+r) if its the wrong B, not
584 completely sure. If otoh we've the right B, then we've the probability
585 r/2 of a set high bit in the effective bkey.
586
587 Interestingly, r^2-r has a maximum at the default r=1/2 anyways, giving
588 the wrong and right probabilities 1/3 and 1/4, respectively.
589
590 I feared this gives the exchange a meaningful fraction of a bit of
591 information per coin involved in the transaction. It sounds damaging if
592 numerous coins were involved. And it could run across transactions in
593 some scenarios.
594
595 We fixed this by using a more uniform deterministic pseudo-random number
596 generator for blinding factors. I do not believe this to be a problem
597 for the rsa_full_domain_hash routine, but better safe than sorry.
598 */
599
600
601int
602GNUNET_CRYPTO_rsa_signature_cmp (const struct GNUNET_CRYPTO_RsaSignature *s1,
603 const struct GNUNET_CRYPTO_RsaSignature *s2)
604{
605 void *b1;
606 void *b2;
607 size_t z1;
608 size_t z2;
609 int ret;
610
611 z1 = GNUNET_CRYPTO_rsa_signature_encode (s1,
612 &b1);
613 z2 = GNUNET_CRYPTO_rsa_signature_encode (s2,
614 &b2);
615 if (z1 != z2)
616 ret = 1;
617 else
618 ret = memcmp (b1,
619 b2,
620 z1);
621 GNUNET_free (b1);
622 GNUNET_free (b2);
623 return ret;
624}
625
626
627int
628GNUNET_CRYPTO_rsa_public_key_cmp (const struct GNUNET_CRYPTO_RsaPublicKey *p1,
629 const struct GNUNET_CRYPTO_RsaPublicKey *p2)
630{
631 void *b1;
632 void *b2;
633 size_t z1;
634 size_t z2;
635 int ret;
636
637 z1 = GNUNET_CRYPTO_rsa_public_key_encode (p1,
638 &b1);
639 z2 = GNUNET_CRYPTO_rsa_public_key_encode (p2,
640 &b2);
641 if (z1 != z2)
642 ret = 1;
643 else
644 ret = memcmp (b1,
645 b2,
646 z1);
647 GNUNET_free (b1);
648 GNUNET_free (b2);
649 return ret;
650}
651
652
653int
654GNUNET_CRYPTO_rsa_private_key_cmp (const struct GNUNET_CRYPTO_RsaPrivateKey *p1,
655 const struct GNUNET_CRYPTO_RsaPrivateKey *p2)
656{
657 void *b1;
658 void *b2;
659 size_t z1;
660 size_t z2;
661 int ret;
662
663 z1 = GNUNET_CRYPTO_rsa_private_key_encode (p1,
664 &b1);
665 z2 = GNUNET_CRYPTO_rsa_private_key_encode (p2,
666 &b2);
667 if (z1 != z2)
668 ret = 1;
669 else
670 ret = memcmp (b1,
671 b2,
672 z1);
673 GNUNET_free (b1);
674 GNUNET_free (b2);
675 return ret;
676}
677
678
679unsigned int
680GNUNET_CRYPTO_rsa_public_key_len (const struct GNUNET_CRYPTO_RsaPublicKey *key)
681{
682 gcry_mpi_t n;
683 unsigned int rval;
684
685 if (0 != key_from_sexp (&n, key->sexp, "rsa", "n"))
686 { /* Not an RSA public key */
687 GNUNET_break (0);
688 return 0;
689 }
690 rval = gcry_mpi_get_nbits (n);
691 gcry_mpi_release (n);
692 return rval;
693}
694
695
696/**
697 * Destroy a blinding key
698 *
699 * @param bkey the blinding key to destroy
700 */
701static void
702rsa_blinding_key_free (struct RsaBlindingKey *bkey)
703{
704 gcry_mpi_release (bkey->r);
705 GNUNET_free (bkey);
706}
707
708
709/**
710 * Print an MPI to a newly created buffer
711 *
712 * @param v MPI to print.
713 * @param[out] buffer newly allocated buffer containing the result
714 * @return number of bytes stored in @a buffer
715 */
716static size_t
717numeric_mpi_alloc_n_print (gcry_mpi_t v,
718 char **buffer)
719{
720 size_t n;
721 char *b;
722 size_t rsize;
723
724 gcry_mpi_print (GCRYMPI_FMT_USG,
725 NULL,
726 0,
727 &n,
728 v);
729 b = GNUNET_malloc (n);
730 GNUNET_assert (0 ==
731 gcry_mpi_print (GCRYMPI_FMT_USG,
732 (unsigned char *) b,
733 n,
734 &rsize,
735 v));
736 *buffer = b;
737 return n;
738}
739
740
741/**
742 * Computes a full domain hash seeded by the given public key.
743 * This gives a measure of provable security to the Taler exchange
744 * against one-more forgery attacks. See:
745 * https://eprint.iacr.org/2001/002.pdf
746 * http://www.di.ens.fr/~pointche/Documents/Papers/2001_fcA.pdf
747 *
748 * @param message the message to sign
749 * @param message_size number of bytes in @a message
750 * @param pkey the public key of the signer
751 * @param rsize If not NULL, the number of bytes actually stored in buffer
752 * @return MPI value set to the FDH, NULL if RSA key is malicious
753 */
754static gcry_mpi_t
755rsa_full_domain_hash (const struct GNUNET_CRYPTO_RsaPublicKey *pkey,
756 const void *message,
757 size_t message_size)
758{
759 gcry_mpi_t r;
760 gcry_mpi_t n;
761 void *xts;
762 size_t xts_len;
763 int ok;
764
765 /* Extract the composite n from the RSA public key */
766 GNUNET_assert (0 ==
767 key_from_sexp (&n,
768 pkey->sexp,
769 "rsa",
770 "n"));
771 /* Assert that it at least looks like an RSA key */
772 GNUNET_assert (0 ==
773 gcry_mpi_get_flag (n,
774 GCRYMPI_FLAG_OPAQUE));
775
776 /* We key with the public denomination key as a homage to RSA-PSS by *
777 * Mihir Bellare and Phillip Rogaway. Doing this lowers the degree *
778 * of the hypothetical polyomial-time attack on RSA-KTI created by a *
779 * polynomial-time one-more forgary attack. Yey seeding! */
780 xts_len = GNUNET_CRYPTO_rsa_public_key_encode (pkey,
781 &xts);
782
783 GNUNET_CRYPTO_kdf_mod_mpi (&r,
784 n,
785 xts, xts_len,
786 message, message_size,
787 "RSA-FDA FTpsW!");
788 GNUNET_free (xts);
789 ok = rsa_gcd_validate (r, n);
790 gcry_mpi_release (n);
791 if (ok)
792 return r;
793 gcry_mpi_release (r);
794 return NULL;
795}
796
797
798void
799GNUNET_CRYPTO_rsa_blinded_message_free (
800 struct GNUNET_CRYPTO_RsaBlindedMessage *bm)
801{
802 GNUNET_free (bm->blinded_msg);
803}
804
805
806enum GNUNET_GenericReturnValue
807GNUNET_CRYPTO_rsa_blind (const void *message,
808 size_t message_size,
809 const struct GNUNET_CRYPTO_RsaBlindingKeySecret *bks,
810 struct GNUNET_CRYPTO_RsaPublicKey *pkey,
811 struct GNUNET_CRYPTO_RsaBlindedMessage *bm)
812{
813 struct RsaBlindingKey *bkey;
814 gcry_mpi_t data;
815 gcry_mpi_t ne[2];
816 gcry_mpi_t r_e;
817 gcry_mpi_t data_r_e;
818 int ret;
819
820 BENCHMARK_START (rsa_blind);
821 ret = key_from_sexp (ne,
822 pkey->sexp,
823 "public-key",
824 "ne");
825 if (0 != ret)
826 ret = key_from_sexp (ne,
827 pkey->sexp,
828 "rsa",
829 "ne");
830 if (0 != ret)
831 {
832 GNUNET_break (0);
833 bm->blinded_msg = NULL;
834 bm->blinded_msg_size = 0;
835 BENCHMARK_END (rsa_blind);
836 return GNUNET_NO;
837 }
838
839 data = rsa_full_domain_hash (pkey,
840 message,
841 message_size);
842 if (NULL == data)
843 goto rsa_gcd_validate_failure;
844 bkey = rsa_blinding_key_derive (pkey,
845 bks);
846 if (NULL == bkey)
847 {
848 gcry_mpi_release (data);
849 goto rsa_gcd_validate_failure;
850 }
851 r_e = gcry_mpi_new (0);
852 gcry_mpi_powm (r_e,
853 bkey->r,
854 ne[1],
855 ne[0]);
856 data_r_e = gcry_mpi_new (0);
857 gcry_mpi_mulm (data_r_e,
858 data,
859 r_e,
860 ne[0]);
861 gcry_mpi_release (data);
862 gcry_mpi_release (ne[0]);
863 gcry_mpi_release (ne[1]);
864 gcry_mpi_release (r_e);
865 rsa_blinding_key_free (bkey);
866
867 bm->blinded_msg_size
868 = numeric_mpi_alloc_n_print (data_r_e,
869 (char **) &bm->blinded_msg);
870 gcry_mpi_release (data_r_e);
871
872 BENCHMARK_END (rsa_blind);
873 return GNUNET_YES;
874
875rsa_gcd_validate_failure:
876 /* We know the RSA key is malicious here, so warn the wallet. */
877 /* GNUNET_break_op (0); */
878 gcry_mpi_release (ne[0]);
879 gcry_mpi_release (ne[1]);
880 bm->blinded_msg = NULL;
881 bm->blinded_msg_size = 0;
882 BENCHMARK_END (rsa_blind);
883 return GNUNET_NO;
884}
885
886
887/**
888 * Convert an MPI to an S-expression suitable for signature operations.
889 *
890 * @param value pointer to the data to convert
891 * @return converted s-expression
892 */
893static gcry_sexp_t
894mpi_to_sexp (gcry_mpi_t value)
895{
896 gcry_sexp_t data = NULL;
897
898 GNUNET_assert (0 ==
899 gcry_sexp_build (&data,
900 NULL,
901 "(data (flags raw) (value %M))",
902 value));
903 return data;
904}
905
906
907/**
908 * Sign the given MPI.
909 *
910 * @param key private key to use for the signing
911 * @param value the MPI to sign
912 * @return NULL on error, signature on success
913 */
914static struct GNUNET_CRYPTO_RsaSignature *
915rsa_sign_mpi (const struct GNUNET_CRYPTO_RsaPrivateKey *key,
916 gcry_mpi_t value)
917{
918 struct GNUNET_CRYPTO_RsaSignature *sig;
919 gcry_sexp_t data;
920 gcry_sexp_t result;
921 int rc;
922
923 data = mpi_to_sexp (value);
924
925 if (0 !=
926 (rc = gcry_pk_sign (&result,
927 data,
928 key->sexp)))
929 {
930 LOG (GNUNET_ERROR_TYPE_WARNING,
931 _ ("RSA signing failed at %s:%d: %s\n"),
932 __FILE__,
933 __LINE__,
934 gcry_strerror (rc));
935 gcry_sexp_release (data);
936 GNUNET_break (0);
937 return NULL;
938 }
939
940 /* Lenstra protection was first added to libgcrypt 1.6.4
941 * with commit c17f84bd02d7ee93845e92e20f6ddba814961588.
942 */
943#if GCRYPT_VERSION_NUMBER < 0x010604
944 /* verify signature (guards against Lenstra's attack with fault injection...) */
945 struct GNUNET_CRYPTO_RsaPublicKey *public_key =
946 GNUNET_CRYPTO_rsa_private_key_get_public (key);
947 if (0 !=
948 gcry_pk_verify (result,
949 data,
950 public_key->sexp))
951 {
952 GNUNET_break (0);
953 GNUNET_CRYPTO_rsa_public_key_free (public_key);
954 gcry_sexp_release (data);
955 gcry_sexp_release (result);
956 return NULL;
957 }
958 GNUNET_CRYPTO_rsa_public_key_free (public_key);
959#endif
960
961 /* return signature */
962 gcry_sexp_release (data);
963 sig = GNUNET_new (struct GNUNET_CRYPTO_RsaSignature);
964 sig->sexp = result;
965 return sig;
966}
967
968
969struct GNUNET_CRYPTO_RsaSignature *
970GNUNET_CRYPTO_rsa_sign_blinded (const struct GNUNET_CRYPTO_RsaPrivateKey *key,
971 const struct GNUNET_CRYPTO_RsaBlindedMessage *bm)
972{
973 gcry_mpi_t v = NULL;
974 struct GNUNET_CRYPTO_RsaSignature *sig;
975
976 BENCHMARK_START (rsa_sign_blinded);
977 GNUNET_assert (0 ==
978 gcry_mpi_scan (&v,
979 GCRYMPI_FMT_USG,
980 bm->blinded_msg,
981 bm->blinded_msg_size,
982 NULL));
983 sig = rsa_sign_mpi (key,
984 v);
985 gcry_mpi_release (v);
986 BENCHMARK_END (rsa_sign_blinded);
987 return sig;
988}
989
990
991struct GNUNET_CRYPTO_RsaSignature *
992GNUNET_CRYPTO_rsa_sign_fdh (const struct GNUNET_CRYPTO_RsaPrivateKey *key,
993 const void *message,
994 size_t message_size)
995{
996 struct GNUNET_CRYPTO_RsaPublicKey *pkey;
997 gcry_mpi_t v = NULL;
998 struct GNUNET_CRYPTO_RsaSignature *sig;
999
1000 pkey = GNUNET_CRYPTO_rsa_private_key_get_public (key);
1001 v = rsa_full_domain_hash (pkey,
1002 message,
1003 message_size);
1004 GNUNET_CRYPTO_rsa_public_key_free (pkey);
1005 if (NULL == v) /* rsa_gcd_validate failed meaning */
1006 return NULL; /* our *own* RSA key is malicious. */
1007
1008 sig = rsa_sign_mpi (key, v);
1009 gcry_mpi_release (v);
1010 return sig;
1011}
1012
1013
1014void
1015GNUNET_CRYPTO_rsa_signature_free (struct GNUNET_CRYPTO_RsaSignature *sig)
1016{
1017 gcry_sexp_release (sig->sexp);
1018 GNUNET_free (sig);
1019}
1020
1021
1022size_t
1023GNUNET_CRYPTO_rsa_signature_encode (
1024 const struct GNUNET_CRYPTO_RsaSignature *sig,
1025 void **buffer)
1026{
1027 gcry_mpi_t s;
1028 size_t buf_size;
1029 size_t rsize;
1030 unsigned char *buf;
1031 int ret;
1032
1033 ret = key_from_sexp (&s,
1034 sig->sexp,
1035 "sig-val",
1036 "s");
1037 if (0 != ret)
1038 ret = key_from_sexp (&s,
1039 sig->sexp,
1040 "rsa",
1041 "s");
1042 GNUNET_assert (0 == ret);
1043 gcry_mpi_print (GCRYMPI_FMT_USG,
1044 NULL,
1045 0,
1046 &buf_size,
1047 s);
1048 buf = GNUNET_malloc (buf_size);
1049 GNUNET_assert (0 ==
1050 gcry_mpi_print (GCRYMPI_FMT_USG,
1051 buf,
1052 buf_size,
1053 &rsize,
1054 s));
1055 GNUNET_assert (rsize == buf_size);
1056 *buffer = (void *) buf;
1057 gcry_mpi_release (s);
1058 return buf_size;
1059}
1060
1061
1062struct GNUNET_CRYPTO_RsaSignature *
1063GNUNET_CRYPTO_rsa_signature_decode (const void *buf,
1064 size_t buf_size)
1065{
1066 struct GNUNET_CRYPTO_RsaSignature *sig;
1067 gcry_mpi_t s;
1068 gcry_sexp_t data;
1069
1070 if (0 !=
1071 gcry_mpi_scan (&s,
1072 GCRYMPI_FMT_USG,
1073 buf,
1074 buf_size,
1075 NULL))
1076 {
1077 GNUNET_break_op (0);
1078 return NULL;
1079 }
1080
1081 if (0 !=
1082 gcry_sexp_build (&data,
1083 NULL,
1084 "(sig-val(rsa(s %M)))",
1085 s))
1086 {
1087 GNUNET_break (0);
1088 gcry_mpi_release (s);
1089 return NULL;
1090 }
1091 gcry_mpi_release (s);
1092 sig = GNUNET_new (struct GNUNET_CRYPTO_RsaSignature);
1093 sig->sexp = data;
1094 return sig;
1095}
1096
1097
1098struct GNUNET_CRYPTO_RsaPublicKey *
1099GNUNET_CRYPTO_rsa_public_key_dup (const struct GNUNET_CRYPTO_RsaPublicKey *key)
1100{
1101 struct GNUNET_CRYPTO_RsaPublicKey *dup;
1102 gcry_sexp_t dup_sexp;
1103 size_t erroff;
1104
1105 /* check if we really are exporting a public key */
1106 dup_sexp = gcry_sexp_find_token (key->sexp, "public-key", 0);
1107 GNUNET_assert (NULL != dup_sexp);
1108 gcry_sexp_release (dup_sexp);
1109 /* copy the sexp */
1110 GNUNET_assert (0 == gcry_sexp_build (&dup_sexp, &erroff, "%S", key->sexp));
1111 dup = GNUNET_new (struct GNUNET_CRYPTO_RsaPublicKey);
1112 dup->sexp = dup_sexp;
1113 return dup;
1114}
1115
1116
1117struct GNUNET_CRYPTO_RsaSignature *
1118GNUNET_CRYPTO_rsa_unblind (const struct GNUNET_CRYPTO_RsaSignature *sig,
1119 const struct GNUNET_CRYPTO_RsaBlindingKeySecret *bks,
1120 struct GNUNET_CRYPTO_RsaPublicKey *pkey)
1121{
1122 struct RsaBlindingKey *bkey;
1123 gcry_mpi_t n;
1124 gcry_mpi_t s;
1125 gcry_mpi_t r_inv;
1126 gcry_mpi_t ubsig;
1127 int ret;
1128 struct GNUNET_CRYPTO_RsaSignature *sret;
1129
1130 BENCHMARK_START (rsa_unblind);
1131
1132 ret = key_from_sexp (&n, pkey->sexp, "public-key", "n");
1133 if (0 != ret)
1134 ret = key_from_sexp (&n, pkey->sexp, "rsa", "n");
1135 if (0 != ret)
1136 {
1137 GNUNET_break_op (0);
1138 return NULL;
1139 }
1140 ret = key_from_sexp (&s, sig->sexp, "sig-val", "s");
1141 if (0 != ret)
1142 ret = key_from_sexp (&s, sig->sexp, "rsa", "s");
1143 if (0 != ret)
1144 {
1145 gcry_mpi_release (n);
1146 GNUNET_break_op (0);
1147 return NULL;
1148 }
1149
1150 bkey = rsa_blinding_key_derive (pkey, bks);
1151 if (NULL == bkey)
1152 {
1153 /* RSA key is malicious since rsa_gcd_validate failed here.
1154 * It should have failed during GNUNET_CRYPTO_rsa_blind too though,
1155 * so the exchange is being malicious in an unfamilair way, maybe
1156 * just trying to crash us. */
1157 GNUNET_break_op (0);
1158 gcry_mpi_release (n);
1159 gcry_mpi_release (s);
1160 return NULL;
1161 }
1162
1163 r_inv = gcry_mpi_new (0);
1164 if (1 !=
1165 gcry_mpi_invm (r_inv,
1166 bkey->r,
1167 n))
1168 {
1169 /* We cannot find r mod n, so gcd(r,n) != 1, which should get *
1170 * caught above, but we handle it the same here. */
1171 GNUNET_break_op (0);
1172 gcry_mpi_release (r_inv);
1173 rsa_blinding_key_free (bkey);
1174 gcry_mpi_release (n);
1175 gcry_mpi_release (s);
1176 return NULL;
1177 }
1178
1179 ubsig = gcry_mpi_new (0);
1180 gcry_mpi_mulm (ubsig, s, r_inv, n);
1181 gcry_mpi_release (n);
1182 gcry_mpi_release (r_inv);
1183 gcry_mpi_release (s);
1184 rsa_blinding_key_free (bkey);
1185
1186 sret = GNUNET_new (struct GNUNET_CRYPTO_RsaSignature);
1187 GNUNET_assert (0 ==
1188 gcry_sexp_build (&sret->sexp,
1189 NULL,
1190 "(sig-val (rsa (s %M)))",
1191 ubsig));
1192 gcry_mpi_release (ubsig);
1193 BENCHMARK_END (rsa_unblind);
1194 return sret;
1195}
1196
1197
1198enum GNUNET_GenericReturnValue
1199GNUNET_CRYPTO_rsa_verify (const void *message,
1200 size_t message_size,
1201 const struct GNUNET_CRYPTO_RsaSignature *sig,
1202 const struct GNUNET_CRYPTO_RsaPublicKey *pkey)
1203{
1204 gcry_sexp_t data;
1205 gcry_mpi_t r;
1206 int rc;
1207
1208 BENCHMARK_START (rsa_verify);
1209
1210 r = rsa_full_domain_hash (pkey,
1211 message,
1212 message_size);
1213 if (NULL == r)
1214 {
1215 GNUNET_break_op (0);
1216 /* RSA key is malicious since rsa_gcd_validate failed here.
1217 * It should have failed during GNUNET_CRYPTO_rsa_blind too though,
1218 * so the exchange is being malicious in an unfamilair way, maybe
1219 * just trying to crash us. Arguably, we've only an internal error
1220 * though because we should've detected this in our previous call
1221 * to GNUNET_CRYPTO_rsa_unblind. *///
1222 return GNUNET_NO;
1223 }
1224
1225 data = mpi_to_sexp (r);
1226 gcry_mpi_release (r);
1227
1228 rc = gcry_pk_verify (sig->sexp,
1229 data,
1230 pkey->sexp);
1231 gcry_sexp_release (data);
1232 if (0 != rc)
1233 {
1234 LOG (GNUNET_ERROR_TYPE_WARNING,
1235 _ ("RSA signature verification failed at %s:%d: %s\n"),
1236 __FILE__,
1237 __LINE__,
1238 gcry_strerror (rc));
1239 BENCHMARK_END (rsa_verify);
1240 return GNUNET_SYSERR;
1241 }
1242 BENCHMARK_END (rsa_verify);
1243 return GNUNET_OK;
1244}
1245
1246
1247struct GNUNET_CRYPTO_RsaPrivateKey *
1248GNUNET_CRYPTO_rsa_private_key_dup (
1249 const struct GNUNET_CRYPTO_RsaPrivateKey *key)
1250{
1251 struct GNUNET_CRYPTO_RsaPrivateKey *dup;
1252 gcry_sexp_t dup_sexp;
1253 size_t erroff;
1254
1255 /* check if we really are exporting a private key */
1256 dup_sexp = gcry_sexp_find_token (key->sexp, "private-key", 0);
1257 GNUNET_assert (NULL != dup_sexp);
1258 gcry_sexp_release (dup_sexp);
1259 /* copy the sexp */
1260 GNUNET_assert (0 == gcry_sexp_build (&dup_sexp, &erroff, "%S", key->sexp));
1261 dup = GNUNET_new (struct GNUNET_CRYPTO_RsaPrivateKey);
1262 dup->sexp = dup_sexp;
1263 return dup;
1264}
1265
1266
1267struct GNUNET_CRYPTO_RsaSignature *
1268GNUNET_CRYPTO_rsa_signature_dup (const struct GNUNET_CRYPTO_RsaSignature *sig)
1269{
1270 struct GNUNET_CRYPTO_RsaSignature *dup;
1271 gcry_sexp_t dup_sexp;
1272 size_t erroff;
1273 gcry_mpi_t s;
1274 int ret;
1275
1276 /* verify that this is an RSA signature */
1277 ret = key_from_sexp (&s, sig->sexp, "sig-val", "s");
1278 if (0 != ret)
1279 ret = key_from_sexp (&s, sig->sexp, "rsa", "s");
1280 GNUNET_assert (0 == ret);
1281 gcry_mpi_release (s);
1282 /* copy the sexp */
1283 GNUNET_assert (0 == gcry_sexp_build (&dup_sexp, &erroff, "%S", sig->sexp));
1284 dup = GNUNET_new (struct GNUNET_CRYPTO_RsaSignature);
1285 dup->sexp = dup_sexp;
1286 return dup;
1287}
1288
1289
1290/* end of crypto_rsa.c */
diff --git a/src/lib/util/crypto_symmetric.c b/src/lib/util/crypto_symmetric.c
new file mode 100644
index 000000000..c08b84c17
--- /dev/null
+++ b/src/lib/util/crypto_symmetric.c
@@ -0,0 +1,254 @@
1/*
2 This file is part of GNUnet.
3 Copyright (C) 2001, 2002, 2003, 2004, 2005, 2006, 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/crypto_symmetric.c
23 * @brief Symmetric encryption services; combined cipher AES+TWOFISH (256-bit each)
24 * @author Christian Grothoff
25 * @author Ioana Patrascu
26 */
27
28
29#include "platform.h"
30#include "gnunet_util_lib.h"
31#include <gcrypt.h>
32
33#define LOG(kind, ...) GNUNET_log_from (kind, "util-crypto-symmetric", \
34 __VA_ARGS__)
35
36/**
37 * Create a new SessionKey (for symmetric encryption).
38 *
39 * @param key session key to initialize
40 */
41void
42GNUNET_CRYPTO_symmetric_create_session_key (struct
43 GNUNET_CRYPTO_SymmetricSessionKey *
44 key)
45{
46 gcry_randomize (key->aes_key,
47 GNUNET_CRYPTO_AES_KEY_LENGTH,
48 GCRY_STRONG_RANDOM);
49 gcry_randomize (key->twofish_key,
50 GNUNET_CRYPTO_AES_KEY_LENGTH,
51 GCRY_STRONG_RANDOM);
52}
53
54
55/**
56 * Initialize AES cipher.
57 *
58 * @param handle handle to initialize
59 * @param sessionkey session key to use
60 * @param iv initialization vector to use
61 * @return #GNUNET_OK on success, #GNUNET_SYSERR on error
62 */
63static int
64setup_cipher_aes (gcry_cipher_hd_t *handle,
65 const struct GNUNET_CRYPTO_SymmetricSessionKey *sessionkey,
66 const struct GNUNET_CRYPTO_SymmetricInitializationVector *iv)
67{
68 int rc;
69
70 GNUNET_assert (0 ==
71 gcry_cipher_open (handle, GCRY_CIPHER_AES256,
72 GCRY_CIPHER_MODE_CFB, 0));
73 rc = gcry_cipher_setkey (*handle,
74 sessionkey->aes_key,
75 sizeof(sessionkey->aes_key));
76 GNUNET_assert ((0 == rc) || ((char) rc == GPG_ERR_WEAK_KEY));
77 rc = gcry_cipher_setiv (*handle,
78 iv->aes_iv,
79 sizeof(iv->aes_iv));
80 GNUNET_assert ((0 == rc) || ((char) rc == GPG_ERR_WEAK_KEY));
81 return GNUNET_OK;
82}
83
84
85/**
86 * Initialize TWOFISH cipher.
87 *
88 * @param handle handle to initialize
89 * @param sessionkey session key to use
90 * @param iv initialization vector to use
91 * @return #GNUNET_OK on success, #GNUNET_SYSERR on error
92 */
93static int
94setup_cipher_twofish (gcry_cipher_hd_t *handle,
95 const struct
96 GNUNET_CRYPTO_SymmetricSessionKey *sessionkey,
97 const struct
98 GNUNET_CRYPTO_SymmetricInitializationVector *iv)
99{
100 int rc;
101
102 GNUNET_assert (0 ==
103 gcry_cipher_open (handle, GCRY_CIPHER_TWOFISH,
104 GCRY_CIPHER_MODE_CFB, 0));
105 rc = gcry_cipher_setkey (*handle,
106 sessionkey->twofish_key,
107 sizeof(sessionkey->twofish_key));
108 GNUNET_assert ((0 == rc) || ((char) rc == GPG_ERR_WEAK_KEY));
109 rc = gcry_cipher_setiv (*handle,
110 iv->twofish_iv,
111 sizeof(iv->twofish_iv));
112 GNUNET_assert ((0 == rc) || ((char) rc == GPG_ERR_WEAK_KEY));
113 return GNUNET_OK;
114}
115
116
117/**
118 * Encrypt a block with a symmetric session key.
119 *
120 * @param block the block to encrypt
121 * @param size the size of the @a block
122 * @param sessionkey the key used to encrypt
123 * @param iv the initialization vector to use, use INITVALUE for streams
124 * @param result the output parameter in which to store the encrypted result
125 * can be the same or overlap with @c block
126 * @returns the size of the encrypted block, -1 for errors.
127 * Due to the use of CFB and therefore an effective stream cipher,
128 * this size should be the same as @c len.
129 */
130ssize_t
131GNUNET_CRYPTO_symmetric_encrypt (const void *block,
132 size_t size,
133 const struct
134 GNUNET_CRYPTO_SymmetricSessionKey *sessionkey,
135 const struct
136 GNUNET_CRYPTO_SymmetricInitializationVector *iv,
137 void *result)
138{
139 gcry_cipher_hd_t handle;
140 char tmp[GNUNET_NZL(size)];
141
142 if (GNUNET_OK != setup_cipher_aes (&handle, sessionkey, iv))
143 return -1;
144 GNUNET_assert (0 == gcry_cipher_encrypt (handle, tmp, size, block, size));
145 gcry_cipher_close (handle);
146 if (GNUNET_OK != setup_cipher_twofish (&handle, sessionkey, iv))
147 return -1;
148 GNUNET_assert (0 == gcry_cipher_encrypt (handle, result, size, tmp, size));
149 gcry_cipher_close (handle);
150 memset (tmp, 0, sizeof(tmp));
151 return size;
152}
153
154
155/**
156 * Decrypt a given block with the session key.
157 *
158 * @param block the data to decrypt, encoded as returned by encrypt
159 * @param size the size of the @a block to decrypt
160 * @param sessionkey the key used to decrypt
161 * @param iv the initialization vector to use, use INITVALUE for streams
162 * @param result address to store the result at
163 * can be the same or overlap with @c block
164 * @return -1 on failure, size of decrypted block on success.
165 * Due to the use of CFB and therefore an effective stream cipher,
166 * this size should be the same as @c size.
167 */
168ssize_t
169GNUNET_CRYPTO_symmetric_decrypt (const void *block,
170 size_t size,
171 const struct
172 GNUNET_CRYPTO_SymmetricSessionKey *sessionkey,
173 const struct
174 GNUNET_CRYPTO_SymmetricInitializationVector *iv,
175 void *result)
176{
177 gcry_cipher_hd_t handle;
178 char tmp[size];
179
180 if (GNUNET_OK != setup_cipher_twofish (&handle, sessionkey, iv))
181 return -1;
182 GNUNET_assert (0 == gcry_cipher_decrypt (handle, tmp, size, block, size));
183 gcry_cipher_close (handle);
184 if (GNUNET_OK != setup_cipher_aes (&handle, sessionkey, iv))
185 return -1;
186 GNUNET_assert (0 == gcry_cipher_decrypt (handle, result, size, tmp, size));
187 gcry_cipher_close (handle);
188 memset (tmp, 0, sizeof(tmp));
189 return size;
190}
191
192
193/**
194 * @brief Derive an IV
195 *
196 * @param iv initialization vector
197 * @param skey session key
198 * @param salt salt for the derivation
199 * @param salt_len size of the @a salt
200 * @param ... pairs of void * & size_t for context chunks, terminated by NULL
201 */
202void
203GNUNET_CRYPTO_symmetric_derive_iv (struct
204 GNUNET_CRYPTO_SymmetricInitializationVector *
205 iv,
206 const struct
207 GNUNET_CRYPTO_SymmetricSessionKey *skey,
208 const void *salt,
209 size_t salt_len,
210 ...)
211{
212 va_list argp;
213
214 va_start (argp, salt_len);
215 GNUNET_CRYPTO_symmetric_derive_iv_v (iv, skey, salt, salt_len, argp);
216 va_end (argp);
217}
218
219
220void
221GNUNET_CRYPTO_symmetric_derive_iv_v (struct
222 GNUNET_CRYPTO_SymmetricInitializationVector
223 *iv,
224 const struct
225 GNUNET_CRYPTO_SymmetricSessionKey *skey,
226 const void *salt,
227 size_t salt_len,
228 va_list argp)
229{
230 char aes_salt[salt_len + 4];
231 char twofish_salt[salt_len + 4];
232
233 GNUNET_memcpy (aes_salt, salt, salt_len);
234 GNUNET_memcpy (&aes_salt[salt_len], "AES!", 4);
235 GNUNET_memcpy (twofish_salt, salt, salt_len);
236 GNUNET_memcpy (&twofish_salt[salt_len], "FISH", 4);
237 GNUNET_CRYPTO_kdf_v (iv->aes_iv,
238 sizeof(iv->aes_iv),
239 aes_salt,
240 salt_len + 4,
241 skey->aes_key,
242 sizeof(skey->aes_key),
243 argp);
244 GNUNET_CRYPTO_kdf_v (iv->twofish_iv,
245 sizeof(iv->twofish_iv),
246 twofish_salt,
247 salt_len + 4,
248 skey->twofish_key,
249 sizeof(skey->twofish_key),
250 argp);
251}
252
253
254/* end of crypto_symmetric.c */
diff --git a/src/lib/util/disk.c b/src/lib/util/disk.c
new file mode 100644
index 000000000..567c2b5bc
--- /dev/null
+++ b/src/lib/util/disk.c
@@ -0,0 +1,1689 @@
1/*
2 This file is part of GNUnet.
3 Copyright (C) 2001--2013, 2016, 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 util/disk.c
22 * @brief disk IO convenience methods
23 * @author Christian Grothoff
24 * @author Nils Durner
25 */
26
27#include "platform.h"
28#include "gnunet_util_lib.h"
29
30#define LOG(kind, ...) GNUNET_log_from (kind, "util-disk", __VA_ARGS__)
31
32#define LOG_STRERROR(kind, syscall) \
33 GNUNET_log_from_strerror (kind, "util-disk", syscall)
34
35#define LOG_STRERROR_FILE(kind, syscall, filename) \
36 GNUNET_log_from_strerror_file (kind, "util-disk", syscall, filename)
37
38/**
39 * Block size for IO for copying files.
40 */
41#define COPY_BLK_SIZE 65536
42
43#include <sys/types.h>
44#if HAVE_SYS_VFS_H
45#include <sys/vfs.h>
46#endif
47#if HAVE_SYS_PARAM_H
48#include <sys/param.h>
49#endif
50#if HAVE_SYS_MOUNT_H
51#include <sys/mount.h>
52#endif
53#if HAVE_SYS_STATVFS_H
54#include <sys/statvfs.h>
55#endif
56
57#ifndef S_ISLNK
58#define _IFMT 0170000 /* type of file */
59#define _IFLNK 0120000 /* symbolic link */
60#define S_ISLNK(m) (((m) & _IFMT) == _IFLNK)
61#endif
62
63
64/**
65 * Handle used to manage a pipe.
66 */
67struct GNUNET_DISK_PipeHandle
68{
69 /**
70 * File descriptors for the pipe.
71 * One or both of them could be NULL.
72 */
73 struct GNUNET_DISK_FileHandle *fd[2];
74};
75
76
77/**
78 * Closure for the recursion to determine the file size
79 * of a directory.
80 */
81struct GetFileSizeData
82{
83 /**
84 * Set to the total file size.
85 */
86 uint64_t total;
87
88 /**
89 * GNUNET_YES if symbolic links should be included.
90 */
91 int include_sym_links;
92
93 /**
94 * #GNUNET_YES if mode is file-only (return total == -1 for directories).
95 */
96 int single_file_mode;
97};
98
99
100/**
101 * Translate GNUnet-internal permission bitmap to UNIX file
102 * access permission bitmap.
103 *
104 * @param perm file permissions, GNUnet style
105 * @return file permissions, UNIX style
106 */
107static int
108translate_unix_perms (enum GNUNET_DISK_AccessPermissions perm)
109{
110 int mode;
111
112 mode = 0;
113 if (perm & GNUNET_DISK_PERM_USER_READ)
114 mode |= S_IRUSR;
115 if (perm & GNUNET_DISK_PERM_USER_WRITE)
116 mode |= S_IWUSR;
117 if (perm & GNUNET_DISK_PERM_USER_EXEC)
118 mode |= S_IXUSR;
119 if (perm & GNUNET_DISK_PERM_GROUP_READ)
120 mode |= S_IRGRP;
121 if (perm & GNUNET_DISK_PERM_GROUP_WRITE)
122 mode |= S_IWGRP;
123 if (perm & GNUNET_DISK_PERM_GROUP_EXEC)
124 mode |= S_IXGRP;
125 if (perm & GNUNET_DISK_PERM_OTHER_READ)
126 mode |= S_IROTH;
127 if (perm & GNUNET_DISK_PERM_OTHER_WRITE)
128 mode |= S_IWOTH;
129 if (perm & GNUNET_DISK_PERM_OTHER_EXEC)
130 mode |= S_IXOTH;
131
132 return mode;
133}
134
135
136/**
137 * Iterate over all files in the given directory and
138 * accumulate their size.
139 *
140 * @param cls closure of type `struct GetFileSizeData`
141 * @param fn current filename we are looking at
142 * @return #GNUNET_SYSERR on serious errors, otherwise #GNUNET_OK
143 */
144static enum GNUNET_GenericReturnValue
145get_size_rec (void *cls, const char *fn)
146{
147 struct GetFileSizeData *gfsd = cls;
148
149#if defined(HAVE_STAT64) && \
150 ! (defined(_FILE_OFFSET_BITS) && _FILE_OFFSET_BITS == 64)
151 struct stat64 buf;
152
153 if (0 != stat64 (fn, &buf))
154 {
155 LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_DEBUG, "stat64", fn);
156 return GNUNET_SYSERR;
157 }
158#else
159 struct stat buf;
160
161 if (0 != stat (fn, &buf))
162 {
163 LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_DEBUG, "stat", fn);
164 return GNUNET_SYSERR;
165 }
166#endif
167 if ((S_ISDIR (buf.st_mode)) && (gfsd->single_file_mode == GNUNET_YES))
168 {
169 errno = EISDIR;
170 return GNUNET_SYSERR;
171 }
172 if ((! S_ISLNK (buf.st_mode)) || (gfsd->include_sym_links == GNUNET_YES))
173 gfsd->total += buf.st_size;
174 if ((S_ISDIR (buf.st_mode)) && (0 == access (fn, X_OK)) &&
175 ((! S_ISLNK (buf.st_mode)) || (gfsd->include_sym_links == GNUNET_YES)))
176 {
177 if (GNUNET_SYSERR == GNUNET_DISK_directory_scan (fn, &get_size_rec, gfsd))
178 return GNUNET_SYSERR;
179 }
180 return GNUNET_OK;
181}
182
183
184enum GNUNET_GenericReturnValue
185GNUNET_DISK_handle_invalid (const struct GNUNET_DISK_FileHandle *h)
186{
187 return ((! h) || (h->fd == -1)) ? GNUNET_YES : GNUNET_NO;
188}
189
190
191enum GNUNET_GenericReturnValue
192GNUNET_DISK_file_handle_size (struct GNUNET_DISK_FileHandle *fh,
193 off_t *size)
194{
195 struct stat sbuf;
196
197 if (0 != fstat (fh->fd, &sbuf))
198 return GNUNET_SYSERR;
199 *size = sbuf.st_size;
200 return GNUNET_OK;
201}
202
203
204off_t
205GNUNET_DISK_file_seek (const struct GNUNET_DISK_FileHandle *h,
206 off_t offset,
207 enum GNUNET_DISK_Seek whence)
208{
209 static int t[] = { SEEK_SET, SEEK_CUR, SEEK_END };
210
211 if (h == NULL)
212 {
213 errno = EINVAL;
214 return GNUNET_SYSERR;
215 }
216 return lseek (h->fd, offset, t[whence]);
217}
218
219
220enum GNUNET_GenericReturnValue
221GNUNET_DISK_file_size (const char *filename,
222 uint64_t *size,
223 int include_symbolic_links,
224 int single_file_mode)
225{
226 struct GetFileSizeData gfsd;
227 enum GNUNET_GenericReturnValue ret;
228
229 GNUNET_assert (size != NULL);
230 gfsd.total = 0;
231 gfsd.include_sym_links = include_symbolic_links;
232 gfsd.single_file_mode = single_file_mode;
233 ret = get_size_rec (&gfsd, filename);
234 *size = gfsd.total;
235 return ret;
236}
237
238
239enum GNUNET_GenericReturnValue
240GNUNET_DISK_file_get_identifiers (const char *filename,
241 uint64_t *dev,
242 uint64_t *ino)
243{
244#if HAVE_STAT
245 {
246 struct stat sbuf;
247
248 if (0 != stat (filename, &sbuf))
249 {
250 return GNUNET_SYSERR;
251 }
252 *ino = (uint64_t) sbuf.st_ino;
253 }
254#else
255 *ino = 0;
256#endif
257#if HAVE_STATVFS
258 {
259 struct statvfs fbuf;
260
261 if (0 != statvfs (filename, &fbuf))
262 {
263 return GNUNET_SYSERR;
264 }
265 *dev = (uint64_t) fbuf.f_fsid;
266 }
267#elif HAVE_STATFS
268 {
269 struct statfs fbuf;
270
271 if (0 != statfs (filename, &fbuf))
272 {
273 return GNUNET_SYSERR;
274 }
275 *dev =
276 ((uint64_t) fbuf.f_fsid.val[0]) << 32 || ((uint64_t) fbuf.f_fsid.val[1]);
277 }
278#else
279 *dev = 0;
280#endif
281 return GNUNET_OK;
282}
283
284
285/**
286 * Create the name for a temporary file or directory from a template.
287 *
288 * @param t template (without XXXXX or "/tmp/")
289 * @return name ready for passing to 'mktemp' or 'mkdtemp', NULL on error
290 */
291static char *
292mktemp_name (const char *t)
293{
294 const char *tmpdir;
295 char *tmpl;
296 char *fn;
297
298 if ((t[0] != '/') && (t[0] != '\\'))
299 {
300 /* FIXME: This uses system codepage on W32, not UTF-8 */
301 tmpdir = getenv ("TMPDIR");
302 if (NULL == tmpdir)
303 tmpdir = getenv ("TMP");
304 if (NULL == tmpdir)
305 tmpdir = getenv ("TEMP");
306 if (NULL == tmpdir)
307 tmpdir = "/tmp";
308 GNUNET_asprintf (&tmpl, "%s/%s%s", tmpdir, t, "XXXXXX");
309 }
310 else
311 {
312 GNUNET_asprintf (&tmpl, "%s%s", t, "XXXXXX");
313 }
314 fn = tmpl;
315 return fn;
316}
317
318
319void
320GNUNET_DISK_fix_permissions (const char *fn,
321 int require_uid_match,
322 int require_gid_match)
323{
324 mode_t mode;
325
326 if (GNUNET_YES == require_uid_match)
327 mode = S_IRUSR | S_IWUSR | S_IXUSR;
328 else if (GNUNET_YES == require_gid_match)
329 mode = S_IRUSR | S_IWUSR | S_IXUSR | S_IRGRP | S_IWGRP | S_IXGRP;
330 else
331 mode = S_IRUSR | S_IWUSR | S_IXUSR | S_IRGRP | S_IWGRP | S_IXGRP | S_IROTH
332 | S_IWOTH | S_IXOTH;
333 if (0 != chmod (fn, mode))
334 GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING, "chmod", fn);
335}
336
337
338char *
339GNUNET_DISK_mkdtemp (const char *t)
340{
341 char *fn;
342 mode_t omask;
343
344 omask = umask (S_IWGRP | S_IWOTH | S_IRGRP | S_IROTH);
345 fn = mktemp_name (t);
346 if (fn != mkdtemp (fn))
347 {
348 LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_ERROR, "mkdtemp", fn);
349 GNUNET_free (fn);
350 umask (omask);
351 return NULL;
352 }
353 umask (omask);
354 return fn;
355}
356
357
358void
359GNUNET_DISK_file_backup (const char *fil)
360{
361 size_t slen;
362 char *target;
363 unsigned int num;
364
365 slen = strlen (fil) + 20;
366 target = GNUNET_malloc (slen);
367 num = 0;
368 do
369 {
370 GNUNET_snprintf (target, slen, "%s.%u~", fil, num++);
371 }
372 while (0 == access (target, F_OK));
373 if (0 != rename (fil, target))
374 GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_ERROR, "rename", fil);
375 GNUNET_free (target);
376}
377
378
379char *
380GNUNET_DISK_mktemp (const char *t)
381{
382 int fd;
383 char *fn;
384 mode_t omask;
385
386 omask = umask (S_IWGRP | S_IWOTH | S_IRGRP | S_IROTH);
387 fn = mktemp_name (t);
388 if (-1 == (fd = mkstemp (fn)))
389 {
390 LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_ERROR, "mkstemp", fn);
391 GNUNET_free (fn);
392 umask (omask);
393 return NULL;
394 }
395 umask (omask);
396 if (0 != close (fd))
397 LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_WARNING, "close", fn);
398 return fn;
399}
400
401
402enum GNUNET_GenericReturnValue
403GNUNET_DISK_directory_test (const char *fil, int is_readable)
404{
405 struct stat filestat;
406 int ret;
407
408 ret = stat (fil, &filestat);
409 if (ret != 0)
410 {
411 if (errno != ENOENT)
412 LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_WARNING, "stat", fil);
413 return GNUNET_SYSERR;
414 }
415 if (! S_ISDIR (filestat.st_mode))
416 {
417 LOG (GNUNET_ERROR_TYPE_INFO,
418 "A file already exits with the same name %s\n",
419 fil);
420 return GNUNET_NO;
421 }
422 if (GNUNET_YES == is_readable)
423 ret = access (fil, R_OK | X_OK);
424 else
425 ret = access (fil, X_OK);
426 if (ret < 0)
427 {
428 LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_WARNING, "access", fil);
429 return GNUNET_NO;
430 }
431 return GNUNET_YES;
432}
433
434
435/**
436 * Check if fil can be accessed using amode.
437 *
438 * @param fil file to check for
439 * @param amode access mode
440 * @returns GNUnet error code
441 */
442static enum GNUNET_GenericReturnValue
443file_test_internal (const char *fil, int amode)
444{
445 struct stat filestat;
446 int ret;
447 char *rdir;
448
449 rdir = GNUNET_STRINGS_filename_expand (fil);
450 if (rdir == NULL)
451 return GNUNET_SYSERR;
452
453 ret = stat (rdir, &filestat);
454 if (0 != ret)
455 {
456 if (errno != ENOENT)
457 {
458 LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_DEBUG, "stat", rdir);
459 GNUNET_free (rdir);
460 return GNUNET_SYSERR;
461 }
462 GNUNET_free (rdir);
463 return GNUNET_NO;
464 }
465 if (! S_ISREG (filestat.st_mode))
466 {
467 GNUNET_free (rdir);
468 return GNUNET_NO;
469 }
470 if (access (rdir, amode) < 0)
471 {
472 LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_DEBUG, "access", rdir);
473 GNUNET_free (rdir);
474 return GNUNET_SYSERR;
475 }
476 GNUNET_free (rdir);
477 return GNUNET_YES;
478}
479
480
481enum GNUNET_GenericReturnValue
482GNUNET_DISK_file_test (const char *fil)
483{
484 return file_test_internal (fil, F_OK);
485}
486
487
488enum GNUNET_GenericReturnValue
489GNUNET_DISK_file_test_read (const char *fil)
490{
491 return file_test_internal (fil, R_OK);
492}
493
494
495enum GNUNET_GenericReturnValue
496GNUNET_DISK_directory_create (const char *dir)
497{
498 char *rdir;
499 unsigned int len;
500 unsigned int pos;
501 unsigned int pos2;
502 int ret = GNUNET_OK;
503
504 rdir = GNUNET_STRINGS_filename_expand (dir);
505 if (rdir == NULL)
506 {
507 GNUNET_break (0);
508 return GNUNET_SYSERR;
509 }
510
511 len = strlen (rdir);
512
513 pos = 1; /* skip heading '/' */
514
515 /* Check which low level directories already exist */
516 pos2 = len;
517 rdir[len] = DIR_SEPARATOR;
518 while (pos <= pos2)
519 {
520 if (DIR_SEPARATOR == rdir[pos2])
521 {
522 rdir[pos2] = '\0';
523 ret = GNUNET_DISK_directory_test (rdir, GNUNET_NO);
524 if (GNUNET_NO == ret)
525 {
526 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
527 "Creating directory `%s' failed",
528 rdir);
529 GNUNET_free (rdir);
530 return GNUNET_SYSERR;
531 }
532 rdir[pos2] = DIR_SEPARATOR;
533 if (GNUNET_YES == ret)
534 {
535 pos2++;
536 break;
537 }
538 }
539 pos2--;
540 }
541 rdir[len] = '\0';
542 if (pos < pos2)
543 pos = pos2;
544 /* Start creating directories */
545 while (pos <= len)
546 {
547 if ((rdir[pos] == DIR_SEPARATOR) || (pos == len))
548 {
549 rdir[pos] = '\0';
550 ret = GNUNET_DISK_directory_test (rdir, GNUNET_NO);
551 if (GNUNET_NO == ret)
552 {
553 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
554 "Creating directory `%s' failed",
555 rdir);
556 GNUNET_free (rdir);
557 return GNUNET_SYSERR;
558 }
559 if (GNUNET_SYSERR == ret)
560 {
561 ret = mkdir (rdir,
562 S_IRUSR | S_IWUSR | S_IXUSR | S_IRGRP | S_IXGRP | S_IROTH
563 | S_IXOTH); /* 755 */
564
565 if ((ret != 0) && (errno != EEXIST))
566 {
567 LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_ERROR, "mkdir", rdir);
568 GNUNET_free (rdir);
569 return GNUNET_SYSERR;
570 }
571 }
572 rdir[pos] = DIR_SEPARATOR;
573 }
574 pos++;
575 }
576 GNUNET_free (rdir);
577 return GNUNET_OK;
578}
579
580
581enum GNUNET_GenericReturnValue
582GNUNET_DISK_directory_create_for_file (const char *filename)
583{
584 char *rdir;
585 size_t len;
586 int eno;
587 enum GNUNET_GenericReturnValue res;
588
589 rdir = GNUNET_STRINGS_filename_expand (filename);
590 if (NULL == rdir)
591 {
592 errno = EINVAL;
593 return GNUNET_SYSERR;
594 }
595 if (0 == access (rdir, W_OK))
596 {
597 GNUNET_free (rdir);
598 return GNUNET_OK;
599 }
600 len = strlen (rdir);
601 while ((len > 0) && (rdir[len] != DIR_SEPARATOR))
602 len--;
603 rdir[len] = '\0';
604 /* The empty path is invalid and in this case refers to / */
605 if (0 == len)
606 {
607 GNUNET_free (rdir);
608 rdir = GNUNET_strdup ("/");
609 }
610 res = GNUNET_DISK_directory_create (rdir);
611 if ( (GNUNET_OK == res) &&
612 (0 != access (rdir, W_OK)) )
613 res = GNUNET_NO;
614 eno = errno;
615 GNUNET_free (rdir);
616 errno = eno;
617 return res;
618}
619
620
621ssize_t
622GNUNET_DISK_file_read (const struct GNUNET_DISK_FileHandle *h,
623 void *result,
624 size_t len)
625{
626 if (NULL == h)
627 {
628 errno = EINVAL;
629 return GNUNET_SYSERR;
630 }
631 return read (h->fd, result, len);
632}
633
634
635ssize_t
636GNUNET_DISK_file_read_non_blocking (const struct GNUNET_DISK_FileHandle *h,
637 void *result,
638 size_t len)
639{
640 int flags;
641 ssize_t ret;
642
643 if (NULL == h)
644 {
645 errno = EINVAL;
646 return GNUNET_SYSERR;
647 }
648 /* set to non-blocking, read, then set back */
649 flags = fcntl (h->fd, F_GETFL);
650 if (0 == (flags & O_NONBLOCK))
651 (void) fcntl (h->fd, F_SETFL, flags | O_NONBLOCK);
652 ret = read (h->fd, result, len);
653 if (0 == (flags & O_NONBLOCK))
654 {
655 int eno = errno;
656 (void) fcntl (h->fd, F_SETFL, flags);
657 errno = eno;
658 }
659 return ret;
660}
661
662
663ssize_t
664GNUNET_DISK_fn_read (const char *fn,
665 void *result,
666 size_t len)
667{
668 struct GNUNET_DISK_FileHandle *fh;
669 ssize_t ret;
670 int eno;
671
672 fh = GNUNET_DISK_file_open (fn,
673 GNUNET_DISK_OPEN_READ,
674 GNUNET_DISK_PERM_NONE);
675 if (NULL == fh)
676 return GNUNET_SYSERR;
677 ret = GNUNET_DISK_file_read (fh, result, len);
678 eno = errno;
679 GNUNET_assert (GNUNET_OK == GNUNET_DISK_file_close (fh));
680 errno = eno;
681 return ret;
682}
683
684
685ssize_t
686GNUNET_DISK_file_write (const struct GNUNET_DISK_FileHandle *h,
687 const void *buffer,
688 size_t n)
689{
690 if (NULL == h)
691 {
692 errno = EINVAL;
693 return GNUNET_SYSERR;
694 }
695
696 return write (h->fd, buffer, n);
697}
698
699
700ssize_t
701GNUNET_DISK_file_write_blocking (const struct GNUNET_DISK_FileHandle *h,
702 const void *buffer,
703 size_t n)
704{
705 int flags;
706 ssize_t ret;
707
708 if (NULL == h)
709 {
710 errno = EINVAL;
711 return GNUNET_SYSERR;
712 }
713 /* set to blocking, write, then set back */
714 flags = fcntl (h->fd, F_GETFL);
715 if (0 != (flags & O_NONBLOCK))
716 (void) fcntl (h->fd, F_SETFL, flags - O_NONBLOCK);
717 ret = write (h->fd, buffer, n);
718 if (0 == (flags & O_NONBLOCK))
719 (void) fcntl (h->fd, F_SETFL, flags);
720 return ret;
721}
722
723
724enum GNUNET_GenericReturnValue
725GNUNET_DISK_fn_write (const char *fn,
726 const void *buf,
727 size_t buf_size,
728 enum GNUNET_DISK_AccessPermissions mode)
729{
730 char *tmpl;
731 int fd;
732
733 if (GNUNET_OK !=
734 GNUNET_DISK_directory_create_for_file (fn))
735 {
736 GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING,
737 "mkstemp",
738 fn);
739 return GNUNET_SYSERR;
740 }
741 {
742 char *dname;
743
744 dname = GNUNET_strdup (fn);
745 GNUNET_asprintf (&tmpl,
746 "%s/XXXXXX",
747 dirname (dname));
748 GNUNET_free (dname);
749 }
750 fd = mkstemp (tmpl);
751 if (-1 == fd)
752 {
753 GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING,
754 "mkstemp",
755 tmpl);
756 GNUNET_free (tmpl);
757 return GNUNET_SYSERR;
758 }
759
760 if (0 != fchmod (fd,
761 translate_unix_perms (mode)))
762 {
763 GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING,
764 "chmod",
765 tmpl);
766 GNUNET_assert (0 == close (fd));
767 if (0 != unlink (tmpl))
768 GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_ERROR,
769 "unlink",
770 tmpl);
771 GNUNET_free (tmpl);
772 return GNUNET_SYSERR;
773 }
774 if (buf_size !=
775 write (fd,
776 buf,
777 buf_size))
778 {
779 GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING,
780 "write",
781 tmpl);
782 GNUNET_assert (0 == close (fd));
783 if (0 != unlink (tmpl))
784 GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_ERROR,
785 "unlink",
786 tmpl);
787 GNUNET_free (tmpl);
788 return GNUNET_SYSERR;
789 }
790 GNUNET_assert (0 == close (fd));
791
792 if (0 != link (tmpl,
793 fn))
794 {
795 if (0 != unlink (tmpl))
796 GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_ERROR,
797 "unlink",
798 tmpl);
799 GNUNET_free (tmpl);
800 return GNUNET_NO;
801 }
802 if (0 != unlink (tmpl))
803 GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_ERROR,
804 "unlink",
805 tmpl);
806 GNUNET_free (tmpl);
807 return GNUNET_OK;
808
809
810}
811
812
813int
814GNUNET_DISK_directory_scan (const char *dir_name,
815 GNUNET_FileNameCallback callback,
816 void *callback_cls)
817{
818 DIR *dinfo;
819 struct dirent *finfo;
820 struct stat istat;
821 int count = 0;
822 enum GNUNET_GenericReturnValue ret;
823 char *name;
824 char *dname;
825 unsigned int name_len;
826 unsigned int n_size;
827
828 GNUNET_assert (NULL != dir_name);
829 dname = GNUNET_STRINGS_filename_expand (dir_name);
830 if (NULL == dname)
831 return GNUNET_SYSERR;
832 while ((strlen (dname) > 0) && (dname[strlen (dname) - 1] == DIR_SEPARATOR))
833 dname[strlen (dname) - 1] = '\0';
834 if (0 != stat (dname, &istat))
835 {
836 LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_WARNING, "stat", dname);
837 GNUNET_free (dname);
838 return GNUNET_SYSERR;
839 }
840 if (! S_ISDIR (istat.st_mode))
841 {
842 LOG (GNUNET_ERROR_TYPE_WARNING,
843 _ ("Expected `%s' to be a directory!\n"),
844 dir_name);
845 GNUNET_free (dname);
846 return GNUNET_SYSERR;
847 }
848 errno = 0;
849 dinfo = opendir (dname);
850 if ((EACCES == errno) || (NULL == dinfo))
851 {
852 LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_WARNING, "opendir", dname);
853 if (NULL != dinfo)
854 closedir (dinfo);
855 GNUNET_free (dname);
856 return GNUNET_SYSERR;
857 }
858 name_len = 256;
859 n_size = strlen (dname) + name_len + strlen (DIR_SEPARATOR_STR) + 1;
860 name = GNUNET_malloc (n_size);
861 while (NULL != (finfo = readdir (dinfo)))
862 {
863 if ((0 == strcmp (finfo->d_name, ".")) ||
864 (0 == strcmp (finfo->d_name, "..")))
865 continue;
866 if (NULL != callback)
867 {
868 if (name_len < strlen (finfo->d_name))
869 {
870 GNUNET_free (name);
871 name_len = strlen (finfo->d_name);
872 n_size = strlen (dname) + name_len + strlen (DIR_SEPARATOR_STR) + 1;
873 name = GNUNET_malloc (n_size);
874 }
875 /* dname can end in "/" only if dname == "/";
876 * if dname does not end in "/", we need to add
877 * a "/" (otherwise, we must not!) */
878 GNUNET_snprintf (name,
879 n_size,
880 "%s%s%s",
881 dname,
882 (0 == strcmp (dname, DIR_SEPARATOR_STR))
883 ? ""
884 : DIR_SEPARATOR_STR,
885 finfo->d_name);
886 ret = callback (callback_cls, name);
887 if (GNUNET_OK != ret)
888 {
889 closedir (dinfo);
890 GNUNET_free (name);
891 GNUNET_free (dname);
892 if (GNUNET_NO == ret)
893 return count;
894 return GNUNET_SYSERR;
895 }
896 }
897 count++;
898 }
899 closedir (dinfo);
900 GNUNET_free (name);
901 GNUNET_free (dname);
902 return count;
903}
904
905
906/**
907 * Check for a simple wildcard match.
908 * Only asterisks are allowed.
909 * Asterisks match everything, including slashes.
910 *
911 * @param pattern pattern with wildcards
912 * @param str string to match against
913 * @returns true on match, false otherwise
914 */
915static bool
916glob_match (const char *pattern, const char *str)
917{
918 /* Position in the input string */
919 const char *str_pos = str;
920 /* Position in the pattern */
921 const char *pat_pos = pattern;
922 /* Backtrack position in string */
923 const char *str_bt = NULL;
924 /* Backtrack position in pattern */
925 const char *pat_bt = NULL;
926
927 for (;;)
928 {
929 if (*pat_pos == '*')
930 {
931 str_bt = str_pos;
932 pat_bt = pat_pos++;
933 }
934 else if (*pat_pos == *str_pos)
935 {
936 if ('\0' == *pat_pos)
937 return true;
938 str_pos++;
939 pat_pos++;
940 }
941 else
942 {
943 if (NULL == str_bt)
944 return false;
945 /* Backtrack to match one more
946 character as part of the asterisk. */
947 str_pos = str_bt + 1;
948 if ('\0' == *str_pos)
949 return false;
950 pat_pos = pat_bt;
951 }
952 }
953}
954
955
956struct GlobClosure
957{
958 const char *glob;
959 GNUNET_FileNameCallback cb;
960 void *cls;
961
962 /**
963 * Number of files that actually matched the glob pattern.
964 */
965 int nres;
966};
967
968/**
969 * Function called with a filename.
970 *
971 * @param cls closure
972 * @param filename complete filename (absolute path)
973 * @return #GNUNET_OK to continue to iterate,
974 * #GNUNET_NO to stop iteration with no error,
975 * #GNUNET_SYSERR to abort iteration with error!
976 */
977static enum GNUNET_GenericReturnValue
978glob_cb (void *cls,
979 const char *filename)
980{
981 struct GlobClosure *gc = cls;
982 const char *fn;
983
984 fn = strrchr (filename, DIR_SEPARATOR);
985 fn = (NULL == fn) ? filename : (fn + 1);
986
987 LOG (GNUNET_ERROR_TYPE_DEBUG,
988 "checking glob '%s' against '%s'\n",
989 gc->glob,
990 fn);
991
992 if (glob_match (gc->glob, fn))
993 {
994 enum GNUNET_GenericReturnValue cbret;
995
996 LOG (GNUNET_ERROR_TYPE_DEBUG,
997 "found glob match '%s'\n",
998 filename);
999 gc->nres++;
1000 cbret = gc->cb (gc->cls, filename);
1001 if (GNUNET_OK != cbret)
1002 return cbret;
1003 }
1004 return GNUNET_OK;
1005}
1006
1007
1008int
1009GNUNET_DISK_glob (const char *glob_pattern,
1010 GNUNET_FileNameCallback callback,
1011 void *callback_cls)
1012{
1013 char *mypat = GNUNET_strdup (glob_pattern);
1014 char *sep;
1015 int ret;
1016
1017 if ( (NULL != strrchr (glob_pattern, '+')) ||
1018 (NULL != strrchr (glob_pattern, '[')) ||
1019 (NULL != strrchr (glob_pattern, '+')) ||
1020 (NULL != strrchr (glob_pattern, '~')) )
1021 {
1022 LOG (GNUNET_ERROR_TYPE_ERROR,
1023 "unsupported glob pattern: '%s'\n",
1024 glob_pattern);
1025 GNUNET_free (mypat);
1026 return -1;
1027 }
1028
1029 sep = strrchr (mypat, DIR_SEPARATOR);
1030 if (NULL == sep)
1031 {
1032 GNUNET_free (mypat);
1033 return -1;
1034 }
1035
1036 *sep = '\0';
1037
1038 if (NULL != strchr (mypat, '*'))
1039 {
1040 GNUNET_free (mypat);
1041 GNUNET_break (0);
1042 LOG (GNUNET_ERROR_TYPE_ERROR,
1043 "glob pattern may only contain '*' in the final path component\n");
1044 return -1;
1045 }
1046
1047 {
1048 struct GlobClosure gc = {
1049 .glob = sep + 1,
1050 .cb = callback,
1051 .cls = callback_cls,
1052 .nres = 0,
1053 };
1054 LOG (GNUNET_ERROR_TYPE_DEBUG,
1055 "scanning directory '%s' for glob matches on '%s'\n",
1056 mypat,
1057 gc.glob);
1058 ret = GNUNET_DISK_directory_scan (mypat,
1059 glob_cb,
1060 &gc
1061 );
1062 GNUNET_free (mypat);
1063 return (ret < 0) ? ret : gc.nres;
1064 }
1065}
1066
1067
1068/**
1069 * Function that removes the given directory by calling
1070 * #GNUNET_DISK_directory_remove().
1071 *
1072 * @param unused not used
1073 * @param fn directory to remove
1074 * @return #GNUNET_OK
1075 */
1076static enum GNUNET_GenericReturnValue
1077remove_helper (void *unused,
1078 const char *fn)
1079{
1080 (void) unused;
1081 (void) GNUNET_DISK_directory_remove (fn);
1082 return GNUNET_OK;
1083}
1084
1085
1086enum GNUNET_GenericReturnValue
1087GNUNET_DISK_directory_remove (const char *filename)
1088{
1089 struct stat istat;
1090
1091 if (NULL == filename)
1092 {
1093 GNUNET_break (0);
1094 return GNUNET_SYSERR;
1095 }
1096 if (0 != lstat (filename, &istat))
1097 return GNUNET_NO; /* file may not exist... */
1098 (void) chmod (filename,
1099 S_IWUSR | S_IRUSR | S_IXUSR);
1100 if (0 == unlink (filename))
1101 return GNUNET_OK;
1102 if ( (errno != EISDIR) &&
1103 /* EISDIR is not sufficient in all cases, e.g.
1104 * sticky /tmp directory may result in EPERM on BSD.
1105 * So we also explicitly check "isDirectory" */
1106 (GNUNET_YES !=
1107 GNUNET_DISK_directory_test (filename,
1108 GNUNET_YES)) )
1109 {
1110 LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_WARNING, "rmdir", filename);
1111 return GNUNET_SYSERR;
1112 }
1113 if (GNUNET_SYSERR ==
1114 GNUNET_DISK_directory_scan (filename, &remove_helper, NULL))
1115 return GNUNET_SYSERR;
1116 if (0 != rmdir (filename))
1117 {
1118 LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_WARNING, "rmdir", filename);
1119 return GNUNET_SYSERR;
1120 }
1121 return GNUNET_OK;
1122}
1123
1124
1125enum GNUNET_GenericReturnValue
1126GNUNET_DISK_file_copy (const char *src,
1127 const char *dst)
1128{
1129 char *buf;
1130 uint64_t pos;
1131 uint64_t size;
1132 size_t len;
1133 ssize_t sret;
1134 struct GNUNET_DISK_FileHandle *in;
1135 struct GNUNET_DISK_FileHandle *out;
1136
1137 if (GNUNET_OK != GNUNET_DISK_file_size (src, &size, GNUNET_YES, GNUNET_YES))
1138 {
1139 GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_ERROR, "stat", src);
1140 return GNUNET_SYSERR;
1141 }
1142 pos = 0;
1143 in =
1144 GNUNET_DISK_file_open (src, GNUNET_DISK_OPEN_READ, GNUNET_DISK_PERM_NONE);
1145 if (! in)
1146 {
1147 GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_ERROR, "open", src);
1148 return GNUNET_SYSERR;
1149 }
1150 out =
1151 GNUNET_DISK_file_open (dst,
1152 GNUNET_DISK_OPEN_WRITE | GNUNET_DISK_OPEN_CREATE
1153 | GNUNET_DISK_OPEN_FAILIFEXISTS,
1154 GNUNET_DISK_PERM_USER_READ
1155 | GNUNET_DISK_PERM_USER_WRITE
1156 | GNUNET_DISK_PERM_GROUP_READ
1157 | GNUNET_DISK_PERM_GROUP_WRITE);
1158 if (! out)
1159 {
1160 GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_ERROR, "open", dst);
1161 GNUNET_DISK_file_close (in);
1162 return GNUNET_SYSERR;
1163 }
1164 buf = GNUNET_malloc (COPY_BLK_SIZE);
1165 while (pos < size)
1166 {
1167 len = COPY_BLK_SIZE;
1168 if (len > size - pos)
1169 len = size - pos;
1170 sret = GNUNET_DISK_file_read (in, buf, len);
1171 if ((sret < 0) || (len != (size_t) sret))
1172 goto FAIL;
1173 sret = GNUNET_DISK_file_write (out, buf, len);
1174 if ((sret < 0) || (len != (size_t) sret))
1175 goto FAIL;
1176 pos += len;
1177 }
1178 GNUNET_free (buf);
1179 GNUNET_DISK_file_close (in);
1180 GNUNET_DISK_file_close (out);
1181 return GNUNET_OK;
1182 FAIL:
1183 GNUNET_free (buf);
1184 GNUNET_DISK_file_close (in);
1185 GNUNET_DISK_file_close (out);
1186 return GNUNET_SYSERR;
1187}
1188
1189
1190void
1191GNUNET_DISK_filename_canonicalize (char *fn)
1192{
1193 char *idx;
1194 char c;
1195
1196 for (idx = fn; *idx; idx++)
1197 {
1198 c = *idx;
1199
1200 if ((c == '/') || (c == '\\') || (c == ':') || (c == '*') || (c == '?') ||
1201 (c ==
1202 '"')
1203 ||
1204 (c == '<') || (c == '>') || (c == '|') )
1205 {
1206 *idx = '_';
1207 }
1208 }
1209}
1210
1211
1212enum GNUNET_GenericReturnValue
1213GNUNET_DISK_file_change_owner (const char *filename,
1214 const char *user)
1215{
1216 struct passwd *pws;
1217
1218 pws = getpwnam (user);
1219 if (NULL == pws)
1220 {
1221 LOG (GNUNET_ERROR_TYPE_ERROR,
1222 _ ("Cannot obtain information about user `%s': %s\n"),
1223 user,
1224 strerror (errno));
1225 return GNUNET_SYSERR;
1226 }
1227 if (0 != chown (filename, pws->pw_uid, pws->pw_gid))
1228 {
1229 LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_WARNING, "chown", filename);
1230 return GNUNET_SYSERR;
1231 }
1232 return GNUNET_OK;
1233}
1234
1235
1236struct GNUNET_DISK_FileHandle *
1237GNUNET_DISK_file_open (const char *fn,
1238 enum GNUNET_DISK_OpenFlags flags,
1239 enum GNUNET_DISK_AccessPermissions perm)
1240{
1241 char *expfn;
1242 struct GNUNET_DISK_FileHandle *ret;
1243
1244 int oflags;
1245 int mode;
1246 int fd;
1247
1248 expfn = GNUNET_STRINGS_filename_expand (fn);
1249 if (NULL == expfn)
1250 return NULL;
1251
1252 mode = 0;
1253 if (GNUNET_DISK_OPEN_READWRITE == (flags & GNUNET_DISK_OPEN_READWRITE))
1254 oflags = O_RDWR; /* note: O_RDWR is NOT always O_RDONLY | O_WRONLY */
1255 else if (flags & GNUNET_DISK_OPEN_READ)
1256 oflags = O_RDONLY;
1257 else if (flags & GNUNET_DISK_OPEN_WRITE)
1258 oflags = O_WRONLY;
1259 else
1260 {
1261 GNUNET_break (0);
1262 GNUNET_free (expfn);
1263 return NULL;
1264 }
1265 if (flags & GNUNET_DISK_OPEN_FAILIFEXISTS)
1266 oflags |= (O_CREAT | O_EXCL);
1267 if (flags & GNUNET_DISK_OPEN_TRUNCATE)
1268 oflags |= O_TRUNC;
1269 if (flags & GNUNET_DISK_OPEN_APPEND)
1270 oflags |= O_APPEND;
1271 if (GNUNET_NO == GNUNET_DISK_file_test (fn))
1272 {
1273 if (flags & GNUNET_DISK_OPEN_CREATE)
1274 {
1275 (void) GNUNET_DISK_directory_create_for_file (expfn);
1276 oflags |= O_CREAT;
1277 mode = translate_unix_perms (perm);
1278 }
1279 }
1280
1281 fd = open (expfn,
1282 oflags
1283#if O_CLOEXEC
1284 | O_CLOEXEC
1285#endif
1286 | O_LARGEFILE,
1287 mode);
1288 if (fd == -1)
1289 {
1290 if (0 == (flags & GNUNET_DISK_OPEN_FAILIFEXISTS))
1291 LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_WARNING, "open", expfn);
1292 else
1293 LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_DEBUG, "open", expfn);
1294 GNUNET_free (expfn);
1295 return NULL;
1296 }
1297
1298 ret = GNUNET_new (struct GNUNET_DISK_FileHandle);
1299
1300 ret->fd = fd;
1301
1302 GNUNET_free (expfn);
1303 return ret;
1304}
1305
1306
1307enum GNUNET_GenericReturnValue
1308GNUNET_DISK_file_close (struct GNUNET_DISK_FileHandle *h)
1309{
1310 enum GNUNET_GenericReturnValue ret;
1311
1312 if (NULL == h)
1313 {
1314 errno = EINVAL;
1315 return GNUNET_SYSERR;
1316 }
1317
1318 ret = GNUNET_OK;
1319 if (0 != close (h->fd))
1320 {
1321 LOG_STRERROR (GNUNET_ERROR_TYPE_WARNING, "close");
1322 ret = GNUNET_SYSERR;
1323 }
1324 GNUNET_free (h);
1325 return ret;
1326}
1327
1328
1329struct GNUNET_DISK_FileHandle *
1330GNUNET_DISK_get_handle_from_int_fd (int fno)
1331{
1332 struct GNUNET_DISK_FileHandle *fh;
1333
1334 if ((((off_t) -1) == lseek (fno, 0, SEEK_CUR)) && (EBADF == errno))
1335 return NULL; /* invalid FD */
1336
1337 fh = GNUNET_new (struct GNUNET_DISK_FileHandle);
1338
1339 fh->fd = fno;
1340
1341 return fh;
1342}
1343
1344
1345struct GNUNET_DISK_FileHandle *
1346GNUNET_DISK_get_handle_from_native (FILE *fd)
1347{
1348 int fno;
1349
1350 fno = fileno (fd);
1351 if (-1 == fno)
1352 return NULL;
1353 return GNUNET_DISK_get_handle_from_int_fd (fno);
1354}
1355
1356
1357/**
1358 * Handle for a memory-mapping operation.
1359 */
1360struct GNUNET_DISK_MapHandle
1361{
1362 /**
1363 * Address where the map is in memory.
1364 */
1365 void *addr;
1366
1367 /**
1368 * Number of bytes mapped.
1369 */
1370 size_t len;
1371};
1372
1373
1374#ifndef MAP_FAILED
1375#define MAP_FAILED ((void *) -1)
1376#endif
1377
1378
1379void *
1380GNUNET_DISK_file_map (const struct GNUNET_DISK_FileHandle *h,
1381 struct GNUNET_DISK_MapHandle **m,
1382 enum GNUNET_DISK_MapType access,
1383 size_t len)
1384{
1385 int prot;
1386
1387 if (NULL == h)
1388 {
1389 errno = EINVAL;
1390 return NULL;
1391 }
1392 prot = 0;
1393 if (access & GNUNET_DISK_MAP_TYPE_READ)
1394 prot = PROT_READ;
1395 if (access & GNUNET_DISK_MAP_TYPE_WRITE)
1396 prot |= PROT_WRITE;
1397 *m = GNUNET_new (struct GNUNET_DISK_MapHandle);
1398 (*m)->addr = mmap (NULL, len, prot, MAP_SHARED, h->fd, 0);
1399 GNUNET_assert (NULL != (*m)->addr);
1400 if (MAP_FAILED == (*m)->addr)
1401 {
1402 GNUNET_free (*m);
1403 return NULL;
1404 }
1405 (*m)->len = len;
1406 return (*m)->addr;
1407}
1408
1409
1410enum GNUNET_GenericReturnValue
1411GNUNET_DISK_file_unmap (struct GNUNET_DISK_MapHandle *h)
1412{
1413 enum GNUNET_GenericReturnValue ret;
1414
1415 if (NULL == h)
1416 {
1417 errno = EINVAL;
1418 return GNUNET_SYSERR;
1419 }
1420 ret = munmap (h->addr, h->len) != -1 ? GNUNET_OK : GNUNET_SYSERR;
1421 GNUNET_free (h);
1422 return ret;
1423}
1424
1425
1426enum GNUNET_GenericReturnValue
1427GNUNET_DISK_file_sync (const struct GNUNET_DISK_FileHandle *h)
1428{
1429 if (h == NULL)
1430 {
1431 errno = EINVAL;
1432 return GNUNET_SYSERR;
1433 }
1434
1435#if ! defined(__linux__) || ! defined(GNU)
1436 return fsync (h->fd) == -1 ? GNUNET_SYSERR : GNUNET_OK;
1437#else
1438 return fdatasync (h->fd) == -1 ? GNUNET_SYSERR : GNUNET_OK;
1439#endif
1440}
1441
1442
1443struct GNUNET_DISK_PipeHandle *
1444GNUNET_DISK_pipe (enum GNUNET_DISK_PipeFlags pf)
1445{
1446 int fd[2];
1447
1448 if (-1 == pipe (fd))
1449 {
1450 int eno = errno;
1451
1452 LOG_STRERROR (GNUNET_ERROR_TYPE_ERROR, "pipe");
1453 errno = eno;
1454 return NULL;
1455 }
1456 return GNUNET_DISK_pipe_from_fd (pf, fd);
1457}
1458
1459
1460struct GNUNET_DISK_PipeHandle *
1461GNUNET_DISK_pipe_from_fd (enum GNUNET_DISK_PipeFlags pf,
1462 int fd[2])
1463{
1464 struct GNUNET_DISK_PipeHandle *p;
1465 int ret = 0;
1466 int flags;
1467 int eno = 0; /* make gcc happy */
1468
1469 p = GNUNET_new (struct GNUNET_DISK_PipeHandle);
1470 if (fd[0] >= 0)
1471 {
1472 p->fd[0] = GNUNET_new (struct GNUNET_DISK_FileHandle);
1473 p->fd[0]->fd = fd[0];
1474 if (0 == (GNUNET_DISK_PF_BLOCKING_READ & pf))
1475 {
1476 flags = fcntl (fd[0], F_GETFL);
1477 flags |= O_NONBLOCK;
1478 if (0 > fcntl (fd[0], F_SETFL, flags))
1479 {
1480 ret = -1;
1481 eno = errno;
1482 }
1483 }
1484 flags = fcntl (fd[0], F_GETFD);
1485 flags |= FD_CLOEXEC;
1486 if (0 > fcntl (fd[0], F_SETFD, flags))
1487 {
1488 ret = -1;
1489 eno = errno;
1490 }
1491 }
1492
1493 if (fd[1] >= 0)
1494 {
1495 p->fd[1] = GNUNET_new (struct GNUNET_DISK_FileHandle);
1496 p->fd[1]->fd = fd[1];
1497 if (0 == (GNUNET_DISK_PF_BLOCKING_WRITE & pf))
1498 {
1499 flags = fcntl (fd[1], F_GETFL);
1500 flags |= O_NONBLOCK;
1501 if (0 > fcntl (fd[1], F_SETFL, flags))
1502 {
1503 ret = -1;
1504 eno = errno;
1505 }
1506 }
1507 flags = fcntl (fd[1], F_GETFD);
1508 flags |= FD_CLOEXEC;
1509 if (0 > fcntl (fd[1], F_SETFD, flags))
1510 {
1511 ret = -1;
1512 eno = errno;
1513 }
1514 }
1515 if (ret == -1)
1516 {
1517 errno = eno;
1518 LOG_STRERROR (GNUNET_ERROR_TYPE_ERROR, "fcntl");
1519 if (p->fd[0]->fd >= 0)
1520 GNUNET_break (0 == close (p->fd[0]->fd));
1521 if (p->fd[1]->fd >= 0)
1522 GNUNET_break (0 == close (p->fd[1]->fd));
1523 GNUNET_free (p->fd[0]);
1524 GNUNET_free (p->fd[1]);
1525 GNUNET_free (p);
1526 errno = eno;
1527 return NULL;
1528 }
1529 return p;
1530}
1531
1532
1533enum GNUNET_GenericReturnValue
1534GNUNET_DISK_pipe_close_end (struct GNUNET_DISK_PipeHandle *p,
1535 enum GNUNET_DISK_PipeEnd end)
1536{
1537 enum GNUNET_GenericReturnValue ret = GNUNET_OK;
1538
1539 if (end == GNUNET_DISK_PIPE_END_READ)
1540 {
1541 if (p->fd[0])
1542 {
1543 ret = GNUNET_DISK_file_close (p->fd[0]);
1544 p->fd[0] = NULL;
1545 }
1546 }
1547 else if (end == GNUNET_DISK_PIPE_END_WRITE)
1548 {
1549 if (p->fd[1])
1550 {
1551 ret = GNUNET_DISK_file_close (p->fd[1]);
1552 p->fd[1] = NULL;
1553 }
1554 }
1555 return ret;
1556}
1557
1558
1559struct GNUNET_DISK_FileHandle *
1560GNUNET_DISK_pipe_detach_end (struct GNUNET_DISK_PipeHandle *p,
1561 enum GNUNET_DISK_PipeEnd end)
1562{
1563 struct GNUNET_DISK_FileHandle *ret = NULL;
1564
1565 if (end == GNUNET_DISK_PIPE_END_READ)
1566 {
1567 if (p->fd[0])
1568 {
1569 ret = p->fd[0];
1570 p->fd[0] = NULL;
1571 }
1572 }
1573 else if (end == GNUNET_DISK_PIPE_END_WRITE)
1574 {
1575 if (p->fd[1])
1576 {
1577 ret = p->fd[1];
1578 p->fd[1] = NULL;
1579 }
1580 }
1581
1582 return ret;
1583}
1584
1585
1586enum GNUNET_GenericReturnValue
1587GNUNET_DISK_pipe_close (struct GNUNET_DISK_PipeHandle *p)
1588{
1589 enum GNUNET_GenericReturnValue ret = GNUNET_OK;
1590 enum GNUNET_GenericReturnValue read_end_close;
1591 enum GNUNET_GenericReturnValue write_end_close;
1592 int read_end_close_errno;
1593 int write_end_close_errno;
1594
1595 read_end_close = GNUNET_DISK_pipe_close_end (p, GNUNET_DISK_PIPE_END_READ);
1596 read_end_close_errno = errno;
1597 write_end_close = GNUNET_DISK_pipe_close_end (p, GNUNET_DISK_PIPE_END_WRITE);
1598 write_end_close_errno = errno;
1599 GNUNET_free (p);
1600
1601 if (GNUNET_OK != read_end_close)
1602 {
1603 errno = read_end_close_errno;
1604 ret = read_end_close;
1605 }
1606 else if (GNUNET_OK != write_end_close)
1607 {
1608 errno = write_end_close_errno;
1609 ret = write_end_close;
1610 }
1611
1612 return ret;
1613}
1614
1615
1616const struct GNUNET_DISK_FileHandle *
1617GNUNET_DISK_pipe_handle (const struct GNUNET_DISK_PipeHandle *p,
1618 enum GNUNET_DISK_PipeEnd n)
1619{
1620 switch (n)
1621 {
1622 case GNUNET_DISK_PIPE_END_READ:
1623 case GNUNET_DISK_PIPE_END_WRITE:
1624 return p->fd[n];
1625
1626 default:
1627 GNUNET_break (0);
1628 return NULL;
1629 }
1630}
1631
1632
1633enum GNUNET_GenericReturnValue
1634GNUNET_DISK_internal_file_handle_ (const struct GNUNET_DISK_FileHandle *fh,
1635 void *dst,
1636 size_t dst_len)
1637{
1638 if (NULL == fh)
1639 return GNUNET_SYSERR;
1640 if (dst_len < sizeof(int))
1641 return GNUNET_SYSERR;
1642 *((int *) dst) = fh->fd;
1643 return GNUNET_OK;
1644}
1645
1646
1647/**
1648 * Helper function for #GNUNET_DISK_purge_cfg_dir.
1649 *
1650 * @param cls a `const char *` with the option to purge
1651 * @param cfg our configuration
1652 * @return #GNUNET_OK on success
1653 */
1654static enum GNUNET_GenericReturnValue
1655purge_cfg_dir (void *cls,
1656 const struct GNUNET_CONFIGURATION_Handle *cfg)
1657{
1658 const char *option = cls;
1659 char *tmpname;
1660
1661 if (GNUNET_OK !=
1662 GNUNET_CONFIGURATION_get_value_filename (cfg, "PATHS", option, &tmpname))
1663 {
1664 GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR, "PATHS", option);
1665 return GNUNET_NO;
1666 }
1667 if (GNUNET_SYSERR == GNUNET_DISK_directory_remove (tmpname))
1668 {
1669 GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_ERROR, "remove", tmpname);
1670 GNUNET_free (tmpname);
1671 return GNUNET_OK;
1672 }
1673 GNUNET_free (tmpname);
1674 return GNUNET_OK;
1675}
1676
1677
1678void
1679GNUNET_DISK_purge_cfg_dir (const char *cfg_filename,
1680 const char *option)
1681{
1682 GNUNET_break (GNUNET_OK ==
1683 GNUNET_CONFIGURATION_parse_and_run (cfg_filename,
1684 &purge_cfg_dir,
1685 (void *) option));
1686}
1687
1688
1689/* end of disk.c */
diff --git a/src/lib/util/disk.h b/src/lib/util/disk.h
new file mode 100644
index 000000000..bca398e26
--- /dev/null
+++ b/src/lib/util/disk.h
@@ -0,0 +1,44 @@
1/*
2 This file is part of GNUnet.
3 Copyright (C) 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/**
22 * @file util/disk.h
23 * @brief Internal DISK related helper functions
24 * @author Nils Durner
25 */
26#ifndef GNUNET_DISK_H_
27#define GNUNET_DISK_H_
28
29#include "gnunet_util_lib.h"
30
31/**
32 * Retrieve OS file handle
33 *
34 * @internal
35 * @param fh GNUnet file descriptor
36 * @param dst destination buffer
37 * @param dst_len length of @a dst
38 * @return #GNUNET_OK on success, #GNUNET_SYSERR otherwise
39 */
40int
41GNUNET_DISK_internal_file_handle_ (const struct GNUNET_DISK_FileHandle *fh,
42 void *dst, size_t dst_len);
43
44#endif /* GNUNET_DISK_H_ */
diff --git a/src/lib/util/dnsparser.c b/src/lib/util/dnsparser.c
new file mode 100644
index 000000000..93b2b590b
--- /dev/null
+++ b/src/lib/util/dnsparser.c
@@ -0,0 +1,1535 @@
1/*
2 This file is part of GNUnet
3 Copyright (C) 2010-2014, 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/**
22 * @file util/dnsparser.c
23 * @brief helper library to parse DNS packets.
24 * @author Philipp Toelke
25 * @author Christian Grothoff
26 */
27
28#include "platform.h"
29#include "gnunet_util_lib.h"
30#if HAVE_LIBIDN2
31#if HAVE_IDN2_H
32#include <idn2.h>
33#elif HAVE_IDN2_IDN2_H
34#include <idn2/idn2.h>
35#endif
36#elif HAVE_LIBIDN
37#if HAVE_IDNA_H
38#include <idna.h>
39#elif HAVE_IDN_IDNA_H
40#include <idn/idna.h>
41#endif
42#endif
43
44/**
45 * Check if a label in UTF-8 format can be coded into valid IDNA.
46 * This can fail if the ASCII-conversion becomes longer than 63 characters.
47 *
48 * @param label label to check (UTF-8 string)
49 * @return #GNUNET_OK if the label can be converted to IDNA,
50 * #GNUNET_SYSERR if the label is not valid for DNS names
51 */
52int
53GNUNET_DNSPARSER_check_label (const char *label)
54{
55 char *output;
56 size_t slen;
57
58 if (NULL != strchr (label, '.'))
59 return GNUNET_SYSERR; /* not a label! Did you mean GNUNET_DNSPARSER_check_name? */
60 if (0 == strcmp (label, "@")) /* '@' is reserved for the empty label, see #GNUNET_GNS_EMPTY_LABEL_AT */
61 return GNUNET_SYSERR;
62 if (IDNA_SUCCESS != idna_to_ascii_8z (label, &output, IDNA_ALLOW_UNASSIGNED))
63 return GNUNET_SYSERR;
64 slen = strlen (output);
65 free (output);
66 return (slen > 63) ? GNUNET_SYSERR : GNUNET_OK;
67}
68
69
70/**
71 * Check if a label in UTF-8 format can be coded into valid IDNA.
72 * This can fail if the ASCII-conversion becomes longer than 253 characters.
73 *
74 * @param name name to check (UTF-8 string)
75 * @return #GNUNET_OK if the label can be converted to IDNA,
76 * #GNUNET_SYSERR if the label is not valid for DNS names
77 */
78int
79GNUNET_DNSPARSER_check_name (const char *name)
80{
81 char *ldup;
82 char *output;
83 size_t slen;
84 char *tok;
85
86 ldup = GNUNET_strdup (name);
87 for (tok = strtok (ldup, "."); NULL != tok; tok = strtok (NULL, "."))
88 if (GNUNET_OK != GNUNET_DNSPARSER_check_label (tok))
89 {
90 GNUNET_free (ldup);
91 return GNUNET_SYSERR;
92 }
93 GNUNET_free (ldup);
94 if (IDNA_SUCCESS != idna_to_ascii_8z (name, &output, IDNA_ALLOW_UNASSIGNED))
95 return GNUNET_SYSERR;
96 slen = strlen (output);
97 free (output);
98 return (slen > 253) ? GNUNET_SYSERR : GNUNET_OK;
99}
100
101
102/**
103 * Free SOA information record.
104 *
105 * @param soa record to free
106 */
107void
108GNUNET_DNSPARSER_free_soa (struct GNUNET_DNSPARSER_SoaRecord *soa)
109{
110 if (NULL == soa)
111 return;
112 GNUNET_free (soa->mname);
113 GNUNET_free (soa->rname);
114 GNUNET_free (soa);
115}
116
117
118/**
119 * Free CERT information record.
120 *
121 * @param cert record to free
122 */
123void
124GNUNET_DNSPARSER_free_cert (struct GNUNET_DNSPARSER_CertRecord *cert)
125{
126 if (NULL == cert)
127 return;
128 GNUNET_free (cert->certificate_data);
129 GNUNET_free (cert);
130}
131
132
133/**
134 * Free SRV information record.
135 *
136 * @param srv record to free
137 */
138void
139GNUNET_DNSPARSER_free_srv (struct GNUNET_DNSPARSER_SrvRecord *srv)
140{
141 if (NULL == srv)
142 return;
143 GNUNET_free (srv->target);
144 GNUNET_free (srv);
145}
146
147
148/**
149 * Free URI information record.
150 *
151 * @param uri record to free
152 */
153void
154GNUNET_DNSPARSER_free_uri (struct GNUNET_DNSPARSER_UriRecord *uri)
155{
156 if (NULL == uri)
157 return;
158 GNUNET_free (uri->target);
159 GNUNET_free (uri);
160}
161
162
163/**
164 * Free MX information record.
165 *
166 * @param mx record to free
167 */
168void
169GNUNET_DNSPARSER_free_mx (struct GNUNET_DNSPARSER_MxRecord *mx)
170{
171 if (NULL == mx)
172 return;
173 GNUNET_free (mx->mxhost);
174 GNUNET_free (mx);
175}
176
177
178/**
179 * Free the given DNS record.
180 *
181 * @param r record to free
182 */
183void
184GNUNET_DNSPARSER_free_record (struct GNUNET_DNSPARSER_Record *r)
185{
186 GNUNET_free (r->name);
187 switch (r->type)
188 {
189 case GNUNET_DNSPARSER_TYPE_MX:
190 GNUNET_DNSPARSER_free_mx (r->data.mx);
191 break;
192
193 case GNUNET_DNSPARSER_TYPE_SOA:
194 GNUNET_DNSPARSER_free_soa (r->data.soa);
195 break;
196
197 case GNUNET_DNSPARSER_TYPE_SRV:
198 GNUNET_DNSPARSER_free_srv (r->data.srv);
199 break;
200
201 case GNUNET_DNSPARSER_TYPE_URI:
202 GNUNET_DNSPARSER_free_uri (r->data.uri);
203 break;
204
205 case GNUNET_DNSPARSER_TYPE_CERT:
206 GNUNET_DNSPARSER_free_cert (r->data.cert);
207 break;
208
209 case GNUNET_DNSPARSER_TYPE_NS:
210 case GNUNET_DNSPARSER_TYPE_CNAME:
211 case GNUNET_DNSPARSER_TYPE_PTR:
212 GNUNET_free (r->data.hostname);
213 break;
214
215 default:
216 GNUNET_free (r->data.raw.data);
217 break;
218 }
219}
220
221
222/**
223 * Parse name inside of a DNS query or record.
224 *
225 * @param udp_payload entire UDP payload
226 * @param udp_payload_length length of @a udp_payload
227 * @param off pointer to the offset of the name to parse in the udp_payload (to be
228 * incremented by the size of the name)
229 * @param depth current depth of our recursion (to prevent stack overflow)
230 * @return name as 0-terminated C string on success, NULL if the payload is malformed
231 */
232static char *
233parse_name (const char *udp_payload,
234 size_t udp_payload_length,
235 size_t *off,
236 unsigned int depth)
237{
238 const uint8_t *input = (const uint8_t *) udp_payload;
239 char *ret;
240 char *tmp;
241 char *xstr;
242 uint8_t len;
243 size_t xoff;
244 char *utf8;
245 Idna_rc rc;
246
247 ret = GNUNET_strdup ("");
248 while (1)
249 {
250 if (*off >= udp_payload_length)
251 {
252 GNUNET_break_op (0);
253 goto error;
254 }
255 len = input[*off];
256 if (0 == len)
257 {
258 (*off)++;
259 break;
260 }
261 if (len < 64)
262 {
263 if (*off + 1 + len > udp_payload_length)
264 {
265 GNUNET_break_op (0);
266 goto error;
267 }
268 GNUNET_asprintf (&tmp, "%.*s", (int) len, &udp_payload[*off + 1]);
269 if (IDNA_SUCCESS !=
270 (rc = idna_to_unicode_8z8z (tmp, &utf8, IDNA_ALLOW_UNASSIGNED)))
271 {
272 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
273 _ ("Failed to convert DNS IDNA name `%s' to UTF-8: %s\n"),
274 tmp,
275 idna_strerror (rc));
276 GNUNET_free (tmp);
277 GNUNET_asprintf (&tmp,
278 "%s%.*s.",
279 ret,
280 (int) len,
281 &udp_payload[*off + 1]);
282 }
283 else
284 {
285 GNUNET_free (tmp);
286 GNUNET_asprintf (&tmp, "%s%s.", ret, utf8);
287 free (utf8);
288 }
289 GNUNET_free (ret);
290 ret = tmp;
291 *off += 1 + len;
292 }
293 else if ((64 | 128) == (len & (64 | 128)))
294 {
295 if (depth > 32)
296 {
297 GNUNET_break_op (0);
298 goto error; /* hard bound on stack to prevent "infinite" recursion, disallow! */
299 }
300 /* pointer to string */
301 if (*off + 1 > udp_payload_length)
302 {
303 GNUNET_break_op (0);
304 goto error;
305 }
306 xoff = ((len - (64 | 128)) << 8) + input[*off + 1];
307 xstr = parse_name (udp_payload, udp_payload_length, &xoff, depth + 1);
308 if (NULL == xstr)
309 {
310 GNUNET_break_op (0);
311 goto error;
312 }
313 GNUNET_asprintf (&tmp, "%s%s.", ret, xstr);
314 GNUNET_free (ret);
315 GNUNET_free (xstr);
316 ret = tmp;
317 if (strlen (ret) > udp_payload_length)
318 {
319 GNUNET_break_op (0);
320 goto error; /* we are looping (building an infinite string) */
321 }
322 *off += 2;
323 /* pointers always terminate names */
324 break;
325 }
326 else
327 {
328 /* neither pointer nor inline string, not supported... */
329 GNUNET_break_op (0);
330 goto error;
331 }
332 }
333 if (0 < strlen (ret))
334 ret[strlen (ret) - 1] = '\0'; /* eat tailing '.' */
335 return ret;
336error:
337 GNUNET_break_op (0);
338 GNUNET_free (ret);
339 return NULL;
340}
341
342
343/**
344 * Parse name inside of a DNS query or record.
345 *
346 * @param udp_payload entire UDP payload
347 * @param udp_payload_length length of @a udp_payload
348 * @param off pointer to the offset of the name to parse in the udp_payload (to be
349 * incremented by the size of the name)
350 * @return name as 0-terminated C string on success, NULL if the payload is malformed
351 */
352char *
353GNUNET_DNSPARSER_parse_name (const char *udp_payload,
354 size_t udp_payload_length,
355 size_t *off)
356{
357 return parse_name (udp_payload, udp_payload_length, off, 0);
358}
359
360
361/**
362 * Parse a DNS query entry.
363 *
364 * @param udp_payload entire UDP payload
365 * @param udp_payload_length length of @a udp_payload
366 * @param off pointer to the offset of the query to parse in the udp_payload (to be
367 * incremented by the size of the query)
368 * @param q where to write the query information
369 * @return #GNUNET_OK on success, #GNUNET_SYSERR if the query is malformed
370 */
371int
372GNUNET_DNSPARSER_parse_query (const char *udp_payload,
373 size_t udp_payload_length,
374 size_t *off,
375 struct GNUNET_DNSPARSER_Query *q)
376{
377 char *name;
378 struct GNUNET_TUN_DnsQueryLine ql;
379
380 name = GNUNET_DNSPARSER_parse_name (udp_payload, udp_payload_length, off);
381 if (NULL == name)
382 {
383 GNUNET_break_op (0);
384 return GNUNET_SYSERR;
385 }
386 q->name = name;
387 if (*off + sizeof(struct GNUNET_TUN_DnsQueryLine) > udp_payload_length)
388 {
389 GNUNET_break_op (0);
390 return GNUNET_SYSERR;
391 }
392 GNUNET_memcpy (&ql, &udp_payload[*off], sizeof(ql));
393 *off += sizeof(ql);
394 q->type = ntohs (ql.type);
395 q->dns_traffic_class = ntohs (ql.dns_traffic_class);
396 return GNUNET_OK;
397}
398
399
400/**
401 * Parse a DNS SOA record.
402 *
403 * @param udp_payload reference to UDP packet
404 * @param udp_payload_length length of @a udp_payload
405 * @param off pointer to the offset of the query to parse in the SOA record (to be
406 * incremented by the size of the record), unchanged on error
407 * @return the parsed SOA record, NULL on error
408 */
409struct GNUNET_DNSPARSER_SoaRecord *
410GNUNET_DNSPARSER_parse_soa (const char *udp_payload,
411 size_t udp_payload_length,
412 size_t *off)
413{
414 struct GNUNET_DNSPARSER_SoaRecord *soa;
415 struct GNUNET_TUN_DnsSoaRecord soa_bin;
416 size_t old_off;
417
418 old_off = *off;
419 soa = GNUNET_new (struct GNUNET_DNSPARSER_SoaRecord);
420 soa->mname =
421 GNUNET_DNSPARSER_parse_name (udp_payload, udp_payload_length, off);
422 soa->rname =
423 GNUNET_DNSPARSER_parse_name (udp_payload, udp_payload_length, off);
424 if ((NULL == soa->mname) || (NULL == soa->rname) ||
425 (*off + sizeof(struct GNUNET_TUN_DnsSoaRecord) > udp_payload_length))
426 {
427 GNUNET_break_op (0);
428 GNUNET_DNSPARSER_free_soa (soa);
429 *off = old_off;
430 return NULL;
431 }
432 GNUNET_memcpy (&soa_bin,
433 &udp_payload[*off],
434 sizeof(struct GNUNET_TUN_DnsSoaRecord));
435 soa->serial = ntohl (soa_bin.serial);
436 soa->refresh = ntohl (soa_bin.refresh);
437 soa->retry = ntohl (soa_bin.retry);
438 soa->expire = ntohl (soa_bin.expire);
439 soa->minimum_ttl = ntohl (soa_bin.minimum);
440 (*off) += sizeof(struct GNUNET_TUN_DnsSoaRecord);
441 return soa;
442}
443
444
445/**
446 * Parse a DNS MX record.
447 *
448 * @param udp_payload reference to UDP packet
449 * @param udp_payload_length length of @a udp_payload
450 * @param off pointer to the offset of the query to parse in the MX record (to be
451 * incremented by the size of the record), unchanged on error
452 * @return the parsed MX record, NULL on error
453 */
454struct GNUNET_DNSPARSER_MxRecord *
455GNUNET_DNSPARSER_parse_mx (const char *udp_payload,
456 size_t udp_payload_length,
457 size_t *off)
458{
459 struct GNUNET_DNSPARSER_MxRecord *mx;
460 uint16_t mxpref;
461 size_t old_off;
462
463 old_off = *off;
464 if (*off + sizeof(uint16_t) > udp_payload_length)
465 {
466 GNUNET_break_op (0);
467 return NULL;
468 }
469 GNUNET_memcpy (&mxpref, &udp_payload[*off], sizeof(uint16_t));
470 (*off) += sizeof(uint16_t);
471 mx = GNUNET_new (struct GNUNET_DNSPARSER_MxRecord);
472 mx->preference = ntohs (mxpref);
473 mx->mxhost =
474 GNUNET_DNSPARSER_parse_name (udp_payload, udp_payload_length, off);
475 if (NULL == mx->mxhost)
476 {
477 GNUNET_break_op (0);
478 GNUNET_DNSPARSER_free_mx (mx);
479 *off = old_off;
480 return NULL;
481 }
482 return mx;
483}
484
485
486/**
487 * Parse a DNS SRV record.
488 *
489 * @param udp_payload reference to UDP packet
490 * @param udp_payload_length length of @a udp_payload
491 * @param off pointer to the offset of the query to parse in the SRV record (to be
492 * incremented by the size of the record), unchanged on error
493 * @return the parsed SRV record, NULL on error
494 */
495struct GNUNET_DNSPARSER_SrvRecord *
496GNUNET_DNSPARSER_parse_srv (const char *udp_payload,
497 size_t udp_payload_length,
498 size_t *off)
499{
500 struct GNUNET_DNSPARSER_SrvRecord *srv;
501 struct GNUNET_TUN_DnsSrvRecord srv_bin;
502 size_t old_off;
503
504 old_off = *off;
505 if (*off + sizeof(struct GNUNET_TUN_DnsSrvRecord) > udp_payload_length)
506 return NULL;
507 GNUNET_memcpy (&srv_bin,
508 &udp_payload[*off],
509 sizeof(struct GNUNET_TUN_DnsSrvRecord));
510 (*off) += sizeof(struct GNUNET_TUN_DnsSrvRecord);
511 srv = GNUNET_new (struct GNUNET_DNSPARSER_SrvRecord);
512 srv->priority = ntohs (srv_bin.prio);
513 srv->weight = ntohs (srv_bin.weight);
514 srv->port = ntohs (srv_bin.port);
515 srv->target =
516 GNUNET_DNSPARSER_parse_name (udp_payload, udp_payload_length, off);
517 if (NULL == srv->target)
518 {
519 GNUNET_DNSPARSER_free_srv (srv);
520 *off = old_off;
521 return NULL;
522 }
523 return srv;
524}
525
526
527/**
528 * Parse a DNS URI record.
529 *
530 * @param udp_payload reference to UDP packet
531 * @param udp_payload_length length of @a udp_payload
532 * @param off pointer to the offset of the query to parse in the URI record (to be
533 * incremented by the size of the record), unchanged on error
534 * @return the parsed URI record, NULL on error
535 */
536struct GNUNET_DNSPARSER_UriRecord *
537GNUNET_DNSPARSER_parse_uri (const char *udp_payload,
538 size_t udp_payload_length,
539 size_t *off)
540{
541 struct GNUNET_DNSPARSER_UriRecord *uri;
542 struct GNUNET_TUN_DnsUriRecord uri_bin;
543 size_t old_off;
544
545 old_off = *off;
546 if (*off + sizeof(struct GNUNET_TUN_DnsUriRecord) > udp_payload_length)
547 return NULL;
548 GNUNET_memcpy (&uri_bin,
549 &udp_payload[*off],
550 sizeof(struct GNUNET_TUN_DnsUriRecord));
551 (*off) += sizeof(struct GNUNET_TUN_DnsUriRecord);
552 uri = GNUNET_new (struct GNUNET_DNSPARSER_UriRecord);
553 uri->priority = ntohs (uri_bin.prio);
554 uri->weight = ntohs (uri_bin.weight);
555 int max_len = udp_payload_length - sizeof(struct GNUNET_TUN_DnsUriRecord);
556 int len = GNUNET_asprintf (&(uri->target), "%.*s", max_len,
557 &udp_payload[*off]);
558 (*off) += len;
559 if (NULL == uri->target)
560 {
561 GNUNET_DNSPARSER_free_uri (uri);
562 *off = old_off;
563 return NULL;
564 }
565 return uri;
566}
567
568
569/**
570 * Parse a DNS CERT record.
571 *
572 * @param udp_payload reference to UDP packet
573 * @param udp_payload_length length of @a udp_payload
574 * @param off pointer to the offset of the query to parse in the CERT record (to be
575 * incremented by the size of the record), unchanged on error
576 * @return the parsed CERT record, NULL on error
577 */
578struct GNUNET_DNSPARSER_CertRecord *
579GNUNET_DNSPARSER_parse_cert (const char *udp_payload,
580 size_t udp_payload_length,
581 size_t *off)
582{
583 struct GNUNET_DNSPARSER_CertRecord *cert;
584 struct GNUNET_TUN_DnsCertRecord dcert;
585
586 if (*off + sizeof(struct GNUNET_TUN_DnsCertRecord) >= udp_payload_length)
587 {
588 GNUNET_break_op (0);
589 return NULL;
590 }
591 GNUNET_memcpy (&dcert,
592 &udp_payload[*off],
593 sizeof(struct GNUNET_TUN_DnsCertRecord));
594 (*off) += sizeof(struct GNUNET_TUN_DnsCertRecord);
595 cert = GNUNET_new (struct GNUNET_DNSPARSER_CertRecord);
596 cert->cert_type = ntohs (dcert.cert_type);
597 cert->cert_tag = ntohs (dcert.cert_tag);
598 cert->algorithm = dcert.algorithm;
599 cert->certificate_size = udp_payload_length - (*off);
600 cert->certificate_data = GNUNET_malloc (cert->certificate_size);
601 GNUNET_memcpy (cert->certificate_data,
602 &udp_payload[*off],
603 cert->certificate_size);
604 (*off) += cert->certificate_size;
605 return cert;
606}
607
608
609/**
610 * Parse a DNS record entry.
611 *
612 * @param udp_payload entire UDP payload
613 * @param udp_payload_length length of @a udp_payload
614 * @param off pointer to the offset of the record to parse in the udp_payload (to be
615 * incremented by the size of the record)
616 * @param r where to write the record information
617 * @return #GNUNET_OK on success, #GNUNET_SYSERR if the record is malformed
618 */
619int
620GNUNET_DNSPARSER_parse_record (const char *udp_payload,
621 size_t udp_payload_length,
622 size_t *off,
623 struct GNUNET_DNSPARSER_Record *r)
624{
625 char *name;
626 struct GNUNET_TUN_DnsRecordLine rl;
627 size_t old_off;
628 uint16_t data_len;
629
630 name = GNUNET_DNSPARSER_parse_name (udp_payload, udp_payload_length, off);
631 if (NULL == name)
632 {
633 GNUNET_break_op (0);
634 return GNUNET_SYSERR;
635 }
636 r->name = name;
637 if (*off + sizeof(struct GNUNET_TUN_DnsRecordLine) > udp_payload_length)
638 {
639 GNUNET_break_op (0);
640 return GNUNET_SYSERR;
641 }
642 GNUNET_memcpy (&rl, &udp_payload[*off], sizeof(rl));
643 (*off) += sizeof(rl);
644 r->type = ntohs (rl.type);
645 r->dns_traffic_class = ntohs (rl.dns_traffic_class);
646 r->expiration_time = GNUNET_TIME_relative_to_absolute (
647 GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS, ntohl (rl.ttl)));
648 data_len = ntohs (rl.data_len);
649 if (*off + data_len > udp_payload_length)
650 {
651 GNUNET_break_op (0);
652 return GNUNET_SYSERR;
653 }
654 old_off = *off;
655 switch (r->type)
656 {
657 case GNUNET_DNSPARSER_TYPE_NS:
658 case GNUNET_DNSPARSER_TYPE_CNAME:
659 case GNUNET_DNSPARSER_TYPE_DNAME:
660 case GNUNET_DNSPARSER_TYPE_PTR:
661 r->data.hostname =
662 GNUNET_DNSPARSER_parse_name (udp_payload, udp_payload_length, off);
663 if ((NULL == r->data.hostname) || (old_off + data_len != *off))
664 return GNUNET_SYSERR;
665 return GNUNET_OK;
666
667 case GNUNET_DNSPARSER_TYPE_SOA:
668 r->data.soa =
669 GNUNET_DNSPARSER_parse_soa (udp_payload, udp_payload_length, off);
670 if ((NULL == r->data.soa) || (old_off + data_len != *off))
671 {
672 GNUNET_break_op (0);
673 return GNUNET_SYSERR;
674 }
675 return GNUNET_OK;
676
677 case GNUNET_DNSPARSER_TYPE_MX:
678 r->data.mx =
679 GNUNET_DNSPARSER_parse_mx (udp_payload, udp_payload_length, off);
680 if ((NULL == r->data.mx) || (old_off + data_len != *off))
681 {
682 GNUNET_break_op (0);
683 return GNUNET_SYSERR;
684 }
685 return GNUNET_OK;
686
687 case GNUNET_DNSPARSER_TYPE_SRV:
688 r->data.srv =
689 GNUNET_DNSPARSER_parse_srv (udp_payload, udp_payload_length, off);
690 if ((NULL == r->data.srv) || (old_off + data_len != *off))
691 {
692 GNUNET_break_op (0);
693 return GNUNET_SYSERR;
694 }
695 return GNUNET_OK;
696
697 case GNUNET_DNSPARSER_TYPE_URI:
698 r->data.uri =
699 GNUNET_DNSPARSER_parse_uri (udp_payload, udp_payload_length, off);
700 if ((NULL == r->data.uri) || (old_off + data_len != *off))
701 {
702 GNUNET_break_op (0);
703 return GNUNET_SYSERR;
704 }
705 return GNUNET_OK;
706
707 default:
708 r->data.raw.data = GNUNET_malloc (data_len);
709 r->data.raw.data_len = data_len;
710 GNUNET_memcpy (r->data.raw.data, &udp_payload[*off], data_len);
711 break;
712 }
713 (*off) += data_len;
714 return GNUNET_OK;
715}
716
717
718/**
719 * Parse a UDP payload of a DNS packet in to a nice struct for further
720 * processing and manipulation.
721 *
722 * @param udp_payload wire-format of the DNS packet
723 * @param udp_payload_length number of bytes in @a udp_payload
724 * @return NULL on error, otherwise the parsed packet
725 */
726struct GNUNET_DNSPARSER_Packet *
727GNUNET_DNSPARSER_parse (const char *udp_payload, size_t udp_payload_length)
728{
729 struct GNUNET_DNSPARSER_Packet *p;
730 const struct GNUNET_TUN_DnsHeader *dns;
731 size_t off;
732 unsigned int n;
733
734 if (udp_payload_length < sizeof(struct GNUNET_TUN_DnsHeader))
735 return NULL;
736 dns = (const struct GNUNET_TUN_DnsHeader *) udp_payload;
737 off = sizeof(struct GNUNET_TUN_DnsHeader);
738 p = GNUNET_new (struct GNUNET_DNSPARSER_Packet);
739 p->flags = dns->flags;
740 p->id = dns->id;
741 n = ntohs (dns->query_count);
742 if (n > 0)
743 {
744 p->queries = GNUNET_new_array (n, struct GNUNET_DNSPARSER_Query);
745 p->num_queries = n;
746 for (unsigned int i = 0; i < n; i++)
747 if (GNUNET_OK != GNUNET_DNSPARSER_parse_query (udp_payload,
748 udp_payload_length,
749 &off,
750 &p->queries[i]))
751 goto error;
752 }
753 n = ntohs (dns->answer_rcount);
754 if (n > 0)
755 {
756 p->answers = GNUNET_new_array (n, struct GNUNET_DNSPARSER_Record);
757 p->num_answers = n;
758 for (unsigned int i = 0; i < n; i++)
759 if (GNUNET_OK != GNUNET_DNSPARSER_parse_record (udp_payload,
760 udp_payload_length,
761 &off,
762 &p->answers[i]))
763 goto error;
764 }
765 n = ntohs (dns->authority_rcount);
766 if (n > 0)
767 {
768 p->authority_records = GNUNET_new_array (n, struct GNUNET_DNSPARSER_Record);
769 p->num_authority_records = n;
770 for (unsigned int i = 0; i < n; i++)
771 if (GNUNET_OK != GNUNET_DNSPARSER_parse_record (udp_payload,
772 udp_payload_length,
773 &off,
774 &p->authority_records[i]))
775 goto error;
776 }
777 n = ntohs (dns->additional_rcount);
778 if (n > 0)
779 {
780 p->additional_records =
781 GNUNET_new_array (n, struct GNUNET_DNSPARSER_Record);
782 p->num_additional_records = n;
783 for (unsigned int i = 0; i < n; i++)
784 {
785 if (GNUNET_OK !=
786 GNUNET_DNSPARSER_parse_record (udp_payload,
787 udp_payload_length,
788 &off,
789 &p->additional_records[i]))
790 goto error;
791 }
792 }
793 return p;
794error:
795 GNUNET_break_op (0);
796 GNUNET_DNSPARSER_free_packet (p);
797 return NULL;
798}
799
800
801/**
802 * Duplicate (deep-copy) the given DNS record
803 *
804 * @param r the record
805 * @return the newly allocated record
806 */
807struct GNUNET_DNSPARSER_Record *
808GNUNET_DNSPARSER_duplicate_record (const struct GNUNET_DNSPARSER_Record *r)
809{
810 struct GNUNET_DNSPARSER_Record *dup = GNUNET_memdup (r, sizeof(*r));
811
812 dup->name = GNUNET_strdup (r->name);
813 switch (r->type)
814 {
815 case GNUNET_DNSPARSER_TYPE_NS:
816 case GNUNET_DNSPARSER_TYPE_CNAME:
817 case GNUNET_DNSPARSER_TYPE_PTR: {
818 dup->data.hostname = GNUNET_strdup (r->data.hostname);
819 break;
820 }
821
822 case GNUNET_DNSPARSER_TYPE_SOA: {
823 dup->data.soa = GNUNET_DNSPARSER_duplicate_soa_record (r->data.soa);
824 break;
825 }
826
827 case GNUNET_DNSPARSER_TYPE_CERT: {
828 dup->data.cert = GNUNET_DNSPARSER_duplicate_cert_record (r->data.cert);
829 break;
830 }
831
832 case GNUNET_DNSPARSER_TYPE_MX: {
833 dup->data.mx = GNUNET_DNSPARSER_duplicate_mx_record (r->data.mx);
834 break;
835 }
836
837 case GNUNET_DNSPARSER_TYPE_SRV: {
838 dup->data.srv = GNUNET_DNSPARSER_duplicate_srv_record (r->data.srv);
839 break;
840 }
841
842 case GNUNET_DNSPARSER_TYPE_URI: {
843 dup->data.uri = GNUNET_DNSPARSER_duplicate_uri_record (r->data.uri);
844 break;
845 }
846
847 default: {
848 dup->data.raw.data = GNUNET_memdup (r->data.raw.data,
849 r->data.raw.data_len);
850 }
851 }
852 return dup;
853}
854
855
856/**
857 * Duplicate (deep-copy) the given DNS record
858 *
859 * @param r the record
860 * @return the newly allocated record
861 */
862struct GNUNET_DNSPARSER_SoaRecord *
863GNUNET_DNSPARSER_duplicate_soa_record (
864 const struct GNUNET_DNSPARSER_SoaRecord *r)
865{
866 struct GNUNET_DNSPARSER_SoaRecord *dup = GNUNET_memdup (r, sizeof(*r));
867
868 dup->mname = GNUNET_strdup (r->mname);
869 dup->rname = GNUNET_strdup (r->rname);
870 return dup;
871}
872
873
874/**
875 * Duplicate (deep-copy) the given DNS record
876 *
877 * @param r the record
878 * @return the newly allocated record
879 */
880struct GNUNET_DNSPARSER_CertRecord *
881GNUNET_DNSPARSER_duplicate_cert_record (
882 const struct GNUNET_DNSPARSER_CertRecord *r)
883{
884 struct GNUNET_DNSPARSER_CertRecord *dup = GNUNET_memdup (r, sizeof(*r));
885
886 dup->certificate_data = GNUNET_strdup (r->certificate_data);
887 return dup;
888}
889
890
891/**
892 * Duplicate (deep-copy) the given DNS record
893 *
894 * @param r the record
895 * @return the newly allocated record
896 */
897struct GNUNET_DNSPARSER_MxRecord *
898GNUNET_DNSPARSER_duplicate_mx_record (const struct GNUNET_DNSPARSER_MxRecord *r)
899{
900 struct GNUNET_DNSPARSER_MxRecord *dup = GNUNET_memdup (r, sizeof(*r));
901
902 dup->mxhost = GNUNET_strdup (r->mxhost);
903 return dup;
904}
905
906
907/**
908 * Duplicate (deep-copy) the given DNS record
909 *
910 * @param r the record
911 * @return the newly allocated record
912 */
913struct GNUNET_DNSPARSER_SrvRecord *
914GNUNET_DNSPARSER_duplicate_srv_record (
915 const struct GNUNET_DNSPARSER_SrvRecord *r)
916{
917 struct GNUNET_DNSPARSER_SrvRecord *dup = GNUNET_memdup (r, sizeof(*r));
918
919 dup->target = GNUNET_strdup (r->target);
920 return dup;
921}
922
923
924/**
925 * Duplicate (deep-copy) the given DNS record
926 *
927 * @param r the record
928 * @return the newly allocated record
929 */
930struct GNUNET_DNSPARSER_UriRecord *
931GNUNET_DNSPARSER_duplicate_uri_record (
932 const struct GNUNET_DNSPARSER_UriRecord *r)
933{
934 struct GNUNET_DNSPARSER_UriRecord *dup = GNUNET_memdup (r, sizeof(*r));
935
936 dup->target = GNUNET_strdup (r->target);
937 return dup;
938}
939
940
941/**
942 * Free memory taken by a packet.
943 *
944 * @param p packet to free
945 */
946void
947GNUNET_DNSPARSER_free_packet (struct GNUNET_DNSPARSER_Packet *p)
948{
949 for (unsigned int i = 0; i < p->num_queries; i++)
950 GNUNET_free (p->queries[i].name);
951 GNUNET_free (p->queries);
952 for (unsigned int i = 0; i < p->num_answers; i++)
953 GNUNET_DNSPARSER_free_record (&p->answers[i]);
954 GNUNET_free (p->answers);
955 for (unsigned int i = 0; i < p->num_authority_records; i++)
956 GNUNET_DNSPARSER_free_record (&p->authority_records[i]);
957 GNUNET_free (p->authority_records);
958 for (unsigned int i = 0; i < p->num_additional_records; i++)
959 GNUNET_DNSPARSER_free_record (&p->additional_records[i]);
960 GNUNET_free (p->additional_records);
961 GNUNET_free (p);
962}
963
964
965/* ********************** DNS packet assembly code **************** */
966
967
968/**
969 * Add a DNS name to the UDP packet at the given location, converting
970 * the name to IDNA notation as necessary.
971 *
972 * @param dst where to write the name (UDP packet)
973 * @param dst_len number of bytes in @a dst
974 * @param off pointer to offset where to write the name (increment by bytes used)
975 * must not be changed if there is an error
976 * @param name name to write
977 * @return #GNUNET_SYSERR if @a name is invalid
978 * #GNUNET_NO if @a name did not fit
979 * #GNUNET_OK if @a name was added to @a dst
980 */
981int
982GNUNET_DNSPARSER_builder_add_name (char *dst,
983 size_t dst_len,
984 size_t *off,
985 const char *name)
986{
987 const char *dot;
988 const char *idna_name;
989 char *idna_start;
990 size_t start;
991 size_t pos;
992 size_t len;
993 Idna_rc rc;
994
995 if (NULL == name)
996 return GNUNET_SYSERR;
997
998 if (IDNA_SUCCESS !=
999 (rc = idna_to_ascii_8z (name, &idna_start, IDNA_ALLOW_UNASSIGNED)))
1000 {
1001 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
1002 _ (
1003 "Failed to convert UTF-8 name `%s' to DNS IDNA format: %s\n"),
1004 name,
1005 idna_strerror (rc));
1006 return GNUNET_NO;
1007 }
1008 idna_name = idna_start;
1009 start = *off;
1010 if (start + strlen (idna_name) + 2 > dst_len)
1011 goto fail;
1012 pos = start;
1013 do
1014 {
1015 dot = strchr (idna_name, '.');
1016 if (NULL == dot)
1017 len = strlen (idna_name);
1018 else
1019 len = dot - idna_name;
1020 if (len == 0)
1021 break;
1022 if (len >= 64)
1023 {
1024 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
1025 "Invalid DNS name `%s': label with %u characters encountered\n",
1026 name,
1027 (unsigned int) len);
1028 goto fail; /* label too long or empty */
1029 }
1030 dst[pos++] = (char) (uint8_t) len;
1031 GNUNET_memcpy (&dst[pos], idna_name, len);
1032 pos += len;
1033 idna_name += len + 1; /* also skip dot */
1034 }
1035 while (NULL != dot);
1036 dst[pos++] = '\0'; /* terminator */
1037 *off = pos;
1038 free (idna_start);
1039 return GNUNET_OK;
1040fail:
1041 free (idna_start);
1042 return GNUNET_NO;
1043}
1044
1045
1046/**
1047 * Add a DNS query to the UDP packet at the given location.
1048 *
1049 * @param dst where to write the query
1050 * @param dst_len number of bytes in @a dst
1051 * @param off pointer to offset where to write the query (increment by bytes used)
1052 * must not be changed if there is an error
1053 * @param query query to write
1054 * @return #GNUNET_SYSERR if @a query is invalid
1055 * #GNUNET_NO if @a query did not fit
1056 * #GNUNET_OK if @a query was added to @a dst
1057 */
1058int
1059GNUNET_DNSPARSER_builder_add_query (char *dst,
1060 size_t dst_len,
1061 size_t *off,
1062 const struct GNUNET_DNSPARSER_Query *query)
1063{
1064 int ret;
1065 struct GNUNET_TUN_DnsQueryLine ql;
1066
1067 ret = GNUNET_DNSPARSER_builder_add_name (dst,
1068 dst_len
1069 - sizeof(
1070 struct GNUNET_TUN_DnsQueryLine),
1071 off,
1072 query->name);
1073 if (ret != GNUNET_OK)
1074 return ret;
1075 ql.type = htons (query->type);
1076 ql.dns_traffic_class = htons (query->dns_traffic_class);
1077 GNUNET_memcpy (&dst[*off], &ql, sizeof(ql));
1078 (*off) += sizeof(ql);
1079 return GNUNET_OK;
1080}
1081
1082
1083/**
1084 * Add an MX record to the UDP packet at the given location.
1085 *
1086 * @param dst where to write the mx record
1087 * @param dst_len number of bytes in @a dst
1088 * @param off pointer to offset where to write the mx information (increment by bytes used);
1089 * can also change if there was an error
1090 * @param mx mx information to write
1091 * @return #GNUNET_SYSERR if @a mx is invalid
1092 * #GNUNET_NO if @a mx did not fit
1093 * #GNUNET_OK if @a mx was added to @a dst
1094 */
1095int
1096GNUNET_DNSPARSER_builder_add_mx (char *dst,
1097 size_t dst_len,
1098 size_t *off,
1099 const struct GNUNET_DNSPARSER_MxRecord *mx)
1100{
1101 uint16_t mxpref;
1102
1103 if (*off + sizeof(uint16_t) > dst_len)
1104 return GNUNET_NO;
1105 mxpref = htons (mx->preference);
1106 GNUNET_memcpy (&dst[*off], &mxpref, sizeof(mxpref));
1107 (*off) += sizeof(mxpref);
1108 return GNUNET_DNSPARSER_builder_add_name (dst, dst_len, off, mx->mxhost);
1109}
1110
1111
1112/**
1113 * Add a CERT record to the UDP packet at the given location.
1114 *
1115 * @param dst where to write the CERT record
1116 * @param dst_len number of bytes in @a dst
1117 * @param off pointer to offset where to write the CERT information (increment by bytes used);
1118 * can also change if there was an error
1119 * @param cert CERT information to write
1120 * @return #GNUNET_SYSERR if @a cert is invalid
1121 * #GNUNET_NO if @a cert did not fit
1122 * #GNUNET_OK if @a cert was added to @a dst
1123 */
1124int
1125GNUNET_DNSPARSER_builder_add_cert (
1126 char *dst,
1127 size_t dst_len,
1128 size_t *off,
1129 const struct GNUNET_DNSPARSER_CertRecord *cert)
1130{
1131 struct GNUNET_TUN_DnsCertRecord dcert;
1132
1133#ifdef __clang__
1134#pragma clang diagnostic push
1135#pragma clang diagnostic ignored "-Wtautological-constant-out-of-range-compare"
1136#endif
1137 if ((cert->cert_type > UINT16_MAX) || (cert->algorithm > UINT8_MAX))
1138 {
1139 GNUNET_break (0);
1140 return GNUNET_SYSERR;
1141 }
1142#ifdef __clang__
1143#pragma clang diagnostic pop
1144#endif
1145 if (*off + sizeof(struct GNUNET_TUN_DnsCertRecord) + cert->certificate_size >
1146 dst_len)
1147 return GNUNET_NO;
1148 dcert.cert_type = htons ((uint16_t) cert->cert_type);
1149 dcert.cert_tag = htons ((uint16_t) cert->cert_tag);
1150 dcert.algorithm = (uint8_t) cert->algorithm;
1151 GNUNET_memcpy (&dst[*off], &dcert, sizeof(dcert));
1152 (*off) += sizeof(dcert);
1153 GNUNET_memcpy (&dst[*off], cert->certificate_data, cert->certificate_size);
1154 (*off) += cert->certificate_size;
1155 return GNUNET_OK;
1156}
1157
1158
1159/**
1160 * Add an SOA record to the UDP packet at the given location.
1161 *
1162 * @param dst where to write the SOA record
1163 * @param dst_len number of bytes in @a dst
1164 * @param off pointer to offset where to write the SOA information (increment by bytes used)
1165 * can also change if there was an error
1166 * @param soa SOA information to write
1167 * @return #GNUNET_SYSERR if @a soa is invalid
1168 * #GNUNET_NO if @a soa did not fit
1169 * #GNUNET_OK if @a soa was added to @a dst
1170 */
1171int
1172GNUNET_DNSPARSER_builder_add_soa (char *dst,
1173 size_t dst_len,
1174 size_t *off,
1175 const struct GNUNET_DNSPARSER_SoaRecord *soa)
1176{
1177 struct GNUNET_TUN_DnsSoaRecord sd;
1178 int ret;
1179
1180 if ((GNUNET_OK !=
1181 (ret =
1182 GNUNET_DNSPARSER_builder_add_name (dst, dst_len, off, soa->mname))) ||
1183 (GNUNET_OK !=
1184 (ret =
1185 GNUNET_DNSPARSER_builder_add_name (dst, dst_len, off, soa->rname))))
1186 return ret;
1187 if (*off + sizeof(struct GNUNET_TUN_DnsSoaRecord) > dst_len)
1188 return GNUNET_NO;
1189 sd.serial = htonl (soa->serial);
1190 sd.refresh = htonl (soa->refresh);
1191 sd.retry = htonl (soa->retry);
1192 sd.expire = htonl (soa->expire);
1193 sd.minimum = htonl (soa->minimum_ttl);
1194 GNUNET_memcpy (&dst[*off], &sd, sizeof(sd));
1195 (*off) += sizeof(sd);
1196 return GNUNET_OK;
1197}
1198
1199
1200/**
1201 * Add an SRV record to the UDP packet at the given location.
1202 *
1203 * @param dst where to write the SRV record
1204 * @param dst_len number of bytes in @a dst
1205 * @param off pointer to offset where to write the SRV information (increment by bytes used)
1206 * can also change if there was an error
1207 * @param srv SRV information to write
1208 * @return #GNUNET_SYSERR if @a srv is invalid
1209 * #GNUNET_NO if @a srv did not fit
1210 * #GNUNET_OK if @a srv was added to @a dst
1211 */
1212int
1213GNUNET_DNSPARSER_builder_add_srv (char *dst,
1214 size_t dst_len,
1215 size_t *off,
1216 const struct GNUNET_DNSPARSER_SrvRecord *srv)
1217{
1218 struct GNUNET_TUN_DnsSrvRecord sd;
1219 int ret;
1220
1221 if (*off + sizeof(struct GNUNET_TUN_DnsSrvRecord) > dst_len)
1222 return GNUNET_NO;
1223 sd.prio = htons (srv->priority);
1224 sd.weight = htons (srv->weight);
1225 sd.port = htons (srv->port);
1226 GNUNET_memcpy (&dst[*off], &sd, sizeof(sd));
1227 (*off) += sizeof(sd);
1228 if (GNUNET_OK !=
1229 (ret =
1230 GNUNET_DNSPARSER_builder_add_name (dst, dst_len, off, srv->target)))
1231 return ret;
1232 return GNUNET_OK;
1233}
1234
1235
1236/**
1237 * Add an URI record to the UDP packet at the given location.
1238 *
1239 * @param dst where to write the URI record
1240 * @param dst_len number of bytes in @a dst
1241 * @param off pointer to offset where to write the URI information (increment by bytes used)
1242 * can also change if there was an error
1243 * @param uri URI information to write
1244 * @return #GNUNET_SYSERR if @a uri is invalid
1245 * #GNUNET_NO if @a uri did not fit
1246 * #GNUNET_OK if @a uri was added to @a dst
1247 */
1248int
1249GNUNET_DNSPARSER_builder_add_uri (char *dst,
1250 size_t dst_len,
1251 size_t *off,
1252 const struct GNUNET_DNSPARSER_UriRecord *uri)
1253{
1254 struct GNUNET_TUN_DnsUriRecord sd;
1255
1256 if (*off + sizeof(struct GNUNET_TUN_DnsUriRecord) > dst_len)
1257 return GNUNET_NO;
1258 sd.prio = htons (uri->priority);
1259 sd.weight = htons (uri->weight);
1260 GNUNET_memcpy (&dst[*off], &sd, sizeof(sd));
1261 (*off) += sizeof(sd);
1262 strncpy (&dst[*off], uri->target, dst_len - sizeof(struct
1263 GNUNET_TUN_DnsUriRecord)
1264 - 1);
1265 (*off) += strlen (uri->target);
1266 dst[*off] = '\0';
1267 return GNUNET_OK;
1268}
1269
1270
1271/**
1272 * Add a DNS record to the UDP packet at the given location.
1273 *
1274 * @param dst where to write the query
1275 * @param dst_len number of bytes in @a dst
1276 * @param off pointer to offset where to write the query (increment by bytes used)
1277 * must not be changed if there is an error
1278 * @param record record to write
1279 * @return #GNUNET_SYSERR if @a record is invalid
1280 * #GNUNET_NO if @a record did not fit
1281 * #GNUNET_OK if @a record was added to @a dst
1282 */
1283static int
1284add_record (char *dst,
1285 size_t dst_len,
1286 size_t *off,
1287 const struct GNUNET_DNSPARSER_Record *record)
1288{
1289 int ret;
1290 size_t start;
1291 size_t pos;
1292 struct GNUNET_TUN_DnsRecordLine rl;
1293
1294 start = *off;
1295 ret = GNUNET_DNSPARSER_builder_add_name (dst,
1296 dst_len
1297 - sizeof(
1298 struct GNUNET_TUN_DnsRecordLine),
1299 off,
1300 record->name);
1301 if (GNUNET_OK != ret)
1302 return ret;
1303 /* '*off' is now the position where we will need to write the record line */
1304
1305 pos = *off + sizeof(struct GNUNET_TUN_DnsRecordLine);
1306 switch (record->type)
1307 {
1308 case GNUNET_DNSPARSER_TYPE_MX:
1309 ret = GNUNET_DNSPARSER_builder_add_mx (dst, dst_len, &pos, record->data.mx);
1310 break;
1311
1312 case GNUNET_DNSPARSER_TYPE_CERT:
1313 ret =
1314 GNUNET_DNSPARSER_builder_add_cert (dst, dst_len, &pos, record->data.cert);
1315 break;
1316
1317 case GNUNET_DNSPARSER_TYPE_SOA:
1318 ret =
1319 GNUNET_DNSPARSER_builder_add_soa (dst, dst_len, &pos, record->data.soa);
1320 break;
1321
1322 case GNUNET_DNSPARSER_TYPE_NS:
1323 case GNUNET_DNSPARSER_TYPE_CNAME:
1324 case GNUNET_DNSPARSER_TYPE_PTR:
1325 ret = GNUNET_DNSPARSER_builder_add_name (dst,
1326 dst_len,
1327 &pos,
1328 record->data.hostname);
1329 break;
1330
1331 case GNUNET_DNSPARSER_TYPE_SRV:
1332 ret =
1333 GNUNET_DNSPARSER_builder_add_srv (dst, dst_len, &pos, record->data.srv);
1334 break;
1335
1336 case GNUNET_DNSPARSER_TYPE_URI:
1337 ret =
1338 GNUNET_DNSPARSER_builder_add_uri (dst, dst_len, &pos, record->data.uri);
1339 break;
1340
1341 default:
1342 if (pos + record->data.raw.data_len > dst_len)
1343 {
1344 ret = GNUNET_NO;
1345 break;
1346 }
1347 GNUNET_memcpy (&dst[pos], record->data.raw.data, record->data.raw.data_len);
1348 pos += record->data.raw.data_len;
1349 ret = GNUNET_OK;
1350 break;
1351 }
1352 if (GNUNET_OK != ret)
1353 {
1354 *off = start;
1355 return GNUNET_NO;
1356 }
1357
1358 if (pos - (*off + sizeof(struct GNUNET_TUN_DnsRecordLine)) > UINT16_MAX)
1359 {
1360 /* record data too long */
1361 *off = start;
1362 return GNUNET_NO;
1363 }
1364 rl.type = htons (record->type);
1365 rl.dns_traffic_class = htons (record->dns_traffic_class);
1366 rl.ttl = htonl (
1367 GNUNET_TIME_absolute_get_remaining (record->expiration_time).rel_value_us
1368 / 1000LL / 1000LL); /* in seconds */
1369 rl.data_len = htons (
1370 (uint16_t) (pos - (*off + sizeof(struct GNUNET_TUN_DnsRecordLine))));
1371 GNUNET_memcpy (&dst[*off], &rl, sizeof(struct GNUNET_TUN_DnsRecordLine));
1372 *off = pos;
1373 return GNUNET_OK;
1374}
1375
1376
1377/**
1378 * Given a DNS packet @a p, generate the corresponding UDP payload.
1379 * Note that we do not attempt to pack the strings with pointers
1380 * as this would complicate the code and this is about being
1381 * simple and secure, not fast, fancy and broken like bind.
1382 *
1383 * @param p packet to pack
1384 * @param max maximum allowed size for the resulting UDP payload
1385 * @param buf set to a buffer with the packed message
1386 * @param buf_length set to the length of @a buf
1387 * @return #GNUNET_SYSERR if @a p is invalid
1388 * #GNUNET_NO if @a p was truncated (but there is still a result in @a buf)
1389 * #GNUNET_OK if @a p was packed completely into @a buf
1390 */
1391int
1392GNUNET_DNSPARSER_pack (const struct GNUNET_DNSPARSER_Packet *p,
1393 uint16_t max,
1394 char **buf,
1395 size_t *buf_length)
1396{
1397 struct GNUNET_TUN_DnsHeader dns;
1398 size_t off;
1399 char tmp[max];
1400 int ret;
1401 int trc;
1402
1403 if ((p->num_queries > UINT16_MAX) || (p->num_answers > UINT16_MAX) ||
1404 (p->num_authority_records > UINT16_MAX) ||
1405 (p->num_additional_records > UINT16_MAX))
1406 return GNUNET_SYSERR;
1407 dns.id = p->id;
1408 dns.flags = p->flags;
1409 dns.query_count = htons (p->num_queries);
1410 dns.answer_rcount = htons (p->num_answers);
1411 dns.authority_rcount = htons (p->num_authority_records);
1412 dns.additional_rcount = htons (p->num_additional_records);
1413
1414 off = sizeof(struct GNUNET_TUN_DnsHeader);
1415 trc = GNUNET_NO;
1416 for (unsigned int i = 0; i < p->num_queries; i++)
1417 {
1418 ret = GNUNET_DNSPARSER_builder_add_query (tmp,
1419 sizeof(tmp),
1420 &off,
1421 &p->queries[i]);
1422 if (GNUNET_SYSERR == ret)
1423 return GNUNET_SYSERR;
1424 if (GNUNET_NO == ret)
1425 {
1426 dns.query_count = htons ((uint16_t) (i - 1));
1427 trc = GNUNET_YES;
1428 break;
1429 }
1430 }
1431 for (unsigned int i = 0; i < p->num_answers; i++)
1432 {
1433 ret = add_record (tmp, sizeof(tmp), &off, &p->answers[i]);
1434 if (GNUNET_SYSERR == ret)
1435 return GNUNET_SYSERR;
1436 if (GNUNET_NO == ret)
1437 {
1438 dns.answer_rcount = htons ((uint16_t) (i - 1));
1439 trc = GNUNET_YES;
1440 break;
1441 }
1442 }
1443 for (unsigned int i = 0; i < p->num_authority_records; i++)
1444 {
1445 ret = add_record (tmp, sizeof(tmp), &off, &p->authority_records[i]);
1446 if (GNUNET_SYSERR == ret)
1447 return GNUNET_SYSERR;
1448 if (GNUNET_NO == ret)
1449 {
1450 dns.authority_rcount = htons ((uint16_t) (i - 1));
1451 trc = GNUNET_YES;
1452 break;
1453 }
1454 }
1455 for (unsigned int i = 0; i < p->num_additional_records; i++)
1456 {
1457 ret = add_record (tmp, sizeof(tmp), &off, &p->additional_records[i]);
1458 if (GNUNET_SYSERR == ret)
1459 return GNUNET_SYSERR;
1460 if (GNUNET_NO == ret)
1461 {
1462 dns.additional_rcount = htons (i - 1);
1463 trc = GNUNET_YES;
1464 break;
1465 }
1466 }
1467
1468 if (GNUNET_YES == trc)
1469 dns.flags.message_truncated = 1;
1470 GNUNET_memcpy (tmp, &dns, sizeof(struct GNUNET_TUN_DnsHeader));
1471
1472 *buf = GNUNET_malloc (off);
1473 *buf_length = off;
1474 GNUNET_memcpy (*buf, tmp, off);
1475 if (GNUNET_YES == trc)
1476 return GNUNET_NO;
1477 return GNUNET_OK;
1478}
1479
1480
1481/**
1482 * Convert a block of binary data to HEX.
1483 *
1484 * @param data binary data to convert
1485 * @param data_size number of bytes in @a data
1486 * @return HEX string (lower case)
1487 */
1488char *
1489GNUNET_DNSPARSER_bin_to_hex (const void *data, size_t data_size)
1490{
1491 char *ret;
1492 size_t off;
1493 const uint8_t *idata;
1494
1495 idata = data;
1496 ret = GNUNET_malloc (data_size * 2 + 1);
1497 for (off = 0; off < data_size; off++)
1498 sprintf (&ret[off * 2], "%02x", idata[off]);
1499 return ret;
1500}
1501
1502
1503/**
1504 * Convert a HEX string to block of binary data.
1505 *
1506 * @param hex HEX string to convert (may contain mixed case)
1507 * @param data where to write result, must be
1508 * at least `strlen(hex)/2` bytes long
1509 * @return number of bytes written to data
1510 */
1511size_t
1512GNUNET_DNSPARSER_hex_to_bin (const char *hex, void *data)
1513{
1514 size_t data_size;
1515 size_t off;
1516 uint8_t *idata;
1517 unsigned int h;
1518 char in[3];
1519
1520 data_size = strlen (hex) / 2;
1521 idata = data;
1522 in[2] = '\0';
1523 for (off = 0; off < data_size; off++)
1524 {
1525 in[0] = tolower ((unsigned char) hex[off * 2]);
1526 in[1] = tolower ((unsigned char) hex[off * 2 + 1]);
1527 if (1 != sscanf (in, "%x", &h))
1528 return off;
1529 idata[off] = (uint8_t) h;
1530 }
1531 return off;
1532}
1533
1534
1535/* end of dnsparser.c */
diff --git a/src/lib/util/dnsstub.c b/src/lib/util/dnsstub.c
new file mode 100644
index 000000000..c259b51dd
--- /dev/null
+++ b/src/lib/util/dnsstub.c
@@ -0,0 +1,721 @@
1/*
2 This file is part of GNUnet.
3 Copyright (C) 2012, 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 dns/dnsstub.c
22 * @brief DNS stub resolver which sends DNS requests to an actual resolver
23 * @author Christian Grothoff
24 */
25
26#include "platform.h"
27#include "gnunet_util_lib.h"
28
29/**
30 * Timeout for retrying DNS queries.
31 */
32#define DNS_RETRANSMIT_DELAY \
33 GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_MILLISECONDS, 250)
34
35
36/**
37 * DNS Server used for resolution.
38 */
39struct DnsServer;
40
41
42/**
43 * UDP socket we are using for sending DNS requests to the Internet.
44 */
45struct GNUNET_DNSSTUB_RequestSocket
46{
47 /**
48 * UDP socket we use for this request for IPv4
49 */
50 struct GNUNET_NETWORK_Handle *dnsout4;
51
52 /**
53 * UDP socket we use for this request for IPv6
54 */
55 struct GNUNET_NETWORK_Handle *dnsout6;
56
57 /**
58 * Function to call with result.
59 */
60 GNUNET_DNSSTUB_ResultCallback rc;
61
62 /**
63 * Closure for @e rc.
64 */
65 void *rc_cls;
66
67 /**
68 * Task for reading from dnsout4 and dnsout6.
69 */
70 struct GNUNET_SCHEDULER_Task *read_task;
71
72 /**
73 * Task for retrying transmission of the query.
74 */
75 struct GNUNET_SCHEDULER_Task *retry_task;
76
77 /**
78 * Next address we sent the DNS request to.
79 */
80 struct DnsServer *ds_pos;
81
82 /**
83 * Context this request executes in.
84 */
85 struct GNUNET_DNSSTUB_Context *ctx;
86
87 /**
88 * Query we sent to @e addr.
89 */
90 void *request;
91
92 /**
93 * Number of bytes in @a request.
94 */
95 size_t request_len;
96};
97
98
99/**
100 * DNS Server used for resolution.
101 */
102struct DnsServer
103{
104 /**
105 * Kept in a DLL.
106 */
107 struct DnsServer *next;
108
109 /**
110 * Kept in a DLL.
111 */
112 struct DnsServer *prev;
113
114 /**
115 * IP address of the DNS resolver.
116 */
117 struct sockaddr_storage ss;
118};
119
120
121/**
122 * Handle to the stub resolver.
123 */
124struct GNUNET_DNSSTUB_Context
125{
126 /**
127 * Array of all open sockets for DNS requests.
128 */
129 struct GNUNET_DNSSTUB_RequestSocket *sockets;
130
131 /**
132 * DLL of DNS resolvers we use.
133 */
134 struct DnsServer *dns_head;
135
136 /**
137 * DLL of DNS resolvers we use.
138 */
139 struct DnsServer *dns_tail;
140
141 /**
142 * How frequently do we retry requests?
143 */
144 struct GNUNET_TIME_Relative retry_freq;
145
146 /**
147 * Length of @e sockets array.
148 */
149 unsigned int num_sockets;
150};
151
152
153/**
154 * We're done with a `struct GNUNET_DNSSTUB_RequestSocket`, close it for now.
155 *
156 * @param rs request socket to clean up
157 */
158static void
159cleanup_rs (struct GNUNET_DNSSTUB_RequestSocket *rs)
160{
161 if (NULL != rs->dnsout4)
162 {
163 GNUNET_NETWORK_socket_close (rs->dnsout4);
164 rs->dnsout4 = NULL;
165 }
166 if (NULL != rs->dnsout6)
167 {
168 GNUNET_NETWORK_socket_close (rs->dnsout6);
169 rs->dnsout6 = NULL;
170 }
171 if (NULL != rs->read_task)
172 {
173 GNUNET_SCHEDULER_cancel (rs->read_task);
174 rs->read_task = NULL;
175 }
176 if (NULL != rs->retry_task)
177 {
178 GNUNET_SCHEDULER_cancel (rs->retry_task);
179 rs->retry_task = NULL;
180 }
181 if (NULL != rs->request)
182 {
183 GNUNET_free (rs->request);
184 rs->request = NULL;
185 }
186}
187
188
189/**
190 * Open source port for sending DNS requests
191 *
192 * @param af AF_INET or AF_INET6
193 * @return #GNUNET_OK on success
194 */
195static struct GNUNET_NETWORK_Handle *
196open_socket (int af)
197{
198 struct sockaddr_in a4;
199 struct sockaddr_in6 a6;
200 struct sockaddr *sa;
201 socklen_t alen;
202 struct GNUNET_NETWORK_Handle *ret;
203
204 ret = GNUNET_NETWORK_socket_create (af, SOCK_DGRAM, 0);
205 if (NULL == ret)
206 return NULL;
207 switch (af)
208 {
209 case AF_INET:
210 memset (&a4, 0, alen = sizeof(struct sockaddr_in));
211 sa = (struct sockaddr *) &a4;
212 break;
213
214 case AF_INET6:
215 memset (&a6, 0, alen = sizeof(struct sockaddr_in6));
216 sa = (struct sockaddr *) &a6;
217 break;
218
219 default:
220 GNUNET_break (0);
221 GNUNET_NETWORK_socket_close (ret);
222 return NULL;
223 }
224 sa->sa_family = af;
225 if (GNUNET_OK != GNUNET_NETWORK_socket_bind (ret, sa, alen))
226 {
227 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
228 _ ("Could not bind to any port: %s\n"),
229 strerror (errno));
230 GNUNET_NETWORK_socket_close (ret);
231 return NULL;
232 }
233 return ret;
234}
235
236
237/**
238 * Get a socket of the specified address family to send out a
239 * UDP DNS request to the Internet.
240 *
241 * @param ctx the DNSSTUB context
242 * @return NULL on error
243 */
244static struct GNUNET_DNSSTUB_RequestSocket *
245get_request_socket (struct GNUNET_DNSSTUB_Context *ctx)
246{
247 struct GNUNET_DNSSTUB_RequestSocket *rs;
248
249 for (unsigned int i = 0; i < 256; i++)
250 {
251 rs = &ctx->sockets[GNUNET_CRYPTO_random_u32 (GNUNET_CRYPTO_QUALITY_NONCE,
252 ctx->num_sockets)];
253 if (NULL == rs->rc)
254 break;
255 }
256 if (NULL != rs->rc)
257 {
258 /* signal "failure" */
259 rs->rc (rs->rc_cls, NULL, 0);
260 rs->rc = NULL;
261 }
262 if (NULL != rs->read_task)
263 {
264 GNUNET_SCHEDULER_cancel (rs->read_task);
265 rs->read_task = NULL;
266 }
267 if (NULL != rs->retry_task)
268 {
269 GNUNET_SCHEDULER_cancel (rs->retry_task);
270 rs->retry_task = NULL;
271 }
272 if (NULL != rs->request)
273 {
274 GNUNET_free (rs->request);
275 rs->request = NULL;
276 }
277 rs->ctx = ctx;
278 return rs;
279}
280
281
282/**
283 * Actually do the reading of a DNS packet from our UDP socket and see
284 * if we have a valid, matching, pending request.
285 *
286 * @param rs request socket with callback details
287 * @param dnsout socket to read from
288 * @return #GNUNET_OK on success, #GNUNET_NO on drop, #GNUNET_SYSERR on IO-errors (closed socket)
289 */
290static int
291do_dns_read (struct GNUNET_DNSSTUB_RequestSocket *rs,
292 struct GNUNET_NETWORK_Handle *dnsout)
293{
294 struct GNUNET_DNSSTUB_Context *ctx = rs->ctx;
295 ssize_t r;
296 int len;
297
298 if (0 != ioctl (GNUNET_NETWORK_get_fd (dnsout), FIONREAD, &len))
299 {
300 /* conservative choice: */
301 len = UINT16_MAX;
302 }
303
304 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Receiving %d byte DNS reply\n", len);
305 {
306 unsigned char buf[len] GNUNET_ALIGN;
307 int found;
308 struct sockaddr_storage addr;
309 socklen_t addrlen;
310 struct GNUNET_TUN_DnsHeader *dns;
311
312 addrlen = sizeof(addr);
313 memset (&addr, 0, sizeof(addr));
314 r = GNUNET_NETWORK_socket_recvfrom (dnsout,
315 buf,
316 sizeof(buf),
317 (struct sockaddr *) &addr,
318 &addrlen);
319 if (-1 == r)
320 {
321 GNUNET_log_strerror (GNUNET_ERROR_TYPE_ERROR, "recvfrom");
322 GNUNET_NETWORK_socket_close (dnsout);
323 return GNUNET_SYSERR;
324 }
325 found = GNUNET_NO;
326 for (struct DnsServer *ds = ctx->dns_head; NULL != ds; ds = ds->next)
327 {
328 if (ds->ss.ss_family != addr.ss_family)
329 continue;
330 if (addr.ss_family == AF_INET)
331 {
332 struct sockaddr_in *v4 = (struct sockaddr_in *) &addr;
333 struct sockaddr_in *ds_v4 = (struct sockaddr_in *) &ds->ss;
334
335
336 if ((0 == memcmp (&v4->sin_addr,
337 &ds_v4->sin_addr,
338 sizeof(struct sockaddr_in))) &&
339 (v4->sin_port == ds_v4->sin_port))
340 {
341 found = GNUNET_YES;
342 break;
343 }
344 }
345 else
346 {
347 struct sockaddr_in6 *v6 = (struct sockaddr_in6 *) &addr;
348 struct sockaddr_in6 *ds_v6 = (struct sockaddr_in6 *) &ds->ss;
349
350 if (0 == memcmp (&v6->sin6_addr,
351 &ds_v6->sin6_addr,
352 sizeof (v6->sin6_addr)) &&
353 (v6->sin6_port == ds_v6->sin6_port))
354 {
355 found = GNUNET_YES;
356 break;
357 }
358
359 }
360 }
361 if (GNUNET_NO == found)
362 {
363 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
364 "Received DNS response from server we never asked (ignored)\n");
365
366 return GNUNET_NO;
367 }
368 if (sizeof(struct GNUNET_TUN_DnsHeader) > (size_t) r)
369 {
370 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
371 _ ("Received DNS response that is too small (%u bytes)\n"),
372 (unsigned int) r);
373 return GNUNET_NO;
374 }
375 dns = (struct GNUNET_TUN_DnsHeader *) buf;
376 if (NULL == rs->rc)
377 {
378 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
379 "Request timeout or cancelled; ignoring reply\n");
380 return GNUNET_NO;
381 }
382 rs->rc (rs->rc_cls, dns, r);
383 }
384 return GNUNET_OK;
385}
386
387
388/**
389 * Read a DNS response from the (unhindered) UDP-Socket
390 *
391 * @param cls `struct GNUNET_DNSSTUB_RequestSocket` to read from
392 */
393static void
394read_response (void *cls);
395
396
397/**
398 * Schedule #read_response() task for @a rs.
399 *
400 * @param rs request to schedule read operation for
401 */
402static void
403schedule_read (struct GNUNET_DNSSTUB_RequestSocket *rs)
404{
405 struct GNUNET_NETWORK_FDSet *rset;
406
407 if (NULL != rs->read_task)
408 GNUNET_SCHEDULER_cancel (rs->read_task);
409 rset = GNUNET_NETWORK_fdset_create ();
410 if (NULL != rs->dnsout4)
411 GNUNET_NETWORK_fdset_set (rset, rs->dnsout4);
412 if (NULL != rs->dnsout6)
413 GNUNET_NETWORK_fdset_set (rset, rs->dnsout6);
414 rs->read_task =
415 GNUNET_SCHEDULER_add_select (GNUNET_SCHEDULER_PRIORITY_DEFAULT,
416 GNUNET_TIME_UNIT_FOREVER_REL,
417 rset,
418 NULL,
419 &read_response,
420 rs);
421 GNUNET_NETWORK_fdset_destroy (rset);
422}
423
424
425static void
426read_response (void *cls)
427{
428 struct GNUNET_DNSSTUB_RequestSocket *rs = cls;
429 const struct GNUNET_SCHEDULER_TaskContext *tc;
430
431 rs->read_task = NULL;
432 tc = GNUNET_SCHEDULER_get_task_context ();
433 /* read and process ready sockets */
434 if ((NULL != rs->dnsout4) &&
435 (GNUNET_NETWORK_fdset_isset (tc->read_ready, rs->dnsout4)) &&
436 (GNUNET_SYSERR == do_dns_read (rs, rs->dnsout4)))
437 rs->dnsout4 = NULL;
438 if ((NULL != rs->dnsout6) &&
439 (GNUNET_NETWORK_fdset_isset (tc->read_ready, rs->dnsout6)) &&
440 (GNUNET_SYSERR == do_dns_read (rs, rs->dnsout6)))
441 rs->dnsout6 = NULL;
442 /* re-schedule read task */
443 schedule_read (rs);
444}
445
446
447/**
448 * Task to (re)transmit the DNS query, possibly repeatedly until
449 * we succeed.
450 *
451 * @param cls our `struct GNUNET_DNSSTUB_RequestSocket *`
452 */
453static void
454transmit_query (void *cls)
455{
456 struct GNUNET_DNSSTUB_RequestSocket *rs = cls;
457 struct GNUNET_DNSSTUB_Context *ctx = rs->ctx;
458 const struct sockaddr *sa;
459 socklen_t salen;
460 struct DnsServer *ds;
461 struct GNUNET_NETWORK_Handle *dnsout;
462
463 rs->retry_task =
464 GNUNET_SCHEDULER_add_delayed (ctx->retry_freq, &transmit_query, rs);
465 ds = rs->ds_pos;
466 rs->ds_pos = ds->next;
467 if (NULL == rs->ds_pos)
468 rs->ds_pos = ctx->dns_head;
469 GNUNET_assert (NULL != ds);
470 dnsout = NULL;
471 switch (ds->ss.ss_family)
472 {
473 case AF_INET:
474 if (NULL == rs->dnsout4)
475 rs->dnsout4 = open_socket (AF_INET);
476 dnsout = rs->dnsout4;
477 sa = (const struct sockaddr *) &ds->ss;
478 salen = sizeof(struct sockaddr_in);
479 break;
480
481 case AF_INET6:
482 if (NULL == rs->dnsout6)
483 rs->dnsout6 = open_socket (AF_INET6);
484 dnsout = rs->dnsout6;
485 sa = (const struct sockaddr *) &ds->ss;
486 salen = sizeof(struct sockaddr_in6);
487 break;
488
489 default:
490 return;
491 }
492 if (NULL == dnsout)
493 {
494 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
495 "Unable to use configure DNS server, skipping\n");
496 return;
497 }
498 if (GNUNET_SYSERR == GNUNET_NETWORK_socket_sendto (dnsout,
499 rs->request,
500 rs->request_len,
501 sa,
502 salen))
503 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
504 _ ("Failed to send DNS request to %s: %s\n"),
505 GNUNET_a2s (sa, salen),
506 strerror (errno));
507 else
508 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
509 _ ("Sent DNS request to %s\n"),
510 GNUNET_a2s (sa, salen));
511 schedule_read (rs);
512}
513
514
515/**
516 * Perform DNS resolution using our default IP from init.
517 *
518 * @param ctx stub resolver to use
519 * @param request DNS request to transmit
520 * @param request_len number of bytes in msg
521 * @param rc function to call with result
522 * @param rc_cls closure for 'rc'
523 * @return socket used for the request, NULL on error
524 */
525struct GNUNET_DNSSTUB_RequestSocket *
526GNUNET_DNSSTUB_resolve (struct GNUNET_DNSSTUB_Context *ctx,
527 const void *request,
528 size_t request_len,
529 GNUNET_DNSSTUB_ResultCallback rc,
530 void *rc_cls)
531{
532 struct GNUNET_DNSSTUB_RequestSocket *rs;
533
534 if (NULL == ctx->dns_head)
535 {
536 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
537 "No DNS server configured for resolution\n");
538 return NULL;
539 }
540 if (NULL == (rs = get_request_socket (ctx)))
541 {
542 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
543 "No request socket available for DNS resolution\n");
544 return NULL;
545 }
546 rs->ds_pos = ctx->dns_head;
547 rs->rc = rc;
548 rs->rc_cls = rc_cls;
549 rs->request = GNUNET_memdup (request, request_len);
550 rs->request_len = request_len;
551 rs->retry_task = GNUNET_SCHEDULER_add_now (&transmit_query, rs);
552 return rs;
553}
554
555
556/**
557 * Cancel DNS resolution.
558 *
559 * @param rs resolution to cancel
560 */
561void
562GNUNET_DNSSTUB_resolve_cancel (struct GNUNET_DNSSTUB_RequestSocket *rs)
563{
564 rs->rc = NULL;
565 if (NULL != rs->retry_task)
566 {
567 GNUNET_SCHEDULER_cancel (rs->retry_task);
568 rs->retry_task = NULL;
569 }
570 if (NULL != rs->read_task)
571 {
572 GNUNET_SCHEDULER_cancel (rs->read_task);
573 rs->read_task = NULL;
574 }
575}
576
577
578/**
579 * Start a DNS stub resolver.
580 *
581 * @param num_sockets how many sockets should we open
582 * in parallel for DNS queries for this stub?
583 * @return NULL on error
584 */
585struct GNUNET_DNSSTUB_Context *
586GNUNET_DNSSTUB_start (unsigned int num_sockets)
587{
588 struct GNUNET_DNSSTUB_Context *ctx;
589
590 if (0 == num_sockets)
591 {
592 GNUNET_break (0);
593 return NULL;
594 }
595 ctx = GNUNET_new (struct GNUNET_DNSSTUB_Context);
596 ctx->num_sockets = num_sockets;
597 ctx->sockets =
598 GNUNET_new_array (num_sockets, struct GNUNET_DNSSTUB_RequestSocket);
599 ctx->retry_freq = DNS_RETRANSMIT_DELAY;
600 return ctx;
601}
602
603
604/**
605 * Add nameserver for use by the DNSSTUB. We will use
606 * all provided nameservers for resolution (round-robin).
607 *
608 * @param ctx resolver context to modify
609 * @param dns_ip target IP address to use (as string)
610 * @return #GNUNET_OK on success
611 */
612int
613GNUNET_DNSSTUB_add_dns_ip (struct GNUNET_DNSSTUB_Context *ctx,
614 const char *dns_ip)
615{
616 struct DnsServer *ds;
617 struct in_addr i4;
618 struct in6_addr i6;
619
620 ds = GNUNET_new (struct DnsServer);
621 if (1 == inet_pton (AF_INET, dns_ip, &i4))
622 {
623 struct sockaddr_in *s4 = (struct sockaddr_in *) &ds->ss;
624
625 s4->sin_family = AF_INET;
626 s4->sin_port = htons (53);
627 s4->sin_addr = i4;
628#if HAVE_SOCKADDR_IN_SIN_LEN
629 s4->sin_len = (u_char) sizeof(struct sockaddr_in);
630#endif
631 }
632 else if (1 == inet_pton (AF_INET6, dns_ip, &i6))
633 {
634 struct sockaddr_in6 *s6 = (struct sockaddr_in6 *) &ds->ss;
635
636 s6->sin6_family = AF_INET6;
637 s6->sin6_port = htons (53);
638 s6->sin6_addr = i6;
639#if HAVE_SOCKADDR_IN_SIN_LEN
640 s6->sin6_len = (u_char) sizeof(struct sockaddr_in6);
641#endif
642 }
643 else
644 {
645 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
646 "Malformed IP address `%s' for DNS server\n",
647 dns_ip);
648 GNUNET_free (ds);
649 return GNUNET_SYSERR;
650 }
651 GNUNET_CONTAINER_DLL_insert (ctx->dns_head, ctx->dns_tail, ds);
652 return GNUNET_OK;
653}
654
655
656/**
657 * Add nameserver for use by the DNSSTUB. We will use
658 * all provided nameservers for resolution (round-robin).
659 *
660 * @param ctx resolver context to modify
661 * @param sa socket address of DNS resolver to use
662 * @return #GNUNET_OK on success
663 */
664int
665GNUNET_DNSSTUB_add_dns_sa (struct GNUNET_DNSSTUB_Context *ctx,
666 const struct sockaddr *sa)
667{
668 struct DnsServer *ds;
669
670 ds = GNUNET_new (struct DnsServer);
671 switch (sa->sa_family)
672 {
673 case AF_INET :
674 GNUNET_memcpy (&ds->ss, sa, sizeof(struct sockaddr_in));
675 break;
676
677 case AF_INET6:
678 GNUNET_memcpy (&ds->ss, sa, sizeof(struct sockaddr_in6));
679 break;
680
681 default:
682 GNUNET_break (0);
683 GNUNET_free (ds);
684 return GNUNET_SYSERR;
685 }
686 GNUNET_CONTAINER_DLL_insert (ctx->dns_head, ctx->dns_tail, ds);
687 return GNUNET_OK;
688}
689
690
691void
692GNUNET_DNSSTUB_set_retry (struct GNUNET_DNSSTUB_Context *ctx,
693 struct GNUNET_TIME_Relative retry_freq)
694{
695 ctx->retry_freq = retry_freq;
696}
697
698
699/**
700 * Cleanup DNSSTUB resolver.
701 *
702 * @param ctx stub resolver to clean up
703 */
704void
705GNUNET_DNSSTUB_stop (struct GNUNET_DNSSTUB_Context *ctx)
706{
707 struct DnsServer *ds;
708
709 while (NULL != (ds = ctx->dns_head))
710 {
711 GNUNET_CONTAINER_DLL_remove (ctx->dns_head, ctx->dns_tail, ds);
712 GNUNET_free (ds);
713 }
714 for (unsigned int i = 0; i < ctx->num_sockets; i++)
715 cleanup_rs (&ctx->sockets[i]);
716 GNUNET_free (ctx->sockets);
717 GNUNET_free (ctx);
718}
719
720
721/* end of dnsstub.c */
diff --git a/src/lib/util/getopt.c b/src/lib/util/getopt.c
new file mode 100644
index 000000000..b1737bbc7
--- /dev/null
+++ b/src/lib/util/getopt.c
@@ -0,0 +1,1015 @@
1/* Getopt for GNU.
2 NOTE: getopt is now part of the C library, so if you don't know what
3 "Keep this file name-space clean" means, talk to roland@gnu.ai.mit.edu
4 before changing it!
5
6 Copyright (C) 1987, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97
7 Free Software Foundation, Inc.
8
9 NOTE: The canonical source of this file is maintained with the GNU C Library.
10 Bugs can be reported to bug-glibc@prep.ai.mit.edu.
11
12 This program is free software; you can redistribute it and/or modify it
13 under the terms of the GNU General Public License as published by the
14 Free Software Foundation; either version 3, or (at your option) any
15 later version.
16
17 This program is distributed in the hope that it will be useful,
18 but WITHOUT ANY WARRANTY; without even the implied warranty of
19 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20 GNU General Public License for more details.
21
22 You should have received a copy of the GNU General Public License
23 along with this program; if not, write to the Free Software
24 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301,
25 USA.
26
27
28 This code was heavily modified for GNUnet.
29 Copyright (C) 2006, 2017 Christian Grothoff
30 */
31
32/**
33 * @file util/getopt.c
34 * @brief GNU style option parsing
35 *
36 * TODO: get rid of statics (make reentrant) and
37 * replace main GNU getopt parser with one that
38 * actually fits our API.
39 */
40
41#include "platform.h"
42#include "gnunet_util_lib.h"
43
44#ifdef VMS
45#include <unixlib.h>
46#if HAVE_STRING_H - 0
47#include <string.h>
48#endif
49#endif
50
51#define LOG(kind, ...) GNUNET_log_from (kind, "util-getopt", __VA_ARGS__)
52
53#define LOG_STRERROR(kind, syscall) \
54 GNUNET_log_from_strerror (kind, "util-getopt", syscall)
55
56#ifndef _
57/* This is for other GNU distributions with internationalized messages.
58 When compiling libc, the _ macro is predefined. */
59#ifdef HAVE_LIBINTL_H
60#include <libintl.h>
61#define _(msgid) gettext (msgid)
62#else
63#define _(msgid) (msgid)
64#endif
65#endif
66
67/* Describe the long-named options requested by the application.
68 The LONG_OPTIONS argument to getopt_long or getopt_long_only is a vector
69 of `struct GNoption' terminated by an element containing a name which is
70 zero.
71
72 The field `has_arg' is:
73 no_argument (or 0) if the option does not take an argument,
74 required_argument (or 1) if the option requires an argument,
75 optional_argument (or 2) if the option takes an optional argument.
76
77 If the field `flag' is not NULL, it points to a variable that is set
78 to the value given in the field `val' when the option is found, but
79 left unchanged if the option is not found.
80
81 To have a long-named option do something other than set an `int' to
82 a compiled-in constant, such as set a value from `GNoptarg', set the
83 option's `flag' field to zero and its `val' field to a nonzero
84 value (the equivalent single-letter option character, if there is
85 one). For long options that have a zero `flag' field, `getopt'
86 returns the contents of the `val' field. */
87
88struct GNoption
89{
90 const char *name;
91 /* has_arg can't be an enum because some compilers complain about
92 * type mismatches in all the code that assumes it is an int. */
93 int has_arg;
94 int *flag;
95 int val;
96};
97
98
99/* This version of `getopt' appears to the caller like standard Unix `getopt'
100 but it behaves differently for the user, since it allows the user
101 to intersperse the options with the other arguments.
102
103 As `getopt' works, it permutes the elements of ARGV so that,
104 when it is done, all the options precede everything else. Thus
105 all application programs are extended to handle flexible argument order.
106
107 Setting the environment variable POSIXLY_CORRECT disables permutation.
108 Then the behavior is completely standard.
109
110 GNU application programs can use a third alternative mode in which
111 they can distinguish the relative order of options and other arguments. */
112
113/* For communication from `getopt' to the caller.
114 When `getopt' finds an option that takes an argument,
115 the argument value is returned here.
116 Also, when `ordering' is RETURN_IN_ORDER,
117 each non-option ARGV-element is returned here. */
118
119static char *GNoptarg = NULL;
120
121/* Index in ARGV of the next element to be scanned.
122 This is used for communication to and from the caller
123 and for communication between successive calls to `getopt'.
124
125 On entry to `getopt', zero means this is the first call; initialize.
126
127 When `getopt' returns -1, this is the index of the first of the
128 non-option elements that the caller should itself scan.
129
130 Otherwise, `GNoptind' communicates from one call to the next
131 how much of ARGV has been scanned so far. */
132
133/* 1003.2 says this must be 1 before any call. */
134static int GNoptind = 1;
135
136/* The next char to be scanned in the option-element
137 in which the last option character we returned was found.
138 This allows us to pick up the scan where we left off.
139
140 If this is zero, or a null string, it means resume the scan
141 by advancing to the next ARGV-element. */
142
143static char *nextchar;
144
145
146/* Describe how to deal with options that follow non-option ARGV-elements.
147
148 If the caller did not specify anything,
149 the default is REQUIRE_ORDER if the environment variable
150 POSIXLY_CORRECT is defined, PERMUTE otherwise.
151
152 REQUIRE_ORDER means don't recognize them as options;
153 stop option processing when the first non-option is seen.
154 This is what Unix does.
155 This mode of operation is selected by either setting the environment
156 variable POSIXLY_CORRECT, or using `+' as the first character
157 of the list of option characters.
158
159 PERMUTE is the default. We GNUNET_CRYPTO_random_permute the contents of ARGV as we scan,
160 so that eventually all the non-options are at the end. This allows options
161 to be given in any order, even with programs that were not written to
162 expect this.
163
164 RETURN_IN_ORDER is an option available to programs that were written
165 to expect GNoptions and other ARGV-elements in any order and that care about
166 the ordering of the two. We describe each non-option ARGV-element
167 as if it were the argument of an option with character code 1.
168 Using `-' as the first character of the list of option characters
169 selects this mode of operation.
170
171 The special argument `--' forces an end of option-scanning regardless
172 of the value of `ordering'. In the case of RETURN_IN_ORDER, only
173 `--' can cause `getopt' to return -1 with `GNoptind' != ARGC. */
174
175static enum { REQUIRE_ORDER, PERMUTE, RETURN_IN_ORDER } ordering;
176
177/* Value of POSIXLY_CORRECT environment variable. */
178static char *posixly_correct;
179
180#ifdef __GNU_LIBRARY__
181/* We want to avoid inclusion of string.h with non-GNU libraries
182 because there are many ways it can cause trouble.
183 On some systems, it contains special magic macros that don't work
184 in GCC. */
185#include <string.h>
186#define my_index strchr
187#else
188
189/* Avoid depending on library functions or files
190 whose names are inconsistent. */
191
192char *
193getenv ();
194
195static char *
196my_index (const char *str, int chr)
197{
198 while (*str)
199 {
200 if (*str == chr)
201 return (char *) str;
202 str++;
203 }
204 return 0;
205}
206
207
208/* If using GCC, we can safely declare strlen this way.
209 If not using GCC, it is ok not to declare it. */
210#ifdef __GNUC__
211/* Note that Motorola Delta 68k R3V7 comes with GCC but not stddef.h.
212 That was relevant to code that was here before. */
213#if ! defined(__STDC__) || ! __STDC__
214/* gcc with -traditional declares the built-in strlen to return int,
215 and has done so at least since version 2.4.5. -- rms. */
216extern int
217strlen (const char *);
218
219#endif /* not __STDC__ */
220#endif /* __GNUC__ */
221
222#endif /* not __GNU_LIBRARY__ */
223
224/* Handle permutation of arguments. */
225
226/* Describe the part of ARGV that contains non-options that have
227 been skipped. `first_nonopt' is the index in ARGV of the first of them;
228 `last_nonopt' is the index after the last of them. */
229
230static int first_nonopt;
231static int last_nonopt;
232
233#define SWAP_FLAGS(ch1, ch2)
234
235/* Exchange two adjacent subsequences of ARGV.
236 One subsequence is elements [first_nonopt,last_nonopt)
237 which contains all the non-options that have been skipped so far.
238 The other is elements [last_nonopt,GNoptind), which contains all
239 the options processed since those non-options were skipped.
240
241 `first_nonopt' and `last_nonopt' are relocated so that they describe
242 the new indices of the non-options in ARGV after they are moved. */
243
244#if defined(__STDC__) && __STDC__
245static void
246exchange (char **);
247
248#endif
249
250static void
251exchange (char **argv)
252{
253 int bottom = first_nonopt;
254 int middle = last_nonopt;
255 int top = GNoptind;
256 char *tem;
257
258 /* Exchange the shorter segment with the far end of the longer segment.
259 * That puts the shorter segment into the right place.
260 * It leaves the longer segment in the right place overall,
261 * but it consists of two parts that need to be swapped next. */
262
263 while (top > middle && middle > bottom)
264 {
265 if (top - middle > middle - bottom)
266 {
267 /* Bottom segment is the short one. */
268 int len = middle - bottom;
269 register int i;
270
271 /* Swap it with the top part of the top segment. */
272 for (i = 0; i < len; i++)
273 {
274 tem = argv[bottom + i];
275 argv[bottom + i] = argv[top - (middle - bottom) + i];
276 argv[top - (middle - bottom) + i] = tem;
277 SWAP_FLAGS (bottom + i, top - (middle - bottom) + i);
278 }
279 /* Exclude the moved bottom segment from further swapping. */
280 top -= len;
281 }
282 else
283 {
284 /* Top segment is the short one. */
285 int len = top - middle;
286 register int i;
287
288 /* Swap it with the bottom part of the bottom segment. */
289 for (i = 0; i < len; i++)
290 {
291 tem = argv[bottom + i];
292 argv[bottom + i] = argv[middle + i];
293 argv[middle + i] = tem;
294 SWAP_FLAGS (bottom + i, middle + i);
295 }
296 /* Exclude the moved top segment from further swapping. */
297 bottom += len;
298 }
299 }
300
301 /* Update records for the slots the non-options now occupy. */
302
303 first_nonopt += (GNoptind - last_nonopt);
304 last_nonopt = GNoptind;
305}
306
307
308/* Initialize the internal data when the first call is made. */
309
310#if defined(__STDC__) && __STDC__
311static const char *
312_getopt_initialize (int, char *const *, const char *);
313
314#endif
315static const char *
316_getopt_initialize (int argc, char *const *argv, const char *optstring)
317{
318 /* Start processing options with ARGV-element 1 (since ARGV-element 0
319 * is the program name); the sequence of previously skipped
320 * non-option ARGV-elements is empty. */
321
322 first_nonopt = last_nonopt = GNoptind;
323
324 nextchar = NULL;
325
326 posixly_correct = getenv ("POSIXLY_CORRECT");
327
328 /* Determine how to handle the ordering of options and nonoptions. */
329
330 if (optstring[0] == '-')
331 {
332 ordering = RETURN_IN_ORDER;
333 ++optstring;
334 }
335 else if (optstring[0] == '+')
336 {
337 ordering = REQUIRE_ORDER;
338 ++optstring;
339 }
340 else if (posixly_correct != NULL)
341 ordering = REQUIRE_ORDER;
342 else
343 ordering = PERMUTE;
344
345 return optstring;
346}
347
348
349/* Scan elements of ARGV (whose length is ARGC) for option characters
350 given in OPTSTRING.
351
352 If an element of ARGV starts with '-', and is not exactly "-" or "--",
353 then it is an option element. The characters of this element
354 (aside from the initial '-') are option characters. If `getopt'
355 is called repeatedly, it returns successively each of the option characters
356 from each of the option elements.
357
358 If `getopt' finds another option character, it returns that character,
359 updating `GNoptind' and `nextchar' so that the next call to `getopt' can
360 resume the scan with the following option character or ARGV-element.
361
362 If there are no more option characters, `getopt' returns -1.
363 Then `GNoptind' is the index in ARGV of the first ARGV-element
364 that is not an option. (The ARGV-elements have been permuted
365 so that those that are not options now come last.)
366
367 OPTSTRING is a string containing the legitimate option characters.
368 If an option character is seen that is not listed in OPTSTRING,
369 return '?' after printing an error message. If you set `GNopterr' to
370 zero, the error message is suppressed but we still return '?'.
371
372 If a char in OPTSTRING is followed by a colon, that means it wants an arg,
373 so the following text in the same ARGV-element, or the text of the following
374 ARGV-element, is returned in `GNoptarg'. Two colons mean an option that
375 wants an optional arg; if there is text in the current ARGV-element,
376 it is returned in `GNoptarg', otherwise `GNoptarg' is set to zero.
377
378 If OPTSTRING starts with `-' or `+', it requests different methods of
379 handling the non-option ARGV-elements.
380 See the comments about RETURN_IN_ORDER and REQUIRE_ORDER, above.
381
382 Long-named options begin with `--' instead of `-'.
383 Their names may be abbreviated as long as the abbreviation is unique
384 or is an exact match for some defined option. If they have an
385 argument, it follows the option name in the same ARGV-element, separated
386 from the option name by a `=', or else the in next ARGV-element.
387 When `getopt' finds a long-named option, it returns 0 if that option's
388 `flag' field is nonzero, the value of the option's `val' field
389 if the `flag' field is zero.
390
391 The elements of ARGV aren't really const, because we GNUNET_CRYPTO_random_permute them.
392 But we pretend they're const in the prototype to be compatible
393 with other systems.
394
395 LONGOPTS is a vector of `struct GNoption' terminated by an
396 element containing a name which is zero.
397
398 LONGIND returns the index in LONGOPT of the long-named option found.
399 It is only valid when a long-named option has been found by the most
400 recent call.
401
402 If LONG_ONLY is nonzero, '-' as well as '--' can introduce
403 long-named options. */
404
405static int
406GN_getopt_internal (int argc,
407 char *const *argv,
408 const char *optstring,
409 const struct GNoption *longopts,
410 int *longind,
411 int long_only)
412{
413 static int __getopt_initialized = 0;
414 static int GNopterr = 1;
415
416 GNoptarg = NULL;
417
418 if ((GNoptind == 0) || ! __getopt_initialized)
419 {
420 if (GNoptind == 0)
421 GNoptind = 1; /* Don't scan ARGV[0], the program name. */
422 optstring = _getopt_initialize (argc, argv, optstring);
423 __getopt_initialized = 1;
424 }
425
426 /* Test whether ARGV[GNoptind] points to a non-option argument.
427 * Either it does not have option syntax, or there is an environment flag
428 * from the shell indicating it is not an option. The later information
429 * is only used when the used in the GNU libc. */
430#define NONOPTION_P (argv[GNoptind][0] != '-' || argv[GNoptind][1] == '\0')
431
432 if ((nextchar == NULL) || (*nextchar == '\0'))
433 {
434 /* Advance to the next ARGV-element. */
435
436 /* Give FIRST_NONOPT & LAST_NONOPT rational values if GNoptind has been
437 * moved back by the user (who may also have changed the arguments). */
438 if (last_nonopt > GNoptind)
439 last_nonopt = GNoptind;
440 if (first_nonopt > GNoptind)
441 first_nonopt = GNoptind;
442
443 if (ordering == PERMUTE)
444 {
445 /* If we have just processed some options following some non-options,
446 * exchange them so that the options come first. */
447
448 if ((first_nonopt != last_nonopt) && (last_nonopt != GNoptind) )
449 exchange ((char **) argv);
450 else if (last_nonopt != GNoptind)
451 first_nonopt = GNoptind;
452
453 /* Skip any additional non-options
454 * and extend the range of non-options previously skipped. */
455
456 while (GNoptind < argc && NONOPTION_P)
457 GNoptind++;
458 last_nonopt = GNoptind;
459 }
460
461 /* The special ARGV-element `--' means premature end of options.
462 * Skip it like a null option,
463 * then exchange with previous non-options as if it were an option,
464 * then skip everything else like a non-option. */
465 if ((GNoptind != argc) && ! strcmp (argv[GNoptind], "--"))
466 {
467 GNoptind++;
468
469 if ((first_nonopt != last_nonopt) && (last_nonopt != GNoptind) )
470 exchange ((char **) argv);
471 else if (first_nonopt == last_nonopt)
472 first_nonopt = GNoptind;
473 last_nonopt = argc;
474
475 GNoptind = argc;
476 }
477
478 /* If we have done all the ARGV-elements, stop the scan
479 * and back over any non-options that we skipped and permuted. */
480
481 if (GNoptind == argc)
482 {
483 /* Set the next-arg-index to point at the non-options
484 * that we previously skipped, so the caller will digest them. */
485 if (first_nonopt != last_nonopt)
486 GNoptind = first_nonopt;
487 return -1;
488 }
489
490 /* If we have come to a non-option and did not permute it,
491 * either stop the scan or describe it to the caller and pass it by. */
492
493 if (NONOPTION_P)
494 {
495 if (ordering == REQUIRE_ORDER)
496 return -1;
497 GNoptarg = argv[GNoptind++];
498 return 1;
499 }
500
501 /* We have found another option-ARGV-element.
502 * Skip the initial punctuation. */
503
504 nextchar =
505 (argv[GNoptind] + 1 + (longopts != NULL && argv[GNoptind][1] == '-'));
506 }
507
508 /* Decode the current option-ARGV-element. */
509
510 /* Check whether the ARGV-element is a long option.
511 *
512 * If long_only and the ARGV-element has the form "-f", where f is
513 * a valid short option, don't consider it an abbreviated form of
514 * a long option that starts with f. Otherwise there would be no
515 * way to give the -f short option.
516 *
517 * On the other hand, if there's a long option "fubar" and
518 * the ARGV-element is "-fu", do consider that an abbreviation of
519 * the long option, just like "--fu", and not "-f" with arg "u".
520 *
521 * This distinction seems to be the most useful approach. */if ((longopts != NULL) &&
522 ((argv[GNoptind][1] == '-') ||
523 (long_only &&
524 (argv[GNoptind][2] || ! my_index (optstring, argv[GNoptind][1])))))
525 {
526 char *nameend;
527 const struct GNoption *p;
528 const struct GNoption *pfound = NULL;
529 int exact = 0;
530 int ambig = 0;
531 int indfound = -1;
532 int option_index;
533
534 for (nameend = nextchar; *nameend && *nameend != '='; nameend++)
535 /* Do nothing. */;
536
537 /* Test all long options for either exact match
538 * or abbreviated matches. */
539 for (p = longopts, option_index = 0; p->name; p++, option_index++)
540 if (! strncmp (p->name, nextchar, nameend - nextchar))
541 {
542 if ((unsigned int) (nameend - nextchar) ==
543 (unsigned int) strlen (p->name))
544 {
545 /* Exact match found. */
546 pfound = p;
547 indfound = option_index;
548 exact = 1;
549 break;
550 }
551 else if (pfound == NULL)
552 {
553 /* First nonexact match found. */
554 pfound = p;
555 indfound = option_index;
556 }
557 else
558 /* Second or later nonexact match found. */
559 ambig = 1;
560 }
561
562 if (ambig && ! exact)
563 {
564 if (GNopterr)
565 fprintf (stderr,
566 _ ("%s: option `%s' is ambiguous\n"),
567 argv[0],
568 argv[GNoptind]);
569 nextchar += strlen (nextchar);
570 GNoptind++;
571 return '?';
572 }
573
574 if (pfound != NULL)
575 {
576 option_index = indfound;
577 GNoptind++;
578 if (*nameend)
579 {
580 /* Don't test has_arg with >, because some C compilers don't
581 * allow it to be used on enums. */
582 if (pfound->has_arg)
583 GNoptarg = nameend + 1;
584 else
585 {
586 if (GNopterr)
587 {
588 if (argv[GNoptind - 1][1] == '-')
589 /* --option */
590 fprintf (stderr,
591 _ ("%s: option `--%s' does not allow an argument\n"),
592 argv[0],
593 pfound->name);
594 else
595 /* +option or -option */
596 fprintf (stderr,
597 _ ("%s: option `%c%s' does not allow an argument\n"),
598 argv[0],
599 argv[GNoptind - 1][0],
600 pfound->name);
601 }
602 nextchar += strlen (nextchar);
603 return '?';
604 }
605 }
606 else if (pfound->has_arg == 1)
607 {
608 if (GNoptind < argc)
609 {
610 GNoptarg = argv[GNoptind++];
611 }
612 else
613 {
614 if (GNopterr)
615 {
616 fprintf (stderr,
617 _ ("%s: option `%s' requires an argument\n"),
618 argv[0],
619 argv[GNoptind - 1]);
620 }
621 nextchar += strlen (nextchar);
622 return (optstring[0] == ':') ? ':' : '?';
623 }
624 }
625 nextchar += strlen (nextchar);
626 if (longind != NULL)
627 *longind = option_index;
628 if (pfound->flag)
629 {
630 *(pfound->flag) = pfound->val;
631 return 0;
632 }
633 return pfound->val;
634 }
635
636 /* Can't find it as a long option. If this is not getopt_long_only,
637 * or the option starts with '--' or is not a valid short
638 * option, then it's an error.
639 * Otherwise interpret it as a short option. */
640 if (! long_only || (argv[GNoptind][1] == '-') ||
641 (my_index (optstring, *nextchar) == NULL) )
642 {
643 if (GNopterr)
644 {
645 if (argv[GNoptind][1] == '-')
646 /* --option */
647 fprintf (stderr,
648 _ ("%s: unrecognized option `--%s'\n"),
649 argv[0],
650 nextchar);
651 else
652 /* +option or -option */
653 fprintf (stderr,
654 _ ("%s: unrecognized option `%c%s'\n"),
655 argv[0],
656 argv[GNoptind][0],
657 nextchar);
658 }
659 nextchar = (char *) "";
660 GNoptind++;
661 return '?';
662 }
663 }
664
665 /* Look at and handle the next short option-character. */
666
667 {
668 char c = *nextchar++;
669 char *temp = my_index (optstring, c);
670
671 /* Increment `GNoptind' when we start to process its last character. */
672 if (*nextchar == '\0')
673 ++GNoptind;
674
675 if ((temp == NULL) || (c == ':'))
676 {
677 if (GNopterr)
678 {
679 if (posixly_correct)
680 /* 1003.2 specifies the format of this message. */
681 fprintf (stderr, _ ("%s: illegal option -- %c\n"), argv[0], c);
682 else
683 fprintf (stderr, _ ("%s: invalid option -- %c\n"), argv[0], c);
684 }
685 return '?';
686 }
687 /* Convenience. Treat POSIX -W foo same as long option --foo */
688 if ((temp[0] == 'W') && (temp[1] == ';'))
689 {
690 char *nameend;
691 const struct GNoption *p;
692 const struct GNoption *pfound = NULL;
693 int exact = 0;
694 int ambig = 0;
695 int indfound = 0;
696 int option_index;
697
698 /* This is an option that requires an argument. */
699 if (*nextchar != '\0')
700 {
701 GNoptarg = nextchar;
702 /* If we end this ARGV-element by taking the rest as an arg,
703 * we must advance to the next element now. */
704 GNoptind++;
705 }
706 else if (GNoptind == argc)
707 {
708 if (GNopterr)
709 {
710 /* 1003.2 specifies the format of this message. */
711 fprintf (stderr,
712 _ ("%s: option requires an argument -- %c\n"),
713 argv[0],
714 c);
715 }
716 if (optstring[0] == ':')
717 c = ':';
718 else
719 c = '?';
720 return c;
721 }
722 else
723 /* We already incremented `GNoptind' once;
724 * increment it again when taking next ARGV-elt as argument. */
725 GNoptarg = argv[GNoptind++];
726
727 /* GNoptarg is now the argument, see if it's in the
728 * table of longopts. */
729
730 for (nextchar = nameend = GNoptarg; *nameend && *nameend != '=';
731 nameend++)
732 /* Do nothing. */;
733
734 /* Test all long options for either exact match
735 * or abbreviated matches. */
736 if (longopts != NULL)
737 for (p = longopts, option_index = 0; p->name; p++, option_index++)
738 if (! strncmp (p->name, nextchar, nameend - nextchar))
739 {
740 if ((unsigned int) (nameend - nextchar) == strlen (p->name))
741 {
742 /* Exact match found. */
743 pfound = p;
744 indfound = option_index;
745 exact = 1;
746 break;
747 }
748 else if (pfound == NULL)
749 {
750 /* First nonexact match found. */
751 pfound = p;
752 indfound = option_index;
753 }
754 else
755 /* Second or later nonexact match found. */
756 ambig = 1;
757 }
758 if (ambig && ! exact)
759 {
760 if (GNopterr)
761 fprintf (stderr,
762 _ ("%s: option `-W %s' is ambiguous\n"),
763 argv[0],
764 argv[GNoptind]);
765 nextchar += strlen (nextchar);
766 GNoptind++;
767 return '?';
768 }
769 if (pfound != NULL)
770 {
771 option_index = indfound;
772 if (*nameend)
773 {
774 /* Don't test has_arg with >, because some C compilers don't
775 * allow it to be used on enums. */
776 if (pfound->has_arg)
777 GNoptarg = nameend + 1;
778 else
779 {
780 if (GNopterr)
781 fprintf (stderr,
782 _ ("%s: option `-W %s' does not allow an argument\n"),
783 argv[0],
784 pfound->name);
785
786 nextchar += strlen (nextchar);
787 return '?';
788 }
789 }
790 else if (pfound->has_arg == 1)
791 {
792 if (GNoptind < argc)
793 GNoptarg = argv[GNoptind++];
794 else
795 {
796 if (GNopterr)
797 fprintf (stderr,
798 _ ("%s: option `%s' requires an argument\n"),
799 argv[0],
800 argv[GNoptind - 1]);
801 nextchar += strlen (nextchar);
802 return optstring[0] == ':' ? ':' : '?';
803 }
804 }
805 nextchar += strlen (nextchar);
806 if (longind != NULL)
807 *longind = option_index;
808 if (pfound->flag)
809 {
810 *(pfound->flag) = pfound->val;
811 return 0;
812 }
813 return pfound->val;
814 }
815 nextchar = NULL;
816 return 'W'; /* Let the application handle it. */
817 }
818 if (temp[1] == ':')
819 {
820 if (temp[2] == ':')
821 {
822 /* This is an option that accepts an argument optionally. */
823 if (*nextchar != '\0')
824 {
825 GNoptarg = nextchar;
826 GNoptind++;
827 }
828 else
829 GNoptarg = NULL;
830 nextchar = NULL;
831 }
832 else
833 {
834 /* This is an option that requires an argument. */
835 if (*nextchar != '\0')
836 {
837 GNoptarg = nextchar;
838 /* If we end this ARGV-element by taking the rest as an arg,
839 * we must advance to the next element now. */
840 GNoptind++;
841 }
842 else if (GNoptind == argc)
843 {
844 if (GNopterr)
845 {
846 /* 1003.2 specifies the format of this message. */
847 fprintf (stderr,
848 _ ("%s: option requires an argument -- %c\n"),
849 argv[0],
850 c);
851 }
852 if (optstring[0] == ':')
853 c = ':';
854 else
855 c = '?';
856 }
857 else
858 /* We already incremented `GNoptind' once;
859 * increment it again when taking next ARGV-elt as argument. */
860 GNoptarg = argv[GNoptind++];
861 nextchar = NULL;
862 }
863 }
864 return c;
865 }
866}
867
868
869static int
870GNgetopt_long (int argc,
871 char *const *argv,
872 const char *options,
873 const struct GNoption *long_options,
874 int *opt_index)
875{
876 return GN_getopt_internal (argc, argv, options, long_options, opt_index, 0);
877}
878
879
880/* ******************** now the GNUnet specific modifications... ********************* */
881
882
883int
884GNUNET_GETOPT_run (const char *binaryOptions,
885 const struct GNUNET_GETOPT_CommandLineOption *allOptions,
886 unsigned int argc,
887 char *const *argv)
888{
889 struct GNoption *long_options;
890 struct GNUNET_GETOPT_CommandLineProcessorContext clpc;
891 int count;
892 char *shorts;
893 int spos;
894 enum GNUNET_GenericReturnValue cont;
895 uint8_t *seen;
896 unsigned int optmatch = 0;
897 const char *have_exclusive = NULL;
898
899 GNUNET_assert (argc > 0);
900 GNoptind = 0;
901 clpc.binaryName = argv[0];
902 clpc.binaryOptions = binaryOptions;
903 clpc.allOptions = allOptions;
904 clpc.argv = argv;
905 clpc.argc = argc;
906 for (count = 0; NULL != allOptions[count].name; count++)
907 ;
908
909 /* transform our option representation into the format
910 used by the GNU getopt copylib */
911 long_options = GNUNET_new_array (count + 1, struct GNoption);
912 seen = GNUNET_new_array (count, uint8_t);
913 shorts = GNUNET_malloc (count * 2 + 1);
914 spos = 0;
915 for (unsigned i = 0; i < count; i++)
916 {
917 long_options[i].name = allOptions[i].name;
918 long_options[i].has_arg = allOptions[i].require_argument;
919 long_options[i].flag = NULL;
920 long_options[i].val = allOptions[i].shortName;
921 shorts[spos++] = allOptions[i].shortName;
922 if (allOptions[i].require_argument != 0)
923 shorts[spos++] = ':';
924 }
925 long_options[count].name = NULL;
926 long_options[count].has_arg = 0;
927 long_options[count].flag = NULL;
928 long_options[count].val = '\0';
929 shorts[spos] = '\0';
930 cont = GNUNET_OK;
931
932 /* main getopt loop */
933 while (1)
934 {
935 int option_index = 0;
936 unsigned int i;
937 int c;
938
939 c = GNgetopt_long (argc,
940 argv,
941 shorts,
942 long_options,
943 &option_index);
944 if (c == GNUNET_SYSERR)
945 break; /* No more flags to process */
946
947 /* Check which of our program's options was given by the user */
948 for (i = 0; i < count; i++)
949 {
950 clpc.currentArgument = GNoptind - 1;
951 if ((char) c == allOptions[i].shortName)
952 {
953 optmatch++;
954 if (allOptions[i].option_exclusive)
955 have_exclusive = allOptions[i].name;
956 if (GNUNET_OK == cont)
957 {
958 /* parse the option using the option-specific processor */
959 cont = allOptions[i].processor (&clpc,
960 allOptions[i].scls,
961 allOptions[i].name,
962 GNoptarg);
963 }
964 seen[i] = 1;
965 break;
966 }
967 }
968 if (i == count)
969 {
970 fprintf (stderr,
971 _ ("Use %s to get a list of options.\n"),
972 "--help");
973 cont = GNUNET_SYSERR;
974 }
975 }
976 GNUNET_free (shorts);
977 GNUNET_free (long_options);
978
979 /* check that if any option that was marked as exclusive
980 is the only option that was provided */
981 if ((NULL != have_exclusive) && (optmatch > 1))
982 {
983 fprintf (stderr,
984 _ ("Option `%s' can't be used with other options.\n"),
985 have_exclusive);
986 cont = GNUNET_SYSERR;
987 }
988 if (GNUNET_YES == cont)
989 {
990 /* check that all mandatory options are present */
991 for (count = 0; NULL != allOptions[count].name; count++)
992 {
993 if ((0 == seen[count]) && (allOptions[count].option_mandatory))
994 {
995 fprintf (stderr,
996 _ ("Missing mandatory option `%s'.\n"),
997 allOptions[count].name);
998 cont = GNUNET_SYSERR;
999 }
1000 }
1001 }
1002 GNUNET_free (seen);
1003
1004 /* call cleaners, if available */
1005 for (unsigned int i = 0; NULL != allOptions[i].name; i++)
1006 if (NULL != allOptions[i].cleaner)
1007 allOptions[i].cleaner (allOptions[i].scls);
1008
1009 if (GNUNET_OK != cont)
1010 return cont;
1011 return GNoptind;
1012}
1013
1014
1015/* end of getopt.c */
diff --git a/src/lib/util/getopt_helpers.c b/src/lib/util/getopt_helpers.c
new file mode 100644
index 000000000..31020f185
--- /dev/null
+++ b/src/lib/util/getopt_helpers.c
@@ -0,0 +1,1017 @@
1/*
2 This file is part of GNUnet
3 Copyright (C) 2006, 2011, 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 src/util/getopt_helpers.c
23 * @brief implements command line that sets option
24 * @author Christian Grothoff
25 */
26
27#include "platform.h"
28#include "gnunet_util_lib.h"
29
30#define LOG(kind, ...) GNUNET_log_from (kind, "util-getopt", __VA_ARGS__)
31
32
33/**
34 * Print out program version (implements --version).
35 *
36 * @param ctx command line processing context
37 * @param scls additional closure (points to version string)
38 * @param option name of the option
39 * @param value not used (NULL)
40 * @return #GNUNET_NO (do not continue, not an error)
41 */
42static enum GNUNET_GenericReturnValue
43print_version (struct GNUNET_GETOPT_CommandLineProcessorContext *ctx,
44 void *scls,
45 const char *option,
46 const char *value)
47{
48 const char *version = scls;
49
50 (void) option;
51 (void) value;
52 printf ("%s v%s\n", ctx->binaryName, version);
53 return GNUNET_NO;
54}
55
56
57struct GNUNET_GETOPT_CommandLineOption
58GNUNET_GETOPT_option_version (const char *version)
59{
60 struct GNUNET_GETOPT_CommandLineOption clo = {
61 .shortName = 'v',
62 .name = "version",
63 .description = gettext_noop (
64 "print the version number"),
65 .option_exclusive = 1,
66 .processor = &print_version,
67 .scls = (void *) version
68 };
69
70 return clo;
71}
72
73
74/**
75 * At what offset does the help text start?
76 */
77#define BORDER 29
78
79/**
80 * Print out details on command line options (implements --help).
81 *
82 * @param ctx command line processing context
83 * @param scls additional closure (points to about text)
84 * @param option name of the option
85 * @param value not used (NULL)
86 * @return #GNUNET_NO (do not continue, not an error)
87 */
88static enum GNUNET_GenericReturnValue
89format_help (struct GNUNET_GETOPT_CommandLineProcessorContext *ctx,
90 void *scls,
91 const char *option,
92 const char *value)
93{
94 const char *about = scls;
95 size_t slen;
96 unsigned int i;
97 int j;
98 size_t ml;
99 size_t p;
100 char *scp;
101 const char *trans;
102 const struct GNUNET_GETOPT_CommandLineOption *opt;
103 const struct GNUNET_OS_ProjectData *pd;
104
105 (void) option;
106 (void) value;
107 if (NULL != about)
108 {
109 printf ("%s\n%s\n", ctx->binaryOptions, gettext (about));
110 printf (_ (
111 "Arguments mandatory for long options are also mandatory for short options.\n"));
112 }
113 i = 0;
114 opt = ctx->allOptions;
115 while (NULL != opt[i].description)
116 {
117 if (opt[i].shortName == '\0')
118 printf (" ");
119 else
120 printf (" -%c, ", opt[i].shortName);
121 printf ("--%s", opt[i].name);
122 slen = 8 + strlen (opt[i].name);
123 if (NULL != opt[i].argumentHelp)
124 {
125 printf ("=%s", opt[i].argumentHelp);
126 slen += 1 + strlen (opt[i].argumentHelp);
127 }
128 if (slen > BORDER)
129 {
130 printf ("\n%*s", BORDER, "");
131 slen = BORDER;
132 }
133 if (slen < BORDER)
134 {
135 printf ("%*s", (int) (BORDER - slen), "");
136 slen = BORDER;
137 }
138 if (0 < strlen (opt[i].description))
139 trans = gettext (opt[i].description);
140 else
141 trans = "";
142 ml = strlen (trans);
143 p = 0;
144OUTER:
145 while (ml - p > 78 - slen)
146 {
147 for (j = p + 78 - slen; j > (int) p; j--)
148 {
149 if (isspace ((unsigned char) trans[j]))
150 {
151 scp = GNUNET_malloc (j - p + 1);
152 GNUNET_memcpy (scp, &trans[p], j - p);
153 scp[j - p] = '\0';
154 printf ("%s\n%*s", scp, BORDER + 2, "");
155 GNUNET_free (scp);
156 p = j + 1;
157 slen = BORDER + 2;
158 goto OUTER;
159 }
160 }
161 /* could not find space to break line */
162 scp = GNUNET_malloc (78 - slen + 1);
163 GNUNET_memcpy (scp, &trans[p], 78 - slen);
164 scp[78 - slen] = '\0';
165 printf ("%s\n%*s", scp, BORDER + 2, "");
166 GNUNET_free (scp);
167 slen = BORDER + 2;
168 p = p + 78 - slen;
169 }
170 /* print rest */
171 if (p < ml)
172 printf ("%s\n", &trans[p]);
173 if (strlen (trans) == 0)
174 printf ("\n");
175 i++;
176 }
177 pd = GNUNET_OS_project_data_get ();
178 printf ("\n"
179 "Report bugs to %s.\n"
180 "Home page: %s\n",
181 pd->bug_email,
182 pd->homepage);
183
184 if (0 != pd->is_gnu)
185 printf ("General help using GNU software: http://www.gnu.org/gethelp/\n");
186
187 return GNUNET_NO;
188}
189
190
191struct GNUNET_GETOPT_CommandLineOption
192GNUNET_GETOPT_option_help (const char *about)
193{
194 struct GNUNET_GETOPT_CommandLineOption clo = {
195 .shortName = 'h',
196 .name = "help",
197 .description = gettext_noop (
198 "print this help"),
199 .option_exclusive = 1,
200 .processor = format_help,
201 .scls = (void *) about
202 };
203
204 return clo;
205}
206
207
208/**
209 * Set an option of type 'unsigned int' from the command line. Each
210 * time the option flag is given, the value is incremented by one.
211 * A pointer to this function should be passed as part of the
212 * 'struct GNUNET_GETOPT_CommandLineOption' array to initialize options
213 * of this type. It should be followed by a pointer to a value of
214 * type 'int'.
215 *
216 * @param ctx command line processing context
217 * @param scls additional closure (will point to the 'unsigned int')
218 * @param option name of the option
219 * @param value not used (NULL)
220 * @return #GNUNET_OK
221 */
222static enum GNUNET_GenericReturnValue
223increment_value (struct GNUNET_GETOPT_CommandLineProcessorContext *ctx,
224 void *scls,
225 const char *option,
226 const char *value)
227{
228 unsigned int *val = scls;
229
230 (void) ctx;
231 (void) option;
232 (void) value;
233 (*val)++;
234 return GNUNET_OK;
235}
236
237
238struct GNUNET_GETOPT_CommandLineOption
239GNUNET_GETOPT_option_increment_uint (char shortName,
240 const char *name,
241 const char *description,
242 unsigned int *val)
243{
244 struct GNUNET_GETOPT_CommandLineOption clo = {
245 .shortName = shortName,
246 .name = name,
247 .description = description,
248 .processor = &increment_value,
249 .scls = (void *) val
250 };
251
252 return clo;
253}
254
255
256struct GNUNET_GETOPT_CommandLineOption
257GNUNET_GETOPT_option_verbose (unsigned int *level)
258{
259 struct GNUNET_GETOPT_CommandLineOption clo = {
260 .shortName = 'V',
261 .name = "verbose",
262 .description =
263 gettext_noop ("be verbose"),
264 .processor = &increment_value,
265 .scls = (void *) level
266 };
267
268 return clo;
269}
270
271
272/**
273 * Set an option of type 'int' from the command line to 1 if the
274 * given option is present.
275 * A pointer to this function should be passed as part of the
276 * 'struct GNUNET_GETOPT_CommandLineOption' array to initialize options
277 * of this type. It should be followed by a pointer to a value of
278 * type 'int'.
279 *
280 * @param ctx command line processing context
281 * @param scls additional closure (will point to the 'int')
282 * @param option name of the option
283 * @param value not used (NULL)
284 * @return #GNUNET_OK
285 */
286static enum GNUNET_GenericReturnValue
287set_one (struct GNUNET_GETOPT_CommandLineProcessorContext *ctx,
288 void *scls,
289 const char *option,
290 const char *value)
291{
292 int *val = scls;
293
294 (void) ctx;
295 (void) option;
296 (void) value;
297 *val = 1;
298 return GNUNET_OK;
299}
300
301
302struct GNUNET_GETOPT_CommandLineOption
303GNUNET_GETOPT_option_flag (char shortName,
304 const char *name,
305 const char *description,
306 int *val)
307{
308 struct GNUNET_GETOPT_CommandLineOption clo = {
309 .shortName = shortName,
310 .name = name,
311 .description = description,
312 .processor = &set_one,
313 .scls = (void *) val
314 };
315
316 return clo;
317}
318
319
320/**
321 * Set an option of type 'char *' from the command line.
322 * A pointer to this function should be passed as part of the
323 * 'struct GNUNET_GETOPT_CommandLineOption' array to initialize options
324 * of this type. It should be followed by a pointer to a value of
325 * type 'char *', which will be allocated with the requested string.
326 *
327 * @param ctx command line processing context
328 * @param scls additional closure (will point to the 'char *',
329 * which will be allocated)
330 * @param option name of the option
331 * @param value actual value of the option (a string)
332 * @return #GNUNET_OK
333 */
334static enum GNUNET_GenericReturnValue
335set_string (struct GNUNET_GETOPT_CommandLineProcessorContext *ctx,
336 void *scls,
337 const char *option,
338 const char *value)
339{
340 char **val = scls;
341
342 (void) ctx;
343 (void) option;
344 GNUNET_assert (NULL != value);
345 GNUNET_free (*val);
346 *val = GNUNET_strdup (value);
347 return GNUNET_OK;
348}
349
350
351struct GNUNET_GETOPT_CommandLineOption
352GNUNET_GETOPT_option_string (char shortName,
353 const char *name,
354 const char *argumentHelp,
355 const char *description,
356 char **str)
357{
358 struct GNUNET_GETOPT_CommandLineOption clo = {
359 .shortName = shortName,
360 .name = name,
361 .argumentHelp = argumentHelp,
362 .description = description,
363 .require_argument = 1,
364 .processor = &set_string,
365 .scls = (void *) str
366 };
367
368 return clo;
369}
370
371
372struct GNUNET_GETOPT_CommandLineOption
373GNUNET_GETOPT_option_loglevel (char **level)
374{
375 struct GNUNET_GETOPT_CommandLineOption clo = {
376 .shortName = 'L',
377 .name = "log",
378 .argumentHelp = "LOGLEVEL",
379 .description = gettext_noop ("configure logging to use LOGLEVEL"),
380 .require_argument = 1,
381 .processor = &set_string,
382 .scls = (void *) level
383 };
384
385 return clo;
386}
387
388
389/**
390 * Set an option of type 'char *' from the command line with
391 * filename expansion a la #GNUNET_STRINGS_filename_expand().
392 *
393 * @param ctx command line processing context
394 * @param scls additional closure (will point to the `char *`,
395 * which will be allocated)
396 * @param option name of the option
397 * @param value actual value of the option (a string)
398 * @return #GNUNET_OK
399 */
400static enum GNUNET_GenericReturnValue
401set_filename (struct GNUNET_GETOPT_CommandLineProcessorContext *ctx,
402 void *scls,
403 const char *option,
404 const char *value)
405{
406 char **val = scls;
407
408 (void) ctx;
409 (void) option;
410 GNUNET_assert (NULL != value);
411 GNUNET_free (*val);
412 *val = GNUNET_STRINGS_filename_expand (value);
413 return GNUNET_OK;
414}
415
416
417struct GNUNET_GETOPT_CommandLineOption
418GNUNET_GETOPT_option_filename (char shortName,
419 const char *name,
420 const char *argumentHelp,
421 const char *description,
422 char **str)
423{
424 struct GNUNET_GETOPT_CommandLineOption clo = {
425 .shortName = shortName,
426 .name = name,
427 .argumentHelp = argumentHelp,
428 .description = description,
429 .require_argument = 1,
430 .processor = &set_filename,
431 .scls = (void *) str
432 };
433
434 return clo;
435}
436
437
438struct GNUNET_GETOPT_CommandLineOption
439GNUNET_GETOPT_option_logfile (char **logfn)
440{
441 struct GNUNET_GETOPT_CommandLineOption clo = {
442 .shortName = 'l',
443 .name = "logfile",
444 .argumentHelp = "FILENAME",
445 .description =
446 gettext_noop ("configure logging to write logs to FILENAME"),
447 .require_argument = 1,
448 .processor = &set_filename,
449 .scls = (void *) logfn
450 };
451
452 return clo;
453}
454
455
456struct GNUNET_GETOPT_CommandLineOption
457GNUNET_GETOPT_option_cfgfile (char **fn)
458{
459 struct GNUNET_GETOPT_CommandLineOption clo = {
460 .shortName = 'c',
461 .name = "config",
462 .argumentHelp = "FILENAME",
463 .description = gettext_noop ("use configuration file FILENAME"),
464 .require_argument = 1,
465 .processor = &set_filename,
466 .scls = (void *) fn
467 };
468
469 return clo;
470}
471
472
473/**
474 * Set an option of type 'unsigned long long' from the command line.
475 * A pointer to this function should be passed as part of the
476 * 'struct GNUNET_GETOPT_CommandLineOption' array to initialize options
477 * of this type. It should be followed by a pointer to a value of
478 * type 'unsigned long long'.
479 *
480 * @param ctx command line processing context
481 * @param scls additional closure (will point to the 'unsigned long long')
482 * @param option name of the option
483 * @param value actual value of the option as a string.
484 * @return #GNUNET_OK if parsing the value worked
485 */
486static enum GNUNET_GenericReturnValue
487set_ulong (struct GNUNET_GETOPT_CommandLineProcessorContext *ctx,
488 void *scls,
489 const char *option,
490 const char *value)
491{
492 unsigned long long *val = scls;
493 char dummy[2];
494
495 (void) ctx;
496 if (1 != sscanf (value, "%llu%1s", val, dummy))
497 {
498 fprintf (stderr,
499 _ ("You must pass a number to the `%s' option.\n"),
500 option);
501 return GNUNET_SYSERR;
502 }
503 return GNUNET_OK;
504}
505
506
507struct GNUNET_GETOPT_CommandLineOption
508GNUNET_GETOPT_option_ulong (char shortName,
509 const char *name,
510 const char *argumentHelp,
511 const char *description,
512 unsigned long long *val)
513{
514 struct GNUNET_GETOPT_CommandLineOption clo = {
515 .shortName = shortName,
516 .name = name,
517 .argumentHelp = argumentHelp,
518 .description = description,
519 .require_argument = 1,
520 .processor = &set_ulong,
521 .scls = (void *) val
522 };
523
524 return clo;
525}
526
527
528/**
529 * Set an option of type 'struct GNUNET_TIME_Relative' from the command line.
530 * A pointer to this function should be passed as part of the
531 * 'struct GNUNET_GETOPT_CommandLineOption' array to initialize options
532 * of this type. It should be followed by a pointer to a value of
533 * type 'struct GNUNET_TIME_Relative'.
534 *
535 * @param ctx command line processing context
536 * @param scls additional closure (will point to the 'struct GNUNET_TIME_Relative')
537 * @param option name of the option
538 * @param value actual value of the option as a string.
539 * @return #GNUNET_OK if parsing the value worked
540 */
541static enum GNUNET_GenericReturnValue
542set_timetravel_time (struct GNUNET_GETOPT_CommandLineProcessorContext *ctx,
543 void *scls,
544 const char *option,
545 const char *value)
546{
547 long long delta;
548 long long minus;
549 struct GNUNET_TIME_Relative rt;
550
551 (void) scls;
552 (void) ctx;
553 while (isspace (value[0]))
554 value++;
555 minus = 1;
556 if (value[0] == '-')
557 {
558 minus = -1;
559 value++;
560 }
561 else if (value[0] == '+')
562 {
563 value++;
564 }
565 if (GNUNET_OK !=
566 GNUNET_STRINGS_fancy_time_to_relative (value,
567 &rt))
568 {
569 fprintf (stderr,
570 _ (
571 "You must pass a relative time (optionally with sign) to the `%s' option.\n"),
572 option);
573 return GNUNET_SYSERR;
574 }
575 if (rt.rel_value_us > LLONG_MAX)
576 {
577 fprintf (stderr,
578 _ ("Value given for time travel `%s' option is too big.\n"),
579 option);
580 return GNUNET_SYSERR;
581 }
582 delta = (long long) rt.rel_value_us;
583 delta *= minus;
584 GNUNET_TIME_set_offset (delta);
585 return GNUNET_OK;
586}
587
588
589struct GNUNET_GETOPT_CommandLineOption
590GNUNET_GETOPT_option_timetravel (char shortName,
591 const char *name)
592{
593 struct GNUNET_GETOPT_CommandLineOption clo = {
594 .shortName = shortName,
595 .name = name,
596 .argumentHelp = _ ("[+/-]MICROSECONDS"),
597 .description = _ (
598 "modify system time by given offset (for debugging/testing only)"),
599 .require_argument = 1,
600 .processor = &set_timetravel_time
601 };
602
603 return clo;
604}
605
606
607/**
608 * Set an option of type 'struct GNUNET_TIME_Relative' from the command line.
609 * A pointer to this function should be passed as part of the
610 * 'struct GNUNET_GETOPT_CommandLineOption' array to initialize options
611 * of this type. It should be followed by a pointer to a value of
612 * type 'struct GNUNET_TIME_Relative'.
613 *
614 * @param ctx command line processing context
615 * @param scls additional closure (will point to the 'struct GNUNET_TIME_Relative')
616 * @param option name of the option
617 * @param value actual value of the option as a string.
618 * @return #GNUNET_OK if parsing the value worked
619 */
620static enum GNUNET_GenericReturnValue
621set_relative_time (struct GNUNET_GETOPT_CommandLineProcessorContext *ctx,
622 void *scls,
623 const char *option,
624 const char *value)
625{
626 struct GNUNET_TIME_Relative *val = scls;
627
628 (void) ctx;
629 if (GNUNET_OK != GNUNET_STRINGS_fancy_time_to_relative (value, val))
630 {
631 fprintf (stderr,
632 _ ("You must pass relative time to the `%s' option.\n"),
633 option);
634 return GNUNET_SYSERR;
635 }
636 return GNUNET_OK;
637}
638
639
640struct GNUNET_GETOPT_CommandLineOption
641GNUNET_GETOPT_option_relative_time (char shortName,
642 const char *name,
643 const char *argumentHelp,
644 const char *description,
645 struct GNUNET_TIME_Relative *val)
646{
647 struct GNUNET_GETOPT_CommandLineOption clo = {
648 .shortName = shortName,
649 .name = name,
650 .argumentHelp = argumentHelp,
651 .description = description,
652 .require_argument = 1,
653 .processor = &set_relative_time,
654 .scls = (void *) val
655 };
656
657 return clo;
658}
659
660
661/**
662 * Set an option of type 'struct GNUNET_TIME_Absolute' from the command line.
663 * A pointer to this function should be passed as part of the
664 * 'struct GNUNET_GETOPT_CommandLineOption' array to initialize options
665 * of this type. It should be followed by a pointer to a value of
666 * type 'struct GNUNET_TIME_Absolute'.
667 *
668 * @param ctx command line processing context
669 * @param scls additional closure (will point to the `struct GNUNET_TIME_Absolute`)
670 * @param option name of the option
671 * @param value actual value of the option as a string.
672 * @return #GNUNET_OK if parsing the value worked
673 */
674static enum GNUNET_GenericReturnValue
675set_absolute_time (struct GNUNET_GETOPT_CommandLineProcessorContext *ctx,
676 void *scls,
677 const char *option,
678 const char *value)
679{
680 struct GNUNET_TIME_Absolute *val = scls;
681
682 (void) ctx;
683 if (GNUNET_OK != GNUNET_STRINGS_fancy_time_to_absolute (value, val))
684 {
685 fprintf (stderr,
686 _ ("You must pass absolute time to the `%s' option.\n"),
687 option);
688 return GNUNET_SYSERR;
689 }
690 return GNUNET_OK;
691}
692
693
694struct GNUNET_GETOPT_CommandLineOption
695GNUNET_GETOPT_option_absolute_time (char shortName,
696 const char *name,
697 const char *argumentHelp,
698 const char *description,
699 struct GNUNET_TIME_Absolute *val)
700{
701 struct GNUNET_GETOPT_CommandLineOption clo = {
702 .shortName = shortName,
703 .name = name,
704 .argumentHelp = argumentHelp,
705 .description = description,
706 .require_argument = 1,
707 .processor = &set_absolute_time,
708 .scls = (void *) val
709 };
710
711 return clo;
712}
713
714
715/**
716 * Set an option of type 'struct GNUNET_TIME_Timestamp' from the command line.
717 * A pointer to this function should be passed as part of the
718 * 'struct GNUNET_GETOPT_CommandLineOption' array to initialize options
719 * of this type. It should be followed by a pointer to a value of
720 * type 'struct GNUNET_TIME_Absolute'.
721 *
722 * @param ctx command line processing context
723 * @param scls additional closure (will point to the `struct GNUNET_TIME_Absolute`)
724 * @param option name of the option
725 * @param value actual value of the option as a string.
726 * @return #GNUNET_OK if parsing the value worked
727 */
728static enum GNUNET_GenericReturnValue
729set_timestamp (struct GNUNET_GETOPT_CommandLineProcessorContext *ctx,
730 void *scls,
731 const char *option,
732 const char *value)
733{
734 struct GNUNET_TIME_Timestamp *t = scls;
735 struct GNUNET_TIME_Absolute abs;
736
737 (void) ctx;
738 if (GNUNET_OK !=
739 GNUNET_STRINGS_fancy_time_to_absolute (value,
740 &abs))
741 {
742 fprintf (stderr,
743 _ ("You must pass a timestamp to the `%s' option.\n"),
744 option);
745 return GNUNET_SYSERR;
746 }
747 if (0 != abs.abs_value_us % GNUNET_TIME_UNIT_SECONDS.rel_value_us)
748 {
749 fprintf (stderr,
750 _ ("The maximum precision allowed for timestamps is seconds.\n"));
751 return GNUNET_SYSERR;
752 }
753 t->abs_time = abs;
754 return GNUNET_OK;
755}
756
757
758struct GNUNET_GETOPT_CommandLineOption
759GNUNET_GETOPT_option_timestamp (char shortName,
760 const char *name,
761 const char *argumentHelp,
762 const char *description,
763 struct GNUNET_TIME_Timestamp *val)
764{
765 struct GNUNET_GETOPT_CommandLineOption clo = {
766 .shortName = shortName,
767 .name = name,
768 .argumentHelp = argumentHelp,
769 .description = description,
770 .require_argument = 1,
771 .processor = &set_timestamp,
772 .scls = (void *) val
773 };
774
775 return clo;
776}
777
778
779/**
780 * Set an option of type 'unsigned int' from the command line.
781 * A pointer to this function should be passed as part of the
782 * 'struct GNUNET_GETOPT_CommandLineOption' array to initialize options
783 * of this type. It should be followed by a pointer to a value of
784 * type 'unsigned int'.
785 *
786 * @param ctx command line processing context
787 * @param scls additional closure (will point to the 'unsigned int')
788 * @param option name of the option
789 * @param value actual value of the option as a string.
790 * @return #GNUNET_OK if parsing the value worked
791 */
792static enum GNUNET_GenericReturnValue
793set_uint (struct GNUNET_GETOPT_CommandLineProcessorContext *ctx,
794 void *scls,
795 const char *option,
796 const char *value)
797{
798 unsigned int *val = scls;
799 char dummy[2];
800
801 (void) ctx;
802 if ('-' == *value)
803 {
804 fprintf (stderr,
805 _ (
806 "Your input for the '%s' option has to be a non negative number\n"),
807 option);
808 return GNUNET_SYSERR;
809 }
810 if (1 != sscanf (value, "%u%1s", val, dummy))
811 {
812 fprintf (stderr,
813 _ ("You must pass a number to the `%s' option.\n"),
814 option);
815 return GNUNET_SYSERR;
816 }
817 return GNUNET_OK;
818}
819
820
821struct GNUNET_GETOPT_CommandLineOption
822GNUNET_GETOPT_option_uint (char shortName,
823 const char *name,
824 const char *argumentHelp,
825 const char *description,
826 unsigned int *val)
827{
828 struct GNUNET_GETOPT_CommandLineOption clo = {
829 .shortName = shortName,
830 .name = name,
831 .argumentHelp = argumentHelp,
832 .description = description,
833 .require_argument = 1,
834 .processor = &set_uint,
835 .scls = (void *) val
836 };
837
838 return clo;
839}
840
841
842/**
843 * Set an option of type 'uint16_t' from the command line.
844 * A pointer to this function should be passed as part of the
845 * 'struct GNUNET_GETOPT_CommandLineOption' array to initialize options
846 * of this type. It should be followed by a pointer to a value of
847 * type 'uint16_t'.
848 *
849 * @param ctx command line processing context
850 * @param scls additional closure (will point to the 'unsigned int')
851 * @param option name of the option
852 * @param value actual value of the option as a string.
853 * @return #GNUNET_OK if parsing the value worked
854 */
855static enum GNUNET_GenericReturnValue
856set_uint16 (struct GNUNET_GETOPT_CommandLineProcessorContext *ctx,
857 void *scls,
858 const char *option,
859 const char *value)
860{
861 uint16_t *val = scls;
862 unsigned int v;
863 char dummy[2];
864
865 (void) ctx;
866 if (1 != sscanf (value, "%u%1s", &v, dummy))
867 {
868 fprintf (stderr,
869 _ ("You must pass a number to the `%s' option.\n"),
870 option);
871 return GNUNET_SYSERR;
872 }
873 if (v > UINT16_MAX)
874 {
875 fprintf (stderr,
876 _ ("You must pass a number below %u to the `%s' option.\n"),
877 (unsigned int) UINT16_MAX,
878 option);
879 return GNUNET_SYSERR;
880 }
881 *val = (uint16_t) v;
882 return GNUNET_OK;
883}
884
885
886struct GNUNET_GETOPT_CommandLineOption
887GNUNET_GETOPT_option_uint16 (char shortName,
888 const char *name,
889 const char *argumentHelp,
890 const char *description,
891 uint16_t *val)
892{
893 struct GNUNET_GETOPT_CommandLineOption clo = {
894 .shortName = shortName,
895 .name = name,
896 .argumentHelp = argumentHelp,
897 .description = description,
898 .require_argument = 1,
899 .processor = &set_uint16,
900 .scls = (void *) val
901 };
902
903 return clo;
904}
905
906
907/**
908 * Closure for #set_base32().
909 */
910struct Base32Context
911{
912 /**
913 * Value to initialize (already allocated)
914 */
915 void *val;
916
917 /**
918 * Number of bytes expected for @e val.
919 */
920 size_t val_size;
921};
922
923
924/**
925 * Set an option of type 'unsigned int' from the command line.
926 * A pointer to this function should be passed as part of the
927 * 'struct GNUNET_GETOPT_CommandLineOption' array to initialize options
928 * of this type. It should be followed by a pointer to a value of
929 * type 'unsigned int'.
930 *
931 * @param ctx command line processing context
932 * @param scls additional closure (will point to the 'unsigned int')
933 * @param option name of the option
934 * @param value actual value of the option as a string.
935 * @return #GNUNET_OK if parsing the value worked
936 */
937static enum GNUNET_GenericReturnValue
938set_base32 (struct GNUNET_GETOPT_CommandLineProcessorContext *ctx,
939 void *scls,
940 const char *option,
941 const char *value)
942{
943 struct Base32Context *bc = scls;
944
945 (void) ctx;
946 if (GNUNET_OK != GNUNET_STRINGS_string_to_data (value,
947 strlen (value),
948 bc->val,
949 bc->val_size))
950 {
951 fprintf (
952 stderr,
953 _ (
954 "Argument `%s' malformed. Expected base32 (Crockford) encoded value.\n"),
955 option);
956 return GNUNET_SYSERR;
957 }
958 return GNUNET_OK;
959}
960
961
962/**
963 * Helper function to clean up after
964 * #GNUNET_GETOPT_option_base32_fixed_size.
965 *
966 * @param cls value to GNUNET_free()
967 */
968static void
969free_bc (void *cls)
970{
971 GNUNET_free (cls);
972}
973
974
975struct GNUNET_GETOPT_CommandLineOption
976GNUNET_GETOPT_option_base32_fixed_size (char shortName,
977 const char *name,
978 const char *argumentHelp,
979 const char *description,
980 void *val,
981 size_t val_size)
982{
983 struct Base32Context *bc = GNUNET_new (struct Base32Context);
984 struct GNUNET_GETOPT_CommandLineOption clo = {
985 .shortName = shortName,
986 .name = name,
987 .argumentHelp = argumentHelp,
988 .description = description,
989 .require_argument = 1,
990 .processor = &set_base32,
991 .cleaner = &free_bc,
992 .scls = (void *) bc
993 };
994
995 bc->val = val;
996 bc->val_size = val_size;
997 return clo;
998}
999
1000
1001struct GNUNET_GETOPT_CommandLineOption
1002GNUNET_GETOPT_option_mandatory (struct GNUNET_GETOPT_CommandLineOption opt)
1003{
1004 opt.option_mandatory = 1;
1005 return opt;
1006}
1007
1008
1009struct GNUNET_GETOPT_CommandLineOption
1010GNUNET_GETOPT_option_exclusive (struct GNUNET_GETOPT_CommandLineOption opt)
1011{
1012 opt.option_exclusive = 1;
1013 return opt;
1014}
1015
1016
1017/* end of getopt_helpers.c */
diff --git a/src/lib/util/gnunet_error_codes.c b/src/lib/util/gnunet_error_codes.c
new file mode 100644
index 000000000..11ce2d0c8
--- /dev/null
+++ b/src/lib/util/gnunet_error_codes.c
@@ -0,0 +1,262 @@
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#include "gnunet_error_codes.h"
21#include <stddef.h>
22#include <microhttpd.h>
23#include <gettext.h>
24
25/**
26 * MHD does not define our value for 0 (client-side generated code).
27 */
28#define MHD_HTTP_UNINITIALIZED 0
29
30/**
31 * A pair containing an error code and its hint.
32 */
33struct ErrorCodeAndHint
34{
35 /**
36 * The error code.
37 */
38 enum GNUNET_ErrorCode ec;
39
40 /**
41 * The hint.
42 */
43 const char *hint;
44
45 /**
46 * The HTTP status code.
47 */
48 unsigned int http_code;
49};
50
51
52/**
53 * The list of all error codes with their hints.
54 */
55static const struct ErrorCodeAndHint code_hint_pairs[] = {
56
57 {
58 .ec = GNUNET_EC_NONE,
59 .hint = gettext_noop ("No error (success)."),
60 .http_code = MHD_HTTP_UNINITIALIZED
61 },
62
63 {
64 .ec = GNUNET_EC_UNKNOWN,
65 .hint = gettext_noop ("Unknown and unspecified error."),
66 .http_code = MHD_HTTP_INTERNAL_SERVER_ERROR
67 },
68
69 {
70 .ec = GNUNET_EC_SERVICE_COMMUNICATION_FAILED,
71 .hint = gettext_noop ("Communication with service failed."),
72 .http_code = MHD_HTTP_INTERNAL_SERVER_ERROR
73 },
74
75 {
76 .ec = GNUNET_EC_IDENTITY_NOT_FOUND,
77 .hint = gettext_noop ("Ego not found."),
78 .http_code = MHD_HTTP_NOT_FOUND
79 },
80
81 {
82 .ec = GNUNET_EC_IDENTITY_NAME_CONFLICT,
83 .hint = gettext_noop ("Identifier already in use for another ego."),
84 .http_code = MHD_HTTP_CONFLICT
85 },
86
87 {
88 .ec = GNUNET_EC_IDENTITY_INVALID,
89 .hint = gettext_noop ("The given ego is invalid or malformed."),
90 .http_code = MHD_HTTP_INTERNAL_SERVER_ERROR
91 },
92
93 {
94 .ec = GNUNET_EC_NAMESTORE_UNKNOWN,
95 .hint = gettext_noop ("Unknown namestore error."),
96 .http_code = MHD_HTTP_INTERNAL_SERVER_ERROR
97 },
98
99 {
100 .ec = GNUNET_EC_NAMESTORE_ITERATION_FAILED,
101 .hint = gettext_noop ("Zone iteration failed."),
102 .http_code = MHD_HTTP_INTERNAL_SERVER_ERROR
103 },
104
105 {
106 .ec = GNUNET_EC_NAMESTORE_ZONE_NOT_FOUND,
107 .hint = gettext_noop ("Zone not found."),
108 .http_code = MHD_HTTP_NOT_FOUND
109 },
110
111 {
112 .ec = GNUNET_EC_NAMESTORE_RECORD_NOT_FOUND,
113 .hint = gettext_noop ("Record not found."),
114 .http_code = MHD_HTTP_NOT_FOUND
115 },
116
117 {
118 .ec = GNUNET_EC_NAMESTORE_RECORD_DELETE_FAILED,
119 .hint = gettext_noop ("Zone iteration failed."),
120 .http_code = MHD_HTTP_INTERNAL_SERVER_ERROR
121 },
122
123 {
124 .ec = GNUNET_EC_NAMESTORE_ZONE_EMPTY,
125 .hint = gettext_noop ("Zone does not contain any records."),
126 .http_code = MHD_HTTP_NOT_FOUND
127 },
128
129 {
130 .ec = GNUNET_EC_NAMESTORE_LOOKUP_ERROR,
131 .hint = gettext_noop ("Failed to lookup record."),
132 .http_code = MHD_HTTP_INTERNAL_SERVER_ERROR
133 },
134
135 {
136 .ec = GNUNET_EC_NAMESTORE_NO_RECORDS_GIVEN,
137 .hint = gettext_noop ("No records given."),
138 .http_code = MHD_HTTP_BAD_REQUEST
139 },
140
141 {
142 .ec = GNUNET_EC_NAMESTORE_RECORD_DATA_INVALID,
143 .hint = gettext_noop ("Record data invalid."),
144 .http_code = MHD_HTTP_BAD_REQUEST
145 },
146
147 {
148 .ec = GNUNET_EC_NAMESTORE_NO_LABEL_GIVEN,
149 .hint = gettext_noop ("No label given."),
150 .http_code = MHD_HTTP_BAD_REQUEST
151 },
152
153 {
154 .ec = GNUNET_EC_NAMESTORE_NO_RESULTS,
155 .hint = gettext_noop ("No results given."),
156 .http_code = MHD_HTTP_NOT_FOUND
157 },
158
159 {
160 .ec = GNUNET_EC_NAMESTORE_RECORD_EXISTS,
161 .hint = gettext_noop ("Record already exists."),
162 .http_code = MHD_HTTP_CONFLICT
163 },
164
165 {
166 .ec = GNUNET_EC_NAMESTORE_RECORD_TOO_BIG,
167 .hint = gettext_noop ("Record size exceeds maximum limit."),
168 .http_code = MHD_HTTP_INTERNAL_SERVER_ERROR
169 },
170
171 {
172 .ec = GNUNET_EC_NAMESTORE_BACKEND_FAILED,
173 .hint = gettext_noop ("There was an error in the database backend."),
174 .http_code = MHD_HTTP_INTERNAL_SERVER_ERROR
175 },
176
177 {
178 .ec = GNUNET_EC_NAMESTORE_STORE_FAILED,
179 .hint = gettext_noop ("Failed to store the given records."),
180 .http_code = MHD_HTTP_INTERNAL_SERVER_ERROR
181 },
182
183 {
184 .ec = GNUNET_EC_NAMESTORE_LABEL_INVALID,
185 .hint = gettext_noop ("Label invalid or malformed."),
186 .http_code = MHD_HTTP_BAD_REQUEST
187 },
188
189
190};
191
192
193/**
194 * The length of @e code_hint_pairs.
195 */
196static const unsigned int code_hint_pairs_length = 22;
197
198
199
200const char *
201GNUNET_ErrorCode_get_hint (enum GNUNET_ErrorCode ec)
202{
203 unsigned int lower = 0;
204 unsigned int upper = code_hint_pairs_length - 1;
205 unsigned int mid = upper / 2;
206 while (lower <= upper)
207 {
208 mid = (upper + lower) / 2;
209 if (code_hint_pairs[mid].ec < ec)
210 {
211 lower = mid + 1;
212 }
213 else if (code_hint_pairs[mid].ec > ec)
214 {
215 upper = mid - 1;
216 }
217 else
218 {
219 return code_hint_pairs[mid].hint;
220 }
221 }
222 return "<no hint found>";
223}
224
225
226unsigned int
227GNUNET_ErrorCode_get_http_status (enum GNUNET_ErrorCode ec)
228{
229 unsigned int lower = 0;
230 unsigned int upper = code_hint_pairs_length - 1;
231 unsigned int mid = upper / 2;
232 while (lower <= upper)
233 {
234 mid = (upper + lower) / 2;
235 if (code_hint_pairs[mid].ec < ec)
236 {
237 lower = mid + 1;
238 }
239 else if (code_hint_pairs[mid].ec > ec)
240 {
241 upper = mid - 1;
242 }
243 else
244 {
245 return code_hint_pairs[mid].http_code;
246 }
247 }
248 return UINT_MAX;
249}
250
251
252unsigned int
253GNUNET_ErrorCode_get_http_status_safe (enum GNUNET_ErrorCode ec)
254{
255 unsigned int hc;
256
257 hc = GNUNET_ErrorCode_get_http_status (ec);
258 if ( (0 == hc) ||
259 (UINT_MAX == hc) )
260 return MHD_HTTP_INTERNAL_SERVER_ERROR;
261 return hc;
262}
diff --git a/src/lib/util/helper.c b/src/lib/util/helper.c
new file mode 100644
index 000000000..87ea749e9
--- /dev/null
+++ b/src/lib/util/helper.c
@@ -0,0 +1,672 @@
1/*
2 This file is part of GNUnet.
3 Copyright (C) 2011, 2012 Christian Grothoff
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/helper.c
23 * @brief API for dealing with (SUID) helper processes that communicate via
24 * GNUNET_MessageHeaders on stdin/stdout
25 * @author Philipp Toelke
26 * @author Christian Grothoff
27 */
28#include "platform.h"
29#include "gnunet_util_lib.h"
30
31/**
32 * Entry in the queue of messages we need to transmit to the helper.
33 */
34struct GNUNET_HELPER_SendHandle
35{
36 /**
37 * This is an entry in a DLL.
38 */
39 struct GNUNET_HELPER_SendHandle *next;
40
41 /**
42 * This is an entry in a DLL.
43 */
44 struct GNUNET_HELPER_SendHandle *prev;
45
46 /**
47 * Message to transmit (allocated at the end of this struct)
48 */
49 const struct GNUNET_MessageHeader *msg;
50
51 /**
52 * The handle to a helper process.
53 */
54 struct GNUNET_HELPER_Handle *h;
55
56 /**
57 * Function to call upon completion.
58 */
59 GNUNET_HELPER_Continuation cont;
60
61 /**
62 * Closure to 'cont'.
63 */
64 void *cont_cls;
65
66 /**
67 * Current write position.
68 */
69 unsigned int wpos;
70};
71
72
73/**
74 * The handle to a helper process.
75 */
76struct GNUNET_HELPER_Handle
77{
78 /**
79 * PipeHandle to receive data from the helper
80 */
81 struct GNUNET_DISK_PipeHandle *helper_in;
82
83 /**
84 * PipeHandle to send data to the helper
85 */
86 struct GNUNET_DISK_PipeHandle *helper_out;
87
88 /**
89 * FileHandle to receive data from the helper
90 */
91 const struct GNUNET_DISK_FileHandle *fh_from_helper;
92
93 /**
94 * FileHandle to send data to the helper
95 */
96 const struct GNUNET_DISK_FileHandle *fh_to_helper;
97
98 /**
99 * The process id of the helper
100 */
101 struct GNUNET_OS_Process *helper_proc;
102
103 /**
104 * The Message-Tokenizer that tokenizes the messages coming from the helper
105 */
106 struct GNUNET_MessageStreamTokenizer *mst;
107
108 /**
109 * The exception callback
110 */
111 GNUNET_HELPER_ExceptionCallback exp_cb;
112
113 /**
114 * The closure for callbacks
115 */
116 void *cb_cls;
117
118 /**
119 * First message queued for transmission to helper.
120 */
121 struct GNUNET_HELPER_SendHandle *sh_head;
122
123 /**
124 * Last message queued for transmission to helper.
125 */
126 struct GNUNET_HELPER_SendHandle *sh_tail;
127
128 /**
129 * Binary to run.
130 */
131 char *binary_name;
132
133 /**
134 * NULL-terminated list of command-line arguments.
135 */
136 char **binary_argv;
137
138 /**
139 * Task to read from the helper.
140 */
141 struct GNUNET_SCHEDULER_Task *read_task;
142
143 /**
144 * Task to read from the helper.
145 */
146 struct GNUNET_SCHEDULER_Task *write_task;
147
148 /**
149 * Restart task.
150 */
151 struct GNUNET_SCHEDULER_Task *restart_task;
152
153 /**
154 * Does the helper support the use of a control pipe for signalling?
155 */
156 int with_control_pipe;
157
158 /**
159 * Count start attempts to increase linear back off
160 */
161 unsigned int retry_back_off;
162};
163
164
165enum GNUNET_GenericReturnValue
166GNUNET_HELPER_kill (struct GNUNET_HELPER_Handle *h, int soft_kill)
167{
168 struct GNUNET_HELPER_SendHandle *sh;
169 int ret;
170
171 while (NULL != (sh = h->sh_head))
172 {
173 GNUNET_CONTAINER_DLL_remove (h->sh_head, h->sh_tail, sh);
174 if (NULL != sh->cont)
175 sh->cont (sh->cont_cls, GNUNET_NO);
176 GNUNET_free (sh);
177 }
178 if (NULL != h->restart_task)
179 {
180 GNUNET_SCHEDULER_cancel (h->restart_task);
181 h->restart_task = NULL;
182 }
183 if (NULL != h->read_task)
184 {
185 GNUNET_SCHEDULER_cancel (h->read_task);
186 h->read_task = NULL;
187 }
188 if (NULL == h->helper_proc)
189 return GNUNET_SYSERR;
190 if (GNUNET_YES == soft_kill)
191 {
192 /* soft-kill only possible with pipes */
193 GNUNET_assert (NULL != h->helper_in);
194 ret = GNUNET_DISK_pipe_close (h->helper_in);
195 h->helper_in = NULL;
196 h->fh_to_helper = NULL;
197 return ret;
198 }
199 if (0 != GNUNET_OS_process_kill (h->helper_proc, GNUNET_TERM_SIG))
200 return GNUNET_SYSERR;
201 return GNUNET_OK;
202}
203
204
205enum GNUNET_GenericReturnValue
206GNUNET_HELPER_wait (struct GNUNET_HELPER_Handle *h)
207{
208 struct GNUNET_HELPER_SendHandle *sh;
209 int ret;
210
211 ret = GNUNET_SYSERR;
212 if (NULL != h->helper_proc)
213 {
214 ret = GNUNET_OS_process_wait (h->helper_proc);
215 GNUNET_OS_process_destroy (h->helper_proc);
216 h->helper_proc = NULL;
217 }
218 if (NULL != h->read_task)
219 {
220 GNUNET_SCHEDULER_cancel (h->read_task);
221 h->read_task = NULL;
222 }
223 if (NULL != h->write_task)
224 {
225 GNUNET_SCHEDULER_cancel (h->write_task);
226 h->write_task = NULL;
227 }
228 if (NULL != h->helper_in)
229 {
230 GNUNET_DISK_pipe_close (h->helper_in);
231 h->helper_in = NULL;
232 h->fh_to_helper = NULL;
233 }
234 if (NULL != h->helper_out)
235 {
236 GNUNET_DISK_pipe_close (h->helper_out);
237 h->helper_out = NULL;
238 h->fh_from_helper = NULL;
239 }
240 while (NULL != (sh = h->sh_head))
241 {
242 GNUNET_CONTAINER_DLL_remove (h->sh_head, h->sh_tail, sh);
243 if (NULL != sh->cont)
244 sh->cont (sh->cont_cls, GNUNET_NO);
245 GNUNET_free (sh);
246 }
247 /* purge MST buffer */
248 if (NULL != h->mst)
249 (void) GNUNET_MST_from_buffer (h->mst, NULL, 0, GNUNET_YES, GNUNET_NO);
250 return ret;
251}
252
253
254/**
255 * Stop the helper process, we're closing down or had an error.
256 *
257 * @param h handle to the helper process
258 * @param soft_kill if #GNUNET_YES, signals termination by closing the helper's
259 * stdin; #GNUNET_NO to signal termination by sending SIGTERM to helper
260 */
261static void
262stop_helper (struct GNUNET_HELPER_Handle *h, int soft_kill)
263{
264 if (NULL != h->restart_task)
265 {
266 GNUNET_SCHEDULER_cancel (h->restart_task);
267 h->restart_task = NULL;
268 }
269 else
270 {
271 GNUNET_break (GNUNET_OK == GNUNET_HELPER_kill (h, soft_kill));
272 GNUNET_break (GNUNET_OK == GNUNET_HELPER_wait (h));
273 }
274}
275
276
277/**
278 * Restart the helper process.
279 *
280 * @param cls handle to the helper process
281 */
282static void
283restart_task (void *cls);
284
285
286/**
287 * Read from the helper-process
288 *
289 * @param cls handle to the helper process
290 */
291static void
292helper_read (void *cls)
293{
294 struct GNUNET_HELPER_Handle *h = cls;
295 char buf[GNUNET_MAX_MESSAGE_SIZE] GNUNET_ALIGN;
296 ssize_t t;
297
298 h->read_task = NULL;
299 t = GNUNET_DISK_file_read (h->fh_from_helper, &buf, sizeof(buf));
300 if (t < 0)
301 {
302 /* On read-error, restart the helper */
303 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
304 _ ("Error reading from `%s': %s\n"),
305 h->binary_name,
306 strerror (errno));
307 if (NULL != h->exp_cb)
308 {
309 h->exp_cb (h->cb_cls);
310 GNUNET_HELPER_stop (h, GNUNET_NO);
311 return;
312 }
313 stop_helper (h, GNUNET_NO);
314 /* Restart the helper */
315 h->restart_task = GNUNET_SCHEDULER_add_delayed (
316 GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS,
317 h->retry_back_off),
318 &restart_task,
319 h);
320 return;
321 }
322 if (0 == t)
323 {
324 /* this happens if the helper is shut down via a
325 signal, so it is not a "hard" error */
326 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
327 "Got 0 bytes from helper `%s' (EOF)\n",
328 h->binary_name);
329 if (NULL != h->exp_cb)
330 {
331 h->exp_cb (h->cb_cls);
332 GNUNET_HELPER_stop (h, GNUNET_NO);
333 return;
334 }
335 stop_helper (h, GNUNET_NO);
336 /* Restart the helper */
337 h->restart_task = GNUNET_SCHEDULER_add_delayed (
338 GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS,
339 h->retry_back_off),
340 &restart_task,
341 h);
342 return;
343 }
344 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
345 "Got %u bytes from helper `%s'\n",
346 (unsigned int) t,
347 h->binary_name);
348 h->read_task = GNUNET_SCHEDULER_add_read_file (GNUNET_TIME_UNIT_FOREVER_REL,
349 h->fh_from_helper,
350 &helper_read,
351 h);
352 if (GNUNET_SYSERR ==
353 GNUNET_MST_from_buffer (h->mst, buf, t, GNUNET_NO, GNUNET_NO))
354 {
355 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
356 _ ("Failed to parse inbound message from helper `%s'\n"),
357 h->binary_name);
358 if (NULL != h->exp_cb)
359 {
360 h->exp_cb (h->cb_cls);
361 GNUNET_HELPER_stop (h, GNUNET_NO);
362 return;
363 }
364 stop_helper (h, GNUNET_NO);
365 /* Restart the helper */
366 h->restart_task = GNUNET_SCHEDULER_add_delayed (
367 GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS,
368 h->retry_back_off),
369 &restart_task,
370 h);
371 return;
372 }
373}
374
375
376/**
377 * Start the helper process.
378 *
379 * @param h handle to the helper process
380 */
381static void
382start_helper (struct GNUNET_HELPER_Handle *h)
383{
384 h->helper_in =
385 GNUNET_DISK_pipe (GNUNET_DISK_PF_BLOCKING_RW);
386 h->helper_out =
387 GNUNET_DISK_pipe (GNUNET_DISK_PF_BLOCKING_RW);
388 if ((h->helper_in == NULL) || (h->helper_out == NULL))
389 {
390 /* out of file descriptors? try again later... */
391 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
392 "out of file descriptors? try again later\n");
393 stop_helper (h, GNUNET_NO);
394 h->restart_task = GNUNET_SCHEDULER_add_delayed (
395 GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS,
396 h->retry_back_off),
397 &restart_task,
398 h);
399 return;
400 }
401 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
402 "Starting HELPER process `%s'\n",
403 h->binary_name);
404 h->fh_from_helper =
405 GNUNET_DISK_pipe_handle (h->helper_out, GNUNET_DISK_PIPE_END_READ);
406 h->fh_to_helper =
407 GNUNET_DISK_pipe_handle (h->helper_in, GNUNET_DISK_PIPE_END_WRITE);
408 h->helper_proc = GNUNET_OS_start_process_vap (h->with_control_pipe
409 ? GNUNET_OS_INHERIT_STD_ERR
410 | GNUNET_OS_USE_PIPE_CONTROL
411 : GNUNET_OS_INHERIT_STD_ERR,
412 h->helper_in,
413 h->helper_out,
414 NULL,
415 h->binary_name,
416 h->binary_argv);
417 if (NULL == h->helper_proc)
418 {
419 /* failed to start process? try again later... */
420 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
421 "failed to start process? try again later\n");
422 stop_helper (h, GNUNET_NO);
423 h->restart_task = GNUNET_SCHEDULER_add_delayed (
424 GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS,
425 h->retry_back_off),
426 &restart_task,
427 h);
428 return;
429 }
430 GNUNET_DISK_pipe_close_end (h->helper_out, GNUNET_DISK_PIPE_END_WRITE);
431 GNUNET_DISK_pipe_close_end (h->helper_in, GNUNET_DISK_PIPE_END_READ);
432 if (NULL != h->mst)
433 h->read_task = GNUNET_SCHEDULER_add_read_file (GNUNET_TIME_UNIT_FOREVER_REL,
434 h->fh_from_helper,
435 &helper_read,
436 h);
437}
438
439
440/**
441 * Restart the helper process.
442 *
443 * @param cls handle to the helper process
444 */
445static void
446restart_task (void *cls)
447{
448 struct GNUNET_HELPER_Handle *h = cls;
449
450 h->restart_task = NULL;
451 h->retry_back_off++;
452 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
453 "Restarting helper with back-off %u\n",
454 h->retry_back_off);
455 start_helper (h);
456}
457
458
459struct GNUNET_HELPER_Handle *
460GNUNET_HELPER_start (int with_control_pipe,
461 const char *binary_name,
462 char *const binary_argv[],
463 GNUNET_MessageTokenizerCallback cb,
464 GNUNET_HELPER_ExceptionCallback exp_cb,
465 void *cb_cls)
466{
467 struct GNUNET_HELPER_Handle *h;
468 unsigned int c;
469
470 h = GNUNET_new (struct GNUNET_HELPER_Handle);
471 h->with_control_pipe = with_control_pipe;
472 /* Lookup in libexec path only if we are starting gnunet helpers */
473 if (NULL != strstr (binary_name, "gnunet"))
474 h->binary_name = GNUNET_OS_get_libexec_binary_path (binary_name);
475 else
476 h->binary_name = GNUNET_strdup (binary_name);
477 for (c = 0; NULL != binary_argv[c]; c++)
478 ;
479 h->binary_argv = GNUNET_malloc (sizeof(char *) * (c + 1));
480 for (c = 0; NULL != binary_argv[c]; c++)
481 h->binary_argv[c] = GNUNET_strdup (binary_argv[c]);
482 h->binary_argv[c] = NULL;
483 h->cb_cls = cb_cls;
484 if (NULL != cb)
485 h->mst = GNUNET_MST_create (cb, h->cb_cls);
486 h->exp_cb = exp_cb;
487 h->retry_back_off = 0;
488 start_helper (h);
489 return h;
490}
491
492
493/**
494 * Free's the resources occupied by the helper handle
495 *
496 * @param h the helper handle to free
497 */
498void
499GNUNET_HELPER_destroy (struct GNUNET_HELPER_Handle *h)
500{
501 unsigned int c;
502 struct GNUNET_HELPER_SendHandle *sh;
503
504 if (NULL != h->write_task)
505 {
506 GNUNET_SCHEDULER_cancel (h->write_task);
507 h->write_task = NULL;
508 }
509 GNUNET_assert (NULL == h->read_task);
510 GNUNET_assert (NULL == h->restart_task);
511 while (NULL != (sh = h->sh_head))
512 {
513 GNUNET_CONTAINER_DLL_remove (h->sh_head, h->sh_tail, sh);
514 if (NULL != sh->cont)
515 sh->cont (sh->cont_cls, GNUNET_SYSERR);
516 GNUNET_free (sh);
517 }
518 if (NULL != h->mst)
519 GNUNET_MST_destroy (h->mst);
520 GNUNET_free (h->binary_name);
521 for (c = 0; h->binary_argv[c] != NULL; c++)
522 GNUNET_free (h->binary_argv[c]);
523 GNUNET_free (h->binary_argv);
524 GNUNET_free (h);
525}
526
527
528/**
529 * Kills the helper, closes the pipe and frees the handle
530 *
531 * @param h handle to helper to stop
532 * @param soft_kill if #GNUNET_YES, signals termination by closing the helper's
533 * stdin; #GNUNET_NO to signal termination by sending SIGTERM to helper
534 */
535void
536GNUNET_HELPER_stop (struct GNUNET_HELPER_Handle *h, int soft_kill)
537{
538 h->exp_cb = NULL;
539 stop_helper (h, soft_kill);
540 GNUNET_HELPER_destroy (h);
541}
542
543
544/**
545 * Write to the helper-process
546 *
547 * @param cls handle to the helper process
548 */
549static void
550helper_write (void *cls)
551{
552 struct GNUNET_HELPER_Handle *h = cls;
553 struct GNUNET_HELPER_SendHandle *sh;
554 const char *buf;
555 ssize_t t;
556
557 h->write_task = NULL;
558 if (NULL == (sh = h->sh_head))
559 {
560 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Helper write had no work!\n");
561 return; /* how did this happen? */
562 }
563 buf = (const char *) sh->msg;
564 t = GNUNET_DISK_file_write (h->fh_to_helper,
565 &buf[sh->wpos],
566 ntohs (sh->msg->size) - sh->wpos);
567 if (-1 == t)
568 {
569 /* On write-error, restart the helper */
570 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
571 _ ("Error writing to `%s': %s\n"),
572 h->binary_name,
573 strerror (errno));
574 if (NULL != h->exp_cb)
575 {
576 h->exp_cb (h->cb_cls);
577 GNUNET_HELPER_stop (h, GNUNET_NO);
578 return;
579 }
580 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
581 "Stopping and restarting helper task!\n");
582 stop_helper (h, GNUNET_NO);
583 /* Restart the helper */
584 h->restart_task = GNUNET_SCHEDULER_add_delayed (
585 GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS,
586 h->retry_back_off),
587 &restart_task,
588 h);
589 return;
590 }
591 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
592 "Transmitted %u bytes to %s\n",
593 (unsigned int) t,
594 h->binary_name);
595 sh->wpos += t;
596 if (sh->wpos == ntohs (sh->msg->size))
597 {
598 GNUNET_CONTAINER_DLL_remove (h->sh_head, h->sh_tail, sh);
599 if (NULL != sh->cont)
600 sh->cont (sh->cont_cls, GNUNET_YES);
601 GNUNET_free (sh);
602 }
603 if (NULL != h->sh_head)
604 h->write_task =
605 GNUNET_SCHEDULER_add_write_file (GNUNET_TIME_UNIT_FOREVER_REL,
606 h->fh_to_helper,
607 &helper_write,
608 h);
609}
610
611
612struct GNUNET_HELPER_SendHandle *
613GNUNET_HELPER_send (struct GNUNET_HELPER_Handle *h,
614 const struct GNUNET_MessageHeader *msg,
615 bool can_drop,
616 GNUNET_HELPER_Continuation cont,
617 void *cont_cls)
618{
619 struct GNUNET_HELPER_SendHandle *sh;
620 uint16_t mlen;
621
622 if (NULL == h->fh_to_helper)
623 return NULL;
624 if (can_drop && (NULL != h->sh_head))
625 return NULL;
626 mlen = ntohs (msg->size);
627 sh = GNUNET_malloc (sizeof(struct GNUNET_HELPER_SendHandle) + mlen);
628 sh->msg = (const struct GNUNET_MessageHeader *) &sh[1];
629 GNUNET_memcpy (&sh[1], msg, mlen);
630 sh->h = h;
631 sh->cont = cont;
632 sh->cont_cls = cont_cls;
633 GNUNET_CONTAINER_DLL_insert_tail (h->sh_head, h->sh_tail, sh);
634 if (NULL == h->write_task)
635 h->write_task =
636 GNUNET_SCHEDULER_add_write_file (GNUNET_TIME_UNIT_FOREVER_REL,
637 h->fh_to_helper,
638 &helper_write,
639 h);
640
641 return sh;
642}
643
644
645/**
646 * Cancel a #GNUNET_HELPER_send operation. If possible, transmitting the
647 * message is also aborted, but at least 'cont' won't be
648 * called.
649 *
650 * @param sh operation to cancel
651 */
652void
653GNUNET_HELPER_send_cancel (struct GNUNET_HELPER_SendHandle *sh)
654{
655 struct GNUNET_HELPER_Handle *h = sh->h;
656
657 sh->cont = NULL;
658 sh->cont_cls = NULL;
659 if (0 == sh->wpos)
660 {
661 GNUNET_CONTAINER_DLL_remove (h->sh_head, h->sh_tail, sh);
662 GNUNET_free (sh);
663 if (NULL == h->sh_head)
664 {
665 GNUNET_SCHEDULER_cancel (h->write_task);
666 h->write_task = NULL;
667 }
668 }
669}
670
671
672/* end of helper.c */
diff --git a/src/lib/util/load.c b/src/lib/util/load.c
new file mode 100644
index 000000000..a64171bd4
--- /dev/null
+++ b/src/lib/util/load.c
@@ -0,0 +1,257 @@
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 util/load.c
23 * @brief functions related to load calculations
24 * @author Christian Grothoff
25 */
26
27#include "platform.h"
28#include "gnunet_util_lib.h"
29
30
31#define LOG(kind, ...) GNUNET_log_from (kind, "util-load", __VA_ARGS__)
32
33/**
34 * Values we track for load calculations.
35 */
36struct GNUNET_LOAD_Value
37{
38 /**
39 * How fast should the load decline if no values are added?
40 */
41 struct GNUNET_TIME_Relative autodecline;
42
43 /**
44 * Last time this load value was updated by an event.
45 */
46 struct GNUNET_TIME_Absolute last_update;
47
48 /**
49 * Sum of all datastore delays ever observed (in ms). Note that
50 * delays above 64k ms are excluded (to avoid overflow within
51 * first 4 billion requests).
52 */
53 uint64_t cummulative_delay;
54
55 /**
56 * Sum of squares of all datastore delays ever observed (in ms). Note that
57 * delays above 64k ms are excluded (to avoid overflow within
58 * first 4 billion requests).
59 */
60 uint64_t cummulative_squared_delay;
61
62 /**
63 * Total number of requests included in the cumulative datastore delay values.
64 */
65 uint64_t cummulative_request_count;
66
67 /**
68 * Current running average datastore delay. Its relation to the
69 * average datastore delay and it std. dev. (as calculated from the
70 * cumulative values) tells us our current load.
71 */
72 double runavg_delay;
73
74 /**
75 * How high is the load? 0 for below average, otherwise
76 * the number of std. devs we are above average, or 100 if the
77 * load is so high that we currently cannot calculate it.
78 */
79 double load;
80};
81
82
83static void
84internal_update (struct GNUNET_LOAD_Value *load)
85{
86 struct GNUNET_TIME_Relative delta;
87 unsigned int n;
88
89 if (load->autodecline.rel_value_us ==
90 GNUNET_TIME_UNIT_FOREVER_REL.rel_value_us)
91 return;
92 delta = GNUNET_TIME_absolute_get_duration (load->last_update);
93 if (delta.rel_value_us < load->autodecline.rel_value_us)
94 return;
95 if (0 == load->autodecline.rel_value_us)
96 {
97 load->runavg_delay = 0.0;
98 load->load = 0;
99 return;
100 }
101 n = delta.rel_value_us / load->autodecline.rel_value_us;
102 if (n > 16)
103 {
104 load->runavg_delay = 0.0;
105 load->load = 0;
106 return;
107 }
108 while (n > 0)
109 {
110 n--;
111 load->runavg_delay = (load->runavg_delay * 7.0) / 8.0;
112 }
113}
114
115
116/**
117 * Create a new load value.
118 *
119 * @param autodecline speed at which this value should automatically
120 * decline in the absence of external events; at the given
121 * frequency, 0-load values will be added to the load
122 * @return the new load value
123 */
124struct GNUNET_LOAD_Value *
125GNUNET_LOAD_value_init (struct GNUNET_TIME_Relative autodecline)
126{
127 struct GNUNET_LOAD_Value *ret;
128
129 ret = GNUNET_new (struct GNUNET_LOAD_Value);
130 ret->autodecline = autodecline;
131 ret->last_update = GNUNET_TIME_absolute_get ();
132 return ret;
133}
134
135
136/**
137 * Change the value by which the load automatically declines.
138 *
139 * @param load load to update
140 * @param autodecline frequency of load decline
141 */
142void
143GNUNET_LOAD_value_set_decline (struct GNUNET_LOAD_Value *load,
144 struct GNUNET_TIME_Relative autodecline)
145{
146 internal_update (load);
147 load->autodecline = autodecline;
148}
149
150
151/**
152 * Recalculate our load value.
153 *
154 * @param load load to update
155 */
156static void
157calculate_load (struct GNUNET_LOAD_Value *load)
158{
159 double stddev;
160 double avgdel;
161 double sum_val_i;
162 double n;
163 double nm1;
164
165 if (load->cummulative_request_count <= 1)
166 return;
167 /* calculate std dev of latency; we have for n values of "i" that:
168 *
169 * avg = (sum val_i) / n
170 * stddev = (sum (val_i - avg)^2) / (n-1)
171 * = (sum (val_i^2 - 2 avg val_i + avg^2) / (n-1)
172 * = (sum (val_i^2) - 2 avg sum (val_i) + n * avg^2) / (n-1)
173 */sum_val_i = (double) load->cummulative_delay;
174 n = ((double) load->cummulative_request_count);
175 nm1 = n - 1.0;
176 avgdel = sum_val_i / n;
177 stddev =
178 (((double) load->cummulative_squared_delay) - 2.0 * avgdel * sum_val_i
179 + n * avgdel * avgdel) / nm1;
180 if (stddev <= 0)
181 stddev = 0.01; /* must have been rounding error or zero; prevent division by zero */
182 /* now calculate load based on how far out we are from
183 * std dev; or if we are below average, simply assume load zero */
184 if (load->runavg_delay < avgdel)
185 load->load = 0.0;
186 else
187 load->load = (load->runavg_delay - avgdel) / stddev;
188}
189
190
191/**
192 * Get the current load.
193 *
194 * @param load load handle
195 * @return zero for below-average load, otherwise
196 * number of std. devs we are above average;
197 * 100 if the latest updates were so large
198 * that we could not do proper calculations
199 */
200double
201GNUNET_LOAD_get_load (struct GNUNET_LOAD_Value *load)
202{
203 internal_update (load);
204 calculate_load (load);
205 return load->load;
206}
207
208
209/**
210 * Get the average value given to update so far.
211 *
212 * @param load load handle
213 * @return zero if update was never called
214 */
215double
216GNUNET_LOAD_get_average (struct GNUNET_LOAD_Value *load)
217{
218 double n;
219 double sum_val_i;
220
221 internal_update (load);
222 if (load->cummulative_request_count == 0)
223 return 0.0;
224 n = ((double) load->cummulative_request_count);
225 sum_val_i = (double) load->cummulative_delay;
226 return sum_val_i / n;
227}
228
229
230/**
231 * Update the current load.
232 *
233 * @param load to update
234 * @param data latest measurement value (for example, delay)
235 */
236void
237GNUNET_LOAD_update (struct GNUNET_LOAD_Value *load, uint64_t data)
238{
239 uint32_t dv;
240
241 internal_update (load);
242 load->last_update = GNUNET_TIME_absolute_get ();
243 if (data > 64 * 1024)
244 {
245 /* very large */
246 load->load = 100.0;
247 return;
248 }
249 dv = (uint32_t) data;
250 load->cummulative_delay += dv;
251 load->cummulative_squared_delay += dv * dv;
252 load->cummulative_request_count++;
253 load->runavg_delay = ((load->runavg_delay * 7.0) + dv) / 8.0;
254}
255
256
257/* end of load.c */
diff --git a/src/lib/util/meson.build b/src/lib/util/meson.build
new file mode 100644
index 000000000..cc7c20201
--- /dev/null
+++ b/src/lib/util/meson.build
@@ -0,0 +1,669 @@
1libgnunetutil_src = ['bandwidth.c',
2 # $(BENCHMARK)',
3 'bio.c',
4 'buffer.c',
5 'child_management.c',
6 'client.c',
7 'common_allocation.c',
8 'common_endian.c',
9 'common_logging.c',
10 'compress.c',
11 'configuration.c',
12 'configuration_helper.c',
13 'consttime_memcmp.c',
14 'container_bloomfilter.c',
15 'container_heap.c',
16 'container_multihashmap.c',
17 'container_multishortmap.c',
18 'container_multiuuidmap.c',
19 'container_multipeermap.c',
20 'container_multihashmap32.c',
21 'crypto_blind_sign.c',
22 'crypto_symmetric.c',
23 'crypto_crc.c',
24 'crypto_cs.c',
25 'crypto_ecc.c',
26 'crypto_ecc_gnsrecord.c',
27 'crypto_ecc_dlog.c',
28 'crypto_ecc_setup.c',
29 'crypto_edx25519.c',
30 'crypto_elligator.c',
31 'crypto_hash.c',
32 'crypto_hash_file.c',
33 'crypto_hkdf.c',
34 'crypto_kdf.c',
35 'crypto_mpi.c',
36 'crypto_paillier.c',
37 'crypto_pkey.c',
38 'crypto_pow.c',
39 'crypto_random.c',
40 'crypto_rsa.c',
41 'disk.c',
42 'disk.h',
43 'dnsparser.c',
44 'dnsstub.c',
45 'getopt.c',
46 'getopt_helpers.c',
47 'helper.c',
48 'load.c',
49 'mst.c',
50 'mq.c',
51 'nc.c',
52 'network.c',
53 'nt.c',
54 'op.c',
55 'os_installation.c',
56 'os_network.c',
57 'os_priority.c',
58 'peer.c',
59 'plugin.c',
60 'program.c',
61 'regex.c',
62 'resolver_api.c',
63 'resolver.h',
64 'scheduler.c',
65 'service.c',
66 'signal.c',
67 'strings.c',
68 'time.c',
69 'tun.c',
70 'uri.c',
71 'speedup.c',
72 'speedup.h',
73 'proc_compat.c',
74 'gnunet_error_codes.c']
75
76configure_file(input : 'util.conf',
77 output : 'util.conf',
78 configuration : cdata,
79 install: true,
80 install_dir: pkgcfgdir)
81
82libgnunetutil = library('gnunetutil',
83 libgnunetutil_src,
84 soversion: '15',
85 version: '15.0.0',
86 dependencies: [
87 lgmp_dep,
88 mhd_dep,
89 sodium_dep,
90 gcrypt_dep,
91 curl_dep,
92 json_dep,
93 zlib_dep,
94 sqlite_dep,
95 unistr_dep,
96 ltdl_dep,
97 idn_dep
98 ],
99 include_directories: [incdir, configuration_inc],
100 install: true,
101 install_dir: get_option('libdir'))
102libgnunetutil_dep = declare_dependency(link_with : libgnunetutil)
103pkg.generate(libgnunetutil, url: 'https://www.gnunet.org',
104 description : 'Provides miscellaneous utility functions and API for GNUnet')
105
106logging_dummy = executable ('test_common_logging_dummy',
107 ['test_common_logging_dummy.c'],
108 dependencies: [libgnunetutil_dep],
109 include_directories: [incdir, configuration_inc],
110 install: false)
111configure_file(input : 'test_client_data.conf',
112 output : 'test_client_data.conf',
113 copy: true)
114configure_file(input : 'test_speedup_data.conf',
115 output : 'test_speedup_data.conf',
116 copy: true)
117configure_file(input : 'test_program_data.conf',
118 output : 'test_program_data.conf',
119 copy: true)
120configure_file(input : 'test_service_data.conf',
121 output : 'test_service_data.conf',
122 copy: true)
123configure_file(input : 'test_configuration_data.conf',
124 output : 'test_configuration_data.conf',
125 copy: true)
126configure_file(input : 'child_management_test.sh',
127 output : 'child_management_test.sh',
128 copy: true)
129
130test_bio = executable ('test_bio',
131 ['test_bio.c'],
132 dependencies: [libgnunetutil_dep],
133 include_directories: [incdir, configuration_inc],
134 install: false)
135test('test_bio', test_bio,
136 workdir: meson.current_build_dir(),
137 suite: ['util', 'util-bio'])
138test_child_management = executable ('test_child_management',
139 ['test_child_management.c'],
140 dependencies: [libgnunetutil_dep],
141 include_directories: [incdir, configuration_inc],
142 install: false)
143test('test_child_management', test_child_management,
144 workdir: meson.current_build_dir(),
145 suite: ['util', 'util-child'])
146
147# test_client.nc
148
149testcommonalloc = executable ('test_common_allocation',
150 ['test_common_allocation.c'],
151 dependencies: [libgnunetutil_dep],
152 build_by_default: false,
153 include_directories: [incdir, configuration_inc],
154 install: false)
155test('test_common_allocation', testcommonalloc,
156 workdir: meson.current_build_dir(),
157 suite: ['util', 'util-common'])
158
159testcommonlog = executable ('test_common_logging',
160 ['test_common_logging.c'],
161 dependencies: [libgnunetutil_dep],
162 build_by_default: false,
163 include_directories: [incdir, configuration_inc],
164 install: false)
165test('test_common_logging', testcommonlog,
166 workdir: meson.current_build_dir(),
167 suite: ['util', 'util-common'])
168testcommonendian = executable ('test_common_endian',
169 ['test_common_endian.c'],
170 dependencies: [libgnunetutil_dep],
171 build_by_default: false,
172 include_directories: [incdir, configuration_inc],
173 install: false)
174test('test_common_endian', testcommonendian,
175 workdir: meson.current_build_dir(),
176 suite: ['util', 'util-common'])
177testconf = executable ('test_configuration',
178 ['test_configuration.c'],
179 dependencies: [libgnunetutil_dep],
180 build_by_default: false,
181 include_directories: [incdir, configuration_inc],
182 install: false)
183test('test_configuration', testconf,
184 workdir: meson.current_build_dir(),
185 suite: ['util', 'util-configuration'])
186testcontainerbloom = executable ('test_container_bloomfilter',
187 ['test_container_bloomfilter.c'],
188 dependencies: [libgnunetutil_dep],
189 include_directories: [incdir, configuration_inc],
190 build_by_default: false,
191 install: false)
192test('test_container_bloomfilter', testcontainerbloom,
193 workdir: meson.current_build_dir(),
194 suite: ['util', 'util-container'])
195
196testcontainerdll = executable ('test_container_dll',
197 ['test_container_dll.c'],
198 dependencies: [libgnunetutil_dep],
199 include_directories: [incdir, configuration_inc],
200 build_by_default: false,
201 install: false)
202test('test_container_dll', testcontainerdll,
203 workdir: meson.current_build_dir(),
204 suite: ['util', 'util-container'])
205
206testcontainermhm = executable ('test_container_multihashmap',
207 ['test_container_multihashmap.c'],
208 dependencies: [libgnunetutil_dep],
209 include_directories: [incdir, configuration_inc],
210 build_by_default: false,
211 install: false)
212test('test_container_multihashmap', testcontainermhm,
213 workdir: meson.current_build_dir(),
214 suite: ['util', 'util-container'])
215
216testcontainermhm32 = executable ('test_container_multihashmap32',
217 ['test_container_multihashmap32.c'],
218 dependencies: [libgnunetutil_dep],
219 include_directories: [incdir, configuration_inc],
220 build_by_default: false,
221 install: false)
222test('test_container_multihashmap32', testcontainermhm32,
223 workdir: meson.current_build_dir(),
224 suite: ['util', 'util-container'])
225
226testcontainermpm = executable ('test_container_multipeermap',
227 ['test_container_multipeermap.c'],
228 dependencies: [libgnunetutil_dep],
229 include_directories: [incdir, configuration_inc],
230 build_by_default: false,
231 install: false)
232test('test_container_multipeermap', testcontainermpm,
233 workdir: meson.current_build_dir(),
234 suite: ['util', 'util-container'])
235
236testcontainerheap = executable ('test_container_heap',
237 ['test_container_heap.c'],
238 dependencies: [libgnunetutil_dep],
239 include_directories: [incdir, configuration_inc],
240 build_by_default: false,
241 install: false)
242test('test_container_heap', testcontainerheap,
243 workdir: meson.current_build_dir(),
244 suite: ['util', 'util-container'])
245
246testcrypto_symmetric = executable ('test_crypto_symmetric',
247 ['test_crypto_symmetric.c'],
248 dependencies: [libgnunetutil_dep],
249 include_directories: [incdir, configuration_inc],
250 build_by_default: false,
251 install: false)
252test('test_crypto_symmetric', testcrypto_symmetric,
253 workdir: meson.current_build_dir(),
254 suite: ['util', 'util-crypto'])
255
256testcrypto_crc = executable ('test_crypto_crc',
257 ['test_crypto_crc.c'],
258 dependencies: [libgnunetutil_dep],
259 include_directories: [incdir, configuration_inc],
260 build_by_default: false,
261 install: false)
262test('test_crypto_crc', testcrypto_crc,
263 workdir: meson.current_build_dir(),
264 suite: ['util', 'util-crypto'])
265
266testcrypto_cs = executable ('test_crypto_cs',
267 ['test_crypto_cs.c'],
268 dependencies: [sodium_dep, libgnunetutil_dep],
269 include_directories: [incdir, configuration_inc],
270 build_by_default: false,
271 install: false)
272test('test_crypto_cs', testcrypto_cs,
273 workdir: meson.current_build_dir(),
274 suite: ['util', 'util-crypto'])
275
276testcrypto_ecdsa = executable ('test_crypto_ecdsa',
277 ['test_crypto_ecdsa.c'],
278 dependencies: [gcrypt_dep, libgnunetutil_dep],
279 include_directories: [incdir, configuration_inc],
280 build_by_default: false,
281 install: false)
282test('test_crypto_ecdsa', testcrypto_ecdsa,
283 workdir: meson.current_build_dir(),
284 suite: ['util', 'util-crypto'])
285
286testcrypto_eddsa = executable ('test_crypto_eddsa',
287 ['test_crypto_eddsa.c'],
288 dependencies: [gcrypt_dep, libgnunetutil_dep],
289 include_directories: [incdir, configuration_inc],
290 build_by_default: false,
291 install: false)
292test('test_crypto_eddsa', testcrypto_eddsa,
293 workdir: meson.current_build_dir(),
294 suite: ['util', 'util-crypto'])
295
296testcrypto_ecdh_eddsa = executable ('test_crypto_ecdh_eddsa',
297 ['test_crypto_ecdh_eddsa.c'],
298 dependencies: [gcrypt_dep, libgnunetutil_dep],
299 include_directories: [incdir, configuration_inc],
300 build_by_default: false,
301 install: false)
302test('test_crypto_ecdh_eddsa', testcrypto_ecdh_eddsa,
303 workdir: meson.current_build_dir(),
304 suite: ['util', 'util-crypto'])
305
306testcrypto_ecdh_ecdsa = executable ('test_crypto_ecdh_ecdsa',
307 ['test_crypto_ecdh_ecdsa.c'],
308 dependencies: [gcrypt_dep, libgnunetutil_dep],
309 include_directories: [incdir, configuration_inc],
310 build_by_default: false,
311 install: false)
312test('test_crypto_ecdh_ecdsa', testcrypto_ecdh_ecdsa,
313 workdir: meson.current_build_dir(),
314 suite: ['util', 'util-crypto'])
315
316testcrypto_edx25519 = executable ('test_crypto_edx25519',
317 ['test_crypto_edx25519.c'],
318 dependencies: [gcrypt_dep, libgnunetutil_dep],
319 include_directories: [incdir, configuration_inc],
320 build_by_default: false,
321 install: false)
322test('test_crypto_edx25519', testcrypto_edx25519,
323 workdir: meson.current_build_dir(),
324 suite: ['util', 'util-crypto'])
325
326testcrypto_ecc_dlog = executable ('test_crypto_ecc_dlog',
327 ['test_crypto_ecc_dlog.c'],
328 dependencies: [gcrypt_dep, sodium_dep, libgnunetutil_dep],
329 include_directories: [incdir, configuration_inc],
330 build_by_default: false,
331 install: false)
332test('test_crypto_ecc_dlog', testcrypto_ecc_dlog,
333 workdir: meson.current_build_dir(),
334 suite: ['util', 'util-crypto'])
335
336testcrypto_elligator = executable ('test_crypto_elligator',
337 ['test_crypto_elligator.c'],
338 dependencies: [gcrypt_dep, libgnunetutil_dep, lgmp_dep, sodium_dep],
339 include_directories: [incdir, configuration_inc],
340 build_by_default: false,
341 install: false)
342test('test_crypto_elligator', testcrypto_elligator,
343 workdir: meson.current_build_dir(),
344 suite: ['util', 'util-crypto'])
345
346testcrypto_hash = executable ('test_crypto_hash',
347 ['test_crypto_hash.c'],
348 dependencies: [gcrypt_dep, libgnunetutil_dep],
349 include_directories: [incdir, configuration_inc],
350 build_by_default: false,
351 install: false)
352test('test_crypto_hash', testcrypto_hash,
353 workdir: meson.current_build_dir(),
354 suite: ['util', 'util-crypto'])
355
356testcrypto_hash_context = executable ('test_crypto_hash_context',
357 ['test_crypto_hash_context.c'],
358 dependencies: [gcrypt_dep, libgnunetutil_dep],
359 include_directories: [incdir, configuration_inc],
360 build_by_default: false,
361 install: false)
362test('test_crypto_hash_context', testcrypto_hash_context,
363 workdir: meson.current_build_dir(),
364 suite: ['util', 'util-crypto'])
365
366testcrypto_hkdf = executable ('test_crypto_hkdf',
367 ['test_crypto_hkdf.c'],
368 dependencies: [gcrypt_dep, libgnunetutil_dep],
369 include_directories: [incdir, configuration_inc],
370 build_by_default: false,
371 install: false)
372test('test_crypto_hkdf', testcrypto_hkdf,
373 workdir: meson.current_build_dir(),
374 suite: ['util', 'util-crypto'])
375
376testcrypto_kdf = executable ('test_crypto_kdf',
377 ['test_crypto_kdf.c'],
378 dependencies: [gcrypt_dep, libgnunetutil_dep],
379 include_directories: [incdir, configuration_inc],
380 build_by_default: false,
381 install: false)
382test('test_crypto_kdf', testcrypto_kdf,
383 workdir: meson.current_build_dir(),
384 suite: ['util', 'util-crypto'])
385
386testcrypto_paillier = executable ('test_crypto_paillier',
387 ['test_crypto_paillier.c'],
388 dependencies: [gcrypt_dep, libgnunetutil_dep],
389 include_directories: [incdir, configuration_inc],
390 build_by_default: false,
391 install: false)
392test('test_crypto_paillier', testcrypto_paillier,
393 workdir: meson.current_build_dir(),
394 suite: ['util', 'util-crypto'])
395
396testcrypto_random = executable ('test_crypto_random',
397 ['test_crypto_random.c'],
398 dependencies: [gcrypt_dep, libgnunetutil_dep],
399 include_directories: [incdir, configuration_inc],
400 build_by_default: false,
401 install: false)
402test('test_crypto_random', testcrypto_random,
403 workdir: meson.current_build_dir(),
404 suite: ['util', 'util-crypto'])
405
406testcrypto_rsa = executable ('test_crypto_rsa',
407 ['test_crypto_rsa.c'],
408 dependencies: [gcrypt_dep, libgnunetutil_dep],
409 include_directories: [incdir, configuration_inc],
410 build_by_default: false,
411 install: false)
412test('test_crypto_rsa', testcrypto_rsa,
413 workdir: meson.current_build_dir(),
414 suite: ['util', 'util-crypto'])
415
416testdisk = executable ('test_disk',
417 ['test_disk.c'],
418 dependencies: [libgnunetutil_dep],
419 include_directories: [incdir, configuration_inc],
420 build_by_default: false,
421 install: false)
422test('test_disk', testdisk,
423 workdir: meson.current_build_dir(),
424 suite: ['util', 'util-misc'])
425
426testgetopt = executable ('test_getopt',
427 ['test_getopt.c'],
428 dependencies: [libgnunetutil_dep],
429 include_directories: [incdir, configuration_inc],
430 build_by_default: false,
431 install: false)
432test('test_getopt', testgetopt,
433 workdir: meson.current_build_dir(),
434 suite: ['util', 'util-misc'])
435
436testhexcoder = executable ('test_hexcoder',
437 ['test_hexcoder.c'],
438 dependencies: [libgnunetutil_dep],
439 include_directories: [incdir, configuration_inc],
440 build_by_default: false,
441 install: false)
442test('test_hexcoder', testhexcoder,
443 workdir: meson.current_build_dir(),
444 suite: ['util', 'util-misc'])
445
446testmq = executable ('test_mq',
447 ['test_mq.c'],
448 dependencies: [libgnunetutil_dep],
449 include_directories: [incdir, configuration_inc],
450 build_by_default: false,
451 install: false)
452test('test_mq', testmq,
453 workdir: meson.current_build_dir(),
454 suite: ['util', 'util-misc'])
455
456testos_network = executable ('test_os_network',
457 ['test_os_network.c'],
458 dependencies: [libgnunetutil_dep],
459 include_directories: [incdir, configuration_inc],
460 build_by_default: false,
461 install: false)
462test('test_os_network', testos_network,
463 workdir: meson.current_build_dir(),
464 suite: ['util', 'util-os'])
465
466testpeer = executable ('test_peer',
467 ['test_peer.c'],
468 dependencies: [gcrypt_dep, libgnunetutil_dep],
469 include_directories: [incdir, configuration_inc],
470 build_by_default: false,
471 install: false)
472test('test_peer', testpeer,
473 workdir: meson.current_build_dir(),
474 suite: ['util', 'util-misc'])
475
476testplugin_plug = shared_module ('gnunet_plugin_utiltest',
477 ['test_plugin_plug.c'],
478 dependencies: [libgnunetutil_dep],
479 include_directories: [incdir, configuration_inc],
480 build_by_default: false,
481 install: false)
482testplugin = executable ('test_plugin',
483 ['test_plugin.c'],
484 dependencies: [libgnunetutil_dep],
485 include_directories: [incdir, configuration_inc],
486 build_by_default: false,
487 install: false)
488test('test_plugin', testplugin,
489 depends: [testplugin_plug],
490 workdir: meson.current_build_dir(),
491 suite: ['util', 'util-misc'])
492
493testprogram = executable ('test_program',
494 ['test_program.c'],
495 dependencies: [libgnunetutil_dep],
496 include_directories: [incdir, configuration_inc],
497 build_by_default: false,
498 install: false)
499test('test_program', testprogram,
500 workdir: meson.current_build_dir(),
501 suite: ['util', 'util-misc'])
502
503testregex = executable ('test_regex',
504 ['test_regex.c'],
505 dependencies: [libgnunetutil_dep],
506 include_directories: [incdir, configuration_inc],
507 build_by_default: false,
508 install: false)
509test('test_regex', testregex,
510 workdir: meson.current_build_dir(),
511 suite: ['util', 'util-misc'])
512
513# test_resolver_api.nc
514
515testscheduler = executable ('test_scheduler',
516 ['test_scheduler.c'],
517 dependencies: [libgnunetutil_dep],
518 include_directories: [incdir, configuration_inc],
519 build_by_default: false,
520 install: false)
521test('test_scheduler', testscheduler,
522 workdir: meson.current_build_dir(),
523 suite: ['util', 'util-scheduler'])
524
525testscheduler_delay = executable ('test_scheduler_delay',
526 ['test_scheduler_delay.c'],
527 dependencies: [libgnunetutil_dep],
528 include_directories: [incdir, configuration_inc],
529 build_by_default: false,
530 install: false)
531test('test_scheduler_delay', testscheduler_delay,
532 workdir: meson.current_build_dir(),
533 suite: ['util', 'util-scheduler'])
534
535testscheduler_hogging_cancel = executable ('test_scheduler_hogging_cancel',
536 ['test_scheduler_hogging_cancel.c'],
537 dependencies: [libgnunetutil_dep],
538 include_directories: [incdir, configuration_inc],
539 build_by_default: false,
540 install: false)
541test('test_scheduler_hogging_cancel', testscheduler_hogging_cancel,
542 workdir: meson.current_build_dir(),
543 suite: ['util', 'util-scheduler'])
544
545testscheduler_hogging_prio = executable ('test_scheduler_hogging_priority',
546 ['test_scheduler_hogging_priority.c'],
547 dependencies: [libgnunetutil_dep],
548 include_directories: [incdir, configuration_inc],
549 build_by_default: false,
550 install: false)
551test('test_scheduler_hogging_priority', testscheduler_hogging_prio,
552 workdir: meson.current_build_dir(),
553 suite: ['util', 'util-scheduler'])
554
555testservice = executable ('test_service',
556 ['test_service.c'],
557 dependencies: [libgnunetutil_dep],
558 include_directories: [incdir, configuration_inc],
559 build_by_default: false,
560 install: false)
561test('test_service', testservice,
562 workdir: meson.current_build_dir(),
563 suite: ['util', 'util-misc'])
564
565teststrings = executable ('test_strings',
566 ['test_strings.c'],
567 dependencies: [libgnunetutil_dep],
568 include_directories: [incdir, configuration_inc],
569 build_by_default: false,
570 install: false)
571test('test_strings', teststrings,
572 workdir: meson.current_build_dir(),
573 suite: ['util', 'util-strings'])
574
575teststrings_todata = executable ('test_strings_to_data',
576 ['test_strings_to_data.c'],
577 dependencies: [libgnunetutil_dep],
578 include_directories: [incdir, configuration_inc],
579 build_by_default: false,
580 install: false)
581test('test_strings_to_data', teststrings_todata,
582 workdir: meson.current_build_dir(),
583 suite: ['util', 'util-strings'])
584
585testspeedup = executable ('test_speedup',
586 ['test_speedup.c'],
587 dependencies: [libgnunetutil_dep],
588 include_directories: [incdir, configuration_inc],
589 build_by_default: false,
590 install: false)
591test('test_speedup', testspeedup,
592 workdir: meson.current_build_dir(),
593 suite: ['util', 'util-misc'])
594
595testtime = executable ('test_time',
596 ['test_time.c'],
597 dependencies: [libgnunetutil_dep],
598 include_directories: [incdir, configuration_inc],
599 build_by_default: false,
600 install: false)
601test('test_time', testtime,
602 workdir: meson.current_build_dir(),
603 suite: ['util', 'util-misc'])
604
605testtun = executable ('test_tun',
606 ['test_tun.c'],
607 dependencies: [libgnunetutil_dep],
608 include_directories: [incdir, configuration_inc],
609 build_by_default: false,
610 install: false)
611test('test_tun', testtun,
612 workdir: meson.current_build_dir(),
613 suite: ['util', 'util-misc'])
614
615testuri = executable ('test_uri',
616 ['test_uri.c'],
617 dependencies: [libgnunetutil_dep],
618 include_directories: [incdir, configuration_inc],
619 build_by_default: false,
620 install: false)
621test('test_uri', testuri,
622 workdir: meson.current_build_dir(),
623 suite: ['util', 'util-misc'])
624
625testos_start_process = executable ('test_os_start_process',
626 ['test_os_start_process.c'],
627 dependencies: [libgnunetutil_dep],
628 include_directories: [incdir, configuration_inc],
629 build_by_default: false,
630 install: false)
631test('test_os_start_process', testos_start_process,
632 workdir: meson.current_build_dir(),
633 suite: ['util', 'util-os'])
634
635testcommon_logging_runtime = executable ('test_common_logging_runtime_loglevels',
636 ['test_common_logging_runtime_loglevels.c'],
637 dependencies: [libgnunetutil_dep],
638 include_directories: [incdir, configuration_inc],
639 build_by_default: false,
640 install: false)
641test('test_common_logging_runtime_loglevels', testcommon_logging_runtime,
642 depends: [logging_dummy],
643 workdir: meson.current_build_dir(),
644 suite: ['util', 'util-common'])
645
646testutil_perf = [
647 'perf_crypto_asymmetric',
648 # 'perf_crypto_cs', FIXME FTBFS
649 'perf_crypto_ecc_dlog',
650 'perf_crypto_hash',
651 'perf_crypto_paillier',
652 'perf_crypto_rsa',
653 'perf_crypto_symmetric',
654 'perf_malloc',
655 'perf_mq',
656 'perf_scheduler',
657]
658
659foreach t : testutil_perf
660
661 test_filename = t + '.sh'
662 testbin = executable(t, [t + '.c'],
663 dependencies: [libgnunetutil_dep, gcrypt_dep, sodium_dep],
664 include_directories: [incdir, configuration_inc],
665 build_by_default: false,
666 install: false)
667 test(t, testbin, suite: ['util', 'perf'],
668 workdir: meson.current_build_dir())
669endforeach
diff --git a/src/lib/util/mq.c b/src/lib/util/mq.c
new file mode 100644
index 000000000..de0cff0c2
--- /dev/null
+++ b/src/lib/util/mq.c
@@ -0,0 +1,1030 @@
1/*
2 This file is part of GNUnet.
3 Copyright (C) 2012-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 * @author Florian Dold
23 * @file util/mq.c
24 * @brief general purpose request queue
25 */
26
27#include "platform.h"
28#include "gnunet_util_lib.h"
29
30#define LOG(kind, ...) GNUNET_log_from (kind, "util-mq", __VA_ARGS__)
31
32
33struct GNUNET_MQ_Envelope
34{
35 /**
36 * Messages are stored in a linked list.
37 * Each queue has its own list of envelopes.
38 */
39 struct GNUNET_MQ_Envelope *next;
40
41 /**
42 * Messages are stored in a linked list
43 * Each queue has its own list of envelopes.
44 */
45 struct GNUNET_MQ_Envelope *prev;
46
47 /**
48 * Actual allocated message header.
49 * The GNUNET_MQ_Envelope header is allocated at
50 * the end of the message.
51 */
52 struct GNUNET_MessageHeader *mh;
53
54 /**
55 * Queue the message is queued in, NULL if message is not queued.
56 */
57 struct GNUNET_MQ_Handle *parent_queue;
58
59 /**
60 * Called after the message was sent irrevocably.
61 */
62 GNUNET_SCHEDULER_TaskCallback sent_cb;
63
64 /**
65 * Closure for @e send_cb
66 */
67 void *sent_cls;
68
69 /**
70 * Flags that were set for this envelope by
71 * #GNUNET_MQ_env_set_options(). Only valid if
72 * @e have_custom_options is set.
73 */
74 enum GNUNET_MQ_PriorityPreferences priority;
75
76 /**
77 * Did the application call #GNUNET_MQ_env_set_options()?
78 */
79 int have_custom_options;
80};
81
82
83/**
84 * Handle to a message queue.
85 */
86struct GNUNET_MQ_Handle
87{
88 /**
89 * Handlers array, or NULL if the queue should not receive messages
90 */
91 struct GNUNET_MQ_MessageHandler *handlers;
92
93 /**
94 * Actual implementation of message sending,
95 * called when a message is added
96 */
97 GNUNET_MQ_SendImpl send_impl;
98
99 /**
100 * Implementation-dependent queue destruction function
101 */
102 GNUNET_MQ_DestroyImpl destroy_impl;
103
104 /**
105 * Implementation-dependent send cancel function
106 */
107 GNUNET_MQ_CancelImpl cancel_impl;
108
109 /**
110 * Implementation-specific state
111 */
112 void *impl_state;
113
114 /**
115 * Callback will be called when an error occurs.
116 */
117 GNUNET_MQ_ErrorHandler error_handler;
118
119 /**
120 * Closure for the error handler.
121 */
122 void *error_handler_cls;
123
124 /**
125 * Task to asynchronously run #impl_send_continue().
126 */
127 struct GNUNET_SCHEDULER_Task *send_task;
128
129 /**
130 * Linked list of messages pending to be sent
131 */
132 struct GNUNET_MQ_Envelope *envelope_head;
133
134 /**
135 * Linked list of messages pending to be sent
136 */
137 struct GNUNET_MQ_Envelope *envelope_tail;
138
139 /**
140 * Message that is currently scheduled to be
141 * sent. Not the head of the message queue, as the implementation
142 * needs to know if sending has been already scheduled or not.
143 */
144 struct GNUNET_MQ_Envelope *current_envelope;
145
146 /**
147 * Map of associations, lazily allocated
148 */
149 struct GNUNET_CONTAINER_MultiHashMap32 *assoc_map;
150
151 /**
152 * Functions to call on queue destruction; kept in a DLL.
153 */
154 struct GNUNET_MQ_DestroyNotificationHandle *dnh_head;
155
156 /**
157 * Functions to call on queue destruction; kept in a DLL.
158 */
159 struct GNUNET_MQ_DestroyNotificationHandle *dnh_tail;
160
161 /**
162 * Flags that were set for this queue by
163 * #GNUNET_MQ_set_options(). Default is 0.
164 */
165 enum GNUNET_MQ_PriorityPreferences priority;
166
167 /**
168 * Next id that should be used for the @e assoc_map,
169 * initialized lazily to a random value together with
170 * @e assoc_map
171 */
172 uint32_t assoc_id;
173
174 /**
175 * Number of entries we have in the envelope-DLL.
176 */
177 unsigned int queue_length;
178
179 /**
180 * True if GNUNET_MQ_impl_send_in_flight() was called.
181 */
182 bool in_flight;
183};
184
185
186void
187GNUNET_MQ_inject_message (struct GNUNET_MQ_Handle *mq,
188 const struct GNUNET_MessageHeader *mh)
189{
190 enum GNUNET_GenericReturnValue ret;
191
192 ret = GNUNET_MQ_handle_message (mq->handlers,
193 mh);
194 if (GNUNET_SYSERR == ret)
195 {
196 GNUNET_break_op (0);
197 GNUNET_MQ_inject_error (mq,
198 GNUNET_MQ_ERROR_MALFORMED);
199 return;
200 }
201}
202
203
204enum GNUNET_GenericReturnValue
205GNUNET_MQ_handle_message (const struct GNUNET_MQ_MessageHandler *handlers,
206 const struct GNUNET_MessageHeader *mh)
207{
208 bool handled = false;
209 uint16_t msize = ntohs (mh->size);
210 uint16_t mtype = ntohs (mh->type);
211
212 LOG (GNUNET_ERROR_TYPE_DEBUG,
213 "Received message of type %u and size %u\n",
214 mtype,
215 msize);
216 if (NULL == handlers)
217 goto done;
218 for (const struct GNUNET_MQ_MessageHandler *handler = handlers;
219 NULL != handler->cb;
220 handler++)
221 {
222 if (handler->type == mtype)
223 {
224 handled = true;
225 if ( (handler->expected_size > msize) ||
226 ( (handler->expected_size != msize) &&
227 (NULL == handler->mv) ) )
228 {
229 /* Too small, or not an exact size and
230 no 'mv' handler to check rest */
231 LOG (GNUNET_ERROR_TYPE_ERROR,
232 "Received malformed message of type %u\n",
233 (unsigned int) handler->type);
234 return GNUNET_SYSERR;
235 }
236 if ( (NULL == handler->mv) ||
237 (GNUNET_OK ==
238 handler->mv (handler->cls,
239 mh)) )
240 {
241 /* message well-formed, pass to handler */
242 handler->cb (handler->cls, mh);
243 }
244 else
245 {
246 /* Message rejected by check routine */
247 LOG (GNUNET_ERROR_TYPE_ERROR,
248 "Received malformed message of type %u\n",
249 (unsigned int) handler->type);
250 return GNUNET_SYSERR;
251 }
252 break;
253 }
254 }
255done:
256 if (! handled)
257 {
258 LOG (GNUNET_ERROR_TYPE_INFO,
259 "No handler for message of type %u and size %u\n",
260 mtype,
261 msize);
262 return GNUNET_NO;
263 }
264 return GNUNET_OK;
265}
266
267
268void
269GNUNET_MQ_inject_error (struct GNUNET_MQ_Handle *mq,
270 enum GNUNET_MQ_Error error)
271{
272 if (NULL == mq->error_handler)
273 {
274 LOG (GNUNET_ERROR_TYPE_WARNING,
275 "Got error %d, but no handler installed\n",
276 (int) error);
277 return;
278 }
279 mq->error_handler (mq->error_handler_cls,
280 error);
281}
282
283
284void
285GNUNET_MQ_discard (struct GNUNET_MQ_Envelope *ev)
286{
287 GNUNET_assert (NULL == ev->parent_queue);
288 GNUNET_free (ev);
289}
290
291
292unsigned int
293GNUNET_MQ_get_length (struct GNUNET_MQ_Handle *mq)
294{
295 if (! mq->in_flight)
296 {
297 return mq->queue_length;
298 }
299 return mq->queue_length - 1;
300}
301
302
303void
304GNUNET_MQ_send (struct GNUNET_MQ_Handle *mq,
305 struct GNUNET_MQ_Envelope *ev)
306{
307 GNUNET_assert (NULL != mq);
308 GNUNET_assert (NULL == ev->parent_queue);
309
310 mq->queue_length++;
311 if (mq->queue_length >= 10000000)
312 {
313 /* This would seem like a bug... */
314 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
315 "MQ with %u entries extended by message of type %u (FC broken?)\n",
316 (unsigned int) mq->queue_length,
317 (unsigned int) ntohs (ev->mh->type));
318 }
319 ev->parent_queue = mq;
320 /* is the implementation busy? queue it! */
321 if ((NULL != mq->current_envelope) || (NULL != mq->send_task))
322 {
323 GNUNET_CONTAINER_DLL_insert_tail (mq->envelope_head,
324 mq->envelope_tail,
325 ev);
326 return;
327 }
328 GNUNET_assert (NULL == mq->envelope_head);
329 mq->current_envelope = ev;
330
331 LOG (GNUNET_ERROR_TYPE_DEBUG,
332 "sending message of type %u and size %u, queue empty (MQ: %p)\n",
333 ntohs (ev->mh->type),
334 ntohs (ev->mh->size),
335 mq);
336
337 mq->send_impl (mq,
338 ev->mh,
339 mq->impl_state);
340}
341
342
343struct GNUNET_MQ_Envelope *
344GNUNET_MQ_unsent_head (struct GNUNET_MQ_Handle *mq)
345{
346 struct GNUNET_MQ_Envelope *env;
347
348 env = mq->envelope_head;
349 GNUNET_CONTAINER_DLL_remove (mq->envelope_head,
350 mq->envelope_tail,
351 env);
352 mq->queue_length--;
353 env->parent_queue = NULL;
354 return env;
355}
356
357
358struct GNUNET_MQ_Envelope *
359GNUNET_MQ_env_copy (struct GNUNET_MQ_Envelope *env)
360{
361 GNUNET_assert (NULL == env->next);
362 GNUNET_assert (NULL == env->parent_queue);
363 GNUNET_assert (NULL == env->sent_cb);
364 GNUNET_assert (GNUNET_NO == env->have_custom_options);
365 return GNUNET_MQ_msg_copy (env->mh);
366}
367
368
369void
370GNUNET_MQ_send_copy (struct GNUNET_MQ_Handle *mq,
371 const struct GNUNET_MQ_Envelope *ev)
372{
373 struct GNUNET_MQ_Envelope *env;
374 uint16_t msize;
375
376 msize = ntohs (ev->mh->size);
377 env = GNUNET_malloc (sizeof(struct GNUNET_MQ_Envelope) + msize);
378 env->mh = (struct GNUNET_MessageHeader *) &env[1];
379 env->sent_cb = ev->sent_cb;
380 env->sent_cls = ev->sent_cls;
381 GNUNET_memcpy (&env[1], ev->mh, msize);
382 GNUNET_MQ_send (mq, env);
383}
384
385
386/**
387 * Task run to call the send implementation for the next queued
388 * message, if any. Only useful for implementing message queues,
389 * results in undefined behavior if not used carefully.
390 *
391 * @param cls message queue to send the next message with
392 */
393static void
394impl_send_continue (void *cls)
395{
396 struct GNUNET_MQ_Handle *mq = cls;
397
398 mq->send_task = NULL;
399 /* call is only valid if we're actually currently sending
400 * a message */
401 if (NULL == mq->envelope_head)
402 return;
403 mq->current_envelope = mq->envelope_head;
404 GNUNET_CONTAINER_DLL_remove (mq->envelope_head,
405 mq->envelope_tail,
406 mq->current_envelope);
407
408 LOG (GNUNET_ERROR_TYPE_DEBUG,
409 "sending message of type %u and size %u from queue (MQ: %p)\n",
410 ntohs (mq->current_envelope->mh->type),
411 ntohs (mq->current_envelope->mh->size),
412 mq);
413
414 mq->send_impl (mq,
415 mq->current_envelope->mh,
416 mq->impl_state);
417}
418
419
420void
421GNUNET_MQ_impl_send_continue (struct GNUNET_MQ_Handle *mq)
422{
423 struct GNUNET_MQ_Envelope *current_envelope;
424 GNUNET_SCHEDULER_TaskCallback cb;
425
426 GNUNET_assert (0 < mq->queue_length);
427 mq->queue_length--;
428 mq->in_flight = false;
429 current_envelope = mq->current_envelope;
430 current_envelope->parent_queue = NULL;
431 mq->current_envelope = NULL;
432 GNUNET_assert (NULL == mq->send_task);
433 mq->send_task = GNUNET_SCHEDULER_add_now (&impl_send_continue, mq);
434 if (NULL != (cb = current_envelope->sent_cb))
435 {
436 current_envelope->sent_cb = NULL;
437 cb (current_envelope->sent_cls);
438 }
439 GNUNET_free (current_envelope);
440}
441
442
443void
444GNUNET_MQ_impl_send_in_flight (struct GNUNET_MQ_Handle *mq)
445{
446 struct GNUNET_MQ_Envelope *current_envelope;
447 GNUNET_SCHEDULER_TaskCallback cb;
448
449 mq->in_flight = true;
450 /* call is only valid if we're actually currently sending
451 * a message */
452 current_envelope = mq->current_envelope;
453 GNUNET_assert (NULL != current_envelope);
454 /* can't call cancel from now on anymore */
455 current_envelope->parent_queue = NULL;
456 if (NULL != (cb = current_envelope->sent_cb))
457 {
458 current_envelope->sent_cb = NULL;
459 cb (current_envelope->sent_cls);
460 }
461}
462
463
464struct GNUNET_MQ_Handle *
465GNUNET_MQ_queue_for_callbacks (GNUNET_MQ_SendImpl send,
466 GNUNET_MQ_DestroyImpl destroy,
467 GNUNET_MQ_CancelImpl cancel,
468 void *impl_state,
469 const struct GNUNET_MQ_MessageHandler *handlers,
470 GNUNET_MQ_ErrorHandler error_handler,
471 void *error_handler_cls)
472{
473 struct GNUNET_MQ_Handle *mq;
474
475 mq = GNUNET_new (struct GNUNET_MQ_Handle);
476 mq->send_impl = send;
477 mq->destroy_impl = destroy;
478 mq->cancel_impl = cancel;
479 mq->handlers = GNUNET_MQ_copy_handlers (handlers);
480 mq->error_handler = error_handler;
481 mq->error_handler_cls = error_handler_cls;
482 mq->impl_state = impl_state;
483
484 return mq;
485}
486
487
488void
489GNUNET_MQ_set_handlers_closure (struct GNUNET_MQ_Handle *mq,
490 void *handlers_cls)
491{
492 if (NULL == mq->handlers)
493 return;
494 for (unsigned int i = 0; NULL != mq->handlers[i].cb; i++)
495 mq->handlers[i].cls = handlers_cls;
496}
497
498
499const struct GNUNET_MessageHeader *
500GNUNET_MQ_impl_current (struct GNUNET_MQ_Handle *mq)
501{
502 GNUNET_assert (NULL != mq->current_envelope);
503 GNUNET_assert (NULL != mq->current_envelope->mh);
504 return mq->current_envelope->mh;
505}
506
507
508void *
509GNUNET_MQ_impl_state (struct GNUNET_MQ_Handle *mq)
510{
511 return mq->impl_state;
512}
513
514
515struct GNUNET_MQ_Envelope *
516GNUNET_MQ_msg_ (struct GNUNET_MessageHeader **mhp,
517 uint16_t size,
518 uint16_t type)
519{
520 struct GNUNET_MQ_Envelope *ev;
521
522 ev = GNUNET_malloc (size + sizeof(struct GNUNET_MQ_Envelope));
523 ev->mh = (struct GNUNET_MessageHeader *) &ev[1];
524 ev->mh->size = htons (size);
525 ev->mh->type = htons (type);
526 if (NULL != mhp)
527 *mhp = ev->mh;
528 return ev;
529}
530
531
532struct GNUNET_MQ_Envelope *
533GNUNET_MQ_msg_copy (const struct GNUNET_MessageHeader *hdr)
534{
535 struct GNUNET_MQ_Envelope *mqm;
536 uint16_t size = ntohs (hdr->size);
537
538 mqm = GNUNET_malloc (sizeof(*mqm) + size);
539 mqm->mh = (struct GNUNET_MessageHeader *) &mqm[1];
540 GNUNET_memcpy (mqm->mh,
541 hdr,
542 size);
543 return mqm;
544}
545
546
547struct GNUNET_MQ_Envelope *
548GNUNET_MQ_msg_nested_mh_ (struct GNUNET_MessageHeader **mhp,
549 uint16_t base_size,
550 uint16_t type,
551 const struct GNUNET_MessageHeader *nested_mh)
552{
553 struct GNUNET_MQ_Envelope *mqm;
554 uint16_t size;
555
556 if (NULL == nested_mh)
557 return GNUNET_MQ_msg_ (mhp,
558 base_size,
559 type);
560 size = base_size + ntohs (nested_mh->size);
561 /* check for uint16_t overflow */
562 if (size < base_size)
563 return NULL;
564 mqm = GNUNET_MQ_msg_ (mhp,
565 size,
566 type);
567 GNUNET_memcpy ((char *) mqm->mh + base_size,
568 nested_mh,
569 ntohs (nested_mh->size));
570 return mqm;
571}
572
573
574uint32_t
575GNUNET_MQ_assoc_add (struct GNUNET_MQ_Handle *mq,
576 void *assoc_data)
577{
578 uint32_t id;
579
580 if (NULL == mq->assoc_map)
581 {
582 mq->assoc_map = GNUNET_CONTAINER_multihashmap32_create (8);
583 mq->assoc_id = 1;
584 }
585 id = mq->assoc_id++;
586 GNUNET_assert (GNUNET_OK ==
587 GNUNET_CONTAINER_multihashmap32_put (
588 mq->assoc_map,
589 id,
590 assoc_data,
591 GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_ONLY));
592 return id;
593}
594
595
596/**
597 * Get the data associated with a @a request_id in a queue
598 *
599 * @param mq the message queue with the association
600 * @param request_id the request id we are interested in
601 * @return the associated data
602 */
603void *
604GNUNET_MQ_assoc_get (struct GNUNET_MQ_Handle *mq,
605 uint32_t request_id)
606{
607 if (NULL == mq->assoc_map)
608 return NULL;
609 return GNUNET_CONTAINER_multihashmap32_get (mq->assoc_map,
610 request_id);
611}
612
613
614/**
615 * Remove the association for a @a request_id
616 *
617 * @param mq the message queue with the association
618 * @param request_id the request id we want to remove
619 * @return the associated data
620 */
621void *
622GNUNET_MQ_assoc_remove (struct GNUNET_MQ_Handle *mq,
623 uint32_t request_id)
624{
625 void *val;
626
627 if (NULL == mq->assoc_map)
628 return NULL;
629 val = GNUNET_CONTAINER_multihashmap32_get (mq->assoc_map,
630 request_id);
631 GNUNET_CONTAINER_multihashmap32_remove_all (mq->assoc_map,
632 request_id);
633 return val;
634}
635
636
637void
638GNUNET_MQ_notify_sent (struct GNUNET_MQ_Envelope *ev,
639 GNUNET_SCHEDULER_TaskCallback cb,
640 void *cb_cls)
641{
642 /* allow setting *OR* clearing callback */
643 GNUNET_assert ((NULL == ev->sent_cb) || (NULL == cb));
644 ev->sent_cb = cb;
645 ev->sent_cls = cb_cls;
646}
647
648
649/**
650 * Handle we return for callbacks registered to be
651 * notified when #GNUNET_MQ_destroy() is called on a queue.
652 */
653struct GNUNET_MQ_DestroyNotificationHandle
654{
655 /**
656 * Kept in a DLL.
657 */
658 struct GNUNET_MQ_DestroyNotificationHandle *prev;
659
660 /**
661 * Kept in a DLL.
662 */
663 struct GNUNET_MQ_DestroyNotificationHandle *next;
664
665 /**
666 * Queue to notify about.
667 */
668 struct GNUNET_MQ_Handle *mq;
669
670 /**
671 * Function to call.
672 */
673 GNUNET_SCHEDULER_TaskCallback cb;
674
675 /**
676 * Closure for @e cb.
677 */
678 void *cb_cls;
679};
680
681
682void
683GNUNET_MQ_destroy (struct GNUNET_MQ_Handle *mq)
684{
685 struct GNUNET_MQ_DestroyNotificationHandle *dnh;
686
687 if (NULL != mq->destroy_impl)
688 {
689 mq->destroy_impl (mq, mq->impl_state);
690 }
691 if (NULL != mq->send_task)
692 {
693 GNUNET_SCHEDULER_cancel (mq->send_task);
694 mq->send_task = NULL;
695 }
696 while (NULL != mq->envelope_head)
697 {
698 struct GNUNET_MQ_Envelope *ev;
699
700 ev = mq->envelope_head;
701 ev->parent_queue = NULL;
702 GNUNET_CONTAINER_DLL_remove (mq->envelope_head, mq->envelope_tail, ev);
703 GNUNET_assert (0 < mq->queue_length);
704 mq->queue_length--;
705 LOG (GNUNET_ERROR_TYPE_DEBUG,
706 "MQ destroy drops message of type %u\n",
707 ntohs (ev->mh->type));
708 GNUNET_MQ_discard (ev);
709 }
710 if (NULL != mq->current_envelope)
711 {
712 /* we can only discard envelopes that
713 * are not queued! */
714 mq->current_envelope->parent_queue = NULL;
715 LOG (GNUNET_ERROR_TYPE_DEBUG,
716 "MQ destroy drops current message of type %u\n",
717 ntohs (mq->current_envelope->mh->type));
718 GNUNET_MQ_discard (mq->current_envelope);
719 mq->current_envelope = NULL;
720 GNUNET_assert (0 < mq->queue_length);
721 mq->queue_length--;
722 }
723 GNUNET_assert (0 == mq->queue_length);
724 while (NULL != (dnh = mq->dnh_head))
725 {
726 dnh->cb (dnh->cb_cls);
727 GNUNET_MQ_destroy_notify_cancel (dnh);
728 }
729 if (NULL != mq->assoc_map)
730 {
731 GNUNET_CONTAINER_multihashmap32_destroy (mq->assoc_map);
732 mq->assoc_map = NULL;
733 }
734 GNUNET_free (mq->handlers);
735 GNUNET_free (mq);
736}
737
738
739const struct GNUNET_MessageHeader *
740GNUNET_MQ_extract_nested_mh_ (const struct GNUNET_MessageHeader *mh,
741 uint16_t base_size)
742{
743 uint16_t whole_size;
744 uint16_t nested_size;
745 const struct GNUNET_MessageHeader *nested_msg;
746
747 whole_size = ntohs (mh->size);
748 GNUNET_assert (whole_size >= base_size);
749 nested_size = whole_size - base_size;
750 if (0 == nested_size)
751 return NULL;
752 if (nested_size < sizeof(struct GNUNET_MessageHeader))
753 {
754 GNUNET_break_op (0);
755 return NULL;
756 }
757 nested_msg = (const struct GNUNET_MessageHeader *) ((char *) mh + base_size);
758 if (ntohs (nested_msg->size) != nested_size)
759 {
760 GNUNET_break_op (0);
761 return NULL;
762 }
763 return nested_msg;
764}
765
766
767void
768GNUNET_MQ_send_cancel (struct GNUNET_MQ_Envelope *ev)
769{
770 struct GNUNET_MQ_Handle *mq = ev->parent_queue;
771
772 GNUNET_assert (NULL != mq);
773 GNUNET_assert (NULL != mq->cancel_impl);
774 GNUNET_assert (0 < mq->queue_length);
775 mq->queue_length--;
776 if (mq->current_envelope == ev)
777 {
778 /* complex case, we already started with transmitting
779 the message using the callbacks. */
780 GNUNET_assert (! mq->in_flight);
781 mq->cancel_impl (mq,
782 mq->impl_state);
783 /* continue sending the next message, if any */
784 mq->current_envelope = mq->envelope_head;
785 if (NULL != mq->current_envelope)
786 {
787 GNUNET_CONTAINER_DLL_remove (mq->envelope_head,
788 mq->envelope_tail,
789 mq->current_envelope);
790 LOG (GNUNET_ERROR_TYPE_DEBUG,
791 "sending canceled message of type %u queue\n",
792 ntohs (ev->mh->type));
793 mq->send_impl (mq,
794 mq->current_envelope->mh,
795 mq->impl_state);
796 }
797 }
798 else
799 {
800 /* simple case, message is still waiting in the queue */
801 GNUNET_CONTAINER_DLL_remove (mq->envelope_head,
802 mq->envelope_tail,
803 ev);
804 }
805 ev->parent_queue = NULL;
806 ev->mh = NULL;
807 /* also frees ev */
808 GNUNET_free (ev);
809}
810
811
812struct GNUNET_MQ_Envelope *
813GNUNET_MQ_get_current_envelope (struct GNUNET_MQ_Handle *mq)
814{
815 return mq->current_envelope;
816}
817
818
819struct GNUNET_MQ_Envelope *
820GNUNET_MQ_get_last_envelope (struct GNUNET_MQ_Handle *mq)
821{
822 if (NULL != mq->envelope_tail)
823 return mq->envelope_tail;
824
825 return mq->current_envelope;
826}
827
828
829void
830GNUNET_MQ_env_set_options (struct GNUNET_MQ_Envelope *env,
831 enum GNUNET_MQ_PriorityPreferences pp)
832{
833 env->priority = pp;
834 env->have_custom_options = GNUNET_YES;
835}
836
837
838enum GNUNET_MQ_PriorityPreferences
839GNUNET_MQ_env_get_options (struct GNUNET_MQ_Envelope *env)
840{
841 struct GNUNET_MQ_Handle *mq = env->parent_queue;
842
843 if (GNUNET_YES == env->have_custom_options)
844 return env->priority;
845 if (NULL == mq)
846 return 0;
847 return mq->priority;
848}
849
850
851enum GNUNET_MQ_PriorityPreferences
852GNUNET_MQ_env_combine_options (enum GNUNET_MQ_PriorityPreferences p1,
853 enum GNUNET_MQ_PriorityPreferences p2)
854{
855 enum GNUNET_MQ_PriorityPreferences ret;
856
857 ret = GNUNET_MAX (p1 & GNUNET_MQ_PRIORITY_MASK, p2 & GNUNET_MQ_PRIORITY_MASK);
858 ret |= ((p1 & GNUNET_MQ_PREF_UNRELIABLE) & (p2 & GNUNET_MQ_PREF_UNRELIABLE));
859 ret |=
860 ((p1 & GNUNET_MQ_PREF_LOW_LATENCY) | (p2 & GNUNET_MQ_PREF_LOW_LATENCY));
861 ret |=
862 ((p1 & GNUNET_MQ_PREF_CORK_ALLOWED) & (p2 & GNUNET_MQ_PREF_CORK_ALLOWED));
863 ret |= ((p1 & GNUNET_MQ_PREF_GOODPUT) & (p2 & GNUNET_MQ_PREF_GOODPUT));
864 ret |=
865 ((p1 & GNUNET_MQ_PREF_OUT_OF_ORDER) & (p2 & GNUNET_MQ_PREF_OUT_OF_ORDER));
866 return ret;
867}
868
869
870void
871GNUNET_MQ_set_options (struct GNUNET_MQ_Handle *mq,
872 enum GNUNET_MQ_PriorityPreferences pp)
873{
874 mq->priority = pp;
875}
876
877
878const struct GNUNET_MessageHeader *
879GNUNET_MQ_env_get_msg (const struct GNUNET_MQ_Envelope *env)
880{
881 return env->mh;
882}
883
884
885const struct GNUNET_MQ_Envelope *
886GNUNET_MQ_env_next (const struct GNUNET_MQ_Envelope *env)
887{
888 return env->next;
889}
890
891
892struct GNUNET_MQ_DestroyNotificationHandle *
893GNUNET_MQ_destroy_notify (struct GNUNET_MQ_Handle *mq,
894 GNUNET_SCHEDULER_TaskCallback cb,
895 void *cb_cls)
896{
897 struct GNUNET_MQ_DestroyNotificationHandle *dnh;
898
899 dnh = GNUNET_new (struct GNUNET_MQ_DestroyNotificationHandle);
900 dnh->mq = mq;
901 dnh->cb = cb;
902 dnh->cb_cls = cb_cls;
903 GNUNET_CONTAINER_DLL_insert (mq->dnh_head,
904 mq->dnh_tail,
905 dnh);
906 return dnh;
907}
908
909
910void
911GNUNET_MQ_destroy_notify_cancel (
912 struct GNUNET_MQ_DestroyNotificationHandle *dnh)
913{
914 struct GNUNET_MQ_Handle *mq = dnh->mq;
915
916 GNUNET_CONTAINER_DLL_remove (mq->dnh_head,
917 mq->dnh_tail,
918 dnh);
919 GNUNET_free (dnh);
920}
921
922
923void
924GNUNET_MQ_dll_insert_head (struct GNUNET_MQ_Envelope **env_head,
925 struct GNUNET_MQ_Envelope **env_tail,
926 struct GNUNET_MQ_Envelope *env)
927{
928 GNUNET_CONTAINER_DLL_insert (*env_head,
929 *env_tail,
930 env);
931}
932
933
934void
935GNUNET_MQ_dll_insert_tail (struct GNUNET_MQ_Envelope **env_head,
936 struct GNUNET_MQ_Envelope **env_tail,
937 struct GNUNET_MQ_Envelope *env)
938{
939 GNUNET_CONTAINER_DLL_insert_tail (*env_head,
940 *env_tail,
941 env);
942}
943
944
945void
946GNUNET_MQ_dll_remove (struct GNUNET_MQ_Envelope **env_head,
947 struct GNUNET_MQ_Envelope **env_tail,
948 struct GNUNET_MQ_Envelope *env)
949{
950 GNUNET_CONTAINER_DLL_remove (*env_head,
951 *env_tail,
952 env);
953}
954
955
956struct GNUNET_MQ_MessageHandler *
957GNUNET_MQ_copy_handlers (const struct GNUNET_MQ_MessageHandler *handlers)
958{
959 struct GNUNET_MQ_MessageHandler *copy;
960 unsigned int count;
961
962 if (NULL == handlers)
963 return NULL;
964 count = GNUNET_MQ_count_handlers (handlers);
965 copy = GNUNET_new_array (count + 1,
966 struct GNUNET_MQ_MessageHandler);
967 GNUNET_memcpy (copy,
968 handlers,
969 count * sizeof(struct GNUNET_MQ_MessageHandler));
970 return copy;
971}
972
973
974struct GNUNET_MQ_MessageHandler *
975GNUNET_MQ_copy_handlers2 (const struct GNUNET_MQ_MessageHandler *handlers,
976 GNUNET_MQ_MessageCallback agpl_handler,
977 void *agpl_cls)
978{
979 struct GNUNET_MQ_MessageHandler *copy;
980 unsigned int count;
981
982 if (NULL == handlers)
983 return NULL;
984 count = GNUNET_MQ_count_handlers (handlers);
985 copy = GNUNET_new_array (count + 2,
986 struct GNUNET_MQ_MessageHandler);
987 GNUNET_memcpy (copy,
988 handlers,
989 count * sizeof(struct GNUNET_MQ_MessageHandler));
990 copy[count].mv = NULL;
991 copy[count].cb = agpl_handler;
992 copy[count].cls = agpl_cls;
993 copy[count].type = GNUNET_MESSAGE_TYPE_REQUEST_AGPL;
994 copy[count].expected_size = sizeof(struct GNUNET_MessageHeader);
995 return copy;
996}
997
998
999unsigned int
1000GNUNET_MQ_count_handlers (const struct GNUNET_MQ_MessageHandler *handlers)
1001{
1002 unsigned int i;
1003
1004 if (NULL == handlers)
1005 return 0;
1006 for (i = 0; NULL != handlers[i].cb; i++)
1007 ;
1008 return i;
1009}
1010
1011
1012const char *
1013GNUNET_MQ_preference_to_string (enum GNUNET_MQ_PreferenceKind type)
1014{
1015 switch (type)
1016 {
1017 case GNUNET_MQ_PREFERENCE_NONE:
1018 return "NONE";
1019 case GNUNET_MQ_PREFERENCE_BANDWIDTH:
1020 return "BANDWIDTH";
1021 case GNUNET_MQ_PREFERENCE_LATENCY:
1022 return "LATENCY";
1023 case GNUNET_MQ_PREFERENCE_RELIABILITY:
1024 return "RELIABILITY";
1025 }
1026 return NULL;
1027}
1028
1029
1030/* end of mq.c */
diff --git a/src/lib/util/mst.c b/src/lib/util/mst.c
new file mode 100644
index 000000000..d8509b7ec
--- /dev/null
+++ b/src/lib/util/mst.c
@@ -0,0 +1,411 @@
1/*
2 This file is part of GNUnet.
3 Copyright (C) 2010, 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 util/mst.c
23 * @brief convenience functions for handling inbound message buffers
24 * @author Christian Grothoff
25 */
26
27
28#include "platform.h"
29#include "gnunet_util_lib.h"
30
31
32#if HAVE_UNALIGNED_64_ACCESS
33#define ALIGN_FACTOR 4
34#else
35#define ALIGN_FACTOR 8
36#endif
37
38#define LOG(kind, ...) GNUNET_log_from (kind, "util-mst", __VA_ARGS__)
39
40
41/**
42 * Handle to a message stream tokenizer.
43 */
44struct GNUNET_MessageStreamTokenizer
45{
46 /**
47 * Function to call on completed messages.
48 */
49 GNUNET_MessageTokenizerCallback cb;
50
51 /**
52 * Closure for @e cb.
53 */
54 void *cb_cls;
55
56 /**
57 * Size of the buffer (starting at @e hdr).
58 */
59 size_t curr_buf;
60
61 /**
62 * How many bytes in buffer have we already processed?
63 */
64 size_t off;
65
66 /**
67 * How many bytes in buffer are valid right now?
68 */
69 size_t pos;
70
71 /**
72 * Beginning of the buffer. Typed like this to force alignment.
73 */
74 struct GNUNET_MessageHeader *hdr;
75};
76
77
78/**
79 * Create a message stream tokenizer.
80 *
81 * @param cb function to call on completed messages
82 * @param cb_cls closure for @a cb
83 * @return handle to tokenizer
84 */
85struct GNUNET_MessageStreamTokenizer *
86GNUNET_MST_create (GNUNET_MessageTokenizerCallback cb,
87 void *cb_cls)
88{
89 struct GNUNET_MessageStreamTokenizer *ret;
90
91 ret = GNUNET_new (struct GNUNET_MessageStreamTokenizer);
92 ret->hdr = GNUNET_malloc (GNUNET_MIN_MESSAGE_SIZE);
93 ret->curr_buf = GNUNET_MIN_MESSAGE_SIZE;
94 ret->cb = cb;
95 ret->cb_cls = cb_cls;
96 return ret;
97}
98
99
100enum GNUNET_GenericReturnValue
101GNUNET_MST_from_buffer (struct GNUNET_MessageStreamTokenizer *mst,
102 const char *buf,
103 size_t size,
104 int purge,
105 int one_shot)
106{
107 const struct GNUNET_MessageHeader *hdr;
108 size_t delta;
109 uint16_t want;
110 char *ibuf;
111 int ret;
112 int cbret;
113
114 GNUNET_assert (mst->off <= mst->pos);
115 GNUNET_assert (mst->pos <= mst->curr_buf);
116 LOG (GNUNET_ERROR_TYPE_DEBUG,
117 "MST receives %u bytes with %u (%u/%u) bytes already in private buffer\n",
118 (unsigned int) size,
119 (unsigned int) (mst->pos - mst->off),
120 (unsigned int) mst->pos,
121 (unsigned int) mst->off);
122 ret = GNUNET_OK;
123 ibuf = (char *) mst->hdr;
124 while (mst->pos > 0)
125 {
126do_align:
127 GNUNET_assert (mst->pos >= mst->off);
128 if ((mst->curr_buf - mst->off < sizeof(struct GNUNET_MessageHeader)) ||
129 (0 != (mst->off % ALIGN_FACTOR)))
130 {
131 /* need to align or need more space */
132 mst->pos -= mst->off;
133 memmove (ibuf,
134 &ibuf[mst->off],
135 mst->pos);
136 mst->off = 0;
137 }
138 if (mst->pos - mst->off < sizeof(struct GNUNET_MessageHeader))
139 {
140 delta
141 = GNUNET_MIN (sizeof(struct GNUNET_MessageHeader)
142 - (mst->pos - mst->off),
143 size);
144 GNUNET_memcpy (&ibuf[mst->pos],
145 buf,
146 delta);
147 mst->pos += delta;
148 buf += delta;
149 size -= delta;
150 }
151 if (mst->pos - mst->off < sizeof(struct GNUNET_MessageHeader))
152 {
153 if (purge)
154 {
155 mst->off = 0;
156 mst->pos = 0;
157 }
158 return GNUNET_OK;
159 }
160 hdr = (const struct GNUNET_MessageHeader *) &ibuf[mst->off];
161 want = ntohs (hdr->size);
162 LOG (GNUNET_ERROR_TYPE_DEBUG,
163 "We want to read message of size %u\n",
164 want);
165 if (want < sizeof(struct GNUNET_MessageHeader))
166 {
167 GNUNET_break_op (0);
168 return GNUNET_SYSERR;
169 }
170 if ((mst->curr_buf - mst->off < want) &&
171 (mst->off > 0))
172 {
173 /* can get more space by moving */
174 mst->pos -= mst->off;
175 memmove (ibuf,
176 &ibuf[mst->off],
177 mst->pos);
178 mst->off = 0;
179 }
180 if (mst->curr_buf < want)
181 {
182 /* need to get more space by growing buffer */
183 GNUNET_assert (0 == mst->off);
184 mst->hdr = GNUNET_realloc (mst->hdr,
185 want);
186 ibuf = (char *) mst->hdr;
187 mst->curr_buf = want;
188 }
189 hdr = (const struct GNUNET_MessageHeader *) &ibuf[mst->off];
190 if (mst->pos - mst->off < want)
191 {
192 delta = GNUNET_MIN (want - (mst->pos - mst->off),
193 size);
194 GNUNET_assert (mst->pos + delta <= mst->curr_buf);
195 GNUNET_memcpy (&ibuf[mst->pos],
196 buf,
197 delta);
198 mst->pos += delta;
199 buf += delta;
200 size -= delta;
201 }
202 if (mst->pos - mst->off < want)
203 {
204 if (purge)
205 {
206 mst->off = 0;
207 mst->pos = 0;
208 }
209 return GNUNET_OK;
210 }
211 if (one_shot == GNUNET_SYSERR)
212 {
213 /* cannot call callback again, but return value saying that
214 * we have another full message in the buffer */
215 ret = GNUNET_NO;
216 goto copy;
217 }
218 if (one_shot == GNUNET_YES)
219 one_shot = GNUNET_SYSERR;
220 mst->off += want;
221 if (GNUNET_OK !=
222 (cbret = mst->cb (mst->cb_cls,
223 hdr)))
224 {
225 if (GNUNET_SYSERR == cbret)
226 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
227 "Failure processing message of type %u and size %u\n",
228 ntohs (hdr->type),
229 ntohs (hdr->size));
230 return GNUNET_SYSERR;
231 }
232 if (mst->off == mst->pos)
233 {
234 /* reset to beginning of buffer, it's free right now! */
235 mst->off = 0;
236 mst->pos = 0;
237 }
238 }
239 GNUNET_assert (0 == mst->pos);
240 while (size > 0)
241 {
242 unsigned long offset = (unsigned long) buf;
243 bool need_align = (0 != (offset % ALIGN_FACTOR));
244
245 LOG (GNUNET_ERROR_TYPE_DEBUG,
246 "Server-mst has %u bytes left in inbound buffer\n",
247 (unsigned int) size);
248 if (size < sizeof(struct GNUNET_MessageHeader))
249 break;
250 if (! need_align)
251 {
252 /* can try to do zero-copy and process directly from original buffer */
253 hdr = (const struct GNUNET_MessageHeader *) buf;
254 want = ntohs (hdr->size);
255 if (want < sizeof(struct GNUNET_MessageHeader))
256 {
257 GNUNET_break_op (0);
258 mst->off = 0;
259 return GNUNET_SYSERR;
260 }
261 if (size < want)
262 break; /* or not: buffer incomplete, so copy to private buffer... */
263 if (one_shot == GNUNET_SYSERR)
264 {
265 /* cannot call callback again, but return value saying that
266 * we have another full message in the buffer */
267 ret = GNUNET_NO;
268 goto copy;
269 }
270 if (GNUNET_YES == one_shot)
271 one_shot = GNUNET_SYSERR;
272 if (GNUNET_OK !=
273 (cbret = mst->cb (mst->cb_cls,
274 hdr)))
275 {
276 if (GNUNET_SYSERR == cbret)
277 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
278 "Failure processing message of type %u and size %u\n",
279 ntohs (hdr->type),
280 ntohs (hdr->size));
281 return GNUNET_SYSERR;
282 }
283 buf += want;
284 size -= want;
285 }
286 else
287 {
288 /* need to copy to private buffer to align;
289 * yes, we go a bit more spaghetti than usual here */
290 goto do_align;
291 }
292 }
293copy:
294 if ((size > 0) && (! purge))
295 {
296 if (size + mst->pos > mst->curr_buf)
297 {
298 mst->hdr = GNUNET_realloc (mst->hdr,
299 size + mst->pos);
300 ibuf = (char *) mst->hdr;
301 mst->curr_buf = size + mst->pos;
302 }
303 GNUNET_assert (size + mst->pos <= mst->curr_buf);
304 GNUNET_memcpy (&ibuf[mst->pos],
305 buf,
306 size);
307 mst->pos += size;
308 }
309 if (purge)
310 {
311 mst->off = 0;
312 mst->pos = 0;
313 }
314 LOG (GNUNET_ERROR_TYPE_DEBUG,
315 "Server-mst leaves %u (%u/%u) bytes in private buffer\n",
316 (unsigned int) (mst->pos - mst->off),
317 (unsigned int) mst->pos,
318 (unsigned int) mst->off);
319 return ret;
320}
321
322
323/**
324 * Add incoming data to the receive buffer and call the
325 * callback for all complete messages.
326 *
327 * @param mst tokenizer to use
328 * @param buf input data to add
329 * @param size number of bytes in @a buf
330 * @param purge should any excess bytes in the buffer be discarded
331 * (i.e. for packet-based services like UDP)
332 * @param one_shot only call callback once, keep rest of message in buffer
333 * @return #GNUNET_OK if we are done processing (need more data)
334 * #GNUNET_NO if one_shot was set and we have another message ready
335 * #GNUNET_SYSERR if the data stream is corrupt
336 */
337enum GNUNET_GenericReturnValue
338GNUNET_MST_read (struct GNUNET_MessageStreamTokenizer *mst,
339 struct GNUNET_NETWORK_Handle *sock,
340 int purge,
341 int one_shot)
342{
343 ssize_t ret;
344 size_t left;
345 char *buf;
346
347 left = mst->curr_buf - mst->pos;
348 buf = (char *) mst->hdr;
349 ret = GNUNET_NETWORK_socket_recv (sock,
350 &buf[mst->pos],
351 left);
352 if (-1 == ret)
353 {
354 if ((EAGAIN == errno) ||
355 (EINTR == errno))
356 return GNUNET_OK;
357 GNUNET_log_strerror (GNUNET_ERROR_TYPE_INFO,
358 "recv");
359 return GNUNET_SYSERR;
360 }
361 if (0 == ret)
362 {
363 /* other side closed connection, treat as error */
364 return GNUNET_SYSERR;
365 }
366 mst->pos += ret;
367 return GNUNET_MST_from_buffer (mst,
368 NULL,
369 0,
370 purge,
371 one_shot);
372}
373
374
375/**
376 * Obtain the next message from the @a mst, assuming that
377 * there are more unprocessed messages in the internal buffer
378 * of the @a mst.
379 *
380 * @param mst tokenizer to use
381 * @param one_shot only call callback once, keep rest of message in buffer
382 * @return #GNUNET_OK if we are done processing (need more data)
383 * #GNUNET_NO if one_shot was set and we have another message ready
384 * #GNUNET_SYSERR if the data stream is corrupt
385 */
386enum GNUNET_GenericReturnValue
387GNUNET_MST_next (struct GNUNET_MessageStreamTokenizer *mst,
388 int one_shot)
389{
390 return GNUNET_MST_from_buffer (mst,
391 NULL,
392 0,
393 GNUNET_NO,
394 one_shot);
395}
396
397
398/**
399 * Destroys a tokenizer.
400 *
401 * @param mst tokenizer to destroy
402 */
403void
404GNUNET_MST_destroy (struct GNUNET_MessageStreamTokenizer *mst)
405{
406 GNUNET_free (mst->hdr);
407 GNUNET_free (mst);
408}
409
410
411/* end of server_mst.c */
diff --git a/src/lib/util/nc.c b/src/lib/util/nc.c
new file mode 100644
index 000000000..2a612917c
--- /dev/null
+++ b/src/lib/util/nc.c
@@ -0,0 +1,228 @@
1/*
2 This file is part of GNUnet.
3 Copyright (C) 2010, 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 util/nc.c
23 * @brief convenience functions for transmission of
24 * messages to multiple clients
25 * @author Christian Grothoff
26 */
27
28
29#include "platform.h"
30#include "gnunet_util_lib.h"
31
32#define LOG(kind, ...) GNUNET_log_from (kind, "util-nc", __VA_ARGS__)
33
34
35/**
36 * Lists of subscribers we manage for notifications.
37 */
38struct SubscriberList
39{
40 /**
41 * This is a doubly linked list.
42 */
43 struct SubscriberList *next;
44
45 /**
46 * This is a doubly linked list.
47 */
48 struct SubscriberList *prev;
49
50 /**
51 * Overall context this subscriber belongs to.
52 */
53 struct GNUNET_NotificationContext *nc;
54
55 /**
56 * Handle where we registered with @e mq to be told about
57 * the MQ's destruction.
58 */
59 struct GNUNET_MQ_DestroyNotificationHandle *mq_nh;
60
61 /**
62 * Message queue for the subscriber.
63 */
64 struct GNUNET_MQ_Handle *mq;
65};
66
67
68/**
69 * The notification context is the key datastructure for a convenience
70 * API used for transmission of notifications to the subscriber until the
71 * subscriber disconnects (or the notification context is destroyed, in
72 * which case we disconnect these subscribers). Essentially, all
73 * (notification) messages are queued up until the subscriber is able to
74 * read them.
75 */
76struct GNUNET_NotificationContext
77{
78 /**
79 * Head of list of subscribers receiving notifications.
80 */
81 struct SubscriberList *subscribers_head;
82
83 /**
84 * Tail of list of subscribers receiving notifications.
85 */
86 struct SubscriberList *subscribers_tail;
87
88 /**
89 * Maximum number of optional messages to queue per subscriber.
90 */
91 unsigned int queue_length;
92};
93
94
95/**
96 * Subscriber has disconnected, clean up.
97 *
98 * @param cls our `struct SubscriberList *`
99 */
100static void
101handle_mq_destroy (void *cls)
102{
103 struct SubscriberList *pos = cls;
104 struct GNUNET_NotificationContext *nc = pos->nc;
105
106 GNUNET_CONTAINER_DLL_remove (nc->subscribers_head,
107 nc->subscribers_tail,
108 pos);
109 GNUNET_free (pos);
110}
111
112
113/**
114 * Create a new notification context.
115 *
116 * @param queue_length maximum number of messages to keep in
117 * the notification queue; optional messages are dropped
118 * if the queue gets longer than this number of messages
119 * @return handle to the notification context
120 */
121struct GNUNET_NotificationContext *
122GNUNET_notification_context_create (unsigned int queue_length)
123{
124 struct GNUNET_NotificationContext *nc;
125
126 nc = GNUNET_new (struct GNUNET_NotificationContext);
127 nc->queue_length = queue_length;
128 return nc;
129}
130
131
132/**
133 * Destroy the context, force disconnect for all subscribers.
134 *
135 * @param nc context to destroy.
136 */
137void
138GNUNET_notification_context_destroy (struct GNUNET_NotificationContext *nc)
139{
140 struct SubscriberList *pos;
141
142 while (NULL != (pos = nc->subscribers_head))
143 {
144 GNUNET_CONTAINER_DLL_remove (nc->subscribers_head,
145 nc->subscribers_tail,
146 pos);
147 GNUNET_MQ_destroy_notify_cancel (pos->mq_nh);
148 GNUNET_free (pos);
149 }
150 GNUNET_free (nc);
151}
152
153
154/**
155 * Add a subscriber to the notification context.
156 *
157 * @param nc context to modify
158 * @param mq message queue add
159 */
160void
161GNUNET_notification_context_add (struct GNUNET_NotificationContext *nc,
162 struct GNUNET_MQ_Handle *mq)
163{
164 struct SubscriberList *cl;
165
166 for (cl = nc->subscribers_head; NULL != cl; cl = cl->next)
167 if (cl->mq == mq)
168 return;
169 /* already present */
170 cl = GNUNET_new (struct SubscriberList);
171 GNUNET_CONTAINER_DLL_insert (nc->subscribers_head,
172 nc->subscribers_tail,
173 cl);
174 cl->nc = nc;
175 cl->mq = mq;
176 cl->mq_nh = GNUNET_MQ_destroy_notify (cl->mq,
177 &handle_mq_destroy,
178 cl);
179}
180
181
182/**
183 * Send a message to all subscribers of this context.
184 *
185 * @param nc context to modify
186 * @param msg message to send
187 * @param can_drop can this message be dropped due to queue length limitations
188 */
189void
190GNUNET_notification_context_broadcast (struct GNUNET_NotificationContext *nc,
191 const struct GNUNET_MessageHeader *msg,
192 int can_drop)
193{
194 struct SubscriberList *pos;
195 struct GNUNET_MQ_Envelope *env;
196
197 for (pos = nc->subscribers_head; NULL != pos; pos = pos->next)
198 {
199 if ((GNUNET_YES == can_drop) &&
200 (GNUNET_MQ_get_length (pos->mq) > nc->queue_length))
201 continue;
202 env = GNUNET_MQ_msg_copy (msg);
203 GNUNET_MQ_send (pos->mq,
204 env);
205 }
206}
207
208
209/**
210 * Return active number of subscribers in this context.
211 *
212 * @param nc context to query
213 * @return number of current subscribers
214 */
215unsigned int
216GNUNET_notification_context_get_size (struct GNUNET_NotificationContext *nc)
217{
218 unsigned int num;
219 struct SubscriberList *pos;
220
221 num = 0;
222 for (pos = nc->subscribers_head; NULL != pos; pos = pos->next)
223 num++;
224 return num;
225}
226
227
228/* end of nc.c */
diff --git a/src/lib/util/network.c b/src/lib/util/network.c
new file mode 100644
index 000000000..8c74c5626
--- /dev/null
+++ b/src/lib/util/network.c
@@ -0,0 +1,1311 @@
1/*
2 This file is part of GNUnet.
3 Copyright (C) 2009-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/network.c
23 * @brief basic, low-level networking interface
24 * @author Nils Durner
25 * @author Christian Grothoff
26 */
27#include "platform.h"
28#include "gnunet_common.h"
29#include "disk.h"
30
31#define LOG(kind, ...) GNUNET_log_from (kind, "util-network", __VA_ARGS__)
32#define LOG_STRERROR_FILE(kind, syscall, \
33 filename) GNUNET_log_from_strerror_file (kind, \
34 "util-network", \
35 syscall, \
36 filename)
37#define LOG_STRERROR(kind, syscall) GNUNET_log_from_strerror (kind, \
38 "util-network", \
39 syscall)
40
41#define DEBUG_NETWORK GNUNET_EXTRA_LOGGING
42
43
44#ifndef INVALID_SOCKET
45#define INVALID_SOCKET -1
46#endif
47
48
49/**
50 * @brief handle to a socket
51 */
52struct GNUNET_NETWORK_Handle
53{
54 int fd;
55
56 /**
57 * Address family / domain.
58 */
59 int af;
60
61 /**
62 * Type of the socket
63 */
64 int type;
65
66 /**
67 * Number of bytes in addr.
68 */
69 socklen_t addrlen;
70
71 /**
72 * Address we were bound to, or NULL.
73 */
74 struct sockaddr *addr;
75};
76
77
78enum GNUNET_GenericReturnValue
79GNUNET_NETWORK_test_pf (int pf)
80{
81 static int cache_v4 = -1;
82 static int cache_v6 = -1;
83 static int cache_un = -1;
84 int s;
85 int ret;
86
87 switch (pf)
88 {
89 case PF_INET:
90 if (-1 != cache_v4)
91 return cache_v4;
92 break;
93
94 case PF_INET6:
95 if (-1 != cache_v6)
96 return cache_v6;
97 break;
98
99#ifdef PF_UNIX
100 case PF_UNIX:
101 if (-1 != cache_un)
102 return cache_un;
103 break;
104#endif
105 }
106 s = socket (pf, SOCK_STREAM, 0);
107 if (-1 == s)
108 {
109 if (EAFNOSUPPORT != errno)
110 {
111 GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING,
112 "socket");
113 return GNUNET_SYSERR;
114 }
115 ret = GNUNET_NO;
116 }
117 else
118 {
119 GNUNET_break (0 == close (s));
120 ret = GNUNET_OK;
121 }
122 switch (pf)
123 {
124 case PF_INET:
125 cache_v4 = ret;
126 break;
127
128 case PF_INET6:
129 cache_v6 = ret;
130 break;
131
132#ifdef PF_UNIX
133 case PF_UNIX:
134 cache_un = ret;
135 break;
136#endif
137 }
138 return ret;
139}
140
141
142char *
143GNUNET_NETWORK_shorten_unixpath (char *unixpath)
144{
145 struct sockaddr_un dummy;
146 size_t slen;
147 char *end;
148 struct GNUNET_HashCode sh;
149 struct GNUNET_CRYPTO_HashAsciiEncoded ae;
150 size_t upm;
151
152 upm = sizeof(dummy.sun_path);
153 slen = strlen (unixpath);
154 if (slen < upm)
155 return unixpath; /* no shortening required */
156 GNUNET_CRYPTO_hash (unixpath, slen, &sh);
157 while (16 + strlen (unixpath) >= upm)
158 {
159 if (NULL == (end = strrchr (unixpath, '/')))
160 {
161 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
162 _ (
163 "Unable to shorten unix path `%s' while keeping name unique\n"),
164 unixpath);
165 GNUNET_free (unixpath);
166 return NULL;
167 }
168 *end = '\0';
169 }
170 GNUNET_CRYPTO_hash_to_enc (&sh, &ae);
171 ae.encoding[16] = '\0';
172 strcat (unixpath, (char *) ae.encoding);
173 return unixpath;
174}
175
176
177void
178GNUNET_NETWORK_unix_precheck (const struct sockaddr_un *un)
179{
180 int s;
181 int eno;
182 struct stat sbuf;
183 int ret;
184
185 s = socket (AF_UNIX, SOCK_STREAM, 0);
186 if (-1 == s)
187 {
188 GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING,
189 "Failed to open AF_UNIX socket");
190 return;
191 }
192 ret = connect (s,
193 (struct sockaddr *) un,
194 sizeof(struct sockaddr_un));
195 eno = errno;
196 GNUNET_break (0 == close (s));
197 if (0 == ret)
198 return; /* another process is listening, do not remove! */
199 if (ECONNREFUSED != eno)
200 return; /* some other error, likely "no such file or directory" -- all well */
201 /* should unlink, but sanity checks first */
202 if (0 != stat (un->sun_path,
203 &sbuf))
204 return; /* failed to 'stat', likely does not exist after all */
205 if (S_IFSOCK != (S_IFMT & sbuf.st_mode))
206 return; /* refuse to unlink anything except sockets */
207 /* finally, really unlink */
208 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
209 "Removing left-over `%s' from previous exeuction\n",
210 un->sun_path);
211 if (0 != unlink (un->sun_path))
212 GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING,
213 "unlink",
214 un->sun_path);
215}
216
217
218#ifndef FD_COPY
219#define FD_COPY(s, d) do { GNUNET_memcpy ((d), (s), sizeof(fd_set)); } while (0)
220#endif
221
222
223enum GNUNET_GenericReturnValue
224GNUNET_NETWORK_socket_set_blocking (struct GNUNET_NETWORK_Handle *fd,
225 int doBlock)
226{
227 int flags = fcntl (fd->fd, F_GETFL);
228
229 if (flags == -1)
230 {
231 LOG_STRERROR (GNUNET_ERROR_TYPE_WARNING,
232 "fcntl");
233 return GNUNET_SYSERR;
234 }
235 if (doBlock)
236 flags &= ~O_NONBLOCK;
237
238 else
239 flags |= O_NONBLOCK;
240 if (0 != fcntl (fd->fd,
241 F_SETFL,
242 flags))
243
244 {
245 LOG_STRERROR (GNUNET_ERROR_TYPE_WARNING,
246 "fcntl");
247 return GNUNET_SYSERR;
248 }
249 return GNUNET_OK;
250}
251
252
253/**
254 * Make a socket non-inheritable to child processes
255 *
256 * @param h the socket to make non-inheritable
257 * @return #GNUNET_OK on success, #GNUNET_SYSERR otherwise
258 * @warning Not implemented on Windows
259 */
260static int
261socket_set_inheritable (const struct GNUNET_NETWORK_Handle *h)
262{
263 int i;
264 i = fcntl (h->fd, F_GETFD);
265 if (i < 0)
266 return GNUNET_SYSERR;
267 if (i == (i | FD_CLOEXEC))
268 return GNUNET_OK;
269 i |= FD_CLOEXEC;
270 if (fcntl (h->fd, F_SETFD, i) < 0)
271 return GNUNET_SYSERR;
272
273 return GNUNET_OK;
274}
275
276
277#ifdef DARWIN
278/**
279 * The MSG_NOSIGNAL equivalent on Mac OS X
280 *
281 * @param h the socket to make non-delaying
282 */
283static int
284socket_set_nosigpipe (const struct GNUNET_NETWORK_Handle *h)
285{
286 int abs_value = 1;
287
288 if (0 !=
289 setsockopt (h->fd, SOL_SOCKET, SO_NOSIGPIPE,
290 (const void *) &abs_value,
291 sizeof(abs_value)))
292 return GNUNET_SYSERR;
293 return GNUNET_OK;
294}
295
296
297#endif
298
299
300/**
301 * Disable delays when sending data via the socket.
302 * (GNUnet makes sure that messages are as big as
303 * possible already).
304 *
305 * @param h the socket to make non-delaying
306 */
307static void
308socket_set_nodelay (const struct GNUNET_NETWORK_Handle *h)
309{
310 int value = 1;
311
312 if (0 !=
313 setsockopt (h->fd,
314 IPPROTO_TCP,
315 TCP_NODELAY,
316 &value, sizeof(value)))
317 LOG_STRERROR (GNUNET_ERROR_TYPE_WARNING,
318 "setsockopt");
319}
320
321
322/**
323 * Perform proper canonical initialization for a network handle.
324 * Set it to non-blocking, make it non-inheritable to child
325 * processes, disable SIGPIPE, enable "nodelay" (if non-UNIX
326 * stream socket) and check that it is smaller than FD_SETSIZE.
327 *
328 * @param h socket to initialize
329 * @param af address family of the socket
330 * @param type socket type
331 * @return #GNUNET_OK on success, #GNUNET_SYSERR if initialization
332 * failed and the handle was destroyed
333 */
334static int
335initialize_network_handle (struct GNUNET_NETWORK_Handle *h,
336 int af,
337 int type)
338{
339 int eno;
340
341 h->af = af;
342 h->type = type;
343 if (h->fd == INVALID_SOCKET)
344 {
345 eno = errno;
346 GNUNET_free (h);
347 errno = eno;
348 return GNUNET_SYSERR;
349 }
350
351 if (h->fd >= FD_SETSIZE)
352 {
353 GNUNET_break (GNUNET_OK ==
354 GNUNET_NETWORK_socket_close (h));
355 errno = EMFILE;
356 return GNUNET_SYSERR;
357 }
358
359 if (GNUNET_OK != socket_set_inheritable (h))
360 LOG_STRERROR (GNUNET_ERROR_TYPE_ERROR | GNUNET_ERROR_TYPE_BULK,
361 "socket_set_inheritable");
362
363 if (GNUNET_SYSERR == GNUNET_NETWORK_socket_set_blocking (h, GNUNET_NO))
364 {
365 eno = errno;
366 GNUNET_break (0);
367 GNUNET_break (GNUNET_OK == GNUNET_NETWORK_socket_close (h));
368 errno = eno;
369 return GNUNET_SYSERR;
370 }
371#ifdef DARWIN
372 if (GNUNET_SYSERR == socket_set_nosigpipe (h))
373 {
374 eno = errno;
375 GNUNET_break (0);
376 GNUNET_break (GNUNET_OK == GNUNET_NETWORK_socket_close (h));
377 errno = eno;
378 return GNUNET_SYSERR;
379 }
380#endif
381 if ((type == SOCK_STREAM)
382#ifdef AF_UNIX
383 && (af != AF_UNIX)
384#endif
385 )
386 socket_set_nodelay (h);
387 return GNUNET_OK;
388}
389
390
391struct GNUNET_NETWORK_Handle *
392GNUNET_NETWORK_socket_accept (const struct GNUNET_NETWORK_Handle *desc,
393 struct sockaddr *address,
394 socklen_t *address_len)
395{
396 struct GNUNET_NETWORK_Handle *ret;
397 int eno;
398
399 ret = GNUNET_new (struct GNUNET_NETWORK_Handle);
400#if DEBUG_NETWORK
401 {
402 struct sockaddr_storage name;
403 socklen_t namelen = sizeof(name);
404
405 int gsn = getsockname (desc->fd,
406 (struct sockaddr *) &name,
407 &namelen);
408
409 if (0 == gsn)
410 LOG (GNUNET_ERROR_TYPE_DEBUG,
411 "Accepting connection on `%s'\n",
412 GNUNET_a2s ((const struct sockaddr *) &name,
413 namelen));
414 }
415#endif
416 ret->fd = accept (desc->fd,
417 address,
418 address_len);
419 if (-1 == ret->fd)
420 {
421 eno = errno;
422 GNUNET_free (ret);
423 errno = eno;
424 return NULL;
425 }
426 if (GNUNET_OK !=
427 initialize_network_handle (ret,
428 (NULL != address) ? address->sa_family :
429 desc->af,
430 SOCK_STREAM))
431 {
432 return NULL;
433 }
434 return ret;
435}
436
437
438enum GNUNET_GenericReturnValue
439GNUNET_NETWORK_socket_bind (struct GNUNET_NETWORK_Handle *desc,
440 const struct sockaddr *address,
441 socklen_t address_len)
442{
443 int ret;
444
445#ifdef IPV6_V6ONLY
446#ifdef IPPROTO_IPV6
447 {
448 const int on = 1;
449
450 if (AF_INET6 == desc->af)
451 if (setsockopt (desc->fd,
452 IPPROTO_IPV6,
453 IPV6_V6ONLY,
454 (const void *) &on,
455 sizeof(on)))
456 LOG_STRERROR (GNUNET_ERROR_TYPE_DEBUG,
457 "setsockopt");
458 }
459#endif
460#endif
461 if (AF_UNIX == address->sa_family)
462 GNUNET_NETWORK_unix_precheck ((const struct sockaddr_un *) address);
463
464 {
465 const int on = 1;
466
467 if ( (SOCK_STREAM == desc->type) &&
468 (0 != setsockopt (desc->fd,
469 SOL_SOCKET,
470 SO_REUSEADDR,
471 &on, sizeof(on))) )
472 LOG_STRERROR (GNUNET_ERROR_TYPE_DEBUG,
473 "setsockopt");
474 }
475 {
476 /* set permissions of newly created non-abstract UNIX domain socket to
477 "user-only"; applications can choose to relax this later */
478 mode_t old_mask = 0; /* assigned to make compiler happy */
479 const struct sockaddr_un *un = (const struct sockaddr_un *) address;
480 int not_abstract = 0;
481
482 if ((AF_UNIX == address->sa_family)
483 && ('\0' != un->sun_path[0])) /* Not an abstract socket */
484 not_abstract = 1;
485 if (not_abstract)
486 old_mask = umask (S_IWGRP | S_IRGRP | S_IXGRP | S_IWOTH | S_IROTH
487 | S_IXOTH);
488
489 ret = bind (desc->fd,
490 address,
491 address_len);
492
493 if (not_abstract)
494 (void) umask (old_mask);
495 }
496 if (0 != ret)
497 return GNUNET_SYSERR;
498
499 desc->addr = GNUNET_malloc (address_len);
500 GNUNET_memcpy (desc->addr, address, address_len);
501 desc->addrlen = address_len;
502
503 return GNUNET_OK;
504}
505
506
507enum GNUNET_GenericReturnValue
508GNUNET_NETWORK_socket_close (struct GNUNET_NETWORK_Handle *desc)
509{
510 int ret;
511
512 ret = close (desc->fd);
513
514 const struct sockaddr_un *un = (const struct sockaddr_un *) desc->addr;
515
516 /* Cleanup the UNIX domain socket and its parent directories in case of non
517 abstract sockets */
518 if ((AF_UNIX == desc->af) &&
519 (NULL != desc->addr) &&
520 ('\0' != un->sun_path[0]))
521 {
522 char *dirname = GNUNET_strndup (un->sun_path,
523 sizeof(un->sun_path));
524
525 if (0 != unlink (dirname))
526 {
527 LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_WARNING,
528 "unlink",
529 dirname);
530 }
531 else
532 {
533 size_t len;
534
535 len = strlen (dirname);
536 while ((len > 0) && (dirname[len] != DIR_SEPARATOR))
537 len--;
538 dirname[len] = '\0';
539 if ((0 != len) && (0 != rmdir (dirname)))
540 {
541 switch (errno)
542 {
543 case EACCES:
544 case ENOTEMPTY:
545 case EPERM:
546 /* these are normal and can just be ignored */
547 break;
548
549 default:
550 GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING,
551 "rmdir",
552 dirname);
553 break;
554 }
555 }
556 }
557 GNUNET_free (dirname);
558 }
559 GNUNET_NETWORK_socket_free_memory_only_ (desc);
560 return (ret == 0) ? GNUNET_OK : GNUNET_SYSERR;
561}
562
563
564void
565GNUNET_NETWORK_socket_free_memory_only_ (struct GNUNET_NETWORK_Handle *desc)
566{
567 GNUNET_free (desc->addr);
568 GNUNET_free (desc);
569}
570
571
572/**
573 * Box a native socket (and check that it is a socket).
574 *
575 * @param fd socket to box
576 * @return NULL on error (including not supported on target platform)
577 */
578struct GNUNET_NETWORK_Handle *
579GNUNET_NETWORK_socket_box_native (int fd)
580{
581 struct GNUNET_NETWORK_Handle *ret;
582
583 if (fcntl (fd, F_GETFD) < 0)
584 return NULL; /* invalid FD */
585 ret = GNUNET_new (struct GNUNET_NETWORK_Handle);
586 ret->fd = fd;
587 ret->af = AF_UNSPEC;
588 return ret;
589}
590
591
592/**
593 * Connect a socket to some remote address.
594 *
595 * @param desc socket
596 * @param address peer address
597 * @param address_len length of @a address
598 * @return #GNUNET_OK on success, #GNUNET_SYSERR otherwise
599 */
600enum GNUNET_GenericReturnValue
601GNUNET_NETWORK_socket_connect (const struct GNUNET_NETWORK_Handle *desc,
602 const struct sockaddr *address,
603 socklen_t address_len)
604{
605 int ret;
606
607 ret = connect (desc->fd,
608 address,
609 address_len);
610
611 return ret == 0 ? GNUNET_OK : GNUNET_SYSERR;
612}
613
614
615/**
616 * Get socket options
617 *
618 * @param desc socket
619 * @param level protocol level of the option
620 * @param optname identifier of the option
621 * @param optval options
622 * @param optlen length of @a optval
623 * @return #GNUNET_OK on success, #GNUNET_SYSERR otherwise
624 */
625enum GNUNET_GenericReturnValue
626GNUNET_NETWORK_socket_getsockopt (const struct GNUNET_NETWORK_Handle *desc,
627 int level,
628 int optname,
629 void *optval,
630 socklen_t *optlen)
631{
632 int ret;
633
634 ret = getsockopt (desc->fd,
635 level,
636 optname,
637 optval, optlen);
638
639 return ret == 0 ? GNUNET_OK : GNUNET_SYSERR;
640}
641
642
643/**
644 * Listen on a socket
645 *
646 * @param desc socket
647 * @param backlog length of the listen queue
648 * @return #GNUNET_OK on success, #GNUNET_SYSERR otherwise
649 */
650enum GNUNET_GenericReturnValue
651GNUNET_NETWORK_socket_listen (const struct GNUNET_NETWORK_Handle *desc,
652 int backlog)
653{
654 int ret;
655
656 ret = listen (desc->fd,
657 backlog);
658
659 return ret == 0 ? GNUNET_OK : GNUNET_SYSERR;
660}
661
662
663/**
664 * How much data is available to be read on this descriptor?
665 *
666 * @param desc socket
667 * @returns #GNUNET_SYSERR if no data is available, or on error!
668 */
669ssize_t
670GNUNET_NETWORK_socket_recvfrom_amount (const struct GNUNET_NETWORK_Handle *desc)
671{
672 int error;
673
674 /* How much is there to be read? */
675 int pending;
676
677 error = ioctl (desc->fd,
678 FIONREAD,
679 &pending);
680 if (0 == error)
681 return (ssize_t) pending;
682 return GNUNET_SYSERR;
683}
684
685
686ssize_t
687GNUNET_NETWORK_socket_recvfrom (const struct GNUNET_NETWORK_Handle *desc,
688 void *buffer,
689 size_t length,
690 struct sockaddr *src_addr,
691 socklen_t *addrlen)
692{
693 int flags = 0;
694
695#ifdef MSG_DONTWAIT
696 flags |= MSG_DONTWAIT;
697#endif
698 return recvfrom (desc->fd,
699 buffer,
700 length,
701 flags,
702 src_addr,
703 addrlen);
704}
705
706
707/**
708 * Read data from a connected socket (always non-blocking).
709 *
710 * @param desc socket
711 * @param buffer buffer
712 * @param length length of @a buffer
713 * @return number of bytes received, -1 on error
714 */
715ssize_t
716GNUNET_NETWORK_socket_recv (const struct GNUNET_NETWORK_Handle *desc,
717 void *buffer,
718 size_t length)
719{
720 int ret;
721 int flags;
722
723 flags = 0;
724
725#ifdef MSG_DONTWAIT
726 flags |= MSG_DONTWAIT;
727#endif
728 ret = recv (desc->fd,
729 buffer,
730 length,
731 flags);
732 return ret;
733}
734
735
736ssize_t
737GNUNET_NETWORK_socket_send (const struct GNUNET_NETWORK_Handle *desc,
738 const void *buffer,
739 size_t length)
740{
741 int ret;
742 int flags;
743
744 flags = 0;
745#ifdef MSG_DONTWAIT
746 flags |= MSG_DONTWAIT;
747#endif
748#ifdef MSG_NOSIGNAL
749 flags |= MSG_NOSIGNAL;
750#endif
751 ret = send (desc->fd,
752 buffer,
753 length,
754 flags);
755 return ret;
756}
757
758
759/**
760 * Send data to a particular destination (always non-blocking).
761 * This function only works for UDP sockets.
762 *
763 * @param desc socket
764 * @param message data to send
765 * @param length size of the @a message
766 * @param dest_addr destination address
767 * @param dest_len length of @a address
768 * @return number of bytes sent, #GNUNET_SYSERR on error
769 */
770ssize_t
771GNUNET_NETWORK_socket_sendto (const struct GNUNET_NETWORK_Handle *desc,
772 const void *message,
773 size_t length,
774 const struct sockaddr *dest_addr,
775 socklen_t dest_len)
776{
777 int flags = 0;
778
779#ifdef MSG_DONTWAIT
780 flags |= MSG_DONTWAIT;
781#endif
782#ifdef MSG_NOSIGNAL
783 flags |= MSG_NOSIGNAL;
784#endif
785 return sendto (desc->fd,
786 message,
787 length,
788 flags,
789 dest_addr,
790 dest_len);
791}
792
793
794/**
795 * Set socket option
796 *
797 * @param fd socket
798 * @param level protocol level of the option
799 * @param option_name option identifier
800 * @param option_value value to set
801 * @param option_len size of @a option_value
802 * @return #GNUNET_OK on success, #GNUNET_SYSERR otherwise
803 */
804int
805GNUNET_NETWORK_socket_setsockopt (struct GNUNET_NETWORK_Handle *fd,
806 int level,
807 int option_name,
808 const void *option_value,
809 socklen_t option_len)
810{
811 return (0 == setsockopt (fd->fd,
812 level,
813 option_name,
814 option_value,
815 option_len))
816 ? GNUNET_OK
817 : GNUNET_SYSERR;
818}
819
820
821/**
822 * Create a new socket. Configure it for non-blocking IO and
823 * mark it as non-inheritable to child processes (set the
824 * close-on-exec flag).
825 *
826 * @param domain domain of the socket
827 * @param type socket type
828 * @param protocol network protocol
829 * @return new socket, NULL on error
830 */
831struct GNUNET_NETWORK_Handle *
832GNUNET_NETWORK_socket_create (int domain,
833 int type,
834 int protocol)
835{
836 struct GNUNET_NETWORK_Handle *ret;
837 int fd;
838
839 fd = socket (domain, type, protocol);
840 if (-1 == fd)
841 return NULL;
842 ret = GNUNET_new (struct GNUNET_NETWORK_Handle);
843 ret->fd = fd;
844 if (GNUNET_OK !=
845 initialize_network_handle (ret,
846 domain,
847 type))
848 return NULL;
849 return ret;
850}
851
852
853/**
854 * Shut down socket operations
855 * @param desc socket
856 * @param how type of shutdown
857 * @return #GNUNET_OK on success, #GNUNET_SYSERR otherwise
858 */
859enum GNUNET_GenericReturnValue
860GNUNET_NETWORK_socket_shutdown (struct GNUNET_NETWORK_Handle *desc,
861 int how)
862{
863 int ret;
864
865 ret = shutdown (desc->fd, how);
866
867 return (0 == ret) ? GNUNET_OK : GNUNET_SYSERR;
868}
869
870
871/**
872 * Disable the "CORK" feature for communication with the given socket,
873 * forcing the OS to immediately flush the buffer on transmission
874 * instead of potentially buffering multiple messages. Essentially
875 * reduces the OS send buffers to zero.
876 *
877 * @param desc socket
878 * @return #GNUNET_OK on success, #GNUNET_SYSERR otherwise
879 */
880enum GNUNET_GenericReturnValue
881GNUNET_NETWORK_socket_disable_corking (struct GNUNET_NETWORK_Handle *desc)
882{
883 int ret = 0;
884
885#ifdef __linux__
886 int value = 0;
887
888 if (0 !=
889 (ret =
890 setsockopt (desc->fd,
891 SOL_SOCKET,
892 SO_SNDBUF,
893 &value,
894 sizeof(value))))
895 LOG_STRERROR (GNUNET_ERROR_TYPE_WARNING,
896 "setsockopt");
897 if (0 !=
898 (ret =
899 setsockopt (desc->fd,
900 SOL_SOCKET,
901 SO_RCVBUF,
902 &value,
903 sizeof(value))))
904 LOG_STRERROR (GNUNET_ERROR_TYPE_WARNING,
905 "setsockopt");
906#endif
907 return ret == 0 ? GNUNET_OK : GNUNET_SYSERR;
908}
909
910
911/**
912 * Reset FD set
913 *
914 * @param fds fd set
915 */
916void
917GNUNET_NETWORK_fdset_zero (struct GNUNET_NETWORK_FDSet *fds)
918{
919 FD_ZERO (&fds->sds);
920 fds->nsds = 0;
921}
922
923
924/**
925 * Add a socket to the FD set
926 *
927 * @param fds fd set
928 * @param desc socket to add
929 */
930void
931GNUNET_NETWORK_fdset_set (struct GNUNET_NETWORK_FDSet *fds,
932 const struct GNUNET_NETWORK_Handle *desc)
933{
934 FD_SET (desc->fd,
935 &fds->sds);
936 fds->nsds = GNUNET_MAX (fds->nsds,
937 desc->fd + 1);
938}
939
940
941/**
942 * Check whether a socket is part of the fd set
943 *
944 * @param fds fd set
945 * @param desc socket
946 * @return 0 if the FD is not set
947 */
948int
949GNUNET_NETWORK_fdset_isset (const struct GNUNET_NETWORK_FDSet *fds,
950 const struct GNUNET_NETWORK_Handle *desc)
951{
952 return FD_ISSET (desc->fd,
953 &fds->sds);
954}
955
956
957/**
958 * Add one fd set to another
959 *
960 * @param dst the fd set to add to
961 * @param src the fd set to add from
962 */
963void
964GNUNET_NETWORK_fdset_add (struct GNUNET_NETWORK_FDSet *dst,
965 const struct GNUNET_NETWORK_FDSet *src)
966{
967 int nfds;
968
969 for (nfds = src->nsds; nfds >= 0; nfds--)
970 if (FD_ISSET (nfds, &src->sds))
971 FD_SET (nfds, &dst->sds);
972 dst->nsds = GNUNET_MAX (dst->nsds,
973 src->nsds);
974}
975
976
977/**
978 * Copy one fd set to another
979 *
980 * @param to destination
981 * @param from source
982 */
983void
984GNUNET_NETWORK_fdset_copy (struct GNUNET_NETWORK_FDSet *to,
985 const struct GNUNET_NETWORK_FDSet *from)
986{
987 FD_COPY (&from->sds,
988 &to->sds);
989 to->nsds = from->nsds;
990}
991
992
993/**
994 * Return file descriptor for this network handle
995 *
996 * @param desc wrapper to process
997 * @return POSIX file descriptor
998 */
999int
1000GNUNET_NETWORK_get_fd (const struct GNUNET_NETWORK_Handle *desc)
1001{
1002 return desc->fd;
1003}
1004
1005
1006/**
1007 * Return sockaddr for this network handle
1008 *
1009 * @param desc wrapper to process
1010 * @return sockaddr
1011 */
1012struct sockaddr*
1013GNUNET_NETWORK_get_addr (const struct GNUNET_NETWORK_Handle *desc)
1014{
1015 return desc->addr;
1016}
1017
1018
1019/**
1020 * Return sockaddr length for this network handle
1021 *
1022 * @param desc wrapper to process
1023 * @return socklen_t for sockaddr
1024 */
1025socklen_t
1026GNUNET_NETWORK_get_addrlen (const struct GNUNET_NETWORK_Handle *desc)
1027{
1028 return desc->addrlen;
1029}
1030
1031
1032/**
1033 * Copy a native fd set
1034 *
1035 * @param to destination
1036 * @param from native source set
1037 * @param nfds the biggest socket number in from + 1
1038 */
1039void
1040GNUNET_NETWORK_fdset_copy_native (struct GNUNET_NETWORK_FDSet *to,
1041 const fd_set *from,
1042 int nfds)
1043{
1044 FD_COPY (from,
1045 &to->sds);
1046 to->nsds = nfds;
1047}
1048
1049
1050/**
1051 * Set a native fd in a set
1052 *
1053 * @param to destination
1054 * @param nfd native FD to set
1055 */
1056void
1057GNUNET_NETWORK_fdset_set_native (struct GNUNET_NETWORK_FDSet *to,
1058 int nfd)
1059{
1060 GNUNET_assert ((nfd >= 0) && (nfd < FD_SETSIZE));
1061 FD_SET (nfd, &to->sds);
1062 to->nsds = GNUNET_MAX (nfd + 1,
1063 to->nsds);
1064}
1065
1066
1067/**
1068 * Test native fd in a set
1069 *
1070 * @param to set to test, NULL for empty set
1071 * @param nfd native FD to test, or -1 for none
1072 * @return #GNUNET_YES if FD is set in the set
1073 */
1074int
1075GNUNET_NETWORK_fdset_test_native (const struct GNUNET_NETWORK_FDSet *to,
1076 int nfd)
1077{
1078 if ((-1 == nfd) ||
1079 (NULL == to))
1080 return GNUNET_NO;
1081 return FD_ISSET (nfd, &to->sds) ? GNUNET_YES : GNUNET_NO;
1082}
1083
1084
1085/**
1086 * Add a file handle to the fd set
1087 * @param fds fd set
1088 * @param h the file handle to add
1089 */
1090void
1091GNUNET_NETWORK_fdset_handle_set (struct GNUNET_NETWORK_FDSet *fds,
1092 const struct GNUNET_DISK_FileHandle *h)
1093{
1094 int fd;
1095
1096 GNUNET_assert (GNUNET_OK ==
1097 GNUNET_DISK_internal_file_handle_ (h,
1098 &fd,
1099 sizeof(int)));
1100 FD_SET (fd,
1101 &fds->sds);
1102 fds->nsds = GNUNET_MAX (fd + 1,
1103 fds->nsds);
1104}
1105
1106
1107/**
1108 * Add a file handle to the fd set
1109 * @param fds fd set
1110 * @param h the file handle to add
1111 */
1112void
1113GNUNET_NETWORK_fdset_handle_set_first (struct GNUNET_NETWORK_FDSet *fds,
1114 const struct GNUNET_DISK_FileHandle *h)
1115{
1116 GNUNET_NETWORK_fdset_handle_set (fds, h);
1117}
1118
1119
1120/**
1121 * Check if a file handle is part of an fd set
1122 *
1123 * @param fds fd set
1124 * @param h file handle
1125 * @return #GNUNET_YES if the file handle is part of the set
1126 */
1127int
1128GNUNET_NETWORK_fdset_handle_isset (const struct GNUNET_NETWORK_FDSet *fds,
1129 const struct GNUNET_DISK_FileHandle *h)
1130{
1131 return FD_ISSET (h->fd,
1132 &fds->sds);
1133}
1134
1135
1136/**
1137 * Checks if two fd sets overlap
1138 *
1139 * @param fds1 first fd set
1140 * @param fds2 second fd set
1141 * @return #GNUNET_YES if they do overlap, #GNUNET_NO otherwise
1142 */
1143int
1144GNUNET_NETWORK_fdset_overlap (const struct GNUNET_NETWORK_FDSet *fds1,
1145 const struct GNUNET_NETWORK_FDSet *fds2)
1146{
1147 int nfds;
1148
1149 nfds = GNUNET_MIN (fds1->nsds,
1150 fds2->nsds);
1151 while (nfds > 0)
1152 {
1153 nfds--;
1154 if ((FD_ISSET (nfds,
1155 &fds1->sds)) &&
1156 (FD_ISSET (nfds,
1157 &fds2->sds)))
1158 return GNUNET_YES;
1159 }
1160 return GNUNET_NO;
1161}
1162
1163
1164/**
1165 * Creates an fd set
1166 *
1167 * @return a new fd set
1168 */
1169struct GNUNET_NETWORK_FDSet *
1170GNUNET_NETWORK_fdset_create ()
1171{
1172 struct GNUNET_NETWORK_FDSet *fds;
1173
1174 fds = GNUNET_new (struct GNUNET_NETWORK_FDSet);
1175 GNUNET_NETWORK_fdset_zero (fds);
1176 return fds;
1177}
1178
1179
1180/**
1181 * Releases the associated memory of an fd set
1182 *
1183 * @param fds fd set
1184 */
1185void
1186GNUNET_NETWORK_fdset_destroy (struct GNUNET_NETWORK_FDSet *fds)
1187{
1188 GNUNET_free (fds);
1189}
1190
1191
1192/**
1193 * Test if the given @a port is available.
1194 *
1195 * @param ipproto transport protocol to test (e.g. IPPROTO_TCP)
1196 * @param port port number to test
1197 * @return #GNUNET_OK if the port is available, #GNUNET_NO if not
1198 */
1199int
1200GNUNET_NETWORK_test_port_free (int ipproto,
1201 uint16_t port)
1202{
1203 struct GNUNET_NETWORK_Handle *socket;
1204 int bind_status;
1205 int socktype;
1206 char open_port_str[6];
1207 struct addrinfo hint;
1208 struct addrinfo *ret;
1209 struct addrinfo *ai;
1210
1211 GNUNET_snprintf (open_port_str,
1212 sizeof(open_port_str),
1213 "%u",
1214 (unsigned int) port);
1215 socktype = (IPPROTO_TCP == ipproto) ? SOCK_STREAM : SOCK_DGRAM;
1216 ret = NULL;
1217 memset (&hint, 0, sizeof(hint));
1218 hint.ai_family = AF_UNSPEC; /* IPv4 and IPv6 */
1219 hint.ai_socktype = socktype;
1220 hint.ai_protocol = ipproto;
1221 hint.ai_addrlen = 0;
1222 hint.ai_addr = NULL;
1223 hint.ai_canonname = NULL;
1224 hint.ai_next = NULL;
1225 hint.ai_flags = AI_PASSIVE | AI_NUMERICSERV; /* Wild card address */
1226 GNUNET_assert (0 == getaddrinfo (NULL,
1227 open_port_str,
1228 &hint,
1229 &ret));
1230 bind_status = GNUNET_NO;
1231 for (ai = ret; NULL != ai; ai = ai->ai_next)
1232 {
1233 socket = GNUNET_NETWORK_socket_create (ai->ai_family,
1234 ai->ai_socktype,
1235 ai->ai_protocol);
1236 if (NULL == socket)
1237 continue;
1238 bind_status = GNUNET_NETWORK_socket_bind (socket,
1239 ai->ai_addr,
1240 ai->ai_addrlen);
1241 GNUNET_NETWORK_socket_close (socket);
1242 if (GNUNET_OK != bind_status)
1243 break;
1244 }
1245 freeaddrinfo (ret);
1246 return bind_status;
1247}
1248
1249
1250/**
1251 * Check if sockets or pipes meet certain conditions
1252 *
1253 * @param rfds set of sockets or pipes to be checked for readability
1254 * @param wfds set of sockets or pipes to be checked for writability
1255 * @param efds set of sockets or pipes to be checked for exceptions
1256 * @param timeout relative value when to return
1257 * @return number of selected sockets or pipes, #GNUNET_SYSERR on error
1258 */
1259int
1260GNUNET_NETWORK_socket_select (struct GNUNET_NETWORK_FDSet *rfds,
1261 struct GNUNET_NETWORK_FDSet *wfds,
1262 struct GNUNET_NETWORK_FDSet *efds,
1263 const struct GNUNET_TIME_Relative timeout)
1264{
1265 int nfds;
1266 struct timeval tv;
1267
1268 if (NULL != rfds)
1269 nfds = rfds->nsds;
1270 else
1271 nfds = 0;
1272 if (NULL != wfds)
1273 nfds = GNUNET_MAX (nfds,
1274 wfds->nsds);
1275 if (NULL != efds)
1276 nfds = GNUNET_MAX (nfds,
1277 efds->nsds);
1278 if ((0 == nfds) &&
1279 (timeout.rel_value_us == GNUNET_TIME_UNIT_FOREVER_REL.rel_value_us))
1280 {
1281 GNUNET_break (0);
1282 LOG (GNUNET_ERROR_TYPE_ERROR,
1283 _ (
1284 "Fatal internal logic error, process hangs in `%s' (abort with CTRL-C)!\n"),
1285 "select");
1286 }
1287 if (timeout.rel_value_us / GNUNET_TIME_UNIT_SECONDS.rel_value_us > (unsigned
1288 long long)
1289 LONG_MAX)
1290 {
1291 tv.tv_sec = LONG_MAX;
1292 tv.tv_usec = 999999L;
1293 }
1294 else
1295 {
1296 tv.tv_sec = (long) (timeout.rel_value_us
1297 / GNUNET_TIME_UNIT_SECONDS.rel_value_us);
1298 tv.tv_usec =
1299 (timeout.rel_value_us
1300 - (tv.tv_sec * GNUNET_TIME_UNIT_SECONDS.rel_value_us));
1301 }
1302 return select (nfds,
1303 (NULL != rfds) ? &rfds->sds : NULL,
1304 (NULL != wfds) ? &wfds->sds : NULL,
1305 (NULL != efds) ? &efds->sds : NULL,
1306 (timeout.rel_value_us ==
1307 GNUNET_TIME_UNIT_FOREVER_REL.rel_value_us) ? NULL : &tv);
1308}
1309
1310
1311/* end of network.c */
diff --git a/src/lib/util/nt.c b/src/lib/util/nt.c
new file mode 100644
index 000000000..24471d9ad
--- /dev/null
+++ b/src/lib/util/nt.c
@@ -0,0 +1,438 @@
1/*
2 This file is part of GNUnet.
3 Copyright (C) 2010-2015, 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 util/nt.c
22 * @brief LAN interface scanning to determine IPs in LAN
23 * @author Christian Grothoff
24 * @author Matthias Wachs
25 */
26#include "platform.h"
27#include "gnunet_util_lib.h"
28
29/**
30 * How frequently do we scan the interfaces for changes to the addresses?
31 */
32#define INTERFACE_PROCESSING_INTERVAL GNUNET_TIME_relative_multiply ( \
33 GNUNET_TIME_UNIT_MINUTES, 2)
34
35
36const char *
37GNUNET_NT_to_string (enum GNUNET_NetworkType net)
38{
39 switch (net)
40 {
41 case GNUNET_NT_UNSPECIFIED:
42 return "UNSPECIFIED";
43
44 case GNUNET_NT_LOOPBACK:
45 return "LOOPBACK";
46
47 case GNUNET_NT_LAN:
48 return "LAN";
49
50 case GNUNET_NT_WAN:
51 return "WAN";
52
53 case GNUNET_NT_WLAN:
54 return "WLAN";
55
56 case GNUNET_NT_BT:
57 return "BLUETOOTH";
58
59 default:
60 return NULL;
61 }
62}
63
64
65/**
66 * We keep a list of our local networks so we can answer
67 * LAN vs. WAN questions. Note: WLAN is not detected yet.
68 * (maybe we can do that heuristically based on interface
69 * name in the future?).
70 */
71struct NT_Network
72{
73 /**
74 * Kept in a DLL.
75 */
76 struct NT_Network *next;
77
78 /**
79 * Kept in a DLL.
80 */
81 struct NT_Network *prev;
82
83 /**
84 * Network address.
85 */
86 struct sockaddr *network;
87
88 /**
89 * Netmask to determine what is in the LAN.
90 */
91 struct sockaddr *netmask;
92
93 /**
94 * How long are @e network and @e netmask?
95 */
96 socklen_t length;
97};
98
99
100/**
101 * Handle to the interface scanner.
102 */
103struct GNUNET_NT_InterfaceScanner
104{
105 /**
106 * Head of LAN networks list.
107 */
108 struct NT_Network *net_head;
109
110 /**
111 * Tail of LAN networks list.
112 */
113 struct NT_Network *net_tail;
114
115 /**
116 * Task for periodically refreshing our LAN network list.
117 */
118 struct GNUNET_SCHEDULER_Task *interface_task;
119};
120
121
122/**
123 * Delete all entries from the current network list.
124 *
125 * @param is scanner to clean up
126 */
127static void
128delete_networks (struct GNUNET_NT_InterfaceScanner *is)
129{
130 struct NT_Network *cur;
131
132 while (NULL != (cur = is->net_head))
133 {
134 GNUNET_CONTAINER_DLL_remove (is->net_head,
135 is->net_tail,
136 cur);
137 GNUNET_free (cur);
138 }
139}
140
141
142/**
143 * Function invoked for each interface found. Adds the interface's
144 * network addresses to the respective DLL, so we can distinguish
145 * between LAN and WAN.
146 *
147 * @param cls closure with the `struct GNUNET_NT_InterfaceScanner`
148 * @param name name of the interface (can be NULL for unknown)
149 * @param isDefault is this presumably the default interface
150 * @param addr address of this interface (can be NULL for unknown or unassigned)
151 * @param broadcast_addr the broadcast address (can be NULL for unknown or unassigned)
152 * @param netmask the network mask (can be NULL for unknown or unassigned)
153 * @param addrlen length of the address
154 * @return #GNUNET_OK to continue iteration
155 */
156static int
157interface_proc (void *cls,
158 const char *name,
159 int isDefault,
160 const struct sockaddr *addr,
161 const struct sockaddr *broadcast_addr,
162 const struct sockaddr *netmask,
163 socklen_t addrlen)
164{
165 struct GNUNET_NT_InterfaceScanner *is = cls;
166 /* Calculate network */
167 struct NT_Network *net = NULL;
168
169 (void) name;
170 (void) isDefault;
171 (void) broadcast_addr;
172
173 /* Skipping IPv4 loopback addresses since we have special check */
174 if (addr->sa_family == AF_INET)
175 {
176 const struct sockaddr_in *a4 = (const struct sockaddr_in *) addr;
177
178 if ((a4->sin_addr.s_addr & htonl (0xff000000)) == htonl (0x7f000000))
179 return GNUNET_OK;
180 }
181 /* Skipping IPv6 loopback addresses since we have special check */
182 if (addr->sa_family == AF_INET6)
183 {
184 const struct sockaddr_in6 *a6 = (const struct sockaddr_in6 *) addr;
185 if (IN6_IS_ADDR_LOOPBACK (&a6->sin6_addr))
186 return GNUNET_OK;
187 }
188
189 if (addr->sa_family == AF_INET)
190 {
191 const struct sockaddr_in *addr4 = (const struct sockaddr_in *) addr;
192 const struct sockaddr_in *netmask4 = (const struct sockaddr_in *) netmask;
193 struct sockaddr_in *tmp;
194 struct sockaddr_in network4;
195
196 net = GNUNET_malloc (sizeof(struct NT_Network) + 2 * sizeof(struct
197 sockaddr_in));
198 tmp = (struct sockaddr_in *) &net[1];
199 net->network = (struct sockaddr *) &tmp[0];
200 net->netmask = (struct sockaddr *) &tmp[1];
201 net->length = addrlen;
202
203 memset (&network4,
204 0,
205 sizeof(network4));
206 network4.sin_family = AF_INET;
207#if HAVE_SOCKADDR_IN_SIN_LEN
208 network4.sin_len = sizeof(network4);
209#endif
210 network4.sin_addr.s_addr = (addr4->sin_addr.s_addr
211 & netmask4->sin_addr.s_addr);
212
213 GNUNET_memcpy (net->netmask,
214 netmask4,
215 sizeof(struct sockaddr_in));
216 GNUNET_memcpy (net->network,
217 &network4,
218 sizeof(struct sockaddr_in));
219 }
220
221 if (addr->sa_family == AF_INET6)
222 {
223 const struct sockaddr_in6 *addr6 = (const struct sockaddr_in6 *) addr;
224 const struct sockaddr_in6 *netmask6 = (const struct sockaddr_in6 *) netmask;
225 struct sockaddr_in6 *tmp;
226 struct sockaddr_in6 network6;
227
228 net = GNUNET_malloc (sizeof(struct NT_Network) + 2 * sizeof(struct
229 sockaddr_in6));
230 tmp = (struct sockaddr_in6 *) &net[1];
231 net->network = (struct sockaddr *) &tmp[0];
232 net->netmask = (struct sockaddr *) &tmp[1];
233 net->length = addrlen;
234
235 memset (&network6, 0, sizeof(network6));
236 network6.sin6_family = AF_INET6;
237#if HAVE_SOCKADDR_IN_SIN_LEN
238 network6.sin6_len = sizeof(network6);
239#endif
240 unsigned int c = 0;
241 uint32_t *addr_elem = (uint32_t *) &addr6->sin6_addr;
242 uint32_t *mask_elem = (uint32_t *) &netmask6->sin6_addr;
243 uint32_t *net_elem = (uint32_t *) &network6.sin6_addr;
244 for (c = 0; c < 4; c++)
245 net_elem[c] = addr_elem[c] & mask_elem[c];
246
247 GNUNET_memcpy (net->netmask,
248 netmask6,
249 sizeof(struct sockaddr_in6));
250 GNUNET_memcpy (net->network,
251 &network6,
252 sizeof(struct sockaddr_in6));
253 }
254 if (NULL == net)
255 return GNUNET_OK; /* odd / unsupported address family */
256
257 /* Store in list */
258#if VERBOSE_NT
259 char *netmask = GNUNET_strdup (GNUNET_a2s ((struct sockaddr *) net->netmask,
260 addrlen));
261 GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG,
262 "nt",
263 "Adding network `%s', netmask `%s'\n",
264 GNUNET_a2s ((struct sockaddr *) net->network,
265 addrlen),
266 netmask);
267 GNUNET_free (netmask);
268#endif
269 GNUNET_CONTAINER_DLL_insert (is->net_head,
270 is->net_tail,
271 net);
272
273 return GNUNET_OK;
274}
275
276
277/**
278 * Periodically get list of network addresses from our interfaces.
279 *
280 * @param cls closure
281 */
282static void
283get_addresses (void *cls)
284{
285 struct GNUNET_NT_InterfaceScanner *is = cls;
286
287 is->interface_task = NULL;
288 delete_networks (is);
289 GNUNET_OS_network_interfaces_list (&interface_proc,
290 is);
291 is->interface_task = GNUNET_SCHEDULER_add_delayed (
292 INTERFACE_PROCESSING_INTERVAL,
293 &get_addresses,
294 is);
295}
296
297
298/**
299 * Returns where the address is located: LAN or WAN or ...
300 *
301 * @param is the interface scanner handle
302 * @param addr address
303 * @param addrlen address length
304 * @return type of the network the address belongs to
305 */
306enum GNUNET_NetworkType
307GNUNET_NT_scanner_get_type (struct GNUNET_NT_InterfaceScanner *is,
308 const struct sockaddr *addr,
309 socklen_t addrlen)
310{
311 struct NT_Network *cur = is->net_head;
312 enum GNUNET_NetworkType type = GNUNET_NT_UNSPECIFIED;
313
314 switch (addr->sa_family)
315 {
316 case AF_UNIX:
317 type = GNUNET_NT_LOOPBACK;
318 break;
319
320 case AF_INET:
321 {
322 const struct sockaddr_in *a4 = (const struct sockaddr_in *) addr;
323
324 if ((a4->sin_addr.s_addr & htonl (0xff000000)) == htonl (0x7f000000))
325 type = GNUNET_NT_LOOPBACK;
326 break;
327 }
328
329 case AF_INET6:
330 {
331 const struct sockaddr_in6 *a6 = (const struct sockaddr_in6 *) addr;
332
333 if (IN6_IS_ADDR_LOOPBACK (&a6->sin6_addr))
334 type = GNUNET_NT_LOOPBACK;
335 break;
336 }
337
338 default:
339 GNUNET_break (0);
340 break;
341 }
342
343 /* Check local networks */
344 while ((NULL != cur) && (GNUNET_NT_UNSPECIFIED == type))
345 {
346 if (addrlen != cur->length)
347 {
348 cur = cur->next;
349 continue;
350 }
351 if (addr->sa_family == AF_INET)
352 {
353 const struct sockaddr_in *a4 = (const struct sockaddr_in *) addr;
354 const struct sockaddr_in *net4 = (const struct
355 sockaddr_in *) cur->network;
356 const struct sockaddr_in *mask4 = (const struct
357 sockaddr_in *) cur->netmask;
358
359 if (((a4->sin_addr.s_addr & mask4->sin_addr.s_addr)) ==
360 net4->sin_addr.s_addr)
361 type = GNUNET_NT_LAN;
362 }
363 if (addr->sa_family == AF_INET6)
364 {
365 const struct sockaddr_in6 *a6 = (const struct sockaddr_in6 *) addr;
366 const struct sockaddr_in6 *net6 = (const struct
367 sockaddr_in6 *) cur->network;
368 const struct sockaddr_in6 *mask6 = (const struct
369 sockaddr_in6 *) cur->netmask;
370
371 int res = GNUNET_YES;
372 int c = 0;
373 uint32_t *addr_elem = (uint32_t *) &a6->sin6_addr;
374 uint32_t *mask_elem = (uint32_t *) &mask6->sin6_addr;
375 uint32_t *net_elem = (uint32_t *) &net6->sin6_addr;
376 for (c = 0; c < 4; c++)
377 if ((addr_elem[c] & mask_elem[c]) != net_elem[c])
378 res = GNUNET_NO;
379
380 if (res == GNUNET_YES)
381 type = GNUNET_NT_LAN;
382 }
383 cur = cur->next;
384 }
385
386 /* no local network found for this address, default: WAN */
387 if (type == GNUNET_NT_UNSPECIFIED)
388 type = GNUNET_NT_WAN;
389 GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG,
390 "nt-scanner-api",
391 "`%s' is in network `%s'\n",
392 GNUNET_a2s (addr,
393 addrlen),
394 GNUNET_NT_to_string (type));
395 return type;
396}
397
398
399/**
400 * Initialize the interface scanner.
401 *
402 * @return interface scanner
403 */
404struct GNUNET_NT_InterfaceScanner *
405GNUNET_NT_scanner_init ()
406{
407 struct GNUNET_NT_InterfaceScanner *is;
408
409 is = GNUNET_new (struct GNUNET_NT_InterfaceScanner);
410 GNUNET_OS_network_interfaces_list (&interface_proc,
411 is);
412 is->interface_task = GNUNET_SCHEDULER_add_delayed (
413 INTERFACE_PROCESSING_INTERVAL,
414 &get_addresses,
415 is);
416 return is;
417}
418
419
420/**
421 * Client is done with the interface scanner, release resources.
422 *
423 * @param is handle to release
424 */
425void
426GNUNET_NT_scanner_done (struct GNUNET_NT_InterfaceScanner *is)
427{
428 if (NULL != is->interface_task)
429 {
430 GNUNET_SCHEDULER_cancel (is->interface_task);
431 is->interface_task = NULL;
432 }
433 delete_networks (is);
434 GNUNET_free (is);
435}
436
437
438/* end of nt.c */
diff --git a/src/lib/util/op.c b/src/lib/util/op.c
new file mode 100644
index 000000000..a8fc3de4a
--- /dev/null
+++ b/src/lib/util/op.c
@@ -0,0 +1,335 @@
1/*
2 This file is part of GNUnet.
3 Copyright (C) 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
23 * Asynchronous operations; register callbacks for operations and call them when a response arrives.
24 *
25 * @author Gabor X Toth
26 */
27
28#include "platform.h"
29#include <inttypes.h>
30
31
32#include "gnunet_util_lib.h"
33
34#define LOG(kind, ...) GNUNET_log_from (kind, "util-op", __VA_ARGS__)
35
36struct OperationListItem
37{
38 struct OperationListItem *prev;
39 struct OperationListItem *next;
40
41 /**
42 * Operation ID.
43 */
44 uint64_t op_id;
45
46 /**
47 * Continuation to invoke with the result of an operation.
48 */
49 GNUNET_ResultCallback result_cb;
50
51 /**
52 * Closure for @a result_cb.
53 */
54 void *cls;
55
56 /**
57 * User context.
58 */
59 void *ctx;
60};
61
62
63/**
64 * Operations handle.
65 */
66
67struct GNUNET_OP_Handle
68{
69 /**
70 * First operation in the linked list.
71 */
72 struct OperationListItem *op_head;
73
74 /**
75 * Last operation in the linked list.
76 */
77 struct OperationListItem *op_tail;
78
79 /**
80 * Last operation ID used.
81 */
82 uint64_t last_op_id;
83};
84
85
86/**
87 * Create new operations handle.
88 */
89struct GNUNET_OP_Handle *
90GNUNET_OP_create ()
91{
92 return GNUNET_new (struct GNUNET_OP_Handle);
93}
94
95
96/**
97 * Destroy operations handle.
98 */
99void
100GNUNET_OP_destroy (struct GNUNET_OP_Handle *h)
101{
102 GNUNET_free (h);
103}
104
105
106/**
107 * Get a unique operation ID to distinguish between asynchronous requests.
108 *
109 * @param h
110 * Operations handle.
111 *
112 * @return Operation ID to use.
113 */
114uint64_t
115GNUNET_OP_get_next_id (struct GNUNET_OP_Handle *h)
116{
117 return ++h->last_op_id;
118}
119
120
121/**
122 * Find operation by ID.
123 *
124 * @param h
125 * Operations handle.
126 * @param op_id
127 * Operation ID to look up.
128 *
129 * @return Operation, or NULL if not found.
130 */
131static struct OperationListItem *
132op_find (struct GNUNET_OP_Handle *h,
133 uint64_t op_id)
134{
135 struct OperationListItem *op;
136
137 for (op = h->op_head; NULL != op; op = op->next)
138 if (op->op_id == op_id)
139 return op;
140 return NULL;
141}
142
143
144/**
145 * Find operation by ID.
146 *
147 * @param h
148 * Operations handle.
149 * @param op_id
150 * Operation ID to look up.
151 * @param[out] result_cb
152 * If an operation was found, its result callback is returned here.
153 * @param[out] cls
154 * If an operation was found, its closure is returned here.
155 * @param[out] ctx
156 * If an operation was found, its user context is returned here.
157 *
158 * @return #GNUNET_YES if an operation was found,
159 * #GNUNET_NO if not found.
160 */
161int
162GNUNET_OP_get (struct GNUNET_OP_Handle *h,
163 uint64_t op_id,
164 GNUNET_ResultCallback *result_cb,
165 void **cls,
166 void **ctx)
167{
168 struct OperationListItem *op = op_find (h, op_id);
169
170 if (NULL != op)
171 {
172 if (NULL != result_cb)
173 *result_cb = op->result_cb;
174 if (NULL != cls)
175 *cls = op->cls;
176 if (NULL != ctx)
177 *ctx = op->ctx;
178 return GNUNET_YES;
179 }
180 return GNUNET_NO;
181}
182
183
184/**
185 * Add a new operation.
186 *
187 * @param h
188 * Operations handle.
189 * @param result_cb
190 * Function to call with the result of the operation.
191 * @param cls
192 * Closure for @a result_cb.
193 * @param ctx
194 * User context.
195 *
196 * @return ID of the new operation.
197 */
198uint64_t
199GNUNET_OP_add (struct GNUNET_OP_Handle *h,
200 GNUNET_ResultCallback result_cb,
201 void *cls,
202 void *ctx)
203{
204 struct OperationListItem *op;
205
206 op = GNUNET_new (struct OperationListItem);
207 op->op_id = GNUNET_OP_get_next_id (h);
208 op->result_cb = result_cb;
209 op->cls = cls;
210 op->ctx = ctx;
211 GNUNET_CONTAINER_DLL_insert_tail (h->op_head,
212 h->op_tail,
213 op);
214 LOG (GNUNET_ERROR_TYPE_DEBUG,
215 "%p Added operation #%" PRIu64 "\n",
216 h, op->op_id);
217 return op->op_id;
218}
219
220
221/**
222 * Remove an operation, and call its result callback (unless it was cancelled).
223 *
224 *
225 * @param h
226 * Operations handle.
227 * @param op_id
228 * Operation ID.
229 * @param result_code
230 * Result of the operation.
231 * @param data
232 * Data result of the operation.
233 * @param data_size
234 * Size of @a data.
235 * @param[out] ctx
236 * User context.
237 * @param cancel
238 * Is the operation cancelled?
239 * #GNUNET_NO Not cancelled, result callback is called.
240 * #GNUNET_YES Cancelled, result callback is not called.
241 *
242 * @return #GNUNET_YES if the operation was found and removed,
243 * #GNUNET_NO if the operation was not found.
244 */
245static int
246op_result (struct GNUNET_OP_Handle *h,
247 uint64_t op_id,
248 int64_t result_code,
249 const void *data,
250 uint16_t data_size,
251 void **ctx,
252 uint8_t cancel)
253{
254 if (0 == op_id)
255 return GNUNET_NO;
256
257 struct OperationListItem *op = op_find (h, op_id);
258 if (NULL == op)
259 {
260 LOG (GNUNET_ERROR_TYPE_WARNING,
261 "Could not find operation #%" PRIu64 "\n", op_id);
262 return GNUNET_NO;
263 }
264
265 if (NULL != ctx)
266 *ctx = op->ctx;
267
268 GNUNET_CONTAINER_DLL_remove (h->op_head,
269 h->op_tail,
270 op);
271
272 if ((GNUNET_YES != cancel) &&
273 (NULL != op->result_cb))
274 op->result_cb (op->cls,
275 result_code, data,
276 data_size);
277 GNUNET_free (op);
278 return GNUNET_YES;
279}
280
281
282/**
283 * Call the result callback of an operation and remove it.
284 *
285 * @param h
286 * Operations handle.
287 * @param op_id
288 * Operation ID.
289 * @param result_code
290 * Result of the operation.
291 * @param data
292 * Data result of the operation.
293 * @param data_size
294 * Size of @a data.
295 * @param[out] ctx
296 * User context.
297 *
298 * @return #GNUNET_YES if the operation was found and removed,
299 * #GNUNET_NO if the operation was not found.
300 */
301int
302GNUNET_OP_result (struct GNUNET_OP_Handle *h,
303 uint64_t op_id,
304 int64_t result_code,
305 const void *data,
306 uint16_t data_size,
307 void **ctx)
308{
309 LOG (GNUNET_ERROR_TYPE_DEBUG,
310 "%p Received result for operation #%" PRIu64 ": %" PRId64 " (size: %u)\n",
311 h, op_id, result_code, data_size);
312 return op_result (h, op_id, result_code, data, data_size, ctx, GNUNET_NO);
313}
314
315
316/**
317 * Remove / cancel an operation.
318 *
319 * @param h
320 * Operations handle.
321 * @param op_id
322 * Operation ID.
323 *
324 * @return #GNUNET_YES if the operation was found and removed,
325 * #GNUNET_NO if the operation was not found.
326 */
327int
328GNUNET_OP_remove (struct GNUNET_OP_Handle *h,
329 uint64_t op_id)
330{
331 LOG (GNUNET_ERROR_TYPE_DEBUG,
332 "%p Cancelling operation #%" PRIu64 "\n",
333 h, op_id);
334 return op_result (h, op_id, 0, NULL, 0, NULL, GNUNET_YES);
335}
diff --git a/src/lib/util/os_installation.c b/src/lib/util/os_installation.c
new file mode 100644
index 000000000..ff04a8a7f
--- /dev/null
+++ b/src/lib/util/os_installation.c
@@ -0,0 +1,829 @@
1/*
2 This file is part of GNUnet.
3 Copyright (C) 2006-2018, 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 * @file src/util/os_installation.c
23 * @brief get paths used by the program
24 * @author Milan
25 * @author Christian Fuchs
26 * @author Christian Grothoff
27 * @author Matthias Wachs
28 * @author Heikki Lindholm
29 * @author LRN
30 */
31#include "platform.h"
32#include <sys/stat.h>
33#include <stdlib.h>
34#include <string.h>
35#include <unistd.h>
36#include <unistr.h> /* for u16_to_u8 */
37
38
39#include "gnunet_util_lib.h"
40#if DARWIN
41#include <mach-o/ldsyms.h>
42#include <mach-o/dyld.h>
43#endif
44
45
46#define LOG(kind, ...) \
47 GNUNET_log_from (kind, "util-os-installation", __VA_ARGS__)
48
49#define LOG_STRERROR_FILE(kind, syscall, filename) \
50 GNUNET_log_from_strerror_file (kind, \
51 "util-os-installation", \
52 syscall, \
53 filename)
54
55
56/**
57 * Default project data used for installation path detection
58 * for GNUnet (core).
59 */
60static const struct GNUNET_OS_ProjectData default_pd = {
61 .libname = "libgnunetutil",
62 .project_dirname = "gnunet",
63 .binary_name = "gnunet-arm",
64 .version = PACKAGE_VERSION " " VCS_VERSION,
65 .env_varname = "GNUNET_PREFIX",
66 .base_config_varname = "GNUNET_BASE_CONFIG",
67 .bug_email = "gnunet-developers@gnu.org",
68 .homepage = "http://www.gnu.org/s/gnunet/",
69 .config_file = "gnunet.conf",
70 .user_config_file = "~/.config/gnunet.conf",
71 .is_gnu = 1,
72 .gettext_domain = "gnunet",
73 .gettext_path = NULL,
74 .agpl_url = GNUNET_AGPL_URL,
75};
76
77/**
78 * Which project data do we currently use for installation
79 * path detection? Never NULL.
80 */
81static const struct GNUNET_OS_ProjectData *current_pd = &default_pd;
82
83/**
84 * PD for which gettext has been initialized last.
85 * Note that the gettext initialization done within
86 * GNUNET_PROGRAM_run2 is for the specific application.
87 */
88static const struct GNUNET_OS_ProjectData *gettextinit;
89
90
91/**
92 * Return default project data used by 'libgnunetutil' for GNUnet.
93 */
94const struct GNUNET_OS_ProjectData *
95GNUNET_OS_project_data_default (void)
96{
97 return &default_pd;
98}
99
100
101/**
102 * @return current project data.
103 */
104const struct GNUNET_OS_ProjectData *
105GNUNET_OS_project_data_get ()
106{
107 if (current_pd != gettextinit)
108 {
109 char *path = GNUNET_OS_installation_get_path (GNUNET_OS_IPK_LOCALEDIR);
110
111 if (NULL != path)
112 bindtextdomain (PACKAGE,
113 path);
114 GNUNET_free (path);
115 gettextinit = current_pd;
116 }
117 return current_pd;
118}
119
120
121void
122GNUNET_OS_init (const struct GNUNET_OS_ProjectData *pd)
123{
124 GNUNET_assert (NULL != pd);
125 current_pd = pd;
126 if (pd != gettextinit)
127 {
128 char *path = GNUNET_OS_installation_get_path (GNUNET_OS_IPK_LOCALEDIR);
129
130 if (NULL != path)
131 bindtextdomain (PACKAGE,
132 path);
133 GNUNET_free (path);
134 gettextinit = pd;
135 }
136}
137
138
139#ifdef __linux__
140/**
141 * Try to determine path by reading /proc/PID/exe
142 *
143 * @return NULL on error
144 */
145static char *
146get_path_from_proc_maps (void)
147{
148 char fn[64];
149 char line[1024];
150 char dir[1024];
151 FILE *f;
152 char *lgu;
153
154 if (NULL == current_pd->libname)
155 return NULL;
156 GNUNET_snprintf (fn,
157 sizeof(fn),
158 "/proc/%u/maps",
159 getpid ());
160 if (NULL == (f = fopen (fn, "r")))
161 return NULL;
162 while (NULL != fgets (line, sizeof(line), f))
163 {
164 if ((1 == sscanf (line,
165 "%*p-%*p %*c%*c%*c%*c %*x %*x:%*x %*u%*[ ]%1023s",
166 dir)) &&
167 (NULL != (lgu = strstr (dir,
168 current_pd->libname))))
169 {
170 lgu[0] = '\0';
171 fclose (f);
172 return GNUNET_strdup (dir);
173 }
174 }
175 fclose (f);
176 return NULL;
177}
178
179
180/**
181 * Try to determine path by reading /proc/PID/exe
182 *
183 * @return NULL on error
184 */
185static char *
186get_path_from_proc_exe (void)
187{
188 char fn[64];
189 char lnk[1024];
190 ssize_t size;
191 char *lep;
192
193 GNUNET_snprintf (fn, sizeof(fn), "/proc/%u/exe", getpid ());
194 size = readlink (fn, lnk, sizeof(lnk) - 1);
195 if (size <= 0)
196 {
197 LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_ERROR, "readlink", fn);
198 return NULL;
199 }
200 GNUNET_assert (((size_t) size) < sizeof(lnk));
201 lnk[size] = '\0';
202 while ((lnk[size] != '/') && (size > 0))
203 size--;
204 GNUNET_asprintf (&lep, "/%s/libexec/", current_pd->project_dirname);
205 /* test for being in lib/gnunet/libexec/ or lib/MULTIARCH/gnunet/libexec */
206 if ((((size_t) size) > strlen (lep)) &&
207 (0 == strcmp (lep, &lnk[size - strlen (lep)])))
208 size -= strlen (lep) - 1;
209 GNUNET_free (lep);
210 if ((size < 4) || (lnk[size - 4] != '/'))
211 {
212 /* not installed in "/bin/" -- binary path probably useless */
213 return NULL;
214 }
215 lnk[size] = '\0';
216 return GNUNET_strdup (lnk);
217}
218
219
220#endif
221
222
223#if DARWIN
224/**
225 * Signature of the '_NSGetExecutablePath" function.
226 *
227 * @param buf where to write the path
228 * @param number of bytes available in @a buf
229 * @return 0 on success, otherwise desired number of bytes is stored in 'bufsize'
230 */
231typedef int (*MyNSGetExecutablePathProto) (char *buf, size_t *bufsize);
232
233
234/**
235 * Try to obtain the path of our executable using '_NSGetExecutablePath'.
236 *
237 * @return NULL on error
238 */
239static char *
240get_path_from_NSGetExecutablePath (void)
241{
242 static char zero = '\0';
243 char *path;
244 size_t len;
245 MyNSGetExecutablePathProto func;
246
247 path = NULL;
248 if (NULL ==
249 (func = (MyNSGetExecutablePathProto) dlsym (RTLD_DEFAULT,
250 "_NSGetExecutablePath")))
251 return NULL;
252 path = &zero;
253 len = 0;
254 /* get the path len, including the trailing \0 */
255 (void) func (path, &len);
256 if (0 == len)
257 return NULL;
258 path = GNUNET_malloc (len);
259 if (0 != func (path, &len))
260 {
261 GNUNET_free (path);
262 return NULL;
263 }
264 len = strlen (path);
265 while ((path[len] != '/') && (len > 0))
266 len--;
267 path[len] = '\0';
268 return path;
269}
270
271
272/**
273 * Try to obtain the path of our executable using '_dyld_image' API.
274 *
275 * @return NULL on error
276 */
277static char *
278get_path_from_dyld_image (void)
279{
280 const char *path;
281 char *p;
282 char *s;
283 unsigned int i;
284 int c;
285
286 c = _dyld_image_count ();
287 for (i = 0; i < c; i++)
288 {
289 if (((const void *) _dyld_get_image_header (i)) !=
290 ((const void *) &_mh_dylib_header))
291 continue;
292 path = _dyld_get_image_name (i);
293 if ((NULL == path) || (0 == strlen (path)))
294 continue;
295 p = GNUNET_strdup (path);
296 s = p + strlen (p);
297 while ((s > p) && ('/' != *s))
298 s--;
299 s++;
300 *s = '\0';
301 return p;
302 }
303 return NULL;
304}
305
306
307#endif
308
309
310/**
311 * Return the actual path to a file found in the current
312 * PATH environment variable.
313 *
314 * @param binary the name of the file to find
315 * @return path to binary, NULL if not found
316 */
317static char *
318get_path_from_PATH (const char *binary)
319{
320 char *path;
321 char *pos;
322 char *end;
323 char *buf;
324 const char *p;
325
326 if (NULL == (p = getenv ("PATH")))
327 return NULL;
328
329 path = GNUNET_strdup (p); /* because we write on it */
330
331 buf = GNUNET_malloc (strlen (path) + strlen (binary) + 1 + 1);
332 pos = path;
333 while (NULL != (end = strchr (pos, PATH_SEPARATOR)))
334 {
335 *end = '\0';
336 sprintf (buf, "%s/%s", pos, binary);
337 if (GNUNET_DISK_file_test (buf) == GNUNET_YES)
338 {
339 pos = GNUNET_strdup (pos);
340 GNUNET_free (buf);
341 GNUNET_free (path);
342 return pos;
343 }
344 pos = end + 1;
345 }
346 sprintf (buf, "%s/%s", pos, binary);
347 if (GNUNET_YES == GNUNET_DISK_file_test (buf))
348 {
349 pos = GNUNET_strdup (pos);
350 GNUNET_free (buf);
351 GNUNET_free (path);
352 return pos;
353 }
354 GNUNET_free (buf);
355 GNUNET_free (path);
356 return NULL;
357}
358
359
360/**
361 * Try to obtain the installation path using the "GNUNET_PREFIX" environment
362 * variable.
363 *
364 * @return NULL on error (environment variable not set)
365 */
366static char *
367get_path_from_GNUNET_PREFIX (void)
368{
369 const char *p;
370
371 if ((NULL != current_pd->env_varname) &&
372 (NULL != (p = getenv (current_pd->env_varname))))
373 return GNUNET_strdup (p);
374 if ((NULL != current_pd->env_varname_alt) &&
375 (NULL != (p = getenv (current_pd->env_varname_alt))))
376 return GNUNET_strdup (p);
377 return NULL;
378}
379
380
381/**
382 * @brief get the path to GNUnet bin/ or lib/, preferring the lib/ path
383 * @author Milan
384 *
385 * @return a pointer to the executable path, or NULL on error
386 */
387static char *
388os_get_gnunet_path (void)
389{
390 char *ret;
391
392 if (NULL != (ret = get_path_from_GNUNET_PREFIX ()))
393 return ret;
394#ifdef __linux__
395 if (NULL != (ret = get_path_from_proc_maps ()))
396 return ret;
397 /* try path *first*, before /proc/exe, as /proc/exe can be wrong */
398 if ((NULL != current_pd->binary_name) &&
399 (NULL != (ret = get_path_from_PATH (current_pd->binary_name))))
400 return ret;
401 if (NULL != (ret = get_path_from_proc_exe ()))
402 return ret;
403#endif
404#if DARWIN
405 if (NULL != (ret = get_path_from_dyld_image ()))
406 return ret;
407 if (NULL != (ret = get_path_from_NSGetExecutablePath ()))
408 return ret;
409#endif
410 if ((NULL != current_pd->binary_name) &&
411 (NULL != (ret = get_path_from_PATH (current_pd->binary_name))))
412 return ret;
413 /* other attempts here */
414 LOG (GNUNET_ERROR_TYPE_ERROR,
415 _ (
416 "Could not determine installation path for %s. Set `%s' environment variable.\n"),
417 current_pd->project_dirname,
418 current_pd->env_varname);
419 return NULL;
420}
421
422
423/**
424 * @brief get the path to current app's bin/
425 * @return a pointer to the executable path, or NULL on error
426 */
427static char *
428os_get_exec_path ()
429{
430 char *ret = NULL;
431
432#ifdef __linux__
433 if (NULL != (ret = get_path_from_proc_exe ()))
434 return ret;
435#endif
436#if DARWIN
437 if (NULL != (ret = get_path_from_NSGetExecutablePath ()))
438 return ret;
439#endif
440 /* other attempts here */
441 return ret;
442}
443
444
445/**
446 * @brief get the path to a specific GNUnet installation directory or,
447 * with #GNUNET_OS_IPK_SELF_PREFIX, the current running apps installation directory
448 * @return a pointer to the dir path (to be freed by the caller)
449 */
450char *
451GNUNET_OS_installation_get_path (enum GNUNET_OS_InstallationPathKind dirkind)
452{
453 size_t n;
454 char *dirname;
455 char *execpath = NULL;
456 char *tmp;
457 char *multiarch;
458 char *libdir;
459 int isbasedir;
460
461 /* if wanted, try to get the current app's bin/ */
462 if (dirkind == GNUNET_OS_IPK_SELF_PREFIX)
463 execpath = os_get_exec_path ();
464
465 /* try to get GNUnet's bin/ or lib/, or if previous was unsuccessful some
466 * guess for the current app */
467 if (NULL == execpath)
468 execpath = os_get_gnunet_path ();
469 if (NULL == execpath)
470 return NULL;
471
472 n = strlen (execpath);
473 if (0 == n)
474 {
475 /* should never happen, but better safe than sorry */
476 GNUNET_free (execpath);
477 return NULL;
478 }
479 /* remove filename itself */
480 while ((n > 1) && (DIR_SEPARATOR == execpath[n - 1]))
481 execpath[--n] = '\0';
482
483 isbasedir = 1;
484 if ((n > 6) && ((0 == strcasecmp (&execpath[n - 6], "/lib32")) ||
485 (0 == strcasecmp (&execpath[n - 6], "/lib64"))))
486 {
487 if ((GNUNET_OS_IPK_LIBDIR != dirkind) &&
488 (GNUNET_OS_IPK_LIBEXECDIR != dirkind))
489 {
490 /* strip '/lib32' or '/lib64' */
491 execpath[n - 6] = '\0';
492 n -= 6;
493 }
494 else
495 isbasedir = 0;
496 }
497 else if ((n > 4) && ((0 == strcasecmp (&execpath[n - 4], "/bin")) ||
498 (0 == strcasecmp (&execpath[n - 4], "/lib"))))
499 {
500 /* strip '/bin' or '/lib' */
501 execpath[n - 4] = '\0';
502 n -= 4;
503 }
504 multiarch = NULL;
505 if (NULL != (libdir = strstr (execpath, "/lib/")))
506 {
507 /* test for multi-arch path of the form "PREFIX/lib/MULTIARCH/";
508 here we need to re-add 'multiarch' to lib and libexec paths later! */
509 multiarch = &libdir[5];
510 if (NULL == strchr (multiarch, '/'))
511 libdir[0] =
512 '\0'; /* Debian multiarch format, cut of from 'execpath' but preserve in multicarch */
513 else
514 multiarch =
515 NULL; /* maybe not, multiarch still has a '/', which is not OK */
516 }
517 /* in case this was a directory named foo-bin, remove "foo-" */
518 while ((n > 1) && (execpath[n - 1] == DIR_SEPARATOR))
519 execpath[--n] = '\0';
520 switch (dirkind)
521 {
522 case GNUNET_OS_IPK_PREFIX:
523 case GNUNET_OS_IPK_SELF_PREFIX:
524 dirname = GNUNET_strdup (DIR_SEPARATOR_STR);
525 break;
526
527 case GNUNET_OS_IPK_BINDIR:
528 dirname = GNUNET_strdup (DIR_SEPARATOR_STR "bin" DIR_SEPARATOR_STR);
529 break;
530
531 case GNUNET_OS_IPK_LIBDIR:
532 if (isbasedir)
533 {
534 GNUNET_asprintf (&tmp,
535 "%s%s%s%s%s%s%s",
536 execpath,
537 DIR_SEPARATOR_STR "lib",
538 (NULL != multiarch) ? DIR_SEPARATOR_STR : "",
539 (NULL != multiarch) ? multiarch : "",
540 DIR_SEPARATOR_STR,
541 current_pd->project_dirname,
542 DIR_SEPARATOR_STR);
543 if (GNUNET_YES == GNUNET_DISK_directory_test (tmp, GNUNET_YES))
544 {
545 GNUNET_free (execpath);
546 return tmp;
547 }
548 GNUNET_free (tmp);
549 tmp = NULL;
550 dirname = NULL;
551 if (4 == sizeof(void *))
552 {
553 GNUNET_asprintf (&dirname,
554 DIR_SEPARATOR_STR "lib32" DIR_SEPARATOR_STR
555 "%s" DIR_SEPARATOR_STR,
556 current_pd->project_dirname);
557 GNUNET_asprintf (&tmp, "%s%s", execpath, dirname);
558 }
559 if (8 == sizeof(void *))
560 {
561 GNUNET_asprintf (&dirname,
562 DIR_SEPARATOR_STR "lib64" DIR_SEPARATOR_STR
563 "%s" DIR_SEPARATOR_STR,
564 current_pd->project_dirname);
565 GNUNET_asprintf (&tmp, "%s%s", execpath, dirname);
566 }
567
568 if ((NULL != tmp) &&
569 (GNUNET_YES == GNUNET_DISK_directory_test (tmp, GNUNET_YES)))
570 {
571 GNUNET_free (execpath);
572 GNUNET_free (dirname);
573 return tmp;
574 }
575 GNUNET_free (tmp);
576 GNUNET_free (dirname);
577 }
578 GNUNET_asprintf (&dirname,
579 DIR_SEPARATOR_STR "%s" DIR_SEPARATOR_STR,
580 current_pd->project_dirname);
581 break;
582
583 case GNUNET_OS_IPK_DATADIR:
584 GNUNET_asprintf (&dirname,
585 DIR_SEPARATOR_STR "share" DIR_SEPARATOR_STR
586 "%s" DIR_SEPARATOR_STR,
587 current_pd->project_dirname);
588 break;
589
590 case GNUNET_OS_IPK_LOCALEDIR:
591 dirname = GNUNET_strdup (DIR_SEPARATOR_STR "share" DIR_SEPARATOR_STR
592 "locale" DIR_SEPARATOR_STR);
593 break;
594
595 case GNUNET_OS_IPK_ICONDIR:
596 dirname = GNUNET_strdup (DIR_SEPARATOR_STR "share" DIR_SEPARATOR_STR
597 "icons" DIR_SEPARATOR_STR);
598 break;
599
600 case GNUNET_OS_IPK_DOCDIR:
601 GNUNET_asprintf (&dirname,
602 DIR_SEPARATOR_STR "share" DIR_SEPARATOR_STR
603 "doc" DIR_SEPARATOR_STR
604 "%s" DIR_SEPARATOR_STR,
605 current_pd->project_dirname);
606 break;
607
608 case GNUNET_OS_IPK_LIBEXECDIR:
609 if (isbasedir)
610 {
611 GNUNET_asprintf (&dirname,
612 DIR_SEPARATOR_STR "%s" DIR_SEPARATOR_STR
613 "libexec" DIR_SEPARATOR_STR,
614 current_pd->project_dirname);
615 GNUNET_asprintf (&tmp,
616 "%s%s%s%s",
617 execpath,
618 DIR_SEPARATOR_STR "lib" DIR_SEPARATOR_STR,
619 (NULL != multiarch) ? multiarch : "",
620 dirname);
621 GNUNET_free (dirname);
622 if (GNUNET_YES == GNUNET_DISK_directory_test (tmp, GNUNET_YES))
623 {
624 GNUNET_free (execpath);
625 return tmp;
626 }
627 GNUNET_free (tmp);
628 tmp = NULL;
629 dirname = NULL;
630 if (4 == sizeof(void *))
631 {
632 GNUNET_asprintf (&dirname,
633 DIR_SEPARATOR_STR "lib32" DIR_SEPARATOR_STR
634 "%s" DIR_SEPARATOR_STR
635 "libexec" DIR_SEPARATOR_STR,
636 current_pd->project_dirname);
637 GNUNET_asprintf (&tmp, "%s%s", execpath, dirname);
638 }
639 if (8 == sizeof(void *))
640 {
641 GNUNET_asprintf (&dirname,
642 DIR_SEPARATOR_STR "lib64" DIR_SEPARATOR_STR
643 "%s" DIR_SEPARATOR_STR
644 "libexec" DIR_SEPARATOR_STR,
645 current_pd->project_dirname);
646 GNUNET_asprintf (&tmp, "%s%s", execpath, dirname);
647 }
648 if ((NULL != tmp) &&
649 (GNUNET_YES == GNUNET_DISK_directory_test (tmp, GNUNET_YES)))
650 {
651 GNUNET_free (execpath);
652 GNUNET_free (dirname);
653 return tmp;
654 }
655 GNUNET_free (tmp);
656 GNUNET_free (dirname);
657 }
658 GNUNET_asprintf (&dirname,
659 DIR_SEPARATOR_STR "%s" DIR_SEPARATOR_STR
660 "libexec" DIR_SEPARATOR_STR,
661 current_pd->project_dirname);
662 break;
663
664 default:
665 GNUNET_free (execpath);
666 return NULL;
667 }
668 GNUNET_asprintf (&tmp, "%s%s", execpath, dirname);
669 GNUNET_free (dirname);
670 GNUNET_free (execpath);
671 return tmp;
672}
673
674
675/**
676 * Given the name of a gnunet-helper, gnunet-service or gnunet-daemon
677 * binary, try to prefix it with the libexec/-directory to get the
678 * full path.
679 *
680 * @param progname name of the binary
681 * @return full path to the binary, if possible, otherwise copy of 'progname'
682 */
683char *
684GNUNET_OS_get_libexec_binary_path (const char *progname)
685{
686 static char *cache;
687 char *libexecdir;
688 char *binary;
689
690 if ((DIR_SEPARATOR == progname[0]) ||
691 (GNUNET_YES ==
692 GNUNET_STRINGS_path_is_absolute (progname, GNUNET_NO, NULL, NULL)))
693 return GNUNET_strdup (progname);
694 if (NULL != cache)
695 libexecdir = cache;
696 else
697 libexecdir = GNUNET_OS_installation_get_path (GNUNET_OS_IPK_LIBEXECDIR);
698 if (NULL == libexecdir)
699 return GNUNET_strdup (progname);
700 GNUNET_asprintf (&binary, "%s%s", libexecdir, progname);
701 cache = libexecdir;
702 return binary;
703}
704
705
706/**
707 * Given the name of a helper, service or daemon binary construct the full
708 * path to the binary using the SUID_BINARY_PATH in the PATHS section of the
709 * configuration. If that option is not present, fall back to
710 * GNUNET_OS_get_libexec_binary_path. If @a progname is an absolute path, a
711 * copy of this path is returned.
712 *
713 * @param cfg configuration to inspect
714 * @param progname name of the binary
715 * @return full path to the binary, if possible, a copy of @a progname
716 * otherwise
717 */
718char *
719GNUNET_OS_get_suid_binary_path (const struct GNUNET_CONFIGURATION_Handle *cfg,
720 const char *progname)
721{
722 static char *cache;
723 char *binary = NULL;
724 char *path = NULL;
725 size_t path_len;
726
727 if (GNUNET_YES ==
728 GNUNET_STRINGS_path_is_absolute (progname, GNUNET_NO, NULL, NULL))
729 {
730 return GNUNET_strdup (progname);
731 }
732 if (NULL != cache)
733 path = cache;
734 else
735 GNUNET_CONFIGURATION_get_value_string (cfg,
736 "PATHS",
737 "SUID_BINARY_PATH",
738 &path);
739 if ((NULL == path) || (0 == strlen (path)))
740 {
741 if (NULL != path)
742 GNUNET_free (path);
743 cache = NULL;
744 return GNUNET_OS_get_libexec_binary_path (progname);
745 }
746 path_len = strlen (path);
747 GNUNET_asprintf (&binary,
748 "%s%s%s",
749 path,
750 (path[path_len - 1] == DIR_SEPARATOR) ? ""
751 : DIR_SEPARATOR_STR,
752 progname);
753 cache = path;
754 return binary;
755}
756
757
758enum GNUNET_GenericReturnValue
759GNUNET_OS_check_helper_binary (const char *binary,
760 bool check_suid,
761 const char *params)
762{
763 struct stat statbuf;
764 char *p;
765 char *pf;
766
767 if ((GNUNET_YES ==
768 GNUNET_STRINGS_path_is_absolute (binary, GNUNET_NO, NULL, NULL)) ||
769 (0 == strncmp (binary, "./", 2)))
770 {
771 p = GNUNET_strdup (binary);
772 }
773 else
774 {
775 p = get_path_from_PATH (binary);
776 if (NULL != p)
777 {
778 GNUNET_asprintf (&pf, "%s/%s", p, binary);
779 GNUNET_free (p);
780 p = pf;
781 }
782 }
783
784 if (NULL == p)
785 {
786 LOG (GNUNET_ERROR_TYPE_INFO,
787 _ ("Could not find binary `%s' in PATH!\n"),
788 binary);
789 return GNUNET_SYSERR;
790 }
791 if (0 != access (p, X_OK))
792 {
793 LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_WARNING, "access", p);
794 GNUNET_free (p);
795 return GNUNET_SYSERR;
796 }
797
798 if (0 == getuid ())
799 {
800 /* as we run as root, we don't insist on SUID */
801 GNUNET_free (p);
802 return GNUNET_YES;
803 }
804
805 if (0 != stat (p, &statbuf))
806 {
807 LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_WARNING, "stat", p);
808 GNUNET_free (p);
809 return GNUNET_SYSERR;
810 }
811 if (check_suid)
812 {
813 (void) params;
814 if ((0 != (statbuf.st_mode & S_ISUID)) && (0 == statbuf.st_uid))
815 {
816 GNUNET_free (p);
817 return GNUNET_YES;
818 }
819 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
820 _ ("Binary `%s' exists, but is not SUID\n"),
821 p);
822 /* binary exists, but not SUID */
823 }
824 GNUNET_free (p);
825 return GNUNET_NO;
826}
827
828
829/* end of os_installation.c */
diff --git a/src/lib/util/os_network.c b/src/lib/util/os_network.c
new file mode 100644
index 000000000..ac9fa691f
--- /dev/null
+++ b/src/lib/util/os_network.c
@@ -0,0 +1,444 @@
1/*
2 This file is part of GNUnet.
3 Copyright (C) 2004, 2005, 2006, 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/**
22 * @file util/os_network.c
23 * @brief function to determine available network interfaces
24 * @author Nils Durner
25 * @author Heikki Lindholm
26 * @author Jake Dust
27 * @author LRN
28 * @author Christian Grothoff
29 */
30
31#include "platform.h"
32#include "gnunet_util_lib.h"
33
34
35#define LOG(kind, ...) GNUNET_log_from (kind, "util-os-network", __VA_ARGS__)
36#define LOG_STRERROR_FILE(kind, syscall, \
37 filename) GNUNET_log_from_strerror_file (kind, \
38 "util-os-network", \
39 syscall, \
40 filename)
41
42
43#if ! (HAVE_GETIFADDRS && HAVE_FREEIFADDRS)
44/**
45 * Try to enumerate all network interfaces using 'ifconfig'.
46 *
47 * @param proc the callback function
48 * @param proc_cls closure for @a proc
49 * @return #GNUNET_OK if it worked
50 */
51static int
52try_ifconfig (GNUNET_OS_NetworkInterfaceProcessor proc,
53 void *proc_cls)
54{
55 int i;
56 char line[1024];
57 char *replace;
58 const char *start;
59 char ifc[12];
60 char addrstr[128];
61 char bcstr[128];
62 char netmaskstr[128];
63 FILE *f;
64 int have_ifc;
65 struct sockaddr_in a4;
66 struct sockaddr_in6 a6;
67 struct in_addr v4;
68 struct in6_addr v6;
69 struct sockaddr_in bcaddr;
70 struct sockaddr_in netmask;
71 struct sockaddr_in6 netmask6;
72 struct sockaddr *pass_bcaddr;
73 struct sockaddr *pass_netmask;
74 int prefixlen;
75 static char *pcall;
76
77 if (NULL == pcall)
78 {
79 const char *sbin_ifconfig;
80
81#ifdef IFCONFIG
82 if (0 == access (IFCONFIG, X_OK))
83 sbin_ifconfig = IFCONFIG;
84 else
85#endif
86 if (0 == access ("/sbin/ifconfig", X_OK))
87 sbin_ifconfig = "/sbin/ifconfig";
88 else if (0 == access ("/usr/sbin/ifconfig", X_OK))
89 sbin_ifconfig = "/usr/sbin/ifconfig";
90 else
91 sbin_ifconfig = "ifconfig";
92 GNUNET_asprintf (&pcall,
93 "%s -a 2> /dev/null",
94 sbin_ifconfig);
95 }
96 f = popen (pcall, "r");
97 if (NULL == f)
98 {
99 LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_WARNING | GNUNET_ERROR_TYPE_BULK,
100 "popen",
101 "ifconfig");
102
103 return GNUNET_SYSERR;
104 }
105
106 have_ifc = GNUNET_NO;
107 ifc[11] = '\0';
108 while (NULL != fgets (line, sizeof(line), f))
109 {
110 if (strlen (line) == 0)
111 {
112 have_ifc = GNUNET_NO;
113 continue;
114 }
115 if (! isspace (line[0]))
116 {
117 have_ifc = (1 == sscanf (line, "%11s", ifc)) ? GNUNET_YES : GNUNET_NO;
118 /* would end with ':' on OSX, fix it! */
119 if (ifc[strlen (ifc) - 1] == ':')
120 ifc[strlen (ifc) - 1] = '\0';
121 continue;
122 }
123 if (! have_ifc)
124 continue; /* strange input, hope for the best */
125
126 /* make parsing of ipv6 addresses easier */
127 for (replace = line; *replace != '\0'; replace++)
128 {
129 if (*replace == '/')
130 *replace = ' ';
131 }
132 prefixlen = -1;
133
134 start = line;
135 while (('\0' != *start) && (isspace (*start)))
136 start++;
137
138 /* Zero-out stack allocated values */
139 memset (addrstr, 0, 128);
140 memset (netmaskstr, 0, 128);
141 memset (bcstr, 0, 128);
142 prefixlen = 0;
143
144 if ( /* Linux */
145 (3 == sscanf (start, "inet addr:%127s Bcast:%127s Mask:%127s", addrstr,
146 bcstr, netmaskstr)) ||
147 (2 == sscanf (start, "inet addr:%127s Mask:%127s", addrstr,
148 netmaskstr)) ||
149 (2 == sscanf (start, "inet6 addr:%127s %d", addrstr, &prefixlen)) ||
150 /* Solaris, OS X */
151 (1 == sscanf (start, "inet %127s", addrstr)) ||
152 (1 == sscanf (start, "inet6 %127s", addrstr)))
153 {
154 /* IPv4 */
155 if (1 == inet_pton (AF_INET, addrstr, &v4))
156 {
157 memset (&a4, 0, sizeof(a4));
158 a4.sin_family = AF_INET;
159#if HAVE_SOCKADDR_IN_SIN_LEN
160 a4.sin_len = (u_char) sizeof(struct sockaddr_in);
161#endif
162 a4.sin_addr = v4;
163
164 pass_bcaddr = NULL;
165 pass_netmask = NULL;
166 if (1 == inet_pton (AF_INET, bcstr, &v4))
167 {
168 memset (&bcaddr, 0, sizeof(bcaddr));
169 bcaddr.sin_family = AF_INET;
170#if HAVE_SOCKADDR_IN_SIN_LEN
171 bcaddr.sin_len = (u_char) sizeof(struct sockaddr_in);
172#endif
173 bcaddr.sin_addr = v4;
174 pass_bcaddr = (struct sockaddr *) &bcaddr;
175 }
176 if (1 == inet_pton (AF_INET, netmaskstr, &v4))
177 {
178 memset (&netmask, 0, sizeof(netmask));
179 netmask.sin_family = AF_INET;
180#if HAVE_SOCKADDR_IN_SIN_LEN
181 netmask.sin_len = (u_char) sizeof(struct sockaddr_in);
182#endif
183 netmask.sin_addr = v4;
184 pass_netmask = (struct sockaddr *) &netmask;
185 }
186
187
188 if (GNUNET_OK !=
189 proc (proc_cls, ifc,(0 == strcmp (ifc, GNUNET_DEFAULT_INTERFACE)),
190 (const struct sockaddr *) &a4,
191 pass_bcaddr, pass_netmask, sizeof(a4)))
192 break;
193 continue;
194 }
195 /* IPv6 */
196 if (1 == inet_pton (AF_INET6, addrstr, &v6))
197 {
198 memset (&a6, 0, sizeof(a6));
199 a6.sin6_family = AF_INET6;
200#if HAVE_SOCKADDR_IN_SIN_LEN
201 a6.sin6_len = (u_char) sizeof(struct sockaddr_in6);
202#endif
203 a6.sin6_addr = v6;
204
205 pass_netmask = NULL;
206 if (prefixlen != -1)
207 {
208 memset (v6.s6_addr, 0, sizeof(v6.s6_addr));
209 for (i = 0; i < prefixlen; i++)
210 {
211 v6.s6_addr[i >> 3] |= 1 << (i & 7);
212 }
213 memset (&netmask6, 0, sizeof(netmask6));
214 netmask6.sin6_family = AF_INET6;
215#if HAVE_SOCKADDR_IN_SIN_LEN
216 netmask6.sin6_len = (u_char) sizeof(struct sockaddr_in6);
217#endif
218 netmask6.sin6_addr = v6;
219
220 pass_netmask = (struct sockaddr *) &netmask6;
221 }
222
223 if (GNUNET_OK !=
224 proc (proc_cls, ifc,(0 == strcmp (ifc, GNUNET_DEFAULT_INTERFACE)),
225 (const struct sockaddr *) &a6,
226 NULL, pass_netmask, sizeof(a6)))
227 break;
228 continue;
229 }
230 }
231 }
232 pclose (f);
233 return GNUNET_OK;
234}
235
236
237/**
238 * Try to enumerate all network interfaces using 'ip'.
239 *
240 * @param proc the callback function
241 * @param proc_cls closure for @a proc
242 * @return #GNUNET_OK if it worked
243 */
244static int
245try_ip (GNUNET_OS_NetworkInterfaceProcessor proc,
246 void *proc_cls)
247{
248 char line[1024];
249 char *replace;
250 char ifname[64];
251 char afstr[6];
252 char addrstr[128];
253 FILE *f;
254 struct sockaddr_in a4;
255 struct sockaddr_in6 a6;
256 struct in_addr v4;
257 struct in6_addr v6;
258 struct sockaddr_in netmask;
259 struct sockaddr_in6 netmask6;
260 unsigned int i;
261 unsigned int prefixlen;
262 static char *pcall;
263
264 if (NULL == pcall)
265 {
266 const char *sbin_ip;
267
268#ifdef PATH_TO_IP
269 if (0 == access (PATH_TO_IP, X_OK))
270 sbin_ip = PATH_TO_IP;
271 else
272#endif
273 if (0 == access ("/sbin/ip", X_OK))
274 sbin_ip = "/sbin/ip";
275 else if (0 == access ("/usr/sbin/ip", X_OK))
276 sbin_ip = "/usr/sbin/ip";
277 else
278 sbin_ip = "if";
279 GNUNET_asprintf (&pcall,
280 "%s -o add 2> /dev/null",
281 sbin_ip);
282 }
283 f = popen (pcall, "r");
284 if (! f)
285 {
286 LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_WARNING | GNUNET_ERROR_TYPE_BULK,
287 "popen",
288 "ip");
289 return GNUNET_SYSERR;
290 }
291
292 while (NULL != fgets (line, sizeof(line), f))
293 {
294 /* make parsing easier */
295 for (replace = line; *replace != '\0'; replace++)
296 {
297 if (*replace == '/')
298 *replace = ' ';
299 }
300 /* Zero-out stack allocated values */
301 memset (ifname, 0, 64);
302 memset (afstr, 0, 6);
303 memset (addrstr, 0, 128);
304 if (4 != sscanf (line,
305 "%*u: %63s %5s %127s %6u",
306 ifname,
307 afstr,
308 addrstr,
309 &prefixlen))
310 continue;
311 /* IPv4 */
312 if ((0 == strcasecmp ("inet",
313 afstr)) &&
314 (1 == inet_pton (AF_INET,
315 addrstr,
316 &v4)))
317 {
318 memset (&a4, 0, sizeof(a4));
319 a4.sin_family = AF_INET;
320#if HAVE_SOCKADDR_IN_SIN_LEN
321 a4.sin_len = (u_char) sizeof(struct sockaddr_in);
322#endif
323 a4.sin_addr = v4;
324
325 memset (&v4.s_addr, 0, sizeof(v4.s_addr));
326 for (i = 0; i < prefixlen; i++)
327 v4.s_addr |= 1 << (i & 7);
328 memset (&netmask, 0, sizeof(netmask));
329 netmask.sin_family = AF_INET;
330#if HAVE_SOCKADDR_IN_SIN_LEN
331 netmask.sin_len = (u_char) sizeof(struct sockaddr_in);
332#endif
333 netmask.sin_addr = v4;
334
335 if (GNUNET_OK !=
336 proc (proc_cls,
337 ifname,
338 (0 == strcmp (ifname,
339 GNUNET_DEFAULT_INTERFACE)),
340 (const struct sockaddr *) &a4,
341 NULL,
342 (const struct sockaddr *) &netmask,
343 sizeof(a4)))
344 break;
345 }
346 /* IPv6 */
347 if ((0 == strcasecmp ("inet6",
348 afstr)) &&
349 (1 == inet_pton (AF_INET6,
350 addrstr,
351 &v6)))
352 {
353 memset (&a6, 0, sizeof(a6));
354 a6.sin6_family = AF_INET6;
355#if HAVE_SOCKADDR_IN_SIN_LEN
356 a6.sin6_len = (u_char) sizeof(struct sockaddr_in6);
357#endif
358 a6.sin6_addr = v6;
359
360 memset (v6.s6_addr, 0, sizeof(v6.s6_addr));
361 for (i = 0; i < prefixlen; i++)
362 v6.s6_addr[i >> 3] |= 1 << (i & 7);
363 memset (&netmask6, 0, sizeof(netmask6));
364 netmask6.sin6_family = AF_INET6;
365#if HAVE_SOCKADDR_IN_SIN_LEN
366 netmask6.sin6_len = (u_char) sizeof(struct sockaddr_in6);
367#endif
368 netmask6.sin6_addr = v6;
369
370 if (GNUNET_OK !=
371 proc (proc_cls,
372 ifname,
373 (0 == strcmp (ifname,
374 GNUNET_DEFAULT_INTERFACE)),
375 (const struct sockaddr *) &a6,
376 NULL,
377 (const struct sockaddr *) &netmask6,
378 sizeof(a6)))
379 break;
380 }
381 }
382 pclose (f);
383 return GNUNET_OK;
384}
385
386
387#endif
388
389
390/**
391 * @brief Enumerate all network interfaces
392 *
393 * @param proc the callback function
394 * @param proc_cls closure for @a proc
395 */
396void
397GNUNET_OS_network_interfaces_list (GNUNET_OS_NetworkInterfaceProcessor proc,
398 void *proc_cls)
399{
400#if HAVE_GETIFADDRS && HAVE_FREEIFADDRS
401 struct ifaddrs *ifa_first;
402 struct ifaddrs *ifa_ptr;
403 socklen_t alen;
404
405 if (getifaddrs (&ifa_first) == 0)
406 {
407 for (ifa_ptr = ifa_first; ifa_ptr != NULL; ifa_ptr = ifa_ptr->ifa_next)
408 {
409 if ((ifa_ptr->ifa_name != NULL) && (ifa_ptr->ifa_addr != NULL) &&
410 ( (ifa_ptr->ifa_flags & IFF_UP) != 0) )
411 {
412 if ((ifa_ptr->ifa_addr->sa_family != AF_INET) &&
413 (ifa_ptr->ifa_addr->sa_family != AF_INET6))
414 continue;
415 if (ifa_ptr->ifa_addr->sa_family == AF_INET)
416 alen = sizeof(struct sockaddr_in);
417 else
418 alen = sizeof(struct sockaddr_in6);
419 if (GNUNET_OK !=
420 proc (proc_cls, ifa_ptr->ifa_name,
421 (0 == strcmp (ifa_ptr->ifa_name, GNUNET_DEFAULT_INTERFACE)),
422 ifa_ptr->ifa_addr, ifa_ptr->ifa_broadaddr,
423 ifa_ptr->ifa_netmask, alen))
424 break;
425 }
426 }
427 freeifaddrs (ifa_first);
428 }
429#else
430 if (GNUNET_OK ==
431 try_ip (proc,
432 proc_cls))
433 return;
434 if (GNUNET_OK ==
435 try_ifconfig (proc,
436 proc_cls))
437 return;
438 LOG (GNUNET_ERROR_TYPE_WARNING | GNUNET_ERROR_TYPE_BULK,
439 "Failed to enumerate network interfaces\n");
440#endif
441}
442
443
444/* end of os_network.c */
diff --git a/src/lib/util/os_priority.c b/src/lib/util/os_priority.c
new file mode 100644
index 000000000..63d18dbe4
--- /dev/null
+++ b/src/lib/util/os_priority.c
@@ -0,0 +1,1063 @@
1/*
2 This file is part of GNUnet
3 Copyright (C) 2002, 2003, 2004, 2005, 2006, 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/**
22 * @file util/os_priority.c
23 * @brief Methods to set process priority
24 * @author Nils Durner
25 */
26
27
28#include "platform.h"
29#include "gnunet_util_lib.h"
30#include "disk.h"
31#include <unistr.h>
32
33#define LOG(kind, ...) GNUNET_log_from (kind, "util-os-priority", __VA_ARGS__)
34
35#define LOG_STRERROR(kind, syscall) \
36 GNUNET_log_from_strerror (kind, "util-os-priority", syscall)
37
38#define LOG_STRERROR_FILE(kind, syscall, filename) \
39 GNUNET_log_from_strerror_file (kind, "util-os-priority", syscall, filename)
40
41#define GNUNET_OS_CONTROL_PIPE "GNUNET_OS_CONTROL_PIPE"
42
43
44struct GNUNET_OS_Process
45{
46 /**
47 * PID of the process.
48 */
49 pid_t pid;
50
51 /**
52 * Pipe we use to signal the process.
53 * NULL if unused, or if process was deemed uncontrollable.
54 */
55 struct GNUNET_DISK_FileHandle *control_pipe;
56};
57
58
59/**
60 * Handle for 'this' process.
61 */
62static struct GNUNET_OS_Process current_process;
63
64/**
65 * Handle for the #parent_control_handler() Task.
66 */
67static struct GNUNET_SCHEDULER_Task *pch;
68
69/**
70 * Handle for the #shutdown_pch() Task.
71 */
72static struct GNUNET_SCHEDULER_Task *spch;
73
74
75/**
76 * This handler is called on shutdown to remove the #pch.
77 *
78 * @param cls the `struct GNUNET_DISK_FileHandle` of the control pipe
79 */
80static void
81shutdown_pch (void *cls)
82{
83 struct GNUNET_DISK_FileHandle *control_pipe = cls;
84
85 GNUNET_SCHEDULER_cancel (pch);
86 pch = NULL;
87 GNUNET_DISK_file_close (control_pipe);
88 control_pipe = NULL;
89}
90
91
92/**
93 * This handler is called when there are control data to be read on the pipe
94 *
95 * @param cls the `struct GNUNET_DISK_FileHandle` of the control pipe
96 */
97static void
98parent_control_handler (void *cls)
99{
100 struct GNUNET_DISK_FileHandle *control_pipe = cls;
101 char sig;
102 char *pipe_fd;
103 ssize_t ret;
104
105 pch = NULL;
106 ret = GNUNET_DISK_file_read (control_pipe, &sig, sizeof(sig));
107 if (sizeof(sig) != ret)
108 {
109 if (-1 == ret)
110 LOG_STRERROR (GNUNET_ERROR_TYPE_ERROR, "GNUNET_DISK_file_read");
111 LOG (GNUNET_ERROR_TYPE_DEBUG, "Closing control pipe\n");
112 GNUNET_DISK_file_close (control_pipe);
113 control_pipe = NULL;
114 GNUNET_SCHEDULER_cancel (spch);
115 spch = NULL;
116 return;
117 }
118 pipe_fd = getenv (GNUNET_OS_CONTROL_PIPE);
119 GNUNET_assert ((NULL == pipe_fd) || (strlen (pipe_fd) <= 0));
120 LOG (GNUNET_ERROR_TYPE_DEBUG,
121 "Got control code %d from parent via pipe %s\n",
122 sig,
123 pipe_fd);
124 pch = GNUNET_SCHEDULER_add_read_file (GNUNET_TIME_UNIT_FOREVER_REL,
125 control_pipe,
126 &parent_control_handler,
127 control_pipe);
128 GNUNET_SIGNAL_raise ((int) sig);
129}
130
131
132void
133GNUNET_OS_install_parent_control_handler (void *cls)
134{
135 const char *env_buf;
136 char *env_buf_end;
137 struct GNUNET_DISK_FileHandle *control_pipe;
138 uint64_t pipe_fd;
139
140 (void) cls;
141 if (NULL != pch)
142 {
143 /* already done, we've been called twice... */
144 GNUNET_break (0);
145 return;
146 }
147 env_buf = getenv (GNUNET_OS_CONTROL_PIPE);
148 if ((NULL == env_buf) || (strlen (env_buf) <= 0))
149 {
150 LOG (GNUNET_ERROR_TYPE_DEBUG,
151 "Not installing a handler because $%s is empty\n",
152 GNUNET_OS_CONTROL_PIPE);
153 putenv (GNUNET_OS_CONTROL_PIPE "=");
154 return;
155 }
156 errno = 0;
157 pipe_fd = strtoull (env_buf, &env_buf_end, 16);
158 if ((0 != errno) || (env_buf == env_buf_end))
159 {
160 LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_WARNING, "strtoull", env_buf);
161 putenv (GNUNET_OS_CONTROL_PIPE "=");
162 return;
163 }
164 if (pipe_fd >= FD_SETSIZE)
165 {
166 LOG (GNUNET_ERROR_TYPE_ERROR,
167 "GNUNET_OS_CONTROL_PIPE `%s' contains garbage?\n",
168 env_buf);
169 putenv (GNUNET_OS_CONTROL_PIPE "=");
170 return;
171 }
172
173 control_pipe = GNUNET_DISK_get_handle_from_int_fd ((int) pipe_fd);
174
175 if (NULL == control_pipe)
176 {
177 LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_WARNING, "open", env_buf);
178 putenv (GNUNET_OS_CONTROL_PIPE "=");
179 return;
180 }
181 LOG (GNUNET_ERROR_TYPE_DEBUG,
182 "Adding parent control handler pipe `%s' to the scheduler\n",
183 env_buf);
184 pch = GNUNET_SCHEDULER_add_read_file (GNUNET_TIME_UNIT_FOREVER_REL,
185 control_pipe,
186 &parent_control_handler,
187 control_pipe);
188 spch = GNUNET_SCHEDULER_add_shutdown (&shutdown_pch, control_pipe);
189 putenv (GNUNET_OS_CONTROL_PIPE "=");
190}
191
192
193/**
194 * Get process structure for current process
195 *
196 * The pointer it returns points to static memory location and must
197 * not be deallocated/closed.
198 *
199 * @return pointer to the process sturcutre for this process
200 */
201struct GNUNET_OS_Process *
202GNUNET_OS_process_current ()
203{
204 current_process.pid = 0;
205 return &current_process;
206}
207
208
209int
210GNUNET_OS_process_kill (struct GNUNET_OS_Process *proc,
211 int sig)
212{
213 int ret;
214 char csig;
215
216 csig = (char) sig;
217 if (NULL != proc->control_pipe)
218 {
219 LOG (GNUNET_ERROR_TYPE_DEBUG,
220 "Sending signal %d to pid: %u via pipe\n",
221 sig,
222 proc->pid);
223 ret = GNUNET_DISK_file_write (proc->control_pipe, &csig, sizeof(csig));
224 if (sizeof(csig) == ret)
225 return 0;
226 }
227 /* pipe failed or non-existent, try other methods */
228 switch (sig)
229 {
230 case SIGHUP:
231 case SIGINT:
232 case SIGKILL:
233 case SIGTERM:
234#if (SIGTERM != GNUNET_TERM_SIG)
235 case GNUNET_TERM_SIG:
236#endif
237 LOG (GNUNET_ERROR_TYPE_DEBUG,
238 "Sending signal %d to pid: %u via system call\n",
239 sig,
240 proc->pid);
241 return kill (proc->pid, sig);
242 default:
243 LOG (GNUNET_ERROR_TYPE_DEBUG,
244 "Sending signal %d to pid: %u via system call\n",
245 sig,
246 proc->pid);
247 return kill (proc->pid, sig);
248 }
249}
250
251
252pid_t
253GNUNET_OS_process_get_pid (struct GNUNET_OS_Process *proc)
254{
255 return proc->pid;
256}
257
258
259void
260GNUNET_OS_process_destroy (struct GNUNET_OS_Process *proc)
261{
262 if (NULL != proc->control_pipe)
263 GNUNET_DISK_file_close (proc->control_pipe);
264
265 GNUNET_free (proc);
266}
267
268
269/**
270 * Open '/dev/null' and make the result the given
271 * file descriptor.
272 *
273 * @param target_fd desired FD to point to /dev/null
274 * @param flags open flags (O_RDONLY, O_WRONLY)
275 */
276static void
277open_dev_null (int target_fd,
278 int flags)
279{
280 int fd;
281
282 fd = open ("/dev/null", flags);
283 if (-1 == fd)
284 {
285 GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_ERROR, "open", "/dev/null");
286 return;
287 }
288 if (fd == target_fd)
289 return;
290 if (-1 == dup2 (fd, target_fd))
291 {
292 GNUNET_log_strerror (GNUNET_ERROR_TYPE_ERROR, "dup2");
293 GNUNET_break (0 == close (fd));
294 return;
295 }
296 GNUNET_break (0 == close (fd));
297}
298
299
300/**
301 * Start a process.
302 *
303 * @param std_inheritance a set of GNUNET_OS_INHERIT_STD_* flags controlling which
304 * std handles of the parent are inherited by the child.
305 * pipe_stdin and pipe_stdout take priority over std_inheritance
306 * (when they are non-NULL).
307 * @param pipe_stdin pipe to use to send input to child process (or NULL)
308 * @param pipe_stdout pipe to use to get output from child process (or NULL)
309 * @param pipe_stderr pipe to use for stderr for child process (or NULL)
310 * @param lsocks array of listen sockets to dup systemd-style (or NULL);
311 * must be NULL on platforms where dup is not supported
312 * @param filename name of the binary
313 * @param argv NULL-terminated list of arguments to the process
314 * @return process ID of the new process, -1 on error
315 */
316static struct GNUNET_OS_Process *
317start_process (enum GNUNET_OS_InheritStdioFlags std_inheritance,
318 struct GNUNET_DISK_PipeHandle *pipe_stdin,
319 struct GNUNET_DISK_PipeHandle *pipe_stdout,
320 struct GNUNET_DISK_PipeHandle *pipe_stderr,
321 const int *lsocks,
322 const char *filename,
323 char *const argv[])
324{
325 pid_t ret;
326 char fds[16];
327 struct GNUNET_OS_Process *gnunet_proc;
328 struct GNUNET_DISK_FileHandle *childpipe_read;
329 struct GNUNET_DISK_FileHandle *childpipe_write;
330 int childpipe_read_fd;
331 int i;
332 int j;
333 int k;
334 int tgt;
335 int flags;
336 int *lscp;
337 unsigned int ls;
338 int fd_stdout_write;
339 int fd_stdout_read;
340 int fd_stderr_write;
341 int fd_stderr_read;
342 int fd_stdin_read;
343 int fd_stdin_write;
344
345 if (GNUNET_SYSERR ==
346 GNUNET_OS_check_helper_binary (filename, GNUNET_NO, NULL))
347 return NULL; /* not executable */
348 if (0 != (std_inheritance & GNUNET_OS_USE_PIPE_CONTROL))
349 {
350 struct GNUNET_DISK_PipeHandle *childpipe;
351 int dup_childpipe_read_fd = -1;
352
353 childpipe = GNUNET_DISK_pipe (GNUNET_DISK_PF_NONE);
354 if (NULL == childpipe)
355 return NULL;
356 childpipe_read =
357 GNUNET_DISK_pipe_detach_end (childpipe, GNUNET_DISK_PIPE_END_READ);
358 childpipe_write =
359 GNUNET_DISK_pipe_detach_end (childpipe, GNUNET_DISK_PIPE_END_WRITE);
360 GNUNET_DISK_pipe_close (childpipe);
361 if ((NULL == childpipe_read) || (NULL == childpipe_write) ||
362 (GNUNET_OK != GNUNET_DISK_internal_file_handle_ (childpipe_read,
363 &childpipe_read_fd,
364 sizeof(int))) ||
365 (-1 == (dup_childpipe_read_fd = dup (childpipe_read_fd))))
366 {
367 if (NULL != childpipe_read)
368 GNUNET_DISK_file_close (childpipe_read);
369 if (NULL != childpipe_write)
370 GNUNET_DISK_file_close (childpipe_write);
371 if (0 <= dup_childpipe_read_fd)
372 GNUNET_break (0 == close (dup_childpipe_read_fd));
373 return NULL;
374 }
375 childpipe_read_fd = dup_childpipe_read_fd;
376 GNUNET_DISK_file_close (childpipe_read);
377 }
378 else
379 {
380 childpipe_write = NULL;
381 childpipe_read_fd = -1;
382 }
383 if (NULL != pipe_stdin)
384 {
385 GNUNET_assert (
386 GNUNET_OK ==
387 GNUNET_DISK_internal_file_handle_ (
388 GNUNET_DISK_pipe_handle (pipe_stdin, GNUNET_DISK_PIPE_END_READ),
389 &fd_stdin_read,
390 sizeof(int)));
391 GNUNET_assert (
392 GNUNET_OK ==
393 GNUNET_DISK_internal_file_handle_ (
394 GNUNET_DISK_pipe_handle (pipe_stdin, GNUNET_DISK_PIPE_END_WRITE),
395 &fd_stdin_write,
396 sizeof(int)));
397 }
398 if (NULL != pipe_stdout)
399 {
400 GNUNET_assert (
401 GNUNET_OK ==
402 GNUNET_DISK_internal_file_handle_ (
403 GNUNET_DISK_pipe_handle (pipe_stdout, GNUNET_DISK_PIPE_END_WRITE),
404 &fd_stdout_write,
405 sizeof(int)));
406 GNUNET_assert (
407 GNUNET_OK ==
408 GNUNET_DISK_internal_file_handle_ (
409 GNUNET_DISK_pipe_handle (pipe_stdout, GNUNET_DISK_PIPE_END_READ),
410 &fd_stdout_read,
411 sizeof(int)));
412 }
413 if (NULL != pipe_stderr)
414 {
415 GNUNET_assert (
416 GNUNET_OK ==
417 GNUNET_DISK_internal_file_handle_ (
418 GNUNET_DISK_pipe_handle (pipe_stderr, GNUNET_DISK_PIPE_END_READ),
419 &fd_stderr_read,
420 sizeof(int)));
421 GNUNET_assert (
422 GNUNET_OK ==
423 GNUNET_DISK_internal_file_handle_ (
424 GNUNET_DISK_pipe_handle (pipe_stderr, GNUNET_DISK_PIPE_END_WRITE),
425 &fd_stderr_write,
426 sizeof(int)));
427 }
428 lscp = NULL;
429 ls = 0;
430 if (NULL != lsocks)
431 {
432 i = 0;
433 while (-1 != (k = lsocks[i++]))
434 GNUNET_array_append (lscp, ls, k);
435 GNUNET_array_append (lscp, ls, -1);
436 }
437#if DARWIN
438 /* see https://web.archive.org/web/20150924082249/gnunet.org/vfork */
439 #pragma GCC diagnostic push
440 #pragma GCC diagnostic ignored "-Wdeprecated"
441 #pragma clang diagnostic push
442 #pragma clang diagnostic ignored "-Wdeprecated"
443 ret = vfork ();
444 #pragma clang diagnostic pop
445 #pragma GCC diagnostic pop
446#else
447 ret = fork ();
448#endif
449 if (-1 == ret)
450 {
451 int eno = errno;
452 LOG_STRERROR (GNUNET_ERROR_TYPE_ERROR, "fork");
453 GNUNET_array_grow (lscp, ls, 0);
454 if (NULL != childpipe_write)
455 GNUNET_DISK_file_close (childpipe_write);
456 if (0 <= childpipe_read_fd)
457 GNUNET_break (0 == close (childpipe_read_fd));
458 errno = eno;
459 return NULL;
460 }
461 if (0 != ret)
462 {
463 unsetenv (GNUNET_OS_CONTROL_PIPE);
464 gnunet_proc = GNUNET_new (struct GNUNET_OS_Process);
465 gnunet_proc->pid = ret;
466 gnunet_proc->control_pipe = childpipe_write;
467 if (0 != (std_inheritance & GNUNET_OS_USE_PIPE_CONTROL))
468 {
469 GNUNET_break (0 == close (childpipe_read_fd));
470 }
471 GNUNET_array_grow (lscp, ls, 0);
472 return gnunet_proc;
473 }
474 if (0 <= childpipe_read_fd)
475 {
476 char fdbuf[100];
477#ifndef DARWIN
478 /* due to vfork, we must NOT free memory on DARWIN! */
479 GNUNET_DISK_file_close (childpipe_write);
480#endif
481 snprintf (fdbuf, 100, "%x", childpipe_read_fd);
482 setenv (GNUNET_OS_CONTROL_PIPE, fdbuf, 1);
483 }
484 else
485 unsetenv (GNUNET_OS_CONTROL_PIPE);
486 if (NULL != pipe_stdin)
487 {
488 GNUNET_break (0 == close (fd_stdin_write));
489 if (-1 == dup2 (fd_stdin_read, 0))
490 LOG_STRERROR (GNUNET_ERROR_TYPE_ERROR, "dup2");
491 GNUNET_break (0 == close (fd_stdin_read));
492 }
493 else if (0 == (std_inheritance & GNUNET_OS_INHERIT_STD_IN))
494 {
495 GNUNET_break (0 == close (0));
496 open_dev_null (0, O_RDONLY);
497 }
498 if (NULL != pipe_stdout)
499 {
500 GNUNET_break (0 == close (fd_stdout_read));
501 if (-1 == dup2 (fd_stdout_write, 1))
502 LOG_STRERROR (GNUNET_ERROR_TYPE_ERROR, "dup2");
503 GNUNET_break (0 == close (fd_stdout_write));
504 }
505 else if (0 == (std_inheritance & GNUNET_OS_INHERIT_STD_OUT))
506 {
507 GNUNET_break (0 == close (1));
508 open_dev_null (1, O_WRONLY);
509 }
510 if (NULL != pipe_stderr)
511 {
512 GNUNET_break (0 == close (fd_stderr_read));
513 if (-1 == dup2 (fd_stderr_write, 2))
514 LOG_STRERROR (GNUNET_ERROR_TYPE_ERROR, "dup2");
515 GNUNET_break (0 == close (fd_stderr_write));
516 }
517 else if (0 == (std_inheritance & GNUNET_OS_INHERIT_STD_ERR))
518 {
519 GNUNET_break (0 == close (2));
520 open_dev_null (2, O_WRONLY);
521 }
522 if (NULL != lscp)
523 {
524 /* read systemd documentation... */
525 i = 0;
526 tgt = 3;
527 while (-1 != lscp[i])
528 {
529 j = i + 1;
530 while (-1 != lscp[j])
531 {
532 if (lscp[j] == tgt)
533 {
534 /* dup away */
535 k = dup (lscp[j]);
536 GNUNET_assert (-1 != k);
537 GNUNET_assert (0 == close (lscp[j]));
538 lscp[j] = k;
539 break;
540 }
541 j++;
542 }
543 if (lscp[i] != tgt)
544 {
545 /* Bury any existing FD, no matter what; they should all be closed
546 * on exec anyway and the important ones have been dup'ed away */
547 GNUNET_break (0 == close (tgt));
548 GNUNET_assert (-1 != dup2 (lscp[i], tgt));
549 }
550 /* unset close-on-exec flag */
551 flags = fcntl (tgt, F_GETFD);
552 GNUNET_assert (flags >= 0);
553 flags &= ~FD_CLOEXEC;
554 fflush (stderr);
555 (void) fcntl (tgt, F_SETFD, flags);
556 tgt++;
557 i++;
558 }
559 GNUNET_snprintf (fds, sizeof(fds), "%u", i);
560 setenv ("LISTEN_FDS", fds, 1);
561 }
562#ifndef DARWIN
563 /* due to vfork, we must NOT free memory on DARWIN! */
564 GNUNET_array_grow (lscp, ls, 0);
565#endif
566 execvp (filename, argv);
567 LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_ERROR, "execvp", filename);
568 _exit (1);
569}
570
571
572struct GNUNET_OS_Process *
573GNUNET_OS_start_process_vap (enum GNUNET_OS_InheritStdioFlags std_inheritance,
574 struct GNUNET_DISK_PipeHandle *pipe_stdin,
575 struct GNUNET_DISK_PipeHandle *pipe_stdout,
576 struct GNUNET_DISK_PipeHandle *pipe_stderr,
577 const char *filename,
578 char *const argv[])
579{
580 return start_process (std_inheritance,
581 pipe_stdin,
582 pipe_stdout,
583 pipe_stderr,
584 NULL,
585 filename,
586 argv);
587}
588
589
590struct GNUNET_OS_Process *
591GNUNET_OS_start_process_va (enum GNUNET_OS_InheritStdioFlags std_inheritance,
592 struct GNUNET_DISK_PipeHandle *pipe_stdin,
593 struct GNUNET_DISK_PipeHandle *pipe_stdout,
594 struct GNUNET_DISK_PipeHandle *pipe_stderr,
595 const char *filename,
596 va_list va)
597{
598 struct GNUNET_OS_Process *ret;
599 va_list ap;
600 char **argv;
601 int argc;
602
603 argc = 0;
604 va_copy (ap, va);
605 while (NULL != va_arg (ap, char *))
606 argc++;
607 va_end (ap);
608 argv = GNUNET_malloc (sizeof(char *) * (argc + 1));
609 argc = 0;
610 va_copy (ap, va);
611 while (NULL != (argv[argc] = va_arg (ap, char *)))
612 argc++;
613 va_end (ap);
614 ret = GNUNET_OS_start_process_vap (std_inheritance,
615 pipe_stdin,
616 pipe_stdout,
617 pipe_stderr,
618 filename,
619 argv);
620 GNUNET_free (argv);
621 return ret;
622}
623
624
625struct GNUNET_OS_Process *
626GNUNET_OS_start_process (enum GNUNET_OS_InheritStdioFlags std_inheritance,
627 struct GNUNET_DISK_PipeHandle *pipe_stdin,
628 struct GNUNET_DISK_PipeHandle *pipe_stdout,
629 struct GNUNET_DISK_PipeHandle *pipe_stderr,
630 const char *filename,
631 ...)
632{
633 struct GNUNET_OS_Process *ret;
634 va_list ap;
635
636 va_start (ap, filename);
637 ret = GNUNET_OS_start_process_va (std_inheritance,
638 pipe_stdin,
639 pipe_stdout,
640 pipe_stderr,
641 filename,
642 ap);
643 va_end (ap);
644 return ret;
645}
646
647
648struct GNUNET_OS_Process *
649GNUNET_OS_start_process_v (enum GNUNET_OS_InheritStdioFlags std_inheritance,
650 const int *lsocks,
651 const char *filename,
652 char *const argv[])
653{
654 return start_process (std_inheritance,
655 NULL,
656 NULL,
657 NULL,
658 lsocks,
659 filename,
660 argv);
661}
662
663
664struct GNUNET_OS_Process *
665GNUNET_OS_start_process_s (enum GNUNET_OS_InheritStdioFlags std_inheritance,
666 const int *lsocks,
667 const char *filename,
668 ...)
669{
670 va_list ap;
671 char **argv;
672 unsigned int argv_size;
673 const char *arg;
674 const char *rpos;
675 char *pos;
676 char *cp;
677 const char *last;
678 struct GNUNET_OS_Process *proc;
679 char *binary_path;
680 int quote_on;
681 unsigned int i;
682 size_t len;
683
684 argv_size = 1;
685 va_start (ap, filename);
686 arg = filename;
687 last = NULL;
688 do
689 {
690 rpos = arg;
691 quote_on = 0;
692 while ('\0' != *rpos)
693 {
694 if ('"' == *rpos)
695 {
696 if (1 == quote_on)
697 quote_on = 0;
698 else
699 quote_on = 1;
700 }
701 if ((' ' == *rpos) && (0 == quote_on))
702 {
703 if (NULL != last)
704 argv_size++;
705 last = NULL;
706 rpos++;
707 while (' ' == *rpos)
708 rpos++;
709 }
710 if ((NULL == last) && ('\0' != *rpos)) // FIXME: == or !=?
711 last = rpos;
712 if ('\0' != *rpos)
713 rpos++;
714 }
715 if (NULL != last)
716 argv_size++;
717 }
718 while (NULL != (arg = (va_arg (ap, const char *))));
719 va_end (ap);
720
721 argv = GNUNET_malloc (argv_size * sizeof(char *));
722 argv_size = 0;
723 va_start (ap, filename);
724 arg = filename;
725 last = NULL;
726 do
727 {
728 cp = GNUNET_strdup (arg);
729 quote_on = 0;
730 pos = cp;
731 while ('\0' != *pos)
732 {
733 if ('"' == *pos)
734 {
735 if (1 == quote_on)
736 quote_on = 0;
737 else
738 quote_on = 1;
739 }
740 if ((' ' == *pos) && (0 == quote_on))
741 {
742 *pos = '\0';
743 if (NULL != last)
744 argv[argv_size++] = GNUNET_strdup (last);
745 last = NULL;
746 pos++;
747 while (' ' == *pos)
748 pos++;
749 }
750 if ((NULL == last) && ('\0' != *pos)) // FIXME: == or !=?
751 last = pos;
752 if ('\0' != *pos)
753 pos++;
754 }
755 if (NULL != last)
756 argv[argv_size++] = GNUNET_strdup (last);
757 last = NULL;
758 GNUNET_free (cp);
759 }
760 while (NULL != (arg = (va_arg (ap, const char *))));
761 va_end (ap);
762 argv[argv_size] = NULL;
763
764 for (i = 0; i < argv_size; i++)
765 {
766 len = strlen (argv[i]);
767 if ((argv[i][0] == '"') && (argv[i][len - 1] == '"'))
768 {
769 memmove (&argv[i][0], &argv[i][1], len - 2);
770 argv[i][len - 2] = '\0';
771 }
772 }
773 binary_path = argv[0];
774 proc = GNUNET_OS_start_process_v (std_inheritance,
775 lsocks,
776 binary_path,
777 argv);
778 while (argv_size > 0)
779 GNUNET_free_nz (argv[--argv_size]);
780 GNUNET_free (argv);
781 return proc;
782}
783
784
785/**
786 * Retrieve the status of a process, waiting on it if dead.
787 * Nonblocking version.
788 *
789 * @param proc process ID
790 * @param type status type
791 * @param code return code/signal number
792 * @param options WNOHANG if non-blocking is desired
793 * @return #GNUNET_OK on success, #GNUNET_NO if the process is still running, #GNUNET_SYSERR otherwise
794 */
795static enum GNUNET_GenericReturnValue
796process_status (struct GNUNET_OS_Process *proc,
797 enum GNUNET_OS_ProcessStatusType *type,
798 unsigned long *code,
799 int options)
800{
801 int status;
802 int ret;
803
804 GNUNET_assert (0 != proc);
805 ret = waitpid (proc->pid,
806 &status,
807 options);
808 if (ret < 0)
809 {
810 LOG_STRERROR (GNUNET_ERROR_TYPE_WARNING,
811 "waitpid");
812 return GNUNET_SYSERR;
813 }
814 if (0 == ret)
815 {
816 *type = GNUNET_OS_PROCESS_RUNNING;
817 *code = 0;
818 return GNUNET_NO;
819 }
820 if (proc->pid != ret)
821 {
822 LOG_STRERROR (GNUNET_ERROR_TYPE_WARNING,
823 "waitpid");
824 return GNUNET_SYSERR;
825 }
826 if (WIFEXITED (status))
827 {
828 *type = GNUNET_OS_PROCESS_EXITED;
829 *code = WEXITSTATUS (status);
830 }
831 else if (WIFSIGNALED (status))
832 {
833 *type = GNUNET_OS_PROCESS_SIGNALED;
834 *code = WTERMSIG (status);
835 }
836 else if (WIFSTOPPED (status))
837 {
838 *type = GNUNET_OS_PROCESS_SIGNALED;
839 *code = WSTOPSIG (status);
840 }
841#ifdef WIFCONTINUED
842 else if (WIFCONTINUED (status))
843 {
844 *type = GNUNET_OS_PROCESS_RUNNING;
845 *code = 0;
846 }
847#endif
848 else
849 {
850 *type = GNUNET_OS_PROCESS_UNKNOWN;
851 *code = 0;
852 }
853
854 return GNUNET_OK;
855}
856
857
858enum GNUNET_GenericReturnValue
859GNUNET_OS_process_status (struct GNUNET_OS_Process *proc,
860 enum GNUNET_OS_ProcessStatusType *type,
861 unsigned long *code)
862{
863 return process_status (proc, type, code, WNOHANG);
864}
865
866
867enum GNUNET_GenericReturnValue
868GNUNET_OS_process_wait_status (struct GNUNET_OS_Process *proc,
869 enum GNUNET_OS_ProcessStatusType *type,
870 unsigned long *code)
871{
872 return process_status (proc, type, code, 0);
873}
874
875
876enum GNUNET_GenericReturnValue
877GNUNET_OS_process_wait (struct GNUNET_OS_Process *proc)
878{
879 pid_t pid = proc->pid;
880 pid_t ret;
881
882 while ((pid != (ret = waitpid (pid, NULL, 0))) && (EINTR == errno))
883 ;
884 if (pid != ret)
885 {
886 LOG_STRERROR (GNUNET_ERROR_TYPE_WARNING,
887 "waitpid");
888 return GNUNET_SYSERR;
889 }
890 return GNUNET_OK;
891}
892
893
894/**
895 * Handle to a command.
896 */
897struct GNUNET_OS_CommandHandle
898{
899 /**
900 * Process handle.
901 */
902 struct GNUNET_OS_Process *eip;
903
904 /**
905 * Handle to the output pipe.
906 */
907 struct GNUNET_DISK_PipeHandle *opipe;
908
909 /**
910 * Read-end of output pipe.
911 */
912 const struct GNUNET_DISK_FileHandle *r;
913
914 /**
915 * Function to call on each line of output.
916 */
917 GNUNET_OS_LineProcessor proc;
918
919 /**
920 * Closure for @e proc.
921 */
922 void *proc_cls;
923
924 /**
925 * Buffer for the output.
926 */
927 char buf[1024];
928
929 /**
930 * Task reading from pipe.
931 */
932 struct GNUNET_SCHEDULER_Task *rtask;
933
934 /**
935 * When to time out.
936 */
937 struct GNUNET_TIME_Absolute timeout;
938
939 /**
940 * Current read offset in buf.
941 */
942 size_t off;
943};
944
945
946void
947GNUNET_OS_command_stop (struct GNUNET_OS_CommandHandle *cmd)
948{
949 if (NULL != cmd->proc)
950 {
951 GNUNET_assert (NULL != cmd->rtask);
952 GNUNET_SCHEDULER_cancel (cmd->rtask);
953 }
954 (void) GNUNET_OS_process_kill (cmd->eip, SIGKILL);
955 GNUNET_break (GNUNET_OK == GNUNET_OS_process_wait (cmd->eip));
956 GNUNET_OS_process_destroy (cmd->eip);
957 GNUNET_DISK_pipe_close (cmd->opipe);
958 GNUNET_free (cmd);
959}
960
961
962/**
963 * Read from the process and call the line processor.
964 *
965 * @param cls the `struct GNUNET_OS_CommandHandle *`
966 */
967static void
968cmd_read (void *cls)
969{
970 struct GNUNET_OS_CommandHandle *cmd = cls;
971 const struct GNUNET_SCHEDULER_TaskContext *tc;
972 GNUNET_OS_LineProcessor proc;
973 char *end;
974 ssize_t ret;
975
976 cmd->rtask = NULL;
977 tc = GNUNET_SCHEDULER_get_task_context ();
978 if (GNUNET_YES != GNUNET_NETWORK_fdset_handle_isset (tc->read_ready, cmd->r))
979 {
980 /* timeout */
981 proc = cmd->proc;
982 cmd->proc = NULL;
983 proc (cmd->proc_cls, NULL);
984 return;
985 }
986 ret = GNUNET_DISK_file_read (cmd->r,
987 &cmd->buf[cmd->off],
988 sizeof(cmd->buf) - cmd->off);
989 if (ret <= 0)
990 {
991 if ((cmd->off > 0) && (cmd->off < sizeof(cmd->buf)))
992 {
993 cmd->buf[cmd->off] = '\0';
994 cmd->proc (cmd->proc_cls, cmd->buf);
995 }
996 proc = cmd->proc;
997 cmd->proc = NULL;
998 proc (cmd->proc_cls, NULL);
999 return;
1000 }
1001 end = memchr (&cmd->buf[cmd->off], '\n', ret);
1002 cmd->off += ret;
1003 while (NULL != end)
1004 {
1005 *end = '\0';
1006 cmd->proc (cmd->proc_cls, cmd->buf);
1007 memmove (cmd->buf, end + 1, cmd->off - (end + 1 - cmd->buf));
1008 cmd->off -= (end + 1 - cmd->buf);
1009 end = memchr (cmd->buf, '\n', cmd->off);
1010 }
1011 cmd->rtask =
1012 GNUNET_SCHEDULER_add_read_file (GNUNET_TIME_absolute_get_remaining (
1013 cmd->timeout),
1014 cmd->r,
1015 &cmd_read,
1016 cmd);
1017}
1018
1019
1020struct GNUNET_OS_CommandHandle *
1021GNUNET_OS_command_run (GNUNET_OS_LineProcessor proc,
1022 void *proc_cls,
1023 struct GNUNET_TIME_Relative timeout,
1024 const char *binary,
1025 ...)
1026{
1027 struct GNUNET_OS_CommandHandle *cmd;
1028 struct GNUNET_OS_Process *eip;
1029 struct GNUNET_DISK_PipeHandle *opipe;
1030 va_list ap;
1031
1032 opipe = GNUNET_DISK_pipe (GNUNET_DISK_PF_BLOCKING_RW);
1033 if (NULL == opipe)
1034 return NULL;
1035 va_start (ap, binary);
1036 /* redirect stdout, don't inherit stderr/stdin */
1037 eip =
1038 GNUNET_OS_start_process_va (GNUNET_OS_INHERIT_STD_NONE,
1039 NULL,
1040 opipe,
1041 NULL,
1042 binary,
1043 ap);
1044 va_end (ap);
1045 if (NULL == eip)
1046 {
1047 GNUNET_DISK_pipe_close (opipe);
1048 return NULL;
1049 }
1050 GNUNET_DISK_pipe_close_end (opipe, GNUNET_DISK_PIPE_END_WRITE);
1051 cmd = GNUNET_new (struct GNUNET_OS_CommandHandle);
1052 cmd->timeout = GNUNET_TIME_relative_to_absolute (timeout);
1053 cmd->eip = eip;
1054 cmd->opipe = opipe;
1055 cmd->proc = proc;
1056 cmd->proc_cls = proc_cls;
1057 cmd->r = GNUNET_DISK_pipe_handle (opipe, GNUNET_DISK_PIPE_END_READ);
1058 cmd->rtask = GNUNET_SCHEDULER_add_read_file (timeout, cmd->r, &cmd_read, cmd);
1059 return cmd;
1060}
1061
1062
1063/* end of os_priority.c */
diff --git a/src/lib/util/peer.c b/src/lib/util/peer.c
new file mode 100644
index 000000000..e5c15b098
--- /dev/null
+++ b/src/lib/util/peer.c
@@ -0,0 +1,242 @@
1/*
2 This file is part of GNUnet
3 Copyright (C) 2006, 2008, 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/**
22 * @file util/peer.c
23 * @brief peer-ID table that assigns integer IDs to peer-IDs to save memory
24 * @author Christian Grothoff
25 */
26
27#include "platform.h"
28#include "gnunet_util_lib.h"
29
30#define LOG(kind, ...) GNUNET_log_from (kind, "util-peer", __VA_ARGS__)
31
32
33struct PeerEntry
34{
35 /**
36 * The identifier itself
37 */
38 struct GNUNET_PeerIdentity id;
39
40 /**
41 * Short version of the identifier; if the RC==0, then index of next
42 * free slot in table, otherwise equal to this slot in the table.
43 */
44 GNUNET_PEER_Id pid;
45
46 /**
47 * Reference counter, 0 if this slot is not used.
48 */
49 unsigned int rc;
50};
51
52
53/**
54 * Table with our interned peer IDs.
55 */
56static struct PeerEntry **table;
57
58/**
59 * Peermap of PeerIdentities to "struct PeerEntry"
60 * (for fast lookup). NULL until the library
61 * is actually being used.
62 */
63static struct GNUNET_CONTAINER_MultiPeerMap *map;
64
65/**
66 * Size of the "table".
67 */
68static unsigned int size;
69
70/**
71 * Index of the beginning of the free list in the table; set to "size"
72 * if no slots are free in the table.
73 */
74static unsigned int free_list_start;
75
76
77/**
78 * Search for a peer identity. The reference counter is not changed.
79 *
80 * @param pid identity to find
81 * @return the interned identity or 0.
82 */
83GNUNET_PEER_Id
84GNUNET_PEER_search (const struct GNUNET_PeerIdentity *pid)
85{
86 struct PeerEntry *e;
87
88 if (NULL == pid)
89 return 0;
90 if (NULL == map)
91 return 0;
92 e = GNUNET_CONTAINER_multipeermap_get (map, pid);
93 if (NULL == e)
94 return 0;
95 GNUNET_assert (e->rc > 0);
96 return e->pid;
97}
98
99
100/**
101 * Intern an peer identity. If the identity is already known, its
102 * reference counter will be increased by one.
103 *
104 * @param pid identity to intern
105 * @return the interned identity.
106 */
107GNUNET_PEER_Id
108GNUNET_PEER_intern (const struct GNUNET_PeerIdentity *pid)
109{
110 GNUNET_PEER_Id ret;
111 struct PeerEntry *e;
112
113 if (NULL == pid)
114 return 0;
115 if (NULL == map)
116 map = GNUNET_CONTAINER_multipeermap_create (32, GNUNET_YES);
117 e = GNUNET_CONTAINER_multipeermap_get (map, pid);
118 if (NULL != e)
119 {
120 GNUNET_assert (e->rc > 0);
121 e->rc++;
122 return e->pid;
123 }
124 ret = free_list_start;
125 if (ret == size)
126 {
127 GNUNET_array_grow (table, size, size + 16);
128 for (unsigned int i = ret; i < size; i++)
129 {
130 table[i] = GNUNET_new (struct PeerEntry);
131 table[i]->pid = i + 1;
132 }
133 }
134 if (0 == ret)
135 {
136 memset (&table[0]->id, 0, sizeof(struct GNUNET_PeerIdentity));
137 table[0]->pid = 0;
138 table[0]->rc = 1;
139 ret = 1;
140 }
141 GNUNET_assert (ret < size);
142 GNUNET_assert (0 == table[ret]->rc);
143 free_list_start = table[ret]->pid;
144 table[ret]->id = *pid;
145 table[ret]->rc = 1;
146 table[ret]->pid = ret;
147 GNUNET_break (GNUNET_OK ==
148 GNUNET_CONTAINER_multipeermap_put (map,
149 &table[ret]->id,
150 table[ret],
151 GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_ONLY));
152 return ret;
153}
154
155
156void
157GNUNET_PEER_decrement_rcs (const GNUNET_PEER_Id *ids, unsigned int count)
158{
159 int i;
160 GNUNET_PEER_Id id;
161
162 if (0 == count)
163 return;
164 for (i = count - 1; i >= 0; i--)
165 {
166 id = ids[i];
167 if (0 == id)
168 continue;
169 GNUNET_assert (id < size);
170 GNUNET_assert (table[id]->rc > 0);
171 table[id]->rc--;
172 if (0 == table[id]->rc)
173 {
174 GNUNET_break (GNUNET_OK ==
175 GNUNET_CONTAINER_multipeermap_remove (map,
176 &table[id]->id,
177 table[id]));
178 table[id]->pid = free_list_start;
179 free_list_start = id;
180 }
181 }
182}
183
184
185/**
186 * Change the reference counter of an interned PID.
187 *
188 * @param id identity to change the RC of
189 * @param delta how much to change the RC
190 */
191void
192GNUNET_PEER_change_rc (GNUNET_PEER_Id id, int delta)
193{
194 if (0 == id)
195 return;
196 GNUNET_assert (id < size);
197 GNUNET_assert (table[id]->rc > 0);
198 GNUNET_assert ((delta >= 0) ||
199 (table[id]->rc >= (unsigned int) (-delta)));
200 table[id]->rc += delta;
201 if (0 == table[id]->rc)
202 {
203 GNUNET_break (GNUNET_OK ==
204 GNUNET_CONTAINER_multipeermap_remove (map,
205 &table[id]->id,
206 table[id]));
207 table[id]->pid = free_list_start;
208 free_list_start = id;
209 }
210}
211
212
213/**
214 * Convert an interned PID to a normal peer identity.
215 *
216 * @param id interned PID to convert
217 * @param pid where to write the normal peer identity
218 */
219void
220GNUNET_PEER_resolve (GNUNET_PEER_Id id, struct GNUNET_PeerIdentity *pid)
221{
222 if (0 == id)
223 {
224 memset (pid, 0, sizeof(struct GNUNET_PeerIdentity));
225 return;
226 }
227 GNUNET_assert (id < size);
228 GNUNET_assert (table[id]->rc > 0);
229 *pid = table[id]->id;
230}
231
232
233const struct GNUNET_PeerIdentity *
234GNUNET_PEER_resolve2 (GNUNET_PEER_Id id)
235{
236 GNUNET_assert (id < size);
237 GNUNET_assert (table[id]->rc > 0);
238 return &table[id]->id;
239}
240
241
242/* end of peer.c */
diff --git a/src/lib/util/perf_crypto_asymmetric.c b/src/lib/util/perf_crypto_asymmetric.c
new file mode 100644
index 000000000..c033a02ca
--- /dev/null
+++ b/src/lib/util/perf_crypto_asymmetric.c
@@ -0,0 +1,133 @@
1/*
2 This file is part of GNUnet.
3 Copyright (C) 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/**
22 * @author Bart Polot
23 * @file util/perf_crypto_asymmetric.c
24 * @brief measure performance of public key functions
25 */
26
27#include "platform.h"
28#include "gnunet_util_lib.h"
29#include <gauger.h>
30
31static struct GNUNET_TIME_Absolute start;
32
33#define l 500
34
35struct TestSig
36{
37 struct GNUNET_CRYPTO_EccSignaturePurpose purp;
38 struct GNUNET_HashCode h;
39 struct GNUNET_CRYPTO_EddsaSignature sig;
40};
41
42
43static void
44log_duration (const char *cryptosystem,
45 const char *description)
46{
47 struct GNUNET_TIME_Relative t;
48 char s[64];
49
50 sprintf (s, "%6s %15s", cryptosystem, description);
51 t = GNUNET_TIME_absolute_get_duration (start);
52 t = GNUNET_TIME_relative_divide (t, l);
53 fprintf (stdout,
54 "%s: %10s\n",
55 s,
56 GNUNET_STRINGS_relative_time_to_string (t,
57 GNUNET_NO));
58 GAUGER ("UTIL", s, t.rel_value_us, "us");
59}
60
61
62int
63main (int argc, char *argv[])
64{
65 int i;
66 struct GNUNET_CRYPTO_EcdhePrivateKey ecdhe[l];
67 struct GNUNET_CRYPTO_EcdhePublicKey dhpub[l];
68 struct GNUNET_CRYPTO_EddsaPrivateKey eddsa[l];
69 struct GNUNET_CRYPTO_EddsaPublicKey dspub[l];
70 struct TestSig sig[l];
71
72 start = GNUNET_TIME_absolute_get ();
73 for (i = 0; i < l; i++)
74 {
75 sig[i].purp.purpose = 0;
76 sig[i].purp.size = htonl (sizeof(struct GNUNET_CRYPTO_EccSignaturePurpose)
77 + sizeof(struct GNUNET_HashCode));
78 GNUNET_CRYPTO_random_block (GNUNET_CRYPTO_QUALITY_WEAK,
79 &sig[i].h,
80 sizeof(sig[i].h));
81 }
82 log_duration ("", "Init");
83
84 start = GNUNET_TIME_absolute_get ();
85 for (i = 0; i < l; i++)
86 GNUNET_CRYPTO_eddsa_key_create (&eddsa[i]);
87 log_duration ("EdDSA", "create key");
88
89 start = GNUNET_TIME_absolute_get ();
90 for (i = 0; i < l; i++)
91 GNUNET_CRYPTO_eddsa_key_get_public (&eddsa[i], &dspub[i]);
92 log_duration ("EdDSA", "get public");
93
94 start = GNUNET_TIME_absolute_get ();
95 for (i = 0; i < l; i++)
96 GNUNET_assert (GNUNET_OK ==
97 GNUNET_CRYPTO_eddsa_sign_ (&eddsa[i],
98 &sig[i].purp,
99 &sig[i].sig));
100 log_duration ("EdDSA", "sign HashCode");
101
102 start = GNUNET_TIME_absolute_get ();
103 for (i = 0; i < l; i++)
104 GNUNET_assert (GNUNET_OK ==
105 GNUNET_CRYPTO_eddsa_verify_ (0,
106 &sig[i].purp,
107 &sig[i].sig,
108 &dspub[i]));
109 log_duration ("EdDSA", "verify HashCode");
110
111 start = GNUNET_TIME_absolute_get ();
112 for (i = 0; i < l; i++)
113 GNUNET_CRYPTO_ecdhe_key_create (&ecdhe[i]);
114 log_duration ("ECDH", "create key");
115
116 start = GNUNET_TIME_absolute_get ();
117 for (i = 0; i < l; i++)
118 GNUNET_CRYPTO_ecdhe_key_get_public (&ecdhe[i], &dhpub[i]);
119 log_duration ("ECDH", "get public");
120
121 start = GNUNET_TIME_absolute_get ();
122 for (i = 0; i < l - 1; i += 2)
123 {
124 GNUNET_CRYPTO_ecc_ecdh (&ecdhe[i], &dhpub[i + 1], &sig[i].h);
125 GNUNET_CRYPTO_ecc_ecdh (&ecdhe[i + 1], &dhpub[i], &sig[i + 1].h);
126 }
127 log_duration ("ECDH", "do DH");
128
129 return 0;
130}
131
132
133/* end of perf_crypto_asymmetric.c */
diff --git a/src/lib/util/perf_crypto_cs.c b/src/lib/util/perf_crypto_cs.c
new file mode 100644
index 000000000..43f32aae0
--- /dev/null
+++ b/src/lib/util/perf_crypto_cs.c
@@ -0,0 +1,184 @@
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/**
22 * @author Lucien Heuzeveldt <lucienclaude.heuzeveldt@students.bfh.ch>
23 * @author Gian Demarmels <gian@demarmels.org>
24 * @file util/perf_crypto_cs.c
25 * @brief measure performance of Clause Blind Schnorr Signatures
26 */
27
28
29#include "platform.h"
30#include "gnunet_util_lib.h"
31#include <gauger.h>
32
33#define ITER 10
34
35/**
36 * Evaluate Clause Blind Schnorr Signature performance.
37 *
38 */
39static void
40eval ()
41{
42 struct GNUNET_TIME_Absolute start;
43 unsigned int i;
44
45 struct GNUNET_CRYPTO_CsPrivateKey priv;
46 struct GNUNET_CRYPTO_CsPublicKey pub;
47
48 struct GNUNET_CRYPTO_CsRSecret r_priv[2];
49 struct GNUNET_CRYPTO_CsRPublic r_pub[2];
50
51 char message[] = "test message";
52 size_t message_len = strlen ("test message");
53
54 // derive a test nonce
55 struct GNUNET_CRYPTO_CsNonce nonce;
56 GNUNET_assert (GNUNET_YES == GNUNET_CRYPTO_hkdf (nonce.nonce,
57 sizeof(nonce.nonce),
58 GCRY_MD_SHA512,
59 GCRY_MD_SHA256,
60 "nonce",
61 strlen ("nonce"),
62 "nonce_secret",
63 strlen ("nonce_secret"),
64 NULL,
65 0));
66
67 struct GNUNET_CRYPTO_CsBlindingSecret bs[2];
68 struct GNUNET_CRYPTO_CsC blinded_cs[2];
69 struct GNUNET_CRYPTO_CsRPublic blinded_r_pub[2];
70 struct GNUNET_CRYPTO_CsBlindS blinded_s;
71 struct GNUNET_CRYPTO_CsS signature_scalar;
72 struct GNUNET_CRYPTO_CsSignature sig;
73
74 // BENCHMARK keygen
75 start = GNUNET_TIME_absolute_get ();
76
77 for (i = 0; i < ITER; i++)
78 {
79 GNUNET_CRYPTO_cs_private_key_generate (&priv);
80 GNUNET_CRYPTO_cs_private_key_get_public (&priv, &pub);
81 }
82 printf ("10x key generation took %s\n",
83 GNUNET_STRINGS_relative_time_to_string (
84 GNUNET_TIME_absolute_get_duration (start),
85 GNUNET_YES));
86
87
88 // BENCHMARK r derive and calc R pub
89 start = GNUNET_TIME_absolute_get ();
90 for (i = 0; i < ITER; i++)
91 {
92 GNUNET_CRYPTO_cs_r_derive (&nonce, &priv, r_priv);
93 GNUNET_CRYPTO_cs_r_get_public (&r_priv[0], &r_pub[0]);
94 GNUNET_CRYPTO_cs_r_get_public (&r_priv[1], &r_pub[1]);
95 }
96 printf ("10x r0, r1 derive and R1,R2 calculation took %s\n",
97 GNUNET_STRINGS_relative_time_to_string (
98 GNUNET_TIME_absolute_get_duration (start),
99 GNUNET_YES));
100
101
102 // BENCHMARK derive blinding secrets
103 start = GNUNET_TIME_absolute_get ();
104 for (i = 0; i < ITER; i++)
105 {
106 GNUNET_CRYPTO_cs_blinding_secrets_derive (&nonce,
107 bs);
108 }
109 printf ("10x derive blinding secrets took %s\n",
110 GNUNET_STRINGS_relative_time_to_string (
111 GNUNET_TIME_absolute_get_duration (start),
112 GNUNET_YES));
113
114
115 // BENCHMARK calculating C
116 start = GNUNET_TIME_absolute_get ();
117 for (i = 0; i < ITER; i++)
118 {
119 GNUNET_CRYPTO_cs_calc_blinded_c (bs,
120 r_pub,
121 &pub,
122 message,
123 message_len,
124 blinded_cs,
125 blinded_r_pub);
126 }
127 printf ("10x calculating the blinded c took %s\n",
128 GNUNET_STRINGS_relative_time_to_string (
129 GNUNET_TIME_absolute_get_duration (start),
130 GNUNET_YES));
131
132
133 // BENCHMARK sign derive
134 unsigned int b;
135 start = GNUNET_TIME_absolute_get ();
136 for (i = 0; i < ITER; i++)
137 {
138 b = GNUNET_CRYPTO_cs_sign_derive (&priv,
139 r_priv,
140 blinded_cs,
141 &nonce,
142 &blinded_s);
143 }
144 printf ("10x signing blinded c took %s\n",
145 GNUNET_STRINGS_relative_time_to_string (
146 GNUNET_TIME_absolute_get_duration (start),
147 GNUNET_YES));
148
149
150 // BENCHMARK unblind signature
151 start = GNUNET_TIME_absolute_get ();
152
153 for (i = 0; i < ITER; i++)
154 {
155 GNUNET_CRYPTO_cs_unblind (&blinded_s, &bs[b], &signature_scalar);
156 sig.r_point = blinded_r_pub[b];
157 sig.s_scalar = signature_scalar;
158 }
159 printf ("10x unblinding s took %s\n",
160 GNUNET_STRINGS_relative_time_to_string (
161 GNUNET_TIME_absolute_get_duration (start),
162 GNUNET_YES));
163
164 // BENCHMARK verify signature
165 start = GNUNET_TIME_absolute_get ();
166 for (i = 0; i < ITER; i++)
167 {
168 GNUNET_CRYPTO_cs_verify (&sig,
169 &pub,
170 message,
171 message_len);
172 }
173 printf ("10x verifying signatures took %s\n",
174 GNUNET_STRINGS_relative_time_to_string (
175 GNUNET_TIME_absolute_get_duration (start),
176 GNUNET_YES));
177}
178
179int
180main (int argc, char *argv[])
181{
182 eval ();
183 return 0;
184}
diff --git a/src/lib/util/perf_crypto_ecc_dlog.c b/src/lib/util/perf_crypto_ecc_dlog.c
new file mode 100644
index 000000000..698a3aafa
--- /dev/null
+++ b/src/lib/util/perf_crypto_ecc_dlog.c
@@ -0,0 +1,184 @@
1/*
2 This file is part of GNUnet.
3 Copyright (C) 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/**
22 * @file util/perf_crypto_ecc_dlog.c
23 * @brief benchmark for ECC DLOG calculation
24 * @author Christian Grothoff
25 */
26
27#include "platform.h"
28#include "gnunet_util_lib.h"
29#include <gcrypt.h>
30#include <gauger.h>
31
32
33/**
34 * Name of the curve we are using. Note that we have hard-coded
35 * structs that use 256 bits, so using a bigger curve will require
36 * changes that break stuff badly. The name of the curve given here
37 * must be agreed by all peers and be supported by libgcrypt.
38 */
39#define CURVE "Ed25519"
40
41/**
42 * Maximum value we benchmark dlog for.
43 */
44#define MAX_FACT (1024 * 1024)
45
46/**
47 * Maximum memory to use, sqrt(MAX_FACT) is a good choice.
48 */
49#define MAX_MEM 1024
50
51/**
52 * How many values do we test?
53 */
54#define TEST_ITER 10
55
56
57/**
58 * Do some DLOG operations for testing.
59 *
60 * @param edc context for ECC operations
61 * @param do_dlog true if we want to actually do the bencharked operation
62 */
63static void
64test_dlog (struct GNUNET_CRYPTO_EccDlogContext *edc,
65 bool do_dlog)
66{
67 for (unsigned int i = 0; i < TEST_ITER; i++)
68 {
69 struct GNUNET_CRYPTO_EccScalar fact;
70 struct GNUNET_CRYPTO_EccScalar n;
71 struct GNUNET_CRYPTO_EccPoint q;
72 int x;
73
74 fprintf (stderr, ".");
75 x = GNUNET_CRYPTO_random_u32 (GNUNET_CRYPTO_QUALITY_WEAK,
76 MAX_FACT);
77 memset (&n,
78 0,
79 sizeof (n));
80 for (unsigned int j = 0; j < x; j++)
81 sodium_increment (n.v,
82 sizeof (n.v));
83 if (0 == GNUNET_CRYPTO_random_u32 (GNUNET_CRYPTO_QUALITY_WEAK,
84 2))
85 {
86 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
87 "Trying negative %d\n",
88 -x);
89 crypto_core_ed25519_scalar_negate (fact.v,
90 n.v);
91 x = -x;
92 }
93 else
94 {
95 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
96 "Trying positive %d\n",
97 x);
98 fact = n;
99 }
100 if (0 == x)
101 {
102 /* libsodium does not like to multiply with zero; make sure
103 'q' is a valid point (g) first, then use q = q - q to get
104 the product with zero */
105 sodium_increment (fact.v,
106 sizeof (fact.v));
107 GNUNET_assert (0 ==
108 crypto_scalarmult_ed25519_base_noclamp (q.v,
109 fact.v));
110 GNUNET_assert (
111 0 ==
112 crypto_core_ed25519_sub (q.v,
113 q.v,
114 q.v));
115 }
116 else
117 GNUNET_assert (0 ==
118 crypto_scalarmult_ed25519_base_noclamp (q.v,
119 fact.v));
120 if (do_dlog)
121 {
122 int iret;
123
124 if (x !=
125 (iret = GNUNET_CRYPTO_ecc_dlog (edc,
126 &q)))
127 {
128 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
129 "DLOG failed for value %d (got: %d)\n",
130 x,
131 iret);
132 GNUNET_assert (0);
133 }
134 }
135 }
136 fprintf (stderr,
137 "\n");
138}
139
140
141int
142main (int argc, char *argv[])
143{
144 struct GNUNET_CRYPTO_EccDlogContext *edc;
145 struct GNUNET_TIME_Absolute start;
146 struct GNUNET_TIME_Relative delta;
147
148 GNUNET_log_setup ("perf-crypto-ecc-dlog",
149 "WARNING",
150 NULL);
151 start = GNUNET_TIME_absolute_get ();
152 edc = GNUNET_CRYPTO_ecc_dlog_prepare (MAX_FACT,
153 MAX_MEM);
154 printf ("DLOG precomputation 1M/1K took %s\n",
155 GNUNET_STRINGS_relative_time_to_string (
156 GNUNET_TIME_absolute_get_duration (start),
157 GNUNET_YES));
158 GAUGER ("UTIL", "ECC DLOG initialization",
159 GNUNET_TIME_absolute_get_duration
160 (start).rel_value_us / 1000LL, "ms/op");
161 start = GNUNET_TIME_absolute_get ();
162 /* first do a baseline run without the DLOG */
163 test_dlog (edc, false);
164 delta = GNUNET_TIME_absolute_get_duration (start);
165 start = GNUNET_TIME_absolute_get ();
166 test_dlog (edc, true);
167 delta = GNUNET_TIME_relative_subtract (GNUNET_TIME_absolute_get_duration (
168 start),
169 delta);
170 printf ("%u DLOG calculations took %s\n",
171 TEST_ITER,
172 GNUNET_STRINGS_relative_time_to_string (delta,
173 GNUNET_YES));
174 GAUGER ("UTIL",
175 "ECC DLOG operations",
176 delta.rel_value_us / 1000LL / TEST_ITER,
177 "ms/op");
178
179 GNUNET_CRYPTO_ecc_dlog_release (edc);
180 return 0;
181}
182
183
184/* end of perf_crypto_ecc_dlog.c */
diff --git a/src/lib/util/perf_crypto_hash.c b/src/lib/util/perf_crypto_hash.c
new file mode 100644
index 000000000..4e37ef758
--- /dev/null
+++ b/src/lib/util/perf_crypto_hash.c
@@ -0,0 +1,115 @@
1/*
2 This file is part of GNUnet.
3 Copyright (C) 2002, 2003, 2004, 2006 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 * @author Christian Grothoff
23 * @file util/perf_crypto_hash.c
24 * @brief measure performance of hash function
25 */
26
27#include "platform.h"
28#include "gnunet_util_lib.h"
29#include <gauger.h>
30#include <gcrypt.h>
31
32
33static void
34perfHash ()
35{
36 struct GNUNET_HashCode hc;
37 unsigned int i;
38 char buf[64 * 1024];
39
40 memset (buf, 1, sizeof(buf));
41 for (i = 0; i < 1024; i++)
42 GNUNET_CRYPTO_hash (buf, sizeof(buf), &hc);
43}
44
45
46static void
47perfHashSmall ()
48{
49 struct GNUNET_HashCode hc;
50 unsigned int i;
51 char buf[64];
52
53 memset (buf, 1, sizeof(buf));
54 for (i = 0; i < 1024; i++)
55 GNUNET_CRYPTO_hash (buf, sizeof(buf), &hc);
56}
57
58
59static void
60perfHKDF ()
61{
62 unsigned int i;
63 char res[128];
64 char buf[128];
65 char skm[64];
66
67 memset (buf, 1, sizeof(buf));
68 memset (skm, 2, sizeof(skm));
69 for (i = 0; i < 1024; i++)
70 GNUNET_CRYPTO_hkdf (res, sizeof(res),
71 GCRY_MD_SHA512, GCRY_MD_SHA256,
72 buf, sizeof(buf),
73 skm, sizeof(skm),
74 "test", (size_t) 4,
75 NULL, 0);
76}
77
78
79int
80main (int argc, char *argv[])
81{
82 struct GNUNET_TIME_Absolute start;
83
84 start = GNUNET_TIME_absolute_get ();
85 perfHashSmall ();
86 printf ("1024x 64-byte Hash perf took %s\n",
87 GNUNET_STRINGS_relative_time_to_string (
88 GNUNET_TIME_absolute_get_duration (start),
89 GNUNET_YES));
90
91 start = GNUNET_TIME_absolute_get ();
92 perfHash ();
93 printf ("1024x 64k Hash perf took %s\n",
94 GNUNET_STRINGS_relative_time_to_string (
95 GNUNET_TIME_absolute_get_duration (start),
96 GNUNET_YES));
97 GAUGER ("UTIL", "Cryptographic hashing",
98 64 * 1024 / (1
99 + GNUNET_TIME_absolute_get_duration
100 (start).rel_value_us / 1000LL), "kb/ms");
101 start = GNUNET_TIME_absolute_get ();
102 perfHKDF ();
103 printf ("HKDF perf took %s\n",
104 GNUNET_STRINGS_relative_time_to_string (
105 GNUNET_TIME_absolute_get_duration (start),
106 GNUNET_YES));
107 GAUGER ("UTIL", "Cryptographic HKDF",
108 64 * 1024 / (1
109 + GNUNET_TIME_absolute_get_duration
110 (start).rel_value_us / 1000LL), "kb/ms");
111 return 0;
112}
113
114
115/* end of perf_crypto_hash.c */
diff --git a/src/lib/util/perf_crypto_paillier.c b/src/lib/util/perf_crypto_paillier.c
new file mode 100644
index 000000000..53c717a66
--- /dev/null
+++ b/src/lib/util/perf_crypto_paillier.c
@@ -0,0 +1,96 @@
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/**
22 * @author Christian Grothoff
23 * @file util/perf_crypto_paillier.c
24 * @brief measure performance of Paillier encryption
25 */
26
27#include "platform.h"
28#include "gnunet_util_lib.h"
29#include <gauger.h>
30
31
32int
33main (int argc, char *argv[])
34{
35 struct GNUNET_TIME_Absolute start;
36 struct GNUNET_CRYPTO_PaillierPublicKey public_key;
37 struct GNUNET_CRYPTO_PaillierPrivateKey private_key;
38 struct GNUNET_CRYPTO_PaillierCiphertext c1;
39 gcry_mpi_t m1;
40 unsigned int i;
41
42 start = GNUNET_TIME_absolute_get ();
43 for (i = 0; i < 10; i++)
44 GNUNET_CRYPTO_paillier_create (&public_key,
45 &private_key);
46 printf ("10x key generation took %s\n",
47 GNUNET_STRINGS_relative_time_to_string (
48 GNUNET_TIME_absolute_get_duration (start),
49 GNUNET_YES));
50 GAUGER ("UTIL", "Paillier key generation",
51 64 * 1024 / (1
52 + GNUNET_TIME_absolute_get_duration
53 (start).rel_value_us / 1000LL), "keys/ms");
54
55 m1 = gcry_mpi_new (0);
56 m1 = gcry_mpi_set_ui (m1, 1);
57 /* m1 = m1 * 2 ^ (GCPB - 3) */
58 gcry_mpi_mul_2exp (m1,
59 m1,
60 GNUNET_CRYPTO_PAILLIER_BITS - 3);
61 start = GNUNET_TIME_absolute_get ();
62 for (i = 0; i < 10; i++)
63 GNUNET_CRYPTO_paillier_encrypt (&public_key,
64 m1,
65 2,
66 &c1);
67 printf ("10x encryption took %s\n",
68 GNUNET_STRINGS_relative_time_to_string (
69 GNUNET_TIME_absolute_get_duration (start),
70 GNUNET_YES));
71 GAUGER ("UTIL", "Paillier encryption",
72 64 * 1024 / (1
73 + GNUNET_TIME_absolute_get_duration
74 (start).rel_value_us / 1000LL), "ops/ms");
75
76 start = GNUNET_TIME_absolute_get ();
77 for (i = 0; i < 10; i++)
78 GNUNET_CRYPTO_paillier_decrypt (&private_key,
79 &public_key,
80 &c1,
81 m1);
82 printf ("10x decryption took %s\n",
83 GNUNET_STRINGS_relative_time_to_string (
84 GNUNET_TIME_absolute_get_duration (start),
85 GNUNET_YES));
86 GAUGER ("UTIL", "Paillier decryption",
87 64 * 1024 / (1
88 + GNUNET_TIME_absolute_get_duration
89 (start).rel_value_us / 1000LL), "ops/ms");
90
91
92 return 0;
93}
94
95
96/* end of perf_crypto_paillier.c */
diff --git a/src/lib/util/perf_crypto_rsa.c b/src/lib/util/perf_crypto_rsa.c
new file mode 100644
index 000000000..721973b1a
--- /dev/null
+++ b/src/lib/util/perf_crypto_rsa.c
@@ -0,0 +1,211 @@
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/**
22 * @author Christian Grothoff
23 * @file util/perf_crypto_rsa.c
24 * @brief measure performance of RSA signing
25 */
26
27#include "platform.h"
28#include "gnunet_util_lib.h"
29#include <gauger.h>
30
31
32/**
33 * Evaluate RSA performance.
34 *
35 * @param len keylength to evaluate with
36 */
37static void
38eval (unsigned int len)
39{
40 struct GNUNET_TIME_Absolute start;
41 struct GNUNET_CRYPTO_RsaSignature *sig;
42 struct GNUNET_CRYPTO_RsaSignature *rsig;
43 struct GNUNET_CRYPTO_RsaPublicKey *public_key;
44 struct GNUNET_CRYPTO_RsaPrivateKey *private_key;
45 struct GNUNET_CRYPTO_RsaBlindingKeySecret bsec[10];
46 unsigned int i;
47 char sbuf[128];
48 struct GNUNET_HashCode hc;
49 struct GNUNET_CRYPTO_RsaBlindedMessage bm;
50
51 start = GNUNET_TIME_absolute_get ();
52 for (i = 0; i < 10; i++)
53 {
54 private_key = GNUNET_CRYPTO_rsa_private_key_create (len);
55 GNUNET_CRYPTO_rsa_private_key_free (private_key);
56 }
57 printf ("10x %u-key generation took %s\n",
58 len,
59 GNUNET_STRINGS_relative_time_to_string (
60 GNUNET_TIME_absolute_get_duration (start),
61 GNUNET_YES));
62 GNUNET_snprintf (sbuf,
63 sizeof(sbuf),
64 "RSA %u-key generation",
65 len);
66 GAUGER ("UTIL", sbuf,
67 64 * 1024 / (1
68 + GNUNET_TIME_absolute_get_duration
69 (start).rel_value_us / 1000LL), "keys/ms");
70 private_key = GNUNET_CRYPTO_rsa_private_key_create (len);
71 public_key = GNUNET_CRYPTO_rsa_private_key_get_public (private_key);
72 for (i = 0; i < 10; i++)
73 GNUNET_CRYPTO_random_block (GNUNET_CRYPTO_QUALITY_WEAK,
74 &bsec[i], sizeof(bsec[0]));
75 /*
76 start = GNUNET_TIME_absolute_get ();
77 for (i=0;i<10;i++)
78 rsa_blinding_key_derive(public_key, &bsec[i]);
79 printf ("10x %u-blinding key generation took %s\n",
80 len,
81 GNUNET_STRINGS_relative_time_to_string (GNUNET_TIME_absolute_get_duration (start),
82 GNUNET_YES));
83 GNUNET_snprintf (sbuf,
84 sizeof (sbuf),
85 "RSA %u-blinding key generation",
86 len);
87 GAUGER ("UTIL", sbuf,
88 64 * 1024 / (1 +
89 GNUNET_TIME_absolute_get_duration
90 (start).rel_value_us / 1000LL), "keys/ms");
91 */
92 start = GNUNET_TIME_absolute_get ();
93 GNUNET_CRYPTO_hash ("test", 4, &hc);
94 for (i = 0; i < 10; i++)
95 {
96 GNUNET_CRYPTO_rsa_blind (&hc,
97 sizeof (hc),
98 &bsec[i],
99 public_key,
100 &bm);
101 GNUNET_CRYPTO_rsa_blinded_message_free (&bm);
102 }
103 printf ("10x %u-blinding took %s\n",
104 len,
105 GNUNET_STRINGS_relative_time_to_string (
106 GNUNET_TIME_absolute_get_duration (start),
107 true));
108 GNUNET_snprintf (sbuf,
109 sizeof(sbuf),
110 "RSA %u-blinding",
111 len);
112 GAUGER ("UTIL",
113 sbuf,
114 64 * 1024 / (1
115 + GNUNET_TIME_absolute_get_duration
116 (start).rel_value_us / 1000LL), "ops/ms");
117 GNUNET_CRYPTO_rsa_blind (&hc,
118 sizeof (hc),
119 &bsec[0],
120 public_key,
121 &bm);
122 start = GNUNET_TIME_absolute_get ();
123 for (i = 0; i < 10; i++)
124 {
125 sig = GNUNET_CRYPTO_rsa_sign_blinded (private_key,
126 &bm);
127 GNUNET_CRYPTO_rsa_signature_free (sig);
128 }
129 printf ("10x %u-signing took %s\n",
130 len,
131 GNUNET_STRINGS_relative_time_to_string (
132 GNUNET_TIME_absolute_get_duration (start),
133 GNUNET_YES));
134 GNUNET_snprintf (sbuf,
135 sizeof(sbuf),
136 "RSA %u-signing",
137 len);
138 GAUGER ("UTIL",
139 sbuf,
140 64 * 1024 / (1
141 + GNUNET_TIME_absolute_get_duration
142 (start).rel_value_us / 1000LL), "ops/ms");
143 sig = GNUNET_CRYPTO_rsa_sign_blinded (private_key,
144 &bm);
145 start = GNUNET_TIME_absolute_get ();
146 for (i = 0; i < 10; i++)
147 {
148 rsig = GNUNET_CRYPTO_rsa_unblind (sig,
149 &bsec[0],
150 public_key);
151 GNUNET_CRYPTO_rsa_signature_free (rsig);
152 }
153 printf ("10x %u-unblinding took %s\n",
154 len,
155 GNUNET_STRINGS_relative_time_to_string (
156 GNUNET_TIME_absolute_get_duration (start),
157 true));
158 GNUNET_snprintf (sbuf,
159 sizeof(sbuf),
160 "RSA %u-unblinding",
161 len);
162 GAUGER ("UTIL",
163 sbuf,
164 64 * 1024 / (1
165 + GNUNET_TIME_absolute_get_duration
166 (start).rel_value_us / 1000LL), "ops/ms");
167 rsig = GNUNET_CRYPTO_rsa_unblind (sig,
168 &bsec[0],
169 public_key);
170 start = GNUNET_TIME_absolute_get ();
171 for (i = 0; i < 10; i++)
172 {
173 GNUNET_assert (GNUNET_OK ==
174 GNUNET_CRYPTO_rsa_verify (&hc,
175 sizeof (hc),
176 rsig,
177 public_key));
178 }
179 printf ("10x %u-verifying took %s\n",
180 len,
181 GNUNET_STRINGS_relative_time_to_string (
182 GNUNET_TIME_absolute_get_duration (start),
183 GNUNET_YES));
184 GNUNET_snprintf (sbuf,
185 sizeof(sbuf),
186 "RSA %u-verification",
187 len);
188 GAUGER ("UTIL",
189 sbuf,
190 64 * 1024 / (1
191 + GNUNET_TIME_absolute_get_duration
192 (start).rel_value_us / 1000LL), "ops/ms");
193 GNUNET_CRYPTO_rsa_signature_free (sig);
194 GNUNET_CRYPTO_rsa_public_key_free (public_key);
195 GNUNET_CRYPTO_rsa_private_key_free (private_key);
196 GNUNET_CRYPTO_rsa_blinded_message_free (&bm);
197}
198
199
200int
201main (int argc, char *argv[])
202{
203 eval (1024);
204 eval (2048);
205 eval (3072);
206 eval (4096);
207 return 0;
208}
209
210
211/* end of perf_crypto_rsa.c */
diff --git a/src/lib/util/perf_crypto_symmetric.c b/src/lib/util/perf_crypto_symmetric.c
new file mode 100644
index 000000000..9be452015
--- /dev/null
+++ b/src/lib/util/perf_crypto_symmetric.c
@@ -0,0 +1,78 @@
1/*
2 This file is part of GNUnet.
3 Copyright (C) 2002, 2003, 2004, 2006 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 * @author Christian Grothoff
23 * @file util/perf_crypto_symmetric.c
24 * @brief measure performance of encryption function
25 */
26
27#include "platform.h"
28#include "gnunet_util_lib.h"
29#include <gauger.h>
30
31
32static void
33perfEncrypt ()
34{
35 unsigned int i;
36 char buf[64 * 1024];
37 char rbuf[64 * 1024];
38 struct GNUNET_CRYPTO_SymmetricSessionKey sk;
39 struct GNUNET_CRYPTO_SymmetricInitializationVector iv;
40
41 GNUNET_CRYPTO_symmetric_create_session_key (&sk);
42
43 memset (buf, 1, sizeof(buf));
44 for (i = 0; i < 1024; i++)
45 {
46 memset (&iv, (int8_t) i, sizeof(iv));
47 GNUNET_CRYPTO_symmetric_encrypt (buf, sizeof(buf),
48 &sk, &iv,
49 rbuf);
50 GNUNET_CRYPTO_symmetric_decrypt (rbuf, sizeof(buf),
51 &sk, &iv,
52 buf);
53 }
54 memset (rbuf, 1, sizeof(rbuf));
55 GNUNET_assert (0 == memcmp (rbuf, buf, sizeof(buf)));
56}
57
58
59int
60main (int argc, char *argv[])
61{
62 struct GNUNET_TIME_Absolute start;
63
64 start = GNUNET_TIME_absolute_get ();
65 perfEncrypt ();
66 printf ("Encrypt perf took %s\n",
67 GNUNET_STRINGS_relative_time_to_string (
68 GNUNET_TIME_absolute_get_duration (start),
69 GNUNET_YES));
70 GAUGER ("UTIL", "Symmetric encryption",
71 64 * 1024 / (1
72 + GNUNET_TIME_absolute_get_duration
73 (start).rel_value_us / 1000LL), "kb/ms");
74 return 0;
75}
76
77
78/* end of perf_crypto_aes.c */
diff --git a/src/lib/util/perf_malloc.c b/src/lib/util/perf_malloc.c
new file mode 100644
index 000000000..48a4a2ae7
--- /dev/null
+++ b/src/lib/util/perf_malloc.c
@@ -0,0 +1,98 @@
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 * @author Christian Grothoff
23 * @file util/perf_malloc.c
24 * @brief measure performance of allocation functions
25 */
26
27#include "platform.h"
28#include "gnunet_util_lib.h"
29#include <gauger.h>
30
31static uint64_t
32perf_malloc ()
33{
34 uint64_t ret;
35
36 ret = 0;
37 for (size_t i = 1; i < 1024 * 1024; i += 1024)
38 {
39 ret += i;
40 GNUNET_free_nz (GNUNET_malloc (i));
41 }
42 return ret;
43}
44
45
46static uint64_t
47perf_realloc ()
48{
49 uint64_t ret;
50
51 ret = 0;
52 for (size_t i = 10; i < 1024 * 1024 / 5; i += 1024)
53 {
54 char *ptr;
55
56 ret += i;
57 ptr = GNUNET_malloc (i);
58 memset (ptr, 1, i);
59 ptr = GNUNET_realloc (ptr, i + 5);
60 for (size_t j = 0; j<i; j++)
61 GNUNET_assert (1 == ptr[j]);
62 memset (ptr, 6, i + 5);
63 ptr = GNUNET_realloc (ptr, i - 5);
64 for (size_t j = 0; j<i - 5; j++)
65 GNUNET_assert (6 == ptr[j]);
66 GNUNET_free (ptr);
67 }
68 return ret;
69}
70
71
72int
73main (int argc, char *argv[])
74{
75 struct GNUNET_TIME_Absolute start;
76 uint64_t kb;
77
78 start = GNUNET_TIME_absolute_get ();
79 kb = perf_malloc ();
80 printf ("Malloc perf took %s\n",
81 GNUNET_STRINGS_relative_time_to_string (
82 GNUNET_TIME_absolute_get_duration (start),
83 GNUNET_YES));
84 GAUGER ("UTIL", "Allocation",
85 kb / 1024 / (1
86 + GNUNET_TIME_absolute_get_duration
87 (start).rel_value_us / 1000LL), "kb/ms");
88 start = GNUNET_TIME_absolute_get ();
89 kb = perf_realloc ();
90 printf ("Realloc perf took %s\n",
91 GNUNET_STRINGS_relative_time_to_string (
92 GNUNET_TIME_absolute_get_duration (start),
93 GNUNET_YES));
94 return 0;
95}
96
97
98/* end of perf_malloc.c */
diff --git a/src/lib/util/perf_mq.c b/src/lib/util/perf_mq.c
new file mode 100644
index 000000000..5c956e4e9
--- /dev/null
+++ b/src/lib/util/perf_mq.c
@@ -0,0 +1,301 @@
1/*
2 This file is part of GNUnet.
3 Copyright (C) 2012, 2018, 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/perf_mq.c
23 * @brief benchmark for mq
24 * @author Florian Dold
25 * @author Christian Grothoff
26 */
27
28#include "platform.h"
29#include "gnunet_util_lib.h"
30#include <gauger.h>
31
32#define NUM_TRANSMISSIONS 1000000
33
34/**
35 * How long does the receiver take per message?
36 */
37#define RECEIVER_THROTTLE GNUNET_TIME_relative_multiply ( \
38 GNUNET_TIME_UNIT_MILLISECONDS, 1)
39
40static unsigned int received_cnt;
41
42
43GNUNET_NETWORK_STRUCT_BEGIN
44
45struct MyMessage
46{
47 struct GNUNET_MessageHeader header;
48 uint32_t x GNUNET_PACKED;
49};
50
51GNUNET_NETWORK_STRUCT_END
52
53static int global_ret;
54
55static struct GNUNET_SCHEDULER_Task *task;
56
57static struct GNUNET_MQ_Handle *cmq;
58
59
60static void
61do_shutdown (void *cls)
62{
63 (void) cls;
64 if (NULL != task)
65 {
66 GNUNET_SCHEDULER_cancel (task);
67 task = NULL;
68 }
69 if (NULL != cmq)
70 {
71 GNUNET_MQ_destroy (cmq);
72 cmq = NULL;
73 }
74}
75
76
77/**
78 * Generic error handler, called with the appropriate
79 * error code and the same closure specified at the creation of
80 * the message queue.
81 * Not every message queue implementation supports an error handler.
82 *
83 * @param cls closure
84 * @param error error code
85 */
86static void
87error_cb (void *cls,
88 enum GNUNET_MQ_Error error)
89{
90 GNUNET_break (0);
91 global_ret = 3;
92 GNUNET_SCHEDULER_shutdown ();
93}
94
95
96static void
97handle_dummy (void *cls,
98 const struct MyMessage *msg)
99{
100 struct GNUNET_SERVICE_Client *c = cls;
101
102 GNUNET_SERVICE_client_continue (c);
103 if (received_cnt != ntohl (msg->x))
104 {
105 GNUNET_break (0);
106 global_ret = 4;
107 GNUNET_SCHEDULER_shutdown ();
108 }
109 received_cnt++;
110}
111
112
113static void
114handle_dummy2 (void *cls,
115 const struct MyMessage *msg)
116{
117 struct GNUNET_SERVICE_Client *c = cls;
118
119 GNUNET_SERVICE_client_continue (c);
120 if (NUM_TRANSMISSIONS != received_cnt)
121 {
122 GNUNET_break (0);
123 global_ret = 5;
124 }
125 GNUNET_SCHEDULER_shutdown ();
126}
127
128
129static void
130do_send (void *cls);
131
132
133/**
134 * Function called whenever MQ has sent a message.
135 */
136static void
137notify_sent_cb (void *cls)
138{
139 static unsigned int seen;
140 unsigned int *cnt = cls;
141
142 if (seen != *cnt)
143 {
144 GNUNET_break (0);
145 global_ret = 6;
146 GNUNET_SCHEDULER_shutdown ();
147 }
148 seen++;
149 GNUNET_free (cnt);
150 task = GNUNET_SCHEDULER_add_now (&do_send,
151 NULL);
152}
153
154
155static void
156do_send (void *cls)
157{
158 static unsigned int i = 0;
159 unsigned int *cnt;
160 struct GNUNET_MQ_Envelope *env;
161 struct MyMessage *m;
162
163 task = NULL;
164 if (NUM_TRANSMISSIONS == i)
165 {
166 env = GNUNET_MQ_msg (m,
167 GNUNET_MESSAGE_TYPE_DUMMY2);
168 GNUNET_MQ_send (cmq,
169 env);
170 return;
171 }
172 cnt = GNUNET_new (unsigned int);
173 *cnt = i;
174 env = GNUNET_MQ_msg (m,
175 GNUNET_MESSAGE_TYPE_DUMMY);
176 GNUNET_MQ_notify_sent (env,
177 &notify_sent_cb,
178 cnt);
179 m->x = htonl (i);
180 GNUNET_MQ_send (cmq,
181 env);
182 i++;
183}
184
185
186/**
187 * Start running the actual test.
188 *
189 * @param cls closure passed to #GNUNET_SERVICE_MAIN
190 * @param cfg configuration to use for this service
191 * @param sh handle to the newly create service
192 */
193static void
194run (void *cls,
195 const struct GNUNET_CONFIGURATION_Handle *cfg,
196 struct GNUNET_SERVICE_Handle *sh)
197{
198 struct GNUNET_MQ_MessageHandler ch[] = {
199 GNUNET_MQ_handler_end ()
200 };
201
202 (void) cls;
203 (void) sh;
204 cmq = GNUNET_CLIENT_connect (cfg,
205 "test_client",
206 ch,
207 &error_cb,
208 NULL);
209 GNUNET_SCHEDULER_add_shutdown (&do_shutdown,
210 NULL);
211 task = GNUNET_SCHEDULER_add_now (&do_send,
212 NULL);
213}
214
215
216/**
217 * Callback to be called when a client connects to the service.
218 *
219 * @param cls closure for the service
220 * @param c the new client that connected to the service
221 * @param mq the message queue used to send messages to the client
222 * @return the client-specific (`internal') closure
223 */
224static void *
225connect_cb (void *cls,
226 struct GNUNET_SERVICE_Client *c,
227 struct GNUNET_MQ_Handle *mq)
228{
229 (void) cls;
230 (void) mq;
231 return c;
232}
233
234
235/**
236 * Callback to be called when a client disconnected from the service
237 *
238 * @param cls closure for the service
239 * @param c the client that disconnected
240 * @param internal_cls the client-specific (`internal') closure
241 */
242static void
243disconnect_cb (void *cls,
244 struct GNUNET_SERVICE_Client *c,
245 void *internal_cls)
246{
247 (void) cls;
248 (void) c;
249 (void) internal_cls;
250}
251
252
253int
254main (int argc, char **argv)
255{
256 struct GNUNET_TIME_Absolute start;
257 char *test_argv[] = {
258 (char *) "test_client",
259 "-c",
260 "test_client_data.conf",
261 NULL
262 };
263 struct GNUNET_MQ_MessageHandler mh[] = {
264 GNUNET_MQ_hd_fixed_size (dummy,
265 GNUNET_MESSAGE_TYPE_DUMMY,
266 struct MyMessage,
267 NULL),
268 GNUNET_MQ_hd_fixed_size (dummy2,
269 GNUNET_MESSAGE_TYPE_DUMMY2,
270 struct MyMessage,
271 NULL),
272 GNUNET_MQ_handler_end ()
273 };
274
275 (void) argc;
276 (void) argv;
277 GNUNET_log_setup ("perf-mq",
278 "INFO",
279 NULL);
280 start = GNUNET_TIME_absolute_get ();
281 if (0 !=
282 GNUNET_SERVICE_run_ (3,
283 test_argv,
284 "test_client",
285 GNUNET_SERVICE_OPTION_NONE,
286 &run,
287 &connect_cb,
288 &disconnect_cb,
289 NULL,
290 mh))
291 return 1;
292 printf ("Scheduler perf took %s\n",
293 GNUNET_STRINGS_relative_time_to_string (
294 GNUNET_TIME_absolute_get_duration (start),
295 GNUNET_YES));
296 GAUGER ("UTIL", "Scheduler",
297 received_cnt / 1024 / (1
298 + GNUNET_TIME_absolute_get_duration
299 (start).rel_value_us / 1000LL), "kmsg/ms");
300 return global_ret;
301}
diff --git a/src/lib/util/perf_scheduler.c b/src/lib/util/perf_scheduler.c
new file mode 100644
index 000000000..af084e04a
--- /dev/null
+++ b/src/lib/util/perf_scheduler.c
@@ -0,0 +1,104 @@
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 * @author Christian Grothoff
22 * @file util/perf_scheduler.c
23 * @brief measure performance of scheduler functions
24 */
25
26#include "platform.h"
27#include "gnunet_util_lib.h"
28#include <gauger.h>
29
30#define RUNS (1024 * 1024)
31
32static struct GNUNET_SCHEDULER_Task *task;
33
34
35static void
36run (void *cls)
37{
38 uint64_t *count = cls;
39
40 task = NULL;
41 (*count)++;
42 if (*count >= RUNS)
43 {
44 GNUNET_SCHEDULER_shutdown ();
45 return;
46 }
47 task = GNUNET_SCHEDULER_add_now (&run,
48 count);
49}
50
51
52static void
53do_shutdown (void *cls)
54{
55 if (NULL != task)
56 GNUNET_SCHEDULER_cancel (task);
57}
58
59
60static void
61first (void *cls)
62{
63 uint64_t *count = cls;
64
65 (*count)++;
66 task = GNUNET_SCHEDULER_add_now (&run,
67 count);
68 GNUNET_SCHEDULER_add_shutdown (&do_shutdown,
69 NULL);
70}
71
72
73static uint64_t
74perf_scheduler ()
75{
76 uint64_t count = 0;
77
78 GNUNET_SCHEDULER_run (&first,
79 &count);
80 return count;
81}
82
83
84int
85main (int argc, char *argv[])
86{
87 struct GNUNET_TIME_Absolute start;
88 uint64_t tasks;
89
90 start = GNUNET_TIME_absolute_get ();
91 tasks = perf_scheduler ();
92 printf ("Scheduler perf took %s\n",
93 GNUNET_STRINGS_relative_time_to_string (
94 GNUNET_TIME_absolute_get_duration (start),
95 GNUNET_YES));
96 GAUGER ("UTIL", "Scheduler",
97 tasks / 1024 / (1
98 + GNUNET_TIME_absolute_get_duration
99 (start).rel_value_us / 1000LL), "tasks/ms");
100 return 0;
101}
102
103
104/* end of perf_scheduler.c */
diff --git a/src/lib/util/plugin.c b/src/lib/util/plugin.c
new file mode 100644
index 000000000..62c2a1df0
--- /dev/null
+++ b/src/lib/util/plugin.c
@@ -0,0 +1,403 @@
1/*
2 This file is part of GNUnet
3 Copyright (C) 2002-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/plugin.c
23 * @brief Methods to access plugins
24 * @author Christian Grothoff
25 */
26
27
28#include "platform.h"
29#include <ltdl.h>
30#include "gnunet_util_lib.h"
31
32#define LOG(kind, ...) GNUNET_log_from (kind, "util-plugin", __VA_ARGS__)
33
34/**
35 * Linked list of active plugins.
36 */
37struct PluginList
38{
39 /**
40 * This is a linked list.
41 */
42 struct PluginList *next;
43
44 /**
45 * Name of the library.
46 */
47 char *name;
48
49 /**
50 * System handle.
51 */
52 void *handle;
53};
54
55
56/**
57 * Have we been initialized?
58 */
59static int initialized;
60
61/**
62 * Libtool search path before we started.
63 */
64static char *old_dlsearchpath;
65
66/**
67 * List of plugins we have loaded.
68 */
69static struct PluginList *plugins;
70
71
72/**
73 * Setup libtool paths.
74 */
75static void
76plugin_init (void)
77{
78 int err;
79 const char *opath;
80 char *path;
81 char *cpath;
82
83 err = lt_dlinit ();
84 if (err > 0)
85 {
86 fprintf (stderr,
87 _ ("Initialization of plugin mechanism failed: %s!\n"),
88 lt_dlerror ());
89 return;
90 }
91 opath = lt_dlgetsearchpath ();
92 if (NULL != opath)
93 old_dlsearchpath = GNUNET_strdup (opath);
94 path = GNUNET_OS_installation_get_path (GNUNET_OS_IPK_LIBDIR);
95 if (NULL != path)
96 {
97 if (NULL != opath)
98 {
99 GNUNET_asprintf (&cpath,
100 "%s:%s",
101 opath,
102 path);
103 lt_dlsetsearchpath (cpath);
104 GNUNET_free (path);
105 GNUNET_free (cpath);
106 }
107 else
108 {
109 lt_dlsetsearchpath (path);
110 GNUNET_free (path);
111 }
112 }
113}
114
115
116/**
117 * Shutdown libtool.
118 */
119static void
120plugin_fini (void)
121{
122 lt_dlsetsearchpath (old_dlsearchpath);
123 if (NULL != old_dlsearchpath)
124 {
125 GNUNET_free (old_dlsearchpath);
126 old_dlsearchpath = NULL;
127 }
128 if (NULL == getenv ("VALGRINDING_PLUGINS"))
129 lt_dlexit ();
130}
131
132
133/**
134 * Lookup a function in the plugin.
135 *
136 * @param plug the plugin to check
137 * @param name name of the symbol to look for
138 * @return NULL if the symbol was not found
139 */
140static GNUNET_PLUGIN_Callback
141resolve_function (struct PluginList *plug,
142 const char *name)
143{
144 char *initName;
145 void *mptr;
146
147 GNUNET_asprintf (&initName,
148 "_%s_%s",
149 plug->name,
150 name);
151 mptr = lt_dlsym (plug->handle,
152 &initName[1]);
153 if (NULL == mptr)
154 mptr = lt_dlsym (plug->handle,
155 initName);
156 if (NULL == mptr)
157 LOG (GNUNET_ERROR_TYPE_ERROR,
158 _ ("`%s' failed to resolve method '%s' with error: %s\n"),
159 "lt_dlsym",
160 &initName[1],
161 lt_dlerror ());
162 GNUNET_free (initName);
163 return mptr;
164}
165
166
167enum GNUNET_GenericReturnValue
168GNUNET_PLUGIN_test (const char *library_name)
169{
170 void *libhandle;
171 GNUNET_PLUGIN_Callback init;
172 struct PluginList plug;
173
174 if (! initialized)
175 {
176 initialized = GNUNET_YES;
177 plugin_init ();
178 }
179 libhandle = lt_dlopenext (library_name);
180 if (NULL == libhandle)
181 return GNUNET_NO;
182 plug.handle = libhandle;
183 plug.name = (char *) library_name;
184 init = resolve_function (&plug,
185 "init");
186 if (NULL == init)
187 {
188 GNUNET_break (0);
189 lt_dlclose (libhandle);
190 return GNUNET_NO;
191 }
192 lt_dlclose (libhandle);
193 return GNUNET_YES;
194}
195
196
197void *
198GNUNET_PLUGIN_load (const char *library_name,
199 void *arg)
200{
201 void *libhandle;
202 struct PluginList *plug;
203 GNUNET_PLUGIN_Callback init;
204 void *ret;
205
206 if (! initialized)
207 {
208 initialized = GNUNET_YES;
209 plugin_init ();
210 }
211 libhandle = lt_dlopenext (library_name);
212 if (NULL == libhandle)
213 {
214 LOG (GNUNET_ERROR_TYPE_ERROR,
215 _ ("`%s' failed for library `%s' with error: %s\n"),
216 "lt_dlopenext",
217 library_name,
218 lt_dlerror ());
219 return NULL;
220 }
221 plug = GNUNET_new (struct PluginList);
222 plug->handle = libhandle;
223 plug->name = GNUNET_strdup (library_name);
224 plug->next = plugins;
225 plugins = plug;
226 init = resolve_function (plug,
227 "init");
228 if ( (NULL == init) ||
229 (NULL == (ret = init (arg))) )
230 {
231 lt_dlclose (libhandle);
232 GNUNET_free (plug->name);
233 plugins = plug->next;
234 GNUNET_free (plug);
235 return NULL;
236 }
237 return ret;
238}
239
240
241void *
242GNUNET_PLUGIN_unload (const char *library_name,
243 void *arg)
244{
245 struct PluginList *pos;
246 struct PluginList *prev;
247 GNUNET_PLUGIN_Callback done;
248 void *ret;
249
250 prev = NULL;
251 pos = plugins;
252 while ( (NULL != pos) &&
253 (0 != strcmp (pos->name,
254 library_name)) )
255 {
256 prev = pos;
257 pos = pos->next;
258 }
259 if (NULL == pos)
260 return NULL;
261
262 done = resolve_function (pos,
263 "done");
264 ret = NULL;
265 if (NULL == prev)
266 plugins = pos->next;
267 else
268 prev->next = pos->next;
269 if (NULL != done)
270 ret = done (arg);
271 if (NULL == getenv ("VALGRINDING_PLUGINS"))
272 lt_dlclose (pos->handle);
273 GNUNET_free (pos->name);
274 GNUNET_free (pos);
275 if (NULL == plugins)
276 {
277 plugin_fini ();
278 initialized = GNUNET_NO;
279 }
280 return ret;
281}
282
283
284/**
285 * Closure for #find_libraries().
286 */
287struct LoadAllContext
288{
289 /**
290 * Prefix the plugin names we find have to match.
291 */
292 const char *basename;
293
294 /**
295 * Argument to give to 'init' when loading the plugin.
296 */
297 void *arg;
298
299 /**
300 * Function to call for each plugin.
301 */
302 GNUNET_PLUGIN_LoaderCallback cb;
303
304 /**
305 * Closure for @e cb
306 */
307 void *cb_cls;
308};
309
310
311/**
312 * Function called on each plugin in the directory. Loads
313 * the plugins that match the given basename.
314 *
315 * @param cls the `struct LoadAllContext` describing which
316 * plugins to load and what to do with them
317 * @param filename name of a plugin library to check
318 * @return #GNUNET_OK (continue loading)
319 */
320static int
321find_libraries (void *cls,
322 const char *filename)
323{
324 struct LoadAllContext *lac = cls;
325 const char *slashpos;
326 const char *libname;
327 char *basename;
328 char *dot;
329 void *lib_ret;
330 size_t n;
331
332 libname = filename;
333 while (NULL != (slashpos = strstr (libname,
334 DIR_SEPARATOR_STR)))
335 libname = slashpos + 1;
336 n = strlen (libname);
337 if (0 != strncmp (lac->basename,
338 libname,
339 strlen (lac->basename)))
340 return GNUNET_OK; /* wrong name */
341 if ( (n > 3) &&
342 (0 == strcmp (&libname[n - 3], ".la")) )
343 return GNUNET_OK; /* .la file */
344 basename = GNUNET_strdup (libname);
345 if (NULL != (dot = strstr (basename, ".")))
346 *dot = '\0';
347 lib_ret = GNUNET_PLUGIN_load (basename,
348 lac->arg);
349 if (NULL != lib_ret)
350 lac->cb (lac->cb_cls,
351 basename,
352 lib_ret);
353 GNUNET_free (basename);
354 return GNUNET_OK;
355}
356
357
358void
359GNUNET_PLUGIN_load_all (const char *basename,
360 void *arg,
361 GNUNET_PLUGIN_LoaderCallback cb,
362 void *cb_cls)
363{
364 struct LoadAllContext lac;
365 char *path;
366
367 path = GNUNET_OS_installation_get_path (GNUNET_OS_IPK_LIBDIR);
368 if (NULL == path)
369 {
370 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
371 _ ("Could not determine plugin installation path.\n"));
372 return;
373 }
374 lac.basename = basename;
375 lac.arg = arg;
376 lac.cb = cb;
377 lac.cb_cls = cb_cls;
378 GNUNET_DISK_directory_scan (path,
379 &find_libraries,
380 &lac);
381 GNUNET_free (path);
382}
383
384
385void
386GNUNET_PLUGIN_load_all_in_context (const struct GNUNET_OS_ProjectData *ctx,
387 const char *basename,
388 void *arg,
389 GNUNET_PLUGIN_LoaderCallback cb,
390 void *cb_cls)
391{
392 const struct GNUNET_OS_ProjectData *cpd = GNUNET_OS_project_data_get ();
393
394 GNUNET_OS_init (ctx);
395 GNUNET_PLUGIN_load_all (basename,
396 arg,
397 cb,
398 cb_cls);
399 GNUNET_OS_init (cpd);
400}
401
402
403/* end of plugin.c */
diff --git a/src/lib/util/proc_compat.c b/src/lib/util/proc_compat.c
new file mode 100644
index 000000000..0423a0d5c
--- /dev/null
+++ b/src/lib/util/proc_compat.c
@@ -0,0 +1,50 @@
1/*
2 This file is part of GNUnet.
3 Copyright (C) 2001, 2002, 2003, 2004, 2005 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/**
23 * @author Martin Schanzenbach
24 *
25 * @file util/proc_compat.c
26 * Definitions for macOS and Win32
27 */
28
29#include "platform.h"
30
31/**
32 * memrchr as defined in glibc
33 *
34 * @param s pointer to memory
35 * @param c character to search for
36 * @param n search character limit
37 */
38void*
39GN_memrchr_ (const void *s,
40 int c,
41 size_t n)
42{
43 const unsigned char *ucs = s;
44 ssize_t i;
45
46 for (i = n - 1; i >= 0; i--)
47 if (c == (int) ucs[i])
48 return (void *) &ucs[i];
49 return NULL;
50}
diff --git a/src/lib/util/program.c b/src/lib/util/program.c
new file mode 100644
index 000000000..2801b7650
--- /dev/null
+++ b/src/lib/util/program.c
@@ -0,0 +1,454 @@
1/*
2 This file is part of GNUnet.
3 Copyright (C) 2009-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 PURPROSE. 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/program.c
23 * @brief standard code for GNUnet startup and shutdown
24 * @author Christian Grothoff
25 */
26
27
28#include "platform.h"
29#include "gnunet_util_lib.h"
30#include "gnunet_resolver_service.h"
31#include "gnunet_constants.h"
32#include "speedup.h"
33#include <gcrypt.h>
34
35#define LOG(kind, ...) GNUNET_log_from (kind, "util-program", __VA_ARGS__)
36
37#define LOG_STRERROR_FILE(kind, syscall, filename) \
38 GNUNET_log_from_strerror_file (kind, "util-program", syscall, filename)
39
40/**
41 * Context for the command.
42 */
43struct CommandContext
44{
45 /**
46 * Argv argument.
47 */
48 char *const *args;
49
50 /**
51 * Name of the configuration file used, can be NULL!
52 */
53 char *cfgfile;
54
55 /**
56 * Main function to run.
57 */
58 GNUNET_PROGRAM_Main task;
59
60 /**
61 * Closure for @e task.
62 */
63 void *task_cls;
64
65 /**
66 * Configuration to use.
67 */
68 const struct GNUNET_CONFIGURATION_Handle *cfg;
69};
70
71
72/**
73 * task run when the scheduler shuts down
74 */
75static void
76shutdown_task (void *cls)
77{
78 (void) cls;
79 GNUNET_SPEEDUP_stop_ ();
80}
81
82
83/**
84 * Initial task called by the scheduler for each
85 * program. Runs the program-specific main task.
86 */
87static void
88program_main (void *cls)
89{
90 struct CommandContext *cc = cls;
91
92 GNUNET_SPEEDUP_start_ (cc->cfg);
93 GNUNET_SCHEDULER_add_shutdown (&shutdown_task,
94 NULL);
95 GNUNET_RESOLVER_connect (cc->cfg);
96 cc->task (cc->task_cls,
97 cc->args,
98 cc->cfgfile,
99 cc->cfg);
100}
101
102
103/**
104 * Compare function for 'qsort' to sort command-line arguments by the
105 * short option.
106 *
107 * @param a1 first command line option
108 * @param a2 second command line option
109 */
110static int
111cmd_sorter (const void *a1,
112 const void *a2)
113{
114 const struct GNUNET_GETOPT_CommandLineOption *c1 = a1;
115 const struct GNUNET_GETOPT_CommandLineOption *c2 = a2;
116
117 if (toupper ((unsigned char) c1->shortName) >
118 toupper ((unsigned char) c2->shortName))
119 return 1;
120 if (toupper ((unsigned char) c1->shortName) <
121 toupper ((unsigned char) c2->shortName))
122 return -1;
123 if (c1->shortName > c2->shortName)
124 return 1;
125 if (c1->shortName < c2->shortName)
126 return -1;
127 return 0;
128}
129
130
131enum GNUNET_GenericReturnValue
132GNUNET_PROGRAM_run2 (int argc,
133 char *const *argv,
134 const char *binaryName,
135 const char *binaryHelp,
136 const struct GNUNET_GETOPT_CommandLineOption *options,
137 GNUNET_PROGRAM_Main task,
138 void *task_cls,
139 int run_without_scheduler)
140{
141 struct CommandContext cc;
142
143#if ENABLE_NLS
144 char *path;
145#endif
146 char *loglev;
147 char *logfile;
148 char *cfg_fn;
149 enum GNUNET_GenericReturnValue ret;
150 int iret;
151 unsigned int cnt;
152 unsigned long long skew_offset;
153 unsigned long long skew_variance;
154 long long clock_offset;
155 struct GNUNET_CONFIGURATION_Handle *cfg;
156 const struct GNUNET_OS_ProjectData *pd = GNUNET_OS_project_data_get ();
157 const struct GNUNET_GETOPT_CommandLineOption defoptions[] = {
158 GNUNET_GETOPT_option_cfgfile (&cc.cfgfile),
159 GNUNET_GETOPT_option_help (binaryHelp),
160 GNUNET_GETOPT_option_loglevel (&loglev),
161 GNUNET_GETOPT_option_logfile (&logfile),
162 GNUNET_GETOPT_option_version (pd->version)
163 };
164 unsigned int deflen = sizeof(defoptions) / sizeof(defoptions[0]);
165 struct GNUNET_GETOPT_CommandLineOption *allopts;
166 const char *gargs;
167 char *lpfx;
168 char *spc;
169
170 logfile = NULL;
171 gargs = getenv ("GNUNET_ARGS");
172 if (NULL != gargs)
173 {
174 char **gargv;
175 unsigned int gargc;
176 char *cargs;
177
178 gargv = NULL;
179 gargc = 0;
180 for (int i = 0; i < argc; i++)
181 GNUNET_array_append (gargv,
182 gargc,
183 GNUNET_strdup (argv[i]));
184 cargs = GNUNET_strdup (gargs);
185 for (char *tok = strtok (cargs, " ");
186 NULL != tok;
187 tok = strtok (NULL, " "))
188 GNUNET_array_append (gargv, gargc, GNUNET_strdup (tok));
189 GNUNET_free (cargs);
190 GNUNET_array_append (gargv, gargc, NULL);
191 argv = (char *const *) gargv;
192 argc = gargc - 1;
193 }
194 memset (&cc, 0, sizeof(cc));
195 loglev = NULL;
196 cc.task = task;
197 cc.task_cls = task_cls;
198 cc.cfg = cfg = GNUNET_CONFIGURATION_create ();
199 /* prepare */
200#if ENABLE_NLS
201 if (NULL != pd->gettext_domain)
202 {
203 setlocale (LC_ALL, "");
204 path = (NULL == pd->gettext_path)
205 ? GNUNET_OS_installation_get_path (GNUNET_OS_IPK_LOCALEDIR)
206 : GNUNET_strdup (pd->gettext_path);
207 if (NULL != path)
208 {
209 bindtextdomain (pd->gettext_domain, path);
210 GNUNET_free (path);
211 }
212 textdomain (pd->gettext_domain);
213 }
214#endif
215 cnt = 0;
216 while (NULL != options[cnt].name)
217 cnt++;
218 allopts = GNUNET_new_array (cnt + deflen + 1,
219 struct GNUNET_GETOPT_CommandLineOption);
220 GNUNET_memcpy (allopts,
221 options,
222 cnt * sizeof(struct GNUNET_GETOPT_CommandLineOption));
223 {
224 unsigned int xtra = 0;
225
226 for (unsigned int i = 0;
227 i<sizeof (defoptions) / sizeof(struct GNUNET_GETOPT_CommandLineOption);
228 i++)
229 {
230 bool found = false;
231
232 for (unsigned int j = 0; j<cnt; j++)
233 {
234 found |= ( (options[j].shortName == defoptions[i].shortName) &&
235 (0 != options[j].shortName) );
236 found |= ( (NULL != options[j].name) &&
237 (NULL != defoptions[i].name) &&
238 (0 == strcmp (options[j].name,
239 defoptions[i].name)) );
240 if (found)
241 break;
242 }
243 if (found)
244 continue;
245 GNUNET_memcpy (&allopts[cnt + xtra],
246 &defoptions[i],
247 sizeof (struct GNUNET_GETOPT_CommandLineOption));
248 xtra++;
249 }
250 cnt += xtra;
251 }
252 qsort (allopts,
253 cnt,
254 sizeof(struct GNUNET_GETOPT_CommandLineOption),
255 &cmd_sorter);
256 loglev = NULL;
257 if ((NULL != pd->config_file) && (NULL != pd->user_config_file))
258 cfg_fn = GNUNET_CONFIGURATION_default_filename ();
259 else
260 cfg_fn = NULL;
261 lpfx = GNUNET_strdup (binaryName);
262 if (NULL != (spc = strstr (lpfx, " ")))
263 *spc = '\0';
264 iret = GNUNET_GETOPT_run (binaryName,
265 allopts,
266 (unsigned int) argc,
267 argv);
268 if ((GNUNET_OK > iret) ||
269 (GNUNET_OK != GNUNET_log_setup (lpfx,
270 loglev,
271 logfile)))
272 {
273 GNUNET_free (allopts);
274 GNUNET_free (lpfx);
275 ret = (enum GNUNET_GenericReturnValue) iret;
276 goto cleanup;
277 }
278 if (NULL != cc.cfgfile)
279 {
280 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
281 "Loading configuration from entry point specified as option (%s)\n",
282 cc.cfgfile);
283 if (GNUNET_YES !=
284 GNUNET_DISK_file_test (cc.cfgfile))
285 {
286 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
287 _ ("Unreadable configuration file `%s', exiting ...\n"),
288 cc.cfgfile);
289 ret = GNUNET_SYSERR;
290 GNUNET_free (allopts);
291 GNUNET_free (lpfx);
292 goto cleanup;
293 }
294 if (GNUNET_SYSERR ==
295 GNUNET_CONFIGURATION_load (cfg,
296 cc.cfgfile))
297 {
298 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
299 _ ("Malformed configuration file `%s', exiting ...\n"),
300 cc.cfgfile);
301 ret = GNUNET_SYSERR;
302 GNUNET_free (allopts);
303 GNUNET_free (lpfx);
304 goto cleanup;
305 }
306 }
307 else
308 {
309 if ( (NULL != cfg_fn) &&
310 (GNUNET_YES !=
311 GNUNET_DISK_file_test (cfg_fn)) )
312 {
313 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
314 _ ("Unreadable configuration file `%s'. Exiting ...\n"),
315 cfg_fn);
316 ret = GNUNET_SYSERR;
317 GNUNET_free (allopts);
318 GNUNET_free (lpfx);
319 goto cleanup;
320 }
321 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
322 "Loading configuration from entry point `%s'\n",
323 cc.cfgfile);
324 if (GNUNET_SYSERR ==
325 GNUNET_CONFIGURATION_load (cfg,
326 cfg_fn))
327 {
328 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
329 _ ("Malformed configuration. Exiting ...\n"));
330 ret = GNUNET_SYSERR;
331 GNUNET_free (allopts);
332 GNUNET_free (lpfx);
333 goto cleanup;
334 }
335 }
336 GNUNET_free (allopts);
337 GNUNET_free (lpfx);
338 if ((GNUNET_OK ==
339 GNUNET_CONFIGURATION_get_value_number (cc.cfg,
340 "testing",
341 "skew_offset",
342 &skew_offset)) &&
343 (GNUNET_OK ==
344 GNUNET_CONFIGURATION_get_value_number (cc.cfg,
345 "testing",
346 "skew_variance",
347 &skew_variance)))
348 {
349 clock_offset = skew_offset - skew_variance;
350 GNUNET_TIME_set_offset (clock_offset);
351 }
352 /* ARM needs to know which configuration file to use when starting
353 services. If we got a command-line option *and* if nothing is
354 specified in the configuration, remember the command-line option
355 in "cfg". This is typically really only having an effect if we
356 are running code in src/arm/, as obviously the rest of the code
357 has little business with ARM-specific options. */
358 if (GNUNET_YES !=
359 GNUNET_CONFIGURATION_have_value (cfg,
360 "arm",
361 "CONFIG"))
362 {
363 if (NULL != cc.cfgfile)
364 GNUNET_CONFIGURATION_set_value_string (cfg,
365 "arm",
366 "CONFIG",
367 cc.cfgfile);
368 else if (NULL != cfg_fn)
369 GNUNET_CONFIGURATION_set_value_string (cfg,
370 "arm",
371 "CONFIG",
372 cfg_fn);
373 }
374
375 /* run */
376 cc.args = &argv[iret];
377 if ((NULL == cc.cfgfile) && (NULL != cfg_fn))
378 cc.cfgfile = GNUNET_strdup (cfg_fn);
379 if (GNUNET_NO == run_without_scheduler)
380 {
381 GNUNET_SCHEDULER_run (&program_main, &cc);
382 }
383 else
384 {
385 GNUNET_RESOLVER_connect (cc.cfg);
386 cc.task (cc.task_cls, cc.args, cc.cfgfile, cc.cfg);
387 }
388 ret = GNUNET_OK;
389cleanup:
390 GNUNET_CONFIGURATION_destroy (cfg);
391 GNUNET_free (cc.cfgfile);
392 GNUNET_free (cfg_fn);
393 GNUNET_free (loglev);
394 GNUNET_free (logfile);
395 return ret;
396}
397
398
399enum GNUNET_GenericReturnValue
400GNUNET_PROGRAM_run (int argc,
401 char *const *argv,
402 const char *binaryName,
403 const char *binaryHelp,
404 const struct GNUNET_GETOPT_CommandLineOption *options,
405 GNUNET_PROGRAM_Main task,
406 void *task_cls)
407{
408 return GNUNET_PROGRAM_run2 (argc,
409 argv,
410 binaryName,
411 binaryHelp,
412 options,
413 task,
414 task_cls,
415 GNUNET_NO);
416}
417
418
419/* A list of daemons to be launched when GNUNET_main()
420 * is called
421 */
422struct DaemonHandleList
423{
424 /* DLL */
425 struct DaemonHandleList *prev;
426
427 /* DLL */
428 struct DaemonHandleList *next;
429
430 /* Program to launch */
431 GNUNET_PROGRAM_Main d;
432};
433
434/* The daemon list */
435static struct DaemonHandleList *hll_head = NULL;
436
437/* The daemon list */
438static struct DaemonHandleList *hll_tail = NULL;
439
440enum GNUNET_GenericReturnValue
441GNUNET_DAEMON_register (const char *daemon_name,
442 const char *daemon_help,
443 GNUNET_PROGRAM_Main task)
444{
445 struct DaemonHandleList *hle;
446
447 hle = GNUNET_new (struct DaemonHandleList);
448 hle->d = task;
449 GNUNET_CONTAINER_DLL_insert (hll_head, hll_tail, hle);
450 return GNUNET_OK;
451}
452
453
454/* end of program.c */
diff --git a/src/lib/util/regex.c b/src/lib/util/regex.c
new file mode 100644
index 000000000..282d3aa99
--- /dev/null
+++ b/src/lib/util/regex.c
@@ -0,0 +1,804 @@
1/*
2 This file is part of GNUnet
3 Copyright (C) 2012, 2013, 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 * @file src/tun/regex.c
22 * @brief functions to convert IP networks to regexes
23 * @author Maximilian Szengel
24 * @author Christian Grothoff
25 */
26
27#include "platform.h"
28#include "gnunet_util_lib.h"
29
30/**
31 * 'wildcard', matches all possible values (for HEX encoding).
32 */
33#define DOT "(0|1|2|3|4|5|6|7|8|9|A|B|C|D|E|F)"
34
35
36void
37GNUNET_TUN_ipv4toregexsearch (const struct in_addr *ip,
38 uint16_t port,
39 char *rxstr)
40{
41 GNUNET_snprintf (rxstr,
42 GNUNET_TUN_IPV4_REGEXLEN,
43 "4-%04X-%08X",
44 (unsigned int) port,
45 ntohl (ip->s_addr));
46}
47
48
49void
50GNUNET_TUN_ipv6toregexsearch (const struct in6_addr *ipv6,
51 uint16_t port,
52 char *rxstr)
53{
54 const uint32_t *addr;
55
56 addr = (const uint32_t *) ipv6;
57 GNUNET_snprintf (rxstr,
58 GNUNET_TUN_IPV6_REGEXLEN,
59 "6-%04X-%08X%08X%08X%08X",
60 (unsigned int) port,
61 ntohl (addr[0]),
62 ntohl (addr[1]),
63 ntohl (addr[2]),
64 ntohl (addr[3]));
65}
66
67
68/**
69 * Convert the given 4-bit (!) number to a regex.
70 *
71 * @param value the value, only the lowest 4 bits will be looked at
72 * @param mask which bits in value are wildcards (any value)?
73 */
74static char *
75nibble_to_regex (uint8_t value,
76 uint8_t mask)
77{
78 char *ret;
79
80 value &= mask;
81 switch (mask)
82 {
83 case 0:
84 return GNUNET_strdup (DOT);
85
86 case 8:
87 GNUNET_asprintf (&ret,
88 "(%X|%X|%X|%X|%X|%X|%X|%X)",
89 value,
90 value + 1,
91 value + 2,
92 value + 3,
93 value + 4,
94 value + 5,
95 value + 6,
96 value + 7);
97 return ret;
98
99 case 12:
100 GNUNET_asprintf (&ret,
101 "(%X|%X|%X|%X)",
102 value,
103 value + 1,
104 value + 2,
105 value + 3);
106 return ret;
107
108 case 14:
109 GNUNET_asprintf (&ret,
110 "(%X|%X)",
111 value,
112 value + 1);
113 return ret;
114
115 case 15:
116 GNUNET_asprintf (&ret,
117 "%X",
118 value);
119 return ret;
120
121 default:
122 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
123 _ ("Bad mask: %d\n"),
124 mask);
125 GNUNET_break (0);
126 return NULL;
127 }
128}
129
130
131/**
132 * Convert the given 16-bit number to a regex.
133 *
134 * @param value the value
135 * @param mask which bits in value are wildcards (any value)?
136 */
137static char *
138num_to_regex (uint16_t value,
139 uint16_t mask)
140{
141 const uint8_t *v = (const uint8_t *) &value;
142 const uint8_t *m = (const uint8_t *) &mask;
143 char *a;
144 char *b;
145 char *c;
146 char *d;
147 char *ret;
148
149 a = nibble_to_regex (v[0] >> 4, m[0] >> 4);
150 b = nibble_to_regex (v[0] & 15, m[0] & 15);
151 c = nibble_to_regex (v[1] >> 4, m[1] >> 4);
152 d = nibble_to_regex (v[1] & 15, m[1] & 15);
153 ret = NULL;
154 if ((NULL != a) &&
155 (NULL != b) &&
156 (NULL != c) &&
157 (NULL != d))
158 GNUNET_asprintf (&ret,
159 "%s%s%s%s",
160 a, b, c, d);
161 GNUNET_free (a);
162 GNUNET_free (b);
163 GNUNET_free (c);
164 GNUNET_free (d);
165 return ret;
166}
167
168
169/**
170 * Do we need to put parents around the given argument?
171 *
172 * @param arg part of a regular expression
173 * @return #GNUNET_YES if we should parens,
174 * #GNUNET_NO if not
175 */
176static int
177needs_parens (const char *arg)
178{
179 size_t off;
180 size_t len;
181 unsigned int op;
182
183 op = 0;
184 len = strlen (arg);
185 for (off = 0; off < len; off++)
186 {
187 switch (arg[off])
188 {
189 case '(':
190 op++;
191 break;
192
193 case ')':
194 GNUNET_assert (op > 0);
195 op--;
196 break;
197
198 case '|':
199 if (0 == op)
200 return GNUNET_YES;
201 break;
202
203 default:
204 break;
205 }
206 }
207 return GNUNET_NO;
208}
209
210
211/**
212 * Compute port policy for the given range of
213 * port numbers.
214 *
215 * @param start starting offset
216 * @param end end offset
217 * @param step increment level (power of 16)
218 * @param pp port policy to convert
219 * @return corresponding regex
220 */
221static char *
222compute_policy (unsigned int start,
223 unsigned int end,
224 unsigned int step,
225 const struct GNUNET_STRINGS_PortPolicy *pp)
226{
227 unsigned int i;
228 char before[36]; /* 16 * 2 + 3 dots + 0-terminator */
229 char middlel[33]; /* 16 * 2 + 0-terminator */
230 char middleh[33]; /* 16 * 2 + 0-terminator */
231 char after[36]; /* 16 * 2 + 3 dots + 0-terminator */
232 char beforep[36 + 2]; /* 16 * 2 + 3 dots + 0-terminator + ()*/
233 char middlehp[33 + 2]; /* 16 * 2 + 0-terminator + () */
234 char middlelp[33 + 2]; /* 16 * 2 + 0-terminator + () */
235 char afterp[36 + 2]; /* 16 * 2 + 3 dots + 0-terminator + () */
236 char dots[5 * strlen (DOT)];
237 char buf[3];
238 char *middle;
239 char *ret;
240 unsigned int xstep;
241 char *recl;
242 char *rech;
243 char *reclp;
244 char *rechp;
245 unsigned int start_port;
246 unsigned int end_port;
247
248 GNUNET_assert (GNUNET_YES == pp->negate_portrange);
249 start_port = pp->start_port;
250 if (1 == start_port)
251 start_port = 0;
252 end_port = pp->end_port;
253 GNUNET_assert ((end - start) / step <= 0xF);
254 before[0] = '\0';
255 middlel[0] = '\0';
256 middleh[0] = '\0';
257 after[0] = '\0';
258 for (i = start; i <= end; i += step)
259 {
260 GNUNET_snprintf (buf,
261 sizeof(buf),
262 "%X|",
263 (i - start) / step);
264 if (i / step < start_port / step)
265 strcat (before, buf);
266 else if (i / step > end_port / step)
267 strcat (after, buf);
268 else if (i / step == start_port / step)
269 strcat (middlel, buf);
270 else if (i / step == end_port / step)
271 strcat (middleh, buf);
272 }
273 if (strlen (before) > 0)
274 before[strlen (before) - 1] = '\0';
275 if (strlen (middlel) > 0)
276 middlel[strlen (middlel) - 1] = '\0';
277 if (strlen (middleh) > 0)
278 middleh[strlen (middleh) - 1] = '\0';
279 if (strlen (after) > 0)
280 after[strlen (after) - 1] = '\0';
281 if (needs_parens (before))
282 GNUNET_snprintf (beforep,
283 sizeof(beforep),
284 "(%s)",
285 before);
286 else
287 strcpy (beforep, before);
288 if (needs_parens (middlel))
289 GNUNET_snprintf (middlelp,
290 sizeof(middlelp),
291 "(%s)",
292 middlel);
293 else
294 strcpy (middlelp, middlel);
295 if (needs_parens (middleh))
296 GNUNET_snprintf (middlehp,
297 sizeof(middlehp),
298 "(%s)",
299 middleh);
300 else
301 strcpy (middlehp, middleh);
302 if (needs_parens (after))
303 GNUNET_snprintf (afterp,
304 sizeof(afterp),
305 "(%s)",
306 after);
307 else
308 strcpy (afterp, after);
309 dots[0] = '\0';
310 for (xstep = step / 16; xstep > 0; xstep /= 16)
311 strcat (dots, DOT);
312 if (step >= 16)
313 {
314 if (strlen (middlel) > 0)
315 recl = compute_policy ((start_port / step) * step,
316 (start_port / step) * step + step - 1,
317 step / 16,
318 pp);
319 else
320 recl = GNUNET_strdup ("");
321 if (strlen (middleh) > 0)
322 rech = compute_policy ((end_port / step) * step,
323 (end_port / step) * step + step - 1,
324 step / 16,
325 pp);
326 else
327 rech = GNUNET_strdup ("");
328 }
329 else
330 {
331 recl = GNUNET_strdup ("");
332 rech = GNUNET_strdup ("");
333 middlel[0] = '\0';
334 middlelp[0] = '\0';
335 middleh[0] = '\0';
336 middlehp[0] = '\0';
337 }
338 if (needs_parens (recl))
339 GNUNET_asprintf (&reclp,
340 "(%s)",
341 recl);
342 else
343 reclp = GNUNET_strdup (recl);
344 if (needs_parens (rech))
345 GNUNET_asprintf (&rechp,
346 "(%s)",
347 rech);
348 else
349 rechp = GNUNET_strdup (rech);
350
351 if ((strlen (middleh) > 0) &&
352 (strlen (rech) > 0) &&
353 (strlen (middlel) > 0) &&
354 (strlen (recl) > 0))
355 {
356 GNUNET_asprintf (&middle,
357 "%s%s|%s%s",
358 middlel,
359 reclp,
360 middleh,
361 rechp);
362 }
363 else if ((strlen (middleh) > 0) &&
364 (strlen (rech) > 0))
365 {
366 GNUNET_asprintf (&middle,
367 "%s%s",
368 middleh,
369 rechp);
370 }
371 else if ((strlen (middlel) > 0) &&
372 (strlen (recl) > 0))
373 {
374 GNUNET_asprintf (&middle,
375 "%s%s",
376 middlel,
377 reclp);
378 }
379 else
380 {
381 middle = GNUNET_strdup ("");
382 }
383 if ((strlen (before) > 0) &&
384 (strlen (after) > 0))
385 {
386 if (strlen (dots) > 0)
387 {
388 if (strlen (middle) > 0)
389 GNUNET_asprintf (&ret,
390 "(%s%s|%s|%s%s)",
391 beforep, dots,
392 middle,
393 afterp, dots);
394 else
395 GNUNET_asprintf (&ret,
396 "(%s|%s)%s",
397 beforep,
398 afterp,
399 dots);
400 }
401 else
402 {
403 if (strlen (middle) > 0)
404 GNUNET_asprintf (&ret,
405 "(%s|%s|%s)",
406 before,
407 middle,
408 after);
409 else if (1 == step)
410 GNUNET_asprintf (&ret,
411 "%s|%s",
412 before,
413 after);
414 else
415 GNUNET_asprintf (&ret,
416 "(%s|%s)",
417 before,
418 after);
419 }
420 }
421 else if (strlen (before) > 0)
422 {
423 if (strlen (dots) > 0)
424 {
425 if (strlen (middle) > 0)
426 GNUNET_asprintf (&ret,
427 "(%s%s|%s)",
428 beforep, dots,
429 middle);
430 else
431 GNUNET_asprintf (&ret,
432 "%s%s",
433 beforep, dots);
434 }
435 else
436 {
437 if (strlen (middle) > 0)
438 GNUNET_asprintf (&ret,
439 "(%s|%s)",
440 before,
441 middle);
442 else
443 GNUNET_asprintf (&ret,
444 "%s",
445 before);
446 }
447 }
448 else if (strlen (after) > 0)
449 {
450 if (strlen (dots) > 0)
451 {
452 if (strlen (middle) > 0)
453 GNUNET_asprintf (&ret,
454 "(%s|%s%s)",
455 middle,
456 afterp, dots);
457 else
458 GNUNET_asprintf (&ret,
459 "%s%s",
460 afterp, dots);
461 }
462 else
463 {
464 if (strlen (middle) > 0)
465 GNUNET_asprintf (&ret,
466 "%s|%s",
467 middle,
468 after);
469 else
470 GNUNET_asprintf (&ret,
471 "%s",
472 after);
473 }
474 }
475 else if (strlen (middle) > 0)
476 {
477 GNUNET_asprintf (&ret,
478 "%s",
479 middle);
480 }
481 else
482 {
483 ret = GNUNET_strdup ("");
484 }
485 GNUNET_free (middle);
486 GNUNET_free (reclp);
487 GNUNET_free (rechp);
488 GNUNET_free (recl);
489 GNUNET_free (rech);
490 return ret;
491}
492
493
494/**
495 * Convert a port policy to a regular expression. Note: this is a
496 * very simplistic implementation, we might want to consider doing
497 * something more sophisiticated (resulting in smaller regular
498 * expressions) at a later time.
499 *
500 * @param pp port policy to convert
501 * @return NULL on error
502 */
503static char *
504port_to_regex (const struct GNUNET_STRINGS_PortPolicy *pp)
505{
506 char *reg;
507 char *ret;
508 char *pos;
509 unsigned int i;
510 unsigned int cnt;
511
512 if ((0 == pp->start_port) ||
513 ((1 == pp->start_port) &&
514 (0xFFFF == pp->end_port) &&
515 (GNUNET_NO == pp->negate_portrange)))
516 return GNUNET_strdup (DOT DOT DOT DOT);
517 if ((pp->start_port == pp->end_port) &&
518 (GNUNET_NO == pp->negate_portrange))
519 {
520 GNUNET_asprintf (&ret,
521 "%04X",
522 pp->start_port);
523 return ret;
524 }
525 if (pp->end_port < pp->start_port)
526 return NULL;
527
528 if (GNUNET_YES == pp->negate_portrange)
529 {
530 ret = compute_policy (0, 0xFFFF, 0x1000, pp);
531 }
532 else
533 {
534 cnt = pp->end_port - pp->start_port + 1;
535 reg = GNUNET_malloc (cnt * 5 + 1);
536 pos = reg;
537 for (i = 1; i <= 0xFFFF; i++)
538 {
539 if ((i >= pp->start_port) && (i <= pp->end_port))
540 {
541 if (pos == reg)
542 {
543 GNUNET_snprintf (pos,
544 5,
545 "%04X",
546 i);
547 }
548 else
549 {
550 GNUNET_snprintf (pos,
551 6,
552 "|%04X",
553 i);
554 }
555 pos += strlen (pos);
556 }
557 }
558 GNUNET_asprintf (&ret,
559 "(%s)",
560 reg);
561 GNUNET_free (reg);
562 }
563 return ret;
564}
565
566
567/**
568 * Convert an address (IPv4 or IPv6) to a regex.
569 *
570 * @param addr address
571 * @param mask network mask
572 * @param len number of bytes in @a addr and @a mask
573 * @return NULL on error, otherwise regex for the address
574 */
575static char *
576address_to_regex (const void *addr,
577 const void *mask,
578 size_t len)
579{
580 const uint16_t *a = addr;
581 const uint16_t *m = mask;
582 char *ret;
583 char *tmp;
584 char *reg;
585 unsigned int i;
586
587 ret = NULL;
588 GNUNET_assert (1 != (len % 2));
589 for (i = 0; i < len / 2; i++)
590 {
591 reg = num_to_regex (a[i], m[i]);
592 if (NULL == reg)
593 {
594 GNUNET_free (ret);
595 return NULL;
596 }
597 if (NULL == ret)
598 {
599 ret = reg;
600 }
601 else
602 {
603 GNUNET_asprintf (&tmp,
604 "%s%s",
605 ret, reg);
606 GNUNET_free (ret);
607 GNUNET_free (reg);
608 ret = tmp;
609 }
610 }
611 return ret;
612}
613
614
615/**
616 * Convert a single line of an IPv4 policy to a regular expression.
617 *
618 * @param v4 line to convert
619 * @return NULL on error
620 */
621static char *
622ipv4_to_regex (const struct GNUNET_STRINGS_IPv4NetworkPolicy *v4)
623{
624 char *reg;
625 char *pp;
626 char *ret;
627
628 reg = address_to_regex (&v4->network,
629 &v4->netmask,
630 sizeof(struct in_addr));
631 if (NULL == reg)
632 return NULL;
633 pp = port_to_regex (&v4->pp);
634 if (NULL == pp)
635 {
636 GNUNET_free (reg);
637 return NULL;
638 }
639 GNUNET_asprintf (&ret,
640 "4-%s-%s",
641 pp, reg);
642 GNUNET_free (pp);
643 GNUNET_free (reg);
644 return ret;
645}
646
647
648/**
649 * Convert a single line of an IPv4 policy to a regular expression.
650 *
651 * @param v6 line to convert
652 * @return NULL on error
653 */
654static char *
655ipv6_to_regex (const struct GNUNET_STRINGS_IPv6NetworkPolicy *v6)
656{
657 char *reg;
658 char *pp;
659 char *ret;
660
661 reg = address_to_regex (&v6->network,
662 &v6->netmask,
663 sizeof(struct in6_addr));
664 if (NULL == reg)
665 return NULL;
666 pp = port_to_regex (&v6->pp);
667 if (NULL == pp)
668 {
669 GNUNET_free (reg);
670 return NULL;
671 }
672 GNUNET_asprintf (&ret,
673 "6-%s-%s",
674 pp, reg);
675 GNUNET_free (pp);
676 GNUNET_free (reg);
677 return ret;
678}
679
680
681char *
682GNUNET_TUN_ipv4policy2regex (const char *policy)
683{
684 struct GNUNET_STRINGS_IPv4NetworkPolicy *np;
685 char *reg;
686 char *tmp;
687 char *line;
688 unsigned int i;
689
690 np = GNUNET_STRINGS_parse_ipv4_policy (policy);
691 if (NULL == np)
692 return NULL;
693 reg = NULL;
694 for (i = 0; (0 == i) || (0 != np[i].network.s_addr); i++)
695 {
696 line = ipv4_to_regex (&np[i]);
697 if (NULL == line)
698 {
699 GNUNET_free (reg);
700 GNUNET_free (np);
701 return NULL;
702 }
703 if (NULL == reg)
704 {
705 reg = line;
706 }
707 else
708 {
709 GNUNET_asprintf (&tmp,
710 "%s|(%s)",
711 reg, line);
712 GNUNET_free (reg);
713 GNUNET_free (line);
714 reg = tmp;
715 }
716 if (0 == np[i].network.s_addr)
717 break;
718 }
719 GNUNET_free (np);
720 return reg;
721}
722
723
724char *
725GNUNET_TUN_ipv6policy2regex (const char *policy)
726{
727 struct in6_addr zero;
728 struct GNUNET_STRINGS_IPv6NetworkPolicy *np;
729 char *reg;
730 char *tmp;
731 char *line;
732 unsigned int i;
733
734 np = GNUNET_STRINGS_parse_ipv6_policy (policy);
735 if (NULL == np)
736 return NULL;
737 reg = NULL;
738 memset (&zero, 0, sizeof(struct in6_addr));
739 for (i = 0; (0 == i) || (0 != memcmp (&zero, &np[i].network, sizeof(struct
740 in6_addr)));
741 i++)
742 {
743 line = ipv6_to_regex (&np[i]);
744 if (NULL == line)
745 {
746 GNUNET_free (reg);
747 GNUNET_free (np);
748 return NULL;
749 }
750 if (NULL == reg)
751 {
752 reg = line;
753 }
754 else
755 {
756 GNUNET_asprintf (&tmp,
757 "%s|(%s)",
758 reg, line);
759 GNUNET_free (reg);
760 GNUNET_free (line);
761 reg = tmp;
762 }
763 if (0 == memcmp (&zero, &np[i].network, sizeof(struct in6_addr)))
764 break;
765 }
766 GNUNET_free (np);
767 return reg;
768}
769
770
771void
772GNUNET_TUN_service_name_to_hash (const char *service_name,
773 struct GNUNET_HashCode *hc)
774{
775 GNUNET_CRYPTO_hash (service_name,
776 strlen (service_name),
777 hc);
778}
779
780
781/**
782 * Compute the CADET port given a service descriptor
783 * (returned from #GNUNET_TUN_service_name_to_hash) and
784 * a TCP/UDP port @a ip_port.
785 *
786 * @param desc service shared secret
787 * @param ip_port TCP/UDP port, use 0 for ICMP
788 * @param[out] cadet_port CADET port to use
789 */
790void
791GNUNET_TUN_compute_service_cadet_port (const struct GNUNET_HashCode *desc,
792 uint16_t ip_port,
793 struct GNUNET_HashCode *cadet_port)
794{
795 uint16_t be_port = htons (ip_port);
796
797 *cadet_port = *desc;
798 GNUNET_memcpy (cadet_port,
799 &be_port,
800 sizeof(uint16_t));
801}
802
803
804/* end of regex.c */
diff --git a/src/lib/util/resolver.conf b/src/lib/util/resolver.conf
new file mode 100644
index 000000000..aae876952
--- /dev/null
+++ b/src/lib/util/resolver.conf
@@ -0,0 +1,20 @@
1[resolver]
2START_ON_DEMAND = YES
3#PORT = 2089
4HOSTNAME = localhost
5BINARY = gnunet-service-resolver
6ACCEPT_FROM = 127.0.0.1;
7ACCEPT_FROM6 = ::1;
8UNIXPATH = $GNUNET_RUNTIME_DIR/gnunet-service-resolver.sock
9UNIX_MATCH_UID = NO
10UNIX_MATCH_GID = NO
11# DISABLE_SOCKET_FORWARDING = NO
12# USERNAME =
13# MAXBUF =
14# TIMEOUT =
15# DISABLEV6 =
16# BINDTO =
17# REJECT_FROM =
18# REJECT_FROM6 =
19# PREFIX =
20
diff --git a/src/lib/util/resolver.h b/src/lib/util/resolver.h
new file mode 100644
index 000000000..e487f6e6f
--- /dev/null
+++ b/src/lib/util/resolver.h
@@ -0,0 +1,93 @@
1/*
2 This file is part of GNUnet.
3 Copyright (C) 2009, 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 * @author Christian Grothoff
23 * @file util/resolver.h
24 */
25#ifndef RESOLVER_H
26#define RESOLVER_H
27
28#include "gnunet_common.h"
29
30GNUNET_NETWORK_STRUCT_BEGIN
31
32/**
33 * Request for the resolver. Followed by either the "struct sockaddr"
34 * or the 0-terminated hostname.
35 *
36 * The response will be one or more messages of type
37 * RESOLVER_RESPONSE, each with the message header immediately
38 * followed by the requested data (0-terminated hostname or struct
39 * in[6]_addr, depending on direction). The last RESOLVER_RESPONSE
40 * will just be a header without any data (used to indicate the end of
41 * the list).
42 */
43struct GNUNET_RESOLVER_GetMessage
44{
45 /**
46 * Type: #GNUNET_MESSAGE_TYPE_RESOLVER_REQUEST
47 */
48 struct GNUNET_MessageHeader header;
49
50 /**
51 * GNUNET_YES to get hostname from IP,
52 * GNUNET_NO to get IP from hostname.
53 */
54 int32_t direction GNUNET_PACKED;
55
56 /**
57 * Address family to use (AF_INET, AF_INET6 or AF_UNSPEC).
58 */
59 int32_t af GNUNET_PACKED;
60
61 /**
62 * identifies the request and is contained in the response message. The
63 * client has to match response to request by this identifier.
64 */
65 uint32_t client_id GNUNET_PACKED;
66
67 /* followed by 0-terminated string for A/AAAA-lookup or
68 by 'struct in_addr' / 'struct in6_addr' for reverse lookup */
69};
70
71
72struct GNUNET_RESOLVER_ResponseMessage
73{
74 /**
75 * Type: #GNUNET_MESSAGE_TYPE_RESOLVER_RESPONSE
76 */
77 struct GNUNET_MessageHeader header;
78
79 /**
80 * identifies the request this message responds to. The client
81 * has to match response to request by this identifier.
82 */
83 uint32_t client_id GNUNET_PACKED;
84
85 /* followed by 0-terminated string for response to a reverse lookup
86 * or by 'struct in_addr' / 'struct in6_addr' for response to
87 * A/AAAA-lookup
88 */
89};
90
91GNUNET_NETWORK_STRUCT_END
92
93#endif
diff --git a/src/lib/util/resolver_api.c b/src/lib/util/resolver_api.c
new file mode 100644
index 000000000..130363c77
--- /dev/null
+++ b/src/lib/util/resolver_api.c
@@ -0,0 +1,1295 @@
1/*
2 This file is part of GNUnet.
3 Copyright (C) 2009-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/**
22 * @file util/resolver_api.c
23 * @brief resolver for writing a tool
24 * @author Christian Grothoff
25 */
26
27#include "platform.h"
28#include "gnunet_util_lib.h"
29#include "gnunet_protocols.h"
30#include "gnunet_resolver_service.h"
31#include "resolver.h"
32
33#define LOG(kind, ...) GNUNET_log_from (kind, "util-resolver-api", __VA_ARGS__)
34
35#define LOG_STRERROR(kind, syscall) GNUNET_log_from_strerror (kind, \
36 "util-resolver-api", \
37 syscall)
38
39/**
40 * Maximum supported length for a hostname
41 */
42#define MAX_HOSTNAME 1024
43
44
45/**
46 * Possible hostnames for "loopback".
47 */
48static const char *loopback[] = {
49 "localhost",
50 "ip6-localnet",
51 NULL
52};
53
54
55/**
56 * Configuration.
57 */
58static const struct GNUNET_CONFIGURATION_Handle *resolver_cfg;
59
60/**
61 * Our connection to the resolver service, created on-demand, but then
62 * persists until error or shutdown.
63 */
64static struct GNUNET_MQ_Handle *mq;
65
66/**
67 * Head of DLL of requests.
68 */
69static struct GNUNET_RESOLVER_RequestHandle *req_head;
70
71/**
72 * Tail of DLL of requests.
73 */
74static struct GNUNET_RESOLVER_RequestHandle *req_tail;
75
76/**
77 * ID of the last request we sent to the service
78 */
79static uint32_t last_request_id;
80
81/**
82 * How long should we wait to reconnect?
83 */
84static struct GNUNET_TIME_Relative backoff;
85
86/**
87 * Task for reconnecting.
88 */
89static struct GNUNET_SCHEDULER_Task *r_task;
90
91/**
92 * Task ID of shutdown task; only present while we have a
93 * connection to the resolver service.
94 */
95static struct GNUNET_SCHEDULER_Task *s_task;
96
97
98/**
99 * Handle to a request given to the resolver. Can be used to cancel
100 * the request prior to the timeout or successful execution. Also
101 * used to track our internal state for the request.
102 */
103struct GNUNET_RESOLVER_RequestHandle
104{
105 /**
106 * Next entry in DLL of requests.
107 */
108 struct GNUNET_RESOLVER_RequestHandle *next;
109
110 /**
111 * Previous entry in DLL of requests.
112 */
113 struct GNUNET_RESOLVER_RequestHandle *prev;
114
115 /**
116 * Callback if this is an name resolution request,
117 * otherwise NULL.
118 */
119 GNUNET_RESOLVER_AddressCallback addr_callback;
120
121 /**
122 * Callback if this is a reverse lookup request,
123 * otherwise NULL.
124 */
125 GNUNET_RESOLVER_HostnameCallback name_callback;
126
127 /**
128 * Closure for the callbacks.
129 */
130 void *cls;
131
132 /**
133 * When should this request time out?
134 */
135 struct GNUNET_TIME_Absolute timeout;
136
137 /**
138 * Task handle for making reply callbacks in numeric lookups
139 * asynchronous, and for timeout handling.
140 */
141 struct GNUNET_SCHEDULER_Task *task;
142
143 /**
144 * Desired address family.
145 */
146 int af;
147
148 /**
149 * Identifies the request. The response will contain this id.
150 */
151 uint32_t id;
152
153 /**
154 * Has this request been transmitted to the service?
155 * #GNUNET_YES if transmitted
156 * #GNUNET_NO if not transmitted
157 * #GNUNET_SYSERR when request was canceled
158 */
159 int was_transmitted;
160
161 /**
162 * Did we add this request to the queue?
163 */
164 int was_queued;
165
166 /**
167 * Desired direction (IP to name or name to IP)
168 */
169 int direction;
170
171 /**
172 * #GNUNET_YES if a response was received
173 */
174 int received_response;
175
176 /**
177 * Length of the data that follows this struct.
178 */
179 size_t data_len;
180};
181
182
183/**
184 * Check that the resolver service runs on localhost
185 * (or equivalent).
186 *
187 * @return #GNUNET_OK if the resolver is properly configured,
188 * #GNUNET_SYSERR otherwise.
189 */
190static int
191check_config ()
192{
193 char *hostname;
194 struct sockaddr_in v4;
195 struct sockaddr_in6 v6;
196
197 if (GNUNET_OK ==
198 GNUNET_CONFIGURATION_have_value (resolver_cfg,
199 "resolver",
200 "UNIXPATH"))
201 return GNUNET_OK;
202 memset (&v4, 0, sizeof(v4));
203 v4.sin_addr.s_addr = htonl (INADDR_LOOPBACK);
204 v4.sin_family = AF_INET;
205#if HAVE_SOCKADDR_IN_SIN_LEN
206 v4.sin_len = sizeof(v4);
207#endif
208 memset (&v6, 0, sizeof(v6));
209 v6.sin6_family = AF_INET6;
210#if HAVE_SOCKADDR_IN_SIN_LEN
211 v6.sin6_len = sizeof(v6);
212#endif
213 if (GNUNET_OK !=
214 GNUNET_CONFIGURATION_get_value_string (resolver_cfg,
215 "resolver",
216 "HOSTNAME",
217 &hostname))
218 {
219 LOG (GNUNET_ERROR_TYPE_INFO,
220 _ (
221 "Missing `%s' for `%s' in configuration, DNS resolution will be unavailable.\n"),
222 "HOSTNAME",
223 "resolver");
224 return GNUNET_SYSERR;
225 }
226 if ((1 == inet_pton (AF_INET, hostname, &v4)) ||
227 (1 == inet_pton (AF_INET6, hostname, &v6)))
228 {
229 GNUNET_free (hostname);
230 return GNUNET_OK;
231 }
232 for (unsigned int i = 0;
233 NULL != loopback[i];
234 i++)
235 if (0 == strcasecmp (loopback[i],
236 hostname))
237 {
238 GNUNET_free (hostname);
239 return GNUNET_OK;
240 }
241 LOG (GNUNET_ERROR_TYPE_INFO,
242 _ (
243 "Missing `%s' or numeric IP address for `%s' of `%s' in configuration, DNS resolution will be unavailable.\n"),
244 "localhost",
245 "HOSTNAME",
246 "resolver");
247 GNUNET_free (hostname);
248 return GNUNET_SYSERR;
249}
250
251
252/**
253 * Create the connection to the resolver service.
254 *
255 * @param cfg configuration to use
256 */
257void
258GNUNET_RESOLVER_connect (const struct GNUNET_CONFIGURATION_Handle *cfg)
259{
260 GNUNET_assert (NULL != cfg);
261 backoff = GNUNET_TIME_UNIT_MILLISECONDS;
262 resolver_cfg = cfg;
263}
264
265
266/**
267 * Destroy the connection to the resolver service.
268 */
269void
270GNUNET_RESOLVER_disconnect ()
271{
272 struct GNUNET_RESOLVER_RequestHandle *rh;
273
274 while (NULL != (rh = req_head))
275 {
276 GNUNET_assert (GNUNET_SYSERR == rh->was_transmitted);
277 GNUNET_CONTAINER_DLL_remove (req_head,
278 req_tail,
279 rh);
280 GNUNET_free (rh);
281 }
282 if (NULL != mq)
283 {
284 LOG (GNUNET_ERROR_TYPE_DEBUG,
285 "Disconnecting from DNS service\n");
286 GNUNET_MQ_destroy (mq);
287 mq = NULL;
288 }
289 if (NULL != r_task)
290 {
291 GNUNET_SCHEDULER_cancel (r_task);
292 r_task = NULL;
293 }
294 if (NULL != s_task)
295 {
296 GNUNET_SCHEDULER_cancel (s_task);
297 s_task = NULL;
298 }
299}
300
301
302/**
303 * Task executed on system shutdown.
304 */
305static void
306shutdown_task (void *cls)
307{
308 (void) cls;
309 s_task = NULL;
310 GNUNET_RESOLVER_disconnect ();
311 backoff = GNUNET_TIME_UNIT_MILLISECONDS;
312}
313
314
315/**
316 * Consider disconnecting if we have no further requests pending.
317 */
318static void
319check_disconnect ()
320{
321 for (struct GNUNET_RESOLVER_RequestHandle *rh = req_head;
322 NULL != rh;
323 rh = rh->next)
324 if (GNUNET_SYSERR != rh->was_transmitted)
325 return;
326 if (NULL != r_task)
327 {
328 GNUNET_SCHEDULER_cancel (r_task);
329 r_task = NULL;
330 }
331 if (NULL != s_task)
332 return;
333 s_task = GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_UNIT_MILLISECONDS,
334 &shutdown_task,
335 NULL);
336}
337
338
339/**
340 * Convert IP address to string without DNS resolution.
341 *
342 * @param af address family
343 * @param ip the address
344 * @param ip_len number of bytes in @a ip
345 * @return address as a string, NULL on error
346 */
347static char *
348no_resolve (int af,
349 const void *ip,
350 socklen_t ip_len)
351{
352 char buf[INET6_ADDRSTRLEN];
353
354 switch (af)
355 {
356 case AF_INET:
357 if (ip_len != sizeof(struct in_addr))
358 return NULL;
359 if (NULL ==
360 inet_ntop (AF_INET,
361 ip,
362 buf,
363 sizeof(buf)))
364 {
365 LOG_STRERROR (GNUNET_ERROR_TYPE_WARNING,
366 "inet_ntop");
367 return NULL;
368 }
369 break;
370
371 case AF_INET6:
372 if (ip_len != sizeof(struct in6_addr))
373 return NULL;
374 if (NULL ==
375 inet_ntop (AF_INET6,
376 ip,
377 buf,
378 sizeof(buf)))
379 {
380 LOG_STRERROR (GNUNET_ERROR_TYPE_WARNING,
381 "inet_ntop");
382 return NULL;
383 }
384 break;
385
386 default:
387 GNUNET_break (0);
388 return NULL;
389 }
390 return GNUNET_strdup (buf);
391}
392
393
394/**
395 * Adjust exponential back-off and reconnect to the service.
396 */
397static void
398reconnect (void);
399
400
401/**
402 * Generic error handler, called with the appropriate error code and
403 * the same closure specified at the creation of the message queue.
404 * Not every message queue implementation supports an error handler.
405 *
406 * @param cls NULL
407 * @param error error code
408 */
409static void
410mq_error_handler (void *cls,
411 enum GNUNET_MQ_Error error)
412{
413 (void) cls;
414 GNUNET_MQ_destroy (mq);
415 mq = NULL;
416 LOG (GNUNET_ERROR_TYPE_DEBUG,
417 "MQ error %d, reconnecting\n",
418 error);
419 reconnect ();
420}
421
422
423/**
424 * Process pending requests to the resolver.
425 */
426static void
427process_requests ()
428{
429 struct GNUNET_RESOLVER_GetMessage *msg;
430 struct GNUNET_MQ_Envelope *env;
431 struct GNUNET_RESOLVER_RequestHandle *rh = req_head;
432
433 if (NULL == mq)
434 {
435 reconnect ();
436 return;
437 }
438 if (NULL == rh)
439 {
440 /* nothing to do, release socket really soon if there is nothing
441 * else happening... */
442 if (NULL == s_task)
443 s_task =
444 GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_UNIT_MILLISECONDS,
445 &shutdown_task,
446 NULL);
447 return;
448 }
449 if (GNUNET_NO != rh->was_transmitted)
450 return; /* waiting for reply */
451 env = GNUNET_MQ_msg_extra (msg,
452 rh->data_len,
453 GNUNET_MESSAGE_TYPE_RESOLVER_REQUEST);
454 msg->direction = htonl (rh->direction);
455 msg->af = htonl (rh->af);
456 msg->client_id = rh->id;
457 GNUNET_memcpy (&msg[1],
458 &rh[1],
459 rh->data_len);
460 LOG (GNUNET_ERROR_TYPE_DEBUG,
461 "Transmitting DNS resolution request (ID %u) to DNS service\n",
462 rh->id);
463 GNUNET_MQ_send (mq,
464 env);
465 rh->was_transmitted = GNUNET_YES;
466}
467
468
469/**
470 * Check validity of response with a hostname for a DNS lookup.
471 *
472 * @param cls NULL
473 * @param msg message with the hostname
474 */
475static int
476check_response (void *cls,
477 const struct GNUNET_RESOLVER_ResponseMessage *msg)
478{
479 (void) cls;
480 (void) msg;
481
482 /* implemented in #handle_response() for now */
483 return GNUNET_OK;
484}
485
486
487/**
488 * Check validity of response with a hostname for a DNS lookup.
489 * NOTE: right now rather messy, might want to use different
490 * message types for different response formats in the future.
491 *
492 * @param cls NULL
493 * @param msg message with the response
494 */
495static void
496handle_response (void *cls,
497 const struct GNUNET_RESOLVER_ResponseMessage *msg)
498{
499 struct GNUNET_RESOLVER_RequestHandle *rh = req_head;
500 uint16_t size;
501 char *nret;
502 uint32_t client_request_id = msg->client_id;
503
504 for (; rh != NULL; rh = rh->next)
505 {
506 if (rh->id == client_request_id)
507 break;
508 }
509
510 (void) cls;
511 if (NULL == rh)
512 {
513 /* Resolver service sent extra replies to query (after terminator)? Bad! */
514 GNUNET_break (0);
515 GNUNET_MQ_destroy (mq);
516 mq = NULL;
517 reconnect ();
518 return;
519 }
520 size = ntohs (msg->header.size);
521 if (size == sizeof(struct GNUNET_RESOLVER_ResponseMessage))
522 {
523 LOG (GNUNET_ERROR_TYPE_DEBUG,
524 "Received empty response from DNS service\n");
525 /* message contains not data, just header; end of replies */
526 /* check if request was canceled */
527 if (GNUNET_SYSERR != rh->was_transmitted)
528 {
529 /* no reverse lookup was successful, return IP as string */
530 if (NULL != rh->name_callback)
531 {
532 if (GNUNET_NO == rh->received_response)
533 {
534 nret = no_resolve (rh->af,
535 &rh[1],
536 rh->data_len);
537 rh->name_callback (rh->cls, nret);
538 GNUNET_free (nret);
539 }
540 /* finally, make termination call */
541 if (GNUNET_SYSERR != rh->was_transmitted)
542 rh->name_callback (rh->cls,
543 NULL);
544 }
545 if ((NULL != rh->addr_callback) &&
546 (GNUNET_SYSERR != rh->was_transmitted))
547 rh->addr_callback (rh->cls,
548 NULL,
549 0);
550 }
551 rh->was_transmitted = GNUNET_NO;
552 GNUNET_RESOLVER_request_cancel (rh);
553 process_requests ();
554 return;
555 }
556 /* return reverse lookup results to caller */
557 if (NULL != rh->name_callback)
558 {
559 const char *hostname;
560
561 hostname = (const char *) &msg[1];
562 if (hostname[size - sizeof(struct GNUNET_RESOLVER_ResponseMessage) - 1] !=
563 '\0')
564 {
565 GNUNET_break (0);
566 if (GNUNET_SYSERR != rh->was_transmitted)
567 rh->name_callback (rh->cls,
568 NULL);
569 rh->was_transmitted = GNUNET_NO;
570 GNUNET_RESOLVER_request_cancel (rh);
571 GNUNET_MQ_destroy (mq);
572 mq = NULL;
573 reconnect ();
574 return;
575 }
576 LOG (GNUNET_ERROR_TYPE_DEBUG,
577 "Resolver returns `%s' for IP `%s'.\n",
578 hostname,
579 GNUNET_a2s ((const void *) &rh[1],
580 rh->data_len));
581 if (rh->was_transmitted != GNUNET_SYSERR)
582 rh->name_callback (rh->cls,
583 hostname);
584 rh->received_response = GNUNET_YES;
585 }
586 /* return lookup results to caller */
587 if (NULL != rh->addr_callback)
588 {
589 struct sockaddr_in v4;
590 struct sockaddr_in6 v6;
591 const struct sockaddr *sa;
592 socklen_t salen;
593 const void *ip;
594 size_t ip_len;
595
596 ip = &msg[1];
597 ip_len = size - sizeof(struct GNUNET_RESOLVER_ResponseMessage);
598 if (ip_len == sizeof(struct in_addr))
599 {
600 memset (&v4, 0, sizeof(v4));
601 v4.sin_family = AF_INET;
602 v4.sin_addr = *(struct in_addr*) ip;
603#if HAVE_SOCKADDR_IN_SIN_LEN
604 v4.sin_len = sizeof(v4);
605#endif
606 salen = sizeof(v4);
607 sa = (const struct sockaddr *) &v4;
608 }
609 else if (ip_len == sizeof(struct in6_addr))
610 {
611 memset (&v6, 0, sizeof(v6));
612 v6.sin6_family = AF_INET6;
613 v6.sin6_addr = *(struct in6_addr*) ip;
614#if HAVE_SOCKADDR_IN_SIN_LEN
615 v6.sin6_len = sizeof(v6);
616#endif
617 salen = sizeof(v6);
618 sa = (const struct sockaddr *) &v6;
619 }
620 else
621 {
622 GNUNET_break (0);
623 if (GNUNET_SYSERR != rh->was_transmitted)
624 rh->addr_callback (rh->cls,
625 NULL,
626 0);
627 rh->was_transmitted = GNUNET_NO;
628 GNUNET_RESOLVER_request_cancel (rh);
629 GNUNET_MQ_destroy (mq);
630 mq = NULL;
631 reconnect ();
632 return;
633 }
634 LOG (GNUNET_ERROR_TYPE_DEBUG,
635 "Received IP from DNS service\n");
636 if (GNUNET_SYSERR != rh->was_transmitted)
637 rh->addr_callback (rh->cls,
638 sa,
639 salen);
640 }
641}
642
643
644/**
645 * We've been asked to lookup the address for a hostname and were
646 * given a valid numeric string. Perform the callbacks for the
647 * numeric addresses.
648 *
649 * @param cls `struct GNUNET_RESOLVER_RequestHandle` for the request
650 */
651static void
652numeric_resolution (void *cls)
653{
654 struct GNUNET_RESOLVER_RequestHandle *rh = cls;
655 struct sockaddr_in v4;
656 struct sockaddr_in6 v6;
657 const char *hostname;
658
659 rh->task = NULL;
660 memset (&v4, 0, sizeof(v4));
661 v4.sin_family = AF_INET;
662#if HAVE_SOCKADDR_IN_SIN_LEN
663 v4.sin_len = sizeof(v4);
664#endif
665 memset (&v6, 0, sizeof(v6));
666 v6.sin6_family = AF_INET6;
667#if HAVE_SOCKADDR_IN_SIN_LEN
668 v6.sin6_len = sizeof(v6);
669#endif
670 hostname = (const char *) &rh[1];
671 if (((rh->af == AF_UNSPEC) ||
672 (rh->af == AF_INET)) &&
673 (1 == inet_pton (AF_INET,
674 hostname,
675 &v4.sin_addr)))
676 {
677 rh->addr_callback (rh->cls,
678 (const struct sockaddr *) &v4,
679 sizeof(v4));
680 if ((rh->af == AF_UNSPEC) &&
681 (GNUNET_SYSERR != rh->was_transmitted) &&
682 (1 == inet_pton (AF_INET6,
683 hostname,
684 &v6.sin6_addr)))
685 {
686 /* this can happen on some systems IF "hostname" is "localhost" */
687 rh->addr_callback (rh->cls,
688 (const struct sockaddr *) &v6,
689 sizeof(v6));
690 }
691 if (GNUNET_SYSERR != rh->was_transmitted)
692 rh->addr_callback (rh->cls,
693 NULL,
694 0);
695 GNUNET_free (rh);
696 return;
697 }
698 if (((rh->af == AF_UNSPEC) ||
699 (rh->af == AF_INET6)) &&
700 (1 == inet_pton (AF_INET6,
701 hostname,
702 &v6.sin6_addr)))
703 {
704 rh->addr_callback (rh->cls,
705 (const struct sockaddr *) &v6,
706 sizeof(v6));
707 if (GNUNET_SYSERR != rh->was_transmitted)
708 rh->addr_callback (rh->cls,
709 NULL,
710 0);
711 GNUNET_free (rh);
712 return;
713 }
714 /* why are we here? this task should not have been scheduled! */
715 GNUNET_assert (0);
716 GNUNET_free (rh);
717}
718
719
720/**
721 * We've been asked to lookup the address for a hostname and were
722 * given a variant of "loopback". Perform the callbacks for the
723 * respective loopback numeric addresses.
724 *
725 * @param cls `struct GNUNET_RESOLVER_RequestHandle` for the request
726 */
727static void
728loopback_resolution (void *cls)
729{
730 struct GNUNET_RESOLVER_RequestHandle *rh = cls;
731 struct sockaddr_in v4;
732 struct sockaddr_in6 v6;
733
734 rh->task = NULL;
735 memset (&v4, 0, sizeof(v4));
736 v4.sin_addr.s_addr = htonl (INADDR_LOOPBACK);
737 v4.sin_family = AF_INET;
738#if HAVE_SOCKADDR_IN_SIN_LEN
739 v4.sin_len = sizeof(v4);
740#endif
741 memset (&v6, 0, sizeof(v6));
742 v6.sin6_family = AF_INET6;
743#if HAVE_SOCKADDR_IN_SIN_LEN
744 v6.sin6_len = sizeof(v6);
745#endif
746 v6.sin6_addr = in6addr_loopback;
747 switch (rh->af)
748 {
749 case AF_INET:
750 rh->addr_callback (rh->cls,
751 (const struct sockaddr *) &v4,
752 sizeof(v4));
753 break;
754
755 case AF_INET6:
756 rh->addr_callback (rh->cls,
757 (const struct sockaddr *) &v6,
758 sizeof(v6));
759 break;
760
761 case AF_UNSPEC:
762 rh->addr_callback (rh->cls,
763 (const struct sockaddr *) &v6,
764 sizeof(v6));
765 rh->addr_callback (rh->cls,
766 (const struct sockaddr *) &v4,
767 sizeof(v4));
768
769 break;
770
771 default:
772 GNUNET_break (0);
773 break;
774 }
775 if (GNUNET_SYSERR != rh->was_transmitted)
776 rh->addr_callback (rh->cls,
777 NULL,
778 0);
779 LOG (GNUNET_ERROR_TYPE_DEBUG,
780 "Finished resolving hostname `%s'.\n",
781 (const char *) &rh[1]);
782 GNUNET_free (rh);
783}
784
785
786/**
787 * Now try to reconnect to the resolver service.
788 *
789 * @param cls NULL
790 */
791static void
792reconnect_task (void *cls)
793{
794 struct GNUNET_MQ_MessageHandler handlers[] = {
795 GNUNET_MQ_hd_var_size (response,
796 GNUNET_MESSAGE_TYPE_RESOLVER_RESPONSE,
797 struct GNUNET_RESOLVER_ResponseMessage,
798 NULL),
799 GNUNET_MQ_handler_end ()
800 };
801
802 (void) cls;
803 r_task = NULL;
804 if (NULL == req_head)
805 return; /* no work pending */
806 LOG (GNUNET_ERROR_TYPE_DEBUG,
807 "Trying to connect to DNS service\n");
808 mq = GNUNET_CLIENT_connect (resolver_cfg,
809 "resolver",
810 handlers,
811 &mq_error_handler,
812 NULL);
813 if (NULL == mq)
814 {
815 LOG (GNUNET_ERROR_TYPE_DEBUG,
816 "Failed to connect, will try again later\n");
817 reconnect ();
818 return;
819 }
820 process_requests ();
821}
822
823
824/**
825 * Adjust exponential back-off and reconnect to the service.
826 */
827static void
828reconnect ()
829{
830 struct GNUNET_RESOLVER_RequestHandle *rh;
831
832 if (NULL != r_task)
833 return;
834 GNUNET_assert (NULL == mq);
835 if (NULL != (rh = req_head))
836 {
837 switch (rh->was_transmitted)
838 {
839 case GNUNET_NO:
840 /* nothing more to do */
841 break;
842
843 case GNUNET_YES:
844 /* disconnected, transmit again! */
845 rh->was_transmitted = GNUNET_NO;
846 break;
847
848 case GNUNET_SYSERR:
849 /* request was cancelled, remove entirely */
850 GNUNET_CONTAINER_DLL_remove (req_head,
851 req_tail,
852 rh);
853 GNUNET_free (rh);
854 check_disconnect ();
855 break;
856
857 default:
858 GNUNET_assert (0);
859 break;
860 }
861 }
862 LOG (GNUNET_ERROR_TYPE_DEBUG,
863 "Will try to connect to DNS service in %s\n",
864 GNUNET_STRINGS_relative_time_to_string (backoff,
865 GNUNET_YES));
866 GNUNET_assert (NULL != resolver_cfg);
867 r_task = GNUNET_SCHEDULER_add_delayed (backoff,
868 &reconnect_task,
869 NULL);
870 backoff = GNUNET_TIME_STD_BACKOFF (backoff);
871}
872
873
874/**
875 * A DNS resolution timed out. Notify the application.
876 *
877 * @param cls the `struct GNUNET_RESOLVER_RequestHandle *`
878 */
879static void
880handle_lookup_timeout (void *cls)
881{
882 struct GNUNET_RESOLVER_RequestHandle *rh = cls;
883
884 rh->task = NULL;
885 if (GNUNET_NO == rh->direction)
886 {
887 LOG (GNUNET_ERROR_TYPE_INFO,
888 _ ("Timeout trying to resolve hostname `%s'.\n"),
889 (const char *) &rh[1]);
890 if (NULL != rh->addr_callback)
891 rh->addr_callback (rh->cls,
892 NULL,
893 0);
894 }
895 else
896 {
897#if ! defined(GNUNET_CULL_LOGGING)
898 char buf[INET6_ADDRSTRLEN];
899
900 LOG (GNUNET_ERROR_TYPE_INFO,
901 _ ("Timeout trying to resolve IP address `%s'.\n"),
902 inet_ntop (rh->af,
903 (const void *) &rh[1],
904 buf,
905 sizeof(buf)));
906#endif
907 if (GNUNET_NO == rh->received_response)
908 {
909 char *nret;
910
911 nret = no_resolve (rh->af,
912 &rh[1],
913 rh->data_len);
914 if (NULL != rh->name_callback)
915 rh->name_callback (rh->cls, nret);
916 GNUNET_free (nret);
917 }
918 /* finally, make termination call */
919 if (NULL != rh->name_callback)
920 rh->name_callback (rh->cls,
921 NULL);
922 }
923 rh->was_transmitted = GNUNET_NO;
924 GNUNET_RESOLVER_request_cancel (rh);
925 process_requests ();
926}
927
928
929/**
930 * Convert a string to one or more IP addresses.
931 *
932 * @param hostname the hostname to resolve
933 * @param af AF_INET or AF_INET6; use AF_UNSPEC for "any"
934 * @param callback function to call with addresses
935 * @param callback_cls closure for @a callback
936 * @param timeout how long to try resolving
937 * @return handle that can be used to cancel the request, NULL on error
938 */
939struct GNUNET_RESOLVER_RequestHandle *
940GNUNET_RESOLVER_ip_get (const char *hostname,
941 int af,
942 struct GNUNET_TIME_Relative timeout,
943 GNUNET_RESOLVER_AddressCallback callback,
944 void *callback_cls)
945{
946 struct GNUNET_RESOLVER_RequestHandle *rh;
947 size_t slen;
948 struct in_addr v4;
949 struct in6_addr v6;
950
951 slen = strlen (hostname) + 1;
952 if (slen + sizeof(struct GNUNET_RESOLVER_GetMessage) >=
953 GNUNET_MAX_MESSAGE_SIZE)
954 {
955 GNUNET_break (0);
956 return NULL;
957 }
958 LOG (GNUNET_ERROR_TYPE_DEBUG,
959 "Trying to resolve hostname `%s'.\n",
960 hostname);
961 rh = GNUNET_malloc (sizeof(struct GNUNET_RESOLVER_RequestHandle) + slen);
962 rh->af = af;
963 rh->id = ++last_request_id;
964 rh->addr_callback = callback;
965 rh->cls = callback_cls;
966 GNUNET_memcpy (&rh[1],
967 hostname,
968 slen);
969 rh->data_len = slen;
970 rh->timeout = GNUNET_TIME_relative_to_absolute (timeout);
971 rh->direction = GNUNET_NO;
972 /* first, check if this is a numeric address */
973 if (((1 == inet_pton (AF_INET,
974 hostname,
975 &v4)) &&
976 ((af == AF_INET) ||
977 (af == AF_UNSPEC))) ||
978 ((1 == inet_pton (AF_INET6,
979 hostname,
980 &v6)) &&
981 ((af == AF_INET6) ||
982 (af == AF_UNSPEC))))
983 {
984 rh->task = GNUNET_SCHEDULER_add_now (&numeric_resolution,
985 rh);
986 return rh;
987 }
988 /* then, check if this is a loopback address */
989 for (unsigned int i = 0;
990 NULL != loopback[i];
991 i++)
992 if (0 == strcasecmp (loopback[i],
993 hostname))
994 {
995 rh->task = GNUNET_SCHEDULER_add_now (&loopback_resolution,
996 rh);
997 return rh;
998 }
999 if (GNUNET_OK != check_config ())
1000 {
1001 GNUNET_free (rh);
1002 return NULL;
1003 }
1004 rh->task = GNUNET_SCHEDULER_add_delayed (timeout,
1005 &handle_lookup_timeout,
1006 rh);
1007 GNUNET_CONTAINER_DLL_insert_tail (req_head,
1008 req_tail,
1009 rh);
1010 rh->was_queued = GNUNET_YES;
1011 if (NULL != s_task)
1012 {
1013 GNUNET_SCHEDULER_cancel (s_task);
1014 s_task = NULL;
1015 }
1016 process_requests ();
1017 return rh;
1018}
1019
1020
1021/**
1022 * We've been asked to convert an address to a string without
1023 * a reverse lookup, either because the client asked for it
1024 * or because the DNS lookup hit a timeout. Do the numeric
1025 * conversion and invoke the callback.
1026 *
1027 * @param cls `struct GNUNET_RESOLVER_RequestHandle` for the request
1028 */
1029static void
1030numeric_reverse (void *cls)
1031{
1032 struct GNUNET_RESOLVER_RequestHandle *rh = cls;
1033 char *result;
1034
1035 rh->task = NULL;
1036 result = no_resolve (rh->af,
1037 &rh[1],
1038 rh->data_len);
1039 LOG (GNUNET_ERROR_TYPE_DEBUG,
1040 "Resolver returns `%s'.\n",
1041 result);
1042 if (NULL != result)
1043 {
1044 rh->name_callback (rh->cls,
1045 result);
1046 GNUNET_free (result);
1047 }
1048 rh->name_callback (rh->cls,
1049 NULL);
1050 if (NULL != rh->task)
1051 {
1052 GNUNET_SCHEDULER_cancel (rh->task);
1053 rh->task = NULL;
1054 }
1055 GNUNET_free (rh);
1056}
1057
1058
1059/**
1060 * Get an IP address as a string.
1061 *
1062 * @param sa host address
1063 * @param salen length of host address in @a sa
1064 * @param do_resolve use #GNUNET_NO to return numeric hostname
1065 * @param timeout how long to try resolving
1066 * @param callback function to call with hostnames
1067 * last callback is NULL when finished
1068 * @param cls closure for @a callback
1069 * @return handle that can be used to cancel the request
1070 */
1071struct GNUNET_RESOLVER_RequestHandle *
1072GNUNET_RESOLVER_hostname_get (const struct sockaddr *sa,
1073 socklen_t salen,
1074 int do_resolve,
1075 struct GNUNET_TIME_Relative timeout,
1076 GNUNET_RESOLVER_HostnameCallback callback,
1077 void *cls)
1078{
1079 struct GNUNET_RESOLVER_RequestHandle *rh;
1080 size_t ip_len;
1081 const void *ip;
1082
1083 if (GNUNET_OK != check_config ())
1084 {
1085 LOG (GNUNET_ERROR_TYPE_ERROR,
1086 _ ("Resolver not configured correctly.\n"));
1087 return NULL;
1088 }
1089
1090 switch (sa->sa_family)
1091 {
1092 case AF_INET:
1093 GNUNET_assert (salen == sizeof(struct sockaddr_in));
1094 ip_len = sizeof(struct in_addr);
1095 ip = &((const struct sockaddr_in*) sa)->sin_addr;
1096 break;
1097
1098 case AF_INET6:
1099 GNUNET_assert (salen == sizeof(struct sockaddr_in6));
1100 ip_len = sizeof(struct in6_addr);
1101 ip = &((const struct sockaddr_in6*) sa)->sin6_addr;
1102 break;
1103
1104 default:
1105 GNUNET_break (0);
1106 return NULL;
1107 }
1108 rh = GNUNET_malloc (sizeof(struct GNUNET_RESOLVER_RequestHandle) + salen);
1109 rh->name_callback = callback;
1110 rh->cls = cls;
1111 rh->af = sa->sa_family;
1112 rh->id = ++last_request_id;
1113 rh->timeout = GNUNET_TIME_relative_to_absolute (timeout);
1114 GNUNET_memcpy (&rh[1],
1115 ip,
1116 ip_len);
1117 rh->data_len = ip_len;
1118 rh->direction = GNUNET_YES;
1119 rh->received_response = GNUNET_NO;
1120 if (GNUNET_NO == do_resolve)
1121 {
1122 rh->task = GNUNET_SCHEDULER_add_now (&numeric_reverse,
1123 rh);
1124 return rh;
1125 }
1126 rh->task = GNUNET_SCHEDULER_add_delayed (timeout,
1127 &handle_lookup_timeout,
1128 rh);
1129 GNUNET_CONTAINER_DLL_insert_tail (req_head,
1130 req_tail,
1131 rh);
1132 rh->was_queued = GNUNET_YES;
1133 if (NULL != s_task)
1134 {
1135 GNUNET_SCHEDULER_cancel (s_task);
1136 s_task = NULL;
1137 }
1138 process_requests ();
1139 return rh;
1140}
1141
1142
1143/**
1144 * Get local fully qualified af name
1145 *
1146 * @return fqdn
1147 */
1148char *
1149GNUNET_RESOLVER_local_fqdn_get ()
1150{
1151 char hostname[GNUNET_OS_get_hostname_max_length () + 1];
1152
1153 if (0 != gethostname (hostname,
1154 sizeof(hostname) - 1))
1155 {
1156 LOG_STRERROR (GNUNET_ERROR_TYPE_ERROR | GNUNET_ERROR_TYPE_BULK,
1157 "gethostname");
1158 return NULL;
1159 }
1160 LOG (GNUNET_ERROR_TYPE_DEBUG,
1161 "Resolving our FQDN `%s'\n",
1162 hostname);
1163#if HAVE_GETADDRINFO
1164 {
1165 struct addrinfo *ai;
1166 int ret;
1167 char *rval;
1168
1169 if (0 != (ret = getaddrinfo (hostname,
1170 NULL,
1171 NULL,
1172 &ai)))
1173 {
1174 LOG (GNUNET_ERROR_TYPE_ERROR,
1175 _ ("Could not resolve our FQDN: %s\n"),
1176 gai_strerror (ret));
1177 return NULL;
1178 }
1179 if (NULL != ai->ai_canonname)
1180 rval = GNUNET_strdup (ai->ai_canonname);
1181 else
1182 rval = GNUNET_strdup (hostname);
1183 freeaddrinfo (ai);
1184 return rval;
1185 }
1186#elif HAVE_GETHOSTBYNAME2
1187 {
1188 struct hostent *host;
1189
1190 host = gethostbyname2 (hostname,
1191 AF_INET);
1192 if (NULL == host)
1193 host = gethostbyname2 (hostname,
1194 AF_INET6);
1195 if (NULL == host)
1196 {
1197 LOG (GNUNET_ERROR_TYPE_ERROR,
1198 _ ("Could not resolve our FQDN: %s\n"),
1199 hstrerror (h_errno));
1200 return NULL;
1201 }
1202 return GNUNET_strdup (host->h_name);
1203 }
1204#elif HAVE_GETHOSTBYNAME
1205 {
1206 struct hostent *host;
1207
1208 host = gethostbyname (hostname);
1209 if (NULL == host)
1210 {
1211 LOG (GNUNET_ERROR_TYPE_ERROR,
1212 _ ("Could not resolve our FQDN: %s\n"),
1213 hstrerror (h_errno));
1214 return NULL;
1215 }
1216 return GNUNET_strdup (host->h_name);
1217 }
1218#else
1219 /* fallback: just hope name is already FQDN */
1220 return GNUNET_strdup (hostname);
1221#endif
1222}
1223
1224
1225/**
1226 * Looking our own hostname.
1227 *
1228 * @param af AF_INET or AF_INET6; use AF_UNSPEC for "any"
1229 * @param timeout how long to try resolving
1230 * @param callback function to call with addresses
1231 * @param cls closure for @a callback
1232 * @return handle that can be used to cancel the request, NULL on error
1233 */
1234struct GNUNET_RESOLVER_RequestHandle *
1235GNUNET_RESOLVER_hostname_resolve (int af,
1236 struct GNUNET_TIME_Relative timeout,
1237 GNUNET_RESOLVER_AddressCallback callback,
1238 void *cls)
1239{
1240 char hostname[GNUNET_OS_get_hostname_max_length () + 1];
1241
1242 if (0 != gethostname (hostname, sizeof(hostname) - 1))
1243 {
1244 LOG_STRERROR (GNUNET_ERROR_TYPE_ERROR | GNUNET_ERROR_TYPE_BULK,
1245 "gethostname");
1246 return NULL;
1247 }
1248 LOG (GNUNET_ERROR_TYPE_DEBUG,
1249 "Resolving our hostname `%s'\n",
1250 hostname);
1251 return GNUNET_RESOLVER_ip_get (hostname,
1252 af,
1253 timeout,
1254 callback,
1255 cls);
1256}
1257
1258
1259/**
1260 * Cancel a request that is still pending with the resolver.
1261 * Note that a client MUST NOT cancel a request that has
1262 * been completed (i.e, the callback has been called to
1263 * signal timeout or the final result).
1264 *
1265 * @param rh handle of request to cancel
1266 */
1267void
1268GNUNET_RESOLVER_request_cancel (struct GNUNET_RESOLVER_RequestHandle *rh)
1269{
1270 if (GNUNET_NO == rh->direction)
1271 LOG (GNUNET_ERROR_TYPE_DEBUG,
1272 "Asked to cancel request to resolve hostname `%s'.\n",
1273 (const char *) &rh[1]);
1274 if (NULL != rh->task)
1275 {
1276 GNUNET_SCHEDULER_cancel (rh->task);
1277 rh->task = NULL;
1278 }
1279 if (GNUNET_NO == rh->was_transmitted)
1280 {
1281 if (GNUNET_YES == rh->was_queued)
1282 GNUNET_CONTAINER_DLL_remove (req_head,
1283 req_tail,
1284 rh);
1285 GNUNET_free (rh);
1286 check_disconnect ();
1287 return;
1288 }
1289 GNUNET_assert (GNUNET_YES == rh->was_transmitted);
1290 rh->was_transmitted = GNUNET_SYSERR; /* mark as cancelled */
1291 check_disconnect ();
1292}
1293
1294
1295/* end of resolver_api.c */
diff --git a/src/lib/util/scheduler.c b/src/lib/util/scheduler.c
new file mode 100644
index 000000000..038d2f6ac
--- /dev/null
+++ b/src/lib/util/scheduler.c
@@ -0,0 +1,2583 @@
1/*
2 This file is part of GNUnet
3 Copyright (C) 2009-2017, 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 util/scheduler.c
22 * @brief schedule computations using continuation passing style
23 * @author Christian Grothoff
24 */
25
26#include "platform.h"
27#include "gnunet_util_lib.h"
28#include "disk.h"
29// DEBUG
30#include <inttypes.h>
31
32#define LOG(kind, ...) GNUNET_log_from (kind, "util-scheduler", __VA_ARGS__)
33
34#define LOG_STRERROR(kind, syscall) GNUNET_log_from_strerror (kind, \
35 "util-scheduler", \
36 syscall)
37
38
39#if HAVE_EXECINFO_H
40#include "execinfo.h"
41
42/**
43 * Use lsof to generate file descriptor reports on select error?
44 * (turn off for stable releases).
45 */
46#define USE_LSOF GNUNET_NO
47
48/**
49 * Obtain trace information for all scheduler calls that schedule tasks.
50 */
51#define EXECINFO GNUNET_NO
52
53/**
54 * Check each file descriptor before adding
55 */
56#define DEBUG_FDS GNUNET_NO
57
58/**
59 * Depth of the traces collected via EXECINFO.
60 */
61#define MAX_TRACE_DEPTH 50
62#endif
63
64/**
65 * Should we figure out which tasks are delayed for a while
66 * before they are run? (Consider using in combination with EXECINFO).
67 */
68#define PROFILE_DELAYS GNUNET_NO
69
70/**
71 * Task that were in the queue for longer than this are reported if
72 * PROFILE_DELAYS is active.
73 */
74#define DELAY_THRESHOLD GNUNET_TIME_UNIT_SECONDS
75
76
77/**
78 * Argument to be passed from the driver to
79 * #GNUNET_SCHEDULER_do_work(). Contains the
80 * scheduler's internal state.
81 */
82struct GNUNET_SCHEDULER_Handle
83{
84 /**
85 * Passed here to avoid constantly allocating/deallocating
86 * this element, but generally we want to get rid of this.
87 * @deprecated
88 */
89 struct GNUNET_NETWORK_FDSet *rs;
90
91 /**
92 * Passed here to avoid constantly allocating/deallocating
93 * this element, but generally we want to get rid of this.
94 * @deprecated
95 */
96 struct GNUNET_NETWORK_FDSet *ws;
97
98 /**
99 * context of the SIGINT handler
100 */
101 struct GNUNET_SIGNAL_Context *shc_int;
102
103 /**
104 * context of the SIGTERM handler
105 */
106 struct GNUNET_SIGNAL_Context *shc_term;
107
108#if (SIGTERM != GNUNET_TERM_SIG)
109 /**
110 * context of the TERM_SIG handler
111 */
112 struct GNUNET_SIGNAL_Context *shc_gterm;
113#endif
114
115 /**
116 * context of the SIGQUIT handler
117 */
118 struct GNUNET_SIGNAL_Context *shc_quit;
119
120 /**
121 * context of the SIGHUP handler
122 */
123 struct GNUNET_SIGNAL_Context *shc_hup;
124
125 /**
126 * context of the SIGPIPE handler
127 */
128 struct GNUNET_SIGNAL_Context *shc_pipe;
129};
130
131
132/**
133 * Entry in list of pending tasks.
134 */
135struct GNUNET_SCHEDULER_Task
136{
137 /**
138 * This is a linked list.
139 */
140 struct GNUNET_SCHEDULER_Task *next;
141
142 /**
143 * This is a linked list.
144 */
145 struct GNUNET_SCHEDULER_Task *prev;
146
147 /**
148 * Function to run when ready.
149 */
150 GNUNET_SCHEDULER_TaskCallback callback;
151
152 /**
153 * Closure for the @e callback.
154 */
155 void *callback_cls;
156
157 /**
158 * Information about which FDs are ready for this task (and why).
159 */
160 struct GNUNET_SCHEDULER_FdInfo *fds;
161
162 /**
163 * Storage location used for @e fds if we want to avoid
164 * a separate malloc() call in the common case that this
165 * task is only about a single FD.
166 */
167 struct GNUNET_SCHEDULER_FdInfo fdx;
168
169 /**
170 * Size of the @e fds array.
171 */
172 unsigned int fds_len;
173
174 /**
175 * Do we own the network and file handles referenced by the FdInfo
176 * structs in the fds array. This will only be GNUNET_YES if the
177 * task was created by the #GNUNET_SCHEDULER_add_select function.
178 */
179 int own_handles;
180
181 /**
182 * Absolute timeout value for the task, or
183 * #GNUNET_TIME_UNIT_FOREVER_ABS for "no timeout".
184 */
185 struct GNUNET_TIME_Absolute timeout;
186
187#if PROFILE_DELAYS
188 /**
189 * When was the task scheduled?
190 */
191 struct GNUNET_TIME_Absolute start_time;
192#endif
193
194 /**
195 * Why is the task ready? Set after task is added to ready queue.
196 * Initially set to zero. All reasons that have already been
197 * satisfied (e.g. read or write ready) will be set over time.
198 */
199 enum GNUNET_SCHEDULER_Reason reason;
200
201 /**
202 * Task priority.
203 */
204 enum GNUNET_SCHEDULER_Priority priority;
205
206 /**
207 * Set if we only wait for reading from a single FD, otherwise -1.
208 */
209 int read_fd;
210
211 /**
212 * Set if we only wait for writing to a single FD, otherwise -1.
213 */
214 int write_fd;
215
216 /**
217 * Should the existence of this task in the queue be counted as
218 * reason to not shutdown the scheduler?
219 */
220 int lifeness;
221
222 /**
223 * Is this task run on shutdown?
224 */
225 int on_shutdown;
226
227 /**
228 * Is this task in the ready list?
229 */
230 int in_ready_list;
231
232#if EXECINFO
233 /**
234 * Array of strings which make up a backtrace from the point when this
235 * task was scheduled (essentially, who scheduled the task?)
236 */
237 char **backtrace_strings;
238
239 /**
240 * Size of the backtrace_strings array
241 */
242 int num_backtrace_strings;
243#endif
244
245 /**
246 * Asynchronous scope of the task that scheduled this scope,
247 */
248 struct GNUNET_AsyncScopeSave scope;
249};
250
251/**
252 * Placed at the end of a ready queue to indicate where a scheduler run pass
253 * ends. The next, prev, in_ready_list and priority fields are the only ones
254 * that should be used.
255 */
256static struct GNUNET_SCHEDULER_Task pass_end_marker;
257
258
259/**
260 * A struct representing an event the select driver is waiting for
261 */
262struct Scheduled
263{
264 struct Scheduled *prev;
265
266 struct Scheduled *next;
267
268 /**
269 * the task, the event is related to
270 */
271 struct GNUNET_SCHEDULER_Task *task;
272
273 /**
274 * information about the network socket / file descriptor where
275 * the event is expected to occur
276 */
277 struct GNUNET_SCHEDULER_FdInfo *fdi;
278
279 /**
280 * the event types (multiple event types can be ORed) the select
281 * driver is expected to wait for
282 */
283 enum GNUNET_SCHEDULER_EventType et;
284};
285
286
287/**
288 * Driver context used by GNUNET_SCHEDULER_run
289 */
290struct DriverContext
291{
292 /**
293 * the head of a DLL containing information about the events the
294 * select driver is waiting for
295 */
296 struct Scheduled *scheduled_head;
297
298 /**
299 * the tail of a DLL containing information about the events the
300 * select driver is waiting for
301 */
302 struct Scheduled *scheduled_tail;
303
304 /**
305 * the time when the select driver will wake up again (after
306 * calling select)
307 */
308 struct GNUNET_TIME_Absolute timeout;
309};
310
311
312/**
313 * The driver used for the event loop. Will be handed over to
314 * the scheduler in #GNUNET_SCHEDULER_do_work(), persisted
315 * there in this variable for later use in functions like
316 * #GNUNET_SCHEDULER_add_select(), #add_without_sets() and
317 * #GNUNET_SCHEDULER_cancel().
318 */
319static const struct GNUNET_SCHEDULER_Driver *scheduler_driver;
320
321/**
322 * Head of list of tasks waiting for an event.
323 */
324static struct GNUNET_SCHEDULER_Task *pending_head;
325
326/**
327 * Tail of list of tasks waiting for an event.
328 */
329static struct GNUNET_SCHEDULER_Task *pending_tail;
330
331/**
332 * Head of list of tasks waiting for shutdown.
333 */
334static struct GNUNET_SCHEDULER_Task *shutdown_head;
335
336/**
337 * Tail of list of tasks waiting for shutdown.
338 */
339static struct GNUNET_SCHEDULER_Task *shutdown_tail;
340
341/**
342 * List of tasks waiting ONLY for a timeout event.
343 * Sorted by timeout (earliest first). Used so that
344 * we do not traverse the list of these tasks when
345 * building select sets (we just look at the head
346 * to determine the respective timeout ONCE).
347 */
348static struct GNUNET_SCHEDULER_Task *pending_timeout_head;
349
350/**
351 * List of tasks waiting ONLY for a timeout event.
352 * Sorted by timeout (earliest first). Used so that
353 * we do not traverse the list of these tasks when
354 * building select sets (we just look at the head
355 * to determine the respective timeout ONCE).
356 */
357static struct GNUNET_SCHEDULER_Task *pending_timeout_tail;
358
359/**
360 * Last inserted task waiting ONLY for a timeout event.
361 * Used to (heuristically) speed up insertion.
362 */
363static struct GNUNET_SCHEDULER_Task *pending_timeout_last;
364
365/**
366 * ID of the task that is running right now.
367 */
368static struct GNUNET_SCHEDULER_Task *active_task;
369
370/**
371 * Head of list of tasks ready to run right now, grouped by importance.
372 */
373static struct
374GNUNET_SCHEDULER_Task *ready_head[GNUNET_SCHEDULER_PRIORITY_COUNT];
375
376/**
377 * Tail of list of tasks ready to run right now, grouped by importance.
378 */
379static struct
380GNUNET_SCHEDULER_Task *ready_tail[GNUNET_SCHEDULER_PRIORITY_COUNT];
381
382/**
383 * Task for installing parent control handlers (it might happen that the
384 * scheduler is shutdown before this task is executed, so
385 * GNUNET_SCHEDULER_shutdown must cancel it in that case)
386 */
387static struct GNUNET_SCHEDULER_Task *install_parent_control_task;
388
389/**
390 * Task for reading from a pipe that signal handlers will use to initiate
391 * shutdown
392 */
393static struct GNUNET_SCHEDULER_Task *shutdown_pipe_task;
394
395/**
396 * Number of tasks on the ready list.
397 */
398static unsigned int ready_count;
399
400/**
401 * Priority of the task running right now. Only
402 * valid while a task is running.
403 */
404static enum GNUNET_SCHEDULER_Priority current_priority;
405
406/**
407 * Priority of the highest task added in the current select
408 * iteration.
409 */
410static enum GNUNET_SCHEDULER_Priority max_priority_added;
411
412/**
413 * Value of the 'lifeness' flag for the current task.
414 */
415static int current_lifeness;
416
417/**
418 * Priority used currently in #GNUNET_SCHEDULER_do_work().
419 */
420static enum GNUNET_SCHEDULER_Priority work_priority;
421
422/**
423 * Function to use as a select() in the scheduler.
424 * If NULL, we use GNUNET_NETWORK_socket_select().
425 */
426static GNUNET_SCHEDULER_select scheduler_select;
427
428/**
429 * Task context of the current task.
430 */
431static struct GNUNET_SCHEDULER_TaskContext tc;
432
433/**
434 * Closure for #scheduler_select.
435 */
436static void *scheduler_select_cls;
437
438
439/**
440 * Sets the select function to use in the scheduler (scheduler_select).
441 *
442 * @param new_select new select function to use
443 * @param new_select_cls closure for @a new_select
444 * @return previously used select function, NULL for default
445 */
446void
447GNUNET_SCHEDULER_set_select (GNUNET_SCHEDULER_select new_select,
448 void *new_select_cls)
449{
450 scheduler_select = new_select;
451 scheduler_select_cls = new_select_cls;
452}
453
454
455/**
456 * Check that the given priority is legal (and return it).
457 *
458 * @param p priority value to check
459 * @return p on success, 0 on error
460 */
461static enum GNUNET_SCHEDULER_Priority
462check_priority (enum GNUNET_SCHEDULER_Priority p)
463{
464 if ((p >= 0) && (p < GNUNET_SCHEDULER_PRIORITY_COUNT))
465 return p;
466 GNUNET_assert (0);
467 return 0; /* make compiler happy */
468}
469
470
471/**
472 * chooses the nearest timeout from all pending tasks, to be used
473 * to tell the driver the next wakeup time (using its set_wakeup
474 * callback)
475 */
476struct GNUNET_TIME_Absolute
477get_timeout ()
478{
479 struct GNUNET_SCHEDULER_Task *pos;
480 struct GNUNET_TIME_Absolute now;
481 struct GNUNET_TIME_Absolute timeout;
482
483 pos = pending_timeout_head;
484 now = GNUNET_TIME_absolute_get ();
485 timeout = GNUNET_TIME_UNIT_FOREVER_ABS;
486 if (NULL != pos)
487 {
488 if (0 != pos->reason)
489 {
490 return now;
491 }
492 else
493 {
494 timeout = pos->timeout;
495 }
496 }
497 for (pos = pending_head; NULL != pos; pos = pos->next)
498 {
499 if (0 != pos->reason)
500 {
501 return now;
502 }
503 else if ((pos->timeout.abs_value_us !=
504 GNUNET_TIME_UNIT_FOREVER_ABS.abs_value_us) &&
505 (timeout.abs_value_us > pos->timeout.abs_value_us))
506 {
507 timeout = pos->timeout;
508 }
509 }
510 return timeout;
511}
512
513
514static void
515remove_pass_end_marker ()
516{
517 if (pass_end_marker.in_ready_list)
518 {
519 GNUNET_CONTAINER_DLL_remove (ready_head[pass_end_marker.priority],
520 ready_tail[pass_end_marker.priority],
521 &pass_end_marker);
522 pass_end_marker.in_ready_list = GNUNET_NO;
523 }
524}
525
526
527static void
528set_work_priority (enum GNUNET_SCHEDULER_Priority p)
529{
530 remove_pass_end_marker ();
531 GNUNET_CONTAINER_DLL_insert_tail (ready_head[p],
532 ready_tail[p],
533 &pass_end_marker);
534 pass_end_marker.priority = p;
535 pass_end_marker.in_ready_list = GNUNET_YES;
536 work_priority = p;
537}
538
539
540/**
541 * Put a task that is ready for execution into the ready queue.
542 *
543 * @param task task ready for execution
544 */
545static void
546queue_ready_task (struct GNUNET_SCHEDULER_Task *task)
547{
548 enum GNUNET_SCHEDULER_Priority p = check_priority (task->priority);
549
550 GNUNET_CONTAINER_DLL_insert_tail (ready_head[p],
551 ready_tail[p],
552 task);
553 if (p > work_priority)
554 set_work_priority (p);
555 task->in_ready_list = GNUNET_YES;
556 ready_count++;
557}
558
559
560/**
561 * Request the shutdown of a scheduler. Marks all tasks
562 * awaiting shutdown as ready. Note that tasks
563 * scheduled with #GNUNET_SCHEDULER_add_shutdown() AFTER this call
564 * will be delayed until the next shutdown signal.
565 */
566void
567GNUNET_SCHEDULER_shutdown ()
568{
569 struct GNUNET_SCHEDULER_Task *pos;
570
571 LOG (GNUNET_ERROR_TYPE_DEBUG,
572 "GNUNET_SCHEDULER_shutdown\n");
573 if (NULL != install_parent_control_task)
574 {
575 GNUNET_SCHEDULER_cancel (install_parent_control_task);
576 install_parent_control_task = NULL;
577 }
578 if (NULL != shutdown_pipe_task)
579 {
580 GNUNET_SCHEDULER_cancel (shutdown_pipe_task);
581 shutdown_pipe_task = NULL;
582 }
583 while (NULL != (pos = shutdown_head))
584 {
585 GNUNET_CONTAINER_DLL_remove (shutdown_head,
586 shutdown_tail,
587 pos);
588 pos->reason |= GNUNET_SCHEDULER_REASON_SHUTDOWN;
589 queue_ready_task (pos);
590 }
591}
592
593
594/**
595 * Output stack trace of task @a t.
596 *
597 * @param t task to dump stack trace of
598 */
599static void
600dump_backtrace (struct GNUNET_SCHEDULER_Task *t)
601{
602#if EXECINFO
603 for (unsigned int i = 0; i < t->num_backtrace_strings; i++)
604 LOG (GNUNET_ERROR_TYPE_WARNING,
605 "Task %p trace %u: %s\n",
606 t,
607 i,
608 t->backtrace_strings[i]);
609#else
610 (void) t;
611#endif
612}
613
614
615/**
616 * Destroy a task (release associated resources)
617 *
618 * @param t task to destroy
619 */
620static void
621destroy_task (struct GNUNET_SCHEDULER_Task *t)
622{
623 LOG (GNUNET_ERROR_TYPE_DEBUG,
624 "destroying task %p\n",
625 t);
626
627 if (GNUNET_YES == t->own_handles)
628 {
629 for (unsigned int i = 0; i != t->fds_len; ++i)
630 {
631 const struct GNUNET_NETWORK_Handle *fd = t->fds[i].fd;
632 const struct GNUNET_DISK_FileHandle *fh = t->fds[i].fh;
633 if (fd)
634 {
635 GNUNET_NETWORK_socket_free_memory_only_ (
636 (struct GNUNET_NETWORK_Handle *) fd);
637 }
638 if (fh)
639 {
640 // FIXME: on WIN32 this is not enough! A function
641 // GNUNET_DISK_file_free_memory_only would be nice
642 GNUNET_free_nz ((void *) fh);
643 }
644 }
645 }
646 if (t->fds_len > 1)
647 {
648 GNUNET_array_grow (t->fds, t->fds_len, 0);
649 }
650#if EXECINFO
651 GNUNET_free (t->backtrace_strings);
652#endif
653 GNUNET_free (t);
654}
655
656
657/**
658 * Pipe used to communicate shutdown via signal.
659 */
660static struct GNUNET_DISK_PipeHandle *shutdown_pipe_handle;
661
662/**
663 * Process ID of this process at the time we installed the various
664 * signal handlers.
665 */
666static pid_t my_pid;
667
668/**
669 * Signal handler called for SIGPIPE.
670 */
671static void
672sighandler_pipe ()
673{
674 return;
675}
676
677
678/**
679 * Signal handler called for signals that should cause us to shutdown.
680 */
681static void
682sighandler_shutdown (void)
683{
684 static char c;
685 int old_errno = errno; /* backup errno */
686
687 if (getpid () != my_pid)
688 _exit (1); /* we have fork'ed since the signal handler was created,
689 * ignore the signal, see https://gnunet.org/vfork discussion */
690 GNUNET_DISK_file_write (GNUNET_DISK_pipe_handle (
691 shutdown_pipe_handle,
692 GNUNET_DISK_PIPE_END_WRITE),
693 &c, sizeof(c));
694 errno = old_errno;
695}
696
697
698static void
699shutdown_if_no_lifeness (void)
700{
701 struct GNUNET_SCHEDULER_Task *t;
702
703 if (ready_count > 0)
704 return;
705 for (t = pending_head; NULL != t; t = t->next)
706 if (GNUNET_YES == t->lifeness)
707 return;
708 for (t = shutdown_head; NULL != t; t = t->next)
709 if (GNUNET_YES == t->lifeness)
710 return;
711 for (t = pending_timeout_head; NULL != t; t = t->next)
712 if (GNUNET_YES == t->lifeness)
713 return;
714 /* No lifeness! */
715 GNUNET_SCHEDULER_shutdown ();
716}
717
718
719static enum GNUNET_GenericReturnValue
720select_loop (struct GNUNET_SCHEDULER_Handle *sh,
721 struct DriverContext *context);
722
723
724void
725GNUNET_SCHEDULER_run (GNUNET_SCHEDULER_TaskCallback task,
726 void *task_cls)
727{
728 struct GNUNET_SCHEDULER_Handle *sh;
729 struct GNUNET_SCHEDULER_Driver *driver;
730 struct DriverContext context = {
731 .scheduled_head = NULL,
732 .scheduled_tail = NULL,
733 .timeout = GNUNET_TIME_absolute_get ()
734 };
735
736 driver = GNUNET_SCHEDULER_driver_select ();
737 driver->cls = &context;
738 sh = GNUNET_SCHEDULER_driver_init (driver);
739 GNUNET_SCHEDULER_add_with_reason_and_priority (task,
740 task_cls,
741 GNUNET_SCHEDULER_REASON_STARTUP,
742 GNUNET_SCHEDULER_PRIORITY_DEFAULT);
743 GNUNET_break (GNUNET_OK ==
744 select_loop (sh,
745 &context));
746 GNUNET_SCHEDULER_driver_done (sh);
747 GNUNET_free (driver);
748}
749
750
751/**
752 * Obtain the task context, giving the reason why the current task was
753 * started.
754 *
755 * @return current tasks' scheduler context
756 */
757const struct GNUNET_SCHEDULER_TaskContext *
758GNUNET_SCHEDULER_get_task_context ()
759{
760 GNUNET_assert (NULL != active_task);
761 return &tc;
762}
763
764
765/**
766 * Get information about the current load of this scheduler. Use this
767 * function to determine if an elective task should be added or simply
768 * dropped (if the decision should be made based on the number of
769 * tasks ready to run).
770 *
771 * @param p priority level to look at
772 * @return number of tasks pending right now
773 */
774unsigned int
775GNUNET_SCHEDULER_get_load (enum GNUNET_SCHEDULER_Priority p)
776{
777 unsigned int ret;
778
779 GNUNET_assert (NULL != active_task);
780 if (p == GNUNET_SCHEDULER_PRIORITY_COUNT)
781 return ready_count;
782 if (p == GNUNET_SCHEDULER_PRIORITY_KEEP)
783 p = current_priority;
784 ret = 0;
785 for (struct GNUNET_SCHEDULER_Task *pos = ready_head[check_priority (p)];
786 NULL != pos;
787 pos = pos->next)
788 ret++;
789 if (pass_end_marker.in_ready_list && pass_end_marker.priority == p)
790 // Don't count the dummy marker
791 ret--;
792 return ret;
793}
794
795
796void
797init_fd_info (struct GNUNET_SCHEDULER_Task *t,
798 const struct GNUNET_NETWORK_Handle *const *read_nh,
799 unsigned int read_nh_len,
800 const struct GNUNET_NETWORK_Handle *const *write_nh,
801 unsigned int write_nh_len,
802 const struct GNUNET_DISK_FileHandle *const *read_fh,
803 unsigned int read_fh_len,
804 const struct GNUNET_DISK_FileHandle *const *write_fh,
805 unsigned int write_fh_len)
806{
807 // FIXME: if we have exactly two network handles / exactly two file handles
808 // and they are equal, we can make one FdInfo with both
809 // GNUNET_SCHEDULER_ET_IN and GNUNET_SCHEDULER_ET_OUT set.
810 struct GNUNET_SCHEDULER_FdInfo *fdi;
811
812 t->fds_len = read_nh_len + write_nh_len + read_fh_len + write_fh_len;
813 if (1 == t->fds_len)
814 {
815 fdi = &t->fdx;
816 t->fds = fdi;
817 if (1 == read_nh_len)
818 {
819 GNUNET_assert (NULL != read_nh);
820 GNUNET_assert (NULL != *read_nh);
821 fdi->fd = *read_nh;
822 fdi->et = GNUNET_SCHEDULER_ET_IN;
823 fdi->sock = GNUNET_NETWORK_get_fd (*read_nh);
824 t->read_fd = fdi->sock;
825 t->write_fd = -1;
826 }
827 else if (1 == write_nh_len)
828 {
829 GNUNET_assert (NULL != write_nh);
830 GNUNET_assert (NULL != *write_nh);
831 fdi->fd = *write_nh;
832 fdi->et = GNUNET_SCHEDULER_ET_OUT;
833 fdi->sock = GNUNET_NETWORK_get_fd (*write_nh);
834 t->read_fd = -1;
835 t->write_fd = fdi->sock;
836 }
837 else if (1 == read_fh_len)
838 {
839 GNUNET_assert (NULL != read_fh);
840 GNUNET_assert (NULL != *read_fh);
841 fdi->fh = *read_fh;
842 fdi->et = GNUNET_SCHEDULER_ET_IN;
843 fdi->sock = (*read_fh)->fd; // FIXME: does not work under WIN32
844 t->read_fd = fdi->sock;
845 t->write_fd = -1;
846 }
847 else
848 {
849 GNUNET_assert (NULL != write_fh);
850 GNUNET_assert (NULL != *write_fh);
851 fdi->fh = *write_fh;
852 fdi->et = GNUNET_SCHEDULER_ET_OUT;
853 fdi->sock = (*write_fh)->fd; // FIXME: does not work under WIN32
854 t->read_fd = -1;
855 t->write_fd = fdi->sock;
856 }
857 }
858 else
859 {
860 fdi = GNUNET_new_array (t->fds_len, struct GNUNET_SCHEDULER_FdInfo);
861 t->fds = fdi;
862 t->read_fd = -1;
863 t->write_fd = -1;
864 unsigned int i;
865 for (i = 0; i != read_nh_len; ++i)
866 {
867 fdi->fd = read_nh[i];
868 GNUNET_assert (NULL != fdi->fd);
869 fdi->et = GNUNET_SCHEDULER_ET_IN;
870 fdi->sock = GNUNET_NETWORK_get_fd (read_nh[i]);
871 ++fdi;
872 }
873 for (i = 0; i != write_nh_len; ++i)
874 {
875 fdi->fd = write_nh[i];
876 GNUNET_assert (NULL != fdi->fd);
877 fdi->et = GNUNET_SCHEDULER_ET_OUT;
878 fdi->sock = GNUNET_NETWORK_get_fd (write_nh[i]);
879 ++fdi;
880 }
881 for (i = 0; i != read_fh_len; ++i)
882 {
883 fdi->fh = read_fh[i];
884 GNUNET_assert (NULL != fdi->fh);
885 fdi->et = GNUNET_SCHEDULER_ET_IN;
886 fdi->sock = (read_fh[i])->fd; // FIXME: does not work under WIN32
887 ++fdi;
888 }
889 for (i = 0; i != write_fh_len; ++i)
890 {
891 fdi->fh = write_fh[i];
892 GNUNET_assert (NULL != fdi->fh);
893 fdi->et = GNUNET_SCHEDULER_ET_OUT;
894 fdi->sock = (write_fh[i])->fd; // FIXME: does not work under WIN32
895 ++fdi;
896 }
897 }
898}
899
900
901/**
902 * calls the given function @a func on each FdInfo related to @a t.
903 * Optionally updates the event type field in each FdInfo after calling
904 * @a func.
905 *
906 * @param t the task
907 * @param driver_func the function to call with each FdInfo contained in
908 * in @a t
909 * @param if_not_ready only call @a driver_func on FdInfos that are not
910 * ready
911 * @param et the event type to be set in each FdInfo after calling
912 * @a driver_func on it, or -1 if no updating not desired.
913 */
914static void
915driver_add_multiple (struct GNUNET_SCHEDULER_Task *t)
916{
917 struct GNUNET_SCHEDULER_FdInfo *fdi;
918 int success = GNUNET_YES;
919
920 for (unsigned int i = 0; i != t->fds_len; ++i)
921 {
922 fdi = &t->fds[i];
923 success = scheduler_driver->add (scheduler_driver->cls,
924 t,
925 fdi) && success;
926 fdi->et = GNUNET_SCHEDULER_ET_NONE;
927 }
928 if (GNUNET_YES != success)
929 {
930 LOG (GNUNET_ERROR_TYPE_ERROR,
931 "driver could not add task\n");
932 }
933}
934
935
936static void
937install_parent_control_handler (void *cls)
938{
939 (void) cls;
940 install_parent_control_task = NULL;
941 GNUNET_OS_install_parent_control_handler (NULL);
942}
943
944
945static void
946shutdown_pipe_cb (void *cls)
947{
948 char c;
949 const struct GNUNET_DISK_FileHandle *pr;
950
951 (void) cls;
952 shutdown_pipe_task = NULL;
953 pr = GNUNET_DISK_pipe_handle (shutdown_pipe_handle,
954 GNUNET_DISK_PIPE_END_READ);
955 GNUNET_assert (! GNUNET_DISK_handle_invalid (pr));
956 /* consume the signal */
957 GNUNET_DISK_file_read (pr, &c, sizeof(c));
958 /* mark all active tasks as ready due to shutdown */
959 GNUNET_SCHEDULER_shutdown ();
960 shutdown_pipe_task =
961 GNUNET_SCHEDULER_add_read_file (GNUNET_TIME_UNIT_FOREVER_REL,
962 pr,
963 &shutdown_pipe_cb,
964 NULL);
965}
966
967
968/**
969 * Cancel the task with the specified identifier.
970 * The task must not yet have run. Only allowed to be called as long as the
971 * scheduler is running, that is one of the following conditions is met:
972 *
973 * - #GNUNET_SCHEDULER_run has been called and has not returned yet
974 * - #GNUNET_SCHEDULER_driver_init has been run and
975 * #GNUNET_SCHEDULER_driver_done has not been called yet
976 *
977 * @param task id of the task to cancel
978 * @return original closure of the task
979 */
980void *
981GNUNET_SCHEDULER_cancel (struct GNUNET_SCHEDULER_Task *task)
982{
983 enum GNUNET_SCHEDULER_Priority p;
984 int is_fd_task;
985 void *ret;
986
987 LOG (GNUNET_ERROR_TYPE_DEBUG,
988 "canceling task %p\n",
989 task);
990
991 /* scheduler must be running */
992 GNUNET_assert (NULL != scheduler_driver);
993 is_fd_task = (NULL != task->fds);
994 if (is_fd_task)
995 {
996 int del_result = scheduler_driver->del (scheduler_driver->cls, task);
997 if (GNUNET_OK != del_result)
998 {
999 LOG (GNUNET_ERROR_TYPE_ERROR,
1000 "driver could not delete task\n");
1001 GNUNET_assert (0);
1002 }
1003 }
1004 if (! task->in_ready_list)
1005 {
1006 if (is_fd_task)
1007 {
1008 GNUNET_CONTAINER_DLL_remove (pending_head,
1009 pending_tail,
1010 task);
1011 }
1012 else if (GNUNET_YES == task->on_shutdown)
1013 {
1014 GNUNET_CONTAINER_DLL_remove (shutdown_head,
1015 shutdown_tail,
1016 task);
1017 }
1018 else
1019 {
1020 GNUNET_CONTAINER_DLL_remove (pending_timeout_head,
1021 pending_timeout_tail,
1022 task);
1023 if (pending_timeout_last == task)
1024 pending_timeout_last = NULL;
1025 }
1026 }
1027 else
1028 {
1029 p = check_priority (task->priority);
1030 GNUNET_CONTAINER_DLL_remove (ready_head[p],
1031 ready_tail[p],
1032 task);
1033 ready_count--;
1034 }
1035 ret = task->callback_cls;
1036 destroy_task (task);
1037 return ret;
1038}
1039
1040
1041/**
1042 * Initialize backtrace data for task @a t
1043 *
1044 * @param t task to initialize
1045 */
1046static void
1047init_backtrace (struct GNUNET_SCHEDULER_Task *t)
1048{
1049#if EXECINFO
1050 void *backtrace_array[MAX_TRACE_DEPTH];
1051
1052 t->num_backtrace_strings
1053 = backtrace (backtrace_array, MAX_TRACE_DEPTH);
1054 t->backtrace_strings =
1055 backtrace_symbols (backtrace_array,
1056 t->num_backtrace_strings);
1057 dump_backtrace (t);
1058#else
1059 (void) t;
1060#endif
1061}
1062
1063
1064/**
1065 * Continue the current execution with the given function. This is
1066 * similar to the other "add" functions except that there is no delay
1067 * and the reason code can be specified.
1068 *
1069 * @param task main function of the task
1070 * @param task_cls closure for @a task
1071 * @param reason reason for task invocation
1072 * @param priority priority to use for the task
1073 */
1074void
1075GNUNET_SCHEDULER_add_with_reason_and_priority (GNUNET_SCHEDULER_TaskCallback
1076 task,
1077 void *task_cls,
1078 enum GNUNET_SCHEDULER_Reason
1079 reason,
1080 enum GNUNET_SCHEDULER_Priority
1081 priority)
1082{
1083 struct GNUNET_SCHEDULER_Task *t;
1084
1085 /* scheduler must be running */
1086 GNUNET_assert (NULL != scheduler_driver);
1087 GNUNET_assert (NULL != task);
1088 t = GNUNET_new (struct GNUNET_SCHEDULER_Task);
1089 t->read_fd = -1;
1090 t->write_fd = -1;
1091 t->callback = task;
1092 t->callback_cls = task_cls;
1093#if PROFILE_DELAYS
1094 t->start_time = GNUNET_TIME_absolute_get ();
1095#endif
1096 t->reason = reason;
1097 t->priority = check_priority (priority);
1098 t->lifeness = current_lifeness;
1099 LOG (GNUNET_ERROR_TYPE_DEBUG,
1100 "Adding continuation task %p\n",
1101 t);
1102 init_backtrace (t);
1103 queue_ready_task (t);
1104}
1105
1106
1107/**
1108 * Schedule a new task to be run at the specified time. The task
1109 * will be scheduled for execution at time @a at.
1110 *
1111 * @param at time when the operation should run
1112 * @param priority priority to use for the task
1113 * @param task main function of the task
1114 * @param task_cls closure of @a task
1115 * @return unique task identifier for the job
1116 * only valid until @a task is started!
1117 */
1118struct GNUNET_SCHEDULER_Task *
1119GNUNET_SCHEDULER_add_at_with_priority (struct GNUNET_TIME_Absolute at,
1120 enum GNUNET_SCHEDULER_Priority priority,
1121 GNUNET_SCHEDULER_TaskCallback task,
1122 void *task_cls)
1123{
1124 struct GNUNET_SCHEDULER_Task *t;
1125 struct GNUNET_SCHEDULER_Task *pos;
1126 struct GNUNET_SCHEDULER_Task *prev;
1127 struct GNUNET_TIME_Relative left;
1128
1129 /* scheduler must be running */
1130 GNUNET_assert (NULL != scheduler_driver);
1131 GNUNET_assert (NULL != task);
1132 t = GNUNET_new (struct GNUNET_SCHEDULER_Task);
1133 GNUNET_async_scope_get (&t->scope);
1134 t->callback = task;
1135 t->callback_cls = task_cls;
1136 t->read_fd = -1;
1137 t->write_fd = -1;
1138#if PROFILE_DELAYS
1139 t->start_time = GNUNET_TIME_absolute_get ();
1140#endif
1141 t->timeout = at;
1142 t->priority = check_priority (priority);
1143 t->lifeness = current_lifeness;
1144 init_backtrace (t);
1145
1146 left = GNUNET_TIME_absolute_get_remaining (at);
1147 if (0 == left.rel_value_us)
1148 {
1149 queue_ready_task (t);
1150 if (priority > work_priority)
1151 work_priority = priority;
1152 return t;
1153 }
1154
1155 /* try tail first (optimization in case we are
1156 * appending to a long list of tasks with timeouts) */
1157 if ((NULL == pending_timeout_head) ||
1158 (at.abs_value_us < pending_timeout_head->timeout.abs_value_us))
1159 {
1160 GNUNET_CONTAINER_DLL_insert (pending_timeout_head,
1161 pending_timeout_tail,
1162 t);
1163 }
1164 else
1165 {
1166 /* first move from heuristic start backwards to before start time */
1167 prev = pending_timeout_last;
1168 while ((NULL != prev) &&
1169 (prev->timeout.abs_value_us > t->timeout.abs_value_us))
1170 prev = prev->prev;
1171 /* now, move from heuristic start (or head of list) forward to insertion point */
1172 if (NULL == prev)
1173 pos = pending_timeout_head;
1174 else
1175 pos = prev->next;
1176 while ((NULL != pos) && (pos->timeout.abs_value_us <=
1177 t->timeout.abs_value_us))
1178 {
1179 prev = pos;
1180 pos = pos->next;
1181 }
1182 GNUNET_CONTAINER_DLL_insert_after (pending_timeout_head,
1183 pending_timeout_tail,
1184 prev,
1185 t);
1186 }
1187 /* finally, update heuristic insertion point to last insertion... */
1188 pending_timeout_last = t;
1189 LOG (GNUNET_ERROR_TYPE_DEBUG,
1190 "Adding task %p\n",
1191 t);
1192 return t;
1193}
1194
1195
1196/**
1197 * Schedule a new task to be run with a specified delay. The task
1198 * will be scheduled for execution once the delay has expired.
1199 *
1200 * @param delay when should this operation time out?
1201 * @param priority priority to use for the task
1202 * @param task main function of the task
1203 * @param task_cls closure of @a task
1204 * @return unique task identifier for the job
1205 * only valid until @a task is started!
1206 */
1207struct GNUNET_SCHEDULER_Task *
1208GNUNET_SCHEDULER_add_delayed_with_priority (struct GNUNET_TIME_Relative delay,
1209 enum GNUNET_SCHEDULER_Priority
1210 priority,
1211 GNUNET_SCHEDULER_TaskCallback task,
1212 void *task_cls)
1213{
1214 return GNUNET_SCHEDULER_add_at_with_priority (
1215 GNUNET_TIME_relative_to_absolute (delay),
1216 priority,
1217 task,
1218 task_cls);
1219}
1220
1221
1222/**
1223 * Schedule a new task to be run with a specified priority.
1224 *
1225 * @param prio how important is the new task?
1226 * @param task main function of the task
1227 * @param task_cls closure of @a task
1228 * @return unique task identifier for the job
1229 * only valid until @a task is started!
1230 */
1231struct GNUNET_SCHEDULER_Task *
1232GNUNET_SCHEDULER_add_with_priority (enum GNUNET_SCHEDULER_Priority prio,
1233 GNUNET_SCHEDULER_TaskCallback task,
1234 void *task_cls)
1235{
1236 return GNUNET_SCHEDULER_add_delayed_with_priority (GNUNET_TIME_UNIT_ZERO,
1237 prio,
1238 task,
1239 task_cls);
1240}
1241
1242
1243/**
1244 * Schedule a new task to be run at the specified time. The task
1245 * will be scheduled for execution once specified time has been
1246 * reached. It will be run with the DEFAULT priority.
1247 *
1248 * @param at time at which this operation should run
1249 * @param task main function of the task
1250 * @param task_cls closure of @a task
1251 * @return unique task identifier for the job
1252 * only valid until @a task is started!
1253 */
1254struct GNUNET_SCHEDULER_Task *
1255GNUNET_SCHEDULER_add_at (struct GNUNET_TIME_Absolute at,
1256 GNUNET_SCHEDULER_TaskCallback task,
1257 void *task_cls)
1258{
1259 return GNUNET_SCHEDULER_add_at_with_priority (at,
1260 GNUNET_SCHEDULER_PRIORITY_DEFAULT,
1261 task,
1262 task_cls);
1263}
1264
1265
1266/**
1267 * Schedule a new task to be run with a specified delay. The task
1268 * will be scheduled for execution once the delay has expired. It
1269 * will be run with the DEFAULT priority.
1270 *
1271 * @param delay when should this operation time out?
1272 * @param task main function of the task
1273 * @param task_cls closure of @a task
1274 * @return unique task identifier for the job
1275 * only valid until @a task is started!
1276 */
1277struct GNUNET_SCHEDULER_Task *
1278GNUNET_SCHEDULER_add_delayed (struct GNUNET_TIME_Relative delay,
1279 GNUNET_SCHEDULER_TaskCallback task,
1280 void *task_cls)
1281{
1282 return GNUNET_SCHEDULER_add_delayed_with_priority (delay,
1283 GNUNET_SCHEDULER_PRIORITY_DEFAULT,
1284 task,
1285 task_cls);
1286}
1287
1288
1289/**
1290 * Schedule a new task to be run as soon as possible. Note that this
1291 * does not guarantee that this will be the next task that is being
1292 * run, as other tasks with higher priority (or that are already ready
1293 * to run) might get to run first. Just as with delays, clients must
1294 * not rely on any particular order of execution between tasks
1295 * scheduled concurrently.
1296 *
1297 * The task will be run with the DEFAULT priority.
1298 *
1299 * @param task main function of the task
1300 * @param task_cls closure of @a task
1301 * @return unique task identifier for the job
1302 * only valid until @a task is started!
1303 */
1304struct GNUNET_SCHEDULER_Task *
1305GNUNET_SCHEDULER_add_now (GNUNET_SCHEDULER_TaskCallback task,
1306 void *task_cls)
1307{
1308 struct GNUNET_SCHEDULER_Task *t;
1309
1310 t = GNUNET_new (struct GNUNET_SCHEDULER_Task);
1311 GNUNET_async_scope_get (&t->scope);
1312 t->callback = task;
1313 t->callback_cls = task_cls;
1314 t->read_fd = -1;
1315 t->write_fd = -1;
1316#if PROFILE_DELAYS
1317 t->start_time = GNUNET_TIME_absolute_get ();
1318#endif
1319 t->timeout = GNUNET_TIME_UNIT_ZERO_ABS;
1320 t->priority = current_priority;
1321 t->on_shutdown = GNUNET_YES;
1322 t->lifeness = current_lifeness;
1323 queue_ready_task (t);
1324 init_backtrace (t);
1325 return t;
1326}
1327
1328
1329/**
1330 * Schedule a new task to be run on shutdown, that is when a CTRL-C
1331 * signal is received, or when #GNUNET_SCHEDULER_shutdown() is being
1332 * invoked.
1333 *
1334 * @param task main function of the task
1335 * @param task_cls closure of @a task
1336 * @return unique task identifier for the job
1337 * only valid until @a task is started!
1338 */
1339struct GNUNET_SCHEDULER_Task *
1340GNUNET_SCHEDULER_add_shutdown (GNUNET_SCHEDULER_TaskCallback task,
1341 void *task_cls)
1342{
1343 struct GNUNET_SCHEDULER_Task *t;
1344
1345 /* scheduler must be running */
1346 GNUNET_assert (NULL != scheduler_driver);
1347 GNUNET_assert (NULL != task);
1348 t = GNUNET_new (struct GNUNET_SCHEDULER_Task);
1349 GNUNET_async_scope_get (&t->scope);
1350 t->callback = task;
1351 t->callback_cls = task_cls;
1352 t->read_fd = -1;
1353 t->write_fd = -1;
1354#if PROFILE_DELAYS
1355 t->start_time = GNUNET_TIME_absolute_get ();
1356#endif
1357 t->timeout = GNUNET_TIME_UNIT_FOREVER_ABS;
1358 t->priority = GNUNET_SCHEDULER_PRIORITY_SHUTDOWN;
1359 t->on_shutdown = GNUNET_YES;
1360 t->lifeness = GNUNET_NO;
1361 GNUNET_CONTAINER_DLL_insert (shutdown_head,
1362 shutdown_tail,
1363 t);
1364 LOG (GNUNET_ERROR_TYPE_DEBUG,
1365 "Adding shutdown task %p\n",
1366 t);
1367 init_backtrace (t);
1368 return t;
1369}
1370
1371
1372struct GNUNET_SCHEDULER_Task *
1373GNUNET_SCHEDULER_add_now_with_lifeness (int lifeness,
1374 GNUNET_SCHEDULER_TaskCallback task,
1375 void *task_cls)
1376{
1377 struct GNUNET_SCHEDULER_Task *ret;
1378
1379 ret = GNUNET_SCHEDULER_add_now (task, task_cls);
1380 ret->lifeness = lifeness;
1381 return ret;
1382}
1383
1384
1385#if DEBUG_FDS
1386/**
1387 * check a raw file descriptor and abort if it is bad (for debugging purposes)
1388 *
1389 * @param t the task related to the file descriptor
1390 * @param raw_fd the raw file descriptor to check
1391 */
1392void
1393check_fd (struct GNUNET_SCHEDULER_Task *t, int raw_fd)
1394{
1395 if (-1 != raw_fd)
1396 {
1397 int flags = fcntl (raw_fd, F_GETFD);
1398
1399 if ((flags == -1) && (errno == EBADF))
1400 {
1401 LOG (GNUNET_ERROR_TYPE_ERROR,
1402 "Got invalid file descriptor %d!\n",
1403 raw_fd);
1404 init_backtrace (t);
1405 GNUNET_assert (0);
1406 }
1407 }
1408}
1409
1410
1411#endif
1412
1413
1414/**
1415 * Schedule a new task to be run with a specified delay or when any of
1416 * the specified file descriptor sets is ready. The delay can be used
1417 * as a timeout on the socket(s) being ready. The task will be
1418 * scheduled for execution once either the delay has expired or any of
1419 * the socket operations is ready. This is the most general
1420 * function of the "add" family. Note that the "prerequisite_task"
1421 * must be satisfied in addition to any of the other conditions. In
1422 * other words, the task will be started when
1423 * <code>
1424 * (prerequisite-run)
1425 * && (delay-ready
1426 * || any-rs-ready
1427 * || any-ws-ready)
1428 * </code>
1429 *
1430 * @param delay how long should we wait?
1431 * @param priority priority to use
1432 * @param rfd file descriptor we want to read (can be -1)
1433 * @param wfd file descriptors we want to write (can be -1)
1434 * @param task main function of the task
1435 * @param task_cls closure of @a task
1436 * @return unique task identifier for the job
1437 * only valid until @a task is started!
1438 */
1439static struct GNUNET_SCHEDULER_Task *
1440add_without_sets (struct GNUNET_TIME_Relative delay,
1441 enum GNUNET_SCHEDULER_Priority priority,
1442 const struct GNUNET_NETWORK_Handle *read_nh,
1443 const struct GNUNET_NETWORK_Handle *write_nh,
1444 const struct GNUNET_DISK_FileHandle *read_fh,
1445 const struct GNUNET_DISK_FileHandle *write_fh,
1446 GNUNET_SCHEDULER_TaskCallback task,
1447 void *task_cls)
1448{
1449 struct GNUNET_SCHEDULER_Task *t;
1450
1451 /* scheduler must be running */
1452 GNUNET_assert (NULL != scheduler_driver);
1453 GNUNET_assert (NULL != task);
1454 t = GNUNET_new (struct GNUNET_SCHEDULER_Task);
1455 GNUNET_async_scope_get (&t->scope);
1456 init_fd_info (t,
1457 &read_nh,
1458 read_nh ? 1 : 0,
1459 &write_nh,
1460 write_nh ? 1 : 0,
1461 &read_fh,
1462 read_fh ? 1 : 0,
1463 &write_fh,
1464 write_fh ? 1 : 0);
1465 t->callback = task;
1466 t->callback_cls = task_cls;
1467#if DEBUG_FDS
1468 check_fd (t, NULL != read_nh ? GNUNET_NETWORK_get_fd (read_nh) : -1);
1469 check_fd (t, NULL != write_nh ? GNUNET_NETWORK_get_fd (write_nh) : -1);
1470 check_fd (t, NULL != read_fh ? read_fh->fd : -1);
1471 check_fd (t, NULL != write_fh ? write_fh->fd : -1);
1472#endif
1473#if PROFILE_DELAYS
1474 t->start_time = GNUNET_TIME_absolute_get ();
1475#endif
1476 t->timeout = GNUNET_TIME_relative_to_absolute (delay);
1477 t->priority = check_priority ((priority == GNUNET_SCHEDULER_PRIORITY_KEEP) ?
1478 current_priority : priority);
1479 t->lifeness = current_lifeness;
1480 GNUNET_CONTAINER_DLL_insert (pending_head,
1481 pending_tail,
1482 t);
1483 driver_add_multiple (t);
1484 max_priority_added = GNUNET_MAX (max_priority_added,
1485 t->priority);
1486 init_backtrace (t);
1487 return t;
1488}
1489
1490
1491/**
1492 * Schedule a new task to be run with a specified delay or when the
1493 * specified file descriptor is ready for reading. The delay can be
1494 * used as a timeout on the socket being ready. The task will be
1495 * scheduled for execution once either the delay has expired or the
1496 * socket operation is ready. It will be run with the DEFAULT priority.
1497 * Only allowed to be called as long as the scheduler is running, that
1498 * is one of the following conditions is met:
1499 *
1500 * - #GNUNET_SCHEDULER_run has been called and has not returned yet
1501 * - #GNUNET_SCHEDULER_driver_init has been run and
1502 * #GNUNET_SCHEDULER_driver_done has not been called yet
1503 *
1504 * @param delay when should this operation time out?
1505 * @param rfd read file-descriptor
1506 * @param task main function of the task
1507 * @param task_cls closure of @a task
1508 * @return unique task identifier for the job
1509 * only valid until @a task is started!
1510 */
1511struct GNUNET_SCHEDULER_Task *
1512GNUNET_SCHEDULER_add_read_net (struct GNUNET_TIME_Relative delay,
1513 struct GNUNET_NETWORK_Handle *rfd,
1514 GNUNET_SCHEDULER_TaskCallback task,
1515 void *task_cls)
1516{
1517 return GNUNET_SCHEDULER_add_read_net_with_priority (delay,
1518 GNUNET_SCHEDULER_PRIORITY_DEFAULT,
1519 rfd, task, task_cls);
1520}
1521
1522
1523/**
1524 * Schedule a new task to be run with a specified priority and to be
1525 * run after the specified delay or when the specified file descriptor
1526 * is ready for reading. The delay can be used as a timeout on the
1527 * socket being ready. The task will be scheduled for execution once
1528 * either the delay has expired or the socket operation is ready. It
1529 * will be run with the DEFAULT priority.
1530 * Only allowed to be called as long as the scheduler is running, that
1531 * is one of the following conditions is met:
1532 *
1533 * - #GNUNET_SCHEDULER_run has been called and has not returned yet
1534 * - #GNUNET_SCHEDULER_driver_init has been run and
1535 * #GNUNET_SCHEDULER_driver_done has not been called yet
1536 *
1537 * @param delay when should this operation time out?
1538 * @param priority priority to use for the task
1539 * @param rfd read file-descriptor
1540 * @param task main function of the task
1541 * @param task_cls closure of @a task
1542 * @return unique task identifier for the job
1543 * only valid until @a task is started!
1544 */
1545struct GNUNET_SCHEDULER_Task *
1546GNUNET_SCHEDULER_add_read_net_with_priority (struct GNUNET_TIME_Relative delay,
1547 enum GNUNET_SCHEDULER_Priority
1548 priority,
1549 struct GNUNET_NETWORK_Handle *rfd,
1550 GNUNET_SCHEDULER_TaskCallback task,
1551 void *task_cls)
1552{
1553 return GNUNET_SCHEDULER_add_net_with_priority (delay, priority,
1554 rfd,
1555 GNUNET_YES,
1556 GNUNET_NO,
1557 task, task_cls);
1558}
1559
1560
1561/**
1562 * Schedule a new task to be run with a specified delay or when the
1563 * specified file descriptor is ready for writing. The delay can be
1564 * used as a timeout on the socket being ready. The task will be
1565 * scheduled for execution once either the delay has expired or the
1566 * socket operation is ready. It will be run with the priority of
1567 * the calling task.
1568 * Only allowed to be called as long as the scheduler is running, that
1569 * is one of the following conditions is met:
1570 *
1571 * - #GNUNET_SCHEDULER_run has been called and has not returned yet
1572 * - #GNUNET_SCHEDULER_driver_init has been run and
1573 * #GNUNET_SCHEDULER_driver_done has not been called yet
1574 *
1575 * @param delay when should this operation time out?
1576 * @param wfd write file-descriptor
1577 * @param task main function of the task
1578 * @param task_cls closure of @a task
1579 * @return unique task identifier for the job
1580 * only valid until @a task is started!
1581 */
1582struct GNUNET_SCHEDULER_Task *
1583GNUNET_SCHEDULER_add_write_net (struct GNUNET_TIME_Relative delay,
1584 struct GNUNET_NETWORK_Handle *wfd,
1585 GNUNET_SCHEDULER_TaskCallback task,
1586 void *task_cls)
1587{
1588 return GNUNET_SCHEDULER_add_net_with_priority (delay,
1589 GNUNET_SCHEDULER_PRIORITY_DEFAULT,
1590 wfd,
1591 GNUNET_NO, GNUNET_YES,
1592 task, task_cls);
1593}
1594
1595
1596/**
1597 * Schedule a new task to be run with a specified delay or when the
1598 * specified file descriptor is ready. The delay can be
1599 * used as a timeout on the socket being ready. The task will be
1600 * scheduled for execution once either the delay has expired or the
1601 * socket operation is ready.
1602 * Only allowed to be called as long as the scheduler is running, that
1603 * is one of the following conditions is met:
1604 *
1605 * - #GNUNET_SCHEDULER_run has been called and has not returned yet
1606 * - #GNUNET_SCHEDULER_driver_init has been run and
1607 * #GNUNET_SCHEDULER_driver_done has not been called yet
1608 *
1609 * @param delay when should this operation time out?
1610 * @param priority priority of the task
1611 * @param fd file-descriptor
1612 * @param on_read whether to poll the file-descriptor for readability
1613 * @param on_write whether to poll the file-descriptor for writability
1614 * @param task main function of the task
1615 * @param task_cls closure of task
1616 * @return unique task identifier for the job
1617 * only valid until "task" is started!
1618 */
1619struct GNUNET_SCHEDULER_Task *
1620GNUNET_SCHEDULER_add_net_with_priority (struct GNUNET_TIME_Relative delay,
1621 enum GNUNET_SCHEDULER_Priority priority,
1622 struct GNUNET_NETWORK_Handle *fd,
1623 int on_read,
1624 int on_write,
1625 GNUNET_SCHEDULER_TaskCallback task,
1626 void *task_cls)
1627{
1628 /* scheduler must be running */
1629 GNUNET_assert (NULL != scheduler_driver);
1630 GNUNET_assert (on_read || on_write);
1631 GNUNET_assert (GNUNET_NETWORK_get_fd (fd) >= 0);
1632 return add_without_sets (delay, priority,
1633 on_read ? fd : NULL,
1634 on_write ? fd : NULL,
1635 NULL,
1636 NULL,
1637 task, task_cls);
1638}
1639
1640
1641/**
1642 * Schedule a new task to be run with a specified delay or when the
1643 * specified file descriptor is ready for reading. The delay can be
1644 * used as a timeout on the socket being ready. The task will be
1645 * scheduled for execution once either the delay has expired or the
1646 * socket operation is ready. It will be run with the DEFAULT priority.
1647 * Only allowed to be called as long as the scheduler is running, that
1648 * is one of the following conditions is met:
1649 *
1650 * - #GNUNET_SCHEDULER_run has been called and has not returned yet
1651 * - #GNUNET_SCHEDULER_driver_init has been run and
1652 * #GNUNET_SCHEDULER_driver_done has not been called yet
1653 *
1654 * @param delay when should this operation time out?
1655 * @param rfd read file-descriptor
1656 * @param task main function of the task
1657 * @param task_cls closure of @a task
1658 * @return unique task identifier for the job
1659 * only valid until @a task is started!
1660 */
1661struct GNUNET_SCHEDULER_Task *
1662GNUNET_SCHEDULER_add_read_file (struct GNUNET_TIME_Relative delay,
1663 const struct GNUNET_DISK_FileHandle *rfd,
1664 GNUNET_SCHEDULER_TaskCallback task,
1665 void *task_cls)
1666{
1667 return GNUNET_SCHEDULER_add_file_with_priority (
1668 delay, GNUNET_SCHEDULER_PRIORITY_DEFAULT,
1669 rfd, GNUNET_YES, GNUNET_NO,
1670 task, task_cls);
1671}
1672
1673
1674/**
1675 * Schedule a new task to be run with a specified delay or when the
1676 * specified file descriptor is ready for writing. The delay can be
1677 * used as a timeout on the socket being ready. The task will be
1678 * scheduled for execution once either the delay has expired or the
1679 * socket operation is ready. It will be run with the DEFAULT priority.
1680 * Only allowed to be called as long as the scheduler is running, that
1681 * is one of the following conditions is met:
1682 *
1683 * - #GNUNET_SCHEDULER_run has been called and has not returned yet
1684 * - #GNUNET_SCHEDULER_driver_init has been run and
1685 * #GNUNET_SCHEDULER_driver_done has not been called yet
1686 *
1687 * @param delay when should this operation time out?
1688 * @param wfd write file-descriptor
1689 * @param task main function of the task
1690 * @param task_cls closure of @a task
1691 * @return unique task identifier for the job
1692 * only valid until @a task is started!
1693 */
1694struct GNUNET_SCHEDULER_Task *
1695GNUNET_SCHEDULER_add_write_file (struct GNUNET_TIME_Relative delay,
1696 const struct GNUNET_DISK_FileHandle *wfd,
1697 GNUNET_SCHEDULER_TaskCallback task,
1698 void *task_cls)
1699{
1700 return GNUNET_SCHEDULER_add_file_with_priority (
1701 delay, GNUNET_SCHEDULER_PRIORITY_DEFAULT,
1702 wfd, GNUNET_NO, GNUNET_YES,
1703 task, task_cls);
1704}
1705
1706
1707/**
1708 * Schedule a new task to be run with a specified delay or when the
1709 * specified file descriptor is ready. The delay can be
1710 * used as a timeout on the socket being ready. The task will be
1711 * scheduled for execution once either the delay has expired or the
1712 * socket operation is ready.
1713 * Only allowed to be called as long as the scheduler is running, that
1714 * is one of the following conditions is met:
1715 *
1716 * - #GNUNET_SCHEDULER_run has been called and has not returned yet
1717 * - #GNUNET_SCHEDULER_driver_init has been run and
1718 * #GNUNET_SCHEDULER_driver_done has not been called yet
1719 *
1720 * @param delay when should this operation time out?
1721 * @param priority priority of the task
1722 * @param fd file-descriptor
1723 * @param on_read whether to poll the file-descriptor for readability
1724 * @param on_write whether to poll the file-descriptor for writability
1725 * @param task main function of the task
1726 * @param task_cls closure of @a task
1727 * @return unique task identifier for the job
1728 * only valid until @a task is started!
1729 */
1730struct GNUNET_SCHEDULER_Task *
1731GNUNET_SCHEDULER_add_file_with_priority (struct GNUNET_TIME_Relative delay,
1732 enum GNUNET_SCHEDULER_Priority
1733 priority,
1734 const struct
1735 GNUNET_DISK_FileHandle *fd,
1736 int on_read, int on_write,
1737 GNUNET_SCHEDULER_TaskCallback task,
1738 void *task_cls)
1739{
1740 /* scheduler must be running */
1741 GNUNET_assert (NULL != scheduler_driver);
1742 GNUNET_assert (on_read || on_write);
1743 GNUNET_assert (fd->fd >= 0);
1744 return add_without_sets (delay, priority,
1745 NULL,
1746 NULL,
1747 on_read ? fd : NULL,
1748 on_write ? fd : NULL,
1749 task, task_cls);
1750}
1751
1752
1753void
1754extract_handles (const struct GNUNET_NETWORK_FDSet *fdset,
1755 const struct GNUNET_NETWORK_Handle ***ntarget,
1756 unsigned int *extracted_nhandles,
1757 const struct GNUNET_DISK_FileHandle ***ftarget,
1758 unsigned int *extracted_fhandles)
1759{
1760 // FIXME: this implementation only works for unix, for WIN32 the file handles
1761 // in fdset must be handled separately
1762 const struct GNUNET_NETWORK_Handle **nhandles;
1763 const struct GNUNET_DISK_FileHandle **fhandles;
1764 unsigned int nhandles_len;
1765 unsigned int fhandles_len;
1766
1767 nhandles = NULL;
1768 fhandles = NULL;
1769 nhandles_len = 0;
1770 fhandles_len = 0;
1771 for (int sock = 0; sock != fdset->nsds; ++sock)
1772 {
1773 if (GNUNET_YES == GNUNET_NETWORK_fdset_test_native (fdset, sock))
1774 {
1775 struct GNUNET_NETWORK_Handle *nhandle;
1776 struct GNUNET_DISK_FileHandle *fhandle;
1777
1778 nhandle = GNUNET_NETWORK_socket_box_native (sock);
1779 if (NULL != nhandle)
1780 {
1781 GNUNET_array_append (nhandles, nhandles_len, nhandle);
1782 }
1783 else
1784 {
1785 fhandle = GNUNET_DISK_get_handle_from_int_fd (sock);
1786 if (NULL != fhandle)
1787 {
1788 GNUNET_array_append (fhandles, fhandles_len, fhandle);
1789 }
1790 else
1791 {
1792 GNUNET_assert (0);
1793 }
1794 }
1795 }
1796 }
1797 *ntarget = nhandles_len > 0 ? nhandles : NULL;
1798 *ftarget = fhandles_len > 0 ? fhandles : NULL;
1799 *extracted_nhandles = nhandles_len;
1800 *extracted_fhandles = fhandles_len;
1801}
1802
1803
1804/**
1805 * Schedule a new task to be run with a specified delay or when any of
1806 * the specified file descriptor sets is ready. The delay can be used
1807 * as a timeout on the socket(s) being ready. The task will be
1808 * scheduled for execution once either the delay has expired or any of
1809 * the socket operations is ready. This is the most general
1810 * function of the "add" family. Note that the "prerequisite_task"
1811 * must be satisfied in addition to any of the other conditions. In
1812 * other words, the task will be started when
1813 * <code>
1814 * (prerequisite-run)
1815 * && (delay-ready
1816 * || any-rs-ready
1817 * || any-ws-ready) )
1818 * </code>
1819 * Only allowed to be called as long as the scheduler is running, that
1820 * is one of the following conditions is met:
1821 *
1822 * - #GNUNET_SCHEDULER_run has been called and has not returned yet
1823 * - #GNUNET_SCHEDULER_driver_init has been run and
1824 * #GNUNET_SCHEDULER_driver_done has not been called yet
1825 *
1826 * @param prio how important is this task?
1827 * @param delay how long should we wait?
1828 * @param rs set of file descriptors we want to read (can be NULL)
1829 * @param ws set of file descriptors we want to write (can be NULL)
1830 * @param task main function of the task
1831 * @param task_cls closure of @a task
1832 * @return unique task identifier for the job
1833 * only valid until @a task is started!
1834 */
1835struct GNUNET_SCHEDULER_Task *
1836GNUNET_SCHEDULER_add_select (enum GNUNET_SCHEDULER_Priority prio,
1837 struct GNUNET_TIME_Relative delay,
1838 const struct GNUNET_NETWORK_FDSet *rs,
1839 const struct GNUNET_NETWORK_FDSet *ws,
1840 GNUNET_SCHEDULER_TaskCallback task,
1841 void *task_cls)
1842{
1843 struct GNUNET_SCHEDULER_Task *t;
1844 const struct GNUNET_NETWORK_Handle **read_nhandles = NULL;
1845 const struct GNUNET_NETWORK_Handle **write_nhandles = NULL;
1846 const struct GNUNET_DISK_FileHandle **read_fhandles = NULL;
1847 const struct GNUNET_DISK_FileHandle **write_fhandles = NULL;
1848 unsigned int read_nhandles_len = 0;
1849 unsigned int write_nhandles_len = 0;
1850 unsigned int read_fhandles_len = 0;
1851 unsigned int write_fhandles_len = 0;
1852
1853 /* scheduler must be running */
1854 GNUNET_assert (NULL != scheduler_driver);
1855 GNUNET_assert (NULL != task);
1856 int no_rs = (NULL == rs);
1857 int no_ws = (NULL == ws);
1858 int empty_rs = (NULL != rs) && (0 == rs->nsds);
1859 int empty_ws = (NULL != ws) && (0 == ws->nsds);
1860 int no_fds = (no_rs && no_ws) ||
1861 (empty_rs && empty_ws) ||
1862 (no_rs && empty_ws) ||
1863 (no_ws && empty_rs);
1864 if (! no_fds)
1865 {
1866 if (NULL != rs)
1867 {
1868 extract_handles (rs,
1869 &read_nhandles,
1870 &read_nhandles_len,
1871 &read_fhandles,
1872 &read_fhandles_len);
1873 }
1874 if (NULL != ws)
1875 {
1876 extract_handles (ws,
1877 &write_nhandles,
1878 &write_nhandles_len,
1879 &write_fhandles,
1880 &write_fhandles_len);
1881 }
1882 }
1883 /**
1884 * here we consider the case that a GNUNET_NETWORK_FDSet might be empty
1885 * although its maximum FD number (nsds) is greater than 0. We handle
1886 * this case gracefully because some libraries such as libmicrohttpd
1887 * only provide a hint what the maximum FD number in an FD set might be
1888 * and not the exact FD number (see e.g. gnunet-rest-service.c)
1889 */int no_fds_extracted = (0 == read_nhandles_len) &&
1890 (0 == read_fhandles_len) &&
1891 (0 == write_nhandles_len) &&
1892 (0 == write_fhandles_len);
1893 if (no_fds || no_fds_extracted)
1894 return GNUNET_SCHEDULER_add_delayed_with_priority (delay,
1895 prio,
1896 task,
1897 task_cls);
1898 t = GNUNET_new (struct GNUNET_SCHEDULER_Task);
1899 GNUNET_async_scope_get (&t->scope);
1900 init_fd_info (t,
1901 read_nhandles,
1902 read_nhandles_len,
1903 write_nhandles,
1904 write_nhandles_len,
1905 read_fhandles,
1906 read_fhandles_len,
1907 write_fhandles,
1908 write_fhandles_len);
1909 t->callback = task;
1910 t->callback_cls = task_cls;
1911 t->own_handles = GNUNET_YES;
1912 /* free the arrays of pointers to network / file handles, the actual
1913 * handles will be freed in destroy_task */
1914 GNUNET_array_grow (read_nhandles, read_nhandles_len, 0);
1915 GNUNET_array_grow (write_nhandles, write_nhandles_len, 0);
1916 GNUNET_array_grow (read_fhandles, read_fhandles_len, 0);
1917 GNUNET_array_grow (write_fhandles, write_fhandles_len, 0);
1918#if PROFILE_DELAYS
1919 t->start_time = GNUNET_TIME_absolute_get ();
1920#endif
1921 t->timeout = GNUNET_TIME_relative_to_absolute (delay);
1922 t->priority =
1923 check_priority ((prio ==
1924 GNUNET_SCHEDULER_PRIORITY_KEEP) ? current_priority :
1925 prio);
1926 t->lifeness = current_lifeness;
1927 GNUNET_CONTAINER_DLL_insert (pending_head,
1928 pending_tail,
1929 t);
1930 driver_add_multiple (t);
1931 max_priority_added = GNUNET_MAX (max_priority_added,
1932 t->priority);
1933 LOG (GNUNET_ERROR_TYPE_DEBUG,
1934 "Adding task %p\n",
1935 t);
1936 init_backtrace (t);
1937 return t;
1938}
1939
1940
1941/**
1942 * Function used by event-loop implementations to signal the scheduler
1943 * that a particular @a task is ready due to an event specified in the
1944 * et field of @a fdi.
1945 *
1946 * This function will then queue the task to notify the application
1947 * that the task is ready (with the respective priority).
1948 *
1949 * @param task the task that is ready
1950 * @param fdi information about the related FD
1951 */
1952void
1953GNUNET_SCHEDULER_task_ready (struct GNUNET_SCHEDULER_Task *task,
1954 struct GNUNET_SCHEDULER_FdInfo *fdi)
1955{
1956 enum GNUNET_SCHEDULER_Reason reason;
1957
1958 reason = task->reason;
1959 if ((0 == (reason & GNUNET_SCHEDULER_REASON_READ_READY)) &&
1960 (0 != (GNUNET_SCHEDULER_ET_IN & fdi->et)))
1961 reason |= GNUNET_SCHEDULER_REASON_READ_READY;
1962 if ((0 == (reason & GNUNET_SCHEDULER_REASON_WRITE_READY)) &&
1963 (0 != (GNUNET_SCHEDULER_ET_OUT & fdi->et)))
1964 reason |= GNUNET_SCHEDULER_REASON_WRITE_READY;
1965 reason |= GNUNET_SCHEDULER_REASON_PREREQ_DONE;
1966 task->reason = reason;
1967 if (GNUNET_NO == task->in_ready_list)
1968 {
1969 GNUNET_CONTAINER_DLL_remove (pending_head,
1970 pending_tail,
1971 task);
1972 queue_ready_task (task);
1973 }
1974}
1975
1976
1977/**
1978 * Function called by external event loop implementations to tell the
1979 * scheduler to run some of the tasks that are ready. Must be called
1980 * only after #GNUNET_SCHEDULER_driver_init has been called and before
1981 * #GNUNET_SCHEDULER_driver_done is called.
1982 * This function may return even though there are tasks left to run
1983 * just to give other tasks a chance as well. If we return #GNUNET_YES,
1984 * the event loop implementation should call this function again as
1985 * soon as possible, while if we return #GNUNET_NO it must block until
1986 * either the operating system has more work (the scheduler has no more
1987 * work to do right now) or the timeout set by the scheduler (using the
1988 * set_wakeup callback) is reached.
1989 *
1990 * @param sh scheduler handle that was returned by
1991 * #GNUNET_SCHEDULER_driver_init
1992 * @return #GNUNET_YES if there are more tasks that are ready,
1993 * and thus we would like to run more (yield to avoid
1994 * blocking other activities for too long) #GNUNET_NO
1995 * if we are done running tasks (yield to block)
1996 */
1997int
1998GNUNET_SCHEDULER_do_work (struct GNUNET_SCHEDULER_Handle *sh)
1999{
2000 struct GNUNET_SCHEDULER_Task *pos;
2001 struct GNUNET_TIME_Absolute now;
2002
2003 /* check for tasks that reached the timeout! */
2004 now = GNUNET_TIME_absolute_get ();
2005 pos = pending_timeout_head;
2006 while (NULL != pos)
2007 {
2008 struct GNUNET_SCHEDULER_Task *next = pos->next;
2009 if (now.abs_value_us >= pos->timeout.abs_value_us)
2010 pos->reason |= GNUNET_SCHEDULER_REASON_TIMEOUT;
2011 if (0 == pos->reason)
2012 break;
2013 GNUNET_CONTAINER_DLL_remove (pending_timeout_head,
2014 pending_timeout_tail,
2015 pos);
2016 if (pending_timeout_last == pos)
2017 pending_timeout_last = NULL;
2018 queue_ready_task (pos);
2019 pos = next;
2020 }
2021 pos = pending_head;
2022 while (NULL != pos)
2023 {
2024 struct GNUNET_SCHEDULER_Task *next = pos->next;
2025 if (now.abs_value_us >= pos->timeout.abs_value_us)
2026 {
2027 pos->reason |= GNUNET_SCHEDULER_REASON_TIMEOUT;
2028 GNUNET_CONTAINER_DLL_remove (pending_head,
2029 pending_tail,
2030 pos);
2031 queue_ready_task (pos);
2032 }
2033 pos = next;
2034 }
2035
2036 if (0 == ready_count)
2037 {
2038 struct GNUNET_TIME_Absolute timeout = get_timeout ();
2039
2040 if (timeout.abs_value_us > now.abs_value_us)
2041 {
2042 /**
2043 * The event loop called this function before the current timeout was
2044 * reached (and no FD tasks are ready). This is acceptable if
2045 *
2046 * - the system time was changed while the driver was waiting for
2047 * the timeout
2048 * - an external event loop called GNUnet API functions outside of
2049 * the callbacks called in GNUNET_SCHEDULER_do_work and thus
2050 * wasn't notified about the new timeout
2051 *
2052 * It might also mean we are busy-waiting because of a programming
2053 * error in the external event loop.
2054 */
2055 LOG (GNUNET_ERROR_TYPE_DEBUG,
2056 "GNUNET_SCHEDULER_do_work did not find any ready "
2057 "tasks and timeout has not been reached yet.\n");
2058 }
2059 else
2060 {
2061 /**
2062 * the current timeout was reached but no ready tasks were found,
2063 * internal scheduler error!
2064 */
2065 GNUNET_assert (0);
2066 }
2067 }
2068 else
2069 {
2070 /* find out which task priority level we are going to
2071 process this time */
2072 max_priority_added = GNUNET_SCHEDULER_PRIORITY_KEEP;
2073 GNUNET_assert (NULL == ready_head[GNUNET_SCHEDULER_PRIORITY_KEEP]);
2074 /* yes, p>0 is correct, 0 is "KEEP" which should
2075 * always be an empty queue (see assertion)! */
2076 for (work_priority = GNUNET_SCHEDULER_PRIORITY_COUNT - 1;
2077 work_priority > 0;
2078 work_priority--)
2079 {
2080 pos = ready_head[work_priority];
2081 if (NULL != pos)
2082 break;
2083 }
2084 GNUNET_assert (NULL != pos); /* ready_count wrong? */
2085
2086 /* process all *existing* tasks at this priority
2087 level, then yield */
2088 set_work_priority (work_priority);
2089 while (NULL != (pos = ready_head[work_priority])
2090 && pos != &pass_end_marker)
2091 {
2092 GNUNET_CONTAINER_DLL_remove (ready_head[work_priority],
2093 ready_tail[work_priority],
2094 pos);
2095 ready_count--;
2096 current_priority = pos->priority;
2097 current_lifeness = pos->lifeness;
2098 active_task = pos;
2099#if PROFILE_DELAYS
2100 if (GNUNET_TIME_absolute_get_duration (pos->start_time).rel_value_us >
2101 DELAY_THRESHOLD.rel_value_us)
2102 {
2103 LOG (GNUNET_ERROR_TYPE_DEBUG,
2104 "Task %p took %s to be scheduled\n",
2105 pos,
2106 GNUNET_STRINGS_relative_time_to_string (
2107 GNUNET_TIME_absolute_get_duration (pos->start_time),
2108 GNUNET_YES));
2109 }
2110#endif
2111 tc.reason = pos->reason;
2112 GNUNET_NETWORK_fdset_zero (sh->rs);
2113 GNUNET_NETWORK_fdset_zero (sh->ws);
2114 // FIXME: do we have to remove FdInfos from fds if they are not ready?
2115 tc.fds_len = pos->fds_len;
2116 tc.fds = pos->fds;
2117 for (unsigned int i = 0; i != pos->fds_len; ++i)
2118 {
2119 struct GNUNET_SCHEDULER_FdInfo *fdi = &pos->fds[i];
2120 if (0 != (GNUNET_SCHEDULER_ET_IN & fdi->et))
2121 {
2122 GNUNET_NETWORK_fdset_set_native (sh->rs,
2123 fdi->sock);
2124 }
2125 if (0 != (GNUNET_SCHEDULER_ET_OUT & fdi->et))
2126 {
2127 GNUNET_NETWORK_fdset_set_native (sh->ws,
2128 fdi->sock);
2129 }
2130 }
2131 tc.read_ready = sh->rs;
2132 tc.write_ready = sh->ws;
2133 LOG (GNUNET_ERROR_TYPE_DEBUG,
2134 "Running task %p\n",
2135 pos);
2136 GNUNET_assert (NULL != pos->callback);
2137 {
2138 struct GNUNET_AsyncScopeSave old_scope;
2139 if (pos->scope.have_scope)
2140 GNUNET_async_scope_enter (&pos->scope.scope_id, &old_scope);
2141 else
2142 GNUNET_async_scope_get (&old_scope);
2143 pos->callback (pos->callback_cls);
2144 GNUNET_async_scope_restore (&old_scope);
2145 }
2146 if (NULL != pos->fds)
2147 {
2148 int del_result = scheduler_driver->del (scheduler_driver->cls,
2149 pos);
2150 if (GNUNET_OK != del_result)
2151 {
2152 LOG (GNUNET_ERROR_TYPE_ERROR,
2153 "driver could not delete task %p\n", pos);
2154 GNUNET_assert (0);
2155 }
2156 }
2157 active_task = NULL;
2158 dump_backtrace (pos);
2159 destroy_task (pos);
2160 }
2161 remove_pass_end_marker ();
2162 }
2163 shutdown_if_no_lifeness ();
2164 if (0 == ready_count)
2165 {
2166 scheduler_driver->set_wakeup (scheduler_driver->cls,
2167 get_timeout ());
2168 return GNUNET_NO;
2169 }
2170 scheduler_driver->set_wakeup (scheduler_driver->cls,
2171 GNUNET_TIME_absolute_get ());
2172 return GNUNET_YES;
2173}
2174
2175
2176/**
2177 * Function called by external event loop implementations to initialize
2178 * the scheduler. An external implementation has to provide @a driver
2179 * which contains callbacks for the scheduler (see definition of struct
2180 * #GNUNET_SCHEDULER_Driver). The callbacks are used to instruct the
2181 * external implementation to watch for events. If it detects any of
2182 * those events it is expected to call #GNUNET_SCHEDULER_do_work to let
2183 * the scheduler handle it. If an event is related to a specific task
2184 * (e.g. the scheduler gave instructions to watch a file descriptor),
2185 * the external implementation is expected to mark that task ready
2186 * before by calling #GNUNET_SCHEDULER_task_ready.
2187
2188 * This function has to be called before any tasks are scheduled and
2189 * before GNUNET_SCHEDULER_do_work is called for the first time. It
2190 * allocates resources that have to be freed again by calling
2191 * #GNUNET_SCHEDULER_driver_done.
2192 *
2193 * This function installs the same signal handlers as
2194 * #GNUNET_SCHEDULER_run. This means SIGTERM (and other similar signals)
2195 * will induce a call to #GNUNET_SCHEDULER_shutdown during the next
2196 * call to #GNUNET_SCHEDULER_do_work. As a result, SIGTERM causes all
2197 * active tasks to be scheduled with reason
2198 * #GNUNET_SCHEDULER_REASON_SHUTDOWN. (However, tasks added afterwards
2199 * will execute normally!). Note that any particular signal will only
2200 * shut down one scheduler; applications should always only create a
2201 * single scheduler.
2202 *
2203 * @param driver to use for the event loop
2204 * @return handle to be passed to #GNUNET_SCHEDULER_do_work and
2205 * #GNUNET_SCHEDULER_driver_done
2206 */
2207struct GNUNET_SCHEDULER_Handle *
2208GNUNET_SCHEDULER_driver_init (const struct GNUNET_SCHEDULER_Driver *driver)
2209{
2210 struct GNUNET_SCHEDULER_Handle *sh;
2211 const struct GNUNET_DISK_FileHandle *pr;
2212
2213 /* scheduler must not be running */
2214 GNUNET_assert (NULL == scheduler_driver);
2215 GNUNET_assert (NULL == shutdown_pipe_handle);
2216 /* general set-up */
2217 sh = GNUNET_new (struct GNUNET_SCHEDULER_Handle);
2218 shutdown_pipe_handle = GNUNET_DISK_pipe (GNUNET_DISK_PF_NONE);
2219 GNUNET_assert (NULL != shutdown_pipe_handle);
2220 pr = GNUNET_DISK_pipe_handle (shutdown_pipe_handle,
2221 GNUNET_DISK_PIPE_END_READ);
2222 my_pid = getpid ();
2223 scheduler_driver = driver;
2224
2225 /* install signal handlers */
2226 LOG (GNUNET_ERROR_TYPE_DEBUG,
2227 "Registering signal handlers\n");
2228 sh->shc_int = GNUNET_SIGNAL_handler_install (SIGINT,
2229 &sighandler_shutdown);
2230 sh->shc_term = GNUNET_SIGNAL_handler_install (SIGTERM,
2231 &sighandler_shutdown);
2232#if (SIGTERM != GNUNET_TERM_SIG)
2233 sh->shc_gterm = GNUNET_SIGNAL_handler_install (GNUNET_TERM_SIG,
2234 &sighandler_shutdown);
2235#endif
2236 sh->shc_pipe = GNUNET_SIGNAL_handler_install (SIGPIPE,
2237 &sighandler_pipe);
2238 sh->shc_quit = GNUNET_SIGNAL_handler_install (SIGQUIT,
2239 &sighandler_shutdown);
2240 sh->shc_hup = GNUNET_SIGNAL_handler_install (SIGHUP,
2241 &sighandler_shutdown);
2242
2243 /* Setup initial tasks */
2244 current_priority = GNUNET_SCHEDULER_PRIORITY_DEFAULT;
2245 current_lifeness = GNUNET_NO;
2246 /* ensure this task runs first, by using a priority level reserved for
2247 the scheduler (not really shutdown, but start-up ;-) */
2248 install_parent_control_task =
2249 GNUNET_SCHEDULER_add_with_priority (GNUNET_SCHEDULER_PRIORITY_SHUTDOWN,
2250 &install_parent_control_handler,
2251 NULL);
2252 shutdown_pipe_task =
2253 GNUNET_SCHEDULER_add_read_file (GNUNET_TIME_UNIT_FOREVER_REL,
2254 pr,
2255 &shutdown_pipe_cb,
2256 NULL);
2257 current_lifeness = GNUNET_YES;
2258 scheduler_driver->set_wakeup (scheduler_driver->cls,
2259 get_timeout ());
2260 /* begin main event loop */
2261 sh->rs = GNUNET_NETWORK_fdset_create ();
2262 sh->ws = GNUNET_NETWORK_fdset_create ();
2263 GNUNET_NETWORK_fdset_handle_set (sh->rs, pr);
2264 return sh;
2265}
2266
2267
2268/**
2269 * Counter-part of #GNUNET_SCHEDULER_driver_init. Has to be called
2270 * by external event loop implementations after the scheduler has
2271 * shut down. This is the case if both of the following conditions
2272 * are met:
2273 *
2274 * - all tasks the scheduler has added through the driver's add
2275 * callback have been removed again through the driver's del
2276 * callback
2277 * - the timeout the scheduler has set through the driver's
2278 * add_wakeup callback is FOREVER
2279 *
2280 * @param sh the handle returned by #GNUNET_SCHEDULER_driver_init
2281 */
2282void
2283GNUNET_SCHEDULER_driver_done (struct GNUNET_SCHEDULER_Handle *sh)
2284{
2285 GNUNET_break (NULL == pending_head);
2286 GNUNET_break (NULL == pending_timeout_head);
2287 GNUNET_break (NULL == shutdown_head);
2288 for (int i = 0; i != GNUNET_SCHEDULER_PRIORITY_COUNT; ++i)
2289 {
2290 GNUNET_break (NULL == ready_head[i]);
2291 }
2292 GNUNET_NETWORK_fdset_destroy (sh->rs);
2293 GNUNET_NETWORK_fdset_destroy (sh->ws);
2294
2295 /* uninstall signal handlers */
2296 GNUNET_SIGNAL_handler_uninstall (sh->shc_int);
2297 GNUNET_SIGNAL_handler_uninstall (sh->shc_term);
2298#if (SIGTERM != GNUNET_TERM_SIG)
2299 GNUNET_SIGNAL_handler_uninstall (sh->shc_gterm);
2300#endif
2301 GNUNET_SIGNAL_handler_uninstall (sh->shc_pipe);
2302 GNUNET_SIGNAL_handler_uninstall (sh->shc_quit);
2303 GNUNET_SIGNAL_handler_uninstall (sh->shc_hup);
2304 GNUNET_DISK_pipe_close (shutdown_pipe_handle);
2305 shutdown_pipe_handle = NULL;
2306 scheduler_driver = NULL;
2307 GNUNET_free (sh);
2308}
2309
2310
2311static enum GNUNET_GenericReturnValue
2312select_loop (struct GNUNET_SCHEDULER_Handle *sh,
2313 struct DriverContext *context)
2314{
2315 struct GNUNET_NETWORK_FDSet *rs;
2316 struct GNUNET_NETWORK_FDSet *ws;
2317 int select_result;
2318
2319 GNUNET_assert (NULL != context);
2320 rs = GNUNET_NETWORK_fdset_create ();
2321 ws = GNUNET_NETWORK_fdset_create ();
2322 while ((NULL != context->scheduled_head) ||
2323 (GNUNET_TIME_UNIT_FOREVER_ABS.abs_value_us !=
2324 context->timeout.abs_value_us))
2325 {
2326 struct GNUNET_TIME_Relative time_remaining;
2327
2328 LOG (GNUNET_ERROR_TYPE_DEBUG,
2329 "select timeout = %s\n",
2330 GNUNET_STRINGS_absolute_time_to_string (context->timeout));
2331
2332 GNUNET_NETWORK_fdset_zero (rs);
2333 GNUNET_NETWORK_fdset_zero (ws);
2334
2335 for (struct Scheduled *pos = context->scheduled_head;
2336 NULL != pos;
2337 pos = pos->next)
2338 {
2339 if (0 != (GNUNET_SCHEDULER_ET_IN & pos->et))
2340 {
2341 GNUNET_NETWORK_fdset_set_native (rs, pos->fdi->sock);
2342 }
2343 if (0 != (GNUNET_SCHEDULER_ET_OUT & pos->et))
2344 {
2345 GNUNET_NETWORK_fdset_set_native (ws, pos->fdi->sock);
2346 }
2347 }
2348 time_remaining = GNUNET_TIME_absolute_get_remaining (context->timeout);
2349 if (0 < ready_count)
2350 time_remaining = GNUNET_TIME_UNIT_ZERO;
2351 if (NULL == scheduler_select)
2352 {
2353 select_result = GNUNET_NETWORK_socket_select (rs,
2354 ws,
2355 NULL,
2356 time_remaining);
2357 }
2358 else
2359 {
2360 select_result = scheduler_select (scheduler_select_cls,
2361 rs,
2362 ws,
2363 NULL,
2364 time_remaining);
2365 }
2366 if (select_result == GNUNET_SYSERR)
2367 {
2368 if (errno == EINTR)
2369 continue;
2370
2371 LOG_STRERROR (GNUNET_ERROR_TYPE_ERROR,
2372 "select");
2373#if USE_LSOF
2374 char lsof[512];
2375
2376 snprintf (lsof,
2377 sizeof(lsof),
2378 "lsof -p %d",
2379 getpid ());
2380 (void) close (1);
2381 (void) dup2 (2, 1);
2382 if (0 != system (lsof))
2383 LOG_STRERROR (GNUNET_ERROR_TYPE_WARNING,
2384 "system");
2385#endif
2386#if DEBUG_FDS
2387 for (struct Scheduled *s = context->scheduled_head;
2388 NULL != s;
2389 s = s->next)
2390 {
2391 int flags = fcntl (s->fdi->sock,
2392 F_GETFD);
2393
2394 if ((flags == -1) &&
2395 (EBADF == errno))
2396 {
2397 LOG (GNUNET_ERROR_TYPE_ERROR,
2398 "Got invalid file descriptor %d!\n",
2399 s->fdi->sock);
2400#if EXECINFO
2401 dump_backtrace (s->task);
2402#endif
2403 }
2404 }
2405#endif
2406 GNUNET_assert (0);
2407 GNUNET_NETWORK_fdset_destroy (rs);
2408 GNUNET_NETWORK_fdset_destroy (ws);
2409 return GNUNET_SYSERR;
2410 }
2411 if (select_result > 0)
2412 {
2413 for (struct Scheduled *pos = context->scheduled_head;
2414 NULL != pos;
2415 pos = pos->next)
2416 {
2417 int is_ready = GNUNET_NO;
2418
2419 if ((0 != (GNUNET_SCHEDULER_ET_IN & pos->et)) &&
2420 (GNUNET_YES ==
2421 GNUNET_NETWORK_fdset_test_native (rs,
2422 pos->fdi->sock)) )
2423 {
2424 pos->fdi->et |= GNUNET_SCHEDULER_ET_IN;
2425 is_ready = GNUNET_YES;
2426 }
2427 if ((0 != (GNUNET_SCHEDULER_ET_OUT & pos->et)) &&
2428 (GNUNET_YES ==
2429 GNUNET_NETWORK_fdset_test_native (ws,
2430 pos->fdi->sock)) )
2431 {
2432 pos->fdi->et |= GNUNET_SCHEDULER_ET_OUT;
2433 is_ready = GNUNET_YES;
2434 }
2435 if (GNUNET_YES == is_ready)
2436 {
2437 GNUNET_SCHEDULER_task_ready (pos->task,
2438 pos->fdi);
2439 }
2440 }
2441 }
2442 if (GNUNET_YES == GNUNET_SCHEDULER_do_work (sh))
2443 {
2444 LOG (GNUNET_ERROR_TYPE_DEBUG,
2445 "scheduler has more tasks ready!\n");
2446 }
2447 }
2448 GNUNET_NETWORK_fdset_destroy (rs);
2449 GNUNET_NETWORK_fdset_destroy (ws);
2450
2451 if ( (NULL == context->scheduled_head) &&
2452 (NULL != pending_timeout_head) &&
2453 (GNUNET_TIME_UNIT_FOREVER_ABS.abs_value_us ==
2454 context->timeout.abs_value_us) )
2455 {
2456 /* Only remaining task has timeout of 'forever'.
2457 We exit here more as sanity measure, as just
2458 waiting forever isn't exactly useful. Still,
2459 this is indicative of a bug in the client code. */
2460 GNUNET_break (0);
2461 return GNUNET_NO;
2462 }
2463 return GNUNET_OK;
2464}
2465
2466
2467static int
2468select_add (void *cls,
2469 struct GNUNET_SCHEDULER_Task *task,
2470 struct GNUNET_SCHEDULER_FdInfo *fdi)
2471{
2472 struct DriverContext *context = cls;
2473
2474 GNUNET_assert (NULL != context);
2475 GNUNET_assert (NULL != task);
2476 GNUNET_assert (NULL != fdi);
2477 GNUNET_assert (0 != (GNUNET_SCHEDULER_ET_IN & fdi->et) ||
2478 0 != (GNUNET_SCHEDULER_ET_OUT & fdi->et));
2479
2480 if (! ((NULL != fdi->fd) ^ (NULL != fdi->fh)) || (fdi->sock < 0))
2481 {
2482 /* exactly one out of {fd, hf} must be != NULL and the OS handle must be valid */
2483 return GNUNET_SYSERR;
2484 }
2485
2486 struct Scheduled *scheduled = GNUNET_new (struct Scheduled);
2487 scheduled->task = task;
2488 scheduled->fdi = fdi;
2489 scheduled->et = fdi->et;
2490
2491 GNUNET_CONTAINER_DLL_insert (context->scheduled_head,
2492 context->scheduled_tail,
2493 scheduled);
2494 return GNUNET_OK;
2495}
2496
2497
2498static int
2499select_del (void *cls,
2500 struct GNUNET_SCHEDULER_Task *task)
2501{
2502 struct DriverContext *context;
2503 struct Scheduled *pos;
2504 int ret;
2505
2506 GNUNET_assert (NULL != cls);
2507
2508 context = cls;
2509 ret = GNUNET_SYSERR;
2510 pos = context->scheduled_head;
2511 while (NULL != pos)
2512 {
2513 struct Scheduled *next = pos->next;
2514 if (pos->task == task)
2515 {
2516 GNUNET_CONTAINER_DLL_remove (context->scheduled_head,
2517 context->scheduled_tail,
2518 pos);
2519 GNUNET_free (pos);
2520 ret = GNUNET_OK;
2521 }
2522 pos = next;
2523 }
2524 return ret;
2525}
2526
2527
2528static void
2529select_set_wakeup (void *cls,
2530 struct GNUNET_TIME_Absolute dt)
2531{
2532 struct DriverContext *context = cls;
2533
2534 GNUNET_assert (NULL != context);
2535 context->timeout = dt;
2536}
2537
2538
2539/**
2540 * Obtain the driver for using select() as the event loop.
2541 *
2542 * @return NULL on error
2543 */
2544struct GNUNET_SCHEDULER_Driver *
2545GNUNET_SCHEDULER_driver_select ()
2546{
2547 struct GNUNET_SCHEDULER_Driver *select_driver;
2548
2549 select_driver = GNUNET_new (struct GNUNET_SCHEDULER_Driver);
2550
2551 select_driver->add = &select_add;
2552 select_driver->del = &select_del;
2553 select_driver->set_wakeup = &select_set_wakeup;
2554
2555 return select_driver;
2556}
2557
2558
2559/**
2560 * Change the async scope for the currently executing task and (transitively)
2561 * for all tasks scheduled by the current task after calling this function.
2562 * Nested tasks can begin their own nested async scope.
2563 *
2564 * Once the current task is finished, the async scope ID is reset to
2565 * its previous value.
2566 *
2567 * Must only be called from a running task.
2568 *
2569 * @param aid the asynchronous scope id to enter
2570 */
2571void
2572GNUNET_SCHEDULER_begin_async_scope (struct GNUNET_AsyncScopeId *aid)
2573{
2574 struct GNUNET_AsyncScopeSave dummy_old_scope;
2575
2576 GNUNET_assert (NULL != active_task);
2577 /* Since we're in a task, the context will be automatically
2578 restored by the scheduler. */
2579 GNUNET_async_scope_enter (aid, &dummy_old_scope);
2580}
2581
2582
2583/* end of scheduler.c */
diff --git a/src/lib/util/service.c b/src/lib/util/service.c
new file mode 100644
index 000000000..363210c61
--- /dev/null
+++ b/src/lib/util/service.c
@@ -0,0 +1,2572 @@
1/*
2 This file is part of GNUnet.
3 Copyright (C) 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 util/service.c
23 * @brief functions related to starting services (redesign)
24 * @author Christian Grothoff
25 * @author Florian Dold
26 */
27#include "platform.h"
28#include "gnunet_util_lib.h"
29#include "gnunet_protocols.h"
30#include "gnunet_constants.h"
31#include "gnunet_resolver_service.h"
32#include "speedup.h"
33
34#if HAVE_MALLINFO2
35#include <malloc.h>
36#include "gauger.h"
37#endif
38
39
40#define LOG(kind, ...) GNUNET_log_from (kind, "util-service", __VA_ARGS__)
41
42#define LOG_STRERROR(kind, syscall) \
43 GNUNET_log_from_strerror (kind, "util-service", syscall)
44
45#define LOG_STRERROR_FILE(kind, syscall, filename) \
46 GNUNET_log_from_strerror_file (kind, "util-service", syscall, filename)
47
48
49/**
50 * Information the service tracks per listen operation.
51 */
52struct ServiceListenContext
53{
54 /**
55 * Kept in a DLL.
56 */
57 struct ServiceListenContext *next;
58
59 /**
60 * Kept in a DLL.
61 */
62 struct ServiceListenContext *prev;
63
64 /**
65 * Service this listen context belongs to.
66 */
67 struct GNUNET_SERVICE_Handle *sh;
68
69 /**
70 * Socket we are listening on.
71 */
72 struct GNUNET_NETWORK_Handle *listen_socket;
73
74 /**
75 * Task scheduled to do the listening.
76 */
77 struct GNUNET_SCHEDULER_Task *listen_task;
78};
79
80
81/**
82 * Reasons why we might be suspended.
83 */
84enum SuspendReason
85{
86 /**
87 * We are running normally.
88 */
89 SUSPEND_STATE_NONE = 0,
90
91 /**
92 * Application requested it.
93 */
94 SUSPEND_STATE_APP = 1,
95
96 /**
97 * OS ran out of file descriptors.
98 */
99 SUSPEND_STATE_EMFILE = 2,
100
101 /**
102 * Both reasons, APP and EMFILE apply.
103 */
104 SUSPEND_STATE_APP_AND_EMFILE = 3,
105
106 /**
107 * Suspension because service was permanently shutdown.
108 */
109 SUSPEND_STATE_SHUTDOWN = 4
110};
111
112
113/**
114 * Handle to a service.
115 */
116struct GNUNET_SERVICE_Handle
117{
118 /**
119 * Our configuration.
120 */
121 const struct GNUNET_CONFIGURATION_Handle *cfg;
122
123 /**
124 * Name of our service.
125 */
126 const char *service_name;
127
128 /**
129 * Main service-specific task to run.
130 */
131 GNUNET_SERVICE_InitCallback service_init_cb;
132
133 /**
134 * Function to call when clients connect.
135 */
136 GNUNET_SERVICE_ConnectHandler connect_cb;
137
138 /**
139 * Function to call when clients disconnect / are disconnected.
140 */
141 GNUNET_SERVICE_DisconnectHandler disconnect_cb;
142
143 /**
144 * Closure for @e service_init_cb, @e connect_cb, @e disconnect_cb.
145 */
146 void *cb_cls;
147
148 /**
149 * DLL of listen sockets used to accept new connections.
150 */
151 struct ServiceListenContext *slc_head;
152
153 /**
154 * DLL of listen sockets used to accept new connections.
155 */
156 struct ServiceListenContext *slc_tail;
157
158 /**
159 * Our clients, kept in a DLL.
160 */
161 struct GNUNET_SERVICE_Client *clients_head;
162
163 /**
164 * Our clients, kept in a DLL.
165 */
166 struct GNUNET_SERVICE_Client *clients_tail;
167
168 /**
169 * Message handlers to use for all clients.
170 */
171 struct GNUNET_MQ_MessageHandler *handlers;
172
173 /**
174 * Closure for @e task.
175 */
176 void *task_cls;
177
178
179 /**
180 * IPv4 addresses that are not allowed to connect.
181 */
182 struct GNUNET_STRINGS_IPv4NetworkPolicy *v4_denied;
183
184 /**
185 * IPv6 addresses that are not allowed to connect.
186 */
187 struct GNUNET_STRINGS_IPv6NetworkPolicy *v6_denied;
188
189 /**
190 * IPv4 addresses that are allowed to connect (if not
191 * set, all are allowed).
192 */
193 struct GNUNET_STRINGS_IPv4NetworkPolicy *v4_allowed;
194
195 /**
196 * IPv6 addresses that are allowed to connect (if not
197 * set, all are allowed).
198 */
199 struct GNUNET_STRINGS_IPv6NetworkPolicy *v6_allowed;
200
201 /**
202 * Do we require a matching UID for UNIX domain socket connections?
203 * #GNUNET_NO means that the UID does not have to match (however,
204 * @e match_gid may still impose other access control checks).
205 */
206 int match_uid;
207
208 /**
209 * Do we require a matching GID for UNIX domain socket connections?
210 * Ignored if @e match_uid is #GNUNET_YES. Note that this is about
211 * checking that the client's UID is in our group OR that the
212 * client's GID is our GID. If both "match_gid" and @e match_uid are
213 * #GNUNET_NO, all users on the local system have access.
214 */
215 int match_gid;
216
217 /**
218 * Are we suspended, and if so, why?
219 */
220 enum SuspendReason suspend_state;
221
222 /**
223 * Our options.
224 */
225 enum GNUNET_SERVICE_Options options;
226
227 /**
228 * If we are daemonizing, this FD is set to the
229 * pipe to the parent. Send '.' if we started
230 * ok, '!' if not. -1 if we are not daemonizing.
231 */
232 int ready_confirm_fd;
233
234 /**
235 * If true, consider unknown message types an error where the
236 * client is disconnected.
237 */
238 bool require_found;
239};
240
241
242/**
243 * Handle to a client that is connected to a service.
244 */
245struct GNUNET_SERVICE_Client
246{
247 /**
248 * Kept in a DLL.
249 */
250 struct GNUNET_SERVICE_Client *next;
251
252 /**
253 * Kept in a DLL.
254 */
255 struct GNUNET_SERVICE_Client *prev;
256
257 /**
258 * Service that this client belongs to.
259 */
260 struct GNUNET_SERVICE_Handle *sh;
261
262 /**
263 * Socket of this client.
264 */
265 struct GNUNET_NETWORK_Handle *sock;
266
267 /**
268 * Message queue for the client.
269 */
270 struct GNUNET_MQ_Handle *mq;
271
272 /**
273 * Tokenizer we use for processing incoming data.
274 */
275 struct GNUNET_MessageStreamTokenizer *mst;
276
277 /**
278 * Task that warns about missing calls to
279 * #GNUNET_SERVICE_client_continue().
280 */
281 struct GNUNET_SCHEDULER_Task *warn_task;
282
283 /**
284 * Task run to finish dropping the client after the stack has
285 * properly unwound.
286 */
287 struct GNUNET_SCHEDULER_Task *drop_task;
288
289 /**
290 * Task that receives data from the client to
291 * pass it to the handlers.
292 */
293 struct GNUNET_SCHEDULER_Task *recv_task;
294
295 /**
296 * Task that transmit data to the client.
297 */
298 struct GNUNET_SCHEDULER_Task *send_task;
299
300 /**
301 * Pointer to the message to be transmitted by @e send_task.
302 */
303 const struct GNUNET_MessageHeader *msg;
304
305 /**
306 * User context value, value returned from
307 * the connect callback.
308 */
309 void *user_context;
310
311 /**
312 * Time when we last gave a message from this client
313 * to the application.
314 */
315 struct GNUNET_TIME_Absolute warn_start;
316
317 /**
318 * Current position in @e msg at which we are transmitting.
319 */
320 size_t msg_pos;
321
322 /**
323 * Persist the file handle for this client no matter what happens,
324 * force the OS to close once the process actually dies. Should only
325 * be used in special cases!
326 */
327 bool persist;
328
329 /**
330 * Is this client a 'monitor' client that should not be counted
331 * when deciding on destroying the server during soft shutdown?
332 * (see also #GNUNET_SERVICE_start)
333 */
334 bool is_monitor;
335
336 /**
337 * Are we waiting for the application to call #GNUNET_SERVICE_client_continue()?
338 */
339 bool needs_continue;
340
341 /**
342 * Type of last message processed (for warn_no_receive_done).
343 */
344 uint16_t warn_type;
345};
346
347
348/**
349 * Check if any of the clients we have left are unrelated to
350 * monitoring.
351 *
352 * @param sh service to check clients for
353 * @return true if we have non-monitoring clients left
354 */
355static enum GNUNET_GenericReturnValue
356have_non_monitor_clients (struct GNUNET_SERVICE_Handle *sh)
357{
358 for (struct GNUNET_SERVICE_Client *client = sh->clients_head;
359 NULL != client;
360 client = client->next)
361 {
362 if (NULL != client->drop_task)
363 continue;
364 if (client->is_monitor)
365 continue;
366 return true;
367 }
368 return false;
369}
370
371
372/**
373 * Suspend accepting connections from the listen socket temporarily.
374 * Resume activity using #do_resume.
375 *
376 * @param sh service to stop accepting connections.
377 * @param sr reason for suspending accepting connections
378 */
379static void
380do_suspend (struct GNUNET_SERVICE_Handle *sh,
381 enum SuspendReason sr)
382{
383 GNUNET_assert (0 == (sh->suspend_state & sr));
384 sh->suspend_state |= sr;
385 for (struct ServiceListenContext *slc = sh->slc_head;
386 NULL != slc;
387 slc = slc->next)
388 {
389 if (NULL != slc->listen_task)
390 {
391 GNUNET_SCHEDULER_cancel (slc->listen_task);
392 slc->listen_task = NULL;
393 }
394 }
395}
396
397
398/**
399 * Shutdown task triggered when a service should be terminated.
400 * This considers active clients and the service options to see
401 * how this specific service is to be terminated, and depending
402 * on this proceeds with the shutdown logic.
403 *
404 * @param cls our `struct GNUNET_SERVICE_Handle`
405 */
406static void
407service_shutdown (void *cls)
408{
409 struct GNUNET_SERVICE_Handle *sh = cls;
410
411 switch (sh->options & GNUNET_SERVICE_OPTION_SHUTDOWN_BITMASK)
412 {
413 case GNUNET_SERVICE_OPTION_NONE:
414 GNUNET_SERVICE_shutdown (sh);
415 break;
416 case GNUNET_SERVICE_OPTION_MANUAL_SHUTDOWN:
417 /* This task should never be run if we are using
418 the manual shutdown. */
419 GNUNET_assert (0);
420 break;
421 case GNUNET_SERVICE_OPTION_SOFT_SHUTDOWN:
422 if (0 == (sh->suspend_state & SUSPEND_STATE_SHUTDOWN))
423 do_suspend (sh, SUSPEND_STATE_SHUTDOWN);
424 if (! have_non_monitor_clients (sh))
425 GNUNET_SERVICE_shutdown (sh);
426 break;
427 }
428}
429
430
431/**
432 * Check if the given IP address is in the list of IP addresses.
433 *
434 * @param list a list of networks
435 * @param add the IP to check (in network byte order)
436 * @return false if the IP is not in the list, true if it it is
437 */
438static bool
439check_ipv4_listed (const struct GNUNET_STRINGS_IPv4NetworkPolicy *list,
440 const struct in_addr *add)
441{
442 for (unsigned int i = 0;
443 0 != list[i].network.s_addr;
444 i++)
445 {
446 if ( (add->s_addr & list[i].netmask.s_addr) ==
447 (list[i].network.s_addr & list[i].netmask.s_addr) )
448 return true;
449 }
450 return false;
451}
452
453
454/**
455 * Check if the given IP address is in the list of IP addresses.
456 *
457 * @param list a list of networks
458 * @param ip the IP to check (in network byte order)
459 * @return false if the IP is not in the list, true if it it is
460 */
461static bool
462check_ipv6_listed (const struct GNUNET_STRINGS_IPv6NetworkPolicy *list,
463 const struct in6_addr *ip)
464{
465 for (unsigned int i = 0;
466 ! GNUNET_is_zero (&list[i].network);
467 i++)
468 {
469 bool match = true;
470
471 for (unsigned int j = 0; j < sizeof(struct in6_addr) / sizeof(int); j++)
472 if (((((int *) ip)[j] & ((int *) &list[i].netmask)[j])) !=
473 (((int *) &list[i].network)[j] & ((int *) &list[i].netmask)[j]))
474 {
475 match = false;
476 break;
477 }
478 if (match)
479 return true;
480 }
481 return false;
482}
483
484
485/**
486 * Task run when we are ready to transmit data to the
487 * client.
488 *
489 * @param cls the `struct GNUNET_SERVICE_Client *` to send to
490 */
491static void
492do_send (void *cls)
493{
494 struct GNUNET_SERVICE_Client *client = cls;
495 ssize_t ret;
496 size_t left;
497 const char *buf;
498
499 LOG (GNUNET_ERROR_TYPE_DEBUG,
500 "service: sending message with type %u\n",
501 ntohs (client->msg->type));
502 client->send_task = NULL;
503 buf = (const char *) client->msg;
504 left = ntohs (client->msg->size) - client->msg_pos;
505 ret = GNUNET_NETWORK_socket_send (client->sock,
506 &buf[client->msg_pos],
507 left);
508 GNUNET_assert (ret <= (ssize_t) left);
509 if (0 == ret)
510 {
511 LOG (GNUNET_ERROR_TYPE_DEBUG, "no data send");
512 GNUNET_MQ_inject_error (client->mq, GNUNET_MQ_ERROR_WRITE);
513 return;
514 }
515 if (-1 == ret)
516 {
517 if ((EAGAIN == errno) || (EINTR == errno))
518 {
519 /* ignore */
520 ret = 0;
521 }
522 else
523 {
524 if (EPIPE != errno)
525 GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING, "send");
526 LOG (GNUNET_ERROR_TYPE_DEBUG,
527 "socket send returned with error code %i",
528 errno);
529 GNUNET_MQ_inject_error (client->mq, GNUNET_MQ_ERROR_WRITE);
530 return;
531 }
532 }
533 if (0 == client->msg_pos)
534 {
535 GNUNET_MQ_impl_send_in_flight (client->mq);
536 }
537 client->msg_pos += ret;
538 if (left > (size_t) ret)
539 {
540 GNUNET_assert (NULL == client->drop_task);
541 client->send_task =
542 GNUNET_SCHEDULER_add_write_net (GNUNET_TIME_UNIT_FOREVER_REL,
543 client->sock,
544 &do_send,
545 client);
546 return;
547 }
548 GNUNET_MQ_impl_send_continue (client->mq);
549}
550
551
552/**
553 * Signature of functions implementing the sending functionality of a
554 * message queue.
555 *
556 * @param mq the message queue
557 * @param msg the message to send
558 * @param impl_state our `struct GNUNET_SERVICE_Client *`
559 */
560static void
561service_mq_send (struct GNUNET_MQ_Handle *mq,
562 const struct GNUNET_MessageHeader *msg,
563 void *impl_state)
564{
565 struct GNUNET_SERVICE_Client *client = impl_state;
566
567 (void) mq;
568 if (NULL != client->drop_task)
569 return; /* we're going down right now, do not try to send */
570 GNUNET_assert (NULL == client->send_task);
571 LOG (GNUNET_ERROR_TYPE_DEBUG,
572 "Sending message of type %u and size %u to client\n",
573 ntohs (msg->type),
574 ntohs (msg->size));
575 client->msg = msg;
576 client->msg_pos = 0;
577 client->send_task = GNUNET_SCHEDULER_add_now (&do_send,
578 client);
579}
580
581
582/**
583 * Implementation function that cancels the currently sent message.
584 *
585 * @param mq message queue
586 * @param impl_state state specific to the implementation
587 */
588static void
589service_mq_cancel (struct GNUNET_MQ_Handle *mq,
590 void *impl_state)
591{
592 struct GNUNET_SERVICE_Client *client = impl_state;
593
594 (void) mq;
595 GNUNET_assert (0 == client->msg_pos);
596 client->msg = NULL;
597 GNUNET_SCHEDULER_cancel (client->send_task);
598 client->send_task = NULL;
599}
600
601
602/**
603 * Generic error handler, called with the appropriate
604 * error code and the same closure specified at the creation of
605 * the message queue.
606 * Not every message queue implementation supports an error handler.
607 *
608 * @param cls closure with our `struct GNUNET_SERVICE_Client`
609 * @param error error code
610 */
611static void
612service_mq_error_handler (void *cls,
613 enum GNUNET_MQ_Error error)
614{
615 struct GNUNET_SERVICE_Client *client = cls;
616 struct GNUNET_SERVICE_Handle *sh = client->sh;
617
618 if ( (GNUNET_MQ_ERROR_NO_MATCH == error) &&
619 (! sh->require_found) )
620 {
621 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
622 "No handler for message of type %u found\n",
623 (unsigned int) client->warn_type);
624 GNUNET_SERVICE_client_continue (client);
625 return; /* ignore error */
626 }
627 GNUNET_SERVICE_client_drop (client);
628}
629
630
631/**
632 * Task run to warn about missing calls to #GNUNET_SERVICE_client_continue().
633 *
634 * @param cls our `struct GNUNET_SERVICE_Client *` to process more requests from
635 */
636static void
637warn_no_client_continue (void *cls)
638{
639 struct GNUNET_SERVICE_Client *client = cls;
640
641 GNUNET_break (
642 0 !=
643 client->warn_type); /* type should never be 0 here, as we don't use 0 */
644 client->warn_task = GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_UNIT_MINUTES,
645 &warn_no_client_continue,
646 client);
647 LOG (
648 GNUNET_ERROR_TYPE_WARNING,
649 _ (
650 "Processing code for message of type %u did not call `GNUNET_SERVICE_client_continue' after %s\n"),
651 (unsigned int) client->warn_type,
652 GNUNET_STRINGS_relative_time_to_string (GNUNET_TIME_absolute_get_duration (
653 client->warn_start),
654 GNUNET_YES));
655}
656
657
658/**
659 * Functions with this signature are called whenever a
660 * complete message is received by the tokenizer for a client.
661 *
662 * Do not call #GNUNET_MST_destroy() from within
663 * the scope of this callback.
664 *
665 * @param cls closure with the `struct GNUNET_SERVICE_Client *`
666 * @param message the actual message
667 * @return #GNUNET_OK on success, #GNUNET_SYSERR if the client was dropped
668 */
669static int
670service_client_mst_cb (void *cls,
671 const struct GNUNET_MessageHeader *message)
672{
673 struct GNUNET_SERVICE_Client *client = cls;
674
675 LOG (GNUNET_ERROR_TYPE_DEBUG,
676 "Received message of type %u and size %u from client\n",
677 ntohs (message->type),
678 ntohs (message->size));
679 GNUNET_assert (! client->needs_continue);
680 client->needs_continue = true;
681 client->warn_type = ntohs (message->type);
682 client->warn_start = GNUNET_TIME_absolute_get ();
683 GNUNET_assert (NULL == client->warn_task);
684 client->warn_task = GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_UNIT_MINUTES,
685 &warn_no_client_continue,
686 client);
687 GNUNET_MQ_inject_message (client->mq, message);
688 if (NULL != client->drop_task)
689 return GNUNET_SYSERR;
690 return GNUNET_OK;
691}
692
693
694/**
695 * A client sent us data. Receive and process it. If we are done,
696 * reschedule this task.
697 *
698 * @param cls the `struct GNUNET_SERVICE_Client` that sent us data.
699 */
700static void
701service_client_recv (void *cls)
702{
703 struct GNUNET_SERVICE_Client *client = cls;
704 enum GNUNET_GenericReturnValue ret;
705
706 client->recv_task = NULL;
707 ret = GNUNET_MST_read (client->mst,
708 client->sock,
709 GNUNET_NO,
710 GNUNET_YES);
711 if (GNUNET_SYSERR == ret)
712 {
713 /* client closed connection (or IO error) */
714 if (NULL == client->drop_task)
715 {
716 GNUNET_assert (! client->needs_continue);
717 GNUNET_SERVICE_client_drop (client);
718 }
719 return;
720 }
721 if (GNUNET_NO == ret)
722 return; /* more messages in buffer, wait for application
723 to be done processing */
724 GNUNET_assert (GNUNET_OK == ret);
725 if (client->needs_continue)
726 return;
727 if (NULL != client->recv_task)
728 return;
729 /* MST needs more data, re-schedule read job */
730 client->recv_task =
731 GNUNET_SCHEDULER_add_read_net (GNUNET_TIME_UNIT_FOREVER_REL,
732 client->sock,
733 &service_client_recv,
734 client);
735}
736
737
738/**
739 * We have successfully accepted a connection from a client. Now
740 * setup the client (with the scheduler) and tell the application.
741 *
742 * @param sh service that accepted the client
743 * @param csock socket associated with the client
744 */
745static void
746start_client (struct GNUNET_SERVICE_Handle *sh,
747 struct GNUNET_NETWORK_Handle *csock)
748{
749 struct GNUNET_SERVICE_Client *client;
750
751 client = GNUNET_new (struct GNUNET_SERVICE_Client);
752 GNUNET_CONTAINER_DLL_insert (sh->clients_head,
753 sh->clients_tail,
754 client);
755 client->sh = sh;
756 client->sock = csock;
757 client->mq = GNUNET_MQ_queue_for_callbacks (&service_mq_send,
758 NULL,
759 &service_mq_cancel,
760 client,
761 sh->handlers,
762 &service_mq_error_handler,
763 client);
764 client->mst = GNUNET_MST_create (&service_client_mst_cb, client);
765 if (NULL != sh->connect_cb)
766 client->user_context = sh->connect_cb (sh->cb_cls, client, client->mq);
767 GNUNET_MQ_set_handlers_closure (client->mq, client->user_context);
768 client->recv_task =
769 GNUNET_SCHEDULER_add_read_net (GNUNET_TIME_UNIT_FOREVER_REL,
770 client->sock,
771 &service_client_recv,
772 client);
773}
774
775
776/**
777 * We have a client. Accept the incoming socket(s) (and reschedule
778 * the listen task).
779 *
780 * @param cls the `struct ServiceListenContext` of the ready listen socket
781 */
782static void
783accept_client (void *cls)
784{
785 struct ServiceListenContext *slc = cls;
786 struct GNUNET_SERVICE_Handle *sh = slc->sh;
787
788 slc->listen_task = NULL;
789 while (1)
790 {
791 struct GNUNET_NETWORK_Handle *sock;
792 const struct sockaddr_in *v4;
793 const struct sockaddr_in6 *v6;
794 struct sockaddr_storage sa;
795 socklen_t addrlen;
796 int ok;
797
798 addrlen = sizeof(sa);
799 sock = GNUNET_NETWORK_socket_accept (slc->listen_socket,
800 (struct sockaddr *) &sa,
801 &addrlen);
802 if (NULL == sock)
803 {
804 if (EMFILE == errno)
805 do_suspend (sh, SUSPEND_STATE_EMFILE);
806 else if (EAGAIN != errno)
807 GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING,
808 "accept");
809 break;
810 }
811 switch (sa.ss_family)
812 {
813 case AF_INET:
814 GNUNET_assert (addrlen == sizeof(struct sockaddr_in));
815 v4 = (const struct sockaddr_in *) &sa;
816 ok = (((NULL == sh->v4_allowed) ||
817 (check_ipv4_listed (sh->v4_allowed,
818 &v4->sin_addr))) &&
819 ((NULL == sh->v4_denied) ||
820 (! check_ipv4_listed (sh->v4_denied,
821 &v4->sin_addr))));
822 break;
823
824 case AF_INET6:
825 GNUNET_assert (addrlen == sizeof(struct sockaddr_in6));
826 v6 = (const struct sockaddr_in6 *) &sa;
827 ok = (((NULL == sh->v6_allowed) ||
828 (check_ipv6_listed (sh->v6_allowed,
829 &v6->sin6_addr))) &&
830 ((NULL == sh->v6_denied) ||
831 (! check_ipv6_listed (sh->v6_denied,
832 &v6->sin6_addr))));
833 break;
834
835 case AF_UNIX:
836 ok = GNUNET_OK; /* controlled using file-system ACL now */
837 break;
838
839 default:
840 LOG (GNUNET_ERROR_TYPE_WARNING,
841 _ ("Unknown address family %d\n"),
842 sa.ss_family);
843 return;
844 }
845 if (! ok)
846 {
847 LOG (GNUNET_ERROR_TYPE_DEBUG,
848 "Service rejected incoming connection from %s due to policy.\n",
849 GNUNET_a2s ((const struct sockaddr *) &sa, addrlen));
850 GNUNET_break (GNUNET_OK == GNUNET_NETWORK_socket_close (sock));
851 continue;
852 }
853 LOG (GNUNET_ERROR_TYPE_DEBUG,
854 "Service accepted incoming connection from %s.\n",
855 GNUNET_a2s ((const struct sockaddr *) &sa, addrlen));
856 start_client (slc->sh,
857 sock);
858 }
859 if (0 != sh->suspend_state)
860 return;
861 slc->listen_task =
862 GNUNET_SCHEDULER_add_read_net (GNUNET_TIME_UNIT_FOREVER_REL,
863 slc->listen_socket,
864 &accept_client,
865 slc);
866}
867
868
869/**
870 * Resume accepting connections from the listen socket.
871 *
872 * @param sh service to resume accepting connections.
873 * @param sr reason that is no longer causing the suspension,
874 * or #SUSPEND_STATE_NONE on first startup
875 */
876static void
877do_resume (struct GNUNET_SERVICE_Handle *sh,
878 enum SuspendReason sr)
879{
880 GNUNET_assert ((SUSPEND_STATE_NONE == sr) || (0 != (sh->suspend_state & sr)));
881 sh->suspend_state -= sr;
882 if (SUSPEND_STATE_NONE != sh->suspend_state)
883 return;
884 for (struct ServiceListenContext *slc = sh->slc_head;
885 NULL != slc;
886 slc = slc->next)
887 {
888 GNUNET_assert (NULL == slc->listen_task);
889 slc->listen_task =
890 GNUNET_SCHEDULER_add_read_net (GNUNET_TIME_UNIT_FOREVER_REL,
891 slc->listen_socket,
892 &accept_client,
893 slc);
894 }
895}
896
897
898/**
899 * First task run by any service. Initializes our shutdown task,
900 * starts the listening operation on our listen sockets and launches
901 * the custom logic of the application service.
902 *
903 * @param cls our `struct GNUNET_SERVICE_Handle`
904 */
905static void
906service_main (void *cls)
907{
908 struct GNUNET_SERVICE_Handle *sh = cls;
909
910 if (GNUNET_SERVICE_OPTION_MANUAL_SHUTDOWN !=
911 (sh->options & GNUNET_SERVICE_OPTION_SHUTDOWN_BITMASK))
912 GNUNET_SCHEDULER_add_shutdown (&service_shutdown, sh);
913 do_resume (sh, SUSPEND_STATE_NONE);
914
915 if (-1 != sh->ready_confirm_fd)
916 {
917 GNUNET_break (1 == write (sh->ready_confirm_fd, ".", 1));
918 GNUNET_break (0 == close (sh->ready_confirm_fd));
919 sh->ready_confirm_fd = -1;
920 }
921
922 if (NULL != sh->service_init_cb)
923 sh->service_init_cb (sh->cb_cls, sh->cfg, sh);
924}
925
926
927/**
928 * Parse an IPv4 access control list.
929 *
930 * @param ret location where to write the ACL (set)
931 * @param sh service context to use to get the configuration
932 * @param option name of the ACL option to parse
933 * @return #GNUNET_SYSERR on parse error, #GNUNET_OK on success (including
934 * no ACL configured)
935 */
936static enum GNUNET_GenericReturnValue
937process_acl4 (struct GNUNET_STRINGS_IPv4NetworkPolicy **ret,
938 struct GNUNET_SERVICE_Handle *sh,
939 const char *option)
940{
941 char *opt;
942
943 if (! GNUNET_CONFIGURATION_have_value (sh->cfg, sh->service_name, option))
944 {
945 *ret = NULL;
946 return GNUNET_OK;
947 }
948 GNUNET_break (GNUNET_OK ==
949 GNUNET_CONFIGURATION_get_value_string (sh->cfg,
950 sh->service_name,
951 option,
952 &opt));
953 if (NULL == (*ret = GNUNET_STRINGS_parse_ipv4_policy (opt)))
954 {
955 LOG (GNUNET_ERROR_TYPE_WARNING,
956 _ ("Could not parse IPv4 network specification `%s' for `%s:%s'\n"),
957 opt,
958 sh->service_name,
959 option);
960 GNUNET_free (opt);
961 return GNUNET_SYSERR;
962 }
963 GNUNET_free (opt);
964 return GNUNET_OK;
965}
966
967
968/**
969 * Parse an IPv6 access control list.
970 *
971 * @param ret location where to write the ACL (set)
972 * @param sh service context to use to get the configuration
973 * @param option name of the ACL option to parse
974 * @return #GNUNET_SYSERR on parse error, #GNUNET_OK on success (including
975 * no ACL configured)
976 */
977static enum GNUNET_GenericReturnValue
978process_acl6 (struct GNUNET_STRINGS_IPv6NetworkPolicy **ret,
979 struct GNUNET_SERVICE_Handle *sh,
980 const char *option)
981{
982 char *opt;
983
984 if (! GNUNET_CONFIGURATION_have_value (sh->cfg, sh->service_name, option))
985 {
986 *ret = NULL;
987 return GNUNET_OK;
988 }
989 GNUNET_break (GNUNET_OK ==
990 GNUNET_CONFIGURATION_get_value_string (sh->cfg,
991 sh->service_name,
992 option,
993 &opt));
994 if (NULL == (*ret = GNUNET_STRINGS_parse_ipv6_policy (opt)))
995 {
996 LOG (GNUNET_ERROR_TYPE_WARNING,
997 _ ("Could not parse IPv6 network specification `%s' for `%s:%s'\n"),
998 opt,
999 sh->service_name,
1000 option);
1001 GNUNET_free (opt);
1002 return GNUNET_SYSERR;
1003 }
1004 GNUNET_free (opt);
1005 return GNUNET_OK;
1006}
1007
1008
1009/**
1010 * Add the given UNIX domain path as an address to the
1011 * list (as the first entry).
1012 *
1013 * @param saddrs array to update
1014 * @param saddrlens where to store the address length
1015 * @param unixpath path to add
1016 */
1017static void
1018add_unixpath (struct sockaddr **saddrs,
1019 socklen_t *saddrlens,
1020 const char *unixpath)
1021{
1022#ifdef AF_UNIX
1023 struct sockaddr_un *un;
1024
1025 un = GNUNET_new (struct sockaddr_un);
1026 un->sun_family = AF_UNIX;
1027 GNUNET_strlcpy (un->sun_path, unixpath, sizeof(un->sun_path));
1028#if HAVE_SOCKADDR_UN_SUN_LEN
1029 un->sun_len = (u_char) sizeof(struct sockaddr_un);
1030#endif
1031 *saddrs = (struct sockaddr *) un;
1032 *saddrlens = sizeof(struct sockaddr_un);
1033#else
1034 /* this function should never be called
1035 * unless AF_UNIX is defined! */
1036 GNUNET_assert (0);
1037#endif
1038}
1039
1040
1041/**
1042 * Get the list of addresses that a server for the given service
1043 * should bind to.
1044 *
1045 * @param service_name name of the service
1046 * @param cfg configuration (which specifies the addresses)
1047 * @param addrs set (call by reference) to an array of pointers to the
1048 * addresses the server should bind to and listen on; the
1049 * array will be NULL-terminated (on success)
1050 * @param addr_lens set (call by reference) to an array of the lengths
1051 * of the respective `struct sockaddr` struct in the @a addrs
1052 * array (on success)
1053 * @return number of addresses found on success,
1054 * #GNUNET_SYSERR if the configuration
1055 * did not specify reasonable finding information or
1056 * if it specified a hostname that could not be resolved;
1057 * #GNUNET_NO if the number of addresses configured is
1058 * zero (in this case, `*addrs` and `*addr_lens` will be
1059 * set to NULL).
1060 */
1061static int
1062get_server_addresses (const char *service_name,
1063 const struct GNUNET_CONFIGURATION_Handle *cfg,
1064 struct sockaddr ***addrs,
1065 socklen_t **addr_lens)
1066{
1067 int disablev6;
1068 struct GNUNET_NETWORK_Handle *desc;
1069 unsigned long long port;
1070 char *unixpath;
1071 struct addrinfo hints;
1072 struct addrinfo *res;
1073 struct addrinfo *pos;
1074 struct addrinfo *next;
1075 unsigned int i;
1076 int resi;
1077 int ret;
1078 struct sockaddr **saddrs;
1079 socklen_t *saddrlens;
1080 char *hostname;
1081
1082 *addrs = NULL;
1083 *addr_lens = NULL;
1084 desc = NULL;
1085 disablev6 = GNUNET_NO;
1086 if ((GNUNET_NO == GNUNET_NETWORK_test_pf (PF_INET6)) ||
1087 (GNUNET_YES ==
1088 GNUNET_CONFIGURATION_get_value_yesno (cfg, service_name, "DISABLEV6")))
1089 disablev6 = GNUNET_YES;
1090
1091 port = 0;
1092 if (GNUNET_CONFIGURATION_have_value (cfg, service_name, "PORT"))
1093 {
1094 if (GNUNET_OK != GNUNET_CONFIGURATION_get_value_number (cfg,
1095 service_name,
1096 "PORT",
1097 &port))
1098 {
1099 LOG (GNUNET_ERROR_TYPE_ERROR,
1100 _ ("Require valid port number for service `%s' in configuration!\n"),
1101 service_name);
1102 }
1103 if (port > 65535)
1104 {
1105 LOG (GNUNET_ERROR_TYPE_ERROR,
1106 _ ("Require valid port number for service `%s' in configuration!\n"),
1107 service_name);
1108 return GNUNET_SYSERR;
1109 }
1110 }
1111
1112 if (GNUNET_CONFIGURATION_have_value (cfg, service_name, "BINDTO"))
1113 {
1114 GNUNET_break (GNUNET_OK ==
1115 GNUNET_CONFIGURATION_get_value_string (cfg,
1116 service_name,
1117 "BINDTO",
1118 &hostname));
1119 }
1120 else
1121 hostname = NULL;
1122
1123 unixpath = NULL;
1124#ifdef AF_UNIX
1125 if ((GNUNET_YES ==
1126 GNUNET_CONFIGURATION_have_value (cfg, service_name, "UNIXPATH")) &&
1127 (GNUNET_OK == GNUNET_CONFIGURATION_get_value_filename (cfg,
1128 service_name,
1129 "UNIXPATH",
1130 &unixpath)) &&
1131 (0 < strlen (unixpath)))
1132 {
1133 /* probe UNIX support */
1134 struct sockaddr_un s_un;
1135
1136 if (strlen (unixpath) >= sizeof(s_un.sun_path))
1137 {
1138 LOG (GNUNET_ERROR_TYPE_WARNING,
1139 _ ("UNIXPATH `%s' too long, maximum length is %llu\n"),
1140 unixpath,
1141 (unsigned long long) sizeof(s_un.sun_path));
1142 unixpath = GNUNET_NETWORK_shorten_unixpath (unixpath);
1143 LOG (GNUNET_ERROR_TYPE_INFO, _ ("Using `%s' instead\n"), unixpath);
1144 }
1145 if (GNUNET_OK != GNUNET_DISK_directory_create_for_file (unixpath))
1146 GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_ERROR, "mkdir", unixpath);
1147 }
1148 if (NULL != unixpath)
1149 {
1150 desc = GNUNET_NETWORK_socket_create (AF_UNIX, SOCK_STREAM, 0);
1151 if (NULL == desc)
1152 {
1153 if ((ENOBUFS == errno) || (ENOMEM == errno) || (ENFILE == errno) ||
1154 (EACCES == errno))
1155 {
1156 LOG_STRERROR (GNUNET_ERROR_TYPE_ERROR, "socket");
1157 GNUNET_free (hostname);
1158 GNUNET_free (unixpath);
1159 return GNUNET_SYSERR;
1160 }
1161 LOG (GNUNET_ERROR_TYPE_INFO,
1162 _ (
1163 "Disabling UNIX domain socket support for service `%s', failed to create UNIX domain socket: %s\n"),
1164 service_name,
1165 strerror (errno));
1166 GNUNET_free (unixpath);
1167 unixpath = NULL;
1168 }
1169 else
1170 {
1171 GNUNET_break (GNUNET_OK == GNUNET_NETWORK_socket_close (desc));
1172 desc = NULL;
1173 }
1174 }
1175#endif
1176
1177 if ((0 == port) && (NULL == unixpath))
1178 {
1179 LOG (GNUNET_ERROR_TYPE_ERROR,
1180 _ (
1181 "Have neither PORT nor UNIXPATH for service `%s', but one is required\n"),
1182 service_name);
1183 GNUNET_free (hostname);
1184 return GNUNET_SYSERR;
1185 }
1186 if (0 == port)
1187 {
1188 saddrs = GNUNET_new_array (2, struct sockaddr *);
1189 saddrlens = GNUNET_new_array (2, socklen_t);
1190 add_unixpath (saddrs, saddrlens, unixpath);
1191 GNUNET_free (unixpath);
1192 GNUNET_free (hostname);
1193 *addrs = saddrs;
1194 *addr_lens = saddrlens;
1195 return 1;
1196 }
1197
1198 if (NULL != hostname)
1199 {
1200 LOG (GNUNET_ERROR_TYPE_DEBUG,
1201 "Resolving `%s' since that is where `%s' will bind to.\n",
1202 hostname,
1203 service_name);
1204 memset (&hints, 0, sizeof(struct addrinfo));
1205 if (disablev6)
1206 hints.ai_family = AF_INET;
1207 hints.ai_protocol = IPPROTO_TCP;
1208 if ((0 != (ret = getaddrinfo (hostname, NULL, &hints, &res))) ||
1209 (NULL == res))
1210 {
1211 LOG (GNUNET_ERROR_TYPE_ERROR,
1212 _ ("Failed to resolve `%s': %s\n"),
1213 hostname,
1214 gai_strerror (ret));
1215 GNUNET_free (hostname);
1216 GNUNET_free (unixpath);
1217 return GNUNET_SYSERR;
1218 }
1219 next = res;
1220 i = 0;
1221 while (NULL != (pos = next))
1222 {
1223 next = pos->ai_next;
1224 if ((disablev6) && (pos->ai_family == AF_INET6))
1225 continue;
1226 i++;
1227 }
1228 if (0 == i)
1229 {
1230 LOG (GNUNET_ERROR_TYPE_ERROR,
1231 _ ("Failed to find %saddress for `%s'.\n"),
1232 disablev6 ? "IPv4 " : "",
1233 hostname);
1234 freeaddrinfo (res);
1235 GNUNET_free (hostname);
1236 GNUNET_free (unixpath);
1237 return GNUNET_SYSERR;
1238 }
1239 resi = i;
1240 if (NULL != unixpath)
1241 resi++;
1242 saddrs = GNUNET_new_array (resi + 1, struct sockaddr *);
1243 saddrlens = GNUNET_new_array (resi + 1, socklen_t);
1244 i = 0;
1245 if (NULL != unixpath)
1246 {
1247 add_unixpath (saddrs, saddrlens, unixpath);
1248 i++;
1249 }
1250 next = res;
1251 while (NULL != (pos = next))
1252 {
1253 next = pos->ai_next;
1254 if ((disablev6) && (AF_INET6 == pos->ai_family))
1255 continue;
1256 if ((IPPROTO_TCP != pos->ai_protocol) && (0 != pos->ai_protocol))
1257 continue; /* not TCP */
1258 if ((SOCK_STREAM != pos->ai_socktype) && (0 != pos->ai_socktype))
1259 continue; /* huh? */
1260 LOG (GNUNET_ERROR_TYPE_DEBUG,
1261 "Service `%s' will bind to `%s'\n",
1262 service_name,
1263 GNUNET_a2s (pos->ai_addr, pos->ai_addrlen));
1264 if (AF_INET == pos->ai_family)
1265 {
1266 GNUNET_assert (sizeof(struct sockaddr_in) == pos->ai_addrlen);
1267 saddrlens[i] = pos->ai_addrlen;
1268 saddrs[i] = GNUNET_malloc (saddrlens[i]);
1269 GNUNET_memcpy (saddrs[i], pos->ai_addr, saddrlens[i]);
1270 ((struct sockaddr_in *) saddrs[i])->sin_port = htons (port);
1271 }
1272 else
1273 {
1274 GNUNET_assert (AF_INET6 == pos->ai_family);
1275 GNUNET_assert (sizeof(struct sockaddr_in6) == pos->ai_addrlen);
1276 saddrlens[i] = pos->ai_addrlen;
1277 saddrs[i] = GNUNET_malloc (saddrlens[i]);
1278 GNUNET_memcpy (saddrs[i], pos->ai_addr, saddrlens[i]);
1279 ((struct sockaddr_in6 *) saddrs[i])->sin6_port = htons (port);
1280 }
1281 i++;
1282 }
1283 GNUNET_free (hostname);
1284 freeaddrinfo (res);
1285 resi = i;
1286 }
1287 else
1288 {
1289 /* will bind against everything, just set port */
1290 if (disablev6)
1291 {
1292 /* V4-only */
1293 resi = 1;
1294 if (NULL != unixpath)
1295 resi++;
1296 i = 0;
1297 saddrs = GNUNET_new_array (resi + 1, struct sockaddr *);
1298 saddrlens = GNUNET_new_array (resi + 1, socklen_t);
1299 if (NULL != unixpath)
1300 {
1301 add_unixpath (saddrs, saddrlens, unixpath);
1302 i++;
1303 }
1304 saddrlens[i] = sizeof(struct sockaddr_in);
1305 saddrs[i] = GNUNET_malloc (saddrlens[i]);
1306#if HAVE_SOCKADDR_IN_SIN_LEN
1307 ((struct sockaddr_in *) saddrs[i])->sin_len = saddrlens[i];
1308#endif
1309 ((struct sockaddr_in *) saddrs[i])->sin_family = AF_INET;
1310 ((struct sockaddr_in *) saddrs[i])->sin_port = htons (port);
1311 }
1312 else
1313 {
1314 /* dual stack */
1315 resi = 2;
1316 if (NULL != unixpath)
1317 resi++;
1318 saddrs = GNUNET_new_array (resi + 1, struct sockaddr *);
1319 saddrlens = GNUNET_new_array (resi + 1, socklen_t);
1320 i = 0;
1321 if (NULL != unixpath)
1322 {
1323 add_unixpath (saddrs, saddrlens, unixpath);
1324 i++;
1325 }
1326 saddrlens[i] = sizeof(struct sockaddr_in6);
1327 saddrs[i] = GNUNET_malloc (saddrlens[i]);
1328#if HAVE_SOCKADDR_IN_SIN_LEN
1329 ((struct sockaddr_in6 *) saddrs[i])->sin6_len = saddrlens[0];
1330#endif
1331 ((struct sockaddr_in6 *) saddrs[i])->sin6_family = AF_INET6;
1332 ((struct sockaddr_in6 *) saddrs[i])->sin6_port = htons (port);
1333 i++;
1334 saddrlens[i] = sizeof(struct sockaddr_in);
1335 saddrs[i] = GNUNET_malloc (saddrlens[i]);
1336#if HAVE_SOCKADDR_IN_SIN_LEN
1337 ((struct sockaddr_in *) saddrs[i])->sin_len = saddrlens[1];
1338#endif
1339 ((struct sockaddr_in *) saddrs[i])->sin_family = AF_INET;
1340 ((struct sockaddr_in *) saddrs[i])->sin_port = htons (port);
1341 }
1342 }
1343 GNUNET_free (unixpath);
1344 *addrs = saddrs;
1345 *addr_lens = saddrlens;
1346 return resi;
1347}
1348
1349
1350/**
1351 * Create and initialize a listen socket for the server.
1352 *
1353 * @param server_addr address to listen on
1354 * @param socklen length of @a server_addr
1355 * @return NULL on error, otherwise the listen socket
1356 */
1357static struct GNUNET_NETWORK_Handle *
1358open_listen_socket (const struct sockaddr *server_addr,
1359 socklen_t socklen)
1360{
1361 struct GNUNET_NETWORK_Handle *sock;
1362 uint16_t port;
1363 int eno;
1364
1365 switch (server_addr->sa_family)
1366 {
1367 case AF_INET:
1368 port = ntohs (((const struct sockaddr_in *) server_addr)->sin_port);
1369 break;
1370 case AF_INET6:
1371 port = ntohs (((const struct sockaddr_in6 *) server_addr)->sin6_port);
1372 break;
1373 case AF_UNIX:
1374 port = 0;
1375 break;
1376 default:
1377 GNUNET_break (0);
1378 port = 0;
1379 break;
1380 }
1381 sock = GNUNET_NETWORK_socket_create (server_addr->sa_family,
1382 SOCK_STREAM,
1383 0);
1384 if (NULL == sock)
1385 {
1386 LOG_STRERROR (GNUNET_ERROR_TYPE_ERROR,
1387 "socket");
1388 errno = 0;
1389 return NULL;
1390 }
1391 /* bind the socket */
1392 if (GNUNET_OK !=
1393 GNUNET_NETWORK_socket_bind (sock,
1394 server_addr,
1395 socklen))
1396 {
1397 eno = errno;
1398 if (EADDRINUSE != errno)
1399 {
1400 /* we don't log 'EADDRINUSE' here since an IPv4 bind may
1401 * fail if we already took the port on IPv6; if both IPv4 and
1402 * IPv6 binds fail, then our caller will log using the
1403 * errno preserved in 'eno' */
1404 if (0 != port)
1405 LOG (GNUNET_ERROR_TYPE_ERROR,
1406 _ ("`%s' failed for port %d (%s).\n"),
1407 "bind",
1408 port,
1409 (AF_INET == server_addr->sa_family) ? "IPv4" : "IPv6");
1410 else
1411 LOG_STRERROR (GNUNET_ERROR_TYPE_ERROR, "bind");
1412 eno = 0;
1413 }
1414 else
1415 {
1416 if (0 != port)
1417 LOG (GNUNET_ERROR_TYPE_WARNING,
1418 _ ("`%s' failed for port %d (%s): address already in use\n"),
1419 "bind",
1420 port,
1421 (AF_INET == server_addr->sa_family) ? "IPv4" : "IPv6");
1422 else if (AF_UNIX == server_addr->sa_family)
1423 {
1424 LOG (GNUNET_ERROR_TYPE_WARNING,
1425 _ ("`%s' failed for `%s': address already in use\n"),
1426 "bind",
1427 GNUNET_a2s (server_addr, socklen));
1428 }
1429 }
1430 GNUNET_break (GNUNET_OK == GNUNET_NETWORK_socket_close (sock));
1431 errno = eno;
1432 return NULL;
1433 }
1434 if (GNUNET_OK != GNUNET_NETWORK_socket_listen (sock, 5))
1435 {
1436 LOG_STRERROR (GNUNET_ERROR_TYPE_ERROR, "listen");
1437 GNUNET_break (GNUNET_OK == GNUNET_NETWORK_socket_close (sock));
1438 errno = 0;
1439 return NULL;
1440 }
1441 if (0 != port)
1442 LOG (GNUNET_ERROR_TYPE_DEBUG,
1443 "Server starts to listen on port %u.\n",
1444 port);
1445 return sock;
1446}
1447
1448
1449/**
1450 * Setup service handle
1451 *
1452 * Configuration may specify:
1453 * - PORT (where to bind to for TCP)
1454 * - UNIXPATH (where to bind to for UNIX domain sockets)
1455 * - DISABLEV6 (disable support for IPv6, otherwise we use dual-stack)
1456 * - BINDTO (hostname or IP address to bind to, otherwise we take everything)
1457 * - ACCEPT_FROM (only allow connections from specified IPv4 subnets)
1458 * - ACCEPT_FROM6 (only allow connections from specified IPv6 subnets)
1459 * - REJECT_FROM (disallow allow connections from specified IPv4 subnets)
1460 * - REJECT_FROM6 (disallow allow connections from specified IPv6 subnets)
1461 *
1462 * @param sh service context to initialize
1463 * @return #GNUNET_OK if configuration succeeded
1464 */
1465static enum GNUNET_GenericReturnValue
1466setup_service (struct GNUNET_SERVICE_Handle *sh)
1467{
1468 int tolerant;
1469 struct GNUNET_NETWORK_Handle **csocks = NULL;
1470 struct GNUNET_NETWORK_Handle **lsocks;
1471 const char *nfds;
1472 unsigned int cnt;
1473 int flags;
1474 char dummy[2];
1475
1476 if (GNUNET_CONFIGURATION_have_value (sh->cfg,
1477 sh->service_name,
1478 "TOLERANT"))
1479 {
1480 if (GNUNET_SYSERR ==
1481 (tolerant = GNUNET_CONFIGURATION_get_value_yesno (sh->cfg,
1482 sh->service_name,
1483 "TOLERANT")))
1484 {
1485 LOG (GNUNET_ERROR_TYPE_ERROR,
1486 _ ("Specified value for `%s' of service `%s' is invalid\n"),
1487 "TOLERANT",
1488 sh->service_name);
1489 return GNUNET_SYSERR;
1490 }
1491 }
1492 else
1493 tolerant = GNUNET_NO;
1494
1495 lsocks = NULL;
1496 errno = 0;
1497 if ( (NULL != (nfds = getenv ("LISTEN_FDS"))) &&
1498 (1 == sscanf (nfds, "%u%1s", &cnt, dummy)) && (cnt > 0) &&
1499 (cnt < FD_SETSIZE) && (cnt + 4 < FD_SETSIZE))
1500 {
1501 lsocks = GNUNET_new_array (cnt + 1, struct GNUNET_NETWORK_Handle *);
1502 while (0 < cnt--)
1503 {
1504 flags = fcntl (3 + cnt, F_GETFD);
1505 if ((flags < 0) || (0 != (flags & FD_CLOEXEC)) ||
1506 (NULL == (lsocks[cnt] = GNUNET_NETWORK_socket_box_native (3 + cnt))))
1507 {
1508 LOG (GNUNET_ERROR_TYPE_ERROR,
1509 _ (
1510 "Could not access pre-bound socket %u, will try to bind myself\n"),
1511 (unsigned int) 3 + cnt);
1512 cnt++;
1513 while (NULL != lsocks[cnt])
1514 GNUNET_break (GNUNET_OK ==
1515 GNUNET_NETWORK_socket_close (lsocks[cnt++]));
1516 GNUNET_free (lsocks);
1517 lsocks = NULL;
1518 break;
1519 }
1520 }
1521 unsetenv ("LISTEN_FDS");
1522 }
1523 if ( (0 != (GNUNET_SERVICE_OPTION_CLOSE_LSOCKS & sh->options)) &&
1524 (NULL != lsocks) )
1525 {
1526 csocks = lsocks;
1527 lsocks = NULL;
1528 }
1529
1530 if (NULL != lsocks)
1531 {
1532 /* listen only on inherited sockets if we have any */
1533 for (struct GNUNET_NETWORK_Handle **ls = lsocks; NULL != *ls; ls++)
1534 {
1535 struct ServiceListenContext *slc;
1536
1537 slc = GNUNET_new (struct ServiceListenContext);
1538 slc->sh = sh;
1539 slc->listen_socket = *ls;
1540 GNUNET_CONTAINER_DLL_insert (sh->slc_head,
1541 sh->slc_tail,
1542 slc);
1543 }
1544 GNUNET_free (lsocks);
1545 }
1546 else
1547 {
1548 struct sockaddr **addrs;
1549 socklen_t *addrlens;
1550 int num;
1551
1552 num = get_server_addresses (sh->service_name, sh->cfg, &addrs, &addrlens);
1553 if (GNUNET_SYSERR == num)
1554 return GNUNET_SYSERR;
1555
1556 for (int i = 0; i < num; i++)
1557 {
1558 struct ServiceListenContext *slc;
1559
1560 slc = GNUNET_new (struct ServiceListenContext);
1561 slc->sh = sh;
1562 slc->listen_socket = open_listen_socket (addrs[i],
1563 addrlens[i]);
1564 GNUNET_free (addrs[i]);
1565 if (NULL == slc->listen_socket)
1566 {
1567 GNUNET_log_strerror (GNUNET_ERROR_TYPE_ERROR, "bind");
1568 GNUNET_free (slc);
1569 continue;
1570 }
1571 GNUNET_CONTAINER_DLL_insert (sh->slc_head,
1572 sh->slc_tail,
1573 slc);
1574 }
1575 GNUNET_free (addrlens);
1576 GNUNET_free (addrs);
1577 if ((0 != num) && (NULL == sh->slc_head))
1578 {
1579 /* All attempts to bind failed, hard failure */
1580 GNUNET_log (
1581 GNUNET_ERROR_TYPE_ERROR,
1582 _ (
1583 "Could not bind to any of the ports I was supposed to, refusing to run!\n"));
1584 GNUNET_free (csocks);
1585 return GNUNET_SYSERR;
1586 }
1587 }
1588 if (NULL != csocks)
1589 {
1590 /* close inherited sockets to signal parent that we are ready */
1591 for (struct GNUNET_NETWORK_Handle **ls = csocks; NULL != *ls; ls++)
1592 GNUNET_NETWORK_socket_close (*ls);
1593 GNUNET_free (csocks);
1594 }
1595 sh->require_found = (GNUNET_NO == tolerant);
1596 sh->match_uid = GNUNET_CONFIGURATION_get_value_yesno (sh->cfg,
1597 sh->service_name,
1598 "UNIX_MATCH_UID");
1599 sh->match_gid = GNUNET_CONFIGURATION_get_value_yesno (sh->cfg,
1600 sh->service_name,
1601 "UNIX_MATCH_GID");
1602 process_acl4 (&sh->v4_denied, sh, "REJECT_FROM");
1603 process_acl4 (&sh->v4_allowed, sh, "ACCEPT_FROM");
1604 process_acl6 (&sh->v6_denied, sh, "REJECT_FROM6");
1605 process_acl6 (&sh->v6_allowed, sh, "ACCEPT_FROM6");
1606 return GNUNET_OK;
1607}
1608
1609
1610/**
1611 * Get the name of the user that'll be used
1612 * to provide the service.
1613 *
1614 * @param sh service context
1615 * @return value of the 'USERNAME' option
1616 */
1617static char *
1618get_user_name (struct GNUNET_SERVICE_Handle *sh)
1619{
1620 char *un;
1621
1622 if (GNUNET_OK !=
1623 GNUNET_CONFIGURATION_get_value_filename (sh->cfg,
1624 sh->service_name,
1625 "USERNAME",
1626 &un))
1627 return NULL;
1628 return un;
1629}
1630
1631
1632/**
1633 * Set user ID.
1634 *
1635 * @param sh service context
1636 * @return #GNUNET_OK on success, #GNUNET_SYSERR on error
1637 */
1638static enum GNUNET_GenericReturnValue
1639set_user_id (struct GNUNET_SERVICE_Handle *sh)
1640{
1641 char *user;
1642 struct passwd *pws;
1643
1644 if (NULL == (user = get_user_name (sh)))
1645 return GNUNET_OK; /* keep */
1646 errno = 0;
1647 pws = getpwnam (user);
1648 if (NULL == pws)
1649 {
1650 LOG (GNUNET_ERROR_TYPE_ERROR,
1651 _ ("Cannot obtain information about user `%s': %s\n"),
1652 user,
1653 errno == 0 ? _ ("No such user") : strerror (errno));
1654 GNUNET_free (user);
1655 return GNUNET_SYSERR;
1656 }
1657 if ((0 != setgid (pws->pw_gid)) || (0 != setegid (pws->pw_gid)) ||
1658#if HAVE_INITGROUPS
1659 (0 != initgroups (user, pws->pw_gid)) ||
1660#endif
1661 (0 != setuid (pws->pw_uid)) ||
1662 (0 != seteuid (pws->pw_uid)))
1663 {
1664 if ((0 != setregid (pws->pw_gid, pws->pw_gid)) ||
1665 (0 != setreuid (pws->pw_uid, pws->pw_uid)))
1666 {
1667 LOG (GNUNET_ERROR_TYPE_ERROR,
1668 _ ("Cannot change user/group to `%s': %s\n"),
1669 user,
1670 strerror (errno));
1671 GNUNET_free (user);
1672 return GNUNET_SYSERR;
1673 }
1674 }
1675
1676 GNUNET_free (user);
1677 return GNUNET_OK;
1678}
1679
1680
1681/**
1682 * Get the name of the file where we will
1683 * write the PID of the service.
1684 *
1685 * @param sh service context
1686 * @return name of the file for the process ID
1687 */
1688static char *
1689get_pid_file_name (struct GNUNET_SERVICE_Handle *sh)
1690{
1691 char *pif;
1692
1693 if (GNUNET_OK !=
1694 GNUNET_CONFIGURATION_get_value_filename (sh->cfg,
1695 sh->service_name,
1696 "PIDFILE",
1697 &pif))
1698 return NULL;
1699 return pif;
1700}
1701
1702
1703/**
1704 * Delete the PID file that was created by our parent.
1705 *
1706 * @param sh service context
1707 */
1708static void
1709pid_file_delete (struct GNUNET_SERVICE_Handle *sh)
1710{
1711 char *pif = get_pid_file_name (sh);
1712
1713 if (NULL == pif)
1714 return; /* no PID file */
1715 if (0 != unlink (pif))
1716 LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_WARNING,
1717 "unlink",
1718 pif);
1719 GNUNET_free (pif);
1720}
1721
1722
1723/**
1724 * Detach from terminal.
1725 *
1726 * @param sh service context
1727 * @return #GNUNET_OK on success, #GNUNET_SYSERR on error
1728 */
1729static enum GNUNET_GenericReturnValue
1730detach_terminal (struct GNUNET_SERVICE_Handle *sh)
1731{
1732 pid_t pid;
1733 int nullfd;
1734 int filedes[2];
1735
1736 if (0 != pipe (filedes))
1737 {
1738 LOG_STRERROR (GNUNET_ERROR_TYPE_ERROR,
1739 "pipe");
1740 return GNUNET_SYSERR;
1741 }
1742 pid = fork ();
1743 if (pid < 0)
1744 {
1745 LOG_STRERROR (GNUNET_ERROR_TYPE_ERROR,
1746 "fork");
1747 return GNUNET_SYSERR;
1748 }
1749 if (0 != pid)
1750 {
1751 /* Parent */
1752 char c;
1753
1754 GNUNET_break (0 == close (filedes[1]));
1755 c = 'X';
1756 if (1 != read (filedes[0], &c, sizeof(char)))
1757 LOG_STRERROR (GNUNET_ERROR_TYPE_WARNING,
1758 "read");
1759 fflush (stdout);
1760 switch (c)
1761 {
1762 case '.':
1763 exit (0);
1764
1765 case 'I':
1766 LOG (GNUNET_ERROR_TYPE_INFO,
1767 _ ("Service process failed to initialize\n"));
1768 break;
1769
1770 case 'S':
1771 LOG (GNUNET_ERROR_TYPE_INFO,
1772 _ ("Service process could not initialize server function\n"));
1773 break;
1774
1775 case 'X':
1776 LOG (GNUNET_ERROR_TYPE_INFO,
1777 _ ("Service process failed to report status\n"));
1778 break;
1779 }
1780 exit (1); /* child reported error */
1781 }
1782 GNUNET_break (0 == close (0));
1783 GNUNET_break (0 == close (1));
1784 GNUNET_break (0 == close (filedes[0]));
1785 nullfd = open ("/dev/null", O_RDWR | O_APPEND);
1786 if (nullfd < 0)
1787 return GNUNET_SYSERR;
1788 /* set stdin/stdout to /dev/null */
1789 if ((dup2 (nullfd, 0) < 0) || (dup2 (nullfd, 1) < 0))
1790 {
1791 LOG_STRERROR (GNUNET_ERROR_TYPE_ERROR, "dup2");
1792 (void) close (nullfd);
1793 return GNUNET_SYSERR;
1794 }
1795 (void) close (nullfd);
1796 /* Detach from controlling terminal */
1797 pid = setsid ();
1798 if (-1 == pid)
1799 LOG_STRERROR (GNUNET_ERROR_TYPE_ERROR,
1800 "setsid");
1801 sh->ready_confirm_fd = filedes[1];
1802
1803 return GNUNET_OK;
1804}
1805
1806
1807/**
1808 * Tear down the service, closing the listen sockets and
1809 * freeing the ACLs.
1810 *
1811 * @param sh handle to the service to tear down.
1812 */
1813static void
1814teardown_service (struct GNUNET_SERVICE_Handle *sh)
1815{
1816 struct ServiceListenContext *slc;
1817
1818 GNUNET_free (sh->v4_denied);
1819 GNUNET_free (sh->v6_denied);
1820 GNUNET_free (sh->v4_allowed);
1821 GNUNET_free (sh->v6_allowed);
1822 while (NULL != (slc = sh->slc_head))
1823 {
1824 GNUNET_CONTAINER_DLL_remove (sh->slc_head,
1825 sh->slc_tail,
1826 slc);
1827 if (NULL != slc->listen_task)
1828 GNUNET_SCHEDULER_cancel (slc->listen_task);
1829 GNUNET_break (GNUNET_OK ==
1830 GNUNET_NETWORK_socket_close (slc->listen_socket));
1831 GNUNET_free (slc);
1832 }
1833}
1834
1835
1836/**
1837 * Function to return link to AGPL source upon request.
1838 *
1839 * @param cls closure with the identification of the client
1840 * @param msg AGPL request
1841 */
1842static void
1843return_agpl (void *cls,
1844 const struct GNUNET_MessageHeader *msg)
1845{
1846 struct GNUNET_SERVICE_Client *client = cls;
1847 struct GNUNET_MQ_Handle *mq;
1848 struct GNUNET_MQ_Envelope *env;
1849 struct GNUNET_MessageHeader *res;
1850 size_t slen;
1851 const struct GNUNET_OS_ProjectData *pd = GNUNET_OS_project_data_get ();
1852
1853 (void) msg;
1854 slen = strlen (pd->agpl_url) + 1;
1855 env = GNUNET_MQ_msg_extra (res,
1856 GNUNET_MESSAGE_TYPE_RESPONSE_AGPL, slen);
1857 memcpy (&res[1],
1858 GNUNET_AGPL_URL,
1859 slen);
1860 mq = GNUNET_SERVICE_client_get_mq (client);
1861 GNUNET_MQ_send (mq, env);
1862 GNUNET_SERVICE_client_continue (client);
1863}
1864
1865
1866struct GNUNET_SERVICE_Handle *
1867GNUNET_SERVICE_start (const char *service_name,
1868 const struct GNUNET_CONFIGURATION_Handle *cfg,
1869 GNUNET_SERVICE_ConnectHandler connect_cb,
1870 GNUNET_SERVICE_DisconnectHandler disconnect_cb,
1871 void *cls,
1872 const struct GNUNET_MQ_MessageHandler *handlers)
1873{
1874 struct GNUNET_SERVICE_Handle *sh;
1875
1876 sh = GNUNET_new (struct GNUNET_SERVICE_Handle);
1877 sh->service_name = service_name;
1878 sh->cfg = cfg;
1879 sh->connect_cb = connect_cb;
1880 sh->disconnect_cb = disconnect_cb;
1881 sh->cb_cls = cls;
1882 sh->handlers = GNUNET_MQ_copy_handlers2 (handlers,
1883 &return_agpl,
1884 NULL);
1885 if (GNUNET_OK != setup_service (sh))
1886 {
1887 GNUNET_free (sh->handlers);
1888 GNUNET_free (sh);
1889 return NULL;
1890 }
1891 do_resume (sh,
1892 SUSPEND_STATE_NONE);
1893 return sh;
1894}
1895
1896
1897/**
1898 * Asynchronously finish dropping the client.
1899 *
1900 * @param cls the `struct GNUNET_SERVICE_Client`.
1901 */
1902static void
1903finish_client_drop (void *cls)
1904{
1905 struct GNUNET_SERVICE_Client *c = cls;
1906 struct GNUNET_SERVICE_Handle *sh = c->sh;
1907
1908 c->drop_task = NULL;
1909 GNUNET_CONTAINER_DLL_remove (sh->clients_head,
1910 sh->clients_tail,
1911 c);
1912 GNUNET_assert (NULL == c->send_task);
1913 GNUNET_assert (NULL == c->recv_task);
1914 GNUNET_assert (NULL == c->warn_task);
1915 GNUNET_MST_destroy (c->mst);
1916 GNUNET_MQ_destroy (c->mq);
1917 if (! c->persist)
1918 {
1919 GNUNET_break (GNUNET_OK ==
1920 GNUNET_NETWORK_socket_close (c->sock));
1921 if ((0 != (SUSPEND_STATE_EMFILE & sh->suspend_state)) &&
1922 (0 == (SUSPEND_STATE_SHUTDOWN & sh->suspend_state)))
1923 do_resume (sh,
1924 SUSPEND_STATE_EMFILE);
1925 }
1926 else
1927 {
1928 GNUNET_NETWORK_socket_free_memory_only_ (c->sock);
1929 }
1930 GNUNET_free (c);
1931 if ((0 != (SUSPEND_STATE_SHUTDOWN & sh->suspend_state)) &&
1932 (! have_non_monitor_clients (sh)))
1933 GNUNET_SERVICE_shutdown (sh);
1934}
1935
1936
1937void
1938GNUNET_SERVICE_stop (struct GNUNET_SERVICE_Handle *srv)
1939{
1940 struct GNUNET_SERVICE_Client *client;
1941
1942 GNUNET_SERVICE_suspend (srv);
1943 while (NULL != (client = srv->clients_head))
1944 {
1945 if (NULL == client->drop_task)
1946 GNUNET_SERVICE_client_drop (client);
1947 GNUNET_SCHEDULER_cancel (client->drop_task);
1948 finish_client_drop (client);
1949 }
1950 teardown_service (srv);
1951 GNUNET_free (srv->handlers);
1952 GNUNET_free (srv);
1953}
1954
1955
1956int
1957GNUNET_SERVICE_run_ (int argc,
1958 char *const *argv,
1959 const char *service_name,
1960 enum GNUNET_SERVICE_Options options,
1961 GNUNET_SERVICE_InitCallback service_init_cb,
1962 GNUNET_SERVICE_ConnectHandler connect_cb,
1963 GNUNET_SERVICE_DisconnectHandler disconnect_cb,
1964 void *cls,
1965 const struct GNUNET_MQ_MessageHandler *handlers)
1966{
1967 struct GNUNET_SERVICE_Handle sh;
1968
1969#if ENABLE_NLS
1970 char *path;
1971#endif
1972 char *cfg_filename;
1973 char *opt_cfg_filename;
1974 char *loglev;
1975 const char *xdg;
1976 char *logfile;
1977 int do_daemonize;
1978 unsigned long long skew_offset;
1979 unsigned long long skew_variance;
1980 long long clock_offset;
1981 struct GNUNET_CONFIGURATION_Handle *cfg;
1982 int ret;
1983 int err;
1984 const struct GNUNET_OS_ProjectData *pd = GNUNET_OS_project_data_get ();
1985 struct GNUNET_GETOPT_CommandLineOption service_options[] = {
1986 GNUNET_GETOPT_option_cfgfile (&opt_cfg_filename),
1987 GNUNET_GETOPT_option_flag ('d',
1988 "daemonize",
1989 gettext_noop (
1990 "do daemonize (detach from terminal)"),
1991 &do_daemonize),
1992 GNUNET_GETOPT_option_help (NULL),
1993 GNUNET_GETOPT_option_loglevel (&loglev),
1994 GNUNET_GETOPT_option_logfile (&logfile),
1995 GNUNET_GETOPT_option_version (pd->version),
1996 GNUNET_GETOPT_OPTION_END
1997 };
1998
1999 err = 1;
2000 memset (&sh, 0, sizeof(sh));
2001 xdg = getenv ("XDG_CONFIG_HOME");
2002 if (NULL != xdg)
2003 GNUNET_asprintf (&cfg_filename,
2004 "%s%s%s",
2005 xdg,
2006 DIR_SEPARATOR_STR,
2007 pd->config_file);
2008 else
2009 cfg_filename = GNUNET_strdup (pd->user_config_file);
2010 sh.ready_confirm_fd = -1;
2011 sh.options = options;
2012 sh.cfg = cfg = GNUNET_CONFIGURATION_create ();
2013 sh.service_init_cb = service_init_cb;
2014 sh.connect_cb = connect_cb;
2015 sh.disconnect_cb = disconnect_cb;
2016 sh.cb_cls = cls;
2017 sh.handlers = (NULL == pd->agpl_url)
2018 ? GNUNET_MQ_copy_handlers (handlers)
2019 : GNUNET_MQ_copy_handlers2 (handlers, &return_agpl, NULL);
2020 sh.service_name = service_name;
2021 /* setup subsystems */
2022 loglev = NULL;
2023 logfile = NULL;
2024 opt_cfg_filename = NULL;
2025 do_daemonize = 0;
2026#if ENABLE_NLS
2027 if (NULL != pd->gettext_domain)
2028 {
2029 setlocale (LC_ALL, "");
2030 path = (NULL == pd->gettext_path) ?
2031 GNUNET_OS_installation_get_path (GNUNET_OS_IPK_LOCALEDIR) :
2032 GNUNET_strdup (pd->gettext_path);
2033 if (NULL != path)
2034 {
2035 bindtextdomain (pd->gettext_domain, path);
2036 GNUNET_free (path);
2037 }
2038 textdomain (pd->gettext_domain);
2039 }
2040#endif
2041 ret = GNUNET_GETOPT_run (service_name,
2042 service_options,
2043 argc,
2044 argv);
2045 if (GNUNET_SYSERR == ret)
2046 goto shutdown;
2047 if (GNUNET_NO == ret)
2048 {
2049 err = 0;
2050 goto shutdown;
2051 }
2052 if (GNUNET_OK != GNUNET_log_setup (service_name,
2053 loglev,
2054 logfile))
2055 {
2056 GNUNET_break (0);
2057 goto shutdown;
2058 }
2059 if (NULL != opt_cfg_filename)
2060 {
2061 if ((GNUNET_YES != GNUNET_DISK_file_test (opt_cfg_filename)) ||
2062 (GNUNET_SYSERR == GNUNET_CONFIGURATION_load (cfg, opt_cfg_filename)))
2063 {
2064 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
2065 _ ("Malformed configuration file `%s', exit ...\n"),
2066 opt_cfg_filename);
2067 goto shutdown;
2068 }
2069 }
2070 else
2071 {
2072 if (GNUNET_YES ==
2073 GNUNET_DISK_file_test (cfg_filename))
2074 {
2075 if (GNUNET_SYSERR ==
2076 GNUNET_CONFIGURATION_load (cfg,
2077 cfg_filename))
2078 {
2079 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
2080 _ ("Malformed configuration file `%s', exit ...\n"),
2081 cfg_filename);
2082 goto shutdown;
2083 }
2084 }
2085 else
2086 {
2087 if (GNUNET_SYSERR ==
2088 GNUNET_CONFIGURATION_load (cfg,
2089 NULL))
2090 {
2091 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
2092 _ ("Malformed configuration, exit ...\n"));
2093 goto shutdown;
2094 }
2095 }
2096 }
2097 if (GNUNET_OK != setup_service (&sh))
2098 goto shutdown;
2099 if ( (1 == do_daemonize) &&
2100 (GNUNET_OK != detach_terminal (&sh)) )
2101 {
2102 GNUNET_break (0);
2103 goto shutdown;
2104 }
2105 if (GNUNET_OK != set_user_id (&sh))
2106 goto shutdown;
2107 LOG (GNUNET_ERROR_TYPE_DEBUG,
2108 "Service `%s' runs with configuration from `%s'\n",
2109 service_name,
2110 (NULL != opt_cfg_filename) ? opt_cfg_filename : cfg_filename);
2111 if ( (GNUNET_OK ==
2112 GNUNET_CONFIGURATION_get_value_number (sh.cfg,
2113 "TESTING",
2114 "SKEW_OFFSET",
2115 &skew_offset)) &&
2116 (GNUNET_OK ==
2117 GNUNET_CONFIGURATION_get_value_number (sh.cfg,
2118 "TESTING",
2119 "SKEW_VARIANCE",
2120 &skew_variance)) )
2121 {
2122 clock_offset = skew_offset - skew_variance;
2123 GNUNET_TIME_set_offset (clock_offset);
2124 LOG (GNUNET_ERROR_TYPE_DEBUG,
2125 "Skewing clock by %lld ms\n",
2126 (long long) clock_offset);
2127 }
2128 GNUNET_RESOLVER_connect (sh.cfg);
2129
2130 /* actually run service */
2131 err = 0;
2132 GNUNET_SCHEDULER_run (&service_main, &sh);
2133 /* shutdown */
2134 if (1 == do_daemonize)
2135 pid_file_delete (&sh);
2136
2137shutdown:
2138 if (-1 != sh.ready_confirm_fd)
2139 {
2140 if (1 != write (sh.ready_confirm_fd, err ? "I" : "S", 1))
2141 LOG_STRERROR (GNUNET_ERROR_TYPE_WARNING, "write");
2142 GNUNET_break (0 == close (sh.ready_confirm_fd));
2143 }
2144#if HAVE_MALLINFO2
2145 {
2146 char *counter;
2147
2148 if ( (GNUNET_YES ==
2149 GNUNET_CONFIGURATION_have_value (sh.cfg,
2150 service_name,
2151 "GAUGER_HEAP")) &&
2152 (GNUNET_OK ==
2153 GNUNET_CONFIGURATION_get_value_string (sh.cfg,
2154 service_name,
2155 "GAUGER_HEAP",
2156 &counter)))
2157 {
2158 struct mallinfo2 mi;
2159
2160 mi = mallinfo2 ();
2161 GAUGER (service_name,
2162 counter,
2163 mi.usmblks,
2164 "blocks");
2165 GNUNET_free (counter);
2166 }
2167 }
2168#endif
2169 teardown_service (&sh);
2170 GNUNET_free (sh.handlers);
2171 GNUNET_SPEEDUP_stop_ ();
2172 GNUNET_CONFIGURATION_destroy (cfg);
2173 GNUNET_free (logfile);
2174 GNUNET_free (loglev);
2175 GNUNET_free (cfg_filename);
2176 GNUNET_free (opt_cfg_filename);
2177
2178 return err ? GNUNET_SYSERR : 0;
2179}
2180
2181
2182/**
2183 * A list of service to be launched when GNUNET_SERVICE_main()
2184 * is called
2185 */
2186struct ServiceHandleList
2187{
2188 /* DLL */
2189 struct ServiceHandleList *prev;
2190
2191 /* DLL */
2192 struct ServiceHandleList *next;
2193
2194 /* Handle to the service to launch */
2195 struct GNUNET_SERVICE_Handle *sh;
2196};
2197
2198/* The service list */
2199static struct ServiceHandleList *hll_head;
2200
2201/* The service list */
2202static struct ServiceHandleList *hll_tail;
2203
2204
2205int
2206GNUNET_SERVICE_register_ (const char *service_name,
2207 enum GNUNET_SERVICE_Options options,
2208 GNUNET_SERVICE_InitCallback service_init_cb,
2209 GNUNET_SERVICE_ConnectHandler connect_cb,
2210 GNUNET_SERVICE_DisconnectHandler disconnect_cb,
2211 void *cls,
2212 const struct GNUNET_MQ_MessageHandler *handlers)
2213{
2214 struct ServiceHandleList *hle;
2215 struct GNUNET_SERVICE_Handle *sh = GNUNET_new (struct GNUNET_SERVICE_Handle);
2216 const struct GNUNET_OS_ProjectData *pd = GNUNET_OS_project_data_get ();
2217
2218 sh->ready_confirm_fd = -1;
2219 sh->options = options;
2220 sh->service_init_cb = service_init_cb;
2221 sh->connect_cb = connect_cb;
2222 sh->disconnect_cb = disconnect_cb;
2223 sh->cb_cls = cls;
2224 sh->handlers = (NULL == pd->agpl_url)
2225 ? GNUNET_MQ_copy_handlers (handlers)
2226 : GNUNET_MQ_copy_handlers2 (handlers, &return_agpl, NULL);
2227 sh->service_name = service_name;
2228 hle = GNUNET_new (struct ServiceHandleList);
2229 hle->sh = sh;
2230 GNUNET_CONTAINER_DLL_insert (hll_head,
2231 hll_tail,
2232 hle);
2233 return 0;
2234}
2235
2236
2237static void
2238do_registered_services_shutdown (void *cls)
2239{
2240 while (NULL != hll_head)
2241 {
2242 struct ServiceHandleList *shl = hll_head;
2243 struct GNUNET_SERVICE_Handle *sh = shl->sh;
2244
2245 GNUNET_CONTAINER_DLL_remove (hll_head,
2246 hll_tail,
2247 shl);
2248 GNUNET_free (shl);
2249 if (-1 != sh->ready_confirm_fd)
2250 {
2251 if (1 != write (sh->ready_confirm_fd, "S", 1))
2252 LOG_STRERROR (GNUNET_ERROR_TYPE_WARNING,
2253 "write");
2254 GNUNET_break (0 ==
2255 close (sh->ready_confirm_fd));
2256 }
2257 teardown_service (sh);
2258 GNUNET_free (sh->handlers);
2259 GNUNET_free (sh);
2260 }
2261}
2262
2263
2264static void
2265launch_registered_services (void *cls)
2266{
2267 struct GNUNET_CONFIGURATION_Handle *cfg = cls;
2268
2269 for (struct ServiceHandleList *shl = hll_head;
2270 NULL != shl;
2271 shl = shl->next)
2272 {
2273 shl->sh->cfg = cfg;
2274 if (GNUNET_OK != setup_service (shl->sh))
2275 continue;
2276 if (GNUNET_OK != set_user_id (shl->sh))
2277 continue;
2278 GNUNET_SCHEDULER_add_now (&service_main,
2279 shl->sh);
2280 }
2281 GNUNET_SCHEDULER_add_shutdown (&do_registered_services_shutdown,
2282 NULL);
2283}
2284
2285
2286void
2287GNUNET_SERVICE_main (int argc,
2288 char *const *argv)
2289{
2290 char *cfg_filename;
2291 char *opt_cfg_filename;
2292 char *logfile;
2293 char *loglev;
2294 const char *xdg;
2295 int do_daemonize;
2296 int ret;
2297 struct GNUNET_CONFIGURATION_Handle *cfg;
2298 const struct GNUNET_OS_ProjectData *pd = GNUNET_OS_project_data_get ();
2299 struct GNUNET_GETOPT_CommandLineOption service_options[] = {
2300 GNUNET_GETOPT_option_cfgfile (&opt_cfg_filename),
2301 GNUNET_GETOPT_option_flag ('d',
2302 "daemonize",
2303 gettext_noop (
2304 "do daemonize (detach from terminal)"),
2305 &do_daemonize),
2306 GNUNET_GETOPT_option_help (NULL),
2307 GNUNET_GETOPT_option_loglevel (&loglev),
2308 GNUNET_GETOPT_option_logfile (&logfile),
2309 GNUNET_GETOPT_option_version (pd->version),
2310 GNUNET_GETOPT_OPTION_END
2311 };
2312
2313 xdg = getenv ("XDG_CONFIG_HOME");
2314 if (NULL != xdg)
2315 GNUNET_asprintf (&cfg_filename,
2316 "%s%s%s",
2317 xdg,
2318 DIR_SEPARATOR_STR,
2319 pd->config_file);
2320 else
2321 cfg_filename = GNUNET_strdup (pd->user_config_file);
2322 cfg = GNUNET_CONFIGURATION_create ();
2323 // FIXME we need to set this up for each service!
2324 ret = GNUNET_GETOPT_run ("libgnunet",
2325 service_options,
2326 argc,
2327 argv);
2328 if (GNUNET_SYSERR == ret)
2329 goto shutdown;
2330 if (GNUNET_NO == ret)
2331 {
2332 goto shutdown;
2333 }
2334 // FIXME we need to set this up for each service!
2335 // NOTE: that was not the idea. What are you proposing? -CG
2336 if (GNUNET_OK !=
2337 GNUNET_log_setup ("libgnunet",
2338 loglev,
2339 logfile))
2340 {
2341 GNUNET_break (0);
2342 goto shutdown;
2343 }
2344 if (NULL != opt_cfg_filename)
2345 {
2346 if ( (GNUNET_YES !=
2347 GNUNET_DISK_file_test (opt_cfg_filename)) ||
2348 (GNUNET_SYSERR ==
2349 GNUNET_CONFIGURATION_load (cfg,
2350 opt_cfg_filename)) )
2351 {
2352 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
2353 _ ("Malformed configuration file `%s', exit ...\n"),
2354 opt_cfg_filename);
2355 goto shutdown;
2356 }
2357 }
2358 else
2359 {
2360 if (GNUNET_YES ==
2361 GNUNET_DISK_file_test (cfg_filename))
2362 {
2363 if (GNUNET_SYSERR ==
2364 GNUNET_CONFIGURATION_load (cfg,
2365 cfg_filename))
2366 {
2367 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
2368 _ ("Malformed configuration file `%s', exit ...\n"),
2369 cfg_filename);
2370 GNUNET_free (cfg_filename);
2371 return;
2372 }
2373 }
2374 else
2375 {
2376 if (GNUNET_SYSERR ==
2377 GNUNET_CONFIGURATION_load (cfg,
2378 NULL))
2379 {
2380 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
2381 _ ("Malformed configuration, exit ...\n"));
2382 GNUNET_free (cfg_filename);
2383 return;
2384 }
2385 }
2386 }
2387 GNUNET_RESOLVER_connect (cfg);
2388
2389 GNUNET_SCHEDULER_run (&launch_registered_services,
2390 cfg);
2391shutdown:
2392 GNUNET_SPEEDUP_stop_ ();
2393 GNUNET_CONFIGURATION_destroy (cfg);
2394 GNUNET_free (logfile);
2395 GNUNET_free (loglev);
2396 GNUNET_free (cfg_filename);
2397 GNUNET_free (opt_cfg_filename);
2398}
2399
2400
2401void
2402GNUNET_SERVICE_suspend (struct GNUNET_SERVICE_Handle *sh)
2403{
2404 do_suspend (sh,
2405 SUSPEND_STATE_APP);
2406}
2407
2408
2409void
2410GNUNET_SERVICE_resume (struct GNUNET_SERVICE_Handle *sh)
2411{
2412 do_resume (sh,
2413 SUSPEND_STATE_APP);
2414}
2415
2416
2417/**
2418 * Task run to resume receiving data from the client after
2419 * the client called #GNUNET_SERVICE_client_continue().
2420 *
2421 * @param cls our `struct GNUNET_SERVICE_Client`
2422 */
2423static void
2424resume_client_receive (void *cls)
2425{
2426 struct GNUNET_SERVICE_Client *c = cls;
2427 int ret;
2428
2429 c->recv_task = NULL;
2430 /* first, check if there is still something in the buffer */
2431 ret = GNUNET_MST_next (c->mst,
2432 GNUNET_YES);
2433 if (GNUNET_SYSERR == ret)
2434 {
2435 if (NULL == c->drop_task)
2436 GNUNET_SERVICE_client_drop (c);
2437 return;
2438 }
2439 if (GNUNET_NO == ret)
2440 return; /* done processing, wait for more later */
2441 GNUNET_assert (GNUNET_OK == ret);
2442 if (c->needs_continue)
2443 return; /* #GNUNET_MST_next() did give a message to the client */
2444 /* need to receive more data from the network first */
2445 if (NULL != c->recv_task)
2446 return;
2447 c->recv_task = GNUNET_SCHEDULER_add_read_net (GNUNET_TIME_UNIT_FOREVER_REL,
2448 c->sock,
2449 &service_client_recv,
2450 c);
2451}
2452
2453
2454void
2455GNUNET_SERVICE_client_continue (struct GNUNET_SERVICE_Client *c)
2456{
2457 GNUNET_assert (NULL == c->drop_task);
2458 GNUNET_assert (c->needs_continue);
2459 GNUNET_assert (NULL == c->recv_task);
2460 c->needs_continue = false;
2461 if (NULL != c->warn_task)
2462 {
2463 GNUNET_SCHEDULER_cancel (c->warn_task);
2464 c->warn_task = NULL;
2465 }
2466 c->recv_task = GNUNET_SCHEDULER_add_now (&resume_client_receive,
2467 c);
2468}
2469
2470
2471void
2472GNUNET_SERVICE_client_disable_continue_warning (struct GNUNET_SERVICE_Client *c)
2473{
2474 GNUNET_break (NULL != c->warn_task);
2475 if (NULL != c->warn_task)
2476 {
2477 GNUNET_SCHEDULER_cancel (c->warn_task);
2478 c->warn_task = NULL;
2479 }
2480}
2481
2482
2483void
2484GNUNET_SERVICE_client_drop (struct GNUNET_SERVICE_Client *c)
2485{
2486 struct GNUNET_SERVICE_Handle *sh = c->sh;
2487
2488 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2489 "Client dropped: %p (MQ: %p)\n",
2490 c,
2491 c->mq);
2492#if EXECINFO
2493 {
2494 void *backtrace_array[MAX_TRACE_DEPTH];
2495 int num_backtrace_strings = backtrace (backtrace_array,
2496 MAX_TRACE_DEPTH);
2497 char **backtrace_strings =
2498 backtrace_symbols (backtrace_array,
2499 t->num_backtrace_strings);
2500 for (unsigned int i = 0; i < num_backtrace_strings; i++)
2501 LOG (GNUNET_ERROR_TYPE_DEBUG,
2502 "client drop trace %u: %s\n",
2503 i,
2504 backtrace_strings[i]);
2505 }
2506#endif
2507 GNUNET_assert (NULL == c->drop_task);
2508 if (NULL != sh->disconnect_cb)
2509 sh->disconnect_cb (sh->cb_cls,
2510 c,
2511 c->user_context);
2512 if (NULL != c->warn_task)
2513 {
2514 GNUNET_SCHEDULER_cancel (c->warn_task);
2515 c->warn_task = NULL;
2516 }
2517 if (NULL != c->recv_task)
2518 {
2519 GNUNET_SCHEDULER_cancel (c->recv_task);
2520 c->recv_task = NULL;
2521 }
2522 if (NULL != c->send_task)
2523 {
2524 GNUNET_SCHEDULER_cancel (c->send_task);
2525 c->send_task = NULL;
2526 }
2527 c->drop_task = GNUNET_SCHEDULER_add_now (&finish_client_drop,
2528 c);
2529}
2530
2531
2532void
2533GNUNET_SERVICE_shutdown (struct GNUNET_SERVICE_Handle *sh)
2534{
2535 if (0 == (sh->suspend_state & SUSPEND_STATE_SHUTDOWN))
2536 do_suspend (sh,
2537 SUSPEND_STATE_SHUTDOWN);
2538 for (struct GNUNET_SERVICE_Client *client = sh->clients_head;
2539 NULL != client;
2540 client = client->next)
2541 {
2542 if (NULL == client->drop_task)
2543 GNUNET_SERVICE_client_drop (client);
2544 }
2545}
2546
2547
2548void
2549GNUNET_SERVICE_client_mark_monitor (struct GNUNET_SERVICE_Client *c)
2550{
2551 c->is_monitor = true;
2552 if (((0 != (SUSPEND_STATE_SHUTDOWN & c->sh->suspend_state)) &&
2553 (! have_non_monitor_clients (c->sh))))
2554 GNUNET_SERVICE_shutdown (c->sh);
2555}
2556
2557
2558void
2559GNUNET_SERVICE_client_persist (struct GNUNET_SERVICE_Client *c)
2560{
2561 c->persist = true;
2562}
2563
2564
2565struct GNUNET_MQ_Handle *
2566GNUNET_SERVICE_client_get_mq (struct GNUNET_SERVICE_Client *c)
2567{
2568 return c->mq;
2569}
2570
2571
2572/* end of service.c */
diff --git a/src/lib/util/signal.c b/src/lib/util/signal.c
new file mode 100644
index 000000000..67849a7d6
--- /dev/null
+++ b/src/lib/util/signal.c
@@ -0,0 +1,110 @@
1/*
2 This file is part of GNUnet.
3 Copyright (C) 2001, 2002, 2006 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/signal.c
23 * @brief code for installing and uninstalling signal handlers
24 * @author Christian Grothoff
25 */
26
27
28#include "platform.h"
29#include "gnunet_util_lib.h"
30
31#define LOG(kind, ...) GNUNET_log_from (kind, "util-signal", __VA_ARGS__)
32
33
34struct GNUNET_SIGNAL_Context
35{
36 struct GNUNET_SIGNAL_Context *next;
37
38 struct GNUNET_SIGNAL_Context *prev;
39
40 int sig;
41
42 GNUNET_SIGNAL_Handler method;
43
44 struct sigaction oldsig;
45};
46
47static struct GNUNET_SIGNAL_Context *sc_head;
48
49static struct GNUNET_SIGNAL_Context *sc_tail;
50
51struct GNUNET_SIGNAL_Context *
52GNUNET_SIGNAL_handler_install (int signum, GNUNET_SIGNAL_Handler handler)
53{
54 struct GNUNET_SIGNAL_Context *ret;
55
56 struct sigaction sig;
57
58 ret = GNUNET_new (struct GNUNET_SIGNAL_Context);
59 ret->sig = signum;
60 ret->method = handler;
61
62 memset (&sig, 0, sizeof(sig));
63 sig.sa_handler = (void *) handler;
64 sigemptyset (&sig.sa_mask);
65#ifdef SA_INTERRUPT
66 sig.sa_flags = SA_INTERRUPT; /* SunOS */
67#else
68 sig.sa_flags = SA_RESTART;
69#endif
70 sigaction (signum, &sig, &ret->oldsig);
71
72 GNUNET_CONTAINER_DLL_insert_tail (sc_head, sc_tail, ret);
73 return ret;
74}
75
76
77void
78GNUNET_SIGNAL_handler_uninstall (struct GNUNET_SIGNAL_Context *ctx)
79{
80 struct sigaction sig;
81
82 sigemptyset (&sig.sa_mask);
83 sigaction (ctx->sig, &ctx->oldsig, &sig);
84
85 GNUNET_CONTAINER_DLL_remove (sc_head, sc_tail, ctx);
86 GNUNET_free (ctx);
87}
88
89
90/**
91 * Raise the given signal by calling the installed signal handlers. This will
92 * not use the @em raise() system call but only calls the handlers registered
93 * through GNUNET_SIGNAL_handler_install().
94 *
95 * @param sig the signal to raise
96 */
97void
98GNUNET_SIGNAL_raise (const int sig)
99{
100 struct GNUNET_SIGNAL_Context *ctx;
101
102 for (ctx = sc_head; NULL != ctx; ctx = ctx->next)
103 {
104 if (sig != ctx->sig)
105 continue;
106 if (NULL == ctx->method)
107 continue;
108 ctx->method ();
109 }
110}
diff --git a/src/lib/util/socks.c b/src/lib/util/socks.c
new file mode 100644
index 000000000..ffde8a667
--- /dev/null
+++ b/src/lib/util/socks.c
@@ -0,0 +1,690 @@
1/*
2 This file is part of GNUnet.
3 Copyright (C) 2009-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/socks.c
23 * @brief SOCKS5 connection support
24 * @author Jeffrey Burdges
25 *
26 * These routines should be called only on newly active connections.
27 */
28
29#include "platform.h"
30#include "gnunet_util_lib.h"
31
32
33#define LOG(kind, ...) GNUNET_log_from (kind, "util-socks", __VA_ARGS__)
34
35#define LOG_STRERROR(kind, syscall) \
36 GNUNET_log_from_strerror (kind, "util-socks", syscall)
37
38
39/* SOCKS5 authentication methods */
40#define SOCKS5_AUTH_REJECT 0xFF /* No acceptable auth method */
41#define SOCKS5_AUTH_NOAUTH 0x00 /* without authentication */
42#define SOCKS5_AUTH_GSSAPI 0x01 /* GSSAPI */
43#define SOCKS5_AUTH_USERPASS 0x02 /* User/Password */
44#define SOCKS5_AUTH_CHAP 0x03 /* Challenge-Handshake Auth Proto. */
45#define SOCKS5_AUTH_EAP 0x05 /* Extensible Authentication Proto. */
46#define SOCKS5_AUTH_MAF 0x08 /* Multi-Authentication Framework */
47
48
49/* SOCKS5 connection responses */
50#define SOCKS5_REP_SUCCEEDED 0x00 /* succeeded */
51#define SOCKS5_REP_FAIL 0x01 /* general SOCKS serer failure */
52#define SOCKS5_REP_NALLOWED 0x02 /* connection not allowed by ruleset */
53#define SOCKS5_REP_NUNREACH 0x03 /* Network unreachable */
54#define SOCKS5_REP_HUNREACH 0x04 /* Host unreachable */
55#define SOCKS5_REP_REFUSED 0x05 /* connection refused */
56#define SOCKS5_REP_EXPIRED 0x06 /* TTL expired */
57#define SOCKS5_REP_CNOTSUP 0x07 /* Command not supported */
58#define SOCKS5_REP_ANOTSUP 0x08 /* Address not supported */
59#define SOCKS5_REP_INVADDR 0x09 /* Invalid address */
60
61const char *
62SOCKS5_REP_names (int rep)
63{
64 switch (rep)
65 {
66 case SOCKS5_REP_SUCCEEDED:
67 return "succeeded";
68
69 case SOCKS5_REP_FAIL:
70 return "general SOCKS server failure";
71
72 case SOCKS5_REP_NALLOWED:
73 return "connection not allowed by ruleset";
74
75 case SOCKS5_REP_NUNREACH:
76 return "Network unreachable";
77
78 case SOCKS5_REP_HUNREACH:
79 return "Host unreachable";
80
81 case SOCKS5_REP_REFUSED:
82 return "connection refused";
83
84 case SOCKS5_REP_EXPIRED:
85 return "TTL expired";
86
87 case SOCKS5_REP_CNOTSUP:
88 return "Command not supported";
89
90 case SOCKS5_REP_ANOTSUP:
91 return "Address not supported";
92
93 case SOCKS5_REP_INVADDR:
94 return "Invalid address";
95
96 default:
97 return NULL;
98 }
99};
100
101
102/**
103 * Encode a string for the SOCKS5 protocol by prefixing it a byte stating its
104 * length and stripping the trailing zero byte. Truncates any string longer
105 * than 255 bytes.
106 *
107 * @param b buffer to contain the encoded string
108 * @param s string to encode
109 * @return pointer to the end of the encoded string in the buffer
110 */
111unsigned char *
112SOCK5_proto_string (unsigned char *b, const char *s)
113{
114 size_t l = strlen (s);
115
116 if (l > 255)
117 {
118 LOG (GNUNET_ERROR_TYPE_WARNING,
119 "SOCKS5 cannot handle hostnames, usernames, or passwords over 255 bytes, truncating.\n");
120 l = 255;
121 }
122 *(b++) = (unsigned char) l;
123 memcpy (b, s, l);
124 return b + l;
125}
126
127
128#define SOCKS5_step_greet 0
129#define SOCKS5_step_auth 1
130#define SOCKS5_step_cmd 2
131#define SOCKS5_step_done 3
132
133/**
134 * State of the SOCKS5 handshake.
135 */
136struct GNUNET_SOCKS_Handshake
137{
138 /**
139 * Connection handle used for SOCKS5
140 */
141 struct GNUNET_CONNECTION_Handle *socks5_connection;
142
143 /**
144 * Connection handle initially returned to client
145 */
146 struct GNUNET_CONNECTION_Handle *target_connection;
147
148 /**
149 * Transmission handle on socks5_connection.
150 */
151 struct GNUNET_CONNECTION_TransmitHandle *th;
152
153 /**
154 * Our stage in the SOCKS5 handshake
155 */
156 int step;
157
158 /**
159 * Precomputed SOCKS5 handshake output buffer
160 */
161 unsigned char outbuf[1024];
162
163 /**
164 * Pointers delineating protoocol steps in the output buffer
165 */
166 unsigned char *(outstep[4]);
167
168 /**
169 * SOCKS5 handshake input buffer
170 */
171 unsigned char inbuf[1024];
172
173 /**
174 * Pointers delimiting the current step in the input buffer
175 */
176 unsigned char *instart;
177 unsigned char *inend;
178};
179
180
181/* Registering prototypes */
182
183void
184register_reciever (struct GNUNET_SOCKS_Handshake *ih, int want);
185
186/* In fact, the client sends first rule in GNUnet suggests one could take
187 * large mac read sizes without fear of screwing up the proxied protocol,
188 * but we make a proper SOCKS5 client. */
189#define register_reciever_wants(ih) ((SOCKS5_step_cmd == ih->step) ? 10 : 2)
190
191
192struct GNUNET_CONNECTION_TransmitHandle *
193register_sender (struct GNUNET_SOCKS_Handshake *ih);
194
195
196/**
197 * Conclude the SOCKS5 handshake successfully.
198 *
199 * @param ih SOCKS5 handshake, consumed here.
200 * @param c open unused connection, consumed here.
201 * @return Connection handle that becomes usable when the handshake completes.
202 */
203void
204SOCKS5_handshake_done (struct GNUNET_SOCKS_Handshake *ih)
205{
206 GNUNET_CONNECTION_acivate_proxied (ih->target_connection);
207}
208
209
210/**
211 * Read one step in the SOCKS5 handshake.
212 *
213 * @param ih SOCKS5 Handshake
214 */
215void
216SOCKS5_handshake_step (struct GNUNET_SOCKS_Handshake *ih)
217{
218 unsigned char *b = ih->instart;
219 size_t available = ih->inend - b;
220
221 int want = register_reciever_wants (ih);
222
223 if (available < want)
224 {
225 register_reciever (ih, want - available);
226 return;
227 }
228 GNUNET_assert (SOCKS5_step_done > ih->step && ih->step >= 0);
229 switch (ih->step)
230 {
231 case SOCKS5_step_greet: /* SOCKS5 server's greeting */
232 if (b[0] != 5)
233 {
234 LOG (GNUNET_ERROR_TYPE_ERROR, "Not a SOCKS5 server\n");
235 GNUNET_assert (0);
236 }
237 switch (b[1])
238 {
239 case SOCKS5_AUTH_NOAUTH:
240 ih->step = SOCKS5_step_cmd; /* no authentication to do */
241 break;
242
243 case SOCKS5_AUTH_USERPASS:
244 ih->step = SOCKS5_step_auth;
245 break;
246
247 case SOCKS5_AUTH_REJECT:
248 LOG (GNUNET_ERROR_TYPE_ERROR, "No authentication method accepted\n");
249 return;
250
251 default:
252 LOG (GNUNET_ERROR_TYPE_ERROR,
253 "Not a SOCKS5 server / Nonsensical authentication\n");
254 return;
255 }
256 b += 2;
257 break;
258
259 case SOCKS5_step_auth: /* SOCKS5 server's response to authentication */
260 if (b[1] != 0)
261 {
262 LOG (GNUNET_ERROR_TYPE_ERROR, "SOCKS5 authentication failed\n");
263 GNUNET_assert (0);
264 }
265 ih->step = SOCKS5_step_cmd;
266 b += 2;
267 break;
268
269 case SOCKS5_step_cmd: /* SOCKS5 server's response to command */
270 if (b[0] != 5)
271 {
272 LOG (GNUNET_ERROR_TYPE_ERROR, "SOCKS5 protocol error\n");
273 GNUNET_assert (0);
274 }
275 if (0 != b[1])
276 {
277 LOG (GNUNET_ERROR_TYPE_ERROR,
278 "SOCKS5 connection error : %s\n",
279 SOCKS5_REP_names (b[1]));
280 return;
281 }
282 b += 3;
283 /* There is no reason to verify host and port afaik. */
284 switch (*(b++))
285 {
286 case 1: /* IPv4 */
287 b += sizeof(struct in_addr); /* 4 */
288 break;
289
290 case 4: /* IPv6 */
291 b += sizeof(struct in6_addr); /* 16 */
292 break;
293
294 case 3: /* hostname */
295 b += *b;
296 break;
297 }
298 b += 2; /* port */
299 if (b > ih->inend)
300 {
301 register_reciever (ih, b - ih->inend);
302 return;
303 }
304 ih->step = SOCKS5_step_done;
305 LOG (GNUNET_ERROR_TYPE_DEBUG,
306 "SOCKS5 server : %s\n",
307 SOCKS5_REP_names (b[1]));
308 ih->instart = b;
309 SOCKS5_handshake_done (ih);
310 return;
311
312 case SOCKS5_step_done:
313 GNUNET_assert (0);
314 }
315 ih->instart = b;
316 /* Do not reschedule the sender unless we're done reading.
317 * I imagine this lets us avoid ever cancelling the transmit handle. */
318 register_sender (ih);
319}
320
321
322/**
323 * Callback to read from the SOCKS5 proxy.
324 *
325 * @param client the service
326 * @param handler function to call with the message
327 * @param handler_cls closure for @a handler
328 */
329void
330receiver (void *cls,
331 const void *buf,
332 size_t available,
333 const struct sockaddr *addr,
334 socklen_t addrlen,
335 int errCode)
336{
337 struct GNUNET_SOCKS_Handshake *ih = cls;
338
339 GNUNET_assert (&ih->inend[available] < &ih->inbuf[1024]);
340 GNUNET_memcpy (ih->inend, buf, available);
341 ih->inend += available;
342 SOCKS5_handshake_step (ih);
343}
344
345
346/**
347 * Register callback to read from the SOCKS5 proxy.
348 *
349 * @param client the service
350 * @param handler function to call with the message
351 * @param handler_cls closure for @a handler
352 */
353void
354register_reciever (struct GNUNET_SOCKS_Handshake *ih, int want)
355{
356 GNUNET_CONNECTION_receive (ih->socks5_connection,
357 want,
358 GNUNET_TIME_relative_get_minute_ (),
359 &receiver,
360 ih);
361}
362
363
364/**
365 * Register SOCKS5 handshake sender
366 *
367 * @param cls closure (SOCKS handshake)
368 * @param size number of bytes available in @a buf
369 * @param buf where the callee should write the message
370 * @return number of bytes written to @a buf
371 */
372size_t
373transmit_ready (void *cls, size_t size, void *buf)
374{
375 struct GNUNET_SOCKS_Handshake *ih = cls;
376
377 /* connection.c has many routines that call us with buf == NULL :
378 * signal_transmit_error() - DNS, etc. active
379 * connect_fail_continuation()
380 * connect_probe_continuation() - timeout
381 * try_connect_using_address() - DNS failure/timeout
382 * transmit_timeout() - retry failed?
383 * GNUNET_CONNECTION_notify_transmit_ready() can schedule :
384 * transmit_timeout() - DNS still working
385 * connect_error() - DNS done but no socket?
386 * transmit_ready() - scheduler shutdown or timeout, or signal_transmit_error()
387 * We'd need to dig into the scheduler to guess at the reason, as
388 * connection.c tells us nothing itself, but mostly its timouts.
389 * Initially, we'll simply ignore this and leave massive timeouts, but
390 * maybe that should change for error handling pruposes. It appears that
391 * successful operations, including DNS resolution, do not use this. */if (NULL == buf)
392 {
393 if (0 == ih->step)
394 {
395 LOG (GNUNET_ERROR_TYPE_WARNING,
396 "Timeout contacting SOCKS server, retrying indefinitely, but probably hopeless.\n");
397 register_sender (ih);
398 }
399 else
400 {
401 LOG (GNUNET_ERROR_TYPE_ERROR,
402 "Timeout during mid SOCKS handshake (step %u), probably not a SOCKS server.\n",
403 ih->step);
404 GNUNET_break (0);
405 }
406 return 0;
407 }
408
409 GNUNET_assert ((1024 >= size) && (size > 0));
410 GNUNET_assert ((SOCKS5_step_done > ih->step) && (ih->step >= 0));
411 unsigned char *b = ih->outstep[ih->step];
412 unsigned char *e = ih->outstep[ih->step + 1];
413 GNUNET_assert (e <= &ih->outbuf[1024]);
414 unsigned int l = e - b;
415 GNUNET_assert (size >= l);
416 GNUNET_memcpy (buf, b, l);
417 register_reciever (ih, register_reciever_wants (ih));
418 return l;
419}
420
421
422/**
423 * Register SOCKS5 handshake sender
424 *
425 * @param ih handshake
426 * @return non-NULL if the notify callback was queued,
427 * NULL if we are already going to notify someone else (busy)
428 */
429struct GNUNET_CONNECTION_TransmitHandle *
430register_sender (struct GNUNET_SOCKS_Handshake *ih)
431{
432 struct GNUNET_TIME_Relative timeout = GNUNET_TIME_UNIT_MINUTES;
433
434 GNUNET_assert (SOCKS5_step_done > ih->step);
435 GNUNET_assert (ih->step >= 0);
436 if (0 == ih->step)
437 timeout = GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_MINUTES, 3);
438 unsigned char *b = ih->outstep[ih->step];
439 unsigned char *e = ih->outstep[ih->step + 1];
440 GNUNET_assert (ih->outbuf <= b && b < e && e < &ih->outbuf[1024]);
441 ih->th = GNUNET_CONNECTION_notify_transmit_ready (ih->socks5_connection,
442 e - b,
443 timeout,
444 &transmit_ready,
445 ih);
446 return ih->th;
447}
448
449
450/**
451 * Initialize a SOCKS5 handshake for authentication via username and
452 * password. Tor uses SOCKS username and password authentication to assign
453 * programs unique circuits.
454 *
455 * @param user username for the proxy
456 * @param pass password for the proxy
457 * @return Valid SOCKS5 hanbdshake handle
458 */
459struct GNUNET_SOCKS_Handshake *
460GNUNET_SOCKS_init_handshake (const char *user, const char *pass)
461{
462 struct GNUNET_SOCKS_Handshake *ih =
463 GNUNET_new (struct GNUNET_SOCKS_Handshake);
464 unsigned char *b = ih->outbuf;
465
466 ih->outstep[SOCKS5_step_greet] = b;
467 *(b++) = 5; /* SOCKS5 */
468 unsigned char *n = b++;
469 *n = 1; /* Number of authentication methods */
470 /* We support no authentication even when requesting authentication,
471 * but this appears harmless, given the way that Tor uses authentication.
472 * And some SOCKS5 servers might require this. */
473 *(b++) = SOCKS5_AUTH_NOAUTH;
474 if (NULL != user)
475 {
476 *(b++) = SOCKS5_AUTH_USERPASS;
477 (*n)++;
478 }
479 /* There is no apparent reason to support authentication methods beyond
480 * username and password since afaik Tor does not support them. */
481
482 /* We authenticate with an empty username and password if the server demands
483 * them but we do not have any. */
484 if (user == NULL)
485 user = "";
486 if (pass == NULL)
487 pass = "";
488
489 ih->outstep[SOCKS5_step_auth] = b;
490 *(b++) = 1; /* subnegotiation ver.: 1 */
491 b = SOCK5_proto_string (b, user);
492 b = SOCK5_proto_string (b, pass);
493
494 ih->outstep[SOCKS5_step_cmd] = b;
495
496 ih->inend = ih->instart = ih->inbuf;
497
498 return ih;
499}
500
501
502/**
503 * Initialize a SOCKS5 handshake without authentication, thereby possibly
504 * sharing a Tor circuit with another process.
505 *
506 * @return Valid SOCKS5 hanbdshake handle
507 */
508struct GNUNET_SOCKS_Handshake *
509GNUNET_SOCKS_init_handshake_noauth ()
510{
511 return GNUNET_SOCKS_init_handshake (NULL, NULL);
512}
513
514
515/**
516 * Build request that the SOCKS5 proxy open a TCP/IP stream to the given host
517 * and port.
518 *
519 * @param ih SOCKS5 handshake
520 * @param host
521 * @param port
522 */
523void
524GNUNET_SOCKS_set_handshake_destination (struct GNUNET_SOCKS_Handshake *ih,
525 const char *host,
526 uint16_t port)
527{
528 union
529 {
530 struct in_addr in4;
531 struct in6_addr in6;
532 } ia;
533 unsigned char *b = ih->outstep[SOCKS5_step_cmd];
534
535 *(b++) = 5; /* SOCKS5 */
536 *(b++) = 1; /* Establish a TCP/IP stream */
537 *(b++) = 0; /* reserved */
538
539 /* Specify destination */
540 if (1 == inet_pton (AF_INET, host, &ia.in4))
541 {
542 *(b++) = 1; /* IPv4 */
543 GNUNET_memcpy (b, &ia.in4, sizeof(struct in_addr));
544 b += sizeof(struct in_addr); /* 4 */
545 }
546 else if (1 == inet_pton (AF_INET6, host, &ia.in6))
547 {
548 *(b++) = 4; /* IPv6 */
549 GNUNET_memcpy (b, &ia.in6, sizeof(struct in6_addr));
550 b += sizeof(struct in6_addr); /* 16 */
551 }
552 else
553 {
554 *(b++) = 3; /* hostname */
555 b = SOCK5_proto_string (b, host);
556 }
557
558 /* Specify port */
559 *(uint16_t *) b = htons (port);
560 b += 2;
561
562 ih->outstep[SOCKS5_step_done] = b;
563}
564
565
566/**
567 * Run a SOCKS5 handshake on an open but unused TCP connection.
568 *
569 * @param ih SOCKS5 handshake, consumed here.
570 * @param c open unused connection, consumed here.
571 * @return Connection handle that becomes usable when the SOCKS5 handshake completes.
572 */
573struct GNUNET_CONNECTION_Handle *
574GNUNET_SOCKS_run_handshake (struct GNUNET_SOCKS_Handshake *ih,
575 struct GNUNET_CONNECTION_Handle *c)
576{
577 ih->socks5_connection = c;
578 ih->target_connection = GNUNET_CONNECTION_create_proxied_from_handshake (c);
579 register_sender (ih);
580
581 return ih->target_connection;
582}
583
584
585/**
586 * Check if a SOCKS proxy is required by a service. Do not use local service
587 * if a SOCKS proxy port is configured as this could deanonymize a user.
588 *
589 * @param service_name name of service to connect to
590 * @param cfg configuration to use
591 * @return GNUNET_YES if so, GNUNET_NO if not
592 */
593int
594GNUNET_SOCKS_check_service (const char *service_name,
595 const struct GNUNET_CONFIGURATION_Handle *cfg)
596{
597 return GNUNET_CONFIGURATION_have_value (cfg, service_name, "SOCKSPORT") ||
598 GNUNET_CONFIGURATION_have_value (cfg, service_name, "SOCKSHOST");
599}
600
601
602/**
603 * Try to connect to a service configured to use a SOCKS5 proxy.
604 *
605 * @param service_name name of service to connect to
606 * @param cfg configuration to use
607 * @return Connection handle that becomes usable when the handshake completes.
608 * NULL if SOCKS not configured or not configured properly
609 */
610struct GNUNET_CONNECTION_Handle *
611GNUNET_SOCKS_do_connect (const char *service_name,
612 const struct GNUNET_CONFIGURATION_Handle *cfg)
613{
614 struct GNUNET_SOCKS_Handshake *ih;
615 struct GNUNET_CONNECTION_Handle *socks5; /* *proxied */
616 char *host0;
617 char *host1;
618 char *user;
619 char *pass;
620 unsigned long long port0;
621 unsigned long long port1;
622
623 if (GNUNET_YES != GNUNET_SOCKS_check_service (service_name, cfg))
624 return NULL;
625 if (GNUNET_OK != GNUNET_CONFIGURATION_get_value_number (cfg,
626 service_name,
627 "SOCKSPORT",
628 &port0))
629 port0 = 9050;
630 /* A typical Tor client should usually try port 9150 for the TBB too, but
631 * GNUnet can probably assume a system Tor installation. */
632 if ((port0 > 65535) || (port0 <= 0))
633 {
634 LOG (GNUNET_ERROR_TYPE_WARNING,
635 _ (
636 "Attempting to use invalid port %d as SOCKS proxy for service `%s'.\n"),
637 port0,
638 service_name);
639 return NULL;
640 }
641 if ((GNUNET_OK != GNUNET_CONFIGURATION_get_value_number (cfg,
642 service_name,
643 "PORT",
644 &port1)) ||
645 (port1 > 65535) || (port1 <= 0) ||
646 (GNUNET_OK != GNUNET_CONFIGURATION_get_value_string (cfg,
647 service_name,
648 "HOSTNAME",
649 &host1)))
650 {
651 LOG (GNUNET_ERROR_TYPE_WARNING,
652 _ (
653 "Attempting to proxy service `%s' to invalid port %d or hostname.\n"),
654 service_name,
655 port1);
656 return NULL;
657 }
658 /* Appeared to still work after host0 corrupted, so either test case is broken, or
659 this whole routine is not being called. */
660 if (GNUNET_OK != GNUNET_CONFIGURATION_get_value_string (cfg,
661 service_name,
662 "SOCKSHOST",
663 &host0))
664 host0 = NULL;
665 socks5 = GNUNET_CONNECTION_create_from_connect (cfg,
666 (host0 != NULL) ? host0
667 : "127.0.0.1",
668 port0);
669 GNUNET_free (host0);
670
671 /* Sets to NULL if they do not exist */
672 (void) GNUNET_CONFIGURATION_get_value_string (cfg,
673 service_name,
674 "SOCKSUSER",
675 &user);
676 (void) GNUNET_CONFIGURATION_get_value_string (cfg,
677 service_name,
678 "SOCKSPASS",
679 &pass);
680 ih = GNUNET_SOCKS_init_handshake (user, pass);
681 GNUNET_free (user);
682 GNUNET_free (pass);
683
684 GNUNET_SOCKS_set_handshake_destination (ih, host1, port1);
685 GNUNET_free (host1);
686 return GNUNET_SOCKS_run_handshake (ih, socks5);
687}
688
689
690/* socks.c */
diff --git a/src/lib/util/speedup.c b/src/lib/util/speedup.c
new file mode 100644
index 000000000..02719e56e
--- /dev/null
+++ b/src/lib/util/speedup.c
@@ -0,0 +1,114 @@
1/*
2 This file is part of GNUnet.
3 Copyright (C) 2011-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/speedup.c
23 * @author Matthias Wachs
24 * @brief functions to speedup peer execution by manipulation system time
25 */
26
27#include "platform.h"
28#include "gnunet_util_lib.h"
29#include "speedup.h"
30
31#define LOG(kind, ...) GNUNET_log_from (kind, "util-speedup", __VA_ARGS__)
32
33
34static struct GNUNET_TIME_Relative interval;
35
36static struct GNUNET_TIME_Relative delta;
37
38static struct GNUNET_SCHEDULER_Task *speedup_task;
39
40
41static void
42do_speedup (void *cls)
43{
44 static long long current_offset;
45
46 (void) cls;
47 speedup_task = NULL;
48 current_offset += delta.rel_value_us;
49 GNUNET_TIME_set_offset (current_offset);
50 LOG (GNUNET_ERROR_TYPE_DEBUG,
51 "Speeding up execution time by %s\n",
52 GNUNET_STRINGS_relative_time_to_string (delta, GNUNET_NO));
53 speedup_task = GNUNET_SCHEDULER_add_delayed (interval,
54 &do_speedup,
55 NULL);
56}
57
58
59int
60GNUNET_SPEEDUP_start_ (const struct GNUNET_CONFIGURATION_Handle *cfg)
61{
62 GNUNET_assert (NULL == speedup_task);
63 if (GNUNET_OK !=
64 GNUNET_CONFIGURATION_get_value_time (cfg,
65 "testing",
66 "SPEEDUP_INTERVAL",
67 &interval))
68 return GNUNET_SYSERR;
69 if (GNUNET_OK !=
70 GNUNET_CONFIGURATION_get_value_time (cfg,
71 "testing",
72 "SPEEDUP_DELTA",
73 &delta))
74 return GNUNET_SYSERR;
75
76 if ((0 == interval.rel_value_us) ||
77 (0 == delta.rel_value_us))
78 {
79 LOG (GNUNET_ERROR_TYPE_DEBUG,
80 "Speed up disabled\n");
81 return GNUNET_OK;
82 }
83 LOG (GNUNET_ERROR_TYPE_DEBUG,
84 "Speed up execution by %s\n",
85 GNUNET_STRINGS_relative_time_to_string (delta, GNUNET_NO));
86 LOG (GNUNET_ERROR_TYPE_DEBUG,
87 "Speed up executed every %s\n",
88 GNUNET_STRINGS_relative_time_to_string (interval, GNUNET_NO));
89 speedup_task = GNUNET_SCHEDULER_add_now_with_lifeness (GNUNET_NO,
90 &do_speedup,
91 NULL);
92 return GNUNET_OK;
93}
94
95
96/**
97 * Stop tasks that modify clock behavior.
98 */
99void
100GNUNET_SPEEDUP_stop_ ()
101{
102 if (NULL != speedup_task)
103 {
104 GNUNET_SCHEDULER_cancel (speedup_task);
105 speedup_task = NULL;
106 }
107 if ((0 != interval.rel_value_us) &&
108 (0 != delta.rel_value_us))
109 LOG (GNUNET_ERROR_TYPE_DEBUG,
110 "Stopped execution speed up\n");
111}
112
113
114/* end of speedup.c */
diff --git a/src/lib/util/speedup.h b/src/lib/util/speedup.h
new file mode 100644
index 000000000..505e73bf4
--- /dev/null
+++ b/src/lib/util/speedup.h
@@ -0,0 +1,45 @@
1/*
2 This file is part of GNUnet.
3 Copyright (C) 2009 -- 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/speedup.c
23 * @brief Interface for speedup routinues
24 * @author Sree Harsha Totakura <sreeharsha@totakura.in>
25 */
26
27#ifndef SPEEDUP_H_
28#define SPEEDUP_H_
29
30/**
31 * Start task that may speed up our system clock artificially
32 *
33 * @param cfg configuration to use
34 * @return GNUNET_OK on success, GNUNET_SYSERR if the speedup was not configured
35 */
36int
37GNUNET_SPEEDUP_start_ (const struct GNUNET_CONFIGURATION_Handle *cfg);
38
39/**
40 * Stop tasks that modify clock behavior.
41 */
42void
43GNUNET_SPEEDUP_stop_ (void);
44
45#endif /* SPEEDUP_H_ */
diff --git a/src/lib/util/strings.c b/src/lib/util/strings.c
new file mode 100644
index 000000000..dc9fd0daf
--- /dev/null
+++ b/src/lib/util/strings.c
@@ -0,0 +1,2077 @@
1/*
2 This file is part of GNUnet.
3 Copyright (C) 2005-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 util/strings.c
22 * @brief string functions
23 * @author Nils Durner
24 * @author Christian Grothoff
25 */
26
27
28#include "platform.h"
29#if HAVE_ICONV
30#include <iconv.h>
31#endif
32#include "gnunet_util_lib.h"
33#include <unicase.h>
34#include <unistr.h>
35#include <uniconv.h>
36
37#define LOG(kind, ...) GNUNET_log_from (kind, "util-strings", __VA_ARGS__)
38
39#define LOG_STRERROR(kind, syscall) \
40 GNUNET_log_from_strerror (kind, "util-strings", syscall)
41
42
43size_t
44GNUNET_STRINGS_buffer_fill (char *buffer,
45 size_t size,
46 unsigned int count, ...)
47{
48 size_t needed;
49 va_list ap;
50
51 needed = 0;
52 va_start (ap, count);
53 while (count > 0)
54 {
55 const char *s = va_arg (ap, const char *);
56 size_t slen = strlen (s) + 1;
57
58 GNUNET_assert (slen <= size - needed);
59 if (NULL != buffer)
60 GNUNET_memcpy (&buffer[needed],
61 s,
62 slen);
63 needed += slen;
64 count--;
65 }
66 va_end (ap);
67 return needed;
68}
69
70
71unsigned int
72GNUNET_STRINGS_buffer_tokenize (const char *buffer,
73 size_t size,
74 unsigned int count,
75 ...)
76{
77 unsigned int start;
78 unsigned int needed;
79 const char **r;
80 va_list ap;
81
82 needed = 0;
83 va_start (ap, count);
84 while (count > 0)
85 {
86 r = va_arg (ap, const char **);
87
88 start = needed;
89 while ((needed < size) && (buffer[needed] != '\0'))
90 needed++;
91 if (needed == size)
92 {
93 va_end (ap);
94 return 0; /* error */
95 }
96 *r = &buffer[start];
97 needed++; /* skip 0-termination */
98 count--;
99 }
100 va_end (ap);
101 return needed;
102}
103
104
105char *
106GNUNET_STRINGS_byte_size_fancy (unsigned long long size)
107{
108 const char *unit = /* size unit */ "b";
109 char *ret;
110
111 if (size > 5 * 1024)
112 {
113 size = size / 1024;
114 unit = "KiB";
115 if (size > 5 * 1024)
116 {
117 size = size / 1024;
118 unit = "MiB";
119 if (size > 5 * 1024)
120 {
121 size = size / 1024;
122 unit = "GiB";
123 if (size > 5 * 1024)
124 {
125 size = size / 1024;
126 unit = "TiB";
127 }
128 }
129 }
130 }
131 ret = GNUNET_malloc (32);
132 GNUNET_snprintf (ret, 32, "%llu %s", size, unit);
133 return ret;
134}
135
136
137size_t
138GNUNET_strlcpy (char *dst,
139 const char *src,
140 size_t n)
141{
142 size_t slen;
143
144 GNUNET_assert (0 != n);
145 slen = strnlen (src, n - 1);
146 memcpy (dst, src, slen);
147 dst[slen] = '\0';
148 return slen;
149}
150
151
152/**
153 * Unit conversion table entry for 'convert_with_table'.
154 */
155struct ConversionTable
156{
157 /**
158 * Name of the unit (or NULL for end of table).
159 */
160 const char *name;
161
162 /**
163 * Factor to apply for this unit.
164 */
165 unsigned long long value;
166};
167
168
169/**
170 * Convert a string of the form "4 X 5 Y" into a numeric value
171 * by interpreting "X" and "Y" as units and then multiplying
172 * the numbers with the values associated with the respective
173 * unit from the conversion table.
174 *
175 * @param input input string to parse
176 * @param table table with the conversion of unit names to numbers
177 * @param output where to store the result
178 * @return #GNUNET_OK on success, #GNUNET_SYSERR on error
179 */
180static enum GNUNET_GenericReturnValue
181convert_with_table (const char *input,
182 const struct ConversionTable *table,
183 unsigned long long *output)
184{
185 unsigned long long ret;
186 char *in;
187 const char *tok;
188 unsigned long long last;
189 unsigned int i;
190 char *sptr;
191
192 ret = 0;
193 last = 0;
194 in = GNUNET_strdup (input);
195 for (tok = strtok_r (in, " ", &sptr);
196 tok != NULL;
197 tok = strtok_r (NULL, " ", &sptr))
198 {
199 do
200 {
201 i = 0;
202 while ((table[i].name != NULL) && (0 != strcasecmp (table[i].name, tok)))
203 i++;
204 if (table[i].name != NULL)
205 {
206 last *= table[i].value;
207 break; /* next tok */
208 }
209 else
210 {
211 char *endptr;
212 ret += last;
213 errno = 0;
214 last = strtoull (tok, &endptr, 10);
215 if ((0 != errno) || (endptr == tok))
216 {
217 GNUNET_free (in);
218 return GNUNET_SYSERR; /* expected number */
219 }
220 if ('\0' == endptr[0])
221 break; /* next tok */
222 else
223 tok = endptr; /* and re-check (handles times like "10s") */
224 }
225 }
226 while (GNUNET_YES);
227 }
228 ret += last;
229 *output = ret;
230 GNUNET_free (in);
231 return GNUNET_OK;
232}
233
234
235enum GNUNET_GenericReturnValue
236GNUNET_STRINGS_fancy_size_to_bytes (const char *fancy_size,
237 unsigned long long *size)
238{
239 static const struct ConversionTable table[] =
240 { { "B", 1 },
241 { "KiB", 1024 },
242 { "kB", 1000 },
243 { "MiB", 1024 * 1024 },
244 { "MB", 1000 * 1000 },
245 { "GiB", 1024 * 1024 * 1024 },
246 { "GB", 1000 * 1000 * 1000 },
247 { "TiB", 1024LL * 1024LL * 1024LL * 1024LL },
248 { "TB", 1000LL * 1000LL * 1000LL * 1024LL },
249 { "PiB", 1024LL * 1024LL * 1024LL * 1024LL * 1024LL },
250 { "PB", 1000LL * 1000LL * 1000LL * 1024LL * 1000LL },
251 { "EiB", 1024LL * 1024LL * 1024LL * 1024LL * 1024LL * 1024LL },
252 { "EB", 1000LL * 1000LL * 1000LL * 1024LL * 1000LL * 1000LL },
253 { NULL, 0 } };
254
255 return convert_with_table (fancy_size, table, size);
256}
257
258
259enum GNUNET_GenericReturnValue
260GNUNET_STRINGS_fancy_time_to_relative (const char *fancy_time,
261 struct GNUNET_TIME_Relative *rtime)
262{
263 static const struct ConversionTable table[] =
264 { { "us", 1 },
265 { "ms", 1000 },
266 { "s", 1000 * 1000LL },
267 { "second", 1000 * 1000LL },
268 { "seconds", 1000 * 1000LL },
269 { "\"", 1000 * 1000LL },
270 { "m", 60 * 1000 * 1000LL },
271 { "min", 60 * 1000 * 1000LL },
272 { "minute", 60 * 1000 * 1000LL },
273 { "minutes", 60 * 1000 * 1000LL },
274 { "'", 60 * 1000 * 1000LL },
275 { "h", 60 * 60 * 1000 * 1000LL },
276 { "hour", 60 * 60 * 1000 * 1000LL },
277 { "hours", 60 * 60 * 1000 * 1000LL },
278 { "d", 24 * 60 * 60 * 1000LL * 1000LL },
279 { "day", 24 * 60 * 60 * 1000LL * 1000LL },
280 { "days", 24 * 60 * 60 * 1000LL * 1000LL },
281 { "week", 7 * 24 * 60 * 60 * 1000LL * 1000LL },
282 { "weeks", 7 * 24 * 60 * 60 * 1000LL * 1000LL },
283 { "year", 31536000000000LL /* year */ },
284 { "years", 31536000000000LL /* year */ },
285 { "a", 31536000000000LL /* year */ },
286 { NULL, 0 } };
287 int ret;
288 unsigned long long val;
289
290 if (0 == strcasecmp ("forever", fancy_time))
291 {
292 *rtime = GNUNET_TIME_UNIT_FOREVER_REL;
293 return GNUNET_OK;
294 }
295 ret = convert_with_table (fancy_time, table, &val);
296 rtime->rel_value_us = (uint64_t) val;
297 return ret;
298}
299
300
301enum GNUNET_GenericReturnValue
302GNUNET_STRINGS_fancy_time_to_absolute (const char *fancy_time,
303 struct GNUNET_TIME_Absolute *atime)
304{
305 struct tm tv;
306 time_t t;
307 const char *eos;
308
309 if (0 == strcasecmp ("end of time", fancy_time))
310 {
311 *atime = GNUNET_TIME_UNIT_FOREVER_ABS;
312 return GNUNET_OK;
313 }
314 eos = &fancy_time[strlen (fancy_time)];
315 memset (&tv, 0, sizeof(tv));
316 if ((eos != strptime (fancy_time, "%a %b %d %H:%M:%S %Y", &tv)) &&
317 (eos != strptime (fancy_time, "%c", &tv)) &&
318 (eos != strptime (fancy_time, "%Ec", &tv)) &&
319 (eos != strptime (fancy_time, "%Y-%m-%d %H:%M:%S", &tv)) &&
320 (eos != strptime (fancy_time, "%Y-%m-%d %H:%M", &tv)) &&
321 (eos != strptime (fancy_time, "%x", &tv)) &&
322 (eos != strptime (fancy_time, "%Ex", &tv)) &&
323 (eos != strptime (fancy_time, "%Y-%m-%d", &tv)) &&
324 (eos != strptime (fancy_time, "%Y-%m", &tv)) &&
325 (eos != strptime (fancy_time, "%Y", &tv)))
326 return GNUNET_SYSERR;
327 t = mktime (&tv);
328 atime->abs_value_us = (uint64_t) ((uint64_t) t * 1000LL * 1000LL);
329 return GNUNET_OK;
330}
331
332
333enum GNUNET_GenericReturnValue
334GNUNET_STRINGS_fancy_time_to_timestamp (const char *fancy_time,
335 struct GNUNET_TIME_Timestamp *atime)
336{
337 return GNUNET_STRINGS_fancy_time_to_absolute (fancy_time,
338 &atime->abs_time);
339}
340
341
342char *
343GNUNET_STRINGS_conv (const char *input,
344 size_t len,
345 const char *input_charset,
346 const char *output_charset)
347{
348 char *ret;
349 uint8_t *u8_string;
350 char *encoded_string;
351 size_t u8_string_length;
352 size_t encoded_string_length;
353
354 u8_string = u8_conv_from_encoding (input_charset,
355 iconveh_error,
356 input,
357 len,
358 NULL,
359 NULL,
360 &u8_string_length);
361 if (NULL == u8_string)
362 {
363 LOG_STRERROR (GNUNET_ERROR_TYPE_WARNING, "u8_conv_from_encoding");
364 goto fail;
365 }
366 if (0 == strcmp (output_charset, "UTF-8"))
367 {
368 ret = GNUNET_malloc (u8_string_length + 1);
369 GNUNET_memcpy (ret, u8_string, u8_string_length);
370 ret[u8_string_length] = '\0';
371 free (u8_string);
372 return ret;
373 }
374 encoded_string = u8_conv_to_encoding (output_charset,
375 iconveh_error,
376 u8_string,
377 u8_string_length,
378 NULL,
379 NULL,
380 &encoded_string_length);
381 free (u8_string);
382 if (NULL == encoded_string)
383 {
384 LOG_STRERROR (GNUNET_ERROR_TYPE_WARNING, "u8_conv_to_encoding");
385 goto fail;
386 }
387 ret = GNUNET_malloc (encoded_string_length + 1);
388 GNUNET_memcpy (ret, encoded_string, encoded_string_length);
389 ret[encoded_string_length] = '\0';
390 free (encoded_string);
391 return ret;
392fail:
393 LOG (GNUNET_ERROR_TYPE_WARNING,
394 _ ("Character sets requested were `%s'->`%s'\n"),
395 "UTF-8",
396 output_charset);
397 ret = GNUNET_malloc (len + 1);
398 GNUNET_memcpy (ret, input, len);
399 ret[len] = '\0';
400 return ret;
401}
402
403
404char *
405GNUNET_STRINGS_to_utf8 (const char *input,
406 size_t len,
407 const char *charset)
408{
409 return GNUNET_STRINGS_conv (input,
410 len,
411 charset,
412 "UTF-8");
413}
414
415
416char *
417GNUNET_STRINGS_from_utf8 (const char *input,
418 size_t len,
419 const char *charset)
420{
421 return GNUNET_STRINGS_conv (input,
422 len,
423 "UTF-8",
424 charset);
425}
426
427
428char *
429GNUNET_STRINGS_utf8_normalize (const char *input)
430{
431 uint8_t *tmp;
432 size_t len;
433 char *output;
434 tmp = u8_normalize (UNINORM_NFC,
435 (uint8_t *) input,
436 strlen ((char*) input),
437 NULL,
438 &len);
439 if (NULL == tmp)
440 return NULL;
441 output = GNUNET_malloc (len + 1);
442 GNUNET_memcpy (output, tmp, len);
443 output[len] = '\0';
444 free (tmp);
445 return output;
446}
447
448
449enum GNUNET_GenericReturnValue
450GNUNET_STRINGS_utf8_tolower (const char *input,
451 char *output)
452{
453 uint8_t *tmp_in;
454 size_t len;
455
456 tmp_in = u8_tolower ((uint8_t *) input,
457 strlen ((char *) input),
458 NULL,
459 UNINORM_NFD,
460 NULL,
461 &len);
462 if (NULL == tmp_in)
463 return GNUNET_SYSERR;
464 GNUNET_memcpy (output, tmp_in, len);
465 output[len] = '\0';
466 GNUNET_free (tmp_in);
467 return GNUNET_OK;
468}
469
470
471enum GNUNET_GenericReturnValue
472GNUNET_STRINGS_utf8_toupper (const char *input,
473 char *output)
474{
475 uint8_t *tmp_in;
476 size_t len;
477
478 tmp_in = u8_toupper ((uint8_t *) input,
479 strlen ((char *) input),
480 NULL,
481 UNINORM_NFD,
482 NULL,
483 &len);
484 if (NULL == tmp_in)
485 return GNUNET_SYSERR;
486 /* 0-terminator does not fit */
487 GNUNET_memcpy (output, tmp_in, len);
488 output[len] = '\0';
489 GNUNET_free (tmp_in);
490 return GNUNET_OK;
491}
492
493
494char *
495GNUNET_STRINGS_filename_expand (const char *fil)
496{
497 char *buffer;
498 size_t len;
499 char *fm;
500 const char *fil_ptr;
501
502 if (fil == NULL)
503 return NULL;
504
505 if (fil[0] == DIR_SEPARATOR)
506 /* absolute path, just copy */
507 return GNUNET_strdup (fil);
508 if (fil[0] == '~')
509 {
510 fm = getenv ("HOME");
511 if (fm == NULL)
512 {
513 LOG (GNUNET_ERROR_TYPE_WARNING,
514 _ ("Failed to expand `$HOME': environment variable `HOME' not set"));
515 return NULL;
516 }
517 fm = GNUNET_strdup (fm);
518 /* do not copy '~' */
519 fil_ptr = fil + 1;
520
521 /* skip over dir separator to be consistent */
522 if (fil_ptr[0] == DIR_SEPARATOR)
523 fil_ptr++;
524 }
525 else
526 {
527 /* relative path */
528 fil_ptr = fil;
529 len = 512;
530 fm = NULL;
531 while (1)
532 {
533 buffer = GNUNET_malloc (len);
534 if (getcwd (buffer, len) != NULL)
535 {
536 fm = buffer;
537 break;
538 }
539 if ((errno == ERANGE) && (len < 1024 * 1024 * 4))
540 {
541 len *= 2;
542 GNUNET_free (buffer);
543 continue;
544 }
545 GNUNET_free (buffer);
546 break;
547 }
548 if (fm == NULL)
549 {
550 LOG_STRERROR (GNUNET_ERROR_TYPE_WARNING, "getcwd");
551 buffer = getenv ("PWD"); /* alternative */
552 if (buffer != NULL)
553 fm = GNUNET_strdup (buffer);
554 }
555 if (fm == NULL)
556 fm = GNUNET_strdup ("./"); /* give up */
557 }
558 GNUNET_asprintf (&buffer,
559 "%s%s%s",
560 fm,
561 (fm[strlen (fm) - 1] == DIR_SEPARATOR) ? ""
562 : DIR_SEPARATOR_STR,
563 fil_ptr);
564 GNUNET_free (fm);
565 return buffer;
566}
567
568
569const char *
570GNUNET_STRINGS_relative_time_to_string (struct GNUNET_TIME_Relative delta,
571 int do_round)
572{
573 static GNUNET_THREAD_LOCAL char buf[128];
574 const char *unit = /* time unit */ "µs";
575 uint64_t dval = delta.rel_value_us;
576
577 if (GNUNET_TIME_UNIT_FOREVER_REL.rel_value_us == delta.rel_value_us)
578 return "forever";
579 if (0 == delta.rel_value_us)
580 return "0 ms";
581 if (((GNUNET_YES == do_round) && (dval > 5 * 1000)) || (0 == (dval % 1000)))
582 {
583 dval = dval / 1000;
584 unit = /* time unit */ "ms";
585 if (((GNUNET_YES == do_round) && (dval > 5 * 1000)) || (0 == (dval % 1000)))
586 {
587 dval = dval / 1000;
588 unit = /* time unit */ "s";
589 if (((GNUNET_YES == do_round) && (dval > 5 * 60)) || (0 == (dval % 60)))
590 {
591 dval = dval / 60;
592 unit = /* time unit */ "m";
593 if (((GNUNET_YES == do_round) && (dval > 5 * 60)) || (0 == (dval % 60)))
594 {
595 dval = dval / 60;
596 unit = /* time unit */ "h";
597 if (((GNUNET_YES == do_round) && (dval > 5 * 24)) ||
598 (0 == (dval % 24)))
599 {
600 dval = dval / 24;
601 if (1 == dval)
602 unit = /* time unit */ "day";
603 else
604 unit = /* time unit */ "days";
605 }
606 }
607 }
608 }
609 }
610 GNUNET_snprintf (buf, sizeof(buf), "%llu %s",
611 (unsigned long long) dval, unit);
612 return buf;
613}
614
615
616const char *
617GNUNET_STRINGS_absolute_time_to_string (struct GNUNET_TIME_Absolute t)
618{
619 static GNUNET_THREAD_LOCAL char buf[255];
620 time_t tt;
621 struct tm *tp;
622
623 if (GNUNET_TIME_absolute_is_never (t))
624 return "end of time";
625 tt = t.abs_value_us / 1000LL / 1000LL;
626 tp = localtime (&tt);
627 /* This is hacky, but i don't know a way to detect libc character encoding.
628 * Just expect utf8 from glibc these days.
629 * As for msvcrt, use the wide variant, which always returns utf16
630 * (otherwise we'd have to detect current codepage or use W32API character
631 * set conversion routines to convert to UTF8).
632 */
633 strftime (buf, sizeof(buf), "%a %b %d %H:%M:%S %Y", tp);
634
635 return buf;
636}
637
638
639const char *
640GNUNET_STRINGS_get_short_name (const char *filename)
641{
642 const char *short_fn = filename;
643 const char *ss;
644
645 while (NULL != (ss = strstr (short_fn, DIR_SEPARATOR_STR)) && (ss[1] != '\0'))
646 short_fn = 1 + ss;
647 return short_fn;
648}
649
650
651/**
652 * Get the decoded value corresponding to a character according to Crockford
653 * Base32 encoding.
654 *
655 * @param a a character
656 * @return corresponding numeric value
657 */
658static unsigned int
659getValue__ (unsigned char a)
660{
661 unsigned int dec;
662
663 switch (a)
664 {
665 case 'O':
666 case 'o':
667 a = '0';
668 break;
669
670 case 'i':
671 case 'I':
672 case 'l':
673 case 'L':
674 a = '1';
675 break;
676
677 /* also consider U to be V */
678 case 'u':
679 case 'U':
680 a = 'V';
681 break;
682
683 default:
684 break;
685 }
686 if ((a >= '0') && (a <= '9'))
687 return a - '0';
688 if ((a >= 'a') && (a <= 'z'))
689 a = toupper (a);
690 /* return (a - 'a' + 10); */
691 dec = 0;
692 if ((a >= 'A') && (a <= 'Z'))
693 {
694 if ('I' < a)
695 dec++;
696 if ('L' < a)
697 dec++;
698 if ('O' < a)
699 dec++;
700 if ('U' < a)
701 dec++;
702 return(a - 'A' + 10 - dec);
703 }
704 return -1;
705}
706
707
708char *
709GNUNET_STRINGS_data_to_string (const void *data,
710 size_t size,
711 char *out,
712 size_t out_size)
713{
714 /**
715 * 32 characters for encoding
716 */
717 static char *encTable__ = "0123456789ABCDEFGHJKMNPQRSTVWXYZ";
718 unsigned int wpos;
719 unsigned int rpos;
720 unsigned int bits;
721 unsigned int vbit;
722 const unsigned char *udata;
723
724 GNUNET_assert (size < SIZE_MAX / 8 - 4);
725 udata = data;
726 if (out_size < (size * 8 + 4) / 5)
727 {
728 GNUNET_break (0);
729 return NULL;
730 }
731 vbit = 0;
732 wpos = 0;
733 rpos = 0;
734 bits = 0;
735 while ((rpos < size) || (vbit > 0))
736 {
737 if ((rpos < size) && (vbit < 5))
738 {
739 bits = (bits << 8) | udata[rpos++]; /* eat 8 more bits */
740 vbit += 8;
741 }
742 if (vbit < 5)
743 {
744 bits <<= (5 - vbit); /* zero-padding */
745 GNUNET_assert (vbit == ((size * 8) % 5));
746 vbit = 5;
747 }
748 if (wpos >= out_size)
749 {
750 GNUNET_break (0);
751 return NULL;
752 }
753 out[wpos++] = encTable__[(bits >> (vbit - 5)) & 31];
754 vbit -= 5;
755 }
756 GNUNET_assert (0 == vbit);
757 if (wpos < out_size)
758 out[wpos] = '\0';
759 return &out[wpos];
760}
761
762
763char *
764GNUNET_STRINGS_data_to_string_alloc (const void *buf, size_t size)
765{
766 char *str_buf;
767 size_t len = size * 8;
768 char *end;
769
770 if (len % 5 > 0)
771 len += 5 - len % 5;
772 len /= 5;
773 str_buf = GNUNET_malloc (len + 1);
774 end = GNUNET_STRINGS_data_to_string (buf,
775 size,
776 str_buf,
777 len);
778 if (NULL == end)
779 {
780 GNUNET_free (str_buf);
781 return NULL;
782 }
783 *end = '\0';
784 return str_buf;
785}
786
787
788enum GNUNET_GenericReturnValue
789GNUNET_STRINGS_string_to_data (const char *enc,
790 size_t enclen,
791 void *out,
792 size_t out_size)
793{
794 size_t rpos;
795 size_t wpos;
796 unsigned int bits;
797 unsigned int vbit;
798 int ret;
799 int shift;
800 unsigned char *uout;
801 size_t encoded_len;
802
803 if (0 == enclen)
804 {
805 if (0 == out_size)
806 return GNUNET_OK;
807 return GNUNET_SYSERR;
808 }
809 GNUNET_assert (out_size < SIZE_MAX / 8);
810 encoded_len = out_size * 8;
811 uout = out;
812 wpos = out_size;
813 rpos = enclen;
814 if ((encoded_len % 5) > 0)
815 {
816 vbit = encoded_len % 5; /* padding! */
817 shift = 5 - vbit;
818 bits = (ret = getValue__ (enc[--rpos])) >> shift;
819 }
820 else
821 {
822 vbit = 5;
823 shift = 0;
824 bits = (ret = getValue__ (enc[--rpos]));
825 }
826 if ((encoded_len + shift) / 5 != enclen)
827 return GNUNET_SYSERR;
828 if (-1 == ret)
829 return GNUNET_SYSERR;
830 while (wpos > 0)
831 {
832 if (0 == rpos)
833 {
834 GNUNET_break (0);
835 return GNUNET_SYSERR;
836 }
837 bits = ((ret = getValue__ (enc[--rpos])) << vbit) | bits;
838 if (-1 == ret)
839 return GNUNET_SYSERR;
840 vbit += 5;
841 if (vbit >= 8)
842 {
843 uout[--wpos] = (unsigned char) bits;
844 bits >>= 8;
845 vbit -= 8;
846 }
847 }
848 if ((0 != rpos) || (0 != vbit))
849 return GNUNET_SYSERR;
850 return GNUNET_OK;
851}
852
853
854enum GNUNET_GenericReturnValue
855GNUNET_STRINGS_string_to_data_alloc (const char *enc,
856 size_t enclen,
857 void **out,
858 size_t *out_size)
859{
860 size_t size;
861 void *data;
862 int res;
863
864 size = (enclen * 5) / 8;
865 if (size >= GNUNET_MAX_MALLOC_CHECKED)
866 {
867 GNUNET_break_op (0);
868 return GNUNET_SYSERR;
869 }
870 data = GNUNET_malloc (size);
871 res = GNUNET_STRINGS_string_to_data (enc,
872 enclen,
873 data,
874 size);
875 if ( (0 < size) &&
876 (GNUNET_OK != res) )
877 {
878 size--;
879 res = GNUNET_STRINGS_string_to_data (enc,
880 enclen,
881 data,
882 size);
883 }
884 if (GNUNET_OK != res)
885 {
886 GNUNET_break_op (0);
887 GNUNET_free (data);
888 return GNUNET_SYSERR;
889 }
890 *out = data;
891 *out_size = size;
892 return GNUNET_OK;
893}
894
895
896enum GNUNET_GenericReturnValue
897GNUNET_STRINGS_parse_uri (const char *path,
898 char **scheme_part,
899 const char **path_part)
900{
901 size_t len;
902 size_t i;
903 int end;
904 int pp_state = 0;
905 const char *post_scheme_part = NULL;
906
907 len = strlen (path);
908 for (end = 0, i = 0; ! end && i < len; i++)
909 {
910 switch (pp_state)
911 {
912 case 0:
913 if ((path[i] == ':') && (i > 0))
914 {
915 pp_state += 1;
916 continue;
917 }
918 if (! (((path[i] >= 'A') && (path[i] <= 'Z') ) ||
919 ((path[i] >= 'a') && (path[i] <= 'z') ) ||
920 ((path[i] >= '0') && (path[i] <= '9') ) || (path[i] == '+') ||
921 (path[i] == '-') || (path[i] == '.')))
922 end = 1;
923 break;
924
925 case 1:
926 case 2:
927 if (path[i] == '/')
928 {
929 pp_state += 1;
930 continue;
931 }
932 end = 1;
933 break;
934
935 case 3:
936 post_scheme_part = &path[i];
937 end = 1;
938 break;
939
940 default:
941 end = 1;
942 }
943 }
944 if (post_scheme_part == NULL)
945 return GNUNET_NO;
946 if (scheme_part)
947 {
948 *scheme_part = GNUNET_strndup (path,
949 post_scheme_part - path);
950 }
951 if (path_part)
952 *path_part = post_scheme_part;
953 return GNUNET_YES;
954}
955
956
957enum GNUNET_GenericReturnValue
958GNUNET_STRINGS_path_is_absolute (const char *filename,
959 int can_be_uri,
960 int *r_is_uri,
961 char **r_uri_scheme)
962{
963 const char *post_scheme_path;
964 int is_uri;
965 char *uri;
966 /* consider POSIX paths to be absolute too, even on W32,
967 * as plibc expansion will fix them for us.
968 */
969 if (filename[0] == '/')
970 return GNUNET_YES;
971 if (can_be_uri)
972 {
973 is_uri = GNUNET_STRINGS_parse_uri (filename, &uri, &post_scheme_path);
974 if (r_is_uri)
975 *r_is_uri = is_uri;
976 if (is_uri)
977 {
978 if (r_uri_scheme)
979 *r_uri_scheme = uri;
980 else
981 GNUNET_free (uri);
982
983 return GNUNET_STRINGS_path_is_absolute (post_scheme_path,
984 GNUNET_NO,
985 NULL,
986 NULL);
987 }
988 }
989 else
990 {
991 if (r_is_uri)
992 *r_is_uri = GNUNET_NO;
993 }
994
995 return GNUNET_NO;
996}
997
998
999enum GNUNET_GenericReturnValue
1000GNUNET_STRINGS_check_filename (const char *filename,
1001 enum GNUNET_STRINGS_FilenameCheck checks)
1002{
1003 struct stat st;
1004
1005 if ((NULL == filename) || (filename[0] == '\0'))
1006 return GNUNET_SYSERR;
1007 if (0 != (checks & GNUNET_STRINGS_CHECK_IS_ABSOLUTE))
1008 if (! GNUNET_STRINGS_path_is_absolute (filename, GNUNET_NO, NULL, NULL))
1009 return GNUNET_NO;
1010 if (0 != (checks
1011 & (GNUNET_STRINGS_CHECK_EXISTS | GNUNET_STRINGS_CHECK_IS_DIRECTORY
1012 | GNUNET_STRINGS_CHECK_IS_LINK)))
1013 {
1014 if (0 != lstat (filename, &st))
1015 {
1016 if (0 != (checks & GNUNET_STRINGS_CHECK_EXISTS))
1017 return GNUNET_NO;
1018 else
1019 return GNUNET_SYSERR;
1020 }
1021 }
1022 if (0 != (checks & GNUNET_STRINGS_CHECK_IS_DIRECTORY))
1023 if (! S_ISDIR (st.st_mode))
1024 return GNUNET_NO;
1025 if (0 != (checks & GNUNET_STRINGS_CHECK_IS_LINK))
1026 if (! S_ISLNK (st.st_mode))
1027 return GNUNET_NO;
1028 return GNUNET_YES;
1029}
1030
1031
1032enum GNUNET_GenericReturnValue
1033GNUNET_STRINGS_to_address_ipv6 (const char *zt_addr,
1034 size_t addrlen,
1035 struct sockaddr_in6 *r_buf)
1036{
1037 if (addrlen < 6)
1038 return GNUNET_SYSERR;
1039 if (addrlen > 512)
1040 return GNUNET_SYSERR; /* sanity check to protect zbuf allocation,
1041 actual limit is not precise */
1042 {
1043 char zbuf[addrlen + 1];
1044 int ret;
1045 char *port_colon;
1046 unsigned int port;
1047 char dummy[2];
1048
1049 GNUNET_memcpy (zbuf, zt_addr, addrlen);
1050 if ('[' != zbuf[0])
1051 {
1052 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
1053 _ ("IPv6 address did not start with `['\n"));
1054 return GNUNET_SYSERR;
1055 }
1056 zbuf[addrlen] = '\0';
1057 port_colon = strrchr (zbuf, ':');
1058 if (NULL == port_colon)
1059 {
1060 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
1061 _ ("IPv6 address did contain ':' to separate port number\n"));
1062 return GNUNET_SYSERR;
1063 }
1064 if (']' != *(port_colon - 1))
1065 {
1066 GNUNET_log (
1067 GNUNET_ERROR_TYPE_WARNING,
1068 _ (
1069 "IPv6 address did contain ']' before ':' to separate port number\n"));
1070 return GNUNET_SYSERR;
1071 }
1072 ret = sscanf (port_colon, ":%u%1s", &port, dummy);
1073 if ((1 != ret) || (port > 65535))
1074 {
1075 GNUNET_log (
1076 GNUNET_ERROR_TYPE_WARNING,
1077 _ (
1078 "IPv6 address did contain a valid port number after the last ':'\n"));
1079 return GNUNET_SYSERR;
1080 }
1081 *(port_colon - 1) = '\0';
1082 memset (r_buf, 0, sizeof(struct sockaddr_in6));
1083 ret = inet_pton (AF_INET6, &zbuf[1], &r_buf->sin6_addr);
1084 if (ret <= 0)
1085 {
1086 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
1087 _ ("Invalid IPv6 address `%s': %s\n"),
1088 &zbuf[1],
1089 strerror (errno));
1090 return GNUNET_SYSERR;
1091 }
1092 r_buf->sin6_port = htons (port);
1093 r_buf->sin6_family = AF_INET6;
1094#if HAVE_SOCKADDR_IN_SIN_LEN
1095 r_buf->sin6_len = (u_char) sizeof(struct sockaddr_in6);
1096#endif
1097 return GNUNET_OK;
1098 }
1099}
1100
1101
1102enum GNUNET_GenericReturnValue
1103GNUNET_STRINGS_to_address_ipv4 (const char *zt_addr,
1104 size_t addrlen,
1105 struct sockaddr_in *r_buf)
1106{
1107 unsigned int temps[4];
1108 unsigned int port;
1109 unsigned int cnt;
1110 char dummy[2];
1111
1112 if (addrlen < 9)
1113 return GNUNET_SYSERR;
1114 cnt = sscanf (zt_addr,
1115 "%u.%u.%u.%u:%u%1s",
1116 &temps[0],
1117 &temps[1],
1118 &temps[2],
1119 &temps[3],
1120 &port,
1121 dummy);
1122 if (5 != cnt)
1123 return GNUNET_SYSERR;
1124 for (cnt = 0; cnt < 4; cnt++)
1125 if (temps[cnt] > 0xFF)
1126 return GNUNET_SYSERR;
1127 if (port > 65535)
1128 return GNUNET_SYSERR;
1129 r_buf->sin_family = AF_INET;
1130 r_buf->sin_port = htons (port);
1131 r_buf->sin_addr.s_addr =
1132 htonl ((temps[0] << 24) + (temps[1] << 16) + (temps[2] << 8) + temps[3]);
1133#if HAVE_SOCKADDR_IN_SIN_LEN
1134 r_buf->sin_len = (u_char) sizeof(struct sockaddr_in);
1135#endif
1136 return GNUNET_OK;
1137}
1138
1139
1140enum GNUNET_GenericReturnValue
1141GNUNET_STRINGS_to_address_ip (const char *addr,
1142 uint16_t addrlen,
1143 struct sockaddr_storage *r_buf)
1144{
1145 if (addr[0] == '[')
1146 return GNUNET_STRINGS_to_address_ipv6 (addr,
1147 addrlen,
1148 (struct sockaddr_in6 *) r_buf);
1149 return GNUNET_STRINGS_to_address_ipv4 (addr,
1150 addrlen,
1151 (struct sockaddr_in *) r_buf);
1152}
1153
1154
1155size_t
1156GNUNET_STRINGS_parse_socket_addr (const char *addr,
1157 uint8_t *af,
1158 struct sockaddr **sa)
1159{
1160 *af = AF_UNSPEC;
1161 if ('[' == *addr)
1162 {
1163 /* IPv6 */
1164 *sa = GNUNET_malloc (sizeof(struct sockaddr_in6));
1165 if (GNUNET_OK !=
1166 GNUNET_STRINGS_to_address_ipv6 (addr,
1167 strlen (addr),
1168 (struct sockaddr_in6 *) *sa))
1169 {
1170 GNUNET_free (*sa);
1171 *sa = NULL;
1172 return 0;
1173 }
1174 *af = AF_INET6;
1175 return sizeof(struct sockaddr_in6);
1176 }
1177 else
1178 {
1179 /* IPv4 */
1180 *sa = GNUNET_malloc (sizeof(struct sockaddr_in));
1181 if (GNUNET_OK !=
1182 GNUNET_STRINGS_to_address_ipv4 (addr,
1183 strlen (addr),
1184 (struct sockaddr_in *) *sa))
1185 {
1186 GNUNET_free (*sa);
1187 *sa = NULL;
1188 return 0;
1189 }
1190 *af = AF_INET;
1191 return sizeof(struct sockaddr_in);
1192 }
1193}
1194
1195
1196/**
1197 * Makes a copy of argv that consists of a single memory chunk that can be
1198 * freed with a single call to GNUNET_free();
1199 */
1200static char *const *
1201_make_continuous_arg_copy (int argc,
1202 char *const *argv)
1203{
1204 size_t argvsize = 0;
1205 char **new_argv;
1206 char *p;
1207
1208 for (int i = 0; i < argc; i++)
1209 {
1210 size_t ail = strlen (argv[i]);
1211
1212 GNUNET_assert (SIZE_MAX - 1 - sizeof (char *) > argvsize);
1213 GNUNET_assert (SIZE_MAX - ail > argvsize + 1 + sizeof (char*));
1214 argvsize += strlen (argv[i]) + 1 + sizeof(char *);
1215 }
1216 new_argv = GNUNET_malloc (argvsize + sizeof(char *));
1217 p = (char *) &new_argv[argc + 1];
1218 for (int i = 0; i < argc; i++)
1219 {
1220 new_argv[i] = p;
1221 strcpy (p, argv[i]);
1222 p += strlen (argv[i]) + 1;
1223 }
1224 new_argv[argc] = NULL;
1225 return (char *const *) new_argv;
1226}
1227
1228
1229enum GNUNET_GenericReturnValue
1230GNUNET_STRINGS_get_utf8_args (int argc,
1231 char *const *argv,
1232 int *u8argc,
1233 char *const **u8argv)
1234{
1235 char *const *new_argv =
1236 (char *const *) _make_continuous_arg_copy (argc, argv);
1237 *u8argv = new_argv;
1238 *u8argc = argc;
1239 return GNUNET_OK;
1240}
1241
1242
1243/**
1244 * Parse the given port policy. The format is
1245 * "[!]SPORT[-DPORT]".
1246 *
1247 * @param port_policy string to parse
1248 * @param pp policy to fill in
1249 * @return #GNUNET_OK on success, #GNUNET_SYSERR if the
1250 * @a port_policy is malformed
1251 */
1252static enum GNUNET_GenericReturnValue
1253parse_port_policy (const char *port_policy,
1254 struct GNUNET_STRINGS_PortPolicy *pp)
1255{
1256 const char *pos;
1257 int s;
1258 int e;
1259 char eol[2];
1260
1261 pos = port_policy;
1262 if ('!' == *pos)
1263 {
1264 pp->negate_portrange = GNUNET_YES;
1265 pos++;
1266 }
1267 if (2 == sscanf (pos, "%u-%u%1s", &s, &e, eol))
1268 {
1269 if ((0 == s) || (s > 0xFFFF) || (e < s) || (e > 0xFFFF))
1270 {
1271 GNUNET_log (GNUNET_ERROR_TYPE_WARNING, _ ("Port not in range\n"));
1272 return GNUNET_SYSERR;
1273 }
1274 pp->start_port = (uint16_t) s;
1275 pp->end_port = (uint16_t) e;
1276 return GNUNET_OK;
1277 }
1278 if (1 == sscanf (pos, "%u%1s", &s, eol))
1279 {
1280 if ((0 == s) || (s > 0xFFFF))
1281 {
1282 GNUNET_log (GNUNET_ERROR_TYPE_WARNING, _ ("Port not in range\n"));
1283 return GNUNET_SYSERR;
1284 }
1285
1286 pp->start_port = (uint16_t) s;
1287 pp->end_port = (uint16_t) s;
1288 return GNUNET_OK;
1289 }
1290 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
1291 _ ("Malformed port policy `%s'\n"),
1292 port_policy);
1293 return GNUNET_SYSERR;
1294}
1295
1296
1297struct GNUNET_STRINGS_IPv4NetworkPolicy *
1298GNUNET_STRINGS_parse_ipv4_policy (const char *routeListX)
1299{
1300 size_t count;
1301 size_t len;
1302 size_t pos;
1303 unsigned int temps[8];
1304 struct GNUNET_STRINGS_IPv4NetworkPolicy *result;
1305 char *routeList;
1306
1307 if (NULL == routeListX)
1308 return NULL;
1309 len = strlen (routeListX);
1310 if (0 == len)
1311 return NULL;
1312 routeList = GNUNET_strdup (routeListX);
1313 count = 0;
1314 for (size_t i = 0; i < len; i++)
1315 if (routeList[i] == ';')
1316 count++;
1317 GNUNET_assert (count < SIZE_MAX);
1318 result = GNUNET_new_array (count + 1,
1319 struct GNUNET_STRINGS_IPv4NetworkPolicy);
1320 pos = 0;
1321 for (size_t i = 0; i < count; i++)
1322 {
1323 size_t colon;
1324 size_t end;
1325 char dummy;
1326
1327 for (colon = pos; ':' != routeList[colon]; colon++)
1328 if ((';' == routeList[colon]) || ('\0' == routeList[colon]))
1329 break;
1330 for (end = colon; ';' != routeList[end]; end++)
1331 if ('\0' == routeList[end])
1332 break;
1333 if ('\0' == routeList[end])
1334 break;
1335 routeList[end] = '\0';
1336 if (':' == routeList[colon])
1337 {
1338 routeList[colon] = '\0';
1339 if (GNUNET_OK != parse_port_policy (&routeList[colon + 1], &result[i].pp))
1340 break;
1341 }
1342 if (8 ==
1343 sscanf (&routeList[pos],
1344 "%u.%u.%u.%u/%u.%u.%u.%u%c",
1345 &temps[0],
1346 &temps[1],
1347 &temps[2],
1348 &temps[3],
1349 &temps[4],
1350 &temps[5],
1351 &temps[6],
1352 &temps[7],
1353 &dummy))
1354 {
1355 for (unsigned int j = 0; j < 8; j++)
1356 if (temps[j] > 0xFF)
1357 {
1358 LOG (GNUNET_ERROR_TYPE_WARNING,
1359 _ ("Invalid format for IP: `%s'\n"),
1360 &routeList[pos]);
1361 GNUNET_free (result);
1362 GNUNET_free (routeList);
1363 return NULL;
1364 }
1365 result[i].network.s_addr = htonl ((temps[0] << 24) + (temps[1] << 16)
1366 + (temps[2] << 8) + temps[3]);
1367 result[i].netmask.s_addr = htonl ((temps[4] << 24) + (temps[5] << 16)
1368 + (temps[6] << 8) + temps[7]);
1369 pos = end + 1;
1370 continue;
1371 }
1372
1373 /* try second notation */
1374 {
1375 unsigned int slash;
1376
1377 if (5 ==
1378 sscanf (&routeList[pos],
1379 "%u.%u.%u.%u/%u%c",
1380 &temps[0],
1381 &temps[1],
1382 &temps[2],
1383 &temps[3],
1384 &slash,
1385 &dummy))
1386 {
1387 for (unsigned int j = 0; j < 4; j++)
1388 if (temps[j] > 0xFF)
1389 {
1390 LOG (GNUNET_ERROR_TYPE_WARNING,
1391 _ ("Invalid format for IP: `%s'\n"),
1392 &routeList[pos]);
1393 GNUNET_free (result);
1394 GNUNET_free (routeList);
1395 return NULL;
1396 }
1397 result[i].network.s_addr = htonl ((temps[0] << 24) + (temps[1] << 16)
1398 + (temps[2] << 8) + temps[3]);
1399 if (slash <= 32)
1400 {
1401 result[i].netmask.s_addr = 0;
1402 while (slash > 0)
1403 {
1404 result[i].netmask.s_addr =
1405 (result[i].netmask.s_addr >> 1) + 0x80000000;
1406 slash--;
1407 }
1408 result[i].netmask.s_addr = htonl (result[i].netmask.s_addr);
1409 pos = end + 1;
1410 continue;
1411 }
1412 else
1413 {
1414 LOG (GNUNET_ERROR_TYPE_WARNING,
1415 _ (
1416 "Invalid network notation ('/%d' is not legal in IPv4 CIDR)."),
1417 slash);
1418 GNUNET_free (result);
1419 GNUNET_free (routeList);
1420 return NULL; /* error */
1421 }
1422 }
1423 }
1424
1425 /* try third notation */
1426 if (4 ==
1427 sscanf (&routeList[pos],
1428 "%u.%u.%u.%u%c",
1429 &temps[0],
1430 &temps[1],
1431 &temps[2],
1432 &temps[3],
1433 &dummy))
1434 {
1435 for (unsigned int j = 0; j < 4; j++)
1436 if (temps[j] > 0xFF)
1437 {
1438 LOG (GNUNET_ERROR_TYPE_WARNING,
1439 _ ("Invalid format for IP: `%s'\n"),
1440 &routeList[pos]);
1441 GNUNET_free (result);
1442 GNUNET_free (routeList);
1443 return NULL;
1444 }
1445 result[i].network.s_addr = htonl ((temps[0] << 24) + (temps[1] << 16)
1446 + (temps[2] << 8) + temps[3]);
1447 result[i].netmask.s_addr = htonl (0xffffffff); /* yeah, the htonl is useless */
1448 pos = end + 1;
1449 continue;
1450 }
1451 LOG (GNUNET_ERROR_TYPE_WARNING,
1452 _ ("Invalid format for IP: `%s'\n"),
1453 &routeList[pos]);
1454 GNUNET_free (result);
1455 GNUNET_free (routeList);
1456 return NULL; /* error */
1457 }
1458 if (pos < strlen (routeList))
1459 {
1460 LOG (GNUNET_ERROR_TYPE_WARNING,
1461 _ ("Invalid format: `%s'\n"),
1462 &routeListX[pos]);
1463 GNUNET_free (result);
1464 GNUNET_free (routeList);
1465 return NULL; /* oops */
1466 }
1467 GNUNET_free (routeList);
1468 return result; /* ok */
1469}
1470
1471
1472struct GNUNET_STRINGS_IPv6NetworkPolicy *
1473GNUNET_STRINGS_parse_ipv6_policy (const char *routeListX)
1474{
1475 size_t count;
1476 size_t len;
1477 size_t pos;
1478 int ret;
1479 char *routeList;
1480 struct GNUNET_STRINGS_IPv6NetworkPolicy *result;
1481 unsigned int off;
1482
1483 if (NULL == routeListX)
1484 return NULL;
1485 len = strlen (routeListX);
1486 if (0 == len)
1487 return NULL;
1488 routeList = GNUNET_strdup (routeListX);
1489 count = 0;
1490 for (size_t j = 0; j < len; j++)
1491 if (';' == routeList[j])
1492 count++;
1493 if (';' != routeList[len - 1])
1494 {
1495 LOG (GNUNET_ERROR_TYPE_WARNING,
1496 _ ("Invalid network notation (does not end with ';': `%s')\n"),
1497 routeList);
1498 GNUNET_free (routeList);
1499 return NULL;
1500 }
1501 GNUNET_assert (count < UINT_MAX);
1502 result = GNUNET_new_array (count + 1,
1503 struct GNUNET_STRINGS_IPv6NetworkPolicy);
1504 pos = 0;
1505 for (size_t i = 0; i < count; i++)
1506 {
1507 size_t start;
1508 size_t slash;
1509
1510 start = pos;
1511 while (';' != routeList[pos])
1512 pos++;
1513 slash = pos;
1514 while ( (slash > start) &&
1515 (routeList[slash] != '/') )
1516 slash--;
1517 if (slash <= start)
1518 {
1519 memset (&result[i].netmask,
1520 0xFF,
1521 sizeof(struct in6_addr));
1522 slash = pos;
1523 }
1524 else
1525 {
1526 size_t colon;
1527
1528 routeList[pos] = '\0';
1529 for (colon = pos; ':' != routeList[colon]; colon--)
1530 if ('/' == routeList[colon])
1531 break;
1532 if (':' == routeList[colon])
1533 {
1534 routeList[colon] = '\0';
1535 if (GNUNET_OK !=
1536 parse_port_policy (&routeList[colon + 1],
1537 &result[i].pp))
1538 {
1539 GNUNET_free (result);
1540 GNUNET_free (routeList);
1541 return NULL;
1542 }
1543 }
1544 ret = inet_pton (AF_INET6,
1545 &routeList[slash + 1],
1546 &result[i].netmask);
1547 if (ret <= 0)
1548 {
1549 char dummy;
1550 unsigned int bits;
1551 int save = errno;
1552
1553 if ( (1 != sscanf (&routeList[slash + 1],
1554 "%u%c",
1555 &bits,
1556 &dummy)) ||
1557 (bits > 128))
1558 {
1559 if (0 == ret)
1560 {
1561 LOG (GNUNET_ERROR_TYPE_WARNING,
1562 _ ("Wrong format `%s' for netmask\n"),
1563 &routeList[slash]);
1564 }
1565 else
1566 {
1567 errno = save;
1568 LOG_STRERROR (GNUNET_ERROR_TYPE_WARNING,
1569 "inet_pton");
1570 }
1571 GNUNET_free (result);
1572 GNUNET_free (routeList);
1573 return NULL;
1574 }
1575 off = 0;
1576 while (bits > 8)
1577 {
1578 result[i].netmask.s6_addr[off++] = 0xFF;
1579 bits -= 8;
1580 }
1581 while (bits > 0)
1582 {
1583 result[i].netmask.s6_addr[off] =
1584 (result[i].netmask.s6_addr[off] >> 1) + 0x80;
1585 bits--;
1586 }
1587 }
1588 }
1589 routeList[slash] = '\0';
1590 ret = inet_pton (AF_INET6,
1591 &routeList[start],
1592 &result[i].network);
1593 if (ret <= 0)
1594 {
1595 if (0 == ret)
1596 LOG (GNUNET_ERROR_TYPE_WARNING,
1597 _ ("Wrong format `%s' for network\n"),
1598 &routeList[slash + 1]);
1599 else
1600 LOG_STRERROR (GNUNET_ERROR_TYPE_ERROR,
1601 "inet_pton");
1602 GNUNET_free (result);
1603 GNUNET_free (routeList);
1604 return NULL;
1605 }
1606 pos++;
1607 }
1608 GNUNET_free (routeList);
1609 return result;
1610}
1611
1612
1613/** ******************** Base64 encoding ***********/
1614
1615#define FILLCHAR '='
1616static char *cvt = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
1617 "abcdefghijklmnopqrstuvwxyz"
1618 "0123456789+/";
1619
1620
1621size_t
1622GNUNET_STRINGS_base64_encode (const void *in,
1623 size_t len,
1624 char **output)
1625{
1626 const unsigned char *data = in;
1627 size_t ret;
1628 char *opt;
1629
1630 ret = 0;
1631 GNUNET_assert (len < SIZE_MAX / 4);
1632 opt = GNUNET_malloc (2 + (len * 4 / 3) + 8);
1633 for (size_t i = 0; i < len; ++i)
1634 {
1635 char c;
1636
1637 c = (data[i] >> 2) & 0x3f;
1638 opt[ret++] = cvt[(int) c];
1639 c = (data[i] << 4) & 0x3f;
1640 if (++i < len)
1641 c |= (data[i] >> 4) & 0x0f;
1642 opt[ret++] = cvt[(int) c];
1643 if (i < len)
1644 {
1645 c = (data[i] << 2) & 0x3f;
1646 if (++i < len)
1647 c |= (data[i] >> 6) & 0x03;
1648 opt[ret++] = cvt[(int) c];
1649 }
1650 else
1651 {
1652 ++i;
1653 opt[ret++] = FILLCHAR;
1654 }
1655 if (i < len)
1656 {
1657 c = data[i] & 0x3f;
1658 opt[ret++] = cvt[(int) c];
1659 }
1660 else
1661 {
1662 opt[ret++] = FILLCHAR;
1663 }
1664 }
1665 *output = opt;
1666 return ret;
1667}
1668
1669
1670size_t
1671GNUNET_STRINGS_base64url_encode (const void *in,
1672 size_t len,
1673 char **output)
1674{
1675 char *enc;
1676 size_t pos;
1677
1678 GNUNET_STRINGS_base64_encode (in,
1679 len,
1680 output);
1681 enc = *output;
1682 /* Replace with correct characters for base64url */
1683 pos = 0;
1684 while ('\0' != enc[pos])
1685 {
1686 if ('+' == enc[pos])
1687 enc[pos] = '-';
1688 if ('/' == enc[pos])
1689 enc[pos] = '_';
1690 if ('=' == enc[pos])
1691 {
1692 enc[pos] = '\0';
1693 break;
1694 }
1695 pos++;
1696 }
1697 return strlen (enc);
1698}
1699
1700
1701#define cvtfind(a) \
1702 ((((a) >= 'A') && ((a) <= 'Z')) \
1703 ? (a) - 'A' \
1704 : (((a) >= 'a') && ((a) <= 'z')) \
1705 ? (a) - 'a' + 26 \
1706 : (((a) >= '0') && ((a) <= '9')) \
1707 ? (a) - '0' + 52 \
1708 : ((a) == '+') ? 62 : ((a) == '/') ? 63 : -1)
1709
1710
1711#define CHECK_CRLF \
1712 while ( (data[i] == '\r') || (data[i] == '\n') ) \
1713 { \
1714 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG | GNUNET_ERROR_TYPE_BULK, \
1715 "ignoring CR/LF\n"); \
1716 i++; \
1717 if (i >= len) { \
1718 goto END; \
1719 } \
1720 }
1721
1722
1723size_t
1724GNUNET_STRINGS_base64_decode (const char *data,
1725 size_t len,
1726 void **out)
1727{
1728 unsigned char *output;
1729 size_t ret = 0;
1730
1731 GNUNET_assert (len / 3 < SIZE_MAX);
1732 output = GNUNET_malloc ((len * 3 / 4) + 8);
1733 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1734 "base64_decode decoding len=%d\n",
1735 (int) len);
1736 for (size_t i = 0; i < len; ++i)
1737 {
1738 unsigned char c;
1739 unsigned char c1;
1740
1741 CHECK_CRLF;
1742 if (FILLCHAR == data[i])
1743 break;
1744 c = (unsigned char) cvtfind (data[i]);
1745 ++i;
1746 CHECK_CRLF;
1747 c1 = (unsigned char) cvtfind (data[i]);
1748 c = (c << 2) | ((c1 >> 4) & 0x3);
1749 output[ret++] = c;
1750 if (++i < len)
1751 {
1752 CHECK_CRLF;
1753 c = data[i];
1754 if (FILLCHAR == c)
1755 break;
1756 c = (unsigned char) cvtfind (c);
1757 c1 = ((c1 << 4) & 0xf0) | ((c >> 2) & 0xf);
1758 output[ret++] = c1;
1759 }
1760 if (++i < len)
1761 {
1762 CHECK_CRLF;
1763 c1 = data[i];
1764 if (FILLCHAR == c1)
1765 break;
1766
1767 c1 = (unsigned char) cvtfind (c1);
1768 c = ((c << 6) & 0xc0) | c1;
1769 output[ret++] = c;
1770 }
1771 }
1772END:
1773 *out = output;
1774 return ret;
1775}
1776
1777
1778#undef CHECK_CRLF
1779
1780
1781size_t
1782GNUNET_STRINGS_base64url_decode (const char *data,
1783 size_t len,
1784 void **out)
1785{
1786 char *s;
1787 int padding;
1788 size_t ret;
1789
1790 /* make enough space for padding */
1791 GNUNET_assert (len < SIZE_MAX - 3);
1792 s = GNUNET_malloc (len + 3);
1793 memcpy (s,
1794 data,
1795 len);
1796 for (size_t i = 0; i < strlen (s); i++)
1797 {
1798 if (s[i] == '-')
1799 s[i] = '+';
1800 if (s[i] == '_')
1801 s[i] = '/';
1802 }
1803 padding = len % 4;
1804 switch (padding) // Pad with trailing '='s
1805 {
1806 case 0:
1807 break; // No pad chars in this case
1808 case 2:
1809 memcpy (&s[len],
1810 "==",
1811 2);
1812 len += 2;
1813 break; // Two pad chars
1814 case 3:
1815 s[len] = '=';
1816 len++;
1817 break; // One pad char
1818 default:
1819 GNUNET_assert (0);
1820 break;
1821 }
1822 ret = GNUNET_STRINGS_base64_decode (s,
1823 len,
1824 out);
1825 GNUNET_free (s);
1826 return ret;
1827}
1828
1829
1830size_t
1831GNUNET_STRINGS_urldecode (const char *data,
1832 size_t len,
1833 char **out)
1834{
1835 const char *rpos = data;
1836 *out = GNUNET_malloc (len + 1); /* output should always fit into input */
1837 char *wpos = *out;
1838 size_t resl = 0;
1839
1840 while ( ('\0' != *rpos) &&
1841 (data + len != rpos) )
1842 {
1843 unsigned int num;
1844 switch (*rpos)
1845 {
1846 case '%':
1847 if (rpos + 3 > data + len)
1848 {
1849 GNUNET_break_op (0);
1850 GNUNET_free (*out);
1851 return 0;
1852 }
1853 if (1 != sscanf (rpos + 1,
1854 "%2x",
1855 &num))
1856 {
1857 /* Invalid URL encoding, try to continue anyway */
1858 GNUNET_break_op (0);
1859 *wpos = *rpos;
1860 wpos++;
1861 resl++;
1862 rpos++;
1863 break;
1864 }
1865 *wpos = (char) ((unsigned char) num);
1866 wpos++;
1867 resl++;
1868 rpos += 3;
1869 break;
1870 /* TODO: add bad sequence handling */
1871 /* intentional fall through! */
1872 default:
1873 *wpos = *rpos;
1874 wpos++;
1875 resl++;
1876 rpos++;
1877 }
1878 }
1879 *wpos = '\0'; /* add 0-terminator */
1880 return resl;
1881}
1882
1883
1884size_t
1885GNUNET_STRINGS_urlencode (size_t len,
1886 const char data[static len],
1887 char **out)
1888{
1889 struct GNUNET_Buffer buf = { 0 };
1890 const uint8_t *i8 = (uint8_t *) data;
1891 const uint8_t *end = (uint8_t *) (data + len);
1892
1893 while (end != i8)
1894 {
1895 if (0 == *i8)
1896 {
1897 /* invalid UTF-8 (or bad @a len): fail */
1898 GNUNET_break (0);
1899 GNUNET_buffer_clear (&buf);
1900 return 0;
1901 }
1902 if (0 == (0x80 & *i8))
1903 {
1904 /* traditional ASCII */
1905 if (isalnum (*i8) ||
1906 (*i8 == '-') ||
1907 (*i8 == '_') ||
1908 (*i8 == '.') ||
1909 (*i8 == '~') )
1910 GNUNET_buffer_write (&buf,
1911 (const char*) i8,
1912 1);
1913 else if (*i8 == ' ')
1914 GNUNET_buffer_write (&buf,
1915 "+",
1916 1);
1917 else
1918 GNUNET_buffer_write_fstr (&buf,
1919 "%%%X%X",
1920 *i8 >> 4,
1921 *i8 & 15);
1922 i8++;
1923 continue;
1924 }
1925 if (0x80 + 0x40 == ((0x80 + 0x40 + 0x20) & *i8))
1926 {
1927 /* 2-byte value, percent-encode */
1928 GNUNET_buffer_write_fstr (&buf,
1929 "%%%X%X",
1930 *i8 >> 4,
1931 *i8 & 15);
1932 i8++;
1933 if ( (end == i8) ||
1934 (0 == *i8) )
1935 {
1936 /* invalid UTF-8 (or bad @a len): fail */
1937 GNUNET_break (0);
1938 GNUNET_buffer_clear (&buf);
1939 return 0;
1940 }
1941 GNUNET_buffer_write_fstr (&buf,
1942 "%%%X%X",
1943 *i8 >> 4,
1944 *i8 & 15);
1945 i8++;
1946 continue;
1947 }
1948 if (0x80 + 0x40 + 0x20 == ((0x80 + 0x40 + 0x20 + 0x10) & *i8))
1949 {
1950 /* 3-byte value, percent-encode */
1951 for (unsigned int i = 0; i<3; i++)
1952 {
1953 if ( (end == i8) ||
1954 (0 == *i8) )
1955 {
1956 /* invalid UTF-8 (or bad @a len): fail */
1957 GNUNET_break (0);
1958 GNUNET_buffer_clear (&buf);
1959 return 0;
1960 }
1961 GNUNET_buffer_write_fstr (&buf,
1962 "%%%X%X",
1963 *i8 >> 4,
1964 *i8 & 15);
1965 i8++;
1966 }
1967 continue;
1968 }
1969 if (0x80 + 0x40 + 0x20 + 0x10 == ((0x80 + 0x40 + 0x20 + 0x10 + 0x08) & *i8))
1970 {
1971 /* 4-byte value, percent-encode */
1972 for (unsigned int i = 0; i<4; i++)
1973 {
1974 if ( (end == i8) ||
1975 (0 == *i8) )
1976 {
1977 /* invalid UTF-8 (or bad @a len): fail */
1978 GNUNET_break (0);
1979 GNUNET_buffer_clear (&buf);
1980 return 0;
1981 }
1982 GNUNET_buffer_write_fstr (&buf,
1983 "%%%X%X",
1984 *i8 >> 4,
1985 *i8 & 15);
1986 i8++;
1987 }
1988 continue;
1989 }
1990 if (0x80 + 0x40 + 0x20 + 0x10 + 0x08 == ((0x80 + 0x40 + 0x20 + 0x10 + 0x08
1991 + 0x04) & *i8))
1992 {
1993 /* 5-byte value, percent-encode (outside of UTF-8 modern standard, but so what) */
1994 for (unsigned int i = 0; i<5; i++)
1995 {
1996 if ( (end == i8) ||
1997 (0 == *i8) )
1998 {
1999 /* invalid UTF-8 (or bad @a len): fail */
2000 GNUNET_break (0);
2001 GNUNET_buffer_clear (&buf);
2002 return 0;
2003 }
2004 GNUNET_buffer_write_fstr (&buf,
2005 "%%%X%X",
2006 *i8 >> 4,
2007 *i8 & 15);
2008 i8++;
2009 }
2010 continue;
2011 }
2012 if (0x80 + 0x40 + 0x20 + 0x10 + 0x08 + 0x04 == ((0x80 + 0x40 + 0x20 + 0x10
2013 + 0x08 + 0x04 + 0x02)
2014 & *i8))
2015 {
2016 /* 6-byte value, percent-encode (outside of UTF-8 modern standard, but so what) */
2017 for (unsigned int i = 0; i<6; i++)
2018 {
2019 if ( (end == i8) ||
2020 (0 == *i8) )
2021 {
2022 /* invalid UTF-8 (or bad @a len): fail */
2023 GNUNET_break (0);
2024 GNUNET_buffer_clear (&buf);
2025 return 0;
2026 }
2027 GNUNET_buffer_write_fstr (&buf,
2028 "%%%X%X",
2029 *i8 >> 4,
2030 *i8 & 15);
2031 i8++;
2032 }
2033 continue;
2034 }
2035 /* really, really invalid UTF-8: fail */
2036 GNUNET_break (0);
2037 GNUNET_buffer_clear (&buf);
2038 return 0;
2039 }
2040 *out = GNUNET_buffer_reap_str (&buf);
2041 return strlen (*out);
2042}
2043
2044
2045/**
2046 * Sometimes we use the binary name to determine which specific
2047 * test to run. In those cases, the string after the last "_"
2048 * in 'argv[0]' specifies a string that determines the configuration
2049 * file or plugin to use.
2050 *
2051 * This function returns the respective substring, taking care
2052 * of issues such as binaries ending in '.exe' on W32.
2053 *
2054 * @param argv0 the name of the binary
2055 * @return string between the last '_' and the '.exe' (or the end of the string),
2056 * NULL if argv0 has no '_'
2057 */
2058char *
2059GNUNET_STRINGS_get_suffix_from_binary_name (const char *argv0)
2060{
2061 const char *ret;
2062 const char *dot;
2063
2064 ret = strrchr (argv0, '_');
2065 if (NULL == ret)
2066 return NULL;
2067 ret++; /* skip underscore */
2068 dot = strchr (ret,
2069 '.');
2070 if (NULL != dot)
2071 return GNUNET_strndup (ret,
2072 dot - ret);
2073 return GNUNET_strdup (ret);
2074}
2075
2076
2077/* end of strings.c */
diff --git a/src/lib/util/test_bio.c b/src/lib/util/test_bio.c
new file mode 100644
index 000000000..b407eccfe
--- /dev/null
+++ b/src/lib/util/test_bio.c
@@ -0,0 +1,383 @@
1/*
2 This file is part of GNUnet.
3 Copyright (C) 2009, 2023 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/test_bio.c
22 * @brief testcase for the buffered IO module
23 * @author Ji Lu
24 */
25#include "platform.h"
26#include "gnunet_util_lib.h"
27#define TESTSTRING "testString"
28#define TESTNUMBER64 ((int64_t) 100000L)
29
30
31static int
32test_normal_rw (void)
33{
34 struct GNUNET_BIO_WriteHandle *wh;
35 struct GNUNET_BIO_ReadHandle *rh;
36 void *buffer;
37 size_t buffer_size = 0;
38 char *filename = GNUNET_DISK_mktemp ("gnunet-bio");
39 char *rString = NULL;
40 int64_t wNum = TESTNUMBER64;
41 int64_t rNum = 0;
42 struct GNUNET_BIO_WriteSpec ws[] = {
43 GNUNET_BIO_write_spec_string ("test-normal-rw-string",
44 TESTSTRING),
45 GNUNET_BIO_write_spec_int64 ("test-normal-rw-int64",
46 &wNum),
47 GNUNET_BIO_write_spec_end (),
48 };
49 struct GNUNET_BIO_ReadSpec rs[] = {
50 GNUNET_BIO_read_spec_string ("test-normal-rw-string",
51 &rString,
52 200),
53 GNUNET_BIO_read_spec_int64 ("test-normal-rw-int64",
54 &rNum),
55 GNUNET_BIO_read_spec_end (),
56 };
57
58 /* I/O on file */
59 wh = GNUNET_BIO_write_open_file (filename);
60 GNUNET_assert (NULL != wh);
61 GNUNET_assert (GNUNET_OK ==
62 GNUNET_BIO_write_spec_commit (wh, ws));
63 GNUNET_assert (GNUNET_OK ==
64 GNUNET_BIO_write_close (wh, NULL));
65
66 rh = GNUNET_BIO_read_open_file (filename);
67 GNUNET_assert (NULL != rh);
68 GNUNET_assert (GNUNET_OK ==
69 GNUNET_BIO_read_spec_commit (rh,
70 rs));
71 GNUNET_assert (GNUNET_OK ==
72 GNUNET_BIO_read_close (rh,
73 NULL));
74 GNUNET_assert (0 == strcmp (TESTSTRING,
75 rString));
76 GNUNET_assert (wNum == rNum);
77 GNUNET_free (rString);
78 GNUNET_assert (GNUNET_OK ==
79 GNUNET_DISK_directory_remove (filename));
80 GNUNET_free (filename);
81
82 /* I/O on buffer */
83 wh = GNUNET_BIO_write_open_buffer ();
84 GNUNET_assert (NULL != wh);
85 GNUNET_assert (GNUNET_OK ==
86 GNUNET_BIO_write_spec_commit (wh,
87 ws));
88 GNUNET_assert (GNUNET_OK ==
89 GNUNET_BIO_get_buffer_contents (wh,
90 NULL,
91 &buffer,
92 &buffer_size));
93 GNUNET_assert (GNUNET_OK ==
94 GNUNET_BIO_write_close (wh,
95 NULL));
96
97 rh = GNUNET_BIO_read_open_buffer (buffer,
98 buffer_size);
99 GNUNET_assert (NULL != rh);
100 GNUNET_assert (GNUNET_OK ==
101 GNUNET_BIO_read_spec_commit (rh,
102 rs));
103 GNUNET_assert (GNUNET_OK ==
104 GNUNET_BIO_read_close (rh,
105 NULL));
106 GNUNET_assert (0 == strcmp (TESTSTRING,
107 rString));
108 GNUNET_assert (wNum == rNum);
109 GNUNET_free (rString);
110 GNUNET_free (buffer);
111
112 return 0;
113}
114
115
116static int
117test_nullstring_rw (void)
118{
119 struct GNUNET_BIO_WriteHandle *wh;
120 struct GNUNET_BIO_ReadHandle *rh;
121 char *filename = GNUNET_DISK_mktemp ("gnunet_bio");
122 char *rString = "not null";
123
124 wh = GNUNET_BIO_write_open_file (filename);
125 GNUNET_assert (NULL != wh);
126 GNUNET_assert (GNUNET_OK ==
127 GNUNET_BIO_write_string (wh,
128 "test-nullstring-rw",
129 NULL));
130 GNUNET_assert (GNUNET_OK ==
131 GNUNET_BIO_write_close (wh,
132 NULL));
133 rh = GNUNET_BIO_read_open_file (filename);
134 GNUNET_assert (NULL != rh);
135 GNUNET_assert (GNUNET_OK ==
136 GNUNET_BIO_read_string (rh,
137 "test-nullstring-rw",
138 &rString,
139 200));
140 GNUNET_assert (GNUNET_OK ==
141 GNUNET_BIO_read_close (rh,
142 NULL));
143 GNUNET_assert (NULL == rString);
144 GNUNET_assert (GNUNET_OK ==
145 GNUNET_DISK_directory_remove (filename));
146 GNUNET_free (filename);
147 return 0;
148}
149
150
151static int
152test_emptystring_rw (void)
153{
154 struct GNUNET_BIO_WriteHandle *wh;
155 struct GNUNET_BIO_ReadHandle *rh;
156 char *filename = GNUNET_DISK_mktemp ("gnunet_bio");
157 char *rString = NULL;
158
159 wh = GNUNET_BIO_write_open_file (filename);
160 GNUNET_assert (NULL != wh);
161 GNUNET_assert (GNUNET_OK ==
162 GNUNET_BIO_write_string (wh,
163 "test-emptystring-rw",
164 ""));
165 GNUNET_assert (GNUNET_OK ==
166 GNUNET_BIO_write_close (wh,
167 NULL));
168
169 rh = GNUNET_BIO_read_open_file (filename);
170 GNUNET_assert (NULL != rh);
171 GNUNET_assert (GNUNET_OK ==
172 GNUNET_BIO_read_string (rh,
173 "test-emptystring-rw",
174 &rString, 200));
175 GNUNET_assert (GNUNET_OK ==
176 GNUNET_BIO_read_close (rh,
177 NULL));
178 GNUNET_free (rString);
179 GNUNET_assert (GNUNET_OK ==
180 GNUNET_DISK_directory_remove (filename));
181 GNUNET_free (filename);
182 return 0;
183}
184
185
186static int
187test_bigstring_rw (void)
188{
189 struct GNUNET_BIO_WriteHandle *wh;
190 struct GNUNET_BIO_ReadHandle *rh;
191 char *filename = GNUNET_DISK_mktemp ("gnunet_bio");
192 char *rString = NULL;
193
194 wh = GNUNET_BIO_write_open_file (filename);
195 GNUNET_assert (NULL != wh);
196 GNUNET_assert (GNUNET_OK ==
197 GNUNET_BIO_write_string (wh,
198 "test-bigstring-rw",
199 TESTSTRING));
200 GNUNET_assert (GNUNET_OK ==
201 GNUNET_BIO_write_close (wh,
202 NULL));
203 rh = GNUNET_BIO_read_open_file (filename);
204 GNUNET_assert (NULL != rh);
205 GNUNET_assert (GNUNET_SYSERR ==
206 GNUNET_BIO_read_string (rh,
207 "test-bigstring-rw",
208 &rString, 1));
209 GNUNET_assert (GNUNET_SYSERR ==
210 GNUNET_BIO_read_close (rh,
211 NULL));
212 GNUNET_assert (NULL == rString);
213 GNUNET_assert (GNUNET_OK ==
214 GNUNET_DISK_directory_remove (filename));
215 GNUNET_free (filename);
216 return 0;
217}
218
219
220static int
221test_directory_r (void)
222{
223#ifdef LINUX
224 struct GNUNET_BIO_ReadHandle *rh;
225 char rString[200];
226
227 rh = GNUNET_BIO_read_open_file ("/dev");
228 GNUNET_assert (NULL != rh);
229 GNUNET_assert (GNUNET_SYSERR ==
230 GNUNET_BIO_read (rh,
231 "test-directory-r",
232 rString,
233 sizeof (rString)));
234 GNUNET_assert (GNUNET_SYSERR ==
235 GNUNET_BIO_read_close (rh,
236 NULL));
237#endif
238 return 0;
239}
240
241
242static int
243test_nullfile_rw (void)
244{
245 static char filename[102401];
246 struct GNUNET_BIO_WriteHandle *wh;
247 struct GNUNET_BIO_ReadHandle *rh;
248
249 memset (filename, 'a', sizeof (filename));
250 filename[sizeof (filename) - 1] = '\0';
251
252 GNUNET_log_skip (1, GNUNET_NO);
253 wh = GNUNET_BIO_write_open_file (filename);
254 GNUNET_log_skip (0, GNUNET_YES);
255 GNUNET_assert (NULL == wh);
256
257 GNUNET_log_skip (1, GNUNET_NO);
258 rh = GNUNET_BIO_read_open_file (filename);
259 GNUNET_log_skip (0, GNUNET_YES);
260 GNUNET_assert (NULL == rh);
261
262 return 0;
263}
264
265
266static int
267test_fullfile_rw (void)
268{
269#ifdef LINUX
270 /* /dev/full doesn't exist on every platform */
271 struct GNUNET_BIO_WriteHandle *wh;
272 struct GNUNET_BIO_ReadHandle *rh;
273 char *rString = NULL;
274 char rResult[200];
275
276 struct GNUNET_BIO_WriteSpec ws[] = {
277 GNUNET_BIO_write_spec_object ("test-fullfile-rw-bytes",
278 TESTSTRING,
279 strlen (TESTSTRING)),
280 GNUNET_BIO_write_spec_string ("test-fullfile-rw-string",
281 TESTSTRING),
282 GNUNET_BIO_write_spec_end (),
283 };
284
285 struct GNUNET_BIO_ReadSpec rs[] = {
286 GNUNET_BIO_read_spec_object ("test-fullfile-rw-bytes",
287 rResult,
288 sizeof (rResult)),
289 GNUNET_BIO_read_spec_string ("test-fullfile-rw-string",
290 &rString,
291 200),
292 GNUNET_BIO_read_spec_end (),
293 };
294
295 wh = GNUNET_BIO_write_open_file ("/dev/full");
296 GNUNET_assert (NULL != wh);
297 GNUNET_assert (GNUNET_SYSERR ==
298 GNUNET_BIO_write_spec_commit (wh, ws));
299 GNUNET_assert (GNUNET_SYSERR ==
300 GNUNET_BIO_write_close (wh, NULL));
301
302 rh = GNUNET_BIO_read_open_file ("/dev/null");
303 GNUNET_assert (NULL != rh);
304 GNUNET_assert (GNUNET_SYSERR ==
305 GNUNET_BIO_read_spec_commit (rh, rs));
306 GNUNET_assert (GNUNET_SYSERR ==
307 GNUNET_BIO_read_close (rh, NULL));
308
309 GNUNET_assert (NULL == rString);
310#endif
311 return 0;
312}
313
314
315static int
316test_fakestring_rw (void)
317{
318 struct GNUNET_BIO_WriteHandle *wh;
319 struct GNUNET_BIO_ReadHandle *rh;
320 char *filename = GNUNET_DISK_mktemp ("gnunet_bio");
321 char *rString = NULL;
322
323 wh = GNUNET_BIO_write_open_file (filename);
324 GNUNET_assert (NULL != wh);
325 GNUNET_assert (GNUNET_OK ==
326 GNUNET_BIO_write_int32 (wh,
327 "test-fakestring-rw-int32",
328 2));
329 GNUNET_assert (GNUNET_OK ==
330 GNUNET_BIO_write_close (wh,
331 NULL));
332
333 rh = GNUNET_BIO_read_open_file (filename);
334 GNUNET_assert (NULL != rh);
335 GNUNET_assert (GNUNET_SYSERR ==
336 GNUNET_BIO_read_string (rh,
337 "test-fakestring-rw-string",
338 &rString,
339 200));
340 GNUNET_assert (GNUNET_SYSERR ==
341 GNUNET_BIO_read_close (rh,
342 NULL));
343 GNUNET_assert (NULL == rString);
344 GNUNET_assert (GNUNET_OK ==
345 GNUNET_DISK_directory_remove (filename));
346 GNUNET_free (filename);
347 return 0;
348}
349
350
351static int
352check_string_rw (void)
353{
354 GNUNET_assert (0 == test_nullstring_rw ());
355 GNUNET_assert (0 == test_emptystring_rw ());
356 GNUNET_assert (0 == test_bigstring_rw ());
357 GNUNET_assert (0 == test_fakestring_rw ());
358 return 0;
359}
360
361
362static int
363check_file_rw (void)
364{
365 GNUNET_assert (0 == test_normal_rw ());
366 GNUNET_assert (0 == test_nullfile_rw ());
367 GNUNET_assert (0 == test_fullfile_rw ());
368 GNUNET_assert (0 == test_directory_r ());
369 return 0;
370}
371
372
373int
374main (int argc, char *argv[])
375{
376 GNUNET_log_setup ("test-bio", "WARNING", NULL);
377 GNUNET_assert (0 == check_file_rw ());
378 GNUNET_assert (0 == check_string_rw ());
379 return 0;
380}
381
382
383/* end of test_bio.c */
diff --git a/src/lib/util/test_child_management.c b/src/lib/util/test_child_management.c
new file mode 100644
index 000000000..90cc74c72
--- /dev/null
+++ b/src/lib/util/test_child_management.c
@@ -0,0 +1,178 @@
1/*
2 This file is part of GNUnet
3 Copyright (C) 2014-2021 GNUnet e.V.
4
5 GNUNET is free software; you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as
7 published by the Free Software Foundation; either version 3, 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
13 GNU General Public License for more details.
14
15 You should have received a copy of the GNU General Public
16 License along with GNUNET; see the file COPYING. If not, see
17 <http://www.gnu.org/licenses/>
18*/
19
20/**
21 * @file lib/test_child_management.c
22 * @brief testcase to test the child management
23 * @author Christian Grothoff
24 * @author Dominik Meister
25 */
26
27#include "platform.h"
28#include "gnunet_util_lib.h"
29
30
31static struct GNUNET_ChildWaitHandle *cwh;
32
33static int global_ret;
34
35static struct GNUNET_OS_Process *pid;
36
37
38static void
39child_completed_callback (void *cls,
40 enum GNUNET_OS_ProcessStatusType type,
41 long unsigned int exit_code)
42{
43 cwh = NULL;
44 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
45 "Process extided with code: %lu \n",
46 exit_code);
47 FILE *file;
48 char code[9];
49
50 file = fopen ("child_management_test.txt", "r");
51 if (NULL == file)
52 {
53 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
54 "could not find file: child_management_test.txt in %s:%u\n",
55 __FILE__,
56 __LINE__);
57 global_ret = 1;
58 return;
59 }
60 if (0 == fscanf (file,
61 "%8s",
62 code))
63 {
64 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
65 "could not read file: child_management_test.txt in %s:%u\n",
66 __FILE__,
67 __LINE__);
68 global_ret = 1;
69 return;
70 }
71
72 if (0 != strcmp ("12345678", code))
73 {
74 global_ret = 1;
75 return;
76 }
77 GNUNET_OS_process_destroy (pid);
78 pid = NULL;
79 GNUNET_break (0 == unlink ("child_management_test.txt"));
80 GNUNET_SCHEDULER_shutdown ();
81 global_ret = 0;
82}
83
84
85static void
86do_shutdown (void *cls)
87{
88 if (NULL != cwh)
89 {
90 GNUNET_wait_child_cancel (cwh);
91 cwh = NULL;
92 }
93 if (NULL != pid)
94 {
95 GNUNET_assert (0 ==
96 GNUNET_OS_process_kill (pid,
97 SIGKILL));
98 GNUNET_assert (GNUNET_OK ==
99 GNUNET_OS_process_wait (pid));
100 GNUNET_OS_process_destroy (pid);
101 pid = NULL;
102 }
103}
104
105
106static void
107test_child_management (void *cls)
108{
109 const char *command = "./child_management_test.sh";
110 struct GNUNET_DISK_PipeHandle *p;
111 struct GNUNET_DISK_FileHandle *out;
112
113 (void) cls;
114 p = GNUNET_DISK_pipe (GNUNET_DISK_PF_NONE);
115 if (NULL == p)
116 {
117 GNUNET_log_strerror (GNUNET_ERROR_TYPE_ERROR,
118 "pipe");
119 global_ret = 2;
120 return;
121 }
122 pid = GNUNET_OS_start_process (0,
123 p,
124 NULL,
125 NULL,
126 command,
127 command,
128 "1234",
129 "5678",
130 NULL);
131 if (NULL == pid)
132 {
133 GNUNET_log_strerror (GNUNET_ERROR_TYPE_ERROR,
134 "fork");
135 GNUNET_break (GNUNET_OK ==
136 GNUNET_DISK_pipe_close (p));
137 global_ret = 1;
138 return;
139 }
140 GNUNET_break (GNUNET_OK ==
141 GNUNET_DISK_pipe_close_end (p,
142 GNUNET_DISK_PIPE_END_READ));
143 out = GNUNET_DISK_pipe_detach_end (p,
144 GNUNET_DISK_PIPE_END_WRITE);
145 GNUNET_assert (NULL != out);
146 GNUNET_break (GNUNET_OK ==
147 GNUNET_DISK_pipe_close (p));
148
149 GNUNET_SCHEDULER_add_shutdown (&do_shutdown,
150 NULL);
151 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,"Alright");
152 cwh = GNUNET_wait_child (pid,
153 &child_completed_callback,
154 cls);
155 GNUNET_break (NULL != cwh);
156 GNUNET_assert (5 ==
157 GNUNET_DISK_file_write (out,
158 "Hello",
159 5));
160 GNUNET_break (GNUNET_OK ==
161 GNUNET_DISK_file_close (out));
162}
163
164
165int
166main (int argc,
167 const char *const argv[])
168{
169 GNUNET_log_setup (argv[0],
170 "DEBUG",
171 NULL);
172 GNUNET_SCHEDULER_run (&test_child_management,
173 NULL);
174 return global_ret;
175}
176
177
178/* end of test_anastasis_child_management.c */
diff --git a/src/lib/util/test_client.c b/src/lib/util/test_client.c
new file mode 100644
index 000000000..64c32f646
--- /dev/null
+++ b/src/lib/util/test_client.c
@@ -0,0 +1,197 @@
1/*
2 This file is part of GNUnet.
3 Copyright (C) 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 * @file util/test_client.c
22 * @brief tests for client.c
23 * @author Christian Grothoff
24 */
25
26#include "platform.h"
27#include "gnunet_util_lib.h"
28
29static int global_ret;
30
31static struct GNUNET_MQ_Handle *client_mq;
32
33#define MY_TYPE 130
34
35
36/**
37 * Callback that just bounces the message back to the sender.
38 */
39static void
40handle_echo (void *cls,
41 const struct GNUNET_MessageHeader *message)
42{
43 struct GNUNET_SERVICE_Client *c = cls;
44 struct GNUNET_MQ_Handle *mq = GNUNET_SERVICE_client_get_mq (c);
45 struct GNUNET_MQ_Envelope *env;
46
47 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
48 "Receiving message from client, bouncing back\n");
49 env = GNUNET_MQ_msg_copy (message);
50 GNUNET_MQ_send (mq,
51 env);
52 GNUNET_SERVICE_client_continue (c);
53}
54
55
56static void
57handle_bounce (void *cls,
58 const struct GNUNET_MessageHeader *got)
59{
60 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
61 "Receiving bounce, checking content\n");
62 GNUNET_assert (NULL != got);
63 global_ret = 2;
64 GNUNET_MQ_destroy (client_mq);
65 client_mq = NULL;
66}
67
68
69/**
70 * Generic error handler, called with the appropriate error code and
71 * the same closure specified at the creation of the message queue.
72 * Not every message queue implementation supports an error handler.
73 *
74 * @param cls closure with the `struct GNUNET_STATISTICS_Handle *`
75 * @param error error code
76 */
77static void
78mq_error_handler (void *cls,
79 enum GNUNET_MQ_Error error)
80{
81 GNUNET_assert (0); /* should never happen */
82}
83
84
85static void
86task (void *cls,
87 const struct GNUNET_CONFIGURATION_Handle *cfg,
88 struct GNUNET_SERVICE_Handle *sh)
89{
90 struct GNUNET_MQ_MessageHandler chandlers[] = {
91 GNUNET_MQ_hd_fixed_size (bounce,
92 MY_TYPE,
93 struct GNUNET_MessageHeader,
94 cls),
95 GNUNET_MQ_handler_end ()
96 };
97 struct GNUNET_MQ_Envelope *env;
98 struct GNUNET_MessageHeader *msg;
99
100 /* test that ill-configured client fails instantly */
101 GNUNET_assert (NULL ==
102 GNUNET_CLIENT_connect (cfg,
103 "invalid-service",
104 NULL,
105 &mq_error_handler,
106 NULL));
107 client_mq = GNUNET_CLIENT_connect (cfg,
108 "test_client",
109 chandlers,
110 &mq_error_handler,
111 NULL);
112 GNUNET_assert (NULL != client_mq);
113 env = GNUNET_MQ_msg (msg,
114 MY_TYPE);
115 GNUNET_MQ_send (client_mq,
116 env);
117}
118
119
120/**
121 * Function called when the client connects to the service.
122 *
123 * @param cls the name of the service
124 * @param c connecting client
125 * @param mq message queue to talk to the client
126 * @return @a c
127 */
128static void *
129connect_cb (void *cls,
130 struct GNUNET_SERVICE_Client *c,
131 struct GNUNET_MQ_Handle *mq)
132{
133 return c;
134}
135
136
137/**
138 * Function called when the client disconnects.
139 *
140 * @param cls our service name
141 * @param c disconnecting client
142 * @param internal_cls must match @a c
143 */
144static void
145disconnect_cb (void *cls,
146 struct GNUNET_SERVICE_Client *c,
147 void *internal_cls)
148{
149 if (2 == global_ret)
150 {
151 GNUNET_SCHEDULER_shutdown ();
152 global_ret = 0;
153 }
154}
155
156
157int
158main (int argc,
159 char *argv[])
160{
161 struct GNUNET_MQ_MessageHandler shandlers[] = {
162 GNUNET_MQ_hd_fixed_size (echo,
163 MY_TYPE,
164 struct GNUNET_MessageHeader,
165 NULL),
166 GNUNET_MQ_handler_end ()
167 };
168 char *test_argv[] = {
169 (char *) "test_client",
170 "-c",
171 "test_client_data.conf",
172 NULL
173 };
174
175 GNUNET_log_setup ("test_client",
176 "WARNING",
177 NULL);
178 if (0 != strstr (argv[0],
179 "unix"))
180 test_argv[2] = "test_client_unix.conf";
181 global_ret = 1;
182 if (0 !=
183 GNUNET_SERVICE_run_ (3,
184 test_argv,
185 "test_client",
186 GNUNET_SERVICE_OPTION_NONE,
187 &task,
188 &connect_cb,
189 &disconnect_cb,
190 NULL,
191 shandlers))
192 global_ret = 3;
193 return global_ret;
194}
195
196
197/* end of test_client.c */
diff --git a/src/lib/util/test_client_data.conf b/src/lib/util/test_client_data.conf
new file mode 100644
index 000000000..fa9a0be03
--- /dev/null
+++ b/src/lib/util/test_client_data.conf
@@ -0,0 +1,3 @@
1[test_client]
2PORT=14325
3HOSTNAME=localhost
diff --git a/src/lib/util/test_client_unix.conf b/src/lib/util/test_client_unix.conf
new file mode 100644
index 000000000..d3d90627d
--- /dev/null
+++ b/src/lib/util/test_client_unix.conf
@@ -0,0 +1,6 @@
1[test_client]
2PORT=0
3HOSTNAME=localhost
4UNIXPATH=$GNUNET_RUNTIME_DIR/test-client-unix.sock
5UNIX_MATCH_UID = YES
6UNIX_MATCH_GID = YES
diff --git a/src/lib/util/test_common_allocation.c b/src/lib/util/test_common_allocation.c
new file mode 100644
index 000000000..d4cc4bb58
--- /dev/null
+++ b/src/lib/util/test_common_allocation.c
@@ -0,0 +1,179 @@
1/*
2 This file is part of GNUnet.
3 Copyright (C) 2001, 2002, 2003, 2005, 2006, 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 util/test_common_allocation.c
23 * @brief testcase for common_allocation.c
24 */
25
26#include "platform.h"
27#include "gnunet_util_lib.h"
28
29
30static int
31check (void)
32{
33#define MAX_TESTVAL 1024
34 char *ptrs[MAX_TESTVAL];
35 unsigned int **a2;
36 char ***a3;
37 char *tmp;
38 int i;
39 int j;
40 int k;
41 unsigned int ui;
42
43 /* GNUNET_malloc/GNUNET_free test */
44 k = 352; /* random start value */
45 for (i = 1; i < MAX_TESTVAL; i++)
46 {
47 ptrs[i] = GNUNET_malloc (i);
48 for (j = 0; j < i; j++)
49 ptrs[i][j] = k++;
50 }
51
52 for (i = MAX_TESTVAL - 1; i >= 1; i--)
53 {
54 for (j = i - 1; j >= 0; j--)
55 if (ptrs[i][j] != (char) --k)
56 return 1;
57 GNUNET_free (ptrs[i]);
58 }
59
60 /* GNUNET_free test */
61 tmp = GNUNET_malloc (4);
62 GNUNET_free (tmp);
63
64 /* GNUNET_strdup tests */
65 ptrs[0] = GNUNET_strdup ("bar");
66 if (0 != strcmp (ptrs[0], "bar"))
67 return 3;
68 /* now realloc */
69 ptrs[0] = GNUNET_realloc (ptrs[0], 12);
70 strcpy (ptrs[0], "Hello World");
71
72 GNUNET_free (ptrs[0]);
73 GNUNET_asprintf (&ptrs[0], "%s %s", "Hello", "World");
74 GNUNET_assert (strlen (ptrs[0]) == 11);
75 GNUNET_free (ptrs[0]);
76
77 /* GNUNET_array_grow tests */
78 ptrs[0] = NULL;
79 ui = 0;
80 GNUNET_array_grow (ptrs[0], ui, 42);
81 if (ui != 42)
82 return 4;
83 GNUNET_array_grow (ptrs[0], ui, 22);
84 if (ui != 22)
85 return 5;
86 for (j = 0; j < 22; j++)
87 ptrs[0][j] = j;
88 GNUNET_array_grow (ptrs[0], ui, 32);
89 for (j = 0; j < 22; j++)
90 if (ptrs[0][j] != j)
91 return 6;
92 for (j = 22; j < 32; j++)
93 if (ptrs[0][j] != 0)
94 return 7;
95 GNUNET_array_grow (ptrs[0], ui, 0);
96 if (i != 0)
97 return 8;
98 if (ptrs[0] != NULL)
99 return 9;
100
101 /* GNUNET_new_array_2d tests */
102 a2 = GNUNET_new_array_2d (17, 22, unsigned int);
103 for (i = 0; i < 17; i++)
104 {
105 for (j = 0; j < 22; j++)
106 {
107 if (0 != a2[i][j])
108 {
109 GNUNET_free (a2);
110 return 10;
111 }
112 a2[i][j] = i * 100 + j;
113 }
114 }
115 GNUNET_free (a2);
116
117 /* GNUNET_new_array_3d tests */
118 a3 = GNUNET_new_array_3d (2, 3, 4, char);
119 for (i = 0; i < 2; i++)
120 {
121 for (j = 0; j < 3; j++)
122 {
123 for (k = 0; k < 4; k++)
124 {
125 if (0 != a3[i][j][k])
126 {
127 GNUNET_free (a3);
128 return 11;
129 }
130 a3[i][j][k] = i * 100 + j * 10 + k;
131 }
132 }
133 }
134 GNUNET_free (a3);
135 return 0;
136}
137
138
139static int
140check2 (void)
141{
142 char *a1 = NULL;
143 unsigned int a1_len = 0;
144 const char *a2 = "test";
145
146 GNUNET_array_append (a1,
147 a1_len,
148 'x');
149 GNUNET_array_concatenate (a1,
150 a1_len,
151 a2,
152 4);
153 GNUNET_assert (0 == strncmp ("xtest",
154 a1,
155 5));
156 GNUNET_assert (5 == a1_len);
157 GNUNET_free (a1);
158 return 0;
159}
160
161
162int
163main (int argc, char *argv[])
164{
165 int ret;
166
167 GNUNET_log_setup ("test-common-allocation",
168 "WARNING",
169 NULL);
170 ret = check () | check2 ();
171 if (ret != 0)
172 fprintf (stderr,
173 "ERROR %d.\n",
174 ret);
175 return ret;
176}
177
178
179/* end of test_common_allocation.c */
diff --git a/src/lib/util/test_common_endian.c b/src/lib/util/test_common_endian.c
new file mode 100644
index 000000000..2c11c594e
--- /dev/null
+++ b/src/lib/util/test_common_endian.c
@@ -0,0 +1,44 @@
1/*
2 This file is part of GNUnet.
3 Copyright (C) 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 util/test_common_endian.c
22 * @brief testcase for common_endian.c
23 */
24
25#include "platform.h"
26#include "gnunet_util_lib.h"
27
28#define CHECK(n) if (n != GNUNET_htonll (GNUNET_ntohll (n))) return 1;
29
30int
31main (int argc, char *argv[])
32{
33 GNUNET_log_setup ("test-common-endian", "WARNING", NULL);
34 CHECK (1);
35 CHECK (0x12345678);
36 CHECK (123456789012345LL);
37 if ((0x1234567890ABCDEFLL != GNUNET_htonll (0xEFCDAB9078563412LL)) &&
38 (42 != htonl (42)) )
39 return 1;
40 return 0;
41}
42
43
44/* end of test_common_endian.c */
diff --git a/src/lib/util/test_common_logging.c b/src/lib/util/test_common_logging.c
new file mode 100644
index 000000000..a2e49f20a
--- /dev/null
+++ b/src/lib/util/test_common_logging.c
@@ -0,0 +1,100 @@
1/*
2 This file is part of GNUnet.
3 Copyright (C) 2008 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/test_common_logging.c
23 * @brief testcase for the logging module
24 * @author Christian Grothoff
25 */
26
27#include "platform.h"
28#include "gnunet_util_lib.h"
29
30static void
31my_log (void *ctx, enum GNUNET_ErrorType kind, const char *component,
32 const char *date, const char *msg)
33{
34 unsigned int *c = ctx;
35
36 (*c)++;
37}
38
39
40int
41main (int argc, char *argv[])
42{
43 unsigned int failureCount = 0;
44 unsigned int logs = 0;
45
46 if (0 != putenv ("GNUNET_FORCE_LOG="))
47 fprintf (stderr, "Failed to putenv: %s\n", strerror (errno));
48 GNUNET_log_setup ("test-common-logging", "DEBUG", "/dev/null");
49 GNUNET_logger_add (&my_log, &logs);
50 GNUNET_logger_add (&my_log, &logs);
51 GNUNET_log (GNUNET_ERROR_TYPE_BULK, "Testing...\n");
52 GNUNET_log (GNUNET_ERROR_TYPE_BULK, "Testing...\n");
53 GNUNET_log (GNUNET_ERROR_TYPE_BULK, "Testing...\n");
54 GNUNET_log (GNUNET_ERROR_TYPE_BULK, "Testing...\n");
55 GNUNET_log (GNUNET_ERROR_TYPE_BULK, "Testing...\n");
56 GNUNET_log (GNUNET_ERROR_TYPE_BULK, "Testing...\n");
57 GNUNET_logger_remove (&my_log, &logs);
58 GNUNET_log (GNUNET_ERROR_TYPE_BULK, "Flusher...\n");
59 /* the last 6 calls should be merged (repated bulk messages!) */
60 GNUNET_logger_remove (&my_log, &logs);
61 if (logs != 4)
62 {
63 fprintf (stdout, "Expected 4 log calls, got %u\n", logs);
64 failureCount++;
65 }
66 GNUNET_break (0 ==
67 strcmp (_ ("ERROR"),
68 GNUNET_error_type_to_string (GNUNET_ERROR_TYPE_ERROR)));
69 GNUNET_break (0 ==
70 strcmp (_ ("WARNING"),
71 GNUNET_error_type_to_string
72 (GNUNET_ERROR_TYPE_WARNING)));
73 GNUNET_break (0 ==
74 strcmp (_ ("INFO"),
75 GNUNET_error_type_to_string (GNUNET_ERROR_TYPE_INFO)));
76 GNUNET_break (0 ==
77 strcmp (_ ("DEBUG"),
78 GNUNET_error_type_to_string (GNUNET_ERROR_TYPE_DEBUG)));
79 GNUNET_log_setup ("test_common_logging", "WARNING", "/dev/null");
80 logs = 0;
81 GNUNET_logger_add (&my_log, &logs);
82 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Checker...\n");
83 GNUNET_log (GNUNET_ERROR_TYPE_INFO, "Drop me...\n");
84 GNUNET_logger_remove (&my_log, &logs);
85 if (logs != 1)
86 {
87 fprintf (stdout, "Expected 1 log call, got %u\n", logs);
88 failureCount++;
89 }
90
91 if (failureCount != 0)
92 {
93 fprintf (stdout, "%u TESTS FAILED!\n", failureCount);
94 return -1;
95 }
96 return 0;
97} /* end of main */
98
99
100/* end of test_common_logging.c */
diff --git a/src/lib/util/test_common_logging_dummy.c b/src/lib/util/test_common_logging_dummy.c
new file mode 100644
index 000000000..7e362c683
--- /dev/null
+++ b/src/lib/util/test_common_logging_dummy.c
@@ -0,0 +1,123 @@
1/*
2 This file is part of GNUnet.
3 Copyright (C) 2008-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/test_common_logging_dummy.c
23 * @brief dummy labrat for the testcase for the logging module (runtime
24 * log level adjustment)
25 * @author LRN
26 */
27
28#include "platform.h"
29#include "gnunet_util_lib.h"
30
31#undef GNUNET_EXTRA_LOGGING
32#define GNUNET_EXTRA_LOGGING GNUNET_YES
33
34/**
35 * Artificial delay attached to each log call that is not skipped out.
36 * This must be long enough for us to not to mistake skipped log call
37 * on a slow machine for a non-skipped one.
38 */
39#define OUTPUT_DELAY \
40 GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_MICROSECONDS, 1000)
41
42static void
43my_log (void *ctx,
44 enum GNUNET_ErrorType kind,
45 const char *component,
46 const char *date,
47 const char *msg)
48{
49 (void) ctx;
50 (void) kind;
51 (void) component;
52 (void) date;
53 if (strncmp ("test-common-logging-dummy", component, 25) != 0)
54 return;
55 fprintf (stdout, "%s", msg);
56 fflush (stdout);
57}
58
59
60#if ! defined(GNUNET_CULL_LOGGING)
61static int
62expensive_func ()
63{
64 return GNUNET_NETWORK_socket_select (NULL, NULL, NULL, OUTPUT_DELAY);
65}
66
67
68#endif
69
70
71#define pr(kind, lvl) \
72 { \
73 struct GNUNET_TIME_Absolute t1, t2; \
74 t1 = GNUNET_TIME_absolute_get (); \
75 GNUNET_log (kind, "L%s %d\n", lvl, expensive_func ()); \
76 t2 = GNUNET_TIME_absolute_get (); \
77 printf ("1%s %llu\n", \
78 lvl, \
79 (unsigned long long) GNUNET_TIME_absolute_get_difference (t1, t2) \
80 .rel_value_us); \
81 }
82
83#define pr2(kind, lvl) \
84 { \
85 struct GNUNET_TIME_Absolute t1, t2; \
86 t1 = GNUNET_TIME_absolute_get (); \
87 GNUNET_log (kind, "L%s %d\n", lvl, expensive_func ()); \
88 t2 = GNUNET_TIME_absolute_get (); \
89 printf ("2%s %llu\n", \
90 lvl, \
91 (unsigned long long) GNUNET_TIME_absolute_get_difference (t1, t2) \
92 .rel_value_us); \
93 }
94
95
96int
97main (int argc, char *argv[])
98{
99 (void) argc;
100 (void) argv;
101 /* We set up logging with NULL level - will be overridden by
102 * GNUNET_LOG or GNUNET_FORCE_LOG at runtime.
103 */
104 GNUNET_log_setup ("test-common-logging-dummy", NULL, "/dev/null");
105 GNUNET_logger_add (&my_log, NULL);
106 pr (GNUNET_ERROR_TYPE_ERROR, "ERROR");
107 pr (GNUNET_ERROR_TYPE_WARNING, "WARNING");
108 pr (GNUNET_ERROR_TYPE_INFO, "INFO");
109 pr (GNUNET_ERROR_TYPE_DEBUG, "DEBUG");
110
111 /* We set up logging with WARNING level - will onle be overridden by
112 * GNUNET_FORCE_LOG at runtime.
113 */
114 GNUNET_log_setup ("test-common-logging-dummy", "WARNING", "/dev/null");
115 pr2 (GNUNET_ERROR_TYPE_ERROR, "ERROR");
116 pr2 (GNUNET_ERROR_TYPE_WARNING, "WARNING");
117 pr2 (GNUNET_ERROR_TYPE_INFO, "INFO");
118 pr2 (GNUNET_ERROR_TYPE_DEBUG, "DEBUG");
119 return 0;
120} /* end of main */
121
122
123/* end of test_common_logging_dummy.c */
diff --git a/src/lib/util/test_common_logging_runtime_loglevels.c b/src/lib/util/test_common_logging_runtime_loglevels.c
new file mode 100644
index 000000000..f3f2fd64f
--- /dev/null
+++ b/src/lib/util/test_common_logging_runtime_loglevels.c
@@ -0,0 +1,457 @@
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/**
22 * @file util/test_common_logging_runtime_loglevels.c
23 * @brief testcase for the logging module (runtime log level adjustment)
24 * @author LRN
25 */
26
27#include "platform.h"
28#include "gnunet_util_lib.h"
29
30#define VERBOSE GNUNET_NO
31
32/**
33 * How much time the child is allowed to waste on skipped log calls, at most.
34 * Raspberry Pi takes 113 microseconds tops, this is 3x that value.
35 */
36#define MAX_SKIP_DELAY GNUNET_TIME_relative_multiply ( \
37 GNUNET_TIME_UNIT_MICROSECONDS, 400).rel_value_us
38
39/**
40 * How much time non-skipped log call should take, at least.
41 * Keep in sync with the value in the dummy!
42 */
43#define OUTPUT_DELAY GNUNET_TIME_relative_multiply ( \
44 GNUNET_TIME_UNIT_MICROSECONDS, 1000).rel_value_us
45
46static int ok;
47
48static int phase = 0;
49
50static struct GNUNET_OS_Process *proc;
51
52/* Pipe to read from started processes stdout (on read end) */
53static struct GNUNET_DISK_PipeHandle *pipe_stdout;
54
55static struct GNUNET_SCHEDULER_Task *die_task;
56
57static struct GNUNET_SCHEDULER_Task *read_task;
58
59static void
60runone (void);
61
62
63static void
64end_task (void *cls)
65{
66 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Ending phase %d, ok is %d\n", phase,
67 ok);
68 if (NULL != proc)
69 {
70 if (0 != GNUNET_OS_process_kill (proc, GNUNET_TERM_SIG))
71 {
72 GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING, "kill");
73 }
74 GNUNET_OS_process_wait (proc);
75 GNUNET_OS_process_destroy (proc);
76 proc = NULL;
77 }
78 if (NULL != read_task)
79 {
80 GNUNET_SCHEDULER_cancel (read_task);
81 read_task = NULL;
82 }
83 GNUNET_DISK_pipe_close (pipe_stdout);
84 if (ok == 1)
85 {
86 if (phase < 9)
87 {
88 phase += 1;
89 runone ();
90 }
91 else
92 ok = 0;
93 }
94 else
95 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "failing\n");
96}
97
98
99static char *
100read_output_line (int phase_from1, int phase_to1, int phase_from2,
101 int phase_to2, char c, const char *expect_level,
102 long delay_morethan, long delay_lessthan, int phase,
103 char *p,
104 int *len, long *delay, char level[8])
105{
106 char *r = p;
107 char t[7];
108 int i, j, stop = 0;
109 int level_matches;
110 int delay_is_sane;
111 int delay_is_a_dummy;
112 int delay_outside_of_range;
113
114 j = 0;
115 int stage = 0;
116
117 if (! ((phase >= phase_from1) && (phase <= phase_to1)) &&
118 ! ((phase >= phase_from2) && (phase <= phase_to2)))
119 return p;
120#if 0
121 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
122 "Trying to match '%c%s \\d\\r\\n' on %s\n", c, expect_level, p);
123#endif
124 for (i = 0; i < *len && ! stop; i++)
125 {
126 switch (stage)
127 {
128 case 0: /* read first char */
129 if (r[i] != c)
130 {
131 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Expected '%c', but got '%c'\n", c,
132 r[i]);
133 GNUNET_break (0);
134 return NULL;
135 }
136 stage += 1;
137 break;
138
139 case 1: /* read at most 7 char-long error level string, finished by ' ' */
140 if (r[i] == ' ')
141 {
142 level[j] = '\0';
143 stage += 1;
144 j = 0;
145 }
146 else if (i == 8)
147 {
148 GNUNET_break (0);
149 ok = 2;
150 return NULL;
151 }
152 else
153 level[j++] = r[i];
154 break;
155
156 case 2: /* read the delay, finished by '\n' */
157 t[j++] = r[i];
158 if (r[i] == '\n')
159 {
160 t[j - 1] = '\0';
161 *delay = strtol (t, NULL, 10);
162 stop = 1;
163 }
164 break;
165 }
166 }
167 level_matches = (strcmp (expect_level, level) == 0);
168 delay_is_sane = (*delay >= 0) && (*delay <= 1000000);
169 delay_is_a_dummy = (c == 'L');
170 /* Delay must be either less than 'lessthan' (log call is skipped)
171 * or more than 'morethan' (log call is not skipped)
172 */
173 delay_outside_of_range = ((*delay < delay_lessthan) || (*delay >=
174 delay_morethan));
175 if (delay_is_a_dummy)
176 delay_outside_of_range = 1;
177
178 if (! stop)
179 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
180 "Wrong log format?\n");
181 if (! level_matches)
182 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
183 "Wrong log level\n");
184 if (! delay_is_sane)
185 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
186 "Delay %ld is insane\n",
187 *delay);
188 if (! delay_outside_of_range)
189 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
190 "Delay %ld is not outside of range (%ld ; %ld)\n",
191 *delay,
192 delay_lessthan,
193 delay_morethan);
194 if (! stop || ! level_matches || ! delay_is_sane || ! delay_outside_of_range)
195 return NULL;
196 *len = *len - i;
197 return &r[i];
198}
199
200
201/**
202 * Up to 8 non-skipped GNUNET_log() calls
203 * + extra line with delay for each one
204 */
205#define LOG_MAX_NUM_LINES (8 * 2)
206/**
207 * Actual message is 17 chars at most
208 */
209#define LOG_MAX_LINE_LENGTH (17)
210
211#define LOG_BUFFER_SIZE LOG_MAX_NUM_LINES *LOG_MAX_LINE_LENGTH
212
213static char buf[LOG_BUFFER_SIZE];
214
215static char *buf_ptr;
216
217static int bytes;
218
219
220static void
221read_call (void *cls)
222{
223 const struct GNUNET_DISK_FileHandle *stdout_read_handle = cls;
224 char level[8];
225 long delay;
226 long delays[8];
227 int rd;
228
229 read_task = NULL;
230 rd = GNUNET_DISK_file_read (stdout_read_handle, buf_ptr,
231 sizeof(buf) - bytes);
232 if (rd > 0)
233 {
234 buf_ptr += rd;
235 bytes += rd;
236#if VERBOSE
237 fprintf (stderr, "got %d bytes, reading more\n", rd);
238#endif
239 read_task = GNUNET_SCHEDULER_add_read_file (GNUNET_TIME_UNIT_FOREVER_REL,
240 stdout_read_handle,
241 &read_call,
242 (void *) stdout_read_handle);
243 return;
244 }
245
246#if VERBOSE
247 fprintf (stderr, "bytes is %d:%s\n", bytes, buf);
248#endif
249
250 /* +------CHILD OUTPUT--
251 * | SOFT HARD
252 * | E W I D E W I D
253 * | 0E * * *
254 * | 1W * * * *
255 * P 2I * * * * *
256 * H 3D * * * * * *
257 * A
258 * S 4E * *
259 * E 5W * * * *
260 * | 6I * * * * * *
261 * | 7D * * * * * * * *
262 * | 8 * * * *
263 * | 9 * * * *
264 */char *p = buf;
265
266 if ((bytes == LOG_BUFFER_SIZE) ||
267 ! (p =
268 read_output_line (0, 3, 4, 9, 'L', "ERROR", -1,
269 1, phase, p,
270 &bytes, &delay, level)) ||
271 ! (p =
272 read_output_line (0, 3, 4, 9, '1', "ERROR", OUTPUT_DELAY,
273 MAX_SKIP_DELAY, phase, p,
274 &bytes, &delays[0], level)) ||
275 ! (p =
276 read_output_line (1, 3, 5, 9, 'L', "WARNING", -1,
277 1, phase, p,
278 &bytes, &delay, level)) ||
279 ! (p =
280 read_output_line (0, 3, 4, 9, '1', "WARNING", OUTPUT_DELAY,
281 MAX_SKIP_DELAY, phase, p,
282 &bytes, &delays[1], level)) ||
283 ! (p =
284 read_output_line (2, 3, 6, 7, 'L', "INFO", -1,
285 1, phase, p,
286 &bytes, &delay, level)) ||
287 ! (p =
288 read_output_line (0, 3, 4, 9, '1', "INFO", OUTPUT_DELAY,
289 MAX_SKIP_DELAY, phase, p,
290 &bytes, &delays[2], level)) ||
291 ! (p =
292 read_output_line (3, 3, 7, 7, 'L', "DEBUG", -1,
293 1, phase, p,
294 &bytes, &delay, level)) ||
295 ! (p =
296 read_output_line (0, 3, 4, 9, '1', "DEBUG", OUTPUT_DELAY,
297 MAX_SKIP_DELAY, phase, p,
298 &bytes, &delays[3], level)) ||
299 ! (p =
300 read_output_line (0, 3, 4, 9, 'L', "ERROR", -1,
301 1, phase, p,
302 &bytes, &delay, level)) ||
303 ! (p =
304 read_output_line (0, 3, 4, 9, '2', "ERROR", OUTPUT_DELAY,
305 MAX_SKIP_DELAY, phase, p,
306 &bytes, &delays[4], level)) ||
307 ! (p =
308 read_output_line (0, 3, 5, 9, 'L', "WARNING", -1,
309 1, phase, p,
310 &bytes, &delay, level)) ||
311 ! (p =
312 read_output_line (0, 3, 4, 9, '2', "WARNING", OUTPUT_DELAY,
313 MAX_SKIP_DELAY, phase, p,
314 &bytes, &delays[5], level)) ||
315 ! (p =
316 read_output_line (-1, -1, 6, 7, 'L', "INFO", -1,
317 1, phase, p,
318 &bytes, &delay, level)) ||
319 ! (p =
320 read_output_line (0, 3, 4, 9, '2', "INFO", OUTPUT_DELAY,
321 MAX_SKIP_DELAY, phase, p,
322 &bytes, &delays[6], level)) ||
323 ! (p =
324 read_output_line (-1, -1, 7, 7, 'L', "DEBUG", -1,
325 1, phase, p,
326 &bytes, &delay, level)) ||
327 ! (p =
328 read_output_line (0, 3, 4, 9, '2', "DEBUG", OUTPUT_DELAY,
329 MAX_SKIP_DELAY, phase, p,
330 &bytes, &delays[7], level)))
331 {
332 if (bytes == LOG_BUFFER_SIZE)
333 fprintf (stderr, "%s", "Ran out of buffer space!\n");
334 GNUNET_break (0);
335 ok = 2;
336 GNUNET_SCHEDULER_cancel (die_task);
337 GNUNET_SCHEDULER_add_now (&end_task, NULL);
338 return;
339 }
340
341 GNUNET_SCHEDULER_cancel (die_task);
342 GNUNET_SCHEDULER_add_now (&end_task, NULL);
343}
344
345
346static void
347runone ()
348{
349 const struct GNUNET_DISK_FileHandle *stdout_read_handle;
350
351 pipe_stdout = GNUNET_DISK_pipe (GNUNET_DISK_PF_BLOCKING_RW);
352
353 if (pipe_stdout == NULL)
354 {
355 GNUNET_break (0);
356 ok = 2;
357 return;
358 }
359
360 putenv ("GNUNET_LOG=");
361 putenv ("GNUNET_FORCE_LOG=");
362 putenv ("GNUNET_FORCE_LOGFILE=");
363 switch (phase)
364 {
365 case 0:
366 putenv ("GNUNET_LOG=;;;;ERROR");
367 break;
368
369 case 1:
370 putenv ("GNUNET_LOG=;;;;WARNING");
371 break;
372
373 case 2:
374 putenv ("GNUNET_LOG=;;;;INFO");
375 break;
376
377 case 3:
378 putenv ("GNUNET_LOG=;;;;DEBUG");
379 break;
380
381 case 4:
382 putenv ("GNUNET_FORCE_LOG=;;;;ERROR");
383 break;
384
385 case 5:
386 putenv ("GNUNET_FORCE_LOG=;;;;WARNING");
387 break;
388
389 case 6:
390 putenv ("GNUNET_FORCE_LOG=;;;;INFO");
391 break;
392
393 case 7:
394 putenv ("GNUNET_FORCE_LOG=;;;;DEBUG");
395 break;
396
397 case 8:
398 putenv ("GNUNET_LOG=blah;;;;ERROR");
399 break;
400
401 case 9:
402 putenv ("GNUNET_FORCE_LOG=blah;;;;ERROR");
403 break;
404 }
405
406 proc = GNUNET_OS_start_process (GNUNET_OS_INHERIT_STD_OUT_AND_ERR,
407 NULL, pipe_stdout, NULL,
408 "./test_common_logging_dummy",
409 "test_common_logging_dummy", NULL);
410 GNUNET_assert (NULL != proc);
411 putenv ("GNUNET_FORCE_LOG=");
412 putenv ("GNUNET_LOG=");
413
414 /* Close the write end of the read pipe */
415 GNUNET_DISK_pipe_close_end (pipe_stdout, GNUNET_DISK_PIPE_END_WRITE);
416
417 stdout_read_handle =
418 GNUNET_DISK_pipe_handle (pipe_stdout, GNUNET_DISK_PIPE_END_READ);
419
420 die_task =
421 GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_relative_multiply
422 (GNUNET_TIME_UNIT_SECONDS, 10),
423 &end_task,
424 NULL);
425
426 bytes = 0;
427 buf_ptr = buf;
428 memset (&buf, 0, sizeof(buf));
429
430 read_task = GNUNET_SCHEDULER_add_read_file (GNUNET_TIME_UNIT_FOREVER_REL,
431 stdout_read_handle,
432 &read_call,
433 (void *) stdout_read_handle);
434}
435
436
437static void
438task (void *cls)
439{
440 phase = 0;
441 runone ();
442}
443
444
445int
446main (int argc, char *argv[])
447{
448 GNUNET_log_setup ("test-common-logging-runtime-loglevels",
449 "WARNING",
450 NULL);
451 ok = 1;
452 GNUNET_SCHEDULER_run (&task, &ok);
453 return ok;
454}
455
456
457/* end of test_common_logging_runtime_loglevels.c */
diff --git a/src/lib/util/test_configuration.c b/src/lib/util/test_configuration.c
new file mode 100644
index 000000000..75610fc74
--- /dev/null
+++ b/src/lib/util/test_configuration.c
@@ -0,0 +1,581 @@
1/*
2 This file is part of GNUnet.
3 Copyright (C) 2003, 2004, 2005, 2006, 2007 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/test_configuration.c
22 * @brief Test that the configuration module works.
23 * @author Christian Grothoff
24 */
25
26
27#include "platform.h"
28#include "gnunet_util_lib.h"
29
30
31/* Test Configuration Diffs Options */
32enum
33{
34 EDIT_NOTHING,
35 EDIT_SECTION,
36 EDIT_ALL,
37 ADD_NEW_SECTION,
38 ADD_NEW_ENTRY,
39 REMOVE_SECTION,
40 REMOVE_ENTRY,
41 COMPARE,
42 PRINT
43};
44
45static struct GNUNET_CONFIGURATION_Handle *cfg;
46static struct GNUNET_CONFIGURATION_Handle *cfg_default;
47
48struct DiffsCBData
49{
50 struct GNUNET_CONFIGURATION_Handle *cfg;
51 struct GNUNET_CONFIGURATION_Handle *cfgDiffs;
52 const char *section;
53 int callBackOption;
54 int status;
55};
56
57
58static void
59initDiffsCBData (struct DiffsCBData *cbData)
60{
61 cbData->section = NULL;
62 cbData->cfg = NULL;
63 cbData->cfgDiffs = NULL;
64 cbData->callBackOption = -1;
65 cbData->status = 0;
66}
67
68
69/**
70 * callback function for modifying
71 * and comparing configuration
72 */
73static void
74diffsCallBack (void *cls,
75 const char *section,
76 const char *option,
77 const char *value)
78{
79 struct DiffsCBData *cbData = cls;
80 int cbOption = cbData->callBackOption;
81
82 if (0 == strcasecmp ("PATHS",
83 section))
84 return;
85 switch (cbOption)
86 {
87 case EDIT_SECTION:
88 if (NULL == cbData->section)
89 cbData->section = section;
90 if (strcmp (cbData->section, section) == 0)
91 {
92 GNUNET_CONFIGURATION_set_value_string (cbData->cfg, section, option,
93 "new-value");
94 GNUNET_CONFIGURATION_set_value_string (cbData->cfgDiffs, section, option,
95 "new-value");
96 }
97 break;
98 case EDIT_ALL:
99 GNUNET_CONFIGURATION_set_value_string (cbData->cfg, section, option,
100 "new-value");
101 GNUNET_CONFIGURATION_set_value_string (cbData->cfgDiffs, section, option,
102 "new-value");
103 break;
104 case ADD_NEW_ENTRY:
105 {
106 static int hit = 0;
107
108 if (hit == 0)
109 {
110 hit = 1;
111 GNUNET_CONFIGURATION_set_value_string (cbData->cfg, section, "new-key",
112 "new-value");
113 GNUNET_CONFIGURATION_set_value_string (cbData->cfgDiffs, section,
114 "new-key", "new-value");
115 }
116 break;
117 }
118 case COMPARE:
119 {
120 int ret;
121 char *diffValue;
122
123 diffValue = NULL;
124 ret =
125 GNUNET_CONFIGURATION_get_value_string (cbData->cfgDiffs,
126 section,
127 option,
128 &diffValue);
129 if (NULL != diffValue)
130 {
131 if ( (ret == GNUNET_SYSERR) ||
132 (strcmp (diffValue, value) != 0) )
133 {
134 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
135 "Unexpected value %s in %s/%s\n",
136 diffValue,
137 section,
138 option);
139 cbData->status = 1;
140 }
141 }
142 else
143 {
144 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
145 "Unexpected value %s in %s/%s\n",
146 diffValue,
147 section,
148 option);
149 cbData->status = 1;
150 }
151 GNUNET_free (diffValue);
152 break;
153 }
154
155#if 0
156 case PRINT:
157 if (NULL == cbData->section)
158 {
159 cbData->section = section;
160 printf ("\nSection: %s\n", section);
161 }
162 else if (strcmp (cbData->section, section) != 0)
163 {
164 cbData->section = section;
165 printf ("\nSection: %s\n", section);
166 }
167 printf ("%s = %s\n", option, value);
168#endif
169 default:
170 break;
171 }
172}
173
174
175static struct GNUNET_CONFIGURATION_Handle *
176editConfiguration (struct GNUNET_CONFIGURATION_Handle *cfg,
177 int option)
178{
179 struct DiffsCBData diffsCB;
180
181 initDiffsCBData (&diffsCB);
182 diffsCB.cfgDiffs = GNUNET_CONFIGURATION_create ();
183
184 switch (option)
185 {
186 case EDIT_SECTION:
187 case EDIT_ALL:
188 case ADD_NEW_ENTRY:
189 diffsCB.callBackOption = option;
190 diffsCB.cfg = cfg;
191 GNUNET_CONFIGURATION_iterate (cfg, diffsCallBack, &diffsCB);
192 break;
193
194 case EDIT_NOTHING:
195 /* Do nothing */
196 break;
197
198 case ADD_NEW_SECTION:
199 {
200 int i;
201 char *key;
202
203 for (i = 0; i < 5; i++)
204 {
205 GNUNET_asprintf (&key, "key%d", i);
206 GNUNET_CONFIGURATION_set_value_string (cfg, "new-section", key,
207 "new-value");
208 GNUNET_CONFIGURATION_set_value_string (diffsCB.cfgDiffs, "new-section",
209 key, "new-value");
210 GNUNET_free (key);
211 }
212 break;
213 }
214
215 case REMOVE_SECTION:
216 break;
217
218 case REMOVE_ENTRY:
219 break;
220
221 default:
222 break;
223 }
224
225 return diffsCB.cfgDiffs;
226}
227
228
229/**
230 * Checking configuration diffs
231 */
232static int
233checkDiffs (struct GNUNET_CONFIGURATION_Handle *cfg_default, int option)
234{
235 struct GNUNET_CONFIGURATION_Handle *cfg;
236 struct GNUNET_CONFIGURATION_Handle *cfgDiffs;
237 struct DiffsCBData cbData;
238 int ret;
239 char *diffsFileName;
240
241 initDiffsCBData (&cbData);
242
243 cfg = GNUNET_CONFIGURATION_create ();
244 /* load defaults */
245 GNUNET_assert (GNUNET_OK == GNUNET_CONFIGURATION_load (cfg, NULL));
246
247 /* Modify configuration and save it */
248 cfgDiffs = editConfiguration (cfg, option);
249 diffsFileName = GNUNET_DISK_mktemp ("gnunet-test-configurations-diffs.conf");
250 if (diffsFileName == NULL)
251 {
252 GNUNET_break (0);
253 GNUNET_CONFIGURATION_destroy (cfg);
254 GNUNET_CONFIGURATION_destroy (cfgDiffs);
255 return 1;
256 }
257 GNUNET_CONFIGURATION_write_diffs (cfg_default, cfg, diffsFileName);
258 GNUNET_CONFIGURATION_destroy (cfg);
259
260 /* Compare the dumped configuration with modifications done */
261 cfg = GNUNET_CONFIGURATION_create ();
262 GNUNET_assert (GNUNET_OK == GNUNET_CONFIGURATION_parse (cfg, diffsFileName));
263 if (0 != remove (diffsFileName))
264 GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING, "remove",
265 diffsFileName);
266 cbData.callBackOption = COMPARE;
267 cbData.cfgDiffs = cfgDiffs;
268 GNUNET_CONFIGURATION_iterate (cfg,
269 &diffsCallBack,
270 &cbData);
271 if (1 == (ret = cbData.status))
272 {
273 fprintf (stderr, "%s",
274 "Incorrect Configuration Diffs: Diffs may contain data not actually edited\n");
275 goto housekeeping;
276 }
277 cbData.cfgDiffs = cfg;
278 GNUNET_CONFIGURATION_iterate (cfgDiffs, diffsCallBack, &cbData);
279 if ((ret = cbData.status) == 1)
280 fprintf (stderr, "%s",
281 "Incorrect Configuration Diffs: Data may be missing in diffs\n");
282
283housekeeping:
284#if 0
285 cbData.section = NULL;
286 cbData.callBackOption = PRINT;
287 printf ("\nExpected Diffs:\n");
288 GNUNET_CONFIGURATION_iterate (cfgDiffs, diffsCallBack, &cbData);
289 cbData.section = NULL;
290 printf ("\nActual Diffs:\n");
291 GNUNET_CONFIGURATION_iterate (cfg, diffsCallBack, &cbData);
292#endif
293 GNUNET_CONFIGURATION_destroy (cfg);
294 GNUNET_CONFIGURATION_destroy (cfgDiffs);
295 GNUNET_free (diffsFileName);
296 return ret;
297}
298
299
300static int
301testConfig ()
302{
303 char *c;
304 unsigned long long l;
305
306 if (GNUNET_OK != GNUNET_CONFIGURATION_get_value_string (cfg, "test", "b", &c))
307 return 1;
308 if (0 != strcmp ("b", c))
309 {
310 fprintf (stderr, "Got `%s'\n", c);
311 GNUNET_free (c);
312 return 2;
313 }
314 GNUNET_free (c);
315 if (GNUNET_OK !=
316 GNUNET_CONFIGURATION_get_value_number (cfg, "test", "five", &l))
317 {
318 GNUNET_break (0);
319 return 3;
320 }
321 if (5 != l)
322 {
323 GNUNET_break (0);
324 return 4;
325 }
326 GNUNET_CONFIGURATION_set_value_string (cfg, "more", "c", "YES");
327 if (GNUNET_NO == GNUNET_CONFIGURATION_get_value_yesno (cfg, "more", "c"))
328 {
329 GNUNET_break (0);
330 return 5;
331 }
332 GNUNET_CONFIGURATION_set_value_number (cfg, "NUMBERS", "TEN", 10);
333 if (GNUNET_OK !=
334 GNUNET_CONFIGURATION_get_value_string (cfg, "NUMBERS", "TEN", &c))
335 {
336 GNUNET_break (0);
337 return 6;
338 }
339 if (0 != strcmp (c, "10"))
340 {
341 GNUNET_free (c);
342 GNUNET_break (0);
343 return 7;
344 }
345 GNUNET_free (c);
346
347 if (GNUNET_OK !=
348 GNUNET_CONFIGURATION_get_value_filename (cfg, "last", "test", &c))
349 {
350 GNUNET_break (0);
351 return 8;
352 }
353
354 if (0 != strcmp (c, "/hello/world"))
355 {
356 GNUNET_break (0);
357 GNUNET_free (c);
358 return 9;
359 }
360 GNUNET_free (c);
361
362 if (GNUNET_OK !=
363 GNUNET_CONFIGURATION_get_value_size (cfg, "last", "size", &l))
364 {
365 GNUNET_break (0);
366 return 10;
367 }
368 if (l != 512 * 1024)
369 {
370 GNUNET_break (0);
371 return 11;
372 }
373 return 0;
374}
375
376
377static const char *want[] = {
378 "/Hello",
379 "/File Name",
380 "/World",
381 NULL,
382 NULL,
383};
384
385static int
386check (void *data, const char *fn)
387{
388 int *idx = data;
389
390 if (0 == strcmp (want[*idx], fn))
391 {
392 (*idx)++;
393 return GNUNET_OK;
394 }
395 GNUNET_break (0);
396 return GNUNET_SYSERR;
397}
398
399
400static int
401testConfigFilenames ()
402{
403 int idx;
404
405 idx = 0;
406 if (3 !=
407 GNUNET_CONFIGURATION_iterate_value_filenames (cfg, "FILENAMES", "test",
408 &check, &idx))
409 {
410 GNUNET_break (0);
411 return 8;
412 }
413 if (idx != 3)
414 return 16;
415 if (GNUNET_OK !=
416 GNUNET_CONFIGURATION_remove_value_filename (cfg, "FILENAMES", "test",
417 "/File Name"))
418 {
419 GNUNET_break (0);
420 return 24;
421 }
422
423 if (GNUNET_NO !=
424 GNUNET_CONFIGURATION_remove_value_filename (cfg, "FILENAMES", "test",
425 "/File Name"))
426 {
427 GNUNET_break (0);
428 return 32;
429 }
430 if (GNUNET_NO !=
431 GNUNET_CONFIGURATION_remove_value_filename (cfg, "FILENAMES", "test",
432 "Stuff"))
433 {
434 GNUNET_break (0);
435 return 40;
436 }
437
438 if (GNUNET_NO !=
439 GNUNET_CONFIGURATION_append_value_filename (cfg, "FILENAMES", "test",
440 "/Hello"))
441 {
442 GNUNET_break (0);
443 return 48;
444 }
445 if (GNUNET_NO !=
446 GNUNET_CONFIGURATION_append_value_filename (cfg, "FILENAMES", "test",
447 "/World"))
448 {
449 GNUNET_break (0);
450 return 56;
451 }
452
453 if (GNUNET_YES !=
454 GNUNET_CONFIGURATION_append_value_filename (cfg, "FILENAMES", "test",
455 "/File 1"))
456 {
457 GNUNET_break (0);
458 return 64;
459 }
460
461 if (GNUNET_YES !=
462 GNUNET_CONFIGURATION_append_value_filename (cfg, "FILENAMES", "test",
463 "/File 2"))
464 {
465 GNUNET_break (0);
466 return 72;
467 }
468
469 idx = 0;
470 want[1] = "/World";
471 want[2] = "/File 1";
472 want[3] = "/File 2";
473 if (4 !=
474 GNUNET_CONFIGURATION_iterate_value_filenames (cfg, "FILENAMES", "test",
475 &check, &idx))
476 {
477 GNUNET_break (0);
478 return 80;
479 }
480 if (idx != 4)
481 {
482 GNUNET_break (0);
483 return 88;
484 }
485 return 0;
486}
487
488
489int
490main (int argc, char *argv[])
491{
492 int failureCount = 0;
493 char *c;
494
495 GNUNET_log_setup ("test_configuration", "WARNING", NULL);
496 cfg = GNUNET_CONFIGURATION_create ();
497 GNUNET_assert (cfg != NULL);
498 if (GNUNET_OK !=
499 GNUNET_CONFIGURATION_parse (cfg, "test_configuration_data.conf"))
500 {
501 fprintf (stderr, "%s", "Failed to parse configuration file\n");
502 GNUNET_CONFIGURATION_destroy (cfg);
503 return 1;
504 }
505 failureCount += testConfig ();
506 if (failureCount > 0)
507 goto error;
508
509 failureCount = testConfigFilenames ();
510 if (failureCount > 0)
511 goto error;
512
513 if (GNUNET_OK != GNUNET_CONFIGURATION_write (cfg, "/tmp/gnunet-test.conf"))
514 {
515 fprintf (stderr, "%s", "Failed to write configuration file\n");
516 GNUNET_CONFIGURATION_destroy (cfg);
517 return 1;
518 }
519 GNUNET_CONFIGURATION_destroy (cfg);
520 GNUNET_assert (0 == unlink ("/tmp/gnunet-test.conf"));
521
522 cfg = GNUNET_CONFIGURATION_create ();
523 if (GNUNET_OK !=
524 GNUNET_CONFIGURATION_load (cfg, "test_configuration_data.conf"))
525 {
526 GNUNET_break (0);
527 GNUNET_CONFIGURATION_destroy (cfg);
528 return 1;
529 }
530 if (GNUNET_OK !=
531 GNUNET_CONFIGURATION_get_value_string (cfg, "TESTING", "WEAKRANDOM", &c))
532 {
533 GNUNET_break (0);
534 GNUNET_CONFIGURATION_destroy (cfg);
535 return 1;
536 }
537 if (0 != strcmp (c, "YES"))
538 {
539 GNUNET_break (0);
540 GNUNET_free (c);
541 GNUNET_CONFIGURATION_destroy (cfg);
542 return 1;
543 }
544
545 GNUNET_free (c);
546 GNUNET_CONFIGURATION_destroy (cfg);
547
548 /* Testing configuration diffs */
549 cfg_default = GNUNET_CONFIGURATION_create ();
550 if (GNUNET_OK != GNUNET_CONFIGURATION_load (cfg_default, NULL))
551 {
552 GNUNET_break (0);
553 GNUNET_CONFIGURATION_destroy (cfg_default);
554 return 1;
555 }
556
557 /* Nothing changed in the new configuration */
558 failureCount += checkDiffs (cfg_default, EDIT_NOTHING);
559
560 /* Modify all entries of the last section */
561 failureCount += checkDiffs (cfg_default, EDIT_SECTION);
562
563 /* Add a new section */
564 failureCount += checkDiffs (cfg_default, ADD_NEW_SECTION);
565
566 /* Add a new entry to the last section */
567 failureCount += checkDiffs (cfg_default, ADD_NEW_ENTRY);
568
569 /* Modify all entries in the configuration */
570 failureCount += checkDiffs (cfg_default, EDIT_ALL);
571
572 GNUNET_CONFIGURATION_destroy (cfg_default);
573
574error:
575 if (failureCount != 0)
576 {
577 fprintf (stderr, "Test failed: %u\n", failureCount);
578 return 1;
579 }
580 return 0;
581}
diff --git a/src/lib/util/test_configuration_data.conf b/src/lib/util/test_configuration_data.conf
new file mode 100644
index 000000000..93dfd128a
--- /dev/null
+++ b/src/lib/util/test_configuration_data.conf
@@ -0,0 +1,30 @@
1[PATHS]
2SUBST=/hello
3
4[GNUNET]
5SUBST=hello
6GNUNET_HOME=$GNUNET_TMP/test-hello-configuration-data/
7
8[test]
9a=a
10b=b
11five=5
12
13[more]
14c=c
15five=42
16
17
18[last]
19test = $SUBST/world
20boom = "1 2 3 testing"
21trailing = YES
22size = 512 KiB
23
24[FILENAMES]
25test = "/Hello /File\ Name /World"
26
27
28[TESTING]
29WEAKRANDOM = YES
30
diff --git a/src/lib/util/test_container_bloomfilter.c b/src/lib/util/test_container_bloomfilter.c
new file mode 100644
index 000000000..244733dd9
--- /dev/null
+++ b/src/lib/util/test_container_bloomfilter.c
@@ -0,0 +1,302 @@
1/*
2 This file is part of GNUnet.
3 Copyright (C) 2004, 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 util/test_container_bloomfilter.c
22 * @brief Testcase for the bloomfilter.
23 * @author Christian Grothoff
24 * @author Igor Wronsky
25 */
26
27
28#include "platform.h"
29#include "gnunet_util_lib.h"
30
31#define K 4
32#define SIZE 65536
33#define TESTFILE "/tmp/bloomtest.dat"
34
35
36static void
37bernd_interop (void)
38{
39 struct GNUNET_HashCode hc;
40 char val[128];
41 size_t len;
42 struct GNUNET_CONTAINER_BloomFilter *bf;
43
44 len = GNUNET_DNSPARSER_hex_to_bin (
45 "ac4d46b62f8ddaf3cefbc1c01e47536b7ff297cb081e27a396362b1e92e5729b",
46 val);
47 GNUNET_assert (len < 128);
48 GNUNET_CRYPTO_hash (val,
49 len,
50 &hc);
51 fprintf (stderr,
52 "sha512: %s\n",
53 GNUNET_DNSPARSER_bin_to_hex (&hc,
54 sizeof (hc)));
55 bf = GNUNET_CONTAINER_bloomfilter_init (NULL,
56 128,
57 16);
58 GNUNET_CONTAINER_bloomfilter_add (bf,
59 &hc);
60 len = GNUNET_CONTAINER_bloomfilter_get_size (bf);
61 {
62 char raw[len];
63
64 GNUNET_CONTAINER_bloomfilter_get_raw_data (bf,
65 raw,
66 len);
67 fprintf (stderr,
68 "BF: %s\n",
69 GNUNET_DNSPARSER_bin_to_hex (raw,
70 len));
71 }
72
73}
74
75
76/**
77 * Generate a random hashcode.
78 */
79static void
80nextHC (struct GNUNET_HashCode *hc)
81{
82 GNUNET_CRYPTO_hash_create_random (GNUNET_CRYPTO_QUALITY_WEAK, hc);
83}
84
85
86static int
87add_iterator (void *cls, struct GNUNET_HashCode *next)
88{
89 int *ret = cls;
90 struct GNUNET_HashCode pos;
91
92 if (0 == (*ret)--)
93 return GNUNET_NO;
94 nextHC (&pos);
95 *next = pos;
96 return GNUNET_YES;
97}
98
99
100int
101main (int argc, char *argv[])
102{
103 struct GNUNET_CONTAINER_BloomFilter *bf;
104 struct GNUNET_CONTAINER_BloomFilter *bfi;
105 struct GNUNET_HashCode tmp;
106 int i;
107 int ok1;
108 int ok2;
109 int falseok;
110 char buf[SIZE];
111 struct stat sbuf;
112
113 GNUNET_log_setup ("test-container-bloomfilter",
114 "WARNING",
115 NULL);
116 if (0)
117 {
118 bernd_interop ();
119 return 0;
120 }
121 GNUNET_CRYPTO_seed_weak_random (1);
122 if (0 == stat (TESTFILE, &sbuf))
123 if (0 != unlink (TESTFILE))
124 GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_ERROR, "unlink", TESTFILE);
125 bf = GNUNET_CONTAINER_bloomfilter_load (TESTFILE, SIZE, K);
126
127 for (i = 0; i < 200; i++)
128 {
129 nextHC (&tmp);
130 GNUNET_CONTAINER_bloomfilter_add (bf, &tmp);
131 }
132 GNUNET_CRYPTO_seed_weak_random (1);
133 ok1 = 0;
134 for (i = 0; i < 200; i++)
135 {
136 nextHC (&tmp);
137 if (GNUNET_CONTAINER_bloomfilter_test (bf, &tmp) == GNUNET_YES)
138 ok1++;
139 }
140 if (ok1 != 200)
141 {
142 printf ("Got %d elements out of"
143 "200 expected after insertion.\n",
144 ok1);
145 GNUNET_CONTAINER_bloomfilter_free (bf);
146 return -1;
147 }
148 if (GNUNET_OK != GNUNET_CONTAINER_bloomfilter_get_raw_data (bf, buf, SIZE))
149 {
150 GNUNET_CONTAINER_bloomfilter_free (bf);
151 return -1;
152 }
153
154 GNUNET_CONTAINER_bloomfilter_free (bf);
155
156 bf = GNUNET_CONTAINER_bloomfilter_load (TESTFILE, SIZE, K);
157 GNUNET_assert (bf != NULL);
158 bfi = GNUNET_CONTAINER_bloomfilter_init (buf, SIZE, K);
159 GNUNET_assert (bfi != NULL);
160
161 GNUNET_CRYPTO_seed_weak_random (1);
162 ok1 = 0;
163 ok2 = 0;
164 for (i = 0; i < 200; i++)
165 {
166 nextHC (&tmp);
167 if (GNUNET_CONTAINER_bloomfilter_test (bf, &tmp) == GNUNET_YES)
168 ok1++;
169 if (GNUNET_CONTAINER_bloomfilter_test (bfi, &tmp) == GNUNET_YES)
170 ok2++;
171 }
172 if (ok1 != 200)
173 {
174 printf ("Got %d elements out of 200 "
175 "expected after reloading.\n",
176 ok1);
177 GNUNET_CONTAINER_bloomfilter_free (bf);
178 GNUNET_CONTAINER_bloomfilter_free (bfi);
179 return -1;
180 }
181
182 if (ok2 != 200)
183 {
184 printf ("Got %d elements out of 200 "
185 "expected after initialization.\n",
186 ok2);
187 GNUNET_CONTAINER_bloomfilter_free (bf);
188 GNUNET_CONTAINER_bloomfilter_free (bfi);
189 return -1;
190 }
191
192 GNUNET_CRYPTO_seed_weak_random (1);
193 for (i = 0; i < 100; i++)
194 {
195 nextHC (&tmp);
196 GNUNET_CONTAINER_bloomfilter_remove (bf, &tmp);
197 GNUNET_CONTAINER_bloomfilter_remove (bfi, &tmp);
198 }
199
200 GNUNET_CRYPTO_seed_weak_random (1);
201
202 ok1 = 0;
203 ok2 = 0;
204 for (i = 0; i < 200; i++)
205 {
206 nextHC (&tmp);
207 if (GNUNET_CONTAINER_bloomfilter_test (bf, &tmp) == GNUNET_YES)
208 ok1++;
209 if (GNUNET_CONTAINER_bloomfilter_test (bfi, &tmp) == GNUNET_YES)
210 ok2++;
211 }
212
213 if (ok1 != 100)
214 {
215 printf ("Expected 100 elements in loaded filter"
216 " after adding 200 and deleting 100, got %d\n",
217 ok1);
218 GNUNET_CONTAINER_bloomfilter_free (bf);
219 GNUNET_CONTAINER_bloomfilter_free (bfi);
220 return -1;
221 }
222 if (ok2 != 200)
223 {
224 printf ("Expected 200 elements in initialized filter"
225 " after adding 200 and deleting 100 "
226 "(which should do nothing for a filter not backed by a file), got %d\n",
227 ok2);
228 GNUNET_CONTAINER_bloomfilter_free (bf);
229 GNUNET_CONTAINER_bloomfilter_free (bfi);
230 return -1;
231 }
232
233 GNUNET_CRYPTO_seed_weak_random (3);
234
235 GNUNET_CONTAINER_bloomfilter_clear (bf);
236 falseok = 0;
237 for (i = 0; i < 1000; i++)
238 {
239 nextHC (&tmp);
240 if (GNUNET_CONTAINER_bloomfilter_test (bf, &tmp) == GNUNET_YES)
241 falseok++;
242 }
243 if (falseok > 0)
244 {
245 GNUNET_CONTAINER_bloomfilter_free (bf);
246 GNUNET_CONTAINER_bloomfilter_free (bfi);
247 return -1;
248 }
249
250 if (GNUNET_OK != GNUNET_CONTAINER_bloomfilter_or (bf, buf, SIZE))
251 {
252 GNUNET_CONTAINER_bloomfilter_free (bf);
253 GNUNET_CONTAINER_bloomfilter_free (bfi);
254 return -1;
255 }
256
257 GNUNET_CRYPTO_seed_weak_random (2);
258 i = 20;
259 GNUNET_CONTAINER_bloomfilter_resize (bfi, &add_iterator, &i, SIZE * 2, K);
260
261 GNUNET_CRYPTO_seed_weak_random (2);
262 i = 20;
263 GNUNET_CONTAINER_bloomfilter_resize (bf, &add_iterator, &i, SIZE * 2, K);
264 GNUNET_CRYPTO_seed_weak_random (2);
265
266 ok1 = 0;
267 ok2 = 0;
268 for (i = 0; i < 20; i++)
269 {
270 nextHC (&tmp);
271 if (GNUNET_CONTAINER_bloomfilter_test (bf, &tmp) == GNUNET_YES)
272 ok1++;
273 if (GNUNET_CONTAINER_bloomfilter_test (bfi, &tmp) == GNUNET_YES)
274 ok2++;
275 }
276
277 if (ok1 != 20)
278 {
279 printf ("Expected 20 elements in resized file-backed filter"
280 " after adding 20, got %d\n",
281 ok1);
282 GNUNET_CONTAINER_bloomfilter_free (bf);
283 GNUNET_CONTAINER_bloomfilter_free (bfi);
284 return -1;
285 }
286 if (ok2 != 20)
287 {
288 printf ("Expected 20 elements in resized filter"
289 " after adding 20, got %d\n",
290 ok2);
291 GNUNET_CONTAINER_bloomfilter_free (bf);
292 GNUNET_CONTAINER_bloomfilter_free (bfi);
293 return -1;
294 }
295
296
297 GNUNET_CONTAINER_bloomfilter_free (bf);
298 GNUNET_CONTAINER_bloomfilter_free (bfi);
299
300 GNUNET_break (0 == unlink (TESTFILE));
301 return 0;
302}
diff --git a/src/lib/util/test_container_dll.c b/src/lib/util/test_container_dll.c
new file mode 100644
index 000000000..fcbef4e8b
--- /dev/null
+++ b/src/lib/util/test_container_dll.c
@@ -0,0 +1,114 @@
1/*
2 This file is part of GNUnet.
3 Copyright (C) 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 * @author Christian Grothoff
23 * @file util/test_container_dll.c
24 * @brief Test of DLL operations
25 */
26
27
28#include "platform.h"
29#include "gnunet_util_lib.h"
30
31/**
32 * Element in the DLL.
33 */
34struct Element
35{
36 /**
37 * Required pointer to previous element.
38 */
39 struct Element *prev;
40
41 /**
42 * Required pointer to next element.
43 */
44 struct Element *next;
45
46 /**
47 * Used to sort.
48 */
49 unsigned int value;
50};
51
52
53/**
54 * Compare two elements.
55 *
56 * @param cls closure, NULL
57 * @param e1 an element of to sort
58 * @param e2 another element to sort
59 * @return #GNUNET_YES if @e1 < @e2, otherwise #GNUNET_NO
60 */
61static int
62cmp_elem (void *cls,
63 struct Element *e1,
64 struct Element *e2)
65{
66 if (e1->value == e2->value)
67 return 0;
68 return (e1->value < e2->value) ? 1 : -1;
69}
70
71
72int
73main (int argc, char **argv)
74{
75 unsigned int values[] = {
76 4, 5, 8, 6, 9, 3, 7, 2, 1, 0
77 };
78 struct Element *head = NULL;
79 struct Element *tail = NULL;
80 struct Element *e;
81 unsigned int want;
82
83 GNUNET_log_setup ("test-container-dll",
84 "WARNING",
85 NULL);
86 for (unsigned int off = 0;
87 0 != values[off];
88 off++)
89 {
90 e = GNUNET_new (struct Element);
91 e->value = values[off];
92 GNUNET_CONTAINER_DLL_insert_sorted (struct Element,
93 cmp_elem,
94 NULL,
95 head,
96 tail,
97 e);
98 }
99
100 want = 1;
101 while (NULL != (e = head))
102 {
103 GNUNET_assert (e->value == want);
104 GNUNET_CONTAINER_DLL_remove (head,
105 tail,
106 e);
107 GNUNET_free (e);
108 want++;
109 }
110 return 0;
111}
112
113
114/* end of test_container_heap.c */
diff --git a/src/lib/util/test_container_heap.c b/src/lib/util/test_container_heap.c
new file mode 100644
index 000000000..f11070ed5
--- /dev/null
+++ b/src/lib/util/test_container_heap.c
@@ -0,0 +1,294 @@
1/*
2 This file is part of GNUnet.
3 Copyright (C) 2008 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 * @author Nathan Evans
23 * @file util/test_container_heap.c
24 * @brief Test of heap operations
25 */
26
27
28#include "platform.h"
29#include "gnunet_util_lib.h"
30
31static int
32iterator_callback (void *cls,
33 struct GNUNET_CONTAINER_HeapNode *node,
34 void *element, GNUNET_CONTAINER_HeapCostType cost)
35{
36 return GNUNET_OK;
37}
38
39
40static int
41nstrcmp (const char *a, const char *b)
42{
43 GNUNET_assert (a != NULL);
44 GNUNET_assert (b != NULL);
45 return strcmp (a, b);
46}
47
48
49static int
50check ()
51{
52 struct GNUNET_CONTAINER_Heap *myHeap;
53 struct GNUNET_CONTAINER_HeapNode *n1;
54 struct GNUNET_CONTAINER_HeapNode *n2;
55 struct GNUNET_CONTAINER_HeapNode *n3;
56 struct GNUNET_CONTAINER_HeapNode *n4;
57 struct GNUNET_CONTAINER_HeapNode *n5;
58 struct GNUNET_CONTAINER_HeapNode *n6;
59 struct GNUNET_CONTAINER_HeapNode *n7;
60 struct GNUNET_CONTAINER_HeapNode *n8;
61 const char *r;
62
63 myHeap = GNUNET_CONTAINER_heap_create (GNUNET_CONTAINER_HEAP_ORDER_MIN);
64
65 // GNUNET_CONTAINER_heap_remove_root heap empty, taking if-branch
66 n1 = GNUNET_CONTAINER_heap_remove_root (myHeap);
67 GNUNET_assert (NULL == n1);
68
69 // GNUNET_CONTAINER_heap_peek heap empty, taking if-branch
70 n1 = GNUNET_CONTAINER_heap_peek (myHeap);
71 GNUNET_assert (NULL == n1);
72
73 // GNUNET_CONTAINER_heap_walk_get_next: heap empty, taking if-branch
74 n1 = GNUNET_CONTAINER_heap_walk_get_next (myHeap);
75 GNUNET_assert (NULL == n1);
76
77 n1 = GNUNET_CONTAINER_heap_insert (myHeap, "11", 11);
78 GNUNET_assert (NULL != n1);
79
80
81 // GNUNET_CONTAINER_heap_peek not empty, taking if-branch
82 n2 = NULL;
83 n2 = GNUNET_CONTAINER_heap_peek (myHeap);
84 GNUNET_assert (NULL != n2);
85
86 // GNUNET_CONTAINER_heap_walk_get_next: 1 element
87 n1 = NULL;
88 n1 = GNUNET_CONTAINER_heap_walk_get_next (myHeap);
89 GNUNET_assert (NULL != n1);
90
91 GNUNET_CONTAINER_heap_iterate (myHeap, &iterator_callback, NULL);
92 GNUNET_assert (1 == GNUNET_CONTAINER_heap_get_size (myHeap));
93 n2 = GNUNET_CONTAINER_heap_insert (myHeap, "78", 78);
94 GNUNET_assert (2 == GNUNET_CONTAINER_heap_get_size (myHeap));
95 GNUNET_assert (0 == strcmp ("78", GNUNET_CONTAINER_heap_remove_node (n2)));
96 GNUNET_assert (1 == GNUNET_CONTAINER_heap_get_size (myHeap));
97 GNUNET_CONTAINER_heap_iterate (myHeap, &iterator_callback, NULL);
98
99 n3 = GNUNET_CONTAINER_heap_insert (myHeap, "15", 5);
100 GNUNET_CONTAINER_heap_update_cost (n3, 15);
101 GNUNET_assert (2 == GNUNET_CONTAINER_heap_get_size (myHeap));
102 GNUNET_CONTAINER_heap_iterate (myHeap, &iterator_callback, NULL);
103
104 n4 = GNUNET_CONTAINER_heap_insert (myHeap, "50", 50);
105 GNUNET_CONTAINER_heap_update_cost (n4, 50);
106 GNUNET_assert (3 == GNUNET_CONTAINER_heap_get_size (myHeap));
107 GNUNET_CONTAINER_heap_iterate (myHeap, &iterator_callback, NULL);
108
109 n5 = GNUNET_CONTAINER_heap_insert (myHeap, "100", 100);
110 n6 = GNUNET_CONTAINER_heap_insert (myHeap, "30/200", 30);
111 GNUNET_assert (5 == GNUNET_CONTAINER_heap_get_size (myHeap));
112 GNUNET_CONTAINER_heap_remove_node (n5);
113 r = GNUNET_CONTAINER_heap_remove_root (myHeap); /* n1 */
114 GNUNET_assert (NULL != r);
115 GNUNET_assert (0 == strcmp ("11", r));
116 GNUNET_CONTAINER_heap_update_cost (n6, 200);
117 GNUNET_CONTAINER_heap_remove_node (n3);
118 r = GNUNET_CONTAINER_heap_remove_root (myHeap); /* n4 */
119 GNUNET_assert (NULL != r);
120 GNUNET_assert (0 == strcmp ("50", r));
121 r = GNUNET_CONTAINER_heap_remove_root (myHeap); /* n6 */
122 GNUNET_assert (NULL != r);
123 GNUNET_assert (0 == strcmp ("30/200", r));
124 GNUNET_assert (0 == GNUNET_CONTAINER_heap_get_size (myHeap));
125
126 GNUNET_CONTAINER_heap_destroy (myHeap);
127
128 // My additions to a complete testcase
129 // Testing a GNUNET_CONTAINER_HEAP_ORDER_MIN
130 // Testing remove_node
131
132 myHeap = GNUNET_CONTAINER_heap_create (GNUNET_CONTAINER_HEAP_ORDER_MIN);
133
134 n1 = GNUNET_CONTAINER_heap_insert (myHeap, "10", 10);
135 GNUNET_CONTAINER_heap_update_cost (n1, 15);
136
137 r = GNUNET_CONTAINER_heap_remove_node (n1);
138 GNUNET_assert (NULL != r);
139 GNUNET_assert (0 == strcmp ("10", r));
140
141 n1 = GNUNET_CONTAINER_heap_insert (myHeap, "10", 10);
142 n2 = GNUNET_CONTAINER_heap_insert (myHeap, "20", 10);
143
144 GNUNET_CONTAINER_heap_walk_get_next (myHeap);
145 r = GNUNET_CONTAINER_heap_remove_node (n2);
146 GNUNET_assert (NULL != r);
147 GNUNET_assert (0 == strcmp ("20", r));
148 r = GNUNET_CONTAINER_heap_remove_node (n1);
149 GNUNET_assert (NULL != r);
150 GNUNET_assert (0 == strcmp ("10", r));
151
152 n1 = GNUNET_CONTAINER_heap_insert (myHeap, "10", 10);
153 n2 = GNUNET_CONTAINER_heap_insert (myHeap, "20", 10);
154 n3 = GNUNET_CONTAINER_heap_insert (myHeap, "30", 10);
155
156 GNUNET_CONTAINER_heap_remove_node (n2);
157 GNUNET_CONTAINER_heap_remove_node (n1);
158 r = GNUNET_CONTAINER_heap_remove_root (myHeap);
159 GNUNET_assert (NULL != r);
160 GNUNET_assert (0 == strcmp ("30", r));
161
162 n1 = GNUNET_CONTAINER_heap_insert (myHeap, "10", 10);
163 n2 = GNUNET_CONTAINER_heap_insert (myHeap, "20", 10);
164 n3 = GNUNET_CONTAINER_heap_insert (myHeap, "30", 10);
165
166 GNUNET_CONTAINER_heap_remove_node (n2);
167 GNUNET_CONTAINER_heap_remove_node (n1);
168 r = GNUNET_CONTAINER_heap_remove_node (n3);
169 GNUNET_assert (NULL != r);
170 GNUNET_assert (0 == strcmp ("30", r));
171
172 n1 = GNUNET_CONTAINER_heap_insert (myHeap, "10", 10);
173 n2 = GNUNET_CONTAINER_heap_insert (myHeap, "20", 20);
174 n3 = GNUNET_CONTAINER_heap_insert (myHeap, "30", 30);
175
176 GNUNET_assert (0 == nstrcmp ("20", GNUNET_CONTAINER_heap_remove_node (n2)));
177 GNUNET_assert (0 ==
178 nstrcmp ("10", GNUNET_CONTAINER_heap_remove_root (myHeap)));
179 GNUNET_assert (0 ==
180 nstrcmp ("30", GNUNET_CONTAINER_heap_remove_root (myHeap)));
181
182 n1 = GNUNET_CONTAINER_heap_insert (myHeap, "10", 10);
183 n2 = GNUNET_CONTAINER_heap_insert (myHeap, "20", 20);
184 n3 = GNUNET_CONTAINER_heap_insert (myHeap, "30", 30);
185 n4 = GNUNET_CONTAINER_heap_insert (myHeap, "40", 40);
186 n5 = GNUNET_CONTAINER_heap_insert (myHeap, "50", 50);
187 n6 = GNUNET_CONTAINER_heap_insert (myHeap, "60", 60);
188
189 // Inserting nodes deeper in the tree with lower costs
190 n7 = GNUNET_CONTAINER_heap_insert (myHeap, "70", 10);
191 n8 = GNUNET_CONTAINER_heap_insert (myHeap, "80", 10);
192
193 GNUNET_assert (0 == nstrcmp ("30", GNUNET_CONTAINER_heap_remove_node (n3)));
194
195 // Cleaning up...
196 GNUNET_assert (0 == nstrcmp ("60", GNUNET_CONTAINER_heap_remove_node (n6)));
197 GNUNET_assert (0 == nstrcmp ("50", GNUNET_CONTAINER_heap_remove_node (n5)));
198
199 // Testing heap_walk_get_next
200 GNUNET_CONTAINER_heap_walk_get_next (myHeap);
201 GNUNET_CONTAINER_heap_walk_get_next (myHeap);
202 GNUNET_CONTAINER_heap_walk_get_next (myHeap);;
203 GNUNET_CONTAINER_heap_walk_get_next (myHeap);
204 GNUNET_CONTAINER_heap_walk_get_next (myHeap);
205
206 GNUNET_assert (0 == nstrcmp ("10", GNUNET_CONTAINER_heap_remove_node (n1)));
207 GNUNET_assert (0 == nstrcmp ("20", GNUNET_CONTAINER_heap_remove_node (n2)));
208 GNUNET_assert (0 == nstrcmp ("40", GNUNET_CONTAINER_heap_remove_node (n4)));
209 GNUNET_assert (0 == nstrcmp ("70", GNUNET_CONTAINER_heap_remove_node (n7)));
210 GNUNET_assert (0 == nstrcmp ("80", GNUNET_CONTAINER_heap_remove_node (n8)));
211
212 // End Testing remove_node
213
214 // Testing a GNUNET_CONTAINER_HEAP_ORDER_MAX
215 GNUNET_CONTAINER_heap_destroy (myHeap);
216
217 myHeap = GNUNET_CONTAINER_heap_create (GNUNET_CONTAINER_HEAP_ORDER_MAX);
218
219 n1 = GNUNET_CONTAINER_heap_insert (myHeap, "10", 10);
220 GNUNET_CONTAINER_heap_update_cost (n1, 15);
221
222 GNUNET_assert (0 == nstrcmp ("10", GNUNET_CONTAINER_heap_remove_node (n1)));
223
224 n1 = GNUNET_CONTAINER_heap_insert (myHeap, "10", 10);
225 n2 = GNUNET_CONTAINER_heap_insert (myHeap, "20", 10);
226
227 GNUNET_CONTAINER_heap_walk_get_next (myHeap);
228 GNUNET_assert (0 == nstrcmp ("20", GNUNET_CONTAINER_heap_remove_node (n2)));
229 GNUNET_assert (0 == nstrcmp ("10", GNUNET_CONTAINER_heap_remove_node (n1)));
230
231 n1 = GNUNET_CONTAINER_heap_insert (myHeap, "10", 10);
232 n2 = GNUNET_CONTAINER_heap_insert (myHeap, "20", 10);
233 n3 = GNUNET_CONTAINER_heap_insert (myHeap, "30", 10);
234
235 GNUNET_CONTAINER_heap_remove_node (n2);
236 GNUNET_CONTAINER_heap_remove_node (n1);
237 GNUNET_assert (0 ==
238 nstrcmp ("30", GNUNET_CONTAINER_heap_remove_root (myHeap)));
239
240 n1 = GNUNET_CONTAINER_heap_insert (myHeap, "10", 10);
241 n2 = GNUNET_CONTAINER_heap_insert (myHeap, "20", 10);
242 n3 = GNUNET_CONTAINER_heap_insert (myHeap, "30", 10);
243
244 GNUNET_CONTAINER_heap_remove_node (n2);
245 GNUNET_CONTAINER_heap_remove_node (n1);
246 GNUNET_assert (0 == nstrcmp ("30", GNUNET_CONTAINER_heap_remove_node (n3)));
247
248 n1 = GNUNET_CONTAINER_heap_insert (myHeap, "10", 10);
249 n2 = GNUNET_CONTAINER_heap_insert (myHeap, "20", 20);
250 n3 = GNUNET_CONTAINER_heap_insert (myHeap, "30", 30);
251 n4 = GNUNET_CONTAINER_heap_insert (myHeap, "40", 40);
252 n5 = GNUNET_CONTAINER_heap_insert (myHeap, "50", 50);
253 n6 = GNUNET_CONTAINER_heap_insert (myHeap, "60", 60);
254
255 // Inserting nodes deeper in the tree with lower costs
256 n7 = GNUNET_CONTAINER_heap_insert (myHeap, "70", 10);
257 n8 = GNUNET_CONTAINER_heap_insert (myHeap, "80", 10);
258
259 GNUNET_assert (0 == nstrcmp ("30", GNUNET_CONTAINER_heap_remove_node (n3)));
260
261 // Cleaning up...
262 GNUNET_assert (0 == nstrcmp ("60", GNUNET_CONTAINER_heap_remove_node (n6)));
263 GNUNET_assert (0 == nstrcmp ("50", GNUNET_CONTAINER_heap_remove_node (n5)));
264
265 // Testing heap_walk_get_next
266 GNUNET_CONTAINER_heap_walk_get_next (myHeap);
267 GNUNET_CONTAINER_heap_walk_get_next (myHeap);
268 GNUNET_CONTAINER_heap_walk_get_next (myHeap);;
269 GNUNET_CONTAINER_heap_walk_get_next (myHeap);
270 GNUNET_CONTAINER_heap_walk_get_next (myHeap);
271
272 GNUNET_assert (0 == nstrcmp ("10", GNUNET_CONTAINER_heap_remove_node (n1)));
273 GNUNET_assert (0 == nstrcmp ("20", GNUNET_CONTAINER_heap_remove_node (n2)));
274 GNUNET_assert (0 == nstrcmp ("40", GNUNET_CONTAINER_heap_remove_node (n4)));
275 GNUNET_assert (0 == nstrcmp ("70", GNUNET_CONTAINER_heap_remove_node (n7)));
276 GNUNET_assert (0 == nstrcmp ("80", GNUNET_CONTAINER_heap_remove_node (n8)));
277
278 // End Testing remove_node
279
280 GNUNET_CONTAINER_heap_destroy (myHeap);
281
282 return 0;
283}
284
285
286int
287main (int argc, char **argv)
288{
289 GNUNET_log_setup ("test-container-heap", "WARNING", NULL);
290 return check ();
291}
292
293
294/* end of test_container_heap.c */
diff --git a/src/lib/util/test_container_multihashmap.c b/src/lib/util/test_container_multihashmap.c
new file mode 100644
index 000000000..233369257
--- /dev/null
+++ b/src/lib/util/test_container_multihashmap.c
@@ -0,0 +1,140 @@
1/*
2 This file is part of GNUnet.
3 Copyright (C) 2008 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/test_container_multihashmap.c
23 * @brief Test for container_multihashmap.c
24 * @author Christian Grothoff
25 */
26
27
28#include "platform.h"
29#include "gnunet_util_lib.h"
30
31#define ABORT() { fprintf (stderr, "Error at %s:%d\n", __FILE__, __LINE__); \
32 if (m != NULL) GNUNET_CONTAINER_multihashmap_destroy (m); \
33 if (NULL != \
34 iter) \
35 GNUNET_CONTAINER_multihashmap_iterator_destroy (iter); \
36 return 1; }
37#define CHECK(c) { if (! (c)) ABORT (); }
38
39static int
40testMap (int i)
41{
42 struct GNUNET_CONTAINER_MultiHashMap *m;
43 struct GNUNET_HashCode k1;
44 struct GNUNET_HashCode k2;
45 struct GNUNET_CONTAINER_MultiHashMapIterator *iter = NULL;
46 struct GNUNET_HashCode key_ret;
47 const char *ret;
48 int j;
49
50 CHECK (NULL != (m = GNUNET_CONTAINER_multihashmap_create (i, GNUNET_NO)));
51 memset (&k1, 0, sizeof(k1));
52 memset (&k2, 1, sizeof(k2));
53 CHECK (GNUNET_NO == GNUNET_CONTAINER_multihashmap_contains (m, &k1));
54 CHECK (GNUNET_NO == GNUNET_CONTAINER_multihashmap_contains (m, &k2));
55 CHECK (GNUNET_NO == GNUNET_CONTAINER_multihashmap_remove (m, &k1, NULL));
56 CHECK (GNUNET_NO == GNUNET_CONTAINER_multihashmap_remove (m, &k2, NULL));
57 CHECK (NULL == GNUNET_CONTAINER_multihashmap_get (m, &k1));
58 CHECK (NULL == GNUNET_CONTAINER_multihashmap_get (m, &k2));
59 CHECK (0 == GNUNET_CONTAINER_multihashmap_remove_all (m, &k1));
60 CHECK (0 == GNUNET_CONTAINER_multihashmap_size (m));
61 CHECK (0 == GNUNET_CONTAINER_multihashmap_iterate (m, NULL, NULL));
62 CHECK (0 == GNUNET_CONTAINER_multihashmap_get_multiple (m, &k1, NULL, NULL));
63
64 CHECK (GNUNET_OK ==
65 GNUNET_CONTAINER_multihashmap_put (m, &k1, "v1",
66 GNUNET_CONTAINER_MULTIHASHMAPOPTION_REPLACE));
67 CHECK (1 == GNUNET_CONTAINER_multihashmap_size (m));
68 ret = GNUNET_CONTAINER_multihashmap_get (m, &k1);
69 GNUNET_assert (ret != NULL);
70 CHECK (0 == strcmp ("v1", ret));
71 CHECK (GNUNET_NO ==
72 GNUNET_CONTAINER_multihashmap_put (m, &k1, "v1",
73 GNUNET_CONTAINER_MULTIHASHMAPOPTION_REPLACE));
74 CHECK (1 == GNUNET_CONTAINER_multihashmap_size (m));
75 CHECK (GNUNET_OK ==
76 GNUNET_CONTAINER_multihashmap_put (m, &k1, "v2",
77 GNUNET_CONTAINER_MULTIHASHMAPOPTION_MULTIPLE));
78 CHECK (GNUNET_OK ==
79 GNUNET_CONTAINER_multihashmap_put (m, &k1, "v3",
80 GNUNET_CONTAINER_MULTIHASHMAPOPTION_MULTIPLE));
81 CHECK (3 == GNUNET_CONTAINER_multihashmap_size (m));
82 CHECK (GNUNET_OK == GNUNET_CONTAINER_multihashmap_remove (m, &k1, "v3"));
83 CHECK (2 == GNUNET_CONTAINER_multihashmap_size (m));
84 CHECK (GNUNET_YES == GNUNET_CONTAINER_multihashmap_contains (m, &k1));
85 CHECK (GNUNET_NO == GNUNET_CONTAINER_multihashmap_contains (m, &k2));
86 CHECK (2 == GNUNET_CONTAINER_multihashmap_get_multiple (m, &k1, NULL, NULL));
87 CHECK (0 == GNUNET_CONTAINER_multihashmap_get_multiple (m, &k2, NULL, NULL));
88 CHECK (2 == GNUNET_CONTAINER_multihashmap_iterate (m, NULL, NULL));
89 iter = GNUNET_CONTAINER_multihashmap_iterator_create (m);
90 CHECK (GNUNET_YES == GNUNET_CONTAINER_multihashmap_iterator_next (iter,
91 &key_ret,
92 (const
93 void **) &
94 ret));
95 CHECK (0 == memcmp (&key_ret, &k1, sizeof(key_ret)));
96 CHECK (GNUNET_YES == GNUNET_CONTAINER_multihashmap_iterator_next (iter,
97 &key_ret,
98 (const
99 void **) &
100 ret));
101 CHECK (0 == memcmp (&key_ret, &k1, sizeof(key_ret)));
102 CHECK (GNUNET_NO == GNUNET_CONTAINER_multihashmap_iterator_next (iter, NULL,
103 NULL));
104 GNUNET_free (iter);
105
106 CHECK (2 == GNUNET_CONTAINER_multihashmap_remove_all (m, &k1));
107 for (j = 0; j < 1024; j++)
108 CHECK (GNUNET_OK ==
109 GNUNET_CONTAINER_multihashmap_put (m, &k1, "v2",
110 GNUNET_CONTAINER_MULTIHASHMAPOPTION_MULTIPLE));
111 iter = GNUNET_CONTAINER_multihashmap_iterator_create (m);
112 for (j = 0; j < GNUNET_CONTAINER_multihashmap_size (m); j++)
113 CHECK (GNUNET_YES == GNUNET_CONTAINER_multihashmap_iterator_next (iter,
114 NULL,
115 NULL));
116 CHECK (GNUNET_NO == GNUNET_CONTAINER_multihashmap_iterator_next (iter, NULL,
117 NULL));
118 GNUNET_CONTAINER_multihashmap_iterator_destroy (iter);
119
120 GNUNET_CONTAINER_multihashmap_destroy (m);
121 return 0;
122}
123
124
125int
126main (int argc, char *argv[])
127{
128 int failureCount = 0;
129 int i;
130
131 GNUNET_log_setup ("test-container-multihashmap", "WARNING", NULL);
132 for (i = 1; i < 255; i++)
133 failureCount += testMap (i);
134 if (failureCount != 0)
135 return 1;
136 return 0;
137}
138
139
140/* end of test_container_multihashmap.c */
diff --git a/src/lib/util/test_container_multihashmap32.c b/src/lib/util/test_container_multihashmap32.c
new file mode 100644
index 000000000..eab5ad795
--- /dev/null
+++ b/src/lib/util/test_container_multihashmap32.c
@@ -0,0 +1,110 @@
1/*
2 This file is part of GNUnet.
3 Copyright (C) 2008 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/test_container_multihashmap32.c
23 * @brief Test for container_multihashmap32.c
24 * @author Christian Grothoff
25 * @author Sree Harsha Totakura
26 */
27
28
29#include "platform.h"
30#include "gnunet_util_lib.h"
31
32#define ABORT() { fprintf (stderr, "Error at %s:%d\n", __FILE__, __LINE__); \
33 if (m != NULL) GNUNET_CONTAINER_multihashmap32_destroy (m); \
34 return 1; }
35#define CHECK(c) { if (! (c)) ABORT (); }
36
37static int
38testMap (int i)
39{
40 struct GNUNET_CONTAINER_MultiHashMap32 *m;
41 uint32_t k1;
42 uint32_t k2;
43 const char *ret;
44 int j;
45
46 CHECK (NULL != (m = GNUNET_CONTAINER_multihashmap32_create (i)));
47 k1 = 0;
48 k2 = UINT32_MAX;
49 CHECK (GNUNET_NO == GNUNET_CONTAINER_multihashmap32_contains (m, k1));
50 CHECK (GNUNET_NO == GNUNET_CONTAINER_multihashmap32_contains (m, k2));
51 CHECK (GNUNET_NO == GNUNET_CONTAINER_multihashmap32_remove (m, k1, NULL));
52 CHECK (GNUNET_NO == GNUNET_CONTAINER_multihashmap32_remove (m, k2, NULL));
53 CHECK (NULL == GNUNET_CONTAINER_multihashmap32_get (m, k1));
54 CHECK (NULL == GNUNET_CONTAINER_multihashmap32_get (m, k2));
55 CHECK (0 == GNUNET_CONTAINER_multihashmap32_remove_all (m, k1));
56 CHECK (0 == GNUNET_CONTAINER_multihashmap32_size (m));
57 CHECK (0 == GNUNET_CONTAINER_multihashmap32_iterate (m, NULL, NULL));
58 CHECK (0 == GNUNET_CONTAINER_multihashmap32_get_multiple (m, k1, NULL, NULL));
59
60 CHECK (GNUNET_OK ==
61 GNUNET_CONTAINER_multihashmap32_put (m, k1, "v1",
62 GNUNET_CONTAINER_MULTIHASHMAPOPTION_REPLACE));
63 CHECK (1 == GNUNET_CONTAINER_multihashmap32_size (m));
64 ret = GNUNET_CONTAINER_multihashmap32_get (m, k1);
65 GNUNET_assert (ret != NULL);
66 CHECK (0 == strcmp ("v1", ret));
67 CHECK (GNUNET_NO ==
68 GNUNET_CONTAINER_multihashmap32_put (m, k1, "v1",
69 GNUNET_CONTAINER_MULTIHASHMAPOPTION_REPLACE));
70 CHECK (1 == GNUNET_CONTAINER_multihashmap32_size (m));
71 CHECK (GNUNET_OK ==
72 GNUNET_CONTAINER_multihashmap32_put (m, k1, "v2",
73 GNUNET_CONTAINER_MULTIHASHMAPOPTION_MULTIPLE));
74 CHECK (GNUNET_OK ==
75 GNUNET_CONTAINER_multihashmap32_put (m, k1, "v3",
76 GNUNET_CONTAINER_MULTIHASHMAPOPTION_MULTIPLE));
77 CHECK (3 == GNUNET_CONTAINER_multihashmap32_size (m));
78 CHECK (GNUNET_OK == GNUNET_CONTAINER_multihashmap32_remove (m, k1, "v3"));
79 CHECK (2 == GNUNET_CONTAINER_multihashmap32_size (m));
80 CHECK (GNUNET_YES == GNUNET_CONTAINER_multihashmap32_contains (m, k1));
81 CHECK (GNUNET_NO == GNUNET_CONTAINER_multihashmap32_contains (m, k2));
82 CHECK (2 == GNUNET_CONTAINER_multihashmap32_get_multiple (m, k1, NULL, NULL));
83 CHECK (0 == GNUNET_CONTAINER_multihashmap32_get_multiple (m, k2, NULL, NULL));
84 CHECK (2 == GNUNET_CONTAINER_multihashmap32_iterate (m, NULL, NULL));
85 CHECK (2 == GNUNET_CONTAINER_multihashmap32_remove_all (m, k1));
86 for (j = 0; j < 1024; j++)
87 CHECK (GNUNET_OK ==
88 GNUNET_CONTAINER_multihashmap32_put (m, k1, "v2",
89 GNUNET_CONTAINER_MULTIHASHMAPOPTION_MULTIPLE));
90 GNUNET_CONTAINER_multihashmap32_destroy (m);
91 return 0;
92}
93
94
95int
96main (int argc, char *argv[])
97{
98 int failureCount = 0;
99 int i;
100
101 GNUNET_log_setup ("test-container-multihashmap", "WARNING", NULL);
102 for (i = 1; i < 255; i++)
103 failureCount += testMap (i);
104 if (failureCount != 0)
105 return 1;
106 return 0;
107}
108
109
110/* end of test_container_multihashmap.c */
diff --git a/src/lib/util/test_container_multipeermap.c b/src/lib/util/test_container_multipeermap.c
new file mode 100644
index 000000000..6639b05ea
--- /dev/null
+++ b/src/lib/util/test_container_multipeermap.c
@@ -0,0 +1,140 @@
1/*
2 This file is part of GNUnet.
3 Copyright (C) 2008, 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/test_container_multipeermap.c
23 * @brief Test for container_multipeermap.c
24 * @author Christian Grothoff
25 */
26
27
28#include "platform.h"
29#include "gnunet_util_lib.h"
30
31#define ABORT() { fprintf (stderr, "Error at %s:%d\n", __FILE__, __LINE__); \
32 if (NULL != m) GNUNET_CONTAINER_multipeermap_destroy (m); \
33 if (NULL != \
34 iter) \
35 GNUNET_CONTAINER_multipeermap_iterator_destroy (iter); \
36 return 1; }
37#define CHECK(c) { if (! (c)) ABORT (); }
38
39static int
40testMap (int i)
41{
42 struct GNUNET_CONTAINER_MultiPeerMap *m;
43 struct GNUNET_PeerIdentity k1;
44 struct GNUNET_PeerIdentity k2;
45 struct GNUNET_CONTAINER_MultiPeerMapIterator *iter = NULL;
46 struct GNUNET_PeerIdentity key_ret;
47 const char *ret;
48 int j;
49
50 CHECK (NULL != (m = GNUNET_CONTAINER_multipeermap_create (i, GNUNET_NO)));
51 memset (&k1, 0, sizeof(k1));
52 memset (&k2, 1, sizeof(k2));
53 CHECK (GNUNET_NO == GNUNET_CONTAINER_multipeermap_contains (m, &k1));
54 CHECK (GNUNET_NO == GNUNET_CONTAINER_multipeermap_contains (m, &k2));
55 CHECK (GNUNET_NO == GNUNET_CONTAINER_multipeermap_remove (m, &k1, NULL));
56 CHECK (GNUNET_NO == GNUNET_CONTAINER_multipeermap_remove (m, &k2, NULL));
57 CHECK (NULL == GNUNET_CONTAINER_multipeermap_get (m, &k1));
58 CHECK (NULL == GNUNET_CONTAINER_multipeermap_get (m, &k2));
59 CHECK (0 == GNUNET_CONTAINER_multipeermap_remove_all (m, &k1));
60 CHECK (0 == GNUNET_CONTAINER_multipeermap_size (m));
61 CHECK (0 == GNUNET_CONTAINER_multipeermap_iterate (m, NULL, NULL));
62 CHECK (0 == GNUNET_CONTAINER_multipeermap_get_multiple (m, &k1, NULL, NULL));
63
64 CHECK (GNUNET_OK ==
65 GNUNET_CONTAINER_multipeermap_put (m, &k1, "v1",
66 GNUNET_CONTAINER_MULTIHASHMAPOPTION_REPLACE));
67 CHECK (1 == GNUNET_CONTAINER_multipeermap_size (m));
68 ret = GNUNET_CONTAINER_multipeermap_get (m, &k1);
69 GNUNET_assert (ret != NULL);
70 CHECK (0 == strcmp ("v1", ret));
71 CHECK (GNUNET_NO ==
72 GNUNET_CONTAINER_multipeermap_put (m, &k1, "v1",
73 GNUNET_CONTAINER_MULTIHASHMAPOPTION_REPLACE));
74 CHECK (1 == GNUNET_CONTAINER_multipeermap_size (m));
75 CHECK (GNUNET_OK ==
76 GNUNET_CONTAINER_multipeermap_put (m, &k1, "v2",
77 GNUNET_CONTAINER_MULTIHASHMAPOPTION_MULTIPLE));
78 CHECK (GNUNET_OK ==
79 GNUNET_CONTAINER_multipeermap_put (m, &k1, "v3",
80 GNUNET_CONTAINER_MULTIHASHMAPOPTION_MULTIPLE));
81 CHECK (3 == GNUNET_CONTAINER_multipeermap_size (m));
82 CHECK (GNUNET_OK == GNUNET_CONTAINER_multipeermap_remove (m, &k1, "v3"));
83 CHECK (2 == GNUNET_CONTAINER_multipeermap_size (m));
84 CHECK (GNUNET_YES == GNUNET_CONTAINER_multipeermap_contains (m, &k1));
85 CHECK (GNUNET_NO == GNUNET_CONTAINER_multipeermap_contains (m, &k2));
86 CHECK (2 == GNUNET_CONTAINER_multipeermap_get_multiple (m, &k1, NULL, NULL));
87 CHECK (0 == GNUNET_CONTAINER_multipeermap_get_multiple (m, &k2, NULL, NULL));
88 CHECK (2 == GNUNET_CONTAINER_multipeermap_iterate (m, NULL, NULL));
89 iter = GNUNET_CONTAINER_multipeermap_iterator_create (m);
90 CHECK (GNUNET_YES == GNUNET_CONTAINER_multipeermap_iterator_next (iter,
91 &key_ret,
92 (const
93 void **) &
94 ret));
95 CHECK (0 == memcmp (&key_ret, &k1, sizeof(key_ret)));
96 CHECK (GNUNET_YES == GNUNET_CONTAINER_multipeermap_iterator_next (iter,
97 &key_ret,
98 (const
99 void **) &
100 ret));
101 CHECK (0 == memcmp (&key_ret, &k1, sizeof(key_ret)));
102 CHECK (GNUNET_NO == GNUNET_CONTAINER_multipeermap_iterator_next (iter, NULL,
103 NULL));
104 GNUNET_free (iter);
105
106 CHECK (2 == GNUNET_CONTAINER_multipeermap_remove_all (m, &k1));
107 for (j = 0; j < 1024; j++)
108 CHECK (GNUNET_OK ==
109 GNUNET_CONTAINER_multipeermap_put (m, &k1, "v2",
110 GNUNET_CONTAINER_MULTIHASHMAPOPTION_MULTIPLE));
111 iter = GNUNET_CONTAINER_multipeermap_iterator_create (m);
112 for (j = 0; j < GNUNET_CONTAINER_multipeermap_size (m); j++)
113 CHECK (GNUNET_YES == GNUNET_CONTAINER_multipeermap_iterator_next (iter,
114 NULL,
115 NULL));
116 CHECK (GNUNET_NO == GNUNET_CONTAINER_multipeermap_iterator_next (iter, NULL,
117 NULL));
118 GNUNET_CONTAINER_multipeermap_iterator_destroy (iter);
119
120 GNUNET_CONTAINER_multipeermap_destroy (m);
121 return 0;
122}
123
124
125int
126main (int argc, char *argv[])
127{
128 int failureCount = 0;
129 int i;
130
131 GNUNET_log_setup ("test-container-multipeermap", "WARNING", NULL);
132 for (i = 1; i < 255; i++)
133 failureCount += testMap (i);
134 if (failureCount != 0)
135 return 1;
136 return 0;
137}
138
139
140/* end of test_container_multipeermap.c */
diff --git a/src/lib/util/test_crypto_blind.c b/src/lib/util/test_crypto_blind.c
new file mode 100644
index 000000000..1b256fca3
--- /dev/null
+++ b/src/lib/util/test_crypto_blind.c
@@ -0,0 +1,87 @@
1/*
2 This file is part of GNUnet
3 Copyright (C) 2014, 2015, 2023 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/test_crypto_blind.c
23 * @brief testcase for utility functions for blind signatures
24 * @author Christian Grothoff <grothoff@gnunet.org>
25 */
26#include "platform.h"
27#include <gcrypt.h>
28#include "gnunet_util_lib.h"
29
30
31int
32main (int argc,
33 char *argv[])
34{
35 struct GNUNET_CRYPTO_BlindSignPrivateKey *priv;
36 struct GNUNET_CRYPTO_BlindSignPublicKey *pub;
37 struct GNUNET_CRYPTO_BlindingInputValues *biv;
38 struct GNUNET_CRYPTO_BlindedMessage *bm;
39 struct GNUNET_CRYPTO_BlindedSignature *bsig;
40 struct GNUNET_CRYPTO_UnblindedSignature *sig;
41 union GNUNET_CRYPTO_BlindingSecretP bsec;
42 union GNUNET_CRYPTO_BlindSessionNonce nonce;
43
44 GNUNET_log_setup ("test-crypto-blind",
45 "WARNING",
46 NULL);
47 GNUNET_CRYPTO_random_block (GNUNET_CRYPTO_QUALITY_WEAK,
48 &bsec,
49 sizeof (bsec));
50 GNUNET_CRYPTO_random_block (GNUNET_CRYPTO_QUALITY_WEAK,
51 &nonce,
52 sizeof (nonce));
53 GNUNET_assert (GNUNET_OK ==
54 GNUNET_CRYPTO_blind_sign_keys_create (&priv,
55 &pub,
56 GNUNET_CRYPTO_BSA_CS));
57 biv = GNUNET_CRYPTO_get_blinding_input_values (priv,
58 &nonce,
59 "salt");
60 bm = GNUNET_CRYPTO_message_blind_to_sign (pub,
61 &bsec,
62 &nonce,
63 "hello",
64 5,
65 biv);
66 bsig = GNUNET_CRYPTO_blind_sign (priv,
67 "salt",
68 bm);
69 sig = GNUNET_CRYPTO_blind_sig_unblind (bsig,
70 &bsec,
71 "hello",
72 5,
73 biv,
74 pub);
75 GNUNET_assert (GNUNET_OK ==
76 GNUNET_CRYPTO_blind_sig_verify (pub,
77 sig,
78 "hello",
79 5));
80 GNUNET_CRYPTO_blinding_input_values_decref (biv);
81 GNUNET_CRYPTO_blinded_sig_decref (bsig);
82 GNUNET_CRYPTO_unblinded_sig_decref (sig);
83 GNUNET_CRYPTO_blinded_message_decref (bm);
84 GNUNET_CRYPTO_blind_sign_priv_decref (priv);
85 GNUNET_CRYPTO_blind_sign_pub_decref (pub);
86 return 0;
87}
diff --git a/src/lib/util/test_crypto_crc.c b/src/lib/util/test_crypto_crc.c
new file mode 100644
index 000000000..d95eaf260
--- /dev/null
+++ b/src/lib/util/test_crypto_crc.c
@@ -0,0 +1,221 @@
1/*
2 This file is part of GNUnet.
3 Copyright (C) 2001, 2002, 2003, 2004, 2006 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 For the actual CRC code:
21 Copyright abandoned; this code is in the public domain.
22 Provided to GNUnet by peter@horizon.com
23 */
24
25/**
26 * @file util/test_crypto_crc.c
27 * @brief testcase for crypto_crc.c
28 */
29
30#include "platform.h"
31#include "gnunet_util_lib.h"
32
33static int expected[] = {
34 -1223996378, 929797997, -1048047323, 1791081351, -425765913, 2138425902,
35 82584863, 1939615314, 1806463044, -1505003452, 1878277636, -997353517,
36 201238705, 1723258694, -1107452366, -344562561, -1102247383, 1973035265,
37 715213337, -1886586005, 2021214515, -1387332962, 593019378, -571088044,
38 1412577760, 412164558, -1626111170, 1556494863, -289796528, -850404775,
39 2066714587, -911838105, -1426027382, 499684507, -835420055, 1817119454,
40 -1221795958, 1516966784, -1038806877, -2115880691, 532627620, 1984437415,
41 -396341583, -1345366324, -590766745, -1801923449, 1752427988, -386896390,
42 453906317, 1552589433, -858925718, 1160445643, -740188079, -486609040,
43 1102529269, -515846212, -1614217202, 1572162207, 943558923, -467330358,
44 -1870764193, 1477005328, -793029208, -888983175, -696956020, 842706021,
45 1642390067, -805889494, 1284862057, 1562545388, 2091626273, 1852404553,
46 -2076508101, 370903003, 1186422975, 1936085227, 769358463, 180401058,
47 2032612572, -105461719, -1119935472, 617249831, 1169304728, 1771205256,
48 -2042554284, 653270859, -918610713, 336081663, -913685370, 1962213744,
49 -505406126, -838622649, -1141518710, 893143582, -1330296611, 122119483,
50 1111564496, 688811976, 1016241049, -1803438473, 359630107, 1034798954,
51 -581359286, 1590946527, -389997034, 2020318460, 1695967527, -464069727,
52 -862641495, -1405012109, -771244841, 738226150, -1035328134, -933945474,
53 1254965774, 1661863830, -884127998, 1800460481, 814702567, -1214068102,
54 -541120421, 1898656429, -236825530, 1505866267, 1252462132, -981007520,
55 1502096471, -2134644056, 483221797, 1276403836, 541133290, -1234093967,
56 350748780, 257941070, 1030457090, 434988890, -1098135432, -1000556640,
57 -577128022, 644806294, -787536281, -1288346343, 998079404, 1259353935,
58 955771631, -958377466, 1746756252, 451579658, 1913409243, -952026299,
59 -1556035958, -830279881, 834744289, -1878491428, 700000962, -1027245802,
60 1393574384, -1260409147, -841420884, 892132797, 1494730226, -1649181766,
61 1651097838, -1041807403, -1916675721, -1324525963, 157405899, -655788033,
62 -1943555237, -79747022, 339721623, -138341083, 1111902411, -435322914,
63 -533294200, -190220608, -1718346014, -1631301894, 1706265243, 745533899,
64 1351941230, 1803009594, -1218191958, 1467751062, 84368433, -711251880,
65 1699423788, -768792716, 846639904, 2103267723, -2095288070, -440571408,
66 -362144485, 2020468971, 352105963, -849211036, -1272592429, 1743440467,
67 2020667861, -1649992312, 172682343, 816705364, -1990206923, 902689869,
68 -298510060, 164207498, 190378213, 242531543, 113383268, 304810777,
69 -1081099373, 819221134, -1100982926, -855941239, 1091308887, -934548124,
70 520508733, -1381763773, -491593287, -2143492665, 700894653, -2049034808,
71 -160942046, -2009323577, 1464245054, 1584746011, -768646852, -993282698,
72 1265838699, -1873820824, 575704373, -986682955, 1270688416, 88587481,
73 -1723991633, -409928242, 866669946, -483811323, -181759253, -963525431,
74 -1686612238, -1663460076, -1128449775, -1368922329, 122318131, 795862385,
75 528576131, -19927090, 1369299478, 1285665642, -738964611, 1328292127,
76 552041252, -1431494354, -1205275362, 42768297, -1329537238, -449177266,
77 943925221, 987016465, -945138414, -270064876, 1650366626, -369252552,
78 582030210, -1229235374, 147901387, -517510506, -1609742888, -1086838308,
79 1391998445, -313975512, -613392078, 855706229, 1475706341, -1112105406,
80 2032001400, 1565777625, 2030937777, 435522421, 1823527907, -691390605,
81 -827253664, 1057171580, -314146639, -630099999, -1347514552, 478716232,
82 -1533658804, -1425371979, 761987780, 1560243817, -1945893959, 1205759225,
83 -959343783, -576742354, -154125407, -1158108776, 1183788580, 1354198127,
84 -1534207721, -823991517, -170534462, -912524170, 1858513573, 467072185,
85 2091040157, -1765027018, -1659401643, -1173890143, -1912754057, -84568053,
86 2010781784, -921970156, 944508352, -922040609, 1055102010, 1018688871,
87 -1186761311, -2012263648, 1311654161, 277659086, 2029602288, 1127061510,
88 1029452642, 285677123, -188521091, -641039012, 653836416, -805916340,
89 -1644860596, 1352872213, 691634876, -1477113308, -748430369, 1030697363,
90 -2007864449, -1196662616, 1313997192, 177342476, -566676450, -1118618118,
91 1697953104, 344671484, -1489783116, -889507873, 1259591310, -716567168,
92 2116447062, 324368527, 1789366816, 1558930442, 1950250221, -785460151,
93 1174714258, -430047304, -859487565, -580633932, 607732845, -1128150220,
94 1544355315, 1460298016, -1771194297, 1215703690, 277231808, -416020628,
95 -418936577, -1724839216, 404731389, 1058730508, -1508366681, 229883053,
96 -572310243, 1883189553, 931286849, 1659300867, -94236383, -241524462,
97 548020458, -302406981, 579986475, 73468197, -984957614, 1554382245,
98 2084807492, -1456802798, -1105192593, 629440327, -16313961, -2102585261,
99 1873675206, 161035128, 1497033351, 1990150811, -499405222, 304019482,
100 41935663, -805987182, -571699268, 1748462913, 2096239823, -116359807,
101 -1871127553, -1074832534, -1558866192, 231353861, 2122854560, -2102323721,
102 -281462361, -343403210, -673268171, 1776058383, 1581561150, 2059580579,
103 768848632, 1347190372, -1701705879, 245282007, -563267886, -592558289,
104 1662399958, 1390406821, -1522485580, -706446863, 2069516289, -301855859,
105 -778346387, -1454093198, 1249083752, -1760506745, 262193320, 630751125,
106 -1495939124, -29980580, -1989626563, 659039376, -329477132, -1003507166,
107 -1322549020, 358606508, -2052572059, 1848014133, 1826958586, -1004948862,
108 -1775370541, 2134177912, -1739214473, 1892700918, 926629675, -1042761322,
109 2020075900, 606370962, -1256609305, 117577265, -586848924, 191368285,
110 1653535275, -1329269701, -375879127, -1089901406, 1206489978, 534223924,
111 -1042752982, -1178316881, -445594741, -1501682065, -1598136839,
112 -467688289, 750784023, 1781080461, 1729380226, 16906088, 862168532,
113 -2037752683, 1455274138, -1491220107, 1058323960, 1711530558, 1355062750,
114 227640096, 396568027, -173579098, -408975801, -993618329, -1470751562,
115 371076647, 209563718, 2015405719, -723460281, -1423934420, -2089643958,
116 353260489, 2084264341, -792676687, 701391030, -1440658244, 1479321011,
117 1907822880, 1232524257, -256712289, 401077577, 621808069, 868263613,
118 1244930119, 2020996902, 117483907, 1341376744, -1936988014, -445200547,
119 -843751811, -435291191, 1041695743, 476132726, -1226874735, -1436046747,
120 -297047422, 1739645396, 1948680937, -718144374, 1141983978, 1673650568,
121 -197244350, 1604464002, 1424069853, -485626505, 1708710014, -849136541,
122 1573778103, 530360999, 1777767203, 1376958336, -1088364352, 1826167753,
123 742735448, -1386211659, -1991323164, -444115655, -443055378, -1586901006,
124 -1741686587, 1925818034, -2118916824, 803890920, -1481793154, 992278937,
125 1302616410, 444517030, 1393144770, -2025632978, 1902300505, -1683582981,
126 800654133, 873850324, -619580878, -2002070410, -2024936385, 1978986634,
127 2012024264, 675768872, 389435615, -867217540, 231209167, -303917385,
128 1445676969, -1385982721, 1310476490, 580273453, -160600202, -1330895874,
129 487110497, 1124384798, 227637416, -1829783306, 1014818058, -1336870683,
130 -1042199518, -468525587, -1186267363, -472843891, 1215617600, -2056648329,
131 -873216891, 156780951, -1883246047, -842549253, -717684332, 760531638,
132 1074787431, 786267513, 814031289, -561255343, -110302255, -1837376592,
133 989669060, -81350614, 546038730, 222899882, 1298746805, 1791615733,
134 1565630269, 1516024174, 421691479, 1860326051, -1973359550, 1854393443,
135 -1401468528, -158562295, 1509929255, -124024738, -462937489, 259890715,
136 -1515121317, -289511197, -913738664, 698079062, -1631229382, -507275144,
137 1897739663, -1118192766, -1687033399, 61405556, -1913606579, -473308896,
138 -259107170, -576944609, -1689355510, 322156799, 545090192, 127425176,
139 -1815211748, -2070235628, -1172529316, 599259550, -910906653, 1797380363,
140 -938649427, 142991392, 504559631, 1208867355, -807699247, -616021271,
141 -254935281, -57151221, -1095534993, 1998380318, 1772459584, 713271407,
142 -1197898266, 808881935, -308133481, -1314455137, 284321772, -743117625,
143 -1622364240, -1667535152, 118713606, 1053615347, -2072876023, -178189072,
144 -828319551, 2047304928, -1311435786, -1970672907, -747972100, 86806159,
145 -436088421, 1464645587, 735840899, 32600466, -190473426, -735703440,
146 482872155, 475662392, -713681085, 1424078728, -150668609, -1137197868,
147 -1682762563, -48035649, 1143959866, -1542015129, 284920371, -1587695586,
148 -625236551, -753893357, -433976266, -1329796037, -1636712478, 1686783454,
149 27839146, 1748631474, -879528256, 2057796026, 773734654, 112269667,
150 -2011541314, 1517797297, -1943171794, 268166111, -1037010413, -1945824504,
151 -1672323792, 306260758, -692968628, -701704965, -462980996, 939188824,
152 553289792, 1790245000, 2093793129, -658085781, -186055037, -2130433650,
153 -1013235433, 1190870089, -2126586963, -1509655742, -1291895256,
154 -1427857845, 309538950, 388316741, 259659733, -1895092434, 110126220,
155 -170175575, -419430224, -696234084, -832170948, -353431720, -797675726,
156 -1644136054, 715163272, -1305904349, -145786463, -99586244, -695450446,
157 -871327102, -725496060, 952863853, -688441983, -1729929460, -103732092,
158 1059054528, 568873585, -982665223, -128672783, 2099418320, 1508239336,
159 -2089480835, -390935727, 664306522, -1607364342, -163246802, -1121295140,
160 -128375779, -615694409, -2079391797, 760542037, 677761593, -750117849,
161 -1060525080, 2128437080, 525250908, 1987657172, 2032530557, -2011247936,
162 1942775263, 1681562788, 688229491, -803856505, 684707948, 1308988965,
163 1455480037, 790659611, 1557968784, -383203149, -361510986, -742575828,
164 558837193, -1214977424, 1253274105, -119513513, -993964385, -33438767,
165 -177452803, 1186928041, -2073533871, 1188528559, 1896514695, 1200128512,
166 1930588755, -1914141443, 1534656032, -1192989829, -1848274656, -220848455,
167 1001806509, 1298797392, 1533031884, -1912322446, 1705583815, 1568094347,
168 -1397640627, 807828512, -1852996497, -1529733505, -1575634185,
169 -1280270160, -1567624159, -1861904922, 1276738579, 1163432999, 626879833,
170 316942006, -1871138342, 1341039701, 1595907877, 1950911580, 1634717748,
171 1071476055, -809354290, -1161553341, -2081621710, -2085557943, 19360224,
172 322135580, -698485151, 1267663094, -233890834, -126361189, -1426257522,
173 1094007921, 500179855, -283548002, -1678987343, 1946999943, 1489410849,
174 2089571262, 1430799093, 1961848046, -99462663, -552833264, 1168700661,
175 -1783882181, 2089196401, 1092839657, 914488673, 80263859, -2140947098,
176 -726384741, -1022448237, 2113887675, 1485770846, -112922517, 1995461466,
177 774613726, 944068011, 1521975359, 289086919, -386920759, -1960513175,
178 358460021, -238698524, -1913640563, -1000324864, 1731755224, -1271586254,
179 -1917469655, 2134162829, -828097534, -1089292503, -1514835999, 1682931514,
180 -482307169, 2110243841, 115744834, -2038340170, 65889188, -539445712,
181 -1713206408, -1842396726, -1659545588, -909558923, 860164922, 1328713040,
182 1044007120, -2103807103, -1073990344, -1312783785, -884980824, -705318011,
183 -1263408788, -2032228692, -1732844111, -1813827156, 1462566279,
184 1179250845, 1732421772, 604429013, -92284336, -1192166516, 304654351,
185 1998552034, -1802461575, -1802704071, -1704833934, -976264396, 1005840702,
186 2108843914, 1363909309, 843040834, -1039625241, 1285007226, 91610001,
187 418426329, 678422358, -945360697, -440008081, -1053091357, 425719777,
188 -1372778676, 591912153, 1229089037, -56663158, 2140251400, 830257037,
189 763914157, 175610373, -2105655963, -1040826150, 1174443038, 339290593,
190 346618443, -180504100, -1363190515, 210620018, 1028894425, 573529714,
191 698460117, 136999397, 1015621712, -1401813739, -297990684, -1820934845,
192 -1299093313, 1299361369, -366522415, 91527707, 1113466178, -956229484,
193 22204763, -1394374195, -1912666711, -1453789804, 1613408399, -169509567,
194 1350520309, 540761213, -2086682848, 1095131491, -812787911, 1860108594,
195 -1121378737, -1667252487, -486084366, 166519760, 1609891237, 728218405,
196 291075010, 646168382, 108462277, -1616661910, 1016600360, 2099958568,
197 27934736, 183821196, 13660496, -805589719, 936068730, -439037934,
198 1414622584, 215845485, -1352304469, -1817427526, -1318710977, -110207199,
199 228524335, 1704746590, 998293651, -1521016702, -641956531, -2089808167,
200 2094404052, -1446381065, -662186492, 1670154584, 9637833, 493925511,
201 660047318, 1197537103, 1696017374, -204994399, -1104145601, -852330465,
202 -1936369658, -829716674, -1255255217, 1264013799, 1642611772, -652520861,
203 777247164, 2028895987, -1424241853, -54367829, -1940161761, -1802831079,
204 -449405299, 838242661, -323055438, 794295411, -136989378, -446686673,
205 -421252799, -16777216,
206};
207
208int
209main (int argc, char *argv[])
210{
211 char buf[1024];
212 int i;
213
214 GNUNET_log_setup ("test-crypto-crc", "WARNING", NULL);
215 for (i = 0; i < 1024; i++)
216 buf[i] = (char) i;
217 for (i = 0; i < 1024; i++)
218 if (expected[i] != GNUNET_CRYPTO_crc32_n (&buf[i], 1024 - i))
219 return 1;
220 return 0;
221}
diff --git a/src/lib/util/test_crypto_cs.c b/src/lib/util/test_crypto_cs.c
new file mode 100644
index 000000000..ee68db72f
--- /dev/null
+++ b/src/lib/util/test_crypto_cs.c
@@ -0,0 +1,621 @@
1/*
2 This file is part of GNUnet
3 Copyright (C) 2021,2022, 2023 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/test_crypto_cs.c
23 * @brief testcase for utility functions for clause blind schnorr signature scheme cryptography
24 * @author Lucien Heuzeveldt <lucienclaude.heuzeveldt@students.bfh.ch>
25 * @author Gian Demarmels <gian@demarmels.org>
26 */
27
28#include "platform.h"
29#include "gnunet_util_lib.h"
30#include <sodium.h>
31
32#define ITER 25
33
34
35static void
36test_create_priv (struct GNUNET_CRYPTO_CsPrivateKey *priv)
37{
38 /* TEST 1
39 * Check that privkey is set
40 */
41 struct GNUNET_CRYPTO_CsPrivateKey other_priv;
42
43 GNUNET_CRYPTO_cs_private_key_generate (priv);
44 memset (&other_priv,
45 42,
46 sizeof (other_priv));
47 GNUNET_assert (0 !=
48 GNUNET_memcmp (&other_priv.scalar,
49 &priv->scalar));
50}
51
52
53static void
54test_generate_pub (const struct GNUNET_CRYPTO_CsPrivateKey *priv,
55 struct GNUNET_CRYPTO_CsPublicKey *pub)
56{
57 /* TEST 1
58 * Check that pubkey is set
59 */
60 struct GNUNET_CRYPTO_CsPublicKey other_pub;
61
62 GNUNET_CRYPTO_cs_private_key_get_public (priv,
63 pub);
64 memset (&other_pub,
65 42,
66 sizeof (other_pub));
67 GNUNET_assert (0 !=
68 GNUNET_memcmp (&other_pub.point,
69 &pub->point));
70
71 /* TEST 2
72 * Check that pubkey is a valid point
73 */
74 GNUNET_assert (1 ==
75 crypto_core_ed25519_is_valid_point (pub->point.y));
76
77 /* TEST 3
78 * Check if function gives the same result for the same output
79 */
80 other_pub = *pub;
81 for (unsigned int i = 0; i<ITER; i++)
82 {
83 GNUNET_CRYPTO_cs_private_key_get_public (priv,
84 pub);
85 GNUNET_assert (0 ==
86 GNUNET_memcmp (&other_pub.point,
87 &pub->point));
88 }
89}
90
91
92static void
93test_derive_rsecret (const struct GNUNET_CRYPTO_CsSessionNonce *nonce,
94 const struct GNUNET_CRYPTO_CsPrivateKey *priv,
95 struct GNUNET_CRYPTO_CsRSecret r[2])
96{
97 /* TEST 1
98 * Check that r are set
99 */
100 struct GNUNET_CRYPTO_CsPrivateKey other_r[2];
101
102 memcpy (other_r,
103 r,
104 sizeof(struct GNUNET_CRYPTO_CsPrivateKey) * 2);
105 GNUNET_CRYPTO_cs_r_derive (nonce,
106 "nw",
107 priv,
108 r);
109 GNUNET_assert (0 !=
110 memcmp (&other_r[0],
111 &r[0],
112 sizeof(struct GNUNET_CRYPTO_CsPrivateKey) * 2));
113
114 /* TEST 2
115 * Check if function gives the same result for the same input.
116 * This test ensures that the derivation is deterministic.
117 */
118 memcpy (other_r,
119 r,
120 sizeof(struct GNUNET_CRYPTO_CsPrivateKey) * 2);
121 for (unsigned int i = 0; i<ITER; i++)
122 {
123 GNUNET_CRYPTO_cs_r_derive (nonce,
124 "nw",
125 priv,
126 r);
127 GNUNET_assert (0 ==
128 memcmp (other_r,
129 r,
130 sizeof(struct GNUNET_CRYPTO_CsPrivateKey) * 2));
131 }
132}
133
134
135static void
136test_generate_rpublic (const struct GNUNET_CRYPTO_CsRSecret *r_priv,
137 struct GNUNET_CRYPTO_CsRPublic *r_pub)
138{
139 /* TEST 1
140 * Check that r_pub is set
141 */
142 struct GNUNET_CRYPTO_CsRPublic other_r_pub;
143
144 other_r_pub = *r_pub;
145 GNUNET_CRYPTO_cs_r_get_public (r_priv,
146 r_pub);
147 GNUNET_assert (0 !=
148 GNUNET_memcmp (&other_r_pub.point,
149 &r_pub->point));
150 /* TEST 2
151 * Check that r_pub is a valid point
152 */
153 GNUNET_assert (1 ==
154 crypto_core_ed25519_is_valid_point (r_pub->point.y));
155
156 /* TEST 3
157 * Check if function gives the same result for the same output
158 */
159 other_r_pub.point = r_pub->point;
160 for (int i = 0; i<ITER; i++)
161 {
162 GNUNET_CRYPTO_cs_r_get_public (r_priv,
163 r_pub);
164 GNUNET_assert (0 ==
165 GNUNET_memcmp (&other_r_pub.point,
166 &r_pub->point));
167 }
168}
169
170
171static void
172test_derive_blindingsecrets (const struct GNUNET_CRYPTO_CsBlindingNonce *blind_seed,
173 struct GNUNET_CRYPTO_CsBlindingSecret bs[2])
174{
175 /* TEST 1
176 * Check that blinding secrets are set
177 */
178 struct GNUNET_CRYPTO_CsBlindingSecret other_bs[2];
179
180 memcpy (other_bs,
181 bs,
182 sizeof(struct GNUNET_CRYPTO_CsBlindingSecret) * 2);
183
184 GNUNET_CRYPTO_cs_blinding_secrets_derive (blind_seed,
185 bs);
186
187 GNUNET_assert (0 !=
188 memcmp (other_bs,
189 bs,
190 sizeof(struct GNUNET_CRYPTO_CsBlindingSecret)
191 * 2));
192
193 /* TEST 2
194 * Check if function gives the same result for the same input.
195 * This test ensures that the derivation is deterministic.
196 */
197 memcpy (other_bs,
198 bs,
199 sizeof(struct GNUNET_CRYPTO_CsBlindingSecret) * 2);
200 for (unsigned int i = 0; i<ITER; i++)
201 {
202 GNUNET_CRYPTO_cs_blinding_secrets_derive (blind_seed,
203 bs);
204 GNUNET_assert (0 == memcmp (&other_bs[0],
205 &bs[0],
206 sizeof(struct GNUNET_CRYPTO_CsBlindingSecret)
207 * 2));
208 }
209}
210
211
212static void
213test_calc_blindedc (const struct GNUNET_CRYPTO_CsBlindingSecret bs[2],
214 const struct GNUNET_CRYPTO_CsRPublic r_pub[2],
215 const struct GNUNET_CRYPTO_CsPublicKey *pub,
216 const void *msg,
217 size_t msg_len,
218 struct GNUNET_CRYPTO_CsC blinded_cs[2],
219 struct GNUNET_CRYPTO_CSPublicRPairP *blinded_r_pub)
220{
221 /* TEST 1
222 * Check that the blinded c's and blinded r's
223 */
224 struct GNUNET_CRYPTO_CsC other_blinded_c[2];
225
226 memcpy (&other_blinded_c[0],
227 &blinded_cs[0],
228 sizeof(struct GNUNET_CRYPTO_CsC) * 2);
229
230 struct GNUNET_CRYPTO_CSPublicRPairP other_blinded_pub;
231 other_blinded_pub = *blinded_r_pub;
232
233 GNUNET_CRYPTO_cs_calc_blinded_c (bs,
234 r_pub,
235 pub,
236 msg,
237 msg_len,
238 blinded_cs,
239 blinded_r_pub);
240
241 GNUNET_assert (0 != memcmp (&other_blinded_c[0],
242 &blinded_cs[0],
243 sizeof(struct GNUNET_CRYPTO_CsC) * 2));
244 GNUNET_assert (0 !=
245 GNUNET_memcmp (&other_blinded_pub,
246 blinded_r_pub));
247
248 /* TEST 2
249 * Check if R' - aG -bX = R for b = 0
250 * This test does the opposite operations and checks whether the equation is still correct.
251 */
252 for (unsigned int b = 0; b <= 1; b++)
253 {
254 struct GNUNET_CRYPTO_Cs25519Point aG;
255 struct GNUNET_CRYPTO_Cs25519Point bX;
256 struct GNUNET_CRYPTO_Cs25519Point r_min_aG;
257 struct GNUNET_CRYPTO_CsRPublic res;
258
259 GNUNET_assert (0 ==
260 crypto_scalarmult_ed25519_base_noclamp (
261 aG.y,
262 bs[b].alpha.d));
263 GNUNET_assert (0 ==
264 crypto_scalarmult_ed25519_noclamp (
265 bX.y,
266 bs[b].beta.d,
267 pub->point.y));
268 GNUNET_assert (0 ==
269 crypto_core_ed25519_sub (
270 r_min_aG.y,
271 blinded_r_pub->r_pub[b].point.y,
272 aG.y));
273 GNUNET_assert (0 == crypto_core_ed25519_sub (
274 res.point.y,
275 r_min_aG.y,
276 bX.y));
277
278 GNUNET_assert (0 ==
279 memcmp (&res,
280 &r_pub[b],
281 sizeof(struct GNUNET_CRYPTO_CsRPublic)));
282 }
283
284
285 /* TEST 3
286 * Check that the blinded r_pubs' are valid points
287 */
288 GNUNET_assert (1 ==
289 crypto_core_ed25519_is_valid_point (
290 blinded_r_pub->r_pub[0].point.y));
291 GNUNET_assert (1 ==
292 crypto_core_ed25519_is_valid_point (
293 blinded_r_pub->r_pub[1].point.y));
294
295 /* TEST 4
296 * Check if function gives the same result for the same input.
297 */
298 memcpy (&other_blinded_c[0],
299 &blinded_cs[0],
300 sizeof(struct GNUNET_CRYPTO_CsC) * 2);
301 other_blinded_pub = *blinded_r_pub;
302
303 for (unsigned int i = 0; i<ITER; i++)
304 {
305 GNUNET_CRYPTO_cs_calc_blinded_c (bs,
306 r_pub,
307 pub,
308 msg,
309 msg_len,
310 blinded_cs,
311 blinded_r_pub);
312 GNUNET_assert (0 ==
313 memcmp (&other_blinded_c[0],
314 &blinded_cs[0],
315 sizeof(struct GNUNET_CRYPTO_CsC) * 2));
316 GNUNET_assert (0 ==
317 GNUNET_memcmp (&other_blinded_pub,
318 blinded_r_pub));
319 }
320}
321
322
323static void
324test_blind_sign (const struct GNUNET_CRYPTO_CsPrivateKey *priv,
325 const struct GNUNET_CRYPTO_CsRSecret r[2],
326 const struct GNUNET_CRYPTO_CsBlindedMessage *bm,
327 struct GNUNET_CRYPTO_CsBlindSignature *cs_blind_sig)
328{
329 /* TEST 1
330 * Check that blinded_s is set
331 */
332 struct GNUNET_CRYPTO_CsBlindSignature other_blind_sig;
333
334 memset (&other_blind_sig,
335 44,
336 sizeof (other_blind_sig));
337 GNUNET_CRYPTO_cs_sign_derive (priv,
338 r,
339 bm,
340 &other_blind_sig);
341 GNUNET_assert (0 == other_blind_sig.b ||
342 1 == other_blind_sig.b);
343
344 /* TEST 2
345 * Check if s := rb + cbX
346 * This test does the opposite operations and checks whether the equation is still correct.
347 */
348 struct GNUNET_CRYPTO_Cs25519Scalar cb_mul_x;
349 struct GNUNET_CRYPTO_Cs25519Scalar s_min_rb;
350
351 crypto_core_ed25519_scalar_mul (cb_mul_x.d,
352 bm->c[other_blind_sig.b].scalar.d,
353 priv->scalar.d);
354 crypto_core_ed25519_scalar_sub (s_min_rb.d,
355 other_blind_sig.s_scalar.scalar.d,
356 r[other_blind_sig.b].scalar.d);
357 GNUNET_assert (0 ==
358 GNUNET_memcmp (&s_min_rb,
359 &cb_mul_x));
360
361 /* TEST 3
362 * Check if function gives the same result for the same input.
363 */
364 for (unsigned int i = 0; i<ITER; i++)
365 {
366 GNUNET_CRYPTO_cs_sign_derive (priv,
367 r,
368 bm,
369 cs_blind_sig);
370 GNUNET_assert (0 ==
371 GNUNET_memcmp (&other_blind_sig,
372 cs_blind_sig));
373 }
374}
375
376
377static void
378test_unblinds (const struct GNUNET_CRYPTO_CsBlindS *blinded_signature_scalar,
379 const struct GNUNET_CRYPTO_CsBlindingSecret *bs,
380 struct GNUNET_CRYPTO_CsS *signature_scalar)
381{
382 /* TEST 1
383 * Check that signature_scalar is set
384 */
385 struct GNUNET_CRYPTO_CsS other_signature_scalar;
386 memcpy (&other_signature_scalar,
387 signature_scalar,
388 sizeof(struct GNUNET_CRYPTO_CsS));
389
390 GNUNET_CRYPTO_cs_unblind (blinded_signature_scalar,
391 bs,
392 signature_scalar);
393
394 GNUNET_assert (0 != memcmp (&other_signature_scalar,
395 signature_scalar,
396 sizeof(struct GNUNET_CRYPTO_CsS)));
397
398 /* TEST 2
399 * Check if s' := s + a mod p
400 * This test does the opposite operations and checks whether the equation is still correct.
401 */
402 struct GNUNET_CRYPTO_Cs25519Scalar s_min_a;
403
404 crypto_core_ed25519_scalar_sub (s_min_a.d,
405 signature_scalar->scalar.d,
406 bs->alpha.d);
407
408 GNUNET_assert (0 == memcmp (&s_min_a, &blinded_signature_scalar->scalar,
409 sizeof(struct
410 GNUNET_CRYPTO_Cs25519Scalar)));
411
412 /* TEST 3
413 * Check if function gives the same result for the same input.
414 */
415 memcpy (&other_signature_scalar, signature_scalar,
416 sizeof(struct GNUNET_CRYPTO_CsS));
417
418 for (unsigned int i = 0; i<ITER; i++)
419 {
420 GNUNET_CRYPTO_cs_unblind (blinded_signature_scalar, bs, signature_scalar);
421 GNUNET_assert (0 == memcmp (&other_signature_scalar,
422 signature_scalar,
423 sizeof(struct GNUNET_CRYPTO_CsS)));
424 }
425}
426
427
428static void
429test_blind_verify (const struct GNUNET_CRYPTO_CsSignature *sig,
430 const struct GNUNET_CRYPTO_CsPublicKey *pub,
431 const struct GNUNET_CRYPTO_CsC *c)
432{
433 /* TEST 1
434 * Test verifies the blinded signature sG == Rb + cbX
435 */
436 struct GNUNET_CRYPTO_Cs25519Point sig_scal_mul_base;
437 struct GNUNET_CRYPTO_Cs25519Point c_mul_pub;
438 struct GNUNET_CRYPTO_Cs25519Point r_add_c_mul_pub;
439
440 GNUNET_assert (0 ==
441 crypto_scalarmult_ed25519_base_noclamp (
442 sig_scal_mul_base.y,
443 sig->s_scalar.scalar.d));
444 GNUNET_assert (0 ==
445 crypto_scalarmult_ed25519_noclamp (c_mul_pub.y,
446 c->scalar.d,
447 pub->point.y));
448 GNUNET_assert (0 ==
449 crypto_core_ed25519_add (r_add_c_mul_pub.y,
450 sig->r_point.point.y,
451 c_mul_pub.y));
452 GNUNET_assert (0 ==
453 GNUNET_memcmp (sig_scal_mul_base.y,
454 r_add_c_mul_pub.y));
455}
456
457
458static void
459test_verify (const struct GNUNET_CRYPTO_CsSignature *sig,
460 const struct GNUNET_CRYPTO_CsPublicKey *pub,
461 const void *msg,
462 size_t msg_len)
463{
464 /* TEST 1
465 * Test simple verification
466 */
467 GNUNET_assert (GNUNET_YES ==
468 GNUNET_CRYPTO_cs_verify (sig,
469 pub,
470 msg,
471 msg_len));
472 /* TEST 2
473 * Test verification of "wrong" message
474 */
475 char other_msg[] = "test massege";
476 size_t other_msg_len = strlen ("test massege");
477 GNUNET_assert (GNUNET_SYSERR ==
478 GNUNET_CRYPTO_cs_verify (sig,
479 pub,
480 other_msg,
481 other_msg_len));
482}
483
484
485int
486main (int argc,
487 char *argv[])
488{
489 printf ("Test started\n");
490
491 // ---------- actions performed by signer
492 char message[] = "test message";
493 size_t message_len = strlen ("test message");
494
495 struct GNUNET_CRYPTO_CsPrivateKey priv;
496
497 GNUNET_log_setup ("test-crypto-cs",
498 "INFO",
499 NULL);
500 memset (&priv,
501 42,
502 sizeof (priv));
503 test_create_priv (&priv);
504
505 struct GNUNET_CRYPTO_CsPublicKey pub;
506
507 memset (&pub,
508 42,
509 sizeof (pub));
510 test_generate_pub (&priv,
511 &pub);
512
513 // set nonce
514 struct GNUNET_CRYPTO_CsSessionNonce nonce;
515 GNUNET_assert (GNUNET_YES ==
516 GNUNET_CRYPTO_kdf (&nonce,
517 sizeof(nonce),
518 "nonce",
519 strlen ("nonce"),
520 "nonce_secret",
521 strlen ("nonce_secret"),
522 NULL,
523 0));
524
525 // generate r, R
526 struct GNUNET_CRYPTO_CsRSecret r_secrets[2];
527
528 memset (r_secrets,
529 42,
530 sizeof (r_secrets));
531 test_derive_rsecret (&nonce,
532 &priv,
533 r_secrets);
534
535 struct GNUNET_CRYPTO_CsRPublic r_publics[2];
536
537 memset (r_publics,
538 42,
539 sizeof (r_publics));
540 test_generate_rpublic (&r_secrets[0],
541 &r_publics[0]);
542 test_generate_rpublic (&r_secrets[1],
543 &r_publics[1]);
544
545 // ---------- actions performed by user
546
547 // generate blinding secrets
548 struct GNUNET_CRYPTO_CsBlindingSecret blindingsecrets[2];
549 struct GNUNET_CRYPTO_CsBlindingNonce bnonce;
550
551 memset (&bnonce,
552 42,
553 sizeof (bnonce));
554 memset (blindingsecrets,
555 42,
556 sizeof (blindingsecrets));
557 test_derive_blindingsecrets (&bnonce,
558 blindingsecrets);
559
560 // calculate blinded c's
561 struct GNUNET_CRYPTO_CsBlindedMessage bm;
562 struct GNUNET_CRYPTO_CsC blinded_cs[2];
563 struct GNUNET_CRYPTO_CSPublicRPairP blinded_r_pubs;
564
565 memset (blinded_cs,
566 42,
567 sizeof (blinded_cs));
568 memset (&blinded_r_pubs,
569 42,
570 sizeof (blinded_r_pubs));
571 test_calc_blindedc (blindingsecrets,
572 r_publics,
573 &pub,
574 message,
575 message_len,
576 blinded_cs,
577 &blinded_r_pubs);
578
579 // ---------- actions performed by signer
580 // sign blinded c's and get b and s in return
581 struct GNUNET_CRYPTO_CsBlindSignature blinded_s;
582
583 memset (&blinded_s,
584 42,
585 sizeof (blinded_s));
586 bm.c[0] = blinded_cs[0];
587 bm.c[1] = blinded_cs[1];
588 bm.nonce = nonce;
589 test_blind_sign (&priv,
590 r_secrets,
591 &bm,
592 &blinded_s);
593 // verify blinded signature
594 struct GNUNET_CRYPTO_CsSignature blinded_signature;
595
596 blinded_signature.r_point = r_publics[blinded_s.b];
597 blinded_signature.s_scalar.scalar = blinded_s.s_scalar.scalar;
598 test_blind_verify (&blinded_signature,
599 &pub,
600 &blinded_cs[blinded_s.b]);
601
602 // ---------- actions performed by user
603 struct GNUNET_CRYPTO_CsS sig_scalar;
604
605 memset (&sig_scalar,
606 42,
607 sizeof (sig_scalar));
608 test_unblinds (&blinded_s.s_scalar,
609 &blindingsecrets[blinded_s.b],
610 &sig_scalar);
611
612 // verify unblinded signature
613 struct GNUNET_CRYPTO_CsSignature signature;
614 signature.r_point = blinded_r_pubs.r_pub[blinded_s.b];
615 signature.s_scalar = sig_scalar;
616 test_verify (&signature,
617 &pub,
618 message,
619 message_len);
620 return 0;
621}
diff --git a/src/lib/util/test_crypto_ecc_dlog.c b/src/lib/util/test_crypto_ecc_dlog.c
new file mode 100644
index 000000000..c3382a4fa
--- /dev/null
+++ b/src/lib/util/test_crypto_ecc_dlog.c
@@ -0,0 +1,219 @@
1/*
2 This file is part of GNUnet.
3 Copyright (C) 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/**
22 * @file util/test_crypto_ecc_dlog.c
23 * @brief testcase for ECC DLOG calculation
24 * @author Christian Grothoff
25 */
26
27#include "platform.h"
28#include "gnunet_util_lib.h"
29#include <gcrypt.h>
30
31
32/**
33 * Name of the curve we are using. Note that we have hard-coded
34 * structs that use 256 bits, so using a bigger curve will require
35 * changes that break stuff badly. The name of the curve given here
36 * must be agreed by all peers and be supported by libgcrypt.
37 */
38#define CURVE "Ed25519"
39
40/**
41 * Maximum value we test dlog for.
42 */
43#define MAX_FACT 100
44
45/**
46 * Maximum memory to use, sqrt(MAX_FACT) is a good choice.
47 */
48#define MAX_MEM 10
49
50/**
51 * How many values do we test?
52 */
53#define TEST_ITER 100
54
55/**
56 * Range of values to use for MATH tests.
57 */
58#define MATH_MAX 5
59
60
61/**
62 * Do some DLOG operations for testing.
63 *
64 * @param edc context for ECC operations
65 */
66static void
67test_dlog (struct GNUNET_CRYPTO_EccDlogContext *edc)
68{
69 for (unsigned int i = 0; i < TEST_ITER; i++)
70 {
71 struct GNUNET_CRYPTO_EccScalar fact;
72 struct GNUNET_CRYPTO_EccScalar n;
73 struct GNUNET_CRYPTO_EccPoint q;
74 int x;
75
76 fprintf (stderr, ".");
77 x = GNUNET_CRYPTO_random_u32 (GNUNET_CRYPTO_QUALITY_WEAK,
78 MAX_FACT);
79 memset (&n,
80 0,
81 sizeof (n));
82 for (unsigned int j = 0; j < x; j++)
83 sodium_increment (n.v,
84 sizeof (n.v));
85 if (0 == GNUNET_CRYPTO_random_u32 (GNUNET_CRYPTO_QUALITY_WEAK,
86 2))
87 {
88 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
89 "Trying negative %d\n",
90 -x);
91 crypto_core_ed25519_scalar_negate (fact.v,
92 n.v);
93 x = -x;
94 }
95 else
96 {
97 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
98 "Trying positive %d\n",
99 x);
100 fact = n;
101 }
102 if (0 == x)
103 {
104 /* libsodium does not like to multiply with zero; make sure
105 'q' is a valid point (g) first, then use q = q - q to get
106 the product with zero */
107 sodium_increment (fact.v,
108 sizeof (fact.v));
109 GNUNET_assert (0 ==
110 crypto_scalarmult_ed25519_base_noclamp (q.v,
111 fact.v));
112 GNUNET_assert (
113 0 ==
114 crypto_core_ed25519_sub (q.v,
115 q.v,
116 q.v));
117 }
118 else
119 GNUNET_assert (0 ==
120 crypto_scalarmult_ed25519_base_noclamp (q.v,
121 fact.v));
122 {
123 int iret;
124
125 if (x !=
126 (iret = GNUNET_CRYPTO_ecc_dlog (edc,
127 &q)))
128 {
129 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
130 "DLOG failed for value %d (got: %d)\n",
131 x,
132 iret);
133 GNUNET_assert (0);
134 }
135 }
136 }
137 fprintf (stderr,
138 "\n");
139}
140
141
142/**
143 * Do some arithmetic operations for testing.
144 *
145 * @param edc context for ECC operations
146 */
147static void
148test_math (struct GNUNET_CRYPTO_EccDlogContext *edc)
149{
150 int i;
151 int j;
152 struct GNUNET_CRYPTO_EccPoint ip;
153 struct GNUNET_CRYPTO_EccPoint jp;
154 struct GNUNET_CRYPTO_EccPoint r;
155 struct GNUNET_CRYPTO_EccPoint ir;
156 struct GNUNET_CRYPTO_EccPoint irj;
157 struct GNUNET_CRYPTO_EccPoint r_inv;
158 struct GNUNET_CRYPTO_EccPoint sum;
159
160 for (i = -MATH_MAX; i < MATH_MAX; i++)
161 {
162 GNUNET_CRYPTO_ecc_dexp (i, &ip);
163 for (j = -MATH_MAX; j < MATH_MAX; j++)
164 {
165 fprintf (stderr, ".");
166 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
167 "%d + %d\n",
168 i,
169 j);
170 GNUNET_CRYPTO_ecc_dexp (j, &jp);
171 GNUNET_CRYPTO_ecc_rnd (&r,
172 &r_inv);
173 GNUNET_CRYPTO_ecc_add (&ip, &r, &ir);
174 GNUNET_CRYPTO_ecc_add (&ir, &jp, &irj);
175 GNUNET_CRYPTO_ecc_add (&irj, &r_inv, &sum);
176 int res = GNUNET_CRYPTO_ecc_dlog (edc, &sum);
177 if (i + j != res)
178 {
179 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
180 "Got %d, expected %d\n",
181 res,
182 i + j);
183 // GNUNET_assert (0);
184 }
185 }
186 }
187 fprintf (stderr, "\n");
188}
189
190
191int
192main (int argc, char *argv[])
193{
194 struct GNUNET_CRYPTO_EccDlogContext *edc;
195
196 if (! gcry_check_version ("1.6.0"))
197 {
198 fprintf (stderr,
199 _
200 (
201 "libgcrypt has not the expected version (version %s is required).\n"),
202 "1.6.0");
203 return 0;
204 }
205 if (getenv ("GNUNET_GCRYPT_DEBUG"))
206 gcry_control (GCRYCTL_SET_DEBUG_FLAGS, 1u, 0);
207 GNUNET_log_setup ("test-crypto-ecc-dlog",
208 "WARNING",
209 NULL);
210 edc = GNUNET_CRYPTO_ecc_dlog_prepare (MAX_FACT,
211 MAX_MEM);
212 test_dlog (edc);
213 test_math (edc);
214 GNUNET_CRYPTO_ecc_dlog_release (edc);
215 return 0;
216}
217
218
219/* end of test_crypto_ecc_dlog.c */
diff --git a/src/lib/util/test_crypto_ecdh_ecdsa.c b/src/lib/util/test_crypto_ecdh_ecdsa.c
new file mode 100644
index 000000000..609f05282
--- /dev/null
+++ b/src/lib/util/test_crypto_ecdh_ecdsa.c
@@ -0,0 +1,95 @@
1/*
2 This file is part of GNUnet.
3 Copyright (C) 2002-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/**
22 * @file util/test_crypto_ecdh_ecdsa.c
23 * @brief testcase for ECC DH key exchange with ECDSA private keys.
24 * @author Christian Grothoff
25 * @author Bart Polot
26 */
27
28#include "platform.h"
29#include "gnunet_util_lib.h"
30#include <gcrypt.h>
31
32
33static int
34test_ecdh ()
35{
36 struct GNUNET_CRYPTO_EcdsaPrivateKey priv_dsa;
37 struct GNUNET_CRYPTO_EcdhePrivateKey priv_ecdh;
38 struct GNUNET_CRYPTO_EcdsaPublicKey id1;
39 struct GNUNET_CRYPTO_EcdhePublicKey id2;
40 struct GNUNET_HashCode dh[2];
41
42 /* Generate keys */
43 GNUNET_CRYPTO_ecdsa_key_create (&priv_dsa);
44 GNUNET_CRYPTO_ecdsa_key_get_public (&priv_dsa,
45 &id1);
46 for (unsigned int j = 0; j < 4; j++)
47 {
48 fprintf (stderr, ",");
49 GNUNET_CRYPTO_ecdhe_key_create (&priv_ecdh);
50 /* Extract public keys */
51 GNUNET_CRYPTO_ecdhe_key_get_public (&priv_ecdh,
52 &id2);
53 /* Do ECDH */
54 GNUNET_assert (GNUNET_OK ==
55 GNUNET_CRYPTO_ecdsa_ecdh (&priv_dsa,
56 &id2,
57 &dh[0]));
58 GNUNET_assert (GNUNET_OK ==
59 GNUNET_CRYPTO_ecdh_ecdsa (&priv_ecdh,
60 &id1,
61 &dh[1]));
62 /* Check that both DH results are equal. */
63 GNUNET_assert (0 ==
64 GNUNET_memcmp (&dh[0],
65 &dh[1]));
66 }
67 return 0;
68}
69
70
71int
72main (int argc, char *argv[])
73{
74 if (! gcry_check_version ("1.6.0"))
75 {
76 fprintf (stderr,
77 "libgcrypt has not the expected version (version %s is required).\n",
78 "1.6.0");
79 return 0;
80 }
81 if (getenv ("GNUNET_GCRYPT_DEBUG"))
82 gcry_control (GCRYCTL_SET_DEBUG_FLAGS, 1u, 0);
83 GNUNET_log_setup ("test-crypto-ecdh-ecdsa", "WARNING", NULL);
84 for (unsigned int i = 0; i < 4; i++)
85 {
86 fprintf (stderr,
87 ".");
88 if (0 != test_ecdh ())
89 return 1;
90 }
91 return 0;
92}
93
94
95/* end of test_crypto_ecdh_ecdsa.c */
diff --git a/src/lib/util/test_crypto_ecdh_eddsa.c b/src/lib/util/test_crypto_ecdh_eddsa.c
new file mode 100644
index 000000000..875f479c2
--- /dev/null
+++ b/src/lib/util/test_crypto_ecdh_eddsa.c
@@ -0,0 +1,96 @@
1/*
2 This file is part of GNUnet.
3 Copyright (C) 2002-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/**
22 * @file util/test_crypto_ecdh_eddsa.c
23 * @brief testcase for ECC DH key exchange with EdDSA private keys.
24 * @author Christian Grothoff
25 * @author Bart Polot
26 */
27
28#include "platform.h"
29#include "gnunet_util_lib.h"
30#include <gcrypt.h>
31
32
33static int
34test_ecdh ()
35{
36 struct GNUNET_CRYPTO_EddsaPrivateKey priv_dsa;
37 struct GNUNET_CRYPTO_EcdhePrivateKey priv_ecdh;
38 struct GNUNET_CRYPTO_EddsaPublicKey id1;
39 struct GNUNET_CRYPTO_EcdhePublicKey id2;
40 struct GNUNET_HashCode dh[2];
41
42 /* Generate keys */
43 GNUNET_CRYPTO_eddsa_key_create (&priv_dsa);
44 GNUNET_CRYPTO_eddsa_key_get_public (&priv_dsa,
45 &id1);
46 for (unsigned int j = 0; j < 4; j++)
47 {
48 fprintf (stderr, ",");
49 GNUNET_CRYPTO_ecdhe_key_create (&priv_ecdh);
50 /* Extract public keys */
51 GNUNET_CRYPTO_ecdhe_key_get_public (&priv_ecdh,
52 &id2);
53 /* Do ECDH */
54 GNUNET_assert (GNUNET_OK ==
55 GNUNET_CRYPTO_eddsa_ecdh (&priv_dsa,
56 &id2,
57 &dh[0]));
58 GNUNET_assert (GNUNET_OK ==
59 GNUNET_CRYPTO_ecdh_eddsa (&priv_ecdh,
60 &id1,
61 &dh[1]));
62 /* Check that both DH results are equal. */
63 GNUNET_assert (0 ==
64 GNUNET_memcmp (&dh[0],
65 &dh[1]));
66 }
67 return 0;
68}
69
70
71int
72main (int argc, char *argv[])
73{
74 if (! gcry_check_version ("1.6.0"))
75 {
76 fprintf (stderr,
77 _ (
78 "libgcrypt has not the expected version (version %s is required).\n"),
79 "1.6.0");
80 return 0;
81 }
82 if (getenv ("GNUNET_GCRYPT_DEBUG"))
83 gcry_control (GCRYCTL_SET_DEBUG_FLAGS, 1u, 0);
84 GNUNET_log_setup ("test-crypto-ecdh-eddsa", "WARNING", NULL);
85 for (unsigned int i = 0; i < 4; i++)
86 {
87 fprintf (stderr,
88 ".");
89 if (0 != test_ecdh ())
90 return 1;
91 }
92 return 0;
93}
94
95
96/* end of test_crypto_ecdh_eddsa.c */
diff --git a/src/lib/util/test_crypto_ecdhe.c b/src/lib/util/test_crypto_ecdhe.c
new file mode 100644
index 000000000..cf59cfa64
--- /dev/null
+++ b/src/lib/util/test_crypto_ecdhe.c
@@ -0,0 +1,71 @@
1/*
2 This file is part of GNUnet.
3 Copyright (C) 2002-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/test_crypto_ecdhe.c
23 * @brief testcase for ECC ECDHE public key crypto
24 * @author Christian Grothoff
25 */
26
27#include "platform.h"
28#include "gnunet_util_lib.h"
29#include <gcrypt.h>
30
31
32int
33main (int argc, char *argv[])
34{
35 struct GNUNET_CRYPTO_EcdhePrivateKey priv1;
36 struct GNUNET_CRYPTO_EcdhePrivateKey priv2;
37 struct GNUNET_CRYPTO_EcdhePublicKey pub1;
38 struct GNUNET_CRYPTO_EcdhePublicKey pub2;
39 struct GNUNET_HashCode ecdh1;
40 struct GNUNET_HashCode ecdh2;
41
42 if (! gcry_check_version ("1.6.0"))
43 {
44 fprintf (stderr,
45 "libgcrypt has not the expected version (version %s is required).\n",
46 "1.6.0");
47 return 0;
48 }
49 if (getenv ("GNUNET_GCRYPT_DEBUG"))
50 gcry_control (GCRYCTL_SET_DEBUG_FLAGS, 1u, 0);
51 GNUNET_log_setup ("test-crypto-ecdhe", "WARNING", NULL);
52
53 for (unsigned int i = 0; i < 100; i++)
54 {
55 fprintf (stderr,
56 ".");
57 GNUNET_CRYPTO_ecdhe_key_create (&priv1);
58 GNUNET_CRYPTO_ecdhe_key_create (&priv2);
59 GNUNET_CRYPTO_ecdhe_key_get_public (&priv1, &pub1);
60 GNUNET_CRYPTO_ecdhe_key_get_public (&priv2, &pub2);
61 GNUNET_CRYPTO_ecc_ecdh (&priv1, &pub2, &ecdh1);
62 GNUNET_CRYPTO_ecc_ecdh (&priv2, &pub1, &ecdh2);
63 GNUNET_assert (0 ==
64 GNUNET_memcmp (&ecdh1,
65 &ecdh2));
66 }
67 return 0;
68}
69
70
71/* end of test_crypto_ecdhe.c */
diff --git a/src/lib/util/test_crypto_ecdsa.c b/src/lib/util/test_crypto_ecdsa.c
new file mode 100644
index 000000000..9dc1f863d
--- /dev/null
+++ b/src/lib/util/test_crypto_ecdsa.c
@@ -0,0 +1,282 @@
1/*
2 This file is part of GNUnet.
3 Copyright (C) 2002-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/test_crypto_ecdsa.c
23 * @brief testcase for ECC ECDSA public key crypto
24 * @author Christian Grothoff
25 */
26
27#include "platform.h"
28#include "gnunet_util_lib.h"
29#include "gnunet_signatures.h"
30#include <gcrypt.h>
31
32#define ITER 25
33
34#define PERF GNUNET_YES
35
36
37static struct GNUNET_CRYPTO_EcdsaPrivateKey key;
38
39
40static int
41testSignVerify (void)
42{
43 struct GNUNET_CRYPTO_EcdsaSignature sig;
44 struct GNUNET_CRYPTO_EccSignaturePurpose purp;
45 struct GNUNET_CRYPTO_EcdsaPublicKey pkey;
46 struct GNUNET_TIME_Absolute start;
47 int ok = GNUNET_OK;
48
49 fprintf (stderr, "%s", "W");
50 GNUNET_CRYPTO_ecdsa_key_get_public (&key,
51 &pkey);
52 start = GNUNET_TIME_absolute_get ();
53 purp.size = htonl (sizeof(struct GNUNET_CRYPTO_EccSignaturePurpose));
54 purp.purpose = htonl (GNUNET_SIGNATURE_PURPOSE_TEST);
55
56 for (unsigned int i = 0; i < ITER; i++)
57 {
58 fprintf (stderr, "%s", ".");
59 fflush (stderr);
60 if (GNUNET_SYSERR ==
61 GNUNET_CRYPTO_ecdsa_sign_ (&key,
62 &purp,
63 &sig))
64 {
65 fprintf (stderr,
66 "GNUNET_CRYPTO_ecdsa_sign returned SYSERR\n");
67 ok = GNUNET_SYSERR;
68 continue;
69 }
70 if (GNUNET_SYSERR ==
71 GNUNET_CRYPTO_ecdsa_verify_ (GNUNET_SIGNATURE_PURPOSE_TEST,
72 &purp,
73 &sig,
74 &pkey))
75 {
76 fprintf (stderr,
77 "GNUNET_CRYPTO_ecdsa_verify failed!\n");
78 ok = GNUNET_SYSERR;
79 continue;
80 }
81 if (GNUNET_SYSERR !=
82 GNUNET_CRYPTO_ecdsa_verify_ (
83 GNUNET_SIGNATURE_PURPOSE_TRANSPORT_PONG_OWN,
84 &purp,
85 &sig,
86 &pkey))
87 {
88 fprintf (stderr,
89 "GNUNET_CRYPTO_ecdsa_verify failed to fail!\n");
90 ok = GNUNET_SYSERR;
91 continue;
92 }
93 }
94 printf ("%d ECDSA sign/verify operations %s\n",
95 ITER,
96 GNUNET_STRINGS_relative_time_to_string (
97 GNUNET_TIME_absolute_get_duration (start),
98 GNUNET_YES));
99 return ok;
100}
101
102
103static int
104testDeriveSignVerify (void)
105{
106 struct GNUNET_CRYPTO_EcdsaSignature sig;
107 struct GNUNET_CRYPTO_EccSignaturePurpose purp;
108 struct GNUNET_CRYPTO_EcdsaPrivateKey *dpriv;
109 struct GNUNET_CRYPTO_EcdsaPublicKey pkey;
110 struct GNUNET_CRYPTO_EcdsaPublicKey dpub;
111 struct GNUNET_CRYPTO_EcdsaPublicKey dpub2;
112
113 dpriv = GNUNET_CRYPTO_ecdsa_private_key_derive (&key,
114 "test-derive",
115 "test-CTX");
116 GNUNET_CRYPTO_ecdsa_key_get_public (&key,
117 &pkey);
118 GNUNET_CRYPTO_ecdsa_public_key_derive (&pkey,
119 "test-derive",
120 "test-CTX",
121 &dpub);
122 GNUNET_CRYPTO_ecdsa_key_get_public (dpriv, &dpub2);
123 purp.size = htonl (sizeof(struct GNUNET_CRYPTO_EccSignaturePurpose));
124 purp.purpose = htonl (GNUNET_SIGNATURE_PURPOSE_TEST);
125
126 if (0 != GNUNET_memcmp (&dpub.q_y, &dpub2.q_y))
127 {
128 fprintf (stderr, "%s", "key derivation failed\n");
129 GNUNET_free (dpriv);
130 return GNUNET_SYSERR;
131 }
132
133 if (GNUNET_SYSERR ==
134 GNUNET_CRYPTO_ecdsa_sign_ (dpriv,
135 &purp,
136 &sig))
137 {
138 fprintf (stderr, "%s", "GNUNET_CRYPTO_ecdsa_sign returned SYSERR\n");
139 GNUNET_free (dpriv);
140 return GNUNET_SYSERR;
141 }
142 if (GNUNET_SYSERR ==
143 GNUNET_CRYPTO_ecdsa_verify_ (GNUNET_SIGNATURE_PURPOSE_TEST,
144 &purp,
145 &sig,
146 &dpub))
147 {
148 fprintf (stderr,
149 "GNUNET_CRYPTO_ecdsa_verify failed!\n");
150 GNUNET_free (dpriv);
151 return GNUNET_SYSERR;
152 }
153 if (GNUNET_SYSERR !=
154 GNUNET_CRYPTO_ecdsa_verify_ (GNUNET_SIGNATURE_PURPOSE_TEST,
155 &purp,
156 &sig,
157 &pkey))
158 {
159 fprintf (stderr,
160 "GNUNET_CRYPTO_ecdsa_verify failed to fail!\n");
161 GNUNET_free (dpriv);
162 return GNUNET_SYSERR;
163 }
164 if (GNUNET_SYSERR !=
165 GNUNET_CRYPTO_ecdsa_verify_ (GNUNET_SIGNATURE_PURPOSE_TRANSPORT_PONG_OWN,
166 &purp,
167 &sig,
168 &dpub))
169 {
170 fprintf (stderr,
171 "GNUNET_CRYPTO_ecdsa_verify failed to fail!\n");
172 GNUNET_free (dpriv);
173 return GNUNET_SYSERR;
174 }
175 GNUNET_free (dpriv);
176 return GNUNET_OK;
177}
178
179
180#if PERF
181static int
182testSignPerformance (void)
183{
184 struct GNUNET_CRYPTO_EccSignaturePurpose purp;
185 struct GNUNET_CRYPTO_EcdsaSignature sig;
186 struct GNUNET_CRYPTO_EcdsaPublicKey pkey;
187 int i;
188 struct GNUNET_TIME_Absolute start;
189 int ok = GNUNET_OK;
190
191 purp.size = htonl (sizeof(struct GNUNET_CRYPTO_EccSignaturePurpose));
192 purp.purpose = htonl (GNUNET_SIGNATURE_PURPOSE_TEST);
193 fprintf (stderr, "%s", "W");
194 GNUNET_CRYPTO_ecdsa_key_get_public (key, &pkey);
195 start = GNUNET_TIME_absolute_get ();
196 for (i = 0; i < ITER; i++)
197 {
198 fprintf (stderr, "%s", "."); fflush (stderr);
199 if (GNUNET_SYSERR == GNUNET_CRYPTO_ecdsa_sign_ (key, &purp, &sig))
200 {
201 fprintf (stderr, "%s",
202 "GNUNET_CRYPTO_ecdsa_sign returned SYSERR\n");
203 ok = GNUNET_SYSERR;
204 continue;
205 }
206 }
207 printf ("%d ECC sign operations %s\n", ITER,
208 GNUNET_STRINGS_relative_time_to_string (
209 GNUNET_TIME_absolute_get_duration (start),
210 GNUNET_YES));
211 return ok;
212}
213
214
215#endif
216
217
218static void
219perf_keygen (void)
220{
221 struct GNUNET_TIME_Absolute start;
222 struct GNUNET_CRYPTO_EcdsaPrivateKey pk;
223
224 fprintf (stderr, "%s", "W");
225 start = GNUNET_TIME_absolute_get ();
226 for (unsigned int i = 0; i < 10; i++)
227 {
228 fprintf (stderr, ".");
229 fflush (stderr);
230 GNUNET_CRYPTO_ecdsa_key_create (&pk);
231 }
232 fflush (stderr);
233 printf ("10 ECDSA keys created in %s\n",
234 GNUNET_STRINGS_relative_time_to_string (
235 GNUNET_TIME_absolute_get_duration (start),
236 GNUNET_YES));
237}
238
239
240int
241main (int argc, char *argv[])
242{
243 int failure_count = 0;
244
245 if (! gcry_check_version ("1.6.0"))
246 {
247 fprintf (stderr,
248 "libgcrypt has not the expected version (version %s is required).\n",
249 "1.6.0");
250 return 0;
251 }
252 if (getenv ("GNUNET_GCRYPT_DEBUG"))
253 gcry_control (GCRYCTL_SET_DEBUG_FLAGS, 1u, 0);
254 GNUNET_log_setup ("test-crypto-ecc", "WARNING", NULL);
255 GNUNET_CRYPTO_ecdsa_key_create (&key);
256 if (GNUNET_OK != testDeriveSignVerify ())
257 {
258 failure_count++;
259 fprintf (stderr,
260 "\n\n%d TESTS FAILED!\n\n", failure_count);
261 return -1;
262 }
263#if PERF
264 if (GNUNET_OK != testSignPerformance ())
265 failure_count++;
266#endif
267 if (GNUNET_OK != testSignVerify ())
268 failure_count++;
269 perf_keygen ();
270
271 if (0 != failure_count)
272 {
273 fprintf (stderr,
274 "\n\n%d TESTS FAILED!\n\n",
275 failure_count);
276 return -1;
277 }
278 return 0;
279}
280
281
282/* end of test_crypto_ecdsa.c */
diff --git a/src/lib/util/test_crypto_eddsa.c b/src/lib/util/test_crypto_eddsa.c
new file mode 100644
index 000000000..820230fd2
--- /dev/null
+++ b/src/lib/util/test_crypto_eddsa.c
@@ -0,0 +1,319 @@
1/*
2 This file is part of GNUnet.
3 Copyright (C) 2002-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/test_crypto_eddsa.c
23 * @brief testcase for ECC public key crypto
24 * @author Christian Grothoff
25 */
26
27#include "platform.h"
28#include "gnunet_util_lib.h"
29#include "gnunet_signatures.h"
30#include <gcrypt.h>
31
32#define ITER 25
33
34#define KEYFILE "/tmp/test-gnunet-crypto-eddsa.key"
35
36#define PERF GNUNET_YES
37
38
39static struct GNUNET_CRYPTO_EddsaPrivateKey key;
40
41
42static int
43testSignVerify (void)
44{
45 struct GNUNET_CRYPTO_EddsaSignature sig;
46 struct GNUNET_CRYPTO_EccSignaturePurpose purp;
47 struct GNUNET_CRYPTO_EddsaPublicKey pkey;
48 struct GNUNET_TIME_Absolute start;
49 int ok = GNUNET_OK;
50
51 fprintf (stderr, "%s", "W");
52 GNUNET_CRYPTO_eddsa_key_get_public (&key,
53 &pkey);
54 start = GNUNET_TIME_absolute_get ();
55 purp.size = htonl (sizeof(struct GNUNET_CRYPTO_EccSignaturePurpose));
56 purp.purpose = htonl (GNUNET_SIGNATURE_PURPOSE_TEST);
57
58 for (unsigned int i = 0; i < ITER; i++)
59 {
60 fprintf (stderr, "%s", "."); fflush (stderr);
61 if (GNUNET_SYSERR == GNUNET_CRYPTO_eddsa_sign_ (&key,
62 &purp,
63 &sig))
64 {
65 fprintf (stderr,
66 "GNUNET_CRYPTO_eddsa_sign returned SYSERR\n");
67 ok = GNUNET_SYSERR;
68 continue;
69 }
70 if (GNUNET_SYSERR ==
71 GNUNET_CRYPTO_eddsa_verify_ (GNUNET_SIGNATURE_PURPOSE_TEST,
72 &purp,
73 &sig,
74 &pkey))
75 {
76 fprintf (stderr,
77 "GNUNET_CRYPTO_eddsa_verify failed!\n");
78 ok = GNUNET_SYSERR;
79 continue;
80 }
81 if (GNUNET_SYSERR !=
82 GNUNET_CRYPTO_eddsa_verify_ (
83 GNUNET_SIGNATURE_PURPOSE_TRANSPORT_PONG_OWN,
84 &purp,
85 &sig,
86 &pkey))
87 {
88 fprintf (stderr,
89 "GNUNET_CRYPTO_eddsa_verify failed to fail!\n");
90 ok = GNUNET_SYSERR;
91 continue;
92 }
93 }
94 fprintf (stderr, "\n");
95 printf ("%d EdDSA sign/verify operations %s\n",
96 ITER,
97 GNUNET_STRINGS_relative_time_to_string (
98 GNUNET_TIME_absolute_get_duration (start),
99 GNUNET_YES));
100 return ok;
101}
102
103
104static int
105testDeriveSignVerify (void)
106{
107 struct GNUNET_CRYPTO_EddsaSignature sig;
108 struct GNUNET_CRYPTO_EccSignaturePurpose purp;
109 struct GNUNET_CRYPTO_EddsaPrivateScalar dpriv;
110 struct GNUNET_CRYPTO_EddsaPublicKey pkey;
111 struct GNUNET_CRYPTO_EddsaPublicKey dpub;
112 struct GNUNET_CRYPTO_EddsaPublicKey dpub2;
113
114 GNUNET_CRYPTO_eddsa_private_key_derive (&key,
115 "test-derive",
116 "test-CTX",
117 &dpriv);
118 GNUNET_CRYPTO_eddsa_key_get_public (&key,
119 &pkey);
120 GNUNET_CRYPTO_eddsa_public_key_derive (&pkey,
121 "test-derive",
122 "test-CTX",
123 &dpub);
124 GNUNET_CRYPTO_eddsa_key_get_public_from_scalar (&dpriv, &dpub2);
125 purp.size = htonl (sizeof(struct GNUNET_CRYPTO_EccSignaturePurpose));
126 purp.purpose = htonl (GNUNET_SIGNATURE_PURPOSE_TEST);
127
128 if (0 != GNUNET_memcmp (&dpub.q_y, &dpub2.q_y))
129 {
130 fprintf (stderr, "%s", "key derivation failed\n");
131 return GNUNET_SYSERR;
132 }
133
134 GNUNET_CRYPTO_eddsa_sign_derived (&key,
135 "test-derive",
136 "test-CTX",
137 &purp,
138 &sig);
139 if (GNUNET_SYSERR ==
140 GNUNET_CRYPTO_eddsa_verify_ (GNUNET_SIGNATURE_PURPOSE_TEST,
141 &purp,
142 &sig,
143 &dpub))
144 {
145 fprintf (stderr,
146 "GNUNET_CRYPTO_eddsa_verify failed!\n");
147 return GNUNET_SYSERR;
148 }
149 if (GNUNET_SYSERR !=
150 GNUNET_CRYPTO_eddsa_verify_ (GNUNET_SIGNATURE_PURPOSE_TEST,
151 &purp,
152 &sig,
153 &pkey))
154 {
155 fprintf (stderr,
156 "GNUNET_CRYPTO_eddsa_verify failed to fail!\n");
157 return GNUNET_SYSERR;
158 }
159 if (GNUNET_SYSERR !=
160 GNUNET_CRYPTO_eddsa_verify_ (GNUNET_SIGNATURE_PURPOSE_TRANSPORT_PONG_OWN,
161 &purp,
162 &sig,
163 &dpub))
164 {
165 fprintf (stderr,
166 "GNUNET_CRYPTO_eddsa_verify failed to fail!\n");
167 return GNUNET_SYSERR;
168 }
169 return GNUNET_OK;
170}
171
172
173#if PERF
174static int
175testSignPerformance ()
176{
177 struct GNUNET_CRYPTO_EccSignaturePurpose purp;
178 struct GNUNET_CRYPTO_EddsaSignature sig;
179 struct GNUNET_CRYPTO_EddsaPublicKey pkey;
180 struct GNUNET_TIME_Absolute start;
181 int ok = GNUNET_OK;
182
183 purp.size = htonl (sizeof(struct GNUNET_CRYPTO_EccSignaturePurpose));
184 purp.purpose = htonl (GNUNET_SIGNATURE_PURPOSE_TEST);
185 fprintf (stderr, "%s", "W");
186 GNUNET_CRYPTO_eddsa_key_get_public (&key,
187 &pkey);
188 start = GNUNET_TIME_absolute_get ();
189 for (unsigned int i = 0; i < ITER; i++)
190 {
191 fprintf (stderr, "%s", ".");
192 fflush (stderr);
193 if (GNUNET_SYSERR ==
194 GNUNET_CRYPTO_eddsa_sign_ (&key,
195 &purp,
196 &sig))
197 {
198 fprintf (stderr, "%s", "GNUNET_CRYPTO_eddsa_sign returned SYSERR\n");
199 ok = GNUNET_SYSERR;
200 continue;
201 }
202 }
203 fprintf (stderr, "\n");
204 printf ("%d EdDSA sign operations %s\n",
205 ITER,
206 GNUNET_STRINGS_relative_time_to_string (
207 GNUNET_TIME_absolute_get_duration (start),
208 GNUNET_YES));
209 return ok;
210}
211
212
213#endif
214
215
216static int
217testCreateFromFile (void)
218{
219 struct GNUNET_CRYPTO_EddsaPublicKey p1;
220 struct GNUNET_CRYPTO_EddsaPublicKey p2;
221
222 /* do_create == GNUNET_YES and non-existing file MUST return GNUNET_YES */
223 GNUNET_assert (0 == unlink (KEYFILE) || ENOENT == errno);
224 GNUNET_assert (GNUNET_YES ==
225 GNUNET_CRYPTO_eddsa_key_from_file (KEYFILE,
226 GNUNET_YES,
227 &key));
228 GNUNET_CRYPTO_eddsa_key_get_public (&key,
229 &p1);
230
231 /* do_create == GNUNET_YES and _existing_ file MUST return GNUNET_NO */
232 GNUNET_assert (GNUNET_NO ==
233 GNUNET_CRYPTO_eddsa_key_from_file (KEYFILE,
234 GNUNET_YES,
235 &key));
236 GNUNET_CRYPTO_eddsa_key_get_public (&key,
237 &p2);
238 GNUNET_assert (0 ==
239 GNUNET_memcmp (&p1,
240 &p2));
241
242 /* do_create == GNUNET_NO and non-existing file MUST return GNUNET_SYSERR */
243 GNUNET_assert (0 == unlink (KEYFILE));
244 GNUNET_assert (GNUNET_SYSERR ==
245 GNUNET_CRYPTO_eddsa_key_from_file (KEYFILE,
246 GNUNET_NO,
247 &key));
248 return GNUNET_OK;
249}
250
251
252static void
253perf_keygen (void)
254{
255 struct GNUNET_TIME_Absolute start;
256 struct GNUNET_CRYPTO_EddsaPrivateKey pk;
257
258 fprintf (stderr, "%s", "W");
259 start = GNUNET_TIME_absolute_get ();
260 for (unsigned int i = 0; i < 10; i++)
261 {
262 fprintf (stderr, ".");
263 fflush (stderr);
264 GNUNET_CRYPTO_eddsa_key_create (&pk);
265 }
266 fprintf (stderr, "\n");
267 printf ("10 EdDSA keys created in %s\n",
268 GNUNET_STRINGS_relative_time_to_string (
269 GNUNET_TIME_absolute_get_duration (start), GNUNET_YES));
270}
271
272
273int
274main (int argc, char *argv[])
275{
276 int failure_count = 0;
277
278 if (! gcry_check_version ("1.6.0"))
279 {
280 fprintf (stderr,
281 "libgcrypt has not the expected version (version %s is required).\n",
282 "1.6.0");
283 return 0;
284 }
285 if (getenv ("GNUNET_GCRYPT_DEBUG"))
286 gcry_control (GCRYCTL_SET_DEBUG_FLAGS, 1u, 0);
287 GNUNET_log_setup ("test-crypto-eddsa",
288 "WARNING",
289 NULL);
290 GNUNET_CRYPTO_eddsa_key_create (&key);
291 if (GNUNET_OK != testDeriveSignVerify ())
292 {
293 failure_count++;
294 fprintf (stderr,
295 "\n\n%d TESTS FAILED!\n\n", failure_count);
296 return -1;
297 }
298#if PERF
299 if (GNUNET_OK != testSignPerformance ())
300 failure_count++;
301#endif
302 if (GNUNET_OK != testSignVerify ())
303 failure_count++;
304 if (GNUNET_OK != testCreateFromFile ())
305 failure_count++;
306 perf_keygen ();
307
308 if (0 != failure_count)
309 {
310 fprintf (stderr,
311 "\n\n%d TESTS FAILED!\n\n",
312 failure_count);
313 return -1;
314 }
315 return 0;
316}
317
318
319/* end of test_crypto_eddsa.c */
diff --git a/src/lib/util/test_crypto_edx25519.c b/src/lib/util/test_crypto_edx25519.c
new file mode 100644
index 000000000..85e235546
--- /dev/null
+++ b/src/lib/util/test_crypto_edx25519.c
@@ -0,0 +1,327 @@
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/**
22 * @file util/test_crypto_edx25519.c
23 * @brief testcase for ECC public key crypto for edx25519
24 * @author Özgür Kesim
25 */
26
27#include "platform.h"
28#include "gnunet_util_lib.h"
29#include "gnunet_signatures.h"
30#include <gcrypt.h>
31
32#define ITER 25
33
34#define KEYFILE "/tmp/test-gnunet-crypto-edx25519.key"
35
36#define PERF GNUNET_YES
37
38
39static struct GNUNET_CRYPTO_Edx25519PrivateKey key;
40
41
42static int
43testSignVerify (void)
44{
45 struct GNUNET_CRYPTO_Edx25519Signature sig;
46 struct GNUNET_CRYPTO_EccSignaturePurpose purp;
47 struct GNUNET_CRYPTO_Edx25519PublicKey pkey;
48 struct GNUNET_TIME_Absolute start;
49 int ok = GNUNET_OK;
50
51 fprintf (stderr, "%s", "W");
52 GNUNET_CRYPTO_edx25519_key_get_public (&key,
53 &pkey);
54 start = GNUNET_TIME_absolute_get ();
55 purp.size = htonl (sizeof(struct GNUNET_CRYPTO_EccSignaturePurpose));
56 purp.purpose = htonl (GNUNET_SIGNATURE_PURPOSE_TEST);
57
58 for (unsigned int i = 0; i < ITER; i++)
59 {
60 fprintf (stderr, "%s", "."); fflush (stderr);
61 if (GNUNET_SYSERR == GNUNET_CRYPTO_edx25519_sign_ (&key,
62 &purp,
63 &sig))
64 {
65 fprintf (stderr,
66 "GNUNET_CRYPTO_edx25519_sign returned SYSERR\n");
67 ok = GNUNET_SYSERR;
68 continue;
69 }
70 if (GNUNET_SYSERR ==
71 GNUNET_CRYPTO_edx25519_verify_ (GNUNET_SIGNATURE_PURPOSE_TEST,
72 &purp,
73 &sig,
74 &pkey))
75 {
76 fprintf (stderr,
77 "GNUNET_CRYPTO_edx25519_verify failed!\n");
78 ok = GNUNET_SYSERR;
79 continue;
80 }
81 if (GNUNET_SYSERR !=
82 GNUNET_CRYPTO_edx25519_verify_ (
83 GNUNET_SIGNATURE_PURPOSE_TRANSPORT_PONG_OWN,
84 &purp,
85 &sig,
86 &pkey))
87 {
88 fprintf (stderr,
89 "GNUNET_CRYPTO_edx25519_verify failed to fail!\n");
90 ok = GNUNET_SYSERR;
91 continue;
92 }
93 }
94 fprintf (stderr, "\n");
95 printf ("%d EdDSA sign/verify operations %s\n",
96 ITER,
97 GNUNET_STRINGS_relative_time_to_string (
98 GNUNET_TIME_absolute_get_duration (start),
99 GNUNET_YES));
100 return ok;
101}
102
103
104static int
105testDeriveSignVerify (void)
106{
107 struct GNUNET_CRYPTO_EccSignaturePurpose purp;
108 struct GNUNET_CRYPTO_Edx25519Signature sig;
109 struct GNUNET_CRYPTO_Edx25519PrivateKey dkey;
110 struct GNUNET_CRYPTO_Edx25519PublicKey pub;
111 struct GNUNET_CRYPTO_Edx25519PublicKey dpub;
112 struct GNUNET_CRYPTO_Edx25519PublicKey dpub2;
113
114 GNUNET_CRYPTO_edx25519_key_get_public (&key, &pub);
115 GNUNET_CRYPTO_edx25519_private_key_derive (&key,
116 "test-derive",
117 sizeof("test-derive"),
118 &dkey);
119 GNUNET_CRYPTO_edx25519_public_key_derive (&pub,
120 "test-derive",
121 sizeof("test-derive"),
122 &dpub);
123 GNUNET_CRYPTO_edx25519_key_get_public (&dkey, &dpub2);
124
125 if (0 != GNUNET_memcmp (&dpub.q_y, &dpub2.q_y))
126 {
127 fprintf (stderr, "key deriviation failed\n");
128 return GNUNET_SYSERR;
129 }
130
131 purp.size = htonl (sizeof(struct GNUNET_CRYPTO_EccSignaturePurpose));
132 purp.purpose = htonl (GNUNET_SIGNATURE_PURPOSE_TEST);
133
134 GNUNET_CRYPTO_edx25519_sign_ (&dkey,
135 &purp,
136 &sig);
137
138 if (GNUNET_SYSERR ==
139 GNUNET_CRYPTO_edx25519_verify_ (GNUNET_SIGNATURE_PURPOSE_TEST,
140 &purp,
141 &sig,
142 &dpub))
143 {
144 fprintf (stderr,
145 "GNUNET_CRYPTO_edx25519_verify failed after derivation!\n");
146 return GNUNET_SYSERR;
147 }
148
149 if (GNUNET_SYSERR !=
150 GNUNET_CRYPTO_edx25519_verify_ (GNUNET_SIGNATURE_PURPOSE_TEST,
151 &purp,
152 &sig,
153 &pub))
154 {
155 fprintf (stderr,
156 "GNUNET_CRYPTO_edx25519_verify failed to fail after derivation!\n");
157 return GNUNET_SYSERR;
158 }
159
160 if (GNUNET_SYSERR !=
161 GNUNET_CRYPTO_edx25519_verify_ (
162 GNUNET_SIGNATURE_PURPOSE_TRANSPORT_PONG_OWN,
163 &purp,
164 &sig,
165 &dpub))
166 {
167 fprintf (stderr,
168 "GNUNET_CRYPTO_edx25519_verify failed to fail after derivation!\n");
169 return GNUNET_SYSERR;
170 }
171 return GNUNET_OK;
172}
173
174
175#if PERF
176static int
177testSignPerformance ()
178{
179 struct GNUNET_CRYPTO_EccSignaturePurpose purp;
180 struct GNUNET_CRYPTO_Edx25519Signature sig;
181 struct GNUNET_CRYPTO_Edx25519PublicKey pkey;
182 struct GNUNET_TIME_Absolute start;
183 int ok = GNUNET_OK;
184
185 purp.size = htonl (sizeof(struct GNUNET_CRYPTO_EccSignaturePurpose));
186 purp.purpose = htonl (GNUNET_SIGNATURE_PURPOSE_TEST);
187 fprintf (stderr, "%s", "W");
188 GNUNET_CRYPTO_edx25519_key_get_public (&key,
189 &pkey);
190 start = GNUNET_TIME_absolute_get ();
191 for (unsigned int i = 0; i < ITER; i++)
192 {
193 fprintf (stderr, "%s", ".");
194 fflush (stderr);
195 if (GNUNET_SYSERR ==
196 GNUNET_CRYPTO_edx25519_sign_ (&key,
197 &purp,
198 &sig))
199 {
200 fprintf (stderr, "%s", "GNUNET_CRYPTO_edx25519_sign returned SYSERR\n");
201 ok = GNUNET_SYSERR;
202 continue;
203 }
204 }
205 fprintf (stderr, "\n");
206 printf ("%d EdDSA sign operations %s\n",
207 ITER,
208 GNUNET_STRINGS_relative_time_to_string (
209 GNUNET_TIME_absolute_get_duration (start),
210 GNUNET_YES));
211 return ok;
212}
213
214
215#endif
216
217
218#if 0 /* not implemented */
219static int
220testCreateFromFile (void)
221{
222 struct GNUNET_CRYPTO_Edx25519PublicKey p1;
223 struct GNUNET_CRYPTO_Edx25519PublicKey p2;
224
225 /* do_create == GNUNET_YES and non-existing file MUST return GNUNET_YES */
226 GNUNET_assert (0 == unlink (KEYFILE) || ENOENT == errno);
227 GNUNET_assert (GNUNET_YES ==
228 GNUNET_CRYPTO_edx25519_key_from_file (KEYFILE,
229 GNUNET_YES,
230 &key));
231 GNUNET_CRYPTO_edx25519_key_get_public (&key,
232 &p1);
233
234 /* do_create == GNUNET_YES and _existing_ file MUST return GNUNET_NO */
235 GNUNET_assert (GNUNET_NO ==
236 GNUNET_CRYPTO_edx25519_key_from_file (KEYFILE,
237 GNUNET_YES,
238 &key));
239 GNUNET_CRYPTO_edx25519_key_get_public (&key,
240 &p2);
241 GNUNET_assert (0 ==
242 GNUNET_memcmp (&p1,
243 &p2));
244
245 /* do_create == GNUNET_NO and non-existing file MUST return GNUNET_SYSERR */
246 GNUNET_assert (0 == unlink (KEYFILE));
247 GNUNET_assert (GNUNET_SYSERR ==
248 GNUNET_CRYPTO_edx25519_key_from_file (KEYFILE,
249 GNUNET_NO,
250 &key));
251 return GNUNET_OK;
252}
253
254
255#endif
256
257
258static void
259perf_keygen (void)
260{
261 struct GNUNET_TIME_Absolute start;
262 struct GNUNET_CRYPTO_Edx25519PrivateKey pk;
263
264 fprintf (stderr, "%s", "W");
265 start = GNUNET_TIME_absolute_get ();
266 for (unsigned int i = 0; i < 10; i++)
267 {
268 fprintf (stderr, ".");
269 fflush (stderr);
270 GNUNET_CRYPTO_edx25519_key_create (&pk);
271 }
272 fprintf (stderr, "\n");
273 printf ("10 EdDSA keys created in %s\n",
274 GNUNET_STRINGS_relative_time_to_string (
275 GNUNET_TIME_absolute_get_duration (start), GNUNET_YES));
276}
277
278
279int
280main (int argc, char *argv[])
281{
282 int failure_count = 0;
283
284 if (! gcry_check_version ("1.6.0"))
285 {
286 fprintf (stderr,
287 "libgcrypt has not the expected version (version %s is required).\n",
288 "1.6.0");
289 return 0;
290 }
291 if (getenv ("GNUNET_GCRYPT_DEBUG"))
292 gcry_control (GCRYCTL_SET_DEBUG_FLAGS, 1u, 0);
293 GNUNET_log_setup ("test-crypto-edx25519",
294 "WARNING",
295 NULL);
296 GNUNET_CRYPTO_edx25519_key_create (&key);
297 if (GNUNET_OK != testDeriveSignVerify ())
298 {
299 failure_count++;
300 fprintf (stderr,
301 "\n\n%d TESTS FAILED!\n\n", failure_count);
302 return -1;
303 }
304#if PERF
305 if (GNUNET_OK != testSignPerformance ())
306 failure_count++;
307#endif
308 if (GNUNET_OK != testSignVerify ())
309 failure_count++;
310#if 0 /* not implemented */
311 if (GNUNET_OK != testCreateFromFile ())
312 failure_count++;
313#endif
314 perf_keygen ();
315
316 if (0 != failure_count)
317 {
318 fprintf (stderr,
319 "\n\n%d TESTS FAILED!\n\n",
320 failure_count);
321 return -1;
322 }
323 return 0;
324}
325
326
327/* end of test_crypto_edx25519.c */
diff --git a/src/lib/util/test_crypto_elligator.c b/src/lib/util/test_crypto_elligator.c
new file mode 100644
index 000000000..2e178b7b1
--- /dev/null
+++ b/src/lib/util/test_crypto_elligator.c
@@ -0,0 +1,302 @@
1#include "gnunet_util_lib.h"
2#include <gcrypt.h>
3#include <stdio.h>
4#include <sodium.h>
5
6#define ITER 25
7
8
9// Test vector from https://github.com/Kleshni/Elligator-2/blob/master/test-vectors.c
10// Using Decoding as a wrapper around direct_map
11static int
12testDirectMap (void)
13{
14 int ok = GNUNET_OK;
15
16 uint8_t repr1[32] = {
17 0x95, 0xa1, 0x60, 0x19, 0x04, 0x1d, 0xbe, 0xfe,
18 0xd9, 0x83, 0x20, 0x48, 0xed, 0xe1, 0x19, 0x28,
19 0xd9, 0x03, 0x65, 0xf2, 0x4a, 0x38, 0xaa, 0x7a,
20 0xef, 0x1b, 0x97, 0xe2, 0x39, 0x54, 0x10, 0x1b
21 };
22
23 uint8_t point1[32] = {
24 0x79, 0x4f, 0x05, 0xba, 0x3e, 0x3a, 0x72, 0x95,
25 0x80, 0x22, 0x46, 0x8c, 0x88, 0x98, 0x1e, 0x0b,
26 0xe5, 0x78, 0x2b, 0xe1, 0xe1, 0x14, 0x5c, 0xe2,
27 0xc3, 0xc6, 0xfd, 0xe1, 0x6d, 0xed, 0x53, 0x63
28 };
29
30 struct GNUNET_CRYPTO_EcdhePublicKey pointResult = {0};
31 struct GNUNET_CRYPTO_ElligatorRepresentative representative = {0};
32 memcpy (&representative.r, &repr1, sizeof(repr1));
33
34 bool highYResult;
35
36 GNUNET_CRYPTO_ecdhe_elligator_decoding (
37 &pointResult,
38 &highYResult,
39 &representative);
40
41 if (memcmp (point1, pointResult.q_y, sizeof(point1)) != 0)
42 {
43 ok = GNUNET_SYSERR;
44 }
45
46 return ok;
47}
48
49
50// Test vector from https://github.com/Kleshni/Elligator-2/blob/master/test-vectors.c
51static int
52testInverseMap (void)
53{
54 int ok = GNUNET_OK;
55 uint8_t point1[32] = {
56 0x33, 0x95, 0x19, 0x64, 0x00, 0x3c, 0x94, 0x08,
57 0x78, 0x06, 0x3c, 0xcf, 0xd0, 0x34, 0x8a, 0xf4,
58 0x21, 0x50, 0xca, 0x16, 0xd2, 0x64, 0x6f, 0x2c,
59 0x58, 0x56, 0xe8, 0x33, 0x83, 0x77, 0xd8, 0x00
60 };
61
62 uint8_t repr1[32] = {
63 0x99, 0x9b, 0x59, 0x1b, 0x66, 0x97, 0xd0, 0x74,
64 0xf2, 0x66, 0x19, 0x22, 0x77, 0xd5, 0x54, 0xde,
65 0xc3, 0xc2, 0x4c, 0x2e, 0xf6, 0x10, 0x81, 0x01,
66 0xf6, 0x3d, 0x94, 0xf7, 0xff, 0xf3, 0xa0, 0x13
67 };
68
69 // uint8_t reprResult1[32];
70 struct GNUNET_CRYPTO_ElligatorRepresentative r = {0};
71 struct GNUNET_CRYPTO_EcdhePublicKey pub = {0};
72 memcpy (&pub.q_y,&point1,sizeof(point1));
73 bool yHigh1 = false;
74
75 bool success = GNUNET_CRYPTO_ecdhe_elligator_encoding (&r,
76 &pub,
77 yHigh1);
78 if (success == false)
79 {
80 ok = GNUNET_SYSERR;
81 }
82 if (memcmp (&repr1,&r.r,sizeof(repr1)) != 0)
83 {
84 ok = GNUNET_SYSERR;
85 }
86
87 return ok;
88}
89
90
91/*
92* Test description: GNUNET_CRYPTO_ecdhe_elligator_generate_public_key() projects a point from the prime subgroup to the whole curve.
93* Both, the original point and the projectes point, should result in the same point when multiplied with a clamped scalar.
94*/
95static int
96testGeneratePkScalarMult (void)
97{
98 struct GNUNET_CRYPTO_EcdhePrivateKey pk;
99 GNUNET_CRYPTO_random_block (GNUNET_CRYPTO_QUALITY_NONCE,
100 &pk,
101 sizeof (struct GNUNET_CRYPTO_EcdhePrivateKey));
102
103 struct GNUNET_CRYPTO_EcdhePublicKey pubWholeCurve = {0};
104 unsigned char pubPrimeCurve[crypto_scalarmult_SCALARBYTES];
105
106 if (GNUNET_CRYPTO_ecdhe_elligator_generate_public_key (&pubWholeCurve,
107 &pk) == -1)
108 {
109 return GNUNET_SYSERR;
110 }
111 crypto_scalarmult_base (pubPrimeCurve, pk.d);
112
113 struct GNUNET_CRYPTO_EcdsaPrivateKey clampedPk;
114 GNUNET_CRYPTO_ecdsa_key_create (&clampedPk);
115 crypto_scalarmult_base (pubWholeCurve.q_y, clampedPk.d);
116 crypto_scalarmult_base (pubPrimeCurve, clampedPk.d);
117 if (memcmp (pubWholeCurve.q_y, pubPrimeCurve, sizeof(pubWholeCurve)) != 0)
118 {
119 return GNUNET_SYSERR;
120 }
121 return GNUNET_OK;
122}
123
124
125/*
126* Test Description: After generating a valid private key and the corresponding representative with
127* GNUNET_CRYPTO_ecdhe_elligator_key_create(), check if using the direct map results in the corresponding public key.
128*/
129static int
130testInverseDirect (void)
131{
132 struct GNUNET_CRYPTO_ElligatorRepresentative repr;
133 struct GNUNET_CRYPTO_EcdhePublicKey point;
134 struct GNUNET_CRYPTO_EcdhePrivateKey pk;
135 GNUNET_CRYPTO_ecdhe_elligator_key_create (&repr, &pk);
136
137 struct GNUNET_CRYPTO_EcdhePublicKey pub = {0};
138 if (GNUNET_CRYPTO_ecdhe_elligator_generate_public_key (&pub, &pk) == -1)
139 {
140 return GNUNET_SYSERR;
141 }
142
143 GNUNET_CRYPTO_ecdhe_elligator_decoding (&point, NULL, &repr);
144
145 if (memcmp (pub.q_y, point.q_y, sizeof(point.q_y)) != 0)
146 {
147 return GNUNET_SYSERR;
148 }
149
150 return GNUNET_OK;
151}
152
153
154/*
155* Test Description: Measuring the time it takes to generate 25 key pairs (pk, representative).
156* Time value can vary because GNUNET_CRYPTO_ecdhe_elligator_key_create generates internally random
157* public keys which are just valid 50% of the time for elligators inverse map.
158* GNUNET_CRYPTO_ecdhe_elligator_key_create will therefore generate as many public keys needed
159* till a valid public key is generated.
160*/
161static int
162testTimeKeyGenerate (void)
163{
164 struct GNUNET_CRYPTO_ElligatorRepresentative repr;
165 struct GNUNET_CRYPTO_EcdhePrivateKey pk;
166 struct GNUNET_TIME_Absolute start;
167 int ok = GNUNET_OK;
168
169 fprintf (stderr, "%s", "W");
170 start = GNUNET_TIME_absolute_get ();
171
172 for (unsigned int i = 0; i < ITER; i++)
173 {
174 fprintf (stderr, "%s", ".");
175 fflush (stderr);
176 GNUNET_CRYPTO_ecdhe_elligator_key_create (&repr, &pk);
177 }
178 printf ("%d encoded public keys generated in %s\n",
179 ITER,
180 GNUNET_STRINGS_relative_time_to_string (
181 GNUNET_TIME_absolute_get_duration (start),
182 GNUNET_YES));
183 return ok;
184}
185
186
187static int
188testTimeDecoding (void)
189{
190 struct GNUNET_CRYPTO_EcdhePublicKey point;
191 struct GNUNET_CRYPTO_ElligatorRepresentative repr[ITER];
192 struct GNUNET_CRYPTO_EcdhePrivateKey pk;
193 struct GNUNET_TIME_Absolute start;
194 int ok = GNUNET_OK;
195
196 for (unsigned int i = 0; i < ITER; i++)
197 {
198 GNUNET_CRYPTO_ecdhe_elligator_key_create (&repr[i], &pk);
199 }
200
201 fprintf (stderr, "%s", "W");
202 start = GNUNET_TIME_absolute_get ();
203
204 for (unsigned int i = 0; i < ITER; i++)
205 {
206 fprintf (stderr, "%s", ".");
207 fflush (stderr);
208 GNUNET_CRYPTO_ecdhe_elligator_decoding (&point, NULL, &repr[i]);
209
210 }
211
212 printf ("%d decoded public keys generated in %s\n",
213 ITER,
214 GNUNET_STRINGS_relative_time_to_string (
215 GNUNET_TIME_absolute_get_duration (start),
216 GNUNET_YES));
217 return ok;
218}
219
220
221static int
222elligatorKEM ()
223{
224 struct GNUNET_CRYPTO_EddsaPrivateKey pk_receiver;
225 struct GNUNET_CRYPTO_EddsaPublicKey pub_receiver;
226 GNUNET_CRYPTO_eddsa_key_create (&pk_receiver);
227 GNUNET_CRYPTO_eddsa_key_get_public (&pk_receiver, &pub_receiver);
228
229 struct GNUNET_CRYPTO_ElligatorRepresentative r_sender;
230
231 // Sender side
232 struct GNUNET_HashCode key_material_encaps;
233 GNUNET_CRYPTO_eddsa_elligator_kem_encaps (&pub_receiver, &r_sender,
234 &key_material_encaps);
235
236 // Receiving side
237 struct GNUNET_HashCode key_material_decaps;
238 GNUNET_CRYPTO_eddsa_elligator_kem_decaps (&pk_receiver, &r_sender,
239 &key_material_decaps);
240
241 if (memcmp (&(key_material_encaps.bits),&(key_material_decaps.bits),
242 sizeof(key_material_encaps.bits)) != 0)
243 {
244 return GNUNET_SYSERR;
245 }
246
247 return GNUNET_OK;
248}
249
250
251int
252main (int argc, char *argv[])
253{
254
255 int failure_count = 0;
256
257 if (GNUNET_OK != testInverseMap ())
258 {
259 printf ("inverse failed!");
260 failure_count++;
261 }
262 if (GNUNET_OK != testDirectMap ())
263 {
264 printf ("direct failed!");
265 failure_count++;
266 }
267 if (GNUNET_OK != testGeneratePkScalarMult ())
268 {
269 printf ("generate PK failed!");
270 failure_count++;
271 }
272 if (GNUNET_OK != testInverseDirect ())
273 {
274 printf ("Inverse and direct map failed!");
275 failure_count++;
276 }
277 if (GNUNET_OK != testTimeKeyGenerate ())
278 {
279 printf ("Time measurement of key generation failed!");
280 failure_count++;
281 }
282 if (GNUNET_OK != testTimeDecoding ())
283 {
284 printf ("Time measurement of decoding failed!");
285 failure_count++;
286 }
287
288 if (GNUNET_OK != elligatorKEM ())
289 {
290 printf ("Elligator KEM failed!");
291 failure_count++;
292 }
293
294 if (0 != failure_count)
295 {
296 fprintf (stderr,
297 "\n\n%d TESTS FAILED!\n\n",
298 failure_count);
299 return -1;
300 }
301 return 0;
302}
diff --git a/src/lib/util/test_crypto_hash.c b/src/lib/util/test_crypto_hash.c
new file mode 100644
index 000000000..337694a89
--- /dev/null
+++ b/src/lib/util/test_crypto_hash.c
@@ -0,0 +1,218 @@
1/*
2 This file is part of GNUnet.
3 Copyright (C) 2002, 2003, 2004, 2006, 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/**
22 * @author Christian Grothoff
23 * @file util/test_crypto_hash.c
24 * @brief Test for crypto_hash.c
25 */
26
27#include "platform.h"
28#include "gnunet_util_lib.h"
29
30static char block[65536];
31
32#define FILENAME "testblock.dat"
33
34static int
35test (int number)
36{
37 struct GNUNET_HashCode h1;
38 struct GNUNET_HashCode h2;
39 struct GNUNET_CRYPTO_HashAsciiEncoded enc;
40
41 memset (&h1,
42 number,
43 sizeof(struct GNUNET_HashCode));
44 GNUNET_CRYPTO_hash_to_enc (&h1,
45 &enc);
46 if (GNUNET_OK !=
47 GNUNET_CRYPTO_hash_from_string ((char *) &enc,
48 &h2))
49 {
50 printf ("enc2hash failed!\n");
51 return 1;
52 }
53 if (0 != GNUNET_memcmp (&h1,
54 &h2))
55 return 1;
56 return 0;
57}
58
59
60static int
61test_encoding (void)
62{
63 for (int i = 0; i < 255; i++)
64 if (0 != test (i))
65 return 1;
66 return 0;
67}
68
69
70static int
71test_arithmetic (void)
72{
73 struct GNUNET_HashCode h1;
74 struct GNUNET_HashCode h2;
75 struct GNUNET_HashCode d;
76 struct GNUNET_HashCode s;
77 struct GNUNET_CRYPTO_SymmetricSessionKey skey;
78 struct GNUNET_CRYPTO_SymmetricInitializationVector iv;
79
80 GNUNET_CRYPTO_hash_create_random (GNUNET_CRYPTO_QUALITY_WEAK,
81 &h1);
82 GNUNET_CRYPTO_hash_create_random (GNUNET_CRYPTO_QUALITY_WEAK,
83 &h2);
84 if (GNUNET_CRYPTO_hash_distance_u32 (&h1,
85 &h2) !=
86 GNUNET_CRYPTO_hash_distance_u32 (&h2,
87 &h1))
88 return 1;
89 GNUNET_CRYPTO_hash_difference (&h1,
90 &h2,
91 &d);
92 GNUNET_CRYPTO_hash_sum (&h1,
93 &d,
94 &s);
95 if (0 !=
96 GNUNET_CRYPTO_hash_cmp (&s,
97 &h2))
98 return 1;
99 GNUNET_CRYPTO_hash_xor (&h1,
100 &h2,
101 &d);
102 GNUNET_CRYPTO_hash_xor (&h1,
103 &d,
104 &s);
105 if (0 !=
106 GNUNET_CRYPTO_hash_cmp (&s,
107 &h2))
108 return 1;
109 if (0 !=
110 GNUNET_CRYPTO_hash_xorcmp (&s,
111 &h2,
112 &h1))
113 return 1;
114 if (-1 !=
115 GNUNET_CRYPTO_hash_xorcmp (&h1,
116 &h2,
117 &h1))
118 return 1;
119 if (1 !=
120 GNUNET_CRYPTO_hash_xorcmp (&h1,
121 &h2,
122 &h2))
123 return 1;
124 memset (&d,
125 0,
126 sizeof(d));
127 GNUNET_CRYPTO_hash_to_aes_key (&d,
128 &skey,
129 &iv);
130 memset (&h1,
131 0,
132 sizeof (h1));
133 h1.bits[1] = htonl (0x00200000); /* 32 + 8 + 2 = 42 MSB bits cleared */
134 GNUNET_assert (42 ==
135 GNUNET_CRYPTO_hash_count_leading_zeros (&h1));
136 GNUNET_assert (512 - 42 - 1 ==
137 GNUNET_CRYPTO_hash_count_tailing_zeros (&h1));
138 return 0;
139}
140
141
142static void
143finished_task (void *cls,
144 const struct GNUNET_HashCode *res)
145{
146 int *ret = cls;
147 struct GNUNET_HashCode want;
148
149 GNUNET_CRYPTO_hash (block,
150 sizeof(block),
151 &want);
152 if (0 != GNUNET_memcmp (res,
153 &want))
154 *ret = 2;
155 else
156 *ret = 0;
157}
158
159
160static void
161file_hasher (void *cls)
162{
163 GNUNET_assert (NULL !=
164 GNUNET_CRYPTO_hash_file (GNUNET_SCHEDULER_PRIORITY_DEFAULT,
165 FILENAME,
166 1024,
167 &finished_task,
168 cls));
169}
170
171
172static int
173test_file_hash (void)
174{
175 int ret;
176 FILE *f;
177
178 memset (block,
179 42,
180 sizeof(block) / 2);
181 memset (&block[sizeof(block) / 2],
182 43,
183 sizeof(block) / 2);
184 GNUNET_assert (NULL != (f = fopen (FILENAME, "w+")));
185 GNUNET_break (sizeof(block) ==
186 fwrite (block,
187 1,
188 sizeof(block),
189 f));
190 GNUNET_break (0 == fclose (f));
191 ret = 1;
192 GNUNET_SCHEDULER_run (&file_hasher,
193 &ret);
194 GNUNET_break (0 == unlink (FILENAME));
195 return ret;
196}
197
198
199int
200main (int argc,
201 char *argv[])
202{
203 int failureCount = 0;
204
205 GNUNET_log_setup ("test-crypto-hash",
206 "WARNING",
207 NULL);
208 for (int i = 0; i < 10; i++)
209 failureCount += test_encoding ();
210 failureCount += test_arithmetic ();
211 failureCount += test_file_hash ();
212 if (0 != failureCount)
213 return 1;
214 return 0;
215}
216
217
218/* end of test_crypto_hash.c */
diff --git a/src/lib/util/test_crypto_hash_context.c b/src/lib/util/test_crypto_hash_context.c
new file mode 100644
index 000000000..08b63800f
--- /dev/null
+++ b/src/lib/util/test_crypto_hash_context.c
@@ -0,0 +1,50 @@
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/test_crypto_hash_context.c
22 * @brief test case for incremental hashing
23 * @author Florian Dold
24 */
25
26#include "platform.h"
27#include "gnunet_util_lib.h"
28
29#define LEN 1234
30
31int
32main ()
33{
34 char data[1234];
35 struct GNUNET_HashCode hc1;
36 struct GNUNET_HashCode hc2;
37 struct GNUNET_HashContext *hctx;
38
39 memset (data, 42, LEN);
40
41 hctx = GNUNET_CRYPTO_hash_context_start ();
42 GNUNET_CRYPTO_hash_context_read (hctx, data, LEN);
43 GNUNET_CRYPTO_hash_context_finish (hctx, &hc1);
44
45 GNUNET_CRYPTO_hash (data, LEN, &hc2);
46
47 if (0 == memcmp (&hc1, &hc2, sizeof(struct GNUNET_HashCode)))
48 return 0;
49 return 1;
50}
diff --git a/src/lib/util/test_crypto_hkdf.c b/src/lib/util/test_crypto_hkdf.c
new file mode 100644
index 000000000..a9a4db7a0
--- /dev/null
+++ b/src/lib/util/test_crypto_hkdf.c
@@ -0,0 +1,339 @@
1/*
2 Copyright (c) 2010 Nils Durner
3
4 Permission is hereby granted, free of charge, to any person obtaining a copy
5 of this software and associated documentation files (the "Software"), to deal
6 in the Software without restriction, including without limitation the rights
7 to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
8 copies of the Software, and to permit persons to whom the Software is
9 furnished to do so, subject to the following conditions:
10
11 The above copyright notice and this permission notice shall be included in
12 all copies or substantial portions of the Software.
13
14 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16 FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17 AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18 LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
19 OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
20 THE SOFTWARE.
21 */
22
23/**
24 * @file src/util/test_crypt_hkdf.c
25 * @brief Testcases for HKDF
26 * @todo: test for out_len < hash_len
27 * @author Nils Durner
28 */
29
30#include "platform.h"
31#include <gcrypt.h>
32
33
34#include "gnunet_util_lib.h"
35
36void
37tc1 ()
38{
39 unsigned char ikm[22] =
40 { 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b,
41 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b };
42 unsigned char salt[13] =
43 { 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09,
44 0x0a, 0x0b, 0x0c };
45 unsigned char info[10] =
46 { 0xf0, 0xf1, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7, 0xf8, 0xf9 };
47 unsigned char okm[42] =
48 { 0x3c, 0xb2, 0x5f, 0x25, 0xfa, 0xac, 0xd5, 0x7a, 0x90, 0x43,
49 0x4f, 0x64, 0xd0, 0x36, 0x2f, 0x2a, 0x2d, 0x2d, 0x0a, 0x90, 0xcf, 0x1a,
50 0x5a, 0x4c, 0x5d, 0xb0, 0x2d, 0x56, 0xec, 0xc4, 0xc5, 0xbf, 0x34, 0x00,
51 0x72, 0x08, 0xd5, 0xb8, 0x87, 0x18, 0x58, 0x65 };
52 unsigned char result[44];
53 int l = 42;
54
55 memset (result, 0, sizeof(result));
56 GNUNET_assert (GNUNET_CRYPTO_hkdf
57 (result, l, GCRY_MD_SHA256, GCRY_MD_SHA256, salt,
58 sizeof(salt), ikm, sizeof(ikm), info, sizeof(info),
59 NULL) == GNUNET_YES);
60 GNUNET_assert (memcmp (result, okm, l) == 0);
61 GNUNET_assert (memcmp (result + l, "\0", 2) == 0);
62}
63
64
65void
66tc2 ()
67{
68 unsigned char ikm[80] =
69 { 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09,
70 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15,
71 0x16, 0x17, 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, 0x20, 0x21,
72 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d,
73 0x2e, 0x2f, 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39,
74 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f, 0x40, 0x41, 0x42, 0x43, 0x44, 0x45,
75 0x46, 0x47, 0x48, 0x49, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e, 0x4f };
76 unsigned char salt[80] =
77 { 0x60, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69,
78 0x6a, 0x6b, 0x6c, 0x6d, 0x6e, 0x6f, 0x70, 0x71, 0x72, 0x73, 0x74, 0x75,
79 0x76, 0x77, 0x78, 0x79, 0x7a, 0x7b, 0x7c, 0x7d, 0x7e, 0x7f, 0x80, 0x81,
80 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, 0x88, 0x89, 0x8a, 0x8b, 0x8c, 0x8d,
81 0x8e, 0x8f, 0x90, 0x91, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, 0x98, 0x99,
82 0x9a, 0x9b, 0x9c, 0x9d, 0x9e, 0x9f, 0xa0, 0xa1, 0xa2, 0xa3, 0xa4, 0xa5,
83 0xa6, 0xa7, 0xa8, 0xa9, 0xaa, 0xab, 0xac, 0xad, 0xae, 0xaf };
84 unsigned char info[80] =
85 { 0xb0, 0xb1, 0xb2, 0xb3, 0xb4, 0xb5, 0xb6, 0xb7, 0xb8, 0xb9,
86 0xba, 0xbb, 0xbc, 0xbd, 0xbe, 0xbf, 0xc0, 0xc1, 0xc2, 0xc3, 0xc4, 0xc5,
87 0xc6, 0xc7, 0xc8, 0xc9, 0xca, 0xcb, 0xcc, 0xcd, 0xce, 0xcf, 0xd0, 0xd1,
88 0xd2, 0xd3, 0xd4, 0xd5, 0xd6, 0xd7, 0xd8, 0xd9, 0xda, 0xdb, 0xdc, 0xdd,
89 0xde, 0xdf, 0xe0, 0xe1, 0xe2, 0xe3, 0xe4, 0xe5, 0xe6, 0xe7, 0xe8, 0xe9,
90 0xea, 0xeb, 0xec, 0xed, 0xee, 0xef, 0xf0, 0xf1, 0xf2, 0xf3, 0xf4, 0xf5,
91 0xf6, 0xf7, 0xf8, 0xf9, 0xfa, 0xfb, 0xfc, 0xfd, 0xfe, 0xff };
92 unsigned char okm[82] =
93 { 0xb1, 0x1e, 0x39, 0x8d, 0xc8, 0x03, 0x27, 0xa1, 0xc8, 0xe7,
94 0xf7, 0x8c, 0x59, 0x6a, 0x49, 0x34, 0x4f, 0x01, 0x2e, 0xda, 0x2d, 0x4e,
95 0xfa, 0xd8, 0xa0, 0x50, 0xcc, 0x4c, 0x19, 0xaf, 0xa9, 0x7c, 0x59, 0x04,
96 0x5a, 0x99, 0xca, 0xc7, 0x82, 0x72, 0x71, 0xcb, 0x41, 0xc6, 0x5e, 0x59,
97 0x0e, 0x09, 0xda, 0x32, 0x75, 0x60, 0x0c, 0x2f, 0x09, 0xb8, 0x36, 0x77,
98 0x93, 0xa9, 0xac, 0xa3, 0xdb, 0x71, 0xcc, 0x30, 0xc5, 0x81, 0x79, 0xec,
99 0x3e, 0x87, 0xc1, 0x4c, 0x01, 0xd5, 0xc1, 0xf3, 0x43, 0x4f, 0x1d, 0x87 };
100 char result[84];
101 int l = 82;
102
103 memset (result, 0, sizeof(result));
104 GNUNET_assert (GNUNET_CRYPTO_hkdf
105 (result, l, GCRY_MD_SHA256, GCRY_MD_SHA256, salt,
106 sizeof(salt), ikm, sizeof(ikm), info, sizeof(info),
107 NULL) == GNUNET_YES);
108 GNUNET_assert (memcmp (result, okm, l) == 0);
109 GNUNET_assert (memcmp (result + l, "\0", 2) == 0);
110}
111
112
113void
114tc3 ()
115{
116 unsigned char ikm[22] =
117 { 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b,
118 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b };
119 unsigned char okm[42] =
120 { 0x8d, 0xa4, 0xe7, 0x75, 0xa5, 0x63, 0xc1, 0x8f, 0x71, 0x5f,
121 0x80, 0x2a, 0x06, 0x3c, 0x5a, 0x31, 0xb8, 0xa1, 0x1f, 0x5c, 0x5e, 0xe1,
122 0x87, 0x9e, 0xc3, 0x45, 0x4e, 0x5f, 0x3c, 0x73, 0x8d, 0x2d, 0x9d, 0x20,
123 0x13, 0x95, 0xfa, 0xa4, 0xb6, 0x1a, 0x96, 0xc8 };
124 unsigned char result[44];
125 int l = 42;
126
127 memset (result, 0, sizeof(result));
128 GNUNET_assert (GNUNET_CRYPTO_hkdf
129 (result, l, GCRY_MD_SHA256, GCRY_MD_SHA256, NULL, 0, ikm,
130 sizeof(ikm), NULL, 0, NULL) == GNUNET_YES);
131 GNUNET_assert (memcmp (result, okm, l) == 0);
132 GNUNET_assert (memcmp (result + l, "\0", 2) == 0);
133}
134
135
136void
137tc4 ()
138{
139 unsigned char ikm[11] =
140 { 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b,
141 0x0b };
142 unsigned char salt[13] =
143 { 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09,
144 0x0a, 0x0b, 0x0c };
145 unsigned char info[10] =
146 { 0xf0, 0xf1, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7, 0xf8, 0xf9 };
147 unsigned char okm[42] =
148 { 0x08, 0x5a, 0x01, 0xea, 0x1b, 0x10, 0xf3, 0x69, 0x33, 0x06,
149 0x8b, 0x56, 0xef, 0xa5, 0xad, 0x81, 0xa4, 0xf1, 0x4b, 0x82, 0x2f, 0x5b,
150 0x09, 0x15, 0x68, 0xa9, 0xcd, 0xd4, 0xf1, 0x55, 0xfd, 0xa2, 0xc2, 0x2e,
151 0x42, 0x24, 0x78, 0xd3, 0x05, 0xf3, 0xf8, 0x96 };
152 char result[84];
153 int l = 42;
154
155 memset (result, 0, sizeof(result));
156 GNUNET_assert (GNUNET_CRYPTO_hkdf
157 (result, l, GCRY_MD_SHA1, GCRY_MD_SHA1, salt, sizeof(salt),
158 ikm, sizeof(ikm), info, sizeof(info), NULL) == GNUNET_YES);
159 GNUNET_assert (memcmp (result, okm, l) == 0);
160 GNUNET_assert (memcmp (result + l, "\0", 2) == 0);
161}
162
163
164void
165tc5 ()
166{
167 unsigned char ikm[80] =
168 { 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09,
169 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15,
170 0x16, 0x17, 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, 0x20, 0x21,
171 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d,
172 0x2e, 0x2f, 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39,
173 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f, 0x40, 0x41, 0x42, 0x43, 0x44, 0x45,
174 0x46, 0x47, 0x48, 0x49, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e, 0x4f };
175 unsigned char salt[80] =
176 { 0x60, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69,
177 0x6a, 0x6b, 0x6c, 0x6d, 0x6e, 0x6f, 0x70, 0x71, 0x72, 0x73, 0x74, 0x75,
178 0x76, 0x77, 0x78, 0x79, 0x7a, 0x7b, 0x7c, 0x7d, 0x7e, 0x7f, 0x80, 0x81,
179 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, 0x88, 0x89, 0x8a, 0x8b, 0x8c, 0x8d,
180 0x8e, 0x8f, 0x90, 0x91, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, 0x98, 0x99,
181 0x9a, 0x9b, 0x9c, 0x9d, 0x9e, 0x9f, 0xa0, 0xa1, 0xa2, 0xa3, 0xa4, 0xa5,
182 0xa6, 0xa7, 0xa8, 0xa9, 0xaa, 0xab, 0xac, 0xad, 0xae, 0xaf };
183 unsigned char info[80] =
184 { 0xb0, 0xb1, 0xb2, 0xb3, 0xb4, 0xb5, 0xb6, 0xb7, 0xb8, 0xb9,
185 0xba, 0xbb, 0xbc, 0xbd, 0xbe, 0xbf, 0xc0, 0xc1, 0xc2, 0xc3, 0xc4, 0xc5,
186 0xc6, 0xc7, 0xc8, 0xc9, 0xca, 0xcb, 0xcc, 0xcd, 0xce, 0xcf, 0xd0, 0xd1,
187 0xd2, 0xd3, 0xd4, 0xd5, 0xd6, 0xd7, 0xd8, 0xd9, 0xda, 0xdb, 0xdc, 0xdd,
188 0xde, 0xdf, 0xe0, 0xe1, 0xe2, 0xe3, 0xe4, 0xe5, 0xe6, 0xe7, 0xe8, 0xe9,
189 0xea, 0xeb, 0xec, 0xed, 0xee, 0xef, 0xf0, 0xf1, 0xf2, 0xf3, 0xf4, 0xf5,
190 0xf6, 0xf7, 0xf8, 0xf9, 0xfa, 0xfb, 0xfc, 0xfd, 0xfe, 0xff };
191 unsigned char okm[82] =
192 { 0x0b, 0xd7, 0x70, 0xa7, 0x4d, 0x11, 0x60, 0xf7, 0xc9, 0xf1,
193 0x2c, 0xd5, 0x91, 0x2a, 0x06, 0xeb, 0xff, 0x6a, 0xdc, 0xae, 0x89, 0x9d,
194 0x92, 0x19, 0x1f, 0xe4, 0x30, 0x56, 0x73, 0xba, 0x2f, 0xfe, 0x8f, 0xa3,
195 0xf1, 0xa4, 0xe5, 0xad, 0x79, 0xf3, 0xf3, 0x34, 0xb3, 0xb2, 0x02, 0xb2,
196 0x17, 0x3c, 0x48, 0x6e, 0xa3, 0x7c, 0xe3, 0xd3, 0x97, 0xed, 0x03, 0x4c,
197 0x7f, 0x9d, 0xfe, 0xb1, 0x5c, 0x5e, 0x92, 0x73, 0x36, 0xd0, 0x44, 0x1f,
198 0x4c, 0x43, 0x00, 0xe2, 0xcf, 0xf0, 0xd0, 0x90, 0x0b, 0x52, 0xd3, 0xb4 };
199 char result[84];
200 int l = 82;
201
202 memset (result, 0, sizeof(result));
203 GNUNET_assert (GNUNET_CRYPTO_hkdf
204 (result, l, GCRY_MD_SHA1, GCRY_MD_SHA1, salt, sizeof(salt),
205 ikm, sizeof(ikm), info, sizeof(info), NULL) == GNUNET_YES);
206 GNUNET_assert (memcmp (result, okm, l) == 0);
207 GNUNET_assert (memcmp (result + l, "\0", 2) == 0);
208}
209
210
211void
212tc6 ()
213{
214 unsigned char ikm[22] =
215 { 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b,
216 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b };
217 unsigned char okm[42] =
218 { 0x0a, 0xc1, 0xaf, 0x70, 0x02, 0xb3, 0xd7, 0x61, 0xd1, 0xe5,
219 0x52, 0x98, 0xda, 0x9d, 0x05, 0x06, 0xb9, 0xae, 0x52, 0x05, 0x72, 0x20,
220 0xa3, 0x06, 0xe0, 0x7b, 0x6b, 0x87, 0xe8, 0xdf, 0x21, 0xd0, 0xea, 0x00,
221 0x03, 0x3d, 0xe0, 0x39, 0x84, 0xd3, 0x49, 0x18 };
222 char result[44];
223 int l = 42;
224
225 memset (result, 0, sizeof(result));
226 GNUNET_assert (GNUNET_CRYPTO_hkdf
227 (result, l, GCRY_MD_SHA1, GCRY_MD_SHA1, NULL, 0, ikm,
228 sizeof(ikm), NULL, 0, NULL) == GNUNET_YES);
229 GNUNET_assert (memcmp (result, okm, l) == 0);
230 GNUNET_assert (memcmp (result + l, "\0", 2) == 0);
231}
232
233
234void
235tc7 ()
236{
237 unsigned char ikm[80] =
238 { 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09,
239 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15,
240 0x16, 0x17, 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, 0x20, 0x21,
241 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d,
242 0x2e, 0x2f, 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39,
243 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f, 0x40, 0x41, 0x42, 0x43, 0x44, 0x45,
244 0x46, 0x47, 0x48, 0x49, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e, 0x4f };
245 unsigned char salt[80] =
246 { 0x60, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69,
247 0x6a, 0x6b, 0x6c, 0x6d, 0x6e, 0x6f, 0x70, 0x71, 0x72, 0x73, 0x74, 0x75,
248 0x76, 0x77, 0x78, 0x79, 0x7a, 0x7b, 0x7c, 0x7d, 0x7e, 0x7f, 0x80, 0x81,
249 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, 0x88, 0x89, 0x8a, 0x8b, 0x8c, 0x8d,
250 0x8e, 0x8f, 0x90, 0x91, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, 0x98, 0x99,
251 0x9a, 0x9b, 0x9c, 0x9d, 0x9e, 0x9f, 0xa0, 0xa1, 0xa2, 0xa3, 0xa4, 0xa5,
252 0xa6, 0xa7, 0xa8, 0xa9, 0xaa, 0xab, 0xac, 0xad, 0xae, 0xaf };
253 unsigned char info1[34] = { 0xb0, 0xb1, 0xb2, 0xb3, 0xb4, 0xb5, 0xb6, 0xb7,
254 0xb8, 0xb9, 0xba, 0xbb, 0xbc, 0xbd, 0xbe, 0xbf,
255 0xc0, 0xc1, 0xc2, 0xc3,
256 0xc4, 0xc5, 0xc6, 0xc7, 0xc8, 0xc9, 0xca, 0xcb,
257 0xcc, 0xcd, 0xce, 0xcf,
258 0xd0, 0xd1 };
259 unsigned char info2[46] = { 0xd2, 0xd3, 0xd4, 0xd5, 0xd6, 0xd7, 0xd8, 0xd9,
260 0xda, 0xdb, 0xdc, 0xdd, 0xde, 0xdf, 0xe0, 0xe1,
261 0xe2, 0xe3, 0xe4, 0xe5,
262 0xe6, 0xe7, 0xe8, 0xe9, 0xea, 0xeb, 0xec, 0xed,
263 0xee, 0xef, 0xf0, 0xf1,
264 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7, 0xf8, 0xf9,
265 0xfa, 0xfb, 0xfc, 0xfd,
266 0xfe, 0xff };
267 unsigned char okm[82] =
268 { 0x0b, 0xd7, 0x70, 0xa7, 0x4d, 0x11, 0x60, 0xf7, 0xc9, 0xf1,
269 0x2c, 0xd5, 0x91, 0x2a, 0x06, 0xeb, 0xff, 0x6a, 0xdc, 0xae, 0x89, 0x9d,
270 0x92, 0x19, 0x1f, 0xe4, 0x30, 0x56, 0x73, 0xba, 0x2f, 0xfe, 0x8f, 0xa3,
271 0xf1, 0xa4, 0xe5, 0xad, 0x79, 0xf3, 0xf3, 0x34, 0xb3, 0xb2, 0x02, 0xb2,
272 0x17, 0x3c, 0x48, 0x6e, 0xa3, 0x7c, 0xe3, 0xd3, 0x97, 0xed, 0x03, 0x4c,
273 0x7f, 0x9d, 0xfe, 0xb1, 0x5c, 0x5e, 0x92, 0x73, 0x36, 0xd0, 0x44, 0x1f,
274 0x4c, 0x43, 0x00, 0xe2, 0xcf, 0xf0, 0xd0, 0x90, 0x0b, 0x52, 0xd3, 0xb4 };
275 char result[84];
276 int l = 82;
277
278 memset (result, 0, sizeof(result));
279 GNUNET_assert (GNUNET_CRYPTO_hkdf
280 (result, l, GCRY_MD_SHA1, GCRY_MD_SHA1, salt, sizeof(salt),
281 ikm, sizeof(ikm), info1, sizeof(info1), info2,
282 sizeof(info2), NULL) == GNUNET_YES);
283 GNUNET_assert (memcmp (result, okm, l) == 0);
284 GNUNET_assert (memcmp (result + l, "\0", 2) == 0);
285}
286
287
288void
289tc8 ()
290{
291 unsigned char ikm[32] =
292 { 0xbf, 0x16, 0x6e, 0x46, 0x3a, 0x6c, 0xf3, 0x93, 0xa7, 0x72,
293 0x11, 0xa1, 0xdc, 0x0b, 0x07, 0xdb, 0x1a, 0x5e, 0xd9, 0xb9, 0x81, 0xbe,
294 0xea, 0xe4, 0x31, 0x5f, 0x24, 0xff, 0xfe, 0x50, 0x8a, 0xde };
295 unsigned char salt[4] = { 0xfc, 0x62, 0x76, 0x35 };
296 unsigned char info[86] =
297 { 0x8c, 0x0d, 0xcf, 0xb3, 0x25, 0x6e, 0x88, 0x0d, 0xc1, 0x0b,
298 0x1d, 0x33, 0x15, 0x3e, 0x52, 0x0b, 0xb0, 0x77, 0xff, 0x7d, 0xc3, 0xc7,
299 0xef, 0xe5, 0x8e, 0x3c, 0xc4, 0x4e, 0x8b, 0x41, 0x46, 0x1f, 0x02, 0x94,
300 0x82, 0x35, 0xc5, 0xa6, 0x5e, 0x91, 0xd8, 0xa2, 0x90, 0xfd, 0x6f, 0xb4,
301 0x07, 0xc9, 0xed, 0x6b, 0x18, 0x90, 0x31, 0xab, 0x0f, 0xb5, 0x6b, 0xec,
302 0x9e, 0x45, 0xa2, 0x83, 0x65, 0x41, 0x69, 0x6e, 0x69, 0x74, 0x69, 0x61,
303 0x6c, 0x69, 0x7a, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x76, 0x65, 0x63,
304 0x74, 0x6f, 0x72, 0x00 };
305 unsigned char okm[16] =
306 { 0xd6, 0x90, 0xec, 0x9e, 0x62, 0xdf, 0xb9, 0x41, 0xff, 0x92,
307 0x4f, 0xd2, 0xf6, 0x1d, 0x67, 0xe0 };
308 char result[18];
309 int l = 16;
310
311 memset (result, 0, sizeof(result));
312 GNUNET_assert (GNUNET_CRYPTO_hkdf
313 (result, l, GCRY_MD_SHA512, GCRY_MD_SHA256, salt,
314 sizeof(salt), ikm, sizeof(ikm), info, sizeof(info),
315 NULL) == GNUNET_YES);
316 GNUNET_assert (memcmp (result, okm, l) == 0);
317 GNUNET_assert (memcmp (result + l, "\0", 2) == 0);
318}
319
320
321int
322main ()
323{
324 GNUNET_log_setup ("test-crypto-hkdf", "WARNING", NULL);
325
326 /* Official test vectors */
327 tc1 ();
328 tc2 ();
329 tc3 ();
330 tc4 ();
331 tc5 ();
332 tc6 ();
333
334 /* Additional tests */
335 tc7 ();
336 tc8 ();
337
338 return 0;
339}
diff --git a/src/lib/util/test_crypto_kdf.c b/src/lib/util/test_crypto_kdf.c
new file mode 100644
index 000000000..81e4b4451
--- /dev/null
+++ b/src/lib/util/test_crypto_kdf.c
@@ -0,0 +1,72 @@
1/*
2 Copyright (c) 2010 Jeffrey Burdges
3
4 Permission is hereby granted, free of charge, to any person obtaining a copy
5 of this software and associated documentation files (the "Software"), to deal
6 in the Software without restriction, including without limitation the rights
7 to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
8 copies of the Software, and to permit persons to whom the Software is
9 furnished to do so, subject to the following conditions:
10
11 The above copyright notice and this permission notice shall be included in
12 all copies or substantial portions of the Software.
13
14 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16 FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17 AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18 LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
19 OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
20 THE SOFTWARE.
21 */
22
23/**
24 * @file src/util/test_crypt_kdf.c
25 * @brief Testcases for KDF mod n
26 * @author Jeffrey Burdges <burdges@gnunet.org>
27 */
28
29#include "platform.h"
30#include <gcrypt.h>
31
32
33#include "gnunet_util_lib.h"
34
35
36int
37main ()
38{
39#define RND_BLK_SIZE 4096
40 unsigned char rnd_blk[RND_BLK_SIZE];
41 int i;
42 gcry_mpi_t r, n;
43
44 GNUNET_log_setup ("test-crypto-kdf", "WARNING", NULL);
45
46 GNUNET_CRYPTO_random_block (GNUNET_CRYPTO_QUALITY_WEAK,
47 rnd_blk,
48 RND_BLK_SIZE);
49
50 /* test full domain hash size */
51 for (i = 0; i < 100; i++)
52 {
53 gcry_mpi_scan (&n,
54 GCRYMPI_FMT_USG,
55 rnd_blk, RND_BLK_SIZE,
56 NULL);
57 GNUNET_CRYPTO_kdf_mod_mpi (&r, n,
58 "", 0,
59 "", 0,
60 "");
61 GNUNET_assert (0 > gcry_mpi_cmp (r, n));
62
63 /* Is it worth checking that it's not too small? */
64 /* GNUNET_assert (gcry_mpi_get_nbits(r) > 3*RND_BLK_SIZE/4); */
65 /* This test necessarily randomly fails with probability 2^(3 - RND_BLK_SIZE/4) */
66
67 gcry_mpi_release (n);
68 gcry_mpi_release (r);
69 }
70
71 return 0;
72}
diff --git a/src/lib/util/test_crypto_paillier.c b/src/lib/util/test_crypto_paillier.c
new file mode 100644
index 000000000..412ce5c23
--- /dev/null
+++ b/src/lib/util/test_crypto_paillier.c
@@ -0,0 +1,248 @@
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/**
22 * @file util/test_crypto_paillier.c
23 * @brief testcase paillier crypto
24 * @author Christian Fuchs
25 * @author Florian Dold
26 */
27
28#include "platform.h"
29#include "gnunet_util_lib.h"
30#include <gcrypt.h>
31
32
33static int
34test_crypto ()
35{
36 gcry_mpi_t plaintext;
37 gcry_mpi_t plaintext_result;
38 struct GNUNET_CRYPTO_PaillierCiphertext ciphertext;
39 struct GNUNET_CRYPTO_PaillierPublicKey public_key;
40 struct GNUNET_CRYPTO_PaillierPrivateKey private_key;
41 int ret = 0;
42
43 GNUNET_CRYPTO_paillier_create (&public_key,
44 &private_key);
45 GNUNET_assert (NULL != (plaintext = gcry_mpi_new (0)));
46 GNUNET_assert (NULL != (plaintext_result = gcry_mpi_new (0)));
47 gcry_mpi_randomize (plaintext,
48 GNUNET_CRYPTO_PAILLIER_BITS / 2,
49 GCRY_WEAK_RANDOM);
50
51 GNUNET_CRYPTO_paillier_encrypt (&public_key,
52 plaintext,
53 0 /* 0 hom ops */,
54 &ciphertext);
55 GNUNET_CRYPTO_paillier_decrypt (&private_key,
56 &public_key,
57 &ciphertext,
58 plaintext_result);
59 if (0 != gcry_mpi_cmp (plaintext,
60 plaintext_result))
61 {
62 fprintf (stderr,
63 "Paillier decryption failed with plaintext of size %u\n",
64 gcry_mpi_get_nbits (plaintext));
65 gcry_log_debugmpi ("\n",
66 plaintext);
67 gcry_log_debugmpi ("\n",
68 plaintext_result);
69 ret = 1;
70 }
71 gcry_mpi_release (plaintext);
72 gcry_mpi_release (plaintext_result);
73 return ret;
74}
75
76
77static int
78test_hom_simple (unsigned int a,
79 unsigned int b)
80{
81 gcry_mpi_t m1;
82 gcry_mpi_t m2;
83 gcry_mpi_t result;
84 gcry_mpi_t hom_result;
85 struct GNUNET_CRYPTO_PaillierCiphertext c1;
86 struct GNUNET_CRYPTO_PaillierCiphertext c2;
87 struct GNUNET_CRYPTO_PaillierCiphertext c_result;
88 struct GNUNET_CRYPTO_PaillierPublicKey public_key;
89 struct GNUNET_CRYPTO_PaillierPrivateKey private_key;
90 int ret = 0;
91
92 GNUNET_CRYPTO_paillier_create (&public_key,
93 &private_key);
94
95 GNUNET_assert (NULL != (m1 = gcry_mpi_new (0)));
96 GNUNET_assert (NULL != (m2 = gcry_mpi_new (0)));
97 GNUNET_assert (NULL != (result = gcry_mpi_new (0)));
98 GNUNET_assert (NULL != (hom_result = gcry_mpi_new (0)));
99 m1 = gcry_mpi_set_ui (m1, a);
100 m2 = gcry_mpi_set_ui (m2, b);
101 gcry_mpi_add (result,
102 m1,
103 m2);
104 GNUNET_CRYPTO_paillier_encrypt (&public_key,
105 m1,
106 2,
107 &c1);
108 GNUNET_CRYPTO_paillier_encrypt (&public_key,
109 m2,
110 2,
111 &c2);
112 GNUNET_CRYPTO_paillier_hom_add (&public_key,
113 &c1,
114 &c2,
115 &c_result);
116 GNUNET_CRYPTO_paillier_decrypt (&private_key,
117 &public_key,
118 &c_result,
119 hom_result);
120 if (0 != gcry_mpi_cmp (result, hom_result))
121 {
122 fprintf (stderr,
123 "GNUNET_CRYPTO_paillier failed simple math!\n");
124 gcry_log_debugmpi ("got ", hom_result);
125 gcry_log_debugmpi ("wanted ", result);
126 ret = 1;
127 }
128 gcry_mpi_release (m1);
129 gcry_mpi_release (m2);
130 gcry_mpi_release (result);
131 gcry_mpi_release (hom_result);
132 return ret;
133}
134
135
136static int
137test_hom ()
138{
139 int ret;
140 gcry_mpi_t m1;
141 gcry_mpi_t m2;
142 gcry_mpi_t result;
143 gcry_mpi_t hom_result;
144 struct GNUNET_CRYPTO_PaillierCiphertext c1;
145 struct GNUNET_CRYPTO_PaillierCiphertext c2;
146 struct GNUNET_CRYPTO_PaillierCiphertext c_result;
147 struct GNUNET_CRYPTO_PaillierPublicKey public_key;
148 struct GNUNET_CRYPTO_PaillierPrivateKey private_key;
149
150 GNUNET_CRYPTO_paillier_create (&public_key,
151 &private_key);
152
153 GNUNET_assert (NULL != (m1 = gcry_mpi_new (0)));
154 GNUNET_assert (NULL != (m2 = gcry_mpi_new (0)));
155 GNUNET_assert (NULL != (result = gcry_mpi_new (0)));
156 GNUNET_assert (NULL != (hom_result = gcry_mpi_new (0)));
157 m1 = gcry_mpi_set_ui (m1, 1);
158 /* m1 = m1 * 2 ^ (GCPB - 3) */
159 gcry_mpi_mul_2exp (m1,
160 m1,
161 GNUNET_CRYPTO_PAILLIER_BITS - 3);
162 m2 = gcry_mpi_set_ui (m2, 15);
163 /* m1 = m1 * 2 ^ (GCPB / 2) */
164 gcry_mpi_mul_2exp (m2,
165 m2,
166 GNUNET_CRYPTO_PAILLIER_BITS / 2);
167 gcry_mpi_add (result,
168 m1,
169 m2);
170
171 if (1 != (ret = GNUNET_CRYPTO_paillier_encrypt (&public_key,
172 m1,
173 2,
174 &c1)))
175 {
176 fprintf (stderr,
177 "GNUNET_CRYPTO_paillier_encrypt 1 failed, should return 1 allowed operation, got %d!\n",
178 ret);
179 ret = 1;
180 goto out;
181 }
182 if (2 != (ret = GNUNET_CRYPTO_paillier_encrypt (&public_key,
183 m2,
184 2,
185 &c2)))
186 {
187 fprintf (stderr,
188 "GNUNET_CRYPTO_paillier_encrypt 2 failed, should return 2 allowed operation, got %d!\n",
189 ret);
190 ret = 1;
191 goto out;
192 }
193
194 if (0 != (ret = GNUNET_CRYPTO_paillier_hom_add (&public_key,
195 &c1,
196 &c2,
197 &c_result)))
198 {
199 fprintf (stderr,
200 "GNUNET_CRYPTO_paillier_hom_add failed, expected 0 remaining operations, got %d!\n",
201 ret);
202 ret = 1;
203 goto out;
204 }
205
206 GNUNET_CRYPTO_paillier_decrypt (&private_key,
207 &public_key,
208 &c_result,
209 hom_result);
210
211 if (0 != gcry_mpi_cmp (result, hom_result))
212 {
213 fprintf (stderr,
214 "GNUNET_CRYPTO_paillier miscalculated with large numbers!\n");
215 gcry_log_debugmpi ("got", hom_result);
216 gcry_log_debugmpi ("wanted", result);
217 ret = 1;
218 }
219out:
220 gcry_mpi_release (m1);
221 gcry_mpi_release (m2);
222 gcry_mpi_release (result);
223 gcry_mpi_release (hom_result);
224 return ret;
225}
226
227
228int
229main (int argc,
230 char *argv[])
231{
232 int ret;
233
234 ret = test_crypto ();
235 if (0 != ret)
236 return ret;
237 ret = test_hom_simple (2, 4);
238 if (0 != ret)
239 return ret;
240 ret = test_hom_simple (13, 17);
241 if (0 != ret)
242 return ret;
243 ret = test_hom ();
244 return ret;
245}
246
247
248/* end of test_crypto_paillier.c */
diff --git a/src/lib/util/test_crypto_random.c b/src/lib/util/test_crypto_random.c
new file mode 100644
index 000000000..b776aef18
--- /dev/null
+++ b/src/lib/util/test_crypto_random.c
@@ -0,0 +1,74 @@
1/*
2 This file is part of GNUnet.
3 Copyright (C) 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
22/**
23 * @file util/test_crypto_random.c
24 * @brief testcase for crypto_random.c
25 */
26
27#include "platform.h"
28#include "gnunet_util_lib.h"
29
30static int
31test (enum GNUNET_CRYPTO_Quality mode)
32{
33 int buf[1024];
34 unsigned int *b2;
35 int i;
36 unsigned long long n;
37 struct GNUNET_Uuid tf;
38
39 for (i = 0; i < 1024; i++)
40 GNUNET_break (1024 > (buf[i] = GNUNET_CRYPTO_random_u32 (mode, 1024)));
41 for (i = 0; i < 10; i++)
42 {
43 b2 = GNUNET_CRYPTO_random_permute (mode, 1024);
44 if (0 == memcmp (b2, buf, sizeof(buf)))
45 {
46 fprintf (stderr, "%s", "!");
47 GNUNET_free (b2);
48 continue;
49 }
50 GNUNET_free (b2);
51 break;
52 }
53 if (i == 10)
54 return 1; /* virtually impossible... */
55
56 for (n = 10; n < 1024LL * 1024LL * 1024LL; n *= 10)
57 GNUNET_break (n > GNUNET_CRYPTO_random_u64 (mode, n));
58 GNUNET_CRYPTO_random_timeflake (mode,
59 &tf);
60 return 0;
61}
62
63
64int
65main (int argc, char *argv[])
66{
67 GNUNET_log_setup ("test-crypto-random", "WARNING", NULL);
68 if (0 != test (GNUNET_CRYPTO_QUALITY_WEAK))
69 return 1;
70 if (0 != test (GNUNET_CRYPTO_QUALITY_STRONG))
71 return 1;
72
73 return 0;
74}
diff --git a/src/lib/util/test_crypto_rsa.c b/src/lib/util/test_crypto_rsa.c
new file mode 100644
index 000000000..c513a68fe
--- /dev/null
+++ b/src/lib/util/test_crypto_rsa.c
@@ -0,0 +1,171 @@
1/*
2 This file is part of GNUnet
3 Copyright (C) 2014, 2015, 2023 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/test_crypto_rsa.c
23 * @brief testcase for utility functions for RSA cryptography
24 * @author Sree Harsha Totakura <sreeharsha@totakura.in>
25 * @author Jeffrey Burdges <burdges@gnunet.org>
26 */
27
28#include "platform.h"
29#include <gcrypt.h>
30#include "gnunet_util_lib.h"
31
32#define KEY_SIZE 1024
33
34
35int
36main (int argc,
37 char *argv[])
38{
39#define RND_BLK_SIZE 4096
40 unsigned char rnd_blk[RND_BLK_SIZE];
41 struct GNUNET_CRYPTO_RsaPrivateKey *priv;
42 struct GNUNET_CRYPTO_RsaPrivateKey *priv_copy;
43 struct GNUNET_CRYPTO_RsaPublicKey *pub;
44 struct GNUNET_CRYPTO_RsaPublicKey *pub_copy;
45 struct GNUNET_CRYPTO_RsaSignature *sig;
46 struct GNUNET_CRYPTO_RsaSignature *sig_copy;
47 struct GNUNET_CRYPTO_RsaSignature *bsig;
48 struct GNUNET_CRYPTO_RsaBlindingKeySecret bsec;
49 struct GNUNET_HashCode hash;
50
51 GNUNET_log_setup ("test-crypto-rsa",
52 "WARNING",
53 NULL);
54 GNUNET_CRYPTO_random_block (GNUNET_CRYPTO_QUALITY_WEAK,
55 rnd_blk,
56 RND_BLK_SIZE);
57 GNUNET_CRYPTO_hash (rnd_blk,
58 RND_BLK_SIZE,
59 &hash);
60 priv = GNUNET_CRYPTO_rsa_private_key_create (KEY_SIZE);
61 priv_copy = GNUNET_CRYPTO_rsa_private_key_dup (priv);
62 GNUNET_assert (NULL != priv_copy);
63 GNUNET_assert (0 == GNUNET_CRYPTO_rsa_private_key_cmp (priv,
64 priv_copy));
65 pub = GNUNET_CRYPTO_rsa_private_key_get_public (priv);
66
67 /* Encoding */
68 size_t size;
69 void *enc;
70 enc = NULL;
71 size = GNUNET_CRYPTO_rsa_private_key_encode (priv,
72 &enc);
73
74 /* Decoding */
75 GNUNET_CRYPTO_rsa_private_key_free (priv);
76 priv = NULL;
77 priv = GNUNET_CRYPTO_rsa_private_key_decode (enc,
78 size);
79 GNUNET_assert (NULL != priv);
80 GNUNET_CRYPTO_random_block (GNUNET_CRYPTO_QUALITY_WEAK,
81 enc,
82 size);
83 GNUNET_assert (NULL ==
84 GNUNET_CRYPTO_rsa_private_key_decode (enc,
85 size));
86 (void) fprintf (stderr,
87 "The above warning is expected.\n");
88 GNUNET_free (enc);
89
90 /* try ordinary sig first */
91 sig = GNUNET_CRYPTO_rsa_sign_fdh (priv,
92 &hash,
93 sizeof (hash));
94 sig_copy = GNUNET_CRYPTO_rsa_signature_dup (sig);
95 GNUNET_assert (NULL != sig);
96 GNUNET_assert (0 == GNUNET_CRYPTO_rsa_signature_cmp (sig,
97 sig_copy));
98 pub_copy = GNUNET_CRYPTO_rsa_public_key_dup (pub);
99 GNUNET_assert (NULL != pub_copy);
100 GNUNET_assert (GNUNET_OK ==
101 GNUNET_CRYPTO_rsa_verify (&hash,
102 sizeof (hash),
103 sig,
104 pub_copy));
105 {
106 void *buf;
107 size_t buf_size;
108 struct GNUNET_CRYPTO_RsaPublicKey *pub2;
109 struct GNUNET_CRYPTO_RsaSignature *sig2;
110
111 buf_size = GNUNET_CRYPTO_rsa_public_key_encode (pub,
112 &buf);
113 pub2 = GNUNET_CRYPTO_rsa_public_key_decode (buf,
114 buf_size);
115 GNUNET_free (buf);
116 buf_size = GNUNET_CRYPTO_rsa_signature_encode (sig,
117 &buf);
118 sig2 = GNUNET_CRYPTO_rsa_signature_decode (buf,
119 buf_size);
120 GNUNET_free (buf);
121 GNUNET_assert (GNUNET_OK ==
122 GNUNET_CRYPTO_rsa_verify (&hash,
123 sizeof (hash),
124 sig2,
125 pub2));
126 GNUNET_CRYPTO_rsa_public_key_free (pub2);
127 GNUNET_CRYPTO_rsa_signature_free (sig2);
128 }
129 /* corrupt our hash and see if the signature is still valid */
130 GNUNET_CRYPTO_random_block (GNUNET_CRYPTO_QUALITY_WEAK,
131 &hash,
132 sizeof(hash));
133 GNUNET_assert (GNUNET_OK !=
134 GNUNET_CRYPTO_rsa_verify (&hash,
135 sizeof (hash),
136 sig,
137 pub));
138 (void) fprintf (stderr,
139 "The above warning is expected.\n");
140 GNUNET_CRYPTO_rsa_signature_free (sig);
141
142 /* test blind signing */
143 GNUNET_CRYPTO_random_block (GNUNET_CRYPTO_QUALITY_WEAK,
144 &bsec,
145 sizeof(bsec));
146 struct GNUNET_CRYPTO_RsaBlindedMessage bm;
147 GNUNET_CRYPTO_rsa_blind (&hash,
148 sizeof (hash),
149 &bsec,
150 pub,
151 &bm);
152 bsig = GNUNET_CRYPTO_rsa_sign_blinded (priv,
153 &bm);
154 GNUNET_CRYPTO_rsa_blinded_message_free (&bm);
155 sig = GNUNET_CRYPTO_rsa_unblind (bsig,
156 &bsec,
157 pub);
158 GNUNET_CRYPTO_rsa_signature_free (bsig);
159 GNUNET_assert (GNUNET_OK ==
160 GNUNET_CRYPTO_rsa_verify (&hash,
161 sizeof (hash),
162 sig,
163 pub));
164 GNUNET_CRYPTO_rsa_signature_free (sig);
165 GNUNET_CRYPTO_rsa_signature_free (sig_copy);
166 GNUNET_CRYPTO_rsa_private_key_free (priv);
167 GNUNET_CRYPTO_rsa_private_key_free (priv_copy);
168 GNUNET_CRYPTO_rsa_public_key_free (pub);
169 GNUNET_CRYPTO_rsa_public_key_free (pub_copy);
170 return 0;
171}
diff --git a/src/lib/util/test_crypto_symmetric.c b/src/lib/util/test_crypto_symmetric.c
new file mode 100644
index 000000000..4c8c2f0c2
--- /dev/null
+++ b/src/lib/util/test_crypto_symmetric.c
@@ -0,0 +1,176 @@
1/*
2 This file is part of GNUnet.
3 Copyright (C) 2002, 2003, 2004, 2006 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 * @author Christian Grothoff
23 * @file util/test_crypto_symmetric.c
24 * @brief test for AES ciphers
25 */
26
27#include "platform.h"
28#include "gnunet_util_lib.h"
29
30#define TESTSTRING "Hello World!"
31#define INITVALUE "InitializationVectorValueinitializationvectorvalue"
32
33static int
34testSymcipher ()
35{
36 struct GNUNET_CRYPTO_SymmetricSessionKey key;
37 char result[100];
38 int size;
39 char res[100];
40
41 GNUNET_CRYPTO_symmetric_create_session_key (&key);
42 size =
43 GNUNET_CRYPTO_symmetric_encrypt (TESTSTRING, strlen (TESTSTRING) + 1, &key,
44 (const struct
45 GNUNET_CRYPTO_SymmetricInitializationVector
46 *)
47 INITVALUE, result);
48 if (size == -1)
49 {
50 printf ("symciphertest failed: encryptBlock returned %d\n", size);
51 return 1;
52 }
53 size =
54 GNUNET_CRYPTO_symmetric_decrypt (result, size, &key,
55 (const struct
56 GNUNET_CRYPTO_SymmetricInitializationVector
57 *)
58 INITVALUE, res);
59 if (strlen (TESTSTRING) + 1 != size)
60 {
61 printf ("symciphertest failed: decryptBlock returned %d\n", size);
62 return 1;
63 }
64 if (0 != strcmp (res, TESTSTRING))
65 {
66 printf ("symciphertest failed: %s != %s\n", res, TESTSTRING);
67 return 1;
68 }
69 else
70 return 0;
71}
72
73
74static int
75verifyCrypto ()
76{
77 struct GNUNET_CRYPTO_SymmetricSessionKey key;
78 char result[GNUNET_CRYPTO_AES_KEY_LENGTH];
79 char *res;
80 int ret;
81
82 unsigned char plain[] = {
83 29, 128, 192, 253, 74, 171, 38, 187, 84, 219, 76, 76, 209, 118, 33, 249,
84 172, 124, 96, 9, 157, 110, 8, 215, 200, 63, 69, 230, 157, 104, 247, 164
85 };
86 unsigned char raw_key_aes[] = {
87 106, 74, 209, 88, 145, 55, 189, 135, 125, 180, 225, 108, 183, 54, 25,
88 169, 129, 188, 131, 75, 227, 245, 105, 10, 225, 15, 115, 159, 148, 184,
89 34, 191
90 };
91 unsigned char raw_key_twofish[] = {
92 145, 55, 189, 135, 125, 180, 225, 108, 183, 54, 25,
93 169, 129, 188, 131, 75, 227, 245, 105, 10, 225, 15, 115, 159, 148, 184,
94 34, 191, 106, 74, 209, 88
95 };
96 unsigned char encrresult[] = {
97 155, 88, 106, 174, 124, 172, 47, 149, 85, 15, 208, 176, 65, 124, 155,
98 74, 215, 25, 177, 231, 162, 109, 165, 4, 133, 165, 93, 44, 213, 77,
99 206, 204, 1
100 };
101
102 res = NULL;
103 ret = 0;
104
105 GNUNET_memcpy (key.aes_key, raw_key_aes, GNUNET_CRYPTO_AES_KEY_LENGTH);
106 GNUNET_memcpy (key.twofish_key, raw_key_twofish,
107 GNUNET_CRYPTO_AES_KEY_LENGTH);
108 if (GNUNET_CRYPTO_AES_KEY_LENGTH !=
109 GNUNET_CRYPTO_symmetric_encrypt (plain, GNUNET_CRYPTO_AES_KEY_LENGTH,
110 &key,
111 (const struct
112 GNUNET_CRYPTO_SymmetricInitializationVector
113 *)
114 "testtesttesttesttesttesttesttest",
115 result))
116 {
117 printf ("Wrong return value from encrypt block.\n");
118 ret = 1;
119 goto error;
120 }
121
122 if (0 != memcmp (encrresult, result, GNUNET_CRYPTO_AES_KEY_LENGTH))
123 {
124 int i;
125 printf ("Encrypted result wrong.\n");
126 for (i = 0; i < GNUNET_CRYPTO_AES_KEY_LENGTH; i++)
127 printf ("%u, ", (uint8_t) result[i]);
128 ret = 1;
129 goto error;
130 }
131
132 res = GNUNET_malloc (GNUNET_CRYPTO_AES_KEY_LENGTH);
133 if (GNUNET_CRYPTO_AES_KEY_LENGTH !=
134 GNUNET_CRYPTO_symmetric_decrypt (result, GNUNET_CRYPTO_AES_KEY_LENGTH,
135 &key,
136 (const struct
137 GNUNET_CRYPTO_SymmetricInitializationVector
138 *)
139 "testtesttesttesttesttesttesttest", res))
140 {
141 printf ("Wrong return value from decrypt block.\n");
142 ret = 1;
143 goto error;
144 }
145 if (0 != memcmp (res, plain, GNUNET_CRYPTO_AES_KEY_LENGTH))
146 {
147 printf ("Decrypted result does not match input.\n");
148 ret = 1;
149 }
150error:
151 GNUNET_free (res);
152 return ret;
153}
154
155
156int
157main (int argc, char *argv[])
158{
159 int failureCount = 0;
160
161 GNUNET_log_setup ("test-crypto-aes", "WARNING", NULL);
162 GNUNET_assert (strlen (INITVALUE) >
163 sizeof(struct GNUNET_CRYPTO_SymmetricInitializationVector));
164 failureCount += testSymcipher ();
165 failureCount += verifyCrypto ();
166
167 if (failureCount != 0)
168 {
169 printf ("%d TESTS FAILED!\n", failureCount);
170 return -1;
171 }
172 return 0;
173}
174
175
176/* end of test_crypto_aes.c */
diff --git a/src/lib/util/test_disk.c b/src/lib/util/test_disk.c
new file mode 100644
index 000000000..35b4bd14a
--- /dev/null
+++ b/src/lib/util/test_disk.c
@@ -0,0 +1,291 @@
1/*
2 This file is part of GNUnet.
3 Copyright (C) 2001, 2002, 2003, 2005, 2006, 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/**
22 * @file util/test_disk.c
23 * @brief testcase for the storage module
24 * @author Christian Grothoff
25 */
26
27#include "platform.h"
28#include "gnunet_util_lib.h"
29
30#define TESTSTRING "Hello World\0"
31
32
33static int
34testReadWrite (void)
35{
36 char tmp[100 + 1];
37 int ret;
38
39 if (GNUNET_OK !=
40 GNUNET_DISK_fn_write (".testfile", TESTSTRING, strlen (TESTSTRING),
41 GNUNET_DISK_PERM_USER_READ
42 | GNUNET_DISK_PERM_USER_WRITE))
43 return 1;
44 if (GNUNET_OK != GNUNET_DISK_file_test (".testfile"))
45 return 1;
46 ret = GNUNET_DISK_fn_read (".testfile", tmp, sizeof(tmp) - 1);
47 if (ret < 0)
48 {
49 fprintf (stderr, "Error reading file `%s' in testReadWrite\n", ".testfile");
50 return 1;
51 }
52 tmp[ret] = '\0';
53 if (0 != memcmp (tmp, TESTSTRING, strlen (TESTSTRING) + 1))
54 {
55 fprintf (stderr, "Error in testReadWrite: *%s* != *%s* for file %s\n", tmp,
56 TESTSTRING, ".testfile");
57 return 1;
58 }
59 GNUNET_DISK_file_copy (".testfile", ".testfile2");
60 memset (tmp, 0, sizeof(tmp));
61 ret = GNUNET_DISK_fn_read (".testfile2", tmp, sizeof(tmp) - 1);
62 if (ret < 0)
63 {
64 fprintf (stderr, "Error reading file `%s' in testReadWrite\n",
65 ".testfile2");
66 return 1;
67 }
68 tmp[ret] = '\0';
69 if (0 != memcmp (tmp, TESTSTRING, strlen (TESTSTRING) + 1))
70 {
71 fprintf (stderr, "Error in testReadWrite: *%s* != *%s* for file %s\n", tmp,
72 TESTSTRING, ".testfile2");
73 return 1;
74 }
75
76 GNUNET_break (0 == unlink (".testfile"));
77 GNUNET_break (0 == unlink (".testfile2"));
78 if (GNUNET_NO != GNUNET_DISK_file_test (".testfile"))
79 return 1;
80
81 return 0;
82}
83
84
85static int
86testOpenClose ()
87{
88 struct GNUNET_DISK_FileHandle *fh;
89 uint64_t size;
90
91 fh = GNUNET_DISK_file_open (".testfile",
92 GNUNET_DISK_OPEN_READWRITE
93 | GNUNET_DISK_OPEN_CREATE,
94 GNUNET_DISK_PERM_USER_READ
95 | GNUNET_DISK_PERM_USER_WRITE);
96 GNUNET_assert (GNUNET_NO == GNUNET_DISK_handle_invalid (fh));
97 GNUNET_break (5 == GNUNET_DISK_file_write (fh, "Hello", 5));
98 GNUNET_DISK_file_close (fh);
99 GNUNET_break (GNUNET_OK ==
100 GNUNET_DISK_file_size (".testfile", &size, GNUNET_NO,
101 GNUNET_YES));
102 if (size != 5)
103 return 1;
104 GNUNET_break (0 == unlink (".testfile"));
105
106 return 0;
107}
108
109
110static int ok;
111
112
113static int
114scan_callback (void *want, const char *filename)
115{
116 if (NULL != strstr (filename, want))
117 ok++;
118 return GNUNET_OK;
119}
120
121
122static int
123testDirScan ()
124{
125 if (GNUNET_OK !=
126 GNUNET_DISK_directory_create ("test" DIR_SEPARATOR_STR "entry"))
127 {
128 GNUNET_break (0);
129 return 1;
130 }
131 if (GNUNET_OK !=
132 GNUNET_DISK_directory_create ("test" DIR_SEPARATOR_STR "entry_more"))
133 {
134 GNUNET_break (0);
135 return 1;
136 }
137 GNUNET_DISK_directory_scan ("test", &scan_callback,
138 "test" DIR_SEPARATOR_STR "entry");
139 if (GNUNET_OK != GNUNET_DISK_directory_remove ("test"))
140 {
141 GNUNET_break (0);
142 return 1;
143 }
144 if (ok < 2)
145 {
146 GNUNET_break (0);
147 return 1;
148 }
149 return 0;
150}
151
152
153static int
154iter_callback (void *cls,
155 const char *filename)
156{
157 int *i = cls;
158
159 (*i)++;
160 return GNUNET_OK;
161}
162
163
164static int
165testDirIter ()
166{
167 int i;
168
169 i = 0;
170 if (GNUNET_OK != GNUNET_DISK_directory_create ("test/entry"))
171 {
172 GNUNET_break (0);
173 return 1;
174 }
175 if (GNUNET_OK != GNUNET_DISK_directory_create ("test/entry_many"))
176 {
177 GNUNET_break (0);
178 return 1;
179 }
180 if (GNUNET_OK != GNUNET_DISK_directory_create ("test/entry_more"))
181 {
182 GNUNET_break (0);
183 return 1;
184 }
185 GNUNET_DISK_directory_scan ("test",
186 &iter_callback,
187 &i);
188 if (GNUNET_OK != GNUNET_DISK_directory_remove ("test"))
189 {
190 GNUNET_break (0);
191 return 1;
192 }
193 if (i < 3)
194 {
195 GNUNET_break (0);
196 return 1;
197 }
198 return 0;
199}
200
201
202static int
203testCanonicalize ()
204{
205 char *fn = GNUNET_strdup ("ab?><|cd*ef:/g\"");
206
207 GNUNET_DISK_filename_canonicalize (fn);
208 if (0 != strcmp (fn, "ab____cd_ef__g_"))
209 {
210 GNUNET_free (fn);
211 return 1;
212 }
213 GNUNET_free (fn);
214 return 0;
215}
216
217
218static int
219testChangeOwner ()
220{
221 GNUNET_log_skip (1, GNUNET_NO);
222 if (GNUNET_OK == GNUNET_DISK_file_change_owner ("/dev/null", "unknownuser"))
223 return 1;
224 return 0;
225}
226
227
228static int
229testDirMani ()
230{
231 if (GNUNET_OK != GNUNET_DISK_directory_create_for_file ("test/ing"))
232 {
233 GNUNET_break (0);
234 return 1;
235 }
236 if (GNUNET_NO != GNUNET_DISK_file_test ("test"))
237 {
238 GNUNET_break (0);
239 return 1;
240 }
241 if (GNUNET_NO != GNUNET_DISK_file_test ("test/ing"))
242 {
243 GNUNET_break (0);
244 return 1;
245 }
246 if (GNUNET_OK != GNUNET_DISK_directory_remove ("test"))
247 {
248 GNUNET_break (0);
249 return 1;
250 }
251 if (GNUNET_OK != GNUNET_DISK_directory_create ("test"))
252 {
253 GNUNET_break (0);
254 return 1;
255 }
256 if (GNUNET_YES != GNUNET_DISK_directory_test ("test", GNUNET_YES))
257 {
258 GNUNET_break (0);
259 return 1;
260 }
261 if (GNUNET_OK != GNUNET_DISK_directory_remove ("test"))
262 {
263 GNUNET_break (0);
264 return 1;
265 }
266 return 0;
267}
268
269
270int
271main (int argc, char *argv[])
272{
273 unsigned int failureCount = 0;
274
275 GNUNET_log_setup ("test-disk", "WARNING", NULL);
276 failureCount += testReadWrite ();
277 failureCount += testOpenClose ();
278 failureCount += testDirScan ();
279 failureCount += testDirIter ();
280 failureCount += testCanonicalize ();
281 failureCount += testChangeOwner ();
282 failureCount += testDirMani ();
283 if (0 != failureCount)
284 {
285 fprintf (stderr,
286 "\n%u TESTS FAILED!\n",
287 failureCount);
288 return -1;
289 }
290 return 0;
291} /* end of main */
diff --git a/src/lib/util/test_getopt.c b/src/lib/util/test_getopt.c
new file mode 100644
index 000000000..cad10504d
--- /dev/null
+++ b/src/lib/util/test_getopt.c
@@ -0,0 +1,183 @@
1/*
2 This file is part of GNUnet.
3 Copyright (C) 2003, 2004, 2005, 2006, 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 util/test_getopt.c
22 * @brief testcase for util/getopt.c
23 */
24
25#include "platform.h"
26#include "gnunet_util_lib.h"
27
28
29static int
30testMinimal ()
31{
32 char *const emptyargv[] = { "test", NULL };
33 const struct GNUNET_GETOPT_CommandLineOption emptyoptionlist[] = {
34 GNUNET_GETOPT_OPTION_END
35 };
36
37 if (1 != GNUNET_GETOPT_run ("test", emptyoptionlist, 1, emptyargv))
38 return 1;
39
40 return 0;
41}
42
43
44static int
45testVerbose ()
46{
47 char *const myargv[] = { "test", "-V", "-V", "more", NULL };
48 unsigned int vflags = 0;
49
50 const struct GNUNET_GETOPT_CommandLineOption verboseoptionlist[] =
51 { GNUNET_GETOPT_option_verbose (&vflags), GNUNET_GETOPT_OPTION_END };
52
53 if (3 != GNUNET_GETOPT_run ("test", verboseoptionlist, 4, myargv))
54 {
55 GNUNET_break (0);
56 return 1;
57 }
58 if (vflags != 2)
59 {
60 GNUNET_break (0);
61 return 1;
62 }
63 return 0;
64}
65
66
67static int
68testVersion ()
69{
70 char *const myargv[] = { "test_getopt", "-v", NULL };
71 const struct GNUNET_GETOPT_CommandLineOption versionoptionlist[] =
72 { GNUNET_GETOPT_option_version (PACKAGE_VERSION " " VCS_VERSION),
73 GNUNET_GETOPT_OPTION_END };
74
75 if (0 != GNUNET_GETOPT_run ("test_getopt", versionoptionlist, 2, myargv))
76 {
77 GNUNET_break (0);
78 return 1;
79 }
80 return 0;
81}
82
83
84static int
85testAbout ()
86{
87 char *const myargv[] = { "test_getopt", "-h", NULL };
88 const struct GNUNET_GETOPT_CommandLineOption aboutoptionlist[] =
89 { GNUNET_GETOPT_option_help ("Testing"), GNUNET_GETOPT_OPTION_END };
90
91 if (0 != GNUNET_GETOPT_run ("test_getopt", aboutoptionlist, 2, myargv))
92 {
93 GNUNET_break (0);
94 return 1;
95 }
96 return 0;
97}
98
99
100static int
101testLogOpts ()
102{
103 char *const myargv[] =
104 { "test_getopt", "-l", "filename", "-L", "WARNING", NULL };
105 char *level = GNUNET_strdup ("stuff");
106 char *fn = NULL;
107
108 const struct GNUNET_GETOPT_CommandLineOption logoptionlist[] =
109 { GNUNET_GETOPT_option_logfile (&fn),
110 GNUNET_GETOPT_option_loglevel (&level),
111 GNUNET_GETOPT_OPTION_END };
112
113 if (5 != GNUNET_GETOPT_run ("test_getopt", logoptionlist, 5, myargv))
114 {
115 GNUNET_break (0);
116 return 1;
117 }
118 GNUNET_assert (NULL != fn);
119 if ((0 != strcmp (level, "WARNING")) || (NULL == strstr (fn, "/filename")))
120 {
121 GNUNET_break (0);
122 GNUNET_free (level);
123 GNUNET_free (fn);
124 return 1;
125 }
126 GNUNET_free (level);
127 GNUNET_free (fn);
128 return 0;
129}
130
131
132static int
133testFlagNum ()
134{
135 char *const myargv[] = { "test_getopt", "-f", "-n", "42", "-N", "42", NULL };
136 int flag = 0;
137 unsigned int num = 0;
138 unsigned long long lnum = 0;
139
140 const struct GNUNET_GETOPT_CommandLineOption logoptionlist[] =
141 { GNUNET_GETOPT_option_flag ('f', "--flag", "helptext", &flag),
142 GNUNET_GETOPT_option_uint ('n', "--num", "ARG", "helptext", &num),
143 GNUNET_GETOPT_option_ulong ('N', "--lnum", "ARG", "helptext", &lnum),
144 GNUNET_GETOPT_OPTION_END };
145
146 if (6 != GNUNET_GETOPT_run ("test_getopt", logoptionlist, 6, myargv))
147 {
148 GNUNET_break (0);
149 return 1;
150 }
151 if ((1 != flag) || (42 != num) || (42 != lnum))
152 {
153 GNUNET_break (0);
154 return 1;
155 }
156 return 0;
157}
158
159
160int
161main (int argc, char *argv[])
162{
163 int errCnt = 0;
164
165 GNUNET_log_setup ("test_getopt", "WARNING", NULL);
166 /* suppress output from -h, -v options */
167
168 GNUNET_break (0 == close (1));
169
170 if (0 != testMinimal ())
171 errCnt++;
172 if (0 != testVerbose ())
173 errCnt++;
174 if (0 != testVersion ())
175 errCnt++;
176 if (0 != testAbout ())
177 errCnt++;
178 if (0 != testLogOpts ())
179 errCnt++;
180 if (0 != testFlagNum ())
181 errCnt++;
182 return errCnt;
183}
diff --git a/src/lib/util/test_hexcoder.c b/src/lib/util/test_hexcoder.c
new file mode 100644
index 000000000..e04631188
--- /dev/null
+++ b/src/lib/util/test_hexcoder.c
@@ -0,0 +1,56 @@
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/**
22 * @author Christian Grothoff
23 * @file dns/test_hexcoder.c
24 * @brief test for #GNUNET_DNSPARSER_hex_to_bin() and
25 * #GNUNET_DNSPARSER_bin_to_hex()
26 */
27
28#include "platform.h"
29#include "gnunet_util_lib.h"
30
31#define TESTSTRING "Hello World!"
32
33
34int
35main (int argc,
36 char *argv[])
37{
38 char buf[strlen (TESTSTRING) + 1];
39 char *ret;
40
41 GNUNET_log_setup ("test-hexcoder", "WARNING", NULL);
42 ret = GNUNET_DNSPARSER_bin_to_hex (TESTSTRING,
43 strlen (TESTSTRING) + 1);
44 GNUNET_assert (NULL != ret);
45 GNUNET_assert (sizeof(buf) ==
46 GNUNET_DNSPARSER_hex_to_bin (ret,
47 buf));
48 GNUNET_assert (0 == memcmp (TESTSTRING,
49 buf,
50 sizeof(buf)));
51 GNUNET_free (ret);
52 return 0;
53}
54
55
56/* end of test_hexcoder.c */
diff --git a/src/lib/util/test_mq.c b/src/lib/util/test_mq.c
new file mode 100644
index 000000000..522589daf
--- /dev/null
+++ b/src/lib/util/test_mq.c
@@ -0,0 +1,342 @@
1/*
2 This file is part of GNUnet.
3 Copyright (C) 2012, 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/**
22 * @file util/test_mq.c
23 * @brief tests for mq
24 * @author Florian Dold
25 * @author Christian Grothoff
26 */
27
28#include "platform.h"
29#include "gnunet_util_lib.h"
30
31#define NUM_TRANSMISSIONS 500
32
33/**
34 * How long does the receiver take per message?
35 */
36#define RECEIVER_THROTTLE GNUNET_TIME_relative_multiply ( \
37 GNUNET_TIME_UNIT_MILLISECONDS, 1)
38
39static unsigned int received_cnt;
40
41
42GNUNET_NETWORK_STRUCT_BEGIN
43
44struct MyMessage
45{
46 struct GNUNET_MessageHeader header;
47 uint32_t x GNUNET_PACKED;
48};
49
50GNUNET_NETWORK_STRUCT_END
51
52static int global_ret;
53
54static struct GNUNET_SCHEDULER_Task *tt;
55
56static struct GNUNET_SCHEDULER_Task *dt;
57
58static struct GNUNET_MQ_Handle *cmq;
59
60
61static void
62do_shutdown (void *cls)
63{
64 (void) cls;
65 if (NULL != tt)
66 {
67 GNUNET_SCHEDULER_cancel (tt);
68 tt = NULL;
69 }
70 if (NULL != cmq)
71 {
72 GNUNET_MQ_destroy (cmq);
73 cmq = NULL;
74 }
75}
76
77
78static void
79do_timeout (void *cls)
80{
81 (void) cls;
82 tt = NULL;
83 GNUNET_SCHEDULER_shutdown ();
84 global_ret = 1;
85}
86
87
88/**
89 * Generic error handler, called with the appropriate
90 * error code and the same closure specified at the creation of
91 * the message queue.
92 * Not every message queue implementation supports an error handler.
93 *
94 * @param cls closure
95 * @param error error code
96 */
97static void
98error_cb (void *cls,
99 enum GNUNET_MQ_Error error)
100{
101 GNUNET_break (0);
102 global_ret = 3;
103 GNUNET_SCHEDULER_shutdown ();
104}
105
106
107static void
108client_continue (void *cls)
109{
110 struct GNUNET_SERVICE_Client *c = cls;
111
112 dt = NULL;
113 GNUNET_SERVICE_client_continue (c);
114}
115
116
117static void
118handle_dummy (void *cls,
119 const struct MyMessage *msg)
120{
121 struct GNUNET_SERVICE_Client *c = cls;
122
123 GNUNET_assert (NULL == dt);
124 /* artificially make receiver slower than sender */
125 dt = GNUNET_SCHEDULER_add_delayed (RECEIVER_THROTTLE,
126 &client_continue,
127 c);
128 if (received_cnt != ntohl (msg->x))
129 {
130 GNUNET_break (0);
131 global_ret = 4;
132 GNUNET_SCHEDULER_shutdown ();
133 }
134 received_cnt++;
135}
136
137
138static void
139handle_dummy2 (void *cls,
140 const struct MyMessage *msg)
141{
142 struct GNUNET_SERVICE_Client *c = cls;
143
144 GNUNET_SERVICE_client_continue (c);
145 if (NUM_TRANSMISSIONS != received_cnt)
146 {
147 GNUNET_break (0);
148 global_ret = 5;
149 }
150 GNUNET_SCHEDULER_shutdown ();
151}
152
153
154/**
155 * Function called whenever MQ has sent a message.
156 */
157static void
158notify_sent_cb (void *cls)
159{
160 static unsigned int seen;
161 unsigned int *cnt = cls;
162
163 if (seen != *cnt)
164 {
165 GNUNET_break (0);
166 global_ret = 6;
167 GNUNET_SCHEDULER_shutdown ();
168 }
169 seen++;
170 GNUNET_free (cnt);
171}
172
173
174/**
175 * Start running the actual test.
176 *
177 * @param cls closure passed to #GNUNET_SERVICE_MAIN
178 * @param cfg configuration to use for this service
179 * @param sh handle to the newly create service
180 */
181static void
182run (void *cls,
183 const struct GNUNET_CONFIGURATION_Handle *cfg,
184 struct GNUNET_SERVICE_Handle *sh)
185{
186 struct GNUNET_MQ_MessageHandler ch[] = {
187 GNUNET_MQ_handler_end ()
188 };
189 struct GNUNET_MQ_Envelope *env;
190 struct MyMessage *m;
191
192 (void) cls;
193 (void) sh;
194 cmq = GNUNET_CLIENT_connect (cfg,
195 "test_client",
196 ch,
197 &error_cb,
198 NULL);
199 GNUNET_SCHEDULER_add_shutdown (&do_shutdown,
200 NULL);
201 tt = GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_UNIT_MINUTES,
202 &do_timeout,
203 NULL);
204 for (unsigned int i = 0; i < NUM_TRANSMISSIONS; i++)
205 {
206 unsigned int *cnt;
207
208 cnt = GNUNET_new (unsigned int);
209 *cnt = i;
210 env = GNUNET_MQ_msg (m,
211 GNUNET_MESSAGE_TYPE_DUMMY);
212 GNUNET_MQ_notify_sent (env,
213 &notify_sent_cb,
214 cnt);
215 m->x = htonl (i);
216 GNUNET_MQ_send (cmq,
217 env);
218 }
219 env = GNUNET_MQ_msg (m,
220 GNUNET_MESSAGE_TYPE_DUMMY2);
221 GNUNET_MQ_send (cmq,
222 env);
223}
224
225
226/**
227 * Callback to be called when a client connects to the service.
228 *
229 * @param cls closure for the service
230 * @param c the new client that connected to the service
231 * @param mq the message queue used to send messages to the client
232 * @return the client-specific (`internal') closure
233 */
234static void *
235connect_cb (void *cls,
236 struct GNUNET_SERVICE_Client *c,
237 struct GNUNET_MQ_Handle *mq)
238{
239 (void) cls;
240 (void) mq;
241 return c;
242}
243
244
245/**
246 * Callback to be called when a client disconnected from the service
247 *
248 * @param cls closure for the service
249 * @param c the client that disconnected
250 * @param internal_cls the client-specific (`internal') closure
251 */
252static void
253disconnect_cb (void *cls,
254 struct GNUNET_SERVICE_Client *c,
255 void *internal_cls)
256{
257 (void) cls;
258 (void) c;
259 (void) internal_cls;
260}
261
262
263static void
264test1 ()
265{
266 struct GNUNET_MQ_Envelope *mqm;
267 struct MyMessage *mm;
268
269 mm = NULL;
270 mqm = NULL;
271
272 mqm = GNUNET_MQ_msg (mm,
273 GNUNET_MESSAGE_TYPE_DUMMY);
274 GNUNET_assert (NULL != mqm);
275 GNUNET_assert (NULL != mm);
276 GNUNET_assert (GNUNET_MESSAGE_TYPE_DUMMY == ntohs (mm->header.type));
277 GNUNET_assert (sizeof(struct MyMessage) == ntohs (mm->header.size));
278 GNUNET_MQ_discard (mqm);
279}
280
281
282static void
283test2 ()
284{
285 struct GNUNET_MQ_Envelope *mqm;
286 struct GNUNET_MessageHeader *mh;
287
288 mqm = GNUNET_MQ_msg_header (GNUNET_MESSAGE_TYPE_DUMMY);
289 /* how could the above be checked? */
290
291 GNUNET_MQ_discard (mqm);
292
293 mqm = GNUNET_MQ_msg_header_extra (mh,
294 20,
295 GNUNET_MESSAGE_TYPE_DUMMY);
296 GNUNET_assert (GNUNET_MESSAGE_TYPE_DUMMY == ntohs (mh->type));
297 GNUNET_assert (sizeof(struct GNUNET_MessageHeader) + 20 == ntohs (mh->size));
298 GNUNET_MQ_discard (mqm);
299}
300
301
302int
303main (int argc, char **argv)
304{
305 char *test_argv[] = {
306 (char *) "test_client",
307 "-c",
308 "test_client_data.conf",
309 NULL
310 };
311 struct GNUNET_MQ_MessageHandler mh[] = {
312 GNUNET_MQ_hd_fixed_size (dummy,
313 GNUNET_MESSAGE_TYPE_DUMMY,
314 struct MyMessage,
315 NULL),
316 GNUNET_MQ_hd_fixed_size (dummy2,
317 GNUNET_MESSAGE_TYPE_DUMMY2,
318 struct MyMessage,
319 NULL),
320 GNUNET_MQ_handler_end ()
321 };
322
323 (void) argc;
324 (void) argv;
325 GNUNET_log_setup ("test-mq",
326 "INFO",
327 NULL);
328 test1 ();
329 test2 ();
330 if (0 !=
331 GNUNET_SERVICE_run_ (3,
332 test_argv,
333 "test_client",
334 GNUNET_SERVICE_OPTION_NONE,
335 &run,
336 &connect_cb,
337 &disconnect_cb,
338 NULL,
339 mh))
340 return 1;
341 return global_ret;
342}
diff --git a/src/lib/util/test_os_network.c b/src/lib/util/test_os_network.c
new file mode 100644
index 000000000..3bf4e1ba0
--- /dev/null
+++ b/src/lib/util/test_os_network.c
@@ -0,0 +1,93 @@
1/*
2 This file is part of GNUnet.
3 Copyright (C) 2003, 2004, 2005, 2006, 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 util/test_os_network.c
22 * @brief testcase for util/os_network.c
23 */
24
25#include "platform.h"
26#include "gnunet_util_lib.h"
27
28
29/**
30 * Check if the address we got is IPv4 or IPv6 loopback (which should
31 * be present on all systems at all times); if so, set ok to 0
32 * (success).
33 */
34static int
35proc (void *cls,
36 const char *name,
37 int isDefault,
38 const struct sockaddr *addr,
39 const struct sockaddr *broadcast_addr,
40 const struct sockaddr *netmask,
41 socklen_t addrlen)
42{
43 int *ok = cls;
44 char buf[INET6_ADDRSTRLEN];
45 const char *protocol;
46
47 GNUNET_log (GNUNET_ERROR_TYPE_MESSAGE, "hiho\n");
48 if (NULL == addr)
49 return GNUNET_OK;
50 if (addrlen == sizeof(struct sockaddr_in))
51 protocol = "IPv4";
52 else
53 protocol = "IPv6";
54 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
55 "%s Address `%s'\n",
56 protocol,
57 GNUNET_a2s ((const struct sockaddr *) addr,
58 addrlen));
59 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
60 "Netmask `%s'\n",
61 GNUNET_a2s ((const struct sockaddr *) netmask,
62 addrlen));
63 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
64 "`%s'\n",
65 GNUNET_a2s ((const struct sockaddr *) broadcast_addr,
66 addrlen));
67 inet_ntop (addr->sa_family,
68 (addr->sa_family ==
69 AF_INET) ? (void *) &((struct sockaddr_in *) addr)->sin_addr
70 : (void *) &((struct sockaddr_in6 *) addr)->sin6_addr, buf,
71 sizeof(buf));
72 if ((0 == strcmp ("::1", buf)) || (0 == strcmp ("127.0.0.1", buf)))
73 *ok = 0;
74 return GNUNET_OK;
75}
76
77
78int
79main (int argc, char *argv[])
80{
81 int ret;
82
83 GNUNET_log_setup ("test-os-network",
84 "WARNING",
85 NULL);
86 ret = 1;
87 GNUNET_OS_network_interfaces_list (&proc,
88 &ret);
89 return ret;
90}
91
92
93/* end of test_os_network.c */
diff --git a/src/lib/util/test_os_start_process.c b/src/lib/util/test_os_start_process.c
new file mode 100644
index 000000000..cdb1acf03
--- /dev/null
+++ b/src/lib/util/test_os_start_process.c
@@ -0,0 +1,290 @@
1/*
2 This file is part of GNUnet.
3 Copyright (C) 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 util/test_os_start_process.c
22 * @brief testcase for os start process code
23 *
24 * This testcase simply calls the os start process code
25 * giving a file descriptor to write stdout to. If the
26 * correct data "HELLO" is read then all is well.
27 */
28
29#include "platform.h"
30#include "gnunet_util_lib.h"
31#include "disk.h"
32
33
34static const char *test_phrase = "HELLO WORLD";
35
36static int ok;
37
38static struct GNUNET_OS_Process *proc;
39
40/**
41 * Pipe to write to started processes stdin (on write end)
42 */
43static struct GNUNET_DISK_PipeHandle *hello_pipe_stdin;
44
45/**
46 * Pipe to read from started processes stdout (on read end)
47 */
48static struct GNUNET_DISK_PipeHandle *hello_pipe_stdout;
49
50static struct GNUNET_SCHEDULER_Task *die_task;
51
52struct read_context
53{
54 char buf[16];
55 int buf_offset;
56 const struct GNUNET_DISK_FileHandle *stdout_read_handle;
57};
58
59
60static struct read_context rc;
61
62
63static void
64end_task (void *cls)
65{
66 if (0 != GNUNET_OS_process_kill (proc, GNUNET_TERM_SIG))
67 {
68 GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING, "kill");
69 }
70 GNUNET_assert (GNUNET_OK == GNUNET_OS_process_wait (proc));
71 GNUNET_OS_process_destroy (proc);
72 proc = NULL;
73 GNUNET_DISK_pipe_close (hello_pipe_stdout);
74 GNUNET_DISK_pipe_close (hello_pipe_stdin);
75}
76
77
78static void
79read_call (void *cls)
80{
81 int bytes;
82
83 bytes = GNUNET_DISK_file_read (rc.stdout_read_handle,
84 &rc.buf[rc.buf_offset],
85 sizeof(rc.buf) - rc.buf_offset);
86 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
87 "bytes is %d\n",
88 bytes);
89
90 if (bytes < 1)
91 {
92 GNUNET_break (0);
93 ok = 1;
94 GNUNET_SCHEDULER_cancel (die_task);
95 (void) GNUNET_SCHEDULER_add_now (&end_task, NULL);
96 return;
97 }
98
99 ok = strncmp (rc.buf, test_phrase, strlen (test_phrase));
100 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
101 "read %s\n",
102 &rc.buf[rc.buf_offset]);
103 rc.buf_offset += bytes;
104
105 if (0 == ok)
106 {
107 GNUNET_SCHEDULER_cancel (die_task);
108 (void) GNUNET_SCHEDULER_add_now (&end_task, NULL);
109 return;
110 }
111
112 GNUNET_SCHEDULER_add_read_file (GNUNET_TIME_UNIT_FOREVER_REL,
113 rc.stdout_read_handle,
114 &read_call,
115 NULL);
116}
117
118
119static void
120run_task (void *cls)
121{
122 const struct GNUNET_DISK_FileHandle *stdout_read_handle;
123 const struct GNUNET_DISK_FileHandle *wh;
124
125 hello_pipe_stdin = GNUNET_DISK_pipe (GNUNET_DISK_PF_BLOCKING_RW);
126 hello_pipe_stdout = GNUNET_DISK_pipe (GNUNET_DISK_PF_BLOCKING_RW);
127 if ((hello_pipe_stdout == NULL) || (hello_pipe_stdin == NULL))
128 {
129 GNUNET_break (0);
130 ok = 1;
131 return;
132 }
133
134 proc =
135 GNUNET_OS_start_process (GNUNET_OS_INHERIT_STD_ERR,
136 hello_pipe_stdin, hello_pipe_stdout, NULL,
137 "cat",
138 "cat", "-", NULL);
139 /* Close the write end of the read pipe */
140 GNUNET_DISK_pipe_close_end (hello_pipe_stdout, GNUNET_DISK_PIPE_END_WRITE);
141 /* Close the read end of the write pipe */
142 GNUNET_DISK_pipe_close_end (hello_pipe_stdin, GNUNET_DISK_PIPE_END_READ);
143
144 wh = GNUNET_DISK_pipe_handle (hello_pipe_stdin, GNUNET_DISK_PIPE_END_WRITE);
145
146 /* Write the test_phrase to the cat process */
147 if (GNUNET_DISK_file_write (wh, test_phrase, strlen (test_phrase) + 1) !=
148 strlen (test_phrase) + 1)
149 {
150 GNUNET_break (0);
151 ok = 1;
152 return;
153 }
154
155 /* Close the write end to end the cycle! */
156 GNUNET_DISK_pipe_close_end (hello_pipe_stdin, GNUNET_DISK_PIPE_END_WRITE);
157
158 stdout_read_handle =
159 GNUNET_DISK_pipe_handle (hello_pipe_stdout, GNUNET_DISK_PIPE_END_READ);
160
161 die_task =
162 GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_relative_multiply
163 (GNUNET_TIME_UNIT_MINUTES, 1),
164 &end_task,
165 NULL);
166
167 memset (&rc, 0, sizeof(rc));
168 rc.stdout_read_handle = stdout_read_handle;
169 GNUNET_SCHEDULER_add_read_file (GNUNET_TIME_UNIT_FOREVER_REL,
170 stdout_read_handle,
171 &read_call,
172 NULL);
173}
174
175
176/**
177 * Main method, starts scheduler with task1,
178 * checks that "ok" is correct at the end.
179 */
180static int
181check_run ()
182{
183 ok = 1;
184 GNUNET_SCHEDULER_run (&run_task, &ok);
185 return ok;
186}
187
188
189/**
190 * Test killing via pipe.
191 */
192static int
193check_kill ()
194{
195 char *fn;
196
197 hello_pipe_stdin = GNUNET_DISK_pipe (GNUNET_DISK_PF_BLOCKING_RW);
198 hello_pipe_stdout = GNUNET_DISK_pipe (GNUNET_DISK_PF_BLOCKING_RW);
199 if ((hello_pipe_stdout == NULL) || (hello_pipe_stdin == NULL))
200 {
201 return 1;
202 }
203 fn = GNUNET_OS_get_libexec_binary_path ("gnunet-service-resolver");
204 proc =
205 GNUNET_OS_start_process (GNUNET_OS_INHERIT_STD_ERR
206 | GNUNET_OS_USE_PIPE_CONTROL,
207 hello_pipe_stdin,
208 hello_pipe_stdout,
209 NULL,
210 fn,
211 "gnunet-service-resolver", "-",
212 NULL);
213 if (NULL == proc)
214 {
215 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
216 "Failed to launch gnunet-service-resolver. Is your system setup correct?\n");
217 return 77;
218 }
219 sleep (1); /* give process time to start, so we actually use the pipe-kill mechanism! */
220 GNUNET_free (fn);
221 if (0 != GNUNET_OS_process_kill (proc, GNUNET_TERM_SIG))
222 GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING, "kill");
223 GNUNET_assert (GNUNET_OK == GNUNET_OS_process_wait (proc));
224 GNUNET_OS_process_destroy (proc);
225 proc = NULL;
226 GNUNET_DISK_pipe_close (hello_pipe_stdout);
227 GNUNET_DISK_pipe_close (hello_pipe_stdin);
228 return 0;
229}
230
231
232/**
233 * Test killing via pipe.
234 */
235static int
236check_instant_kill ()
237{
238 char *fn;
239
240 hello_pipe_stdin = GNUNET_DISK_pipe (GNUNET_DISK_PF_BLOCKING_RW);
241 hello_pipe_stdout = GNUNET_DISK_pipe (GNUNET_DISK_PF_BLOCKING_RW);
242 if ((hello_pipe_stdout == NULL) || (hello_pipe_stdin == NULL))
243 {
244 return 1;
245 }
246 fn = GNUNET_OS_get_libexec_binary_path ("gnunet-service-resolver");
247 proc =
248 GNUNET_OS_start_process (GNUNET_OS_INHERIT_STD_ERR
249 | GNUNET_OS_USE_PIPE_CONTROL,
250 hello_pipe_stdin, hello_pipe_stdout, NULL,
251 fn,
252 "gnunet-service-resolver", "-", NULL);
253 if (NULL == proc)
254 {
255 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
256 "Failed to launch gnunet-service-resolver. Is your system setup correct?\n");
257 return 77;
258 }
259 if (0 != GNUNET_OS_process_kill (proc,
260 GNUNET_TERM_SIG))
261 {
262 GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING, "kill");
263 }
264 GNUNET_free (fn);
265 GNUNET_assert (GNUNET_OK == GNUNET_OS_process_wait (proc));
266 GNUNET_OS_process_destroy (proc);
267 proc = NULL;
268 GNUNET_DISK_pipe_close (hello_pipe_stdout);
269 GNUNET_DISK_pipe_close (hello_pipe_stdin);
270 return 0;
271}
272
273
274int
275main (int argc, char *argv[])
276{
277 int ret;
278
279 GNUNET_log_setup ("test-os-start-process",
280 "WARNING",
281 NULL);
282 ret = 0;
283 ret |= check_run ();
284 ret |= check_kill ();
285 ret |= check_instant_kill ();
286 return ret;
287}
288
289
290/* end of test_os_start_process.c */
diff --git a/src/lib/util/test_peer.c b/src/lib/util/test_peer.c
new file mode 100644
index 000000000..ad4e6aac9
--- /dev/null
+++ b/src/lib/util/test_peer.c
@@ -0,0 +1,140 @@
1/*
2 This file is part of GNUnet.
3 Copyright (C) 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 util/test_peer.c
22 * @brief testcase for peer.c
23 * @author Safey Mohammed
24 */
25
26
27#include "platform.h"
28#include "gnunet_util_lib.h"
29#include <gcrypt.h>
30
31#define NUMBER_OF_PEERS 10
32
33/**
34 * A list of Peer ID's to play with
35 */
36static struct GNUNET_PeerIdentity pidArr[NUMBER_OF_PEERS];
37
38
39static void
40generatePeerIdList ()
41{
42 for (unsigned int i = 0; i < NUMBER_OF_PEERS; i++)
43 {
44 gcry_randomize (&pidArr[i],
45 sizeof(struct GNUNET_PeerIdentity),
46 GCRY_STRONG_RANDOM);
47 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
48 "Peer %u: %s\n",
49 i,
50 GNUNET_i2s (&pidArr[i]));
51 }
52}
53
54
55static int
56check ()
57{
58 GNUNET_PEER_Id pid;
59 struct GNUNET_PeerIdentity res;
60 GNUNET_PEER_Id ids[] = { 1, 2, 3 };
61
62 GNUNET_assert (0 == GNUNET_PEER_intern (NULL));
63 /* Insert Peers into PeerEntry table and hashmap */
64 for (unsigned int i = 0; i < NUMBER_OF_PEERS; i++)
65 {
66 pid = GNUNET_PEER_intern (&pidArr[i]);
67 if (pid != (i + 1))
68 {
69 fprintf (stderr, "%s",
70 "Unexpected Peer ID returned by intern function\n");
71 return 1;
72 }
73 }
74
75 /* Referencing the first 3 peers once again */
76 for (unsigned int i = 0; i < 3; i++)
77 {
78 pid = GNUNET_PEER_intern (&pidArr[i]);
79 if (pid != (i + 1))
80 {
81 fprintf (stderr, "%s",
82 "Unexpected Peer ID returned by intern function\n");
83 return 1;
84 }
85 }
86
87 /* Dereferencing the first 3 peers once [decrementing their reference count] */
88 GNUNET_PEER_decrement_rcs (ids, 3);
89
90 /* re-referencing the first 3 peers using the change_rc function */
91 for (unsigned int i = 1; i <= 3; i++)
92 GNUNET_PEER_change_rc (i, 1);
93
94 /* Removing the second Peer from the PeerEntry hash map */
95 GNUNET_PEER_change_rc (2, -2);
96
97 /* convert the pid of the first PeerEntry into that of the third */
98 GNUNET_PEER_resolve (1,
99 &res);
100 GNUNET_assert (0 ==
101 GNUNET_memcmp (&res,
102 &pidArr[0]));
103
104 /*
105 * Attempt to convert pid = 0 (which is reserved)
106 * into a peer identity object, the peer identity memory
107 * is expected to be set to zero
108 */GNUNET_log_skip (1, GNUNET_YES);
109 GNUNET_PEER_resolve (0, &res);
110 GNUNET_assert (GNUNET_YES == GNUNET_is_zero (&res));
111
112 /* Removing peer entries 1 and 3 from table using the list decrement function */
113 /* If count = 0, nothing should be done whatsoever */
114 GNUNET_PEER_decrement_rcs (ids, 0);
115
116 ids[1] = 3;
117 GNUNET_PEER_decrement_rcs (ids, 2);
118 GNUNET_PEER_decrement_rcs (ids, 2);
119
120 return 0;
121}
122
123
124int
125main ()
126{
127 GNUNET_log_setup ("test-peer",
128 "ERROR",
129 NULL);
130 for (unsigned int i = 0; i < 1; i++)
131 {
132 generatePeerIdList ();
133 if (0 != check ())
134 return 1;
135 }
136 return 0;
137}
138
139
140/* end of test_peer.c */
diff --git a/src/lib/util/test_plugin.c b/src/lib/util/test_plugin.c
new file mode 100644
index 000000000..0831f3068
--- /dev/null
+++ b/src/lib/util/test_plugin.c
@@ -0,0 +1,88 @@
1/*
2 This file is part of GNUnet.
3 Copyright (C) 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 util/test_plugin.c
22 * @brief testcase for plugin.c
23 */
24
25#include "platform.h"
26#include "gnunet_util_lib.h"
27
28
29static void
30test_cb (void *cls,
31 const char *libname,
32 void *lib_ret)
33{
34 const char *test_cls = cls;
35 char *ret;
36
37 GNUNET_assert (0 == strcmp (test_cls,
38 "test-closure"));
39 GNUNET_assert (0 == strcmp (lib_ret,
40 "Hello"));
41 ret = GNUNET_PLUGIN_unload (libname,
42 "out");
43 GNUNET_assert (NULL != ret);
44 GNUNET_assert (0 == strcmp (ret,
45 "World"));
46 free (ret);
47}
48
49
50int
51main (int argc, char *argv[])
52{
53 void *ret;
54
55 GNUNET_log_setup ("test-plugin",
56 "WARNING",
57 NULL);
58 GNUNET_log_skip (1,
59 GNUNET_NO);
60 ret = GNUNET_PLUGIN_load ("libgnunet_plugin_missing",
61 NULL);
62 GNUNET_log_skip (0, GNUNET_NO);
63 if (NULL != ret)
64 return 1;
65 ret = GNUNET_PLUGIN_load ("libgnunet_plugin_utiltest",
66 "in");
67 if (NULL == ret)
68 return 1;
69 if (0 != strcmp (ret,
70 "Hello"))
71 return 2;
72 ret = GNUNET_PLUGIN_unload ("libgnunet_plugin_utiltest",
73 "out");
74 if (NULL == ret)
75 return 3;
76 if (0 != strcmp (ret,
77 "World"))
78 return 4;
79 free (ret);
80 GNUNET_PLUGIN_load_all ("libgnunet_plugin_utiltes",
81 "in",
82 &test_cb,
83 "test-closure");
84 return 0;
85}
86
87
88/* end of test_plugin.c */
diff --git a/src/lib/util/test_plugin_plug.c b/src/lib/util/test_plugin_plug.c
new file mode 100644
index 000000000..ed582517b
--- /dev/null
+++ b/src/lib/util/test_plugin_plug.c
@@ -0,0 +1,46 @@
1/*
2 This file is part of GNUnet.
3 Copyright (C) 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 util/test_plugin_plug.c
22 * @brief plugin for testing
23 */
24
25#include "platform.h"
26
27
28void *
29libgnunet_plugin_utiltest_init (void *arg)
30{
31 if (0 == strcmp (arg, "in"))
32 return "Hello";
33 return NULL;
34}
35
36
37void *
38libgnunet_plugin_utiltest_done (void *arg)
39{
40 if (0 == strcmp (arg, "out"))
41 return strdup ("World");
42 return NULL;
43}
44
45
46/* end of test_plugin_plug.c */
diff --git a/src/lib/util/test_program.c b/src/lib/util/test_program.c
new file mode 100644
index 000000000..3d63b0336
--- /dev/null
+++ b/src/lib/util/test_program.c
@@ -0,0 +1,139 @@
1/*
2 This file is part of GNUnet.
3 Copyright (C) 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 util/test_program.c
22 * @brief tests for program.c
23 */
24
25#include "platform.h"
26#include "gnunet_util_lib.h"
27
28
29static int setme1;
30
31static int setme2;
32
33
34/**
35 * Main function that will be run.
36 */
37static void
38runner (void *cls,
39 char *const *args,
40 const char *cfgfile,
41 const struct GNUNET_CONFIGURATION_Handle *cfg)
42{
43 int *ok = cls;
44
45 GNUNET_assert (setme1 == 1);
46 GNUNET_assert (0 == strcmp (args[0], "extra"));
47 GNUNET_assert (args[1] == NULL);
48 GNUNET_assert (NULL != strstr (cfgfile, "/test_program_data.conf"));
49 *ok = 0;
50}
51
52
53int
54main (int argc, char *argv[])
55{
56 int ok = 1;
57 char *const argvx[] = {
58 "test_program",
59 "-c",
60 "test_program_data.conf",
61 "-L",
62 "WARNING",
63 "-n",
64 "extra",
65 NULL
66 };
67 struct GNUNET_GETOPT_CommandLineOption options1[] = {
68 GNUNET_GETOPT_option_flag ('n',
69 "name",
70 "description",
71 &setme1),
72 GNUNET_GETOPT_OPTION_END
73 };
74 struct GNUNET_GETOPT_CommandLineOption options2[] = {
75 GNUNET_GETOPT_option_flag ('n',
76 "name",
77 "description",
78 &setme1),
79 GNUNET_GETOPT_option_flag ('N',
80 "number",
81 "description",
82 &setme2),
83 GNUNET_GETOPT_OPTION_END
84 };
85 struct GNUNET_GETOPT_CommandLineOption options3[] = {
86 GNUNET_GETOPT_option_flag ('N',
87 "number",
88 "description",
89 &setme1),
90 GNUNET_GETOPT_option_flag ('n',
91 "name",
92 "description",
93 &setme2),
94 GNUNET_GETOPT_OPTION_END
95 };
96 struct GNUNET_GETOPT_CommandLineOption options4[] = {
97 GNUNET_GETOPT_option_flag ('n',
98 "name",
99 "description",
100 &setme1),
101 GNUNET_GETOPT_option_flag ('n',
102 "name",
103 "description",
104 &setme2),
105 GNUNET_GETOPT_OPTION_END
106 };
107
108
109 GNUNET_log_setup ("test_program",
110 "WARNING",
111 NULL);
112 GNUNET_assert (GNUNET_OK ==
113 GNUNET_PROGRAM_run (7, argvx,
114 "test_program",
115 "A test",
116 options1,
117 &runner, &ok));
118
119 GNUNET_assert (GNUNET_OK ==
120 GNUNET_PROGRAM_run (7, argvx,
121 "test_program", "A test",
122 options2,
123 &runner, &ok));
124 GNUNET_assert (GNUNET_OK ==
125 GNUNET_PROGRAM_run (7, argvx,
126 "test_program", "A test",
127 options3,
128 &runner, &ok));
129 GNUNET_assert (GNUNET_OK ==
130 GNUNET_PROGRAM_run (7, argvx,
131 "test_program", "A test",
132 options4,
133 &runner, &ok));
134
135 return ok;
136}
137
138
139/* end of test_program.c */
diff --git a/src/lib/util/test_program_data.conf b/src/lib/util/test_program_data.conf
new file mode 100644
index 000000000..fbf745644
--- /dev/null
+++ b/src/lib/util/test_program_data.conf
@@ -0,0 +1,2 @@
1[resolver]
2HOSTNAME = localhost
diff --git a/src/lib/util/test_regex.c b/src/lib/util/test_regex.c
new file mode 100644
index 000000000..789418fa0
--- /dev/null
+++ b/src/lib/util/test_regex.c
@@ -0,0 +1,194 @@
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 tun/test_regex.c
22 * @brief simple test for regex.c iptoregex functions
23 * @author Maximilian Szengel
24 */
25
26#include "platform.h"
27#include "gnunet_util_lib.h"
28
29/**
30 * 'wildcard', matches all possible values (for HEX encoding).
31 */
32#define DOT "(0|1|2|3|4|5|6|7|8|9|A|B|C|D|E|F)"
33
34
35static int
36test_iptoregex (const char *ipv4,
37 uint16_t port,
38 const char *expectedv4,
39 const char *ipv6,
40 uint16_t port6,
41 const char *expectedv6)
42{
43 int error = 0;
44
45 struct in_addr a;
46 struct in6_addr b;
47 char rxv4[GNUNET_TUN_IPV4_REGEXLEN];
48 char rxv6[GNUNET_TUN_IPV6_REGEXLEN];
49
50 GNUNET_assert (1 == inet_pton (AF_INET, ipv4, &a));
51 GNUNET_TUN_ipv4toregexsearch (&a, port, rxv4);
52
53 if (0 != strcmp (rxv4, expectedv4))
54 {
55 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
56 "Expected: %s but got: %s\n",
57 expectedv4,
58 rxv4);
59 error++;
60 }
61
62 GNUNET_assert (1 == inet_pton (AF_INET6, ipv6, &b));
63 GNUNET_TUN_ipv6toregexsearch (&b, port6, rxv6);
64 if (0 != strcmp (rxv6, expectedv6))
65 {
66 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
67 "Expected: %s but got: %s\n",
68 expectedv6, rxv6);
69 error++;
70 }
71 return error;
72}
73
74
75static int
76test_policy4toregex (const char *policy,
77 const char *regex)
78{
79 char *r;
80 int ret;
81
82 ret = 0;
83 r = GNUNET_TUN_ipv4policy2regex (policy);
84 if (NULL == r)
85 {
86 GNUNET_break (0);
87 return 1;
88 }
89 if (0 != strcmp (regex, r))
90 {
91 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
92 "Expected: `%s' but got: `%s'\n",
93 regex, r);
94 ret = 2;
95 }
96 GNUNET_free (r);
97 return ret;
98}
99
100
101static int
102test_policy6toregex (const char *policy,
103 const char *regex)
104{
105 char *r;
106 int ret;
107
108 ret = 0;
109 r = GNUNET_TUN_ipv6policy2regex (policy);
110 if (NULL == r)
111 {
112 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
113 "Failed to parse `%s'\n",
114 policy);
115 GNUNET_break (0);
116 return 1;
117 }
118 if (0 != strcmp (regex,
119 r))
120 {
121 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
122 "Expected: `%s' but got: `%s'\n",
123 regex, r);
124 ret = 2;
125 }
126 GNUNET_free (r);
127 return ret;
128}
129
130
131int
132main (int argc, char *argv[])
133{
134 int error;
135 char *r;
136
137 GNUNET_log_setup ("test-regex", "WARNING", NULL);
138 error = 0;
139
140 /* this is just a performance test ... */
141 r = GNUNET_TUN_ipv4policy2regex ("1.2.3.4/16:!25;");
142 GNUNET_break (NULL != r);
143 GNUNET_free (r);
144
145 error +=
146 test_iptoregex ("192.1.2.3", 2086,
147 "4-0826-C0010203",
148 "FFFF::1", 8080,
149 "6-1F90-FFFF0000000000000000000000000001");
150 error +=
151 test_iptoregex ("187.238.255.0", 80,
152 "4-0050-BBEEFF00",
153 "E1E1:73F9:51BE::0", 49,
154 "6-0031-E1E173F951BE00000000000000000000");
155 error +=
156 test_policy4toregex ("192.1.2.0/24:80;",
157 "4-0050-C00102" DOT DOT);
158 error +=
159 test_policy4toregex ("192.1.0.0/16;",
160 "4-" DOT DOT DOT DOT "-C001" DOT DOT DOT DOT);
161 error +=
162 test_policy4toregex ("192.1.0.0/16:80-81;",
163 "4-(0050|0051)-C001" DOT DOT DOT DOT);
164 error +=
165 test_policy4toregex ("192.1.0.0/8:!3-65535;",
166 "4-000(0|1|2)-C0" DOT DOT DOT DOT DOT DOT);
167 error +=
168 test_policy4toregex ("192.1.0.0/8:!25-56;",
169 "4-(0(0(0"DOT
170 "|1(0|1|2|3|4|5|6|7|8)|3(9|A|B|C|D|E|F)|(4|5|6|7|8|9|A|B|C|D|E|F)"DOT
171 ")|(1|2|3|4|5|6|7|8|9|A|B|C|D|E|F)"DOT DOT
172 ")|(1|2|3|4|5|6|7|8|9|A|B|C|D|E|F)"DOT DOT
173 DOT ")-C0"DOT DOT DOT DOT DOT DOT);
174 error +=
175 test_policy6toregex ("E1E1::1;",
176 "6-"DOT DOT DOT
177 DOT "-E1E10000000000000000000000000001");
178 error +=
179 test_policy6toregex ("E1E1:ABCD::1/120;",
180 "6-"DOT DOT DOT
181 DOT "-E1E1ABCD0000000000000000000000" DOT DOT);
182 error +=
183 test_policy6toregex ("E1E1:ABCD::ABCD/126;",
184 "6-"DOT DOT DOT
185 DOT "-E1E1ABCD00000000000000000000ABC(C|D|E|F)");
186 error +=
187 test_policy6toregex ("E1E1:ABCD::ABCD/127;",
188 "6-"DOT DOT DOT
189 DOT "-E1E1ABCD00000000000000000000ABC(C|D)");
190 error +=
191 test_policy6toregex ("E1E1:ABCD::ABCD/128:80;",
192 "6-0050-E1E1ABCD00000000000000000000ABCD");
193 return error;
194}
diff --git a/src/lib/util/test_scheduler.c b/src/lib/util/test_scheduler.c
new file mode 100644
index 000000000..4573518fd
--- /dev/null
+++ b/src/lib/util/test_scheduler.c
@@ -0,0 +1,294 @@
1/*
2 This file is part of GNUnet.
3 Copyright (C) 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 util/test_scheduler.c
22 * @brief tests for the scheduler
23 */
24
25#include "platform.h"
26#include "gnunet_util_lib.h"
27
28
29static struct GNUNET_DISK_PipeHandle *p;
30
31static const struct GNUNET_DISK_FileHandle *fds[2];
32
33static struct GNUNET_SCHEDULER_Task *never_run_task;
34
35
36static void
37task2 (void *cls)
38{
39 int *ok = cls;
40
41 /* t3 should be ready (albeit with lower priority) */
42 GNUNET_assert (1 ==
43 GNUNET_SCHEDULER_get_load (GNUNET_SCHEDULER_PRIORITY_COUNT));
44 GNUNET_assert (2 == *ok);
45 (*ok) = 3;
46}
47
48
49static void
50task3 (void *cls)
51{
52 int *ok = cls;
53
54 GNUNET_assert (3 == *ok);
55 (*ok) = 4;
56}
57
58
59static void
60taskWrt (void *cls)
61{
62 static char c;
63 int *ok = cls;
64 const struct GNUNET_SCHEDULER_TaskContext *tc;
65
66 tc = GNUNET_SCHEDULER_get_task_context ();
67 GNUNET_assert (6 == *ok);
68 GNUNET_assert (GNUNET_NETWORK_fdset_handle_isset (tc->write_ready, fds[1]));
69 (*ok) = 7;
70 GNUNET_assert (1 == GNUNET_DISK_file_write (fds[1], &c, 1));
71}
72
73
74static void
75taskNeverRun (void *cls)
76{
77 GNUNET_assert (0);
78}
79
80
81static void
82taskLastRd (void *cls)
83{
84 int *ok = cls;
85
86 GNUNET_assert (8 == *ok);
87 (*ok) = 0;
88}
89
90
91static void
92taskLastSig (void *cls)
93{
94 int *ok = cls;
95
96 GNUNET_SCHEDULER_cancel (never_run_task);
97 GNUNET_assert (9 == *ok);
98 (*ok) = 0;
99}
100
101
102static void
103taskLastShutdown (void *cls)
104{
105 int *ok = cls;
106
107 GNUNET_assert (10 == *ok);
108 (*ok) = 0;
109}
110
111
112static void
113taskRd (void *cls)
114{
115 static char c;
116 int *ok = cls;
117 const struct GNUNET_SCHEDULER_TaskContext *tc;
118
119 tc = GNUNET_SCHEDULER_get_task_context ();
120 GNUNET_assert (7 == *ok);
121 GNUNET_assert (GNUNET_NETWORK_fdset_handle_isset (tc->read_ready, fds[0]));
122 GNUNET_assert (1 == GNUNET_DISK_file_read (fds[0], &c, 1));
123 (*ok) = 8;
124 GNUNET_SCHEDULER_add_shutdown (&taskLastRd,
125 cls);
126 GNUNET_SCHEDULER_shutdown ();
127}
128
129
130static void
131task4 (void *cls)
132{
133 int *ok = cls;
134
135 GNUNET_assert (4 == *ok);
136 (*ok) = 6;
137 p = GNUNET_DISK_pipe (GNUNET_DISK_PF_NONE);
138 GNUNET_assert (NULL != p);
139 fds[0] = GNUNET_DISK_pipe_handle (p, GNUNET_DISK_PIPE_END_READ);
140 fds[1] = GNUNET_DISK_pipe_handle (p, GNUNET_DISK_PIPE_END_WRITE);
141 GNUNET_SCHEDULER_add_read_file (GNUNET_TIME_UNIT_FOREVER_REL,
142 fds[0],
143 &taskRd,
144 cls);
145 GNUNET_SCHEDULER_add_write_file (GNUNET_TIME_UNIT_FOREVER_REL,
146 fds[1],
147 &taskWrt,
148 cls);
149}
150
151
152static void
153task1 (void *cls)
154{
155 int *ok = cls;
156
157 GNUNET_assert (1 == *ok);
158 (*ok) = 2;
159 GNUNET_SCHEDULER_add_now (&task3, cls);
160 GNUNET_SCHEDULER_add_with_priority (GNUNET_SCHEDULER_PRIORITY_UI,
161 &task2,
162 cls);
163 GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_UNIT_SECONDS,
164 &task4,
165 cls);
166}
167
168
169/**
170 * Main method, starts scheduler with task1,
171 * checks that "ok" is correct at the end.
172 */
173static int
174check ()
175{
176 int ok;
177
178 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
179 "[Check scheduling]\n");
180 ok = 1;
181 GNUNET_SCHEDULER_run (&task1, &ok);
182 return ok;
183}
184
185
186static void
187taskShutdown (void *cls)
188{
189 int *ok = cls;
190
191 GNUNET_assert (1 == *ok);
192 *ok = 10;
193 GNUNET_SCHEDULER_add_shutdown (&taskLastShutdown, cls);
194 GNUNET_SCHEDULER_shutdown ();
195}
196
197
198/**
199 * Main method, starts scheduler with task1,
200 * checks that "ok" is correct at the end.
201 */
202static int
203checkShutdown ()
204{
205 int ok;
206
207 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
208 "[Check shutdown]\n");
209 ok = 1;
210 GNUNET_SCHEDULER_run (&taskShutdown, &ok);
211 return ok;
212}
213
214
215static void
216taskSig (void *cls)
217{
218 int *ok = cls;
219
220 GNUNET_assert (1 == *ok);
221 *ok = 9;
222 GNUNET_SCHEDULER_add_shutdown (&taskLastSig, cls);
223 never_run_task =
224 GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_relative_multiply (
225 GNUNET_TIME_UNIT_SECONDS, 5),
226 &taskNeverRun,
227 NULL);
228 GNUNET_break (0 == kill (getpid (),
229 GNUNET_TERM_SIG));
230}
231
232
233/**
234 * Main method, starts scheduler with task1,
235 * checks that "ok" is correct at the end.
236 */
237static int
238checkSignal (void)
239{
240 int ok;
241
242 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
243 "[Check signal handling]\n");
244 ok = 1;
245 GNUNET_SCHEDULER_run (&taskSig, &ok);
246 return ok;
247}
248
249
250static void
251taskCancel (void *cls)
252{
253 int *ok = cls;
254
255 GNUNET_assert (1 == *ok);
256 *ok = 0;
257 GNUNET_SCHEDULER_cancel (GNUNET_SCHEDULER_add_now (&taskNeverRun, NULL));
258}
259
260
261/**
262 * Main method, starts scheduler with task1,
263 * checks that "ok" is correct at the end.
264 */
265static int
266checkCancel ()
267{
268 int ok;
269
270 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
271 "[Check task cancellation]\n");
272 ok = 1;
273 GNUNET_SCHEDULER_run (&taskCancel, &ok);
274 return ok;
275}
276
277
278int
279main (int argc, char *argv[])
280{
281 int ret = 0;
282
283 GNUNET_log_setup ("test_scheduler", "WARNING", NULL);
284 ret += check ();
285 ret += checkCancel ();
286 ret += checkSignal ();
287 ret += checkShutdown ();
288 GNUNET_DISK_pipe_close (p);
289
290 return ret;
291}
292
293
294/* end of test_scheduler.c */
diff --git a/src/lib/util/test_scheduler_delay.c b/src/lib/util/test_scheduler_delay.c
new file mode 100644
index 000000000..41990272a
--- /dev/null
+++ b/src/lib/util/test_scheduler_delay.c
@@ -0,0 +1,96 @@
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 util/test_scheduler_delay.c
22 * @brief testcase for delay of scheduler, measures how
23 * precise the timers are. Expect values between 0.2 and 2 ms on
24 * modern machines.
25 */
26
27#include "platform.h"
28#include "gnunet_util_lib.h"
29
30static struct GNUNET_TIME_Absolute target;
31
32static int i;
33
34static unsigned long long cumDelta;
35
36#define INCR 47
37
38#define MAXV 5000
39
40
41/**
42 * Signature of the main function of a task.
43 *
44 * @param cls closure
45 */
46static void
47test_task (void *cls)
48{
49 struct GNUNET_TIME_Absolute now;
50
51 now = GNUNET_TIME_absolute_get ();
52 if (now.abs_value_us > target.abs_value_us)
53 cumDelta += (now.abs_value_us - target.abs_value_us);
54 else
55 cumDelta += (target.abs_value_us - now.abs_value_us);
56 target =
57 GNUNET_TIME_relative_to_absolute (GNUNET_TIME_relative_multiply
58 (GNUNET_TIME_UNIT_MICROSECONDS, i));
59 fprintf (stderr, "%s", ".");
60 if (i > MAXV)
61 {
62 fprintf (stderr, "%s", "\n");
63 return;
64 }
65 GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_relative_multiply
66 (GNUNET_TIME_UNIT_MICROSECONDS, i),
67 &test_task,
68 NULL);
69 i += INCR;
70}
71
72
73int
74main (int argc, char *argv[])
75{
76 GNUNET_log_setup ("test-scheduler-delay",
77 "WARNING",
78 NULL);
79 target = GNUNET_TIME_absolute_get ();
80 GNUNET_SCHEDULER_run (&test_task, NULL);
81 fprintf (stdout,
82 "Sleep precision: %llu microseconds (average delta). ",
83 cumDelta / (MAXV / INCR));
84 if (cumDelta <= 500 * MAXV / INCR)
85 fprintf (stdout, "%s", "Timer precision is excellent.\n");
86 else if (cumDelta <= 5000 * MAXV / INCR) /* 5 ms average deviation */
87 fprintf (stdout, "%s", "Timer precision is good.\n");
88 else if (cumDelta > 25000 * MAXV / INCR)
89 fprintf (stdout, "%s", "Timer precision is awful.\n");
90 else
91 fprintf (stdout, "%s", "Timer precision is acceptable.\n");
92 return 0;
93}
94
95
96/* end of test_scheduler_delay.c */
diff --git a/src/lib/util/test_scheduler_hogging_cancel.c b/src/lib/util/test_scheduler_hogging_cancel.c
new file mode 100644
index 000000000..7611338b3
--- /dev/null
+++ b/src/lib/util/test_scheduler_hogging_cancel.c
@@ -0,0 +1,51 @@
1#include "gnunet_util_lib.h"
2#include <unistd.h>
3
4static int count = 0;
5static int final_count;
6static struct GNUNET_SCHEDULER_Task *t4;
7
8static void end (void *cls)
9{
10 final_count = count;
11 count = 5000;
12 GNUNET_SCHEDULER_shutdown ();
13}
14
15static void self_rescheduling (void *cls)
16{
17 if (0 == count)
18 {
19 GNUNET_SCHEDULER_cancel (t4);
20 GNUNET_SCHEDULER_add_delayed_with_priority (GNUNET_TIME_UNIT_MILLISECONDS,
21 GNUNET_SCHEDULER_PRIORITY_URGENT,
22 &end,
23 NULL);
24 sleep (1);
25 /* end should be added to ready queue on next scheduler pass for certain
26 now */
27 }
28 if (++count < 5000)
29 {
30 GNUNET_SCHEDULER_add_now (&self_rescheduling, NULL);
31 }
32}
33
34static void to_be_canceled (void *cls)
35{
36 /* Don't run me! */
37}
38
39
40static void init (void *cls)
41{
42 GNUNET_SCHEDULER_add_now (&self_rescheduling, NULL);
43 t4 = GNUNET_SCHEDULER_add_now (&to_be_canceled, NULL);
44}
45
46
47int main (int argc, char **argv)
48{
49 GNUNET_SCHEDULER_run (&init, NULL);
50 return final_count < 5000 ? 0 : 1;
51}
diff --git a/src/lib/util/test_scheduler_hogging_priority.c b/src/lib/util/test_scheduler_hogging_priority.c
new file mode 100644
index 000000000..217a39ce7
--- /dev/null
+++ b/src/lib/util/test_scheduler_hogging_priority.c
@@ -0,0 +1,55 @@
1#include "gnunet_util_lib.h"
2#include <unistd.h>
3
4static int count = 0;
5static int final_count;
6
7static void end (void *cls)
8{
9 final_count = count;
10 count = 5000;
11 GNUNET_SCHEDULER_shutdown ();
12}
13
14static void self_rescheduling (void *cls)
15{
16 if (count == 0)
17 {
18 GNUNET_SCHEDULER_add_delayed_with_priority (GNUNET_TIME_UNIT_MILLISECONDS,
19 GNUNET_SCHEDULER_PRIORITY_URGENT,
20 &end,
21 NULL);
22 sleep(1);
23 /* end should be added to ready queue on next scheduler pass for certain
24 now */
25 }
26 if (++count < 5000)
27 {
28 GNUNET_SCHEDULER_add_now (&self_rescheduling, NULL);
29 }
30}
31
32
33static void noop (void *cls)
34{
35}
36
37static void indirection (void *cls)
38{
39 GNUNET_SCHEDULER_add_with_reason_and_priority (&self_rescheduling, NULL,
40 GNUNET_SCHEDULER_REASON_STARTUP,
41 GNUNET_SCHEDULER_PRIORITY_HIGH);
42}
43
44static void init (void *cls)
45{
46 GNUNET_SCHEDULER_add_now (&indirection, NULL);
47 GNUNET_SCHEDULER_add_now (&noop, NULL);
48}
49
50
51int main (int argc, char **argv)
52{
53 GNUNET_SCHEDULER_run (&init, NULL);
54 return final_count < 5000 ? 0 : 1;
55}
diff --git a/src/lib/util/test_service.c b/src/lib/util/test_service.c
new file mode 100644
index 000000000..198ae68ec
--- /dev/null
+++ b/src/lib/util/test_service.c
@@ -0,0 +1,239 @@
1/*
2 This file is part of GNUnet.
3 Copyright (C) 2009, 2013, 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 * @file util/test_service.c
22 * @brief tests for service.c
23 * @author Christian Grothoff
24 */
25
26#include "platform.h"
27#include "gnunet_util_lib.h"
28
29/**
30 * Message type we use for testing.
31 */
32#define MY_TYPE 256
33
34#define TIMEOUT GNUNET_TIME_UNIT_SECONDS
35
36static int global_ret = 1;
37
38static struct GNUNET_MQ_Handle *mq;
39
40/**
41 * Timeout task.
42 */
43static struct GNUNET_SCHEDULER_Task *tt;
44
45
46static void
47handle_recv (void *cls,
48 const struct GNUNET_MessageHeader *message)
49{
50 struct GNUNET_SERVICE_Client *client = cls;
51
52 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
53 "Received client message...\n");
54 GNUNET_SERVICE_client_continue (client);
55 global_ret = 2;
56 if (NULL != mq)
57 {
58 GNUNET_MQ_destroy (mq);
59 mq = NULL;
60 }
61}
62
63
64/**
65 * Function called when the client connects to the service.
66 *
67 * @param cls the name of the service
68 * @param c connecting client
69 * @param mq message queue to talk to the client
70 * @return @a c so we have the client handle in the future
71 */
72static void *
73connect_cb (void *cls,
74 struct GNUNET_SERVICE_Client *c,
75 struct GNUNET_MQ_Handle *mq)
76{
77 /* FIXME: in the future, do something with mq
78 to test sending messages to the client! */
79 return c;
80}
81
82
83/**
84 * Function called when the client disconnects.
85 *
86 * @param cls our service name
87 * @param c disconnecting client
88 * @param internal_cls must match @a c
89 */
90static void
91disconnect_cb (void *cls,
92 struct GNUNET_SERVICE_Client *c,
93 void *internal_cls)
94{
95 GNUNET_assert (c == internal_cls);
96 if (2 == global_ret)
97 {
98 GNUNET_SCHEDULER_shutdown ();
99 global_ret = 0;
100 if (NULL != tt)
101 {
102 GNUNET_SCHEDULER_cancel (tt);
103 tt = NULL;
104 }
105 }
106}
107
108
109static void
110timeout_task (void *cls)
111{
112 tt = NULL;
113 if (NULL != mq)
114 {
115 GNUNET_MQ_destroy (mq);
116 mq = NULL;
117 }
118 global_ret = 33;
119 GNUNET_SCHEDULER_shutdown ();
120}
121
122
123/**
124 * Initialization function of the service. Starts
125 * a client to connect to the service.
126 *
127 * @param cls the name of the service (const char *)
128 * @param cfg the configuration we use
129 * @param sh handle to the service
130 */
131static void
132service_init (void *cls,
133 const struct GNUNET_CONFIGURATION_Handle *cfg,
134 struct GNUNET_SERVICE_Handle *sh)
135{
136 const char *service_name = cls;
137 struct GNUNET_MQ_Envelope *env;
138 struct GNUNET_MessageHeader *msg;
139
140 GNUNET_assert (NULL == tt);
141 tt = GNUNET_SCHEDULER_add_delayed (TIMEOUT,
142 &timeout_task,
143 NULL);
144 mq = GNUNET_CLIENT_connect (cfg,
145 service_name,
146 NULL,
147 NULL,
148 NULL);
149 GNUNET_assert (NULL != mq);
150 env = GNUNET_MQ_msg (msg,
151 MY_TYPE);
152 GNUNET_MQ_send (mq,
153 env);
154}
155
156
157/**
158 * Main method, starts the service and initiates
159 * the running of the test.
160 *
161 * @param sname name of the service to run
162 */
163static int
164check (const char *sname)
165{
166 struct GNUNET_MQ_MessageHandler myhandlers[] = {
167 GNUNET_MQ_hd_fixed_size (recv,
168 MY_TYPE,
169 struct GNUNET_MessageHeader,
170 NULL),
171 GNUNET_MQ_handler_end ()
172 };
173 char *const argv[] = {
174 (char *) sname,
175 "-c",
176 "test_service_data.conf",
177 NULL
178 };
179
180 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
181 "Starting `%s' service\n",
182 sname);
183 global_ret = 1;
184 GNUNET_assert (0 ==
185 GNUNET_SERVICE_run_ (3,
186 argv,
187 sname,
188 GNUNET_SERVICE_OPTION_NONE,
189 &service_init,
190 &connect_cb,
191 &disconnect_cb,
192 (void *) sname,
193 myhandlers));
194 return global_ret;
195}
196
197
198int
199main (int argc,
200 char *argv[])
201{
202 int ret = 0;
203 struct GNUNET_NETWORK_Handle *s = NULL;
204
205 GNUNET_log_setup ("test-service",
206 "WARNING",
207 NULL);
208 ret += check ("test_service");
209 ret += check ("test_service");
210 s = GNUNET_NETWORK_socket_create (PF_INET6,
211 SOCK_STREAM,
212 0);
213
214 if (NULL == s)
215 {
216 if ((errno == ENOBUFS) ||
217 (errno == ENOMEM) ||
218 (errno == ENFILE) ||
219 (errno == EACCES))
220 {
221 GNUNET_log_strerror (GNUNET_ERROR_TYPE_ERROR,
222 "socket");
223 return 1;
224 }
225 fprintf (stderr,
226 "IPv6 support seems to not be available (%s), not testing it!\n",
227 strerror (errno));
228 }
229 else
230 {
231 GNUNET_break (GNUNET_OK ==
232 GNUNET_NETWORK_socket_close (s));
233 ret += check ("test_service6");
234 }
235 return ret;
236}
237
238
239/* end of test_service.c */
diff --git a/src/lib/util/test_service_data.conf b/src/lib/util/test_service_data.conf
new file mode 100644
index 000000000..3263a52c0
--- /dev/null
+++ b/src/lib/util/test_service_data.conf
@@ -0,0 +1,28 @@
1[test_service]
2PORT=12435
3BINDTO=127.0.0.1
4PIDFILE=$GNUNET_TMP/test-service.pid
5MAXBUF=1024
6DISABLEV6=NO
7ACCEPT_FROM=127.0.0.1;
8REJECT_FROM=1.2.3.0/15;4.5.0.0/8;6.7.8.9/255.255.255.0;
9ACCEPT_FROM6=::1;
10REJECT_FROM6=AB:CD:EF::00;AB:CD::00/40;
11HOSTNAME=localhost
12
13[test_service6]
14PORT=12435
15PIDFILE=$GNUNET_TMP/test-service.pid
16TIMEOUT=30 s
17MAXBUF=1024
18DISABLEV6=NO
19ACCEPT_FROM=127.0.0.1;
20REJECT_FROM=1.2.3.0/15;4.5.0.0/8;6.7.8.9/255.255.255.0;
21ACCEPT_FROM6=::1;
22REJECT_FROM6=AB:CD:EF::00;AB:CD::00/40;
23HOSTNAME=::1
24
25[resolver]
26HOSTNAME=localhost
27
28
diff --git a/src/lib/util/test_socks.c b/src/lib/util/test_socks.c
new file mode 100644
index 000000000..680ecada5
--- /dev/null
+++ b/src/lib/util/test_socks.c
@@ -0,0 +1,258 @@
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 * @file util/test_socks.c
22 * @brief tests for socks.c
23 */
24
25#include "platform.h"
26#include "gnunet_util_lib.h"
27
28
29#define PORT 35124
30
31#define MYNAME "test_sockst"
32
33static struct GNUNET_MQ_Handle *mq;
34
35static struct GNUNET_SERVER_Handle *server;
36
37static struct GNUNET_CONFIGURATION_Handle *cfg;
38
39#define MY_TYPE 130
40
41struct CopyContext
42{
43 struct GNUNET_SERVER_Client *client;
44 struct GNUNET_MessageHeader *cpy;
45};
46
47static size_t
48copy_msg (void *cls, size_t size, void *buf)
49{
50 struct CopyContext *ctx = cls;
51 struct GNUNET_MessageHeader *cpy = ctx->cpy;
52
53 GNUNET_assert (sizeof(struct GNUNET_MessageHeader) == ntohs (cpy->size));
54 GNUNET_assert (size >= ntohs (cpy->size));
55 GNUNET_memcpy (buf, cpy, ntohs (cpy->size));
56 GNUNET_SERVER_receive_done (ctx->client, GNUNET_OK);
57 GNUNET_free (cpy);
58 GNUNET_free (ctx);
59 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Message bounced back to client\n");
60 return sizeof(struct GNUNET_MessageHeader);
61}
62
63
64/**
65 * Callback that just bounces the message back to the sender.
66 */
67static void
68echo_cb (void *cls, struct GNUNET_SERVER_Client *client,
69 const struct GNUNET_MessageHeader *message)
70{
71 struct CopyContext *cc;
72 struct GNUNET_MessageHeader *cpy;
73
74 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
75 "Receiving message from client, bouncing back\n");
76 GNUNET_assert (sizeof(struct GNUNET_MessageHeader) == ntohs (message->size));
77 cc = GNUNET_new (struct CopyContext);
78 cc->client = client;
79 cpy = GNUNET_malloc (ntohs (message->size));
80 GNUNET_memcpy (cpy, message, ntohs (message->size));
81 cc->cpy = cpy;
82 GNUNET_assert (NULL !=
83 GNUNET_SERVER_notify_transmit_ready (client,
84 ntohs (message->size),
85 GNUNET_TIME_UNIT_SECONDS,
86 &copy_msg, cc));
87}
88
89
90static struct GNUNET_SERVER_MessageHandler handlers[] = {
91 { &echo_cb, NULL, MY_TYPE, sizeof(struct GNUNET_MessageHeader) },
92 { NULL, NULL, 0, 0 }
93};
94
95
96static void
97handle_bounce (void *cls,
98 const struct GNUNET_MessageHeader *got)
99{
100 int *ok = cls;
101
102 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
103 "Receiving bounce, checking content\n");
104 GNUNET_assert (NULL != got);
105 GNUNET_MQ_destroy (mq);
106 mq = NULL;
107 GNUNET_SERVER_destroy (server);
108 server = NULL;
109 *ok = 0;
110}
111
112
113/**
114 * Generic error handler, called with the appropriate error code and
115 * the same closure specified at the creation of the message queue.
116 * Not every message queue implementation supports an error handler.
117 *
118 * @param cls closure with the `struct GNUNET_STATISTICS_Handle *`
119 * @param error error code
120 */
121static void
122mq_error_handler (void *cls,
123 enum GNUNET_MQ_Error error)
124{
125 GNUNET_assert (0); /* should never happen */
126}
127
128
129static void
130task (void *cls)
131{
132 struct sockaddr_in sa;
133 struct sockaddr *sap[2];
134 socklen_t slens[2];
135 struct GNUNET_MQ_Envelope *env;
136 struct GNUNET_MessageHeader *msg;
137 struct GNUNET_MQ_MessageHandler chandlers[] = {
138 GNUNET_MQ_hd_fixed_size (bounce,
139 MY_TYPE,
140 struct GNUNET_MessageHeader,
141 cls),
142 GNUNET_MQ_handler_end ()
143 };
144
145 /* test IPC between client and server */
146 sap[0] = (struct sockaddr *) &sa;
147 slens[0] = sizeof(sa);
148 sap[1] = NULL;
149 slens[1] = 0;
150 memset (&sa, 0, sizeof(sa));
151#if HAVE_SOCKADDR_IN_SIN_LEN
152 sa.sin_len = sizeof(sa);
153#endif
154 sa.sin_family = AF_INET;
155 sa.sin_port = htons (PORT);
156 server =
157 GNUNET_SERVER_create (NULL, NULL, sap, slens,
158 GNUNET_TIME_relative_multiply
159 (GNUNET_TIME_UNIT_MILLISECONDS, 10000), GNUNET_NO);
160 GNUNET_assert (server != NULL);
161 handlers[0].callback_cls = cls;
162 handlers[1].callback_cls = cls;
163 GNUNET_SERVER_add_handlers (server, handlers);
164 mq = GNUNET_CLIENT_connect (cfg,
165 MYNAME,
166 chandlers,
167 &mq_error_handler,
168 NULL);
169 GNUNET_assert (NULL != mq);
170 env = GNUNET_MQ_msg (msg,
171 MY_TYPE);
172 GNUNET_MQ_send (mq,
173 env);
174}
175
176
177int
178main (int argc, char *argv[])
179{
180 int ok;
181 int status;
182 const char *socksport = "1081";
183
184 GNUNET_log_setup ("test_client",
185 "WARNING",
186 NULL);
187
188 pid_t pid = fork ();
189 GNUNET_assert (pid >= 0);
190 if (pid == 0)
191 {
192 execlp ("ssh",
193 "ssh", "-D", socksport,
194 "-o", "BatchMode yes",
195 "-o", "UserKnownHostsFile /tmp/gnunet_test_socks_ssh_garbage",
196 "-o", "StrictHostKeyChecking no",
197 "127.0.0.1", "-N", (char *) NULL);
198 perror (
199 "execlp (\"ssh\",\"ssh\",...,\"-D\",\"1081\",\"127.0.0.1\",\"-N\") ");
200 printf (""
201 "Please ensure you have ssh installed and have sshd installed and running :\n"
202 "\tsudo apt-get install openssh-client openssh-server\n"
203 "If you run Tor as a network proxy then Tor might prevent ssh from connecting\n"
204 "to localhost. Please either run make check from an unproxied user, or else\n"
205 "add these lines to the beginning of your ~/.ssh/config file :"
206 "\tHost 127.0.0.1 localhost\n"
207 "\t CheckHostIP no\n"
208 "\t Protocol 2\n"
209 "\t ProxyCommand nc 127.0.0.1 22\n");
210 kill (getppid (), SIGALRM);
211 return 1;
212 }
213 if (0 != sleep (1))
214 {
215 /* sleep interrupted, likely SIGALRM, failure to
216 launch child, terminate */
217 printf (""
218 "Please ensure you have ssh installed and have sshd installed and running :\n"
219 "\tsudo apt-get install openssh-client openssh-server\n"
220 "If you run Tor as a network proxy then Tor might prevent ssh from connecting\n"
221 "to localhost. Please either run make check from an unproxied user, or else\n"
222 "add these lines to the beginning of your ~/.ssh/config file :"
223 "\tHost 127.0.0.1 localhost\n"
224 "\t CheckHostIP no\n"
225 "\t Protocol 2\n"
226 "\t ProxyCommand nc 127.0.0.1 22\n");
227 return 77;
228 }
229 /* check if child exec()ed but died */
230 if (0 != waitpid (pid, &status, WNOHANG))
231 {
232 printf (""
233 "If you run Tor as a network proxy then Tor might prevent ssh from connecting\n"
234 "to localhost. Please either run make check from an unproxied user, or else\n"
235 "add these lines to the beginning of your ~/.ssh/config file :"
236 "\tHost 127.0.0.1 localhost\n"
237 "\t CheckHostIP no\n"
238 "\t Protocol 2\n"
239 "\t ProxyCommand nc 127.0.0.1 22\n");
240 return 77;
241 }
242
243 cfg = GNUNET_CONFIGURATION_create ();
244 GNUNET_CONFIGURATION_set_value_string (cfg, MYNAME, "SOCKSHOST", "127.0.0.1");
245 GNUNET_CONFIGURATION_set_value_string (cfg, MYNAME, "SOCKSPORT", socksport);
246 GNUNET_CONFIGURATION_set_value_number (cfg, MYNAME, "PORT", PORT);
247 GNUNET_CONFIGURATION_set_value_string (cfg, MYNAME, "HOSTNAME", "127.0.0.1");
248 ok = 1;
249 GNUNET_SCHEDULER_run (&task, &ok);
250 GNUNET_CONFIGURATION_destroy (cfg);
251
252 GNUNET_break (0 == kill (pid, SIGTERM));
253 GNUNET_break (pid == waitpid (pid, &status, 0));
254 return ok;
255}
256
257
258/* end of test_socks.c */
diff --git a/src/lib/util/test_speedup.c b/src/lib/util/test_speedup.c
new file mode 100644
index 000000000..58d78641b
--- /dev/null
+++ b/src/lib/util/test_speedup.c
@@ -0,0 +1,127 @@
1/*
2 This file is part of GNUnet.
3 Copyright (C) 2011-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 util/test_speedup.c
22 * @brief testcase for speedup.c
23 */
24
25#include "platform.h"
26#include "gnunet_util_lib.h"
27
28/**
29 * Start time of the testcase
30 */
31static struct GNUNET_TIME_Absolute start;
32
33/**
34 * End-time of the testcase (affected by speed-up)
35 */
36static struct GNUNET_TIME_Absolute end;
37
38/**
39 * Number of cycles we have spent in 'run'.
40 */
41static unsigned int cycles;
42
43
44/**
45 * Main task that is scheduled with the speed-up.
46 *
47 * @param cls NULL
48 * @param tc scheduler context, unused
49 */
50static void
51run (void *cls)
52{
53 cycles++;
54 fprintf (stderr, "..%u", cycles);
55 if (cycles <= 5)
56 {
57 GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_UNIT_SECONDS,
58 &run,
59 NULL);
60 return;
61 }
62 end = GNUNET_TIME_absolute_get ();
63 fprintf (stderr, "\n");
64 fflush (stdout);
65}
66
67
68/**
69 *
70 */
71static void
72check (void *cls,
73 char *const *args,
74 const char *cfgfile,
75 const struct GNUNET_CONFIGURATION_Handle *cfg)
76{
77 fprintf (stderr, "0");
78 fflush (stdout);
79 GNUNET_SCHEDULER_add_now (&run, NULL);
80}
81
82
83int
84main (int argc, char *argv[])
85{
86 static char *const argvn[] = {
87 "test-speedup",
88 "-c", "test_speedup_data.conf",
89 NULL
90 };
91 static struct GNUNET_GETOPT_CommandLineOption options[] = {
92 GNUNET_GETOPT_OPTION_END
93 };
94 time_t start_real;
95 time_t end_real;
96 struct GNUNET_TIME_Relative delta;
97
98 start_real = time (NULL);
99 start = GNUNET_TIME_absolute_get ();
100 GNUNET_PROGRAM_run ((sizeof(argvn) / sizeof(char *)) - 1, argvn,
101 "test-speedup",
102 "nohelp", options, &check, NULL);
103
104 end_real = time (NULL);
105 delta = GNUNET_TIME_absolute_get_difference (start, end);
106
107 if (delta.rel_value_us > ((end_real - start_real) * 1500LL * 1000LL))
108 {
109 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
110 "Execution time in GNUnet time: %s\n",
111 GNUNET_STRINGS_relative_time_to_string (delta, GNUNET_YES));
112 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
113 "Execution time in system time: %llu ms\n",
114 (unsigned long long) ((end_real - start_real) * 1000LL));
115 return 0;
116 }
117 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
118 "Execution time in GNUnet time: %s\n",
119 GNUNET_STRINGS_relative_time_to_string (delta, GNUNET_YES));
120 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
121 "Execution time in system time: %llu ms\n",
122 (unsigned long long) ((end_real - start_real) * 1000LL));
123 return 1;
124}
125
126
127/* end of test_speedup.c */
diff --git a/src/lib/util/test_speedup_data.conf b/src/lib/util/test_speedup_data.conf
new file mode 100644
index 000000000..699cdc933
--- /dev/null
+++ b/src/lib/util/test_speedup_data.conf
@@ -0,0 +1,3 @@
1[testing]
2SPEEDUP_INTERVAL = 100 ms
3SPEEDUP_DELTA = 100 ms
diff --git a/src/lib/util/test_strings.c b/src/lib/util/test_strings.c
new file mode 100644
index 000000000..806324be3
--- /dev/null
+++ b/src/lib/util/test_strings.c
@@ -0,0 +1,174 @@
1/*
2 This file is part of GNUnet.
3 Copyright (C) 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 util/test_strings.c
22 * @brief testcase for strings.c
23 */
24
25#include "platform.h"
26#include "gnunet_util_lib.h"
27
28
29#define WANT(a, b) if (0 != strcmp (a, b)) { fprintf (stderr, \
30 "Got `%s', wanted `%s'\n", \
31 b, a); GNUNET_free (b); \
32 GNUNET_break (0); \
33 return 1; } else { GNUNET_free (b); \
34}
35#define WANTNF(a, b) do { if (0 != strcmp (a, b)) { fprintf (stderr, \
36 "Got `%s', wanted `%s'\n", \
37 b, a); \
38 GNUNET_break (0); return 1; \
39 } } while (0)
40#define WANTB(a, b, l) if (0 != memcmp (a, b, l)) { GNUNET_break (0); return 1; \
41} else { }
42
43#define URLENCODE_TEST_VECTOR_PLAIN "Asbjlaw=ljsdlasjd?人aslkdsa"
44
45#define URLENCODE_TEST_VECTOR_ENCODED \
46 "Asbjlaw\%3Dljsdlasjd\%3F\%E4\%BA\%BAaslkdsa"
47
48int
49main (int argc, char *argv[])
50{
51 char buf[128];
52 char *r;
53 char *b;
54 const char *bc;
55 struct GNUNET_TIME_Absolute at;
56 struct GNUNET_TIME_Absolute atx;
57 struct GNUNET_TIME_Relative rt;
58 struct GNUNET_TIME_Relative rtx;
59 const char *hdir;
60 struct GNUNET_STRINGS_IPv6NetworkPolicy *pol;
61
62 pol = GNUNET_STRINGS_parse_ipv6_policy ("::1;");
63 GNUNET_assert (NULL != pol);
64 GNUNET_free (pol);
65
66 GNUNET_log_setup ("test_strings", "ERROR", NULL);
67 sprintf (buf, "4 %s", _ (/* size unit */ "b"));
68 b = GNUNET_STRINGS_byte_size_fancy (4);
69 WANT (buf, b);
70 sprintf (buf, "10 %s", _ (/* size unit */ "KiB"));
71 b = GNUNET_STRINGS_byte_size_fancy (10240);
72 WANT (buf, b);
73 sprintf (buf, "10 %s", _ (/* size unit */ "TiB"));
74 b = GNUNET_STRINGS_byte_size_fancy (10240LL * 1024LL * 1024LL * 1024LL);
75 WANT (buf, b);
76 sprintf (buf, "4 %s", _ (/* time unit */ "ms"));
77 bc = GNUNET_STRINGS_relative_time_to_string (GNUNET_TIME_relative_multiply
78 (GNUNET_TIME_UNIT_MILLISECONDS,
79 4), GNUNET_YES);
80 WANTNF (buf, bc);
81 sprintf (buf, "7 %s", _ (/* time unit */ "s"));
82 bc = GNUNET_STRINGS_relative_time_to_string (GNUNET_TIME_relative_multiply
83 (GNUNET_TIME_UNIT_MILLISECONDS,
84 7 * 1000), GNUNET_YES);
85 WANTNF (buf, bc);
86 sprintf (buf, "7 %s", _ (/* time unit */ "h"));
87 bc = GNUNET_STRINGS_relative_time_to_string (GNUNET_TIME_relative_multiply
88 (GNUNET_TIME_UNIT_MILLISECONDS,
89 7 * 60 * 60 * 1000),
90 GNUNET_YES);
91 WANTNF (buf, bc);
92
93 hdir = getenv ("HOME");
94
95 GNUNET_snprintf (buf, sizeof(buf), "%s%s", hdir, DIR_SEPARATOR_STR);
96 b = GNUNET_STRINGS_filename_expand ("~");
97 GNUNET_assert (b != NULL);
98 WANT (buf, b);
99 GNUNET_STRINGS_buffer_fill (buf, sizeof(buf), 3, "a", "btx", "c");
100 WANTB ("a\0btx\0c", buf, 8);
101 if (6 != GNUNET_STRINGS_buffer_tokenize (buf, sizeof(buf), 2, &r, &b))
102 return 1;
103 r = GNUNET_strdup (r);
104 WANT ("a", r);
105 b = GNUNET_strdup (b);
106 WANT ("btx", b);
107 if (0 != GNUNET_STRINGS_buffer_tokenize (buf, 2, 2, &r, &b))
108 return 1;
109 at.abs_value_us = 5000000;
110 bc = GNUNET_STRINGS_absolute_time_to_string (at);
111 /* bc should be something like "Wed Dec 31 17:00:05 1969"
112 * where the details of the day and hour depend on the timezone;
113 * however, the "0:05 19" should always be there; hence: */
114 if (NULL == strstr (bc, "0:05 19"))
115 {
116 fprintf (stderr, "Got %s\n", bc);
117 GNUNET_break (0);
118 return 1;
119 }
120 /* Normalization */
121 r = "q\u0307\u0323"; /* Non-canonical order */
122
123 b = GNUNET_STRINGS_utf8_normalize (r);
124 GNUNET_assert (0 == strcmp ("q\u0323\u0307", b));
125 GNUNET_free (b);
126 b = GNUNET_STRINGS_to_utf8 ("TEST", 4, "ASCII");
127 WANT ("TEST", b);
128
129 at = GNUNET_TIME_UNIT_FOREVER_ABS;
130 bc = GNUNET_STRINGS_absolute_time_to_string (at);
131 GNUNET_assert (GNUNET_OK ==
132 GNUNET_STRINGS_fancy_time_to_absolute (bc, &atx));
133 GNUNET_assert (atx.abs_value_us == at.abs_value_us);
134
135 at.abs_value_us = 50000000000;
136 bc = GNUNET_STRINGS_absolute_time_to_string (at);
137
138 GNUNET_assert (GNUNET_OK ==
139 GNUNET_STRINGS_fancy_time_to_absolute (bc, &atx));
140
141 if (atx.abs_value_us != at.abs_value_us)
142 {
143 GNUNET_assert (0);
144 }
145
146 GNUNET_log_skip (2, GNUNET_NO);
147 b = GNUNET_STRINGS_to_utf8 ("TEST", 4, "unknown");
148 GNUNET_log_skip (0, GNUNET_YES);
149 WANT ("TEST", b);
150
151 GNUNET_assert (GNUNET_OK ==
152 GNUNET_STRINGS_fancy_time_to_relative ("15m", &rt));
153 GNUNET_assert (GNUNET_OK ==
154 GNUNET_STRINGS_fancy_time_to_relative ("15 m", &rtx));
155 GNUNET_assert (rt.rel_value_us == rtx.rel_value_us);
156
157 GNUNET_assert (0 != GNUNET_STRINGS_urlencode (strlen (
158 URLENCODE_TEST_VECTOR_PLAIN),
159 URLENCODE_TEST_VECTOR_PLAIN,
160 &b));
161 WANT (URLENCODE_TEST_VECTOR_ENCODED, b);
162 GNUNET_free (b);
163 GNUNET_assert (0 !=
164 GNUNET_STRINGS_urldecode (
165 URLENCODE_TEST_VECTOR_ENCODED,
166 strlen (URLENCODE_TEST_VECTOR_ENCODED),
167 &b));
168 WANT (URLENCODE_TEST_VECTOR_PLAIN, b);
169 GNUNET_free (b);
170 return 0;
171}
172
173
174/* end of test_strings.c */
diff --git a/src/lib/util/test_strings_to_data.c b/src/lib/util/test_strings_to_data.c
new file mode 100644
index 000000000..75cbd135d
--- /dev/null
+++ b/src/lib/util/test_strings_to_data.c
@@ -0,0 +1,66 @@
1/*
2 This file is part of GNUnet.
3 Copyright (C) 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 util/test_strings_to_data.c
22 * @brief testcase for strings.c
23 */
24
25#include "platform.h"
26#include "gnunet_util_lib.h"
27
28
29int
30main (int argc, char *argv[])
31{
32 char buf[1024];
33 char *end;
34 char src[128];
35 char dst[128];
36 unsigned int i;
37 int ret = 0;
38
39 GNUNET_log_setup ("util", "DEBUG", NULL);
40 for (i = 0; i < sizeof(src); i++)
41 {
42 memset (src, i, sizeof(src));
43 memset (dst, i + 1, sizeof(dst));
44
45 end = GNUNET_STRINGS_data_to_string (&src, i, buf, sizeof(buf));
46 GNUNET_assert (NULL != end);
47 end[0] = '\0';
48 if (GNUNET_OK !=
49 GNUNET_STRINGS_string_to_data (buf, strlen (buf), dst, i))
50 {
51 fprintf (stderr, "%u failed decode (%u bytes)\n", i, (unsigned
52 int) strlen (buf));
53 ret = 1;
54 }
55 else if (0 != memcmp (src, dst, i))
56 {
57 fprintf (stderr, "%u wrong decode (%u bytes)\n", i, (unsigned
58 int) strlen (buf));
59 ret = 1;
60 }
61 }
62 return ret;
63}
64
65
66/* end of test_strings_to_data.c */
diff --git a/src/lib/util/test_time.c b/src/lib/util/test_time.c
new file mode 100644
index 000000000..35f270a44
--- /dev/null
+++ b/src/lib/util/test_time.c
@@ -0,0 +1,265 @@
1/*
2 This file is part of GNUnet.
3 Copyright (C) 2001-2013, 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 util/test_time.c
22 * @brief testcase for time.c
23 */
24
25#include "platform.h"
26#include "gnunet_util_lib.h"
27
28
29int
30main (int argc, char *argv[])
31{
32 struct GNUNET_TIME_Absolute now;
33 struct GNUNET_TIME_AbsoluteNBO nown;
34 struct GNUNET_TIME_Absolute future;
35 struct GNUNET_TIME_Absolute past;
36 struct GNUNET_TIME_Absolute last;
37 struct GNUNET_TIME_Absolute forever;
38 struct GNUNET_TIME_Absolute zero;
39 struct GNUNET_TIME_Relative rel;
40 struct GNUNET_TIME_Relative relForever;
41 struct GNUNET_TIME_Relative relUnit;
42 struct GNUNET_TIME_RelativeNBO reln;
43 unsigned int i;
44
45 GNUNET_log_setup ("test-time", "WARNING", NULL);
46 forever = GNUNET_TIME_UNIT_FOREVER_ABS;
47 relForever = GNUNET_TIME_UNIT_FOREVER_REL;
48 relUnit = GNUNET_TIME_UNIT_MILLISECONDS;
49 zero.abs_value_us = 0;
50
51 last = now = GNUNET_TIME_absolute_get ();
52 while (now.abs_value_us == last.abs_value_us)
53 now = GNUNET_TIME_absolute_get ();
54 GNUNET_assert (now.abs_value_us > last.abs_value_us);
55
56 /* test overflow checking in multiply */
57 rel = GNUNET_TIME_UNIT_MILLISECONDS;
58 GNUNET_log_skip (1, GNUNET_NO);
59 for (i = 0; i < 55; i++)
60 rel = GNUNET_TIME_relative_multiply (rel, 2);
61 GNUNET_log_skip (0, GNUNET_NO);
62 GNUNET_assert (rel.rel_value_us == GNUNET_TIME_UNIT_FOREVER_REL.rel_value_us);
63 /*check zero */
64 rel.rel_value_us = (UINT64_MAX) -1024;
65 GNUNET_assert (GNUNET_TIME_UNIT_ZERO.rel_value_us ==
66 GNUNET_TIME_relative_multiply (rel, 0).rel_value_us);
67
68 /* test infinity-check for relative to absolute */
69 GNUNET_log_skip (1, GNUNET_NO);
70 last = GNUNET_TIME_relative_to_absolute (rel);
71 GNUNET_assert (last.abs_value_us ==
72 GNUNET_TIME_UNIT_FOREVER_ABS.abs_value_us);
73 GNUNET_log_skip (0, GNUNET_YES);
74
75 /* check relative to absolute */
76 rel.rel_value_us = 1000000;
77 GNUNET_assert (GNUNET_TIME_absolute_get ().abs_value_us <
78 GNUNET_TIME_relative_to_absolute (rel).abs_value_us);
79 /*check forever */
80 rel.rel_value_us = UINT64_MAX;
81 GNUNET_assert (GNUNET_TIME_UNIT_FOREVER_ABS.abs_value_us ==
82 GNUNET_TIME_relative_to_absolute (rel).abs_value_us);
83 /* check overflow for r2a */
84 rel.rel_value_us = (UINT64_MAX) -1024;
85 GNUNET_log_skip (1, GNUNET_NO);
86 last = GNUNET_TIME_relative_to_absolute (rel);
87 GNUNET_log_skip (0, GNUNET_NO);
88 GNUNET_assert (last.abs_value_us ==
89 GNUNET_TIME_UNIT_FOREVER_ABS.abs_value_us);
90
91 /* check overflow for relative add */
92 GNUNET_log_skip (1, GNUNET_NO);
93 rel = GNUNET_TIME_relative_add (rel, rel);
94 GNUNET_log_skip (0, GNUNET_NO);
95 GNUNET_assert (rel.rel_value_us == GNUNET_TIME_UNIT_FOREVER_REL.rel_value_us);
96
97 GNUNET_log_skip (1, GNUNET_NO);
98 rel = GNUNET_TIME_relative_add (relForever, relForever);
99 GNUNET_log_skip (0, GNUNET_NO);
100 GNUNET_assert (rel.rel_value_us == relForever.rel_value_us);
101
102 GNUNET_log_skip (1, GNUNET_NO);
103 rel = GNUNET_TIME_relative_add (relUnit, relUnit);
104 GNUNET_assert (rel.rel_value_us == 2 * relUnit.rel_value_us);
105
106 /* check relation check in get_duration */
107 future.abs_value_us = now.abs_value_us + 1000000;
108 GNUNET_assert (GNUNET_TIME_absolute_get_difference (now,
109 future).rel_value_us ==
110 1000000);
111 GNUNET_assert (GNUNET_TIME_absolute_get_difference (future,
112 now).rel_value_us ==
113 0);
114
115 GNUNET_assert (GNUNET_TIME_absolute_get_difference (zero,
116 forever).rel_value_us
117 == forever.abs_value_us);
118
119 past.abs_value_us = now.abs_value_us - 1000000;
120 rel = GNUNET_TIME_absolute_get_duration (future);
121 GNUNET_assert (rel.rel_value_us == 0);
122 rel = GNUNET_TIME_absolute_get_duration (past);
123 GNUNET_assert (rel.rel_value_us >= 1000000);
124
125 /* check get remaining */
126 rel = GNUNET_TIME_absolute_get_remaining (now);
127 GNUNET_assert (rel.rel_value_us == 0);
128 rel = GNUNET_TIME_absolute_get_remaining (past);
129 GNUNET_assert (rel.rel_value_us == 0);
130 rel = GNUNET_TIME_absolute_get_remaining (future);
131 GNUNET_assert (rel.rel_value_us > 0);
132 GNUNET_assert (rel.rel_value_us <= 1000000);
133 forever = GNUNET_TIME_UNIT_FOREVER_ABS;
134 GNUNET_assert (GNUNET_TIME_UNIT_FOREVER_REL.rel_value_us ==
135 GNUNET_TIME_absolute_get_remaining (forever).rel_value_us);
136
137 /* check endianness */
138 reln = GNUNET_TIME_relative_hton (rel);
139 GNUNET_assert (rel.rel_value_us == GNUNET_TIME_relative_ntoh (
140 reln).rel_value_us);
141 nown = GNUNET_TIME_absolute_hton (now);
142 GNUNET_assert (now.abs_value_us == GNUNET_TIME_absolute_ntoh (
143 nown).abs_value_us);
144
145 /* check absolute addition */
146 future = GNUNET_TIME_absolute_add (now, GNUNET_TIME_UNIT_SECONDS);
147 GNUNET_assert (future.abs_value_us == now.abs_value_us + 1000 * 1000LL);
148
149 future = GNUNET_TIME_absolute_add (forever, GNUNET_TIME_UNIT_ZERO);
150 GNUNET_assert (future.abs_value_us == forever.abs_value_us);
151
152 rel.rel_value_us = (UINT64_MAX) -1024;
153 now.abs_value_us = rel.rel_value_us;
154 future = GNUNET_TIME_absolute_add (now, rel);
155 GNUNET_assert (future.abs_value_us == forever.abs_value_us);
156
157 /* check zero */
158 future = GNUNET_TIME_absolute_add (now, GNUNET_TIME_UNIT_ZERO);
159 GNUNET_assert (future.abs_value_us == now.abs_value_us);
160
161 GNUNET_assert (forever.abs_value_us ==
162 GNUNET_TIME_absolute_subtract (forever,
163 GNUNET_TIME_UNIT_MINUTES).
164 abs_value_us);
165 /*check absolute subtract */
166 now.abs_value_us = 50000;
167 rel.rel_value_us = 100000;
168 GNUNET_assert (GNUNET_TIME_UNIT_ZERO_ABS.abs_value_us ==
169 (GNUNET_TIME_absolute_subtract (now, rel)).abs_value_us);
170 rel.rel_value_us = 10000;
171 GNUNET_assert (40000 == (GNUNET_TIME_absolute_subtract (now,
172 rel)).abs_value_us);
173
174 /*check relative divide */
175 GNUNET_assert (GNUNET_TIME_UNIT_FOREVER_REL.rel_value_us ==
176 (GNUNET_TIME_relative_divide (rel, 0)).rel_value_us);
177
178 rel = GNUNET_TIME_UNIT_FOREVER_REL;
179 GNUNET_assert (GNUNET_TIME_UNIT_FOREVER_REL.rel_value_us ==
180 (GNUNET_TIME_relative_divide (rel, 2)).rel_value_us);
181
182 rel = GNUNET_TIME_relative_divide (relUnit, 2);
183 GNUNET_assert (rel.rel_value_us == relUnit.rel_value_us / 2);
184
185
186 /* check Return absolute time of 0ms */
187 zero = GNUNET_TIME_UNIT_ZERO_ABS;
188
189 /* check GNUNET_TIME_calculate_eta */
190 last.abs_value_us = GNUNET_TIME_absolute_get ().abs_value_us - 1024;
191 forever = GNUNET_TIME_UNIT_FOREVER_ABS;
192 forever.abs_value_us = forever.abs_value_us - 1024;
193 GNUNET_assert (GNUNET_TIME_UNIT_ZERO_ABS.abs_value_us ==
194 GNUNET_TIME_calculate_eta (forever, 50000,
195 100000).rel_value_us);
196 /* check zero */
197 GNUNET_log_skip (1, GNUNET_NO);
198 GNUNET_assert (GNUNET_TIME_UNIT_ZERO.rel_value_us ==
199 (GNUNET_TIME_calculate_eta (last, 60000, 50000)).rel_value_us);
200 GNUNET_log_skip (0, GNUNET_YES);
201 /*check forever */
202 GNUNET_assert (GNUNET_TIME_UNIT_FOREVER_REL.rel_value_us ==
203 (GNUNET_TIME_calculate_eta (last, 0, 50000)).rel_value_us);
204
205 /*check relative subtract */
206 now = GNUNET_TIME_absolute_get ();
207 rel.rel_value_us = now.abs_value_us;
208 relForever.rel_value_us = rel.rel_value_us + 1024;
209 GNUNET_assert (1024 ==
210 GNUNET_TIME_relative_subtract (relForever, rel).rel_value_us);
211 /*check zero */
212 GNUNET_assert (GNUNET_TIME_UNIT_ZERO.rel_value_us ==
213 GNUNET_TIME_relative_subtract (rel, relForever).rel_value_us);
214 /*check forever */
215 rel.rel_value_us = UINT64_MAX;
216 GNUNET_assert (GNUNET_TIME_UNIT_FOREVER_REL.rel_value_us ==
217 GNUNET_TIME_relative_subtract (rel, relForever).rel_value_us);
218
219 /*check GNUNET_TIME_relative_min */
220 now = GNUNET_TIME_absolute_get ();
221 rel.rel_value_us = now.abs_value_us;
222 relForever.rel_value_us = rel.rel_value_us - 1024;
223 GNUNET_assert (relForever.rel_value_us ==
224 GNUNET_TIME_relative_min (rel, relForever).rel_value_us);
225
226 /*check GNUNET_TIME_relative_max */
227 GNUNET_assert (rel.rel_value_us ==
228 GNUNET_TIME_relative_max (rel, relForever).rel_value_us);
229
230 /*check GNUNET_TIME_absolute_min */
231 now = GNUNET_TIME_absolute_get ();
232 last.abs_value_us = now.abs_value_us - 1024;
233 GNUNET_assert (last.abs_value_us ==
234 GNUNET_TIME_absolute_min (now, last).abs_value_us);
235
236 /*check GNUNET_TIME_absolute_max */
237 GNUNET_assert (now.abs_value_us ==
238 GNUNET_TIME_absolute_max (now, last).abs_value_us);
239 for (unsigned int i = 0; i < 30; i++)
240 {
241 struct GNUNET_CONFIGURATION_Handle *cfg;
242
243 cfg = GNUNET_CONFIGURATION_create ();
244 last = GNUNET_TIME_absolute_get_monotonic (cfg);
245 now = GNUNET_TIME_absolute_get_monotonic (cfg);
246 GNUNET_assert (now.abs_value_us > last.abs_value_us);
247 (void) GNUNET_TIME_absolute_get_monotonic (NULL);
248 GNUNET_CONFIGURATION_set_value_string (cfg,
249 "util",
250 "MONOTONIC_TIME_FILENAME",
251 "monotonic-time.dat");
252 last = GNUNET_TIME_absolute_get_monotonic (cfg);
253 now = GNUNET_TIME_absolute_get_monotonic (cfg);
254 (void) GNUNET_TIME_absolute_get_monotonic (NULL);
255 GNUNET_assert (now.abs_value_us > last.abs_value_us);
256 GNUNET_CONFIGURATION_destroy (cfg);
257 }
258 GNUNET_break (GNUNET_OK ==
259 GNUNET_DISK_directory_remove ("monotonic-time.dat"));
260
261 return 0;
262}
263
264
265/* end of test_time.c */
diff --git a/src/lib/util/test_tun.c b/src/lib/util/test_tun.c
new file mode 100644
index 000000000..1086ef3ca
--- /dev/null
+++ b/src/lib/util/test_tun.c
@@ -0,0 +1,77 @@
1/*
2 This file is part of GNUnet.
3 Copyright (C) 2010, 2011, 2012 Christian Grothoff
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 tun/test_tun.c
23 * @brief test for tun.c
24 * @author Christian Grothoff
25 */
26
27#include "platform.h"
28#include "gnunet_util_lib.h"
29
30static int ret;
31
32static void
33test_udp (size_t pll,
34 int pl_fill,
35 uint16_t crc)
36{
37 struct GNUNET_TUN_IPv4Header ip;
38 struct GNUNET_TUN_UdpHeader udp;
39 char payload[pll];
40 struct in_addr src;
41 struct in_addr dst;
42
43 GNUNET_assert (1 == inet_pton (AF_INET, "1.2.3.4", &src));
44 GNUNET_assert (1 == inet_pton (AF_INET, "122.2.3.5", &dst));
45 memset (payload, pl_fill, sizeof(payload));
46 GNUNET_TUN_initialize_ipv4_header (&ip,
47 IPPROTO_UDP,
48 pll + sizeof(udp),
49 &src,
50 &dst);
51 udp.source_port = htons (4242);
52 udp.destination_port = htons (4242);
53 udp.len = htons (pll);
54 GNUNET_TUN_calculate_udp4_checksum (&ip,
55 &udp,
56 payload,
57 pll);
58 if (crc != ntohs (udp.crc))
59 {
60 fprintf (stderr, "Got CRC: %u, wanted: %u\n",
61 ntohs (udp.crc),
62 crc);
63 ret = 1;
64 }
65}
66
67
68int
69main (int argc,
70 char **argv)
71{
72 test_udp (4, 3, 22439);
73 test_udp (4, 1, 23467);
74 test_udp (7, 17, 6516);
75 test_udp (12451, 251, 42771);
76 return ret;
77}
diff --git a/src/lib/util/test_uri.c b/src/lib/util/test_uri.c
new file mode 100644
index 000000000..d8fa38e96
--- /dev/null
+++ b/src/lib/util/test_uri.c
@@ -0,0 +1,838 @@
1#include "platform.h"
2#include <stdlib.h>
3#include <stdio.h>
4#include <string.h>
5#include "gnunet_uri_lib.h"
6
7#define KNRM "\x1B[0m"
8#define KBLU "\x1B[34m"
9#define KGRN "\x1B[32m"
10#define KERR "\x1B[5;31;50m"
11
12/* macro to print out the header for a new group of tests */
13#define mu_group(name) printf ("%s • %s%s\n", KBLU, name, KNRM)
14
15/* macro for asserting a statement */
16#define mu_assert(message, test) do { \
17 if (!(test)) { \
18 printf ("\t%s× %s%s\n", KERR, message, KNRM); \
19 return message; \
20 } \
21 printf ("\t%s• %s%s\n", KGRN, message, KNRM); \
22 } while (0)
23
24/* macro for asserting a statement without printing it unless it is a failure */
25#define mu_silent_assert(message, test) do { \
26 if (!(test)) { \
27 printf ("\t\t%s× %s%s\n", KERR, message, KNRM); \
28 return message; \
29 } \
30 } while (0)
31
32/* run a test function and return result */
33#define mu_run_test(test) do { \
34 char *message = test (); tests_run++; \
35 if (message) { return message; } \
36 } while (0)
37
38
39int tests_run;
40
41static int
42strcmp_wrap (const char *str,
43 const char *str2)
44{
45 if (NULL == str && NULL == str2) {
46 return 0;
47 }
48 if (NULL == str) {
49 return 1;
50 }
51 if (NULL == str2) {
52 return -1;
53 }
54
55 return strcmp (str, str2);
56}
57
58#define assert_struct(as_url, \
59 as_scheme, \
60 as_user, \
61 as_pass, \
62 as_host, \
63 as_port, \
64 as_path, \
65 as_query, \
66 as_fragment) \
67 mu_silent_assert ("should set the scheme attribute correctly", \
68 0 == strcmp_wrap (as_url.scheme, as_scheme)); \
69 mu_silent_assert ("should set the username attribute correctly", \
70 0 == strcmp_wrap (as_url.username, as_user)); \
71 mu_silent_assert ("should set the password attribute correctly", \
72 0 == strcmp_wrap (as_url.password, as_pass)); \
73 mu_silent_assert ("should set the host attribute correctly", \
74 0 == strcmp_wrap (as_url.host, as_host)); \
75 mu_silent_assert ("should set the port attribute correctly", \
76 as_port == as_url.port); \
77 mu_silent_assert ("should set the path attribute correctly", \
78 0 == strcmp_wrap (as_url.path, as_path)); \
79 mu_silent_assert ("should set the query attribute correctly", \
80 0 == strcmp_wrap (as_url.query, as_query)); \
81 mu_silent_assert ("should set the fragment attribute correctly", \
82 0 == strcmp_wrap (as_url.fragment, as_fragment));
83
84static char *
85test_parse_http_url_ok (void)
86{
87 int rc;
88 struct GNUNET_Uri url;
89 char *url_string;
90
91 /* Minimal URL */
92 url_string = strdup ("http://example.com");
93 rc = GNUNET_uri_parse (&url,
94 url_string);
95 mu_assert ("minimal HTTP URL", -1 != rc);
96 assert_struct (url,
97 "http",
98 NULL,
99 NULL,
100 "example.com",
101 0,
102 NULL,
103 NULL,
104 NULL);
105 free (url_string);
106
107 /* With path (/) */
108 url_string = strdup ("http://example.com/");
109 rc = GNUNET_uri_parse (&url,
110 url_string);
111 mu_assert ("with path ('/')", -1 != rc);
112 assert_struct (url,
113 "http",
114 NULL,
115 NULL,
116 "example.com",
117 0,
118 "",
119 NULL,
120 NULL);
121 free (url_string);
122
123 /* With path */
124 url_string = strdup ("http://example.com/path");
125 rc = GNUNET_uri_parse (&url,
126 url_string);
127 mu_assert ("with path ('/path')", -1 != rc);
128 assert_struct (url,
129 "http",
130 NULL,
131 NULL,
132 "example.com",
133 0,
134 "path",
135 NULL,
136 NULL);
137 free (url_string);
138
139 /* With port */
140 url_string = strdup ("http://example.com:80");
141 rc = GNUNET_uri_parse (&url,
142 url_string);
143 mu_assert ("with port only",
144 -1 != rc);
145 assert_struct (url,
146 "http",
147 NULL,
148 NULL,
149 "example.com",
150 80,
151 NULL,
152 NULL,
153 NULL);
154 free (url_string);
155
156 /* With query */
157 url_string = strdup ("http://example.com?query=only");
158 rc = GNUNET_uri_parse (&url,
159 url_string);
160 mu_assert ("with query only",
161 -1 != rc);
162 assert_struct (url,
163 "http",
164 NULL,
165 NULL,
166 "example.com",
167 0,
168 NULL,
169 "query=only",
170 NULL);
171 free (url_string);
172
173 /* With fragment */
174 url_string = strdup ("http://example.com#frag=f1");
175 rc = GNUNET_uri_parse (&url,
176 url_string);
177 mu_assert ("with fragment only",
178 -1 != rc);
179 assert_struct (url,
180 "http",
181 NULL,
182 NULL,
183 "example.com",
184 0,
185 NULL,
186 NULL,
187 "frag=f1");
188 free (url_string);
189
190 /* With credentials */
191 url_string = strdup ("http://u:p@example.com");
192 rc = GNUNET_uri_parse (&url,
193 url_string);
194 mu_assert ("with credentials only",
195 -1 != rc);
196 assert_struct (url,
197 "http",
198 "u",
199 "p",
200 "example.com",
201 0,
202 NULL,
203 NULL,
204 NULL);
205 free (url_string);
206
207 /* With port and path */
208 url_string = strdup ("http://example.com:8080/port/and/path");
209 rc = GNUNET_uri_parse (&url,
210 url_string);
211 mu_assert ("with port and path",
212 -1 != rc);
213 assert_struct (url,
214 "http",
215 NULL,
216 NULL,
217 "example.com",
218 8080,
219 "port/and/path",
220 NULL,
221 NULL);
222 free (url_string);
223
224 /* With port and query */
225 url_string = strdup ("http://example.com:8080?query=portANDquery");
226 rc = GNUNET_uri_parse (&url,
227 url_string);
228 mu_assert ("with port and query",
229 -1 != rc);
230 assert_struct (url,
231 "http",
232 NULL,
233 NULL,
234 "example.com",
235 8080,
236 NULL,
237 "query=portANDquery",
238 NULL);
239 free (url_string);
240
241 /* With port and fragment */
242 url_string = strdup ("http://example.com:8080#f1");
243 rc = GNUNET_uri_parse (&url,
244 url_string);
245 mu_assert ("with port and fragment",
246 -1 != rc);
247 assert_struct (url,
248 "http",
249 NULL,
250 NULL,
251 "example.com",
252 8080,
253 NULL,
254 NULL,
255 "f1");
256 free (url_string);
257
258 /* With port and credentials */
259 url_string = strdup ("http://u:p@example.com:8080");
260 rc = GNUNET_uri_parse (&url,
261 url_string);
262 mu_assert ("with port and credentials",
263 -1 != rc);
264 assert_struct (url,
265 "http",
266 "u",
267 "p",
268 "example.com",
269 8080,
270 NULL,
271 NULL,
272 NULL);
273 free (url_string);
274
275 /* With path and query */
276 url_string = strdup ("http://example.com/path/and/query?q=yes");
277 rc = GNUNET_uri_parse (&url,
278 url_string);
279 mu_assert ("with path and query",
280 -1 != rc);
281 assert_struct (url,
282 "http",
283 NULL,
284 NULL,
285 "example.com",
286 0,
287 "path/and/query",
288 "q=yes",
289 NULL);
290 free (url_string);
291
292 /* With path and fragment */
293 url_string = strdup ("http://example.com/path/and#fragment");
294 rc = GNUNET_uri_parse (&url,
295 url_string);
296 mu_assert ("with path and fragment",
297 -1 != rc);
298 assert_struct (url,
299 "http",
300 NULL,
301 NULL,
302 "example.com",
303 0,
304 "path/and",
305 NULL,
306 "fragment");
307 free (url_string);
308
309 /* With query and fragment */
310 url_string = strdup ("http://example.com?q=yes#f1");
311 rc = GNUNET_uri_parse (&url,
312 url_string);
313 mu_assert ("with query and fragment",
314 -1 != rc);
315 assert_struct (url,
316 "http",
317 NULL,
318 NULL,
319 "example.com",
320 0,
321 NULL,
322 "q=yes",
323 "f1");
324 free (url_string);
325
326 /* With query and credentials */
327 url_string = strdup ("http://u:p@example.com?q=yes");
328 rc = GNUNET_uri_parse (&url,
329 url_string);
330 mu_assert ("with query and credentials",
331 -1 != rc);
332 assert_struct (url,
333 "http",
334 "u",
335 "p",
336 "example.com",
337 0,
338 NULL,
339 "q=yes",
340 NULL);
341 free (url_string);
342
343 /* With empty credentials */
344 url_string = strdup ("http://:@example.com");
345 rc = GNUNET_uri_parse (&url,
346 url_string);
347 mu_assert ("with empty credentials",
348 -1 != rc);
349 assert_struct (url,
350 "http",
351 "",
352 "",
353 "example.com",
354 0,
355 NULL,
356 NULL,
357 NULL);
358 free (url_string);
359
360 /* With empty credentials and port */
361 url_string = strdup ("http://:@example.com:89");
362 rc = GNUNET_uri_parse (&url,
363 url_string);
364 mu_assert ("with empty credentials and port",
365 -1 != rc);
366 assert_struct (url,
367 "http",
368 "",
369 "",
370 "example.com",
371 89,
372 NULL,
373 NULL,
374 NULL);
375 free (url_string);
376
377 /* Full URL */
378 url_string = strdup ("https://jack:password@localhost:8989/path/to/test?query=yes&q=jack#fragment1");
379 rc = GNUNET_uri_parse (&url,
380 url_string);
381 mu_assert ("with port, path and query",
382 -1 != rc);
383 assert_struct (url,
384 "https",
385 "jack",
386 "password",
387 "localhost",
388 8989,
389 "path/to/test",
390 "query=yes&q=jack",
391 "fragment1");
392 free (url_string);
393
394 return NULL;
395}
396
397static char *
398test_parse_http_rel_url_ok (void)
399{
400 int rc;
401 struct GNUNET_Uri url;
402 char *url_string;
403
404 /* Minimal relative URL */
405 url_string = strdup ("/");
406 rc = GNUNET_uri_parse (&url,
407 url_string);
408 mu_assert ("minimal relative URL",
409 -1 != rc);
410 assert_struct (url,
411 NULL,
412 NULL,
413 NULL,
414 NULL,
415 0,
416 "",
417 NULL,
418 NULL);
419 free (url_string);
420
421 /* Path only */
422 url_string = strdup ("/hejsan");
423 rc = GNUNET_uri_parse (&url,
424 url_string);
425 mu_assert ("path only",
426 -1 != rc);
427 assert_struct (url,
428 NULL,
429 NULL,
430 NULL,
431 NULL,
432 0,
433 "hejsan",
434 NULL,
435 NULL);
436 free (url_string);
437
438 /* Path and query */
439 url_string = strdup ("/hejsan?q=yes");
440 rc = GNUNET_uri_parse (&url,
441 url_string);
442 mu_assert ("path only",
443 -1 != rc);
444 assert_struct (url,
445 NULL,
446 NULL,
447 NULL,
448 NULL,
449 0,
450 "hejsan",
451 "q=yes",
452 NULL);
453 free (url_string);
454
455 /* Path and fragment */
456 url_string = strdup ("/hejsan#fragment");
457 rc = GNUNET_uri_parse (&url,
458 url_string);
459 mu_assert ("path and fragment",
460 -1 != rc);
461 assert_struct (url,
462 NULL,
463 NULL,
464 NULL,
465 NULL,
466 0,
467 "hejsan",
468 NULL,
469 "fragment");
470 free (url_string);
471
472 /* Path, query and fragment */
473 url_string = strdup ("/?q=yes&q2=no#fragment");
474 rc = GNUNET_uri_parse (&url,
475 url_string);
476 mu_assert ("path, query and fragment",
477 -1 != rc);
478 assert_struct (url,
479 NULL,
480 NULL,
481 NULL,
482 NULL,
483 0,
484 "",
485 "q=yes&q2=no",
486 "fragment");
487 free (url_string);
488
489 return NULL;
490}
491
492static char *
493test_parse_url_fail (void)
494{
495 int rc;
496 struct GNUNET_Uri url;
497 char *url_string;
498
499 /* Empty */
500 url_string = strdup ("");
501 rc = GNUNET_uri_parse (&url,
502 url_string);
503 mu_assert ("empty string should return -1",
504 -1 == rc);
505 free (url_string);
506
507 /* Scheme only */
508 url_string = strdup ("rtsp://");
509 rc = GNUNET_uri_parse (&url,
510 url_string);
511 mu_assert ("scheme only should return -1",
512 -1 == rc);
513 free (url_string);
514
515 /* Hostname only */
516 url_string = strdup ("hostname");
517 rc = GNUNET_uri_parse (&url,
518 url_string);
519 mu_assert ("hostname only should return -1",
520 -1 == rc);
521 free (url_string);
522
523 /* Query only */
524 url_string = strdup ("?query=only");
525 rc = GNUNET_uri_parse (&url,
526 url_string);
527 mu_assert ("query only should return -1",
528 -1 == rc);
529 free (url_string);
530
531 /* Missing scheme */
532 url_string = strdup ("://");
533 rc = GNUNET_uri_parse (&url,
534 url_string);
535 mu_assert ("missing scheme should return -1",
536 -1 == rc);
537 free (url_string);
538
539 /* Missing hostname */
540 url_string = strdup ("rtsp://:8910/path");
541 rc = GNUNET_uri_parse (&url,
542 url_string);
543 mu_assert ("missing hostname should return -1",
544 -1 == rc);
545 free (url_string);
546
547 /* Missing credentials */
548 url_string = strdup ("rtsp://@hostname:8910/path");
549 rc = GNUNET_uri_parse (&url,
550 url_string);
551 mu_assert ("missing credentials should return -1",
552 -1 == rc);
553 free (url_string);
554
555 return NULL;
556}
557
558static char *
559test_split_path_ok (void)
560{
561 int rc;
562 char *path;
563 char *parts[10];
564
565 /* Simple path */
566 path = strdup ("/this/is/a/path");
567 rc = GNUNET_uri_split_path (path,
568 parts,
569 10);
570 mu_assert ("should be able to parse a regular path",
571 4 == rc);
572 mu_silent_assert ("first part should be 'this'",
573 0 == strcmp ("this", parts[0]));
574 mu_silent_assert ("second part should be 'is'",
575 0 == strcmp ("is", parts[1]));
576 mu_silent_assert ("third part should be 'a'",
577 0 == strcmp ("a", parts[2]));
578 mu_silent_assert ("fourth part should be 'path'",
579 0 == strcmp ("path", parts[3]));
580 free (path);
581
582 /* Relative path */
583 path = strdup ("this/is/a/path");
584 rc = GNUNET_uri_split_path (path,
585 parts,
586 10);
587 mu_assert ("should be able to parse a relative path",
588 4 == rc);
589 mu_silent_assert ("first part should be 'this'",
590 0 == strcmp ("this", parts[0]));
591 mu_silent_assert ("second part should be 'is'",
592 0 == strcmp ("is", parts[1]));
593 mu_silent_assert ("third part should be 'a'",
594 0 == strcmp ("a", parts[2]));
595 mu_silent_assert ("fourth part should be 'path'",
596 0 == strcmp ("path", parts[3]));
597 free (path);
598
599 /* Path with empty parts */
600 path = strdup ("//this//is/a/path/");
601 rc = GNUNET_uri_split_path (path,
602 parts,
603 10);
604 mu_assert ("should treat multiple slashes as one",
605 4 == rc);
606 mu_silent_assert ("first part should be 'this'",
607 0 == strcmp("this", parts[0]));
608 mu_silent_assert ("second part should be 'is'",
609 0 == strcmp("is", parts[1]));
610 mu_silent_assert ("third part should be 'a'",
611 0 == strcmp("a", parts[2]));
612 mu_silent_assert ("fourth part should be 'path'",
613 0 == strcmp("path", parts[3]));
614 free (path);
615
616 /* Just one level */
617 path = strdup("/one_level");
618 rc = GNUNET_uri_split_path(path, parts, 10);
619 mu_assert("should be able to parse a path with one level", 1 == rc);
620 mu_silent_assert("first part should be 'this'", 0 == strcmp("one_level", parts[0]));
621 free(path);
622
623 return NULL;
624}
625
626static char *
627test_parse_query_ok (void)
628{
629 int rc;
630 char *q;
631 struct GNUNET_UriParam params[10];
632
633 /* One param query */
634 q = strdup ("q=yes");
635 rc = GNUNET_uri_parse_query (q,
636 '&',
637 params,
638 10);
639 mu_assert ("single parameter with value",
640 1 == rc);
641 mu_silent_assert ("first param key should be 'q'",
642 0 == strcmp ("q", params[0].key));
643 mu_silent_assert ("first param val should be 'yes'",
644 0 == strcmp ("yes", params[0].val));
645 free (q);
646
647 /* One param query without value */
648 q = strdup ("q");
649 rc = GNUNET_uri_parse_query (q,
650 '&',
651 params,
652 10);
653 mu_assert ("single parameter without value",
654 1 == rc);
655 mu_silent_assert ("first param key should be 'q'",
656 0 == strcmp ("q", params[0].key));
657 mu_silent_assert ("first param val should be NULL",
658 NULL == params[0].val);
659 free (q);
660
661 /* Two param query */
662 q = strdup ("query=yes&a1=hello");
663 rc = GNUNET_uri_parse_query (q,
664 '&',
665 params,
666 10);
667 mu_assert ("multiple params with value",
668 2 == rc);
669 mu_silent_assert ("first param key should be 'query'",
670 0 == strcmp ("query", params[0].key));
671 mu_silent_assert ("first param val should be 'yes'",
672 0 == strcmp ("yes", params[0].val));
673 mu_silent_assert ("second param key should be 'a1'",
674 0 == strcmp ("a1", params[1].key));
675 mu_silent_assert ("second param val should be 'hello'",
676 0 == strcmp ("hello", params[1].val));
677 free (q);
678
679 /* Two param query, one without value */
680 q = strdup ("query=yes&forceHttps");
681 rc = GNUNET_uri_parse_query (q,
682 '&',
683 params,
684 10);
685 mu_assert ("multiple params one without value",
686 2 == rc);
687 mu_silent_assert ("first param key should be 'query'",
688 0 == strcmp ("query", params[0].key));
689 mu_silent_assert ("first param val should be 'yes'",
690 0 == strcmp ("yes", params[0].val));
691 mu_silent_assert ("second param key should be 'forceHttps'",
692 0 == strcmp ("forceHttps", params[1].key));
693 mu_silent_assert ("second param val should be NULL",
694 NULL == params[1].val);
695 free (q);
696
697 /* Three param query, all without value */
698 q = strdup ("query&forceHttps&log");
699 rc = GNUNET_uri_parse_query (q,
700 '&',
701 params,
702 10);
703 mu_assert ("multiple params all without value",
704 3 == rc);
705 mu_silent_assert ("first param key should be 'query'",
706 0 == strcmp ("query", params[0].key));
707 mu_silent_assert ("first param val should be NULL",
708 NULL == params[0].val);
709 mu_silent_assert ("second param key should be 'forceHttps'",
710 0 == strcmp ("forceHttps", params[1].key));
711 mu_silent_assert ("second param val should be NULL",
712 NULL == params[1].val);
713 mu_silent_assert ("third param key should be 'log'",
714 0 == strcmp ("log", params[2].key));
715 mu_silent_assert ("third param val should be NULL",
716 NULL == params[2].val);
717 free (q);
718
719 /* Param with empty value */
720 q = strdup ("param=&query=no");
721 rc = GNUNET_uri_parse_query (q,
722 '&',
723 params,
724 10);
725 mu_assert ("param with empty value",
726 2 == rc);
727 mu_silent_assert ("first param key should be 'param'",
728 0 == strcmp ("param", params[0].key));
729 mu_silent_assert ("first param val should be ''",
730 0 == strcmp ("", params[0].val));
731 mu_silent_assert ("second param key should be 'query'",
732 0 == strcmp ("query", params[1].key));
733 mu_silent_assert ("second param val should be 'no'",
734 0 == strcmp ("no", params[1].val));
735 free (q);
736
737 /* Double delimiter */
738 q = strdup ("param=jack&&query=no");
739 rc = GNUNET_uri_parse_query (q,
740 '&',
741 params,
742 10);
743 mu_assert ("double delimiter",
744 3 == rc);
745 mu_silent_assert ("first param key should be 'param'",
746 0 == strcmp ("param", params[0].key));
747 mu_silent_assert ("first param val should be 'jack'",
748 0 == strcmp ("jack", params[0].val));
749 mu_silent_assert ("second param key should be ''",
750 0 == strcmp ("", params[1].key));
751 mu_silent_assert ("second param val should be NULL",
752 NULL == params[1].val);
753 mu_silent_assert ("third param key should be 'query'",
754 0 == strcmp ("query", params[2].key));
755 mu_silent_assert ("third param val should be 'no'",
756 0 == strcmp ("no", params[2].val));
757 free (q);
758
759 /* Delimiter in beginning */
760 q = strdup ("&param=jack&query=no");
761 rc = GNUNET_uri_parse_query (q,
762 '&',
763 params,
764 10);
765 mu_assert ("delimiter in beginning",
766 3 == rc);
767 mu_silent_assert ("first param key should be ''",
768 0 == strcmp ("", params[0].key));
769 mu_silent_assert ("first param val should be NULL",
770 NULL == params[0].val);
771 mu_silent_assert ("second param key should be 'param'",
772 0 == strcmp ("param", params[1].key));
773 mu_silent_assert ("second param val should be 'jack'",
774 0 == strcmp ("jack", params[1].val));
775 mu_silent_assert ("third param key should be 'query'",
776 0 == strcmp ("query", params[2].key));
777 mu_silent_assert ("third param val should be 'no'",
778 0 == strcmp ("no", params[2].val));
779 free (q);
780
781 /* Delimiter at the end */
782 q = strdup ("param=jack&query=no&");
783 rc = GNUNET_uri_parse_query (q,
784 '&',
785 params,
786 10);
787 mu_assert ("delimiter at the end",
788 3 == rc);
789 mu_silent_assert ("first param key should be 'param'",
790 0 == strcmp ("param", params[0].key));
791 mu_silent_assert ("first param val should be 'jack'",
792 0 == strcmp ("jack", params[0].val));
793 mu_silent_assert ("second param key should be 'query'",
794 0 == strcmp ("query", params[1].key));
795 mu_silent_assert ("second param val should be 'no'",
796 0 == strcmp ("no", params[1].val));
797 mu_silent_assert ("third param key should be ''",
798 0 == strcmp ("", params[2].key));
799 mu_silent_assert ("third param val should be NULL",
800 NULL == params[2].val);
801 free (q);
802
803 return NULL;
804}
805
806static char *
807all_tests (void)
808{
809 mu_group ("GNUNET_uri_parse () with an HTTP URL");
810 mu_run_test (test_parse_http_url_ok);
811
812 mu_group ("GNUNET_uri_parse () with an relative URL");
813 mu_run_test (test_parse_http_rel_url_ok);
814
815 mu_group ("GNUNET_uri_parse () with faulty values");
816 mu_run_test (test_parse_url_fail);
817
818 mu_group ("GNUNET_uri_split_path ()");
819 mu_run_test (test_split_path_ok);
820
821 mu_group ("GNUNET_uri_parse_query ()");
822 mu_run_test (test_parse_query_ok);
823
824 return NULL;
825}
826
827int
828main (void)
829{
830 char *result;
831
832 result = all_tests ();
833 if (result != NULL) {
834 exit (EXIT_FAILURE);
835 }
836
837 exit (EXIT_SUCCESS);
838}
diff --git a/src/lib/util/time.c b/src/lib/util/time.c
new file mode 100644
index 000000000..1bc7baeaa
--- /dev/null
+++ b/src/lib/util/time.c
@@ -0,0 +1,991 @@
1/*
2 This file is part of GNUnet.
3 Copyright (C) 2001-2013, 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/**
22 * @file util/time.c
23 * @author Christian Grothoff
24 * @brief functions for handling time and time arithmetic
25 */
26
27#include "platform.h"
28#include "gnunet_util_lib.h"
29#if __STDC_NO_ATOMICS__
30#define ATOMIC
31#else
32#ifdef HAVE_STDATOMIC_H
33#include <stdatomic.h>
34#define ATOMIC _Atomic
35#else
36#define __STDC_NO_ATOMICS__ 1
37#define ATOMIC
38#endif
39#endif
40
41#define LOG(kind, ...) GNUNET_log_from (kind, "util-time", __VA_ARGS__)
42
43/**
44 * Variable used to simulate clock skew. Used for testing, never in production.
45 */
46static long long timestamp_offset;
47
48void
49GNUNET_TIME_set_offset (long long offset)
50{
51 timestamp_offset = offset;
52}
53
54
55long long
56GNUNET_TIME_get_offset ()
57{
58 return timestamp_offset;
59}
60
61
62bool
63GNUNET_TIME_absolute_approx_eq (struct GNUNET_TIME_Absolute a1,
64 struct GNUNET_TIME_Absolute a2,
65 struct GNUNET_TIME_Relative t)
66{
67 struct GNUNET_TIME_Relative delta;
68
69 delta = GNUNET_TIME_relative_min (
70 GNUNET_TIME_absolute_get_difference (a1, a2),
71 GNUNET_TIME_absolute_get_difference (a2, a1));
72 return GNUNET_TIME_relative_cmp (delta,
73 <=,
74 t);
75}
76
77
78struct GNUNET_TIME_Timestamp
79GNUNET_TIME_absolute_to_timestamp (struct GNUNET_TIME_Absolute at)
80{
81 struct GNUNET_TIME_Timestamp ts;
82
83 if (GNUNET_TIME_absolute_is_never (at))
84 return GNUNET_TIME_UNIT_FOREVER_TS;
85 ts.abs_time.abs_value_us = at.abs_value_us - at.abs_value_us % 1000000;
86 return ts;
87}
88
89
90struct GNUNET_TIME_TimestampNBO
91GNUNET_TIME_timestamp_hton (struct GNUNET_TIME_Timestamp t)
92{
93 struct GNUNET_TIME_TimestampNBO tn;
94
95 tn.abs_time_nbo = GNUNET_TIME_absolute_hton (t.abs_time);
96 return tn;
97}
98
99
100struct GNUNET_TIME_Timestamp
101GNUNET_TIME_timestamp_ntoh (struct GNUNET_TIME_TimestampNBO tn)
102{
103 struct GNUNET_TIME_Timestamp t;
104
105 t.abs_time = GNUNET_TIME_absolute_ntoh (tn.abs_time_nbo);
106 return t;
107}
108
109
110struct GNUNET_TIME_Absolute
111GNUNET_TIME_absolute_get ()
112{
113 struct GNUNET_TIME_Absolute ret;
114 struct timeval tv;
115
116 gettimeofday (&tv, NULL);
117 ret.abs_value_us = (uint64_t) (((uint64_t) tv.tv_sec * 1000LL * 1000LL)
118 + ((uint64_t) tv.tv_usec))
119 + timestamp_offset;
120 return ret;
121}
122
123
124struct GNUNET_TIME_Timestamp
125GNUNET_TIME_timestamp_get ()
126{
127 return GNUNET_TIME_absolute_to_timestamp (
128 GNUNET_TIME_absolute_get ());
129}
130
131
132struct GNUNET_TIME_Relative
133GNUNET_TIME_relative_get_zero_ ()
134{
135 static struct GNUNET_TIME_Relative zero;
136
137 return zero;
138}
139
140
141struct GNUNET_TIME_Absolute
142GNUNET_TIME_absolute_get_zero_ ()
143{
144 static struct GNUNET_TIME_Absolute zero;
145
146 return zero;
147}
148
149
150struct GNUNET_TIME_Relative
151GNUNET_TIME_relative_get_unit_ ()
152{
153 static struct GNUNET_TIME_Relative one = { 1 };
154
155 return one;
156}
157
158
159struct GNUNET_TIME_Relative
160GNUNET_TIME_relative_get_millisecond_ ()
161{
162 static struct GNUNET_TIME_Relative one = { 1000 };
163
164 return one;
165}
166
167
168struct GNUNET_TIME_Relative
169GNUNET_TIME_relative_get_second_ ()
170{
171 static struct GNUNET_TIME_Relative one = { 1000 * 1000LL };
172
173 return one;
174}
175
176
177struct GNUNET_TIME_Relative
178GNUNET_TIME_relative_get_minute_ ()
179{
180 static struct GNUNET_TIME_Relative one = { 60 * 1000 * 1000LL };
181
182 return one;
183}
184
185
186struct GNUNET_TIME_Relative
187GNUNET_TIME_relative_get_hour_ ()
188{
189 static struct GNUNET_TIME_Relative one = { 60 * 60 * 1000 * 1000LL };
190
191 return one;
192}
193
194
195struct GNUNET_TIME_Relative
196GNUNET_TIME_relative_get_forever_ ()
197{
198 static struct GNUNET_TIME_Relative forever = { UINT64_MAX };
199
200 return forever;
201}
202
203
204struct GNUNET_TIME_Absolute
205GNUNET_TIME_absolute_get_forever_ ()
206{
207 static struct GNUNET_TIME_Absolute forever = { UINT64_MAX };
208
209 return forever;
210}
211
212
213const char *
214GNUNET_TIME_timestamp2s (struct GNUNET_TIME_Timestamp ts)
215{
216 static GNUNET_THREAD_LOCAL char buf[255];
217 time_t tt;
218 struct tm *tp;
219
220 if (GNUNET_TIME_absolute_is_never (ts.abs_time))
221 return "end of time";
222 tt = ts.abs_time.abs_value_us / 1000LL / 1000LL;
223 tp = localtime (&tt);
224 /* This is hacky, but i don't know a way to detect libc character encoding.
225 * Just expect utf8 from glibc these days.
226 * As for msvcrt, use the wide variant, which always returns utf16
227 * (otherwise we'd have to detect current codepage or use W32API character
228 * set conversion routines to convert to UTF8).
229 */
230 strftime (buf,
231 sizeof(buf),
232 "%a %b %d %H:%M:%S %Y",
233 tp);
234 return buf;
235}
236
237
238const char *
239GNUNET_TIME_absolute2s (struct GNUNET_TIME_Absolute t)
240{
241 static GNUNET_THREAD_LOCAL char buf[255];
242 time_t tt;
243 struct tm *tp;
244
245 if (GNUNET_TIME_absolute_is_never (t))
246 return "end of time";
247 tt = t.abs_value_us / 1000LL / 1000LL;
248 tp = localtime (&tt);
249 /* This is hacky, but i don't know a way to detect libc character encoding.
250 * Just expect utf8 from glibc these days.
251 * As for msvcrt, use the wide variant, which always returns utf16
252 * (otherwise we'd have to detect current codepage or use W32API character
253 * set conversion routines to convert to UTF8).
254 */
255 strftime (buf,
256 sizeof(buf),
257 "%a %b %d %H:%M:%S %Y",
258 tp);
259 return buf;
260}
261
262
263const char *
264GNUNET_TIME_relative2s (struct GNUNET_TIME_Relative delta,
265 bool do_round)
266{
267 static GNUNET_THREAD_LOCAL char buf[128];
268 const char *unit = /* time unit */ "µs";
269 uint64_t dval = delta.rel_value_us;
270
271 if (GNUNET_TIME_relative_is_forever (delta))
272 return "forever";
273 if (0 == delta.rel_value_us)
274 return "0 ms";
275 if ( ((GNUNET_YES == do_round) &&
276 (dval > 5 * 1000)) ||
277 (0 == (dval % 1000)))
278 {
279 dval = dval / 1000;
280 unit = /* time unit */ "ms";
281 if (((GNUNET_YES == do_round) && (dval > 5 * 1000)) || (0 == (dval % 1000)))
282 {
283 dval = dval / 1000;
284 unit = /* time unit */ "s";
285 if (((GNUNET_YES == do_round) && (dval > 5 * 60)) || (0 == (dval % 60)))
286 {
287 dval = dval / 60;
288 unit = /* time unit */ "m";
289 if (((GNUNET_YES == do_round) && (dval > 5 * 60)) || (0 == (dval % 60)))
290 {
291 dval = dval / 60;
292 unit = /* time unit */ "h";
293 if (((GNUNET_YES == do_round) && (dval > 5 * 24)) ||
294 (0 == (dval % 24)))
295 {
296 dval = dval / 24;
297 if (1 == dval)
298 unit = /* time unit */ "day";
299 else
300 unit = /* time unit */ "days";
301 }
302 }
303 }
304 }
305 }
306 GNUNET_snprintf (buf,
307 sizeof(buf),
308 "%llu %s",
309 (unsigned long long) dval,
310 unit);
311 return buf;
312}
313
314
315struct GNUNET_TIME_Absolute
316GNUNET_TIME_relative_to_absolute (struct GNUNET_TIME_Relative rel)
317{
318 struct GNUNET_TIME_Absolute ret;
319
320 if (GNUNET_TIME_relative_is_forever (rel))
321 return GNUNET_TIME_UNIT_FOREVER_ABS;
322 struct GNUNET_TIME_Absolute now = GNUNET_TIME_absolute_get ();
323
324 if (rel.rel_value_us + now.abs_value_us < rel.rel_value_us)
325 {
326 GNUNET_break (0); /* overflow... */
327 return GNUNET_TIME_UNIT_FOREVER_ABS;
328 }
329 ret.abs_value_us = rel.rel_value_us + now.abs_value_us;
330 return ret;
331}
332
333
334struct GNUNET_TIME_Timestamp
335GNUNET_TIME_relative_to_timestamp (struct GNUNET_TIME_Relative rel)
336{
337 return GNUNET_TIME_absolute_to_timestamp (
338 GNUNET_TIME_relative_to_absolute (rel));
339}
340
341
342struct GNUNET_TIME_Relative
343GNUNET_TIME_relative_min (struct GNUNET_TIME_Relative t1,
344 struct GNUNET_TIME_Relative t2)
345{
346 return (t1.rel_value_us < t2.rel_value_us) ? t1 : t2;
347}
348
349
350struct GNUNET_TIME_Relative
351GNUNET_TIME_relative_max (struct GNUNET_TIME_Relative t1,
352 struct GNUNET_TIME_Relative t2)
353{
354 return (t1.rel_value_us > t2.rel_value_us) ? t1 : t2;
355}
356
357
358struct GNUNET_TIME_Absolute
359GNUNET_TIME_absolute_min (struct GNUNET_TIME_Absolute t1,
360 struct GNUNET_TIME_Absolute t2)
361{
362 return (t1.abs_value_us < t2.abs_value_us) ? t1 : t2;
363}
364
365
366struct GNUNET_TIME_Absolute
367GNUNET_TIME_absolute_max (struct GNUNET_TIME_Absolute t1,
368 struct GNUNET_TIME_Absolute t2)
369{
370 return (t1.abs_value_us > t2.abs_value_us) ? t1 : t2;
371}
372
373
374struct GNUNET_TIME_Timestamp
375GNUNET_TIME_timestamp_max (struct GNUNET_TIME_Timestamp t1,
376 struct GNUNET_TIME_Timestamp t2)
377{
378 return (t1.abs_time.abs_value_us > t2.abs_time.abs_value_us) ? t1 : t2;
379}
380
381
382struct GNUNET_TIME_Timestamp
383GNUNET_TIME_timestamp_min (struct GNUNET_TIME_Timestamp t1,
384 struct GNUNET_TIME_Timestamp t2)
385{
386 return (t1.abs_time.abs_value_us < t2.abs_time.abs_value_us) ? t1 : t2;
387}
388
389
390struct GNUNET_TIME_Absolute
391GNUNET_TIME_absolute_round_down (struct GNUNET_TIME_Absolute at,
392 struct GNUNET_TIME_Relative rt)
393{
394 struct GNUNET_TIME_Absolute ret;
395
396 GNUNET_assert (! GNUNET_TIME_relative_is_zero (rt));
397 ret.abs_value_us
398 = at.abs_value_us
399 - at.abs_value_us % rt.rel_value_us;
400 return ret;
401}
402
403
404struct GNUNET_TIME_Relative
405GNUNET_TIME_absolute_get_remaining (struct GNUNET_TIME_Absolute future)
406{
407 struct GNUNET_TIME_Relative ret;
408
409 if (GNUNET_TIME_absolute_is_never (future))
410 return GNUNET_TIME_UNIT_FOREVER_REL;
411 struct GNUNET_TIME_Absolute now = GNUNET_TIME_absolute_get ();
412
413 if (now.abs_value_us > future.abs_value_us)
414 return GNUNET_TIME_UNIT_ZERO;
415 ret.rel_value_us = future.abs_value_us - now.abs_value_us;
416 return ret;
417}
418
419
420struct GNUNET_TIME_Relative
421GNUNET_TIME_absolute_get_difference (struct GNUNET_TIME_Absolute start,
422 struct GNUNET_TIME_Absolute end)
423{
424 struct GNUNET_TIME_Relative ret;
425
426 if (GNUNET_TIME_absolute_is_never (end))
427 return GNUNET_TIME_UNIT_FOREVER_REL;
428 if (end.abs_value_us < start.abs_value_us)
429 return GNUNET_TIME_UNIT_ZERO;
430 ret.rel_value_us = end.abs_value_us - start.abs_value_us;
431 return ret;
432}
433
434
435struct GNUNET_TIME_Relative
436GNUNET_TIME_absolute_get_duration (struct GNUNET_TIME_Absolute whence)
437{
438 struct GNUNET_TIME_Absolute now;
439 struct GNUNET_TIME_Relative ret;
440
441 now = GNUNET_TIME_absolute_get ();
442 if (whence.abs_value_us > now.abs_value_us)
443 return GNUNET_TIME_UNIT_ZERO;
444 ret.rel_value_us = now.abs_value_us - whence.abs_value_us;
445 return ret;
446}
447
448
449struct GNUNET_TIME_Absolute
450GNUNET_TIME_absolute_add (struct GNUNET_TIME_Absolute start,
451 struct GNUNET_TIME_Relative duration)
452{
453 struct GNUNET_TIME_Absolute ret;
454
455 if (GNUNET_TIME_absolute_is_never (start) ||
456 GNUNET_TIME_relative_is_forever (duration))
457 return GNUNET_TIME_UNIT_FOREVER_ABS;
458 if (start.abs_value_us + duration.rel_value_us < start.abs_value_us)
459 {
460 GNUNET_break (0);
461 return GNUNET_TIME_UNIT_FOREVER_ABS;
462 }
463 ret.abs_value_us = start.abs_value_us + duration.rel_value_us;
464 return ret;
465}
466
467
468struct GNUNET_TIME_Absolute
469GNUNET_TIME_absolute_subtract (struct GNUNET_TIME_Absolute start,
470 struct GNUNET_TIME_Relative duration)
471{
472 struct GNUNET_TIME_Absolute ret;
473
474 if (start.abs_value_us <= duration.rel_value_us)
475 return GNUNET_TIME_UNIT_ZERO_ABS;
476 if (GNUNET_TIME_absolute_is_never (start))
477 return GNUNET_TIME_UNIT_FOREVER_ABS;
478 ret.abs_value_us = start.abs_value_us - duration.rel_value_us;
479 return ret;
480}
481
482
483struct GNUNET_TIME_Relative
484GNUNET_TIME_relative_multiply (struct GNUNET_TIME_Relative rel,
485 unsigned long long factor)
486{
487 struct GNUNET_TIME_Relative ret;
488
489 if (0 == factor)
490 return GNUNET_TIME_UNIT_ZERO;
491 if (GNUNET_TIME_relative_is_forever (rel))
492 return GNUNET_TIME_UNIT_FOREVER_REL;
493 ret.rel_value_us = rel.rel_value_us * factor;
494 if (ret.rel_value_us / factor != rel.rel_value_us)
495 {
496 GNUNET_break (0);
497 return GNUNET_TIME_UNIT_FOREVER_REL;
498 }
499 return ret;
500}
501
502
503struct GNUNET_TIME_Relative
504GNUNET_TIME_relative_multiply_double (struct GNUNET_TIME_Relative rel,
505 double factor)
506{
507 struct GNUNET_TIME_Relative out;
508 double m;
509
510 GNUNET_assert (0 <= factor);
511
512 if (0 == factor)
513 return GNUNET_TIME_UNIT_ZERO;
514 if (GNUNET_TIME_relative_is_forever (rel))
515 return GNUNET_TIME_UNIT_FOREVER_REL;
516
517 m = ((double) rel.rel_value_us) * factor;
518
519 if (m >= (double) (GNUNET_TIME_UNIT_FOREVER_REL).rel_value_us)
520 {
521 GNUNET_break (0);
522 return GNUNET_TIME_UNIT_FOREVER_REL;
523 }
524
525 out.rel_value_us = (uint64_t) m;
526 return out;
527}
528
529
530struct GNUNET_TIME_Relative
531GNUNET_TIME_relative_saturating_multiply (struct GNUNET_TIME_Relative rel,
532 unsigned long long factor)
533{
534 struct GNUNET_TIME_Relative ret;
535
536 if (0 == factor)
537 return GNUNET_TIME_UNIT_ZERO;
538 if (GNUNET_TIME_relative_is_forever (rel))
539 return GNUNET_TIME_UNIT_FOREVER_REL;
540 ret.rel_value_us = rel.rel_value_us * factor;
541 if (ret.rel_value_us / factor != rel.rel_value_us)
542 {
543 return GNUNET_TIME_UNIT_FOREVER_REL;
544 }
545 return ret;
546}
547
548
549struct GNUNET_TIME_Relative
550GNUNET_TIME_relative_divide (struct GNUNET_TIME_Relative rel,
551 unsigned long long factor)
552{
553 struct GNUNET_TIME_Relative ret;
554
555 if ((0 == factor) ||
556 (GNUNET_TIME_relative_is_forever (rel)))
557 return GNUNET_TIME_UNIT_FOREVER_REL;
558 ret.rel_value_us = rel.rel_value_us / factor;
559 return ret;
560}
561
562
563struct GNUNET_TIME_Relative
564GNUNET_TIME_calculate_eta (struct GNUNET_TIME_Absolute start,
565 uint64_t finished,
566 uint64_t total)
567{
568 struct GNUNET_TIME_Relative due;
569 double exp;
570 struct GNUNET_TIME_Relative ret;
571
572 GNUNET_break (finished <= total);
573 if (finished >= total)
574 return GNUNET_TIME_UNIT_ZERO;
575 if (0 == finished)
576 return GNUNET_TIME_UNIT_FOREVER_REL;
577 due = GNUNET_TIME_absolute_get_duration (start);
578 exp = ((double) due.rel_value_us) * ((double) total) / ((double) finished);
579 ret.rel_value_us = ((uint64_t) exp) - due.rel_value_us;
580 return ret;
581}
582
583
584struct GNUNET_TIME_Relative
585GNUNET_TIME_relative_add (struct GNUNET_TIME_Relative a1,
586 struct GNUNET_TIME_Relative a2)
587{
588 struct GNUNET_TIME_Relative ret;
589
590 if ((a1.rel_value_us == UINT64_MAX) || (a2.rel_value_us == UINT64_MAX))
591 return GNUNET_TIME_UNIT_FOREVER_REL;
592 if (a1.rel_value_us + a2.rel_value_us < a1.rel_value_us)
593 {
594 GNUNET_break (0);
595 return GNUNET_TIME_UNIT_FOREVER_REL;
596 }
597 ret.rel_value_us = a1.rel_value_us + a2.rel_value_us;
598 return ret;
599}
600
601
602struct GNUNET_TIME_Relative
603GNUNET_TIME_relative_subtract (struct GNUNET_TIME_Relative a1,
604 struct GNUNET_TIME_Relative a2)
605{
606 struct GNUNET_TIME_Relative ret;
607
608 if (a2.rel_value_us >= a1.rel_value_us)
609 return GNUNET_TIME_UNIT_ZERO;
610 if (a1.rel_value_us == UINT64_MAX)
611 return GNUNET_TIME_UNIT_FOREVER_REL;
612 ret.rel_value_us = a1.rel_value_us - a2.rel_value_us;
613 return ret;
614}
615
616
617struct GNUNET_TIME_RelativeNBO
618GNUNET_TIME_relative_hton (struct GNUNET_TIME_Relative a)
619{
620 struct GNUNET_TIME_RelativeNBO ret;
621
622 ret.rel_value_us__ = GNUNET_htonll (a.rel_value_us);
623 return ret;
624}
625
626
627struct GNUNET_TIME_Relative
628GNUNET_TIME_relative_ntoh (struct GNUNET_TIME_RelativeNBO a)
629{
630 struct GNUNET_TIME_Relative ret;
631
632 ret.rel_value_us = GNUNET_ntohll (a.rel_value_us__);
633 return ret;
634}
635
636
637struct GNUNET_TIME_AbsoluteNBO
638GNUNET_TIME_absolute_hton (struct GNUNET_TIME_Absolute a)
639{
640 struct GNUNET_TIME_AbsoluteNBO ret;
641
642 ret.abs_value_us__ = GNUNET_htonll (a.abs_value_us);
643 return ret;
644}
645
646
647bool
648GNUNET_TIME_absolute_is_never (struct GNUNET_TIME_Absolute abs)
649{
650 return GNUNET_TIME_UNIT_FOREVER_ABS.abs_value_us == abs.abs_value_us;
651}
652
653
654bool
655GNUNET_TIME_relative_is_forever (struct GNUNET_TIME_Relative rel)
656{
657 return GNUNET_TIME_UNIT_FOREVER_REL.rel_value_us == rel.rel_value_us;
658}
659
660
661bool
662GNUNET_TIME_relative_is_zero (struct GNUNET_TIME_Relative rel)
663{
664 return 0 == rel.rel_value_us;
665}
666
667
668bool
669GNUNET_TIME_absolute_is_past (struct GNUNET_TIME_Absolute abs)
670{
671 struct GNUNET_TIME_Absolute now;
672
673 now = GNUNET_TIME_absolute_get ();
674 return abs.abs_value_us < now.abs_value_us;
675}
676
677
678bool
679GNUNET_TIME_absolute_is_future (struct GNUNET_TIME_Absolute abs)
680{
681 struct GNUNET_TIME_Absolute now;
682
683 now = GNUNET_TIME_absolute_get ();
684 return abs.abs_value_us > now.abs_value_us;
685}
686
687
688struct GNUNET_TIME_Absolute
689GNUNET_TIME_absolute_from_ms (uint64_t ms_after_epoch)
690{
691 struct GNUNET_TIME_Absolute ret;
692
693 ret.abs_value_us = GNUNET_TIME_UNIT_MILLISECONDS.rel_value_us
694 * ms_after_epoch;
695 if (ret.abs_value_us / GNUNET_TIME_UNIT_MILLISECONDS.rel_value_us !=
696 ms_after_epoch)
697 ret = GNUNET_TIME_UNIT_FOREVER_ABS;
698 return ret;
699}
700
701
702struct GNUNET_TIME_Absolute
703GNUNET_TIME_absolute_from_s (uint64_t s_after_epoch)
704{
705 struct GNUNET_TIME_Absolute ret;
706
707 ret.abs_value_us = GNUNET_TIME_UNIT_SECONDS.rel_value_us * s_after_epoch;
708 if (ret.abs_value_us / GNUNET_TIME_UNIT_SECONDS.rel_value_us !=
709 s_after_epoch)
710 ret = GNUNET_TIME_UNIT_FOREVER_ABS;
711 return ret;
712}
713
714
715struct GNUNET_TIME_Timestamp
716GNUNET_TIME_timestamp_from_s (uint64_t s_after_epoch)
717{
718 struct GNUNET_TIME_Timestamp ret;
719
720 ret.abs_time.abs_value_us
721 = GNUNET_TIME_UNIT_SECONDS.rel_value_us * s_after_epoch;
722 if (ret.abs_time.abs_value_us / GNUNET_TIME_UNIT_SECONDS.rel_value_us
723 != s_after_epoch)
724 ret = GNUNET_TIME_UNIT_FOREVER_TS;
725 return ret;
726}
727
728
729uint64_t
730GNUNET_TIME_timestamp_to_s (struct GNUNET_TIME_Timestamp ts)
731{
732 return ts.abs_time.abs_value_us / GNUNET_TIME_UNIT_SECONDS.rel_value_us;
733}
734
735
736struct GNUNET_TIME_Absolute
737GNUNET_TIME_absolute_ntoh (struct GNUNET_TIME_AbsoluteNBO a)
738{
739 struct GNUNET_TIME_Absolute ret;
740
741 ret.abs_value_us = GNUNET_ntohll (a.abs_value_us__);
742 return ret;
743}
744
745
746unsigned int
747GNUNET_TIME_get_current_year ()
748{
749 time_t tp;
750 struct tm *t;
751
752 tp = time (NULL);
753 t = gmtime (&tp);
754 if (t == NULL)
755 return 0;
756 return t->tm_year + 1900;
757}
758
759
760unsigned int
761GNUNET_TIME_time_to_year (struct GNUNET_TIME_Absolute at)
762{
763 struct tm *t;
764 time_t tp;
765
766 tp = at.abs_value_us / 1000LL / 1000LL; /* microseconds to seconds */
767 t = gmtime (&tp);
768 if (t == NULL)
769 return 0;
770 return t->tm_year + 1900;
771}
772
773
774#ifndef HAVE_TIMEGM
775/**
776 * As suggested in the timegm() man page.
777 */
778static time_t
779my_timegm (struct tm *tm)
780{
781 time_t ret;
782 char *tz;
783
784 tz = getenv ("TZ");
785 setenv ("TZ", "", 1);
786 tzset ();
787 ret = mktime (tm);
788 if (tz)
789 setenv ("TZ", tz, 1);
790 else
791 unsetenv ("TZ");
792 tzset ();
793 return ret;
794}
795
796
797#endif
798
799
800struct GNUNET_TIME_Absolute
801GNUNET_TIME_year_to_time (unsigned int year)
802{
803 struct GNUNET_TIME_Absolute ret;
804 time_t tp;
805 struct tm t;
806
807 memset (&t, 0, sizeof(t));
808 if (year < 1900)
809 {
810 GNUNET_break (0);
811 return GNUNET_TIME_absolute_get (); /* now */
812 }
813 t.tm_year = year - 1900;
814 t.tm_mday = 1;
815 t.tm_mon = 0;
816 t.tm_wday = 1;
817 t.tm_yday = 1;
818#ifndef HAVE_TIMEGM
819 tp = my_timegm (&t);
820#else
821 tp = timegm (&t);
822#endif
823 GNUNET_break (tp != (time_t) -1);
824 ret.abs_value_us = tp * 1000LL * 1000LL; /* seconds to microseconds */
825 return ret;
826}
827
828
829struct GNUNET_TIME_Relative
830GNUNET_TIME_randomized_backoff (struct GNUNET_TIME_Relative rt,
831 struct GNUNET_TIME_Relative threshold)
832{
833 double r = (rand () % 500) / 1000.0;
834 struct GNUNET_TIME_Relative t;
835
836 t = GNUNET_TIME_relative_multiply_double (
837 GNUNET_TIME_relative_max (GNUNET_TIME_UNIT_MILLISECONDS, rt),
838 2 + r);
839 return GNUNET_TIME_relative_min (threshold, t);
840}
841
842
843bool
844GNUNET_TIME_absolute_is_zero (struct GNUNET_TIME_Absolute abs)
845{
846 return 0 == abs.abs_value_us;
847}
848
849
850struct GNUNET_TIME_Relative
851GNUNET_TIME_randomize (struct GNUNET_TIME_Relative r)
852{
853 double d = ((rand () % 1001) + 500) / 1000.0;
854
855 return GNUNET_TIME_relative_multiply_double (r, d);
856}
857
858
859struct GNUNET_TIME_Absolute
860GNUNET_TIME_absolute_get_monotonic (
861 const struct GNUNET_CONFIGURATION_Handle *cfg)
862{
863 static const struct GNUNET_CONFIGURATION_Handle *last_cfg;
864 static struct GNUNET_TIME_Absolute last_time;
865 static struct GNUNET_DISK_MapHandle *map_handle;
866 static ATOMIC volatile uint64_t *map;
867 struct GNUNET_TIME_Absolute now;
868
869 now = GNUNET_TIME_absolute_get ();
870 if (last_cfg != cfg)
871 {
872 char *filename;
873
874 if (NULL != map_handle)
875 {
876 GNUNET_DISK_file_unmap (map_handle);
877 map_handle = NULL;
878 }
879 map = NULL;
880
881 last_cfg = cfg;
882 if ((NULL != cfg) &&
883 (GNUNET_OK ==
884 GNUNET_CONFIGURATION_get_value_filename (cfg,
885 "util",
886 "MONOTONIC_TIME_FILENAME",
887 &filename)))
888 {
889 struct GNUNET_DISK_FileHandle *fh;
890
891 fh = GNUNET_DISK_file_open (filename,
892 GNUNET_DISK_OPEN_READWRITE
893 | GNUNET_DISK_OPEN_CREATE,
894 GNUNET_DISK_PERM_USER_WRITE
895 | GNUNET_DISK_PERM_GROUP_WRITE
896 | GNUNET_DISK_PERM_USER_READ
897 | GNUNET_DISK_PERM_GROUP_READ);
898 if (NULL == fh)
899 {
900 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
901 _ ("Failed to map `%s', cannot assure monotonic time!\n"),
902 filename);
903 }
904 else
905 {
906 off_t size;
907
908 size = 0;
909 GNUNET_break (GNUNET_OK == GNUNET_DISK_file_handle_size (fh, &size));
910 if (size < (off_t) sizeof(*map))
911 {
912 struct GNUNET_TIME_AbsoluteNBO o;
913
914 o = GNUNET_TIME_absolute_hton (now);
915 if (sizeof(o) != GNUNET_DISK_file_write (fh, &o, sizeof(o)))
916 size = 0;
917 else
918 size = sizeof(o);
919 }
920 if (size == sizeof(*map))
921 {
922 map = GNUNET_DISK_file_map (fh,
923 &map_handle,
924 GNUNET_DISK_MAP_TYPE_READWRITE,
925 sizeof(*map));
926 if (NULL == map)
927 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
928 _ (
929 "Failed to map `%s', cannot assure monotonic time!\n"),
930 filename);
931 }
932 else
933 {
934 GNUNET_log (
935 GNUNET_ERROR_TYPE_WARNING,
936 _ (
937 "Failed to setup monotonic time file `%s', cannot assure monotonic time!\n"),
938 filename);
939 }
940 }
941 GNUNET_DISK_file_close (fh);
942 GNUNET_free (filename);
943 }
944 }
945 if (NULL != map)
946 {
947 struct GNUNET_TIME_AbsoluteNBO mt;
948
949#if __STDC_NO_ATOMICS__
950#if __GNUC__
951 mt.abs_value_us__ = __sync_fetch_and_or (map, 0);
952#else
953 mt.abs_value_us__ = *map; /* godspeed, pray this is atomic */
954#endif
955#else
956 mt.abs_value_us__ = atomic_load (map);
957#endif
958 last_time =
959 GNUNET_TIME_absolute_max (GNUNET_TIME_absolute_ntoh (mt), last_time);
960 }
961 if (now.abs_value_us <= last_time.abs_value_us)
962 now.abs_value_us = last_time.abs_value_us + 1;
963 last_time = now;
964 if (NULL != map)
965 {
966 uint64_t val = GNUNET_TIME_absolute_hton (now).abs_value_us__;
967#if __STDC_NO_ATOMICS__
968#if __GNUC__
969 (void) __sync_lock_test_and_set (map, val);
970#else
971 *map = val; /* godspeed, pray this is atomic */
972#endif
973#else
974 atomic_store (map, val);
975#endif
976 }
977 return now;
978}
979
980
981/**
982 * Destructor
983 */
984void __attribute__ ((destructor))
985GNUNET_util_time_fini ()
986{
987 (void) GNUNET_TIME_absolute_get_monotonic (NULL);
988}
989
990
991/* end of time.c */
diff --git a/src/lib/util/tun.c b/src/lib/util/tun.c
new file mode 100644
index 000000000..c4ac6ff88
--- /dev/null
+++ b/src/lib/util/tun.c
@@ -0,0 +1,281 @@
1/*
2 This file is part of GNUnet.
3 Copyright (C) 2010, 2011, 2012 Christian Grothoff
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 tun/tun.c
23 * @brief standard IP calculations for TUN interaction
24 * @author Philipp Toelke
25 * @author Christian Grothoff
26 */
27
28#include "platform.h"
29#include "gnunet_util_lib.h"
30
31/**
32 * IP TTL we use for packets that we assemble (8 bit unsigned integer)
33 */
34#define FRESH_TTL 64
35
36
37/**
38 * Initialize an IPv4 header.
39 *
40 * @param ip header to initialize
41 * @param protocol protocol to use (e.g. IPPROTO_UDP)
42 * @param payload_length number of bytes of payload that follow (excluding IPv4 header)
43 * @param src source IP address to use
44 * @param dst destination IP address to use
45 */
46void
47GNUNET_TUN_initialize_ipv4_header (struct GNUNET_TUN_IPv4Header *ip,
48 uint8_t protocol,
49 uint16_t payload_length,
50 const struct in_addr *src,
51 const struct in_addr *dst)
52{
53 GNUNET_assert (20 == sizeof(struct GNUNET_TUN_IPv4Header));
54 GNUNET_assert (payload_length <=
55 UINT16_MAX - sizeof(struct GNUNET_TUN_IPv4Header));
56 memset (ip, 0, sizeof(struct GNUNET_TUN_IPv4Header));
57 ip->header_length = sizeof(struct GNUNET_TUN_IPv4Header) / 4;
58 ip->version = 4;
59 ip->total_length =
60 htons (sizeof(struct GNUNET_TUN_IPv4Header) + payload_length);
61 ip->identification =
62 (uint16_t) GNUNET_CRYPTO_random_u32 (GNUNET_CRYPTO_QUALITY_WEAK, 65536);
63 ip->ttl = FRESH_TTL;
64 ip->protocol = protocol;
65 ip->source_address = *src;
66 ip->destination_address = *dst;
67 ip->checksum =
68 GNUNET_CRYPTO_crc16_n (ip, sizeof(struct GNUNET_TUN_IPv4Header));
69}
70
71
72/**
73 * Initialize an IPv6 header.
74 *
75 * @param ip header to initialize
76 * @param protocol protocol to use (e.g. IPPROTO_UDP), technically "next_header" for IPv6
77 * @param payload_length number of bytes of payload that follow (excluding IPv6 header)
78 * @param src source IP address to use
79 * @param dst destination IP address to use
80 */
81void
82GNUNET_TUN_initialize_ipv6_header (struct GNUNET_TUN_IPv6Header *ip,
83 uint8_t protocol,
84 uint16_t payload_length,
85 const struct in6_addr *src,
86 const struct in6_addr *dst)
87{
88 GNUNET_assert (40 == sizeof(struct GNUNET_TUN_IPv6Header));
89 GNUNET_assert (payload_length <=
90 UINT16_MAX - sizeof(struct GNUNET_TUN_IPv6Header));
91 memset (ip, 0, sizeof(struct GNUNET_TUN_IPv6Header));
92 ip->version = 6;
93 ip->next_header = protocol;
94 ip->payload_length = htons ((uint16_t) payload_length);
95 ip->hop_limit = FRESH_TTL;
96 ip->destination_address = *dst;
97 ip->source_address = *src;
98}
99
100
101void
102GNUNET_TUN_calculate_tcp4_checksum (const struct GNUNET_TUN_IPv4Header *ip,
103 struct GNUNET_TUN_TcpHeader *tcp,
104 const void *payload,
105 uint16_t payload_length)
106{
107 uint32_t sum;
108 uint16_t tmp;
109
110 GNUNET_assert (20 == sizeof(struct GNUNET_TUN_TcpHeader));
111 GNUNET_assert (payload_length + sizeof(struct GNUNET_TUN_IPv4Header)
112 + sizeof(struct GNUNET_TUN_TcpHeader) ==
113 ntohs (ip->total_length));
114 GNUNET_assert (IPPROTO_TCP == ip->protocol);
115
116 tcp->crc = 0;
117 sum = GNUNET_CRYPTO_crc16_step (0,
118 &ip->source_address,
119 sizeof(struct in_addr) * 2);
120 tmp = htons (IPPROTO_TCP);
121 sum = GNUNET_CRYPTO_crc16_step (sum, &tmp, sizeof(uint16_t));
122 tmp = htons (payload_length + sizeof(struct GNUNET_TUN_TcpHeader));
123 sum = GNUNET_CRYPTO_crc16_step (sum, &tmp, sizeof(uint16_t));
124 sum =
125 GNUNET_CRYPTO_crc16_step (sum, tcp, sizeof(struct GNUNET_TUN_TcpHeader));
126 sum = GNUNET_CRYPTO_crc16_step (sum, payload, payload_length);
127 tcp->crc = GNUNET_CRYPTO_crc16_finish (sum);
128}
129
130
131void
132GNUNET_TUN_calculate_tcp6_checksum (const struct GNUNET_TUN_IPv6Header *ip,
133 struct GNUNET_TUN_TcpHeader *tcp,
134 const void *payload,
135 uint16_t payload_length)
136{
137 uint32_t sum;
138 uint32_t tmp;
139
140 GNUNET_assert (20 == sizeof(struct GNUNET_TUN_TcpHeader));
141 GNUNET_assert (payload_length + sizeof(struct GNUNET_TUN_TcpHeader) ==
142 ntohs (ip->payload_length));
143 GNUNET_assert (IPPROTO_TCP == ip->next_header);
144 tcp->crc = 0;
145 sum = GNUNET_CRYPTO_crc16_step (0,
146 &ip->source_address,
147 2 * sizeof(struct in6_addr));
148 tmp = htonl (sizeof(struct GNUNET_TUN_TcpHeader) + payload_length);
149 sum = GNUNET_CRYPTO_crc16_step (sum, &tmp, sizeof(uint32_t));
150 tmp = htonl (IPPROTO_TCP);
151 sum = GNUNET_CRYPTO_crc16_step (sum, &tmp, sizeof(uint32_t));
152 sum =
153 GNUNET_CRYPTO_crc16_step (sum, tcp, sizeof(struct GNUNET_TUN_TcpHeader));
154 sum = GNUNET_CRYPTO_crc16_step (sum, payload, payload_length);
155 tcp->crc = GNUNET_CRYPTO_crc16_finish (sum);
156}
157
158
159void
160GNUNET_TUN_calculate_udp4_checksum (const struct GNUNET_TUN_IPv4Header *ip,
161 struct GNUNET_TUN_UdpHeader *udp,
162 const void *payload,
163 uint16_t payload_length)
164{
165 uint32_t sum;
166 uint16_t tmp;
167
168 GNUNET_assert (8 == sizeof(struct GNUNET_TUN_UdpHeader));
169 GNUNET_assert (payload_length + sizeof(struct GNUNET_TUN_IPv4Header)
170 + sizeof(struct GNUNET_TUN_UdpHeader) ==
171 ntohs (ip->total_length));
172 GNUNET_assert (IPPROTO_UDP == ip->protocol);
173
174 udp->crc =
175 0; /* technically optional, but we calculate it anyway, just to be sure */
176 sum = GNUNET_CRYPTO_crc16_step (0,
177 &ip->source_address,
178 sizeof(struct in_addr) * 2);
179 tmp = htons (IPPROTO_UDP);
180 sum = GNUNET_CRYPTO_crc16_step (sum, &tmp, sizeof(uint16_t));
181 tmp = htons (sizeof(struct GNUNET_TUN_UdpHeader) + payload_length);
182 sum = GNUNET_CRYPTO_crc16_step (sum, &tmp, sizeof(uint16_t));
183 sum =
184 GNUNET_CRYPTO_crc16_step (sum, udp, sizeof(struct GNUNET_TUN_UdpHeader));
185 sum = GNUNET_CRYPTO_crc16_step (sum, payload, payload_length);
186 udp->crc = GNUNET_CRYPTO_crc16_finish (sum);
187}
188
189
190void
191GNUNET_TUN_calculate_udp6_checksum (const struct GNUNET_TUN_IPv6Header *ip,
192 struct GNUNET_TUN_UdpHeader *udp,
193 const void *payload,
194 uint16_t payload_length)
195{
196 uint32_t sum;
197 uint32_t tmp;
198
199 GNUNET_assert (payload_length + sizeof(struct GNUNET_TUN_UdpHeader) ==
200 ntohs (ip->payload_length));
201 GNUNET_assert (payload_length + sizeof(struct GNUNET_TUN_UdpHeader) ==
202 ntohs (udp->len));
203 GNUNET_assert (IPPROTO_UDP == ip->next_header);
204
205 udp->crc = 0;
206 sum = GNUNET_CRYPTO_crc16_step (0,
207 &ip->source_address,
208 sizeof(struct in6_addr) * 2);
209 tmp = htons (sizeof(struct GNUNET_TUN_UdpHeader)
210 + payload_length); /* aka udp->len */
211 sum = GNUNET_CRYPTO_crc16_step (sum, &tmp, sizeof(uint32_t));
212 tmp = htons (ip->next_header);
213 sum = GNUNET_CRYPTO_crc16_step (sum, &tmp, sizeof(uint32_t));
214 sum =
215 GNUNET_CRYPTO_crc16_step (sum, udp, sizeof(struct GNUNET_TUN_UdpHeader));
216 sum = GNUNET_CRYPTO_crc16_step (sum, payload, payload_length);
217 udp->crc = GNUNET_CRYPTO_crc16_finish (sum);
218}
219
220
221void
222GNUNET_TUN_calculate_icmp_checksum (struct GNUNET_TUN_IcmpHeader *icmp,
223 const void *payload,
224 uint16_t payload_length)
225{
226 uint32_t sum;
227
228 GNUNET_assert (8 == sizeof(struct GNUNET_TUN_IcmpHeader));
229 icmp->crc = 0;
230 sum =
231 GNUNET_CRYPTO_crc16_step (0, icmp, sizeof(struct GNUNET_TUN_IcmpHeader));
232 sum = GNUNET_CRYPTO_crc16_step (sum, payload, payload_length);
233 icmp->crc = GNUNET_CRYPTO_crc16_finish (sum);
234}
235
236
237/**
238 * Check if two sockaddrs are equal.
239 *
240 * @param sa one address
241 * @param sb another address
242 * @param include_port also check ports
243 * @return #GNUNET_YES if they are equal
244 */
245int
246GNUNET_TUN_sockaddr_cmp (const struct sockaddr *sa,
247 const struct sockaddr *sb,
248 int include_port)
249{
250 if (sa->sa_family != sb->sa_family)
251 return GNUNET_NO;
252
253 switch (sa->sa_family)
254 {
255 case AF_INET: {
256 const struct sockaddr_in *sa4 = (const struct sockaddr_in *) sa;
257 const struct sockaddr_in *sb4 = (const struct sockaddr_in *) sb;
258 if ((include_port) && (sa4->sin_port != sb4->sin_port))
259 return GNUNET_NO;
260 return(sa4->sin_addr.s_addr == sb4->sin_addr.s_addr);
261 }
262
263 case AF_INET6: {
264 const struct sockaddr_in6 *sa6 = (const struct sockaddr_in6 *) sa;
265 const struct sockaddr_in6 *sb6 = (const struct sockaddr_in6 *) sb;
266
267 if ((include_port) && (sa6->sin6_port != sb6->sin6_port))
268 return GNUNET_NO;
269 return(
270 0 == memcmp (&sa6->sin6_addr, &sb6->sin6_addr, sizeof(struct
271 in6_addr)));
272 }
273
274 default:
275 GNUNET_break (0);
276 return GNUNET_SYSERR;
277 }
278}
279
280
281/* end of tun.c */
diff --git a/src/lib/util/uri.c b/src/lib/util/uri.c
new file mode 100644
index 000000000..b09968581
--- /dev/null
+++ b/src/lib/util/uri.c
@@ -0,0 +1,345 @@
1/**
2 * Copyright (C) 2016,2017 Jack Engqvist Johansson
3 *
4 * Permission is hereby granted, free of charge, to any person obtaining a copy
5 * of this software and associated documentation files (the "Software"), to deal
6 * in the Software without restriction, including without limitation the rights
7 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
8 * copies of the Software, and to permit persons to whom the Software is
9 * furnished to do so, subject to the following conditions:
10 *
11 * The above copyright notice and this permission notice shall be included in all
12 * copies or substantial portions of the Software.
13 *
14 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
19 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
20 * SOFTWARE.
21 */
22#include "platform.h"
23#include <stdlib.h>
24#include <stdio.h>
25#include <string.h>
26#include "gnunet_uri_lib.h"
27
28
29/**
30 * Parse a non null terminated string into an integer.
31 *
32 * str: the string containing the number.
33 * len: Number of characters to parse.
34 */
35static inline int
36natoi (const char *str,
37 size_t len)
38{
39 int i, r = 0;
40 for (i = 0; i < len; i++) {
41 r *= 10;
42 r += str[i] - '0';
43 }
44
45 return r;
46}
47
48
49/**
50 * Check if a URL is relative (no scheme and hostname).
51 *
52 * url: the string containing the URL to check.
53 *
54 * Returns 1 if relative, otherwise 0.
55 */
56static inline int
57is_relative (const char *url)
58{
59 return (*url == '/') ? 1 : 0;
60}
61
62
63/**
64 * Parse the scheme of a URL by inserting a null terminator after the scheme.
65 *
66 * str: the string containing the URL to parse. Will be modified.
67 *
68 * Returns a pointer to the hostname on success, otherwise NULL.
69 */
70static inline char *
71parse_scheme (char *str)
72{
73 char *s;
74
75 /* If not found or first in string, return error */
76 s = strchr (str, ':');
77 if (s == NULL || s == str) {
78 return NULL;
79 }
80
81 /* If not followed by two slashes, return error */
82 if (s[1] == '\0' || s[1] != '/' || s[2] == '\0' || s[2] != '/') {
83 return NULL;
84 }
85
86 *s = '\0'; // Replace ':' with NULL
87
88 return s + 3;
89}
90
91
92/**
93 * Find a character in a string, replace it with '\0' and return the next
94 * character in the string.
95 *
96 * str: the string to search in.
97 * find: the character to search for.
98 *
99 * Returns a pointer to the character after the one to search for. If not
100 * found, NULL is returned.
101 */
102static inline char *
103find_and_terminate (char *str,
104 char find)
105{
106 str = strchr(str, find);
107 if (NULL == str) {
108 return NULL;
109 }
110
111 *str = '\0';
112 return str + 1;
113}
114
115
116/* Yes, the following functions could be implemented as preprocessor macros
117 instead of inline functions, but I think that this approach will be more
118 clean in this case. */
119static inline char *
120find_fragment (char *str)
121{
122 return find_and_terminate (str, '#');
123}
124
125
126static inline char *
127find_query (char *str)
128{
129 return find_and_terminate (str, '?');
130}
131
132
133static inline char *
134find_path (char *str)
135{
136 return find_and_terminate (str, '/');
137}
138
139
140/**
141 * Parse a URL to a struct.
142 *
143 * The URL string should be in one of the following formats:
144 *
145 * Absolute URL:
146 * scheme ":" [ "//" ] [ username ":" password "@" ] host [ ":" port ] [ "/" ] [ path ] [ "?" query ] [ "#" fragment ]
147 *
148 * Relative URL:
149 * path [ "?" query ] [ "#" fragment ]
150 *
151 * The following parts will be parsed to the corresponding struct member.
152 *
153 * *url: a pointer to the struct where to store the parsed values.
154 * *url_str: a pointer to the url to be parsed (null terminated). The string
155 * will be modified.
156 *
157 * Returns 0 on success, otherwise -1.
158 */
159int
160GNUNET_uri_parse (struct GNUNET_Uri *url,
161 char *u)
162{
163 if (NULL == url || NULL == u) {
164 return -1;
165 }
166
167 memset(url, 0, sizeof (struct GNUNET_Uri));
168
169 /* (Fragment) */
170 url->fragment = find_fragment (u);
171
172 /* (Query) */
173 url->query = find_query (u);
174
175 /* Relative URL? Parse scheme and hostname */
176 if (!is_relative (u)) {
177 /* Scheme */
178 url->scheme = u;
179 u = parse_scheme (u);
180 if (u == NULL) {
181 return -1;
182 }
183
184 /* Host */
185 if ('\0' == *u) {
186 return -1;
187 }
188 url->host = u;
189
190 /* (Path) */
191 url->path = find_path (u);
192
193 /* (Credentials) */
194 u = strchr (url->host, '@');
195 if (NULL != u) {
196 /* Missing credentials? */
197 if (u == url->host) {
198 return -1;
199 }
200
201 url->username = url->host;
202 url->host = u + 1;
203 *u = '\0';
204
205 u = strchr (url->username, ':');
206 if (NULL == u) {
207 return -1;
208 }
209
210 url->password = u + 1;
211 *u = '\0';
212 }
213
214 /* Missing hostname? */
215 if ('\0' == *url->host) {
216 return -1;
217 }
218
219 /* (Port) */
220 u = strchr (url->host, ':');
221 if (NULL != u && (NULL == url->path || u < url->path)) {
222 *(u++) = '\0';
223 if ('\0' == *u) {
224 return -1;
225 }
226
227 if (url->path) {
228 url->port = natoi (u, url->path - u - 1);
229 } else {
230 url->port = atoi (u);
231 }
232 }
233
234 /* Missing hostname? */
235 if ('\0' == *url->host) {
236 return -1;
237 }
238 } else {
239 /* (Path) */
240 url->path = find_path (u);
241 }
242
243 return 0;
244}
245
246
247/**
248 * Split a path into several strings.
249 *
250 * No data is copied, the slashed are used as null terminators and then
251 * pointers to each path part will be stored in **parts. Double slashes will be
252 * treated as one.
253 *
254 * *path: the path to split. The string will be modified.
255 * **parts: a pointer to an array of (char *) where to store the result.
256 * max_parts: max number of parts to parse.
257 *
258 * Returns the number of parsed items. -1 on error.
259 */
260int
261GNUNET_uri_split_path (char *path,
262 char **parts,
263 int max_parts)
264{
265 int i = 0;
266
267 if (NULL == path || '\0' == *path) {
268 return -1;
269 }
270
271 do {
272 /* Forward to after slashes */
273 while (*path == '/') path++;
274
275 if ('\0' == *path) {
276 break;
277 }
278
279 parts[i++] = path;
280
281 path = strchr (path, '/');
282 if (NULL == path) {
283 break;
284 }
285
286 *(path++) = '\0';
287 } while (i < max_parts);
288
289 return i;
290}
291
292
293/**
294 * Parse a query string into a key/value struct.
295 *
296 * The query string should be a null terminated string of parameters separated by
297 * a delimiter. Each parameter are checked for the equal sign character. If it
298 * appears in the parameter, it will be used as a null terminator and the part
299 * that comes after it will be the value of the parameter.
300 *
301 * No data are copied, the equal sign and delimiters are used as null
302 * terminators and then pointers to each parameter key and value will be stored
303 * in the yuarel_param struct.
304 *
305 * *query: the query string to parse. The string will be modified.
306 * delimiter: the character that separates the key/value pairs from each other.
307 * *params: an array of (struct yuarel_param) where to store the result.
308 * max_values: max number of parameters to parse.
309 *
310 * Returns the number of parsed items. -1 on error.
311 */
312int
313GNUNET_uri_parse_query (char *query,
314 char delimiter,
315 struct GNUNET_UriParam *params,
316 int max_params)
317{
318 int i = 0;
319
320 if (NULL == query || '\0' == *query) {
321 return -1;
322 }
323
324 params[i++].key = query;
325 while (i < max_params && NULL != (query = strchr (query, delimiter))) {
326 *query = '\0';
327 params[i].key = ++query;
328 params[i].val = NULL;
329
330 /* Go back and split previous param */
331 if (i > 0) {
332 if ((params[i - 1].val = strchr (params[i - 1].key, '=')) != NULL) {
333 *(params[i - 1].val)++ = '\0';
334 }
335 }
336 i++;
337 }
338
339 /* Go back and split last param */
340 if ((params[i - 1].val = strchr (params[i - 1].key, '=')) != NULL) {
341 *(params[i - 1].val)++ = '\0';
342 }
343
344 return i;
345}
diff --git a/src/lib/util/util.conf b/src/lib/util/util.conf
new file mode 100644
index 000000000..4f0860a49
--- /dev/null
+++ b/src/lib/util/util.conf
@@ -0,0 +1,79 @@
1[PATHS]
2# The PATHS section is special, as filenames including $-expression are
3# expanded using the values from PATHS or the system environment (PATHS
4# is checked first). GNUnet also supports expanding $-expressions using
5# defaults with the syntax "${VAR:-default}". Here, "default" can again
6# be a $-expression.
7#
8# We usually want $HOME for $GNUNET_HOME, but we allow testcases to
9# easily override this by setting $GNUNET_TEST_HOME.
10#
11GNUNET_HOME = ${GNUNET_TEST_HOME:-${HOME:-${USERPROFILE}}}
12
13# see XDG Base Directory Specification at
14# http://standards.freedesktop.org/basedir-spec/basedir-spec-latest.html
15# for how these should be used.
16
17# Persistent data storage
18GNUNET_DATA_HOME = ${XDG_DATA_HOME:-$GNUNET_HOME/.local/share}/gnunet/
19
20# Configuration files
21GNUNET_CONFIG_HOME = ${XDG_CONFIG_HOME:-$GNUNET_HOME/.config}/gnunet/
22
23# Cached data, no big deal if lost
24GNUNET_CACHE_HOME = ${XDG_CACHE_HOME:-$GNUNET_HOME/.cache}/gnunet/
25
26# Runtime data (i.e UNIX domain sockets, locks, always lost on system boot)
27# This is the variable for system-wide services; use GNUNET_USER_RUNTIME_DIR
28# for per-user services (where RUN_PER_USER=YES is set)
29# Note that the 'gnunet'/system user must have $TMPDIR/$TMP set to
30# exactly the same values as 'normal' users, otherwise this will fail.
31# If $TMPDIR or $TMP are set to different directories for different
32# users, this option should be changed to point to the same directory
33# for all users (i.e. by simply using "/tmp/gnunet-system-runtime/").
34GNUNET_RUNTIME_DIR = ${TMPDIR:-${TMP:-/tmp}}/gnunet-system-runtime/
35
36# Runtime data for per-user services
37GNUNET_USER_RUNTIME_DIR = ${TMPDIR:-${TMP:-/tmp}}/gnunet-${USERHOME:-${USER:-user}}-runtime/
38
39# Directory to use for temporary files.
40GNUNET_TMP = ${TMPDIR:-${TMP:-/tmp}}/gnunet/
41
42
43# Override for GNUNET_HOME used by test cases.
44# GNUNET_TEST_HOME = /tmp/foo/bar
45
46# DEFAULTCONFIG = /etc/gnunet.conf
47# If 'DEFAULTCONFIG' is not defined, the current
48# configuration file is assumed to be the default,
49# which is what we want by default...
50
51# Location of binaries requiring setuid or setgid flags, e.g. gnunet-helper-vpn.
52# By default it is assumed to be in the libexec directory, but on some systems
53# like NixOS setuid / setgid is only possible through a wrapper in a specific
54# location.
55# SUID_BINARY_PATH =
56
57
58[PEER]
59# Where do we store our private key?
60PRIVATE_KEY = $GNUNET_DATA_HOME/private_key.ecc
61
62# What kind of system are we on? Choices are
63# INFRASTRUCTURE (always-on, grid, data center)
64# DESKTOP (sometimes-on, grid, office)
65# NOTEBOOK (sometimes-on, mobile, often limited network,
66# if on-battery than large battery)
67# MOBILE (sometimes-on, mobile, always limited network,
68# always battery limited)
69# UNKNOWN (not configured/specified/known)
70SYSTEM_TYPE = UNKNOWN
71
72[TESTING]
73SPEEDUP_INTERVAL = 0 ms
74SPEEDUP_DELTA = 0 ms
75# This following option is applicable to LINUX. Enabling this option causes all
76# UNIX domain sockets to be opened as abstract sockets. Note that the
77# filesystem level restrictions no longer apply for abstract sockets. An
78# end-user should not modify this option.
79USE_ABSTRACT_SOCKETS = NO
diff --git a/src/lib/util/util.supp b/src/lib/util/util.supp
new file mode 100644
index 000000000..f04775cac
--- /dev/null
+++ b/src/lib/util/util.supp
@@ -0,0 +1,11 @@
1{
2 cache-libexecdir-value
3 Memcheck:Leak
4 fun:malloc
5 fun:GNUNET_xmalloc_unchecked_
6 fun:GNUNET_xmalloc_
7 fun:GNUNET_asprintf
8 fun:GNUNET_OS_installation_get_path
9 fun:GNUNET_OS_get_libexec_binary_path
10 ...
11} \ No newline at end of file