diff options
53 files changed, 13134 insertions, 93 deletions
diff --git a/configure.ac b/configure.ac index 3d68ee9b0..c8e316416 100644 --- a/configure.ac +++ b/configure.ac | |||
@@ -1722,6 +1722,7 @@ src/testing/Makefile | |||
1722 | src/topology/Makefile | 1722 | src/topology/Makefile |
1723 | src/transport/Makefile | 1723 | src/transport/Makefile |
1724 | src/transport/transport.conf | 1724 | src/transport/transport.conf |
1725 | src/tun/Makefile | ||
1725 | src/util/Makefile | 1726 | src/util/Makefile |
1726 | src/util/resolver.conf | 1727 | src/util/resolver.conf |
1727 | src/vpn/Makefile | 1728 | src/vpn/Makefile |
diff --git a/po/POTFILES.in b/po/POTFILES.in index 38fa52508..de6bd90e4 100644 --- a/po/POTFILES.in +++ b/po/POTFILES.in | |||
@@ -4,13 +4,21 @@ src/arm/arm_monitor_api.c | |||
4 | src/arm/gnunet-arm.c | 4 | src/arm/gnunet-arm.c |
5 | src/arm/gnunet-service-arm.c | 5 | src/arm/gnunet-service-arm.c |
6 | src/arm/mockup-service.c | 6 | src/arm/mockup-service.c |
7 | src/ats-tests/ats-testing-experiment.c | ||
8 | src/ats-tests/ats-testing-log.c | ||
9 | src/ats-tests/ats-testing-preferences.c | ||
10 | src/ats-tests/ats-testing-traffic.c | ||
11 | src/ats-tests/ats-testing.c | ||
12 | src/ats-tests/gnunet-ats-sim.c | ||
13 | src/ats-tests/gnunet-solver-eval.c | ||
14 | src/ats-tool/gnunet-ats.c | ||
7 | src/ats/ats_api_connectivity.c | 15 | src/ats/ats_api_connectivity.c |
8 | src/ats/ats_api_performance.c | 16 | src/ats/ats_api_performance.c |
9 | src/ats/ats_api_scanner.c | 17 | src/ats/ats_api_scanner.c |
10 | src/ats/ats_api_scheduling.c | 18 | src/ats/ats_api_scheduling.c |
11 | src/ats/gnunet-ats-solver-eval.c | 19 | src/ats/gnunet-ats-solver-eval.c |
12 | src/ats/gnunet-service-ats_addresses.c | ||
13 | src/ats/gnunet-service-ats.c | 20 | src/ats/gnunet-service-ats.c |
21 | src/ats/gnunet-service-ats_addresses.c | ||
14 | src/ats/gnunet-service-ats_connectivity.c | 22 | src/ats/gnunet-service-ats_connectivity.c |
15 | src/ats/gnunet-service-ats_normalization.c | 23 | src/ats/gnunet-service-ats_normalization.c |
16 | src/ats/gnunet-service-ats_performance.c | 24 | src/ats/gnunet-service-ats_performance.c |
@@ -21,14 +29,6 @@ src/ats/gnunet-service-ats_scheduling.c | |||
21 | src/ats/plugin_ats_mlp.c | 29 | src/ats/plugin_ats_mlp.c |
22 | src/ats/plugin_ats_proportional.c | 30 | src/ats/plugin_ats_proportional.c |
23 | src/ats/plugin_ats_ril.c | 31 | src/ats/plugin_ats_ril.c |
24 | src/ats-tests/ats-testing.c | ||
25 | src/ats-tests/ats-testing-experiment.c | ||
26 | src/ats-tests/ats-testing-log.c | ||
27 | src/ats-tests/ats-testing-preferences.c | ||
28 | src/ats-tests/ats-testing-traffic.c | ||
29 | src/ats-tests/gnunet-ats-sim.c | ||
30 | src/ats-tests/gnunet-solver-eval.c | ||
31 | src/ats-tool/gnunet-ats.c | ||
32 | src/auction/gnunet-auction-create.c | 32 | src/auction/gnunet-auction-create.c |
33 | src/auction/gnunet-auction-info.c | 33 | src/auction/gnunet-auction-info.c |
34 | src/auction/gnunet-auction-join.c | 34 | src/auction/gnunet-auction-join.c |
@@ -40,8 +40,8 @@ src/block/plugin_block_test.c | |||
40 | src/cadet/cadet_api.c | 40 | src/cadet/cadet_api.c |
41 | src/cadet/cadet_test_lib.c | 41 | src/cadet/cadet_test_lib.c |
42 | src/cadet/desirability_table.c | 42 | src/cadet/desirability_table.c |
43 | src/cadet/gnunet-cadet.c | ||
44 | src/cadet/gnunet-cadet-profiler.c | 43 | src/cadet/gnunet-cadet-profiler.c |
44 | src/cadet/gnunet-cadet.c | ||
45 | src/cadet/gnunet-service-cadet.c | 45 | src/cadet/gnunet-service-cadet.c |
46 | src/cadet/gnunet-service-cadet_channel.c | 46 | src/cadet/gnunet-service-cadet_channel.c |
47 | src/cadet/gnunet-service-cadet_connection.c | 47 | src/cadet/gnunet-service-cadet_connection.c |
@@ -57,15 +57,15 @@ src/consensus/gnunet-service-consensus.c | |||
57 | src/consensus/plugin_block_consensus.c | 57 | src/consensus/plugin_block_consensus.c |
58 | src/conversation/conversation_api.c | 58 | src/conversation/conversation_api.c |
59 | src/conversation/conversation_api_call.c | 59 | src/conversation/conversation_api_call.c |
60 | src/conversation/gnunet-conversation.c | ||
61 | src/conversation/gnunet-conversation-test.c | 60 | src/conversation/gnunet-conversation-test.c |
62 | src/conversation/gnunet_gst.c | 61 | src/conversation/gnunet-conversation.c |
63 | src/conversation/gnunet_gst_test.c | ||
64 | src/conversation/gnunet-helper-audio-playback.c | ||
65 | src/conversation/gnunet-helper-audio-playback-gst.c | 62 | src/conversation/gnunet-helper-audio-playback-gst.c |
66 | src/conversation/gnunet-helper-audio-record.c | 63 | src/conversation/gnunet-helper-audio-playback.c |
67 | src/conversation/gnunet-helper-audio-record-gst.c | 64 | src/conversation/gnunet-helper-audio-record-gst.c |
65 | src/conversation/gnunet-helper-audio-record.c | ||
68 | src/conversation/gnunet-service-conversation.c | 66 | src/conversation/gnunet-service-conversation.c |
67 | src/conversation/gnunet_gst.c | ||
68 | src/conversation/gnunet_gst_test.c | ||
69 | src/conversation/microphone.c | 69 | src/conversation/microphone.c |
70 | src/conversation/plugin_gnsrecord_conversation.c | 70 | src/conversation/plugin_gnsrecord_conversation.c |
71 | src/conversation/speaker.c | 71 | src/conversation/speaker.c |
@@ -102,7 +102,6 @@ src/dht/dht_api.c | |||
102 | src/dht/dht_test_lib.c | 102 | src/dht/dht_test_lib.c |
103 | src/dht/gnunet-dht-get.c | 103 | src/dht/gnunet-dht-get.c |
104 | src/dht/gnunet-dht-monitor.c | 104 | src/dht/gnunet-dht-monitor.c |
105 | src/dht/gnunet_dht_profiler.c | ||
106 | src/dht/gnunet-dht-put.c | 105 | src/dht/gnunet-dht-put.c |
107 | src/dht/gnunet-service-dht.c | 106 | src/dht/gnunet-service-dht.c |
108 | src/dht/gnunet-service-dht_clients.c | 107 | src/dht/gnunet-service-dht_clients.c |
@@ -111,8 +110,11 @@ src/dht/gnunet-service-dht_hello.c | |||
111 | src/dht/gnunet-service-dht_neighbours.c | 110 | src/dht/gnunet-service-dht_neighbours.c |
112 | src/dht/gnunet-service-dht_nse.c | 111 | src/dht/gnunet-service-dht_nse.c |
113 | src/dht/gnunet-service-dht_routing.c | 112 | src/dht/gnunet-service-dht_routing.c |
113 | src/dht/gnunet_dht_profiler.c | ||
114 | src/dht/plugin_block_dht.c | 114 | src/dht/plugin_block_dht.c |
115 | src/dns/dns_api.c | 115 | src/dns/dns_api.c |
116 | src/dns/dnsparser.c | ||
117 | src/dns/dnsstub.c | ||
116 | src/dns/gnunet-dns-monitor.c | 118 | src/dns/gnunet-dns-monitor.c |
117 | src/dns/gnunet-dns-redirector.c | 119 | src/dns/gnunet-dns-redirector.c |
118 | src/dns/gnunet-helper-dns.c | 120 | src/dns/gnunet-helper-dns.c |
@@ -124,8 +126,8 @@ src/dv/gnunet-dv.c | |||
124 | src/dv/gnunet-service-dv.c | 126 | src/dv/gnunet-service-dv.c |
125 | src/dv/plugin_transport_dv.c | 127 | src/dv/plugin_transport_dv.c |
126 | src/exit/gnunet-daemon-exit.c | 128 | src/exit/gnunet-daemon-exit.c |
127 | src/exit/gnunet-helper-exit.c | ||
128 | src/exit/gnunet-helper-exit-windows.c | 129 | src/exit/gnunet-helper-exit-windows.c |
130 | src/exit/gnunet-helper-exit.c | ||
129 | src/fragmentation/defragmentation.c | 131 | src/fragmentation/defragmentation.c |
130 | src/fragmentation/fragmentation.c | 132 | src/fragmentation/fragmentation.c |
131 | src/fs/fs_api.c | 133 | src/fs/fs_api.c |
@@ -150,8 +152,8 @@ src/fs/gnunet-auto-share.c | |||
150 | src/fs/gnunet-daemon-fsprofiler.c | 152 | src/fs/gnunet-daemon-fsprofiler.c |
151 | src/fs/gnunet-directory.c | 153 | src/fs/gnunet-directory.c |
152 | src/fs/gnunet-download.c | 154 | src/fs/gnunet-download.c |
153 | src/fs/gnunet-fs.c | ||
154 | src/fs/gnunet-fs-profiler.c | 155 | src/fs/gnunet-fs-profiler.c |
156 | src/fs/gnunet-fs.c | ||
155 | src/fs/gnunet-helper-fs-publish.c | 157 | src/fs/gnunet-helper-fs-publish.c |
156 | src/fs/gnunet-publish.c | 158 | src/fs/gnunet-publish.c |
157 | src/fs/gnunet-search.c | 159 | src/fs/gnunet-search.c |
@@ -171,10 +173,10 @@ src/gns/gns_tld_api.c | |||
171 | src/gns/gnunet-bcd.c | 173 | src/gns/gnunet-bcd.c |
172 | src/gns/gnunet-dns2gns.c | 174 | src/gns/gnunet-dns2gns.c |
173 | src/gns/gnunet-gns-benchmark.c | 175 | src/gns/gnunet-gns-benchmark.c |
174 | src/gns/gnunet-gns.c | ||
175 | src/gns/gnunet-gns-helper-service-w32.c | 176 | src/gns/gnunet-gns-helper-service-w32.c |
176 | src/gns/gnunet-gns-import.c | 177 | src/gns/gnunet-gns-import.c |
177 | src/gns/gnunet-gns-proxy.c | 178 | src/gns/gnunet-gns-proxy.c |
179 | src/gns/gnunet-gns.c | ||
178 | src/gns/gnunet-service-gns.c | 180 | src/gns/gnunet-service-gns.c |
179 | src/gns/gnunet-service-gns_interceptor.c | 181 | src/gns/gnunet-service-gns_interceptor.c |
180 | src/gns/gnunet-service-gns_resolver.c | 182 | src/gns/gnunet-service-gns_resolver.c |
@@ -183,15 +185,15 @@ src/gns/nss/nss_gns_query.c | |||
183 | src/gns/plugin_block_gns.c | 185 | src/gns/plugin_block_gns.c |
184 | src/gns/plugin_gnsrecord_gns.c | 186 | src/gns/plugin_gnsrecord_gns.c |
185 | src/gns/plugin_rest_gns.c | 187 | src/gns/plugin_rest_gns.c |
188 | src/gns/w32nsp-install.c | ||
189 | src/gns/w32nsp-resolve.c | ||
190 | src/gns/w32nsp-uninstall.c | ||
191 | src/gns/w32nsp.c | ||
186 | src/gnsrecord/gnsrecord.c | 192 | src/gnsrecord/gnsrecord.c |
187 | src/gnsrecord/gnsrecord_crypto.c | 193 | src/gnsrecord/gnsrecord_crypto.c |
188 | src/gnsrecord/gnsrecord_misc.c | 194 | src/gnsrecord/gnsrecord_misc.c |
189 | src/gnsrecord/gnsrecord_serialization.c | 195 | src/gnsrecord/gnsrecord_serialization.c |
190 | src/gnsrecord/plugin_gnsrecord_dns.c | 196 | src/gnsrecord/plugin_gnsrecord_dns.c |
191 | src/gns/w32nsp.c | ||
192 | src/gns/w32nsp-install.c | ||
193 | src/gns/w32nsp-resolve.c | ||
194 | src/gns/w32nsp-uninstall.c | ||
195 | src/hello/address.c | 197 | src/hello/address.c |
196 | src/hello/gnunet-hello.c | 198 | src/hello/gnunet-hello.c |
197 | src/hello/hello.c | 199 | src/hello/hello.c |
@@ -200,11 +202,6 @@ src/hostlist/gnunet-daemon-hostlist_client.c | |||
200 | src/hostlist/gnunet-daemon-hostlist_server.c | 202 | src/hostlist/gnunet-daemon-hostlist_server.c |
201 | src/identity-attribute/identity_attribute.c | 203 | src/identity-attribute/identity_attribute.c |
202 | src/identity-attribute/plugin_identity_attribute_gnuid.c | 204 | src/identity-attribute/plugin_identity_attribute_gnuid.c |
203 | src/identity/gnunet-identity.c | ||
204 | src/identity/gnunet-service-identity.c | ||
205 | src/identity/identity_api.c | ||
206 | src/identity/identity_api_lookup.c | ||
207 | src/identity/plugin_rest_identity.c | ||
208 | src/identity-provider/gnunet-idp.c | 205 | src/identity-provider/gnunet-idp.c |
209 | src/identity-provider/gnunet-service-identity-provider.c | 206 | src/identity-provider/gnunet-service-identity-provider.c |
210 | src/identity-provider/identity_provider_api.c | 207 | src/identity-provider/identity_provider_api.c |
@@ -213,15 +210,20 @@ src/identity-provider/plugin_gnsrecord_identity_provider.c | |||
213 | src/identity-provider/plugin_identity_provider_sqlite.c | 210 | src/identity-provider/plugin_identity_provider_sqlite.c |
214 | src/identity-provider/plugin_rest_identity_provider.c | 211 | src/identity-provider/plugin_rest_identity_provider.c |
215 | src/identity-provider/plugin_rest_openid_connect.c | 212 | src/identity-provider/plugin_rest_openid_connect.c |
213 | src/identity/gnunet-identity.c | ||
214 | src/identity/gnunet-service-identity.c | ||
215 | src/identity/identity_api.c | ||
216 | src/identity/identity_api_lookup.c | ||
217 | src/identity/plugin_rest_identity.c | ||
218 | src/json/json.c | ||
219 | src/json/json_generator.c | ||
220 | src/json/json_helper.c | ||
221 | src/json/json_mhd.c | ||
216 | src/jsonapi/jsonapi.c | 222 | src/jsonapi/jsonapi.c |
217 | src/jsonapi/jsonapi_document.c | 223 | src/jsonapi/jsonapi_document.c |
218 | src/jsonapi/jsonapi_error.c | 224 | src/jsonapi/jsonapi_error.c |
219 | src/jsonapi/jsonapi_relationship.c | 225 | src/jsonapi/jsonapi_relationship.c |
220 | src/jsonapi/jsonapi_resource.c | 226 | src/jsonapi/jsonapi_resource.c |
221 | src/json/json.c | ||
222 | src/json/json_generator.c | ||
223 | src/json/json_helper.c | ||
224 | src/json/json_mhd.c | ||
225 | src/multicast/gnunet-multicast.c | 227 | src/multicast/gnunet-multicast.c |
226 | src/multicast/gnunet-service-multicast.c | 228 | src/multicast/gnunet-service-multicast.c |
227 | src/multicast/multicast_api.c | 229 | src/multicast/multicast_api.c |
@@ -235,8 +237,8 @@ src/namecache/namecache_api.c | |||
235 | src/namecache/plugin_namecache_flat.c | 237 | src/namecache/plugin_namecache_flat.c |
236 | src/namecache/plugin_namecache_postgres.c | 238 | src/namecache/plugin_namecache_postgres.c |
237 | src/namecache/plugin_namecache_sqlite.c | 239 | src/namecache/plugin_namecache_sqlite.c |
238 | src/namestore/gnunet-namestore.c | ||
239 | src/namestore/gnunet-namestore-fcfsd.c | 240 | src/namestore/gnunet-namestore-fcfsd.c |
241 | src/namestore/gnunet-namestore.c | ||
240 | src/namestore/gnunet-service-namestore.c | 242 | src/namestore/gnunet-service-namestore.c |
241 | src/namestore/gnunet-zoneimport.c | 243 | src/namestore/gnunet-zoneimport.c |
242 | src/namestore/namestore_api.c | 244 | src/namestore/namestore_api.c |
@@ -252,10 +254,10 @@ src/nat-auto/gnunet-service-nat-auto.c | |||
252 | src/nat-auto/gnunet-service-nat-auto_legacy.c | 254 | src/nat-auto/gnunet-service-nat-auto_legacy.c |
253 | src/nat-auto/nat_auto_api.c | 255 | src/nat-auto/nat_auto_api.c |
254 | src/nat-auto/nat_auto_api_test.c | 256 | src/nat-auto/nat_auto_api_test.c |
255 | src/nat/gnunet-helper-nat-client.c | ||
256 | src/nat/gnunet-helper-nat-client-windows.c | 257 | src/nat/gnunet-helper-nat-client-windows.c |
257 | src/nat/gnunet-helper-nat-server.c | 258 | src/nat/gnunet-helper-nat-client.c |
258 | src/nat/gnunet-helper-nat-server-windows.c | 259 | src/nat/gnunet-helper-nat-server-windows.c |
260 | src/nat/gnunet-helper-nat-server.c | ||
259 | src/nat/gnunet-nat.c | 261 | src/nat/gnunet-nat.c |
260 | src/nat/gnunet-service-nat.c | 262 | src/nat/gnunet-service-nat.c |
261 | src/nat/gnunet-service-nat_externalip.c | 263 | src/nat/gnunet-service-nat_externalip.c |
@@ -264,15 +266,15 @@ src/nat/gnunet-service-nat_mini.c | |||
264 | src/nat/gnunet-service-nat_stun.c | 266 | src/nat/gnunet-service-nat_stun.c |
265 | src/nat/nat_api.c | 267 | src/nat/nat_api.c |
266 | src/nat/nat_api_stun.c | 268 | src/nat/nat_api_stun.c |
267 | src/nse/gnunet-nse.c | ||
268 | src/nse/gnunet-nse-profiler.c | 269 | src/nse/gnunet-nse-profiler.c |
270 | src/nse/gnunet-nse.c | ||
269 | src/nse/gnunet-service-nse.c | 271 | src/nse/gnunet-service-nse.c |
270 | src/nse/nse_api.c | 272 | src/nse/nse_api.c |
273 | src/peerinfo-tool/gnunet-peerinfo.c | ||
274 | src/peerinfo-tool/gnunet-peerinfo_plugins.c | ||
271 | src/peerinfo/gnunet-service-peerinfo.c | 275 | src/peerinfo/gnunet-service-peerinfo.c |
272 | src/peerinfo/peerinfo_api.c | 276 | src/peerinfo/peerinfo_api.c |
273 | src/peerinfo/peerinfo_api_notify.c | 277 | src/peerinfo/peerinfo_api_notify.c |
274 | src/peerinfo-tool/gnunet-peerinfo.c | ||
275 | src/peerinfo-tool/gnunet-peerinfo_plugins.c | ||
276 | src/peerstore/gnunet-peerstore.c | 278 | src/peerstore/gnunet-peerstore.c |
277 | src/peerstore/gnunet-service-peerstore.c | 279 | src/peerstore/gnunet-service-peerstore.c |
278 | src/peerstore/peerstore_api.c | 280 | src/peerstore/peerstore_api.c |
@@ -317,20 +319,20 @@ src/revocation/gnunet-revocation.c | |||
317 | src/revocation/gnunet-service-revocation.c | 319 | src/revocation/gnunet-service-revocation.c |
318 | src/revocation/plugin_block_revocation.c | 320 | src/revocation/plugin_block_revocation.c |
319 | src/revocation/revocation_api.c | 321 | src/revocation/revocation_api.c |
320 | src/rps/gnunet-rps.c | ||
321 | src/rps/gnunet-rps-profiler.c | 322 | src/rps/gnunet-rps-profiler.c |
323 | src/rps/gnunet-rps.c | ||
322 | src/rps/gnunet-service-rps.c | 324 | src/rps/gnunet-service-rps.c |
323 | src/rps/gnunet-service-rps_custommap.c | 325 | src/rps/gnunet-service-rps_custommap.c |
324 | src/rps/gnunet-service-rps_sampler.c | 326 | src/rps/gnunet-service-rps_sampler.c |
325 | src/rps/gnunet-service-rps_sampler_elem.c | 327 | src/rps/gnunet-service-rps_sampler_elem.c |
326 | src/rps/gnunet-service-rps_view.c | 328 | src/rps/gnunet-service-rps_view.c |
327 | src/rps/rps_api.c | ||
328 | src/rps/rps-test_util.c | 329 | src/rps/rps-test_util.c |
330 | src/rps/rps_api.c | ||
329 | src/scalarproduct/gnunet-scalarproduct.c | 331 | src/scalarproduct/gnunet-scalarproduct.c |
330 | src/scalarproduct/gnunet-service-scalarproduct_alice.c | ||
331 | src/scalarproduct/gnunet-service-scalarproduct_bob.c | ||
332 | src/scalarproduct/gnunet-service-scalarproduct-ecc_alice.c | 332 | src/scalarproduct/gnunet-service-scalarproduct-ecc_alice.c |
333 | src/scalarproduct/gnunet-service-scalarproduct-ecc_bob.c | 333 | src/scalarproduct/gnunet-service-scalarproduct-ecc_bob.c |
334 | src/scalarproduct/gnunet-service-scalarproduct_alice.c | ||
335 | src/scalarproduct/gnunet-service-scalarproduct_bob.c | ||
334 | src/scalarproduct/scalarproduct_api.c | 336 | src/scalarproduct/scalarproduct_api.c |
335 | src/secretsharing/gnunet-secretsharing-profiler.c | 337 | src/secretsharing/gnunet-secretsharing-profiler.c |
336 | src/secretsharing/gnunet-service-secretsharing.c | 338 | src/secretsharing/gnunet-service-secretsharing.c |
@@ -359,15 +361,16 @@ src/statistics/gnunet-statistics.c | |||
359 | src/statistics/statistics_api.c | 361 | src/statistics/statistics_api.c |
360 | src/template/gnunet-service-template.c | 362 | src/template/gnunet-service-template.c |
361 | src/template/gnunet-template.c | 363 | src/template/gnunet-template.c |
364 | src/testbed-logger/gnunet-service-testbed-logger.c | ||
365 | src/testbed-logger/testbed_logger_api.c | ||
362 | src/testbed/generate-underlay-topology.c | 366 | src/testbed/generate-underlay-topology.c |
363 | src/testbed/gnunet-daemon-latency-logger.c | 367 | src/testbed/gnunet-daemon-latency-logger.c |
364 | src/testbed/gnunet-daemon-testbed-blacklist.c | 368 | src/testbed/gnunet-daemon-testbed-blacklist.c |
365 | src/testbed/gnunet-daemon-testbed-underlay.c | 369 | src/testbed/gnunet-daemon-testbed-underlay.c |
366 | src/testbed/gnunet-helper-testbed.c | 370 | src/testbed/gnunet-helper-testbed.c |
367 | src/testbed/gnunet_mpi_test.c | ||
368 | src/testbed/gnunet-service-test-barriers.c | 371 | src/testbed/gnunet-service-test-barriers.c |
369 | src/testbed/gnunet-service-testbed_barriers.c | ||
370 | src/testbed/gnunet-service-testbed.c | 372 | src/testbed/gnunet-service-testbed.c |
373 | src/testbed/gnunet-service-testbed_barriers.c | ||
371 | src/testbed/gnunet-service-testbed_cache.c | 374 | src/testbed/gnunet-service-testbed_cache.c |
372 | src/testbed/gnunet-service-testbed_connectionpool.c | 375 | src/testbed/gnunet-service-testbed_connectionpool.c |
373 | src/testbed/gnunet-service-testbed_cpustatus.c | 376 | src/testbed/gnunet-service-testbed_cpustatus.c |
@@ -375,20 +378,19 @@ src/testbed/gnunet-service-testbed_links.c | |||
375 | src/testbed/gnunet-service-testbed_meminfo.c | 378 | src/testbed/gnunet-service-testbed_meminfo.c |
376 | src/testbed/gnunet-service-testbed_oc.c | 379 | src/testbed/gnunet-service-testbed_oc.c |
377 | src/testbed/gnunet-service-testbed_peers.c | 380 | src/testbed/gnunet-service-testbed_peers.c |
378 | src/testbed/gnunet_testbed_mpi_spawn.c | ||
379 | src/testbed/gnunet-testbed-profiler.c | 381 | src/testbed/gnunet-testbed-profiler.c |
380 | src/testbed-logger/gnunet-service-testbed-logger.c | 382 | src/testbed/gnunet_mpi_test.c |
381 | src/testbed-logger/testbed_logger_api.c | 383 | src/testbed/gnunet_testbed_mpi_spawn.c |
382 | src/testbed/testbed_api_barriers.c | ||
383 | src/testbed/testbed_api.c | 384 | src/testbed/testbed_api.c |
385 | src/testbed/testbed_api_barriers.c | ||
384 | src/testbed/testbed_api_hosts.c | 386 | src/testbed/testbed_api_hosts.c |
385 | src/testbed/testbed_api_operations.c | 387 | src/testbed/testbed_api_operations.c |
386 | src/testbed/testbed_api_peers.c | 388 | src/testbed/testbed_api_peers.c |
387 | src/testbed/testbed_api_sd.c | 389 | src/testbed/testbed_api_sd.c |
388 | src/testbed/testbed_api_services.c | 390 | src/testbed/testbed_api_services.c |
389 | src/testbed/testbed_api_statistics.c | 391 | src/testbed/testbed_api_statistics.c |
390 | src/testbed/testbed_api_testbed.c | ||
391 | src/testbed/testbed_api_test.c | 392 | src/testbed/testbed_api_test.c |
393 | src/testbed/testbed_api_testbed.c | ||
392 | src/testbed/testbed_api_topology.c | 394 | src/testbed/testbed_api_topology.c |
393 | src/testbed/testbed_api_underlay.c | 395 | src/testbed/testbed_api_underlay.c |
394 | src/testing/gnunet-testing.c | 396 | src/testing/gnunet-testing.c |
@@ -397,28 +399,28 @@ src/testing/testing.c | |||
397 | src/topology/friends.c | 399 | src/topology/friends.c |
398 | src/topology/gnunet-daemon-topology.c | 400 | src/topology/gnunet-daemon-topology.c |
399 | src/transport/gnunet-helper-transport-bluetooth.c | 401 | src/transport/gnunet-helper-transport-bluetooth.c |
400 | src/transport/gnunet-helper-transport-wlan.c | ||
401 | src/transport/gnunet-helper-transport-wlan-dummy.c | 402 | src/transport/gnunet-helper-transport-wlan-dummy.c |
402 | src/transport/gnunet-service-transport_ats.c | 403 | src/transport/gnunet-helper-transport-wlan.c |
403 | src/transport/gnunet-service-transport.c | 404 | src/transport/gnunet-service-transport.c |
405 | src/transport/gnunet-service-transport_ats.c | ||
404 | src/transport/gnunet-service-transport_hello.c | 406 | src/transport/gnunet-service-transport_hello.c |
405 | src/transport/gnunet-service-transport_manipulation.c | 407 | src/transport/gnunet-service-transport_manipulation.c |
406 | src/transport/gnunet-service-transport_neighbours.c | 408 | src/transport/gnunet-service-transport_neighbours.c |
407 | src/transport/gnunet-service-transport_plugins.c | 409 | src/transport/gnunet-service-transport_plugins.c |
408 | src/transport/gnunet-service-transport_validation.c | 410 | src/transport/gnunet-service-transport_validation.c |
409 | src/transport/gnunet-transport.c | ||
410 | src/transport/gnunet-transport-certificate-creation.c | 411 | src/transport/gnunet-transport-certificate-creation.c |
411 | src/transport/gnunet-transport-profiler.c | 412 | src/transport/gnunet-transport-profiler.c |
412 | src/transport/gnunet-transport-wlan-receiver.c | 413 | src/transport/gnunet-transport-wlan-receiver.c |
413 | src/transport/gnunet-transport-wlan-sender.c | 414 | src/transport/gnunet-transport-wlan-sender.c |
415 | src/transport/gnunet-transport.c | ||
414 | src/transport/plugin_transport_http_client.c | 416 | src/transport/plugin_transport_http_client.c |
415 | src/transport/plugin_transport_http_common.c | 417 | src/transport/plugin_transport_http_common.c |
416 | src/transport/plugin_transport_http_server.c | 418 | src/transport/plugin_transport_http_server.c |
417 | src/transport/plugin_transport_smtp.c | 419 | src/transport/plugin_transport_smtp.c |
418 | src/transport/plugin_transport_tcp.c | 420 | src/transport/plugin_transport_tcp.c |
419 | src/transport/plugin_transport_template.c | 421 | src/transport/plugin_transport_template.c |
420 | src/transport/plugin_transport_udp_broadcasting.c | ||
421 | src/transport/plugin_transport_udp.c | 422 | src/transport/plugin_transport_udp.c |
423 | src/transport/plugin_transport_udp_broadcasting.c | ||
422 | src/transport/plugin_transport_unix.c | 424 | src/transport/plugin_transport_unix.c |
423 | src/transport/plugin_transport_wlan.c | 425 | src/transport/plugin_transport_wlan.c |
424 | src/transport/plugin_transport_xt.c | 426 | src/transport/plugin_transport_xt.c |
@@ -427,6 +429,11 @@ src/transport/tcp_connection_legacy.c | |||
427 | src/transport/tcp_server_legacy.c | 429 | src/transport/tcp_server_legacy.c |
428 | src/transport/tcp_server_mst_legacy.c | 430 | src/transport/tcp_server_mst_legacy.c |
429 | src/transport/tcp_service_legacy.c | 431 | src/transport/tcp_service_legacy.c |
432 | src/transport/transport-testing-filenames.c | ||
433 | src/transport/transport-testing-loggers.c | ||
434 | src/transport/transport-testing-main.c | ||
435 | src/transport/transport-testing-send.c | ||
436 | src/transport/transport-testing.c | ||
430 | src/transport/transport_api_address_to_string.c | 437 | src/transport/transport_api_address_to_string.c |
431 | src/transport/transport_api_blacklist.c | 438 | src/transport/transport_api_blacklist.c |
432 | src/transport/transport_api_core.c | 439 | src/transport/transport_api_core.c |
@@ -435,11 +442,8 @@ src/transport/transport_api_manipulation.c | |||
435 | src/transport/transport_api_monitor_peers.c | 442 | src/transport/transport_api_monitor_peers.c |
436 | src/transport/transport_api_monitor_plugins.c | 443 | src/transport/transport_api_monitor_plugins.c |
437 | src/transport/transport_api_offer_hello.c | 444 | src/transport/transport_api_offer_hello.c |
438 | src/transport/transport-testing.c | 445 | src/tun/regex.c |
439 | src/transport/transport-testing-filenames.c | 446 | src/tun/tun.c |
440 | src/transport/transport-testing-loggers.c | ||
441 | src/transport/transport-testing-main.c | ||
442 | src/transport/transport-testing-send.c | ||
443 | src/util/bandwidth.c | 447 | src/util/bandwidth.c |
444 | src/util/bio.c | 448 | src/util/bio.c |
445 | src/util/client.c | 449 | src/util/client.c |
@@ -451,8 +455,8 @@ src/util/configuration_loader.c | |||
451 | src/util/container_bloomfilter.c | 455 | src/util/container_bloomfilter.c |
452 | src/util/container_heap.c | 456 | src/util/container_heap.c |
453 | src/util/container_meta_data.c | 457 | src/util/container_meta_data.c |
454 | src/util/container_multihashmap32.c | ||
455 | src/util/container_multihashmap.c | 458 | src/util/container_multihashmap.c |
459 | src/util/container_multihashmap32.c | ||
456 | src/util/container_multipeermap.c | 460 | src/util/container_multipeermap.c |
457 | src/util/container_multishortmap.c | 461 | src/util/container_multishortmap.c |
458 | src/util/crypto_abe.c | 462 | src/util/crypto_abe.c |
@@ -470,12 +474,10 @@ src/util/crypto_random.c | |||
470 | src/util/crypto_rsa.c | 474 | src/util/crypto_rsa.c |
471 | src/util/crypto_symmetric.c | 475 | src/util/crypto_symmetric.c |
472 | src/util/disk.c | 476 | src/util/disk.c |
473 | src/util/dnsparser.c | ||
474 | src/util/dnsstub.c | ||
475 | src/util/getopt.c | 477 | src/util/getopt.c |
476 | src/util/getopt_helpers.c | 478 | src/util/getopt_helpers.c |
477 | src/util/gnunet-config.c | ||
478 | src/util/gnunet-config-diff.c | 479 | src/util/gnunet-config-diff.c |
480 | src/util/gnunet-config.c | ||
479 | src/util/gnunet-ecc.c | 481 | src/util/gnunet-ecc.c |
480 | src/util/gnunet-helper-w32-console.c | 482 | src/util/gnunet-helper-w32-console.c |
481 | src/util/gnunet-resolver.c | 483 | src/util/gnunet-resolver.c |
@@ -495,7 +497,6 @@ src/util/os_priority.c | |||
495 | src/util/peer.c | 497 | src/util/peer.c |
496 | src/util/plugin.c | 498 | src/util/plugin.c |
497 | src/util/program.c | 499 | src/util/program.c |
498 | src/util/regex.c | ||
499 | src/util/resolver_api.c | 500 | src/util/resolver_api.c |
500 | src/util/scheduler.c | 501 | src/util/scheduler.c |
501 | src/util/service.c | 502 | src/util/service.c |
@@ -504,17 +505,16 @@ src/util/socks.c | |||
504 | src/util/speedup.c | 505 | src/util/speedup.c |
505 | src/util/strings.c | 506 | src/util/strings.c |
506 | src/util/time.c | 507 | src/util/time.c |
507 | src/util/tun.c | ||
508 | src/util/w32cat.c | 508 | src/util/w32cat.c |
509 | src/util/win.c | 509 | src/util/win.c |
510 | src/util/winproc.c | 510 | src/util/winproc.c |
511 | src/vpn/gnunet-helper-vpn.c | ||
512 | src/vpn/gnunet-helper-vpn-windows.c | 511 | src/vpn/gnunet-helper-vpn-windows.c |
512 | src/vpn/gnunet-helper-vpn.c | ||
513 | src/vpn/gnunet-service-vpn.c | 513 | src/vpn/gnunet-service-vpn.c |
514 | src/vpn/gnunet-vpn.c | 514 | src/vpn/gnunet-vpn.c |
515 | src/vpn/vpn_api.c | 515 | src/vpn/vpn_api.c |
516 | src/zonemaster/gnunet-service-zonemaster.c | ||
517 | src/zonemaster/gnunet-service-zonemaster-monitor.c | 516 | src/zonemaster/gnunet-service-zonemaster-monitor.c |
517 | src/zonemaster/gnunet-service-zonemaster.c | ||
518 | src/fs/fs_api.h | 518 | src/fs/fs_api.h |
519 | src/include/gnunet_common.h | 519 | src/include/gnunet_common.h |
520 | src/include/gnunet_mq_lib.h | 520 | src/include/gnunet_mq_lib.h |
diff --git a/src/Makefile.am b/src/Makefile.am index 00f30adc3..d8d548706 100644 --- a/src/Makefile.am +++ b/src/Makefile.am | |||
@@ -83,6 +83,7 @@ SUBDIRS = \ | |||
83 | $(REST_DIR) \ | 83 | $(REST_DIR) \ |
84 | $(JSONAPI_DIR) \ | 84 | $(JSONAPI_DIR) \ |
85 | hello \ | 85 | hello \ |
86 | tun \ | ||
86 | block \ | 87 | block \ |
87 | statistics \ | 88 | statistics \ |
88 | arm \ | 89 | arm \ |
diff --git a/src/dns/Makefile.am b/src/dns/Makefile.am index ca2685765..9a4ecdcfd 100644 --- a/src/dns/Makefile.am +++ b/src/dns/Makefile.am | |||
@@ -27,6 +27,8 @@ install-exec-hook: | |||
27 | endif | 27 | endif |
28 | 28 | ||
29 | lib_LTLIBRARIES = \ | 29 | lib_LTLIBRARIES = \ |
30 | libgnunetdnsparser.la \ | ||
31 | libgnunetdnsstub.la \ | ||
30 | libgnunetdns.la | 32 | libgnunetdns.la |
31 | 33 | ||
32 | libexec_PROGRAMS = \ | 34 | libexec_PROGRAMS = \ |
@@ -45,6 +47,9 @@ check_SCRIPTS = \ | |||
45 | test_gnunet_dns.sh | 47 | test_gnunet_dns.sh |
46 | endif | 48 | endif |
47 | 49 | ||
50 | check_PROGRAMS = \ | ||
51 | test_hexcoder | ||
52 | |||
48 | gnunet_helper_dns_SOURCES = \ | 53 | gnunet_helper_dns_SOURCES = \ |
49 | gnunet-helper-dns.c | 54 | gnunet-helper-dns.c |
50 | 55 | ||
@@ -52,6 +57,7 @@ gnunet_helper_dns_SOURCES = \ | |||
52 | gnunet_dns_monitor_SOURCES = \ | 57 | gnunet_dns_monitor_SOURCES = \ |
53 | gnunet-dns-monitor.c | 58 | gnunet-dns-monitor.c |
54 | gnunet_dns_monitor_LDADD = \ | 59 | gnunet_dns_monitor_LDADD = \ |
60 | libgnunetdnsparser.la \ | ||
55 | libgnunetdns.la \ | 61 | libgnunetdns.la \ |
56 | $(top_builddir)/src/util/libgnunetutil.la \ | 62 | $(top_builddir)/src/util/libgnunetutil.la \ |
57 | $(GN_LIBINTL) | 63 | $(GN_LIBINTL) |
@@ -59,12 +65,15 @@ gnunet_dns_monitor_LDADD = \ | |||
59 | gnunet_zonewalk_SOURCES = \ | 65 | gnunet_zonewalk_SOURCES = \ |
60 | gnunet-zonewalk.c | 66 | gnunet-zonewalk.c |
61 | gnunet_zonewalk_LDADD = \ | 67 | gnunet_zonewalk_LDADD = \ |
68 | libgnunetdnsparser.la \ | ||
69 | libgnunetdnsstub.la \ | ||
62 | $(top_builddir)/src/util/libgnunetutil.la \ | 70 | $(top_builddir)/src/util/libgnunetutil.la \ |
63 | $(GN_LIBINTL) | 71 | $(GN_LIBINTL) |
64 | 72 | ||
65 | gnunet_dns_redirector_SOURCES = \ | 73 | gnunet_dns_redirector_SOURCES = \ |
66 | gnunet-dns-redirector.c | 74 | gnunet-dns-redirector.c |
67 | gnunet_dns_redirector_LDADD = \ | 75 | gnunet_dns_redirector_LDADD = \ |
76 | libgnunetdnsparser.la \ | ||
68 | libgnunetdns.la \ | 77 | libgnunetdns.la \ |
69 | $(top_builddir)/src/util/libgnunetutil.la \ | 78 | $(top_builddir)/src/util/libgnunetutil.la \ |
70 | $(GN_LIBINTL) | 79 | $(GN_LIBINTL) |
@@ -72,10 +81,30 @@ gnunet_dns_redirector_LDADD = \ | |||
72 | gnunet_service_dns_SOURCES = \ | 81 | gnunet_service_dns_SOURCES = \ |
73 | gnunet-service-dns.c | 82 | gnunet-service-dns.c |
74 | gnunet_service_dns_LDADD = \ | 83 | gnunet_service_dns_LDADD = \ |
84 | libgnunetdnsstub.la \ | ||
85 | $(top_builddir)/src/tun/libgnunettun.la \ | ||
75 | $(top_builddir)/src/statistics/libgnunetstatistics.la \ | 86 | $(top_builddir)/src/statistics/libgnunetstatistics.la \ |
76 | $(top_builddir)/src/util/libgnunetutil.la \ | 87 | $(top_builddir)/src/util/libgnunetutil.la \ |
77 | $(GN_LIBINTL) | 88 | $(GN_LIBINTL) |
78 | 89 | ||
90 | libgnunetdnsparser_la_SOURCES = \ | ||
91 | dnsparser.c | ||
92 | libgnunetdnsparser_la_LIBADD = \ | ||
93 | $(top_builddir)/src/util/libgnunetutil.la $(XLIB) \ | ||
94 | -lidn | ||
95 | libgnunetdnsparser_la_LDFLAGS = \ | ||
96 | $(GN_LIB_LDFLAGS) \ | ||
97 | -version-info 1:0:1 | ||
98 | |||
99 | libgnunetdnsstub_la_SOURCES = \ | ||
100 | dnsstub.c | ||
101 | libgnunetdnsstub_la_LIBADD = \ | ||
102 | $(top_builddir)/src/tun/libgnunettun.la \ | ||
103 | $(top_builddir)/src/util/libgnunetutil.la $(XLIB) | ||
104 | libgnunetdnsstub_la_LDFLAGS = \ | ||
105 | $(GN_LIB_LDFLAGS) \ | ||
106 | -version-info 0:0:0 | ||
107 | |||
79 | libgnunetdns_la_SOURCES = \ | 108 | libgnunetdns_la_SOURCES = \ |
80 | dns_api.c dns.h | 109 | dns_api.c dns.h |
81 | libgnunetdns_la_LIBADD = \ | 110 | libgnunetdns_la_LIBADD = \ |
@@ -102,3 +131,8 @@ EXTRA_DIST = \ | |||
102 | $(check_SCRIPTS) | 131 | $(check_SCRIPTS) |
103 | 132 | ||
104 | 133 | ||
134 | test_hexcoder_SOURCES = \ | ||
135 | test_hexcoder.c | ||
136 | test_hexcoder_LDADD = \ | ||
137 | libgnunetdnsparser.la \ | ||
138 | $(top_builddir)/src/util/libgnunetutil.la | ||
diff --git a/src/util/dnsparser.c b/src/dns/dnsparser.c index 32ad7c0c2..32ad7c0c2 100644 --- a/src/util/dnsparser.c +++ b/src/dns/dnsparser.c | |||
diff --git a/src/util/dnsstub.c b/src/dns/dnsstub.c index 969ff7beb..969ff7beb 100644 --- a/src/util/dnsstub.c +++ b/src/dns/dnsstub.c | |||
diff --git a/src/util/test_hexcoder.c b/src/dns/test_hexcoder.c index 441d7e200..441d7e200 100644 --- a/src/util/test_hexcoder.c +++ b/src/dns/test_hexcoder.c | |||
diff --git a/src/exit/Makefile.am b/src/exit/Makefile.am index ea4f08c73..aa1210269 100644 --- a/src/exit/Makefile.am +++ b/src/exit/Makefile.am | |||
@@ -49,8 +49,10 @@ endif | |||
49 | gnunet_daemon_exit_SOURCES = \ | 49 | gnunet_daemon_exit_SOURCES = \ |
50 | gnunet-daemon-exit.c exit.h | 50 | gnunet-daemon-exit.c exit.h |
51 | gnunet_daemon_exit_LDADD = \ | 51 | gnunet_daemon_exit_LDADD = \ |
52 | $(top_builddir)/src/dns/libgnunetdnsstub.la \ | ||
52 | $(top_builddir)/src/dht/libgnunetdht.la \ | 53 | $(top_builddir)/src/dht/libgnunetdht.la \ |
53 | $(top_builddir)/src/statistics/libgnunetstatistics.la \ | 54 | $(top_builddir)/src/statistics/libgnunetstatistics.la \ |
55 | $(top_builddir)/src/tun/libgnunettun.la \ | ||
54 | $(top_builddir)/src/util/libgnunetutil.la \ | 56 | $(top_builddir)/src/util/libgnunetutil.la \ |
55 | $(top_builddir)/src/cadet/libgnunetcadet.la \ | 57 | $(top_builddir)/src/cadet/libgnunetcadet.la \ |
56 | $(top_builddir)/src/regex/libgnunetregex.la \ | 58 | $(top_builddir)/src/regex/libgnunetregex.la \ |
diff --git a/src/gns/Makefile.am b/src/gns/Makefile.am index 2c7bb8ebb..46642113f 100644 --- a/src/gns/Makefile.am +++ b/src/gns/Makefile.am | |||
@@ -102,6 +102,7 @@ libgnunet_plugin_gnsrecord_gns_la_SOURCES = \ | |||
102 | plugin_gnsrecord_gns.c | 102 | plugin_gnsrecord_gns.c |
103 | libgnunet_plugin_gnsrecord_gns_la_LIBADD = \ | 103 | libgnunet_plugin_gnsrecord_gns_la_LIBADD = \ |
104 | $(top_builddir)/src/gnsrecord/libgnunetgnsrecord.la \ | 104 | $(top_builddir)/src/gnsrecord/libgnunetgnsrecord.la \ |
105 | $(top_builddir)/src/dns/libgnunetdnsparser.la \ | ||
105 | $(top_builddir)/src/util/libgnunetutil.la \ | 106 | $(top_builddir)/src/util/libgnunetutil.la \ |
106 | $(LTLIBINTL) | 107 | $(LTLIBINTL) |
107 | libgnunet_plugin_gnsrecord_gns_la_LDFLAGS = \ | 108 | libgnunet_plugin_gnsrecord_gns_la_LDFLAGS = \ |
@@ -139,6 +140,8 @@ gnunet_dns2gns_LDADD = \ | |||
139 | libgnunetgns.la \ | 140 | libgnunetgns.la \ |
140 | $(top_builddir)/src/util/libgnunetutil.la \ | 141 | $(top_builddir)/src/util/libgnunetutil.la \ |
141 | $(top_builddir)/src/identity/libgnunetidentity.la \ | 142 | $(top_builddir)/src/identity/libgnunetidentity.la \ |
143 | $(top_builddir)/src/dns/libgnunetdnsparser.la \ | ||
144 | $(top_builddir)/src/dns/libgnunetdnsstub.la \ | ||
142 | $(GN_LIBINTL) | 145 | $(GN_LIBINTL) |
143 | 146 | ||
144 | if LINUX | 147 | if LINUX |
@@ -203,7 +206,10 @@ gnunet_service_gns_LDADD = \ | |||
203 | $(top_builddir)/src/statistics/libgnunetstatistics.la \ | 206 | $(top_builddir)/src/statistics/libgnunetstatistics.la \ |
204 | $(top_builddir)/src/util/libgnunetutil.la \ | 207 | $(top_builddir)/src/util/libgnunetutil.la \ |
205 | $(top_builddir)/src/dns/libgnunetdns.la \ | 208 | $(top_builddir)/src/dns/libgnunetdns.la \ |
209 | $(top_builddir)/src/dns/libgnunetdnsparser.la \ | ||
210 | $(top_builddir)/src/dns/libgnunetdnsstub.la \ | ||
206 | $(top_builddir)/src/dht/libgnunetdht.la \ | 211 | $(top_builddir)/src/dht/libgnunetdht.la \ |
212 | $(top_builddir)/src/tun/libgnunettun.la \ | ||
207 | $(top_builddir)/src/namecache/libgnunetnamecache.la \ | 213 | $(top_builddir)/src/namecache/libgnunetnamecache.la \ |
208 | $(USE_VPN) \ | 214 | $(USE_VPN) \ |
209 | $(GN_LIBINTL) | 215 | $(GN_LIBINTL) |
diff --git a/src/gnsrecord/Makefile.am b/src/gnsrecord/Makefile.am index f840a31a4..c83aa3307 100644 --- a/src/gnsrecord/Makefile.am +++ b/src/gnsrecord/Makefile.am | |||
@@ -38,6 +38,7 @@ libgnunetgnsrecord_la_SOURCES = \ | |||
38 | gnsrecord_crypto.c \ | 38 | gnsrecord_crypto.c \ |
39 | gnsrecord_misc.c | 39 | gnsrecord_misc.c |
40 | libgnunetgnsrecord_la_LIBADD = \ | 40 | libgnunetgnsrecord_la_LIBADD = \ |
41 | $(top_builddir)/src/dns/libgnunetdnsparser.la \ | ||
41 | $(top_builddir)/src/util/libgnunetutil.la \ | 42 | $(top_builddir)/src/util/libgnunetutil.la \ |
42 | $(GN_LIBINTL) | 43 | $(GN_LIBINTL) |
43 | libgnunetgnsrecord_la_LDFLAGS = \ | 44 | libgnunetgnsrecord_la_LDFLAGS = \ |
@@ -52,6 +53,7 @@ plugin_LTLIBRARIES = \ | |||
52 | libgnunet_plugin_gnsrecord_dns_la_SOURCES = \ | 53 | libgnunet_plugin_gnsrecord_dns_la_SOURCES = \ |
53 | plugin_gnsrecord_dns.c | 54 | plugin_gnsrecord_dns.c |
54 | libgnunet_plugin_gnsrecord_dns_la_LIBADD = \ | 55 | libgnunet_plugin_gnsrecord_dns_la_LIBADD = \ |
56 | $(top_builddir)/src/dns/libgnunetdnsparser.la \ | ||
55 | $(top_builddir)/src/util/libgnunetutil.la \ | 57 | $(top_builddir)/src/util/libgnunetutil.la \ |
56 | $(LTLIBINTL) | 58 | $(LTLIBINTL) |
57 | libgnunet_plugin_gnsrecord_dns_la_LDFLAGS = \ | 59 | libgnunet_plugin_gnsrecord_dns_la_LDFLAGS = \ |
diff --git a/src/include/gnunet_dnsstub_lib.h b/src/include/gnunet_dnsstub_lib.h index 9b10c5c48..8f1b90425 100644 --- a/src/include/gnunet_dnsstub_lib.h +++ b/src/include/gnunet_dnsstub_lib.h | |||
@@ -85,7 +85,7 @@ GNUNET_DNSSTUB_add_dns_sa (struct GNUNET_DNSSTUB_Context *ctx, | |||
85 | * Only effective for requests issued after this call. | 85 | * Only effective for requests issued after this call. |
86 | * | 86 | * |
87 | * @param ctx resolver context to modify | 87 | * @param ctx resolver context to modify |
88 | * @param retry_freq how long to wait between retries | 88 | * @param retry_frequ how long to wait between retries |
89 | */ | 89 | */ |
90 | void | 90 | void |
91 | GNUNET_DNSSTUB_set_retry (struct GNUNET_DNSSTUB_Context *ctx, | 91 | GNUNET_DNSSTUB_set_retry (struct GNUNET_DNSSTUB_Context *ctx, |
diff --git a/src/include/gnunet_util_lib.h b/src/include/gnunet_util_lib.h index 82f61a22b..5e8eef1ad 100644 --- a/src/include/gnunet_util_lib.h +++ b/src/include/gnunet_util_lib.h | |||
@@ -78,8 +78,6 @@ extern "C" | |||
78 | #include "gnunet_service_lib.h" | 78 | #include "gnunet_service_lib.h" |
79 | #include "gnunet_signal_lib.h" | 79 | #include "gnunet_signal_lib.h" |
80 | #include "gnunet_strings_lib.h" | 80 | #include "gnunet_strings_lib.h" |
81 | #include "gnunet_dnsparser_lib.h" | ||
82 | #include "gnunet_dnsstub_lib.h" | ||
83 | 81 | ||
84 | #if 0 /* keep Emacsens' auto-indent happy */ | 82 | #if 0 /* keep Emacsens' auto-indent happy */ |
85 | { | 83 | { |
diff --git a/src/namestore/Makefile.am b/src/namestore/Makefile.am index 777e722c2..fa85cc060 100644 --- a/src/namestore/Makefile.am +++ b/src/namestore/Makefile.am | |||
@@ -147,6 +147,8 @@ gnunet_zoneimport_LDADD = \ | |||
147 | $(top_builddir)/src/statistics/libgnunetstatistics.la \ | 147 | $(top_builddir)/src/statistics/libgnunetstatistics.la \ |
148 | $(top_builddir)/src/identity/libgnunetidentity.la \ | 148 | $(top_builddir)/src/identity/libgnunetidentity.la \ |
149 | $(top_builddir)/src/gnsrecord/libgnunetgnsrecord.la \ | 149 | $(top_builddir)/src/gnsrecord/libgnunetgnsrecord.la \ |
150 | $(top_builddir)/src/dns/libgnunetdnsparser.la \ | ||
151 | $(top_builddir)/src/dns/libgnunetdnsstub.la \ | ||
150 | $(top_builddir)/src/util/libgnunetutil.la \ | 152 | $(top_builddir)/src/util/libgnunetutil.la \ |
151 | $(GN_LIBINTL) | 153 | $(GN_LIBINTL) |
152 | 154 | ||
diff --git a/src/pt/Makefile.am b/src/pt/Makefile.am index 188387c0c..e36630ae4 100644 --- a/src/pt/Makefile.am +++ b/src/pt/Makefile.am | |||
@@ -28,6 +28,7 @@ gnunet_daemon_pt_LDADD = \ | |||
28 | $(top_builddir)/src/cadet/libgnunetcadet.la \ | 28 | $(top_builddir)/src/cadet/libgnunetcadet.la \ |
29 | $(top_builddir)/src/dht/libgnunetdht.la \ | 29 | $(top_builddir)/src/dht/libgnunetdht.la \ |
30 | $(top_builddir)/src/dns/libgnunetdns.la \ | 30 | $(top_builddir)/src/dns/libgnunetdns.la \ |
31 | $(top_builddir)/src/dns/libgnunetdnsparser.la \ | ||
31 | $(top_builddir)/src/statistics/libgnunetstatistics.la \ | 32 | $(top_builddir)/src/statistics/libgnunetstatistics.la \ |
32 | $(top_builddir)/src/util/libgnunetutil.la \ | 33 | $(top_builddir)/src/util/libgnunetutil.la \ |
33 | $(GN_LIBINTL) | 34 | $(GN_LIBINTL) |
diff --git a/src/regex/.gitignore b/src/regex/.gitignore new file mode 100644 index 000000000..39dc89c88 --- /dev/null +++ b/src/regex/.gitignore | |||
@@ -0,0 +1,12 @@ | |||
1 | perf-regex | ||
2 | gnunet-daemon-regexprofiler | ||
3 | gnunet-regex-profiler | ||
4 | gnunet-regex-simulation-profiler | ||
5 | gnunet-service-regex | ||
6 | test_graph.dot | ||
7 | test_regex_api | ||
8 | test_regex_eval_api | ||
9 | test_regex_graph_api | ||
10 | test_regex_integration | ||
11 | test_regex_iterate_api | ||
12 | test_regex_proofs | ||
diff --git a/src/regex/Makefile.am b/src/regex/Makefile.am new file mode 100644 index 000000000..80997db40 --- /dev/null +++ b/src/regex/Makefile.am | |||
@@ -0,0 +1,213 @@ | |||
1 | # This Makefile.am is in the public domain | ||
2 | AM_CPPFLAGS = -I$(top_srcdir)/src/include | ||
3 | |||
4 | if MINGW | ||
5 | WINFLAGS = -Wl,--no-undefined -Wl,--export-all-symbols | ||
6 | endif | ||
7 | |||
8 | if USE_COVERAGE | ||
9 | AM_CFLAGS = --coverage | ||
10 | endif | ||
11 | |||
12 | pkgcfgdir= $(pkgdatadir)/config.d/ | ||
13 | |||
14 | libexecdir= $(pkglibdir)/libexec/ | ||
15 | |||
16 | plugindir = $(libdir)/gnunet | ||
17 | |||
18 | pkgcfg_DATA = \ | ||
19 | regex.conf | ||
20 | |||
21 | libexec_PROGRAMS = \ | ||
22 | gnunet-service-regex \ | ||
23 | gnunet-daemon-regexprofiler | ||
24 | |||
25 | |||
26 | gnunet_service_regex_SOURCES = \ | ||
27 | gnunet-service-regex.c | ||
28 | gnunet_service_regex_LDADD = -lm \ | ||
29 | libgnunetregex_internal.a \ | ||
30 | libgnunetregexblock.la \ | ||
31 | $(top_builddir)/src/dht/libgnunetdht.la \ | ||
32 | $(top_builddir)/src/statistics/libgnunetstatistics.la \ | ||
33 | $(top_builddir)/src/util/libgnunetutil.la \ | ||
34 | $(GN_LIBINTL) | ||
35 | |||
36 | noinst_LIBRARIES = \ | ||
37 | libgnunetregex_internal.a \ | ||
38 | libgnunetregextest.a | ||
39 | |||
40 | lib_LTLIBRARIES = \ | ||
41 | libgnunetregexblock.la \ | ||
42 | libgnunetregex.la | ||
43 | |||
44 | |||
45 | libgnunetregexblock_la_SOURCES = \ | ||
46 | regex_block_lib.c regex_block_lib.h | ||
47 | libgnunetregexblock_la_LIBADD = \ | ||
48 | $(top_builddir)/src/util/libgnunetutil.la \ | ||
49 | $(XLIB) \ | ||
50 | $(LTLIBINTL) | ||
51 | libgnunetregexblock_la_LDFLAGS = \ | ||
52 | $(GN_LIB_LDFLAGS) $(WINFLAGS) \ | ||
53 | -version-info 1:0:0 | ||
54 | |||
55 | |||
56 | libgnunetregex_internal_a_SOURCES = \ | ||
57 | regex_internal_lib.h \ | ||
58 | regex_internal.h regex_internal.c \ | ||
59 | regex_internal_dht.c | ||
60 | libgnunetregex_internal_a_DEPENDENCIES = \ | ||
61 | libgnunetregexblock.la | ||
62 | |||
63 | |||
64 | libgnunetregex_la_SOURCES = \ | ||
65 | regex_api_announce.c \ | ||
66 | regex_api_search.c \ | ||
67 | regex_ipc.h | ||
68 | libgnunetregex_la_LIBADD = \ | ||
69 | $(top_builddir)/src/util/libgnunetutil.la | ||
70 | libgnunetregex_la_LDFLAGS = \ | ||
71 | $(GN_LIB_LDFLAGS) \ | ||
72 | -version-info 3:1:0 | ||
73 | |||
74 | |||
75 | plugin_LTLIBRARIES = \ | ||
76 | libgnunet_plugin_block_regex.la | ||
77 | |||
78 | libgnunet_plugin_block_regex_la_SOURCES = \ | ||
79 | plugin_block_regex.c | ||
80 | libgnunet_plugin_block_regex_la_LIBADD = \ | ||
81 | libgnunetregexblock.la \ | ||
82 | $(top_builddir)/src/block/libgnunetblock.la \ | ||
83 | $(top_builddir)/src/block/libgnunetblockgroup.la \ | ||
84 | $(top_builddir)/src/util/libgnunetutil.la | ||
85 | libgnunet_plugin_block_regex_la_LDFLAGS = \ | ||
86 | $(GN_PLUGIN_LDFLAGS) | ||
87 | |||
88 | if HAVE_MYSQL | ||
89 | noinst_mysql_progs = \ | ||
90 | gnunet-regex-simulation-profiler | ||
91 | |||
92 | gnunet_regex_simulation_profiler_SOURCES = \ | ||
93 | gnunet-regex-simulation-profiler.c | ||
94 | gnunet_regex_simulation_profiler_LDADD = \ | ||
95 | $(top_builddir)/src/util/libgnunetutil.la \ | ||
96 | libgnunetregex_internal.a \ | ||
97 | $(top_builddir)/src/dht/libgnunetdht.la \ | ||
98 | $(top_builddir)/src/my/libgnunetmy.la \ | ||
99 | $(top_builddir)/src/mysql/libgnunetmysql.la | ||
100 | endif | ||
101 | |||
102 | libgnunetregextest_a_SOURCES = \ | ||
103 | regex_test_lib.c regex_test_lib.h \ | ||
104 | regex_test_graph.c \ | ||
105 | regex_test_random.c | ||
106 | libgnunetregextest_a_LIBADD = \ | ||
107 | $(top_builddir)/src/util/libgnunetutil.la \ | ||
108 | libgnunetregex_internal.a | ||
109 | |||
110 | if HAVE_TESTING | ||
111 | noinst_PROGRAMS = $(noinst_mysql_progs) \ | ||
112 | perf-regex \ | ||
113 | gnunet-regex-profiler | ||
114 | endif | ||
115 | |||
116 | perf_regex_SOURCES = \ | ||
117 | perf-regex.c | ||
118 | perf_regex_LDADD = -lm \ | ||
119 | libgnunetregex_internal.a \ | ||
120 | $(top_builddir)/src/dht/libgnunetdht.la \ | ||
121 | libgnunetregexblock.la \ | ||
122 | libgnunetregextest.a \ | ||
123 | $(top_builddir)/src/util/libgnunetutil.la | ||
124 | |||
125 | gnunet_regex_profiler_SOURCES = \ | ||
126 | gnunet-regex-profiler.c | ||
127 | gnunet_regex_profiler_LDADD = -lm \ | ||
128 | $(top_builddir)/src/arm/libgnunetarm.la \ | ||
129 | $(top_builddir)/src/testbed/libgnunettestbed.la \ | ||
130 | libgnunetregex_internal.a \ | ||
131 | $(top_builddir)/src/dht/libgnunetdht.la \ | ||
132 | libgnunetregexblock.la \ | ||
133 | libgnunetregextest.a \ | ||
134 | $(top_builddir)/src/statistics/libgnunetstatistics.la \ | ||
135 | $(top_builddir)/src/util/libgnunetutil.la | ||
136 | |||
137 | gnunet_daemon_regexprofiler_SOURCES = \ | ||
138 | gnunet-daemon-regexprofiler.c | ||
139 | gnunet_daemon_regexprofiler_LDADD = -lm \ | ||
140 | libgnunetregex_internal.a \ | ||
141 | $(top_builddir)/src/dht/libgnunetdht.la \ | ||
142 | libgnunetregexblock.la \ | ||
143 | libgnunetregextest.a \ | ||
144 | $(top_builddir)/src/statistics/libgnunetstatistics.la \ | ||
145 | $(top_builddir)/src/util/libgnunetutil.la | ||
146 | |||
147 | check_PROGRAMS = \ | ||
148 | test_regex_integration \ | ||
149 | test_regex_eval_api \ | ||
150 | test_regex_iterate_api \ | ||
151 | test_regex_proofs \ | ||
152 | test_regex_graph_api \ | ||
153 | test_regex_api | ||
154 | |||
155 | if ENABLE_TEST_RUN | ||
156 | AM_TESTS_ENVIRONMENT=export GNUNET_PREFIX=$${GNUNET_PREFIX:-@libdir@};export PATH=$${GNUNET_PREFIX:-@prefix@}/bin:$$PATH;unset XDG_DATA_HOME;unset XDG_CONFIG_HOME; | ||
157 | TESTS = $(check_PROGRAMS) | ||
158 | endif | ||
159 | |||
160 | test_regex_eval_api_SOURCES = \ | ||
161 | test_regex_eval_api.c | ||
162 | test_regex_eval_api_LDADD = -lm \ | ||
163 | libgnunetregex_internal.a \ | ||
164 | $(top_builddir)/src/dht/libgnunetdht.la \ | ||
165 | libgnunetregextest.a \ | ||
166 | libgnunetregexblock.la \ | ||
167 | $(top_builddir)/src/util/libgnunetutil.la | ||
168 | |||
169 | test_regex_integration_SOURCES = \ | ||
170 | test_regex_integration.c | ||
171 | test_regex_integration_LDADD = -lm \ | ||
172 | libgnunetregex.la \ | ||
173 | $(top_builddir)/src/testing/libgnunettesting.la \ | ||
174 | $(top_builddir)/src/tun/libgnunettun.la \ | ||
175 | $(top_builddir)/src/util/libgnunetutil.la | ||
176 | |||
177 | test_regex_api_SOURCES = \ | ||
178 | test_regex_api.c | ||
179 | test_regex_api_LDADD = -lm \ | ||
180 | libgnunetregex.la \ | ||
181 | $(top_builddir)/src/testing/libgnunettesting.la \ | ||
182 | $(top_builddir)/src/util/libgnunetutil.la | ||
183 | |||
184 | test_regex_iterate_api_SOURCES = \ | ||
185 | test_regex_iterate_api.c | ||
186 | test_regex_iterate_api_LDADD = -lm \ | ||
187 | libgnunetregex_internal.a \ | ||
188 | libgnunetregexblock.la \ | ||
189 | $(top_builddir)/src/dht/libgnunetdht.la \ | ||
190 | $(top_builddir)/src/util/libgnunetutil.la | ||
191 | |||
192 | test_regex_proofs_SOURCES = \ | ||
193 | test_regex_proofs.c | ||
194 | test_regex_proofs_LDADD = -lm \ | ||
195 | libgnunetregex_internal.a \ | ||
196 | $(top_builddir)/src/dht/libgnunetdht.la \ | ||
197 | libgnunetregextest.a \ | ||
198 | libgnunetregexblock.la \ | ||
199 | $(top_builddir)/src/util/libgnunetutil.la | ||
200 | |||
201 | test_regex_graph_api_SOURCES = \ | ||
202 | test_regex_graph_api.c | ||
203 | test_regex_graph_api_LDADD = -lm \ | ||
204 | libgnunetregex_internal.a \ | ||
205 | $(top_builddir)/src/dht/libgnunetdht.la \ | ||
206 | libgnunetregextest.a \ | ||
207 | libgnunetregexblock.la \ | ||
208 | $(top_builddir)/src/util/libgnunetutil.la | ||
209 | |||
210 | |||
211 | EXTRA_DIST = \ | ||
212 | regex_simulation_profiler_test.conf \ | ||
213 | test_regex_api_data.conf | ||
diff --git a/src/regex/gnunet-daemon-regexprofiler.c b/src/regex/gnunet-daemon-regexprofiler.c new file mode 100644 index 000000000..11c2f513a --- /dev/null +++ b/src/regex/gnunet-daemon-regexprofiler.c | |||
@@ -0,0 +1,398 @@ | |||
1 | /* | ||
2 | This file is part of GNUnet. | ||
3 | Copyright (C) 2012, 2013 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 | |||
19 | /** | ||
20 | * @file regex/gnunet-daemon-regexprofiler.c | ||
21 | * @brief daemon that uses cadet to announce a regular expression. Used in | ||
22 | * conjunction with gnunet-regex-profiler to announce regexes on serveral peers | ||
23 | * without the need to explicitly connect to the cadet service running on the | ||
24 | * peer from within the profiler. | ||
25 | * @author Maximilian Szengel | ||
26 | * @author Bartlomiej Polot | ||
27 | */ | ||
28 | #include "platform.h" | ||
29 | #include "gnunet_util_lib.h" | ||
30 | #include "regex_internal_lib.h" | ||
31 | #include "regex_test_lib.h" | ||
32 | #include "gnunet_dht_service.h" | ||
33 | #include "gnunet_statistics_service.h" | ||
34 | |||
35 | /** | ||
36 | * Return value from 'main'. | ||
37 | */ | ||
38 | static int global_ret; | ||
39 | |||
40 | /** | ||
41 | * Configuration we use. | ||
42 | */ | ||
43 | static const struct GNUNET_CONFIGURATION_Handle *cfg; | ||
44 | |||
45 | /** | ||
46 | * Handle to the statistics service. | ||
47 | */ | ||
48 | static struct GNUNET_STATISTICS_Handle *stats_handle; | ||
49 | |||
50 | /** | ||
51 | * Peer's dht handle. | ||
52 | */ | ||
53 | static struct GNUNET_DHT_Handle *dht_handle; | ||
54 | |||
55 | /** | ||
56 | * Peer's regex announce handle. | ||
57 | */ | ||
58 | static struct REGEX_INTERNAL_Announcement *announce_handle; | ||
59 | |||
60 | /** | ||
61 | * Periodically reannounce regex. | ||
62 | */ | ||
63 | static struct GNUNET_SCHEDULER_Task * reannounce_task; | ||
64 | |||
65 | /** | ||
66 | * What's the maximum reannounce period. | ||
67 | */ | ||
68 | static struct GNUNET_TIME_Relative reannounce_period_max; | ||
69 | |||
70 | /** | ||
71 | * Maximal path compression length for regex announcing. | ||
72 | */ | ||
73 | static unsigned long long max_path_compression; | ||
74 | |||
75 | /** | ||
76 | * Name of the file containing policies that this peer should announce. One | ||
77 | * policy per line. | ||
78 | */ | ||
79 | static char * policy_filename; | ||
80 | |||
81 | /** | ||
82 | * Prefix to add before every regex we're announcing. | ||
83 | */ | ||
84 | static char * regex_prefix; | ||
85 | |||
86 | /** | ||
87 | * Regex with prefix. | ||
88 | */ | ||
89 | static char *rx_with_pfx; | ||
90 | |||
91 | /** | ||
92 | * How many put rounds should we do. | ||
93 | */ | ||
94 | static unsigned int rounds = 3; | ||
95 | |||
96 | /** | ||
97 | * Private key for this peer. | ||
98 | */ | ||
99 | static struct GNUNET_CRYPTO_EddsaPrivateKey *my_private_key; | ||
100 | |||
101 | |||
102 | |||
103 | /** | ||
104 | * Task run during shutdown. | ||
105 | * | ||
106 | * @param cls unused | ||
107 | */ | ||
108 | static void | ||
109 | shutdown_task (void *cls) | ||
110 | { | ||
111 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "shutting down\n"); | ||
112 | |||
113 | if (NULL != announce_handle) | ||
114 | { | ||
115 | REGEX_INTERNAL_announce_cancel (announce_handle); | ||
116 | announce_handle = NULL; | ||
117 | } | ||
118 | if (NULL != reannounce_task) | ||
119 | { | ||
120 | GNUNET_free (GNUNET_SCHEDULER_cancel (reannounce_task)); | ||
121 | reannounce_task = NULL; | ||
122 | } | ||
123 | if (NULL != dht_handle) | ||
124 | { | ||
125 | GNUNET_DHT_disconnect (dht_handle); | ||
126 | dht_handle = NULL; | ||
127 | } | ||
128 | GNUNET_free (my_private_key); | ||
129 | my_private_key = NULL; | ||
130 | |||
131 | GNUNET_log (GNUNET_ERROR_TYPE_INFO, | ||
132 | "Daemon for %s shutting down\n", | ||
133 | policy_filename); | ||
134 | } | ||
135 | |||
136 | |||
137 | /** | ||
138 | * Announce a previously announced regex re-using cached data. | ||
139 | * | ||
140 | * @param cls Closure (regex to announce if needed). | ||
141 | */ | ||
142 | static void | ||
143 | reannounce_regex (void *cls) | ||
144 | { | ||
145 | char *regex = cls; | ||
146 | struct GNUNET_TIME_Relative random_delay; | ||
147 | |||
148 | reannounce_task = NULL; | ||
149 | if (0 == rounds--) | ||
150 | { | ||
151 | global_ret = 0; | ||
152 | GNUNET_SCHEDULER_shutdown (); | ||
153 | GNUNET_free (regex); | ||
154 | return; | ||
155 | } | ||
156 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Announcing regex: %s\n", regex); | ||
157 | GNUNET_STATISTICS_update (stats_handle, "# regexes announced", 1, GNUNET_NO); | ||
158 | if (NULL == announce_handle && NULL != regex) | ||
159 | { | ||
160 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, | ||
161 | "First time, creating regex: %s\n", | ||
162 | regex); | ||
163 | announce_handle = REGEX_INTERNAL_announce (dht_handle, | ||
164 | my_private_key, | ||
165 | regex, | ||
166 | (unsigned int) max_path_compression, | ||
167 | stats_handle); | ||
168 | } | ||
169 | else | ||
170 | { | ||
171 | GNUNET_assert (NULL != announce_handle); | ||
172 | REGEX_INTERNAL_reannounce (announce_handle); | ||
173 | } | ||
174 | |||
175 | random_delay = | ||
176 | GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_MICROSECONDS, | ||
177 | GNUNET_CRYPTO_random_u32 ( | ||
178 | GNUNET_CRYPTO_QUALITY_WEAK, | ||
179 | reannounce_period_max.rel_value_us)); | ||
180 | reannounce_task = GNUNET_SCHEDULER_add_delayed (random_delay, | ||
181 | &reannounce_regex, cls); | ||
182 | } | ||
183 | |||
184 | |||
185 | /** | ||
186 | * Announce the given regular expression using regex and the path compression | ||
187 | * length read from config. | ||
188 | * | ||
189 | * @param regex regular expression to announce on this peer's cadet. | ||
190 | */ | ||
191 | static void | ||
192 | announce_regex (const char *regex) | ||
193 | { | ||
194 | char *copy; | ||
195 | |||
196 | if (NULL == regex || 0 == strlen (regex)) | ||
197 | { | ||
198 | GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Cannot announce empty regex\n"); | ||
199 | return; | ||
200 | } | ||
201 | |||
202 | GNUNET_log (GNUNET_ERROR_TYPE_INFO, | ||
203 | "Daemon for %s starting\n", | ||
204 | policy_filename); | ||
205 | GNUNET_assert (NULL == reannounce_task); | ||
206 | copy = GNUNET_strdup (regex); | ||
207 | reannounce_task = GNUNET_SCHEDULER_add_now (&reannounce_regex, | ||
208 | (void *) copy); | ||
209 | } | ||
210 | |||
211 | |||
212 | /** | ||
213 | * Scan through the policy_dir looking for the n-th filename. | ||
214 | * | ||
215 | * @param cls Closure (target number n). | ||
216 | * @param filename complete filename (absolute path). | ||
217 | * @return GNUNET_OK to continue to iterate, | ||
218 | * GNUNET_NO to stop when found | ||
219 | */ | ||
220 | static int | ||
221 | scan (void *cls, const char *filename) | ||
222 | { | ||
223 | long n = (long) cls; | ||
224 | static long c = 0; | ||
225 | |||
226 | if (c == n) | ||
227 | { | ||
228 | policy_filename = GNUNET_strdup (filename); | ||
229 | return GNUNET_NO; | ||
230 | } | ||
231 | c++; | ||
232 | return GNUNET_OK; | ||
233 | } | ||
234 | |||
235 | |||
236 | /** | ||
237 | * @brief Main function that will be run by the scheduler. | ||
238 | * | ||
239 | * @param cls closure | ||
240 | * @param args remaining command-line arguments | ||
241 | * @param cfgfile name of the configuration file used (for saving, can be NULL!) | ||
242 | * @param cfg_ configuration | ||
243 | */ | ||
244 | static void | ||
245 | run (void *cls, char *const *args GNUNET_UNUSED, | ||
246 | const char *cfgfile GNUNET_UNUSED, | ||
247 | const struct GNUNET_CONFIGURATION_Handle *cfg_) | ||
248 | { | ||
249 | char *regex = NULL; | ||
250 | char **components; | ||
251 | char *policy_dir; | ||
252 | long long unsigned int peer_id; | ||
253 | |||
254 | cfg = cfg_; | ||
255 | |||
256 | my_private_key = GNUNET_CRYPTO_eddsa_key_create_from_configuration (cfg); | ||
257 | GNUNET_assert (NULL != my_private_key); | ||
258 | if (GNUNET_OK != | ||
259 | GNUNET_CONFIGURATION_get_value_number (cfg, "REGEXPROFILER", | ||
260 | "MAX_PATH_COMPRESSION", | ||
261 | &max_path_compression)) | ||
262 | { | ||
263 | GNUNET_log (GNUNET_ERROR_TYPE_ERROR, | ||
264 | _ | ||
265 | ("%s service is lacking key configuration settings (%s). Exiting.\n"), | ||
266 | "regexprofiler", "max_path_compression"); | ||
267 | global_ret = GNUNET_SYSERR; | ||
268 | GNUNET_SCHEDULER_shutdown (); | ||
269 | return; | ||
270 | } | ||
271 | if (GNUNET_OK != | ||
272 | GNUNET_CONFIGURATION_get_value_string (cfg, "REGEXPROFILER", | ||
273 | "POLICY_DIR", &policy_dir)) | ||
274 | { | ||
275 | GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR, "REGEXPROFILER", "POLICY_DIR"); | ||
276 | global_ret = GNUNET_SYSERR; | ||
277 | GNUNET_SCHEDULER_shutdown (); | ||
278 | return; | ||
279 | } | ||
280 | if (GNUNET_OK != | ||
281 | GNUNET_CONFIGURATION_get_value_number (cfg, "TESTBED", | ||
282 | "PEERID", &peer_id)) | ||
283 | { | ||
284 | GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR, "TESTBED", "PEERID"); | ||
285 | global_ret = GNUNET_SYSERR; | ||
286 | GNUNET_free (policy_dir); | ||
287 | GNUNET_SCHEDULER_shutdown (); | ||
288 | return; | ||
289 | } | ||
290 | |||
291 | if (GNUNET_OK != | ||
292 | GNUNET_CONFIGURATION_get_value_string (cfg, "REGEXPROFILER", | ||
293 | "REGEX_PREFIX", ®ex_prefix)) | ||
294 | { | ||
295 | GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR, "REGEXPROFILER", "REGEX_PREFIX"); | ||
296 | global_ret = GNUNET_SYSERR; | ||
297 | GNUNET_free (policy_dir); | ||
298 | GNUNET_SCHEDULER_shutdown (); | ||
299 | return; | ||
300 | } | ||
301 | |||
302 | if (GNUNET_OK != | ||
303 | GNUNET_CONFIGURATION_get_value_time (cfg, "REGEXPROFILER", | ||
304 | "REANNOUNCE_PERIOD_MAX", | ||
305 | &reannounce_period_max)) | ||
306 | { | ||
307 | GNUNET_log (GNUNET_ERROR_TYPE_ERROR, | ||
308 | "reannounce_period_max not given. Using 10 minutes.\n"); | ||
309 | reannounce_period_max = | ||
310 | GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_MINUTES, 10); | ||
311 | } | ||
312 | |||
313 | stats_handle = GNUNET_STATISTICS_create ("regexprofiler", cfg); | ||
314 | |||
315 | dht_handle = GNUNET_DHT_connect (cfg, 1); | ||
316 | |||
317 | if (NULL == dht_handle) | ||
318 | { | ||
319 | GNUNET_log (GNUNET_ERROR_TYPE_ERROR, | ||
320 | "Could not acquire dht handle. Exiting.\n"); | ||
321 | global_ret = GNUNET_SYSERR; | ||
322 | GNUNET_free (policy_dir); | ||
323 | GNUNET_SCHEDULER_shutdown (); | ||
324 | return; | ||
325 | } | ||
326 | |||
327 | /* Read regexes from policy files */ | ||
328 | GNUNET_assert (-1 != GNUNET_DISK_directory_scan (policy_dir, &scan, | ||
329 | (void *) (long) peer_id)); | ||
330 | if (NULL == (components = REGEX_TEST_read_from_file (policy_filename))) | ||
331 | { | ||
332 | GNUNET_log (GNUNET_ERROR_TYPE_ERROR, | ||
333 | "Policy file %s contains no policies. Exiting.\n", | ||
334 | policy_filename); | ||
335 | global_ret = GNUNET_SYSERR; | ||
336 | GNUNET_free (policy_dir); | ||
337 | GNUNET_SCHEDULER_shutdown (); | ||
338 | return; | ||
339 | } | ||
340 | GNUNET_free (policy_dir); | ||
341 | regex = REGEX_TEST_combine (components, 16); | ||
342 | REGEX_TEST_free_from_file (components); | ||
343 | |||
344 | /* Announcing regexes from policy_filename */ | ||
345 | GNUNET_asprintf (&rx_with_pfx, | ||
346 | "%s(%s)(0|1|2|3|4|5|6|7|8|9|a|b|c|d|e|f)*", | ||
347 | regex_prefix, | ||
348 | regex); | ||
349 | announce_regex (rx_with_pfx); | ||
350 | GNUNET_free (regex); | ||
351 | GNUNET_free (rx_with_pfx); | ||
352 | |||
353 | /* Scheduled the task to clean up when shutdown is called */ | ||
354 | GNUNET_SCHEDULER_add_shutdown (&shutdown_task, | ||
355 | NULL); | ||
356 | } | ||
357 | |||
358 | |||
359 | /** | ||
360 | * The main function of the regexprofiler service. | ||
361 | * | ||
362 | * @param argc number of arguments from the command line | ||
363 | * @param argv command line arguments | ||
364 | * @return 0 ok, 1 on error | ||
365 | */ | ||
366 | int | ||
367 | main (int argc, char *const *argv) | ||
368 | { | ||
369 | static const struct GNUNET_GETOPT_CommandLineOption options[] = { | ||
370 | GNUNET_GETOPT_OPTION_END | ||
371 | }; | ||
372 | |||
373 | if (GNUNET_OK != GNUNET_STRINGS_get_utf8_args (argc, argv, &argc, &argv)) | ||
374 | return 2; | ||
375 | return (GNUNET_OK == | ||
376 | GNUNET_PROGRAM_run (argc, argv, "regexprofiler", | ||
377 | gettext_noop | ||
378 | ("Daemon to announce regular expressions for the peer using cadet."), | ||
379 | options, &run, NULL)) ? global_ret : 1; | ||
380 | } | ||
381 | |||
382 | |||
383 | #if defined(LINUX) && defined(__GLIBC__) | ||
384 | #include <malloc.h> | ||
385 | |||
386 | /** | ||
387 | * MINIMIZE heap size (way below 128k) since this process doesn't need much. | ||
388 | */ | ||
389 | void __attribute__ ((constructor)) GNUNET_ARM_memory_init () | ||
390 | { | ||
391 | mallopt (M_TRIM_THRESHOLD, 4 * 1024); | ||
392 | mallopt (M_TOP_PAD, 1 * 1024); | ||
393 | malloc_trim (0); | ||
394 | } | ||
395 | #endif | ||
396 | |||
397 | |||
398 | /* end of gnunet-daemon-regexprofiler.c */ | ||
diff --git a/src/regex/gnunet-regex-profiler.c b/src/regex/gnunet-regex-profiler.c new file mode 100644 index 000000000..f65ba3db1 --- /dev/null +++ b/src/regex/gnunet-regex-profiler.c | |||
@@ -0,0 +1,1600 @@ | |||
1 | /* | ||
2 | This file is part of GNUnet. | ||
3 | Copyright (C) 2011 - 2017 GNUnet e.V. | ||
4 | |||
5 | GNUnet is free software: you can redistribute it and/or modify it | ||
6 | under the terms of the GNU Affero General Public License as published | ||
7 | by the Free Software Foundation, either version 3 of the License, | ||
8 | or (at your option) any later version. | ||
9 | |||
10 | GNUnet is distributed in the hope that it will be useful, but | ||
11 | WITHOUT ANY WARRANTY; without even the implied warranty of | ||
12 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | ||
13 | Affero General Public License for more details. | ||
14 | |||
15 | You should have received a copy of the GNU Affero General Public License | ||
16 | along with this program. If not, see <http://www.gnu.org/licenses/>. | ||
17 | */ | ||
18 | |||
19 | /** | ||
20 | * @file regex/gnunet-regex-profiler.c | ||
21 | * @brief Regex profiler for testing distributed regex use. | ||
22 | * @author Bartlomiej Polot | ||
23 | * @author Maximilian Szengel | ||
24 | * | ||
25 | */ | ||
26 | |||
27 | #include <string.h> | ||
28 | |||
29 | #include "platform.h" | ||
30 | #include "gnunet_applications.h" | ||
31 | #include "gnunet_util_lib.h" | ||
32 | #include "regex_internal_lib.h" | ||
33 | #include "gnunet_arm_service.h" | ||
34 | #include "gnunet_dht_service.h" | ||
35 | #include "gnunet_testbed_service.h" | ||
36 | |||
37 | #define FIND_TIMEOUT \ | ||
38 | GNUNET_TIME_relative_multiply(GNUNET_TIME_UNIT_SECONDS, 90) | ||
39 | |||
40 | /** | ||
41 | * DLL of operations | ||
42 | */ | ||
43 | struct DLLOperation | ||
44 | { | ||
45 | /** | ||
46 | * The testbed operation handle | ||
47 | */ | ||
48 | struct GNUNET_TESTBED_Operation *op; | ||
49 | |||
50 | /** | ||
51 | * Closure | ||
52 | */ | ||
53 | void *cls; | ||
54 | |||
55 | /** | ||
56 | * The next pointer for DLL | ||
57 | */ | ||
58 | struct DLLOperation *next; | ||
59 | |||
60 | /** | ||
61 | * The prev pointer for DLL | ||
62 | */ | ||
63 | struct DLLOperation *prev; | ||
64 | }; | ||
65 | |||
66 | |||
67 | /** | ||
68 | * Available states during profiling | ||
69 | */ | ||
70 | enum State | ||
71 | { | ||
72 | /** | ||
73 | * Initial state | ||
74 | */ | ||
75 | STATE_INIT = 0, | ||
76 | |||
77 | /** | ||
78 | * Starting slaves | ||
79 | */ | ||
80 | STATE_SLAVES_STARTING, | ||
81 | |||
82 | /** | ||
83 | * Creating peers | ||
84 | */ | ||
85 | STATE_PEERS_CREATING, | ||
86 | |||
87 | /** | ||
88 | * Starting peers | ||
89 | */ | ||
90 | STATE_PEERS_STARTING, | ||
91 | |||
92 | /** | ||
93 | * Linking peers | ||
94 | */ | ||
95 | STATE_PEERS_LINKING, | ||
96 | |||
97 | /** | ||
98 | * Matching strings against announced regexes | ||
99 | */ | ||
100 | STATE_SEARCH_REGEX, | ||
101 | |||
102 | /** | ||
103 | * Destroying peers; we can do this as the controller takes care of stopping a | ||
104 | * peer if it is running | ||
105 | */ | ||
106 | STATE_PEERS_DESTROYING | ||
107 | }; | ||
108 | |||
109 | |||
110 | /** | ||
111 | * Peer handles. | ||
112 | */ | ||
113 | struct RegexPeer | ||
114 | { | ||
115 | /** | ||
116 | * Peer id. | ||
117 | */ | ||
118 | unsigned int id; | ||
119 | |||
120 | /** | ||
121 | * Peer configuration handle. | ||
122 | */ | ||
123 | struct GNUNET_CONFIGURATION_Handle *cfg; | ||
124 | |||
125 | /** | ||
126 | * The actual testbed peer handle. | ||
127 | */ | ||
128 | struct GNUNET_TESTBED_Peer *peer_handle; | ||
129 | |||
130 | /** | ||
131 | * Peer's search string. | ||
132 | */ | ||
133 | const char *search_str; | ||
134 | |||
135 | /** | ||
136 | * Set to GNUNET_YES if the peer successfully matched the above | ||
137 | * search string. GNUNET_NO if the string could not be matched | ||
138 | * during the profiler run. GNUNET_SYSERR if the string matching | ||
139 | * timed out. Undefined if search_str is NULL | ||
140 | */ | ||
141 | int search_str_matched; | ||
142 | |||
143 | /** | ||
144 | * Peer's DHT handle. | ||
145 | */ | ||
146 | struct GNUNET_DHT_Handle *dht_handle; | ||
147 | |||
148 | /** | ||
149 | * Handle to a running regex search. | ||
150 | */ | ||
151 | struct REGEX_INTERNAL_Search *search_handle; | ||
152 | |||
153 | /** | ||
154 | * Testbed operation handle for DHT. | ||
155 | */ | ||
156 | struct GNUNET_TESTBED_Operation *op_handle; | ||
157 | |||
158 | /** | ||
159 | * Peers's statistics handle. | ||
160 | */ | ||
161 | struct GNUNET_STATISTICS_Handle *stats_handle; | ||
162 | |||
163 | /** | ||
164 | * The starting time of a profiling step. | ||
165 | */ | ||
166 | struct GNUNET_TIME_Absolute prof_start_time; | ||
167 | |||
168 | /** | ||
169 | * Operation timeout | ||
170 | */ | ||
171 | struct GNUNET_SCHEDULER_Task * timeout; | ||
172 | |||
173 | /** | ||
174 | * Deamon start | ||
175 | */ | ||
176 | struct GNUNET_TESTBED_Operation *daemon_op; | ||
177 | }; | ||
178 | |||
179 | /** | ||
180 | * Set when shutting down to avoid making more queries. | ||
181 | */ | ||
182 | static int in_shutdown; | ||
183 | |||
184 | /** | ||
185 | * The array of peers; we fill this as the peers are given to us by the testbed | ||
186 | */ | ||
187 | static struct RegexPeer *peers; | ||
188 | |||
189 | /** | ||
190 | * Host registration handle | ||
191 | */ | ||
192 | static struct GNUNET_TESTBED_HostRegistrationHandle *reg_handle; | ||
193 | |||
194 | /** | ||
195 | * Handle to the master controller process | ||
196 | */ | ||
197 | static struct GNUNET_TESTBED_ControllerProc *mc_proc; | ||
198 | |||
199 | /** | ||
200 | * Handle to the master controller | ||
201 | */ | ||
202 | static struct GNUNET_TESTBED_Controller *mc; | ||
203 | |||
204 | /** | ||
205 | * Handle to global configuration | ||
206 | */ | ||
207 | static struct GNUNET_CONFIGURATION_Handle *cfg; | ||
208 | |||
209 | /** | ||
210 | * Abort task identifier | ||
211 | */ | ||
212 | static struct GNUNET_SCHEDULER_Task * abort_task; | ||
213 | |||
214 | /** | ||
215 | * Host registration task identifier | ||
216 | */ | ||
217 | static struct GNUNET_SCHEDULER_Task * register_hosts_task; | ||
218 | |||
219 | /** | ||
220 | * Global event mask for all testbed events | ||
221 | */ | ||
222 | static uint64_t event_mask; | ||
223 | |||
224 | /** | ||
225 | * The starting time of a profiling step | ||
226 | */ | ||
227 | static struct GNUNET_TIME_Absolute prof_start_time; | ||
228 | |||
229 | /** | ||
230 | * Duration profiling step has taken | ||
231 | */ | ||
232 | static struct GNUNET_TIME_Relative prof_time; | ||
233 | |||
234 | /** | ||
235 | * Number of peers to be started by the profiler | ||
236 | */ | ||
237 | static unsigned int num_peers; | ||
238 | |||
239 | /** | ||
240 | * Global testing status | ||
241 | */ | ||
242 | static int result; | ||
243 | |||
244 | /** | ||
245 | * current state of profiling | ||
246 | */ | ||
247 | enum State state; | ||
248 | |||
249 | /** | ||
250 | * Folder where policy files are stored. | ||
251 | */ | ||
252 | static char * policy_dir; | ||
253 | |||
254 | /** | ||
255 | * File with hostnames where to execute the test. | ||
256 | */ | ||
257 | static char *hosts_file; | ||
258 | |||
259 | /** | ||
260 | * File with the strings to look for. | ||
261 | */ | ||
262 | static char *strings_file; | ||
263 | |||
264 | /** | ||
265 | * Search strings (num_peers of them). | ||
266 | */ | ||
267 | static char **search_strings; | ||
268 | |||
269 | /** | ||
270 | * How many searches are we going to start in parallel | ||
271 | */ | ||
272 | static long long unsigned int init_parallel_searches; | ||
273 | |||
274 | /** | ||
275 | * How many searches are running in parallel | ||
276 | */ | ||
277 | static unsigned int parallel_searches; | ||
278 | |||
279 | /** | ||
280 | * Number of strings found in the published regexes. | ||
281 | */ | ||
282 | static unsigned int strings_found; | ||
283 | |||
284 | /** | ||
285 | * Index of peer to start next announce/search. | ||
286 | */ | ||
287 | static unsigned int next_search; | ||
288 | |||
289 | /** | ||
290 | * Search timeout task identifier. | ||
291 | */ | ||
292 | static struct GNUNET_SCHEDULER_Task * search_timeout_task; | ||
293 | |||
294 | /** | ||
295 | * Search timeout in seconds. | ||
296 | */ | ||
297 | static struct GNUNET_TIME_Relative search_timeout_time = { 60000 }; | ||
298 | |||
299 | /** | ||
300 | * File to log statistics to. | ||
301 | */ | ||
302 | static struct GNUNET_DISK_FileHandle *data_file; | ||
303 | |||
304 | /** | ||
305 | * Filename to log statistics to. | ||
306 | */ | ||
307 | static char *data_filename; | ||
308 | |||
309 | /** | ||
310 | * Prefix used for regex announcing. We need to prefix the search | ||
311 | * strings with it, in order to find something. | ||
312 | */ | ||
313 | static char * regex_prefix; | ||
314 | |||
315 | /** | ||
316 | * What's the maximum regex reannounce period. | ||
317 | */ | ||
318 | static struct GNUNET_TIME_Relative reannounce_period_max; | ||
319 | |||
320 | |||
321 | /******************************************************************************/ | ||
322 | /****************************** DECLARATIONS ********************************/ | ||
323 | /******************************************************************************/ | ||
324 | |||
325 | /** | ||
326 | * DHT connect callback. | ||
327 | * | ||
328 | * @param cls internal peer id. | ||
329 | * @param op operation handle. | ||
330 | * @param ca_result connect adapter result. | ||
331 | * @param emsg error message. | ||
332 | */ | ||
333 | static void | ||
334 | dht_connect_cb (void *cls, struct GNUNET_TESTBED_Operation *op, | ||
335 | void *ca_result, const char *emsg); | ||
336 | |||
337 | /** | ||
338 | * DHT connect adapter. | ||
339 | * | ||
340 | * @param cls not used. | ||
341 | * @param cfg configuration handle. | ||
342 | * | ||
343 | * @return | ||
344 | */ | ||
345 | static void * | ||
346 | dht_ca (void *cls, const struct GNUNET_CONFIGURATION_Handle *cfg); | ||
347 | |||
348 | |||
349 | /** | ||
350 | * Adapter function called to destroy a connection to | ||
351 | * the DHT service | ||
352 | * | ||
353 | * @param cls closure | ||
354 | * @param op_result service handle returned from the connect adapter | ||
355 | */ | ||
356 | static void | ||
357 | dht_da (void *cls, void *op_result); | ||
358 | |||
359 | |||
360 | /** | ||
361 | * Function called by testbed once we are connected to stats | ||
362 | * service. Get the statistics for the services of interest. | ||
363 | * | ||
364 | * @param cls the 'struct RegexPeer' for which we connected to stats | ||
365 | * @param op connect operation handle | ||
366 | * @param ca_result handle to stats service | ||
367 | * @param emsg error message on failure | ||
368 | */ | ||
369 | static void | ||
370 | stats_connect_cb (void *cls, | ||
371 | struct GNUNET_TESTBED_Operation *op, | ||
372 | void *ca_result, | ||
373 | const char *emsg); | ||
374 | |||
375 | |||
376 | /** | ||
377 | * Start announcing the next regex in the DHT. | ||
378 | * | ||
379 | * @param cls Index of the next peer in the peers array. | ||
380 | */ | ||
381 | static void | ||
382 | announce_next_regex (void *cls); | ||
383 | |||
384 | |||
385 | /******************************************************************************/ | ||
386 | /******************************** SHUTDOWN **********************************/ | ||
387 | /******************************************************************************/ | ||
388 | |||
389 | |||
390 | /** | ||
391 | * Shutdown nicely | ||
392 | * | ||
393 | * @param cls NULL | ||
394 | */ | ||
395 | static void | ||
396 | do_shutdown (void *cls) | ||
397 | { | ||
398 | struct RegexPeer *peer; | ||
399 | unsigned int peer_cnt; | ||
400 | unsigned int search_str_cnt; | ||
401 | char output_buffer[512]; | ||
402 | size_t size; | ||
403 | |||
404 | if (NULL != abort_task) | ||
405 | { | ||
406 | GNUNET_SCHEDULER_cancel (abort_task); | ||
407 | abort_task = NULL; | ||
408 | } | ||
409 | if (NULL != register_hosts_task) | ||
410 | { | ||
411 | GNUNET_SCHEDULER_cancel (register_hosts_task); | ||
412 | register_hosts_task = NULL; | ||
413 | } | ||
414 | for (peer_cnt = 0; peer_cnt < num_peers; peer_cnt++) | ||
415 | { | ||
416 | peer = &peers[peer_cnt]; | ||
417 | |||
418 | if (GNUNET_YES != peer->search_str_matched && NULL != data_file) | ||
419 | { | ||
420 | prof_time = GNUNET_TIME_absolute_get_duration (peer->prof_start_time); | ||
421 | size = | ||
422 | GNUNET_snprintf (output_buffer, | ||
423 | sizeof (output_buffer), | ||
424 | "%p Search string not found: %s (%d)\n" | ||
425 | "%p On peer: %u (%p)\n" | ||
426 | "%p After: %s\n", | ||
427 | peer, peer->search_str, peer->search_str_matched, | ||
428 | peer, peer->id, peer, | ||
429 | peer, | ||
430 | GNUNET_STRINGS_relative_time_to_string (prof_time, | ||
431 | GNUNET_NO)); | ||
432 | if (size != GNUNET_DISK_file_write (data_file, output_buffer, size)) | ||
433 | GNUNET_log (GNUNET_ERROR_TYPE_WARNING, "Unable to write to file!\n"); | ||
434 | } | ||
435 | |||
436 | if (NULL != peers[peer_cnt].op_handle) | ||
437 | GNUNET_TESTBED_operation_done (peers[peer_cnt].op_handle); | ||
438 | } | ||
439 | |||
440 | if (NULL != data_file) | ||
441 | { | ||
442 | GNUNET_DISK_file_close (data_file); | ||
443 | data_file = NULL; | ||
444 | } | ||
445 | for (search_str_cnt = 0; | ||
446 | search_str_cnt < num_peers && NULL != search_strings; | ||
447 | search_str_cnt++) | ||
448 | { | ||
449 | GNUNET_free_non_null (search_strings[search_str_cnt]); | ||
450 | } | ||
451 | GNUNET_free_non_null (search_strings); | ||
452 | search_strings = NULL; | ||
453 | |||
454 | if (NULL != reg_handle) | ||
455 | { | ||
456 | GNUNET_TESTBED_cancel_registration (reg_handle); | ||
457 | reg_handle = NULL; | ||
458 | } | ||
459 | if (NULL != mc) | ||
460 | { | ||
461 | GNUNET_TESTBED_controller_disconnect (mc); | ||
462 | mc = NULL; | ||
463 | } | ||
464 | if (NULL != mc_proc) | ||
465 | { | ||
466 | GNUNET_TESTBED_controller_stop (mc_proc); | ||
467 | mc_proc = NULL; | ||
468 | } | ||
469 | if (NULL != cfg) | ||
470 | { | ||
471 | GNUNET_CONFIGURATION_destroy (cfg); | ||
472 | cfg = NULL; | ||
473 | } | ||
474 | } | ||
475 | |||
476 | |||
477 | /** | ||
478 | * abort task to run on test timed out | ||
479 | * | ||
480 | * @param cls NULL | ||
481 | */ | ||
482 | static void | ||
483 | do_abort (void *cls) | ||
484 | { | ||
485 | unsigned long i = (unsigned long) cls; | ||
486 | |||
487 | GNUNET_log (GNUNET_ERROR_TYPE_WARNING, | ||
488 | "Aborting from line %lu...\n", i); | ||
489 | abort_task = NULL; | ||
490 | result = GNUNET_SYSERR; | ||
491 | GNUNET_SCHEDULER_add_now (&do_shutdown, NULL); | ||
492 | } | ||
493 | |||
494 | |||
495 | /******************************************************************************/ | ||
496 | /********************* STATISTICS SERVICE CONNECTIONS ***********************/ | ||
497 | /******************************************************************************/ | ||
498 | |||
499 | /** | ||
500 | * Adapter function called to establish a connection to | ||
501 | * statistics service. | ||
502 | * | ||
503 | * @param cls closure | ||
504 | * @param cfg configuration of the peer to connect to; will be available until | ||
505 | * GNUNET_TESTBED_operation_done() is called on the operation returned | ||
506 | * from GNUNET_TESTBED_service_connect() | ||
507 | * @return service handle to return in 'op_result', NULL on error | ||
508 | */ | ||
509 | static void * | ||
510 | stats_ca (void *cls, | ||
511 | const struct GNUNET_CONFIGURATION_Handle *cfg) | ||
512 | { | ||
513 | return GNUNET_STATISTICS_create ("<driver>", cfg); | ||
514 | } | ||
515 | |||
516 | |||
517 | /** | ||
518 | * Adapter function called to destroy a connection to | ||
519 | * statistics service. | ||
520 | * | ||
521 | * @param cls closure | ||
522 | * @param op_result service handle returned from the connect adapter | ||
523 | */ | ||
524 | static void | ||
525 | stats_da (void *cls, void *op_result) | ||
526 | { | ||
527 | struct RegexPeer *peer = cls; | ||
528 | |||
529 | GNUNET_assert (op_result == peer->stats_handle); | ||
530 | |||
531 | GNUNET_STATISTICS_destroy (peer->stats_handle, GNUNET_NO); | ||
532 | peer->stats_handle = NULL; | ||
533 | } | ||
534 | |||
535 | |||
536 | /** | ||
537 | * Process statistic values. Write all values to global 'data_file', if present. | ||
538 | * | ||
539 | * @param cls closure | ||
540 | * @param subsystem name of subsystem that created the statistic | ||
541 | * @param name the name of the datum | ||
542 | * @param value the current value | ||
543 | * @param is_persistent GNUNET_YES if the value is persistent, GNUNET_NO if not | ||
544 | * @return #GNUNET_OK to continue, #GNUNET_SYSERR to abort iteration | ||
545 | */ | ||
546 | static int | ||
547 | stats_iterator (void *cls, | ||
548 | const char *subsystem, | ||
549 | const char *name, | ||
550 | uint64_t value, int is_persistent) | ||
551 | { | ||
552 | struct RegexPeer *peer = cls; | ||
553 | char output_buffer[512]; | ||
554 | size_t size; | ||
555 | |||
556 | if (NULL == data_file) | ||
557 | { | ||
558 | GNUNET_log (GNUNET_ERROR_TYPE_INFO, | ||
559 | "%p -> %s [%s]: %llu\n", | ||
560 | peer, | ||
561 | subsystem, | ||
562 | name, | ||
563 | (unsigned long long) value); | ||
564 | return GNUNET_OK; | ||
565 | } | ||
566 | size = | ||
567 | GNUNET_snprintf (output_buffer, | ||
568 | sizeof (output_buffer), | ||
569 | "%p [%s] %llu %s\n", | ||
570 | peer, | ||
571 | subsystem, value, name); | ||
572 | if (size != GNUNET_DISK_file_write (data_file, output_buffer, size)) | ||
573 | GNUNET_log (GNUNET_ERROR_TYPE_WARNING, | ||
574 | "Unable to write to file!\n"); | ||
575 | |||
576 | return GNUNET_OK; | ||
577 | } | ||
578 | |||
579 | |||
580 | /** | ||
581 | * Stats callback. Finish the stats testbed operation and when all stats have | ||
582 | * been iterated, shutdown the profiler. | ||
583 | * | ||
584 | * @param cls closure | ||
585 | * @param success GNUNET_OK if statistics were | ||
586 | * successfully obtained, GNUNET_SYSERR if not. | ||
587 | */ | ||
588 | static void | ||
589 | stats_cb (void *cls, | ||
590 | int success) | ||
591 | { | ||
592 | static unsigned int peer_cnt; | ||
593 | struct RegexPeer *peer = cls; | ||
594 | |||
595 | if (GNUNET_OK != success) | ||
596 | { | ||
597 | GNUNET_log (GNUNET_ERROR_TYPE_ERROR, | ||
598 | "Getting statistics for peer %u failed!\n", | ||
599 | peer->id); | ||
600 | return; | ||
601 | } | ||
602 | |||
603 | GNUNET_assert (NULL != peer->op_handle); | ||
604 | |||
605 | GNUNET_TESTBED_operation_done (peer->op_handle); | ||
606 | peer->op_handle = NULL; | ||
607 | |||
608 | peer_cnt++; | ||
609 | peer = &peers[peer_cnt]; | ||
610 | |||
611 | fprintf (stderr, "s"); | ||
612 | if (peer_cnt == num_peers) | ||
613 | { | ||
614 | GNUNET_log (GNUNET_ERROR_TYPE_INFO, | ||
615 | "\nCollecting stats finished. Shutting down.\n"); | ||
616 | GNUNET_SCHEDULER_shutdown (); | ||
617 | result = GNUNET_OK; | ||
618 | } | ||
619 | else | ||
620 | { | ||
621 | peer->op_handle = | ||
622 | GNUNET_TESTBED_service_connect (NULL, | ||
623 | peer->peer_handle, | ||
624 | "statistics", | ||
625 | &stats_connect_cb, | ||
626 | peer, | ||
627 | &stats_ca, | ||
628 | &stats_da, | ||
629 | peer); | ||
630 | } | ||
631 | } | ||
632 | |||
633 | |||
634 | /** | ||
635 | * Function called by testbed once we are connected to stats | ||
636 | * service. Get the statistics for the services of interest. | ||
637 | * | ||
638 | * @param cls the 'struct RegexPeer' for which we connected to stats | ||
639 | * @param op connect operation handle | ||
640 | * @param ca_result handle to stats service | ||
641 | * @param emsg error message on failure | ||
642 | */ | ||
643 | static void | ||
644 | stats_connect_cb (void *cls, | ||
645 | struct GNUNET_TESTBED_Operation *op, | ||
646 | void *ca_result, | ||
647 | const char *emsg) | ||
648 | { | ||
649 | struct RegexPeer *peer = cls; | ||
650 | |||
651 | if (NULL == ca_result || NULL != emsg) | ||
652 | { | ||
653 | GNUNET_log (GNUNET_ERROR_TYPE_ERROR, | ||
654 | "Failed to connect to statistics service on peer %u: %s\n", | ||
655 | peer->id, emsg); | ||
656 | |||
657 | peer->stats_handle = NULL; | ||
658 | return; | ||
659 | } | ||
660 | |||
661 | peer->stats_handle = ca_result; | ||
662 | |||
663 | if (NULL == GNUNET_STATISTICS_get (peer->stats_handle, NULL, NULL, | ||
664 | &stats_cb, | ||
665 | &stats_iterator, peer)) | ||
666 | { | ||
667 | GNUNET_log (GNUNET_ERROR_TYPE_ERROR, | ||
668 | "Could not get statistics of peer %u!\n", peer->id); | ||
669 | } | ||
670 | } | ||
671 | |||
672 | |||
673 | /** | ||
674 | * Task to collect all statistics from all peers, will shutdown the | ||
675 | * profiler, when done. | ||
676 | * | ||
677 | * @param cls NULL | ||
678 | */ | ||
679 | static void | ||
680 | do_collect_stats (void *cls) | ||
681 | { | ||
682 | struct RegexPeer *peer = &peers[0]; | ||
683 | |||
684 | GNUNET_assert (NULL != peer->peer_handle); | ||
685 | |||
686 | peer->op_handle = | ||
687 | GNUNET_TESTBED_service_connect (NULL, | ||
688 | peer->peer_handle, | ||
689 | "statistics", | ||
690 | &stats_connect_cb, | ||
691 | peer, | ||
692 | &stats_ca, | ||
693 | &stats_da, | ||
694 | peer); | ||
695 | } | ||
696 | |||
697 | |||
698 | /******************************************************************************/ | ||
699 | /************************ REGEX FIND CONNECTIONS **************************/ | ||
700 | /******************************************************************************/ | ||
701 | |||
702 | |||
703 | /** | ||
704 | * Start searching for the next string in the DHT. | ||
705 | * | ||
706 | * @param cls Index of the next peer in the peers array. | ||
707 | */ | ||
708 | static void | ||
709 | find_string (void *cls); | ||
710 | |||
711 | |||
712 | /** | ||
713 | * Method called when we've found a peer that announced a regex | ||
714 | * that matches our search string. Now get the statistics. | ||
715 | * | ||
716 | * @param cls Closure provided in REGEX_INTERNAL_search. | ||
717 | * @param id Peer providing a regex that matches the string. | ||
718 | * @param get_path Path of the get request. | ||
719 | * @param get_path_length Lenght of get_path. | ||
720 | * @param put_path Path of the put request. | ||
721 | * @param put_path_length Length of the put_path. | ||
722 | */ | ||
723 | static void | ||
724 | regex_found_handler (void *cls, | ||
725 | const struct GNUNET_PeerIdentity *id, | ||
726 | const struct GNUNET_PeerIdentity *get_path, | ||
727 | unsigned int get_path_length, | ||
728 | const struct GNUNET_PeerIdentity *put_path, | ||
729 | unsigned int put_path_length) | ||
730 | { | ||
731 | struct RegexPeer *peer = cls; | ||
732 | char output_buffer[512]; | ||
733 | size_t size; | ||
734 | |||
735 | if (GNUNET_YES == peer->search_str_matched) | ||
736 | { | ||
737 | GNUNET_log (GNUNET_ERROR_TYPE_ERROR, | ||
738 | "String %s on peer %u already matched!\n", | ||
739 | peer->search_str, peer->id); | ||
740 | return; | ||
741 | } | ||
742 | |||
743 | strings_found++; | ||
744 | parallel_searches--; | ||
745 | |||
746 | if (NULL != peer->timeout) | ||
747 | { | ||
748 | GNUNET_SCHEDULER_cancel (peer->timeout); | ||
749 | peer->timeout = NULL; | ||
750 | if (GNUNET_NO == in_shutdown) | ||
751 | GNUNET_SCHEDULER_add_now (&announce_next_regex, NULL); | ||
752 | } | ||
753 | |||
754 | if (NULL == id) | ||
755 | { | ||
756 | // FIXME not possible right now | ||
757 | GNUNET_log (GNUNET_ERROR_TYPE_WARNING, | ||
758 | "String matching timed out for string %s on peer %u (%i/%i)\n", | ||
759 | peer->search_str, peer->id, strings_found, num_peers); | ||
760 | peer->search_str_matched = GNUNET_SYSERR; | ||
761 | } | ||
762 | else | ||
763 | { | ||
764 | prof_time = GNUNET_TIME_absolute_get_duration (peer->prof_start_time); | ||
765 | |||
766 | GNUNET_log (GNUNET_ERROR_TYPE_INFO, | ||
767 | "String %s found on peer %u after %s (%i/%i) (%u||)\n", | ||
768 | peer->search_str, peer->id, | ||
769 | GNUNET_STRINGS_relative_time_to_string (prof_time, GNUNET_NO), | ||
770 | strings_found, num_peers, parallel_searches); | ||
771 | |||
772 | peer->search_str_matched = GNUNET_YES; | ||
773 | |||
774 | if (NULL != data_file) | ||
775 | { | ||
776 | size = | ||
777 | GNUNET_snprintf (output_buffer, | ||
778 | sizeof (output_buffer), | ||
779 | "%p Peer: %u\n" | ||
780 | "%p Search string: %s\n" | ||
781 | "%p Search duration: %s\n\n", | ||
782 | peer, peer->id, | ||
783 | peer, peer->search_str, | ||
784 | peer, | ||
785 | GNUNET_STRINGS_relative_time_to_string (prof_time, | ||
786 | GNUNET_NO)); | ||
787 | |||
788 | if (size != GNUNET_DISK_file_write (data_file, output_buffer, size)) | ||
789 | GNUNET_log (GNUNET_ERROR_TYPE_WARNING, "Unable to write to file!\n"); | ||
790 | } | ||
791 | } | ||
792 | |||
793 | GNUNET_TESTBED_operation_done (peer->op_handle); | ||
794 | peer->op_handle = NULL; | ||
795 | |||
796 | if (strings_found == num_peers) | ||
797 | { | ||
798 | prof_time = GNUNET_TIME_absolute_get_duration (prof_start_time); | ||
799 | GNUNET_log (GNUNET_ERROR_TYPE_INFO, | ||
800 | "All strings successfully matched in %s\n", | ||
801 | GNUNET_STRINGS_relative_time_to_string (prof_time, GNUNET_NO)); | ||
802 | |||
803 | if (NULL != search_timeout_task) | ||
804 | { | ||
805 | GNUNET_SCHEDULER_cancel (search_timeout_task); | ||
806 | search_timeout_task = NULL; | ||
807 | } | ||
808 | |||
809 | GNUNET_log (GNUNET_ERROR_TYPE_INFO, "Collecting stats.\n"); | ||
810 | GNUNET_SCHEDULER_add_now (&do_collect_stats, NULL); | ||
811 | } | ||
812 | } | ||
813 | |||
814 | |||
815 | /** | ||
816 | * Connect by string timeout task. This will cancel the profiler after the | ||
817 | * specified timeout 'search_timeout'. | ||
818 | * | ||
819 | * @param cls NULL | ||
820 | */ | ||
821 | static void | ||
822 | search_timed_out (void *cls) | ||
823 | { | ||
824 | unsigned int i; | ||
825 | |||
826 | GNUNET_log (GNUNET_ERROR_TYPE_INFO, | ||
827 | "Finding matches to all strings did not succeed after %s.\n", | ||
828 | GNUNET_STRINGS_relative_time_to_string (search_timeout_time, | ||
829 | GNUNET_NO)); | ||
830 | GNUNET_log (GNUNET_ERROR_TYPE_INFO, | ||
831 | "Found %i of %i strings\n", strings_found, num_peers); | ||
832 | |||
833 | GNUNET_log (GNUNET_ERROR_TYPE_INFO, | ||
834 | "Search timed out after %s." | ||
835 | "Collecting stats and shutting down.\n", | ||
836 | GNUNET_STRINGS_relative_time_to_string (search_timeout_time, | ||
837 | GNUNET_NO)); | ||
838 | |||
839 | in_shutdown = GNUNET_YES; | ||
840 | for (i = 0; i < num_peers; i++) | ||
841 | { | ||
842 | if (NULL != peers[i].op_handle) | ||
843 | { | ||
844 | GNUNET_TESTBED_operation_done (peers[i].op_handle); | ||
845 | peers[i].op_handle = NULL; | ||
846 | } | ||
847 | } | ||
848 | GNUNET_SCHEDULER_add_now (&do_collect_stats, NULL); | ||
849 | } | ||
850 | |||
851 | |||
852 | /** | ||
853 | * Search timed out. It might still complete in the future, | ||
854 | * but we should start another one. | ||
855 | * | ||
856 | * @param cls Index of the next peer in the peers array. | ||
857 | */ | ||
858 | static void | ||
859 | find_timed_out (void *cls) | ||
860 | { | ||
861 | struct RegexPeer *p = cls; | ||
862 | |||
863 | p->timeout = NULL; | ||
864 | GNUNET_log (GNUNET_ERROR_TYPE_WARNING, | ||
865 | "Searching for string \"%s\" on peer %d timed out.\n", | ||
866 | p->search_str, | ||
867 | p->id); | ||
868 | if (GNUNET_NO == in_shutdown) | ||
869 | GNUNET_SCHEDULER_add_now (&announce_next_regex, NULL); | ||
870 | } | ||
871 | |||
872 | |||
873 | /** | ||
874 | * Start searching for a string in the DHT. | ||
875 | * | ||
876 | * @param cls Index of the next peer in the peers array. | ||
877 | */ | ||
878 | static void | ||
879 | find_string (void *cls) | ||
880 | { | ||
881 | unsigned int search_peer = (unsigned int) (long) cls; | ||
882 | |||
883 | if ( (search_peer >= num_peers) || | ||
884 | (GNUNET_YES == in_shutdown) ) | ||
885 | return; | ||
886 | |||
887 | GNUNET_log (GNUNET_ERROR_TYPE_INFO, | ||
888 | "Searching for string \"%s\" on peer %d (%u||)\n", | ||
889 | peers[search_peer].search_str, | ||
890 | search_peer, | ||
891 | parallel_searches); | ||
892 | |||
893 | peers[search_peer].op_handle = | ||
894 | GNUNET_TESTBED_service_connect (NULL, | ||
895 | peers[search_peer].peer_handle, | ||
896 | "dht", | ||
897 | &dht_connect_cb, | ||
898 | &peers[search_peer], | ||
899 | &dht_ca, | ||
900 | &dht_da, | ||
901 | &peers[search_peer]); | ||
902 | GNUNET_assert (NULL != peers[search_peer].op_handle); | ||
903 | peers[search_peer].timeout | ||
904 | = GNUNET_SCHEDULER_add_delayed (FIND_TIMEOUT, | ||
905 | &find_timed_out, | ||
906 | &peers[search_peer]); | ||
907 | } | ||
908 | |||
909 | |||
910 | /** | ||
911 | * Callback called when testbed has started the daemon we asked for. | ||
912 | * | ||
913 | * @param cls NULL | ||
914 | * @param op the operation handle | ||
915 | * @param emsg NULL on success; otherwise an error description | ||
916 | */ | ||
917 | static void | ||
918 | daemon_started (void *cls, | ||
919 | struct GNUNET_TESTBED_Operation *op, | ||
920 | const char *emsg) | ||
921 | { | ||
922 | struct RegexPeer *peer = (struct RegexPeer *) cls; | ||
923 | unsigned long search_peer; | ||
924 | unsigned int i; | ||
925 | |||
926 | GNUNET_TESTBED_operation_done (peer->daemon_op); | ||
927 | peer->daemon_op = NULL; | ||
928 | if (NULL != emsg) | ||
929 | { | ||
930 | GNUNET_log (GNUNET_ERROR_TYPE_WARNING, | ||
931 | "Failed to start/stop daemon at peer %u: %s\n", peer->id, emsg); | ||
932 | GNUNET_assert (0); | ||
933 | } | ||
934 | else | ||
935 | { | ||
936 | GNUNET_log (GNUNET_ERROR_TYPE_INFO, | ||
937 | "Deamon %u started successfully\n", peer->id); | ||
938 | } | ||
939 | |||
940 | /* Find a peer to look for a string matching the regex announced */ | ||
941 | search_peer = GNUNET_CRYPTO_random_u32 (GNUNET_CRYPTO_QUALITY_WEAK, | ||
942 | num_peers); | ||
943 | for (i = 0; peers[search_peer].search_str != NULL; i++) | ||
944 | { | ||
945 | search_peer = (search_peer + 1) % num_peers; | ||
946 | if (i > num_peers) | ||
947 | GNUNET_assert (0); /* we ran out of peers, must be a bug */ | ||
948 | } | ||
949 | peers[search_peer].search_str = search_strings[peer->id]; | ||
950 | peers[search_peer].search_str_matched = GNUNET_NO; | ||
951 | GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_relative_saturating_multiply( | ||
952 | reannounce_period_max, | ||
953 | 2), | ||
954 | &find_string, | ||
955 | (void *) search_peer); | ||
956 | } | ||
957 | |||
958 | |||
959 | /** | ||
960 | * Task to start the daemons on each peer so that the regexes are announced | ||
961 | * into the DHT. | ||
962 | * | ||
963 | * @param cls NULL | ||
964 | * @param tc the task context | ||
965 | */ | ||
966 | static void | ||
967 | do_announce (void *cls) | ||
968 | { | ||
969 | unsigned int i; | ||
970 | |||
971 | if (GNUNET_YES == in_shutdown) | ||
972 | return; | ||
973 | GNUNET_log (GNUNET_ERROR_TYPE_INFO, | ||
974 | "Starting announce.\n"); | ||
975 | for (i = 0; i < init_parallel_searches; i++) | ||
976 | { | ||
977 | GNUNET_log (GNUNET_ERROR_TYPE_INFO, | ||
978 | " scheduling announce %u\n", | ||
979 | i); | ||
980 | (void) GNUNET_SCHEDULER_add_now (&announce_next_regex, NULL); | ||
981 | } | ||
982 | } | ||
983 | |||
984 | |||
985 | /** | ||
986 | * Start announcing the next regex in the DHT. | ||
987 | * | ||
988 | * @param cls Closure (unused). | ||
989 | */ | ||
990 | static void | ||
991 | announce_next_regex (void *cls) | ||
992 | { | ||
993 | struct RegexPeer *peer; | ||
994 | |||
995 | if (GNUNET_YES == in_shutdown) | ||
996 | return; | ||
997 | if (next_search >= num_peers) | ||
998 | { | ||
999 | if (strings_found != num_peers) | ||
1000 | { | ||
1001 | struct GNUNET_TIME_Relative new_delay; | ||
1002 | if (NULL != search_timeout_task) | ||
1003 | GNUNET_SCHEDULER_cancel (search_timeout_task); | ||
1004 | new_delay = GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_MINUTES, 15); | ||
1005 | search_timeout_task = GNUNET_SCHEDULER_add_delayed (new_delay, | ||
1006 | &search_timed_out, | ||
1007 | NULL); | ||
1008 | } | ||
1009 | return; | ||
1010 | } | ||
1011 | |||
1012 | GNUNET_log (GNUNET_ERROR_TYPE_INFO, "Starting daemon %u\n", next_search); | ||
1013 | peer = &peers[next_search]; | ||
1014 | peer->daemon_op = | ||
1015 | GNUNET_TESTBED_peer_manage_service (NULL, | ||
1016 | peer->peer_handle, | ||
1017 | "regexprofiler", | ||
1018 | &daemon_started, | ||
1019 | peer, | ||
1020 | 1); | ||
1021 | next_search++; | ||
1022 | parallel_searches++; | ||
1023 | } | ||
1024 | |||
1025 | |||
1026 | /** | ||
1027 | * DHT connect callback. Called when we are connected to the dht service for | ||
1028 | * the peer in 'cls'. If successfull we connect to the stats service of this | ||
1029 | * peer and then try to match the search string of this peer. | ||
1030 | * | ||
1031 | * @param cls internal peer id. | ||
1032 | * @param op operation handle. | ||
1033 | * @param ca_result connect adapter result. | ||
1034 | * @param emsg error message. | ||
1035 | */ | ||
1036 | static void | ||
1037 | dht_connect_cb (void *cls, | ||
1038 | struct GNUNET_TESTBED_Operation *op, | ||
1039 | void *ca_result, | ||
1040 | const char *emsg) | ||
1041 | { | ||
1042 | struct RegexPeer *peer = (struct RegexPeer *) cls; | ||
1043 | |||
1044 | if (NULL != emsg || NULL == op || NULL == ca_result) | ||
1045 | { | ||
1046 | GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "DHT connect failed: %s\n", emsg); | ||
1047 | GNUNET_assert (0); | ||
1048 | } | ||
1049 | |||
1050 | GNUNET_assert (NULL != peer->dht_handle); | ||
1051 | GNUNET_assert (peer->op_handle == op); | ||
1052 | GNUNET_assert (peer->dht_handle == ca_result); | ||
1053 | |||
1054 | peer->search_str_matched = GNUNET_NO; | ||
1055 | peer->search_handle = REGEX_INTERNAL_search (peer->dht_handle, | ||
1056 | peer->search_str, | ||
1057 | ®ex_found_handler, peer, | ||
1058 | NULL); | ||
1059 | peer->prof_start_time = GNUNET_TIME_absolute_get (); | ||
1060 | } | ||
1061 | |||
1062 | |||
1063 | /** | ||
1064 | * DHT connect adapter. Opens a connection to the dht service. | ||
1065 | * | ||
1066 | * @param cls Closure (peer). | ||
1067 | * @param cfg Configuration handle. | ||
1068 | * | ||
1069 | * @return | ||
1070 | */ | ||
1071 | static void * | ||
1072 | dht_ca (void *cls, const struct GNUNET_CONFIGURATION_Handle *cfg) | ||
1073 | { | ||
1074 | struct RegexPeer *peer = cls; | ||
1075 | |||
1076 | peer->dht_handle = GNUNET_DHT_connect (cfg, 32); | ||
1077 | |||
1078 | return peer->dht_handle; | ||
1079 | } | ||
1080 | |||
1081 | |||
1082 | /** | ||
1083 | * Adapter function called to destroy a connection to the dht service. | ||
1084 | * | ||
1085 | * @param cls Closure (peer). | ||
1086 | * @param op_result Service handle returned from the connect adapter. | ||
1087 | */ | ||
1088 | static void | ||
1089 | dht_da (void *cls, void *op_result) | ||
1090 | { | ||
1091 | struct RegexPeer *peer = (struct RegexPeer *) cls; | ||
1092 | |||
1093 | GNUNET_assert (peer->dht_handle == op_result); | ||
1094 | |||
1095 | if (NULL != peer->search_handle) | ||
1096 | { | ||
1097 | REGEX_INTERNAL_search_cancel (peer->search_handle); | ||
1098 | peer->search_handle = NULL; | ||
1099 | } | ||
1100 | |||
1101 | if (NULL != peer->dht_handle) | ||
1102 | { | ||
1103 | GNUNET_DHT_disconnect (peer->dht_handle); | ||
1104 | peer->dht_handle = NULL; | ||
1105 | } | ||
1106 | } | ||
1107 | |||
1108 | |||
1109 | /** | ||
1110 | * Signature of a main function for a testcase. | ||
1111 | * | ||
1112 | * @param cls NULL | ||
1113 | * @param h the run handle | ||
1114 | * @param num_peers_ number of peers in 'peers' | ||
1115 | * @param testbed_peers handle to peers run in the testbed. NULL upon timeout (see | ||
1116 | * GNUNET_TESTBED_test_run()). | ||
1117 | * @param links_succeeded the number of overlay link connection attempts that | ||
1118 | * succeeded | ||
1119 | * @param links_failed the number of overlay link connection attempts that | ||
1120 | * failed | ||
1121 | */ | ||
1122 | static void | ||
1123 | test_master (void *cls, | ||
1124 | struct GNUNET_TESTBED_RunHandle *h, | ||
1125 | unsigned int num_peers_, | ||
1126 | struct GNUNET_TESTBED_Peer **testbed_peers, | ||
1127 | unsigned int links_succeeded, | ||
1128 | unsigned int links_failed) | ||
1129 | { | ||
1130 | unsigned int i; | ||
1131 | |||
1132 | GNUNET_assert (num_peers_ == num_peers); | ||
1133 | |||
1134 | prof_time = GNUNET_TIME_absolute_get_duration (prof_start_time); | ||
1135 | GNUNET_log (GNUNET_ERROR_TYPE_INFO, | ||
1136 | "Testbed started in %s\n", | ||
1137 | GNUNET_STRINGS_relative_time_to_string (prof_time, GNUNET_NO)); | ||
1138 | |||
1139 | if (NULL != abort_task) | ||
1140 | { | ||
1141 | GNUNET_SCHEDULER_cancel (abort_task); | ||
1142 | abort_task = NULL; | ||
1143 | } | ||
1144 | |||
1145 | for (i = 0; i < num_peers; i++) | ||
1146 | { | ||
1147 | peers[i].peer_handle = testbed_peers[i]; | ||
1148 | } | ||
1149 | if (GNUNET_NO == | ||
1150 | GNUNET_CONFIGURATION_get_value_yesno (cfg, "DHT", "DISABLE_TRY_CONNECT")) | ||
1151 | { | ||
1152 | struct GNUNET_TIME_Relative settle_time; | ||
1153 | |||
1154 | settle_time = | ||
1155 | GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_MILLISECONDS, | ||
1156 | 10 * num_peers); | ||
1157 | GNUNET_log (GNUNET_ERROR_TYPE_INFO, | ||
1158 | "Waiting for DHT for %s to settle new connections.\n\n", | ||
1159 | GNUNET_STRINGS_relative_time_to_string(settle_time, GNUNET_NO)); | ||
1160 | GNUNET_SCHEDULER_add_delayed (settle_time, &do_announce, NULL); | ||
1161 | } | ||
1162 | else | ||
1163 | { | ||
1164 | GNUNET_SCHEDULER_add_now (&do_announce, NULL); | ||
1165 | } | ||
1166 | search_timeout_task = | ||
1167 | GNUNET_SCHEDULER_add_delayed (search_timeout_time, &search_timed_out, NULL); | ||
1168 | } | ||
1169 | |||
1170 | /** | ||
1171 | * Function that will be called whenever something in the testbed changes. | ||
1172 | * | ||
1173 | * @param cls closure, NULL | ||
1174 | * @param event information on what is happening | ||
1175 | */ | ||
1176 | static void | ||
1177 | master_controller_cb (void *cls, | ||
1178 | const struct GNUNET_TESTBED_EventInformation *event) | ||
1179 | { | ||
1180 | switch (event->type) | ||
1181 | { | ||
1182 | case GNUNET_TESTBED_ET_CONNECT: | ||
1183 | printf("."); | ||
1184 | break; | ||
1185 | case GNUNET_TESTBED_ET_PEER_START: | ||
1186 | printf("#"); | ||
1187 | break; | ||
1188 | default: | ||
1189 | break; | ||
1190 | } | ||
1191 | fflush(stdout); | ||
1192 | } | ||
1193 | |||
1194 | |||
1195 | /******************************************************************************/ | ||
1196 | /*************************** TESTBED PEER SETUP *****************************/ | ||
1197 | /******************************************************************************/ | ||
1198 | |||
1199 | /** | ||
1200 | * Process the text buffer counting the non-empty lines and separating them | ||
1201 | * with NULL characters, for later ease of copy using (as)printf. | ||
1202 | * | ||
1203 | * @param data Memory buffer with strings. | ||
1204 | * @param data_size Size of the @a data buffer in bytes. | ||
1205 | * @param str_max Maximum number of strings to return. | ||
1206 | * @return Positive number of lines found in the buffer, | ||
1207 | * #GNUNET_SYSERR otherwise. | ||
1208 | */ | ||
1209 | static int | ||
1210 | count_and_separate_strings (char *data, | ||
1211 | uint64_t data_size, | ||
1212 | unsigned int str_max) | ||
1213 | { | ||
1214 | char *buf; // Keep track of last string to skip blank lines | ||
1215 | unsigned int offset; | ||
1216 | unsigned int str_cnt; | ||
1217 | |||
1218 | buf = data; | ||
1219 | offset = 0; | ||
1220 | str_cnt = 0; | ||
1221 | while ( (offset < (data_size - 1)) && (str_cnt < str_max) ) | ||
1222 | { | ||
1223 | offset++; | ||
1224 | if ( ((data[offset] == '\n')) && | ||
1225 | (buf != &data[offset]) ) | ||
1226 | { | ||
1227 | data[offset] = '\0'; | ||
1228 | str_cnt++; | ||
1229 | buf = &data[offset + 1]; | ||
1230 | } | ||
1231 | else if ( (data[offset] == '\n') || | ||
1232 | (data[offset] == '\0') ) | ||
1233 | buf = &data[offset + 1]; | ||
1234 | } | ||
1235 | return str_cnt; | ||
1236 | } | ||
1237 | |||
1238 | |||
1239 | /** | ||
1240 | * Allocate a string array and fill it with the prefixed strings | ||
1241 | * from a pre-processed, NULL-separated memory region. | ||
1242 | * | ||
1243 | * @param data Preprocessed memory with strings | ||
1244 | * @param data_size Size of the @a data buffer in bytes. | ||
1245 | * @param strings Address of the string array to be created. | ||
1246 | * Must be freed by caller if function end in success. | ||
1247 | * @param str_cnt String count. The @a data buffer should contain | ||
1248 | * at least this many NULL-separated strings. | ||
1249 | * @return #GNUNET_OK in ase of success, #GNUNET_SYSERR otherwise. | ||
1250 | * In case of error @a strings must not be freed. | ||
1251 | */ | ||
1252 | static int | ||
1253 | create_string_array (char *data, uint64_t data_size, | ||
1254 | char ***strings, unsigned int str_cnt) | ||
1255 | { | ||
1256 | uint64_t offset; | ||
1257 | uint64_t len; | ||
1258 | unsigned int i; | ||
1259 | |||
1260 | *strings = GNUNET_malloc (sizeof (char *) * str_cnt); | ||
1261 | offset = 0; | ||
1262 | for (i = 0; i < str_cnt; i++) | ||
1263 | { | ||
1264 | len = strlen (&data[offset]); | ||
1265 | if (offset + len >= data_size) | ||
1266 | { | ||
1267 | GNUNET_free (*strings); | ||
1268 | *strings = NULL; | ||
1269 | return GNUNET_SYSERR; | ||
1270 | } | ||
1271 | if (0 == len) // empty line | ||
1272 | { | ||
1273 | offset++; | ||
1274 | i--; | ||
1275 | continue; | ||
1276 | } | ||
1277 | |||
1278 | GNUNET_asprintf (&(*strings)[i], | ||
1279 | "%s%s", | ||
1280 | regex_prefix, | ||
1281 | &data[offset]); | ||
1282 | offset += len + 1; | ||
1283 | } | ||
1284 | return GNUNET_OK; | ||
1285 | } | ||
1286 | |||
1287 | |||
1288 | /** | ||
1289 | * Load search strings from given filename. One search string per line. | ||
1290 | * | ||
1291 | * @param filename filename of the file containing the search strings. | ||
1292 | * @param strings set of strings loaded from file. Caller needs to free this | ||
1293 | * if number returned is greater than zero. | ||
1294 | * @param limit upper limit on the number of strings read from the file | ||
1295 | * @return number of strings found in the file. #GNUNET_SYSERR on error. | ||
1296 | */ | ||
1297 | static int | ||
1298 | load_search_strings (const char *filename, | ||
1299 | char ***strings, | ||
1300 | unsigned int limit) | ||
1301 | { | ||
1302 | char *data; | ||
1303 | uint64_t filesize; | ||
1304 | int str_cnt; | ||
1305 | |||
1306 | /* Sanity checks */ | ||
1307 | if (NULL == filename) | ||
1308 | { | ||
1309 | return GNUNET_SYSERR; | ||
1310 | } | ||
1311 | if (GNUNET_YES != GNUNET_DISK_file_test (filename)) | ||
1312 | { | ||
1313 | GNUNET_log (GNUNET_ERROR_TYPE_WARNING, | ||
1314 | "Could not find search strings file %s\n", filename); | ||
1315 | return GNUNET_SYSERR; | ||
1316 | } | ||
1317 | if (GNUNET_OK != | ||
1318 | GNUNET_DISK_file_size (filename, | ||
1319 | &filesize, | ||
1320 | GNUNET_YES, | ||
1321 | GNUNET_YES)) | ||
1322 | { | ||
1323 | GNUNET_log (GNUNET_ERROR_TYPE_WARNING, | ||
1324 | "Search strings file %s cannot be read.\n", | ||
1325 | filename); | ||
1326 | return GNUNET_SYSERR; | ||
1327 | } | ||
1328 | if (0 == filesize) | ||
1329 | { | ||
1330 | GNUNET_log (GNUNET_ERROR_TYPE_WARNING, | ||
1331 | "Search strings file %s is empty.\n", | ||
1332 | filename); | ||
1333 | return GNUNET_SYSERR; | ||
1334 | } | ||
1335 | |||
1336 | /* Read data into memory */ | ||
1337 | data = GNUNET_malloc (filesize + 1); | ||
1338 | if (filesize != GNUNET_DISK_fn_read (filename, | ||
1339 | data, | ||
1340 | filesize)) | ||
1341 | { | ||
1342 | GNUNET_free (data); | ||
1343 | GNUNET_log (GNUNET_ERROR_TYPE_WARNING, | ||
1344 | "Could not read search strings file %s.\n", | ||
1345 | filename); | ||
1346 | return GNUNET_SYSERR; | ||
1347 | } | ||
1348 | |||
1349 | /* Process buffer and build array */ | ||
1350 | str_cnt = count_and_separate_strings (data, filesize, limit); | ||
1351 | if (GNUNET_OK != create_string_array (data, filesize, strings, str_cnt)) | ||
1352 | { | ||
1353 | str_cnt = GNUNET_SYSERR; | ||
1354 | } | ||
1355 | GNUNET_free (data); | ||
1356 | return str_cnt; | ||
1357 | } | ||
1358 | |||
1359 | |||
1360 | /** | ||
1361 | * Main function that will be run by the scheduler. | ||
1362 | * | ||
1363 | * @param cls closure | ||
1364 | * @param args remaining command-line arguments | ||
1365 | * @param cfgfile name of the configuration file used (for saving, can be NULL!) | ||
1366 | * @param config configuration | ||
1367 | */ | ||
1368 | static void | ||
1369 | run (void *cls, | ||
1370 | char *const *args, | ||
1371 | const char *cfgfile, | ||
1372 | const struct GNUNET_CONFIGURATION_Handle *config) | ||
1373 | { | ||
1374 | unsigned int nsearchstrs; | ||
1375 | unsigned int i; | ||
1376 | struct GNUNET_TIME_Relative abort_time; | ||
1377 | |||
1378 | in_shutdown = GNUNET_NO; | ||
1379 | |||
1380 | /* Check config */ | ||
1381 | if (NULL == config) | ||
1382 | { | ||
1383 | GNUNET_log (GNUNET_ERROR_TYPE_ERROR, | ||
1384 | _("No configuration file given. Exiting\n")); | ||
1385 | GNUNET_SCHEDULER_add_now (&do_shutdown, NULL); | ||
1386 | return; | ||
1387 | } | ||
1388 | cfg = GNUNET_CONFIGURATION_dup (config); | ||
1389 | if (GNUNET_OK != | ||
1390 | GNUNET_CONFIGURATION_get_value_string (cfg, "REGEXPROFILER", | ||
1391 | "REGEX_PREFIX", | ||
1392 | ®ex_prefix)) | ||
1393 | { | ||
1394 | GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR, | ||
1395 | "regexprofiler", | ||
1396 | "regex_prefix"); | ||
1397 | GNUNET_SCHEDULER_add_now (&do_shutdown, NULL); | ||
1398 | return; | ||
1399 | } | ||
1400 | if (GNUNET_OK != | ||
1401 | GNUNET_CONFIGURATION_get_value_number (cfg, "REGEXPROFILER", | ||
1402 | "PARALLEL_SEARCHES", | ||
1403 | &init_parallel_searches)) | ||
1404 | { | ||
1405 | GNUNET_log (GNUNET_ERROR_TYPE_WARNING, | ||
1406 | "Configuration option \"PARALLEL_SEARCHES\" missing." | ||
1407 | " Using default (%d)\n", 10); | ||
1408 | init_parallel_searches = 10; | ||
1409 | } | ||
1410 | if (GNUNET_OK != | ||
1411 | GNUNET_CONFIGURATION_get_value_time (cfg, "REGEXPROFILER", | ||
1412 | "REANNOUNCE_PERIOD_MAX", | ||
1413 | &reannounce_period_max)) | ||
1414 | { | ||
1415 | GNUNET_log (GNUNET_ERROR_TYPE_WARNING, | ||
1416 | "reannounce_period_max not given. Using 10 minutes.\n"); | ||
1417 | reannounce_period_max = | ||
1418 | GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_MINUTES, 10); | ||
1419 | } | ||
1420 | |||
1421 | /* Check arguments */ | ||
1422 | if (NULL == policy_dir) | ||
1423 | { | ||
1424 | GNUNET_log (GNUNET_ERROR_TYPE_ERROR, | ||
1425 | _("No policy directory specified on command line. Exiting.\n")); | ||
1426 | return; | ||
1427 | } | ||
1428 | if (GNUNET_YES != GNUNET_DISK_directory_test (policy_dir, GNUNET_YES)) | ||
1429 | { | ||
1430 | GNUNET_log (GNUNET_ERROR_TYPE_ERROR, | ||
1431 | _("Specified policies directory does not exist. Exiting.\n")); | ||
1432 | GNUNET_SCHEDULER_add_now (&do_shutdown, NULL); | ||
1433 | return; | ||
1434 | } | ||
1435 | if (0 >= (int) (num_peers = GNUNET_DISK_directory_scan (policy_dir, NULL, NULL))) | ||
1436 | { | ||
1437 | GNUNET_log (GNUNET_ERROR_TYPE_ERROR, | ||
1438 | _("No files found in `%s'\n"), | ||
1439 | policy_dir); | ||
1440 | return; | ||
1441 | } | ||
1442 | GNUNET_CONFIGURATION_set_value_string (cfg, "REGEXPROFILER", | ||
1443 | "POLICY_DIR", policy_dir); | ||
1444 | if (GNUNET_YES != GNUNET_DISK_file_test (strings_file)) | ||
1445 | { | ||
1446 | GNUNET_log (GNUNET_ERROR_TYPE_ERROR, | ||
1447 | _("No search strings file given. Exiting.\n")); | ||
1448 | GNUNET_SCHEDULER_add_now (&do_shutdown, NULL); | ||
1449 | return; | ||
1450 | } | ||
1451 | nsearchstrs = load_search_strings (strings_file, | ||
1452 | &search_strings, | ||
1453 | num_peers); | ||
1454 | if (num_peers != nsearchstrs) | ||
1455 | { | ||
1456 | GNUNET_log (GNUNET_ERROR_TYPE_ERROR, | ||
1457 | "Error loading search strings.\n"); | ||
1458 | GNUNET_log (GNUNET_ERROR_TYPE_ERROR, | ||
1459 | "File (%s) does not contain enough strings (%u/%u).\n", | ||
1460 | strings_file, nsearchstrs, num_peers); | ||
1461 | GNUNET_SCHEDULER_add_now (&do_shutdown, NULL); | ||
1462 | return; | ||
1463 | } | ||
1464 | if ( (0 == num_peers) || (NULL == search_strings)) | ||
1465 | { | ||
1466 | GNUNET_log (GNUNET_ERROR_TYPE_ERROR, | ||
1467 | _("Error loading search strings. Exiting.\n")); | ||
1468 | GNUNET_SCHEDULER_add_now (&do_shutdown, NULL); | ||
1469 | return; | ||
1470 | } | ||
1471 | for (i = 0; i < num_peers; i++) | ||
1472 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, | ||
1473 | "search string: %s\n", | ||
1474 | search_strings[i]); | ||
1475 | |||
1476 | /* Check logfile */ | ||
1477 | if ( (NULL != data_filename) && | ||
1478 | (NULL == (data_file = | ||
1479 | GNUNET_DISK_file_open (data_filename, | ||
1480 | GNUNET_DISK_OPEN_READWRITE | | ||
1481 | GNUNET_DISK_OPEN_TRUNCATE | | ||
1482 | GNUNET_DISK_OPEN_CREATE, | ||
1483 | GNUNET_DISK_PERM_USER_READ | | ||
1484 | GNUNET_DISK_PERM_USER_WRITE))) ) | ||
1485 | { | ||
1486 | GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_ERROR, | ||
1487 | "open", | ||
1488 | data_filename); | ||
1489 | return; | ||
1490 | } | ||
1491 | |||
1492 | /* Initialize peers */ | ||
1493 | peers = GNUNET_malloc (sizeof (struct RegexPeer) * num_peers); | ||
1494 | for (i = 0; i < num_peers; i++) | ||
1495 | peers[i].id = i; | ||
1496 | |||
1497 | GNUNET_CONFIGURATION_set_value_number (cfg, | ||
1498 | "TESTBED", "OVERLAY_RANDOM_LINKS", | ||
1499 | num_peers * 20); | ||
1500 | GNUNET_CONFIGURATION_set_value_number (cfg, | ||
1501 | "DHT", "FORCE_NSE", | ||
1502 | (long long unsigned) | ||
1503 | (log (num_peers) / log (2.0))); | ||
1504 | event_mask = 0LL; | ||
1505 | /* For feedback about the start process activate these and pass master_cb */ | ||
1506 | event_mask |= (1LL << GNUNET_TESTBED_ET_PEER_START); | ||
1507 | // event_mask |= (1LL << GNUNET_TESTBED_ET_PEER_STOP); | ||
1508 | event_mask |= (1LL << GNUNET_TESTBED_ET_CONNECT); | ||
1509 | // event_mask |= (1LL << GNUNET_TESTBED_ET_DISCONNECT); | ||
1510 | prof_start_time = GNUNET_TIME_absolute_get (); | ||
1511 | GNUNET_TESTBED_run (hosts_file, | ||
1512 | cfg, | ||
1513 | num_peers, | ||
1514 | event_mask, | ||
1515 | &master_controller_cb, | ||
1516 | NULL, /* master_controller_cb cls */ | ||
1517 | &test_master, | ||
1518 | NULL); /* test_master cls */ | ||
1519 | if (GNUNET_OK != | ||
1520 | GNUNET_CONFIGURATION_get_value_time (cfg, "TESTBED", | ||
1521 | "SETUP_TIMEOUT", | ||
1522 | &abort_time)) | ||
1523 | { | ||
1524 | GNUNET_log (GNUNET_ERROR_TYPE_WARNING, | ||
1525 | "SETUP_TIMEOUT not given. Using 15 minutes.\n"); | ||
1526 | abort_time = | ||
1527 | GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_MINUTES, 15); | ||
1528 | } | ||
1529 | abort_time = GNUNET_TIME_relative_add (abort_time, GNUNET_TIME_UNIT_MINUTES); | ||
1530 | abort_task = | ||
1531 | GNUNET_SCHEDULER_add_delayed (abort_time, | ||
1532 | &do_abort, | ||
1533 | (void*) __LINE__); | ||
1534 | GNUNET_log (GNUNET_ERROR_TYPE_WARNING, | ||
1535 | "setup_timeout: %s\n", | ||
1536 | GNUNET_STRINGS_relative_time_to_string (abort_time, GNUNET_YES)); | ||
1537 | } | ||
1538 | |||
1539 | |||
1540 | /** | ||
1541 | * Main function. | ||
1542 | * | ||
1543 | * @param argc argument count | ||
1544 | * @param argv argument values | ||
1545 | * @return 0 on success | ||
1546 | */ | ||
1547 | int | ||
1548 | main (int argc, char *const *argv) | ||
1549 | { | ||
1550 | struct GNUNET_GETOPT_CommandLineOption options[] = { | ||
1551 | |||
1552 | GNUNET_GETOPT_option_filename ('o', | ||
1553 | "output-file", | ||
1554 | "FILENAME", | ||
1555 | gettext_noop ("name of the file for writing statistics"), | ||
1556 | &data_filename), | ||
1557 | |||
1558 | GNUNET_GETOPT_option_relative_time ('t', | ||
1559 | "matching-timeout", | ||
1560 | "TIMEOUT", | ||
1561 | gettext_noop ("wait TIMEOUT before ending the experiment"), | ||
1562 | &search_timeout_time), | ||
1563 | |||
1564 | GNUNET_GETOPT_option_filename ('p', | ||
1565 | "policy-dir", | ||
1566 | "DIRECTORY", | ||
1567 | gettext_noop ("directory with policy files"), | ||
1568 | &policy_dir), | ||
1569 | |||
1570 | |||
1571 | GNUNET_GETOPT_option_filename ('s', | ||
1572 | "strings-file", | ||
1573 | "FILENAME", | ||
1574 | gettext_noop ("name of file with input strings"), | ||
1575 | &strings_file), | ||
1576 | |||
1577 | GNUNET_GETOPT_option_filename ('H', | ||
1578 | "hosts-file", | ||
1579 | "FILENAME", | ||
1580 | gettext_noop ("name of file with hosts' names"), | ||
1581 | &hosts_file), | ||
1582 | |||
1583 | GNUNET_GETOPT_OPTION_END | ||
1584 | }; | ||
1585 | int ret; | ||
1586 | |||
1587 | if (GNUNET_OK != GNUNET_STRINGS_get_utf8_args (argc, argv, &argc, &argv)) | ||
1588 | return 2; | ||
1589 | result = GNUNET_SYSERR; | ||
1590 | ret = | ||
1591 | GNUNET_PROGRAM_run (argc, argv, | ||
1592 | "gnunet-regex-profiler", | ||
1593 | _("Profiler for regex"), | ||
1594 | options, &run, NULL); | ||
1595 | if (GNUNET_OK != ret) | ||
1596 | return ret; | ||
1597 | if (GNUNET_OK != result) | ||
1598 | return 1; | ||
1599 | return 0; | ||
1600 | } | ||
diff --git a/src/regex/gnunet-regex-simulation-profiler.c b/src/regex/gnunet-regex-simulation-profiler.c new file mode 100644 index 000000000..b7e256f48 --- /dev/null +++ b/src/regex/gnunet-regex-simulation-profiler.c | |||
@@ -0,0 +1,723 @@ | |||
1 | /* | ||
2 | This file is part of GNUnet. | ||
3 | Copyright (C) 2011, 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 | |||
19 | |||
20 | /** | ||
21 | * @file regex/gnunet-regex-simulation-profiler.c | ||
22 | * @brief Regex profiler that dumps all DFAs into a database instead of | ||
23 | * using the DHT (with cadet). | ||
24 | * @author Maximilian Szengel | ||
25 | * @author Christophe Genevey | ||
26 | * | ||
27 | */ | ||
28 | |||
29 | #include "platform.h" | ||
30 | #include "gnunet_util_lib.h" | ||
31 | #include "regex_internal_lib.h" | ||
32 | #include "gnunet_mysql_lib.h" | ||
33 | #include "gnunet_my_lib.h" | ||
34 | #include <mysql/mysql.h> | ||
35 | |||
36 | /** | ||
37 | * MySQL statement to insert an edge. | ||
38 | */ | ||
39 | #define INSERT_EDGE_STMT "INSERT IGNORE INTO `%s` "\ | ||
40 | "(`key`, `label`, `to_key`, `accepting`) "\ | ||
41 | "VALUES (?, ?, ?, ?);" | ||
42 | |||
43 | /** | ||
44 | * MySQL statement to select a key count. | ||
45 | */ | ||
46 | #define SELECT_KEY_STMT "SELECT COUNT(*) FROM `%s` "\ | ||
47 | "WHERE `key` = ? AND `label` = ?;" | ||
48 | |||
49 | /** | ||
50 | * Simple struct to keep track of progress, and print a | ||
51 | * nice little percentage meter for long running tasks. | ||
52 | */ | ||
53 | struct ProgressMeter | ||
54 | { | ||
55 | /** | ||
56 | * Total number of elements. | ||
57 | */ | ||
58 | unsigned int total; | ||
59 | |||
60 | /** | ||
61 | * Intervall for printing percentage. | ||
62 | */ | ||
63 | unsigned int modnum; | ||
64 | |||
65 | /** | ||
66 | * Number of dots to print. | ||
67 | */ | ||
68 | unsigned int dotnum; | ||
69 | |||
70 | /** | ||
71 | * Completed number. | ||
72 | */ | ||
73 | unsigned int completed; | ||
74 | |||
75 | /** | ||
76 | * Should the meter be printed? | ||
77 | */ | ||
78 | int print; | ||
79 | |||
80 | /** | ||
81 | * String to print on startup. | ||
82 | */ | ||
83 | char *startup_string; | ||
84 | }; | ||
85 | |||
86 | |||
87 | /** | ||
88 | * Handle for the progress meter | ||
89 | */ | ||
90 | static struct ProgressMeter *meter; | ||
91 | |||
92 | /** | ||
93 | * Scan task identifier; | ||
94 | */ | ||
95 | static struct GNUNET_SCHEDULER_Task *scan_task; | ||
96 | |||
97 | /** | ||
98 | * Global testing status. | ||
99 | */ | ||
100 | static int result; | ||
101 | |||
102 | /** | ||
103 | * MySQL context. | ||
104 | */ | ||
105 | static struct GNUNET_MYSQL_Context *mysql_ctx; | ||
106 | |||
107 | /** | ||
108 | * MySQL prepared statement handle. | ||
109 | */ | ||
110 | static struct GNUNET_MYSQL_StatementHandle *stmt_handle; | ||
111 | |||
112 | /** | ||
113 | * MySQL prepared statement handle for `key` select. | ||
114 | */ | ||
115 | static struct GNUNET_MYSQL_StatementHandle *select_stmt_handle; | ||
116 | |||
117 | /** | ||
118 | * MySQL table name. | ||
119 | */ | ||
120 | static char *table_name; | ||
121 | |||
122 | /** | ||
123 | * Policy dir containing files that contain policies. | ||
124 | */ | ||
125 | static char *policy_dir; | ||
126 | |||
127 | /** | ||
128 | * Number of policy files. | ||
129 | */ | ||
130 | static unsigned int num_policy_files; | ||
131 | |||
132 | /** | ||
133 | * Number of policies. | ||
134 | */ | ||
135 | static unsigned int num_policies; | ||
136 | |||
137 | /** | ||
138 | * Maximal path compression length. | ||
139 | */ | ||
140 | static unsigned int max_path_compression; | ||
141 | |||
142 | /** | ||
143 | * Number of merged transitions. | ||
144 | */ | ||
145 | static unsigned long long num_merged_transitions; | ||
146 | |||
147 | /** | ||
148 | * Number of merged states from different policies. | ||
149 | */ | ||
150 | static unsigned long long num_merged_states; | ||
151 | |||
152 | /** | ||
153 | * Prefix to add before every regex we're announcing. | ||
154 | */ | ||
155 | static char *regex_prefix; | ||
156 | |||
157 | |||
158 | /** | ||
159 | * Create a meter to keep track of the progress of some task. | ||
160 | * | ||
161 | * @param total the total number of items to complete | ||
162 | * @param start_string a string to prefix the meter with (if printing) | ||
163 | * @param print GNUNET_YES to print the meter, GNUNET_NO to count | ||
164 | * internally only | ||
165 | * | ||
166 | * @return the progress meter | ||
167 | */ | ||
168 | static struct ProgressMeter * | ||
169 | create_meter (unsigned int total, char *start_string, int print) | ||
170 | { | ||
171 | struct ProgressMeter *ret; | ||
172 | |||
173 | ret = GNUNET_new (struct ProgressMeter); | ||
174 | ret->print = print; | ||
175 | ret->total = total; | ||
176 | ret->modnum = total / 4; | ||
177 | if (ret->modnum == 0) /* Divide by zero check */ | ||
178 | ret->modnum = 1; | ||
179 | ret->dotnum = (total / 50) + 1; | ||
180 | if (start_string != NULL) | ||
181 | ret->startup_string = GNUNET_strdup (start_string); | ||
182 | else | ||
183 | ret->startup_string = GNUNET_strdup (""); | ||
184 | |||
185 | return ret; | ||
186 | } | ||
187 | |||
188 | |||
189 | /** | ||
190 | * Update progress meter (increment by one). | ||
191 | * | ||
192 | * @param meter the meter to update and print info for | ||
193 | * | ||
194 | * @return GNUNET_YES if called the total requested, | ||
195 | * GNUNET_NO if more items expected | ||
196 | */ | ||
197 | static int | ||
198 | update_meter (struct ProgressMeter *meter) | ||
199 | { | ||
200 | if (meter->print == GNUNET_YES) | ||
201 | { | ||
202 | if (meter->completed % meter->modnum == 0) | ||
203 | { | ||
204 | if (meter->completed == 0) | ||
205 | { | ||
206 | FPRINTF (stdout, "%sProgress: [0%%", meter->startup_string); | ||
207 | } | ||
208 | else | ||
209 | FPRINTF (stdout, "%d%%", | ||
210 | (int) (((float) meter->completed / meter->total) * 100)); | ||
211 | } | ||
212 | else if (meter->completed % meter->dotnum == 0) | ||
213 | FPRINTF (stdout, "%s", "."); | ||
214 | |||
215 | if (meter->completed + 1 == meter->total) | ||
216 | FPRINTF (stdout, "%d%%]\n", 100); | ||
217 | fflush (stdout); | ||
218 | } | ||
219 | meter->completed++; | ||
220 | |||
221 | if (meter->completed == meter->total) | ||
222 | return GNUNET_YES; | ||
223 | if (meter->completed > meter->total) | ||
224 | GNUNET_log (GNUNET_ERROR_TYPE_WARNING, "Progress meter overflow!!\n"); | ||
225 | return GNUNET_NO; | ||
226 | } | ||
227 | |||
228 | |||
229 | /** | ||
230 | * Reset progress meter. | ||
231 | * | ||
232 | * @param meter the meter to reset | ||
233 | * | ||
234 | * @return #GNUNET_YES if meter reset, | ||
235 | * #GNUNET_SYSERR on error | ||
236 | */ | ||
237 | static int | ||
238 | reset_meter (struct ProgressMeter *meter) | ||
239 | { | ||
240 | if (meter == NULL) | ||
241 | return GNUNET_SYSERR; | ||
242 | |||
243 | meter->completed = 0; | ||
244 | return GNUNET_YES; | ||
245 | } | ||
246 | |||
247 | |||
248 | /** | ||
249 | * Release resources for meter | ||
250 | * | ||
251 | * @param meter the meter to free | ||
252 | */ | ||
253 | static void | ||
254 | free_meter (struct ProgressMeter *meter) | ||
255 | { | ||
256 | GNUNET_free_non_null (meter->startup_string); | ||
257 | GNUNET_free (meter); | ||
258 | } | ||
259 | |||
260 | |||
261 | /** | ||
262 | * Shutdown task. | ||
263 | * | ||
264 | * @param cls NULL | ||
265 | */ | ||
266 | static void | ||
267 | do_shutdown (void *cls) | ||
268 | { | ||
269 | if (NULL != mysql_ctx) | ||
270 | { | ||
271 | GNUNET_MYSQL_context_destroy (mysql_ctx); | ||
272 | mysql_ctx = NULL; | ||
273 | } | ||
274 | if (NULL != meter) | ||
275 | { | ||
276 | free_meter (meter); | ||
277 | meter = NULL; | ||
278 | } | ||
279 | } | ||
280 | |||
281 | |||
282 | /** | ||
283 | * Abort task to run on test timed out. | ||
284 | * | ||
285 | * FIXME: this doesn't actually work, it used to cancel | ||
286 | * the already running 'scan_task', but now that should | ||
287 | * always be NULL and do nothing. We instead need to set | ||
288 | * a global variable and abort scan_task internally, not | ||
289 | * via scheduler. | ||
290 | * | ||
291 | * @param cls NULL | ||
292 | */ | ||
293 | static void | ||
294 | do_abort (void *cls) | ||
295 | { | ||
296 | GNUNET_log (GNUNET_ERROR_TYPE_WARNING, "Aborting\n"); | ||
297 | if (NULL != scan_task) | ||
298 | { | ||
299 | GNUNET_SCHEDULER_cancel (scan_task); | ||
300 | scan_task = NULL; | ||
301 | } | ||
302 | result = GNUNET_SYSERR; | ||
303 | GNUNET_SCHEDULER_shutdown (); | ||
304 | } | ||
305 | |||
306 | /** | ||
307 | * Iterator over all states that inserts each state into the MySQL db. | ||
308 | * | ||
309 | * @param cls closure. | ||
310 | * @param key hash for current state. | ||
311 | * @param proof proof for current state. | ||
312 | * @param accepting #GNUNET_YES if this is an accepting state, #GNUNET_NO if not. | ||
313 | * @param num_edges number of edges leaving current state. | ||
314 | * @param edges edges leaving current state. | ||
315 | */ | ||
316 | static void | ||
317 | regex_iterator (void *cls, | ||
318 | const struct GNUNET_HashCode *key, | ||
319 | const char *proof, | ||
320 | int accepting, | ||
321 | unsigned int num_edges, | ||
322 | const struct REGEX_BLOCK_Edge *edges) | ||
323 | { | ||
324 | unsigned int i; | ||
325 | int result; | ||
326 | |||
327 | uint32_t iaccepting = (uint32_t)accepting; | ||
328 | uint64_t total; | ||
329 | |||
330 | GNUNET_assert (NULL != mysql_ctx); | ||
331 | |||
332 | for (i = 0; i < num_edges; i++) | ||
333 | { | ||
334 | struct GNUNET_MY_QueryParam params_select[] = { | ||
335 | GNUNET_MY_query_param_auto_from_type (key), | ||
336 | GNUNET_MY_query_param_string (edges[i].label), | ||
337 | GNUNET_MY_query_param_end | ||
338 | }; | ||
339 | |||
340 | struct GNUNET_MY_ResultSpec results_select[] = { | ||
341 | GNUNET_MY_result_spec_uint64 (&total), | ||
342 | GNUNET_MY_result_spec_end | ||
343 | }; | ||
344 | |||
345 | result = | ||
346 | GNUNET_MY_exec_prepared (mysql_ctx, | ||
347 | select_stmt_handle, | ||
348 | params_select); | ||
349 | |||
350 | if (GNUNET_SYSERR == result) | ||
351 | { | ||
352 | GNUNET_log (GNUNET_ERROR_TYPE_ERROR, | ||
353 | "Error executing prepared mysql select statement\n"); | ||
354 | GNUNET_SCHEDULER_add_now (&do_abort, NULL); | ||
355 | return; | ||
356 | } | ||
357 | |||
358 | result = | ||
359 | GNUNET_MY_extract_result (select_stmt_handle, | ||
360 | results_select); | ||
361 | |||
362 | if (GNUNET_SYSERR == result) | ||
363 | { | ||
364 | GNUNET_log (GNUNET_ERROR_TYPE_ERROR, | ||
365 | "Error extracting result mysql select statement\n"); | ||
366 | GNUNET_SCHEDULER_add_now (&do_abort, NULL); | ||
367 | return; | ||
368 | } | ||
369 | |||
370 | if (-1 != total && total > 0) | ||
371 | { | ||
372 | GNUNET_log (GNUNET_ERROR_TYPE_INFO, "Total: %llu (%s, %s)\n", | ||
373 | (unsigned long long)total, | ||
374 | GNUNET_h2s (key), edges[i].label); | ||
375 | } | ||
376 | |||
377 | struct GNUNET_MY_QueryParam params_stmt[] = { | ||
378 | GNUNET_MY_query_param_auto_from_type (&key), | ||
379 | GNUNET_MY_query_param_string (edges[i].label), | ||
380 | GNUNET_MY_query_param_auto_from_type (&edges[i].destination), | ||
381 | GNUNET_MY_query_param_uint32 (&iaccepting), | ||
382 | GNUNET_MY_query_param_end | ||
383 | }; | ||
384 | |||
385 | result = | ||
386 | GNUNET_MY_exec_prepared (mysql_ctx, | ||
387 | stmt_handle, | ||
388 | params_stmt); | ||
389 | |||
390 | if (0 == result) | ||
391 | { | ||
392 | char *key_str = GNUNET_strdup (GNUNET_h2s (key)); | ||
393 | char *to_key_str = GNUNET_strdup (GNUNET_h2s (&edges[i].destination)); | ||
394 | |||
395 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Merged (%s, %s, %s, %i)\n", | ||
396 | key_str, | ||
397 | edges[i].label, | ||
398 | to_key_str, | ||
399 | accepting); | ||
400 | |||
401 | GNUNET_free (key_str); | ||
402 | GNUNET_free (to_key_str); | ||
403 | num_merged_transitions++; | ||
404 | } | ||
405 | else if (-1 != total) | ||
406 | { | ||
407 | num_merged_states++; | ||
408 | } | ||
409 | |||
410 | if (GNUNET_SYSERR == result || (1 != result && 0 != result)) | ||
411 | { | ||
412 | GNUNET_log (GNUNET_ERROR_TYPE_ERROR, | ||
413 | "Error executing prepared mysql statement for edge: Affected rows: %i, expected 0 or 1!\n", | ||
414 | result); | ||
415 | GNUNET_SCHEDULER_add_now (&do_abort, NULL); | ||
416 | } | ||
417 | } | ||
418 | |||
419 | if (0 == num_edges) | ||
420 | { | ||
421 | struct GNUNET_MY_QueryParam params_stmt[] = { | ||
422 | GNUNET_MY_query_param_auto_from_type (key), | ||
423 | GNUNET_MY_query_param_string (""), | ||
424 | GNUNET_MY_query_param_fixed_size (NULL, 0), | ||
425 | GNUNET_MY_query_param_uint32 (&iaccepting), | ||
426 | GNUNET_MY_query_param_end | ||
427 | }; | ||
428 | |||
429 | result = | ||
430 | GNUNET_MY_exec_prepared (mysql_ctx, | ||
431 | stmt_handle, | ||
432 | params_stmt); | ||
433 | |||
434 | if (1 != result && 0 != result) | ||
435 | { | ||
436 | GNUNET_log (GNUNET_ERROR_TYPE_ERROR, | ||
437 | "Error executing prepared mysql statement for edge: Affected rows: %i, expected 0 or 1!\n", | ||
438 | result); | ||
439 | GNUNET_SCHEDULER_add_now (&do_abort, NULL); | ||
440 | } | ||
441 | } | ||
442 | } | ||
443 | |||
444 | |||
445 | /** | ||
446 | * Announce a regex by creating the DFA and iterating over each state, inserting | ||
447 | * each state into a MySQL database. | ||
448 | * | ||
449 | * @param regex regular expression. | ||
450 | * @return #GNUNET_OK on success, #GNUNET_SYSERR on failure. | ||
451 | */ | ||
452 | static int | ||
453 | announce_regex (const char *regex) | ||
454 | { | ||
455 | struct REGEX_INTERNAL_Automaton *dfa; | ||
456 | |||
457 | dfa = | ||
458 | REGEX_INTERNAL_construct_dfa (regex, | ||
459 | strlen (regex), | ||
460 | max_path_compression); | ||
461 | |||
462 | if (NULL == dfa) | ||
463 | { | ||
464 | GNUNET_log (GNUNET_ERROR_TYPE_ERROR, | ||
465 | "Failed to create DFA for regex %s\n", | ||
466 | regex); | ||
467 | GNUNET_SCHEDULER_add_now (&do_abort, NULL); | ||
468 | return GNUNET_SYSERR; | ||
469 | } | ||
470 | REGEX_INTERNAL_iterate_all_edges (dfa, | ||
471 | ®ex_iterator, NULL); | ||
472 | REGEX_INTERNAL_automaton_destroy (dfa); | ||
473 | |||
474 | return GNUNET_OK; | ||
475 | } | ||
476 | |||
477 | |||
478 | /** | ||
479 | * Function called with a filename. | ||
480 | * | ||
481 | * @param cls closure | ||
482 | * @param filename complete filename (absolute path) | ||
483 | * @return #GNUNET_OK to continue to iterate, | ||
484 | * #GNUNET_SYSERR to abort iteration with error! | ||
485 | */ | ||
486 | static int | ||
487 | policy_filename_cb (void *cls, const char *filename) | ||
488 | { | ||
489 | char *regex; | ||
490 | char *data; | ||
491 | char *buf; | ||
492 | uint64_t filesize; | ||
493 | unsigned int offset; | ||
494 | |||
495 | GNUNET_assert (NULL != filename); | ||
496 | |||
497 | GNUNET_log (GNUNET_ERROR_TYPE_INFO, | ||
498 | "Announcing regexes from file %s\n", | ||
499 | filename); | ||
500 | |||
501 | if (GNUNET_YES != GNUNET_DISK_file_test (filename)) | ||
502 | { | ||
503 | GNUNET_log (GNUNET_ERROR_TYPE_WARNING, | ||
504 | "Could not find policy file %s\n", | ||
505 | filename); | ||
506 | return GNUNET_OK; | ||
507 | } | ||
508 | if (GNUNET_OK != | ||
509 | GNUNET_DISK_file_size (filename, &filesize, | ||
510 | GNUNET_YES, GNUNET_YES)) | ||
511 | filesize = 0; | ||
512 | if (0 == filesize) | ||
513 | { | ||
514 | GNUNET_log (GNUNET_ERROR_TYPE_WARNING, "Policy file %s is empty.\n", | ||
515 | filename); | ||
516 | return GNUNET_OK; | ||
517 | } | ||
518 | data = GNUNET_malloc (filesize); | ||
519 | if (filesize != GNUNET_DISK_fn_read (filename, data, filesize)) | ||
520 | { | ||
521 | GNUNET_free (data); | ||
522 | GNUNET_log (GNUNET_ERROR_TYPE_WARNING, | ||
523 | "Could not read policy file %s.\n", | ||
524 | filename); | ||
525 | return GNUNET_OK; | ||
526 | } | ||
527 | |||
528 | update_meter (meter); | ||
529 | |||
530 | buf = data; | ||
531 | offset = 0; | ||
532 | regex = NULL; | ||
533 | while (offset < (filesize - 1)) | ||
534 | { | ||
535 | offset++; | ||
536 | if (((data[offset] == '\n')) && (buf != &data[offset])) | ||
537 | { | ||
538 | data[offset] = '|'; | ||
539 | num_policies++; | ||
540 | buf = &data[offset + 1]; | ||
541 | } | ||
542 | else if ((data[offset] == '\n') || (data[offset] == '\0')) | ||
543 | buf = &data[offset + 1]; | ||
544 | } | ||
545 | data[offset] = '\0'; | ||
546 | GNUNET_asprintf (®ex, "%s(%s)", regex_prefix, data); | ||
547 | GNUNET_assert (NULL != regex); | ||
548 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, | ||
549 | "Announcing regex: %s\n", regex); | ||
550 | |||
551 | if (GNUNET_OK != announce_regex (regex)) | ||
552 | { | ||
553 | GNUNET_log (GNUNET_ERROR_TYPE_ERROR, | ||
554 | "Could not announce regex %s\n", | ||
555 | regex); | ||
556 | } | ||
557 | GNUNET_free (regex); | ||
558 | GNUNET_free (data); | ||
559 | return GNUNET_OK; | ||
560 | } | ||
561 | |||
562 | |||
563 | /** | ||
564 | * Iterate over files contained in policy_dir. | ||
565 | * | ||
566 | * @param cls NULL | ||
567 | */ | ||
568 | static void | ||
569 | do_directory_scan (void *cls) | ||
570 | { | ||
571 | struct GNUNET_TIME_Absolute start_time; | ||
572 | struct GNUNET_TIME_Relative duration; | ||
573 | char *stmt; | ||
574 | |||
575 | /* Create an MySQL prepared statement for the inserts */ | ||
576 | scan_task = NULL; | ||
577 | GNUNET_asprintf (&stmt, INSERT_EDGE_STMT, table_name); | ||
578 | stmt_handle = GNUNET_MYSQL_statement_prepare (mysql_ctx, stmt); | ||
579 | GNUNET_free (stmt); | ||
580 | |||
581 | GNUNET_asprintf (&stmt, SELECT_KEY_STMT, table_name); | ||
582 | select_stmt_handle = GNUNET_MYSQL_statement_prepare (mysql_ctx, stmt); | ||
583 | GNUNET_free (stmt); | ||
584 | |||
585 | GNUNET_assert (NULL != stmt_handle); | ||
586 | |||
587 | meter = create_meter (num_policy_files, | ||
588 | "Announcing policy files\n", | ||
589 | GNUNET_YES); | ||
590 | start_time = GNUNET_TIME_absolute_get (); | ||
591 | GNUNET_DISK_directory_scan (policy_dir, | ||
592 | &policy_filename_cb, | ||
593 | stmt_handle); | ||
594 | duration = GNUNET_TIME_absolute_get_duration (start_time); | ||
595 | reset_meter (meter); | ||
596 | free_meter (meter); | ||
597 | meter = NULL; | ||
598 | |||
599 | printf ("Announced %u files containing %u policies in %s\n" | ||
600 | "Duplicate transitions: %llu\nMerged states: %llu\n", | ||
601 | num_policy_files, | ||
602 | num_policies, | ||
603 | GNUNET_STRINGS_relative_time_to_string (duration, GNUNET_NO), | ||
604 | num_merged_transitions, | ||
605 | num_merged_states); | ||
606 | result = GNUNET_OK; | ||
607 | GNUNET_SCHEDULER_shutdown (); | ||
608 | } | ||
609 | |||
610 | |||
611 | /** | ||
612 | * Main function that will be run by the scheduler. | ||
613 | * | ||
614 | * @param cls closure | ||
615 | * @param args remaining command-line arguments | ||
616 | * @param cfgfile name of the configuration file used (for saving, can be NULL!) | ||
617 | * @param config configuration | ||
618 | */ | ||
619 | static void | ||
620 | run (void *cls, | ||
621 | char *const *args, | ||
622 | const char *cfgfile, | ||
623 | const struct GNUNET_CONFIGURATION_Handle *config) | ||
624 | { | ||
625 | if (NULL == args[0]) | ||
626 | { | ||
627 | fprintf (stderr, | ||
628 | _("No policy directory specified on command line. Exiting.\n")); | ||
629 | result = GNUNET_SYSERR; | ||
630 | return; | ||
631 | } | ||
632 | if (GNUNET_YES != | ||
633 | GNUNET_DISK_directory_test (args[0], GNUNET_YES)) | ||
634 | { | ||
635 | fprintf (stderr, | ||
636 | _("Specified policies directory does not exist. Exiting.\n")); | ||
637 | result = GNUNET_SYSERR; | ||
638 | return; | ||
639 | } | ||
640 | policy_dir = args[0]; | ||
641 | |||
642 | num_policy_files = GNUNET_DISK_directory_scan (policy_dir, | ||
643 | NULL, NULL); | ||
644 | meter = NULL; | ||
645 | |||
646 | if (NULL == table_name) | ||
647 | { | ||
648 | GNUNET_log (GNUNET_ERROR_TYPE_WARNING, | ||
649 | "No table name specified, using default \"NFA\".\n"); | ||
650 | table_name = "NFA"; | ||
651 | } | ||
652 | |||
653 | mysql_ctx = GNUNET_MYSQL_context_create (config, "regex-mysql"); | ||
654 | if (NULL == mysql_ctx) | ||
655 | { | ||
656 | GNUNET_log (GNUNET_ERROR_TYPE_ERROR, | ||
657 | "Failed to create mysql context\n"); | ||
658 | result = GNUNET_SYSERR; | ||
659 | return; | ||
660 | } | ||
661 | |||
662 | if (GNUNET_OK != | ||
663 | GNUNET_CONFIGURATION_get_value_string (config, | ||
664 | "regex-mysql", | ||
665 | "REGEX_PREFIX", | ||
666 | ®ex_prefix)) | ||
667 | { | ||
668 | GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR, | ||
669 | "regex-mysql", | ||
670 | "REGEX_PREFIX"); | ||
671 | result = GNUNET_SYSERR; | ||
672 | return; | ||
673 | } | ||
674 | |||
675 | result = GNUNET_OK; | ||
676 | GNUNET_SCHEDULER_add_shutdown (&do_shutdown, | ||
677 | NULL); | ||
678 | scan_task = GNUNET_SCHEDULER_add_now (&do_directory_scan, NULL); | ||
679 | } | ||
680 | |||
681 | |||
682 | /** | ||
683 | * Main function. | ||
684 | * | ||
685 | * @param argc argument count | ||
686 | * @param argv argument values | ||
687 | * @return 0 on success | ||
688 | */ | ||
689 | int | ||
690 | main (int argc, char *const *argv) | ||
691 | { | ||
692 | struct GNUNET_GETOPT_CommandLineOption options[] = { | ||
693 | |||
694 | GNUNET_GETOPT_option_string ('t', | ||
695 | "table", | ||
696 | "TABLENAME", | ||
697 | gettext_noop ("name of the table to write DFAs"), | ||
698 | &table_name), | ||
699 | |||
700 | GNUNET_GETOPT_option_uint ('p', | ||
701 | "max-path-compression", | ||
702 | "MAX_PATH_COMPRESSION", | ||
703 | gettext_noop ("maximum path compression length"), | ||
704 | &max_path_compression), | ||
705 | |||
706 | GNUNET_GETOPT_OPTION_END | ||
707 | }; | ||
708 | int ret; | ||
709 | |||
710 | if (GNUNET_OK != GNUNET_STRINGS_get_utf8_args (argc, argv, &argc, &argv)) | ||
711 | return 2; | ||
712 | |||
713 | result = GNUNET_SYSERR; | ||
714 | ret = | ||
715 | GNUNET_PROGRAM_run (argc, argv, | ||
716 | "gnunet-regex-simulationprofiler [OPTIONS] policy-dir", | ||
717 | _("Profiler for regex library"), options, &run, NULL); | ||
718 | if (GNUNET_OK != ret) | ||
719 | return ret; | ||
720 | if (GNUNET_OK != result) | ||
721 | return 1; | ||
722 | return 0; | ||
723 | } | ||
diff --git a/src/regex/gnunet-service-regex.c b/src/regex/gnunet-service-regex.c new file mode 100644 index 000000000..eb10e7c07 --- /dev/null +++ b/src/regex/gnunet-service-regex.c | |||
@@ -0,0 +1,416 @@ | |||
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 | |||
19 | /** | ||
20 | * @file regex/gnunet-service-regex.c | ||
21 | * @brief service to advertise capabilities described as regex and to | ||
22 | * lookup capabilities by regex | ||
23 | * @author Christian Grothoff | ||
24 | */ | ||
25 | #include "platform.h" | ||
26 | #include "gnunet_util_lib.h" | ||
27 | #include "regex_internal_lib.h" | ||
28 | #include "regex_ipc.h" | ||
29 | |||
30 | |||
31 | /** | ||
32 | * Information about one of our clients. | ||
33 | */ | ||
34 | struct ClientEntry | ||
35 | { | ||
36 | |||
37 | /** | ||
38 | * Queue for transmissions to @e client. | ||
39 | */ | ||
40 | struct GNUNET_MQ_Handle *mq; | ||
41 | |||
42 | /** | ||
43 | * Handle identifying the client. | ||
44 | */ | ||
45 | struct GNUNET_SERVICE_Client *client; | ||
46 | |||
47 | /** | ||
48 | * Search handle (if this client is searching). | ||
49 | */ | ||
50 | struct REGEX_INTERNAL_Search *sh; | ||
51 | |||
52 | /** | ||
53 | * Announcement handle (if this client is announcing). | ||
54 | */ | ||
55 | struct REGEX_INTERNAL_Announcement *ah; | ||
56 | |||
57 | /** | ||
58 | * Refresh frequency for announcements. | ||
59 | */ | ||
60 | struct GNUNET_TIME_Relative frequency; | ||
61 | |||
62 | /** | ||
63 | * Task for re-announcing. | ||
64 | */ | ||
65 | struct GNUNET_SCHEDULER_Task *refresh_task; | ||
66 | |||
67 | }; | ||
68 | |||
69 | |||
70 | /** | ||
71 | * Connection to the DHT. | ||
72 | */ | ||
73 | static struct GNUNET_DHT_Handle *dht; | ||
74 | |||
75 | /** | ||
76 | * Handle for doing statistics. | ||
77 | */ | ||
78 | static struct GNUNET_STATISTICS_Handle *stats; | ||
79 | |||
80 | /** | ||
81 | * Private key for this peer. | ||
82 | */ | ||
83 | static struct GNUNET_CRYPTO_EddsaPrivateKey *my_private_key; | ||
84 | |||
85 | |||
86 | /** | ||
87 | * Task run during shutdown. | ||
88 | * | ||
89 | * @param cls unused | ||
90 | */ | ||
91 | static void | ||
92 | cleanup_task (void *cls) | ||
93 | { | ||
94 | GNUNET_DHT_disconnect (dht); | ||
95 | dht = NULL; | ||
96 | GNUNET_STATISTICS_destroy (stats, | ||
97 | GNUNET_NO); | ||
98 | stats = NULL; | ||
99 | GNUNET_free (my_private_key); | ||
100 | my_private_key = NULL; | ||
101 | } | ||
102 | |||
103 | |||
104 | /** | ||
105 | * Periodic task to refresh our announcement of the regex. | ||
106 | * | ||
107 | * @param cls the `struct ClientEntry *` of the client that triggered the | ||
108 | * announcement | ||
109 | */ | ||
110 | static void | ||
111 | reannounce (void *cls) | ||
112 | { | ||
113 | struct ClientEntry *ce = cls; | ||
114 | |||
115 | REGEX_INTERNAL_reannounce (ce->ah); | ||
116 | ce->refresh_task = GNUNET_SCHEDULER_add_delayed (ce->frequency, | ||
117 | &reannounce, | ||
118 | ce); | ||
119 | } | ||
120 | |||
121 | |||
122 | /** | ||
123 | * Check ANNOUNCE message. | ||
124 | * | ||
125 | * @param cls identification of the client | ||
126 | * @param am the actual message | ||
127 | * @return #GNUNET_OK if @am is well-formed | ||
128 | */ | ||
129 | static int | ||
130 | check_announce (void *cls, | ||
131 | const struct AnnounceMessage *am) | ||
132 | { | ||
133 | struct ClientEntry *ce = cls; | ||
134 | const char *regex; | ||
135 | uint16_t size; | ||
136 | |||
137 | size = ntohs (am->header.size) - sizeof (*am); | ||
138 | regex = (const char *) &am[1]; | ||
139 | if ('\0' != regex[size - 1]) | ||
140 | { | ||
141 | GNUNET_break (0); | ||
142 | return GNUNET_SYSERR; | ||
143 | } | ||
144 | if (NULL != ce->ah) | ||
145 | { | ||
146 | /* only one announcement per client allowed */ | ||
147 | GNUNET_break (0); | ||
148 | return GNUNET_SYSERR; | ||
149 | } | ||
150 | return GNUNET_OK; | ||
151 | } | ||
152 | |||
153 | |||
154 | /** | ||
155 | * Handle ANNOUNCE message. | ||
156 | * | ||
157 | * @param cls identification of the client | ||
158 | * @param am the actual message | ||
159 | */ | ||
160 | static void | ||
161 | handle_announce (void *cls, | ||
162 | const struct AnnounceMessage *am) | ||
163 | { | ||
164 | struct ClientEntry *ce = cls; | ||
165 | const char *regex; | ||
166 | |||
167 | regex = (const char *) &am[1]; | ||
168 | ce->frequency = GNUNET_TIME_relative_ntoh (am->refresh_delay); | ||
169 | ce->refresh_task = GNUNET_SCHEDULER_add_delayed (ce->frequency, | ||
170 | &reannounce, | ||
171 | ce); | ||
172 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, | ||
173 | "Starting to announce regex `%s' every %s\n", | ||
174 | regex, | ||
175 | GNUNET_STRINGS_relative_time_to_string (ce->frequency, | ||
176 | GNUNET_NO)); | ||
177 | ce->ah = REGEX_INTERNAL_announce (dht, | ||
178 | my_private_key, | ||
179 | regex, | ||
180 | ntohs (am->compression), | ||
181 | stats); | ||
182 | if (NULL == ce->ah) | ||
183 | { | ||
184 | GNUNET_break (0); | ||
185 | GNUNET_SCHEDULER_cancel (ce->refresh_task); | ||
186 | ce->refresh_task = NULL; | ||
187 | GNUNET_SERVICE_client_drop (ce->client); | ||
188 | return; | ||
189 | } | ||
190 | GNUNET_SERVICE_client_continue (ce->client); | ||
191 | } | ||
192 | |||
193 | |||
194 | /** | ||
195 | * Handle result, pass it back to the client. | ||
196 | * | ||
197 | * @param cls the struct ClientEntry of the client searching | ||
198 | * @param id Peer providing a regex that matches the string. | ||
199 | * @param get_path Path of the get request. | ||
200 | * @param get_path_length Lenght of @a get_path. | ||
201 | * @param put_path Path of the put request. | ||
202 | * @param put_path_length Length of the @a put_path. | ||
203 | */ | ||
204 | static void | ||
205 | handle_search_result (void *cls, | ||
206 | const struct GNUNET_PeerIdentity *id, | ||
207 | const struct GNUNET_PeerIdentity *get_path, | ||
208 | unsigned int get_path_length, | ||
209 | const struct GNUNET_PeerIdentity *put_path, | ||
210 | unsigned int put_path_length) | ||
211 | { | ||
212 | struct ClientEntry *ce = cls; | ||
213 | struct GNUNET_MQ_Envelope *env; | ||
214 | struct ResultMessage *result; | ||
215 | struct GNUNET_PeerIdentity *gp; | ||
216 | uint16_t size; | ||
217 | |||
218 | if ( (get_path_length >= 65536) || | ||
219 | (put_path_length >= 65536) || | ||
220 | ( (get_path_length + put_path_length) * sizeof (struct GNUNET_PeerIdentity)) | ||
221 | + sizeof (struct ResultMessage) >= GNUNET_MAX_MESSAGE_SIZE) | ||
222 | { | ||
223 | GNUNET_break (0); | ||
224 | return; | ||
225 | } | ||
226 | size = (get_path_length + put_path_length) * sizeof (struct GNUNET_PeerIdentity); | ||
227 | env = GNUNET_MQ_msg_extra (result, | ||
228 | size, | ||
229 | GNUNET_MESSAGE_TYPE_REGEX_RESULT); | ||
230 | result->get_path_length = htons ((uint16_t) get_path_length); | ||
231 | result->put_path_length = htons ((uint16_t) put_path_length); | ||
232 | result->id = *id; | ||
233 | gp = &result->id; | ||
234 | GNUNET_memcpy (&gp[1], | ||
235 | get_path, | ||
236 | get_path_length * sizeof (struct GNUNET_PeerIdentity)); | ||
237 | GNUNET_memcpy (&gp[1 + get_path_length], | ||
238 | put_path, | ||
239 | put_path_length * sizeof (struct GNUNET_PeerIdentity)); | ||
240 | GNUNET_MQ_send (ce->mq, | ||
241 | env); | ||
242 | } | ||
243 | |||
244 | |||
245 | /** | ||
246 | * Check SEARCH message. | ||
247 | * | ||
248 | * @param cls identification of the client | ||
249 | * @param message the actual message | ||
250 | */ | ||
251 | static int | ||
252 | check_search (void *cls, | ||
253 | const struct RegexSearchMessage *sm) | ||
254 | { | ||
255 | struct ClientEntry *ce = cls; | ||
256 | const char *string; | ||
257 | uint16_t size; | ||
258 | |||
259 | size = ntohs (sm->header.size) - sizeof (*sm); | ||
260 | string = (const char *) &sm[1]; | ||
261 | if ('\0' != string[size - 1]) | ||
262 | { | ||
263 | GNUNET_break (0); | ||
264 | return GNUNET_SYSERR; | ||
265 | } | ||
266 | if (NULL != ce->sh) | ||
267 | { | ||
268 | /* only one search allowed per client */ | ||
269 | GNUNET_break (0); | ||
270 | return GNUNET_SYSERR; | ||
271 | } | ||
272 | return GNUNET_OK; | ||
273 | } | ||
274 | |||
275 | |||
276 | /** | ||
277 | * Handle SEARCH message. | ||
278 | * | ||
279 | * @param cls identification of the client | ||
280 | * @param message the actual message | ||
281 | */ | ||
282 | static void | ||
283 | handle_search (void *cls, | ||
284 | const struct RegexSearchMessage *sm) | ||
285 | { | ||
286 | struct ClientEntry *ce = cls; | ||
287 | const char *string; | ||
288 | |||
289 | string = (const char *) &sm[1]; | ||
290 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, | ||
291 | "Starting to search for `%s'\n", | ||
292 | string); | ||
293 | ce->sh = REGEX_INTERNAL_search (dht, | ||
294 | string, | ||
295 | &handle_search_result, | ||
296 | ce, | ||
297 | stats); | ||
298 | if (NULL == ce->sh) | ||
299 | { | ||
300 | GNUNET_break (0); | ||
301 | GNUNET_SERVICE_client_drop (ce->client); | ||
302 | return; | ||
303 | } | ||
304 | GNUNET_SERVICE_client_continue (ce->client); | ||
305 | } | ||
306 | |||
307 | |||
308 | /** | ||
309 | * Process regex requests. | ||
310 | * | ||
311 | * @param cls closure | ||
312 | * @param cfg configuration to use | ||
313 | * @param service the initialized service | ||
314 | */ | ||
315 | static void | ||
316 | run (void *cls, | ||
317 | const struct GNUNET_CONFIGURATION_Handle *cfg, | ||
318 | struct GNUNET_SERVICE_Handle *service) | ||
319 | { | ||
320 | my_private_key = GNUNET_CRYPTO_eddsa_key_create_from_configuration (cfg); | ||
321 | if (NULL == my_private_key) | ||
322 | { | ||
323 | GNUNET_SCHEDULER_shutdown (); | ||
324 | return; | ||
325 | } | ||
326 | dht = GNUNET_DHT_connect (cfg, 1024); | ||
327 | if (NULL == dht) | ||
328 | { | ||
329 | GNUNET_free (my_private_key); | ||
330 | my_private_key = NULL; | ||
331 | GNUNET_SCHEDULER_shutdown (); | ||
332 | return; | ||
333 | } | ||
334 | GNUNET_SCHEDULER_add_shutdown (&cleanup_task, | ||
335 | NULL); | ||
336 | stats = GNUNET_STATISTICS_create ("regex", cfg); | ||
337 | } | ||
338 | |||
339 | |||
340 | /** | ||
341 | * Callback called when a client connects to the service. | ||
342 | * | ||
343 | * @param cls closure for the service | ||
344 | * @param c the new client that connected to the service | ||
345 | * @param mq the message queue used to send messages to the client | ||
346 | * @return @a c | ||
347 | */ | ||
348 | static void * | ||
349 | client_connect_cb (void *cls, | ||
350 | struct GNUNET_SERVICE_Client *c, | ||
351 | struct GNUNET_MQ_Handle *mq) | ||
352 | { | ||
353 | struct ClientEntry *ce; | ||
354 | |||
355 | ce = GNUNET_new (struct ClientEntry); | ||
356 | ce->client = c; | ||
357 | ce->mq = mq; | ||
358 | return ce; | ||
359 | } | ||
360 | |||
361 | |||
362 | /** | ||
363 | * Callback called when a client disconnected from the service | ||
364 | * | ||
365 | * @param cls closure for the service | ||
366 | * @param c the client that disconnected | ||
367 | * @param internal_cls should be equal to @a c | ||
368 | */ | ||
369 | static void | ||
370 | client_disconnect_cb (void *cls, | ||
371 | struct GNUNET_SERVICE_Client *c, | ||
372 | void *internal_cls) | ||
373 | { | ||
374 | struct ClientEntry *ce = internal_cls; | ||
375 | |||
376 | if (NULL != ce->refresh_task) | ||
377 | { | ||
378 | GNUNET_SCHEDULER_cancel (ce->refresh_task); | ||
379 | ce->refresh_task = NULL; | ||
380 | } | ||
381 | if (NULL != ce->ah) | ||
382 | { | ||
383 | REGEX_INTERNAL_announce_cancel (ce->ah); | ||
384 | ce->ah = NULL; | ||
385 | } | ||
386 | if (NULL != ce->sh) | ||
387 | { | ||
388 | REGEX_INTERNAL_search_cancel (ce->sh); | ||
389 | ce->sh = NULL; | ||
390 | } | ||
391 | GNUNET_free (ce); | ||
392 | } | ||
393 | |||
394 | |||
395 | /** | ||
396 | * Define "main" method using service macro. | ||
397 | */ | ||
398 | GNUNET_SERVICE_MAIN | ||
399 | ("regex", | ||
400 | GNUNET_SERVICE_OPTION_NONE, | ||
401 | &run, | ||
402 | &client_connect_cb, | ||
403 | &client_disconnect_cb, | ||
404 | NULL, | ||
405 | GNUNET_MQ_hd_var_size (announce, | ||
406 | GNUNET_MESSAGE_TYPE_REGEX_ANNOUNCE, | ||
407 | struct AnnounceMessage, | ||
408 | NULL), | ||
409 | GNUNET_MQ_hd_var_size (search, | ||
410 | GNUNET_MESSAGE_TYPE_REGEX_SEARCH, | ||
411 | struct RegexSearchMessage, | ||
412 | NULL), | ||
413 | GNUNET_MQ_handler_end ()); | ||
414 | |||
415 | |||
416 | /* end of gnunet-service-regex.c */ | ||
diff --git a/src/regex/perf-data.tar.gz b/src/regex/perf-data.tar.gz new file mode 100644 index 000000000..9e909e58e --- /dev/null +++ b/src/regex/perf-data.tar.gz | |||
Binary files differ | |||
diff --git a/src/regex/perf-regex.c b/src/regex/perf-regex.c new file mode 100644 index 000000000..b507e75b0 --- /dev/null +++ b/src/regex/perf-regex.c | |||
@@ -0,0 +1,126 @@ | |||
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 | |||
19 | /** | ||
20 | * @file src/regex/perf-regex.c | ||
21 | * @brief Test how long it takes to create a automaton from a string regex. | ||
22 | * @author Bartlomiej Polot | ||
23 | */ | ||
24 | #include <regex.h> | ||
25 | #include <time.h> | ||
26 | #include "platform.h" | ||
27 | #include "regex_internal_lib.h" | ||
28 | #include "regex_test_lib.h" | ||
29 | |||
30 | |||
31 | /** | ||
32 | * Print information about the given node and its edges | ||
33 | * to stdout. | ||
34 | * | ||
35 | * @param cls closure, unused. | ||
36 | * @param key hash for current state. | ||
37 | * @param proof proof for current state. | ||
38 | * @param accepting GNUNET_YES if this is an accepting state, GNUNET_NO if not. | ||
39 | * @param num_edges number of edges leaving current state. | ||
40 | * @param edges edges leaving current state. | ||
41 | */ | ||
42 | static void | ||
43 | print_edge (void *cls, | ||
44 | const struct GNUNET_HashCode *key, | ||
45 | const char *proof, | ||
46 | int accepting, | ||
47 | unsigned int num_edges, | ||
48 | const struct REGEX_BLOCK_Edge *edges) | ||
49 | { | ||
50 | unsigned int i; | ||
51 | |||
52 | printf ("%s: %s, proof: `%s'\n", | ||
53 | GNUNET_h2s (key), | ||
54 | accepting ? "ACCEPTING" : "", | ||
55 | proof); | ||
56 | for (i = 0; i < num_edges; i++) | ||
57 | printf (" `%s': %s\n", | ||
58 | edges[i].label, | ||
59 | GNUNET_h2s (&edges[i].destination)); | ||
60 | } | ||
61 | |||
62 | |||
63 | /** | ||
64 | * The main function of the regex performace test. | ||
65 | * | ||
66 | * Read a set of regex from a file, combine them and create a DFA from the | ||
67 | * resulting combined regex. | ||
68 | * | ||
69 | * @param argc number of arguments from the command line | ||
70 | * @param argv command line arguments | ||
71 | * @return 0 ok, 1 on error | ||
72 | */ | ||
73 | int | ||
74 | main (int argc, char *const *argv) | ||
75 | { | ||
76 | struct REGEX_INTERNAL_Automaton* dfa; | ||
77 | char **regexes; | ||
78 | char *buffer; | ||
79 | char *regex; | ||
80 | int compression; | ||
81 | unsigned int alphabet_size; | ||
82 | long size; | ||
83 | |||
84 | GNUNET_log_setup ("perf-regex", "DEBUG", NULL); | ||
85 | if (4 != argc) | ||
86 | { | ||
87 | fprintf (stderr, | ||
88 | "Usage: %s REGEX_FILE ALPHABET_SIZE COMPRESSION\n", | ||
89 | argv[0]); | ||
90 | return 1; | ||
91 | } | ||
92 | regexes = REGEX_TEST_read_from_file (argv[1]); | ||
93 | if (NULL == regexes) | ||
94 | { | ||
95 | fprintf (stderr, | ||
96 | "Failed to read regexes from `%s'\n", | ||
97 | argv[1]); | ||
98 | return 2; | ||
99 | } | ||
100 | alphabet_size = atoi (argv[2]); | ||
101 | compression = atoi (argv[3]); | ||
102 | printf ("********* PERF-REGEX *********'\n"); | ||
103 | printf ("Using:\n file '%s'\n Alphabet size %u\n compression %d\n", | ||
104 | argv[1], alphabet_size, compression); | ||
105 | fflush(stdout); | ||
106 | buffer = REGEX_TEST_combine (regexes, alphabet_size); | ||
107 | GNUNET_asprintf (®ex, "GNUNET_REGEX_PROFILER_(%s)(0|1)*", buffer); | ||
108 | size = strlen (regex); | ||
109 | |||
110 | fprintf (stderr, | ||
111 | "Combined regex (%ld bytes):\n%s\n", | ||
112 | size, | ||
113 | regex); | ||
114 | dfa = REGEX_INTERNAL_construct_dfa (regex, size, compression); | ||
115 | printf ("********* ALL EDGES *********'\n"); | ||
116 | REGEX_INTERNAL_iterate_all_edges (dfa, &print_edge, NULL); | ||
117 | printf ("\n\n********* REACHABLE EDGES *********'\n"); | ||
118 | REGEX_INTERNAL_iterate_reachable_edges (dfa, &print_edge, NULL); | ||
119 | REGEX_INTERNAL_automaton_destroy (dfa); | ||
120 | GNUNET_free (buffer); | ||
121 | REGEX_TEST_free_from_file (regexes); | ||
122 | GNUNET_free (regex); | ||
123 | return 0; | ||
124 | } | ||
125 | |||
126 | /* end of prof-regex.c */ | ||
diff --git a/src/regex/plugin_block_regex.c b/src/regex/plugin_block_regex.c new file mode 100644 index 000000000..76045efcd --- /dev/null +++ b/src/regex/plugin_block_regex.c | |||
@@ -0,0 +1,402 @@ | |||
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 | |||
19 | /** | ||
20 | * @file regex/plugin_block_regex.c | ||
21 | * @brief blocks used for regex storage and search | ||
22 | * @author Bartlomiej Polot | ||
23 | */ | ||
24 | #include "platform.h" | ||
25 | #include "gnunet_block_plugin.h" | ||
26 | #include "gnunet_block_group_lib.h" | ||
27 | #include "block_regex.h" | ||
28 | #include "regex_block_lib.h" | ||
29 | #include "gnunet_signatures.h" | ||
30 | |||
31 | |||
32 | /** | ||
33 | * Number of bits we set per entry in the bloomfilter. | ||
34 | * Do not change! | ||
35 | */ | ||
36 | #define BLOOMFILTER_K 16 | ||
37 | |||
38 | |||
39 | /** | ||
40 | * How big is the BF we use for REGEX blocks? | ||
41 | */ | ||
42 | #define REGEX_BF_SIZE 8 | ||
43 | |||
44 | |||
45 | /** | ||
46 | * Create a new block group. | ||
47 | * | ||
48 | * @param ctx block context in which the block group is created | ||
49 | * @param type type of the block for which we are creating the group | ||
50 | * @param nonce random value used to seed the group creation | ||
51 | * @param raw_data optional serialized prior state of the group, NULL if unavailable/fresh | ||
52 | * @param raw_data_size number of bytes in @a raw_data, 0 if unavailable/fresh | ||
53 | * @param va variable arguments specific to @a type | ||
54 | * @return block group handle, NULL if block groups are not supported | ||
55 | * by this @a type of block (this is not an error) | ||
56 | */ | ||
57 | static struct GNUNET_BLOCK_Group * | ||
58 | block_plugin_regex_create_group (void *cls, | ||
59 | enum GNUNET_BLOCK_Type type, | ||
60 | uint32_t nonce, | ||
61 | const void *raw_data, | ||
62 | size_t raw_data_size, | ||
63 | va_list va) | ||
64 | { | ||
65 | unsigned int bf_size; | ||
66 | const char *guard; | ||
67 | |||
68 | guard = va_arg (va, const char *); | ||
69 | if (0 == strcmp (guard, | ||
70 | "seen-set-size")) | ||
71 | bf_size = GNUNET_BLOCK_GROUP_compute_bloomfilter_size (va_arg (va, unsigned int), | ||
72 | BLOOMFILTER_K); | ||
73 | else if (0 == strcmp (guard, | ||
74 | "filter-size")) | ||
75 | bf_size = va_arg (va, unsigned int); | ||
76 | else | ||
77 | { | ||
78 | GNUNET_break (0); | ||
79 | bf_size = REGEX_BF_SIZE; | ||
80 | } | ||
81 | GNUNET_break (NULL == va_arg (va, const char *)); | ||
82 | return GNUNET_BLOCK_GROUP_bf_create (cls, | ||
83 | bf_size, | ||
84 | BLOOMFILTER_K, | ||
85 | type, | ||
86 | nonce, | ||
87 | raw_data, | ||
88 | raw_data_size); | ||
89 | } | ||
90 | |||
91 | |||
92 | /** | ||
93 | * Function called to validate a reply or a request of type | ||
94 | * #GNUNET_BLOCK_TYPE_REGEX. | ||
95 | * For request evaluation, pass "NULL" for the reply_block. | ||
96 | * Note that it is assumed that the reply has already been | ||
97 | * matched to the key (and signatures checked) as it would | ||
98 | * be done with the #GNUNET_BLOCK_get_key() function. | ||
99 | * | ||
100 | * @param cls closure | ||
101 | * @param type block type | ||
102 | * @param bg block group to evaluate against | ||
103 | * @param eo control flags | ||
104 | * @param query original query (hash) | ||
105 | * @param xquery extrended query data (can be NULL, depending on type) | ||
106 | * @param xquery_size number of bytes in @a xquery | ||
107 | * @param reply_block response to validate | ||
108 | * @param reply_block_size number of bytes in @a reply_block | ||
109 | * @return characterization of result | ||
110 | */ | ||
111 | static enum GNUNET_BLOCK_EvaluationResult | ||
112 | evaluate_block_regex (void *cls, | ||
113 | enum GNUNET_BLOCK_Type type, | ||
114 | struct GNUNET_BLOCK_Group *bg, | ||
115 | enum GNUNET_BLOCK_EvaluationOptions eo, | ||
116 | const struct GNUNET_HashCode *query, | ||
117 | const void *xquery, | ||
118 | size_t xquery_size, | ||
119 | const void *reply_block, | ||
120 | size_t reply_block_size) | ||
121 | { | ||
122 | struct GNUNET_HashCode chash; | ||
123 | |||
124 | if (NULL == reply_block) | ||
125 | { | ||
126 | if (0 != xquery_size) | ||
127 | { | ||
128 | const char *s; | ||
129 | |||
130 | s = (const char *) xquery; | ||
131 | if ('\0' != s[xquery_size - 1]) /* must be valid 0-terminated string */ | ||
132 | { | ||
133 | GNUNET_break_op (0); | ||
134 | return GNUNET_BLOCK_EVALUATION_REQUEST_INVALID; | ||
135 | } | ||
136 | } | ||
137 | return GNUNET_BLOCK_EVALUATION_REQUEST_VALID; | ||
138 | } | ||
139 | if (0 != xquery_size) | ||
140 | { | ||
141 | const char *s; | ||
142 | |||
143 | s = (const char *) xquery; | ||
144 | if ('\0' != s[xquery_size - 1]) /* must be valid 0-terminated string */ | ||
145 | { | ||
146 | GNUNET_break_op (0); | ||
147 | return GNUNET_BLOCK_EVALUATION_REQUEST_INVALID; | ||
148 | } | ||
149 | } | ||
150 | else if (NULL != query) | ||
151 | { | ||
152 | /* xquery is required for regex GETs, at least an empty string */ | ||
153 | GNUNET_break_op (0); | ||
154 | GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "type %d, query %p, xquery %p\n", | ||
155 | type, query, xquery); | ||
156 | return GNUNET_BLOCK_EVALUATION_REQUEST_INVALID; | ||
157 | } | ||
158 | switch (REGEX_BLOCK_check (reply_block, | ||
159 | reply_block_size, | ||
160 | query, | ||
161 | xquery)) | ||
162 | { | ||
163 | case GNUNET_SYSERR: | ||
164 | GNUNET_break_op(0); | ||
165 | return GNUNET_BLOCK_EVALUATION_RESULT_INVALID; | ||
166 | case GNUNET_NO: | ||
167 | /* xquery missmatch, can happen */ | ||
168 | return GNUNET_BLOCK_EVALUATION_RESULT_IRRELEVANT; | ||
169 | default: | ||
170 | break; | ||
171 | } | ||
172 | GNUNET_CRYPTO_hash (reply_block, | ||
173 | reply_block_size, | ||
174 | &chash); | ||
175 | if (GNUNET_YES == | ||
176 | GNUNET_BLOCK_GROUP_bf_test_and_set (bg, | ||
177 | &chash)) | ||
178 | return GNUNET_BLOCK_EVALUATION_OK_DUPLICATE; | ||
179 | return GNUNET_BLOCK_EVALUATION_OK_MORE; | ||
180 | } | ||
181 | |||
182 | |||
183 | /** | ||
184 | * Function called to validate a reply or a request of type | ||
185 | * #GNUNET_BLOCK_TYPE_REGEX_ACCEPT. | ||
186 | * For request evaluation, pass "NULL" for the reply_block. | ||
187 | * Note that it is assumed that the reply has already been | ||
188 | * matched to the key (and signatures checked) as it would | ||
189 | * be done with the #GNUNET_BLOCK_get_key() function. | ||
190 | * | ||
191 | * @param cls closure | ||
192 | * @param type block type | ||
193 | * @param bg block group to evaluate against | ||
194 | * @param eo control flags | ||
195 | * @param query original query (hash) | ||
196 | * @param xquery extrended query data (can be NULL, depending on type) | ||
197 | * @param xquery_size number of bytes in @a xquery | ||
198 | * @param reply_block response to validate | ||
199 | * @param reply_block_size number of bytes in @a reply_block | ||
200 | * @return characterization of result | ||
201 | */ | ||
202 | static enum GNUNET_BLOCK_EvaluationResult | ||
203 | evaluate_block_regex_accept (void *cls, | ||
204 | enum GNUNET_BLOCK_Type type, | ||
205 | struct GNUNET_BLOCK_Group *bg, | ||
206 | enum GNUNET_BLOCK_EvaluationOptions eo, | ||
207 | const struct GNUNET_HashCode *query, | ||
208 | const void *xquery, | ||
209 | size_t xquery_size, const void *reply_block, | ||
210 | size_t reply_block_size) | ||
211 | { | ||
212 | const struct RegexAcceptBlock *rba; | ||
213 | struct GNUNET_HashCode chash; | ||
214 | |||
215 | if (0 != xquery_size) | ||
216 | { | ||
217 | GNUNET_break_op (0); | ||
218 | return GNUNET_BLOCK_EVALUATION_REQUEST_INVALID; | ||
219 | } | ||
220 | if (NULL == reply_block) | ||
221 | return GNUNET_BLOCK_EVALUATION_REQUEST_VALID; | ||
222 | if (sizeof (struct RegexAcceptBlock) != reply_block_size) | ||
223 | { | ||
224 | GNUNET_break_op(0); | ||
225 | return GNUNET_BLOCK_EVALUATION_RESULT_INVALID; | ||
226 | } | ||
227 | rba = reply_block; | ||
228 | if (ntohl (rba->purpose.size) != | ||
229 | sizeof (struct GNUNET_CRYPTO_EccSignaturePurpose) + | ||
230 | sizeof (struct GNUNET_TIME_AbsoluteNBO) + | ||
231 | sizeof (struct GNUNET_HashCode)) | ||
232 | { | ||
233 | GNUNET_break_op(0); | ||
234 | return GNUNET_BLOCK_EVALUATION_RESULT_INVALID; | ||
235 | } | ||
236 | if (0 == GNUNET_TIME_absolute_get_remaining (GNUNET_TIME_absolute_ntoh (rba->expiration_time)).rel_value_us) | ||
237 | { | ||
238 | /* technically invalid, but can happen without an error, so | ||
239 | we're nice by reporting it as a 'duplicate' */ | ||
240 | return GNUNET_BLOCK_EVALUATION_OK_DUPLICATE; | ||
241 | } | ||
242 | if (GNUNET_OK != | ||
243 | GNUNET_CRYPTO_eddsa_verify (GNUNET_SIGNATURE_PURPOSE_REGEX_ACCEPT, | ||
244 | &rba->purpose, | ||
245 | &rba->signature, | ||
246 | &rba->peer.public_key)) | ||
247 | { | ||
248 | GNUNET_break_op(0); | ||
249 | return GNUNET_BLOCK_EVALUATION_RESULT_INVALID; | ||
250 | } | ||
251 | GNUNET_CRYPTO_hash (reply_block, | ||
252 | reply_block_size, | ||
253 | &chash); | ||
254 | if (GNUNET_YES == | ||
255 | GNUNET_BLOCK_GROUP_bf_test_and_set (bg, | ||
256 | &chash)) | ||
257 | return GNUNET_BLOCK_EVALUATION_OK_DUPLICATE; | ||
258 | return GNUNET_BLOCK_EVALUATION_OK_MORE; | ||
259 | } | ||
260 | |||
261 | |||
262 | /** | ||
263 | * Function called to validate a reply or a request. For | ||
264 | * request evaluation, simply pass "NULL" for the reply_block. | ||
265 | * Note that it is assumed that the reply has already been | ||
266 | * matched to the key (and signatures checked) as it would | ||
267 | * be done with the #GNUNET_BLOCK_get_key() function. | ||
268 | * | ||
269 | * @param cls closure | ||
270 | * @param ctx block context | ||
271 | * @param type block type | ||
272 | * @param bg group to evaluate against | ||
273 | * @param eo control flags | ||
274 | * @param query original query (hash) | ||
275 | * @param xquery extrended query data (can be NULL, depending on type) | ||
276 | * @param xquery_size number of bytes in xquery | ||
277 | * @param reply_block response to validate | ||
278 | * @param reply_block_size number of bytes in reply block | ||
279 | * @return characterization of result | ||
280 | */ | ||
281 | static enum GNUNET_BLOCK_EvaluationResult | ||
282 | block_plugin_regex_evaluate (void *cls, | ||
283 | struct GNUNET_BLOCK_Context *ctx, | ||
284 | enum GNUNET_BLOCK_Type type, | ||
285 | struct GNUNET_BLOCK_Group *bg, | ||
286 | enum GNUNET_BLOCK_EvaluationOptions eo, | ||
287 | const struct GNUNET_HashCode *query, | ||
288 | const void *xquery, | ||
289 | size_t xquery_size, | ||
290 | const void *reply_block, | ||
291 | size_t reply_block_size) | ||
292 | { | ||
293 | enum GNUNET_BLOCK_EvaluationResult result; | ||
294 | |||
295 | switch (type) | ||
296 | { | ||
297 | case GNUNET_BLOCK_TYPE_REGEX: | ||
298 | result = evaluate_block_regex (cls, | ||
299 | type, | ||
300 | bg, | ||
301 | eo, | ||
302 | query, | ||
303 | xquery, xquery_size, | ||
304 | reply_block, reply_block_size); | ||
305 | break; | ||
306 | case GNUNET_BLOCK_TYPE_REGEX_ACCEPT: | ||
307 | result = evaluate_block_regex_accept (cls, | ||
308 | type, | ||
309 | bg, | ||
310 | eo, | ||
311 | query, | ||
312 | xquery, xquery_size, | ||
313 | reply_block, reply_block_size); | ||
314 | break; | ||
315 | |||
316 | default: | ||
317 | result = GNUNET_BLOCK_EVALUATION_TYPE_NOT_SUPPORTED; | ||
318 | } | ||
319 | return result; | ||
320 | } | ||
321 | |||
322 | |||
323 | /** | ||
324 | * Function called to obtain the key for a block. | ||
325 | * | ||
326 | * @param cls closure | ||
327 | * @param type block type | ||
328 | * @param block block to get the key for | ||
329 | * @param block_size number of bytes in @a block | ||
330 | * @param key set to the key (query) for the given block | ||
331 | * @return #GNUNET_OK on success, #GNUNET_SYSERR if type not supported | ||
332 | * (or if extracting a key from a block of this type does not work) | ||
333 | */ | ||
334 | static int | ||
335 | block_plugin_regex_get_key (void *cls, | ||
336 | enum GNUNET_BLOCK_Type type, | ||
337 | const void *block, | ||
338 | size_t block_size, | ||
339 | struct GNUNET_HashCode *key) | ||
340 | { | ||
341 | switch (type) | ||
342 | { | ||
343 | case GNUNET_BLOCK_TYPE_REGEX: | ||
344 | if (GNUNET_OK != | ||
345 | REGEX_BLOCK_get_key (block, block_size, | ||
346 | key)) | ||
347 | { | ||
348 | GNUNET_break_op (0); | ||
349 | return GNUNET_NO; | ||
350 | } | ||
351 | return GNUNET_OK; | ||
352 | case GNUNET_BLOCK_TYPE_REGEX_ACCEPT: | ||
353 | if (sizeof (struct RegexAcceptBlock) != block_size) | ||
354 | { | ||
355 | GNUNET_break_op (0); | ||
356 | return GNUNET_NO; | ||
357 | } | ||
358 | *key = ((struct RegexAcceptBlock *) block)->key; | ||
359 | return GNUNET_OK; | ||
360 | default: | ||
361 | GNUNET_break (0); | ||
362 | return GNUNET_SYSERR; | ||
363 | } | ||
364 | } | ||
365 | |||
366 | |||
367 | /** | ||
368 | * Entry point for the plugin. | ||
369 | */ | ||
370 | void * | ||
371 | libgnunet_plugin_block_regex_init (void *cls) | ||
372 | { | ||
373 | static enum GNUNET_BLOCK_Type types[] = | ||
374 | { | ||
375 | GNUNET_BLOCK_TYPE_REGEX, | ||
376 | GNUNET_BLOCK_TYPE_REGEX_ACCEPT, | ||
377 | GNUNET_BLOCK_TYPE_ANY /* end of list */ | ||
378 | }; | ||
379 | struct GNUNET_BLOCK_PluginFunctions *api; | ||
380 | |||
381 | api = GNUNET_new (struct GNUNET_BLOCK_PluginFunctions); | ||
382 | api->evaluate = &block_plugin_regex_evaluate; | ||
383 | api->get_key = &block_plugin_regex_get_key; | ||
384 | api->create_group = &block_plugin_regex_create_group; | ||
385 | api->types = types; | ||
386 | return api; | ||
387 | } | ||
388 | |||
389 | |||
390 | /** | ||
391 | * Exit point from the plugin. | ||
392 | */ | ||
393 | void * | ||
394 | libgnunet_plugin_block_regex_done (void *cls) | ||
395 | { | ||
396 | struct GNUNET_BLOCK_PluginFunctions *api = cls; | ||
397 | |||
398 | GNUNET_free (api); | ||
399 | return NULL; | ||
400 | } | ||
401 | |||
402 | /* end of plugin_block_regex.c */ | ||
diff --git a/src/regex/regex.conf.in b/src/regex/regex.conf.in new file mode 100644 index 000000000..5e68a43da --- /dev/null +++ b/src/regex/regex.conf.in | |||
@@ -0,0 +1,8 @@ | |||
1 | [regex] | ||
2 | START_ON_DEMAND = @START_ON_DEMAND@ | ||
3 | @UNIXONLY@ PORT = 2107 | ||
4 | UNIXPATH = $GNUNET_RUNTIME_DIR/gnunet-service-regex.sock | ||
5 | HOSTNAME = localhost | ||
6 | BINARY = gnunet-service-regex | ||
7 | ACCEPT_FROM = 127.0.0.1; | ||
8 | ACCEPT_FROM6 = ::1; | ||
diff --git a/src/regex/regex_api_announce.c b/src/regex/regex_api_announce.c new file mode 100644 index 000000000..e3ad70c6a --- /dev/null +++ b/src/regex/regex_api_announce.c | |||
@@ -0,0 +1,184 @@ | |||
1 | /* | ||
2 | This file is part of GNUnet | ||
3 | Copyright (C) 2012, 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 | /** | ||
19 | * @file regex/regex_api_announce.c | ||
20 | * @brief access regex service to advertise capabilities via regex | ||
21 | * @author Maximilian Szengel | ||
22 | * @author Christian Grothoff | ||
23 | */ | ||
24 | #include "platform.h" | ||
25 | #include "gnunet_protocols.h" | ||
26 | #include "gnunet_util_lib.h" | ||
27 | #include "gnunet_regex_service.h" | ||
28 | #include "regex_ipc.h" | ||
29 | |||
30 | #define LOG(kind,...) GNUNET_log_from (kind, "regex-api",__VA_ARGS__) | ||
31 | |||
32 | /** | ||
33 | * Handle to store cached data about a regex announce. | ||
34 | */ | ||
35 | struct GNUNET_REGEX_Announcement | ||
36 | { | ||
37 | /** | ||
38 | * Connection to the regex service. | ||
39 | */ | ||
40 | struct GNUNET_MQ_Handle *mq; | ||
41 | |||
42 | /** | ||
43 | * Our configuration. | ||
44 | */ | ||
45 | const struct GNUNET_CONFIGURATION_Handle *cfg; | ||
46 | |||
47 | /** | ||
48 | * Message we're sending to the service. | ||
49 | */ | ||
50 | char *regex; | ||
51 | |||
52 | /** | ||
53 | * Frequency of announcements. | ||
54 | */ | ||
55 | struct GNUNET_TIME_Relative refresh_delay; | ||
56 | |||
57 | /** | ||
58 | * Number of characters per edge. | ||
59 | */ | ||
60 | uint16_t compression; | ||
61 | }; | ||
62 | |||
63 | |||
64 | |||
65 | /** | ||
66 | * (Re)connect to the REGEX service with the given announcement @a a. | ||
67 | * | ||
68 | * @param a REGEX to announce. | ||
69 | */ | ||
70 | static void | ||
71 | announce_reconnect (struct GNUNET_REGEX_Announcement *a); | ||
72 | |||
73 | |||
74 | /** | ||
75 | * We got a disconnect after asking regex to do the announcement. | ||
76 | * Retry. | ||
77 | * | ||
78 | * @param cls the `struct GNUNET_REGEX_Announcement` to retry | ||
79 | * @param error error code | ||
80 | */ | ||
81 | static void | ||
82 | announce_mq_error_handler (void *cls, | ||
83 | enum GNUNET_MQ_Error error) | ||
84 | { | ||
85 | struct GNUNET_REGEX_Announcement *a = cls; | ||
86 | |||
87 | GNUNET_MQ_destroy (a->mq); | ||
88 | a->mq = NULL; | ||
89 | announce_reconnect (a); | ||
90 | } | ||
91 | |||
92 | |||
93 | /** | ||
94 | * (Re)connect to the REGEX service with the given announcement @a a. | ||
95 | * | ||
96 | * @param a REGEX to announce. | ||
97 | */ | ||
98 | static void | ||
99 | announce_reconnect (struct GNUNET_REGEX_Announcement *a) | ||
100 | { | ||
101 | struct GNUNET_MQ_Envelope *env; | ||
102 | struct AnnounceMessage *am; | ||
103 | size_t slen; | ||
104 | |||
105 | a->mq = GNUNET_CLIENT_connect (a->cfg, | ||
106 | "regex", | ||
107 | NULL, | ||
108 | &announce_mq_error_handler, | ||
109 | a); | ||
110 | if (NULL == a->mq) | ||
111 | return; | ||
112 | slen = strlen (a->regex) + 1; | ||
113 | env = GNUNET_MQ_msg_extra (am, | ||
114 | slen, | ||
115 | GNUNET_MESSAGE_TYPE_REGEX_ANNOUNCE); | ||
116 | am->compression = htons (a->compression); | ||
117 | am->reserved = htons (0); | ||
118 | am->refresh_delay = GNUNET_TIME_relative_hton (a->refresh_delay); | ||
119 | GNUNET_memcpy (&am[1], | ||
120 | a->regex, | ||
121 | slen); | ||
122 | GNUNET_MQ_send (a->mq, | ||
123 | env); | ||
124 | } | ||
125 | |||
126 | |||
127 | /** | ||
128 | * Announce the given peer under the given regular expression. | ||
129 | * | ||
130 | * @param cfg configuration to use | ||
131 | * @param regex Regular expression to announce. | ||
132 | * @param refresh_delay after what delay should the announcement be repeated? | ||
133 | * @param compression How many characters per edge can we squeeze? | ||
134 | * @return Handle to reuse o free cached resources. | ||
135 | * Must be freed by calling #GNUNET_REGEX_announce_cancel(). | ||
136 | */ | ||
137 | struct GNUNET_REGEX_Announcement * | ||
138 | GNUNET_REGEX_announce (const struct GNUNET_CONFIGURATION_Handle *cfg, | ||
139 | const char *regex, | ||
140 | struct GNUNET_TIME_Relative refresh_delay, | ||
141 | uint16_t compression) | ||
142 | { | ||
143 | struct GNUNET_REGEX_Announcement *a; | ||
144 | size_t slen; | ||
145 | |||
146 | slen = strlen (regex) + 1; | ||
147 | if (slen + sizeof (struct AnnounceMessage) >= GNUNET_MAX_MESSAGE_SIZE) | ||
148 | { | ||
149 | GNUNET_log (GNUNET_ERROR_TYPE_WARNING, | ||
150 | _("Regex `%s' is too long!\n"), | ||
151 | regex); | ||
152 | GNUNET_break (0); | ||
153 | return NULL; | ||
154 | } | ||
155 | a = GNUNET_new (struct GNUNET_REGEX_Announcement); | ||
156 | a->cfg = cfg; | ||
157 | a->refresh_delay = refresh_delay; | ||
158 | a->compression = compression; | ||
159 | a->regex = GNUNET_strdup (regex); | ||
160 | announce_reconnect (a); | ||
161 | if (NULL == a->mq) | ||
162 | { | ||
163 | GNUNET_free (a->regex); | ||
164 | GNUNET_free (a); | ||
165 | return NULL; | ||
166 | } | ||
167 | return a; | ||
168 | } | ||
169 | |||
170 | |||
171 | /** | ||
172 | * Stop announcing the regex specified by the given handle. | ||
173 | * | ||
174 | * @param a handle returned by a previous #GNUNET_REGEX_announce() call. | ||
175 | */ | ||
176 | void | ||
177 | GNUNET_REGEX_announce_cancel (struct GNUNET_REGEX_Announcement *a) | ||
178 | { | ||
179 | GNUNET_MQ_destroy (a->mq); | ||
180 | GNUNET_free (a->regex); | ||
181 | GNUNET_free (a); | ||
182 | } | ||
183 | |||
184 | /* end of regex_api_announce.c */ | ||
diff --git a/src/regex/regex_api_search.c b/src/regex/regex_api_search.c new file mode 100644 index 000000000..2e2536a02 --- /dev/null +++ b/src/regex/regex_api_search.c | |||
@@ -0,0 +1,248 @@ | |||
1 | /* | ||
2 | This file is part of GNUnet | ||
3 | Copyright (C) 2012, 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 | /** | ||
19 | * @file regex/regex_api_search.c | ||
20 | * @brief access regex service to discover | ||
21 | * peers using matching strings | ||
22 | * @author Maximilian Szengel | ||
23 | * @author Christian Grothoff | ||
24 | */ | ||
25 | #include "platform.h" | ||
26 | #include "gnunet_protocols.h" | ||
27 | #include "gnunet_util_lib.h" | ||
28 | #include "gnunet_regex_service.h" | ||
29 | #include "regex_ipc.h" | ||
30 | |||
31 | #define LOG(kind,...) GNUNET_log_from (kind, "regex-api",__VA_ARGS__) | ||
32 | |||
33 | |||
34 | /** | ||
35 | * Handle to store data about a regex search. | ||
36 | */ | ||
37 | struct GNUNET_REGEX_Search | ||
38 | { | ||
39 | /** | ||
40 | * Connection to the regex service. | ||
41 | */ | ||
42 | struct GNUNET_MQ_Handle *mq; | ||
43 | |||
44 | /** | ||
45 | * Our configuration. | ||
46 | */ | ||
47 | const struct GNUNET_CONFIGURATION_Handle *cfg; | ||
48 | |||
49 | /** | ||
50 | * Function to call with results. | ||
51 | */ | ||
52 | GNUNET_REGEX_Found callback; | ||
53 | |||
54 | /** | ||
55 | * Closure for @e callback. | ||
56 | */ | ||
57 | void *callback_cls; | ||
58 | |||
59 | /** | ||
60 | * Search string to transmit to the service. | ||
61 | */ | ||
62 | char *string; | ||
63 | }; | ||
64 | |||
65 | |||
66 | /** | ||
67 | * (Re)connect to the REGEX service for the given search @a s. | ||
68 | * | ||
69 | * @param s context for the search search for | ||
70 | */ | ||
71 | static void | ||
72 | search_reconnect (struct GNUNET_REGEX_Search *s); | ||
73 | |||
74 | |||
75 | /** | ||
76 | * We got a response or disconnect after asking regex | ||
77 | * to do the search. Check it is well-formed. | ||
78 | * | ||
79 | * @param cls the `struct GNUNET_REGEX_Search` to handle reply for | ||
80 | * @param result the message | ||
81 | * @return #GNUNET_SYSERR if @a rm is not well-formed. | ||
82 | */ | ||
83 | static int | ||
84 | check_search_response (void *cls, | ||
85 | const struct ResultMessage *result) | ||
86 | { | ||
87 | uint16_t size = ntohs (result->header.size) - sizeof (*result); | ||
88 | uint16_t gpl = ntohs (result->get_path_length); | ||
89 | uint16_t ppl = ntohs (result->put_path_length); | ||
90 | |||
91 | if (size != (gpl + ppl) * sizeof (struct GNUNET_PeerIdentity)) | ||
92 | { | ||
93 | GNUNET_break (0); | ||
94 | return GNUNET_SYSERR; | ||
95 | } | ||
96 | return GNUNET_OK; | ||
97 | } | ||
98 | |||
99 | |||
100 | /** | ||
101 | * We got a response or disconnect after asking regex | ||
102 | * to do the search. Handle it. | ||
103 | * | ||
104 | * @param cls the `struct GNUNET_REGEX_Search` to handle reply for | ||
105 | * @param result the message | ||
106 | */ | ||
107 | static void | ||
108 | handle_search_response (void *cls, | ||
109 | const struct ResultMessage *result) | ||
110 | { | ||
111 | struct GNUNET_REGEX_Search *s = cls; | ||
112 | uint16_t gpl = ntohs (result->get_path_length); | ||
113 | uint16_t ppl = ntohs (result->put_path_length); | ||
114 | const struct GNUNET_PeerIdentity *pid; | ||
115 | |||
116 | pid = &result->id; | ||
117 | LOG (GNUNET_ERROR_TYPE_DEBUG, | ||
118 | "Got regex result %s\n", | ||
119 | GNUNET_i2s (pid)); | ||
120 | s->callback (s->callback_cls, | ||
121 | pid, | ||
122 | &pid[1], | ||
123 | gpl, | ||
124 | &pid[1 + gpl], | ||
125 | ppl); | ||
126 | } | ||
127 | |||
128 | |||
129 | /** | ||
130 | * We got a disconnect after asking regex to do the announcement. | ||
131 | * Retry. | ||
132 | * | ||
133 | * @param cls the `struct GNUNET_REGEX_Search` to retry | ||
134 | * @param error error code | ||
135 | */ | ||
136 | static void | ||
137 | mq_error_handler (void *cls, | ||
138 | enum GNUNET_MQ_Error error) | ||
139 | { | ||
140 | struct GNUNET_REGEX_Search *s = cls; | ||
141 | |||
142 | GNUNET_MQ_destroy (s->mq); | ||
143 | s->mq = NULL; | ||
144 | search_reconnect (s); | ||
145 | } | ||
146 | |||
147 | |||
148 | /** | ||
149 | * (Re)connect to the REGEX service for the given search @a s. | ||
150 | * | ||
151 | * @param s context for the search search for | ||
152 | */ | ||
153 | static void | ||
154 | search_reconnect (struct GNUNET_REGEX_Search *s) | ||
155 | { | ||
156 | struct GNUNET_MQ_MessageHandler handlers[] = { | ||
157 | GNUNET_MQ_hd_var_size (search_response, | ||
158 | GNUNET_MESSAGE_TYPE_REGEX_RESULT, | ||
159 | struct ResultMessage, | ||
160 | s), | ||
161 | GNUNET_MQ_handler_end () | ||
162 | }; | ||
163 | size_t slen = strlen (s->string) + 1; | ||
164 | struct GNUNET_MQ_Envelope *env; | ||
165 | struct RegexSearchMessage *rsm; | ||
166 | |||
167 | GNUNET_assert (NULL == s->mq); | ||
168 | s->mq = GNUNET_CLIENT_connect (s->cfg, | ||
169 | "regex", | ||
170 | handlers, | ||
171 | &mq_error_handler, | ||
172 | s); | ||
173 | if (NULL == s->mq) | ||
174 | return; | ||
175 | env = GNUNET_MQ_msg_extra (rsm, | ||
176 | slen, | ||
177 | GNUNET_MESSAGE_TYPE_REGEX_SEARCH); | ||
178 | GNUNET_memcpy (&rsm[1], | ||
179 | s->string, | ||
180 | slen); | ||
181 | GNUNET_MQ_send (s->mq, | ||
182 | env); | ||
183 | } | ||
184 | |||
185 | |||
186 | /** | ||
187 | * Search for a peer offering a regex matching certain string in the DHT. | ||
188 | * The search runs until #GNUNET_REGEX_search_cancel() is called, even if results | ||
189 | * are returned. | ||
190 | * | ||
191 | * @param cfg configuration to use | ||
192 | * @param string String to match against the regexes in the DHT. | ||
193 | * @param callback Callback for found peers. | ||
194 | * @param callback_cls Closure for @c callback. | ||
195 | * @return Handle to stop search and free resources. | ||
196 | * Must be freed by calling #GNUNET_REGEX_search_cancel(). | ||
197 | */ | ||
198 | struct GNUNET_REGEX_Search * | ||
199 | GNUNET_REGEX_search (const struct GNUNET_CONFIGURATION_Handle *cfg, | ||
200 | const char *string, | ||
201 | GNUNET_REGEX_Found callback, | ||
202 | void *callback_cls) | ||
203 | { | ||
204 | struct GNUNET_REGEX_Search *s; | ||
205 | size_t slen = strlen (string) + 1; | ||
206 | |||
207 | if (slen + sizeof (struct RegexSearchMessage) >= GNUNET_MAX_MESSAGE_SIZE) | ||
208 | { | ||
209 | GNUNET_log (GNUNET_ERROR_TYPE_WARNING, | ||
210 | _("Search string `%s' is too long!\n"), | ||
211 | string); | ||
212 | GNUNET_break (0); | ||
213 | return NULL; | ||
214 | } | ||
215 | LOG (GNUNET_ERROR_TYPE_DEBUG, | ||
216 | "Starting regex search for %s\n", | ||
217 | string); | ||
218 | s = GNUNET_new (struct GNUNET_REGEX_Search); | ||
219 | s->cfg = cfg; | ||
220 | s->string = GNUNET_strdup (string); | ||
221 | s->callback = callback; | ||
222 | s->callback_cls = callback_cls; | ||
223 | search_reconnect (s); | ||
224 | if (NULL == s->mq) | ||
225 | { | ||
226 | GNUNET_free (s->string); | ||
227 | GNUNET_free (s); | ||
228 | return NULL; | ||
229 | } | ||
230 | return s; | ||
231 | } | ||
232 | |||
233 | |||
234 | /** | ||
235 | * Stop search and free all data used by a #GNUNET_REGEX_search() call. | ||
236 | * | ||
237 | * @param s Handle returned by a previous #GNUNET_REGEX_search() call. | ||
238 | */ | ||
239 | void | ||
240 | GNUNET_REGEX_search_cancel (struct GNUNET_REGEX_Search *s) | ||
241 | { | ||
242 | GNUNET_MQ_destroy (s->mq); | ||
243 | GNUNET_free (s->string); | ||
244 | GNUNET_free (s); | ||
245 | } | ||
246 | |||
247 | |||
248 | /* end of regex_api_search.c */ | ||
diff --git a/src/regex/regex_block_lib.c b/src/regex/regex_block_lib.c new file mode 100644 index 000000000..33eaf466f --- /dev/null +++ b/src/regex/regex_block_lib.c | |||
@@ -0,0 +1,469 @@ | |||
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 | /** | ||
19 | * @author Bartlomiej Polot | ||
20 | * @file regex/regex_block_lib.c | ||
21 | * @brief functions for manipulating non-accept blocks stored for | ||
22 | * regex in the DHT | ||
23 | */ | ||
24 | #include "platform.h" | ||
25 | #include "regex_block_lib.h" | ||
26 | #include "gnunet_constants.h" | ||
27 | |||
28 | #define LOG(kind,...) GNUNET_log_from (kind,"regex-bck",__VA_ARGS__) | ||
29 | |||
30 | GNUNET_NETWORK_STRUCT_BEGIN | ||
31 | |||
32 | /** | ||
33 | * Information for each edge. | ||
34 | */ | ||
35 | struct EdgeInfo | ||
36 | { | ||
37 | /** | ||
38 | * Index of the destination of this edge in the | ||
39 | * unique destinations array. | ||
40 | */ | ||
41 | uint16_t destination_index GNUNET_PACKED; | ||
42 | |||
43 | /** | ||
44 | * Number of bytes the token for this edge takes in the | ||
45 | * token area. | ||
46 | */ | ||
47 | uint16_t token_length GNUNET_PACKED; | ||
48 | }; | ||
49 | |||
50 | |||
51 | /** | ||
52 | * @brief Block to announce a regex state. | ||
53 | */ | ||
54 | struct RegexBlock | ||
55 | { | ||
56 | |||
57 | /** | ||
58 | * Length of the proof regex string. | ||
59 | */ | ||
60 | uint16_t proof_len GNUNET_PACKED; | ||
61 | |||
62 | /** | ||
63 | * Is this state an accepting state? | ||
64 | */ | ||
65 | int16_t is_accepting GNUNET_PACKED; | ||
66 | |||
67 | /** | ||
68 | * Number of edges parting from this state. | ||
69 | */ | ||
70 | uint16_t num_edges GNUNET_PACKED; | ||
71 | |||
72 | /** | ||
73 | * Nubmer of unique destinations reachable from this state. | ||
74 | */ | ||
75 | uint16_t num_destinations GNUNET_PACKED; | ||
76 | |||
77 | /* followed by 'struct GNUNET_HashCode[num_destinations]' */ | ||
78 | |||
79 | /* followed by 'struct EdgeInfo[edge_destination_indices]' */ | ||
80 | |||
81 | /* followed by 'char proof[n_proof]', NOT 0-terminated */ | ||
82 | |||
83 | /* followed by 'char tokens[num_edges][edge_info[k].token_length]'; | ||
84 | essentially all of the tokens one after the other in the | ||
85 | order of the edges; tokens are NOT 0-terminated */ | ||
86 | |||
87 | }; | ||
88 | |||
89 | |||
90 | GNUNET_NETWORK_STRUCT_END | ||
91 | |||
92 | |||
93 | /** | ||
94 | * Test if this block is marked as being an accept state. | ||
95 | * | ||
96 | * @param block block to test | ||
97 | * @param size number of bytes in block | ||
98 | * @return #GNUNET_YES if the block is accepting, #GNUNET_NO if not | ||
99 | */ | ||
100 | int | ||
101 | GNUNET_BLOCK_is_accepting (const struct RegexBlock *block, | ||
102 | size_t size) | ||
103 | { | ||
104 | if (size < sizeof (struct RegexBlock)) | ||
105 | { | ||
106 | GNUNET_break_op (0); | ||
107 | return GNUNET_SYSERR; | ||
108 | } | ||
109 | return ntohs (block->is_accepting); | ||
110 | } | ||
111 | |||
112 | |||
113 | /** | ||
114 | * Check if the given 'proof' matches the given 'key'. | ||
115 | * | ||
116 | * @param proof partial regex of a state | ||
117 | * @param proof_len number of bytes in 'proof' | ||
118 | * @param key hash of a state. | ||
119 | * @return #GNUNET_OK if the proof is valid for the given key. | ||
120 | */ | ||
121 | int | ||
122 | REGEX_BLOCK_check_proof (const char *proof, | ||
123 | size_t proof_len, | ||
124 | const struct GNUNET_HashCode *key) | ||
125 | { | ||
126 | struct GNUNET_HashCode key_check; | ||
127 | |||
128 | if ( (NULL == proof) || (NULL == key)) | ||
129 | { | ||
130 | GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Proof check failed, was NULL.\n"); | ||
131 | return GNUNET_NO; | ||
132 | } | ||
133 | GNUNET_CRYPTO_hash (proof, proof_len, &key_check); | ||
134 | return (0 == | ||
135 | GNUNET_CRYPTO_hash_cmp (key, &key_check)) ? GNUNET_OK : GNUNET_NO; | ||
136 | } | ||
137 | |||
138 | |||
139 | /** | ||
140 | * Struct to keep track of the xquery while iterating all the edges in a block. | ||
141 | */ | ||
142 | struct CheckEdgeContext | ||
143 | { | ||
144 | /** | ||
145 | * Xquery: string we are looking for. | ||
146 | */ | ||
147 | const char *xquery; | ||
148 | |||
149 | /** | ||
150 | * Has any edge matched the xquery so far? (GNUNET_OK / GNUNET_NO) | ||
151 | */ | ||
152 | int found; | ||
153 | |||
154 | }; | ||
155 | |||
156 | |||
157 | /** | ||
158 | * Iterator over all edges in a block, checking for a presence of a given query. | ||
159 | * | ||
160 | * @param cls Closure, (xquery context). | ||
161 | * @param token Token that follows to next state. | ||
162 | * @param len Lenght of token. | ||
163 | * @param key Hash of next state. | ||
164 | * | ||
165 | * @return #GNUNET_YES, to keep iterating | ||
166 | */ | ||
167 | static int | ||
168 | check_edge (void *cls, | ||
169 | const char *token, | ||
170 | size_t len, | ||
171 | const struct GNUNET_HashCode *key) | ||
172 | { | ||
173 | struct CheckEdgeContext *ctx = cls; | ||
174 | |||
175 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, | ||
176 | "edge %.*s [%u]: %s\n", | ||
177 | (int) len, | ||
178 | token, | ||
179 | (unsigned int) len, | ||
180 | GNUNET_h2s (key)); | ||
181 | if (NULL == ctx->xquery) | ||
182 | return GNUNET_YES; | ||
183 | if (strlen (ctx->xquery) < len) | ||
184 | return GNUNET_YES; /* too long */ | ||
185 | if (0 == strncmp (ctx->xquery, token, len)) | ||
186 | ctx->found = GNUNET_OK; | ||
187 | return GNUNET_YES; /* keep checking for malformed data! */ | ||
188 | } | ||
189 | |||
190 | |||
191 | /** | ||
192 | * Check if the regex block is well formed, including all edges. | ||
193 | * | ||
194 | * @param block The start of the block. | ||
195 | * @param size The size of the block. | ||
196 | * @param query the query for the block | ||
197 | * @param xquery String describing the edge we are looking for. | ||
198 | * Can be NULL in case this is a put block. | ||
199 | * @return #GNUNET_OK in case it's fine. | ||
200 | * #GNUNET_NO in case the xquery exists and is not found (IRRELEVANT). | ||
201 | * #GNUNET_SYSERR if the block is invalid. | ||
202 | */ | ||
203 | int | ||
204 | REGEX_BLOCK_check (const struct RegexBlock *block, | ||
205 | size_t size, | ||
206 | const struct GNUNET_HashCode *query, | ||
207 | const char *xquery) | ||
208 | { | ||
209 | struct GNUNET_HashCode key; | ||
210 | struct CheckEdgeContext ctx; | ||
211 | int res; | ||
212 | |||
213 | LOG (GNUNET_ERROR_TYPE_DEBUG, | ||
214 | "Block check\n"); | ||
215 | if (GNUNET_OK != | ||
216 | REGEX_BLOCK_get_key (block, size, | ||
217 | &key)) | ||
218 | { | ||
219 | GNUNET_break_op (0); | ||
220 | return GNUNET_SYSERR; | ||
221 | } | ||
222 | if (NULL != query && | ||
223 | 0 != memcmp (&key, | ||
224 | query, | ||
225 | sizeof (struct GNUNET_HashCode))) | ||
226 | { | ||
227 | GNUNET_break_op (0); | ||
228 | return GNUNET_SYSERR; | ||
229 | } | ||
230 | if ( (GNUNET_YES == ntohs (block->is_accepting)) && | ||
231 | ( (NULL == xquery) || ('\0' == xquery[0]) ) ) | ||
232 | { | ||
233 | LOG (GNUNET_ERROR_TYPE_DEBUG, | ||
234 | " out! Is accepting: %u, xquery %p\n", | ||
235 | ntohs(block->is_accepting), | ||
236 | xquery); | ||
237 | return GNUNET_OK; | ||
238 | } | ||
239 | ctx.xquery = xquery; | ||
240 | ctx.found = GNUNET_NO; | ||
241 | res = REGEX_BLOCK_iterate (block, size, &check_edge, &ctx); | ||
242 | if (GNUNET_SYSERR == res) | ||
243 | return GNUNET_SYSERR; | ||
244 | if (NULL == xquery) | ||
245 | return GNUNET_YES; | ||
246 | LOG (GNUNET_ERROR_TYPE_DEBUG, "Result %d\n", ctx.found); | ||
247 | return ctx.found; | ||
248 | } | ||
249 | |||
250 | |||
251 | /** | ||
252 | * Obtain the key that a particular block is to be stored under. | ||
253 | * | ||
254 | * @param block block to get the key from | ||
255 | * @param block_len number of bytes in block | ||
256 | * @param key where to store the key | ||
257 | * @return #GNUNET_OK on success, #GNUNET_SYSERR if the block is malformed | ||
258 | */ | ||
259 | int | ||
260 | REGEX_BLOCK_get_key (const struct RegexBlock *block, | ||
261 | size_t block_len, | ||
262 | struct GNUNET_HashCode *key) | ||
263 | { | ||
264 | uint16_t len; | ||
265 | const struct GNUNET_HashCode *destinations; | ||
266 | const struct EdgeInfo *edges; | ||
267 | uint16_t num_destinations; | ||
268 | uint16_t num_edges; | ||
269 | size_t total; | ||
270 | |||
271 | if (block_len < sizeof (struct RegexBlock)) | ||
272 | { | ||
273 | GNUNET_break_op (0); | ||
274 | return GNUNET_SYSERR; | ||
275 | } | ||
276 | num_destinations = ntohs (block->num_destinations); | ||
277 | num_edges = ntohs (block->num_edges); | ||
278 | len = ntohs (block->proof_len); | ||
279 | destinations = (const struct GNUNET_HashCode *) &block[1]; | ||
280 | edges = (const struct EdgeInfo *) &destinations[num_destinations]; | ||
281 | total = sizeof (struct RegexBlock) + num_destinations * sizeof (struct GNUNET_HashCode) + num_edges * sizeof (struct EdgeInfo) + len; | ||
282 | if (block_len < total) | ||
283 | { | ||
284 | GNUNET_break_op (0); | ||
285 | return GNUNET_SYSERR; | ||
286 | } | ||
287 | GNUNET_CRYPTO_hash (&edges[num_edges], len, key); | ||
288 | return GNUNET_OK; | ||
289 | } | ||
290 | |||
291 | |||
292 | /** | ||
293 | * Iterate over all edges of a block of a regex state. | ||
294 | * | ||
295 | * @param block Block to iterate over. | ||
296 | * @param size Size of @a block. | ||
297 | * @param iterator Function to call on each edge in the block. | ||
298 | * @param iter_cls Closure for the @a iterator. | ||
299 | * @return #GNUNET_SYSERR if an error has been encountered. | ||
300 | * #GNUNET_OK if no error has been encountered. | ||
301 | * Note that if the iterator stops the iteration by returning | ||
302 | * #GNUNET_NO, the block will no longer be checked for further errors. | ||
303 | * The return value will be GNUNET_OK meaning that no errors were | ||
304 | * found until the edge last notified to the iterator, but there might | ||
305 | * be errors in further edges. | ||
306 | */ | ||
307 | int | ||
308 | REGEX_BLOCK_iterate (const struct RegexBlock *block, | ||
309 | size_t size, | ||
310 | REGEX_INTERNAL_EgdeIterator iterator, | ||
311 | void *iter_cls) | ||
312 | { | ||
313 | uint16_t len; | ||
314 | const struct GNUNET_HashCode *destinations; | ||
315 | const struct EdgeInfo *edges; | ||
316 | const char *aux; | ||
317 | uint16_t num_destinations; | ||
318 | uint16_t num_edges; | ||
319 | size_t total; | ||
320 | unsigned int n; | ||
321 | size_t off; | ||
322 | |||
323 | LOG (GNUNET_ERROR_TYPE_DEBUG, "Block iterate\n"); | ||
324 | if (size < sizeof (struct RegexBlock)) | ||
325 | { | ||
326 | GNUNET_break_op (0); | ||
327 | return GNUNET_SYSERR; | ||
328 | } | ||
329 | num_destinations = ntohs (block->num_destinations); | ||
330 | num_edges = ntohs (block->num_edges); | ||
331 | len = ntohs (block->proof_len); | ||
332 | destinations = (const struct GNUNET_HashCode *) &block[1]; | ||
333 | edges = (const struct EdgeInfo *) &destinations[num_destinations]; | ||
334 | aux = (const char *) &edges[num_edges]; | ||
335 | total = sizeof (struct RegexBlock) + num_destinations * sizeof (struct GNUNET_HashCode) + num_edges * sizeof (struct EdgeInfo) + len; | ||
336 | if (size < total) | ||
337 | { | ||
338 | GNUNET_break_op (0); | ||
339 | return GNUNET_SYSERR; | ||
340 | } | ||
341 | for (n=0;n<num_edges;n++) | ||
342 | total += ntohs (edges[n].token_length); | ||
343 | if (size != total) | ||
344 | { | ||
345 | fprintf (stderr, "Expected %u, got %u\n", | ||
346 | (unsigned int) size, | ||
347 | (unsigned int) total); | ||
348 | GNUNET_break_op (0); | ||
349 | return GNUNET_SYSERR; | ||
350 | } | ||
351 | off = len; | ||
352 | LOG (GNUNET_ERROR_TYPE_DEBUG, | ||
353 | "Start iterating block of size %u, proof %u, off %u edges %u\n", | ||
354 | size, len, off, n); | ||
355 | /* &aux[off] always points to our token */ | ||
356 | for (n=0;n<num_edges;n++) | ||
357 | { | ||
358 | LOG (GNUNET_ERROR_TYPE_DEBUG, | ||
359 | "Edge %u/%u, off %u tokenlen %u (%.*s)\n", | ||
360 | n+1, num_edges, off, | ||
361 | ntohs (edges[n].token_length), ntohs (edges[n].token_length), | ||
362 | &aux[off]); | ||
363 | if (NULL != iterator) | ||
364 | if (GNUNET_NO == iterator (iter_cls, | ||
365 | &aux[off], | ||
366 | ntohs (edges[n].token_length), | ||
367 | &destinations[ntohs (edges[n].destination_index)])) | ||
368 | return GNUNET_OK; | ||
369 | off += ntohs (edges[n].token_length); | ||
370 | } | ||
371 | return GNUNET_OK; | ||
372 | } | ||
373 | |||
374 | |||
375 | /** | ||
376 | * Construct a regex block to be stored in the DHT. | ||
377 | * | ||
378 | * @param proof proof string for the block | ||
379 | * @param num_edges number of edges in the block | ||
380 | * @param edges the edges of the block | ||
381 | * @param accepting is this an accepting state | ||
382 | * @param rsize set to the size of the returned block (OUT-only) | ||
383 | * @return the regex block, NULL on error | ||
384 | */ | ||
385 | struct RegexBlock * | ||
386 | REGEX_BLOCK_create (const char *proof, | ||
387 | unsigned int num_edges, | ||
388 | const struct REGEX_BLOCK_Edge *edges, | ||
389 | int accepting, | ||
390 | size_t *rsize) | ||
391 | { | ||
392 | struct RegexBlock *block; | ||
393 | struct GNUNET_HashCode destinations[1024]; /* 1024 = 64k/64 bytes/key == absolute MAX */ | ||
394 | uint16_t destination_indices[num_edges]; | ||
395 | struct GNUNET_HashCode *dests; | ||
396 | struct EdgeInfo *edgeinfos; | ||
397 | size_t off; | ||
398 | size_t len; | ||
399 | size_t total; | ||
400 | size_t slen; | ||
401 | unsigned int unique_destinations; | ||
402 | unsigned int j; | ||
403 | unsigned int i; | ||
404 | char *aux; | ||
405 | |||
406 | len = strlen (proof); | ||
407 | if (len > UINT16_MAX) | ||
408 | { | ||
409 | GNUNET_break (0); | ||
410 | return NULL; | ||
411 | } | ||
412 | unique_destinations = 0; | ||
413 | total = sizeof (struct RegexBlock) + len; | ||
414 | for (i=0;i<num_edges;i++) | ||
415 | { | ||
416 | slen = strlen (edges[i].label); | ||
417 | if (slen > UINT16_MAX) | ||
418 | { | ||
419 | GNUNET_break (0); | ||
420 | return NULL; | ||
421 | } | ||
422 | total += slen; | ||
423 | for (j=0;j<unique_destinations;j++) | ||
424 | if (0 == memcmp (&destinations[j], | ||
425 | &edges[i].destination, | ||
426 | sizeof (struct GNUNET_HashCode))) | ||
427 | break; | ||
428 | if (j >= 1024) | ||
429 | { | ||
430 | GNUNET_break (0); | ||
431 | return NULL; | ||
432 | } | ||
433 | destination_indices[i] = j; | ||
434 | if (j == unique_destinations) | ||
435 | destinations[unique_destinations++] = edges[i].destination; | ||
436 | } | ||
437 | total += num_edges * sizeof (struct EdgeInfo) + unique_destinations * sizeof (struct GNUNET_HashCode); | ||
438 | if (total >= GNUNET_CONSTANTS_MAX_BLOCK_SIZE) | ||
439 | { | ||
440 | GNUNET_break (0); | ||
441 | return NULL; | ||
442 | } | ||
443 | block = GNUNET_malloc (total); | ||
444 | block->proof_len = htons (len); | ||
445 | block->is_accepting = htons (accepting); | ||
446 | block->num_edges = htons (num_edges); | ||
447 | block->num_destinations = htons (unique_destinations); | ||
448 | dests = (struct GNUNET_HashCode *) &block[1]; | ||
449 | GNUNET_memcpy (dests, destinations, sizeof (struct GNUNET_HashCode) * unique_destinations); | ||
450 | edgeinfos = (struct EdgeInfo *) &dests[unique_destinations]; | ||
451 | aux = (char *) &edgeinfos[num_edges]; | ||
452 | off = len; | ||
453 | GNUNET_memcpy (aux, proof, len); | ||
454 | for (i=0;i<num_edges;i++) | ||
455 | { | ||
456 | slen = strlen (edges[i].label); | ||
457 | edgeinfos[i].token_length = htons ((uint16_t) slen); | ||
458 | edgeinfos[i].destination_index = htons (destination_indices[i]); | ||
459 | GNUNET_memcpy (&aux[off], | ||
460 | edges[i].label, | ||
461 | slen); | ||
462 | off += slen; | ||
463 | } | ||
464 | *rsize = total; | ||
465 | return block; | ||
466 | } | ||
467 | |||
468 | |||
469 | /* end of regex_block_lib.c */ | ||
diff --git a/src/regex/regex_block_lib.h b/src/regex/regex_block_lib.h new file mode 100644 index 000000000..c5f5f31c0 --- /dev/null +++ b/src/regex/regex_block_lib.h | |||
@@ -0,0 +1,191 @@ | |||
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 | |||
19 | /** | ||
20 | * @author Bartlomiej Polot | ||
21 | * @file regex/regex_block_lib.h | ||
22 | * @brief common function to manipulate blocks stored by regex in the DHT | ||
23 | */ | ||
24 | |||
25 | #ifndef REGEX_BLOCK_LIB_H_ | ||
26 | #define REGEX_BLOCK_LIB_H_ | ||
27 | |||
28 | #ifdef __cplusplus | ||
29 | extern "C" | ||
30 | { | ||
31 | #if 0 | ||
32 | /* keep Emacsens' auto-indent happy */ | ||
33 | } | ||
34 | #endif | ||
35 | #endif | ||
36 | |||
37 | #include "platform.h" | ||
38 | #include "block_regex.h" | ||
39 | |||
40 | |||
41 | /** | ||
42 | * Representation of a Regex node (and edges) in the DHT. | ||
43 | */ | ||
44 | struct RegexBlock; | ||
45 | |||
46 | |||
47 | /** | ||
48 | * Edge representation. | ||
49 | */ | ||
50 | struct REGEX_BLOCK_Edge | ||
51 | { | ||
52 | /** | ||
53 | * Label of the edge. FIXME: might want to not consume exactly | ||
54 | * multiples of 8 bits, need length! | ||
55 | */ | ||
56 | const char *label; | ||
57 | |||
58 | /** | ||
59 | * Destionation of the edge. | ||
60 | */ | ||
61 | struct GNUNET_HashCode destination; | ||
62 | }; | ||
63 | |||
64 | |||
65 | /** | ||
66 | * Check if the given 'proof' matches the given 'key'. | ||
67 | * | ||
68 | * @param proof partial regex of a state | ||
69 | * @param proof_len number of bytes in @a proof | ||
70 | * @param key hash of a state. | ||
71 | * @return #GNUNET_OK if the proof is valid for the given key. | ||
72 | */ | ||
73 | int | ||
74 | REGEX_BLOCK_check_proof (const char *proof, | ||
75 | size_t proof_len, | ||
76 | const struct GNUNET_HashCode *key); | ||
77 | |||
78 | |||
79 | /** | ||
80 | * Check if the regex block is well formed, including all edges. | ||
81 | * | ||
82 | * @param block The start of the block. | ||
83 | * @param size The size of the @a block. | ||
84 | * @param query the query for the @a block | ||
85 | * @param xquery String describing the edge we are looking for. | ||
86 | * Can be NULL in case this is a put block. | ||
87 | * @return #GNUNET_OK in case it's fine. | ||
88 | * #GNUNET_NO in case the xquery exists and is not found (IRRELEVANT). | ||
89 | * #GNUNET_SYSERR if the block is invalid. | ||
90 | */ | ||
91 | int | ||
92 | REGEX_BLOCK_check (const struct RegexBlock *block, | ||
93 | size_t size, | ||
94 | const struct GNUNET_HashCode *query, | ||
95 | const char *xquery); | ||
96 | |||
97 | |||
98 | /* FIXME: might want to use 'struct REGEX_BLOCK_Edge' here instead of 3 arguments! */ | ||
99 | |||
100 | /** | ||
101 | * Iterator over edges in a block. | ||
102 | * | ||
103 | * @param cls Closure. | ||
104 | * @param token Token that follows to next state. | ||
105 | * @param len Length of token. | ||
106 | * @param key Hash of next state. | ||
107 | * @return #GNUNET_YES if should keep iterating, #GNUNET_NO otherwise. | ||
108 | */ | ||
109 | typedef int | ||
110 | (*REGEX_INTERNAL_EgdeIterator)(void *cls, | ||
111 | const char *token, | ||
112 | size_t len, | ||
113 | const struct GNUNET_HashCode *key); | ||
114 | |||
115 | |||
116 | /** | ||
117 | * Iterate over all edges of a block of a regex state. | ||
118 | * | ||
119 | * @param block Block to iterate over. | ||
120 | * @param size Size of block. | ||
121 | * @param iterator Function to call on each edge in the block. | ||
122 | * @param iter_cls Closure for the @a iterator. | ||
123 | * @return #GNUNET_SYSERR if an error has been encountered. | ||
124 | * #GNUNET_OK if no error has been encountered. | ||
125 | * Note that if the iterator stops the iteration by returning | ||
126 | * #GNUNET_NO, the block will no longer be checked for further errors. | ||
127 | * The return value will be #GNUNET_OK meaning that no errors were | ||
128 | * found until the edge last notified to the iterator, but there might | ||
129 | * be errors in further edges. | ||
130 | */ | ||
131 | int | ||
132 | REGEX_BLOCK_iterate (const struct RegexBlock *block, | ||
133 | size_t size, | ||
134 | REGEX_INTERNAL_EgdeIterator iterator, | ||
135 | void *iter_cls); | ||
136 | |||
137 | |||
138 | /** | ||
139 | * Obtain the key that a particular block is to be stored under. | ||
140 | * | ||
141 | * @param block block to get the key from | ||
142 | * @param block_len number of bytes in @a block | ||
143 | * @param key where to store the key | ||
144 | * @return #GNUNET_OK on success, #GNUNET_SYSERR if the block is malformed | ||
145 | */ | ||
146 | int | ||
147 | REGEX_BLOCK_get_key (const struct RegexBlock *block, | ||
148 | size_t block_len, | ||
149 | struct GNUNET_HashCode *key); | ||
150 | |||
151 | |||
152 | /** | ||
153 | * Test if this block is marked as being an accept state. | ||
154 | * | ||
155 | * @param block block to test | ||
156 | * @param size number of bytes in block | ||
157 | * @return #GNUNET_YES if the block is accepting, #GNUNET_NO if not | ||
158 | */ | ||
159 | int | ||
160 | GNUNET_BLOCK_is_accepting (const struct RegexBlock *block, | ||
161 | size_t block_len); | ||
162 | |||
163 | |||
164 | /** | ||
165 | * Construct a regex block to be stored in the DHT. | ||
166 | * | ||
167 | * @param proof proof string for the block | ||
168 | * @param num_edges number of edges in the block | ||
169 | * @param edges the edges of the block | ||
170 | * @param accepting is this an accepting state | ||
171 | * @param rsize set to the size of the returned block (OUT-only) | ||
172 | * @return the regex block, NULL on error | ||
173 | */ | ||
174 | struct RegexBlock * | ||
175 | REGEX_BLOCK_create (const char *proof, | ||
176 | unsigned int num_edges, | ||
177 | const struct REGEX_BLOCK_Edge *edges, | ||
178 | int accepting, | ||
179 | size_t *rsize); | ||
180 | |||
181 | |||
182 | #if 0 /* keep Emacsens' auto-indent happy */ | ||
183 | { | ||
184 | #endif | ||
185 | #ifdef __cplusplus | ||
186 | } | ||
187 | #endif | ||
188 | |||
189 | /* ifndef REGEX_BLOCK_LIB_H */ | ||
190 | #endif | ||
191 | /* end of regex_block_lib.h */ | ||
diff --git a/src/regex/regex_internal.c b/src/regex/regex_internal.c new file mode 100644 index 000000000..944ca9bb9 --- /dev/null +++ b/src/regex/regex_internal.c | |||
@@ -0,0 +1,3706 @@ | |||
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 | /** | ||
19 | * @file src/regex/regex_internal.c | ||
20 | * @brief library to create Deterministic Finite Automatons (DFAs) from regular | ||
21 | * expressions (regexes). | ||
22 | * @author Maximilian Szengel | ||
23 | */ | ||
24 | #include "platform.h" | ||
25 | #include "gnunet_util_lib.h" | ||
26 | #include "gnunet_regex_service.h" | ||
27 | #include "regex_internal_lib.h" | ||
28 | #include "regex_internal.h" | ||
29 | |||
30 | |||
31 | /** | ||
32 | * Set this to #GNUNET_YES to enable state naming. Used to debug NFA->DFA | ||
33 | * creation. Disabled by default for better performance. | ||
34 | */ | ||
35 | #define REGEX_DEBUG_DFA GNUNET_NO | ||
36 | |||
37 | /** | ||
38 | * Set of states using MDLL API. | ||
39 | */ | ||
40 | struct REGEX_INTERNAL_StateSet_MDLL | ||
41 | { | ||
42 | /** | ||
43 | * MDLL of states. | ||
44 | */ | ||
45 | struct REGEX_INTERNAL_State *head; | ||
46 | |||
47 | /** | ||
48 | * MDLL of states. | ||
49 | */ | ||
50 | struct REGEX_INTERNAL_State *tail; | ||
51 | |||
52 | /** | ||
53 | * Length of the MDLL. | ||
54 | */ | ||
55 | unsigned int len; | ||
56 | }; | ||
57 | |||
58 | |||
59 | /** | ||
60 | * Append state to the given StateSet. | ||
61 | * | ||
62 | * @param set set to be modified | ||
63 | * @param state state to be appended | ||
64 | */ | ||
65 | static void | ||
66 | state_set_append (struct REGEX_INTERNAL_StateSet *set, | ||
67 | struct REGEX_INTERNAL_State *state) | ||
68 | { | ||
69 | if (set->off == set->size) | ||
70 | GNUNET_array_grow (set->states, set->size, set->size * 2 + 4); | ||
71 | set->states[set->off++] = state; | ||
72 | } | ||
73 | |||
74 | |||
75 | /** | ||
76 | * Compare two strings for equality. If either is NULL they are not equal. | ||
77 | * | ||
78 | * @param str1 first string for comparison. | ||
79 | * @param str2 second string for comparison. | ||
80 | * | ||
81 | * @return 0 if the strings are the same or both NULL, 1 or -1 if not. | ||
82 | */ | ||
83 | static int | ||
84 | nullstrcmp (const char *str1, const char *str2) | ||
85 | { | ||
86 | if ((NULL == str1) != (NULL == str2)) | ||
87 | return -1; | ||
88 | if ((NULL == str1) && (NULL == str2)) | ||
89 | return 0; | ||
90 | |||
91 | return strcmp (str1, str2); | ||
92 | } | ||
93 | |||
94 | |||
95 | /** | ||
96 | * Adds a transition from one state to another on @a label. Does not add | ||
97 | * duplicate states. | ||
98 | * | ||
99 | * @param ctx context | ||
100 | * @param from_state starting state for the transition | ||
101 | * @param label transition label | ||
102 | * @param to_state state to where the transition should point to | ||
103 | */ | ||
104 | static void | ||
105 | state_add_transition (struct REGEX_INTERNAL_Context *ctx, | ||
106 | struct REGEX_INTERNAL_State *from_state, | ||
107 | const char *label, | ||
108 | struct REGEX_INTERNAL_State *to_state) | ||
109 | { | ||
110 | struct REGEX_INTERNAL_Transition *t; | ||
111 | struct REGEX_INTERNAL_Transition *oth; | ||
112 | |||
113 | if (NULL == from_state) | ||
114 | { | ||
115 | GNUNET_log (GNUNET_ERROR_TYPE_ERROR, | ||
116 | "Could not create Transition.\n"); | ||
117 | return; | ||
118 | } | ||
119 | |||
120 | /* Do not add duplicate state transitions */ | ||
121 | for (t = from_state->transitions_head; NULL != t; t = t->next) | ||
122 | { | ||
123 | if (t->to_state == to_state && 0 == nullstrcmp (t->label, label) && | ||
124 | t->from_state == from_state) | ||
125 | return; | ||
126 | } | ||
127 | |||
128 | /* sort transitions by label */ | ||
129 | for (oth = from_state->transitions_head; NULL != oth; oth = oth->next) | ||
130 | { | ||
131 | if (0 < nullstrcmp (oth->label, label)) | ||
132 | break; | ||
133 | } | ||
134 | |||
135 | t = GNUNET_new (struct REGEX_INTERNAL_Transition); | ||
136 | if (NULL != ctx) | ||
137 | t->id = ctx->transition_id++; | ||
138 | if (NULL != label) | ||
139 | t->label = GNUNET_strdup (label); | ||
140 | else | ||
141 | t->label = NULL; | ||
142 | t->to_state = to_state; | ||
143 | t->from_state = from_state; | ||
144 | |||
145 | /* Add outgoing transition to 'from_state' */ | ||
146 | from_state->transition_count++; | ||
147 | GNUNET_CONTAINER_DLL_insert_before (from_state->transitions_head, | ||
148 | from_state->transitions_tail, oth, t); | ||
149 | } | ||
150 | |||
151 | |||
152 | /** | ||
153 | * Remove a 'transition' from 'state'. | ||
154 | * | ||
155 | * @param state state from which the to-be-removed transition originates. | ||
156 | * @param transition transition that should be removed from state 'state'. | ||
157 | */ | ||
158 | static void | ||
159 | state_remove_transition (struct REGEX_INTERNAL_State *state, | ||
160 | struct REGEX_INTERNAL_Transition *transition) | ||
161 | { | ||
162 | if (NULL == state || NULL == transition) | ||
163 | return; | ||
164 | |||
165 | if (transition->from_state != state) | ||
166 | return; | ||
167 | |||
168 | GNUNET_free_non_null (transition->label); | ||
169 | |||
170 | state->transition_count--; | ||
171 | GNUNET_CONTAINER_DLL_remove (state->transitions_head, state->transitions_tail, | ||
172 | transition); | ||
173 | |||
174 | GNUNET_free (transition); | ||
175 | } | ||
176 | |||
177 | |||
178 | /** | ||
179 | * Compare two states. Used for sorting. | ||
180 | * | ||
181 | * @param a first state | ||
182 | * @param b second state | ||
183 | * | ||
184 | * @return an integer less than, equal to, or greater than zero | ||
185 | * if the first argument is considered to be respectively | ||
186 | * less than, equal to, or greater than the second. | ||
187 | */ | ||
188 | static int | ||
189 | state_compare (const void *a, const void *b) | ||
190 | { | ||
191 | struct REGEX_INTERNAL_State **s1 = (struct REGEX_INTERNAL_State **) a; | ||
192 | struct REGEX_INTERNAL_State **s2 = (struct REGEX_INTERNAL_State **) b; | ||
193 | |||
194 | return (*s1)->id - (*s2)->id; | ||
195 | } | ||
196 | |||
197 | |||
198 | /** | ||
199 | * Get all edges leaving state @a s. | ||
200 | * | ||
201 | * @param s state. | ||
202 | * @param edges all edges leaving @a s, expected to be allocated and have enough | ||
203 | * space for `s->transitions_count` elements. | ||
204 | * | ||
205 | * @return number of edges. | ||
206 | */ | ||
207 | static unsigned int | ||
208 | state_get_edges (struct REGEX_INTERNAL_State *s, | ||
209 | struct REGEX_BLOCK_Edge *edges) | ||
210 | { | ||
211 | struct REGEX_INTERNAL_Transition *t; | ||
212 | unsigned int count; | ||
213 | |||
214 | if (NULL == s) | ||
215 | return 0; | ||
216 | |||
217 | count = 0; | ||
218 | |||
219 | for (t = s->transitions_head; NULL != t; t = t->next) | ||
220 | { | ||
221 | if (NULL != t->to_state) | ||
222 | { | ||
223 | edges[count].label = t->label; | ||
224 | edges[count].destination = t->to_state->hash; | ||
225 | count++; | ||
226 | } | ||
227 | } | ||
228 | return count; | ||
229 | } | ||
230 | |||
231 | |||
232 | /** | ||
233 | * Compare to state sets by comparing the id's of the states that are contained | ||
234 | * in each set. Both sets are expected to be sorted by id! | ||
235 | * | ||
236 | * @param sset1 first state set | ||
237 | * @param sset2 second state set | ||
238 | * @return 0 if the sets are equal, otherwise non-zero | ||
239 | */ | ||
240 | static int | ||
241 | state_set_compare (struct REGEX_INTERNAL_StateSet *sset1, | ||
242 | struct REGEX_INTERNAL_StateSet *sset2) | ||
243 | { | ||
244 | int result; | ||
245 | unsigned int i; | ||
246 | |||
247 | if (NULL == sset1 || NULL == sset2) | ||
248 | return 1; | ||
249 | |||
250 | result = sset1->off - sset2->off; | ||
251 | if (result < 0) | ||
252 | return -1; | ||
253 | if (result > 0) | ||
254 | return 1; | ||
255 | for (i = 0; i < sset1->off; i++) | ||
256 | if (0 != (result = state_compare (&sset1->states[i], &sset2->states[i]))) | ||
257 | break; | ||
258 | return result; | ||
259 | } | ||
260 | |||
261 | |||
262 | /** | ||
263 | * Clears the given StateSet 'set' | ||
264 | * | ||
265 | * @param set set to be cleared | ||
266 | */ | ||
267 | static void | ||
268 | state_set_clear (struct REGEX_INTERNAL_StateSet *set) | ||
269 | { | ||
270 | GNUNET_array_grow (set->states, set->size, 0); | ||
271 | set->off = 0; | ||
272 | } | ||
273 | |||
274 | |||
275 | /** | ||
276 | * Clears an automaton fragment. Does not destroy the states inside the | ||
277 | * automaton. | ||
278 | * | ||
279 | * @param a automaton to be cleared | ||
280 | */ | ||
281 | static void | ||
282 | automaton_fragment_clear (struct REGEX_INTERNAL_Automaton *a) | ||
283 | { | ||
284 | if (NULL == a) | ||
285 | return; | ||
286 | |||
287 | a->start = NULL; | ||
288 | a->end = NULL; | ||
289 | a->states_head = NULL; | ||
290 | a->states_tail = NULL; | ||
291 | a->state_count = 0; | ||
292 | GNUNET_free (a); | ||
293 | } | ||
294 | |||
295 | |||
296 | /** | ||
297 | * Frees the memory used by State @a s | ||
298 | * | ||
299 | * @param s state that should be destroyed | ||
300 | */ | ||
301 | static void | ||
302 | automaton_destroy_state (struct REGEX_INTERNAL_State *s) | ||
303 | { | ||
304 | struct REGEX_INTERNAL_Transition *t; | ||
305 | struct REGEX_INTERNAL_Transition *next_t; | ||
306 | |||
307 | if (NULL == s) | ||
308 | return; | ||
309 | |||
310 | GNUNET_free_non_null (s->name); | ||
311 | GNUNET_free_non_null (s->proof); | ||
312 | state_set_clear (&s->nfa_set); | ||
313 | for (t = s->transitions_head; NULL != t; t = next_t) | ||
314 | { | ||
315 | next_t = t->next; | ||
316 | state_remove_transition (s, t); | ||
317 | } | ||
318 | |||
319 | GNUNET_free (s); | ||
320 | } | ||
321 | |||
322 | |||
323 | /** | ||
324 | * Remove a state from the given automaton 'a'. Always use this function when | ||
325 | * altering the states of an automaton. Will also remove all transitions leading | ||
326 | * to this state, before destroying it. | ||
327 | * | ||
328 | * @param a automaton | ||
329 | * @param s state to remove | ||
330 | */ | ||
331 | static void | ||
332 | automaton_remove_state (struct REGEX_INTERNAL_Automaton *a, | ||
333 | struct REGEX_INTERNAL_State *s) | ||
334 | { | ||
335 | struct REGEX_INTERNAL_State *s_check; | ||
336 | struct REGEX_INTERNAL_Transition *t_check; | ||
337 | struct REGEX_INTERNAL_Transition *t_check_next; | ||
338 | |||
339 | if (NULL == a || NULL == s) | ||
340 | return; | ||
341 | |||
342 | /* remove all transitions leading to this state */ | ||
343 | for (s_check = a->states_head; NULL != s_check; s_check = s_check->next) | ||
344 | { | ||
345 | for (t_check = s_check->transitions_head; NULL != t_check; | ||
346 | t_check = t_check_next) | ||
347 | { | ||
348 | t_check_next = t_check->next; | ||
349 | if (t_check->to_state == s) | ||
350 | state_remove_transition (s_check, t_check); | ||
351 | } | ||
352 | } | ||
353 | |||
354 | /* remove state */ | ||
355 | GNUNET_CONTAINER_DLL_remove (a->states_head, a->states_tail, s); | ||
356 | a->state_count--; | ||
357 | |||
358 | automaton_destroy_state (s); | ||
359 | } | ||
360 | |||
361 | |||
362 | /** | ||
363 | * Merge two states into one. Will merge 's1' and 's2' into 's1' and destroy | ||
364 | * 's2'. 's1' will contain all (non-duplicate) outgoing transitions of 's2'. | ||
365 | * | ||
366 | * @param ctx context | ||
367 | * @param a automaton | ||
368 | * @param s1 first state | ||
369 | * @param s2 second state, will be destroyed | ||
370 | */ | ||
371 | static void | ||
372 | automaton_merge_states (struct REGEX_INTERNAL_Context *ctx, | ||
373 | struct REGEX_INTERNAL_Automaton *a, | ||
374 | struct REGEX_INTERNAL_State *s1, | ||
375 | struct REGEX_INTERNAL_State *s2) | ||
376 | { | ||
377 | struct REGEX_INTERNAL_State *s_check; | ||
378 | struct REGEX_INTERNAL_Transition *t_check; | ||
379 | struct REGEX_INTERNAL_Transition *t; | ||
380 | struct REGEX_INTERNAL_Transition *t_next; | ||
381 | int is_dup; | ||
382 | |||
383 | if (s1 == s2) | ||
384 | return; | ||
385 | |||
386 | /* 1. Make all transitions pointing to s2 point to s1, unless this transition | ||
387 | * does not already exists, if it already exists remove transition. */ | ||
388 | for (s_check = a->states_head; NULL != s_check; s_check = s_check->next) | ||
389 | { | ||
390 | for (t_check = s_check->transitions_head; NULL != t_check; t_check = t_next) | ||
391 | { | ||
392 | t_next = t_check->next; | ||
393 | |||
394 | if (s2 == t_check->to_state) | ||
395 | { | ||
396 | is_dup = GNUNET_NO; | ||
397 | for (t = t_check->from_state->transitions_head; NULL != t; t = t->next) | ||
398 | { | ||
399 | if (t->to_state == s1 && 0 == strcmp (t_check->label, t->label)) | ||
400 | is_dup = GNUNET_YES; | ||
401 | } | ||
402 | if (GNUNET_NO == is_dup) | ||
403 | t_check->to_state = s1; | ||
404 | else | ||
405 | state_remove_transition (t_check->from_state, t_check); | ||
406 | } | ||
407 | } | ||
408 | } | ||
409 | |||
410 | /* 2. Add all transitions from s2 to sX to s1 */ | ||
411 | for (t_check = s2->transitions_head; NULL != t_check; t_check = t_check->next) | ||
412 | { | ||
413 | if (t_check->to_state != s1) | ||
414 | state_add_transition (ctx, s1, t_check->label, t_check->to_state); | ||
415 | } | ||
416 | |||
417 | /* 3. Rename s1 to {s1,s2} */ | ||
418 | #if REGEX_DEBUG_DFA | ||
419 | char *new_name; | ||
420 | |||
421 | new_name = s1->name; | ||
422 | GNUNET_asprintf (&s1->name, "{%s,%s}", new_name, s2->name); | ||
423 | GNUNET_free (new_name); | ||
424 | #endif | ||
425 | |||
426 | /* remove state */ | ||
427 | GNUNET_CONTAINER_DLL_remove (a->states_head, a->states_tail, s2); | ||
428 | a->state_count--; | ||
429 | automaton_destroy_state (s2); | ||
430 | } | ||
431 | |||
432 | |||
433 | /** | ||
434 | * Add a state to the automaton 'a', always use this function to alter the | ||
435 | * states DLL of the automaton. | ||
436 | * | ||
437 | * @param a automaton to add the state to | ||
438 | * @param s state that should be added | ||
439 | */ | ||
440 | static void | ||
441 | automaton_add_state (struct REGEX_INTERNAL_Automaton *a, | ||
442 | struct REGEX_INTERNAL_State *s) | ||
443 | { | ||
444 | GNUNET_CONTAINER_DLL_insert (a->states_head, a->states_tail, s); | ||
445 | a->state_count++; | ||
446 | } | ||
447 | |||
448 | |||
449 | /** | ||
450 | * Depth-first traversal (DFS) of all states that are reachable from state | ||
451 | * 's'. Performs 'action' on each visited state. | ||
452 | * | ||
453 | * @param s start state. | ||
454 | * @param marks an array of size a->state_count to remember which state was | ||
455 | * already visited. | ||
456 | * @param count current count of the state. | ||
457 | * @param check function that is checked before advancing on each transition | ||
458 | * in the DFS. | ||
459 | * @param check_cls closure for check. | ||
460 | * @param action action to be performed on each state. | ||
461 | * @param action_cls closure for action. | ||
462 | */ | ||
463 | static void | ||
464 | automaton_state_traverse (struct REGEX_INTERNAL_State *s, int *marks, | ||
465 | unsigned int *count, | ||
466 | REGEX_INTERNAL_traverse_check check, void *check_cls, | ||
467 | REGEX_INTERNAL_traverse_action action, void *action_cls) | ||
468 | { | ||
469 | struct REGEX_INTERNAL_Transition *t; | ||
470 | |||
471 | if (GNUNET_YES == marks[s->traversal_id]) | ||
472 | return; | ||
473 | |||
474 | marks[s->traversal_id] = GNUNET_YES; | ||
475 | |||
476 | if (NULL != action) | ||
477 | action (action_cls, *count, s); | ||
478 | |||
479 | (*count)++; | ||
480 | |||
481 | for (t = s->transitions_head; NULL != t; t = t->next) | ||
482 | { | ||
483 | if (NULL == check || | ||
484 | (NULL != check && GNUNET_YES == check (check_cls, s, t))) | ||
485 | { | ||
486 | automaton_state_traverse (t->to_state, marks, count, check, check_cls, | ||
487 | action, action_cls); | ||
488 | } | ||
489 | } | ||
490 | } | ||
491 | |||
492 | |||
493 | /** | ||
494 | * Traverses the given automaton using depth-first-search (DFS) from it's start | ||
495 | * state, visiting all reachable states and calling 'action' on each one of | ||
496 | * them. | ||
497 | * | ||
498 | * @param a automaton to be traversed. | ||
499 | * @param start start state, pass a->start or NULL to traverse the whole automaton. | ||
500 | * @param check function that is checked before advancing on each transition | ||
501 | * in the DFS. | ||
502 | * @param check_cls closure for @a check. | ||
503 | * @param action action to be performed on each state. | ||
504 | * @param action_cls closure for @a action | ||
505 | */ | ||
506 | void | ||
507 | REGEX_INTERNAL_automaton_traverse (const struct REGEX_INTERNAL_Automaton *a, | ||
508 | struct REGEX_INTERNAL_State *start, | ||
509 | REGEX_INTERNAL_traverse_check check, | ||
510 | void *check_cls, | ||
511 | REGEX_INTERNAL_traverse_action action, | ||
512 | void *action_cls) | ||
513 | { | ||
514 | unsigned int count; | ||
515 | struct REGEX_INTERNAL_State *s; | ||
516 | |||
517 | if (NULL == a || 0 == a->state_count) | ||
518 | return; | ||
519 | |||
520 | int marks[a->state_count]; | ||
521 | |||
522 | for (count = 0, s = a->states_head; NULL != s && count < a->state_count; | ||
523 | s = s->next, count++) | ||
524 | { | ||
525 | s->traversal_id = count; | ||
526 | marks[s->traversal_id] = GNUNET_NO; | ||
527 | } | ||
528 | |||
529 | count = 0; | ||
530 | |||
531 | if (NULL == start) | ||
532 | s = a->start; | ||
533 | else | ||
534 | s = start; | ||
535 | |||
536 | automaton_state_traverse (s, marks, &count, | ||
537 | check, check_cls, | ||
538 | action, action_cls); | ||
539 | } | ||
540 | |||
541 | |||
542 | /** | ||
543 | * String container for faster string operations. | ||
544 | */ | ||
545 | struct StringBuffer | ||
546 | { | ||
547 | /** | ||
548 | * Buffer holding the string (may start in the middle!); | ||
549 | * NOT 0-terminated! | ||
550 | */ | ||
551 | char *sbuf; | ||
552 | |||
553 | /** | ||
554 | * Allocated buffer. | ||
555 | */ | ||
556 | char *abuf; | ||
557 | |||
558 | /** | ||
559 | * Length of the string in the buffer. | ||
560 | */ | ||
561 | size_t slen; | ||
562 | |||
563 | /** | ||
564 | * Number of bytes allocated for @e sbuf | ||
565 | */ | ||
566 | unsigned int blen; | ||
567 | |||
568 | /** | ||
569 | * Buffer currently represents "NULL" (not the empty string!) | ||
570 | */ | ||
571 | int16_t null_flag; | ||
572 | |||
573 | /** | ||
574 | * If this entry is part of the last/current generation array, | ||
575 | * this flag is #GNUNET_YES if the last and current generation are | ||
576 | * identical (and thus copying is unnecessary if the value didn't | ||
577 | * change). This is used in an optimization that improves | ||
578 | * performance by about 1% --- if we use int16_t here. With just | ||
579 | * "int" for both flags, performance drops (on my system) significantly, | ||
580 | * most likely due to increased cache misses. | ||
581 | */ | ||
582 | int16_t synced; | ||
583 | |||
584 | }; | ||
585 | |||
586 | |||
587 | /** | ||
588 | * Compare two strings for equality. If either is NULL they are not equal. | ||
589 | * | ||
590 | * @param s1 first string for comparison. | ||
591 | * @param s2 second string for comparison. | ||
592 | * | ||
593 | * @return 0 if the strings are the same or both NULL, 1 or -1 if not. | ||
594 | */ | ||
595 | static int | ||
596 | sb_nullstrcmp (const struct StringBuffer *s1, | ||
597 | const struct StringBuffer *s2) | ||
598 | { | ||
599 | if ( (GNUNET_YES == s1->null_flag) && | ||
600 | (GNUNET_YES == s2->null_flag) ) | ||
601 | return 0; | ||
602 | if ( (GNUNET_YES == s1->null_flag) || | ||
603 | (GNUNET_YES == s2->null_flag) ) | ||
604 | return -1; | ||
605 | if (s1->slen != s2->slen) | ||
606 | return -1; | ||
607 | if (0 == s1->slen) | ||
608 | return 0; | ||
609 | return memcmp (s1->sbuf, s2->sbuf, s1->slen); | ||
610 | } | ||
611 | |||
612 | |||
613 | /** | ||
614 | * Compare two strings for equality. | ||
615 | * | ||
616 | * @param s1 first string for comparison. | ||
617 | * @param s2 second string for comparison. | ||
618 | * | ||
619 | * @return 0 if the strings are the same, 1 or -1 if not. | ||
620 | */ | ||
621 | static int | ||
622 | sb_strcmp (const struct StringBuffer *s1, | ||
623 | const struct StringBuffer *s2) | ||
624 | { | ||
625 | if (s1->slen != s2->slen) | ||
626 | return -1; | ||
627 | if (0 == s1->slen) | ||
628 | return 0; | ||
629 | return memcmp (s1->sbuf, s2->sbuf, s1->slen); | ||
630 | } | ||
631 | |||
632 | |||
633 | /** | ||
634 | * Reallocate the buffer of 'ret' to fit 'nlen' characters; | ||
635 | * move the existing string to the beginning of the new buffer. | ||
636 | * | ||
637 | * @param ret current buffer, to be updated | ||
638 | * @param nlen target length for the buffer, must be at least ret->slen | ||
639 | */ | ||
640 | static void | ||
641 | sb_realloc (struct StringBuffer *ret, | ||
642 | size_t nlen) | ||
643 | { | ||
644 | char *old; | ||
645 | |||
646 | GNUNET_assert (nlen >= ret->slen); | ||
647 | old = ret->abuf; | ||
648 | ret->abuf = GNUNET_malloc (nlen); | ||
649 | ret->blen = nlen; | ||
650 | GNUNET_memcpy (ret->abuf, | ||
651 | ret->sbuf, | ||
652 | ret->slen); | ||
653 | ret->sbuf = ret->abuf; | ||
654 | GNUNET_free_non_null (old); | ||
655 | } | ||
656 | |||
657 | |||
658 | /** | ||
659 | * Append a string. | ||
660 | * | ||
661 | * @param ret where to write the result | ||
662 | * @param sarg string to append | ||
663 | */ | ||
664 | static void | ||
665 | sb_append (struct StringBuffer *ret, | ||
666 | const struct StringBuffer *sarg) | ||
667 | { | ||
668 | if (GNUNET_YES == ret->null_flag) | ||
669 | ret->slen = 0; | ||
670 | ret->null_flag = GNUNET_NO; | ||
671 | if (ret->blen < sarg->slen + ret->slen) | ||
672 | sb_realloc (ret, ret->blen + sarg->slen + 128); | ||
673 | GNUNET_memcpy (&ret->sbuf[ret->slen], | ||
674 | sarg->sbuf, | ||
675 | sarg->slen); | ||
676 | ret->slen += sarg->slen; | ||
677 | } | ||
678 | |||
679 | |||
680 | /** | ||
681 | * Append a C string. | ||
682 | * | ||
683 | * @param ret where to write the result | ||
684 | * @param cstr string to append | ||
685 | */ | ||
686 | static void | ||
687 | sb_append_cstr (struct StringBuffer *ret, | ||
688 | const char *cstr) | ||
689 | { | ||
690 | size_t cstr_len = strlen (cstr); | ||
691 | |||
692 | if (GNUNET_YES == ret->null_flag) | ||
693 | ret->slen = 0; | ||
694 | ret->null_flag = GNUNET_NO; | ||
695 | if (ret->blen < cstr_len + ret->slen) | ||
696 | sb_realloc (ret, ret->blen + cstr_len + 128); | ||
697 | GNUNET_memcpy (&ret->sbuf[ret->slen], | ||
698 | cstr, | ||
699 | cstr_len); | ||
700 | ret->slen += cstr_len; | ||
701 | } | ||
702 | |||
703 | |||
704 | /** | ||
705 | * Wrap a string buffer, that is, set ret to the format string | ||
706 | * which contains an "%s" which is to be replaced with the original | ||
707 | * content of 'ret'. Note that optimizing this function is not | ||
708 | * really worth it, it is rarely called. | ||
709 | * | ||
710 | * @param ret where to write the result and take the input for %.*s from | ||
711 | * @param format format string, fprintf-style, with exactly one "%.*s" | ||
712 | * @param extra_chars how long will the result be, in addition to 'sarg' length | ||
713 | */ | ||
714 | static void | ||
715 | sb_wrap (struct StringBuffer *ret, | ||
716 | const char *format, | ||
717 | size_t extra_chars) | ||
718 | { | ||
719 | char *temp; | ||
720 | |||
721 | if (GNUNET_YES == ret->null_flag) | ||
722 | ret->slen = 0; | ||
723 | ret->null_flag = GNUNET_NO; | ||
724 | temp = GNUNET_malloc (ret->slen + extra_chars + 1); | ||
725 | GNUNET_snprintf (temp, | ||
726 | ret->slen + extra_chars + 1, | ||
727 | format, | ||
728 | (int) ret->slen, | ||
729 | ret->sbuf); | ||
730 | GNUNET_free_non_null (ret->abuf); | ||
731 | ret->abuf = temp; | ||
732 | ret->sbuf = temp; | ||
733 | ret->blen = ret->slen + extra_chars + 1; | ||
734 | ret->slen = ret->slen + extra_chars; | ||
735 | } | ||
736 | |||
737 | |||
738 | /** | ||
739 | * Format a string buffer. Note that optimizing this function is not | ||
740 | * really worth it, it is rarely called. | ||
741 | * | ||
742 | * @param ret where to write the result | ||
743 | * @param format format string, fprintf-style, with exactly one "%.*s" | ||
744 | * @param extra_chars how long will the result be, in addition to 'sarg' length | ||
745 | * @param sarg string to print into the format | ||
746 | */ | ||
747 | static void | ||
748 | sb_printf1 (struct StringBuffer *ret, | ||
749 | const char *format, | ||
750 | size_t extra_chars, | ||
751 | const struct StringBuffer *sarg) | ||
752 | { | ||
753 | if (ret->blen < sarg->slen + extra_chars + 1) | ||
754 | sb_realloc (ret, | ||
755 | sarg->slen + extra_chars + 1); | ||
756 | ret->null_flag = GNUNET_NO; | ||
757 | ret->sbuf = ret->abuf; | ||
758 | ret->slen = sarg->slen + extra_chars; | ||
759 | GNUNET_snprintf (ret->sbuf, | ||
760 | ret->blen, | ||
761 | format, | ||
762 | (int) sarg->slen, | ||
763 | sarg->sbuf); | ||
764 | } | ||
765 | |||
766 | |||
767 | /** | ||
768 | * Format a string buffer. | ||
769 | * | ||
770 | * @param ret where to write the result | ||
771 | * @param format format string, fprintf-style, with exactly two "%.*s" | ||
772 | * @param extra_chars how long will the result be, in addition to 'sarg1/2' length | ||
773 | * @param sarg1 first string to print into the format | ||
774 | * @param sarg2 second string to print into the format | ||
775 | */ | ||
776 | static void | ||
777 | sb_printf2 (struct StringBuffer *ret, | ||
778 | const char *format, | ||
779 | size_t extra_chars, | ||
780 | const struct StringBuffer *sarg1, | ||
781 | const struct StringBuffer *sarg2) | ||
782 | { | ||
783 | if (ret->blen < sarg1->slen + sarg2->slen + extra_chars + 1) | ||
784 | sb_realloc (ret, | ||
785 | sarg1->slen + sarg2->slen + extra_chars + 1); | ||
786 | ret->null_flag = GNUNET_NO; | ||
787 | ret->slen = sarg1->slen + sarg2->slen + extra_chars; | ||
788 | ret->sbuf = ret->abuf; | ||
789 | GNUNET_snprintf (ret->sbuf, | ||
790 | ret->blen, | ||
791 | format, | ||
792 | (int) sarg1->slen, | ||
793 | sarg1->sbuf, | ||
794 | (int) sarg2->slen, | ||
795 | sarg2->sbuf); | ||
796 | } | ||
797 | |||
798 | |||
799 | /** | ||
800 | * Format a string buffer. Note that optimizing this function is not | ||
801 | * really worth it, it is rarely called. | ||
802 | * | ||
803 | * @param ret where to write the result | ||
804 | * @param format format string, fprintf-style, with exactly three "%.*s" | ||
805 | * @param extra_chars how long will the result be, in addition to 'sarg1/2/3' length | ||
806 | * @param sarg1 first string to print into the format | ||
807 | * @param sarg2 second string to print into the format | ||
808 | * @param sarg3 third string to print into the format | ||
809 | */ | ||
810 | static void | ||
811 | sb_printf3 (struct StringBuffer *ret, | ||
812 | const char *format, | ||
813 | size_t extra_chars, | ||
814 | const struct StringBuffer *sarg1, | ||
815 | const struct StringBuffer *sarg2, | ||
816 | const struct StringBuffer *sarg3) | ||
817 | { | ||
818 | if (ret->blen < sarg1->slen + sarg2->slen + sarg3->slen + extra_chars + 1) | ||
819 | sb_realloc (ret, | ||
820 | sarg1->slen + sarg2->slen + sarg3->slen + extra_chars + 1); | ||
821 | ret->null_flag = GNUNET_NO; | ||
822 | ret->slen = sarg1->slen + sarg2->slen + sarg3->slen + extra_chars; | ||
823 | ret->sbuf = ret->abuf; | ||
824 | GNUNET_snprintf (ret->sbuf, | ||
825 | ret->blen, | ||
826 | format, | ||
827 | (int) sarg1->slen, | ||
828 | sarg1->sbuf, | ||
829 | (int) sarg2->slen, | ||
830 | sarg2->sbuf, | ||
831 | (int) sarg3->slen, | ||
832 | sarg3->sbuf); | ||
833 | } | ||
834 | |||
835 | |||
836 | /** | ||
837 | * Free resources of the given string buffer. | ||
838 | * | ||
839 | * @param sb buffer to free (actual pointer is not freed, as they | ||
840 | * should not be individually allocated) | ||
841 | */ | ||
842 | static void | ||
843 | sb_free (struct StringBuffer *sb) | ||
844 | { | ||
845 | GNUNET_array_grow (sb->abuf, | ||
846 | sb->blen, | ||
847 | 0); | ||
848 | sb->slen = 0; | ||
849 | sb->sbuf = NULL; | ||
850 | sb->null_flag= GNUNET_YES; | ||
851 | } | ||
852 | |||
853 | |||
854 | /** | ||
855 | * Copy the given string buffer from 'in' to 'out'. | ||
856 | * | ||
857 | * @param in input string | ||
858 | * @param out output string | ||
859 | */ | ||
860 | static void | ||
861 | sb_strdup (struct StringBuffer *out, | ||
862 | const struct StringBuffer *in) | ||
863 | |||
864 | { | ||
865 | out->null_flag = in->null_flag; | ||
866 | if (GNUNET_YES == out->null_flag) | ||
867 | return; | ||
868 | if (out->blen < in->slen) | ||
869 | { | ||
870 | GNUNET_array_grow (out->abuf, | ||
871 | out->blen, | ||
872 | in->slen); | ||
873 | } | ||
874 | out->sbuf = out->abuf; | ||
875 | out->slen = in->slen; | ||
876 | GNUNET_memcpy (out->sbuf, in->sbuf, out->slen); | ||
877 | } | ||
878 | |||
879 | |||
880 | /** | ||
881 | * Copy the given string buffer from 'in' to 'out'. | ||
882 | * | ||
883 | * @param cstr input string | ||
884 | * @param out output string | ||
885 | */ | ||
886 | static void | ||
887 | sb_strdup_cstr (struct StringBuffer *out, | ||
888 | const char *cstr) | ||
889 | { | ||
890 | if (NULL == cstr) | ||
891 | { | ||
892 | out->null_flag = GNUNET_YES; | ||
893 | return; | ||
894 | } | ||
895 | out->null_flag = GNUNET_NO; | ||
896 | out->slen = strlen (cstr); | ||
897 | if (out->blen < out->slen) | ||
898 | { | ||
899 | GNUNET_array_grow (out->abuf, | ||
900 | out->blen, | ||
901 | out->slen); | ||
902 | } | ||
903 | out->sbuf = out->abuf; | ||
904 | GNUNET_memcpy (out->sbuf, cstr, out->slen); | ||
905 | } | ||
906 | |||
907 | |||
908 | /** | ||
909 | * Check if the given string @a str needs parentheses around it when | ||
910 | * using it to generate a regex. | ||
911 | * | ||
912 | * @param str string | ||
913 | * | ||
914 | * @return #GNUNET_YES if parentheses are needed, #GNUNET_NO otherwise | ||
915 | */ | ||
916 | static int | ||
917 | needs_parentheses (const struct StringBuffer *str) | ||
918 | { | ||
919 | size_t slen; | ||
920 | const char *op; | ||
921 | const char *cl; | ||
922 | const char *pos; | ||
923 | const char *end; | ||
924 | unsigned int cnt; | ||
925 | |||
926 | if ((GNUNET_YES == str->null_flag) || ((slen = str->slen) < 2)) | ||
927 | return GNUNET_NO; | ||
928 | pos = str->sbuf; | ||
929 | if ('(' != pos[0]) | ||
930 | return GNUNET_YES; | ||
931 | end = str->sbuf + slen; | ||
932 | cnt = 1; | ||
933 | pos++; | ||
934 | while (cnt > 0) | ||
935 | { | ||
936 | cl = memchr (pos, ')', end - pos); | ||
937 | if (NULL == cl) | ||
938 | { | ||
939 | GNUNET_break (0); | ||
940 | return GNUNET_YES; | ||
941 | } | ||
942 | /* while '(' before ')', count opening parens */ | ||
943 | while ( (NULL != (op = memchr (pos, '(', end - pos))) && | ||
944 | (op < cl) ) | ||
945 | { | ||
946 | cnt++; | ||
947 | pos = op + 1; | ||
948 | } | ||
949 | /* got ')' first */ | ||
950 | cnt--; | ||
951 | pos = cl + 1; | ||
952 | } | ||
953 | return (*pos == '\0') ? GNUNET_NO : GNUNET_YES; | ||
954 | } | ||
955 | |||
956 | |||
957 | /** | ||
958 | * Remove parentheses surrounding string @a str. | ||
959 | * Example: "(a)" becomes "a", "(a|b)|(a|c)" stays the same. | ||
960 | * You need to #GNUNET_free() the returned string. | ||
961 | * | ||
962 | * @param str string, modified to contain a | ||
963 | * @return string without surrounding parentheses, string 'str' if no preceding | ||
964 | * epsilon could be found, NULL if 'str' was NULL | ||
965 | */ | ||
966 | static void | ||
967 | remove_parentheses (struct StringBuffer *str) | ||
968 | { | ||
969 | size_t slen; | ||
970 | const char *pos; | ||
971 | const char *end; | ||
972 | const char *sbuf; | ||
973 | const char *op; | ||
974 | const char *cp; | ||
975 | unsigned int cnt; | ||
976 | |||
977 | if (0) | ||
978 | return; | ||
979 | sbuf = str->sbuf; | ||
980 | if ( (GNUNET_YES == str->null_flag) || | ||
981 | (1 >= (slen = str->slen)) || | ||
982 | ('(' != str->sbuf[0]) || | ||
983 | (')' != str->sbuf[slen - 1]) ) | ||
984 | return; | ||
985 | cnt = 0; | ||
986 | pos = &sbuf[1]; | ||
987 | end = &sbuf[slen - 1]; | ||
988 | op = memchr (pos, '(', end - pos); | ||
989 | cp = memchr (pos, ')', end - pos); | ||
990 | while (NULL != cp) | ||
991 | { | ||
992 | while ( (NULL != op) && | ||
993 | (op < cp) ) | ||
994 | { | ||
995 | cnt++; | ||
996 | pos = op + 1; | ||
997 | op = memchr (pos, '(', end - pos); | ||
998 | } | ||
999 | while ( (NULL != cp) && | ||
1000 | ( (NULL == op) || | ||
1001 | (cp < op) ) ) | ||
1002 | { | ||
1003 | if (0 == cnt) | ||
1004 | return; /* can't strip parens */ | ||
1005 | cnt--; | ||
1006 | pos = cp + 1; | ||
1007 | cp = memchr (pos, ')', end - pos); | ||
1008 | } | ||
1009 | } | ||
1010 | if (0 != cnt) | ||
1011 | { | ||
1012 | GNUNET_break (0); | ||
1013 | return; | ||
1014 | } | ||
1015 | str->sbuf++; | ||
1016 | str->slen -= 2; | ||
1017 | } | ||
1018 | |||
1019 | |||
1020 | /** | ||
1021 | * Check if the string 'str' starts with an epsilon (empty string). | ||
1022 | * Example: "(|a)" is starting with an epsilon. | ||
1023 | * | ||
1024 | * @param str string to test | ||
1025 | * | ||
1026 | * @return 0 if str has no epsilon, 1 if str starts with '(|' and ends with ')' | ||
1027 | */ | ||
1028 | static int | ||
1029 | has_epsilon (const struct StringBuffer *str) | ||
1030 | { | ||
1031 | return | ||
1032 | (GNUNET_YES != str->null_flag) && | ||
1033 | (0 < str->slen) && | ||
1034 | ('(' == str->sbuf[0]) && | ||
1035 | ('|' == str->sbuf[1]) && | ||
1036 | (')' == str->sbuf[str->slen - 1]); | ||
1037 | } | ||
1038 | |||
1039 | |||
1040 | /** | ||
1041 | * Remove an epsilon from the string str. Where epsilon is an empty string | ||
1042 | * Example: str = "(|a|b|c)", result: "a|b|c" | ||
1043 | * The returned string needs to be freed. | ||
1044 | * | ||
1045 | * @param str original string | ||
1046 | * @param ret where to return string without preceding epsilon, string 'str' if no preceding | ||
1047 | * epsilon could be found, NULL if 'str' was NULL | ||
1048 | */ | ||
1049 | static void | ||
1050 | remove_epsilon (const struct StringBuffer *str, | ||
1051 | struct StringBuffer *ret) | ||
1052 | { | ||
1053 | if (GNUNET_YES == str->null_flag) | ||
1054 | { | ||
1055 | ret->null_flag = GNUNET_YES; | ||
1056 | return; | ||
1057 | } | ||
1058 | if ( (str->slen > 1) && | ||
1059 | ('(' == str->sbuf[0]) && | ||
1060 | ('|' == str->sbuf[1]) && | ||
1061 | (')' == str->sbuf[str->slen - 1]) ) | ||
1062 | { | ||
1063 | /* remove epsilon */ | ||
1064 | if (ret->blen < str->slen - 3) | ||
1065 | { | ||
1066 | GNUNET_array_grow (ret->abuf, | ||
1067 | ret->blen, | ||
1068 | str->slen - 3); | ||
1069 | } | ||
1070 | ret->sbuf = ret->abuf; | ||
1071 | ret->slen = str->slen - 3; | ||
1072 | GNUNET_memcpy (ret->sbuf, &str->sbuf[2], ret->slen); | ||
1073 | return; | ||
1074 | } | ||
1075 | sb_strdup (ret, str); | ||
1076 | } | ||
1077 | |||
1078 | |||
1079 | /** | ||
1080 | * Compare n bytes of 'str1' and 'str2' | ||
1081 | * | ||
1082 | * @param str1 first string to compare | ||
1083 | * @param str2 second string for comparison | ||
1084 | * @param n number of bytes to compare | ||
1085 | * | ||
1086 | * @return -1 if any of the strings is NULL, 0 if equal, non 0 otherwise | ||
1087 | */ | ||
1088 | static int | ||
1089 | sb_strncmp (const struct StringBuffer *str1, | ||
1090 | const struct StringBuffer *str2, size_t n) | ||
1091 | { | ||
1092 | size_t max; | ||
1093 | |||
1094 | if ( (str1->slen != str2->slen) && | ||
1095 | ( (str1->slen < n) || | ||
1096 | (str2->slen < n) ) ) | ||
1097 | return -1; | ||
1098 | max = GNUNET_MAX (str1->slen, str2->slen); | ||
1099 | if (max > n) | ||
1100 | max = n; | ||
1101 | return memcmp (str1->sbuf, str2->sbuf, max); | ||
1102 | } | ||
1103 | |||
1104 | |||
1105 | /** | ||
1106 | * Compare n bytes of 'str1' and 'str2' | ||
1107 | * | ||
1108 | * @param str1 first string to compare | ||
1109 | * @param str2 second C string for comparison | ||
1110 | * @param n number of bytes to compare (and length of str2) | ||
1111 | * | ||
1112 | * @return -1 if any of the strings is NULL, 0 if equal, non 0 otherwise | ||
1113 | */ | ||
1114 | static int | ||
1115 | sb_strncmp_cstr (const struct StringBuffer *str1, | ||
1116 | const char *str2, size_t n) | ||
1117 | { | ||
1118 | if (str1->slen < n) | ||
1119 | return -1; | ||
1120 | return memcmp (str1->sbuf, str2, n); | ||
1121 | } | ||
1122 | |||
1123 | |||
1124 | /** | ||
1125 | * Initialize string buffer for storing strings of up to n | ||
1126 | * characters. | ||
1127 | * | ||
1128 | * @param sb buffer to initialize | ||
1129 | * @param n desired target length | ||
1130 | */ | ||
1131 | static void | ||
1132 | sb_init (struct StringBuffer *sb, | ||
1133 | size_t n) | ||
1134 | { | ||
1135 | sb->null_flag = GNUNET_NO; | ||
1136 | sb->abuf = sb->sbuf = (0 == n) ? NULL : GNUNET_malloc (n); | ||
1137 | sb->blen = n; | ||
1138 | sb->slen = 0; | ||
1139 | } | ||
1140 | |||
1141 | |||
1142 | /** | ||
1143 | * Compare 'str1', starting from position 'k', with whole 'str2' | ||
1144 | * | ||
1145 | * @param str1 first string to compare, starting from position 'k' | ||
1146 | * @param str2 second string for comparison | ||
1147 | * @param k starting position in 'str1' | ||
1148 | * | ||
1149 | * @return -1 if any of the strings is NULL, 0 if equal, non 0 otherwise | ||
1150 | */ | ||
1151 | static int | ||
1152 | sb_strkcmp (const struct StringBuffer *str1, | ||
1153 | const struct StringBuffer *str2, size_t k) | ||
1154 | { | ||
1155 | if ( (GNUNET_YES == str1->null_flag) || | ||
1156 | (GNUNET_YES == str2->null_flag) || | ||
1157 | (k > str1->slen) || | ||
1158 | (str1->slen - k != str2->slen) ) | ||
1159 | return -1; | ||
1160 | return memcmp (&str1->sbuf[k], str2->sbuf, str2->slen); | ||
1161 | } | ||
1162 | |||
1163 | |||
1164 | /** | ||
1165 | * Helper function used as 'action' in 'REGEX_INTERNAL_automaton_traverse' | ||
1166 | * function to create the depth-first numbering of the states. | ||
1167 | * | ||
1168 | * @param cls states array. | ||
1169 | * @param count current state counter. | ||
1170 | * @param s current state. | ||
1171 | */ | ||
1172 | static void | ||
1173 | number_states (void *cls, const unsigned int count, | ||
1174 | struct REGEX_INTERNAL_State *s) | ||
1175 | { | ||
1176 | struct REGEX_INTERNAL_State **states = cls; | ||
1177 | |||
1178 | s->dfs_id = count; | ||
1179 | if (NULL != states) | ||
1180 | states[count] = s; | ||
1181 | } | ||
1182 | |||
1183 | |||
1184 | |||
1185 | #define PRIS(a) \ | ||
1186 | ((GNUNET_YES == a.null_flag) ? 6 : (int) a.slen), \ | ||
1187 | ((GNUNET_YES == a.null_flag) ? "(null)" : a.sbuf) | ||
1188 | |||
1189 | |||
1190 | /** | ||
1191 | * Construct the regular expression given the inductive step, | ||
1192 | * $R^{(k)}_{ij} = R^{(k-1)}_{ij} | R^{(k-1)}_{ik} ( R^{(k-1)}_{kk} )^* | ||
1193 | * R^{(k-1)}_{kj}, and simplify the resulting expression saved in R_cur_ij. | ||
1194 | * | ||
1195 | * @param R_last_ij value of $R^{(k-1)_{ij}. | ||
1196 | * @param R_last_ik value of $R^{(k-1)_{ik}. | ||
1197 | * @param R_last_kk value of $R^{(k-1)_{kk}. | ||
1198 | * @param R_last_kj value of $R^{(k-1)_{kj}. | ||
1199 | * @param R_cur_ij result for this inductive step is saved in R_cur_ij, R_cur_ij | ||
1200 | * is expected to be NULL when called! | ||
1201 | * @param R_cur_l optimization -- kept between iterations to avoid realloc | ||
1202 | * @param R_cur_r optimization -- kept between iterations to avoid realloc | ||
1203 | */ | ||
1204 | static void | ||
1205 | automaton_create_proofs_simplify (const struct StringBuffer *R_last_ij, | ||
1206 | const struct StringBuffer *R_last_ik, | ||
1207 | const struct StringBuffer *R_last_kk, | ||
1208 | const struct StringBuffer *R_last_kj, | ||
1209 | struct StringBuffer *R_cur_ij, | ||
1210 | struct StringBuffer *R_cur_l, | ||
1211 | struct StringBuffer *R_cur_r) | ||
1212 | { | ||
1213 | struct StringBuffer R_temp_ij; | ||
1214 | struct StringBuffer R_temp_ik; | ||
1215 | struct StringBuffer R_temp_kj; | ||
1216 | struct StringBuffer R_temp_kk; | ||
1217 | int eps_check; | ||
1218 | int ij_ik_cmp; | ||
1219 | int ij_kj_cmp; | ||
1220 | int ik_kk_cmp; | ||
1221 | int kk_kj_cmp; | ||
1222 | int clean_ik_kk_cmp; | ||
1223 | int clean_kk_kj_cmp; | ||
1224 | size_t length; | ||
1225 | size_t length_l; | ||
1226 | size_t length_r; | ||
1227 | |||
1228 | /* | ||
1229 | * $R^{(k)}_{ij} = R^{(k-1)}_{ij} | R^{(k-1)}_{ik} ( R^{(k-1)}_{kk} )^* R^{(k-1)}_{kj} | ||
1230 | * R_last == R^{(k-1)}, R_cur == R^{(k)} | ||
1231 | * R_cur_ij = R_cur_l | R_cur_r | ||
1232 | * R_cur_l == R^{(k-1)}_{ij} | ||
1233 | * R_cur_r == R^{(k-1)}_{ik} ( R^{(k-1)}_{kk} )^* R^{(k-1)}_{kj} | ||
1234 | */ | ||
1235 | |||
1236 | if ( (GNUNET_YES == R_last_ij->null_flag) && | ||
1237 | ( (GNUNET_YES == R_last_ik->null_flag) || | ||
1238 | (GNUNET_YES == R_last_kj->null_flag))) | ||
1239 | { | ||
1240 | /* R^{(k)}_{ij} = N | N */ | ||
1241 | R_cur_ij->null_flag = GNUNET_YES; | ||
1242 | R_cur_ij->synced = GNUNET_NO; | ||
1243 | return; | ||
1244 | } | ||
1245 | |||
1246 | if ( (GNUNET_YES == R_last_ik->null_flag) || | ||
1247 | (GNUNET_YES == R_last_kj->null_flag) ) | ||
1248 | { | ||
1249 | /* R^{(k)}_{ij} = R^{(k-1)}_{ij} | N */ | ||
1250 | if (GNUNET_YES == R_last_ij->synced) | ||
1251 | { | ||
1252 | R_cur_ij->synced = GNUNET_YES; | ||
1253 | R_cur_ij->null_flag = GNUNET_NO; | ||
1254 | return; | ||
1255 | } | ||
1256 | R_cur_ij->synced = GNUNET_YES; | ||
1257 | sb_strdup (R_cur_ij, R_last_ij); | ||
1258 | return; | ||
1259 | } | ||
1260 | R_cur_ij->synced = GNUNET_NO; | ||
1261 | |||
1262 | /* $R^{(k)}_{ij} = N | R^{(k-1)}_{ik} ( R^{(k-1)}_{kk} )^* R^{(k-1)}_{kj} OR | ||
1263 | * $R^{(k)}_{ij} = R^{(k-1)}_{ij} | R^{(k-1)}_{ik} ( R^{(k-1)}_{kk} )^* R^{(k-1)}_{kj} */ | ||
1264 | |||
1265 | R_cur_r->null_flag = GNUNET_YES; | ||
1266 | R_cur_r->slen = 0; | ||
1267 | R_cur_l->null_flag = GNUNET_YES; | ||
1268 | R_cur_l->slen = 0; | ||
1269 | |||
1270 | /* cache results from strcmp, we might need these many times */ | ||
1271 | ij_kj_cmp = sb_nullstrcmp (R_last_ij, R_last_kj); | ||
1272 | ij_ik_cmp = sb_nullstrcmp (R_last_ij, R_last_ik); | ||
1273 | ik_kk_cmp = sb_nullstrcmp (R_last_ik, R_last_kk); | ||
1274 | kk_kj_cmp = sb_nullstrcmp (R_last_kk, R_last_kj); | ||
1275 | |||
1276 | /* Assign R_temp_(ik|kk|kj) to R_last[][] and remove epsilon as well | ||
1277 | * as parentheses, so we can better compare the contents */ | ||
1278 | |||
1279 | memset (&R_temp_ij, 0, sizeof (struct StringBuffer)); | ||
1280 | memset (&R_temp_ik, 0, sizeof (struct StringBuffer)); | ||
1281 | memset (&R_temp_kk, 0, sizeof (struct StringBuffer)); | ||
1282 | memset (&R_temp_kj, 0, sizeof (struct StringBuffer)); | ||
1283 | remove_epsilon (R_last_ik, &R_temp_ik); | ||
1284 | remove_epsilon (R_last_kk, &R_temp_kk); | ||
1285 | remove_epsilon (R_last_kj, &R_temp_kj); | ||
1286 | remove_parentheses (&R_temp_ik); | ||
1287 | remove_parentheses (&R_temp_kk); | ||
1288 | remove_parentheses (&R_temp_kj); | ||
1289 | clean_ik_kk_cmp = sb_nullstrcmp (R_last_ik, &R_temp_kk); | ||
1290 | clean_kk_kj_cmp = sb_nullstrcmp (&R_temp_kk, R_last_kj); | ||
1291 | |||
1292 | /* construct R_cur_l (and, if necessary R_cur_r) */ | ||
1293 | if (GNUNET_YES != R_last_ij->null_flag) | ||
1294 | { | ||
1295 | /* Assign R_temp_ij to R_last_ij and remove epsilon as well | ||
1296 | * as parentheses, so we can better compare the contents */ | ||
1297 | remove_epsilon (R_last_ij, &R_temp_ij); | ||
1298 | remove_parentheses (&R_temp_ij); | ||
1299 | |||
1300 | if ( (0 == sb_strcmp (&R_temp_ij, &R_temp_ik)) && | ||
1301 | (0 == sb_strcmp (&R_temp_ik, &R_temp_kk)) && | ||
1302 | (0 == sb_strcmp (&R_temp_kk, &R_temp_kj)) ) | ||
1303 | { | ||
1304 | if (0 == R_temp_ij.slen) | ||
1305 | { | ||
1306 | R_cur_r->null_flag = GNUNET_NO; | ||
1307 | } | ||
1308 | else if ((0 == sb_strncmp_cstr (R_last_ij, "(|", 2)) || | ||
1309 | (0 == sb_strncmp_cstr (R_last_ik, "(|", 2) && | ||
1310 | 0 == sb_strncmp_cstr (R_last_kj, "(|", 2))) | ||
1311 | { | ||
1312 | /* | ||
1313 | * a|(e|a)a*(e|a) = a* | ||
1314 | * a|(e|a)(e|a)*(e|a) = a* | ||
1315 | * (e|a)|aa*a = a* | ||
1316 | * (e|a)|aa*(e|a) = a* | ||
1317 | * (e|a)|(e|a)a*a = a* | ||
1318 | * (e|a)|(e|a)a*(e|a) = a* | ||
1319 | * (e|a)|(e|a)(e|a)*(e|a) = a* | ||
1320 | */ | ||
1321 | if (GNUNET_YES == needs_parentheses (&R_temp_ij)) | ||
1322 | sb_printf1 (R_cur_r, "(%.*s)*", 3, &R_temp_ij); | ||
1323 | else | ||
1324 | sb_printf1 (R_cur_r, "%.*s*", 1, &R_temp_ij); | ||
1325 | } | ||
1326 | else | ||
1327 | { | ||
1328 | /* | ||
1329 | * a|aa*a = a+ | ||
1330 | * a|(e|a)a*a = a+ | ||
1331 | * a|aa*(e|a) = a+ | ||
1332 | * a|(e|a)(e|a)*a = a+ | ||
1333 | * a|a(e|a)*(e|a) = a+ | ||
1334 | */ | ||
1335 | if (GNUNET_YES == needs_parentheses (&R_temp_ij)) | ||
1336 | sb_printf1 (R_cur_r, "(%.*s)+", 3, &R_temp_ij); | ||
1337 | else | ||
1338 | sb_printf1 (R_cur_r, "%.*s+", 1, &R_temp_ij); | ||
1339 | } | ||
1340 | } | ||
1341 | else if ( (0 == ij_ik_cmp) && (0 == clean_kk_kj_cmp) && (0 != clean_ik_kk_cmp) ) | ||
1342 | { | ||
1343 | /* a|ab*b = ab* */ | ||
1344 | if (0 == R_last_kk->slen) | ||
1345 | sb_strdup (R_cur_r, R_last_ij); | ||
1346 | else if (GNUNET_YES == needs_parentheses (&R_temp_kk)) | ||
1347 | sb_printf2 (R_cur_r, "%.*s(%.*s)*", 3, R_last_ij, &R_temp_kk); | ||
1348 | else | ||
1349 | sb_printf2 (R_cur_r, "%.*s%.*s*", 1, R_last_ij, R_last_kk); | ||
1350 | R_cur_l->null_flag = GNUNET_YES; | ||
1351 | } | ||
1352 | else if ( (0 == ij_kj_cmp) && (0 == clean_ik_kk_cmp) && (0 != clean_kk_kj_cmp)) | ||
1353 | { | ||
1354 | /* a|bb*a = b*a */ | ||
1355 | if (R_last_kk->slen < 1) | ||
1356 | { | ||
1357 | sb_strdup (R_cur_r, R_last_kj); | ||
1358 | } | ||
1359 | else if (GNUNET_YES == needs_parentheses (&R_temp_kk)) | ||
1360 | sb_printf2 (R_cur_r, "(%.*s)*%.*s", 3, &R_temp_kk, R_last_kj); | ||
1361 | else | ||
1362 | sb_printf2 (R_cur_r, "%.*s*%.*s", 1, &R_temp_kk, R_last_kj); | ||
1363 | |||
1364 | R_cur_l->null_flag = GNUNET_YES; | ||
1365 | } | ||
1366 | else if ( (0 == ij_ik_cmp) && (0 == kk_kj_cmp) && (! has_epsilon (R_last_ij)) && | ||
1367 | has_epsilon (R_last_kk)) | ||
1368 | { | ||
1369 | /* a|a(e|b)*(e|b) = a|ab* = a|a|ab|abb|abbb|... = ab* */ | ||
1370 | if (needs_parentheses (&R_temp_kk)) | ||
1371 | sb_printf2 (R_cur_r, "%.*s(%.*s)*", 3, R_last_ij, &R_temp_kk); | ||
1372 | else | ||
1373 | sb_printf2 (R_cur_r, "%.*s%.*s*", 1, R_last_ij, &R_temp_kk); | ||
1374 | R_cur_l->null_flag = GNUNET_YES; | ||
1375 | } | ||
1376 | else if ( (0 == ij_kj_cmp) && (0 == ik_kk_cmp) && (! has_epsilon (R_last_ij)) && | ||
1377 | has_epsilon (R_last_kk)) | ||
1378 | { | ||
1379 | /* a|(e|b)(e|b)*a = a|b*a = a|a|ba|bba|bbba|... = b*a */ | ||
1380 | if (needs_parentheses (&R_temp_kk)) | ||
1381 | sb_printf2 (R_cur_r, "(%.*s)*%.*s", 3, &R_temp_kk, R_last_ij); | ||
1382 | else | ||
1383 | sb_printf2 (R_cur_r, "%.*s*%.*s", 1, &R_temp_kk, R_last_ij); | ||
1384 | R_cur_l->null_flag = GNUNET_YES; | ||
1385 | } | ||
1386 | else | ||
1387 | { | ||
1388 | sb_strdup (R_cur_l, R_last_ij); | ||
1389 | remove_parentheses (R_cur_l); | ||
1390 | } | ||
1391 | } | ||
1392 | else | ||
1393 | { | ||
1394 | /* we have no left side */ | ||
1395 | R_cur_l->null_flag = GNUNET_YES; | ||
1396 | } | ||
1397 | |||
1398 | /* construct R_cur_r, if not already constructed */ | ||
1399 | if (GNUNET_YES == R_cur_r->null_flag) | ||
1400 | { | ||
1401 | length = R_temp_kk.slen - R_last_ik->slen; | ||
1402 | |||
1403 | /* a(ba)*bx = (ab)+x */ | ||
1404 | if ( (length > 0) && | ||
1405 | (GNUNET_YES != R_last_kk->null_flag) && | ||
1406 | (0 < R_last_kk->slen) && | ||
1407 | (GNUNET_YES != R_last_kj->null_flag) && | ||
1408 | (0 < R_last_kj->slen) && | ||
1409 | (GNUNET_YES != R_last_ik->null_flag) && | ||
1410 | (0 < R_last_ik->slen) && | ||
1411 | (0 == sb_strkcmp (&R_temp_kk, R_last_ik, length)) && | ||
1412 | (0 == sb_strncmp (&R_temp_kk, R_last_kj, length)) ) | ||
1413 | { | ||
1414 | struct StringBuffer temp_a; | ||
1415 | struct StringBuffer temp_b; | ||
1416 | |||
1417 | sb_init (&temp_a, length); | ||
1418 | sb_init (&temp_b, R_last_kj->slen - length); | ||
1419 | |||
1420 | length_l = length; | ||
1421 | temp_a.sbuf = temp_a.abuf; | ||
1422 | GNUNET_memcpy (temp_a.sbuf, R_last_kj->sbuf, length_l); | ||
1423 | temp_a.slen = length_l; | ||
1424 | |||
1425 | length_r = R_last_kj->slen - length; | ||
1426 | temp_b.sbuf = temp_b.abuf; | ||
1427 | GNUNET_memcpy (temp_b.sbuf, &R_last_kj->sbuf[length], length_r); | ||
1428 | temp_b.slen = length_r; | ||
1429 | |||
1430 | /* e|(ab)+ = (ab)* */ | ||
1431 | if ( (GNUNET_YES != R_cur_l->null_flag) && | ||
1432 | (0 == R_cur_l->slen) && | ||
1433 | (0 == temp_b.slen) ) | ||
1434 | { | ||
1435 | sb_printf2 (R_cur_r, "(%.*s%.*s)*", 3, R_last_ik, &temp_a); | ||
1436 | sb_free (R_cur_l); | ||
1437 | R_cur_l->null_flag = GNUNET_YES; | ||
1438 | } | ||
1439 | else | ||
1440 | { | ||
1441 | sb_printf3 (R_cur_r, "(%.*s%.*s)+%.*s", 3, R_last_ik, &temp_a, &temp_b); | ||
1442 | } | ||
1443 | sb_free (&temp_a); | ||
1444 | sb_free (&temp_b); | ||
1445 | } | ||
1446 | else if (0 == sb_strcmp (&R_temp_ik, &R_temp_kk) && | ||
1447 | 0 == sb_strcmp (&R_temp_kk, &R_temp_kj)) | ||
1448 | { | ||
1449 | /* | ||
1450 | * (e|a)a*(e|a) = a* | ||
1451 | * (e|a)(e|a)*(e|a) = a* | ||
1452 | */ | ||
1453 | if (has_epsilon (R_last_ik) && has_epsilon (R_last_kj)) | ||
1454 | { | ||
1455 | if (needs_parentheses (&R_temp_kk)) | ||
1456 | sb_printf1 (R_cur_r, "(%.*s)*", 3, &R_temp_kk); | ||
1457 | else | ||
1458 | sb_printf1 (R_cur_r, "%.*s*", 1, &R_temp_kk); | ||
1459 | } | ||
1460 | /* aa*a = a+a */ | ||
1461 | else if ( (0 == clean_ik_kk_cmp) && | ||
1462 | (0 == clean_kk_kj_cmp) && | ||
1463 | (! has_epsilon (R_last_ik)) ) | ||
1464 | { | ||
1465 | if (needs_parentheses (&R_temp_kk)) | ||
1466 | sb_printf2 (R_cur_r, "(%.*s)+%.*s", 3, &R_temp_kk, &R_temp_kk); | ||
1467 | else | ||
1468 | sb_printf2 (R_cur_r, "%.*s+%.*s", 1, &R_temp_kk, &R_temp_kk); | ||
1469 | } | ||
1470 | /* | ||
1471 | * (e|a)a*a = a+ | ||
1472 | * aa*(e|a) = a+ | ||
1473 | * a(e|a)*(e|a) = a+ | ||
1474 | * (e|a)a*a = a+ | ||
1475 | */ | ||
1476 | else | ||
1477 | { | ||
1478 | eps_check = | ||
1479 | (has_epsilon (R_last_ik) + has_epsilon (R_last_kk) + | ||
1480 | has_epsilon (R_last_kj)); | ||
1481 | |||
1482 | if (1 == eps_check) | ||
1483 | { | ||
1484 | if (needs_parentheses (&R_temp_kk)) | ||
1485 | sb_printf1 (R_cur_r, "(%.*s)+", 3, &R_temp_kk); | ||
1486 | else | ||
1487 | sb_printf1 (R_cur_r, "%.*s+", 1, &R_temp_kk); | ||
1488 | } | ||
1489 | } | ||
1490 | } | ||
1491 | /* | ||
1492 | * aa*b = a+b | ||
1493 | * (e|a)(e|a)*b = a*b | ||
1494 | */ | ||
1495 | else if (0 == sb_strcmp (&R_temp_ik, &R_temp_kk)) | ||
1496 | { | ||
1497 | if (has_epsilon (R_last_ik)) | ||
1498 | { | ||
1499 | if (needs_parentheses (&R_temp_kk)) | ||
1500 | sb_printf2 (R_cur_r, "(%.*s)*%.*s", 3, &R_temp_kk, R_last_kj); | ||
1501 | else | ||
1502 | sb_printf2 (R_cur_r, "%.*s*%.*s", 1, &R_temp_kk, R_last_kj); | ||
1503 | } | ||
1504 | else | ||
1505 | { | ||
1506 | if (needs_parentheses (&R_temp_kk)) | ||
1507 | sb_printf2 (R_cur_r, "(%.*s)+%.*s", 3, &R_temp_kk, R_last_kj); | ||
1508 | else | ||
1509 | sb_printf2 (R_cur_r, "%.*s+%.*s", 1, &R_temp_kk, R_last_kj); | ||
1510 | } | ||
1511 | } | ||
1512 | /* | ||
1513 | * ba*a = ba+ | ||
1514 | * b(e|a)*(e|a) = ba* | ||
1515 | */ | ||
1516 | else if (0 == sb_strcmp (&R_temp_kk, &R_temp_kj)) | ||
1517 | { | ||
1518 | if (has_epsilon (R_last_kj)) | ||
1519 | { | ||
1520 | if (needs_parentheses (&R_temp_kk)) | ||
1521 | sb_printf2 (R_cur_r, "%.*s(%.*s)*", 3, R_last_ik, &R_temp_kk); | ||
1522 | else | ||
1523 | sb_printf2 (R_cur_r, "%.*s%.*s*", 1, R_last_ik, &R_temp_kk); | ||
1524 | } | ||
1525 | else | ||
1526 | { | ||
1527 | if (needs_parentheses (&R_temp_kk)) | ||
1528 | sb_printf2 (R_cur_r, "(%.*s)+%.*s", 3, R_last_ik, &R_temp_kk); | ||
1529 | else | ||
1530 | sb_printf2 (R_cur_r, "%.*s+%.*s", 1, R_last_ik, &R_temp_kk); | ||
1531 | } | ||
1532 | } | ||
1533 | else | ||
1534 | { | ||
1535 | if (0 < R_temp_kk.slen) | ||
1536 | { | ||
1537 | if (needs_parentheses (&R_temp_kk)) | ||
1538 | { | ||
1539 | sb_printf3 (R_cur_r, "%.*s(%.*s)*%.*s", 3, R_last_ik, &R_temp_kk, | ||
1540 | R_last_kj); | ||
1541 | } | ||
1542 | else | ||
1543 | { | ||
1544 | sb_printf3 (R_cur_r, "%.*s%.*s*%.*s", 1, R_last_ik, &R_temp_kk, | ||
1545 | R_last_kj); | ||
1546 | } | ||
1547 | } | ||
1548 | else | ||
1549 | { | ||
1550 | sb_printf2 (R_cur_r, "%.*s%.*s", 0, R_last_ik, R_last_kj); | ||
1551 | } | ||
1552 | } | ||
1553 | } | ||
1554 | sb_free (&R_temp_ij); | ||
1555 | sb_free (&R_temp_ik); | ||
1556 | sb_free (&R_temp_kk); | ||
1557 | sb_free (&R_temp_kj); | ||
1558 | |||
1559 | if ( (GNUNET_YES == R_cur_l->null_flag) && | ||
1560 | (GNUNET_YES == R_cur_r->null_flag) ) | ||
1561 | { | ||
1562 | R_cur_ij->null_flag = GNUNET_YES; | ||
1563 | return; | ||
1564 | } | ||
1565 | |||
1566 | if ( (GNUNET_YES != R_cur_l->null_flag) && | ||
1567 | (GNUNET_YES == R_cur_r->null_flag) ) | ||
1568 | { | ||
1569 | struct StringBuffer tmp; | ||
1570 | |||
1571 | tmp = *R_cur_ij; | ||
1572 | *R_cur_ij = *R_cur_l; | ||
1573 | *R_cur_l = tmp; | ||
1574 | return; | ||
1575 | } | ||
1576 | |||
1577 | if ( (GNUNET_YES == R_cur_l->null_flag) && | ||
1578 | (GNUNET_YES != R_cur_r->null_flag) ) | ||
1579 | { | ||
1580 | struct StringBuffer tmp; | ||
1581 | |||
1582 | tmp = *R_cur_ij; | ||
1583 | *R_cur_ij = *R_cur_r; | ||
1584 | *R_cur_r = tmp; | ||
1585 | return; | ||
1586 | } | ||
1587 | |||
1588 | if (0 == sb_nullstrcmp (R_cur_l, R_cur_r)) | ||
1589 | { | ||
1590 | struct StringBuffer tmp; | ||
1591 | |||
1592 | tmp = *R_cur_ij; | ||
1593 | *R_cur_ij = *R_cur_l; | ||
1594 | *R_cur_l = tmp; | ||
1595 | return; | ||
1596 | } | ||
1597 | sb_printf2 (R_cur_ij, "(%.*s|%.*s)", 3, R_cur_l, R_cur_r); | ||
1598 | } | ||
1599 | |||
1600 | |||
1601 | /** | ||
1602 | * Create proofs for all states in the given automaton. Implementation of the | ||
1603 | * algorithm descriped in chapter 3.2.1 of "Automata Theory, Languages, and | ||
1604 | * Computation 3rd Edition" by Hopcroft, Motwani and Ullman. | ||
1605 | * | ||
1606 | * Each state in the automaton gets assigned 'proof' and 'hash' (hash of the | ||
1607 | * proof) fields. The starting state will only have a valid proof/hash if it has | ||
1608 | * any incoming transitions. | ||
1609 | * | ||
1610 | * @param a automaton for which to assign proofs and hashes, must not be NULL | ||
1611 | */ | ||
1612 | static int | ||
1613 | automaton_create_proofs (struct REGEX_INTERNAL_Automaton *a) | ||
1614 | { | ||
1615 | unsigned int n = a->state_count; | ||
1616 | struct REGEX_INTERNAL_State *states[n]; | ||
1617 | struct StringBuffer *R_last; | ||
1618 | struct StringBuffer *R_cur; | ||
1619 | struct StringBuffer R_cur_r; | ||
1620 | struct StringBuffer R_cur_l; | ||
1621 | struct StringBuffer *R_swap; | ||
1622 | struct REGEX_INTERNAL_Transition *t; | ||
1623 | struct StringBuffer complete_regex; | ||
1624 | unsigned int i; | ||
1625 | unsigned int j; | ||
1626 | unsigned int k; | ||
1627 | |||
1628 | R_last = GNUNET_malloc_large (sizeof (struct StringBuffer) * n * n); | ||
1629 | R_cur = GNUNET_malloc_large (sizeof (struct StringBuffer) * n * n); | ||
1630 | if ( (NULL == R_last) || | ||
1631 | (NULL == R_cur) ) | ||
1632 | { | ||
1633 | GNUNET_log_strerror (GNUNET_ERROR_TYPE_ERROR, "malloc"); | ||
1634 | GNUNET_free_non_null (R_cur); | ||
1635 | GNUNET_free_non_null (R_last); | ||
1636 | return GNUNET_SYSERR; | ||
1637 | } | ||
1638 | |||
1639 | /* create depth-first numbering of the states, initializes 'state' */ | ||
1640 | REGEX_INTERNAL_automaton_traverse (a, a->start, NULL, NULL, &number_states, | ||
1641 | states); | ||
1642 | |||
1643 | for (i = 0; i < n; i++) | ||
1644 | GNUNET_assert (NULL != states[i]); | ||
1645 | for (i = 0; i < n; i++) | ||
1646 | for (j = 0; j < n; j++) | ||
1647 | R_last[i *n + j].null_flag = GNUNET_YES; | ||
1648 | |||
1649 | /* Compute regular expressions of length "1" between each pair of states */ | ||
1650 | for (i = 0; i < n; i++) | ||
1651 | { | ||
1652 | for (t = states[i]->transitions_head; NULL != t; t = t->next) | ||
1653 | { | ||
1654 | j = t->to_state->dfs_id; | ||
1655 | if (GNUNET_YES == R_last[i * n + j].null_flag) | ||
1656 | { | ||
1657 | sb_strdup_cstr (&R_last[i * n + j], t->label); | ||
1658 | } | ||
1659 | else | ||
1660 | { | ||
1661 | sb_append_cstr (&R_last[i * n + j], "|"); | ||
1662 | sb_append_cstr (&R_last[i * n + j], t->label); | ||
1663 | } | ||
1664 | } | ||
1665 | /* add self-loop: i is reachable from i via epsilon-transition */ | ||
1666 | if (GNUNET_YES == R_last[i * n + i].null_flag) | ||
1667 | { | ||
1668 | R_last[i * n + i].slen = 0; | ||
1669 | R_last[i * n + i].null_flag = GNUNET_NO; | ||
1670 | } | ||
1671 | else | ||
1672 | { | ||
1673 | sb_wrap (&R_last[i * n + i], "(|%.*s)", 3); | ||
1674 | } | ||
1675 | } | ||
1676 | for (i = 0; i < n; i++) | ||
1677 | for (j = 0; j < n; j++) | ||
1678 | if (needs_parentheses (&R_last[i * n + j])) | ||
1679 | sb_wrap (&R_last[i * n + j], "(%.*s)", 2); | ||
1680 | /* Compute regular expressions of length "k" between each pair of states per | ||
1681 | * induction */ | ||
1682 | memset (&R_cur_l, 0, sizeof (struct StringBuffer)); | ||
1683 | memset (&R_cur_r, 0, sizeof (struct StringBuffer)); | ||
1684 | for (k = 0; k < n; k++) | ||
1685 | { | ||
1686 | for (i = 0; i < n; i++) | ||
1687 | { | ||
1688 | for (j = 0; j < n; j++) | ||
1689 | { | ||
1690 | /* Basis for the recursion: | ||
1691 | * $R^{(k)}_{ij} = R^{(k-1)}_{ij} | R^{(k-1)}_{ik} ( R^{(k-1)}_{kk} )^* R^{(k-1)}_{kj} | ||
1692 | * R_last == R^{(k-1)}, R_cur == R^{(k)} | ||
1693 | */ | ||
1694 | |||
1695 | /* Create R_cur[i][j] and simplify the expression */ | ||
1696 | automaton_create_proofs_simplify (&R_last[i * n + j], &R_last[i * n + k], | ||
1697 | &R_last[k * n + k], &R_last[k * n + j], | ||
1698 | &R_cur[i * n + j], | ||
1699 | &R_cur_l, &R_cur_r); | ||
1700 | } | ||
1701 | } | ||
1702 | /* set R_last = R_cur */ | ||
1703 | R_swap = R_last; | ||
1704 | R_last = R_cur; | ||
1705 | R_cur = R_swap; | ||
1706 | /* clear 'R_cur' for next iteration */ | ||
1707 | for (i = 0; i < n; i++) | ||
1708 | for (j = 0; j < n; j++) | ||
1709 | R_cur[i * n + j].null_flag = GNUNET_YES; | ||
1710 | } | ||
1711 | sb_free (&R_cur_l); | ||
1712 | sb_free (&R_cur_r); | ||
1713 | /* assign proofs and hashes */ | ||
1714 | for (i = 0; i < n; i++) | ||
1715 | { | ||
1716 | if (GNUNET_YES != R_last[a->start->dfs_id * n + i].null_flag) | ||
1717 | { | ||
1718 | states[i]->proof = GNUNET_strndup (R_last[a->start->dfs_id * n + i].sbuf, | ||
1719 | R_last[a->start->dfs_id * n + i].slen); | ||
1720 | GNUNET_CRYPTO_hash (states[i]->proof, strlen (states[i]->proof), | ||
1721 | &states[i]->hash); | ||
1722 | } | ||
1723 | } | ||
1724 | |||
1725 | /* complete regex for whole DFA: union of all pairs (start state/accepting | ||
1726 | * state(s)). */ | ||
1727 | sb_init (&complete_regex, 16 * n); | ||
1728 | for (i = 0; i < n; i++) | ||
1729 | { | ||
1730 | if (states[i]->accepting) | ||
1731 | { | ||
1732 | if ( (0 == complete_regex.slen) && | ||
1733 | (0 < R_last[a->start->dfs_id * n + i].slen) ) | ||
1734 | { | ||
1735 | sb_append (&complete_regex, | ||
1736 | &R_last[a->start->dfs_id * n + i]); | ||
1737 | } | ||
1738 | else if ( (GNUNET_YES != R_last[a->start->dfs_id * n + i].null_flag) && | ||
1739 | (0 < R_last[a->start->dfs_id * n + i].slen) ) | ||
1740 | { | ||
1741 | sb_append_cstr (&complete_regex, "|"); | ||
1742 | sb_append (&complete_regex, | ||
1743 | &R_last[a->start->dfs_id * n + i]); | ||
1744 | } | ||
1745 | } | ||
1746 | } | ||
1747 | a->canonical_regex = GNUNET_strndup (complete_regex.sbuf, complete_regex.slen); | ||
1748 | |||
1749 | /* cleanup */ | ||
1750 | sb_free (&complete_regex); | ||
1751 | for (i = 0; i < n; i++) | ||
1752 | for (j = 0; j < n; j++) | ||
1753 | { | ||
1754 | sb_free (&R_cur[i * n + j]); | ||
1755 | sb_free (&R_last[i * n + j]); | ||
1756 | } | ||
1757 | GNUNET_free (R_cur); | ||
1758 | GNUNET_free (R_last); | ||
1759 | return GNUNET_OK; | ||
1760 | } | ||
1761 | |||
1762 | |||
1763 | /** | ||
1764 | * Creates a new DFA state based on a set of NFA states. Needs to be freed using | ||
1765 | * automaton_destroy_state. | ||
1766 | * | ||
1767 | * @param ctx context | ||
1768 | * @param nfa_states set of NFA states on which the DFA should be based on | ||
1769 | * | ||
1770 | * @return new DFA state | ||
1771 | */ | ||
1772 | static struct REGEX_INTERNAL_State * | ||
1773 | dfa_state_create (struct REGEX_INTERNAL_Context *ctx, | ||
1774 | struct REGEX_INTERNAL_StateSet *nfa_states) | ||
1775 | { | ||
1776 | struct REGEX_INTERNAL_State *s; | ||
1777 | char *pos; | ||
1778 | size_t len; | ||
1779 | struct REGEX_INTERNAL_State *cstate; | ||
1780 | struct REGEX_INTERNAL_Transition *ctran; | ||
1781 | unsigned int i; | ||
1782 | |||
1783 | s = GNUNET_new (struct REGEX_INTERNAL_State); | ||
1784 | s->id = ctx->state_id++; | ||
1785 | s->index = -1; | ||
1786 | s->lowlink = -1; | ||
1787 | |||
1788 | if (NULL == nfa_states) | ||
1789 | { | ||
1790 | GNUNET_asprintf (&s->name, "s%i", s->id); | ||
1791 | return s; | ||
1792 | } | ||
1793 | |||
1794 | s->nfa_set = *nfa_states; | ||
1795 | |||
1796 | if (nfa_states->off < 1) | ||
1797 | return s; | ||
1798 | |||
1799 | /* Create a name based on 'nfa_states' */ | ||
1800 | len = nfa_states->off * 14 + 4; | ||
1801 | s->name = GNUNET_malloc (len); | ||
1802 | strcat (s->name, "{"); | ||
1803 | pos = s->name + 1; | ||
1804 | |||
1805 | for (i = 0; i < nfa_states->off; i++) | ||
1806 | { | ||
1807 | cstate = nfa_states->states[i]; | ||
1808 | GNUNET_snprintf (pos, | ||
1809 | pos - s->name + len, | ||
1810 | "%i,", | ||
1811 | cstate->id); | ||
1812 | pos += strlen (pos); | ||
1813 | |||
1814 | /* Add a transition for each distinct label to NULL state */ | ||
1815 | for (ctran = cstate->transitions_head; NULL != ctran; ctran = ctran->next) | ||
1816 | if (NULL != ctran->label) | ||
1817 | state_add_transition (ctx, s, ctran->label, NULL); | ||
1818 | |||
1819 | /* If the nfa_states contain an accepting state, the new dfa state is also | ||
1820 | * accepting. */ | ||
1821 | if (cstate->accepting) | ||
1822 | s->accepting = 1; | ||
1823 | } | ||
1824 | pos[-1] = '}'; | ||
1825 | s->name = GNUNET_realloc (s->name, strlen (s->name) + 1); | ||
1826 | |||
1827 | memset (nfa_states, 0, sizeof (struct REGEX_INTERNAL_StateSet)); | ||
1828 | return s; | ||
1829 | } | ||
1830 | |||
1831 | |||
1832 | /** | ||
1833 | * Move from the given state 's' to the next state on transition 'str'. Consumes | ||
1834 | * as much of the given 'str' as possible (usefull for strided DFAs). On return | ||
1835 | * 's' will point to the next state, and the length of the substring used for | ||
1836 | * this transition will be returned. If no transition possible 0 is returned and | ||
1837 | * 's' points to NULL. | ||
1838 | * | ||
1839 | * @param s starting state, will point to the next state or NULL (if no | ||
1840 | * transition possible) | ||
1841 | * @param str edge label to follow (will match longest common prefix) | ||
1842 | * | ||
1843 | * @return length of the substring comsumed from 'str' | ||
1844 | */ | ||
1845 | static unsigned int | ||
1846 | dfa_move (struct REGEX_INTERNAL_State **s, const char *str) | ||
1847 | { | ||
1848 | struct REGEX_INTERNAL_Transition *t; | ||
1849 | struct REGEX_INTERNAL_State *new_s; | ||
1850 | unsigned int len; | ||
1851 | unsigned int max_len; | ||
1852 | |||
1853 | if (NULL == s) | ||
1854 | return 0; | ||
1855 | |||
1856 | new_s = NULL; | ||
1857 | max_len = 0; | ||
1858 | for (t = (*s)->transitions_head; NULL != t; t = t->next) | ||
1859 | { | ||
1860 | len = strlen (t->label); | ||
1861 | |||
1862 | if (0 == strncmp (t->label, str, len)) | ||
1863 | { | ||
1864 | if (len >= max_len) | ||
1865 | { | ||
1866 | max_len = len; | ||
1867 | new_s = t->to_state; | ||
1868 | } | ||
1869 | } | ||
1870 | } | ||
1871 | |||
1872 | *s = new_s; | ||
1873 | return max_len; | ||
1874 | } | ||
1875 | |||
1876 | |||
1877 | /** | ||
1878 | * Set the given state 'marked' to #GNUNET_YES. Used by the | ||
1879 | * #dfa_remove_unreachable_states() function to detect unreachable states in the | ||
1880 | * automaton. | ||
1881 | * | ||
1882 | * @param cls closure, not used. | ||
1883 | * @param count count, not used. | ||
1884 | * @param s state where the marked attribute will be set to #GNUNET_YES. | ||
1885 | */ | ||
1886 | static void | ||
1887 | mark_states (void *cls, | ||
1888 | const unsigned int count, | ||
1889 | struct REGEX_INTERNAL_State *s) | ||
1890 | { | ||
1891 | s->marked = GNUNET_YES; | ||
1892 | } | ||
1893 | |||
1894 | |||
1895 | /** | ||
1896 | * Remove all unreachable states from DFA 'a'. Unreachable states are those | ||
1897 | * states that are not reachable from the starting state. | ||
1898 | * | ||
1899 | * @param a DFA automaton | ||
1900 | */ | ||
1901 | static void | ||
1902 | dfa_remove_unreachable_states (struct REGEX_INTERNAL_Automaton *a) | ||
1903 | { | ||
1904 | struct REGEX_INTERNAL_State *s; | ||
1905 | struct REGEX_INTERNAL_State *s_next; | ||
1906 | |||
1907 | /* 1. unmark all states */ | ||
1908 | for (s = a->states_head; NULL != s; s = s->next) | ||
1909 | s->marked = GNUNET_NO; | ||
1910 | |||
1911 | /* 2. traverse dfa from start state and mark all visited states */ | ||
1912 | REGEX_INTERNAL_automaton_traverse (a, a->start, NULL, NULL, &mark_states, NULL); | ||
1913 | |||
1914 | /* 3. delete all states that were not visited */ | ||
1915 | for (s = a->states_head; NULL != s; s = s_next) | ||
1916 | { | ||
1917 | s_next = s->next; | ||
1918 | if (GNUNET_NO == s->marked) | ||
1919 | automaton_remove_state (a, s); | ||
1920 | } | ||
1921 | } | ||
1922 | |||
1923 | |||
1924 | /** | ||
1925 | * Remove all dead states from the DFA 'a'. Dead states are those states that do | ||
1926 | * not transition to any other state but themselves. | ||
1927 | * | ||
1928 | * @param a DFA automaton | ||
1929 | */ | ||
1930 | static void | ||
1931 | dfa_remove_dead_states (struct REGEX_INTERNAL_Automaton *a) | ||
1932 | { | ||
1933 | struct REGEX_INTERNAL_State *s; | ||
1934 | struct REGEX_INTERNAL_State *s_next; | ||
1935 | struct REGEX_INTERNAL_Transition *t; | ||
1936 | int dead; | ||
1937 | |||
1938 | GNUNET_assert (DFA == a->type); | ||
1939 | |||
1940 | for (s = a->states_head; NULL != s; s = s_next) | ||
1941 | { | ||
1942 | s_next = s->next; | ||
1943 | |||
1944 | if (s->accepting) | ||
1945 | continue; | ||
1946 | |||
1947 | dead = 1; | ||
1948 | for (t = s->transitions_head; NULL != t; t = t->next) | ||
1949 | { | ||
1950 | if (NULL != t->to_state && t->to_state != s) | ||
1951 | { | ||
1952 | dead = 0; | ||
1953 | break; | ||
1954 | } | ||
1955 | } | ||
1956 | |||
1957 | if (0 == dead) | ||
1958 | continue; | ||
1959 | |||
1960 | /* state s is dead, remove it */ | ||
1961 | automaton_remove_state (a, s); | ||
1962 | } | ||
1963 | } | ||
1964 | |||
1965 | |||
1966 | /** | ||
1967 | * Merge all non distinguishable states in the DFA 'a' | ||
1968 | * | ||
1969 | * @param ctx context | ||
1970 | * @param a DFA automaton | ||
1971 | * @return #GNUNET_OK on success | ||
1972 | */ | ||
1973 | static int | ||
1974 | dfa_merge_nondistinguishable_states (struct REGEX_INTERNAL_Context *ctx, | ||
1975 | struct REGEX_INTERNAL_Automaton *a) | ||
1976 | { | ||
1977 | uint32_t *table; | ||
1978 | struct REGEX_INTERNAL_State *s1; | ||
1979 | struct REGEX_INTERNAL_State *s2; | ||
1980 | struct REGEX_INTERNAL_Transition *t1; | ||
1981 | struct REGEX_INTERNAL_Transition *t2; | ||
1982 | struct REGEX_INTERNAL_State *s1_next; | ||
1983 | struct REGEX_INTERNAL_State *s2_next; | ||
1984 | int change; | ||
1985 | unsigned int num_equal_edges; | ||
1986 | unsigned int i; | ||
1987 | unsigned int state_cnt; | ||
1988 | unsigned long long idx; | ||
1989 | unsigned long long idx1; | ||
1990 | |||
1991 | if ( (NULL == a) || (0 == a->state_count) ) | ||
1992 | { | ||
1993 | GNUNET_log (GNUNET_ERROR_TYPE_ERROR, | ||
1994 | "Could not merge nondistinguishable states, automaton was NULL.\n"); | ||
1995 | return GNUNET_SYSERR; | ||
1996 | } | ||
1997 | |||
1998 | state_cnt = a->state_count; | ||
1999 | table = GNUNET_malloc_large ((sizeof (uint32_t) * state_cnt * state_cnt / 32) + sizeof (uint32_t)); | ||
2000 | if (NULL == table) | ||
2001 | { | ||
2002 | GNUNET_log_strerror (GNUNET_ERROR_TYPE_ERROR, "malloc"); | ||
2003 | return GNUNET_SYSERR; | ||
2004 | } | ||
2005 | |||
2006 | for (i = 0, s1 = a->states_head; NULL != s1; s1 = s1->next) | ||
2007 | s1->marked = i++; | ||
2008 | |||
2009 | /* Mark all pairs of accepting/!accepting states */ | ||
2010 | for (s1 = a->states_head; NULL != s1; s1 = s1->next) | ||
2011 | for (s2 = a->states_head; NULL != s2; s2 = s2->next) | ||
2012 | if ( (s1->accepting && !s2->accepting) || | ||
2013 | (!s1->accepting && s2->accepting) ) | ||
2014 | { | ||
2015 | idx = (unsigned long long) s1->marked * state_cnt + s2->marked; | ||
2016 | table[idx / 32] |= (1U << (idx % 32)); | ||
2017 | } | ||
2018 | |||
2019 | /* Find all equal states */ | ||
2020 | change = 1; | ||
2021 | while (0 != change) | ||
2022 | { | ||
2023 | change = 0; | ||
2024 | for (s1 = a->states_head; NULL != s1; s1 = s1->next) | ||
2025 | { | ||
2026 | for (s2 = a->states_head; NULL != s2 && s1 != s2; s2 = s2->next) | ||
2027 | { | ||
2028 | idx = (unsigned long long) s1->marked * state_cnt + s2->marked; | ||
2029 | if (0 != (table[idx / 32] & (1U << (idx % 32)))) | ||
2030 | continue; | ||
2031 | num_equal_edges = 0; | ||
2032 | for (t1 = s1->transitions_head; NULL != t1; t1 = t1->next) | ||
2033 | { | ||
2034 | for (t2 = s2->transitions_head; NULL != t2; t2 = t2->next) | ||
2035 | { | ||
2036 | if (0 == strcmp (t1->label, t2->label)) | ||
2037 | { | ||
2038 | num_equal_edges++; | ||
2039 | /* same edge, but targets definitively different, so we're different | ||
2040 | as well */ | ||
2041 | if (t1->to_state->marked > t2->to_state->marked) | ||
2042 | idx1 = (unsigned long long) t1->to_state->marked * state_cnt + t2->to_state->marked; | ||
2043 | else | ||
2044 | idx1 = (unsigned long long) t2->to_state->marked * state_cnt + t1->to_state->marked; | ||
2045 | if (0 != (table[idx1 / 32] & (1U << (idx1 % 32)))) | ||
2046 | { | ||
2047 | table[idx / 32] |= (1U << (idx % 32)); | ||
2048 | change = 1; /* changed a marker, need to run again */ | ||
2049 | } | ||
2050 | } | ||
2051 | } | ||
2052 | } | ||
2053 | if ( (num_equal_edges != s1->transition_count) || | ||
2054 | (num_equal_edges != s2->transition_count) ) | ||
2055 | { | ||
2056 | /* Make sure ALL edges of possible equal states are the same */ | ||
2057 | table[idx / 32] |= (1U << (idx % 32)); | ||
2058 | change = 1; /* changed a marker, need to run again */ | ||
2059 | } | ||
2060 | } | ||
2061 | } | ||
2062 | } | ||
2063 | |||
2064 | /* Merge states that are equal */ | ||
2065 | for (s1 = a->states_head; NULL != s1; s1 = s1_next) | ||
2066 | { | ||
2067 | s1_next = s1->next; | ||
2068 | for (s2 = a->states_head; NULL != s2 && s1 != s2; s2 = s2_next) | ||
2069 | { | ||
2070 | s2_next = s2->next; | ||
2071 | idx = (unsigned long long) s1->marked * state_cnt + s2->marked; | ||
2072 | if (0 == (table[idx / 32] & (1U << (idx % 32)))) | ||
2073 | automaton_merge_states (ctx, a, s1, s2); | ||
2074 | } | ||
2075 | } | ||
2076 | |||
2077 | GNUNET_free (table); | ||
2078 | return GNUNET_OK; | ||
2079 | } | ||
2080 | |||
2081 | |||
2082 | /** | ||
2083 | * Minimize the given DFA 'a' by removing all unreachable states, removing all | ||
2084 | * dead states and merging all non distinguishable states | ||
2085 | * | ||
2086 | * @param ctx context | ||
2087 | * @param a DFA automaton | ||
2088 | * @return GNUNET_OK on success | ||
2089 | */ | ||
2090 | static int | ||
2091 | dfa_minimize (struct REGEX_INTERNAL_Context *ctx, | ||
2092 | struct REGEX_INTERNAL_Automaton *a) | ||
2093 | { | ||
2094 | if (NULL == a) | ||
2095 | return GNUNET_SYSERR; | ||
2096 | |||
2097 | GNUNET_assert (DFA == a->type); | ||
2098 | |||
2099 | /* 1. remove unreachable states */ | ||
2100 | dfa_remove_unreachable_states (a); | ||
2101 | |||
2102 | /* 2. remove dead states */ | ||
2103 | dfa_remove_dead_states (a); | ||
2104 | |||
2105 | /* 3. Merge nondistinguishable states */ | ||
2106 | if (GNUNET_OK != dfa_merge_nondistinguishable_states (ctx, a)) | ||
2107 | return GNUNET_SYSERR; | ||
2108 | return GNUNET_OK; | ||
2109 | } | ||
2110 | |||
2111 | |||
2112 | /** | ||
2113 | * Context for adding strided transitions to a DFA. | ||
2114 | */ | ||
2115 | struct REGEX_INTERNAL_Strided_Context | ||
2116 | { | ||
2117 | /** | ||
2118 | * Length of the strides. | ||
2119 | */ | ||
2120 | const unsigned int stride; | ||
2121 | |||
2122 | /** | ||
2123 | * Strided transitions DLL. New strided transitions will be stored in this DLL | ||
2124 | * and afterwards added to the DFA. | ||
2125 | */ | ||
2126 | struct REGEX_INTERNAL_Transition *transitions_head; | ||
2127 | |||
2128 | /** | ||
2129 | * Strided transitions DLL. | ||
2130 | */ | ||
2131 | struct REGEX_INTERNAL_Transition *transitions_tail; | ||
2132 | }; | ||
2133 | |||
2134 | |||
2135 | /** | ||
2136 | * Recursive helper function to add strides to a DFA. | ||
2137 | * | ||
2138 | * @param cls context, contains stride length and strided transitions DLL. | ||
2139 | * @param depth current depth of the depth-first traversal of the graph. | ||
2140 | * @param label current label, string that contains all labels on the path from | ||
2141 | * 'start' to 's'. | ||
2142 | * @param start start state for the depth-first traversal of the graph. | ||
2143 | * @param s current state in the depth-first traversal | ||
2144 | */ | ||
2145 | static void | ||
2146 | dfa_add_multi_strides_helper (void *cls, const unsigned int depth, char *label, | ||
2147 | struct REGEX_INTERNAL_State *start, | ||
2148 | struct REGEX_INTERNAL_State *s) | ||
2149 | { | ||
2150 | struct REGEX_INTERNAL_Strided_Context *ctx = cls; | ||
2151 | struct REGEX_INTERNAL_Transition *t; | ||
2152 | char *new_label; | ||
2153 | |||
2154 | if (depth == ctx->stride) | ||
2155 | { | ||
2156 | t = GNUNET_new (struct REGEX_INTERNAL_Transition); | ||
2157 | t->label = GNUNET_strdup (label); | ||
2158 | t->to_state = s; | ||
2159 | t->from_state = start; | ||
2160 | GNUNET_CONTAINER_DLL_insert (ctx->transitions_head, ctx->transitions_tail, | ||
2161 | t); | ||
2162 | } | ||
2163 | else | ||
2164 | { | ||
2165 | for (t = s->transitions_head; NULL != t; t = t->next) | ||
2166 | { | ||
2167 | /* Do not consider self-loops, because it end's up in too many | ||
2168 | * transitions */ | ||
2169 | if (t->to_state == t->from_state) | ||
2170 | continue; | ||
2171 | |||
2172 | if (NULL != label) | ||
2173 | { | ||
2174 | GNUNET_asprintf (&new_label, "%s%s", label, t->label); | ||
2175 | } | ||
2176 | else | ||
2177 | new_label = GNUNET_strdup (t->label); | ||
2178 | |||
2179 | dfa_add_multi_strides_helper (cls, (depth + 1), new_label, start, | ||
2180 | t->to_state); | ||
2181 | } | ||
2182 | } | ||
2183 | GNUNET_free_non_null (label); | ||
2184 | } | ||
2185 | |||
2186 | |||
2187 | /** | ||
2188 | * Function called for each state in the DFA. Starts a traversal of depth set in | ||
2189 | * context starting from state 's'. | ||
2190 | * | ||
2191 | * @param cls context. | ||
2192 | * @param count not used. | ||
2193 | * @param s current state. | ||
2194 | */ | ||
2195 | static void | ||
2196 | dfa_add_multi_strides (void *cls, const unsigned int count, | ||
2197 | struct REGEX_INTERNAL_State *s) | ||
2198 | { | ||
2199 | dfa_add_multi_strides_helper (cls, 0, NULL, s, s); | ||
2200 | } | ||
2201 | |||
2202 | |||
2203 | /** | ||
2204 | * Adds multi-strided transitions to the given 'dfa'. | ||
2205 | * | ||
2206 | * @param regex_ctx regex context needed to add transitions to the automaton. | ||
2207 | * @param dfa DFA to which the multi strided transitions should be added. | ||
2208 | * @param stride_len length of the strides. | ||
2209 | */ | ||
2210 | void | ||
2211 | REGEX_INTERNAL_dfa_add_multi_strides (struct REGEX_INTERNAL_Context *regex_ctx, | ||
2212 | struct REGEX_INTERNAL_Automaton *dfa, | ||
2213 | const unsigned int stride_len) | ||
2214 | { | ||
2215 | struct REGEX_INTERNAL_Strided_Context ctx = { stride_len, NULL, NULL }; | ||
2216 | struct REGEX_INTERNAL_Transition *t; | ||
2217 | struct REGEX_INTERNAL_Transition *t_next; | ||
2218 | |||
2219 | if (1 > stride_len || GNUNET_YES == dfa->is_multistrided) | ||
2220 | return; | ||
2221 | |||
2222 | /* Compute the new transitions of given stride_len */ | ||
2223 | REGEX_INTERNAL_automaton_traverse (dfa, dfa->start, NULL, NULL, | ||
2224 | &dfa_add_multi_strides, &ctx); | ||
2225 | |||
2226 | /* Add all the new transitions to the automaton. */ | ||
2227 | for (t = ctx.transitions_head; NULL != t; t = t_next) | ||
2228 | { | ||
2229 | t_next = t->next; | ||
2230 | state_add_transition (regex_ctx, t->from_state, t->label, t->to_state); | ||
2231 | GNUNET_CONTAINER_DLL_remove (ctx.transitions_head, ctx.transitions_tail, t); | ||
2232 | GNUNET_free_non_null (t->label); | ||
2233 | GNUNET_free (t); | ||
2234 | } | ||
2235 | |||
2236 | /* Mark this automaton as multistrided */ | ||
2237 | dfa->is_multistrided = GNUNET_YES; | ||
2238 | } | ||
2239 | |||
2240 | /** | ||
2241 | * Recursive Helper function for DFA path compression. Does DFS on the DFA graph | ||
2242 | * and adds new transitions to the given transitions DLL and marks states that | ||
2243 | * should be removed by setting state->contained to GNUNET_YES. | ||
2244 | * | ||
2245 | * @param dfa DFA for which the paths should be compressed. | ||
2246 | * @param start starting state for linear path search. | ||
2247 | * @param cur current state in the recursive DFS. | ||
2248 | * @param label current label (string of traversed labels). | ||
2249 | * @param max_len maximal path compression length. | ||
2250 | * @param transitions_head transitions DLL. | ||
2251 | * @param transitions_tail transitions DLL. | ||
2252 | */ | ||
2253 | void | ||
2254 | dfa_compress_paths_helper (struct REGEX_INTERNAL_Automaton *dfa, | ||
2255 | struct REGEX_INTERNAL_State *start, | ||
2256 | struct REGEX_INTERNAL_State *cur, char *label, | ||
2257 | unsigned int max_len, | ||
2258 | struct REGEX_INTERNAL_Transition **transitions_head, | ||
2259 | struct REGEX_INTERNAL_Transition **transitions_tail) | ||
2260 | { | ||
2261 | struct REGEX_INTERNAL_Transition *t; | ||
2262 | char *new_label; | ||
2263 | |||
2264 | |||
2265 | if (NULL != label && | ||
2266 | ((cur->incoming_transition_count > 1 || GNUNET_YES == cur->accepting || | ||
2267 | GNUNET_YES == cur->marked) || (start != dfa->start && max_len > 0 && | ||
2268 | max_len == strlen (label)) || | ||
2269 | (start == dfa->start && GNUNET_REGEX_INITIAL_BYTES == strlen (label)))) | ||
2270 | { | ||
2271 | t = GNUNET_new (struct REGEX_INTERNAL_Transition); | ||
2272 | t->label = GNUNET_strdup (label); | ||
2273 | t->to_state = cur; | ||
2274 | t->from_state = start; | ||
2275 | GNUNET_CONTAINER_DLL_insert (*transitions_head, *transitions_tail, t); | ||
2276 | |||
2277 | if (GNUNET_NO == cur->marked) | ||
2278 | { | ||
2279 | dfa_compress_paths_helper (dfa, cur, cur, NULL, max_len, transitions_head, | ||
2280 | transitions_tail); | ||
2281 | } | ||
2282 | return; | ||
2283 | } | ||
2284 | else if (cur != start) | ||
2285 | cur->contained = GNUNET_YES; | ||
2286 | |||
2287 | if (GNUNET_YES == cur->marked && cur != start) | ||
2288 | return; | ||
2289 | |||
2290 | cur->marked = GNUNET_YES; | ||
2291 | |||
2292 | |||
2293 | for (t = cur->transitions_head; NULL != t; t = t->next) | ||
2294 | { | ||
2295 | if (NULL != label) | ||
2296 | GNUNET_asprintf (&new_label, "%s%s", label, t->label); | ||
2297 | else | ||
2298 | new_label = GNUNET_strdup (t->label); | ||
2299 | |||
2300 | if (t->to_state != cur) | ||
2301 | { | ||
2302 | dfa_compress_paths_helper (dfa, start, t->to_state, new_label, max_len, | ||
2303 | transitions_head, transitions_tail); | ||
2304 | } | ||
2305 | GNUNET_free (new_label); | ||
2306 | } | ||
2307 | } | ||
2308 | |||
2309 | |||
2310 | /** | ||
2311 | * Compress paths in the given 'dfa'. Linear paths like 0->1->2->3 will be | ||
2312 | * compressed to 0->3 by combining transitions. | ||
2313 | * | ||
2314 | * @param regex_ctx context for adding new transitions. | ||
2315 | * @param dfa DFA representation, will directly modify the given DFA. | ||
2316 | * @param max_len maximal length of the compressed paths. | ||
2317 | */ | ||
2318 | static void | ||
2319 | dfa_compress_paths (struct REGEX_INTERNAL_Context *regex_ctx, | ||
2320 | struct REGEX_INTERNAL_Automaton *dfa, unsigned int max_len) | ||
2321 | { | ||
2322 | struct REGEX_INTERNAL_State *s; | ||
2323 | struct REGEX_INTERNAL_State *s_next; | ||
2324 | struct REGEX_INTERNAL_Transition *t; | ||
2325 | struct REGEX_INTERNAL_Transition *t_next; | ||
2326 | struct REGEX_INTERNAL_Transition *transitions_head = NULL; | ||
2327 | struct REGEX_INTERNAL_Transition *transitions_tail = NULL; | ||
2328 | |||
2329 | if (NULL == dfa) | ||
2330 | return; | ||
2331 | |||
2332 | /* Count the incoming transitions on each state. */ | ||
2333 | for (s = dfa->states_head; NULL != s; s = s->next) | ||
2334 | { | ||
2335 | for (t = s->transitions_head; NULL != t; t = t->next) | ||
2336 | { | ||
2337 | if (NULL != t->to_state) | ||
2338 | t->to_state->incoming_transition_count++; | ||
2339 | } | ||
2340 | } | ||
2341 | |||
2342 | /* Unmark all states. */ | ||
2343 | for (s = dfa->states_head; NULL != s; s = s->next) | ||
2344 | { | ||
2345 | s->marked = GNUNET_NO; | ||
2346 | s->contained = GNUNET_NO; | ||
2347 | } | ||
2348 | |||
2349 | /* Add strides and mark states that can be deleted. */ | ||
2350 | dfa_compress_paths_helper (dfa, dfa->start, dfa->start, NULL, max_len, | ||
2351 | &transitions_head, &transitions_tail); | ||
2352 | |||
2353 | /* Add all the new transitions to the automaton. */ | ||
2354 | for (t = transitions_head; NULL != t; t = t_next) | ||
2355 | { | ||
2356 | t_next = t->next; | ||
2357 | state_add_transition (regex_ctx, t->from_state, t->label, t->to_state); | ||
2358 | GNUNET_CONTAINER_DLL_remove (transitions_head, transitions_tail, t); | ||
2359 | GNUNET_free_non_null (t->label); | ||
2360 | GNUNET_free (t); | ||
2361 | } | ||
2362 | |||
2363 | /* Remove marked states (including their incoming and outgoing transitions). */ | ||
2364 | for (s = dfa->states_head; NULL != s; s = s_next) | ||
2365 | { | ||
2366 | s_next = s->next; | ||
2367 | if (GNUNET_YES == s->contained) | ||
2368 | automaton_remove_state (dfa, s); | ||
2369 | } | ||
2370 | } | ||
2371 | |||
2372 | |||
2373 | /** | ||
2374 | * Creates a new NFA fragment. Needs to be cleared using | ||
2375 | * automaton_fragment_clear. | ||
2376 | * | ||
2377 | * @param start starting state | ||
2378 | * @param end end state | ||
2379 | * | ||
2380 | * @return new NFA fragment | ||
2381 | */ | ||
2382 | static struct REGEX_INTERNAL_Automaton * | ||
2383 | nfa_fragment_create (struct REGEX_INTERNAL_State *start, | ||
2384 | struct REGEX_INTERNAL_State *end) | ||
2385 | { | ||
2386 | struct REGEX_INTERNAL_Automaton *n; | ||
2387 | |||
2388 | n = GNUNET_new (struct REGEX_INTERNAL_Automaton); | ||
2389 | |||
2390 | n->type = NFA; | ||
2391 | n->start = NULL; | ||
2392 | n->end = NULL; | ||
2393 | n->state_count = 0; | ||
2394 | |||
2395 | if (NULL == start || NULL == end) | ||
2396 | return n; | ||
2397 | |||
2398 | automaton_add_state (n, end); | ||
2399 | automaton_add_state (n, start); | ||
2400 | |||
2401 | n->state_count = 2; | ||
2402 | |||
2403 | n->start = start; | ||
2404 | n->end = end; | ||
2405 | |||
2406 | return n; | ||
2407 | } | ||
2408 | |||
2409 | |||
2410 | /** | ||
2411 | * Adds a list of states to the given automaton 'n'. | ||
2412 | * | ||
2413 | * @param n automaton to which the states should be added | ||
2414 | * @param states_head head of the DLL of states | ||
2415 | * @param states_tail tail of the DLL of states | ||
2416 | */ | ||
2417 | static void | ||
2418 | nfa_add_states (struct REGEX_INTERNAL_Automaton *n, | ||
2419 | struct REGEX_INTERNAL_State *states_head, | ||
2420 | struct REGEX_INTERNAL_State *states_tail) | ||
2421 | { | ||
2422 | struct REGEX_INTERNAL_State *s; | ||
2423 | |||
2424 | if (NULL == n || NULL == states_head) | ||
2425 | { | ||
2426 | GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Could not add states\n"); | ||
2427 | return; | ||
2428 | } | ||
2429 | |||
2430 | if (NULL == n->states_head) | ||
2431 | { | ||
2432 | n->states_head = states_head; | ||
2433 | n->states_tail = states_tail; | ||
2434 | return; | ||
2435 | } | ||
2436 | |||
2437 | if (NULL != states_head) | ||
2438 | { | ||
2439 | n->states_tail->next = states_head; | ||
2440 | n->states_tail = states_tail; | ||
2441 | } | ||
2442 | |||
2443 | for (s = states_head; NULL != s; s = s->next) | ||
2444 | n->state_count++; | ||
2445 | } | ||
2446 | |||
2447 | |||
2448 | /** | ||
2449 | * Creates a new NFA state. Needs to be freed using automaton_destroy_state. | ||
2450 | * | ||
2451 | * @param ctx context | ||
2452 | * @param accepting is it an accepting state or not | ||
2453 | * | ||
2454 | * @return new NFA state | ||
2455 | */ | ||
2456 | static struct REGEX_INTERNAL_State * | ||
2457 | nfa_state_create (struct REGEX_INTERNAL_Context *ctx, int accepting) | ||
2458 | { | ||
2459 | struct REGEX_INTERNAL_State *s; | ||
2460 | |||
2461 | s = GNUNET_new (struct REGEX_INTERNAL_State); | ||
2462 | s->id = ctx->state_id++; | ||
2463 | s->accepting = accepting; | ||
2464 | s->marked = GNUNET_NO; | ||
2465 | s->contained = 0; | ||
2466 | s->index = -1; | ||
2467 | s->lowlink = -1; | ||
2468 | s->scc_id = 0; | ||
2469 | s->name = NULL; | ||
2470 | GNUNET_asprintf (&s->name, "s%i", s->id); | ||
2471 | |||
2472 | return s; | ||
2473 | } | ||
2474 | |||
2475 | |||
2476 | /** | ||
2477 | * Calculates the closure set for the given set of states. | ||
2478 | * | ||
2479 | * @param ret set to sorted nfa closure on 'label' (epsilon closure if 'label' is NULL) | ||
2480 | * @param nfa the NFA containing 's' | ||
2481 | * @param states list of states on which to base the closure on | ||
2482 | * @param label transitioning label for which to base the closure on, | ||
2483 | * pass NULL for epsilon transition | ||
2484 | */ | ||
2485 | static void | ||
2486 | nfa_closure_set_create (struct REGEX_INTERNAL_StateSet *ret, | ||
2487 | struct REGEX_INTERNAL_Automaton *nfa, | ||
2488 | struct REGEX_INTERNAL_StateSet *states, const char *label) | ||
2489 | { | ||
2490 | struct REGEX_INTERNAL_State *s; | ||
2491 | unsigned int i; | ||
2492 | struct REGEX_INTERNAL_StateSet_MDLL cls_stack; | ||
2493 | struct REGEX_INTERNAL_State *clsstate; | ||
2494 | struct REGEX_INTERNAL_State *currentstate; | ||
2495 | struct REGEX_INTERNAL_Transition *ctran; | ||
2496 | |||
2497 | memset (ret, 0, sizeof (struct REGEX_INTERNAL_StateSet)); | ||
2498 | if (NULL == states) | ||
2499 | return; | ||
2500 | |||
2501 | for (i = 0; i < states->off; i++) | ||
2502 | { | ||
2503 | s = states->states[i]; | ||
2504 | |||
2505 | /* Add start state to closure only for epsilon closure */ | ||
2506 | if (NULL == label) | ||
2507 | state_set_append (ret, s); | ||
2508 | |||
2509 | /* initialize work stack */ | ||
2510 | cls_stack.head = NULL; | ||
2511 | cls_stack.tail = NULL; | ||
2512 | GNUNET_CONTAINER_MDLL_insert (ST, cls_stack.head, cls_stack.tail, s); | ||
2513 | cls_stack.len = 1; | ||
2514 | |||
2515 | while (NULL != (currentstate = cls_stack.tail)) | ||
2516 | { | ||
2517 | GNUNET_CONTAINER_MDLL_remove (ST, cls_stack.head, cls_stack.tail, | ||
2518 | currentstate); | ||
2519 | cls_stack.len--; | ||
2520 | for (ctran = currentstate->transitions_head; NULL != ctran; | ||
2521 | ctran = ctran->next) | ||
2522 | { | ||
2523 | if (NULL == (clsstate = ctran->to_state)) | ||
2524 | continue; | ||
2525 | if (0 != clsstate->contained) | ||
2526 | continue; | ||
2527 | if (0 != nullstrcmp (label, ctran->label)) | ||
2528 | continue; | ||
2529 | state_set_append (ret, clsstate); | ||
2530 | GNUNET_CONTAINER_MDLL_insert_tail (ST, cls_stack.head, cls_stack.tail, | ||
2531 | clsstate); | ||
2532 | cls_stack.len++; | ||
2533 | clsstate->contained = 1; | ||
2534 | } | ||
2535 | } | ||
2536 | } | ||
2537 | for (i = 0; i < ret->off; i++) | ||
2538 | ret->states[i]->contained = 0; | ||
2539 | |||
2540 | if (ret->off > 1) | ||
2541 | qsort (ret->states, ret->off, sizeof (struct REGEX_INTERNAL_State *), | ||
2542 | &state_compare); | ||
2543 | } | ||
2544 | |||
2545 | |||
2546 | /** | ||
2547 | * Pops two NFA fragments (a, b) from the stack and concatenates them (ab) | ||
2548 | * | ||
2549 | * @param ctx context | ||
2550 | */ | ||
2551 | static void | ||
2552 | nfa_add_concatenation (struct REGEX_INTERNAL_Context *ctx) | ||
2553 | { | ||
2554 | struct REGEX_INTERNAL_Automaton *a; | ||
2555 | struct REGEX_INTERNAL_Automaton *b; | ||
2556 | struct REGEX_INTERNAL_Automaton *new_nfa; | ||
2557 | |||
2558 | b = ctx->stack_tail; | ||
2559 | GNUNET_assert (NULL != b); | ||
2560 | GNUNET_CONTAINER_DLL_remove (ctx->stack_head, ctx->stack_tail, b); | ||
2561 | a = ctx->stack_tail; | ||
2562 | GNUNET_assert (NULL != a); | ||
2563 | GNUNET_CONTAINER_DLL_remove (ctx->stack_head, ctx->stack_tail, a); | ||
2564 | |||
2565 | state_add_transition (ctx, a->end, NULL, b->start); | ||
2566 | a->end->accepting = 0; | ||
2567 | b->end->accepting = 1; | ||
2568 | |||
2569 | new_nfa = nfa_fragment_create (NULL, NULL); | ||
2570 | nfa_add_states (new_nfa, a->states_head, a->states_tail); | ||
2571 | nfa_add_states (new_nfa, b->states_head, b->states_tail); | ||
2572 | new_nfa->start = a->start; | ||
2573 | new_nfa->end = b->end; | ||
2574 | new_nfa->state_count += a->state_count + b->state_count; | ||
2575 | automaton_fragment_clear (a); | ||
2576 | automaton_fragment_clear (b); | ||
2577 | |||
2578 | GNUNET_CONTAINER_DLL_insert_tail (ctx->stack_head, ctx->stack_tail, new_nfa); | ||
2579 | } | ||
2580 | |||
2581 | |||
2582 | /** | ||
2583 | * Pops a NFA fragment from the stack (a) and adds a new fragment (a*) | ||
2584 | * | ||
2585 | * @param ctx context | ||
2586 | */ | ||
2587 | static void | ||
2588 | nfa_add_star_op (struct REGEX_INTERNAL_Context *ctx) | ||
2589 | { | ||
2590 | struct REGEX_INTERNAL_Automaton *a; | ||
2591 | struct REGEX_INTERNAL_Automaton *new_nfa; | ||
2592 | struct REGEX_INTERNAL_State *start; | ||
2593 | struct REGEX_INTERNAL_State *end; | ||
2594 | |||
2595 | a = ctx->stack_tail; | ||
2596 | |||
2597 | if (NULL == a) | ||
2598 | { | ||
2599 | GNUNET_log (GNUNET_ERROR_TYPE_ERROR, | ||
2600 | "nfa_add_star_op failed, because there was no element on the stack"); | ||
2601 | return; | ||
2602 | } | ||
2603 | |||
2604 | GNUNET_CONTAINER_DLL_remove (ctx->stack_head, ctx->stack_tail, a); | ||
2605 | |||
2606 | start = nfa_state_create (ctx, 0); | ||
2607 | end = nfa_state_create (ctx, 1); | ||
2608 | |||
2609 | state_add_transition (ctx, start, NULL, a->start); | ||
2610 | state_add_transition (ctx, start, NULL, end); | ||
2611 | state_add_transition (ctx, a->end, NULL, a->start); | ||
2612 | state_add_transition (ctx, a->end, NULL, end); | ||
2613 | |||
2614 | a->end->accepting = 0; | ||
2615 | end->accepting = 1; | ||
2616 | |||
2617 | new_nfa = nfa_fragment_create (start, end); | ||
2618 | nfa_add_states (new_nfa, a->states_head, a->states_tail); | ||
2619 | automaton_fragment_clear (a); | ||
2620 | |||
2621 | GNUNET_CONTAINER_DLL_insert_tail (ctx->stack_head, ctx->stack_tail, new_nfa); | ||
2622 | } | ||
2623 | |||
2624 | |||
2625 | /** | ||
2626 | * Pops an NFA fragment (a) from the stack and adds a new fragment (a+) | ||
2627 | * | ||
2628 | * @param ctx context | ||
2629 | */ | ||
2630 | static void | ||
2631 | nfa_add_plus_op (struct REGEX_INTERNAL_Context *ctx) | ||
2632 | { | ||
2633 | struct REGEX_INTERNAL_Automaton *a; | ||
2634 | |||
2635 | a = ctx->stack_tail; | ||
2636 | |||
2637 | if (NULL == a) | ||
2638 | { | ||
2639 | GNUNET_log (GNUNET_ERROR_TYPE_ERROR, | ||
2640 | "nfa_add_plus_op failed, because there was no element on the stack"); | ||
2641 | return; | ||
2642 | } | ||
2643 | |||
2644 | GNUNET_CONTAINER_DLL_remove (ctx->stack_head, ctx->stack_tail, a); | ||
2645 | |||
2646 | state_add_transition (ctx, a->end, NULL, a->start); | ||
2647 | |||
2648 | GNUNET_CONTAINER_DLL_insert_tail (ctx->stack_head, ctx->stack_tail, a); | ||
2649 | } | ||
2650 | |||
2651 | |||
2652 | /** | ||
2653 | * Pops an NFA fragment (a) from the stack and adds a new fragment (a?) | ||
2654 | * | ||
2655 | * @param ctx context | ||
2656 | */ | ||
2657 | static void | ||
2658 | nfa_add_question_op (struct REGEX_INTERNAL_Context *ctx) | ||
2659 | { | ||
2660 | struct REGEX_INTERNAL_Automaton *a; | ||
2661 | struct REGEX_INTERNAL_Automaton *new_nfa; | ||
2662 | struct REGEX_INTERNAL_State *start; | ||
2663 | struct REGEX_INTERNAL_State *end; | ||
2664 | |||
2665 | a = ctx->stack_tail; | ||
2666 | if (NULL == a) | ||
2667 | { | ||
2668 | GNUNET_log (GNUNET_ERROR_TYPE_ERROR, | ||
2669 | "nfa_add_question_op failed, because there was no element on the stack"); | ||
2670 | return; | ||
2671 | } | ||
2672 | |||
2673 | GNUNET_CONTAINER_DLL_remove (ctx->stack_head, ctx->stack_tail, a); | ||
2674 | |||
2675 | start = nfa_state_create (ctx, 0); | ||
2676 | end = nfa_state_create (ctx, 1); | ||
2677 | |||
2678 | state_add_transition (ctx, start, NULL, a->start); | ||
2679 | state_add_transition (ctx, start, NULL, end); | ||
2680 | state_add_transition (ctx, a->end, NULL, end); | ||
2681 | |||
2682 | a->end->accepting = 0; | ||
2683 | |||
2684 | new_nfa = nfa_fragment_create (start, end); | ||
2685 | nfa_add_states (new_nfa, a->states_head, a->states_tail); | ||
2686 | GNUNET_CONTAINER_DLL_insert_tail (ctx->stack_head, ctx->stack_tail, new_nfa); | ||
2687 | automaton_fragment_clear (a); | ||
2688 | } | ||
2689 | |||
2690 | |||
2691 | /** | ||
2692 | * Pops two NFA fragments (a, b) from the stack and adds a new NFA fragment that | ||
2693 | * alternates between a and b (a|b) | ||
2694 | * | ||
2695 | * @param ctx context | ||
2696 | */ | ||
2697 | static void | ||
2698 | nfa_add_alternation (struct REGEX_INTERNAL_Context *ctx) | ||
2699 | { | ||
2700 | struct REGEX_INTERNAL_Automaton *a; | ||
2701 | struct REGEX_INTERNAL_Automaton *b; | ||
2702 | struct REGEX_INTERNAL_Automaton *new_nfa; | ||
2703 | struct REGEX_INTERNAL_State *start; | ||
2704 | struct REGEX_INTERNAL_State *end; | ||
2705 | |||
2706 | b = ctx->stack_tail; | ||
2707 | GNUNET_assert (NULL != b); | ||
2708 | GNUNET_CONTAINER_DLL_remove (ctx->stack_head, ctx->stack_tail, b); | ||
2709 | a = ctx->stack_tail; | ||
2710 | GNUNET_assert (NULL != a); | ||
2711 | GNUNET_CONTAINER_DLL_remove (ctx->stack_head, ctx->stack_tail, a); | ||
2712 | |||
2713 | start = nfa_state_create (ctx, 0); | ||
2714 | end = nfa_state_create (ctx, 1); | ||
2715 | state_add_transition (ctx, start, NULL, a->start); | ||
2716 | state_add_transition (ctx, start, NULL, b->start); | ||
2717 | |||
2718 | state_add_transition (ctx, a->end, NULL, end); | ||
2719 | state_add_transition (ctx, b->end, NULL, end); | ||
2720 | |||
2721 | a->end->accepting = 0; | ||
2722 | b->end->accepting = 0; | ||
2723 | end->accepting = 1; | ||
2724 | |||
2725 | new_nfa = nfa_fragment_create (start, end); | ||
2726 | nfa_add_states (new_nfa, a->states_head, a->states_tail); | ||
2727 | nfa_add_states (new_nfa, b->states_head, b->states_tail); | ||
2728 | automaton_fragment_clear (a); | ||
2729 | automaton_fragment_clear (b); | ||
2730 | |||
2731 | GNUNET_CONTAINER_DLL_insert_tail (ctx->stack_head, ctx->stack_tail, new_nfa); | ||
2732 | } | ||
2733 | |||
2734 | |||
2735 | /** | ||
2736 | * Adds a new nfa fragment to the stack | ||
2737 | * | ||
2738 | * @param ctx context | ||
2739 | * @param label label for nfa transition | ||
2740 | */ | ||
2741 | static void | ||
2742 | nfa_add_label (struct REGEX_INTERNAL_Context *ctx, const char *label) | ||
2743 | { | ||
2744 | struct REGEX_INTERNAL_Automaton *n; | ||
2745 | struct REGEX_INTERNAL_State *start; | ||
2746 | struct REGEX_INTERNAL_State *end; | ||
2747 | |||
2748 | GNUNET_assert (NULL != ctx); | ||
2749 | |||
2750 | start = nfa_state_create (ctx, 0); | ||
2751 | end = nfa_state_create (ctx, 1); | ||
2752 | state_add_transition (ctx, start, label, end); | ||
2753 | n = nfa_fragment_create (start, end); | ||
2754 | GNUNET_assert (NULL != n); | ||
2755 | GNUNET_CONTAINER_DLL_insert_tail (ctx->stack_head, ctx->stack_tail, n); | ||
2756 | } | ||
2757 | |||
2758 | |||
2759 | /** | ||
2760 | * Initialize a new context | ||
2761 | * | ||
2762 | * @param ctx context | ||
2763 | */ | ||
2764 | static void | ||
2765 | REGEX_INTERNAL_context_init (struct REGEX_INTERNAL_Context *ctx) | ||
2766 | { | ||
2767 | if (NULL == ctx) | ||
2768 | { | ||
2769 | GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Context was NULL!"); | ||
2770 | return; | ||
2771 | } | ||
2772 | ctx->state_id = 0; | ||
2773 | ctx->transition_id = 0; | ||
2774 | ctx->stack_head = NULL; | ||
2775 | ctx->stack_tail = NULL; | ||
2776 | } | ||
2777 | |||
2778 | |||
2779 | /** | ||
2780 | * Construct an NFA by parsing the regex string of length 'len'. | ||
2781 | * | ||
2782 | * @param regex regular expression string | ||
2783 | * @param len length of the string | ||
2784 | * | ||
2785 | * @return NFA, needs to be freed using REGEX_INTERNAL_destroy_automaton | ||
2786 | */ | ||
2787 | struct REGEX_INTERNAL_Automaton * | ||
2788 | REGEX_INTERNAL_construct_nfa (const char *regex, const size_t len) | ||
2789 | { | ||
2790 | struct REGEX_INTERNAL_Context ctx; | ||
2791 | struct REGEX_INTERNAL_Automaton *nfa; | ||
2792 | const char *regexp; | ||
2793 | char curlabel[2]; | ||
2794 | char *error_msg; | ||
2795 | unsigned int count; | ||
2796 | unsigned int altcount; | ||
2797 | unsigned int atomcount; | ||
2798 | unsigned int poff; | ||
2799 | unsigned int psize; | ||
2800 | struct | ||
2801 | { | ||
2802 | int altcount; | ||
2803 | int atomcount; | ||
2804 | } *p; | ||
2805 | |||
2806 | if (NULL == regex || 0 == strlen (regex) || 0 == len) | ||
2807 | { | ||
2808 | GNUNET_log (GNUNET_ERROR_TYPE_ERROR, | ||
2809 | "Could not parse regex. Empty regex string provided.\n"); | ||
2810 | |||
2811 | return NULL; | ||
2812 | } | ||
2813 | REGEX_INTERNAL_context_init (&ctx); | ||
2814 | |||
2815 | regexp = regex; | ||
2816 | curlabel[1] = '\0'; | ||
2817 | p = NULL; | ||
2818 | error_msg = NULL; | ||
2819 | altcount = 0; | ||
2820 | atomcount = 0; | ||
2821 | poff = 0; | ||
2822 | psize = 0; | ||
2823 | |||
2824 | for (count = 0; count < len && *regexp; count++, regexp++) | ||
2825 | { | ||
2826 | switch (*regexp) | ||
2827 | { | ||
2828 | case '(': | ||
2829 | if (atomcount > 1) | ||
2830 | { | ||
2831 | --atomcount; | ||
2832 | nfa_add_concatenation (&ctx); | ||
2833 | } | ||
2834 | if (poff == psize) | ||
2835 | GNUNET_array_grow (p, psize, psize * 2 + 4); /* FIXME why *2 +4? */ | ||
2836 | p[poff].altcount = altcount; | ||
2837 | p[poff].atomcount = atomcount; | ||
2838 | poff++; | ||
2839 | altcount = 0; | ||
2840 | atomcount = 0; | ||
2841 | break; | ||
2842 | case '|': | ||
2843 | if (0 == atomcount) | ||
2844 | { | ||
2845 | error_msg = "Cannot append '|' to nothing"; | ||
2846 | goto error; | ||
2847 | } | ||
2848 | while (--atomcount > 0) | ||
2849 | nfa_add_concatenation (&ctx); | ||
2850 | altcount++; | ||
2851 | break; | ||
2852 | case ')': | ||
2853 | if (0 == poff) | ||
2854 | { | ||
2855 | error_msg = "Missing opening '('"; | ||
2856 | goto error; | ||
2857 | } | ||
2858 | if (0 == atomcount) | ||
2859 | { | ||
2860 | /* Ignore this: "()" */ | ||
2861 | poff--; | ||
2862 | altcount = p[poff].altcount; | ||
2863 | atomcount = p[poff].atomcount; | ||
2864 | break; | ||
2865 | } | ||
2866 | while (--atomcount > 0) | ||
2867 | nfa_add_concatenation (&ctx); | ||
2868 | for (; altcount > 0; altcount--) | ||
2869 | nfa_add_alternation (&ctx); | ||
2870 | poff--; | ||
2871 | altcount = p[poff].altcount; | ||
2872 | atomcount = p[poff].atomcount; | ||
2873 | atomcount++; | ||
2874 | break; | ||
2875 | case '*': | ||
2876 | if (atomcount == 0) | ||
2877 | { | ||
2878 | error_msg = "Cannot append '*' to nothing"; | ||
2879 | goto error; | ||
2880 | } | ||
2881 | nfa_add_star_op (&ctx); | ||
2882 | break; | ||
2883 | case '+': | ||
2884 | if (atomcount == 0) | ||
2885 | { | ||
2886 | error_msg = "Cannot append '+' to nothing"; | ||
2887 | goto error; | ||
2888 | } | ||
2889 | nfa_add_plus_op (&ctx); | ||
2890 | break; | ||
2891 | case '?': | ||
2892 | if (atomcount == 0) | ||
2893 | { | ||
2894 | error_msg = "Cannot append '?' to nothing"; | ||
2895 | goto error; | ||
2896 | } | ||
2897 | nfa_add_question_op (&ctx); | ||
2898 | break; | ||
2899 | default: | ||
2900 | if (atomcount > 1) | ||
2901 | { | ||
2902 | --atomcount; | ||
2903 | nfa_add_concatenation (&ctx); | ||
2904 | } | ||
2905 | curlabel[0] = *regexp; | ||
2906 | nfa_add_label (&ctx, curlabel); | ||
2907 | atomcount++; | ||
2908 | break; | ||
2909 | } | ||
2910 | } | ||
2911 | if (0 != poff) | ||
2912 | { | ||
2913 | error_msg = "Unbalanced parenthesis"; | ||
2914 | goto error; | ||
2915 | } | ||
2916 | while (--atomcount > 0) | ||
2917 | nfa_add_concatenation (&ctx); | ||
2918 | for (; altcount > 0; altcount--) | ||
2919 | nfa_add_alternation (&ctx); | ||
2920 | |||
2921 | GNUNET_array_grow (p, psize, 0); | ||
2922 | |||
2923 | nfa = ctx.stack_tail; | ||
2924 | GNUNET_CONTAINER_DLL_remove (ctx.stack_head, ctx.stack_tail, nfa); | ||
2925 | |||
2926 | if (NULL != ctx.stack_head) | ||
2927 | { | ||
2928 | error_msg = "Creating the NFA failed. NFA stack was not empty!"; | ||
2929 | goto error; | ||
2930 | } | ||
2931 | |||
2932 | /* Remember the regex that was used to generate this NFA */ | ||
2933 | nfa->regex = GNUNET_strdup (regex); | ||
2934 | |||
2935 | /* create depth-first numbering of the states for pretty printing */ | ||
2936 | REGEX_INTERNAL_automaton_traverse (nfa, NULL, NULL, NULL, &number_states, NULL); | ||
2937 | |||
2938 | /* No multistriding added so far */ | ||
2939 | nfa->is_multistrided = GNUNET_NO; | ||
2940 | |||
2941 | return nfa; | ||
2942 | |||
2943 | error: | ||
2944 | GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Could not parse regex: `%s'\n", regex); | ||
2945 | if (NULL != error_msg) | ||
2946 | GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "%s\n", error_msg); | ||
2947 | |||
2948 | GNUNET_free_non_null (p); | ||
2949 | |||
2950 | while (NULL != (nfa = ctx.stack_head)) | ||
2951 | { | ||
2952 | GNUNET_CONTAINER_DLL_remove (ctx.stack_head, ctx.stack_tail, nfa); | ||
2953 | REGEX_INTERNAL_automaton_destroy (nfa); | ||
2954 | } | ||
2955 | |||
2956 | return NULL; | ||
2957 | } | ||
2958 | |||
2959 | |||
2960 | /** | ||
2961 | * Create DFA states based on given 'nfa' and starting with 'dfa_state'. | ||
2962 | * | ||
2963 | * @param ctx context. | ||
2964 | * @param nfa NFA automaton. | ||
2965 | * @param dfa DFA automaton. | ||
2966 | * @param dfa_state current dfa state, pass epsilon closure of first nfa state | ||
2967 | * for starting. | ||
2968 | */ | ||
2969 | static void | ||
2970 | construct_dfa_states (struct REGEX_INTERNAL_Context *ctx, | ||
2971 | struct REGEX_INTERNAL_Automaton *nfa, | ||
2972 | struct REGEX_INTERNAL_Automaton *dfa, | ||
2973 | struct REGEX_INTERNAL_State *dfa_state) | ||
2974 | { | ||
2975 | struct REGEX_INTERNAL_Transition *ctran; | ||
2976 | struct REGEX_INTERNAL_State *new_dfa_state; | ||
2977 | struct REGEX_INTERNAL_State *state_contains; | ||
2978 | struct REGEX_INTERNAL_State *state_iter; | ||
2979 | struct REGEX_INTERNAL_StateSet tmp; | ||
2980 | struct REGEX_INTERNAL_StateSet nfa_set; | ||
2981 | |||
2982 | for (ctran = dfa_state->transitions_head; NULL != ctran; ctran = ctran->next) | ||
2983 | { | ||
2984 | if (NULL == ctran->label || NULL != ctran->to_state) | ||
2985 | continue; | ||
2986 | |||
2987 | nfa_closure_set_create (&tmp, nfa, &dfa_state->nfa_set, ctran->label); | ||
2988 | nfa_closure_set_create (&nfa_set, nfa, &tmp, NULL); | ||
2989 | state_set_clear (&tmp); | ||
2990 | |||
2991 | state_contains = NULL; | ||
2992 | for (state_iter = dfa->states_head; NULL != state_iter; | ||
2993 | state_iter = state_iter->next) | ||
2994 | { | ||
2995 | if (0 == state_set_compare (&state_iter->nfa_set, &nfa_set)) | ||
2996 | { | ||
2997 | state_contains = state_iter; | ||
2998 | break; | ||
2999 | } | ||
3000 | } | ||
3001 | if (NULL == state_contains) | ||
3002 | { | ||
3003 | new_dfa_state = dfa_state_create (ctx, &nfa_set); | ||
3004 | automaton_add_state (dfa, new_dfa_state); | ||
3005 | ctran->to_state = new_dfa_state; | ||
3006 | construct_dfa_states (ctx, nfa, dfa, new_dfa_state); | ||
3007 | } | ||
3008 | else | ||
3009 | { | ||
3010 | ctran->to_state = state_contains; | ||
3011 | state_set_clear (&nfa_set); | ||
3012 | } | ||
3013 | } | ||
3014 | } | ||
3015 | |||
3016 | |||
3017 | /** | ||
3018 | * Construct DFA for the given 'regex' of length 'len'. | ||
3019 | * | ||
3020 | * Path compression means, that for example a DFA o -> a -> b -> c -> o will be | ||
3021 | * compressed to o -> abc -> o. Note that this parameter influences the | ||
3022 | * non-determinism of states of the resulting NFA in the DHT (number of outgoing | ||
3023 | * edges with the same label). For example for an application that stores IPv4 | ||
3024 | * addresses as bitstrings it could make sense to limit the path compression to | ||
3025 | * 4 or 8. | ||
3026 | * | ||
3027 | * @param regex regular expression string. | ||
3028 | * @param len length of the regular expression. | ||
3029 | * @param max_path_len limit the path compression length to the | ||
3030 | * given value. If set to 1, no path compression is applied. Set to 0 for | ||
3031 | * maximal possible path compression (generally not desireable). | ||
3032 | * @return DFA, needs to be freed using REGEX_INTERNAL_automaton_destroy. | ||
3033 | */ | ||
3034 | struct REGEX_INTERNAL_Automaton * | ||
3035 | REGEX_INTERNAL_construct_dfa (const char *regex, const size_t len, | ||
3036 | unsigned int max_path_len) | ||
3037 | { | ||
3038 | struct REGEX_INTERNAL_Context ctx; | ||
3039 | struct REGEX_INTERNAL_Automaton *dfa; | ||
3040 | struct REGEX_INTERNAL_Automaton *nfa; | ||
3041 | struct REGEX_INTERNAL_StateSet nfa_start_eps_cls; | ||
3042 | struct REGEX_INTERNAL_StateSet singleton_set; | ||
3043 | |||
3044 | REGEX_INTERNAL_context_init (&ctx); | ||
3045 | |||
3046 | /* Create NFA */ | ||
3047 | nfa = REGEX_INTERNAL_construct_nfa (regex, len); | ||
3048 | |||
3049 | if (NULL == nfa) | ||
3050 | { | ||
3051 | GNUNET_log (GNUNET_ERROR_TYPE_ERROR, | ||
3052 | "Could not create DFA, because NFA creation failed\n"); | ||
3053 | return NULL; | ||
3054 | } | ||
3055 | |||
3056 | dfa = GNUNET_new (struct REGEX_INTERNAL_Automaton); | ||
3057 | dfa->type = DFA; | ||
3058 | dfa->regex = GNUNET_strdup (regex); | ||
3059 | |||
3060 | /* Create DFA start state from epsilon closure */ | ||
3061 | memset (&singleton_set, 0, sizeof (struct REGEX_INTERNAL_StateSet)); | ||
3062 | state_set_append (&singleton_set, nfa->start); | ||
3063 | nfa_closure_set_create (&nfa_start_eps_cls, nfa, &singleton_set, NULL); | ||
3064 | state_set_clear (&singleton_set); | ||
3065 | dfa->start = dfa_state_create (&ctx, &nfa_start_eps_cls); | ||
3066 | automaton_add_state (dfa, dfa->start); | ||
3067 | |||
3068 | construct_dfa_states (&ctx, nfa, dfa, dfa->start); | ||
3069 | REGEX_INTERNAL_automaton_destroy (nfa); | ||
3070 | |||
3071 | /* Minimize DFA */ | ||
3072 | if (GNUNET_OK != dfa_minimize (&ctx, dfa)) | ||
3073 | { | ||
3074 | REGEX_INTERNAL_automaton_destroy (dfa); | ||
3075 | return NULL; | ||
3076 | } | ||
3077 | |||
3078 | /* Create proofs and hashes for all states */ | ||
3079 | if (GNUNET_OK != automaton_create_proofs (dfa)) | ||
3080 | { | ||
3081 | REGEX_INTERNAL_automaton_destroy (dfa); | ||
3082 | return NULL; | ||
3083 | } | ||
3084 | |||
3085 | /* Compress linear DFA paths */ | ||
3086 | if (1 != max_path_len) | ||
3087 | dfa_compress_paths (&ctx, dfa, max_path_len); | ||
3088 | |||
3089 | return dfa; | ||
3090 | } | ||
3091 | |||
3092 | |||
3093 | /** | ||
3094 | * Free the memory allocated by constructing the REGEX_INTERNAL_Automaton data | ||
3095 | * structure. | ||
3096 | * | ||
3097 | * @param a automaton to be destroyed | ||
3098 | */ | ||
3099 | void | ||
3100 | REGEX_INTERNAL_automaton_destroy (struct REGEX_INTERNAL_Automaton *a) | ||
3101 | { | ||
3102 | struct REGEX_INTERNAL_State *s; | ||
3103 | struct REGEX_INTERNAL_State *next_state; | ||
3104 | |||
3105 | if (NULL == a) | ||
3106 | return; | ||
3107 | |||
3108 | GNUNET_free_non_null (a->regex); | ||
3109 | GNUNET_free_non_null (a->canonical_regex); | ||
3110 | |||
3111 | for (s = a->states_head; NULL != s; s = next_state) | ||
3112 | { | ||
3113 | next_state = s->next; | ||
3114 | GNUNET_CONTAINER_DLL_remove (a->states_head, a->states_tail, s); | ||
3115 | automaton_destroy_state (s); | ||
3116 | } | ||
3117 | |||
3118 | GNUNET_free (a); | ||
3119 | } | ||
3120 | |||
3121 | |||
3122 | /** | ||
3123 | * Evaluates the given string using the given DFA automaton | ||
3124 | * | ||
3125 | * @param a automaton, type must be DFA | ||
3126 | * @param string string that should be evaluated | ||
3127 | * | ||
3128 | * @return 0 if string matches, non-0 otherwise | ||
3129 | */ | ||
3130 | static int | ||
3131 | evaluate_dfa (struct REGEX_INTERNAL_Automaton *a, | ||
3132 | const char *string) | ||
3133 | { | ||
3134 | const char *strp; | ||
3135 | struct REGEX_INTERNAL_State *s; | ||
3136 | unsigned int step_len; | ||
3137 | |||
3138 | if (DFA != a->type) | ||
3139 | { | ||
3140 | GNUNET_log (GNUNET_ERROR_TYPE_ERROR, | ||
3141 | "Tried to evaluate DFA, but NFA automaton given"); | ||
3142 | return -1; | ||
3143 | } | ||
3144 | |||
3145 | s = a->start; | ||
3146 | |||
3147 | /* If the string is empty but the starting state is accepting, we accept. */ | ||
3148 | if ((NULL == string || 0 == strlen (string)) && s->accepting) | ||
3149 | return 0; | ||
3150 | |||
3151 | for (strp = string; NULL != strp && *strp; strp += step_len) | ||
3152 | { | ||
3153 | step_len = dfa_move (&s, strp); | ||
3154 | |||
3155 | if (NULL == s) | ||
3156 | break; | ||
3157 | } | ||
3158 | |||
3159 | if (NULL != s && s->accepting) | ||
3160 | return 0; | ||
3161 | |||
3162 | return 1; | ||
3163 | } | ||
3164 | |||
3165 | |||
3166 | /** | ||
3167 | * Evaluates the given string using the given NFA automaton | ||
3168 | * | ||
3169 | * @param a automaton, type must be NFA | ||
3170 | * @param string string that should be evaluated | ||
3171 | * @return 0 if string matches, non-0 otherwise | ||
3172 | */ | ||
3173 | static int | ||
3174 | evaluate_nfa (struct REGEX_INTERNAL_Automaton *a, | ||
3175 | const char *string) | ||
3176 | { | ||
3177 | const char *strp; | ||
3178 | char str[2]; | ||
3179 | struct REGEX_INTERNAL_State *s; | ||
3180 | struct REGEX_INTERNAL_StateSet sset; | ||
3181 | struct REGEX_INTERNAL_StateSet new_sset; | ||
3182 | struct REGEX_INTERNAL_StateSet singleton_set; | ||
3183 | unsigned int i; | ||
3184 | int result; | ||
3185 | |||
3186 | if (NFA != a->type) | ||
3187 | { | ||
3188 | GNUNET_log (GNUNET_ERROR_TYPE_ERROR, | ||
3189 | "Tried to evaluate NFA, but DFA automaton given"); | ||
3190 | return -1; | ||
3191 | } | ||
3192 | |||
3193 | /* If the string is empty but the starting state is accepting, we accept. */ | ||
3194 | if ((NULL == string || 0 == strlen (string)) && a->start->accepting) | ||
3195 | return 0; | ||
3196 | |||
3197 | result = 1; | ||
3198 | memset (&singleton_set, 0, sizeof (struct REGEX_INTERNAL_StateSet)); | ||
3199 | state_set_append (&singleton_set, a->start); | ||
3200 | nfa_closure_set_create (&sset, a, &singleton_set, NULL); | ||
3201 | state_set_clear (&singleton_set); | ||
3202 | |||
3203 | str[1] = '\0'; | ||
3204 | for (strp = string; NULL != strp && *strp; strp++) | ||
3205 | { | ||
3206 | str[0] = *strp; | ||
3207 | nfa_closure_set_create (&new_sset, a, &sset, str); | ||
3208 | state_set_clear (&sset); | ||
3209 | nfa_closure_set_create (&sset, a, &new_sset, 0); | ||
3210 | state_set_clear (&new_sset); | ||
3211 | } | ||
3212 | |||
3213 | for (i = 0; i < sset.off; i++) | ||
3214 | { | ||
3215 | s = sset.states[i]; | ||
3216 | if ( (NULL != s) && (s->accepting) ) | ||
3217 | { | ||
3218 | result = 0; | ||
3219 | break; | ||
3220 | } | ||
3221 | } | ||
3222 | |||
3223 | state_set_clear (&sset); | ||
3224 | return result; | ||
3225 | } | ||
3226 | |||
3227 | |||
3228 | /** | ||
3229 | * Evaluates the given @a string against the given compiled regex @a a | ||
3230 | * | ||
3231 | * @param a automaton | ||
3232 | * @param string string to check | ||
3233 | * @return 0 if string matches, non-0 otherwise | ||
3234 | */ | ||
3235 | int | ||
3236 | REGEX_INTERNAL_eval (struct REGEX_INTERNAL_Automaton *a, | ||
3237 | const char *string) | ||
3238 | { | ||
3239 | int result; | ||
3240 | |||
3241 | switch (a->type) | ||
3242 | { | ||
3243 | case DFA: | ||
3244 | result = evaluate_dfa (a, string); | ||
3245 | break; | ||
3246 | case NFA: | ||
3247 | result = evaluate_nfa (a, string); | ||
3248 | break; | ||
3249 | default: | ||
3250 | GNUNET_log (GNUNET_ERROR_TYPE_ERROR, | ||
3251 | "Evaluating regex failed, automaton has no type!\n"); | ||
3252 | result = GNUNET_SYSERR; | ||
3253 | break; | ||
3254 | } | ||
3255 | |||
3256 | return result; | ||
3257 | } | ||
3258 | |||
3259 | |||
3260 | /** | ||
3261 | * Get the canonical regex of the given automaton. | ||
3262 | * When constructing the automaton a proof is computed for each state, | ||
3263 | * consisting of the regular expression leading to this state. A complete | ||
3264 | * regex for the automaton can be computed by combining these proofs. | ||
3265 | * As of now this function is only useful for testing. | ||
3266 | * | ||
3267 | * @param a automaton for which the canonical regex should be returned. | ||
3268 | * | ||
3269 | * @return | ||
3270 | */ | ||
3271 | const char * | ||
3272 | REGEX_INTERNAL_get_canonical_regex (struct REGEX_INTERNAL_Automaton *a) | ||
3273 | { | ||
3274 | if (NULL == a) | ||
3275 | return NULL; | ||
3276 | |||
3277 | return a->canonical_regex; | ||
3278 | } | ||
3279 | |||
3280 | |||
3281 | /** | ||
3282 | * Get the number of transitions that are contained in the given automaton. | ||
3283 | * | ||
3284 | * @param a automaton for which the number of transitions should be returned. | ||
3285 | * | ||
3286 | * @return number of transitions in the given automaton. | ||
3287 | */ | ||
3288 | unsigned int | ||
3289 | REGEX_INTERNAL_get_transition_count (struct REGEX_INTERNAL_Automaton *a) | ||
3290 | { | ||
3291 | unsigned int t_count; | ||
3292 | struct REGEX_INTERNAL_State *s; | ||
3293 | |||
3294 | if (NULL == a) | ||
3295 | return 0; | ||
3296 | |||
3297 | t_count = 0; | ||
3298 | for (s = a->states_head; NULL != s; s = s->next) | ||
3299 | t_count += s->transition_count; | ||
3300 | |||
3301 | return t_count; | ||
3302 | } | ||
3303 | |||
3304 | |||
3305 | /** | ||
3306 | * Get the first key for the given @a input_string. This hashes the first x bits | ||
3307 | * of the @a input_string. | ||
3308 | * | ||
3309 | * @param input_string string. | ||
3310 | * @param string_len length of the @a input_string. | ||
3311 | * @param key pointer to where to write the hash code. | ||
3312 | * @return number of bits of @a input_string that have been consumed | ||
3313 | * to construct the key | ||
3314 | */ | ||
3315 | size_t | ||
3316 | REGEX_INTERNAL_get_first_key (const char *input_string, | ||
3317 | size_t string_len, | ||
3318 | struct GNUNET_HashCode *key) | ||
3319 | { | ||
3320 | size_t size; | ||
3321 | |||
3322 | size = string_len < GNUNET_REGEX_INITIAL_BYTES ? string_len : | ||
3323 | GNUNET_REGEX_INITIAL_BYTES; | ||
3324 | if (NULL == input_string) | ||
3325 | { | ||
3326 | GNUNET_log (GNUNET_ERROR_TYPE_ERROR, | ||
3327 | "Given input string was NULL!\n"); | ||
3328 | return 0; | ||
3329 | } | ||
3330 | GNUNET_CRYPTO_hash (input_string, size, key); | ||
3331 | |||
3332 | return size; | ||
3333 | } | ||
3334 | |||
3335 | |||
3336 | /** | ||
3337 | * Recursive function that calls the iterator for each synthetic start state. | ||
3338 | * | ||
3339 | * @param min_len minimum length of the path in the graph. | ||
3340 | * @param max_len maximum length of the path in the graph. | ||
3341 | * @param consumed_string string consumed by traversing the graph till this state. | ||
3342 | * @param state current state of the automaton. | ||
3343 | * @param iterator iterator function called for each edge. | ||
3344 | * @param iterator_cls closure for the @a iterator function. | ||
3345 | */ | ||
3346 | static void | ||
3347 | iterate_initial_edge (unsigned int min_len, | ||
3348 | unsigned int max_len, | ||
3349 | char *consumed_string, | ||
3350 | struct REGEX_INTERNAL_State *state, | ||
3351 | REGEX_INTERNAL_KeyIterator iterator, | ||
3352 | void *iterator_cls) | ||
3353 | { | ||
3354 | char *temp; | ||
3355 | struct REGEX_INTERNAL_Transition *t; | ||
3356 | unsigned int num_edges = state->transition_count; | ||
3357 | struct REGEX_BLOCK_Edge edges[num_edges]; | ||
3358 | struct REGEX_BLOCK_Edge edge[1]; | ||
3359 | struct GNUNET_HashCode hash; | ||
3360 | struct GNUNET_HashCode hash_new; | ||
3361 | unsigned int cur_len; | ||
3362 | |||
3363 | if (NULL != consumed_string) | ||
3364 | cur_len = strlen (consumed_string); | ||
3365 | else | ||
3366 | cur_len = 0; | ||
3367 | |||
3368 | if ( ( (cur_len >= min_len) || | ||
3369 | (GNUNET_YES == state->accepting) ) && | ||
3370 | (cur_len > 0) && | ||
3371 | (NULL != consumed_string) ) | ||
3372 | { | ||
3373 | if (cur_len <= max_len) | ||
3374 | { | ||
3375 | if ( (NULL != state->proof) && | ||
3376 | (0 != strcmp (consumed_string, | ||
3377 | state->proof)) ) | ||
3378 | { | ||
3379 | (void) state_get_edges (state, edges); | ||
3380 | GNUNET_CRYPTO_hash (consumed_string, | ||
3381 | strlen (consumed_string), | ||
3382 | &hash); | ||
3383 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, | ||
3384 | "Start state for string `%s' is %s\n", | ||
3385 | consumed_string, | ||
3386 | GNUNET_h2s (&hash)); | ||
3387 | iterator (iterator_cls, | ||
3388 | &hash, | ||
3389 | consumed_string, | ||
3390 | state->accepting, | ||
3391 | num_edges, edges); | ||
3392 | } | ||
3393 | |||
3394 | if ( (GNUNET_YES == state->accepting) && | ||
3395 | (cur_len > 1) && | ||
3396 | (state->transition_count < 1) && | ||
3397 | (cur_len < max_len) ) | ||
3398 | { | ||
3399 | /* Special case for regex consisting of just a string that is shorter than | ||
3400 | * max_len */ | ||
3401 | edge[0].label = &consumed_string[cur_len - 1]; | ||
3402 | edge[0].destination = state->hash; | ||
3403 | temp = GNUNET_strdup (consumed_string); | ||
3404 | temp[cur_len - 1] = '\0'; | ||
3405 | GNUNET_CRYPTO_hash (temp, | ||
3406 | cur_len - 1, | ||
3407 | &hash_new); | ||
3408 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, | ||
3409 | "Start state for short string `%s' is %s\n", | ||
3410 | temp, | ||
3411 | GNUNET_h2s (&hash_new)); | ||
3412 | iterator (iterator_cls, | ||
3413 | &hash_new, | ||
3414 | temp, | ||
3415 | GNUNET_NO, 1, | ||
3416 | edge); | ||
3417 | GNUNET_free (temp); | ||
3418 | } | ||
3419 | } | ||
3420 | else /* cur_len > max_len */ | ||
3421 | { | ||
3422 | /* Case where the concatenated labels are longer than max_len, then split. */ | ||
3423 | edge[0].label = &consumed_string[max_len]; | ||
3424 | edge[0].destination = state->hash; | ||
3425 | temp = GNUNET_strdup (consumed_string); | ||
3426 | temp[max_len] = '\0'; | ||
3427 | GNUNET_CRYPTO_hash (temp, max_len, &hash); | ||
3428 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, | ||
3429 | "Start state at split edge `%s'-`%s` is %s\n", | ||
3430 | temp, | ||
3431 | edge[0].label, | ||
3432 | GNUNET_h2s (&hash_new)); | ||
3433 | iterator (iterator_cls, | ||
3434 | &hash, | ||
3435 | temp, | ||
3436 | GNUNET_NO, | ||
3437 | 1, | ||
3438 | edge); | ||
3439 | GNUNET_free (temp); | ||
3440 | } | ||
3441 | } | ||
3442 | |||
3443 | if (cur_len < max_len) | ||
3444 | { | ||
3445 | for (t = state->transitions_head; NULL != t; t = t->next) | ||
3446 | { | ||
3447 | if (NULL != strchr (t->label, | ||
3448 | (int) '.')) | ||
3449 | { | ||
3450 | /* Wildcards not allowed during starting states */ | ||
3451 | GNUNET_break (0); | ||
3452 | continue; | ||
3453 | } | ||
3454 | if (NULL != consumed_string) | ||
3455 | GNUNET_asprintf (&temp, | ||
3456 | "%s%s", | ||
3457 | consumed_string, | ||
3458 | t->label); | ||
3459 | else | ||
3460 | GNUNET_asprintf (&temp, | ||
3461 | "%s", | ||
3462 | t->label); | ||
3463 | iterate_initial_edge (min_len, | ||
3464 | max_len, | ||
3465 | temp, | ||
3466 | t->to_state, | ||
3467 | iterator, | ||
3468 | iterator_cls); | ||
3469 | GNUNET_free (temp); | ||
3470 | } | ||
3471 | } | ||
3472 | } | ||
3473 | |||
3474 | |||
3475 | /** | ||
3476 | * Iterate over all edges starting from start state of automaton 'a'. Calling | ||
3477 | * iterator for each edge. | ||
3478 | * | ||
3479 | * @param a automaton. | ||
3480 | * @param iterator iterator called for each edge. | ||
3481 | * @param iterator_cls closure. | ||
3482 | */ | ||
3483 | void | ||
3484 | REGEX_INTERNAL_iterate_all_edges (struct REGEX_INTERNAL_Automaton *a, | ||
3485 | REGEX_INTERNAL_KeyIterator iterator, | ||
3486 | void *iterator_cls) | ||
3487 | { | ||
3488 | struct REGEX_INTERNAL_State *s; | ||
3489 | |||
3490 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, | ||
3491 | "Iterating over starting edges\n"); | ||
3492 | iterate_initial_edge (GNUNET_REGEX_INITIAL_BYTES, | ||
3493 | GNUNET_REGEX_INITIAL_BYTES, | ||
3494 | NULL, a->start, | ||
3495 | iterator, iterator_cls); | ||
3496 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, | ||
3497 | "Iterating over DFA edges\n"); | ||
3498 | for (s = a->states_head; NULL != s; s = s->next) | ||
3499 | { | ||
3500 | struct REGEX_BLOCK_Edge edges[s->transition_count]; | ||
3501 | unsigned int num_edges; | ||
3502 | |||
3503 | num_edges = state_get_edges (s, edges); | ||
3504 | if ( ( (NULL != s->proof) && | ||
3505 | (0 < strlen (s->proof)) ) || s->accepting) | ||
3506 | { | ||
3507 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, | ||
3508 | "Creating DFA edges at `%s' under key %s\n", | ||
3509 | s->proof, | ||
3510 | GNUNET_h2s (&s->hash)); | ||
3511 | iterator (iterator_cls, &s->hash, s->proof, | ||
3512 | s->accepting, | ||
3513 | num_edges, edges); | ||
3514 | } | ||
3515 | s->marked = GNUNET_NO; | ||
3516 | } | ||
3517 | } | ||
3518 | |||
3519 | |||
3520 | /** | ||
3521 | * Struct to hold all the relevant state information in the HashMap. | ||
3522 | * | ||
3523 | * Contains the same info as the Regex Iterator parametes except the key, | ||
3524 | * which comes directly from the HashMap iterator. | ||
3525 | */ | ||
3526 | struct temporal_state_store { | ||
3527 | int reachable; | ||
3528 | char *proof; | ||
3529 | int accepting; | ||
3530 | int num_edges; | ||
3531 | struct REGEX_BLOCK_Edge *edges; | ||
3532 | }; | ||
3533 | |||
3534 | |||
3535 | /** | ||
3536 | * Store regex iterator and cls in one place to pass to the hashmap iterator. | ||
3537 | */ | ||
3538 | struct client_iterator { | ||
3539 | REGEX_INTERNAL_KeyIterator iterator; | ||
3540 | void *iterator_cls; | ||
3541 | }; | ||
3542 | |||
3543 | |||
3544 | /** | ||
3545 | * Iterator over all edges of a dfa. Stores all of them in a HashMap | ||
3546 | * for later reachability marking. | ||
3547 | * | ||
3548 | * @param cls Closure (HashMap) | ||
3549 | * @param key hash for current state. | ||
3550 | * @param proof proof for current state | ||
3551 | * @param accepting GNUNET_YES if this is an accepting state, GNUNET_NO if not. | ||
3552 | * @param num_edges number of edges leaving current state. | ||
3553 | * @param edges edges leaving current state. | ||
3554 | */ | ||
3555 | static void | ||
3556 | store_all_states (void *cls, | ||
3557 | const struct GNUNET_HashCode *key, | ||
3558 | const char *proof, | ||
3559 | int accepting, | ||
3560 | unsigned int num_edges, | ||
3561 | const struct REGEX_BLOCK_Edge *edges) | ||
3562 | { | ||
3563 | struct GNUNET_CONTAINER_MultiHashMap *hm = cls; | ||
3564 | struct temporal_state_store *tmp; | ||
3565 | size_t edges_size; | ||
3566 | |||
3567 | tmp = GNUNET_new (struct temporal_state_store); | ||
3568 | tmp->reachable = GNUNET_NO; | ||
3569 | tmp->proof = GNUNET_strdup (proof); | ||
3570 | tmp->accepting = accepting; | ||
3571 | tmp->num_edges = num_edges; | ||
3572 | edges_size = sizeof (struct REGEX_BLOCK_Edge) * num_edges; | ||
3573 | tmp->edges = GNUNET_malloc (edges_size); | ||
3574 | GNUNET_memcpy(tmp->edges, edges, edges_size); | ||
3575 | GNUNET_CONTAINER_multihashmap_put (hm, key, tmp, | ||
3576 | GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_FAST); | ||
3577 | } | ||
3578 | |||
3579 | |||
3580 | /** | ||
3581 | * Mark state as reachable and call recursively on all its edges. | ||
3582 | * | ||
3583 | * If already marked as reachable, do nothing. | ||
3584 | * | ||
3585 | * @param state State to mark as reachable. | ||
3586 | * @param hm HashMap which stores all the states indexed by key. | ||
3587 | */ | ||
3588 | static void | ||
3589 | mark_as_reachable (struct temporal_state_store *state, | ||
3590 | struct GNUNET_CONTAINER_MultiHashMap *hm) | ||
3591 | { | ||
3592 | struct temporal_state_store *child; | ||
3593 | unsigned int i; | ||
3594 | |||
3595 | if (GNUNET_YES == state->reachable) | ||
3596 | /* visited */ | ||
3597 | return; | ||
3598 | |||
3599 | state->reachable = GNUNET_YES; | ||
3600 | for (i = 0; i < state->num_edges; i++) | ||
3601 | { | ||
3602 | child = GNUNET_CONTAINER_multihashmap_get (hm, | ||
3603 | &state->edges[i].destination); | ||
3604 | if (NULL == child) | ||
3605 | { | ||
3606 | GNUNET_break (0); | ||
3607 | continue; | ||
3608 | } | ||
3609 | mark_as_reachable (child, hm); | ||
3610 | } | ||
3611 | } | ||
3612 | |||
3613 | |||
3614 | /** | ||
3615 | * Iterator over hash map entries to mark the ones that are reachable. | ||
3616 | * | ||
3617 | * @param cls closure | ||
3618 | * @param key current key code | ||
3619 | * @param value value in the hash map | ||
3620 | * @return #GNUNET_YES if we should continue to iterate, | ||
3621 | * #GNUNET_NO if not. | ||
3622 | */ | ||
3623 | static int | ||
3624 | reachability_iterator (void *cls, | ||
3625 | const struct GNUNET_HashCode *key, | ||
3626 | void *value) | ||
3627 | { | ||
3628 | struct GNUNET_CONTAINER_MultiHashMap *hm = cls; | ||
3629 | struct temporal_state_store *state = value; | ||
3630 | |||
3631 | if (GNUNET_YES == state->reachable) | ||
3632 | /* already visited and marked */ | ||
3633 | return GNUNET_YES; | ||
3634 | |||
3635 | if (GNUNET_REGEX_INITIAL_BYTES > strlen (state->proof) && | ||
3636 | GNUNET_NO == state->accepting) | ||
3637 | /* not directly reachable */ | ||
3638 | return GNUNET_YES; | ||
3639 | |||
3640 | mark_as_reachable (state, hm); | ||
3641 | return GNUNET_YES; | ||
3642 | } | ||
3643 | |||
3644 | |||
3645 | /** | ||
3646 | * Iterator over hash map entries. | ||
3647 | * Calling the callback on the ones marked as reachables. | ||
3648 | * | ||
3649 | * @param cls closure | ||
3650 | * @param key current key code | ||
3651 | * @param value value in the hash map | ||
3652 | * @return #GNUNET_YES if we should continue to iterate, | ||
3653 | * #GNUNET_NO if not. | ||
3654 | */ | ||
3655 | static int | ||
3656 | iterate_reachables (void *cls, | ||
3657 | const struct GNUNET_HashCode *key, | ||
3658 | void *value) | ||
3659 | { | ||
3660 | struct client_iterator *ci = cls; | ||
3661 | struct temporal_state_store *state = value; | ||
3662 | |||
3663 | if (GNUNET_YES == state->reachable) | ||
3664 | { | ||
3665 | ci->iterator (ci->iterator_cls, key, | ||
3666 | state->proof, state->accepting, | ||
3667 | state->num_edges, state->edges); | ||
3668 | } | ||
3669 | GNUNET_free (state->edges); | ||
3670 | GNUNET_free (state->proof); | ||
3671 | GNUNET_free (state); | ||
3672 | return GNUNET_YES; | ||
3673 | |||
3674 | } | ||
3675 | |||
3676 | /** | ||
3677 | * Iterate over all edges of automaton 'a' that are reachable from a state with | ||
3678 | * a proof of at least GNUNET_REGEX_INITIAL_BYTES characters. | ||
3679 | * | ||
3680 | * Call the iterator for each such edge. | ||
3681 | * | ||
3682 | * @param a automaton. | ||
3683 | * @param iterator iterator called for each reachable edge. | ||
3684 | * @param iterator_cls closure. | ||
3685 | */ | ||
3686 | void | ||
3687 | REGEX_INTERNAL_iterate_reachable_edges (struct REGEX_INTERNAL_Automaton *a, | ||
3688 | REGEX_INTERNAL_KeyIterator iterator, | ||
3689 | void *iterator_cls) | ||
3690 | { | ||
3691 | struct GNUNET_CONTAINER_MultiHashMap *hm; | ||
3692 | struct client_iterator ci; | ||
3693 | |||
3694 | hm = GNUNET_CONTAINER_multihashmap_create (a->state_count * 2, GNUNET_NO); | ||
3695 | ci.iterator = iterator; | ||
3696 | ci.iterator_cls = iterator_cls; | ||
3697 | |||
3698 | REGEX_INTERNAL_iterate_all_edges (a, &store_all_states, hm); | ||
3699 | GNUNET_CONTAINER_multihashmap_iterate (hm, &reachability_iterator, hm); | ||
3700 | GNUNET_CONTAINER_multihashmap_iterate (hm, &iterate_reachables, &ci); | ||
3701 | |||
3702 | GNUNET_CONTAINER_multihashmap_destroy (hm); | ||
3703 | } | ||
3704 | |||
3705 | |||
3706 | /* end of regex_internal.c */ | ||
diff --git a/src/regex/regex_internal.h b/src/regex/regex_internal.h new file mode 100644 index 000000000..d52479ffe --- /dev/null +++ b/src/regex/regex_internal.h | |||
@@ -0,0 +1,453 @@ | |||
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 | /** | ||
19 | * @file src/regex/regex_internal.h | ||
20 | * @brief common internal definitions for regex library. | ||
21 | * @author Maximilian Szengel | ||
22 | */ | ||
23 | #ifndef REGEX_INTERNAL_H | ||
24 | #define REGEX_INTERNAL_H | ||
25 | |||
26 | #include "regex_internal_lib.h" | ||
27 | |||
28 | #ifdef __cplusplus | ||
29 | extern "C" | ||
30 | { | ||
31 | #if 0 /* keep Emacsens' auto-indent happy */ | ||
32 | } | ||
33 | #endif | ||
34 | #endif | ||
35 | |||
36 | /** | ||
37 | * char array of literals that are allowed inside a regex (apart from the | ||
38 | * operators) | ||
39 | */ | ||
40 | #define ALLOWED_LITERALS "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz" | ||
41 | |||
42 | |||
43 | /** | ||
44 | * Transition between two states. Transitions are stored at the states from | ||
45 | * which they origin ('from_state'). Each state can have 0-n transitions. | ||
46 | * If label is NULL, this is considered to be an epsilon transition. | ||
47 | */ | ||
48 | struct REGEX_INTERNAL_Transition | ||
49 | { | ||
50 | /** | ||
51 | * This is a linked list. | ||
52 | */ | ||
53 | struct REGEX_INTERNAL_Transition *prev; | ||
54 | |||
55 | /** | ||
56 | * This is a linked list. | ||
57 | */ | ||
58 | struct REGEX_INTERNAL_Transition *next; | ||
59 | |||
60 | /** | ||
61 | * Unique id of this transition. | ||
62 | */ | ||
63 | unsigned int id; | ||
64 | |||
65 | /** | ||
66 | * Label for this transition. This is basically the edge label for the graph. | ||
67 | */ | ||
68 | char *label; | ||
69 | |||
70 | /** | ||
71 | * State to which this transition leads. | ||
72 | */ | ||
73 | struct REGEX_INTERNAL_State *to_state; | ||
74 | |||
75 | /** | ||
76 | * State from which this transition origins. | ||
77 | */ | ||
78 | struct REGEX_INTERNAL_State *from_state; | ||
79 | }; | ||
80 | |||
81 | |||
82 | /** | ||
83 | * A state. Can be used in DFA and NFA automatons. | ||
84 | */ | ||
85 | struct REGEX_INTERNAL_State; | ||
86 | |||
87 | |||
88 | /** | ||
89 | * Set of states. | ||
90 | */ | ||
91 | struct REGEX_INTERNAL_StateSet | ||
92 | { | ||
93 | /** | ||
94 | * Array of states. | ||
95 | */ | ||
96 | struct REGEX_INTERNAL_State **states; | ||
97 | |||
98 | /** | ||
99 | * Number of entries in *use* in the 'states' array. | ||
100 | */ | ||
101 | unsigned int off; | ||
102 | |||
103 | /** | ||
104 | * Length of the 'states' array. | ||
105 | */ | ||
106 | unsigned int size; | ||
107 | }; | ||
108 | |||
109 | |||
110 | /** | ||
111 | * A state. Can be used in DFA and NFA automatons. | ||
112 | */ | ||
113 | struct REGEX_INTERNAL_State | ||
114 | { | ||
115 | /** | ||
116 | * This is a linked list to keep states in an automaton. | ||
117 | */ | ||
118 | struct REGEX_INTERNAL_State *prev; | ||
119 | |||
120 | /** | ||
121 | * This is a linked list to keep states in an automaton. | ||
122 | */ | ||
123 | struct REGEX_INTERNAL_State *next; | ||
124 | |||
125 | /** | ||
126 | * This is a multi DLL for StateSet_MDLL. | ||
127 | */ | ||
128 | struct REGEX_INTERNAL_State *prev_SS; | ||
129 | |||
130 | /** | ||
131 | * This is a multi DLL for StateSet_MDLL. | ||
132 | */ | ||
133 | struct REGEX_INTERNAL_State *next_SS; | ||
134 | |||
135 | /** | ||
136 | * This is a multi DLL for StateSet_MDLL Stack. | ||
137 | */ | ||
138 | struct REGEX_INTERNAL_State *prev_ST; | ||
139 | |||
140 | /** | ||
141 | * This is a multi DLL for StateSet_MDLL Stack. | ||
142 | */ | ||
143 | struct REGEX_INTERNAL_State *next_ST; | ||
144 | |||
145 | /** | ||
146 | * Unique state id. | ||
147 | */ | ||
148 | unsigned int id; | ||
149 | |||
150 | /** | ||
151 | * Unique state id that is used for traversing the automaton. It is guaranteed | ||
152 | * to be > 0 and < state_count. | ||
153 | */ | ||
154 | unsigned int traversal_id; | ||
155 | |||
156 | /** | ||
157 | * If this is an accepting state or not. | ||
158 | */ | ||
159 | int accepting; | ||
160 | |||
161 | /** | ||
162 | * Marking of the state. This is used for marking all visited states when | ||
163 | * traversing all states of an automaton and for cases where the state id | ||
164 | * cannot be used (dfa minimization). | ||
165 | */ | ||
166 | int marked; | ||
167 | |||
168 | /** | ||
169 | * Marking the state as contained. This is used for checking, if the state is | ||
170 | * contained in a set in constant time. | ||
171 | */ | ||
172 | int contained; | ||
173 | |||
174 | /** | ||
175 | * Marking the state as part of an SCC (Strongly Connected Component). All | ||
176 | * states with the same scc_id are part of the same SCC. scc_id is 0, if state | ||
177 | * is not a part of any SCC. | ||
178 | */ | ||
179 | unsigned int scc_id; | ||
180 | |||
181 | /** | ||
182 | * Used for SCC detection. | ||
183 | */ | ||
184 | int index; | ||
185 | |||
186 | /** | ||
187 | * Used for SCC detection. | ||
188 | */ | ||
189 | int lowlink; | ||
190 | |||
191 | /** | ||
192 | * Human readable name of the state. Used for debugging and graph | ||
193 | * creation. | ||
194 | */ | ||
195 | char *name; | ||
196 | |||
197 | /** | ||
198 | * Hash of the state. | ||
199 | */ | ||
200 | struct GNUNET_HashCode hash; | ||
201 | |||
202 | /** | ||
203 | * Linear state ID accquired by depth-first-search. This ID should be used for | ||
204 | * storing information about the state in an array, because the 'id' of the | ||
205 | * state is not guaranteed to be linear. The 'dfs_id' is guaranteed to be > 0 | ||
206 | * and < 'state_count'. | ||
207 | */ | ||
208 | unsigned int dfs_id; | ||
209 | |||
210 | /** | ||
211 | * Proof for this state. | ||
212 | */ | ||
213 | char *proof; | ||
214 | |||
215 | /** | ||
216 | * Number of transitions from this state to other states. | ||
217 | */ | ||
218 | unsigned int transition_count; | ||
219 | |||
220 | /** | ||
221 | * DLL of transitions. | ||
222 | */ | ||
223 | struct REGEX_INTERNAL_Transition *transitions_head; | ||
224 | |||
225 | /** | ||
226 | * DLL of transitions. | ||
227 | */ | ||
228 | struct REGEX_INTERNAL_Transition *transitions_tail; | ||
229 | |||
230 | /** | ||
231 | * Number of incoming transitions. Used for compressing DFA paths. | ||
232 | */ | ||
233 | unsigned int incoming_transition_count; | ||
234 | |||
235 | /** | ||
236 | * Set of states on which this state is based on. Used when creating a DFA out | ||
237 | * of several NFA states. | ||
238 | */ | ||
239 | struct REGEX_INTERNAL_StateSet nfa_set; | ||
240 | }; | ||
241 | |||
242 | |||
243 | /** | ||
244 | * Type of an automaton. | ||
245 | */ | ||
246 | enum REGEX_INTERNAL_AutomatonType | ||
247 | { | ||
248 | NFA, | ||
249 | DFA | ||
250 | }; | ||
251 | |||
252 | |||
253 | /** | ||
254 | * Automaton representation. | ||
255 | */ | ||
256 | struct REGEX_INTERNAL_Automaton | ||
257 | { | ||
258 | /** | ||
259 | * Linked list of NFAs used for partial NFA creation. | ||
260 | */ | ||
261 | struct REGEX_INTERNAL_Automaton *prev; | ||
262 | |||
263 | /** | ||
264 | * Linked list of NFAs used for partial NFA creation. | ||
265 | */ | ||
266 | struct REGEX_INTERNAL_Automaton *next; | ||
267 | |||
268 | /** | ||
269 | * First state of the automaton. This is mainly used for constructing an NFA, | ||
270 | * where each NFA itself consists of one or more NFAs linked together. | ||
271 | */ | ||
272 | struct REGEX_INTERNAL_State *start; | ||
273 | |||
274 | /** | ||
275 | * End state of the partial NFA. This is undefined for DFAs | ||
276 | */ | ||
277 | struct REGEX_INTERNAL_State *end; | ||
278 | |||
279 | /** | ||
280 | * Number of states in the automaton. | ||
281 | */ | ||
282 | unsigned int state_count; | ||
283 | |||
284 | /** | ||
285 | * DLL of states. | ||
286 | */ | ||
287 | struct REGEX_INTERNAL_State *states_head; | ||
288 | |||
289 | /** | ||
290 | * DLL of states | ||
291 | */ | ||
292 | struct REGEX_INTERNAL_State *states_tail; | ||
293 | |||
294 | /** | ||
295 | * Type of the automaton. | ||
296 | */ | ||
297 | enum REGEX_INTERNAL_AutomatonType type; | ||
298 | |||
299 | /** | ||
300 | * Regex | ||
301 | */ | ||
302 | char *regex; | ||
303 | |||
304 | /** | ||
305 | * Canonical regex (result of RX->NFA->DFA->RX) | ||
306 | */ | ||
307 | char *canonical_regex; | ||
308 | |||
309 | /** | ||
310 | * GNUNET_YES, if multi strides have been added to the Automaton. | ||
311 | */ | ||
312 | int is_multistrided; | ||
313 | }; | ||
314 | |||
315 | |||
316 | /** | ||
317 | * Construct an NFA by parsing the regex string of length 'len'. | ||
318 | * | ||
319 | * @param regex regular expression string. | ||
320 | * @param len length of the string. | ||
321 | * | ||
322 | * @return NFA, needs to be freed using REGEX_INTERNAL_automaton_destroy. | ||
323 | */ | ||
324 | struct REGEX_INTERNAL_Automaton * | ||
325 | REGEX_INTERNAL_construct_nfa (const char *regex, const size_t len); | ||
326 | |||
327 | |||
328 | /** | ||
329 | * Function that get's passed to automaton traversal and is called before each | ||
330 | * next traversal from state 's' using transition 't' to check if traversal | ||
331 | * should proceed. Return GNUNET_NO to stop traversal or GNUNET_YES to continue. | ||
332 | * | ||
333 | * @param cls closure for the check. | ||
334 | * @param s current state in the traversal. | ||
335 | * @param t current transition from state 's' that will be used for the next | ||
336 | * step. | ||
337 | * | ||
338 | * @return GNUNET_YES to proceed traversal, GNUNET_NO to stop. | ||
339 | */ | ||
340 | typedef int (*REGEX_INTERNAL_traverse_check) (void *cls, | ||
341 | struct REGEX_INTERNAL_State * s, | ||
342 | struct REGEX_INTERNAL_Transition * t); | ||
343 | |||
344 | |||
345 | /** | ||
346 | * Function that is called with each state, when traversing an automaton. | ||
347 | * | ||
348 | * @param cls closure. | ||
349 | * @param count current count of the state, from 0 to a->state_count -1. | ||
350 | * @param s state. | ||
351 | */ | ||
352 | typedef void (*REGEX_INTERNAL_traverse_action) (void *cls, | ||
353 | const unsigned int count, | ||
354 | struct REGEX_INTERNAL_State * s); | ||
355 | |||
356 | |||
357 | /** | ||
358 | * Traverses the given automaton using depth-first-search (DFS) from it's start | ||
359 | * state, visiting all reachable states and calling 'action' on each one of | ||
360 | * them. | ||
361 | * | ||
362 | * @param a automaton to be traversed. | ||
363 | * @param start start state, pass a->start or NULL to traverse the whole automaton. | ||
364 | * @param check function that is checked before advancing on each transition | ||
365 | * in the DFS. | ||
366 | * @param check_cls closure for check. | ||
367 | * @param action action to be performed on each state. | ||
368 | * @param action_cls closure for action | ||
369 | */ | ||
370 | void | ||
371 | REGEX_INTERNAL_automaton_traverse (const struct REGEX_INTERNAL_Automaton *a, | ||
372 | struct REGEX_INTERNAL_State *start, | ||
373 | REGEX_INTERNAL_traverse_check check, | ||
374 | void *check_cls, | ||
375 | REGEX_INTERNAL_traverse_action action, | ||
376 | void *action_cls); | ||
377 | |||
378 | /** | ||
379 | * Get the canonical regex of the given automaton. | ||
380 | * When constructing the automaton a proof is computed for each state, | ||
381 | * consisting of the regular expression leading to this state. A complete | ||
382 | * regex for the automaton can be computed by combining these proofs. | ||
383 | * As of now this function is only useful for testing. | ||
384 | * | ||
385 | * @param a automaton for which the canonical regex should be returned. | ||
386 | * | ||
387 | * @return canonical regex string. | ||
388 | */ | ||
389 | const char * | ||
390 | REGEX_INTERNAL_get_canonical_regex (struct REGEX_INTERNAL_Automaton *a); | ||
391 | |||
392 | |||
393 | /** | ||
394 | * Get the number of transitions that are contained in the given automaton. | ||
395 | * | ||
396 | * @param a automaton for which the number of transitions should be returned. | ||
397 | * | ||
398 | * @return number of transitions in the given automaton. | ||
399 | */ | ||
400 | unsigned int | ||
401 | REGEX_INTERNAL_get_transition_count (struct REGEX_INTERNAL_Automaton *a); | ||
402 | |||
403 | |||
404 | /** | ||
405 | * Context that contains an id counter for states and transitions as well as a | ||
406 | * DLL of automatons used as a stack for NFA construction. | ||
407 | */ | ||
408 | struct REGEX_INTERNAL_Context | ||
409 | { | ||
410 | /** | ||
411 | * Unique state id. | ||
412 | */ | ||
413 | unsigned int state_id; | ||
414 | |||
415 | /** | ||
416 | * Unique transition id. | ||
417 | */ | ||
418 | unsigned int transition_id; | ||
419 | |||
420 | /** | ||
421 | * DLL of REGEX_INTERNAL_Automaton's used as a stack. | ||
422 | */ | ||
423 | struct REGEX_INTERNAL_Automaton *stack_head; | ||
424 | |||
425 | /** | ||
426 | * DLL of REGEX_INTERNAL_Automaton's used as a stack. | ||
427 | */ | ||
428 | struct REGEX_INTERNAL_Automaton *stack_tail; | ||
429 | }; | ||
430 | |||
431 | |||
432 | /** | ||
433 | * Adds multi-strided transitions to the given 'dfa'. | ||
434 | * | ||
435 | * @param regex_ctx regex context needed to add transitions to the automaton. | ||
436 | * @param dfa DFA to which the multi strided transitions should be added. | ||
437 | * @param stride_len length of the strides. | ||
438 | */ | ||
439 | void | ||
440 | REGEX_INTERNAL_dfa_add_multi_strides (struct REGEX_INTERNAL_Context *regex_ctx, | ||
441 | struct REGEX_INTERNAL_Automaton *dfa, | ||
442 | const unsigned int stride_len); | ||
443 | |||
444 | |||
445 | |||
446 | #if 0 /* keep Emacsens' auto-indent happy */ | ||
447 | { | ||
448 | #endif | ||
449 | #ifdef __cplusplus | ||
450 | } | ||
451 | #endif | ||
452 | |||
453 | #endif | ||
diff --git a/src/regex/regex_internal_dht.c b/src/regex/regex_internal_dht.c new file mode 100644 index 000000000..2555ef1df --- /dev/null +++ b/src/regex/regex_internal_dht.c | |||
@@ -0,0 +1,829 @@ | |||
1 | /* | ||
2 | This file is part of GNUnet | ||
3 | Copyright (C) 2012, 2015 GNUnet e.V. | ||
4 | |||
5 | GNUnet is free software: you can redistribute it and/or modify it | ||
6 | under the terms of the GNU Affero General Public License as published | ||
7 | by the Free Software Foundation, either version 3 of the License, | ||
8 | or (at your option) any later version. | ||
9 | |||
10 | GNUnet is distributed in the hope that it will be useful, but | ||
11 | WITHOUT ANY WARRANTY; without even the implied warranty of | ||
12 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | ||
13 | Affero General Public License for more details. | ||
14 | |||
15 | You should have received a copy of the GNU Affero General Public License | ||
16 | along with this program. If not, see <http://www.gnu.org/licenses/>. | ||
17 | */ | ||
18 | /** | ||
19 | * @file src/regex/regex_internal_dht.c | ||
20 | * @brief library to announce regexes in the network and match strings | ||
21 | * against published regexes. | ||
22 | * @author Bartlomiej Polot | ||
23 | */ | ||
24 | #include "platform.h" | ||
25 | #include "regex_internal_lib.h" | ||
26 | #include "regex_block_lib.h" | ||
27 | #include "gnunet_dht_service.h" | ||
28 | #include "gnunet_statistics_service.h" | ||
29 | #include "gnunet_constants.h" | ||
30 | #include "gnunet_signatures.h" | ||
31 | |||
32 | |||
33 | #define LOG(kind,...) GNUNET_log_from (kind,"regex-dht",__VA_ARGS__) | ||
34 | |||
35 | /** | ||
36 | * DHT replication level to use. | ||
37 | */ | ||
38 | #define DHT_REPLICATION 5 | ||
39 | |||
40 | /** | ||
41 | * DHT record lifetime to use. | ||
42 | */ | ||
43 | #define DHT_TTL GNUNET_TIME_UNIT_HOURS | ||
44 | |||
45 | /** | ||
46 | * DHT options to set. | ||
47 | */ | ||
48 | #define DHT_OPT GNUNET_DHT_RO_DEMULTIPLEX_EVERYWHERE | ||
49 | |||
50 | |||
51 | /** | ||
52 | * Handle to store cached data about a regex announce. | ||
53 | */ | ||
54 | struct REGEX_INTERNAL_Announcement | ||
55 | { | ||
56 | /** | ||
57 | * DHT handle to use, must be initialized externally. | ||
58 | */ | ||
59 | struct GNUNET_DHT_Handle *dht; | ||
60 | |||
61 | /** | ||
62 | * Regular expression. | ||
63 | */ | ||
64 | const char *regex; | ||
65 | |||
66 | /** | ||
67 | * Automaton representation of the regex (expensive to build). | ||
68 | */ | ||
69 | struct REGEX_INTERNAL_Automaton *dfa; | ||
70 | |||
71 | /** | ||
72 | * Our private key. | ||
73 | */ | ||
74 | const struct GNUNET_CRYPTO_EddsaPrivateKey *priv; | ||
75 | |||
76 | /** | ||
77 | * Optional statistics handle to report usage. Can be NULL. | ||
78 | */ | ||
79 | struct GNUNET_STATISTICS_Handle *stats; | ||
80 | }; | ||
81 | |||
82 | |||
83 | /** | ||
84 | * Regex callback iterator to store own service description in the DHT. | ||
85 | * | ||
86 | * @param cls closure. | ||
87 | * @param key hash for current state. | ||
88 | * @param proof proof for current state. | ||
89 | * @param accepting #GNUNET_YES if this is an accepting state, #GNUNET_NO if not. | ||
90 | * @param num_edges number of edges leaving current state. | ||
91 | * @param edges edges leaving current state. | ||
92 | */ | ||
93 | static void | ||
94 | regex_iterator (void *cls, | ||
95 | const struct GNUNET_HashCode *key, | ||
96 | const char *proof, | ||
97 | int accepting, | ||
98 | unsigned int num_edges, | ||
99 | const struct REGEX_BLOCK_Edge *edges) | ||
100 | { | ||
101 | struct REGEX_INTERNAL_Announcement *h = cls; | ||
102 | struct RegexBlock *block; | ||
103 | size_t size; | ||
104 | unsigned int i; | ||
105 | |||
106 | LOG (GNUNET_ERROR_TYPE_INFO, | ||
107 | "DHT PUT for state %s with proof `%s' and %u edges:\n", | ||
108 | GNUNET_h2s (key), | ||
109 | proof, | ||
110 | num_edges); | ||
111 | for (i = 0; i < num_edges; i++) | ||
112 | { | ||
113 | LOG (GNUNET_ERROR_TYPE_INFO, | ||
114 | "Edge %u `%s' towards %s\n", | ||
115 | i, | ||
116 | edges[i].label, | ||
117 | GNUNET_h2s (&edges[i].destination)); | ||
118 | } | ||
119 | if (GNUNET_YES == accepting) | ||
120 | { | ||
121 | struct RegexAcceptBlock ab; | ||
122 | |||
123 | LOG (GNUNET_ERROR_TYPE_INFO, | ||
124 | "State %s is accepting, putting own id\n", | ||
125 | GNUNET_h2s (key)); | ||
126 | size = sizeof (struct RegexAcceptBlock); | ||
127 | ab.purpose.size = ntohl (sizeof (struct GNUNET_CRYPTO_EccSignaturePurpose) + | ||
128 | sizeof (struct GNUNET_TIME_AbsoluteNBO) + | ||
129 | sizeof (struct GNUNET_HashCode)); | ||
130 | ab.purpose.purpose = ntohl (GNUNET_SIGNATURE_PURPOSE_REGEX_ACCEPT); | ||
131 | ab.expiration_time = GNUNET_TIME_absolute_hton (GNUNET_TIME_relative_to_absolute (GNUNET_CONSTANTS_DHT_MAX_EXPIRATION)); | ||
132 | ab.key = *key; | ||
133 | GNUNET_CRYPTO_eddsa_key_get_public (h->priv, | ||
134 | &ab.peer.public_key); | ||
135 | GNUNET_assert (GNUNET_OK == | ||
136 | GNUNET_CRYPTO_eddsa_sign (h->priv, | ||
137 | &ab.purpose, | ||
138 | &ab.signature)); | ||
139 | |||
140 | GNUNET_STATISTICS_update (h->stats, "# regex accepting blocks stored", | ||
141 | 1, GNUNET_NO); | ||
142 | GNUNET_STATISTICS_update (h->stats, "# regex accepting block bytes stored", | ||
143 | sizeof (struct RegexAcceptBlock), GNUNET_NO); | ||
144 | (void) | ||
145 | GNUNET_DHT_put (h->dht, key, | ||
146 | DHT_REPLICATION, | ||
147 | DHT_OPT | GNUNET_DHT_RO_RECORD_ROUTE, | ||
148 | GNUNET_BLOCK_TYPE_REGEX_ACCEPT, | ||
149 | size, | ||
150 | &ab, | ||
151 | GNUNET_TIME_relative_to_absolute (DHT_TTL), | ||
152 | NULL, NULL); | ||
153 | } | ||
154 | block = REGEX_BLOCK_create (proof, | ||
155 | num_edges, | ||
156 | edges, | ||
157 | accepting, | ||
158 | &size); | ||
159 | if (NULL == block) | ||
160 | return; | ||
161 | (void) GNUNET_DHT_put (h->dht, | ||
162 | key, | ||
163 | DHT_REPLICATION, | ||
164 | DHT_OPT, | ||
165 | GNUNET_BLOCK_TYPE_REGEX, | ||
166 | size, | ||
167 | block, | ||
168 | GNUNET_TIME_relative_to_absolute (DHT_TTL), | ||
169 | NULL, | ||
170 | NULL); | ||
171 | GNUNET_STATISTICS_update (h->stats, | ||
172 | "# regex blocks stored", | ||
173 | 1, | ||
174 | GNUNET_NO); | ||
175 | GNUNET_STATISTICS_update (h->stats, | ||
176 | "# regex block bytes stored", | ||
177 | size, | ||
178 | GNUNET_NO); | ||
179 | GNUNET_free (block); | ||
180 | } | ||
181 | |||
182 | |||
183 | /** | ||
184 | * Announce a regular expression: put all states of the automaton in the DHT. | ||
185 | * Does not free resources, must call #REGEX_INTERNAL_announce_cancel() for that. | ||
186 | * | ||
187 | * @param dht An existing and valid DHT service handle. CANNOT be NULL. | ||
188 | * @param priv our private key, must remain valid until the announcement is cancelled | ||
189 | * @param regex Regular expression to announce. | ||
190 | * @param compression How many characters per edge can we squeeze? | ||
191 | * @param stats Optional statistics handle to report usage. Can be NULL. | ||
192 | * @return Handle to reuse o free cached resources. | ||
193 | * Must be freed by calling #REGEX_INTERNAL_announce_cancel(). | ||
194 | */ | ||
195 | struct REGEX_INTERNAL_Announcement * | ||
196 | REGEX_INTERNAL_announce (struct GNUNET_DHT_Handle *dht, | ||
197 | const struct GNUNET_CRYPTO_EddsaPrivateKey *priv, | ||
198 | const char *regex, | ||
199 | uint16_t compression, | ||
200 | struct GNUNET_STATISTICS_Handle *stats) | ||
201 | { | ||
202 | struct REGEX_INTERNAL_Announcement *h; | ||
203 | |||
204 | GNUNET_assert (NULL != dht); | ||
205 | h = GNUNET_new (struct REGEX_INTERNAL_Announcement); | ||
206 | h->regex = regex; | ||
207 | h->dht = dht; | ||
208 | h->stats = stats; | ||
209 | h->priv = priv; | ||
210 | h->dfa = REGEX_INTERNAL_construct_dfa (regex, strlen (regex), compression); | ||
211 | REGEX_INTERNAL_reannounce (h); | ||
212 | return h; | ||
213 | } | ||
214 | |||
215 | |||
216 | /** | ||
217 | * Announce again a regular expression previously announced. | ||
218 | * Does use caching to speed up process. | ||
219 | * | ||
220 | * @param h Handle returned by a previous #REGEX_INTERNAL_announce call(). | ||
221 | */ | ||
222 | void | ||
223 | REGEX_INTERNAL_reannounce (struct REGEX_INTERNAL_Announcement *h) | ||
224 | { | ||
225 | GNUNET_assert (NULL != h->dfa); /* make sure to call announce first */ | ||
226 | LOG (GNUNET_ERROR_TYPE_INFO, | ||
227 | "REGEX_INTERNAL_reannounce: %s\n", | ||
228 | h->regex); | ||
229 | REGEX_INTERNAL_iterate_reachable_edges (h->dfa, | ||
230 | ®ex_iterator, | ||
231 | h); | ||
232 | } | ||
233 | |||
234 | |||
235 | /** | ||
236 | * Clear all cached data used by a regex announce. | ||
237 | * Does not close DHT connection. | ||
238 | * | ||
239 | * @param h Handle returned by a previous #REGEX_INTERNAL_announce() call. | ||
240 | */ | ||
241 | void | ||
242 | REGEX_INTERNAL_announce_cancel (struct REGEX_INTERNAL_Announcement *h) | ||
243 | { | ||
244 | REGEX_INTERNAL_automaton_destroy (h->dfa); | ||
245 | GNUNET_free (h); | ||
246 | } | ||
247 | |||
248 | |||
249 | /******************************************************************************/ | ||
250 | |||
251 | |||
252 | /** | ||
253 | * Struct to keep state of running searches that have consumed a part of | ||
254 | * the inital string. | ||
255 | */ | ||
256 | struct RegexSearchContext | ||
257 | { | ||
258 | /** | ||
259 | * Part of the description already consumed by | ||
260 | * this particular search branch. | ||
261 | */ | ||
262 | size_t position; | ||
263 | |||
264 | /** | ||
265 | * Information about the search. | ||
266 | */ | ||
267 | struct REGEX_INTERNAL_Search *info; | ||
268 | |||
269 | /** | ||
270 | * We just want to look for one edge, the longer the better. | ||
271 | * Keep its length. | ||
272 | */ | ||
273 | unsigned int longest_match; | ||
274 | |||
275 | /** | ||
276 | * Destination hash of the longest match. | ||
277 | */ | ||
278 | struct GNUNET_HashCode hash; | ||
279 | }; | ||
280 | |||
281 | |||
282 | /** | ||
283 | * Type of values in `dht_get_results`. | ||
284 | */ | ||
285 | struct Result | ||
286 | { | ||
287 | /** | ||
288 | * Number of bytes in data. | ||
289 | */ | ||
290 | size_t size; | ||
291 | |||
292 | /** | ||
293 | * The raw result data. | ||
294 | */ | ||
295 | const void *data; | ||
296 | }; | ||
297 | |||
298 | |||
299 | /** | ||
300 | * Struct to keep information of searches of services described by a regex | ||
301 | * using a user-provided string service description. | ||
302 | */ | ||
303 | struct REGEX_INTERNAL_Search | ||
304 | { | ||
305 | /** | ||
306 | * DHT handle to use, must be initialized externally. | ||
307 | */ | ||
308 | struct GNUNET_DHT_Handle *dht; | ||
309 | |||
310 | /** | ||
311 | * Optional statistics handle to report usage. Can be NULL. | ||
312 | */ | ||
313 | struct GNUNET_STATISTICS_Handle *stats; | ||
314 | |||
315 | /** | ||
316 | * User provided description of the searched service. | ||
317 | */ | ||
318 | char *description; | ||
319 | |||
320 | /** | ||
321 | * Running DHT GETs. | ||
322 | */ | ||
323 | struct GNUNET_CONTAINER_MultiHashMap *dht_get_handles; | ||
324 | |||
325 | /** | ||
326 | * Results from running DHT GETs, values are of type | ||
327 | * 'struct Result'. | ||
328 | */ | ||
329 | struct GNUNET_CONTAINER_MultiHashMap *dht_get_results; | ||
330 | |||
331 | /** | ||
332 | * Contexts, for each running DHT GET. Free all on end of search. | ||
333 | */ | ||
334 | struct RegexSearchContext **contexts; | ||
335 | |||
336 | /** | ||
337 | * Number of contexts (branches/steps in search). | ||
338 | */ | ||
339 | unsigned int n_contexts; | ||
340 | |||
341 | /** | ||
342 | * @param callback Callback for found peers. | ||
343 | */ | ||
344 | REGEX_INTERNAL_Found callback; | ||
345 | |||
346 | /** | ||
347 | * @param callback_cls Closure for @c callback. | ||
348 | */ | ||
349 | void *callback_cls; | ||
350 | }; | ||
351 | |||
352 | |||
353 | /** | ||
354 | * Jump to the next edge, with the longest matching token. | ||
355 | * | ||
356 | * @param block Block found in the DHT. | ||
357 | * @param size Size of the block. | ||
358 | * @param ctx Context of the search. | ||
359 | */ | ||
360 | static void | ||
361 | regex_next_edge (const struct RegexBlock *block, | ||
362 | size_t size, | ||
363 | struct RegexSearchContext *ctx); | ||
364 | |||
365 | |||
366 | /** | ||
367 | * Function to process DHT string to regex matching. | ||
368 | * Called on each result obtained for the DHT search. | ||
369 | * | ||
370 | * @param cls Closure (search context). | ||
371 | * @param exp When will this value expire. | ||
372 | * @param key Key of the result. | ||
373 | * @param get_path Path of the get request. | ||
374 | * @param get_path_length Lenght of get_path. | ||
375 | * @param put_path Path of the put request. | ||
376 | * @param put_path_length Length of the put_path. | ||
377 | * @param type Type of the result. | ||
378 | * @param size Number of bytes in data. | ||
379 | * @param data Pointer to the result data. | ||
380 | */ | ||
381 | static void | ||
382 | dht_get_string_accept_handler (void *cls, struct GNUNET_TIME_Absolute exp, | ||
383 | const struct GNUNET_HashCode *key, | ||
384 | const struct GNUNET_PeerIdentity *get_path, | ||
385 | unsigned int get_path_length, | ||
386 | const struct GNUNET_PeerIdentity *put_path, | ||
387 | unsigned int put_path_length, | ||
388 | enum GNUNET_BLOCK_Type type, | ||
389 | size_t size, const void *data) | ||
390 | { | ||
391 | const struct RegexAcceptBlock *block = data; | ||
392 | struct RegexSearchContext *ctx = cls; | ||
393 | struct REGEX_INTERNAL_Search *info = ctx->info; | ||
394 | |||
395 | LOG (GNUNET_ERROR_TYPE_DEBUG, | ||
396 | "Regex result accept for %s (key %s)\n", | ||
397 | info->description, GNUNET_h2s(key)); | ||
398 | |||
399 | GNUNET_STATISTICS_update (info->stats, | ||
400 | "# regex accepting blocks found", | ||
401 | 1, GNUNET_NO); | ||
402 | GNUNET_STATISTICS_update (info->stats, | ||
403 | "# regex accepting block bytes found", | ||
404 | size, GNUNET_NO); | ||
405 | info->callback (info->callback_cls, | ||
406 | &block->peer, | ||
407 | get_path, get_path_length, | ||
408 | put_path, put_path_length); | ||
409 | } | ||
410 | |||
411 | |||
412 | /** | ||
413 | * Find a path to a peer that offers a regex service compatible | ||
414 | * with a given string. | ||
415 | * | ||
416 | * @param key The key of the accepting state. | ||
417 | * @param ctx Context containing info about the string, tunnel, etc. | ||
418 | */ | ||
419 | static void | ||
420 | regex_find_path (const struct GNUNET_HashCode *key, | ||
421 | struct RegexSearchContext *ctx) | ||
422 | { | ||
423 | struct GNUNET_DHT_GetHandle *get_h; | ||
424 | |||
425 | LOG (GNUNET_ERROR_TYPE_DEBUG, | ||
426 | "Accept state found, now searching for paths to %s\n", | ||
427 | GNUNET_h2s (key), | ||
428 | (unsigned int) ctx->position); | ||
429 | get_h = GNUNET_DHT_get_start (ctx->info->dht, /* handle */ | ||
430 | GNUNET_BLOCK_TYPE_REGEX_ACCEPT, /* type */ | ||
431 | key, /* key to search */ | ||
432 | DHT_REPLICATION, /* replication level */ | ||
433 | DHT_OPT | GNUNET_DHT_RO_RECORD_ROUTE, | ||
434 | NULL, /* xquery */ // FIXME BLOOMFILTER | ||
435 | 0, /* xquery bits */ // FIXME BLOOMFILTER SIZE | ||
436 | &dht_get_string_accept_handler, ctx); | ||
437 | GNUNET_break (GNUNET_OK == | ||
438 | GNUNET_CONTAINER_multihashmap_put(ctx->info->dht_get_handles, | ||
439 | key, | ||
440 | get_h, | ||
441 | GNUNET_CONTAINER_MULTIHASHMAPOPTION_MULTIPLE)); | ||
442 | } | ||
443 | |||
444 | |||
445 | /** | ||
446 | * Function to process DHT string to regex matching. | ||
447 | * Called on each result obtained for the DHT search. | ||
448 | * | ||
449 | * @param cls closure (search context) | ||
450 | * @param exp when will this value expire | ||
451 | * @param key key of the result | ||
452 | * @param get_path path of the get request (not used) | ||
453 | * @param get_path_length length of @a get_path (not used) | ||
454 | * @param put_path path of the put request (not used) | ||
455 | * @param put_path_length length of the @a put_path (not used) | ||
456 | * @param type type of the result | ||
457 | * @param size number of bytes in data | ||
458 | * @param data pointer to the result data | ||
459 | * | ||
460 | * TODO: re-issue the request after certain time? cancel after X results? | ||
461 | */ | ||
462 | static void | ||
463 | dht_get_string_handler (void *cls, struct GNUNET_TIME_Absolute exp, | ||
464 | const struct GNUNET_HashCode *key, | ||
465 | const struct GNUNET_PeerIdentity *get_path, | ||
466 | unsigned int get_path_length, | ||
467 | const struct GNUNET_PeerIdentity *put_path, | ||
468 | unsigned int put_path_length, | ||
469 | enum GNUNET_BLOCK_Type type, | ||
470 | size_t size, const void *data) | ||
471 | { | ||
472 | const struct RegexBlock *block = data; | ||
473 | struct RegexSearchContext *ctx = cls; | ||
474 | struct REGEX_INTERNAL_Search *info = ctx->info; | ||
475 | size_t len; | ||
476 | struct Result *copy; | ||
477 | |||
478 | LOG (GNUNET_ERROR_TYPE_INFO, | ||
479 | "DHT GET result for %s (%s)\n", | ||
480 | GNUNET_h2s (key), ctx->info->description); | ||
481 | copy = GNUNET_malloc (sizeof (struct Result) + size); | ||
482 | copy->size = size; | ||
483 | copy->data = ©[1]; | ||
484 | GNUNET_memcpy (©[1], block, size); | ||
485 | GNUNET_break (GNUNET_OK == | ||
486 | GNUNET_CONTAINER_multihashmap_put (info->dht_get_results, | ||
487 | key, copy, | ||
488 | GNUNET_CONTAINER_MULTIHASHMAPOPTION_MULTIPLE)); | ||
489 | len = strlen (info->description); | ||
490 | if (len == ctx->position) // String processed | ||
491 | { | ||
492 | if (GNUNET_YES == GNUNET_BLOCK_is_accepting (block, size)) | ||
493 | { | ||
494 | regex_find_path (key, ctx); | ||
495 | } | ||
496 | else | ||
497 | { | ||
498 | LOG (GNUNET_ERROR_TYPE_INFO, "block not accepting!\n"); | ||
499 | /* FIXME REGEX this block not successful, wait for more? start timeout? */ | ||
500 | } | ||
501 | return; | ||
502 | } | ||
503 | regex_next_edge (block, size, ctx); | ||
504 | } | ||
505 | |||
506 | |||
507 | /** | ||
508 | * Iterator over found existing cadet regex blocks that match an ongoing search. | ||
509 | * | ||
510 | * @param cls Closure (current context)- | ||
511 | * @param key Current key code (key for cached block). | ||
512 | * @param value Value in the hash map (cached RegexBlock). | ||
513 | * @return #GNUNET_YES: we should always continue to iterate. | ||
514 | */ | ||
515 | static int | ||
516 | regex_result_iterator (void *cls, | ||
517 | const struct GNUNET_HashCode * key, | ||
518 | void *value) | ||
519 | { | ||
520 | struct Result *result = value; | ||
521 | const struct RegexBlock *block = result->data; | ||
522 | struct RegexSearchContext *ctx = cls; | ||
523 | |||
524 | if ( (GNUNET_YES == | ||
525 | GNUNET_BLOCK_is_accepting (block, result->size)) && | ||
526 | (ctx->position == strlen (ctx->info->description)) ) | ||
527 | { | ||
528 | LOG (GNUNET_ERROR_TYPE_INFO, | ||
529 | "Found accepting known block\n"); | ||
530 | regex_find_path (key, ctx); | ||
531 | return GNUNET_YES; // We found an accept state! | ||
532 | } | ||
533 | LOG (GNUNET_ERROR_TYPE_DEBUG, | ||
534 | "* %u, %u, [%u]\n", | ||
535 | ctx->position, | ||
536 | strlen (ctx->info->description), | ||
537 | GNUNET_BLOCK_is_accepting (block, result->size)); | ||
538 | regex_next_edge (block, result->size, ctx); | ||
539 | |||
540 | GNUNET_STATISTICS_update (ctx->info->stats, "# regex cadet blocks iterated", | ||
541 | 1, GNUNET_NO); | ||
542 | |||
543 | return GNUNET_YES; | ||
544 | } | ||
545 | |||
546 | |||
547 | /** | ||
548 | * Iterator over edges in a regex block retrieved from the DHT. | ||
549 | * | ||
550 | * @param cls Closure (context of the search). | ||
551 | * @param token Token that follows to next state. | ||
552 | * @param len Lenght of token. | ||
553 | * @param key Hash of next state. | ||
554 | * @return #GNUNET_YES if should keep iterating, #GNUNET_NO otherwise. | ||
555 | */ | ||
556 | static int | ||
557 | regex_edge_iterator (void *cls, | ||
558 | const char *token, | ||
559 | size_t len, | ||
560 | const struct GNUNET_HashCode *key) | ||
561 | { | ||
562 | struct RegexSearchContext *ctx = cls; | ||
563 | struct REGEX_INTERNAL_Search *info = ctx->info; | ||
564 | const char *current; | ||
565 | size_t current_len; | ||
566 | |||
567 | GNUNET_STATISTICS_update (info->stats, "# regex edges iterated", | ||
568 | 1, GNUNET_NO); | ||
569 | current = &info->description[ctx->position]; | ||
570 | current_len = strlen (info->description) - ctx->position; | ||
571 | if (len > current_len) | ||
572 | { | ||
573 | LOG (GNUNET_ERROR_TYPE_DEBUG, "Token too long, END\n"); | ||
574 | return GNUNET_YES; | ||
575 | } | ||
576 | if (0 != strncmp (current, token, len)) | ||
577 | { | ||
578 | LOG (GNUNET_ERROR_TYPE_DEBUG, "Token doesn't match, END\n"); | ||
579 | return GNUNET_YES; | ||
580 | } | ||
581 | |||
582 | if (len > ctx->longest_match) | ||
583 | { | ||
584 | LOG (GNUNET_ERROR_TYPE_DEBUG, "Token is longer, KEEP\n"); | ||
585 | ctx->longest_match = len; | ||
586 | ctx->hash = *key; | ||
587 | } | ||
588 | else | ||
589 | { | ||
590 | LOG (GNUNET_ERROR_TYPE_DEBUG, "Token is not longer, IGNORE\n"); | ||
591 | } | ||
592 | |||
593 | LOG (GNUNET_ERROR_TYPE_DEBUG, "* End of regex edge iterator\n"); | ||
594 | return GNUNET_YES; | ||
595 | } | ||
596 | |||
597 | |||
598 | /** | ||
599 | * Jump to the next edge, with the longest matching token. | ||
600 | * | ||
601 | * @param block Block found in the DHT. | ||
602 | * @param size Size of the block. | ||
603 | * @param ctx Context of the search. | ||
604 | */ | ||
605 | static void | ||
606 | regex_next_edge (const struct RegexBlock *block, | ||
607 | size_t size, | ||
608 | struct RegexSearchContext *ctx) | ||
609 | { | ||
610 | struct RegexSearchContext *new_ctx; | ||
611 | struct REGEX_INTERNAL_Search *info = ctx->info; | ||
612 | struct GNUNET_DHT_GetHandle *get_h; | ||
613 | struct GNUNET_HashCode *hash; | ||
614 | const char *rest; | ||
615 | int result; | ||
616 | |||
617 | LOG (GNUNET_ERROR_TYPE_DEBUG, "Next edge\n"); | ||
618 | /* Find the longest match for the current string position, | ||
619 | * among tokens in the given block */ | ||
620 | ctx->longest_match = 0; | ||
621 | result = REGEX_BLOCK_iterate (block, size, | ||
622 | ®ex_edge_iterator, ctx); | ||
623 | GNUNET_break (GNUNET_OK == result); | ||
624 | |||
625 | /* Did anything match? */ | ||
626 | if (0 == ctx->longest_match) | ||
627 | { | ||
628 | LOG (GNUNET_ERROR_TYPE_DEBUG, | ||
629 | "no match in block\n"); | ||
630 | return; | ||
631 | } | ||
632 | |||
633 | hash = &ctx->hash; | ||
634 | new_ctx = GNUNET_new (struct RegexSearchContext); | ||
635 | new_ctx->info = info; | ||
636 | new_ctx->position = ctx->position + ctx->longest_match; | ||
637 | GNUNET_array_append (info->contexts, info->n_contexts, new_ctx); | ||
638 | |||
639 | /* Check whether we already have a DHT GET running for it */ | ||
640 | if (GNUNET_YES == | ||
641 | GNUNET_CONTAINER_multihashmap_contains (info->dht_get_handles, hash)) | ||
642 | { | ||
643 | LOG (GNUNET_ERROR_TYPE_DEBUG, | ||
644 | "GET for %s running, END\n", | ||
645 | GNUNET_h2s (hash)); | ||
646 | GNUNET_CONTAINER_multihashmap_get_multiple (info->dht_get_results, | ||
647 | hash, | ||
648 | ®ex_result_iterator, | ||
649 | new_ctx); | ||
650 | return; /* We are already looking for it */ | ||
651 | } | ||
652 | |||
653 | GNUNET_STATISTICS_update (info->stats, "# regex nodes traversed", | ||
654 | 1, GNUNET_NO); | ||
655 | |||
656 | LOG (GNUNET_ERROR_TYPE_DEBUG, | ||
657 | "Following edges at %s for offset %u in `%s'\n", | ||
658 | GNUNET_h2s (hash), | ||
659 | (unsigned int) ctx->position, | ||
660 | info->description); | ||
661 | rest = &new_ctx->info->description[new_ctx->position]; | ||
662 | get_h = | ||
663 | GNUNET_DHT_get_start (info->dht, /* handle */ | ||
664 | GNUNET_BLOCK_TYPE_REGEX, /* type */ | ||
665 | hash, /* key to search */ | ||
666 | DHT_REPLICATION, /* replication level */ | ||
667 | DHT_OPT, | ||
668 | rest, /* xquery */ | ||
669 | strlen (rest) + 1, /* xquery bits */ | ||
670 | &dht_get_string_handler, new_ctx); | ||
671 | if (GNUNET_OK != | ||
672 | GNUNET_CONTAINER_multihashmap_put(info->dht_get_handles, | ||
673 | hash, | ||
674 | get_h, | ||
675 | GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_FAST)) | ||
676 | { | ||
677 | GNUNET_break (0); | ||
678 | return; | ||
679 | } | ||
680 | } | ||
681 | |||
682 | |||
683 | /** | ||
684 | * Search for a peer offering a regex matching certain string in the DHT. | ||
685 | * The search runs until #REGEX_INTERNAL_search_cancel() is called, even if results | ||
686 | * are returned. | ||
687 | * | ||
688 | * @param dht An existing and valid DHT service handle. | ||
689 | * @param string String to match against the regexes in the DHT. | ||
690 | * @param callback Callback for found peers. | ||
691 | * @param callback_cls Closure for @c callback. | ||
692 | * @param stats Optional statistics handle to report usage. Can be NULL. | ||
693 | * @return Handle to stop search and free resources. | ||
694 | * Must be freed by calling #REGEX_INTERNAL_search_cancel(). | ||
695 | */ | ||
696 | struct REGEX_INTERNAL_Search * | ||
697 | REGEX_INTERNAL_search (struct GNUNET_DHT_Handle *dht, | ||
698 | const char *string, | ||
699 | REGEX_INTERNAL_Found callback, | ||
700 | void *callback_cls, | ||
701 | struct GNUNET_STATISTICS_Handle *stats) | ||
702 | { | ||
703 | struct REGEX_INTERNAL_Search *h; | ||
704 | struct GNUNET_DHT_GetHandle *get_h; | ||
705 | struct RegexSearchContext *ctx; | ||
706 | struct GNUNET_HashCode key; | ||
707 | size_t size; | ||
708 | size_t len; | ||
709 | |||
710 | /* Initialize handle */ | ||
711 | GNUNET_assert (NULL != dht); | ||
712 | GNUNET_assert (NULL != callback); | ||
713 | h = GNUNET_new (struct REGEX_INTERNAL_Search); | ||
714 | h->dht = dht; | ||
715 | h->description = GNUNET_strdup (string); | ||
716 | h->callback = callback; | ||
717 | h->callback_cls = callback_cls; | ||
718 | h->stats = stats; | ||
719 | h->dht_get_handles = GNUNET_CONTAINER_multihashmap_create (32, GNUNET_NO); | ||
720 | h->dht_get_results = GNUNET_CONTAINER_multihashmap_create (32, GNUNET_NO); | ||
721 | |||
722 | /* Initialize context */ | ||
723 | len = strlen (string); | ||
724 | size = REGEX_INTERNAL_get_first_key (string, len, &key); | ||
725 | LOG (GNUNET_ERROR_TYPE_INFO, | ||
726 | "Initial key for `%s' is %s (based on `%.*s')\n", | ||
727 | string, | ||
728 | GNUNET_h2s (&key), | ||
729 | size, | ||
730 | string); | ||
731 | ctx = GNUNET_new (struct RegexSearchContext); | ||
732 | ctx->position = size; | ||
733 | ctx->info = h; | ||
734 | GNUNET_array_append (h->contexts, | ||
735 | h->n_contexts, | ||
736 | ctx); | ||
737 | /* Start search in DHT */ | ||
738 | get_h = GNUNET_DHT_get_start (h->dht, /* handle */ | ||
739 | GNUNET_BLOCK_TYPE_REGEX, /* type */ | ||
740 | &key, /* key to search */ | ||
741 | DHT_REPLICATION, /* replication level */ | ||
742 | DHT_OPT, | ||
743 | &h->description[size], /* xquery */ | ||
744 | // FIXME add BLOOMFILTER to exclude filtered peers | ||
745 | len + 1 - size, /* xquery bits */ | ||
746 | // FIXME add BLOOMFILTER SIZE | ||
747 | &dht_get_string_handler, ctx); | ||
748 | GNUNET_break ( | ||
749 | GNUNET_OK == | ||
750 | GNUNET_CONTAINER_multihashmap_put (h->dht_get_handles, | ||
751 | &key, | ||
752 | get_h, | ||
753 | GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_FAST) | ||
754 | ); | ||
755 | |||
756 | return h; | ||
757 | } | ||
758 | |||
759 | |||
760 | /** | ||
761 | * Iterator over hash map entries to cancel DHT GET requests after a | ||
762 | * successful connect_by_string. | ||
763 | * | ||
764 | * @param cls Closure (unused). | ||
765 | * @param key Current key code (unused). | ||
766 | * @param value Value in the hash map (get handle). | ||
767 | * @return #GNUNET_YES if we should continue to iterate, | ||
768 | * #GNUNET_NO if not. | ||
769 | */ | ||
770 | static int | ||
771 | regex_cancel_dht_get (void *cls, | ||
772 | const struct GNUNET_HashCode * key, | ||
773 | void *value) | ||
774 | { | ||
775 | struct GNUNET_DHT_GetHandle *h = value; | ||
776 | |||
777 | GNUNET_DHT_get_stop (h); | ||
778 | return GNUNET_YES; | ||
779 | } | ||
780 | |||
781 | |||
782 | /** | ||
783 | * Iterator over hash map entries to free CadetRegexBlocks stored during the | ||
784 | * search for connect_by_string. | ||
785 | * | ||
786 | * @param cls Closure (unused). | ||
787 | * @param key Current key code (unused). | ||
788 | * @param value CadetRegexBlock in the hash map. | ||
789 | * @return #GNUNET_YES if we should continue to iterate, | ||
790 | * #GNUNET_NO if not. | ||
791 | */ | ||
792 | static int | ||
793 | regex_free_result (void *cls, | ||
794 | const struct GNUNET_HashCode * key, | ||
795 | void *value) | ||
796 | { | ||
797 | GNUNET_free (value); | ||
798 | return GNUNET_YES; | ||
799 | } | ||
800 | |||
801 | |||
802 | /** | ||
803 | * Cancel an ongoing regex search in the DHT and free all resources. | ||
804 | * | ||
805 | * @param h the search context. | ||
806 | */ | ||
807 | void | ||
808 | REGEX_INTERNAL_search_cancel (struct REGEX_INTERNAL_Search *h) | ||
809 | { | ||
810 | unsigned int i; | ||
811 | |||
812 | GNUNET_free (h->description); | ||
813 | GNUNET_CONTAINER_multihashmap_iterate (h->dht_get_handles, | ||
814 | ®ex_cancel_dht_get, NULL); | ||
815 | GNUNET_CONTAINER_multihashmap_iterate (h->dht_get_results, | ||
816 | ®ex_free_result, NULL); | ||
817 | GNUNET_CONTAINER_multihashmap_destroy (h->dht_get_results); | ||
818 | GNUNET_CONTAINER_multihashmap_destroy (h->dht_get_handles); | ||
819 | if (0 < h->n_contexts) | ||
820 | { | ||
821 | for (i = 0; i < h->n_contexts; i++) | ||
822 | GNUNET_free (h->contexts[i]); | ||
823 | GNUNET_free (h->contexts); | ||
824 | } | ||
825 | GNUNET_free (h); | ||
826 | } | ||
827 | |||
828 | |||
829 | /* end of regex_internal_dht.c */ | ||
diff --git a/src/regex/regex_internal_lib.h b/src/regex/regex_internal_lib.h new file mode 100644 index 000000000..dc194546d --- /dev/null +++ b/src/regex/regex_internal_lib.h | |||
@@ -0,0 +1,267 @@ | |||
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 | /** | ||
19 | * @file regex/regex_internal_lib.h | ||
20 | * @brief library to parse regular expressions into dfa | ||
21 | * @author Maximilian Szengel | ||
22 | */ | ||
23 | |||
24 | #ifndef REGEX_INTERNAL_LIB_H | ||
25 | #define REGEX_INTERNAL_LIB_H | ||
26 | |||
27 | #include "gnunet_util_lib.h" | ||
28 | #include "gnunet_dht_service.h" | ||
29 | #include "gnunet_statistics_service.h" | ||
30 | #include "regex_block_lib.h" | ||
31 | |||
32 | #ifdef __cplusplus | ||
33 | extern "C" | ||
34 | { | ||
35 | #if 0 /* keep Emacsens' auto-indent happy */ | ||
36 | } | ||
37 | #endif | ||
38 | #endif | ||
39 | |||
40 | |||
41 | /** | ||
42 | * Automaton (NFA/DFA) representation. | ||
43 | */ | ||
44 | struct REGEX_INTERNAL_Automaton; | ||
45 | |||
46 | |||
47 | /** | ||
48 | * Construct DFA for the given 'regex' of length 'len'. | ||
49 | * | ||
50 | * Path compression means, that for example a DFA o -> a -> b -> c -> o will be | ||
51 | * compressed to o -> abc -> o. Note that this parameter influences the | ||
52 | * non-determinism of states of the resulting NFA in the DHT (number of outgoing | ||
53 | * edges with the same label). For example for an application that stores IPv4 | ||
54 | * addresses as bitstrings it could make sense to limit the path compression to | ||
55 | * 4 or 8. | ||
56 | * | ||
57 | * @param regex regular expression string. | ||
58 | * @param len length of the regular expression. | ||
59 | * @param max_path_len limit the path compression length to the | ||
60 | * given value. If set to 1, no path compression is applied. Set to 0 for | ||
61 | * maximal possible path compression (generally not desireable). | ||
62 | * @return DFA, needs to be freed using #REGEX_INTERNAL_automaton_destroy(). | ||
63 | */ | ||
64 | struct REGEX_INTERNAL_Automaton * | ||
65 | REGEX_INTERNAL_construct_dfa (const char *regex, | ||
66 | const size_t len, | ||
67 | unsigned int max_path_len); | ||
68 | |||
69 | |||
70 | /** | ||
71 | * Free the memory allocated by constructing the REGEX_INTERNAL_Automaton. | ||
72 | * data structure. | ||
73 | * | ||
74 | * @param a automaton to be destroyed. | ||
75 | */ | ||
76 | void | ||
77 | REGEX_INTERNAL_automaton_destroy (struct REGEX_INTERNAL_Automaton *a); | ||
78 | |||
79 | |||
80 | /** | ||
81 | * Evaluates the given 'string' against the given compiled regex. | ||
82 | * | ||
83 | * @param a automaton. | ||
84 | * @param string string to check. | ||
85 | * | ||
86 | * @return 0 if string matches, non 0 otherwise. | ||
87 | */ | ||
88 | int | ||
89 | REGEX_INTERNAL_eval (struct REGEX_INTERNAL_Automaton *a, | ||
90 | const char *string); | ||
91 | |||
92 | |||
93 | /** | ||
94 | * Get the first key for the given @a input_string. This hashes | ||
95 | * the first x bits of the @a input_string. | ||
96 | * | ||
97 | * @param input_string string. | ||
98 | * @param string_len length of the @a input_string. | ||
99 | * @param key pointer to where to write the hash code. | ||
100 | * @return number of bits of @a input_string that have been consumed | ||
101 | * to construct the key | ||
102 | */ | ||
103 | size_t | ||
104 | REGEX_INTERNAL_get_first_key (const char *input_string, | ||
105 | size_t string_len, | ||
106 | struct GNUNET_HashCode * key); | ||
107 | |||
108 | |||
109 | /** | ||
110 | * Iterator callback function. | ||
111 | * | ||
112 | * @param cls closure. | ||
113 | * @param key hash for current state. | ||
114 | * @param proof proof for current state | ||
115 | * @param accepting #GNUNET_YES if this is an accepting state, #GNUNET_NO if not. | ||
116 | * @param num_edges number of edges leaving current state. | ||
117 | * @param edges edges leaving current state. | ||
118 | */ | ||
119 | typedef void | ||
120 | (*REGEX_INTERNAL_KeyIterator)(void *cls, | ||
121 | const struct GNUNET_HashCode *key, | ||
122 | const char *proof, | ||
123 | int accepting, | ||
124 | unsigned int num_edges, | ||
125 | const struct REGEX_BLOCK_Edge *edges); | ||
126 | |||
127 | |||
128 | /** | ||
129 | * Iterate over all edges starting from start state of automaton 'a'. Calling | ||
130 | * iterator for each edge. | ||
131 | * | ||
132 | * @param a automaton. | ||
133 | * @param iterator iterator called for each edge. | ||
134 | * @param iterator_cls closure. | ||
135 | */ | ||
136 | void | ||
137 | REGEX_INTERNAL_iterate_all_edges (struct REGEX_INTERNAL_Automaton *a, | ||
138 | REGEX_INTERNAL_KeyIterator iterator, | ||
139 | void *iterator_cls); | ||
140 | |||
141 | |||
142 | /** | ||
143 | * Iterate over all edges of automaton 'a' that are reachable from a state with | ||
144 | * a proof of at least #GNUNET_REGEX_INITIAL_BYTES characters. | ||
145 | * | ||
146 | * Call the iterator for each such edge. | ||
147 | * | ||
148 | * @param a automaton. | ||
149 | * @param iterator iterator called for each reachable edge. | ||
150 | * @param iterator_cls closure. | ||
151 | */ | ||
152 | void | ||
153 | REGEX_INTERNAL_iterate_reachable_edges (struct REGEX_INTERNAL_Automaton *a, | ||
154 | REGEX_INTERNAL_KeyIterator iterator, | ||
155 | void *iterator_cls); | ||
156 | |||
157 | |||
158 | |||
159 | /** | ||
160 | * Handle to store cached data about a regex announce. | ||
161 | */ | ||
162 | struct REGEX_INTERNAL_Announcement; | ||
163 | |||
164 | /** | ||
165 | * Handle to store data about a regex search. | ||
166 | */ | ||
167 | struct REGEX_INTERNAL_Search; | ||
168 | |||
169 | |||
170 | /** | ||
171 | * Announce a regular expression: put all states of the automaton in the DHT. | ||
172 | * Does not free resources, must call #REGEX_INTERNAL_announce_cancel() for that. | ||
173 | * | ||
174 | * @param dht An existing and valid DHT service handle. CANNOT be NULL. | ||
175 | * @param priv our private key, must remain valid until the announcement is cancelled | ||
176 | * @param regex Regular expression to announce. | ||
177 | * @param compression How many characters per edge can we squeeze? | ||
178 | * @param stats Optional statistics handle to report usage. Can be NULL. | ||
179 | * @return Handle to reuse o free cached resources. | ||
180 | * Must be freed by calling #REGEX_INTERNAL_announce_cancel(). | ||
181 | */ | ||
182 | struct REGEX_INTERNAL_Announcement * | ||
183 | REGEX_INTERNAL_announce (struct GNUNET_DHT_Handle *dht, | ||
184 | const struct GNUNET_CRYPTO_EddsaPrivateKey *priv, | ||
185 | const char *regex, | ||
186 | uint16_t compression, | ||
187 | struct GNUNET_STATISTICS_Handle *stats); | ||
188 | |||
189 | |||
190 | /** | ||
191 | * Announce again a regular expression previously announced. | ||
192 | * Does use caching to speed up process. | ||
193 | * | ||
194 | * @param h Handle returned by a previous #REGEX_INTERNAL_announce() call. | ||
195 | */ | ||
196 | void | ||
197 | REGEX_INTERNAL_reannounce (struct REGEX_INTERNAL_Announcement *h); | ||
198 | |||
199 | |||
200 | /** | ||
201 | * Clear all cached data used by a regex announce. | ||
202 | * Does not close DHT connection. | ||
203 | * | ||
204 | * @param h Handle returned by a previous #REGEX_INTERNAL_announce() call. | ||
205 | */ | ||
206 | void | ||
207 | REGEX_INTERNAL_announce_cancel (struct REGEX_INTERNAL_Announcement *h); | ||
208 | |||
209 | |||
210 | /** | ||
211 | * Search callback function. | ||
212 | * | ||
213 | * @param cls Closure provided in #REGEX_INTERNAL_search(). | ||
214 | * @param id Peer providing a regex that matches the string. | ||
215 | * @param get_path Path of the get request. | ||
216 | * @param get_path_length Length of @a get_path. | ||
217 | * @param put_path Path of the put request. | ||
218 | * @param put_path_length Length of the @a put_path. | ||
219 | */ | ||
220 | typedef void | ||
221 | (*REGEX_INTERNAL_Found)(void *cls, | ||
222 | const struct GNUNET_PeerIdentity *id, | ||
223 | const struct GNUNET_PeerIdentity *get_path, | ||
224 | unsigned int get_path_length, | ||
225 | const struct GNUNET_PeerIdentity *put_path, | ||
226 | unsigned int put_path_length); | ||
227 | |||
228 | |||
229 | /** | ||
230 | * Search for a peer offering a regex matching certain string in the DHT. | ||
231 | * The search runs until #REGEX_INTERNAL_search_cancel() is called, even if results | ||
232 | * are returned. | ||
233 | * | ||
234 | * @param dht An existing and valid DHT service handle. | ||
235 | * @param string String to match against the regexes in the DHT. | ||
236 | * @param callback Callback for found peers. | ||
237 | * @param callback_cls Closure for @c callback. | ||
238 | * @param stats Optional statistics handle to report usage. Can be NULL. | ||
239 | * @return Handle to stop search and free resources. | ||
240 | * Must be freed by calling #REGEX_INTERNAL_search_cancel(). | ||
241 | */ | ||
242 | struct REGEX_INTERNAL_Search * | ||
243 | REGEX_INTERNAL_search (struct GNUNET_DHT_Handle *dht, | ||
244 | const char *string, | ||
245 | REGEX_INTERNAL_Found callback, | ||
246 | void *callback_cls, | ||
247 | struct GNUNET_STATISTICS_Handle *stats); | ||
248 | |||
249 | /** | ||
250 | * Stop search and free all data used by a #REGEX_INTERNAL_search() call. | ||
251 | * Does not close DHT connection. | ||
252 | * | ||
253 | * @param h Handle returned by a previous #REGEX_INTERNAL_search() call. | ||
254 | */ | ||
255 | void | ||
256 | REGEX_INTERNAL_search_cancel (struct REGEX_INTERNAL_Search *h); | ||
257 | |||
258 | |||
259 | #if 0 /* keep Emacsens' auto-indent happy */ | ||
260 | { | ||
261 | #endif | ||
262 | #ifdef __cplusplus | ||
263 | } | ||
264 | #endif | ||
265 | |||
266 | /* end of regex_internal_lib.h */ | ||
267 | #endif | ||
diff --git a/src/regex/regex_ipc.h b/src/regex/regex_ipc.h new file mode 100644 index 000000000..71ac273cd --- /dev/null +++ b/src/regex/regex_ipc.h | |||
@@ -0,0 +1,105 @@ | |||
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 | /** | ||
19 | * @file regex/regex_ipc.h | ||
20 | * @brief regex IPC messages (not called 'regex.h' due to conflict with | ||
21 | * system headers) | ||
22 | * @author Christian Grothoff | ||
23 | */ | ||
24 | #ifndef REGEX_IPC_H | ||
25 | #define REGEX_IPC_H | ||
26 | |||
27 | #include "gnunet_util_lib.h" | ||
28 | |||
29 | /** | ||
30 | * Request for regex service to announce capability. | ||
31 | */ | ||
32 | struct AnnounceMessage | ||
33 | { | ||
34 | |||
35 | /** | ||
36 | * Type is GNUNET_MESSAGE_TYPE_REGEX_ANNOUNCE | ||
37 | */ | ||
38 | struct GNUNET_MessageHeader header; | ||
39 | |||
40 | /** | ||
41 | * How many characters can we squeeze per edge? | ||
42 | */ | ||
43 | uint16_t compression; | ||
44 | |||
45 | /** | ||
46 | * Always zero. | ||
47 | */ | ||
48 | uint16_t reserved; | ||
49 | |||
50 | /** | ||
51 | * Delay between repeated announcements. | ||
52 | */ | ||
53 | struct GNUNET_TIME_RelativeNBO refresh_delay; | ||
54 | |||
55 | /* followed by 0-terminated regex as string */ | ||
56 | }; | ||
57 | |||
58 | |||
59 | /** | ||
60 | * Message to initiate regex search. | ||
61 | */ | ||
62 | struct RegexSearchMessage | ||
63 | { | ||
64 | /** | ||
65 | * Type is GNUNET_MESSAGE_TYPE_REGEX_SEARCH | ||
66 | */ | ||
67 | struct GNUNET_MessageHeader header; | ||
68 | |||
69 | /* followed by 0-terminated search string */ | ||
70 | |||
71 | }; | ||
72 | |||
73 | |||
74 | /** | ||
75 | * Result from regex search. | ||
76 | */ | ||
77 | struct ResultMessage | ||
78 | { | ||
79 | /** | ||
80 | * Type is GNUNET_MESSAGE_TYPE_REGEX_RESULT | ||
81 | */ | ||
82 | struct GNUNET_MessageHeader header; | ||
83 | |||
84 | /** | ||
85 | * Number of entries in the GET path. | ||
86 | */ | ||
87 | uint16_t get_path_length; | ||
88 | |||
89 | /** | ||
90 | * Number of entries in the PUT path. | ||
91 | */ | ||
92 | uint16_t put_path_length; | ||
93 | |||
94 | /** | ||
95 | * Identity of the peer that was found. | ||
96 | */ | ||
97 | struct GNUNET_PeerIdentity id; | ||
98 | |||
99 | /* followed by GET path and PUT path arrays */ | ||
100 | |||
101 | }; | ||
102 | |||
103 | |||
104 | /* end of regex_ipc.h */ | ||
105 | #endif | ||
diff --git a/src/regex/regex_simulation_profiler_test.conf b/src/regex/regex_simulation_profiler_test.conf new file mode 100644 index 000000000..9384aa249 --- /dev/null +++ b/src/regex/regex_simulation_profiler_test.conf | |||
@@ -0,0 +1,7 @@ | |||
1 | [regex-mysql] | ||
2 | DATABASE = regex | ||
3 | USER = gnunet | ||
4 | PASSWORD = | ||
5 | HOST = localhost | ||
6 | PORT = 3306 | ||
7 | REGEX_PREFIX = GNVPN-0001-PAD | ||
diff --git a/src/regex/regex_test_graph.c b/src/regex/regex_test_graph.c new file mode 100644 index 000000000..d8f16e894 --- /dev/null +++ b/src/regex/regex_test_graph.c | |||
@@ -0,0 +1,316 @@ | |||
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 | /** | ||
19 | * @file src/regex/regex_test_graph.c | ||
20 | * @brief functions for creating .dot graphs from regexes | ||
21 | * @author Maximilian Szengel | ||
22 | */ | ||
23 | #include "platform.h" | ||
24 | #include "regex_internal_lib.h" | ||
25 | #include "regex_test_lib.h" | ||
26 | #include "regex_internal.h" | ||
27 | |||
28 | /** | ||
29 | * Context for graph creation. Passed as the cls to | ||
30 | * REGEX_TEST_automaton_save_graph_step. | ||
31 | */ | ||
32 | struct REGEX_TEST_Graph_Context | ||
33 | { | ||
34 | /** | ||
35 | * File pointer to the dot file used for output. | ||
36 | */ | ||
37 | FILE *filep; | ||
38 | |||
39 | /** | ||
40 | * Verbose flag, if it's set to GNUNET_YES additional info will be printed in | ||
41 | * the graph. | ||
42 | */ | ||
43 | int verbose; | ||
44 | |||
45 | /** | ||
46 | * Coloring flag, if set to GNUNET_YES SCCs will be colored. | ||
47 | */ | ||
48 | int coloring; | ||
49 | }; | ||
50 | |||
51 | |||
52 | /** | ||
53 | * Recursive function doing DFS with 'v' as a start, detecting all SCCs inside | ||
54 | * the subgraph reachable from 'v'. Used with scc_tarjan function to detect all | ||
55 | * SCCs inside an automaton. | ||
56 | * | ||
57 | * @param scc_counter counter for numbering the sccs | ||
58 | * @param v start vertex | ||
59 | * @param index current index | ||
60 | * @param stack stack for saving all SCCs | ||
61 | * @param stack_size current size of the stack | ||
62 | */ | ||
63 | static void | ||
64 | scc_tarjan_strongconnect (unsigned int *scc_counter, | ||
65 | struct REGEX_INTERNAL_State *v, unsigned int *index, | ||
66 | struct REGEX_INTERNAL_State **stack, | ||
67 | unsigned int *stack_size) | ||
68 | { | ||
69 | struct REGEX_INTERNAL_State *w; | ||
70 | struct REGEX_INTERNAL_Transition *t; | ||
71 | |||
72 | v->index = *index; | ||
73 | v->lowlink = *index; | ||
74 | (*index)++; | ||
75 | stack[(*stack_size)++] = v; | ||
76 | v->contained = 1; | ||
77 | |||
78 | for (t = v->transitions_head; NULL != t; t = t->next) | ||
79 | { | ||
80 | w = t->to_state; | ||
81 | |||
82 | if (NULL == w) | ||
83 | continue; | ||
84 | |||
85 | if (w->index < 0) | ||
86 | { | ||
87 | scc_tarjan_strongconnect (scc_counter, w, index, stack, stack_size); | ||
88 | v->lowlink = (v->lowlink > w->lowlink) ? w->lowlink : v->lowlink; | ||
89 | } | ||
90 | else if (1 == w->contained) | ||
91 | v->lowlink = (v->lowlink > w->index) ? w->index : v->lowlink; | ||
92 | } | ||
93 | |||
94 | if (v->lowlink == v->index) | ||
95 | { | ||
96 | (*scc_counter)++; | ||
97 | do | ||
98 | { | ||
99 | w = stack[--(*stack_size)]; | ||
100 | w->contained = 0; | ||
101 | w->scc_id = *scc_counter; | ||
102 | } | ||
103 | while (w != v); | ||
104 | } | ||
105 | } | ||
106 | |||
107 | |||
108 | /** | ||
109 | * Detect all SCCs (Strongly Connected Components) inside the given automaton. | ||
110 | * SCCs will be marked using the scc_id on each state. | ||
111 | * | ||
112 | * @param a the automaton for which SCCs should be computed and assigned. | ||
113 | */ | ||
114 | static void | ||
115 | scc_tarjan (struct REGEX_INTERNAL_Automaton *a) | ||
116 | { | ||
117 | unsigned int index; | ||
118 | unsigned int scc_counter; | ||
119 | struct REGEX_INTERNAL_State *v; | ||
120 | struct REGEX_INTERNAL_State *stack[a->state_count]; | ||
121 | unsigned int stack_size; | ||
122 | |||
123 | for (v = a->states_head; NULL != v; v = v->next) | ||
124 | { | ||
125 | v->contained = 0; | ||
126 | v->index = -1; | ||
127 | v->lowlink = -1; | ||
128 | } | ||
129 | |||
130 | stack_size = 0; | ||
131 | index = 0; | ||
132 | scc_counter = 0; | ||
133 | |||
134 | for (v = a->states_head; NULL != v; v = v->next) | ||
135 | { | ||
136 | if (v->index < 0) | ||
137 | scc_tarjan_strongconnect (&scc_counter, v, &index, stack, &stack_size); | ||
138 | } | ||
139 | } | ||
140 | |||
141 | |||
142 | /** | ||
143 | * Save a state to an open file pointer. cls is expected to be a file pointer to | ||
144 | * an open file. Used only in conjunction with | ||
145 | * REGEX_TEST_automaton_save_graph. | ||
146 | * | ||
147 | * @param cls file pointer. | ||
148 | * @param count current count of the state, not used. | ||
149 | * @param s state. | ||
150 | */ | ||
151 | void | ||
152 | REGEX_TEST_automaton_save_graph_step (void *cls, unsigned int count, | ||
153 | struct REGEX_INTERNAL_State *s) | ||
154 | { | ||
155 | struct REGEX_TEST_Graph_Context *ctx = cls; | ||
156 | struct REGEX_INTERNAL_Transition *ctran; | ||
157 | char *s_acc = NULL; | ||
158 | char *s_tran = NULL; | ||
159 | char *name; | ||
160 | char *to_name; | ||
161 | |||
162 | if (GNUNET_YES == ctx->verbose) | ||
163 | GNUNET_asprintf (&name, "%i (%s) (%s) (%s)", s->dfs_id, s->name, s->proof, | ||
164 | GNUNET_h2s (&s->hash)); | ||
165 | else | ||
166 | GNUNET_asprintf (&name, "%i", s->dfs_id); | ||
167 | |||
168 | if (s->accepting) | ||
169 | { | ||
170 | if (GNUNET_YES == ctx->coloring) | ||
171 | { | ||
172 | GNUNET_asprintf (&s_acc, | ||
173 | "\"%s\" [shape=doublecircle, color=\"0.%i 0.8 0.95\"];\n", | ||
174 | name, s->scc_id * s->scc_id); | ||
175 | } | ||
176 | else | ||
177 | { | ||
178 | GNUNET_asprintf (&s_acc, "\"%s\" [shape=doublecircle];\n", name, | ||
179 | s->scc_id); | ||
180 | } | ||
181 | } | ||
182 | else if (GNUNET_YES == ctx->coloring) | ||
183 | { | ||
184 | GNUNET_asprintf (&s_acc, | ||
185 | "\"%s\" [shape=circle, color=\"0.%i 0.8 0.95\"];\n", name, | ||
186 | s->scc_id * s->scc_id); | ||
187 | } | ||
188 | else | ||
189 | { | ||
190 | GNUNET_asprintf (&s_acc, "\"%s\" [shape=circle];\n", name, s->scc_id); | ||
191 | } | ||
192 | |||
193 | GNUNET_assert (NULL != s_acc); | ||
194 | |||
195 | fwrite (s_acc, strlen (s_acc), 1, ctx->filep); | ||
196 | GNUNET_free (s_acc); | ||
197 | s_acc = NULL; | ||
198 | |||
199 | for (ctran = s->transitions_head; NULL != ctran; ctran = ctran->next) | ||
200 | { | ||
201 | if (NULL == ctran->to_state) | ||
202 | { | ||
203 | GNUNET_log (GNUNET_ERROR_TYPE_ERROR, | ||
204 | "Transition from State %i has no state for transitioning\n", | ||
205 | s->id); | ||
206 | continue; | ||
207 | } | ||
208 | |||
209 | if (GNUNET_YES == ctx->verbose) | ||
210 | { | ||
211 | GNUNET_asprintf (&to_name, "%i (%s) (%s) (%s)", ctran->to_state->dfs_id, | ||
212 | ctran->to_state->name, ctran->to_state->proof, | ||
213 | GNUNET_h2s (&ctran->to_state->hash)); | ||
214 | } | ||
215 | else | ||
216 | GNUNET_asprintf (&to_name, "%i", ctran->to_state->dfs_id); | ||
217 | |||
218 | if (NULL == ctran->label) | ||
219 | { | ||
220 | if (GNUNET_YES == ctx->coloring) | ||
221 | { | ||
222 | GNUNET_asprintf (&s_tran, | ||
223 | "\"%s\" -> \"%s\" [label = \"ε\", color=\"0.%i 0.8 0.95\"];\n", | ||
224 | name, to_name, s->scc_id * s->scc_id); | ||
225 | } | ||
226 | else | ||
227 | { | ||
228 | GNUNET_asprintf (&s_tran, "\"%s\" -> \"%s\" [label = \"ε\"];\n", name, | ||
229 | to_name, s->scc_id); | ||
230 | } | ||
231 | } | ||
232 | else | ||
233 | { | ||
234 | if (GNUNET_YES == ctx->coloring) | ||
235 | { | ||
236 | GNUNET_asprintf (&s_tran, | ||
237 | "\"%s\" -> \"%s\" [label = \"%s\", color=\"0.%i 0.8 0.95\"];\n", | ||
238 | name, to_name, ctran->label, s->scc_id * s->scc_id); | ||
239 | } | ||
240 | else | ||
241 | { | ||
242 | GNUNET_asprintf (&s_tran, "\"%s\" -> \"%s\" [label = \"%s\"];\n", name, | ||
243 | to_name, ctran->label, s->scc_id); | ||
244 | } | ||
245 | } | ||
246 | |||
247 | GNUNET_free (to_name); | ||
248 | |||
249 | GNUNET_assert (NULL != s_tran); | ||
250 | |||
251 | fwrite (s_tran, strlen (s_tran), 1, ctx->filep); | ||
252 | GNUNET_free (s_tran); | ||
253 | s_tran = NULL; | ||
254 | } | ||
255 | |||
256 | GNUNET_free (name); | ||
257 | } | ||
258 | |||
259 | |||
260 | /** | ||
261 | * Save the given automaton as a GraphViz dot file. | ||
262 | * | ||
263 | * @param a the automaton to be saved. | ||
264 | * @param filename where to save the file. | ||
265 | * @param options options for graph generation that include coloring or verbose | ||
266 | * mode | ||
267 | */ | ||
268 | void | ||
269 | REGEX_TEST_automaton_save_graph (struct REGEX_INTERNAL_Automaton *a, | ||
270 | const char *filename, | ||
271 | enum REGEX_TEST_GraphSavingOptions options) | ||
272 | { | ||
273 | char *start; | ||
274 | char *end; | ||
275 | struct REGEX_TEST_Graph_Context ctx; | ||
276 | |||
277 | if (NULL == a) | ||
278 | { | ||
279 | GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Could not print NFA, was NULL!"); | ||
280 | return; | ||
281 | } | ||
282 | |||
283 | if (NULL == filename || strlen (filename) < 1) | ||
284 | { | ||
285 | GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "No Filename given!"); | ||
286 | return; | ||
287 | } | ||
288 | |||
289 | ctx.filep = fopen (filename, "w"); | ||
290 | ctx.verbose = | ||
291 | (0 == (options & REGEX_TEST_GRAPH_VERBOSE)) ? GNUNET_NO : GNUNET_YES; | ||
292 | ctx.coloring = | ||
293 | (0 == (options & REGEX_TEST_GRAPH_COLORING)) ? GNUNET_NO : GNUNET_YES; | ||
294 | |||
295 | if (NULL == ctx.filep) | ||
296 | { | ||
297 | GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Could not open file for writing: %s", | ||
298 | filename); | ||
299 | return; | ||
300 | } | ||
301 | |||
302 | /* First add the SCCs to the automaton, so we can color them nicely */ | ||
303 | if (GNUNET_YES == ctx.coloring) | ||
304 | scc_tarjan (a); | ||
305 | |||
306 | start = "digraph G {\nrankdir=LR\n"; | ||
307 | fwrite (start, strlen (start), 1, ctx.filep); | ||
308 | |||
309 | REGEX_INTERNAL_automaton_traverse (a, a->start, NULL, NULL, | ||
310 | ®EX_TEST_automaton_save_graph_step, | ||
311 | &ctx); | ||
312 | |||
313 | end = "\n}\n"; | ||
314 | fwrite (end, strlen (end), 1, ctx.filep); | ||
315 | fclose (ctx.filep); | ||
316 | } | ||
diff --git a/src/regex/regex_test_lib.c b/src/regex/regex_test_lib.c new file mode 100644 index 000000000..d25799ea4 --- /dev/null +++ b/src/regex/regex_test_lib.c | |||
@@ -0,0 +1,646 @@ | |||
1 | /* | ||
2 | * This file is part of GNUnet | ||
3 | * Copyright (C) 2012-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 | /** | ||
19 | * @file src/regex/regex_test_lib.c | ||
20 | * @brief library to read regexes representing IP networks from a file. | ||
21 | * and simplyfinying the into one big regex, in order to run | ||
22 | * tests (regex performance, cadet profiler). | ||
23 | * @author Bartlomiej Polot | ||
24 | */ | ||
25 | |||
26 | #include "platform.h" | ||
27 | #include "gnunet_util_lib.h" | ||
28 | |||
29 | |||
30 | /** | ||
31 | * Struct to hold the tree formed by prefix-combining the regexes. | ||
32 | */ | ||
33 | struct RegexCombineCtx | ||
34 | { | ||
35 | /** | ||
36 | * Child nodes with same prefix and token. | ||
37 | */ | ||
38 | struct RegexCombineCtx **children; | ||
39 | |||
40 | /** | ||
41 | * Alphabet size (how many @a children there are) | ||
42 | */ | ||
43 | unsigned int size; | ||
44 | |||
45 | /** | ||
46 | * Token. | ||
47 | */ | ||
48 | char *s; | ||
49 | }; | ||
50 | |||
51 | |||
52 | /** | ||
53 | * Char 2 int | ||
54 | * | ||
55 | * Convert a character into its int value depending on the base used | ||
56 | * | ||
57 | * @param c Char | ||
58 | * @param size base (2, 8 or 16(hex)) | ||
59 | * | ||
60 | * @return Int in range [0, (base-1)] | ||
61 | */ | ||
62 | static int | ||
63 | c2i (char c, int size) | ||
64 | { | ||
65 | switch (size) | ||
66 | { | ||
67 | case 2: | ||
68 | case 8: | ||
69 | return c - '0'; | ||
70 | break; | ||
71 | case 16: | ||
72 | if (c >= '0' && c <= '9') | ||
73 | return c - '0'; | ||
74 | else if (c >= 'A' && c <= 'F') | ||
75 | return c - 'A' + 10; | ||
76 | else if (c >= 'a' && c <= 'f') | ||
77 | return c - 'a' + 10; | ||
78 | else | ||
79 | { | ||
80 | GNUNET_log (GNUNET_ERROR_TYPE_ERROR, | ||
81 | "Cannot convert char %c in base %u\n", | ||
82 | c, size); | ||
83 | GNUNET_assert (0); | ||
84 | } | ||
85 | break; | ||
86 | default: | ||
87 | GNUNET_assert (0); | ||
88 | } | ||
89 | } | ||
90 | |||
91 | |||
92 | /** | ||
93 | * Printf spaces to indent the regex tree | ||
94 | * | ||
95 | * @param n Indentation level | ||
96 | */ | ||
97 | static void | ||
98 | space (int n) | ||
99 | { | ||
100 | for (int i = 0; i < n; i++) | ||
101 | fprintf (stderr, "| "); | ||
102 | } | ||
103 | |||
104 | |||
105 | /** | ||
106 | * Printf the combined regex ctx. | ||
107 | * | ||
108 | * @param ctx The ctx to printf | ||
109 | * @param level Indentation level to start with | ||
110 | */ | ||
111 | static void | ||
112 | debugctx (struct RegexCombineCtx *ctx, int level) | ||
113 | { | ||
114 | #if DEBUG_REGEX | ||
115 | if (NULL != ctx->s) | ||
116 | { | ||
117 | space (level - 1); | ||
118 | fprintf (stderr, "%u:'%s'\n", c2i(ctx->s[0], ctx->size), ctx->s); | ||
119 | } | ||
120 | else | ||
121 | fprintf (stderr, "ROOT (base %u)\n", ctx->size); | ||
122 | for (unsigned int i = 0; i < ctx->size; i++) | ||
123 | { | ||
124 | if (NULL != ctx->children[i]) | ||
125 | { | ||
126 | space (level); | ||
127 | debugctx (ctx->children[i], level + 1); | ||
128 | } | ||
129 | } | ||
130 | fflush(stderr); | ||
131 | #endif | ||
132 | } | ||
133 | |||
134 | |||
135 | /** | ||
136 | * Add a single regex to a context, combining with exisiting regex by-prefix. | ||
137 | * | ||
138 | * @param ctx Context with 0 or more regexes. | ||
139 | * @param regex Regex to add. | ||
140 | */ | ||
141 | static void | ||
142 | regex_add (struct RegexCombineCtx *ctx, | ||
143 | const char *regex); | ||
144 | |||
145 | |||
146 | /** | ||
147 | * Create and initialize a new RegexCombineCtx. | ||
148 | * | ||
149 | * @param alphabet_size Size of the alphabet (and the Trie array) | ||
150 | */ | ||
151 | static struct RegexCombineCtx * | ||
152 | new_regex_ctx (unsigned int alphabet_size) | ||
153 | { | ||
154 | struct RegexCombineCtx *ctx; | ||
155 | size_t array_size; | ||
156 | |||
157 | array_size = sizeof(struct RegexCombineCtx *) * alphabet_size; | ||
158 | ctx = GNUNET_new (struct RegexCombineCtx); | ||
159 | ctx->children = GNUNET_malloc (array_size); | ||
160 | ctx->size = alphabet_size; | ||
161 | |||
162 | return ctx; | ||
163 | } | ||
164 | |||
165 | |||
166 | static void | ||
167 | move_children (struct RegexCombineCtx *dst, | ||
168 | const struct RegexCombineCtx *src) | ||
169 | { | ||
170 | size_t array_size; | ||
171 | |||
172 | array_size = sizeof(struct RegexCombineCtx *) * src->size; | ||
173 | GNUNET_memcpy (dst->children, | ||
174 | src->children, | ||
175 | array_size); | ||
176 | for (unsigned int i = 0; i < src->size; i++) | ||
177 | { | ||
178 | src->children[i] = NULL; | ||
179 | } | ||
180 | } | ||
181 | |||
182 | |||
183 | /** | ||
184 | * Extract a string from all prefix-combined regexes. | ||
185 | * | ||
186 | * @param ctx Context with 0 or more regexes. | ||
187 | * | ||
188 | * @return Regex that matches any of the added regexes. | ||
189 | */ | ||
190 | static char * | ||
191 | regex_combine (struct RegexCombineCtx *ctx) | ||
192 | { | ||
193 | struct RegexCombineCtx *p; | ||
194 | unsigned int i; | ||
195 | size_t len; | ||
196 | char *regex; | ||
197 | char *tmp; | ||
198 | char *s; | ||
199 | int opt; | ||
200 | |||
201 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "new combine %s\n", ctx->s); | ||
202 | regex = GNUNET_strdup (""); | ||
203 | opt = GNUNET_NO; | ||
204 | for (i = 0; i < ctx->size; i++) | ||
205 | { | ||
206 | p = ctx->children[i]; | ||
207 | if (NULL == p) | ||
208 | continue; | ||
209 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, | ||
210 | "adding '%s' to innner %s\n", | ||
211 | p->s, ctx->s); | ||
212 | s = regex_combine (p); | ||
213 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, " total '%s'\n", s); | ||
214 | if (strlen(s) == 0) | ||
215 | { | ||
216 | opt = GNUNET_YES; | ||
217 | } | ||
218 | else | ||
219 | { | ||
220 | GNUNET_asprintf (&tmp, "%s%s|", regex, s); | ||
221 | GNUNET_free_non_null (regex); | ||
222 | regex = tmp; | ||
223 | } | ||
224 | GNUNET_free_non_null (s); | ||
225 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, " so far '%s' for inner %s\n", regex, ctx->s); | ||
226 | } | ||
227 | |||
228 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "opt: %d, innner: '%s'\n", opt, regex); | ||
229 | len = strlen (regex); | ||
230 | if (0 == len) | ||
231 | { | ||
232 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "empty, returning ''\n"); | ||
233 | GNUNET_free (regex); | ||
234 | return NULL == ctx->s ? NULL : GNUNET_strdup (ctx->s); | ||
235 | } | ||
236 | |||
237 | if ('|' == regex[len - 1]) | ||
238 | regex[len - 1] = '\0'; | ||
239 | |||
240 | if (NULL != ctx->s) | ||
241 | { | ||
242 | if (opt) | ||
243 | GNUNET_asprintf (&s, "%s(%s)?", ctx->s, regex); | ||
244 | else | ||
245 | GNUNET_asprintf (&s, "%s(%s)", ctx->s, regex); | ||
246 | GNUNET_free (regex); | ||
247 | regex = s; | ||
248 | } | ||
249 | |||
250 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "partial: %s\n", regex); | ||
251 | return regex; | ||
252 | } | ||
253 | |||
254 | |||
255 | /** | ||
256 | * Get the number of matching characters on the prefix of both strings. | ||
257 | * | ||
258 | * @param s1 String 1. | ||
259 | * @param s2 String 2. | ||
260 | * | ||
261 | * @return Number of characters of matching prefix. | ||
262 | */ | ||
263 | static unsigned int | ||
264 | get_prefix_length (const char *s1, const char *s2) | ||
265 | { | ||
266 | unsigned int l1; | ||
267 | unsigned int l2; | ||
268 | unsigned int limit; | ||
269 | unsigned int i; | ||
270 | |||
271 | l1 = strlen (s1); | ||
272 | l2 = strlen (s2); | ||
273 | limit = l1 > l2 ? l2 : l1; | ||
274 | |||
275 | for (i = 0; i < limit; i++) | ||
276 | { | ||
277 | if (s1[i] != s2[i]) | ||
278 | return i; | ||
279 | } | ||
280 | return limit; | ||
281 | } | ||
282 | |||
283 | |||
284 | /** | ||
285 | * Return the child context with the longest prefix match with the regex. | ||
286 | * Usually only one child will match, search all just in case. | ||
287 | * | ||
288 | * @param ctx Context whose children to search. | ||
289 | * @param regex String to match. | ||
290 | * | ||
291 | * @return Child with the longest prefix, NULL if no child matches. | ||
292 | */ | ||
293 | static struct RegexCombineCtx * | ||
294 | get_longest_prefix (struct RegexCombineCtx *ctx, const char *regex) | ||
295 | { | ||
296 | struct RegexCombineCtx *p; | ||
297 | struct RegexCombineCtx *best; | ||
298 | unsigned int i; | ||
299 | unsigned int l; | ||
300 | unsigned int best_l; | ||
301 | |||
302 | best_l = 0; | ||
303 | best = NULL; | ||
304 | |||
305 | for (i = 0; i < ctx->size; i++) | ||
306 | { | ||
307 | p = ctx->children[i]; | ||
308 | if (NULL == p) | ||
309 | continue; | ||
310 | |||
311 | l = get_prefix_length (p->s, regex); | ||
312 | if (l > best_l) | ||
313 | { | ||
314 | GNUNET_break (0 == best_l); | ||
315 | best = p; | ||
316 | best_l = l; | ||
317 | } | ||
318 | } | ||
319 | return best; | ||
320 | } | ||
321 | |||
322 | static void | ||
323 | regex_add_multiple (struct RegexCombineCtx *ctx, | ||
324 | const char *regex, | ||
325 | struct RegexCombineCtx **children) | ||
326 | { | ||
327 | char tmp[2]; | ||
328 | long unsigned int i; | ||
329 | size_t l; | ||
330 | struct RegexCombineCtx *newctx; | ||
331 | unsigned int count; | ||
332 | |||
333 | if ('(' != regex[0]) | ||
334 | { | ||
335 | GNUNET_assert (0); | ||
336 | } | ||
337 | |||
338 | /* Does the regex cover *all* possible children? Then don't add any, | ||
339 | * as it will be covered by the post-regex "(a-z)*" | ||
340 | */ | ||
341 | l = strlen (regex); | ||
342 | count = 0; | ||
343 | for (i = 1UL; i < l; i++) | ||
344 | { | ||
345 | if (regex[i] != '|' && regex[i] != ')') | ||
346 | { | ||
347 | count++; | ||
348 | } | ||
349 | } | ||
350 | if (count == ctx->size) | ||
351 | { | ||
352 | return; | ||
353 | } | ||
354 | |||
355 | /* Add every component as a child node */ | ||
356 | tmp[1] = '\0'; | ||
357 | for (i = 1UL; i < l; i++) | ||
358 | { | ||
359 | if (regex[i] != '|' && regex[i] != ')') | ||
360 | { | ||
361 | tmp[0] = regex[i]; | ||
362 | newctx = new_regex_ctx(ctx->size); | ||
363 | newctx->s = GNUNET_strdup (tmp); | ||
364 | if (children != NULL) | ||
365 | GNUNET_memcpy (newctx->children, | ||
366 | children, | ||
367 | sizeof (*children) * ctx->size); | ||
368 | ctx->children[c2i(tmp[0], ctx->size)] = newctx; | ||
369 | } | ||
370 | } | ||
371 | } | ||
372 | |||
373 | /** | ||
374 | * Add a single regex to a context, splitting the exisiting state. | ||
375 | * | ||
376 | * We only had a partial match, split existing state, truncate the current node | ||
377 | * so it only contains the prefix, add suffix(es) as children. | ||
378 | * | ||
379 | * @param ctx Context to split. | ||
380 | * @param len Lenght of ctx->s | ||
381 | * @param prefix_l Lenght of common prefix of the new regex and @a ctx->s | ||
382 | */ | ||
383 | static void | ||
384 | regex_split (struct RegexCombineCtx *ctx, | ||
385 | unsigned int len, | ||
386 | unsigned int prefix_l) | ||
387 | { | ||
388 | struct RegexCombineCtx *newctx; | ||
389 | unsigned int idx; | ||
390 | char *suffix; | ||
391 | |||
392 | suffix = GNUNET_malloc (len - prefix_l + 1); | ||
393 | strncpy (suffix, &ctx->s[prefix_l], len - prefix_l + 1); | ||
394 | |||
395 | /* Suffix saved, truncate current node so it only contains the prefix, | ||
396 | * copy any children nodes to put as grandchildren and initialize new empty | ||
397 | * children array. | ||
398 | */ | ||
399 | ctx->s[prefix_l] = '\0'; | ||
400 | |||
401 | /* If the suffix is an OR expression, add multiple children */ | ||
402 | if ('(' == suffix[0]) | ||
403 | { | ||
404 | struct RegexCombineCtx **tmp; | ||
405 | |||
406 | tmp = ctx->children; | ||
407 | ctx->children = GNUNET_malloc (sizeof(*tmp) * ctx->size); | ||
408 | regex_add_multiple (ctx, suffix, tmp); | ||
409 | GNUNET_free (suffix); | ||
410 | GNUNET_free (tmp); | ||
411 | return; | ||
412 | } | ||
413 | |||
414 | /* The suffix is a normal string, add as one node */ | ||
415 | newctx = new_regex_ctx (ctx->size); | ||
416 | newctx->s = suffix; | ||
417 | move_children (newctx, ctx); | ||
418 | idx = c2i(suffix[0], ctx->size); | ||
419 | ctx->children[idx] = newctx; | ||
420 | } | ||
421 | |||
422 | |||
423 | /** | ||
424 | * Add a single regex to a context, combining with exisiting regex by-prefix. | ||
425 | * | ||
426 | * @param ctx Context with 0 or more regexes. | ||
427 | * @param regex Regex to add. | ||
428 | */ | ||
429 | static void | ||
430 | regex_add (struct RegexCombineCtx *ctx, const char *regex) | ||
431 | { | ||
432 | struct RegexCombineCtx *p; | ||
433 | struct RegexCombineCtx *newctx; | ||
434 | long unsigned int l; | ||
435 | unsigned int prefix_l; | ||
436 | const char *rest_r; | ||
437 | const char *rest_s; | ||
438 | size_t len; | ||
439 | int idx; | ||
440 | |||
441 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, | ||
442 | "regex_add '%s' into '%s'\n", | ||
443 | regex, ctx->s); | ||
444 | l = strlen (regex); | ||
445 | if (0UL == l) | ||
446 | return; | ||
447 | |||
448 | /* If the regex is in the form of (a|b|c), add every character separately */ | ||
449 | if ('(' == regex[0]) | ||
450 | { | ||
451 | regex_add_multiple (ctx, regex, NULL); | ||
452 | return; | ||
453 | } | ||
454 | |||
455 | p = get_longest_prefix (ctx, regex); | ||
456 | if (NULL != p) | ||
457 | { | ||
458 | /* There is some prefix match, reduce regex and try again */ | ||
459 | prefix_l = get_prefix_length (p->s, regex); | ||
460 | rest_s = &p->s[prefix_l]; | ||
461 | rest_r = ®ex[prefix_l]; | ||
462 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "chosen '%s' [%u]\n", p->s, prefix_l); | ||
463 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "prefix r '%.*s'\n", prefix_l, p->s); | ||
464 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "rest r '%s'\n", rest_r); | ||
465 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "rest s '%s'\n", rest_s); | ||
466 | len = strlen (p->s); | ||
467 | if (prefix_l < len) | ||
468 | { | ||
469 | regex_split (p, len, prefix_l); | ||
470 | } | ||
471 | regex_add (p, rest_r); | ||
472 | return; | ||
473 | } | ||
474 | |||
475 | /* There is no prefix match, add new */ | ||
476 | idx = c2i(regex[0], ctx->size); | ||
477 | if (NULL == ctx->children[idx] && NULL != ctx->s) | ||
478 | { | ||
479 | /* this was the end before, add empty string */ | ||
480 | newctx = new_regex_ctx (ctx->size); | ||
481 | newctx->s = GNUNET_strdup (""); | ||
482 | ctx->children[idx] = newctx; | ||
483 | } | ||
484 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, " no match\n"); | ||
485 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, " new state %s\n", regex); | ||
486 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, " under %s\n", ctx->s); | ||
487 | newctx = new_regex_ctx(ctx->size); | ||
488 | newctx->s = GNUNET_strdup (regex); | ||
489 | ctx->children[idx] = newctx; | ||
490 | } | ||
491 | |||
492 | |||
493 | /** | ||
494 | * Free all resources used by the context node and all its children. | ||
495 | * | ||
496 | * @param ctx Context to free. | ||
497 | */ | ||
498 | static void | ||
499 | regex_ctx_destroy (struct RegexCombineCtx *ctx) | ||
500 | { | ||
501 | unsigned int i; | ||
502 | |||
503 | if (NULL == ctx) | ||
504 | return; | ||
505 | |||
506 | for (i = 0; i < ctx->size; i++) | ||
507 | { | ||
508 | regex_ctx_destroy (ctx->children[i]); | ||
509 | } | ||
510 | GNUNET_free_non_null (ctx->s); /* 's' on root node is null */ | ||
511 | GNUNET_free (ctx->children); | ||
512 | GNUNET_free (ctx); | ||
513 | } | ||
514 | |||
515 | |||
516 | /** | ||
517 | * Combine an array of regexes into a single prefix-shared regex. | ||
518 | * Returns a prefix-combine regex that matches the same strings as | ||
519 | * any of the original regexes. | ||
520 | * | ||
521 | * WARNING: only useful for reading specific regexes for specific applications, | ||
522 | * namely the gnunet-regex-profiler / gnunet-regex-daemon. | ||
523 | * This function DOES NOT support arbitrary regex combining. | ||
524 | * | ||
525 | * @param regexes A NULL-terminated array of regexes. | ||
526 | * @param alphabet_size Size of the alphabet the regex uses. | ||
527 | * | ||
528 | * @return A string with a single regex that matches any of the original regexes | ||
529 | */ | ||
530 | char * | ||
531 | REGEX_TEST_combine (char * const regexes[], unsigned int alphabet_size) | ||
532 | { | ||
533 | unsigned int i; | ||
534 | char *combined; | ||
535 | const char *current; | ||
536 | struct RegexCombineCtx *ctx; | ||
537 | |||
538 | ctx = new_regex_ctx (alphabet_size); | ||
539 | for (i = 0; regexes[i]; i++) | ||
540 | { | ||
541 | current = regexes[i]; | ||
542 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Regex %u: %s\n", i, current); | ||
543 | regex_add (ctx, current); | ||
544 | debugctx (ctx, 0); | ||
545 | } | ||
546 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "\nCombining...\n"); | ||
547 | debugctx (ctx, 0); | ||
548 | |||
549 | combined = regex_combine (ctx); | ||
550 | |||
551 | regex_ctx_destroy (ctx); | ||
552 | |||
553 | return combined; | ||
554 | } | ||
555 | |||
556 | |||
557 | /** | ||
558 | * Read a set of regexes from a file, one per line and return them in an array | ||
559 | * suitable for REGEX_TEST_combine. | ||
560 | * The array must be free'd using REGEX_TEST_free_from_file. | ||
561 | * | ||
562 | * @param filename Name of the file containing the regexes. | ||
563 | * | ||
564 | * @return A newly allocated, NULL terminated array of regexes. | ||
565 | */ | ||
566 | char ** | ||
567 | REGEX_TEST_read_from_file (const char *filename) | ||
568 | { | ||
569 | struct GNUNET_DISK_FileHandle *f; | ||
570 | unsigned int nr; | ||
571 | unsigned int offset; | ||
572 | off_t size; | ||
573 | size_t len; | ||
574 | char *buffer; | ||
575 | char *regex; | ||
576 | char **regexes; | ||
577 | |||
578 | f = GNUNET_DISK_file_open (filename, | ||
579 | GNUNET_DISK_OPEN_READ, | ||
580 | GNUNET_DISK_PERM_NONE); | ||
581 | if (NULL == f) | ||
582 | { | ||
583 | GNUNET_log (GNUNET_ERROR_TYPE_ERROR, | ||
584 | "Can't open file %s for reading\n", filename); | ||
585 | return NULL; | ||
586 | } | ||
587 | if (GNUNET_OK != GNUNET_DISK_file_handle_size (f, &size)) | ||
588 | { | ||
589 | GNUNET_log (GNUNET_ERROR_TYPE_ERROR, | ||
590 | "Can't get size of file %s\n", filename); | ||
591 | GNUNET_DISK_file_close (f); | ||
592 | return NULL; | ||
593 | } | ||
594 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, | ||
595 | "using file %s, size %llu\n", | ||
596 | filename, (unsigned long long) size); | ||
597 | |||
598 | buffer = GNUNET_malloc (size + 1); | ||
599 | GNUNET_DISK_file_read (f, buffer, size); | ||
600 | GNUNET_DISK_file_close (f); | ||
601 | regexes = GNUNET_malloc (sizeof (char *)); | ||
602 | nr = 1; | ||
603 | offset = 0; | ||
604 | regex = NULL; | ||
605 | do | ||
606 | { | ||
607 | if (NULL == regex) | ||
608 | regex = GNUNET_malloc (size + 1); | ||
609 | len = (size_t) sscanf (&buffer[offset], "%s", regex); | ||
610 | if (0 == len) | ||
611 | break; | ||
612 | len = strlen (regex); | ||
613 | offset += len + 1; | ||
614 | if (len < 1) | ||
615 | continue; | ||
616 | regex[len] = '\0'; | ||
617 | regex = GNUNET_realloc (regex, len + 1); | ||
618 | GNUNET_array_grow (regexes, nr, nr + 1); | ||
619 | GNUNET_assert (NULL == regexes[nr - 2]); | ||
620 | regexes[nr - 2] = regex; | ||
621 | regexes[nr - 1] = NULL; | ||
622 | regex = NULL; | ||
623 | } while (offset < size); | ||
624 | GNUNET_free_non_null (regex); | ||
625 | GNUNET_free (buffer); | ||
626 | |||
627 | return regexes; | ||
628 | } | ||
629 | |||
630 | |||
631 | /** | ||
632 | * Free all memory reserved for a set of regexes created by read_from_file. | ||
633 | * | ||
634 | * @param regexes NULL-terminated array of regexes. | ||
635 | */ | ||
636 | void | ||
637 | REGEX_TEST_free_from_file (char **regexes) | ||
638 | { | ||
639 | unsigned int i; | ||
640 | |||
641 | for (i = 0; regexes[i]; i++) | ||
642 | GNUNET_free (regexes[i]); | ||
643 | GNUNET_free (regexes); | ||
644 | } | ||
645 | |||
646 | /* end of regex_test_lib.c */ | ||
diff --git a/src/regex/regex_test_lib.h b/src/regex/regex_test_lib.h new file mode 100644 index 000000000..533ba3efb --- /dev/null +++ b/src/regex/regex_test_lib.h | |||
@@ -0,0 +1,157 @@ | |||
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 | /** | ||
19 | * @file src/regex/regex_test_lib.h | ||
20 | * @brief library to read regexes representing IP networks from a file. | ||
21 | * and simplifying the into one big regex, in order to run | ||
22 | * tests (regex performance, regex profiler). | ||
23 | * @author Bertlomiej Polot | ||
24 | */ | ||
25 | |||
26 | #ifndef REGEX_INTERNAL_TEST_LIB_H | ||
27 | #define REGEX_INTERNAL_TEST_LIB_H | ||
28 | |||
29 | #include "regex_internal_lib.h" | ||
30 | |||
31 | #ifdef __cplusplus | ||
32 | extern "C" | ||
33 | { | ||
34 | #if 0 /* keep Emacsens' auto-indent happy */ | ||
35 | } | ||
36 | #endif | ||
37 | #endif | ||
38 | |||
39 | |||
40 | /** | ||
41 | * Combine an array of regexes into a single prefix-shared regex. | ||
42 | * Returns a prefix-combine regex that matches the same strings as | ||
43 | * any of the original regexes. | ||
44 | * | ||
45 | * WARNING: only useful for reading specific regexes for specific applications, | ||
46 | * namely the gnunet-regex-profiler / gnunet-regex-daemon. | ||
47 | * This function DOES NOT support arbitrary regex combining. | ||
48 | * | ||
49 | * @param regexes A NULL-terminated array of regexes. | ||
50 | * @param alphabet_size Size of the alphabet the regex uses. | ||
51 | * | ||
52 | * @return A string with a single regex that matches any of the original regexes | ||
53 | */ | ||
54 | char * | ||
55 | REGEX_TEST_combine (char * const regexes[], unsigned int alphabet_size); | ||
56 | |||
57 | |||
58 | /** | ||
59 | * Read a set of regexes from a file, one per line and return them in an array | ||
60 | * suitable for REGEX_TEST_combine. | ||
61 | * The array must be free'd using REGEX_TEST_free_from_file. | ||
62 | * | ||
63 | * @param filename Name of the file containing the regexes. | ||
64 | * | ||
65 | * @return A newly allocated, NULL terminated array of regexes. | ||
66 | */ | ||
67 | char ** | ||
68 | REGEX_TEST_read_from_file (const char *filename); | ||
69 | |||
70 | |||
71 | /** | ||
72 | * Free all memory reserved for a set of regexes created by read_from_file. | ||
73 | * | ||
74 | * @param regexes NULL-terminated array of regexes. | ||
75 | */ | ||
76 | void | ||
77 | REGEX_TEST_free_from_file (char **regexes); | ||
78 | |||
79 | |||
80 | /** | ||
81 | * Generate a (pseudo) random regular expression of length 'rx_length', as well | ||
82 | * as a (optional) string that will be matched by the generated regex. The | ||
83 | * returned regex needs to be freed. | ||
84 | * | ||
85 | * @param rx_length length of the random regex. | ||
86 | * @param matching_str (optional) pointer to a string that will contain a string | ||
87 | * that will be matched by the generated regex, if | ||
88 | * 'matching_str' pointer was not NULL. | ||
89 | * | ||
90 | * @return NULL if 'rx_length' is 0, a random regex of length 'rx_length', which | ||
91 | * needs to be freed, otherwise. | ||
92 | */ | ||
93 | char * | ||
94 | REGEX_TEST_generate_random_regex (size_t rx_length, char *matching_str); | ||
95 | |||
96 | |||
97 | /** | ||
98 | * Generate a random string of maximum length 'max_len' that only contains literals allowed | ||
99 | * in a regular expression. The string might be 0 chars long but is garantueed | ||
100 | * to be shorter or equal to 'max_len'. | ||
101 | * | ||
102 | * @param max_len maximum length of the string that should be generated. | ||
103 | * | ||
104 | * @return random string that needs to be freed. | ||
105 | */ | ||
106 | char * | ||
107 | REGEX_TEST_generate_random_string (size_t max_len); | ||
108 | |||
109 | |||
110 | /** | ||
111 | * Options for graph creation function | ||
112 | * REGEX_TEST_automaton_save_graph. | ||
113 | */ | ||
114 | enum REGEX_TEST_GraphSavingOptions | ||
115 | { | ||
116 | /** | ||
117 | * Default. Do nothing special. | ||
118 | */ | ||
119 | REGEX_TEST_GRAPH_DEFAULT = 0, | ||
120 | |||
121 | /** | ||
122 | * The generated graph will include extra information such as the NFA states | ||
123 | * that were used to generate the DFA state. | ||
124 | */ | ||
125 | REGEX_TEST_GRAPH_VERBOSE = 1, | ||
126 | |||
127 | /** | ||
128 | * Enable graph coloring. Will color each SCC in a different color. | ||
129 | */ | ||
130 | REGEX_TEST_GRAPH_COLORING = 2 | ||
131 | }; | ||
132 | |||
133 | |||
134 | /** | ||
135 | * Save the given automaton as a GraphViz dot file. | ||
136 | * | ||
137 | * @param a the automaton to be saved. | ||
138 | * @param filename where to save the file. | ||
139 | * @param options options for graph generation that include coloring or verbose | ||
140 | * mode | ||
141 | */ | ||
142 | void | ||
143 | REGEX_TEST_automaton_save_graph (struct REGEX_INTERNAL_Automaton *a, | ||
144 | const char *filename, | ||
145 | enum REGEX_TEST_GraphSavingOptions options); | ||
146 | |||
147 | |||
148 | |||
149 | #if 0 /* keep Emacsens' auto-indent happy */ | ||
150 | { | ||
151 | #endif | ||
152 | #ifdef __cplusplus | ||
153 | } | ||
154 | #endif | ||
155 | |||
156 | /* end of regex_internal_lib.h */ | ||
157 | #endif | ||
diff --git a/src/regex/regex_test_random.c b/src/regex/regex_test_random.c new file mode 100644 index 000000000..6e51885ee --- /dev/null +++ b/src/regex/regex_test_random.c | |||
@@ -0,0 +1,169 @@ | |||
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 | /** | ||
19 | * @file src/regex/regex_test_random.c | ||
20 | * @brief functions for creating random regular expressions and strings | ||
21 | * @author Maximilian Szengel | ||
22 | */ | ||
23 | #include "platform.h" | ||
24 | #include "regex_test_lib.h" | ||
25 | #include "gnunet_crypto_lib.h" | ||
26 | #include "regex_internal.h" | ||
27 | |||
28 | |||
29 | /** | ||
30 | * Get a (pseudo) random valid literal for building a regular expression. | ||
31 | * | ||
32 | * @return random valid literal | ||
33 | */ | ||
34 | static char | ||
35 | get_random_literal () | ||
36 | { | ||
37 | uint32_t ridx; | ||
38 | |||
39 | ridx = | ||
40 | GNUNET_CRYPTO_random_u32 (GNUNET_CRYPTO_QUALITY_WEAK, | ||
41 | (uint32_t) strlen (ALLOWED_LITERALS)); | ||
42 | |||
43 | return ALLOWED_LITERALS[ridx]; | ||
44 | } | ||
45 | |||
46 | |||
47 | /** | ||
48 | * Generate a (pseudo) random regular expression of length 'rx_length', as well | ||
49 | * as a (optional) string that will be matched by the generated regex. The | ||
50 | * returned regex needs to be freed. | ||
51 | * | ||
52 | * @param rx_length length of the random regex. | ||
53 | * @param matching_str (optional) pointer to a string that will contain a string | ||
54 | * that will be matched by the generated regex, if | ||
55 | * 'matching_str' pointer was not NULL. Make sure you | ||
56 | * allocated at least rx_length+1 bytes for this sting. | ||
57 | * | ||
58 | * @return NULL if 'rx_length' is 0, a random regex of length 'rx_length', which | ||
59 | * needs to be freed, otherwise. | ||
60 | */ | ||
61 | char * | ||
62 | REGEX_TEST_generate_random_regex (size_t rx_length, char *matching_str) | ||
63 | { | ||
64 | char *rx; | ||
65 | char *rx_p; | ||
66 | char *matching_strp; | ||
67 | unsigned int i; | ||
68 | unsigned int char_op_switch; | ||
69 | unsigned int last_was_op; | ||
70 | int rx_op; | ||
71 | char current_char; | ||
72 | |||
73 | if (0 == rx_length) | ||
74 | return NULL; | ||
75 | |||
76 | if (NULL != matching_str) | ||
77 | matching_strp = matching_str; | ||
78 | else | ||
79 | matching_strp = NULL; | ||
80 | |||
81 | rx = GNUNET_malloc (rx_length + 1); | ||
82 | rx_p = rx; | ||
83 | current_char = 0; | ||
84 | last_was_op = 1; | ||
85 | |||
86 | for (i = 0; i < rx_length; i++) | ||
87 | { | ||
88 | char_op_switch = GNUNET_CRYPTO_random_u32 (GNUNET_CRYPTO_QUALITY_WEAK, 2); | ||
89 | |||
90 | if (0 == char_op_switch && !last_was_op) | ||
91 | { | ||
92 | last_was_op = 1; | ||
93 | rx_op = GNUNET_CRYPTO_random_u32 (GNUNET_CRYPTO_QUALITY_WEAK, 4); | ||
94 | |||
95 | switch (rx_op) | ||
96 | { | ||
97 | case 0: | ||
98 | current_char = '+'; | ||
99 | break; | ||
100 | case 1: | ||
101 | current_char = '*'; | ||
102 | break; | ||
103 | case 2: | ||
104 | current_char = '?'; | ||
105 | break; | ||
106 | case 3: | ||
107 | if (i < rx_length - 1) /* '|' cannot be at the end */ | ||
108 | current_char = '|'; | ||
109 | else | ||
110 | current_char = get_random_literal (); | ||
111 | break; | ||
112 | } | ||
113 | } | ||
114 | else | ||
115 | { | ||
116 | current_char = get_random_literal (); | ||
117 | last_was_op = 0; | ||
118 | } | ||
119 | |||
120 | if (NULL != matching_strp && | ||
121 | (current_char != '+' && current_char != '*' && current_char != '?' && | ||
122 | current_char != '|')) | ||
123 | { | ||
124 | *matching_strp = current_char; | ||
125 | matching_strp++; | ||
126 | } | ||
127 | |||
128 | *rx_p = current_char; | ||
129 | rx_p++; | ||
130 | } | ||
131 | *rx_p = '\0'; | ||
132 | if (NULL != matching_strp) | ||
133 | *matching_strp = '\0'; | ||
134 | |||
135 | return rx; | ||
136 | } | ||
137 | |||
138 | |||
139 | /** | ||
140 | * Generate a random string of maximum length 'max_len' that only contains literals allowed | ||
141 | * in a regular expression. The string might be 0 chars long but is garantueed | ||
142 | * to be shorter or equal to 'max_len'. | ||
143 | * | ||
144 | * @param max_len maximum length of the string that should be generated. | ||
145 | * | ||
146 | * @return random string that needs to be freed. | ||
147 | */ | ||
148 | char * | ||
149 | REGEX_TEST_generate_random_string (size_t max_len) | ||
150 | { | ||
151 | unsigned int i; | ||
152 | char *str; | ||
153 | size_t len; | ||
154 | |||
155 | if (1 > max_len) | ||
156 | return GNUNET_strdup (""); | ||
157 | |||
158 | len = (size_t) GNUNET_CRYPTO_random_u32 (GNUNET_CRYPTO_QUALITY_WEAK, max_len); | ||
159 | str = GNUNET_malloc (len + 1); | ||
160 | |||
161 | for (i = 0; i < len; i++) | ||
162 | { | ||
163 | str[i] = get_random_literal (); | ||
164 | } | ||
165 | |||
166 | str[i] = '\0'; | ||
167 | |||
168 | return str; | ||
169 | } | ||
diff --git a/src/regex/test_regex_api.c b/src/regex/test_regex_api.c new file mode 100644 index 000000000..a08e2ed6d --- /dev/null +++ b/src/regex/test_regex_api.c | |||
@@ -0,0 +1,127 @@ | |||
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 | /** | ||
19 | * @file regex/test_regex_api.c | ||
20 | * @brief base test case for regex api (and DHT functions) | ||
21 | * @author Christian Grothoff | ||
22 | */ | ||
23 | #include "platform.h" | ||
24 | #include "gnunet_util_lib.h" | ||
25 | #include "gnunet_testing_lib.h" | ||
26 | #include "gnunet_regex_service.h" | ||
27 | |||
28 | |||
29 | /** | ||
30 | * How long until we really give up on a particular testcase portion? | ||
31 | */ | ||
32 | #define TOTAL_TIMEOUT GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS, 600) | ||
33 | |||
34 | /** | ||
35 | * How long until we give up on any particular operation (and retry)? | ||
36 | */ | ||
37 | #define BASE_TIMEOUT GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS, 3) | ||
38 | |||
39 | |||
40 | static struct GNUNET_REGEX_Announcement *a; | ||
41 | |||
42 | static struct GNUNET_REGEX_Search *s; | ||
43 | |||
44 | static int ok = 1; | ||
45 | |||
46 | static struct GNUNET_SCHEDULER_Task *die_task; | ||
47 | |||
48 | |||
49 | static void | ||
50 | end (void *cls) | ||
51 | { | ||
52 | die_task = NULL; | ||
53 | GNUNET_REGEX_announce_cancel (a); | ||
54 | a = NULL; | ||
55 | GNUNET_REGEX_search_cancel (s); | ||
56 | s = NULL; | ||
57 | ok = 0; | ||
58 | } | ||
59 | |||
60 | |||
61 | static void | ||
62 | end_badly () | ||
63 | { | ||
64 | die_task = NULL; | ||
65 | FPRINTF (stderr, "%s", "Testcase failed (timeout).\n"); | ||
66 | GNUNET_REGEX_announce_cancel (a); | ||
67 | a = NULL; | ||
68 | GNUNET_REGEX_search_cancel (s); | ||
69 | s = NULL; | ||
70 | ok = 1; | ||
71 | } | ||
72 | |||
73 | |||
74 | /** | ||
75 | * Search callback function, invoked for every result that was found. | ||
76 | * | ||
77 | * @param cls Closure provided in GNUNET_REGEX_search. | ||
78 | * @param id Peer providing a regex that matches the string. | ||
79 | * @param get_path Path of the get request. | ||
80 | * @param get_path_length Lenght of get_path. | ||
81 | * @param put_path Path of the put request. | ||
82 | * @param put_path_length Length of the put_path. | ||
83 | */ | ||
84 | static void | ||
85 | found_cb (void *cls, | ||
86 | const struct GNUNET_PeerIdentity *id, | ||
87 | const struct GNUNET_PeerIdentity *get_path, | ||
88 | unsigned int get_path_length, | ||
89 | const struct GNUNET_PeerIdentity *put_path, | ||
90 | unsigned int put_path_length) | ||
91 | { | ||
92 | GNUNET_SCHEDULER_cancel (die_task); | ||
93 | die_task = | ||
94 | GNUNET_SCHEDULER_add_now (&end, NULL); | ||
95 | } | ||
96 | |||
97 | |||
98 | static void | ||
99 | run (void *cls, | ||
100 | const struct GNUNET_CONFIGURATION_Handle *cfg, | ||
101 | struct GNUNET_TESTING_Peer *peer) | ||
102 | { | ||
103 | die_task = | ||
104 | GNUNET_SCHEDULER_add_delayed (TOTAL_TIMEOUT, | ||
105 | &end_badly, NULL); | ||
106 | a = GNUNET_REGEX_announce (cfg, | ||
107 | "my long prefix - hello world(0|1)*", | ||
108 | GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS, | ||
109 | 5), | ||
110 | 1); | ||
111 | s = GNUNET_REGEX_search (cfg, | ||
112 | "my long prefix - hello world0101", | ||
113 | &found_cb, NULL); | ||
114 | } | ||
115 | |||
116 | |||
117 | int | ||
118 | main (int argc, char *argv[]) | ||
119 | { | ||
120 | if (0 != GNUNET_TESTING_peer_run ("test-regex-api", | ||
121 | "test_regex_api_data.conf", | ||
122 | &run, NULL)) | ||
123 | return 1; | ||
124 | return ok; | ||
125 | } | ||
126 | |||
127 | /* end of test_regex_api.c */ | ||
diff --git a/src/regex/test_regex_api_data.conf b/src/regex/test_regex_api_data.conf new file mode 100644 index 000000000..40fee1e54 --- /dev/null +++ b/src/regex/test_regex_api_data.conf | |||
@@ -0,0 +1,39 @@ | |||
1 | @INLINE@ ../../contrib/conf/gnunet/no_forcestart.conf | ||
2 | @INLINE@ ../../contrib/conf/gnunet/no_autostart_above_core.conf | ||
3 | |||
4 | [PATHS] | ||
5 | GNUNET_TEST_HOME = $GNUNET_TMP/test-regex-api/ | ||
6 | |||
7 | [dhtcache] | ||
8 | QUOTA = 1 MB | ||
9 | DATABASE = heap | ||
10 | |||
11 | [topology] | ||
12 | TARGET-CONNECTION-COUNT = 16 | ||
13 | AUTOCONNECT = YES | ||
14 | FRIENDS-ONLY = NO | ||
15 | MINIMUM-FRIENDS = 0 | ||
16 | |||
17 | [ats] | ||
18 | WAN_QUOTA_IN = 1 GB | ||
19 | WAN_QUOTA_OUT = 1 GB | ||
20 | |||
21 | [dht] | ||
22 | START_ON_DEMAND = YES | ||
23 | PORT = 12370 | ||
24 | |||
25 | [regex] | ||
26 | START_ON_DEMAND = YES | ||
27 | |||
28 | [transport] | ||
29 | plugins = tcp | ||
30 | NEIGHBOUR_LIMIT = 50 | ||
31 | |||
32 | [nat] | ||
33 | DISABLEV6 = YES | ||
34 | BINDTO = 127.0.0.1 | ||
35 | ENABLE_UPNP = NO | ||
36 | BEHIND_NAT = NO | ||
37 | ALLOW_NAT = NO | ||
38 | INTERNAL_ADDRESS = 127.0.0.1 | ||
39 | EXTERNAL_ADDRESS = 127.0.0.1 | ||
diff --git a/src/regex/test_regex_eval_api.c b/src/regex/test_regex_eval_api.c new file mode 100644 index 000000000..8a0c0d024 --- /dev/null +++ b/src/regex/test_regex_eval_api.c | |||
@@ -0,0 +1,364 @@ | |||
1 | /* | ||
2 | This file is part of GNUnet | ||
3 | Copyright (C) 2012 GNUnet e.V. | ||
4 | |||
5 | GNUnet is free software: you can redistribute it and/or modify it | ||
6 | under the terms of the GNU Affero General Public License as published | ||
7 | by the Free Software Foundation, either version 3 of the License, | ||
8 | or (at your option) any later version. | ||
9 | |||
10 | GNUnet is distributed in the hope that it will be useful, but | ||
11 | WITHOUT ANY WARRANTY; without even the implied warranty of | ||
12 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | ||
13 | Affero General Public License for more details. | ||
14 | |||
15 | You should have received a copy of the GNU Affero General Public License | ||
16 | along with this program. If not, see <http://www.gnu.org/licenses/>. | ||
17 | */ | ||
18 | /** | ||
19 | * @file regex/test_regex_eval_api.c | ||
20 | * @brief test for regex.c | ||
21 | * @author Maximilian Szengel | ||
22 | */ | ||
23 | #include <regex.h> | ||
24 | #include <time.h> | ||
25 | #include "platform.h" | ||
26 | #include "regex_internal_lib.h" | ||
27 | #include "regex_test_lib.h" | ||
28 | #include "regex_internal.h" | ||
29 | |||
30 | enum Match_Result | ||
31 | { | ||
32 | match = 0, | ||
33 | nomatch = 1 | ||
34 | }; | ||
35 | |||
36 | struct Regex_String_Pair | ||
37 | { | ||
38 | char *regex; | ||
39 | int string_count; | ||
40 | char *strings[20]; | ||
41 | enum Match_Result expected_results[20]; | ||
42 | }; | ||
43 | |||
44 | |||
45 | /** | ||
46 | * Random regex test. Generate a random regex as well as 'str_count' strings to | ||
47 | * match it against. Will match using GNUNET_REGEX implementation and compare | ||
48 | * the result to glibc regex result. 'rx_length' has to be smaller then | ||
49 | * 'max_str_len'. | ||
50 | * | ||
51 | * @param rx_length length of the regular expression. | ||
52 | * @param max_str_len maximum length of the random strings. | ||
53 | * @param str_count number of generated random strings. | ||
54 | * | ||
55 | * @return 0 on success, non 0 otherwise. | ||
56 | */ | ||
57 | int | ||
58 | test_random (unsigned int rx_length, unsigned int max_str_len, | ||
59 | unsigned int str_count) | ||
60 | { | ||
61 | unsigned int i; | ||
62 | char *rand_rx; | ||
63 | char *matching_str; | ||
64 | int eval; | ||
65 | int eval_check; | ||
66 | int eval_canonical; | ||
67 | int eval_canonical_check; | ||
68 | struct REGEX_INTERNAL_Automaton *dfa; | ||
69 | regex_t rx; | ||
70 | regmatch_t matchptr[1]; | ||
71 | char error[200]; | ||
72 | int result; | ||
73 | char *canonical_regex = NULL; | ||
74 | |||
75 | /* At least one string is needed for matching */ | ||
76 | GNUNET_assert (str_count > 0); | ||
77 | /* The string should be at least as long as the regex itself */ | ||
78 | GNUNET_assert (max_str_len >= rx_length); | ||
79 | |||
80 | /* Generate random regex and a string that matches the regex */ | ||
81 | matching_str = GNUNET_malloc (rx_length + 1); | ||
82 | rand_rx = REGEX_TEST_generate_random_regex (rx_length, matching_str); | ||
83 | |||
84 | /* Now match */ | ||
85 | result = 0; | ||
86 | for (i = 0; i < str_count; i++) | ||
87 | { | ||
88 | if (0 < i) | ||
89 | { | ||
90 | matching_str = REGEX_TEST_generate_random_string (max_str_len); | ||
91 | } | ||
92 | |||
93 | /* Match string using DFA */ | ||
94 | dfa = REGEX_INTERNAL_construct_dfa (rand_rx, strlen (rand_rx), 0); | ||
95 | if (NULL == dfa) | ||
96 | { | ||
97 | GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Constructing DFA failed\n"); | ||
98 | goto error; | ||
99 | } | ||
100 | |||
101 | eval = REGEX_INTERNAL_eval (dfa, matching_str); | ||
102 | /* save the canonical regex for later comparison */ | ||
103 | canonical_regex = GNUNET_strdup (REGEX_INTERNAL_get_canonical_regex (dfa)); | ||
104 | REGEX_INTERNAL_automaton_destroy (dfa); | ||
105 | |||
106 | /* Match string using glibc regex */ | ||
107 | if (0 != regcomp (&rx, rand_rx, REG_EXTENDED)) | ||
108 | { | ||
109 | GNUNET_log (GNUNET_ERROR_TYPE_ERROR, | ||
110 | "Could not compile regex using regcomp: %s\n", rand_rx); | ||
111 | goto error; | ||
112 | } | ||
113 | |||
114 | eval_check = regexec (&rx, matching_str, 1, matchptr, 0); | ||
115 | regfree (&rx); | ||
116 | |||
117 | /* We only want to match the whole string, because that's what our DFA does, | ||
118 | * too. */ | ||
119 | if (eval_check == 0 && | ||
120 | (matchptr[0].rm_so != 0 || matchptr[0].rm_eo != strlen (matching_str))) | ||
121 | eval_check = 1; | ||
122 | |||
123 | /* Match canonical regex */ | ||
124 | dfa = | ||
125 | REGEX_INTERNAL_construct_dfa (canonical_regex, strlen (canonical_regex), | ||
126 | 0); | ||
127 | if (NULL == dfa) | ||
128 | { | ||
129 | GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Constructing DFA failed\n"); | ||
130 | goto error; | ||
131 | } | ||
132 | |||
133 | eval_canonical = REGEX_INTERNAL_eval (dfa, matching_str); | ||
134 | REGEX_INTERNAL_automaton_destroy (dfa); | ||
135 | |||
136 | if (0 != regcomp (&rx, canonical_regex, REG_EXTENDED)) | ||
137 | { | ||
138 | GNUNET_log (GNUNET_ERROR_TYPE_ERROR, | ||
139 | "Could not compile regex using regcomp: %s\n", | ||
140 | canonical_regex); | ||
141 | goto error; | ||
142 | } | ||
143 | |||
144 | eval_canonical_check = regexec (&rx, matching_str, 1, matchptr, 0); | ||
145 | regfree (&rx); | ||
146 | |||
147 | /* We only want to match the whole string, because that's what our DFA does, | ||
148 | * too. */ | ||
149 | if (eval_canonical_check == 0 && | ||
150 | (matchptr[0].rm_so != 0 || matchptr[0].rm_eo != strlen (matching_str))) | ||
151 | eval_canonical_check = 1; | ||
152 | |||
153 | /* compare results */ | ||
154 | if (eval_check != eval || eval_canonical != eval_canonical_check) | ||
155 | { | ||
156 | regerror (eval_check, &rx, error, sizeof error); | ||
157 | GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Unexpected result:\nregex: %s\ncanonical_regex: %s\n\ | ||
158 | string: %s\ngnunet regex: %i\nglibc regex: %i\n\ | ||
159 | canonical regex: %i\ncanonical regex glibc: %i\n\ | ||
160 | glibc error: %s\n\n", rand_rx, canonical_regex, matching_str, | ||
161 | eval, eval_check, eval_canonical, eval_canonical_check, error); | ||
162 | result += 1; | ||
163 | } | ||
164 | GNUNET_free (canonical_regex); | ||
165 | GNUNET_free (matching_str); | ||
166 | canonical_regex = NULL; | ||
167 | matching_str = NULL; | ||
168 | } | ||
169 | |||
170 | GNUNET_free (rand_rx); | ||
171 | |||
172 | return result; | ||
173 | |||
174 | error: | ||
175 | GNUNET_free_non_null (matching_str); | ||
176 | GNUNET_free_non_null (rand_rx); | ||
177 | GNUNET_free_non_null (canonical_regex); | ||
178 | return -1; | ||
179 | } | ||
180 | |||
181 | /** | ||
182 | * Automaton test that compares the result of matching regular expression 'rx' | ||
183 | * with the strings and expected results in 'rxstr' with the result of matching | ||
184 | * the same strings with glibc regex. | ||
185 | * | ||
186 | * @param a automaton. | ||
187 | * @param rx compiled glibc regex. | ||
188 | * @param rxstr regular expression and strings with expected results to | ||
189 | * match against. | ||
190 | * | ||
191 | * @return 0 on successfull, non 0 otherwise | ||
192 | */ | ||
193 | int | ||
194 | test_automaton (struct REGEX_INTERNAL_Automaton *a, regex_t * rx, | ||
195 | struct Regex_String_Pair *rxstr) | ||
196 | { | ||
197 | int result; | ||
198 | int eval; | ||
199 | int eval_check; | ||
200 | char error[200]; | ||
201 | regmatch_t matchptr[1]; | ||
202 | int i; | ||
203 | |||
204 | if (NULL == a) | ||
205 | { | ||
206 | GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Automaton was NULL\n"); | ||
207 | return 1; | ||
208 | } | ||
209 | |||
210 | result = 0; | ||
211 | |||
212 | for (i = 0; i < rxstr->string_count; i++) | ||
213 | { | ||
214 | eval = REGEX_INTERNAL_eval (a, rxstr->strings[i]); | ||
215 | eval_check = regexec (rx, rxstr->strings[i], 1, matchptr, 0); | ||
216 | |||
217 | /* We only want to match the whole string, because that's what our DFA does, | ||
218 | * too. */ | ||
219 | if (eval_check == 0 && | ||
220 | (matchptr[0].rm_so != 0 || | ||
221 | matchptr[0].rm_eo != strlen (rxstr->strings[i]))) | ||
222 | eval_check = 1; | ||
223 | |||
224 | if ((rxstr->expected_results[i] == match && (0 != eval || 0 != eval_check)) | ||
225 | || (rxstr->expected_results[i] == nomatch && | ||
226 | (0 == eval || 0 == eval_check))) | ||
227 | { | ||
228 | result = 1; | ||
229 | regerror (eval_check, rx, error, sizeof error); | ||
230 | GNUNET_log (GNUNET_ERROR_TYPE_ERROR, | ||
231 | "Unexpected result:\nregex: %s\ncanonical_regex: %s\n" | ||
232 | "string: %s\nexpected result: %i\n" | ||
233 | "gnunet regex: %i\nglibc regex: %i\nglibc error: %s\n" | ||
234 | "rm_so: %i\nrm_eo: %i\n\n", rxstr->regex, | ||
235 | REGEX_INTERNAL_get_canonical_regex (a), rxstr->strings[i], | ||
236 | rxstr->expected_results[i], eval, eval_check, error, | ||
237 | matchptr[0].rm_so, matchptr[0].rm_eo); | ||
238 | } | ||
239 | } | ||
240 | return result; | ||
241 | } | ||
242 | |||
243 | int | ||
244 | main (int argc, char *argv[]) | ||
245 | { | ||
246 | GNUNET_log_setup ("test-regex", "WARNING", NULL); | ||
247 | |||
248 | struct REGEX_INTERNAL_Automaton *a; | ||
249 | regex_t rx; | ||
250 | int i; | ||
251 | int check_nfa; | ||
252 | int check_dfa; | ||
253 | int check_rand; | ||
254 | char *check_proof; | ||
255 | |||
256 | struct Regex_String_Pair rxstr[19] = { | ||
257 | {"ab?(abcd)?", 5, | ||
258 | {"ababcd", "abab", "aabcd", "a", "abb"}, | ||
259 | {match, nomatch, match, match, nomatch}}, | ||
260 | {"ab(c|d)+c*(a(b|c)d)+", 5, | ||
261 | {"abcdcdcdcdddddabd", "abcd", | ||
262 | "abcddddddccccccccccccccccccccccccabdacdabd", | ||
263 | "abccccca", "abcdcdcdccdabdabd"}, | ||
264 | {match, nomatch, match, nomatch, match}}, | ||
265 | {"ab+c*(a(bx|c)d)+", 5, | ||
266 | {"abcdcdcdcdddddabd", "abcd", | ||
267 | "abcddddddccccccccccccccccccccccccabdacdabd", | ||
268 | "abccccca", "abcdcdcdccdabdabd"}, | ||
269 | {nomatch, nomatch, nomatch, nomatch, nomatch}}, | ||
270 | {"a+X*y+c|p|R|Z*K*y*R+w|Y*6+n+h*k*w+V*F|W*B*e*", 1, | ||
271 | {"kaXycQepRZKyRwY6nhkwVFWBegNVtLPj39XhJJ6bEifRSZRYZg"}, | ||
272 | {nomatch}}, | ||
273 | {"k|a+X*y+c|Q*e|p|R|Z*K*y*R+w|Y*6+n+h*k*w+V*F|W*B*e*g|N+V|t+L|P*j*3*9+X*h*J|J*6|b|E*i*f*R+S|Z|R|Y*Z|g*", 1, | ||
274 | {"kaXycQepRZKyRwY6nhkwVFWBegNVtLPj39XhJJ6bEifRSZRYZg"}, | ||
275 | {nomatch}}, | ||
276 | {"F?W+m+2*6*c*s|P?U?a|B|y*i+t+A|V|6*C*7*e?Z*n*i|J?5+g?W*V?7*j?p?1|r?B?C+E+3+6*i+W*P?K?0|D+7?y*m+3?g?K?", 1, | ||
277 | {"osfjsodfonONONOnosndfsdnfsd"}, | ||
278 | {nomatch}}, | ||
279 | {"V|M*o?x*p*d+h+b|E*m?h?Y*E*O?W*W*P+o?Z+H*M|I*q+C*a+5?5*9|b?z|G*y*k?R|p+u|8*h?B+l*H|e|L*O|1|F?v*0?5|C+", 1, | ||
280 | {"VMoxpdhbEmhYEOWWPoZHMIqCa559bzGykRpu8hBlHeLO1Fv05C"}, | ||
281 | {nomatch}}, | ||
282 | {"(bla)*", 8, | ||
283 | {"", "bla", "blabla", "bl", "la", "b", "l", "a"}, | ||
284 | {match, match, match, nomatch, nomatch, nomatch, nomatch, nomatch}}, | ||
285 | {"ab(c|d)+c*(a(b|c)+d)+(bla)(bla)*", 8, | ||
286 | {"ab", "abcabdbla", "abdcccccccccccabcbccdblablabla", "bl", "la", "b", | ||
287 | "l", | ||
288 | "a"}, | ||
289 | {nomatch, match, match, nomatch, nomatch, nomatch, nomatch, nomatch}}, | ||
290 | {"a|aa*a", 6, | ||
291 | {"", "a", "aa", "aaa", "aaaa", "aaaaa"}, | ||
292 | {nomatch, match, match, match, match, match}}, | ||
293 | {"ab(c|d)+c*(a(b|c)+d)+(bla)+", 1, | ||
294 | {"abcabdblaacdbla"}, | ||
295 | {nomatch}}, | ||
296 | {"(ac|b)+", 8, | ||
297 | {"b", "bb", "ac", "", "acb", "bacbacac", "acacac", "abc"}, | ||
298 | {match, match, match, nomatch, match, match, match, nomatch}}, | ||
299 | {"(ab|c)+", 7, | ||
300 | {"", "ab", "c", "abc", "ababcc", "acc", "abac"}, | ||
301 | {nomatch, match, match, match, match, nomatch, nomatch}}, | ||
302 | {"((j|2j)K|(j|2j)AK|(j|2j)(D|e|(j|2j)A(D|e))D*K)", 1, | ||
303 | {"", "2j2jADK", "j2jADK"}, | ||
304 | {nomatch, match, match}}, | ||
305 | {"((j|2j)K|(j|2j)(D|e|((j|2j)j|(j|2j)2j)A(D|e))D*K|(j|2j)AK)", 2, | ||
306 | {"", "2j2jjADK", "j2jADK"}, | ||
307 | {nomatch, match, match}}, | ||
308 | {"ab(c|d)+c*(a(b|c)d)+", 1, | ||
309 | {"abacd"}, | ||
310 | {nomatch}}, | ||
311 | {"d|5kl", 1, | ||
312 | {"d5kl"}, | ||
313 | {nomatch}}, | ||
314 | {"a()b", 1, | ||
315 | {"ab"}, | ||
316 | {match}}, | ||
317 | {"GNVPN-0001-PAD(001110101001001010(0|1)*|001110101001001010000(0|1)*|001110101001001010001(0|1)*|001110101001001010010(0|1)*|001110101001001010011(0|1)*|001110101001001010100(0|1)*|001110101001001010101(0|1)*|001110101001001010110(0|1)*|001110101001001010111(0|1)*|0011101010110110(0|1)*|001110101011011000000(0|1)*|001110101011011000001(0|1)*|001110101011011000010(0|1)*|001110101011011000011(0|1)*|001110101011011000100(0|1)*|001110101011011000101(0|1)*|001110101011011000110(0|1)*|001110101011011000111(0|1)*|001110101011011001000(0|1)*|001110101011011001001(0|1)*|001110101011011001010(0|1)*|001110101011011001011(0|1)*|001110101011011001100(0|1)*|001110101011011001101(0|1)*|001110101011011001110(0|1)*|001110101011011001111(0|1)*|001110101011011010000(0|1)*|001110101011011010001(0|1)*|001110101011011010010(0|1)*|001110101011011010011(0|1)*|001110101011011010100(0|1)*|001110101011011010101(0|1)*|001110101011011010110(0|1)*|001110101011011010111(0|1)*|001110101011011011000(0|1)*|001110101011011011001(0|1)*|001110101011011011010(0|1)*|001110101011011011011(0|1)*|001110101011011011100(0|1)*|001110101011011011101(0|1)*|001110101011011011110(0|1)*|001110101011011011111(0|1)*|0011101110111101(0|1)*|001110111011110100000(0|1)*|001110111011110100001(0|1)*|001110111011110100010(0|1)*|001110111011110100011(0|1)*|001110111011110100100(0|1)*|001110111011110100101(0|1)*|001110111011110100110(0|1)*|001110111011110100111(0|1)*|001110111011110101000(0|1)*|001110111011110101001(0|1)*|001110111011110101010(0|1)*|001110111011110101011(0|1)*|001110111011110101100(0|1)*|001110111011110101101(0|1)*|001110111011110101110(0|1)*|001110111011110101111(0|1)*|001110111011110110000(0|1)*|001110111011110110001(0|1)*|001110111011110110010(0|1)*|001110111011110110011(0|1)*|001110111011110110100(0|1)*|001110111011110110101(0|1)*|001110111011110110110(0|1)*|001110111011110110111(0|1)*|001110111011110111000(0|1)*|001110111011110111001(0|1)*|001110111011110111010(0|1)*|001110111011110111011(0|1)*|001110111011110111100(0|1)*|001110111011110111101(0|1)*|001110111011110111110(0|1)*|0111010001010110(0|1)*|011101000101011000000(0|1)*|011101000101011000001(0|1)*|011101000101011000010(0|1)*|011101000101011000011(0|1)*|011101000101011000100(0|1)*|011101000101011000101(0|1)*|011101000101011000110(0|1)*|011101000101011000111(0|1)*|011101000101011001000(0|1)*|011101000101011001001(0|1)*|011101000101011001010(0|1)*|011101000101011001011(0|1)*|011101000101011001100(0|1)*|011101000101011001101(0|1)*|011101000101011001110(0|1)*|011101000101011001111(0|1)*|011101000101011010000(0|1)*|011101000101011010001(0|1)*|011101000101011010010(0|1)*|011101000101011010011(0|1)*|011101000101011010100(0|1)*|011101000101011010101(0|1)*|011101000101011010110(0|1)*|011101000101011010111(0|1)*|011101000101011011000(0|1)*|011101000101011011001(0|1)*|011101000101011011010(0|1)*|011101000101011011011(0|1)*|011101000101011011100(0|1)*|011101000101011011101(0|1)*|011101000101011011110(0|1)*|011101000101011011111(0|1)*|0111010001010111(0|1)*|011101000101011100000(0|1)*|011101000101011100001(0|1)*|011101000101011100010(0|1)*|011101000101011100011(0|1)*|011101000101011100100(0|1)*|011101000101011100101(0|1)*|011101000101011100110(0|1)*|011101000101011100111(0|1)*|011101000101011101000(0|1)*|011101000101011101001(0|1)*|011101000101011101010(0|1)*|011101000101011101011(0|1)*|011101000101011101100(0|1)*|011101000101011101101(0|1)*|011101000101011101110(0|1)*|011101000101011101111(0|1)*|011101000101011110000(0|1)*|011101000101011110001(0|1)*|011101000101011110010(0|1)*|011101000101011110011(0|1)*|011101000101011110100(0|1)*|011101000101011110101(0|1)*|011101000101011110110(0|1)*|011101000101011110111(0|1)*|011101000101011111000(0|1)*|011101000101011111001(0|1)*|011101000101011111010(0|1)*|011101000101011111011(0|1)*|011101000101011111100(0|1)*|011101000101011111101(0|1)*|011101000101011111110(0|1)*|011101000101011111111(0|1)*|0111010001011000(0|1)*|011101000101100000000(0|1)*|011101000101100000001(0|1)*|011101000101100000010(0|1)*|011101000101100000011(0|1)*|011101000101100000100(0|1)*|011101000101100000101(0|1)*|011101000101100000110(0|1)*|011101000101100000111(0|1)*|011101000101100001000(0|1)*|011101000101100001001(0|1)*|011101000101100001010(0|1)*|011101000101100001011(0|1)*|011101000101100001100(0|1)*|011101000101100001101(0|1)*|011101000101100001110(0|1)*|011101000101100001111(0|1)*|011101000101100010000(0|1)*|011101000101100010001(0|1)*|011101000101100010010(0|1)*|011101000101100010011(0|1)*|011101000101100010100(0|1)*|011101000101100010101(0|1)*|011101000101100010110(0|1)*|011101000101100010111(0|1)*|011101000101100011000(0|1)*|011101000101100011001(0|1)*|011101000101100011010(0|1)*|011101000101100011011(0|1)*|011101000101100011100(0|1)*|011101000101100011101(0|1)*|011101000101100011110(0|1)*|011101000101100011111(0|1)*|01110100010110010(0|1)*|011101000101100100000(0|1)*|011101000101100100001(0|1)*|011101000101100100010(0|1)*|011101000101100100011(0|1)*|011101000101100100100(0|1)*|011101000101100100101(0|1)*|011101000101100100110(0|1)*|011101000101100100111(0|1)*|011101000101100101000(0|1)*|011101000101100101001(0|1)*|011101000101100101010(0|1)*|011101000101100101011(0|1)*|011101000101100101100(0|1)*|011101000101100101101(0|1)*|011101000101100101110(0|1)*|011101000101100101111(0|1)*|011101000101100101111000(0|1)*|1100101010011100(0|1)*|110010101001110000000(0|1)*|110010101001110000000001(0|1)*|110010101001110000000010(0|1)*|110010101001110000000110(0|1)*|110010101001110000001(0|1)*|110010101001110000001000(0|1)*|110010101001110000001001(0|1)*|110010101001110000001010(0|1)*|110010101001110000001011(0|1)*|110010101001110000001101(0|1)*|110010101001110000001110(0|1)*|110010101001110000010(0|1)*|110010101001110000011(0|1)*|110010101001110000100(0|1)*|110010101001110000101(0|1)*|110010101001110000110(0|1)*|110010101001110000111(0|1)*|110010101001110001000(0|1)*|110010101001110001001(0|1)*|110010101001110001010(0|1)*|110010101001110001011(0|1)*|110010101001110001100(0|1)*|110010101001110001101(0|1)*|110010101001110001110(0|1)*|110010101001110001111(0|1)*|110010101001110010000(0|1)*|110010101001110010001(0|1)*|110010101001110010010(0|1)*|110010101001110010011(0|1)*|110010101001110010100(0|1)*|110010101001110010101(0|1)*|110010101001110010110(0|1)*|110010101001110010111(0|1)*|110010101001110011000(0|1)*|110010101001110011001(0|1)*|110010101001110011010(0|1)*|110010101001110011011(0|1)*|110010101001110011100(0|1)*|110010101001110011101(0|1)*|110010101001110011110(0|1)*|110010101001110011111(0|1)*|1101101010111010(0|1)*|110110101011101000000(0|1)*|110110101011101000000001(0|1)*|110110101011101000001000(0|1)*|110110101011101000001001(0|1)*|110110101011101000001010(0|1)*|110110101011101000001011(0|1)*|110110101011101000001100(0|1)*|110110101011101000001110(0|1)*|110110101011101000001111(0|1)*|110110101011101000010(0|1)*|110110101011101000010000(0|1)*|110110101011101000010001(0|1)*|110110101011101000010010(0|1)*|110110101011101000010011(0|1)*|110110101011101000011(0|1)*|110110101011101000100(0|1)*|110110101011101000101(0|1)*|110110101011101000110(0|1)*|110110101011101000111(0|1)*|110110101011101001000(0|1)*|110110101011101001001(0|1)*|110110101011101001010(0|1)*|110110101011101001011(0|1)*|110110101011101001100(0|1)*|110110101011101001101(0|1)*|110110101011101001110(0|1)*|110110101011101001111(0|1)*|110110101011101010000(0|1)*|110110101011101010001(0|1)*|110110101011101010010(0|1)*|110110101011101010011(0|1)*|110110101011101010100(0|1)*|110110101011101010101(0|1)*|110110101011101010110(0|1)*|110110101011101010111(0|1)*|110110101011101011000(0|1)*|110110101011101011001(0|1)*|110110101011101011010(0|1)*|110110101011101011011(0|1)*|110110101011101011100(0|1)*|110110101011101011101(0|1)*|110110101011101011110(0|1)*|110110101011101011111(0|1)*|1101101011010100(0|1)*|110110101101010000000(0|1)*|110110101101010000001(0|1)*|110110101101010000010(0|1)*|110110101101010000011(0|1)*|110110101101010000100(0|1)*|110110101101010000101(0|1)*|110110101101010000110(0|1)*|110110101101010000111(0|1)*|110110101101010001000(0|1)*|110110101101010001001(0|1)*|110110101101010001010(0|1)*|110110101101010001011(0|1)*|110110101101010001100(0|1)*|110110101101010001101(0|1)*|110110101101010001110(0|1)*|110110101101010001111(0|1)*|110110101101010010000(0|1)*|110110101101010010001(0|1)*|110110101101010010010(0|1)*|110110101101010010011(0|1)*|110110101101010010100(0|1)*|1101101011010100101000(0|1)*|110110101101010010101(0|1)*|110110101101010010110(0|1)*|110110101101010010111(0|1)*|110110101101010011000(0|1)*|110110101101010011010(0|1)*|110110101101010011011(0|1)*|110110101101010011100(0|1)*|110110101101010011101(0|1)*|110110101101010011110(0|1)*|110110101101010011111(0|1)*|1101111010100100(0|1)*|110111101010010000000(0|1)*|110111101010010000001(0|1)*|110111101010010000010(0|1)*|110111101010010000011(0|1)*|110111101010010000100(0|1)*|110111101010010000101(0|1)*|110111101010010000110(0|1)*|110111101010010000111(0|1)*|110111101010010001000(0|1)*|110111101010010001001(0|1)*|110111101010010001010(0|1)*|110111101010010001011(0|1)*|110111101010010001100(0|1)*|110111101010010001101(0|1)*|110111101010010001110(0|1)*|110111101010010001111(0|1)*|110111101010010010000(0|1)*|110111101010010010001(0|1)*|110111101010010010010(0|1)*|110111101010010010011(0|1)*|110111101010010010100(0|1)*|110111101010010010101(0|1)*|110111101010010010110(0|1)*|110111101010010010111(0|1)*|110111101010010011000(0|1)*|110111101010010011001(0|1)*|110111101010010011010(0|1)*|110111101010010011011(0|1)*|110111101010010011100(0|1)*|110111101010010011101(0|1)*|110111101010010011110(0|1)*|110111101010010011111(0|1)*|11011110101001010(0|1)*|110111101010010100000(0|1)*|110111101010010100001(0|1)*|110111101010010100010(0|1)*|110111101010010100011(0|1)*|110111101010010100100(0|1)*|110111101010010100101(0|1)*|110111101010010100110(0|1)*|110111101010010100111(0|1)*|110111101010010101000(0|1)*|110111101010010101001(0|1)*|110111101010010101010(0|1)*|110111101010010101011(0|1)*|110111101010010101100(0|1)*|110111101010010101101(0|1)*|110111101010010101110(0|1)*|110111101010010101111(0|1)*)", | ||
318 | 2, | ||
319 | {"GNVPN-0001-PAD1101111010100101011101010101010101", | ||
320 | "GNVPN-0001-PAD11001010100111000101101010101"}, | ||
321 | {match, match}} | ||
322 | }; | ||
323 | |||
324 | check_nfa = 0; | ||
325 | check_dfa = 0; | ||
326 | check_rand = 0; | ||
327 | |||
328 | for (i = 0; i < 19; i++) | ||
329 | { | ||
330 | if (0 != regcomp (&rx, rxstr[i].regex, REG_EXTENDED)) | ||
331 | { | ||
332 | GNUNET_log (GNUNET_ERROR_TYPE_ERROR, | ||
333 | "Could not compile regex using regcomp()\n"); | ||
334 | return 1; | ||
335 | } | ||
336 | |||
337 | /* NFA test */ | ||
338 | a = REGEX_INTERNAL_construct_nfa (rxstr[i].regex, strlen (rxstr[i].regex)); | ||
339 | check_nfa += test_automaton (a, &rx, &rxstr[i]); | ||
340 | REGEX_INTERNAL_automaton_destroy (a); | ||
341 | |||
342 | /* DFA test */ | ||
343 | a = REGEX_INTERNAL_construct_dfa (rxstr[i].regex, strlen (rxstr[i].regex), 0); | ||
344 | check_dfa += test_automaton (a, &rx, &rxstr[i]); | ||
345 | check_proof = GNUNET_strdup (REGEX_INTERNAL_get_canonical_regex (a)); | ||
346 | REGEX_INTERNAL_automaton_destroy (a); | ||
347 | |||
348 | a = REGEX_INTERNAL_construct_dfa (check_proof, strlen (check_proof), 0); | ||
349 | check_dfa += test_automaton (a, &rx, &rxstr[i]); | ||
350 | REGEX_INTERNAL_automaton_destroy (a); | ||
351 | if (0 != check_dfa) | ||
352 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "check_proof: %s\n", check_proof); | ||
353 | GNUNET_free_non_null (check_proof); | ||
354 | |||
355 | regfree (&rx); | ||
356 | } | ||
357 | |||
358 | /* Random tests */ | ||
359 | srand (time (NULL)); | ||
360 | for (i = 0; i < 20; i++) | ||
361 | check_rand += test_random (50, 60, 10); | ||
362 | |||
363 | return check_nfa + check_dfa + check_rand; | ||
364 | } | ||
diff --git a/src/regex/test_regex_graph_api.c b/src/regex/test_regex_graph_api.c new file mode 100644 index 000000000..46eacc1d7 --- /dev/null +++ b/src/regex/test_regex_graph_api.c | |||
@@ -0,0 +1,156 @@ | |||
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 | /** | ||
19 | * @file regex/test_regex_graph_api.c | ||
20 | * @brief test for regex_graph.c | ||
21 | * @author Maximilian Szengel | ||
22 | */ | ||
23 | #include <regex.h> | ||
24 | #include <time.h> | ||
25 | #include "platform.h" | ||
26 | #include "regex_internal_lib.h" | ||
27 | #include "regex_test_lib.h" | ||
28 | #include "regex_internal.h" | ||
29 | |||
30 | #define KEEP_FILES 1 | ||
31 | |||
32 | /** | ||
33 | * Check if 'filename' exists and is not empty. | ||
34 | * | ||
35 | * @param filename name of the file that should be checked | ||
36 | * | ||
37 | * @return 0 if ok, non 0 on error. | ||
38 | */ | ||
39 | static int | ||
40 | filecheck (const char *filename) | ||
41 | { | ||
42 | int error = 0; | ||
43 | FILE *fp; | ||
44 | |||
45 | /* Check if file was created and delete it again */ | ||
46 | if (NULL == (fp = fopen (filename, "r"))) | ||
47 | { | ||
48 | GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Could not find graph %s\n", filename); | ||
49 | return 1; | ||
50 | } | ||
51 | |||
52 | GNUNET_break (0 == fseek (fp, 0L, SEEK_END)); | ||
53 | if (1 > ftell (fp)) | ||
54 | { | ||
55 | GNUNET_log (GNUNET_ERROR_TYPE_ERROR, | ||
56 | "Graph writing failed, got empty file (%s)!\n", filename); | ||
57 | error = 2; | ||
58 | } | ||
59 | |||
60 | GNUNET_assert (0 == fclose (fp)); | ||
61 | |||
62 | if (!KEEP_FILES) | ||
63 | { | ||
64 | if (0 != unlink (filename)) | ||
65 | GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_ERROR, "unlink", filename); | ||
66 | } | ||
67 | return error; | ||
68 | } | ||
69 | |||
70 | |||
71 | int | ||
72 | main (int argc, char *argv[]) | ||
73 | { | ||
74 | int error; | ||
75 | struct REGEX_INTERNAL_Automaton *a; | ||
76 | unsigned int i; | ||
77 | const char *filename = "test_graph.dot"; | ||
78 | |||
79 | const char *regex[12] = { | ||
80 | "ab(c|d)+c*(a(b|c)+d)+(bla)+", | ||
81 | "(bla)*", | ||
82 | "b(lab)*la", | ||
83 | "(ab)*", | ||
84 | "ab(c|d)+c*(a(b|c)+d)+(bla)(bla)*", | ||
85 | "z(abc|def)?xyz", | ||
86 | "1*0(0|1)*", | ||
87 | "a*b*", | ||
88 | "a+X*y+c|p|R|Z*K*y*R+w|Y*6+n+h*k*w+V*F|W*B*e*", | ||
89 | "a", | ||
90 | "a|b", | ||
91 | "PADPADPADPADPADPabcdefghixxxxxxxxxxxxxjklmnop*qstoisdjfguisdfguihsdfgbdsuivggsd" | ||
92 | }; | ||
93 | |||
94 | GNUNET_log_setup ("test-regex", "WARNING", NULL); | ||
95 | error = 0; | ||
96 | for (i = 0; i < 12; i++) | ||
97 | { | ||
98 | /* Check NFA graph creation */ | ||
99 | a = REGEX_INTERNAL_construct_nfa (regex[i], strlen (regex[i])); | ||
100 | REGEX_TEST_automaton_save_graph (a, filename, REGEX_TEST_GRAPH_DEFAULT); | ||
101 | REGEX_INTERNAL_automaton_destroy (a); | ||
102 | error += filecheck (filename); | ||
103 | |||
104 | a = REGEX_INTERNAL_construct_nfa (regex[i], strlen (regex[i])); | ||
105 | REGEX_TEST_automaton_save_graph (a, filename, | ||
106 | REGEX_TEST_GRAPH_DEFAULT | | ||
107 | REGEX_TEST_GRAPH_VERBOSE); | ||
108 | REGEX_INTERNAL_automaton_destroy (a); | ||
109 | error += filecheck (filename); | ||
110 | |||
111 | a = REGEX_INTERNAL_construct_nfa (regex[i], strlen (regex[i])); | ||
112 | REGEX_TEST_automaton_save_graph (a, filename, | ||
113 | REGEX_TEST_GRAPH_DEFAULT | | ||
114 | REGEX_TEST_GRAPH_COLORING); | ||
115 | REGEX_INTERNAL_automaton_destroy (a); | ||
116 | error += filecheck (filename); | ||
117 | |||
118 | a = REGEX_INTERNAL_construct_nfa (regex[i], strlen (regex[i])); | ||
119 | REGEX_TEST_automaton_save_graph (a, filename, | ||
120 | REGEX_TEST_GRAPH_DEFAULT | | ||
121 | REGEX_TEST_GRAPH_VERBOSE | | ||
122 | REGEX_TEST_GRAPH_COLORING); | ||
123 | REGEX_INTERNAL_automaton_destroy (a); | ||
124 | error += filecheck (filename); | ||
125 | |||
126 | |||
127 | /* Check DFA graph creation */ | ||
128 | a = REGEX_INTERNAL_construct_dfa (regex[i], strlen (regex[i]), 0); | ||
129 | REGEX_TEST_automaton_save_graph (a, filename, REGEX_TEST_GRAPH_DEFAULT); | ||
130 | REGEX_INTERNAL_automaton_destroy (a); | ||
131 | error += filecheck (filename); | ||
132 | |||
133 | a = REGEX_INTERNAL_construct_dfa (regex[i], strlen (regex[i]), 0); | ||
134 | REGEX_TEST_automaton_save_graph (a, filename, | ||
135 | REGEX_TEST_GRAPH_DEFAULT | | ||
136 | REGEX_TEST_GRAPH_VERBOSE); | ||
137 | REGEX_INTERNAL_automaton_destroy (a); | ||
138 | error += filecheck (filename); | ||
139 | |||
140 | a = REGEX_INTERNAL_construct_dfa (regex[i], strlen (regex[i]), 0); | ||
141 | REGEX_TEST_automaton_save_graph (a, filename, | ||
142 | REGEX_TEST_GRAPH_DEFAULT | | ||
143 | REGEX_TEST_GRAPH_COLORING); | ||
144 | REGEX_INTERNAL_automaton_destroy (a); | ||
145 | error += filecheck (filename); | ||
146 | |||
147 | |||
148 | a = REGEX_INTERNAL_construct_dfa (regex[i], strlen (regex[i]), 4); | ||
149 | REGEX_TEST_automaton_save_graph (a, filename, REGEX_TEST_GRAPH_DEFAULT); | ||
150 | REGEX_INTERNAL_automaton_destroy (a); | ||
151 | error += filecheck (filename); | ||
152 | |||
153 | } | ||
154 | |||
155 | return error; | ||
156 | } | ||
diff --git a/src/regex/test_regex_integration.c b/src/regex/test_regex_integration.c new file mode 100644 index 000000000..99287243e --- /dev/null +++ b/src/regex/test_regex_integration.c | |||
@@ -0,0 +1,206 @@ | |||
1 | /* | ||
2 | This file is part of GNUnet. | ||
3 | Copyright (C) 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 | /** | ||
19 | * @file regex/test_regex_integration.c | ||
20 | * @brief base test case for regex integration with VPN; | ||
21 | * tests that the regexes generated by the TUN API | ||
22 | * for IP addresses work (for some simple cases) | ||
23 | * @author Christian Grothoff | ||
24 | */ | ||
25 | #include "platform.h" | ||
26 | #include "gnunet_applications.h" | ||
27 | #include "gnunet_util_lib.h" | ||
28 | #include "gnunet_tun_lib.h" | ||
29 | #include "gnunet_testing_lib.h" | ||
30 | #include "gnunet_regex_service.h" | ||
31 | |||
32 | |||
33 | /** | ||
34 | * How long until we really give up on a particular testcase portion? | ||
35 | */ | ||
36 | #define TOTAL_TIMEOUT GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS, 600) | ||
37 | |||
38 | /** | ||
39 | * How long until we give up on any particular operation (and retry)? | ||
40 | */ | ||
41 | #define BASE_TIMEOUT GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS, 3) | ||
42 | |||
43 | |||
44 | static struct GNUNET_REGEX_Announcement *a4; | ||
45 | |||
46 | static struct GNUNET_REGEX_Search *s4; | ||
47 | |||
48 | static struct GNUNET_REGEX_Announcement *a6; | ||
49 | |||
50 | static struct GNUNET_REGEX_Search *s6; | ||
51 | |||
52 | static int ok = 1; | ||
53 | |||
54 | static struct GNUNET_SCHEDULER_Task *die_task; | ||
55 | |||
56 | |||
57 | static void | ||
58 | end (void *cls) | ||
59 | { | ||
60 | die_task = NULL; | ||
61 | GNUNET_REGEX_announce_cancel (a4); | ||
62 | a4 = NULL; | ||
63 | GNUNET_REGEX_search_cancel (s4); | ||
64 | s4 = NULL; | ||
65 | GNUNET_REGEX_announce_cancel (a6); | ||
66 | a6 = NULL; | ||
67 | GNUNET_REGEX_search_cancel (s6); | ||
68 | s6 = NULL; | ||
69 | ok = 0; | ||
70 | } | ||
71 | |||
72 | |||
73 | static void | ||
74 | end_badly () | ||
75 | { | ||
76 | FPRINTF (stderr, "%s", "Testcase failed (timeout).\n"); | ||
77 | end (NULL); | ||
78 | ok = 1; | ||
79 | } | ||
80 | |||
81 | |||
82 | /** | ||
83 | * Search callback function, invoked for every result that was found. | ||
84 | * | ||
85 | * @param cls Closure provided in #GNUNET_REGEX_search(). | ||
86 | * @param id Peer providing a regex that matches the string. | ||
87 | * @param get_path Path of the get request. | ||
88 | * @param get_path_length Length of @a get_path. | ||
89 | * @param put_path Path of the put request. | ||
90 | * @param put_path_length Length of the @a put_path. | ||
91 | */ | ||
92 | static void | ||
93 | found_cb (void *cls, | ||
94 | const struct GNUNET_PeerIdentity *id, | ||
95 | const struct GNUNET_PeerIdentity *get_path, | ||
96 | unsigned int get_path_length, | ||
97 | const struct GNUNET_PeerIdentity *put_path, | ||
98 | unsigned int put_path_length) | ||
99 | { | ||
100 | const char *str = cls; | ||
101 | static int found; | ||
102 | |||
103 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, | ||
104 | "IPv%s-exit found\n", | ||
105 | str); | ||
106 | if (0 == strcmp (str, "4")) | ||
107 | found |= 4; | ||
108 | if (0 == strcmp (str, "6")) | ||
109 | found |= 2; | ||
110 | if ((4|2) == found) | ||
111 | { | ||
112 | GNUNET_SCHEDULER_cancel (die_task); | ||
113 | die_task = | ||
114 | GNUNET_SCHEDULER_add_now (&end, NULL); | ||
115 | } | ||
116 | } | ||
117 | |||
118 | |||
119 | static void | ||
120 | run (void *cls, | ||
121 | const struct GNUNET_CONFIGURATION_Handle *cfg, | ||
122 | struct GNUNET_TESTING_Peer *peer) | ||
123 | { | ||
124 | char rxstr4[GNUNET_TUN_IPV4_REGEXLEN]; | ||
125 | char rxstr6[GNUNET_TUN_IPV6_REGEXLEN]; | ||
126 | char *p4r; | ||
127 | char *p6r; | ||
128 | char *p4; | ||
129 | char *p6; | ||
130 | char *ss4; | ||
131 | char *ss6; | ||
132 | struct in_addr i4; | ||
133 | struct in6_addr i6; | ||
134 | |||
135 | die_task = | ||
136 | GNUNET_SCHEDULER_add_delayed (TOTAL_TIMEOUT, | ||
137 | &end_badly, NULL); | ||
138 | GNUNET_assert (1 == | ||
139 | inet_pton (AF_INET, | ||
140 | "127.0.0.1", | ||
141 | &i4)); | ||
142 | GNUNET_assert (1 == | ||
143 | inet_pton (AF_INET6, | ||
144 | "::1:5", | ||
145 | &i6)); | ||
146 | GNUNET_TUN_ipv4toregexsearch (&i4, | ||
147 | 8080, | ||
148 | rxstr4); | ||
149 | GNUNET_TUN_ipv6toregexsearch (&i6, | ||
150 | 8686, | ||
151 | rxstr6); | ||
152 | GNUNET_asprintf (&ss4, | ||
153 | "%s%s", | ||
154 | GNUNET_APPLICATION_TYPE_EXIT_REGEX_PREFIX, | ||
155 | rxstr4); | ||
156 | GNUNET_asprintf (&ss6, | ||
157 | "%s%s", | ||
158 | GNUNET_APPLICATION_TYPE_EXIT_REGEX_PREFIX, | ||
159 | rxstr6); | ||
160 | p4r = GNUNET_TUN_ipv4policy2regex ("0.0.0.0/0:!25;"); | ||
161 | p6r = GNUNET_TUN_ipv6policy2regex ("::/0:!25;"); | ||
162 | GNUNET_asprintf (&p4, | ||
163 | "%s%s", | ||
164 | GNUNET_APPLICATION_TYPE_EXIT_REGEX_PREFIX, | ||
165 | p4r); | ||
166 | GNUNET_asprintf (&p6, | ||
167 | "%s%s", | ||
168 | GNUNET_APPLICATION_TYPE_EXIT_REGEX_PREFIX, | ||
169 | p6r); | ||
170 | GNUNET_free (p4r); | ||
171 | GNUNET_free (p6r); | ||
172 | a4 = GNUNET_REGEX_announce (cfg, | ||
173 | p4, | ||
174 | GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS, | ||
175 | 5), | ||
176 | 1); | ||
177 | a6 = GNUNET_REGEX_announce (cfg, | ||
178 | p6, | ||
179 | GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS, | ||
180 | 5), | ||
181 | 1); | ||
182 | GNUNET_free (p4); | ||
183 | GNUNET_free (p6); | ||
184 | |||
185 | s4 = GNUNET_REGEX_search (cfg, | ||
186 | ss4, | ||
187 | &found_cb, "4"); | ||
188 | s6 = GNUNET_REGEX_search (cfg, | ||
189 | ss6, | ||
190 | &found_cb, "6"); | ||
191 | GNUNET_free (ss4); | ||
192 | GNUNET_free (ss6); | ||
193 | } | ||
194 | |||
195 | |||
196 | int | ||
197 | main (int argc, char *argv[]) | ||
198 | { | ||
199 | if (0 != GNUNET_TESTING_peer_run ("test-regex-integration", | ||
200 | "test_regex_api_data.conf", | ||
201 | &run, NULL)) | ||
202 | return 1; | ||
203 | return ok; | ||
204 | } | ||
205 | |||
206 | /* end of test_regex_integration.c */ | ||
diff --git a/src/regex/test_regex_iterate_api.c b/src/regex/test_regex_iterate_api.c new file mode 100644 index 000000000..f4cc725e0 --- /dev/null +++ b/src/regex/test_regex_iterate_api.c | |||
@@ -0,0 +1,258 @@ | |||
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 | /** | ||
19 | * @file regex/test_regex_iterate_api.c | ||
20 | * @brief test for regex.c | ||
21 | * @author Maximilian Szengel | ||
22 | */ | ||
23 | #include <regex.h> | ||
24 | #include <time.h> | ||
25 | #include "platform.h" | ||
26 | #include "regex_internal_lib.h" | ||
27 | #include "regex_block_lib.h" | ||
28 | #include "regex_internal.h" | ||
29 | |||
30 | /** | ||
31 | * Regex initial padding. | ||
32 | */ | ||
33 | #define INITIAL_PADDING "PADPADPADPADPADP" | ||
34 | |||
35 | /** | ||
36 | * Set to GNUNET_YES to save a debug graph. | ||
37 | */ | ||
38 | #define REGEX_INTERNAL_ITERATE_SAVE_DEBUG_GRAPH GNUNET_NO | ||
39 | |||
40 | static unsigned int transition_counter; | ||
41 | |||
42 | struct IteratorContext | ||
43 | { | ||
44 | int error; | ||
45 | int should_save_graph; | ||
46 | FILE *graph_filep; | ||
47 | unsigned int string_count; | ||
48 | char *const *strings; | ||
49 | unsigned int match_count; | ||
50 | }; | ||
51 | |||
52 | struct RegexStringPair | ||
53 | { | ||
54 | char *regex; | ||
55 | unsigned int string_count; | ||
56 | char *strings[20]; | ||
57 | }; | ||
58 | |||
59 | |||
60 | static void | ||
61 | key_iterator (void *cls, const struct GNUNET_HashCode *key, | ||
62 | const char *proof, | ||
63 | int accepting, unsigned int num_edges, | ||
64 | const struct REGEX_BLOCK_Edge *edges) | ||
65 | { | ||
66 | unsigned int i; | ||
67 | struct IteratorContext *ctx = cls; | ||
68 | char *out_str; | ||
69 | char *state_id = GNUNET_strdup (GNUNET_h2s (key)); | ||
70 | |||
71 | GNUNET_assert (NULL != proof); | ||
72 | if (GNUNET_YES == ctx->should_save_graph) | ||
73 | { | ||
74 | if (GNUNET_YES == accepting) | ||
75 | GNUNET_asprintf (&out_str, "\"%s\" [shape=doublecircle]\n", state_id); | ||
76 | else | ||
77 | GNUNET_asprintf (&out_str, "\"%s\" [shape=circle]\n", state_id); | ||
78 | fwrite (out_str, strlen (out_str), 1, ctx->graph_filep); | ||
79 | GNUNET_free (out_str); | ||
80 | |||
81 | for (i = 0; i < num_edges; i++) | ||
82 | { | ||
83 | transition_counter++; | ||
84 | GNUNET_asprintf (&out_str, "\"%s\" -> \"%s\" [label = \"%s (%s)\"]\n", | ||
85 | state_id, GNUNET_h2s (&edges[i].destination), | ||
86 | edges[i].label, proof); | ||
87 | fwrite (out_str, strlen (out_str), 1, ctx->graph_filep); | ||
88 | |||
89 | GNUNET_free (out_str); | ||
90 | } | ||
91 | } | ||
92 | else | ||
93 | { | ||
94 | for (i = 0; i < num_edges; i++) | ||
95 | transition_counter++; | ||
96 | } | ||
97 | |||
98 | for (i = 0; i < ctx->string_count; i++) | ||
99 | { | ||
100 | if (0 == strcmp (proof, ctx->strings[i])) | ||
101 | ctx->match_count++; | ||
102 | } | ||
103 | |||
104 | if (GNUNET_OK != REGEX_BLOCK_check_proof (proof, strlen (proof), key)) | ||
105 | { | ||
106 | ctx->error++; | ||
107 | GNUNET_log (GNUNET_ERROR_TYPE_ERROR, | ||
108 | "Proof check failed: proof: %s key: %s\n", proof, state_id); | ||
109 | } | ||
110 | GNUNET_free (state_id); | ||
111 | } | ||
112 | |||
113 | |||
114 | int | ||
115 | main (int argc, char *argv[]) | ||
116 | { | ||
117 | GNUNET_log_setup ("test-regex", "WARNING", NULL); | ||
118 | |||
119 | int error; | ||
120 | struct REGEX_INTERNAL_Automaton *dfa; | ||
121 | unsigned int i; | ||
122 | unsigned int num_transitions; | ||
123 | char *filename = NULL; | ||
124 | struct IteratorContext ctx = { 0, 0, NULL, 0, NULL, 0 }; | ||
125 | |||
126 | error = 0; | ||
127 | |||
128 | const struct RegexStringPair rxstr[13] = { | ||
129 | {INITIAL_PADDING "ab(c|d)+c*(a(b|c)+d)+(bla)+", 2, | ||
130 | {INITIAL_PADDING "abcdcdca", INITIAL_PADDING "abcabdbl"}}, | ||
131 | {INITIAL_PADDING | ||
132 | "abcdefghixxxxxxxxxxxxxjklmnop*qstoisdjfguisdfguihsdfgbdsuivggsd", 1, | ||
133 | {INITIAL_PADDING "abcdefgh"}}, | ||
134 | {INITIAL_PADDING "VPN-4-1(0|1)*", 2, | ||
135 | {INITIAL_PADDING "VPN-4-10", INITIAL_PADDING "VPN-4-11"}}, | ||
136 | {INITIAL_PADDING "(a+X*y+c|p|R|Z*K*y*R+w|Y*6+n+h*k*w+V*F|W*B*e*)", 2, | ||
137 | {INITIAL_PADDING "aaaaaaaa", INITIAL_PADDING "aaXXyyyc"}}, | ||
138 | {INITIAL_PADDING "a*", 1, {INITIAL_PADDING "aaaaaaaa"}}, | ||
139 | {INITIAL_PADDING "xzxzxzxzxz", 1, {INITIAL_PADDING "xzxzxzxz"}}, | ||
140 | {INITIAL_PADDING "xyz*", 1, {INITIAL_PADDING "xyzzzzzz"}}, | ||
141 | {INITIAL_PADDING | ||
142 | "abcd:(0|1)(0|1)(0|1)(0|1)(0|1)(0|1)(0|1)(0|1)(0|1)(0|1)(0|1)(0|1)(0|1)(0|1)(0|1)(0|1)(0|1)(0|1)(0|1)(0|1):(0|1)(0|1)(0|1)(0|1)(0|1)(0|1)(0|1)(0|1)(0|1)(0|1)(0|1)(0|1)(0|1)(0|1)(0|1)(0|1)(0|1)(0|1)(0|1)(0|1)(0|1)(0|1)(0|1)(0|1)(0|1)(0|1)(0|1)(0|1)(0|1)(0|1)(0|1)(0|1)(0|1)(0|1)(0|1)(0|1)(0|1)(0|1)(0|1)(0|1)(0|1)(0|1)(0|1)(0|1)", | ||
143 | 2, {INITIAL_PADDING "abcd:000", INITIAL_PADDING "abcd:101"}}, | ||
144 | {INITIAL_PADDING "(x*|(0|1|2)(a|b|c|d)+)", 2, | ||
145 | {INITIAL_PADDING "xxxxxxxx", INITIAL_PADDING "0abcdbad"}}, | ||
146 | {INITIAL_PADDING "(0|1)(0|1)23456789ABC", 1, {INITIAL_PADDING "11234567"}}, | ||
147 | {INITIAL_PADDING "0*123456789ABC*", 3, | ||
148 | {INITIAL_PADDING "00123456", INITIAL_PADDING "00000000", | ||
149 | INITIAL_PADDING "12345678"}}, | ||
150 | {INITIAL_PADDING "0123456789A*BC", 1, {INITIAL_PADDING "01234567"}}, | ||
151 | {"GNUNETVPN000100000IPEX6-fc5a:4e1:c2ba::1", 1, {"GNUNETVPN000100000IPEX6-"}} | ||
152 | }; | ||
153 | |||
154 | const char *graph_start_str = "digraph G {\nrankdir=LR\n"; | ||
155 | const char *graph_end_str = "\n}\n"; | ||
156 | |||
157 | for (i = 0; i < 13; i++) | ||
158 | { | ||
159 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Iterating DFA for regex %s\n", | ||
160 | rxstr[i].regex); | ||
161 | |||
162 | |||
163 | /* Create graph */ | ||
164 | if (GNUNET_YES == REGEX_INTERNAL_ITERATE_SAVE_DEBUG_GRAPH) | ||
165 | { | ||
166 | GNUNET_asprintf (&filename, "iteration_graph_%u.dot", i); | ||
167 | ctx.graph_filep = fopen (filename, "w"); | ||
168 | if (NULL == ctx.graph_filep) | ||
169 | { | ||
170 | GNUNET_log (GNUNET_ERROR_TYPE_WARNING, | ||
171 | "Could not open file %s for saving iteration graph.\n", | ||
172 | filename); | ||
173 | ctx.should_save_graph = GNUNET_NO; | ||
174 | } | ||
175 | else | ||
176 | { | ||
177 | ctx.should_save_graph = GNUNET_YES; | ||
178 | fwrite (graph_start_str, strlen (graph_start_str), 1, ctx.graph_filep); | ||
179 | } | ||
180 | GNUNET_free (filename); | ||
181 | } | ||
182 | else | ||
183 | { | ||
184 | ctx.should_save_graph = GNUNET_NO; | ||
185 | ctx.graph_filep = NULL; | ||
186 | } | ||
187 | |||
188 | /* Iterate over DFA edges */ | ||
189 | transition_counter = 0; | ||
190 | ctx.string_count = rxstr[i].string_count; | ||
191 | ctx.strings = rxstr[i].strings; | ||
192 | ctx.match_count = 0; | ||
193 | dfa = | ||
194 | REGEX_INTERNAL_construct_dfa (rxstr[i].regex, strlen (rxstr[i].regex), 0); | ||
195 | REGEX_INTERNAL_iterate_all_edges (dfa, key_iterator, &ctx); | ||
196 | num_transitions = | ||
197 | REGEX_INTERNAL_get_transition_count (dfa) - dfa->start->transition_count; | ||
198 | |||
199 | if (transition_counter < num_transitions) | ||
200 | { | ||
201 | GNUNET_log (GNUNET_ERROR_TYPE_ERROR, | ||
202 | "Automaton has %d transitions, iterated over %d transitions\n", | ||
203 | num_transitions, transition_counter); | ||
204 | error += 1; | ||
205 | } | ||
206 | |||
207 | if (ctx.match_count < ctx.string_count) | ||
208 | { | ||
209 | GNUNET_log (GNUNET_ERROR_TYPE_ERROR, | ||
210 | "Missing initial states for regex %s\n", rxstr[i].regex); | ||
211 | error += (ctx.string_count - ctx.match_count); | ||
212 | } | ||
213 | else if (ctx.match_count > ctx.string_count) | ||
214 | { | ||
215 | GNUNET_log (GNUNET_ERROR_TYPE_ERROR, | ||
216 | "Duplicate initial transitions for regex %s\n", | ||
217 | rxstr[i].regex); | ||
218 | error += (ctx.string_count - ctx.match_count); | ||
219 | } | ||
220 | |||
221 | REGEX_INTERNAL_automaton_destroy (dfa); | ||
222 | |||
223 | /* Finish graph */ | ||
224 | if (GNUNET_YES == ctx.should_save_graph) | ||
225 | { | ||
226 | fwrite (graph_end_str, strlen (graph_end_str), 1, ctx.graph_filep); | ||
227 | fclose (ctx.graph_filep); | ||
228 | ctx.graph_filep = NULL; | ||
229 | ctx.should_save_graph = GNUNET_NO; | ||
230 | } | ||
231 | } | ||
232 | |||
233 | |||
234 | for (i = 0; i < 13; i++) | ||
235 | { | ||
236 | ctx.string_count = rxstr[i].string_count; | ||
237 | ctx.strings = rxstr[i].strings; | ||
238 | ctx.match_count = 0; | ||
239 | |||
240 | dfa = | ||
241 | REGEX_INTERNAL_construct_dfa (rxstr[i].regex, strlen (rxstr[i].regex), 0); | ||
242 | REGEX_INTERNAL_dfa_add_multi_strides (NULL, dfa, 2); | ||
243 | REGEX_INTERNAL_iterate_all_edges (dfa, key_iterator, &ctx); | ||
244 | |||
245 | if (ctx.match_count < ctx.string_count) | ||
246 | { | ||
247 | GNUNET_log (GNUNET_ERROR_TYPE_ERROR, | ||
248 | "Missing initial states for regex %s\n", rxstr[i].regex); | ||
249 | error += (ctx.string_count - ctx.match_count); | ||
250 | } | ||
251 | |||
252 | REGEX_INTERNAL_automaton_destroy (dfa); | ||
253 | } | ||
254 | |||
255 | error += ctx.error; | ||
256 | |||
257 | return error; | ||
258 | } | ||
diff --git a/src/regex/test_regex_proofs.c b/src/regex/test_regex_proofs.c new file mode 100644 index 000000000..72e02fa07 --- /dev/null +++ b/src/regex/test_regex_proofs.c | |||
@@ -0,0 +1,170 @@ | |||
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 | /** | ||
19 | * @file regex/test_regex_proofs.c | ||
20 | * @brief test for regex.c | ||
21 | * @author Maximilian Szengel | ||
22 | */ | ||
23 | #include "platform.h" | ||
24 | #include "regex_internal_lib.h" | ||
25 | #include "regex_test_lib.h" | ||
26 | #include "regex_internal.h" | ||
27 | |||
28 | |||
29 | /** | ||
30 | * Test if the given regex's canonical regex is the same as this canonical | ||
31 | * regex's canonical regex. Confused? Ok, then: 1. construct a dfa A from the | ||
32 | * given 'regex' 2. get the canonical regex of dfa A 3. construct a dfa B from | ||
33 | * this canonical regex 3. compare the canonical regex of dfa A with the | ||
34 | * canonical regex of dfa B. | ||
35 | * | ||
36 | * @param regex regular expression used for this test (see above). | ||
37 | * | ||
38 | * @return 0 on success, 1 on failure | ||
39 | */ | ||
40 | static unsigned int | ||
41 | test_proof (const char *regex) | ||
42 | { | ||
43 | unsigned int error; | ||
44 | struct REGEX_INTERNAL_Automaton *dfa; | ||
45 | char *c_rx1; | ||
46 | const char *c_rx2; | ||
47 | |||
48 | dfa = REGEX_INTERNAL_construct_dfa (regex, strlen (regex), 1); | ||
49 | GNUNET_assert (NULL != dfa); | ||
50 | c_rx1 = GNUNET_strdup (REGEX_INTERNAL_get_canonical_regex (dfa)); | ||
51 | REGEX_INTERNAL_automaton_destroy (dfa); | ||
52 | dfa = REGEX_INTERNAL_construct_dfa (c_rx1, strlen (c_rx1), 1); | ||
53 | GNUNET_assert (NULL != dfa); | ||
54 | c_rx2 = REGEX_INTERNAL_get_canonical_regex (dfa); | ||
55 | |||
56 | error = (0 == strcmp (c_rx1, c_rx2)) ? 0 : 1; | ||
57 | |||
58 | if (error > 0) | ||
59 | { | ||
60 | GNUNET_log (GNUNET_ERROR_TYPE_ERROR, | ||
61 | "Comparing canonical regex of\n%s\nfailed:\n%s\nvs.\n%s\n", | ||
62 | regex, c_rx1, c_rx2); | ||
63 | } | ||
64 | |||
65 | GNUNET_free (c_rx1); | ||
66 | REGEX_INTERNAL_automaton_destroy (dfa); | ||
67 | |||
68 | return error; | ||
69 | } | ||
70 | |||
71 | |||
72 | /** | ||
73 | * Use 'test_proof' function to randomly test the canonical regexes of 'count' | ||
74 | * random expressions of length 'rx_length'. | ||
75 | * | ||
76 | * @param count number of random regular expressions to test. | ||
77 | * @param rx_length length of the random regular expressions. | ||
78 | * | ||
79 | * @return 0 on succes, number of failures otherwise. | ||
80 | */ | ||
81 | static unsigned int | ||
82 | test_proofs_random (unsigned int count, size_t rx_length) | ||
83 | { | ||
84 | unsigned int i; | ||
85 | char *rand_rx; | ||
86 | unsigned int failures; | ||
87 | |||
88 | failures = 0; | ||
89 | |||
90 | for (i = 0; i < count; i++) | ||
91 | { | ||
92 | rand_rx = REGEX_TEST_generate_random_regex (rx_length, NULL); | ||
93 | failures += test_proof (rand_rx); | ||
94 | GNUNET_free (rand_rx); | ||
95 | } | ||
96 | |||
97 | return failures; | ||
98 | } | ||
99 | |||
100 | |||
101 | /** | ||
102 | * Test a number of known examples of regexes for proper canonicalization. | ||
103 | * | ||
104 | * @return 0 on success, number of failures otherwise. | ||
105 | */ | ||
106 | static unsigned int | ||
107 | test_proofs_static () | ||
108 | { | ||
109 | unsigned int i; | ||
110 | unsigned int error; | ||
111 | |||
112 | const char *regex[8] = { | ||
113 | "a|aa*a", | ||
114 | "a+", | ||
115 | "a*", | ||
116 | "a*a*", | ||
117 | "(F*C|WfPf|y+F*C)", | ||
118 | "y*F*C|WfPf", | ||
119 | "((a|b)c|(a|b)(d|(a|b)e))", | ||
120 | "((a|b)(c|d)|(a|b)(a|b)e)" | ||
121 | }; | ||
122 | |||
123 | const char *canon_rx1; | ||
124 | const char *canon_rx2; | ||
125 | struct REGEX_INTERNAL_Automaton *dfa1; | ||
126 | struct REGEX_INTERNAL_Automaton *dfa2; | ||
127 | |||
128 | error = 0; | ||
129 | |||
130 | for (i = 0; i < 8; i += 2) | ||
131 | { | ||
132 | dfa1 = REGEX_INTERNAL_construct_dfa (regex[i], strlen (regex[i]), 1); | ||
133 | dfa2 = REGEX_INTERNAL_construct_dfa (regex[i + 1], strlen (regex[i + 1]), 1); | ||
134 | GNUNET_assert (NULL != dfa1); | ||
135 | GNUNET_assert (NULL != dfa2); | ||
136 | |||
137 | canon_rx1 = REGEX_INTERNAL_get_canonical_regex (dfa1); | ||
138 | canon_rx2 = REGEX_INTERNAL_get_canonical_regex (dfa2); | ||
139 | |||
140 | error += (0 == strcmp (canon_rx1, canon_rx2)) ? 0 : 1; | ||
141 | |||
142 | if (error > 0) | ||
143 | { | ||
144 | GNUNET_log (GNUNET_ERROR_TYPE_ERROR, | ||
145 | "Comparing canonical regex failed:\nrx1:\t%s\ncrx1:\t%s\nrx2:\t%s\ncrx2:\t%s\n", | ||
146 | regex[i], canon_rx1, regex[i + 1], canon_rx2); | ||
147 | } | ||
148 | |||
149 | REGEX_INTERNAL_automaton_destroy (dfa1); | ||
150 | REGEX_INTERNAL_automaton_destroy (dfa2); | ||
151 | } | ||
152 | |||
153 | return error; | ||
154 | } | ||
155 | |||
156 | |||
157 | int | ||
158 | main (int argc, char *argv[]) | ||
159 | { | ||
160 | GNUNET_log_setup ("test-regex", "WARNING", NULL); | ||
161 | |||
162 | int error; | ||
163 | |||
164 | error = 0; | ||
165 | |||
166 | error += test_proofs_static (); | ||
167 | error += test_proofs_random (100, 30); | ||
168 | |||
169 | return error; | ||
170 | } | ||
diff --git a/src/tun/.gitignore b/src/tun/.gitignore new file mode 100644 index 000000000..b26685596 --- /dev/null +++ b/src/tun/.gitignore | |||
@@ -0,0 +1,2 @@ | |||
1 | test_regex | ||
2 | test_tun | ||
diff --git a/src/tun/Makefile.am b/src/tun/Makefile.am new file mode 100644 index 000000000..c741f5654 --- /dev/null +++ b/src/tun/Makefile.am | |||
@@ -0,0 +1,46 @@ | |||
1 | # This Makefile.am is in the public domain | ||
2 | AM_CPPFLAGS = -I$(top_srcdir)/src/include | ||
3 | |||
4 | if MINGW | ||
5 | WINFLAGS = -Wl,--no-undefined -Wl,--export-all-symbols | ||
6 | endif | ||
7 | |||
8 | if USE_COVERAGE | ||
9 | AM_CFLAGS = --coverage -O0 | ||
10 | XLIB = -lgcov | ||
11 | endif | ||
12 | |||
13 | lib_LTLIBRARIES = libgnunettun.la | ||
14 | |||
15 | libgnunettun_la_SOURCES = \ | ||
16 | tun.c \ | ||
17 | regex.c | ||
18 | libgnunettun_la_LIBADD = \ | ||
19 | $(top_builddir)/src/util/libgnunetutil.la $(XLIB) \ | ||
20 | $(LTLIBINTL) | ||
21 | libgnunettun_la_LDFLAGS = \ | ||
22 | $(GN_LIB_LDFLAGS) \ | ||
23 | -version-info 1:0:1 | ||
24 | |||
25 | |||
26 | check_PROGRAMS = \ | ||
27 | test_tun \ | ||
28 | test_regex | ||
29 | |||
30 | if ENABLE_TEST_RUN | ||
31 | AM_TESTS_ENVIRONMENT=export GNUNET_PREFIX=$${GNUNET_PREFIX:-@libdir@};export PATH=$${GNUNET_PREFIX:-@prefix@}/bin:$$PATH;unset XDG_DATA_HOME;unset XDG_CONFIG_HOME; | ||
32 | TESTS = $(check_PROGRAMS) | ||
33 | endif | ||
34 | |||
35 | test_tun_SOURCES = \ | ||
36 | test_tun.c | ||
37 | test_tun_LDADD = \ | ||
38 | $(top_builddir)/src/util/libgnunetutil.la \ | ||
39 | libgnunettun.la | ||
40 | |||
41 | |||
42 | test_regex_SOURCES = \ | ||
43 | test_regex.c | ||
44 | test_regex_LDADD = \ | ||
45 | $(top_builddir)/src/util/libgnunetutil.la \ | ||
46 | libgnunettun.la | ||
diff --git a/src/util/regex.c b/src/tun/regex.c index 7565a9eac..7565a9eac 100644 --- a/src/util/regex.c +++ b/src/tun/regex.c | |||
diff --git a/src/util/test_regex.c b/src/tun/test_regex.c index 2e7d52828..2e7d52828 100644 --- a/src/util/test_regex.c +++ b/src/tun/test_regex.c | |||
diff --git a/src/util/test_tun.c b/src/tun/test_tun.c index edbd4c05d..edbd4c05d 100644 --- a/src/util/test_tun.c +++ b/src/tun/test_tun.c | |||
diff --git a/src/util/tun.c b/src/tun/tun.c index 7c35a76da..f85f72209 100644 --- a/src/util/tun.c +++ b/src/tun/tun.c | |||
@@ -23,7 +23,7 @@ | |||
23 | * @author Christian Grothoff | 23 | * @author Christian Grothoff |
24 | */ | 24 | */ |
25 | #include "platform.h" | 25 | #include "platform.h" |
26 | #include "gnunet_util_lib.h" | 26 | #include "gnunet_tun_lib.h" |
27 | 27 | ||
28 | /** | 28 | /** |
29 | * IP TTL we use for packets that we assemble (8 bit unsigned integer) | 29 | * IP TTL we use for packets that we assemble (8 bit unsigned integer) |
diff --git a/src/util/Makefile.am b/src/util/Makefile.am index ec7bcb016..4296199db 100644 --- a/src/util/Makefile.am +++ b/src/util/Makefile.am | |||
@@ -89,8 +89,6 @@ libgnunetutil_la_SOURCES = \ | |||
89 | crypto_rsa.c \ | 89 | crypto_rsa.c \ |
90 | disk.c \ | 90 | disk.c \ |
91 | disk.h \ | 91 | disk.h \ |
92 | dnsparser.c \ | ||
93 | dnsstub.c \ | ||
94 | getopt.c \ | 92 | getopt.c \ |
95 | getopt_helpers.c \ | 93 | getopt_helpers.c \ |
96 | helper.c \ | 94 | helper.c \ |
@@ -106,14 +104,12 @@ libgnunetutil_la_SOURCES = \ | |||
106 | peer.c \ | 104 | peer.c \ |
107 | plugin.c \ | 105 | plugin.c \ |
108 | program.c \ | 106 | program.c \ |
109 | regex.c \ | ||
110 | resolver_api.c resolver.h \ | 107 | resolver_api.c resolver.h \ |
111 | scheduler.c \ | 108 | scheduler.c \ |
112 | service.c \ | 109 | service.c \ |
113 | signal.c \ | 110 | signal.c \ |
114 | strings.c \ | 111 | strings.c \ |
115 | time.c \ | 112 | time.c \ |
116 | tun.c \ | ||
117 | speedup.c speedup.h | 113 | speedup.c speedup.h |
118 | 114 | ||
119 | libgnunetutil_la_LIBADD = \ | 115 | libgnunetutil_la_LIBADD = \ |
@@ -121,7 +117,7 @@ libgnunetutil_la_LIBADD = \ | |||
121 | $(LIBGCRYPT_LIBS) \ | 117 | $(LIBGCRYPT_LIBS) \ |
122 | $(LTLIBICONV) \ | 118 | $(LTLIBICONV) \ |
123 | $(LTLIBINTL) \ | 119 | $(LTLIBINTL) \ |
124 | -lltdl -lidn $(Z_LIBS) -lunistring $(XLIB) | 120 | -lltdl $(Z_LIBS) -lunistring $(XLIB) |
125 | 121 | ||
126 | libgnunetutil_la_LDFLAGS = \ | 122 | libgnunetutil_la_LDFLAGS = \ |
127 | $(GN_LIB_LDFLAGS) \ | 123 | $(GN_LIB_LDFLAGS) \ |
@@ -295,22 +291,19 @@ check_PROGRAMS = \ | |||
295 | test_crypto_rsa \ | 291 | test_crypto_rsa \ |
296 | test_disk \ | 292 | test_disk \ |
297 | test_getopt \ | 293 | test_getopt \ |
298 | test_hexcoder \ | ||
299 | test_mq \ | 294 | test_mq \ |
300 | test_os_network \ | 295 | test_os_network \ |
301 | test_peer \ | 296 | test_peer \ |
302 | test_plugin \ | 297 | test_plugin \ |
303 | test_program \ | 298 | test_program \ |
304 | test_regex \ | ||
305 | test_resolver_api.nc \ | 299 | test_resolver_api.nc \ |
306 | test_scheduler \ | 300 | test_scheduler \ |
307 | test_scheduler_delay \ | 301 | test_scheduler_delay \ |
308 | test_service \ | 302 | test_service \ |
309 | test_strings \ | 303 | test_strings \ |
310 | test_strings_to_data \ | 304 | test_strings_to_data \ |
311 | test_speedup \ | ||
312 | test_time \ | 305 | test_time \ |
313 | test_tun \ | 306 | test_speedup \ |
314 | $(BENCHMARKS) \ | 307 | $(BENCHMARKS) \ |
315 | test_os_start_process \ | 308 | test_os_start_process \ |
316 | test_common_logging_runtime_loglevels | 309 | test_common_logging_runtime_loglevels |
@@ -326,20 +319,6 @@ test_bio_SOURCES = \ | |||
326 | test_bio_LDADD = \ | 319 | test_bio_LDADD = \ |
327 | libgnunetutil.la | 320 | libgnunetutil.la |
328 | 321 | ||
329 | test_hexcoder_SOURCES = \ | ||
330 | test_hexcoder.c | ||
331 | test_hexcoder_LDADD = \ | ||
332 | libgnunetutil.la | ||
333 | |||
334 | test_tun_SOURCES = \ | ||
335 | test_tun.c | ||
336 | test_tun_LDADD = \ | ||
337 | libgnunetutil.la | ||
338 | |||
339 | test_regex_SOURCES = \ | ||
340 | test_regex.c | ||
341 | test_regex_LDADD = \ | ||
342 | libgnunetutil.la | ||
343 | 322 | ||
344 | test_os_start_process_SOURCES = \ | 323 | test_os_start_process_SOURCES = \ |
345 | test_os_start_process.c | 324 | test_os_start_process.c |
diff --git a/src/vpn/Makefile.am b/src/vpn/Makefile.am index d1f74d35b..5c16fa349 100644 --- a/src/vpn/Makefile.am +++ b/src/vpn/Makefile.am | |||
@@ -57,6 +57,7 @@ gnunet_service_vpn_SOURCES = \ | |||
57 | gnunet-service-vpn.c | 57 | gnunet-service-vpn.c |
58 | gnunet_service_vpn_LDADD = \ | 58 | gnunet_service_vpn_LDADD = \ |
59 | $(top_builddir)/src/statistics/libgnunetstatistics.la \ | 59 | $(top_builddir)/src/statistics/libgnunetstatistics.la \ |
60 | $(top_builddir)/src/tun/libgnunettun.la \ | ||
60 | $(top_builddir)/src/util/libgnunetutil.la \ | 61 | $(top_builddir)/src/util/libgnunetutil.la \ |
61 | $(top_builddir)/src/cadet/libgnunetcadet.la \ | 62 | $(top_builddir)/src/cadet/libgnunetcadet.la \ |
62 | $(top_builddir)/src/regex/libgnunetregex.la \ | 63 | $(top_builddir)/src/regex/libgnunetregex.la \ |
@@ -68,6 +69,7 @@ gnunet_vpn_SOURCES = \ | |||
68 | gnunet-vpn.c | 69 | gnunet-vpn.c |
69 | gnunet_vpn_LDADD = \ | 70 | gnunet_vpn_LDADD = \ |
70 | libgnunetvpn.la \ | 71 | libgnunetvpn.la \ |
72 | $(top_builddir)/src/tun/libgnunettun.la \ | ||
71 | $(top_builddir)/src/util/libgnunetutil.la \ | 73 | $(top_builddir)/src/util/libgnunetutil.la \ |
72 | $(GN_LIBINTL) | 74 | $(GN_LIBINTL) |
73 | 75 | ||