Revert "integrate dnsparser and dnsstub and tun with libgnunetutil"
authorlurchi <lurchi@strangeplace.net>
Mon, 25 Jun 2018 16:38:13 +0000 (18:38 +0200)
committerlurchi <lurchi@strangeplace.net>
Mon, 25 Jun 2018 16:38:27 +0000 (18:38 +0200)
This reverts commit 7da98cf076e9c5101244dfbbf8c3ddff045d298e.

60 files changed:
configure.ac
po/POTFILES.in
src/Makefile.am
src/dns/Makefile.am
src/dns/dnsparser.c [new file with mode: 0644]
src/dns/dnsstub.c [new file with mode: 0644]
src/dns/test_hexcoder.c [new file with mode: 0644]
src/exit/Makefile.am
src/gns/Makefile.am
src/gnsrecord/Makefile.am
src/include/gnunet_dnsstub_lib.h
src/include/gnunet_util_lib.h
src/namestore/Makefile.am
src/pt/Makefile.am
src/regex/.gitignore [new file with mode: 0644]
src/regex/Makefile.am [new file with mode: 0644]
src/regex/gnunet-daemon-regexprofiler.c [new file with mode: 0644]
src/regex/gnunet-regex-profiler.c [new file with mode: 0644]
src/regex/gnunet-regex-simulation-profiler.c [new file with mode: 0644]
src/regex/gnunet-service-regex.c [new file with mode: 0644]
src/regex/perf-data.tar.gz [new file with mode: 0644]
src/regex/perf-regex.c [new file with mode: 0644]
src/regex/plugin_block_regex.c [new file with mode: 0644]
src/regex/regex.conf.in [new file with mode: 0644]
src/regex/regex_api_announce.c [new file with mode: 0644]
src/regex/regex_api_search.c [new file with mode: 0644]
src/regex/regex_block_lib.c [new file with mode: 0644]
src/regex/regex_block_lib.h [new file with mode: 0644]
src/regex/regex_internal.c [new file with mode: 0644]
src/regex/regex_internal.h [new file with mode: 0644]
src/regex/regex_internal_dht.c [new file with mode: 0644]
src/regex/regex_internal_lib.h [new file with mode: 0644]
src/regex/regex_ipc.h [new file with mode: 0644]
src/regex/regex_simulation_profiler_test.conf [new file with mode: 0644]
src/regex/regex_test_graph.c [new file with mode: 0644]
src/regex/regex_test_lib.c [new file with mode: 0644]
src/regex/regex_test_lib.h [new file with mode: 0644]
src/regex/regex_test_random.c [new file with mode: 0644]
src/regex/test_regex_api.c [new file with mode: 0644]
src/regex/test_regex_api_data.conf [new file with mode: 0644]
src/regex/test_regex_eval_api.c [new file with mode: 0644]
src/regex/test_regex_graph_api.c [new file with mode: 0644]
src/regex/test_regex_integration.c [new file with mode: 0644]
src/regex/test_regex_iterate_api.c [new file with mode: 0644]
src/regex/test_regex_proofs.c [new file with mode: 0644]
src/tun/.gitignore [new file with mode: 0644]
src/tun/Makefile.am [new file with mode: 0644]
src/tun/regex.c [new file with mode: 0644]
src/tun/test_regex.c [new file with mode: 0644]
src/tun/test_tun.c [new file with mode: 0644]
src/tun/tun.c [new file with mode: 0644]
src/util/Makefile.am
src/util/dnsparser.c [deleted file]
src/util/dnsstub.c [deleted file]
src/util/regex.c [deleted file]
src/util/test_hexcoder.c [deleted file]
src/util/test_regex.c [deleted file]
src/util/test_tun.c [deleted file]
src/util/tun.c [deleted file]
src/vpn/Makefile.am

index 3d68ee9b0c11dbd5f10c38acf05e2dab812e109c..c8e3164169afd6b4be25adf4f88ec9334f2874ca 100644 (file)
@@ -1722,6 +1722,7 @@ src/testing/Makefile
 src/topology/Makefile
 src/transport/Makefile
 src/transport/transport.conf
+src/tun/Makefile
 src/util/Makefile
 src/util/resolver.conf
 src/vpn/Makefile
index 38fa52508c29c48660f188a2e59b256485dd3d3e..de6bd90e456c34aa191b706ef441d56bc596df9b 100644 (file)
@@ -4,13 +4,21 @@ src/arm/arm_monitor_api.c
 src/arm/gnunet-arm.c
 src/arm/gnunet-service-arm.c
 src/arm/mockup-service.c
+src/ats-tests/ats-testing-experiment.c
+src/ats-tests/ats-testing-log.c
+src/ats-tests/ats-testing-preferences.c
+src/ats-tests/ats-testing-traffic.c
+src/ats-tests/ats-testing.c
+src/ats-tests/gnunet-ats-sim.c
+src/ats-tests/gnunet-solver-eval.c
+src/ats-tool/gnunet-ats.c
 src/ats/ats_api_connectivity.c
 src/ats/ats_api_performance.c
 src/ats/ats_api_scanner.c
 src/ats/ats_api_scheduling.c
 src/ats/gnunet-ats-solver-eval.c
-src/ats/gnunet-service-ats_addresses.c
 src/ats/gnunet-service-ats.c
+src/ats/gnunet-service-ats_addresses.c
 src/ats/gnunet-service-ats_connectivity.c
 src/ats/gnunet-service-ats_normalization.c
 src/ats/gnunet-service-ats_performance.c
@@ -21,14 +29,6 @@ src/ats/gnunet-service-ats_scheduling.c
 src/ats/plugin_ats_mlp.c
 src/ats/plugin_ats_proportional.c
 src/ats/plugin_ats_ril.c
-src/ats-tests/ats-testing.c
-src/ats-tests/ats-testing-experiment.c
-src/ats-tests/ats-testing-log.c
-src/ats-tests/ats-testing-preferences.c
-src/ats-tests/ats-testing-traffic.c
-src/ats-tests/gnunet-ats-sim.c
-src/ats-tests/gnunet-solver-eval.c
-src/ats-tool/gnunet-ats.c
 src/auction/gnunet-auction-create.c
 src/auction/gnunet-auction-info.c
 src/auction/gnunet-auction-join.c
@@ -40,8 +40,8 @@ src/block/plugin_block_test.c
 src/cadet/cadet_api.c
 src/cadet/cadet_test_lib.c
 src/cadet/desirability_table.c
-src/cadet/gnunet-cadet.c
 src/cadet/gnunet-cadet-profiler.c
+src/cadet/gnunet-cadet.c
 src/cadet/gnunet-service-cadet.c
 src/cadet/gnunet-service-cadet_channel.c
 src/cadet/gnunet-service-cadet_connection.c
@@ -57,15 +57,15 @@ src/consensus/gnunet-service-consensus.c
 src/consensus/plugin_block_consensus.c
 src/conversation/conversation_api.c
 src/conversation/conversation_api_call.c
-src/conversation/gnunet-conversation.c
 src/conversation/gnunet-conversation-test.c
-src/conversation/gnunet_gst.c
-src/conversation/gnunet_gst_test.c
-src/conversation/gnunet-helper-audio-playback.c
+src/conversation/gnunet-conversation.c
 src/conversation/gnunet-helper-audio-playback-gst.c
-src/conversation/gnunet-helper-audio-record.c
+src/conversation/gnunet-helper-audio-playback.c
 src/conversation/gnunet-helper-audio-record-gst.c
+src/conversation/gnunet-helper-audio-record.c
 src/conversation/gnunet-service-conversation.c
+src/conversation/gnunet_gst.c
+src/conversation/gnunet_gst_test.c
 src/conversation/microphone.c
 src/conversation/plugin_gnsrecord_conversation.c
 src/conversation/speaker.c
@@ -102,7 +102,6 @@ src/dht/dht_api.c
 src/dht/dht_test_lib.c
 src/dht/gnunet-dht-get.c
 src/dht/gnunet-dht-monitor.c
-src/dht/gnunet_dht_profiler.c
 src/dht/gnunet-dht-put.c
 src/dht/gnunet-service-dht.c
 src/dht/gnunet-service-dht_clients.c
@@ -111,8 +110,11 @@ src/dht/gnunet-service-dht_hello.c
 src/dht/gnunet-service-dht_neighbours.c
 src/dht/gnunet-service-dht_nse.c
 src/dht/gnunet-service-dht_routing.c
+src/dht/gnunet_dht_profiler.c
 src/dht/plugin_block_dht.c
 src/dns/dns_api.c
+src/dns/dnsparser.c
+src/dns/dnsstub.c
 src/dns/gnunet-dns-monitor.c
 src/dns/gnunet-dns-redirector.c
 src/dns/gnunet-helper-dns.c
@@ -124,8 +126,8 @@ src/dv/gnunet-dv.c
 src/dv/gnunet-service-dv.c
 src/dv/plugin_transport_dv.c
 src/exit/gnunet-daemon-exit.c
-src/exit/gnunet-helper-exit.c
 src/exit/gnunet-helper-exit-windows.c
+src/exit/gnunet-helper-exit.c
 src/fragmentation/defragmentation.c
 src/fragmentation/fragmentation.c
 src/fs/fs_api.c
@@ -150,8 +152,8 @@ src/fs/gnunet-auto-share.c
 src/fs/gnunet-daemon-fsprofiler.c
 src/fs/gnunet-directory.c
 src/fs/gnunet-download.c
-src/fs/gnunet-fs.c
 src/fs/gnunet-fs-profiler.c
+src/fs/gnunet-fs.c
 src/fs/gnunet-helper-fs-publish.c
 src/fs/gnunet-publish.c
 src/fs/gnunet-search.c
@@ -171,10 +173,10 @@ src/gns/gns_tld_api.c
 src/gns/gnunet-bcd.c
 src/gns/gnunet-dns2gns.c
 src/gns/gnunet-gns-benchmark.c
-src/gns/gnunet-gns.c
 src/gns/gnunet-gns-helper-service-w32.c
 src/gns/gnunet-gns-import.c
 src/gns/gnunet-gns-proxy.c
+src/gns/gnunet-gns.c
 src/gns/gnunet-service-gns.c
 src/gns/gnunet-service-gns_interceptor.c
 src/gns/gnunet-service-gns_resolver.c
@@ -183,15 +185,15 @@ src/gns/nss/nss_gns_query.c
 src/gns/plugin_block_gns.c
 src/gns/plugin_gnsrecord_gns.c
 src/gns/plugin_rest_gns.c
+src/gns/w32nsp-install.c
+src/gns/w32nsp-resolve.c
+src/gns/w32nsp-uninstall.c
+src/gns/w32nsp.c
 src/gnsrecord/gnsrecord.c
 src/gnsrecord/gnsrecord_crypto.c
 src/gnsrecord/gnsrecord_misc.c
 src/gnsrecord/gnsrecord_serialization.c
 src/gnsrecord/plugin_gnsrecord_dns.c
-src/gns/w32nsp.c
-src/gns/w32nsp-install.c
-src/gns/w32nsp-resolve.c
-src/gns/w32nsp-uninstall.c
 src/hello/address.c
 src/hello/gnunet-hello.c
 src/hello/hello.c
@@ -200,11 +202,6 @@ src/hostlist/gnunet-daemon-hostlist_client.c
 src/hostlist/gnunet-daemon-hostlist_server.c
 src/identity-attribute/identity_attribute.c
 src/identity-attribute/plugin_identity_attribute_gnuid.c
-src/identity/gnunet-identity.c
-src/identity/gnunet-service-identity.c
-src/identity/identity_api.c
-src/identity/identity_api_lookup.c
-src/identity/plugin_rest_identity.c
 src/identity-provider/gnunet-idp.c
 src/identity-provider/gnunet-service-identity-provider.c
 src/identity-provider/identity_provider_api.c
@@ -213,15 +210,20 @@ src/identity-provider/plugin_gnsrecord_identity_provider.c
 src/identity-provider/plugin_identity_provider_sqlite.c
 src/identity-provider/plugin_rest_identity_provider.c
 src/identity-provider/plugin_rest_openid_connect.c
+src/identity/gnunet-identity.c
+src/identity/gnunet-service-identity.c
+src/identity/identity_api.c
+src/identity/identity_api_lookup.c
+src/identity/plugin_rest_identity.c
+src/json/json.c
+src/json/json_generator.c
+src/json/json_helper.c
+src/json/json_mhd.c
 src/jsonapi/jsonapi.c
 src/jsonapi/jsonapi_document.c
 src/jsonapi/jsonapi_error.c
 src/jsonapi/jsonapi_relationship.c
 src/jsonapi/jsonapi_resource.c
-src/json/json.c
-src/json/json_generator.c
-src/json/json_helper.c
-src/json/json_mhd.c
 src/multicast/gnunet-multicast.c
 src/multicast/gnunet-service-multicast.c
 src/multicast/multicast_api.c
@@ -235,8 +237,8 @@ src/namecache/namecache_api.c
 src/namecache/plugin_namecache_flat.c
 src/namecache/plugin_namecache_postgres.c
 src/namecache/plugin_namecache_sqlite.c
-src/namestore/gnunet-namestore.c
 src/namestore/gnunet-namestore-fcfsd.c
+src/namestore/gnunet-namestore.c
 src/namestore/gnunet-service-namestore.c
 src/namestore/gnunet-zoneimport.c
 src/namestore/namestore_api.c
@@ -252,10 +254,10 @@ src/nat-auto/gnunet-service-nat-auto.c
 src/nat-auto/gnunet-service-nat-auto_legacy.c
 src/nat-auto/nat_auto_api.c
 src/nat-auto/nat_auto_api_test.c
-src/nat/gnunet-helper-nat-client.c
 src/nat/gnunet-helper-nat-client-windows.c
-src/nat/gnunet-helper-nat-server.c
+src/nat/gnunet-helper-nat-client.c
 src/nat/gnunet-helper-nat-server-windows.c
+src/nat/gnunet-helper-nat-server.c
 src/nat/gnunet-nat.c
 src/nat/gnunet-service-nat.c
 src/nat/gnunet-service-nat_externalip.c
@@ -264,15 +266,15 @@ src/nat/gnunet-service-nat_mini.c
 src/nat/gnunet-service-nat_stun.c
 src/nat/nat_api.c
 src/nat/nat_api_stun.c
-src/nse/gnunet-nse.c
 src/nse/gnunet-nse-profiler.c
+src/nse/gnunet-nse.c
 src/nse/gnunet-service-nse.c
 src/nse/nse_api.c
+src/peerinfo-tool/gnunet-peerinfo.c
+src/peerinfo-tool/gnunet-peerinfo_plugins.c
 src/peerinfo/gnunet-service-peerinfo.c
 src/peerinfo/peerinfo_api.c
 src/peerinfo/peerinfo_api_notify.c
-src/peerinfo-tool/gnunet-peerinfo.c
-src/peerinfo-tool/gnunet-peerinfo_plugins.c
 src/peerstore/gnunet-peerstore.c
 src/peerstore/gnunet-service-peerstore.c
 src/peerstore/peerstore_api.c
@@ -317,20 +319,20 @@ src/revocation/gnunet-revocation.c
 src/revocation/gnunet-service-revocation.c
 src/revocation/plugin_block_revocation.c
 src/revocation/revocation_api.c
-src/rps/gnunet-rps.c
 src/rps/gnunet-rps-profiler.c
+src/rps/gnunet-rps.c
 src/rps/gnunet-service-rps.c
 src/rps/gnunet-service-rps_custommap.c
 src/rps/gnunet-service-rps_sampler.c
 src/rps/gnunet-service-rps_sampler_elem.c
 src/rps/gnunet-service-rps_view.c
-src/rps/rps_api.c
 src/rps/rps-test_util.c
+src/rps/rps_api.c
 src/scalarproduct/gnunet-scalarproduct.c
-src/scalarproduct/gnunet-service-scalarproduct_alice.c
-src/scalarproduct/gnunet-service-scalarproduct_bob.c
 src/scalarproduct/gnunet-service-scalarproduct-ecc_alice.c
 src/scalarproduct/gnunet-service-scalarproduct-ecc_bob.c
+src/scalarproduct/gnunet-service-scalarproduct_alice.c
+src/scalarproduct/gnunet-service-scalarproduct_bob.c
 src/scalarproduct/scalarproduct_api.c
 src/secretsharing/gnunet-secretsharing-profiler.c
 src/secretsharing/gnunet-service-secretsharing.c
@@ -359,15 +361,16 @@ src/statistics/gnunet-statistics.c
 src/statistics/statistics_api.c
 src/template/gnunet-service-template.c
 src/template/gnunet-template.c
+src/testbed-logger/gnunet-service-testbed-logger.c
+src/testbed-logger/testbed_logger_api.c
 src/testbed/generate-underlay-topology.c
 src/testbed/gnunet-daemon-latency-logger.c
 src/testbed/gnunet-daemon-testbed-blacklist.c
 src/testbed/gnunet-daemon-testbed-underlay.c
 src/testbed/gnunet-helper-testbed.c
-src/testbed/gnunet_mpi_test.c
 src/testbed/gnunet-service-test-barriers.c
-src/testbed/gnunet-service-testbed_barriers.c
 src/testbed/gnunet-service-testbed.c
+src/testbed/gnunet-service-testbed_barriers.c
 src/testbed/gnunet-service-testbed_cache.c
 src/testbed/gnunet-service-testbed_connectionpool.c
 src/testbed/gnunet-service-testbed_cpustatus.c
@@ -375,20 +378,19 @@ src/testbed/gnunet-service-testbed_links.c
 src/testbed/gnunet-service-testbed_meminfo.c
 src/testbed/gnunet-service-testbed_oc.c
 src/testbed/gnunet-service-testbed_peers.c
-src/testbed/gnunet_testbed_mpi_spawn.c
 src/testbed/gnunet-testbed-profiler.c
-src/testbed-logger/gnunet-service-testbed-logger.c
-src/testbed-logger/testbed_logger_api.c
-src/testbed/testbed_api_barriers.c
+src/testbed/gnunet_mpi_test.c
+src/testbed/gnunet_testbed_mpi_spawn.c
 src/testbed/testbed_api.c
+src/testbed/testbed_api_barriers.c
 src/testbed/testbed_api_hosts.c
 src/testbed/testbed_api_operations.c
 src/testbed/testbed_api_peers.c
 src/testbed/testbed_api_sd.c
 src/testbed/testbed_api_services.c
 src/testbed/testbed_api_statistics.c
-src/testbed/testbed_api_testbed.c
 src/testbed/testbed_api_test.c
+src/testbed/testbed_api_testbed.c
 src/testbed/testbed_api_topology.c
 src/testbed/testbed_api_underlay.c
 src/testing/gnunet-testing.c
@@ -397,28 +399,28 @@ src/testing/testing.c
 src/topology/friends.c
 src/topology/gnunet-daemon-topology.c
 src/transport/gnunet-helper-transport-bluetooth.c
-src/transport/gnunet-helper-transport-wlan.c
 src/transport/gnunet-helper-transport-wlan-dummy.c
-src/transport/gnunet-service-transport_ats.c
+src/transport/gnunet-helper-transport-wlan.c
 src/transport/gnunet-service-transport.c
+src/transport/gnunet-service-transport_ats.c
 src/transport/gnunet-service-transport_hello.c
 src/transport/gnunet-service-transport_manipulation.c
 src/transport/gnunet-service-transport_neighbours.c
 src/transport/gnunet-service-transport_plugins.c
 src/transport/gnunet-service-transport_validation.c
-src/transport/gnunet-transport.c
 src/transport/gnunet-transport-certificate-creation.c
 src/transport/gnunet-transport-profiler.c
 src/transport/gnunet-transport-wlan-receiver.c
 src/transport/gnunet-transport-wlan-sender.c
+src/transport/gnunet-transport.c
 src/transport/plugin_transport_http_client.c
 src/transport/plugin_transport_http_common.c
 src/transport/plugin_transport_http_server.c
 src/transport/plugin_transport_smtp.c
 src/transport/plugin_transport_tcp.c
 src/transport/plugin_transport_template.c
-src/transport/plugin_transport_udp_broadcasting.c
 src/transport/plugin_transport_udp.c
+src/transport/plugin_transport_udp_broadcasting.c
 src/transport/plugin_transport_unix.c
 src/transport/plugin_transport_wlan.c
 src/transport/plugin_transport_xt.c
@@ -427,6 +429,11 @@ src/transport/tcp_connection_legacy.c
 src/transport/tcp_server_legacy.c
 src/transport/tcp_server_mst_legacy.c
 src/transport/tcp_service_legacy.c
+src/transport/transport-testing-filenames.c
+src/transport/transport-testing-loggers.c
+src/transport/transport-testing-main.c
+src/transport/transport-testing-send.c
+src/transport/transport-testing.c
 src/transport/transport_api_address_to_string.c
 src/transport/transport_api_blacklist.c
 src/transport/transport_api_core.c
@@ -435,11 +442,8 @@ src/transport/transport_api_manipulation.c
 src/transport/transport_api_monitor_peers.c
 src/transport/transport_api_monitor_plugins.c
 src/transport/transport_api_offer_hello.c
-src/transport/transport-testing.c
-src/transport/transport-testing-filenames.c
-src/transport/transport-testing-loggers.c
-src/transport/transport-testing-main.c
-src/transport/transport-testing-send.c
+src/tun/regex.c
+src/tun/tun.c
 src/util/bandwidth.c
 src/util/bio.c
 src/util/client.c
@@ -451,8 +455,8 @@ src/util/configuration_loader.c
 src/util/container_bloomfilter.c
 src/util/container_heap.c
 src/util/container_meta_data.c
-src/util/container_multihashmap32.c
 src/util/container_multihashmap.c
+src/util/container_multihashmap32.c
 src/util/container_multipeermap.c
 src/util/container_multishortmap.c
 src/util/crypto_abe.c
@@ -470,12 +474,10 @@ src/util/crypto_random.c
 src/util/crypto_rsa.c
 src/util/crypto_symmetric.c
 src/util/disk.c
-src/util/dnsparser.c
-src/util/dnsstub.c
 src/util/getopt.c
 src/util/getopt_helpers.c
-src/util/gnunet-config.c
 src/util/gnunet-config-diff.c
+src/util/gnunet-config.c
 src/util/gnunet-ecc.c
 src/util/gnunet-helper-w32-console.c
 src/util/gnunet-resolver.c
@@ -495,7 +497,6 @@ src/util/os_priority.c
 src/util/peer.c
 src/util/plugin.c
 src/util/program.c
-src/util/regex.c
 src/util/resolver_api.c
 src/util/scheduler.c
 src/util/service.c
@@ -504,17 +505,16 @@ src/util/socks.c
 src/util/speedup.c
 src/util/strings.c
 src/util/time.c
-src/util/tun.c
 src/util/w32cat.c
 src/util/win.c
 src/util/winproc.c
-src/vpn/gnunet-helper-vpn.c
 src/vpn/gnunet-helper-vpn-windows.c
+src/vpn/gnunet-helper-vpn.c
 src/vpn/gnunet-service-vpn.c
 src/vpn/gnunet-vpn.c
 src/vpn/vpn_api.c
-src/zonemaster/gnunet-service-zonemaster.c
 src/zonemaster/gnunet-service-zonemaster-monitor.c
+src/zonemaster/gnunet-service-zonemaster.c
 src/fs/fs_api.h
 src/include/gnunet_common.h
 src/include/gnunet_mq_lib.h
index 00f30adc3b838bbc5090f4949f725adc60720a95..d8d5487066c4ab969753104ea86e183e2e1a35dc 100644 (file)
@@ -83,6 +83,7 @@ SUBDIRS = \
   $(REST_DIR) \
   $(JSONAPI_DIR) \
   hello \
+  tun \
   block \
   statistics \
   arm \
index ca2685765e57467039304abf9beb5bff17862f12..9a4ecdcfd9eeb3c0fb33d5ddafbcfc2f8b6267a5 100644 (file)
@@ -27,6 +27,8 @@ install-exec-hook:
 endif
 
 lib_LTLIBRARIES = \
+  libgnunetdnsparser.la \
+  libgnunetdnsstub.la \
   libgnunetdns.la
 
 libexec_PROGRAMS = \
@@ -45,6 +47,9 @@ check_SCRIPTS = \
  test_gnunet_dns.sh
 endif
 
+check_PROGRAMS = \
+ test_hexcoder
+
 gnunet_helper_dns_SOURCES = \
  gnunet-helper-dns.c
 
@@ -52,6 +57,7 @@ gnunet_helper_dns_SOURCES = \
 gnunet_dns_monitor_SOURCES = \
  gnunet-dns-monitor.c
 gnunet_dns_monitor_LDADD = \
+  libgnunetdnsparser.la \
   libgnunetdns.la \
   $(top_builddir)/src/util/libgnunetutil.la \
   $(GN_LIBINTL)
@@ -59,12 +65,15 @@ gnunet_dns_monitor_LDADD = \
 gnunet_zonewalk_SOURCES = \
  gnunet-zonewalk.c
 gnunet_zonewalk_LDADD = \
+  libgnunetdnsparser.la \
+  libgnunetdnsstub.la \
   $(top_builddir)/src/util/libgnunetutil.la \
   $(GN_LIBINTL)
 
 gnunet_dns_redirector_SOURCES = \
  gnunet-dns-redirector.c
 gnunet_dns_redirector_LDADD = \
+  libgnunetdnsparser.la \
   libgnunetdns.la \
   $(top_builddir)/src/util/libgnunetutil.la \
   $(GN_LIBINTL)
@@ -72,10 +81,30 @@ gnunet_dns_redirector_LDADD = \
 gnunet_service_dns_SOURCES = \
  gnunet-service-dns.c
 gnunet_service_dns_LDADD = \
+  libgnunetdnsstub.la \
+  $(top_builddir)/src/tun/libgnunettun.la \
   $(top_builddir)/src/statistics/libgnunetstatistics.la \
   $(top_builddir)/src/util/libgnunetutil.la \
   $(GN_LIBINTL)
 
+libgnunetdnsparser_la_SOURCES = \
+ dnsparser.c
+libgnunetdnsparser_la_LIBADD = \
+ $(top_builddir)/src/util/libgnunetutil.la $(XLIB) \
+  -lidn
+libgnunetdnsparser_la_LDFLAGS = \
+  $(GN_LIB_LDFLAGS) \
+  -version-info 1:0:1
+
+libgnunetdnsstub_la_SOURCES = \
+ dnsstub.c
+libgnunetdnsstub_la_LIBADD = \
+  $(top_builddir)/src/tun/libgnunettun.la \
+ $(top_builddir)/src/util/libgnunetutil.la $(XLIB)
+libgnunetdnsstub_la_LDFLAGS = \
+  $(GN_LIB_LDFLAGS) \
+  -version-info 0:0:0
+
 libgnunetdns_la_SOURCES = \
  dns_api.c dns.h
 libgnunetdns_la_LIBADD = \
@@ -102,3 +131,8 @@ EXTRA_DIST = \
   $(check_SCRIPTS)
 
 
+test_hexcoder_SOURCES = \
+ test_hexcoder.c
+test_hexcoder_LDADD = \
+ libgnunetdnsparser.la \
+ $(top_builddir)/src/util/libgnunetutil.la
diff --git a/src/dns/dnsparser.c b/src/dns/dnsparser.c
new file mode 100644 (file)
index 0000000..32ad7c0
--- /dev/null
@@ -0,0 +1,1334 @@
+/*
+      This file is part of GNUnet
+      Copyright (C) 2010-2014 GNUnet e.V.
+
+      GNUnet is free software: you can redistribute it and/or modify it
+      under the terms of the GNU Affero General Public License as published
+      by the Free Software Foundation, either version 3 of the License,
+      or (at your option) any later version.
+
+      GNUnet is distributed in the hope that it will be useful, but
+      WITHOUT ANY WARRANTY; without even the implied warranty of
+      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+      Affero General Public License for more details.
+     
+      You should have received a copy of the GNU Affero General Public License
+      along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+/**
+ * @file dns/dnsparser.c
+ * @brief helper library to parse DNS packets.
+ * @author Philipp Toelke
+ * @author Christian Grothoff
+ */
+#include "platform.h"
+#include <idna.h>
+#if WINDOWS
+#include <idn-free.h>
+#endif
+#include "gnunet_util_lib.h"
+#include "gnunet_dnsparser_lib.h"
+#include "gnunet_tun_lib.h"
+
+
+/**
+ * Check if a label in UTF-8 format can be coded into valid IDNA.
+ * This can fail if the ASCII-conversion becomes longer than 63 characters.
+ *
+ * @param label label to check (UTF-8 string)
+ * @return #GNUNET_OK if the label can be converted to IDNA,
+ *         #GNUNET_SYSERR if the label is not valid for DNS names
+ */
+int
+GNUNET_DNSPARSER_check_label (const char *label)
+{
+  char *output;
+  size_t slen;
+
+  if (NULL != strchr (label, '.'))
+    return GNUNET_SYSERR; /* not a label! Did you mean GNUNET_DNSPARSER_check_name? */
+  if (IDNA_SUCCESS !=
+      idna_to_ascii_8z (label, &output, IDNA_ALLOW_UNASSIGNED))
+    return GNUNET_SYSERR;
+  slen = strlen (output);
+#if WINDOWS
+  idn_free (output);
+#else
+  free (output);
+#endif
+  return (slen > 63) ? GNUNET_SYSERR : GNUNET_OK;
+}
+
+
+/**
+ * Check if a label in UTF-8 format can be coded into valid IDNA.
+ * This can fail if the ASCII-conversion becomes longer than 253 characters.
+ *
+ * @param name name to check (UTF-8 string)
+ * @return #GNUNET_OK if the label can be converted to IDNA,
+ *         #GNUNET_SYSERR if the label is not valid for DNS names
+ */
+int
+GNUNET_DNSPARSER_check_name (const char *name)
+{
+  char *ldup;
+  char *output;
+  size_t slen;
+  char *tok;
+
+  ldup = GNUNET_strdup (name);
+  for (tok = strtok (ldup, "."); NULL != tok; tok = strtok (NULL, "."))
+    if (GNUNET_OK !=
+       GNUNET_DNSPARSER_check_label (tok))
+    {
+      GNUNET_free (ldup);
+      return GNUNET_SYSERR;
+    }
+  GNUNET_free (ldup);
+  if (IDNA_SUCCESS !=
+      idna_to_ascii_8z (name, &output, IDNA_ALLOW_UNASSIGNED))
+    return GNUNET_SYSERR;
+  slen = strlen (output);
+#if WINDOWS
+  idn_free (output);
+#else
+  free (output);
+#endif
+  return (slen > 253) ? GNUNET_SYSERR : GNUNET_OK;
+}
+
+
+/**
+ * Free SOA information record.
+ *
+ * @param soa record to free
+ */
+void
+GNUNET_DNSPARSER_free_soa (struct GNUNET_DNSPARSER_SoaRecord *soa)
+{
+  if (NULL == soa)
+    return;
+  GNUNET_free_non_null (soa->mname);
+  GNUNET_free_non_null (soa->rname);
+  GNUNET_free (soa);
+}
+
+
+/**
+ * Free CERT information record.
+ *
+ * @param cert record to free
+ */
+void
+GNUNET_DNSPARSER_free_cert (struct GNUNET_DNSPARSER_CertRecord *cert)
+{
+  if (NULL == cert)
+    return;
+  GNUNET_free_non_null (cert->certificate_data);
+  GNUNET_free (cert);
+}
+
+
+/**
+ * Free SRV information record.
+ *
+ * @param srv record to free
+ */
+void
+GNUNET_DNSPARSER_free_srv (struct GNUNET_DNSPARSER_SrvRecord *srv)
+{
+  if (NULL == srv)
+    return;
+  GNUNET_free_non_null (srv->target);
+  GNUNET_free (srv);
+}
+
+
+/**
+ * Free MX information record.
+ *
+ * @param mx record to free
+ */
+void
+GNUNET_DNSPARSER_free_mx (struct GNUNET_DNSPARSER_MxRecord *mx)
+{
+  if (NULL == mx)
+    return;
+  GNUNET_free_non_null (mx->mxhost);
+  GNUNET_free (mx);
+}
+
+
+/**
+ * Free the given DNS record.
+ *
+ * @param r record to free
+ */
+void
+GNUNET_DNSPARSER_free_record (struct GNUNET_DNSPARSER_Record *r)
+{
+  GNUNET_free_non_null (r->name);
+  switch (r->type)
+  {
+  case GNUNET_DNSPARSER_TYPE_MX:
+    GNUNET_DNSPARSER_free_mx (r->data.mx);
+    break;
+  case GNUNET_DNSPARSER_TYPE_SOA:
+    GNUNET_DNSPARSER_free_soa (r->data.soa);
+    break;
+  case GNUNET_DNSPARSER_TYPE_SRV:
+    GNUNET_DNSPARSER_free_srv (r->data.srv);
+    break;
+  case GNUNET_DNSPARSER_TYPE_CERT:
+    GNUNET_DNSPARSER_free_cert (r->data.cert);
+    break;
+  case GNUNET_DNSPARSER_TYPE_NS:
+  case GNUNET_DNSPARSER_TYPE_CNAME:
+  case GNUNET_DNSPARSER_TYPE_PTR:
+    GNUNET_free_non_null (r->data.hostname);
+    break;
+  default:
+    GNUNET_free_non_null (r->data.raw.data);
+    break;
+  }
+}
+
+
+/**
+ * Parse name inside of a DNS query or record.
+ *
+ * @param udp_payload entire UDP payload
+ * @param udp_payload_length length of @a udp_payload
+ * @param off pointer to the offset of the name to parse in the udp_payload (to be
+ *                    incremented by the size of the name)
+ * @param depth current depth of our recursion (to prevent stack overflow)
+ * @return name as 0-terminated C string on success, NULL if the payload is malformed
+ */
+static char *
+parse_name (const char *udp_payload,
+           size_t udp_payload_length,
+           size_t *off,
+           unsigned int depth)
+{
+  const uint8_t *input = (const uint8_t *) udp_payload;
+  char *ret;
+  char *tmp;
+  char *xstr;
+  uint8_t len;
+  size_t xoff;
+  char *utf8;
+  Idna_rc rc;
+
+  ret = GNUNET_strdup ("");
+  while (1)
+  {
+    if (*off >= udp_payload_length)
+    {
+      GNUNET_break_op (0);
+      goto error;
+    }
+    len = input[*off];
+    if (0 == len)
+    {
+      (*off)++;
+      break;
+    }
+    if (len < 64)
+    {
+      if (*off + 1 + len > udp_payload_length)
+      {
+       GNUNET_break_op (0);
+       goto error;
+      }
+      GNUNET_asprintf (&tmp,
+                      "%.*s",
+                      (int) len,
+                      &udp_payload[*off + 1]);
+      if (IDNA_SUCCESS !=
+         (rc = idna_to_unicode_8z8z (tmp, &utf8, IDNA_ALLOW_UNASSIGNED)))
+      {
+       GNUNET_log (GNUNET_ERROR_TYPE_INFO,
+                   _("Failed to convert DNS IDNA name `%s' to UTF-8: %s\n"),
+                   tmp,
+                   idna_strerror (rc));
+       GNUNET_free (tmp);
+       GNUNET_asprintf (&tmp,
+                        "%s%.*s.",
+                        ret,
+                        (int) len,
+                        &udp_payload[*off + 1]);
+      }
+      else
+      {
+       GNUNET_free (tmp);
+       GNUNET_asprintf (&tmp,
+                        "%s%s.",
+                        ret,
+                        utf8);
+#if WINDOWS
+       idn_free (utf8);
+#else
+       free (utf8);
+#endif
+      }
+      GNUNET_free (ret);
+      ret = tmp;
+      *off += 1 + len;
+    }
+    else if ((64 | 128) == (len & (64 | 128)) )
+    {
+      if (depth > 32)
+      {
+       GNUNET_break_op (0);
+       goto error; /* hard bound on stack to prevent "infinite" recursion, disallow! */
+      }
+      /* pointer to string */
+      if (*off + 1 > udp_payload_length)
+      {
+       GNUNET_break_op (0);
+       goto error;
+      }
+      xoff = ((len - (64 | 128)) << 8) + input[*off+1];
+      xstr = parse_name (udp_payload,
+                        udp_payload_length,
+                        &xoff,
+                        depth + 1);
+      if (NULL == xstr)
+      {
+       GNUNET_break_op (0);
+       goto error;
+      }
+      GNUNET_asprintf (&tmp,
+                      "%s%s.",
+                      ret,
+                      xstr);
+      GNUNET_free (ret);
+      GNUNET_free (xstr);
+      ret = tmp;
+      if (strlen (ret) > udp_payload_length)
+      {
+       GNUNET_break_op (0);
+       goto error; /* we are looping (building an infinite string) */
+      }
+      *off += 2;
+      /* pointers always terminate names */
+      break;
+    }
+    else
+    {
+      /* neither pointer nor inline string, not supported... */
+      GNUNET_break_op (0);
+      goto error;
+    }
+  }
+  if (0 < strlen(ret))
+    ret[strlen(ret)-1] = '\0'; /* eat tailing '.' */
+  return ret;
+ error:
+  GNUNET_break_op (0);
+  GNUNET_free (ret);
+  return NULL;
+}
+
+
+/**
+ * Parse name inside of a DNS query or record.
+ *
+ * @param udp_payload entire UDP payload
+ * @param udp_payload_length length of @a udp_payload
+ * @param off pointer to the offset of the name to parse in the udp_payload (to be
+ *                    incremented by the size of the name)
+ * @return name as 0-terminated C string on success, NULL if the payload is malformed
+ */
+char *
+GNUNET_DNSPARSER_parse_name (const char *udp_payload,
+                            size_t udp_payload_length,
+                            size_t *off)
+{
+  return parse_name (udp_payload, udp_payload_length, off, 0);
+}
+
+
+/**
+ * Parse a DNS query entry.
+ *
+ * @param udp_payload entire UDP payload
+ * @param udp_payload_length length of @a udp_payload
+ * @param off pointer to the offset of the query to parse in the udp_payload (to be
+ *                    incremented by the size of the query)
+ * @param q where to write the query information
+ * @return #GNUNET_OK on success, #GNUNET_SYSERR if the query is malformed
+ */
+int
+GNUNET_DNSPARSER_parse_query (const char *udp_payload,
+                             size_t udp_payload_length,
+                             size_t *off,
+                             struct GNUNET_DNSPARSER_Query *q)
+{
+  char *name;
+  struct GNUNET_TUN_DnsQueryLine ql;
+
+  name = GNUNET_DNSPARSER_parse_name (udp_payload,
+                                     udp_payload_length,
+                                     off);
+  if (NULL == name)
+  {
+    GNUNET_break_op (0);
+    return GNUNET_SYSERR;
+  }
+  q->name = name;
+  if (*off + sizeof (struct GNUNET_TUN_DnsQueryLine) > udp_payload_length)
+  {
+    GNUNET_break_op (0);
+    return GNUNET_SYSERR;
+  }
+  GNUNET_memcpy (&ql, &udp_payload[*off], sizeof (ql));
+  *off += sizeof (ql);
+  q->type = ntohs (ql.type);
+  q->dns_traffic_class = ntohs (ql.dns_traffic_class);
+  return GNUNET_OK;
+}
+
+
+/**
+ * Parse a DNS SOA record.
+ *
+ * @param udp_payload reference to UDP packet
+ * @param udp_payload_length length of @a udp_payload
+ * @param off pointer to the offset of the query to parse in the SOA record (to be
+ *                    incremented by the size of the record), unchanged on error
+ * @return the parsed SOA record, NULL on error
+ */
+struct GNUNET_DNSPARSER_SoaRecord *
+GNUNET_DNSPARSER_parse_soa (const char *udp_payload,
+                           size_t udp_payload_length,
+                           size_t *off)
+{
+  struct GNUNET_DNSPARSER_SoaRecord *soa;
+  struct GNUNET_TUN_DnsSoaRecord soa_bin;
+  size_t old_off;
+
+  old_off = *off;
+  soa = GNUNET_new (struct GNUNET_DNSPARSER_SoaRecord);
+  soa->mname = GNUNET_DNSPARSER_parse_name (udp_payload,
+                                           udp_payload_length,
+                                           off);
+  soa->rname = GNUNET_DNSPARSER_parse_name (udp_payload,
+                                           udp_payload_length,
+                                           off);
+  if ( (NULL == soa->mname) ||
+       (NULL == soa->rname) ||
+       (*off + sizeof (struct GNUNET_TUN_DnsSoaRecord) > udp_payload_length) )
+  {
+    GNUNET_break_op (0);
+    GNUNET_DNSPARSER_free_soa (soa);
+    *off = old_off;
+    return NULL;
+  }
+  GNUNET_memcpy (&soa_bin,
+         &udp_payload[*off],
+         sizeof (struct GNUNET_TUN_DnsSoaRecord));
+  soa->serial = ntohl (soa_bin.serial);
+  soa->refresh = ntohl (soa_bin.refresh);
+  soa->retry = ntohl (soa_bin.retry);
+  soa->expire = ntohl (soa_bin.expire);
+  soa->minimum_ttl = ntohl (soa_bin.minimum);
+  (*off) += sizeof (struct GNUNET_TUN_DnsSoaRecord);
+  return soa;
+}
+
+
+/**
+ * Parse a DNS MX record.
+ *
+ * @param udp_payload reference to UDP packet
+ * @param udp_payload_length length of @a udp_payload
+ * @param off pointer to the offset of the query to parse in the MX record (to be
+ *                    incremented by the size of the record), unchanged on error
+ * @return the parsed MX record, NULL on error
+ */
+struct GNUNET_DNSPARSER_MxRecord *
+GNUNET_DNSPARSER_parse_mx (const char *udp_payload,
+                          size_t udp_payload_length,
+                          size_t *off)
+{
+  struct GNUNET_DNSPARSER_MxRecord *mx;
+  uint16_t mxpref;
+  size_t old_off;
+
+  old_off = *off;
+  if (*off + sizeof (uint16_t) > udp_payload_length)
+  {
+    GNUNET_break_op (0);
+    return NULL;
+  }
+  GNUNET_memcpy (&mxpref, &udp_payload[*off], sizeof (uint16_t));
+  (*off) += sizeof (uint16_t);
+  mx = GNUNET_new (struct GNUNET_DNSPARSER_MxRecord);
+  mx->preference = ntohs (mxpref);
+  mx->mxhost = GNUNET_DNSPARSER_parse_name (udp_payload,
+                                           udp_payload_length,
+                                           off);
+  if (NULL == mx->mxhost)
+  {
+    GNUNET_break_op (0);
+    GNUNET_DNSPARSER_free_mx (mx);
+    *off = old_off;
+    return NULL;
+  }
+  return mx;
+}
+
+
+/**
+ * Parse a DNS SRV record.
+ *
+ * @param udp_payload reference to UDP packet
+ * @param udp_payload_length length of @a udp_payload
+ * @param off pointer to the offset of the query to parse in the SRV record (to be
+ *                    incremented by the size of the record), unchanged on error
+ * @return the parsed SRV record, NULL on error
+ */
+struct GNUNET_DNSPARSER_SrvRecord *
+GNUNET_DNSPARSER_parse_srv (const char *udp_payload,
+                           size_t udp_payload_length,
+                           size_t *off)
+{
+  struct GNUNET_DNSPARSER_SrvRecord *srv;
+  struct GNUNET_TUN_DnsSrvRecord srv_bin;
+  size_t old_off;
+
+  old_off = *off;
+  if (*off + sizeof (struct GNUNET_TUN_DnsSrvRecord) > udp_payload_length)
+    return NULL;
+  GNUNET_memcpy (&srv_bin,
+         &udp_payload[*off],
+         sizeof (struct GNUNET_TUN_DnsSrvRecord));
+  (*off) += sizeof (struct GNUNET_TUN_DnsSrvRecord);
+  srv = GNUNET_new (struct GNUNET_DNSPARSER_SrvRecord);
+  srv->priority = ntohs (srv_bin.prio);
+  srv->weight = ntohs (srv_bin.weight);
+  srv->port = ntohs (srv_bin.port);
+  srv->target = GNUNET_DNSPARSER_parse_name (udp_payload,
+                                            udp_payload_length,
+                                            off);
+  if (NULL == srv->target)
+  {
+    GNUNET_DNSPARSER_free_srv (srv);
+    *off = old_off;
+    return NULL;
+  }
+  return srv;
+}
+
+
+/**
+ * Parse a DNS CERT record.
+ *
+ * @param udp_payload reference to UDP packet
+ * @param udp_payload_length length of @a udp_payload
+ * @param off pointer to the offset of the query to parse in the CERT record (to be
+ *                    incremented by the size of the record), unchanged on error
+ * @return the parsed CERT record, NULL on error
+ */
+struct GNUNET_DNSPARSER_CertRecord *
+GNUNET_DNSPARSER_parse_cert (const char *udp_payload,
+                             size_t udp_payload_length,
+                             size_t *off)
+{
+  struct GNUNET_DNSPARSER_CertRecord *cert;
+  struct GNUNET_TUN_DnsCertRecord dcert;
+
+  if (*off + sizeof (struct GNUNET_TUN_DnsCertRecord) >= udp_payload_length)
+  {
+    GNUNET_break_op (0);
+    return NULL;
+  }
+  GNUNET_memcpy (&dcert, &udp_payload[*off], sizeof (struct GNUNET_TUN_DnsCertRecord));
+  (*off) += sizeof (struct GNUNET_TUN_DnsCertRecord);
+  cert = GNUNET_new (struct GNUNET_DNSPARSER_CertRecord);
+  cert->cert_type = ntohs (dcert.cert_type);
+  cert->cert_tag = ntohs (dcert.cert_tag);
+  cert->algorithm = dcert.algorithm;
+  cert->certificate_size = udp_payload_length - (*off);
+  cert->certificate_data = GNUNET_malloc (cert->certificate_size);
+  GNUNET_memcpy (cert->certificate_data,
+          &udp_payload[*off],
+          cert->certificate_size);
+  (*off) += cert->certificate_size;
+  return cert;
+}
+
+
+/**
+ * Parse a DNS record entry.
+ *
+ * @param udp_payload entire UDP payload
+ * @param udp_payload_length length of @a udp_payload
+ * @param off pointer to the offset of the record to parse in the udp_payload (to be
+ *                    incremented by the size of the record)
+ * @param r where to write the record information
+ * @return #GNUNET_OK on success, #GNUNET_SYSERR if the record is malformed
+ */
+int
+GNUNET_DNSPARSER_parse_record (const char *udp_payload,
+                              size_t udp_payload_length,
+                              size_t *off,
+                              struct GNUNET_DNSPARSER_Record *r)
+{
+  char *name;
+  struct GNUNET_TUN_DnsRecordLine rl;
+  size_t old_off;
+  uint16_t data_len;
+
+  name = GNUNET_DNSPARSER_parse_name (udp_payload,
+                                     udp_payload_length,
+                                     off);
+  if (NULL == name)
+  {
+    GNUNET_break_op (0);
+    return GNUNET_SYSERR;
+  }
+  r->name = name;
+  if (*off + sizeof (struct GNUNET_TUN_DnsRecordLine) > udp_payload_length)
+  {
+    GNUNET_break_op (0);
+    return GNUNET_SYSERR;
+  }
+  GNUNET_memcpy (&rl, &udp_payload[*off], sizeof (rl));
+  (*off) += sizeof (rl);
+  r->type = ntohs (rl.type);
+  r->dns_traffic_class = ntohs (rl.dns_traffic_class);
+  r->expiration_time = GNUNET_TIME_relative_to_absolute (GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS,
+                                                                                       ntohl (rl.ttl)));
+  data_len = ntohs (rl.data_len);
+  if (*off + data_len > udp_payload_length)
+  {
+    GNUNET_break_op (0);
+    return GNUNET_SYSERR;
+  }
+  old_off = *off;
+  switch (r->type)
+  {
+  case GNUNET_DNSPARSER_TYPE_NS:
+  case GNUNET_DNSPARSER_TYPE_CNAME:
+  case GNUNET_DNSPARSER_TYPE_DNAME:
+  case GNUNET_DNSPARSER_TYPE_PTR:
+    r->data.hostname = GNUNET_DNSPARSER_parse_name (udp_payload,
+                                                   udp_payload_length,
+                                                   off);
+    if ( (NULL == r->data.hostname) ||
+        (old_off + data_len != *off) )
+      return GNUNET_SYSERR;
+    return GNUNET_OK;
+  case GNUNET_DNSPARSER_TYPE_SOA:
+    r->data.soa = GNUNET_DNSPARSER_parse_soa (udp_payload,
+                                             udp_payload_length,
+                                             off);
+    if ( (NULL == r->data.soa) ||
+        (old_off + data_len != *off) )
+    {
+      GNUNET_break_op (0);
+      return GNUNET_SYSERR;
+    }
+    return GNUNET_OK;
+  case GNUNET_DNSPARSER_TYPE_MX:
+    r->data.mx = GNUNET_DNSPARSER_parse_mx (udp_payload,
+                                           udp_payload_length,
+                                           off);
+    if ( (NULL == r->data.mx) ||
+        (old_off + data_len != *off) )
+    {
+      GNUNET_break_op (0);
+      return GNUNET_SYSERR;
+    }
+    return GNUNET_OK;
+  case GNUNET_DNSPARSER_TYPE_SRV:
+    r->data.srv = GNUNET_DNSPARSER_parse_srv (udp_payload,
+                                             udp_payload_length,
+                                             off);
+    if ( (NULL == r->data.srv) ||
+        (old_off + data_len != *off) )
+    {
+      GNUNET_break_op (0);
+      return GNUNET_SYSERR;
+    }
+    return GNUNET_OK;
+  default:
+    r->data.raw.data = GNUNET_malloc (data_len);
+    r->data.raw.data_len = data_len;
+    GNUNET_memcpy (r->data.raw.data,
+                   &udp_payload[*off],
+                   data_len);
+    break;
+  }
+  (*off) += data_len;
+  return GNUNET_OK;
+}
+
+
+/**
+ * Parse a UDP payload of a DNS packet in to a nice struct for further
+ * processing and manipulation.
+ *
+ * @param udp_payload wire-format of the DNS packet
+ * @param udp_payload_length number of bytes in @a udp_payload
+ * @return NULL on error, otherwise the parsed packet
+ */
+struct GNUNET_DNSPARSER_Packet *
+GNUNET_DNSPARSER_parse (const char *udp_payload,
+                       size_t udp_payload_length)
+{
+  struct GNUNET_DNSPARSER_Packet *p;
+  const struct GNUNET_TUN_DnsHeader *dns;
+  size_t off;
+  unsigned int n;
+  unsigned int i;
+
+  if (udp_payload_length < sizeof (struct GNUNET_TUN_DnsHeader))
+    return NULL;
+  dns = (const struct GNUNET_TUN_DnsHeader *) udp_payload;
+  off = sizeof (struct GNUNET_TUN_DnsHeader);
+  p = GNUNET_new (struct GNUNET_DNSPARSER_Packet);
+  p->flags = dns->flags;
+  p->id = dns->id;
+  n = ntohs (dns->query_count);
+  if (n > 0)
+  {
+    p->queries = GNUNET_malloc (n * sizeof (struct GNUNET_DNSPARSER_Query));
+    p->num_queries = n;
+    for (i=0;i<n;i++)
+      if (GNUNET_OK !=
+         GNUNET_DNSPARSER_parse_query (udp_payload,
+                                       udp_payload_length,
+                                       &off,
+                                       &p->queries[i]))
+       goto error;
+  }
+  n = ntohs (dns->answer_rcount);
+  if (n > 0)
+  {
+    p->answers = GNUNET_malloc (n * sizeof (struct GNUNET_DNSPARSER_Record));
+    p->num_answers = n;
+    for (i=0;i<n;i++)
+      if (GNUNET_OK !=
+         GNUNET_DNSPARSER_parse_record (udp_payload,
+                                        udp_payload_length,
+                                        &off,
+                                        &p->answers[i]))
+       goto error;
+  }
+  n = ntohs (dns->authority_rcount);
+  if (n > 0)
+  {
+    p->authority_records = GNUNET_malloc (n * sizeof (struct GNUNET_DNSPARSER_Record));
+    p->num_authority_records = n;
+    for (i=0;i<n;i++)
+      if (GNUNET_OK !=
+         GNUNET_DNSPARSER_parse_record (udp_payload,
+                                        udp_payload_length,
+                                        &off,
+                                        &p->authority_records[i]))
+       goto error;
+  }
+  n = ntohs (dns->additional_rcount);
+  if (n > 0)
+  {
+    p->additional_records = GNUNET_malloc (n * sizeof (struct GNUNET_DNSPARSER_Record));
+    p->num_additional_records = n;
+    for (i=0;i<n;i++)
+      if (GNUNET_OK !=
+         GNUNET_DNSPARSER_parse_record (udp_payload,
+                                        udp_payload_length,
+                                        &off,
+                                        &p->additional_records[i]))
+       goto error;
+  }
+  return p;
+ error:
+  GNUNET_break_op (0);
+  GNUNET_DNSPARSER_free_packet (p);
+  return NULL;
+}
+
+
+/**
+ * Free memory taken by a packet.
+ *
+ * @param p packet to free
+ */
+void
+GNUNET_DNSPARSER_free_packet (struct GNUNET_DNSPARSER_Packet *p)
+{
+  unsigned int i;
+
+  for (i=0;i<p->num_queries;i++)
+    GNUNET_free_non_null (p->queries[i].name);
+  GNUNET_free_non_null (p->queries);
+  for (i=0;i<p->num_answers;i++)
+    GNUNET_DNSPARSER_free_record (&p->answers[i]);
+  GNUNET_free_non_null (p->answers);
+  for (i=0;i<p->num_authority_records;i++)
+    GNUNET_DNSPARSER_free_record (&p->authority_records[i]);
+  GNUNET_free_non_null (p->authority_records);
+  for (i=0;i<p->num_additional_records;i++)
+    GNUNET_DNSPARSER_free_record (&p->additional_records[i]);
+  GNUNET_free_non_null (p->additional_records);
+  GNUNET_free (p);
+}
+
+
+/* ********************** DNS packet assembly code **************** */
+
+
+/**
+ * Add a DNS name to the UDP packet at the given location, converting
+ * the name to IDNA notation as necessary.
+ *
+ * @param dst where to write the name (UDP packet)
+ * @param dst_len number of bytes in @a dst
+ * @param off pointer to offset where to write the name (increment by bytes used)
+ *            must not be changed if there is an error
+ * @param name name to write
+ * @return #GNUNET_SYSERR if @a name is invalid
+ *         #GNUNET_NO if @a name did not fit
+ *         #GNUNET_OK if @a name was added to @a dst
+ */
+int
+GNUNET_DNSPARSER_builder_add_name (char *dst,
+                                  size_t dst_len,
+                                  size_t *off,
+                                  const char *name)
+{
+  const char *dot;
+  const char *idna_name;
+  char *idna_start;
+  size_t start;
+  size_t pos;
+  size_t len;
+  Idna_rc rc;
+
+  if (NULL == name)
+    return GNUNET_SYSERR;
+
+  if (IDNA_SUCCESS !=
+      (rc = idna_to_ascii_8z (name,
+                              &idna_start,
+                              IDNA_ALLOW_UNASSIGNED)))
+  {
+    GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
+               _("Failed to convert UTF-8 name `%s' to DNS IDNA format: %s\n"),
+               name,
+               idna_strerror (rc));
+    return GNUNET_NO;
+  }
+  idna_name = idna_start;
+  start = *off;
+  if (start + strlen (idna_name) + 2 > dst_len)
+    goto fail;
+  pos = start;
+  do
+  {
+    dot = strchr (idna_name, '.');
+    if (NULL == dot)
+      len = strlen (idna_name);
+    else
+      len = dot - idna_name;
+    if ( (len >= 64) || (0 == len) )
+    {
+      GNUNET_break (0);
+      goto fail; /* segment too long or empty */
+    }
+    dst[pos++] = (char) (uint8_t) len;
+    GNUNET_memcpy (&dst[pos],
+                   idna_name,
+                   len);
+    pos += len;
+    idna_name += len + 1; /* also skip dot */
+  }
+  while (NULL != dot);
+  dst[pos++] = '\0'; /* terminator */
+  *off = pos;
+#if WINDOWS
+  idn_free (idna_start);
+#else
+  free (idna_start);
+#endif
+  return GNUNET_OK;
+ fail:
+#if WINDOWS
+  idn_free (idna_start);
+#else
+  free (idna_start);
+#endif
+  return GNUNET_NO;
+}
+
+
+/**
+ * Add a DNS query to the UDP packet at the given location.
+ *
+ * @param dst where to write the query
+ * @param dst_len number of bytes in @a dst
+ * @param off pointer to offset where to write the query (increment by bytes used)
+ *            must not be changed if there is an error
+ * @param query query to write
+ * @return #GNUNET_SYSERR if @a query is invalid
+ *         #GNUNET_NO if @a query did not fit
+ *         #GNUNET_OK if @a query was added to @a dst
+ */
+int
+GNUNET_DNSPARSER_builder_add_query (char *dst,
+                                   size_t dst_len,
+                                   size_t *off,
+                                   const struct GNUNET_DNSPARSER_Query *query)
+{
+  int ret;
+  struct GNUNET_TUN_DnsQueryLine ql;
+
+  ret = GNUNET_DNSPARSER_builder_add_name (dst, dst_len - sizeof (struct GNUNET_TUN_DnsQueryLine), off, query->name);
+  if (ret != GNUNET_OK)
+    return ret;
+  ql.type = htons (query->type);
+  ql.dns_traffic_class = htons (query->dns_traffic_class);
+  GNUNET_memcpy (&dst[*off], &ql, sizeof (ql));
+  (*off) += sizeof (ql);
+  return GNUNET_OK;
+}
+
+
+/**
+ * Add an MX record to the UDP packet at the given location.
+ *
+ * @param dst where to write the mx record
+ * @param dst_len number of bytes in @a dst
+ * @param off pointer to offset where to write the mx information (increment by bytes used);
+ *            can also change if there was an error
+ * @param mx mx information to write
+ * @return #GNUNET_SYSERR if @a mx is invalid
+ *         #GNUNET_NO if @a mx did not fit
+ *         #GNUNET_OK if @a mx was added to @a dst
+ */
+int
+GNUNET_DNSPARSER_builder_add_mx (char *dst,
+                                size_t dst_len,
+                                size_t *off,
+                                const struct GNUNET_DNSPARSER_MxRecord *mx)
+{
+  uint16_t mxpref;
+
+  if (*off + sizeof (uint16_t) > dst_len)
+    return GNUNET_NO;
+  mxpref = htons (mx->preference);
+  GNUNET_memcpy (&dst[*off],
+                &mxpref,
+                sizeof (mxpref));
+  (*off) += sizeof (mxpref);
+  return GNUNET_DNSPARSER_builder_add_name (dst,
+                                           dst_len,
+                                           off,
+                                           mx->mxhost);
+}
+
+
+/**
+ * Add a CERT record to the UDP packet at the given location.
+ *
+ * @param dst where to write the CERT record
+ * @param dst_len number of bytes in @a dst
+ * @param off pointer to offset where to write the CERT information (increment by bytes used);
+ *            can also change if there was an error
+ * @param cert CERT information to write
+ * @return #GNUNET_SYSERR if @a cert is invalid
+ *         #GNUNET_NO if @a cert did not fit
+ *         #GNUNET_OK if @a cert was added to @a dst
+ */
+int
+GNUNET_DNSPARSER_builder_add_cert (char *dst,
+                                   size_t dst_len,
+                                   size_t *off,
+                                   const struct GNUNET_DNSPARSER_CertRecord *cert)
+{
+  struct GNUNET_TUN_DnsCertRecord dcert;
+
+  if ( (cert->cert_type > UINT16_MAX) ||
+       (cert->cert_tag > UINT16_MAX) ||
+       (cert->algorithm > UINT8_MAX) )
+  {
+    GNUNET_break (0);
+    return GNUNET_SYSERR;
+  }
+  if (*off + sizeof (struct GNUNET_TUN_DnsCertRecord) + cert->certificate_size > dst_len)
+    return GNUNET_NO;
+  dcert.cert_type = htons ((uint16_t) cert->cert_type);
+  dcert.cert_tag = htons ((uint16_t) cert->cert_tag);
+  dcert.algorithm = (uint8_t) cert->algorithm;
+  GNUNET_memcpy (&dst[*off], &dcert, sizeof (dcert));
+  (*off) += sizeof (dcert);
+  GNUNET_memcpy (&dst[*off], cert->certificate_data, cert->certificate_size);
+  (*off) += cert->certificate_size;
+  return GNUNET_OK;
+}
+
+
+/**
+ * Add an SOA record to the UDP packet at the given location.
+ *
+ * @param dst where to write the SOA record
+ * @param dst_len number of bytes in @a dst
+ * @param off pointer to offset where to write the SOA information (increment by bytes used)
+ *            can also change if there was an error
+ * @param soa SOA information to write
+ * @return #GNUNET_SYSERR if @a soa is invalid
+ *         #GNUNET_NO if @a soa did not fit
+ *         #GNUNET_OK if @a soa was added to @a dst
+ */
+int
+GNUNET_DNSPARSER_builder_add_soa (char *dst,
+                                 size_t dst_len,
+                                 size_t *off,
+                                 const struct GNUNET_DNSPARSER_SoaRecord *soa)
+{
+  struct GNUNET_TUN_DnsSoaRecord sd;
+  int ret;
+
+  if ( (GNUNET_OK != (ret = GNUNET_DNSPARSER_builder_add_name (dst,
+                                                               dst_len,
+                                                               off,
+                                                               soa->mname))) ||
+       (GNUNET_OK != (ret = GNUNET_DNSPARSER_builder_add_name (dst,
+                                                               dst_len,
+                                                               off,
+                                                               soa->rname)) ) )
+    return ret;
+  if (*off + sizeof (struct GNUNET_TUN_DnsSoaRecord) > dst_len)
+    return GNUNET_NO;
+  sd.serial = htonl (soa->serial);
+  sd.refresh = htonl (soa->refresh);
+  sd.retry = htonl (soa->retry);
+  sd.expire = htonl (soa->expire);
+  sd.minimum = htonl (soa->minimum_ttl);
+  GNUNET_memcpy (&dst[*off], &sd, sizeof (sd));
+  (*off) += sizeof (sd);
+  return GNUNET_OK;
+}
+
+
+/**
+ * Add an SRV record to the UDP packet at the given location.
+ *
+ * @param dst where to write the SRV record
+ * @param dst_len number of bytes in @a dst
+ * @param off pointer to offset where to write the SRV information (increment by bytes used)
+ *            can also change if there was an error
+ * @param srv SRV information to write
+ * @return #GNUNET_SYSERR if @a srv is invalid
+ *         #GNUNET_NO if @a srv did not fit
+ *         #GNUNET_OK if @a srv was added to @a dst
+ */
+int
+GNUNET_DNSPARSER_builder_add_srv (char *dst,
+                                 size_t dst_len,
+                                 size_t *off,
+                                 const struct GNUNET_DNSPARSER_SrvRecord *srv)
+{
+  struct GNUNET_TUN_DnsSrvRecord sd;
+  int ret;
+
+  if (*off + sizeof (struct GNUNET_TUN_DnsSrvRecord) > dst_len)
+    return GNUNET_NO;
+  sd.prio = htons (srv->priority);
+  sd.weight = htons (srv->weight);
+  sd.port = htons (srv->port);
+  GNUNET_memcpy (&dst[*off], &sd, sizeof (sd));
+  (*off) += sizeof (sd);
+  if (GNUNET_OK != (ret = GNUNET_DNSPARSER_builder_add_name (dst,
+                                   dst_len,
+                                   off,
+                                   srv->target)))
+    return ret;
+  return GNUNET_OK;
+}
+
+
+/**
+ * Add a DNS record to the UDP packet at the given location.
+ *
+ * @param dst where to write the query
+ * @param dst_len number of bytes in @a dst
+ * @param off pointer to offset where to write the query (increment by bytes used)
+ *            must not be changed if there is an error
+ * @param record record to write
+ * @return #GNUNET_SYSERR if @a record is invalid
+ *         #GNUNET_NO if @a record did not fit
+ *         #GNUNET_OK if @a record was added to @a dst
+ */
+static int
+add_record (char *dst,
+           size_t dst_len,
+           size_t *off,
+           const struct GNUNET_DNSPARSER_Record *record)
+{
+  int ret;
+  size_t start;
+  size_t pos;
+  struct GNUNET_TUN_DnsRecordLine rl;
+
+  start = *off;
+  ret = GNUNET_DNSPARSER_builder_add_name (dst,
+                                           dst_len - sizeof (struct GNUNET_TUN_DnsRecordLine),
+                                           off,
+                                           record->name);
+  if (GNUNET_OK != ret)
+    return ret;
+  /* '*off' is now the position where we will need to write the record line */
+
+  pos = *off + sizeof (struct GNUNET_TUN_DnsRecordLine);
+  switch (record->type)
+  {
+  case GNUNET_DNSPARSER_TYPE_MX:
+    ret = GNUNET_DNSPARSER_builder_add_mx (dst,
+                                          dst_len,
+                                          &pos,
+                                          record->data.mx);
+    break;
+  case GNUNET_DNSPARSER_TYPE_CERT:
+    ret = GNUNET_DNSPARSER_builder_add_cert (dst,
+                                            dst_len,
+                                            &pos,
+                                            record->data.cert);
+    break;
+  case GNUNET_DNSPARSER_TYPE_SOA:
+    ret = GNUNET_DNSPARSER_builder_add_soa (dst,
+                                           dst_len,
+                                           &pos,
+                                           record->data.soa);
+    break;
+  case GNUNET_DNSPARSER_TYPE_NS:
+  case GNUNET_DNSPARSER_TYPE_CNAME:
+  case GNUNET_DNSPARSER_TYPE_PTR:
+    ret = GNUNET_DNSPARSER_builder_add_name (dst,
+                                            dst_len,
+                                            &pos,
+                                            record->data.hostname);
+    break;
+  case GNUNET_DNSPARSER_TYPE_SRV:
+    ret = GNUNET_DNSPARSER_builder_add_srv (dst,
+                                           dst_len,
+                                           &pos,
+                                           record->data.srv);
+    break;
+  default:
+    if (pos + record->data.raw.data_len > dst_len)
+    {
+      ret = GNUNET_NO;
+      break;
+    }
+    GNUNET_memcpy (&dst[pos],
+                  record->data.raw.data,
+                  record->data.raw.data_len);
+    pos += record->data.raw.data_len;
+    ret = GNUNET_OK;
+    break;
+  }
+  if (GNUNET_OK != ret)
+  {
+    *off = start;
+    return GNUNET_NO;
+  }
+
+  if (pos - (*off + sizeof (struct GNUNET_TUN_DnsRecordLine)) > UINT16_MAX)
+  {
+    /* record data too long */
+    *off = start;
+    return GNUNET_NO;
+  }
+  rl.type = htons (record->type);
+  rl.dns_traffic_class = htons (record->dns_traffic_class);
+  rl.ttl = htonl (GNUNET_TIME_absolute_get_remaining (record->expiration_time).rel_value_us / 1000LL / 1000LL); /* in seconds */
+  rl.data_len = htons ((uint16_t) (pos - (*off + sizeof (struct GNUNET_TUN_DnsRecordLine))));
+  GNUNET_memcpy (&dst[*off], &rl, sizeof (struct GNUNET_TUN_DnsRecordLine));
+  *off = pos;
+  return GNUNET_OK;
+}
+
+
+/**
+ * Given a DNS packet @a p, generate the corresponding UDP payload.
+ * Note that we do not attempt to pack the strings with pointers
+ * as this would complicate the code and this is about being
+ * simple and secure, not fast, fancy and broken like bind.
+ *
+ * @param p packet to pack
+ * @param max maximum allowed size for the resulting UDP payload
+ * @param buf set to a buffer with the packed message
+ * @param buf_length set to the length of @a buf
+ * @return #GNUNET_SYSERR if @a p is invalid
+ *         #GNUNET_NO if @a p was truncated (but there is still a result in @a buf)
+ *         #GNUNET_OK if @a p was packed completely into @a buf
+ */
+int
+GNUNET_DNSPARSER_pack (const struct GNUNET_DNSPARSER_Packet *p,
+                      uint16_t max,
+                      char **buf,
+                      size_t *buf_length)
+{
+  struct GNUNET_TUN_DnsHeader dns;
+  size_t off;
+  char tmp[max];
+  unsigned int i;
+  int ret;
+  int trc;
+
+  if ( (p->num_queries > UINT16_MAX) ||
+       (p->num_answers > UINT16_MAX) ||
+       (p->num_authority_records > UINT16_MAX) ||
+       (p->num_additional_records > UINT16_MAX) )
+    return GNUNET_SYSERR;
+  dns.id = p->id;
+  dns.flags = p->flags;
+  dns.query_count = htons (p->num_queries);
+  dns.answer_rcount = htons (p->num_answers);
+  dns.authority_rcount = htons (p->num_authority_records);
+  dns.additional_rcount = htons (p->num_additional_records);
+
+  off = sizeof (struct GNUNET_TUN_DnsHeader);
+  trc = GNUNET_NO;
+  for (i=0;i<p->num_queries;i++)
+  {
+    ret = GNUNET_DNSPARSER_builder_add_query (tmp,
+                                             sizeof (tmp),
+                                             &off,
+                                             &p->queries[i]);
+    if (GNUNET_SYSERR == ret)
+      return GNUNET_SYSERR;
+    if (GNUNET_NO == ret)
+    {
+      dns.query_count = htons ((uint16_t) (i-1));
+      trc = GNUNET_YES;
+      break;
+    }
+  }
+  for (i=0;i<p->num_answers;i++)
+  {
+    ret = add_record (tmp,
+                     sizeof (tmp),
+                     &off,
+                     &p->answers[i]);
+    if (GNUNET_SYSERR == ret)
+      return GNUNET_SYSERR;
+    if (GNUNET_NO == ret)
+    {
+      dns.answer_rcount = htons ((uint16_t) (i-1));
+      trc = GNUNET_YES;
+      break;
+    }
+  }
+  for (i=0;i<p->num_authority_records;i++)
+  {
+    ret = add_record (tmp,
+                     sizeof (tmp),
+                     &off,
+                     &p->authority_records[i]);
+    if (GNUNET_SYSERR == ret)
+      return GNUNET_SYSERR;
+    if (GNUNET_NO == ret)
+    {
+      dns.authority_rcount = htons ((uint16_t) (i-1));
+      trc = GNUNET_YES;
+      break;
+    }
+  }
+  for (i=0;i<p->num_additional_records;i++)
+  {
+    ret = add_record (tmp,
+                     sizeof (tmp),
+                     &off,
+                     &p->additional_records[i]);
+    if (GNUNET_SYSERR == ret)
+      return GNUNET_SYSERR;
+    if (GNUNET_NO == ret)
+    {
+      dns.additional_rcount = htons (i-1);
+      trc = GNUNET_YES;
+      break;
+    }
+  }
+
+  if (GNUNET_YES == trc)
+    dns.flags.message_truncated = 1;
+  GNUNET_memcpy (tmp,
+                &dns,
+                sizeof (struct GNUNET_TUN_DnsHeader));
+
+  *buf = GNUNET_malloc (off);
+  *buf_length = off;
+  GNUNET_memcpy (*buf,
+                tmp,
+                off);
+  if (GNUNET_YES == trc)
+    return GNUNET_NO;
+  return GNUNET_OK;
+}
+
+
+/**
+ * Convert a block of binary data to HEX.
+ *
+ * @param data binary data to convert
+ * @param data_size number of bytes in @a data
+ * @return HEX string (lower case)
+ */
+char *
+GNUNET_DNSPARSER_bin_to_hex (const void *data,
+                             size_t data_size)
+{
+  char *ret;
+  size_t off;
+  const uint8_t *idata;
+
+  idata = data;
+  ret = GNUNET_malloc (data_size * 2 + 1);
+  for (off = 0; off < data_size; off++)
+    sprintf (&ret[off * 2],
+             "%02x",
+             idata[off]);
+  return ret;
+}
+
+
+/**
+ * Convert a HEX string to block of binary data.
+ *
+ * @param hex HEX string to convert (may contain mixed case)
+ * @param data where to write result, must be
+ *             at least `strlen(hex)/2` bytes long
+ * @return number of bytes written to data
+ */
+size_t
+GNUNET_DNSPARSER_hex_to_bin (const char *hex,
+                             void *data)
+{
+  size_t data_size;
+  size_t off;
+  uint8_t *idata;
+  unsigned int h;
+  char in[3];
+
+  data_size = strlen (hex) / 2;
+  idata = data;
+  in[2] = '\0';
+  for (off = 0; off < data_size; off++)
+  {
+    in[0] = tolower ((unsigned char) hex[off * 2]);
+    in[1] = tolower ((unsigned char) hex[off * 2 + 1]);
+    if (1 != sscanf (in, "%x", &h))
+      return off;
+    idata[off] = (uint8_t) h;
+  }
+  return off;
+}
+
+
+/* end of dnsparser.c */
diff --git a/src/dns/dnsstub.c b/src/dns/dnsstub.c
new file mode 100644 (file)
index 0000000..969ff7b
--- /dev/null
@@ -0,0 +1,749 @@
+/*
+     This file is part of GNUnet.
+     Copyright (C) 2012, 2018 GNUnet e.V.
+
+     GNUnet is free software: you can redistribute it and/or modify it
+     under the terms of the GNU Affero General Public License as published
+     by the Free Software Foundation, either version 3 of the License,
+     or (at your option) any later version.
+
+     GNUnet is distributed in the hope that it will be useful, but
+     WITHOUT ANY WARRANTY; without even the implied warranty of
+     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+     Affero General Public License for more details.
+    
+     You should have received a copy of the GNU Affero General Public License
+     along with this program.  If not, see <http://www.gnu.org/licenses/>.
+*/
+/**
+ * @file dns/dnsstub.c
+ * @brief DNS stub resolver which sends DNS requests to an actual resolver
+ * @author Christian Grothoff
+ */
+#include "platform.h"
+#include "gnunet_util_lib.h"
+#include "gnunet_tun_lib.h"
+#include "gnunet_dnsstub_lib.h"
+
+/**
+ * Timeout for retrying DNS queries.
+ */
+#define DNS_RETRANSMIT_DELAY GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_MILLISECONDS, 250)
+
+
+/**
+ * DNS Server used for resolution.
+ */
+struct DnsServer;
+
+
+/**
+ * UDP socket we are using for sending DNS requests to the Internet.
+ */
+struct GNUNET_DNSSTUB_RequestSocket
+{
+
+  /**
+   * UDP socket we use for this request for IPv4
+   */
+  struct GNUNET_NETWORK_Handle *dnsout4;
+
+  /**
+   * UDP socket we use for this request for IPv6
+   */
+  struct GNUNET_NETWORK_Handle *dnsout6;
+
+  /**
+   * Function to call with result.
+   */
+  GNUNET_DNSSTUB_ResultCallback rc;
+
+  /**
+   * Closure for @e rc.
+   */
+  void *rc_cls;
+
+  /**
+   * Task for reading from dnsout4 and dnsout6.
+   */
+  struct GNUNET_SCHEDULER_Task *read_task;
+
+  /**
+   * Task for retrying transmission of the query.
+   */
+  struct GNUNET_SCHEDULER_Task *retry_task;
+
+  /**
+   * Next address we sent the DNS request to.
+   */
+  struct DnsServer *ds_pos;
+
+  /**
+   * Context this request executes in.
+   */
+  struct GNUNET_DNSSTUB_Context *ctx;
+
+  /**
+   * Query we sent to @e addr.
+   */
+  void *request;
+
+  /**
+   * Number of bytes in @a request.
+   */
+  size_t request_len;
+
+};
+
+
+/**
+ * DNS Server used for resolution.
+ */
+struct DnsServer
+{
+
+  /**
+   * Kept in a DLL.
+   */
+  struct DnsServer *next;
+
+  /**
+   * Kept in a DLL.
+   */
+  struct DnsServer *prev;
+
+  /**
+   * IP address of the DNS resolver.
+   */
+  struct sockaddr_storage ss;
+};
+
+
+/**
+ * Handle to the stub resolver.
+ */
+struct GNUNET_DNSSTUB_Context
+{
+
+  /**
+   * Array of all open sockets for DNS requests.
+   */
+  struct GNUNET_DNSSTUB_RequestSocket *sockets;
+
+  /**
+   * DLL of DNS resolvers we use.
+   */
+  struct DnsServer *dns_head;
+
+  /**
+   * DLL of DNS resolvers we use.
+   */
+  struct DnsServer *dns_tail;
+
+  /**
+   * How frequently do we retry requests?
+   */
+  struct GNUNET_TIME_Relative retry_freq;
+
+  /**
+   * Length of @e sockets array.
+   */
+  unsigned int num_sockets;
+
+};
+
+
+/**
+ * We're done with a `struct GNUNET_DNSSTUB_RequestSocket`, close it for now.
+ *
+ * @param rs request socket to clean up
+ */
+static void
+cleanup_rs (struct GNUNET_DNSSTUB_RequestSocket *rs)
+{
+  if (NULL != rs->dnsout4)
+  {
+    GNUNET_NETWORK_socket_close (rs->dnsout4);
+    rs->dnsout4 = NULL;
+  }
+  if (NULL != rs->dnsout6)
+  {
+    GNUNET_NETWORK_socket_close (rs->dnsout6);
+    rs->dnsout6 = NULL;
+  }
+  if (NULL != rs->read_task)
+  {
+    GNUNET_SCHEDULER_cancel (rs->read_task);
+    rs->read_task = NULL;
+  }
+  if (NULL != rs->retry_task)
+  {
+    GNUNET_SCHEDULER_cancel (rs->retry_task);
+    rs->retry_task = NULL;
+  }
+  if (NULL != rs->request)
+  {
+    GNUNET_free (rs->request);
+    rs->request = NULL;
+  }
+}
+
+
+/**
+ * Open source port for sending DNS requests
+ *
+ * @param af AF_INET or AF_INET6
+ * @return #GNUNET_OK on success
+ */
+static struct GNUNET_NETWORK_Handle *
+open_socket (int af)
+{
+  struct sockaddr_in a4;
+  struct sockaddr_in6 a6;
+  struct sockaddr *sa;
+  socklen_t alen;
+  struct GNUNET_NETWORK_Handle *ret;
+
+  ret = GNUNET_NETWORK_socket_create (af, SOCK_DGRAM, 0);
+  if (NULL == ret)
+    return NULL;
+  switch (af)
+  {
+  case AF_INET:
+    memset (&a4, 0, alen = sizeof (struct sockaddr_in));
+    sa = (struct sockaddr *) &a4;
+    break;
+  case AF_INET6:
+    memset (&a6, 0, alen = sizeof (struct sockaddr_in6));
+    sa = (struct sockaddr *) &a6;
+    break;
+  default:
+    GNUNET_break (0);
+    GNUNET_NETWORK_socket_close (ret);
+    return NULL;
+  }
+  sa->sa_family = af;
+  if (GNUNET_OK != GNUNET_NETWORK_socket_bind (ret,
+                                              sa,
+                                              alen))
+  {
+    GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+               _("Could not bind to any port: %s\n"),
+               STRERROR (errno));
+    GNUNET_NETWORK_socket_close (ret);
+    return NULL;
+  }
+  return ret;
+}
+
+
+/**
+ * Get a socket of the specified address family to send out a
+ * UDP DNS request to the Internet.
+ *
+ * @param ctx the DNSSTUB context
+ * @return NULL on error
+ */
+static struct GNUNET_DNSSTUB_RequestSocket *
+get_request_socket (struct GNUNET_DNSSTUB_Context *ctx)
+{
+  struct GNUNET_DNSSTUB_RequestSocket *rs;
+
+  for (unsigned int i=0;i<256;i++)
+  {
+    rs = &ctx->sockets[GNUNET_CRYPTO_random_u32 (GNUNET_CRYPTO_QUALITY_NONCE,
+                                                 ctx->num_sockets)];
+    if (NULL == rs->rc)
+      break;
+  }
+  if (NULL != rs->rc)
+  {
+    /* signal "failure" */
+    rs->rc (rs->rc_cls,
+            NULL,
+            0);
+    rs->rc = NULL;
+  }
+  if (NULL != rs->read_task)
+  {
+    GNUNET_SCHEDULER_cancel (rs->read_task);
+    rs->read_task = NULL;
+  }
+  if (NULL != rs->retry_task)
+  {
+    GNUNET_SCHEDULER_cancel (rs->retry_task);
+    rs->retry_task = NULL;
+  }
+  if (NULL != rs->request)
+  {
+    GNUNET_free (rs->request);
+    rs->request = NULL;
+  }
+  rs->ctx = ctx;
+  return rs;
+}
+
+
+/**
+ * Actually do the reading of a DNS packet from our UDP socket and see
+ * if we have a valid, matching, pending request.
+ *
+ * @param rs request socket with callback details
+ * @param dnsout socket to read from
+ * @return #GNUNET_OK on success, #GNUNET_NO on drop, #GNUNET_SYSERR on IO-errors (closed socket)
+ */
+static int
+do_dns_read (struct GNUNET_DNSSTUB_RequestSocket *rs,
+            struct GNUNET_NETWORK_Handle *dnsout)
+{
+  struct GNUNET_DNSSTUB_Context *ctx = rs->ctx;
+  ssize_t r;
+  int len;
+
+#ifndef MINGW
+  if (0 != ioctl (GNUNET_NETWORK_get_fd (dnsout),
+                  FIONREAD,
+                  &len))
+  {
+    /* conservative choice: */
+    len = UINT16_MAX;
+  }
+#else
+  /* port the code above? */
+  len = UINT16_MAX;
+#endif
+  GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+             "Receiving %d byte DNS reply\n",
+             len);
+  {
+    unsigned char buf[len] GNUNET_ALIGN;
+    int found;
+    struct sockaddr_storage addr;
+    socklen_t addrlen;
+    struct GNUNET_TUN_DnsHeader *dns;
+
+    addrlen = sizeof (addr);
+    memset (&addr,
+            0,
+            sizeof (addr));
+    r = GNUNET_NETWORK_socket_recvfrom (dnsout,
+                                       buf,
+                                        sizeof (buf),
+                                       (struct sockaddr*) &addr,
+                                        &addrlen);
+    if (-1 == r)
+    {
+      GNUNET_log_strerror (GNUNET_ERROR_TYPE_ERROR,
+                           "recvfrom");
+      GNUNET_NETWORK_socket_close (dnsout);
+      return GNUNET_SYSERR;
+    }
+    found = GNUNET_NO;
+    for (struct DnsServer *ds = ctx->dns_head; NULL != ds; ds = ds->next)
+    {
+      if (0 == memcmp (&addr,
+                       &ds->ss,
+                       GNUNET_MIN (sizeof (struct sockaddr_storage),
+                                   addrlen)))
+      {
+        found = GNUNET_YES;
+        break;
+      }
+    }
+    if (GNUNET_NO == found)
+    {
+      GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+                 "Received DNS response from server we never asked (ignored)");
+      return GNUNET_NO;
+    }
+    if (sizeof (struct GNUNET_TUN_DnsHeader) > r)
+    {
+      GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+                 _("Received DNS response that is too small (%u bytes)"),
+                 (unsigned int) r);
+      return GNUNET_NO;
+    }
+    dns = (struct GNUNET_TUN_DnsHeader *) buf;
+    if (NULL == rs->rc)
+    {
+      GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+                 "Request timeout or cancelled; ignoring reply\n");
+      return GNUNET_NO;
+    }
+    rs->rc (rs->rc_cls,
+            dns,
+            r);
+  }
+  return GNUNET_OK;
+}
+
+
+/**
+ * Read a DNS response from the (unhindered) UDP-Socket
+ *
+ * @param cls socket to read from
+ */
+static void
+read_response (void *cls);
+
+
+/**
+ * Schedule #read_response() task for @a rs.
+ *
+ * @param rs request to schedule read operation for
+ */
+static void
+schedule_read (struct GNUNET_DNSSTUB_RequestSocket *rs)
+{
+  struct GNUNET_NETWORK_FDSet *rset;
+
+  if (NULL != rs->read_task)
+    GNUNET_SCHEDULER_cancel (rs->read_task);
+  rset = GNUNET_NETWORK_fdset_create ();
+  if (NULL != rs->dnsout4)
+    GNUNET_NETWORK_fdset_set (rset,
+                              rs->dnsout4);
+  if (NULL != rs->dnsout6)
+    GNUNET_NETWORK_fdset_set (rset,
+                              rs->dnsout6);
+  rs->read_task = GNUNET_SCHEDULER_add_select (GNUNET_SCHEDULER_PRIORITY_DEFAULT,
+                                              GNUNET_TIME_UNIT_FOREVER_REL,
+                                              rset,
+                                              NULL,
+                                              &read_response,
+                                               rs);
+  GNUNET_NETWORK_fdset_destroy (rset);
+}
+
+
+/**
+ * Read a DNS response from the (unhindered) UDP-Socket
+ *
+ * @param cls `struct GNUNET_DNSSTUB_RequestSocket` to read from
+ */
+static void
+read_response (void *cls)
+{
+  struct GNUNET_DNSSTUB_RequestSocket *rs = cls;
+  const struct GNUNET_SCHEDULER_TaskContext *tc;
+
+  rs->read_task = NULL;
+  tc = GNUNET_SCHEDULER_get_task_context ();
+  /* read and process ready sockets */
+  if ( (NULL != rs->dnsout4) &&
+       (GNUNET_NETWORK_fdset_isset (tc->read_ready,
+                                    rs->dnsout4)) &&
+       (GNUNET_SYSERR ==
+        do_dns_read (rs,
+                     rs->dnsout4)) )
+    rs->dnsout4 = NULL;
+  if ( (NULL != rs->dnsout6) &&
+       (GNUNET_NETWORK_fdset_isset (tc->read_ready,
+                                    rs->dnsout6)) &&
+       (GNUNET_SYSERR ==
+        do_dns_read (rs,
+                     rs->dnsout6)) )
+    rs->dnsout6 = NULL;
+  /* re-schedule read task */
+  schedule_read (rs);
+}
+
+
+/**
+ * Task to (re)transmit the DNS query, possibly repeatedly until
+ * we succeed.
+ *
+ * @param cls our `struct GNUNET_DNSSTUB_RequestSocket *`
+ */
+static void
+transmit_query (void *cls)
+{
+  struct GNUNET_DNSSTUB_RequestSocket *rs = cls;
+  struct GNUNET_DNSSTUB_Context *ctx = rs->ctx;
+  const struct sockaddr *sa;
+  socklen_t salen;
+  struct DnsServer *ds;
+  struct GNUNET_NETWORK_Handle *dnsout;
+
+  rs->retry_task = GNUNET_SCHEDULER_add_delayed (ctx->retry_freq,
+                                                 &transmit_query,
+                                                 rs);
+  ds = rs->ds_pos;
+  rs->ds_pos = ds->next;
+  if (NULL == rs->ds_pos)
+    rs->ds_pos = ctx->dns_head;
+  GNUNET_assert (NULL != ds);
+  dnsout = NULL;
+  switch (ds->ss.ss_family)
+  {
+  case AF_INET:
+    if (NULL == rs->dnsout4)
+      rs->dnsout4 = open_socket (AF_INET);
+    dnsout = rs->dnsout4;
+    sa = (const struct sockaddr *) &ds->ss;
+    salen = sizeof (struct sockaddr_in);
+    break;
+  case AF_INET6:
+    if (NULL == rs->dnsout6)
+      rs->dnsout6 = open_socket (AF_INET6);
+    dnsout = rs->dnsout6;
+    sa = (const struct sockaddr *) &ds->ss;
+    salen = sizeof (struct sockaddr_in6);
+    break;
+  default:
+    return;
+  }
+  if (NULL == dnsout)
+  {
+    GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+                "Unable to use configure DNS server, skipping\n");
+    return;
+  }
+  if (GNUNET_SYSERR ==
+      GNUNET_NETWORK_socket_sendto (dnsout,
+                                   rs->request,
+                                   rs->request_len,
+                                    sa,
+                                    salen))
+    GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
+               _("Failed to send DNS request to %s: %s\n"),
+               GNUNET_a2s (sa,
+                            salen),
+                STRERROR (errno));
+  else
+    GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+               _("Sent DNS request to %s\n"),
+               GNUNET_a2s (sa,
+                            salen));
+  schedule_read (rs);
+}
+
+
+/**
+ * Perform DNS resolution using our default IP from init.
+ *
+ * @param ctx stub resolver to use
+ * @param request DNS request to transmit
+ * @param request_len number of bytes in msg
+ * @param rc function to call with result
+ * @param rc_cls closure for 'rc'
+ * @return socket used for the request, NULL on error
+ */
+struct GNUNET_DNSSTUB_RequestSocket *
+GNUNET_DNSSTUB_resolve (struct GNUNET_DNSSTUB_Context *ctx,
+                        const void *request,
+                        size_t request_len,
+                        GNUNET_DNSSTUB_ResultCallback rc,
+                        void *rc_cls)
+{
+  struct GNUNET_DNSSTUB_RequestSocket *rs;
+
+  if (NULL == ctx->dns_head)
+  {
+    GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+                "No DNS server configured for resolution\n");
+    return NULL;
+  }
+  if (NULL == (rs = get_request_socket (ctx)))
+  {
+    GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+                "No request socket available for DNS resolution\n");
+    return NULL;
+  }
+  rs->ds_pos = ctx->dns_head;
+  rs->rc = rc;
+  rs->rc_cls = rc_cls;
+  rs->request = GNUNET_memdup (request,
+                               request_len);
+  rs->request_len = request_len;
+  rs->retry_task = GNUNET_SCHEDULER_add_now (&transmit_query,
+                                             rs);
+  return rs;
+}
+
+
+/**
+ * Cancel DNS resolution.
+ *
+ * @param rs resolution to cancel
+ */
+void
+GNUNET_DNSSTUB_resolve_cancel (struct GNUNET_DNSSTUB_RequestSocket *rs)
+{
+  rs->rc = NULL;
+  if (NULL != rs->retry_task)
+  {
+    GNUNET_SCHEDULER_cancel (rs->retry_task);
+    rs->retry_task = NULL;
+  }
+  if (NULL != rs->read_task)
+  {
+    GNUNET_SCHEDULER_cancel (rs->read_task);
+    rs->read_task = NULL;
+  }
+}
+
+
+/**
+ * Start a DNS stub resolver.
+ *
+ * @param num_sockets how many sockets should we open
+ *        in parallel for DNS queries for this stub?
+ * @return NULL on error
+ */
+struct GNUNET_DNSSTUB_Context *
+GNUNET_DNSSTUB_start (unsigned int num_sockets)
+{
+  struct GNUNET_DNSSTUB_Context *ctx;
+
+  if (0 == num_sockets)
+  {
+    GNUNET_break (0);
+    return NULL;
+  }
+  ctx = GNUNET_new (struct GNUNET_DNSSTUB_Context);
+  ctx->num_sockets = num_sockets;
+  ctx->sockets = GNUNET_new_array (num_sockets,
+                                   struct GNUNET_DNSSTUB_RequestSocket);
+  ctx->retry_freq = DNS_RETRANSMIT_DELAY;
+  return ctx;
+}
+
+
+/**
+ * Add nameserver for use by the DNSSTUB.  We will use
+ * all provided nameservers for resolution (round-robin).
+ *
+ * @param ctx resolver context to modify
+ * @param dns_ip target IP address to use (as string)
+ * @return #GNUNET_OK on success
+ */
+int
+GNUNET_DNSSTUB_add_dns_ip (struct GNUNET_DNSSTUB_Context *ctx,
+                           const char *dns_ip)
+{
+  struct DnsServer *ds;
+  struct in_addr i4;
+  struct in6_addr i6;
+
+  ds = GNUNET_new (struct DnsServer);
+  if (1 == inet_pton (AF_INET,
+                      dns_ip,
+                      &i4))
+  {
+    struct sockaddr_in *s4 = (struct sockaddr_in *) &ds->ss;
+
+    s4->sin_family = AF_INET;
+    s4->sin_port = htons (53);
+    s4->sin_addr = i4;
+#if HAVE_SOCKADDR_IN_SIN_LEN
+    s4->sin_len = (u_char) sizeof (struct sockaddr_in);
+#endif
+  }
+  else if (1 == inet_pton (AF_INET6,
+                           dns_ip,
+                           &i6))
+  {
+    struct sockaddr_in6 *s6 = (struct sockaddr_in6 *) &ds->ss;
+
+    s6->sin6_family = AF_INET6;
+    s6->sin6_port = htons (53);
+    s6->sin6_addr = i6;
+#if HAVE_SOCKADDR_IN_SIN_LEN
+    s6->sin6_len = (u_char) sizeof (struct sockaddr_in6);
+#endif
+  }
+  else
+  {
+    GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+                "Malformed IP address `%s' for DNS server\n",
+                dns_ip);
+    GNUNET_free (ds);
+    return GNUNET_SYSERR;
+  }
+  GNUNET_CONTAINER_DLL_insert (ctx->dns_head,
+                               ctx->dns_tail,
+                               ds);
+  return GNUNET_OK;
+}
+
+
+/**
+ * Add nameserver for use by the DNSSTUB.  We will use
+ * all provided nameservers for resolution (round-robin).
+ *
+ * @param ctx resolver context to modify
+ * @param sa socket address of DNS resolver to use
+ * @return #GNUNET_OK on success
+ */
+int
+GNUNET_DNSSTUB_add_dns_sa (struct GNUNET_DNSSTUB_Context *ctx,
+                           const struct sockaddr *sa)
+{
+  struct DnsServer *ds;
+
+  ds = GNUNET_new (struct DnsServer);
+  switch (sa->sa_family)
+  {
+  case AF_INET:
+    GNUNET_memcpy (&ds->ss,
+                   sa,
+                   sizeof (struct sockaddr_in));
+    break;
+  case AF_INET6:
+    GNUNET_memcpy (&ds->ss,
+                   sa,
+                   sizeof (struct sockaddr_in6));
+    break;
+  default:
+    GNUNET_break (0);
+    GNUNET_free (ds);
+    return GNUNET_SYSERR;
+  }
+  GNUNET_CONTAINER_DLL_insert (ctx->dns_head,
+                               ctx->dns_tail,
+                               ds);
+  return GNUNET_OK;
+}
+
+
+/**
+ * How long should we try requests before timing out?
+ * Only effective for requests issued after this call.
+ *
+ * @param ctx resolver context to modify
+ * @param retry_freq how long to wait between retries
+ */
+void
+GNUNET_DNSSTUB_set_retry (struct GNUNET_DNSSTUB_Context *ctx,
+                          struct GNUNET_TIME_Relative retry_freq)
+{
+  ctx->retry_freq = retry_freq;
+}
+
+
+/**
+ * Cleanup DNSSTUB resolver.
+ *
+ * @param ctx stub resolver to clean up
+ */
+void
+GNUNET_DNSSTUB_stop (struct GNUNET_DNSSTUB_Context *ctx)
+{
+  struct DnsServer *ds;
+
+  while (NULL != (ds = ctx->dns_head))
+  {
+    GNUNET_CONTAINER_DLL_remove (ctx->dns_head,
+                                 ctx->dns_tail,
+                                 ds);
+    GNUNET_free (ds);
+  }
+  for (unsigned int i=0;i<ctx->num_sockets;i++)
+    cleanup_rs (&ctx->sockets[i]);
+  GNUNET_free (ctx->sockets);
+  GNUNET_free (ctx);
+}
+
+
+/* end of dnsstub.c */
diff --git a/src/dns/test_hexcoder.c b/src/dns/test_hexcoder.c
new file mode 100644 (file)
index 0000000..441d7e2
--- /dev/null
@@ -0,0 +1,54 @@
+/*
+     This file is part of GNUnet.
+     Copyright (C) 2014 GNUnet e.V.
+
+     GNUnet is free software: you can redistribute it and/or modify it
+     under the terms of the GNU Affero General Public License as published
+     by the Free Software Foundation, either version 3 of the License,
+     or (at your option) any later version.
+
+     GNUnet is distributed in the hope that it will be useful, but
+     WITHOUT ANY WARRANTY; without even the implied warranty of
+     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+     Affero General Public License for more details.
+    
+     You should have received a copy of the GNU Affero General Public License
+     along with this program.  If not, see <http://www.gnu.org/licenses/>.
+
+*/
+/**
+ * @author Christian Grothoff
+ * @file dns/test_hexcoder.c
+ * @brief test for #GNUNET_DNSPARSER_hex_to_bin() and
+ *                 #GNUNET_DNSPARSER_bin_to_hex()
+ */
+#include "platform.h"
+#include "gnunet_util_lib.h"
+#include "gnunet_dnsparser_lib.h"
+
+#define TESTSTRING "Hello World!"
+
+
+int
+main (int argc,
+      char *argv[])
+{
+  char buf[strlen (TESTSTRING)  + 1];
+  char *ret;
+
+  GNUNET_log_setup ("test-hexcoder", "WARNING", NULL);
+  ret = GNUNET_DNSPARSER_bin_to_hex (TESTSTRING,
+                                     strlen (TESTSTRING) + 1);
+  GNUNET_assert (NULL != ret);
+  GNUNET_assert (sizeof (buf) ==
+                 GNUNET_DNSPARSER_hex_to_bin (ret,
+                                              buf));
+  GNUNET_assert (0 == memcmp (TESTSTRING,
+                              buf,
+                              sizeof (buf)));
+  GNUNET_free (ret);
+  return 0;
+}
+
+
+/* end of test_hexcoder.c */
index ea4f08c73a4e4b36ddd8fc8048b23c6efc4a4778..aa1210269e025c856776f1f72e7e626f4a364dab 100644 (file)
@@ -49,8 +49,10 @@ endif
 gnunet_daemon_exit_SOURCES = \
  gnunet-daemon-exit.c exit.h
 gnunet_daemon_exit_LDADD = \
+  $(top_builddir)/src/dns/libgnunetdnsstub.la \
   $(top_builddir)/src/dht/libgnunetdht.la \
   $(top_builddir)/src/statistics/libgnunetstatistics.la \
+  $(top_builddir)/src/tun/libgnunettun.la \
   $(top_builddir)/src/util/libgnunetutil.la \
   $(top_builddir)/src/cadet/libgnunetcadet.la \
   $(top_builddir)/src/regex/libgnunetregex.la \
index 2c7bb8ebba6a7ae9115e04405c3a363759b75df3..46642113fac0518f639929e46c97187401451496 100644 (file)
@@ -102,6 +102,7 @@ libgnunet_plugin_gnsrecord_gns_la_SOURCES = \
   plugin_gnsrecord_gns.c
 libgnunet_plugin_gnsrecord_gns_la_LIBADD = \
   $(top_builddir)/src/gnsrecord/libgnunetgnsrecord.la \
+  $(top_builddir)/src/dns/libgnunetdnsparser.la \
   $(top_builddir)/src/util/libgnunetutil.la \
   $(LTLIBINTL)
 libgnunet_plugin_gnsrecord_gns_la_LDFLAGS = \
@@ -139,6 +140,8 @@ gnunet_dns2gns_LDADD = \
   libgnunetgns.la \
   $(top_builddir)/src/util/libgnunetutil.la \
   $(top_builddir)/src/identity/libgnunetidentity.la \
+  $(top_builddir)/src/dns/libgnunetdnsparser.la \
+  $(top_builddir)/src/dns/libgnunetdnsstub.la \
   $(GN_LIBINTL)
 
 if LINUX
@@ -203,7 +206,10 @@ gnunet_service_gns_LDADD = \
   $(top_builddir)/src/statistics/libgnunetstatistics.la \
   $(top_builddir)/src/util/libgnunetutil.la \
   $(top_builddir)/src/dns/libgnunetdns.la \
+  $(top_builddir)/src/dns/libgnunetdnsparser.la \
+  $(top_builddir)/src/dns/libgnunetdnsstub.la \
   $(top_builddir)/src/dht/libgnunetdht.la \
+  $(top_builddir)/src/tun/libgnunettun.la \
   $(top_builddir)/src/namecache/libgnunetnamecache.la \
   $(USE_VPN) \
   $(GN_LIBINTL)
index f840a31a47043ab1f4e181f43bc8472d8ac2f92d..c83aa33079197b3fdd5214ab212f27f456694c97 100644 (file)
@@ -38,6 +38,7 @@ libgnunetgnsrecord_la_SOURCES = \
   gnsrecord_crypto.c \
   gnsrecord_misc.c
 libgnunetgnsrecord_la_LIBADD = \
+  $(top_builddir)/src/dns/libgnunetdnsparser.la \
   $(top_builddir)/src/util/libgnunetutil.la \
   $(GN_LIBINTL)
 libgnunetgnsrecord_la_LDFLAGS = \
@@ -52,6 +53,7 @@ plugin_LTLIBRARIES = \
 libgnunet_plugin_gnsrecord_dns_la_SOURCES = \
   plugin_gnsrecord_dns.c
 libgnunet_plugin_gnsrecord_dns_la_LIBADD = \
+  $(top_builddir)/src/dns/libgnunetdnsparser.la \
   $(top_builddir)/src/util/libgnunetutil.la \
   $(LTLIBINTL)
 libgnunet_plugin_gnsrecord_dns_la_LDFLAGS = \
index 9b10c5c48d0ab3f12c892d9deb8ffd15fe7c0c82..8f1b90425aebfa8ab5c631d0695e0cb53771c0ff 100644 (file)
@@ -85,7 +85,7 @@ GNUNET_DNSSTUB_add_dns_sa (struct GNUNET_DNSSTUB_Context *ctx,
  * Only effective for requests issued after this call.
  *
  * @param ctx resolver context to modify
- * @param retry_freq how long to wait between retries
+ * @param retry_frequ how long to wait between retries
  */
 void
 GNUNET_DNSSTUB_set_retry (struct GNUNET_DNSSTUB_Context *ctx,
index 82f61a22b0fa534b55d96fd3be3aec3dc892e915..5e8eef1ad94270119241209266802e8a2698bb5e 100644 (file)
@@ -78,8 +78,6 @@ extern "C"
 #include "gnunet_service_lib.h"
 #include "gnunet_signal_lib.h"
 #include "gnunet_strings_lib.h"
-#include "gnunet_dnsparser_lib.h"
-#include "gnunet_dnsstub_lib.h"
 
 #if 0                           /* keep Emacsens' auto-indent happy */
 {
index 777e722c28b8e4be81bb285e2037fb2c002ac33b..fa85cc060dd3827b954b8f9a691c40637733f5ad 100644 (file)
@@ -147,6 +147,8 @@ gnunet_zoneimport_LDADD = \
   $(top_builddir)/src/statistics/libgnunetstatistics.la \
   $(top_builddir)/src/identity/libgnunetidentity.la \
   $(top_builddir)/src/gnsrecord/libgnunetgnsrecord.la \
+  $(top_builddir)/src/dns/libgnunetdnsparser.la \
+  $(top_builddir)/src/dns/libgnunetdnsstub.la \
   $(top_builddir)/src/util/libgnunetutil.la \
   $(GN_LIBINTL)
 
index 188387c0c342942d4d7886b39188ccaba06cd6d5..e36630ae44bdd4017826f7ea6b40337641de8d00 100644 (file)
@@ -28,6 +28,7 @@ gnunet_daemon_pt_LDADD = \
   $(top_builddir)/src/cadet/libgnunetcadet.la \
   $(top_builddir)/src/dht/libgnunetdht.la \
   $(top_builddir)/src/dns/libgnunetdns.la \
+  $(top_builddir)/src/dns/libgnunetdnsparser.la \
   $(top_builddir)/src/statistics/libgnunetstatistics.la \
   $(top_builddir)/src/util/libgnunetutil.la \
   $(GN_LIBINTL)
diff --git a/src/regex/.gitignore b/src/regex/.gitignore
new file mode 100644 (file)
index 0000000..39dc89c
--- /dev/null
@@ -0,0 +1,12 @@
+perf-regex
+gnunet-daemon-regexprofiler
+gnunet-regex-profiler
+gnunet-regex-simulation-profiler
+gnunet-service-regex
+test_graph.dot
+test_regex_api
+test_regex_eval_api
+test_regex_graph_api
+test_regex_integration
+test_regex_iterate_api
+test_regex_proofs
diff --git a/src/regex/Makefile.am b/src/regex/Makefile.am
new file mode 100644 (file)
index 0000000..80997db
--- /dev/null
@@ -0,0 +1,213 @@
+# This Makefile.am is in the public domain
+AM_CPPFLAGS = -I$(top_srcdir)/src/include
+
+if MINGW
+  WINFLAGS = -Wl,--no-undefined -Wl,--export-all-symbols
+endif
+
+if USE_COVERAGE
+  AM_CFLAGS = --coverage
+endif
+
+pkgcfgdir= $(pkgdatadir)/config.d/
+
+libexecdir= $(pkglibdir)/libexec/
+
+plugindir = $(libdir)/gnunet
+
+pkgcfg_DATA = \
+  regex.conf
+
+libexec_PROGRAMS = \
+  gnunet-service-regex \
+  gnunet-daemon-regexprofiler
+
+
+gnunet_service_regex_SOURCES =  \
+ gnunet-service-regex.c
+gnunet_service_regex_LDADD =  -lm \
+ libgnunetregex_internal.a \
+ libgnunetregexblock.la \
+ $(top_builddir)/src/dht/libgnunetdht.la \
+ $(top_builddir)/src/statistics/libgnunetstatistics.la \
+ $(top_builddir)/src/util/libgnunetutil.la \
+ $(GN_LIBINTL)
+
+noinst_LIBRARIES = \
+  libgnunetregex_internal.a \
+  libgnunetregextest.a
+
+lib_LTLIBRARIES = \
+  libgnunetregexblock.la \
+  libgnunetregex.la
+
+
+libgnunetregexblock_la_SOURCES = \
+  regex_block_lib.c regex_block_lib.h
+libgnunetregexblock_la_LIBADD = \
+  $(top_builddir)/src/util/libgnunetutil.la \
+  $(XLIB) \
+  $(LTLIBINTL)
+libgnunetregexblock_la_LDFLAGS = \
+  $(GN_LIB_LDFLAGS) $(WINFLAGS) \
+  -version-info 1:0:0
+
+
+libgnunetregex_internal_a_SOURCES = \
+  regex_internal_lib.h \
+  regex_internal.h regex_internal.c \
+  regex_internal_dht.c
+libgnunetregex_internal_a_DEPENDENCIES = \
+  libgnunetregexblock.la
+
+
+libgnunetregex_la_SOURCES = \
+  regex_api_announce.c \
+  regex_api_search.c \
+  regex_ipc.h
+libgnunetregex_la_LIBADD = \
+  $(top_builddir)/src/util/libgnunetutil.la
+libgnunetregex_la_LDFLAGS = \
+  $(GN_LIB_LDFLAGS) \
+   -version-info 3:1:0
+
+
+plugin_LTLIBRARIES = \
+ libgnunet_plugin_block_regex.la
+
+libgnunet_plugin_block_regex_la_SOURCES = \
+ plugin_block_regex.c
+libgnunet_plugin_block_regex_la_LIBADD = \
+ libgnunetregexblock.la \
+ $(top_builddir)/src/block/libgnunetblock.la \
+ $(top_builddir)/src/block/libgnunetblockgroup.la \
+ $(top_builddir)/src/util/libgnunetutil.la
+libgnunet_plugin_block_regex_la_LDFLAGS = \
+ $(GN_PLUGIN_LDFLAGS)
+
+if HAVE_MYSQL
+noinst_mysql_progs = \
+  gnunet-regex-simulation-profiler
+
+gnunet_regex_simulation_profiler_SOURCES = \
+  gnunet-regex-simulation-profiler.c
+gnunet_regex_simulation_profiler_LDADD = \
+  $(top_builddir)/src/util/libgnunetutil.la \
+  libgnunetregex_internal.a \
+  $(top_builddir)/src/dht/libgnunetdht.la \
+  $(top_builddir)/src/my/libgnunetmy.la \
+  $(top_builddir)/src/mysql/libgnunetmysql.la
+endif
+
+libgnunetregextest_a_SOURCES = \
+  regex_test_lib.c regex_test_lib.h \
+  regex_test_graph.c \
+  regex_test_random.c
+libgnunetregextest_a_LIBADD = \
+ $(top_builddir)/src/util/libgnunetutil.la \
+ libgnunetregex_internal.a
+
+if HAVE_TESTING
+noinst_PROGRAMS = $(noinst_mysql_progs) \
+  perf-regex \
+  gnunet-regex-profiler
+endif
+
+perf_regex_SOURCES = \
+  perf-regex.c
+perf_regex_LDADD = -lm \
+  libgnunetregex_internal.a \
+  $(top_builddir)/src/dht/libgnunetdht.la \
+  libgnunetregexblock.la \
+  libgnunetregextest.a \
+  $(top_builddir)/src/util/libgnunetutil.la
+
+gnunet_regex_profiler_SOURCES = \
+  gnunet-regex-profiler.c
+gnunet_regex_profiler_LDADD = -lm \
+  $(top_builddir)/src/arm/libgnunetarm.la \
+  $(top_builddir)/src/testbed/libgnunettestbed.la \
+  libgnunetregex_internal.a \
+  $(top_builddir)/src/dht/libgnunetdht.la \
+  libgnunetregexblock.la \
+  libgnunetregextest.a \
+  $(top_builddir)/src/statistics/libgnunetstatistics.la \
+  $(top_builddir)/src/util/libgnunetutil.la
+
+gnunet_daemon_regexprofiler_SOURCES = \
+  gnunet-daemon-regexprofiler.c
+gnunet_daemon_regexprofiler_LDADD = -lm \
+  libgnunetregex_internal.a \
+  $(top_builddir)/src/dht/libgnunetdht.la \
+  libgnunetregexblock.la \
+  libgnunetregextest.a \
+  $(top_builddir)/src/statistics/libgnunetstatistics.la \
+  $(top_builddir)/src/util/libgnunetutil.la
+
+check_PROGRAMS = \
+  test_regex_integration \
+  test_regex_eval_api \
+  test_regex_iterate_api \
+  test_regex_proofs \
+  test_regex_graph_api \
+  test_regex_api
+
+if ENABLE_TEST_RUN
+ AM_TESTS_ENVIRONMENT=export GNUNET_PREFIX=$${GNUNET_PREFIX:-@libdir@};export PATH=$${GNUNET_PREFIX:-@prefix@}/bin:$$PATH;unset XDG_DATA_HOME;unset XDG_CONFIG_HOME;
+ TESTS = $(check_PROGRAMS)
+endif
+
+test_regex_eval_api_SOURCES = \
+  test_regex_eval_api.c
+test_regex_eval_api_LDADD = -lm \
+  libgnunetregex_internal.a \
+  $(top_builddir)/src/dht/libgnunetdht.la \
+  libgnunetregextest.a \
+  libgnunetregexblock.la \
+  $(top_builddir)/src/util/libgnunetutil.la
+
+test_regex_integration_SOURCES = \
+  test_regex_integration.c
+test_regex_integration_LDADD = -lm \
+  libgnunetregex.la \
+  $(top_builddir)/src/testing/libgnunettesting.la \
+  $(top_builddir)/src/tun/libgnunettun.la \
+  $(top_builddir)/src/util/libgnunetutil.la
+
+test_regex_api_SOURCES = \
+  test_regex_api.c
+test_regex_api_LDADD = -lm \
+  libgnunetregex.la \
+  $(top_builddir)/src/testing/libgnunettesting.la \
+  $(top_builddir)/src/util/libgnunetutil.la
+
+test_regex_iterate_api_SOURCES = \
+  test_regex_iterate_api.c
+test_regex_iterate_api_LDADD = -lm \
+  libgnunetregex_internal.a \
+  libgnunetregexblock.la \
+  $(top_builddir)/src/dht/libgnunetdht.la \
+  $(top_builddir)/src/util/libgnunetutil.la
+
+test_regex_proofs_SOURCES = \
+  test_regex_proofs.c
+test_regex_proofs_LDADD = -lm \
+  libgnunetregex_internal.a \
+  $(top_builddir)/src/dht/libgnunetdht.la \
+  libgnunetregextest.a \
+  libgnunetregexblock.la \
+  $(top_builddir)/src/util/libgnunetutil.la
+
+test_regex_graph_api_SOURCES = \
+  test_regex_graph_api.c
+test_regex_graph_api_LDADD = -lm \
+  libgnunetregex_internal.a \
+  $(top_builddir)/src/dht/libgnunetdht.la \
+  libgnunetregextest.a \
+  libgnunetregexblock.la \
+  $(top_builddir)/src/util/libgnunetutil.la
+
+
+EXTRA_DIST = \
+  regex_simulation_profiler_test.conf \
+  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 (file)
index 0000000..11c2f51
--- /dev/null
@@ -0,0 +1,398 @@
+/*
+     This file is part of GNUnet.
+     Copyright (C) 2012, 2013 Christian Grothoff
+
+     GNUnet is free software: you can redistribute it and/or modify it
+     under the terms of the GNU Affero General Public License as published
+     by the Free Software Foundation, either version 3 of the License,
+     or (at your option) any later version.
+
+     GNUnet is distributed in the hope that it will be useful, but
+     WITHOUT ANY WARRANTY; without even the implied warranty of
+     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+     Affero General Public License for more details.
+    
+     You should have received a copy of the GNU Affero General Public License
+     along with this program.  If not, see <http://www.gnu.org/licenses/>.
+*/
+
+/**
+ * @file regex/gnunet-daemon-regexprofiler.c
+ * @brief daemon that uses cadet to announce a regular expression. Used in
+ * conjunction with gnunet-regex-profiler to announce regexes on serveral peers
+ * without the need to explicitly connect to the cadet service running on the
+ * peer from within the profiler.
+ * @author Maximilian Szengel
+ * @author Bartlomiej Polot
+ */
+#include "platform.h"
+#include "gnunet_util_lib.h"
+#include "regex_internal_lib.h"
+#include "regex_test_lib.h"
+#include "gnunet_dht_service.h"
+#include "gnunet_statistics_service.h"
+
+/**
+ * Return value from 'main'.
+ */
+static int global_ret;
+
+/**
+ * Configuration we use.
+ */
+static const struct GNUNET_CONFIGURATION_Handle *cfg;
+
+/**
+ * Handle to the statistics service.
+ */
+static struct GNUNET_STATISTICS_Handle *stats_handle;
+
+/**
+ * Peer's dht handle.
+ */
+static struct GNUNET_DHT_Handle *dht_handle;
+
+/**
+ * Peer's regex announce handle.
+ */
+static struct REGEX_INTERNAL_Announcement *announce_handle;
+
+/**
+ * Periodically reannounce regex.
+ */
+static struct GNUNET_SCHEDULER_Task * reannounce_task;
+
+/**
+ * What's the maximum reannounce period.
+ */
+static struct GNUNET_TIME_Relative reannounce_period_max;
+
+/**
+ * Maximal path compression length for regex announcing.
+ */
+static unsigned long long max_path_compression;
+
+/**
+ * Name of the file containing policies that this peer should announce. One
+ * policy per line.
+ */
+static char * policy_filename;
+
+/**
+ * Prefix to add before every regex we're announcing.
+ */
+static char * regex_prefix;
+
+/**
+ * Regex with prefix.
+ */
+static char *rx_with_pfx;
+
+/**
+ * How many put rounds should we do.
+ */
+static unsigned int rounds = 3;
+
+/**
+ * Private key for this peer.
+ */
+static struct GNUNET_CRYPTO_EddsaPrivateKey *my_private_key;
+
+
+
+/**
+ * Task run during shutdown.
+ *
+ * @param cls unused
+ */
+static void
+shutdown_task (void *cls)
+{
+  GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "shutting down\n");
+
+  if (NULL != announce_handle)
+  {
+    REGEX_INTERNAL_announce_cancel (announce_handle);
+    announce_handle = NULL;
+  }
+  if (NULL != reannounce_task)
+  {
+    GNUNET_free (GNUNET_SCHEDULER_cancel (reannounce_task));
+    reannounce_task = NULL;
+  }
+  if (NULL != dht_handle)
+  {
+    GNUNET_DHT_disconnect (dht_handle);
+    dht_handle = NULL;
+  }
+  GNUNET_free (my_private_key);
+  my_private_key = NULL;
+
+  GNUNET_log (GNUNET_ERROR_TYPE_INFO,
+              "Daemon for %s shutting down\n",
+              policy_filename);
+}
+
+
+/**
+ * Announce a previously announced regex re-using cached data.
+ *
+ * @param cls Closure (regex to announce if needed).
+ */
+static void
+reannounce_regex (void *cls)
+{
+  char *regex = cls;
+  struct GNUNET_TIME_Relative random_delay;
+
+  reannounce_task = NULL;
+  if (0 == rounds--)
+  {
+    global_ret = 0;
+    GNUNET_SCHEDULER_shutdown ();
+    GNUNET_free (regex);
+    return;
+  }
+  GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Announcing regex: %s\n", regex);
+  GNUNET_STATISTICS_update (stats_handle, "# regexes announced", 1, GNUNET_NO);
+  if (NULL == announce_handle && NULL != regex)
+  {
+    GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+                "First time, creating regex: %s\n",
+                regex);
+    announce_handle = REGEX_INTERNAL_announce (dht_handle,
+                                              my_private_key,
+                                              regex,
+                                              (unsigned int) max_path_compression,
+                                              stats_handle);
+  }
+  else
+  {
+    GNUNET_assert (NULL != announce_handle);
+    REGEX_INTERNAL_reannounce (announce_handle);
+  }
+
+  random_delay =
+    GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_MICROSECONDS,
+                                   GNUNET_CRYPTO_random_u32 (
+                                     GNUNET_CRYPTO_QUALITY_WEAK,
+                                     reannounce_period_max.rel_value_us));
+  reannounce_task = GNUNET_SCHEDULER_add_delayed (random_delay,
+                                                  &reannounce_regex, cls);
+}
+
+
+/**
+ * Announce the given regular expression using regex and the path compression
+ * length read from config.
+ *
+ * @param regex regular expression to announce on this peer's cadet.
+ */
+static void
+announce_regex (const char *regex)
+{
+  char *copy;
+
+  if (NULL == regex || 0 == strlen (regex))
+  {
+    GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Cannot announce empty regex\n");
+    return;
+  }
+
+  GNUNET_log (GNUNET_ERROR_TYPE_INFO,
+              "Daemon for %s starting\n",
+              policy_filename);
+  GNUNET_assert (NULL == reannounce_task);
+  copy = GNUNET_strdup (regex);
+  reannounce_task = GNUNET_SCHEDULER_add_now (&reannounce_regex,
+                                             (void *) copy);
+}
+
+
+/**
+ * Scan through the policy_dir looking for the n-th filename.
+ *
+ * @param cls Closure (target number n).
+ * @param filename complete filename (absolute path).
+ * @return GNUNET_OK to continue to iterate,
+ *  GNUNET_NO to stop when found
+ */
+static int
+scan (void *cls, const char *filename)
+{
+  long n = (long) cls;
+  static long c = 0;
+
+  if (c == n)
+  {
+    policy_filename = GNUNET_strdup (filename);
+    return GNUNET_NO;
+  }
+  c++;
+  return GNUNET_OK;
+}
+
+
+/**
+ * @brief Main function that will be run by the scheduler.
+ *
+ * @param cls closure
+ * @param args remaining command-line arguments
+ * @param cfgfile name of the configuration file used (for saving, can be NULL!)
+ * @param cfg_ configuration
+ */
+static void
+run (void *cls, char *const *args GNUNET_UNUSED,
+     const char *cfgfile GNUNET_UNUSED,
+     const struct GNUNET_CONFIGURATION_Handle *cfg_)
+{
+  char *regex = NULL;
+  char **components;
+  char *policy_dir;
+  long long unsigned int peer_id;
+
+  cfg = cfg_;
+
+  my_private_key = GNUNET_CRYPTO_eddsa_key_create_from_configuration (cfg);
+  GNUNET_assert (NULL != my_private_key);
+  if (GNUNET_OK !=
+      GNUNET_CONFIGURATION_get_value_number (cfg, "REGEXPROFILER",
+                                             "MAX_PATH_COMPRESSION",
+                                             &max_path_compression))
+  {
+    GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+                _
+                ("%s service is lacking key configuration settings (%s).  Exiting.\n"),
+                "regexprofiler", "max_path_compression");
+    global_ret = GNUNET_SYSERR;
+    GNUNET_SCHEDULER_shutdown ();
+    return;
+  }
+  if (GNUNET_OK !=
+      GNUNET_CONFIGURATION_get_value_string (cfg, "REGEXPROFILER",
+                                             "POLICY_DIR", &policy_dir))
+  {
+    GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR, "REGEXPROFILER", "POLICY_DIR");
+    global_ret = GNUNET_SYSERR;
+    GNUNET_SCHEDULER_shutdown ();
+    return;
+  }
+  if (GNUNET_OK !=
+      GNUNET_CONFIGURATION_get_value_number (cfg, "TESTBED",
+                                             "PEERID", &peer_id))
+  {
+    GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR, "TESTBED", "PEERID");
+    global_ret = GNUNET_SYSERR;
+    GNUNET_free (policy_dir);
+    GNUNET_SCHEDULER_shutdown ();
+    return;
+  }
+
+  if (GNUNET_OK !=
+      GNUNET_CONFIGURATION_get_value_string (cfg, "REGEXPROFILER",
+                                             "REGEX_PREFIX", &regex_prefix))
+  {
+    GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR, "REGEXPROFILER", "REGEX_PREFIX");
+    global_ret = GNUNET_SYSERR;
+    GNUNET_free (policy_dir);
+    GNUNET_SCHEDULER_shutdown ();
+    return;
+  }
+
+  if (GNUNET_OK !=
+      GNUNET_CONFIGURATION_get_value_time (cfg, "REGEXPROFILER",
+                                           "REANNOUNCE_PERIOD_MAX",
+                                           &reannounce_period_max))
+  {
+    GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+                "reannounce_period_max not given. Using 10 minutes.\n");
+    reannounce_period_max =
+      GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_MINUTES, 10);
+  }
+
+  stats_handle = GNUNET_STATISTICS_create ("regexprofiler", cfg);
+
+  dht_handle = GNUNET_DHT_connect (cfg, 1);
+
+  if (NULL == dht_handle)
+  {
+    GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+                "Could not acquire dht handle. Exiting.\n");
+    global_ret = GNUNET_SYSERR;
+    GNUNET_free (policy_dir);
+    GNUNET_SCHEDULER_shutdown ();
+    return;
+  }
+
+  /* Read regexes from policy files */
+  GNUNET_assert (-1 != GNUNET_DISK_directory_scan (policy_dir, &scan,
+                                                   (void *) (long) peer_id));
+  if (NULL == (components = REGEX_TEST_read_from_file (policy_filename)))
+  {
+    GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+                "Policy file %s contains no policies. Exiting.\n",
+                policy_filename);
+    global_ret = GNUNET_SYSERR;
+    GNUNET_free (policy_dir);
+    GNUNET_SCHEDULER_shutdown ();
+    return;
+  }
+  GNUNET_free (policy_dir);
+  regex = REGEX_TEST_combine (components, 16);
+  REGEX_TEST_free_from_file (components);
+
+  /* Announcing regexes from policy_filename */
+  GNUNET_asprintf (&rx_with_pfx,
+                  "%s(%s)(0|1|2|3|4|5|6|7|8|9|a|b|c|d|e|f)*",
+                  regex_prefix,
+                  regex);
+  announce_regex (rx_with_pfx);
+  GNUNET_free (regex);
+  GNUNET_free (rx_with_pfx);
+
+  /* Scheduled the task to clean up when shutdown is called */
+  GNUNET_SCHEDULER_add_shutdown (&shutdown_task,
+                                NULL);
+}
+
+
+/**
+ * The main function of the regexprofiler service.
+ *
+ * @param argc number of arguments from the command line
+ * @param argv command line arguments
+ * @return 0 ok, 1 on error
+ */
+int
+main (int argc, char *const *argv)
+{
+  static const struct GNUNET_GETOPT_CommandLineOption options[] = {
+    GNUNET_GETOPT_OPTION_END
+  };
+
+  if (GNUNET_OK != GNUNET_STRINGS_get_utf8_args (argc, argv, &argc, &argv))
+    return 2;
+  return (GNUNET_OK ==
+          GNUNET_PROGRAM_run (argc, argv, "regexprofiler",
+                              gettext_noop
+                              ("Daemon to announce regular expressions for the peer using cadet."),
+                              options, &run, NULL)) ? global_ret : 1;
+}
+
+
+#if defined(LINUX) && defined(__GLIBC__)
+#include <malloc.h>
+
+/**
+ * MINIMIZE heap size (way below 128k) since this process doesn't need much.
+ */
+void __attribute__ ((constructor)) GNUNET_ARM_memory_init ()
+{
+  mallopt (M_TRIM_THRESHOLD, 4 * 1024);
+  mallopt (M_TOP_PAD, 1 * 1024);
+  malloc_trim (0);
+}
+#endif
+
+
+/* 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 (file)
index 0000000..f65ba3d
--- /dev/null
@@ -0,0 +1,1600 @@
+/*
+     This file is part of GNUnet.
+     Copyright (C) 2011 - 2017 GNUnet e.V.
+
+     GNUnet is free software: you can redistribute it and/or modify it
+     under the terms of the GNU Affero General Public License as published
+     by the Free Software Foundation, either version 3 of the License,
+     or (at your option) any later version.
+
+     GNUnet is distributed in the hope that it will be useful, but
+     WITHOUT ANY WARRANTY; without even the implied warranty of
+     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+     Affero General Public License for more details.
+    
+     You should have received a copy of the GNU Affero General Public License
+     along with this program.  If not, see <http://www.gnu.org/licenses/>.
+*/
+
+/**
+ * @file regex/gnunet-regex-profiler.c
+ * @brief Regex profiler for testing distributed regex use.
+ * @author Bartlomiej Polot
+ * @author Maximilian Szengel
+ *
+ */
+
+#include <string.h>
+
+#include "platform.h"
+#include "gnunet_applications.h"
+#include "gnunet_util_lib.h"
+#include "regex_internal_lib.h"
+#include "gnunet_arm_service.h"
+#include "gnunet_dht_service.h"
+#include "gnunet_testbed_service.h"
+
+#define FIND_TIMEOUT \
+        GNUNET_TIME_relative_multiply(GNUNET_TIME_UNIT_SECONDS, 90)
+
+/**
+ * DLL of operations
+ */
+struct DLLOperation
+{
+  /**
+   * The testbed operation handle
+   */
+  struct GNUNET_TESTBED_Operation *op;
+
+  /**
+   * Closure
+   */
+  void *cls;
+
+  /**
+   * The next pointer for DLL
+   */
+  struct DLLOperation *next;
+
+  /**
+   * The prev pointer for DLL
+   */
+  struct DLLOperation *prev;
+};
+
+
+/**
+ * Available states during profiling
+ */
+enum State
+{
+  /**
+   * Initial state
+   */
+  STATE_INIT = 0,
+
+  /**
+   * Starting slaves
+   */
+  STATE_SLAVES_STARTING,
+
+  /**
+   * Creating peers
+   */
+  STATE_PEERS_CREATING,
+
+  /**
+   * Starting peers
+   */
+  STATE_PEERS_STARTING,
+
+  /**
+   * Linking peers
+   */
+  STATE_PEERS_LINKING,
+
+  /**
+   * Matching strings against announced regexes
+   */
+  STATE_SEARCH_REGEX,
+
+  /**
+   * Destroying peers; we can do this as the controller takes care of stopping a
+   * peer if it is running
+   */
+  STATE_PEERS_DESTROYING
+};
+
+
+/**
+ * Peer handles.
+ */
+struct RegexPeer
+{
+  /**
+   * Peer id.
+   */
+  unsigned int id;
+
+  /**
+   * Peer configuration handle.
+   */
+  struct GNUNET_CONFIGURATION_Handle *cfg;
+
+  /**
+   * The actual testbed peer handle.
+   */
+  struct GNUNET_TESTBED_Peer *peer_handle;
+
+  /**
+   * Peer's search string.
+   */
+  const char *search_str;
+
+  /**
+   * Set to GNUNET_YES if the peer successfully matched the above
+   * search string. GNUNET_NO if the string could not be matched
+   * during the profiler run. GNUNET_SYSERR if the string matching
+   * timed out. Undefined if search_str is NULL
+   */
+  int search_str_matched;
+
+  /**
+   * Peer's DHT handle.
+   */
+  struct GNUNET_DHT_Handle *dht_handle;
+
+  /**
+   * Handle to a running regex search.
+   */
+   struct REGEX_INTERNAL_Search *search_handle;
+
+  /**
+   * Testbed operation handle for DHT.
+   */
+  struct GNUNET_TESTBED_Operation *op_handle;
+
+  /**
+   * Peers's statistics handle.
+   */
+  struct GNUNET_STATISTICS_Handle *stats_handle;
+
+  /**
+   * The starting time of a profiling step.
+   */
+  struct GNUNET_TIME_Absolute prof_start_time;
+
+  /**
+   * Operation timeout
+   */
+  struct GNUNET_SCHEDULER_Task * timeout;
+
+  /**
+   * Deamon start
+   */
+  struct GNUNET_TESTBED_Operation *daemon_op;
+};
+
+/**
+ * Set when shutting down to avoid making more queries.
+ */
+static int in_shutdown;
+
+/**
+ * The array of peers; we fill this as the peers are given to us by the testbed
+ */
+static struct RegexPeer *peers;
+
+/**
+ * Host registration handle
+ */
+static struct GNUNET_TESTBED_HostRegistrationHandle *reg_handle;
+
+/**
+ * Handle to the master controller process
+ */
+static struct GNUNET_TESTBED_ControllerProc *mc_proc;
+
+/**
+ * Handle to the master controller
+ */
+static struct GNUNET_TESTBED_Controller *mc;
+
+/**
+ * Handle to global configuration
+ */
+static struct GNUNET_CONFIGURATION_Handle *cfg;
+
+/**
+ * Abort task identifier
+ */
+static struct GNUNET_SCHEDULER_Task * abort_task;
+
+/**
+ * Host registration task identifier
+ */
+static struct GNUNET_SCHEDULER_Task * register_hosts_task;
+
+/**
+ * Global event mask for all testbed events
+ */
+static uint64_t event_mask;
+
+/**
+ * The starting time of a profiling step
+ */
+static struct GNUNET_TIME_Absolute prof_start_time;
+
+/**
+ * Duration profiling step has taken
+ */
+static struct GNUNET_TIME_Relative prof_time;
+
+/**
+ * Number of peers to be started by the profiler
+ */
+static unsigned int num_peers;
+
+/**
+ * Global testing status
+ */
+static int result;
+
+/**
+ * current state of profiling
+ */
+enum State state;
+
+/**
+ * Folder where policy files are stored.
+ */
+static char * policy_dir;
+
+/**
+ * File with hostnames where to execute the test.
+ */
+static char *hosts_file;
+
+/**
+ * File with the strings to look for.
+ */
+static char *strings_file;
+
+/**
+ * Search strings (num_peers of them).
+ */
+static char **search_strings;
+
+/**
+ * How many searches are we going to start in parallel
+ */
+static long long unsigned int init_parallel_searches;
+
+/**
+ * How many searches are running in parallel
+ */
+static unsigned int parallel_searches;
+
+/**
+ * Number of strings found in the published regexes.
+ */
+static unsigned int strings_found;
+
+/**
+ * Index of peer to start next announce/search.
+ */
+static unsigned int next_search;
+
+/**
+ * Search timeout task identifier.
+ */
+static struct GNUNET_SCHEDULER_Task * search_timeout_task;
+
+/**
+ * Search timeout in seconds.
+ */
+static struct GNUNET_TIME_Relative search_timeout_time = { 60000 };
+
+/**
+ * File to log statistics to.
+ */
+static struct GNUNET_DISK_FileHandle *data_file;
+
+/**
+ * Filename to log statistics to.
+ */
+static char *data_filename;
+
+/**
+ * Prefix used for regex announcing. We need to prefix the search
+ * strings with it, in order to find something.
+ */
+static char * regex_prefix;
+
+/**
+ * What's the maximum regex reannounce period.
+ */
+static struct GNUNET_TIME_Relative reannounce_period_max;
+
+
+/******************************************************************************/
+/******************************  DECLARATIONS  ********************************/
+/******************************************************************************/
+
+/**
+ * DHT connect callback.
+ *
+ * @param cls internal peer id.
+ * @param op operation handle.
+ * @param ca_result connect adapter result.
+ * @param emsg error message.
+ */
+static void
+dht_connect_cb (void *cls, struct GNUNET_TESTBED_Operation *op,
+                void *ca_result, const char *emsg);
+
+/**
+ * DHT connect adapter.
+ *
+ * @param cls not used.
+ * @param cfg configuration handle.
+ *
+ * @return
+ */
+static void *
+dht_ca (void *cls, const struct GNUNET_CONFIGURATION_Handle *cfg);
+
+
+/**
+ * Adapter function called to destroy a connection to
+ * the DHT service
+ *
+ * @param cls closure
+ * @param op_result service handle returned from the connect adapter
+ */
+static void
+dht_da (void *cls, void *op_result);
+
+
+/**
+ * Function called by testbed once we are connected to stats
+ * service. Get the statistics for the services of interest.
+ *
+ * @param cls the 'struct RegexPeer' for which we connected to stats
+ * @param op connect operation handle
+ * @param ca_result handle to stats service
+ * @param emsg error message on failure
+ */
+static void
+stats_connect_cb (void *cls,
+                  struct GNUNET_TESTBED_Operation *op,
+                  void *ca_result,
+                  const char *emsg);
+
+
+/**
+ * Start announcing the next regex in the DHT.
+ *
+ * @param cls Index of the next peer in the peers array.
+ */
+static void
+announce_next_regex (void *cls);
+
+
+/******************************************************************************/
+/********************************  SHUTDOWN  **********************************/
+/******************************************************************************/
+
+
+/**
+ * Shutdown nicely
+ *
+ * @param cls NULL
+ */
+static void
+do_shutdown (void *cls)
+{
+  struct RegexPeer *peer;
+  unsigned int peer_cnt;
+  unsigned int search_str_cnt;
+  char output_buffer[512];
+  size_t size;
+
+  if (NULL != abort_task)
+  {
+    GNUNET_SCHEDULER_cancel (abort_task);
+    abort_task = NULL;
+  }
+  if (NULL != register_hosts_task)
+  {
+    GNUNET_SCHEDULER_cancel (register_hosts_task);
+    register_hosts_task = NULL;
+  }
+  for (peer_cnt = 0; peer_cnt < num_peers; peer_cnt++)
+  {
+    peer = &peers[peer_cnt];
+
+    if (GNUNET_YES != peer->search_str_matched && NULL != data_file)
+    {
+      prof_time = GNUNET_TIME_absolute_get_duration (peer->prof_start_time);
+      size =
+        GNUNET_snprintf (output_buffer,
+                         sizeof (output_buffer),
+                         "%p Search string not found: %s (%d)\n"
+                         "%p On peer: %u (%p)\n"
+                         "%p After: %s\n",
+                         peer, peer->search_str, peer->search_str_matched,
+                         peer, peer->id, peer,
+                         peer,
+                         GNUNET_STRINGS_relative_time_to_string (prof_time,
+                                                                 GNUNET_NO));
+      if (size != GNUNET_DISK_file_write (data_file, output_buffer, size))
+        GNUNET_log (GNUNET_ERROR_TYPE_WARNING, "Unable to write to file!\n");
+    }
+
+    if (NULL != peers[peer_cnt].op_handle)
+      GNUNET_TESTBED_operation_done (peers[peer_cnt].op_handle);
+  }
+
+  if (NULL != data_file)
+  {
+    GNUNET_DISK_file_close (data_file);
+    data_file = NULL;
+  }
+  for (search_str_cnt = 0;
+       search_str_cnt < num_peers && NULL != search_strings;
+       search_str_cnt++)
+  {
+    GNUNET_free_non_null (search_strings[search_str_cnt]);
+  }
+  GNUNET_free_non_null (search_strings);
+  search_strings = NULL;
+
+  if (NULL != reg_handle)
+  {
+    GNUNET_TESTBED_cancel_registration (reg_handle);
+    reg_handle = NULL;
+  }
+  if (NULL != mc)
+  {
+    GNUNET_TESTBED_controller_disconnect (mc);
+    mc = NULL;
+  }
+  if (NULL != mc_proc)
+  {
+    GNUNET_TESTBED_controller_stop (mc_proc);
+    mc_proc = NULL;
+  }
+  if (NULL != cfg)
+  {
+    GNUNET_CONFIGURATION_destroy (cfg);
+    cfg = NULL;
+  }
+}
+
+
+/**
+ * abort task to run on test timed out
+ *
+ * @param cls NULL
+ */
+static void
+do_abort (void *cls)
+{
+  unsigned long i = (unsigned long) cls;
+
+  GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
+             "Aborting from line %lu...\n", i);
+  abort_task = NULL;
+  result = GNUNET_SYSERR;
+  GNUNET_SCHEDULER_add_now (&do_shutdown, NULL);
+}
+
+
+/******************************************************************************/
+/*********************  STATISTICS SERVICE CONNECTIONS  ***********************/
+/******************************************************************************/
+
+/**
+ * Adapter function called to establish a connection to
+ * statistics service.
+ *
+ * @param cls closure
+ * @param cfg configuration of the peer to connect to; will be available until
+ *          GNUNET_TESTBED_operation_done() is called on the operation returned
+ *          from GNUNET_TESTBED_service_connect()
+ * @return service handle to return in 'op_result', NULL on error
+ */
+static void *
+stats_ca (void *cls,
+         const struct GNUNET_CONFIGURATION_Handle *cfg)
+{
+  return GNUNET_STATISTICS_create ("<driver>", cfg);
+}
+
+
+/**
+ * Adapter function called to destroy a connection to
+ * statistics service.
+ *
+ * @param cls closure
+ * @param op_result service handle returned from the connect adapter
+ */
+static void
+stats_da (void *cls, void *op_result)
+{
+  struct RegexPeer *peer = cls;
+
+  GNUNET_assert (op_result == peer->stats_handle);
+
+  GNUNET_STATISTICS_destroy (peer->stats_handle, GNUNET_NO);
+  peer->stats_handle = NULL;
+}
+
+
+/**
+ * Process statistic values. Write all values to global 'data_file', if present.
+ *
+ * @param cls closure
+ * @param subsystem name of subsystem that created the statistic
+ * @param name the name of the datum
+ * @param value the current value
+ * @param is_persistent GNUNET_YES if the value is persistent, GNUNET_NO if not
+ * @return #GNUNET_OK to continue, #GNUNET_SYSERR to abort iteration
+ */
+static int
+stats_iterator (void *cls,
+               const char *subsystem,
+               const char *name,
+                uint64_t value, int is_persistent)
+{
+  struct RegexPeer *peer = cls;
+  char output_buffer[512];
+  size_t size;
+
+  if (NULL == data_file)
+  {
+    GNUNET_log (GNUNET_ERROR_TYPE_INFO,
+                "%p -> %s [%s]: %llu\n",
+                peer,
+                subsystem,
+                name,
+                (unsigned long long) value);
+    return GNUNET_OK;
+  }
+  size =
+    GNUNET_snprintf (output_buffer,
+                     sizeof (output_buffer),
+                     "%p [%s] %llu %s\n",
+                     peer,
+                     subsystem, value, name);
+  if (size != GNUNET_DISK_file_write (data_file, output_buffer, size))
+    GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
+                "Unable to write to file!\n");
+
+  return GNUNET_OK;
+}
+
+
+/**
+ * Stats callback. Finish the stats testbed operation and when all stats have
+ * been iterated, shutdown the profiler.
+ *
+ * @param cls closure
+ * @param success GNUNET_OK if statistics were
+ *        successfully obtained, GNUNET_SYSERR if not.
+ */
+static void
+stats_cb (void *cls,
+          int success)
+{
+  static unsigned int peer_cnt;
+  struct RegexPeer *peer = cls;
+
+  if (GNUNET_OK != success)
+  {
+    GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+                "Getting statistics for peer %u failed!\n",
+                peer->id);
+    return;
+  }
+
+  GNUNET_assert (NULL != peer->op_handle);
+
+  GNUNET_TESTBED_operation_done (peer->op_handle);
+  peer->op_handle = NULL;
+
+  peer_cnt++;
+  peer = &peers[peer_cnt];
+
+  fprintf (stderr, "s");
+  if (peer_cnt == num_peers)
+  {
+    GNUNET_log (GNUNET_ERROR_TYPE_INFO,
+               "\nCollecting stats finished. Shutting down.\n");
+    GNUNET_SCHEDULER_shutdown ();
+    result = GNUNET_OK;
+  }
+  else
+  {
+    peer->op_handle =
+      GNUNET_TESTBED_service_connect (NULL,
+                                      peer->peer_handle,
+                                      "statistics",
+                                      &stats_connect_cb,
+                                      peer,
+                                      &stats_ca,
+                                      &stats_da,
+                                      peer);
+  }
+}
+
+
+/**
+ * Function called by testbed once we are connected to stats
+ * service. Get the statistics for the services of interest.
+ *
+ * @param cls the 'struct RegexPeer' for which we connected to stats
+ * @param op connect operation handle
+ * @param ca_result handle to stats service
+ * @param emsg error message on failure
+ */
+static void
+stats_connect_cb (void *cls,
+                  struct GNUNET_TESTBED_Operation *op,
+                  void *ca_result,
+                  const char *emsg)
+{
+  struct RegexPeer *peer = cls;
+
+  if (NULL == ca_result || NULL != emsg)
+  {
+    GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+                "Failed to connect to statistics service on peer %u: %s\n",
+                peer->id, emsg);
+
+    peer->stats_handle = NULL;
+    return;
+  }
+
+  peer->stats_handle = ca_result;
+
+  if (NULL == GNUNET_STATISTICS_get (peer->stats_handle, NULL, NULL,
+                                     &stats_cb,
+                                     &stats_iterator, peer))
+  {
+    GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+                "Could not get statistics of peer %u!\n", peer->id);
+  }
+}
+
+
+/**
+ * Task to collect all statistics from all peers, will shutdown the
+ * profiler, when done.
+ *
+ * @param cls NULL
+ */
+static void
+do_collect_stats (void *cls)
+{
+  struct RegexPeer *peer = &peers[0];
+
+  GNUNET_assert (NULL != peer->peer_handle);
+
+  peer->op_handle =
+    GNUNET_TESTBED_service_connect (NULL,
+                                    peer->peer_handle,
+                                    "statistics",
+                                    &stats_connect_cb,
+                                    peer,
+                                    &stats_ca,
+                                    &stats_da,
+                                    peer);
+}
+
+
+/******************************************************************************/
+/************************   REGEX FIND CONNECTIONS   **************************/
+/******************************************************************************/
+
+
+/**
+ * Start searching for the next string in the DHT.
+ *
+ * @param cls Index of the next peer in the peers array.
+ */
+static void
+find_string (void *cls);
+
+
+/**
+ * Method called when we've found a peer that announced a regex
+ * that matches our search string. Now get the statistics.
+ *
+ * @param cls Closure provided in REGEX_INTERNAL_search.
+ * @param id Peer providing a regex that matches the string.
+ * @param get_path Path of the get request.
+ * @param get_path_length Lenght of get_path.
+ * @param put_path Path of the put request.
+ * @param put_path_length Length of the put_path.
+ */
+static void
+regex_found_handler (void *cls,
+                     const struct GNUNET_PeerIdentity *id,
+                     const struct GNUNET_PeerIdentity *get_path,
+                     unsigned int get_path_length,
+                     const struct GNUNET_PeerIdentity *put_path,
+                     unsigned int put_path_length)
+{
+  struct RegexPeer *peer = cls;
+  char output_buffer[512];
+  size_t size;
+
+  if (GNUNET_YES == peer->search_str_matched)
+  {
+    GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+                "String %s on peer %u already matched!\n",
+                peer->search_str, peer->id);
+    return;
+  }
+
+  strings_found++;
+  parallel_searches--;
+
+  if (NULL != peer->timeout)
+  {
+    GNUNET_SCHEDULER_cancel (peer->timeout);
+    peer->timeout = NULL;
+    if (GNUNET_NO == in_shutdown)
+      GNUNET_SCHEDULER_add_now (&announce_next_regex, NULL);
+  }
+
+  if (NULL == id)
+  {
+    // FIXME not possible right now
+    GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
+                "String matching timed out for string %s on peer %u (%i/%i)\n",
+                peer->search_str, peer->id, strings_found, num_peers);
+    peer->search_str_matched = GNUNET_SYSERR;
+  }
+  else
+  {
+    prof_time = GNUNET_TIME_absolute_get_duration (peer->prof_start_time);
+
+    GNUNET_log (GNUNET_ERROR_TYPE_INFO,
+                "String %s found on peer %u after %s (%i/%i) (%u||)\n",
+                peer->search_str, peer->id,
+                GNUNET_STRINGS_relative_time_to_string (prof_time, GNUNET_NO),
+                strings_found, num_peers, parallel_searches);
+
+    peer->search_str_matched = GNUNET_YES;
+
+    if (NULL != data_file)
+    {
+      size =
+        GNUNET_snprintf (output_buffer,
+                         sizeof (output_buffer),
+                         "%p Peer: %u\n"
+                         "%p Search string: %s\n"
+                         "%p Search duration: %s\n\n",
+                         peer, peer->id,
+                         peer, peer->search_str,
+                         peer,
+                         GNUNET_STRINGS_relative_time_to_string (prof_time,
+                                                                 GNUNET_NO));
+
+      if (size != GNUNET_DISK_file_write (data_file, output_buffer, size))
+        GNUNET_log (GNUNET_ERROR_TYPE_WARNING, "Unable to write to file!\n");
+    }
+  }
+
+  GNUNET_TESTBED_operation_done (peer->op_handle);
+  peer->op_handle = NULL;
+
+  if (strings_found == num_peers)
+  {
+    prof_time = GNUNET_TIME_absolute_get_duration (prof_start_time);
+    GNUNET_log (GNUNET_ERROR_TYPE_INFO,
+                "All strings successfully matched in %s\n",
+                GNUNET_STRINGS_relative_time_to_string (prof_time, GNUNET_NO));
+
+    if (NULL != search_timeout_task)
+    {
+      GNUNET_SCHEDULER_cancel (search_timeout_task);
+      search_timeout_task = NULL;
+    }
+
+    GNUNET_log (GNUNET_ERROR_TYPE_INFO, "Collecting stats.\n");
+    GNUNET_SCHEDULER_add_now (&do_collect_stats, NULL);
+  }
+}
+
+
+/**
+ * Connect by string timeout task. This will cancel the profiler after the
+ * specified timeout 'search_timeout'.
+ *
+ * @param cls NULL
+ */
+static void
+search_timed_out (void *cls)
+{
+  unsigned int i;
+
+  GNUNET_log (GNUNET_ERROR_TYPE_INFO,
+              "Finding matches to all strings did not succeed after %s.\n",
+              GNUNET_STRINGS_relative_time_to_string (search_timeout_time,
+                                                      GNUNET_NO));
+  GNUNET_log (GNUNET_ERROR_TYPE_INFO,
+              "Found %i of %i strings\n", strings_found, num_peers);
+
+  GNUNET_log (GNUNET_ERROR_TYPE_INFO,
+              "Search timed out after %s."
+              "Collecting stats and shutting down.\n",
+              GNUNET_STRINGS_relative_time_to_string (search_timeout_time,
+                                                      GNUNET_NO));
+
+  in_shutdown = GNUNET_YES;
+  for (i = 0; i < num_peers; i++)
+  {
+    if (NULL != peers[i].op_handle)
+    {
+      GNUNET_TESTBED_operation_done (peers[i].op_handle);
+      peers[i].op_handle = NULL;
+    }
+  }
+  GNUNET_SCHEDULER_add_now (&do_collect_stats, NULL);
+}
+
+
+/**
+ * Search timed out. It might still complete in the future,
+ * but we should start another one.
+ *
+ * @param cls Index of the next peer in the peers array.
+ */
+static void
+find_timed_out (void *cls)
+{
+  struct RegexPeer *p = cls;
+
+  p->timeout = NULL;
+  GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
+              "Searching for string \"%s\" on peer %d timed out.\n",
+              p->search_str,
+              p->id);
+  if (GNUNET_NO == in_shutdown)
+    GNUNET_SCHEDULER_add_now (&announce_next_regex, NULL);
+}
+
+
+/**
+ * Start searching for a string in the DHT.
+ *
+ * @param cls Index of the next peer in the peers array.
+ */
+static void
+find_string (void *cls)
+{
+  unsigned int search_peer = (unsigned int) (long) cls;
+
+  if ( (search_peer >= num_peers) ||
+       (GNUNET_YES == in_shutdown) )
+    return;
+
+  GNUNET_log (GNUNET_ERROR_TYPE_INFO,
+              "Searching for string \"%s\" on peer %d (%u||)\n",
+              peers[search_peer].search_str,
+              search_peer,
+              parallel_searches);
+
+  peers[search_peer].op_handle =
+    GNUNET_TESTBED_service_connect (NULL,
+                                    peers[search_peer].peer_handle,
+                                    "dht",
+                                    &dht_connect_cb,
+                                    &peers[search_peer],
+                                    &dht_ca,
+                                    &dht_da,
+                                    &peers[search_peer]);
+  GNUNET_assert (NULL != peers[search_peer].op_handle);
+  peers[search_peer].timeout
+    = GNUNET_SCHEDULER_add_delayed (FIND_TIMEOUT,
+                                   &find_timed_out,
+                                   &peers[search_peer]);
+}
+
+
+/**
+ * Callback called when testbed has started the daemon we asked for.
+ *
+ * @param cls NULL
+ * @param op the operation handle
+ * @param emsg NULL on success; otherwise an error description
+ */
+static void
+daemon_started (void *cls,
+               struct GNUNET_TESTBED_Operation *op,
+                const char *emsg)
+{
+  struct RegexPeer *peer = (struct RegexPeer *) cls;
+  unsigned long search_peer;
+  unsigned int i;
+
+  GNUNET_TESTBED_operation_done (peer->daemon_op);
+  peer->daemon_op = NULL;
+  if (NULL != emsg)
+  {
+    GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
+                "Failed to start/stop daemon at peer %u: %s\n", peer->id, emsg);
+    GNUNET_assert (0);
+  }
+  else
+  {
+    GNUNET_log (GNUNET_ERROR_TYPE_INFO,
+                "Deamon %u started successfully\n", peer->id);
+  }
+
+  /* Find a peer to look for a string matching the regex announced */
+  search_peer = GNUNET_CRYPTO_random_u32 (GNUNET_CRYPTO_QUALITY_WEAK,
+                                          num_peers);
+  for (i = 0; peers[search_peer].search_str != NULL; i++)
+  {
+    search_peer = (search_peer + 1) % num_peers;
+    if (i > num_peers)
+      GNUNET_assert (0); /* we ran out of peers, must be a bug */
+  }
+  peers[search_peer].search_str = search_strings[peer->id];
+  peers[search_peer].search_str_matched = GNUNET_NO;
+  GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_relative_saturating_multiply(
+                                  reannounce_period_max,
+                                  2),
+                                &find_string,
+                                (void *) search_peer);
+}
+
+
+/**
+ * Task to start the daemons on each peer so that the regexes are announced
+ * into the DHT.
+ *
+ * @param cls NULL
+ * @param tc the task context
+ */
+static void
+do_announce (void *cls)
+{
+  unsigned int i;
+
+  if (GNUNET_YES == in_shutdown)
+    return;
+  GNUNET_log (GNUNET_ERROR_TYPE_INFO,
+             "Starting announce.\n");
+  for (i = 0; i < init_parallel_searches; i++)
+  {
+    GNUNET_log (GNUNET_ERROR_TYPE_INFO,
+                "  scheduling announce %u\n",
+                i);
+    (void) GNUNET_SCHEDULER_add_now (&announce_next_regex, NULL);
+  }
+}
+
+
+/**
+ * Start announcing the next regex in the DHT.
+ *
+ * @param cls Closure (unused).
+ */
+static void
+announce_next_regex (void *cls)
+{
+  struct RegexPeer *peer;
+
+  if (GNUNET_YES == in_shutdown)
+    return;
+  if (next_search >= num_peers)
+  {
+    if (strings_found != num_peers)
+    {
+      struct GNUNET_TIME_Relative new_delay;
+      if (NULL != search_timeout_task)
+        GNUNET_SCHEDULER_cancel (search_timeout_task);
+      new_delay = GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_MINUTES, 15);
+      search_timeout_task = GNUNET_SCHEDULER_add_delayed (new_delay,
+                                                          &search_timed_out,
+                                                          NULL);
+    }
+    return;
+  }
+
+  GNUNET_log (GNUNET_ERROR_TYPE_INFO, "Starting daemon %u\n", next_search);
+  peer = &peers[next_search];
+  peer->daemon_op =
+  GNUNET_TESTBED_peer_manage_service (NULL,
+                                      peer->peer_handle,
+                                      "regexprofiler",
+                                      &daemon_started,
+                                      peer,
+                                      1);
+  next_search++;
+  parallel_searches++;
+}
+
+
+/**
+ * DHT connect callback. Called when we are connected to the dht service for
+ * the peer in 'cls'. If successfull we connect to the stats service of this
+ * peer and then try to match the search string of this peer.
+ *
+ * @param cls internal peer id.
+ * @param op operation handle.
+ * @param ca_result connect adapter result.
+ * @param emsg error message.
+ */
+static void
+dht_connect_cb (void *cls,
+               struct GNUNET_TESTBED_Operation *op,
+                void *ca_result,
+               const char *emsg)
+{
+  struct RegexPeer *peer = (struct RegexPeer *) cls;
+
+  if (NULL != emsg || NULL == op || NULL == ca_result)
+  {
+    GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "DHT connect failed: %s\n", emsg);
+    GNUNET_assert (0);
+  }
+
+  GNUNET_assert (NULL != peer->dht_handle);
+  GNUNET_assert (peer->op_handle == op);
+  GNUNET_assert (peer->dht_handle == ca_result);
+
+  peer->search_str_matched = GNUNET_NO;
+  peer->search_handle = REGEX_INTERNAL_search (peer->dht_handle,
+                                             peer->search_str,
+                                             &regex_found_handler, peer,
+                                             NULL);
+  peer->prof_start_time = GNUNET_TIME_absolute_get ();
+}
+
+
+/**
+ * DHT connect adapter. Opens a connection to the dht service.
+ *
+ * @param cls Closure (peer).
+ * @param cfg Configuration handle.
+ *
+ * @return
+ */
+static void *
+dht_ca (void *cls, const struct GNUNET_CONFIGURATION_Handle *cfg)
+{
+  struct RegexPeer *peer = cls;
+
+  peer->dht_handle = GNUNET_DHT_connect (cfg, 32);
+
+  return peer->dht_handle;
+}
+
+
+/**
+ * Adapter function called to destroy a connection to the dht service.
+ *
+ * @param cls Closure (peer).
+ * @param op_result Service handle returned from the connect adapter.
+ */
+static void
+dht_da (void *cls, void *op_result)
+{
+  struct RegexPeer *peer = (struct RegexPeer *) cls;
+
+  GNUNET_assert (peer->dht_handle == op_result);
+
+  if (NULL != peer->search_handle)
+  {
+    REGEX_INTERNAL_search_cancel (peer->search_handle);
+    peer->search_handle = NULL;
+  }
+
+  if (NULL != peer->dht_handle)
+  {
+    GNUNET_DHT_disconnect (peer->dht_handle);
+    peer->dht_handle = NULL;
+  }
+}
+
+
+/**
+ * Signature of a main function for a testcase.
+ *
+ * @param cls NULL
+ * @param h the run handle
+ * @param num_peers_ number of peers in 'peers'
+ * @param testbed_peers handle to peers run in the testbed.  NULL upon timeout (see
+ *          GNUNET_TESTBED_test_run()).
+ * @param links_succeeded the number of overlay link connection attempts that
+ *          succeeded
+ * @param links_failed the number of overlay link connection attempts that
+ *          failed
+ */
+static void
+test_master (void *cls,
+             struct GNUNET_TESTBED_RunHandle *h,
+             unsigned int num_peers_,
+             struct GNUNET_TESTBED_Peer **testbed_peers,
+             unsigned int links_succeeded,
+             unsigned int links_failed)
+{
+  unsigned int i;
+
+  GNUNET_assert (num_peers_ == num_peers);
+
+  prof_time = GNUNET_TIME_absolute_get_duration (prof_start_time);
+  GNUNET_log (GNUNET_ERROR_TYPE_INFO,
+              "Testbed started in %s\n",
+              GNUNET_STRINGS_relative_time_to_string (prof_time, GNUNET_NO));
+
+  if (NULL != abort_task)
+  {
+    GNUNET_SCHEDULER_cancel (abort_task);
+    abort_task = NULL;
+  }
+
+  for (i = 0; i < num_peers; i++)
+  {
+    peers[i].peer_handle = testbed_peers[i];
+  }
+  if (GNUNET_NO ==
+      GNUNET_CONFIGURATION_get_value_yesno (cfg, "DHT", "DISABLE_TRY_CONNECT"))
+  {
+    struct GNUNET_TIME_Relative settle_time;
+
+    settle_time =
+      GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_MILLISECONDS,
+                                     10 * num_peers);
+    GNUNET_log (GNUNET_ERROR_TYPE_INFO,
+                "Waiting for DHT for %s to settle new connections.\n\n",
+                GNUNET_STRINGS_relative_time_to_string(settle_time, GNUNET_NO));
+    GNUNET_SCHEDULER_add_delayed (settle_time, &do_announce, NULL);
+  }
+  else
+  {
+    GNUNET_SCHEDULER_add_now (&do_announce, NULL);
+  }
+  search_timeout_task =
+      GNUNET_SCHEDULER_add_delayed (search_timeout_time, &search_timed_out, NULL);
+}
+
+/**
+ * Function that will be called whenever something in the testbed changes.
+ *
+ * @param cls closure, NULL
+ * @param event information on what is happening
+ */
+static void
+master_controller_cb (void *cls,
+                      const struct GNUNET_TESTBED_EventInformation *event)
+{
+  switch (event->type)
+  {
+  case GNUNET_TESTBED_ET_CONNECT:
+    printf(".");
+    break;
+  case GNUNET_TESTBED_ET_PEER_START:
+    printf("#");
+    break;
+  default:
+    break;
+  }
+  fflush(stdout);
+}
+
+
+/******************************************************************************/
+/***************************  TESTBED PEER SETUP  *****************************/
+/******************************************************************************/
+
+/**
+ * Process the text buffer counting the non-empty lines and separating them
+ * with NULL characters, for later ease of copy using (as)printf.
+ *
+ * @param data Memory buffer with strings.
+ * @param data_size Size of the @a data buffer in bytes.
+ * @param str_max Maximum number of strings to return.
+ * @return Positive number of lines found in the buffer,
+ *         #GNUNET_SYSERR otherwise.
+ */
+static int
+count_and_separate_strings (char *data,
+                            uint64_t data_size,
+                            unsigned int str_max)
+{
+  char *buf;            // Keep track of last string to skip blank lines
+  unsigned int offset;
+  unsigned int str_cnt;
+
+  buf = data;
+  offset = 0;
+  str_cnt = 0;
+  while ( (offset < (data_size - 1)) && (str_cnt < str_max) )
+  {
+    offset++;
+    if ( ((data[offset] == '\n')) &&
+         (buf != &data[offset]) )
+    {
+      data[offset] = '\0';
+      str_cnt++;
+      buf = &data[offset + 1];
+    }
+    else if ( (data[offset] == '\n') ||
+              (data[offset] == '\0') )
+      buf = &data[offset + 1];
+  }
+  return str_cnt;
+}
+
+
+/**
+ * Allocate a string array and fill it with the prefixed strings
+ * from a pre-processed, NULL-separated memory region.
+ *
+ * @param data Preprocessed memory with strings
+ * @param data_size Size of the @a data buffer in bytes.
+ * @param strings Address of the string array to be created.
+ *                Must be freed by caller if function end in success.
+ * @param str_cnt String count. The @a data buffer should contain
+ *                at least this many NULL-separated strings.
+ * @return #GNUNET_OK in ase of success, #GNUNET_SYSERR otherwise.
+ *         In case of error @a strings must not be freed.
+ */
+static int
+create_string_array (char *data, uint64_t data_size,
+                     char ***strings, unsigned int str_cnt)
+{
+  uint64_t offset;
+  uint64_t len;
+  unsigned int i;
+
+  *strings = GNUNET_malloc (sizeof (char *) * str_cnt);
+  offset = 0;
+  for (i = 0; i < str_cnt; i++)
+  {
+    len = strlen (&data[offset]);
+    if (offset + len >= data_size)
+    {
+      GNUNET_free (*strings);
+      *strings = NULL;
+      return GNUNET_SYSERR;
+    }
+    if (0 == len) // empty line
+    {
+      offset++;
+      i--;
+      continue;
+    }
+
+    GNUNET_asprintf (&(*strings)[i],
+                     "%s%s",
+                     regex_prefix,
+                     &data[offset]);
+    offset += len + 1;
+  }
+  return GNUNET_OK;
+}
+
+
+/**
+ * Load search strings from given filename. One search string per line.
+ *
+ * @param filename filename of the file containing the search strings.
+ * @param strings set of strings loaded from file. Caller needs to free this
+ *                if number returned is greater than zero.
+ * @param limit upper limit on the number of strings read from the file
+ * @return number of strings found in the file. #GNUNET_SYSERR on error.
+ */
+static int
+load_search_strings (const char *filename,
+                    char ***strings,
+                    unsigned int limit)
+{
+  char *data;
+  uint64_t filesize;
+  int str_cnt;
+
+  /* Sanity checks */
+  if (NULL == filename)
+  {
+    return GNUNET_SYSERR;
+  }
+  if (GNUNET_YES != GNUNET_DISK_file_test (filename))
+  {
+    GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
+                "Could not find search strings file %s\n", filename);
+    return GNUNET_SYSERR;
+  }
+  if (GNUNET_OK !=
+      GNUNET_DISK_file_size (filename,
+                             &filesize,
+                             GNUNET_YES,
+                             GNUNET_YES))
+  {
+    GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
+                "Search strings file %s cannot be read.\n",
+                filename);
+    return GNUNET_SYSERR;
+  }
+  if (0 == filesize)
+  {
+    GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
+                "Search strings file %s is empty.\n",
+                filename);
+    return GNUNET_SYSERR;
+  }
+
+  /* Read data into memory */
+  data = GNUNET_malloc (filesize + 1);
+  if (filesize != GNUNET_DISK_fn_read (filename,
+                                       data,
+                                       filesize))
+  {
+    GNUNET_free (data);
+    GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
+                "Could not read search strings file %s.\n",
+         filename);
+    return GNUNET_SYSERR;
+  }
+
+  /* Process buffer and build array */
+  str_cnt = count_and_separate_strings (data, filesize, limit);
+  if (GNUNET_OK != create_string_array (data, filesize, strings, str_cnt))
+  {
+    str_cnt = GNUNET_SYSERR;
+  }
+  GNUNET_free (data);
+  return str_cnt;
+}
+
+
+/**
+ * Main function that will be run by the scheduler.
+ *
+ * @param cls closure
+ * @param args remaining command-line arguments
+ * @param cfgfile name of the configuration file used (for saving, can be NULL!)
+ * @param config configuration
+ */
+static void
+run (void *cls,
+     char *const *args,
+     const char *cfgfile,
+     const struct GNUNET_CONFIGURATION_Handle *config)
+{
+  unsigned int nsearchstrs;
+  unsigned int i;
+  struct GNUNET_TIME_Relative abort_time;
+
+  in_shutdown = GNUNET_NO;
+
+  /* Check config */
+  if (NULL == config)
+  {
+    GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+                _("No configuration file given. Exiting\n"));
+    GNUNET_SCHEDULER_add_now (&do_shutdown, NULL);
+    return;
+  }
+  cfg = GNUNET_CONFIGURATION_dup (config);
+  if (GNUNET_OK !=
+      GNUNET_CONFIGURATION_get_value_string (cfg, "REGEXPROFILER",
+                                             "REGEX_PREFIX",
+                                             &regex_prefix))
+  {
+    GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR,
+                              "regexprofiler",
+                              "regex_prefix");
+    GNUNET_SCHEDULER_add_now (&do_shutdown, NULL);
+    return;
+  }
+  if (GNUNET_OK !=
+      GNUNET_CONFIGURATION_get_value_number (cfg, "REGEXPROFILER",
+                                             "PARALLEL_SEARCHES",
+                                             &init_parallel_searches))
+  {
+    GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
+                "Configuration option \"PARALLEL_SEARCHES\" missing."
+                " Using default (%d)\n", 10);
+    init_parallel_searches = 10;
+  }
+  if (GNUNET_OK !=
+      GNUNET_CONFIGURATION_get_value_time (cfg, "REGEXPROFILER",
+                                           "REANNOUNCE_PERIOD_MAX",
+                                           &reannounce_period_max))
+  {
+    GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
+                "reannounce_period_max not given. Using 10 minutes.\n");
+    reannounce_period_max =
+      GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_MINUTES, 10);
+  }
+
+  /* Check arguments */
+  if (NULL == policy_dir)
+  {
+    GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+                _("No policy directory specified on command line. Exiting.\n"));
+    return;
+  }
+  if (GNUNET_YES != GNUNET_DISK_directory_test (policy_dir, GNUNET_YES))
+  {
+    GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+                _("Specified policies directory does not exist. Exiting.\n"));
+    GNUNET_SCHEDULER_add_now (&do_shutdown, NULL);
+    return;
+  }
+  if (0 >= (int) (num_peers = GNUNET_DISK_directory_scan (policy_dir, NULL, NULL)))
+  {
+    GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+                _("No files found in `%s'\n"),
+                policy_dir);
+    return;
+  }
+  GNUNET_CONFIGURATION_set_value_string (cfg, "REGEXPROFILER",
+                                         "POLICY_DIR", policy_dir);
+  if (GNUNET_YES != GNUNET_DISK_file_test (strings_file))
+  {
+    GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+                _("No search strings file given. Exiting.\n"));
+    GNUNET_SCHEDULER_add_now (&do_shutdown, NULL);
+    return;
+  }
+  nsearchstrs = load_search_strings (strings_file,
+                                     &search_strings,
+                                     num_peers);
+  if (num_peers != nsearchstrs)
+  {
+    GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+                "Error loading search strings.\n");
+    GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+                "File (%s) does not contain enough strings (%u/%u).\n",
+                strings_file, nsearchstrs, num_peers);
+    GNUNET_SCHEDULER_add_now (&do_shutdown, NULL);
+    return;
+  }
+  if ( (0 == num_peers) || (NULL == search_strings))
+  {
+    GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+                _("Error loading search strings. Exiting.\n"));
+    GNUNET_SCHEDULER_add_now (&do_shutdown, NULL);
+    return;
+  }
+  for (i = 0; i < num_peers; i++)
+    GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+                "search string: %s\n",
+                search_strings[i]);
+
+  /* Check logfile */
+  if ( (NULL != data_filename) &&
+       (NULL == (data_file =
+                 GNUNET_DISK_file_open (data_filename,
+                                        GNUNET_DISK_OPEN_READWRITE |
+                                        GNUNET_DISK_OPEN_TRUNCATE |
+                                        GNUNET_DISK_OPEN_CREATE,
+                                        GNUNET_DISK_PERM_USER_READ |
+                                        GNUNET_DISK_PERM_USER_WRITE))) )
+  {
+    GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_ERROR,
+                              "open",
+                              data_filename);
+    return;
+  }
+
+  /* Initialize peers */
+  peers = GNUNET_malloc (sizeof (struct RegexPeer) * num_peers);
+  for (i = 0; i < num_peers; i++)
+    peers[i].id = i;
+
+  GNUNET_CONFIGURATION_set_value_number (cfg,
+                                         "TESTBED", "OVERLAY_RANDOM_LINKS",
+                                         num_peers * 20);
+  GNUNET_CONFIGURATION_set_value_number (cfg,
+                                         "DHT", "FORCE_NSE",
+                                         (long long unsigned)
+                                         (log (num_peers) / log (2.0)));
+  event_mask = 0LL;
+/* For feedback about the start process activate these and pass master_cb */
+  event_mask |= (1LL << GNUNET_TESTBED_ET_PEER_START);
+//   event_mask |= (1LL << GNUNET_TESTBED_ET_PEER_STOP);
+  event_mask |= (1LL << GNUNET_TESTBED_ET_CONNECT);
+//   event_mask |= (1LL << GNUNET_TESTBED_ET_DISCONNECT);
+  prof_start_time = GNUNET_TIME_absolute_get ();
+  GNUNET_TESTBED_run (hosts_file,
+                      cfg,
+                      num_peers,
+                      event_mask,
+                      &master_controller_cb,
+                      NULL,     /* master_controller_cb cls */
+                      &test_master,
+                      NULL);    /* test_master cls */
+  if (GNUNET_OK !=
+      GNUNET_CONFIGURATION_get_value_time (cfg, "TESTBED",
+                                           "SETUP_TIMEOUT",
+                                           &abort_time))
+  {
+    GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
+                "SETUP_TIMEOUT not given. Using 15 minutes.\n");
+    abort_time =
+      GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_MINUTES, 15);
+  }
+  abort_time = GNUNET_TIME_relative_add (abort_time, GNUNET_TIME_UNIT_MINUTES);
+  abort_task =
+      GNUNET_SCHEDULER_add_delayed (abort_time,
+                                    &do_abort,
+                                    (void*) __LINE__);
+  GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
+              "setup_timeout: %s\n",
+              GNUNET_STRINGS_relative_time_to_string (abort_time, GNUNET_YES));
+}
+
+
+/**
+ * Main function.
+ *
+ * @param argc argument count
+ * @param argv argument values
+ * @return 0 on success
+ */
+int
+main (int argc, char *const *argv)
+{
+  struct GNUNET_GETOPT_CommandLineOption options[] = {
+
+    GNUNET_GETOPT_option_filename ('o',
+                                   "output-file",
+                                   "FILENAME",
+                                   gettext_noop ("name of the file for writing statistics"),
+                                   &data_filename),
+
+    GNUNET_GETOPT_option_relative_time ('t',
+                                            "matching-timeout",
+                                            "TIMEOUT",
+                                            gettext_noop ("wait TIMEOUT before ending the experiment"),
+                                            &search_timeout_time), 
+
+    GNUNET_GETOPT_option_filename ('p',
+                                   "policy-dir",
+                                   "DIRECTORY",
+                                   gettext_noop ("directory with policy files"),
+                                   &policy_dir),
+
+
+    GNUNET_GETOPT_option_filename ('s',
+                                   "strings-file",
+                                   "FILENAME",
+                                   gettext_noop ("name of file with input strings"),
+                                   &strings_file),
+
+    GNUNET_GETOPT_option_filename ('H',
+                                   "hosts-file",
+                                   "FILENAME",
+                                   gettext_noop ("name of file with hosts' names"),
+                                   &hosts_file),
+
+    GNUNET_GETOPT_OPTION_END
+  };
+  int ret;
+
+  if (GNUNET_OK != GNUNET_STRINGS_get_utf8_args (argc, argv, &argc, &argv))
+    return 2;
+  result = GNUNET_SYSERR;
+  ret =
+      GNUNET_PROGRAM_run (argc, argv,
+                          "gnunet-regex-profiler",
+                          _("Profiler for regex"),
+                          options, &run, NULL);
+  if (GNUNET_OK != ret)
+    return ret;
+  if (GNUNET_OK != result)
+    return 1;
+  return 0;
+}
diff --git a/src/regex/gnunet-regex-simulation-profiler.c b/src/regex/gnunet-regex-simulation-profiler.c
new file mode 100644 (file)
index 0000000..b7e256f
--- /dev/null
@@ -0,0 +1,723 @@
+/*
+     This file is part of GNUnet.
+     Copyright (C) 2011, 2012 GNUnet e.V.
+
+     GNUnet is free software: you can redistribute it and/or modify it
+     under the terms of the GNU Affero General Public License as published
+     by the Free Software Foundation, either version 3 of the License,
+     or (at your option) any later version.
+
+     GNUnet is distributed in the hope that it will be useful, but
+     WITHOUT ANY WARRANTY; without even the implied warranty of
+     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+     Affero General Public License for more details.
+    
+     You should have received a copy of the GNU Affero General Public License
+     along with this program.  If not, see <http://www.gnu.org/licenses/>.
+*/
+
+
+/**
+ * @file regex/gnunet-regex-simulation-profiler.c
+ * @brief Regex profiler that dumps all DFAs into a database instead of
+ *        using the DHT (with cadet).
+ * @author Maximilian Szengel
+ * @author Christophe Genevey
+ *
+ */
+
+#include "platform.h"
+#include "gnunet_util_lib.h"
+#include "regex_internal_lib.h"
+#include "gnunet_mysql_lib.h"
+#include "gnunet_my_lib.h"
+#include <mysql/mysql.h>
+
+/**
+ * MySQL statement to insert an edge.
+ */
+#define INSERT_EDGE_STMT "INSERT IGNORE INTO `%s` "\
+                         "(`key`, `label`, `to_key`, `accepting`) "\
+                         "VALUES (?, ?, ?, ?);"
+
+/**
+ * MySQL statement to select a key count.
+ */
+#define SELECT_KEY_STMT "SELECT COUNT(*) FROM `%s` "\
+                        "WHERE `key` = ? AND `label` = ?;"
+
+/**
+ * Simple struct to keep track of progress, and print a
+ * nice little percentage meter for long running tasks.
+ */
+struct ProgressMeter
+{
+  /**
+   * Total number of elements.
+   */
+  unsigned int total;
+
+  /**
+   * Intervall for printing percentage.
+   */
+  unsigned int modnum;
+
+  /**
+   * Number of dots to print.
+   */
+  unsigned int dotnum;
+
+  /**
+   * Completed number.
+   */
+  unsigned int completed;
+
+  /**
+   * Should the meter be printed?
+   */
+  int print;
+
+  /**
+   * String to print on startup.
+   */
+  char *startup_string;
+};
+
+
+/**
+ * Handle for the progress meter
+ */
+static struct ProgressMeter *meter;
+
+/**
+ * Scan task identifier;
+ */
+static struct GNUNET_SCHEDULER_Task *scan_task;
+
+/**
+ * Global testing status.
+ */
+static int result;
+
+/**
+ * MySQL context.
+ */
+static struct GNUNET_MYSQL_Context *mysql_ctx;
+
+/**
+ * MySQL prepared statement handle.
+ */
+static struct GNUNET_MYSQL_StatementHandle *stmt_handle;
+
+/**
+ * MySQL prepared statement handle for `key` select.
+ */
+static struct GNUNET_MYSQL_StatementHandle *select_stmt_handle;
+
+/**
+ * MySQL table name.
+ */
+static char *table_name;
+
+/**
+ * Policy dir containing files that contain policies.
+ */
+static char *policy_dir;
+
+/**
+ * Number of policy files.
+ */
+static unsigned int num_policy_files;
+
+/**
+ * Number of policies.
+ */
+static unsigned int num_policies;
+
+/**
+ * Maximal path compression length.
+ */
+static unsigned int max_path_compression;
+
+/**
+ * Number of merged transitions.
+ */
+static unsigned long long num_merged_transitions;
+
+/**
+ * Number of merged states from different policies.
+ */
+static unsigned long long num_merged_states;
+
+/**
+ * Prefix to add before every regex we're announcing.
+ */
+static char *regex_prefix;
+
+
+/**
+ * Create a meter to keep track of the progress of some task.
+ *
+ * @param total the total number of items to complete
+ * @param start_string a string to prefix the meter with (if printing)
+ * @param print GNUNET_YES to print the meter, GNUNET_NO to count
+ *              internally only
+ *
+ * @return the progress meter
+ */
+static struct ProgressMeter *
+create_meter (unsigned int total, char *start_string, int print)
+{
+  struct ProgressMeter *ret;
+
+  ret = GNUNET_new (struct ProgressMeter);
+  ret->print = print;
+  ret->total = total;
+  ret->modnum = total / 4;
+  if (ret->modnum == 0)         /* Divide by zero check */
+    ret->modnum = 1;
+  ret->dotnum = (total / 50) + 1;
+  if (start_string != NULL)
+    ret->startup_string = GNUNET_strdup (start_string);
+  else
+    ret->startup_string = GNUNET_strdup ("");
+
+  return ret;
+}
+
+
+/**
+ * Update progress meter (increment by one).
+ *
+ * @param meter the meter to update and print info for
+ *
+ * @return GNUNET_YES if called the total requested,
+ *         GNUNET_NO if more items expected
+ */
+static int
+update_meter (struct ProgressMeter *meter)
+{
+  if (meter->print == GNUNET_YES)
+  {
+    if (meter->completed % meter->modnum == 0)
+    {
+      if (meter->completed == 0)
+      {
+        FPRINTF (stdout, "%sProgress: [0%%", meter->startup_string);
+      }
+      else
+        FPRINTF (stdout, "%d%%",
+                 (int) (((float) meter->completed / meter->total) * 100));
+    }
+    else if (meter->completed % meter->dotnum == 0)
+      FPRINTF (stdout, "%s", ".");
+
+    if (meter->completed + 1 == meter->total)
+      FPRINTF (stdout, "%d%%]\n", 100);
+    fflush (stdout);
+  }
+  meter->completed++;
+
+  if (meter->completed == meter->total)
+    return GNUNET_YES;
+  if (meter->completed > meter->total)
+    GNUNET_log (GNUNET_ERROR_TYPE_WARNING, "Progress meter overflow!!\n");
+  return GNUNET_NO;
+}
+
+
+/**
+ * Reset progress meter.
+ *
+ * @param meter the meter to reset
+ *
+ * @return #GNUNET_YES if meter reset,
+ *         #GNUNET_SYSERR on error
+ */
+static int
+reset_meter (struct ProgressMeter *meter)
+{
+  if (meter == NULL)
+    return GNUNET_SYSERR;
+
+  meter->completed = 0;
+  return GNUNET_YES;
+}
+
+
+/**
+ * Release resources for meter
+ *
+ * @param meter the meter to free
+ */
+static void
+free_meter (struct ProgressMeter *meter)
+{
+  GNUNET_free_non_null (meter->startup_string);
+  GNUNET_free (meter);
+}
+
+
+/**
+ * Shutdown task.
+ *
+ * @param cls NULL
+ */
+static void
+do_shutdown (void *cls)
+{
+  if (NULL != mysql_ctx)
+  {
+    GNUNET_MYSQL_context_destroy (mysql_ctx);
+    mysql_ctx = NULL;
+  }
+  if (NULL != meter)
+  {
+    free_meter (meter);
+    meter = NULL;
+  }
+}
+
+
+/**
+ * Abort task to run on test timed out.
+ *
+ * FIXME: this doesn't actually work, it used to cancel
+ * the already running 'scan_task', but now that should
+ * always be NULL and do nothing. We instead need to set
+ * a global variable and abort scan_task internally, not
+ * via scheduler.
+ *
+ * @param cls NULL
+ */
+static void
+do_abort (void *cls)
+{
+  GNUNET_log (GNUNET_ERROR_TYPE_WARNING, "Aborting\n");
+  if (NULL != scan_task)
+  {
+    GNUNET_SCHEDULER_cancel (scan_task);
+    scan_task = NULL;
+  }
+  result = GNUNET_SYSERR;
+  GNUNET_SCHEDULER_shutdown ();
+}
+
+/**
+ * Iterator over all states that inserts each state into the MySQL db.
+ *
+ * @param cls closure.
+ * @param key hash for current state.
+ * @param proof proof for current state.
+ * @param accepting #GNUNET_YES if this is an accepting state, #GNUNET_NO if not.
+ * @param num_edges number of edges leaving current state.
+ * @param edges edges leaving current state.
+ */
+static void
+regex_iterator (void *cls,
+               const struct GNUNET_HashCode *key,
+               const char *proof,
+                int accepting,
+               unsigned int num_edges,
+                const struct REGEX_BLOCK_Edge *edges)
+{
+  unsigned int i;
+  int result;
+
+  uint32_t iaccepting = (uint32_t)accepting;
+  uint64_t total;
+
+  GNUNET_assert (NULL != mysql_ctx);
+
+  for (i = 0; i < num_edges; i++)
+  {
+    struct GNUNET_MY_QueryParam params_select[] = {
+      GNUNET_MY_query_param_auto_from_type (key),
+      GNUNET_MY_query_param_string (edges[i].label),
+      GNUNET_MY_query_param_end
+    };
+
+    struct GNUNET_MY_ResultSpec results_select[] = {
+      GNUNET_MY_result_spec_uint64 (&total),
+      GNUNET_MY_result_spec_end
+    };
+
+    result = 
+      GNUNET_MY_exec_prepared (mysql_ctx,
+                              select_stmt_handle,
+                              params_select);
+
+    if (GNUNET_SYSERR == result)
+    {
+      GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+                  "Error executing prepared mysql select statement\n");
+      GNUNET_SCHEDULER_add_now (&do_abort, NULL);
+      return;
+    }
+
+    result = 
+      GNUNET_MY_extract_result (select_stmt_handle,
+                                results_select);
+
+    if (GNUNET_SYSERR == result)
+    {
+      GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+                  "Error extracting result mysql select statement\n");
+      GNUNET_SCHEDULER_add_now (&do_abort, NULL);
+      return;
+    }
+
+    if (-1 != total && total > 0)
+    {
+      GNUNET_log (GNUNET_ERROR_TYPE_INFO, "Total: %llu (%s, %s)\n", 
+                 (unsigned long long)total,
+                  GNUNET_h2s (key), edges[i].label);
+    }
+
+    struct GNUNET_MY_QueryParam params_stmt[] = {
+      GNUNET_MY_query_param_auto_from_type (&key),
+      GNUNET_MY_query_param_string (edges[i].label),
+      GNUNET_MY_query_param_auto_from_type (&edges[i].destination),
+      GNUNET_MY_query_param_uint32 (&iaccepting),
+      GNUNET_MY_query_param_end
+    };
+
+    result = 
+      GNUNET_MY_exec_prepared (mysql_ctx,
+                              stmt_handle,
+                              params_stmt);
+
+    if (0 == result)
+    {
+      char *key_str = GNUNET_strdup (GNUNET_h2s (key));
+      char *to_key_str = GNUNET_strdup (GNUNET_h2s (&edges[i].destination));
+
+      GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Merged (%s, %s, %s, %i)\n", 
+                 key_str,
+                  edges[i].label, 
+                 to_key_str, 
+                 accepting);
+
+      GNUNET_free (key_str);
+      GNUNET_free (to_key_str);
+      num_merged_transitions++;
+    }
+    else if (-1 != total)
+    {
+      num_merged_states++;
+    }
+
+    if (GNUNET_SYSERR == result || (1 != result && 0 != result))
+    {
+      GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+                  "Error executing prepared mysql statement for edge: Affected rows: %i, expected 0 or 1!\n",
+                  result);
+      GNUNET_SCHEDULER_add_now (&do_abort, NULL);
+    }
+  }
+
+  if (0 == num_edges)
+  {
+    struct GNUNET_MY_QueryParam params_stmt[] = {
+      GNUNET_MY_query_param_auto_from_type (key),
+      GNUNET_MY_query_param_string (""),
+      GNUNET_MY_query_param_fixed_size (NULL, 0),
+      GNUNET_MY_query_param_uint32 (&iaccepting),
+      GNUNET_MY_query_param_end
+    };
+
+    result = 
+      GNUNET_MY_exec_prepared (mysql_ctx,
+                              stmt_handle,
+                              params_stmt);
+
+    if (1 != result && 0 != result)
+    {
+      GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+                  "Error executing prepared mysql statement for edge: Affected rows: %i, expected 0 or 1!\n",
+                  result);
+      GNUNET_SCHEDULER_add_now (&do_abort, NULL);
+    }
+  }
+}
+
+
+/**
+ * Announce a regex by creating the DFA and iterating over each state, inserting
+ * each state into a MySQL database.
+ *
+ * @param regex regular expression.
+ * @return #GNUNET_OK on success, #GNUNET_SYSERR on failure.
+ */
+static int
+announce_regex (const char *regex)
+{
+  struct REGEX_INTERNAL_Automaton *dfa;
+
+  dfa =
+      REGEX_INTERNAL_construct_dfa (regex,
+                                   strlen (regex),
+                                   max_path_compression);
+
+  if (NULL == dfa)
+  {
+    GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+               "Failed to create DFA for regex %s\n",
+                regex);
+    GNUNET_SCHEDULER_add_now (&do_abort, NULL);
+    return GNUNET_SYSERR;
+  }
+  REGEX_INTERNAL_iterate_all_edges (dfa,
+                                   &regex_iterator, NULL);
+  REGEX_INTERNAL_automaton_destroy (dfa);
+
+  return GNUNET_OK;
+}
+
+
+/**
+ * Function called with a filename.
+ *
+ * @param cls closure
+ * @param filename complete filename (absolute path)
+ * @return #GNUNET_OK to continue to iterate,
+ *  #GNUNET_SYSERR to abort iteration with error!
+ */
+static int
+policy_filename_cb (void *cls, const char *filename)
+{
+  char *regex;
+  char *data;
+  char *buf;
+  uint64_t filesize;
+  unsigned int offset;
+
+  GNUNET_assert (NULL != filename);
+
+  GNUNET_log (GNUNET_ERROR_TYPE_INFO,
+             "Announcing regexes from file %s\n",
+              filename);
+
+  if (GNUNET_YES != GNUNET_DISK_file_test (filename))
+  {
+    GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
+               "Could not find policy file %s\n",
+                filename);
+    return GNUNET_OK;
+  }
+  if (GNUNET_OK !=
+      GNUNET_DISK_file_size (filename, &filesize,
+                            GNUNET_YES, GNUNET_YES))
+    filesize = 0;
+  if (0 == filesize)
+  {
+    GNUNET_log (GNUNET_ERROR_TYPE_WARNING, "Policy file %s is empty.\n",
+                filename);
+    return GNUNET_OK;
+  }
+  data = GNUNET_malloc (filesize);
+  if (filesize != GNUNET_DISK_fn_read (filename, data, filesize))
+  {
+    GNUNET_free (data);
+    GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
+               "Could not read policy file %s.\n",
+                filename);
+    return GNUNET_OK;
+  }
+
+  update_meter (meter);
+
+  buf = data;
+  offset = 0;
+  regex = NULL;
+  while (offset < (filesize - 1))
+  {
+    offset++;
+    if (((data[offset] == '\n')) && (buf != &data[offset]))
+    {
+      data[offset] = '|';
+      num_policies++;
+      buf = &data[offset + 1];
+    }
+    else if ((data[offset] == '\n') || (data[offset] == '\0'))
+      buf = &data[offset + 1];
+  }
+  data[offset] = '\0';
+  GNUNET_asprintf (&regex, "%s(%s)", regex_prefix, data);
+  GNUNET_assert (NULL != regex);
+  GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+             "Announcing regex: %s\n", regex);
+
+  if (GNUNET_OK != announce_regex (regex))
+  {
+    GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+               "Could not announce regex %s\n",
+                regex);
+  }
+  GNUNET_free (regex);
+  GNUNET_free (data);
+  return GNUNET_OK;
+}
+
+
+/**
+ * Iterate over files contained in policy_dir.
+ *
+ * @param cls NULL
+ */
+static void
+do_directory_scan (void *cls)
+{
+  struct GNUNET_TIME_Absolute start_time;
+  struct GNUNET_TIME_Relative duration;
+  char *stmt;
+
+  /* Create an MySQL prepared statement for the inserts */
+  scan_task = NULL;
+  GNUNET_asprintf (&stmt, INSERT_EDGE_STMT, table_name);
+  stmt_handle = GNUNET_MYSQL_statement_prepare (mysql_ctx, stmt);
+  GNUNET_free (stmt);
+
+  GNUNET_asprintf (&stmt, SELECT_KEY_STMT, table_name);
+  select_stmt_handle = GNUNET_MYSQL_statement_prepare (mysql_ctx, stmt);
+  GNUNET_free (stmt);
+
+  GNUNET_assert (NULL != stmt_handle);
+
+  meter = create_meter (num_policy_files,
+                       "Announcing policy files\n",
+                       GNUNET_YES);
+  start_time = GNUNET_TIME_absolute_get ();
+  GNUNET_DISK_directory_scan (policy_dir,
+                             &policy_filename_cb,
+                             stmt_handle);
+  duration = GNUNET_TIME_absolute_get_duration (start_time);
+  reset_meter (meter);
+  free_meter (meter);
+  meter = NULL;
+
+  printf ("Announced %u files containing %u policies in %s\n"
+          "Duplicate transitions: %llu\nMerged states: %llu\n",
+          num_policy_files,
+         num_policies,
+          GNUNET_STRINGS_relative_time_to_string (duration, GNUNET_NO),
+          num_merged_transitions,
+         num_merged_states);
+  result = GNUNET_OK;
+  GNUNET_SCHEDULER_shutdown ();
+}
+
+
+/**
+ * Main function that will be run by the scheduler.
+ *
+ * @param cls closure
+ * @param args remaining command-line arguments
+ * @param cfgfile name of the configuration file used (for saving, can be NULL!)
+ * @param config configuration
+ */
+static void
+run (void *cls,
+     char *const *args,
+     const char *cfgfile,
+     const struct GNUNET_CONFIGURATION_Handle *config)
+{
+  if (NULL == args[0])
+  {
+    fprintf (stderr,
+             _("No policy directory specified on command line. Exiting.\n"));
+    result = GNUNET_SYSERR;
+    return;
+  }
+  if (GNUNET_YES !=
+      GNUNET_DISK_directory_test (args[0], GNUNET_YES))
+  {
+    fprintf (stderr,
+             _("Specified policies directory does not exist. Exiting.\n"));
+    result = GNUNET_SYSERR;
+    return;
+  }
+  policy_dir = args[0];
+
+  num_policy_files = GNUNET_DISK_directory_scan (policy_dir,
+                                                NULL, NULL);
+  meter = NULL;
+
+  if (NULL == table_name)
+  {
+    GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
+                "No table name specified, using default \"NFA\".\n");
+    table_name = "NFA";
+  }
+
+  mysql_ctx = GNUNET_MYSQL_context_create (config, "regex-mysql");
+  if (NULL == mysql_ctx)
+  {
+    GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+               "Failed to create mysql context\n");
+    result = GNUNET_SYSERR;
+    return;
+  }
+
+  if (GNUNET_OK !=
+      GNUNET_CONFIGURATION_get_value_string (config,
+                                            "regex-mysql",
+                                             "REGEX_PREFIX",
+                                            &regex_prefix))
+  {
+    GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR,
+                              "regex-mysql",
+                              "REGEX_PREFIX");
+    result = GNUNET_SYSERR;
+    return;
+  }
+
+  result = GNUNET_OK;
+  GNUNET_SCHEDULER_add_shutdown (&do_shutdown,
+                                NULL);
+  scan_task = GNUNET_SCHEDULER_add_now (&do_directory_scan, NULL);
+}
+
+
+/**
+ * Main function.
+ *
+ * @param argc argument count
+ * @param argv argument values
+ * @return 0 on success
+ */
+int
+main (int argc, char *const *argv)
+{
+  struct GNUNET_GETOPT_CommandLineOption options[] = {
+
+    GNUNET_GETOPT_option_string ('t',
+                                 "table",
+                                 "TABLENAME",
+                                 gettext_noop ("name of the table to write DFAs"),
+                                 &table_name),
+
+    GNUNET_GETOPT_option_uint ('p',
+                                   "max-path-compression",
+                                   "MAX_PATH_COMPRESSION",
+                                   gettext_noop ("maximum path compression length"),
+                                   &max_path_compression),
+
+    GNUNET_GETOPT_OPTION_END
+  };
+  int ret;
+
+  if (GNUNET_OK != GNUNET_STRINGS_get_utf8_args (argc, argv, &argc, &argv))
+    return 2;
+
+  result = GNUNET_SYSERR;
+  ret =
+      GNUNET_PROGRAM_run (argc, argv,
+                          "gnunet-regex-simulationprofiler [OPTIONS] policy-dir",
+                          _("Profiler for regex library"), options, &run, NULL);
+  if (GNUNET_OK != ret)
+    return ret;
+  if (GNUNET_OK != result)
+    return 1;
+  return 0;
+}
diff --git a/src/regex/gnunet-service-regex.c b/src/regex/gnunet-service-regex.c
new file mode 100644 (file)
index 0000000..eb10e7c
--- /dev/null
@@ -0,0 +1,416 @@
+/*
+     This file is part of GNUnet.
+     Copyright (C) 2013 GNUnet e.V.
+
+     GNUnet is free software: you can redistribute it and/or modify it
+     under the terms of the GNU Affero General Public License as published
+     by the Free Software Foundation, either version 3 of the License,
+     or (at your option) any later version.
+
+     GNUnet is distributed in the hope that it will be useful, but
+     WITHOUT ANY WARRANTY; without even the implied warranty of
+     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+     Affero General Public License for more details.
+    
+     You should have received a copy of the GNU Affero General Public License
+     along with this program.  If not, see <http://www.gnu.org/licenses/>.
+*/
+
+/**
+ * @file regex/gnunet-service-regex.c
+ * @brief service to advertise capabilities described as regex and to
+ *        lookup capabilities by regex
+ * @author Christian Grothoff
+ */
+#include "platform.h"
+#include "gnunet_util_lib.h"
+#include "regex_internal_lib.h"
+#include "regex_ipc.h"
+
+
+/**
+ * Information about one of our clients.
+ */
+struct ClientEntry
+{
+
+  /**
+   * Queue for transmissions to @e client.
+   */
+  struct GNUNET_MQ_Handle *mq;
+
+  /**
+   * Handle identifying the client.
+   */
+  struct GNUNET_SERVICE_Client *client;
+
+  /**
+   * Search handle (if this client is searching).
+   */
+  struct REGEX_INTERNAL_Search *sh;
+
+  /**
+   * Announcement handle (if this client is announcing).
+   */
+  struct REGEX_INTERNAL_Announcement *ah;
+
+  /**
+   * Refresh frequency for announcements.
+   */
+  struct GNUNET_TIME_Relative frequency;
+
+  /**
+   * Task for re-announcing.
+   */
+  struct GNUNET_SCHEDULER_Task *refresh_task;
+
+};
+
+
+/**
+ * Connection to the DHT.
+ */
+static struct GNUNET_DHT_Handle *dht;
+
+/**
+ * Handle for doing statistics.
+ */
+static struct GNUNET_STATISTICS_Handle *stats;
+
+/**
+ * Private key for this peer.
+ */
+static struct GNUNET_CRYPTO_EddsaPrivateKey *my_private_key;
+
+
+/**
+ * Task run during shutdown.
+ *
+ * @param cls unused
+ */
+static void
+cleanup_task (void *cls)
+{
+  GNUNET_DHT_disconnect (dht);
+  dht = NULL;
+  GNUNET_STATISTICS_destroy (stats,
+                             GNUNET_NO);
+  stats = NULL;
+  GNUNET_free (my_private_key);
+  my_private_key = NULL;
+}
+
+
+/**
+ * Periodic task to refresh our announcement of the regex.
+ *
+ * @param cls the `struct ClientEntry *` of the client that triggered the
+ *        announcement
+ */
+static void
+reannounce (void *cls)
+{
+  struct ClientEntry *ce = cls;
+
+  REGEX_INTERNAL_reannounce (ce->ah);
+  ce->refresh_task = GNUNET_SCHEDULER_add_delayed (ce->frequency,
+                                                  &reannounce,
+                                                  ce);
+}
+
+
+/**
+ * Check ANNOUNCE message.
+ *
+ * @param cls identification of the client
+ * @param am the actual message
+ * @return #GNUNET_OK if @am is well-formed
+ */
+static int
+check_announce (void *cls,
+                const struct AnnounceMessage *am)
+{
+  struct ClientEntry *ce = cls;
+  const char *regex;
+  uint16_t size;
+
+  size = ntohs (am->header.size) - sizeof (*am);
+  regex = (const char *) &am[1];
+  if ('\0' != regex[size - 1])
+  {
+    GNUNET_break (0);
+    return GNUNET_SYSERR;
+  }
+  if (NULL != ce->ah)
+  {
+    /* only one announcement per client allowed */
+    GNUNET_break (0);
+    return GNUNET_SYSERR;
+  }
+  return GNUNET_OK;
+}
+
+
+/**
+ * Handle ANNOUNCE message.
+ *
+ * @param cls identification of the client
+ * @param am the actual message
+ */
+static void
+handle_announce (void *cls,
+                const struct AnnounceMessage *am)
+{
+  struct ClientEntry *ce = cls;
+  const char *regex;
+
+  regex = (const char *) &am[1];
+  ce->frequency = GNUNET_TIME_relative_ntoh (am->refresh_delay);
+  ce->refresh_task = GNUNET_SCHEDULER_add_delayed (ce->frequency,
+                                                  &reannounce,
+                                                  ce);
+  GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+             "Starting to announce regex `%s' every %s\n",
+             regex,
+             GNUNET_STRINGS_relative_time_to_string (ce->frequency,
+                                                     GNUNET_NO));
+  ce->ah = REGEX_INTERNAL_announce (dht,
+                                   my_private_key,
+                                   regex,
+                                   ntohs (am->compression),
+                                   stats);
+  if (NULL == ce->ah)
+  {
+    GNUNET_break (0);
+    GNUNET_SCHEDULER_cancel (ce->refresh_task);
+    ce->refresh_task = NULL;
+    GNUNET_SERVICE_client_drop (ce->client);
+    return;
+  }
+  GNUNET_SERVICE_client_continue (ce->client);
+}
+
+
+/**
+ * Handle result, pass it back to the client.
+ *
+ * @param cls the struct ClientEntry of the client searching
+ * @param id Peer providing a regex that matches the string.
+ * @param get_path Path of the get request.
+ * @param get_path_length Lenght of @a get_path.
+ * @param put_path Path of the put request.
+ * @param put_path_length Length of the @a put_path.
+ */
+static void
+handle_search_result (void *cls,
+                     const struct GNUNET_PeerIdentity *id,
+                     const struct GNUNET_PeerIdentity *get_path,
+                     unsigned int get_path_length,
+                     const struct GNUNET_PeerIdentity *put_path,
+                     unsigned int put_path_length)
+{
+  struct ClientEntry *ce = cls;
+  struct GNUNET_MQ_Envelope *env;
+  struct ResultMessage *result;
+  struct GNUNET_PeerIdentity *gp;
+  uint16_t size;
+
+  if ( (get_path_length >= 65536) ||
+       (put_path_length >= 65536) ||
+       ( (get_path_length + put_path_length) * sizeof (struct GNUNET_PeerIdentity))
+       + sizeof (struct ResultMessage) >= GNUNET_MAX_MESSAGE_SIZE)
+  {
+    GNUNET_break (0);
+    return;
+  }
+  size = (get_path_length + put_path_length) * sizeof (struct GNUNET_PeerIdentity);
+  env = GNUNET_MQ_msg_extra (result,
+                             size,
+                             GNUNET_MESSAGE_TYPE_REGEX_RESULT);
+  result->get_path_length = htons ((uint16_t) get_path_length);
+  result->put_path_length = htons ((uint16_t) put_path_length);
+  result->id = *id;
+  gp = &result->id;
+  GNUNET_memcpy (&gp[1],
+                 get_path,
+                 get_path_length * sizeof (struct GNUNET_PeerIdentity));
+  GNUNET_memcpy (&gp[1 + get_path_length],
+                 put_path,
+                 put_path_length * sizeof (struct GNUNET_PeerIdentity));
+  GNUNET_MQ_send (ce->mq,
+                  env);
+}
+
+
+/**
+ * Check SEARCH message.
+ *
+ * @param cls identification of the client
+ * @param message the actual message
+ */
+static int
+check_search (void *cls,
+              const struct RegexSearchMessage *sm)
+{
+  struct ClientEntry *ce = cls;
+  const char *string;
+  uint16_t size;
+
+  size = ntohs (sm->header.size) - sizeof (*sm);
+  string = (const char *) &sm[1];
+  if ('\0' != string[size - 1])
+  {
+    GNUNET_break (0);
+    return GNUNET_SYSERR;
+  }
+  if (NULL != ce->sh)
+  {
+    /* only one search allowed per client */
+    GNUNET_break (0);
+    return GNUNET_SYSERR;
+  }
+  return GNUNET_OK;
+}
+
+
+/**
+ * Handle SEARCH message.
+ *
+ * @param cls identification of the client
+ * @param message the actual message
+ */
+static void
+handle_search (void *cls,
+              const struct RegexSearchMessage *sm)
+{
+  struct ClientEntry *ce = cls;
+  const char *string;
+
+  string = (const char *) &sm[1];
+  GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+             "Starting to search for `%s'\n",
+             string);
+  ce->sh = REGEX_INTERNAL_search (dht,
+                                  string,
+                                  &handle_search_result,
+                                  ce,
+                                  stats);
+  if (NULL == ce->sh)
+  {
+    GNUNET_break (0);
+    GNUNET_SERVICE_client_drop (ce->client);
+    return;
+  }
+  GNUNET_SERVICE_client_continue (ce->client);
+}
+
+
+/**
+ * Process regex requests.
+ *
+ * @param cls closure
+ * @param cfg configuration to use
+ * @param service the initialized service
+ */
+static void
+run (void *cls,
+     const struct GNUNET_CONFIGURATION_Handle *cfg,
+     struct GNUNET_SERVICE_Handle *service)
+{
+  my_private_key = GNUNET_CRYPTO_eddsa_key_create_from_configuration (cfg);
+  if (NULL == my_private_key)
+  {
+    GNUNET_SCHEDULER_shutdown ();
+    return;
+  }
+  dht = GNUNET_DHT_connect (cfg, 1024);
+  if (NULL == dht)
+  {
+    GNUNET_free (my_private_key);
+    my_private_key = NULL;
+    GNUNET_SCHEDULER_shutdown ();
+    return;
+  }
+  GNUNET_SCHEDULER_add_shutdown (&cleanup_task,
+                                NULL);
+  stats = GNUNET_STATISTICS_create ("regex", cfg);
+}
+
+
+/**
+ * Callback called when a client connects to the service.
+ *
+ * @param cls closure for the service
+ * @param c the new client that connected to the service
+ * @param mq the message queue used to send messages to the client
+ * @return @a c
+ */
+static void *
+client_connect_cb (void *cls,
+                  struct GNUNET_SERVICE_Client *c,
+                  struct GNUNET_MQ_Handle *mq)
+{
+  struct ClientEntry *ce;
+
+  ce = GNUNET_new (struct ClientEntry);
+  ce->client = c;
+  ce->mq = mq;
+  return ce;
+}
+
+
+/**
+ * Callback called when a client disconnected from the service
+ *
+ * @param cls closure for the service
+ * @param c the client that disconnected
+ * @param internal_cls should be equal to @a c
+ */
+static void
+client_disconnect_cb (void *cls,
+                     struct GNUNET_SERVICE_Client *c,
+                     void *internal_cls)
+{
+  struct ClientEntry *ce = internal_cls;
+
+  if (NULL != ce->refresh_task)
+  {
+    GNUNET_SCHEDULER_cancel (ce->refresh_task);
+    ce->refresh_task = NULL;
+  }
+  if (NULL != ce->ah)
+  {
+    REGEX_INTERNAL_announce_cancel (ce->ah);
+    ce->ah = NULL;
+  }
+  if (NULL != ce->sh)
+  {
+    REGEX_INTERNAL_search_cancel (ce->sh);
+    ce->sh = NULL;
+  }
+  GNUNET_free (ce);
+}
+
+
+/**
+ * Define "main" method using service macro.
+ */
+GNUNET_SERVICE_MAIN
+("regex",
+ GNUNET_SERVICE_OPTION_NONE,
+ &run,
+ &client_connect_cb,
+ &client_disconnect_cb,
+ NULL,
+ GNUNET_MQ_hd_var_size (announce,
+                        GNUNET_MESSAGE_TYPE_REGEX_ANNOUNCE,
+                        struct AnnounceMessage,
+                        NULL),
+ GNUNET_MQ_hd_var_size (search,
+                        GNUNET_MESSAGE_TYPE_REGEX_SEARCH,
+                        struct RegexSearchMessage,
+                        NULL),
+ GNUNET_MQ_handler_end ());
+
+
+/* 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 (file)
index 0000000..9e909e5
Binary files /dev/null and b/src/regex/perf-data.tar.gz differ
diff --git a/src/regex/perf-regex.c b/src/regex/perf-regex.c
new file mode 100644 (file)
index 0000000..b507e75
--- /dev/null
@@ -0,0 +1,126 @@
+/*
+     This file is part of GNUnet.
+     Copyright (C) 2012 GNUnet e.V.
+
+     GNUnet is free software: you can redistribute it and/or modify it
+     under the terms of the GNU Affero General Public License as published
+     by the Free Software Foundation, either version 3 of the License,
+     or (at your option) any later version.
+
+     GNUnet is distributed in the hope that it will be useful, but
+     WITHOUT ANY WARRANTY; without even the implied warranty of
+     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+     Affero General Public License for more details.
+    
+     You should have received a copy of the GNU Affero General Public License
+     along with this program.  If not, see <http://www.gnu.org/licenses/>.
+*/
+
+/**
+ * @file src/regex/perf-regex.c
+ * @brief Test how long it takes to create a automaton from a string regex.
+ * @author Bartlomiej Polot
+ */
+#include <regex.h>
+#include <time.h>
+#include "platform.h"
+#include "regex_internal_lib.h"
+#include "regex_test_lib.h"
+
+
+/**
+ * Print information about the given node and its edges
+ * to stdout.
+ *
+ * @param cls closure, unused.
+ * @param key hash for current state.
+ * @param proof proof for current state.
+ * @param accepting GNUNET_YES if this is an accepting state, GNUNET_NO if not.
+ * @param num_edges number of edges leaving current state.
+ * @param edges edges leaving current state.
+ */
+static void
+print_edge (void *cls,
+           const struct GNUNET_HashCode *key,
+           const char *proof,
+           int accepting,
+           unsigned int num_edges,
+           const struct REGEX_BLOCK_Edge *edges)
+{
+  unsigned int i;
+
+  printf ("%s: %s, proof: `%s'\n",
+         GNUNET_h2s (key),
+         accepting ? "ACCEPTING" : "",
+         proof);
+  for (i = 0; i < num_edges; i++)
+    printf ("    `%s': %s\n",
+           edges[i].label,
+           GNUNET_h2s (&edges[i].destination));
+}
+
+
+/**
+ * The main function of the regex performace test.
+ *
+ * Read a set of regex from a file, combine them and create a DFA from the
+ * resulting combined regex.
+ *
+ * @param argc number of arguments from the command line
+ * @param argv command line arguments
+ * @return 0 ok, 1 on error
+ */
+int
+main (int argc, char *const *argv)
+{
+  struct REGEX_INTERNAL_Automaton* dfa;
+  char **regexes;
+  char *buffer;
+  char *regex;
+  int compression;
+  unsigned int alphabet_size;
+  long size;
+
+  GNUNET_log_setup ("perf-regex", "DEBUG", NULL);
+  if (4 != argc)
+  {
+    fprintf (stderr,
+            "Usage: %s REGEX_FILE ALPHABET_SIZE COMPRESSION\n",
+            argv[0]);
+    return 1;
+  }
+  regexes = REGEX_TEST_read_from_file (argv[1]);
+  if (NULL == regexes)
+  {
+    fprintf (stderr,
+            "Failed to read regexes from `%s'\n",
+            argv[1]);
+    return 2;
+  }
+  alphabet_size = atoi (argv[2]);
+  compression = atoi (argv[3]);
+  printf ("********* PERF-REGEX *********'\n");
+  printf ("Using:\n file '%s'\n Alphabet size %u\n compression %d\n",
+          argv[1], alphabet_size, compression);
+  fflush(stdout);
+  buffer = REGEX_TEST_combine (regexes, alphabet_size);
+  GNUNET_asprintf (&regex, "GNUNET_REGEX_PROFILER_(%s)(0|1)*", buffer);
+  size = strlen (regex);
+
+  fprintf (stderr,
+          "Combined regex (%ld bytes):\n%s\n",
+          size,
+          regex);
+  dfa = REGEX_INTERNAL_construct_dfa (regex, size, compression);
+  printf ("********* ALL EDGES *********'\n");
+  REGEX_INTERNAL_iterate_all_edges (dfa, &print_edge, NULL);
+  printf ("\n\n********* REACHABLE EDGES *********'\n");
+  REGEX_INTERNAL_iterate_reachable_edges (dfa, &print_edge, NULL);
+  REGEX_INTERNAL_automaton_destroy (dfa);
+  GNUNET_free (buffer);
+  REGEX_TEST_free_from_file (regexes);
+  GNUNET_free (regex);
+  return 0;
+}
+
+/* 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 (file)
index 0000000..76045ef
--- /dev/null
@@ -0,0 +1,402 @@
+/*
+     This file is part of GNUnet
+     Copyright (C) 2013 GNUnet e.V.
+
+     GNUnet is free software: you can redistribute it and/or modify it
+     under the terms of the GNU Affero General Public License as published
+     by the Free Software Foundation, either version 3 of the License,
+     or (at your option) any later version.
+
+     GNUnet is distributed in the hope that it will be useful, but
+     WITHOUT ANY WARRANTY; without even the implied warranty of
+     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+     Affero General Public License for more details.
+    
+     You should have received a copy of the GNU Affero General Public License
+     along with this program.  If not, see <http://www.gnu.org/licenses/>.
+*/
+
+/**
+ * @file regex/plugin_block_regex.c
+ * @brief blocks used for regex storage and search
+ * @author Bartlomiej Polot
+ */
+#include "platform.h"
+#include "gnunet_block_plugin.h"
+#include "gnunet_block_group_lib.h"
+#include "block_regex.h"
+#include "regex_block_lib.h"
+#include "gnunet_signatures.h"
+
+
+/**
+ * Number of bits we set per entry in the bloomfilter.
+ * Do not change!
+ */
+#define BLOOMFILTER_K 16
+
+
+/**
+ * How big is the BF we use for REGEX blocks?
+ */
+#define REGEX_BF_SIZE 8
+
+
+/**
+ * Create a new block group.
+ *
+ * @param ctx block context in which the block group is created
+ * @param type type of the block for which we are creating the group
+ * @param nonce random value used to seed the group creation
+ * @param raw_data optional serialized prior state of the group, NULL if unavailable/fresh
+ * @param raw_data_size number of bytes in @a raw_data, 0 if unavailable/fresh
+ * @param va variable arguments specific to @a type
+ * @return block group handle, NULL if block groups are not supported
+ *         by this @a type of block (this is not an error)
+ */
+static struct GNUNET_BLOCK_Group *
+block_plugin_regex_create_group (void *cls,
+                                 enum GNUNET_BLOCK_Type type,
+                                 uint32_t nonce,
+                                 const void *raw_data,
+                                 size_t raw_data_size,
+                                 va_list va)
+{
+  unsigned int bf_size;
+  const char *guard;
+
+  guard = va_arg (va, const char *);
+  if (0 == strcmp (guard,
+                   "seen-set-size"))
+    bf_size = GNUNET_BLOCK_GROUP_compute_bloomfilter_size (va_arg (va, unsigned int),
+                                                           BLOOMFILTER_K);
+  else if (0 == strcmp (guard,
+                        "filter-size"))
+    bf_size = va_arg (va, unsigned int);
+  else
+  {
+    GNUNET_break (0);
+    bf_size = REGEX_BF_SIZE;
+  }
+  GNUNET_break (NULL == va_arg (va, const char *));
+  return GNUNET_BLOCK_GROUP_bf_create (cls,
+                                       bf_size,
+                                       BLOOMFILTER_K,
+                                       type,
+                                       nonce,
+                                       raw_data,
+                                       raw_data_size);
+}
+
+
+/**
+ * Function called to validate a reply or a request of type
+ * #GNUNET_BLOCK_TYPE_REGEX.
+ * For request evaluation, pass "NULL" for the reply_block.
+ * Note that it is assumed that the reply has already been
+ * matched to the key (and signatures checked) as it would
+ * be done with the #GNUNET_BLOCK_get_key() function.
+ *
+ * @param cls closure
+ * @param type block type
+ * @param bg block group to evaluate against
+ * @param eo control flags
+ * @param query original query (hash)
+ * @param xquery extrended query data (can be NULL, depending on type)
+ * @param xquery_size number of bytes in @a xquery
+ * @param reply_block response to validate
+ * @param reply_block_size number of bytes in @a reply_block
+ * @return characterization of result
+ */
+static enum GNUNET_BLOCK_EvaluationResult
+evaluate_block_regex (void *cls,
+                      enum GNUNET_BLOCK_Type type,
+                      struct GNUNET_BLOCK_Group *bg,
+                      enum GNUNET_BLOCK_EvaluationOptions eo,
+                      const struct GNUNET_HashCode *query,
+                      const void *xquery,
+                      size_t xquery_size,
+                      const void *reply_block,
+                      size_t reply_block_size)
+{
+  struct GNUNET_HashCode chash;
+
+  if (NULL == reply_block)
+  {
+    if (0 != xquery_size)
+      {
+        const char *s;
+
+        s = (const char *) xquery;
+        if ('\0' != s[xquery_size - 1]) /* must be valid 0-terminated string */
+          {
+            GNUNET_break_op (0);
+            return GNUNET_BLOCK_EVALUATION_REQUEST_INVALID;
+          }
+      }
+    return GNUNET_BLOCK_EVALUATION_REQUEST_VALID;
+  }
+  if (0 != xquery_size)
+  {
+    const char *s;
+
+    s = (const char *) xquery;
+    if ('\0' != s[xquery_size - 1]) /* must be valid 0-terminated string */
+    {
+      GNUNET_break_op (0);
+      return GNUNET_BLOCK_EVALUATION_REQUEST_INVALID;
+    }
+  }
+  else if (NULL != query)
+  {
+    /* xquery is required for regex GETs, at least an empty string */
+    GNUNET_break_op (0);
+    GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "type %d, query %p, xquery %p\n",
+                type, query, xquery);
+    return GNUNET_BLOCK_EVALUATION_REQUEST_INVALID;
+  }
+  switch (REGEX_BLOCK_check (reply_block,
+                            reply_block_size,
+                            query,
+                            xquery))
+  {
+    case GNUNET_SYSERR:
+      GNUNET_break_op(0);
+      return GNUNET_BLOCK_EVALUATION_RESULT_INVALID;
+    case GNUNET_NO:
+      /* xquery missmatch, can happen */
+      return GNUNET_BLOCK_EVALUATION_RESULT_IRRELEVANT;
+    default:
+      break;
+  }
+  GNUNET_CRYPTO_hash (reply_block,
+                      reply_block_size,
+                      &chash);
+  if (GNUNET_YES ==
+      GNUNET_BLOCK_GROUP_bf_test_and_set (bg,
+                                          &chash))
+    return GNUNET_BLOCK_EVALUATION_OK_DUPLICATE;
+  return GNUNET_BLOCK_EVALUATION_OK_MORE;
+}
+
+
+/**
+ * Function called to validate a reply or a request of type
+ * #GNUNET_BLOCK_TYPE_REGEX_ACCEPT.
+ * For request evaluation, pass "NULL" for the reply_block.
+ * Note that it is assumed that the reply has already been
+ * matched to the key (and signatures checked) as it would
+ * be done with the #GNUNET_BLOCK_get_key() function.
+ *
+ * @param cls closure
+ * @param type block type
+ * @param bg block group to evaluate against
+ * @param eo control flags
+ * @param query original query (hash)
+ * @param xquery extrended query data (can be NULL, depending on type)
+ * @param xquery_size number of bytes in @a xquery
+ * @param reply_block response to validate
+ * @param reply_block_size number of bytes in @a reply_block
+ * @return characterization of result
+ */
+static enum GNUNET_BLOCK_EvaluationResult
+evaluate_block_regex_accept (void *cls,
+                             enum GNUNET_BLOCK_Type type,
+                             struct GNUNET_BLOCK_Group *bg,
+                             enum GNUNET_BLOCK_EvaluationOptions eo,
+                             const struct GNUNET_HashCode *query,
+                             const void *xquery,
+                             size_t xquery_size, const void *reply_block,
+                             size_t reply_block_size)
+{
+  const struct RegexAcceptBlock *rba;
+  struct GNUNET_HashCode chash;
+
+  if (0 != xquery_size)
+  {
+    GNUNET_break_op (0);
+    return GNUNET_BLOCK_EVALUATION_REQUEST_INVALID;
+  }
+  if (NULL == reply_block)
+    return GNUNET_BLOCK_EVALUATION_REQUEST_VALID;
+  if (sizeof (struct RegexAcceptBlock) != reply_block_size)
+  {
+    GNUNET_break_op(0);
+    return GNUNET_BLOCK_EVALUATION_RESULT_INVALID;
+  }
+  rba = reply_block;
+  if (ntohl (rba->purpose.size) !=
+      sizeof (struct GNUNET_CRYPTO_EccSignaturePurpose) +
+      sizeof (struct GNUNET_TIME_AbsoluteNBO) +
+      sizeof (struct GNUNET_HashCode))
+  {
+    GNUNET_break_op(0);
+    return GNUNET_BLOCK_EVALUATION_RESULT_INVALID;
+  }
+  if (0 == GNUNET_TIME_absolute_get_remaining (GNUNET_TIME_absolute_ntoh (rba->expiration_time)).rel_value_us)
+  {
+    /* technically invalid, but can happen without an error, so
+       we're nice by reporting it as a 'duplicate' */
+    return GNUNET_BLOCK_EVALUATION_OK_DUPLICATE;
+  }
+  if (GNUNET_OK !=
+      GNUNET_CRYPTO_eddsa_verify (GNUNET_SIGNATURE_PURPOSE_REGEX_ACCEPT,
+                               &rba->purpose,
+                               &rba->signature,
+                               &rba->peer.public_key))
+  {
+    GNUNET_break_op(0);
+    return GNUNET_BLOCK_EVALUATION_RESULT_INVALID;
+  }
+  GNUNET_CRYPTO_hash (reply_block,
+                      reply_block_size,
+                      &chash);
+  if (GNUNET_YES ==
+      GNUNET_BLOCK_GROUP_bf_test_and_set (bg,
+                                          &chash))
+    return GNUNET_BLOCK_EVALUATION_OK_DUPLICATE;
+  return GNUNET_BLOCK_EVALUATION_OK_MORE;
+}
+
+
+/**
+ * Function called to validate a reply or a request.  For
+ * request evaluation, simply pass "NULL" for the reply_block.
+ * Note that it is assumed that the reply has already been
+ * matched to the key (and signatures checked) as it would
+ * be done with the #GNUNET_BLOCK_get_key() function.
+ *
+ * @param cls closure
+ * @param ctx block context
+ * @param type block type
+ * @param bg group to evaluate against
+ * @param eo control flags
+ * @param query original query (hash)
+ * @param xquery extrended query data (can be NULL, depending on type)
+ * @param xquery_size number of bytes in xquery
+ * @param reply_block response to validate
+ * @param reply_block_size number of bytes in reply block
+ * @return characterization of result
+ */
+static enum GNUNET_BLOCK_EvaluationResult
+block_plugin_regex_evaluate (void *cls,
+                             struct GNUNET_BLOCK_Context *ctx,
+                             enum GNUNET_BLOCK_Type type,
+                             struct GNUNET_BLOCK_Group *bg,
+                             enum GNUNET_BLOCK_EvaluationOptions eo,
+                             const struct GNUNET_HashCode *query,
+                             const void *xquery,
+                             size_t xquery_size,
+                             const void *reply_block,
+                             size_t reply_block_size)
+{
+  enum GNUNET_BLOCK_EvaluationResult result;
+
+  switch (type)
+  {
+    case GNUNET_BLOCK_TYPE_REGEX:
+      result = evaluate_block_regex (cls,
+                                     type,
+                                     bg,
+                                     eo,
+                                     query,
+                                     xquery, xquery_size,
+                                     reply_block, reply_block_size);
+      break;
+    case GNUNET_BLOCK_TYPE_REGEX_ACCEPT:
+      result = evaluate_block_regex_accept (cls,
+                                            type,
+                                            bg,
+                                            eo,
+                                            query,
+                                            xquery, xquery_size,
+                                            reply_block, reply_block_size);
+      break;
+
+    default:
+      result = GNUNET_BLOCK_EVALUATION_TYPE_NOT_SUPPORTED;
+  }
+  return result;
+}
+
+
+/**
+ * Function called to obtain the key for a block.
+ *
+ * @param cls closure
+ * @param type block type
+ * @param block block to get the key for
+ * @param block_size number of bytes in @a block
+ * @param key set to the key (query) for the given block
+ * @return #GNUNET_OK on success, #GNUNET_SYSERR if type not supported
+ *         (or if extracting a key from a block of this type does not work)
+ */
+static int
+block_plugin_regex_get_key (void *cls,
+                            enum GNUNET_BLOCK_Type type,
+                            const void *block,
+                            size_t block_size,
+                            struct GNUNET_HashCode *key)
+{
+  switch (type)
+  {
+    case GNUNET_BLOCK_TYPE_REGEX:
+      if (GNUNET_OK !=
+         REGEX_BLOCK_get_key (block, block_size,
+                              key))
+      {
+       GNUNET_break_op (0);
+       return GNUNET_NO;
+      }
+      return GNUNET_OK;
+    case GNUNET_BLOCK_TYPE_REGEX_ACCEPT:
+      if (sizeof (struct RegexAcceptBlock) != block_size)
+      {
+       GNUNET_break_op (0);
+       return GNUNET_NO;
+      }
+      *key = ((struct RegexAcceptBlock *) block)->key;
+      return GNUNET_OK;
+    default:
+      GNUNET_break (0);
+      return GNUNET_SYSERR;
+  }
+}
+
+
+/**
+ * Entry point for the plugin.
+ */
+void *
+libgnunet_plugin_block_regex_init (void *cls)
+{
+  static enum GNUNET_BLOCK_Type types[] =
+  {
+    GNUNET_BLOCK_TYPE_REGEX,
+    GNUNET_BLOCK_TYPE_REGEX_ACCEPT,
+    GNUNET_BLOCK_TYPE_ANY       /* end of list */
+  };
+  struct GNUNET_BLOCK_PluginFunctions *api;
+
+  api = GNUNET_new (struct GNUNET_BLOCK_PluginFunctions);
+  api->evaluate = &block_plugin_regex_evaluate;
+  api->get_key = &block_plugin_regex_get_key;
+  api->create_group = &block_plugin_regex_create_group;
+  api->types = types;
+  return api;
+}
+
+
+/**
+ * Exit point from the plugin.
+ */
+void *
+libgnunet_plugin_block_regex_done (void *cls)
+{
+  struct GNUNET_BLOCK_PluginFunctions *api = cls;
+
+  GNUNET_free (api);
+  return NULL;
+}
+
+/* end of plugin_block_regex.c */
diff --git a/src/regex/regex.conf.in b/src/regex/regex.conf.in
new file mode 100644 (file)
index 0000000..5e68a43
--- /dev/null
@@ -0,0 +1,8 @@
+[regex]
+START_ON_DEMAND = @START_ON_DEMAND@
+@UNIXONLY@ PORT = 2107
+UNIXPATH = $GNUNET_RUNTIME_DIR/gnunet-service-regex.sock
+HOSTNAME = localhost
+BINARY = gnunet-service-regex
+ACCEPT_FROM = 127.0.0.1;
+ACCEPT_FROM6 = ::1;
diff --git a/src/regex/regex_api_announce.c b/src/regex/regex_api_announce.c
new file mode 100644 (file)
index 0000000..e3ad70c
--- /dev/null
@@ -0,0 +1,184 @@
+/*
+     This file is part of GNUnet
+     Copyright (C) 2012, 2013, 2016 GNUnet e.V.
+
+     GNUnet is free software: you can redistribute it and/or modify it
+     under the terms of the GNU Affero General Public License as published
+     by the Free Software Foundation, either version 3 of the License,
+     or (at your option) any later version.
+
+     GNUnet is distributed in the hope that it will be useful, but
+     WITHOUT ANY WARRANTY; without even the implied warranty of
+     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+     Affero General Public License for more details.
+    
+     You should have received a copy of the GNU Affero General Public License
+     along with this program.  If not, see <http://www.gnu.org/licenses/>.
+*/
+/**
+ * @file regex/regex_api_announce.c
+ * @brief access regex service to advertise capabilities via regex
+ * @author Maximilian Szengel
+ * @author Christian Grothoff
+ */
+#include "platform.h"
+#include "gnunet_protocols.h"
+#include "gnunet_util_lib.h"
+#include "gnunet_regex_service.h"
+#include "regex_ipc.h"
+
+#define LOG(kind,...) GNUNET_log_from (kind, "regex-api",__VA_ARGS__)
+
+/**
+ * Handle to store cached data about a regex announce.
+ */
+struct GNUNET_REGEX_Announcement
+{
+  /**
+   * Connection to the regex service.
+   */
+  struct GNUNET_MQ_Handle *mq;
+
+  /**
+   * Our configuration.
+   */
+  const struct GNUNET_CONFIGURATION_Handle *cfg;
+
+  /**
+   * Message we're sending to the service.
+   */
+  char *regex;
+
+  /**
+   * Frequency of announcements.
+   */
+  struct GNUNET_TIME_Relative refresh_delay;
+
+  /**
+   * Number of characters per edge.
+   */
+  uint16_t compression;
+};
+
+
+
+/**
+ * (Re)connect to the REGEX service with the given announcement @a a.
+ *
+ * @param a REGEX to announce.
+ */
+static void
+announce_reconnect (struct GNUNET_REGEX_Announcement *a);
+
+
+/**
+ * We got a disconnect after asking regex to do the announcement.
+ * Retry.
+ *
+ * @param cls the `struct GNUNET_REGEX_Announcement` to retry
+ * @param error error code
+ */
+static void
+announce_mq_error_handler (void *cls,
+                           enum GNUNET_MQ_Error error)
+{
+  struct GNUNET_REGEX_Announcement *a = cls;
+
+  GNUNET_MQ_destroy (a->mq);
+  a->mq = NULL;
+  announce_reconnect (a);
+}
+
+
+/**
+ * (Re)connect to the REGEX service with the given announcement @a a.
+ *
+ * @param a REGEX to announce.
+ */
+static void
+announce_reconnect (struct GNUNET_REGEX_Announcement *a)
+{
+  struct GNUNET_MQ_Envelope *env;
+  struct AnnounceMessage *am;
+  size_t slen;
+
+  a->mq = GNUNET_CLIENT_connect (a->cfg,
+                                 "regex",
+                                 NULL,
+                                 &announce_mq_error_handler,
+                                 a);
+  if (NULL == a->mq)
+    return;
+  slen = strlen (a->regex) + 1;
+  env = GNUNET_MQ_msg_extra (am,
+                             slen,
+                             GNUNET_MESSAGE_TYPE_REGEX_ANNOUNCE);
+  am->compression = htons (a->compression);
+  am->reserved = htons (0);
+  am->refresh_delay = GNUNET_TIME_relative_hton (a->refresh_delay);
+  GNUNET_memcpy (&am[1],
+          a->regex,
+          slen);
+  GNUNET_MQ_send (a->mq,
+                  env);
+}
+
+
+/**
+ * Announce the given peer under the given regular expression.
+ *
+ * @param cfg configuration to use
+ * @param regex Regular expression to announce.
+ * @param refresh_delay after what delay should the announcement be repeated?
+ * @param compression How many characters per edge can we squeeze?
+ * @return Handle to reuse o free cached resources.
+ *         Must be freed by calling #GNUNET_REGEX_announce_cancel().
+ */
+struct GNUNET_REGEX_Announcement *
+GNUNET_REGEX_announce (const struct GNUNET_CONFIGURATION_Handle *cfg,
+                       const char *regex,
+                      struct GNUNET_TIME_Relative refresh_delay,
+                       uint16_t compression)
+{
+  struct GNUNET_REGEX_Announcement *a;
+  size_t slen;
+
+  slen = strlen (regex) + 1;
+  if (slen + sizeof (struct AnnounceMessage) >= GNUNET_MAX_MESSAGE_SIZE)
+  {
+    GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
+                _("Regex `%s' is too long!\n"),
+                regex);
+    GNUNET_break (0);
+    return NULL;
+  }
+  a = GNUNET_new (struct GNUNET_REGEX_Announcement);
+  a->cfg = cfg;
+  a->refresh_delay = refresh_delay;
+  a->compression = compression;
+  a->regex = GNUNET_strdup (regex);
+  announce_reconnect (a);
+  if (NULL == a->mq)
+  {
+    GNUNET_free (a->regex);
+    GNUNET_free (a);
+    return NULL;
+  }
+  return a;
+}
+
+
+/**
+ * Stop announcing the regex specified by the given handle.
+ *
+ * @param a handle returned by a previous #GNUNET_REGEX_announce() call.
+ */
+void
+GNUNET_REGEX_announce_cancel (struct GNUNET_REGEX_Announcement *a)
+{
+  GNUNET_MQ_destroy (a->mq);
+  GNUNET_free (a->regex);
+  GNUNET_free (a);
+}
+
+/* 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 (file)
index 0000000..2e2536a
--- /dev/null
@@ -0,0 +1,248 @@
+/*
+     This file is part of GNUnet
+     Copyright (C) 2012, 2013, 2016 GNUnet e.V.
+
+     GNUnet is free software: you can redistribute it and/or modify it
+     under the terms of the GNU Affero General Public License as published
+     by the Free Software Foundation, either version 3 of the License,
+     or (at your option) any later version.
+
+     GNUnet is distributed in the hope that it will be useful, but
+     WITHOUT ANY WARRANTY; without even the implied warranty of
+     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+     Affero General Public License for more details.
+    
+     You should have received a copy of the GNU Affero General Public License
+     along with this program.  If not, see <http://www.gnu.org/licenses/>.
+*/
+/**
+ * @file regex/regex_api_search.c
+ * @brief access regex service to discover
+ *        peers using matching strings
+ * @author Maximilian Szengel
+ * @author Christian Grothoff
+ */
+#include "platform.h"
+#include "gnunet_protocols.h"
+#include "gnunet_util_lib.h"
+#include "gnunet_regex_service.h"
+#include "regex_ipc.h"
+
+#define LOG(kind,...) GNUNET_log_from (kind, "regex-api",__VA_ARGS__)
+
+
+/**
+ * Handle to store data about a regex search.
+ */
+struct GNUNET_REGEX_Search
+{
+  /**
+   * Connection to the regex service.
+   */
+  struct GNUNET_MQ_Handle *mq;
+
+  /**
+   * Our configuration.
+   */
+  const struct GNUNET_CONFIGURATION_Handle *cfg;
+
+  /**
+   * Function to call with results.
+   */
+  GNUNET_REGEX_Found callback;
+
+  /**
+   * Closure for @e callback.
+   */
+  void *callback_cls;
+
+  /**
+   * Search string to transmit to the service.
+   */
+  char *string;
+};
+
+
+/**
+ * (Re)connect to the REGEX service for the given search @a s.
+ *
+ * @param s context for the search search for
+ */
+static void
+search_reconnect (struct GNUNET_REGEX_Search *s);
+
+
+/**
+ * We got a response or disconnect after asking regex
+ * to do the search.  Check it is well-formed.
+ *
+ * @param cls the `struct GNUNET_REGEX_Search` to handle reply for
+ * @param result the message
+ * @return #GNUNET_SYSERR if @a rm is not well-formed.
+ */
+static int
+check_search_response (void *cls,
+                       const struct ResultMessage *result)
+{
+  uint16_t size = ntohs (result->header.size) - sizeof (*result);
+  uint16_t gpl = ntohs (result->get_path_length);
+  uint16_t ppl = ntohs (result->put_path_length);
+
+  if (size != (gpl + ppl) * sizeof (struct GNUNET_PeerIdentity))
+  {
+    GNUNET_break (0);
+    return GNUNET_SYSERR;
+  }
+  return GNUNET_OK;
+}
+
+
+/**
+ * We got a response or disconnect after asking regex
+ * to do the search.  Handle it.
+ *
+ * @param cls the `struct GNUNET_REGEX_Search` to handle reply for
+ * @param result the message
+ */
+static void
+handle_search_response (void *cls,
+                       const struct ResultMessage *result)
+{
+  struct GNUNET_REGEX_Search *s = cls;
+  uint16_t gpl = ntohs (result->get_path_length);
+  uint16_t ppl = ntohs (result->put_path_length);
+  const struct GNUNET_PeerIdentity *pid;
+
+  pid = &result->id;
+  LOG (GNUNET_ERROR_TYPE_DEBUG,
+       "Got regex result %s\n",
+       GNUNET_i2s (pid));
+  s->callback (s->callback_cls,
+               pid,
+               &pid[1],
+               gpl,
+               &pid[1 + gpl],
+               ppl);
+}
+
+
+/**
+ * We got a disconnect after asking regex to do the announcement.
+ * Retry.
+ *
+ * @param cls the `struct GNUNET_REGEX_Search` to retry
+ * @param error error code
+ */
+static void
+mq_error_handler (void *cls,
+                  enum GNUNET_MQ_Error error)
+{
+  struct GNUNET_REGEX_Search *s = cls;
+
+  GNUNET_MQ_destroy (s->mq);
+  s->mq = NULL;
+  search_reconnect (s);
+}
+
+
+/**
+ * (Re)connect to the REGEX service for the given search @a s.
+ *
+ * @param s context for the search search for
+ */
+static void
+search_reconnect (struct GNUNET_REGEX_Search *s)
+{
+  struct GNUNET_MQ_MessageHandler handlers[] = {
+    GNUNET_MQ_hd_var_size (search_response,
+                           GNUNET_MESSAGE_TYPE_REGEX_RESULT,
+                           struct ResultMessage,
+                           s),
+    GNUNET_MQ_handler_end ()
+  };
+  size_t slen = strlen (s->string) + 1;
+  struct GNUNET_MQ_Envelope *env;
+  struct RegexSearchMessage *rsm;
+
+  GNUNET_assert (NULL == s->mq);
+  s->mq = GNUNET_CLIENT_connect (s->cfg,
+                                 "regex",
+                                 handlers,
+                                 &mq_error_handler,
+                                 s);
+  if (NULL == s->mq)
+    return;
+  env = GNUNET_MQ_msg_extra (rsm,
+                             slen,
+                             GNUNET_MESSAGE_TYPE_REGEX_SEARCH);
+  GNUNET_memcpy (&rsm[1],
+          s->string,
+          slen);
+  GNUNET_MQ_send (s->mq,
+                  env);
+}
+
+
+/**
+ * Search for a peer offering a regex matching certain string in the DHT.
+ * The search runs until #GNUNET_REGEX_search_cancel() is called, even if results
+ * are returned.
+ *
+ * @param cfg configuration to use
+ * @param string String to match against the regexes in the DHT.
+ * @param callback Callback for found peers.
+ * @param callback_cls Closure for @c callback.
+ * @return Handle to stop search and free resources.
+ *         Must be freed by calling #GNUNET_REGEX_search_cancel().
+ */
+struct GNUNET_REGEX_Search *
+GNUNET_REGEX_search (const struct GNUNET_CONFIGURATION_Handle *cfg,
+                    const char *string,
+                     GNUNET_REGEX_Found callback,
+                     void *callback_cls)
+{
+  struct GNUNET_REGEX_Search *s;
+  size_t slen = strlen (string) + 1;
+
+  if (slen + sizeof (struct RegexSearchMessage) >= GNUNET_MAX_MESSAGE_SIZE)
+  {
+    GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
+                _("Search string `%s' is too long!\n"),
+                string);
+    GNUNET_break (0);
+    return NULL;
+  }
+  LOG (GNUNET_ERROR_TYPE_DEBUG,
+       "Starting regex search for %s\n",
+       string);
+  s = GNUNET_new (struct GNUNET_REGEX_Search);
+  s->cfg = cfg;
+  s->string = GNUNET_strdup (string);
+  s->callback = callback;
+  s->callback_cls = callback_cls;
+  search_reconnect (s);
+  if (NULL == s->mq)
+  {
+    GNUNET_free (s->string);
+    GNUNET_free (s);
+    return NULL;
+  }
+  return s;
+}
+
+
+/**
+ * Stop search and free all data used by a #GNUNET_REGEX_search() call.
+ *
+ * @param s Handle returned by a previous #GNUNET_REGEX_search() call.
+ */
+void
+GNUNET_REGEX_search_cancel (struct GNUNET_REGEX_Search *s)
+{
+  GNUNET_MQ_destroy (s->mq);
+  GNUNET_free (s->string);
+  GNUNET_free (s);
+}
+
+
+/* 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 (file)
index 0000000..33eaf46
--- /dev/null
@@ -0,0 +1,469 @@
+/*
+     This file is part of GNUnet.
+     Copyright (C) 2012,2013 GNUnet e.V.
+
+     GNUnet is free software: you can redistribute it and/or modify it
+     under the terms of the GNU Affero General Public License as published
+     by the Free Software Foundation, either version 3 of the License,
+     or (at your option) any later version.
+
+     GNUnet is distributed in the hope that it will be useful, but
+     WITHOUT ANY WARRANTY; without even the implied warranty of
+     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+     Affero General Public License for more details.
+    
+     You should have received a copy of the GNU Affero General Public License
+     along with this program.  If not, see <http://www.gnu.org/licenses/>.
+*/
+/**
+ * @author Bartlomiej Polot
+ * @file regex/regex_block_lib.c
+ * @brief functions for manipulating non-accept blocks stored for
+ *        regex in the DHT
+ */
+#include "platform.h"
+#include "regex_block_lib.h"
+#include "gnunet_constants.h"
+
+#define LOG(kind,...) GNUNET_log_from (kind,"regex-bck",__VA_ARGS__)
+
+GNUNET_NETWORK_STRUCT_BEGIN
+
+/**
+ * Information for each edge.
+ */
+struct EdgeInfo
+{
+  /**
+   * Index of the destination of this edge in the
+   * unique destinations array.
+   */
+  uint16_t destination_index GNUNET_PACKED;
+
+  /**
+   * Number of bytes the token for this edge takes in the
+   * token area.
+   */
+  uint16_t token_length GNUNET_PACKED;
+};
+
+
+/**
+ * @brief Block to announce a regex state.
+ */
+struct RegexBlock
+{
+
+  /**
+   * Length of the proof regex string.
+   */
+  uint16_t proof_len GNUNET_PACKED;
+
+  /**
+   * Is this state an accepting state?
+   */
+  int16_t is_accepting GNUNET_PACKED;
+
+  /**
+   * Number of edges parting from this state.
+   */
+  uint16_t num_edges GNUNET_PACKED;
+
+  /**
+   * Nubmer of unique destinations reachable from this state.
+   */
+  uint16_t num_destinations GNUNET_PACKED;
+
+  /* followed by 'struct GNUNET_HashCode[num_destinations]' */
+
+  /* followed by 'struct EdgeInfo[edge_destination_indices]' */
+
+  /* followed by 'char proof[n_proof]', NOT 0-terminated */
+
+  /* followed by 'char tokens[num_edges][edge_info[k].token_length]';
+     essentially all of the tokens one after the other in the
+     order of the edges; tokens are NOT 0-terminated */
+
+};
+
+
+GNUNET_NETWORK_STRUCT_END
+
+
+/**
+ * Test if this block is marked as being an accept state.
+ *
+ * @param block block to test
+ * @param size number of bytes in block
+ * @return #GNUNET_YES if the block is accepting, #GNUNET_NO if not
+ */
+int
+GNUNET_BLOCK_is_accepting (const struct RegexBlock *block,
+                          size_t size)
+{
+  if (size < sizeof (struct RegexBlock))
+  {
+    GNUNET_break_op (0);
+    return GNUNET_SYSERR;
+  }
+  return ntohs (block->is_accepting);
+}
+
+
+/**
+ * Check if the given 'proof' matches the given 'key'.
+ *
+ * @param proof partial regex of a state
+ * @param proof_len number of bytes in 'proof'
+ * @param key hash of a state.
+ * @return #GNUNET_OK if the proof is valid for the given key.
+ */
+int
+REGEX_BLOCK_check_proof (const char *proof,
+                        size_t proof_len,
+                        const struct GNUNET_HashCode *key)
+{
+  struct GNUNET_HashCode key_check;
+
+  if ( (NULL == proof) || (NULL == key))
+  {
+    GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Proof check failed, was NULL.\n");
+    return GNUNET_NO;
+  }
+  GNUNET_CRYPTO_hash (proof, proof_len, &key_check);
+  return (0 ==
+          GNUNET_CRYPTO_hash_cmp (key, &key_check)) ? GNUNET_OK : GNUNET_NO;
+}
+
+
+/**
+ * Struct to keep track of the xquery while iterating all the edges in a block.
+ */
+struct CheckEdgeContext
+{
+  /**
+   * Xquery: string we are looking for.
+   */
+  const char *xquery;
+
+  /**
+   * Has any edge matched the xquery so far? (GNUNET_OK / GNUNET_NO)
+   */
+  int found;
+
+};
+
+
+/**
+ * Iterator over all edges in a block, checking for a presence of a given query.
+ *
+ * @param cls Closure, (xquery context).
+ * @param token Token that follows to next state.
+ * @param len Lenght of token.
+ * @param key Hash of next state.
+ *
+ * @return #GNUNET_YES, to keep iterating
+ */
+static int
+check_edge (void *cls,
+            const char *token,
+            size_t len,
+            const struct GNUNET_HashCode *key)
+{
+  struct CheckEdgeContext *ctx = cls;
+
+  GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+             "edge %.*s [%u]: %s\n",
+              (int) len,
+              token,
+              (unsigned int) len,
+              GNUNET_h2s (key));
+  if (NULL == ctx->xquery)
+    return GNUNET_YES;
+  if (strlen (ctx->xquery) < len)
+    return GNUNET_YES; /* too long */
+  if (0 == strncmp (ctx->xquery, token, len))
+    ctx->found = GNUNET_OK;
+  return GNUNET_YES; /* keep checking for malformed data! */
+}
+
+
+/**
+ * Check if the regex block is well formed, including all edges.
+ *
+ * @param block The start of the block.
+ * @param size The size of the block.
+ * @param query the query for the block
+ * @param xquery String describing the edge we are looking for.
+ *               Can be NULL in case this is a put block.
+ * @return #GNUNET_OK in case it's fine.
+ *         #GNUNET_NO in case the xquery exists and is not found (IRRELEVANT).
+ *         #GNUNET_SYSERR if the block is invalid.
+ */
+int
+REGEX_BLOCK_check (const struct RegexBlock *block,
+                  size_t size,
+                  const struct GNUNET_HashCode *query,
+                  const char *xquery)
+{
+  struct GNUNET_HashCode key;
+  struct CheckEdgeContext ctx;
+  int res;
+
+  LOG (GNUNET_ERROR_TYPE_DEBUG,
+       "Block check\n");
+  if (GNUNET_OK !=
+      REGEX_BLOCK_get_key (block, size,
+                          &key))
+  {
+    GNUNET_break_op (0);
+    return GNUNET_SYSERR;
+  }
+  if (NULL != query &&
+      0 != memcmp (&key,
+                   query,
+                   sizeof (struct GNUNET_HashCode)))
+  {
+    GNUNET_break_op (0);
+    return GNUNET_SYSERR;
+  }
+  if ( (GNUNET_YES == ntohs (block->is_accepting)) &&
+       ( (NULL == xquery) || ('\0' == xquery[0]) ) )
+  {
+    LOG (GNUNET_ERROR_TYPE_DEBUG,
+         "  out! Is accepting: %u, xquery %p\n",
+         ntohs(block->is_accepting),
+         xquery);
+    return GNUNET_OK;
+  }
+  ctx.xquery = xquery;
+  ctx.found = GNUNET_NO;
+  res = REGEX_BLOCK_iterate (block, size, &check_edge, &ctx);
+  if (GNUNET_SYSERR == res)
+    return GNUNET_SYSERR;
+  if (NULL == xquery)
+    return GNUNET_YES;
+  LOG (GNUNET_ERROR_TYPE_DEBUG, "Result %d\n", ctx.found);
+  return ctx.found;
+}
+
+
+/**
+ * Obtain the key that a particular block is to be stored under.
+ *
+ * @param block block to get the key from
+ * @param block_len number of bytes in block
+ * @param key where to store the key
+ * @return #GNUNET_OK on success, #GNUNET_SYSERR if the block is malformed
+ */
+int
+REGEX_BLOCK_get_key (const struct RegexBlock *block,
+                     size_t block_len,
+                     struct GNUNET_HashCode *key)
+{
+  uint16_t len;
+  const struct GNUNET_HashCode *destinations;
+  const struct EdgeInfo *edges;
+  uint16_t num_destinations;
+  uint16_t num_edges;
+  size_t total;
+
+  if (block_len < sizeof (struct RegexBlock))
+  {
+    GNUNET_break_op (0);
+    return GNUNET_SYSERR;
+  }
+  num_destinations = ntohs (block->num_destinations);
+  num_edges = ntohs (block->num_edges);
+  len = ntohs (block->proof_len);
+  destinations = (const struct GNUNET_HashCode *) &block[1];
+  edges = (const struct EdgeInfo *) &destinations[num_destinations];
+  total = sizeof (struct RegexBlock) + num_destinations * sizeof (struct GNUNET_HashCode) + num_edges * sizeof (struct EdgeInfo) + len;
+  if (block_len < total)
+  {
+    GNUNET_break_op (0);
+    return GNUNET_SYSERR;
+  }
+  GNUNET_CRYPTO_hash (&edges[num_edges], len, key);
+  return GNUNET_OK;
+}
+
+
+/**
+ * Iterate over all edges of a block of a regex state.
+ *
+ * @param block Block to iterate over.
+ * @param size Size of @a block.
+ * @param iterator Function to call on each edge in the block.
+ * @param iter_cls Closure for the @a iterator.
+ * @return #GNUNET_SYSERR if an error has been encountered.
+ *         #GNUNET_OK if no error has been encountered.
+ *           Note that if the iterator stops the iteration by returning
+ *         #GNUNET_NO, the block will no longer be checked for further errors.
+ *           The return value will be GNUNET_OK meaning that no errors were
+ *         found until the edge last notified to the iterator, but there might
+ *         be errors in further edges.
+ */
+int
+REGEX_BLOCK_iterate (const struct RegexBlock *block,
+                    size_t size,
+                    REGEX_INTERNAL_EgdeIterator iterator,
+                    void *iter_cls)
+{
+  uint16_t len;
+  const struct GNUNET_HashCode *destinations;
+  const struct EdgeInfo *edges;
+  const char *aux;
+  uint16_t num_destinations;
+  uint16_t num_edges;
+  size_t total;
+  unsigned int n;
+  size_t off;
+
+  LOG (GNUNET_ERROR_TYPE_DEBUG, "Block iterate\n");
+  if (size < sizeof (struct RegexBlock))
+  {
+    GNUNET_break_op (0);
+    return GNUNET_SYSERR;
+  }
+  num_destinations = ntohs (block->num_destinations);
+  num_edges = ntohs (block->num_edges);
+  len = ntohs (block->proof_len);
+  destinations = (const struct GNUNET_HashCode *) &block[1];
+  edges = (const struct EdgeInfo *) &destinations[num_destinations];
+  aux = (const char *) &edges[num_edges];
+  total = sizeof (struct RegexBlock) + num_destinations * sizeof (struct GNUNET_HashCode) + num_edges * sizeof (struct EdgeInfo) + len;
+  if (size < total)
+  {
+    GNUNET_break_op (0);
+    return GNUNET_SYSERR;
+  }
+  for (n=0;n<num_edges;n++)
+    total += ntohs (edges[n].token_length);
+  if (size != total)
+  {
+    fprintf (stderr, "Expected %u, got %u\n",
+            (unsigned int) size,
+            (unsigned int) total);
+    GNUNET_break_op (0);
+    return GNUNET_SYSERR;
+  }
+  off = len;
+  LOG (GNUNET_ERROR_TYPE_DEBUG,
+       "Start iterating block of size %u, proof %u, off %u edges %u\n",
+       size, len, off, n);
+  /* &aux[off] always points to our token */
+  for (n=0;n<num_edges;n++)
+  {
+    LOG (GNUNET_ERROR_TYPE_DEBUG,
+        "Edge %u/%u, off %u tokenlen %u (%.*s)\n",
+         n+1, num_edges, off,
+        ntohs (edges[n].token_length), ntohs (edges[n].token_length),
+         &aux[off]);
+    if (NULL != iterator)
+      if (GNUNET_NO == iterator (iter_cls,
+                                &aux[off],
+                                ntohs (edges[n].token_length),
+                                &destinations[ntohs (edges[n].destination_index)]))
+       return GNUNET_OK;
+    off += ntohs (edges[n].token_length);
+  }
+  return GNUNET_OK;
+}
+
+
+/**
+ * Construct a regex block to be stored in the DHT.
+ *
+ * @param proof proof string for the block
+ * @param num_edges number of edges in the block
+ * @param edges the edges of the block
+ * @param accepting is this an accepting state
+ * @param rsize set to the size of the returned block (OUT-only)
+ * @return the regex block, NULL on error
+ */
+struct RegexBlock *
+REGEX_BLOCK_create (const char *proof,
+                   unsigned int num_edges,
+                   const struct REGEX_BLOCK_Edge *edges,
+                   int accepting,
+                   size_t *rsize)
+{
+  struct RegexBlock *block;
+  struct GNUNET_HashCode destinations[1024]; /* 1024 = 64k/64 bytes/key == absolute MAX */
+  uint16_t destination_indices[num_edges];
+  struct GNUNET_HashCode *dests;
+  struct EdgeInfo *edgeinfos;
+  size_t off;
+  size_t len;
+  size_t total;
+  size_t slen;
+  unsigned int unique_destinations;
+  unsigned int j;
+  unsigned int i;
+  char *aux;
+
+  len = strlen (proof);
+  if (len > UINT16_MAX)
+  {
+    GNUNET_break (0);
+    return NULL;
+  }
+  unique_destinations = 0;
+  total = sizeof (struct RegexBlock) + len;
+  for (i=0;i<num_edges;i++)
+  {
+    slen = strlen (edges[i].label);
+    if (slen > UINT16_MAX)
+    {
+      GNUNET_break (0);
+      return NULL;
+    }
+    total += slen;
+    for (j=0;j<unique_destinations;j++)
+      if (0 == memcmp (&destinations[j],
+                      &edges[i].destination,
+                      sizeof (struct GNUNET_HashCode)))
+       break;
+    if (j >= 1024)
+    {
+      GNUNET_break (0);
+      return NULL;
+    }
+    destination_indices[i] = j;
+    if (j == unique_destinations)
+      destinations[unique_destinations++] = edges[i].destination;
+  }
+  total += num_edges * sizeof (struct EdgeInfo) + unique_destinations * sizeof (struct GNUNET_HashCode);
+  if (total >= GNUNET_CONSTANTS_MAX_BLOCK_SIZE)
+  {
+    GNUNET_break (0);
+    return NULL;
+  }
+  block = GNUNET_malloc (total);
+  block->proof_len = htons (len);
+  block->is_accepting = htons (accepting);
+  block->num_edges = htons (num_edges);
+  block->num_destinations = htons (unique_destinations);
+  dests = (struct GNUNET_HashCode *) &block[1];
+  GNUNET_memcpy (dests, destinations, sizeof (struct GNUNET_HashCode) * unique_destinations);
+  edgeinfos = (struct EdgeInfo *) &dests[unique_destinations];
+  aux = (char *) &edgeinfos[num_edges];
+  off = len;
+  GNUNET_memcpy (aux, proof, len);
+  for (i=0;i<num_edges;i++)
+  {
+    slen = strlen (edges[i].label);
+    edgeinfos[i].token_length = htons ((uint16_t) slen);
+    edgeinfos[i].destination_index = htons (destination_indices[i]);
+    GNUNET_memcpy (&aux[off],
+           edges[i].label,
+           slen);
+    off += slen;
+  }
+  *rsize = total;
+  return block;
+}
+
+
+/* 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 (file)
index 0000000..c5f5f31
--- /dev/null
@@ -0,0 +1,191 @@
+/*
+     This file is part of GNUnet.
+     Copyright (C) 2012,2013 GNUnet e.V.
+
+     GNUnet is free software: you can redistribute it and/or modify it
+     under the terms of the GNU Affero General Public License as published
+     by the Free Software Foundation, either version 3 of the License,
+     or (at your option) any later version.
+
+     GNUnet is distributed in the hope that it will be useful, but
+     WITHOUT ANY WARRANTY; without even the implied warranty of
+     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+     Affero General Public License for more details.
+    
+     You should have received a copy of the GNU Affero General Public License
+     along with this program.  If not, see <http://www.gnu.org/licenses/>.
+*/
+
+/**
+ * @author Bartlomiej Polot
+ * @file regex/regex_block_lib.h
+ * @brief common function to manipulate blocks stored by regex in the DHT
+ */
+
+#ifndef REGEX_BLOCK_LIB_H_
+#define REGEX_BLOCK_LIB_H_
+
+#ifdef __cplusplus
+extern "C"
+{
+#if 0
+  /* keep Emacsens' auto-indent happy */
+}
+#endif
+#endif
+
+#include "platform.h"
+#include "block_regex.h"
+
+
+/**
+ * Representation of a Regex node (and edges) in the DHT.
+ */
+struct RegexBlock;
+
+
+/**
+ * Edge representation.
+ */
+struct REGEX_BLOCK_Edge
+{
+  /**
+   * Label of the edge.  FIXME: might want to not consume exactly
+   * multiples of 8 bits, need length!
+   */
+  const char *label;
+
+  /**
+   * Destionation of the edge.
+   */
+  struct GNUNET_HashCode destination;
+};
+
+
+/**
+ * Check if the given 'proof' matches the given 'key'.
+ *
+ * @param proof partial regex of a state
+ * @param proof_len number of bytes in @a proof
+ * @param key hash of a state.
+ * @return #GNUNET_OK if the proof is valid for the given key.
+ */
+int
+REGEX_BLOCK_check_proof (const char *proof,
+                        size_t proof_len,
+                        const struct GNUNET_HashCode *key);
+
+
+/**
+ * Check if the regex block is well formed, including all edges.
+ *
+ * @param block The start of the block.
+ * @param size The size of the @a block.
+ * @param query the query for the @a block
+ * @param xquery String describing the edge we are looking for.
+ *               Can be NULL in case this is a put block.
+ * @return #GNUNET_OK in case it's fine.
+ *         #GNUNET_NO in case the xquery exists and is not found (IRRELEVANT).
+ *         #GNUNET_SYSERR if the block is invalid.
+ */
+int
+REGEX_BLOCK_check (const struct RegexBlock *block,
+                  size_t size,
+                  const struct GNUNET_HashCode *query,
+                  const char *xquery);
+
+
+/* FIXME: might want to use 'struct REGEX_BLOCK_Edge' here instead of 3 arguments! */
+
+/**
+ * Iterator over edges in a block.
+ *
+ * @param cls Closure.
+ * @param token Token that follows to next state.
+ * @param len Length of token.
+ * @param key Hash of next state.
+ * @return #GNUNET_YES if should keep iterating, #GNUNET_NO otherwise.
+ */
+typedef int
+(*REGEX_INTERNAL_EgdeIterator)(void *cls,
+                               const char *token,
+                               size_t len,
+                               const struct GNUNET_HashCode *key);
+
+
+/**
+ * Iterate over all edges of a block of a regex state.
+ *
+ * @param block Block to iterate over.
+ * @param size Size of block.
+ * @param iterator Function to call on each edge in the block.
+ * @param iter_cls Closure for the @a iterator.
+ * @return #GNUNET_SYSERR if an error has been encountered.
+ *         #GNUNET_OK if no error has been encountered.
+ *           Note that if the iterator stops the iteration by returning
+ *         #GNUNET_NO, the block will no longer be checked for further errors.
+ *           The return value will be #GNUNET_OK meaning that no errors were
+ *         found until the edge last notified to the iterator, but there might
+ *         be errors in further edges.
+ */
+int
+REGEX_BLOCK_iterate (const struct RegexBlock *block,
+                     size_t size,
+                     REGEX_INTERNAL_EgdeIterator iterator,
+                     void *iter_cls);
+
+
+/**
+ * Obtain the key that a particular block is to be stored under.
+ *
+ * @param block block to get the key from
+ * @param block_len number of bytes in @a block
+ * @param key where to store the key
+ * @return #GNUNET_OK on success, #GNUNET_SYSERR if the block is malformed
+ */
+int
+REGEX_BLOCK_get_key (const struct RegexBlock *block,
+                    size_t block_len,
+                    struct GNUNET_HashCode *key);
+
+
+/**
+ * Test if this block is marked as being an accept state.
+ *
+ * @param block block to test
+ * @param size number of bytes in block
+ * @return #GNUNET_YES if the block is accepting, #GNUNET_NO if not
+ */
+int
+GNUNET_BLOCK_is_accepting (const struct RegexBlock *block,
+                          size_t block_len);
+
+
+/**
+ * Construct a regex block to be stored in the DHT.
+ *
+ * @param proof proof string for the block
+ * @param num_edges number of edges in the block
+ * @param edges the edges of the block
+ * @param accepting is this an accepting state
+ * @param rsize set to the size of the returned block (OUT-only)
+ * @return the regex block, NULL on error
+ */
+struct RegexBlock *
+REGEX_BLOCK_create (const char *proof,
+                   unsigned int num_edges,
+                   const struct REGEX_BLOCK_Edge *edges,
+                   int accepting,
+                   size_t *rsize);
+
+
+#if 0                           /* keep Emacsens' auto-indent happy */
+{
+#endif
+#ifdef __cplusplus
+}
+#endif
+
+/* ifndef REGEX_BLOCK_LIB_H */
+#endif
+/* end of regex_block_lib.h */
diff --git a/src/regex/regex_internal.c b/src/regex/regex_internal.c
new file mode 100644 (file)
index 0000000..944ca9b
--- /dev/null
@@ -0,0 +1,3706 @@
+/*
+     This file is part of GNUnet
+     Copyright (C) 2012 GNUnet e.V.
+
+     GNUnet is free software: you can redistribute it and/or modify it
+     under the terms of the GNU Affero General Public License as published
+     by the Free Software Foundation, either version 3 of the License,
+     or (at your option) any later version.
+
+     GNUnet is distributed in the hope that it will be useful, but
+     WITHOUT ANY WARRANTY; without even the implied warranty of
+     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+     Affero General Public License for more details.
+    
+     You should have received a copy of the GNU Affero General Public License
+     along with this program.  If not, see <http://www.gnu.org/licenses/>.
+*/
+/**
+ * @file src/regex/regex_internal.c
+ * @brief library to create Deterministic Finite Automatons (DFAs) from regular
+ * expressions (regexes).
+ * @author Maximilian Szengel
+ */
+#include "platform.h"
+#include "gnunet_util_lib.h"
+#include "gnunet_regex_service.h"
+#include "regex_internal_lib.h"
+#include "regex_internal.h"
+
+
+/**
+ * Set this to #GNUNET_YES to enable state naming. Used to debug NFA->DFA
+ * creation. Disabled by default for better performance.
+ */
+#define REGEX_DEBUG_DFA GNUNET_NO
+
+/**
+ * Set of states using MDLL API.
+ */
+struct REGEX_INTERNAL_StateSet_MDLL
+{
+  /**
+   * MDLL of states.
+   */
+  struct REGEX_INTERNAL_State *head;
+
+  /**
+   * MDLL of states.
+   */
+  struct REGEX_INTERNAL_State *tail;
+
+  /**
+   * Length of the MDLL.
+   */
+  unsigned int len;
+};
+
+
+/**
+ * Append state to the given StateSet.
+ *
+ * @param set set to be modified
+ * @param state state to be appended
+ */
+static void
+state_set_append (struct REGEX_INTERNAL_StateSet *set,
+                 struct REGEX_INTERNAL_State *state)
+{
+  if (set->off == set->size)
+    GNUNET_array_grow (set->states, set->size, set->size * 2 + 4);
+  set->states[set->off++] = state;
+}
+
+
+/**
+ * Compare two strings for equality. If either is NULL they are not equal.
+ *
+ * @param str1 first string for comparison.
+ * @param str2 second string for comparison.
+ *
+ * @return 0 if the strings are the same or both NULL, 1 or -1 if not.
+ */
+static int
+nullstrcmp (const char *str1, const char *str2)
+{
+  if ((NULL == str1) != (NULL == str2))
+    return -1;
+  if ((NULL == str1) && (NULL == str2))
+    return 0;
+
+  return strcmp (str1, str2);
+}
+
+
+/**
+ * Adds a transition from one state to another on @a label. Does not add
+ * duplicate states.
+ *
+ * @param ctx context
+ * @param from_state starting state for the transition
+ * @param label transition label
+ * @param to_state state to where the transition should point to
+ */
+static void
+state_add_transition (struct REGEX_INTERNAL_Context *ctx,
+                      struct REGEX_INTERNAL_State *from_state,
+                      const char *label,
+                      struct REGEX_INTERNAL_State *to_state)
+{
+  struct REGEX_INTERNAL_Transition *t;
+  struct REGEX_INTERNAL_Transition *oth;
+
+  if (NULL == from_state)
+  {
+    GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+                "Could not create Transition.\n");
+    return;
+  }
+
+  /* Do not add duplicate state transitions */
+  for (t = from_state->transitions_head; NULL != t; t = t->next)
+  {
+    if (t->to_state == to_state && 0 == nullstrcmp (t->label, label) &&
+        t->from_state == from_state)
+      return;
+  }
+
+  /* sort transitions by label */
+  for (oth = from_state->transitions_head; NULL != oth; oth = oth->next)
+  {
+    if (0 < nullstrcmp (oth->label, label))
+      break;
+  }
+
+  t = GNUNET_new (struct REGEX_INTERNAL_Transition);
+  if (NULL != ctx)
+    t->id = ctx->transition_id++;
+  if (NULL != label)
+    t->label = GNUNET_strdup (label);
+  else
+    t->label = NULL;
+  t->to_state = to_state;
+  t->from_state = from_state;
+
+  /* Add outgoing transition to 'from_state' */
+  from_state->transition_count++;
+  GNUNET_CONTAINER_DLL_insert_before (from_state->transitions_head,
+                                      from_state->transitions_tail, oth, t);
+}
+
+
+/**
+ * Remove a 'transition' from 'state'.
+ *
+ * @param state state from which the to-be-removed transition originates.
+ * @param transition transition that should be removed from state 'state'.
+ */
+static void
+state_remove_transition (struct REGEX_INTERNAL_State *state,
+                         struct REGEX_INTERNAL_Transition *transition)
+{
+  if (NULL == state || NULL == transition)
+    return;
+
+  if (transition->from_state != state)
+    return;
+
+  GNUNET_free_non_null (transition->label);
+
+  state->transition_count--;
+  GNUNET_CONTAINER_DLL_remove (state->transitions_head, state->transitions_tail,
+                               transition);
+
+  GNUNET_free (transition);
+}
+
+
+/**
+ * Compare two states. Used for sorting.
+ *
+ * @param a first state
+ * @param b second state
+ *
+ * @return an integer less than, equal to, or greater than zero
+ *         if the first argument is considered to be respectively
+ *         less than, equal to, or greater than the second.
+ */
+static int
+state_compare (const void *a, const void *b)
+{
+  struct REGEX_INTERNAL_State **s1 = (struct REGEX_INTERNAL_State **) a;
+  struct REGEX_INTERNAL_State **s2 = (struct REGEX_INTERNAL_State **) b;
+
+  return (*s1)->id - (*s2)->id;
+}
+
+
+/**
+ * Get all edges leaving state @a s.
+ *
+ * @param s state.
+ * @param edges all edges leaving @a s, expected to be allocated and have enough
+ *        space for `s->transitions_count` elements.
+ *
+ * @return number of edges.
+ */
+static unsigned int
+state_get_edges (struct REGEX_INTERNAL_State *s,
+                 struct REGEX_BLOCK_Edge *edges)
+{
+  struct REGEX_INTERNAL_Transition *t;
+  unsigned int count;
+
+  if (NULL == s)
+    return 0;
+
+  count = 0;
+
+  for (t = s->transitions_head; NULL != t; t = t->next)
+  {
+    if (NULL != t->to_state)
+    {
+      edges[count].label = t->label;
+      edges[count].destination = t->to_state->hash;
+      count++;
+    }
+  }
+  return count;
+}
+
+
+/**
+ * Compare to state sets by comparing the id's of the states that are contained
+ * in each set. Both sets are expected to be sorted by id!
+ *
+ * @param sset1 first state set
+ * @param sset2 second state set
+ * @return 0 if the sets are equal, otherwise non-zero
+ */
+static int
+state_set_compare (struct REGEX_INTERNAL_StateSet *sset1,
+                   struct REGEX_INTERNAL_StateSet *sset2)
+{
+  int result;
+  unsigned int i;
+
+  if (NULL == sset1 || NULL == sset2)
+    return 1;
+
+  result = sset1->off - sset2->off;
+  if (result < 0)
+    return -1;
+  if (result > 0)
+    return 1;
+  for (i = 0; i < sset1->off; i++)
+    if (0 != (result = state_compare (&sset1->states[i], &sset2->states[i])))
+      break;
+  return result;
+}
+
+
+/**
+ * Clears the given StateSet 'set'
+ *
+ * @param set set to be cleared
+ */
+static void
+state_set_clear (struct REGEX_INTERNAL_StateSet *set)
+{
+  GNUNET_array_grow (set->states, set->size, 0);
+  set->off = 0;
+}
+
+
+/**
+ * Clears an automaton fragment. Does not destroy the states inside the
+ * automaton.
+ *
+ * @param a automaton to be cleared
+ */
+static void
+automaton_fragment_clear (struct REGEX_INTERNAL_Automaton *a)
+{
+  if (NULL == a)
+    return;
+
+  a->start = NULL;
+  a->end = NULL;
+  a->states_head = NULL;
+  a->states_tail = NULL;
+  a->state_count = 0;
+  GNUNET_free (a);
+}
+
+
+/**
+ * Frees the memory used by State @a s
+ *
+ * @param s state that should be destroyed
+ */
+static void
+automaton_destroy_state (struct REGEX_INTERNAL_State *s)
+{
+  struct REGEX_INTERNAL_Transition *t;
+  struct REGEX_INTERNAL_Transition *next_t;
+
+  if (NULL == s)
+    return;
+
+  GNUNET_free_non_null (s->name);
+  GNUNET_free_non_null (s->proof);
+  state_set_clear (&s->nfa_set);
+  for (t = s->transitions_head; NULL != t; t = next_t)
+  {
+    next_t = t->next;
+    state_remove_transition (s, t);
+  }
+
+  GNUNET_free (s);
+}
+
+
+/**
+ * Remove a state from the given automaton 'a'. Always use this function when
+ * altering the states of an automaton. Will also remove all transitions leading
+ * to this state, before destroying it.
+ *
+ * @param a automaton
+ * @param s state to remove
+ */
+static void
+automaton_remove_state (struct REGEX_INTERNAL_Automaton *a,
+                        struct REGEX_INTERNAL_State *s)
+{
+  struct REGEX_INTERNAL_State *s_check;
+  struct REGEX_INTERNAL_Transition *t_check;
+  struct REGEX_INTERNAL_Transition *t_check_next;
+
+  if (NULL == a || NULL == s)
+    return;
+
+  /* remove all transitions leading to this state */
+  for (s_check = a->states_head; NULL != s_check; s_check = s_check->next)
+  {
+    for (t_check = s_check->transitions_head; NULL != t_check;
+         t_check = t_check_next)
+    {
+      t_check_next = t_check->next;
+      if (t_check->to_state == s)
+        state_remove_transition (s_check, t_check);
+    }
+  }
+
+  /* remove state */
+  GNUNET_CONTAINER_DLL_remove (a->states_head, a->states_tail, s);
+  a->state_count--;
+
+  automaton_destroy_state (s);
+}
+
+
+/**
+ * Merge two states into one. Will merge 's1' and 's2' into 's1' and destroy
+ * 's2'. 's1' will contain all (non-duplicate) outgoing transitions of 's2'.
+ *
+ * @param ctx context
+ * @param a automaton
+ * @param s1 first state
+ * @param s2 second state, will be destroyed
+ */
+static void
+automaton_merge_states (struct REGEX_INTERNAL_Context *ctx,
+                        struct REGEX_INTERNAL_Automaton *a,
+                        struct REGEX_INTERNAL_State *s1,
+                        struct REGEX_INTERNAL_State *s2)
+{
+  struct REGEX_INTERNAL_State *s_check;
+  struct REGEX_INTERNAL_Transition *t_check;
+  struct REGEX_INTERNAL_Transition *t;
+  struct REGEX_INTERNAL_Transition *t_next;
+  int is_dup;
+
+  if (s1 == s2)
+    return;
+
+  /* 1. Make all transitions pointing to s2 point to s1, unless this transition
+   * does not already exists, if it already exists remove transition. */
+  for (s_check = a->states_head; NULL != s_check; s_check = s_check->next)
+  {
+    for (t_check = s_check->transitions_head; NULL != t_check; t_check = t_next)
+    {
+      t_next = t_check->next;
+
+      if (s2 == t_check->to_state)
+      {
+        is_dup = GNUNET_NO;
+        for (t = t_check->from_state->transitions_head; NULL != t; t = t->next)
+        {
+          if (t->to_state == s1 && 0 == strcmp (t_check->label, t->label))
+            is_dup = GNUNET_YES;
+        }
+        if (GNUNET_NO == is_dup)
+          t_check->to_state = s1;
+        else
+          state_remove_transition (t_check->from_state, t_check);
+      }
+    }
+  }
+
+  /* 2. Add all transitions from s2 to sX to s1 */
+  for (t_check = s2->transitions_head; NULL != t_check; t_check = t_check->next)
+  {
+    if (t_check->to_state != s1)
+      state_add_transition (ctx, s1, t_check->label, t_check->to_state);
+  }
+
+  /* 3. Rename s1 to {s1,s2} */
+#if REGEX_DEBUG_DFA
+  char *new_name;
+
+  new_name = s1->name;
+  GNUNET_asprintf (&s1->name, "{%s,%s}", new_name, s2->name);
+  GNUNET_free (new_name);
+#endif
+
+  /* remove state */
+  GNUNET_CONTAINER_DLL_remove (a->states_head, a->states_tail, s2);
+  a->state_count--;
+  automaton_destroy_state (s2);
+}
+
+
+/**
+ * Add a state to the automaton 'a', always use this function to alter the
+ * states DLL of the automaton.
+ *
+ * @param a automaton to add the state to
+ * @param s state that should be added
+ */
+static void
+automaton_add_state (struct REGEX_INTERNAL_Automaton *a,
+                     struct REGEX_INTERNAL_State *s)
+{
+  GNUNET_CONTAINER_DLL_insert (a->states_head, a->states_tail, s);
+  a->state_count++;
+}
+
+
+/**
+ * Depth-first traversal (DFS) of all states that are reachable from state
+ * 's'. Performs 'action' on each visited state.
+ *
+ * @param s start state.
+ * @param marks an array of size a->state_count to remember which state was
+ *        already visited.
+ * @param count current count of the state.
+ * @param check function that is checked before advancing on each transition
+ *              in the DFS.
+ * @param check_cls closure for check.
+ * @param action action to be performed on each state.
+ * @param action_cls closure for action.
+ */
+static void
+automaton_state_traverse (struct REGEX_INTERNAL_State *s, int *marks,
+                          unsigned int *count,
+                          REGEX_INTERNAL_traverse_check check, void *check_cls,
+                          REGEX_INTERNAL_traverse_action action, void *action_cls)
+{
+  struct REGEX_INTERNAL_Transition *t;
+
+  if (GNUNET_YES == marks[s->traversal_id])
+    return;
+
+  marks[s->traversal_id] = GNUNET_YES;
+
+  if (NULL != action)
+    action (action_cls, *count, s);
+
+  (*count)++;
+
+  for (t = s->transitions_head; NULL != t; t = t->next)
+  {
+    if (NULL == check ||
+        (NULL != check && GNUNET_YES == check (check_cls, s, t)))
+    {
+      automaton_state_traverse (t->to_state, marks, count, check, check_cls,
+                                action, action_cls);
+    }
+  }
+}
+
+
+/**
+ * Traverses the given automaton using depth-first-search (DFS) from it's start
+ * state, visiting all reachable states and calling 'action' on each one of
+ * them.
+ *
+ * @param a automaton to be traversed.
+ * @param start start state, pass a->start or NULL to traverse the whole automaton.
+ * @param check function that is checked before advancing on each transition
+ *              in the DFS.
+ * @param check_cls closure for @a check.
+ * @param action action to be performed on each state.
+ * @param action_cls closure for @a action
+ */
+void
+REGEX_INTERNAL_automaton_traverse (const struct REGEX_INTERNAL_Automaton *a,
+                                   struct REGEX_INTERNAL_State *start,
+                                   REGEX_INTERNAL_traverse_check check,
+                                   void *check_cls,
+                                   REGEX_INTERNAL_traverse_action action,
+                                   void *action_cls)
+{
+  unsigned int count;
+  struct REGEX_INTERNAL_State *s;
+
+  if (NULL == a || 0 == a->state_count)
+    return;
+
+  int marks[a->state_count];
+
+  for (count = 0, s = a->states_head; NULL != s && count < a->state_count;
+       s = s->next, count++)
+  {
+    s->traversal_id = count;
+    marks[s->traversal_id] = GNUNET_NO;
+  }
+
+  count = 0;
+
+  if (NULL == start)
+    s = a->start;
+  else
+    s = start;
+
+  automaton_state_traverse (s, marks, &count,
+                            check, check_cls,
+                            action, action_cls);
+}
+
+
+/**
+ * String container for faster string operations.
+ */
+struct StringBuffer
+{
+  /**
+   * Buffer holding the string (may start in the middle!);
+   * NOT 0-terminated!
+   */
+  char *sbuf;
+
+  /**
+   * Allocated buffer.
+   */
+  char *abuf;
+
+  /**
+   * Length of the string in the buffer.
+   */
+  size_t slen;
+
+  /**
+   * Number of bytes allocated for @e sbuf
+   */
+  unsigned int blen;
+
+  /**
+   * Buffer currently represents "NULL" (not the empty string!)
+   */
+  int16_t null_flag;
+
+  /**
+   * If this entry is part of the last/current generation array,
+   * this flag is #GNUNET_YES if the last and current generation are
+   * identical (and thus copying is unnecessary if the value didn't
+   * change).  This is used in an optimization that improves
+   * performance by about 1% --- if we use int16_t here.  With just
+   * "int" for both flags, performance drops (on my system) significantly,
+   * most likely due to increased cache misses.
+   */
+  int16_t synced;
+
+};
+
+
+/**
+ * Compare two strings for equality. If either is NULL they are not equal.
+ *
+ * @param s1 first string for comparison.
+ * @param s2 second string for comparison.
+ *
+ * @return 0 if the strings are the same or both NULL, 1 or -1 if not.
+ */
+static int
+sb_nullstrcmp (const struct StringBuffer *s1,
+              const struct StringBuffer *s2)
+{
+  if ( (GNUNET_YES == s1->null_flag) &&
+       (GNUNET_YES == s2->null_flag) )
+    return 0;
+  if ( (GNUNET_YES == s1->null_flag) ||
+       (GNUNET_YES == s2->null_flag) )
+    return -1;
+  if (s1->slen != s2->slen)
+    return -1;
+  if (0 == s1->slen)
+    return 0;
+  return memcmp (s1->sbuf, s2->sbuf, s1->slen);
+}
+
+
+/**
+ * Compare two strings for equality.
+ *
+ * @param s1 first string for comparison.
+ * @param s2 second string for comparison.
+ *
+ * @return 0 if the strings are the same, 1 or -1 if not.
+ */
+static int
+sb_strcmp (const struct StringBuffer *s1,
+          const struct StringBuffer *s2)
+{
+  if (s1->slen != s2->slen)
+    return -1;
+  if (0 == s1->slen)
+    return 0;
+  return memcmp (s1->sbuf, s2->sbuf, s1->slen);
+}
+
+
+/**
+ * Reallocate the buffer of 'ret' to fit 'nlen' characters;
+ * move the existing string to the beginning of the new buffer.
+ *
+ * @param ret current buffer, to be updated
+ * @param nlen target length for the buffer, must be at least ret->slen
+ */
+static void
+sb_realloc (struct StringBuffer *ret,
+           size_t nlen)
+{
+  char *old;
+
+  GNUNET_assert (nlen >= ret->slen);
+  old = ret->abuf;
+  ret->abuf = GNUNET_malloc (nlen);
+  ret->blen = nlen;
+  GNUNET_memcpy (ret->abuf,
+         ret->sbuf,
+         ret->slen);
+  ret->sbuf = ret->abuf;
+  GNUNET_free_non_null (old);
+}
+
+
+/**
+ * Append a string.
+ *
+ * @param ret where to write the result
+ * @param sarg string to append
+ */
+static void
+sb_append (struct StringBuffer *ret,
+          const struct StringBuffer *sarg)
+{
+  if (GNUNET_YES == ret->null_flag)
+    ret->slen = 0;
+  ret->null_flag = GNUNET_NO;
+  if (ret->blen < sarg->slen + ret->slen)
+    sb_realloc (ret, ret->blen + sarg->slen + 128);
+  GNUNET_memcpy (&ret->sbuf[ret->slen],
+         sarg->sbuf,
+         sarg->slen);
+  ret->slen += sarg->slen;
+}
+
+
+/**
+ * Append a C string.
+ *
+ * @param ret where to write the result
+ * @param cstr string to append
+ */
+static void
+sb_append_cstr (struct StringBuffer *ret,
+               const char *cstr)
+{
+  size_t cstr_len = strlen (cstr);
+
+  if (GNUNET_YES == ret->null_flag)
+    ret->slen = 0;
+  ret->null_flag = GNUNET_NO;
+  if (ret->blen < cstr_len + ret->slen)
+    sb_realloc (ret, ret->blen + cstr_len + 128);
+  GNUNET_memcpy (&ret->sbuf[ret->slen],
+         cstr,
+         cstr_len);
+  ret->slen += cstr_len;
+}
+
+
+/**
+ * Wrap a string buffer, that is, set ret to the format string
+ * which contains an "%s" which is to be replaced with the original
+ * content of 'ret'.  Note that optimizing this function is not
+ * really worth it, it is rarely called.
+ *
+ * @param ret where to write the result and take the input for %.*s from
+ * @param format format string, fprintf-style, with exactly one "%.*s"
+ * @param extra_chars how long will the result be, in addition to 'sarg' length
+ */
+static void
+sb_wrap (struct StringBuffer *ret,
+        const char *format,
+        size_t extra_chars)
+{
+  char *temp;
+
+  if (GNUNET_YES == ret->null_flag)
+    ret->slen = 0;
+  ret->null_flag = GNUNET_NO;
+  temp = GNUNET_malloc (ret->slen + extra_chars + 1);
+  GNUNET_snprintf (temp,
+                  ret->slen + extra_chars + 1,
+                  format,
+                  (int) ret->slen,
+                  ret->sbuf);
+  GNUNET_free_non_null (ret->abuf);
+  ret->abuf = temp;
+  ret->sbuf = temp;
+  ret->blen = ret->slen + extra_chars + 1;
+  ret->slen = ret->slen + extra_chars;
+}
+
+
+/**
+ * Format a string buffer.    Note that optimizing this function is not
+ * really worth it, it is rarely called.
+ *
+ * @param ret where to write the result
+ * @param format format string, fprintf-style, with exactly one "%.*s"
+ * @param extra_chars how long will the result be, in addition to 'sarg' length
+ * @param sarg string to print into the format
+ */
+static void
+sb_printf1 (struct StringBuffer *ret,
+           const char *format,
+           size_t extra_chars,
+           const struct StringBuffer *sarg)
+{
+  if (ret->blen < sarg->slen + extra_chars + 1)
+    sb_realloc (ret,
+               sarg->slen + extra_chars + 1);
+  ret->null_flag = GNUNET_NO;
+  ret->sbuf = ret->abuf;
+  ret->slen = sarg->slen + extra_chars;
+  GNUNET_snprintf (ret->sbuf,
+                  ret->blen,
+                  format,
+                  (int) sarg->slen,
+                  sarg->sbuf);
+}
+
+
+/**
+ * Format a string buffer.
+ *
+ * @param ret where to write the result
+ * @param format format string, fprintf-style, with exactly two "%.*s"
+ * @param extra_chars how long will the result be, in addition to 'sarg1/2' length
+ * @param sarg1 first string to print into the format
+ * @param sarg2 second string to print into the format
+ */
+static void
+sb_printf2 (struct StringBuffer *ret,
+           const char *format,
+           size_t extra_chars,
+           const struct StringBuffer *sarg1,
+           const struct StringBuffer *sarg2)
+{
+  if (ret->blen < sarg1->slen + sarg2->slen + extra_chars + 1)
+    sb_realloc (ret,
+               sarg1->slen + sarg2->slen + extra_chars + 1);
+  ret->null_flag = GNUNET_NO;
+  ret->slen = sarg1->slen + sarg2->slen + extra_chars;
+  ret->sbuf = ret->abuf;
+  GNUNET_snprintf (ret->sbuf,
+                  ret->blen,
+                  format,
+                  (int) sarg1->slen,
+                  sarg1->sbuf,
+                  (int) sarg2->slen,
+                  sarg2->sbuf);
+}
+
+
+/**
+ * Format a string buffer.     Note that optimizing this function is not
+ * really worth it, it is rarely called.
+ *
+ * @param ret where to write the result
+ * @param format format string, fprintf-style, with exactly three "%.*s"
+ * @param extra_chars how long will the result be, in addition to 'sarg1/2/3' length
+ * @param sarg1 first string to print into the format
+ * @param sarg2 second string to print into the format
+ * @param sarg3 third string to print into the format
+ */
+static void
+sb_printf3 (struct StringBuffer *ret,
+           const char *format,
+           size_t extra_chars,
+           const struct StringBuffer *sarg1,
+           const struct StringBuffer *sarg2,
+           const struct StringBuffer *sarg3)
+{
+  if (ret->blen < sarg1->slen + sarg2->slen + sarg3->slen + extra_chars + 1)
+    sb_realloc (ret,
+               sarg1->slen + sarg2->slen + sarg3->slen + extra_chars + 1);
+  ret->null_flag = GNUNET_NO;
+  ret->slen = sarg1->slen + sarg2->slen + sarg3->slen + extra_chars;
+  ret->sbuf = ret->abuf;
+  GNUNET_snprintf (ret->sbuf,
+                  ret->blen,
+                  format,
+                  (int) sarg1->slen,
+                  sarg1->sbuf,
+                  (int) sarg2->slen,
+                  sarg2->sbuf,
+                  (int) sarg3->slen,
+                  sarg3->sbuf);
+}
+
+
+/**
+ * Free resources of the given string buffer.
+ *
+ * @param sb buffer to free (actual pointer is not freed, as they
+ *        should not be individually allocated)
+ */
+static void
+sb_free (struct StringBuffer *sb)
+{
+  GNUNET_array_grow (sb->abuf,
+                    sb->blen,
+                    0);
+  sb->slen = 0;
+  sb->sbuf = NULL;
+  sb->null_flag= GNUNET_YES;
+}
+
+
+/**
+ * Copy the given string buffer from 'in' to 'out'.
+ *
+ * @param in input string
+ * @param out output string
+ */
+static void
+sb_strdup (struct StringBuffer *out,
+          const struct StringBuffer *in)
+
+{
+  out->null_flag = in->null_flag;
+  if (GNUNET_YES == out->null_flag)
+    return;
+  if (out->blen < in->slen)
+  {
+    GNUNET_array_grow (out->abuf,
+                      out->blen,
+                      in->slen);
+  }
+  out->sbuf = out->abuf;
+  out->slen = in->slen;
+  GNUNET_memcpy (out->sbuf, in->sbuf, out->slen);
+}
+
+
+/**
+ * Copy the given string buffer from 'in' to 'out'.
+ *
+ * @param cstr input string
+ * @param out output string
+ */
+static void
+sb_strdup_cstr (struct StringBuffer *out,
+               const char *cstr)
+{
+  if (NULL == cstr)
+  {
+    out->null_flag = GNUNET_YES;
+    return;
+  }
+  out->null_flag = GNUNET_NO;
+  out->slen = strlen (cstr);
+  if (out->blen < out->slen)
+  {
+    GNUNET_array_grow (out->abuf,
+                      out->blen,
+                      out->slen);
+  }
+  out->sbuf = out->abuf;
+  GNUNET_memcpy (out->sbuf, cstr, out->slen);
+}
+
+
+/**
+ * Check if the given string @a str needs parentheses around it when
+ * using it to generate a regex.
+ *
+ * @param str string
+ *
+ * @return #GNUNET_YES if parentheses are needed, #GNUNET_NO otherwise
+ */
+static int
+needs_parentheses (const struct StringBuffer *str)
+{
+  size_t slen;
+  const char *op;
+  const char *cl;
+  const char *pos;
+  const char *end;
+  unsigned int cnt;
+
+  if ((GNUNET_YES == str->null_flag) || ((slen = str->slen) < 2))
+    return GNUNET_NO;
+  pos = str->sbuf;
+  if ('(' != pos[0])
+    return GNUNET_YES;
+  end = str->sbuf + slen;
+  cnt = 1;
+  pos++;
+  while (cnt > 0)
+  {
+    cl = memchr (pos, ')', end - pos);
+    if (NULL == cl)
+    {
+      GNUNET_break (0);
+      return GNUNET_YES;
+    }
+    /* while '(' before ')', count opening parens */
+    while ( (NULL != (op = memchr (pos, '(', end - pos)))  &&
+           (op < cl) )
+    {
+      cnt++;
+      pos = op + 1;
+    }
+    /* got ')' first */
+    cnt--;
+    pos = cl + 1;
+  }
+  return (*pos == '\0') ? GNUNET_NO : GNUNET_YES;
+}
+
+
+/**
+ * Remove parentheses surrounding string @a str.
+ * Example: "(a)" becomes "a", "(a|b)|(a|c)" stays the same.
+ * You need to #GNUNET_free() the returned string.
+ *
+ * @param str string, modified to contain a
+ * @return string without surrounding parentheses, string 'str' if no preceding
+ *         epsilon could be found, NULL if 'str' was NULL
+ */
+static void
+remove_parentheses (struct StringBuffer *str)
+{
+  size_t slen;
+  const char *pos;
+  const char *end;
+  const char *sbuf;
+  const char *op;
+  const char *cp;
+  unsigned int cnt;
+
+  if (0)
+    return;
+  sbuf = str->sbuf;
+  if ( (GNUNET_YES == str->null_flag) ||
+       (1 >=  (slen = str->slen)) ||
+       ('(' != str->sbuf[0]) ||
+       (')' != str->sbuf[slen - 1]) )
+    return;
+  cnt = 0;
+  pos = &sbuf[1];
+  end = &sbuf[slen - 1];
+  op = memchr (pos, '(', end - pos);
+  cp = memchr (pos, ')', end - pos);
+  while (NULL != cp)
+  {
+    while ( (NULL != op) &&
+           (op < cp) )
+    {
+      cnt++;
+      pos = op + 1;
+      op = memchr (pos, '(', end - pos);
+    }
+    while ( (NULL != cp) &&
+           ( (NULL == op) ||
+             (cp < op) ) )
+    {
+      if (0 == cnt)
+       return; /* can't strip parens */
+      cnt--;
+      pos = cp + 1;
+      cp = memchr (pos, ')', end - pos);
+    }
+  }
+  if (0 != cnt)
+  {
+    GNUNET_break (0);
+    return;
+  }
+  str->sbuf++;
+  str->slen -= 2;
+}
+
+
+/**
+ * Check if the string 'str' starts with an epsilon (empty string).
+ * Example: "(|a)" is starting with an epsilon.
+ *
+ * @param str string to test
+ *
+ * @return 0 if str has no epsilon, 1 if str starts with '(|' and ends with ')'
+ */
+static int
+has_epsilon (const struct StringBuffer *str)
+{
+  return
+    (GNUNET_YES != str->null_flag) &&
+    (0 < str->slen) &&
+    ('(' == str->sbuf[0]) &&
+    ('|' == str->sbuf[1]) &&
+    (')' == str->sbuf[str->slen - 1]);
+}
+
+
+/**
+ * Remove an epsilon from the string str. Where epsilon is an empty string
+ * Example: str = "(|a|b|c)", result: "a|b|c"
+ * The returned string needs to be freed.
+ *
+ * @param str original string
+ * @param ret where to return string without preceding epsilon, string 'str' if no preceding
+ *         epsilon could be found, NULL if 'str' was NULL
+ */
+static void
+remove_epsilon (const struct StringBuffer *str,
+               struct StringBuffer *ret)
+{
+  if (GNUNET_YES == str->null_flag)
+  {
+    ret->null_flag = GNUNET_YES;
+    return;
+  }
+  if ( (str->slen > 1) &&
+       ('(' == str->sbuf[0]) &&
+       ('|' == str->sbuf[1]) &&
+       (')' == str->sbuf[str->slen - 1]) )
+  {
+    /* remove epsilon */
+    if (ret->blen < str->slen - 3)
+    {
+      GNUNET_array_grow (ret->abuf,
+                        ret->blen,
+                        str->slen - 3);
+    }
+    ret->sbuf = ret->abuf;
+    ret->slen = str->slen - 3;
+    GNUNET_memcpy (ret->sbuf, &str->sbuf[2], ret->slen);
+    return;
+  }
+  sb_strdup (ret, str);
+}
+
+
+/**
+ * Compare n bytes of 'str1' and 'str2'
+ *
+ * @param str1 first string to compare
+ * @param str2 second string for comparison
+ * @param n number of bytes to compare
+ *
+ * @return -1 if any of the strings is NULL, 0 if equal, non 0 otherwise
+ */
+static int
+sb_strncmp (const struct StringBuffer *str1,
+           const struct StringBuffer *str2, size_t n)
+{
+  size_t max;
+
+  if ( (str1->slen != str2->slen) &&
+       ( (str1->slen < n) ||
+        (str2->slen < n) ) )
+    return -1;
+  max = GNUNET_MAX (str1->slen, str2->slen);
+  if (max > n)
+    max = n;
+  return memcmp (str1->sbuf, str2->sbuf, max);
+}
+
+
+/**
+ * Compare n bytes of 'str1' and 'str2'
+ *
+ * @param str1 first string to compare
+ * @param str2 second C string for comparison
+ * @param n number of bytes to compare (and length of str2)
+ *
+ * @return -1 if any of the strings is NULL, 0 if equal, non 0 otherwise
+ */
+static int
+sb_strncmp_cstr (const struct StringBuffer *str1,
+                const char *str2, size_t n)
+{
+  if (str1->slen < n)
+    return -1;
+  return memcmp (str1->sbuf, str2, n);
+}
+
+
+/**
+ * Initialize string buffer for storing strings of up to n
+ * characters.
+ *
+ * @param sb buffer to initialize
+ * @param n desired target length
+ */
+static void
+sb_init (struct StringBuffer *sb,
+        size_t n)
+{
+  sb->null_flag = GNUNET_NO;
+  sb->abuf = sb->sbuf = (0 == n) ? NULL : GNUNET_malloc (n);
+  sb->blen = n;
+  sb->slen = 0;
+}
+
+
+/**
+ * Compare 'str1', starting from position 'k',  with whole 'str2'
+ *
+ * @param str1 first string to compare, starting from position 'k'
+ * @param str2 second string for comparison
+ * @param k starting position in 'str1'
+ *
+ * @return -1 if any of the strings is NULL, 0 if equal, non 0 otherwise
+ */
+static int
+sb_strkcmp (const struct StringBuffer *str1,
+           const struct StringBuffer *str2, size_t k)
+{
+  if ( (GNUNET_YES == str1->null_flag) ||
+       (GNUNET_YES == str2->null_flag) ||
+       (k > str1->slen) ||
+       (str1->slen - k != str2->slen) )
+    return -1;
+  return memcmp (&str1->sbuf[k], str2->sbuf, str2->slen);
+}
+
+
+/**
+ * Helper function used as 'action' in 'REGEX_INTERNAL_automaton_traverse'
+ * function to create the depth-first numbering of the states.
+ *
+ * @param cls states array.
+ * @param count current state counter.
+ * @param s current state.
+ */
+static void
+number_states (void *cls, const unsigned int count,
+               struct REGEX_INTERNAL_State *s)
+{
+  struct REGEX_INTERNAL_State **states = cls;
+
+  s->dfs_id = count;
+  if (NULL != states)
+    states[count] = s;
+}
+
+
+
+#define PRIS(a) \
+  ((GNUNET_YES == a.null_flag) ? 6 : (int) a.slen), \
+  ((GNUNET_YES == a.null_flag) ? "(null)" : a.sbuf)
+
+
+/**
+ * Construct the regular expression given the inductive step,
+ * $R^{(k)}_{ij} = R^{(k-1)}_{ij} | R^{(k-1)}_{ik} ( R^{(k-1)}_{kk} )^*
+ * R^{(k-1)}_{kj}, and simplify the resulting expression saved in R_cur_ij.
+ *
+ * @param R_last_ij value of  $R^{(k-1)_{ij}.
+ * @param R_last_ik value of  $R^{(k-1)_{ik}.
+ * @param R_last_kk value of  $R^{(k-1)_{kk}.
+ * @param R_last_kj value of  $R^{(k-1)_{kj}.
+ * @param R_cur_ij result for this inductive step is saved in R_cur_ij, R_cur_ij
+ *                 is expected to be NULL when called!
+ * @param R_cur_l optimization -- kept between iterations to avoid realloc
+ * @param R_cur_r optimization -- kept between iterations to avoid realloc
+ */
+static void
+automaton_create_proofs_simplify (const struct StringBuffer *R_last_ij,
+                                 const struct StringBuffer *R_last_ik,
+                                  const struct StringBuffer *R_last_kk,
+                                 const struct StringBuffer *R_last_kj,
+                                  struct StringBuffer *R_cur_ij,
+                                 struct StringBuffer *R_cur_l,
+                                 struct StringBuffer *R_cur_r)
+{
+  struct StringBuffer R_temp_ij;
+  struct StringBuffer R_temp_ik;
+  struct StringBuffer R_temp_kj;
+  struct StringBuffer R_temp_kk;
+  int eps_check;
+  int ij_ik_cmp;
+  int ij_kj_cmp;
+  int ik_kk_cmp;
+  int kk_kj_cmp;
+  int clean_ik_kk_cmp;
+  int clean_kk_kj_cmp;
+  size_t length;
+  size_t length_l;
+  size_t length_r;
+
+  /*
+   * $R^{(k)}_{ij} = R^{(k-1)}_{ij} | R^{(k-1)}_{ik} ( R^{(k-1)}_{kk} )^* R^{(k-1)}_{kj}
+   * R_last == R^{(k-1)}, R_cur == R^{(k)}
+   * R_cur_ij = R_cur_l | R_cur_r
+   * R_cur_l == R^{(k-1)}_{ij}
+   * R_cur_r == R^{(k-1)}_{ik} ( R^{(k-1)}_{kk} )^* R^{(k-1)}_{kj}
+   */
+
+  if ( (GNUNET_YES == R_last_ij->null_flag) &&
+       ( (GNUNET_YES == R_last_ik->null_flag) ||
+        (GNUNET_YES == R_last_kj->null_flag)))
+  {
+    /* R^{(k)}_{ij} = N | N */
+    R_cur_ij->null_flag = GNUNET_YES;
+    R_cur_ij->synced = GNUNET_NO;
+    return;
+  }
+
+  if ( (GNUNET_YES == R_last_ik->null_flag) ||
+       (GNUNET_YES == R_last_kj->null_flag) )
+  {
+    /*  R^{(k)}_{ij} = R^{(k-1)}_{ij} | N */
+    if (GNUNET_YES == R_last_ij->synced)
+    {
+      R_cur_ij->synced = GNUNET_YES;
+      R_cur_ij->null_flag = GNUNET_NO;
+      return;
+    }
+    R_cur_ij->synced = GNUNET_YES;
+    sb_strdup (R_cur_ij, R_last_ij);
+    return;
+  }
+  R_cur_ij->synced = GNUNET_NO;
+
+  /* $R^{(k)}_{ij} = N | R^{(k-1)}_{ik} ( R^{(k-1)}_{kk} )^* R^{(k-1)}_{kj} OR
+   * $R^{(k)}_{ij} = R^{(k-1)}_{ij} | R^{(k-1)}_{ik} ( R^{(k-1)}_{kk} )^* R^{(k-1)}_{kj} */
+
+  R_cur_r->null_flag = GNUNET_YES;
+  R_cur_r->slen = 0;
+  R_cur_l->null_flag = GNUNET_YES;
+  R_cur_l->slen = 0;
+
+  /* cache results from strcmp, we might need these many times */
+  ij_kj_cmp = sb_nullstrcmp (R_last_ij, R_last_kj);
+  ij_ik_cmp = sb_nullstrcmp (R_last_ij, R_last_ik);
+  ik_kk_cmp = sb_nullstrcmp (R_last_ik, R_last_kk);
+  kk_kj_cmp = sb_nullstrcmp (R_last_kk, R_last_kj);
+
+  /* Assign R_temp_(ik|kk|kj) to R_last[][] and remove epsilon as well
+   * as parentheses, so we can better compare the contents */
+
+  memset (&R_temp_ij, 0, sizeof (struct StringBuffer));
+  memset (&R_temp_ik, 0, sizeof (struct StringBuffer));
+  memset (&R_temp_kk, 0, sizeof (struct StringBuffer));
+  memset (&R_temp_kj, 0, sizeof (struct StringBuffer));
+  remove_epsilon (R_last_ik, &R_temp_ik);
+  remove_epsilon (R_last_kk, &R_temp_kk);
+  remove_epsilon (R_last_kj, &R_temp_kj);
+  remove_parentheses (&R_temp_ik);
+  remove_parentheses (&R_temp_kk);
+  remove_parentheses (&R_temp_kj);
+  clean_ik_kk_cmp = sb_nullstrcmp (R_last_ik, &R_temp_kk);
+  clean_kk_kj_cmp = sb_nullstrcmp (&R_temp_kk, R_last_kj);
+
+  /* construct R_cur_l (and, if necessary R_cur_r) */
+  if (GNUNET_YES != R_last_ij->null_flag)
+  {
+    /* Assign R_temp_ij to R_last_ij and remove epsilon as well
+     * as parentheses, so we can better compare the contents */
+    remove_epsilon (R_last_ij, &R_temp_ij);
+    remove_parentheses (&R_temp_ij);
+
+    if ( (0 == sb_strcmp (&R_temp_ij, &R_temp_ik)) &&
+        (0 == sb_strcmp (&R_temp_ik, &R_temp_kk)) &&
+        (0 == sb_strcmp (&R_temp_kk, &R_temp_kj)) )
+    {
+      if (0 == R_temp_ij.slen)
+      {
+        R_cur_r->null_flag = GNUNET_NO;
+      }
+      else if ((0 == sb_strncmp_cstr (R_last_ij, "(|", 2)) ||
+               (0 == sb_strncmp_cstr (R_last_ik, "(|", 2) &&
+                0 == sb_strncmp_cstr (R_last_kj, "(|", 2)))
+      {
+        /*
+         * a|(e|a)a*(e|a) = a*
+         * a|(e|a)(e|a)*(e|a) = a*
+         * (e|a)|aa*a = a*
+         * (e|a)|aa*(e|a) = a*
+         * (e|a)|(e|a)a*a = a*
+         * (e|a)|(e|a)a*(e|a) = a*
+         * (e|a)|(e|a)(e|a)*(e|a) = a*
+         */
+        if (GNUNET_YES == needs_parentheses (&R_temp_ij))
+          sb_printf1 (R_cur_r, "(%.*s)*", 3, &R_temp_ij);
+        else
+          sb_printf1 (R_cur_r, "%.*s*", 1, &R_temp_ij);
+      }
+      else
+      {
+        /*
+         * a|aa*a = a+
+         * a|(e|a)a*a = a+
+         * a|aa*(e|a) = a+
+         * a|(e|a)(e|a)*a = a+
+         * a|a(e|a)*(e|a) = a+
+         */
+        if (GNUNET_YES == needs_parentheses (&R_temp_ij))
+          sb_printf1 (R_cur_r, "(%.*s)+", 3, &R_temp_ij);
+        else
+          sb_printf1 (R_cur_r, "%.*s+", 1, &R_temp_ij);
+      }
+    }
+    else if ( (0 == ij_ik_cmp) && (0 == clean_kk_kj_cmp) && (0 != clean_ik_kk_cmp) )
+    {
+      /* a|ab*b = ab* */
+      if (0 == R_last_kk->slen)
+        sb_strdup (R_cur_r, R_last_ij);
+      else if (GNUNET_YES == needs_parentheses (&R_temp_kk))
+        sb_printf2 (R_cur_r, "%.*s(%.*s)*", 3, R_last_ij, &R_temp_kk);
+      else
+        sb_printf2 (R_cur_r, "%.*s%.*s*", 1, R_last_ij, R_last_kk);
+      R_cur_l->null_flag = GNUNET_YES;
+    }
+    else if ( (0 == ij_kj_cmp) && (0 == clean_ik_kk_cmp) && (0 != clean_kk_kj_cmp))
+    {
+      /* a|bb*a = b*a */
+      if (R_last_kk->slen < 1)
+      {
+        sb_strdup (R_cur_r, R_last_kj);
+      }
+      else if (GNUNET_YES == needs_parentheses (&R_temp_kk))
+        sb_printf2 (R_cur_r, "(%.*s)*%.*s", 3, &R_temp_kk, R_last_kj);
+      else
+        sb_printf2 (R_cur_r, "%.*s*%.*s", 1, &R_temp_kk, R_last_kj);
+
+      R_cur_l->null_flag = GNUNET_YES;
+    }
+    else if ( (0 == ij_ik_cmp) && (0 == kk_kj_cmp) && (! has_epsilon (R_last_ij)) &&
+             has_epsilon (R_last_kk))
+    {
+      /* a|a(e|b)*(e|b) = a|ab* = a|a|ab|abb|abbb|... = ab* */
+      if (needs_parentheses (&R_temp_kk))
+        sb_printf2 (R_cur_r, "%.*s(%.*s)*", 3, R_last_ij, &R_temp_kk);
+      else
+        sb_printf2 (R_cur_r, "%.*s%.*s*", 1, R_last_ij, &R_temp_kk);
+      R_cur_l->null_flag = GNUNET_YES;
+    }
+    else if ( (0 == ij_kj_cmp) && (0 == ik_kk_cmp) && (! has_epsilon (R_last_ij)) &&
+             has_epsilon (R_last_kk))
+    {
+      /* a|(e|b)(e|b)*a = a|b*a = a|a|ba|bba|bbba|...  = b*a */
+      if (needs_parentheses (&R_temp_kk))
+        sb_printf2 (R_cur_r, "(%.*s)*%.*s", 3, &R_temp_kk, R_last_ij);
+      else
+        sb_printf2 (R_cur_r, "%.*s*%.*s", 1, &R_temp_kk, R_last_ij);
+      R_cur_l->null_flag = GNUNET_YES;
+    }
+    else
+    {
+      sb_strdup (R_cur_l, R_last_ij);
+      remove_parentheses (R_cur_l);
+    }
+  }
+  else
+  {
+    /* we have no left side */
+    R_cur_l->null_flag = GNUNET_YES;
+  }
+
+  /* construct R_cur_r, if not already constructed */
+  if (GNUNET_YES == R_cur_r->null_flag)
+  {
+    length = R_temp_kk.slen - R_last_ik->slen;
+
+    /* a(ba)*bx = (ab)+x */
+    if ( (length > 0) &&
+        (GNUNET_YES != R_last_kk->null_flag) &&
+        (0 < R_last_kk->slen) &&
+        (GNUNET_YES != R_last_kj->null_flag) &&
+        (0 < R_last_kj->slen) &&
+        (GNUNET_YES != R_last_ik->null_flag) &&
+        (0 < R_last_ik->slen) &&
+        (0 == sb_strkcmp (&R_temp_kk, R_last_ik, length)) &&
+        (0 == sb_strncmp (&R_temp_kk, R_last_kj, length)) )
+    {
+      struct StringBuffer temp_a;
+      struct StringBuffer temp_b;
+
+      sb_init (&temp_a, length);
+      sb_init (&temp_b, R_last_kj->slen - length);
+
+      length_l = length;
+      temp_a.sbuf = temp_a.abuf;
+      GNUNET_memcpy (temp_a.sbuf, R_last_kj->sbuf, length_l);
+      temp_a.slen = length_l;
+
+      length_r = R_last_kj->slen - length;
+      temp_b.sbuf = temp_b.abuf;
+      GNUNET_memcpy (temp_b.sbuf, &R_last_kj->sbuf[length], length_r);
+      temp_b.slen = length_r;
+
+      /* e|(ab)+ = (ab)* */
+      if ( (GNUNET_YES != R_cur_l->null_flag) &&
+          (0 == R_cur_l->slen) &&
+          (0 == temp_b.slen) )
+      {
+        sb_printf2 (R_cur_r, "(%.*s%.*s)*", 3, R_last_ik, &temp_a);
+        sb_free (R_cur_l);
+        R_cur_l->null_flag = GNUNET_YES;
+      }
+      else
+      {
+        sb_printf3 (R_cur_r, "(%.*s%.*s)+%.*s", 3, R_last_ik, &temp_a, &temp_b);
+      }
+      sb_free (&temp_a);
+      sb_free (&temp_b);
+    }
+    else if (0 == sb_strcmp (&R_temp_ik, &R_temp_kk) &&
+             0 == sb_strcmp (&R_temp_kk, &R_temp_kj))
+    {
+      /*
+       * (e|a)a*(e|a) = a*
+       * (e|a)(e|a)*(e|a) = a*
+       */
+      if (has_epsilon (R_last_ik) && has_epsilon (R_last_kj))
+      {
+        if (needs_parentheses (&R_temp_kk))
+          sb_printf1 (R_cur_r, "(%.*s)*", 3, &R_temp_kk);
+        else
+          sb_printf1 (R_cur_r, "%.*s*", 1, &R_temp_kk);
+      }
+      /* aa*a = a+a */
+      else if ( (0 == clean_ik_kk_cmp) &&
+               (0 == clean_kk_kj_cmp) &&
+               (! has_epsilon (R_last_ik)) )
+      {
+        if (needs_parentheses (&R_temp_kk))
+          sb_printf2 (R_cur_r, "(%.*s)+%.*s", 3, &R_temp_kk, &R_temp_kk);
+        else
+          sb_printf2 (R_cur_r, "%.*s+%.*s", 1, &R_temp_kk, &R_temp_kk);
+      }
+      /*
+       * (e|a)a*a = a+
+       * aa*(e|a) = a+
+       * a(e|a)*(e|a) = a+
+       * (e|a)a*a = a+
+       */
+      else
+      {
+        eps_check =
+         (has_epsilon (R_last_ik) + has_epsilon (R_last_kk) +
+          has_epsilon (R_last_kj));
+
+        if (1 == eps_check)
+        {
+          if (needs_parentheses (&R_temp_kk))
+            sb_printf1 (R_cur_r, "(%.*s)+", 3, &R_temp_kk);
+          else
+            sb_printf1 (R_cur_r, "%.*s+", 1, &R_temp_kk);
+        }
+      }
+    }
+    /*
+     * aa*b = a+b
+     * (e|a)(e|a)*b = a*b
+     */
+    else if (0 == sb_strcmp (&R_temp_ik, &R_temp_kk))
+    {
+      if (has_epsilon (R_last_ik))
+      {
+        if (needs_parentheses (&R_temp_kk))
+          sb_printf2 (R_cur_r, "(%.*s)*%.*s", 3, &R_temp_kk, R_last_kj);
+        else
+          sb_printf2 (R_cur_r, "%.*s*%.*s", 1, &R_temp_kk, R_last_kj);
+      }
+      else
+      {
+        if (needs_parentheses (&R_temp_kk))
+          sb_printf2 (R_cur_r, "(%.*s)+%.*s", 3, &R_temp_kk, R_last_kj);
+        else
+          sb_printf2 (R_cur_r, "%.*s+%.*s", 1, &R_temp_kk, R_last_kj);
+      }
+    }
+    /*
+     * ba*a = ba+
+     * b(e|a)*(e|a) = ba*
+     */
+    else if (0 == sb_strcmp (&R_temp_kk, &R_temp_kj))
+    {
+      if (has_epsilon (R_last_kj))
+      {
+        if (needs_parentheses (&R_temp_kk))
+          sb_printf2 (R_cur_r, "%.*s(%.*s)*", 3, R_last_ik, &R_temp_kk);
+        else
+          sb_printf2 (R_cur_r, "%.*s%.*s*", 1, R_last_ik, &R_temp_kk);
+      }
+      else
+      {
+        if (needs_parentheses (&R_temp_kk))
+          sb_printf2 (R_cur_r, "(%.*s)+%.*s", 3, R_last_ik, &R_temp_kk);
+        else
+          sb_printf2 (R_cur_r, "%.*s+%.*s", 1, R_last_ik, &R_temp_kk);
+      }
+    }
+    else
+    {
+      if (0 < R_temp_kk.slen)
+      {
+        if (needs_parentheses (&R_temp_kk))
+        {
+          sb_printf3 (R_cur_r, "%.*s(%.*s)*%.*s", 3, R_last_ik, &R_temp_kk,
+                     R_last_kj);
+        }
+        else
+        {
+          sb_printf3 (R_cur_r, "%.*s%.*s*%.*s", 1, R_last_ik, &R_temp_kk,
+                     R_last_kj);
+        }
+      }
+      else
+      {
+       sb_printf2 (R_cur_r, "%.*s%.*s", 0, R_last_ik, R_last_kj);
+      }
+    }
+  }
+  sb_free (&R_temp_ij);
+  sb_free (&R_temp_ik);
+  sb_free (&R_temp_kk);
+  sb_free (&R_temp_kj);
+
+  if ( (GNUNET_YES == R_cur_l->null_flag) &&
+       (GNUNET_YES == R_cur_r->null_flag) )
+  {
+    R_cur_ij->null_flag = GNUNET_YES;
+    return;
+  }
+
+  if ( (GNUNET_YES != R_cur_l->null_flag) &&
+       (GNUNET_YES == R_cur_r->null_flag) )
+  {
+    struct StringBuffer tmp;
+
+    tmp = *R_cur_ij;
+    *R_cur_ij = *R_cur_l;
+    *R_cur_l = tmp;
+    return;
+  }
+
+  if ( (GNUNET_YES == R_cur_l->null_flag) &&
+       (GNUNET_YES != R_cur_r->null_flag) )
+  {
+    struct StringBuffer tmp;
+
+    tmp = *R_cur_ij;
+    *R_cur_ij = *R_cur_r;
+    *R_cur_r = tmp;
+    return;
+  }
+
+  if (0 == sb_nullstrcmp (R_cur_l, R_cur_r))
+  {
+    struct StringBuffer tmp;
+
+    tmp = *R_cur_ij;
+    *R_cur_ij = *R_cur_l;
+    *R_cur_l = tmp;
+    return;
+  }
+  sb_printf2 (R_cur_ij, "(%.*s|%.*s)", 3, R_cur_l, R_cur_r);
+}
+
+
+/**
+ * Create proofs for all states in the given automaton. Implementation of the
+ * algorithm descriped in chapter 3.2.1 of "Automata Theory, Languages, and
+ * Computation 3rd Edition" by Hopcroft, Motwani and Ullman.
+ *
+ * Each state in the automaton gets assigned 'proof' and 'hash' (hash of the
+ * proof) fields. The starting state will only have a valid proof/hash if it has
+ * any incoming transitions.
+ *
+ * @param a automaton for which to assign proofs and hashes, must not be NULL
+ */
+static int
+automaton_create_proofs (struct REGEX_INTERNAL_Automaton *a)
+{
+  unsigned int n = a->state_count;
+  struct REGEX_INTERNAL_State *states[n];
+  struct StringBuffer *R_last;
+  struct StringBuffer *R_cur;
+  struct StringBuffer R_cur_r;
+  struct StringBuffer R_cur_l;
+  struct StringBuffer *R_swap;
+  struct REGEX_INTERNAL_Transition *t;
+  struct StringBuffer complete_regex;
+  unsigned int i;
+  unsigned int j;
+  unsigned int k;
+
+  R_last = GNUNET_malloc_large (sizeof (struct StringBuffer) * n * n);
+  R_cur = GNUNET_malloc_large (sizeof (struct StringBuffer) * n * n);
+  if ( (NULL == R_last) ||
+       (NULL == R_cur) )
+  {
+    GNUNET_log_strerror (GNUNET_ERROR_TYPE_ERROR, "malloc");
+    GNUNET_free_non_null (R_cur);
+    GNUNET_free_non_null (R_last);
+    return GNUNET_SYSERR;
+  }
+
+  /* create depth-first numbering of the states, initializes 'state' */
+  REGEX_INTERNAL_automaton_traverse (a, a->start, NULL, NULL, &number_states,
+                                   states);
+
+  for (i = 0; i < n; i++)
+    GNUNET_assert (NULL != states[i]);
+  for (i = 0; i < n; i++)
+    for (j = 0; j < n; j++)
+      R_last[i *n + j].null_flag = GNUNET_YES;
+
+  /* Compute regular expressions of length "1" between each pair of states */
+  for (i = 0; i < n; i++)
+  {
+    for (t = states[i]->transitions_head; NULL != t; t = t->next)
+    {
+      j = t->to_state->dfs_id;
+      if (GNUNET_YES == R_last[i * n + j].null_flag)
+      {
+        sb_strdup_cstr (&R_last[i * n + j], t->label);
+      }
+      else
+      {
+       sb_append_cstr (&R_last[i * n + j], "|");
+       sb_append_cstr (&R_last[i * n + j], t->label);
+      }
+    }
+    /* add self-loop: i is reachable from i via epsilon-transition */
+    if (GNUNET_YES == R_last[i * n + i].null_flag)
+    {
+      R_last[i * n + i].slen = 0;
+      R_last[i * n + i].null_flag = GNUNET_NO;
+    }
+    else
+    {
+      sb_wrap (&R_last[i * n + i], "(|%.*s)", 3);
+    }
+  }
+  for (i = 0; i < n; i++)
+    for (j = 0; j < n; j++)
+      if (needs_parentheses (&R_last[i * n + j]))
+        sb_wrap (&R_last[i * n + j], "(%.*s)", 2);
+  /* Compute regular expressions of length "k" between each pair of states per
+   * induction */
+  memset (&R_cur_l, 0, sizeof (struct StringBuffer));
+  memset (&R_cur_r, 0, sizeof (struct StringBuffer));
+  for (k = 0; k < n; k++)
+  {
+    for (i = 0; i < n; i++)
+    {
+      for (j = 0; j < n; j++)
+      {
+        /* Basis for the recursion:
+         * $R^{(k)}_{ij} = R^{(k-1)}_{ij} | R^{(k-1)}_{ik} ( R^{(k-1)}_{kk} )^* R^{(k-1)}_{kj}
+         * R_last == R^{(k-1)}, R_cur == R^{(k)}
+         */
+
+        /* Create R_cur[i][j] and simplify the expression */
+        automaton_create_proofs_simplify (&R_last[i * n + j], &R_last[i * n + k],
+                                          &R_last[k * n + k], &R_last[k * n + j],
+                                          &R_cur[i * n + j],
+                                         &R_cur_l, &R_cur_r);
+      }
+    }
+    /* set R_last = R_cur */
+    R_swap = R_last;
+    R_last = R_cur;
+    R_cur = R_swap;
+    /* clear 'R_cur' for next iteration */
+    for (i = 0; i < n; i++)
+      for (j = 0; j < n; j++)
+        R_cur[i * n + j].null_flag = GNUNET_YES;
+  }
+  sb_free (&R_cur_l);
+  sb_free (&R_cur_r);
+  /* assign proofs and hashes */
+  for (i = 0; i < n; i++)
+  {
+    if (GNUNET_YES != R_last[a->start->dfs_id * n + i].null_flag)
+    {
+      states[i]->proof = GNUNET_strndup (R_last[a->start->dfs_id * n + i].sbuf,
+                                        R_last[a->start->dfs_id * n + i].slen);
+      GNUNET_CRYPTO_hash (states[i]->proof, strlen (states[i]->proof),
+                          &states[i]->hash);
+    }
+  }
+
+  /* complete regex for whole DFA: union of all pairs (start state/accepting
+   * state(s)). */
+  sb_init (&complete_regex, 16 * n);
+  for (i = 0; i < n; i++)
+  {
+    if (states[i]->accepting)
+    {
+      if ( (0 == complete_regex.slen) &&
+          (0 < R_last[a->start->dfs_id * n + i].slen) )
+      {
+       sb_append (&complete_regex,
+                  &R_last[a->start->dfs_id * n + i]);
+      }
+      else if ( (GNUNET_YES != R_last[a->start->dfs_id * n + i].null_flag) &&
+               (0 < R_last[a->start->dfs_id * n + i].slen) )
+      {
+       sb_append_cstr (&complete_regex, "|");
+       sb_append (&complete_regex,
+                  &R_last[a->start->dfs_id * n + i]);
+      }
+    }
+  }
+  a->canonical_regex = GNUNET_strndup (complete_regex.sbuf, complete_regex.slen);
+
+  /* cleanup */
+  sb_free (&complete_regex);
+  for (i = 0; i < n; i++)
+    for (j = 0; j < n; j++)
+    {
+      sb_free (&R_cur[i * n + j]);
+      sb_free (&R_last[i * n + j]);
+    }
+  GNUNET_free (R_cur);
+  GNUNET_free (R_last);
+  return GNUNET_OK;
+}
+
+
+/**
+ * Creates a new DFA state based on a set of NFA states. Needs to be freed using
+ * automaton_destroy_state.
+ *
+ * @param ctx context
+ * @param nfa_states set of NFA states on which the DFA should be based on
+ *
+ * @return new DFA state
+ */
+static struct REGEX_INTERNAL_State *
+dfa_state_create (struct REGEX_INTERNAL_Context *ctx,
+                  struct REGEX_INTERNAL_StateSet *nfa_states)
+{
+  struct REGEX_INTERNAL_State *s;
+  char *pos;
+  size_t len;
+  struct REGEX_INTERNAL_State *cstate;
+  struct REGEX_INTERNAL_Transition *ctran;
+  unsigned int i;
+
+  s = GNUNET_new (struct REGEX_INTERNAL_State);
+  s->id = ctx->state_id++;
+  s->index = -1;
+  s->lowlink = -1;
+
+  if (NULL == nfa_states)
+  {
+    GNUNET_asprintf (&s->name, "s%i", s->id);
+    return s;
+  }
+
+  s->nfa_set = *nfa_states;
+
+  if (nfa_states->off < 1)
+    return s;
+
+  /* Create a name based on 'nfa_states' */
+  len = nfa_states->off * 14 + 4;
+  s->name = GNUNET_malloc (len);
+  strcat (s->name, "{");
+  pos = s->name + 1;
+
+  for (i = 0; i < nfa_states->off; i++)
+  {
+    cstate = nfa_states->states[i];
+    GNUNET_snprintf (pos,
+                     pos - s->name + len,
+                    "%i,",
+                     cstate->id);
+    pos += strlen (pos);
+
+    /* Add a transition for each distinct label to NULL state */
+    for (ctran = cstate->transitions_head; NULL != ctran; ctran = ctran->next)
+      if (NULL != ctran->label)
+        state_add_transition (ctx, s, ctran->label, NULL);
+
+    /* If the nfa_states contain an accepting state, the new dfa state is also
+     * accepting. */
+    if (cstate->accepting)
+      s->accepting = 1;
+  }
+  pos[-1] = '}';
+  s->name = GNUNET_realloc (s->name, strlen (s->name) + 1);
+
+  memset (nfa_states, 0, sizeof (struct REGEX_INTERNAL_StateSet));
+  return s;
+}
+
+
+/**
+ * Move from the given state 's' to the next state on transition 'str'. Consumes
+ * as much of the given 'str' as possible (usefull for strided DFAs). On return
+ * 's' will point to the next state, and the length of the substring used for
+ * this transition will be returned. If no transition possible 0 is returned and
+ * 's' points to NULL.
+ *
+ * @param s starting state, will point to the next state or NULL (if no
+ * transition possible)
+ * @param str edge label to follow (will match longest common prefix)
+ *
+ * @return length of the substring comsumed from 'str'
+ */
+static unsigned int
+dfa_move (struct REGEX_INTERNAL_State **s, const char *str)
+{
+  struct REGEX_INTERNAL_Transition *t;
+  struct REGEX_INTERNAL_State *new_s;
+  unsigned int len;
+  unsigned int max_len;
+
+  if (NULL == s)
+    return 0;
+
+  new_s = NULL;
+  max_len = 0;
+  for (t = (*s)->transitions_head; NULL != t; t = t->next)
+  {
+    len = strlen (t->label);
+
+    if (0 == strncmp (t->label, str, len))
+    {
+      if (len >= max_len)
+      {
+        max_len = len;
+        new_s = t->to_state;
+      }
+    }
+  }
+
+  *s = new_s;
+  return max_len;
+}
+
+
+/**
+ * Set the given state 'marked' to #GNUNET_YES. Used by the
+ * #dfa_remove_unreachable_states() function to detect unreachable states in the
+ * automaton.
+ *
+ * @param cls closure, not used.
+ * @param count count, not used.
+ * @param s state where the marked attribute will be set to #GNUNET_YES.
+ */
+static void
+mark_states (void *cls,
+             const unsigned int count,
+             struct REGEX_INTERNAL_State *s)
+{
+  s->marked = GNUNET_YES;
+}
+
+
+/**
+ * Remove all unreachable states from DFA 'a'. Unreachable states are those
+ * states that are not reachable from the starting state.
+ *
+ * @param a DFA automaton
+ */
+static void
+dfa_remove_unreachable_states (struct REGEX_INTERNAL_Automaton *a)
+{
+  struct REGEX_INTERNAL_State *s;
+  struct REGEX_INTERNAL_State *s_next;
+
+  /* 1. unmark all states */
+  for (s = a->states_head; NULL != s; s = s->next)
+    s->marked = GNUNET_NO;
+
+  /* 2. traverse dfa from start state and mark all visited states */
+  REGEX_INTERNAL_automaton_traverse (a, a->start, NULL, NULL, &mark_states, NULL);
+
+  /* 3. delete all states that were not visited */
+  for (s = a->states_head; NULL != s; s = s_next)
+  {
+    s_next = s->next;
+    if (GNUNET_NO == s->marked)
+      automaton_remove_state (a, s);
+  }
+}
+
+
+/**
+ * Remove all dead states from the DFA 'a'. Dead states are those states that do
+ * not transition to any other state but themselves.
+ *
+ * @param a DFA automaton
+ */
+static void
+dfa_remove_dead_states (struct REGEX_INTERNAL_Automaton *a)
+{
+  struct REGEX_INTERNAL_State *s;
+  struct REGEX_INTERNAL_State *s_next;
+  struct REGEX_INTERNAL_Transition *t;
+  int dead;
+
+  GNUNET_assert (DFA == a->type);
+
+  for (s = a->states_head; NULL != s; s = s_next)
+  {
+    s_next = s->next;
+
+    if (s->accepting)
+      continue;
+
+    dead = 1;
+    for (t = s->transitions_head; NULL != t; t = t->next)
+    {
+      if (NULL != t->to_state && t->to_state != s)
+      {
+        dead = 0;
+        break;
+      }
+    }
+
+    if (0 == dead)
+      continue;
+
+    /* state s is dead, remove it */
+    automaton_remove_state (a, s);
+  }
+}
+
+
+/**
+ * Merge all non distinguishable states in the DFA 'a'
+ *
+ * @param ctx context
+ * @param a DFA automaton
+ * @return #GNUNET_OK on success
+ */
+static int
+dfa_merge_nondistinguishable_states (struct REGEX_INTERNAL_Context *ctx,
+                                     struct REGEX_INTERNAL_Automaton *a)
+{
+  uint32_t *table;
+  struct REGEX_INTERNAL_State *s1;
+  struct REGEX_INTERNAL_State *s2;
+  struct REGEX_INTERNAL_Transition *t1;
+  struct REGEX_INTERNAL_Transition *t2;
+  struct REGEX_INTERNAL_State *s1_next;
+  struct REGEX_INTERNAL_State *s2_next;
+  int change;
+  unsigned int num_equal_edges;
+  unsigned int i;
+  unsigned int state_cnt;
+  unsigned long long idx;
+  unsigned long long idx1;
+
+  if ( (NULL == a) || (0 == a->state_count) )
+  {
+    GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+                "Could not merge nondistinguishable states, automaton was NULL.\n");
+    return GNUNET_SYSERR;
+  }
+
+  state_cnt = a->state_count;
+  table = GNUNET_malloc_large ((sizeof (uint32_t) * state_cnt * state_cnt / 32)  + sizeof (uint32_t));
+  if (NULL == table)
+  {
+    GNUNET_log_strerror (GNUNET_ERROR_TYPE_ERROR, "malloc");
+    return GNUNET_SYSERR;
+  }
+
+  for (i = 0, s1 = a->states_head; NULL != s1; s1 = s1->next)
+    s1->marked = i++;
+
+  /* Mark all pairs of accepting/!accepting states */
+  for (s1 = a->states_head; NULL != s1; s1 = s1->next)
+    for (s2 = a->states_head; NULL != s2; s2 = s2->next)
+      if ( (s1->accepting && !s2->accepting) ||
+          (!s1->accepting && s2->accepting) )
+      {
+       idx = (unsigned long long) s1->marked * state_cnt + s2->marked;
+        table[idx / 32] |= (1U << (idx % 32));
+      }
+
+  /* Find all equal states */
+  change = 1;
+  while (0 != change)
+  {
+    change = 0;
+    for (s1 = a->states_head; NULL != s1; s1 = s1->next)
+    {
+      for (s2 = a->states_head; NULL != s2 && s1 != s2; s2 = s2->next)
+      {
+       idx = (unsigned long long) s1->marked * state_cnt + s2->marked;
+        if (0 != (table[idx / 32] & (1U << (idx % 32))))
+          continue;
+        num_equal_edges = 0;
+        for (t1 = s1->transitions_head; NULL != t1; t1 = t1->next)
+        {
+          for (t2 = s2->transitions_head; NULL != t2; t2 = t2->next)
+          {
+            if (0 == strcmp (t1->label, t2->label))
+           {
+             num_equal_edges++;
+             /* same edge, but targets definitively different, so we're different
+                as well */
+             if (t1->to_state->marked > t2->to_state->marked)
+               idx1 = (unsigned long long) t1->to_state->marked * state_cnt + t2->to_state->marked;
+             else
+               idx1 = (unsigned long long) t2->to_state->marked * state_cnt + t1->to_state->marked;
+             if (0 != (table[idx1 / 32] & (1U << (idx1 % 32))))
+             {
+               table[idx / 32] |= (1U << (idx % 32));
+               change = 1; /* changed a marker, need to run again */
+             }
+           }
+         }
+        }
+        if ( (num_equal_edges != s1->transition_count) ||
+            (num_equal_edges != s2->transition_count) )
+        {
+          /* Make sure ALL edges of possible equal states are the same */
+         table[idx / 32] |= (1U << (idx % 32));
+         change = 1;  /* changed a marker, need to run again */
+        }
+      }
+    }
+  }
+
+  /* Merge states that are equal */
+  for (s1 = a->states_head; NULL != s1; s1 = s1_next)
+  {
+    s1_next = s1->next;
+    for (s2 = a->states_head; NULL != s2 && s1 != s2; s2 = s2_next)
+    {
+      s2_next = s2->next;
+      idx = (unsigned long long) s1->marked * state_cnt + s2->marked;
+      if (0 == (table[idx / 32] & (1U << (idx % 32))))
+        automaton_merge_states (ctx, a, s1, s2);
+    }
+  }
+
+  GNUNET_free (table);
+  return GNUNET_OK;
+}
+
+
+/**
+ * Minimize the given DFA 'a' by removing all unreachable states, removing all
+ * dead states and merging all non distinguishable states
+ *
+ * @param ctx context
+ * @param a DFA automaton
+ * @return GNUNET_OK on success
+ */
+static int
+dfa_minimize (struct REGEX_INTERNAL_Context *ctx,
+              struct REGEX_INTERNAL_Automaton *a)
+{
+  if (NULL == a)
+    return GNUNET_SYSERR;
+
+  GNUNET_assert (DFA == a->type);
+
+  /* 1. remove unreachable states */
+  dfa_remove_unreachable_states (a);
+
+  /* 2. remove dead states */
+  dfa_remove_dead_states (a);
+
+  /* 3. Merge nondistinguishable states */
+  if (GNUNET_OK != dfa_merge_nondistinguishable_states (ctx, a))
+    return GNUNET_SYSERR;
+  return GNUNET_OK;
+}
+
+
+/**
+ * Context for adding strided transitions to a DFA.
+ */
+struct REGEX_INTERNAL_Strided_Context
+{
+  /**
+   * Length of the strides.
+   */
+  const unsigned int stride;
+
+  /**
+   * Strided transitions DLL. New strided transitions will be stored in this DLL
+   * and afterwards added to the DFA.
+   */
+  struct REGEX_INTERNAL_Transition *transitions_head;
+
+  /**
+   * Strided transitions DLL.
+   */
+  struct REGEX_INTERNAL_Transition *transitions_tail;
+};
+
+
+/**
+ * Recursive helper function to add strides to a DFA.
+ *
+ * @param cls context, contains stride length and strided transitions DLL.
+ * @param depth current depth of the depth-first traversal of the graph.
+ * @param label current label, string that contains all labels on the path from
+ *        'start' to 's'.
+ * @param start start state for the depth-first traversal of the graph.
+ * @param s current state in the depth-first traversal
+ */
+static void
+dfa_add_multi_strides_helper (void *cls, const unsigned int depth, char *label,
+                              struct REGEX_INTERNAL_State *start,
+                              struct REGEX_INTERNAL_State *s)
+{
+  struct REGEX_INTERNAL_Strided_Context *ctx = cls;
+  struct REGEX_INTERNAL_Transition *t;
+  char *new_label;
+
+  if (depth == ctx->stride)
+  {
+    t = GNUNET_new (struct REGEX_INTERNAL_Transition);
+    t->label = GNUNET_strdup (label);
+    t->to_state = s;
+    t->from_state = start;
+    GNUNET_CONTAINER_DLL_insert (ctx->transitions_head, ctx->transitions_tail,
+                                 t);
+  }
+  else
+  {
+    for (t = s->transitions_head; NULL != t; t = t->next)
+    {
+      /* Do not consider self-loops, because it end's up in too many
+       * transitions */
+      if (t->to_state == t->from_state)
+        continue;
+
+      if (NULL != label)
+      {
+        GNUNET_asprintf (&new_label, "%s%s", label, t->label);
+      }
+      else
+        new_label = GNUNET_strdup (t->label);
+
+      dfa_add_multi_strides_helper (cls, (depth + 1), new_label, start,
+                                    t->to_state);
+    }
+  }
+  GNUNET_free_non_null (label);
+}
+
+
+/**
+ * Function called for each state in the DFA. Starts a traversal of depth set in
+ * context starting from state 's'.
+ *
+ * @param cls context.
+ * @param count not used.
+ * @param s current state.
+ */
+static void
+dfa_add_multi_strides (void *cls, const unsigned int count,
+                       struct REGEX_INTERNAL_State *s)
+{
+  dfa_add_multi_strides_helper (cls, 0, NULL, s, s);
+}
+
+
+/**
+ * Adds multi-strided transitions to the given 'dfa'.
+ *
+ * @param regex_ctx regex context needed to add transitions to the automaton.
+ * @param dfa DFA to which the multi strided transitions should be added.
+ * @param stride_len length of the strides.
+ */
+void
+REGEX_INTERNAL_dfa_add_multi_strides (struct REGEX_INTERNAL_Context *regex_ctx,
+                                    struct REGEX_INTERNAL_Automaton *dfa,
+                                    const unsigned int stride_len)
+{
+  struct REGEX_INTERNAL_Strided_Context ctx = { stride_len, NULL, NULL };
+  struct REGEX_INTERNAL_Transition *t;
+  struct REGEX_INTERNAL_Transition *t_next;
+
+  if (1 > stride_len || GNUNET_YES == dfa->is_multistrided)
+    return;
+
+  /* Compute the new transitions of given stride_len */
+  REGEX_INTERNAL_automaton_traverse (dfa, dfa->start, NULL, NULL,
+                                   &dfa_add_multi_strides, &ctx);
+
+  /* Add all the new transitions to the automaton. */
+  for (t = ctx.transitions_head; NULL != t; t = t_next)
+  {
+    t_next = t->next;
+    state_add_transition (regex_ctx, t->from_state, t->label, t->to_state);
+    GNUNET_CONTAINER_DLL_remove (ctx.transitions_head, ctx.transitions_tail, t);
+    GNUNET_free_non_null (t->label);
+    GNUNET_free (t);
+  }
+
+  /* Mark this automaton as multistrided */
+  dfa->is_multistrided = GNUNET_YES;
+}
+
+/**
+ * Recursive Helper function for DFA path compression. Does DFS on the DFA graph
+ * and adds new transitions to the given transitions DLL and marks states that
+ * should be removed by setting state->contained to GNUNET_YES.
+ *
+ * @param dfa DFA for which the paths should be compressed.
+ * @param start starting state for linear path search.
+ * @param cur current state in the recursive DFS.
+ * @param label current label (string of traversed labels).
+ * @param max_len maximal path compression length.
+ * @param transitions_head transitions DLL.
+ * @param transitions_tail transitions DLL.
+ */
+void
+dfa_compress_paths_helper (struct REGEX_INTERNAL_Automaton *dfa,
+                           struct REGEX_INTERNAL_State *start,
+                           struct REGEX_INTERNAL_State *cur, char *label,
+                           unsigned int max_len,
+                           struct REGEX_INTERNAL_Transition **transitions_head,
+                           struct REGEX_INTERNAL_Transition **transitions_tail)
+{
+  struct REGEX_INTERNAL_Transition *t;
+  char *new_label;
+
+
+  if (NULL != label &&
+      ((cur->incoming_transition_count > 1 || GNUNET_YES == cur->accepting ||
+        GNUNET_YES == cur->marked) || (start != dfa->start && max_len > 0 &&
+                                       max_len == strlen (label)) ||
+       (start == dfa->start && GNUNET_REGEX_INITIAL_BYTES == strlen (label))))
+  {
+    t = GNUNET_new (struct REGEX_INTERNAL_Transition);
+    t->label = GNUNET_strdup (label);
+    t->to_state = cur;
+    t->from_state = start;
+    GNUNET_CONTAINER_DLL_insert (*transitions_head, *transitions_tail, t);
+
+    if (GNUNET_NO == cur->marked)
+    {
+      dfa_compress_paths_helper (dfa, cur, cur, NULL, max_len, transitions_head,
+                                 transitions_tail);
+    }
+    return;
+  }
+  else if (cur != start)
+    cur->contained = GNUNET_YES;
+
+  if (GNUNET_YES == cur->marked && cur != start)
+    return;
+
+  cur->marked = GNUNET_YES;
+
+
+  for (t = cur->transitions_head; NULL != t; t = t->next)
+  {
+    if (NULL != label)
+      GNUNET_asprintf (&new_label, "%s%s", label, t->label);
+    else
+      new_label = GNUNET_strdup (t->label);
+
+    if (t->to_state != cur)
+    {
+      dfa_compress_paths_helper (dfa, start, t->to_state, new_label, max_len,
+                                 transitions_head, transitions_tail);
+    }
+    GNUNET_free (new_label);
+  }
+}
+
+
+/**
+ * Compress paths in the given 'dfa'. Linear paths like 0->1->2->3 will be
+ * compressed to 0->3 by combining transitions.
+ *
+ * @param regex_ctx context for adding new transitions.
+ * @param dfa DFA representation, will directly modify the given DFA.
+ * @param max_len maximal length of the compressed paths.
+ */
+static void
+dfa_compress_paths (struct REGEX_INTERNAL_Context *regex_ctx,
+                    struct REGEX_INTERNAL_Automaton *dfa, unsigned int max_len)
+{
+  struct REGEX_INTERNAL_State *s;
+  struct REGEX_INTERNAL_State *s_next;
+  struct REGEX_INTERNAL_Transition *t;
+  struct REGEX_INTERNAL_Transition *t_next;
+  struct REGEX_INTERNAL_Transition *transitions_head = NULL;
+  struct REGEX_INTERNAL_Transition *transitions_tail = NULL;
+
+  if (NULL == dfa)
+    return;
+
+  /* Count the incoming transitions on each state. */
+  for (s = dfa->states_head; NULL != s; s = s->next)
+  {
+    for (t = s->transitions_head; NULL != t; t = t->next)
+    {
+      if (NULL != t->to_state)
+        t->to_state->incoming_transition_count++;
+    }
+  }
+
+  /* Unmark all states. */
+  for (s = dfa->states_head; NULL != s; s = s->next)
+  {
+    s->marked = GNUNET_NO;
+    s->contained = GNUNET_NO;
+  }
+
+  /* Add strides and mark states that can be deleted. */
+  dfa_compress_paths_helper (dfa, dfa->start, dfa->start, NULL, max_len,
+                             &transitions_head, &transitions_tail);
+
+  /* Add all the new transitions to the automaton. */
+  for (t = transitions_head; NULL != t; t = t_next)
+  {
+    t_next = t->next;
+    state_add_transition (regex_ctx, t->from_state, t->label, t->to_state);
+    GNUNET_CONTAINER_DLL_remove (transitions_head, transitions_tail, t);
+    GNUNET_free_non_null (t->label);
+    GNUNET_free (t);
+  }
+
+  /* Remove marked states (including their incoming and outgoing transitions). */
+  for (s = dfa->states_head; NULL != s; s = s_next)
+  {
+    s_next = s->next;
+    if (GNUNET_YES == s->contained)
+      automaton_remove_state (dfa, s);
+  }
+}
+
+
+/**
+ * Creates a new NFA fragment. Needs to be cleared using
+ * automaton_fragment_clear.
+ *
+ * @param start starting state
+ * @param end end state
+ *
+ * @return new NFA fragment
+ */
+static struct REGEX_INTERNAL_Automaton *
+nfa_fragment_create (struct REGEX_INTERNAL_State *start,
+                     struct REGEX_INTERNAL_State *end)
+{
+  struct REGEX_INTERNAL_Automaton *n;
+
+  n = GNUNET_new (struct REGEX_INTERNAL_Automaton);
+
+  n->type = NFA;
+  n->start = NULL;
+  n->end = NULL;
+  n->state_count = 0;
+
+  if (NULL == start || NULL == end)
+    return n;
+
+  automaton_add_state (n, end);
+  automaton_add_state (n, start);
+
+  n->state_count = 2;
+
+  n->start = start;
+  n->end = end;
+
+  return n;
+}
+
+
+/**
+ * Adds a list of states to the given automaton 'n'.
+ *
+ * @param n automaton to which the states should be added
+ * @param states_head head of the DLL of states
+ * @param states_tail tail of the DLL of states
+ */
+static void
+nfa_add_states (struct REGEX_INTERNAL_Automaton *n,
+                struct REGEX_INTERNAL_State *states_head,
+                struct REGEX_INTERNAL_State *states_tail)
+{
+  struct REGEX_INTERNAL_State *s;
+
+  if (NULL == n || NULL == states_head)
+  {
+    GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Could not add states\n");
+    return;
+  }
+
+  if (NULL == n->states_head)
+  {
+    n->states_head = states_head;
+    n->states_tail = states_tail;
+    return;
+  }
+
+  if (NULL != states_head)
+  {
+    n->states_tail->next = states_head;
+    n->states_tail = states_tail;
+  }
+
+  for (s = states_head; NULL != s; s = s->next)
+    n->state_count++;
+}
+
+
+/**
+ * Creates a new NFA state. Needs to be freed using automaton_destroy_state.
+ *
+ * @param ctx context
+ * @param accepting is it an accepting state or not
+ *
+ * @return new NFA state
+ */
+static struct REGEX_INTERNAL_State *
+nfa_state_create (struct REGEX_INTERNAL_Context *ctx, int accepting)
+{
+  struct REGEX_INTERNAL_State *s;
+
+  s = GNUNET_new (struct REGEX_INTERNAL_State);
+  s->id = ctx->state_id++;
+  s->accepting = accepting;
+  s->marked = GNUNET_NO;
+  s->contained = 0;
+  s->index = -1;
+  s->lowlink = -1;
+  s->scc_id = 0;
+  s->name = NULL;
+  GNUNET_asprintf (&s->name, "s%i", s->id);
+
+  return s;
+}
+
+
+/**
+ * Calculates the closure set for the given set of states.
+ *
+ * @param ret set to sorted nfa closure on 'label' (epsilon closure if 'label' is NULL)
+ * @param nfa the NFA containing 's'
+ * @param states list of states on which to base the closure on
+ * @param label transitioning label for which to base the closure on,
+ *                pass NULL for epsilon transition
+ */
+static void
+nfa_closure_set_create (struct REGEX_INTERNAL_StateSet *ret,
+                       struct REGEX_INTERNAL_Automaton *nfa,
+                        struct REGEX_INTERNAL_StateSet *states, const char *label)
+{
+  struct REGEX_INTERNAL_State *s;
+  unsigned int i;
+  struct REGEX_INTERNAL_StateSet_MDLL cls_stack;
+  struct REGEX_INTERNAL_State *clsstate;
+  struct REGEX_INTERNAL_State *currentstate;
+  struct REGEX_INTERNAL_Transition *ctran;
+
+  memset (ret, 0, sizeof (struct REGEX_INTERNAL_StateSet));
+  if (NULL == states)
+    return;
+
+  for (i = 0; i < states->off; i++)
+  {
+    s = states->states[i];
+
+    /* Add start state to closure only for epsilon closure */
+    if (NULL == label)
+      state_set_append (ret, s);
+
+    /* initialize work stack */
+    cls_stack.head = NULL;
+    cls_stack.tail = NULL;
+    GNUNET_CONTAINER_MDLL_insert (ST, cls_stack.head, cls_stack.tail, s);
+    cls_stack.len = 1;
+
+    while (NULL != (currentstate = cls_stack.tail))
+    {
+      GNUNET_CONTAINER_MDLL_remove (ST, cls_stack.head, cls_stack.tail,
+                                   currentstate);
+      cls_stack.len--;
+      for (ctran = currentstate->transitions_head; NULL != ctran;
+          ctran = ctran->next)
+      {
+       if (NULL == (clsstate = ctran->to_state))
+         continue;
+       if (0 != clsstate->contained)
+         continue;
+       if (0 != nullstrcmp (label, ctran->label))
+         continue;
+       state_set_append (ret, clsstate);
+       GNUNET_CONTAINER_MDLL_insert_tail (ST, cls_stack.head, cls_stack.tail,
+                                          clsstate);
+       cls_stack.len++;
+       clsstate->contained = 1;
+      }
+    }
+  }
+  for (i = 0; i < ret->off; i++)
+    ret->states[i]->contained = 0;
+
+  if (ret->off > 1)
+    qsort (ret->states, ret->off, sizeof (struct REGEX_INTERNAL_State *),
+           &state_compare);
+}
+
+
+/**
+ * Pops two NFA fragments (a, b) from the stack and concatenates them (ab)
+ *
+ * @param ctx context
+ */
+static void
+nfa_add_concatenation (struct REGEX_INTERNAL_Context *ctx)
+{
+  struct REGEX_INTERNAL_Automaton *a;
+  struct REGEX_INTERNAL_Automaton *b;
+  struct REGEX_INTERNAL_Automaton *new_nfa;
+
+  b = ctx->stack_tail;
+  GNUNET_assert (NULL != b);
+  GNUNET_CONTAINER_DLL_remove (ctx->stack_head, ctx->stack_tail, b);
+  a = ctx->stack_tail;
+  GNUNET_assert (NULL != a);
+  GNUNET_CONTAINER_DLL_remove (ctx->stack_head, ctx->stack_tail, a);
+
+  state_add_transition (ctx, a->end, NULL, b->start);
+  a->end->accepting = 0;
+  b->end->accepting = 1;
+
+  new_nfa = nfa_fragment_create (NULL, NULL);
+  nfa_add_states (new_nfa, a->states_head, a->states_tail);
+  nfa_add_states (new_nfa, b->states_head, b->states_tail);
+  new_nfa->start = a->start;
+  new_nfa->end = b->end;
+  new_nfa->state_count += a->state_count + b->state_count;
+  automaton_fragment_clear (a);
+  automaton_fragment_clear (b);
+
+  GNUNET_CONTAINER_DLL_insert_tail (ctx->stack_head, ctx->stack_tail, new_nfa);
+}
+
+
+/**
+ * Pops a NFA fragment from the stack (a) and adds a new fragment (a*)
+ *
+ * @param ctx context
+ */
+static void
+nfa_add_star_op (struct REGEX_INTERNAL_Context *ctx)
+{
+  struct REGEX_INTERNAL_Automaton *a;
+  struct REGEX_INTERNAL_Automaton *new_nfa;
+  struct REGEX_INTERNAL_State *start;
+  struct REGEX_INTERNAL_State *end;
+
+  a = ctx->stack_tail;
+
+  if (NULL == a)
+  {
+    GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+                "nfa_add_star_op failed, because there was no element on the stack");
+    return;
+  }
+
+  GNUNET_CONTAINER_DLL_remove (ctx->stack_head, ctx->stack_tail, a);
+
+  start = nfa_state_create (ctx, 0);
+  end = nfa_state_create (ctx, 1);
+
+  state_add_transition (ctx, start, NULL, a->start);
+  state_add_transition (ctx, start, NULL, end);
+  state_add_transition (ctx, a->end, NULL, a->start);
+  state_add_transition (ctx, a->end, NULL, end);
+
+  a->end->accepting = 0;
+  end->accepting = 1;
+
+  new_nfa = nfa_fragment_create (start, end);
+  nfa_add_states (new_nfa, a->states_head, a->states_tail);
+  automaton_fragment_clear (a);
+
+  GNUNET_CONTAINER_DLL_insert_tail (ctx->stack_head, ctx->stack_tail, new_nfa);
+}
+
+
+/**
+ * Pops an NFA fragment (a) from the stack and adds a new fragment (a+)
+ *
+ * @param ctx context
+ */
+static void
+nfa_add_plus_op (struct REGEX_INTERNAL_Context *ctx)
+{
+  struct REGEX_INTERNAL_Automaton *a;
+
+  a = ctx->stack_tail;
+
+  if (NULL == a)
+  {
+    GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+                "nfa_add_plus_op failed, because there was no element on the stack");
+    return;
+  }
+
+  GNUNET_CONTAINER_DLL_remove (ctx->stack_head, ctx->stack_tail, a);
+
+  state_add_transition (ctx, a->end, NULL, a->start);
+
+  GNUNET_CONTAINER_DLL_insert_tail (ctx->stack_head, ctx->stack_tail, a);
+}
+
+
+/**
+ * Pops an NFA fragment (a) from the stack and adds a new fragment (a?)
+ *
+ * @param ctx context
+ */
+static void
+nfa_add_question_op (struct REGEX_INTERNAL_Context *ctx)
+{
+  struct REGEX_INTERNAL_Automaton *a;
+  struct REGEX_INTERNAL_Automaton *new_nfa;
+  struct REGEX_INTERNAL_State *start;
+  struct REGEX_INTERNAL_State *end;
+
+  a = ctx->stack_tail;
+  if (NULL == a)
+  {
+    GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+                "nfa_add_question_op failed, because there was no element on the stack");
+    return;
+  }
+
+  GNUNET_CONTAINER_DLL_remove (ctx->stack_head, ctx->stack_tail, a);
+
+  start = nfa_state_create (ctx, 0);
+  end = nfa_state_create (ctx, 1);
+
+  state_add_transition (ctx, start, NULL, a->start);
+  state_add_transition (ctx, start, NULL, end);
+  state_add_transition (ctx, a->end, NULL, end);
+
+  a->end->accepting = 0;
+
+  new_nfa = nfa_fragment_create (start, end);
+  nfa_add_states (new_nfa, a->states_head, a->states_tail);
+  GNUNET_CONTAINER_DLL_insert_tail (ctx->stack_head, ctx->stack_tail, new_nfa);
+  automaton_fragment_clear (a);
+}
+
+
+/**
+ * Pops two NFA fragments (a, b) from the stack and adds a new NFA fragment that
+ * alternates between a and b (a|b)
+ *
+ * @param ctx context
+ */
+static void
+nfa_add_alternation (struct REGEX_INTERNAL_Context *ctx)
+{
+  struct REGEX_INTERNAL_Automaton *a;
+  struct REGEX_INTERNAL_Automaton *b;
+  struct REGEX_INTERNAL_Automaton *new_nfa;
+  struct REGEX_INTERNAL_State *start;
+  struct REGEX_INTERNAL_State *end;
+
+  b = ctx->stack_tail;
+  GNUNET_assert (NULL != b);
+  GNUNET_CONTAINER_DLL_remove (ctx->stack_head, ctx->stack_tail, b);
+  a = ctx->stack_tail;
+  GNUNET_assert (NULL != a);
+  GNUNET_CONTAINER_DLL_remove (ctx->stack_head, ctx->stack_tail, a);
+
+  start = nfa_state_create (ctx, 0);
+  end = nfa_state_create (ctx, 1);
+  state_add_transition (ctx, start, NULL, a->start);
+  state_add_transition (ctx, start, NULL, b->start);
+
+  state_add_transition (ctx, a->end, NULL, end);
+  state_add_transition (ctx, b->end, NULL, end);
+
+  a->end->accepting = 0;
+  b->end->accepting = 0;
+  end->accepting = 1;
+
+  new_nfa = nfa_fragment_create (start, end);
+  nfa_add_states (new_nfa, a->states_head, a->states_tail);
+  nfa_add_states (new_nfa, b->states_head, b->states_tail);
+  automaton_fragment_clear (a);
+  automaton_fragment_clear (b);
+
+  GNUNET_CONTAINER_DLL_insert_tail (ctx->stack_head, ctx->stack_tail, new_nfa);
+}
+
+
+/**
+ * Adds a new nfa fragment to the stack
+ *
+ * @param ctx context
+ * @param label label for nfa transition
+ */
+static void
+nfa_add_label (struct REGEX_INTERNAL_Context *ctx, const char *label)
+{
+  struct REGEX_INTERNAL_Automaton *n;
+  struct REGEX_INTERNAL_State *start;
+  struct REGEX_INTERNAL_State *end;
+
+  GNUNET_assert (NULL != ctx);
+
+  start = nfa_state_create (ctx, 0);
+  end = nfa_state_create (ctx, 1);
+  state_add_transition (ctx, start, label, end);
+  n = nfa_fragment_create (start, end);
+  GNUNET_assert (NULL != n);
+  GNUNET_CONTAINER_DLL_insert_tail (ctx->stack_head, ctx->stack_tail, n);
+}
+
+
+/**
+ * Initialize a new context
+ *
+ * @param ctx context
+ */
+static void
+REGEX_INTERNAL_context_init (struct REGEX_INTERNAL_Context *ctx)
+{
+  if (NULL == ctx)
+  {
+    GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Context was NULL!");
+    return;
+  }
+  ctx->state_id = 0;
+  ctx->transition_id = 0;
+  ctx->stack_head = NULL;
+  ctx->stack_tail = NULL;
+}
+
+
+/**
+ * Construct an NFA by parsing the regex string of length 'len'.
+ *
+ * @param regex regular expression string
+ * @param len length of the string
+ *
+ * @return NFA, needs to be freed using REGEX_INTERNAL_destroy_automaton
+ */
+struct REGEX_INTERNAL_Automaton *
+REGEX_INTERNAL_construct_nfa (const char *regex, const size_t len)
+{
+  struct REGEX_INTERNAL_Context ctx;
+  struct REGEX_INTERNAL_Automaton *nfa;
+  const char *regexp;
+  char curlabel[2];
+  char *error_msg;
+  unsigned int count;
+  unsigned int altcount;
+  unsigned int atomcount;
+  unsigned int poff;
+  unsigned int psize;
+  struct
+  {
+    int altcount;
+    int atomcount;
+  }     *p;
+
+  if (NULL == regex || 0 == strlen (regex) || 0 == len)
+  {
+    GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+                "Could not parse regex. Empty regex string provided.\n");
+
+    return NULL;
+  }
+  REGEX_INTERNAL_context_init (&ctx);
+
+  regexp = regex;
+  curlabel[1] = '\0';
+  p = NULL;
+  error_msg = NULL;
+  altcount = 0;
+  atomcount = 0;
+  poff = 0;
+  psize = 0;
+
+  for (count = 0; count < len && *regexp; count++, regexp++)
+  {
+    switch (*regexp)
+    {
+    case '(':
+      if (atomcount > 1)
+      {
+        --atomcount;
+        nfa_add_concatenation (&ctx);
+      }
+      if (poff == psize)
+        GNUNET_array_grow (p, psize, psize * 2 + 4); /* FIXME why *2 +4? */
+      p[poff].altcount = altcount;
+      p[poff].atomcount = atomcount;
+      poff++;
+      altcount = 0;
+      atomcount = 0;
+      break;
+    case '|':
+      if (0 == atomcount)
+      {
+        error_msg = "Cannot append '|' to nothing";
+        goto error;
+      }
+      while (--atomcount > 0)
+        nfa_add_concatenation (&ctx);
+      altcount++;
+      break;
+    case ')':
+      if (0 == poff)
+      {
+        error_msg = "Missing opening '('";
+        goto error;
+      }
+      if (0 == atomcount)
+      {
+        /* Ignore this: "()" */
+        poff--;
+        altcount = p[poff].altcount;
+        atomcount = p[poff].atomcount;
+        break;
+      }
+      while (--atomcount > 0)
+        nfa_add_concatenation (&ctx);
+      for (; altcount > 0; altcount--)
+        nfa_add_alternation (&ctx);
+      poff--;
+      altcount = p[poff].altcount;
+      atomcount = p[poff].atomcount;
+      atomcount++;
+      break;
+    case '*':
+      if (atomcount == 0)
+      {
+        error_msg = "Cannot append '*' to nothing";
+        goto error;
+      }
+      nfa_add_star_op (&ctx);
+      break;
+    case '+':
+      if (atomcount == 0)
+      {
+        error_msg = "Cannot append '+' to nothing";
+        goto error;
+      }
+      nfa_add_plus_op (&ctx);
+      break;
+    case '?':
+      if (atomcount == 0)
+      {
+        error_msg = "Cannot append '?' to nothing";
+        goto error;
+      }
+      nfa_add_question_op (&ctx);
+      break;
+    default:
+      if (atomcount > 1)
+      {
+        --atomcount;
+        nfa_add_concatenation (&ctx);
+      }
+      curlabel[0] = *regexp;
+      nfa_add_label (&ctx, curlabel);
+      atomcount++;
+      break;
+    }
+  }
+  if (0 != poff)
+  {
+    error_msg = "Unbalanced parenthesis";
+    goto error;
+  }
+  while (--atomcount > 0)
+    nfa_add_concatenation (&ctx);
+  for (; altcount > 0; altcount--)
+    nfa_add_alternation (&ctx);
+
+  GNUNET_array_grow (p, psize, 0);
+
+  nfa = ctx.stack_tail;
+  GNUNET_CONTAINER_DLL_remove (ctx.stack_head, ctx.stack_tail, nfa);
+
+  if (NULL != ctx.stack_head)
+  {
+    error_msg = "Creating the NFA failed. NFA stack was not empty!";
+    goto error;
+  }
+
+  /* Remember the regex that was used to generate this NFA */
+  nfa->regex = GNUNET_strdup (regex);
+
+  /* create depth-first numbering of the states for pretty printing */
+  REGEX_INTERNAL_automaton_traverse (nfa, NULL, NULL, NULL, &number_states, NULL);
+
+  /* No multistriding added so far */
+  nfa->is_multistrided = GNUNET_NO;
+
+  return nfa;
+
+error:
+  GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Could not parse regex: `%s'\n", regex);
+  if (NULL != error_msg)
+    GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "%s\n", error_msg);
+
+  GNUNET_free_non_null (p);
+
+  while (NULL != (nfa = ctx.stack_head))
+  {
+    GNUNET_CONTAINER_DLL_remove (ctx.stack_head, ctx.stack_tail, nfa);
+    REGEX_INTERNAL_automaton_destroy (nfa);
+  }
+
+  return NULL;
+}
+
+
+/**
+ * Create DFA states based on given 'nfa' and starting with 'dfa_state'.
+ *
+ * @param ctx context.
+ * @param nfa NFA automaton.
+ * @param dfa DFA automaton.
+ * @param dfa_state current dfa state, pass epsilon closure of first nfa state
+ *                  for starting.
+ */
+static void
+construct_dfa_states (struct REGEX_INTERNAL_Context *ctx,
+                      struct REGEX_INTERNAL_Automaton *nfa,
+                      struct REGEX_INTERNAL_Automaton *dfa,
+                      struct REGEX_INTERNAL_State *dfa_state)
+{
+  struct REGEX_INTERNAL_Transition *ctran;
+  struct REGEX_INTERNAL_State *new_dfa_state;
+  struct REGEX_INTERNAL_State *state_contains;
+  struct REGEX_INTERNAL_State *state_iter;
+  struct REGEX_INTERNAL_StateSet tmp;
+  struct REGEX_INTERNAL_StateSet nfa_set;
+
+  for (ctran = dfa_state->transitions_head; NULL != ctran; ctran = ctran->next)
+  {
+    if (NULL == ctran->label || NULL != ctran->to_state)
+      continue;
+
+    nfa_closure_set_create (&tmp, nfa, &dfa_state->nfa_set, ctran->label);
+    nfa_closure_set_create (&nfa_set, nfa, &tmp, NULL);
+    state_set_clear (&tmp);
+
+    state_contains = NULL;
+    for (state_iter = dfa->states_head; NULL != state_iter;
+         state_iter = state_iter->next)
+    {
+      if (0 == state_set_compare (&state_iter->nfa_set, &nfa_set))
+      {
+        state_contains = state_iter;
+       break;
+      }
+    }
+    if (NULL == state_contains)
+    {
+      new_dfa_state = dfa_state_create (ctx, &nfa_set);
+      automaton_add_state (dfa, new_dfa_state);
+      ctran->to_state = new_dfa_state;
+      construct_dfa_states (ctx, nfa, dfa, new_dfa_state);
+    }
+    else
+    {
+      ctran->to_state = state_contains;
+      state_set_clear (&nfa_set);
+    }
+  }
+}
+
+
+/**
+ * Construct DFA for the given 'regex' of length 'len'.
+ *
+ * Path compression means, that for example a DFA o -> a -> b -> c -> o will be
+ * compressed to o -> abc -> o. Note that this parameter influences the
+ * non-determinism of states of the resulting NFA in the DHT (number of outgoing
+ * edges with the same label). For example for an application that stores IPv4
+ * addresses as bitstrings it could make sense to limit the path compression to
+ * 4 or 8.
+ *
+ * @param regex regular expression string.
+ * @param len length of the regular expression.
+ * @param max_path_len limit the path compression length to the
+ *        given value. If set to 1, no path compression is applied. Set to 0 for
+ *        maximal possible path compression (generally not desireable).
+ * @return DFA, needs to be freed using REGEX_INTERNAL_automaton_destroy.
+ */
+struct REGEX_INTERNAL_Automaton *
+REGEX_INTERNAL_construct_dfa (const char *regex, const size_t len,
+                              unsigned int max_path_len)
+{
+  struct REGEX_INTERNAL_Context ctx;
+  struct REGEX_INTERNAL_Automaton *dfa;
+  struct REGEX_INTERNAL_Automaton *nfa;
+  struct REGEX_INTERNAL_StateSet nfa_start_eps_cls;
+  struct REGEX_INTERNAL_StateSet singleton_set;
+
+  REGEX_INTERNAL_context_init (&ctx);
+
+  /* Create NFA */
+  nfa = REGEX_INTERNAL_construct_nfa (regex, len);
+
+  if (NULL == nfa)
+  {
+    GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+                "Could not create DFA, because NFA creation failed\n");
+    return NULL;
+  }
+
+  dfa = GNUNET_new (struct REGEX_INTERNAL_Automaton);
+  dfa->type = DFA;
+  dfa->regex = GNUNET_strdup (regex);
+
+  /* Create DFA start state from epsilon closure */
+  memset (&singleton_set, 0, sizeof (struct REGEX_INTERNAL_StateSet));
+  state_set_append (&singleton_set, nfa->start);
+  nfa_closure_set_create (&nfa_start_eps_cls, nfa, &singleton_set, NULL);
+  state_set_clear (&singleton_set);
+  dfa->start = dfa_state_create (&ctx, &nfa_start_eps_cls);
+  automaton_add_state (dfa, dfa->start);
+
+  construct_dfa_states (&ctx, nfa, dfa, dfa->start);
+  REGEX_INTERNAL_automaton_destroy (nfa);
+
+  /* Minimize DFA */
+  if (GNUNET_OK != dfa_minimize (&ctx, dfa))
+  {
+    REGEX_INTERNAL_automaton_destroy (dfa);
+    return NULL;
+  }
+
+  /* Create proofs and hashes for all states */
+  if (GNUNET_OK != automaton_create_proofs (dfa))
+  {
+    REGEX_INTERNAL_automaton_destroy (dfa);
+    return NULL;
+  }
+
+  /* Compress linear DFA paths */
+  if (1 != max_path_len)
+    dfa_compress_paths (&ctx, dfa, max_path_len);
+
+  return dfa;
+}
+
+
+/**
+ * Free the memory allocated by constructing the REGEX_INTERNAL_Automaton data
+ * structure.
+ *
+ * @param a automaton to be destroyed
+ */
+void
+REGEX_INTERNAL_automaton_destroy (struct REGEX_INTERNAL_Automaton *a)
+{
+  struct REGEX_INTERNAL_State *s;
+  struct REGEX_INTERNAL_State *next_state;
+
+  if (NULL == a)
+    return;
+
+  GNUNET_free_non_null (a->regex);
+  GNUNET_free_non_null (a->canonical_regex);
+
+  for (s = a->states_head; NULL != s; s = next_state)
+  {
+    next_state = s->next;
+    GNUNET_CONTAINER_DLL_remove (a->states_head, a->states_tail, s);
+    automaton_destroy_state (s);
+  }
+
+  GNUNET_free (a);
+}
+
+
+/**
+ * Evaluates the given string using the given DFA automaton
+ *
+ * @param a automaton, type must be DFA
+ * @param string string that should be evaluated
+ *
+ * @return 0 if string matches, non-0 otherwise
+ */
+static int
+evaluate_dfa (struct REGEX_INTERNAL_Automaton *a,
+              const char *string)
+{
+  const char *strp;
+  struct REGEX_INTERNAL_State *s;
+  unsigned int step_len;
+
+  if (DFA != a->type)
+  {
+    GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+                "Tried to evaluate DFA, but NFA automaton given");
+    return -1;
+  }
+
+  s = a->start;
+
+  /* If the string is empty but the starting state is accepting, we accept. */
+  if ((NULL == string || 0 == strlen (string)) && s->accepting)
+    return 0;
+
+  for (strp = string; NULL != strp && *strp; strp += step_len)
+  {
+    step_len = dfa_move (&s, strp);
+
+    if (NULL == s)
+      break;
+  }
+
+  if (NULL != s && s->accepting)
+    return 0;
+
+  return 1;
+}
+
+
+/**
+ * Evaluates the given string using the given NFA automaton
+ *
+ * @param a automaton, type must be NFA
+ * @param string string that should be evaluated
+ * @return 0 if string matches, non-0 otherwise
+ */
+static int
+evaluate_nfa (struct REGEX_INTERNAL_Automaton *a,
+              const char *string)
+{
+  const char *strp;
+  char str[2];
+  struct REGEX_INTERNAL_State *s;
+  struct REGEX_INTERNAL_StateSet sset;
+  struct REGEX_INTERNAL_StateSet new_sset;
+  struct REGEX_INTERNAL_StateSet singleton_set;
+  unsigned int i;
+  int result;
+
+  if (NFA != a->type)
+  {
+    GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+                "Tried to evaluate NFA, but DFA automaton given");
+    return -1;
+  }
+
+  /* If the string is empty but the starting state is accepting, we accept. */
+  if ((NULL == string || 0 == strlen (string)) && a->start->accepting)
+    return 0;
+
+  result = 1;
+  memset (&singleton_set, 0, sizeof (struct REGEX_INTERNAL_StateSet));
+  state_set_append (&singleton_set, a->start);
+  nfa_closure_set_create (&sset, a, &singleton_set, NULL);
+  state_set_clear (&singleton_set);
+
+  str[1] = '\0';
+  for (strp = string; NULL != strp && *strp; strp++)
+  {
+    str[0] = *strp;
+    nfa_closure_set_create (&new_sset, a, &sset, str);
+    state_set_clear (&sset);
+    nfa_closure_set_create (&sset, a, &new_sset, 0);
+    state_set_clear (&new_sset);
+  }
+
+  for (i = 0; i < sset.off; i++)
+  {
+    s = sset.states[i];
+    if ( (NULL != s) && (s->accepting) )
+    {
+      result = 0;
+      break;
+    }
+  }
+
+  state_set_clear (&sset);
+  return result;
+}
+
+
+/**
+ * Evaluates the given @a string against the given compiled regex @a a
+ *
+ * @param a automaton
+ * @param string string to check
+ * @return 0 if string matches, non-0 otherwise
+ */
+int
+REGEX_INTERNAL_eval (struct REGEX_INTERNAL_Automaton *a,
+                     const char *string)
+{
+  int result;
+
+  switch (a->type)
+  {
+  case DFA:
+    result = evaluate_dfa (a, string);
+    break;
+  case NFA:
+    result = evaluate_nfa (a, string);
+    break;
+  default:
+    GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+                "Evaluating regex failed, automaton has no type!\n");
+    result = GNUNET_SYSERR;
+    break;
+  }
+
+  return result;
+}
+
+
+/**
+ * Get the canonical regex of the given automaton.
+ * When constructing the automaton a proof is computed for each state,
+ * consisting of the regular expression leading to this state. A complete
+ * regex for the automaton can be computed by combining these proofs.
+ * As of now this function is only useful for testing.
+ *
+ * @param a automaton for which the canonical regex should be returned.
+ *
+ * @return
+ */
+const char *
+REGEX_INTERNAL_get_canonical_regex (struct REGEX_INTERNAL_Automaton *a)
+{
+  if (NULL == a)
+    return NULL;
+
+  return a->canonical_regex;
+}
+
+
+/**
+ * Get the number of transitions that are contained in the given automaton.
+ *
+ * @param a automaton for which the number of transitions should be returned.
+ *
+ * @return number of transitions in the given automaton.
+ */
+unsigned int
+REGEX_INTERNAL_get_transition_count (struct REGEX_INTERNAL_Automaton *a)
+{
+  unsigned int t_count;
+  struct REGEX_INTERNAL_State *s;
+
+  if (NULL == a)
+    return 0;
+
+  t_count = 0;
+  for (s = a->states_head; NULL != s; s = s->next)
+    t_count += s->transition_count;
+
+  return t_count;
+}
+
+
+/**
+ * Get the first key for the given @a input_string. This hashes the first x bits
+ * of the @a input_string.
+ *
+ * @param input_string string.
+ * @param string_len length of the @a input_string.
+ * @param key pointer to where to write the hash code.
+ * @return number of bits of @a input_string that have been consumed
+ *         to construct the key
+ */
+size_t
+REGEX_INTERNAL_get_first_key (const char *input_string,
+                              size_t string_len,
+                              struct GNUNET_HashCode *key)
+{
+  size_t size;
+
+  size = string_len < GNUNET_REGEX_INITIAL_BYTES ? string_len :
+                                                   GNUNET_REGEX_INITIAL_BYTES;
+  if (NULL == input_string)
+  {
+    GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+               "Given input string was NULL!\n");
+    return 0;
+  }
+  GNUNET_CRYPTO_hash (input_string, size, key);
+
+  return size;
+}
+
+
+/**
+ * Recursive function that calls the iterator for each synthetic start state.
+ *
+ * @param min_len minimum length of the path in the graph.
+ * @param max_len maximum length of the path in the graph.
+ * @param consumed_string string consumed by traversing the graph till this state.
+ * @param state current state of the automaton.
+ * @param iterator iterator function called for each edge.
+ * @param iterator_cls closure for the @a iterator function.
+ */
+static void
+iterate_initial_edge (unsigned int min_len,
+                      unsigned int max_len,
+                      char *consumed_string,
+                      struct REGEX_INTERNAL_State *state,
+                      REGEX_INTERNAL_KeyIterator iterator,
+                      void *iterator_cls)
+{
+  char *temp;
+  struct REGEX_INTERNAL_Transition *t;
+  unsigned int num_edges = state->transition_count;
+  struct REGEX_BLOCK_Edge edges[num_edges];
+  struct REGEX_BLOCK_Edge edge[1];
+  struct GNUNET_HashCode hash;
+  struct GNUNET_HashCode hash_new;
+  unsigned int cur_len;
+
+  if (NULL != consumed_string)
+    cur_len = strlen (consumed_string);
+  else
+    cur_len = 0;
+
+  if ( ( (cur_len >= min_len) ||
+         (GNUNET_YES == state->accepting) ) &&
+       (cur_len > 0) &&
+       (NULL != consumed_string) )
+  {
+    if (cur_len <= max_len)
+    {
+      if ( (NULL != state->proof) &&
+           (0 != strcmp (consumed_string,
+                         state->proof)) )
+      {
+        (void) state_get_edges (state, edges);
+        GNUNET_CRYPTO_hash (consumed_string,
+                            strlen (consumed_string),
+                            &hash);
+        GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+                    "Start state for string `%s' is %s\n",
+                    consumed_string,
+                    GNUNET_h2s (&hash));
+        iterator (iterator_cls,
+                  &hash,
+                  consumed_string,
+                  state->accepting,
+                  num_edges, edges);
+      }
+
+      if ( (GNUNET_YES == state->accepting) &&
+           (cur_len > 1) &&
+           (state->transition_count < 1) &&
+           (cur_len < max_len) )
+      {
+        /* Special case for regex consisting of just a string that is shorter than
+         * max_len */
+        edge[0].label = &consumed_string[cur_len - 1];
+        edge[0].destination = state->hash;
+        temp = GNUNET_strdup (consumed_string);
+        temp[cur_len - 1] = '\0';
+        GNUNET_CRYPTO_hash (temp,
+                            cur_len - 1,
+                            &hash_new);
+        GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+                    "Start state for short string `%s' is %s\n",
+                    temp,
+                    GNUNET_h2s (&hash_new));
+        iterator (iterator_cls,
+                  &hash_new,
+                  temp,
+                  GNUNET_NO, 1,
+                  edge);
+        GNUNET_free (temp);
+      }
+    }
+    else /* cur_len > max_len */
+    {
+      /* Case where the concatenated labels are longer than max_len, then split. */
+      edge[0].label = &consumed_string[max_len];
+      edge[0].destination = state->hash;
+      temp = GNUNET_strdup (consumed_string);
+      temp[max_len] = '\0';
+      GNUNET_CRYPTO_hash (temp, max_len, &hash);
+      GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+                  "Start state at split edge `%s'-`%s` is %s\n",
+                  temp,
+                  edge[0].label,
+                  GNUNET_h2s (&hash_new));
+      iterator (iterator_cls,
+                &hash,
+                temp,
+                GNUNET_NO,
+                1,
+                edge);
+      GNUNET_free (temp);
+    }
+  }
+
+  if (cur_len < max_len)
+  {
+    for (t = state->transitions_head; NULL != t; t = t->next)
+    {
+      if (NULL != strchr (t->label,
+                          (int) '.'))
+      {
+        /* Wildcards not allowed during starting states */
+        GNUNET_break (0);
+        continue;
+      }
+      if (NULL != consumed_string)
+        GNUNET_asprintf (&temp,
+                         "%s%s",
+                         consumed_string,
+                         t->label);
+      else
+        GNUNET_asprintf (&temp,
+                         "%s",
+                         t->label);
+      iterate_initial_edge (min_len,
+                            max_len,
+                            temp,
+                            t->to_state,
+                            iterator,
+                            iterator_cls);
+      GNUNET_free (temp);
+    }
+  }
+}
+
+
+/**
+ * Iterate over all edges starting from start state of automaton 'a'. Calling
+ * iterator for each edge.
+ *
+ * @param a automaton.
+ * @param iterator iterator called for each edge.
+ * @param iterator_cls closure.
+ */
+void
+REGEX_INTERNAL_iterate_all_edges (struct REGEX_INTERNAL_Automaton *a,
+                                  REGEX_INTERNAL_KeyIterator iterator,
+                                  void *iterator_cls)
+{
+  struct REGEX_INTERNAL_State *s;
+
+  GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+              "Iterating over starting edges\n");
+  iterate_initial_edge (GNUNET_REGEX_INITIAL_BYTES,
+                        GNUNET_REGEX_INITIAL_BYTES,
+                        NULL, a->start,
+                        iterator, iterator_cls);
+  GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+              "Iterating over DFA edges\n");
+  for (s = a->states_head; NULL != s; s = s->next)
+  {
+    struct REGEX_BLOCK_Edge edges[s->transition_count];
+    unsigned int num_edges;
+
+    num_edges = state_get_edges (s, edges);
+    if ( ( (NULL != s->proof) &&
+           (0 < strlen (s->proof)) ) || s->accepting)
+    {
+      GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+                  "Creating DFA edges at `%s' under key %s\n",
+                  s->proof,
+                  GNUNET_h2s (&s->hash));
+      iterator (iterator_cls, &s->hash, s->proof,
+                s->accepting,
+                num_edges, edges);
+    }
+    s->marked = GNUNET_NO;
+  }
+}
+
+
+/**
+ * Struct to hold all the relevant state information in the HashMap.
+ *
+ * Contains the same info as the Regex Iterator parametes except the key,
+ * which comes directly from the HashMap iterator.
+ */
+struct temporal_state_store {
+  int reachable;
+  char *proof;
+  int accepting;
+  int num_edges;
+  struct REGEX_BLOCK_Edge *edges;
+};
+
+
+/**
+ * Store regex iterator and cls in one place to pass to the hashmap iterator.
+ */
+struct client_iterator {
+  REGEX_INTERNAL_KeyIterator iterator;
+  void *iterator_cls;
+};
+
+
+/**
+ * Iterator over all edges of a dfa. Stores all of them in a HashMap
+ * for later reachability marking.
+ *
+ * @param cls Closure (HashMap)
+ * @param key hash for current state.
+ * @param proof proof for current state
+ * @param accepting GNUNET_YES if this is an accepting state, GNUNET_NO if not.
+ * @param num_edges number of edges leaving current state.
+ * @param edges edges leaving current state.
+ */
+static void
+store_all_states (void *cls,
+                  const struct GNUNET_HashCode *key,
+                  const char *proof,
+                  int accepting,
+                  unsigned int num_edges,
+                  const struct REGEX_BLOCK_Edge *edges)
+{
+  struct GNUNET_CONTAINER_MultiHashMap *hm = cls;
+  struct temporal_state_store *tmp;
+  size_t edges_size;
+
+  tmp = GNUNET_new (struct temporal_state_store);
+  tmp->reachable = GNUNET_NO;
+  tmp->proof = GNUNET_strdup (proof);
+  tmp->accepting = accepting;
+  tmp->num_edges = num_edges;
+  edges_size = sizeof (struct REGEX_BLOCK_Edge) * num_edges;
+  tmp->edges = GNUNET_malloc (edges_size);
+  GNUNET_memcpy(tmp->edges, edges, edges_size);
+  GNUNET_CONTAINER_multihashmap_put (hm, key, tmp,
+                                     GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_FAST);
+}
+
+
+/**
+ * Mark state as reachable and call recursively on all its edges.
+ *
+ * If already marked as reachable, do nothing.
+ *
+ * @param state State to mark as reachable.
+ * @param hm HashMap which stores all the states indexed by key.
+ */
+static void
+mark_as_reachable (struct temporal_state_store *state,
+                   struct GNUNET_CONTAINER_MultiHashMap *hm)
+{
+  struct temporal_state_store *child;
+  unsigned int i;
+
+  if (GNUNET_YES == state->reachable)
+    /* visited */
+    return;
+
+  state->reachable = GNUNET_YES;
+  for (i = 0; i < state->num_edges; i++)
+  {
+    child = GNUNET_CONTAINER_multihashmap_get (hm,
+                                               &state->edges[i].destination);
+    if (NULL == child)
+    {
+      GNUNET_break (0);
+      continue;
+    }
+    mark_as_reachable (child, hm);
+  }
+}
+
+
+/**
+ * Iterator over hash map entries to mark the ones that are reachable.
+ *
+ * @param cls closure
+ * @param key current key code
+ * @param value value in the hash map
+ * @return #GNUNET_YES if we should continue to iterate,
+ *         #GNUNET_NO if not.
+ */
+static int
+reachability_iterator (void *cls,
+                       const struct GNUNET_HashCode *key,
+                       void *value)
+{
+  struct GNUNET_CONTAINER_MultiHashMap *hm = cls;
+  struct temporal_state_store *state = value;
+
+  if (GNUNET_YES == state->reachable)
+    /* already visited and marked */
+    return GNUNET_YES;
+
+  if (GNUNET_REGEX_INITIAL_BYTES > strlen (state->proof) &&
+      GNUNET_NO == state->accepting)
+    /* not directly reachable */
+    return GNUNET_YES;
+
+  mark_as_reachable (state, hm);
+  return GNUNET_YES;
+}
+
+
+/**
+ * Iterator over hash map entries.
+ * Calling the callback on the ones marked as reachables.
+ *
+ * @param cls closure
+ * @param key current key code
+ * @param value value in the hash map
+ * @return #GNUNET_YES if we should continue to iterate,
+ *         #GNUNET_NO if not.
+ */
+static int
+iterate_reachables (void *cls,
+                    const struct GNUNET_HashCode *key,
+                    void *value)
+{
+  struct client_iterator *ci = cls;
+  struct temporal_state_store *state = value;
+
+  if (GNUNET_YES == state->reachable)
+  {
+    ci->iterator (ci->iterator_cls, key,
+                  state->proof, state->accepting,
+                  state->num_edges, state->edges);
+  }
+  GNUNET_free (state->edges);
+  GNUNET_free (state->proof);
+  GNUNET_free (state);
+  return GNUNET_YES;
+
+}
+
+/**
+ * Iterate over all edges of automaton 'a' that are reachable from a state with
+ * a proof of at least GNUNET_REGEX_INITIAL_BYTES characters.
+ *
+ * Call the iterator for each such edge.
+ *
+ * @param a automaton.
+ * @param iterator iterator called for each reachable edge.
+ * @param iterator_cls closure.
+ */
+void
+REGEX_INTERNAL_iterate_reachable_edges (struct REGEX_INTERNAL_Automaton *a,
+                                        REGEX_INTERNAL_KeyIterator iterator,
+                                        void *iterator_cls)
+{
+  struct GNUNET_CONTAINER_MultiHashMap *hm;
+  struct client_iterator ci;
+
+  hm = GNUNET_CONTAINER_multihashmap_create (a->state_count * 2, GNUNET_NO);
+  ci.iterator = iterator;
+  ci.iterator_cls = iterator_cls;
+
+  REGEX_INTERNAL_iterate_all_edges (a, &store_all_states, hm);
+  GNUNET_CONTAINER_multihashmap_iterate (hm, &reachability_iterator, hm);
+  GNUNET_CONTAINER_multihashmap_iterate (hm, &iterate_reachables, &ci);
+
+  GNUNET_CONTAINER_multihashmap_destroy (hm);
+}
+
+
+/* end of regex_internal.c */
diff --git a/src/regex/regex_internal.h b/src/regex/regex_internal.h
new file mode 100644 (file)
index 0000000..d52479f
--- /dev/null
@@ -0,0 +1,453 @@
+/*
+     This file is part of GNUnet
+     Copyright (C) 2012 GNUnet e.V.
+
+     GNUnet is free software: you can redistribute it and/or modify it
+     under the terms of the GNU Affero General Public License as published
+     by the Free Software Foundation, either version 3 of the License,
+     or (at your option) any later version.
+
+     GNUnet is distributed in the hope that it will be useful, but
+     WITHOUT ANY WARRANTY; without even the implied warranty of
+     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+     Affero General Public License for more details.
+    
+     You should have received a copy of the GNU Affero General Public License
+     along with this program.  If not, see <http://www.gnu.org/licenses/>.
+*/
+/**
+ * @file src/regex/regex_internal.h
+ * @brief common internal definitions for regex library.
+ * @author Maximilian Szengel
+ */
+#ifndef REGEX_INTERNAL_H
+#define REGEX_INTERNAL_H
+
+#include "regex_internal_lib.h"
+
+#ifdef __cplusplus
+extern "C"
+{
+#if 0                           /* keep Emacsens' auto-indent happy */
+}
+#endif
+#endif
+
+/**
+ * char array of literals that are allowed inside a regex (apart from the
+ * operators)
+ */
+#define ALLOWED_LITERALS "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"
+
+
+/**
+ * Transition between two states. Transitions are stored at the states from
+ * which they origin ('from_state'). Each state can have 0-n transitions.
+ * If label is NULL, this is considered to be an epsilon transition.
+ */
+struct REGEX_INTERNAL_Transition
+{
+  /**
+   * This is a linked list.
+   */
+  struct REGEX_INTERNAL_Transition *prev;
+
+  /**
+   * This is a linked list.
+   */
+  struct REGEX_INTERNAL_Transition *next;
+
+  /**
+   * Unique id of this transition.
+   */
+  unsigned int id;
+
+  /**
+   * Label for this transition. This is basically the edge label for the graph.
+   */
+  char *label;
+
+  /**
+   * State to which this transition leads.
+   */
+  struct REGEX_INTERNAL_State *to_state;
+
+  /**
+   * State from which this transition origins.
+   */
+  struct REGEX_INTERNAL_State *from_state;
+};
+
+
+/**
+ * A state. Can be used in DFA and NFA automatons.
+ */
+struct REGEX_INTERNAL_State;
+
+
+/**
+ * Set of states.
+ */
+struct REGEX_INTERNAL_StateSet
+{
+  /**
+   * Array of states.
+   */
+  struct REGEX_INTERNAL_State **states;
+
+  /**
+   * Number of entries in *use* in the 'states' array.
+   */
+  unsigned int off;
+
+  /**
+   * Length of the 'states' array.
+   */
+  unsigned int size;
+};
+
+
+/**
+ * A state. Can be used in DFA and NFA automatons.
+ */
+struct REGEX_INTERNAL_State
+{
+  /**
+   * This is a linked list to keep states in an automaton.
+   */
+  struct REGEX_INTERNAL_State *prev;
+
+  /**
+   * This is a linked list to keep states in an automaton.
+   */
+  struct REGEX_INTERNAL_State *next;
+
+  /**
+   * This is a multi DLL for StateSet_MDLL.
+   */
+  struct REGEX_INTERNAL_State *prev_SS;
+
+  /**
+   * This is a multi DLL for StateSet_MDLL.
+   */
+  struct REGEX_INTERNAL_State *next_SS;
+
+  /**
+   * This is a multi DLL for StateSet_MDLL Stack.
+   */
+  struct REGEX_INTERNAL_State *prev_ST;
+
+  /**
+   * This is a multi DLL for StateSet_MDLL Stack.
+   */
+  struct REGEX_INTERNAL_State *next_ST;
+
+  /**
+   * Unique state id.
+   */
+  unsigned int id;
+
+  /**
+   * Unique state id that is used for traversing the automaton. It is guaranteed
+   * to be > 0 and < state_count.
+   */
+  unsigned int traversal_id;
+
+  /**
+   * If this is an accepting state or not.
+   */
+  int accepting;
+
+  /**
+   * Marking of the state. This is used for marking all visited states when
+   * traversing all states of an automaton and for cases where the state id
+   * cannot be used (dfa minimization).
+   */
+  int marked;
+
+  /**
+   * Marking the state as contained. This is used for checking, if the state is
+   * contained in a set in constant time.
+   */
+  int contained;
+
+  /**
+   * Marking the state as part of an SCC (Strongly Connected Component).  All
+   * states with the same scc_id are part of the same SCC. scc_id is 0, if state
+   * is not a part of any SCC.
+   */
+  unsigned int scc_id;
+
+  /**
+   * Used for SCC detection.
+   */
+  int index;
+
+  /**
+   * Used for SCC detection.
+   */
+  int lowlink;
+
+  /**
+   * Human readable name of the state. Used for debugging and graph
+   * creation.
+   */
+  char *name;
+
+  /**
+   * Hash of the state.
+   */
+  struct GNUNET_HashCode hash;
+
+  /**
+   * Linear state ID accquired by depth-first-search. This ID should be used for
+   * storing information about the state in an array, because the 'id' of the
+   * state is not guaranteed to be linear. The 'dfs_id' is guaranteed to be > 0
+   * and < 'state_count'.
+   */
+  unsigned int dfs_id;
+
+  /**
+   * Proof for this state.
+   */
+  char *proof;
+
+  /**
+   * Number of transitions from this state to other states.
+   */
+  unsigned int transition_count;
+
+  /**
+   * DLL of transitions.
+   */
+  struct REGEX_INTERNAL_Transition *transitions_head;
+
+  /**
+   * DLL of transitions.
+   */
+  struct REGEX_INTERNAL_Transition *transitions_tail;
+
+  /**
+   * Number of incoming transitions. Used for compressing DFA paths.
+   */
+  unsigned int incoming_transition_count;
+
+  /**
+   * Set of states on which this state is based on. Used when creating a DFA out
+   * of several NFA states.
+   */
+  struct REGEX_INTERNAL_StateSet nfa_set;
+};
+
+
+/**
+ * Type of an automaton.
+ */
+enum REGEX_INTERNAL_AutomatonType
+{
+  NFA,
+  DFA
+};
+
+
+/**
+ * Automaton representation.
+ */
+struct REGEX_INTERNAL_Automaton
+{
+  /**
+   * Linked list of NFAs used for partial NFA creation.
+   */
+  struct REGEX_INTERNAL_Automaton *prev;
+
+  /**
+   * Linked list of NFAs used for partial NFA creation.
+   */
+  struct REGEX_INTERNAL_Automaton *next;
+
+  /**
+   * First state of the automaton. This is mainly used for constructing an NFA,
+   * where each NFA itself consists of one or more NFAs linked together.
+   */
+  struct REGEX_INTERNAL_State *start;
+
+  /**
+   * End state of the partial NFA. This is undefined for DFAs
+   */
+  struct REGEX_INTERNAL_State *end;
+
+  /**
+   * Number of states in the automaton.
+   */
+  unsigned int state_count;
+
+  /**
+   * DLL of states.
+   */
+  struct REGEX_INTERNAL_State *states_head;
+
+  /**
+   * DLL of states
+   */
+  struct REGEX_INTERNAL_State *states_tail;
+
+  /**
+   * Type of the automaton.
+   */
+  enum REGEX_INTERNAL_AutomatonType type;
+
+  /**
+   * Regex
+   */
+  char *regex;
+
+  /**
+   * Canonical regex (result of RX->NFA->DFA->RX)
+   */
+  char *canonical_regex;
+
+  /**
+   * GNUNET_YES, if multi strides have been added to the Automaton.
+   */
+  int is_multistrided;
+};
+
+
+/**
+ * Construct an NFA by parsing the regex string of length 'len'.
+ *
+ * @param regex regular expression string.
+ * @param len length of the string.
+ *
+ * @return NFA, needs to be freed using REGEX_INTERNAL_automaton_destroy.
+ */
+struct REGEX_INTERNAL_Automaton *
+REGEX_INTERNAL_construct_nfa (const char *regex, const size_t len);
+
+
+/**
+ * Function that get's passed to automaton traversal and is called before each
+ * next traversal from state 's' using transition 't' to check if traversal
+ * should proceed. Return GNUNET_NO to stop traversal or GNUNET_YES to continue.
+ *
+ * @param cls closure for the check.
+ * @param s current state in the traversal.
+ * @param t current transition from state 's' that will be used for the next
+ *          step.
+ *
+ * @return GNUNET_YES to proceed traversal, GNUNET_NO to stop.
+ */
+typedef int (*REGEX_INTERNAL_traverse_check) (void *cls,
+                                            struct REGEX_INTERNAL_State * s,
+                                            struct REGEX_INTERNAL_Transition * t);
+
+
+/**
+ * Function that is called with each state, when traversing an automaton.
+ *
+ * @param cls closure.
+ * @param count current count of the state, from 0 to a->state_count -1.
+ * @param s state.
+ */
+typedef void (*REGEX_INTERNAL_traverse_action) (void *cls,
+                                              const unsigned int count,
+                                              struct REGEX_INTERNAL_State * s);
+
+
+/**
+ * Traverses the given automaton using depth-first-search (DFS) from it's start
+ * state, visiting all reachable states and calling 'action' on each one of
+ * them.
+ *
+ * @param a automaton to be traversed.
+ * @param start start state, pass a->start or NULL to traverse the whole automaton.
+ * @param check function that is checked before advancing on each transition
+ *              in the DFS.
+ * @param check_cls closure for check.
+ * @param action action to be performed on each state.
+ * @param action_cls closure for action
+ */
+void
+REGEX_INTERNAL_automaton_traverse (const struct REGEX_INTERNAL_Automaton *a,
+                                 struct REGEX_INTERNAL_State *start,
+                                 REGEX_INTERNAL_traverse_check check,
+                                 void *check_cls,
+                                 REGEX_INTERNAL_traverse_action action,
+                                 void *action_cls);
+
+/**
+ * Get the canonical regex of the given automaton.
+ * When constructing the automaton a proof is computed for each state,
+ * consisting of the regular expression leading to this state. A complete
+ * regex for the automaton can be computed by combining these proofs.
+ * As of now this function is only useful for testing.
+ *
+ * @param a automaton for which the canonical regex should be returned.
+ *
+ * @return canonical regex string.
+ */
+const char *
+REGEX_INTERNAL_get_canonical_regex (struct REGEX_INTERNAL_Automaton *a);
+
+
+/**
+ * Get the number of transitions that are contained in the given automaton.
+ *
+ * @param a automaton for which the number of transitions should be returned.
+ *
+ * @return number of transitions in the given automaton.
+ */
+unsigned int
+REGEX_INTERNAL_get_transition_count (struct REGEX_INTERNAL_Automaton *a);
+
+
+/**
+ * Context that contains an id counter for states and transitions as well as a
+ * DLL of automatons used as a stack for NFA construction.
+ */
+struct REGEX_INTERNAL_Context
+{
+  /**
+   * Unique state id.
+   */
+  unsigned int state_id;
+
+  /**
+   * Unique transition id.
+   */
+  unsigned int transition_id;
+
+  /**
+   * DLL of REGEX_INTERNAL_Automaton's used as a stack.
+   */
+  struct REGEX_INTERNAL_Automaton *stack_head;
+
+  /**
+   * DLL of REGEX_INTERNAL_Automaton's used as a stack.
+   */
+  struct REGEX_INTERNAL_Automaton *stack_tail;
+};
+
+
+/**
+ * Adds multi-strided transitions to the given 'dfa'.
+ *
+ * @param regex_ctx regex context needed to add transitions to the automaton.
+ * @param dfa DFA to which the multi strided transitions should be added.
+ * @param stride_len length of the strides.
+ */
+void
+REGEX_INTERNAL_dfa_add_multi_strides (struct REGEX_INTERNAL_Context *regex_ctx,
+                                    struct REGEX_INTERNAL_Automaton *dfa,
+                                    const unsigned int stride_len);
+
+
+
+#if 0                           /* keep Emacsens' auto-indent happy */
+{
+#endif
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/src/regex/regex_internal_dht.c b/src/regex/regex_internal_dht.c
new file mode 100644 (file)
index 0000000..2555ef1
--- /dev/null
@@ -0,0 +1,829 @@
+/*
+     This file is part of GNUnet
+     Copyright (C) 2012, 2015 GNUnet e.V.
+
+     GNUnet is free software: you can redistribute it and/or modify it
+     under the terms of the GNU Affero General Public License as published
+     by the Free Software Foundation, either version 3 of the License,
+     or (at your option) any later version.
+
+     GNUnet is distributed in the hope that it will be useful, but
+     WITHOUT ANY WARRANTY; without even the implied warranty of
+     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+     Affero General Public License for more details.
+    
+     You should have received a copy of the GNU Affero General Public License
+     along with this program.  If not, see <http://www.gnu.org/licenses/>.
+*/
+/**
+ * @file src/regex/regex_internal_dht.c
+ * @brief library to announce regexes in the network and match strings
+ * against published regexes.
+ * @author Bartlomiej Polot
+ */
+#include "platform.h"
+#include "regex_internal_lib.h"
+#include "regex_block_lib.h"
+#include "gnunet_dht_service.h"
+#include "gnunet_statistics_service.h"
+#include "gnunet_constants.h"
+#include "gnunet_signatures.h"
+
+
+#define LOG(kind,...) GNUNET_log_from (kind,"regex-dht",__VA_ARGS__)
+
+/**
+ * DHT replication level to use.
+ */
+#define DHT_REPLICATION 5
+
+/**
+ * DHT record lifetime to use.
+ */
+#define DHT_TTL         GNUNET_TIME_UNIT_HOURS
+
+/**
+ * DHT options to set.
+ */
+#define DHT_OPT         GNUNET_DHT_RO_DEMULTIPLEX_EVERYWHERE
+
+
+/**
+ * Handle to store cached data about a regex announce.
+ */
+struct REGEX_INTERNAL_Announcement
+{
+  /**
+   * DHT handle to use, must be initialized externally.
+   */
+  struct GNUNET_DHT_Handle *dht;
+
+  /**
+   * Regular expression.
+   */
+  const char *regex;
+
+  /**
+   * Automaton representation of the regex (expensive to build).
+   */
+  struct REGEX_INTERNAL_Automaton *dfa;
+
+  /**
+   * Our private key.
+   */
+  const struct GNUNET_CRYPTO_EddsaPrivateKey *priv;
+
+  /**
+   * Optional statistics handle to report usage. Can be NULL.
+   */
+  struct GNUNET_STATISTICS_Handle *stats;
+};
+
+
+/**
+ * Regex callback iterator to store own service description in the DHT.
+ *
+ * @param cls closure.
+ * @param key hash for current state.
+ * @param proof proof for current state.
+ * @param accepting #GNUNET_YES if this is an accepting state, #GNUNET_NO if not.
+ * @param num_edges number of edges leaving current state.
+ * @param edges edges leaving current state.
+ */
+static void
+regex_iterator (void *cls,
+                const struct GNUNET_HashCode *key,
+                const char *proof,
+                int accepting,
+                unsigned int num_edges,
+                const struct REGEX_BLOCK_Edge *edges)
+{
+  struct REGEX_INTERNAL_Announcement *h = cls;
+  struct RegexBlock *block;
+  size_t size;
+  unsigned int i;
+
+  LOG (GNUNET_ERROR_TYPE_INFO,
+       "DHT PUT for state %s with proof `%s' and %u edges:\n",
+       GNUNET_h2s (key),
+       proof,
+       num_edges);
+  for (i = 0; i < num_edges; i++)
+  {
+    LOG (GNUNET_ERROR_TYPE_INFO,
+         "Edge %u `%s' towards %s\n",
+         i,
+         edges[i].label,
+         GNUNET_h2s (&edges[i].destination));
+  }
+  if (GNUNET_YES == accepting)
+  {
+    struct RegexAcceptBlock ab;
+
+    LOG (GNUNET_ERROR_TYPE_INFO,
+         "State %s is accepting, putting own id\n",
+         GNUNET_h2s (key));
+    size = sizeof (struct RegexAcceptBlock);
+    ab.purpose.size = ntohl (sizeof (struct GNUNET_CRYPTO_EccSignaturePurpose) +
+                             sizeof (struct GNUNET_TIME_AbsoluteNBO) +
+                             sizeof (struct GNUNET_HashCode));
+    ab.purpose.purpose = ntohl (GNUNET_SIGNATURE_PURPOSE_REGEX_ACCEPT);
+    ab.expiration_time = GNUNET_TIME_absolute_hton (GNUNET_TIME_relative_to_absolute (GNUNET_CONSTANTS_DHT_MAX_EXPIRATION));
+    ab.key = *key;
+    GNUNET_CRYPTO_eddsa_key_get_public (h->priv,
+                                        &ab.peer.public_key);
+    GNUNET_assert (GNUNET_OK ==
+                   GNUNET_CRYPTO_eddsa_sign (h->priv,
+                                           &ab.purpose,
+                                           &ab.signature));
+
+    GNUNET_STATISTICS_update (h->stats, "# regex accepting blocks stored",
+                              1, GNUNET_NO);
+    GNUNET_STATISTICS_update (h->stats, "# regex accepting block bytes stored",
+                              sizeof (struct RegexAcceptBlock), GNUNET_NO);
+    (void)
+    GNUNET_DHT_put (h->dht, key,
+                    DHT_REPLICATION,
+                    DHT_OPT | GNUNET_DHT_RO_RECORD_ROUTE,
+                    GNUNET_BLOCK_TYPE_REGEX_ACCEPT,
+                    size,
+                    &ab,
+                    GNUNET_TIME_relative_to_absolute (DHT_TTL),
+                    NULL, NULL);
+  }
+  block = REGEX_BLOCK_create (proof,
+                              num_edges,
+                             edges,
+                              accepting,
+                              &size);
+  if (NULL == block)
+    return;
+  (void) GNUNET_DHT_put (h->dht,
+                        key,
+                        DHT_REPLICATION,
+                        DHT_OPT,
+                        GNUNET_BLOCK_TYPE_REGEX,
+                        size,
+                        block,
+                        GNUNET_TIME_relative_to_absolute (DHT_TTL),
+                        NULL,
+                        NULL);
+  GNUNET_STATISTICS_update (h->stats,
+                            "# regex blocks stored",
+                            1,
+                           GNUNET_NO);
+  GNUNET_STATISTICS_update (h->stats,
+                            "# regex block bytes stored",
+                            size,
+                           GNUNET_NO);
+  GNUNET_free (block);
+}
+
+
+/**
+ * Announce a regular expression: put all states of the automaton in the DHT.
+ * Does not free resources, must call #REGEX_INTERNAL_announce_cancel() for that.
+ *
+ * @param dht An existing and valid DHT service handle. CANNOT be NULL.
+ * @param priv our private key, must remain valid until the announcement is cancelled
+ * @param regex Regular expression to announce.
+ * @param compression How many characters per edge can we squeeze?
+ * @param stats Optional statistics handle to report usage. Can be NULL.
+ * @return Handle to reuse o free cached resources.
+ *         Must be freed by calling #REGEX_INTERNAL_announce_cancel().
+ */
+struct REGEX_INTERNAL_Announcement *
+REGEX_INTERNAL_announce (struct GNUNET_DHT_Handle *dht,
+                        const struct GNUNET_CRYPTO_EddsaPrivateKey *priv,
+                        const char *regex,
+                        uint16_t compression,
+                        struct GNUNET_STATISTICS_Handle *stats)
+{
+  struct REGEX_INTERNAL_Announcement *h;
+
+  GNUNET_assert (NULL != dht);
+  h = GNUNET_new (struct REGEX_INTERNAL_Announcement);
+  h->regex = regex;
+  h->dht = dht;
+  h->stats = stats;
+  h->priv = priv;
+  h->dfa = REGEX_INTERNAL_construct_dfa (regex, strlen (regex), compression);
+  REGEX_INTERNAL_reannounce (h);
+  return h;
+}
+
+
+/**
+ * Announce again a regular expression previously announced.
+ * Does use caching to speed up process.
+ *
+ * @param h Handle returned by a previous #REGEX_INTERNAL_announce call().
+ */
+void
+REGEX_INTERNAL_reannounce (struct REGEX_INTERNAL_Announcement *h)
+{
+  GNUNET_assert (NULL != h->dfa); /* make sure to call announce first */
+  LOG (GNUNET_ERROR_TYPE_INFO,
+       "REGEX_INTERNAL_reannounce: %s\n",
+       h->regex);
+  REGEX_INTERNAL_iterate_reachable_edges (h->dfa,
+                                          &regex_iterator,
+                                          h);
+}
+
+
+/**
+ * Clear all cached data used by a regex announce.
+ * Does not close DHT connection.
+ *
+ * @param h Handle returned by a previous #REGEX_INTERNAL_announce() call.
+ */
+void
+REGEX_INTERNAL_announce_cancel (struct REGEX_INTERNAL_Announcement *h)
+{
+  REGEX_INTERNAL_automaton_destroy (h->dfa);
+  GNUNET_free (h);
+}
+
+
+/******************************************************************************/
+
+
+/**
+ * Struct to keep state of running searches that have consumed a part of
+ * the inital string.
+ */
+struct RegexSearchContext
+{
+  /**
+   * Part of the description already consumed by
+   * this particular search branch.
+   */
+  size_t position;
+
+  /**
+   * Information about the search.
+   */
+  struct REGEX_INTERNAL_Search *info;
+
+  /**
+   * We just want to look for one edge, the longer the better.
+   * Keep its length.
+   */
+  unsigned int longest_match;
+
+  /**
+   * Destination hash of the longest match.
+   */
+  struct GNUNET_HashCode hash;
+};
+
+
+/**
+ * Type of values in `dht_get_results`.
+ */
+struct Result
+{
+  /**
+   * Number of bytes in data.
+   */
+  size_t size;
+
+  /**
+   * The raw result data.
+   */
+  const void *data;
+};
+
+
+/**
+ * Struct to keep information of searches of services described by a regex
+ * using a user-provided string service description.
+ */
+struct REGEX_INTERNAL_Search
+{
+  /**
+   * DHT handle to use, must be initialized externally.
+   */
+  struct GNUNET_DHT_Handle *dht;
+
+  /**
+   * Optional statistics handle to report usage. Can be NULL.
+   */
+  struct GNUNET_STATISTICS_Handle *stats;
+
+  /**
+   * User provided description of the searched service.
+   */
+  char *description;
+
+  /**
+   * Running DHT GETs.
+   */
+  struct GNUNET_CONTAINER_MultiHashMap *dht_get_handles;
+
+  /**
+   * Results from running DHT GETs, values are of type
+   * 'struct Result'.
+   */
+  struct GNUNET_CONTAINER_MultiHashMap *dht_get_results;
+
+  /**
+   * Contexts, for each running DHT GET. Free all on end of search.
+   */
+  struct RegexSearchContext **contexts;
+
+  /**
+   * Number of contexts (branches/steps in search).
+   */
+  unsigned int n_contexts;
+
+  /**
+   * @param callback Callback for found peers.
+   */
+  REGEX_INTERNAL_Found callback;
+
+  /**
+   * @param callback_cls Closure for @c callback.
+   */
+  void *callback_cls;
+};
+
+
+/**
+ * Jump to the next edge, with the longest matching token.
+ *
+ * @param block Block found in the DHT.
+ * @param size Size of the block.
+ * @param ctx Context of the search.
+ */
+static void
+regex_next_edge (const struct RegexBlock *block,
+                 size_t size,
+                 struct RegexSearchContext *ctx);
+
+
+/**
+ * Function to process DHT string to regex matching.
+ * Called on each result obtained for the DHT search.
+ *
+ * @param cls Closure (search context).
+ * @param exp When will this value expire.
+ * @param key Key of the result.
+ * @param get_path Path of the get request.
+ * @param get_path_length Lenght of get_path.
+ * @param put_path Path of the put request.
+ * @param put_path_length Length of the put_path.
+ * @param type Type of the result.
+ * @param size Number of bytes in data.
+ * @param data Pointer to the result data.
+ */
+static void
+dht_get_string_accept_handler (void *cls, struct GNUNET_TIME_Absolute exp,
+                               const struct GNUNET_HashCode *key,
+                               const struct GNUNET_PeerIdentity *get_path,
+                               unsigned int get_path_length,
+                               const struct GNUNET_PeerIdentity *put_path,
+                               unsigned int put_path_length,
+                               enum GNUNET_BLOCK_Type type,
+                               size_t size, const void *data)
+{
+  const struct RegexAcceptBlock *block = data;
+  struct RegexSearchContext *ctx = cls;
+  struct REGEX_INTERNAL_Search *info = ctx->info;
+
+  LOG (GNUNET_ERROR_TYPE_DEBUG,
+       "Regex result accept for %s (key %s)\n",
+       info->description, GNUNET_h2s(key));
+
+  GNUNET_STATISTICS_update (info->stats,
+                           "# regex accepting blocks found",
+                            1, GNUNET_NO);
+  GNUNET_STATISTICS_update (info->stats,
+                           "# regex accepting block bytes found",
+                            size, GNUNET_NO);
+  info->callback (info->callback_cls,
+                  &block->peer,
+                  get_path, get_path_length,
+                  put_path, put_path_length);
+}
+
+
+/**
+ * Find a path to a peer that offers a regex service compatible
+ * with a given string.
+ *
+ * @param key The key of the accepting state.
+ * @param ctx Context containing info about the string, tunnel, etc.
+ */
+static void
+regex_find_path (const struct GNUNET_HashCode *key,
+                 struct RegexSearchContext *ctx)
+{
+  struct GNUNET_DHT_GetHandle *get_h;
+
+  LOG (GNUNET_ERROR_TYPE_DEBUG,
+       "Accept state found, now searching for paths to %s\n",
+       GNUNET_h2s (key),
+       (unsigned int) ctx->position);
+  get_h = GNUNET_DHT_get_start (ctx->info->dht,    /* handle */
+                                GNUNET_BLOCK_TYPE_REGEX_ACCEPT, /* type */
+                                key,     /* key to search */
+                                DHT_REPLICATION, /* replication level */
+                                DHT_OPT | GNUNET_DHT_RO_RECORD_ROUTE,
+                                NULL,       /* xquery */ // FIXME BLOOMFILTER
+                                0,     /* xquery bits */ // FIXME BLOOMFILTER SIZE
+                                &dht_get_string_accept_handler, ctx);
+  GNUNET_break (GNUNET_OK ==
+                GNUNET_CONTAINER_multihashmap_put(ctx->info->dht_get_handles,
+                                                  key,
+                                                  get_h,
+                                                  GNUNET_CONTAINER_MULTIHASHMAPOPTION_MULTIPLE));
+}
+
+
+/**
+ * Function to process DHT string to regex matching.
+ * Called on each result obtained for the DHT search.
+ *
+ * @param cls closure (search context)
+ * @param exp when will this value expire
+ * @param key key of the result
+ * @param get_path path of the get request (not used)
+ * @param get_path_length length of @a get_path (not used)
+ * @param put_path path of the put request (not used)
+ * @param put_path_length length of the @a put_path (not used)
+ * @param type type of the result
+ * @param size number of bytes in data
+ * @param data pointer to the result data
+ *
+ * TODO: re-issue the request after certain time? cancel after X results?
+ */
+static void
+dht_get_string_handler (void *cls, struct GNUNET_TIME_Absolute exp,
+                        const struct GNUNET_HashCode *key,
+                        const struct GNUNET_PeerIdentity *get_path,
+                        unsigned int get_path_length,
+                        const struct GNUNET_PeerIdentity *put_path,
+                        unsigned int put_path_length,
+                        enum GNUNET_BLOCK_Type type,
+                        size_t size, const void *data)
+{
+  const struct RegexBlock *block = data;
+  struct RegexSearchContext *ctx = cls;
+  struct REGEX_INTERNAL_Search *info = ctx->info;
+  size_t len;
+  struct Result *copy;
+
+  LOG (GNUNET_ERROR_TYPE_INFO,
+       "DHT GET result for %s (%s)\n",
+       GNUNET_h2s (key), ctx->info->description);
+  copy = GNUNET_malloc (sizeof (struct Result) + size);
+  copy->size = size;
+  copy->data = &copy[1];
+  GNUNET_memcpy (&copy[1], block, size);
+  GNUNET_break (GNUNET_OK ==
+               GNUNET_CONTAINER_multihashmap_put (info->dht_get_results,
+                                                  key, copy,
+                                                  GNUNET_CONTAINER_MULTIHASHMAPOPTION_MULTIPLE));
+  len = strlen (info->description);
+  if (len == ctx->position) // String processed
+  {
+    if (GNUNET_YES == GNUNET_BLOCK_is_accepting (block, size))
+    {
+      regex_find_path (key, ctx);
+    }
+    else
+    {
+      LOG (GNUNET_ERROR_TYPE_INFO, "block not accepting!\n");
+      /* FIXME REGEX this block not successful, wait for more? start timeout? */
+    }
+    return;
+  }
+  regex_next_edge (block, size, ctx);
+}
+
+
+/**
+ * Iterator over found existing cadet regex blocks that match an ongoing search.
+ *
+ * @param cls Closure (current context)-
+ * @param key Current key code (key for cached block).
+ * @param value Value in the hash map (cached RegexBlock).
+ * @return #GNUNET_YES: we should always continue to iterate.
+ */
+static int
+regex_result_iterator (void *cls,
+                       const struct GNUNET_HashCode * key,
+                       void *value)
+{
+  struct Result *result = value;
+  const struct RegexBlock *block = result->data;
+  struct RegexSearchContext *ctx = cls;
+
+  if ( (GNUNET_YES ==
+       GNUNET_BLOCK_is_accepting (block, result->size)) &&
+       (ctx->position == strlen (ctx->info->description)) )
+  {
+    LOG (GNUNET_ERROR_TYPE_INFO,
+        "Found accepting known block\n");
+    regex_find_path (key, ctx);
+    return GNUNET_YES; // We found an accept state!
+  }
+  LOG (GNUNET_ERROR_TYPE_DEBUG,
+       "* %u, %u, [%u]\n",
+       ctx->position,
+       strlen (ctx->info->description),
+       GNUNET_BLOCK_is_accepting (block, result->size));
+  regex_next_edge (block, result->size, ctx);
+
+  GNUNET_STATISTICS_update (ctx->info->stats, "# regex cadet blocks iterated",
+                            1, GNUNET_NO);
+
+  return GNUNET_YES;
+}
+
+
+/**
+ * Iterator over edges in a regex block retrieved from the DHT.
+ *
+ * @param cls Closure (context of the search).
+ * @param token Token that follows to next state.
+ * @param len Lenght of token.
+ * @param key Hash of next state.
+ * @return #GNUNET_YES if should keep iterating, #GNUNET_NO otherwise.
+ */
+static int
+regex_edge_iterator (void *cls,
+                     const char *token,
+                     size_t len,
+                     const struct GNUNET_HashCode *key)
+{
+  struct RegexSearchContext *ctx = cls;
+  struct REGEX_INTERNAL_Search *info = ctx->info;
+  const char *current;
+  size_t current_len;
+
+  GNUNET_STATISTICS_update (info->stats, "# regex edges iterated",
+                            1, GNUNET_NO);
+  current = &info->description[ctx->position];
+  current_len = strlen (info->description) - ctx->position;
+  if (len > current_len)
+  {
+    LOG (GNUNET_ERROR_TYPE_DEBUG, "Token too long, END\n");
+    return GNUNET_YES;
+  }
+  if (0 != strncmp (current, token, len))
+  {
+    LOG (GNUNET_ERROR_TYPE_DEBUG, "Token doesn't match, END\n");
+    return GNUNET_YES;
+  }
+
+  if (len > ctx->longest_match)
+  {
+    LOG (GNUNET_ERROR_TYPE_DEBUG, "Token is longer, KEEP\n");
+    ctx->longest_match = len;
+    ctx->hash = *key;
+  }
+  else
+  {
+    LOG (GNUNET_ERROR_TYPE_DEBUG, "Token is not longer, IGNORE\n");
+  }
+
+  LOG (GNUNET_ERROR_TYPE_DEBUG, "*    End of regex edge iterator\n");
+  return GNUNET_YES;
+}
+
+
+/**
+ * Jump to the next edge, with the longest matching token.
+ *
+ * @param block Block found in the DHT.
+ * @param size Size of the block.
+ * @param ctx Context of the search.
+ */
+static void
+regex_next_edge (const struct RegexBlock *block,
+                 size_t size,
+                 struct RegexSearchContext *ctx)
+{
+  struct RegexSearchContext *new_ctx;
+  struct REGEX_INTERNAL_Search *info = ctx->info;
+  struct GNUNET_DHT_GetHandle *get_h;
+  struct GNUNET_HashCode *hash;
+  const char *rest;
+  int result;
+
+  LOG (GNUNET_ERROR_TYPE_DEBUG, "Next edge\n");
+  /* Find the longest match for the current string position,
+   * among tokens in the given block */
+  ctx->longest_match = 0;
+  result = REGEX_BLOCK_iterate (block, size,
+                                &regex_edge_iterator, ctx);
+  GNUNET_break (GNUNET_OK == result);
+
+  /* Did anything match? */
+  if (0 == ctx->longest_match)
+  {
+    LOG (GNUNET_ERROR_TYPE_DEBUG,
+        "no match in block\n");
+    return;
+  }
+
+  hash = &ctx->hash;
+  new_ctx = GNUNET_new (struct RegexSearchContext);
+  new_ctx->info = info;
+  new_ctx->position = ctx->position + ctx->longest_match;
+  GNUNET_array_append (info->contexts, info->n_contexts, new_ctx);
+
+  /* Check whether we already have a DHT GET running for it */
+  if (GNUNET_YES ==
+      GNUNET_CONTAINER_multihashmap_contains (info->dht_get_handles, hash))
+  {
+    LOG (GNUNET_ERROR_TYPE_DEBUG,
+        "GET for %s running, END\n",
+         GNUNET_h2s (hash));
+    GNUNET_CONTAINER_multihashmap_get_multiple (info->dht_get_results,
+                                                hash,
+                                                &regex_result_iterator,
+                                                new_ctx);
+    return; /* We are already looking for it */
+  }
+
+  GNUNET_STATISTICS_update (info->stats, "# regex nodes traversed",
+                            1, GNUNET_NO);
+
+  LOG (GNUNET_ERROR_TYPE_DEBUG,
+       "Following edges at %s for offset %u in `%s'\n",
+       GNUNET_h2s (hash),
+       (unsigned int) ctx->position,
+       info->description);
+  rest = &new_ctx->info->description[new_ctx->position];
+  get_h =
+      GNUNET_DHT_get_start (info->dht,    /* handle */
+                            GNUNET_BLOCK_TYPE_REGEX, /* type */
+                            hash,     /* key to search */
+                            DHT_REPLICATION, /* replication level */
+                            DHT_OPT,
+                            rest, /* xquery */
+                            strlen (rest) + 1,     /* xquery bits */
+                            &dht_get_string_handler, new_ctx);
+  if (GNUNET_OK !=
+      GNUNET_CONTAINER_multihashmap_put(info->dht_get_handles,
+                                        hash,
+                                        get_h,
+                                        GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_FAST))
+  {
+    GNUNET_break (0);
+    return;
+  }
+}
+
+
+/**
+ * Search for a peer offering a regex matching certain string in the DHT.
+ * The search runs until #REGEX_INTERNAL_search_cancel() is called, even if results
+ * are returned.
+ *
+ * @param dht An existing and valid DHT service handle.
+ * @param string String to match against the regexes in the DHT.
+ * @param callback Callback for found peers.
+ * @param callback_cls Closure for @c callback.
+ * @param stats Optional statistics handle to report usage. Can be NULL.
+ * @return Handle to stop search and free resources.
+ *         Must be freed by calling #REGEX_INTERNAL_search_cancel().
+ */
+struct REGEX_INTERNAL_Search *
+REGEX_INTERNAL_search (struct GNUNET_DHT_Handle *dht,
+                       const char *string,
+                       REGEX_INTERNAL_Found callback,
+                       void *callback_cls,
+                       struct GNUNET_STATISTICS_Handle *stats)
+{
+  struct REGEX_INTERNAL_Search *h;
+  struct GNUNET_DHT_GetHandle *get_h;
+  struct RegexSearchContext *ctx;
+  struct GNUNET_HashCode key;
+  size_t size;
+  size_t len;
+
+  /* Initialize handle */
+  GNUNET_assert (NULL != dht);
+  GNUNET_assert (NULL != callback);
+  h = GNUNET_new (struct REGEX_INTERNAL_Search);
+  h->dht = dht;
+  h->description = GNUNET_strdup (string);
+  h->callback = callback;
+  h->callback_cls = callback_cls;
+  h->stats = stats;
+  h->dht_get_handles = GNUNET_CONTAINER_multihashmap_create (32, GNUNET_NO);
+  h->dht_get_results = GNUNET_CONTAINER_multihashmap_create (32, GNUNET_NO);
+
+  /* Initialize context */
+  len = strlen (string);
+  size = REGEX_INTERNAL_get_first_key (string, len, &key);
+  LOG (GNUNET_ERROR_TYPE_INFO,
+       "Initial key for `%s' is %s (based on `%.*s')\n",
+       string,
+       GNUNET_h2s (&key),
+       size,
+       string);
+  ctx = GNUNET_new (struct RegexSearchContext);
+  ctx->position = size;
+  ctx->info = h;
+  GNUNET_array_append (h->contexts,
+                       h->n_contexts,
+                       ctx);
+  /* Start search in DHT */
+  get_h = GNUNET_DHT_get_start (h->dht,    /* handle */
+                                GNUNET_BLOCK_TYPE_REGEX, /* type */
+                                &key,     /* key to search */
+                                DHT_REPLICATION, /* replication level */
+                                DHT_OPT,
+                                &h->description[size],           /* xquery */
+                                // FIXME add BLOOMFILTER to exclude filtered peers
+                                len + 1 - size,                /* xquery bits */
+                                // FIXME add BLOOMFILTER SIZE
+                                &dht_get_string_handler, ctx);
+  GNUNET_break (
+    GNUNET_OK ==
+    GNUNET_CONTAINER_multihashmap_put (h->dht_get_handles,
+                                       &key,
+                                       get_h,
+                                       GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_FAST)
+               );
+
+  return h;
+}
+
+
+/**
+ * Iterator over hash map entries to cancel DHT GET requests after a
+ * successful connect_by_string.
+ *
+ * @param cls Closure (unused).
+ * @param key Current key code (unused).
+ * @param value Value in the hash map (get handle).
+ * @return #GNUNET_YES if we should continue to iterate,
+ *         #GNUNET_NO if not.
+ */
+static int
+regex_cancel_dht_get (void *cls,
+                      const struct GNUNET_HashCode * key,
+                      void *value)
+{
+  struct GNUNET_DHT_GetHandle *h = value;
+
+  GNUNET_DHT_get_stop (h);
+  return GNUNET_YES;
+}
+
+
+/**
+ * Iterator over hash map entries to free CadetRegexBlocks stored during the
+ * search for connect_by_string.
+ *
+ * @param cls Closure (unused).
+ * @param key Current key code (unused).
+ * @param value CadetRegexBlock in the hash map.
+ * @return #GNUNET_YES if we should continue to iterate,
+ *         #GNUNET_NO if not.
+ */
+static int
+regex_free_result (void *cls,
+                   const struct GNUNET_HashCode * key,
+                   void *value)
+{
+  GNUNET_free (value);
+  return GNUNET_YES;
+}
+
+
+/**
+ * Cancel an ongoing regex search in the DHT and free all resources.
+ *
+ * @param h the search context.
+ */
+void
+REGEX_INTERNAL_search_cancel (struct REGEX_INTERNAL_Search *h)
+{
+  unsigned int i;
+
+  GNUNET_free (h->description);
+  GNUNET_CONTAINER_multihashmap_iterate (h->dht_get_handles,
+                                         &regex_cancel_dht_get, NULL);
+  GNUNET_CONTAINER_multihashmap_iterate (h->dht_get_results,
+                                         &regex_free_result, NULL);
+  GNUNET_CONTAINER_multihashmap_destroy (h->dht_get_results);
+  GNUNET_CONTAINER_multihashmap_destroy (h->dht_get_handles);
+  if (0 < h->n_contexts)
+  {
+    for (i = 0; i < h->n_contexts; i++)
+      GNUNET_free (h->contexts[i]);
+    GNUNET_free (h->contexts);
+  }
+  GNUNET_free (h);
+}
+
+
+/* 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 (file)
index 0000000..dc19454
--- /dev/null
@@ -0,0 +1,267 @@
+/*
+     This file is part of GNUnet
+     Copyright (C) 2012, 2013 GNUnet e.V.
+
+     GNUnet is free software: you can redistribute it and/or modify it
+     under the terms of the GNU Affero General Public License as published
+     by the Free Software Foundation, either version 3 of the License,
+     or (at your option) any later version.
+
+     GNUnet is distributed in the hope that it will be useful, but
+     WITHOUT ANY WARRANTY; without even the implied warranty of
+     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+     Affero General Public License for more details.
+    
+     You should have received a copy of the GNU Affero General Public License
+     along with this program.  If not, see <http://www.gnu.org/licenses/>.
+*/
+/**
+ * @file regex/regex_internal_lib.h
+ * @brief library to parse regular expressions into dfa
+ * @author Maximilian Szengel
+ */
+
+#ifndef REGEX_INTERNAL_LIB_H
+#define REGEX_INTERNAL_LIB_H
+
+#include "gnunet_util_lib.h"
+#include "gnunet_dht_service.h"
+#include "gnunet_statistics_service.h"
+#include "regex_block_lib.h"
+
+#ifdef __cplusplus
+extern "C"
+{
+#if 0                           /* keep Emacsens' auto-indent happy */
+}
+#endif
+#endif
+
+
+/**
+ * Automaton (NFA/DFA) representation.
+ */
+struct REGEX_INTERNAL_Automaton;
+
+
+/**
+ * Construct DFA for the given 'regex' of length 'len'.
+ *
+ * Path compression means, that for example a DFA o -> a -> b -> c -> o will be
+ * compressed to o -> abc -> o. Note that this parameter influences the
+ * non-determinism of states of the resulting NFA in the DHT (number of outgoing
+ * edges with the same label). For example for an application that stores IPv4
+ * addresses as bitstrings it could make sense to limit the path compression to
+ * 4 or 8.
+ *
+ * @param regex regular expression string.
+ * @param len length of the regular expression.
+ * @param max_path_len limit the path compression length to the
+ *        given value. If set to 1, no path compression is applied. Set to 0 for
+ *        maximal possible path compression (generally not desireable).
+ * @return DFA, needs to be freed using #REGEX_INTERNAL_automaton_destroy().
+ */
+struct REGEX_INTERNAL_Automaton *
+REGEX_INTERNAL_construct_dfa (const char *regex,
+                              const size_t len,
+                              unsigned int max_path_len);
+
+
+/**
+ * Free the memory allocated by constructing the REGEX_INTERNAL_Automaton.
+ * data structure.
+ *
+ * @param a automaton to be destroyed.
+ */
+void
+REGEX_INTERNAL_automaton_destroy (struct REGEX_INTERNAL_Automaton *a);
+
+
+/**
+ * Evaluates the given 'string' against the given compiled regex.
+ *
+ * @param a automaton.
+ * @param string string to check.
+ *
+ * @return 0 if string matches, non 0 otherwise.
+ */
+int
+REGEX_INTERNAL_eval (struct REGEX_INTERNAL_Automaton *a,
+                     const char *string);
+
+
+/**
+ * Get the first key for the given @a input_string. This hashes
+ * the first x bits of the @a input_string.
+ *
+ * @param input_string string.
+ * @param string_len length of the @a input_string.
+ * @param key pointer to where to write the hash code.
+ * @return number of bits of @a input_string that have been consumed
+ *         to construct the key
+ */
+size_t
+REGEX_INTERNAL_get_first_key (const char *input_string,
+                              size_t string_len,
+                              struct GNUNET_HashCode * key);
+
+
+/**
+ * Iterator callback function.
+ *
+ * @param cls closure.
+ * @param key hash for current state.
+ * @param proof proof for current state
+ * @param accepting #GNUNET_YES if this is an accepting state, #GNUNET_NO if not.
+ * @param num_edges number of edges leaving current state.
+ * @param edges edges leaving current state.
+ */
+typedef void
+(*REGEX_INTERNAL_KeyIterator)(void *cls,
+                              const struct GNUNET_HashCode *key,
+                              const char *proof,
+                              int accepting,
+                              unsigned int num_edges,
+                              const struct REGEX_BLOCK_Edge *edges);
+
+
+/**
+ * Iterate over all edges starting from start state of automaton 'a'. Calling
+ * iterator for each edge.
+ *
+ * @param a automaton.
+ * @param iterator iterator called for each edge.
+ * @param iterator_cls closure.
+ */
+void
+REGEX_INTERNAL_iterate_all_edges (struct REGEX_INTERNAL_Automaton *a,
+                                  REGEX_INTERNAL_KeyIterator iterator,
+                                  void *iterator_cls);
+
+
+/**
+ * Iterate over all edges of automaton 'a' that are reachable from a state with
+ * a proof of at least #GNUNET_REGEX_INITIAL_BYTES characters.
+ *
+ * Call the iterator for each such edge.
+ *
+ * @param a automaton.
+ * @param iterator iterator called for each reachable edge.
+ * @param iterator_cls closure.
+ */
+void
+REGEX_INTERNAL_iterate_reachable_edges (struct REGEX_INTERNAL_Automaton *a,
+                                        REGEX_INTERNAL_KeyIterator iterator,
+                                        void *iterator_cls);
+
+
+
+/**
+ * Handle to store cached data about a regex announce.
+ */
+struct REGEX_INTERNAL_Announcement;
+
+/**
+ * Handle to store data about a regex search.
+ */
+struct REGEX_INTERNAL_Search;
+
+
+/**
+ * Announce a regular expression: put all states of the automaton in the DHT.
+ * Does not free resources, must call #REGEX_INTERNAL_announce_cancel() for that.
+ *
+ * @param dht An existing and valid DHT service handle. CANNOT be NULL.
+ * @param priv our private key, must remain valid until the announcement is cancelled
+ * @param regex Regular expression to announce.
+ * @param compression How many characters per edge can we squeeze?
+ * @param stats Optional statistics handle to report usage. Can be NULL.
+ * @return Handle to reuse o free cached resources.
+ *         Must be freed by calling #REGEX_INTERNAL_announce_cancel().
+ */
+struct REGEX_INTERNAL_Announcement *
+REGEX_INTERNAL_announce (struct GNUNET_DHT_Handle *dht,
+                        const struct GNUNET_CRYPTO_EddsaPrivateKey *priv,
+                        const char *regex,
+                        uint16_t compression,
+                        struct GNUNET_STATISTICS_Handle *stats);
+
+
+/**
+ * Announce again a regular expression previously announced.
+ * Does use caching to speed up process.
+ *
+ * @param h Handle returned by a previous #REGEX_INTERNAL_announce() call.
+ */
+void
+REGEX_INTERNAL_reannounce (struct REGEX_INTERNAL_Announcement *h);
+
+
+/**
+ * Clear all cached data used by a regex announce.
+ * Does not close DHT connection.
+ *
+ * @param h Handle returned by a previous #REGEX_INTERNAL_announce() call.
+ */
+void
+REGEX_INTERNAL_announce_cancel (struct REGEX_INTERNAL_Announcement *h);
+
+
+/**
+ * Search callback function.
+ *
+ * @param cls Closure provided in #REGEX_INTERNAL_search().
+ * @param id Peer providing a regex that matches the string.
+ * @param get_path Path of the get request.
+ * @param get_path_length Length of @a get_path.
+ * @param put_path Path of the put request.
+ * @param put_path_length Length of the @a put_path.
+ */
+typedef void
+(*REGEX_INTERNAL_Found)(void *cls,
+                        const struct GNUNET_PeerIdentity *id,
+                        const struct GNUNET_PeerIdentity *get_path,
+                        unsigned int get_path_length,
+                        const struct GNUNET_PeerIdentity *put_path,
+                        unsigned int put_path_length);
+
+
+/**
+ * Search for a peer offering a regex matching certain string in the DHT.
+ * The search runs until #REGEX_INTERNAL_search_cancel() is called, even if results
+ * are returned.
+ *
+ * @param dht An existing and valid DHT service handle.
+ * @param string String to match against the regexes in the DHT.
+ * @param callback Callback for found peers.
+ * @param callback_cls Closure for @c callback.
+ * @param stats Optional statistics handle to report usage. Can be NULL.
+ * @return Handle to stop search and free resources.
+ *         Must be freed by calling #REGEX_INTERNAL_search_cancel().
+ */
+struct REGEX_INTERNAL_Search *
+REGEX_INTERNAL_search (struct GNUNET_DHT_Handle *dht,
+                       const char *string,
+                       REGEX_INTERNAL_Found callback,
+                       void *callback_cls,
+                       struct GNUNET_STATISTICS_Handle *stats);
+
+/**
+ * Stop search and free all data used by a #REGEX_INTERNAL_search() call.
+ * Does not close DHT connection.
+ *
+ * @param h Handle returned by a previous #REGEX_INTERNAL_search() call.
+ */
+void
+REGEX_INTERNAL_search_cancel (struct REGEX_INTERNAL_Search *h);
+
+
+#if 0                           /* keep Emacsens' auto-indent happy */
+{
+#endif
+#ifdef __cplusplus
+}
+#endif
+
+/* end of regex_internal_lib.h */
+#endif
diff --git a/src/regex/regex_ipc.h b/src/regex/regex_ipc.h
new file mode 100644 (file)
index 0000000..71ac273
--- /dev/null
@@ -0,0 +1,105 @@
+/*
+     This file is part of GNUnet
+     Copyright (C) 2012, 2013 GNUnet e.V.
+
+     GNUnet is free software: you can redistribute it and/or modify it
+     under the terms of the GNU Affero General Public License as published
+     by the Free Software Foundation, either version 3 of the License,
+     or (at your option) any later version.
+
+     GNUnet is distributed in the hope that it will be useful, but
+     WITHOUT ANY WARRANTY; without even the implied warranty of
+     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+     Affero General Public License for more details.
+    
+     You should have received a copy of the GNU Affero General Public License
+     along with this program.  If not, see <http://www.gnu.org/licenses/>.
+*/
+/**
+ * @file regex/regex_ipc.h
+ * @brief regex IPC messages (not called 'regex.h' due to conflict with
+ *        system headers)
+ * @author Christian Grothoff
+ */
+#ifndef REGEX_IPC_H
+#define REGEX_IPC_H
+
+#include "gnunet_util_lib.h"
+
+/**
+ * Request for regex service to announce capability.
+ */
+struct AnnounceMessage
+{
+
+  /**
+   * Type is GNUNET_MESSAGE_TYPE_REGEX_ANNOUNCE
+   */
+  struct GNUNET_MessageHeader header;
+
+  /**
+   * How many characters can we squeeze per edge?
+   */
+  uint16_t compression;
+
+  /**
+   * Always zero.
+   */
+  uint16_t reserved;
+
+  /**
+   * Delay between repeated announcements.
+   */
+  struct GNUNET_TIME_RelativeNBO refresh_delay;
+
+  /* followed by 0-terminated regex as string */
+};
+
+
+/**
+ * Message to initiate regex search.
+ */
+struct RegexSearchMessage
+{
+  /**
+   * Type is GNUNET_MESSAGE_TYPE_REGEX_SEARCH
+   */
+  struct GNUNET_MessageHeader header;
+
+  /* followed by 0-terminated search string */
+
+};
+
+
+/**
+ * Result from regex search.
+ */
+struct ResultMessage
+{
+  /**
+   * Type is GNUNET_MESSAGE_TYPE_REGEX_RESULT
+   */
+  struct GNUNET_MessageHeader header;
+
+  /**
+   * Number of entries in the GET path.
+   */
+  uint16_t get_path_length;
+
+  /**
+   * Number of entries in the PUT path.
+   */
+  uint16_t put_path_length;
+
+  /**
+   * Identity of the peer that was found.
+   */
+  struct GNUNET_PeerIdentity id;
+
+  /* followed by GET path and PUT path arrays */
+
+};
+
+
+/* end of regex_ipc.h */
+#endif
diff --git a/src/regex/regex_simulation_profiler_test.conf b/src/regex/regex_simulation_profiler_test.conf
new file mode 100644 (file)
index 0000000..9384aa2
--- /dev/null
@@ -0,0 +1,7 @@
+[regex-mysql]
+DATABASE = regex
+USER = gnunet
+PASSWORD = 
+HOST = localhost
+PORT = 3306
+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 (file)
index 0000000..d8f16e8
--- /dev/null
@@ -0,0 +1,316 @@
+/*
+     This file is part of GNUnet
+     Copyright (C) 2012 GNUnet e.V.
+
+     GNUnet is free software: you can redistribute it and/or modify it
+     under the terms of the GNU Affero General Public License as published
+     by the Free Software Foundation, either version 3 of the License,
+     or (at your option) any later version.
+
+     GNUnet is distributed in the hope that it will be useful, but
+     WITHOUT ANY WARRANTY; without even the implied warranty of
+     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+     Affero General Public License for more details.
+    
+     You should have received a copy of the GNU Affero General Public License
+     along with this program.  If not, see <http://www.gnu.org/licenses/>.
+*/
+/**
+ * @file src/regex/regex_test_graph.c
+ * @brief functions for creating .dot graphs from regexes
+ * @author Maximilian Szengel
+ */
+#include "platform.h"
+#include "regex_internal_lib.h"
+#include "regex_test_lib.h"
+#include "regex_internal.h"
+
+/**
+ * Context for graph creation. Passed as the cls to
+ * REGEX_TEST_automaton_save_graph_step.
+ */
+struct REGEX_TEST_Graph_Context
+{
+  /**
+   * File pointer to the dot file used for output.
+   */
+  FILE *filep;
+
+  /**
+   * Verbose flag, if it's set to GNUNET_YES additional info will be printed in
+   * the graph.
+   */
+  int verbose;
+
+  /**
+   * Coloring flag, if set to GNUNET_YES SCCs will be colored.
+   */
+  int coloring;
+};
+
+
+/**
+ * Recursive function doing DFS with 'v' as a start, detecting all SCCs inside
+ * the subgraph reachable from 'v'. Used with scc_tarjan function to detect all
+ * SCCs inside an automaton.
+ *
+ * @param scc_counter counter for numbering the sccs
+ * @param v start vertex
+ * @param index current index
+ * @param stack stack for saving all SCCs
+ * @param stack_size current size of the stack
+ */
+static void
+scc_tarjan_strongconnect (unsigned int *scc_counter,
+                          struct REGEX_INTERNAL_State *v, unsigned int *index,
+                          struct REGEX_INTERNAL_State **stack,
+                          unsigned int *stack_size)
+{
+  struct REGEX_INTERNAL_State *w;
+  struct REGEX_INTERNAL_Transition *t;
+
+  v->index = *index;
+  v->lowlink = *index;
+  (*index)++;
+  stack[(*stack_size)++] = v;
+  v->contained = 1;
+
+  for (t = v->transitions_head; NULL != t; t = t->next)
+  {
+    w = t->to_state;
+
+    if (NULL == w)
+      continue;
+
+    if (w->index < 0)
+    {
+      scc_tarjan_strongconnect (scc_counter, w, index, stack, stack_size);
+      v->lowlink = (v->lowlink > w->lowlink) ? w->lowlink : v->lowlink;
+    }
+    else if (1 == w->contained)
+      v->lowlink = (v->lowlink > w->index) ? w->index : v->lowlink;
+  }
+
+  if (v->lowlink == v->index)
+  {
+    (*scc_counter)++;
+    do
+    {
+      w = stack[--(*stack_size)];
+      w->contained = 0;
+      w->scc_id = *scc_counter;
+    }
+    while (w != v);
+  }
+}
+
+
+/**
+ * Detect all SCCs (Strongly Connected Components) inside the given automaton.
+ * SCCs will be marked using the scc_id on each state.
+ *
+ * @param a the automaton for which SCCs should be computed and assigned.
+ */
+static void
+scc_tarjan (struct REGEX_INTERNAL_Automaton *a)
+{
+  unsigned int index;
+  unsigned int scc_counter;
+  struct REGEX_INTERNAL_State *v;
+  struct REGEX_INTERNAL_State *stack[a->state_count];
+  unsigned int stack_size;
+
+  for (v = a->states_head; NULL != v; v = v->next)
+  {
+    v->contained = 0;
+    v->index = -1;
+    v->lowlink = -1;
+  }
+
+  stack_size = 0;
+  index = 0;
+  scc_counter = 0;
+
+  for (v = a->states_head; NULL != v; v = v->next)
+  {
+    if (v->index < 0)
+      scc_tarjan_strongconnect (&scc_counter, v, &index, stack, &stack_size);
+  }
+}
+
+
+/**
+ * Save a state to an open file pointer. cls is expected to be a file pointer to
+ * an open file. Used only in conjunction with
+ * REGEX_TEST_automaton_save_graph.
+ *
+ * @param cls file pointer.
+ * @param count current count of the state, not used.
+ * @param s state.
+ */
+void
+REGEX_TEST_automaton_save_graph_step (void *cls, unsigned int count,
+                                        struct REGEX_INTERNAL_State *s)
+{
+  struct REGEX_TEST_Graph_Context *ctx = cls;
+  struct REGEX_INTERNAL_Transition *ctran;
+  char *s_acc = NULL;
+  char *s_tran = NULL;
+  char *name;
+  char *to_name;
+
+  if (GNUNET_YES == ctx->verbose)
+    GNUNET_asprintf (&name, "%i (%s) (%s) (%s)", s->dfs_id, s->name, s->proof,
+                     GNUNET_h2s (&s->hash));
+  else
+    GNUNET_asprintf (&name, "%i", s->dfs_id);
+
+  if (s->accepting)
+  {
+    if (GNUNET_YES == ctx->coloring)
+    {
+      GNUNET_asprintf (&s_acc,
+                       "\"%s\" [shape=doublecircle, color=\"0.%i 0.8 0.95\"];\n",
+                       name, s->scc_id * s->scc_id);
+    }
+    else
+    {
+      GNUNET_asprintf (&s_acc, "\"%s\" [shape=doublecircle];\n", name,
+                       s->scc_id);
+    }
+  }
+  else if (GNUNET_YES == ctx->coloring)
+  {
+    GNUNET_asprintf (&s_acc,
+                     "\"%s\" [shape=circle, color=\"0.%i 0.8 0.95\"];\n", name,
+                     s->scc_id * s->scc_id);
+  }
+  else
+  {
+    GNUNET_asprintf (&s_acc, "\"%s\" [shape=circle];\n", name, s->scc_id);
+  }
+
+  GNUNET_assert (NULL != s_acc);
+
+  fwrite (s_acc, strlen (s_acc), 1, ctx->filep);
+  GNUNET_free (s_acc);
+  s_acc = NULL;
+
+  for (ctran = s->transitions_head; NULL != ctran; ctran = ctran->next)
+  {
+    if (NULL == ctran->to_state)
+    {
+      GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+                  "Transition from State %i has no state for transitioning\n",
+                  s->id);
+      continue;
+    }
+
+    if (GNUNET_YES == ctx->verbose)
+    {
+      GNUNET_asprintf (&to_name, "%i (%s) (%s) (%s)", ctran->to_state->dfs_id,
+                       ctran->to_state->name, ctran->to_state->proof,
+                       GNUNET_h2s (&ctran->to_state->hash));
+    }
+    else
+      GNUNET_asprintf (&to_name, "%i", ctran->to_state->dfs_id);
+
+    if (NULL == ctran->label)
+    {
+      if (GNUNET_YES == ctx->coloring)
+      {
+        GNUNET_asprintf (&s_tran,
+                         "\"%s\" -> \"%s\" [label = \"ε\", color=\"0.%i 0.8 0.95\"];\n",
+                         name, to_name, s->scc_id * s->scc_id);
+      }
+      else
+      {
+        GNUNET_asprintf (&s_tran, "\"%s\" -> \"%s\" [label = \"ε\"];\n", name,
+                         to_name, s->scc_id);
+      }
+    }
+    else
+    {
+      if (GNUNET_YES == ctx->coloring)
+      {
+        GNUNET_asprintf (&s_tran,
+                         "\"%s\" -> \"%s\" [label = \"%s\", color=\"0.%i 0.8 0.95\"];\n",
+                         name, to_name, ctran->label, s->scc_id * s->scc_id);
+      }
+      else
+      {
+        GNUNET_asprintf (&s_tran, "\"%s\" -> \"%s\" [label = \"%s\"];\n", name,
+                         to_name, ctran->label, s->scc_id);
+      }
+    }
+
+    GNUNET_free (to_name);
+
+    GNUNET_assert (NULL != s_tran);
+
+    fwrite (s_tran, strlen (s_tran), 1, ctx->filep);
+    GNUNET_free (s_tran);
+    s_tran = NULL;
+  }
+
+  GNUNET_free (name);
+}
+
+
+/**
+ * Save the given automaton as a GraphViz dot file.
+ *
+ * @param a the automaton to be saved.
+ * @param filename where to save the file.
+ * @param options options for graph generation that include coloring or verbose
+ *                mode
+ */
+void
+REGEX_TEST_automaton_save_graph (struct REGEX_INTERNAL_Automaton *a,
+                                   const char *filename,
+                                   enum REGEX_TEST_GraphSavingOptions options)
+{
+  char *start;
+  char *end;
+  struct REGEX_TEST_Graph_Context ctx;
+
+  if (NULL == a)
+  {
+    GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Could not print NFA, was NULL!");
+    return;
+  }
+
+  if (NULL == filename || strlen (filename) < 1)
+  {
+    GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "No Filename given!");
+    return;
+  }
+
+  ctx.filep = fopen (filename, "w");
+  ctx.verbose =
+      (0 == (options & REGEX_TEST_GRAPH_VERBOSE)) ? GNUNET_NO : GNUNET_YES;
+  ctx.coloring =
+      (0 == (options & REGEX_TEST_GRAPH_COLORING)) ? GNUNET_NO : GNUNET_YES;
+
+  if (NULL == ctx.filep)
+  {
+    GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Could not open file for writing: %s",
+                filename);
+    return;
+  }
+
+  /* First add the SCCs to the automaton, so we can color them nicely */
+  if (GNUNET_YES == ctx.coloring)
+    scc_tarjan (a);
+
+  start = "digraph G {\nrankdir=LR\n";
+  fwrite (start, strlen (start), 1, ctx.filep);
+
+  REGEX_INTERNAL_automaton_traverse (a, a->start, NULL, NULL,
+                                   &REGEX_TEST_automaton_save_graph_step,
+                                   &ctx);
+
+  end = "\n}\n";
+  fwrite (end, strlen (end), 1, ctx.filep);
+  fclose (ctx.filep);
+}
diff --git a/src/regex/regex_test_lib.c b/src/regex/regex_test_lib.c
new file mode 100644 (file)
index 0000000..d25799e
--- /dev/null
@@ -0,0 +1,646 @@
+/*
+ *  This file is part of GNUnet
+ *  Copyright (C) 2012-2017 GNUnet e.V.
+ *
+ *  GNUnet is free software: you can redistribute it and/or modify it
+ *  under the terms of the GNU Affero General Public License as published
+ *  by the Free Software Foundation, either version 3 of the License,
+ *  or (at your option) any later version.
+ *
+ *  GNUnet is distributed in the hope that it will be useful, but
+ *  WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ *  Affero General Public License for more details.
+ * 
+ *  You should have received a copy of the GNU Affero General Public License
+ *  along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+/**
+ * @file src/regex/regex_test_lib.c
+ * @brief library to read regexes representing IP networks from a file.
+ *        and simplyfinying the into one big regex, in order to run
+ *        tests (regex performance, cadet profiler).
+ * @author Bartlomiej Polot
+ */
+
+#include "platform.h"
+#include "gnunet_util_lib.h"
+
+
+/**
+ * Struct to hold the tree formed by prefix-combining the regexes.
+ */
+struct RegexCombineCtx
+{
+  /**
+   * Child nodes with same prefix and token.
+   */
+  struct RegexCombineCtx **children;
+
+  /**
+   * Alphabet size (how many @a children there are)
+   */
+  unsigned int size;
+
+  /**
+   * Token.
+   */
+  char *s;
+};
+
+
+/**
+ * Char 2 int
+ *
+ * Convert a character into its int value depending on the base used
+ *
+ * @param c Char
+ * @param size base (2, 8 or 16(hex))
+ *
+ * @return Int in range [0, (base-1)]
+ */
+static int
+c2i (char c, int size)
+{
+  switch (size)
+  {
+    case 2:
+    case 8:
+      return c - '0';
+      break;
+    case 16:
+      if (c >= '0' && c <= '9')
+        return c - '0';
+      else if (c >= 'A' && c <= 'F')
+        return c - 'A' + 10;
+      else if (c >= 'a' && c <= 'f')
+        return c - 'a' + 10;
+      else
+      {
+        GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+                    "Cannot convert char %c in base %u\n",
+                    c, size);
+        GNUNET_assert (0);
+      }
+      break;
+    default:
+      GNUNET_assert (0);
+  }
+}
+
+
+/**
+ * Printf spaces to indent the regex tree
+ *
+ * @param n Indentation level
+ */
+static void
+space (int n)
+{
+  for (int i = 0; i < n; i++)
+    fprintf (stderr, "| ");
+}
+
+
+/**
+ * Printf the combined regex ctx.
+ *
+ * @param ctx The ctx to printf
+ * @param level Indentation level to start with
+ */
+static void
+debugctx (struct RegexCombineCtx *ctx, int level)
+{
+#if DEBUG_REGEX
+  if (NULL != ctx->s)
+  {
+    space (level - 1);
+    fprintf (stderr, "%u:'%s'\n", c2i(ctx->s[0], ctx->size), ctx->s);
+  }
+  else
+    fprintf (stderr, "ROOT (base %u)\n", ctx->size);
+  for (unsigned int i = 0; i < ctx->size; i++)
+  {
+    if (NULL != ctx->children[i])
+    {
+      space (level);
+      debugctx (ctx->children[i], level + 1);
+    }
+  }
+  fflush(stderr);
+#endif
+}
+
+
+/**
+ * Add a single regex to a context, combining with exisiting regex by-prefix.
+ *
+ * @param ctx Context with 0 or more regexes.
+ * @param regex Regex to add.
+ */
+static void
+regex_add (struct RegexCombineCtx *ctx,
+          const char *regex);
+
+
+/**
+ * Create and initialize a new RegexCombineCtx.
+ *
+ * @param alphabet_size Size of the alphabet (and the Trie array)
+ */
+static struct RegexCombineCtx *
+new_regex_ctx (unsigned int alphabet_size)
+{
+  struct RegexCombineCtx *ctx;
+  size_t array_size;
+
+  array_size = sizeof(struct RegexCombineCtx *) * alphabet_size;
+  ctx = GNUNET_new (struct RegexCombineCtx);
+  ctx->children = GNUNET_malloc (array_size);
+  ctx->size = alphabet_size;
+
+  return ctx;
+}
+
+
+static void
+move_children (struct RegexCombineCtx *dst,
+              const struct RegexCombineCtx *src)
+{
+  size_t array_size;
+
+  array_size = sizeof(struct RegexCombineCtx *) * src->size;
+  GNUNET_memcpy (dst->children,
+                 src->children,
+                 array_size);
+  for (unsigned int i = 0; i < src->size; i++)
+  {
+    src->children[i] = NULL;
+  }
+}
+
+
+/**
+ * Extract a string from all prefix-combined regexes.
+ *
+ * @param ctx Context with 0 or more regexes.
+ *
+ * @return Regex that matches any of the added regexes.
+ */
+static char *
+regex_combine (struct RegexCombineCtx *ctx)
+{
+  struct RegexCombineCtx *p;
+  unsigned int i;
+  size_t len;
+  char *regex;
+  char *tmp;
+  char *s;
+  int opt;
+
+  GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "new combine %s\n", ctx->s);
+  regex = GNUNET_strdup ("");
+  opt = GNUNET_NO;
+  for (i = 0; i < ctx->size; i++)
+  {
+    p = ctx->children[i];
+    if (NULL == p)
+      continue;
+    GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+                "adding '%s' to innner %s\n",
+                p->s, ctx->s);
+    s = regex_combine (p);
+    GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "  total '%s'\n", s);
+    if (strlen(s) == 0)
+    {
+      opt = GNUNET_YES;
+    }
+    else
+    {
+      GNUNET_asprintf (&tmp, "%s%s|", regex, s);
+      GNUNET_free_non_null (regex);
+      regex = tmp;
+    }
+    GNUNET_free_non_null (s);
+    GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "  so far '%s' for inner %s\n", regex, ctx->s);
+  }
+
+  GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "opt: %d, innner: '%s'\n", opt, regex);
+  len = strlen (regex);
+  if (0 == len)
+  {
+    GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "empty, returning ''\n");
+    GNUNET_free (regex);
+    return NULL == ctx->s ? NULL : GNUNET_strdup (ctx->s);
+  }
+
+  if ('|' == regex[len - 1])
+    regex[len - 1] = '\0';
+
+  if (NULL != ctx->s)
+  {
+    if (opt)
+      GNUNET_asprintf (&s, "%s(%s)?", ctx->s, regex);
+    else
+      GNUNET_asprintf (&s, "%s(%s)", ctx->s, regex);
+    GNUNET_free (regex);
+    regex = s;
+  }
+
+  GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "partial: %s\n", regex);
+  return regex;
+}
+
+
+/**
+ * Get the number of matching characters on the prefix of both strings.
+ *
+ * @param s1 String 1.
+ * @param s2 String 2.
+ *
+ * @return Number of characters of matching prefix.
+ */
+static unsigned int
+get_prefix_length (const char *s1, const char *s2)
+{
+  unsigned int l1;
+  unsigned int l2;
+  unsigned int limit;
+  unsigned int i;
+
+  l1 = strlen (s1);
+  l2 = strlen (s2);
+  limit = l1 > l2 ? l2 : l1;
+
+  for (i = 0; i < limit; i++)
+  {
+    if (s1[i] != s2[i])
+      return i;
+  }
+  return limit;
+}
+
+
+/**
+ * Return the child context with the longest prefix match with the regex.
+ * Usually only one child will match, search all just in case.
+ *
+ * @param ctx Context whose children to search.
+ * @param regex String to match.
+ *
+ * @return Child with the longest prefix, NULL if no child matches.
+ */
+static struct RegexCombineCtx *
+get_longest_prefix (struct RegexCombineCtx *ctx, const char *regex)
+{
+  struct RegexCombineCtx *p;
+  struct RegexCombineCtx *best;
+  unsigned int i;
+  unsigned int l;
+  unsigned int best_l;
+
+  best_l = 0;
+  best = NULL;
+
+  for (i = 0; i < ctx->size; i++)
+  {
+    p = ctx->children[i];
+    if (NULL == p)
+      continue;
+
+    l = get_prefix_length (p->s, regex);
+    if (l > best_l)
+    {
+      GNUNET_break (0 == best_l);
+      best = p;
+      best_l = l;
+    }
+  }
+  return best;
+}
+
+static void
+regex_add_multiple (struct RegexCombineCtx *ctx,
+                    const char *regex,
+                    struct RegexCombineCtx **children)
+{
+  char tmp[2];
+  long unsigned int i;
+  size_t l;
+  struct RegexCombineCtx *newctx;
+  unsigned int count;
+
+  if ('(' != regex[0])
+  {
+    GNUNET_assert (0);
+  }
+
+  /* Does the regex cover *all* possible children? Then don't add any,
+   * as it will be covered by the post-regex "(a-z)*"
+   */
+  l = strlen (regex);
+  count = 0;
+  for (i = 1UL; i < l; i++)
+  {
+    if (regex[i] != '|' && regex[i] != ')')
+    {
+      count++;
+    }
+  }
+  if (count == ctx->size)
+  {
+    return;
+  }
+
+  /* Add every component as a child node */
+  tmp[1] = '\0';
+  for (i = 1UL; i < l; i++)
+  {
+    if (regex[i] != '|' && regex[i] != ')')
+    {
+      tmp[0] = regex[i];
+      newctx = new_regex_ctx(ctx->size);
+      newctx->s = GNUNET_strdup (tmp);
+      if (children != NULL)
+        GNUNET_memcpy (newctx->children,
+                       children,
+                       sizeof (*children) * ctx->size);
+      ctx->children[c2i(tmp[0], ctx->size)] = newctx;
+    }
+  }
+}
+
+/**
+ * Add a single regex to a context, splitting the exisiting state.
+ *
+ * We only had a partial match, split existing state, truncate the current node
+ * so it only contains the prefix, add suffix(es) as children.
+ *
+ * @param ctx Context to split.
+ * @param len Lenght of ctx->s
+ * @param prefix_l Lenght of common prefix of the new regex and @a ctx->s
+ */
+static void
+regex_split (struct RegexCombineCtx *ctx,
+             unsigned int len,
+             unsigned int prefix_l)
+{
+  struct RegexCombineCtx *newctx;
+  unsigned int idx;
+  char *suffix;
+
+  suffix = GNUNET_malloc (len - prefix_l + 1);
+  strncpy (suffix, &ctx->s[prefix_l], len - prefix_l + 1);
+
+  /* Suffix saved, truncate current node so it only contains the prefix,
+   * copy any children nodes to put as grandchildren and initialize new empty
+   * children array.
+   */
+  ctx->s[prefix_l] = '\0';
+
+  /* If the suffix is an OR expression, add multiple children */
+  if ('(' == suffix[0])
+  {
+    struct RegexCombineCtx **tmp;
+
+    tmp = ctx->children;
+    ctx->children = GNUNET_malloc (sizeof(*tmp) * ctx->size);
+    regex_add_multiple (ctx, suffix, tmp);
+    GNUNET_free (suffix);
+    GNUNET_free (tmp);
+    return;
+  }
+
+  /* The suffix is a normal string, add as one node */
+  newctx = new_regex_ctx (ctx->size);
+  newctx->s = suffix;
+  move_children (newctx, ctx);
+  idx = c2i(suffix[0], ctx->size);
+  ctx->children[idx] = newctx;
+}
+
+
+/**
+ * Add a single regex to a context, combining with exisiting regex by-prefix.
+ *
+ * @param ctx Context with 0 or more regexes.
+ * @param regex Regex to add.
+ */
+static void
+regex_add (struct RegexCombineCtx *ctx, const char *regex)
+{
+  struct RegexCombineCtx *p;
+  struct RegexCombineCtx *newctx;
+  long unsigned int l;
+  unsigned int prefix_l;
+  const char *rest_r;
+  const char *rest_s;
+  size_t len;
+  int idx;
+
+  GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+              "regex_add '%s' into '%s'\n",
+              regex, ctx->s);
+  l = strlen (regex);
+  if (0UL == l)
+    return;
+
+  /* If the regex is in the form of (a|b|c), add every character separately */
+  if ('(' == regex[0])
+  {
+    regex_add_multiple (ctx, regex, NULL);
+    return;
+  }
+
+  p = get_longest_prefix (ctx, regex);
+  if (NULL != p)
+  {
+    /* There is some prefix match, reduce regex and try again */
+    prefix_l = get_prefix_length (p->s, regex);
+    rest_s = &p->s[prefix_l];
+    rest_r = &regex[prefix_l];
+    GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "chosen '%s' [%u]\n", p->s, prefix_l);
+    GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "prefix r '%.*s'\n", prefix_l, p->s);
+    GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "rest r '%s'\n", rest_r);
+    GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "rest s '%s'\n", rest_s);
+    len = strlen (p->s);
+    if (prefix_l < len)
+    {
+      regex_split (p, len, prefix_l);
+    }
+    regex_add (p, rest_r);
+    return;
+  }
+
+  /* There is no prefix match, add new */
+  idx = c2i(regex[0], ctx->size);
+  if (NULL == ctx->children[idx] && NULL != ctx->s)
+  {
+    /* this was the end before, add empty string */
+    newctx = new_regex_ctx (ctx->size);
+    newctx->s = GNUNET_strdup ("");
+    ctx->children[idx] = newctx;
+  }
+  GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, " no match\n");
+  GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, " new state %s\n", regex);
+  GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, " under %s\n", ctx->s);
+  newctx = new_regex_ctx(ctx->size);
+  newctx->s = GNUNET_strdup (regex);
+  ctx->children[idx] = newctx;
+}
+
+
+/**
+ * Free all resources used by the context node and all its children.
+ *
+ * @param ctx Context to free.
+ */
+static void
+regex_ctx_destroy (struct RegexCombineCtx *ctx)
+{
+  unsigned int i;
+
+  if (NULL == ctx)
+    return;
+
+  for (i = 0; i < ctx->size; i++)
+  {
+    regex_ctx_destroy (ctx->children[i]);
+  }
+  GNUNET_free_non_null (ctx->s); /* 's' on root node is null */
+  GNUNET_free (ctx->children);
+  GNUNET_free (ctx);
+}
+
+
+/**
+ * Combine an array of regexes into a single prefix-shared regex.
+ * Returns a prefix-combine regex that matches the same strings as
+ * any of the original regexes.
+ *
+ * WARNING: only useful for reading specific regexes for specific applications,
+ *          namely the gnunet-regex-profiler / gnunet-regex-daemon.
+ *          This function DOES NOT support arbitrary regex combining.
+ *
+ * @param regexes A NULL-terminated array of regexes.
+ * @param alphabet_size Size of the alphabet the regex uses.
+ *
+ * @return A string with a single regex that matches any of the original regexes
+ */
+char *
+REGEX_TEST_combine (char * const regexes[], unsigned int alphabet_size)
+{
+  unsigned int i;
+  char *combined;
+  const char *current;
+  struct RegexCombineCtx *ctx;
+
+  ctx = new_regex_ctx (alphabet_size);
+  for (i = 0; regexes[i]; i++)
+  {
+    current = regexes[i];
+    GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Regex %u: %s\n", i, current);
+    regex_add (ctx, current);
+    debugctx (ctx, 0);
+  }
+  GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "\nCombining...\n");
+  debugctx (ctx, 0);
+
+  combined = regex_combine (ctx);
+
+  regex_ctx_destroy (ctx);
+
+  return combined;
+}
+
+
+/**
+ * Read a set of regexes from a file, one per line and return them in an array
+ * suitable for REGEX_TEST_combine.
+ * The array must be free'd using REGEX_TEST_free_from_file.
+ *
+ * @param filename Name of the file containing the regexes.
+ *
+ * @return A newly allocated, NULL terminated array of regexes.
+ */
+char **
+REGEX_TEST_read_from_file (const char *filename)
+{
+  struct GNUNET_DISK_FileHandle *f;
+  unsigned int nr;
+  unsigned int offset;
+  off_t size;
+  size_t len;
+  char *buffer;
+  char *regex;
+  char **regexes;
+
+  f = GNUNET_DISK_file_open (filename,
+                             GNUNET_DISK_OPEN_READ,
+                             GNUNET_DISK_PERM_NONE);
+  if (NULL == f)
+  {
+    GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+                "Can't open file %s for reading\n", filename);
+    return NULL;
+  }
+  if (GNUNET_OK != GNUNET_DISK_file_handle_size (f, &size))
+  {
+    GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+                "Can't get size of file %s\n", filename);
+    GNUNET_DISK_file_close (f);
+    return NULL;
+  }
+  GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+              "using file %s, size %llu\n",
+              filename, (unsigned long long) size);
+
+  buffer = GNUNET_malloc (size + 1);
+  GNUNET_DISK_file_read (f, buffer, size);
+  GNUNET_DISK_file_close (f);
+  regexes = GNUNET_malloc (sizeof (char *));
+  nr = 1;
+  offset = 0;
+  regex = NULL;
+  do
+  {
+    if (NULL == regex)
+      regex = GNUNET_malloc (size + 1);
+    len = (size_t) sscanf (&buffer[offset], "%s", regex);
+    if (0 == len)
+      break;
+    len = strlen (regex);
+    offset += len + 1;
+    if (len < 1)
+      continue;
+    regex[len] = '\0';
+    regex = GNUNET_realloc (regex, len + 1);
+    GNUNET_array_grow (regexes, nr, nr + 1);
+    GNUNET_assert (NULL == regexes[nr - 2]);
+    regexes[nr - 2] = regex;
+    regexes[nr - 1] = NULL;
+    regex = NULL;
+  } while (offset < size);
+  GNUNET_free_non_null (regex);
+  GNUNET_free (buffer);
+
+  return regexes;
+}
+
+
+/**
+ * Free all memory reserved for a set of regexes created by read_from_file.
+ *
+ * @param regexes NULL-terminated array of regexes.
+ */
+void
+REGEX_TEST_free_from_file (char **regexes)
+{
+  unsigned int i;
+
+  for (i = 0; regexes[i]; i++)
+    GNUNET_free (regexes[i]);
+  GNUNET_free (regexes);
+}
+
+/* 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 (file)
index 0000000..533ba3e
--- /dev/null
@@ -0,0 +1,157 @@
+/*
+ *  This file is part of GNUnet
+ *  Copyright (C) 2012 GNUnet e.V.
+ *
+ *  GNUnet is free software: you can redistribute it and/or modify it
+ *  under the terms of the GNU Affero General Public License as published
+ *  by the Free Software Foundation, either version 3 of the License,
+ *  or (at your option) any later version.
+ *
+ *  GNUnet is distributed in the hope that it will be useful, but
+ *  WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ *  Affero General Public License for more details.
+ * 
+ *  You should have received a copy of the GNU Affero General Public License
+ *  along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+/**
+ * @file src/regex/regex_test_lib.h
+ * @brief library to read regexes representing IP networks from a file.
+ *        and simplifying the into one big regex, in order to run
+ *        tests (regex performance, regex profiler).
+ * @author Bertlomiej Polot
+ */
+
+#ifndef REGEX_INTERNAL_TEST_LIB_H
+#define REGEX_INTERNAL_TEST_LIB_H
+
+#include "regex_internal_lib.h"
+
+#ifdef __cplusplus
+extern "C"
+{
+  #if 0                           /* keep Emacsens' auto-indent happy */
+}
+#endif
+#endif
+
+
+/**
+ * Combine an array of regexes into a single prefix-shared regex.
+ * Returns a prefix-combine regex that matches the same strings as
+ * any of the original regexes.
+ *
+ * WARNING: only useful for reading specific regexes for specific applications,
+ *          namely the gnunet-regex-profiler / gnunet-regex-daemon.
+ *          This function DOES NOT support arbitrary regex combining.
+ *
+ * @param regexes A NULL-terminated array of regexes.
+ * @param alphabet_size Size of the alphabet the regex uses.
+ *
+ * @return A string with a single regex that matches any of the original regexes
+ */
+char *
+REGEX_TEST_combine (char * const regexes[], unsigned int alphabet_size);
+
+
+/**
+ * Read a set of regexes from a file, one per line and return them in an array
+ * suitable for REGEX_TEST_combine.
+ * The array must be free'd using REGEX_TEST_free_from_file.
+ *
+ * @param filename Name of the file containing the regexes.
+ *
+ * @return A newly allocated, NULL terminated array of regexes.
+ */
+char **
+REGEX_TEST_read_from_file (const char *filename);
+
+
+/**
+ * Free all memory reserved for a set of regexes created by read_from_file.
+ *
+ * @param regexes NULL-terminated array of regexes.
+ */
+void
+REGEX_TEST_free_from_file (char **regexes);
+
+
+/**
+ * Generate a (pseudo) random regular expression of length 'rx_length', as well
+ * as a (optional) string that will be matched by the generated regex. The
+ * returned regex needs to be freed.
+ *
+ * @param rx_length length of the random regex.
+ * @param matching_str (optional) pointer to a string that will contain a string
+ *                     that will be matched by the generated regex, if
+ *                     'matching_str' pointer was not NULL.
+ *
+ * @return NULL if 'rx_length' is 0, a random regex of length 'rx_length', which
+ *         needs to be freed, otherwise.
+ */
+char *
+REGEX_TEST_generate_random_regex (size_t rx_length, char *matching_str);
+
+
+/**
+ * Generate a random string of maximum length 'max_len' that only contains literals allowed
+ * in a regular expression. The string might be 0 chars long but is garantueed
+ * to be shorter or equal to 'max_len'.
+ *
+ * @param max_len maximum length of the string that should be generated.
+ *
+ * @return random string that needs to be freed.
+ */
+char *
+REGEX_TEST_generate_random_string (size_t max_len);
+
+
+/**
+ * Options for graph creation function
+ * REGEX_TEST_automaton_save_graph.
+ */
+enum REGEX_TEST_GraphSavingOptions
+{
+  /**
+   * Default. Do nothing special.
+   */
+  REGEX_TEST_GRAPH_DEFAULT = 0,
+
+  /**
+   * The generated graph will include extra information such as the NFA states
+   * that were used to generate the DFA state.
+   */
+  REGEX_TEST_GRAPH_VERBOSE = 1,
+
+  /**
+   * Enable graph coloring. Will color each SCC in a different color.
+   */
+  REGEX_TEST_GRAPH_COLORING = 2
+};
+
+
+/**
+ * Save the given automaton as a GraphViz dot file.
+ *
+ * @param a the automaton to be saved.
+ * @param filename where to save the file.
+ * @param options options for graph generation that include coloring or verbose
+ *                mode
+ */
+void
+REGEX_TEST_automaton_save_graph (struct REGEX_INTERNAL_Automaton *a,
+                                   const char *filename,
+                                   enum REGEX_TEST_GraphSavingOptions options);
+
+
+
+#if 0                           /* keep Emacsens' auto-indent happy */
+{
+  #endif
+  #ifdef __cplusplus
+}
+#endif
+
+/* end of regex_internal_lib.h */
+#endif
diff --git a/src/regex/regex_test_random.c b/src/regex/regex_test_random.c
new file mode 100644 (file)
index 0000000..6e51885
--- /dev/null
@@ -0,0 +1,169 @@
+/*
+     This file is part of GNUnet
+     Copyright (C) 2012 GNUnet e.V.
+
+     GNUnet is free software: you can redistribute it and/or modify it
+     under the terms of the GNU Affero General Public License as published
+     by the Free Software Foundation, either version 3 of the License,
+     or (at your option) any later version.
+
+     GNUnet is distributed in the hope that it will be useful, but
+     WITHOUT ANY WARRANTY; without even the implied warranty of
+     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+     Affero General Public License for more details.
+    
+     You should have received a copy of the GNU Affero General Public License
+     along with this program.  If not, see <http://www.gnu.org/licenses/>.
+*/
+/**
+ * @file src/regex/regex_test_random.c
+ * @brief functions for creating random regular expressions and strings
+ * @author Maximilian Szengel
+ */
+#include "platform.h"
+#include "regex_test_lib.h"
+#include "gnunet_crypto_lib.h"
+#include "regex_internal.h"
+
+
+/**
+ * Get a (pseudo) random valid literal for building a regular expression.
+ *
+ * @return random valid literal
+ */
+static char
+get_random_literal ()
+{
+  uint32_t ridx;
+
+  ridx =
+      GNUNET_CRYPTO_random_u32 (GNUNET_CRYPTO_QUALITY_WEAK,
+                                (uint32_t) strlen (ALLOWED_LITERALS));
+
+  return ALLOWED_LITERALS[ridx];
+}
+
+
+/**
+ * Generate a (pseudo) random regular expression of length 'rx_length', as well
+ * as a (optional) string that will be matched by the generated regex. The
+ * returned regex needs to be freed.
+ *
+ * @param rx_length length of the random regex.
+ * @param matching_str (optional) pointer to a string that will contain a string
+ *                     that will be matched by the generated regex, if
+ *                     'matching_str' pointer was not NULL. Make sure you
+ *                     allocated at least rx_length+1 bytes for this sting.
+ *
+ * @return NULL if 'rx_length' is 0, a random regex of length 'rx_length', which
+ *         needs to be freed, otherwise.
+ */
+char *
+REGEX_TEST_generate_random_regex (size_t rx_length, char *matching_str)
+{
+  char *rx;
+  char *rx_p;
+  char *matching_strp;
+  unsigned int i;
+  unsigned int char_op_switch;
+  unsigned int last_was_op;
+  int rx_op;
+  char current_char;
+
+  if (0 == rx_length)
+    return NULL;
+
+  if (NULL != matching_str)
+    matching_strp = matching_str;
+  else
+    matching_strp = NULL;
+
+  rx = GNUNET_malloc (rx_length + 1);
+  rx_p = rx;
+  current_char = 0;
+  last_was_op = 1;
+
+  for (i = 0; i < rx_length; i++)
+  {
+    char_op_switch = GNUNET_CRYPTO_random_u32 (GNUNET_CRYPTO_QUALITY_WEAK, 2);
+
+    if (0 == char_op_switch && !last_was_op)
+    {
+      last_was_op = 1;
+      rx_op = GNUNET_CRYPTO_random_u32 (GNUNET_CRYPTO_QUALITY_WEAK, 4);
+
+      switch (rx_op)
+      {
+      case 0:
+        current_char = '+';
+        break;
+      case 1:
+        current_char = '*';
+        break;
+      case 2:
+        current_char = '?';
+        break;
+      case 3:
+        if (i < rx_length - 1)  /* '|' cannot be at the end */
+          current_char = '|';
+        else
+          current_char = get_random_literal ();
+        break;
+      }
+    }
+    else
+    {
+      current_char = get_random_literal ();
+      last_was_op = 0;
+    }
+
+    if (NULL != matching_strp &&
+        (current_char != '+' && current_char != '*' && current_char != '?' &&
+         current_char != '|'))
+    {
+      *matching_strp = current_char;
+      matching_strp++;
+    }
+
+    *rx_p = current_char;
+    rx_p++;
+  }
+  *rx_p = '\0';
+  if (NULL != matching_strp)
+    *matching_strp = '\0';
+
+  return rx;
+}
+
+
+/**
+ * Generate a random string of maximum length 'max_len' that only contains literals allowed
+ * in a regular expression. The string might be 0 chars long but is garantueed
+ * to be shorter or equal to 'max_len'.
+ *
+ * @param max_len maximum length of the string that should be generated.
+ *
+ * @return random string that needs to be freed.
+ */
+char *
+REGEX_TEST_generate_random_string (size_t max_len)
+{
+  unsigned int i;
+  char *str;
+  size_t len;
+
+  if (1 > max_len)
+    return GNUNET_strdup ("");
+
+  len = (size_t) GNUNET_CRYPTO_random_u32 (GNUNET_CRYPTO_QUALITY_WEAK, max_len);
+  str = GNUNET_malloc (len + 1);
+
+  for (i = 0; i < len; i++)
+  {
+    str[i] = get_random_literal ();
+  }
+
+  str[i] = '\0';
+
+  return str;
+}
diff --git a/src/regex/test_regex_api.c b/src/regex/test_regex_api.c
new file mode 100644 (file)
index 0000000..a08e2ed
--- /dev/null
@@ -0,0 +1,127 @@
+/*
+     This file is part of GNUnet.
+     Copyright (C) 2013 GNUnet e.V.
+
+     GNUnet is free software: you can redistribute it and/or modify it
+     under the terms of the GNU Affero General Public License as published
+     by the Free Software Foundation, either version 3 of the License,
+     or (at your option) any later version.
+
+     GNUnet is distributed in the hope that it will be useful, but
+     WITHOUT ANY WARRANTY; without even the implied warranty of
+     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+     Affero General Public License for more details.
+    
+     You should have received a copy of the GNU Affero General Public License
+     along with this program.  If not, see <http://www.gnu.org/licenses/>.
+*/
+/**
+ * @file regex/test_regex_api.c
+ * @brief base test case for regex api (and DHT functions)
+ * @author Christian Grothoff
+ */
+#include "platform.h"
+#include "gnunet_util_lib.h"
+#include "gnunet_testing_lib.h"
+#include "gnunet_regex_service.h"
+
+
+/**
+ * How long until we really give up on a particular testcase portion?
+ */
+#define TOTAL_TIMEOUT GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS, 600)
+
+/**
+ * How long until we give up on any particular operation (and retry)?
+ */
+#define BASE_TIMEOUT GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS, 3)
+
+
+static struct GNUNET_REGEX_Announcement *a;
+
+static struct GNUNET_REGEX_Search *s;
+
+static int ok = 1;
+
+static struct GNUNET_SCHEDULER_Task *die_task;
+
+
+static void
+end (void *cls)
+{
+  die_task = NULL;
+  GNUNET_REGEX_announce_cancel (a);
+  a = NULL;
+  GNUNET_REGEX_search_cancel (s);
+  s = NULL;
+  ok = 0;
+}
+
+
+static void
+end_badly ()
+{
+  die_task = NULL;
+  FPRINTF (stderr, "%s",  "Testcase failed (timeout).\n");
+  GNUNET_REGEX_announce_cancel (a);
+  a = NULL;
+  GNUNET_REGEX_search_cancel (s);
+  s = NULL;
+  ok = 1;
+}
+
+
+/**
+ * Search callback function, invoked for every result that was found.
+ *
+ * @param cls Closure provided in GNUNET_REGEX_search.
+ * @param id Peer providing a regex that matches the string.
+ * @param get_path Path of the get request.
+ * @param get_path_length Lenght of get_path.
+ * @param put_path Path of the put request.
+ * @param put_path_length Length of the put_path.
+ */
+static void
+found_cb (void *cls,
+         const struct GNUNET_PeerIdentity *id,
+         const struct GNUNET_PeerIdentity *get_path,
+         unsigned int get_path_length,
+         const struct GNUNET_PeerIdentity *put_path,
+         unsigned int put_path_length)
+{
+  GNUNET_SCHEDULER_cancel (die_task);
+  die_task =
+    GNUNET_SCHEDULER_add_now (&end, NULL);
+}
+
+
+static void
+run (void *cls,
+     const struct GNUNET_CONFIGURATION_Handle *cfg,
+     struct GNUNET_TESTING_Peer *peer)
+{
+  die_task =
+    GNUNET_SCHEDULER_add_delayed (TOTAL_TIMEOUT,
+                                 &end_badly, NULL);
+  a = GNUNET_REGEX_announce (cfg,
+                            "my long prefix - hello world(0|1)*",
+                            GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS,
+                                                           5),
+                            1);
+  s = GNUNET_REGEX_search (cfg,
+                          "my long prefix - hello world0101",
+                          &found_cb, NULL);
+}
+
+
+int
+main (int argc, char *argv[])
+{
+  if (0 != GNUNET_TESTING_peer_run ("test-regex-api",
+                                   "test_regex_api_data.conf",
+                                   &run, NULL))
+    return 1;
+  return ok;
+}
+
+/* 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 (file)
index 0000000..40fee1e
--- /dev/null
@@ -0,0 +1,39 @@
+@INLINE@ ../../contrib/conf/gnunet/no_forcestart.conf
+@INLINE@ ../../contrib/conf/gnunet/no_autostart_above_core.conf
+
+[PATHS]
+GNUNET_TEST_HOME = $GNUNET_TMP/test-regex-api/
+
+[dhtcache]
+QUOTA = 1 MB
+DATABASE = heap
+
+[topology]
+TARGET-CONNECTION-COUNT = 16
+AUTOCONNECT = YES
+FRIENDS-ONLY = NO
+MINIMUM-FRIENDS = 0
+
+[ats]
+WAN_QUOTA_IN = 1 GB
+WAN_QUOTA_OUT = 1 GB
+
+[dht]
+START_ON_DEMAND = YES
+PORT = 12370
+
+[regex]
+START_ON_DEMAND = YES
+
+[transport]
+plugins = tcp
+NEIGHBOUR_LIMIT = 50
+
+[nat]
+DISABLEV6 = YES
+BINDTO = 127.0.0.1
+ENABLE_UPNP = NO
+BEHIND_NAT = NO
+ALLOW_NAT = NO
+INTERNAL_ADDRESS = 127.0.0.1
+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 (file)
index 0000000..8a0c0d0
--- /dev/null
@@ -0,0 +1,364 @@
+/*
+     This file is part of GNUnet
+     Copyright (C) 2012 GNUnet e.V.
+
+     GNUnet is free software: you can redistribute it and/or modify it
+     under the terms of the GNU Affero General Public License as published
+     by the Free Software Foundation, either version 3 of the License,
+     or (at your option) any later version.
+
+     GNUnet is distributed in the hope that it will be useful, but
+     WITHOUT ANY WARRANTY; without even the implied warranty of
+     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+     Affero General Public License for more details.
+    
+     You should have received a copy of the GNU Affero General Public License
+     along with this program.  If not, see <http://www.gnu.org/licenses/>.
+*/
+/**
+ * @file regex/test_regex_eval_api.c
+ * @brief test for regex.c
+ * @author Maximilian Szengel
+ */
+#include <regex.h>
+#include <time.h>
+#include "platform.h"
+#include "regex_internal_lib.h"
+#include "regex_test_lib.h"
+#include "regex_internal.h"
+
+enum Match_Result
+{
+  match = 0,
+  nomatch = 1
+};
+
+struct Regex_String_Pair
+{
+  char *regex;
+  int string_count;
+  char *strings[20];
+  enum Match_Result expected_results[20];
+};
+
+
+/**
+ * Random regex test. Generate a random regex as well as 'str_count' strings to
+ * match it against. Will match using GNUNET_REGEX implementation and compare
+ * the result to glibc regex result. 'rx_length' has to be smaller then
+ * 'max_str_len'.
+ *
+ * @param rx_length length of the regular expression.
+ * @param max_str_len maximum length of the random strings.
+ * @param str_count number of generated random strings.
+ *
+ * @return 0 on success, non 0 otherwise.
+ */
+int
+test_random (unsigned int rx_length, unsigned int max_str_len,
+             unsigned int str_count)
+{
+  unsigned int i;
+  char *rand_rx;
+  char *matching_str;
+  int eval;
+  int eval_check;
+  int eval_canonical;
+  int eval_canonical_check;
+  struct REGEX_INTERNAL_Automaton *dfa;
+  regex_t rx;
+  regmatch_t matchptr[1];
+  char error[200];
+  int result;
+  char *canonical_regex = NULL;
+
+  /* At least one string is needed for matching */
+  GNUNET_assert (str_count > 0);
+  /* The string should be at least as long as the regex itself */
+  GNUNET_assert (max_str_len >= rx_length);
+
+  /* Generate random regex and a string that matches the regex */
+  matching_str = GNUNET_malloc (rx_length + 1);
+  rand_rx = REGEX_TEST_generate_random_regex (rx_length, matching_str);
+
+  /* Now match */
+  result = 0;
+  for (i = 0; i < str_count; i++)
+  {
+    if (0 < i)
+    {
+      matching_str = REGEX_TEST_generate_random_string (max_str_len);
+    }
+
+    /* Match string using DFA */
+    dfa = REGEX_INTERNAL_construct_dfa (rand_rx, strlen (rand_rx), 0);
+    if (NULL == dfa)
+    {
+      GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Constructing DFA failed\n");
+      goto error;
+    }
+
+    eval = REGEX_INTERNAL_eval (dfa, matching_str);
+    /* save the canonical regex for later comparison */
+    canonical_regex = GNUNET_strdup (REGEX_INTERNAL_get_canonical_regex (dfa));
+    REGEX_INTERNAL_automaton_destroy (dfa);
+
+    /* Match string using glibc regex */
+    if (0 != regcomp (&rx, rand_rx, REG_EXTENDED))
+    {
+      GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+                  "Could not compile regex using regcomp: %s\n", rand_rx);
+      goto error;
+    }
+
+    eval_check = regexec (&rx, matching_str, 1, matchptr, 0);
+    regfree (&rx);
+
+    /* We only want to match the whole string, because that's what our DFA does,
+     * too. */
+    if (eval_check == 0 &&
+        (matchptr[0].rm_so != 0 || matchptr[0].rm_eo != strlen (matching_str)))
+      eval_check = 1;
+
+    /* Match canonical regex */
+    dfa =
+        REGEX_INTERNAL_construct_dfa (canonical_regex, strlen (canonical_regex),
+                                    0);
+    if (NULL == dfa)
+    {
+      GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Constructing DFA failed\n");
+      goto error;
+    }
+
+    eval_canonical = REGEX_INTERNAL_eval (dfa, matching_str);
+    REGEX_INTERNAL_automaton_destroy (dfa);
+
+    if (0 != regcomp (&rx, canonical_regex, REG_EXTENDED))
+    {
+      GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+                  "Could not compile regex using regcomp: %s\n",
+                  canonical_regex);
+      goto error;
+    }
+
+    eval_canonical_check = regexec (&rx, matching_str, 1, matchptr, 0);
+    regfree (&rx);
+
+    /* We only want to match the whole string, because that's what our DFA does,
+     * too. */
+    if (eval_canonical_check == 0 &&
+        (matchptr[0].rm_so != 0 || matchptr[0].rm_eo != strlen (matching_str)))
+      eval_canonical_check = 1;
+
+    /* compare results */
+    if (eval_check != eval || eval_canonical != eval_canonical_check)
+    {
+      regerror (eval_check, &rx, error, sizeof error);
+      GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Unexpected result:\nregex: %s\ncanonical_regex: %s\n\
+                   string: %s\ngnunet regex: %i\nglibc regex: %i\n\
+                   canonical regex: %i\ncanonical regex glibc: %i\n\
+                   glibc error: %s\n\n", rand_rx, canonical_regex, matching_str,
+                  eval, eval_check, eval_canonical, eval_canonical_check, error);
+      result += 1;
+    }
+    GNUNET_free (canonical_regex);
+    GNUNET_free (matching_str);
+    canonical_regex = NULL;
+    matching_str = NULL;
+  }
+
+  GNUNET_free (rand_rx);
+
+  return result;
+
+error:
+  GNUNET_free_non_null (matching_str);
+  GNUNET_free_non_null (rand_rx);
+  GNUNET_free_non_null (canonical_regex);
+  return -1;
+}
+
+/**
+ * Automaton test that compares the result of matching regular expression 'rx'
+ * with the strings and expected results in 'rxstr' with the result of matching
+ * the same strings with glibc regex.
+ *
+ * @param a automaton.
+ * @param rx compiled glibc regex.
+ * @param rxstr regular expression and strings with expected results to
+ *              match against.
+ *
+ * @return 0 on successfull, non 0 otherwise
+ */
+int
+test_automaton (struct REGEX_INTERNAL_Automaton *a, regex_t * rx,
+                struct Regex_String_Pair *rxstr)
+{
+  int result;
+  int eval;
+  int eval_check;
+  char error[200];
+  regmatch_t matchptr[1];
+  int i;
+
+  if (NULL == a)
+  {
+    GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Automaton was NULL\n");
+    return 1;
+  }
+
+  result = 0;
+
+  for (i = 0; i < rxstr->string_count; i++)
+  {
+    eval = REGEX_INTERNAL_eval (a, rxstr->strings[i]);
+    eval_check = regexec (rx, rxstr->strings[i], 1, matchptr, 0);
+
+    /* We only want to match the whole string, because that's what our DFA does,
+     * too. */
+    if (eval_check == 0 &&
+        (matchptr[0].rm_so != 0 ||
+         matchptr[0].rm_eo != strlen (rxstr->strings[i])))
+      eval_check = 1;
+
+    if ((rxstr->expected_results[i] == match && (0 != eval || 0 != eval_check))
+        || (rxstr->expected_results[i] == nomatch &&
+            (0 == eval || 0 == eval_check)))
+    {
+      result = 1;
+      regerror (eval_check, rx, error, sizeof error);
+      GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+                  "Unexpected result:\nregex: %s\ncanonical_regex: %s\n"
+                  "string: %s\nexpected result: %i\n"
+                  "gnunet regex: %i\nglibc regex: %i\nglibc error: %s\n"
+                  "rm_so: %i\nrm_eo: %i\n\n", rxstr->regex,
+                  REGEX_INTERNAL_get_canonical_regex (a), rxstr->strings[i],
+                  rxstr->expected_results[i], eval, eval_check, error,
+                  matchptr[0].rm_so, matchptr[0].rm_eo);
+    }
+  }
+  return result;
+}
+
+int
+main (int argc, char *argv[])
+{
+  GNUNET_log_setup ("test-regex", "WARNING", NULL);
+
+  struct REGEX_INTERNAL_Automaton *a;
+  regex_t rx;
+  int i;
+  int check_nfa;
+  int check_dfa;
+  int check_rand;
+  char *check_proof;
+
+  struct Regex_String_Pair rxstr[19] = {
+    {"ab?(abcd)?", 5,
+     {"ababcd", "abab", "aabcd", "a", "abb"},
+     {match, nomatch, match, match, nomatch}},
+    {"ab(c|d)+c*(a(b|c)d)+", 5,
+     {"abcdcdcdcdddddabd", "abcd",
+      "abcddddddccccccccccccccccccccccccabdacdabd",
+      "abccccca", "abcdcdcdccdabdabd"},
+     {match, nomatch, match, nomatch, match}},
+    {"ab+c*(a(bx|c)d)+", 5,
+     {"abcdcdcdcdddddabd", "abcd",
+      "abcddddddccccccccccccccccccccccccabdacdabd",
+      "abccccca", "abcdcdcdccdabdabd"},
+     {nomatch, nomatch, nomatch, nomatch, nomatch}},
+    {"a+X*y+c|p|R|Z*K*y*R+w|Y*6+n+h*k*w+V*F|W*B*e*", 1,
+     {"kaXycQepRZKyRwY6nhkwVFWBegNVtLPj39XhJJ6bEifRSZRYZg"},
+     {nomatch}},
+    {"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,
+     {"kaXycQepRZKyRwY6nhkwVFWBegNVtLPj39XhJJ6bEifRSZRYZg"},
+     {nomatch}},
+    {"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,
+     {"osfjsodfonONONOnosndfsdnfsd"},
+     {nomatch}},
+    {"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,
+     {"VMoxpdhbEmhYEOWWPoZHMIqCa559bzGykRpu8hBlHeLO1Fv05C"},
+     {nomatch}},
+    {"(bla)*", 8,
+     {"", "bla", "blabla", "bl", "la", "b", "l", "a"},
+     {match, match, match, nomatch, nomatch, nomatch, nomatch, nomatch}},
+    {"ab(c|d)+c*(a(b|c)+d)+(bla)(bla)*", 8,
+     {"ab", "abcabdbla", "abdcccccccccccabcbccdblablabla", "bl", "la", "b",
+      "l",
+      "a"},
+     {nomatch, match, match, nomatch, nomatch, nomatch, nomatch, nomatch}},
+    {"a|aa*a", 6,
+     {"", "a", "aa", "aaa", "aaaa", "aaaaa"},
+     {nomatch, match, match, match, match, match}},
+    {"ab(c|d)+c*(a(b|c)+d)+(bla)+", 1,
+     {"abcabdblaacdbla"},
+     {nomatch}},
+    {"(ac|b)+", 8,
+     {"b", "bb", "ac", "", "acb", "bacbacac", "acacac", "abc"},
+     {match, match, match, nomatch, match, match, match, nomatch}},
+    {"(ab|c)+", 7,
+     {"", "ab", "c", "abc", "ababcc", "acc", "abac"},
+     {nomatch, match, match, match, match, nomatch, nomatch}},
+    {"((j|2j)K|(j|2j)AK|(j|2j)(D|e|(j|2j)A(D|e))D*K)", 1,
+     {"", "2j2jADK", "j2jADK"},
+     {nomatch, match, match}},
+    {"((j|2j)K|(j|2j)(D|e|((j|2j)j|(j|2j)2j)A(D|e))D*K|(j|2j)AK)", 2,
+     {"", "2j2jjADK", "j2jADK"},
+     {nomatch, match, match}},
+    {"ab(c|d)+c*(a(b|c)d)+", 1,
+     {"abacd"},
+     {nomatch}},
+    {"d|5kl", 1,
+     {"d5kl"},
+     {nomatch}},
+    {"a()b", 1,
+     {"ab"},
+     {match}},
+    {"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)*)",
+     2,
+     {"GNVPN-0001-PAD1101111010100101011101010101010101",
+      "GNVPN-0001-PAD11001010100111000101101010101"},
+     {match, match}}
+  };
+
+  check_nfa = 0;
+  check_dfa = 0;
+  check_rand = 0;
+
+  for (i = 0; i < 19; i++)
+  {
+    if (0 != regcomp (&rx, rxstr[i].regex, REG_EXTENDED))
+    {
+      GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+                  "Could not compile regex using regcomp()\n");
+      return 1;
+    }
+
+    /* NFA test */
+    a = REGEX_INTERNAL_construct_nfa (rxstr[i].regex, strlen (rxstr[i].regex));
+    check_nfa += test_automaton (a, &rx, &rxstr[i]);
+    REGEX_INTERNAL_automaton_destroy (a);
+
+    /* DFA test */
+    a = REGEX_INTERNAL_construct_dfa (rxstr[i].regex, strlen (rxstr[i].regex), 0);
+    check_dfa += test_automaton (a, &rx, &rxstr[i]);
+    check_proof = GNUNET_strdup (REGEX_INTERNAL_get_canonical_regex (a));
+    REGEX_INTERNAL_automaton_destroy (a);
+
+    a = REGEX_INTERNAL_construct_dfa (check_proof, strlen (check_proof), 0);
+    check_dfa += test_automaton (a, &rx, &rxstr[i]);
+    REGEX_INTERNAL_automaton_destroy (a);
+    if (0 != check_dfa)
+      GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "check_proof: %s\n", check_proof);
+    GNUNET_free_non_null (check_proof);
+
+    regfree (&rx);
+  }
+
+  /* Random tests */
+  srand (time (NULL));
+  for (i = 0; i < 20; i++)
+    check_rand += test_random (50, 60, 10);
+
+  return check_nfa + check_dfa + check_rand;
+}
diff --git a/src/regex/test_regex_graph_api.c b/src/regex/test_regex_graph_api.c
new file mode 100644 (file)
index 0000000..46eacc1
--- /dev/null
@@ -0,0 +1,156 @@
+/*
+     This file is part of GNUnet
+     Copyright (C) 2012 GNUnet e.V.
+
+     GNUnet is free software: you can redistribute it and/or modify it
+     under the terms of the GNU Affero General Public License as published
+     by the Free Software Foundation, either version 3 of the License,
+     or (at your option) any later version.
+
+     GNUnet is distributed in the hope that it will be useful, but
+     WITHOUT ANY WARRANTY; without even the implied warranty of
+     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+     Affero General Public License for more details.
+    
+     You should have received a copy of the GNU Affero General Public License
+     along with this program.  If not, see <http://www.gnu.org/licenses/>.
+*/
+/**
+ * @file regex/test_regex_graph_api.c
+ * @brief test for regex_graph.c
+ * @author Maximilian Szengel
+ */
+#include <regex.h>
+#include <time.h>
+#include "platform.h"
+#include "regex_internal_lib.h"
+#include "regex_test_lib.h"
+#include "regex_internal.h"
+
+#define KEEP_FILES 1
+
+/**
+ * Check if 'filename' exists and is not empty.
+ *
+ * @param filename name of the file that should be checked
+ *
+ * @return 0 if ok, non 0 on error.
+ */
+static int
+filecheck (const char *filename)
+{
+  int error = 0;
+  FILE *fp;
+
+  /* Check if file was created and delete it again */
+  if (NULL == (fp = fopen (filename, "r")))
+  {
+    GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Could not find graph %s\n", filename);
+    return 1;
+  }
+
+  GNUNET_break (0 == fseek (fp, 0L, SEEK_END));
+  if (1 > ftell (fp))
+  {
+    GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+                "Graph writing failed, got empty file (%s)!\n", filename);
+    error = 2;
+  }
+
+  GNUNET_assert (0 == fclose (fp));
+
+  if (!KEEP_FILES)
+  {
+    if (0 != unlink (filename))
+      GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_ERROR, "unlink", filename);
+  }
+  return error;
+}
+
+
+int
+main (int argc, char *argv[])
+{
+  int error;
+  struct REGEX_INTERNAL_Automaton *a;
+  unsigned int i;
+  const char *filename = "test_graph.dot";
+
+  const char *regex[12] = {
+    "ab(c|d)+c*(a(b|c)+d)+(bla)+",
+    "(bla)*",
+    "b(lab)*la",
+    "(ab)*",
+    "ab(c|d)+c*(a(b|c)+d)+(bla)(bla)*",
+    "z(abc|def)?xyz",
+    "1*0(0|1)*",
+    "a*b*",
+    "a+X*y+c|p|R|Z*K*y*R+w|Y*6+n+h*k*w+V*F|W*B*e*",
+    "a",
+    "a|b",
+    "PADPADPADPADPADPabcdefghixxxxxxxxxxxxxjklmnop*qstoisdjfguisdfguihsdfgbdsuivggsd"
+  };
+
+  GNUNET_log_setup ("test-regex", "WARNING", NULL);
+  error = 0;
+  for (i = 0; i < 12; i++)
+  {
+    /* Check NFA graph creation */
+    a = REGEX_INTERNAL_construct_nfa (regex[i], strlen (regex[i]));
+    REGEX_TEST_automaton_save_graph (a, filename, REGEX_TEST_GRAPH_DEFAULT);
+    REGEX_INTERNAL_automaton_destroy (a);
+    error += filecheck (filename);
+
+    a = REGEX_INTERNAL_construct_nfa (regex[i], strlen (regex[i]));
+    REGEX_TEST_automaton_save_graph (a, filename,
+                                       REGEX_TEST_GRAPH_DEFAULT |
+                                       REGEX_TEST_GRAPH_VERBOSE);
+    REGEX_INTERNAL_automaton_destroy (a);
+    error += filecheck (filename);
+
+    a = REGEX_INTERNAL_construct_nfa (regex[i], strlen (regex[i]));
+    REGEX_TEST_automaton_save_graph (a, filename,
+                                       REGEX_TEST_GRAPH_DEFAULT |
+                                       REGEX_TEST_GRAPH_COLORING);
+    REGEX_INTERNAL_automaton_destroy (a);
+    error += filecheck (filename);
+
+    a = REGEX_INTERNAL_construct_nfa (regex[i], strlen (regex[i]));
+    REGEX_TEST_automaton_save_graph (a, filename,
+                                       REGEX_TEST_GRAPH_DEFAULT |
+                                       REGEX_TEST_GRAPH_VERBOSE |
+                                       REGEX_TEST_GRAPH_COLORING);
+    REGEX_INTERNAL_automaton_destroy (a);
+    error += filecheck (filename);
+
+
+    /* Check DFA graph creation */
+    a = REGEX_INTERNAL_construct_dfa (regex[i], strlen (regex[i]), 0);
+    REGEX_TEST_automaton_save_graph (a, filename, REGEX_TEST_GRAPH_DEFAULT);
+    REGEX_INTERNAL_automaton_destroy (a);
+    error += filecheck (filename);
+
+    a = REGEX_INTERNAL_construct_dfa (regex[i], strlen (regex[i]), 0);
+    REGEX_TEST_automaton_save_graph (a, filename,
+                                       REGEX_TEST_GRAPH_DEFAULT |
+                                       REGEX_TEST_GRAPH_VERBOSE);
+    REGEX_INTERNAL_automaton_destroy (a);
+    error += filecheck (filename);
+
+    a = REGEX_INTERNAL_construct_dfa (regex[i], strlen (regex[i]), 0);
+    REGEX_TEST_automaton_save_graph (a, filename,
+                                       REGEX_TEST_GRAPH_DEFAULT |
+                                       REGEX_TEST_GRAPH_COLORING);
+    REGEX_INTERNAL_automaton_destroy (a);
+    error += filecheck (filename);
+
+
+    a = REGEX_INTERNAL_construct_dfa (regex[i], strlen (regex[i]), 4);
+    REGEX_TEST_automaton_save_graph (a, filename, REGEX_TEST_GRAPH_DEFAULT);
+    REGEX_INTERNAL_automaton_destroy (a);
+    error += filecheck (filename);
+
+  }
+
+  return error;
+}
diff --git a/src/regex/test_regex_integration.c b/src/regex/test_regex_integration.c
new file mode 100644 (file)
index 0000000..9928724
--- /dev/null
@@ -0,0 +1,206 @@
+/*
+     This file is part of GNUnet.
+     Copyright (C) 2013, 2015 GNUnet e.V.
+
+     GNUnet is free software: you can redistribute it and/or modify it
+     under the terms of the GNU Affero General Public License as published
+     by the Free Software Foundation, either version 3 of the License,
+     or (at your option) any later version.
+
+     GNUnet is distributed in the hope that it will be useful, but
+     WITHOUT ANY WARRANTY; without even the implied warranty of
+     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+     Affero General Public License for more details.
+    
+     You should have received a copy of the GNU Affero General Public License
+     along with this program.  If not, see <http://www.gnu.org/licenses/>.
+*/
+/**
+ * @file regex/test_regex_integration.c
+ * @brief base test case for regex integration with VPN;
+ *        tests that the regexes generated by the TUN API
+ *        for IP addresses work (for some simple cases)
+ * @author Christian Grothoff
+ */
+#include "platform.h"
+#include "gnunet_applications.h"
+#include "gnunet_util_lib.h"
+#include "gnunet_tun_lib.h"
+#include "gnunet_testing_lib.h"
+#include "gnunet_regex_service.h"
+
+
+/**
+ * How long until we really give up on a particular testcase portion?
+ */
+#define TOTAL_TIMEOUT GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS, 600)
+
+/**
+ * How long until we give up on any particular operation (and retry)?
+ */
+#define BASE_TIMEOUT GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS, 3)
+
+
+static struct GNUNET_REGEX_Announcement *a4;
+
+static struct GNUNET_REGEX_Search *s4;
+
+static struct GNUNET_REGEX_Announcement *a6;
+
+static struct GNUNET_REGEX_Search *s6;
+
+static int ok = 1;
+
+static struct GNUNET_SCHEDULER_Task *die_task;
+
+
+static void
+end (void *cls)
+{
+  die_task = NULL;
+  GNUNET_REGEX_announce_cancel (a4);
+  a4 = NULL;
+  GNUNET_REGEX_search_cancel (s4);
+  s4 = NULL;
+  GNUNET_REGEX_announce_cancel (a6);
+  a6 = NULL;
+  GNUNET_REGEX_search_cancel (s6);
+  s6 = NULL;
+  ok = 0;
+}
+
+
+static void
+end_badly ()
+{
+  FPRINTF (stderr, "%s",  "Testcase failed (timeout).\n");
+  end (NULL);
+  ok = 1;
+}
+
+
+/**
+ * Search callback function, invoked for every result that was found.
+ *
+ * @param cls Closure provided in #GNUNET_REGEX_search().
+ * @param id Peer providing a regex that matches the string.
+ * @param get_path Path of the get request.
+ * @param get_path_length Length of @a get_path.
+ * @param put_path Path of the put request.
+ * @param put_path_length Length of the @a put_path.
+ */
+static void
+found_cb (void *cls,
+         const struct GNUNET_PeerIdentity *id,
+         const struct GNUNET_PeerIdentity *get_path,
+         unsigned int get_path_length,
+         const struct GNUNET_PeerIdentity *put_path,
+         unsigned int put_path_length)
+{
+  const char *str = cls;
+  static int found;
+
+  GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+              "IPv%s-exit found\n",
+              str);
+  if (0 == strcmp (str, "4"))
+    found |= 4;
+  if (0 == strcmp (str, "6"))
+    found |= 2;
+  if ((4|2) == found)
+  {
+    GNUNET_SCHEDULER_cancel (die_task);
+    die_task =
+    GNUNET_SCHEDULER_add_now (&end, NULL);
+  }
+}
+
+
+static void
+run (void *cls,
+     const struct GNUNET_CONFIGURATION_Handle *cfg,
+     struct GNUNET_TESTING_Peer *peer)
+{
+  char rxstr4[GNUNET_TUN_IPV4_REGEXLEN];
+  char rxstr6[GNUNET_TUN_IPV6_REGEXLEN];
+  char *p4r;
+  char *p6r;
+  char *p4;
+  char *p6;
+  char *ss4;
+  char *ss6;
+  struct in_addr i4;
+  struct in6_addr i6;
+
+  die_task =
+    GNUNET_SCHEDULER_add_delayed (TOTAL_TIMEOUT,
+                                 &end_badly, NULL);
+  GNUNET_assert (1 ==
+                 inet_pton (AF_INET,
+                            "127.0.0.1",
+                            &i4));
+  GNUNET_assert (1 ==
+                 inet_pton (AF_INET6,
+                            "::1:5",
+                            &i6));
+  GNUNET_TUN_ipv4toregexsearch (&i4,
+                                8080,
+                                rxstr4);
+  GNUNET_TUN_ipv6toregexsearch (&i6,
+                                8686,
+                                rxstr6);
+  GNUNET_asprintf (&ss4,
+                   "%s%s",
+                   GNUNET_APPLICATION_TYPE_EXIT_REGEX_PREFIX,
+                   rxstr4);
+  GNUNET_asprintf (&ss6,
+                   "%s%s",
+                   GNUNET_APPLICATION_TYPE_EXIT_REGEX_PREFIX,
+                   rxstr6);
+  p4r = GNUNET_TUN_ipv4policy2regex ("0.0.0.0/0:!25;");
+  p6r = GNUNET_TUN_ipv6policy2regex ("::/0:!25;");
+  GNUNET_asprintf (&p4,
+                   "%s%s",
+                   GNUNET_APPLICATION_TYPE_EXIT_REGEX_PREFIX,
+                   p4r);
+  GNUNET_asprintf (&p6,
+                   "%s%s",
+                   GNUNET_APPLICATION_TYPE_EXIT_REGEX_PREFIX,
+                   p6r);
+  GNUNET_free (p4r);
+  GNUNET_free (p6r);
+  a4 = GNUNET_REGEX_announce (cfg,
+                              p4,
+                              GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS,
+                                                             5),
+                            1);
+  a6 = GNUNET_REGEX_announce (cfg,
+                              p6,
+                              GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS,
+                                                             5),
+                              1);
+  GNUNET_free (p4);
+  GNUNET_free (p6);
+
+  s4 = GNUNET_REGEX_search (cfg,
+                            ss4,
+                            &found_cb, "4");
+  s6 = GNUNET_REGEX_search (cfg,
+                            ss6,
+                            &found_cb, "6");
+  GNUNET_free (ss4);
+  GNUNET_free (ss6);
+}
+
+
+int
+main (int argc, char *argv[])
+{
+  if (0 != GNUNET_TESTING_peer_run ("test-regex-integration",
+                                   "test_regex_api_data.conf",
+                                   &run, NULL))
+    return 1;
+  return ok;
+}
+
+/* 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 (file)
index 0000000..f4cc725
--- /dev/null
@@ -0,0 +1,258 @@
+/*
+     This file is part of GNUnet
+     Copyright (C) 2012 GNUnet e.V.
+
+     GNUnet is free software: you can redistribute it and/or modify it
+     under the terms of the GNU Affero General Public License as published
+     by the Free Software Foundation, either version 3 of the License,
+     or (at your option) any later version.
+
+     GNUnet is distributed in the hope that it will be useful, but
+     WITHOUT ANY WARRANTY; without even the implied warranty of
+     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+     Affero General Public License for more details.
+    
+     You should have received a copy of the GNU Affero General Public License
+     along with this program.  If not, see <http://www.gnu.org/licenses/>.
+*/
+/**
+ * @file regex/test_regex_iterate_api.c
+ * @brief test for regex.c
+ * @author Maximilian Szengel
+ */
+#include <regex.h>
+#include <time.h>
+#include "platform.h"
+#include "regex_internal_lib.h"
+#include "regex_block_lib.h"
+#include "regex_internal.h"
+
+/**
+ * Regex initial padding.
+ */
+#define INITIAL_PADDING "PADPADPADPADPADP"
+
+/**
+ * Set to GNUNET_YES to save a debug graph.
+ */
+#define REGEX_INTERNAL_ITERATE_SAVE_DEBUG_GRAPH GNUNET_NO
+
+static unsigned int transition_counter;
+
+struct IteratorContext
+{
+  int error;
+  int should_save_graph;
+  FILE *graph_filep;
+  unsigned int string_count;
+  char *const *strings;
+  unsigned int match_count;
+};
+
+struct RegexStringPair
+{
+  char *regex;
+  unsigned int string_count;
+  char *strings[20];
+};
+
+
+static void
+key_iterator (void *cls, const struct GNUNET_HashCode *key,
+             const char *proof,
+              int accepting, unsigned int num_edges,
+              const struct REGEX_BLOCK_Edge *edges)
+{
+  unsigned int i;
+  struct IteratorContext *ctx = cls;
+  char *out_str;
+  char *state_id = GNUNET_strdup (GNUNET_h2s (key));
+
+  GNUNET_assert (NULL != proof);
+  if (GNUNET_YES == ctx->should_save_graph)
+  {
+    if (GNUNET_YES == accepting)
+      GNUNET_asprintf (&out_str, "\"%s\" [shape=doublecircle]\n", state_id);
+    else
+      GNUNET_asprintf (&out_str, "\"%s\" [shape=circle]\n", state_id);
+    fwrite (out_str, strlen (out_str), 1, ctx->graph_filep);
+    GNUNET_free (out_str);
+
+    for (i = 0; i < num_edges; i++)
+    {
+      transition_counter++;
+      GNUNET_asprintf (&out_str, "\"%s\" -> \"%s\" [label = \"%s (%s)\"]\n",
+                       state_id, GNUNET_h2s (&edges[i].destination),
+                       edges[i].label, proof);
+      fwrite (out_str, strlen (out_str), 1, ctx->graph_filep);
+
+      GNUNET_free (out_str);
+    }
+  }
+  else
+  {
+    for (i = 0; i < num_edges; i++)
+      transition_counter++;
+  }
+
+  for (i = 0; i < ctx->string_count; i++)
+  {
+    if (0 == strcmp (proof, ctx->strings[i]))
+      ctx->match_count++;
+  }
+
+  if (GNUNET_OK != REGEX_BLOCK_check_proof (proof, strlen (proof), key))
+  {
+    ctx->error++;
+    GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+                "Proof check failed: proof: %s key: %s\n", proof, state_id);
+  }
+  GNUNET_free (state_id);
+}
+
+
+int
+main (int argc, char *argv[])
+{
+  GNUNET_log_setup ("test-regex", "WARNING", NULL);
+
+  int error;
+  struct REGEX_INTERNAL_Automaton *dfa;
+  unsigned int i;
+  unsigned int num_transitions;
+  char *filename = NULL;
+  struct IteratorContext ctx = { 0, 0, NULL, 0, NULL, 0 };
+
+  error = 0;
+
+  const struct RegexStringPair rxstr[13] = {
+    {INITIAL_PADDING "ab(c|d)+c*(a(b|c)+d)+(bla)+", 2,
+     {INITIAL_PADDING "abcdcdca", INITIAL_PADDING "abcabdbl"}},
+    {INITIAL_PADDING
+     "abcdefghixxxxxxxxxxxxxjklmnop*qstoisdjfguisdfguihsdfgbdsuivggsd", 1,
+     {INITIAL_PADDING "abcdefgh"}},
+    {INITIAL_PADDING "VPN-4-1(0|1)*", 2,
+     {INITIAL_PADDING "VPN-4-10", INITIAL_PADDING "VPN-4-11"}},
+    {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,
+     {INITIAL_PADDING "aaaaaaaa", INITIAL_PADDING "aaXXyyyc"}},
+    {INITIAL_PADDING "a*", 1, {INITIAL_PADDING "aaaaaaaa"}},
+    {INITIAL_PADDING "xzxzxzxzxz", 1, {INITIAL_PADDING "xzxzxzxz"}},
+    {INITIAL_PADDING "xyz*", 1, {INITIAL_PADDING "xyzzzzzz"}},
+    {INITIAL_PADDING
+     "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)",
+     2, {INITIAL_PADDING "abcd:000", INITIAL_PADDING "abcd:101"}},
+    {INITIAL_PADDING "(x*|(0|1|2)(a|b|c|d)+)", 2,
+     {INITIAL_PADDING "xxxxxxxx", INITIAL_PADDING "0abcdbad"}},
+    {INITIAL_PADDING "(0|1)(0|1)23456789ABC", 1, {INITIAL_PADDING "11234567"}},
+    {INITIAL_PADDING "0*123456789ABC*", 3,
+     {INITIAL_PADDING "00123456", INITIAL_PADDING "00000000",
+      INITIAL_PADDING "12345678"}},
+    {INITIAL_PADDING "0123456789A*BC", 1, {INITIAL_PADDING "01234567"}},
+    {"GNUNETVPN000100000IPEX6-fc5a:4e1:c2ba::1", 1, {"GNUNETVPN000100000IPEX6-"}}
+  };
+
+  const char *graph_start_str = "digraph G {\nrankdir=LR\n";
+  const char *graph_end_str = "\n}\n";
+
+  for (i = 0; i < 13; i++)
+  {
+    GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Iterating DFA for regex %s\n",
+                rxstr[i].regex);
+
+
+    /* Create graph */
+    if (GNUNET_YES == REGEX_INTERNAL_ITERATE_SAVE_DEBUG_GRAPH)
+    {
+      GNUNET_asprintf (&filename, "iteration_graph_%u.dot", i);
+      ctx.graph_filep = fopen (filename, "w");
+      if (NULL == ctx.graph_filep)
+      {
+        GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
+                    "Could not open file %s for saving iteration graph.\n",
+                    filename);
+        ctx.should_save_graph = GNUNET_NO;
+      }
+      else
+      {
+        ctx.should_save_graph = GNUNET_YES;
+        fwrite (graph_start_str, strlen (graph_start_str), 1, ctx.graph_filep);
+      }
+      GNUNET_free (filename);
+    }
+    else
+    {
+      ctx.should_save_graph = GNUNET_NO;
+      ctx.graph_filep = NULL;
+    }
+
+    /* Iterate over DFA edges */
+    transition_counter = 0;
+    ctx.string_count = rxstr[i].string_count;
+    ctx.strings = rxstr[i].strings;
+    ctx.match_count = 0;
+    dfa =
+        REGEX_INTERNAL_construct_dfa (rxstr[i].regex, strlen (rxstr[i].regex), 0);
+    REGEX_INTERNAL_iterate_all_edges (dfa, key_iterator, &ctx);
+    num_transitions =
+        REGEX_INTERNAL_get_transition_count (dfa) - dfa->start->transition_count;
+
+    if (transition_counter < num_transitions)
+    {
+      GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+                  "Automaton has %d transitions, iterated over %d transitions\n",
+                  num_transitions, transition_counter);
+      error += 1;
+    }
+
+    if (ctx.match_count < ctx.string_count)
+    {
+      GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+                  "Missing initial states for regex %s\n", rxstr[i].regex);
+      error += (ctx.string_count - ctx.match_count);
+    }
+    else if (ctx.match_count > ctx.string_count)
+    {
+      GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+                  "Duplicate initial transitions for regex %s\n",
+                  rxstr[i].regex);
+      error += (ctx.string_count - ctx.match_count);
+    }
+
+    REGEX_INTERNAL_automaton_destroy (dfa);
+
+    /* Finish graph */
+    if (GNUNET_YES == ctx.should_save_graph)
+    {
+      fwrite (graph_end_str, strlen (graph_end_str), 1, ctx.graph_filep);
+      fclose (ctx.graph_filep);
+      ctx.graph_filep = NULL;
+      ctx.should_save_graph = GNUNET_NO;
+    }
+  }
+
+
+  for (i = 0; i < 13; i++)
+  {
+    ctx.string_count = rxstr[i].string_count;
+    ctx.strings = rxstr[i].strings;
+    ctx.match_count = 0;
+
+    dfa =
+        REGEX_INTERNAL_construct_dfa (rxstr[i].regex, strlen (rxstr[i].regex), 0);
+    REGEX_INTERNAL_dfa_add_multi_strides (NULL, dfa, 2);
+    REGEX_INTERNAL_iterate_all_edges (dfa, key_iterator, &ctx);
+
+    if (ctx.match_count < ctx.string_count)
+    {
+      GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+                  "Missing initial states for regex %s\n", rxstr[i].regex);
+      error += (ctx.string_count - ctx.match_count);
+    }
+
+    REGEX_INTERNAL_automaton_destroy (dfa);
+  }
+
+  error += ctx.error;
+
+  return error;
+}
diff --git a/src/regex/test_regex_proofs.c b/src/regex/test_regex_proofs.c
new file mode 100644 (file)
index 0000000..72e02fa
--- /dev/null
@@ -0,0 +1,170 @@
+/*
+     This file is part of GNUnet
+     Copyright (C) 2012 GNUnet e.V.
+
+     GNUnet is free software: you can redistribute it and/or modify it
+     under the terms of the GNU Affero General Public License as published
+     by the Free Software Foundation, either version 3 of the License,
+     or (at your option) any later version.
+
+     GNUnet is distributed in the hope that it will be useful, but
+     WITHOUT ANY WARRANTY; without even the implied warranty of
+     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+     Affero General Public License for more details.
+    
+     You should have received a copy of the GNU Affero General Public License
+     along with this program.  If not, see <http://www.gnu.org/licenses/>.
+*/
+/**
+ * @file regex/test_regex_proofs.c
+ * @brief test for regex.c
+ * @author Maximilian Szengel
+ */
+#include "platform.h"
+#include "regex_internal_lib.h"
+#include "regex_test_lib.h"
+#include "regex_internal.h"
+
+
+/**
+ * Test if the given regex's canonical regex is the same as this canonical
+ * regex's canonical regex. Confused? Ok, then: 1. construct a dfa A from the
+ * given 'regex' 2. get the canonical regex of dfa A 3. construct a dfa B from
+ * this canonical regex 3. compare the canonical regex of dfa A with the
+ * canonical regex of dfa B.
+ *
+ * @param regex regular expression used for this test (see above).
+ *
+ * @return 0 on success, 1 on failure
+ */
+static unsigned int
+test_proof (const char *regex)
+{
+  unsigned int error;
+  struct REGEX_INTERNAL_Automaton *dfa;
+  char *c_rx1;
+  const char *c_rx2;
+
+  dfa = REGEX_INTERNAL_construct_dfa (regex, strlen (regex), 1);
+  GNUNET_assert (NULL != dfa);
+  c_rx1 = GNUNET_strdup (REGEX_INTERNAL_get_canonical_regex (dfa));
+  REGEX_INTERNAL_automaton_destroy (dfa);
+  dfa = REGEX_INTERNAL_construct_dfa (c_rx1, strlen (c_rx1), 1);
+  GNUNET_assert (NULL != dfa);
+  c_rx2 = REGEX_INTERNAL_get_canonical_regex (dfa);
+
+  error = (0 == strcmp (c_rx1, c_rx2)) ? 0 : 1;
+
+  if (error > 0)
+  {
+    GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+                "Comparing canonical regex of\n%s\nfailed:\n%s\nvs.\n%s\n",
+                regex, c_rx1, c_rx2);
+  }
+
+  GNUNET_free (c_rx1);
+  REGEX_INTERNAL_automaton_destroy (dfa);
+
+  return error;
+}
+
+
+/**
+ * Use 'test_proof' function to randomly test the canonical regexes of 'count'
+ * random expressions of length 'rx_length'.
+ *
+ * @param count number of random regular expressions to test.
+ * @param rx_length length of the random regular expressions.
+ *
+ * @return 0 on succes, number of failures otherwise.
+ */
+static unsigned int
+test_proofs_random (unsigned int count, size_t rx_length)
+{
+  unsigned int i;
+  char *rand_rx;
+  unsigned int failures;
+
+  failures = 0;
+
+  for (i = 0; i < count; i++)
+  {
+    rand_rx = REGEX_TEST_generate_random_regex (rx_length, NULL);
+    failures += test_proof (rand_rx);
+    GNUNET_free (rand_rx);
+  }
+
+  return failures;
+}
+
+
+/**
+ * Test a number of known examples of regexes for proper canonicalization.
+ *
+ * @return 0 on success, number of failures otherwise.
+ */
+static unsigned int
+test_proofs_static ()
+{
+  unsigned int i;
+  unsigned int error;
+
+  const char *regex[8] = {
+    "a|aa*a",
+    "a+",
+    "a*",
+    "a*a*",
+    "(F*C|WfPf|y+F*C)",
+    "y*F*C|WfPf",
+    "((a|b)c|(a|b)(d|(a|b)e))",
+    "((a|b)(c|d)|(a|b)(a|b)e)"
+  };
+
+  const char *canon_rx1;
+  const char *canon_rx2;
+  struct REGEX_INTERNAL_Automaton *dfa1;
+  struct REGEX_INTERNAL_Automaton *dfa2;
+
+  error = 0;
+
+  for (i = 0; i < 8; i += 2)
+  {
+    dfa1 = REGEX_INTERNAL_construct_dfa (regex[i], strlen (regex[i]), 1);
+    dfa2 = REGEX_INTERNAL_construct_dfa (regex[i + 1], strlen (regex[i + 1]), 1);
+    GNUNET_assert (NULL != dfa1);
+    GNUNET_assert (NULL != dfa2);
+
+    canon_rx1 = REGEX_INTERNAL_get_canonical_regex (dfa1);
+    canon_rx2 = REGEX_INTERNAL_get_canonical_regex (dfa2);
+
+    error += (0 == strcmp (canon_rx1, canon_rx2)) ? 0 : 1;
+
+    if (error > 0)
+    {
+      GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+                  "Comparing canonical regex failed:\nrx1:\t%s\ncrx1:\t%s\nrx2:\t%s\ncrx2:\t%s\n",
+                  regex[i], canon_rx1, regex[i + 1], canon_rx2);
+    }
+
+    REGEX_INTERNAL_automaton_destroy (dfa1);
+    REGEX_INTERNAL_automaton_destroy (dfa2);
+  }
+
+  return error;
+}
+
+
+int
+main (int argc, char *argv[])
+{
+  GNUNET_log_setup ("test-regex", "WARNING", NULL);
+
+  int error;
+
+  error = 0;
+
+  error += test_proofs_static ();
+  error += test_proofs_random (100, 30);
+
+  return error;
+}
diff --git a/src/tun/.gitignore b/src/tun/.gitignore
new file mode 100644 (file)
index 0000000..b266855
--- /dev/null
@@ -0,0 +1,2 @@
+test_regex
+test_tun
diff --git a/src/tun/Makefile.am b/src/tun/Makefile.am
new file mode 100644 (file)
index 0000000..c741f56
--- /dev/null
@@ -0,0 +1,46 @@
+# This Makefile.am is in the public domain
+AM_CPPFLAGS = -I$(top_srcdir)/src/include
+
+if MINGW
+  WINFLAGS = -Wl,--no-undefined -Wl,--export-all-symbols
+endif
+
+if USE_COVERAGE
+  AM_CFLAGS = --coverage -O0
+  XLIB = -lgcov
+endif
+
+lib_LTLIBRARIES = libgnunettun.la
+
+libgnunettun_la_SOURCES = \
+  tun.c \
+  regex.c
+libgnunettun_la_LIBADD = \
+ $(top_builddir)/src/util/libgnunetutil.la $(XLIB) \
+ $(LTLIBINTL)
+libgnunettun_la_LDFLAGS = \
+  $(GN_LIB_LDFLAGS) \
+  -version-info 1:0:1
+
+
+check_PROGRAMS = \
+ test_tun \
+ test_regex
+
+if ENABLE_TEST_RUN
+AM_TESTS_ENVIRONMENT=export GNUNET_PREFIX=$${GNUNET_PREFIX:-@libdir@};export PATH=$${GNUNET_PREFIX:-@prefix@}/bin:$$PATH;unset XDG_DATA_HOME;unset XDG_CONFIG_HOME;
+TESTS = $(check_PROGRAMS)
+endif
+
+test_tun_SOURCES = \
+ test_tun.c
+test_tun_LDADD = \
+ $(top_builddir)/src/util/libgnunetutil.la \
+ libgnunettun.la
+
+
+test_regex_SOURCES = \
+ test_regex.c
+test_regex_LDADD = \
+ $(top_builddir)/src/util/libgnunetutil.la \
+ libgnunettun.la
diff --git a/src/tun/regex.c b/src/tun/regex.c
new file mode 100644 (file)
index 0000000..7565a9e
--- /dev/null
@@ -0,0 +1,834 @@
+/*
+     This file is part of GNUnet
+     Copyright (C) 2012, 2013, 2015 GNUnet e.V.
+
+     GNUnet is free software: you can redistribute it and/or modify it
+     under the terms of the GNU Affero General Public License as published
+     by the Free Software Foundation, either version 3 of the License,
+     or (at your option) any later version.
+
+     GNUnet is distributed in the hope that it will be useful, but
+     WITHOUT ANY WARRANTY; without even the implied warranty of
+     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+     Affero General Public License for more details.
+    
+     You should have received a copy of the GNU Affero General Public License
+     along with this program.  If not, see <http://www.gnu.org/licenses/>.
+*/
+/**
+ * @file src/tun/regex.c
+ * @brief functions to convert IP networks to regexes
+ * @author Maximilian Szengel
+ * @author Christian Grothoff
+ */
+#include "platform.h"
+#include "gnunet_util_lib.h"
+#include "gnunet_tun_lib.h"
+
+/**
+ * 'wildcard', matches all possible values (for HEX encoding).
+ */
+#define DOT "(0|1|2|3|4|5|6|7|8|9|A|B|C|D|E|F)"
+
+
+/**
+ * Create a regex in @a rxstr from the given @a ip and @a netmask.
+ *
+ * @param ip IPv4 representation.
+ * @param port destination port
+ * @param rxstr generated regex, must be at least #GNUNET_TUN_IPV4_REGEXLEN
+ *              bytes long.
+ */
+void
+GNUNET_TUN_ipv4toregexsearch (const struct in_addr *ip,
+                              uint16_t port,
+                              char *rxstr)
+{
+  GNUNET_snprintf (rxstr,
+                   GNUNET_TUN_IPV4_REGEXLEN,
+                   "4-%04X-%08X",
+                   (unsigned int) port,
+                   ntohl (ip->s_addr));
+}
+
+
+/**
+ * Create a regex in @a rxstr from the given @a ipv6 and @a prefixlen.
+ *
+ * @param ipv6 IPv6 representation.
+ * @param port destination port
+ * @param rxstr generated regex, must be at least #GNUNET_TUN_IPV6_REGEXLEN
+ *              bytes long.
+ */
+void
+GNUNET_TUN_ipv6toregexsearch (const struct in6_addr *ipv6,
+                              uint16_t port,
+                              char *rxstr)
+{
+  const uint32_t *addr;
+
+  addr = (const uint32_t *) ipv6;
+  GNUNET_snprintf (rxstr,
+                   GNUNET_TUN_IPV6_REGEXLEN,
+                   "6-%04X-%08X%08X%08X%08X",
+                   (unsigned int) port,
+                   ntohl (addr[0]),
+                   ntohl (addr[1]),
+                   ntohl (addr[2]),
+                   ntohl (addr[3]));
+}
+
+
+/**
+ * Convert the given 4-bit (!) number to a regex.
+ *
+ * @param value the value, only the lowest 4 bits will be looked at
+ * @param mask which bits in value are wildcards (any value)?
+ */
+static char *
+nibble_to_regex (uint8_t value,
+                 uint8_t mask)
+{
+  char *ret;
+
+  value &= mask;
+  switch (mask)
+  {
+  case 0:
+    return GNUNET_strdup (DOT);
+  case 8:
+    GNUNET_asprintf (&ret,
+                     "(%X|%X|%X|%X|%X|%X|%X|%X)",
+                     value,
+                     value + 1,
+                     value + 2,
+                     value + 3,
+                     value + 4,
+                     value + 5,
+                     value + 6,
+                     value + 7);
+    return ret;
+  case 12:
+    GNUNET_asprintf (&ret,
+                     "(%X|%X|%X|%X)",
+                     value,
+                     value + 1,
+                     value + 2,
+                     value + 3);
+    return ret;
+  case 14:
+    GNUNET_asprintf (&ret,
+                     "(%X|%X)",
+                     value,
+                     value + 1);
+    return ret;
+  case 15:
+    GNUNET_asprintf (&ret,
+                     "%X",
+                     value);
+    return ret;
+  default:
+    GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
+                _("Bad mask: %d\n"),
+                mask);
+    GNUNET_break (0);
+    return NULL;
+  }
+}
+
+
+/**
+ * Convert the given 16-bit number to a regex.
+ *
+ * @param value the value
+ * @param mask which bits in value are wildcards (any value)?
+ */
+static char *
+num_to_regex (uint16_t value,
+              uint16_t mask)
+{
+  const uint8_t *v = (const uint8_t *) &value;
+  const uint8_t *m = (const uint8_t *) &mask;
+  char *a;
+  char *b;
+  char *c;
+  char *d;
+  char *ret;
+
+  a = nibble_to_regex (v[0] >> 4, m[0] >> 4);
+  b = nibble_to_regex (v[0] & 15, m[0] & 15);
+  c = nibble_to_regex (v[1] >> 4, m[1] >> 4);
+  d = nibble_to_regex (v[1] & 15, m[1] & 15);
+  ret = NULL;
+  if ( (NULL != a) &&
+       (NULL != b) &&
+       (NULL != c) &&
+       (NULL != d) )
+    GNUNET_asprintf (&ret,
+                     "%s%s%s%s",
+                     a, b, c, d);
+  GNUNET_free_non_null (a);
+  GNUNET_free_non_null (b);
+  GNUNET_free_non_null (c);
+  GNUNET_free_non_null (d);
+  return ret;
+}
+
+
+/**
+ * Do we need to put parents around the given argument?
+ *
+ * @param arg part of a regular expression
+ * @return #GNUNET_YES if we should parens,
+ *         #GNUNET_NO if not
+ */
+static int
+needs_parens (const char *arg)
+{
+  size_t off;
+  size_t len;
+  unsigned int op;
+
+  op = 0;
+  len = strlen (arg);
+  for (off=0;off<len;off++)
+  {
+    switch (arg[off])
+    {
+    case '(':
+      op++;
+      break;
+    case ')':
+      GNUNET_assert (op > 0);
+      op--;
+      break;
+    case '|':
+      if (0 == op)
+        return GNUNET_YES;
+      break;
+    default:
+      break;
+    }
+  }
+  return GNUNET_NO;
+}
+
+
+/**
+ * Compute port policy for the given range of
+ * port numbers.
+ *
+ * @param start starting offset
+ * @param end end offset
+ * @param step increment level (power of 16)
+ * @param pp port policy to convert
+ * @return corresponding regex
+ */
+static char *
+compute_policy (unsigned int start,
+                unsigned int end,
+                unsigned int step,
+                const struct GNUNET_STRINGS_PortPolicy *pp)
+{
+  unsigned int i;
+  char before[36]; /* 16 * 2 + 3 dots + 0-terminator */
+  char middlel[33]; /* 16 * 2 + 0-terminator */
+  char middleh[33]; /* 16 * 2 + 0-terminator */
+  char after[36]; /* 16 * 2 + 3 dots + 0-terminator */
+  char beforep[36+2]; /* 16 * 2 + 3 dots + 0-terminator + ()*/
+  char middlehp[33+2]; /* 16 * 2 + 0-terminator + () */
+  char middlelp[33+2]; /* 16 * 2 + 0-terminator + () */
+  char afterp[36+2]; /* 16 * 2 + 3 dots + 0-terminator + () */
+  char dots[5 * strlen (DOT)];
+  char buf[3];
+  char *middle;
+  char *ret;
+  unsigned int xstep;
+  char *recl;
+  char *rech;
+  char *reclp;
+  char *rechp;
+  unsigned int start_port;
+  unsigned int end_port;
+
+  GNUNET_assert (GNUNET_YES == pp->negate_portrange);
+  start_port = pp->start_port;
+  if (1 == start_port)
+    start_port = 0;
+  end_port = pp->end_port;
+  GNUNET_assert ((end - start) / step <= 0xF);
+  before[0] = '\0';
+  middlel[0] = '\0';
+  middleh[0] = '\0';
+  after[0] = '\0';
+  for (i=start;i<=end;i+=step)
+  {
+    GNUNET_snprintf (buf,
+                     sizeof (buf),
+                     "%X|",
+                     (i - start) / step);
+    if (i / step < start_port / step)
+      strcat (before, buf);
+    else if (i / step > end_port / step)
+      strcat (after, buf);
+    else if (i / step == start_port / step)
+      strcat (middlel, buf);
+    else if (i / step == end_port / step)
+      strcat (middleh, buf);
+  }
+  if (strlen (before) > 0)
+    before[strlen (before)-1] = '\0';
+  if (strlen (middlel) > 0)
+    middlel[strlen (middlel)-1] = '\0';
+  if (strlen (middleh) > 0)
+    middleh[strlen (middleh)-1] = '\0';
+  if (strlen (after) > 0)
+    after[strlen (after)-1] = '\0';
+  if (needs_parens (before))
+    GNUNET_snprintf (beforep,
+                     sizeof (beforep),
+                     "(%s)",
+                     before);
+  else
+    strcpy (beforep, before);
+  if (needs_parens (middlel))
+    GNUNET_snprintf (middlelp,
+                     sizeof (middlelp),
+                     "(%s)",
+                     middlel);
+  else
+    strcpy (middlelp, middlel);
+  if (needs_parens (middleh))
+    GNUNET_snprintf (middlehp,
+                     sizeof (middlehp),
+                     "(%s)",
+                     middleh);
+  else
+    strcpy (middlehp, middleh);
+  if (needs_parens (after))
+    GNUNET_snprintf (afterp,
+                     sizeof (afterp),
+                     "(%s)",
+                     after);
+  else
+    strcpy (afterp, after);
+  dots[0] = '\0';
+  for (xstep=step/16;xstep>0;xstep/=16)
+    strcat (dots, DOT);
+  if (step >= 16)
+  {
+    if (strlen (middlel) > 0)
+      recl = compute_policy ((start_port / step) * step,
+                             (start_port / step) * step + step - 1,
+                             step / 16,
+                             pp);
+    else
+      recl = GNUNET_strdup ("");
+    if (strlen (middleh) > 0)
+      rech = compute_policy ((end_port / step) * step,
+                             (end_port / step) * step + step - 1,
+                             step / 16,
+                             pp);
+    else
+      rech = GNUNET_strdup ("");
+  }
+  else
+  {
+    recl = GNUNET_strdup ("");
+    rech = GNUNET_strdup ("");
+    middlel[0] = '\0';
+    middlelp[0] = '\0';
+    middleh[0] = '\0';
+    middlehp[0] = '\0';
+  }
+  if (needs_parens (recl))
+    GNUNET_asprintf (&reclp,
+                     "(%s)",
+                     recl);
+  else
+    reclp = GNUNET_strdup (recl);
+  if (needs_parens (rech))
+    GNUNET_asprintf (&rechp,
+                     "(%s)",
+                     rech);
+  else
+    rechp = GNUNET_strdup (rech);
+
+  if ( (strlen (middleh) > 0) &&
+       (strlen (rech) > 0) &&
+       (strlen (middlel) > 0) &&
+       (strlen (recl) > 0) )
+  {
+    GNUNET_asprintf (&middle,
+                     "%s%s|%s%s",
+                     middlel,
+                     reclp,
+                     middleh,
+                     rechp);
+  }
+  else if ( (strlen (middleh) > 0) &&
+            (strlen (rech) > 0) )
+  {
+    GNUNET_asprintf (&middle,
+                     "%s%s",
+                     middleh,
+                     rechp);
+  }
+  else if ( (strlen (middlel) > 0) &&
+            (strlen (recl) > 0) )
+  {
+    GNUNET_asprintf (&middle,
+                     "%s%s",
+                     middlel,
+                     reclp);
+  }
+  else
+  {
+    middle = GNUNET_strdup ("");
+  }
+  if ( (strlen(before) > 0) &&
+       (strlen(after) > 0) )
+  {
+    if (strlen (dots) > 0)
+    {
+      if (strlen (middle) > 0)
+        GNUNET_asprintf (&ret,
+                         "(%s%s|%s|%s%s)",
+                         beforep, dots,
+                         middle,
+                         afterp, dots);
+      else
+        GNUNET_asprintf (&ret,
+                         "(%s|%s)%s",
+                         beforep,
+                         afterp,
+                         dots);
+    }
+    else
+    {
+      if (strlen (middle) > 0)
+        GNUNET_asprintf (&ret,
+                         "(%s|%s|%s)",
+                         before,
+                         middle,
+                         after);
+      else if (1 == step)
+        GNUNET_asprintf (&ret,
+                         "%s|%s",
+                         before,
+                         after);
+      else
+        GNUNET_asprintf (&ret,
+                         "(%s|%s)",
+                         before,
+                         after);
+    }
+  }
+  else if (strlen (before) > 0)
+  {
+    if (strlen (dots) > 0)
+    {
+      if (strlen (middle) > 0)
+        GNUNET_asprintf (&ret,
+                         "(%s%s|%s)",
+                         beforep, dots,
+                         middle);
+      else
+        GNUNET_asprintf (&ret,
+                         "%s%s",
+                         beforep, dots);
+    }
+    else
+    {
+      if (strlen (middle) > 0)
+        GNUNET_asprintf (&ret,
+                         "(%s|%s)",
+                         before,
+                         middle);
+      else
+        GNUNET_asprintf (&ret,
+                         "%s",
+                         before);
+    }
+  }
+  else if (strlen (after) > 0)
+  {
+    if (strlen (dots) > 0)
+    {
+      if (strlen (middle) > 0)
+        GNUNET_asprintf (&ret,
+                         "(%s|%s%s)",
+                         middle,
+                         afterp, dots);
+      else
+        GNUNET_asprintf (&ret,
+                         "%s%s",
+                         afterp, dots);
+    }
+    else
+    {
+      if (strlen (middle) > 0)
+        GNUNET_asprintf (&ret,
+                         "%s|%s",
+                         middle,
+                         after);
+      else
+        GNUNET_asprintf (&ret,
+                         "%s",
+                         after);
+    }
+  }
+  else if (strlen (middle) > 0)
+  {
+    GNUNET_asprintf (&ret,
+                     "%s",
+                     middle);
+  }
+  else
+  {
+    ret = GNUNET_strdup ("");
+  }
+  GNUNET_free (middle);
+  GNUNET_free (reclp);
+  GNUNET_free (rechp);
+  GNUNET_free (recl);
+  GNUNET_free (rech);
+  return ret;
+}
+
+
+/**
+ * Convert a port policy to a regular expression.  Note: this is a
+ * very simplistic implementation, we might want to consider doing
+ * something more sophisiticated (resulting in smaller regular
+ * expressions) at a later time.
+ *
+ * @param pp port policy to convert
+ * @return NULL on error
+ */
+static char *
+port_to_regex (const struct GNUNET_STRINGS_PortPolicy *pp)
+{
+  char *reg;
+  char *ret;
+  char *pos;
+  unsigned int i;
+  unsigned int cnt;
+
+  if ( (0 == pp->start_port) ||
+       ( (1 == pp->start_port) &&
+         (0xFFFF == pp->end_port) &&
+         (GNUNET_NO == pp->negate_portrange)) )
+    return GNUNET_strdup (DOT DOT DOT DOT);
+  if ( (pp->start_port == pp->end_port) &&
+       (GNUNET_NO == pp->negate_portrange))
+  {
+    GNUNET_asprintf (&ret,
+                     "%04X",
+                     pp->start_port);
+    return ret;
+  }
+  if (pp->end_port < pp->start_port)
+    return NULL;
+
+  if (GNUNET_YES == pp->negate_portrange)
+  {
+    ret = compute_policy (0, 0xFFFF, 0x1000, pp);
+  }
+  else
+  {
+    cnt = pp->end_port - pp->start_port + 1;
+    reg = GNUNET_malloc (cnt * 5 + 1);
+    pos = reg;
+    for (i=1;i<=0xFFFF;i++)
+    {
+      if ( (i >= pp->start_port) && (i <= pp->end_port) )
+      {
+        if (pos == reg)
+        {
+          GNUNET_snprintf (pos,
+                           5,
+                           "%04X",
+                           i);
+        }
+        else
+        {
+          GNUNET_snprintf (pos,
+                           6,
+                           "|%04X",
+                           i);
+        }
+        pos += strlen (pos);
+      }
+    }
+    GNUNET_asprintf (&ret,
+                     "(%s)",
+                     reg);
+    GNUNET_free (reg);
+  }
+  return ret;
+}
+
+
+/**
+ * Convert an address (IPv4 or IPv6) to a regex.
+ *
+ * @param addr address
+ * @param mask network mask
+ * @param len number of bytes in @a addr and @a mask
+ * @return NULL on error, otherwise regex for the address
+ */
+static char *
+address_to_regex (const void *addr,
+                  const void *mask,
+                  size_t len)
+{
+  const uint16_t *a = addr;
+  const uint16_t *m = mask;
+  char *ret;
+  char *tmp;
+  char *reg;
+  unsigned int i;
+
+  ret = NULL;
+  GNUNET_assert (1 != (len % 2));
+  for (i=0;i<len / 2;i++)
+  {
+    reg = num_to_regex (a[i], m[i]);
+    if (NULL == reg)
+    {
+      GNUNET_free_non_null (ret);
+      return NULL;
+    }
+    if (NULL == ret)
+    {
+      ret = reg;
+    }
+    else
+    {
+      GNUNET_asprintf (&tmp,
+                       "%s%s",
+                       ret, reg);
+      GNUNET_free (ret);
+      GNUNET_free (reg);
+      ret = tmp;
+    }
+  }
+  return ret;
+}
+
+
+/**
+ * Convert a single line of an IPv4 policy to a regular expression.
+ *
+ * @param v4 line to convert
+ * @return NULL on error
+ */
+static char *
+ipv4_to_regex (const struct GNUNET_STRINGS_IPv4NetworkPolicy *v4)
+{
+  char *reg;
+  char *pp;
+  char *ret;
+
+  reg = address_to_regex (&v4->network,
+                          &v4->netmask,
+                          sizeof (struct in_addr));
+  if (NULL == reg)
+    return NULL;
+  pp = port_to_regex (&v4->pp);
+  if (NULL == pp)
+  {
+    GNUNET_free (reg);
+    return NULL;
+  }
+  GNUNET_asprintf (&ret,
+                   "4-%s-%s",
+                   pp, reg);
+  GNUNET_free (pp);
+  GNUNET_free (reg);
+  return ret;
+}
+
+
+/**
+ * Convert a single line of an IPv4 policy to a regular expression.
+ *
+ * @param v6 line to convert
+ * @return NULL on error
+ */
+static char *
+ipv6_to_regex (const struct GNUNET_STRINGS_IPv6NetworkPolicy *v6)
+{
+  char *reg;
+  char *pp;
+  char *ret;
+
+  reg = address_to_regex (&v6->network,
+                          &v6->netmask,
+                          sizeof (struct in6_addr));
+  if (NULL == reg)
+    return NULL;
+  pp = port_to_regex (&v6->pp);
+  if (NULL == pp)
+  {
+    GNUNET_free (reg);
+    return NULL;
+  }
+  GNUNET_asprintf (&ret,
+                   "6-%s-%s",
+                   pp, reg);
+  GNUNET_free (pp);
+  GNUNET_free (reg);
+  return ret;
+}
+
+
+/**
+ * Convert an exit policy to a regular expression.  The exit policy
+ * specifies a set of subnets this peer is willing to serve as an
+ * exit for; the resulting regular expression will match the
+ * IPv4 address strings as returned by #GNUNET_TUN_ipv4toregexsearch().
+ *
+ * @param policy exit policy specification
+ * @return regular expression, NULL on error
+ */
+char *
+GNUNET_TUN_ipv4policy2regex (const char *policy)
+{
+  struct GNUNET_STRINGS_IPv4NetworkPolicy *np;
+  char *reg;
+  char *tmp;
+  char *line;
+  unsigned int i;
+
+  np = GNUNET_STRINGS_parse_ipv4_policy (policy);
+  if (NULL == np)
+    return NULL;
+  reg = NULL;
+  for (i=0; (0 == i) || (0 != np[i].network.s_addr); i++)
+  {
+    line = ipv4_to_regex (&np[i]);
+    if (NULL == line)
+    {
+      GNUNET_free_non_null (reg);
+      GNUNET_free (np);
+      return NULL;
+    }
+    if (NULL == reg)
+    {
+      reg = line;
+    }
+    else
+    {
+      GNUNET_asprintf (&tmp,
+                       "%s|(%s)",
+                       reg, line);
+      GNUNET_free (reg);
+      GNUNET_free (line);
+      reg = tmp;
+    }
+    if (0 == np[i].network.s_addr)
+      break;
+  }
+  GNUNET_free (np);
+  return reg;
+}
+
+
+/**
+ * Convert an exit policy to a regular expression.  The exit policy
+ * specifies a set of subnets this peer is willing to serve as an
+ * exit for; the resulting regular expression will match the
+ * IPv6 address strings as returned by #GNUNET_TUN_ipv6toregexsearch().
+ *
+ * @param policy exit policy specification
+ * @return regular expression, NULL on error
+ */
+char *
+GNUNET_TUN_ipv6policy2regex (const char *policy)
+{
+  struct in6_addr zero;
+  struct GNUNET_STRINGS_IPv6NetworkPolicy *np;
+  char *reg;
+  char *tmp;
+  char *line;
+  unsigned int i;
+
+  np = GNUNET_STRINGS_parse_ipv6_policy (policy);
+  if (NULL == np)
+    return NULL;
+  reg = NULL;
+  memset (&zero, 0, sizeof (struct in6_addr));
+  for (i=0; (0 == i) || (0 != memcmp (&zero, &np[i].network, sizeof (struct in6_addr))); i++)
+  {
+    line = ipv6_to_regex (&np[i]);
+    if (NULL == line)
+    {
+      GNUNET_free_non_null (reg);
+      GNUNET_free (np);
+      return NULL;
+    }
+    if (NULL == reg)
+    {
+      reg = line;
+    }
+    else
+    {
+      GNUNET_asprintf (&tmp,
+                       "%s|(%s)",
+                       reg, line);
+      GNUNET_free (reg);
+      GNUNET_free (line);
+      reg = tmp;
+    }
+    if (0 == memcmp (&zero, &np[i].network, sizeof (struct in6_addr)))
+      break;
+  }
+  GNUNET_free (np);
+  return reg;
+}
+
+
+/**
+ * Hash the service name of a hosted service to the
+ * hash code that is used to identify the service on
+ * the network.
+ *
+ * @param service_name a string
+ * @param hc corresponding hash
+ */
+void
+GNUNET_TUN_service_name_to_hash (const char *service_name,
+                                 struct GNUNET_HashCode *hc)
+{
+  GNUNET_CRYPTO_hash (service_name,
+                      strlen (service_name),
+                      hc);
+}
+
+
+/**
+ * Compute the CADET port given a service descriptor
+ * (returned from #GNUNET_TUN_service_name_to_hash) and
+ * a TCP/UDP port @a ip_port.
+ *
+ * @param desc service shared secret
+ * @param ip_port TCP/UDP port, use 0 for ICMP
+ * @param[out] cadet_port CADET port to use
+ */
+void
+GNUNET_TUN_compute_service_cadet_port (const struct GNUNET_HashCode *desc,
+                                       uint16_t ip_port,
+                                       struct GNUNET_HashCode *cadet_port)
+{
+  uint16_t be_port = htons (ip_port);
+
+  *cadet_port = *desc;
+  GNUNET_memcpy (cadet_port,
+                 &be_port,
+                 sizeof (uint16_t));
+}
+
+
+/* end of regex.c */
diff --git a/src/tun/test_regex.c b/src/tun/test_regex.c
new file mode 100644 (file)
index 0000000..2e7d528
--- /dev/null
@@ -0,0 +1,179 @@
+/*
+     This file is part of GNUnet
+     Copyright (C) 2012, 2013 GNUnet e.V.
+
+     GNUnet is free software: you can redistribute it and/or modify it
+     under the terms of the GNU Affero General Public License as published
+     by the Free Software Foundation, either version 3 of the License,
+     or (at your option) any later version.
+
+     GNUnet is distributed in the hope that it will be useful, but
+     WITHOUT ANY WARRANTY; without even the implied warranty of
+     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+     Affero General Public License for more details.
+    
+     You should have received a copy of the GNU Affero General Public License
+     along with this program.  If not, see <http://www.gnu.org/licenses/>.
+*/
+/**
+ * @file tun/test_regex.c
+ * @brief simple test for regex.c iptoregex functions
+ * @author Maximilian Szengel
+ */
+#include "platform.h"
+#include "gnunet_tun_lib.h"
+
+/**
+ * 'wildcard', matches all possible values (for HEX encoding).
+ */
+#define DOT "(0|1|2|3|4|5|6|7|8|9|A|B|C|D|E|F)"
+
+
+static int
+test_iptoregex (const char *ipv4,
+                uint16_t port,
+                const char *expectedv4,
+                const char *ipv6,
+                uint16_t port6,
+                const char *expectedv6)
+{
+  int error = 0;
+
+  struct in_addr a;
+  struct in6_addr b;
+  char rxv4[GNUNET_TUN_IPV4_REGEXLEN];
+  char rxv6[GNUNET_TUN_IPV6_REGEXLEN];
+
+  GNUNET_assert (1 == inet_pton (AF_INET, ipv4, &a));
+  GNUNET_TUN_ipv4toregexsearch (&a, port, rxv4);
+
+  if (0 != strcmp (rxv4, expectedv4))
+  {
+    GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+                "Expected: %s but got: %s\n",
+                expectedv4,
+                rxv4);
+    error++;
+  }
+
+  GNUNET_assert (1 == inet_pton (AF_INET6, ipv6, &b));
+  GNUNET_TUN_ipv6toregexsearch (&b, port6, rxv6);
+  if (0 != strcmp (rxv6, expectedv6))
+  {
+    GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+                "Expected: %s but got: %s\n",
+                expectedv6, rxv6);
+    error++;
+  }
+  return error;
+}
+
+
+static int
+test_policy4toregex (const char *policy,
+                     const char *regex)
+{
+  char *r;
+  int ret;
+
+  ret = 0;
+  r = GNUNET_TUN_ipv4policy2regex (policy);
+  if (NULL == r)
+  {
+    GNUNET_break (0);
+    return 1;
+  }
+  if (0 != strcmp (regex, r))
+  {
+    GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+                "Expected: `%s' but got: `%s'\n",
+                regex, r);
+    ret = 2;
+  }
+  GNUNET_free (r);
+  return ret;
+}
+
+
+static int
+test_policy6toregex (const char *policy,
+                     const char *regex)
+{
+  char *r;
+  int ret;
+
+  ret = 0;
+  r = GNUNET_TUN_ipv6policy2regex (policy);
+  if (NULL == r)
+  {
+    GNUNET_break (0);
+    return 1;
+  }
+  if (0 != strcmp (regex, r))
+  {
+    GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+                "Expected: `%s' but got: `%s'\n",
+                regex, r);
+    ret = 2;
+  }
+  GNUNET_free (r);
+  return ret;
+}
+
+
+int
+main (int argc, char *argv[])
+{
+  int error;
+  char *r;
+
+  GNUNET_log_setup ("test-regex", "WARNING", NULL);
+  error = 0;
+
+  /* this is just a performance test ... */
+  r = GNUNET_TUN_ipv4policy2regex ("1.2.3.4/16:!25;");
+  GNUNET_break (NULL != r);
+  GNUNET_free (r);
+
+  error +=
+    test_iptoregex ("192.1.2.3", 2086,
+                    "4-0826-C0010203",
+                    "FFFF::1", 8080,
+                    "6-1F90-FFFF0000000000000000000000000001");
+  error +=
+    test_iptoregex ("187.238.255.0", 80,
+                    "4-0050-BBEEFF00",
+                    "E1E1:73F9:51BE::0", 49,
+                    "6-0031-E1E173F951BE00000000000000000000");
+  error +=
+    test_policy4toregex ("192.1.2.0/24:80;",
+                         "4-0050-C00102" DOT DOT);
+  error +=
+    test_policy4toregex ("192.1.0.0/16;",
+                         "4-" DOT DOT DOT DOT "-C001" DOT DOT DOT DOT);
+  error +=
+    test_policy4toregex ("192.1.0.0/16:80-81;",
+                         "4-(0050|0051)-C001" DOT DOT DOT DOT);
+  error +=
+    test_policy4toregex ("192.1.0.0/8:!3-65535;",
+                         "4-000(0|1|2)-C0" DOT DOT DOT DOT DOT DOT);
+  error +=
+    test_policy4toregex ("192.1.0.0/8:!25-56;",
+                         "4-(0(0(0"DOT"|1(0|1|2|3|4|5|6|7|8)|3(9|A|B|C|D|E|F)|(4|5|6|7|8|9|A|B|C|D|E|F)"DOT")|(1|2|3|4|5|6|7|8|9|A|B|C|D|E|F)"DOT DOT")|(1|2|3|4|5|6|7|8|9|A|B|C|D|E|F)"DOT DOT DOT")-C0"DOT DOT DOT DOT DOT DOT);
+  error +=
+    test_policy6toregex ("E1E1::1;",
+                         "6-"DOT DOT DOT DOT"-E1E10000000000000000000000000001");
+  error +=
+    test_policy6toregex ("E1E1:ABCD::1/120;",
+                         "6-"DOT DOT DOT DOT"-E1E1ABCD0000000000000000000000" DOT DOT);
+  error +=
+    test_policy6toregex ("E1E1:ABCD::ABCD/126;",
+                         "6-"DOT DOT DOT DOT"-E1E1ABCD00000000000000000000ABC(C|D|E|F)");
+  error +=
+    test_policy6toregex ("E1E1:ABCD::ABCD/127;",
+                         "6-"DOT DOT DOT DOT"-E1E1ABCD00000000000000000000ABC(C|D)");
+  error +=
+    test_policy6toregex ("E1E1:ABCD::ABCD/128:80;",
+                         "6-0050-E1E1ABCD00000000000000000000ABCD");
+  return error;
+}
diff --git a/src/tun/test_tun.c b/src/tun/test_tun.c
new file mode 100644 (file)
index 0000000..edbd4c0
--- /dev/null
@@ -0,0 +1,72 @@
+/*
+     This file is part of GNUnet.
+     Copyright (C) 2010, 2011, 2012 Christian Grothoff
+
+     GNUnet is free software: you can redistribute it and/or modify it
+     under the terms of the GNU Affero General Public License as published
+     by the Free Software Foundation, either version 3 of the License,
+     or (at your option) any later version.
+
+     GNUnet is distributed in the hope that it will be useful, but
+     WITHOUT ANY WARRANTY; without even the implied warranty of
+     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+     Affero General Public License for more details.
+    
+     You should have received a copy of the GNU Affero General Public License
+     along with this program.  If not, see <http://www.gnu.org/licenses/>.
+*/
+
+/**
+ * @file tun/test_tun.c
+ * @brief test for tun.c
+ * @author Christian Grothoff
+ */
+#include "platform.h"
+#include "gnunet_tun_lib.h"
+
+static int ret;
+
+static void
+test_udp (size_t pll,
+         int pl_fill,
+         uint16_t crc)
+{
+  struct GNUNET_TUN_IPv4Header ip;
+  struct GNUNET_TUN_UdpHeader udp;
+  char payload[pll];
+  struct in_addr src;
+  struct in_addr dst;
+
+  GNUNET_assert (1 == inet_pton (AF_INET, "1.2.3.4", &src));
+  GNUNET_assert (1 == inet_pton (AF_INET, "122.2.3.5", &dst));
+  memset (payload, pl_fill, sizeof (payload));
+  GNUNET_TUN_initialize_ipv4_header (&ip,
+                                    IPPROTO_UDP,
+                                    pll + sizeof (udp),
+                                    &src,
+                                    &dst);
+  udp.source_port = htons (4242);
+  udp.destination_port = htons (4242);
+  udp.len = htons (pll);
+  GNUNET_TUN_calculate_udp4_checksum (&ip,
+                                     &udp,
+                                     payload,
+                                     pll);
+  if (crc != ntohs (udp.crc))
+  {
+    fprintf (stderr, "Got CRC: %u, wanted: %u\n",
+            ntohs (udp.crc),
+            crc);
+    ret = 1;
+  }
+}
+
+int main (int argc,
+         char **argv)
+{
+  test_udp (4, 3, 22439);
+  test_udp (4, 1, 23467);
+  test_udp (7, 17, 6516);
+  test_udp (12451, 251, 42771);
+  return ret;
+}
diff --git a/src/tun/tun.c b/src/tun/tun.c
new file mode 100644 (file)
index 0000000..f85f722
--- /dev/null
@@ -0,0 +1,309 @@
+/*
+     This file is part of GNUnet.
+     Copyright (C) 2010, 2011, 2012 Christian Grothoff
+
+     GNUnet is free software: you can redistribute it and/or modify it
+     under the terms of the GNU Affero General Public License as published
+     by the Free Software Foundation, either version 3 of the License,
+     or (at your option) any later version.
+
+     GNUnet is distributed in the hope that it will be useful, but
+     WITHOUT ANY WARRANTY; without even the implied warranty of
+     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+     Affero General Public License for more details.
+    
+     You should have received a copy of the GNU Affero General Public License
+     along with this program.  If not, see <http://www.gnu.org/licenses/>.
+*/
+
+/**
+ * @file tun/tun.c
+ * @brief standard IP calculations for TUN interaction
+ * @author Philipp Toelke
+ * @author Christian Grothoff
+ */
+#include "platform.h"
+#include "gnunet_tun_lib.h"
+
+/**
+ * IP TTL we use for packets that we assemble (8 bit unsigned integer)
+ */
+#define FRESH_TTL 64
+
+
+/**
+ * Initialize an IPv4 header.
+ *
+ * @param ip header to initialize
+ * @param protocol protocol to use (i.e. IPPROTO_UDP)
+ * @param payload_length number of bytes of payload that follow (excluding IPv4 header)
+ * @param src source IP address to use
+ * @param dst destination IP address to use
+ */
+void
+GNUNET_TUN_initialize_ipv4_header (struct GNUNET_TUN_IPv4Header *ip,
+                                  uint8_t protocol,
+                                  uint16_t payload_length,
+                                  const struct in_addr *src,
+                                  const struct in_addr *dst)
+{
+  GNUNET_assert (20 == sizeof (struct GNUNET_TUN_IPv4Header));
+  GNUNET_assert (payload_length <= UINT16_MAX - sizeof (struct GNUNET_TUN_IPv4Header));
+  memset (ip, 0, sizeof (struct GNUNET_TUN_IPv4Header));
+  ip->header_length =  sizeof (struct GNUNET_TUN_IPv4Header) / 4;
+  ip->version = 4;
+  ip->total_length = htons (sizeof (struct GNUNET_TUN_IPv4Header) + payload_length);
+  ip->identification = (uint16_t) GNUNET_CRYPTO_random_u32 (GNUNET_CRYPTO_QUALITY_WEAK,
+                                                           65536);
+  ip->ttl = FRESH_TTL;
+  ip->protocol = protocol;
+  ip->source_address = *src;
+  ip->destination_address = *dst;
+  ip->checksum = GNUNET_CRYPTO_crc16_n (ip, sizeof (struct GNUNET_TUN_IPv4Header));
+}
+
+
+/**
+ * Initialize an IPv6 header.
+ *
+ * @param ip header to initialize
+ * @param protocol protocol to use (i.e. IPPROTO_UDP), technically "next_header" for IPv6
+ * @param payload_length number of bytes of payload that follow (excluding IPv6 header)
+ * @param src source IP address to use
+ * @param dst destination IP address to use
+ */
+void
+GNUNET_TUN_initialize_ipv6_header (struct GNUNET_TUN_IPv6Header *ip,
+                                  uint8_t protocol,
+                                  uint16_t payload_length,
+                                  const struct in6_addr *src,
+                                  const struct in6_addr *dst)
+{
+  GNUNET_assert (40 == sizeof (struct GNUNET_TUN_IPv6Header));
+  GNUNET_assert (payload_length <= UINT16_MAX - sizeof (struct GNUNET_TUN_IPv6Header));
+  memset (ip, 0, sizeof (struct GNUNET_TUN_IPv6Header));
+  ip->version = 6;
+  ip->next_header = protocol;
+  ip->payload_length = htons ((uint16_t) payload_length);
+  ip->hop_limit = FRESH_TTL;
+  ip->destination_address = *dst;
+  ip->source_address = *src;
+}
+
+
+/**
+ * Calculate IPv4 TCP checksum.
+ *
+ * @param ip ipv4 header fully initialized
+ * @param tcp TCP header (initialized except for CRC)
+ * @param payload the TCP payload
+ * @param payload_length number of bytes of TCP payload
+ */
+void
+GNUNET_TUN_calculate_tcp4_checksum (const struct GNUNET_TUN_IPv4Header *ip,
+                                   struct GNUNET_TUN_TcpHeader *tcp,
+                                   const void *payload,
+                                   uint16_t payload_length)
+{
+  uint32_t sum;
+  uint16_t tmp;
+
+  GNUNET_assert (20 == sizeof (struct GNUNET_TUN_TcpHeader));
+  GNUNET_assert (payload_length + sizeof (struct GNUNET_TUN_IPv4Header) + sizeof (struct GNUNET_TUN_TcpHeader) ==
+                ntohs (ip->total_length));
+  GNUNET_assert (IPPROTO_TCP == ip->protocol);
+
+  tcp->crc = 0;
+  sum = GNUNET_CRYPTO_crc16_step (0,
+                                 &ip->source_address,
+                                 sizeof (struct in_addr) * 2);
+  tmp = htons (IPPROTO_TCP);
+  sum = GNUNET_CRYPTO_crc16_step (sum, &tmp, sizeof (uint16_t));
+  tmp = htons (payload_length + sizeof (struct GNUNET_TUN_TcpHeader));
+  sum = GNUNET_CRYPTO_crc16_step (sum, &tmp, sizeof (uint16_t));
+  sum = GNUNET_CRYPTO_crc16_step (sum, tcp, sizeof (struct GNUNET_TUN_TcpHeader));
+  sum = GNUNET_CRYPTO_crc16_step (sum, payload, payload_length);
+  tcp->crc = GNUNET_CRYPTO_crc16_finish (sum);
+}
+
+
+/**
+ * Calculate IPv6 TCP checksum.
+ *
+ * @param ip ipv6 header fully initialized
+ * @param tcp header (initialized except for CRC)
+ * @param payload the TCP payload
+ * @param payload_length number of bytes of TCP payload
+ */
+void
+GNUNET_TUN_calculate_tcp6_checksum (const struct GNUNET_TUN_IPv6Header *ip,
+                                   struct GNUNET_TUN_TcpHeader *tcp,
+                                   const void *payload,
+                                   uint16_t payload_length)
+{
+  uint32_t sum;
+  uint32_t tmp;
+
+  GNUNET_assert (20 == sizeof (struct GNUNET_TUN_TcpHeader));
+  GNUNET_assert (payload_length + sizeof (struct GNUNET_TUN_TcpHeader) ==
+                ntohs (ip->payload_length));
+  GNUNET_assert (IPPROTO_TCP == ip->next_header);
+  tcp->crc = 0;
+  sum = GNUNET_CRYPTO_crc16_step (0, &ip->source_address, 2 * sizeof (struct in6_addr));
+  tmp = htonl (sizeof (struct GNUNET_TUN_TcpHeader) + payload_length);
+  sum = GNUNET_CRYPTO_crc16_step (sum, &tmp, sizeof (uint32_t));
+  tmp = htonl (IPPROTO_TCP);
+  sum = GNUNET_CRYPTO_crc16_step (sum, &tmp, sizeof (uint32_t));
+  sum = GNUNET_CRYPTO_crc16_step (sum, tcp,
+                                 sizeof (struct GNUNET_TUN_TcpHeader));
+  sum = GNUNET_CRYPTO_crc16_step (sum, payload, payload_length);
+  tcp->crc = GNUNET_CRYPTO_crc16_finish (sum);
+}
+
+
+/**
+ * Calculate IPv4 UDP checksum.
+ *
+ * @param ip ipv4 header fully initialized
+ * @param udp UDP header (initialized except for CRC)
+ * @param payload the UDP payload
+ * @param payload_length number of bytes of UDP payload
+ */
+void
+GNUNET_TUN_calculate_udp4_checksum (const struct GNUNET_TUN_IPv4Header *ip,
+                                   struct GNUNET_TUN_UdpHeader *udp,
+                                   const void *payload,
+                                   uint16_t payload_length)
+{
+  uint32_t sum;
+  uint16_t tmp;
+
+  GNUNET_assert (8 == sizeof (struct GNUNET_TUN_UdpHeader));
+  GNUNET_assert (payload_length + sizeof (struct GNUNET_TUN_IPv4Header) + sizeof (struct GNUNET_TUN_UdpHeader) ==
+                ntohs (ip->total_length));
+  GNUNET_assert (IPPROTO_UDP == ip->protocol);
+
+  udp->crc = 0; /* technically optional, but we calculate it anyway, just to be sure */
+  sum = GNUNET_CRYPTO_crc16_step (0,
+                                 &ip->source_address,
+                                 sizeof (struct in_addr) * 2);
+  tmp = htons (IPPROTO_UDP);
+  sum = GNUNET_CRYPTO_crc16_step (sum,
+                                 &tmp,
+                                 sizeof (uint16_t));
+  tmp = htons (sizeof (struct GNUNET_TUN_UdpHeader) + payload_length);
+  sum = GNUNET_CRYPTO_crc16_step (sum,
+                                 &tmp,
+                                 sizeof (uint16_t));
+  sum = GNUNET_CRYPTO_crc16_step (sum,
+                                 udp,
+                                 sizeof (struct GNUNET_TUN_UdpHeader));
+  sum = GNUNET_CRYPTO_crc16_step (sum,
+                                 payload,
+                                 payload_length);
+  udp->crc = GNUNET_CRYPTO_crc16_finish (sum);
+}
+
+
+/**
+ * Calculate IPv6 UDP checksum.
+ *
+ * @param ip ipv6 header fully initialized
+ * @param udp UDP header (initialized except for CRC)
+ * @param payload the UDP payload
+ * @param payload_length number of bytes of UDP payload
+ */
+void
+GNUNET_TUN_calculate_udp6_checksum (const struct GNUNET_TUN_IPv6Header *ip,
+                                   struct GNUNET_TUN_UdpHeader *udp,
+                                   const void *payload,
+                                   uint16_t payload_length)
+{
+  uint32_t sum;
+  uint32_t tmp;
+
+  GNUNET_assert (payload_length + sizeof (struct GNUNET_TUN_UdpHeader) ==
+                ntohs (ip->payload_length));
+  GNUNET_assert (payload_length + sizeof (struct GNUNET_TUN_UdpHeader) ==
+                ntohs (udp->len));
+  GNUNET_assert (IPPROTO_UDP == ip->next_header);
+
+  udp->crc = 0;
+  sum = GNUNET_CRYPTO_crc16_step (0,
+                                 &ip->source_address,
+                                 sizeof (struct in6_addr) * 2);
+  tmp = htons (sizeof (struct GNUNET_TUN_UdpHeader) + payload_length); /* aka udp->len */
+  sum = GNUNET_CRYPTO_crc16_step (sum, &tmp, sizeof (uint32_t));
+  tmp = htons (ip->next_header);
+  sum = GNUNET_CRYPTO_crc16_step (sum, &tmp, sizeof (uint32_t));
+  sum = GNUNET_CRYPTO_crc16_step (sum, udp, sizeof (struct GNUNET_TUN_UdpHeader));
+  sum = GNUNET_CRYPTO_crc16_step (sum, payload, payload_length);
+  udp->crc = GNUNET_CRYPTO_crc16_finish (sum);
+}
+
+
+/**
+ * Calculate ICMP checksum.
+ *
+ * @param icmp IMCP header (initialized except for CRC)
+ * @param payload the ICMP payload
+ * @param payload_length number of bytes of ICMP payload
+ */
+void
+GNUNET_TUN_calculate_icmp_checksum (struct GNUNET_TUN_IcmpHeader *icmp,
+                                   const void *payload,
+                                   uint16_t payload_length)
+{
+  uint32_t sum;
+
+  GNUNET_assert (8 == sizeof (struct GNUNET_TUN_IcmpHeader));
+  icmp->crc = 0;
+  sum = GNUNET_CRYPTO_crc16_step (0,
+                                 icmp,
+                                 sizeof (struct GNUNET_TUN_IcmpHeader));
+  sum = GNUNET_CRYPTO_crc16_step (sum, payload, payload_length);
+  icmp->crc = GNUNET_CRYPTO_crc16_finish (sum);
+}
+
+
+/**
+ * Check if two sockaddrs are equal.
+ *
+ * @param sa one address
+ * @param sb another address
+ * @param include_port also check ports
+ * @return #GNUNET_YES if they are equal
+ */
+int
+GNUNET_TUN_sockaddr_cmp (const struct sockaddr *sa,
+                         const struct sockaddr *sb,
+                         int include_port)
+{
+  if (sa->sa_family != sb->sa_family)
+    return GNUNET_NO;
+
+  switch (sa->sa_family)
+  {
+  case AF_INET:
+    {
+      const struct sockaddr_in *sa4 = (const struct sockaddr_in *) sa;
+      const struct sockaddr_in *sb4 = (const struct sockaddr_in *) sb;
+      return (sa4->sin_addr.s_addr == sb4->sin_addr.s_addr);
+    }
+  case AF_INET6:
+    {
+      const struct sockaddr_in6 *sa6 = (const struct sockaddr_in6 *) sa;
+      const struct sockaddr_in6 *sb6 = (const struct sockaddr_in6 *) sb;
+
+      return (0 == memcmp(&sa6->sin6_addr,
+                          &sb6->sin6_addr,
+                          sizeof (struct in6_addr)));
+    }
+  default:
+    GNUNET_break (0);
+    return GNUNET_SYSERR;
+  }
+}
+
+
+/* end of tun.c */
index ec7bcb016facbd2b4c44b85a3685db99be33f72c..4296199db419ece8e543d5a098055d96633cb1c4 100644 (file)
@@ -89,8 +89,6 @@ libgnunetutil_la_SOURCES = \
   crypto_rsa.c \
   disk.c \
   disk.h \
-  dnsparser.c \
-  dnsstub.c \
   getopt.c \
   getopt_helpers.c \
   helper.c \
@@ -106,14 +104,12 @@ libgnunetutil_la_SOURCES = \
   peer.c \
   plugin.c \
   program.c \
-  regex.c \
   resolver_api.c resolver.h \
   scheduler.c \
   service.c \
   signal.c \
   strings.c \
   time.c \
-  tun.c \
   speedup.c speedup.h
 
 libgnunetutil_la_LIBADD = \
@@ -121,7 +117,7 @@ libgnunetutil_la_LIBADD = \
   $(LIBGCRYPT_LIBS) \
   $(LTLIBICONV) \
   $(LTLIBINTL) \
-  -lltdl -lidn $(Z_LIBS) -lunistring $(XLIB)
+  -lltdl $(Z_LIBS) -lunistring $(XLIB)
 
 libgnunetutil_la_LDFLAGS = \
   $(GN_LIB_LDFLAGS) \
@@ -295,22 +291,19 @@ check_PROGRAMS = \
  test_crypto_rsa \
  test_disk \
  test_getopt \
- test_hexcoder \
  test_mq \
  test_os_network \
  test_peer \
  test_plugin \
  test_program \
- test_regex \
  test_resolver_api.nc \
  test_scheduler \
  test_scheduler_delay \
  test_service \
  test_strings \
  test_strings_to_data \
- test_speedup \
  test_time \
- test_tun \
+ test_speedup \
  $(BENCHMARKS) \
  test_os_start_process \
  test_common_logging_runtime_loglevels
@@ -326,20 +319,6 @@ test_bio_SOURCES = \
 test_bio_LDADD = \
  libgnunetutil.la
 
-test_hexcoder_SOURCES = \
- test_hexcoder.c
-test_hexcoder_LDADD = \
- libgnunetutil.la
-
-test_tun_SOURCES = \
- test_tun.c
-test_tun_LDADD = \
- libgnunetutil.la 
-
-test_regex_SOURCES = \
- test_regex.c
-test_regex_LDADD = \
- libgnunetutil.la 
 
 test_os_start_process_SOURCES = \
  test_os_start_process.c
diff --git a/src/util/dnsparser.c b/src/util/dnsparser.c
deleted file mode 100644 (file)
index 32ad7c0..0000000
+++ /dev/null
@@ -1,1334 +0,0 @@
-/*
-      This file is part of GNUnet
-      Copyright (C) 2010-2014 GNUnet e.V.
-
-      GNUnet is free software: you can redistribute it and/or modify it
-      under the terms of the GNU Affero General Public License as published
-      by the Free Software Foundation, either version 3 of the License,
-      or (at your option) any later version.
-
-      GNUnet is distributed in the hope that it will be useful, but
-      WITHOUT ANY WARRANTY; without even the implied warranty of
-      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
-      Affero General Public License for more details.
-     
-      You should have received a copy of the GNU Affero General Public License
-      along with this program.  If not, see <http://www.gnu.org/licenses/>.
- */
-
-/**
- * @file dns/dnsparser.c
- * @brief helper library to parse DNS packets.
- * @author Philipp Toelke
- * @author Christian Grothoff
- */
-#include "platform.h"
-#include <idna.h>
-#if WINDOWS
-#include <idn-free.h>
-#endif
-#include "gnunet_util_lib.h"
-#include "gnunet_dnsparser_lib.h"
-#include "gnunet_tun_lib.h"
-
-
-/**
- * Check if a label in UTF-8 format can be coded into valid IDNA.
- * This can fail if the ASCII-conversion becomes longer than 63 characters.
- *
- * @param label label to check (UTF-8 string)
- * @return #GNUNET_OK if the label can be converted to IDNA,
- *         #GNUNET_SYSERR if the label is not valid for DNS names
- */
-int
-GNUNET_DNSPARSER_check_label (const char *label)
-{
-  char *output;
-  size_t slen;
-
-  if (NULL != strchr (label, '.'))
-    return GNUNET_SYSERR; /* not a label! Did you mean GNUNET_DNSPARSER_check_name? */
-  if (IDNA_SUCCESS !=
-      idna_to_ascii_8z (label, &output, IDNA_ALLOW_UNASSIGNED))
-    return GNUNET_SYSERR;
-  slen = strlen (output);
-#if WINDOWS
-  idn_free (output);
-#else
-  free (output);
-#endif
-  return (slen > 63) ? GNUNET_SYSERR : GNUNET_OK;
-}
-
-
-/**
- * Check if a label in UTF-8 format can be coded into valid IDNA.
- * This can fail if the ASCII-conversion becomes longer than 253 characters.
- *
- * @param name name to check (UTF-8 string)
- * @return #GNUNET_OK if the label can be converted to IDNA,
- *         #GNUNET_SYSERR if the label is not valid for DNS names
- */
-int
-GNUNET_DNSPARSER_check_name (const char *name)
-{
-  char *ldup;
-  char *output;
-  size_t slen;
-  char *tok;
-
-  ldup = GNUNET_strdup (name);
-  for (tok = strtok (ldup, "."); NULL != tok; tok = strtok (NULL, "."))
-    if (GNUNET_OK !=
-       GNUNET_DNSPARSER_check_label (tok))
-    {
-      GNUNET_free (ldup);
-      return GNUNET_SYSERR;
-    }
-  GNUNET_free (ldup);
-  if (IDNA_SUCCESS !=
-      idna_to_ascii_8z (name, &output, IDNA_ALLOW_UNASSIGNED))
-    return GNUNET_SYSERR;
-  slen = strlen (output);
-#if WINDOWS
-  idn_free (output);
-#else
-  free (output);
-#endif
-  return (slen > 253) ? GNUNET_SYSERR : GNUNET_OK;
-}
-
-
-/**
- * Free SOA information record.
- *
- * @param soa record to free
- */
-void
-GNUNET_DNSPARSER_free_soa (struct GNUNET_DNSPARSER_SoaRecord *soa)
-{
-  if (NULL == soa)
-    return;
-  GNUNET_free_non_null (soa->mname);
-  GNUNET_free_non_null (soa->rname);
-  GNUNET_free (soa);
-}
-
-
-/**
- * Free CERT information record.
- *
- * @param cert record to free
- */
-void
-GNUNET_DNSPARSER_free_cert (struct GNUNET_DNSPARSER_CertRecord *cert)
-{
-  if (NULL == cert)
-    return;
-  GNUNET_free_non_null (cert->certificate_data);
-  GNUNET_free (cert);
-}
-
-
-/**
- * Free SRV information record.
- *
- * @param srv record to free
- */
-void
-GNUNET_DNSPARSER_free_srv (struct GNUNET_DNSPARSER_SrvRecord *srv)
-{
-  if (NULL == srv)
-    return;
-  GNUNET_free_non_null (srv->target);
-  GNUNET_free (srv);
-}
-
-
-/**
- * Free MX information record.
- *
- * @param mx record to free
- */
-void
-GNUNET_DNSPARSER_free_mx (struct GNUNET_DNSPARSER_MxRecord *mx)
-{
-  if (NULL == mx)
-    return;
-  GNUNET_free_non_null (mx->mxhost);
-  GNUNET_free (mx);
-}
-
-
-/**
- * Free the given DNS record.
- *
- * @param r record to free
- */
-void
-GNUNET_DNSPARSER_free_record (struct GNUNET_DNSPARSER_Record *r)
-{
-  GNUNET_free_non_null (r->name);
-  switch (r->type)
-  {
-  case GNUNET_DNSPARSER_TYPE_MX:
-    GNUNET_DNSPARSER_free_mx (r->data.mx);
-    break;
-  case GNUNET_DNSPARSER_TYPE_SOA:
-    GNUNET_DNSPARSER_free_soa (r->data.soa);
-    break;
-  case GNUNET_DNSPARSER_TYPE_SRV:
-    GNUNET_DNSPARSER_free_srv (r->data.srv);
-    break;
-  case GNUNET_DNSPARSER_TYPE_CERT:
-    GNUNET_DNSPARSER_free_cert (r->data.cert);
-    break;
-  case GNUNET_DNSPARSER_TYPE_NS:
-  case GNUNET_DNSPARSER_TYPE_CNAME:
-  case GNUNET_DNSPARSER_TYPE_PTR:
-    GNUNET_free_non_null (r->data.hostname);
-    break;
-  default:
-    GNUNET_free_non_null (r->data.raw.data);
-    break;
-  }
-}
-
-
-/**
- * Parse name inside of a DNS query or record.
- *
- * @param udp_payload entire UDP payload
- * @param udp_payload_length length of @a udp_payload
- * @param off pointer to the offset of the name to parse in the udp_payload (to be
- *                    incremented by the size of the name)
- * @param depth current depth of our recursion (to prevent stack overflow)
- * @return name as 0-terminated C string on success, NULL if the payload is malformed
- */
-static char *
-parse_name (const char *udp_payload,
-           size_t udp_payload_length,
-           size_t *off,
-           unsigned int depth)
-{
-  const uint8_t *input = (const uint8_t *) udp_payload;
-  char *ret;
-  char *tmp;
-  char *xstr;
-  uint8_t len;
-  size_t xoff;
-  char *utf8;
-  Idna_rc rc;
-
-  ret = GNUNET_strdup ("");
-  while (1)
-  {
-    if (*off >= udp_payload_length)
-    {
-      GNUNET_break_op (0);
-      goto error;
-    }
-    len = input[*off];
-    if (0 == len)
-    {
-      (*off)++;
-      break;
-    }
-    if (len < 64)
-    {
-      if (*off + 1 + len > udp_payload_length)
-      {
-       GNUNET_break_op (0);
-       goto error;
-      }
-      GNUNET_asprintf (&tmp,
-                      "%.*s",
-                      (int) len,
-                      &udp_payload[*off + 1]);
-      if (IDNA_SUCCESS !=
-         (rc = idna_to_unicode_8z8z (tmp, &utf8, IDNA_ALLOW_UNASSIGNED)))
-      {
-       GNUNET_log (GNUNET_ERROR_TYPE_INFO,
-                   _("Failed to convert DNS IDNA name `%s' to UTF-8: %s\n"),
-                   tmp,
-                   idna_strerror (rc));
-       GNUNET_free (tmp);
-       GNUNET_asprintf (&tmp,
-                        "%s%.*s.",
-                        ret,
-                        (int) len,
-                        &udp_payload[*off + 1]);
-      }
-      else
-      {
-       GNUNET_free (tmp);
-       GNUNET_asprintf (&tmp,
-                        "%s%s.",
-                        ret,
-                        utf8);
-#if WINDOWS
-       idn_free (utf8);
-#else
-       free (utf8);
-#endif
-      }
-      GNUNET_free (ret);
-      ret = tmp;
-      *off += 1 + len;
-    }
-    else if ((64 | 128) == (len & (64 | 128)) )
-    {
-      if (depth > 32)
-      {
-       GNUNET_break_op (0);
-       goto error; /* hard bound on stack to prevent "infinite" recursion, disallow! */
-      }
-      /* pointer to string */
-      if (*off + 1 > udp_payload_length)
-      {
-       GNUNET_break_op (0);
-       goto error;
-      }
-      xoff = ((len - (64 | 128)) << 8) + input[*off+1];
-      xstr = parse_name (udp_payload,
-                        udp_payload_length,
-                        &xoff,
-                        depth + 1);
-      if (NULL == xstr)
-      {
-       GNUNET_break_op (0);
-       goto error;
-      }
-      GNUNET_asprintf (&tmp,
-                      "%s%s.",
-                      ret,
-                      xstr);
-      GNUNET_free (ret);
-      GNUNET_free (xstr);
-      ret = tmp;
-      if (strlen (ret) > udp_payload_length)
-      {
-       GNUNET_break_op (0);
-       goto error; /* we are looping (building an infinite string) */
-      }
-      *off += 2;
-      /* pointers always terminate names */
-      break;
-    }
-    else
-    {
-      /* neither pointer nor inline string, not supported... */
-      GNUNET_break_op (0);
-      goto error;
-    }
-  }
-  if (0 < strlen(ret))
-    ret[strlen(ret)-1] = '\0'; /* eat tailing '.' */
-  return ret;
- error:
-  GNUNET_break_op (0);
-  GNUNET_free (ret);
-  return NULL;
-}
-
-
-/**
- * Parse name inside of a DNS query or record.
- *
- * @param udp_payload entire UDP payload
- * @param udp_payload_length length of @a udp_payload
- * @param off pointer to the offset of the name to parse in the udp_payload (to be
- *                    incremented by the size of the name)
- * @return name as 0-terminated C string on success, NULL if the payload is malformed
- */
-char *
-GNUNET_DNSPARSER_parse_name (const char *udp_payload,
-                            size_t udp_payload_length,
-                            size_t *off)
-{
-  return parse_name (udp_payload, udp_payload_length, off, 0);
-}
-
-
-/**
- * Parse a DNS query entry.
- *
- * @param udp_payload entire UDP payload
- * @param udp_payload_length length of @a udp_payload
- * @param off pointer to the offset of the query to parse in the udp_payload (to be
- *                    incremented by the size of the query)
- * @param q where to write the query information
- * @return #GNUNET_OK on success, #GNUNET_SYSERR if the query is malformed
- */
-int
-GNUNET_DNSPARSER_parse_query (const char *udp_payload,
-                             size_t udp_payload_length,
-                             size_t *off,
-                             struct GNUNET_DNSPARSER_Query *q)
-{
-  char *name;
-  struct GNUNET_TUN_DnsQueryLine ql;
-
-  name = GNUNET_DNSPARSER_parse_name (udp_payload,
-                                     udp_payload_length,
-                                     off);
-  if (NULL == name)
-  {
-    GNUNET_break_op (0);
-    return GNUNET_SYSERR;
-  }
-  q->name = name;
-  if (*off + sizeof (struct GNUNET_TUN_DnsQueryLine) > udp_payload_length)
-  {
-    GNUNET_break_op (0);
-    return GNUNET_SYSERR;
-  }
-  GNUNET_memcpy (&ql, &udp_payload[*off], sizeof (ql));
-  *off += sizeof (ql);
-  q->type = ntohs (ql.type);
-  q->dns_traffic_class = ntohs (ql.dns_traffic_class);
-  return GNUNET_OK;
-}
-
-
-/**
- * Parse a DNS SOA record.
- *
- * @param udp_payload reference to UDP packet
- * @param udp_payload_length length of @a udp_payload
- * @param off pointer to the offset of the query to parse in the SOA record (to be
- *                    incremented by the size of the record), unchanged on error
- * @return the parsed SOA record, NULL on error
- */
-struct GNUNET_DNSPARSER_SoaRecord *
-GNUNET_DNSPARSER_parse_soa (const char *udp_payload,
-                           size_t udp_payload_length,
-                           size_t *off)
-{
-  struct GNUNET_DNSPARSER_SoaRecord *soa;
-  struct GNUNET_TUN_DnsSoaRecord soa_bin;
-  size_t old_off;
-
-  old_off = *off;
-  soa = GNUNET_new (struct GNUNET_DNSPARSER_SoaRecord);
-  soa->mname = GNUNET_DNSPARSER_parse_name (udp_payload,
-                                           udp_payload_length,
-                                           off);
-  soa->rname = GNUNET_DNSPARSER_parse_name (udp_payload,
-                                           udp_payload_length,
-                                           off);
-  if ( (NULL == soa->mname) ||
-       (NULL == soa->rname) ||
-       (*off + sizeof (struct GNUNET_TUN_DnsSoaRecord) > udp_payload_length) )
-  {
-    GNUNET_break_op (0);
-    GNUNET_DNSPARSER_free_soa (soa);
-    *off = old_off;
-    return NULL;
-  }
-  GNUNET_memcpy (&soa_bin,
-         &udp_payload[*off],
-         sizeof (struct GNUNET_TUN_DnsSoaRecord));
-  soa->serial = ntohl (soa_bin.serial);
-  soa->refresh = ntohl (soa_bin.refresh);
-  soa->retry = ntohl (soa_bin.retry);
-  soa->expire = ntohl (soa_bin.expire);
-  soa->minimum_ttl = ntohl (soa_bin.minimum);
-  (*off) += sizeof (struct GNUNET_TUN_DnsSoaRecord);
-  return soa;
-}
-
-
-/**
- * Parse a DNS MX record.
- *
- * @param udp_payload reference to UDP packet
- * @param udp_payload_length length of @a udp_payload
- * @param off pointer to the offset of the query to parse in the MX record (to be
- *                    incremented by the size of the record), unchanged on error
- * @return the parsed MX record, NULL on error
- */
-struct GNUNET_DNSPARSER_MxRecord *
-GNUNET_DNSPARSER_parse_mx (const char *udp_payload,
-                          size_t udp_payload_length,
-                          size_t *off)
-{
-  struct GNUNET_DNSPARSER_MxRecord *mx;
-  uint16_t mxpref;
-  size_t old_off;
-
-  old_off = *off;
-  if (*off + sizeof (uint16_t) > udp_payload_length)
-  {
-    GNUNET_break_op (0);
-    return NULL;
-  }
-  GNUNET_memcpy (&mxpref, &udp_payload[*off], sizeof (uint16_t));
-  (*off) += sizeof (uint16_t);
-  mx = GNUNET_new (struct GNUNET_DNSPARSER_MxRecord);
-  mx->preference = ntohs (mxpref);
-  mx->mxhost = GNUNET_DNSPARSER_parse_name (udp_payload,
-                                           udp_payload_length,
-                                           off);
-  if (NULL == mx->mxhost)
-  {
-    GNUNET_break_op (0);
-    GNUNET_DNSPARSER_free_mx (mx);
-    *off = old_off;
-    return NULL;
-  }
-  return mx;
-}
-
-
-/**
- * Parse a DNS SRV record.
- *
- * @param udp_payload reference to UDP packet
- * @param udp_payload_length length of @a udp_payload
- * @param off pointer to the offset of the query to parse in the SRV record (to be
- *                    incremented by the size of the record), unchanged on error
- * @return the parsed SRV record, NULL on error
- */
-struct GNUNET_DNSPARSER_SrvRecord *
-GNUNET_DNSPARSER_parse_srv (const char *udp_payload,
-                           size_t udp_payload_length,
-                           size_t *off)
-{
-  struct GNUNET_DNSPARSER_SrvRecord *srv;
-  struct GNUNET_TUN_DnsSrvRecord srv_bin;
-  size_t old_off;
-
-  old_off = *off;
-  if (*off + sizeof (struct GNUNET_TUN_DnsSrvRecord) > udp_payload_length)
-    return NULL;
-  GNUNET_memcpy (&srv_bin,
-         &udp_payload[*off],
-         sizeof (struct GNUNET_TUN_DnsSrvRecord));
-  (*off) += sizeof (struct GNUNET_TUN_DnsSrvRecord);
-  srv = GNUNET_new (struct GNUNET_DNSPARSER_SrvRecord);
-  srv->priority = ntohs (srv_bin.prio);
-  srv->weight = ntohs (srv_bin.weight);
-  srv->port = ntohs (srv_bin.port);
-  srv->target = GNUNET_DNSPARSER_parse_name (udp_payload,
-                                            udp_payload_length,
-                                            off);
-  if (NULL == srv->target)
-  {
-    GNUNET_DNSPARSER_free_srv (srv);
-    *off = old_off;
-    return NULL;
-  }
-  return srv;
-}
-
-
-/**
- * Parse a DNS CERT record.
- *
- * @param udp_payload reference to UDP packet
- * @param udp_payload_length length of @a udp_payload
- * @param off pointer to the offset of the query to parse in the CERT record (to be
- *                    incremented by the size of the record), unchanged on error
- * @return the parsed CERT record, NULL on error
- */
-struct GNUNET_DNSPARSER_CertRecord *
-GNUNET_DNSPARSER_parse_cert (const char *udp_payload,
-                             size_t udp_payload_length,
-                             size_t *off)
-{
-  struct GNUNET_DNSPARSER_CertRecord *cert;
-  struct GNUNET_TUN_DnsCertRecord dcert;
-
-  if (*off + sizeof (struct GNUNET_TUN_DnsCertRecord) >= udp_payload_length)
-  {
-    GNUNET_break_op (0);
-    return NULL;
-  }
-  GNUNET_memcpy (&dcert, &udp_payload[*off], sizeof (struct GNUNET_TUN_DnsCertRecord));
-  (*off) += sizeof (struct GNUNET_TUN_DnsCertRecord);
-  cert = GNUNET_new (struct GNUNET_DNSPARSER_CertRecord);
-  cert->cert_type = ntohs (dcert.cert_type);
-  cert->cert_tag = ntohs (dcert.cert_tag);
-  cert->algorithm = dcert.algorithm;
-  cert->certificate_size = udp_payload_length - (*off);
-  cert->certificate_data = GNUNET_malloc (cert->certificate_size);
-  GNUNET_memcpy (cert->certificate_data,
-          &udp_payload[*off],
-          cert->certificate_size);
-  (*off) += cert->certificate_size;
-  return cert;
-}
-
-
-/**
- * Parse a DNS record entry.
- *
- * @param udp_payload entire UDP payload
- * @param udp_payload_length length of @a udp_payload
- * @param off pointer to the offset of the record to parse in the udp_payload (to be
- *                    incremented by the size of the record)
- * @param r where to write the record information
- * @return #GNUNET_OK on success, #GNUNET_SYSERR if the record is malformed
- */
-int
-GNUNET_DNSPARSER_parse_record (const char *udp_payload,
-                              size_t udp_payload_length,
-                              size_t *off,
-                              struct GNUNET_DNSPARSER_Record *r)
-{
-  char *name;
-  struct GNUNET_TUN_DnsRecordLine rl;
-  size_t old_off;
-  uint16_t data_len;
-
-  name = GNUNET_DNSPARSER_parse_name (udp_payload,
-                                     udp_payload_length,
-                                     off);
-  if (NULL == name)
-  {
-    GNUNET_break_op (0);
-    return GNUNET_SYSERR;
-  }
-  r->name = name;
-  if (*off + sizeof (struct GNUNET_TUN_DnsRecordLine) > udp_payload_length)
-  {
-    GNUNET_break_op (0);
-    return GNUNET_SYSERR;
-  }
-  GNUNET_memcpy (&rl, &udp_payload[*off], sizeof (rl));
-  (*off) += sizeof (rl);
-  r->type = ntohs (rl.type);
-  r->dns_traffic_class = ntohs (rl.dns_traffic_class);
-  r->expiration_time = GNUNET_TIME_relative_to_absolute (GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS,
-                                                                                       ntohl (rl.ttl)));
-  data_len = ntohs (rl.data_len);
-  if (*off + data_len > udp_payload_length)
-  {
-    GNUNET_break_op (0);
-    return GNUNET_SYSERR;
-  }
-  old_off = *off;
-  switch (r->type)
-  {
-  case GNUNET_DNSPARSER_TYPE_NS:
-  case GNUNET_DNSPARSER_TYPE_CNAME:
-  case GNUNET_DNSPARSER_TYPE_DNAME:
-  case GNUNET_DNSPARSER_TYPE_PTR:
-    r->data.hostname = GNUNET_DNSPARSER_parse_name (udp_payload,
-                                                   udp_payload_length,
-                                                   off);
-    if ( (NULL == r->data.hostname) ||
-        (old_off + data_len != *off) )
-      return GNUNET_SYSERR;
-    return GNUNET_OK;
-  case GNUNET_DNSPARSER_TYPE_SOA:
-    r->data.soa = GNUNET_DNSPARSER_parse_soa (udp_payload,
-                                             udp_payload_length,
-                                             off);
-    if ( (NULL == r->data.soa) ||
-        (old_off + data_len != *off) )
-    {
-      GNUNET_break_op (0);
-      return GNUNET_SYSERR;
-    }
-    return GNUNET_OK;
-  case GNUNET_DNSPARSER_TYPE_MX:
-    r->data.mx = GNUNET_DNSPARSER_parse_mx (udp_payload,
-                                           udp_payload_length,
-                                           off);
-    if ( (NULL == r->data.mx) ||
-        (old_off + data_len != *off) )
-    {
-      GNUNET_break_op (0);
-      return GNUNET_SYSERR;
-    }
-    return GNUNET_OK;
-  case GNUNET_DNSPARSER_TYPE_SRV:
-    r->data.srv = GNUNET_DNSPARSER_parse_srv (udp_payload,
-                                             udp_payload_length,
-                                             off);
-    if ( (NULL == r->data.srv) ||
-        (old_off + data_len != *off) )
-    {
-      GNUNET_break_op (0);
-      return GNUNET_SYSERR;
-    }
-    return GNUNET_OK;
-  default:
-    r->data.raw.data = GNUNET_malloc (data_len);
-    r->data.raw.data_len = data_len;
-    GNUNET_memcpy (r->data.raw.data,
-                   &udp_payload[*off],
-                   data_len);
-    break;
-  }
-  (*off) += data_len;
-  return GNUNET_OK;
-}
-
-
-/**
- * Parse a UDP payload of a DNS packet in to a nice struct for further
- * processing and manipulation.
- *
- * @param udp_payload wire-format of the DNS packet
- * @param udp_payload_length number of bytes in @a udp_payload
- * @return NULL on error, otherwise the parsed packet
- */
-struct GNUNET_DNSPARSER_Packet *
-GNUNET_DNSPARSER_parse (const char *udp_payload,
-                       size_t udp_payload_length)
-{
-  struct GNUNET_DNSPARSER_Packet *p;
-  const struct GNUNET_TUN_DnsHeader *dns;
-  size_t off;
-  unsigned int n;
-  unsigned int i;
-
-  if (udp_payload_length < sizeof (struct GNUNET_TUN_DnsHeader))
-    return NULL;
-  dns = (const struct GNUNET_TUN_DnsHeader *) udp_payload;
-  off = sizeof (struct GNUNET_TUN_DnsHeader);
-  p = GNUNET_new (struct GNUNET_DNSPARSER_Packet);
-  p->flags = dns->flags;
-  p->id = dns->id;
-  n = ntohs (dns->query_count);
-  if (n > 0)
-  {
-    p->queries = GNUNET_malloc (n * sizeof (struct GNUNET_DNSPARSER_Query));
-    p->num_queries = n;
-    for (i=0;i<n;i++)
-      if (GNUNET_OK !=
-         GNUNET_DNSPARSER_parse_query (udp_payload,
-                                       udp_payload_length,
-                                       &off,
-                                       &p->queries[i]))
-       goto error;
-  }
-  n = ntohs (dns->answer_rcount);
-  if (n > 0)
-  {
-    p->answers = GNUNET_malloc (n * sizeof (struct GNUNET_DNSPARSER_Record));
-    p->num_answers = n;
-    for (i=0;i<n;i++)
-      if (GNUNET_OK !=
-         GNUNET_DNSPARSER_parse_record (udp_payload,
-                                        udp_payload_length,
-                                        &off,
-                                        &p->answers[i]))
-       goto error;
-  }
-  n = ntohs (dns->authority_rcount);
-  if (n > 0)
-  {
-    p->authority_records = GNUNET_malloc (n * sizeof (struct GNUNET_DNSPARSER_Record));
-    p->num_authority_records = n;
-    for (i=0;i<n;i++)
-      if (GNUNET_OK !=
-         GNUNET_DNSPARSER_parse_record (udp_payload,
-                                        udp_payload_length,
-                                        &off,
-                                        &p->authority_records[i]))
-       goto error;
-  }
-  n = ntohs (dns->additional_rcount);
-  if (n > 0)
-  {
-    p->additional_records = GNUNET_malloc (n * sizeof (struct GNUNET_DNSPARSER_Record));
-    p->num_additional_records = n;
-    for (i=0;i<n;i++)
-      if (GNUNET_OK !=
-         GNUNET_DNSPARSER_parse_record (udp_payload,
-                                        udp_payload_length,
-                                        &off,
-                                        &p->additional_records[i]))
-       goto error;
-  }
-  return p;
- error:
-  GNUNET_break_op (0);
-  GNUNET_DNSPARSER_free_packet (p);
-  return NULL;
-}
-
-
-/**
- * Free memory taken by a packet.
- *
- * @param p packet to free
- */
-void
-GNUNET_DNSPARSER_free_packet (struct GNUNET_DNSPARSER_Packet *p)
-{
-  unsigned int i;
-
-  for (i=0;i<p->num_queries;i++)
-    GNUNET_free_non_null (p->queries[i].name);
-  GNUNET_free_non_null (p->queries);
-  for (i=0;i<p->num_answers;i++)
-    GNUNET_DNSPARSER_free_record (&p->answers[i]);
-  GNUNET_free_non_null (p->answers);
-  for (i=0;i<p->num_authority_records;i++)
-    GNUNET_DNSPARSER_free_record (&p->authority_records[i]);
-  GNUNET_free_non_null (p->authority_records);
-  for (i=0;i<p->num_additional_records;i++)
-    GNUNET_DNSPARSER_free_record (&p->additional_records[i]);
-  GNUNET_free_non_null (p->additional_records);
-  GNUNET_free (p);
-}
-
-
-/* ********************** DNS packet assembly code **************** */
-
-
-/**
- * Add a DNS name to the UDP packet at the given location, converting
- * the name to IDNA notation as necessary.
- *
- * @param dst where to write the name (UDP packet)
- * @param dst_len number of bytes in @a dst
- * @param off pointer to offset where to write the name (increment by bytes used)
- *            must not be changed if there is an error
- * @param name name to write
- * @return #GNUNET_SYSERR if @a name is invalid
- *         #GNUNET_NO if @a name did not fit
- *         #GNUNET_OK if @a name was added to @a dst
- */
-int
-GNUNET_DNSPARSER_builder_add_name (char *dst,
-                                  size_t dst_len,
-                                  size_t *off,
-                                  const char *name)
-{
-  const char *dot;
-  const char *idna_name;
-  char *idna_start;
-  size_t start;
-  size_t pos;
-  size_t len;
-  Idna_rc rc;
-
-  if (NULL == name)
-    return GNUNET_SYSERR;
-
-  if (IDNA_SUCCESS !=
-      (rc = idna_to_ascii_8z (name,
-                              &idna_start,
-                              IDNA_ALLOW_UNASSIGNED)))
-  {
-    GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
-               _("Failed to convert UTF-8 name `%s' to DNS IDNA format: %s\n"),
-               name,
-               idna_strerror (rc));
-    return GNUNET_NO;
-  }
-  idna_name = idna_start;
-  start = *off;
-  if (start + strlen (idna_name) + 2 > dst_len)
-    goto fail;
-  pos = start;
-  do
-  {
-    dot = strchr (idna_name, '.');
-    if (NULL == dot)
-      len = strlen (idna_name);
-    else
-      len = dot - idna_name;
-    if ( (len >= 64) || (0 == len) )
-    {
-      GNUNET_break (0);
-      goto fail; /* segment too long or empty */
-    }
-    dst[pos++] = (char) (uint8_t) len;
-    GNUNET_memcpy (&dst[pos],
-                   idna_name,
-                   len);
-    pos += len;
-    idna_name += len + 1; /* also skip dot */
-  }
-  while (NULL != dot);
-  dst[pos++] = '\0'; /* terminator */
-  *off = pos;
-#if WINDOWS
-  idn_free (idna_start);
-#else
-  free (idna_start);
-#endif
-  return GNUNET_OK;
- fail:
-#if WINDOWS
-  idn_free (idna_start);
-#else
-  free (idna_start);
-#endif
-  return GNUNET_NO;
-}
-
-
-/**
- * Add a DNS query to the UDP packet at the given location.
- *
- * @param dst where to write the query
- * @param dst_len number of bytes in @a dst
- * @param off pointer to offset where to write the query (increment by bytes used)
- *            must not be changed if there is an error
- * @param query query to write
- * @return #GNUNET_SYSERR if @a query is invalid
- *         #GNUNET_NO if @a query did not fit
- *         #GNUNET_OK if @a query was added to @a dst
- */
-int
-GNUNET_DNSPARSER_builder_add_query (char *dst,
-                                   size_t dst_len,
-                                   size_t *off,
-                                   const struct GNUNET_DNSPARSER_Query *query)
-{
-  int ret;
-  struct GNUNET_TUN_DnsQueryLine ql;
-
-  ret = GNUNET_DNSPARSER_builder_add_name (dst, dst_len - sizeof (struct GNUNET_TUN_DnsQueryLine), off, query->name);
-  if (ret != GNUNET_OK)
-    return ret;
-  ql.type = htons (query->type);
-  ql.dns_traffic_class = htons (query->dns_traffic_class);
-  GNUNET_memcpy (&dst[*off], &ql, sizeof (ql));
-  (*off) += sizeof (ql);
-  return GNUNET_OK;
-}
-
-
-/**
- * Add an MX record to the UDP packet at the given location.
- *
- * @param dst where to write the mx record
- * @param dst_len number of bytes in @a dst
- * @param off pointer to offset where to write the mx information (increment by bytes used);
- *            can also change if there was an error
- * @param mx mx information to write
- * @return #GNUNET_SYSERR if @a mx is invalid
- *         #GNUNET_NO if @a mx did not fit
- *         #GNUNET_OK if @a mx was added to @a dst
- */
-int
-GNUNET_DNSPARSER_builder_add_mx (char *dst,
-                                size_t dst_len,
-                                size_t *off,
-                                const struct GNUNET_DNSPARSER_MxRecord *mx)
-{
-  uint16_t mxpref;
-
-  if (*off + sizeof (uint16_t) > dst_len)
-    return GNUNET_NO;
-  mxpref = htons (mx->preference);
-  GNUNET_memcpy (&dst[*off],
-                &mxpref,
-                sizeof (mxpref));
-  (*off) += sizeof (mxpref);
-  return GNUNET_DNSPARSER_builder_add_name (dst,
-                                           dst_len,
-                                           off,
-                                           mx->mxhost);
-}
-
-
-/**
- * Add a CERT record to the UDP packet at the given location.
- *
- * @param dst where to write the CERT record
- * @param dst_len number of bytes in @a dst
- * @param off pointer to offset where to write the CERT information (increment by bytes used);
- *            can also change if there was an error
- * @param cert CERT information to write
- * @return #GNUNET_SYSERR if @a cert is invalid
- *         #GNUNET_NO if @a cert did not fit
- *         #GNUNET_OK if @a cert was added to @a dst
- */
-int
-GNUNET_DNSPARSER_builder_add_cert (char *dst,
-                                   size_t dst_len,
-                                   size_t *off,
-                                   const struct GNUNET_DNSPARSER_CertRecord *cert)
-{
-  struct GNUNET_TUN_DnsCertRecord dcert;
-
-  if ( (cert->cert_type > UINT16_MAX) ||
-       (cert->cert_tag > UINT16_MAX) ||
-       (cert->algorithm > UINT8_MAX) )
-  {
-    GNUNET_break (0);
-    return GNUNET_SYSERR;
-  }
-  if (*off + sizeof (struct GNUNET_TUN_DnsCertRecord) + cert->certificate_size > dst_len)
-    return GNUNET_NO;
-  dcert.cert_type = htons ((uint16_t) cert->cert_type);
-  dcert.cert_tag = htons ((uint16_t) cert->cert_tag);
-  dcert.algorithm = (uint8_t) cert->algorithm;
-  GNUNET_memcpy (&dst[*off], &dcert, sizeof (dcert));
-  (*off) += sizeof (dcert);
-  GNUNET_memcpy (&dst[*off], cert->certificate_data, cert->certificate_size);
-  (*off) += cert->certificate_size;
-  return GNUNET_OK;
-}
-
-
-/**
- * Add an SOA record to the UDP packet at the given location.
- *
- * @param dst where to write the SOA record
- * @param dst_len number of bytes in @a dst
- * @param off pointer to offset where to write the SOA information (increment by bytes used)
- *            can also change if there was an error
- * @param soa SOA information to write
- * @return #GNUNET_SYSERR if @a soa is invalid
- *         #GNUNET_NO if @a soa did not fit
- *         #GNUNET_OK if @a soa was added to @a dst
- */
-int
-GNUNET_DNSPARSER_builder_add_soa (char *dst,
-                                 size_t dst_len,
-                                 size_t *off,
-                                 const struct GNUNET_DNSPARSER_SoaRecord *soa)
-{
-  struct GNUNET_TUN_DnsSoaRecord sd;
-  int ret;
-
-  if ( (GNUNET_OK != (ret = GNUNET_DNSPARSER_builder_add_name (dst,
-                                                               dst_len,
-                                                               off,
-                                                               soa->mname))) ||
-       (GNUNET_OK != (ret = GNUNET_DNSPARSER_builder_add_name (dst,
-                                                               dst_len,
-                                                               off,
-                                                               soa->rname)) ) )
-    return ret;
-  if (*off + sizeof (struct GNUNET_TUN_DnsSoaRecord) > dst_len)
-    return GNUNET_NO;
-  sd.serial = htonl (soa->serial);
-  sd.refresh = htonl (soa->refresh);
-  sd.retry = htonl (soa->retry);
-  sd.expire = htonl (soa->expire);
-  sd.minimum = htonl (soa->minimum_ttl);
-  GNUNET_memcpy (&dst[*off], &sd, sizeof (sd));
-  (*off) += sizeof (sd);
-  return GNUNET_OK;
-}
-
-
-/**
- * Add an SRV record to the UDP packet at the given location.
- *
- * @param dst where to write the SRV record
- * @param dst_len number of bytes in @a dst
- * @param off pointer to offset where to write the SRV information (increment by bytes used)
- *            can also change if there was an error
- * @param srv SRV information to write
- * @return #GNUNET_SYSERR if @a srv is invalid
- *         #GNUNET_NO if @a srv did not fit
- *         #GNUNET_OK if @a srv was added to @a dst
- */
-int
-GNUNET_DNSPARSER_builder_add_srv (char *dst,
-                                 size_t dst_len,
-                                 size_t *off,
-                                 const struct GNUNET_DNSPARSER_SrvRecord *srv)
-{
-  struct GNUNET_TUN_DnsSrvRecord sd;
-  int ret;
-
-  if (*off + sizeof (struct GNUNET_TUN_DnsSrvRecord) > dst_len)
-    return GNUNET_NO;
-  sd.prio = htons (srv->priority);
-  sd.weight = htons (srv->weight);
-  sd.port = htons (srv->port);
-  GNUNET_memcpy (&dst[*off], &sd, sizeof (sd));
-  (*off) += sizeof (sd);
-  if (GNUNET_OK != (ret = GNUNET_DNSPARSER_builder_add_name (dst,
-                                   dst_len,
-                                   off,
-                                   srv->target)))
-    return ret;
-  return GNUNET_OK;
-}
-
-
-/**
- * Add a DNS record to the UDP packet at the given location.
- *
- * @param dst where to write the query
- * @param dst_len number of bytes in @a dst
- * @param off pointer to offset where to write the query (increment by bytes used)
- *            must not be changed if there is an error
- * @param record record to write
- * @return #GNUNET_SYSERR if @a record is invalid
- *         #GNUNET_NO if @a record did not fit
- *         #GNUNET_OK if @a record was added to @a dst
- */
-static int
-add_record (char *dst,
-           size_t dst_len,
-           size_t *off,
-           const struct GNUNET_DNSPARSER_Record *record)
-{
-  int ret;
-  size_t start;
-  size_t pos;
-  struct GNUNET_TUN_DnsRecordLine rl;
-
-  start = *off;
-  ret = GNUNET_DNSPARSER_builder_add_name (dst,
-                                           dst_len - sizeof (struct GNUNET_TUN_DnsRecordLine),
-                                           off,
-                                           record->name);
-  if (GNUNET_OK != ret)
-    return ret;
-  /* '*off' is now the position where we will need to write the record line */
-
-  pos = *off + sizeof (struct GNUNET_TUN_DnsRecordLine);
-  switch (record->type)
-  {
-  case GNUNET_DNSPARSER_TYPE_MX:
-    ret = GNUNET_DNSPARSER_builder_add_mx (dst,
-                                          dst_len,
-                                          &pos,
-                                          record->data.mx);
-    break;
-  case GNUNET_DNSPARSER_TYPE_CERT:
-    ret = GNUNET_DNSPARSER_builder_add_cert (dst,
-                                            dst_len,
-                                            &pos,
-                                            record->data.cert);
-    break;
-  case GNUNET_DNSPARSER_TYPE_SOA:
-    ret = GNUNET_DNSPARSER_builder_add_soa (dst,
-                                           dst_len,
-                                           &pos,
-                                           record->data.soa);
-    break;
-  case GNUNET_DNSPARSER_TYPE_NS:
-  case GNUNET_DNSPARSER_TYPE_CNAME:
-  case GNUNET_DNSPARSER_TYPE_PTR:
-    ret = GNUNET_DNSPARSER_builder_add_name (dst,
-                                            dst_len,
-                                            &pos,
-                                            record->data.hostname);
-    break;
-  case GNUNET_DNSPARSER_TYPE_SRV:
-    ret = GNUNET_DNSPARSER_builder_add_srv (dst,
-                                           dst_len,
-                                           &pos,
-                                           record->data.srv);
-    break;
-  default:
-    if (pos + record->data.raw.data_len > dst_len)
-    {
-      ret = GNUNET_NO;
-      break;
-    }
-    GNUNET_memcpy (&dst[pos],
-                  record->data.raw.data,
-                  record->data.raw.data_len);
-    pos += record->data.raw.data_len;
-    ret = GNUNET_OK;
-    break;
-  }
-  if (GNUNET_OK != ret)
-  {
-    *off = start;
-    return GNUNET_NO;
-  }
-
-  if (pos - (*off + sizeof (struct GNUNET_TUN_DnsRecordLine)) > UINT16_MAX)
-  {
-    /* record data too long */
-    *off = start;
-    return GNUNET_NO;
-  }
-  rl.type = htons (record->type);
-  rl.dns_traffic_class = htons (record->dns_traffic_class);
-  rl.ttl = htonl (GNUNET_TIME_absolute_get_remaining (record->expiration_time).rel_value_us / 1000LL / 1000LL); /* in seconds */
-  rl.data_len = htons ((uint16_t) (pos - (*off + sizeof (struct GNUNET_TUN_DnsRecordLine))));
-  GNUNET_memcpy (&dst[*off], &rl, sizeof (struct GNUNET_TUN_DnsRecordLine));
-  *off = pos;
-  return GNUNET_OK;
-}
-
-
-/**
- * Given a DNS packet @a p, generate the corresponding UDP payload.
- * Note that we do not attempt to pack the strings with pointers
- * as this would complicate the code and this is about being
- * simple and secure, not fast, fancy and broken like bind.
- *
- * @param p packet to pack
- * @param max maximum allowed size for the resulting UDP payload
- * @param buf set to a buffer with the packed message
- * @param buf_length set to the length of @a buf
- * @return #GNUNET_SYSERR if @a p is invalid
- *         #GNUNET_NO if @a p was truncated (but there is still a result in @a buf)
- *         #GNUNET_OK if @a p was packed completely into @a buf
- */
-int
-GNUNET_DNSPARSER_pack (const struct GNUNET_DNSPARSER_Packet *p,
-                      uint16_t max,
-                      char **buf,
-                      size_t *buf_length)
-{
-  struct GNUNET_TUN_DnsHeader dns;
-  size_t off;
-  char tmp[max];
-  unsigned int i;
-  int ret;
-  int trc;
-
-  if ( (p->num_queries > UINT16_MAX) ||
-       (p->num_answers > UINT16_MAX) ||
-       (p->num_authority_records > UINT16_MAX) ||
-       (p->num_additional_records > UINT16_MAX) )
-    return GNUNET_SYSERR;
-  dns.id = p->id;
-  dns.flags = p->flags;
-  dns.query_count = htons (p->num_queries);
-  dns.answer_rcount = htons (p->num_answers);
-  dns.authority_rcount = htons (p->num_authority_records);
-  dns.additional_rcount = htons (p->num_additional_records);
-
-  off = sizeof (struct GNUNET_TUN_DnsHeader);
-  trc = GNUNET_NO;
-  for (i=0;i<p->num_queries;i++)
-  {
-    ret = GNUNET_DNSPARSER_builder_add_query (tmp,
-                                             sizeof (tmp),
-                                             &off,
-                                             &p->queries[i]);
-    if (GNUNET_SYSERR == ret)
-      return GNUNET_SYSERR;
-    if (GNUNET_NO == ret)
-    {
-      dns.query_count = htons ((uint16_t) (i-1));
-      trc = GNUNET_YES;
-      break;
-    }
-  }
-  for (i=0;i<p->num_answers;i++)
-  {
-    ret = add_record (tmp,
-                     sizeof (tmp),
-                     &off,
-                     &p->answers[i]);
-    if (GNUNET_SYSERR == ret)
-      return GNUNET_SYSERR;
-    if (GNUNET_NO == ret)
-    {
-      dns.answer_rcount = htons ((uint16_t) (i-1));
-      trc = GNUNET_YES;
-      break;
-    }
-  }
-  for (i=0;i<p->num_authority_records;i++)
-  {
-    ret = add_record (tmp,
-                     sizeof (tmp),
-                     &off,
-                     &p->authority_records[i]);
-    if (GNUNET_SYSERR == ret)
-      return GNUNET_SYSERR;
-    if (GNUNET_NO == ret)
-    {
-      dns.authority_rcount = htons ((uint16_t) (i-1));
-      trc = GNUNET_YES;
-      break;
-    }
-  }
-  for (i=0;i<p->num_additional_records;i++)
-  {
-    ret = add_record (tmp,
-                     sizeof (tmp),
-                     &off,
-                     &p->additional_records[i]);
-    if (GNUNET_SYSERR == ret)
-      return GNUNET_SYSERR;
-    if (GNUNET_NO == ret)
-    {
-      dns.additional_rcount = htons (i-1);
-      trc = GNUNET_YES;
-      break;
-    }
-  }
-
-  if (GNUNET_YES == trc)
-    dns.flags.message_truncated = 1;
-  GNUNET_memcpy (tmp,
-                &dns,
-                sizeof (struct GNUNET_TUN_DnsHeader));
-
-  *buf = GNUNET_malloc (off);
-  *buf_length = off;
-  GNUNET_memcpy (*buf,
-                tmp,
-                off);
-  if (GNUNET_YES == trc)
-    return GNUNET_NO;
-  return GNUNET_OK;
-}
-
-
-/**
- * Convert a block of binary data to HEX.
- *
- * @param data binary data to convert
- * @param data_size number of bytes in @a data
- * @return HEX string (lower case)
- */
-char *
-GNUNET_DNSPARSER_bin_to_hex (const void *data,
-                             size_t data_size)
-{
-  char *ret;
-  size_t off;
-  const uint8_t *idata;
-
-  idata = data;
-  ret = GNUNET_malloc (data_size * 2 + 1);
-  for (off = 0; off < data_size; off++)
-    sprintf (&ret[off * 2],
-             "%02x",
-             idata[off]);
-  return ret;
-}
-
-
-/**
- * Convert a HEX string to block of binary data.
- *
- * @param hex HEX string to convert (may contain mixed case)
- * @param data where to write result, must be
- *             at least `strlen(hex)/2` bytes long
- * @return number of bytes written to data
- */
-size_t
-GNUNET_DNSPARSER_hex_to_bin (const char *hex,
-                             void *data)
-{
-  size_t data_size;
-  size_t off;
-  uint8_t *idata;
-  unsigned int h;
-  char in[3];
-
-  data_size = strlen (hex) / 2;
-  idata = data;
-  in[2] = '\0';
-  for (off = 0; off < data_size; off++)
-  {
-    in[0] = tolower ((unsigned char) hex[off * 2]);
-    in[1] = tolower ((unsigned char) hex[off * 2 + 1]);
-    if (1 != sscanf (in, "%x", &h))
-      return off;
-    idata[off] = (uint8_t) h;
-  }
-  return off;
-}
-
-
-/* end of dnsparser.c */
diff --git a/src/util/dnsstub.c b/src/util/dnsstub.c
deleted file mode 100644 (file)
index 969ff7b..0000000
+++ /dev/null
@@ -1,749 +0,0 @@
-/*
-     This file is part of GNUnet.
-     Copyright (C) 2012, 2018 GNUnet e.V.
-
-     GNUnet is free software: you can redistribute it and/or modify it
-     under the terms of the GNU Affero General Public License as published
-     by the Free Software Foundation, either version 3 of the License,
-     or (at your option) any later version.
-
-     GNUnet is distributed in the hope that it will be useful, but
-     WITHOUT ANY WARRANTY; without even the implied warranty of
-     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
-     Affero General Public License for more details.
-    
-     You should have received a copy of the GNU Affero General Public License
-     along with this program.  If not, see <http://www.gnu.org/licenses/>.
-*/
-/**
- * @file dns/dnsstub.c
- * @brief DNS stub resolver which sends DNS requests to an actual resolver
- * @author Christian Grothoff
- */
-#include "platform.h"
-#include "gnunet_util_lib.h"
-#include "gnunet_tun_lib.h"
-#include "gnunet_dnsstub_lib.h"
-
-/**
- * Timeout for retrying DNS queries.
- */
-#define DNS_RETRANSMIT_DELAY GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_MILLISECONDS, 250)
-
-
-/**
- * DNS Server used for resolution.
- */
-struct DnsServer;
-
-
-/**
- * UDP socket we are using for sending DNS requests to the Internet.
- */
-struct GNUNET_DNSSTUB_RequestSocket
-{
-
-  /**
-   * UDP socket we use for this request for IPv4
-   */
-  struct GNUNET_NETWORK_Handle *dnsout4;
-
-  /**
-   * UDP socket we use for this request for IPv6
-   */
-  struct GNUNET_NETWORK_Handle *dnsout6;
-
-  /**
-   * Function to call with result.
-   */
-  GNUNET_DNSSTUB_ResultCallback rc;
-
-  /**
-   * Closure for @e rc.
-   */
-  void *rc_cls;
-
-  /**
-   * Task for reading from dnsout4 and dnsout6.
-   */
-  struct GNUNET_SCHEDULER_Task *read_task;
-
-  /**
-   * Task for retrying transmission of the query.
-   */
-  struct GNUNET_SCHEDULER_Task *retry_task;
-
-  /**
-   * Next address we sent the DNS request to.
-   */
-  struct DnsServer *ds_pos;
-
-  /**
-   * Context this request executes in.
-   */
-  struct GNUNET_DNSSTUB_Context *ctx;
-
-  /**
-   * Query we sent to @e addr.
-   */
-  void *request;
-
-  /**
-   * Number of bytes in @a request.
-   */
-  size_t request_len;
-
-};
-
-
-/**
- * DNS Server used for resolution.
- */
-struct DnsServer
-{
-
-  /**
-   * Kept in a DLL.
-   */
-  struct DnsServer *next;
-
-  /**
-   * Kept in a DLL.
-   */
-  struct DnsServer *prev;
-
-  /**
-   * IP address of the DNS resolver.
-   */
-  struct sockaddr_storage ss;
-};
-
-
-/**
- * Handle to the stub resolver.
- */
-struct GNUNET_DNSSTUB_Context
-{
-
-  /**
-   * Array of all open sockets for DNS requests.
-   */
-  struct GNUNET_DNSSTUB_RequestSocket *sockets;
-
-  /**
-   * DLL of DNS resolvers we use.
-   */
-  struct DnsServer *dns_head;
-
-  /**
-   * DLL of DNS resolvers we use.
-   */
-  struct DnsServer *dns_tail;
-
-  /**
-   * How frequently do we retry requests?
-   */
-  struct GNUNET_TIME_Relative retry_freq;
-
-  /**
-   * Length of @e sockets array.
-   */
-  unsigned int num_sockets;
-
-};
-
-
-/**
- * We're done with a `struct GNUNET_DNSSTUB_RequestSocket`, close it for now.
- *
- * @param rs request socket to clean up
- */
-static void
-cleanup_rs (struct GNUNET_DNSSTUB_RequestSocket *rs)
-{
-  if (NULL != rs->dnsout4)
-  {
-    GNUNET_NETWORK_socket_close (rs->dnsout4);
-    rs->dnsout4 = NULL;
-  }
-  if (NULL != rs->dnsout6)
-  {
-    GNUNET_NETWORK_socket_close (rs->dnsout6);
-    rs->dnsout6 = NULL;
-  }
-  if (NULL != rs->read_task)
-  {
-    GNUNET_SCHEDULER_cancel (rs->read_task);
-    rs->read_task = NULL;
-  }
-  if (NULL != rs->retry_task)
-  {
-    GNUNET_SCHEDULER_cancel (rs->retry_task);
-    rs->retry_task = NULL;
-  }
-  if (NULL != rs->request)
-  {
-    GNUNET_free (rs->request);
-    rs->request = NULL;
-  }
-}
-
-
-/**
- * Open source port for sending DNS requests
- *
- * @param af AF_INET or AF_INET6
- * @return #GNUNET_OK on success
- */
-static struct GNUNET_NETWORK_Handle *
-open_socket (int af)
-{
-  struct sockaddr_in a4;
-  struct sockaddr_in6 a6;
-  struct sockaddr *sa;
-  socklen_t alen;
-  struct GNUNET_NETWORK_Handle *ret;
-
-  ret = GNUNET_NETWORK_socket_create (af, SOCK_DGRAM, 0);
-  if (NULL == ret)
-    return NULL;
-  switch (af)
-  {
-  case AF_INET:
-    memset (&a4, 0, alen = sizeof (struct sockaddr_in));
-    sa = (struct sockaddr *) &a4;
-    break;
-  case AF_INET6:
-    memset (&a6, 0, alen = sizeof (struct sockaddr_in6));
-    sa = (struct sockaddr *) &a6;
-    break;
-  default:
-    GNUNET_break (0);
-    GNUNET_NETWORK_socket_close (ret);
-    return NULL;
-  }
-  sa->sa_family = af;
-  if (GNUNET_OK != GNUNET_NETWORK_socket_bind (ret,
-                                              sa,
-                                              alen))
-  {
-    GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
-               _("Could not bind to any port: %s\n"),
-               STRERROR (errno));
-    GNUNET_NETWORK_socket_close (ret);
-    return NULL;
-  }
-  return ret;
-}
-
-
-/**
- * Get a socket of the specified address family to send out a
- * UDP DNS request to the Internet.
- *
- * @param ctx the DNSSTUB context
- * @return NULL on error
- */
-static struct GNUNET_DNSSTUB_RequestSocket *
-get_request_socket (struct GNUNET_DNSSTUB_Context *ctx)
-{
-  struct GNUNET_DNSSTUB_RequestSocket *rs;
-
-  for (unsigned int i=0;i<256;i++)
-  {
-    rs = &ctx->sockets[GNUNET_CRYPTO_random_u32 (GNUNET_CRYPTO_QUALITY_NONCE,
-                                                 ctx->num_sockets)];
-    if (NULL == rs->rc)
-      break;
-  }
-  if (NULL != rs->rc)
-  {
-    /* signal "failure" */
-    rs->rc (rs->rc_cls,
-            NULL,
-            0);
-    rs->rc = NULL;
-  }
-  if (NULL != rs->read_task)
-  {
-    GNUNET_SCHEDULER_cancel (rs->read_task);
-    rs->read_task = NULL;
-  }
-  if (NULL != rs->retry_task)
-  {
-    GNUNET_SCHEDULER_cancel (rs->retry_task);
-    rs->retry_task = NULL;
-  }
-  if (NULL != rs->request)
-  {
-    GNUNET_free (rs->request);
-    rs->request = NULL;
-  }
-  rs->ctx = ctx;
-  return rs;
-}
-
-
-/**
- * Actually do the reading of a DNS packet from our UDP socket and see
- * if we have a valid, matching, pending request.
- *
- * @param rs request socket with callback details
- * @param dnsout socket to read from
- * @return #GNUNET_OK on success, #GNUNET_NO on drop, #GNUNET_SYSERR on IO-errors (closed socket)
- */
-static int
-do_dns_read (struct GNUNET_DNSSTUB_RequestSocket *rs,
-            struct GNUNET_NETWORK_Handle *dnsout)
-{
-  struct GNUNET_DNSSTUB_Context *ctx = rs->ctx;
-  ssize_t r;
-  int len;
-
-#ifndef MINGW
-  if (0 != ioctl (GNUNET_NETWORK_get_fd (dnsout),
-                  FIONREAD,
-                  &len))
-  {
-    /* conservative choice: */
-    len = UINT16_MAX;
-  }
-#else
-  /* port the code above? */
-  len = UINT16_MAX;
-#endif
-  GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
-             "Receiving %d byte DNS reply\n",
-             len);
-  {
-    unsigned char buf[len] GNUNET_ALIGN;
-    int found;
-    struct sockaddr_storage addr;
-    socklen_t addrlen;
-    struct GNUNET_TUN_DnsHeader *dns;
-
-    addrlen = sizeof (addr);
-    memset (&addr,
-            0,
-            sizeof (addr));
-    r = GNUNET_NETWORK_socket_recvfrom (dnsout,
-                                       buf,
-                                        sizeof (buf),
-                                       (struct sockaddr*) &addr,
-                                        &addrlen);
-    if (-1 == r)
-    {
-      GNUNET_log_strerror (GNUNET_ERROR_TYPE_ERROR,
-                           "recvfrom");
-      GNUNET_NETWORK_socket_close (dnsout);
-      return GNUNET_SYSERR;
-    }
-    found = GNUNET_NO;
-    for (struct DnsServer *ds = ctx->dns_head; NULL != ds; ds = ds->next)
-    {
-      if (0 == memcmp (&addr,
-                       &ds->ss,
-                       GNUNET_MIN (sizeof (struct sockaddr_storage),
-                                   addrlen)))
-      {
-        found = GNUNET_YES;
-        break;
-      }
-    }
-    if (GNUNET_NO == found)
-    {
-      GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
-                 "Received DNS response from server we never asked (ignored)");
-      return GNUNET_NO;
-    }
-    if (sizeof (struct GNUNET_TUN_DnsHeader) > r)
-    {
-      GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
-                 _("Received DNS response that is too small (%u bytes)"),
-                 (unsigned int) r);
-      return GNUNET_NO;
-    }
-    dns = (struct GNUNET_TUN_DnsHeader *) buf;
-    if (NULL == rs->rc)
-    {
-      GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
-                 "Request timeout or cancelled; ignoring reply\n");
-      return GNUNET_NO;
-    }
-    rs->rc (rs->rc_cls,
-            dns,
-            r);
-  }
-  return GNUNET_OK;
-}
-
-
-/**
- * Read a DNS response from the (unhindered) UDP-Socket
- *
- * @param cls socket to read from
- */
-static void
-read_response (void *cls);
-
-
-/**
- * Schedule #read_response() task for @a rs.
- *
- * @param rs request to schedule read operation for
- */
-static void
-schedule_read (struct GNUNET_DNSSTUB_RequestSocket *rs)
-{
-  struct GNUNET_NETWORK_FDSet *rset;
-
-  if (NULL != rs->read_task)
-    GNUNET_SCHEDULER_cancel (rs->read_task);
-  rset = GNUNET_NETWORK_fdset_create ();
-  if (NULL != rs->dnsout4)
-    GNUNET_NETWORK_fdset_set (rset,
-                              rs->dnsout4);
-  if (NULL != rs->dnsout6)
-    GNUNET_NETWORK_fdset_set (rset,
-                              rs->dnsout6);
-  rs->read_task = GNUNET_SCHEDULER_add_select (GNUNET_SCHEDULER_PRIORITY_DEFAULT,
-                                              GNUNET_TIME_UNIT_FOREVER_REL,
-                                              rset,
-                                              NULL,
-                                              &read_response,
-                                               rs);
-  GNUNET_NETWORK_fdset_destroy (rset);
-}
-
-
-/**
- * Read a DNS response from the (unhindered) UDP-Socket
- *
- * @param cls `struct GNUNET_DNSSTUB_RequestSocket` to read from
- */
-static void
-read_response (void *cls)
-{
-  struct GNUNET_DNSSTUB_RequestSocket *rs = cls;
-  const struct GNUNET_SCHEDULER_TaskContext *tc;
-
-  rs->read_task = NULL;
-  tc = GNUNET_SCHEDULER_get_task_context ();
-  /* read and process ready sockets */
-  if ( (NULL != rs->dnsout4) &&
-       (GNUNET_NETWORK_fdset_isset (tc->read_ready,
-                                    rs->dnsout4)) &&
-       (GNUNET_SYSERR ==
-        do_dns_read (rs,
-                     rs->dnsout4)) )
-    rs->dnsout4 = NULL;
-  if ( (NULL != rs->dnsout6) &&
-       (GNUNET_NETWORK_fdset_isset (tc->read_ready,
-                                    rs->dnsout6)) &&
-       (GNUNET_SYSERR ==
-        do_dns_read (rs,
-                     rs->dnsout6)) )
-    rs->dnsout6 = NULL;
-  /* re-schedule read task */
-  schedule_read (rs);
-}
-
-
-/**
- * Task to (re)transmit the DNS query, possibly repeatedly until
- * we succeed.
- *
- * @param cls our `struct GNUNET_DNSSTUB_RequestSocket *`
- */
-static void
-transmit_query (void *cls)
-{
-  struct GNUNET_DNSSTUB_RequestSocket *rs = cls;
-  struct GNUNET_DNSSTUB_Context *ctx = rs->ctx;
-  const struct sockaddr *sa;
-  socklen_t salen;
-  struct DnsServer *ds;
-  struct GNUNET_NETWORK_Handle *dnsout;
-
-  rs->retry_task = GNUNET_SCHEDULER_add_delayed (ctx->retry_freq,
-                                                 &transmit_query,
-                                                 rs);
-  ds = rs->ds_pos;
-  rs->ds_pos = ds->next;
-  if (NULL == rs->ds_pos)
-    rs->ds_pos = ctx->dns_head;
-  GNUNET_assert (NULL != ds);
-  dnsout = NULL;
-  switch (ds->ss.ss_family)
-  {
-  case AF_INET:
-    if (NULL == rs->dnsout4)
-      rs->dnsout4 = open_socket (AF_INET);
-    dnsout = rs->dnsout4;
-    sa = (const struct sockaddr *) &ds->ss;
-    salen = sizeof (struct sockaddr_in);
-    break;
-  case AF_INET6:
-    if (NULL == rs->dnsout6)
-      rs->dnsout6 = open_socket (AF_INET6);
-    dnsout = rs->dnsout6;
-    sa = (const struct sockaddr *) &ds->ss;
-    salen = sizeof (struct sockaddr_in6);
-    break;
-  default:
-    return;
-  }
-  if (NULL == dnsout)
-  {
-    GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
-                "Unable to use configure DNS server, skipping\n");
-    return;
-  }
-  if (GNUNET_SYSERR ==
-      GNUNET_NETWORK_socket_sendto (dnsout,
-                                   rs->request,
-                                   rs->request_len,
-                                    sa,
-                                    salen))
-    GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
-               _("Failed to send DNS request to %s: %s\n"),
-               GNUNET_a2s (sa,
-                            salen),
-                STRERROR (errno));
-  else
-    GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
-               _("Sent DNS request to %s\n"),
-               GNUNET_a2s (sa,
-                            salen));
-  schedule_read (rs);
-}
-
-
-/**
- * Perform DNS resolution using our default IP from init.
- *
- * @param ctx stub resolver to use
- * @param request DNS request to transmit
- * @param request_len number of bytes in msg
- * @param rc function to call with result
- * @param rc_cls closure for 'rc'
- * @return socket used for the request, NULL on error
- */
-struct GNUNET_DNSSTUB_RequestSocket *
-GNUNET_DNSSTUB_resolve (struct GNUNET_DNSSTUB_Context *ctx,
-                        const void *request,
-                        size_t request_len,
-                        GNUNET_DNSSTUB_ResultCallback rc,
-                        void *rc_cls)
-{
-  struct GNUNET_DNSSTUB_RequestSocket *rs;
-
-  if (NULL == ctx->dns_head)
-  {
-    GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
-                "No DNS server configured for resolution\n");
-    return NULL;
-  }
-  if (NULL == (rs = get_request_socket (ctx)))
-  {
-    GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
-                "No request socket available for DNS resolution\n");
-    return NULL;
-  }
-  rs->ds_pos = ctx->dns_head;
-  rs->rc = rc;
-  rs->rc_cls = rc_cls;
-  rs->request = GNUNET_memdup (request,
-                               request_len);
-  rs->request_len = request_len;
-  rs->retry_task = GNUNET_SCHEDULER_add_now (&transmit_query,
-                                             rs);
-  return rs;
-}
-
-
-/**
- * Cancel DNS resolution.
- *
- * @param rs resolution to cancel
- */
-void
-GNUNET_DNSSTUB_resolve_cancel (struct GNUNET_DNSSTUB_RequestSocket *rs)
-{
-  rs->rc = NULL;
-  if (NULL != rs->retry_task)
-  {
-    GNUNET_SCHEDULER_cancel (rs->retry_task);
-    rs->retry_task = NULL;
-  }
-  if (NULL != rs->read_task)
-  {
-    GNUNET_SCHEDULER_cancel (rs->read_task);
-    rs->read_task = NULL;
-  }
-}
-
-
-/**
- * Start a DNS stub resolver.
- *
- * @param num_sockets how many sockets should we open
- *        in parallel for DNS queries for this stub?
- * @return NULL on error
- */
-struct GNUNET_DNSSTUB_Context *
-GNUNET_DNSSTUB_start (unsigned int num_sockets)
-{
-  struct GNUNET_DNSSTUB_Context *ctx;
-
-  if (0 == num_sockets)
-  {
-    GNUNET_break (0);
-    return NULL;
-  }
-  ctx = GNUNET_new (struct GNUNET_DNSSTUB_Context);
-  ctx->num_sockets = num_sockets;
-  ctx->sockets = GNUNET_new_array (num_sockets,
-                                   struct GNUNET_DNSSTUB_RequestSocket);
-  ctx->retry_freq = DNS_RETRANSMIT_DELAY;
-  return ctx;
-}
-
-
-/**
- * Add nameserver for use by the DNSSTUB.  We will use
- * all provided nameservers for resolution (round-robin).
- *
- * @param ctx resolver context to modify
- * @param dns_ip target IP address to use (as string)
- * @return #GNUNET_OK on success
- */
-int
-GNUNET_DNSSTUB_add_dns_ip (struct GNUNET_DNSSTUB_Context *ctx,
-                           const char *dns_ip)
-{
-  struct DnsServer *ds;
-  struct in_addr i4;
-  struct in6_addr i6;
-
-  ds = GNUNET_new (struct DnsServer);
-  if (1 == inet_pton (AF_INET,
-                      dns_ip,
-                      &i4))
-  {
-    struct sockaddr_in *s4 = (struct sockaddr_in *) &ds->ss;
-
-    s4->sin_family = AF_INET;
-    s4->sin_port = htons (53);
-    s4->sin_addr = i4;
-#if HAVE_SOCKADDR_IN_SIN_LEN
-    s4->sin_len = (u_char) sizeof (struct sockaddr_in);
-#endif
-  }
-  else if (1 == inet_pton (AF_INET6,
-                           dns_ip,
-                           &i6))
-  {
-    struct sockaddr_in6 *s6 = (struct sockaddr_in6 *) &ds->ss;
-
-    s6->sin6_family = AF_INET6;
-    s6->sin6_port = htons (53);
-    s6->sin6_addr = i6;
-#if HAVE_SOCKADDR_IN_SIN_LEN
-    s6->sin6_len = (u_char) sizeof (struct sockaddr_in6);
-#endif
-  }
-  else
-  {
-    GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
-                "Malformed IP address `%s' for DNS server\n",
-                dns_ip);
-    GNUNET_free (ds);
-    return GNUNET_SYSERR;
-  }
-  GNUNET_CONTAINER_DLL_insert (ctx->dns_head,
-                               ctx->dns_tail,
-                               ds);
-  return GNUNET_OK;
-}
-
-
-/**
- * Add nameserver for use by the DNSSTUB.  We will use
- * all provided nameservers for resolution (round-robin).
- *
- * @param ctx resolver context to modify
- * @param sa socket address of DNS resolver to use
- * @return #GNUNET_OK on success
- */
-int
-GNUNET_DNSSTUB_add_dns_sa (struct GNUNET_DNSSTUB_Context *ctx,
-                           const struct sockaddr *sa)
-{
-  struct DnsServer *ds;
-
-  ds = GNUNET_new (struct DnsServer);
-  switch (sa->sa_family)
-  {
-  case AF_INET:
-    GNUNET_memcpy (&ds->ss,
-                   sa,
-                   sizeof (struct sockaddr_in));
-    break;
-  case AF_INET6:
-    GNUNET_memcpy (&ds->ss,
-                   sa,
-                   sizeof (struct sockaddr_in6));
-    break;
-  default:
-    GNUNET_break (0);
-    GNUNET_free (ds);
-    return GNUNET_SYSERR;
-  }
-  GNUNET_CONTAINER_DLL_insert (ctx->dns_head,
-                               ctx->dns_tail,
-                               ds);
-  return GNUNET_OK;
-}
-
-
-/**
- * How long should we try requests before timing out?
- * Only effective for requests issued after this call.
- *
- * @param ctx resolver context to modify
- * @param retry_freq how long to wait between retries
- */
-void
-GNUNET_DNSSTUB_set_retry (struct GNUNET_DNSSTUB_Context *ctx,
-                          struct GNUNET_TIME_Relative retry_freq)
-{
-  ctx->retry_freq = retry_freq;
-}
-
-
-/**
- * Cleanup DNSSTUB resolver.
- *
- * @param ctx stub resolver to clean up
- */
-void
-GNUNET_DNSSTUB_stop (struct GNUNET_DNSSTUB_Context *ctx)
-{
-  struct DnsServer *ds;
-
-  while (NULL != (ds = ctx->dns_head))
-  {
-    GNUNET_CONTAINER_DLL_remove (ctx->dns_head,
-                                 ctx->dns_tail,
-                                 ds);
-    GNUNET_free (ds);
-  }
-  for (unsigned int i=0;i<ctx->num_sockets;i++)
-    cleanup_rs (&ctx->sockets[i]);
-  GNUNET_free (ctx->sockets);
-  GNUNET_free (ctx);
-}
-
-
-/* end of dnsstub.c */
diff --git a/src/util/regex.c b/src/util/regex.c
deleted file mode 100644 (file)
index 7565a9e..0000000
+++ /dev/null
@@ -1,834 +0,0 @@
-/*
-     This file is part of GNUnet
-     Copyright (C) 2012, 2013, 2015 GNUnet e.V.
-
-     GNUnet is free software: you can redistribute it and/or modify it
-     under the terms of the GNU Affero General Public License as published
-     by the Free Software Foundation, either version 3 of the License,
-     or (at your option) any later version.
-
-     GNUnet is distributed in the hope that it will be useful, but
-     WITHOUT ANY WARRANTY; without even the implied warranty of
-     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
-     Affero General Public License for more details.
-    
-     You should have received a copy of the GNU Affero General Public License
-     along with this program.  If not, see <http://www.gnu.org/licenses/>.
-*/
-/**
- * @file src/tun/regex.c
- * @brief functions to convert IP networks to regexes
- * @author Maximilian Szengel
- * @author Christian Grothoff
- */
-#include "platform.h"
-#include "gnunet_util_lib.h"
-#include "gnunet_tun_lib.h"
-
-/**
- * 'wildcard', matches all possible values (for HEX encoding).
- */
-#define DOT "(0|1|2|3|4|5|6|7|8|9|A|B|C|D|E|F)"
-
-
-/**
- * Create a regex in @a rxstr from the given @a ip and @a netmask.
- *
- * @param ip IPv4 representation.
- * @param port destination port
- * @param rxstr generated regex, must be at least #GNUNET_TUN_IPV4_REGEXLEN
- *              bytes long.
- */
-void
-GNUNET_TUN_ipv4toregexsearch (const struct in_addr *ip,
-                              uint16_t port,
-                              char *rxstr)
-{
-  GNUNET_snprintf (rxstr,
-                   GNUNET_TUN_IPV4_REGEXLEN,
-                   "4-%04X-%08X",
-                   (unsigned int) port,
-                   ntohl (ip->s_addr));
-}
-
-
-/**
- * Create a regex in @a rxstr from the given @a ipv6 and @a prefixlen.
- *
- * @param ipv6 IPv6 representation.
- * @param port destination port
- * @param rxstr generated regex, must be at least #GNUNET_TUN_IPV6_REGEXLEN
- *              bytes long.
- */
-void
-GNUNET_TUN_ipv6toregexsearch (const struct in6_addr *ipv6,
-                              uint16_t port,
-                              char *rxstr)
-{
-  const uint32_t *addr;
-
-  addr = (const uint32_t *) ipv6;
-  GNUNET_snprintf (rxstr,
-                   GNUNET_TUN_IPV6_REGEXLEN,
-                   "6-%04X-%08X%08X%08X%08X",
-                   (unsigned int) port,
-                   ntohl (addr[0]),
-                   ntohl (addr[1]),
-                   ntohl (addr[2]),
-                   ntohl (addr[3]));
-}
-
-
-/**
- * Convert the given 4-bit (!) number to a regex.
- *
- * @param value the value, only the lowest 4 bits will be looked at
- * @param mask which bits in value are wildcards (any value)?
- */
-static char *
-nibble_to_regex (uint8_t value,
-                 uint8_t mask)
-{
-  char *ret;
-
-  value &= mask;
-  switch (mask)
-  {
-  case 0:
-    return GNUNET_strdup (DOT);
-  case 8:
-    GNUNET_asprintf (&ret,
-                     "(%X|%X|%X|%X|%X|%X|%X|%X)",
-                     value,
-                     value + 1,
-                     value + 2,
-                     value + 3,
-                     value + 4,
-                     value + 5,
-                     value + 6,
-                     value + 7);
-    return ret;
-  case 12:
-    GNUNET_asprintf (&ret,
-                     "(%X|%X|%X|%X)",
-                     value,
-                     value + 1,
-                     value + 2,
-                     value + 3);
-    return ret;
-  case 14:
-    GNUNET_asprintf (&ret,
-                     "(%X|%X)",
-                     value,
-                     value + 1);
-    return ret;
-  case 15:
-    GNUNET_asprintf (&ret,
-                     "%X",
-                     value);
-    return ret;
-  default:
-    GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
-                _("Bad mask: %d\n"),
-                mask);
-    GNUNET_break (0);
-    return NULL;
-  }
-}
-
-
-/**
- * Convert the given 16-bit number to a regex.
- *
- * @param value the value
- * @param mask which bits in value are wildcards (any value)?
- */
-static char *
-num_to_regex (uint16_t value,
-              uint16_t mask)
-{
-  const uint8_t *v = (const uint8_t *) &value;
-  const uint8_t *m = (const uint8_t *) &mask;
-  char *a;
-  char *b;
-  char *c;
-  char *d;
-  char *ret;
-
-  a = nibble_to_regex (v[0] >> 4, m[0] >> 4);
-  b = nibble_to_regex (v[0] & 15, m[0] & 15);
-  c = nibble_to_regex (v[1] >> 4, m[1] >> 4);
-  d = nibble_to_regex (v[1] & 15, m[1] & 15);
-  ret = NULL;
-  if ( (NULL != a) &&
-       (NULL != b) &&
-       (NULL != c) &&
-       (NULL != d) )
-    GNUNET_asprintf (&ret,
-                     "%s%s%s%s",
-                     a, b, c, d);
-  GNUNET_free_non_null (a);
-  GNUNET_free_non_null (b);
-  GNUNET_free_non_null (c);
-  GNUNET_free_non_null (d);
-  return ret;
-}
-
-
-/**
- * Do we need to put parents around the given argument?
- *
- * @param arg part of a regular expression
- * @return #GNUNET_YES if we should parens,
- *         #GNUNET_NO if not
- */
-static int
-needs_parens (const char *arg)
-{
-  size_t off;
-  size_t len;
-  unsigned int op;
-
-  op = 0;
-  len = strlen (arg);
-  for (off=0;off<len;off++)
-  {
-    switch (arg[off])
-    {
-    case '(':
-      op++;
-      break;
-    case ')':
-      GNUNET_assert (op > 0);
-      op--;
-      break;
-    case '|':
-      if (0 == op)
-        return GNUNET_YES;
-      break;
-    default:
-      break;
-    }
-  }
-  return GNUNET_NO;
-}
-
-
-/**
- * Compute port policy for the given range of
- * port numbers.
- *
- * @param start starting offset
- * @param end end offset
- * @param step increment level (power of 16)
- * @param pp port policy to convert
- * @return corresponding regex
- */
-static char *
-compute_policy (unsigned int start,
-                unsigned int end,
-                unsigned int step,
-                const struct GNUNET_STRINGS_PortPolicy *pp)
-{
-  unsigned int i;
-  char before[36]; /* 16 * 2 + 3 dots + 0-terminator */
-  char middlel[33]; /* 16 * 2 + 0-terminator */
-  char middleh[33]; /* 16 * 2 + 0-terminator */
-  char after[36]; /* 16 * 2 + 3 dots + 0-terminator */
-  char beforep[36+2]; /* 16 * 2 + 3 dots + 0-terminator + ()*/
-  char middlehp[33+2]; /* 16 * 2 + 0-terminator + () */
-  char middlelp[33+2]; /* 16 * 2 + 0-terminator + () */
-  char afterp[36+2]; /* 16 * 2 + 3 dots + 0-terminator + () */
-  char dots[5 * strlen (DOT)];
-  char buf[3];
-  char *middle;
-  char *ret;
-  unsigned int xstep;
-  char *recl;
-  char *rech;
-  char *reclp;
-  char *rechp;
-  unsigned int start_port;
-  unsigned int end_port;
-
-  GNUNET_assert (GNUNET_YES == pp->negate_portrange);
-  start_port = pp->start_port;
-  if (1 == start_port)
-    start_port = 0;
-  end_port = pp->end_port;
-  GNUNET_assert ((end - start) / step <= 0xF);
-  before[0] = '\0';
-  middlel[0] = '\0';
-  middleh[0] = '\0';
-  after[0] = '\0';
-  for (i=start;i<=end;i+=step)
-  {
-    GNUNET_snprintf (buf,
-                     sizeof (buf),
-                     "%X|",
-                     (i - start) / step);
-    if (i / step < start_port / step)
-      strcat (before, buf);
-    else if (i / step > end_port / step)
-      strcat (after, buf);
-    else if (i / step == start_port / step)
-      strcat (middlel, buf);
-    else if (i / step == end_port / step)
-      strcat (middleh, buf);
-  }
-  if (strlen (before) > 0)
-    before[strlen (before)-1] = '\0';
-  if (strlen (middlel) > 0)
-    middlel[strlen (middlel)-1] = '\0';
-  if (strlen (middleh) > 0)
-    middleh[strlen (middleh)-1] = '\0';
-  if (strlen (after) > 0)
-    after[strlen (after)-1] = '\0';
-  if (needs_parens (before))
-    GNUNET_snprintf (beforep,
-                     sizeof (beforep),
-                     "(%s)",
-                     before);
-  else
-    strcpy (beforep, before);
-  if (needs_parens (middlel))
-    GNUNET_snprintf (middlelp,
-                     sizeof (middlelp),
-                     "(%s)",
-                     middlel);
-  else
-    strcpy (middlelp, middlel);
-  if (needs_parens (middleh))
-    GNUNET_snprintf (middlehp,
-                     sizeof (middlehp),
-                     "(%s)",
-                     middleh);
-  else
-    strcpy (middlehp, middleh);
-  if (needs_parens (after))
-    GNUNET_snprintf (afterp,
-                     sizeof (afterp),
-                     "(%s)",
-                     after);
-  else
-    strcpy (afterp, after);
-  dots[0] = '\0';
-  for (xstep=step/16;xstep>0;xstep/=16)
-    strcat (dots, DOT);
-  if (step >= 16)
-  {
-    if (strlen (middlel) > 0)
-      recl = compute_policy ((start_port / step) * step,
-                             (start_port / step) * step + step - 1,
-                             step / 16,
-                             pp);
-    else
-      recl = GNUNET_strdup ("");
-    if (strlen (middleh) > 0)
-      rech = compute_policy ((end_port / step) * step,
-                             (end_port / step) * step + step - 1,
-                             step / 16,
-                             pp);
-    else
-      rech = GNUNET_strdup ("");
-  }
-  else
-  {
-    recl = GNUNET_strdup ("");
-    rech = GNUNET_strdup ("");
-    middlel[0] = '\0';
-    middlelp[0] = '\0';
-    middleh[0] = '\0';
-    middlehp[0] = '\0';
-  }
-  if (needs_parens (recl))
-    GNUNET_asprintf (&reclp,
-                     "(%s)",
-                     recl);
-  else
-    reclp = GNUNET_strdup (recl);
-  if (needs_parens (rech))
-    GNUNET_asprintf (&rechp,
-                     "(%s)",
-                     rech);
-  else
-    rechp = GNUNET_strdup (rech);
-
-  if ( (strlen (middleh) > 0) &&
-       (strlen (rech) > 0) &&
-       (strlen (middlel) > 0) &&
-       (strlen (recl) > 0) )
-  {
-    GNUNET_asprintf (&middle,
-                     "%s%s|%s%s",
-                     middlel,
-                     reclp,
-                     middleh,
-                     rechp);
-  }
-  else if ( (strlen (middleh) > 0) &&
-            (strlen (rech) > 0) )
-  {
-    GNUNET_asprintf (&middle,
-                     "%s%s",
-                     middleh,
-                     rechp);
-  }
-  else if ( (strlen (middlel) > 0) &&
-            (strlen (recl) > 0) )
-  {
-    GNUNET_asprintf (&middle,
-                     "%s%s",
-                     middlel,
-                     reclp);
-  }
-  else
-  {
-    middle = GNUNET_strdup ("");
-  }
-  if ( (strlen(before) > 0) &&
-       (strlen(after) > 0) )
-  {
-    if (strlen (dots) > 0)
-    {
-      if (strlen (middle) > 0)
-        GNUNET_asprintf (&ret,
-                         "(%s%s|%s|%s%s)",
-                         beforep, dots,
-                         middle,
-                         afterp, dots);
-      else
-        GNUNET_asprintf (&ret,
-                         "(%s|%s)%s",
-                         beforep,
-                         afterp,
-                         dots);
-    }
-    else
-    {
-      if (strlen (middle) > 0)
-        GNUNET_asprintf (&ret,
-                         "(%s|%s|%s)",
-                         before,
-                         middle,
-                         after);
-      else if (1 == step)
-        GNUNET_asprintf (&ret,
-                         "%s|%s",
-                         before,
-                         after);
-      else
-        GNUNET_asprintf (&ret,
-                         "(%s|%s)",
-                         before,
-                         after);
-    }
-  }
-  else if (strlen (before) > 0)
-  {
-    if (strlen (dots) > 0)
-    {
-      if (strlen (middle) > 0)
-        GNUNET_asprintf (&ret,
-                         "(%s%s|%s)",
-                         beforep, dots,
-                         middle);
-      else
-        GNUNET_asprintf (&ret,
-                         "%s%s",
-                         beforep, dots);
-    }
-    else
-    {
-      if (strlen (middle) > 0)
-        GNUNET_asprintf (&ret,
-                         "(%s|%s)",
-                         before,
-                         middle);
-      else
-        GNUNET_asprintf (&ret,
-                         "%s",
-                         before);
-    }
-  }
-  else if (strlen (after) > 0)
-  {
-    if (strlen (dots) > 0)
-    {
-      if (strlen (middle) > 0)
-        GNUNET_asprintf (&ret,
-                         "(%s|%s%s)",
-                         middle,
-                         afterp, dots);
-      else
-        GNUNET_asprintf (&ret,
-                         "%s%s",
-                         afterp, dots);
-    }
-    else
-    {
-      if (strlen (middle) > 0)
-        GNUNET_asprintf (&ret,
-                         "%s|%s",
-                         middle,
-                         after);
-      else
-        GNUNET_asprintf (&ret,
-                         "%s",
-                         after);
-    }
-  }
-  else if (strlen (middle) > 0)
-  {
-    GNUNET_asprintf (&ret,
-                     "%s",
-                     middle);
-  }
-  else
-  {
-    ret = GNUNET_strdup ("");
-  }
-  GNUNET_free (middle);
-  GNUNET_free (reclp);
-  GNUNET_free (rechp);
-  GNUNET_free (recl);
-  GNUNET_free (rech);
-  return ret;
-}
-
-
-/**
- * Convert a port policy to a regular expression.  Note: this is a
- * very simplistic implementation, we might want to consider doing
- * something more sophisiticated (resulting in smaller regular
- * expressions) at a later time.
- *
- * @param pp port policy to convert
- * @return NULL on error
- */
-static char *
-port_to_regex (const struct GNUNET_STRINGS_PortPolicy *pp)
-{
-  char *reg;
-  char *ret;
-  char *pos;
-  unsigned int i;
-  unsigned int cnt;
-
-  if ( (0 == pp->start_port) ||
-       ( (1 == pp->start_port) &&
-         (0xFFFF == pp->end_port) &&
-         (GNUNET_NO == pp->negate_portrange)) )
-    return GNUNET_strdup (DOT DOT DOT DOT);
-  if ( (pp->start_port == pp->end_port) &&
-       (GNUNET_NO == pp->negate_portrange))
-  {
-    GNUNET_asprintf (&ret,
-                     "%04X",
-                     pp->start_port);
-    return ret;
-  }
-  if (pp->end_port < pp->start_port)
-    return NULL;
-
-  if (GNUNET_YES == pp->negate_portrange)
-  {
-    ret = compute_policy (0, 0xFFFF, 0x1000, pp);
-  }
-  else
-  {
-    cnt = pp->end_port - pp->start_port + 1;
-    reg = GNUNET_malloc (cnt * 5 + 1);
-    pos = reg;
-    for (i=1;i<=0xFFFF;i++)
-    {
-      if ( (i >= pp->start_port) && (i <= pp->end_port) )
-      {
-        if (pos == reg)
-        {
-          GNUNET_snprintf (pos,
-                           5,
-                           "%04X",
-                           i);
-        }
-        else
-        {
-          GNUNET_snprintf (pos,
-                           6,
-                           "|%04X",
-                           i);
-        }
-        pos += strlen (pos);
-      }
-    }
-    GNUNET_asprintf (&ret,
-                     "(%s)",
-                     reg);
-    GNUNET_free (reg);
-  }
-  return ret;
-}
-
-
-/**
- * Convert an address (IPv4 or IPv6) to a regex.
- *
- * @param addr address
- * @param mask network mask
- * @param len number of bytes in @a addr and @a mask
- * @return NULL on error, otherwise regex for the address
- */
-static char *
-address_to_regex (const void *addr,
-                  const void *mask,
-                  size_t len)
-{
-  const uint16_t *a = addr;
-  const uint16_t *m = mask;
-  char *ret;
-  char *tmp;
-  char *reg;
-  unsigned int i;
-
-  ret = NULL;
-  GNUNET_assert (1 != (len % 2));
-  for (i=0;i<len / 2;i++)
-  {
-    reg = num_to_regex (a[i], m[i]);
-    if (NULL == reg)
-    {
-      GNUNET_free_non_null (ret);
-      return NULL;
-    }
-    if (NULL == ret)
-    {
-      ret = reg;
-    }
-    else
-    {
-      GNUNET_asprintf (&tmp,
-                       "%s%s",
-                       ret, reg);
-      GNUNET_free (ret);
-      GNUNET_free (reg);
-      ret = tmp;
-    }
-  }
-  return ret;
-}
-
-
-/**
- * Convert a single line of an IPv4 policy to a regular expression.
- *
- * @param v4 line to convert
- * @return NULL on error
- */
-static char *
-ipv4_to_regex (const struct GNUNET_STRINGS_IPv4NetworkPolicy *v4)
-{
-  char *reg;
-  char *pp;
-  char *ret;
-
-  reg = address_to_regex (&v4->network,
-                          &v4->netmask,
-                          sizeof (struct in_addr));
-  if (NULL == reg)
-    return NULL;
-  pp = port_to_regex (&v4->pp);
-  if (NULL == pp)
-  {
-    GNUNET_free (reg);
-    return NULL;
-  }
-  GNUNET_asprintf (&ret,
-                   "4-%s-%s",
-                   pp, reg);
-  GNUNET_free (pp);
-  GNUNET_free (reg);
-  return ret;
-}
-
-
-/**
- * Convert a single line of an IPv4 policy to a regular expression.
- *
- * @param v6 line to convert
- * @return NULL on error
- */
-static char *
-ipv6_to_regex (const struct GNUNET_STRINGS_IPv6NetworkPolicy *v6)
-{
-  char *reg;
-  char *pp;
-  char *ret;
-
-  reg = address_to_regex (&v6->network,
-                          &v6->netmask,
-                          sizeof (struct in6_addr));
-  if (NULL == reg)
-    return NULL;
-  pp = port_to_regex (&v6->pp);
-  if (NULL == pp)
-  {
-    GNUNET_free (reg);
-    return NULL;
-  }
-  GNUNET_asprintf (&ret,
-                   "6-%s-%s",
-                   pp, reg);
-  GNUNET_free (pp);
-  GNUNET_free (reg);
-  return ret;
-}
-
-
-/**
- * Convert an exit policy to a regular expression.  The exit policy
- * specifies a set of subnets this peer is willing to serve as an
- * exit for; the resulting regular expression will match the
- * IPv4 address strings as returned by #GNUNET_TUN_ipv4toregexsearch().
- *
- * @param policy exit policy specification
- * @return regular expression, NULL on error
- */
-char *
-GNUNET_TUN_ipv4policy2regex (const char *policy)
-{
-  struct GNUNET_STRINGS_IPv4NetworkPolicy *np;
-  char *reg;
-  char *tmp;
-  char *line;
-  unsigned int i;
-
-  np = GNUNET_STRINGS_parse_ipv4_policy (policy);
-  if (NULL == np)
-    return NULL;
-  reg = NULL;
-  for (i=0; (0 == i) || (0 != np[i].network.s_addr); i++)
-  {
-    line = ipv4_to_regex (&np[i]);
-    if (NULL == line)
-    {
-      GNUNET_free_non_null (reg);
-      GNUNET_free (np);
-      return NULL;
-    }
-    if (NULL == reg)
-    {
-      reg = line;
-    }
-    else
-    {
-      GNUNET_asprintf (&tmp,
-                       "%s|(%s)",
-                       reg, line);
-      GNUNET_free (reg);
-      GNUNET_free (line);
-      reg = tmp;
-    }
-    if (0 == np[i].network.s_addr)
-      break;
-  }
-  GNUNET_free (np);
-  return reg;
-}
-
-
-/**
- * Convert an exit policy to a regular expression.  The exit policy
- * specifies a set of subnets this peer is willing to serve as an
- * exit for; the resulting regular expression will match the
- * IPv6 address strings as returned by #GNUNET_TUN_ipv6toregexsearch().
- *
- * @param policy exit policy specification
- * @return regular expression, NULL on error
- */
-char *
-GNUNET_TUN_ipv6policy2regex (const char *policy)
-{
-  struct in6_addr zero;
-  struct GNUNET_STRINGS_IPv6NetworkPolicy *np;
-  char *reg;
-  char *tmp;
-  char *line;
-  unsigned int i;
-
-  np = GNUNET_STRINGS_parse_ipv6_policy (policy);
-  if (NULL == np)
-    return NULL;
-  reg = NULL;
-  memset (&zero, 0, sizeof (struct in6_addr));
-  for (i=0; (0 == i) || (0 != memcmp (&zero, &np[i].network, sizeof (struct in6_addr))); i++)
-  {
-    line = ipv6_to_regex (&np[i]);
-    if (NULL == line)
-    {
-      GNUNET_free_non_null (reg);
-      GNUNET_free (np);
-      return NULL;
-    }
-    if (NULL == reg)
-    {
-      reg = line;
-    }
-    else
-    {
-      GNUNET_asprintf (&tmp,
-                       "%s|(%s)",
-                       reg, line);
-      GNUNET_free (reg);
-      GNUNET_free (line);
-      reg = tmp;
-    }
-    if (0 == memcmp (&zero, &np[i].network, sizeof (struct in6_addr)))
-      break;
-  }
-  GNUNET_free (np);
-  return reg;
-}
-
-
-/**
- * Hash the service name of a hosted service to the
- * hash code that is used to identify the service on
- * the network.
- *
- * @param service_name a string
- * @param hc corresponding hash
- */
-void
-GNUNET_TUN_service_name_to_hash (const char *service_name,
-                                 struct GNUNET_HashCode *hc)
-{
-  GNUNET_CRYPTO_hash (service_name,
-                      strlen (service_name),
-                      hc);
-}
-
-
-/**
- * Compute the CADET port given a service descriptor
- * (returned from #GNUNET_TUN_service_name_to_hash) and
- * a TCP/UDP port @a ip_port.
- *
- * @param desc service shared secret
- * @param ip_port TCP/UDP port, use 0 for ICMP
- * @param[out] cadet_port CADET port to use
- */
-void
-GNUNET_TUN_compute_service_cadet_port (const struct GNUNET_HashCode *desc,
-                                       uint16_t ip_port,
-                                       struct GNUNET_HashCode *cadet_port)
-{
-  uint16_t be_port = htons (ip_port);
-
-  *cadet_port = *desc;
-  GNUNET_memcpy (cadet_port,
-                 &be_port,
-                 sizeof (uint16_t));
-}
-
-
-/* end of regex.c */
diff --git a/src/util/test_hexcoder.c b/src/util/test_hexcoder.c
deleted file mode 100644 (file)
index 441d7e2..0000000
+++ /dev/null
@@ -1,54 +0,0 @@
-/*
-     This file is part of GNUnet.
-     Copyright (C) 2014 GNUnet e.V.
-
-     GNUnet is free software: you can redistribute it and/or modify it
-     under the terms of the GNU Affero General Public License as published
-     by the Free Software Foundation, either version 3 of the License,
-     or (at your option) any later version.
-
-     GNUnet is distributed in the hope that it will be useful, but
-     WITHOUT ANY WARRANTY; without even the implied warranty of
-     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
-     Affero General Public License for more details.
-    
-     You should have received a copy of the GNU Affero General Public License
-     along with this program.  If not, see <http://www.gnu.org/licenses/>.
-
-*/
-/**
- * @author Christian Grothoff
- * @file dns/test_hexcoder.c
- * @brief test for #GNUNET_DNSPARSER_hex_to_bin() and
- *                 #GNUNET_DNSPARSER_bin_to_hex()
- */
-#include "platform.h"
-#include "gnunet_util_lib.h"
-#include "gnunet_dnsparser_lib.h"
-
-#define TESTSTRING "Hello World!"
-
-
-int
-main (int argc,
-      char *argv[])
-{
-  char buf[strlen (TESTSTRING)  + 1];
-  char *ret;
-
-  GNUNET_log_setup ("test-hexcoder", "WARNING", NULL);
-  ret = GNUNET_DNSPARSER_bin_to_hex (TESTSTRING,
-                                     strlen (TESTSTRING) + 1);
-  GNUNET_assert (NULL != ret);
-  GNUNET_assert (sizeof (buf) ==
-                 GNUNET_DNSPARSER_hex_to_bin (ret,
-                                              buf));
-  GNUNET_assert (0 == memcmp (TESTSTRING,
-                              buf,
-                              sizeof (buf)));
-  GNUNET_free (ret);
-  return 0;
-}
-
-
-/* end of test_hexcoder.c */
diff --git a/src/util/test_regex.c b/src/util/test_regex.c
deleted file mode 100644 (file)
index 2e7d528..0000000
+++ /dev/null
@@ -1,179 +0,0 @@
-/*
-     This file is part of GNUnet
-     Copyright (C) 2012, 2013 GNUnet e.V.
-
-     GNUnet is free software: you can redistribute it and/or modify it
-     under the terms of the GNU Affero General Public License as published
-     by the Free Software Foundation, either version 3 of the License,
-     or (at your option) any later version.
-
-     GNUnet is distributed in the hope that it will be useful, but
-     WITHOUT ANY WARRANTY; without even the implied warranty of
-     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
-     Affero General Public License for more details.
-    
-     You should have received a copy of the GNU Affero General Public License
-     along with this program.  If not, see <http://www.gnu.org/licenses/>.
-*/
-/**
- * @file tun/test_regex.c
- * @brief simple test for regex.c iptoregex functions
- * @author Maximilian Szengel
- */
-#include "platform.h"
-#include "gnunet_tun_lib.h"
-
-/**
- * 'wildcard', matches all possible values (for HEX encoding).
- */
-#define DOT "(0|1|2|3|4|5|6|7|8|9|A|B|C|D|E|F)"
-
-
-static int
-test_iptoregex (const char *ipv4,
-                uint16_t port,
-                const char *expectedv4,
-                const char *ipv6,
-                uint16_t port6,
-                const char *expectedv6)
-{
-  int error = 0;
-
-  struct in_addr a;
-  struct in6_addr b;
-  char rxv4[GNUNET_TUN_IPV4_REGEXLEN];
-  char rxv6[GNUNET_TUN_IPV6_REGEXLEN];
-
-  GNUNET_assert (1 == inet_pton (AF_INET, ipv4, &a));
-  GNUNET_TUN_ipv4toregexsearch (&a, port, rxv4);
-
-  if (0 != strcmp (rxv4, expectedv4))
-  {
-    GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
-                "Expected: %s but got: %s\n",
-                expectedv4,
-                rxv4);
-    error++;
-  }
-
-  GNUNET_assert (1 == inet_pton (AF_INET6, ipv6, &b));
-  GNUNET_TUN_ipv6toregexsearch (&b, port6, rxv6);
-  if (0 != strcmp (rxv6, expectedv6))
-  {
-    GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
-                "Expected: %s but got: %s\n",
-                expectedv6, rxv6);
-    error++;
-  }
-  return error;
-}
-
-
-static int
-test_policy4toregex (const char *policy,
-                     const char *regex)
-{
-  char *r;
-  int ret;
-
-  ret = 0;
-  r = GNUNET_TUN_ipv4policy2regex (policy);
-  if (NULL == r)
-  {
-    GNUNET_break (0);
-    return 1;
-  }
-  if (0 != strcmp (regex, r))
-  {
-    GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
-                "Expected: `%s' but got: `%s'\n",
-                regex, r);
-    ret = 2;
-  }
-  GNUNET_free (r);
-  return ret;
-}
-
-
-static int
-test_policy6toregex (const char *policy,
-                     const char *regex)
-{
-  char *r;
-  int ret;
-
-  ret = 0;
-  r = GNUNET_TUN_ipv6policy2regex (policy);
-  if (NULL == r)
-  {
-    GNUNET_break (0);
-    return 1;
-  }
-  if (0 != strcmp (regex, r))
-  {
-    GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
-                "Expected: `%s' but got: `%s'\n",
-                regex, r);
-    ret = 2;
-  }
-  GNUNET_free (r);
-  return ret;
-}
-
-
-int
-main (int argc, char *argv[])
-{
-  int error;
-  char *r;
-
-  GNUNET_log_setup ("test-regex", "WARNING", NULL);
-  error = 0;
-
-  /* this is just a performance test ... */
-  r = GNUNET_TUN_ipv4policy2regex ("1.2.3.4/16:!25;");
-  GNUNET_break (NULL != r);
-  GNUNET_free (r);
-
-  error +=
-    test_iptoregex ("192.1.2.3", 2086,
-                    "4-0826-C0010203",
-                    "FFFF::1", 8080,
-                    "6-1F90-FFFF0000000000000000000000000001");
-  error +=
-    test_iptoregex ("187.238.255.0", 80,
-                    "4-0050-BBEEFF00",
-                    "E1E1:73F9:51BE::0", 49,
-                    "6-0031-E1E173F951BE00000000000000000000");
-  error +=
-    test_policy4toregex ("192.1.2.0/24:80;",
-                         "4-0050-C00102" DOT DOT);
-  error +=
-    test_policy4toregex ("192.1.0.0/16;",
-                         "4-" DOT DOT DOT DOT "-C001" DOT DOT DOT DOT);
-  error +=
-    test_policy4toregex ("192.1.0.0/16:80-81;",
-                         "4-(0050|0051)-C001" DOT DOT DOT DOT);
-  error +=
-    test_policy4toregex ("192.1.0.0/8:!3-65535;",
-                         "4-000(0|1|2)-C0" DOT DOT DOT DOT DOT DOT);
-  error +=
-    test_policy4toregex ("192.1.0.0/8:!25-56;",
-                         "4-(0(0(0"DOT"|1(0|1|2|3|4|5|6|7|8)|3(9|A|B|C|D|E|F)|(4|5|6|7|8|9|A|B|C|D|E|F)"DOT")|(1|2|3|4|5|6|7|8|9|A|B|C|D|E|F)"DOT DOT")|(1|2|3|4|5|6|7|8|9|A|B|C|D|E|F)"DOT DOT DOT")-C0"DOT DOT DOT DOT DOT DOT);
-  error +=
-    test_policy6toregex ("E1E1::1;",
-                         "6-"DOT DOT DOT DOT"-E1E10000000000000000000000000001");
-  error +=
-    test_policy6toregex ("E1E1:ABCD::1/120;",
-                         "6-"DOT DOT DOT DOT"-E1E1ABCD0000000000000000000000" DOT DOT);
-  error +=
-    test_policy6toregex ("E1E1:ABCD::ABCD/126;",
-                         "6-"DOT DOT DOT DOT"-E1E1ABCD00000000000000000000ABC(C|D|E|F)");
-  error +=
-    test_policy6toregex ("E1E1:ABCD::ABCD/127;",
-                         "6-"DOT DOT DOT DOT"-E1E1ABCD00000000000000000000ABC(C|D)");
-  error +=
-    test_policy6toregex ("E1E1:ABCD::ABCD/128:80;",
-                         "6-0050-E1E1ABCD00000000000000000000ABCD");
-  return error;
-}
diff --git a/src/util/test_tun.c b/src/util/test_tun.c
deleted file mode 100644 (file)
index edbd4c0..0000000
+++ /dev/null
@@ -1,72 +0,0 @@
-/*
-     This file is part of GNUnet.
-     Copyright (C) 2010, 2011, 2012 Christian Grothoff
-
-     GNUnet is free software: you can redistribute it and/or modify it
-     under the terms of the GNU Affero General Public License as published
-     by the Free Software Foundation, either version 3 of the License,
-     or (at your option) any later version.
-
-     GNUnet is distributed in the hope that it will be useful, but
-     WITHOUT ANY WARRANTY; without even the implied warranty of
-     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
-     Affero General Public License for more details.
-    
-     You should have received a copy of the GNU Affero General Public License
-     along with this program.  If not, see <http://www.gnu.org/licenses/>.
-*/
-
-/**
- * @file tun/test_tun.c
- * @brief test for tun.c
- * @author Christian Grothoff
- */
-#include "platform.h"
-#include "gnunet_tun_lib.h"
-
-static int ret;
-
-static void
-test_udp (size_t pll,
-         int pl_fill,
-         uint16_t crc)
-{
-  struct GNUNET_TUN_IPv4Header ip;
-  struct GNUNET_TUN_UdpHeader udp;
-  char payload[pll];
-  struct in_addr src;
-  struct in_addr dst;
-
-  GNUNET_assert (1 == inet_pton (AF_INET, "1.2.3.4", &src));
-  GNUNET_assert (1 == inet_pton (AF_INET, "122.2.3.5", &dst));
-  memset (payload, pl_fill, sizeof (payload));
-  GNUNET_TUN_initialize_ipv4_header (&ip,
-                                    IPPROTO_UDP,
-                                    pll + sizeof (udp),
-                                    &src,
-                                    &dst);
-  udp.source_port = htons (4242);
-  udp.destination_port = htons (4242);
-  udp.len = htons (pll);
-  GNUNET_TUN_calculate_udp4_checksum (&ip,
-                                     &udp,
-                                     payload,
-                                     pll);
-  if (crc != ntohs (udp.crc))
-  {
-    fprintf (stderr, "Got CRC: %u, wanted: %u\n",
-            ntohs (udp.crc),
-            crc);
-    ret = 1;
-  }
-}
-
-int main (int argc,
-         char **argv)
-{
-  test_udp (4, 3, 22439);
-  test_udp (4, 1, 23467);
-  test_udp (7, 17, 6516);
-  test_udp (12451, 251, 42771);
-  return ret;
-}
diff --git a/src/util/tun.c b/src/util/tun.c
deleted file mode 100644 (file)
index 7c35a76..0000000
+++ /dev/null
@@ -1,309 +0,0 @@
-/*
-     This file is part of GNUnet.
-     Copyright (C) 2010, 2011, 2012 Christian Grothoff
-
-     GNUnet is free software: you can redistribute it and/or modify it
-     under the terms of the GNU Affero General Public License as published
-     by the Free Software Foundation, either version 3 of the License,
-     or (at your option) any later version.
-
-     GNUnet is distributed in the hope that it will be useful, but
-     WITHOUT ANY WARRANTY; without even the implied warranty of
-     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
-     Affero General Public License for more details.
-    
-     You should have received a copy of the GNU Affero General Public License
-     along with this program.  If not, see <http://www.gnu.org/licenses/>.
-*/
-
-/**
- * @file tun/tun.c
- * @brief standard IP calculations for TUN interaction
- * @author Philipp Toelke
- * @author Christian Grothoff
- */
-#include "platform.h"
-#include "gnunet_util_lib.h"
-
-/**
- * IP TTL we use for packets that we assemble (8 bit unsigned integer)
- */
-#define FRESH_TTL 64
-
-
-/**
- * Initialize an IPv4 header.
- *
- * @param ip header to initialize
- * @param protocol protocol to use (i.e. IPPROTO_UDP)
- * @param payload_length number of bytes of payload that follow (excluding IPv4 header)
- * @param src source IP address to use
- * @param dst destination IP address to use
- */
-void
-GNUNET_TUN_initialize_ipv4_header (struct GNUNET_TUN_IPv4Header *ip,
-                                  uint8_t protocol,
-                                  uint16_t payload_length,
-                                  const struct in_addr *src,
-                                  const struct in_addr *dst)
-{
-  GNUNET_assert (20 == sizeof (struct GNUNET_TUN_IPv4Header));
-  GNUNET_assert (payload_length <= UINT16_MAX - sizeof (struct GNUNET_TUN_IPv4Header));
-  memset (ip, 0, sizeof (struct GNUNET_TUN_IPv4Header));
-  ip->header_length =  sizeof (struct GNUNET_TUN_IPv4Header) / 4;
-  ip->version = 4;
-  ip->total_length = htons (sizeof (struct GNUNET_TUN_IPv4Header) + payload_length);
-  ip->identification = (uint16_t) GNUNET_CRYPTO_random_u32 (GNUNET_CRYPTO_QUALITY_WEAK,
-                                                           65536);
-  ip->ttl = FRESH_TTL;
-  ip->protocol = protocol;
-  ip->source_address = *src;
-  ip->destination_address = *dst;
-  ip->checksum = GNUNET_CRYPTO_crc16_n (ip, sizeof (struct GNUNET_TUN_IPv4Header));
-}
-
-
-/**
- * Initialize an IPv6 header.
- *
- * @param ip header to initialize
- * @param protocol protocol to use (i.e. IPPROTO_UDP), technically "next_header" for IPv6
- * @param payload_length number of bytes of payload that follow (excluding IPv6 header)
- * @param src source IP address to use
- * @param dst destination IP address to use
- */
-void
-GNUNET_TUN_initialize_ipv6_header (struct GNUNET_TUN_IPv6Header *ip,
-                                  uint8_t protocol,
-                                  uint16_t payload_length,
-                                  const struct in6_addr *src,
-                                  const struct in6_addr *dst)
-{
-  GNUNET_assert (40 == sizeof (struct GNUNET_TUN_IPv6Header));
-  GNUNET_assert (payload_length <= UINT16_MAX - sizeof (struct GNUNET_TUN_IPv6Header));
-  memset (ip, 0, sizeof (struct GNUNET_TUN_IPv6Header));
-  ip->version = 6;
-  ip->next_header = protocol;
-  ip->payload_length = htons ((uint16_t) payload_length);
-  ip->hop_limit = FRESH_TTL;
-  ip->destination_address = *dst;
-  ip->source_address = *src;
-}
-
-
-/**
- * Calculate IPv4 TCP checksum.
- *
- * @param ip ipv4 header fully initialized
- * @param tcp TCP header (initialized except for CRC)
- * @param payload the TCP payload
- * @param payload_length number of bytes of TCP payload
- */
-void
-GNUNET_TUN_calculate_tcp4_checksum (const struct GNUNET_TUN_IPv4Header *ip,
-                                   struct GNUNET_TUN_TcpHeader *tcp,
-                                   const void *payload,
-                                   uint16_t payload_length)
-{
-  uint32_t sum;
-  uint16_t tmp;
-
-  GNUNET_assert (20 == sizeof (struct GNUNET_TUN_TcpHeader));
-  GNUNET_assert (payload_length + sizeof (struct GNUNET_TUN_IPv4Header) + sizeof (struct GNUNET_TUN_TcpHeader) ==
-                ntohs (ip->total_length));
-  GNUNET_assert (IPPROTO_TCP == ip->protocol);
-
-  tcp->crc = 0;
-  sum = GNUNET_CRYPTO_crc16_step (0,
-                                 &ip->source_address,
-                                 sizeof (struct in_addr) * 2);
-  tmp = htons (IPPROTO_TCP);
-  sum = GNUNET_CRYPTO_crc16_step (sum, &tmp, sizeof (uint16_t));
-  tmp = htons (payload_length + sizeof (struct GNUNET_TUN_TcpHeader));
-  sum = GNUNET_CRYPTO_crc16_step (sum, &tmp, sizeof (uint16_t));
-  sum = GNUNET_CRYPTO_crc16_step (sum, tcp, sizeof (struct GNUNET_TUN_TcpHeader));
-  sum = GNUNET_CRYPTO_crc16_step (sum, payload, payload_length);
-  tcp->crc = GNUNET_CRYPTO_crc16_finish (sum);
-}
-
-
-/**
- * Calculate IPv6 TCP checksum.
- *
- * @param ip ipv6 header fully initialized
- * @param tcp header (initialized except for CRC)
- * @param payload the TCP payload
- * @param payload_length number of bytes of TCP payload
- */
-void
-GNUNET_TUN_calculate_tcp6_checksum (const struct GNUNET_TUN_IPv6Header *ip,
-                                   struct GNUNET_TUN_TcpHeader *tcp,
-                                   const void *payload,
-                                   uint16_t payload_length)
-{
-  uint32_t sum;
-  uint32_t tmp;
-
-  GNUNET_assert (20 == sizeof (struct GNUNET_TUN_TcpHeader));
-  GNUNET_assert (payload_length + sizeof (struct GNUNET_TUN_TcpHeader) ==
-                ntohs (ip->payload_length));
-  GNUNET_assert (IPPROTO_TCP == ip->next_header);
-  tcp->crc = 0;
-  sum = GNUNET_CRYPTO_crc16_step (0, &ip->source_address, 2 * sizeof (struct in6_addr));
-  tmp = htonl (sizeof (struct GNUNET_TUN_TcpHeader) + payload_length);
-  sum = GNUNET_CRYPTO_crc16_step (sum, &tmp, sizeof (uint32_t));
-  tmp = htonl (IPPROTO_TCP);
-  sum = GNUNET_CRYPTO_crc16_step (sum, &tmp, sizeof (uint32_t));
-  sum = GNUNET_CRYPTO_crc16_step (sum, tcp,
-                                 sizeof (struct GNUNET_TUN_TcpHeader));
-  sum = GNUNET_CRYPTO_crc16_step (sum, payload, payload_length);
-  tcp->crc = GNUNET_CRYPTO_crc16_finish (sum);
-}
-
-
-/**
- * Calculate IPv4 UDP checksum.
- *
- * @param ip ipv4 header fully initialized
- * @param udp UDP header (initialized except for CRC)
- * @param payload the UDP payload
- * @param payload_length number of bytes of UDP payload
- */
-void
-GNUNET_TUN_calculate_udp4_checksum (const struct GNUNET_TUN_IPv4Header *ip,
-                                   struct GNUNET_TUN_UdpHeader *udp,
-                                   const void *payload,
-                                   uint16_t payload_length)
-{
-  uint32_t sum;
-  uint16_t tmp;
-
-  GNUNET_assert (8 == sizeof (struct GNUNET_TUN_UdpHeader));
-  GNUNET_assert (payload_length + sizeof (struct GNUNET_TUN_IPv4Header) + sizeof (struct GNUNET_TUN_UdpHeader) ==
-                ntohs (ip->total_length));
-  GNUNET_assert (IPPROTO_UDP == ip->protocol);
-
-  udp->crc = 0; /* technically optional, but we calculate it anyway, just to be sure */
-  sum = GNUNET_CRYPTO_crc16_step (0,
-                                 &ip->source_address,
-                                 sizeof (struct in_addr) * 2);
-  tmp = htons (IPPROTO_UDP);
-  sum = GNUNET_CRYPTO_crc16_step (sum,
-                                 &tmp,
-                                 sizeof (uint16_t));
-  tmp = htons (sizeof (struct GNUNET_TUN_UdpHeader) + payload_length);
-  sum = GNUNET_CRYPTO_crc16_step (sum,
-                                 &tmp,
-                                 sizeof (uint16_t));
-  sum = GNUNET_CRYPTO_crc16_step (sum,
-                                 udp,
-                                 sizeof (struct GNUNET_TUN_UdpHeader));
-  sum = GNUNET_CRYPTO_crc16_step (sum,
-                                 payload,
-                                 payload_length);
-  udp->crc = GNUNET_CRYPTO_crc16_finish (sum);
-}
-
-
-/**
- * Calculate IPv6 UDP checksum.
- *
- * @param ip ipv6 header fully initialized
- * @param udp UDP header (initialized except for CRC)
- * @param payload the UDP payload
- * @param payload_length number of bytes of UDP payload
- */
-void
-GNUNET_TUN_calculate_udp6_checksum (const struct GNUNET_TUN_IPv6Header *ip,
-                                   struct GNUNET_TUN_UdpHeader *udp,
-                                   const void *payload,
-                                   uint16_t payload_length)
-{
-  uint32_t sum;
-  uint32_t tmp;
-
-  GNUNET_assert (payload_length + sizeof (struct GNUNET_TUN_UdpHeader) ==
-                ntohs (ip->payload_length));
-  GNUNET_assert (payload_length + sizeof (struct GNUNET_TUN_UdpHeader) ==
-                ntohs (udp->len));
-  GNUNET_assert (IPPROTO_UDP == ip->next_header);
-
-  udp->crc = 0;
-  sum = GNUNET_CRYPTO_crc16_step (0,
-                                 &ip->source_address,
-                                 sizeof (struct in6_addr) * 2);
-  tmp = htons (sizeof (struct GNUNET_TUN_UdpHeader) + payload_length); /* aka udp->len */
-  sum = GNUNET_CRYPTO_crc16_step (sum, &tmp, sizeof (uint32_t));
-  tmp = htons (ip->next_header);
-  sum = GNUNET_CRYPTO_crc16_step (sum, &tmp, sizeof (uint32_t));
-  sum = GNUNET_CRYPTO_crc16_step (sum, udp, sizeof (struct GNUNET_TUN_UdpHeader));
-  sum = GNUNET_CRYPTO_crc16_step (sum, payload, payload_length);
-  udp->crc = GNUNET_CRYPTO_crc16_finish (sum);
-}
-
-
-/**
- * Calculate ICMP checksum.
- *
- * @param icmp IMCP header (initialized except for CRC)
- * @param payload the ICMP payload
- * @param payload_length number of bytes of ICMP payload
- */
-void
-GNUNET_TUN_calculate_icmp_checksum (struct GNUNET_TUN_IcmpHeader *icmp,
-                                   const void *payload,
-                                   uint16_t payload_length)
-{
-  uint32_t sum;
-
-  GNUNET_assert (8 == sizeof (struct GNUNET_TUN_IcmpHeader));
-  icmp->crc = 0;
-  sum = GNUNET_CRYPTO_crc16_step (0,
-                                 icmp,
-                                 sizeof (struct GNUNET_TUN_IcmpHeader));
-  sum = GNUNET_CRYPTO_crc16_step (sum, payload, payload_length);
-  icmp->crc = GNUNET_CRYPTO_crc16_finish (sum);
-}
-
-
-/**
- * Check if two sockaddrs are equal.
- *
- * @param sa one address
- * @param sb another address
- * @param include_port also check ports
- * @return #GNUNET_YES if they are equal
- */
-int
-GNUNET_TUN_sockaddr_cmp (const struct sockaddr *sa,
-                         const struct sockaddr *sb,
-                         int include_port)
-{
-  if (sa->sa_family != sb->sa_family)
-    return GNUNET_NO;
-
-  switch (sa->sa_family)
-  {
-  case AF_INET:
-    {
-      const struct sockaddr_in *sa4 = (const struct sockaddr_in *) sa;
-      const struct sockaddr_in *sb4 = (const struct sockaddr_in *) sb;
-      return (sa4->sin_addr.s_addr == sb4->sin_addr.s_addr);
-    }
-  case AF_INET6:
-    {
-      const struct sockaddr_in6 *sa6 = (const struct sockaddr_in6 *) sa;
-      const struct sockaddr_in6 *sb6 = (const struct sockaddr_in6 *) sb;
-
-      return (0 == memcmp(&sa6->sin6_addr,
-                          &sb6->sin6_addr,
-                          sizeof (struct in6_addr)));
-    }
-  default:
-    GNUNET_break (0);
-    return GNUNET_SYSERR;
-  }
-}
-
-
-/* end of tun.c */
index d1f74d35b838e11277a998c05a71e1613c914710..5c16fa349d4811ee268d09ea5a2078a6dbc8a311 100644 (file)
@@ -57,6 +57,7 @@ gnunet_service_vpn_SOURCES = \
  gnunet-service-vpn.c
 gnunet_service_vpn_LDADD = \
   $(top_builddir)/src/statistics/libgnunetstatistics.la \
+  $(top_builddir)/src/tun/libgnunettun.la \
   $(top_builddir)/src/util/libgnunetutil.la \
   $(top_builddir)/src/cadet/libgnunetcadet.la \
   $(top_builddir)/src/regex/libgnunetregex.la \
@@ -68,6 +69,7 @@ gnunet_vpn_SOURCES = \
  gnunet-vpn.c
 gnunet_vpn_LDADD = \
   libgnunetvpn.la \
+  $(top_builddir)/src/tun/libgnunettun.la \
   $(top_builddir)/src/util/libgnunetutil.la \
   $(GN_LIBINTL)