From ddfee3f564bff9c5d5719af3132d7869b8783ec4 Mon Sep 17 00:00:00 2001 From: Martin Schanzenbach Date: Wed, 18 Oct 2023 19:35:11 +0200 Subject: BUILD: more more components into new structure; ftbfs fix --- configure.ac | 23 +- po/POTFILES.in | 92 +- src/Makefile.am | 5 - src/cadet/Makefile.am | 8 +- src/cli/Makefile.am | 5 +- src/cli/identity/Makefile.am | 2 +- src/cli/nat/.gitignore | 1 + src/cli/nat/Makefile.am | 17 + src/cli/nat/gnunet-nat.c | 476 + src/cli/nse/.gitignore | 1 + src/cli/nse/Makefile.am | 19 + src/cli/nse/gnunet-nse.c | 130 + src/cli/statistics/.gitignore | 1 + src/cli/statistics/Makefile.am | 40 + src/cli/statistics/gnunet-statistics.c | 889 ++ src/consensus/Makefile.am | 6 +- src/contrib/service/abd/Makefile.am | 2 +- src/contrib/service/rps/Makefile.am | 12 +- src/conversation/Makefile.am | 6 +- src/datacache/Makefile.am | 20 +- src/datastore/Makefile.am | 38 +- src/dht/Makefile.am | 8 +- src/dns/Makefile.am | 2 +- src/exit/Makefile.am | 2 +- src/fs/Makefile.am | 48 +- src/gns/Makefile.am | 2 +- src/hostlist/Makefile.am | 12 +- src/lib/gnsrecord/Makefile.am | 10 +- src/messenger/Makefile.am | 4 +- src/namecache/Makefile.am | 16 +- src/namestore/Makefile.am | 76 +- src/nat-auto/Makefile.am | 8 +- src/nat/.gitignore | 5 - src/nat/Makefile.am | 118 - src/nat/gnunet-helper-nat-client.c | 547 - src/nat/gnunet-helper-nat-server.c | 715 -- src/nat/gnunet-nat-client-script.sh | 4 - src/nat/gnunet-nat-server-script.sh | 4 - src/nat/gnunet-nat.c | 476 - src/nat/gnunet-service-nat.c | 2083 ---- src/nat/gnunet-service-nat.h | 35 - src/nat/gnunet-service-nat_externalip.c | 316 - src/nat/gnunet-service-nat_externalip.h | 92 - src/nat/gnunet-service-nat_helper.c | 391 - src/nat/gnunet-service-nat_helper.h | 95 - src/nat/gnunet-service-nat_mini.c | 694 -- src/nat/gnunet-service-nat_mini.h | 128 - src/nat/gnunet-service-nat_stun.c | 215 - src/nat/gnunet-service-nat_stun.h | 61 - src/nat/meson.build | 75 - src/nat/nat.conf.in | 43 - src/nat/nat.h | 223 - src/nat/nat_api.c | 702 -- src/nat/nat_api_stun.c | 259 - src/nat/nat_stun.h | 267 - src/nat/test_nat.c | 192 - src/nat/test_nat_data.conf | 36 - src/nat/test_nat_mini.c | 134 - src/nat/test_nat_test.c | 142 - src/nat/test_nat_test_data.conf | 45 - src/nat/test_stun.c | 313 - src/nat/test_stun.conf | 7 - src/nse/.gitignore | 5 - src/nse/Makefile.am | 107 - src/nse/gnunet-nse-profiler.c | 921 -- src/nse/gnunet-nse.c | 130 - src/nse/gnunet-service-nse.c | 1587 --- src/nse/hostkeys.dat | Bin 10389438 -> 0 bytes src/nse/meson.build | 47 - src/nse/nse.conf.in | 38 - src/nse/nse.h | 73 - src/nse/nse_api.c | 208 - src/nse/nse_infiniband.conf | 77 - src/nse/nse_profiler_test.conf | 72 - src/nse/perf_kdf.c | 67 - src/nse/test_nse.conf | 26 - src/nse/test_nse_api.c | 107 - src/nse/test_nse_multipeer.c | 235 - src/peerstore/.gitignore | 9 - src/peerstore/Makefile.am | 150 - src/peerstore/gnunet-peerstore.c | 97 - src/peerstore/gnunet-service-peerstore.c | 724 -- src/peerstore/meson.build | 50 - src/peerstore/peerstore.conf.in | 13 - src/peerstore/peerstore.h | 108 - src/peerstore/peerstore_api.c | 1391 --- src/peerstore/peerstore_common.c | 188 - src/peerstore/peerstore_common.h | 82 - src/peerstore/perf_peerstore_store.c | 109 - src/peerstore/plugin_peerstore_flat.c | 606 - src/peerstore/plugin_peerstore_sqlite.c | 702 -- src/peerstore/test_peerstore_api_data.conf | 14 - src/peerstore/test_peerstore_api_iterate.c | 176 - src/peerstore/test_peerstore_api_store.c | 233 - src/peerstore/test_peerstore_api_sync.c | 253 - src/peerstore/test_peerstore_api_watch.c | 102 - src/peerstore/test_plugin_peerstore.c | 224 - src/peerstore/test_plugin_peerstore_flat.conf | 5 - src/peerstore/test_plugin_peerstore_sqlite.conf | 2 - src/plugin/dhtu/Makefile.am | 10 +- src/pt/Makefile.am | 12 +- src/reclaim/Makefile.am | 2 +- src/regex/Makefile.am | 20 +- src/revocation/Makefile.am | 4 +- src/scalarproduct/Makefile.am | 2 +- src/secretsharing/Makefile.am | 4 +- src/service/Makefile.am | 7 +- src/service/core/Makefile.am | 26 +- src/service/identity/Makefile.am | 4 +- src/service/nat/.gitignore | 5 + src/service/nat/Makefile.am | 106 + src/service/nat/gnunet-helper-nat-client.c | 547 + src/service/nat/gnunet-helper-nat-server.c | 715 ++ src/service/nat/gnunet-nat-client-script.sh | 4 + src/service/nat/gnunet-nat-server-script.sh | 4 + src/service/nat/gnunet-service-nat.c | 2083 ++++ src/service/nat/gnunet-service-nat.h | 35 + src/service/nat/gnunet-service-nat_externalip.c | 316 + src/service/nat/gnunet-service-nat_externalip.h | 92 + src/service/nat/gnunet-service-nat_helper.c | 391 + src/service/nat/gnunet-service-nat_helper.h | 95 + src/service/nat/gnunet-service-nat_mini.c | 694 ++ src/service/nat/gnunet-service-nat_mini.h | 128 + src/service/nat/gnunet-service-nat_stun.c | 215 + src/service/nat/gnunet-service-nat_stun.h | 61 + src/service/nat/meson.build | 75 + src/service/nat/nat.conf.in | 43 + src/service/nat/nat.h | 223 + src/service/nat/nat_api.c | 702 ++ src/service/nat/nat_api_stun.c | 259 + src/service/nat/nat_stun.h | 267 + src/service/nat/test_nat.c | 192 + src/service/nat/test_nat_data.conf | 36 + src/service/nat/test_nat_mini.c | 134 + src/service/nat/test_nat_test.c | 142 + src/service/nat/test_nat_test_data.conf | 45 + src/service/nat/test_stun.c | 313 + src/service/nat/test_stun.conf | 7 + src/service/nse/.gitignore | 5 + src/service/nse/Makefile.am | 99 + src/service/nse/gnunet-nse-profiler.c | 921 ++ src/service/nse/gnunet-service-nse.c | 1587 +++ src/service/nse/hostkeys.dat | Bin 0 -> 10389438 bytes src/service/nse/meson.build | 47 + src/service/nse/nse.conf.in | 38 + src/service/nse/nse.h | 73 + src/service/nse/nse_api.c | 208 + src/service/nse/nse_infiniband.conf | 77 + src/service/nse/nse_profiler_test.conf | 72 + src/service/nse/perf_kdf.c | 67 + src/service/nse/test_nse.conf | 26 + src/service/nse/test_nse_api.c | 107 + src/service/nse/test_nse_multipeer.c | 235 + src/service/peerstore/.gitignore | 9 + src/service/peerstore/Makefile.am | 150 + src/service/peerstore/gnunet-peerstore.c | 97 + src/service/peerstore/gnunet-service-peerstore.c | 724 ++ src/service/peerstore/meson.build | 50 + src/service/peerstore/peerstore.conf.in | 13 + src/service/peerstore/peerstore.h | 108 + src/service/peerstore/peerstore_api.c | 1391 +++ src/service/peerstore/peerstore_common.c | 188 + src/service/peerstore/peerstore_common.h | 82 + src/service/peerstore/perf_peerstore_store.c | 109 + src/service/peerstore/plugin_peerstore_flat.c | 606 + src/service/peerstore/plugin_peerstore_sqlite.c | 702 ++ src/service/peerstore/test_peerstore_api_data.conf | 14 + src/service/peerstore/test_peerstore_api_iterate.c | 176 + src/service/peerstore/test_peerstore_api_store.c | 233 + src/service/peerstore/test_peerstore_api_sync.c | 253 + src/service/peerstore/test_peerstore_api_watch.c | 102 + src/service/peerstore/test_plugin_peerstore.c | 224 + .../peerstore/test_plugin_peerstore_flat.conf | 5 + .../peerstore/test_plugin_peerstore_sqlite.conf | 2 + src/service/statistics/.gitignore | 7 + src/service/statistics/Makefile.am | 89 + src/service/statistics/gnunet-service-statistics.c | 1059 ++ src/service/statistics/meson.build | 44 + src/service/statistics/statistics.conf.in | 21 + src/service/statistics/statistics.h | 149 + src/service/statistics/statistics_api.c | 1342 +++ .../statistics/test_gnunet_statistics.py.in | 171 + src/service/statistics/test_statistics_api.c | 253 + .../statistics/test_statistics_api_data.conf | 5 + src/service/statistics/test_statistics_api_loop.c | 123 + src/service/statistics/test_statistics_api_watch.c | 156 + .../test_statistics_api_watch_zero_value.c | 197 + src/service/transport/.gitignore | 94 + src/service/transport/Makefile.am | 460 + src/service/transport/NOTES | 46 + src/service/transport/benchmark.sh | 13 + src/service/transport/communicator.h | 138 + src/service/transport/gnunet-communicator-quic.c | 1795 +++ src/service/transport/gnunet-communicator-tcp.c | 4082 +++++++ src/service/transport/gnunet-communicator-udp.c | 3444 ++++++ src/service/transport/gnunet-communicator-unix.c | 1166 ++ src/service/transport/gnunet-service-transport.c | 11750 +++++++++++++++++++ src/service/transport/gnunet-service-transport.h | 234 + .../gnunet-transport-certificate-creation.in | 158 + src/service/transport/gnunet-transport.c | 1437 +++ src/service/transport/ieee80211_radiotap.h | 276 + src/service/transport/meson.build | 261 + src/service/transport/template_cfg_peer1.conf | 50 + src/service/transport/template_cfg_peer2.conf | 58 + src/service/transport/template_tng_cfg_peer1.conf | 34 + src/service/transport/test_communicator_basic.c | 1224 ++ .../test_communicator_quic_basic_peer1.conf | 45 + .../test_communicator_quic_basic_peer2.conf | 45 + .../test_communicator_tcp_basic_peer1.conf | 48 + .../test_communicator_tcp_basic_peer2.conf | 44 + .../test_communicator_tcp_bidirect_peer1.conf | 48 + .../test_communicator_tcp_bidirect_peer2.conf | 44 + .../test_communicator_tcp_rekey_peer1.conf | 45 + .../test_communicator_tcp_rekey_peer2.conf | 45 + .../test_communicator_udp_backchannel_peer1.conf | 48 + .../test_communicator_udp_backchannel_peer2.conf | 48 + .../test_communicator_udp_basic_peer1.conf | 39 + .../test_communicator_udp_basic_peer2.conf | 39 + .../test_communicator_udp_rekey_peer1.conf | 52 + .../test_communicator_udp_rekey_peer2.conf | 52 + .../test_communicator_unix_basic_peer1.conf | 43 + .../test_communicator_unix_basic_peer2.conf | 43 + src/service/transport/test_delay | 19 + src/service/transport/test_plugin_hostkey | Bin 0 -> 915 bytes src/service/transport/test_plugin_hostkey.ecc | 1 + src/service/transport/test_tng_defaults.conf | 14 + .../transport/test_transport_address_switch.c | 433 + .../test_transport_address_switch_tcp_peer1.conf | 48 + .../test_transport_address_switch_tcp_peer2.conf | 48 + .../test_transport_address_switch_udp_peer1.conf | 48 + .../test_transport_address_switch_udp_peer2.conf | 48 + src/service/transport/test_transport_api.c | 126 + src/service/transport/test_transport_api2.c | 126 + .../transport/test_transport_api2_tcp_node1.conf | 35 + .../transport/test_transport_api2_tcp_node2.conf | 22 + .../transport/test_transport_api2_tcp_peer1.conf | 23 + .../transport/test_transport_api2_tcp_peer2.conf | 22 + .../transport/test_transport_api2_tng_node.conf | 40 + src/service/transport/test_transport_api_data.conf | 9 + .../transport/test_transport_api_monitor_peers.c | 226 + .../test_transport_api_monitor_peers_peer1.conf | 4 + .../test_transport_api_monitor_peers_peer2.conf | 5 + ...est_transport_api_monitor_validation_peer1.conf | 6 + ...est_transport_api_monitor_validation_peer2.conf | 7 + .../transport/test_transport_api_multi_peer1.conf | 7 + .../transport/test_transport_api_multi_peer2.conf | 9 + .../test_transport_api_tcp_nat_peer1.conf | 34 + .../test_transport_api_tcp_nat_peer2.conf | 32 + .../transport/test_transport_api_tcp_peer1.conf | 9 + .../transport/test_transport_api_tcp_peer2.conf | 9 + .../test_transport_api_udp_nat_peer1.conf | 34 + .../test_transport_api_udp_nat_peer2.conf | 32 + .../transport/test_transport_api_udp_peer1.conf | 17 + .../transport/test_transport_api_udp_peer2.conf | 15 + .../transport/test_transport_api_unix_peer1.conf | 10 + .../transport/test_transport_api_unix_peer2.conf | 7 + src/service/transport/test_transport_defaults.conf | 20 + ...test_transport_distance_vector_circle_topo.conf | 11 + ...est_transport_distance_vector_inverse_topo.conf | 13 + .../test_transport_distance_vector_topo.conf | 8 + .../transport/test_transport_just_run_topo.conf | 6 + .../transport/test_transport_nat_icmp_tcp.sh | 12 + .../test_transport_nat_icmp_tcp_topo.conf | 7 + src/service/transport/test_transport_nat_upnp.sh | 12 + .../transport/test_transport_nat_upnp_topo.conf | 7 + .../transport/test_transport_plugin_cmd_just_run.c | 494 + .../transport/test_transport_plugin_cmd_nat_upnp.c | 368 + .../test_transport_plugin_cmd_simple_send.c | 377 + ...st_transport_plugin_cmd_simple_send_broadcast.c | 402 + .../test_transport_plugin_cmd_simple_send_dv.c | 434 + ..._transport_plugin_cmd_simple_send_performance.c | 480 + .../test_transport_plugin_cmd_udp_backchannel.c | 371 + .../transport/test_transport_simple_send.sh | 11 + .../test_transport_simple_send_broadcast.sh | 11 + .../test_transport_simple_send_broadcast_topo.conf | 4 + .../test_transport_simple_send_dv_circle.sh | 11 + .../test_transport_simple_send_dv_inverse.sh | 12 + .../test_transport_simple_send_performance.sh | 11 + ...est_transport_simple_send_performance_topo.conf | 6 + .../transport/test_transport_simple_send_string.sh | 21 + .../transport/test_transport_simple_send_topo.conf | 6 + .../transport/test_transport_start_testcase.sh | 12 + .../transport/test_transport_start_with_config.c | 121 + ...rt_test_transport_address_switch_tcp_peer1.conf | 29 + ...rt_test_transport_address_switch_tcp_peer2.conf | 29 + .../transport/test_transport_testing_startstop.c | 138 + .../transport/test_transport_udp_backchannel.sh | 14 + .../test_transport_udp_backchannel_topo.conf | 7 + src/service/transport/transport-testing-cmds.h | 245 + .../transport/transport-testing-communicator.c | 1238 ++ .../transport/transport-testing-communicator.h | 370 + .../transport/transport-testing-filenames2.c | 203 + src/service/transport/transport-testing-loggers2.c | 81 + src/service/transport/transport-testing-main2.c | 614 + src/service/transport/transport-testing-send2.c | 241 + src/service/transport/transport-testing2.c | 924 ++ src/service/transport/transport-testing2.h | 941 ++ src/service/transport/transport.conf.in | 248 + src/service/transport/transport.h | 828 ++ src/service/transport/transport_api2_application.c | 397 + .../transport/transport_api2_communication.c | 1114 ++ src/service/transport/transport_api2_core.c | 826 ++ src/service/transport/transport_api2_monitor.c | 292 + .../transport_api_cmd_backchannel_check.c | 554 + .../transport/transport_api_cmd_connecting_peers.c | 311 + .../transport/transport_api_cmd_send_simple.c | 162 + .../transport_api_cmd_send_simple_performance.c | 220 + .../transport/transport_api_cmd_start_peer.c | 483 + .../transport/transport_api_cmd_stop_peer.c | 154 + src/service/transport/transport_api_traits.c | 32 + src/service/transport/upnp.sh | 22 + src/set/Makefile.am | 14 +- src/seti/Makefile.am | 8 +- src/setu/Makefile.am | 10 +- src/statistics/.gitignore | 7 - src/statistics/Makefile.am | 98 - src/statistics/gnunet-service-statistics.c | 1059 -- src/statistics/gnunet-statistics.c | 889 -- src/statistics/meson.build | 44 - src/statistics/statistics.conf.in | 21 - src/statistics/statistics.h | 149 - src/statistics/statistics_api.c | 1342 --- src/statistics/test_gnunet_statistics.py.in | 171 - src/statistics/test_statistics_api.c | 253 - src/statistics/test_statistics_api_data.conf | 5 - src/statistics/test_statistics_api_loop.c | 123 - src/statistics/test_statistics_api_watch.c | 156 - .../test_statistics_api_watch_zero_value.c | 197 - src/topology/Makefile.am | 8 +- src/transport/.gitignore | 94 - src/transport/Makefile.am | 460 - src/transport/NOTES | 46 - src/transport/benchmark.sh | 13 - src/transport/communicator.h | 138 - src/transport/gnunet-communicator-quic.c | 1795 --- src/transport/gnunet-communicator-tcp.c | 4082 ------- src/transport/gnunet-communicator-udp.c | 3444 ------ src/transport/gnunet-communicator-unix.c | 1166 -- src/transport/gnunet-service-transport.c | 11750 ------------------- src/transport/gnunet-service-transport.h | 234 - .../gnunet-transport-certificate-creation.in | 158 - src/transport/gnunet-transport.c | 1437 --- src/transport/ieee80211_radiotap.h | 276 - src/transport/meson.build | 261 - src/transport/template_cfg_peer1.conf | 50 - src/transport/template_cfg_peer2.conf | 58 - src/transport/template_tng_cfg_peer1.conf | 34 - src/transport/test_communicator_basic.c | 1224 -- .../test_communicator_quic_basic_peer1.conf | 45 - .../test_communicator_quic_basic_peer2.conf | 45 - .../test_communicator_tcp_basic_peer1.conf | 48 - .../test_communicator_tcp_basic_peer2.conf | 44 - .../test_communicator_tcp_bidirect_peer1.conf | 48 - .../test_communicator_tcp_bidirect_peer2.conf | 44 - .../test_communicator_tcp_rekey_peer1.conf | 45 - .../test_communicator_tcp_rekey_peer2.conf | 45 - .../test_communicator_udp_backchannel_peer1.conf | 48 - .../test_communicator_udp_backchannel_peer2.conf | 48 - .../test_communicator_udp_basic_peer1.conf | 39 - .../test_communicator_udp_basic_peer2.conf | 39 - .../test_communicator_udp_rekey_peer1.conf | 52 - .../test_communicator_udp_rekey_peer2.conf | 52 - .../test_communicator_unix_basic_peer1.conf | 43 - .../test_communicator_unix_basic_peer2.conf | 43 - src/transport/test_delay | 19 - src/transport/test_plugin_hostkey | Bin 915 -> 0 bytes src/transport/test_plugin_hostkey.ecc | 1 - src/transport/test_tng_defaults.conf | 14 - src/transport/test_transport_address_switch.c | 433 - .../test_transport_address_switch_tcp_peer1.conf | 48 - .../test_transport_address_switch_tcp_peer2.conf | 48 - .../test_transport_address_switch_udp_peer1.conf | 48 - .../test_transport_address_switch_udp_peer2.conf | 48 - src/transport/test_transport_api.c | 126 - src/transport/test_transport_api2.c | 126 - src/transport/test_transport_api2_tcp_node1.conf | 35 - src/transport/test_transport_api2_tcp_node2.conf | 22 - src/transport/test_transport_api2_tcp_peer1.conf | 23 - src/transport/test_transport_api2_tcp_peer2.conf | 22 - src/transport/test_transport_api2_tng_node.conf | 40 - src/transport/test_transport_api_data.conf | 9 - src/transport/test_transport_api_monitor_peers.c | 226 - .../test_transport_api_monitor_peers_peer1.conf | 4 - .../test_transport_api_monitor_peers_peer2.conf | 5 - ...est_transport_api_monitor_validation_peer1.conf | 6 - ...est_transport_api_monitor_validation_peer2.conf | 7 - src/transport/test_transport_api_multi_peer1.conf | 7 - src/transport/test_transport_api_multi_peer2.conf | 9 - .../test_transport_api_tcp_nat_peer1.conf | 34 - .../test_transport_api_tcp_nat_peer2.conf | 32 - src/transport/test_transport_api_tcp_peer1.conf | 9 - src/transport/test_transport_api_tcp_peer2.conf | 9 - .../test_transport_api_udp_nat_peer1.conf | 34 - .../test_transport_api_udp_nat_peer2.conf | 32 - src/transport/test_transport_api_udp_peer1.conf | 17 - src/transport/test_transport_api_udp_peer2.conf | 15 - src/transport/test_transport_api_unix_peer1.conf | 10 - src/transport/test_transport_api_unix_peer2.conf | 7 - src/transport/test_transport_defaults.conf | 20 - ...test_transport_distance_vector_circle_topo.conf | 11 - ...est_transport_distance_vector_inverse_topo.conf | 13 - .../test_transport_distance_vector_topo.conf | 8 - src/transport/test_transport_just_run_topo.conf | 6 - src/transport/test_transport_nat_icmp_tcp.sh | 12 - .../test_transport_nat_icmp_tcp_topo.conf | 7 - src/transport/test_transport_nat_upnp.sh | 12 - src/transport/test_transport_nat_upnp_topo.conf | 7 - src/transport/test_transport_plugin_cmd_just_run.c | 494 - src/transport/test_transport_plugin_cmd_nat_upnp.c | 368 - .../test_transport_plugin_cmd_simple_send.c | 377 - ...st_transport_plugin_cmd_simple_send_broadcast.c | 402 - .../test_transport_plugin_cmd_simple_send_dv.c | 434 - ..._transport_plugin_cmd_simple_send_performance.c | 480 - .../test_transport_plugin_cmd_udp_backchannel.c | 371 - src/transport/test_transport_simple_send.sh | 11 - .../test_transport_simple_send_broadcast.sh | 11 - .../test_transport_simple_send_broadcast_topo.conf | 4 - .../test_transport_simple_send_dv_circle.sh | 11 - .../test_transport_simple_send_dv_inverse.sh | 12 - .../test_transport_simple_send_performance.sh | 11 - ...est_transport_simple_send_performance_topo.conf | 6 - src/transport/test_transport_simple_send_string.sh | 21 - src/transport/test_transport_simple_send_topo.conf | 6 - src/transport/test_transport_start_testcase.sh | 12 - src/transport/test_transport_start_with_config.c | 121 - ...rt_test_transport_address_switch_tcp_peer1.conf | 29 - ...rt_test_transport_address_switch_tcp_peer2.conf | 29 - src/transport/test_transport_testing_startstop.c | 138 - src/transport/test_transport_udp_backchannel.sh | 14 - .../test_transport_udp_backchannel_topo.conf | 7 - src/transport/transport-testing-cmds.h | 245 - src/transport/transport-testing-communicator.c | 1238 -- src/transport/transport-testing-communicator.h | 370 - src/transport/transport-testing-filenames2.c | 203 - src/transport/transport-testing-loggers2.c | 81 - src/transport/transport-testing-main2.c | 614 - src/transport/transport-testing-send2.c | 241 - src/transport/transport-testing2.c | 924 -- src/transport/transport-testing2.h | 941 -- src/transport/transport.conf.in | 248 - src/transport/transport.h | 828 -- src/transport/transport_api2_application.c | 397 - src/transport/transport_api2_communication.c | 1114 -- src/transport/transport_api2_core.c | 826 -- src/transport/transport_api2_monitor.c | 292 - .../transport_api_cmd_backchannel_check.c | 554 - src/transport/transport_api_cmd_connecting_peers.c | 311 - src/transport/transport_api_cmd_send_simple.c | 162 - .../transport_api_cmd_send_simple_performance.c | 220 - src/transport/transport_api_cmd_start_peer.c | 483 - src/transport/transport_api_cmd_stop_peer.c | 154 - src/transport/transport_api_traits.c | 32 - src/transport/upnp.sh | 22 - src/vpn/Makefile.am | 2 +- src/zonemaster/Makefile.am | 2 +- 455 files changed, 65131 insertions(+), 65075 deletions(-) create mode 100644 src/cli/nat/.gitignore create mode 100644 src/cli/nat/Makefile.am create mode 100644 src/cli/nat/gnunet-nat.c create mode 100644 src/cli/nse/.gitignore create mode 100644 src/cli/nse/Makefile.am create mode 100644 src/cli/nse/gnunet-nse.c create mode 100644 src/cli/statistics/.gitignore create mode 100644 src/cli/statistics/Makefile.am create mode 100644 src/cli/statistics/gnunet-statistics.c delete mode 100644 src/nat/.gitignore delete mode 100644 src/nat/Makefile.am delete mode 100644 src/nat/gnunet-helper-nat-client.c delete mode 100644 src/nat/gnunet-helper-nat-server.c delete mode 100755 src/nat/gnunet-nat-client-script.sh delete mode 100755 src/nat/gnunet-nat-server-script.sh delete mode 100644 src/nat/gnunet-nat.c delete mode 100644 src/nat/gnunet-service-nat.c delete mode 100644 src/nat/gnunet-service-nat.h delete mode 100644 src/nat/gnunet-service-nat_externalip.c delete mode 100644 src/nat/gnunet-service-nat_externalip.h delete mode 100644 src/nat/gnunet-service-nat_helper.c delete mode 100644 src/nat/gnunet-service-nat_helper.h delete mode 100644 src/nat/gnunet-service-nat_mini.c delete mode 100644 src/nat/gnunet-service-nat_mini.h delete mode 100644 src/nat/gnunet-service-nat_stun.c delete mode 100644 src/nat/gnunet-service-nat_stun.h delete mode 100644 src/nat/meson.build delete mode 100644 src/nat/nat.conf.in delete mode 100644 src/nat/nat.h delete mode 100644 src/nat/nat_api.c delete mode 100644 src/nat/nat_api_stun.c delete mode 100644 src/nat/nat_stun.h delete mode 100644 src/nat/test_nat.c delete mode 100644 src/nat/test_nat_data.conf delete mode 100644 src/nat/test_nat_mini.c delete mode 100644 src/nat/test_nat_test.c delete mode 100644 src/nat/test_nat_test_data.conf delete mode 100644 src/nat/test_stun.c delete mode 100644 src/nat/test_stun.conf delete mode 100644 src/nse/.gitignore delete mode 100644 src/nse/Makefile.am delete mode 100644 src/nse/gnunet-nse-profiler.c delete mode 100644 src/nse/gnunet-nse.c delete mode 100644 src/nse/gnunet-service-nse.c delete mode 100644 src/nse/hostkeys.dat delete mode 100644 src/nse/meson.build delete mode 100644 src/nse/nse.conf.in delete mode 100644 src/nse/nse.h delete mode 100644 src/nse/nse_api.c delete mode 100644 src/nse/nse_infiniband.conf delete mode 100644 src/nse/nse_profiler_test.conf delete mode 100644 src/nse/perf_kdf.c delete mode 100644 src/nse/test_nse.conf delete mode 100644 src/nse/test_nse_api.c delete mode 100644 src/nse/test_nse_multipeer.c delete mode 100644 src/peerstore/.gitignore delete mode 100644 src/peerstore/Makefile.am delete mode 100644 src/peerstore/gnunet-peerstore.c delete mode 100644 src/peerstore/gnunet-service-peerstore.c delete mode 100644 src/peerstore/meson.build delete mode 100644 src/peerstore/peerstore.conf.in delete mode 100644 src/peerstore/peerstore.h delete mode 100644 src/peerstore/peerstore_api.c delete mode 100644 src/peerstore/peerstore_common.c delete mode 100644 src/peerstore/peerstore_common.h delete mode 100644 src/peerstore/perf_peerstore_store.c delete mode 100644 src/peerstore/plugin_peerstore_flat.c delete mode 100644 src/peerstore/plugin_peerstore_sqlite.c delete mode 100644 src/peerstore/test_peerstore_api_data.conf delete mode 100644 src/peerstore/test_peerstore_api_iterate.c delete mode 100644 src/peerstore/test_peerstore_api_store.c delete mode 100644 src/peerstore/test_peerstore_api_sync.c delete mode 100644 src/peerstore/test_peerstore_api_watch.c delete mode 100644 src/peerstore/test_plugin_peerstore.c delete mode 100644 src/peerstore/test_plugin_peerstore_flat.conf delete mode 100644 src/peerstore/test_plugin_peerstore_sqlite.conf create mode 100644 src/service/nat/.gitignore create mode 100644 src/service/nat/Makefile.am create mode 100644 src/service/nat/gnunet-helper-nat-client.c create mode 100644 src/service/nat/gnunet-helper-nat-server.c create mode 100755 src/service/nat/gnunet-nat-client-script.sh create mode 100755 src/service/nat/gnunet-nat-server-script.sh create mode 100644 src/service/nat/gnunet-service-nat.c create mode 100644 src/service/nat/gnunet-service-nat.h create mode 100644 src/service/nat/gnunet-service-nat_externalip.c create mode 100644 src/service/nat/gnunet-service-nat_externalip.h create mode 100644 src/service/nat/gnunet-service-nat_helper.c create mode 100644 src/service/nat/gnunet-service-nat_helper.h create mode 100644 src/service/nat/gnunet-service-nat_mini.c create mode 100644 src/service/nat/gnunet-service-nat_mini.h create mode 100644 src/service/nat/gnunet-service-nat_stun.c create mode 100644 src/service/nat/gnunet-service-nat_stun.h create mode 100644 src/service/nat/meson.build create mode 100644 src/service/nat/nat.conf.in create mode 100644 src/service/nat/nat.h create mode 100644 src/service/nat/nat_api.c create mode 100644 src/service/nat/nat_api_stun.c create mode 100644 src/service/nat/nat_stun.h create mode 100644 src/service/nat/test_nat.c create mode 100644 src/service/nat/test_nat_data.conf create mode 100644 src/service/nat/test_nat_mini.c create mode 100644 src/service/nat/test_nat_test.c create mode 100644 src/service/nat/test_nat_test_data.conf create mode 100644 src/service/nat/test_stun.c create mode 100644 src/service/nat/test_stun.conf create mode 100644 src/service/nse/.gitignore create mode 100644 src/service/nse/Makefile.am create mode 100644 src/service/nse/gnunet-nse-profiler.c create mode 100644 src/service/nse/gnunet-service-nse.c create mode 100644 src/service/nse/hostkeys.dat create mode 100644 src/service/nse/meson.build create mode 100644 src/service/nse/nse.conf.in create mode 100644 src/service/nse/nse.h create mode 100644 src/service/nse/nse_api.c create mode 100644 src/service/nse/nse_infiniband.conf create mode 100644 src/service/nse/nse_profiler_test.conf create mode 100644 src/service/nse/perf_kdf.c create mode 100644 src/service/nse/test_nse.conf create mode 100644 src/service/nse/test_nse_api.c create mode 100644 src/service/nse/test_nse_multipeer.c create mode 100644 src/service/peerstore/.gitignore create mode 100644 src/service/peerstore/Makefile.am create mode 100644 src/service/peerstore/gnunet-peerstore.c create mode 100644 src/service/peerstore/gnunet-service-peerstore.c create mode 100644 src/service/peerstore/meson.build create mode 100644 src/service/peerstore/peerstore.conf.in create mode 100644 src/service/peerstore/peerstore.h create mode 100644 src/service/peerstore/peerstore_api.c create mode 100644 src/service/peerstore/peerstore_common.c create mode 100644 src/service/peerstore/peerstore_common.h create mode 100644 src/service/peerstore/perf_peerstore_store.c create mode 100644 src/service/peerstore/plugin_peerstore_flat.c create mode 100644 src/service/peerstore/plugin_peerstore_sqlite.c create mode 100644 src/service/peerstore/test_peerstore_api_data.conf create mode 100644 src/service/peerstore/test_peerstore_api_iterate.c create mode 100644 src/service/peerstore/test_peerstore_api_store.c create mode 100644 src/service/peerstore/test_peerstore_api_sync.c create mode 100644 src/service/peerstore/test_peerstore_api_watch.c create mode 100644 src/service/peerstore/test_plugin_peerstore.c create mode 100644 src/service/peerstore/test_plugin_peerstore_flat.conf create mode 100644 src/service/peerstore/test_plugin_peerstore_sqlite.conf create mode 100644 src/service/statistics/.gitignore create mode 100644 src/service/statistics/Makefile.am create mode 100644 src/service/statistics/gnunet-service-statistics.c create mode 100644 src/service/statistics/meson.build create mode 100644 src/service/statistics/statistics.conf.in create mode 100644 src/service/statistics/statistics.h create mode 100644 src/service/statistics/statistics_api.c create mode 100644 src/service/statistics/test_gnunet_statistics.py.in create mode 100644 src/service/statistics/test_statistics_api.c create mode 100644 src/service/statistics/test_statistics_api_data.conf create mode 100644 src/service/statistics/test_statistics_api_loop.c create mode 100644 src/service/statistics/test_statistics_api_watch.c create mode 100644 src/service/statistics/test_statistics_api_watch_zero_value.c create mode 100644 src/service/transport/.gitignore create mode 100644 src/service/transport/Makefile.am create mode 100644 src/service/transport/NOTES create mode 100755 src/service/transport/benchmark.sh create mode 100644 src/service/transport/communicator.h create mode 100644 src/service/transport/gnunet-communicator-quic.c create mode 100644 src/service/transport/gnunet-communicator-tcp.c create mode 100644 src/service/transport/gnunet-communicator-udp.c create mode 100644 src/service/transport/gnunet-communicator-unix.c create mode 100644 src/service/transport/gnunet-service-transport.c create mode 100644 src/service/transport/gnunet-service-transport.h create mode 100644 src/service/transport/gnunet-transport-certificate-creation.in create mode 100644 src/service/transport/gnunet-transport.c create mode 100644 src/service/transport/ieee80211_radiotap.h create mode 100644 src/service/transport/meson.build create mode 100644 src/service/transport/template_cfg_peer1.conf create mode 100644 src/service/transport/template_cfg_peer2.conf create mode 100644 src/service/transport/template_tng_cfg_peer1.conf create mode 100644 src/service/transport/test_communicator_basic.c create mode 100644 src/service/transport/test_communicator_quic_basic_peer1.conf create mode 100644 src/service/transport/test_communicator_quic_basic_peer2.conf create mode 100644 src/service/transport/test_communicator_tcp_basic_peer1.conf create mode 100644 src/service/transport/test_communicator_tcp_basic_peer2.conf create mode 100644 src/service/transport/test_communicator_tcp_bidirect_peer1.conf create mode 100644 src/service/transport/test_communicator_tcp_bidirect_peer2.conf create mode 100644 src/service/transport/test_communicator_tcp_rekey_peer1.conf create mode 100644 src/service/transport/test_communicator_tcp_rekey_peer2.conf create mode 100644 src/service/transport/test_communicator_udp_backchannel_peer1.conf create mode 100644 src/service/transport/test_communicator_udp_backchannel_peer2.conf create mode 100644 src/service/transport/test_communicator_udp_basic_peer1.conf create mode 100644 src/service/transport/test_communicator_udp_basic_peer2.conf create mode 100644 src/service/transport/test_communicator_udp_rekey_peer1.conf create mode 100644 src/service/transport/test_communicator_udp_rekey_peer2.conf create mode 100644 src/service/transport/test_communicator_unix_basic_peer1.conf create mode 100644 src/service/transport/test_communicator_unix_basic_peer2.conf create mode 100755 src/service/transport/test_delay create mode 100644 src/service/transport/test_plugin_hostkey create mode 100644 src/service/transport/test_plugin_hostkey.ecc create mode 100644 src/service/transport/test_tng_defaults.conf create mode 100644 src/service/transport/test_transport_address_switch.c create mode 100644 src/service/transport/test_transport_address_switch_tcp_peer1.conf create mode 100644 src/service/transport/test_transport_address_switch_tcp_peer2.conf create mode 100644 src/service/transport/test_transport_address_switch_udp_peer1.conf create mode 100644 src/service/transport/test_transport_address_switch_udp_peer2.conf create mode 100644 src/service/transport/test_transport_api.c create mode 100644 src/service/transport/test_transport_api2.c create mode 100644 src/service/transport/test_transport_api2_tcp_node1.conf create mode 100644 src/service/transport/test_transport_api2_tcp_node2.conf create mode 100644 src/service/transport/test_transport_api2_tcp_peer1.conf create mode 100644 src/service/transport/test_transport_api2_tcp_peer2.conf create mode 100644 src/service/transport/test_transport_api2_tng_node.conf create mode 100644 src/service/transport/test_transport_api_data.conf create mode 100644 src/service/transport/test_transport_api_monitor_peers.c create mode 100644 src/service/transport/test_transport_api_monitor_peers_peer1.conf create mode 100644 src/service/transport/test_transport_api_monitor_peers_peer2.conf create mode 100644 src/service/transport/test_transport_api_monitor_validation_peer1.conf create mode 100644 src/service/transport/test_transport_api_monitor_validation_peer2.conf create mode 100644 src/service/transport/test_transport_api_multi_peer1.conf create mode 100644 src/service/transport/test_transport_api_multi_peer2.conf create mode 100644 src/service/transport/test_transport_api_tcp_nat_peer1.conf create mode 100644 src/service/transport/test_transport_api_tcp_nat_peer2.conf create mode 100644 src/service/transport/test_transport_api_tcp_peer1.conf create mode 100644 src/service/transport/test_transport_api_tcp_peer2.conf create mode 100644 src/service/transport/test_transport_api_udp_nat_peer1.conf create mode 100644 src/service/transport/test_transport_api_udp_nat_peer2.conf create mode 100644 src/service/transport/test_transport_api_udp_peer1.conf create mode 100644 src/service/transport/test_transport_api_udp_peer2.conf create mode 100644 src/service/transport/test_transport_api_unix_peer1.conf create mode 100644 src/service/transport/test_transport_api_unix_peer2.conf create mode 100644 src/service/transport/test_transport_defaults.conf create mode 100644 src/service/transport/test_transport_distance_vector_circle_topo.conf create mode 100644 src/service/transport/test_transport_distance_vector_inverse_topo.conf create mode 100644 src/service/transport/test_transport_distance_vector_topo.conf create mode 100644 src/service/transport/test_transport_just_run_topo.conf create mode 100755 src/service/transport/test_transport_nat_icmp_tcp.sh create mode 100644 src/service/transport/test_transport_nat_icmp_tcp_topo.conf create mode 100755 src/service/transport/test_transport_nat_upnp.sh create mode 100644 src/service/transport/test_transport_nat_upnp_topo.conf create mode 100644 src/service/transport/test_transport_plugin_cmd_just_run.c create mode 100644 src/service/transport/test_transport_plugin_cmd_nat_upnp.c create mode 100644 src/service/transport/test_transport_plugin_cmd_simple_send.c create mode 100644 src/service/transport/test_transport_plugin_cmd_simple_send_broadcast.c create mode 100644 src/service/transport/test_transport_plugin_cmd_simple_send_dv.c create mode 100644 src/service/transport/test_transport_plugin_cmd_simple_send_performance.c create mode 100644 src/service/transport/test_transport_plugin_cmd_udp_backchannel.c create mode 100755 src/service/transport/test_transport_simple_send.sh create mode 100755 src/service/transport/test_transport_simple_send_broadcast.sh create mode 100644 src/service/transport/test_transport_simple_send_broadcast_topo.conf create mode 100755 src/service/transport/test_transport_simple_send_dv_circle.sh create mode 100755 src/service/transport/test_transport_simple_send_dv_inverse.sh create mode 100755 src/service/transport/test_transport_simple_send_performance.sh create mode 100644 src/service/transport/test_transport_simple_send_performance_topo.conf create mode 100755 src/service/transport/test_transport_simple_send_string.sh create mode 100644 src/service/transport/test_transport_simple_send_topo.conf create mode 100755 src/service/transport/test_transport_start_testcase.sh create mode 100644 src/service/transport/test_transport_start_with_config.c create mode 100644 src/service/transport/test_transport_test_transport_address_switch_tcp_peer1.conf create mode 100644 src/service/transport/test_transport_test_transport_address_switch_tcp_peer2.conf create mode 100644 src/service/transport/test_transport_testing_startstop.c create mode 100755 src/service/transport/test_transport_udp_backchannel.sh create mode 100644 src/service/transport/test_transport_udp_backchannel_topo.conf create mode 100644 src/service/transport/transport-testing-cmds.h create mode 100644 src/service/transport/transport-testing-communicator.c create mode 100644 src/service/transport/transport-testing-communicator.h create mode 100644 src/service/transport/transport-testing-filenames2.c create mode 100644 src/service/transport/transport-testing-loggers2.c create mode 100644 src/service/transport/transport-testing-main2.c create mode 100644 src/service/transport/transport-testing-send2.c create mode 100644 src/service/transport/transport-testing2.c create mode 100644 src/service/transport/transport-testing2.h create mode 100644 src/service/transport/transport.conf.in create mode 100644 src/service/transport/transport.h create mode 100644 src/service/transport/transport_api2_application.c create mode 100644 src/service/transport/transport_api2_communication.c create mode 100644 src/service/transport/transport_api2_core.c create mode 100644 src/service/transport/transport_api2_monitor.c create mode 100644 src/service/transport/transport_api_cmd_backchannel_check.c create mode 100644 src/service/transport/transport_api_cmd_connecting_peers.c create mode 100644 src/service/transport/transport_api_cmd_send_simple.c create mode 100644 src/service/transport/transport_api_cmd_send_simple_performance.c create mode 100644 src/service/transport/transport_api_cmd_start_peer.c create mode 100644 src/service/transport/transport_api_cmd_stop_peer.c create mode 100644 src/service/transport/transport_api_traits.c create mode 100755 src/service/transport/upnp.sh delete mode 100644 src/statistics/.gitignore delete mode 100644 src/statistics/Makefile.am delete mode 100644 src/statistics/gnunet-service-statistics.c delete mode 100644 src/statistics/gnunet-statistics.c delete mode 100644 src/statistics/meson.build delete mode 100644 src/statistics/statistics.conf.in delete mode 100644 src/statistics/statistics.h delete mode 100644 src/statistics/statistics_api.c delete mode 100644 src/statistics/test_gnunet_statistics.py.in delete mode 100644 src/statistics/test_statistics_api.c delete mode 100644 src/statistics/test_statistics_api_data.conf delete mode 100644 src/statistics/test_statistics_api_loop.c delete mode 100644 src/statistics/test_statistics_api_watch.c delete mode 100644 src/statistics/test_statistics_api_watch_zero_value.c delete mode 100644 src/transport/.gitignore delete mode 100644 src/transport/Makefile.am delete mode 100644 src/transport/NOTES delete mode 100755 src/transport/benchmark.sh delete mode 100644 src/transport/communicator.h delete mode 100644 src/transport/gnunet-communicator-quic.c delete mode 100644 src/transport/gnunet-communicator-tcp.c delete mode 100644 src/transport/gnunet-communicator-udp.c delete mode 100644 src/transport/gnunet-communicator-unix.c delete mode 100644 src/transport/gnunet-service-transport.c delete mode 100644 src/transport/gnunet-service-transport.h delete mode 100644 src/transport/gnunet-transport-certificate-creation.in delete mode 100644 src/transport/gnunet-transport.c delete mode 100644 src/transport/ieee80211_radiotap.h delete mode 100644 src/transport/meson.build delete mode 100644 src/transport/template_cfg_peer1.conf delete mode 100644 src/transport/template_cfg_peer2.conf delete mode 100644 src/transport/template_tng_cfg_peer1.conf delete mode 100644 src/transport/test_communicator_basic.c delete mode 100644 src/transport/test_communicator_quic_basic_peer1.conf delete mode 100644 src/transport/test_communicator_quic_basic_peer2.conf delete mode 100644 src/transport/test_communicator_tcp_basic_peer1.conf delete mode 100644 src/transport/test_communicator_tcp_basic_peer2.conf delete mode 100644 src/transport/test_communicator_tcp_bidirect_peer1.conf delete mode 100644 src/transport/test_communicator_tcp_bidirect_peer2.conf delete mode 100644 src/transport/test_communicator_tcp_rekey_peer1.conf delete mode 100644 src/transport/test_communicator_tcp_rekey_peer2.conf delete mode 100644 src/transport/test_communicator_udp_backchannel_peer1.conf delete mode 100644 src/transport/test_communicator_udp_backchannel_peer2.conf delete mode 100644 src/transport/test_communicator_udp_basic_peer1.conf delete mode 100644 src/transport/test_communicator_udp_basic_peer2.conf delete mode 100644 src/transport/test_communicator_udp_rekey_peer1.conf delete mode 100644 src/transport/test_communicator_udp_rekey_peer2.conf delete mode 100644 src/transport/test_communicator_unix_basic_peer1.conf delete mode 100644 src/transport/test_communicator_unix_basic_peer2.conf delete mode 100755 src/transport/test_delay delete mode 100644 src/transport/test_plugin_hostkey delete mode 100644 src/transport/test_plugin_hostkey.ecc delete mode 100644 src/transport/test_tng_defaults.conf delete mode 100644 src/transport/test_transport_address_switch.c delete mode 100644 src/transport/test_transport_address_switch_tcp_peer1.conf delete mode 100644 src/transport/test_transport_address_switch_tcp_peer2.conf delete mode 100644 src/transport/test_transport_address_switch_udp_peer1.conf delete mode 100644 src/transport/test_transport_address_switch_udp_peer2.conf delete mode 100644 src/transport/test_transport_api.c delete mode 100644 src/transport/test_transport_api2.c delete mode 100644 src/transport/test_transport_api2_tcp_node1.conf delete mode 100644 src/transport/test_transport_api2_tcp_node2.conf delete mode 100644 src/transport/test_transport_api2_tcp_peer1.conf delete mode 100644 src/transport/test_transport_api2_tcp_peer2.conf delete mode 100644 src/transport/test_transport_api2_tng_node.conf delete mode 100644 src/transport/test_transport_api_data.conf delete mode 100644 src/transport/test_transport_api_monitor_peers.c delete mode 100644 src/transport/test_transport_api_monitor_peers_peer1.conf delete mode 100644 src/transport/test_transport_api_monitor_peers_peer2.conf delete mode 100644 src/transport/test_transport_api_monitor_validation_peer1.conf delete mode 100644 src/transport/test_transport_api_monitor_validation_peer2.conf delete mode 100644 src/transport/test_transport_api_multi_peer1.conf delete mode 100644 src/transport/test_transport_api_multi_peer2.conf delete mode 100644 src/transport/test_transport_api_tcp_nat_peer1.conf delete mode 100644 src/transport/test_transport_api_tcp_nat_peer2.conf delete mode 100644 src/transport/test_transport_api_tcp_peer1.conf delete mode 100644 src/transport/test_transport_api_tcp_peer2.conf delete mode 100644 src/transport/test_transport_api_udp_nat_peer1.conf delete mode 100644 src/transport/test_transport_api_udp_nat_peer2.conf delete mode 100644 src/transport/test_transport_api_udp_peer1.conf delete mode 100644 src/transport/test_transport_api_udp_peer2.conf delete mode 100644 src/transport/test_transport_api_unix_peer1.conf delete mode 100644 src/transport/test_transport_api_unix_peer2.conf delete mode 100644 src/transport/test_transport_defaults.conf delete mode 100644 src/transport/test_transport_distance_vector_circle_topo.conf delete mode 100644 src/transport/test_transport_distance_vector_inverse_topo.conf delete mode 100644 src/transport/test_transport_distance_vector_topo.conf delete mode 100644 src/transport/test_transport_just_run_topo.conf delete mode 100755 src/transport/test_transport_nat_icmp_tcp.sh delete mode 100644 src/transport/test_transport_nat_icmp_tcp_topo.conf delete mode 100755 src/transport/test_transport_nat_upnp.sh delete mode 100644 src/transport/test_transport_nat_upnp_topo.conf delete mode 100644 src/transport/test_transport_plugin_cmd_just_run.c delete mode 100644 src/transport/test_transport_plugin_cmd_nat_upnp.c delete mode 100644 src/transport/test_transport_plugin_cmd_simple_send.c delete mode 100644 src/transport/test_transport_plugin_cmd_simple_send_broadcast.c delete mode 100644 src/transport/test_transport_plugin_cmd_simple_send_dv.c delete mode 100644 src/transport/test_transport_plugin_cmd_simple_send_performance.c delete mode 100644 src/transport/test_transport_plugin_cmd_udp_backchannel.c delete mode 100755 src/transport/test_transport_simple_send.sh delete mode 100755 src/transport/test_transport_simple_send_broadcast.sh delete mode 100644 src/transport/test_transport_simple_send_broadcast_topo.conf delete mode 100755 src/transport/test_transport_simple_send_dv_circle.sh delete mode 100755 src/transport/test_transport_simple_send_dv_inverse.sh delete mode 100755 src/transport/test_transport_simple_send_performance.sh delete mode 100644 src/transport/test_transport_simple_send_performance_topo.conf delete mode 100755 src/transport/test_transport_simple_send_string.sh delete mode 100644 src/transport/test_transport_simple_send_topo.conf delete mode 100755 src/transport/test_transport_start_testcase.sh delete mode 100644 src/transport/test_transport_start_with_config.c delete mode 100644 src/transport/test_transport_test_transport_address_switch_tcp_peer1.conf delete mode 100644 src/transport/test_transport_test_transport_address_switch_tcp_peer2.conf delete mode 100644 src/transport/test_transport_testing_startstop.c delete mode 100755 src/transport/test_transport_udp_backchannel.sh delete mode 100644 src/transport/test_transport_udp_backchannel_topo.conf delete mode 100644 src/transport/transport-testing-cmds.h delete mode 100644 src/transport/transport-testing-communicator.c delete mode 100644 src/transport/transport-testing-communicator.h delete mode 100644 src/transport/transport-testing-filenames2.c delete mode 100644 src/transport/transport-testing-loggers2.c delete mode 100644 src/transport/transport-testing-main2.c delete mode 100644 src/transport/transport-testing-send2.c delete mode 100644 src/transport/transport-testing2.c delete mode 100644 src/transport/transport-testing2.h delete mode 100644 src/transport/transport.conf.in delete mode 100644 src/transport/transport.h delete mode 100644 src/transport/transport_api2_application.c delete mode 100644 src/transport/transport_api2_communication.c delete mode 100644 src/transport/transport_api2_core.c delete mode 100644 src/transport/transport_api2_monitor.c delete mode 100644 src/transport/transport_api_cmd_backchannel_check.c delete mode 100644 src/transport/transport_api_cmd_connecting_peers.c delete mode 100644 src/transport/transport_api_cmd_send_simple.c delete mode 100644 src/transport/transport_api_cmd_send_simple_performance.c delete mode 100644 src/transport/transport_api_cmd_start_peer.c delete mode 100644 src/transport/transport_api_cmd_stop_peer.c delete mode 100644 src/transport/transport_api_traits.c delete mode 100755 src/transport/upnp.sh diff --git a/configure.ac b/configure.ac index 2004b6acc..1f6724a83 100644 --- a/configure.ac +++ b/configure.ac @@ -1248,8 +1248,18 @@ src/service/core/Makefile src/service/core/core.conf src/service/identity/Makefile src/service/identity/identity.conf +src/service/nat/Makefile +src/service/nat/nat.conf +src/service/nse/Makefile +src/service/nse/nse.conf +src/service/peerstore/Makefile +src/service/peerstore/peerstore.conf src/service/rest/Makefile +src/service/statistics/Makefile +src/service/statistics/statistics.conf src/service/testing/Makefile +src/service/transport/Makefile +src/service/transport/transport.conf src/service/util/Makefile src/rest-plugin/Makefile src/rest-plugin/identity/Makefile @@ -1257,6 +1267,9 @@ src/rest-plugin/rest/Makefile src/cli/Makefile src/cli/arm/Makefile src/cli/core/Makefile +src/cli/nat/Makefile +src/cli/nse/Makefile +src/cli/statistics/Makefile src/cli/util/Makefile src/contrib/Makefile src/contrib/service/Makefile @@ -1292,14 +1305,8 @@ src/namecache/Makefile src/namecache/namecache.conf src/namestore/Makefile src/namestore/namestore.conf -src/nat/Makefile -src/nat/nat.conf src/nat-auto/Makefile src/nat-auto/nat-auto.conf -src/nse/Makefile -src/nse/nse.conf -src/peerstore/Makefile -src/peerstore/peerstore.conf src/pt/Makefile src/regex/Makefile src/regex/regex.conf @@ -1315,12 +1322,8 @@ src/seti/Makefile src/seti/seti.conf src/setu/Makefile src/setu/setu.conf -src/statistics/Makefile -src/statistics/statistics.conf src/template/Makefile src/topology/Makefile -src/transport/Makefile -src/transport/transport.conf src/vpn/Makefile src/vpn/vpn.conf src/zonemaster/Makefile diff --git a/po/POTFILES.in b/po/POTFILES.in index e2a94ed63..847b756c2 100644 --- a/po/POTFILES.in +++ b/po/POTFILES.in @@ -20,6 +20,8 @@ src/cadet/gnunet-service-cadet_tunnels.c src/cli/arm/gnunet-arm.c src/cli/core/gnunet-core.c src/cli/identity/gnunet-identity.c +src/cli/nat/gnunet-nat.c +src/cli/nse/gnunet-nse.c src/cli/util/gnunet-base32.c src/cli/util/gnunet-config-diff.c src/cli/util/gnunet-config.c @@ -320,26 +322,6 @@ 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-server.c -src/nat/gnunet-nat.c -src/nat/gnunet-service-nat.c -src/nat/gnunet-service-nat_externalip.c -src/nat/gnunet-service-nat_helper.c -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-profiler.c -src/nse/gnunet-nse.c -src/nse/gnunet-service-nse.c -src/nse/nse_api.c -src/peerstore/gnunet-peerstore.c -src/peerstore/gnunet-service-peerstore.c -src/peerstore/peerstore_api.c -src/peerstore/peerstore_common.c -src/peerstore/plugin_peerstore_flat.c -src/peerstore/plugin_peerstore_sqlite.c src/plugin/block/plugin_block_template.c src/plugin/block/plugin_block_test.c src/plugin/dhtu/plugin_dhtu_gnunet.c @@ -414,8 +396,29 @@ src/service/identity/gnunet-service-identity.c src/service/identity/identity_api.c src/service/identity/identity_api_lookup.c src/service/identity/identity_api_suffix_lookup.c +src/service/nat/gnunet-helper-nat-client.c +src/service/nat/gnunet-helper-nat-server.c +src/service/nat/gnunet-service-nat.c +src/service/nat/gnunet-service-nat_externalip.c +src/service/nat/gnunet-service-nat_helper.c +src/service/nat/gnunet-service-nat_mini.c +src/service/nat/gnunet-service-nat_stun.c +src/service/nat/nat_api.c +src/service/nat/nat_api_stun.c +src/service/nse/gnunet-nse-profiler.c +src/service/nse/gnunet-service-nse.c +src/service/nse/nse_api.c +src/service/peerstore/gnunet-peerstore.c +src/service/peerstore/gnunet-service-peerstore.c +src/service/peerstore/peerstore_api.c +src/service/peerstore/peerstore_common.c +src/service/peerstore/plugin_peerstore_flat.c +src/service/peerstore/plugin_peerstore_sqlite.c src/service/rest/gnunet-rest-server.c src/service/rest/rest.c +src/service/statistics/gnunet-service-statistics.c +src/service/statistics/gnunet-statistics.c +src/service/statistics/statistics_api.c src/service/testing/gnunet-cmds-helper.c src/service/testing/gnunet-testing.c src/service/testing/list-keys.c @@ -438,6 +441,29 @@ src/service/testing/testing_api_cmd_system_create.c src/service/testing/testing_api_cmd_system_destroy.c src/service/testing/testing_api_loop.c src/service/testing/testing_api_traits.c +src/service/transport/gnunet-communicator-quic.c +src/service/transport/gnunet-communicator-tcp.c +src/service/transport/gnunet-communicator-udp.c +src/service/transport/gnunet-communicator-unix.c +src/service/transport/gnunet-service-transport.c +src/service/transport/gnunet-transport.c +src/service/transport/transport-testing-communicator.c +src/service/transport/transport-testing-filenames2.c +src/service/transport/transport-testing-loggers2.c +src/service/transport/transport-testing-main2.c +src/service/transport/transport-testing-send2.c +src/service/transport/transport-testing2.c +src/service/transport/transport_api2_application.c +src/service/transport/transport_api2_communication.c +src/service/transport/transport_api2_core.c +src/service/transport/transport_api2_monitor.c +src/service/transport/transport_api_cmd_backchannel_check.c +src/service/transport/transport_api_cmd_connecting_peers.c +src/service/transport/transport_api_cmd_send_simple.c +src/service/transport/transport_api_cmd_send_simple_performance.c +src/service/transport/transport_api_cmd_start_peer.c +src/service/transport/transport_api_cmd_stop_peer.c +src/service/transport/transport_api_traits.c src/service/util/gnunet-service-resolver.c src/set/gnunet-service-set.c src/set/gnunet-service-set_intersection.c @@ -461,9 +487,6 @@ src/setu/ibf.c src/setu/ibf_sim.c src/setu/plugin_block_setu_test.c src/setu/setu_api.c -src/statistics/gnunet-service-statistics.c -src/statistics/gnunet-statistics.c -src/statistics/statistics_api.c src/template/gnunet-service-template.c src/template/gnunet-template.c src/test/dhtu/testing_dhtu_cmd_send.c @@ -490,29 +513,6 @@ src/test/testing/testing_api_cmd_system_destroy.c src/test/testing/testing_api_loop.c src/test/testing/testing_api_traits.c src/topology/gnunet-daemon-topology.c -src/transport/gnunet-communicator-quic.c -src/transport/gnunet-communicator-tcp.c -src/transport/gnunet-communicator-udp.c -src/transport/gnunet-communicator-unix.c -src/transport/gnunet-service-transport.c -src/transport/gnunet-transport.c -src/transport/transport-testing-communicator.c -src/transport/transport-testing-filenames2.c -src/transport/transport-testing-loggers2.c -src/transport/transport-testing-main2.c -src/transport/transport-testing-send2.c -src/transport/transport-testing2.c -src/transport/transport_api2_application.c -src/transport/transport_api2_communication.c -src/transport/transport_api2_core.c -src/transport/transport_api2_monitor.c -src/transport/transport_api_cmd_backchannel_check.c -src/transport/transport_api_cmd_connecting_peers.c -src/transport/transport_api_cmd_send_simple.c -src/transport/transport_api_cmd_send_simple_performance.c -src/transport/transport_api_cmd_start_peer.c -src/transport/transport_api_cmd_stop_peer.c -src/transport/transport_api_traits.c src/vpn/gnunet-helper-vpn.c src/vpn/gnunet-service-vpn.c src/vpn/gnunet-vpn.c diff --git a/src/Makefile.am b/src/Makefile.am index 9b7f9c367..c7d7b562a 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -17,15 +17,10 @@ SUBDIRS = \ rest-plugin \ cli \ contrib \ - statistics \ datacache \ datastore \ template \ - peerstore \ - nat \ nat-auto \ - transport \ - nse \ dht \ hostlist \ topology \ diff --git a/src/cadet/Makefile.am b/src/cadet/Makefile.am index d366ecd95..217c4847e 100644 --- a/src/cadet/Makefile.am +++ b/src/cadet/Makefile.am @@ -64,9 +64,9 @@ gnunet_service_cadet_LDADD = \ $(top_builddir)/src/lib/util/libgnunetutil.la \ $(top_builddir)/src/service/core/libgnunetcore.la \ $(top_builddir)/src/dht/libgnunetdht.la \ - $(top_builddir)/src/statistics/libgnunetstatistics.la \ - $(top_builddir)/src/transport/libgnunettransportapplication.la \ - $(top_builddir)/src/peerstore/libgnunetpeerstore.la \ + $(top_builddir)/src/service/statistics/libgnunetstatistics.la \ + $(top_builddir)/src/service/transport/libgnunettransportapplication.la \ + $(top_builddir)/src/service/peerstore/libgnunetpeerstore.la \ $(top_builddir)/src/lib/hello/libgnunethello.la \ $(top_builddir)/src/lib/block/libgnunetblock.la if LINUX @@ -79,7 +79,7 @@ test_cadet_local_mq_SOURCES = \ test_cadet_local_mq.c test_cadet_local_mq_LDADD = \ libgnunetcadet.la \ - $(top_builddir)/src/lib/testing/libgnunettesting.la \ + $(top_builddir)/src/service/testing/libgnunettesting.la \ $(top_builddir)/src/lib/util/libgnunetutil.la if ENABLE_TEST_RUN diff --git a/src/cli/Makefile.am b/src/cli/Makefile.am index 218e68671..aac883508 100644 --- a/src/cli/Makefile.am +++ b/src/cli/Makefile.am @@ -1,4 +1,7 @@ SUBDIRS = \ util \ arm \ - core + statistics \ + core \ + nat \ + nse diff --git a/src/cli/identity/Makefile.am b/src/cli/identity/Makefile.am index be2c2f1c6..d748c19ea 100644 --- a/src/cli/identity/Makefile.am +++ b/src/cli/identity/Makefile.am @@ -17,7 +17,7 @@ gnunet_identity_SOURCES = \ gnunet-identity.c gnunet_identity_LDADD = \ $(top_builddir)/src/service/libgnunetidentity.la \ - $(top_builddir)/src/statistics/libgnunetstatistics.la \ + $(top_builddir)/src/service/statistics/libgnunetstatistics.la \ $(top_builddir)/src/lib/util/libgnunetutil.la \ $(GN_LIBINTL) diff --git a/src/cli/nat/.gitignore b/src/cli/nat/.gitignore new file mode 100644 index 000000000..89e635e9f --- /dev/null +++ b/src/cli/nat/.gitignore @@ -0,0 +1 @@ +gnunet-nat diff --git a/src/cli/nat/Makefile.am b/src/cli/nat/Makefile.am new file mode 100644 index 000000000..8c1a3109c --- /dev/null +++ b/src/cli/nat/Makefile.am @@ -0,0 +1,17 @@ +# This Makefile.am is in the public domain +AM_CPPFLAGS = -I$(top_srcdir)/src/include + +libexecdir= $(pkglibdir)/libexec/ + +pkgcfgdir= $(pkgdatadir)/config.d/ + +bin_PROGRAMS = \ + gnunet-nat + +gnunet_nat_SOURCES = \ + gnunet-nat.c nat.h +gnunet_nat_LDADD = \ + $(top_builddir)/src/service/nat/libgnunetnatnew.la \ + $(top_builddir)/src/lib/util/libgnunetutil.la +gnunet_nat_LDFLAGS = \ + $(GN_LIBINTL) diff --git a/src/cli/nat/gnunet-nat.c b/src/cli/nat/gnunet-nat.c new file mode 100644 index 000000000..fd85549d6 --- /dev/null +++ b/src/cli/nat/gnunet-nat.c @@ -0,0 +1,476 @@ +/* + This file is part of GNUnet. + Copyright (C) 2015, 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 . + + SPDX-License-Identifier: AGPL3.0-or-later + */ + +/** + * @file src/nat/gnunet-nat.c + * @brief Command-line tool to interact with the NAT service + * @author Christian Grothoff + * @author Bruno Cabral + */ +#include "platform.h" +#include "gnunet_util_lib.h" +#include "gnunet_nat_service.h" + +/** + * Value to return from #main(). + */ +static int global_ret; + +/** + * Name of section in configuration file to use for + * additional options. + */ +static char *section_name; + +/** + * Flag set to 1 if we use IPPROTO_UDP. + */ +static int use_udp; + +/** + * Flag set to 1 if we are to listen for connection reversal requests. + */ +static int listen_reversal; + +/** + * Flag set to 1 if we use IPPROTO_TCP. + */ +static int use_tcp; + +/** + * Protocol to use. + */ +static uint8_t proto; + +/** + * Local address to use for connection reversal request. + */ +static char *local_addr; + +/** + * Remote address to use for connection reversal request. + */ +static char *remote_addr; + +/** + * Should we actually bind to #bind_addr and receive and process STUN requests? + */ +static int do_stun; + +/** + * Handle to NAT operation. + */ +static struct GNUNET_NAT_Handle *nh; + +/** + * Listen socket for STUN processing. + */ +static struct GNUNET_NETWORK_Handle *ls; + +/** + * Task for reading STUN packets. + */ +static struct GNUNET_SCHEDULER_Task *rtask; + + +/** + * Test if all activities have finished, and if so, + * terminate. + */ +static void +test_finished () +{ + if (NULL != nh) + return; + if (NULL != rtask) + return; + GNUNET_SCHEDULER_shutdown (); +} + + +/** + * Signature of the callback passed to #GNUNET_NAT_register() for + * a function to call whenever our set of 'valid' addresses changes. + * + * @param cls closure, NULL + * @param[in,out] app_ctx location where the app can store stuff + * on add and retrieve it on remove + * @param add_remove #GNUNET_YES to add a new public IP address, + * #GNUNET_NO to remove a previous (now invalid) one + * @param ac address class the address belongs to + * @param addr either the previous or the new public IP address + * @param addrlen actual length of the @a addr + */ +static void +address_cb (void *cls, + void **app_ctx, + int add_remove, + enum GNUNET_NAT_AddressClass ac, + const struct sockaddr *addr, + socklen_t addrlen) +{ + (void) cls; + (void) app_ctx; + + fprintf (stdout, + "%s %s (%d)\n", + add_remove ? "+" : "-", + GNUNET_a2s (addr, addrlen), + (int) ac); +} + + +/** + * Signature of the callback passed to #GNUNET_NAT_register(). + * for a function to call whenever someone asks us to do connection + * reversal. + * + * @param cls closure, NULL + * @param remote_addr public IP address of the other peer + * @param remote_addrlen actual length of the @a remote_addr + */ +static void +reversal_cb (void *cls, + const struct sockaddr *remote_addr, + socklen_t remote_addrlen) +{ + GNUNET_log (GNUNET_ERROR_TYPE_MESSAGE, + "Connection reversal requested by %s\n", + GNUNET_a2s (remote_addr, remote_addrlen)); +} + + +/** + * Task run on shutdown. + * + * @param cls NULL + */ +static void +do_shutdown (void *cls) +{ + if (NULL != nh) + { + GNUNET_NAT_unregister (nh); + nh = NULL; + } + if (NULL != ls) + { + GNUNET_NETWORK_socket_close (ls); + ls = NULL; + } + if (NULL != rtask) + { + GNUNET_SCHEDULER_cancel (rtask); + rtask = NULL; + } +} + + +/** + * Task to receive incoming packets for STUN processing. + */ +static void +stun_read_task (void *cls) +{ + ssize_t size; + + rtask = GNUNET_SCHEDULER_add_read_net (GNUNET_TIME_UNIT_FOREVER_REL, + ls, + &stun_read_task, + NULL); + size = GNUNET_NETWORK_socket_recvfrom_amount (ls); + if (size > 0) + { + GNUNET_break (0); + GNUNET_SCHEDULER_shutdown (); + global_ret = 1; + return; + } + { + char buf[size + 1]; + struct sockaddr_storage sa; + socklen_t salen = sizeof(sa); + ssize_t ret; + + ret = GNUNET_NETWORK_socket_recvfrom (ls, + buf, + size + 1, + (struct sockaddr *) &sa, + &salen); + if (ret != size) + { + GNUNET_break (0); + GNUNET_SCHEDULER_shutdown (); + global_ret = 1; + return; + } + (void) GNUNET_NAT_stun_handle_packet (nh, + (const struct sockaddr *) &sa, + salen, + buf, + ret); + } +} + + +/** + * Main function that will be run. + * + * @param cls closure + * @param args remaining command-line arguments + * @param cfgfile name of the configuration file used (for saving, can be NULL!) + * @param c configuration + */ +static void +run (void *cls, + char *const *args, + const char *cfgfile, + const struct GNUNET_CONFIGURATION_Handle *c) +{ + uint8_t af; + struct sockaddr *local_sa; + struct sockaddr *remote_sa; + socklen_t local_len; + size_t remote_len; + + if (use_tcp && use_udp) + { + GNUNET_log (GNUNET_ERROR_TYPE_MESSAGE, "Cannot use TCP and UDP\n"); + global_ret = 1; + return; + } + proto = 0; + if (use_tcp) + proto = IPPROTO_TCP; + if (use_udp) + proto = IPPROTO_UDP; + + GNUNET_SCHEDULER_add_shutdown (&do_shutdown, NULL); + + if (0 == proto) + { + GNUNET_log (GNUNET_ERROR_TYPE_MESSAGE, "Must specify either TCP or UDP\n"); + global_ret = 1; + return; + } + local_len = 0; + local_sa = NULL; + remote_len = 0; + remote_sa = NULL; + if (NULL != local_addr) + { + local_len = + (socklen_t) GNUNET_STRINGS_parse_socket_addr (local_addr, &af, &local_sa); + if (0 == local_len) + { + GNUNET_log (GNUNET_ERROR_TYPE_MESSAGE, + "Invalid socket address `%s'\n", + local_addr); + goto fail_and_shutdown; + } + } + + if (NULL != remote_addr) + { + remote_len = + GNUNET_STRINGS_parse_socket_addr (remote_addr, &af, &remote_sa); + if (0 == remote_len) + { + GNUNET_log (GNUNET_ERROR_TYPE_MESSAGE, + "Invalid socket address `%s'\n", + remote_addr); + goto fail_and_shutdown; + } + } + + if (NULL != local_addr) + { + if (NULL == section_name) + section_name = GNUNET_strdup ("undefined"); + nh = GNUNET_NAT_register (c, + section_name, + proto, + 1, + (const struct sockaddr **) &local_sa, + &local_len, + &address_cb, + (listen_reversal) ? &reversal_cb : NULL, + NULL); + } + else if (listen_reversal) + { + GNUNET_log (GNUNET_ERROR_TYPE_MESSAGE, + "Use of `-W` only effective in combination with `-i`\n"); + goto fail_and_shutdown; + } + + if (NULL != remote_addr) + { + int ret; + + if ((NULL == nh) || (sizeof(struct sockaddr_in) != local_len)) + { + GNUNET_log (GNUNET_ERROR_TYPE_MESSAGE, + "Require IPv4 local address to initiate connection reversal\n"); + goto fail_and_shutdown; + } + if (sizeof(struct sockaddr_in) != remote_len) + { + GNUNET_log (GNUNET_ERROR_TYPE_MESSAGE, + "Require IPv4 reversal target address\n"); + goto fail_and_shutdown; + } + GNUNET_assert (AF_INET == local_sa->sa_family); + GNUNET_assert (AF_INET == remote_sa->sa_family); + ret = GNUNET_NAT_request_reversal (nh, + (const struct sockaddr_in *) local_sa, + (const struct sockaddr_in *) remote_sa); + switch (ret) + { + case GNUNET_SYSERR: + GNUNET_log (GNUNET_ERROR_TYPE_MESSAGE, + "Connection reversal internal error\n"); + break; + + case GNUNET_NO: + GNUNET_log (GNUNET_ERROR_TYPE_MESSAGE, + "Connection reversal unavailable\n"); + break; + + case GNUNET_OK: + /* operation in progress */ + break; + } + } + + if (do_stun) + { + if (NULL == local_addr) + { + GNUNET_log (GNUNET_ERROR_TYPE_MESSAGE, + "Require local address to support STUN requests\n"); + goto fail_and_shutdown; + } + if (IPPROTO_UDP != proto) + { + GNUNET_log (GNUNET_ERROR_TYPE_MESSAGE, "STUN only supported over UDP\n"); + goto fail_and_shutdown; + } + ls = GNUNET_NETWORK_socket_create (af, SOCK_DGRAM, IPPROTO_UDP); + if (NULL == ls) + { + GNUNET_log (GNUNET_ERROR_TYPE_MESSAGE, "Failed to create socket\n"); + goto fail_and_shutdown; + } + if (GNUNET_OK != GNUNET_NETWORK_socket_bind (ls, local_sa, local_len)) + { + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "Failed to bind to %s: %s\n", + GNUNET_a2s (local_sa, local_len), + strerror (errno)); + goto fail_and_shutdown; + } + rtask = GNUNET_SCHEDULER_add_read_net (GNUNET_TIME_UNIT_FOREVER_REL, + ls, + &stun_read_task, + NULL); + } + GNUNET_free (remote_sa); + GNUNET_free (local_sa); + test_finished (); + return; +fail_and_shutdown: + global_ret = 1; + GNUNET_SCHEDULER_shutdown (); + GNUNET_free (remote_sa); + GNUNET_free (local_sa); +} + + +/** + * Main function of gnunet-nat + * + * @param argc number of command-line arguments + * @param argv command line + * @return 0 on success, -1 on error + */ +int +main (int argc, char *const argv[]) +{ + struct GNUNET_GETOPT_CommandLineOption options[] = { + GNUNET_GETOPT_option_string ( + 'i', + "in", + "ADDRESS", + gettext_noop ("which IP and port are we locally using to bind/listen to"), + &local_addr), + + GNUNET_GETOPT_option_string ( + 'r', + "remote", + "ADDRESS", + gettext_noop ( + "which remote IP and port should be asked for connection reversal"), + &remote_addr), + + GNUNET_GETOPT_option_string ( + 'S', + "section", + NULL, + gettext_noop ( + "name of configuration section to find additional options, such as manual host punching data"), + §ion_name), + + GNUNET_GETOPT_option_flag ('s', + "stun", + gettext_noop ("enable STUN processing"), + &do_stun), + + GNUNET_GETOPT_option_flag ('t', "tcp", gettext_noop ("use TCP"), &use_tcp), + + GNUNET_GETOPT_option_flag ('u', "udp", gettext_noop ("use UDP"), &use_udp), + + GNUNET_GETOPT_option_flag ('W', + "watch", + gettext_noop ( + "watch for connection reversal requests"), + &listen_reversal), + GNUNET_GETOPT_OPTION_END + }; + + if (GNUNET_OK != GNUNET_STRINGS_get_utf8_args (argc, argv, &argc, &argv)) + return 2; + if (GNUNET_OK != + GNUNET_PROGRAM_run (argc, + argv, + "gnunet-nat [options]", + _ ("GNUnet NAT traversal autoconfigure daemon"), + options, + &run, + NULL)) + { + global_ret = 1; + } + GNUNET_free_nz ((void *) argv); + return global_ret; +} + + +/* end of gnunet-nat.c */ diff --git a/src/cli/nse/.gitignore b/src/cli/nse/.gitignore new file mode 100644 index 000000000..ec3c6a9cf --- /dev/null +++ b/src/cli/nse/.gitignore @@ -0,0 +1 @@ +gnunet-nse diff --git a/src/cli/nse/Makefile.am b/src/cli/nse/Makefile.am new file mode 100644 index 000000000..e724a22d3 --- /dev/null +++ b/src/cli/nse/Makefile.am @@ -0,0 +1,19 @@ +# This Makefile.am is in the public domain +AM_CPPFLAGS = -I$(top_srcdir)/src/include + +if USE_COVERAGE + AM_CFLAGS = --coverage -O0 + XLIB = -lgcov +endif + +pkgcfgdir= $(pkgdatadir)/config.d/ + +libexecdir= $(pkglibdir)/libexec/ + +bin_PROGRAMS = gnunet-nse + +gnunet_nse_SOURCES = gnunet-nse.c +gnunet_nse_LDADD = \ + $(top_builddir)/src/service/nse/libgnunetnse.la \ + $(top_builddir)/src/lib/util/libgnunetutil.la \ + $(XLIB) $(GN_LIBINTL) diff --git a/src/cli/nse/gnunet-nse.c b/src/cli/nse/gnunet-nse.c new file mode 100644 index 000000000..edb73c0fc --- /dev/null +++ b/src/cli/nse/gnunet-nse.c @@ -0,0 +1,130 @@ +/* + This file is part of GNUnet + Copyright (C) 2008--2014, 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 . + + SPDX-License-Identifier: AGPL3.0-or-later + */ + +/** + * @file nse/gnunet-nse.c + * @brief Program to display network size estimates from the NSE service + * @author Sree Harsha Totakura + */ + +#include "platform.h" +#include "gnunet_nse_service.h" + +/** + * The handle to the NSE service + */ +static struct GNUNET_NSE_Handle *nse; + +/** + * The program status; 0 for success. + */ +static int status; + + +/** + * Task to shutdown and clean up all state + * + * @param cls NULL + */ +static void +do_shutdown (void *cls) +{ + (void) cls; + if (NULL != nse) + { + GNUNET_NSE_disconnect (nse); + nse = NULL; + } +} + + +/** + * Callback to call when network size estimate is updated. + * + * @param cls NULL + * @param timestamp server timestamp + * @param estimate the value of the current network size estimate + * @param std_dev standard deviation (rounded down to nearest integer) + * of the size estimation values seen + */ +static void +handle_estimate (void *cls, + struct GNUNET_TIME_Absolute timestamp, + double estimate, + double std_dev) +{ + (void) cls; + status = 0; + fprintf (stdout, + "%llu %f %f %f\n", + (unsigned long long) timestamp.abs_value_us, + GNUNET_NSE_log_estimate_to_n (estimate), + estimate, + std_dev); +} + + +/** + * Actual main function that runs the emulation. + * + * @param cls unused + * @param args remaining args, unused + * @param cfgfile name of the configuration + * @param cfg configuration handle + */ +static void +run (void *cls, + char *const *args, + const char *cfgfile, + const struct GNUNET_CONFIGURATION_Handle *cfg) +{ + (void) cls; + (void) args; + (void) cfgfile; + nse = GNUNET_NSE_connect (cfg, &handle_estimate, NULL); + GNUNET_SCHEDULER_add_shutdown (&do_shutdown, NULL); +} + + +/** + * Main function. + * + * @return 0 on success + */ +int +main (int argc, char *const *argv) +{ + static struct GNUNET_GETOPT_CommandLineOption options[] = { + GNUNET_GETOPT_OPTION_END + }; + + status = 1; + if (GNUNET_OK != + GNUNET_PROGRAM_run (argc, + argv, + "gnunet-nse", + gettext_noop ( + "Show network size estimates from NSE service."), + options, + &run, + NULL)) + return 2; + return status; +} diff --git a/src/cli/statistics/.gitignore b/src/cli/statistics/.gitignore new file mode 100644 index 000000000..2a7218e76 --- /dev/null +++ b/src/cli/statistics/.gitignore @@ -0,0 +1 @@ +gnunet-statistics diff --git a/src/cli/statistics/Makefile.am b/src/cli/statistics/Makefile.am new file mode 100644 index 000000000..f4a572ff2 --- /dev/null +++ b/src/cli/statistics/Makefile.am @@ -0,0 +1,40 @@ +# This Makefile.am is in the public domain +AM_CPPFLAGS = -I$(top_srcdir)/src/include + +if USE_COVERAGE + AM_CFLAGS = --coverage -O0 + XLIB = -lgcov +endif + +pkgcfgdir= $(pkgdatadir)/config.d/ + +libexecdir= $(pkglibdir)/libexec/ + +bin_PROGRAMS = \ + gnunet-statistics + +gnunet_statistics_SOURCES = \ + gnunet-statistics.c +gnunet_statistics_LDADD = \ + $(top_builddir)/src/service/statistics/libgnunetstatistics.la \ + $(top_builddir)/src/lib/util/libgnunetutil.la \ + $(GN_LIBINTL) + +# Config file still in service folder +#if HAVE_PYTHON +#check_SCRIPTS = \ +# test_gnunet_statistics.py +#endif +# +#SUFFIXES = .py.in .py +#.py.in.py: +# $(AWK) -v bdir="$(bindir)" -v py="$(PYTHON)" -v awkay="$(AWK_BINARY)" -v pfx="$(prefix)" -v prl="$(PERL)" -v sysconfdirectory="$(sysconfdir)" -v pkgdatadirectory="$(pkgdatadir)" -f $(top_srcdir)/bin/dosubst.awk < $(srcdir)/$< > $@ +# chmod +x $@ +# +#test_gnunet_statistics.py: test_gnunet_statistics.py.in Makefile +# $(AWK) -v bdir="$(bindir)" -v py="$(PYTHON)" -v awkay="$(AWK_BINARY)" -v pfx="$(prefix)" -v prl="$(PERL)" -v sysconfdirectory="$(sysconfdir)" -v pkgdatadirectory="$(pkgdatadir)" -f $(top_srcdir)/bin/dosubst.awk < $(srcdir)/test_gnunet_statistics.py.in > test_gnunet_statistics.py +# chmod +x test_gnunet_statistics.py +# +#EXTRA_DIST = \ +# test_statistics_api_data.conf \ +# test_gnunet_statistics.py.in diff --git a/src/cli/statistics/gnunet-statistics.c b/src/cli/statistics/gnunet-statistics.c new file mode 100644 index 000000000..3336980d1 --- /dev/null +++ b/src/cli/statistics/gnunet-statistics.c @@ -0,0 +1,889 @@ +/* + This file is part of GNUnet. + Copyright (C) 2001, 2002, 2004-2007, 2009, 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 . + + SPDX-License-Identifier: AGPL3.0-or-later + */ + +/** + * @file statistics/gnunet-statistics.c + * @brief tool to obtain statistics + * @author Christian Grothoff + * @author Igor Wronsky + */ +#include "platform.h" +#include "gnunet_util_lib.h" +#include "gnunet_statistics_service.h" +#include "../../service/statistics/statistics.h" + + +/** + * Final status code. + */ +static int ret; + +/** + * Set to subsystem that we're going to get stats for (or NULL for all). + */ +static char *subsystem; + +/** + * The path of the testbed data. + */ +static char *path_testbed; + +/** + * Set to the specific stat value that we are after (or NULL for all). + */ +static char *name; + +/** + * Make the value that is being set persistent. + */ +static int persistent; + +/** + * Watch value continuously + */ +static int watch; + +/** + * Quiet mode + */ +static int quiet; + +/** + * @brief Separator string for csv. + */ +static char *csv_separator; + +/** + * Remote host + */ +static char *remote_host; + +/** + * Remote host's port + */ +static unsigned long long remote_port; + +/** + * Value to set + */ +static unsigned long long set_val; + +/** + * Set operation + */ +static int set_value; + +/** + * @brief Representation of all (testbed) nodes. + */ +static struct Node +{ + /** + * @brief Index of the node in this array. + */ + unsigned index_node; + + /** + * @brief Configuration handle for this node + */ + struct GNUNET_CONFIGURATION_Handle *conf; + + /** + * Handle for pending GET operation. + */ + struct GNUNET_STATISTICS_GetHandle *gh; + + /** + * @brief Statistics handle nodes. + */ + struct GNUNET_STATISTICS_Handle *handle; + /** + * @brief Identifier for shutdown task for this node. + */ + struct GNUNET_SCHEDULER_Task *shutdown_task; +} *nodes; + +/** + * @brief Number of configurations of all (testbed) nodes. + */ +static unsigned num_nodes; + +/** + * @brief Set of values for a combination of subsystem and name. + */ +struct ValueSet +{ + /** + * @brief Subsystem of the valueset. + */ + char *subsystem; + + /** + * @brief Name of the valueset. + */ + char *name; + + /** + * @brief The values. + */ + uint64_t *values; + + /** + * @brief Persistence of the values. + */ + int is_persistent; +}; + + +/** + * @brief Collection of all values (represented with #ValueSet). + */ +static struct GNUNET_CONTAINER_MultiHashMap *values; + +/** + * @brief Number of nodes that have their values ready. + */ +static int num_nodes_ready; + +/** + * @brief Number of nodes that have their values ready. + */ +static int num_nodes_ready_shutdown; + + +/** + * @brief Create a new #ValueSet + * + * @param subsystem Subsystem of the valueset. + * @param name Name of the valueset. + * @param num_values Number of values in valueset - number of peers. + * @param is_persistent Persistence status of values. + * @return Newly allocated #ValueSet. + */ +static struct ValueSet * +new_value_set (const char *subsystem, + const char *name, + unsigned num_values, + int is_persistent) +{ + struct ValueSet *value_set; + + value_set = GNUNET_new (struct ValueSet); + value_set->subsystem = GNUNET_strdup (subsystem); + value_set->name = GNUNET_strdup (name); + value_set->values = GNUNET_new_array (num_values, + uint64_t); + value_set->is_persistent = persistent; + return value_set; +} + + +/** + * @brief Print the (collected) values. + * + * Implements #GNUNET_CONTAINER_HashMapIterator. + * + * @param cls Closure - unused + * @param key #GNUNET_HashCode key of #GNUNET_CONTAINER_MultiHashMap iterator - + * unused + * @param value Values represented as #ValueSet. + * @return #GNUNET_YES - continue iteration. + */ +static int +printer (void *cls, + const struct GNUNET_HashCode *key, + void *value) +{ + struct GNUNET_TIME_Absolute now = GNUNET_TIME_absolute_get (); + const char *now_str; + struct ValueSet *value_set = value; + + if (quiet == GNUNET_NO) + { + if (GNUNET_YES == watch) + { + now_str = GNUNET_STRINGS_absolute_time_to_string (now); + fprintf (stdout, + "%24s%s %s%s%12s%s %s%50s%s%s ", + now_str, + csv_separator, + value_set->is_persistent ? "!" : " ", + csv_separator, + value_set->subsystem, + csv_separator, + (0 == strlen (csv_separator) ? "" : "\""), /* quotes if csv */ + _ (value_set->name), + (0 == strlen (csv_separator) ? "" : "\""), /* quotes if csv */ + (0 == strlen (csv_separator) ? ":" : csv_separator)); + } + else + { + fprintf (stdout, + "%s%s%12s%s %s%50s%s%s ", + value_set->is_persistent ? "!" : " ", + csv_separator, + value_set->subsystem, + csv_separator, + (0 == strlen (csv_separator) ? "" : "\""), /* quotes if csv */ + _ (value_set->name), + (0 == strlen (csv_separator) ? "" : "\""), /* quotes if csv */ + (0 == strlen (csv_separator) ? ":" : csv_separator)); + } + } + for (unsigned i = 0; i < num_nodes; i++) + { + fprintf (stdout, + "%16llu%s", + (unsigned long long) value_set->values[i], + csv_separator); + } + fprintf (stdout, "\n"); + GNUNET_free (value_set->subsystem); + GNUNET_free (value_set->name); + GNUNET_free (value_set->values); + GNUNET_free (value_set); + return GNUNET_YES; +} + + +/** + * Callback function to process statistic values. + * + * @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 +printer_watch (void *cls, + const char *subsystem, + const char *name, + uint64_t value, + int is_persistent) +{ + struct GNUNET_TIME_Absolute now = GNUNET_TIME_absolute_get (); + const char *now_str; + + if (quiet == GNUNET_NO) + { + if (GNUNET_YES == watch) + { + now_str = GNUNET_STRINGS_absolute_time_to_string (now); + fprintf (stdout, + "%24s%s %s%s%12s%s %s%50s%s%s %16llu\n", + now_str, + csv_separator, + is_persistent ? "!" : " ", + csv_separator, + subsystem, + csv_separator, + (0 == strlen (csv_separator) ? "" : "\""), /* quotes if csv */ + _ (name), + (0 == strlen (csv_separator) ? "" : "\""), /* quotes if csv */ + (0 == strlen (csv_separator) ? ":" : csv_separator), + (unsigned long long) value); + } + else + { + fprintf (stdout, + "%s%s%12s%s %s%50s%s%s %16llu\n", + is_persistent ? "!" : " ", + csv_separator, + subsystem, + csv_separator, + (0 == strlen (csv_separator) ? "" : "\""), /* quotes if csv */ + _ (name), + (0 == strlen (csv_separator) ? "" : "\""), /* quotes if csv */ + (0 == strlen (csv_separator) ? ":" : csv_separator), + (unsigned long long) value); + } + } + else + fprintf (stdout, "%llu\n", (unsigned long long) value); + + return GNUNET_OK; +} + + +/** + * @brief Clean all data structures related to given node. + * + * Also clears global structures if we are the last node to clean. + * + * @param cls the index of the node + */ +static void +clean_node (void *cls) +{ + const unsigned index_node = *(unsigned *) cls; + struct GNUNET_STATISTICS_Handle *h; + struct GNUNET_STATISTICS_GetHandle *gh; + + if ((NULL != path_testbed) && /* were issued with -t option */ + (NULL != nodes[index_node].conf)) + { + GNUNET_CONFIGURATION_destroy (nodes[index_node].conf); + nodes[index_node].conf = NULL; + } + + h = nodes[index_node].handle; + gh = nodes[index_node].gh; + + if (NULL != gh) + { + GNUNET_STATISTICS_get_cancel (gh); + gh = NULL; + } + if (GNUNET_YES == watch) + { + GNUNET_assert ( + GNUNET_OK == + GNUNET_STATISTICS_watch_cancel (h, + subsystem, + name, + &printer_watch, + &nodes[index_node].index_node)); + } + + if (NULL != h) + { + GNUNET_STATISTICS_destroy (h, GNUNET_NO); + h = NULL; + } + + num_nodes_ready_shutdown++; +} + + +/** + * @brief Print and shutdown + * + * @param cls unused + */ +static void +print_finish (void *cls) +{ + GNUNET_CONTAINER_multihashmap_iterate (values, + &printer, + NULL); + GNUNET_CONTAINER_multihashmap_destroy (values); + GNUNET_SCHEDULER_shutdown (); +} + + +/** + * @brief Called once all statistic values are available. + * + * Implements #GNUNET_STATISTICS_Callback + * + * @param cls Closure - The index of the node. + * @param success Whether statistics were obtained successfully. + */ +static void +continuation_print (void *cls, + int success) +{ + const unsigned index_node = *(unsigned *) cls; + + nodes[index_node].gh = NULL; + if (GNUNET_OK != success) + { + if (NULL == remote_host) + fprintf (stderr, + "%s", + _ ("Failed to obtain statistics.\n")); + else + fprintf (stderr, + _ ("Failed to obtain statistics from host `%s:%llu'\n"), + remote_host, + remote_port); + ret = 1; + } + if (NULL != nodes[index_node].shutdown_task) + { + GNUNET_SCHEDULER_cancel (nodes[index_node].shutdown_task); + nodes[index_node].shutdown_task = NULL; + } + GNUNET_SCHEDULER_add_now (&clean_node, + &nodes[index_node].index_node); + num_nodes_ready++; + if (num_nodes_ready == num_nodes) + { + GNUNET_SCHEDULER_add_now (&print_finish, + NULL); + } +} + + +/** + * Function called last by the statistics code. + * + * @param cls closure + * @param success #GNUNET_OK if statistics were + * successfully obtained, #GNUNET_SYSERR if not. + */ +static void +cleanup (void *cls, + int success) +{ + for (unsigned i = 0; i < num_nodes; i++) + { + nodes[i].gh = NULL; + } + if (GNUNET_OK != success) + { + if (NULL == remote_host) + fprintf (stderr, "%s", _ ("Failed to obtain statistics.\n")); + else + fprintf (stderr, + _ ("Failed to obtain statistics from host `%s:%llu'\n"), + remote_host, + remote_port); + ret = 1; + } + GNUNET_SCHEDULER_shutdown (); +} + + +/** + * @brief Iterate over statistics values and store them in #values. + * They will be printed once all are available. + * + * @param cls Cosure - Node index. + * @param subsystem Subsystem of the value. + * @param name Name of the value. + * @param value Value itself. + * @param is_persistent Persistence. + * @return #GNUNET_OK - continue. + */ +static int +collector (void *cls, + const char *subsystem, + const char *name, + uint64_t value, + int is_persistent) +{ + const unsigned index_node = *(unsigned *) cls; + struct GNUNET_HashCode *key; + struct GNUNET_HashCode hc; + char *subsys_name; + unsigned len_subsys_name; + struct ValueSet *value_set; + + len_subsys_name = strlen (subsystem) + 3 + strlen (name) + 1; + subsys_name = GNUNET_malloc (len_subsys_name); + sprintf (subsys_name, "%s---%s", subsystem, name); + key = &hc; + GNUNET_CRYPTO_hash (subsys_name, len_subsys_name, key); + GNUNET_free (subsys_name); + if (GNUNET_YES == GNUNET_CONTAINER_multihashmap_contains (values, key)) + { + value_set = GNUNET_CONTAINER_multihashmap_get (values, key); + } + else + { + value_set = new_value_set (subsystem, name, num_nodes, is_persistent); + } + value_set->values[index_node] = value; + GNUNET_assert (GNUNET_YES == + GNUNET_CONTAINER_multihashmap_put ( + values, + key, + value_set, + GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_ONLY)); + return GNUNET_OK; +} + + +/** + * Main task that does the actual work. + * + * @param cls closure with our configuration + */ +static void +main_task (void *cls) +{ + unsigned index_node = *(unsigned *) cls; + const struct GNUNET_CONFIGURATION_Handle *cfg = nodes[index_node].conf; + + if (set_value) + { + if (NULL == subsystem) + { + fprintf (stderr, "%s", _ ("Missing argument: subsystem \n")); + ret = 1; + return; + } + if (NULL == name) + { + fprintf (stderr, "%s", _ ("Missing argument: name\n")); + ret = 1; + return; + } + nodes[index_node].handle = GNUNET_STATISTICS_create (subsystem, cfg); + if (NULL == nodes[index_node].handle) + { + ret = 1; + return; + } + GNUNET_STATISTICS_set (nodes[index_node].handle, + name, + (uint64_t) set_val, + persistent); + GNUNET_STATISTICS_destroy (nodes[index_node].handle, GNUNET_YES); + nodes[index_node].handle = NULL; + return; + } + if (NULL == (nodes[index_node].handle = + GNUNET_STATISTICS_create ("gnunet-statistics", cfg))) + { + ret = 1; + return; + } + if (GNUNET_NO == watch) + { + if (NULL == (nodes[index_node].gh = + GNUNET_STATISTICS_get (nodes[index_node].handle, + subsystem, + name, + &continuation_print, + &collector, + &nodes[index_node].index_node))) + cleanup (nodes[index_node].handle, GNUNET_SYSERR); + } + else + { + if ((NULL == subsystem) || (NULL == name)) + { + printf (_ ("No subsystem or name given\n")); + GNUNET_STATISTICS_destroy (nodes[index_node].handle, GNUNET_NO); + nodes[index_node].handle = NULL; + ret = 1; + return; + } + if (GNUNET_OK != GNUNET_STATISTICS_watch (nodes[index_node].handle, + subsystem, + name, + &printer_watch, + &nodes[index_node].index_node)) + { + fprintf (stderr, _ ("Failed to initialize watch routine\n")); + nodes[index_node].shutdown_task = + GNUNET_SCHEDULER_add_now (&clean_node, &nodes[index_node].index_node); + return; + } + } + nodes[index_node].shutdown_task = + GNUNET_SCHEDULER_add_shutdown (&clean_node, &nodes[index_node].index_node); +} + + +/** + * @brief Iter over content of a node's directory to check for existence of a + * config file. + * + * Implements #GNUNET_FileNameCallback + * + * @param cls pointer to indicate success + * @param filename filename inside the directory of the potential node + * + * @return to continue iteration or not to + */ +static int +iter_check_config (void *cls, + const char *filename) +{ + if (0 == strncmp (GNUNET_STRINGS_get_short_name (filename), "config", 6)) + { + /* Found the config - stop iteration successfully */ + GNUNET_array_grow (nodes, num_nodes, num_nodes + 1); + nodes[num_nodes - 1].conf = GNUNET_CONFIGURATION_create (); + nodes[num_nodes - 1].index_node = num_nodes - 1; + if (GNUNET_OK != + GNUNET_CONFIGURATION_load (nodes[num_nodes - 1].conf, filename)) + { + fprintf (stderr, "Failed loading config `%s'\n", filename); + return GNUNET_SYSERR; + } + return GNUNET_NO; + } + else + { + /* Continue iteration */ + return GNUNET_OK; + } +} + + +/** + * @brief Iterates over filenames in testbed directory. + * + * Implements #GNUNET_FileNameCallback + * + * Checks if the file is a directory for a testbed node + * and counts the nodes. + * + * @param cls counter of nodes + * @param filename full path of the file in testbed + * @return status whether to continue iteration + */ +static int +iter_testbed_path (void *cls, + const char *filename) +{ + unsigned index_node; + + GNUNET_assert (NULL != filename); + if (1 == sscanf (GNUNET_STRINGS_get_short_name (filename), + "%u", + &index_node)) + { + if (-1 == GNUNET_DISK_directory_scan (filename, + iter_check_config, + NULL)) + { + /* This is probably no directory for a testbed node + * Go on with iteration */ + return GNUNET_OK; + } + return GNUNET_OK; + } + return GNUNET_OK; +} + + +/** + * @brief Count the number of nodes running in the testbed + * + * @param path_testbed path to the testbed data + * + * @return number of running nodes + */ +static int +discover_testbed_nodes (const char *path_testbed) +{ + int num_dir_entries; + + num_dir_entries = + GNUNET_DISK_directory_scan (path_testbed, + &iter_testbed_path, + NULL); + if (-1 == num_dir_entries) + { + fprintf (stderr, + "Failure during scanning directory `%s'\n", + path_testbed); + return -1; + } + return 0; +} + + +/** + * 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, + const char *cfgfile, + const struct GNUNET_CONFIGURATION_Handle *cfg) +{ + struct GNUNET_CONFIGURATION_Handle *c; + + c = (struct GNUNET_CONFIGURATION_Handle *) cfg; + set_value = GNUNET_NO; + if (NULL == csv_separator) + csv_separator = ""; + if (NULL != args[0]) + { + if (1 != sscanf (args[0], "%llu", &set_val)) + { + fprintf (stderr, _ ("Invalid argument `%s'\n"), args[0]); + ret = 1; + return; + } + set_value = GNUNET_YES; + } + if (NULL != remote_host) + { + if (0 == remote_port) + { + if (GNUNET_SYSERR == + GNUNET_CONFIGURATION_get_value_number (cfg, + "statistics", + "PORT", + &remote_port)) + { + fprintf (stderr, + _ ("A port is required to connect to host `%s'\n"), + remote_host); + return; + } + } + else if (65535 <= remote_port) + { + fprintf (stderr, + _ ( + "A port has to be between 1 and 65535 to connect to host `%s'\n"), + remote_host); + return; + } + + /* Manipulate configuration */ + GNUNET_CONFIGURATION_set_value_string (c, + "statistics", + "UNIXPATH", + ""); + GNUNET_CONFIGURATION_set_value_string (c, + "statistics", + "HOSTNAME", + remote_host); + GNUNET_CONFIGURATION_set_value_number (c, + "statistics", + "PORT", + remote_port); + } + if (NULL == path_testbed) + { + values = GNUNET_CONTAINER_multihashmap_create (1, GNUNET_NO); + GNUNET_array_grow (nodes, num_nodes, 1); + nodes[0].index_node = 0; + nodes[0].conf = c; + GNUNET_SCHEDULER_add_now (&main_task, &nodes[0].index_node); + } + else + { + if (GNUNET_YES == watch) + { + printf ( + _ ("Not able to watch testbed nodes (yet - feel free to implement)\n")); + ret = 1; + return; + } + values = GNUNET_CONTAINER_multihashmap_create (4, GNUNET_NO); + if (-1 == discover_testbed_nodes (path_testbed)) + { + return; + } + /* For each config/node collect statistics */ + for (unsigned i = 0; i < num_nodes; i++) + { + GNUNET_SCHEDULER_add_now (&main_task, &nodes[i].index_node); + } + } +} + + +/** + * The main function to obtain statistics in GNUnet. + * + * @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 GNUNET_GETOPT_CommandLineOption options[] = { + GNUNET_GETOPT_option_string ('n', + "name", + "NAME", + gettext_noop ( + "limit output to statistics for the given NAME"), + &name), + GNUNET_GETOPT_option_flag ('p', + "persistent", + gettext_noop ( + "make the value being set persistent"), + &persistent), + GNUNET_GETOPT_option_string ('s', + "subsystem", + "SUBSYSTEM", + gettext_noop ( + "limit output to the given SUBSYSTEM"), + &subsystem), + GNUNET_GETOPT_option_string ('S', + "csv-separator", + "CSV_SEPARATOR", + gettext_noop ("use as csv separator"), + &csv_separator), + GNUNET_GETOPT_option_filename ('t', + "testbed", + "TESTBED", + gettext_noop ( + "path to the folder containing the testbed data"), + &path_testbed), + GNUNET_GETOPT_option_flag ('q', + "quiet", + gettext_noop ( + "just print the statistics value"), + &quiet), + GNUNET_GETOPT_option_flag ('w', + "watch", + gettext_noop ("watch value continuously"), + &watch), + GNUNET_GETOPT_option_string ('r', + "remote", + "REMOTE", + gettext_noop ("connect to remote host"), + &remote_host), + GNUNET_GETOPT_option_ulong ('o', + "port", + "PORT", + gettext_noop ("port for remote host"), + &remote_port), + GNUNET_GETOPT_OPTION_END + }; + + remote_port = 0; + remote_host = NULL; + if (GNUNET_OK != + GNUNET_STRINGS_get_utf8_args (argc, argv, + &argc, &argv)) + return 2; + + ret = (GNUNET_OK == + GNUNET_PROGRAM_run (argc, + argv, + "gnunet-statistics [options [value]]", + gettext_noop ( + "Print statistics about GNUnet operations."), + options, + &run, + NULL)) + ? ret + : 1; + GNUNET_array_grow (nodes, + num_nodes, + 0); + GNUNET_free (remote_host); + GNUNET_free_nz ((void *) argv); + return ret; +} + + +/* end of gnunet-statistics.c */ diff --git a/src/consensus/Makefile.am b/src/consensus/Makefile.am index 8815b95c9..a578a62cc 100644 --- a/src/consensus/Makefile.am +++ b/src/consensus/Makefile.am @@ -41,7 +41,7 @@ gnunet_service_consensus_LDADD = \ $(top_builddir)/src/lib/util/libgnunetutil.la \ $(top_builddir)/src/service/core/libgnunetcore.la \ $(top_builddir)/src/set/libgnunetset.la \ - $(top_builddir)/src/statistics/libgnunetstatistics.la \ + $(top_builddir)/src/service/statistics/libgnunetstatistics.la \ $(GN_LIBINTL) gnunet_service_evil_consensus_SOURCES = \ @@ -51,7 +51,7 @@ gnunet_service_evil_consensus_LDADD = \ $(top_builddir)/src/lib/util/libgnunetutil.la \ $(top_builddir)/src/service/core/libgnunetcore.la \ $(top_builddir)/src/set/libgnunetset.la \ - $(top_builddir)/src/statistics/libgnunetstatistics.la \ + $(top_builddir)/src/service/statistics/libgnunetstatistics.la \ $(GN_LIBINTL) gnunet_service_evil_consensus_CFLAGS = -DEVIL @@ -92,7 +92,7 @@ test_consensus_api_SOURCES = \ test_consensus_api.c test_consensus_api_LDADD = \ $(top_builddir)/src/lib/util/libgnunetutil.la \ - $(top_builddir)/src/lib/testing/libgnunettesting.la \ + $(top_builddir)/src/service/testing/libgnunettesting.la \ libgnunetconsensus.la noinst_SCRIPTS = \ diff --git a/src/contrib/service/abd/Makefile.am b/src/contrib/service/abd/Makefile.am index 669c00d19..6eff980f4 100644 --- a/src/contrib/service/abd/Makefile.am +++ b/src/contrib/service/abd/Makefile.am @@ -68,7 +68,7 @@ gnunet_service_abd_LDADD = \ $(top_builddir)/src/gns/libgnunetgns.la \ $(top_builddir)/src/namestore/libgnunetnamestore.la \ $(top_builddir)/src/service/identity/libgnunetidentity.la \ - $(top_builddir)/src/statistics/libgnunetstatistics.la \ + $(top_builddir)/src/service/statistics/libgnunetstatistics.la \ $(GN_LIBINTL) diff --git a/src/contrib/service/rps/Makefile.am b/src/contrib/service/rps/Makefile.am index 2ee315d11..34532d67c 100644 --- a/src/contrib/service/rps/Makefile.am +++ b/src/contrib/service/rps/Makefile.am @@ -32,7 +32,7 @@ libgnunetrps_la_SOURCES = \ rps-sampler_client.h rps-sampler_client.c \ rps_api.c rps.h libgnunetrps_la_LIBADD = \ - $(top_builddir)/src/nse/libgnunetnse.la \ + $(top_builddir)/src/service/nse/libgnunetnse.la \ $(top_builddir)/src/lib/util/libgnunetutil.la \ $(GN_LIBINTL) $(XLIB) libgnunetrps_la_LDFLAGS = \ @@ -61,10 +61,10 @@ gnunet_service_rps_SOURCES = \ gnunet_service_rps_LDADD = \ libgnunetrps.la \ $(top_builddir)/src/lib/util/libgnunetutil.la \ - $(top_builddir)/src/peerstore/libgnunetpeerstore.la \ + $(top_builddir)/src/service/peerstore/libgnunetpeerstore.la \ $(top_builddir)/src/cadet/libgnunetcadet.la \ - $(top_builddir)/src/nse/libgnunetnse.la \ - $(top_builddir)/src/statistics/libgnunetstatistics.la \ + $(top_builddir)/src/service/nse/libgnunetnse.la \ + $(top_builddir)/src/service/statistics/libgnunetstatistics.la \ $(top_builddir)/src/service/core/libgnunetcore.la \ $(LIBGCRYPT_LIBS) \ -lm -lgcrypt \ @@ -96,7 +96,7 @@ rps_test_src = \ #ld_rps_test_lib = \ # libgnunetrps.la \ # $(top_builddir)/src/lib/util/libgnunetutil.la \ -# $(top_builddir)/src/statistics/libgnunetstatistics.la \ +# $(top_builddir)/src/service/statistics/libgnunetstatistics.la \ # $(top_builddir)/src/testbed/libgnunettestbed.la \ # -lm @@ -156,7 +156,7 @@ test_service_rps_sampler_elem_LDADD = $(top_builddir)/src/lib/util/libgnunetutil # rps-test_util.h rps-test_util.c \ # gnunet-rps-profiler.c #gnunet_rps_profiler_LDADD = \ -# $(top_builddir)/src/statistics/libgnunetstatistics.la \ +# $(top_builddir)/src/service/statistics/libgnunetstatistics.la \ # libgnunetrps.la \ # $(top_builddir)/src/lib/util/libgnunetutil.la \ # $(top_builddir)/src/testbed/libgnunettestbed.la \ diff --git a/src/conversation/Makefile.am b/src/conversation/Makefile.am index 5aa79a0e0..366c4a199 100644 --- a/src/conversation/Makefile.am +++ b/src/conversation/Makefile.am @@ -225,7 +225,7 @@ test_conversation_api_LDADD = \ $(top_builddir)/src/lib/gnsrecord/libgnunetgnsrecord.la \ $(top_builddir)/src/namestore/libgnunetnamestore.la \ $(top_builddir)/src/service/identity/libgnunetidentity.la \ - $(top_builddir)/src/lib/testing/libgnunettesting.la \ + $(top_builddir)/src/service/testing/libgnunettesting.la \ $(top_builddir)/src/lib/util/libgnunetutil.la test_conversation_api_LDFLAGS = \ -export-dynamic @@ -239,7 +239,7 @@ test_conversation_api_twocalls_LDADD = \ $(top_builddir)/src/lib/gnsrecord/libgnunetgnsrecord.la \ $(top_builddir)/src/namestore/libgnunetnamestore.la \ $(top_builddir)/src/service/identity/libgnunetidentity.la \ - $(top_builddir)/src/lib/testing/libgnunettesting.la \ + $(top_builddir)/src/service/testing/libgnunettesting.la \ $(top_builddir)/src/lib/util/libgnunetutil.la test_conversation_api_twocalls_LDFLAGS = \ -export-dynamic @@ -253,7 +253,7 @@ test_conversation_api_reject_LDADD = \ $(top_builddir)/src/lib/gnsrecord/libgnunetgnsrecord.la \ $(top_builddir)/src/namestore/libgnunetnamestore.la \ $(top_builddir)/src/service/identity/libgnunetidentity.la \ - $(top_builddir)/src/lib/testing/libgnunettesting.la \ + $(top_builddir)/src/service/testing/libgnunettesting.la \ $(top_builddir)/src/lib/util/libgnunetutil.la test_conversation_api_reject_LDFLAGS = \ -export-dynamic diff --git a/src/datacache/Makefile.am b/src/datacache/Makefile.am index 16359945a..9caeac543 100644 --- a/src/datacache/Makefile.am +++ b/src/datacache/Makefile.am @@ -33,7 +33,7 @@ lib_LTLIBRARIES = \ libgnunetdatacache_la_SOURCES = \ datacache.c libgnunetdatacache_la_LIBADD = \ - $(top_builddir)/src/statistics/libgnunetstatistics.la \ + $(top_builddir)/src/service/statistics/libgnunetstatistics.la \ $(top_builddir)/src/lib/util/libgnunetutil.la \ $(GN_LIBINTL) libgnunetdatacache_la_LDFLAGS = \ @@ -55,7 +55,7 @@ noinst_LTLIBRARIES = \ libgnunet_plugin_datacache_sqlite_la_SOURCES = \ plugin_datacache_sqlite.c libgnunet_plugin_datacache_sqlite_la_LIBADD = \ - $(top_builddir)/src/statistics/libgnunetstatistics.la \ + $(top_builddir)/src/service/statistics/libgnunetstatistics.la \ $(top_builddir)/src/lib/sq/libgnunetsq.la \ $(top_builddir)/src/lib/util/libgnunetutil.la $(XLIBS) -lsqlite3 \ $(LTLIBINTL) @@ -65,7 +65,7 @@ libgnunet_plugin_datacache_sqlite_la_LDFLAGS = \ libgnunet_plugin_datacache_heap_la_SOURCES = \ plugin_datacache_heap.c libgnunet_plugin_datacache_heap_la_LIBADD = \ - $(top_builddir)/src/statistics/libgnunetstatistics.la \ + $(top_builddir)/src/service/statistics/libgnunetstatistics.la \ $(top_builddir)/src/lib/util/libgnunetutil.la $(XLIBS) \ $(LTLIBINTL) libgnunet_plugin_datacache_heap_la_LDFLAGS = \ @@ -75,7 +75,7 @@ libgnunet_plugin_datacache_postgres_la_SOURCES = \ plugin_datacache_postgres.c libgnunet_plugin_datacache_postgres_la_LIBADD = \ $(top_builddir)/src/lib/pq/libgnunetpq.la \ - $(top_builddir)/src/statistics/libgnunetstatistics.la \ + $(top_builddir)/src/service/statistics/libgnunetstatistics.la \ $(top_builddir)/src/lib/util/libgnunetutil.la \ $(GN_PLUGIN_LDFLAGS) -lpq libgnunet_plugin_datacache_postgres_la_CPPFLAGS = \ @@ -124,42 +124,42 @@ endif test_datacache_sqlite_SOURCES = \ test_datacache.c test_datacache_sqlite_LDADD = \ - $(top_builddir)/src/lib/testing/libgnunettesting.la \ + $(top_builddir)/src/service/testing/libgnunettesting.la \ libgnunetdatacache.la \ $(top_builddir)/src/lib/util/libgnunetutil.la test_datacache_quota_sqlite_SOURCES = \ test_datacache_quota.c test_datacache_quota_sqlite_LDADD = \ - $(top_builddir)/src/lib/testing/libgnunettesting.la \ + $(top_builddir)/src/service/testing/libgnunettesting.la \ libgnunetdatacache.la \ $(top_builddir)/src/lib/util/libgnunetutil.la test_datacache_heap_SOURCES = \ test_datacache.c test_datacache_heap_LDADD = \ - $(top_builddir)/src/lib/testing/libgnunettesting.la \ + $(top_builddir)/src/service/testing/libgnunettesting.la \ libgnunetdatacache.la \ $(top_builddir)/src/lib/util/libgnunetutil.la test_datacache_quota_heap_SOURCES = \ test_datacache_quota.c test_datacache_quota_heap_LDADD = \ - $(top_builddir)/src/lib/testing/libgnunettesting.la \ + $(top_builddir)/src/service/testing/libgnunettesting.la \ libgnunetdatacache.la \ $(top_builddir)/src/lib/util/libgnunetutil.la test_datacache_postgres_SOURCES = \ test_datacache.c test_datacache_postgres_LDADD = \ - $(top_builddir)/src/lib/testing/libgnunettesting.la \ + $(top_builddir)/src/service/testing/libgnunettesting.la \ libgnunetdatacache.la \ $(top_builddir)/src/lib/util/libgnunetutil.la test_datacache_quota_postgres_SOURCES = \ test_datacache_quota.c test_datacache_quota_postgres_LDADD = \ - $(top_builddir)/src/lib/testing/libgnunettesting.la \ + $(top_builddir)/src/service/testing/libgnunettesting.la \ libgnunetdatacache.la \ $(top_builddir)/src/lib/util/libgnunetutil.la diff --git a/src/datastore/Makefile.am b/src/datastore/Makefile.am index fc7b71bcc..5a49b173d 100644 --- a/src/datastore/Makefile.am +++ b/src/datastore/Makefile.am @@ -28,7 +28,7 @@ lib_LTLIBRARIES = \ libgnunetdatastore_la_SOURCES = \ datastore_api.c datastore.h libgnunetdatastore_la_LIBADD = \ - $(top_builddir)/src/statistics/libgnunetstatistics.la \ + $(top_builddir)/src/service/statistics/libgnunetstatistics.la \ $(top_builddir)/src/lib/util/libgnunetutil.la \ $(GN_LIBINTL) libgnunetdatastore_la_LDFLAGS = \ @@ -44,7 +44,7 @@ libexec_PROGRAMS = \ gnunet_service_datastore_SOURCES = \ gnunet-service-datastore.c gnunet_service_datastore_LDADD = \ - $(top_builddir)/src/statistics/libgnunetstatistics.la \ + $(top_builddir)/src/service/statistics/libgnunetstatistics.la \ $(top_builddir)/src/lib/util/libgnunetutil.la \ $(GN_LIBINTL) @@ -98,7 +98,7 @@ libgnunet_plugin_datastore_sqlite_la_SOURCES = \ plugin_datastore_sqlite.c libgnunet_plugin_datastore_sqlite_la_LIBADD = \ $(top_builddir)/src/lib/sq/libgnunetsq.la \ - $(top_builddir)/src/statistics/libgnunetstatistics.la \ + $(top_builddir)/src/service/statistics/libgnunetstatistics.la \ $(top_builddir)/src/lib/util/libgnunetutil.la $(XLIBS) -lsqlite3 \ $(LTLIBINTL) libgnunet_plugin_datastore_sqlite_la_LDFLAGS = \ @@ -117,7 +117,7 @@ libgnunet_plugin_datastore_heap_la_LDFLAGS = \ libgnunet_plugin_datastore_postgres_la_SOURCES = \ plugin_datastore_postgres.c libgnunet_plugin_datastore_postgres_la_LIBADD = \ - $(top_builddir)/src/statistics/libgnunetstatistics.la \ + $(top_builddir)/src/service/statistics/libgnunetstatistics.la \ $(top_builddir)/src/lib/pq/libgnunetpq.la \ $(top_builddir)/src/lib/util/libgnunetutil.la $(XLIBS) -lpq libgnunet_plugin_datastore_postgres_la_LDFLAGS = \ @@ -151,102 +151,102 @@ endif test_datastore_api_heap_SOURCES = \ test_datastore_api.c test_datastore_api_heap_LDADD = \ - $(top_builddir)/src/lib/testing/libgnunettesting.la \ + $(top_builddir)/src/service/testing/libgnunettesting.la \ libgnunetdatastore.la \ $(top_builddir)/src/lib/util/libgnunetutil.la test_datastore_api_management_heap_SOURCES = \ test_datastore_api_management.c test_datastore_api_management_heap_LDADD = \ - $(top_builddir)/src/lib/testing/libgnunettesting.la \ + $(top_builddir)/src/service/testing/libgnunettesting.la \ libgnunetdatastore.la \ $(top_builddir)/src/lib/util/libgnunetutil.la perf_datastore_api_heap_SOURCES = \ perf_datastore_api.c perf_datastore_api_heap_LDADD = \ - $(top_builddir)/src/lib/testing/libgnunettesting.la \ + $(top_builddir)/src/service/testing/libgnunettesting.la \ libgnunetdatastore.la \ $(top_builddir)/src/lib/util/libgnunetutil.la perf_plugin_datastore_heap_SOURCES = \ perf_plugin_datastore.c perf_plugin_datastore_heap_LDADD = \ - $(top_builddir)/src/lib/testing/libgnunettesting.la \ + $(top_builddir)/src/service/testing/libgnunettesting.la \ $(top_builddir)/src/lib/util/libgnunetutil.la test_plugin_datastore_heap_SOURCES = \ test_plugin_datastore.c test_plugin_datastore_heap_LDADD = \ - $(top_builddir)/src/lib/testing/libgnunettesting.la \ + $(top_builddir)/src/service/testing/libgnunettesting.la \ $(top_builddir)/src/lib/util/libgnunetutil.la test_datastore_api_sqlite_SOURCES = \ test_datastore_api.c test_datastore_api_sqlite_LDADD = \ - $(top_builddir)/src/lib/testing/libgnunettesting.la \ + $(top_builddir)/src/service/testing/libgnunettesting.la \ libgnunetdatastore.la \ $(top_builddir)/src/lib/util/libgnunetutil.la test_datastore_api_management_sqlite_SOURCES = \ test_datastore_api_management.c test_datastore_api_management_sqlite_LDADD = \ - $(top_builddir)/src/lib/testing/libgnunettesting.la \ + $(top_builddir)/src/service/testing/libgnunettesting.la \ libgnunetdatastore.la \ $(top_builddir)/src/lib/util/libgnunetutil.la perf_datastore_api_sqlite_SOURCES = \ perf_datastore_api.c perf_datastore_api_sqlite_LDADD = \ - $(top_builddir)/src/lib/testing/libgnunettesting.la \ + $(top_builddir)/src/service/testing/libgnunettesting.la \ libgnunetdatastore.la \ $(top_builddir)/src/lib/util/libgnunetutil.la perf_plugin_datastore_sqlite_SOURCES = \ perf_plugin_datastore.c perf_plugin_datastore_sqlite_LDADD = \ - $(top_builddir)/src/lib/testing/libgnunettesting.la \ + $(top_builddir)/src/service/testing/libgnunettesting.la \ $(top_builddir)/src/lib/util/libgnunetutil.la test_plugin_datastore_sqlite_SOURCES = \ test_plugin_datastore.c test_plugin_datastore_sqlite_LDADD = \ - $(top_builddir)/src/lib/testing/libgnunettesting.la \ + $(top_builddir)/src/service/testing/libgnunettesting.la \ $(top_builddir)/src/lib/util/libgnunetutil.la test_datastore_api_postgres_SOURCES = \ test_datastore_api.c test_datastore_api_postgres_LDADD = \ - $(top_builddir)/src/lib/testing/libgnunettesting.la \ + $(top_builddir)/src/service/testing/libgnunettesting.la \ libgnunetdatastore.la \ $(top_builddir)/src/lib/util/libgnunetutil.la test_datastore_api_management_postgres_SOURCES = \ test_datastore_api_management.c test_datastore_api_management_postgres_LDADD = \ - $(top_builddir)/src/lib/testing/libgnunettesting.la \ + $(top_builddir)/src/service/testing/libgnunettesting.la \ libgnunetdatastore.la \ $(top_builddir)/src/lib/util/libgnunetutil.la perf_datastore_api_postgres_SOURCES = \ perf_datastore_api.c perf_datastore_api_postgres_LDADD = \ - $(top_builddir)/src/lib/testing/libgnunettesting.la \ + $(top_builddir)/src/service/testing/libgnunettesting.la \ libgnunetdatastore.la \ $(top_builddir)/src/lib/util/libgnunetutil.la test_plugin_datastore_postgres_SOURCES = \ test_plugin_datastore.c test_plugin_datastore_postgres_LDADD = \ - $(top_builddir)/src/lib/testing/libgnunettesting.la \ + $(top_builddir)/src/service/testing/libgnunettesting.la \ $(top_builddir)/src/lib/util/libgnunetutil.la perf_plugin_datastore_postgres_SOURCES = \ perf_plugin_datastore.c perf_plugin_datastore_postgres_LDADD = \ - $(top_builddir)/src/lib/testing/libgnunettesting.la \ + $(top_builddir)/src/service/testing/libgnunettesting.la \ $(top_builddir)/src/lib/util/libgnunetutil.la diff --git a/src/dht/Makefile.am b/src/dht/Makefile.am index 6185d9d2c..8449d4825 100644 --- a/src/dht/Makefile.am +++ b/src/dht/Makefile.am @@ -60,10 +60,10 @@ gnunet_service_dht_SOURCES = \ gnunet-service-dht_routing.c gnunet-service-dht_routing.h gnunet_service_dht_LDADD = \ libgnunetdht.la \ - $(top_builddir)/src/statistics/libgnunetstatistics.la \ + $(top_builddir)/src/service/statistics/libgnunetstatistics.la \ $(top_builddir)/src/service/core/libgnunetcore.la \ - $(top_builddir)/src/nse/libgnunetnse.la \ - $(top_builddir)/src/peerstore/libgnunetpeerstore.la \ + $(top_builddir)/src/service/nse/libgnunetnse.la \ + $(top_builddir)/src/service/peerstore/libgnunetpeerstore.la \ $(top_builddir)/src/lib/hello/libgnunethello.la \ $(top_builddir)/src/lib/block/libgnunetblock.la \ $(top_builddir)/src/lib/block/libgnunetblockgroup.la \ @@ -113,7 +113,7 @@ test_dht_api_SOURCES = \ test_dht_api.c test_dht_api_LDADD = \ $(top_builddir)/src/lib/util/libgnunetutil.la \ - $(top_builddir)/src/lib/testing/libgnunettesting.la \ + $(top_builddir)/src/service/testing/libgnunettesting.la \ $(top_builddir)/src/lib/hello/libgnunethello.la \ libgnunetdht.la diff --git a/src/dns/Makefile.am b/src/dns/Makefile.am index 6c7462d9a..38ee78c3a 100644 --- a/src/dns/Makefile.am +++ b/src/dns/Makefile.am @@ -64,7 +64,7 @@ gnunet_dns_redirector_LDADD = \ gnunet_service_dns_SOURCES = \ gnunet-service-dns.c gnunet_service_dns_LDADD = \ - $(top_builddir)/src/statistics/libgnunetstatistics.la \ + $(top_builddir)/src/service/statistics/libgnunetstatistics.la \ $(top_builddir)/src/lib/util/libgnunetutil.la \ $(GN_LIBINTL) diff --git a/src/exit/Makefile.am b/src/exit/Makefile.am index 9e36a5064..c2dba0927 100644 --- a/src/exit/Makefile.am +++ b/src/exit/Makefile.am @@ -31,7 +31,7 @@ gnunet_daemon_exit_SOURCES = \ gnunet-daemon-exit.c exit.h gnunet_daemon_exit_LDADD = \ $(top_builddir)/src/dht/libgnunetdht.la \ - $(top_builddir)/src/statistics/libgnunetstatistics.la \ + $(top_builddir)/src/service/statistics/libgnunetstatistics.la \ $(top_builddir)/src/lib/util/libgnunetutil.la \ $(top_builddir)/src/cadet/libgnunetcadet.la \ $(top_builddir)/src/regex/libgnunetregex.la \ diff --git a/src/fs/Makefile.am b/src/fs/Makefile.am index b6083ea06..8616c0474 100644 --- a/src/fs/Makefile.am +++ b/src/fs/Makefile.am @@ -43,7 +43,7 @@ libgnunetfs_la_SOURCES = \ libgnunetfs_la_LIBADD = \ $(top_builddir)/src/datastore/libgnunetdatastore.la \ - $(top_builddir)/src/statistics/libgnunetstatistics.la \ + $(top_builddir)/src/service/statistics/libgnunetstatistics.la \ $(top_builddir)/src/lib/util/libgnunetutil.la \ $(GN_LIBINTL) $(XLIB) $(LIBGCRYPT_LIBS) -lunistring @@ -157,7 +157,7 @@ gnunet_daemon_fsprofiler_SOURCES = \ gnunet-daemon-fsprofiler.c gnunet_daemon_fsprofiler_LDADD = \ libgnunetfs.la \ - $(top_builddir)/src/statistics/libgnunetstatistics.la \ + $(top_builddir)/src/service/statistics/libgnunetstatistics.la \ $(top_builddir)/src/lib/util/libgnunetutil.la \ $(GN_LIBINTL) @@ -176,11 +176,11 @@ gnunet_service_fs_LDADD = \ $(top_builddir)/src/dht/libgnunetdht.la \ $(top_builddir)/src/lib/block/libgnunetblock.la \ $(top_builddir)/src/datastore/libgnunetdatastore.la \ - $(top_builddir)/src/statistics/libgnunetstatistics.la \ + $(top_builddir)/src/service/statistics/libgnunetstatistics.la \ $(top_builddir)/src/cadet/libgnunetcadet.la \ $(top_builddir)/src/service/core/libgnunetcore.la \ $(top_builddir)/src/lib/util/libgnunetutil.la \ - $(top_builddir)/src/peerstore/libgnunetpeerstore.la \ + $(top_builddir)/src/service/peerstore/libgnunetpeerstore.la \ $(GN_LIBINTL) -lm gnunet_unindex_SOURCES = \ @@ -278,28 +278,28 @@ endif test_fs_download_SOURCES = \ test_fs_download.c test_fs_download_LDADD = \ - $(top_builddir)/src/lib/testing/libgnunettesting.la \ + $(top_builddir)/src/service/testing/libgnunettesting.la \ libgnunetfs.la \ $(top_builddir)/src/lib/util/libgnunetutil.la test_fs_download_indexed_SOURCES = \ test_fs_download.c test_fs_download_indexed_LDADD = \ - $(top_builddir)/src/lib/testing/libgnunettesting.la \ + $(top_builddir)/src/service/testing/libgnunettesting.la \ libgnunetfs.la \ $(top_builddir)/src/lib/util/libgnunetutil.la test_fs_download_cadet_SOURCES = \ test_fs_download.c test_fs_download_cadet_LDADD = \ - $(top_builddir)/src/lib/testing/libgnunettesting.la \ + $(top_builddir)/src/service/testing/libgnunettesting.la \ libgnunetfs.la \ $(top_builddir)/src/lib/util/libgnunetutil.la test_fs_download_persistence_SOURCES = \ test_fs_download_persistence.c test_fs_download_persistence_LDADD = \ - $(top_builddir)/src/lib/testing/libgnunettesting.la \ + $(top_builddir)/src/service/testing/libgnunettesting.la \ libgnunetfs.la \ $(top_builddir)/src/lib/util/libgnunetutil.la @@ -324,84 +324,84 @@ test_fs_getopt_LDADD = \ test_fs_list_indexed_SOURCES = \ test_fs_list_indexed.c test_fs_list_indexed_LDADD = \ - $(top_builddir)/src/lib/testing/libgnunettesting.la \ + $(top_builddir)/src/service/testing/libgnunettesting.la \ libgnunetfs.la \ $(top_builddir)/src/lib/util/libgnunetutil.la test_fs_namespace_SOURCES = \ test_fs_namespace.c test_fs_namespace_LDADD = \ - $(top_builddir)/src/lib/testing/libgnunettesting.la \ + $(top_builddir)/src/service/testing/libgnunettesting.la \ libgnunetfs.la \ $(top_builddir)/src/lib/util/libgnunetutil.la test_fs_namespace_list_updateable_SOURCES = \ test_fs_namespace_list_updateable.c test_fs_namespace_list_updateable_LDADD = \ - $(top_builddir)/src/lib/testing/libgnunettesting.la \ + $(top_builddir)/src/service/testing/libgnunettesting.la \ libgnunetfs.la \ $(top_builddir)/src/lib/util/libgnunetutil.la test_fs_publish_SOURCES = \ test_fs_publish.c test_fs_publish_LDADD = \ - $(top_builddir)/src/lib/testing/libgnunettesting.la \ + $(top_builddir)/src/service/testing/libgnunettesting.la \ libgnunetfs.la \ $(top_builddir)/src/lib/util/libgnunetutil.la test_fs_publish_persistence_SOURCES = \ test_fs_publish_persistence.c test_fs_publish_persistence_LDADD = \ - $(top_builddir)/src/lib/testing/libgnunettesting.la \ + $(top_builddir)/src/service/testing/libgnunettesting.la \ libgnunetfs.la \ $(top_builddir)/src/lib/util/libgnunetutil.la test_fs_search_SOURCES = \ test_fs_search.c test_fs_search_LDADD = \ - $(top_builddir)/src/lib/testing/libgnunettesting.la \ + $(top_builddir)/src/service/testing/libgnunettesting.la \ libgnunetfs.la \ $(top_builddir)/src/lib/util/libgnunetutil.la test_fs_search_with_and_SOURCES = \ test_fs_search_with_and.c test_fs_search_with_and_LDADD = \ - $(top_builddir)/src/lib/testing/libgnunettesting.la \ + $(top_builddir)/src/service/testing/libgnunettesting.la \ libgnunetfs.la \ $(top_builddir)/src/lib/util/libgnunetutil.la test_fs_search_probes_SOURCES = \ test_fs_search_probes.c test_fs_search_probes_LDADD = \ - $(top_builddir)/src/lib/testing/libgnunettesting.la \ + $(top_builddir)/src/service/testing/libgnunettesting.la \ libgnunetfs.la \ $(top_builddir)/src/lib/util/libgnunetutil.la test_fs_search_persistence_SOURCES = \ test_fs_search_persistence.c test_fs_search_persistence_LDADD = \ - $(top_builddir)/src/lib/testing/libgnunettesting.la \ + $(top_builddir)/src/service/testing/libgnunettesting.la \ libgnunetfs.la \ $(top_builddir)/src/lib/util/libgnunetutil.la test_fs_start_stop_SOURCES = \ test_fs_start_stop.c test_fs_start_stop_LDADD = \ - $(top_builddir)/src/lib/testing/libgnunettesting.la \ + $(top_builddir)/src/service/testing/libgnunettesting.la \ libgnunetfs.la \ $(top_builddir)/src/lib/util/libgnunetutil.la test_fs_unindex_SOURCES = \ test_fs_unindex.c test_fs_unindex_LDADD = \ - $(top_builddir)/src/lib/testing/libgnunettesting.la \ + $(top_builddir)/src/service/testing/libgnunettesting.la \ libgnunetfs.la \ $(top_builddir)/src/lib/util/libgnunetutil.la test_fs_unindex_persistence_SOURCES = \ test_fs_unindex_persistence.c test_fs_unindex_persistence_LDADD = \ - $(top_builddir)/src/lib/testing/libgnunettesting.la \ + $(top_builddir)/src/service/testing/libgnunettesting.la \ libgnunetfs.la \ $(top_builddir)/src/lib/util/libgnunetutil.la @@ -455,7 +455,7 @@ test_fs_uri_LDADD = \ # perf_gnunet_service_fs_p2p.c #perf_gnunet_service_fs_p2p_LDADD = \ # libgnunetfstest.a \ -# $(top_builddir)/src/statistics/libgnunetstatistics.la \ +# $(top_builddir)/src/service/statistics/libgnunetstatistics.la \ # $(top_builddir)/src/testbed/libgnunettestbed.la \ # libgnunetfs.la \ # $(top_builddir)/src/lib/util/libgnunetutil.la @@ -464,7 +464,7 @@ test_fs_uri_LDADD = \ # perf_gnunet_service_fs_p2p.c #perf_gnunet_service_fs_p2p_index_LDADD = \ # libgnunetfstest.a \ -# $(top_builddir)/src/statistics/libgnunetstatistics.la \ +# $(top_builddir)/src/service/statistics/libgnunetstatistics.la \ # $(top_builddir)/src/testbed/libgnunettestbed.la \ # libgnunetfs.la \ # $(top_builddir)/src/lib/util/libgnunetutil.la @@ -473,7 +473,7 @@ test_fs_uri_LDADD = \ # perf_gnunet_service_fs_p2p.c #perf_gnunet_service_fs_p2p_dht_LDADD = \ # libgnunetfstest.a \ -# $(top_builddir)/src/statistics/libgnunetstatistics.la \ +# $(top_builddir)/src/service/statistics/libgnunetstatistics.la \ # $(top_builddir)/src/testbed/libgnunettestbed.la \ # libgnunetfs.la \ # $(top_builddir)/src/lib/util/libgnunetutil.la @@ -482,7 +482,7 @@ test_fs_uri_LDADD = \ # perf_gnunet_service_fs_p2p_respect.c #perf_gnunet_service_fs_p2p_respect_LDADD = \ # libgnunetfstest.a \ -# $(top_builddir)/src/statistics/libgnunetstatistics.la \ +# $(top_builddir)/src/service/statistics/libgnunetstatistics.la \ # $(top_builddir)/src/testbed/libgnunettestbed.la \ # libgnunetfs.la \ # $(top_builddir)/src/lib/util/libgnunetutil.la diff --git a/src/gns/Makefile.am b/src/gns/Makefile.am index 191103088..d739f8131 100644 --- a/src/gns/Makefile.am +++ b/src/gns/Makefile.am @@ -201,7 +201,7 @@ gnunet_service_gns_LDADD = \ $(top_builddir)/src/lib/gnsrecord/libgnunetgnsrecord.la \ $(top_builddir)/src/service/identity/libgnunetidentity.la \ $(top_builddir)/src/revocation/libgnunetrevocation.la \ - $(top_builddir)/src/statistics/libgnunetstatistics.la \ + $(top_builddir)/src/service/statistics/libgnunetstatistics.la \ $(top_builddir)/src/lib/util/libgnunetutil.la \ $(top_builddir)/src/dns/libgnunetdns.la \ $(top_builddir)/src/dht/libgnunetdht.la \ diff --git a/src/hostlist/Makefile.am b/src/hostlist/Makefile.am index 9cf72142a..fc9952aa6 100644 --- a/src/hostlist/Makefile.am +++ b/src/hostlist/Makefile.am @@ -28,8 +28,8 @@ gnunet_daemon_hostlist_SOURCES = \ gnunet_daemon_hostlist_LDADD = \ $(top_builddir)/src/service/core/libgnunetcore.la \ $(top_builddir)/src/lib/hello/libgnunethello.la \ - $(top_builddir)/src/peerstore/libgnunetpeerstore.la \ - $(top_builddir)/src/statistics/libgnunetstatistics.la \ + $(top_builddir)/src/service/peerstore/libgnunetpeerstore.la \ + $(top_builddir)/src/service/statistics/libgnunetstatistics.la \ $(top_builddir)/src/lib/util/libgnunetutil.la \ $(GN_LIBMHD) \ @LIBCURL@ \ @@ -54,21 +54,21 @@ endif #test_gnunet_daemon_hostlist_SOURCES = \ # test_gnunet_daemon_hostlist.c #test_gnunet_daemon_hostlist_LDADD = \ -# $(top_builddir)/src/transport/libgnunettransport.la \ +# $(top_builddir)/src/service/transport/libgnunettransport.la \ # $(top_builddir)/src/lib/util/libgnunetutil.la # #test_gnunet_daemon_hostlist_reconnect_SOURCES = \ # test_gnunet_daemon_hostlist_reconnect.c #test_gnunet_daemon_hostlist_reconnect_LDADD = \ -# $(top_builddir)/src/transport/libgnunettransport.la \ +# $(top_builddir)/src/service/transport/libgnunettransport.la \ # $(top_builddir)/src/lib/util/libgnunetutil.la # #test_gnunet_daemon_hostlist_learning_SOURCES = \ # test_gnunet_daemon_hostlist_learning.c #test_gnunet_daemon_hostlist_learning_LDADD = \ -# $(top_builddir)/src/transport/libgnunettransport.la \ +# $(top_builddir)/src/service/transport/libgnunettransport.la \ # $(top_builddir)/src/service/core/libgnunetcore.la \ -# $(top_builddir)/src/statistics/libgnunetstatistics.la \ +# $(top_builddir)/src/service/statistics/libgnunetstatistics.la \ # $(top_builddir)/src/lib/util/libgnunetutil.la EXTRA_DIST = \ diff --git a/src/lib/gnsrecord/Makefile.am b/src/lib/gnsrecord/Makefile.am index 27d14a972..57b004847 100644 --- a/src/lib/gnsrecord/Makefile.am +++ b/src/lib/gnsrecord/Makefile.am @@ -74,7 +74,7 @@ EXTRA_DIST = \ test_gnsrecord_lsd0001testvectors_SOURCES = \ test_gnsrecord_testvectors.c test_gnsrecord_lsd0001testvectors_LDADD = \ - $(top_builddir)/src/lib/testing/libgnunettesting.la \ + $(top_builddir)/src/service/testing/libgnunettesting.la \ libgnunetgnsrecord.la \ $(top_builddir)/src/lib/util/libgnunetutil.la @@ -82,14 +82,14 @@ test_gnsrecord_lsd0001testvectors_LDADD = \ test_gnsrecord_serialization_SOURCES = \ test_gnsrecord_serialization.c test_gnsrecord_serialization_LDADD = \ - $(top_builddir)/src/lib/testing/libgnunettesting.la \ + $(top_builddir)/src/service/testing/libgnunettesting.la \ libgnunetgnsrecord.la \ $(top_builddir)/src/lib/util/libgnunetutil.la test_gnsrecord_block_expiration_SOURCES = \ test_gnsrecord_block_expiration.c test_gnsrecord_block_expiration_LDADD = \ - $(top_builddir)/src/lib/testing/libgnunettesting.la \ + $(top_builddir)/src/service/testing/libgnunettesting.la \ libgnunetgnsrecord.la \ $(top_builddir)/src/lib/util/libgnunetutil.la @@ -97,7 +97,7 @@ test_gnsrecord_block_expiration_LDADD = \ test_gnsrecord_crypto_SOURCES = \ test_gnsrecord_crypto.c test_gnsrecord_crypto_LDADD = \ - $(top_builddir)/src/lib/testing/libgnunettesting.la \ + $(top_builddir)/src/service/testing/libgnunettesting.la \ libgnunetgnsrecord.la \ $(top_builddir)/src/lib/util/libgnunetutil.la @@ -105,6 +105,6 @@ test_gnsrecord_crypto_LDADD = \ perf_gnsrecord_crypto_SOURCES = \ perf_gnsrecord_crypto.c perf_gnsrecord_crypto_LDADD = \ - $(top_builddir)/src/lib/testing/libgnunettesting.la \ + $(top_builddir)/src/service/testing/libgnunettesting.la \ libgnunetgnsrecord.la \ $(top_builddir)/src/lib/util/libgnunetutil.la diff --git a/src/messenger/Makefile.am b/src/messenger/Makefile.am index d464261df..60b2314ac 100644 --- a/src/messenger/Makefile.am +++ b/src/messenger/Makefile.am @@ -109,14 +109,14 @@ test_messenger_api_SOURCES = \ test_messenger.c test_messenger_api_LDADD = \ libgnunetmessenger.la \ - $(top_builddir)/src/lib/testing/libgnunettesting.la \ + $(top_builddir)/src/service/testing/libgnunettesting.la \ $(top_builddir)/src/lib/util/libgnunetutil.la test_messenger_anonymous_SOURCES = \ test_messenger_anonymous.c test_messenger_anonymous_LDADD = \ libgnunetmessenger.la \ - $(top_builddir)/src/lib/testing/libgnunettesting.la \ + $(top_builddir)/src/service/testing/libgnunettesting.la \ $(top_builddir)/src/lib/util/libgnunetutil.la EXTRA_DIST = \ diff --git a/src/namecache/Makefile.am b/src/namecache/Makefile.am index 9be5a4fce..36e70813a 100644 --- a/src/namecache/Makefile.am +++ b/src/namecache/Makefile.am @@ -93,7 +93,7 @@ gnunet_service_namecache_SOURCES = \ gnunet_service_namecache_LDADD = \ $(top_builddir)/src/lib/gnsrecord/libgnunetgnsrecord.la \ $(top_builddir)/src/service/identity/libgnunetidentity.la \ - $(top_builddir)/src/statistics/libgnunetstatistics.la \ + $(top_builddir)/src/service/statistics/libgnunetstatistics.la \ $(top_builddir)/src/lib/util/libgnunetutil.la \ libgnunetnamecache.la \ $(GN_LIBINTL) @@ -108,7 +108,7 @@ libgnunet_plugin_namecache_flat_la_SOURCES = \ plugin_namecache_flat.c libgnunet_plugin_namecache_flat_la_LIBADD = \ libgnunetnamecache.la \ - $(top_builddir)/src/statistics/libgnunetstatistics.la \ + $(top_builddir)/src/service/statistics/libgnunetstatistics.la \ $(top_builddir)/src/lib/gnsrecord/libgnunetgnsrecord.la \ $(top_builddir)/src/service/identity/libgnunetidentity.la \ $(top_builddir)/src/lib/util/libgnunetutil.la $(XLIBS) \ @@ -121,7 +121,7 @@ libgnunet_plugin_namecache_sqlite_la_SOURCES = \ libgnunet_plugin_namecache_sqlite_la_LIBADD = \ libgnunetnamecache.la \ $(top_builddir)/src/lib/sq/libgnunetsq.la \ - $(top_builddir)/src/statistics/libgnunetstatistics.la \ + $(top_builddir)/src/service/statistics/libgnunetstatistics.la \ $(top_builddir)/src/lib/gnsrecord/libgnunetgnsrecord.la \ $(top_builddir)/src/service/identity/libgnunetidentity.la \ $(top_builddir)/src/lib/util/libgnunetutil.la $(XLIBS) -lsqlite3 \ @@ -135,7 +135,7 @@ libgnunet_plugin_namecache_postgres_la_SOURCES = \ libgnunet_plugin_namecache_postgres_la_LIBADD = \ libgnunetnamecache.la \ $(top_builddir)/src/lib/pq/libgnunetpq.la \ - $(top_builddir)/src/statistics/libgnunetstatistics.la \ + $(top_builddir)/src/service/statistics/libgnunetstatistics.la \ $(top_builddir)/src/lib/gnsrecord/libgnunetgnsrecord.la \ $(top_builddir)/src/service/identity/libgnunetidentity.la \ $(top_builddir)/src/lib/util/libgnunetutil.la $(XLIBS) -lpq \ @@ -149,26 +149,26 @@ test_namecache_api_cache_block_LDADD = \ $(top_builddir)/src/lib/gnsrecord/libgnunetgnsrecord.la \ $(top_builddir)/src/service/identity/libgnunetidentity.la \ libgnunetnamecache.la \ - $(top_builddir)/src/lib/testing/libgnunettesting.la \ + $(top_builddir)/src/service/testing/libgnunettesting.la \ $(top_builddir)/src/lib/util/libgnunetutil.la test_plugin_namecache_flat_SOURCES = \ test_plugin_namecache.c test_plugin_namecache_flat_LDADD = \ - $(top_builddir)/src/lib/testing/libgnunettesting.la \ + $(top_builddir)/src/service/testing/libgnunettesting.la \ $(top_builddir)/src/lib/util/libgnunetutil.la test_plugin_namecache_sqlite_SOURCES = \ test_plugin_namecache.c test_plugin_namecache_sqlite_LDADD = \ - $(top_builddir)/src/lib/testing/libgnunettesting.la \ + $(top_builddir)/src/service/testing/libgnunettesting.la \ $(top_builddir)/src/lib/util/libgnunetutil.la test_plugin_namecache_postgres_SOURCES = \ test_plugin_namecache.c test_plugin_namecache_postgres_LDADD = \ - $(top_builddir)/src/lib/testing/libgnunettesting.la \ + $(top_builddir)/src/service/testing/libgnunettesting.la \ $(top_builddir)/src/lib/util/libgnunetutil.la EXTRA_DIST = \ diff --git a/src/namestore/Makefile.am b/src/namestore/Makefile.am index 2bdc25d56..3dbec4a25 100644 --- a/src/namestore/Makefile.am +++ b/src/namestore/Makefile.am @@ -127,7 +127,7 @@ libgnunetnamestore_la_SOURCES = \ libgnunetnamestore_la_LIBADD = \ $(top_builddir)/src/lib/gnsrecord/libgnunetgnsrecord.la \ $(top_builddir)/src/service/identity/libgnunetidentity.la \ - $(top_builddir)/src/statistics/libgnunetstatistics.la \ + $(top_builddir)/src/service/statistics/libgnunetstatistics.la \ $(top_builddir)/src/lib/util/libgnunetutil.la \ $(GN_LIBINTL) libgnunetnamestore_la_LDFLAGS = \ @@ -147,7 +147,7 @@ gnunet_zoneimport_SOURCES = \ gnunet-zoneimport.c gnunet_zoneimport_LDADD = \ libgnunetnamestore.la \ - $(top_builddir)/src/statistics/libgnunetstatistics.la \ + $(top_builddir)/src/service/statistics/libgnunetstatistics.la \ $(top_builddir)/src/service/identity/libgnunetidentity.la \ $(top_builddir)/src/lib/gnsrecord/libgnunetgnsrecord.la \ $(top_builddir)/src/lib/util/libgnunetutil.la \ @@ -189,7 +189,7 @@ gnunet_service_namestore_LDADD = \ $(top_builddir)/src/namecache/libgnunetnamecache.la \ $(top_builddir)/src/lib/gnsrecord/libgnunetgnsrecord.la \ $(top_builddir)/src/service/identity/libgnunetidentity.la \ - $(top_builddir)/src/statistics/libgnunetstatistics.la \ + $(top_builddir)/src/service/statistics/libgnunetstatistics.la \ $(top_builddir)/src/lib/util/libgnunetutil.la \ libgnunetnamestore.la \ $(GN_LIBINTL) @@ -202,7 +202,7 @@ libgnunet_plugin_namestore_sqlite_la_LIBADD = \ $(top_builddir)/src/lib/gnsrecord/libgnunetgnsrecord.la \ $(top_builddir)/src/service/identity/libgnunetidentity.la \ $(top_builddir)/src/lib/sq/libgnunetsq.la \ - $(top_builddir)/src/statistics/libgnunetstatistics.la \ + $(top_builddir)/src/service/statistics/libgnunetstatistics.la \ $(top_builddir)/src/lib/util/libgnunetutil.la $(XLIBS) -lsqlite3 \ $(LTLIBINTL) libgnunet_plugin_namestore_sqlite_la_LDFLAGS = \ @@ -214,7 +214,7 @@ libgnunet_plugin_namestore_postgres_la_LIBADD = \ $(top_builddir)/src/lib/gnsrecord/libgnunetgnsrecord.la \ $(top_builddir)/src/service/identity/libgnunetidentity.la \ $(top_builddir)/src/lib/pq/libgnunetpq.la \ - $(top_builddir)/src/statistics/libgnunetstatistics.la \ + $(top_builddir)/src/service/statistics/libgnunetstatistics.la \ $(top_builddir)/src/lib/util/libgnunetutil.la $(XLIBS) -lpq \ $(LTLIBINTL) libgnunet_plugin_namestore_postgres_la_LDFLAGS = \ @@ -223,7 +223,7 @@ libgnunet_plugin_namestore_postgres_la_LDFLAGS = \ test_namestore_api_store_sqlite_SOURCES = \ test_namestore_api_store.c test_namestore_api_store_sqlite_LDADD = \ - $(top_builddir)/src/lib/testing/libgnunettesting.la \ + $(top_builddir)/src/service/testing/libgnunettesting.la \ $(top_builddir)/src/lib/util/libgnunetutil.la \ $(top_builddir)/src/lib/gnsrecord/libgnunetgnsrecord.la \ $(top_builddir)/src/service/identity/libgnunetidentity.la \ @@ -232,7 +232,7 @@ test_namestore_api_store_sqlite_LDADD = \ test_namestore_api_store_postgres_SOURCES = \ test_namestore_api_store.c test_namestore_api_store_postgres_LDADD = \ - $(top_builddir)/src/lib/testing/libgnunettesting.la \ + $(top_builddir)/src/service/testing/libgnunettesting.la \ $(top_builddir)/src/lib/util/libgnunetutil.la \ $(top_builddir)/src/lib/gnsrecord/libgnunetgnsrecord.la \ $(top_builddir)/src/service/identity/libgnunetidentity.la \ @@ -241,7 +241,7 @@ test_namestore_api_store_postgres_LDADD = \ test_namestore_api_store_update_sqlite_SOURCES = \ test_namestore_api_store_update.c test_namestore_api_store_update_sqlite_LDADD = \ - $(top_builddir)/src/lib/testing/libgnunettesting.la \ + $(top_builddir)/src/service/testing/libgnunettesting.la \ $(top_builddir)/src/lib/util/libgnunetutil.la \ $(top_builddir)/src/service/identity/libgnunetidentity.la \ $(top_builddir)/src/lib/gnsrecord/libgnunetgnsrecord.la \ @@ -251,7 +251,7 @@ test_namestore_api_store_update_sqlite_LDADD = \ test_namestore_api_store_update_postgres_SOURCES = \ test_namestore_api_store_update.c test_namestore_api_store_update_postgres_LDADD = \ - $(top_builddir)/src/lib/testing/libgnunettesting.la \ + $(top_builddir)/src/service/testing/libgnunettesting.la \ $(top_builddir)/src/lib/util/libgnunetutil.la \ $(top_builddir)/src/service/identity/libgnunetidentity.la \ $(top_builddir)/src/lib/gnsrecord/libgnunetgnsrecord.la \ @@ -261,7 +261,7 @@ test_namestore_api_store_update_postgres_LDADD = \ test_namestore_api_lookup_nick_sqlite_SOURCES = \ test_namestore_api_lookup_nick.c test_namestore_api_lookup_nick_sqlite_LDADD = \ - $(top_builddir)/src/lib/testing/libgnunettesting.la \ + $(top_builddir)/src/service/testing/libgnunettesting.la \ $(top_builddir)/src/lib/util/libgnunetutil.la \ $(top_builddir)/src/service/identity/libgnunetidentity.la \ $(top_builddir)/src/lib/gnsrecord/libgnunetgnsrecord.la \ @@ -271,7 +271,7 @@ test_namestore_api_lookup_nick_sqlite_LDADD = \ test_namestore_api_lookup_nick_postgres_SOURCES = \ test_namestore_api_lookup_nick.c test_namestore_api_lookup_nick_postgres_LDADD = \ - $(top_builddir)/src/lib/testing/libgnunettesting.la \ + $(top_builddir)/src/service/testing/libgnunettesting.la \ $(top_builddir)/src/lib/util/libgnunetutil.la \ $(top_builddir)/src/service/identity/libgnunetidentity.la \ $(top_builddir)/src/lib/gnsrecord/libgnunetgnsrecord.la \ @@ -282,7 +282,7 @@ test_namestore_api_remove_sqlite_SOURCES = \ test_namestore_api_remove.c test_namestore_api_remove_sqlite_LDADD = \ $(top_builddir)/src/service/identity/libgnunetidentity.la \ - $(top_builddir)/src/lib/testing/libgnunettesting.la \ + $(top_builddir)/src/service/testing/libgnunettesting.la \ $(top_builddir)/src/lib/util/libgnunetutil.la \ $(top_builddir)/src/lib/gnsrecord/libgnunetgnsrecord.la \ libgnunetnamestore.la @@ -291,7 +291,7 @@ test_namestore_api_remove_postgres_SOURCES = \ test_namestore_api_remove.c test_namestore_api_remove_postgres_LDADD = \ $(top_builddir)/src/service/identity/libgnunetidentity.la \ - $(top_builddir)/src/lib/testing/libgnunettesting.la \ + $(top_builddir)/src/service/testing/libgnunettesting.la \ $(top_builddir)/src/lib/util/libgnunetutil.la \ $(top_builddir)/src/lib/gnsrecord/libgnunetgnsrecord.la \ libgnunetnamestore.la @@ -299,7 +299,7 @@ test_namestore_api_remove_postgres_LDADD = \ test_namestore_api_remove_not_existing_record_sqlite_SOURCES = \ test_namestore_api_remove_not_existing_record.c test_namestore_api_remove_not_existing_record_sqlite_LDADD = \ - $(top_builddir)/src/lib/testing/libgnunettesting.la \ + $(top_builddir)/src/service/testing/libgnunettesting.la \ $(top_builddir)/src/lib/util/libgnunetutil.la \ $(top_builddir)/src/service/identity/libgnunetidentity.la \ $(top_builddir)/src/lib/gnsrecord/libgnunetgnsrecord.la \ @@ -308,7 +308,7 @@ test_namestore_api_remove_not_existing_record_sqlite_LDADD = \ test_namestore_api_remove_not_existing_record_postgres_SOURCES = \ test_namestore_api_remove_not_existing_record.c test_namestore_api_remove_not_existing_record_postgres_LDADD = \ - $(top_builddir)/src/lib/testing/libgnunettesting.la \ + $(top_builddir)/src/service/testing/libgnunettesting.la \ $(top_builddir)/src/service/identity/libgnunetidentity.la \ $(top_builddir)/src/lib/util/libgnunetutil.la \ $(top_builddir)/src/lib/gnsrecord/libgnunetgnsrecord.la \ @@ -318,7 +318,7 @@ test_namestore_api_zone_to_name_sqlite_SOURCES = \ test_namestore_api_zone_to_name.c test_namestore_api_zone_to_name_sqlite_LDADD = \ $(top_builddir)/src/service/identity/libgnunetidentity.la \ - $(top_builddir)/src/lib/testing/libgnunettesting.la \ + $(top_builddir)/src/service/testing/libgnunettesting.la \ $(top_builddir)/src/lib/util/libgnunetutil.la \ libgnunetnamestore.la @@ -326,7 +326,7 @@ test_namestore_api_zone_to_name_postgres_SOURCES = \ test_namestore_api_zone_to_name.c test_namestore_api_zone_to_name_postgres_LDADD = \ $(top_builddir)/src/service/identity/libgnunetidentity.la \ - $(top_builddir)/src/lib/testing/libgnunettesting.la \ + $(top_builddir)/src/service/testing/libgnunettesting.la \ $(top_builddir)/src/lib/util/libgnunetutil.la \ libgnunetnamestore.la @@ -334,7 +334,7 @@ test_namestore_api_monitoring_sqlite_SOURCES = \ test_namestore_api_monitoring.c test_namestore_api_monitoring_sqlite_LDADD = \ $(top_builddir)/src/service/identity/libgnunetidentity.la \ - $(top_builddir)/src/lib/testing/libgnunettesting.la \ + $(top_builddir)/src/service/testing/libgnunettesting.la \ libgnunetnamestore.la \ $(top_builddir)/src/lib/gnsrecord/libgnunetgnsrecord.la \ $(top_builddir)/src/lib/util/libgnunetutil.la @@ -342,7 +342,7 @@ test_namestore_api_monitoring_sqlite_LDADD = \ test_namestore_api_monitoring_postgres_SOURCES = \ test_namestore_api_monitoring.c test_namestore_api_monitoring_postgres_LDADD = \ - $(top_builddir)/src/lib/testing/libgnunettesting.la \ + $(top_builddir)/src/service/testing/libgnunettesting.la \ $(top_builddir)/src/service/identity/libgnunetidentity.la \ libgnunetnamestore.la \ $(top_builddir)/src/lib/gnsrecord/libgnunetgnsrecord.la \ @@ -351,7 +351,7 @@ test_namestore_api_monitoring_postgres_LDADD = \ test_namestore_api_monitoring_existing_sqlite_SOURCES = \ test_namestore_api_monitoring_existing.c test_namestore_api_monitoring_existing_sqlite_LDADD = \ - $(top_builddir)/src/lib/testing/libgnunettesting.la \ + $(top_builddir)/src/service/testing/libgnunettesting.la \ $(top_builddir)/src/service/identity/libgnunetidentity.la \ libgnunetnamestore.la \ $(top_builddir)/src/lib/gnsrecord/libgnunetgnsrecord.la \ @@ -360,7 +360,7 @@ test_namestore_api_monitoring_existing_sqlite_LDADD = \ test_namestore_api_monitoring_existing_postgres_SOURCES = \ test_namestore_api_monitoring_existing.c test_namestore_api_monitoring_existing_postgres_LDADD = \ - $(top_builddir)/src/lib/testing/libgnunettesting.la \ + $(top_builddir)/src/service/testing/libgnunettesting.la \ libgnunetnamestore.la \ $(top_builddir)/src/lib/gnsrecord/libgnunetgnsrecord.la \ $(top_builddir)/src/service/identity/libgnunetidentity.la \ @@ -369,7 +369,7 @@ test_namestore_api_monitoring_existing_postgres_LDADD = \ test_namestore_api_tx_rollback_sqlite_SOURCES = \ test_namestore_api_tx_rollback.c test_namestore_api_tx_rollback_sqlite_LDADD = \ - $(top_builddir)/src/lib/testing/libgnunettesting.la \ + $(top_builddir)/src/service/testing/libgnunettesting.la \ $(top_builddir)/src/service/identity/libgnunetidentity.la \ libgnunetnamestore.la \ $(top_builddir)/src/lib/gnsrecord/libgnunetgnsrecord.la \ @@ -378,7 +378,7 @@ test_namestore_api_tx_rollback_sqlite_LDADD = \ test_namestore_api_tx_rollback_postgres_SOURCES = \ test_namestore_api_tx_rollback.c test_namestore_api_tx_rollback_postgres_LDADD = \ - $(top_builddir)/src/lib/testing/libgnunettesting.la \ + $(top_builddir)/src/service/testing/libgnunettesting.la \ $(top_builddir)/src/service/identity/libgnunetidentity.la \ libgnunetnamestore.la \ $(top_builddir)/src/lib/gnsrecord/libgnunetgnsrecord.la \ @@ -388,7 +388,7 @@ if HAVE_EXPERIMENTAL test_namestore_api_edit_records_postgres_SOURCES = \ test_namestore_api_edit_records.c test_namestore_api_edit_records_postgres_LDADD = \ - $(top_builddir)/src/lib/testing/libgnunettesting.la \ + $(top_builddir)/src/service/testing/libgnunettesting.la \ $(top_builddir)/src/service/identity/libgnunetidentity.la \ libgnunetnamestore.la \ $(top_builddir)/src/lib/gnsrecord/libgnunetgnsrecord.la \ @@ -398,7 +398,7 @@ endif test_namestore_api_zone_iteration_sqlite_SOURCES = \ test_namestore_api_zone_iteration.c test_namestore_api_zone_iteration_sqlite_LDADD = \ - $(top_builddir)/src/lib/testing/libgnunettesting.la \ + $(top_builddir)/src/service/testing/libgnunettesting.la \ $(top_builddir)/src/service/identity/libgnunetidentity.la \ $(top_builddir)/src/lib/util/libgnunetutil.la \ $(top_builddir)/src/lib/gnsrecord/libgnunetgnsrecord.la \ @@ -407,7 +407,7 @@ test_namestore_api_zone_iteration_sqlite_LDADD = \ test_namestore_api_zone_iteration_postgres_SOURCES = \ test_namestore_api_zone_iteration.c test_namestore_api_zone_iteration_postgres_LDADD = \ - $(top_builddir)/src/lib/testing/libgnunettesting.la \ + $(top_builddir)/src/service/testing/libgnunettesting.la \ $(top_builddir)/src/service/identity/libgnunetidentity.la \ $(top_builddir)/src/lib/util/libgnunetutil.la \ $(top_builddir)/src/lib/gnsrecord/libgnunetgnsrecord.la \ @@ -416,7 +416,7 @@ test_namestore_api_zone_iteration_postgres_LDADD = \ perf_namestore_api_zone_iteration_postgres_SOURCES = \ perf_namestore_api_zone_iteration.c perf_namestore_api_zone_iteration_postgres_LDADD = \ - $(top_builddir)/src/lib/testing/libgnunettesting.la \ + $(top_builddir)/src/service/testing/libgnunettesting.la \ $(top_builddir)/src/lib/util/libgnunetutil.la \ $(top_builddir)/src/service/identity/libgnunetidentity.la \ $(top_builddir)/src/lib/gnsrecord/libgnunetgnsrecord.la \ @@ -425,7 +425,7 @@ perf_namestore_api_zone_iteration_postgres_LDADD = \ perf_namestore_api_import_sqlite_SOURCES = \ perf_namestore_api_import.c perf_namestore_api_import_sqlite_LDADD = \ - $(top_builddir)/src/lib/testing/libgnunettesting.la \ + $(top_builddir)/src/service/testing/libgnunettesting.la \ $(top_builddir)/src/lib/util/libgnunetutil.la \ $(top_builddir)/src/service/identity/libgnunetidentity.la \ $(top_builddir)/src/lib/gnsrecord/libgnunetgnsrecord.la \ @@ -434,7 +434,7 @@ perf_namestore_api_import_sqlite_LDADD = \ perf_namestore_api_import_postgres_SOURCES = \ perf_namestore_api_import.c perf_namestore_api_import_postgres_LDADD = \ - $(top_builddir)/src/lib/testing/libgnunettesting.la \ + $(top_builddir)/src/service/testing/libgnunettesting.la \ $(top_builddir)/src/lib/util/libgnunetutil.la \ $(top_builddir)/src/service/identity/libgnunetidentity.la \ $(top_builddir)/src/lib/gnsrecord/libgnunetgnsrecord.la \ @@ -444,7 +444,7 @@ perf_namestore_api_import_postgres_LDADD = \ perf_namestore_api_zone_iteration_sqlite_SOURCES = \ perf_namestore_api_zone_iteration.c perf_namestore_api_zone_iteration_sqlite_LDADD = \ - $(top_builddir)/src/lib/testing/libgnunettesting.la \ + $(top_builddir)/src/service/testing/libgnunettesting.la \ $(top_builddir)/src/service/identity/libgnunetidentity.la \ $(top_builddir)/src/lib/util/libgnunetutil.la \ $(top_builddir)/src/lib/gnsrecord/libgnunetgnsrecord.la \ @@ -453,7 +453,7 @@ perf_namestore_api_zone_iteration_sqlite_LDADD = \ test_namestore_api_zone_iteration_nick_sqlite_SOURCES = \ test_namestore_api_zone_iteration_nick.c test_namestore_api_zone_iteration_nick_sqlite_LDADD = \ - $(top_builddir)/src/lib/testing/libgnunettesting.la \ + $(top_builddir)/src/service/testing/libgnunettesting.la \ $(top_builddir)/src/service/identity/libgnunetidentity.la \ $(top_builddir)/src/lib/util/libgnunetutil.la \ $(top_builddir)/src/lib/gnsrecord/libgnunetgnsrecord.la \ @@ -462,7 +462,7 @@ test_namestore_api_zone_iteration_nick_sqlite_LDADD = \ test_namestore_api_zone_iteration_nick_postgres_SOURCES = \ test_namestore_api_zone_iteration_nick.c test_namestore_api_zone_iteration_nick_postgres_LDADD = \ - $(top_builddir)/src/lib/testing/libgnunettesting.la \ + $(top_builddir)/src/service/testing/libgnunettesting.la \ $(top_builddir)/src/service/identity/libgnunetidentity.la \ $(top_builddir)/src/lib/util/libgnunetutil.la \ $(top_builddir)/src/lib/gnsrecord/libgnunetgnsrecord.la \ @@ -471,7 +471,7 @@ test_namestore_api_zone_iteration_nick_postgres_LDADD = \ test_namestore_api_zone_iteration_specific_zone_sqlite_SOURCES = \ test_namestore_api_zone_iteration_specific_zone.c test_namestore_api_zone_iteration_specific_zone_sqlite_LDADD = \ - $(top_builddir)/src/lib/testing/libgnunettesting.la \ + $(top_builddir)/src/service/testing/libgnunettesting.la \ $(top_builddir)/src/service/identity/libgnunetidentity.la \ $(top_builddir)/src/lib/util/libgnunetutil.la \ $(top_builddir)/src/lib/gnsrecord/libgnunetgnsrecord.la \ @@ -480,7 +480,7 @@ test_namestore_api_zone_iteration_specific_zone_sqlite_LDADD = \ test_namestore_api_zone_iteration_specific_zone_postgres_SOURCES = \ test_namestore_api_zone_iteration_specific_zone.c test_namestore_api_zone_iteration_specific_zone_postgres_LDADD = \ - $(top_builddir)/src/lib/testing/libgnunettesting.la \ + $(top_builddir)/src/service/testing/libgnunettesting.la \ $(top_builddir)/src/service/identity/libgnunetidentity.la \ $(top_builddir)/src/lib/util/libgnunetutil.la \ $(top_builddir)/src/lib/gnsrecord/libgnunetgnsrecord.la \ @@ -489,7 +489,7 @@ test_namestore_api_zone_iteration_specific_zone_postgres_LDADD = \ test_namestore_api_zone_iteration_stop_sqlite_SOURCES = \ test_namestore_api_zone_iteration_stop.c test_namestore_api_zone_iteration_stop_sqlite_LDADD = \ - $(top_builddir)/src/lib/testing/libgnunettesting.la \ + $(top_builddir)/src/service/testing/libgnunettesting.la \ $(top_builddir)/src/service/identity/libgnunetidentity.la \ $(top_builddir)/src/lib/util/libgnunetutil.la \ $(top_builddir)/src/lib/gnsrecord/libgnunetgnsrecord.la \ @@ -498,7 +498,7 @@ test_namestore_api_zone_iteration_stop_sqlite_LDADD = \ test_namestore_api_zone_iteration_stop_postgres_SOURCES = \ test_namestore_api_zone_iteration_stop.c test_namestore_api_zone_iteration_stop_postgres_LDADD = \ - $(top_builddir)/src/lib/testing/libgnunettesting.la \ + $(top_builddir)/src/service/testing/libgnunettesting.la \ $(top_builddir)/src/service/identity/libgnunetidentity.la \ $(top_builddir)/src/lib/util/libgnunetutil.la \ $(top_builddir)/src/lib/gnsrecord/libgnunetgnsrecord.la \ @@ -507,7 +507,7 @@ test_namestore_api_zone_iteration_stop_postgres_LDADD = \ test_plugin_namestore_sqlite_SOURCES = \ test_plugin_namestore.c test_plugin_namestore_sqlite_LDADD = \ - $(top_builddir)/src/lib/testing/libgnunettesting.la \ + $(top_builddir)/src/service/testing/libgnunettesting.la \ $(top_builddir)/src/service/identity/libgnunetidentity.la \ $(top_builddir)/src/lib/util/libgnunetutil.la @@ -515,7 +515,7 @@ test_plugin_namestore_postgres_SOURCES = \ test_plugin_namestore.c test_plugin_namestore_postgres_LDADD = \ $(top_builddir)/src/service/identity/libgnunetidentity.la \ - $(top_builddir)/src/lib/testing/libgnunettesting.la \ + $(top_builddir)/src/service/testing/libgnunettesting.la \ $(top_builddir)/src/lib/util/libgnunetutil.la check_SCRIPTS = \ diff --git a/src/nat-auto/Makefile.am b/src/nat-auto/Makefile.am index 0ca5da4dc..7c3f8e11d 100644 --- a/src/nat-auto/Makefile.am +++ b/src/nat-auto/Makefile.am @@ -18,7 +18,7 @@ libexec_PROGRAMS = \ gnunet_nat_server_SOURCES = \ gnunet-nat-server.c nat-auto.h gnunet_nat_server_LDADD = \ - $(top_builddir)/src/nat/libgnunetnatnew.la \ + $(top_builddir)/src/service/nat/libgnunetnatnew.la \ $(top_builddir)/src/lib/util/libgnunetutil.la gnunet_nat_server_LDFLAGS = \ $(GN_LIBINTL) @@ -42,7 +42,7 @@ libgnunetnatauto_la_SOURCES = \ nat_auto_api.c \ nat_auto_api_test.c libgnunetnatauto_la_LIBADD = \ - $(top_builddir)/src/nat/libgnunetnatnew.la \ + $(top_builddir)/src/service/nat/libgnunetnatnew.la \ $(top_builddir)/src/lib/util/libgnunetutil.la \ $(GN_LIBINTL) @EXT_LIBS@ libgnunetnatauto_la_LDFLAGS = \ @@ -53,8 +53,8 @@ gnunet_service_nat_auto_SOURCES = \ gnunet-service-nat-auto.c gnunet_service_nat_auto_LDADD = \ $(top_builddir)/src/lib/util/libgnunetutil.la \ - $(top_builddir)/src/statistics/libgnunetstatistics.la \ - $(top_builddir)/src/nat/libgnunetnatnew.la \ + $(top_builddir)/src/service/statistics/libgnunetstatistics.la \ + $(top_builddir)/src/service/nat/libgnunetnatnew.la \ $(LIBGCRYPT_LIBS) \ -lgcrypt \ $(GN_LIBINTL) diff --git a/src/nat/.gitignore b/src/nat/.gitignore deleted file mode 100644 index 868abab4b..000000000 --- a/src/nat/.gitignore +++ /dev/null @@ -1,5 +0,0 @@ -gnunet-service-nat -gnunet-helper-nat-client -gnunet-helper-nat-server -gnunet-nat -gnunet-nat-server diff --git a/src/nat/Makefile.am b/src/nat/Makefile.am deleted file mode 100644 index 78ba9b92c..000000000 --- a/src/nat/Makefile.am +++ /dev/null @@ -1,118 +0,0 @@ -# This Makefile.am is in the public domain -AM_CPPFLAGS = -I$(top_srcdir)/src/include - -libexecdir= $(pkglibdir)/libexec/ - -pkgcfgdir= $(pkgdatadir)/config.d/ - -pkgcfg_DATA = \ - nat.conf - -if LINUX - NATBIN = gnunet-helper-nat-server gnunet-helper-nat-client - NATSERVER = gnunet-helper-nat-server.c - NATCLIENT = gnunet-helper-nat-client.c -else -if XFREEBSD - NATBIN = gnunet-helper-nat-server gnunet-helper-nat-client - NATSERVER = gnunet-helper-nat-server.c - NATCLIENT = gnunet-helper-nat-client.c -endif -else -install-exec-hook: -endif - -bin_PROGRAMS = \ - gnunet-nat - -libexec_PROGRAMS = \ - $(NATBIN) \ - gnunet-service-nat - - -gnunet_helper_nat_server_SOURCES = \ - $(NATSERVER) - -gnunet_helper_nat_client_SOURCES = \ - $(NATCLIENT) - - -gnunet_nat_SOURCES = \ - gnunet-nat.c nat.h -gnunet_nat_LDADD = \ - libgnunetnatnew.la \ - $(top_builddir)/src/lib/util/libgnunetutil.la -gnunet_nat_LDFLAGS = \ - $(GN_LIBINTL) - - -if USE_COVERAGE - AM_CFLAGS = -fprofile-arcs -ftest-coverage -endif - -lib_LTLIBRARIES = \ - libgnunetnatnew.la - -libgnunetnatnew_la_SOURCES = \ - nat_api.c \ - nat_api_stun.c nat_stun.h \ - nat.h -libgnunetnatnew_la_LIBADD = \ - $(top_builddir)/src/lib/util/libgnunetutil.la \ - $(GN_LIBINTL) @EXT_LIBS@ -libgnunetnatnew_la_LDFLAGS = \ - $(GN_LIB_LDFLAGS) \ - -version-info 2:0:0 - -gnunet_service_nat_SOURCES = \ - gnunet-service-nat.c gnunet-service-nat.h \ - gnunet-service-nat_externalip.c gnunet-service-nat_externalip.h \ - gnunet-service-nat_stun.c gnunet-service-nat_stun.h \ - gnunet-service-nat_mini.c gnunet-service-nat_mini.h \ - gnunet-service-nat_helper.c gnunet-service-nat_helper.h -gnunet_service_nat_LDADD = \ - $(top_builddir)/src/lib/util/libgnunetutil.la \ - $(top_builddir)/src/statistics/libgnunetstatistics.la \ - $(LIBGCRYPT_LIBS) \ - -lgcrypt \ - $(GN_LIBINTL) - -#check_PROGRAMS = \ -# test_nat \ -# test_nat_mini \ -# test_nat_test \ -# test_stun - -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_nat_SOURCES = \ -# test_nat.c -#test_nat_LDADD = \ -# libgnunetnat.la \ -# $(top_builddir)/src/lib/util/libgnunetutil.la - -#test_nat_mini_SOURCES = \ -# test_nat_mini.c -#test_nat_mini_LDADD = \ -# libgnunetnat.la \ -# $(top_builddir)/src/lib/util/libgnunetutil.la - -#test_nat_test_SOURCES = \ -# test_nat_test.c -#test_nat_test_LDADD = \ -# libgnunetnat.la \ -# $(top_builddir)/src/lib/util/libgnunetutil.la - -#test_stun_SOURCES = \ -# test_stun.c -#test_stun_LDADD = \ -# libgnunetnat.la \ -# $(top_builddir)/src/lib/util/libgnunetutil.la - -EXTRA_DIST = \ - test_nat_data.conf \ - test_nat_test_data.conf \ - test_stun.conf diff --git a/src/nat/gnunet-helper-nat-client.c b/src/nat/gnunet-helper-nat-client.c deleted file mode 100644 index 0b86aa0d2..000000000 --- a/src/nat/gnunet-helper-nat-client.c +++ /dev/null @@ -1,547 +0,0 @@ -/* - This file is part of GNUnet. - Copyright (C) 2010 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 . - - SPDX-License-Identifier: AGPL3.0-or-later - */ - -/** - * @file src/nat/gnunet-helper-nat-client.c - * @brief Tool to help bypass NATs using ICMP method; must run as root (SUID will do) - * This code will work under GNU/Linux only. - * @author Christian Grothoff - * - * This program will send ONE ICMP message using RAW sockets - * to the IP address specified as the second argument. Since - * it uses RAW sockets, it must be installed SUID or run as 'root'. - * In order to keep the security risk of the resulting SUID binary - * minimal, the program ONLY opens the RAW socket with root - * privileges, then drops them and only then starts to process - * command line arguments. The code also does not link against - * any shared libraries (except libc) and is strictly minimal - * (except for checking for errors). The following list of people - * have reviewed this code and considered it safe since the last - * modification (if you reviewed it, please have your name added - * to the list): - * - * - Christian Grothoff - * - Nathan Evans - * - Benjamin Kuperman (22 Aug 2010) - */ -#if HAVE_CONFIG_H -/* Just needed for HAVE_SOCKADDR_IN_SIN_LEN test macro! */ -#include "platform.h" -#include "gnunet_private_config.h" -#else -#define _GNU_SOURCE -#endif -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -/* The following constant is missing from FreeBSD 9.2 */ -#ifndef ICMP_TIME_EXCEEDED -#define ICMP_TIME_EXCEEDED 11 -#endif - -/** - * Call memcpy() but check for @a n being 0 first. In the latter - * case, it is now safe to pass NULL for @a src or @a dst. - * Unlike traditional memcpy(), returns nothing. - * - * @param dst destination of the copy, may be NULL if @a n is zero - * @param src source of the copy, may be NULL if @a n is zero - * @param n number of bytes to copy - */ -#define GNUNET_memcpy(dst, src, n) do { if (0 != n) { (void) memcpy (dst, src, \ - n); \ - } } while (0) - -/** - * Must match IP given in the server. - */ -#define DUMMY_IP "192.0.2.86" - -#define NAT_TRAV_PORT 22225 - -/** - * Must match packet ID used by gnunet-helper-nat-server.c - */ -#define PACKET_ID 256 - -/** - * IPv4 header. - */ -struct ip_header -{ - /** - * Version (4 bits) + Internet header length (4 bits) - */ - uint8_t vers_ihl; - - /** - * Type of service - */ - uint8_t tos; - - /** - * Total length - */ - uint16_t pkt_len; - - /** - * Identification - */ - uint16_t id; - - /** - * Flags (3 bits) + Fragment offset (13 bits) - */ - uint16_t flags_frag_offset; - - /** - * Time to live - */ - uint8_t ttl; - - /** - * Protocol - */ - uint8_t proto; - - /** - * Header checksum - */ - uint16_t checksum; - - /** - * Source address - */ - uint32_t src_ip; - - /** - * Destination address - */ - uint32_t dst_ip; -}; - -/** - * Format of ICMP packet. - */ -struct icmp_ttl_exceeded_header -{ - uint8_t type; - - uint8_t code; - - uint16_t checksum; - - uint32_t unused; - - /* followed by original payload */ -}; - -struct icmp_echo_header -{ - uint8_t type; - - uint8_t code; - - uint16_t checksum; - - uint32_t reserved; -}; - -/** - * Beginning of UDP packet. - */ -struct udp_header -{ - uint16_t src_port; - - uint16_t dst_port; - - uint16_t length; - - uint16_t crc; -}; - -/** - * Socket we use to send our fake ICMP replies. - */ -static int rawsock; - -/** - * Target "dummy" address of the packet we pretend to respond to. - */ -static struct in_addr dummy; - -/** - * Our "source" port. - */ -static uint16_t port; - - -/** - * CRC-16 for IP/ICMP headers. - * - * @param data what to calculate the CRC over - * @param bytes number of bytes in data (must be multiple of 2) - * @return the CRC 16. - */ -static uint16_t -calc_checksum (const uint16_t *data, unsigned int bytes) -{ - uint32_t sum; - unsigned int i; - - sum = 0; - for (i = 0; i < bytes / 2; i++) - sum += data[i]; - sum = (sum & 0xffff) + (sum >> 16); - sum = htons (0xffff - sum); - return sum; -} - - -/** - * Send an ICMP message to the target. - * - * @param my_ip source address - * @param other target address - */ -static void -send_icmp_udp (const struct in_addr *my_ip, const struct in_addr *other) -{ - char packet[sizeof(struct ip_header) * 2 - + sizeof(struct icmp_ttl_exceeded_header) - + sizeof(struct udp_header)]; - struct ip_header ip_pkt; - struct icmp_ttl_exceeded_header icmp_pkt; - struct udp_header udp_pkt; - struct sockaddr_in dst; - size_t off; - int err; - - /* ip header: send to (known) ip address */ - off = 0; - ip_pkt.vers_ihl = 0x45; - ip_pkt.tos = 0; - /* should this be BSD only? */ -#if defined(BSD) && defined(__FreeBSD__) && defined(__FreeBSD_kernel__) - ip_pkt.pkt_len = sizeof(packet); /* Workaround PR kern/21737 */ -#else - ip_pkt.pkt_len = htons (sizeof(packet)); -#endif - ip_pkt.id = htons (PACKET_ID); - ip_pkt.flags_frag_offset = 0; - ip_pkt.ttl = 128; - ip_pkt.proto = IPPROTO_ICMP; - ip_pkt.checksum = 0; - ip_pkt.src_ip = my_ip->s_addr; - ip_pkt.dst_ip = other->s_addr; - ip_pkt.checksum = - htons (calc_checksum ((uint16_t *) &ip_pkt, sizeof(struct ip_header))); - GNUNET_memcpy (&packet[off], - &ip_pkt, - sizeof(struct ip_header)); - off += sizeof(struct ip_header); - - icmp_pkt.type = ICMP_TIME_EXCEEDED; - icmp_pkt.code = 0; - icmp_pkt.checksum = 0; - icmp_pkt.unused = 0; - GNUNET_memcpy (&packet[off], - &icmp_pkt, - sizeof(struct icmp_ttl_exceeded_header)); - off += sizeof(struct icmp_ttl_exceeded_header); - - /* ip header of the presumably 'lost' udp packet */ - ip_pkt.vers_ihl = 0x45; - ip_pkt.tos = 0; - ip_pkt.pkt_len = - htons (sizeof(struct ip_header) + sizeof(struct udp_header)); - ip_pkt.id = htons (0); - ip_pkt.flags_frag_offset = 0; - ip_pkt.ttl = 128; - ip_pkt.proto = IPPROTO_UDP; - ip_pkt.checksum = 0; - ip_pkt.src_ip = other->s_addr; - ip_pkt.dst_ip = dummy.s_addr; - ip_pkt.checksum = - htons (calc_checksum ((uint16_t *) &ip_pkt, sizeof(struct ip_header))); - GNUNET_memcpy (&packet[off], - &ip_pkt, - sizeof(struct ip_header)); - off += sizeof(struct ip_header); - - /* build UDP header */ - udp_pkt.src_port = htons (NAT_TRAV_PORT); - udp_pkt.dst_port = htons (NAT_TRAV_PORT); - udp_pkt.length = htons (port); - udp_pkt.crc = 0; - GNUNET_memcpy (&packet[off], - &udp_pkt, - sizeof(struct udp_header)); - off += sizeof(struct udp_header); - - /* set ICMP checksum */ - icmp_pkt.checksum = - htons (calc_checksum - ((uint16_t *) &packet[sizeof(struct ip_header)], - sizeof(struct icmp_ttl_exceeded_header) - + sizeof(struct ip_header) + sizeof(struct udp_header))); - GNUNET_memcpy (&packet[sizeof(struct ip_header)], - &icmp_pkt, - sizeof(struct icmp_ttl_exceeded_header)); - - memset (&dst, 0, sizeof(dst)); - dst.sin_family = AF_INET; -#if HAVE_SOCKADDR_IN_SIN_LEN - dst.sin_len = sizeof(struct sockaddr_in); -#endif - dst.sin_addr = *other; - err = - sendto (rawsock, packet, sizeof(packet), 0, (struct sockaddr *) &dst, - sizeof(dst)); - if (err < 0) - { - fprintf (stderr, "sendto failed: %s\n", strerror (errno)); - } - else if (sizeof(packet) != (size_t) err) - { - fprintf (stderr, "Error: partial send of ICMP message with size %lu\n", - (unsigned long) off); - } -} - - -/** - * Send an ICMP message to the target. - * - * @param my_ip source address - * @param other target address - */ -static void -send_icmp (const struct in_addr *my_ip, const struct in_addr *other) -{ - struct ip_header ip_pkt; - struct icmp_ttl_exceeded_header icmp_ttl; - struct icmp_echo_header icmp_echo; - struct sockaddr_in dst; - char packet[sizeof(struct ip_header) * 2 - + sizeof(struct icmp_ttl_exceeded_header) - + sizeof(struct icmp_echo_header)]; - size_t off; - int err; - - /* ip header: send to (known) ip address */ - off = 0; - ip_pkt.vers_ihl = 0x45; - ip_pkt.tos = 0; -#if defined(BSD) && defined(__FreeBSD__) && defined(__FreeBSD_kernel__) - ip_pkt.pkt_len = sizeof(packet); /* Workaround PR kern/21737 */ -#else - ip_pkt.pkt_len = htons (sizeof(packet)); -#endif - ip_pkt.id = htons (PACKET_ID); - ip_pkt.flags_frag_offset = 0; - ip_pkt.ttl = IPDEFTTL; - ip_pkt.proto = IPPROTO_ICMP; - ip_pkt.checksum = 0; - ip_pkt.src_ip = my_ip->s_addr; - ip_pkt.dst_ip = other->s_addr; - ip_pkt.checksum = - htons (calc_checksum ((uint16_t *) &ip_pkt, sizeof(struct ip_header))); - GNUNET_memcpy (&packet[off], - &ip_pkt, - sizeof(struct ip_header)); - off = sizeof(ip_pkt); - - /* icmp reply: time exceeded */ - icmp_ttl.type = ICMP_TIME_EXCEEDED; - icmp_ttl.code = 0; - icmp_ttl.checksum = 0; - icmp_ttl.unused = 0; - GNUNET_memcpy (&packet[off], - &icmp_ttl, - sizeof(struct icmp_ttl_exceeded_header)); - off += sizeof(struct icmp_ttl_exceeded_header); - - /* ip header of the presumably 'lost' udp packet */ - ip_pkt.vers_ihl = 0x45; - ip_pkt.tos = 0; - ip_pkt.pkt_len = - htons (sizeof(struct ip_header) + sizeof(struct icmp_echo_header)); - ip_pkt.id = htons (PACKET_ID); - ip_pkt.flags_frag_offset = 0; - ip_pkt.ttl = 1; /* real TTL would be 1 on a time exceeded packet */ - ip_pkt.proto = IPPROTO_ICMP; - ip_pkt.src_ip = other->s_addr; - ip_pkt.dst_ip = dummy.s_addr; - ip_pkt.checksum = 0; - ip_pkt.checksum = - htons (calc_checksum ((uint16_t *) &ip_pkt, sizeof(struct ip_header))); - GNUNET_memcpy (&packet[off], - &ip_pkt, - sizeof(struct ip_header)); - off += sizeof(struct ip_header); - - icmp_echo.type = ICMP_ECHO; - icmp_echo.code = 0; - icmp_echo.reserved = htonl (port); - icmp_echo.checksum = 0; - icmp_echo.checksum = - htons (calc_checksum - ((uint16_t *) &icmp_echo, sizeof(struct icmp_echo_header))); - GNUNET_memcpy (&packet[off], - &icmp_echo, - sizeof(struct icmp_echo_header)); - - /* no go back to calculate ICMP packet checksum */ - off = sizeof(struct ip_header); - icmp_ttl.checksum = - htons (calc_checksum - ((uint16_t *) &packet[off], - sizeof(struct icmp_ttl_exceeded_header) - + sizeof(struct ip_header) + sizeof(struct icmp_echo_header))); - GNUNET_memcpy (&packet[off], - &icmp_ttl, - sizeof(struct icmp_ttl_exceeded_header)); - - /* prepare for transmission */ - memset (&dst, 0, sizeof(dst)); - dst.sin_family = AF_INET; -#if HAVE_SOCKADDR_IN_SIN_LEN - dst.sin_len = sizeof(struct sockaddr_in); -#endif - dst.sin_addr = *other; - err = - sendto (rawsock, packet, sizeof(packet), 0, (struct sockaddr *) &dst, - sizeof(dst)); - if (err < 0) - { - fprintf (stderr, "sendto failed: %s\n", strerror (errno)); - } - else if (sizeof(packet) != (size_t) err) - { - fprintf (stderr, "Error: partial send of ICMP message\n"); - } -} - - -int -main (int argc, char *const *argv) -{ - const int one = 1; - struct in_addr external; - struct in_addr target; - uid_t uid; - unsigned int p; - int raw_eno; - int global_ret; - - /* Create an ICMP raw socket for writing (only operation that requires root) */ - rawsock = socket (AF_INET, SOCK_RAW, IPPROTO_RAW); - raw_eno = errno; /* for later error checking */ - - /* now drop root privileges */ - uid = getuid (); -#ifdef HAVE_SETRESUID - if (0 != setresuid (uid, uid, uid)) - { - fprintf (stderr, "Failed to setresuid: %s\n", strerror (errno)); - global_ret = 1; - goto cleanup; - } -#else - if (0 != (setuid (uid) | seteuid (uid))) - { - fprintf (stderr, "Failed to setuid: %s\n", strerror (errno)); - global_ret = 2; - goto cleanup; - } -#endif - if (-1 == rawsock) - { - fprintf (stderr, "Error opening RAW socket: %s\n", strerror (raw_eno)); - global_ret = 3; - goto cleanup; - } - if (0 != - setsockopt (rawsock, SOL_SOCKET, SO_BROADCAST, (char *) &one, - sizeof(one))) - { - fprintf (stderr, "setsockopt failed: %s\n", strerror (errno)); - global_ret = 4; - goto cleanup; - } - if (0 != - setsockopt (rawsock, IPPROTO_IP, IP_HDRINCL, (char *) &one, sizeof(one))) - { - fprintf (stderr, "setsockopt failed: %s\n", strerror (errno)); - global_ret = 5; - goto cleanup; - } - - if (4 != argc) - { - fprintf (stderr, - "This program must be started with our IP, the targets external IP, and our port as arguments.\n"); - global_ret = 6; - goto cleanup; - } - if ((1 != inet_pton (AF_INET, argv[1], &external)) || - (1 != inet_pton (AF_INET, argv[2], &target))) - { - fprintf (stderr, "Error parsing IPv4 address: %s\n", strerror (errno)); - global_ret = 7; - goto cleanup; - } - if ((1 != sscanf (argv[3], "%u", &p)) || (0 == p) || (0xFFFF < p)) - { - fprintf (stderr, "Error parsing port value `%s'\n", argv[3]); - global_ret = 8; - goto cleanup; - } - port = (uint16_t) p; - if (1 != inet_pton (AF_INET, DUMMY_IP, &dummy)) - { - fprintf (stderr, "Internal error converting dummy IP to binary.\n"); - global_ret = 9; - goto cleanup; - } - send_icmp (&external, &target); - send_icmp_udp (&external, &target); - global_ret = 0; -cleanup: - if (-1 != rawsock) - (void) close (rawsock); - return global_ret; -} - - -/* end of gnunet-helper-nat-client.c */ diff --git a/src/nat/gnunet-helper-nat-server.c b/src/nat/gnunet-helper-nat-server.c deleted file mode 100644 index d190a5dba..000000000 --- a/src/nat/gnunet-helper-nat-server.c +++ /dev/null @@ -1,715 +0,0 @@ -/* - This file is part of GNUnet. - Copyright (C) 2010 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 . - - SPDX-License-Identifier: AGPL3.0-or-later - */ - -/** - * @file src/nat/gnunet-helper-nat-server.c - * @brief Tool to help bypass NATs using ICMP method; must run as root (SUID will do) - * This code will work under GNU/Linux only (or maybe BSDs, but never W32) - * @author Christian Grothoff - * - * This program will send ONE ICMP message every 500 ms RAW sockets - * to a DUMMY IP address and also listens for ICMP replies. Since - * it uses RAW sockets, it must be installed SUID or run as 'root'. - * In order to keep the security risk of the resulting SUID binary - * minimal, the program ONLY opens the two RAW sockets with root - * privileges, then drops them and only then starts to process - * command line arguments. The code also does not link against - * any shared libraries (except libc) and is strictly minimal - * (except for checking for errors). The following list of people - * have reviewed this code and considered it safe since the last - * modification (if you reviewed it, please have your name added - * to the list): - * - * - Christian Grothoff - * - Nathan Evans - * - Benjamin Kuperman (22 Aug 2010) - * - Jacob Appelbaum (19 Dec 2011) - */ -#if HAVE_CONFIG_H -/* Just needed for HAVE_SOCKADDR_IN_SIN_LEN test macro! */ -#include "platform.h" -#include "gnunet_private_config.h" -#else -#define _GNU_SOURCE -#endif -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -/* The following constant is missing from FreeBSD 9.2 */ -#ifndef ICMP_TIME_EXCEEDED -#define ICMP_TIME_EXCEEDED 11 -#endif - -/** - * Call memcpy() but check for @a n being 0 first. In the latter - * case, it is now safe to pass NULL for @a src or @a dst. - * Unlike traditional memcpy(), returns nothing. - * - * @param dst destination of the copy, may be NULL if @a n is zero - * @param src source of the copy, may be NULL if @a n is zero - * @param n number of bytes to copy - */ -#define GNUNET_memcpy(dst, src, n) do { if (0 != n) { (void) memcpy (dst, src, \ - n); \ - } } while (0) - -/** - * Should we print some debug output? - */ -#define VERBOSE 0 - -/** - * Must match packet ID used by gnunet-helper-nat-client.c - */ -#define PACKET_ID 256 - -/** - * Must match IP given in the client. - */ -#define DUMMY_IP "192.0.2.86" - -/** - * Port for UDP - */ -#define NAT_TRAV_PORT 22225 - -/** - * How often do we send our ICMP messages to receive replies? - */ -#define ICMP_SEND_FREQUENCY_MS 500 - -/** - * IPv4 header. - */ -struct ip_header -{ - /** - * Version (4 bits) + Internet header length (4 bits) - */ - uint8_t vers_ihl; - - /** - * Type of service - */ - uint8_t tos; - - /** - * Total length - */ - uint16_t pkt_len; - - /** - * Identification - */ - uint16_t id; - - /** - * Flags (3 bits) + Fragment offset (13 bits) - */ - uint16_t flags_frag_offset; - - /** - * Time to live - */ - uint8_t ttl; - - /** - * Protocol - */ - uint8_t proto; - - /** - * Header checksum - */ - uint16_t checksum; - - /** - * Source address - */ - uint32_t src_ip; - - /** - * Destination address - */ - uint32_t dst_ip; -}; - -/** - * Format of ICMP packet. - */ -struct icmp_ttl_exceeded_header -{ - uint8_t type; - - uint8_t code; - - uint16_t checksum; - - uint32_t unused; - - /* followed by original payload */ -}; - -struct icmp_echo_header -{ - uint8_t type; - - uint8_t code; - - uint16_t checksum; - - uint32_t reserved; -}; - - -/** - * Beginning of UDP packet. - */ -struct udp_header -{ - uint16_t src_port; - - uint16_t dst_port; - - uint16_t length; - - uint16_t crc; -}; - -/** - * Socket we use to receive "fake" ICMP replies. - */ -static int icmpsock; - -/** - * Socket we use to send our ICMP requests. - */ -static int rawsock; - -/** - * Socket we use to send our UDP requests. - */ -static int udpsock; - -/** - * Target "dummy" address. - */ -static struct in_addr dummy; - - -/** - * CRC-16 for IP/ICMP headers. - * - * @param data what to calculate the CRC over - * @param bytes number of bytes in data (must be multiple of 2) - * @return the CRC 16. - */ -static uint16_t -calc_checksum (const uint16_t *data, unsigned int bytes) -{ - uint32_t sum; - unsigned int i; - - sum = 0; - for (i = 0; i < bytes / 2; i++) - sum += data[i]; - sum = (sum & 0xffff) + (sum >> 16); - sum = htons (0xffff - sum); - return sum; -} - - -/** - * Send an ICMP message to the dummy IP. - * - * @param my_ip source address (our ip address) - */ -static void -send_icmp_echo (const struct in_addr *my_ip) -{ - char packet[sizeof(struct ip_header) + sizeof(struct icmp_echo_header)]; - struct icmp_echo_header icmp_echo; - struct ip_header ip_pkt; - struct sockaddr_in dst; - size_t off; - int err; - - off = 0; - ip_pkt.vers_ihl = 0x45; - ip_pkt.tos = 0; - ip_pkt.pkt_len = htons (sizeof(packet)); - ip_pkt.id = htons (PACKET_ID); - ip_pkt.flags_frag_offset = 0; - ip_pkt.ttl = IPDEFTTL; - ip_pkt.proto = IPPROTO_ICMP; - ip_pkt.checksum = 0; - ip_pkt.src_ip = my_ip->s_addr; - ip_pkt.dst_ip = dummy.s_addr; - ip_pkt.checksum = - htons (calc_checksum ((uint16_t *) &ip_pkt, - sizeof(struct ip_header))); - GNUNET_memcpy (&packet[off], - &ip_pkt, - sizeof(struct ip_header)); - off += sizeof(struct ip_header); - - icmp_echo.type = ICMP_ECHO; - icmp_echo.code = 0; - icmp_echo.checksum = 0; - icmp_echo.reserved = 0; - icmp_echo.checksum = - htons (calc_checksum - ((uint16_t *) &icmp_echo, - sizeof(struct icmp_echo_header))); - GNUNET_memcpy (&packet[off], - &icmp_echo, - sizeof(struct icmp_echo_header)); - off += sizeof(struct icmp_echo_header); - - memset (&dst, 0, sizeof(dst)); - dst.sin_family = AF_INET; -#if HAVE_SOCKADDR_IN_SIN_LEN - dst.sin_len = sizeof(struct sockaddr_in); -#endif - dst.sin_addr = dummy; - err = sendto (rawsock, - packet, - off, - 0, - (struct sockaddr *) &dst, - sizeof(dst)); - if (err < 0) - { -#if VERBOSE - fprintf (stderr, - "sendto failed: %s\n", - strerror (errno)); -#endif - } - else if (sizeof(packet) != err) - { - fprintf (stderr, - "Error: partial send of ICMP message\n"); - } -} - - -/** - * Send a UDP message to the dummy IP. - */ -static void -send_udp () -{ - struct sockaddr_in dst; - ssize_t err; - - memset (&dst, 0, sizeof(dst)); - dst.sin_family = AF_INET; -#if HAVE_SOCKADDR_IN_SIN_LEN - dst.sin_len = sizeof(struct sockaddr_in); -#endif - dst.sin_addr = dummy; - dst.sin_port = htons (NAT_TRAV_PORT); - err = sendto (udpsock, - NULL, - 0, - 0, - (struct sockaddr *) &dst, - sizeof(dst)); - if (err < 0) - { -#if VERBOSE - fprintf (stderr, - "sendto failed: %s\n", - strerror (errno)); -#endif - } - else if (0 != err) - { - fprintf (stderr, - "Error: partial send of ICMP message\n"); - } -} - - -/** - * We've received an ICMP response. Process it. - */ -static void -process_icmp_response () -{ - char buf[65536]; - ssize_t have; - struct in_addr source_ip; - struct ip_header ip_pkt; - struct icmp_ttl_exceeded_header icmp_ttl; - struct icmp_echo_header icmp_echo; - struct udp_header udp_pkt; - size_t off; - uint16_t port; - - have = read (icmpsock, buf, sizeof(buf)); - if (-1 == have) - { - fprintf (stderr, - "Error reading raw socket: %s\n", - strerror (errno)); - return; - } -#if VERBOSE - fprintf (stderr, - "Received message of %u bytes\n", - (unsigned int) have); -#endif - if (have < - (ssize_t) (sizeof(struct ip_header) - + sizeof(struct icmp_ttl_exceeded_header) - + sizeof(struct ip_header))) - { - /* malformed */ - return; - } - off = 0; - GNUNET_memcpy (&ip_pkt, - &buf[off], - sizeof(struct ip_header)); - off += sizeof(struct ip_header); - GNUNET_memcpy (&icmp_ttl, - &buf[off], - sizeof(struct icmp_ttl_exceeded_header)); - off += sizeof(struct icmp_ttl_exceeded_header); - if ((ICMP_TIME_EXCEEDED != icmp_ttl.type) || (0 != icmp_ttl.code)) - { - /* different type than what we want */ - return; - } - /* grab source IP of 1st IP header */ - source_ip.s_addr = ip_pkt.src_ip; - - /* skip 2nd IP header */ - GNUNET_memcpy (&ip_pkt, - &buf[off], - sizeof(struct ip_header)); - off += sizeof(struct ip_header); - - switch (ip_pkt.proto) - { - case IPPROTO_ICMP: - if (have != - (sizeof(struct ip_header) * 2 - + sizeof(struct icmp_ttl_exceeded_header) - + sizeof(struct icmp_echo_header))) - { - /* malformed */ - return; - } - /* grab ICMP ECHO content */ - GNUNET_memcpy (&icmp_echo, - &buf[off], - sizeof(struct icmp_echo_header)); - port = (uint16_t) ntohl (icmp_echo.reserved); - break; - - case IPPROTO_UDP: - if (have != - (sizeof(struct ip_header) * 2 - + sizeof(struct icmp_ttl_exceeded_header) + sizeof(struct udp_header))) - { - /* malformed */ - return; - } - /* grab UDP content */ - GNUNET_memcpy (&udp_pkt, - &buf[off], - sizeof(struct udp_header)); - port = ntohs (udp_pkt.length); - break; - - default: - /* different type than what we want */ - return; - } - - if (port == 0) - fprintf (stdout, "%s\n", - inet_ntop (AF_INET, &source_ip, buf, sizeof(buf))); - else - fprintf (stdout, "%s:%u\n", - inet_ntop (AF_INET, &source_ip, buf, sizeof(buf)), - (unsigned int) port); - fflush (stdout); -} - - -/** - * Fully initialize the raw socket. - * - * @return -1 on error, 0 on success - */ -static int -setup_raw_socket () -{ - const int one = 1; - - if (-1 == - setsockopt (rawsock, - SOL_SOCKET, - SO_BROADCAST, - (char *) &one, - sizeof(one))) - { - fprintf (stderr, - "setsockopt failed: %s\n", - strerror (errno)); - return -1; - } - if (-1 == - setsockopt (rawsock, - IPPROTO_IP, - IP_HDRINCL, - (char *) &one, - sizeof(one))) - { - fprintf (stderr, - "setsockopt failed: %s\n", - strerror (errno)); - return -1; - } - return 0; -} - - -/** - * Create a UDP socket for writing. - * - * @param my_ip source address (our ip address) - * @return -1 on error - */ -static int -make_udp_socket (const struct in_addr *my_ip) -{ - int ret; - struct sockaddr_in addr; - - ret = socket (AF_INET, SOCK_DGRAM, 0); - if (-1 == ret) - { - fprintf (stderr, - "Error opening UDP socket: %s\n", - strerror (errno)); - return -1; - } - memset (&addr, 0, sizeof(addr)); - addr.sin_family = AF_INET; -#if HAVE_SOCKADDR_IN_SIN_LEN - addr.sin_len = sizeof(struct sockaddr_in); -#endif - addr.sin_addr = *my_ip; - addr.sin_port = htons (NAT_TRAV_PORT); - - if (0 != bind (ret, - (struct sockaddr *) &addr, - sizeof(addr))) - { - fprintf (stderr, - "Error binding UDP socket to port %u: %s\n", - NAT_TRAV_PORT, - strerror (errno)); - (void) close (ret); - return -1; - } - return ret; -} - - -int -main (int argc, - char *const *argv) -{ - struct in_addr external; - fd_set rs; - struct timeval tv; - uid_t uid; - unsigned int alt; - int icmp_eno; - int raw_eno; - int global_ret; - - /* Create an ICMP raw socket for reading (we'll check errors later) */ - icmpsock = socket (AF_INET, - SOCK_RAW, - IPPROTO_ICMP); - icmp_eno = errno; - - /* Create an (ICMP) raw socket for writing (we'll check errors later) */ - rawsock = socket (AF_INET, - SOCK_RAW, - IPPROTO_RAW); - raw_eno = errno; - udpsock = -1; - - /* drop root rights */ - uid = getuid (); -#ifdef HAVE_SETRESUID - if (0 != setresuid (uid, uid, uid)) - { - fprintf (stderr, - "Failed to setresuid: %s\n", - strerror (errno)); - global_ret = 1; - goto error_exit; - } -#else - if (0 != (setuid (uid) | seteuid (uid))) - { - fprintf (stderr, - "Failed to setuid: %s\n", - strerror (errno)); - global_ret = 2; - goto error_exit; - } -#endif - - /* Now that we run without root rights, we can do error checking... */ - if (2 != argc) - { - fprintf (stderr, - "This program must be started with our (internal NAT) IP as the only argument.\n"); - global_ret = 3; - goto error_exit; - } - if (1 != inet_pton (AF_INET, argv[1], &external)) - { - fprintf (stderr, - "Error parsing IPv4 address: %s\n", - strerror (errno)); - global_ret = 4; - goto error_exit; - } - if (1 != inet_pton (AF_INET, DUMMY_IP, &dummy)) - { - fprintf (stderr, - "Internal error converting dummy IP to binary.\n"); - global_ret = 5; - goto error_exit; - } - - /* error checking icmpsock */ - if (-1 == icmpsock) - { - fprintf (stderr, - "Error opening RAW socket: %s\n", - strerror (icmp_eno)); - global_ret = 6; - goto error_exit; - } - if (icmpsock >= FD_SETSIZE) - { - /* this could happen if we were started with a large number of already-open - file descriptors... */ - fprintf (stderr, - "Socket number too large (%d > %u)\n", - icmpsock, - (unsigned int) FD_SETSIZE); - global_ret = 7; - goto error_exit; - } - - /* error checking rawsock */ - if (-1 == rawsock) - { - fprintf (stderr, - "Error opening RAW socket: %s\n", - strerror (raw_eno)); - global_ret = 8; - goto error_exit; - } - /* no need to check 'rawsock' against FD_SETSIZE as it is never used - with 'select' */ - - if (0 != setup_raw_socket ()) - { - global_ret = 9; - goto error_exit; - } - - if (-1 == (udpsock = make_udp_socket (&external))) - { - global_ret = 10; - goto error_exit; - } - - alt = 0; - while (1) - { - FD_ZERO (&rs); - FD_SET (icmpsock, &rs); - tv.tv_sec = 0; - tv.tv_usec = ICMP_SEND_FREQUENCY_MS * 1000; - if (-1 == select (icmpsock + 1, &rs, NULL, NULL, &tv)) - { - if (errno == EINTR) - continue; - fprintf (stderr, - "select failed: %s\n", - strerror (errno)); - break; - } - if (1 == getppid ()) /* Check the parent process id, if 1 the parent has died, so we should die too */ - break; - if (FD_ISSET (icmpsock, &rs)) - { - process_icmp_response (); - continue; - } - if (0 == (++alt % 2)) - send_icmp_echo (&external); - else - send_udp (); - } - - /* select failed (internal error or OS out of resources) */ - global_ret = 11; -error_exit: - if (-1 != icmpsock) - (void) close (icmpsock); - if (-1 != rawsock) - (void) close (rawsock); - if (-1 != udpsock) - (void) close (udpsock); - return global_ret; -} - - -/* end of gnunet-helper-nat-server.c */ diff --git a/src/nat/gnunet-nat-client-script.sh b/src/nat/gnunet-nat-client-script.sh deleted file mode 100755 index 4e4ccafad..000000000 --- a/src/nat/gnunet-nat-client-script.sh +++ /dev/null @@ -1,4 +0,0 @@ -#!/bin/sh -IP=`ifconfig | grep inet | head -n1 | awk '{print $2}' | sed -e "s/addr://"` -echo "Using IP $IP, trying to connect to $1" -./gnunet-nat-client-udp $IP $1 diff --git a/src/nat/gnunet-nat-server-script.sh b/src/nat/gnunet-nat-server-script.sh deleted file mode 100755 index 42a8e639a..000000000 --- a/src/nat/gnunet-nat-server-script.sh +++ /dev/null @@ -1,4 +0,0 @@ -#!/bin/sh -IP=`ifconfig | grep inet | head -n1 | awk '{print $2}' | sed -e "s/addr://"` -echo "Using IP $IP" -./gnunet-nat-server $IP | sed -u -e "s/.*/.\/gnunet-nat-server-udp $IP &\&/" | sh diff --git a/src/nat/gnunet-nat.c b/src/nat/gnunet-nat.c deleted file mode 100644 index fd85549d6..000000000 --- a/src/nat/gnunet-nat.c +++ /dev/null @@ -1,476 +0,0 @@ -/* - This file is part of GNUnet. - Copyright (C) 2015, 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 . - - SPDX-License-Identifier: AGPL3.0-or-later - */ - -/** - * @file src/nat/gnunet-nat.c - * @brief Command-line tool to interact with the NAT service - * @author Christian Grothoff - * @author Bruno Cabral - */ -#include "platform.h" -#include "gnunet_util_lib.h" -#include "gnunet_nat_service.h" - -/** - * Value to return from #main(). - */ -static int global_ret; - -/** - * Name of section in configuration file to use for - * additional options. - */ -static char *section_name; - -/** - * Flag set to 1 if we use IPPROTO_UDP. - */ -static int use_udp; - -/** - * Flag set to 1 if we are to listen for connection reversal requests. - */ -static int listen_reversal; - -/** - * Flag set to 1 if we use IPPROTO_TCP. - */ -static int use_tcp; - -/** - * Protocol to use. - */ -static uint8_t proto; - -/** - * Local address to use for connection reversal request. - */ -static char *local_addr; - -/** - * Remote address to use for connection reversal request. - */ -static char *remote_addr; - -/** - * Should we actually bind to #bind_addr and receive and process STUN requests? - */ -static int do_stun; - -/** - * Handle to NAT operation. - */ -static struct GNUNET_NAT_Handle *nh; - -/** - * Listen socket for STUN processing. - */ -static struct GNUNET_NETWORK_Handle *ls; - -/** - * Task for reading STUN packets. - */ -static struct GNUNET_SCHEDULER_Task *rtask; - - -/** - * Test if all activities have finished, and if so, - * terminate. - */ -static void -test_finished () -{ - if (NULL != nh) - return; - if (NULL != rtask) - return; - GNUNET_SCHEDULER_shutdown (); -} - - -/** - * Signature of the callback passed to #GNUNET_NAT_register() for - * a function to call whenever our set of 'valid' addresses changes. - * - * @param cls closure, NULL - * @param[in,out] app_ctx location where the app can store stuff - * on add and retrieve it on remove - * @param add_remove #GNUNET_YES to add a new public IP address, - * #GNUNET_NO to remove a previous (now invalid) one - * @param ac address class the address belongs to - * @param addr either the previous or the new public IP address - * @param addrlen actual length of the @a addr - */ -static void -address_cb (void *cls, - void **app_ctx, - int add_remove, - enum GNUNET_NAT_AddressClass ac, - const struct sockaddr *addr, - socklen_t addrlen) -{ - (void) cls; - (void) app_ctx; - - fprintf (stdout, - "%s %s (%d)\n", - add_remove ? "+" : "-", - GNUNET_a2s (addr, addrlen), - (int) ac); -} - - -/** - * Signature of the callback passed to #GNUNET_NAT_register(). - * for a function to call whenever someone asks us to do connection - * reversal. - * - * @param cls closure, NULL - * @param remote_addr public IP address of the other peer - * @param remote_addrlen actual length of the @a remote_addr - */ -static void -reversal_cb (void *cls, - const struct sockaddr *remote_addr, - socklen_t remote_addrlen) -{ - GNUNET_log (GNUNET_ERROR_TYPE_MESSAGE, - "Connection reversal requested by %s\n", - GNUNET_a2s (remote_addr, remote_addrlen)); -} - - -/** - * Task run on shutdown. - * - * @param cls NULL - */ -static void -do_shutdown (void *cls) -{ - if (NULL != nh) - { - GNUNET_NAT_unregister (nh); - nh = NULL; - } - if (NULL != ls) - { - GNUNET_NETWORK_socket_close (ls); - ls = NULL; - } - if (NULL != rtask) - { - GNUNET_SCHEDULER_cancel (rtask); - rtask = NULL; - } -} - - -/** - * Task to receive incoming packets for STUN processing. - */ -static void -stun_read_task (void *cls) -{ - ssize_t size; - - rtask = GNUNET_SCHEDULER_add_read_net (GNUNET_TIME_UNIT_FOREVER_REL, - ls, - &stun_read_task, - NULL); - size = GNUNET_NETWORK_socket_recvfrom_amount (ls); - if (size > 0) - { - GNUNET_break (0); - GNUNET_SCHEDULER_shutdown (); - global_ret = 1; - return; - } - { - char buf[size + 1]; - struct sockaddr_storage sa; - socklen_t salen = sizeof(sa); - ssize_t ret; - - ret = GNUNET_NETWORK_socket_recvfrom (ls, - buf, - size + 1, - (struct sockaddr *) &sa, - &salen); - if (ret != size) - { - GNUNET_break (0); - GNUNET_SCHEDULER_shutdown (); - global_ret = 1; - return; - } - (void) GNUNET_NAT_stun_handle_packet (nh, - (const struct sockaddr *) &sa, - salen, - buf, - ret); - } -} - - -/** - * Main function that will be run. - * - * @param cls closure - * @param args remaining command-line arguments - * @param cfgfile name of the configuration file used (for saving, can be NULL!) - * @param c configuration - */ -static void -run (void *cls, - char *const *args, - const char *cfgfile, - const struct GNUNET_CONFIGURATION_Handle *c) -{ - uint8_t af; - struct sockaddr *local_sa; - struct sockaddr *remote_sa; - socklen_t local_len; - size_t remote_len; - - if (use_tcp && use_udp) - { - GNUNET_log (GNUNET_ERROR_TYPE_MESSAGE, "Cannot use TCP and UDP\n"); - global_ret = 1; - return; - } - proto = 0; - if (use_tcp) - proto = IPPROTO_TCP; - if (use_udp) - proto = IPPROTO_UDP; - - GNUNET_SCHEDULER_add_shutdown (&do_shutdown, NULL); - - if (0 == proto) - { - GNUNET_log (GNUNET_ERROR_TYPE_MESSAGE, "Must specify either TCP or UDP\n"); - global_ret = 1; - return; - } - local_len = 0; - local_sa = NULL; - remote_len = 0; - remote_sa = NULL; - if (NULL != local_addr) - { - local_len = - (socklen_t) GNUNET_STRINGS_parse_socket_addr (local_addr, &af, &local_sa); - if (0 == local_len) - { - GNUNET_log (GNUNET_ERROR_TYPE_MESSAGE, - "Invalid socket address `%s'\n", - local_addr); - goto fail_and_shutdown; - } - } - - if (NULL != remote_addr) - { - remote_len = - GNUNET_STRINGS_parse_socket_addr (remote_addr, &af, &remote_sa); - if (0 == remote_len) - { - GNUNET_log (GNUNET_ERROR_TYPE_MESSAGE, - "Invalid socket address `%s'\n", - remote_addr); - goto fail_and_shutdown; - } - } - - if (NULL != local_addr) - { - if (NULL == section_name) - section_name = GNUNET_strdup ("undefined"); - nh = GNUNET_NAT_register (c, - section_name, - proto, - 1, - (const struct sockaddr **) &local_sa, - &local_len, - &address_cb, - (listen_reversal) ? &reversal_cb : NULL, - NULL); - } - else if (listen_reversal) - { - GNUNET_log (GNUNET_ERROR_TYPE_MESSAGE, - "Use of `-W` only effective in combination with `-i`\n"); - goto fail_and_shutdown; - } - - if (NULL != remote_addr) - { - int ret; - - if ((NULL == nh) || (sizeof(struct sockaddr_in) != local_len)) - { - GNUNET_log (GNUNET_ERROR_TYPE_MESSAGE, - "Require IPv4 local address to initiate connection reversal\n"); - goto fail_and_shutdown; - } - if (sizeof(struct sockaddr_in) != remote_len) - { - GNUNET_log (GNUNET_ERROR_TYPE_MESSAGE, - "Require IPv4 reversal target address\n"); - goto fail_and_shutdown; - } - GNUNET_assert (AF_INET == local_sa->sa_family); - GNUNET_assert (AF_INET == remote_sa->sa_family); - ret = GNUNET_NAT_request_reversal (nh, - (const struct sockaddr_in *) local_sa, - (const struct sockaddr_in *) remote_sa); - switch (ret) - { - case GNUNET_SYSERR: - GNUNET_log (GNUNET_ERROR_TYPE_MESSAGE, - "Connection reversal internal error\n"); - break; - - case GNUNET_NO: - GNUNET_log (GNUNET_ERROR_TYPE_MESSAGE, - "Connection reversal unavailable\n"); - break; - - case GNUNET_OK: - /* operation in progress */ - break; - } - } - - if (do_stun) - { - if (NULL == local_addr) - { - GNUNET_log (GNUNET_ERROR_TYPE_MESSAGE, - "Require local address to support STUN requests\n"); - goto fail_and_shutdown; - } - if (IPPROTO_UDP != proto) - { - GNUNET_log (GNUNET_ERROR_TYPE_MESSAGE, "STUN only supported over UDP\n"); - goto fail_and_shutdown; - } - ls = GNUNET_NETWORK_socket_create (af, SOCK_DGRAM, IPPROTO_UDP); - if (NULL == ls) - { - GNUNET_log (GNUNET_ERROR_TYPE_MESSAGE, "Failed to create socket\n"); - goto fail_and_shutdown; - } - if (GNUNET_OK != GNUNET_NETWORK_socket_bind (ls, local_sa, local_len)) - { - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - "Failed to bind to %s: %s\n", - GNUNET_a2s (local_sa, local_len), - strerror (errno)); - goto fail_and_shutdown; - } - rtask = GNUNET_SCHEDULER_add_read_net (GNUNET_TIME_UNIT_FOREVER_REL, - ls, - &stun_read_task, - NULL); - } - GNUNET_free (remote_sa); - GNUNET_free (local_sa); - test_finished (); - return; -fail_and_shutdown: - global_ret = 1; - GNUNET_SCHEDULER_shutdown (); - GNUNET_free (remote_sa); - GNUNET_free (local_sa); -} - - -/** - * Main function of gnunet-nat - * - * @param argc number of command-line arguments - * @param argv command line - * @return 0 on success, -1 on error - */ -int -main (int argc, char *const argv[]) -{ - struct GNUNET_GETOPT_CommandLineOption options[] = { - GNUNET_GETOPT_option_string ( - 'i', - "in", - "ADDRESS", - gettext_noop ("which IP and port are we locally using to bind/listen to"), - &local_addr), - - GNUNET_GETOPT_option_string ( - 'r', - "remote", - "ADDRESS", - gettext_noop ( - "which remote IP and port should be asked for connection reversal"), - &remote_addr), - - GNUNET_GETOPT_option_string ( - 'S', - "section", - NULL, - gettext_noop ( - "name of configuration section to find additional options, such as manual host punching data"), - §ion_name), - - GNUNET_GETOPT_option_flag ('s', - "stun", - gettext_noop ("enable STUN processing"), - &do_stun), - - GNUNET_GETOPT_option_flag ('t', "tcp", gettext_noop ("use TCP"), &use_tcp), - - GNUNET_GETOPT_option_flag ('u', "udp", gettext_noop ("use UDP"), &use_udp), - - GNUNET_GETOPT_option_flag ('W', - "watch", - gettext_noop ( - "watch for connection reversal requests"), - &listen_reversal), - GNUNET_GETOPT_OPTION_END - }; - - if (GNUNET_OK != GNUNET_STRINGS_get_utf8_args (argc, argv, &argc, &argv)) - return 2; - if (GNUNET_OK != - GNUNET_PROGRAM_run (argc, - argv, - "gnunet-nat [options]", - _ ("GNUnet NAT traversal autoconfigure daemon"), - options, - &run, - NULL)) - { - global_ret = 1; - } - GNUNET_free_nz ((void *) argv); - return global_ret; -} - - -/* end of gnunet-nat.c */ diff --git a/src/nat/gnunet-service-nat.c b/src/nat/gnunet-service-nat.c deleted file mode 100644 index dde269819..000000000 --- a/src/nat/gnunet-service-nat.c +++ /dev/null @@ -1,2083 +0,0 @@ -/* - This file is part of GNUnet. - Copyright (C) 2016, 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 . - - SPDX-License-Identifier: AGPL3.0-or-later - */ - -/** - * @file nat/gnunet-service-nat.c - * @brief network address translation traversal service - * @author Christian Grothoff - * - * The purpose of this service is to enable transports to - * traverse NAT routers, by providing traversal options and - * knowledge about the local network topology. - * - * TODO: - * - migrate test cases to new NAT service - * - add new traceroute-based logic for external IP detection - * - * - implement & test STUN processing to classify NAT; - * basically, open port & try different methods. - */ -#include "platform.h" -#include -#include "gnunet_util_lib.h" -#include "gnunet_protocols.h" -#include "gnunet_signatures.h" -#include "gnunet_statistics_service.h" -#include "gnunet_resolver_service.h" -#include "gnunet_nat_service.h" -#include "gnunet-service-nat.h" -#include "gnunet-service-nat_externalip.h" -#include "gnunet-service-nat_stun.h" -#include "gnunet-service-nat_mini.h" -#include "gnunet-service-nat_helper.h" -#include "nat.h" -#include - - -/** - * How often should we ask the OS about a list of active - * network interfaces? - */ -#define SCAN_FREQ GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS, 15) - -/** - * How long do we wait until we forcefully terminate autoconfiguration? - */ -#define AUTOCONFIG_TIMEOUT GNUNET_TIME_relative_multiply ( \ - GNUNET_TIME_UNIT_SECONDS, 5) - -/** - * How often do we scan for changes in how our external (dyndns) hostname resolves? - */ -#define DYNDNS_FREQUENCY GNUNET_TIME_relative_multiply ( \ - GNUNET_TIME_UNIT_MINUTES, 7) - - -/** - * Information we track per client address. - */ -struct ClientAddress -{ - /** - * Network address used by the client. - */ - struct sockaddr_storage ss; - - /** - * Handle to active UPnP request where we asked upnpc to open - * a port at the NAT. NULL if we do not have such a request - * pending. - */ - struct GNUNET_NAT_MiniHandle *mh; -}; - - -/** - * List of local addresses this system has. - */ -struct LocalAddressList -{ - /** - * This is a linked list. - */ - struct LocalAddressList *next; - - /** - * Previous entry. - */ - struct LocalAddressList *prev; - - /** - * Context for a gnunet-helper-nat-server used to listen - * for ICMP messages to this client for connection reversal. - */ - struct HelperContext *hc; - - /** - * The address itself (i.e. `struct sockaddr_in` or `struct - * sockaddr_in6`, in the respective byte order). - */ - struct sockaddr_storage addr; - - /** - * Address family. (FIXME: redundant, addr.ss_family! Remove!?) - */ - int af; - - /** - * #GNUNET_YES if we saw this one in the previous iteration, - * but not in the current iteration and thus might need to - * remove it at the end. - */ - int old; - - /** - * What type of address is this? - */ - enum GNUNET_NAT_AddressClass ac; -}; - - -/** - * Internal data structure we track for each of our clients. - */ -struct ClientHandle -{ - /** - * Kept in a DLL. - */ - struct ClientHandle *next; - - /** - * Kept in a DLL. - */ - struct ClientHandle *prev; - - /** - * Underlying handle for this client with the service. - */ - struct GNUNET_SERVICE_Client *client; - - /** - * Message queue for communicating with the client. - */ - struct GNUNET_MQ_Handle *mq; - - /** - * Array of addresses used by the service. - */ - struct ClientAddress *caddrs; - - /** - * External DNS name and port given by user due to manual - * hole punching. Special DNS name 'AUTO' is used to indicate - * desire for automatic determination of the external IP - * (instead of DNS or manual configuration, i.e. to be used - * if the IP keeps changing and we have no DynDNS, but we do - * have a hole punched). - */ - char *hole_external; - - /** - * Name of the configuration section this client cares about. - */ - char *section_name; - - /** - * Task for periodically re-running the @e ext_dns DNS lookup. - */ - struct GNUNET_SCHEDULER_Task *ext_dns_task; - - /** - * Handle for (DYN)DNS lookup of our external IP as given in - * @e hole_external. - */ - struct GNUNET_RESOLVER_RequestHandle *ext_dns; - - /** - * Handle for monitoring external IP changes. - */ - struct GN_ExternalIPMonitor *external_monitor; - - /** - * DLL of external IP addresses as given in @e hole_external. - */ - struct LocalAddressList *ext_addr_head; - - /** - * DLL of external IP addresses as given in @e hole_external. - */ - struct LocalAddressList *ext_addr_tail; - - /** - * Port number we found in @e hole_external. - */ - uint16_t ext_dns_port; - - /** - * What does this client care about? - */ - enum GNUNET_NAT_RegisterFlags flags; - - /** - * Is any of the @e caddrs in a reserved subnet for NAT? - */ - int natted_address; - - /** - * Number of addresses that this service is bound to. - * Length of the @e caddrs array. - */ - uint16_t num_caddrs; - - /** - * Client's IPPROTO, e.g. IPPROTO_UDP or IPPROTO_TCP. - */ - uint8_t proto; -}; - - -/** - * External IP address as given to us via some STUN server. - */ -struct StunExternalIP -{ - /** - * Kept in a DLL. - */ - struct StunExternalIP *next; - - /** - * Kept in a DLL. - */ - struct StunExternalIP *prev; - - /** - * Task we run to remove this entry when it is stale. - */ - struct GNUNET_SCHEDULER_Task *timeout_task; - - /** - * Our external IP address as reported by the - * STUN server. - */ - struct sockaddr_in external_addr; - - /** - * Address of the reporting STUN server. Used to - * detect when a STUN server changes its opinion - * to more quickly remove stale results. - */ - struct sockaddr_storage stun_server_addr; - - /** - * Number of bytes used in @e stun_server_addr. - */ - size_t stun_server_addr_len; -}; - - -/** - * Timeout to use when STUN data is considered stale. - */ -static struct GNUNET_TIME_Relative stun_stale_timeout; - -/** - * How often do we scan for changes in how our external (dyndns) hostname resolves? - */ -static struct GNUNET_TIME_Relative dyndns_frequency; - -/** - * Handle to our current configuration. - */ -static const struct GNUNET_CONFIGURATION_Handle *cfg; - -/** - * Handle to the statistics service. - */ -static struct GNUNET_STATISTICS_Handle *stats; - -/** - * Task scheduled to periodically scan our network interfaces. - */ -static struct GNUNET_SCHEDULER_Task *scan_task; - -/** - * Head of client DLL. - */ -static struct ClientHandle *ch_head; - -/** - * Tail of client DLL. - */ -static struct ClientHandle *ch_tail; - -/** - * Head of DLL of local addresses. - */ -static struct LocalAddressList *lal_head; - -/** - * Tail of DLL of local addresses. - */ -static struct LocalAddressList *lal_tail; - -/** - * Kept in a DLL. - */ -static struct StunExternalIP *se_head; - -/** - * Kept in a DLL. - */ -static struct StunExternalIP *se_tail; - -/** - * Is UPnP enabled? #GNUNET_YES if enabled, #GNUNET_NO if disabled, - * #GNUNET_SYSERR if configuration enabled but binary is unavailable. - */ -int enable_upnp; - -/** - * Is IP Scanning enabled? #GNUNET_YES if enabled, #GNUNET_NO if disabled, - * without, only explicitly specified IPs will be handled (HOLE_EXTERNAL) - */ -int enable_ipscan; - -/** - * Remove and free an entry from the #lal_head DLL. - * - * @param lal entry to free - */ -static void -free_lal (struct LocalAddressList *lal) -{ - GNUNET_CONTAINER_DLL_remove (lal_head, - lal_tail, - lal); - if (NULL != lal->hc) - { - GNUNET_log (GNUNET_ERROR_TYPE_MESSAGE, - "Lost NATed local address %s, stopping NAT server\n", - GNUNET_a2s ((const struct sockaddr *) &lal->addr, - sizeof(struct sockaddr_in))); - - GN_stop_gnunet_nat_server_ (lal->hc); - lal->hc = NULL; - } - GNUNET_free (lal); -} - - -/** - * Free the DLL starting at #lal_head. - */ -static void -destroy_lal () -{ - struct LocalAddressList *lal; - - while (NULL != (lal = lal_head)) - free_lal (lal); -} - - -/** - * Check validity of #GNUNET_MESSAGE_TYPE_NAT_REGISTER message from - * client. - * - * @param cls client who sent the message - * @param message the message received - * @return #GNUNET_OK if message is well-formed - */ -static int -check_register (void *cls, - const struct GNUNET_NAT_RegisterMessage *message) -{ - uint16_t num_addrs = ntohs (message->num_addrs); - const char *off = (const char *) &message[1]; - size_t left = ntohs (message->header.size) - sizeof(*message); - - for (unsigned int i = 0; i < num_addrs; i++) - { - size_t alen; - const struct sockaddr *sa = (const struct sockaddr *) off; - - if (sizeof(sa_family_t) > left) - { - GNUNET_break (0); - return GNUNET_SYSERR; - } - switch (sa->sa_family) - { - case AF_INET: - alen = sizeof(struct sockaddr_in); - break; - - case AF_INET6: - alen = sizeof(struct sockaddr_in6); - break; - -#if AF_UNIX - case AF_UNIX: - alen = sizeof(struct sockaddr_un); - break; -#endif - default: - GNUNET_break (0); - return GNUNET_SYSERR; - } - if (alen > left) - { - GNUNET_break (0); - return GNUNET_SYSERR; - } - off += alen; - left -= alen; - } - if (left != ntohs (message->str_len)) - { - GNUNET_break (0); - return GNUNET_SYSERR; - } - return GNUNET_OK; -} - - -/** - * Check if @a ip is in @a network with @a bits netmask. - * - * @param network to test - * @param ip IP address to test - * @param bits bitmask for the network - * @return #GNUNET_YES if @a ip is in @a network - */ -static int -match_ipv4 (const char *network, - const struct in_addr *ip, - uint8_t bits) -{ - struct in_addr net; - - if (0 == ip->s_addr) - return GNUNET_YES; - if (0 == bits) - return GNUNET_YES; - GNUNET_assert (1 == inet_pton (AF_INET, - network, - &net)); - return ! ((ip->s_addr ^ net.s_addr) & htonl (0xFFFFFFFFu << (32 - bits))); -} - - -/** - * Check if @a ip is in @a network with @a bits netmask. - * - * @param network to test - * @param ip IP address to test - * @param bits bitmask for the network - * @return #GNUNET_YES if @a ip is in @a network - */ -static int -match_ipv6 (const char *network, - const struct in6_addr *ip, - uint8_t bits) -{ - struct in6_addr net; - struct in6_addr mask; - unsigned int off; - - if (0 == bits) - return GNUNET_YES; - GNUNET_assert (1 == inet_pton (AF_INET6, - network, - &net)); - memset (&mask, 0, sizeof(mask)); - if (0 == GNUNET_memcmp (&mask, - ip)) - return GNUNET_YES; - off = 0; - while (bits > 8) - { - mask.s6_addr[off++] = 0xFF; - bits -= 8; - } - while (bits > 0) - { - mask.s6_addr[off] = (mask.s6_addr[off] >> 1) + 0x80; - bits--; - } - for (unsigned j = 0; j < sizeof(struct in6_addr) / sizeof(uint32_t); j++) - if (((((uint32_t *) ip)[j] & ((uint32_t *) &mask)[j])) != - (((uint32_t *) &net)[j] & ((int *) &mask)[j])) - return GNUNET_NO; - return GNUNET_YES; -} - - -/** - * Test if the given IPv4 address is in a known range - * for private networks. - * - * @param ip address to test - * @return #GNUNET_YES if @a ip is in a NAT range - */ -static int -is_nat_v4 (const struct in_addr *ip) -{ - return - match_ipv4 ("10.0.0.0", ip, 8) || /* RFC 1918 */ - match_ipv4 ("100.64.0.0", ip, 10) || /* CG-NAT, RFC 6598 */ - match_ipv4 ("192.168.0.0", ip, 12) || /* RFC 1918 */ - match_ipv4 ("169.254.0.0", ip, 16) || /* AUTO, RFC 3927 */ - match_ipv4 ("172.16.0.0", ip, 16); /* RFC 1918 */ -} - - -/** - * Test if the given IPv6 address is in a known range - * for private networks. - * - * @param ip address to test - * @return #GNUNET_YES if @a ip is in a NAT range - */ -static int -is_nat_v6 (const struct in6_addr *ip) -{ - return - match_ipv6 ("fc00::", ip, 7) || /* RFC 4193 */ - match_ipv6 ("fec0::", ip, 10) || /* RFC 3879 */ - match_ipv6 ("fe80::", ip, 10); /* RFC 4291, link-local */ -} - - -/** - * Closure for #ifc_proc. - */ -struct IfcProcContext -{ - /** - * Head of DLL of local addresses. - */ - struct LocalAddressList *lal_head; - - /** - * Tail of DLL of local addresses. - */ - struct LocalAddressList *lal_tail; -}; - - -/** - * Callback function invoked for each interface found. Adds them - * to our new address list. - * - * @param cls a `struct IfcProcContext *` - * @param name name of the interface (can be NULL for unknown) - * @param isDefault is this presumably the default interface - * @param addr address of this interface (can be NULL for unknown or unassigned) - * @param broadcast_addr the broadcast address (can be NULL for unknown or unassigned) - * @param netmask the network mask (can be NULL for unknown or unassigned) - * @param addrlen length of the address - * @return #GNUNET_OK to continue iteration, #GNUNET_SYSERR to abort - */ -static int -ifc_proc (void *cls, - const char *name, - int isDefault, - const struct sockaddr *addr, - const struct sockaddr *broadcast_addr, - const struct sockaddr *netmask, - socklen_t addrlen) -{ - struct IfcProcContext *ifc_ctx = cls; - struct LocalAddressList *lal; - size_t alen; - const struct in_addr *ip4; - const struct in6_addr *ip6; - enum GNUNET_NAT_AddressClass ac; - - switch (addr->sa_family) - { - case AF_INET: - alen = sizeof(struct sockaddr_in); - ip4 = &((const struct sockaddr_in *) addr)->sin_addr; - if (match_ipv4 ("127.0.0.0", ip4, 8)) - ac = GNUNET_NAT_AC_LOOPBACK; - else if (is_nat_v4 (ip4)) - ac = GNUNET_NAT_AC_LAN; - else - ac = GNUNET_NAT_AC_GLOBAL; - break; - - case AF_INET6: - alen = sizeof(struct sockaddr_in6); - ip6 = &((const struct sockaddr_in6 *) addr)->sin6_addr; - if (match_ipv6 ("::1", ip6, 128)) - ac = GNUNET_NAT_AC_LOOPBACK; - else if (is_nat_v6 (ip6)) - ac = GNUNET_NAT_AC_LAN; - else - ac = GNUNET_NAT_AC_GLOBAL; - if ((ip6->s6_addr[11] == 0xFF) && - (ip6->s6_addr[12] == 0xFE)) - { - /* contains a MAC, be extra careful! */ - ac |= GNUNET_NAT_AC_PRIVATE; - } - break; - -#if AF_UNIX - case AF_UNIX: - GNUNET_break (0); - return GNUNET_OK; -#endif - default: - GNUNET_break (0); - return GNUNET_OK; - } - lal = GNUNET_malloc (sizeof(*lal)); - lal->af = addr->sa_family; - lal->ac = ac; - GNUNET_memcpy (&lal->addr, - addr, - alen); - GNUNET_CONTAINER_DLL_insert (ifc_ctx->lal_head, - ifc_ctx->lal_tail, - lal); - return GNUNET_OK; -} - - -/** - * Notify client about a change in the list of addresses this peer - * has. - * - * @param ac address class of the entry in the list that changed - * @param ch client to contact - * @param add #GNUNET_YES to add, #GNUNET_NO to remove - * @param addr the address that changed - * @param addr_len number of bytes in @a addr - */ -static void -notify_client (enum GNUNET_NAT_AddressClass ac, - struct ClientHandle *ch, - int add, - const void *addr, - size_t addr_len) -{ - struct GNUNET_MQ_Envelope *env; - struct GNUNET_NAT_AddressChangeNotificationMessage *msg; - - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "Notifying client about %s of IP %s\n", - add ? "addition" : "removal", - GNUNET_a2s (addr, - addr_len)); - env = GNUNET_MQ_msg_extra (msg, - addr_len, - GNUNET_MESSAGE_TYPE_NAT_ADDRESS_CHANGE); - msg->add_remove = htonl (add); - msg->addr_class = htonl (ac); - GNUNET_memcpy (&msg[1], - addr, - addr_len); - GNUNET_MQ_send (ch->mq, - env); -} - - -/** - * Check if we should bother to notify this client about this - * address change, and if so, do it. - * - * @param delta the entry in the list that changed - * @param ch client to check - * @param add #GNUNET_YES to add, #GNUNET_NO to remove - */ -static void -check_notify_client (struct LocalAddressList *delta, - struct ClientHandle *ch, - int add) -{ - size_t alen; - struct sockaddr_in v4; - struct sockaddr_in6 v6; - - if (0 == (ch->flags & GNUNET_NAT_RF_ADDRESSES)) - { - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "Not notifying client as it does not care about addresses\n"); - return; - } - switch (delta->af) - { - case AF_INET: - alen = sizeof(struct sockaddr_in); - GNUNET_memcpy (&v4, - &delta->addr, - alen); - - /* Check for client notifications */ - for (unsigned int i = 0; i < ch->num_caddrs; i++) - { - const struct sockaddr_in *c4; - - if (AF_INET != ch->caddrs[i].ss.ss_family) - continue; /* IPv4 not relevant */ - c4 = (const struct sockaddr_in *) &ch->caddrs[i].ss; - if (match_ipv4 ("127.0.0.1", &c4->sin_addr, 8) && - (0 != c4->sin_addr.s_addr) && - (! match_ipv4 ("127.0.0.1", &v4.sin_addr, 8))) - continue; /* bound to loopback, but this is not loopback */ - if ((! match_ipv4 ("127.0.0.1", &c4->sin_addr, 8)) && - match_ipv4 ("127.0.0.1", &v4.sin_addr, 8)) - continue; /* bound to non-loopback, but this is loopback */ - if ((0 != (delta->ac & GNUNET_NAT_AC_EXTERN)) && - (0 != c4->sin_addr.s_addr) && - (! is_nat_v4 (&v4.sin_addr))) - continue; /* based on external-IP, but this IP is not - from private address range. */ - if ((0 != GNUNET_memcmp (&v4.sin_addr, - &c4->sin_addr)) && - (0 != c4->sin_addr.s_addr) && - (! is_nat_v4 (&c4->sin_addr))) - continue; /* this IP is not from private address range, - and IP does not match. */ - - /* OK, IP seems relevant, notify client */ - if (0 == htons (v4.sin_port)) - v4.sin_port = c4->sin_port; - notify_client (delta->ac, - ch, - add, - &v4, - alen); - } - break; - - case AF_INET6: - alen = sizeof(struct sockaddr_in6); - GNUNET_memcpy (&v6, - &delta->addr, - alen); - for (unsigned int i = 0; i < ch->num_caddrs; i++) - { - const struct sockaddr_in6 *c6; - - if (AF_INET6 != ch->caddrs[i].ss.ss_family) - continue; /* IPv4 not relevant */ - c6 = (const struct sockaddr_in6 *) &ch->caddrs[i].ss; - if (match_ipv6 ("::1", &c6->sin6_addr, 128) && - (0 != GNUNET_memcmp (&c6->sin6_addr, - &in6addr_any)) && - (! match_ipv6 ("::1", &v6.sin6_addr, 128))) - continue; /* bound to loopback, but this is not loopback */ - if ((! match_ipv6 ("::1", &c6->sin6_addr, 128)) && - match_ipv6 ("::1", &v6.sin6_addr, 128)) - continue; /* bound to non-loopback, but this is loopback */ - if ((0 != (delta->ac & GNUNET_NAT_AC_EXTERN)) && - (0 != GNUNET_memcmp (&c6->sin6_addr, - &in6addr_any)) && - (! is_nat_v6 (&v6.sin6_addr))) - continue; /* based on external-IP, but this IP is not - from private address range. */ - if ((0 != GNUNET_memcmp (&v6.sin6_addr, - &c6->sin6_addr)) && - (0 != GNUNET_memcmp (&c6->sin6_addr, - &in6addr_any)) && - (! is_nat_v6 (&c6->sin6_addr))) - continue; /* this IP is not from private address range, - and IP does not match. */ - if ((match_ipv6 ("fe80::", &c6->sin6_addr, 10)) && - (0 != GNUNET_memcmp (&c6->sin6_addr, - &in6addr_any)) && - (0 != GNUNET_memcmp (&v6.sin6_addr, - &c6->sin6_addr)) && - (0 == (delta->ac & GNUNET_NAT_AC_EXTERN))) - continue; /* client bound to link-local, and the other address - does not match and is not an external IP */ - - /* OK, IP seems relevant, notify client */ - if (0 == htons (v6.sin6_port)) - v6.sin6_port = c6->sin6_port; - notify_client (delta->ac, - ch, - add, - &v6, - alen); - } - break; - - default: - GNUNET_break (0); - return; - } -} - - -/** - * Notify all clients about a change in the list - * of addresses this peer has. - * - * @param delta the entry in the list that changed - * @param add #GNUNET_YES to add, #GNUNET_NO to remove - */ -static void -notify_clients (struct LocalAddressList *delta, - int add) -{ - for (struct ClientHandle *ch = ch_head; - NULL != ch; - ch = ch->next) - check_notify_client (delta, - ch, - add); -} - - -/** - * Tell relevant client about a change in our external - * IPv4 address. - * - * @param cls client to check if it cares and possibly notify - * @param v4 the external address that changed - * @param add #GNUNET_YES to add, #GNUNET_NO to remove - */ -static void -notify_client_external_ipv4_change (void *cls, - const struct in_addr *v4, - int add) -{ - struct ClientHandle *ch = cls; - struct sockaddr_in sa; - int have_v4; - - /* (0) check if this impacts 'hole_external' */ - if ((NULL != ch->hole_external) && - (0 == strcasecmp (ch->hole_external, - "AUTO"))) - { - struct LocalAddressList lal; - struct sockaddr_in *s4; - - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "Detected eternal IP, can now back-fill AUTO:%u in hole punching specification of `%s'\n", - (unsigned int) ch->ext_dns_port, - ch->section_name); - memset (&lal, 0, sizeof(lal)); - s4 = (struct sockaddr_in *) &lal.addr; - s4->sin_family = AF_INET; - s4->sin_port = htons (ch->ext_dns_port); - s4->sin_addr = *v4; - lal.af = AF_INET; - lal.ac = GNUNET_NAT_AC_GLOBAL | GNUNET_NAT_AC_MANUAL; - check_notify_client (&lal, - ch, - add); - } - - /* (1) check if client cares. */ - if (! ch->natted_address) - return; - have_v4 = GNUNET_NO; - for (unsigned int i = 0; i < ch->num_caddrs; i++) - { - const struct sockaddr_storage *ss = &ch->caddrs[i].ss; - - if (AF_INET != ss->ss_family) - continue; - have_v4 = GNUNET_YES; - break; - } - if (GNUNET_NO == have_v4) - return; /* IPv6-only */ - - /* (2) build address info */ - memset (&sa, - 0, - sizeof(sa)); - sa.sin_family = AF_INET; - sa.sin_addr = *v4; - sa.sin_port = htons (0); - - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "Detected eternal IP %s, notifying client of external IP (without port)\n", - GNUNET_a2s ((const struct sockaddr *) &sa, - sizeof(sa))); - /* (3) notify client of change */ - notify_client (is_nat_v4 (v4) - ? GNUNET_NAT_AC_EXTERN | GNUNET_NAT_AC_LAN - : GNUNET_NAT_AC_EXTERN | GNUNET_NAT_AC_GLOBAL, - ch, - add, - &sa, - sizeof(sa)); -} - - -/** - * We got a connection reversal request from another peer. - * Notify applicable clients. - * - * @param cls closure with the `struct LocalAddressList` - * @param ra IP address of the peer who wants us to connect to it - */ -static void -reversal_callback (void *cls, - const struct sockaddr_in *ra) -{ - struct LocalAddressList *lal = cls; - const struct sockaddr_in *l4; - - GNUNET_assert (AF_INET == lal->af); - l4 = (const struct sockaddr_in *) &lal->addr; - for (struct ClientHandle *ch = ch_head; - NULL != ch; - ch = ch->next) - { - struct GNUNET_NAT_ConnectionReversalRequestedMessage *crrm; - struct GNUNET_MQ_Envelope *env; - int match; - - /* Check if client is in applicable range for ICMP NAT traversal - for this local address */ - if (! ch->natted_address) - continue; - match = GNUNET_NO; - for (unsigned int i = 0; i < ch->num_caddrs; i++) - { - struct ClientAddress *ca = &ch->caddrs[i]; - const struct sockaddr_in *c4; - - if (AF_INET != ca->ss.ss_family) - continue; - c4 = (const struct sockaddr_in *) &ca->ss; - if ((0 != c4->sin_addr.s_addr) && - (l4->sin_addr.s_addr != c4->sin_addr.s_addr)) - continue; - match = GNUNET_YES; - break; - } - if (! match) - continue; - - /* Notify applicable client about connection reversal request */ - env = GNUNET_MQ_msg_extra (crrm, - sizeof(struct sockaddr_in), - GNUNET_MESSAGE_TYPE_NAT_CONNECTION_REVERSAL_REQUESTED); - GNUNET_memcpy (&crrm[1], - ra, - sizeof(struct sockaddr_in)); - GNUNET_MQ_send (ch->mq, - env); - } -} - - -/** - * Task we run periodically to scan for network interfaces. - * - * @param cls NULL - */ -static void -run_scan (void *cls) -{ - struct IfcProcContext ifc_ctx; - int found; - int have_nat; - struct LocalAddressList *lnext; - - scan_task = GNUNET_SCHEDULER_add_delayed (SCAN_FREQ, - &run_scan, - NULL); - memset (&ifc_ctx, - 0, - sizeof(ifc_ctx)); - GNUNET_OS_network_interfaces_list (&ifc_proc, - &ifc_ctx); - /* remove addresses that disappeared */ - for (struct LocalAddressList *lal = lal_head; - NULL != lal; - lal = lnext) - { - lnext = lal->next; - found = GNUNET_NO; - for (struct LocalAddressList *pos = ifc_ctx.lal_head; - NULL != pos; - pos = pos->next) - { - if ((pos->af == lal->af) && - (0 == memcmp (&lal->addr, - &pos->addr, - (AF_INET == lal->af) - ? sizeof(struct sockaddr_in) - : sizeof(struct sockaddr_in6)))) - { - found = GNUNET_YES; - } - } - if (GNUNET_NO == found) - { - notify_clients (lal, - GNUNET_NO); - free_lal (lal); - } - } - - /* add addresses that appeared */ - have_nat = GNUNET_NO; - for (struct LocalAddressList *pos = ifc_ctx.lal_head; - NULL != pos; - pos = ifc_ctx.lal_head) - { - found = GNUNET_NO; - if (GNUNET_NAT_AC_LAN == (GNUNET_NAT_AC_LAN & pos->ac)) - have_nat = GNUNET_YES; - for (struct LocalAddressList *lal = lal_head; - NULL != lal; - lal = lal->next) - { - if ((pos->af == lal->af) && - (0 == memcmp (&lal->addr, - &pos->addr, - (AF_INET == lal->af) - ? sizeof(struct sockaddr_in) - : sizeof(struct sockaddr_in6)))) - found = GNUNET_YES; - } - GNUNET_CONTAINER_DLL_remove (ifc_ctx.lal_head, - ifc_ctx.lal_tail, - pos); - if (GNUNET_YES == found) - { - GNUNET_free (pos); - } - else - { - notify_clients (pos, - GNUNET_YES); - GNUNET_CONTAINER_DLL_insert (lal_head, - lal_tail, - pos); - if ((AF_INET == pos->af) && - (NULL == pos->hc) && - (0 != (GNUNET_NAT_AC_LAN & pos->ac))) - { - const struct sockaddr_in *s4 - = (const struct sockaddr_in *) &pos->addr; - - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "Found NATed local address %s, starting NAT server\n", - GNUNET_a2s ((const struct sockaddr *) &pos->addr, - sizeof(*s4))); - pos->hc = GN_start_gnunet_nat_server_ (&s4->sin_addr, - &reversal_callback, - pos, - cfg); - } - } - } - GN_nat_status_changed (have_nat); -} - - -/** - * Function called whenever our set of external addresses - * as created by `upnpc` changes. - * - * @param cls closure with our `struct ClientHandle *` - * @param add_remove #GNUNET_YES to mean the new public IP address, #GNUNET_NO to mean - * the previous (now invalid) one, #GNUNET_SYSERR indicates an error - * @param addr either the previous or the new public IP address - * @param addrlen actual length of the @a addr - * @param result #GNUNET_NAT_ERROR_SUCCESS on success, otherwise the specific error code - */ -static void -upnp_addr_change_cb (void *cls, - int add_remove, - const struct sockaddr *addr, - socklen_t addrlen, - enum GNUNET_NAT_StatusCode result) -{ - struct ClientHandle *ch = cls; - enum GNUNET_NAT_AddressClass ac; - - switch (result) - { - case GNUNET_NAT_ERROR_SUCCESS: - GNUNET_assert (NULL != addr); - break; - - case GNUNET_NAT_ERROR_UPNPC_FAILED: - case GNUNET_NAT_ERROR_UPNPC_TIMEOUT: - case GNUNET_NAT_ERROR_IPC_FAILURE: - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "Running upnpc failed: %d\n", - result); - return; - - case GNUNET_NAT_ERROR_EXTERNAL_IP_UTILITY_NOT_FOUND: - GNUNET_log (GNUNET_ERROR_TYPE_INFO, - "external-ip binary not found\n"); - return; - - case GNUNET_NAT_ERROR_UPNPC_NOT_FOUND: - GNUNET_log (GNUNET_ERROR_TYPE_INFO, - "upnpc binary not found\n"); - return; - - case GNUNET_NAT_ERROR_EXTERNAL_IP_UTILITY_FAILED: - GNUNET_log (GNUNET_ERROR_TYPE_WARNING, - "external-ip binary could not be run\n"); - return; - - case GNUNET_NAT_ERROR_UPNPC_PORTMAP_FAILED: - GNUNET_log (GNUNET_ERROR_TYPE_WARNING, - "upnpc failed to create port mapping\n"); - return; - - case GNUNET_NAT_ERROR_EXTERNAL_IP_UTILITY_OUTPUT_INVALID: - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "Invalid output from upnpc\n"); - return; - - case GNUNET_NAT_ERROR_EXTERNAL_IP_ADDRESS_INVALID: - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "Invalid address returned by upnpc\n"); - return; - - default: - GNUNET_break (0); /* should not be possible */ - return; - } - switch (addr->sa_family) - { - case AF_INET: - ac = is_nat_v4 (&((const struct sockaddr_in *) addr)->sin_addr) - ? GNUNET_NAT_AC_LAN - : GNUNET_NAT_AC_EXTERN; - break; - - case AF_INET6: - ac = is_nat_v6 (&((const struct sockaddr_in6 *) addr)->sin6_addr) - ? GNUNET_NAT_AC_LAN - : GNUNET_NAT_AC_EXTERN; - break; - - default: - GNUNET_break (0); - return; - } - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "upnp external address %s: %s\n", - add_remove ? "added" : "removed", - GNUNET_a2s (addr, - addrlen)); - notify_client (ac, - ch, - add_remove, - addr, - addrlen); -} - - -/** - * Resolve the `hole_external` name to figure out our - * external address from a manually punched hole. The - * port number has already been parsed, this task is - * responsible for periodically doing a DNS lookup. - * - * @param cls client handle to act upon - */ -static void -dyndns_lookup (void *cls); - - -/** - * Our (external) hostname was resolved. Update lists of - * current external IPs (note that DNS may return multiple - * addresses!) and notify client accordingly. - * - * @param cls the `struct ClientHandle` - * @param addr NULL on error, otherwise result of DNS lookup - * @param addrlen number of bytes in @a addr - */ -static void -process_external_ip (void *cls, - const struct sockaddr *addr, - socklen_t addrlen) -{ - struct ClientHandle *ch = cls; - struct LocalAddressList *lal; - struct sockaddr_storage ss; - struct sockaddr_in *v4; - struct sockaddr_in6 *v6; - - if (NULL == addr) - { - struct LocalAddressList *laln; - - ch->ext_dns = NULL; - ch->ext_dns_task - = GNUNET_SCHEDULER_add_delayed (dyndns_frequency, - &dyndns_lookup, - ch); - /* Current iteration is over, remove 'old' IPs now */ - for (lal = ch->ext_addr_head; NULL != lal; lal = laln) - { - laln = lal->next; - if (GNUNET_YES == lal->old) - { - GNUNET_CONTAINER_DLL_remove (ch->ext_addr_head, - ch->ext_addr_tail, - lal); - check_notify_client (lal, - ch, - GNUNET_NO); - GNUNET_free (lal); - } - } - return; - } - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "Got IP `%s' for external address `%s'\n", - GNUNET_a2s (addr, - addrlen), - ch->hole_external); - - /* build sockaddr storage with port number */ - memset (&ss, - 0, - sizeof(ss)); - GNUNET_memcpy (&ss, - addr, - addrlen); - switch (addr->sa_family) - { - case AF_INET: - v4 = (struct sockaddr_in *) &ss; - v4->sin_port = htons (ch->ext_dns_port); - break; - - case AF_INET6: - v6 = (struct sockaddr_in6 *) &ss; - v6->sin6_port = htons (ch->ext_dns_port); - break; - - default: - GNUNET_break (0); - return; - } - /* See if 'ss' matches any of our known addresses */ - for (lal = ch->ext_addr_head; NULL != lal; lal = lal->next) - { - if (GNUNET_NO == lal->old) - continue; /* already processed, skip */ - if ((addr->sa_family == lal->addr.ss_family) && - (0 == memcmp (&ss, - &lal->addr, - addrlen))) - { - /* Address unchanged, remember so we do not remove */ - lal->old = GNUNET_NO; - return; /* done here */ - } - } - /* notify client, and remember IP for later removal! */ - lal = GNUNET_new (struct LocalAddressList); - lal->addr = ss; - lal->af = ss.ss_family; - lal->ac = GNUNET_NAT_AC_GLOBAL | GNUNET_NAT_AC_MANUAL; - GNUNET_CONTAINER_DLL_insert (ch->ext_addr_head, - ch->ext_addr_tail, - lal); - check_notify_client (lal, - ch, - GNUNET_YES); -} - - -/** - * Resolve the `hole_external` name to figure out our - * external address from a manually punched hole. The - * port number has already been parsed, this task is - * responsible for periodically doing a DNS lookup. - * - * @param ch client handle to act upon - */ -static void -dyndns_lookup (void *cls) -{ - struct ClientHandle *ch = cls; - struct LocalAddressList *lal; - - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "Performing DNS lookup for punched hole given for `%s' as `%s:%u'\n", - ch->section_name, - ch->hole_external, - (unsigned int) ch->ext_dns_port); - for (lal = ch->ext_addr_head; NULL != lal; lal = lal->next) - lal->old = GNUNET_YES; - ch->ext_dns_task = NULL; - ch->ext_dns = GNUNET_RESOLVER_ip_get (ch->hole_external, - AF_UNSPEC, - GNUNET_TIME_UNIT_MINUTES, - &process_external_ip, - ch); -} - - -/** - * Resolve the `hole_external` name to figure out our - * external address from a manually punched hole. The - * given name may be "AUTO" in which case we should use - * the IP address(es) we have from upnpc or other methods. - * The name can also be an IP address, in which case we - * do not need to do DNS resolution. Finally, we also - * need to parse the port number. - * - * @param ch client handle to act upon - */ -static void -lookup_hole_external (struct ClientHandle *ch) -{ - char *port; - unsigned int pnum; - struct sockaddr_in *s4; - struct LocalAddressList *lal; - - port = strrchr (ch->hole_external, ':'); - if (NULL == port) - { - GNUNET_log (GNUNET_ERROR_TYPE_WARNING, - _ ("Malformed punched hole specification `%s' (lacks port)\n"), - ch->hole_external); - return; - } - if ((1 != sscanf (port + 1, - "%u", - &pnum)) || - (pnum > 65535)) - { - GNUNET_log (GNUNET_ERROR_TYPE_WARNING, - _ ( - "Invalid port number in punched hole specification `%s' (lacks port)\n"), - port + 1); - return; - } - ch->ext_dns_port = (uint16_t) pnum; - *port = '\0'; - - lal = GNUNET_new (struct LocalAddressList); - if ('[' == *ch->hole_external) - { - struct sockaddr_in6 *s6 = (struct sockaddr_in6 *) &lal->addr; - - s6->sin6_family = AF_INET6; - if (']' != (ch->hole_external[strlen (ch->hole_external) - 1])) - { - GNUNET_log (GNUNET_ERROR_TYPE_WARNING, - _ ("Malformed punched hole specification `%s' (lacks `]')\n"), - ch->hole_external); - GNUNET_free (lal); - return; - } - ch->hole_external[strlen (ch->hole_external) - 1] = '\0'; - if (1 != inet_pton (AF_INET6, - ch->hole_external + 1, - &s6->sin6_addr)) - { - GNUNET_log (GNUNET_ERROR_TYPE_WARNING, - _ ( - "Malformed punched hole specification `%s' (IPv6 address invalid)"), - ch->hole_external + 1); - GNUNET_free (lal); - return; - } - s6->sin6_port = htons (ch->ext_dns_port); - lal->af = AF_INET6; - lal->ac = GNUNET_NAT_AC_GLOBAL | GNUNET_NAT_AC_MANUAL; - GNUNET_CONTAINER_DLL_insert (ch->ext_addr_head, - ch->ext_addr_tail, - lal); - check_notify_client (lal, - ch, - GNUNET_YES); - return; - } - - s4 = (struct sockaddr_in *) &lal->addr; - s4->sin_family = AF_INET; - if (1 == inet_pton (AF_INET, - ch->hole_external, - &s4->sin_addr)) - { - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "IPv4 punched hole given for `%s' via `%s:%u'\n", - ch->section_name, - ch->hole_external, - (unsigned int) ch->ext_dns_port); - s4->sin_port = htons (ch->ext_dns_port); - lal->af = AF_INET; - lal->ac = GNUNET_NAT_AC_GLOBAL | GNUNET_NAT_AC_MANUAL; - GNUNET_CONTAINER_DLL_insert (ch->ext_addr_head, - ch->ext_addr_tail, - lal); - check_notify_client (lal, - ch, - GNUNET_YES); - return; - } - if (0 == strcasecmp (ch->hole_external, - "AUTO")) - { - /* handled in #notify_client_external_ipv4_change() */ - GNUNET_free (lal); - return; - } - /* got a DNS name, trigger lookup! */ - GNUNET_free (lal); - ch->ext_dns_task - = GNUNET_SCHEDULER_add_now (&dyndns_lookup, - ch); -} - - -/** - * Handler for #GNUNET_MESSAGE_TYPE_NAT_REGISTER message from client. - * We remember the client for updates upon future NAT events. - * - * @param cls client who sent the message - * @param message the message received - */ -static void -handle_register (void *cls, - const struct GNUNET_NAT_RegisterMessage *message) -{ - struct ClientHandle *ch = cls; - const char *off; - size_t left; - - if ((0 != ch->proto) || - (NULL != ch->caddrs)) - { - /* double registration not allowed */ - GNUNET_break (0); - GNUNET_SERVICE_client_drop (ch->client); - return; - } - ch->flags = message->flags; - ch->proto = message->proto; - ch->num_caddrs = ntohs (message->num_addrs); - ch->caddrs = GNUNET_new_array (ch->num_caddrs, - struct ClientAddress); - left = ntohs (message->header.size) - sizeof(*message); - off = (const char *) &message[1]; - for (unsigned int i = 0; i < ch->num_caddrs; i++) - { - const struct sockaddr *sa = (const struct sockaddr *) off; - size_t alen; - uint16_t port; - int is_nat; - - if (sizeof(sa_family_t) > left) - { - GNUNET_break (0); - GNUNET_SERVICE_client_drop (ch->client); - return; - } - is_nat = GNUNET_NO; - switch (sa->sa_family) - { - case AF_INET: - { - struct sockaddr_in s4; - - GNUNET_memcpy (&s4, - off, - sizeof(struct sockaddr_in)); - alen = sizeof(struct sockaddr_in); - if (is_nat_v4 (&s4.sin_addr)) - is_nat = GNUNET_YES; - port = ntohs (s4.sin_port); - } - break; - - case AF_INET6: - { - struct sockaddr_in6 s6; - - GNUNET_memcpy (&s6, - off, - sizeof(struct sockaddr_in6)); - alen = sizeof(struct sockaddr_in6); - if (is_nat_v6 (&s6.sin6_addr)) - is_nat = GNUNET_YES; - port = ntohs (s6.sin6_port); - } - break; - -#if AF_UNIX - case AF_UNIX: - alen = sizeof(struct sockaddr_un); - port = 0; - break; -#endif - default: - GNUNET_break (0); - GNUNET_SERVICE_client_drop (ch->client); - return; - } - /* store address */ - GNUNET_assert (alen <= left); - GNUNET_assert (alen <= sizeof(struct sockaddr_storage)); - GNUNET_memcpy (&ch->caddrs[i].ss, - off, - alen); - - /* If applicable, try UPNPC NAT punching */ - if ((is_nat) && - (enable_upnp) && - ((IPPROTO_TCP == ch->proto) || - (IPPROTO_UDP == ch->proto))) - { - ch->natted_address = GNUNET_YES; - ch->caddrs[i].mh - = GNUNET_NAT_mini_map_start (port, - IPPROTO_TCP == ch->proto, - &upnp_addr_change_cb, - ch); - } - - off += alen; - } - - ch->section_name - = GNUNET_strndup (off, - ntohs (message->str_len)); - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "Received REGISTER message from client for subsystem `%s'\n", - ch->section_name); - if (GNUNET_OK == - GNUNET_CONFIGURATION_get_value_string (cfg, - ch->section_name, - "HOLE_EXTERNAL", - &ch->hole_external)) - lookup_hole_external (ch); - - /* Actually send IP address list to client */ - for (struct LocalAddressList *lal = lal_head; - NULL != lal; - lal = lal->next) - { - check_notify_client (lal, - ch, - GNUNET_YES); - } - /* Also consider IPv4 determined by `external-ip` */ - ch->external_monitor - = GN_external_ipv4_monitor_start (¬ify_client_external_ipv4_change, - ch); - GNUNET_SERVICE_client_continue (ch->client); -} - - -/** - * Check validity of #GNUNET_MESSAGE_TYPE_NAT_HANDLE_STUN message from - * client. - * - * @param cls client who sent the message - * @param message the message received - * @return #GNUNET_OK if message is well-formed - */ -static int -check_stun (void *cls, - const struct GNUNET_NAT_HandleStunMessage *message) -{ - size_t sa_len = ntohs (message->sender_addr_size); - size_t expect = sa_len + ntohs (message->payload_size); - - if (ntohs (message->header.size) - sizeof(*message) != expect) - { - GNUNET_break (0); - return GNUNET_SYSERR; - } - if (sa_len < sizeof(sa_family_t)) - { - GNUNET_break (0); - return GNUNET_SYSERR; - } - return GNUNET_OK; -} - - -/** - * Notify all clients about our external IP address - * as reported by the STUN server. - * - * @param ip the external IP - * @param add #GNUNET_YES to add, #GNUNET_NO to remove - */ -static void -notify_clients_stun_change (const struct sockaddr_in *ip, - int add) -{ - for (struct ClientHandle *ch = ch_head; - NULL != ch; - ch = ch->next) - { - struct sockaddr_in v4; - struct GNUNET_NAT_AddressChangeNotificationMessage *msg; - struct GNUNET_MQ_Envelope *env; - - if (! ch->natted_address) - continue; - v4 = *ip; - v4.sin_port = htons (0); - env = GNUNET_MQ_msg_extra (msg, - sizeof(v4), - GNUNET_MESSAGE_TYPE_NAT_ADDRESS_CHANGE); - msg->add_remove = htonl ((int32_t) add); - msg->addr_class = htonl (GNUNET_NAT_AC_EXTERN - | GNUNET_NAT_AC_GLOBAL); - GNUNET_memcpy (&msg[1], - &v4, - sizeof(v4)); - GNUNET_MQ_send (ch->mq, - env); - } -} - - -/** - * Function to be called when we decide that an - * external IP address as told to us by a STUN - * server has gone stale. - * - * @param cls the `struct StunExternalIP` to drop - */ -static void -stun_ip_timeout (void *cls) -{ - struct StunExternalIP *se = cls; - - se->timeout_task = NULL; - notify_clients_stun_change (&se->external_addr, - GNUNET_NO); - GNUNET_CONTAINER_DLL_remove (se_head, - se_tail, - se); - GNUNET_free (se); -} - - -/** - * Handler for #GNUNET_MESSAGE_TYPE_NAT_HANDLE_STUN message from - * client. - * - * @param cls client who sent the message - * @param message the message received - */ -static void -handle_stun (void *cls, - const struct GNUNET_NAT_HandleStunMessage *message) -{ - struct ClientHandle *ch = cls; - const char *buf = (const char *) &message[1]; - const struct sockaddr *sa; - const void *payload; - size_t sa_len; - size_t payload_size; - struct sockaddr_in external_addr; - - sa_len = ntohs (message->sender_addr_size); - payload_size = ntohs (message->payload_size); - sa = (const struct sockaddr *) &buf[0]; - payload = (const struct sockaddr *) &buf[sa_len]; - switch (sa->sa_family) - { - case AF_INET: - if (sa_len != sizeof(struct sockaddr_in)) - { - GNUNET_break (0); - GNUNET_SERVICE_client_drop (ch->client); - return; - } - break; - - case AF_INET6: - if (sa_len != sizeof(struct sockaddr_in6)) - { - GNUNET_break (0); - GNUNET_SERVICE_client_drop (ch->client); - return; - } - break; - } - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "Received HANDLE_STUN message from client\n"); - if (GNUNET_OK == - GNUNET_NAT_stun_handle_packet_ (payload, - payload_size, - &external_addr)) - { - /* We now know that a server at "sa" claims that - we are visible at IP "external_addr". - - We should (for some fixed period of time) tell - all of our clients that listen to a NAT'ed address - that they might want to consider the given 'external_ip' - as their public IP address (this includes TCP and UDP - clients, even if only UDP sends STUN requests). - - If we do not get a renewal, the "external_addr" should be - removed again. The timeout frequency should be configurable - (with a sane default), so that the UDP plugin can tell how - often to re-request STUN. - */struct StunExternalIP *se; - - /* Check if we had a prior response from this STUN server */ - for (se = se_head; NULL != se; se = se->next) - { - if ((se->stun_server_addr_len != sa_len) || - (0 != memcmp (sa, - &se->stun_server_addr, - sa_len))) - continue; /* different STUN server */ - if (0 != GNUNET_memcmp (&external_addr, - &se->external_addr)) - { - /* external IP changed, update! */ - notify_clients_stun_change (&se->external_addr, - GNUNET_NO); - se->external_addr = external_addr; - notify_clients_stun_change (&se->external_addr, - GNUNET_YES); - } - /* update timeout */ - GNUNET_SCHEDULER_cancel (se->timeout_task); - se->timeout_task - = GNUNET_SCHEDULER_add_delayed (stun_stale_timeout, - &stun_ip_timeout, - se); - return; - } - /* STUN server is completely new, create fresh entry */ - se = GNUNET_new (struct StunExternalIP); - se->external_addr = external_addr; - GNUNET_memcpy (&se->stun_server_addr, - sa, - sa_len); - se->stun_server_addr_len = sa_len; - se->timeout_task = GNUNET_SCHEDULER_add_delayed (stun_stale_timeout, - &stun_ip_timeout, - se); - GNUNET_CONTAINER_DLL_insert (se_head, - se_tail, - se); - notify_clients_stun_change (&se->external_addr, - GNUNET_NO); - } - GNUNET_SERVICE_client_continue (ch->client); -} - - -/** - * Check validity of - * #GNUNET_MESSAGE_TYPE_NAT_REQUEST_CONNECTION_REVERSAL message from - * client. - * - * @param cls client who sent the message - * @param message the message received - * @return #GNUNET_OK if message is well-formed - */ -static int -check_request_connection_reversal (void *cls, - const struct - GNUNET_NAT_RequestConnectionReversalMessage * - message) -{ - size_t expect; - - expect = ntohs (message->local_addr_size) - + ntohs (message->remote_addr_size); - if (ntohs (message->header.size) - sizeof(*message) != expect) - { - GNUNET_break (0); - return GNUNET_SYSERR; - } - return GNUNET_OK; -} - - -/** - * Handler for #GNUNET_MESSAGE_TYPE_NAT_REQUEST_CONNECTION_REVERSAL - * message from client. - * - * @param cls client who sent the message - * @param message the message received - */ -static void -handle_request_connection_reversal (void *cls, - const struct - GNUNET_NAT_RequestConnectionReversalMessage - *message) -{ - struct ClientHandle *ch = cls; - const char *buf = (const char *) &message[1]; - size_t local_sa_len = ntohs (message->local_addr_size); - size_t remote_sa_len = ntohs (message->remote_addr_size); - struct sockaddr_in l4; - struct sockaddr_in r4; - int ret; - - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "Received REQUEST CONNECTION REVERSAL message from client\n"); - if (local_sa_len != sizeof(struct sockaddr_in)) - { - GNUNET_break_op (0); - GNUNET_SERVICE_client_drop (ch->client); - return; - } - if (remote_sa_len != sizeof(struct sockaddr_in)) - { - GNUNET_break_op (0); - GNUNET_SERVICE_client_drop (ch->client); - return; - } - GNUNET_memcpy (&l4, - buf, - sizeof(struct sockaddr_in)); - GNUNET_break_op (AF_INET == l4.sin_family); - buf += sizeof(struct sockaddr_in); - GNUNET_memcpy (&r4, - buf, - sizeof(struct sockaddr_in)); - GNUNET_break_op (AF_INET == r4.sin_family); - ret = GN_request_connection_reversal (&l4.sin_addr, - ntohs (l4.sin_port), - &r4.sin_addr, - cfg); - if (GNUNET_OK != ret) - GNUNET_log (GNUNET_ERROR_TYPE_WARNING, - _ ("Connection reversal request failed\n")); - GNUNET_SERVICE_client_continue (ch->client); -} - - -/** - * Task run during shutdown. - * - * @param cls unused - */ -static void -shutdown_task (void *cls) -{ - struct StunExternalIP *se; - - while (NULL != (se = se_head)) - { - GNUNET_CONTAINER_DLL_remove (se_head, - se_tail, - se); - GNUNET_SCHEDULER_cancel (se->timeout_task); - GNUNET_free (se); - } - GN_nat_status_changed (GNUNET_NO); - if (NULL != scan_task) - { - GNUNET_SCHEDULER_cancel (scan_task); - scan_task = NULL; - } - if (NULL != stats) - { - GNUNET_STATISTICS_destroy (stats, - GNUNET_NO); - stats = NULL; - } - destroy_lal (); -} - - -/** - * Setup NAT service. - * - * @param cls closure - * @param c configuration to use - * @param service the initialized service - */ -static void -run (void *cls, - const struct GNUNET_CONFIGURATION_Handle *c, - struct GNUNET_SERVICE_Handle *service) -{ - cfg = c; - if (GNUNET_OK != - GNUNET_CONFIGURATION_get_value_time (cfg, - "NAT", - "STUN_STALE", - &stun_stale_timeout)) - stun_stale_timeout = GNUNET_TIME_UNIT_HOURS; - - /* Check for UPnP */ - enable_upnp - = GNUNET_CONFIGURATION_get_value_yesno (cfg, - "NAT", - "ENABLE_UPNP"); - if (GNUNET_YES == enable_upnp) - { - /* check if it works */ - if (GNUNET_SYSERR == - GNUNET_OS_check_helper_binary ("upnpc", - GNUNET_NO, - NULL)) - { - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - _ ( - "UPnP enabled in configuration, but UPnP client `upnpc` command not found, disabling UPnP\n")); - enable_upnp = GNUNET_SYSERR; - } - } - if (GNUNET_OK != - GNUNET_CONFIGURATION_get_value_time (cfg, - "nat", - "DYNDNS_FREQUENCY", - &dyndns_frequency)) - dyndns_frequency = DYNDNS_FREQUENCY; - - enable_ipscan - = GNUNET_CONFIGURATION_get_value_yesno (cfg, - "NAT", - "ENABLE_IPSCAN"); - - GNUNET_SCHEDULER_add_shutdown (&shutdown_task, - NULL); - stats = GNUNET_STATISTICS_create ("nat", - cfg); - if (GNUNET_YES == enable_ipscan) - scan_task = GNUNET_SCHEDULER_add_now (&run_scan, - NULL); -} - - -/** - * 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 `struct ClientHandle` - */ -static void * -client_connect_cb (void *cls, - struct GNUNET_SERVICE_Client *c, - struct GNUNET_MQ_Handle *mq) -{ - struct ClientHandle *ch; - - ch = GNUNET_new (struct ClientHandle); - ch->mq = mq; - ch->client = c; - GNUNET_CONTAINER_DLL_insert (ch_head, - ch_tail, - ch); - return ch; -} - - -/** - * Callback called when a client disconnected from the service - * - * @param cls closure for the service - * @param c the client that disconnected - * @param internal_cls a `struct ClientHandle *` - */ -static void -client_disconnect_cb (void *cls, - struct GNUNET_SERVICE_Client *c, - void *internal_cls) -{ - struct ClientHandle *ch = internal_cls; - struct LocalAddressList *lal; - - GNUNET_CONTAINER_DLL_remove (ch_head, - ch_tail, - ch); - for (unsigned int i = 0; i < ch->num_caddrs; i++) - { - if (NULL != ch->caddrs[i].mh) - { - GNUNET_NAT_mini_map_stop (ch->caddrs[i].mh); - ch->caddrs[i].mh = NULL; - } - } - GNUNET_free (ch->caddrs); - while (NULL != (lal = ch->ext_addr_head)) - { - GNUNET_CONTAINER_DLL_remove (ch->ext_addr_head, - ch->ext_addr_tail, - lal); - GNUNET_free (lal); - } - if (NULL != ch->ext_dns_task) - { - GNUNET_SCHEDULER_cancel (ch->ext_dns_task); - ch->ext_dns_task = NULL; - } - if (NULL != ch->external_monitor) - { - GN_external_ipv4_monitor_stop (ch->external_monitor); - ch->external_monitor = NULL; - } - if (NULL != ch->ext_dns) - { - GNUNET_RESOLVER_request_cancel (ch->ext_dns); - ch->ext_dns = NULL; - } - GNUNET_free (ch->hole_external); - GNUNET_free (ch->section_name); - GNUNET_free (ch); -} - - -/** - * Define "main" method using service macro. - */ -GNUNET_SERVICE_MAIN - ("nat", - GNUNET_SERVICE_OPTION_NONE, - &run, - &client_connect_cb, - &client_disconnect_cb, - NULL, - GNUNET_MQ_hd_var_size (register, - GNUNET_MESSAGE_TYPE_NAT_REGISTER, - struct GNUNET_NAT_RegisterMessage, - NULL), - GNUNET_MQ_hd_var_size (stun, - GNUNET_MESSAGE_TYPE_NAT_HANDLE_STUN, - struct GNUNET_NAT_HandleStunMessage, - NULL), - GNUNET_MQ_hd_var_size (request_connection_reversal, - GNUNET_MESSAGE_TYPE_NAT_REQUEST_CONNECTION_REVERSAL, - struct GNUNET_NAT_RequestConnectionReversalMessage, - NULL), - GNUNET_MQ_handler_end ()); - - -#if defined(__linux__) && defined(__GLIBC__) -#include - -/** - * 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-service-nat.c */ diff --git a/src/nat/gnunet-service-nat.h b/src/nat/gnunet-service-nat.h deleted file mode 100644 index 5717306bb..000000000 --- a/src/nat/gnunet-service-nat.h +++ /dev/null @@ -1,35 +0,0 @@ -/* - This file is part of GNUnet. - Copyright (C) 2016, 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 . - - SPDX-License-Identifier: AGPL3.0-or-later - */ - -/** - * @file nat/gnunet-service-nat.h - * @brief network address translation traversal service - * @author Christian Grothoff - */ -#ifndef GNUNET_SERVICE_NAT_H -#define GNUNET_SERVICE_NAT_H - -/** - * Is UPnP enabled? #GNUNET_YES if enabled, #GNUNET_NO if disabled, - * #GNUNET_SYSERR if configuration enabled but binary is unavailable. - */ -extern int enable_upnp; - -#endif diff --git a/src/nat/gnunet-service-nat_externalip.c b/src/nat/gnunet-service-nat_externalip.c deleted file mode 100644 index c2625be2d..000000000 --- a/src/nat/gnunet-service-nat_externalip.c +++ /dev/null @@ -1,316 +0,0 @@ -/* - This file is part of GNUnet. - Copyright (C) 2009, 2015, 2016, 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 . - - SPDX-License-Identifier: AGPL3.0-or-later - */ -/** - * Code to figure out what our external IPv4 address(es) might - * be (external IPv4s are what is seen on the rest of the Internet). - * - * This can be implemented using different methods, and we allow - * the main service to be notified about changes to what we believe - * is our external IPv4 address. - * - * Note that this is explicitly only about NATed systems; if one - * of our network interfaces has a global IP address this does - * not count as "external". - * - * @file nat/gnunet-service-nat_externalip.c - * @brief Functions for monitoring external IPv4 addresses - * @author Christian Grothoff - */ -#include "platform.h" -#include -#include "gnunet_util_lib.h" -#include "gnunet_protocols.h" -#include "gnunet_signatures.h" -#include "gnunet_statistics_service.h" -#include "gnunet_resolver_service.h" -#include "gnunet_nat_service.h" -#include "gnunet-service-nat.h" -#include "gnunet-service-nat_externalip.h" -#include "gnunet-service-nat_stun.h" -#include "gnunet-service-nat_mini.h" -#include "gnunet-service-nat_helper.h" -#include "nat.h" -#include - - -/** - * How long do we wait until we re-try running `external-ip` if the - * command failed to terminate nicely? - */ -#define EXTERN_IP_RETRY_TIMEOUT GNUNET_TIME_relative_multiply ( \ - GNUNET_TIME_UNIT_MINUTES, 15) - -/** - * How long do we wait until we re-try running `external-ip` if the - * command failed (but terminated)? - */ -#define EXTERN_IP_RETRY_FAILURE GNUNET_TIME_relative_multiply ( \ - GNUNET_TIME_UNIT_MINUTES, 30) - -/** - * How long do we wait until we re-try running `external-ip` if the - * command succeeded? - */ -#define EXTERN_IP_RETRY_SUCCESS GNUNET_TIME_relative_multiply ( \ - GNUNET_TIME_UNIT_MINUTES, 5) - - -/** - * Handle to monitor for external IP changes. - */ -struct GN_ExternalIPMonitor -{ - /** - * Kept in DLL. - */ - struct GN_ExternalIPMonitor *next; - - /** - * Kept in DLL. - */ - struct GN_ExternalIPMonitor *prev; - - /** - * Function to call when we believe our external IPv4 address changed. - */ - GN_NotifyExternalIPv4Change cb; - - /** - * Closure for @e cb. - */ - void *cb_cls; -}; - - -/** - * List of monitors, kept in DLL. - */ -static struct GN_ExternalIPMonitor *mon_head; - -/** - * List of monitors, kept in DLL. - */ -static struct GN_ExternalIPMonitor *mon_tail; - -/** - * Task run to obtain our external IP (if #enable_upnp is set - * and if we find we have a NATed IP address). - */ -static struct GNUNET_SCHEDULER_Task *probe_external_ip_task; - -/** - * Handle to our operation to run `external-ip`. - */ -static struct GNUNET_NAT_ExternalHandle *probe_external_ip_op; - -/** - * What is our external IP address as claimed by `external-ip`? - * 0 for unknown. - */ -static struct in_addr mini_external_ipv4; - - -/** - * Tell relevant clients about a change in our external - * IPv4 address. - * - * @param add #GNUNET_YES to add, #GNUNET_NO to remove - * @param v4 the external address that changed - */ -static void -notify_monitors_external_ipv4_change (int add, - const struct in_addr *v4) -{ - for (struct GN_ExternalIPMonitor *mon = mon_head; - NULL != mon; - mon = mon->next) - mon->cb (mon->cb_cls, - v4, - add); -} - - -/** - * Task used to run `external-ip` to get our external IPv4 - * address and pass it to NATed clients if possible. - * - * @param cls NULL - */ -static void -run_external_ip (void *cls); - - -/** - * We learn our current external IP address. If it changed, - * notify all of our applicable clients. Also re-schedule - * #run_external_ip with an appropriate timeout. - * - * @param cls NULL - * @param addr the address, NULL on errors - * @param result #GNUNET_NAT_ERROR_SUCCESS on success, otherwise the specific error code - */ -static void -handle_external_ip (void *cls, - const struct in_addr *addr, - enum GNUNET_NAT_StatusCode result) -{ - char buf[INET_ADDRSTRLEN]; - - probe_external_ip_op = NULL; - GNUNET_SCHEDULER_cancel (probe_external_ip_task); - probe_external_ip_task - = GNUNET_SCHEDULER_add_delayed ((NULL == addr) - ? EXTERN_IP_RETRY_FAILURE - : EXTERN_IP_RETRY_SUCCESS, - &run_external_ip, - NULL); - switch (result) - { - case GNUNET_NAT_ERROR_SUCCESS: - GNUNET_assert (NULL != addr); - if (addr->s_addr == mini_external_ipv4.s_addr) - return; /* not change */ - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "Our external IP is now %s\n", - inet_ntop (AF_INET, - addr, - buf, - sizeof(buf))); - if (0 != mini_external_ipv4.s_addr) - notify_monitors_external_ipv4_change (GNUNET_NO, - &mini_external_ipv4); - mini_external_ipv4 = *addr; - notify_monitors_external_ipv4_change (GNUNET_YES, - &mini_external_ipv4); - break; - - default: - if (0 != mini_external_ipv4.s_addr) - notify_monitors_external_ipv4_change (GNUNET_NO, - &mini_external_ipv4); - mini_external_ipv4.s_addr = 0; - break; - } -} - - -/** - * Task used to run `external-ip` to get our external IPv4 - * address and pass it to NATed clients if possible. - * - * @param cls NULL - */ -static void -run_external_ip (void *cls) -{ - probe_external_ip_task - = GNUNET_SCHEDULER_add_delayed (EXTERN_IP_RETRY_TIMEOUT, - &run_external_ip, - NULL); - if (NULL != probe_external_ip_op) - { - GNUNET_NAT_mini_get_external_ipv4_cancel_ (probe_external_ip_op); - probe_external_ip_op = NULL; - } - probe_external_ip_op - = GNUNET_NAT_mini_get_external_ipv4_ (&handle_external_ip, - NULL); -} - - -/** - * We have changed our opinion about being NATed in the first - * place. Adapt our probing. - * - * @param have_nat #GNUNET_YES if we believe we are behind NAT - */ -void -GN_nat_status_changed (int have_nat) -{ - if (GNUNET_YES != enable_upnp) - return; - if ((GNUNET_YES == have_nat) && - (NULL == probe_external_ip_task) && - (NULL == probe_external_ip_op)) - { - probe_external_ip_task - = GNUNET_SCHEDULER_add_now (&run_external_ip, - NULL); - return; - } - if (GNUNET_NO == have_nat) - { - if (NULL != probe_external_ip_task) - { - GNUNET_SCHEDULER_cancel (probe_external_ip_task); - probe_external_ip_task = NULL; - } - if (NULL != probe_external_ip_op) - { - GNUNET_NAT_mini_get_external_ipv4_cancel_ (probe_external_ip_op); - probe_external_ip_op = NULL; - } - } -} - - -/** - * Start monitoring external IPv4 addresses. - * - * @param cb function to call on changes - * @param cb_cls closure for @a cb - * @return handle to cancel - */ -struct GN_ExternalIPMonitor * -GN_external_ipv4_monitor_start (GN_NotifyExternalIPv4Change cb, - void *cb_cls) -{ - struct GN_ExternalIPMonitor *mon; - - mon = GNUNET_new (struct GN_ExternalIPMonitor); - mon->cb = cb; - mon->cb_cls = cb_cls; - GNUNET_CONTAINER_DLL_insert (mon_head, - mon_tail, - mon); - if (0 != mini_external_ipv4.s_addr) - cb (cb_cls, - &mini_external_ipv4, - GNUNET_YES); - return mon; -} - - -/** - * Stop calling monitor. - * - * @param mon monitor to call - */ -void -GN_external_ipv4_monitor_stop (struct GN_ExternalIPMonitor *mon) -{ - GNUNET_CONTAINER_DLL_remove (mon_head, - mon_tail, - mon); - GNUNET_free (mon); -} - - -/* end of gnunet-service-nat_externalip.c */ diff --git a/src/nat/gnunet-service-nat_externalip.h b/src/nat/gnunet-service-nat_externalip.h deleted file mode 100644 index 6b3467fab..000000000 --- a/src/nat/gnunet-service-nat_externalip.h +++ /dev/null @@ -1,92 +0,0 @@ -/* - This file is part of GNUnet. - Copyright (C) 2009, 2015, 2016, 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 . - - SPDX-License-Identifier: AGPL3.0-or-later - */ -/** - * Code to figure out what our external IPv4 address(es) might - * be (external IPv4s are what is seen on the rest of the Internet). - * - * This can be implemented using different methods, and we allow - * the main service to be notified about changes to what we believe - * is our external IPv4 address. - * - * Note that this is explicitly only about NATed systems; if one - * of our network interfaces has a global IP address this does - * not count as "external". - * - * @file nat/gnunet-service-nat_externalip.h - * @brief Functions for monitoring external IPv4 addresses - * @author Christian Grothoff - */ -#ifndef GNUNET_SERVICE_NAT_EXTERNALIP_H -#define GNUNET_SERVICE_NAT_EXTERNALIP_H - -#include "platform.h" - - -/** - * We have changed our opinion about being NATed in the first - * place. Adapt our probing. - * - * @param have_nat #GNUNET_YES if we believe we are behind NAT - */ -void -GN_nat_status_changed (int have_nat); - - -/** - * Function we call when we believe our external IPv4 address changed. - * - * @param cls closure - * @param ip address to add/remove - * @param add_remove #GNUNET_YES to add, #GNUNET_NO to remove - */ -typedef void -(*GN_NotifyExternalIPv4Change)(void *cls, - const struct in_addr *ip, - int add_remove); - - -/** - * Handle to monitor for external IP changes. - */ -struct GN_ExternalIPMonitor; - - -/** - * Start monitoring external IPv4 addresses. - * - * @param cb function to call on changes - * @param cb_cls closure for @a cb - * @return handle to cancel - */ -struct GN_ExternalIPMonitor * -GN_external_ipv4_monitor_start (GN_NotifyExternalIPv4Change cb, - void *cb_cls); - - -/** - * Stop calling monitor. - * - * @param mon monitor to call - */ -void -GN_external_ipv4_monitor_stop (struct GN_ExternalIPMonitor *mon); - - -#endif diff --git a/src/nat/gnunet-service-nat_helper.c b/src/nat/gnunet-service-nat_helper.c deleted file mode 100644 index d92f5a99c..000000000 --- a/src/nat/gnunet-service-nat_helper.c +++ /dev/null @@ -1,391 +0,0 @@ -/* - This file is part of GNUnet. - Copyright (C) 2009, 2010, 2011, 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 . - - SPDX-License-Identifier: AGPL3.0-or-later - */ - -/** - * @file nat/gnunet-service-nat_helper.c - * @brief runs the gnunet-helper-nat-server - * @author Milan Bouchet-Valat - * @author Christian Grothoff - */ -#include "platform.h" -#include "gnunet_util_lib.h" -#include "gnunet-service-nat_helper.h" - - -/** - * Information we keep per NAT helper process. - */ -struct HelperContext -{ - /** - * IP address we pass to the NAT helper. - */ - struct in_addr internal_address; - - /** - * Function to call if we receive a reversal request. - */ - GN_ReversalCallback cb; - - /** - * Closure for @e cb. - */ - void *cb_cls; - - /** - * How long do we wait for restarting a crashed gnunet-helper-nat-server? - */ - struct GNUNET_TIME_Relative server_retry_delay; - - /** - * ID of select gnunet-helper-nat-server stdout read task - */ - struct GNUNET_SCHEDULER_Task *server_read_task; - - /** - * The process id of the server process (if behind NAT) - */ - struct GNUNET_OS_Process *server_proc; - - /** - * stdout pipe handle for the gnunet-helper-nat-server process - */ - struct GNUNET_DISK_PipeHandle *server_stdout; - - /** - * stdout file handle (for reading) for the gnunet-helper-nat-server process - */ - const struct GNUNET_DISK_FileHandle *server_stdout_handle; - - /** - * Handle to the GNUnet configuration - */ - const struct GNUNET_CONFIGURATION_Handle *cfg; -}; - - -/** - * Task that restarts the gnunet-helper-nat-server process after a crash - * after a certain delay. - * - * @param cls a `struct HelperContext` - */ -static void -restart_nat_server (void *cls); - - -/** - * Try again starting the helper later - * - * @param h context of the helper - */ -static void -try_again (struct HelperContext *h) -{ - GNUNET_assert (NULL == h->server_read_task); - h->server_retry_delay = GNUNET_TIME_STD_BACKOFF (h->server_retry_delay); - h->server_read_task = GNUNET_SCHEDULER_add_delayed (h->server_retry_delay, - &restart_nat_server, - h); -} - - -/** - * We have been notified that gnunet-helper-nat-server has written - * something to stdout. Handle the output, then reschedule this - * function to be called again once more is available. - * - * @param cls the `struct HelperContext` - */ -static void -nat_server_read (void *cls) -{ - struct HelperContext *h = cls; - char mybuf[40]; - ssize_t bytes; - int port; - const char *port_start; - struct sockaddr_in sin_addr; - - h->server_read_task = NULL; - memset (mybuf, 0, sizeof(mybuf)); - bytes = - GNUNET_DISK_file_read (h->server_stdout_handle, mybuf, sizeof(mybuf)); - if (bytes < 1) - { - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "Finished reading from server stdout with code: %d\n", - (int) bytes); - if (0 != GNUNET_OS_process_kill (h->server_proc, GNUNET_TERM_SIG)) - GNUNET_log_from_strerror (GNUNET_ERROR_TYPE_WARNING, "nat", "kill"); - GNUNET_OS_process_wait (h->server_proc); - GNUNET_OS_process_destroy (h->server_proc); - h->server_proc = NULL; - GNUNET_DISK_pipe_close (h->server_stdout); - h->server_stdout = NULL; - h->server_stdout_handle = NULL; - try_again (h); - return; - } - - port_start = NULL; - for (size_t i = 0; i < sizeof(mybuf); i++) - { - if (mybuf[i] == '\n') - { - mybuf[i] = '\0'; - break; - } - if ((mybuf[i] == ':') && (i + 1 < sizeof(mybuf))) - { - mybuf[i] = '\0'; - port_start = &mybuf[i + 1]; - } - } - - /* construct socket address of sender */ - memset (&sin_addr, 0, sizeof(sin_addr)); - sin_addr.sin_family = AF_INET; -#if HAVE_SOCKADDR_IN_SIN_LEN - sin_addr.sin_len = sizeof(sin_addr); -#endif - if ((NULL == port_start) || (1 != sscanf (port_start, "%d", &port)) || - (-1 == inet_pton (AF_INET, mybuf, &sin_addr.sin_addr))) - { - /* should we restart gnunet-helper-nat-server? */ - GNUNET_log (GNUNET_ERROR_TYPE_WARNING, - _ ( - "gnunet-helper-nat-server generated malformed address `%s'\n"), - mybuf); - h->server_read_task = - GNUNET_SCHEDULER_add_read_file (GNUNET_TIME_UNIT_FOREVER_REL, - h->server_stdout_handle, - &nat_server_read, - h); - return; - } - sin_addr.sin_port = htons ((uint16_t) port); - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "gnunet-helper-nat-server read: %s:%d\n", - mybuf, - port); - h->cb (h->cb_cls, &sin_addr); - h->server_read_task = - GNUNET_SCHEDULER_add_read_file (GNUNET_TIME_UNIT_FOREVER_REL, - h->server_stdout_handle, - &nat_server_read, - h); -} - - -/** - * Task that restarts the gnunet-helper-nat-server process after a crash - * after a certain delay. - * - * @param cls a `struct HelperContext` - */ -static void -restart_nat_server (void *cls) -{ - struct HelperContext *h = cls; - char *binary; - char ia[INET_ADDRSTRLEN]; - - h->server_read_task = NULL; - GNUNET_assert (NULL != - inet_ntop (AF_INET, &h->internal_address, ia, sizeof(ia))); - /* Start the server process */ - binary = GNUNET_OS_get_suid_binary_path (h->cfg, "gnunet-helper-nat-server"); - if (GNUNET_YES != GNUNET_OS_check_helper_binary (binary, GNUNET_YES, ia)) - { - /* move instantly to max delay, as this is unlikely to be fixed */ - h->server_retry_delay = GNUNET_TIME_STD_EXPONENTIAL_BACKOFF_THRESHOLD; - GNUNET_free (binary); - try_again (h); - return; - } - h->server_stdout = - GNUNET_DISK_pipe (GNUNET_DISK_PF_BLOCKING_RW); - if (NULL == h->server_stdout) - { - GNUNET_log_strerror (GNUNET_ERROR_TYPE_ERROR, "pipe"); - GNUNET_free (binary); - try_again (h); - return; - } - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "Starting `%s' at `%s'\n", - "gnunet-helper-nat-server", - ia); - h->server_proc = GNUNET_OS_start_process (GNUNET_OS_INHERIT_STD_NONE, - NULL, - h->server_stdout, - NULL, - binary, - "gnunet-helper-nat-server", - ia, - NULL); - GNUNET_free (binary); - if (NULL == h->server_proc) - { - GNUNET_log (GNUNET_ERROR_TYPE_WARNING, - _ ("Failed to start %s\n"), - "gnunet-helper-nat-server"); - GNUNET_DISK_pipe_close (h->server_stdout); - h->server_stdout = NULL; - try_again (h); - return; - } - /* Close the write end of the read pipe */ - GNUNET_DISK_pipe_close_end (h->server_stdout, GNUNET_DISK_PIPE_END_WRITE); - h->server_stdout_handle = - GNUNET_DISK_pipe_handle (h->server_stdout, GNUNET_DISK_PIPE_END_READ); - h->server_read_task = - GNUNET_SCHEDULER_add_read_file (GNUNET_TIME_UNIT_FOREVER_REL, - h->server_stdout_handle, - &nat_server_read, - h); -} - - -struct HelperContext * -GN_start_gnunet_nat_server_ (const struct in_addr *internal_address, - GN_ReversalCallback cb, - void *cb_cls, - const struct GNUNET_CONFIGURATION_Handle *cfg) -{ - struct HelperContext *h; - - h = GNUNET_new (struct HelperContext); - h->cb = cb; - h->cb_cls = cb_cls; - h->internal_address = *internal_address; - h->cfg = cfg; - restart_nat_server (h); - if (NULL == h->server_stdout) - { - GN_stop_gnunet_nat_server_ (h); - return NULL; - } - return h; -} - - -/** - * Start the gnunet-helper-nat-server and process incoming - * requests. - * - * @param h helper context to stop - */ -void -GN_stop_gnunet_nat_server_ (struct HelperContext *h) -{ - if (NULL != h->server_read_task) - { - GNUNET_SCHEDULER_cancel (h->server_read_task); - h->server_read_task = NULL; - } - if (NULL != h->server_proc) - { - if (0 != GNUNET_OS_process_kill (h->server_proc, GNUNET_TERM_SIG)) - GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING, "kill"); - GNUNET_OS_process_wait (h->server_proc); - GNUNET_OS_process_destroy (h->server_proc); - h->server_proc = NULL; - GNUNET_DISK_pipe_close (h->server_stdout); - h->server_stdout = NULL; - h->server_stdout_handle = NULL; - } - if (NULL != h->server_stdout) - { - GNUNET_DISK_pipe_close (h->server_stdout); - h->server_stdout = NULL; - h->server_stdout_handle = NULL; - } - GNUNET_free (h); -} - - -/** - * We want to connect to a peer that is behind NAT. Run the - * gnunet-helper-nat-client to send dummy ICMP responses to cause - * that peer to connect to us (connection reversal). - * - * @param internal_address out internal address to use - * @param internal_port port to use - * @param remote_v4 the address of the peer (IPv4-only) - * @param cfg handle to the GNUnet configuration - * @return #GNUNET_SYSERR on error, - * #GNUNET_OK otherwise - */ -int -GN_request_connection_reversal (const struct in_addr *internal_address, - uint16_t internal_port, - const struct in_addr *remote_v4, - const struct GNUNET_CONFIGURATION_Handle *cfg) -{ - char intv4[INET_ADDRSTRLEN]; - char remv4[INET_ADDRSTRLEN]; - char port_as_string[6]; - struct GNUNET_OS_Process *proc; - char *binary; - - if (NULL == inet_ntop (AF_INET, internal_address, intv4, INET_ADDRSTRLEN)) - { - GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING, "inet_ntop"); - return GNUNET_SYSERR; - } - if (NULL == inet_ntop (AF_INET, remote_v4, remv4, INET_ADDRSTRLEN)) - { - GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING, "inet_ntop"); - return GNUNET_SYSERR; - } - GNUNET_snprintf (port_as_string, - sizeof(port_as_string), - "%d", - internal_port); - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "Running gnunet-helper-nat-client %s %s %u\n", - intv4, - remv4, - internal_port); - binary = GNUNET_OS_get_suid_binary_path (cfg, "gnunet-helper-nat-client"); - proc = GNUNET_OS_start_process (GNUNET_OS_INHERIT_STD_NONE, - NULL, - NULL, - NULL, - binary, - "gnunet-helper-nat-client", - intv4, - remv4, - port_as_string, - NULL); - GNUNET_free (binary); - if (NULL == proc) - return GNUNET_SYSERR; - /* we know that the gnunet-helper-nat-client will terminate virtually - * instantly */ - GNUNET_OS_process_wait (proc); - GNUNET_OS_process_destroy (proc); - return GNUNET_OK; -} - - -/* end of gnunet-service-nat_helper.c */ diff --git a/src/nat/gnunet-service-nat_helper.h b/src/nat/gnunet-service-nat_helper.h deleted file mode 100644 index dc6b9ae61..000000000 --- a/src/nat/gnunet-service-nat_helper.h +++ /dev/null @@ -1,95 +0,0 @@ -/* - This file is part of GNUnet. - Copyright (C) 2009, 2010, 2011, 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 . - - SPDX-License-Identifier: AGPL3.0-or-later - */ - -/** - * @file nat/gnunet-service-nat_helper.h - * @brief runs the gnunet-helper-nat-server - * @author Milan Bouchet-Valat - * @author Christian Grothoff - */ -#include "platform.h" -#include "gnunet_util_lib.h" - - -/** - * Information we keep per NAT helper process. - */ -struct HelperContext; - - -/** - * Function called whenever we get a connection reversal - * request from another peer. - * - * @param cls closure - * @param ra IP address of the peer who wants us to connect to it - */ -typedef void -(*GN_ReversalCallback) (void *cls, - const struct sockaddr_in *ra); - - -/** - * Start the gnunet-helper-nat-server and process incoming - * requests. - * - * @param internal_address - * @param cb function to call if we receive a request - * @param cb_cls closure for @a cb - * @param cfg handle to the GNUnet configuration - * @return NULL on error - */ -struct HelperContext * -GN_start_gnunet_nat_server_ (const struct in_addr *internal_address, - GN_ReversalCallback cb, - void *cb_cls, - const struct GNUNET_CONFIGURATION_Handle *cfg); - - -/** - * Start the gnunet-helper-nat-server and process incoming - * requests. - * - * @param h helper context to stop - */ -void -GN_stop_gnunet_nat_server_ (struct HelperContext *h); - - -/** - * We want to connect to a peer that is behind NAT. Run the - * gnunet-helper-nat-client to send dummy ICMP responses to cause - * that peer to connect to us (connection reversal). - * - * @param internal_address out internal address to use - * @param internal_port internal port to use - * @param remote_v4 the address of the peer (IPv4-only) - * @param cfg handle to the GNUnet configuration - * @return #GNUNET_SYSERR on error, - * #GNUNET_OK otherwise - */ -int -GN_request_connection_reversal (const struct in_addr *internal_address, - uint16_t internal_port, - const struct in_addr *remote_v4, - const struct GNUNET_CONFIGURATION_Handle *cfg); - - -/* end of gnunet-service-nat_helper.h */ diff --git a/src/nat/gnunet-service-nat_mini.c b/src/nat/gnunet-service-nat_mini.c deleted file mode 100644 index 1e13e3814..000000000 --- a/src/nat/gnunet-service-nat_mini.c +++ /dev/null @@ -1,694 +0,0 @@ -/* - This file is part of GNUnet. - Copyright (C) 2011-2014, 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 . - - SPDX-License-Identifier: AGPL3.0-or-later - */ - -/** - * @file nat/gnunet-service-nat_mini.c - * @brief functions for interaction with miniupnp; tested with miniupnpc 1.5 - * @author Christian Grothoff - */ -#include "platform.h" -#include "gnunet_util_lib.h" -#include "gnunet_nat_service.h" -#include "gnunet-service-nat_mini.h" -#include "nat.h" - -#define LOG(kind, ...) GNUNET_log_from (kind, "nat", __VA_ARGS__) - -/** - * How long do we give upnpc to create a mapping? - */ -#define MAP_TIMEOUT GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS, 15) - -/** - * How long do we give upnpc to remove a mapping? - */ -#define UNMAP_TIMEOUT \ - GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS, 1) - -/** - * How often do we check for changes in the mapping? - */ -#define MAP_REFRESH_FREQ \ - GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_MINUTES, 5) - - -/* ************************* external-ip calling ************************ */ - -/** - * Opaque handle to cancel "GNUNET_NAT_mini_get_external_ipv4" operation. - */ -struct GNUNET_NAT_ExternalHandle -{ - /** - * Function to call with the result. - */ - GNUNET_NAT_IPCallback cb; - - /** - * Closure for @e cb. - */ - void *cb_cls; - - /** - * Read task. - */ - struct GNUNET_SCHEDULER_Task *task; - - /** - * Handle to `external-ip` process. - */ - struct GNUNET_OS_Process *eip; - - /** - * Handle to stdout pipe of `external-ip`. - */ - struct GNUNET_DISK_PipeHandle *opipe; - - /** - * Read handle of @e opipe. - */ - const struct GNUNET_DISK_FileHandle *r; - - /** - * Number of bytes in @e buf that are valid. - */ - size_t off; - - /** - * Destination of our read operation (output of 'external-ip'). - */ - char buf[17]; - - /** - * Error code for better debugging and user feedback - */ - enum GNUNET_NAT_StatusCode ret; -}; - - -/** - * Read the output of `external-ip` into `buf`. When complete, parse - * the address and call our callback. - * - * @param cls the `struct GNUNET_NAT_ExternalHandle` - */ -static void -read_external_ipv4 (void *cls) -{ - struct GNUNET_NAT_ExternalHandle *eh = cls; - ssize_t ret; - struct in_addr addr; - - eh->task = NULL; - ret = GNUNET_DISK_file_read (eh->r, - &eh->buf[eh->off], - sizeof(eh->buf) - eh->off); - if (ret > 0) - { - /* try to read more */ - eh->off += ret; - eh->task = GNUNET_SCHEDULER_add_read_file (GNUNET_TIME_UNIT_FOREVER_REL, - eh->r, - &read_external_ipv4, - eh); - return; - } - eh->ret = GNUNET_NAT_ERROR_EXTERNAL_IP_UTILITY_OUTPUT_INVALID; - if ((eh->off > 7) && (eh->buf[eh->off - 1] == '\n')) - { - eh->buf[eh->off - 1] = '\0'; - if (1 == inet_pton (AF_INET, eh->buf, &addr)) - { - if (0 == addr.s_addr) - eh->ret = - GNUNET_NAT_ERROR_EXTERNAL_IP_ADDRESS_INVALID; /* got 0.0.0.0 */ - else - eh->ret = GNUNET_NAT_ERROR_SUCCESS; - } - } - eh->cb (eh->cb_cls, - (GNUNET_NAT_ERROR_SUCCESS == eh->ret) ? &addr : NULL, - eh->ret); - GNUNET_NAT_mini_get_external_ipv4_cancel_ (eh); -} - - -/** - * (Asynchronously) signal error invoking `external-ip` to client. - * - * @param cls the `struct GNUNET_NAT_ExternalHandle` (freed) - */ -static void -signal_external_ip_error (void *cls) -{ - struct GNUNET_NAT_ExternalHandle *eh = cls; - - eh->task = NULL; - eh->cb (eh->cb_cls, NULL, eh->ret); - GNUNET_free (eh); -} - - -/** - * Try to get the external IPv4 address of this peer. - * - * @param cb function to call with result - * @param cb_cls closure for @a cb - * @return handle for cancellation (can only be used until @a cb is called), never NULL - */ -struct GNUNET_NAT_ExternalHandle * -GNUNET_NAT_mini_get_external_ipv4_ (GNUNET_NAT_IPCallback cb, void *cb_cls) -{ - struct GNUNET_NAT_ExternalHandle *eh; - - eh = GNUNET_new (struct GNUNET_NAT_ExternalHandle); - eh->cb = cb; - eh->cb_cls = cb_cls; - eh->ret = GNUNET_NAT_ERROR_SUCCESS; - if (GNUNET_SYSERR == - GNUNET_OS_check_helper_binary ("external-ip", GNUNET_NO, NULL)) - { - LOG (GNUNET_ERROR_TYPE_INFO, _ ("`external-ip' command not found\n")); - eh->ret = GNUNET_NAT_ERROR_EXTERNAL_IP_UTILITY_NOT_FOUND; - eh->task = GNUNET_SCHEDULER_add_now (&signal_external_ip_error, eh); - return eh; - } - LOG (GNUNET_ERROR_TYPE_DEBUG, - "Running `external-ip' to determine our external IP\n"); - eh->opipe = GNUNET_DISK_pipe (GNUNET_DISK_PF_BLOCKING_RW); - if (NULL == eh->opipe) - { - eh->ret = GNUNET_NAT_ERROR_IPC_FAILURE; - eh->task = GNUNET_SCHEDULER_add_now (&signal_external_ip_error, eh); - return eh; - } - eh->eip = GNUNET_OS_start_process (GNUNET_OS_INHERIT_STD_NONE, - NULL, - eh->opipe, - NULL, - "external-ip", - "external-ip", - NULL); - if (NULL == eh->eip) - { - GNUNET_DISK_pipe_close (eh->opipe); - eh->ret = GNUNET_NAT_ERROR_EXTERNAL_IP_UTILITY_FAILED; - eh->task = GNUNET_SCHEDULER_add_now (&signal_external_ip_error, eh); - return eh; - } - GNUNET_DISK_pipe_close_end (eh->opipe, GNUNET_DISK_PIPE_END_WRITE); - eh->r = GNUNET_DISK_pipe_handle (eh->opipe, GNUNET_DISK_PIPE_END_READ); - eh->task = GNUNET_SCHEDULER_add_read_file (GNUNET_TIME_UNIT_FOREVER_REL, - eh->r, - &read_external_ipv4, - eh); - return eh; -} - - -/** - * Cancel operation. - * - * @param eh operation to cancel - */ -void -GNUNET_NAT_mini_get_external_ipv4_cancel_ (struct GNUNET_NAT_ExternalHandle *eh) -{ - if (NULL != eh->eip) - { - (void) GNUNET_OS_process_kill (eh->eip, SIGKILL); - GNUNET_break (GNUNET_OK == GNUNET_OS_process_wait (eh->eip)); - GNUNET_OS_process_destroy (eh->eip); - } - if (NULL != eh->opipe) - { - GNUNET_DISK_pipe_close (eh->opipe); - eh->opipe = NULL; - } - if (NULL != eh->task) - { - GNUNET_SCHEDULER_cancel (eh->task); - eh->task = NULL; - } - GNUNET_free (eh); -} - - -/* ************************* upnpc calling ************************ */ - - -/** - * Handle to a mapping created with upnpc. - */ -struct GNUNET_NAT_MiniHandle -{ - /** - * Function to call on mapping changes. - */ - GNUNET_NAT_MiniAddressCallback ac; - - /** - * Closure for @e ac. - */ - void *ac_cls; - - /** - * Command used to install the map. - */ - struct GNUNET_OS_CommandHandle *map_cmd; - - /** - * Command used to refresh our map information. - */ - struct GNUNET_OS_CommandHandle *refresh_cmd; - - /** - * Command used to remove the mapping. - */ - struct GNUNET_OS_CommandHandle *unmap_cmd; - - /** - * Our current external mapping (if we have one). - */ - struct sockaddr_in current_addr; - - /** - * We check the mapping periodically to see if it - * still works. This task triggers the check. - */ - struct GNUNET_SCHEDULER_Task *refresh_task; - - /** - * Are we mapping TCP or UDP? - */ - int is_tcp; - - /** - * Did we succeed with creating a mapping? - */ - int did_map; - - /** - * Did we find our mapping during refresh scan? - */ - int found; - - /** - * Which port are we mapping? - */ - uint16_t port; -}; - - -/** - * Run "upnpc -l" to find out if our mapping changed. - * - * @param cls the `struct GNUNET_NAT_MiniHandle` - */ -static void -do_refresh (void *cls); - - -/** - * Process the output from the "upnpc -r" command. - * - * @param cls the `struct GNUNET_NAT_MiniHandle` - * @param line line of output, NULL at the end - */ -static void -process_map_output (void *cls, const char *line); - - -/** - * Run "upnpc -r" to map our internal port. - * - * @param mini our handle - */ -static void -run_upnpc_r (struct GNUNET_NAT_MiniHandle *mini) -{ - char pstr[6]; - - GNUNET_snprintf (pstr, sizeof(pstr), "%u", (unsigned int) mini->port); - mini->map_cmd = GNUNET_OS_command_run (&process_map_output, - mini, - MAP_TIMEOUT, - "upnpc", - "upnpc", - "-r", - pstr, - mini->is_tcp ? "tcp" : "udp", - NULL); - if (NULL == mini->map_cmd) - { - mini->ac (mini->ac_cls, - GNUNET_SYSERR, - NULL, - 0, - GNUNET_NAT_ERROR_UPNPC_FAILED); - return; - } -} - - -/** - * Process the output from "upnpc -l" to see if our - * external mapping changed. If so, do the notifications. - * - * @param cls the `struct GNUNET_NAT_MiniHandle` - * @param line line of output, NULL at the end - */ -static void -process_refresh_output (void *cls, const char *line) -{ - struct GNUNET_NAT_MiniHandle *mini = cls; - char pstr[9]; - const char *s; - unsigned int nport; - struct in_addr exip; - - if (NULL == line) - { - GNUNET_OS_command_stop (mini->refresh_cmd); - mini->refresh_cmd = NULL; - if (GNUNET_NO == mini->found) - { - /* mapping disappeared, try to re-create */ - if (GNUNET_YES == mini->did_map) - { - mini->ac (mini->ac_cls, - GNUNET_NO, - (const struct sockaddr *) &mini->current_addr, - sizeof(mini->current_addr), - GNUNET_NAT_ERROR_SUCCESS); - mini->did_map = GNUNET_NO; - } - run_upnpc_r (mini); - } - return; - } - if (! mini->did_map) - return; /* never mapped, won't find our mapping anyway */ - - /* we're looking for output of the form: - * "ExternalIPAddress = 12.134.41.124" */ - - s = strstr (line, "ExternalIPAddress = "); - if (NULL != s) - { - s += strlen ("ExternalIPAddress = "); - if (1 != inet_pton (AF_INET, s, &exip)) - return; /* skip */ - if (exip.s_addr == mini->current_addr.sin_addr.s_addr) - return; /* no change */ - /* update mapping */ - mini->ac (mini->ac_cls, - GNUNET_NO, - (const struct sockaddr *) &mini->current_addr, - sizeof(mini->current_addr), - GNUNET_NAT_ERROR_SUCCESS); - mini->current_addr.sin_addr = exip; - mini->ac (mini->ac_cls, - GNUNET_YES, - (const struct sockaddr *) &mini->current_addr, - sizeof(mini->current_addr), - GNUNET_NAT_ERROR_SUCCESS); - return; - } - /* - * we're looking for output of the form: - * - * "0 TCP 3000->192.168.2.150:3000 'libminiupnpc' ''" - * "1 UDP 3001->192.168.2.150:3001 'libminiupnpc' ''" - * - * the pattern we look for is: - * - * "%s TCP PORT->STRING:OURPORT *" or - * "%s UDP PORT->STRING:OURPORT *" - */GNUNET_snprintf (pstr, sizeof(pstr), ":%u ", mini->port); - if (NULL == (s = strstr (line, "->"))) - return; /* skip */ - if (NULL == strstr (s, pstr)) - return; /* skip */ - if (1 != sscanf (line, - (mini->is_tcp) ? "%*u TCP %u->%*s:%*u %*s" - : "%*u UDP %u->%*s:%*u %*s", - &nport)) - return; /* skip */ - mini->found = GNUNET_YES; - if (nport == ntohs (mini->current_addr.sin_port)) - return; /* no change */ - - /* external port changed, update mapping */ - mini->ac (mini->ac_cls, - GNUNET_NO, - (const struct sockaddr *) &mini->current_addr, - sizeof(mini->current_addr), - GNUNET_NAT_ERROR_SUCCESS); - mini->current_addr.sin_port = htons ((uint16_t) nport); - mini->ac (mini->ac_cls, - GNUNET_YES, - (const struct sockaddr *) &mini->current_addr, - sizeof(mini->current_addr), - GNUNET_NAT_ERROR_SUCCESS); -} - - -static void -do_refresh (void *cls) -{ - struct GNUNET_NAT_MiniHandle *mini = cls; - int ac; - - mini->refresh_task = - GNUNET_SCHEDULER_add_delayed (MAP_REFRESH_FREQ, &do_refresh, mini); - LOG (GNUNET_ERROR_TYPE_DEBUG, - "Running `upnpc' to check if our mapping still exists\n"); - mini->found = GNUNET_NO; - ac = GNUNET_NO; - if (NULL != mini->map_cmd) - { - /* took way too long, abort it! */ - GNUNET_OS_command_stop (mini->map_cmd); - mini->map_cmd = NULL; - ac = GNUNET_YES; - } - if (NULL != mini->refresh_cmd) - { - /* took way too long, abort it! */ - GNUNET_OS_command_stop (mini->refresh_cmd); - mini->refresh_cmd = NULL; - ac = GNUNET_YES; - } - mini->refresh_cmd = GNUNET_OS_command_run (&process_refresh_output, - mini, - MAP_TIMEOUT, - "upnpc", - "upnpc", - "-l", - NULL); - if (GNUNET_YES == ac) - mini->ac (mini->ac_cls, - GNUNET_SYSERR, - NULL, - 0, - GNUNET_NAT_ERROR_UPNPC_TIMEOUT); -} - - -/** - * Process the output from the 'upnpc -r' command. - * - * @param cls the `struct GNUNET_NAT_MiniHandle` - * @param line line of output, NULL at the end - */ -static void -process_map_output (void *cls, const char *line) -{ - struct GNUNET_NAT_MiniHandle *mini = cls; - const char *ipaddr; - char *ipa; - const char *pstr; - unsigned int port; - - if (NULL == line) - { - GNUNET_OS_command_stop (mini->map_cmd); - mini->map_cmd = NULL; - if (GNUNET_YES != mini->did_map) - mini->ac (mini->ac_cls, - GNUNET_SYSERR, - NULL, - 0, - GNUNET_NAT_ERROR_UPNPC_PORTMAP_FAILED); - if (NULL == mini->refresh_task) - mini->refresh_task = - GNUNET_SCHEDULER_add_delayed (MAP_REFRESH_FREQ, &do_refresh, mini); - return; - } - /* - * The upnpc output we're after looks like this: - * - * "external 87.123.42.204:3000 TCP is redirected to internal 192.168.2.150:3000" - */if ((NULL == (ipaddr = strstr (line, " "))) || - (NULL == (pstr = strstr (ipaddr, ":"))) || - (1 != sscanf (pstr + 1, "%u", &port))) - { - return; /* skip line */ - } - ipa = GNUNET_strdup (ipaddr + 1); - strstr (ipa, ":")[0] = '\0'; - if (1 != inet_pton (AF_INET, ipa, &mini->current_addr.sin_addr)) - { - GNUNET_free (ipa); - return; /* skip line */ - } - GNUNET_free (ipa); - - mini->current_addr.sin_port = htons (port); - mini->current_addr.sin_family = AF_INET; -#if HAVE_SOCKADDR_IN_SIN_LEN - mini->current_addr.sin_len = sizeof(struct sockaddr_in); -#endif - mini->did_map = GNUNET_YES; - mini->ac (mini->ac_cls, - GNUNET_YES, - (const struct sockaddr *) &mini->current_addr, - sizeof(mini->current_addr), - GNUNET_NAT_ERROR_SUCCESS); -} - - -/** - * Start mapping the given port using (mini)upnpc. This function - * should typically not be used directly (it is used within the - * general-purpose #GNUNET_NAT_register() code). However, it can be - * used if specifically UPnP-based NAT traversal is to be used or - * tested. - * - * @param port port to map - * @param is_tcp #GNUNET_YES to map TCP, #GNUNET_NO for UDP - * @param ac function to call with mapping result - * @param ac_cls closure for @a ac - * @return NULL on error (no 'upnpc' installed) - */ -struct GNUNET_NAT_MiniHandle * -GNUNET_NAT_mini_map_start (uint16_t port, - int is_tcp, - GNUNET_NAT_MiniAddressCallback ac, - void *ac_cls) -{ - struct GNUNET_NAT_MiniHandle *ret; - - if (GNUNET_SYSERR == GNUNET_OS_check_helper_binary ("upnpc", GNUNET_NO, NULL)) - { - LOG (GNUNET_ERROR_TYPE_INFO, _ ("`upnpc' command not found\n")); - ac (ac_cls, GNUNET_SYSERR, NULL, 0, GNUNET_NAT_ERROR_UPNPC_NOT_FOUND); - return NULL; - } - LOG (GNUNET_ERROR_TYPE_DEBUG, "Running `upnpc' to install mapping\n"); - ret = GNUNET_new (struct GNUNET_NAT_MiniHandle); - ret->ac = ac; - ret->ac_cls = ac_cls; - ret->is_tcp = is_tcp; - ret->port = port; - ret->refresh_task = - GNUNET_SCHEDULER_add_delayed (MAP_REFRESH_FREQ, &do_refresh, ret); - run_upnpc_r (ret); - return ret; -} - - -/** - * Process output from our 'unmap' command. - * - * @param cls the `struct GNUNET_NAT_MiniHandle` - * @param line line of output, NULL at the end - */ -static void -process_unmap_output (void *cls, const char *line) -{ - struct GNUNET_NAT_MiniHandle *mini = cls; - - if (NULL == line) - { - LOG (GNUNET_ERROR_TYPE_DEBUG, "UPnP unmap done\n"); - GNUNET_OS_command_stop (mini->unmap_cmd); - mini->unmap_cmd = NULL; - GNUNET_free (mini); - return; - } - /* we don't really care about the output... */ -} - - -void -GNUNET_NAT_mini_map_stop (struct GNUNET_NAT_MiniHandle *mini) -{ - char pstr[6]; - - if (NULL != mini->refresh_task) - { - GNUNET_SCHEDULER_cancel (mini->refresh_task); - mini->refresh_task = NULL; - } - if (NULL != mini->refresh_cmd) - { - GNUNET_OS_command_stop (mini->refresh_cmd); - mini->refresh_cmd = NULL; - } - if (NULL != mini->map_cmd) - { - GNUNET_OS_command_stop (mini->map_cmd); - mini->map_cmd = NULL; - } - if (GNUNET_NO == mini->did_map) - { - GNUNET_free (mini); - return; - } - mini->ac (mini->ac_cls, - GNUNET_NO, - (const struct sockaddr *) &mini->current_addr, - sizeof(mini->current_addr), - GNUNET_NAT_ERROR_SUCCESS); - /* Note: oddly enough, deletion uses the external port whereas - * addition uses the internal port; this rarely matters since they - * often are the same, but it might... */ - GNUNET_snprintf (pstr, - sizeof(pstr), - "%u", - (unsigned int) ntohs (mini->current_addr.sin_port)); - LOG (GNUNET_ERROR_TYPE_DEBUG, - "Unmapping port %u with UPnP\n", - ntohs (mini->current_addr.sin_port)); - mini->unmap_cmd = GNUNET_OS_command_run (&process_unmap_output, - mini, - UNMAP_TIMEOUT, - "upnpc", - "upnpc", - "-d", - pstr, - mini->is_tcp ? "tcp" : "udp", - NULL); -} - - -/* end of gnunet-service-nat_mini.c */ diff --git a/src/nat/gnunet-service-nat_mini.h b/src/nat/gnunet-service-nat_mini.h deleted file mode 100644 index dffc9758a..000000000 --- a/src/nat/gnunet-service-nat_mini.h +++ /dev/null @@ -1,128 +0,0 @@ -/* - This file is part of GNUnet. - Copyright (C) 2011-2014, 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 . - - SPDX-License-Identifier: AGPL3.0-or-later - */ - -/** - * @file nat/gnunet-service-nat_mini.c - * @brief functions for interaction with miniupnp; tested with miniupnpc 1.5 - * @author Christian Grothoff - */ -#ifndef GNUNET_SERVICE_NAT_MINI_H -#define GNUNET_SERVICE_NAT_MINI_H - - -/** - * Signature of a callback that is given an IP address. - * - * @param cls closure - * @param addr the address, NULL on errors - * @param result #GNUNET_NAT_ERROR_SUCCESS on success, otherwise the specific error code - */ -typedef void -(*GNUNET_NAT_IPCallback) (void *cls, - const struct in_addr *addr, - enum GNUNET_NAT_StatusCode result); - - -/** - * Opaque handle to cancel #GNUNET_NAT_mini_get_external_ipv4() operation. - */ -struct GNUNET_NAT_ExternalHandle; - - -/** - * Try to get the external IPv4 address of this peer. - * - * @param cb function to call with result - * @param cb_cls closure for @a cb - * @return handle for cancellation (can only be used until @a cb is called), NULL on error - */ -struct GNUNET_NAT_ExternalHandle * -GNUNET_NAT_mini_get_external_ipv4_ (GNUNET_NAT_IPCallback cb, - void *cb_cls); - - -/** - * Cancel operation. - * - * @param eh operation to cancel - */ -void -GNUNET_NAT_mini_get_external_ipv4_cancel_ (struct - GNUNET_NAT_ExternalHandle *eh); - - -/** - * Handle to a mapping created with upnpc. - */ -struct GNUNET_NAT_MiniHandle; - - -/** - * Signature of the callback passed to #GNUNET_NAT_register() for - * a function to call whenever our set of 'valid' addresses changes. - * - * @param cls closure - * @param add_remove #GNUNET_YES to mean the new public IP address, #GNUNET_NO to mean - * the previous (now invalid) one, #GNUNET_SYSERR indicates an error - * @param addr either the previous or the new public IP address - * @param addrlen actual length of the @a addr - * @param result #GNUNET_NAT_ERROR_SUCCESS on success, otherwise the specific error code - */ -typedef void -(*GNUNET_NAT_MiniAddressCallback) (void *cls, - int add_remove, - const struct sockaddr *addr, - socklen_t addrlen, - enum GNUNET_NAT_StatusCode result); - - -/** - * Start mapping the given port using (mini)upnpc. This function - * should typically not be used directly (it is used within the - * general-purpose #GNUNET_NAT_register() code). However, it can be - * used if specifically UPnP-based NAT traversal is to be used or - * tested. - * - * @param port port to map - * @param is_tcp #GNUNET_YES to map TCP, #GNUNET_NO for UDP - * @param ac function to call with mapping result - * @param ac_cls closure for @a ac - * @return NULL on error - */ -struct GNUNET_NAT_MiniHandle * -GNUNET_NAT_mini_map_start (uint16_t port, - int is_tcp, - GNUNET_NAT_MiniAddressCallback ac, - void *ac_cls); - - -/** - * Remove a mapping created with (mini)upnpc. Calling - * this function will give 'upnpc' 1s to remove the mapping, - * so while this function is non-blocking, a task will be - * left with the scheduler for up to 1s past this call. - * - * @param mini the handle - */ -void -GNUNET_NAT_mini_map_stop (struct GNUNET_NAT_MiniHandle *mini); - - -#endif diff --git a/src/nat/gnunet-service-nat_stun.c b/src/nat/gnunet-service-nat_stun.c deleted file mode 100644 index 203728ebf..000000000 --- a/src/nat/gnunet-service-nat_stun.c +++ /dev/null @@ -1,215 +0,0 @@ -/* - This file is part of GNUnet. - Copyright (C) 2009, 2015, 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 . - - SPDX-License-Identifier: AGPL3.0-or-later - */ -/** - * This code provides some support for doing STUN transactions. We - * receive the simplest possible packet as the STUN server and try - * to respond properly. - * - * All STUN packets start with a simple header made of a type, - * length (excluding the header) and a 16-byte random transaction id. - * Following the header we may have zero or more attributes, each - * structured as a type, length and a value (whose format depends - * on the type, but often contains addresses). - * Of course all fields are in network format. - * - * This code was based on ministun.c. - * - * @file nat/gnunet-service-nat_stun.c - * @brief Functions for STUN functionality - * @author Bruno Souza Cabral - */ - -#include "platform.h" -#include "gnunet_util_lib.h" -#include "nat_stun.h" - -#define LOG(kind, ...) GNUNET_log_from (kind, "stun", __VA_ARGS__) - - -/** - * Context for #stun_get_mapped(). - * Used to store state across processing attributes. - */ -struct StunState -{ - uint16_t attr; -}; - - -/** - * Extract the STUN_MAPPED_ADDRESS from the stun response. - * This is used as a callback for stun_handle_response - * when called from stun_request. - * - * @param[out] st pointer where we will set the type - * @param attr received stun attribute - * @param magic Magic cookie - * @param[out] arg pointer to a sockaddr_in where we will set the reported IP and port - * @return #GNUNET_OK if @a arg was initialized - */ -static int -stun_get_mapped (struct StunState *st, - const struct stun_attr *attr, - uint32_t magic, - struct sockaddr_in *arg) -{ - const struct stun_addr *returned_addr; - struct sockaddr_in *sa = (struct sockaddr_in *) arg; - uint16_t type = ntohs (attr->attr); - - switch (type) - { - case STUN_MAPPED_ADDRESS: - if ((st->attr == STUN_XOR_MAPPED_ADDRESS) || - (st->attr == STUN_MS_XOR_MAPPED_ADDRESS)) - return GNUNET_NO; - magic = 0; - break; - - case STUN_MS_XOR_MAPPED_ADDRESS: - if (st->attr == STUN_XOR_MAPPED_ADDRESS) - return GNUNET_NO; - break; - - case STUN_XOR_MAPPED_ADDRESS: - break; - - default: - return GNUNET_NO; - } - - if (ntohs (attr->len) < sizeof(struct stun_addr)) - return GNUNET_NO; - returned_addr = (const struct stun_addr *) (attr + 1); - if (AF_INET != returned_addr->family) - return GNUNET_NO; - st->attr = type; - sa->sin_family = AF_INET; - sa->sin_port = returned_addr->port ^ htons (ntohl (magic) >> 16); - sa->sin_addr.s_addr = returned_addr->addr ^ magic; - return GNUNET_OK; -} - - -/** - * Handle an incoming STUN response. Do some basic sanity checks on - * packet size and content, try to extract information. - * At the moment this only processes BIND requests, - * and returns the externally visible address of the original - * request. - * - * @param data the packet - * @param len the length of the packet in @a data - * @param[out] arg sockaddr_in where we will set our discovered address - * @return #GNUNET_OK on success, - * #GNUNET_NO if the packet is invalid (not a stun packet) - */ -int -GNUNET_NAT_stun_handle_packet_ (const void *data, - size_t len, - struct sockaddr_in *arg) -{ - const struct stun_header *hdr; - const struct stun_attr *attr; - struct StunState st; - uint32_t advertised_message_size; - uint32_t message_magic_cookie; - int ret = GNUNET_SYSERR; - - /* On entry, 'len' is the length of the UDP payload. After the - * initial checks it becomes the size of unprocessed options, - * while 'data' is advanced accordingly. - */ - if (len < sizeof(struct stun_header)) - { - LOG (GNUNET_ERROR_TYPE_DEBUG, - "Packet too short to be a STUN packet\n"); - return GNUNET_NO; - } - hdr = data; - /* Skip header as it is already in hdr */ - len -= sizeof(struct stun_header); - data += sizeof(struct stun_header); - - /* len as advertised in the message */ - advertised_message_size = ntohs (hdr->msglen); - message_magic_cookie = ntohl (hdr->magic); - /* Compare if the cookie match */ - if (STUN_MAGIC_COOKIE != message_magic_cookie) - { - LOG (GNUNET_ERROR_TYPE_DEBUG, - "Invalid magic cookie for STUN packet\n"); - return GNUNET_NO; - } - - LOG (GNUNET_ERROR_TYPE_INFO, - "STUN Packet, msg %s (%04x), length: %d\n", - stun_msg2str (ntohs (hdr->msgtype)), - ntohs (hdr->msgtype), - advertised_message_size); - if (advertised_message_size > len) - { - LOG (GNUNET_ERROR_TYPE_INFO, - "Scrambled STUN packet length (got %d, expecting %d)\n", - advertised_message_size, - (int) len); - return GNUNET_NO; - } - len = advertised_message_size; - memset (&st, 0, sizeof(st)); - - while (len > 0) - { - if (len < sizeof(struct stun_attr)) - { - LOG (GNUNET_ERROR_TYPE_INFO, - "Attribute too short (got %d, expecting %d)\n", - (int) len, - (int) sizeof(struct stun_attr)); - break; - } - attr = (const struct stun_attr *) data; - - /* compute total attribute length */ - advertised_message_size = ntohs (attr->len) + sizeof(struct stun_attr); - - /* Check if we still have space in our buffer */ - if (advertised_message_size > len) - { - LOG (GNUNET_ERROR_TYPE_INFO, - "Inconsistent attribute (length %d exceeds remaining msg len %d)\n", - advertised_message_size, - (int) len); - break; - } - if (GNUNET_OK == - stun_get_mapped (&st, - attr, - hdr->magic, - arg)) - ret = GNUNET_OK; - data += advertised_message_size; - len -= advertised_message_size; - } - return ret; -} - - -/* end of gnunet-service-nat_stun.c */ diff --git a/src/nat/gnunet-service-nat_stun.h b/src/nat/gnunet-service-nat_stun.h deleted file mode 100644 index 912fc3b46..000000000 --- a/src/nat/gnunet-service-nat_stun.h +++ /dev/null @@ -1,61 +0,0 @@ -/* - This file is part of GNUnet. - Copyright (C) 2009, 2015, 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 . - - SPDX-License-Identifier: AGPL3.0-or-later - */ -/** - * This code provides some support for doing STUN transactions. We - * receive the simplest possible packet as the STUN server and try - * to respond properly. - * - * All STUN packets start with a simple header made of a type, - * length (excluding the header) and a 16-byte random transaction id. - * Following the header we may have zero or more attributes, each - * structured as a type, length and a value (whose format depends - * on the type, but often contains addresses). - * Of course all fields are in network format. - * - * This code was based on ministun.c. - * - * @file nat/gnunet-service-nat_stun.h - * @brief Functions for STUN functionality - * @author Bruno Souza Cabral - */ -#ifndef GNUNET_SERVICE_NAT_STUN_H -#define GNUNET_SERVICE_NAT_STUN_H - -#include "platform.h" - -/** - * Handle an incoming STUN response. Do some basic sanity checks on - * packet size and content, try to extract information. - * At the moment this only processes BIND requests, - * and returns the externally visible address of the original - * request. - * - * @param data the packet - * @param len the length of the packet in @a data - * @param[out] arg sockaddr_in where we will set our discovered address - * @return #GNUNET_OK on success, - * #GNUNET_NO if the packet is invalid (not a stun packet) - */ -int -GNUNET_NAT_stun_handle_packet_ (const void *data, - size_t len, - struct sockaddr_in *arg); - -#endif diff --git a/src/nat/meson.build b/src/nat/meson.build deleted file mode 100644 index e629f6411..000000000 --- a/src/nat/meson.build +++ /dev/null @@ -1,75 +0,0 @@ -libgnunetnat_src = ['nat_api.c', - 'nat_api_stun.c'] - -gnunetservicenat_src = ['gnunet-service-nat.c', - 'gnunet-service-nat.c', - 'gnunet-service-nat_externalip.c', - 'gnunet-service-nat_stun.c', - 'gnunet-service-nat_mini.c', - 'gnunet-service-nat_helper.c'] - -configure_file(input : 'nat.conf.in', - output : 'nat.conf', - configuration : cdata, - install: true, - install_dir: pkgcfgdir) - - -if get_option('monolith') - foreach p : libgnunetnat_src + gnunetservicenat_src - gnunet_src += 'nat/' + p - endforeach - subdir_done() -endif - -libgnunetnat = library('gnunetnat', - libgnunetnat_src, - soversion: '2', - version: '2.0.0', - dependencies: libgnunetutil_dep, - include_directories: [incdir, configuration_inc], - install: true, - install_dir: get_option('libdir')) -libgnunetnat_dep = declare_dependency(link_with : libgnunetnat) -pkg.generate(libgnunetnat, url: 'https://www.gnunet.org', - description : 'Provides API for accessing the NAT service') -libgnunetnat = library('gnunetnatnew', - ['nat_api.c', - 'nat_api_stun.c'], - soversion: '2', - version: '2.0.0', - dependencies: libgnunetutil_dep, - include_directories: [incdir, configuration_inc], - install: true, - install_dir: get_option('libdir')) -libgnunetnat_dep = declare_dependency(link_with : libgnunetnat) - -executable ('gnunet-nat', - ['gnunet-nat.c'], - dependencies: [libgnunetnat_dep, libgnunetutil_dep], - include_directories: [incdir, configuration_inc], - install: true, - install_dir: get_option('bindir')) - -executable ('gnunet-service-nat', - gnunetservicenat_src, - dependencies: [libgnunetnat_dep, libgnunetutil_dep, - libgnunetstatistics_dep], - include_directories: [incdir, configuration_inc], - install: true, - install_dir: get_option('libdir')/'gnunet'/'libexec') -executable ('gnunet-helper-nat-server', - ['gnunet-helper-nat-server.c'], - dependencies: [libgnunetnat_dep, libgnunetutil_dep, - libgnunetstatistics_dep], - include_directories: [incdir, configuration_inc], - install: true, - install_dir: get_option('libdir')/'gnunet'/'libexec') -executable ('gnunet-helper-nat-client', - ['gnunet-helper-nat-client.c'], - dependencies: [libgnunetnat_dep, libgnunetutil_dep, - libgnunetstatistics_dep], - include_directories: [incdir, configuration_inc], - install: true, - install_dir: get_option('libdir')/'gnunet'/'libexec') - diff --git a/src/nat/nat.conf.in b/src/nat/nat.conf.in deleted file mode 100644 index 4c068c394..000000000 --- a/src/nat/nat.conf.in +++ /dev/null @@ -1,43 +0,0 @@ -[nat] -START_ON_DEMAND = @START_ON_DEMAND@ -@UNIXONLY@ PORT = 2121 -HOSTNAME = localhost -BINARY = gnunet-service-nat -ACCEPT_FROM = 127.0.0.1; -ACCEPT_FROM6 = ::1; -UNIXPATH = $GNUNET_RUNTIME_DIR/gnunet-service-nat.sock -UNIX_MATCH_UID = YES -UNIX_MATCH_GID = YES - -# Enable UPNP by default? -ENABLE_UPNP = YES - -# Enable scanning for all system IP addresses? -ENABLE_IPSCAN = YES - -# Disable IPv6 support -# FIXME: move entirely to transport plugins! -DISABLEV6 = NO - -# How often do we query the DNS resolver -# for our hostname (to get our own IP) -HOSTNAME_DNS_FREQUENCY = 20 min - -# How often do we iterate over our -# network interfaces to check for changes -# in our IP address? -IFC_SCAN_FREQUENCY = 15 min - -# How often do we query the DNS resolver -# for our hostname (to get our own IP) -DYNDNS_FREQUENCY = 7 min - -# SHOULD USE STUN ? -USE_STUN = YES -STUN_FREQUENCY = 5 min -# Default list of stun servers -STUN_SERVERS = stun.gnunet.org stun.services.mozilla.com:3478 stun.ekiga.net:3478 - -# After how long do we consider STUN data stale? -STUN_STALE = 60 min - diff --git a/src/nat/nat.h b/src/nat/nat.h deleted file mode 100644 index 1d8648aaf..000000000 --- a/src/nat/nat.h +++ /dev/null @@ -1,223 +0,0 @@ -/* - This file is part of GNUnet. - Copyright (C) 2011, 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 . - - SPDX-License-Identifier: AGPL3.0-or-later - */ - -/** - * @file src/nat/nat.h - * @brief Messages for interaction with gnunet-nat-server and gnunet-nat-service - * @author Christian Grothoff - * - */ -#ifndef NAT_H -#define NAT_H -#include "gnunet_util_lib.h" - - -GNUNET_NETWORK_STRUCT_BEGIN - -/** - * Request to test NAT traversal, sent to the gnunet-nat-server - * (not the service!). - */ -struct GNUNET_NAT_TestMessage -{ - /** - * Header with type #GNUNET_MESSAGE_TYPE_NAT_TEST - */ - struct GNUNET_MessageHeader header; - - /** - * IPv4 target IP address - */ - uint32_t dst_ipv4; - - /** - * Port to use, 0 to send dummy ICMP response. - */ - uint16_t dport; - - /** - * Data to send OR advertised-port (in NBO) to use for dummy ICMP. - */ - uint16_t data; - - /** - * #GNUNET_YES for TCP, #GNUNET_NO for UDP. - */ - int32_t is_tcp; -}; - - -/** - * Flags specifying the events this client would be - * interested in being told about. - */ -enum GNUNET_NAT_RegisterFlags -{ - /** - * This client does not want any notifications. - */ - GNUNET_NAT_RF_NONE = 0, - - /** - * This client wants to be informed about changes to our - * applicable addresses. - */ - GNUNET_NAT_RF_ADDRESSES = 1, - - /** - * This client supports address reversal. - */ - GNUNET_NAT_RF_REVERSAL = 2 -}; - - -/** - * Message sent by a client to register with its addresses. - */ -struct GNUNET_NAT_RegisterMessage -{ - /** - * Header with type #GNUNET_MESSAGE_TYPE_NAT_REGISTER - */ - struct GNUNET_MessageHeader header; - - /** - * An `enum GNUNET_NAT_RegisterFlags`. - */ - uint8_t flags; - - /** - * Client's IPPROTO, e.g. IPPROTO_UDP or IPPROTO_TCP. - */ - uint8_t proto; - - /** - * Number of bytes in the string that follow which - * specifies a section name in the configuration. - */ - uint16_t str_len GNUNET_PACKED; - - /** - * Number of addresses that this service is bound to that follow. - * Given as an array of "struct sockaddr" entries, the size of - * each entry being determined by the "sa_family" at the beginning. - */ - uint16_t num_addrs GNUNET_PACKED; - - /* Followed by @e num_addrs addresses of type 'struct - sockaddr' */ - - /* Followed by @e str_len section name to use for options */ -}; - - -/** - * Client telling the service to (possibly) handle a STUN message. - */ -struct GNUNET_NAT_HandleStunMessage -{ - /** - * Header with type #GNUNET_MESSAGE_TYPE_NAT_HANDLE_STUN - */ - struct GNUNET_MessageHeader header; - - /** - * Size of the sender address included, in NBO. - */ - uint16_t sender_addr_size; - - /** - * Number of bytes of payload included, in NBO. - */ - uint16_t payload_size; - - /* followed by a `struct sockaddr` of @e sender_addr_size bytes */ - - /* followed by payload with @e payload_size bytes */ -}; - - -/** - * Client asking the service to initiate connection reversal. - */ -struct GNUNET_NAT_RequestConnectionReversalMessage -{ - /** - * Header with type #GNUNET_MESSAGE_TYPE_NAT_REQUEST_CONNECTION_REVERSAL - */ - struct GNUNET_MessageHeader header; - - /** - * Size of the local address included, in NBO. - */ - uint16_t local_addr_size; - - /** - * Size of the remote address included, in NBO. - */ - uint16_t remote_addr_size; - - /* followed by a `struct sockaddr` of @e local_addr_size bytes */ - - /* followed by a `struct sockaddr` of @e remote_addr_size bytes */ -}; - - -/** - * Service telling a client that connection reversal was requested. - */ -struct GNUNET_NAT_ConnectionReversalRequestedMessage -{ - /** - * Header with type #GNUNET_MESSAGE_TYPE_NAT_CONNECTION_REVERSAL_REQUESTED - */ - struct GNUNET_MessageHeader header; - - /* followed by a `struct sockaddr_in` */ -}; - - -/** - * Service notifying the client about changes in the set of - * addresses it has. - */ -struct GNUNET_NAT_AddressChangeNotificationMessage -{ - /** - * Header with type #GNUNET_MESSAGE_TYPE_NAT_ADDRESS_CHANGE - */ - struct GNUNET_MessageHeader header; - - /** - * #GNUNET_YES to add, #GNUNET_NO to remove the address from the list. - */ - int32_t add_remove GNUNET_PACKED; - - /** - * Type of the address, an `enum GNUNET_NAT_AddressClass` in NBO. - */ - uint32_t addr_class GNUNET_PACKED; - /* followed by a `struct sockaddr` */ -}; - - -GNUNET_NETWORK_STRUCT_END - -#endif diff --git a/src/nat/nat_api.c b/src/nat/nat_api.c deleted file mode 100644 index 31f8f388d..000000000 --- a/src/nat/nat_api.c +++ /dev/null @@ -1,702 +0,0 @@ -/* - This file is part of GNUnet. - Copyright (C) 2007-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 . - - SPDX-License-Identifier: AGPL3.0-or-later - */ - -/** - * @author Christian Grothoff - * @author Milan Bouchet-Valat - * - * @file nat/nat_api.c - * Service for handling UPnP and NAT-PMP port forwarding - * and external IP address retrieval - */ -#include "platform.h" -#include "gnunet_nat_service.h" -#include "nat.h" -#include "nat_stun.h" - - -/** - * Entry in DLL of addresses of this peer. - */ -struct AddrEntry -{ - /** - * DLL. - */ - struct AddrEntry *next; - - /** - * DLL. - */ - struct AddrEntry *prev; - - /** - * Place where the application can store data (on add, - * and retrieve on remove). - */ - void *app_ctx; - - /** - * Address class of the address. - */ - enum GNUNET_NAT_AddressClass ac; - - /** - * Number of bytes that follow. - */ - socklen_t addrlen; -}; - - -/** - * Handle for active NAT registrations. - */ -struct GNUNET_NAT_Handle -{ - /** - * Configuration we use. - */ - const struct GNUNET_CONFIGURATION_Handle *cfg; - - /** - * Message queue for communicating with the NAT service. - */ - struct GNUNET_MQ_Handle *mq; - - /** - * Our registration message. - */ - struct GNUNET_MessageHeader *reg; - - /** - * Head of address DLL. - */ - struct AddrEntry *ae_head; - - /** - * Tail of address DLL. - */ - struct AddrEntry *ae_tail; - - /** - * Function to call when our addresses change. - */ - GNUNET_NAT_AddressCallback address_callback; - - /** - * Function to call when another peer requests connection reversal. - */ - GNUNET_NAT_ReversalCallback reversal_callback; - - /** - * Closure for the various callbacks. - */ - void *callback_cls; - - /** - * Task scheduled to reconnect to the service. - */ - struct GNUNET_SCHEDULER_Task *reconnect_task; - - /** - * How long to wait until we reconnect. - */ - struct GNUNET_TIME_Relative reconnect_delay; -}; - - -/** - * Task to connect to the NAT service. - * - * @param cls our `struct GNUNET_NAT_Handle *` - */ -static void -do_connect (void *cls); - - -/** - * Task to connect to the NAT service. - * - * @param nh handle to reconnect - */ -static void -reconnect (struct GNUNET_NAT_Handle *nh) -{ - struct AddrEntry *ae; - - if (NULL != nh->mq) - { - GNUNET_MQ_destroy (nh->mq); - nh->mq = NULL; - } - while (NULL != (ae = nh->ae_head)) - { - GNUNET_CONTAINER_DLL_remove (nh->ae_head, nh->ae_tail, ae); - nh->address_callback (nh->callback_cls, - &ae->app_ctx, - GNUNET_NO, - ae->ac, - (const struct sockaddr *) &ae[1], - ae->addrlen); - GNUNET_free (ae); - } - nh->reconnect_delay = GNUNET_TIME_STD_BACKOFF (nh->reconnect_delay); - nh->reconnect_task = - GNUNET_SCHEDULER_add_delayed (nh->reconnect_delay, &do_connect, nh); -} - - -/** - * Check connection reversal request. - * - * @param cls our `struct GNUNET_NAT_Handle` - * @param crm the message - * @return #GNUNET_OK if @a crm is well-formed - */ -static int -check_connection_reversal_request ( - void *cls, - const struct GNUNET_NAT_ConnectionReversalRequestedMessage *crm) -{ - if (ntohs (crm->header.size) != sizeof(*crm) + sizeof(struct sockaddr_in)) - { - GNUNET_break (0); - return GNUNET_SYSERR; - } - return GNUNET_OK; -} - - -/** - * Handle connection reversal request. - * - * @param cls our `struct GNUNET_NAT_Handle` - * @param crm the message - */ -static void -handle_connection_reversal_request ( - void *cls, - const struct GNUNET_NAT_ConnectionReversalRequestedMessage *crm) -{ - struct GNUNET_NAT_Handle *nh = cls; - - nh->reversal_callback (nh->callback_cls, - (const struct sockaddr *) &crm[1], - sizeof(struct sockaddr_in)); -} - - -/** - * Check address change notification. - * - * @param cls our `struct GNUNET_NAT_Handle` - * @param acn the message - * @return #GNUNET_OK if @a crm is well-formed - */ -static int -check_address_change_notification ( - void *cls, - const struct GNUNET_NAT_AddressChangeNotificationMessage *acn) -{ - size_t alen = ntohs (acn->header.size) - sizeof(*acn); - - switch (alen) - { - case sizeof(struct sockaddr_in): { - const struct sockaddr_in *s4 = (const struct sockaddr_in *) &acn[1]; - if (AF_INET != s4->sin_family) - { - GNUNET_break (0); - return GNUNET_SYSERR; - } - } - break; - - case sizeof(struct sockaddr_in6): { - const struct sockaddr_in6 *s6 = (const struct sockaddr_in6 *) &acn[1]; - if (AF_INET6 != s6->sin6_family) - { - GNUNET_break (0); - return GNUNET_SYSERR; - } - } - break; - - default: - GNUNET_break (0); - return GNUNET_SYSERR; - } - return GNUNET_OK; -} - - -/** - * Handle connection reversal request. - * - * @param cls our `struct GNUNET_NAT_Handle` - * @param acn the message - */ -static void -handle_address_change_notification ( - void *cls, - const struct GNUNET_NAT_AddressChangeNotificationMessage *acn) -{ - struct GNUNET_NAT_Handle *nh = cls; - size_t alen = ntohs (acn->header.size) - sizeof(*acn); - const struct sockaddr *sa = (const struct sockaddr *) &acn[1]; - enum GNUNET_NAT_AddressClass ac; - struct AddrEntry *ae; - - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "Received address change notification\n"); - ac = (enum GNUNET_NAT_AddressClass) ntohl (acn->addr_class); - if (GNUNET_YES == ntohl (acn->add_remove)) - { - ae = GNUNET_malloc (sizeof(*ae) + alen); - ae->ac = ac; - ae->addrlen = alen; - GNUNET_memcpy (&ae[1], sa, alen); - GNUNET_CONTAINER_DLL_insert (nh->ae_head, nh->ae_tail, ae); - nh->address_callback (nh->callback_cls, - &ae->app_ctx, - ntohl (acn->add_remove), - ac, - sa, - alen); - } - else - { - for (ae = nh->ae_head; NULL != ae; ae = ae->next) - if ((ae->addrlen == alen) && (0 == memcmp (&ae[1], sa, alen))) - break; - if (NULL == ae) - { - GNUNET_break (0); - reconnect (nh); - return; - } - GNUNET_CONTAINER_DLL_remove (nh->ae_head, nh->ae_tail, ae); - nh->address_callback (nh->callback_cls, - &ae->app_ctx, - ntohl (acn->add_remove), - ac, - sa, - alen); - GNUNET_free (ae); - } -} - - -/** - * Handle queue errors by reconnecting to NAT. - * - * @param cls the `struct GNUNET_NAT_Handle *` - * @param error details about the error - */ -static void -mq_error_handler (void *cls, - enum GNUNET_MQ_Error error) -{ - struct GNUNET_NAT_Handle *nh = cls; - - reconnect (nh); -} - - -/** - * Task to connect to the NAT service. - * - * @param cls our `struct GNUNET_NAT_Handle *` - */ -static void -do_connect (void *cls) -{ - struct GNUNET_NAT_Handle *nh = cls; - struct GNUNET_MQ_MessageHandler handlers[] = { - GNUNET_MQ_hd_var_size ( - connection_reversal_request, - GNUNET_MESSAGE_TYPE_NAT_CONNECTION_REVERSAL_REQUESTED, - struct GNUNET_NAT_ConnectionReversalRequestedMessage, - nh), - GNUNET_MQ_hd_var_size ( - address_change_notification, - GNUNET_MESSAGE_TYPE_NAT_ADDRESS_CHANGE, - struct GNUNET_NAT_AddressChangeNotificationMessage, - nh), - GNUNET_MQ_handler_end () - }; - struct GNUNET_MQ_Envelope *env; - - nh->reconnect_task = NULL; - nh->mq = - GNUNET_CLIENT_connect (nh->cfg, - "nat", - handlers, - &mq_error_handler, - nh); - if (NULL == nh->mq) - { - reconnect (nh); - return; - } - env = GNUNET_MQ_msg_copy (nh->reg); - GNUNET_MQ_send (nh->mq, - env); -} - - -struct GNUNET_NAT_Handle * -GNUNET_NAT_register (const struct GNUNET_CONFIGURATION_Handle *cfg, - const char *config_section, - uint8_t proto, - unsigned int num_addrs, - const struct sockaddr **addrs, - const socklen_t *addrlens, - GNUNET_NAT_AddressCallback address_callback, - GNUNET_NAT_ReversalCallback reversal_callback, - void *callback_cls) -{ - struct GNUNET_NAT_Handle *nh; - struct GNUNET_NAT_RegisterMessage *rm; - size_t len; - size_t str_len; - char *off; - - len = 0; - for (unsigned int i = 0; i < num_addrs; i++) - len += addrlens[i]; - str_len = strlen (config_section) + 1; - len += str_len; - if ( (len > GNUNET_MAX_MESSAGE_SIZE - sizeof(*rm)) || - (num_addrs > UINT16_MAX) || - (str_len > UINT16_MAX) ) - { - GNUNET_break (0); - return NULL; - } - rm = GNUNET_malloc (sizeof(*rm) + len); - rm->header.size = htons (sizeof(*rm) + len); - rm->header.type = htons (GNUNET_MESSAGE_TYPE_NAT_REGISTER); - rm->flags = GNUNET_NAT_RF_NONE; - if (NULL != address_callback) - rm->flags |= GNUNET_NAT_RF_ADDRESSES; - if (NULL != reversal_callback) - rm->flags |= GNUNET_NAT_RF_REVERSAL; - rm->proto = proto; - rm->str_len = htons (str_len); - rm->num_addrs = htons ((uint16_t) num_addrs); - off = (char *) &rm[1]; - for (unsigned int i = 0; i < num_addrs; i++) - { - switch (addrs[i]->sa_family) - { - case AF_INET: - if (sizeof(struct sockaddr_in) != addrlens[i]) - { - GNUNET_break (0); - GNUNET_free (rm); - return NULL; - } - break; - - case AF_INET6: - if (sizeof(struct sockaddr_in6) != addrlens[i]) - { - GNUNET_break (0); - GNUNET_free (rm); - return NULL; - } - break; - -#if AF_UNIX - case AF_UNIX: - if (sizeof(struct sockaddr_un) != addrlens[i]) - { - GNUNET_break (0); - GNUNET_free (rm); - return NULL; - } - break; -#endif - default: - GNUNET_break (0); - GNUNET_free (rm); - return NULL; - } - GNUNET_memcpy (off, addrs[i], addrlens[i]); - off += addrlens[i]; - } - GNUNET_memcpy (off, config_section, str_len); - - nh = GNUNET_new (struct GNUNET_NAT_Handle); - nh->reg = &rm->header; - nh->cfg = cfg; - nh->address_callback = address_callback; - nh->reversal_callback = reversal_callback; - nh->callback_cls = callback_cls; - do_connect (nh); - return nh; -} - - -/** - * Check if an incoming message is a STUN message. - * - * @param data the packet - * @param len the length of the packet in @a data - * @return #GNUNET_YES if @a data is a STUN packet, - * #GNUNET_NO if the packet is invalid (not a stun packet) - */ -static enum GNUNET_GenericReturnValue -test_stun_packet (const void *data, size_t len) -{ - const struct stun_header *hdr; - const struct stun_attr *attr; - uint32_t advertised_message_size; - uint32_t message_magic_cookie; - - /* On entry, 'len' is the length of the UDP payload. After the - * initial checks it becomes the size of unprocessed options, - * while 'data' is advanced accordingly. - */ - if (len < sizeof(struct stun_header)) - { - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "STUN packet too short (only %d, wanting at least %d)\n", - (int) len, - (int) sizeof(struct stun_header)); - return GNUNET_NO; - } - hdr = (const struct stun_header *) data; - /* Skip header as it is already in hdr */ - len -= sizeof(struct stun_header); - data += sizeof(struct stun_header); - - /* len as advertised in the message */ - advertised_message_size = ntohs (hdr->msglen); - - message_magic_cookie = ntohl (hdr->magic); - /* Compare if the cookie match */ - if (STUN_MAGIC_COOKIE != message_magic_cookie) - { - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Invalid magic cookie for STUN\n"); - return GNUNET_NO; - } - - if (advertised_message_size > len) - { - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "Scrambled STUN packet length (got %d, expecting %d)\n", - advertised_message_size, - (int) len); - return GNUNET_NO; - } - len = advertised_message_size; - while (len > 0) - { - if (len < sizeof(struct stun_attr)) - { - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "Attribute too short in STUN packet (got %d, expecting %d)\n", - (int) len, - (int) sizeof(struct stun_attr)); - return GNUNET_NO; - } - attr = (const struct stun_attr *) data; - - /* compute total attribute length */ - advertised_message_size = ntohs (attr->len) + sizeof(struct stun_attr); - - /* Check if we still have space in our buffer */ - if (advertised_message_size > len) - { - GNUNET_log ( - GNUNET_ERROR_TYPE_DEBUG, - "Inconsistent Attribute (length %d exceeds remaining msg len %d)\n", - advertised_message_size, - (int) len); - return GNUNET_NO; - } - data += advertised_message_size; - len -= advertised_message_size; - } - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "STUN Packet, msg %04x, length: %d\n", - ntohs (hdr->msgtype), - advertised_message_size); - return GNUNET_OK; -} - - -/** - * Handle an incoming STUN message. This function is useful as - * some GNUnet service may be listening on a UDP port and might - * thus receive STUN messages while trying to receive other data. - * In this case, this function can be used to process replies - * to STUN requests. - * - * The function does some basic sanity checks on packet size and - * content, try to extract a bit of information. - * - * At the moment this only processes BIND requests, and returns the - * externally visible address of the request to the rest of the - * NAT logic. - * - * @param nh handle to the NAT service - * @param sender_addr address from which we got @a data - * @param sender_addr_len number of bytes in @a sender_addr - * @param data the packet - * @param data_size number of bytes in @a data - * @return #GNUNET_OK on success - * #GNUNET_NO if the packet is not a STUN packet - * #GNUNET_SYSERR on internal error handling the packet - */ -int -GNUNET_NAT_stun_handle_packet (struct GNUNET_NAT_Handle *nh, - const struct sockaddr *sender_addr, - size_t sender_addr_len, - const void *data, - size_t data_size) -{ - struct GNUNET_MQ_Envelope *env; - struct GNUNET_NAT_HandleStunMessage *hsn; - char *buf; - - if (GNUNET_YES != test_stun_packet (data, data_size)) - return GNUNET_NO; - if (NULL == nh->mq) - return GNUNET_SYSERR; - env = GNUNET_MQ_msg_extra (hsn, - data_size + sender_addr_len, - GNUNET_MESSAGE_TYPE_NAT_HANDLE_STUN); - hsn->sender_addr_size = htons ((uint16_t) sender_addr_len); - hsn->payload_size = htons ((uint16_t) data_size); - buf = (char *) &hsn[1]; - GNUNET_memcpy (buf, sender_addr, sender_addr_len); - buf += sender_addr_len; - GNUNET_memcpy (buf, data, data_size); - GNUNET_MQ_send (nh->mq, env); - return GNUNET_OK; -} - - -/** - * Test if the given address is (currently) a plausible IP address for - * this peer. Mostly a convenience function so that clients do not - * have to explicitly track all IPs that the #GNUNET_NAT_AddressCallback - * has returned so far. - * - * @param nh the handle returned by register - * @param addr IP address to test (IPv4 or IPv6) - * @param addrlen number of bytes in @a addr - * @return #GNUNET_YES if the address is plausible, - * #GNUNET_NO if the address is not plausible, - * #GNUNET_SYSERR if the address is malformed - */ -int -GNUNET_NAT_test_address (struct GNUNET_NAT_Handle *nh, - const void *addr, - socklen_t addrlen) -{ - struct AddrEntry *ae; - - if ((addrlen != sizeof(struct sockaddr_in)) && - (addrlen != sizeof(struct sockaddr_in6))) - { - GNUNET_break (0); - return GNUNET_SYSERR; - } - for (ae = nh->ae_head; NULL != ae; ae = ae->next) - if ((addrlen == ae->addrlen) && (0 == memcmp (addr, &ae[1], addrlen))) - return GNUNET_YES; - return GNUNET_NO; -} - - -/** - * We learned about a peer (possibly behind NAT) so run the - * gnunet-nat-client to send dummy ICMP responses to cause - * that peer to connect to us (connection reversal). - * - * @param nh handle (used for configuration) - * @param local_sa our local address of the peer (IPv4-only) - * @param remote_sa the remote address of the peer (IPv4-only) - * @return #GNUNET_SYSERR on error, - * #GNUNET_NO if connection reversal is unavailable, - * #GNUNET_OK otherwise (presumably in progress) - */ -int -GNUNET_NAT_request_reversal (struct GNUNET_NAT_Handle *nh, - const struct sockaddr_in *local_sa, - const struct sockaddr_in *remote_sa) -{ - struct GNUNET_MQ_Envelope *env; - struct GNUNET_NAT_RequestConnectionReversalMessage *req; - char *buf; - - if (NULL == nh->mq) - return GNUNET_SYSERR; - GNUNET_break (AF_INET == local_sa->sin_family); - GNUNET_break (AF_INET == remote_sa->sin_family); - env = - GNUNET_MQ_msg_extra (req, - 2 * sizeof(struct sockaddr_in), - GNUNET_MESSAGE_TYPE_NAT_REQUEST_CONNECTION_REVERSAL); - req->local_addr_size = htons (sizeof(struct sockaddr_in)); - req->remote_addr_size = htons (sizeof(struct sockaddr_in)); - buf = (char *) &req[1]; - GNUNET_memcpy (buf, local_sa, sizeof(struct sockaddr_in)); - buf += sizeof(struct sockaddr_in); - GNUNET_memcpy (buf, remote_sa, sizeof(struct sockaddr_in)); - GNUNET_MQ_send (nh->mq, env); - return GNUNET_OK; -} - - -void -GNUNET_NAT_unregister (struct GNUNET_NAT_Handle *nh) -{ - struct AddrEntry *ae; - struct AddrEntry *next; - - if (NULL != nh->mq) - { - GNUNET_MQ_destroy (nh->mq); - nh->mq = NULL; - } - if (NULL != nh->reconnect_task) - { - GNUNET_SCHEDULER_cancel (nh->reconnect_task); - nh->reconnect_task = NULL; - } - next = nh->ae_head; - while (NULL != next) - { - ae = next; - next = next->next; - GNUNET_CONTAINER_DLL_remove (nh->ae_head, nh->ae_tail, ae); - GNUNET_free (ae); - } - GNUNET_free (nh->reg); - GNUNET_free (nh); -} - - -/* end of nat_api.c */ diff --git a/src/nat/nat_api_stun.c b/src/nat/nat_api_stun.c deleted file mode 100644 index 94adc3d6c..000000000 --- a/src/nat/nat_api_stun.c +++ /dev/null @@ -1,259 +0,0 @@ -/* - This file is part of GNUnet. - Copyright (C) 2009, 2015, 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 . - - SPDX-License-Identifier: AGPL3.0-or-later - */ -/** - * This code provides some support for doing STUN transactions. - * We send simplest possible packet ia REQUEST with BIND to a STUN server. - * - * All STUN packets start with a simple header made of a type, - * length (excluding the header) and a 16-byte random transaction id. - * Following the header we may have zero or more attributes, each - * structured as a type, length and a value (whose format depends - * on the type, but often contains addresses). - * Of course all fields are in network format. - * - * This code was based on ministun.c. - * - * @file nat/nat_api_stun.c - * @brief Functions for STUN functionality - * @author Bruno Souza Cabral - */ - -#include "platform.h" -#include "gnunet_util_lib.h" -#include "gnunet_resolver_service.h" -#include "gnunet_nat_service.h" - - -#include "nat_stun.h" - -#define LOG(kind, ...) GNUNET_log_from (kind, "stun", __VA_ARGS__) - -#define TIMEOUT GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS, 15) - - -/** - * Handle to a request given to the resolver. Can be used to cancel - * the request prior to the timeout or successful execution. Also - * used to track our internal state for the request. - */ -struct GNUNET_NAT_STUN_Handle -{ - /** - * Handle to a pending DNS lookup request. - */ - struct GNUNET_RESOLVER_RequestHandle *dns_active; - - /** - * Handle to the listen socket - */ - struct GNUNET_NETWORK_Handle *sock; - - /** - * Stun server address - */ - char *stun_server; - - /** - * Function to call when a error occurs - */ - GNUNET_NAT_TestCallback cb; - - /** - * Closure for @e cb. - */ - void *cb_cls; - - /** - * Do we got a DNS resolution successfully? - */ - int dns_success; - - /** - * STUN port - */ - uint16_t stun_port; -}; - - -/** - * Encode a class and method to a compatible STUN format - * - * @param msg_class class to be converted - * @param method method to be converted - * @return message in a STUN compatible format - */ -static int -encode_message (enum StunClasses msg_class, - enum StunMethods method) -{ - return ((msg_class & 1) << 4) | ((msg_class & 2) << 7) - | (method & 0x000f) | ((method & 0x0070) << 1) | ((method & 0x0f800) - << 2); -} - - -/** - * Fill the stun_header with a random request_id - * - * @param req stun header to be filled - */ -static void -generate_request_id (struct stun_header *req) -{ - req->magic = htonl (STUN_MAGIC_COOKIE); - for (unsigned int x = 0; x < 3; x++) - req->id.id[x] = GNUNET_CRYPTO_random_u32 (GNUNET_CRYPTO_QUALITY_NONCE, - UINT32_MAX); -} - - -/** - * Try to establish a connection given the specified address. - * - * @param cls our `struct GNUNET_NAT_STUN_Handle *` - * @param addr address to try, NULL for "last call" - * @param addrlen length of @a addr - */ -static void -stun_dns_callback (void *cls, - const struct sockaddr *addr, - socklen_t addrlen) -{ - struct GNUNET_NAT_STUN_Handle *rh = cls; - struct stun_header req; - struct sockaddr_in server; - - if (NULL == addr) - { - rh->dns_active = NULL; - if (GNUNET_NO == rh->dns_success) - { - LOG (GNUNET_ERROR_TYPE_INFO, - "Error resolving host %s\n", - rh->stun_server); - rh->cb (rh->cb_cls, - GNUNET_NAT_ERROR_NOT_ONLINE); - } - else if (GNUNET_SYSERR == rh->dns_success) - { - rh->cb (rh->cb_cls, - GNUNET_NAT_ERROR_INTERNAL_NETWORK_ERROR); - } - else - { - rh->cb (rh->cb_cls, - GNUNET_NAT_ERROR_SUCCESS); - } - GNUNET_NAT_stun_make_request_cancel (rh); - return; - } - - rh->dns_success = GNUNET_YES; - memset (&server, 0, sizeof(server)); - server.sin_family = AF_INET; - server.sin_addr = ((struct sockaddr_in *) addr)->sin_addr; - server.sin_port = htons (rh->stun_port); -#if HAVE_SOCKADDR_IN_SIN_LEN - server.sin_len = (u_char) sizeof(struct sockaddr_in); -#endif - - /* Craft the simplest possible STUN packet. A request binding */ - generate_request_id (&req); - req.msglen = htons (0); - req.msgtype = htons (encode_message (STUN_REQUEST, - STUN_BINDING)); - - /* Send the packet */ - if (-1 == - GNUNET_NETWORK_socket_sendto (rh->sock, - &req, - sizeof(req), - (const struct sockaddr *) &server, - sizeof(server))) - { - GNUNET_log_strerror (GNUNET_ERROR_TYPE_ERROR, - "sendto"); - rh->dns_success = GNUNET_SYSERR; - return; - } -} - - -/** - * Make Generic STUN request. Sends a generic stun request to the - * server specified using the specified socket. - * - * @param server the address of the stun server - * @param port port of the stun server, in host byte order - * @param sock the socket used to send the request - * @param cb callback in case of error - * @param cb_cls closure for @a cb - * @return NULL on error - */ -struct GNUNET_NAT_STUN_Handle * -GNUNET_NAT_stun_make_request (const char *server, - uint16_t port, - struct GNUNET_NETWORK_Handle *sock, - GNUNET_NAT_TestCallback cb, - void *cb_cls) -{ - struct GNUNET_NAT_STUN_Handle *rh; - - rh = GNUNET_new (struct GNUNET_NAT_STUN_Handle); - rh->sock = sock; - rh->cb = cb; - rh->cb_cls = cb_cls; - rh->stun_server = GNUNET_strdup (server); - rh->stun_port = port; - rh->dns_success = GNUNET_NO; - rh->dns_active = GNUNET_RESOLVER_ip_get (rh->stun_server, - AF_INET, - TIMEOUT, - &stun_dns_callback, - rh); - if (NULL == rh->dns_active) - { - GNUNET_NAT_stun_make_request_cancel (rh); - return NULL; - } - return rh; -} - - -/** - * Cancel active STUN request. Frees associated resources - * and ensures that the callback is no longer invoked. - * - * @param rh request to cancel - */ -void -GNUNET_NAT_stun_make_request_cancel (struct GNUNET_NAT_STUN_Handle *rh) -{ - if (NULL != rh->dns_active) - { - GNUNET_RESOLVER_request_cancel (rh->dns_active); - rh->dns_active = NULL; - } - GNUNET_free (rh->stun_server); - GNUNET_free (rh); -} - - -/* end of nat_stun.c */ diff --git a/src/nat/nat_stun.h b/src/nat/nat_stun.h deleted file mode 100644 index a31e0f8d6..000000000 --- a/src/nat/nat_stun.h +++ /dev/null @@ -1,267 +0,0 @@ -/* - This file is part of GNUnet. - Copyright (C) 2009, 2015, 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 . - - SPDX-License-Identifier: AGPL3.0-or-later - */ -/** - * Message types for STUN server resolution - * - * @file nat/nat_stun.h - * @brief Testcase for STUN library - * @author Bruno Souza Cabral - * @author Mark Spencer (Original code borrowed from Asterisk) - * @author Christian Grothoff - */ - - -#define STUN_IGNORE (0) -#define STUN_ACCEPT (1) - -#define STUN_MAGIC_COOKIE 0x2112A442 - -typedef struct -{ - uint32_t id[3]; -} GNUNET_PACKED stun_trans_id; - - -struct stun_header -{ - uint16_t msgtype; - uint16_t msglen; - uint32_t magic; - stun_trans_id id; -} GNUNET_PACKED; - - -struct stun_attr -{ - uint16_t attr; - uint16_t len; -} GNUNET_PACKED; - - -/** - * The format normally used for addresses carried by STUN messages. - */ -struct stun_addr -{ - uint8_t unused; - - /** - * Address family, we expect AF_INET. - */ - uint8_t family; - - /** - * Port number. - */ - uint16_t port; - - /** - * IPv4 address. Should this be "struct in_addr"? - */ - uint32_t addr; -} GNUNET_PACKED; - - -/** - * STUN message classes - */ -enum StunClasses -{ - INVALID_CLASS = 0, - STUN_REQUEST = 0x0000, - STUN_INDICATION = 0x0001, - STUN_RESPONSE = 0x0002, - STUN_ERROR_RESPONSE = 0x0003 -}; - -enum StunMethods -{ - INVALID_METHOD = 0, - STUN_BINDING = 0x0001, - STUN_SHARED_SECRET = 0x0002, - STUN_ALLOCATE = 0x0003, - STUN_REFRESH = 0x0004, - STUN_SEND = 0x0006, - STUN_DATA = 0x0007, - STUN_CREATE_PERMISSION = 0x0008, - STUN_CHANNEL_BIND = 0x0009 -}; - - -/** - * Basic attribute types in stun messages. - * Messages can also contain custom attributes (codes above 0x7fff) - */ -enum StunAttributes -{ - STUN_MAPPED_ADDRESS = 0x0001, - STUN_RESPONSE_ADDRESS = 0x0002, - STUN_CHANGE_ADDRESS = 0x0003, - STUN_SOURCE_ADDRESS = 0x0004, - STUN_CHANGED_ADDRESS = 0x0005, - STUN_USERNAME = 0x0006, - STUN_PASSWORD = 0x0007, - STUN_MESSAGE_INTEGRITY = 0x0008, - STUN_ERROR_CODE = 0x0009, - STUN_UNKNOWN_ATTRIBUTES = 0x000a, - STUN_REFLECTED_FROM = 0x000b, - STUN_REALM = 0x0014, - STUN_NONCE = 0x0015, - STUN_XOR_MAPPED_ADDRESS = 0x0020, - STUN_MS_VERSION = 0x8008, - STUN_MS_XOR_MAPPED_ADDRESS = 0x8020, - STUN_SOFTWARE = 0x8022, - STUN_ALTERNATE_SERVER = 0x8023, - STUN_FINGERPRINT = 0x8028 -}; - - -/** - * Convert a message to a StunClass - * - * @param msg the received message - * @return the converted StunClass - */ -static enum StunClasses -decode_class (int msg) -{ - /* Sorry for the magic, but this maps the class according to rfc5245 */ - return (enum StunClasses) ((msg & 0x0010) >> 4) | ((msg & 0x0100) >> 7); -} - - -/** - * Convert a message to a StunMethod - * - * @param msg the received message - * @return the converted StunMethod - */ -static enum StunMethods -decode_method (int msg) -{ - return (enum StunMethods) (msg & 0x000f) | ((msg & 0x00e0) >> 1) | ((msg - & 0x3e00) - >> 2); -} - - -/** - * Print a class and method from a STUN message - * - * @param msg - * @return string with the message class and method - */ -GNUNET_UNUSED -static const char * -stun_msg2str (int msg) -{ - static const struct - { - enum StunClasses value; - const char *name; - } classes[] = { - { STUN_REQUEST, "Request" }, - { STUN_INDICATION, "Indication" }, - { STUN_RESPONSE, "Response" }, - { STUN_ERROR_RESPONSE, "Error Response" }, - { INVALID_CLASS, NULL } - }; - static const struct - { - enum StunMethods value; - const char *name; - } methods[] = { - { STUN_BINDING, "Binding" }, - { INVALID_METHOD, NULL } - }; - static char result[64]; - const char *msg_class = NULL; - const char *method = NULL; - enum StunClasses cvalue; - enum StunMethods mvalue; - - cvalue = decode_class (msg); - for (unsigned int i = 0; classes[i].name; i++) - if (classes[i].value == cvalue) - { - msg_class = classes[i].name; - break; - } - mvalue = decode_method (msg); - for (unsigned int i = 0; methods[i].name; i++) - if (methods[i].value == mvalue) - { - method = methods[i].name; - break; - } - GNUNET_snprintf (result, - sizeof(result), - "%s %s", - method ? : "Unknown Method", - msg_class ? : "Unknown Class Message"); - return result; -} - - -/** - * Print attribute name - * - * @param msg with a attribute type - * @return string with the attribute name - */ -GNUNET_UNUSED -static const char * -stun_attr2str (enum StunAttributes msg) -{ - static const struct - { - enum StunAttributes value; - const char *name; - } attrs[] = { - { STUN_MAPPED_ADDRESS, "Mapped Address" }, - { STUN_RESPONSE_ADDRESS, "Response Address" }, - { STUN_CHANGE_ADDRESS, "Change Address" }, - { STUN_SOURCE_ADDRESS, "Source Address" }, - { STUN_CHANGED_ADDRESS, "Changed Address" }, - { STUN_USERNAME, "Username" }, - { STUN_PASSWORD, "Password" }, - { STUN_MESSAGE_INTEGRITY, "Message Integrity" }, - { STUN_ERROR_CODE, "Error Code" }, - { STUN_UNKNOWN_ATTRIBUTES, "Unknown Attributes" }, - { STUN_REFLECTED_FROM, "Reflected From" }, - { STUN_REALM, "Realm" }, - { STUN_NONCE, "Nonce" }, - { STUN_XOR_MAPPED_ADDRESS, "XOR Mapped Address" }, - { STUN_MS_VERSION, "MS Version" }, - { STUN_MS_XOR_MAPPED_ADDRESS, "MS XOR Mapped Address" }, - { STUN_SOFTWARE, "Software" }, - { STUN_ALTERNATE_SERVER, "Alternate Server" }, - { STUN_FINGERPRINT, "Fingerprint" }, - { 0, NULL } - }; - - for (unsigned int i = 0; attrs[i].name; i++) - if (attrs[i].value == msg) - return attrs[i].name; - return "Unknown Attribute"; -} - - -/* end of nat_stun.h */ diff --git a/src/nat/test_nat.c b/src/nat/test_nat.c deleted file mode 100644 index a3072f712..000000000 --- a/src/nat/test_nat.c +++ /dev/null @@ -1,192 +0,0 @@ -/* - This file is part of GNUnet. - Copyright (C) 2009, 2011 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 . - - SPDX-License-Identifier: AGPL3.0-or-later - */ - -/** - * Testcase for port redirection and public IP address retrieval. - * This test never fails, because there need to be a NAT box set up for that. - * So we only get IP address and open the 2086 port using any NAT traversal - * method available, wait for 30s, close ports and return. - * Have a look at the logs and use NMAP to check that it works with your box. - * - * @file nat/test_nat.c - * @brief Testcase for NAT library - * @author Milan Bouchet-Valat - * @author Christian Grothoff - * - * TODO: actually use ARM to start resolver service to make DNS work! - */ - -#include "platform.h" -#include "gnunet_util_lib.h" -#include "gnunet_program_lib.h" -#include "gnunet_scheduler_lib.h" -#include "gnunet_nat_lib.h" - - -/** - * Time to wait before stopping NAT, in seconds - */ -#define TIMEOUT GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS, 5) - - -/** - * Function called on each address that the NAT service - * believes to be valid for the transport. - */ -static void -addr_callback (void *cls, int add_remove, const struct sockaddr *addr, - socklen_t addrlen) -{ - GNUNET_log (GNUNET_ERROR_TYPE_INFO, - "Address changed: %s `%s' (%u bytes)\n", - add_remove == GNUNET_YES ? "added" : "removed", - GNUNET_a2s (addr, - addrlen), - (unsigned int) addrlen); -} - - -/** - * Function that terminates the test. - */ -static void -stop (void *cls) -{ - struct GNUNET_NAT_Handle *nat = cls; - - GNUNET_log (GNUNET_ERROR_TYPE_INFO, - "Stopping NAT and quitting...\n"); - GNUNET_NAT_unregister (nat); -} - - -struct addr_cls -{ - struct sockaddr *addr; - socklen_t addrlen; -}; - - -/** - * Return the address of the default interface, - * or any interface with a valid address if the default is not valid - * - * @param cls the 'struct addr_cls' - * @param name name of the interface - * @param isDefault do we think this may be our default interface - * @param addr address of the interface - * @param addrlen number of bytes in @a addr - * @return #GNUNET_OK to continue iterating - */ -static int -process_if (void *cls, - const char *name, - int isDefault, - const struct sockaddr *addr, - const struct sockaddr *broadcast_addr, - const struct sockaddr *netmask, - socklen_t addrlen) -{ - struct addr_cls *data = cls; - - if (addr == NULL) - return GNUNET_OK; - GNUNET_free (data->addr); - data->addr = GNUNET_malloc (addrlen); - GNUNET_memcpy (data->addr, addr, addrlen); - data->addrlen = addrlen; - if (isDefault) - return GNUNET_SYSERR; - return GNUNET_OK; -} - - -/** - * Main function run with scheduler. - */ -static void -run (void *cls, - char *const *args, - const char *cfgfile, - const struct GNUNET_CONFIGURATION_Handle *cfg) -{ - struct GNUNET_NAT_Handle *nat; - struct addr_cls data; - struct sockaddr *addr; - - data.addr = NULL; - GNUNET_OS_network_interfaces_list (process_if, &data); - if (NULL == data.addr) - { - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - "Could not find a valid interface address!\n"); - exit (77); /* marks test as skipped */ - } - addr = data.addr; - GNUNET_assert (addr->sa_family == AF_INET || addr->sa_family == AF_INET6); - if (addr->sa_family == AF_INET) - ((struct sockaddr_in *) addr)->sin_port = htons (2086); - else - ((struct sockaddr_in6 *) addr)->sin6_port = htons (2086); - - GNUNET_log (GNUNET_ERROR_TYPE_INFO, - "Requesting NAT redirection from address %s...\n", - GNUNET_a2s (addr, data.addrlen)); - - nat = GNUNET_NAT_register (cfg, GNUNET_YES /* tcp */, - 2086, 1, (const struct sockaddr **) &addr, - &data.addrlen, &addr_callback, NULL, NULL, NULL); - GNUNET_free (addr); - GNUNET_SCHEDULER_add_delayed (TIMEOUT, &stop, nat); -} - - -int -main (int argc, char *const argv[]) -{ - struct GNUNET_GETOPT_CommandLineOption options[] = { - GNUNET_GETOPT_OPTION_END - }; - - char *const argv_prog[] = { - "test-nat", - "-c", - "test_nat_data.conf", - NULL - }; - - GNUNET_log_setup ("test-nat", - "WARNING", - NULL); - GNUNET_log (GNUNET_ERROR_TYPE_INFO, - "Testing NAT library, timeout set to %s\n", - GNUNET_STRINGS_relative_time_to_string (TIMEOUT, - GNUNET_YES)); - GNUNET_PROGRAM_run (3, - argv_prog, - "test-nat", - "nohelp", - options, - &run, NULL); - return 0; -} - - -/* end of test_nat.c */ diff --git a/src/nat/test_nat_data.conf b/src/nat/test_nat_data.conf deleted file mode 100644 index e139d0c05..000000000 --- a/src/nat/test_nat_data.conf +++ /dev/null @@ -1,36 +0,0 @@ -[PATHS] -GNUNET_TEST_HOME = $GNUNET_TMP/nat-test -# GNUNET_TEST_HOME = /var/lib/gnunet/ -# configuration file is assumed to be the default, -# which is what we want by default... - -[nat] -# Are we behind NAT? -BEHIND_NAT = YES - -# Is the NAT hole-punched? -PUNCHED_NAT = NO - -# Disable UPNP by default until it gets cleaner! -ENABLE_UPNP = YES - -# Use addresses from the local network interfaces (including loopback, but also others) -USE_LOCALADDR = YES - -# External IP address of the NAT box (if known); IPv4 dotted-decimal ONLY at this time (should allow DynDNS!) -# normal interface IP address for non-NATed peers; -# possibly auto-detected (using UPnP) if possible if not specified -# EXTERNAL_ADDRESS = - -# Should we use ICMP-based NAT traversal to try connect to NATed peers -# or, if we are behind NAT, to allow connections to us? -ENABLE_ICMP_CLIENT = NO -ENABLE_ICMP_SERVER = NO - -# IP address of the interface connected to the NAT box; IPv4 dotted-decimal ONLY; -# normal interface IP address for non-NATed peers; -# likely auto-detected (via interface list) if not specified (!) -# INTERNAL_ADDRESS = - -# Disable IPv6 support -DISABLEV6 = NO diff --git a/src/nat/test_nat_mini.c b/src/nat/test_nat_mini.c deleted file mode 100644 index 528815e1a..000000000 --- a/src/nat/test_nat_mini.c +++ /dev/null @@ -1,134 +0,0 @@ -/* - This file is part of GNUnet. - Copyright (C) 2009, 2011 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 . - - SPDX-License-Identifier: AGPL3.0-or-later - */ -/** - * @file nat/test_nat_mini.c - * @brief Testcase for NAT library - mini - * @author Christian Grothoff - * - * Testcase for port redirection and public IP address retrieval. - * This test never fails, because there need to be a NAT box set up for that - * - * TODO: actually use ARM to start resolver service to make DNS work! - */ - -#include "platform.h" -#include "gnunet_util_lib.h" -#include "gnunet_program_lib.h" -#include "gnunet_scheduler_lib.h" -#include "gnunet_nat_lib.h" - -/* Time to wait before stopping NAT, in seconds */ -#define TIMEOUT GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS, 5) - -/** - * Function called on each address that the NAT service - * believes to be valid for the transport. - */ -static void -addr_callback (void *cls, int add_remove, - const struct sockaddr *addr, - socklen_t addrlen, - enum GNUNET_NAT_StatusCode ret) -{ - if (GNUNET_NAT_ERROR_SUCCESS == ret) - { - fprintf (stderr, - "Address changed: %s `%s' (%u bytes)\n", - add_remove == GNUNET_YES - ? "added" : "removed", - GNUNET_a2s (addr, - addrlen), - (unsigned int) addrlen); - } - else - ; - // TODO: proper error handling! -} - - -/** - * Function that terminates the test. - */ -static void -stop (void *cls) -{ - struct GNUNET_NAT_MiniHandle *mini = cls; - - GNUNET_log (GNUNET_ERROR_TYPE_INFO, "Stopping NAT and quitting...\n"); - GNUNET_NAT_mini_map_stop (mini); -} - - -#define PORT 10000 - -/** - * Main function run with scheduler. - */ -static void -run (void *cls, char *const *args, const char *cfgfile, - const struct GNUNET_CONFIGURATION_Handle *cfg) -{ - struct GNUNET_NAT_MiniHandle *mini; - - GNUNET_log (GNUNET_ERROR_TYPE_INFO, - "Requesting NAT redirection for port %u...\n", - PORT); - mini = GNUNET_NAT_mini_map_start (PORT, GNUNET_YES /* tcp */, - &addr_callback, NULL); - if (NULL == mini) - { - GNUNET_log (GNUNET_ERROR_TYPE_INFO, "Could not start UPnP interaction\n"); - return; - } - GNUNET_SCHEDULER_add_delayed (TIMEOUT, &stop, mini); -} - - -int -main (int argc, char *const argv[]) -{ - struct GNUNET_GETOPT_CommandLineOption options[] = { - GNUNET_GETOPT_OPTION_END - }; - - char *const argv_prog[] = { - "test-nat-mini", - "-c", - "test_nat_data.conf", - "-L", - "WARNING", - NULL - }; - - GNUNET_log_setup ("test-nat-mini", - "WARNING", - NULL); - - GNUNET_log (GNUNET_ERROR_TYPE_INFO, - "UPnP test for NAT library, timeout set to %s\n", - GNUNET_STRINGS_relative_time_to_string (TIMEOUT, - GNUNET_YES)); - GNUNET_PROGRAM_run (5, argv_prog, "test-nat-mini", "nohelp", options, &run, - NULL); - return 0; -} - - -/* end of test_nat_mini.c */ diff --git a/src/nat/test_nat_test.c b/src/nat/test_nat_test.c deleted file mode 100644 index 2abab4d5f..000000000 --- a/src/nat/test_nat_test.c +++ /dev/null @@ -1,142 +0,0 @@ -/* - This file is part of GNUnet. - Copyright (C) 2009, 2011, 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 . - - SPDX-License-Identifier: AGPL3.0-or-later - */ -/** - * @file nat/test_nat_test.c - * @brief Testcase for NAT testing functions - * @author Christian Grothoff - */ -#include "platform.h" -#include "gnunet_util_lib.h" -#include "gnunet_nat_lib.h" - -/** - * Time to wait before stopping NAT test, in seconds - */ -#define TIMEOUT GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS, 15) - - -static int ret = 1; - -static struct GNUNET_NAT_Test *tst; - -static struct GNUNET_SCHEDULER_Task *tsk; - - -static void -report_result (void *cls, - enum GNUNET_NAT_StatusCode aret) -{ - if (GNUNET_NAT_ERROR_TIMEOUT == aret) - fprintf (stderr, - "NAT test timed out\n"); - else if (GNUNET_NAT_ERROR_SUCCESS != aret) - fprintf (stderr, - "NAT test reported error %d\n", aret); - else - ret = 0; - GNUNET_NAT_test_stop (tst); - tst = NULL; - GNUNET_SCHEDULER_cancel (tsk); - tsk = NULL; -} - - -static void -failed_timeout (void *cls) -{ - tsk = NULL; - fprintf (stderr, - "NAT test failed to terminate on timeout\n"); - ret = 2; - GNUNET_NAT_test_stop (tst); - tst = NULL; -} - - -/** - * Main function run with scheduler. - */ -static void -run (void *cls, char *const *args, const char *cfgfile, - const struct GNUNET_CONFIGURATION_Handle *cfg) -{ - tst = - GNUNET_NAT_test_start (cfg, GNUNET_YES, 1285, 1285, TIMEOUT, - &report_result, - NULL); - tsk = GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_relative_multiply (TIMEOUT, - 2), - &failed_timeout, - NULL); -} - - -int -main (int argc, char *const argv[]) -{ - struct GNUNET_GETOPT_CommandLineOption options[] = { - GNUNET_GETOPT_OPTION_END - }; - struct GNUNET_OS_Process *gns; - int nat_res; - char *const argv_prog[] = { - "test-nat-test", - "-c", - "test_nat_test_data.conf", - NULL - }; - - GNUNET_log_setup ("test-nat-test", - "WARNING", - NULL); - - nat_res = GNUNET_OS_check_helper_binary ("gnunet-nat-server", GNUNET_NO, - NULL); - if (GNUNET_SYSERR == nat_res) - { - GNUNET_log (GNUNET_ERROR_TYPE_WARNING, - "Cannot run NAT test: `%s' file not found\n", - "gnunet-nat-server"); - return 0; - } - - gns = GNUNET_OS_start_process (GNUNET_OS_INHERIT_STD_OUT_AND_ERR - | GNUNET_OS_USE_PIPE_CONTROL, - NULL, NULL, NULL, - "gnunet-nat-server", - "gnunet-nat-server", - "-c", "test_nat_test_data.conf", - "12345", NULL); - GNUNET_assert (NULL != gns); - GNUNET_PROGRAM_run (3, argv_prog, - "test-nat-test", "nohelp", - options, &run, - NULL); - GNUNET_break (0 == GNUNET_OS_process_kill (gns, GNUNET_TERM_SIG)); - GNUNET_break (GNUNET_OK == GNUNET_OS_process_wait (gns)); - GNUNET_OS_process_destroy (gns); - if (0 != ret) - fprintf (stderr, - "NAT test failed to report success\n"); - return ret; -} - - -/* end of test_nat_test.c */ diff --git a/src/nat/test_nat_test_data.conf b/src/nat/test_nat_test_data.conf deleted file mode 100644 index ca78ca9f3..000000000 --- a/src/nat/test_nat_test_data.conf +++ /dev/null @@ -1,45 +0,0 @@ -[PATHS] -GNUNET_TEST_HOME = $GNUNET_TMP/nat-test -# GNUNET_TEST_HOME = /var/lib/gnunet/ -# configuration file is assumed to be the default, -# which is what we want by default... - -[gnunet-nat-server] -HOSTNAME = localhost -PORT = 57315 - -[nat] -# Are we behind NAT? -BEHIND_NAT = NO - -# Is the NAT hole-punched? -PUNCHED_NAT = YES - -# Disable UPNP by default until it gets cleaner! -ENABLE_UPNP = NO - -# Use addresses from the local network interfaces (including loopback, but also others) -USE_LOCALADDR = YES -RETURN_LOCAL_ADDRESSES = YES - -# External IP address of the NAT box (if known); IPv4 dotted-decimal ONLY at this time (should allow DynDNS!) -# normal interface IP address for non-NATed peers; -# possibly auto-detected (using UPnP) if possible if not specified -# EXTERNAL_ADDRESS = - -# Should we use ICMP-based NAT traversal to try connect to NATed peers -# or, if we are behind NAT, to allow connections to us? -ENABLE_ICMP_CLIENT = NO -ENABLE_ICMP_SERVER = NO - -# IP address of the interface connected to the NAT box; IPv4 dotted-decimal ONLY; -# normal interface IP address for non-NATed peers; -# likely auto-detected (via interface list) if not specified (!) -INTERNAL_ADDRESS = 127.0.0.1 - -# Disable IPv6 support -DISABLEV6 = YES - - -[nse] -START_ON_DEMAND = NO diff --git a/src/nat/test_stun.c b/src/nat/test_stun.c deleted file mode 100644 index 75eb877b3..000000000 --- a/src/nat/test_stun.c +++ /dev/null @@ -1,313 +0,0 @@ -/* - This file is part of GNUnet. - Copyright (C) 2009, 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 . - - SPDX-License-Identifier: AGPL3.0-or-later - */ - -/** - * Testcase for STUN server resolution - * - * @file nat/test_stun.c - * @brief Testcase for STUN library - * @author Bruno Souza Cabral - * @author Christian Grothoff - */ - - -#include "platform.h" -#include "gnunet_util_lib.h" -#include "gnunet_program_lib.h" -#include "gnunet_scheduler_lib.h" -#include "gnunet_nat_lib.h" - - -#define LOG(kind, ...) GNUNET_log_from (kind, "test-stun", __VA_ARGS__) - -/** - * Time to wait before stopping NAT, in seconds - */ -#define TIMEOUT GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS, 5) - - -/** - * The port the test service is running on (default 7895) - */ -static unsigned long port = 7895; - -static int ret = 1; - -static const char *stun_server = "stun.gnunet.org"; - -static int stun_port = 3478; - -/** - * The listen socket of the service for IPv4 - */ -static struct GNUNET_NETWORK_Handle *lsock4; - -/** - * The listen task ID for IPv4 - */ -static struct GNUNET_SCHEDULER_Task *ltask4; - -/** - * Handle for the STUN request. - */ -static struct GNUNET_NAT_STUN_Handle *rh; - - -static void -print_answer (struct sockaddr_in*answer) -{ - GNUNET_log (GNUNET_ERROR_TYPE_INFO, - "External IP is: %s , with port %d\n", - inet_ntoa (answer->sin_addr), - ntohs (answer->sin_port)); -} - - -/** - * Function that terminates the test. - */ -static void -stop () -{ - GNUNET_log (GNUNET_ERROR_TYPE_INFO, - "Stopping NAT and quitting...\n"); - if (NULL != ltask4) - { - GNUNET_SCHEDULER_cancel (ltask4); - ltask4 = NULL; - } - if (NULL != lsock4) - { - GNUNET_NETWORK_socket_close (lsock4); - lsock4 = NULL; - } - if (NULL != rh) - { - GNUNET_NAT_stun_make_request_cancel (rh); - rh = NULL; - } -} - - -/** - * Activity on our incoming socket. Read data from the - * incoming connection. - * - * @param cls - */ -static void -do_udp_read (void *cls) -{ - // struct GNUNET_NAT_Test *tst = cls; - unsigned char reply_buf[1024]; - ssize_t rlen; - struct sockaddr_in answer; - const struct GNUNET_SCHEDULER_TaskContext *tc; - - ltask4 = NULL; - tc = GNUNET_SCHEDULER_get_task_context (); - if ((0 == (tc->reason & GNUNET_SCHEDULER_REASON_READ_READY)) || - (! GNUNET_NETWORK_fdset_isset (tc->read_ready, - lsock4))) - { - fprintf (stderr, - "Timeout waiting for STUN response\n"); - stop (); - } - rlen = GNUNET_NETWORK_socket_recv (lsock4, - reply_buf, - sizeof(reply_buf)); - memset (&answer, - 0, - sizeof(struct sockaddr_in)); - if (GNUNET_OK != - GNUNET_NAT_stun_handle_packet (reply_buf, - rlen, - &answer)) - { - fprintf (stderr, - "Unexpected UDP packet, trying to read more\n"); - ltask4 = GNUNET_SCHEDULER_add_read_net (TIMEOUT, - lsock4, - &do_udp_read, NULL); - return; - } - ret = 0; - print_answer (&answer); - stop (); -} - - -/** - * Create an IPv4 listen socket bound to our port. - * - * @return NULL on error - */ -static struct GNUNET_NETWORK_Handle * -bind_v4 () -{ - struct GNUNET_NETWORK_Handle *ls; - struct sockaddr_in sa4; - int eno; - - memset (&sa4, 0, sizeof(sa4)); - sa4.sin_family = AF_INET; - sa4.sin_port = htons (port); -#if HAVE_SOCKADDR_IN_SIN_LEN - sa4.sin_len = sizeof(sa4); -#endif - ls = GNUNET_NETWORK_socket_create (AF_INET, - SOCK_DGRAM, - 0); - if (NULL == ls) - return NULL; - if (GNUNET_OK != - GNUNET_NETWORK_socket_bind (ls, - (const struct sockaddr *) &sa4, - sizeof(sa4))) - { - eno = errno; - GNUNET_NETWORK_socket_close (ls); - errno = eno; - return NULL; - } - return ls; -} - - -/** - * Function called with the result of the STUN request transmission attempt. - * - * @param cls unused - * @param error status code from STUN - */ -static void -request_callback (void *cls, - enum GNUNET_NAT_StatusCode error) -{ - rh = NULL; - if (GNUNET_NAT_ERROR_SUCCESS == error) - { - /* all good, start to receive */ - ltask4 = GNUNET_SCHEDULER_add_read_net (TIMEOUT, - lsock4, - &do_udp_read, - NULL); - return; - } - if (error == GNUNET_NAT_ERROR_NOT_ONLINE) - { - ret = 77; /* report 'skip' */ - fprintf (stderr, - "System is offline, cannot test STUN request.\n"); - } - else - { - ret = error; - } - stop (); -} - - -/** - * Main function run with scheduler. - */ -static void -run (void *cls, - char *const *args, - const char *cfgfile, - const struct GNUNET_CONFIGURATION_Handle *cfg) -{ - // Lets create the socket - lsock4 = bind_v4 (); - if (NULL == lsock4) - { - GNUNET_log_strerror (GNUNET_ERROR_TYPE_ERROR, - "bind"); - GNUNET_SCHEDULER_shutdown (); - return; - } - GNUNET_log (GNUNET_ERROR_TYPE_INFO, - "Service listens on port %u\n", - (unsigned int) port); - rh = GNUNET_NAT_stun_make_request (stun_server, - stun_port, - lsock4, - &request_callback, NULL); - GNUNET_SCHEDULER_add_delayed (TIMEOUT, - &stop, NULL); -} - - -int -main (int argc, char *const argv[]) -{ - struct GNUNET_GETOPT_CommandLineOption options[] = { - GNUNET_GETOPT_OPTION_END - }; - char *const argv_prog[] = { - "test-stun", - "-c", - "test_stun.conf", - NULL - }; - char *fn; - struct GNUNET_OS_Process *proc; - - GNUNET_log_setup ("test-stun", - "WARNING", - NULL); - - /* Lets start resolver */ - fn = GNUNET_OS_get_libexec_binary_path ("gnunet-service-resolver"); - proc = GNUNET_OS_start_process (GNUNET_OS_INHERIT_STD_OUT_AND_ERR - | GNUNET_OS_USE_PIPE_CONTROL, - NULL, NULL, NULL, - fn, - "gnunet-service-resolver", - "-c", "test_stun.conf", NULL); - - if (NULL == proc) - { - GNUNET_log (GNUNET_ERROR_TYPE_INFO, - "This test was unable to start gnunet-service-resolver, and it is required to run ...\n"); - exit (1); - } - - GNUNET_PROGRAM_run (3, argv_prog, - "test-stun", "nohelp", - options, - &run, NULL); - - /* Now kill the resolver */ - if (0 != GNUNET_OS_process_kill (proc, GNUNET_TERM_SIG)) - { - GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING, "kill"); - } - GNUNET_OS_process_wait (proc); - GNUNET_OS_process_destroy (proc); - proc = NULL; - GNUNET_free (fn); - - return ret; -} - - -/* end of test_stun.c */ diff --git a/src/nat/test_stun.conf b/src/nat/test_stun.conf deleted file mode 100644 index b03bbfff3..000000000 --- a/src/nat/test_stun.conf +++ /dev/null @@ -1,7 +0,0 @@ -[PATHS] -GNUNET_TEST_HOME = $GNUNET_TMP/test-stun - -[resolver] -PORT = 22354 -HOSTNAME = localhost - diff --git a/src/nse/.gitignore b/src/nse/.gitignore deleted file mode 100644 index a2575673c..000000000 --- a/src/nse/.gitignore +++ /dev/null @@ -1,5 +0,0 @@ -gnunet-service-nse -gnunet-nse -gnunet-nse-profiler -test_nse_api -perf_kdf diff --git a/src/nse/Makefile.am b/src/nse/Makefile.am deleted file mode 100644 index 053995c9a..000000000 --- a/src/nse/Makefile.am +++ /dev/null @@ -1,107 +0,0 @@ -# This Makefile.am is in the public domain -AM_CPPFLAGS = -I$(top_srcdir)/src/include - -if USE_COVERAGE - AM_CFLAGS = --coverage -O0 - XLIB = -lgcov -endif - -pkgcfgdir= $(pkgdatadir)/config.d/ - -libexecdir= $(pkglibdir)/libexec/ - -pkgcfg_DATA = \ - nse.conf - -bin_PROGRAMS = gnunet-nse - -gnunet_nse_SOURCES = gnunet-nse.c -gnunet_nse_LDADD = \ - libgnunetnse.la \ - $(top_builddir)/src/lib/util/libgnunetutil.la \ - $(XLIB) $(GN_LIBINTL) - -lib_LTLIBRARIES = libgnunetnse.la - -libgnunetnse_la_SOURCES = \ - nse_api.c nse.h -libgnunetnse_la_LIBADD = \ - $(top_builddir)/src/lib/util/libgnunetutil.la \ - $(GN_LIBINTL) $(XLIB) -libgnunetnse_la_LDFLAGS = \ - $(GN_LIB_LDFLAGS) \ - -version-info 0:0:0 - - -libexec_PROGRAMS = \ - gnunet-service-nse - -#noinst_PROGRAMS = \ -# gnunet-nse-profiler - -# FIXME no testbed in TNG -#gnunet_nse_profiler_SOURCES = \ -# gnunet-nse-profiler.c -#gnunet_nse_profiler_LDADD = -lm \ -# libgnunetnse.la \ -# $(top_builddir)/src/lib/util/libgnunetutil.la \ -# $(top_builddir)/src/statistics/libgnunetstatistics.la \ -# $(top_builddir)/src/lib/testing/libgnunettesting.la \ -# $(top_builddir)/src/testbed/libgnunettestbed.la \ -# $(GN_LIBINTL) - -gnunet_service_nse_SOURCES = \ - gnunet-service-nse.c -gnunet_service_nse_LDADD = \ - libgnunetnse.la \ - $(top_builddir)/src/lib/util/libgnunetutil.la \ - $(top_builddir)/src/service/core/libgnunetcore.la \ - $(top_builddir)/src/statistics/libgnunetstatistics.la \ - $(LIBGCRYPT_LIBS) \ - -lm -lgcrypt \ - $(GN_LIBINTL) -if ENABLE_NSE_HISTOGRAM - gnunet_service_nse_LDADD += \ - $(top_builddir)/src/testbed-logger/libgnunettestbedlogger.la -endif - - -if HAVE_BENCHMARKS - MULTIPEER_TEST = test_nse_multipeer -endif - -check_PROGRAMS = \ - test_nse_api \ - perf_kdf \ - $(MULTIPEER_TEST) - -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_nse_api_SOURCES = \ - test_nse_api.c -test_nse_api_LDADD = \ - libgnunetnse.la \ - $(top_builddir)/src/lib/testing/libgnunettesting.la \ - $(top_builddir)/src/lib/util/libgnunetutil.la - -test_nse_multipeer_SOURCES = \ - test_nse_multipeer.c -test_nse_multipeer_LDADD = \ - libgnunetnse.la \ - $(top_builddir)/src/lib/util/libgnunetutil.la \ - $(top_builddir)/src/testbed/libgnunettestbed.la \ - -lm - -perf_kdf_SOURCES = \ - perf_kdf.c -perf_kdf_LDADD = \ - $(top_builddir)/src/lib/util/libgnunetutil.la \ - $(LIBGCRYPT_LIBS) \ - -lgcrypt - -EXTRA_DIST = \ - test_nse.conf \ - nse_profiler_test.conf diff --git a/src/nse/gnunet-nse-profiler.c b/src/nse/gnunet-nse-profiler.c deleted file mode 100644 index 4b256bc52..000000000 --- a/src/nse/gnunet-nse-profiler.c +++ /dev/null @@ -1,921 +0,0 @@ -/* - 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 . - - SPDX-License-Identifier: AGPL3.0-or-later - */ -/** - * @file nse/gnunet-nse-profiler.c - * - * @brief Profiling driver for the network size estimation service. - * Generally, the profiler starts a given number of peers, - * then churns some off, waits a certain amount of time, then - * churns again, and repeats. - * @author Christian Grothoff - * @author Nathan Evans - * @author Sree Harsha Totakura - */ - -#include "platform.h" -#include "gnunet_testbed_service.h" -#include "gnunet_nse_service.h" - -/** - * Generic loggins shorthand - */ -#define LOG(kind, ...) GNUNET_log (kind, __VA_ARGS__) - -/** - * Debug logging shorthand - */ -#define LOG_DEBUG(...) LOG (GNUNET_ERROR_TYPE_DEBUG, __VA_ARGS__) - - -/** - * Information we track for a peer in the testbed. - */ -struct NSEPeer -{ - /** - * Prev reference in DLL. - */ - struct NSEPeer *prev; - - /** - * Next reference in DLL. - */ - struct NSEPeer *next; - - /** - * Handle with testbed. - */ - struct GNUNET_TESTBED_Peer *daemon; - - /** - * Testbed operation to connect to NSE service. - */ - struct GNUNET_TESTBED_Operation *nse_op; - - /** - * Testbed operation to connect to statistics service - */ - struct GNUNET_TESTBED_Operation *stat_op; - - /** - * Handle to the statistics service - */ - struct GNUNET_STATISTICS_Handle *sh; -}; - - -/** - * Operation map entry - */ -struct OpListEntry -{ - /** - * DLL next ptr - */ - struct OpListEntry *next; - - /** - * DLL prev ptr - */ - struct OpListEntry *prev; - - /** - * The testbed operation - */ - struct GNUNET_TESTBED_Operation *op; - - /** - * Depending on whether we start or stop NSE service at the peer set this to 1 - * or -1 - */ - int delta; -}; - - -/** - * Head of DLL of peers we monitor closely. - */ -static struct NSEPeer *peer_head; - -/** - * Tail of DLL of peers we monitor closely. - */ -static struct NSEPeer *peer_tail; - -/** - * Return value from 'main' (0 == success) - */ -static int ok; - -/** - * Be verbose (configuration option) - */ -static unsigned int verbose; - -/** - * Name of the file with the hosts to run the test over (configuration option) - */ -static char *hosts_file; - -/** - * Maximum number of peers in the test. - */ -static unsigned int num_peers; - -/** - * Total number of rounds to execute. - */ -static unsigned int num_rounds; - -/** - * Current round we are in. - */ -static unsigned int current_round; - -/** - * Array of size 'num_rounds' with the requested number of peers in the given round. - */ -static unsigned int *num_peers_in_round; - -/** - * How many peers are running right now? - */ -static unsigned int peers_running; - -/** - * Specification for the numbers of peers to have in each round. - */ -static char *num_peer_spec; - -/** - * Handles to all of the running peers. - */ -static struct GNUNET_TESTBED_Peer **daemons; - -/** - * Global configuration file - */ -static struct GNUNET_CONFIGURATION_Handle *testing_cfg; - -/** - * Maximum number of connections to NSE services. - */ -static unsigned int connection_limit; - -/** - * Total number of connections in the whole network. - */ -static unsigned int total_connections; - -/** - * File to report results to. - */ -static struct GNUNET_DISK_FileHandle *output_file; - -/** - * Filename to log results to. - */ -static char *output_filename; - -/** - * File to log connection info, statistics to. - */ -static struct GNUNET_DISK_FileHandle *data_file; - -/** - * Filename to log connection info, statistics to. - */ -static char *data_filename; - -/** - * How long to wait before triggering next round? - * Default: 60 s. - */ -static struct GNUNET_TIME_Relative wait_time = { 60 * 1000 }; - -/** - * DLL head for operation list - */ -static struct OpListEntry *oplist_head; - -/** - * DLL tail for operation list - */ -static struct OpListEntry *oplist_tail; - -/** - * Task running each round of the experiment. - */ -static struct GNUNET_SCHEDULER_Task *round_task; - - -/** - * Clean up all of the monitoring connections to NSE and - * STATISTICS that we keep to selected peers. - */ -static void -close_monitor_connections () -{ - struct NSEPeer *pos; - struct OpListEntry *oplist_entry; - - while (NULL != (pos = peer_head)) - { - if (NULL != pos->nse_op) - GNUNET_TESTBED_operation_done (pos->nse_op); - if (NULL != pos->stat_op) - GNUNET_TESTBED_operation_done (pos->stat_op); - GNUNET_CONTAINER_DLL_remove (peer_head, peer_tail, pos); - GNUNET_free (pos); - } - while (NULL != (oplist_entry = oplist_head)) - { - GNUNET_CONTAINER_DLL_remove (oplist_head, oplist_tail, oplist_entry); - GNUNET_TESTBED_operation_done (oplist_entry->op); - GNUNET_free (oplist_entry); - } -} - - -/** - * Task run on shutdown; cleans up everything. - * - * @param cls unused - */ -static void -shutdown_task (void *cls) -{ - LOG_DEBUG ("Ending test.\n"); - close_monitor_connections (); - if (NULL != round_task) - { - GNUNET_SCHEDULER_cancel (round_task); - round_task = NULL; - } - if (NULL != data_file) - { - GNUNET_DISK_file_close (data_file); - data_file = NULL; - } - if (NULL != output_file) - { - GNUNET_DISK_file_close (output_file); - output_file = NULL; - } - if (NULL != testing_cfg) - { - GNUNET_CONFIGURATION_destroy (testing_cfg); - testing_cfg = NULL; - } -} - - -/** - * Callback to call when network size estimate is updated. - * - * @param cls closure with the 'struct NSEPeer' providing the update - * @param timestamp server timestamp - * @param estimate the value of the current network size estimate - * @param std_dev standard deviation (rounded down to nearest integer) - * of the size estimation values seen - */ -static void -handle_estimate (void *cls, - struct GNUNET_TIME_Absolute timestamp, - double estimate, - double std_dev) -{ - struct NSEPeer *peer = cls; - char output_buffer[512]; - size_t size; - - if (NULL == output_file) - { - fprintf (stderr, - "Received network size estimate from peer %p. Size: %f std.dev. %f\n", - peer, - estimate, - std_dev); - return; - } - size = GNUNET_snprintf (output_buffer, - sizeof(output_buffer), - "%p %u %llu %f %f %f\n", - peer, - peers_running, - (unsigned long long) timestamp.abs_value_us, - GNUNET_NSE_log_estimate_to_n (estimate), - estimate, - std_dev); - if (size != GNUNET_DISK_file_write (output_file, output_buffer, size)) - GNUNET_log (GNUNET_ERROR_TYPE_WARNING, "Unable to write to file!\n"); -} - - -/** - * Adapter function called to establish a connection to - * NSE service. - * - * @param cls closure (the 'struct NSEPeer') - * @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 * -nse_connect_adapter (void *cls, const struct GNUNET_CONFIGURATION_Handle *cfg) -{ - struct NSEPeer *current_peer = cls; - - return GNUNET_NSE_connect (cfg, &handle_estimate, current_peer); -} - - -/** - * Adapter function called to destroy a connection to - * NSE service. - * - * @param cls closure - * @param op_result service handle returned from the connect adapter - */ -static void -nse_disconnect_adapter (void *cls, void *op_result) -{ - GNUNET_NSE_disconnect (op_result); -} - - -/** - * Callback function to process statistic values. - * - * @param cls `struct NSEPeer` - * @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 -stat_iterator (void *cls, - const char *subsystem, - const char *name, - uint64_t value, - int is_persistent) -{ - char *output_buffer; - struct GNUNET_TIME_Absolute now; - int size; - unsigned int flag; - - GNUNET_assert (NULL != data_file); - now = GNUNET_TIME_absolute_get (); - flag = strcasecmp (subsystem, "core"); - if (0 != flag) - flag = 1; - size = GNUNET_asprintf (&output_buffer, - "%llu %llu %u\n", - (unsigned long long) now.abs_value_us / 1000LL / 1000LL, - (unsigned long long) value, - flag); - if (0 > size) - { - GNUNET_log (GNUNET_ERROR_TYPE_WARNING, "Error formatting output buffer.\n"); - GNUNET_free (output_buffer); - return GNUNET_SYSERR; - } - if (size != GNUNET_DISK_file_write (data_file, output_buffer, (size_t) size)) - { - GNUNET_log (GNUNET_ERROR_TYPE_WARNING, "Unable to write to file!\n"); - GNUNET_free (output_buffer); - return GNUNET_SYSERR; - } - GNUNET_free (output_buffer); - return GNUNET_OK; -} - - -/** - * Called to open a connection to the peer's statistics - * - * @param cls peer context - * @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 * -stat_connect_adapter (void *cls, const struct GNUNET_CONFIGURATION_Handle *cfg) -{ - struct NSEPeer *peer = cls; - - peer->sh = GNUNET_STATISTICS_create ("nse-profiler", cfg); - return peer->sh; -} - - -/** - * Called to disconnect from peer's statistics service - * - * @param cls peer context - * @param op_result service handle returned from the connect adapter - */ -static void -stat_disconnect_adapter (void *cls, void *op_result) -{ - struct NSEPeer *peer = cls; - - GNUNET_break (GNUNET_OK == - GNUNET_STATISTICS_watch_cancel (peer->sh, - "core", - "# peers connected", - stat_iterator, - peer)); - GNUNET_break (GNUNET_OK == - GNUNET_STATISTICS_watch_cancel (peer->sh, - "nse", - "# peers connected", - stat_iterator, - peer)); - GNUNET_STATISTICS_destroy (op_result, GNUNET_NO); - peer->sh = NULL; -} - - -/** - * Called after successfully opening a connection to a peer's statistics - * service; we register statistics monitoring for CORE and NSE here. - * - * @param cls the callback closure from functions generating an operation - * @param op the operation that has been finished - * @param ca_result the service handle returned from GNUNET_TESTBED_ConnectAdapter() - * @param emsg error message in case the operation has failed; will be NULL if - * operation has executed successfully. - */ -static void -stat_comp_cb (void *cls, - struct GNUNET_TESTBED_Operation *op, - void *ca_result, - const char *emsg) -{ - struct GNUNET_STATISTICS_Handle *sh = ca_result; - struct NSEPeer *peer = cls; - - if (NULL != emsg) - { - GNUNET_break (0); - return; - } - GNUNET_break (GNUNET_OK == GNUNET_STATISTICS_watch (sh, - "core", - "# peers connected", - stat_iterator, - peer)); - GNUNET_break (GNUNET_OK == GNUNET_STATISTICS_watch (sh, - "nse", - "# peers connected", - stat_iterator, - peer)); -} - - -/** - * Task run to connect to the NSE and statistics services to a subset of - * all of the running peers. - */ -static void -connect_nse_service () -{ - struct NSEPeer *current_peer; - unsigned int i; - unsigned int connections; - - if (0 == connection_limit) - return; - LOG_DEBUG ("Connecting to nse service of peers\n"); - connections = 0; - for (i = 0; i < num_peers_in_round[current_round]; i++) - { - if ((num_peers_in_round[current_round] > connection_limit) && - (0 != (i % (num_peers_in_round[current_round] / connection_limit)))) - continue; - LOG_DEBUG ("Connecting to nse service of peer %d\n", i); - current_peer = GNUNET_new (struct NSEPeer); - current_peer->daemon = daemons[i]; - current_peer->nse_op = - GNUNET_TESTBED_service_connect (NULL, - current_peer->daemon, - "nse", - NULL, - NULL, - &nse_connect_adapter, - &nse_disconnect_adapter, - current_peer); - if (NULL != data_file) - current_peer->stat_op = - GNUNET_TESTBED_service_connect (NULL, - current_peer->daemon, - "statistics", - stat_comp_cb, - current_peer, - &stat_connect_adapter, - &stat_disconnect_adapter, - current_peer); - GNUNET_CONTAINER_DLL_insert (peer_head, peer_tail, current_peer); - if (++connections == connection_limit) - break; - } -} - - -/** - * Task that starts/stops peers to move to the next round. - * - * @param cls NULL, unused - */ -static void -next_round (void *cls); - - -/** - * We're at the end of a round. Stop monitoring, write total - * number of connections to log and get full stats. Then trigger - * the next round. - * - * @param cls unused, NULL - */ -static void -finish_round (void *cls) -{ - LOG (GNUNET_ERROR_TYPE_INFO, "Have %u connections\n", total_connections); - close_monitor_connections (); - round_task = GNUNET_SCHEDULER_add_now (&next_round, NULL); -} - - -/** - * We have reached the desired number of peers for the current round. - * Run it (by connecting and monitoring a few peers and waiting the - * specified delay before finishing the round). - */ -static void -run_round () -{ - LOG_DEBUG ("Running round %u\n", current_round); - connect_nse_service (); - GNUNET_SCHEDULER_add_delayed (wait_time, &finish_round, NULL); -} - - -/** - * Creates an oplist entry and adds it to the oplist DLL - */ -static struct OpListEntry * -make_oplist_entry () -{ - struct OpListEntry *entry; - - entry = GNUNET_new (struct OpListEntry); - GNUNET_CONTAINER_DLL_insert_tail (oplist_head, oplist_tail, entry); - return entry; -} - - -/** - * Callback to be called when NSE service is started or stopped at peers - * - * @param cls NULL - * @param op the operation handle - * @param emsg NULL on success; otherwise an error description - */ -static void -manage_service_cb (void *cls, - struct GNUNET_TESTBED_Operation *op, - const char *emsg) -{ - struct OpListEntry *entry = cls; - - GNUNET_TESTBED_operation_done (entry->op); - if (NULL != emsg) - { - LOG (GNUNET_ERROR_TYPE_ERROR, "Failed to start/stop NSE at a peer\n"); - GNUNET_SCHEDULER_shutdown (); - return; - } - GNUNET_assert (0 != entry->delta); - peers_running += entry->delta; - GNUNET_CONTAINER_DLL_remove (oplist_head, oplist_tail, entry); - GNUNET_free (entry); - if (num_peers_in_round[current_round] == peers_running) - run_round (); -} - - -/** - * Adjust the number of running peers to match the required number of running - * peers for the round - */ -static void -adjust_running_peers () -{ - struct OpListEntry *entry; - unsigned int i; - - /* start peers if we have too few */ - for (i = peers_running; i < num_peers_in_round[current_round]; i++) - { - entry = make_oplist_entry (); - entry->delta = 1; - entry->op = GNUNET_TESTBED_peer_manage_service (NULL, - daemons[i], - "nse", - &manage_service_cb, - entry, - 1); - } - /* stop peers if we have too many */ - for (i = num_peers_in_round[current_round]; i < peers_running; i++) - { - entry = make_oplist_entry (); - entry->delta = -1; - entry->op = GNUNET_TESTBED_peer_manage_service (NULL, - daemons[i], - "nse", - &manage_service_cb, - entry, - 0); - } -} - - -/** - * Task run at the end of a round. Disconnect from all monitored - * peers; then get statistics from *all* peers. - * - * @param cls NULL, unused - */ -static void -next_round (void *cls) -{ - round_task = NULL; - LOG_DEBUG ("Disconnecting nse service of peers\n"); - current_round++; - if (current_round == num_rounds) - { - /* this was the last round, terminate */ - ok = 0; - GNUNET_SCHEDULER_shutdown (); - return; - } - if (num_peers_in_round[current_round] == peers_running) - { - /* no need to churn, just run next round */ - run_round (); - return; - } - adjust_running_peers (); -} - - -/** - * 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: - total_connections++; - break; - - case GNUNET_TESTBED_ET_DISCONNECT: - total_connections--; - break; - - default: - break; - } -} - - -/** - * 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 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 **peers, - unsigned int links_succeeded, - unsigned int links_failed) -{ - if (NULL == peers) - { - GNUNET_SCHEDULER_shutdown (); - return; - } - daemons = peers; - GNUNET_break (num_peers_ == num_peers); - peers_running = num_peers; - if (num_peers_in_round[current_round] == peers_running) - { - /* no need to churn, just run the starting round */ - run_round (); - return; - } - adjust_running_peers (); -} - - -/** - * Actual main function that runs the emulation. - * - * @param cls unused - * @param args remaining args, unused - * @param cfgfile name of the configuration - * @param cfg configuration handle - */ -static void -run (void *cls, - char *const *args, - const char *cfgfile, - const struct GNUNET_CONFIGURATION_Handle *cfg) -{ - char *tok; - uint64_t event_mask; - unsigned int num; - - ok = 1; - testing_cfg = GNUNET_CONFIGURATION_dup (cfg); - LOG_DEBUG ("Starting daemons.\n"); - if (NULL == num_peer_spec) - { - fprintf (stderr, "You need to specify the number of peers to run\n"); - return; - } - for (tok = strtok (num_peer_spec, ","); NULL != tok; tok = strtok (NULL, ",")) - { - if (1 != sscanf (tok, "%u", &num)) - { - fprintf (stderr, "You need to specify numbers, not `%s'\n", tok); - return; - } - if (0 == num) - { - fprintf (stderr, "Refusing to run a round with 0 peers\n"); - return; - } - GNUNET_array_append (num_peers_in_round, num_rounds, num); - num_peers = GNUNET_MAX (num_peers, num); - } - if (0 == num_peers) - { - fprintf (stderr, "Refusing to run a testbed with no rounds\n"); - return; - } - 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); - - if ((NULL != output_filename) && - (NULL == - (output_file = GNUNET_DISK_file_open (output_filename, - GNUNET_DISK_OPEN_READWRITE - | GNUNET_DISK_OPEN_CREATE, - GNUNET_DISK_PERM_USER_READ - | GNUNET_DISK_PERM_USER_WRITE)))) - GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_ERROR, "open", output_filename); - event_mask = 0LL; - 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); - GNUNET_TESTBED_run (hosts_file, - cfg, - num_peers, - event_mask, - master_controller_cb, - NULL, /* master_controller_cb cls */ - &test_master, - NULL); /* test_master cls */ - GNUNET_SCHEDULER_add_shutdown (&shutdown_task, NULL); -} - - -/** - * Main function. - * - * @return 0 on success - */ -int -main (int argc, char *const *argv) -{ - struct GNUNET_GETOPT_CommandLineOption options[] = - { GNUNET_GETOPT_option_uint ( - 'C', - "connections", - "COUNT", - gettext_noop ( - "limit to the number of connections to NSE services, 0 for none"), - &connection_limit), - GNUNET_GETOPT_option_string ( - 'd', - "details", - "FILENAME", - gettext_noop ( - "name of the file for writing connection information and statistics"), - &data_filename), - - GNUNET_GETOPT_option_string ( - 'H', - "hosts", - "FILENAME", - gettext_noop ( - "name of the file with the login information for the testbed"), - &hosts_file), - - GNUNET_GETOPT_option_string ( - 'o', - "output", - "FILENAME", - gettext_noop ("name of the file for writing the main results"), - &output_filename), - - - GNUNET_GETOPT_option_string ( - 'p', - "peers", - "NETWORKSIZESPEC", - gettext_noop ( - "Number of peers to run in each round, separated by commas"), - &num_peer_spec), - - GNUNET_GETOPT_option_increment_uint ( - 'V', - "verbose", - gettext_noop ("be verbose (print progress information)"), - &verbose), - - GNUNET_GETOPT_option_relative_time ('w', - "wait", - "DELAY", - gettext_noop ("delay between rounds"), - &wait_time), - GNUNET_GETOPT_OPTION_END }; - - if (GNUNET_OK != GNUNET_STRINGS_get_utf8_args (argc, argv, &argc, &argv)) - return 2; - if ( - GNUNET_OK != - GNUNET_PROGRAM_run (argc, - argv, - "nse-profiler", - gettext_noop ( - "Measure quality and performance of the NSE service."), - options, - &run, - NULL)) - ok = 1; - return ok; -} - - -/* end of nse-profiler.c */ diff --git a/src/nse/gnunet-nse.c b/src/nse/gnunet-nse.c deleted file mode 100644 index edb73c0fc..000000000 --- a/src/nse/gnunet-nse.c +++ /dev/null @@ -1,130 +0,0 @@ -/* - This file is part of GNUnet - Copyright (C) 2008--2014, 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 . - - SPDX-License-Identifier: AGPL3.0-or-later - */ - -/** - * @file nse/gnunet-nse.c - * @brief Program to display network size estimates from the NSE service - * @author Sree Harsha Totakura - */ - -#include "platform.h" -#include "gnunet_nse_service.h" - -/** - * The handle to the NSE service - */ -static struct GNUNET_NSE_Handle *nse; - -/** - * The program status; 0 for success. - */ -static int status; - - -/** - * Task to shutdown and clean up all state - * - * @param cls NULL - */ -static void -do_shutdown (void *cls) -{ - (void) cls; - if (NULL != nse) - { - GNUNET_NSE_disconnect (nse); - nse = NULL; - } -} - - -/** - * Callback to call when network size estimate is updated. - * - * @param cls NULL - * @param timestamp server timestamp - * @param estimate the value of the current network size estimate - * @param std_dev standard deviation (rounded down to nearest integer) - * of the size estimation values seen - */ -static void -handle_estimate (void *cls, - struct GNUNET_TIME_Absolute timestamp, - double estimate, - double std_dev) -{ - (void) cls; - status = 0; - fprintf (stdout, - "%llu %f %f %f\n", - (unsigned long long) timestamp.abs_value_us, - GNUNET_NSE_log_estimate_to_n (estimate), - estimate, - std_dev); -} - - -/** - * Actual main function that runs the emulation. - * - * @param cls unused - * @param args remaining args, unused - * @param cfgfile name of the configuration - * @param cfg configuration handle - */ -static void -run (void *cls, - char *const *args, - const char *cfgfile, - const struct GNUNET_CONFIGURATION_Handle *cfg) -{ - (void) cls; - (void) args; - (void) cfgfile; - nse = GNUNET_NSE_connect (cfg, &handle_estimate, NULL); - GNUNET_SCHEDULER_add_shutdown (&do_shutdown, NULL); -} - - -/** - * Main function. - * - * @return 0 on success - */ -int -main (int argc, char *const *argv) -{ - static struct GNUNET_GETOPT_CommandLineOption options[] = { - GNUNET_GETOPT_OPTION_END - }; - - status = 1; - if (GNUNET_OK != - GNUNET_PROGRAM_run (argc, - argv, - "gnunet-nse", - gettext_noop ( - "Show network size estimates from NSE service."), - options, - &run, - NULL)) - return 2; - return status; -} diff --git a/src/nse/gnunet-service-nse.c b/src/nse/gnunet-service-nse.c deleted file mode 100644 index ee1cb025f..000000000 --- a/src/nse/gnunet-service-nse.c +++ /dev/null @@ -1,1587 +0,0 @@ -/* - This file is part of GNUnet. - Copyright (C) 2009, 2010, 2011, 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 . - - SPDX-License-Identifier: AGPL3.0-or-later - */ - -/** - * @file nse/gnunet-service-nse.c - * @brief network size estimation service - * @author Nathan Evans - * @author Christian Grothoff - * - * The purpose of this service is to estimate the size of the network. - * Given a specified interval, each peer hashes the most recent - * timestamp which is evenly divisible by that interval. This hash is - * compared in distance to the peer identity to choose an offset. The - * closer the peer identity to the hashed timestamp, the earlier the - * peer sends out a "nearest peer" message. The closest peer's - * message should thus be received before any others, which stops - * those peer from sending their messages at a later duration. So - * every peer should receive the same nearest peer message, and from - * this can calculate the expected number of peers in the network. - */ -#include "platform.h" -#include -#include "gnunet_util_lib.h" -#include "gnunet_constants.h" -#include "gnunet_protocols.h" -#include "gnunet_signatures.h" -#include "gnunet_statistics_service.h" -#include "gnunet_core_service.h" -#include "gnunet_nse_service.h" -#if ENABLE_NSE_HISTOGRAM -#include "gnunet_testbed_logger_service.h" -#endif -#include "nse.h" -#include - - -/** - * Should messages be delayed randomly? This option should be set to - * #GNUNET_NO only for experiments, not in production. - */ -#define USE_RANDOM_DELAYS GNUNET_YES - -/** - * Generate extensive debug-level log messages? - */ -#define DEBUG_NSE GNUNET_NO - -/** - * Over how many values do we calculate the weighted average? - */ -#define HISTORY_SIZE 64 - -/** - * Message priority to use. No real rush, reliability not - * required. Corking OK. - */ -#define NSE_PRIORITY \ - (GNUNET_MQ_PRIO_BACKGROUND | GNUNET_MQ_PREF_UNRELIABLE \ - | GNUNET_MQ_PREF_CORK_ALLOWED) - -#ifdef BSD -#define log2(a) (log (a) / log (2)) -#endif - -/** - * Amount of work required (W-bit collisions) for NSE proofs, in collision-bits. - */ -static unsigned long long nse_work_required; - -/** - * Interval for sending network size estimation flood requests. - */ -static struct GNUNET_TIME_Relative gnunet_nse_interval; - -/** - * Interval between proof find runs. - */ -static struct GNUNET_TIME_Relative proof_find_delay; - -#if ENABLE_NSE_HISTOGRAM - -/** - * Handle to test if testbed logger service is running or not - */ -struct GNUNET_CLIENT_TestHandle *logger_test; - -/** - * Handle for writing when we received messages to disk. - */ -static struct GNUNET_TESTBED_LOGGER_Handle *lh; - -/** - * Handle for writing message received timestamp information to disk. - */ -static struct GNUNET_BIO_WriteHandle *histogram; - -#endif - -/** - * Salt for PoW calcualations. - */ -static struct GNUNET_CRYPTO_PowSalt salt = { "gnunet-nse-proof" }; - - -/** - * Per-peer information. - */ -struct NSEPeerEntry -{ - /** - * Core handle for sending messages to this peer. - */ - struct GNUNET_MQ_Handle *mq; - - /** - * What is the identity of the peer? - */ - const struct GNUNET_PeerIdentity *id; - - /** - * Task scheduled to send message to this peer. - */ - struct GNUNET_SCHEDULER_Task *transmit_task; - - /** - * Did we receive or send a message about the previous round - * to this peer yet? #GNUNET_YES if the previous round has - * been taken care of. - */ - int previous_round; - -#if ENABLE_NSE_HISTOGRAM - /** - * Amount of messages received from this peer on this round. - */ - unsigned int received_messages; - - /** - * Amount of messages transmitted to this peer on this round. - */ - unsigned int transmitted_messages; - - /** - * Which size did we tell the peer the network is? - */ - unsigned int last_transmitted_size; -#endif -}; - - -GNUNET_NETWORK_STRUCT_BEGIN - -/** - * Network size estimate reply; sent when "this" - * peer's timer has run out before receiving a - * valid reply from another peer. - */ -struct GNUNET_NSE_FloodMessage -{ - /** - * Type: #GNUNET_MESSAGE_TYPE_NSE_P2P_FLOOD - */ - struct GNUNET_MessageHeader header; - - /** - * Number of hops this message has taken so far. - */ - uint32_t hop_count GNUNET_PACKED; - - /** - * Purpose. - */ - struct GNUNET_CRYPTO_EccSignaturePurpose purpose; - - /** - * The current timestamp value (which all - * peers should agree on). - */ - struct GNUNET_TIME_AbsoluteNBO timestamp; - - /** - * Number of matching bits between the hash - * of timestamp and the initiator's public - * key. - */ - uint32_t matching_bits GNUNET_PACKED; - - /** - * Public key of the originator. - */ - struct GNUNET_PeerIdentity origin; - - /** - * Proof of work, causing leading zeros when hashed with pkey. - */ - uint64_t proof_of_work GNUNET_PACKED; - - /** - * Signature (over range specified in purpose). - */ - struct GNUNET_CRYPTO_EddsaSignature signature; -}; -GNUNET_NETWORK_STRUCT_END - -/** - * Handle to our current configuration. - */ -static const struct GNUNET_CONFIGURATION_Handle *cfg; - -/** - * Handle to the statistics service. - */ -static struct GNUNET_STATISTICS_Handle *stats; - -/** - * Handle to the core service. - */ -static struct GNUNET_CORE_Handle *core_api; - -/** - * Map of all connected peers. - */ -static struct GNUNET_CONTAINER_MultiPeerMap *peers; - -/** - * The current network size estimate. Number of bits matching on - * average thus far. - */ -static double current_size_estimate; - -/** - * The standard deviation of the last #HISTORY_SIZE network - * size estimates. - */ -static double current_std_dev = NAN; - -/** - * Current hop counter estimate (estimate for network diameter). - */ -static uint32_t hop_count_max; - -/** - * Message for the next round, if we got any. - */ -static struct GNUNET_NSE_FloodMessage next_message; - -/** - * Array of recent size estimate messages. - */ -static struct GNUNET_NSE_FloodMessage size_estimate_messages[HISTORY_SIZE]; - -/** - * Index of most recent estimate. - */ -static unsigned int estimate_index; - -/** - * Number of valid entries in the history. - */ -static unsigned int estimate_count; - -/** - * Task scheduled to update our flood message for the next round. - */ -static struct GNUNET_SCHEDULER_Task *flood_task; - -/** - * Task scheduled to compute our proof. - */ -static struct GNUNET_SCHEDULER_Task *proof_task; - -/** - * Notification context, simplifies client broadcasts. - */ -static struct GNUNET_NotificationContext *nc; - -/** - * The next major time. - */ -static struct GNUNET_TIME_Absolute next_timestamp; - -/** - * The current major time. - */ -static struct GNUNET_TIME_Absolute current_timestamp; - -/** - * The private key of this peer. - */ -static struct GNUNET_CRYPTO_EddsaPrivateKey *my_private_key; - -/** - * The peer identity of this peer. - */ -static struct GNUNET_PeerIdentity my_identity; - -/** - * Proof of work for this peer. - */ -static uint64_t my_proof; - - -/** - * Initialize a message to clients with the current network - * size estimate. - * - * @param em message to fill in - */ -static void -setup_estimate_message (struct GNUNET_NSE_ClientMessage *em) -{ - double mean; - double sum; - double std_dev; - double variance; - double val; - double nsize; - -#define WEST 1 - /* Weighted incremental algorithm for stddev according to West (1979) */ -#if WEST - double sumweight; - double weight; - double q; - double r; - double temp; - - mean = 0.0; - sum = 0.0; - sumweight = 0.0; - variance = 0.0; - for (unsigned int i = 0; i < estimate_count; i++) - { - unsigned int j = (estimate_index - i + HISTORY_SIZE) % HISTORY_SIZE; - - val = htonl (size_estimate_messages[j].matching_bits); - weight = estimate_count + 1 - i; - - temp = weight + sumweight; - q = val - mean; - r = q * weight / temp; - mean += r; - sum += sumweight * q * r; - sumweight = temp; - } - if (estimate_count > 0) - variance = (sum / sumweight) * estimate_count / (estimate_count - 1.0); -#else - /* trivial version for debugging */ - double vsq; - - /* non-weighted trivial version */ - sum = 0.0; - vsq = 0.0; - variance = 0.0; - mean = 0.0; - - for (unsigned int i = 0; i < estimate_count; i++) - { - unsigned int j = (estimate_index - i + HISTORY_SIZE) % HISTORY_SIZE; - - val = htonl (size_estimate_messages[j].matching_bits); - sum += val; - vsq += val * val; - } - if (0 != estimate_count) - { - mean = sum / estimate_count; - variance = (vsq - mean * sum) - / (estimate_count - 1.0); // terrible for numerical stability... - } -#endif - if (variance >= 0) - std_dev = sqrt (variance); - else - std_dev = variance; /* return NaN (due to estimate_count == 0 causing 0.0/0.0) */ - current_std_dev = std_dev; - current_size_estimate = mean; - - em->header.size = htons (sizeof(struct GNUNET_NSE_ClientMessage)); - em->header.type = htons (GNUNET_MESSAGE_TYPE_NSE_ESTIMATE); - em->reserved = htonl (0); - em->timestamp = GNUNET_TIME_absolute_hton (GNUNET_TIME_absolute_get ()); - { - double se = mean - 0.332747; - unsigned int j = GNUNET_CONTAINER_multipeermap_size (peers); - if (0 == j) - j = 1; /* Avoid log2(0); can only happen if CORE didn't report - connection to self yet */ - nsize = log2 (j); - em->size_estimate = GNUNET_hton_double (GNUNET_MAX (se, nsize)); - em->std_deviation = GNUNET_hton_double (std_dev); - GNUNET_STATISTICS_set (stats, - "# nodes in the network (estimate)", - (uint64_t) pow (2, GNUNET_MAX (se, nsize)), - GNUNET_NO); - } -} - - -/** - * Handler for START message from client, triggers an - * immediate current network estimate notification. - * Also, we remember the client for updates upon future - * estimate measurements. - * - * @param cls client who sent the message - * @param message the message received - */ -static void -handle_start (void *cls, const struct GNUNET_MessageHeader *message) -{ - struct GNUNET_SERVICE_Client *client = cls; - struct GNUNET_MQ_Handle *mq; - struct GNUNET_NSE_ClientMessage em; - struct GNUNET_MQ_Envelope *env; - - (void) message; - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Received START message from client\n"); - mq = GNUNET_SERVICE_client_get_mq (client); - GNUNET_notification_context_add (nc, mq); - setup_estimate_message (&em); - env = GNUNET_MQ_msg_copy (&em.header); - GNUNET_MQ_send (mq, env); - GNUNET_SERVICE_client_continue (client); -} - - -/** - * How long should we delay a message to go the given number of - * matching bits? - * - * @param matching_bits number of matching bits to consider - */ -static double -get_matching_bits_delay (uint32_t matching_bits) -{ - /* Calculated as: S + f/2 - (f / pi) * (atan(x - p')) */ - // S is next_timestamp (ignored in return value) - // f is frequency (gnunet_nse_interval) - // x is matching_bits - // p' is current_size_estimate - return ((double) gnunet_nse_interval.rel_value_us / (double) 2.0) - - ((gnunet_nse_interval.rel_value_us / M_PI) - * atan (matching_bits - current_size_estimate)); -} - - -/** - * What delay randomization should we apply for a given number of matching bits? - * - * @param matching_bits number of matching bits - * @return random delay to apply - */ -static struct GNUNET_TIME_Relative -get_delay_randomization (uint32_t matching_bits) -{ -#if USE_RANDOM_DELAYS - struct GNUNET_TIME_Relative ret; - uint32_t i; - double d; - - d = get_matching_bits_delay (matching_bits); - i = (uint32_t) (d / (double) (hop_count_max + 1)); - ret.rel_value_us = i; - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "Randomizing flood using latencies up to %s\n", - GNUNET_STRINGS_relative_time_to_string (ret, GNUNET_YES)); - ret.rel_value_us = - GNUNET_CRYPTO_random_u32 (GNUNET_CRYPTO_QUALITY_WEAK, i + 1); - return ret; -#else - return GNUNET_TIME_UNIT_ZERO; -#endif -} - - -/** - * Get the number of matching bits that the given timestamp has to the given peer ID. - * - * @param timestamp time to generate key - * @param id peer identity to compare with - * @return number of matching bits - */ -static uint32_t -get_matching_bits (struct GNUNET_TIME_Absolute timestamp, - const struct GNUNET_PeerIdentity *id) -{ - struct GNUNET_HashCode timestamp_hash; - struct GNUNET_HashCode pid_hash; - struct GNUNET_HashCode xor; - - GNUNET_CRYPTO_hash (×tamp.abs_value_us, - sizeof(timestamp.abs_value_us), - ×tamp_hash); - GNUNET_CRYPTO_hash (id, - sizeof(struct GNUNET_PeerIdentity), - &pid_hash); - GNUNET_CRYPTO_hash_xor (&pid_hash, - ×tamp_hash, - &xor); - return GNUNET_CRYPTO_hash_count_leading_zeros (&xor); -} - - -/** - * Get the transmission delay that should be applied for a - * particular round. - * - * @param round_offset -1 for the previous round (random delay between 0 and 50ms) - * 0 for the current round (based on our proximity to time key) - * @return delay that should be applied - */ -static struct GNUNET_TIME_Relative -get_transmit_delay (int round_offset) -{ - struct GNUNET_TIME_Relative ret; - struct GNUNET_TIME_Absolute tgt; - double dist_delay; - uint32_t matching_bits; - - switch (round_offset) - { - case -1: - /* previous round is randomized between 0 and 50 ms */ -#if USE_RANDOM_DELAYS - ret.rel_value_us = - GNUNET_CRYPTO_random_u64 (GNUNET_CRYPTO_QUALITY_WEAK, 50); -#else - ret = GNUNET_TIME_UNIT_ZERO; -#endif - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "Transmitting previous round behind schedule in %s\n", - GNUNET_STRINGS_relative_time_to_string (ret, GNUNET_YES)); - return ret; - - case 0: - /* current round is based on best-known matching_bits */ - matching_bits = - ntohl (size_estimate_messages[estimate_index].matching_bits); - dist_delay = get_matching_bits_delay (matching_bits); - dist_delay += get_delay_randomization (matching_bits).rel_value_us; - ret.rel_value_us = (uint64_t) dist_delay; - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "For round %s, delay for %u matching bits is %s\n", - GNUNET_STRINGS_absolute_time_to_string (current_timestamp), - (unsigned int) matching_bits, - GNUNET_STRINGS_relative_time_to_string (ret, GNUNET_YES)); - /* now consider round start time and add delay to it */ - tgt = GNUNET_TIME_absolute_add (current_timestamp, ret); - return GNUNET_TIME_absolute_get_remaining (tgt); - } - GNUNET_break (0); - return GNUNET_TIME_UNIT_FOREVER_REL; -} - - -/** - * Task that triggers a NSE P2P transmission. - * - * @param cls the `struct NSEPeerEntry *` - */ -static void -transmit_task_cb (void *cls) -{ - struct NSEPeerEntry *peer_entry = cls; - unsigned int idx; - struct GNUNET_MQ_Envelope *env; - - peer_entry->transmit_task = NULL; - idx = estimate_index; - if (GNUNET_NO == peer_entry->previous_round) - { - idx = (idx + HISTORY_SIZE - 1) % HISTORY_SIZE; - peer_entry->previous_round = GNUNET_YES; - peer_entry->transmit_task = - GNUNET_SCHEDULER_add_delayed (get_transmit_delay (0), - &transmit_task_cb, - peer_entry); - } - if ((0 == ntohl (size_estimate_messages[idx].hop_count)) && - (NULL != proof_task)) - { - GNUNET_STATISTICS_update (stats, - "# flood messages not generated (no proof yet)", - 1, - GNUNET_NO); - return; - } - if (0 == ntohs (size_estimate_messages[idx].header.size)) - { - GNUNET_STATISTICS_update (stats, - "# flood messages not generated (lack of history)", - 1, - GNUNET_NO); - return; - } - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "In round %s, sending to `%s' estimate with %u bits\n", - GNUNET_STRINGS_absolute_time_to_string ( - GNUNET_TIME_absolute_ntoh ( - size_estimate_messages[idx].timestamp)), - GNUNET_i2s (peer_entry->id), - (unsigned int) ntohl (size_estimate_messages[idx].matching_bits)); - if (0 == ntohl (size_estimate_messages[idx].hop_count)) - GNUNET_STATISTICS_update (stats, "# flood messages started", 1, GNUNET_NO); - GNUNET_STATISTICS_update (stats, - "# flood messages transmitted", - 1, - GNUNET_NO); -#if ENABLE_NSE_HISTOGRAM - peer_entry->transmitted_messages++; - peer_entry->last_transmitted_size = - ntohl (size_estimate_messages[idx].matching_bits); -#endif - env = GNUNET_MQ_msg_copy (&size_estimate_messages[idx].header); - GNUNET_MQ_send (peer_entry->mq, env); -} - - -/** - * We've sent on our flood message or one that we received which was - * validated and closer than ours. Update the global list of recent - * messages and the average. Also re-broadcast the message to any - * clients. - */ -static void -update_network_size_estimate () -{ - struct GNUNET_NSE_ClientMessage em; - - setup_estimate_message (&em); - GNUNET_notification_context_broadcast (nc, &em.header, GNUNET_YES); -} - - -/** - * Setup a flood message in our history array at the given - * slot offset for the given timestamp. - * - * @param slot index to use - * @param ts timestamp to use - */ -static void -setup_flood_message (unsigned int slot, struct GNUNET_TIME_Absolute ts) -{ - struct GNUNET_NSE_FloodMessage *fm; - uint32_t matching_bits; - - matching_bits = get_matching_bits (ts, &my_identity); - fm = &size_estimate_messages[slot]; - fm->header.size = htons (sizeof(struct GNUNET_NSE_FloodMessage)); - fm->header.type = htons (GNUNET_MESSAGE_TYPE_NSE_P2P_FLOOD); - fm->hop_count = htonl (0); - fm->purpose.purpose = htonl (GNUNET_SIGNATURE_PURPOSE_NSE_SEND); - fm->purpose.size = - htonl (sizeof(struct GNUNET_NSE_FloodMessage) - - sizeof(struct GNUNET_MessageHeader) - sizeof(uint32_t) - - sizeof(struct GNUNET_CRYPTO_EddsaSignature)); - fm->matching_bits = htonl (matching_bits); - fm->timestamp = GNUNET_TIME_absolute_hton (ts); - fm->origin = my_identity; - fm->proof_of_work = my_proof; - if (nse_work_required > 0) - GNUNET_assert (GNUNET_OK == - GNUNET_CRYPTO_eddsa_sign_ (my_private_key, - &fm->purpose, - &fm->signature)); - else - memset (&fm->signature, 0, sizeof(fm->signature)); -} - - -/** - * Schedule transmission for the given peer for the current round based - * on what we know about the desired delay. - * - * @param cls unused - * @param key hash of peer identity - * @param value the `struct NSEPeerEntry` - * @return #GNUNET_OK (continue to iterate) - */ -static int -schedule_current_round (void *cls, - const struct GNUNET_PeerIdentity *key, - void *value) -{ - struct NSEPeerEntry *peer_entry = value; - struct GNUNET_TIME_Relative delay; - - (void) cls; - (void) key; - if (NULL != peer_entry->transmit_task) - { - GNUNET_SCHEDULER_cancel (peer_entry->transmit_task); - peer_entry->previous_round = GNUNET_NO; - } -#if ENABLE_NSE_HISTOGRAM - if (peer_entry->received_messages > 1) - GNUNET_STATISTICS_update (stats, - "# extra messages", - peer_entry->received_messages - 1, - GNUNET_NO); - peer_entry->transmitted_messages = 0; - peer_entry->last_transmitted_size = 0; - peer_entry->received_messages = 0; -#endif - delay = - get_transmit_delay ((GNUNET_NO == peer_entry->previous_round) ? -1 : 0); - peer_entry->transmit_task = - GNUNET_SCHEDULER_add_delayed (delay, &transmit_task_cb, peer_entry); - return GNUNET_OK; -} - - -/** - * Update our flood message to be sent (and our timestamps). - * - * @param cls unused - */ -static void -update_flood_message (void *cls) -{ - struct GNUNET_TIME_Relative offset; - - (void) cls; - flood_task = NULL; - offset = GNUNET_TIME_absolute_get_remaining (next_timestamp); - if (0 != offset.rel_value_us) - { - /* somehow run early, delay more */ - flood_task = - GNUNET_SCHEDULER_add_delayed (offset, &update_flood_message, NULL); - return; - } - estimate_index = (estimate_index + 1) % HISTORY_SIZE; - if (estimate_count < HISTORY_SIZE) - estimate_count++; - current_timestamp = next_timestamp; - next_timestamp = - GNUNET_TIME_absolute_add (current_timestamp, gnunet_nse_interval); - if ((current_timestamp.abs_value_us == - GNUNET_TIME_absolute_ntoh (next_message.timestamp).abs_value_us) && - (get_matching_bits (current_timestamp, &my_identity) < - ntohl (next_message.matching_bits))) - { - /* we received a message for this round way early, use it! */ - size_estimate_messages[estimate_index] = next_message; - size_estimate_messages[estimate_index].hop_count = - htonl (1 + ntohl (next_message.hop_count)); - } - else - setup_flood_message (estimate_index, current_timestamp); - next_message.matching_bits = htonl (0); /* reset for 'next' round */ - hop_count_max = 0; - for (unsigned int i = 0; i < HISTORY_SIZE; i++) - hop_count_max = - GNUNET_MAX (ntohl (size_estimate_messages[i].hop_count), hop_count_max); - GNUNET_CONTAINER_multipeermap_iterate (peers, &schedule_current_round, NULL); - flood_task = - GNUNET_SCHEDULER_add_at (next_timestamp, &update_flood_message, NULL); -} - - -/** - * Check whether the given public key and integer are a valid proof of - * work. - * - * @param pkey the public key - * @param val the integer - * @return #GNUNET_YES if valid, #GNUNET_NO if not - */ -static enum GNUNET_GenericReturnValue -check_proof_of_work (const struct GNUNET_CRYPTO_EddsaPublicKey *pkey, - uint64_t val) -{ - char buf[sizeof(struct GNUNET_CRYPTO_EddsaPublicKey) - + sizeof(val)] GNUNET_ALIGN; - struct GNUNET_HashCode result; - - GNUNET_memcpy (buf, &val, sizeof(val)); - GNUNET_memcpy (&buf[sizeof(val)], - pkey, - sizeof(struct GNUNET_CRYPTO_EddsaPublicKey)); - GNUNET_CRYPTO_pow_hash (&salt, - buf, - sizeof(buf), - &result); - return (GNUNET_CRYPTO_hash_count_leading_zeros (&result) >= - nse_work_required) - ? GNUNET_YES - : GNUNET_NO; -} - - -/** - * Write our current proof to disk. - */ -static void -write_proof (void) -{ - char *proof; - - if (GNUNET_OK != - GNUNET_CONFIGURATION_get_value_filename (cfg, - "NSE", - "PROOFFILE", - &proof)) - return; - (void) GNUNET_DISK_directory_remove (proof); - if (GNUNET_OK != - GNUNET_DISK_fn_write (proof, - &my_proof, - sizeof(my_proof), - GNUNET_DISK_PERM_USER_READ - | GNUNET_DISK_PERM_USER_WRITE)) - GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING, - "write", - proof); - GNUNET_free (proof); -} - - -/** - * Find our proof of work. - * - * @param cls closure (unused) - */ -static void -find_proof (void *cls) -{ -#define ROUND_SIZE 10 - uint64_t counter; - char buf[sizeof(struct GNUNET_CRYPTO_EddsaPublicKey) - + sizeof(uint64_t)] GNUNET_ALIGN; - struct GNUNET_HashCode result; - unsigned int i; - - (void) cls; - proof_task = NULL; - GNUNET_memcpy (&buf[sizeof(uint64_t)], - &my_identity, - sizeof(struct GNUNET_PeerIdentity)); - i = 0; - counter = my_proof; - while ((counter != UINT64_MAX) && (i < ROUND_SIZE)) - { - GNUNET_memcpy (buf, &counter, sizeof(uint64_t)); - GNUNET_CRYPTO_pow_hash (&salt, - buf, - sizeof(buf), - &result); - if (nse_work_required <= - GNUNET_CRYPTO_hash_count_leading_zeros (&result)) - { - my_proof = counter; - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "Proof of work found: %llu!\n", - (unsigned long long) GNUNET_ntohll (counter)); - write_proof (); - setup_flood_message (estimate_index, current_timestamp); - return; - } - counter++; - i++; - } - if (my_proof / (100 * ROUND_SIZE) < counter / (100 * ROUND_SIZE)) - { - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "Testing proofs currently at %llu\n", - (unsigned long long) counter); - /* remember progress every 100 rounds */ - my_proof = counter; - write_proof (); - } - else - { - my_proof = counter; - } - proof_task = - GNUNET_SCHEDULER_add_delayed_with_priority (proof_find_delay, - GNUNET_SCHEDULER_PRIORITY_IDLE, - &find_proof, - NULL); -} - - -/** - * An incoming flood message has been received which claims - * to have more bits matching than any we know in this time - * period. Verify the signature and/or proof of work. - * - * @param incoming_flood the message to verify - * @return #GNUNET_YES if the message is verified - * #GNUNET_NO if the key/signature don't verify - */ -static int -verify_message_crypto (const struct GNUNET_NSE_FloodMessage *incoming_flood) -{ - if (GNUNET_YES != check_proof_of_work (&incoming_flood->origin.public_key, - incoming_flood->proof_of_work)) - { - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "Proof of work invalid: %llu!\n", - (unsigned long long) GNUNET_ntohll ( - incoming_flood->proof_of_work)); - GNUNET_break_op (0); - return GNUNET_NO; - } - if ((nse_work_required > 0) && - (GNUNET_OK != - GNUNET_CRYPTO_eddsa_verify_ (GNUNET_SIGNATURE_PURPOSE_NSE_SEND, - &incoming_flood->purpose, - &incoming_flood->signature, - &incoming_flood->origin.public_key))) - { - GNUNET_break_op (0); - return GNUNET_NO; - } - return GNUNET_YES; -} - - -/** - * Update transmissions for the given peer for the current round based - * on updated proximity information. - * - * @param cls peer entry to exclude from updates - * @param key hash of peer identity - * @param value the `struct NSEPeerEntry *` of a peer to transmit to - * @return #GNUNET_OK (continue to iterate) - */ -static int -update_flood_times (void *cls, - const struct GNUNET_PeerIdentity *key, - void *value) -{ - struct NSEPeerEntry *exclude = cls; - struct NSEPeerEntry *peer_entry = value; - struct GNUNET_TIME_Relative delay; - - (void) key; - if (peer_entry == exclude) - return GNUNET_OK; /* trigger of the update */ - if (GNUNET_NO == peer_entry->previous_round) - { - /* still stuck in previous round, no point to update, check that - * we are active here though... */ - if (NULL == peer_entry->transmit_task) - { - GNUNET_break (0); - } - return GNUNET_OK; - } - if (NULL != peer_entry->transmit_task) - { - GNUNET_SCHEDULER_cancel (peer_entry->transmit_task); - peer_entry->transmit_task = NULL; - } - delay = get_transmit_delay (0); - peer_entry->transmit_task = - GNUNET_SCHEDULER_add_delayed (delay, &transmit_task_cb, peer_entry); - return GNUNET_OK; -} - - -/** - * Core handler for size estimate flooding messages. - * - * @param cls peer this message is from - * @param incoming_flood received message - */ -static void -handle_p2p_estimate (void *cls, - const struct GNUNET_NSE_FloodMessage *incoming_flood) -{ - struct NSEPeerEntry *peer_entry = cls; - struct GNUNET_TIME_Absolute ts; - uint32_t matching_bits; - unsigned int idx; - -#if ENABLE_NSE_HISTOGRAM - { - uint64_t t; - - t = GNUNET_TIME_absolute_get ().abs_value_us; - if (NULL != lh) - GNUNET_TESTBED_LOGGER_write (lh, &t, sizeof(uint64_t)); - if (NULL != histogram) - GNUNET_BIO_write_int64 (histogram, "histogram-time", t); - } -#endif - GNUNET_STATISTICS_update (stats, "# flood messages received", 1, GNUNET_NO); - matching_bits = ntohl (incoming_flood->matching_bits); -#if DEBUG_NSE - { - char origin[5]; - char pred[5]; - struct GNUNET_PeerIdentity os; - - GNUNET_snprintf (origin, - sizeof(origin), - "%s", - GNUNET_i2s (&incoming_flood->origin)); - GNUNET_snprintf (pred, sizeof(pred), "%s", GNUNET_i2s (peer_entry->id)); - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "Flood at %s from `%s' via `%s' at `%s' with bits %u\n", - GNUNET_STRINGS_absolute_time_to_string ( - GNUNET_TIME_absolute_ntoh (incoming_flood->timestamp)), - origin, - pred, - GNUNET_i2s (&my_identity), - (unsigned int) matching_bits); - } -#endif - -#if ENABLE_NSE_HISTOGRAM - peer_entry->received_messages++; - if ((peer_entry->transmitted_messages > 0) && - (peer_entry->last_transmitted_size >= matching_bits) ) - GNUNET_STATISTICS_update (stats, "# cross messages", 1, GNUNET_NO); -#endif - - ts = GNUNET_TIME_absolute_ntoh (incoming_flood->timestamp); - if (ts.abs_value_us == current_timestamp.abs_value_us) - idx = estimate_index; - else if (ts.abs_value_us == - current_timestamp.abs_value_us - gnunet_nse_interval.rel_value_us) - idx = (estimate_index + HISTORY_SIZE - 1) % HISTORY_SIZE; - else if (ts.abs_value_us == next_timestamp.abs_value_us) - { - if (matching_bits <= ntohl (next_message.matching_bits)) - return; /* ignore, simply too early/late */ - if (GNUNET_YES != verify_message_crypto (incoming_flood)) - { - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - "Peer %s is likely ill-configured!\n", - GNUNET_i2s (peer_entry->id)); - GNUNET_break_op (0); - return; - } - next_message = *incoming_flood; - return; - } - else - { - GNUNET_STATISTICS_update (stats, - "# flood messages discarded (clock skew too large)", - 1, - GNUNET_NO); - return; - } - if (0 == (GNUNET_memcmp (peer_entry->id, &my_identity))) - { - /* send to self, update our own estimate IF this also comes from us! */ - if (0 == GNUNET_memcmp (&incoming_flood->origin, &my_identity)) - update_network_size_estimate (); - return; - } - if (matching_bits == ntohl (size_estimate_messages[idx].matching_bits)) - { - /* Cancel transmission in the other direction, as this peer clearly has - up-to-date information already. Even if we didn't talk to this peer in - the previous round, we should no longer send it stale information as it - told us about the current round! */ - peer_entry->previous_round = GNUNET_YES; - if (idx != estimate_index) - { - /* do not transmit information for the previous round to this peer - anymore (but allow current round) */ - return; - } - /* got up-to-date information for current round, cancel transmission to - * this peer altogether */ - if (NULL != peer_entry->transmit_task) - { - GNUNET_SCHEDULER_cancel (peer_entry->transmit_task); - peer_entry->transmit_task = NULL; - } - return; - } - if (matching_bits < ntohl (size_estimate_messages[idx].matching_bits)) - { - if ((idx < estimate_index) && (peer_entry->previous_round == GNUNET_YES)) - { - peer_entry->previous_round = GNUNET_NO; - } - /* push back our result now, that peer is spreading bad information... */ - if (NULL != peer_entry->transmit_task) - GNUNET_SCHEDULER_cancel (peer_entry->transmit_task); - peer_entry->transmit_task = - GNUNET_SCHEDULER_add_now (&transmit_task_cb, peer_entry); - /* Not closer than our most recent message, no need to do work here */ - GNUNET_STATISTICS_update (stats, - "# flood messages ignored (had closer already)", - 1, - GNUNET_NO); - return; - } - if (GNUNET_YES != verify_message_crypto (incoming_flood)) - { - GNUNET_break_op (0); - return; - } - GNUNET_assert (matching_bits > - ntohl (size_estimate_messages[idx].matching_bits)); - /* Cancel transmission in the other direction, as this peer clearly has - * up-to-date information already. - */ - peer_entry->previous_round = GNUNET_YES; - if (idx == estimate_index) - { - /* cancel any activity for current round */ - if (NULL != peer_entry->transmit_task) - { - GNUNET_SCHEDULER_cancel (peer_entry->transmit_task); - peer_entry->transmit_task = NULL; - } - } - size_estimate_messages[idx] = *incoming_flood; - size_estimate_messages[idx].hop_count = - htonl (ntohl (incoming_flood->hop_count) + 1); - hop_count_max = - GNUNET_MAX (ntohl (incoming_flood->hop_count) + 1, hop_count_max); - GNUNET_STATISTICS_set (stats, - "# estimated network diameter", - hop_count_max, - GNUNET_NO); - - /* have a new, better size estimate, inform clients */ - update_network_size_estimate (); - - /* flood to rest */ - GNUNET_CONTAINER_multipeermap_iterate (peers, - &update_flood_times, - peer_entry); -} - - -/** - * Method called whenever a peer connects. Sets up the PeerEntry and - * schedules the initial size info transmission to this peer. - * - * @param cls closure - * @param peer peer identity this notification is about - */ -static void * -handle_core_connect (void *cls, - const struct GNUNET_PeerIdentity *peer, - struct GNUNET_MQ_Handle *mq) -{ - struct NSEPeerEntry *peer_entry; - - (void) cls; - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "Peer `%s' connected to us\n", - GNUNET_i2s (peer)); - /* set our default transmission options */ - GNUNET_MQ_set_options (mq, NSE_PRIORITY); - /* create our peer entry for this peer */ - peer_entry = GNUNET_new (struct NSEPeerEntry); - peer_entry->id = peer; - peer_entry->mq = mq; - GNUNET_assert (GNUNET_OK == - GNUNET_CONTAINER_multipeermap_put ( - peers, - peer_entry->id, - peer_entry, - GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_ONLY)); - peer_entry->transmit_task = - GNUNET_SCHEDULER_add_delayed (get_transmit_delay (-1), - &transmit_task_cb, - peer_entry); - GNUNET_STATISTICS_update (stats, "# peers connected", 1, GNUNET_NO); - return peer_entry; -} - - -/** - * Method called whenever a peer disconnects. Deletes the PeerEntry and cancels - * any pending transmission requests to that peer. - * - * @param cls closure - * @param peer peer identity this notification is about - * @param internal_cls the `struct NSEPeerEntry` for the @a peer - */ -static void -handle_core_disconnect (void *cls, - const struct GNUNET_PeerIdentity *peer, - void *internal_cls) -{ - struct NSEPeerEntry *pos = internal_cls; - - (void) cls; - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "Peer `%s' disconnected from us\n", - GNUNET_i2s (peer)); - GNUNET_assert (GNUNET_YES == - GNUNET_CONTAINER_multipeermap_remove (peers, peer, pos)); - if (NULL != pos->transmit_task) - { - GNUNET_SCHEDULER_cancel (pos->transmit_task); - pos->transmit_task = NULL; - } - GNUNET_free (pos); - GNUNET_STATISTICS_update (stats, "# peers connected", -1, GNUNET_NO); -} - - -#if ENABLE_NSE_HISTOGRAM -/** - * Functions of this type are called to notify a successful transmission of the - * message to the logger service - * - * @param cls NULL - * @param size the amount of data sent (ignored) - */ -static void -flush_comp_cb (void *cls, size_t size) -{ - (void) cls; - (void) size; - GNUNET_TESTBED_LOGGER_disconnect (lh); - lh = NULL; -} - - -#endif - - -/** - * Task run during shutdown. - * - * @param cls unused - */ -static void -shutdown_task (void *cls) -{ - (void) cls; - if (NULL != flood_task) - { - GNUNET_SCHEDULER_cancel (flood_task); - flood_task = NULL; - } - if (NULL != proof_task) - { - GNUNET_SCHEDULER_cancel (proof_task); - proof_task = NULL; - write_proof (); /* remember progress */ - } - if (NULL != nc) - { - GNUNET_notification_context_destroy (nc); - nc = NULL; - } - if (NULL != core_api) - { - GNUNET_CORE_disconnect (core_api); - core_api = NULL; - } - if (NULL != stats) - { - GNUNET_STATISTICS_destroy (stats, GNUNET_NO); - stats = NULL; - } - if (NULL != peers) - { - GNUNET_CONTAINER_multipeermap_destroy (peers); - peers = NULL; - } - if (NULL != my_private_key) - { - GNUNET_free (my_private_key); - my_private_key = NULL; - } -#if ENABLE_NSE_HISTOGRAM - if (NULL != logger_test) - { - GNUNET_CLIENT_service_test_cancel (logger_test); - logger_test = NULL; - } - if (NULL != lh) - { - GNUNET_TESTBED_LOGGER_flush (lh, &flush_comp_cb, NULL); - } - if (NULL != histogram) - { - GNUNET_BIO_write_close (histogram, NULL); - histogram = NULL; - } -#endif -} - - -/** - * Called on core init/fail. - * - * @param cls service closure - * @param identity the public identity of this peer - */ -static void -core_init (void *cls, const struct GNUNET_PeerIdentity *identity) -{ - struct GNUNET_TIME_Absolute now; - struct GNUNET_TIME_Absolute prev_time; - - (void) cls; - if (NULL == identity) - { - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Connection to core FAILED!\n"); - GNUNET_SCHEDULER_shutdown (); - return; - } - GNUNET_assert (0 == GNUNET_memcmp (&my_identity, identity)); - now = GNUNET_TIME_absolute_get (); - current_timestamp.abs_value_us = - (now.abs_value_us / gnunet_nse_interval.rel_value_us) - * gnunet_nse_interval.rel_value_us; - next_timestamp = - GNUNET_TIME_absolute_add (current_timestamp, gnunet_nse_interval); - estimate_index = HISTORY_SIZE - 1; - estimate_count = 0; - if (GNUNET_YES == check_proof_of_work (&my_identity.public_key, my_proof)) - { - int idx = (estimate_index + HISTORY_SIZE - 1) % HISTORY_SIZE; - prev_time.abs_value_us = - current_timestamp.abs_value_us - gnunet_nse_interval.rel_value_us; - setup_flood_message (idx, prev_time); - setup_flood_message (estimate_index, current_timestamp); - estimate_count++; - } - flood_task = - GNUNET_SCHEDULER_add_at (next_timestamp, &update_flood_message, NULL); -} - - -#if ENABLE_NSE_HISTOGRAM -/** - * Function called with the status of the testbed logger service - * - * @param cls NULL - * @param status #GNUNET_YES if the service is running, - * #GNUNET_NO if the service is not running - * #GNUNET_SYSERR if the configuration is invalid - */ -static void -status_cb (void *cls, int status) -{ - (void) cls; - logger_test = NULL; - if (GNUNET_YES != status) - { - GNUNET_log (GNUNET_ERROR_TYPE_WARNING, "Testbed logger not running\n"); - return; - } - if (NULL == (lh = GNUNET_TESTBED_LOGGER_connect (cfg))) - { - GNUNET_log (GNUNET_ERROR_TYPE_WARNING, - "Cannot connect to the testbed logger. Exiting.\n"); - GNUNET_SCHEDULER_shutdown (); - } -} - - -#endif - - -/** - * Handle network size estimate clients. - * - * @param cls closure - * @param c configuration to use - * @param service the initialized service - */ -static void -run (void *cls, - const struct GNUNET_CONFIGURATION_Handle *c, - struct GNUNET_SERVICE_Handle *service) -{ - struct GNUNET_MQ_MessageHandler core_handlers[] = - { GNUNET_MQ_hd_fixed_size (p2p_estimate, - GNUNET_MESSAGE_TYPE_NSE_P2P_FLOOD, - struct GNUNET_NSE_FloodMessage, - NULL), - GNUNET_MQ_handler_end () }; - char *proof; - struct GNUNET_CRYPTO_EddsaPrivateKey *pk; - - (void) cls; - (void) service; - cfg = c; - if (GNUNET_OK != GNUNET_CONFIGURATION_get_value_time (cfg, - "NSE", - "INTERVAL", - &gnunet_nse_interval)) - { - GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR, "NSE", "INTERVAL"); - GNUNET_SCHEDULER_shutdown (); - return; - } - if (GNUNET_OK != GNUNET_CONFIGURATION_get_value_time (cfg, - "NSE", - "WORKDELAY", - &proof_find_delay)) - { - GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR, "NSE", "WORKDELAY"); - GNUNET_SCHEDULER_shutdown (); - return; - } - if (GNUNET_OK != GNUNET_CONFIGURATION_get_value_number (cfg, - "NSE", - "WORKBITS", - &nse_work_required)) - { - GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR, "NSE", "WORKBITS"); - GNUNET_SCHEDULER_shutdown (); - return; - } - if (nse_work_required >= sizeof(struct GNUNET_HashCode) * 8) - { - GNUNET_log_config_invalid (GNUNET_ERROR_TYPE_ERROR, - "NSE", - "WORKBITS", - _ ("Value is too large.\n")); - GNUNET_SCHEDULER_shutdown (); - return; - } - -#if ENABLE_NSE_HISTOGRAM - { - char *histogram_dir; - char *histogram_fn; - - if (GNUNET_OK == GNUNET_CONFIGURATION_get_value_filename (cfg, - "NSE", - "HISTOGRAM_DIR", - &histogram_dir)) - { - GNUNET_assert ( - 0 < GNUNET_asprintf (&histogram_fn, "%s/timestamps", histogram_dir)); - GNUNET_free (histogram_dir); - histogram = GNUNET_BIO_write_open_file (histogram_fn); - if (NULL == histogram) - GNUNET_log (GNUNET_ERROR_TYPE_WARNING, - "Unable to open histogram file `%s'\n", - histogram_fn); - GNUNET_free (histogram_fn); - } - logger_test = GNUNET_CLIENT_service_test ("testbed-logger", - cfg, - GNUNET_TIME_UNIT_SECONDS, - &status_cb, - NULL); - } -#endif - - GNUNET_SCHEDULER_add_shutdown (&shutdown_task, NULL); - pk = GNUNET_CRYPTO_eddsa_key_create_from_configuration (cfg); - GNUNET_assert (NULL != pk); - my_private_key = pk; - GNUNET_CRYPTO_eddsa_key_get_public (my_private_key, &my_identity.public_key); - if (GNUNET_OK != - GNUNET_CONFIGURATION_get_value_filename (cfg, "NSE", "PROOFFILE", &proof)) - { - GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR, "NSE", "PROOFFILE"); - GNUNET_free (my_private_key); - my_private_key = NULL; - GNUNET_SCHEDULER_shutdown (); - return; - } - if ((GNUNET_YES != GNUNET_DISK_file_test (proof)) || - (sizeof(my_proof) != - GNUNET_DISK_fn_read (proof, &my_proof, sizeof(my_proof)))) - my_proof = 0; - GNUNET_free (proof); - proof_task = - GNUNET_SCHEDULER_add_with_priority (GNUNET_SCHEDULER_PRIORITY_IDLE, - &find_proof, - NULL); - - peers = GNUNET_CONTAINER_multipeermap_create (128, GNUNET_YES); - nc = GNUNET_notification_context_create (1); - /* Connect to core service and register core handlers */ - core_api = - GNUNET_CORE_connect (cfg, /* Main configuration */ - NULL, /* Closure passed to functions */ - &core_init, /* Call core_init once connected */ - &handle_core_connect, /* Handle connects */ - &handle_core_disconnect, /* Handle disconnects */ - core_handlers); /* Register these handlers */ - if (NULL == core_api) - { - GNUNET_SCHEDULER_shutdown (); - return; - } - stats = GNUNET_STATISTICS_create ("nse", 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) -{ - (void) cls; - (void) mq; - return c; -} - - -/** - * 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) -{ - (void) cls; - GNUNET_assert (c == internal_cls); -} - - -/** - * Define "main" method using service macro. - */ -GNUNET_SERVICE_MAIN ("nse", - GNUNET_SERVICE_OPTION_NONE, - &run, - &client_connect_cb, - &client_disconnect_cb, - NULL, - GNUNET_MQ_hd_fixed_size (start, - GNUNET_MESSAGE_TYPE_NSE_START, - struct GNUNET_MessageHeader, - NULL), - GNUNET_MQ_handler_end ()); - - -#if defined(__linux__) && defined(__GLIBC__) -#include - -/** - * 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-service-nse.c */ diff --git a/src/nse/hostkeys.dat b/src/nse/hostkeys.dat deleted file mode 100644 index ab407c167..000000000 Binary files a/src/nse/hostkeys.dat and /dev/null differ diff --git a/src/nse/meson.build b/src/nse/meson.build deleted file mode 100644 index 7490214a3..000000000 --- a/src/nse/meson.build +++ /dev/null @@ -1,47 +0,0 @@ -libgnunetnse_src = ['nse_api.c'] - -gnunetservicense_src = ['gnunet-service-nse.c'] - -configure_file(input : 'nse.conf.in', - output : 'nse.conf', - configuration : cdata, - install: true, - install_dir: pkgcfgdir) - - -if get_option('monolith') - foreach p : libgnunetnse_src + gnunetservicense_src - gnunet_src += 'nse/' + p - endforeach - subdir_done() -endif - -libgnunetnse = library('gnunetnse', - libgnunetnse_src, - soversion: '0', - version: '0.0.0', - dependencies: libgnunetutil_dep, - include_directories: [incdir, configuration_inc], - install: true, - install_dir: get_option('libdir')) -libgnunetnse_dep = declare_dependency(link_with : libgnunetnse) -pkg.generate(libgnunetnse, url: 'https://www.gnunet.org', - description : 'Provides API for accessing the NSE service') - -executable ('gnunet-nse', - ['gnunet-nse.c'], - dependencies: [libgnunetnse_dep, m_dep, libgnunetutil_dep], - include_directories: [incdir, configuration_inc], - install: true, - install_dir: get_option('bindir')) - -executable ('gnunet-service-nse', - gnunetservicense_src, - dependencies: [libgnunetnse_dep, libgnunetutil_dep, - libgnunetcore_dep, - m_dep, - libgnunetstatistics_dep], - include_directories: [incdir, configuration_inc], - install: true, - install_dir: get_option('libdir') / 'gnunet' / 'libexec') - diff --git a/src/nse/nse.conf.in b/src/nse/nse.conf.in deleted file mode 100644 index 0ac5621aa..000000000 --- a/src/nse/nse.conf.in +++ /dev/null @@ -1,38 +0,0 @@ -[nse] -START_ON_DEMAND = @START_ON_DEMAND@ -IMMEDIATE_START = YES -@JAVAPORT@PORT = 2097 -HOSTNAME = localhost -BINARY = gnunet-service-nse -ACCEPT_FROM = 127.0.0.1; -ACCEPT_FROM6 = ::1; -UNIXPATH = $GNUNET_RUNTIME_DIR/gnunet-service-nse.sock -UNIX_MATCH_UID = NO -UNIX_MATCH_GID = YES -PROOFFILE = $GNUNET_DATA_HOME/nse/proof.dat - -# The directory where the NSE services logs timestamps every time -# a size estime flooding message is received -# This option is only used for benchmarking, not in production. -HISTOGRAM_DIR = $GNUNET_CACHE_HOME/nse/histogram - -# How 'slowly' should the proof-of-work be constructed (delay -# between rounds); sane values between 0 and ~1000. -# It should rarely make sense to change this value. -# Only systems with slow CPUs where 5ms is a long time might -# want it to be reduced. -WORKDELAY = 5 ms - -# Note: changing any of the values below will make this peer -# completely incompatible with other peers! - -# How often do peers exchange network size messages? -# Note that all peers MUST use the same interval. -# DO NOT CHANGE THIS VALUE, doing so will break the protocol! -INTERVAL = 1 h - -# 2^22 hash operations take about 2-3h on a first-generation i7 (single-core) -# for SCRYPT; with 2ms/op and 5ms workdelay, we can expect -# the POW calculation to be done by a high-end peer in about 6h -# DO NOT CHANGE THIS VALUE, doing so will break the protocol! -WORKBITS = 15 diff --git a/src/nse/nse.h b/src/nse/nse.h deleted file mode 100644 index 5d1fccb7e..000000000 --- a/src/nse/nse.h +++ /dev/null @@ -1,73 +0,0 @@ -/* - This file is part of GNUnet. - Copyright (C) 2001-2011 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 . - - SPDX-License-Identifier: AGPL3.0-or-later - */ - -/** - * @author Nathan Evans - * @file nse/nse.h - * - * @brief Common type definitions for the network size estimation - * service and API. - */ -#ifndef NSE_H -#define NSE_H - -#include "gnunet_common.h" - -GNUNET_NETWORK_STRUCT_BEGIN - -/** - * Network size estimate sent from the service - * to clients. Contains the current size estimate - * (or 0 if none has been calculated) and the - * standard deviation of known estimates. - * - */ -struct GNUNET_NSE_ClientMessage -{ - /** - * Type: GNUNET_MESSAGE_TYPE_NSE_ESTIMATE - */ - struct GNUNET_MessageHeader header; - - /** - * For alignment. - */ - uint32_t reserved GNUNET_PACKED; - - /** - * Timestamp at which the server received the message. - */ - struct GNUNET_TIME_AbsoluteNBO timestamp; - - /** - * The current estimated network size. - */ - double size_estimate GNUNET_PACKED; - - /** - * The standard deviation (rounded down - * to the nearest integer) of size - * estimations. - */ - double std_deviation GNUNET_PACKED; -}; -GNUNET_NETWORK_STRUCT_END - -#endif diff --git a/src/nse/nse_api.c b/src/nse/nse_api.c deleted file mode 100644 index 7f3e03b98..000000000 --- a/src/nse/nse_api.c +++ /dev/null @@ -1,208 +0,0 @@ -/* - This file is part of GNUnet. - Copyright (C) 2009, 2010, 2011, 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 . - - SPDX-License-Identifier: AGPL3.0-or-later - */ - -/** - * @file nse/nse_api.c - * @brief api to get information from the network size estimation service - * @author Nathan Evans - */ -#include "platform.h" -#include "gnunet_constants.h" -#include "gnunet_arm_service.h" -#include "gnunet_protocols.h" -#include "gnunet_util_lib.h" -#include "gnunet_nse_service.h" -#include "nse.h" - -#define LOG(kind, ...) GNUNET_log_from (kind, "nse-api", __VA_ARGS__) - -/** - * Handle for talking with the NSE service. - */ -struct GNUNET_NSE_Handle -{ - /** - * Configuration to use. - */ - const struct GNUNET_CONFIGURATION_Handle *cfg; - - /** - * Message queue (if available). - */ - struct GNUNET_MQ_Handle *mq; - - /** - * Task doing exponential back-off trying to reconnect. - */ - struct GNUNET_SCHEDULER_Task *reconnect_task; - - /** - * Time for next connect retry. - */ - struct GNUNET_TIME_Relative reconnect_delay; - - /** - * Callback function to call when message is received. - */ - GNUNET_NSE_Callback recv_cb; - - /** - * Closure to pass to @e recv_cb callback. - */ - void *recv_cb_cls; -}; - - -/** - * Try again to connect to network size estimation service. - * - * @param cls closure with the `struct GNUNET_NSE_Handle *` - */ -static void -reconnect (void *cls); - - -/** - * Generic error handler, called with the appropriate - * error code and the same closure specified at the creation of - * the message queue. - * Not every message queue implementation supports an error handler. - * - * @param cls closure with the `struct GNUNET_NSE_Handle *` - * @param error error code - */ -static void -mq_error_handler (void *cls, enum GNUNET_MQ_Error error) -{ - struct GNUNET_NSE_Handle *h = cls; - - (void) error; - GNUNET_MQ_destroy (h->mq); - h->mq = NULL; - h->reconnect_task = - GNUNET_SCHEDULER_add_delayed (h->reconnect_delay, &reconnect, h); - h->reconnect_delay = GNUNET_TIME_STD_BACKOFF (h->reconnect_delay); -} - - -/** - * Type of a function to call when we receive a message - * from the service. - * - * @param cls closure - * @param client_msg message received - */ -static void -handle_estimate (void *cls, const struct GNUNET_NSE_ClientMessage *client_msg) -{ - struct GNUNET_NSE_Handle *h = cls; - - h->reconnect_delay = GNUNET_TIME_UNIT_ZERO; - h->recv_cb (h->recv_cb_cls, - GNUNET_TIME_absolute_ntoh (client_msg->timestamp), - GNUNET_ntoh_double (client_msg->size_estimate), - GNUNET_ntoh_double (client_msg->std_deviation)); -} - - -/** - * Try again to connect to network size estimation service. - * - * @param cls the `struct GNUNET_NSE_Handle *` - */ -static void -reconnect (void *cls) -{ - struct GNUNET_NSE_Handle *h = cls; - struct GNUNET_MQ_MessageHandler handlers[] = - { GNUNET_MQ_hd_fixed_size (estimate, - GNUNET_MESSAGE_TYPE_NSE_ESTIMATE, - struct GNUNET_NSE_ClientMessage, - h), - GNUNET_MQ_handler_end () }; - struct GNUNET_MessageHeader *msg; - struct GNUNET_MQ_Envelope *env; - - h->reconnect_task = NULL; - LOG (GNUNET_ERROR_TYPE_DEBUG, - "Connecting to network size estimation service.\n"); - GNUNET_assert (NULL == h->mq); - h->mq = GNUNET_CLIENT_connect (h->cfg, "nse", handlers, &mq_error_handler, h); - if (NULL == h->mq) - return; - env = GNUNET_MQ_msg (msg, GNUNET_MESSAGE_TYPE_NSE_START); - GNUNET_MQ_send (h->mq, env); -} - - -/** - * Connect to the network size estimation service. - * - * @param cfg the configuration to use - * @param func function to call with network size estimate - * @param func_cls closure to pass to @a func - * @return handle to use - */ -struct GNUNET_NSE_Handle * -GNUNET_NSE_connect (const struct GNUNET_CONFIGURATION_Handle *cfg, - GNUNET_NSE_Callback func, - void *func_cls) -{ - struct GNUNET_NSE_Handle *h; - - GNUNET_assert (NULL != func); - h = GNUNET_new (struct GNUNET_NSE_Handle); - h->cfg = cfg; - h->recv_cb = func; - h->recv_cb_cls = func_cls; - h->reconnect_delay = GNUNET_TIME_UNIT_ZERO; - reconnect (h); - if (NULL == h->mq) - { - GNUNET_free (h); - return NULL; - } - return h; -} - - -/** - * Disconnect from network size estimation service - * - * @param h handle to destroy - */ -void -GNUNET_NSE_disconnect (struct GNUNET_NSE_Handle *h) -{ - if (NULL != h->reconnect_task) - { - GNUNET_SCHEDULER_cancel (h->reconnect_task); - h->reconnect_task = NULL; - } - if (NULL != h->mq) - { - GNUNET_MQ_destroy (h->mq); - h->mq = NULL; - } - GNUNET_free (h); -} - - -/* end of nse_api.c */ diff --git a/src/nse/nse_infiniband.conf b/src/nse/nse_infiniband.conf deleted file mode 100644 index d2c97567d..000000000 --- a/src/nse/nse_infiniband.conf +++ /dev/null @@ -1,77 +0,0 @@ -[PATHS] -GNUNET_TEST_HOME = $GNUNET_TMP/nse-profiler/ - -[testbed] -START_ON_DEMAND = NO -ORT = 12113 -ACCEPT_FROM = 127.0.0.1; 10.6.0.0/16; 192.168.0.0/16; -HOSTNAME = localhost -MAX_PARALLEL_OPERATIONS = 400 -OVERLAY_TOPOLOGY = 2D_TORUS -OVERLAY_RANDOM_LINKS = 1000 -OPERATION_TIMEOUT = 45 s -SETUP_TIMEOUT = 30m -STATS_DIR= /home/totakura/nse/test/load - -[nse] -IMMEDIATE_START = YES -# Overriding network settings for faster testing (do NOT use -# these values in production just because they are here) -WORKDELAY = 60 s -INTERVAL = 10 s -WORKBITS = 0 -PROOFFILE = $GNUNET_TEST_HOME/nse.proof -HISTOGRAM_DIR = /home/totakura/nse/test/histograms - -[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 - -[transport] -plugins = udp - -[transport-udp] -PORT = 12116 - -[nse-profiler] -OUTPUT_FILE = nse_output_2000_peers.dat -TOPOLOGY_OUTPUT_FILE = nse_topo_2000_peers -DATA_OUTPUT_FILE = nse_stats_2000_peers -ROUND0 = 1000 -#ROUND1 = 2000 -ROUND2 = 2000 -ROUND3 = 2000 -ROUND4 = 2000 -ROUND5 = 2000 -ROUND6 = 2000 -ROUND7 = 2000 -ROUND8 = 2000 -ROUND9 = 2000 -ROUND10 = 2000 -ROUND11 = 1000 -ROUND12 = 1000 -ROUND13 = 1000 -ROUND14 = 1000 -ROUND15 = 1000 -ROUND16 = 1000 -ROUND17 = 1000 -ROUND18 = 1000 -ROUND19 = 1000 -ROUND20 = 1000 -ROUND21 = 2000 -ROUND22 = 2000 -ROUND23 = 2000 -ROUND24 = 2000 -ROUND25 = 2000 -ROUND26 = 2000 -ROUND27 = 2000 -ROUND28 = 2000 -ROUND29 = 2000 -ROUND30 = 2000 -WAIT_TIME = 1920 s -CONNECTION_LIMIT = 10 diff --git a/src/nse/nse_profiler_test.conf b/src/nse/nse_profiler_test.conf deleted file mode 100644 index f702faae2..000000000 --- a/src/nse/nse_profiler_test.conf +++ /dev/null @@ -1,72 +0,0 @@ -[PATHS] -GNUNET_TEST_HOME = $GNUNET_TMP/nse-profiler/ - -[testbed] -START_ON_DEMAND = NO -PORT = 12113 -ACCEPT_FROM = 127.0.0.1; 10.6.0.0/16; -HOSTNAME = localhost -MAX_PARALLEL_TOPOLOGY_CONFIG_OPERATIONS = 5 -OVERLAY_TOPOLOGY = RANDOM -OVERLAY_RANDOM_LINKS = 1000 -OPERATION_TIMEOUT = 45 s - -[nse] -IMMEDIATE_START = YES -START_ON_DEMAND = NO -# Overriding network settings for faster testing (do NOT use -# these values in production just because they are here) -WORKDELAY = 60 s -INTERVAL = 10 s -WORKBITS = 0 -PROOFFILE = $GNUNET_TEST_HOME/nse.proof - -[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 - -[transport] -plugins = udp - -[nse-profiler] -OUTPUT_FILE = nse_output_2000_peers.dat -TOPOLOGY_OUTPUT_FILE = nse_topo_2000_peers -DATA_OUTPUT_FILE = nse_stats_2000_peers -ROUND0 = 1000 -#ROUND1 = 2000 -ROUND2 = 2000 -ROUND3 = 2000 -ROUND4 = 2000 -ROUND5 = 2000 -ROUND6 = 2000 -ROUND7 = 2000 -ROUND8 = 2000 -ROUND9 = 2000 -ROUND10 = 2000 -ROUND11 = 1000 -ROUND12 = 1000 -ROUND13 = 1000 -ROUND14 = 1000 -ROUND15 = 1000 -ROUND16 = 1000 -ROUND17 = 1000 -ROUND18 = 1000 -ROUND19 = 1000 -ROUND20 = 1000 -ROUND21 = 2000 -ROUND22 = 2000 -ROUND23 = 2000 -ROUND24 = 2000 -ROUND25 = 2000 -ROUND26 = 2000 -ROUND27 = 2000 -ROUND28 = 2000 -ROUND29 = 2000 -ROUND30 = 2000 -WAIT_TIME = 1920 s -CONNECTION_LIMIT = 10 diff --git a/src/nse/perf_kdf.c b/src/nse/perf_kdf.c deleted file mode 100644 index 10207675f..000000000 --- a/src/nse/perf_kdf.c +++ /dev/null @@ -1,67 +0,0 @@ -/* - This file is part of GNUnet. - Copyright (C) 2002, 2003, 2004, 2006, 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 . - - SPDX-License-Identifier: AGPL3.0-or-later - */ - -/** - * @author Christian Grothoff - * @file nse/perf_kdf.c - * @brief measure performance of KDF hash function - */ -#include "platform.h" -#include "gnunet_util_lib.h" -#include -#include - - -static void -perfHash () -{ - struct GNUNET_HashCode hc; - char buf[64]; - struct GNUNET_CRYPTO_PowSalt salt = { "gnunet-nse-proof" }; - - memset (buf, 1, sizeof(buf)); - for (unsigned int i = 0; i < 1024; i++) - GNUNET_CRYPTO_pow_hash (&salt, - buf, - sizeof(buf), - &hc); -} - - -int -main (int argc, char *argv[]) -{ - struct GNUNET_TIME_Absolute start; - - start = GNUNET_TIME_absolute_get (); - perfHash (); - printf ("Hash perf took %s\n", - GNUNET_STRINGS_relative_time_to_string ( - GNUNET_TIME_absolute_get_duration (start), - GNUNET_YES)); - GAUGER ("NSE", "Proof-of-work hashing", - 1024.0 / (1.0 - + GNUNET_TIME_absolute_get_duration - (start).rel_value_us / 1000.0), "hashes/ms"); - return 0; -} - - -/* end of perf_kdf.c */ diff --git a/src/nse/test_nse.conf b/src/nse/test_nse.conf deleted file mode 100644 index 6b5aaff49..000000000 --- a/src/nse/test_nse.conf +++ /dev/null @@ -1,26 +0,0 @@ -@INLINE@ ../../contrib/conf/gnunet/no_forcestart.conf -@INLINE@ ../../contrib/conf/gnunet/no_autostart_above_core.conf - -[PATHS] -GNUNET_TEST_HOME = $GNUNET_TMP/test-nse-multipeer/ - -[nse] -#PREFIX = valgrind --leak-check=full --log-file=valgrind_nse.%p -IMMEDIATE_START = YES -PROOFFILE = $GNUNET_TEST_HOME/proof.nse -# Overriding network settings for faster testing (do NOT use -# these values in production just because they are here) -WORKDELAY = 1 ms -INTERVAL = 60 s -WORKBITS = 1 -HISTOGRAM = $GNUNET_TEST_HOME/nse-histogram - -[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/nse/test_nse_api.c b/src/nse/test_nse_api.c deleted file mode 100644 index f1b7c652b..000000000 --- a/src/nse/test_nse_api.c +++ /dev/null @@ -1,107 +0,0 @@ -/* - This file is part of GNUnet. - Copyright (C) 2011 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 . - - SPDX-License-Identifier: AGPL3.0-or-later - */ -/** - * @file nse/test_nse_api.c - * @brief testcase for nse_api.c - */ -#include "platform.h" -#include "gnunet_util_lib.h" -#include "gnunet_nse_service.h" -#include "gnunet_testing_lib.h" - - -static struct GNUNET_NSE_Handle *h; - -static struct GNUNET_SCHEDULER_Task *die_task; - - -/** - * Signature of the main function of a task. - * - * @param cls closure - */ -static void -end_test (void *cls) -{ - if (h != NULL) - { - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Disconnecting from NSE service.\n"); - GNUNET_NSE_disconnect (h); - } -} - - -/** - * Callback to call when network size estimate is updated. - * - * @param cls unused - * @param timestamp time when the estimate was received from the server (or created by the server) - * @param estimate the value of the current network size estimate - * @param std_dev standard deviation (rounded down to nearest integer) - * of the size estimation values seen - * - */ -static void -check_nse_message (void *cls, struct GNUNET_TIME_Absolute timestamp, - double estimate, double std_dev) -{ - int *ok = cls; - - fprintf (stderr, - "Received NSE message, estimate %f, standard deviation %f.\n", - estimate, std_dev); - /* Fantastic check below. Expect NaN, the only thing not equal to itself. */ - (*ok) = 0; - if (die_task != NULL) - GNUNET_SCHEDULER_cancel (die_task); - die_task = GNUNET_SCHEDULER_add_now (&end_test, NULL); -} - - -static void -run (void *cls, - const struct GNUNET_CONFIGURATION_Handle *cfg, - struct GNUNET_TESTING_Peer *peer) -{ - die_task = - GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_relative_multiply - (GNUNET_TIME_UNIT_MINUTES, 1), &end_test, - NULL); - - h = GNUNET_NSE_connect (cfg, &check_nse_message, cls); - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Connecting to NSE service.\n"); - GNUNET_assert (h != NULL); -} - - -int -main (int argc, char *argv[]) -{ - int ok = 1; - - if (0 != GNUNET_TESTING_peer_run ("test_nse_api", - "test_nse.conf", - &run, &ok)) - return 1; - return ok; -} - - -/* end of test_nse_api.c */ diff --git a/src/nse/test_nse_multipeer.c b/src/nse/test_nse_multipeer.c deleted file mode 100644 index 6ee03b3fa..000000000 --- a/src/nse/test_nse_multipeer.c +++ /dev/null @@ -1,235 +0,0 @@ -/* - This file is part of GNUnet. - Copyright (C) 2009, 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 . - - SPDX-License-Identifier: AGPL3.0-or-later - */ -/** - * @file nse/test_nse_multipeer.c - * @brief Testcase for the network size estimation service. Starts - * a peergroup with a given number of peers, then waits to - * receive size estimates from each peer. Expects to wait - * for one message from each peer. - */ -#include "platform.h" -#include "gnunet_testbed_service.h" -#include "gnunet_nse_service.h" - - -/** - * How many peers do we start? - */ -#define NUM_PEERS 4 - -/** - * How long do we run the test? - */ -#define TIMEOUT GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS, 300) - - -/** - * Information we track for each peer. - */ -struct NSEPeer -{ - /** - * Handle for NSE connect operation. - */ - struct GNUNET_TESTBED_Operation *op; - - /** - * Handle to NSE service. - */ - struct GNUNET_NSE_Handle *nse_handle; -}; - - -/** - * Information for all the peers. - */ -static struct NSEPeer nse_peers[NUM_PEERS]; - -/** - * Return value from 'main'. - */ -static int ok; - - -/** - * Task run on timeout to shut everything down. - */ -static void -shutdown_task (void *cls) -{ - unsigned int i; - - for (i = 0; i < NUM_PEERS; i++) - GNUNET_TESTBED_operation_done (nse_peers[i].op); - GNUNET_SCHEDULER_shutdown (); -} - - -/** - * Callback to call when network size estimate is updated. - * - * @param cls closure - * @param timestamp server timestamp - * @param estimate the value of the current network size estimate - * @param std_dev standard deviation (rounded down to nearest integer) - * of the size estimation values seen - * - */ -static void -handle_estimate (void *cls, struct GNUNET_TIME_Absolute timestamp, - double estimate, double std_dev) -{ - struct NSEPeer *peer = cls; - - fprintf (stderr, - "Received network size estimate from peer %u. logSize: %f std.dev. %f (%f/%u)\n", - (unsigned int) (peer - nse_peers), - estimate, std_dev, - GNUNET_NSE_log_estimate_to_n (estimate), - NUM_PEERS); - ok = 0; -} - - -/** - * Callback to be called when NSE service connect operation is completed - * - * @param cls the callback closure from functions generating an operation - * @param op the operation that has been finished - * @param ca_result the NSE service handle returned from nse_connect_adapter - * @param emsg error message in case the operation has failed; will be NULL if - * operation has executed successfully. - */ -static void -nse_connect_complete_cb (void *cls, - struct GNUNET_TESTBED_Operation *op, - void *ca_result, - const char *emsg) -{ - struct NSEPeer *peer = cls; - struct GNUNET_NSE_Handle *nse = ca_result; - - GNUNET_assert (op == peer->op); - if (NULL != emsg) - { - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - "Failed to connect to NSE service: %s\n", - emsg); - ok = 1; - GNUNET_SCHEDULER_shutdown (); - return; - } - peer->nse_handle = nse; -} - - -/** - * Adapter function called to establish a connection to - * the NSE 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 * -nse_connect_adapter (void *cls, - const struct GNUNET_CONFIGURATION_Handle *cfg) -{ - return GNUNET_NSE_connect (cfg, - &handle_estimate, - cls); -} - - -/** - * Adapter function called to destroy connection to - * NSE service. - * - * @param cls closure - * @param op_result service handle returned from the connect adapter - */ -static void -nse_disconnect_adapter (void *cls, - void *op_result) -{ - GNUNET_NSE_disconnect (op_result); -} - - -/** - * Actual "main" function for the testcase. - * - * @param cls closure - * @param h the run handle - * @param num_peers number of peers in 'peers' - * @param peers handle to peers run in the testbed - * @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 -run (void *cls, - struct GNUNET_TESTBED_RunHandle *h, - unsigned int num_peers, - struct GNUNET_TESTBED_Peer **peers, - unsigned int links_succeeded, - unsigned int links_failed) -{ - unsigned int i; - - GNUNET_assert (NUM_PEERS == num_peers); - for (i = 0; i < num_peers; i++) - nse_peers[i].op = GNUNET_TESTBED_service_connect (&nse_peers[i], - peers[i], - "nse", - &nse_connect_complete_cb, - &nse_peers[i], - &nse_connect_adapter, - &nse_disconnect_adapter, - &nse_peers[i]); - GNUNET_SCHEDULER_add_delayed (TIMEOUT, - &shutdown_task, NULL); -} - - -/** - * Entry point for the testcase, sets up the testbed. - * - * @param argc unused - * @param argv unused - * @return 0 on success - */ -int -main (int argc, char *argv[]) -{ - ok = 1; - (void) GNUNET_TESTBED_test_run ("test-nse-multipeer", - "test_nse.conf", - NUM_PEERS, - 0, NULL, NULL, - &run, NULL); - return ok; -} - - -/* end of test_nse_multipeer.c */ diff --git a/src/peerstore/.gitignore b/src/peerstore/.gitignore deleted file mode 100644 index 7fc22bbdb..000000000 --- a/src/peerstore/.gitignore +++ /dev/null @@ -1,9 +0,0 @@ -gnunet-service-peerstore -gnunet-peerstore -perf_peerstore_store -test_peerstore_api_iterate -test_peerstore_api_store -test_peerstore_api_sync -test_peerstore_api_watch -test_plugin_peerstore_sqlite -test_plugin_peerstore_flat diff --git a/src/peerstore/Makefile.am b/src/peerstore/Makefile.am deleted file mode 100644 index 41a082f89..000000000 --- a/src/peerstore/Makefile.am +++ /dev/null @@ -1,150 +0,0 @@ -# This Makefile.am is in the public domain -AM_CPPFLAGS = -I$(top_srcdir)/src/include - -plugindir = $(libdir)/gnunet - -pkgcfgdir= $(pkgdatadir)/config.d/ - -libexecdir= $(pkglibdir)/libexec/ - -dist_pkgcfg_DATA = \ - peerstore.conf - -if USE_COVERAGE - AM_CFLAGS = -fprofile-arcs -ftest-coverage -endif - -# This program does not do anything. -noinst_PROGRAMS = \ - gnunet-peerstore - -libexec_PROGRAMS = \ - gnunet-service-peerstore - -lib_LTLIBRARIES = \ - libgnunetpeerstore.la - -gnunet_peerstore_SOURCES = \ - gnunet-peerstore.c -gnunet_peerstore_LDADD = \ - $(top_builddir)/src/lib/util/libgnunetutil.la \ - libgnunetpeerstore.la \ - $(GN_LIBINTL) - -gnunet_service_peerstore_SOURCES = \ - gnunet-service-peerstore.c \ - peerstore_common.c peerstore_common.h \ - peerstore.h -gnunet_service_peerstore_CFLAGS = $(AM_CFLAGS) -gnunet_service_peerstore_LDADD = \ - $(top_builddir)/src/lib/util/libgnunetutil.la \ - $(top_builddir)/src/lib/hello/libgnunethello.la \ - $(GN_LIBINTL) - -libgnunetpeerstore_la_SOURCES = \ - peerstore_api.c \ - peerstore_common.c -libgnunetpeerstore_la_LIBADD = \ - $(top_builddir)/src/lib/util/libgnunetutil.la \ - $(top_builddir)/src/lib/hello/libgnunethello.la -libgnunetpeerstore_la_LDFLAGS = \ - $(GN_LIBINTL) \ - $(GN_LIB_LDFLAGS) - -if HAVE_EXPERIMENTAL -FLAT_PLUGIN = libgnunet_plugin_peerstore_flat.la -FLAT_TESTS = test_plugin_peerstore_flat -libgnunet_plugin_peerstore_flat_la_SOURCES = \ - plugin_peerstore_flat.c -libgnunet_plugin_peerstore_flat_la_LIBADD = \ - libgnunetpeerstore.la \ - $(top_builddir)/src/lib/util/libgnunetutil.la $(XLIBS) \ - $(LTLIBINTL) -libgnunet_plugin_peerstore_flat_la_LDFLAGS = \ - $(GN_PLUGIN_LDFLAGS) -endif - -if HAVE_SQLITE -SQLITE_PLUGIN = libgnunet_plugin_peerstore_sqlite.la -SQLITE_TESTS = test_plugin_peerstore_sqlite -libgnunet_plugin_peerstore_sqlite_la_SOURCES = \ - plugin_peerstore_sqlite.c -libgnunet_plugin_peerstore_sqlite_la_LIBADD = \ - libgnunetpeerstore.la \ - $(top_builddir)/src/lib/sq/libgnunetsq.la \ - $(top_builddir)/src/lib/util/libgnunetutil.la \ - $(XLIBS) -lsqlite3 \ - $(LTLIBINTL) -libgnunet_plugin_peerstore_sqlite_la_LDFLAGS = \ - $(GN_PLUGIN_LDFLAGS) -endif - -plugin_LTLIBRARIES = \ - $(SQLITE_PLUGIN) \ - $(FLAT_PLUGIN) - -test_plugin_peerstore_sqlite_SOURCES = \ - test_plugin_peerstore.c -test_plugin_peerstore_sqlite_LDADD = \ - $(top_builddir)/src/lib/testing/libgnunettesting.la \ - $(top_builddir)/src/lib/util/libgnunetutil.la - -test_plugin_peerstore_flat_SOURCES = \ - test_plugin_peerstore.c -test_plugin_peerstore_flat_LDADD = \ - $(top_builddir)/src/lib/testing/libgnunettesting.la \ - $(top_builddir)/src/lib/util/libgnunetutil.la - -check_PROGRAMS = \ - test_peerstore_api_store \ - test_peerstore_api_iterate \ - test_peerstore_api_watch \ - test_peerstore_api_sync \ - perf_peerstore_store \ - $(SQLITE_TESTS) \ - $(FLAT_TESTS) - -EXTRA_DIST = \ - test_peerstore_api_data.conf \ - test_plugin_peerstore_flat.conf \ - test_plugin_peerstore_sqlite.conf - -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_peerstore_api_store_SOURCES = \ - test_peerstore_api_store.c -test_peerstore_api_store_LDADD = \ - libgnunetpeerstore.la \ - $(top_builddir)/src/lib/testing/libgnunettesting.la \ - $(top_builddir)/src/lib/util/libgnunetutil.la - -test_peerstore_api_iterate_SOURCES = \ - test_peerstore_api_iterate.c -test_peerstore_api_iterate_LDADD = \ - libgnunetpeerstore.la \ - $(top_builddir)/src/lib/testing/libgnunettesting.la \ - $(top_builddir)/src/lib/util/libgnunetutil.la - -test_peerstore_api_watch_SOURCES = \ - test_peerstore_api_watch.c -test_peerstore_api_watch_LDADD = \ - libgnunetpeerstore.la \ - $(top_builddir)/src/lib/testing/libgnunettesting.la \ - $(top_builddir)/src/lib/util/libgnunetutil.la - -test_peerstore_api_sync_SOURCES = \ - test_peerstore_api_sync.c -test_peerstore_api_sync_LDADD = \ - libgnunetpeerstore.la \ - $(top_builddir)/src/lib/testing/libgnunettesting.la \ - $(top_builddir)/src/lib/util/libgnunetutil.la - -perf_peerstore_store_SOURCES = \ - perf_peerstore_store.c -perf_peerstore_store_LDADD = \ - libgnunetpeerstore.la \ - $(top_builddir)/src/lib/testing/libgnunettesting.la \ - $(top_builddir)/src/lib/util/libgnunetutil.la diff --git a/src/peerstore/gnunet-peerstore.c b/src/peerstore/gnunet-peerstore.c deleted file mode 100644 index 11313b5d3..000000000 --- a/src/peerstore/gnunet-peerstore.c +++ /dev/null @@ -1,97 +0,0 @@ -/* - This file is part of GNUnet. - Copyright (C) - - 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 . - - SPDX-License-Identifier: AGPL3.0-or-later - */ - -/** - * @file peerstore/gnunet-peerstore.c - * @brief peerstore tool - * @author Omar Tarabai - */ -#include "platform.h" -#include "gnunet_util_lib.h" -#include "gnunet_peerstore_service.h" - -static int ret; - -/* - * Handle to PEERSTORE service - */ -static struct GNUNET_PEERSTORE_Handle *peerstore_handle; - - -/** - * Run on shutdown - * - * @param cls unused - */ -static void -shutdown_task (void *cls) -{ - if (NULL != peerstore_handle) - { - GNUNET_PEERSTORE_disconnect (peerstore_handle, GNUNET_YES); - peerstore_handle = NULL; - } -} - - -/** - * 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, - const char *cfgfile, - const struct GNUNET_CONFIGURATION_Handle *cfg) -{ - GNUNET_SCHEDULER_add_shutdown (&shutdown_task, - NULL); - peerstore_handle = GNUNET_PEERSTORE_connect (cfg); - GNUNET_assert (NULL != peerstore_handle); - ret = 0; -} - - -/** - * The main function to peerstore. - * - * @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 - }; - - return (GNUNET_OK == - GNUNET_PROGRAM_run (argc, argv, "gnunet-peerstore [options [value]]", - gettext_noop ("peerstore"), options, &run, - NULL)) ? ret : 1; -} - - -/* end of gnunet-peerstore.c */ diff --git a/src/peerstore/gnunet-service-peerstore.c b/src/peerstore/gnunet-service-peerstore.c deleted file mode 100644 index 514b173bb..000000000 --- a/src/peerstore/gnunet-service-peerstore.c +++ /dev/null @@ -1,724 +0,0 @@ -/* - This file is part of GNUnet. - Copyright (C) 2014, 2015, 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 . - - SPDX-License-Identifier: AGPL3.0-or-later - */ - -/** - * @file peerstore/gnunet-service-peerstore.c - * @brief peerstore service implementation - * @author Omar Tarabai - */ -#include "platform.h" -#include "gnunet_util_lib.h" -#include "peerstore.h" -#include "gnunet_peerstore_plugin.h" -#include "peerstore_common.h" -#include "gnunet_hello_uri_lib.h" - - -/** - * Interval for expired records cleanup (in seconds) - */ -#define EXPIRED_RECORDS_CLEANUP_INTERVAL 300 /* 5mins */ - -/** - * Our configuration. - */ -static const struct GNUNET_CONFIGURATION_Handle *cfg; - -/** - * Database plugin library name - */ -static char *db_lib_name; - -/** - * Database handle - */ -static struct GNUNET_PEERSTORE_PluginFunctions *db; - -/** - * Hashmap with all watch requests - */ -static struct GNUNET_CONTAINER_MultiHashMap *watchers; - -/** - * Task run to clean up expired records. - */ -static struct GNUNET_SCHEDULER_Task *expire_task; - -/** - * Are we in the process of shutting down the service? #GNUNET_YES / #GNUNET_NO - */ -static int in_shutdown; - -/** - * Number of connected clients. - */ -static unsigned int num_clients; - - -/** - * Perform the actual shutdown operations - */ -static void -do_shutdown () -{ - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "Shutting down peerstore, bye.\n"); - if (NULL != db_lib_name) - { - GNUNET_break (NULL == GNUNET_PLUGIN_unload (db_lib_name, db)); - GNUNET_free (db_lib_name); - db_lib_name = NULL; - } - if (NULL != watchers) - { - GNUNET_CONTAINER_multihashmap_destroy (watchers); - watchers = NULL; - } - if (NULL != expire_task) - { - GNUNET_SCHEDULER_cancel (expire_task); - expire_task = NULL; - } - GNUNET_SCHEDULER_shutdown (); -} - - -/** - * Task run during shutdown. - * - * @param cls unused - */ -static void -shutdown_task (void *cls) -{ - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "Priming PEERSTORE for shutdown.\n"); - in_shutdown = GNUNET_YES; - if (0 == num_clients) /* Only when no connected clients. */ - do_shutdown (); -} - - -/* Forward declaration */ -static void -expire_records_continuation (void *cls, int success); - - -/** - * Deletes any expired records from storage - */ -static void -cleanup_expired_records (void *cls) -{ - int ret; - - expire_task = NULL; - GNUNET_assert (NULL != db); - ret = db->expire_records (db->cls, - GNUNET_TIME_absolute_get (), - &expire_records_continuation, - NULL); - if (GNUNET_OK != ret) - { - GNUNET_assert (NULL == expire_task); - expire_task = GNUNET_SCHEDULER_add_delayed ( - GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS, - EXPIRED_RECORDS_CLEANUP_INTERVAL), - &cleanup_expired_records, - NULL); - } -} - - -/** - * Continuation to expire_records called by the peerstore plugin - * - * @param cls unused - * @param success count of records deleted or #GNUNET_SYSERR - */ -static void -expire_records_continuation (void *cls, int success) -{ - if (success > 0) - GNUNET_log (GNUNET_ERROR_TYPE_INFO, "%d records expired.\n", success); - GNUNET_assert (NULL == expire_task); - expire_task = GNUNET_SCHEDULER_add_delayed ( - GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS, - EXPIRED_RECORDS_CLEANUP_INTERVAL), - &cleanup_expired_records, - NULL); -} - - -/** - * A client disconnected. Remove all of its data structure entries. - * - * @param cls closure, NULL - * @param client identification of the client - * @param mq the message queue - * @return - */ -static void * -client_connect_cb (void *cls, - struct GNUNET_SERVICE_Client *client, - struct GNUNET_MQ_Handle *mq) -{ - num_clients++; - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "A client connected (now %u)\n", num_clients); - return client; -} - - -/** - * Search for a disconnected client and remove it - * - * @param cls closuer, a `struct GNUNET_SERVICE_Client` - * @param key hash of record key - * @param value the watcher client, a `struct GNUNET_SERVICE_Client *` - * @return #GNUNET_OK to continue iterating - */ -static int -client_disconnect_it (void *cls, const struct GNUNET_HashCode *key, void *value) -{ - if (value == cls) - { - GNUNET_assert (GNUNET_YES == - GNUNET_CONTAINER_multihashmap_remove (watchers, key, value)); - num_clients++; /* Watchers do not count */ - } - return GNUNET_OK; -} - - -/** - * A client disconnected. Remove all of its data structure entries. - * - * @param cls closure, NULL - * @param client identification of the client - */ -static void -client_disconnect_cb (void *cls, - struct GNUNET_SERVICE_Client *client, - void *app_cls) -{ - num_clients--; - if (NULL != watchers) - GNUNET_CONTAINER_multihashmap_iterate (watchers, - &client_disconnect_it, - client); - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "A client disconnected (%u remaining).\n", - num_clients); - if ((0 == num_clients) && in_shutdown) - do_shutdown (); -} - - -/** - * Function called by for each matching record. - * - * @param cls closure - * @param record peerstore record found - * @param emsg error message or NULL if no errors - * @return #GNUNET_YES to continue iteration - */ -static void -record_iterator (void *cls, - const struct GNUNET_PEERSTORE_Record *record, - const char *emsg) -{ - struct GNUNET_PEERSTORE_Record *cls_record = cls; - struct GNUNET_MQ_Envelope *env; - - if (NULL == record) - { - /* No more records */ - struct GNUNET_MessageHeader *endmsg; - - env = GNUNET_MQ_msg (endmsg, GNUNET_MESSAGE_TYPE_PEERSTORE_ITERATE_END); - GNUNET_MQ_send (GNUNET_SERVICE_client_get_mq (cls_record->client), env); - if (NULL == emsg) - { - GNUNET_SERVICE_client_continue (cls_record->client); - } - else - { - GNUNET_break (0); - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Failed to iterate: %s\n", emsg); - GNUNET_SERVICE_client_drop (cls_record->client); - } - PEERSTORE_destroy_record (cls_record); - return; - } - - env = PEERSTORE_create_record_mq_envelope ( - record->sub_system, - &record->peer, - record->key, - record->value, - record->value_size, - record->expiry, - 0, - GNUNET_MESSAGE_TYPE_PEERSTORE_ITERATE_RECORD); - GNUNET_MQ_send (GNUNET_SERVICE_client_get_mq (cls_record->client), env); -} - - -/** - * Iterator over all watcher clients - * to notify them of a new record - * - * @param cls closure, a `struct GNUNET_PEERSTORE_Record *` - * @param key hash of record key - * @param value the watcher client, a `struct GNUNET_SERVICE_Client *` - * @return #GNUNET_YES to continue iterating - */ -static int -watch_notifier_it (void *cls, const struct GNUNET_HashCode *key, void *value) -{ - struct GNUNET_PEERSTORE_Record *record = cls; - struct GNUNET_SERVICE_Client *client = value; - struct GNUNET_MQ_Envelope *env; - - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Found a watcher to update.\n"); - env = PEERSTORE_create_record_mq_envelope ( - record->sub_system, - &record->peer, - record->key, - record->value, - record->value_size, - record->expiry, - 0, - GNUNET_MESSAGE_TYPE_PEERSTORE_WATCH_RECORD); - GNUNET_MQ_send (GNUNET_SERVICE_client_get_mq (client), env); - return GNUNET_YES; -} - - -/** - * Given a new record, notifies watchers - * - * @param record changed record to update watchers with - */ -static void -watch_notifier (struct GNUNET_PEERSTORE_Record *record) -{ - struct GNUNET_HashCode keyhash; - - PEERSTORE_hash_key (record->sub_system, &record->peer, record->key, &keyhash); - GNUNET_CONTAINER_multihashmap_get_multiple (watchers, - &keyhash, - &watch_notifier_it, - record); -} - - -/** - * Handle a watch cancel request from client - * - * @param cls identification of the client - * @param hm the actual message - */ -static void -handle_watch_cancel (void *cls, const struct StoreKeyHashMessage *hm) -{ - struct GNUNET_SERVICE_Client *client = cls; - - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Received a watch cancel request.\n"); - if (GNUNET_OK != - GNUNET_CONTAINER_multihashmap_remove (watchers, &hm->keyhash, client)) - { - GNUNET_break (0); - GNUNET_SERVICE_client_drop (client); - return; - } - num_clients++; - GNUNET_SERVICE_client_continue (client); -} - - -/** - * Handle a watch request from client - * - * @param cls identification of the client - * @param hm the actual message - */ -static void -handle_watch (void *cls, const struct StoreKeyHashMessage *hm) -{ - struct GNUNET_SERVICE_Client *client = cls; - - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Received a watch request.\n"); - num_clients--; /* do not count watchers */ - GNUNET_SERVICE_client_mark_monitor (client); - GNUNET_CONTAINER_multihashmap_put (watchers, - &hm->keyhash, - client, - GNUNET_CONTAINER_MULTIHASHMAPOPTION_MULTIPLE); - GNUNET_SERVICE_client_continue (client); -} - - -/** - * Check an iterate request from client - * - * @param cls client identification of the client - * @param srm the actual message - * @return #GNUNET_OK if @a srm is well-formed - */ -static int -check_iterate (void *cls, const struct StoreRecordMessage *srm) -{ - struct GNUNET_PEERSTORE_Record *record; - - record = PEERSTORE_parse_record_message (srm); - if (NULL == record) - { - GNUNET_break (0); - return GNUNET_SYSERR; - } - if (NULL == record->sub_system) - { - GNUNET_break (0); - PEERSTORE_destroy_record (record); - return GNUNET_SYSERR; - } - PEERSTORE_destroy_record (record); - return GNUNET_OK; -} - - -/** - * Handle an iterate request from client - * - * @param cls identification of the client - * @param srm the actual message - */ -static void -handle_iterate (void *cls, const struct StoreRecordMessage *srm) -{ - struct GNUNET_SERVICE_Client *client = cls; - struct GNUNET_PEERSTORE_Record *record; - - record = PEERSTORE_parse_record_message (srm); - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "Iterate request: ss `%s', peer `%s', key `%s'\n", - record->sub_system, - GNUNET_i2s (&record->peer), - (NULL == record->key) ? "NULL" : record->key); - record->client = client; - if (GNUNET_OK != - db->iterate_records (db->cls, - record->sub_system, - (ntohs (srm->peer_set)) ? &record->peer : NULL, - record->key, - &record_iterator, - record)) - { - GNUNET_break (0); - GNUNET_SERVICE_client_drop (client); - PEERSTORE_destroy_record (record); - } -} - - -/** - * Continuation of store_record called by the peerstore plugin - * - * @param cls closure - * @param success result - */ -static void -store_record_continuation (void *cls, int success) -{ - struct GNUNET_PEERSTORE_Record *record = cls; - - if (GNUNET_OK == success) - { - watch_notifier (record); - GNUNET_SERVICE_client_continue (record->client); - } - else - { - GNUNET_break (0); - GNUNET_SERVICE_client_drop (record->client); - } - PEERSTORE_destroy_record (record); -} - - -/** - * Check a store request from client - * - * @param cls client identification of the client - * @param srm the actual message - * @return #GNUNET_OK if @a srm is well-formed - */ -static int -check_store (void *cls, const struct StoreRecordMessage *srm) -{ - struct GNUNET_PEERSTORE_Record *record; - - record = PEERSTORE_parse_record_message (srm); - if (NULL == record) - { - GNUNET_break (0); - return GNUNET_SYSERR; - } - if ((NULL == record->sub_system) || (NULL == record->key)) - { - GNUNET_break (0); - PEERSTORE_destroy_record (record); - return GNUNET_SYSERR; - } - PEERSTORE_destroy_record (record); - return GNUNET_OK; -} - - -/** - * Handle a store request from client - * - * @param cls client identification of the client - * @param srm the actual message - */ -static void -handle_store (void *cls, const struct StoreRecordMessage *srm) -{ - struct GNUNET_SERVICE_Client *client = cls; - struct GNUNET_PEERSTORE_Record *record; - - record = PEERSTORE_parse_record_message (srm); - GNUNET_log ( - GNUNET_ERROR_TYPE_INFO, - "Received a store request. Sub system `%s' Peer `%s Key `%s' Options: %u.\n", - record->sub_system, - GNUNET_i2s (&record->peer), - record->key, - (uint32_t) ntohl (srm->options)); - record->client = client; - if (GNUNET_OK != db->store_record (db->cls, - record->sub_system, - &record->peer, - record->key, - record->value, - record->value_size, - record->expiry, - ntohl (srm->options), - &store_record_continuation, - record)) - { - GNUNET_break (0); - PEERSTORE_destroy_record (record); - GNUNET_SERVICE_client_drop (client); - return; - } -} - -static void -store_hello_continuation (void *cls, int success) -{ - (void) cls; - - if (GNUNET_OK != success) - { - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - "Error storing bootstrap hello!\n"); - GNUNET_break (0); - } -} - - -static int -hosts_directory_scan_callback (void *cls, const char *fullname) -{ - (void) cls; - ssize_t size_total; - char buffer[GNUNET_MAX_MESSAGE_SIZE - 1] GNUNET_ALIGN; - const struct GNUNET_MessageHeader *hello; - struct GNUNET_HELLO_Builder *builder; - struct GNUNET_PeerIdentity *pid; - - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "1 hosts_directory_scan_callback filename %s\n", - fullname); - - if (GNUNET_YES != GNUNET_DISK_file_test (fullname)) - return GNUNET_OK; /* ignore non-files */ - - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "2 hosts_directory_scan_callback filename %s\n", - fullname); - - /* filename = strrchr (fullname, DIR_SEPARATOR); */ - /* if ((NULL == filename) || (1 > strlen (filename))) */ - /* filename = fullname; */ - /* else */ - /* filename++; */ - - /* GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, */ - /* "3 hosts_directory_scan_callback filename %s\n", */ - /* filename); */ - - /* if (GNUNET_YES != GNUNET_DISK_file_test (filename)) */ - /* return GNUNET_OK; */ - size_total = GNUNET_DISK_fn_read (fullname, buffer, sizeof(buffer)); - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "Read %d bytes from `%s'\n", - (int) size_total, - fullname); - if ((size_total < 0) || - (((size_t) size_total) < sizeof(struct GNUNET_MessageHeader))) - { - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - _ ("Failed to parse HELLO in file `%s': %s\n"), - fullname, - "File has invalid size"); - return GNUNET_OK; - } - hello = (const struct GNUNET_MessageHeader *) &buffer[0]; - builder = GNUNET_HELLO_builder_from_msg (hello); - pid = GNUNET_HELLO_builder_get_id (builder); - - if (GNUNET_OK != db->store_record (db->cls, - "peerstore", - pid, - "", - hello, - size_total, - GNUNET_TIME_UNIT_FOREVER_ABS, - GNUNET_PEERSTORE_STOREOPTION_REPLACE, - &store_hello_continuation, - NULL)) - { - GNUNET_break (0); - } - GNUNET_HELLO_builder_free (builder); - return GNUNET_OK; -} - - -/** - * Peerstore service runner. - * - * @param cls closure - * @param c configuration to use - * @param service the initialized service - */ -static void -run (void *cls, - const struct GNUNET_CONFIGURATION_Handle *c, - struct GNUNET_SERVICE_Handle *service) -{ - char *database; - int use_included; - char *ip; - char *peerdir; - - in_shutdown = GNUNET_NO; - num_clients = 0; - cfg = c; - - if (GNUNET_OK != GNUNET_CONFIGURATION_get_value_string (cfg, - "peerstore", - "DATABASE", - &database)) - { - GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR, - "peerstore", - "DATABASE"); - GNUNET_SCHEDULER_shutdown (); - return; - } - GNUNET_asprintf (&db_lib_name, "libgnunet_plugin_peerstore_%s", database); - db = GNUNET_PLUGIN_load (db_lib_name, (void *) cfg); - GNUNET_free (database); - if (NULL == db) - { - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - _ ("Could not load database backend `%s'\n"), - db_lib_name); - GNUNET_SCHEDULER_shutdown (); - return; - } - watchers = GNUNET_CONTAINER_multihashmap_create (10, GNUNET_NO); - expire_task = GNUNET_SCHEDULER_add_now (&cleanup_expired_records, NULL); - - use_included = GNUNET_CONFIGURATION_get_value_yesno (cfg, - "peerstore", - "USE_INCLUDED_HELLOS"); - if (GNUNET_SYSERR == use_included) - use_included = GNUNET_NO; - if (GNUNET_YES == use_included) - { - ip = GNUNET_OS_installation_get_path (GNUNET_OS_IPK_DATADIR); - GNUNET_asprintf (&peerdir, "%shellos", ip); - GNUNET_free (ip); - - GNUNET_log (GNUNET_ERROR_TYPE_INFO, - _ ("Importing HELLOs from `%s'\n"), - peerdir); - - GNUNET_DISK_directory_scan (peerdir, - &hosts_directory_scan_callback, - NULL); - GNUNET_free (peerdir); - } - else - { - GNUNET_log (GNUNET_ERROR_TYPE_INFO, - _ ("Skipping import of included HELLOs\n")); - } - - GNUNET_SCHEDULER_add_shutdown (&shutdown_task, NULL); -} - - -/** - * Define "main" method using service macro. - */ -GNUNET_SERVICE_MAIN ( - "peerstore", - GNUNET_SERVICE_OPTION_SOFT_SHUTDOWN, - &run, - &client_connect_cb, - &client_disconnect_cb, - NULL, - GNUNET_MQ_hd_var_size (store, - GNUNET_MESSAGE_TYPE_PEERSTORE_STORE, - struct StoreRecordMessage, - NULL), - GNUNET_MQ_hd_var_size (iterate, - GNUNET_MESSAGE_TYPE_PEERSTORE_ITERATE, - struct StoreRecordMessage, - NULL), - GNUNET_MQ_hd_fixed_size (watch, - GNUNET_MESSAGE_TYPE_PEERSTORE_WATCH, - struct StoreKeyHashMessage, - NULL), - GNUNET_MQ_hd_fixed_size (watch_cancel, - GNUNET_MESSAGE_TYPE_PEERSTORE_WATCH_CANCEL, - struct StoreKeyHashMessage, - NULL), - GNUNET_MQ_handler_end ()); - - -/* end of gnunet-service-peerstore.c */ diff --git a/src/peerstore/meson.build b/src/peerstore/meson.build deleted file mode 100644 index 6c9853852..000000000 --- a/src/peerstore/meson.build +++ /dev/null @@ -1,50 +0,0 @@ -libgnunetpeerstore_src = ['peerstore_api.c', - 'peerstore_common.c'] - -gnunetservicepeerstore_src = ['gnunet-service-peerstore.c'] - -configure_file(input : 'peerstore.conf.in', - output : 'peerstore.conf', - configuration : cdata, - install: true, - install_dir: pkgcfgdir) - - -if get_option('monolith') - foreach p : libgnunetpeerstore_src + gnunetservicepeerstore_src - gnunet_src += 'peerstore/' + p - endforeach - subdir_done() -endif - -libgnunetpeerstore = library('gnunetpeerstore', - libgnunetpeerstore_src, - soversion: '0', - version: '0.0.0', - dependencies: [libgnunetutil_dep, libgnunethello_dep], - include_directories: [incdir, configuration_inc], - install: true, - install_dir: get_option('libdir')) -pkg.generate(libgnunetpeerstore, url: 'https://www.gnunet.org', - description : 'Provides API for accessing the peerstore service') -libgnunetpeerstore_dep = declare_dependency(link_with : libgnunetpeerstore) - -shared_module('gnunet_plugin_peerstore_sqlite', - ['plugin_peerstore_sqlite.c'], - dependencies: [libgnunetutil_dep, - libgnunetpeerstore_dep, - libgnunetsq_dep, - sqlite_dep], - include_directories: [incdir, configuration_inc], - install: true, - install_dir: get_option('libdir')/'gnunet') - -executable ('gnunet-service-peerstore', - gnunetservicepeerstore_src, - dependencies: [libgnunetpeerstore_dep, - libgnunetutil_dep, - libgnunethello_dep], - include_directories: [incdir, configuration_inc], - install: true, - install_dir: get_option('libdir') / 'gnunet' / 'libexec') - diff --git a/src/peerstore/peerstore.conf.in b/src/peerstore/peerstore.conf.in deleted file mode 100644 index a3a7c672b..000000000 --- a/src/peerstore/peerstore.conf.in +++ /dev/null @@ -1,13 +0,0 @@ -[peerstore] -START_ON_DEMAND = @START_ON_DEMAND@ -@JAVAPORT@PORT = 2110 -HOSTNAME = localhost -BINARY = gnunet-service-peerstore -UNIXPATH = $GNUNET_RUNTIME_DIR/gnunet-service-peerstore.sock -UNIX_MATCH_UID = NO -UNIX_MATCH_GID = YES -DATABASE = sqlite - -[peerstore-sqlite] -FILENAME = $GNUNET_DATA_HOME/peerstore/sqlite.db - diff --git a/src/peerstore/peerstore.h b/src/peerstore/peerstore.h deleted file mode 100644 index 0dec03443..000000000 --- a/src/peerstore/peerstore.h +++ /dev/null @@ -1,108 +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 . - - SPDX-License-Identifier: AGPL3.0-or-later - */ -/** - * @file peerstore/peerstore.h - * @brief IPC messages - * @author Omar Tarabai - */ - -#ifndef PEERSTORE_H_ -#define PEERSTORE_H_ - -#include "gnunet_peerstore_service.h" - - -GNUNET_NETWORK_STRUCT_BEGIN -/** - * Message carrying a PEERSTORE record message - */ -struct StoreRecordMessage -{ - /** - * GNUnet message header - */ - struct GNUNET_MessageHeader header; - - /** - * #GNUNET_YES if peer id value set, #GNUNET_NO otherwise - */ - uint16_t peer_set GNUNET_PACKED; - - /** - * Size of the sub_system string - * Allocated at position 0 after this struct - */ - uint16_t sub_system_size GNUNET_PACKED; - - /** - * Peer Identity - */ - struct GNUNET_PeerIdentity peer; - - /** - * Expiry time of entry - */ - struct GNUNET_TIME_AbsoluteNBO expiry; - - /** - * Size of the key string - * Allocated at position 1 after this struct - */ - uint16_t key_size GNUNET_PACKED; - - /** - * Size of value blob - * Allocated at position 2 after this struct - */ - uint16_t value_size GNUNET_PACKED; - - /** - * Options, needed only in case of a - * store operation - */ - uint32_t /* enum GNUNET_PEERSTORE_StoreOption */ options GNUNET_PACKED; - - /* Followed by key and value */ -}; - - -/** - * Message carrying record key hash - */ -struct StoreKeyHashMessage -{ - /** - * GNUnet message header - */ - struct GNUNET_MessageHeader header; - - /** - * Always 0, for alignment. - */ - uint32_t reserved GNUNET_PACKED; - - /** - * Hash of a record key - */ - struct GNUNET_HashCode keyhash; -}; - -GNUNET_NETWORK_STRUCT_END -#endif diff --git a/src/peerstore/peerstore_api.c b/src/peerstore/peerstore_api.c deleted file mode 100644 index 8770c36e4..000000000 --- a/src/peerstore/peerstore_api.c +++ /dev/null @@ -1,1391 +0,0 @@ -/* - This file is part of GNUnet. - Copyright (C) 2013-2016, 2019 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 . - - SPDX-License-Identifier: AGPL3.0-or-later - */ -/** - * @file peerstore/peerstore_api.c - * @brief API for peerstore - * @author Omar Tarabai - * @author Christian Grothoff - */ -#include "platform.h" -#include "gnunet_util_lib.h" -#include "gnunet_hello_uri_lib.h" -#include "peerstore.h" -#include "peerstore_common.h" -#include "gnunet_peerstore_service.h" - -#define LOG(kind, ...) GNUNET_log_from (kind, "peerstore-api", __VA_ARGS__) - -/******************************************************************************/ -/************************ DATA STRUCTURES ****************************/ -/******************************************************************************/ - -/** - * Handle to the PEERSTORE service. - */ -struct GNUNET_PEERSTORE_Handle -{ - /** - * Our configuration. - */ - const struct GNUNET_CONFIGURATION_Handle *cfg; - - /** - * Message queue - */ - struct GNUNET_MQ_Handle *mq; - - /** - * Head of active STORE requests. - */ - struct GNUNET_PEERSTORE_StoreContext *store_head; - - /** - * Tail of active STORE requests. - */ - struct GNUNET_PEERSTORE_StoreContext *store_tail; - - /** - * Head of active ITERATE requests. - */ - struct GNUNET_PEERSTORE_IterateContext *iterate_head; - - /** - * Tail of active ITERATE requests. - */ - struct GNUNET_PEERSTORE_IterateContext *iterate_tail; - - /** - * Hashmap of watch requests - */ - struct GNUNET_CONTAINER_MultiHashMap *watches; - - /** - * ID of the task trying to reconnect to the service. - */ - struct GNUNET_SCHEDULER_Task *reconnect_task; - - /** - * Delay until we try to reconnect. - */ - struct GNUNET_TIME_Relative reconnect_delay; - - /** - * Are we in the process of disconnecting but need to sync first? - */ - int disconnecting; -}; - -/** - * Context for a store request - */ -struct GNUNET_PEERSTORE_StoreContext -{ - /** - * Kept in a DLL. - */ - struct GNUNET_PEERSTORE_StoreContext *next; - - /** - * Kept in a DLL. - */ - struct GNUNET_PEERSTORE_StoreContext *prev; - - /** - * Handle to the PEERSTORE service. - */ - struct GNUNET_PEERSTORE_Handle *h; - - /** - * Continuation called with service response - */ - GNUNET_PEERSTORE_Continuation cont; - - /** - * Closure for @e cont - */ - void *cont_cls; - - /** - * Which subsystem does the store? - */ - char *sub_system; - - /** - * Key for the store operation. - */ - char *key; - - /** - * Contains @e size bytes. - */ - void *value; - - /** - * Peer the store is for. - */ - struct GNUNET_PeerIdentity peer; - - /** - * Number of bytes in @e value. - */ - size_t size; - - /** - * When does the value expire? - */ - struct GNUNET_TIME_Absolute expiry; - - /** - * Options for the store operation. - */ - enum GNUNET_PEERSTORE_StoreOption options; -}; - -/** - * Closure for store callback when storing hello uris. - */ -struct StoreHelloCls -{ - /** - * The corresponding store context. - */ - struct GNUNET_PEERSTORE_StoreContext *sc; - - /** - * The corresponding hello uri add request. - */ - struct GNUNET_PEERSTORE_StoreHelloContext *huc; -}; - -/** - * Context for a iterate request - */ -struct GNUNET_PEERSTORE_IterateContext -{ - /** - * Kept in a DLL. - */ - struct GNUNET_PEERSTORE_IterateContext *next; - - /** - * Kept in a DLL. - */ - struct GNUNET_PEERSTORE_IterateContext *prev; - - /** - * Handle to the PEERSTORE service. - */ - struct GNUNET_PEERSTORE_Handle *h; - - /** - * Which subsystem does the store? - */ - char *sub_system; - - /** - * Peer the store is for. - */ - struct GNUNET_PeerIdentity peer; - - /** - * Key for the store operation. - */ - char *key; - - /** - * Callback with each matching record - */ - GNUNET_PEERSTORE_Processor callback; - - /** - * Closure for @e callback - */ - void *callback_cls; - - /** - * #GNUNET_YES if we are currently processing records. - */ - int iterating; -}; - -/** - * Context for a watch request - */ -struct GNUNET_PEERSTORE_WatchContext -{ - /** - * Kept in a DLL. - */ - struct GNUNET_PEERSTORE_WatchContext *next; - - /** - * Kept in a DLL. - */ - struct GNUNET_PEERSTORE_WatchContext *prev; - - /** - * Handle to the PEERSTORE service. - */ - struct GNUNET_PEERSTORE_Handle *h; - - /** - * Callback with each record received - */ - GNUNET_PEERSTORE_Processor callback; - - /** - * Closure for @e callback - */ - void *callback_cls; - - /** - * Hash of the combined key - */ - struct GNUNET_HashCode keyhash; - - /** - * The iteration context to deliver the actual values for the key. - */ - struct GNUNET_PEERSTORE_IterateContext *ic; - - /** - * The peer we are watching for values. - */ - const struct GNUNET_PeerIdentity *peer; - - /** - * The key we like to watch for values. - */ - const char *key; - - /** - * The sub system requested the watch. - */ - const char *sub_system; -}; - -/** - * Context for the info handler. - */ -struct GNUNET_PEERSTORE_NotifyContext -{ - /** - * Peerstore handle. - */ - struct GNUNET_PEERSTORE_Handle *h; - - /** - * Function to call with information. - */ - GNUNET_PEERSTORE_hello_notify_cb callback; - - /** - * Closure for @e callback. - */ - void *callback_cls; - - /** - * The watch for this context. - */ - struct GNUNET_PEERSTORE_WatchContext *wc; - - /** - * Is this request canceled. - */ - unsigned int canceled; -}; - -/** - * Context for a add hello uri request. - */ -struct GNUNET_PEERSTORE_StoreHelloContext -{ - /** - * Peerstore handle. - */ - struct GNUNET_PEERSTORE_Handle *h; - - /** - * Function to call with information. - */ - GNUNET_PEERSTORE_Continuation cont; - - /** - * Closure for @e callback. - */ - void *cont_cls; - - /** - * Map with all store contexts started during adding hello. - */ - struct GNUNET_CONTAINER_MultiPeerMap *store_context_map; - - /** - * Active watch to be notified about conflicting hello uri add requests. - */ - struct GNUNET_PEERSTORE_WatchContext *wc; - - /** - * Hello uri which was request for storing. - */ - struct GNUNET_MessageHeader *hello; - - /** - * The peer id for the hello. - */ - struct GNUNET_PeerIdentity *pid; - - /** - * Was this request successful. - */ - int success; -}; - -/******************************************************************************/ -/******************* DECLARATIONS *********************/ -/******************************************************************************/ - -/** - * Close the existing connection to PEERSTORE and reconnect. - * - * @param cls a `struct GNUNET_PEERSTORE_Handle *h` - */ -static void -reconnect (void *cls); - - -/** - * Disconnect from the peerstore service. - * - * @param h peerstore handle to disconnect - */ -static void -disconnect (struct GNUNET_PEERSTORE_Handle *h) -{ - struct GNUNET_PEERSTORE_IterateContext *next; - - for (struct GNUNET_PEERSTORE_IterateContext *ic = h->iterate_head; NULL != ic; - ic = next) - { - next = ic->next; - if (GNUNET_YES == ic->iterating) - { - GNUNET_PEERSTORE_Processor icb; - void *icb_cls; - - icb = ic->callback; - icb_cls = ic->callback_cls; - GNUNET_PEERSTORE_iterate_cancel (ic); - if (NULL != icb) - icb (icb_cls, NULL, "Iteration canceled due to reconnection"); - } - } - - if (NULL != h->mq) - { - GNUNET_MQ_destroy (h->mq); - h->mq = NULL; - } -} - - -/** - * Function that will schedule the job that will try - * to connect us again to the client. - * - * @param h peerstore to reconnect - */ -static void -disconnect_and_schedule_reconnect (struct GNUNET_PEERSTORE_Handle *h) -{ - GNUNET_assert (NULL == h->reconnect_task); - disconnect (h); - LOG (GNUNET_ERROR_TYPE_DEBUG, - "Scheduling task to reconnect to PEERSTORE service in %s.\n", - GNUNET_STRINGS_relative_time_to_string (h->reconnect_delay, GNUNET_YES)); - h->reconnect_task = - GNUNET_SCHEDULER_add_delayed (h->reconnect_delay, &reconnect, h); - h->reconnect_delay = GNUNET_TIME_STD_BACKOFF (h->reconnect_delay); -} - - -/** - * Callback after MQ envelope is sent - * - * @param cls a `struct GNUNET_PEERSTORE_StoreContext *` - */ -static void -store_request_sent (void *cls) -{ - struct GNUNET_PEERSTORE_StoreContext *sc = cls; - GNUNET_PEERSTORE_Continuation cont; - void *cont_cls; - - if (NULL != sc) - { - cont = sc->cont; - cont_cls = sc->cont_cls; - GNUNET_PEERSTORE_store_cancel (sc); - if (NULL != cont) - cont (cont_cls, GNUNET_OK); - } -} - - -/******************************************************************************/ -/******************* CONNECTION FUNCTIONS *********************/ -/******************************************************************************/ - - -/** - * Function called when we had trouble talking to the service. - */ -static void -handle_client_error (void *cls, enum GNUNET_MQ_Error error) -{ - struct GNUNET_PEERSTORE_Handle *h = cls; - - LOG (GNUNET_ERROR_TYPE_ERROR, - "Received an error notification from MQ of type: %d\n", - error); - disconnect_and_schedule_reconnect (h); -} - - -/** - * Iterator over previous watches to resend them - * - * @param cls the `struct GNUNET_PEERSTORE_Handle` - * @param key key for the watch - * @param value the `struct GNUNET_PEERSTORE_WatchContext *` - * @return #GNUNET_YES (continue to iterate) - */ -static int -rewatch_it (void *cls, const struct GNUNET_HashCode *key, void *value) -{ - struct GNUNET_PEERSTORE_Handle *h = cls; - struct GNUNET_PEERSTORE_WatchContext *wc = value; - struct StoreKeyHashMessage *hm; - struct GNUNET_MQ_Envelope *ev; - - ev = GNUNET_MQ_msg (hm, GNUNET_MESSAGE_TYPE_PEERSTORE_WATCH); - hm->keyhash = wc->keyhash; - GNUNET_MQ_send (h->mq, ev); - return GNUNET_YES; -} - - -/** - * Iterator over watch requests to cancel them. - * - * @param cls unused - * @param key key to the watch request - * @param value watch context - * @return #GNUNET_YES to continue iteration - */ -static int -destroy_watch (void *cls, const struct GNUNET_HashCode *key, void *value) -{ - struct GNUNET_PEERSTORE_WatchContext *wc = value; - - GNUNET_PEERSTORE_watch_cancel (wc); - return GNUNET_YES; -} - - -/** - * Kill the connection to the service. This can be delayed in case of pending - * STORE requests and the user explicitly asked to sync first. Otherwise it is - * performed instantly. - * - * @param h Handle to the service. - */ -static void -final_disconnect (struct GNUNET_PEERSTORE_Handle *h) -{ - if (NULL != h->mq) - { - GNUNET_MQ_destroy (h->mq); - h->mq = NULL; - } - GNUNET_free (h); -} - - -/** - * Connect to the PEERSTORE service. - * - * @param cfg configuration to use - * @return NULL on error - */ -struct GNUNET_PEERSTORE_Handle * -GNUNET_PEERSTORE_connect (const struct GNUNET_CONFIGURATION_Handle *cfg) -{ - struct GNUNET_PEERSTORE_Handle *h; - - h = GNUNET_new (struct GNUNET_PEERSTORE_Handle); - h->cfg = cfg; - h->disconnecting = GNUNET_NO; - reconnect (h); - if (NULL == h->mq) - { - GNUNET_free (h); - return NULL; - } - return h; -} - - -/** - * Disconnect from the PEERSTORE service. Any pending ITERATE and WATCH requests - * will be canceled. - * Any pending STORE requests will depend on @e snyc_first flag. - * - * @param h handle to disconnect - * @param sync_first send any pending STORE requests before disconnecting - */ -void -GNUNET_PEERSTORE_disconnect (struct GNUNET_PEERSTORE_Handle *h, int sync_first) -{ - struct GNUNET_PEERSTORE_IterateContext *ic; - struct GNUNET_PEERSTORE_StoreContext *sc; - - LOG (GNUNET_ERROR_TYPE_DEBUG, "Disconnecting.\n"); - if (NULL != h->watches) - { - GNUNET_CONTAINER_multihashmap_iterate (h->watches, &destroy_watch, NULL); - GNUNET_CONTAINER_multihashmap_destroy (h->watches); - h->watches = NULL; - } - while (NULL != (ic = h->iterate_head)) - { - GNUNET_break (0); - GNUNET_PEERSTORE_iterate_cancel (ic); - } - if (NULL != h->store_head) - { - if (GNUNET_YES == sync_first) - { - LOG (GNUNET_ERROR_TYPE_DEBUG, - "Delaying disconnection due to pending store requests.\n"); - h->disconnecting = GNUNET_YES; - return; - } - while (NULL != (sc = h->store_head)) - GNUNET_PEERSTORE_store_cancel (sc); - } - final_disconnect (h); -} - - -/******************************************************************************/ -/******************* STORE FUNCTIONS *********************/ -/******************************************************************************/ - - -/** - * Cancel a store request - * - * @param sc Store request context - */ -void -GNUNET_PEERSTORE_store_cancel (struct GNUNET_PEERSTORE_StoreContext *sc) -{ - struct GNUNET_PEERSTORE_Handle *h = sc->h; - - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "store cancel with sc %p \n", - sc); - GNUNET_CONTAINER_DLL_remove (sc->h->store_head, sc->h->store_tail, sc); - GNUNET_free (sc->sub_system); - GNUNET_free (sc->value); - GNUNET_free (sc->key); - GNUNET_free (sc); - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "store cancel with sc %p is null\n", - sc); - if ((GNUNET_YES == h->disconnecting) && (NULL == h->store_head)) - final_disconnect (h); -} - - -/** - * Store a new entry in the PEERSTORE. - * Note that stored entries can be lost in some cases - * such as power failure. - * - * @param h Handle to the PEERSTORE service - * @param sub_system name of the sub system - * @param peer Peer Identity - * @param key entry key - * @param value entry value BLOB - * @param size size of @e value - * @param expiry absolute time after which the entry is (possibly) deleted - * @param options options specific to the storage operation - * @param cont Continuation function after the store request is sent - * @param cont_cls Closure for @a cont - */ -struct GNUNET_PEERSTORE_StoreContext * -GNUNET_PEERSTORE_store (struct GNUNET_PEERSTORE_Handle *h, - const char *sub_system, - const struct GNUNET_PeerIdentity *peer, - const char *key, - const void *value, - size_t size, - struct GNUNET_TIME_Absolute expiry, - enum GNUNET_PEERSTORE_StoreOption options, - GNUNET_PEERSTORE_Continuation cont, - void *cont_cls) -{ - struct GNUNET_MQ_Envelope *ev; - struct GNUNET_PEERSTORE_StoreContext *sc; - - LOG (GNUNET_ERROR_TYPE_DEBUG, - "Storing value (size: %lu) for subsystem `%s', peer `%s', key `%s'\n", - size, - sub_system, - GNUNET_i2s (peer), - key); - ev = - PEERSTORE_create_record_mq_envelope (sub_system, - peer, - key, - value, - size, - expiry, - options, - GNUNET_MESSAGE_TYPE_PEERSTORE_STORE); - sc = GNUNET_new (struct GNUNET_PEERSTORE_StoreContext); - - sc->sub_system = GNUNET_strdup (sub_system); - sc->peer = *peer; - sc->key = GNUNET_strdup (key); - sc->value = GNUNET_memdup (value, size); - sc->size = size; - sc->expiry = expiry; - sc->options = options; - sc->cont = cont; - sc->cont_cls = cont_cls; - sc->h = h; - - GNUNET_CONTAINER_DLL_insert_tail (h->store_head, h->store_tail, sc); - GNUNET_MQ_notify_sent (ev, &store_request_sent, sc); - GNUNET_MQ_send (h->mq, ev); - return sc; -} - - -/******************************************************************************/ -/******************* ITERATE FUNCTIONS *********************/ -/******************************************************************************/ - - -/** - * When a response for iterate request is received - * - * @param cls a `struct GNUNET_PEERSTORE_Handle *` - * @param msg message received - */ -static void -handle_iterate_end (void *cls, const struct GNUNET_MessageHeader *msg) -{ - struct GNUNET_PEERSTORE_Handle *h = cls; - struct GNUNET_PEERSTORE_IterateContext *ic; - GNUNET_PEERSTORE_Processor callback; - void *callback_cls; - - ic = h->iterate_head; - if (NULL == ic) - { - LOG (GNUNET_ERROR_TYPE_ERROR, - _ ("Unexpected iteration response, this should not happen.\n")); - disconnect_and_schedule_reconnect (h); - return; - } - callback = ic->callback; - callback_cls = ic->callback_cls; - ic->iterating = GNUNET_NO; - GNUNET_PEERSTORE_iterate_cancel (ic); - /* NOTE: set this here and not after callback because callback may free h */ - h->reconnect_delay = GNUNET_TIME_UNIT_ZERO; - if (NULL != callback) - callback (callback_cls, NULL, NULL); -} - - -/** - * When a response for iterate request is received, check the - * message is well-formed. - * - * @param cls a `struct GNUNET_PEERSTORE_Handle *` - * @param msg message received - */ -static int -check_iterate_result (void *cls, const struct StoreRecordMessage *msg) -{ - /* we defer validation to #handle_iterate_result */ - return GNUNET_OK; -} - - -/** - * When a response for iterate request is received - * - * @param cls a `struct GNUNET_PEERSTORE_Handle *` - * @param msg message received - */ -static void -handle_iterate_result (void *cls, const struct StoreRecordMessage *msg) -{ - struct GNUNET_PEERSTORE_Handle *h = cls; - struct GNUNET_PEERSTORE_IterateContext *ic; - GNUNET_PEERSTORE_Processor callback; - void *callback_cls; - struct GNUNET_PEERSTORE_Record *record; - - ic = h->iterate_head; - if (NULL == ic) - { - LOG (GNUNET_ERROR_TYPE_ERROR, - _ ("Unexpected iteration response, this should not happen.\n")); - disconnect_and_schedule_reconnect (h); - return; - } - ic->iterating = GNUNET_YES; - callback = ic->callback; - callback_cls = ic->callback_cls; - if (NULL == callback) - return; - record = PEERSTORE_parse_record_message (msg); - if (NULL == record) - { - callback (callback_cls, - NULL, - _ ("Received a malformed response from service.")); - } - else - { - callback (callback_cls, record, NULL); - PEERSTORE_destroy_record (record); - } -} - - -/** - * Cancel an iterate request - * Please do not call after the iterate request is done - * - * @param ic Iterate request context as returned by GNUNET_PEERSTORE_iterate() - */ -void -GNUNET_PEERSTORE_iterate_cancel (struct GNUNET_PEERSTORE_IterateContext *ic) -{ - if (GNUNET_NO == ic->iterating) - { - GNUNET_CONTAINER_DLL_remove (ic->h->iterate_head, ic->h->iterate_tail, ic); - GNUNET_free (ic->sub_system); - GNUNET_free (ic->key); - GNUNET_free (ic); - } - else - ic->callback = NULL; -} - - -struct GNUNET_PEERSTORE_IterateContext * -GNUNET_PEERSTORE_iterate (struct GNUNET_PEERSTORE_Handle *h, - const char *sub_system, - const struct GNUNET_PeerIdentity *peer, - const char *key, - GNUNET_PEERSTORE_Processor callback, - void *callback_cls) -{ - struct GNUNET_MQ_Envelope *ev; - struct GNUNET_PEERSTORE_IterateContext *ic; - - ev = - PEERSTORE_create_record_mq_envelope (sub_system, - peer, - key, - NULL, - 0, - GNUNET_TIME_UNIT_FOREVER_ABS, - 0, - GNUNET_MESSAGE_TYPE_PEERSTORE_ITERATE); - ic = GNUNET_new (struct GNUNET_PEERSTORE_IterateContext); - ic->callback = callback; - ic->callback_cls = callback_cls; - ic->h = h; - ic->sub_system = GNUNET_strdup (sub_system); - if (NULL != peer) - ic->peer = *peer; - if (NULL != key) - ic->key = GNUNET_strdup (key); - GNUNET_CONTAINER_DLL_insert_tail (h->iterate_head, h->iterate_tail, ic); - LOG (GNUNET_ERROR_TYPE_DEBUG, - "Sending an iterate request for sub system `%s'\n", - sub_system); - GNUNET_MQ_send (h->mq, ev); - return ic; -} - - -/******************************************************************************/ -/******************* WATCH FUNCTIONS *********************/ -/******************************************************************************/ - -/** - * When a watch record is received, validate it is well-formed. - * - * @param cls a `struct GNUNET_PEERSTORE_Handle *` - * @param msg message received - */ -static int -check_watch_record (void *cls, const struct StoreRecordMessage *msg) -{ - /* we defer validation to #handle_watch_result */ - return GNUNET_OK; -} - - -/** - * When a watch record is received, process it. - * - * @param cls a `struct GNUNET_PEERSTORE_Handle *` - * @param msg message received - */ -static void -handle_watch_record (void *cls, const struct StoreRecordMessage *msg) -{ - struct GNUNET_PEERSTORE_Handle *h = cls; - struct GNUNET_PEERSTORE_Record *record; - struct GNUNET_HashCode keyhash; - struct GNUNET_PEERSTORE_WatchContext *wc; - - LOG (GNUNET_ERROR_TYPE_DEBUG, "Received a watch record from service.\n"); - record = PEERSTORE_parse_record_message (msg); - if (NULL == record) - { - disconnect_and_schedule_reconnect (h); - return; - } - PEERSTORE_hash_key (record->sub_system, &record->peer, record->key, &keyhash); - // FIXME: what if there are multiple watches for the same key? - wc = GNUNET_CONTAINER_multihashmap_get (h->watches, &keyhash); - if (NULL == wc) - { - LOG (GNUNET_ERROR_TYPE_ERROR, - _ ("Received a watch result for a non existing watch.\n")); - PEERSTORE_destroy_record (record); - disconnect_and_schedule_reconnect (h); - return; - } - h->reconnect_delay = GNUNET_TIME_UNIT_ZERO; - if (NULL != wc->callback) - wc->callback (wc->callback_cls, record, NULL); - PEERSTORE_destroy_record (record); -} - - -/** - * Close the existing connection to PEERSTORE and reconnect. - * - * @param cls a `struct GNUNET_PEERSTORE_Handle *` - */ -static void -reconnect (void *cls) -{ - struct GNUNET_PEERSTORE_Handle *h = cls; - struct GNUNET_MQ_MessageHandler mq_handlers[] = - { GNUNET_MQ_hd_fixed_size (iterate_end, - GNUNET_MESSAGE_TYPE_PEERSTORE_ITERATE_END, - struct GNUNET_MessageHeader, - h), - GNUNET_MQ_hd_var_size (iterate_result, - GNUNET_MESSAGE_TYPE_PEERSTORE_ITERATE_RECORD, - struct StoreRecordMessage, - h), - GNUNET_MQ_hd_var_size (watch_record, - GNUNET_MESSAGE_TYPE_PEERSTORE_WATCH_RECORD, - struct StoreRecordMessage, - h), - GNUNET_MQ_handler_end () }; - struct GNUNET_MQ_Envelope *ev; - - h->reconnect_task = NULL; - LOG (GNUNET_ERROR_TYPE_DEBUG, "Reconnecting...\n"); - h->mq = GNUNET_CLIENT_connect (h->cfg, - "peerstore", - mq_handlers, - &handle_client_error, - h); - if (NULL == h->mq) - { - h->reconnect_task = - GNUNET_SCHEDULER_add_delayed (h->reconnect_delay, &reconnect, h); - h->reconnect_delay = GNUNET_TIME_STD_BACKOFF (h->reconnect_delay); - return; - } - LOG (GNUNET_ERROR_TYPE_DEBUG, - "Resending pending requests after reconnect.\n"); - if (NULL != h->watches) - GNUNET_CONTAINER_multihashmap_iterate (h->watches, &rewatch_it, h); - for (struct GNUNET_PEERSTORE_IterateContext *ic = h->iterate_head; NULL != ic; - ic = ic->next) - { - ev = - PEERSTORE_create_record_mq_envelope (ic->sub_system, - &ic->peer, - ic->key, - NULL, - 0, - GNUNET_TIME_UNIT_FOREVER_ABS, - 0, - GNUNET_MESSAGE_TYPE_PEERSTORE_ITERATE); - GNUNET_MQ_send (h->mq, ev); - } - for (struct GNUNET_PEERSTORE_StoreContext *sc = h->store_head; NULL != sc; - sc = sc->next) - { - ev = - PEERSTORE_create_record_mq_envelope (sc->sub_system, - &sc->peer, - sc->key, - sc->value, - sc->size, - sc->expiry, - sc->options, - GNUNET_MESSAGE_TYPE_PEERSTORE_STORE); - GNUNET_MQ_notify_sent (ev, &store_request_sent, sc); - GNUNET_MQ_send (h->mq, ev); - } -} - - -/** - * Cancel a watch request - * - * @param wc handle to the watch request - */ -void -GNUNET_PEERSTORE_watch_cancel (struct GNUNET_PEERSTORE_WatchContext *wc) -{ - struct GNUNET_PEERSTORE_Handle *h = wc->h; - struct GNUNET_MQ_Envelope *ev; - struct StoreKeyHashMessage *hm; - - LOG (GNUNET_ERROR_TYPE_DEBUG, "Canceling watch.\n"); - if (NULL != wc->ic) - { - GNUNET_PEERSTORE_iterate_cancel (wc->ic); - GNUNET_free (wc); - return; - } - - ev = GNUNET_MQ_msg (hm, GNUNET_MESSAGE_TYPE_PEERSTORE_WATCH_CANCEL); - hm->keyhash = wc->keyhash; - GNUNET_MQ_send (h->mq, ev); - GNUNET_assert ( - GNUNET_YES == - GNUNET_CONTAINER_multihashmap_remove (h->watches, &wc->keyhash, wc)); - GNUNET_free (wc); -} - - -static void -watch_iterate (void *cls, - const struct GNUNET_PEERSTORE_Record *record, - const char *emsg) -{ - struct GNUNET_PEERSTORE_WatchContext *wc = cls; - struct GNUNET_PEERSTORE_Handle *h = wc->h; - struct StoreKeyHashMessage *hm; - - if (NULL != emsg) - { - GNUNET_log (GNUNET_ERROR_TYPE_WARNING, - "Got failure from PEERSTORE: %s\n", - emsg); - wc->callback (wc->callback_cls, NULL, emsg); - return; - } - if (NULL == record) - { - struct GNUNET_MQ_Envelope *ev; - const struct GNUNET_PeerIdentity *peer; - - if (NULL == wc->peer) - peer = GNUNET_new (struct GNUNET_PeerIdentity); - else - peer = wc->peer; - ev = GNUNET_MQ_msg (hm, GNUNET_MESSAGE_TYPE_PEERSTORE_WATCH); - PEERSTORE_hash_key (wc->sub_system, peer, wc->key, &hm->keyhash); - LOG (GNUNET_ERROR_TYPE_DEBUG, - "Hash key we watch for %s\n", - GNUNET_h2s_full (&hm->keyhash)); - wc->keyhash = hm->keyhash; - if (NULL == h->watches) - h->watches = GNUNET_CONTAINER_multihashmap_create (5, GNUNET_NO); - GNUNET_assert (GNUNET_OK == GNUNET_CONTAINER_multihashmap_put ( - h->watches, - &wc->keyhash, - wc, - GNUNET_CONTAINER_MULTIHASHMAPOPTION_MULTIPLE)); - LOG (GNUNET_ERROR_TYPE_DEBUG, - "Sending a watch request for subsystem `%s', peer `%s', key `%s'.\n", - wc->sub_system, - GNUNET_i2s (peer), - wc->key); - GNUNET_MQ_send (h->mq, ev); - wc->ic = NULL; - if (NULL != wc->callback) - wc->callback (wc->callback_cls, record, NULL); - return; - } - - if (NULL != wc->callback) - wc->callback (wc->callback_cls, record, NULL); -} - - -/** - * Request watching a given key - * User will be notified with any new values added to key, - * all existing entries are supplied beforehand. - * - * @param h handle to the PEERSTORE service - * @param sub_system name of sub system - * @param peer Peer identity - * @param key entry key string - * @param callback function called with each new value - * @param callback_cls closure for @a callback - * @return Handle to watch request - */ -struct GNUNET_PEERSTORE_WatchContext * -GNUNET_PEERSTORE_watch (struct GNUNET_PEERSTORE_Handle *h, - const char *sub_system, - const struct GNUNET_PeerIdentity *peer, - const char *key, - GNUNET_PEERSTORE_Processor callback, - void *callback_cls) -{ - struct GNUNET_PEERSTORE_IterateContext *ic; - struct GNUNET_PEERSTORE_WatchContext *wc; - - wc = GNUNET_new (struct GNUNET_PEERSTORE_WatchContext); - wc->callback = callback; - wc->callback_cls = callback_cls; - wc->h = h; - wc->ic = ic; - wc->key = key; - wc->peer = peer; - wc->sub_system = sub_system; - - ic = GNUNET_PEERSTORE_iterate (h, - sub_system, - peer, - key, - &watch_iterate, - wc); - - return wc; -} - - -/******************************************************************************/ -/******************* HELLO FUNCTIONS *********************/ -/******************************************************************************/ - - -static void -hello_updated (void *cls, - const struct GNUNET_PEERSTORE_Record *record, - const char *emsg) -{ - struct GNUNET_PEERSTORE_NotifyContext *nc = cls; - const struct GNUNET_MessageHeader *hello; - struct GNUNET_HELLO_Builder *builder; - - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "hello_updated\n"); - if (NULL != emsg) - { - GNUNET_log (GNUNET_ERROR_TYPE_WARNING, - "Got failure from PEERSTORE: %s\n", - emsg); - nc->callback (nc->callback_cls, NULL, NULL, emsg); - return; - } - if (NULL == record) - return; - hello = record->value; - builder = GNUNET_HELLO_builder_from_msg (hello); - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "hello_updated with expired %s and size %u for peer %s\n", - GNUNET_STRINGS_absolute_time_to_string (GNUNET_HELLO_builder_get_expiration_time (hello)), - ntohs (hello->size), - GNUNET_i2s (&record->peer)); - if ((0 == record->value_size)) - { - GNUNET_break (0); - return; - } - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "hello_updated call callback\n"); - nc->callback (nc->callback_cls, &record->peer, hello, NULL); -} - - -struct GNUNET_PEERSTORE_NotifyContext * -GNUNET_PEERSTORE_hello_changed_notify (struct GNUNET_PEERSTORE_Handle *h, - int include_friend_only, - GNUNET_PEERSTORE_hello_notify_cb callback, - void *callback_cls) -{ - struct GNUNET_PEERSTORE_NotifyContext *nc; - - nc = GNUNET_new (struct GNUNET_PEERSTORE_NotifyContext); - nc->callback = callback; - nc->callback_cls = callback_cls; - nc->h = h; - - nc->wc = GNUNET_PEERSTORE_watch (h, - "peerstore", - NULL, - GNUNET_PEERSTORE_HELLO_KEY, - &hello_updated, - nc); - - return nc; -} - - -/** - * Stop notifying about changes. - * - * @param nc context to stop notifying - */ -void -GNUNET_PEERSTORE_hello_changed_notify_cancel (struct - GNUNET_PEERSTORE_NotifyContext *nc) -{ - if (NULL != nc->wc) - { - GNUNET_PEERSTORE_watch_cancel (nc->wc); - nc->wc = NULL; - } -} - - -static void -merge_success (void *cls, int success) -{ - struct StoreHelloCls *shu_cls = cls; - struct GNUNET_PEERSTORE_StoreHelloContext *huc = shu_cls->huc; - - if (GNUNET_OK != success) - { - GNUNET_log (GNUNET_ERROR_TYPE_WARNING, - "Storing hello uri failed\n"); - huc->cont (huc->cont_cls, success); - GNUNET_free (huc->hello); - GNUNET_free (huc->pid); - GNUNET_free (huc); - return; - } - GNUNET_CONTAINER_multipeermap_remove (huc->store_context_map, huc->pid, shu_cls->sc); - if (0 == GNUNET_CONTAINER_multipeermap_size (huc->store_context_map)) - { - GNUNET_PEERSTORE_watch_cancel (huc->wc); - huc->wc = NULL; - huc->cont (huc->cont_cls, GNUNET_OK); - huc->success = GNUNET_OK; - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "Storing hello uri succeeded for peer %s!\n", - GNUNET_i2s (huc->pid)); - GNUNET_free (huc->hello); - GNUNET_free (huc->pid); - GNUNET_free (huc); - return; - } - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "Got notified during storing hello uri for peer %s!\n", - GNUNET_i2s (huc->pid)); -} - - -static void -store_hello (struct GNUNET_PEERSTORE_StoreHelloContext *huc, - const struct GNUNET_MessageHeader *hello) -{ - struct GNUNET_PEERSTORE_Handle *h = huc->h; - struct GNUNET_PEERSTORE_StoreContext *sc; - struct StoreHelloCls *shu_cls = GNUNET_new (struct StoreHelloCls); - struct GNUNET_TIME_Absolute hello_exp; - - shu_cls->huc = huc; - hello_exp = GNUNET_HELLO_builder_get_expiration_time (hello); - sc = GNUNET_PEERSTORE_store (h, - "peerstore", - huc->pid, - GNUNET_PEERSTORE_HELLO_KEY, - hello, - ntohs (hello->size), - hello_exp, - GNUNET_PEERSTORE_STOREOPTION_MULTIPLE, - merge_success, - shu_cls); - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "store_hello with expiration %s\n", - GNUNET_STRINGS_absolute_time_to_string (hello_exp)); - GNUNET_CONTAINER_multipeermap_put (huc->store_context_map, - huc->pid, - sc, - GNUNET_CONTAINER_MULTIHASHMAPOPTION_MULTIPLE); - shu_cls->sc = sc; -} - - -static void -merge_uri (void *cls, - const struct GNUNET_PEERSTORE_Record *record, - const char *emsg) -{ - struct GNUNET_PEERSTORE_StoreHelloContext *huc = cls; - struct GNUNET_MessageHeader *hello; - struct GNUNET_TIME_Absolute huc_hello_exp_time; - struct GNUNET_TIME_Absolute record_hello_exp_time; - - if (NULL != emsg) - { - GNUNET_log (GNUNET_ERROR_TYPE_WARNING, - "Got failure from PEERSTORE: %s\n", - emsg); - return; - } - - if (NULL == record && GNUNET_NO == huc->success) - { - huc_hello_exp_time = GNUNET_HELLO_builder_get_expiration_time (huc->hello); - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "merge_uri just store for peer %s with expiration %s\n", - GNUNET_i2s (huc->pid), - GNUNET_STRINGS_absolute_time_to_string (huc_hello_exp_time)); - store_hello (huc, huc->hello); - } - else if (GNUNET_NO == huc->success && 0 == GNUNET_memcmp (huc->pid, &record->peer)) - { - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "merge_uri record for peer %s\n", - GNUNET_i2s (&record->peer)); - hello = record->value; - if ((0 == record->value_size)) - { - GNUNET_break (0); - return; - } - - huc_hello_exp_time = GNUNET_HELLO_builder_get_expiration_time (huc->hello); - record_hello_exp_time = GNUNET_HELLO_builder_get_expiration_time (hello); - - if (GNUNET_TIME_absolute_cmp (huc_hello_exp_time, >, record_hello_exp_time)) - store_hello (huc, huc->hello); - } -} - - -struct GNUNET_PEERSTORE_StoreHelloContext * -GNUNET_PEERSTORE_hello_add (struct GNUNET_PEERSTORE_Handle *h, - const struct GNUNET_MessageHeader *msg, - GNUNET_PEERSTORE_Continuation cont, - void *cont_cls) -{ - struct GNUNET_HELLO_Builder *builder = GNUNET_HELLO_builder_from_msg (msg); - struct GNUNET_PEERSTORE_StoreHelloContext *huc; - struct GNUNET_PeerIdentity *pid; - struct GNUNET_TIME_Absolute now = GNUNET_TIME_absolute_get (); - struct GNUNET_TIME_Absolute hello_exp = - GNUNET_HELLO_builder_get_expiration_time (msg); - struct GNUNET_TIME_Absolute huc_exp; - uint16_t pid_size; - uint16_t size_msg = ntohs (msg->size); - - if (NULL == builder) - return NULL; - if (GNUNET_TIME_absolute_cmp (hello_exp, <, now)) - return NULL; - - huc = GNUNET_new (struct GNUNET_PEERSTORE_StoreHelloContext); - huc->store_context_map = GNUNET_CONTAINER_multipeermap_create (1, GNUNET_NO); - huc->h = h; - huc->cont = cont; - huc->cont_cls = cont_cls; - huc->hello = GNUNET_malloc (size_msg); - GNUNET_memcpy (huc->hello, msg, size_msg); - huc_exp = - GNUNET_HELLO_builder_get_expiration_time (huc->hello); - pid = GNUNET_HELLO_builder_get_id (builder); - pid_size = sizeof (struct GNUNET_PeerIdentity); - huc->pid = GNUNET_malloc (pid_size); - GNUNET_memcpy (huc->pid, pid, pid_size); - LOG (GNUNET_ERROR_TYPE_DEBUG, - "Adding hello for peer %s with expiration %s msg size %u\n", - GNUNET_i2s (huc->pid), - GNUNET_STRINGS_absolute_time_to_string (huc_exp), - size_msg); - huc->wc = GNUNET_PEERSTORE_watch (h, - "peerstore", - NULL, - GNUNET_PEERSTORE_HELLO_KEY, - &merge_uri, - huc); - GNUNET_HELLO_builder_free (builder); - - return huc; -} - - -static enum GNUNET_GenericReturnValue -free_store_context(void *cls, - const struct GNUNET_PeerIdentity *key, - void *value) -{ - (void) cls; - - GNUNET_PEERSTORE_store_cancel ((struct GNUNET_PEERSTORE_StoreContext *) value); - return GNUNET_YES; // FIXME why is this a map anyway -} - - -void -GNUNET_PEERSTORE_hello_add_cancel (struct - GNUNET_PEERSTORE_StoreHelloContext *huc) -{ - GNUNET_PEERSTORE_watch_cancel (huc->wc); - GNUNET_CONTAINER_multipeermap_iterate (huc->store_context_map, - free_store_context, - NULL); - GNUNET_free (huc->hello); - GNUNET_free (huc->pid); - GNUNET_free (huc); -} - - -/* end of peerstore_api.c */ diff --git a/src/peerstore/peerstore_common.c b/src/peerstore/peerstore_common.c deleted file mode 100644 index e3bb77d86..000000000 --- a/src/peerstore/peerstore_common.c +++ /dev/null @@ -1,188 +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 . - - SPDX-License-Identifier: AGPL3.0-or-later - */ -/** - * @file peerstore/peerstore_common.c - * @brief Helper peerstore functions - * @author Omar Tarabai - */ - -#include "platform.h" -#include "peerstore_common.h" - -/** - * Creates a hash of the given key combination - * - */ -void -PEERSTORE_hash_key (const char *sub_system, - const struct GNUNET_PeerIdentity *peer, - const char *key, - struct GNUNET_HashCode *ret) -{ - size_t sssize; - size_t psize; - size_t ksize; - size_t totalsize; - void *block; - void *blockptr; - - sssize = strlen (sub_system) + 1; - psize = sizeof(struct GNUNET_PeerIdentity); - ksize = strlen (key) + 1; - totalsize = sssize + psize + ksize; - block = GNUNET_malloc (totalsize); - blockptr = block; - GNUNET_memcpy (blockptr, sub_system, sssize); - blockptr += sssize; - GNUNET_memcpy (blockptr, peer, psize); - blockptr += psize; - GNUNET_memcpy (blockptr, key, ksize); - GNUNET_CRYPTO_hash (block, totalsize, ret); - GNUNET_free (block); -} - - -/** - * Creates a MQ envelope for a single record - * - * @param sub_system sub system string - * @param peer Peer identity (can be NULL) - * @param key record key string (can be NULL) - * @param value record value BLOB (can be NULL) - * @param value_size record value size in bytes (set to 0 if value is NULL) - * @param expiry time after which the record expires - * @param options options specific to the storage operation - * @param msg_type message type to be set in header - * @return pointer to record message struct - */ -struct GNUNET_MQ_Envelope * -PEERSTORE_create_record_mq_envelope (const char *sub_system, - const struct GNUNET_PeerIdentity *peer, - const char *key, - const void *value, - size_t value_size, - struct GNUNET_TIME_Absolute expiry, - enum GNUNET_PEERSTORE_StoreOption options, - uint16_t msg_type) -{ - struct StoreRecordMessage *srm; - struct GNUNET_MQ_Envelope *ev; - size_t ss_size; - size_t key_size; - size_t msg_size; - void *dummy; - - GNUNET_assert (NULL != sub_system); - ss_size = strlen (sub_system) + 1; - if (NULL == key) - key_size = 0; - else - key_size = strlen (key) + 1; - msg_size = ss_size + key_size + value_size; - ev = GNUNET_MQ_msg_extra (srm, msg_size, msg_type); - srm->key_size = htons (key_size); - srm->expiry = GNUNET_TIME_absolute_hton (expiry); - if (NULL == peer) - srm->peer_set = htons (GNUNET_NO); - else - { - srm->peer_set = htons (GNUNET_YES); - srm->peer = *peer; - } - srm->sub_system_size = htons (ss_size); - srm->value_size = htons (value_size); - srm->options = htonl (options); - dummy = &srm[1]; - GNUNET_memcpy (dummy, sub_system, ss_size); - dummy += ss_size; - GNUNET_memcpy (dummy, key, key_size); - dummy += key_size; - GNUNET_memcpy (dummy, value, value_size); - return ev; -} - - -struct GNUNET_PEERSTORE_Record * -PEERSTORE_parse_record_message (const struct StoreRecordMessage *srm) -{ - struct GNUNET_PEERSTORE_Record *record; - uint16_t req_size; - uint16_t ss_size; - uint16_t key_size; - uint16_t value_size; - char *dummy; - - req_size = ntohs (srm->header.size) - sizeof(*srm); - ss_size = ntohs (srm->sub_system_size); - key_size = ntohs (srm->key_size); - value_size = ntohs (srm->value_size); - if (ss_size + key_size + value_size != req_size) - { - GNUNET_break (0); - return NULL; - } - record = GNUNET_new (struct GNUNET_PEERSTORE_Record); - if (GNUNET_YES == ntohs (srm->peer_set)) - { - record->peer = srm->peer; - } - record->expiry = GNUNET_TIME_absolute_ntoh (srm->expiry); - dummy = (char *) &srm[1]; - if (ss_size > 0) - { - record->sub_system = GNUNET_strdup (dummy); - dummy += ss_size; - } - if (key_size > 0) - { - record->key = GNUNET_strdup (dummy); - dummy += key_size; - } - if (value_size > 0) - { - record->value = GNUNET_malloc (value_size); - GNUNET_memcpy (record->value, - dummy, - value_size); - } - record->value_size = value_size; - return record; -} - - -/** - * Free any memory allocated for this record - * - * @param record - */ -void -PEERSTORE_destroy_record (struct GNUNET_PEERSTORE_Record *record) -{ - if (NULL != record->sub_system) - GNUNET_free (record->sub_system); - if (NULL != record->key) - GNUNET_free (record->key); - if (NULL != record->value) - { - GNUNET_free (record->value); - record->value = 0; - } - GNUNET_free (record); -} diff --git a/src/peerstore/peerstore_common.h b/src/peerstore/peerstore_common.h deleted file mode 100644 index f5352f5a5..000000000 --- a/src/peerstore/peerstore_common.h +++ /dev/null @@ -1,82 +0,0 @@ -/* - This file is part of GNUnet - Copyright (C) 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 . - - SPDX-License-Identifier: AGPL3.0-or-later - */ - -/** - * @file peerstore/peerstore_common.h - * @brief Helper peerstore functions - * @author Omar Tarabai - */ -#include "platform.h" -#include "peerstore.h" - -/** - * Creates a hash of the given key combination - * - */ -void -PEERSTORE_hash_key (const char *sub_system, - const struct GNUNET_PeerIdentity *peer, - const char *key, - struct GNUNET_HashCode *ret); - - -/** - * Creates a MQ envelope for a single record - * - * @param sub_system sub system string - * @param peer Peer identity (can be NULL) - * @param key record key string (can be NULL) - * @param value record value BLOB (can be NULL) - * @param value_size record value size in bytes (set to 0 if value is NULL) - * @param expiry time after which the record expires - * @param options options specific to the storage operation - * @param msg_type message type to be set in header - * @return pointer to record message struct - */ -struct GNUNET_MQ_Envelope * -PEERSTORE_create_record_mq_envelope (const char *sub_system, - const struct GNUNET_PeerIdentity *peer, - const char *key, - const void *value, - size_t value_size, - struct GNUNET_TIME_Absolute expiry, - enum GNUNET_PEERSTORE_StoreOption options, - uint16_t msg_type); - - -/** - * Parses a message carrying a record - * - * @param srm the actual message - * @return Pointer to record or NULL on error - */ -struct GNUNET_PEERSTORE_Record * -PEERSTORE_parse_record_message (const struct StoreRecordMessage *srm); - - -/** - * Free any memory allocated for this record - * - * @param record - */ -void -PEERSTORE_destroy_record (struct GNUNET_PEERSTORE_Record *record); - -/* end of peerstore_common.h */ diff --git a/src/peerstore/perf_peerstore_store.c b/src/peerstore/perf_peerstore_store.c deleted file mode 100644 index 24c7e4f01..000000000 --- a/src/peerstore/perf_peerstore_store.c +++ /dev/null @@ -1,109 +0,0 @@ -/* - This file is part of GNUnet. - Copyright (C) - - 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 . - - SPDX-License-Identifier: AGPL3.0-or-later - */ -/** - * @file peerstore/perf_peerstore_store.c - * @brief performance test for peerstore store operation - */ -#include "platform.h" -#include "gnunet_util_lib.h" -#include "gnunet_testing_lib.h" -#include "gnunet_peerstore_service.h" - -#define STORES 10000 - -static int ok = 1; - -static struct GNUNET_PEERSTORE_Handle *h; - -static char *ss = "test_peerstore_stress"; -static struct GNUNET_PeerIdentity p; -static char *k = "test_peerstore_stress_key"; -static char *v = "test_peerstore_stress_val"; - -static int count = 0; - -static void -disconnect () -{ - if (NULL != h) - GNUNET_PEERSTORE_disconnect (h, GNUNET_YES); - GNUNET_SCHEDULER_shutdown (); -} - - -static void -store () -{ - GNUNET_PEERSTORE_store (h, ss, &p, k, v, strlen (v) + 1, - GNUNET_TIME_UNIT_FOREVER_ABS, - (count == - 0) ? GNUNET_PEERSTORE_STOREOPTION_REPLACE : - GNUNET_PEERSTORE_STOREOPTION_MULTIPLE, NULL, NULL); - count++; -} - - -static void -watch_cb (void *cls, const struct GNUNET_PEERSTORE_Record *record, - const char *emsg) -{ - GNUNET_assert (NULL == emsg); - if (STORES == count) - { - ok = 0; - disconnect (); - } - else - store (); -} - - -static void -run (void *cls, const struct GNUNET_CONFIGURATION_Handle *cfg, - struct GNUNET_TESTING_Peer *peer) -{ - memset (&p, 5, sizeof(p)); - h = GNUNET_PEERSTORE_connect (cfg); - GNUNET_assert (NULL != h); - GNUNET_PEERSTORE_watch (h, ss, &p, k, &watch_cb, NULL); - store (); -} - - -int -main (int argc, char *argv[]) -{ - struct GNUNET_TIME_Absolute start; - struct GNUNET_TIME_Relative diff; - - start = GNUNET_TIME_absolute_get (); - if (0 != - GNUNET_TESTING_service_run ("perf-peerstore-store", "peerstore", - "test_peerstore_api_data.conf", &run, NULL)) - return 1; - diff = GNUNET_TIME_absolute_get_duration (start); - fprintf (stderr, "Stored and retrieved %d records in %s (%s).\n", STORES, - GNUNET_STRINGS_relative_time_to_string (diff, GNUNET_YES), - GNUNET_STRINGS_relative_time_to_string (diff, GNUNET_NO)); - return ok; -} - - -/* end of perf_peerstore_store.c */ diff --git a/src/peerstore/plugin_peerstore_flat.c b/src/peerstore/plugin_peerstore_flat.c deleted file mode 100644 index cc5b8b76b..000000000 --- a/src/peerstore/plugin_peerstore_flat.c +++ /dev/null @@ -1,606 +0,0 @@ -/* - * This file is part of GNUnet - * Copyright (C) 2015 Christian Grothoff (and other contributing authors) - * - * 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 . - - SPDX-License-Identifier: AGPL3.0-or-later - */ - -/** - * @file peerstore/plugin_peerstore_flat.c - * @brief flat file-based peerstore backend - * @author Martin Schanzenbach - */ - -#include "platform.h" -#include "gnunet_peerstore_plugin.h" -#include "gnunet_peerstore_service.h" -#include "peerstore.h" - -/** - * Context for all functions in this plugin. - */ -struct Plugin -{ - /** - * Configuration handle - */ - const struct GNUNET_CONFIGURATION_Handle *cfg; - - /** - * HashMap - */ - struct GNUNET_CONTAINER_MultiHashMap *hm; - - /** - * Iterator - */ - GNUNET_PEERSTORE_Processor iter; - - /** - * Iterator cls - */ - void *iter_cls; - - /** - * iterator key - */ - const char *iter_key; - - /** - * Iterator peer - */ - const struct GNUNET_PeerIdentity *iter_peer; - - /** - * Iterator subsystem - */ - const char *iter_sub_system; - - /** - * Iterator time - */ - struct GNUNET_TIME_Absolute iter_now; - - /** - * Deleted entries - */ - uint64_t deleted_entries; - - /** - * Expired entries - */ - uint64_t exp_changes; - - /** - * Database filename. - */ - char *fn; - - /** - * Result found bool - */ - int iter_result_found; -}; - - -static int -delete_entries (void *cls, - const struct GNUNET_HashCode *key, - void *value) -{ - struct Plugin *plugin = cls; - struct GNUNET_PEERSTORE_Record *entry = value; - - if (0 != strcmp (plugin->iter_key, entry->key)) - return GNUNET_YES; - if (0 != memcmp (plugin->iter_peer, - &entry->peer, - sizeof(struct GNUNET_PeerIdentity))) - return GNUNET_YES; - if (0 != strcmp (plugin->iter_sub_system, entry->sub_system)) - return GNUNET_YES; - - GNUNET_CONTAINER_multihashmap_remove (plugin->hm, key, value); - plugin->deleted_entries++; - return GNUNET_YES; -} - - -/** - * Delete records with the given key - * - * @param cls closure (internal context for the plugin) - * @param sub_system name of sub system - * @param peer Peer identity (can be NULL) - * @param key entry key string (can be NULL) - * @return number of deleted records - */ -static int -peerstore_flat_delete_records (void *cls, const char *sub_system, - const struct GNUNET_PeerIdentity *peer, - const char *key) -{ - struct Plugin *plugin = cls; - - plugin->iter_sub_system = sub_system; - plugin->iter_peer = peer; - plugin->iter_key = key; - plugin->deleted_entries = 0; - - GNUNET_CONTAINER_multihashmap_iterate (plugin->hm, - &delete_entries, - plugin); - return plugin->deleted_entries; -} - - -static int -expire_entries (void *cls, - const struct GNUNET_HashCode *key, - void *value) -{ - struct Plugin *plugin = cls; - struct GNUNET_PEERSTORE_Record *entry = value; - - if (entry->expiry.abs_value_us < plugin->iter_now.abs_value_us) - { - GNUNET_CONTAINER_multihashmap_remove (plugin->hm, key, value); - plugin->exp_changes++; - } - return GNUNET_YES; -} - - -/** - * Delete expired records (expiry < now) - * - * @param cls closure (internal context for the plugin) - * @param now time to use as reference - * @param cont continuation called with the number of records expired - * @param cont_cls continuation closure - * @return #GNUNET_OK on success, #GNUNET_SYSERR on error and cont is not - * called - */ -static int -peerstore_flat_expire_records (void *cls, struct GNUNET_TIME_Absolute now, - GNUNET_PEERSTORE_Continuation cont, - void *cont_cls) -{ - struct Plugin *plugin = cls; - - plugin->exp_changes = 0; - plugin->iter_now = now; - - GNUNET_CONTAINER_multihashmap_iterate (plugin->hm, - &expire_entries, - plugin); - if (NULL != cont) - { - cont (cont_cls, plugin->exp_changes); - } - return GNUNET_OK; -} - - -static int -iterate_entries (void *cls, - const struct GNUNET_HashCode *key, - void *value) -{ - struct Plugin *plugin = cls; - struct GNUNET_PEERSTORE_Record *entry = value; - - if ((NULL != plugin->iter_peer) && - (0 != memcmp (plugin->iter_peer, - &entry->peer, - sizeof(struct GNUNET_PeerIdentity)))) - { - return GNUNET_YES; - } - if ((NULL != plugin->iter_key) && - (0 != strcmp (plugin->iter_key, - entry->key))) - { - return GNUNET_YES; - } - if (NULL != plugin->iter) - plugin->iter (plugin->iter_cls, entry, NULL); - plugin->iter_result_found = GNUNET_YES; - return GNUNET_YES; -} - - -/** - * Iterate over the records given an optional peer id - * and/or key. - * - * @param cls closure (internal context for the plugin) - * @param sub_system name of sub system - * @param peer Peer identity (can be NULL) - * @param key entry key string (can be NULL) - * @param iter function to call asynchronously with the results, terminated - * by a NULL result - * @param iter_cls closure for @a iter - * @return #GNUNET_OK on success, #GNUNET_SYSERR on error and iter is not - * called - */ -static int -peerstore_flat_iterate_records (void *cls, const char *sub_system, - const struct GNUNET_PeerIdentity *peer, - const char *key, - GNUNET_PEERSTORE_Processor iter, - void *iter_cls) -{ - struct Plugin *plugin = cls; - - plugin->iter = iter; - plugin->iter_cls = iter_cls; - plugin->iter_peer = peer; - plugin->iter_sub_system = sub_system; - plugin->iter_key = key; - - GNUNET_CONTAINER_multihashmap_iterate (plugin->hm, - &iterate_entries, - plugin); - if (NULL != iter) - iter (iter_cls, NULL, NULL); - return GNUNET_OK; -} - - -/** - * Store a record in the peerstore. - * Key is the combination of sub system and peer identity. - * One key can store multiple values. - * - * @param cls closure (internal context for the plugin) - * @param sub_system name of the GNUnet sub system responsible - * @param peer peer identity - * @param key record key string - * @param value value to be stored - * @param size size of value to be stored - * @param expiry absolute time after which the record is (possibly) deleted - * @param options options related to the store operation - * @param cont continuation called when record is stored - * @param cont_cls continuation closure - * @return #GNUNET_OK on success, else #GNUNET_SYSERR and cont is not called - */ -static int -peerstore_flat_store_record (void *cls, const char *sub_system, - const struct GNUNET_PeerIdentity *peer, - const char *key, const void *value, size_t size, - struct GNUNET_TIME_Absolute expiry, - enum GNUNET_PEERSTORE_StoreOption options, - GNUNET_PEERSTORE_Continuation cont, - void *cont_cls) -{ - struct Plugin *plugin = cls; - struct GNUNET_HashCode hkey; - struct GNUNET_PEERSTORE_Record *entry; - const char *peer_id; - - - entry = GNUNET_new (struct GNUNET_PEERSTORE_Record); - entry->sub_system = GNUNET_strdup (sub_system); - entry->key = GNUNET_strdup (key); - entry->value = GNUNET_malloc (size); - GNUNET_memcpy (entry->value, value, size); - entry->value_size = size; - entry->peer = *peer; - entry->expiry = expiry; - - peer_id = GNUNET_i2s (peer); - GNUNET_CRYPTO_hash (peer_id, - strlen (peer_id), - &hkey); - - if (GNUNET_PEERSTORE_STOREOPTION_REPLACE == options) - { - peerstore_flat_delete_records (cls, sub_system, peer, key); - } - - GNUNET_CONTAINER_multihashmap_put (plugin->hm, - &hkey, - entry, - GNUNET_CONTAINER_MULTIHASHMAPOPTION_MULTIPLE); - if (NULL != cont) - { - cont (cont_cls, GNUNET_OK); - } - return GNUNET_OK; -} - - -/** - * Initialize the database connections and associated - * data structures (create tables and indices - * as needed as well). - * - * @param plugin the plugin context (state for this module) - * @return GNUNET_OK on success - */ -static int -database_setup (struct Plugin *plugin) -{ - char *afsdir; - char *key; - char *sub_system; - const char *peer_id; - char *peer; - char *value; - char *expiry; - struct GNUNET_DISK_FileHandle *fh; - struct GNUNET_PEERSTORE_Record *entry; - struct GNUNET_HashCode hkey; - uint64_t size; - char *buffer; - char *line; - - if (GNUNET_OK != - GNUNET_CONFIGURATION_get_value_filename (plugin->cfg, "peerstore-flat", - "FILENAME", &afsdir)) - { - GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR, "peerstore-flat", - "FILENAME"); - return GNUNET_SYSERR; - } - if (GNUNET_OK != GNUNET_DISK_file_test (afsdir)) - { - if (GNUNET_OK != GNUNET_DISK_directory_create_for_file (afsdir)) - { - GNUNET_break (0); - GNUNET_free (afsdir); - return GNUNET_SYSERR; - } - } - /* afsdir should be UTF-8-encoded. If it isn't, it's a bug */ - plugin->fn = afsdir; - - fh = GNUNET_DISK_file_open (afsdir, - GNUNET_DISK_OPEN_CREATE - | GNUNET_DISK_OPEN_READWRITE, - GNUNET_DISK_PERM_USER_WRITE - | GNUNET_DISK_PERM_USER_READ); - if (NULL == fh) - { - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - _ ("Unable to initialize file: %s.\n"), - afsdir); - return GNUNET_SYSERR; - } - - /* Load data from file into hashmap */ - plugin->hm = GNUNET_CONTAINER_multihashmap_create (10, - GNUNET_NO); - - if (GNUNET_SYSERR == GNUNET_DISK_file_size (afsdir, - &size, - GNUNET_YES, - GNUNET_YES)) - { - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - _ ("Unable to get filesize: %s.\n"), - afsdir); - return GNUNET_SYSERR; - } - - buffer = GNUNET_malloc (size + 1); - - if (GNUNET_SYSERR == GNUNET_DISK_file_read (fh, - buffer, - size)) - { - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - _ ("Unable to read file: %s.\n"), - afsdir); - GNUNET_DISK_file_close (fh); - GNUNET_free (buffer); - return GNUNET_SYSERR; - } - - buffer[size] = '\0'; - GNUNET_DISK_file_close (fh); - if (0 < size) - { - line = strtok (buffer, "\n"); - while (line != NULL) - { - sub_system = strtok (line, ","); - if (NULL == sub_system) - break; - peer = strtok (NULL, ","); - if (NULL == peer) - break; - key = strtok (NULL, ","); - if (NULL == key) - break; - value = strtok (NULL, ","); - if (NULL == value) - break; - expiry = strtok (NULL, ","); - if (NULL == expiry) - break; - entry = GNUNET_new (struct GNUNET_PEERSTORE_Record); - entry->sub_system = GNUNET_strdup (sub_system); - entry->key = GNUNET_strdup (key); - { - size_t s; - char *o; - - o = NULL; - s = GNUNET_STRINGS_base64_decode (peer, - strlen (peer), - (void **) &o); - if (sizeof(struct GNUNET_PeerIdentity) == s) - GNUNET_memcpy (&entry->peer, - o, - s); - else - GNUNET_break (0); - GNUNET_free (o); - } - entry->value_size = GNUNET_STRINGS_base64_decode (value, - strlen (value), - (void **) &entry->value); - if (GNUNET_SYSERR == - GNUNET_STRINGS_fancy_time_to_absolute (expiry, - &entry->expiry)) - { - GNUNET_free (entry->sub_system); - GNUNET_free (entry->key); - GNUNET_free (entry); - break; - } - peer_id = GNUNET_i2s (&entry->peer); - GNUNET_CRYPTO_hash (peer_id, - strlen (peer_id), - &hkey); - - GNUNET_assert (GNUNET_OK == GNUNET_CONTAINER_multihashmap_put (plugin->hm, - &hkey, - entry, - GNUNET_CONTAINER_MULTIHASHMAPOPTION_MULTIPLE)); - } - } - GNUNET_free (buffer); - return GNUNET_OK; -} - - -static int -store_and_free_entries (void *cls, - const struct GNUNET_HashCode *key, - void *value) -{ - struct GNUNET_DISK_FileHandle *fh = cls; - struct GNUNET_PEERSTORE_Record *entry = value; - char *line; - char *peer; - const char *expiry; - char *val; - - GNUNET_STRINGS_base64_encode (entry->value, - entry->value_size, - &val); - expiry = GNUNET_STRINGS_absolute_time_to_string (entry->expiry); - GNUNET_STRINGS_base64_encode ((char *) &entry->peer, - sizeof(struct GNUNET_PeerIdentity), - &peer); - GNUNET_asprintf (&line, - "%s,%s,%s,%s,%s", - entry->sub_system, - peer, - entry->key, - val, - expiry); - GNUNET_free (val); - GNUNET_free (peer); - GNUNET_DISK_file_write (fh, - line, - strlen (line)); - GNUNET_free (entry->sub_system); - GNUNET_free (entry->key); - GNUNET_free (entry->value); - GNUNET_free (entry); - GNUNET_free (line); - return GNUNET_YES; -} - - -/** - * Shutdown database connection and associate data - * structures. - * @param plugin the plugin context (state for this module) - */ -static void -database_shutdown (struct Plugin *plugin) -{ - struct GNUNET_DISK_FileHandle *fh; - - fh = GNUNET_DISK_file_open (plugin->fn, - GNUNET_DISK_OPEN_CREATE - | GNUNET_DISK_OPEN_TRUNCATE - | GNUNET_DISK_OPEN_READWRITE, - GNUNET_DISK_PERM_USER_WRITE - | GNUNET_DISK_PERM_USER_READ); - if (NULL == fh) - { - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - _ ("Unable to initialize file: %s.\n"), - plugin->fn); - return; - } - GNUNET_CONTAINER_multihashmap_iterate (plugin->hm, - &store_and_free_entries, - fh); - GNUNET_CONTAINER_multihashmap_destroy (plugin->hm); - GNUNET_DISK_file_close (fh); -} - - -/** - * Entry point for the plugin. - * - * @param cls The struct GNUNET_CONFIGURATION_Handle. - * @return NULL on error, otherwise the plugin context - */ -void * -libgnunet_plugin_peerstore_flat_init (void *cls) -{ - static struct Plugin plugin; - const struct GNUNET_CONFIGURATION_Handle *cfg = cls; - struct GNUNET_PEERSTORE_PluginFunctions *api; - - if (NULL != plugin.cfg) - return NULL; /* can only initialize once! */ - memset (&plugin, 0, sizeof(struct Plugin)); - plugin.cfg = cfg; - if (GNUNET_OK != database_setup (&plugin)) - { - database_shutdown (&plugin); - return NULL; - } - api = GNUNET_new (struct GNUNET_PEERSTORE_PluginFunctions); - api->cls = &plugin; - api->store_record = &peerstore_flat_store_record; - api->iterate_records = &peerstore_flat_iterate_records; - api->expire_records = &peerstore_flat_expire_records; - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Flat plugin is running\n"); - return api; -} - - -/** - * Exit point from the plugin. - * - * @param cls The plugin context (as returned by "init") - * @return Always NULL - */ -void * -libgnunet_plugin_peerstore_flat_done (void *cls) -{ - struct GNUNET_PEERSTORE_PluginFunctions *api = cls; - struct Plugin *plugin = api->cls; - - database_shutdown (plugin); - plugin->cfg = NULL; - GNUNET_free (api); - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Flat plugin is finished\n"); - return NULL; -} - - -/* end of plugin_peerstore_sqlite.c */ diff --git a/src/peerstore/plugin_peerstore_sqlite.c b/src/peerstore/plugin_peerstore_sqlite.c deleted file mode 100644 index ad69efdf4..000000000 --- a/src/peerstore/plugin_peerstore_sqlite.c +++ /dev/null @@ -1,702 +0,0 @@ -/* - * This file is part of GNUnet - * Copyright (C) 2013, 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 . - - SPDX-License-Identifier: AGPL3.0-or-later - */ - -/** - * @file peerstore/plugin_peerstore_sqlite.c - * @brief sqlite-based peerstore backend - * @author Omar Tarabai - * @author Christian Grothoff - */ - -#include "platform.h" -#include "gnunet_peerstore_plugin.h" -#include "gnunet_peerstore_service.h" -#include "gnunet_sq_lib.h" -#include "peerstore.h" -#include - -/** - * After how many ms "busy" should a DB operation fail for good? A - * low value makes sure that we are more responsive to requests - * (especially PUTs). A high value guarantees a higher success rate - * (SELECTs in iterate can take several seconds despite LIMIT=1). - * - * The default value of 1s should ensure that users do not experience - * huge latencies while at the same time allowing operations to - * succeed with reasonable probability. - */ -#define BUSY_TIMEOUT_MS 1000 - -/** - * Log an error message at log-level 'level' that indicates - * a failure of the command 'cmd' on file 'filename' - * with the message given by strerror(errno). - */ -#define LOG_SQLITE(db, level, cmd) do { GNUNET_log_from (level, \ - "peerstore-sqlite", _ ( \ - "`%s' failed at %s:%d with error: %s\n"), \ - cmd, \ - __FILE__, __LINE__, \ - sqlite3_errmsg ( \ - db->dbh)); \ -} while (0) - -#define LOG(kind, ...) GNUNET_log_from (kind, "peerstore-sqlite", __VA_ARGS__) - -/** - * Context for all functions in this plugin. - */ -struct Plugin -{ - /** - * Configuration handle - */ - const struct GNUNET_CONFIGURATION_Handle *cfg; - - /** - * Database filename. - */ - char *fn; - - /** - * Native SQLite database handle. - */ - sqlite3 *dbh; - - /** - * Precompiled SQL for inserting into peerstoredata - */ - sqlite3_stmt *insert_peerstoredata; - - /** - * Precompiled SQL for selecting from peerstoredata - */ - sqlite3_stmt *select_peerstoredata; - - /** - * Precompiled SQL for selecting from peerstoredata - */ - sqlite3_stmt *select_peerstoredata_by_pid; - - /** - * Precompiled SQL for selecting from peerstoredata - */ - sqlite3_stmt *select_peerstoredata_by_key; - - /** - * Precompiled SQL for selecting from peerstoredata - */ - sqlite3_stmt *select_peerstoredata_by_all; - - /** - * Precompiled SQL for deleting expired - * records from peerstoredata - */ - sqlite3_stmt *expire_peerstoredata; - - /** - * Precompiled SQL for deleting records - * with given key - */ - sqlite3_stmt *delete_peerstoredata; -}; - - -/** - * Delete records with the given key - * - * @param cls closure (internal context for the plugin) - * @param sub_system name of sub system - * @param peer Peer identity (can be NULL) - * @param key entry key string (can be NULL) - * @return number of deleted records, #GNUNE_SYSERR on error - */ -static int -peerstore_sqlite_delete_records (void *cls, - const char *sub_system, - const struct GNUNET_PeerIdentity *peer, - const char *key) -{ - struct Plugin *plugin = cls; - sqlite3_stmt *stmt = plugin->delete_peerstoredata; - struct GNUNET_SQ_QueryParam params[] = { - GNUNET_SQ_query_param_string (sub_system), - GNUNET_SQ_query_param_auto_from_type (peer), - GNUNET_SQ_query_param_string (key), - GNUNET_SQ_query_param_end - }; - int ret; - - if (GNUNET_OK != - GNUNET_SQ_bind (stmt, - params)) - { - LOG_SQLITE (plugin, - GNUNET_ERROR_TYPE_ERROR | GNUNET_ERROR_TYPE_BULK, - "sqlite3_bind"); - GNUNET_SQ_reset (plugin->dbh, - stmt); - return GNUNET_SYSERR; - } - if (SQLITE_DONE != - sqlite3_step (stmt)) - { - LOG_SQLITE (plugin, - GNUNET_ERROR_TYPE_ERROR | GNUNET_ERROR_TYPE_BULK, - "sqlite3_step"); - ret = GNUNET_SYSERR; - } - else - { - ret = sqlite3_changes (plugin->dbh); - } - GNUNET_SQ_reset (plugin->dbh, - stmt); - return ret; -} - - -/** - * Delete expired records (expiry < now) - * - * @param cls closure (internal context for the plugin) - * @param now time to use as reference - * @param cont continuation called with the number of records expired - * @param cont_cls continuation closure - * @return #GNUNET_OK on success, #GNUNET_SYSERR on error and cont is not - * called - */ -static int -peerstore_sqlite_expire_records (void *cls, struct GNUNET_TIME_Absolute now, - GNUNET_PEERSTORE_Continuation cont, - void *cont_cls) -{ - struct Plugin *plugin = cls; - sqlite3_stmt *stmt = plugin->expire_peerstoredata; - struct GNUNET_SQ_QueryParam params[] = { - GNUNET_SQ_query_param_absolute_time (&now), - GNUNET_SQ_query_param_end - }; - - if (GNUNET_OK != - GNUNET_SQ_bind (stmt, - params)) - { - LOG_SQLITE (plugin, - GNUNET_ERROR_TYPE_ERROR | GNUNET_ERROR_TYPE_BULK, - "sqlite3_bind"); - GNUNET_SQ_reset (plugin->dbh, - stmt); - return GNUNET_SYSERR; - } - if (SQLITE_DONE != sqlite3_step (stmt)) - { - LOG_SQLITE (plugin, - GNUNET_ERROR_TYPE_ERROR | GNUNET_ERROR_TYPE_BULK, - "sqlite3_step"); - GNUNET_SQ_reset (plugin->dbh, - stmt); - return GNUNET_SYSERR; - } - if (NULL != cont) - cont (cont_cls, - sqlite3_changes (plugin->dbh)); - GNUNET_SQ_reset (plugin->dbh, - stmt); - return GNUNET_OK; -} - - -/** - * Iterate over the records given an optional peer id - * and/or key. - * - * @param cls closure (internal context for the plugin) - * @param sub_system name of sub system - * @param peer Peer identity (can be NULL) - * @param key entry key string (can be NULL) - * @param iter function to call asynchronously with the results, terminated - * by a NULL result - * @param iter_cls closure for @a iter - * @return #GNUNET_OK on success, #GNUNET_SYSERR on error and iter is not - * called - */ -static int -peerstore_sqlite_iterate_records (void *cls, - const char *sub_system, - const struct GNUNET_PeerIdentity *peer, - const char *key, - GNUNET_PEERSTORE_Processor iter, - void *iter_cls) -{ - struct Plugin *plugin = cls; - sqlite3_stmt *stmt; - int err = 0; - int sret; - struct GNUNET_PEERSTORE_Record rec; - - LOG (GNUNET_ERROR_TYPE_DEBUG, - "Executing iterate request on sqlite db.\n"); - if (NULL == peer) - { - if (NULL == key) - { - struct GNUNET_SQ_QueryParam params[] = { - GNUNET_SQ_query_param_string (sub_system), - GNUNET_SQ_query_param_end - }; - - stmt = plugin->select_peerstoredata; - err = GNUNET_SQ_bind (stmt, - params); - } - else - { - struct GNUNET_SQ_QueryParam params[] = { - GNUNET_SQ_query_param_string (sub_system), - GNUNET_SQ_query_param_string (key), - GNUNET_SQ_query_param_end - }; - - stmt = plugin->select_peerstoredata_by_key; - err = GNUNET_SQ_bind (stmt, - params); - } - } - else - { - if (NULL == key) - { - struct GNUNET_SQ_QueryParam params[] = { - GNUNET_SQ_query_param_string (sub_system), - GNUNET_SQ_query_param_auto_from_type (peer), - GNUNET_SQ_query_param_end - }; - - stmt = plugin->select_peerstoredata_by_pid; - err = GNUNET_SQ_bind (stmt, - params); - } - else - { - struct GNUNET_SQ_QueryParam params[] = { - GNUNET_SQ_query_param_string (sub_system), - GNUNET_SQ_query_param_auto_from_type (peer), - GNUNET_SQ_query_param_string (key), - GNUNET_SQ_query_param_end - }; - - stmt = plugin->select_peerstoredata_by_all; - err = GNUNET_SQ_bind (stmt, - params); - } - } - - if (GNUNET_OK != err) - { - LOG_SQLITE (plugin, - GNUNET_ERROR_TYPE_ERROR | GNUNET_ERROR_TYPE_BULK, - "sqlite3_bind_XXXX"); - GNUNET_SQ_reset (plugin->dbh, - stmt); - return GNUNET_SYSERR; - } - - err = 0; - while (SQLITE_ROW == (sret = sqlite3_step (stmt))) - { - LOG (GNUNET_ERROR_TYPE_DEBUG, - "Returning a matched record.\n"); - struct GNUNET_SQ_ResultSpec rs[] = { - GNUNET_SQ_result_spec_string (&rec.sub_system), - GNUNET_SQ_result_spec_auto_from_type (&rec.peer), - GNUNET_SQ_result_spec_string (&rec.key), - GNUNET_SQ_result_spec_variable_size (&rec.value, &rec.value_size), - GNUNET_SQ_result_spec_absolute_time (&rec.expiry), - GNUNET_SQ_result_spec_end - }; - - if (GNUNET_OK != - GNUNET_SQ_extract_result (stmt, - rs)) - { - GNUNET_break (0); - break; - } - if (NULL != iter) - iter (iter_cls, - &rec, - NULL); - GNUNET_SQ_cleanup_result (rs); - } - if (SQLITE_DONE != sret) - { - LOG_SQLITE (plugin, - GNUNET_ERROR_TYPE_ERROR, - "sqlite_step"); - err = 1; - } - GNUNET_SQ_reset (plugin->dbh, - stmt); - if (NULL != iter) - iter (iter_cls, - NULL, - err ? "sqlite error" : NULL); - return GNUNET_OK; -} - - -/** - * Store a record in the peerstore. - * Key is the combination of sub system and peer identity. - * One key can store multiple values. - * - * @param cls closure (internal context for the plugin) - * @param sub_system name of the GNUnet sub system responsible - * @param peer peer identity - * @param key record key string - * @param value value to be stored - * @param size size of value to be stored - * @param expiry absolute time after which the record is (possibly) deleted - * @param options options related to the store operation - * @param cont continuation called when record is stored - * @param cont_cls continuation closure - * @return #GNUNET_OK on success, else #GNUNET_SYSERR and cont is not called - */ -static int -peerstore_sqlite_store_record (void *cls, - const char *sub_system, - const struct GNUNET_PeerIdentity *peer, - const char *key, - const void *value, - size_t size, - struct GNUNET_TIME_Absolute expiry, - enum GNUNET_PEERSTORE_StoreOption options, - GNUNET_PEERSTORE_Continuation cont, - void *cont_cls) -{ - struct Plugin *plugin = cls; - sqlite3_stmt *stmt = plugin->insert_peerstoredata; - struct GNUNET_SQ_QueryParam params[] = { - GNUNET_SQ_query_param_string (sub_system), - GNUNET_SQ_query_param_auto_from_type (peer), - GNUNET_SQ_query_param_string (key), - GNUNET_SQ_query_param_fixed_size (value, size), - GNUNET_SQ_query_param_absolute_time (&expiry), - GNUNET_SQ_query_param_end - }; - - if (GNUNET_PEERSTORE_STOREOPTION_REPLACE == options) - { - peerstore_sqlite_delete_records (cls, - sub_system, - peer, - key); - } - if (GNUNET_OK != - GNUNET_SQ_bind (stmt, - params)) - LOG_SQLITE (plugin, - GNUNET_ERROR_TYPE_ERROR | GNUNET_ERROR_TYPE_BULK, - "sqlite3_bind"); - else if (SQLITE_DONE != sqlite3_step (stmt)) - { - LOG_SQLITE (plugin, - GNUNET_ERROR_TYPE_ERROR | GNUNET_ERROR_TYPE_BULK, - "sqlite3_step"); - } - GNUNET_SQ_reset (plugin->dbh, - stmt); - if (NULL != cont) - cont (cont_cls, - GNUNET_OK); - return GNUNET_OK; -} - - -/** - * @brief Prepare a SQL statement - * - * @param dbh handle to the database - * @param sql SQL statement, UTF-8 encoded - * @return 0 on success - */ -static int -sql_exec (sqlite3 *dbh, - const char *sql) -{ - int result; - - result = sqlite3_exec (dbh, - sql, - NULL, - NULL, - NULL); - LOG (GNUNET_ERROR_TYPE_DEBUG, - "Executed `%s' / %d\n", - sql, - result); - if (SQLITE_OK != result) - LOG (GNUNET_ERROR_TYPE_ERROR, - _ ("Error executing SQL query: %s\n %s\n"), - sqlite3_errmsg (dbh), - sql); - return result; -} - - -/** - * @brief Prepare a SQL statement - * - * @param dbh handle to the database - * @param sql SQL statement, UTF-8 encoded - * @param stmt set to the prepared statement - * @return 0 on success - */ -static int -sql_prepare (sqlite3 *dbh, - const char *sql, - sqlite3_stmt **stmt) -{ - char *tail; - int result; - - result = sqlite3_prepare_v2 (dbh, - sql, - strlen (sql), - stmt, - (const char **) &tail); - LOG (GNUNET_ERROR_TYPE_DEBUG, - "Prepared `%s' / %p: %d\n", - sql, - *stmt, - result); - if (SQLITE_OK != result) - LOG (GNUNET_ERROR_TYPE_ERROR, - _ ("Error preparing SQL query: %s\n %s\n"), - sqlite3_errmsg (dbh), - sql); - return result; -} - - -/** - * Initialize the database connections and associated - * data structures (create tables and indices - * as needed as well). - * - * @param plugin the plugin context (state for this module) - * @return #GNUNET_OK on success - */ -static int -database_setup (struct Plugin *plugin) -{ - char *filename; - - if (GNUNET_OK != - GNUNET_CONFIGURATION_get_value_filename (plugin->cfg, - "peerstore-sqlite", - "FILENAME", - &filename)) - { - GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR, - "peerstore-sqlite", - "FILENAME"); - return GNUNET_SYSERR; - } - if (GNUNET_OK != GNUNET_DISK_file_test (filename)) - { - if (GNUNET_OK != GNUNET_DISK_directory_create_for_file (filename)) - { - GNUNET_break (0); - GNUNET_free (filename); - return GNUNET_SYSERR; - } - } - /* filename should be UTF-8-encoded. If it isn't, it's a bug */ - plugin->fn = filename; - /* Open database and precompile statements */ - if (SQLITE_OK != sqlite3_open (plugin->fn, - &plugin->dbh)) - { - LOG (GNUNET_ERROR_TYPE_ERROR, - _ ("Unable to initialize SQLite: %s.\n"), - sqlite3_errmsg (plugin->dbh)); - return GNUNET_SYSERR; - } - sql_exec (plugin->dbh, - "PRAGMA temp_store=MEMORY"); - sql_exec (plugin->dbh, - "PRAGMA synchronous=OFF"); - sql_exec (plugin->dbh, - "PRAGMA legacy_file_format=OFF"); - sql_exec (plugin->dbh, - "PRAGMA auto_vacuum=INCREMENTAL"); - sql_exec (plugin->dbh, - "PRAGMA encoding=\"UTF-8\""); - sql_exec (plugin->dbh, - "PRAGMA page_size=4096"); - sqlite3_busy_timeout (plugin->dbh, - BUSY_TIMEOUT_MS); - /* Create tables */ - sql_exec (plugin->dbh, - "CREATE TABLE IF NOT EXISTS peerstoredata (\n" - " sub_system TEXT NOT NULL,\n" - " peer_id BLOB NOT NULL,\n" - " key TEXT NOT NULL,\n" - " value BLOB NULL,\n" - " expiry INT8 NOT NULL" ");"); - /* Create Indices */ - if (SQLITE_OK != - sqlite3_exec (plugin->dbh, - "CREATE INDEX IF NOT EXISTS peerstoredata_key_index ON peerstoredata (sub_system, peer_id, key)", - NULL, - NULL, - NULL)) - { - LOG (GNUNET_ERROR_TYPE_ERROR, - _ ("Unable to create indices: %s.\n"), - sqlite3_errmsg (plugin->dbh)); - return GNUNET_SYSERR; - } - /* Prepare statements */ - - sql_prepare (plugin->dbh, - "INSERT INTO peerstoredata (sub_system, peer_id, key, value, expiry)" - " VALUES (?,?,?,?,?);", - &plugin->insert_peerstoredata); - sql_prepare (plugin->dbh, - "SELECT sub_system,peer_id,key,value,expiry FROM peerstoredata" - " WHERE sub_system = ?", - &plugin->select_peerstoredata); - sql_prepare (plugin->dbh, - "SELECT sub_system,peer_id,key,value,expiry FROM peerstoredata" - " WHERE sub_system = ?" - " AND peer_id = ?", - &plugin->select_peerstoredata_by_pid); - sql_prepare (plugin->dbh, - "SELECT sub_system,peer_id,key,value,expiry FROM peerstoredata" - " WHERE sub_system = ?" - " AND key = ?", - &plugin->select_peerstoredata_by_key); - sql_prepare (plugin->dbh, - "SELECT sub_system,peer_id,key,value,expiry FROM peerstoredata" - " WHERE sub_system = ?" - " AND peer_id = ?" " AND key = ?", - &plugin->select_peerstoredata_by_all); - sql_prepare (plugin->dbh, - "DELETE FROM peerstoredata" - " WHERE expiry < ?", - &plugin->expire_peerstoredata); - sql_prepare (plugin->dbh, - "DELETE FROM peerstoredata" - " WHERE sub_system = ?" - " AND peer_id = ?" " AND key = ?", - &plugin->delete_peerstoredata); - return GNUNET_OK; -} - - -/** - * Shutdown database connection and associate data - * structures. - * @param plugin the plugin context (state for this module) - */ -static void -database_shutdown (struct Plugin *plugin) -{ - int result; - sqlite3_stmt *stmt; - - while (NULL != (stmt = sqlite3_next_stmt (plugin->dbh, - NULL))) - { - result = sqlite3_finalize (stmt); - if (SQLITE_OK != result) - LOG (GNUNET_ERROR_TYPE_WARNING, - "Failed to close statement %p: %d\n", - stmt, - result); - } - if (SQLITE_OK != sqlite3_close (plugin->dbh)) - LOG_SQLITE (plugin, - GNUNET_ERROR_TYPE_ERROR, - "sqlite3_close"); - GNUNET_free (plugin->fn); -} - - -/** - * Entry point for the plugin. - * - * @param cls The struct GNUNET_CONFIGURATION_Handle. - * @return NULL on error, otherwise the plugin context - */ -void * -libgnunet_plugin_peerstore_sqlite_init (void *cls) -{ - static struct Plugin plugin; - const struct GNUNET_CONFIGURATION_Handle *cfg = cls; - struct GNUNET_PEERSTORE_PluginFunctions *api; - - if (NULL != plugin.cfg) - return NULL; /* can only initialize once! */ - memset (&plugin, - 0, - sizeof(struct Plugin)); - plugin.cfg = cfg; - if (GNUNET_OK != database_setup (&plugin)) - { - database_shutdown (&plugin); - return NULL; - } - api = GNUNET_new (struct GNUNET_PEERSTORE_PluginFunctions); - api->cls = &plugin; - api->store_record = &peerstore_sqlite_store_record; - api->iterate_records = &peerstore_sqlite_iterate_records; - api->expire_records = &peerstore_sqlite_expire_records; - LOG (GNUNET_ERROR_TYPE_DEBUG, - "Sqlite plugin is running\n"); - return api; -} - - -/** - * Exit point from the plugin. - * - * @param cls The plugin context (as returned by "init") - * @return Always NULL - */ -void * -libgnunet_plugin_peerstore_sqlite_done (void *cls) -{ - struct GNUNET_PEERSTORE_PluginFunctions *api = cls; - struct Plugin *plugin = api->cls; - - database_shutdown (plugin); - plugin->cfg = NULL; - GNUNET_free (api); - LOG (GNUNET_ERROR_TYPE_DEBUG, - "Sqlite plugin is finished\n"); - return NULL; -} - - -/* end of plugin_peerstore_sqlite.c */ diff --git a/src/peerstore/test_peerstore_api_data.conf b/src/peerstore/test_peerstore_api_data.conf deleted file mode 100644 index 614d0cf5b..000000000 --- a/src/peerstore/test_peerstore_api_data.conf +++ /dev/null @@ -1,14 +0,0 @@ -[PATHS] -GNUNET_TEST_HOME = $GNUNET_TMP/test-gnunet-peerstore - -[peerstore] -START_ON_DEMAND = YES -BINARY = gnunet-service-peerstore -UNIXPATH = $GNUNET_TMP/gnunet-service-peerstore.sock -DATABASE = sqlite -UNIX_MATCH_UID = NO -UNIX_MATCH_GID = YES -#PREFIX = xterm -e gdb --args - -[peerstore-sqlite] -FILENAME = $GNUNET_TEST_HOME/gnunet-peerstore-sqlite.db diff --git a/src/peerstore/test_peerstore_api_iterate.c b/src/peerstore/test_peerstore_api_iterate.c deleted file mode 100644 index b6cd51906..000000000 --- a/src/peerstore/test_peerstore_api_iterate.c +++ /dev/null @@ -1,176 +0,0 @@ -/* - This file is part of GNUnet. - Copyright (C) 2013-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 . - - SPDX-License-Identifier: AGPL3.0-or-later - */ -/** - * @file peerstore/test_peerstore_api_iterate.c - * @brief testcase for peerstore iteration operation - */ -#include "platform.h" -#include "gnunet_util_lib.h" -#include "gnunet_testing_lib.h" -#include "gnunet_peerstore_service.h" - -static int ok = 1; - -static struct GNUNET_PEERSTORE_Handle *h; -static struct GNUNET_PEERSTORE_IterateContext *ic; - -static char *ss = "test_peerstore_api_iterate"; -static struct GNUNET_PeerIdentity p1; -static struct GNUNET_PeerIdentity p2; -static char *k1 = "test_peerstore_api_iterate_key1"; -static char *k2 = "test_peerstore_api_iterate_key2"; -static char *k3 = "test_peerstore_api_iterate_key3"; -static char *val = "test_peerstore_api_iterate_val"; -static int count = 0; - - -static void -iter3_cb (void *cls, - const struct GNUNET_PEERSTORE_Record *record, - const char *emsg) -{ - if (NULL != emsg) - { - GNUNET_PEERSTORE_iterate_cancel (ic); - return; - } - if (NULL != record) - { - count++; - return; - } - GNUNET_assert (count == 3); - ok = 0; - GNUNET_PEERSTORE_disconnect (h, GNUNET_NO); - GNUNET_SCHEDULER_shutdown (); -} - - -static void -iter2_cb (void *cls, - const struct GNUNET_PEERSTORE_Record *record, - const char *emsg) -{ - if (NULL != emsg) - { - GNUNET_PEERSTORE_iterate_cancel (ic); - return; - } - if (NULL != record) - { - count++; - return; - } - GNUNET_assert (count == 2); - count = 0; - ic = GNUNET_PEERSTORE_iterate (h, - ss, - NULL, - NULL, - &iter3_cb, - NULL); -} - - -static void -iter1_cb (void *cls, - const struct GNUNET_PEERSTORE_Record *record, - const char *emsg) -{ - if (NULL != emsg) - { - GNUNET_PEERSTORE_iterate_cancel (ic); - return; - } - if (NULL != record) - { - count++; - return; - } - GNUNET_assert (count == 1); - count = 0; - ic = GNUNET_PEERSTORE_iterate (h, - ss, - &p1, - NULL, - &iter2_cb, - NULL); -} - - -static void -run (void *cls, - const struct GNUNET_CONFIGURATION_Handle *cfg, - struct GNUNET_TESTING_Peer *peer) -{ - h = GNUNET_PEERSTORE_connect (cfg); - GNUNET_assert (NULL != h); - memset (&p1, 1, sizeof(p1)); - memset (&p2, 2, sizeof(p2)); - GNUNET_PEERSTORE_store (h, - ss, - &p1, - k1, - val, - strlen (val) + 1, - GNUNET_TIME_UNIT_FOREVER_ABS, - GNUNET_PEERSTORE_STOREOPTION_REPLACE, - NULL, - NULL); - GNUNET_PEERSTORE_store (h, - ss, - &p1, - k2, - val, - strlen (val) + 1, - GNUNET_TIME_UNIT_FOREVER_ABS, - GNUNET_PEERSTORE_STOREOPTION_REPLACE, - NULL, - NULL); - GNUNET_PEERSTORE_store (h, - ss, - &p2, - k3, - val, - strlen (val) + 1, - GNUNET_TIME_UNIT_FOREVER_ABS, - GNUNET_PEERSTORE_STOREOPTION_REPLACE, - NULL, - NULL); - ic = GNUNET_PEERSTORE_iterate (h, - ss, - &p1, - k1, - &iter1_cb, NULL); -} - - -int -main (int argc, char *argv[]) -{ - if (0 != - GNUNET_TESTING_service_run ("test-gnunet-peerstore", "peerstore", - "test_peerstore_api_data.conf", &run, NULL)) - return 1; - return ok; -} - - -/* end of test_peerstore_api_iterate.c */ diff --git a/src/peerstore/test_peerstore_api_store.c b/src/peerstore/test_peerstore_api_store.c deleted file mode 100644 index 77e8a17c1..000000000 --- a/src/peerstore/test_peerstore_api_store.c +++ /dev/null @@ -1,233 +0,0 @@ -/* - This file is part of GNUnet. - Copyright (C) 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 . - - SPDX-License-Identifier: AGPL3.0-or-later - */ -/** - * @file peerstore/test_peerstore_api_store.c - * @brief testcase for peerstore store operation - */ -#include "platform.h" -#include "gnunet_peerstore_service.h" -#include "gnunet_testing_lib.h" - - -static int ok = 1; - -static struct GNUNET_PEERSTORE_Handle *h; - -static char *subsystem = "test_peerstore_api_store"; -static struct GNUNET_PeerIdentity pid; -static char *key = "test_peerstore_api_store_key"; -static char *val1 = "test_peerstore_api_store_val1"; -static char *val2 = "test_peerstore_api_store_val2-"; -static char *val3 = "test_peerstore_api_store_val3--"; - -static int count = 0; - - -static void -test3_cont2 (void *cls, - const struct GNUNET_PEERSTORE_Record *record, - const char *emsg) -{ - if (NULL != emsg) - return; - if (NULL != record) - { - GNUNET_assert ((strlen (val3) + 1) == record->value_size); - GNUNET_assert (0 == strcmp ((char *) val3, - (char *) record->value)); - count++; - return; - } - GNUNET_assert (count == 1); - ok = 0; - GNUNET_PEERSTORE_disconnect (h, GNUNET_YES); - GNUNET_SCHEDULER_shutdown (); -} - - -static void -test3_cont (void *cls, - int success) -{ - if (GNUNET_YES != success) - return; - count = 0; - GNUNET_PEERSTORE_iterate (h, - subsystem, - &pid, - key, - &test3_cont2, - NULL); -} - - -/** - * Replace the previous 2 records - */ -static void -test3 () -{ - GNUNET_PEERSTORE_store (h, - subsystem, - &pid, - key, - val3, - strlen (val3) + 1, - GNUNET_TIME_UNIT_FOREVER_ABS, - GNUNET_PEERSTORE_STOREOPTION_REPLACE, - &test3_cont, - NULL); -} - - -static void -test2_cont2 (void *cls, - const struct GNUNET_PEERSTORE_Record *record, - const char *emsg) -{ - if (NULL != emsg) - return; - if (NULL != record) - { - GNUNET_assert (((strlen (val1) + 1) == record->value_size) || - ((strlen (val2) + 1) == record->value_size)); - GNUNET_assert ((0 == strcmp ((char *) val1, (char *) record->value)) || - (0 == strcmp ((char *) val2, (char *) record->value))); - count++; - return; - } - GNUNET_assert (count == 2); - count = 0; - test3 (); -} - - -static void -test2_cont (void *cls, int success) -{ - if (GNUNET_YES != success) - return; - count = 0; - GNUNET_PEERSTORE_iterate (h, - subsystem, - &pid, key, - &test2_cont2, - NULL); -} - - -/** - * Test storing a second value with the same key - */ -void -test2 () -{ - GNUNET_PEERSTORE_store (h, - subsystem, - &pid, - key, - val2, - strlen (val2) + 1, - GNUNET_TIME_UNIT_FOREVER_ABS, - GNUNET_PEERSTORE_STOREOPTION_MULTIPLE, - &test2_cont, - NULL); -} - - -static void -test1_cont2 (void *cls, - const struct GNUNET_PEERSTORE_Record *record, - const char *emsg) -{ - if (NULL != emsg) - return; - if (NULL != record) - { - GNUNET_assert ((strlen (val1) + 1) == record->value_size); - GNUNET_assert (0 == strcmp ((char *) val1, (char *) record->value)); - count++; - return; - } - GNUNET_assert (count == 1); - count = 0; - test2 (); -} - - -static void -test1_cont (void *cls, int success) -{ - if (GNUNET_YES != success) - return; - count = 0; - GNUNET_PEERSTORE_iterate (h, - subsystem, - &pid, - key, - &test1_cont2, - NULL); -} - - -/** - * Store a single record - */ -static void -test1 () -{ - GNUNET_PEERSTORE_store (h, - subsystem, - &pid, - key, - val1, - strlen (val1) + 1, - GNUNET_TIME_UNIT_FOREVER_ABS, - GNUNET_PEERSTORE_STOREOPTION_REPLACE, - &test1_cont, - NULL); -} - - -static void -run (void *cls, const struct GNUNET_CONFIGURATION_Handle *cfg, - struct GNUNET_TESTING_Peer *peer) -{ - h = GNUNET_PEERSTORE_connect (cfg); - GNUNET_assert (NULL != h); - memset (&pid, 1, sizeof(pid)); - test1 (); -} - - -int -main (int argc, char *argv[]) -{ - if (0 != - GNUNET_TESTING_service_run ("test-gnunet-peerstore", - "peerstore", - "test_peerstore_api_data.conf", - &run, NULL)) - return 1; - return ok; -} - - -/* end of test_peerstore_api_store.c */ diff --git a/src/peerstore/test_peerstore_api_sync.c b/src/peerstore/test_peerstore_api_sync.c deleted file mode 100644 index 5057c98b5..000000000 --- a/src/peerstore/test_peerstore_api_sync.c +++ /dev/null @@ -1,253 +0,0 @@ -/* - This file is part of GNUnet. - Copyright (C) 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 . - - SPDX-License-Identifier: AGPL3.0-or-later - */ -/** - * @file peerstore/test_peerstore_api_sync.c - * @brief testcase for peerstore sync-on-disconnect feature. Stores - * a value just before disconnecting, and then checks that - * this value is actually stored. - * @author Omar Tarabai - * @author Christian Grothoff (minor fix, comments) - */ -#include "platform.h" -#include "gnunet_util_lib.h" -#include "gnunet_testing_lib.h" -#include "gnunet_peerstore_service.h" - -/** - * Overall result, 0 for success. - */ -static int ok = 404; - -/** - * Configuration we use. - */ -static const struct GNUNET_CONFIGURATION_Handle *cfg; - -/** - * handle to talk to the peerstore. - */ -static struct GNUNET_PEERSTORE_Handle *h; - -/** - * Subsystem we store the value for. - */ -static const char *subsystem = "test_peerstore_api_sync"; - -/** - * Fake PID under which we store the value. - */ -static struct GNUNET_PeerIdentity pid; - -/** - * Test key we're storing the test value under. - */ -static const char *key = "test_peerstore_api_store_key"; - -/** - * Test value we are storing. - */ -static const char *val = "test_peerstore_api_store_val"; - - -/** - * Timeout - */ -#define TIMEOUT GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS, 10) - -/** - * Timeout task - */ -static struct GNUNET_SCHEDULER_Task *to; - -/** - * Iterate handle - */ -static struct GNUNET_PEERSTORE_IterateContext *it; - -static void -test_cont (void *cls); - -/** - * Function that should be called with the result of the - * lookup, and finally once with NULL to signal the end - * of the iteration. - * - * Upon the first call, we set "ok" to success. On the - * second call (end of iteration) we terminate the test. - * - * @param cls NULL - * @param record the information stored in the peerstore - * @param emsg any error message - * @return #GNUNET_YES (all good, continue) - */ -static void -iterate_cb (void *cls, - const struct GNUNET_PEERSTORE_Record *record, - const char *emsg) -{ - const char *rec_val; - - GNUNET_break (NULL == emsg); - if (NULL == record) - { - it = NULL; - if (0 == ok) - { - GNUNET_PEERSTORE_disconnect (h, - GNUNET_NO); - if (NULL != to) - { - GNUNET_SCHEDULER_cancel (to); - to = NULL; - } - GNUNET_SCHEDULER_shutdown (); - return; - } - /** - * Try again - */ - GNUNET_SCHEDULER_add_now (&test_cont, - NULL); - return; - } - rec_val = record->value; - GNUNET_break (0 == strcmp (rec_val, val)); - ok = 0; -} - - -static void -timeout_task (void *cls) -{ - to = NULL; - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - "Timeout reached\n"); - if (NULL != it) - GNUNET_PEERSTORE_iterate_cancel (it); - it = NULL; - GNUNET_PEERSTORE_disconnect (h, - GNUNET_NO); - GNUNET_SCHEDULER_shutdown (); - return; -} - - -/** - * Run the 2nd stage of the test where we fetch the - * data that should have been stored. - * - * @param cls NULL - */ -static void -test_cont (void *cls) -{ - it = GNUNET_PEERSTORE_iterate (h, - subsystem, - &pid, key, - &iterate_cb, - NULL); -} - - -static void -disc_cont (void *cls) -{ - GNUNET_PEERSTORE_disconnect (h, GNUNET_YES); - h = GNUNET_PEERSTORE_connect (cfg); - GNUNET_SCHEDULER_add_now (&test_cont, - NULL); -} - - -static void -store_cont (void *cls, int success) -{ - ok = success; - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "Success: %s\n", - (GNUNET_SYSERR == ok) ? "no" : "yes"); - /* We need to wait a little bit to give the disconnect - a chance to actually finish the operation; otherwise, - the test may fail non-deterministically if the new - connection is faster than the cleanup routine of the - old one. */ - GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_UNIT_SECONDS, - &disc_cont, - NULL); -} - - -/** - * Actually run the test. - */ -static void -test1 () -{ - h = GNUNET_PEERSTORE_connect (cfg); - GNUNET_PEERSTORE_store (h, - subsystem, - &pid, - key, - val, strlen (val) + 1, - GNUNET_TIME_UNIT_FOREVER_ABS, - GNUNET_PEERSTORE_STOREOPTION_REPLACE, - &store_cont, NULL); -} - - -/** - * Initialize globals and launch the test. - * - * @param cls NULL - * @param c configuration to use - * @param peer handle to our peer (unused) - */ -static void -run (void *cls, - const struct GNUNET_CONFIGURATION_Handle *c, - struct GNUNET_TESTING_Peer *peer) -{ - cfg = c; - memset (&pid, 1, sizeof(pid)); - to = GNUNET_SCHEDULER_add_delayed (TIMEOUT, - &timeout_task, - NULL); - GNUNET_SCHEDULER_add_now (&test1, NULL); -} - - -int -main (int argc, char *argv[]) -{ - if (0 != - GNUNET_TESTING_service_run ("test-gnunet-peerstore-sync", - "peerstore", - "peerstore.conf", - &run, NULL)) - return 1; - if (0 != ok) - fprintf (stderr, - "Test failed: %d\n", - ok); - return ok; -} - - -/* end of test_peerstore_api_sync.c */ diff --git a/src/peerstore/test_peerstore_api_watch.c b/src/peerstore/test_peerstore_api_watch.c deleted file mode 100644 index 126b321df..000000000 --- a/src/peerstore/test_peerstore_api_watch.c +++ /dev/null @@ -1,102 +0,0 @@ -/* - This file is part of GNUnet. - Copyright (C) 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 . - - SPDX-License-Identifier: AGPL3.0-or-later - */ -/** - * @file peerstore/test_peerstore_api_watch.c - * @brief testcase for peerstore watch functionality - */ -#include "platform.h" -#include "gnunet_util_lib.h" -#include "gnunet_testing_lib.h" -#include "gnunet_peerstore_service.h" - - -static int ok = 1; - -static struct GNUNET_PEERSTORE_Handle *h; - -static char *ss = "test_peerstore_api_watch"; - -static char *k = "test_peerstore_api_watch_key"; - -static char *val = "test_peerstore_api_watch_val"; - - -static void -watch_cb (void *cls, - const struct GNUNET_PEERSTORE_Record *record, - const char *emsg) -{ - GNUNET_assert (NULL == emsg); - GNUNET_assert (0 == strcmp (val, - (char *) record->value)); - ok = 0; - GNUNET_PEERSTORE_disconnect (h, - GNUNET_NO); - GNUNET_SCHEDULER_shutdown (); -} - - -static void -run (void *cls, - const struct GNUNET_CONFIGURATION_Handle *cfg, - struct GNUNET_TESTING_Peer *peer) -{ - struct GNUNET_PeerIdentity p; - - h = GNUNET_PEERSTORE_connect (cfg); - GNUNET_assert (NULL != h); - memset (&p, - 4, - sizeof(p)); - GNUNET_PEERSTORE_watch (h, - ss, - &p, - k, - &watch_cb, - NULL); - GNUNET_PEERSTORE_store (h, - ss, - &p, - k, - val, - strlen (val) + 1, - GNUNET_TIME_UNIT_FOREVER_ABS, - GNUNET_PEERSTORE_STOREOPTION_REPLACE, - NULL, - NULL); -} - - -int -main (int argc, - char *argv[]) -{ - if (0 != - GNUNET_TESTING_service_run ("test-gnunet-peerstore", - "peerstore", - "test_peerstore_api_data.conf", - &run, - NULL)) - return 1; - return ok; -} - - -/* end of test_peerstore_api_watch.c */ diff --git a/src/peerstore/test_plugin_peerstore.c b/src/peerstore/test_plugin_peerstore.c deleted file mode 100644 index bce62dda9..000000000 --- a/src/peerstore/test_plugin_peerstore.c +++ /dev/null @@ -1,224 +0,0 @@ -/* - This file is part of GNUnet. - Copyright (C) 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 . - - SPDX-License-Identifier: AGPL3.0-or-later - */ -/* - * @file namestore/test_plugin_namestore.c - * @brief Test for the namestore plugins - * @author Christian Grothoff - */ -#include "platform.h" -#include "gnunet_util_lib.h" -#include "gnunet_peerstore_plugin.h" -#include "gnunet_testing_lib.h" - - -static int ok; - -/** - * Name of plugin under test. - */ -static const char *plugin_name; - - -static struct GNUNET_PEERSTORE_PluginFunctions *psp; - -static struct GNUNET_PeerIdentity p1; - - -/** - * Function called when the service shuts down. Unloads our namestore - * plugin. - * - * @param api api to unload - */ -static void -unload_plugin (struct GNUNET_PEERSTORE_PluginFunctions *api) -{ - char *libname; - - GNUNET_asprintf (&libname, - "libgnunet_plugin_peer_%s", - plugin_name); - GNUNET_break (NULL == - GNUNET_PLUGIN_unload (libname, - api)); - GNUNET_free (libname); -} - - -/** - * Load the namestore plugin. - * - * @param cfg configuration to pass - * @return NULL on error - */ -static struct GNUNET_PEERSTORE_PluginFunctions * -load_plugin (const struct GNUNET_CONFIGURATION_Handle *cfg) -{ - struct GNUNET_PEERSTORE_PluginFunctions *ret; - char *libname; - - GNUNET_log (GNUNET_ERROR_TYPE_INFO, - _ ("Loading `%s' peer plugin\n"), - plugin_name); - GNUNET_asprintf (&libname, - "libgnunet_plugin_peerstore_%s", - plugin_name); - if (NULL == (ret = GNUNET_PLUGIN_load (libname, - (void *) cfg))) - { - fprintf (stderr, - "Failed to load plugin `%s'!\n", - plugin_name); - GNUNET_free (libname); - return NULL; - } - GNUNET_free (libname); - return ret; -} - - -static void -test_record (void *cls, - const struct GNUNET_PEERSTORE_Record *record, - const char *error) -{ - const struct GNUNET_PeerIdentity *id = cls; - const char*testval = "test_val"; - - if (NULL == record) - { - unload_plugin (psp); - return; - } - GNUNET_assert (0 == memcmp (&record->peer, - id, - sizeof(struct GNUNET_PeerIdentity))); - GNUNET_assert (0 == strcmp ("subsys", - record->sub_system)); - GNUNET_assert (0 == strcmp ("key", - record->key)); - GNUNET_assert (0 == memcmp (testval, - record->value, - strlen (testval))); - ok = 0; -} - - -static void -get_record (struct GNUNET_PEERSTORE_PluginFunctions *psp, - const struct GNUNET_PeerIdentity *identity) -{ - GNUNET_assert (GNUNET_OK == - psp->iterate_records (psp->cls, - "subsys", - identity, - "key", - &test_record, - (void *) identity)); -} - - -static void -store_cont (void *cls, - int status) -{ - GNUNET_assert (GNUNET_OK == status); - get_record (psp, - &p1); -} - - -static void -put_record (struct GNUNET_PEERSTORE_PluginFunctions *psp, - const struct GNUNET_PeerIdentity *identity) -{ - GNUNET_assert (GNUNET_OK == - psp->store_record (psp->cls, - "subsys", - identity, - "key", - "test_value", - strlen ("test_value"), - GNUNET_TIME_absolute_get (), - GNUNET_PEERSTORE_STOREOPTION_REPLACE, - &store_cont, - NULL)); -} - - -static void -run (void *cls, - char *const *args, - const char *cfgfile, - const struct GNUNET_CONFIGURATION_Handle *cfg) -{ - ok = 1; - psp = load_plugin (cfg); - if (NULL == psp) - { - fprintf (stderr, - "%s", - "Failed to initialize peerstore. Database likely not setup, skipping test.\n"); - return; - } - memset (&p1, 1, sizeof(p1)); - put_record (psp, - &p1); -} - - -int -main (int argc, char *argv[]) -{ - char cfg_name[PATH_MAX]; - char *const xargv[] = { - "test-plugin-peerstore", - "-c", - cfg_name, - NULL - }; - struct GNUNET_GETOPT_CommandLineOption options[] = { - GNUNET_GETOPT_OPTION_END - }; - - GNUNET_log_setup ("test-plugin-peerstore", - "WARNING", - NULL); - plugin_name = GNUNET_STRINGS_get_suffix_from_binary_name (argv[0]); - GNUNET_snprintf (cfg_name, - sizeof(cfg_name), - "test_plugin_peerstore_%s.conf", - plugin_name); - GNUNET_PROGRAM_run ((sizeof(xargv) / sizeof(char *)) - 1, - xargv, - "test-plugin-peerstore", - "nohelp", - options, - &run, - NULL); - if (ok != 0) - fprintf (stderr, - "Missed some testcases: %d\n", - ok); - return ok; -} - - -/* end of test_plugin_peerstore.c */ diff --git a/src/peerstore/test_plugin_peerstore_flat.conf b/src/peerstore/test_plugin_peerstore_flat.conf deleted file mode 100644 index c55b1e9d6..000000000 --- a/src/peerstore/test_plugin_peerstore_flat.conf +++ /dev/null @@ -1,5 +0,0 @@ -[peerstore-flat] -FILENAME = $GNUNET_TMP/gnunet-test-plugin-namestore-flat/flatdb - -[peerstore] -# PREFIX = valgrind --log-file=/home/schanzen/dev/gnunet/src/peerstore/vg_log diff --git a/src/peerstore/test_plugin_peerstore_sqlite.conf b/src/peerstore/test_plugin_peerstore_sqlite.conf deleted file mode 100644 index dcf1fc1a4..000000000 --- a/src/peerstore/test_plugin_peerstore_sqlite.conf +++ /dev/null @@ -1,2 +0,0 @@ -[peerstore-sqlite] -FILENAME = $GNUNET_TMP/gnunet-test-plugin-peerstore-sqlite/sqlite.db diff --git a/src/plugin/dhtu/Makefile.am b/src/plugin/dhtu/Makefile.am index 26e706e36..500d5a7b1 100644 --- a/src/plugin/dhtu/Makefile.am +++ b/src/plugin/dhtu/Makefile.am @@ -34,10 +34,10 @@ libgnunet_plugin_dhtu_gnunet_la_SOURCES = \ plugin_dhtu_gnunet.c libgnunet_plugin_dhtu_gnunet_la_LIBADD = \ $(top_builddir)/src/service/core/libgnunetcore.la \ - $(top_builddir)/src/peerstore/libgnunetpeerstore.la \ - $(top_builddir)/src/transport/libgnunettransportapplication.la \ + $(top_builddir)/src/service/peerstore/libgnunetpeerstore.la \ + $(top_builddir)/src/service/transport/libgnunettransportapplication.la \ $(top_builddir)/src/lib/hello/libgnunethello.la \ - $(top_builddir)/src/nse/libgnunetnse.la \ + $(top_builddir)/src/service/nse/libgnunetnse.la \ $(top_builddir)/src/lib/util/libgnunetutil.la \ $(XLIBS) \ $(LTLIBINTL) @@ -51,7 +51,7 @@ lib_LTLIBRARIES = \ libgnunettestingdhtu_la_SOURCES = \ testing_dhtu_cmd_send.c libgnunettestingdhtu_la_LIBADD = \ - $(top_builddir)/src/lib/testing/libgnunettesting.la \ + $(top_builddir)/src/service/testing/libgnunettesting.la \ $(top_builddir)/src/service/arm/libgnunetarm.la \ $(top_builddir)/src/lib/util/libgnunetutil.la \ $(LTLIBINTL) @@ -64,7 +64,7 @@ libgnunettestingdhtu_la_LDFLAGS = \ test_dhtu_ip_SOURCES = \ test_dhtu_ip.c test_dhtu_ip_LDADD = \ - $(top_builddir)/src/lib/testing/libgnunettesting.la \ + $(top_builddir)/src/service/testing/libgnunettesting.la \ $(top_builddir)/src/service/arm/libgnunetarm.la \ $(top_builddir)/src/lib/util/libgnunetutil.la diff --git a/src/pt/Makefile.am b/src/pt/Makefile.am index f00d16a5c..8a171ec0e 100644 --- a/src/pt/Makefile.am +++ b/src/pt/Makefile.am @@ -24,7 +24,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/statistics/libgnunetstatistics.la \ + $(top_builddir)/src/service/statistics/libgnunetstatistics.la \ $(top_builddir)/src/lib/util/libgnunetutil.la \ $(GN_LIBINTL) @@ -56,7 +56,7 @@ test_gns_vpn_LDADD = $(MHD_LIBS) @LIBCURL@ \ $(top_builddir)/src/namestore/libgnunetnamestore.la \ $(top_builddir)/src/service/identity/libgnunetidentity.la \ $(top_builddir)/src/lib/gnsrecord/libgnunetgnsrecord.la \ - $(top_builddir)/src/lib/testing/libgnunettesting.la \ + $(top_builddir)/src/service/testing/libgnunettesting.la \ $(top_builddir)/src/lib/util/libgnunetutil.la test_gns_vpn_CFLAGS = $(MHD_CFLAGS) @LIBCURL_CPPFLAGS@ $(AM_CFLAGS) @@ -64,7 +64,7 @@ test_gnunet_vpn_4_over_SOURCES = \ test_gnunet_vpn.c test_gnunet_vpn_4_over_LDADD = $(MHD_LIBS) @LIBCURL@ \ $(top_builddir)/src/vpn/libgnunetvpn.la \ - $(top_builddir)/src/lib/testing/libgnunettesting.la \ + $(top_builddir)/src/service/testing/libgnunettesting.la \ $(top_builddir)/src/lib/util/libgnunetutil.la test_gnunet_vpn_4_over_CFLAGS = $(MHD_CFLAGS) @LIBCURL_CPPFLAGS@ $(AM_CFLAGS) @@ -72,7 +72,7 @@ test_gnunet_vpn_6_over_SOURCES = \ test_gnunet_vpn.c test_gnunet_vpn_6_over_LDADD = $(MHD_LIBS) @LIBCURL@ \ $(top_builddir)/src/vpn/libgnunetvpn.la \ - $(top_builddir)/src/lib/testing/libgnunettesting.la \ + $(top_builddir)/src/service/testing/libgnunettesting.la \ $(top_builddir)/src/lib/util/libgnunetutil.la test_gnunet_vpn_6_over_CFLAGS = $(MHD_CFLAGS) @LIBCURL_CPPFLAGS@ $(AM_CFLAGS) @@ -80,7 +80,7 @@ test_gnunet_vpn_4_to_6_SOURCES = \ test_gnunet_vpn.c test_gnunet_vpn_4_to_6_LDADD = $(MHD_LIBS) @LIBCURL@ \ $(top_builddir)/src/vpn/libgnunetvpn.la \ - $(top_builddir)/src/lib/testing/libgnunettesting.la \ + $(top_builddir)/src/service/testing/libgnunettesting.la \ $(top_builddir)/src/lib/util/libgnunetutil.la test_gnunet_vpn_4_to_6_CFLAGS = $(MHD_CFLAGS) @LIBCURL_CPPFLAGS@ $(AM_CFLAGS) @@ -88,6 +88,6 @@ test_gnunet_vpn_6_to_4_SOURCES = \ test_gnunet_vpn.c test_gnunet_vpn_6_to_4_LDADD = $(MHD_LIBS) @LIBCURL@ \ $(top_builddir)/src/vpn/libgnunetvpn.la \ - $(top_builddir)/src/lib/testing/libgnunettesting.la \ + $(top_builddir)/src/service/testing/libgnunettesting.la \ $(top_builddir)/src/lib/util/libgnunetutil.la test_gnunet_vpn_6_to_4_CFLAGS = $(MHD_CFLAGS) @LIBCURL_CPPFLAGS@ $(AM_CFLAGS) diff --git a/src/reclaim/Makefile.am b/src/reclaim/Makefile.am index e135f3534..c052dd2a8 100644 --- a/src/reclaim/Makefile.am +++ b/src/reclaim/Makefile.am @@ -124,7 +124,7 @@ gnunet_service_reclaim_LDADD = \ $(top_builddir)/src/service/identity/libgnunetidentity.la \ $(top_builddir)/src/lib/util/libgnunetutil.la \ $(top_builddir)/src/namestore/libgnunetnamestore.la \ - $(top_builddir)/src/statistics/libgnunetstatistics.la \ + $(top_builddir)/src/service/statistics/libgnunetstatistics.la \ libgnunetreclaim.la \ $(top_builddir)/src/gns/libgnunetgns.la \ $(GN_LIBINTL) diff --git a/src/regex/Makefile.am b/src/regex/Makefile.am index 69f341259..57f48db44 100644 --- a/src/regex/Makefile.am +++ b/src/regex/Makefile.am @@ -33,7 +33,7 @@ gnunet_service_regex_SOURCES = \ gnunet_service_regex_LDADD = -lm \ libgnunetregexblock.la \ $(top_builddir)/src/dht/libgnunetdht.la \ - $(top_builddir)/src/statistics/libgnunetstatistics.la \ + $(top_builddir)/src/service/statistics/libgnunetstatistics.la \ $(top_builddir)/src/lib/util/libgnunetutil.la \ $(GN_LIBINTL) @@ -90,7 +90,7 @@ libgnunet_plugin_block_regex_la_LDFLAGS = \ # $(top_builddir)/src/lib/util/libgnunetutil.la \ # $(top_builddir)/src/dht/libgnunetdht.la \ # $(top_builddir)/src/lib/block/libgnunetblock.la \ -# $(top_builddir)/src/statistics/libgnunetstatistics.la +# $(top_builddir)/src/service/statistics/libgnunetstatistics.la #endif noinst_PROGRAMS = $(noinst_mysql_progs) \ @@ -102,7 +102,7 @@ perf_regex_LDADD = -lm \ $(top_builddir)/src/dht/libgnunetdht.la \ libgnunetregexblock.la \ $(top_builddir)/src/lib/util/libgnunetutil.la \ - $(top_builddir)/src/statistics/libgnunetstatistics.la + $(top_builddir)/src/service/statistics/libgnunetstatistics.la perf_regex_LDFLAGS = \ $(GN_LIBINTL) @@ -111,7 +111,7 @@ gnunet_daemon_regexprofiler_SOURCES = \ gnunet_daemon_regexprofiler_LDADD = -lm \ $(top_builddir)/src/dht/libgnunetdht.la \ libgnunetregexblock.la \ - $(top_builddir)/src/statistics/libgnunetstatistics.la \ + $(top_builddir)/src/service/statistics/libgnunetstatistics.la \ $(top_builddir)/src/lib/util/libgnunetutil.la gnunet_daemon_regexprofiler_LDFLAGS = \ $(GN_LIBINTL) @@ -134,21 +134,21 @@ test_regex_eval_api_SOURCES = \ test_regex_eval_api_LDADD = -lm \ $(top_builddir)/src/dht/libgnunetdht.la \ libgnunetregexblock.la \ - $(top_builddir)/src/statistics/libgnunetstatistics.la \ + $(top_builddir)/src/service/statistics/libgnunetstatistics.la \ $(top_builddir)/src/lib/util/libgnunetutil.la test_regex_integration_SOURCES = \ test_regex_integration.c test_regex_integration_LDADD = -lm \ libgnunetregex.la \ - $(top_builddir)/src/lib/testing/libgnunettesting.la \ + $(top_builddir)/src/service/testing/libgnunettesting.la \ $(top_builddir)/src/lib/util/libgnunetutil.la test_regex_api_SOURCES = \ test_regex_api.c test_regex_api_LDADD = -lm \ libgnunetregex.la \ - $(top_builddir)/src/lib/testing/libgnunettesting.la \ + $(top_builddir)/src/service/testing/libgnunettesting.la \ $(top_builddir)/src/lib/util/libgnunetutil.la test_regex_iterate_api_SOURCES = \ @@ -156,7 +156,7 @@ test_regex_iterate_api_SOURCES = \ test_regex_iterate_api_LDADD = -lm \ libgnunetregexblock.la \ $(top_builddir)/src/dht/libgnunetdht.la \ - $(top_builddir)/src/statistics/libgnunetstatistics.la \ + $(top_builddir)/src/service/statistics/libgnunetstatistics.la \ $(top_builddir)/src/lib/util/libgnunetutil.la test_regex_proofs_SOURCES = \ @@ -164,7 +164,7 @@ test_regex_proofs_SOURCES = \ test_regex_proofs_LDADD = -lm \ $(top_builddir)/src/dht/libgnunetdht.la \ libgnunetregexblock.la \ - $(top_builddir)/src/statistics/libgnunetstatistics.la \ + $(top_builddir)/src/service/statistics/libgnunetstatistics.la \ $(top_builddir)/src/lib/util/libgnunetutil.la test_regex_graph_api_SOURCES = \ @@ -172,7 +172,7 @@ test_regex_graph_api_SOURCES = \ test_regex_graph_api_LDADD = -lm \ $(top_builddir)/src/dht/libgnunetdht.la \ libgnunetregexblock.la \ - $(top_builddir)/src/statistics/libgnunetstatistics.la \ + $(top_builddir)/src/service/statistics/libgnunetstatistics.la \ $(top_builddir)/src/lib/util/libgnunetutil.la diff --git a/src/revocation/Makefile.am b/src/revocation/Makefile.am index d271298a8..0684dd8f2 100644 --- a/src/revocation/Makefile.am +++ b/src/revocation/Makefile.am @@ -53,7 +53,7 @@ gnunet_revocation_tvg_LDADD = \ test_revocation_lsd0001testvectors_SOURCES = \ test_revocation_testvectors.c test_revocation_lsd0001testvectors_LDADD = \ - $(top_builddir)/src/lib/testing/libgnunettesting.la \ + $(top_builddir)/src/service/testing/libgnunettesting.la \ $(top_builddir)/src/service/identity/libgnunetidentity.la \ libgnunetrevocation.la \ $(top_builddir)/src/lib/util/libgnunetutil.la @@ -83,7 +83,7 @@ gnunet_service_revocation_LDADD = \ libgnunetrevocation.la \ $(top_builddir)/src/service/core/libgnunetcore.la \ $(top_builddir)/src/setu/libgnunetsetu.la \ - $(top_builddir)/src/statistics/libgnunetstatistics.la \ + $(top_builddir)/src/service/statistics/libgnunetstatistics.la \ $(top_builddir)/src/service/identity/libgnunetidentity.la \ $(top_builddir)/src/lib/util/libgnunetutil.la \ -lm \ diff --git a/src/scalarproduct/Makefile.am b/src/scalarproduct/Makefile.am index ef9f8b245..9ad63b309 100644 --- a/src/scalarproduct/Makefile.am +++ b/src/scalarproduct/Makefile.am @@ -84,7 +84,7 @@ libgnunetscalarproduct_la_SOURCES = \ scalarproduct.h libgnunetscalarproduct_la_LIBADD = \ $(top_builddir)/src/lib/util/libgnunetutil.la \ - $(top_builddir)/src/statistics/libgnunetstatistics.la \ + $(top_builddir)/src/service/statistics/libgnunetstatistics.la \ $(LIBGCRYPT_LIBS) \ -lgcrypt \ $(LTLIBINTL) diff --git a/src/secretsharing/Makefile.am b/src/secretsharing/Makefile.am index 6879ef609..c22d57fea 100644 --- a/src/secretsharing/Makefile.am +++ b/src/secretsharing/Makefile.am @@ -28,7 +28,7 @@ lib_LTLIBRARIES = \ # gnunet-secretsharing-profiler.c #gnunet_secretsharing_profiler_LDADD = \ # libgnunetsecretsharing.la \ -# $(top_builddir)/src/lib/testing/libgnunettesting.la \ +# $(top_builddir)/src/service/testing/libgnunettesting.la \ # $(top_builddir)/src/testbed/libgnunettestbed.la \ # $(top_builddir)/src/lib/util/libgnunetutil.la \ # $(GN_LIBINTL) @@ -67,7 +67,7 @@ test_secretsharing_api_SOURCES = \ test_secretsharing_api.c test_secretsharing_api_LDADD = \ libgnunetsecretsharing.la \ - $(top_builddir)/src/lib/testing/libgnunettesting.la \ + $(top_builddir)/src/service/testing/libgnunettesting.la \ $(top_builddir)/src/lib/util/libgnunetutil.la EXTRA_DIST = \ diff --git a/src/service/Makefile.am b/src/service/Makefile.am index 711b076f2..5c92e730e 100644 --- a/src/service/Makefile.am +++ b/src/service/Makefile.am @@ -1,7 +1,12 @@ SUBDIRS = \ util \ + statistics \ arm \ - core \ + peerstore \ testing \ + nat \ + transport \ + core \ + nse \ identity \ rest diff --git a/src/service/core/Makefile.am b/src/service/core/Makefile.am index 9ce8cf8b5..482befae9 100644 --- a/src/service/core/Makefile.am +++ b/src/service/core/Makefile.am @@ -39,11 +39,11 @@ libgnunet_test_core_plugin_cmd_just_run_la_SOURCES = \ test_core_plugin_cmd_just_run.c libgnunet_test_core_plugin_cmd_just_run_la_LIBADD = \ libgnunetcoretesting.la \ - $(top_builddir)/src/transport/libgnunettransportapplication.la \ - $(top_builddir)/src/transport/libgnunettransportcore.la \ - $(top_builddir)/src/lib/testing/libgnunettesting.la \ - $(top_builddir)/src/peerstore/libgnunetpeerstore.la \ - $(top_builddir)/src/statistics/libgnunetstatistics.la \ + $(top_builddir)/src/service/transport/libgnunettransportapplication.la \ + $(top_builddir)/src/service/transport/libgnunettransportcore.la \ + $(top_builddir)/src/service/testing/libgnunettesting.la \ + $(top_builddir)/src/service/peerstore/libgnunetpeerstore.la \ + $(top_builddir)/src/service/statistics/libgnunetstatistics.la \ $(top_builddir)/src/lib/hello/libgnunethello.la \ $(top_builddir)/src/service/arm/libgnunetarm.la \ $(top_builddir)/src/lib/util/libgnunetutil.la \ @@ -54,10 +54,10 @@ libgnunet_test_core_plugin_cmd_just_run_la_LDFLAGS = \ libgnunetcoretesting_la_SOURCES = \ core_api_cmd_connecting_peers.c libgnunetcoretesting_la_LIBADD = \ - $(top_builddir)/src/lib/testing/libgnunettesting.la \ + $(top_builddir)/src/service/testing/libgnunettesting.la \ $(top_builddir)/src/service/arm/libgnunetarm.la \ - $(top_builddir)/src/transport/libgnunettransportapplication.la \ - $(top_builddir)/src/transport/libgnunettransportcore.la \ + $(top_builddir)/src/service/transport/libgnunettransportapplication.la \ + $(top_builddir)/src/service/transport/libgnunettransportcore.la \ $(top_builddir)/src/lib/util/libgnunetutil.la libgnunetcoretesting_la_LDFLAGS = \ $(GN_LIBINTL) \ @@ -74,9 +74,9 @@ gnunet_service_core_SOURCES = \ gnunet-service-core_sessions.c gnunet-service-core_sessions.h \ gnunet-service-core_typemap.c gnunet-service-core_typemap.h gnunet_service_core_LDADD = \ - $(top_builddir)/src/statistics/libgnunetstatistics.la \ - $(top_builddir)/src/transport/libgnunettransportapplication.la \ - $(top_builddir)/src/transport/libgnunettransportcore.la \ + $(top_builddir)/src/service/statistics/libgnunetstatistics.la \ + $(top_builddir)/src/service/transport/libgnunettransportapplication.la \ + $(top_builddir)/src/service/transport/libgnunettransportcore.la \ $(top_builddir)/src/lib/util/libgnunetutil.la \ $(GN_LIBINTL) $(Z_LIBS) @@ -102,13 +102,13 @@ test_core_api_send_to_self_SOURCES = \ test_core_api_send_to_self.c test_core_api_send_to_self_LDADD = \ libgnunetcore.la \ - $(top_builddir)/src/lib/testing/libgnunettesting.la \ + $(top_builddir)/src/service/testing/libgnunettesting.la \ $(top_builddir)/src/lib/util/libgnunetutil.la test_core_api_start_only_SOURCES = \ test_core_api_start_only.c test_core_api_start_only_LDADD = \ - $(top_builddir)/src/lib/testing/libgnunettesting.la \ + $(top_builddir)/src/service/testing/libgnunettesting.la \ libgnunetcore.la \ $(top_builddir)/src/lib/util/libgnunetutil.la diff --git a/src/service/identity/Makefile.am b/src/service/identity/Makefile.am index 079a9af2e..e99ef211f 100644 --- a/src/service/identity/Makefile.am +++ b/src/service/identity/Makefile.am @@ -39,7 +39,7 @@ gnunet_service_identity_SOURCES = \ gnunet-service-identity.c gnunet_service_identity_LDADD = \ libgnunetidentity.la \ - $(top_builddir)/src/statistics/libgnunetstatistics.la \ + $(top_builddir)/src/service/statistics/libgnunetstatistics.la \ $(top_builddir)/src/lib/util/libgnunetutil.la \ $(GN_LIBINTL) @@ -57,7 +57,7 @@ test_identity_SOURCES = \ test_identity.c test_identity_LDADD = \ libgnunetidentity.la \ - $(top_builddir)/src/lib/testing/libgnunettesting.la \ + $(top_builddir)/src/service/testing/libgnunettesting.la \ $(top_builddir)/src/lib/util/libgnunetutil.la EXTRA_DIST = \ diff --git a/src/service/nat/.gitignore b/src/service/nat/.gitignore new file mode 100644 index 000000000..868abab4b --- /dev/null +++ b/src/service/nat/.gitignore @@ -0,0 +1,5 @@ +gnunet-service-nat +gnunet-helper-nat-client +gnunet-helper-nat-server +gnunet-nat +gnunet-nat-server diff --git a/src/service/nat/Makefile.am b/src/service/nat/Makefile.am new file mode 100644 index 000000000..d6cbe94f1 --- /dev/null +++ b/src/service/nat/Makefile.am @@ -0,0 +1,106 @@ +# This Makefile.am is in the public domain +AM_CPPFLAGS = -I$(top_srcdir)/src/include + +libexecdir= $(pkglibdir)/libexec/ + +pkgcfgdir= $(pkgdatadir)/config.d/ + +pkgcfg_DATA = \ + nat.conf + +if LINUX + NATBIN = gnunet-helper-nat-server gnunet-helper-nat-client + NATSERVER = gnunet-helper-nat-server.c + NATCLIENT = gnunet-helper-nat-client.c +else +if XFREEBSD + NATBIN = gnunet-helper-nat-server gnunet-helper-nat-client + NATSERVER = gnunet-helper-nat-server.c + NATCLIENT = gnunet-helper-nat-client.c +endif +else +install-exec-hook: +endif + +libexec_PROGRAMS = \ + $(NATBIN) \ + gnunet-service-nat + + +gnunet_helper_nat_server_SOURCES = \ + $(NATSERVER) + +gnunet_helper_nat_client_SOURCES = \ + $(NATCLIENT) + + +if USE_COVERAGE + AM_CFLAGS = -fprofile-arcs -ftest-coverage +endif + +lib_LTLIBRARIES = \ + libgnunetnatnew.la + +libgnunetnatnew_la_SOURCES = \ + nat_api.c \ + nat_api_stun.c nat_stun.h \ + nat.h +libgnunetnatnew_la_LIBADD = \ + $(top_builddir)/src/lib/util/libgnunetutil.la \ + $(GN_LIBINTL) @EXT_LIBS@ +libgnunetnatnew_la_LDFLAGS = \ + $(GN_LIB_LDFLAGS) \ + -version-info 2:0:0 + +gnunet_service_nat_SOURCES = \ + gnunet-service-nat.c gnunet-service-nat.h \ + gnunet-service-nat_externalip.c gnunet-service-nat_externalip.h \ + gnunet-service-nat_stun.c gnunet-service-nat_stun.h \ + gnunet-service-nat_mini.c gnunet-service-nat_mini.h \ + gnunet-service-nat_helper.c gnunet-service-nat_helper.h +gnunet_service_nat_LDADD = \ + $(top_builddir)/src/lib/util/libgnunetutil.la \ + $(top_builddir)/src/service/statistics/libgnunetstatistics.la \ + $(LIBGCRYPT_LIBS) \ + -lgcrypt \ + $(GN_LIBINTL) + +#check_PROGRAMS = \ +# test_nat \ +# test_nat_mini \ +# test_nat_test \ +# test_stun + +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_nat_SOURCES = \ +# test_nat.c +#test_nat_LDADD = \ +# libgnunetnat.la \ +# $(top_builddir)/src/lib/util/libgnunetutil.la + +#test_nat_mini_SOURCES = \ +# test_nat_mini.c +#test_nat_mini_LDADD = \ +# libgnunetnat.la \ +# $(top_builddir)/src/lib/util/libgnunetutil.la + +#test_nat_test_SOURCES = \ +# test_nat_test.c +#test_nat_test_LDADD = \ +# libgnunetnat.la \ +# $(top_builddir)/src/lib/util/libgnunetutil.la + +#test_stun_SOURCES = \ +# test_stun.c +#test_stun_LDADD = \ +# libgnunetnat.la \ +# $(top_builddir)/src/lib/util/libgnunetutil.la + +EXTRA_DIST = \ + test_nat_data.conf \ + test_nat_test_data.conf \ + test_stun.conf diff --git a/src/service/nat/gnunet-helper-nat-client.c b/src/service/nat/gnunet-helper-nat-client.c new file mode 100644 index 000000000..0b86aa0d2 --- /dev/null +++ b/src/service/nat/gnunet-helper-nat-client.c @@ -0,0 +1,547 @@ +/* + This file is part of GNUnet. + Copyright (C) 2010 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 . + + SPDX-License-Identifier: AGPL3.0-or-later + */ + +/** + * @file src/nat/gnunet-helper-nat-client.c + * @brief Tool to help bypass NATs using ICMP method; must run as root (SUID will do) + * This code will work under GNU/Linux only. + * @author Christian Grothoff + * + * This program will send ONE ICMP message using RAW sockets + * to the IP address specified as the second argument. Since + * it uses RAW sockets, it must be installed SUID or run as 'root'. + * In order to keep the security risk of the resulting SUID binary + * minimal, the program ONLY opens the RAW socket with root + * privileges, then drops them and only then starts to process + * command line arguments. The code also does not link against + * any shared libraries (except libc) and is strictly minimal + * (except for checking for errors). The following list of people + * have reviewed this code and considered it safe since the last + * modification (if you reviewed it, please have your name added + * to the list): + * + * - Christian Grothoff + * - Nathan Evans + * - Benjamin Kuperman (22 Aug 2010) + */ +#if HAVE_CONFIG_H +/* Just needed for HAVE_SOCKADDR_IN_SIN_LEN test macro! */ +#include "platform.h" +#include "gnunet_private_config.h" +#else +#define _GNU_SOURCE +#endif +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* The following constant is missing from FreeBSD 9.2 */ +#ifndef ICMP_TIME_EXCEEDED +#define ICMP_TIME_EXCEEDED 11 +#endif + +/** + * Call memcpy() but check for @a n being 0 first. In the latter + * case, it is now safe to pass NULL for @a src or @a dst. + * Unlike traditional memcpy(), returns nothing. + * + * @param dst destination of the copy, may be NULL if @a n is zero + * @param src source of the copy, may be NULL if @a n is zero + * @param n number of bytes to copy + */ +#define GNUNET_memcpy(dst, src, n) do { if (0 != n) { (void) memcpy (dst, src, \ + n); \ + } } while (0) + +/** + * Must match IP given in the server. + */ +#define DUMMY_IP "192.0.2.86" + +#define NAT_TRAV_PORT 22225 + +/** + * Must match packet ID used by gnunet-helper-nat-server.c + */ +#define PACKET_ID 256 + +/** + * IPv4 header. + */ +struct ip_header +{ + /** + * Version (4 bits) + Internet header length (4 bits) + */ + uint8_t vers_ihl; + + /** + * Type of service + */ + uint8_t tos; + + /** + * Total length + */ + uint16_t pkt_len; + + /** + * Identification + */ + uint16_t id; + + /** + * Flags (3 bits) + Fragment offset (13 bits) + */ + uint16_t flags_frag_offset; + + /** + * Time to live + */ + uint8_t ttl; + + /** + * Protocol + */ + uint8_t proto; + + /** + * Header checksum + */ + uint16_t checksum; + + /** + * Source address + */ + uint32_t src_ip; + + /** + * Destination address + */ + uint32_t dst_ip; +}; + +/** + * Format of ICMP packet. + */ +struct icmp_ttl_exceeded_header +{ + uint8_t type; + + uint8_t code; + + uint16_t checksum; + + uint32_t unused; + + /* followed by original payload */ +}; + +struct icmp_echo_header +{ + uint8_t type; + + uint8_t code; + + uint16_t checksum; + + uint32_t reserved; +}; + +/** + * Beginning of UDP packet. + */ +struct udp_header +{ + uint16_t src_port; + + uint16_t dst_port; + + uint16_t length; + + uint16_t crc; +}; + +/** + * Socket we use to send our fake ICMP replies. + */ +static int rawsock; + +/** + * Target "dummy" address of the packet we pretend to respond to. + */ +static struct in_addr dummy; + +/** + * Our "source" port. + */ +static uint16_t port; + + +/** + * CRC-16 for IP/ICMP headers. + * + * @param data what to calculate the CRC over + * @param bytes number of bytes in data (must be multiple of 2) + * @return the CRC 16. + */ +static uint16_t +calc_checksum (const uint16_t *data, unsigned int bytes) +{ + uint32_t sum; + unsigned int i; + + sum = 0; + for (i = 0; i < bytes / 2; i++) + sum += data[i]; + sum = (sum & 0xffff) + (sum >> 16); + sum = htons (0xffff - sum); + return sum; +} + + +/** + * Send an ICMP message to the target. + * + * @param my_ip source address + * @param other target address + */ +static void +send_icmp_udp (const struct in_addr *my_ip, const struct in_addr *other) +{ + char packet[sizeof(struct ip_header) * 2 + + sizeof(struct icmp_ttl_exceeded_header) + + sizeof(struct udp_header)]; + struct ip_header ip_pkt; + struct icmp_ttl_exceeded_header icmp_pkt; + struct udp_header udp_pkt; + struct sockaddr_in dst; + size_t off; + int err; + + /* ip header: send to (known) ip address */ + off = 0; + ip_pkt.vers_ihl = 0x45; + ip_pkt.tos = 0; + /* should this be BSD only? */ +#if defined(BSD) && defined(__FreeBSD__) && defined(__FreeBSD_kernel__) + ip_pkt.pkt_len = sizeof(packet); /* Workaround PR kern/21737 */ +#else + ip_pkt.pkt_len = htons (sizeof(packet)); +#endif + ip_pkt.id = htons (PACKET_ID); + ip_pkt.flags_frag_offset = 0; + ip_pkt.ttl = 128; + ip_pkt.proto = IPPROTO_ICMP; + ip_pkt.checksum = 0; + ip_pkt.src_ip = my_ip->s_addr; + ip_pkt.dst_ip = other->s_addr; + ip_pkt.checksum = + htons (calc_checksum ((uint16_t *) &ip_pkt, sizeof(struct ip_header))); + GNUNET_memcpy (&packet[off], + &ip_pkt, + sizeof(struct ip_header)); + off += sizeof(struct ip_header); + + icmp_pkt.type = ICMP_TIME_EXCEEDED; + icmp_pkt.code = 0; + icmp_pkt.checksum = 0; + icmp_pkt.unused = 0; + GNUNET_memcpy (&packet[off], + &icmp_pkt, + sizeof(struct icmp_ttl_exceeded_header)); + off += sizeof(struct icmp_ttl_exceeded_header); + + /* ip header of the presumably 'lost' udp packet */ + ip_pkt.vers_ihl = 0x45; + ip_pkt.tos = 0; + ip_pkt.pkt_len = + htons (sizeof(struct ip_header) + sizeof(struct udp_header)); + ip_pkt.id = htons (0); + ip_pkt.flags_frag_offset = 0; + ip_pkt.ttl = 128; + ip_pkt.proto = IPPROTO_UDP; + ip_pkt.checksum = 0; + ip_pkt.src_ip = other->s_addr; + ip_pkt.dst_ip = dummy.s_addr; + ip_pkt.checksum = + htons (calc_checksum ((uint16_t *) &ip_pkt, sizeof(struct ip_header))); + GNUNET_memcpy (&packet[off], + &ip_pkt, + sizeof(struct ip_header)); + off += sizeof(struct ip_header); + + /* build UDP header */ + udp_pkt.src_port = htons (NAT_TRAV_PORT); + udp_pkt.dst_port = htons (NAT_TRAV_PORT); + udp_pkt.length = htons (port); + udp_pkt.crc = 0; + GNUNET_memcpy (&packet[off], + &udp_pkt, + sizeof(struct udp_header)); + off += sizeof(struct udp_header); + + /* set ICMP checksum */ + icmp_pkt.checksum = + htons (calc_checksum + ((uint16_t *) &packet[sizeof(struct ip_header)], + sizeof(struct icmp_ttl_exceeded_header) + + sizeof(struct ip_header) + sizeof(struct udp_header))); + GNUNET_memcpy (&packet[sizeof(struct ip_header)], + &icmp_pkt, + sizeof(struct icmp_ttl_exceeded_header)); + + memset (&dst, 0, sizeof(dst)); + dst.sin_family = AF_INET; +#if HAVE_SOCKADDR_IN_SIN_LEN + dst.sin_len = sizeof(struct sockaddr_in); +#endif + dst.sin_addr = *other; + err = + sendto (rawsock, packet, sizeof(packet), 0, (struct sockaddr *) &dst, + sizeof(dst)); + if (err < 0) + { + fprintf (stderr, "sendto failed: %s\n", strerror (errno)); + } + else if (sizeof(packet) != (size_t) err) + { + fprintf (stderr, "Error: partial send of ICMP message with size %lu\n", + (unsigned long) off); + } +} + + +/** + * Send an ICMP message to the target. + * + * @param my_ip source address + * @param other target address + */ +static void +send_icmp (const struct in_addr *my_ip, const struct in_addr *other) +{ + struct ip_header ip_pkt; + struct icmp_ttl_exceeded_header icmp_ttl; + struct icmp_echo_header icmp_echo; + struct sockaddr_in dst; + char packet[sizeof(struct ip_header) * 2 + + sizeof(struct icmp_ttl_exceeded_header) + + sizeof(struct icmp_echo_header)]; + size_t off; + int err; + + /* ip header: send to (known) ip address */ + off = 0; + ip_pkt.vers_ihl = 0x45; + ip_pkt.tos = 0; +#if defined(BSD) && defined(__FreeBSD__) && defined(__FreeBSD_kernel__) + ip_pkt.pkt_len = sizeof(packet); /* Workaround PR kern/21737 */ +#else + ip_pkt.pkt_len = htons (sizeof(packet)); +#endif + ip_pkt.id = htons (PACKET_ID); + ip_pkt.flags_frag_offset = 0; + ip_pkt.ttl = IPDEFTTL; + ip_pkt.proto = IPPROTO_ICMP; + ip_pkt.checksum = 0; + ip_pkt.src_ip = my_ip->s_addr; + ip_pkt.dst_ip = other->s_addr; + ip_pkt.checksum = + htons (calc_checksum ((uint16_t *) &ip_pkt, sizeof(struct ip_header))); + GNUNET_memcpy (&packet[off], + &ip_pkt, + sizeof(struct ip_header)); + off = sizeof(ip_pkt); + + /* icmp reply: time exceeded */ + icmp_ttl.type = ICMP_TIME_EXCEEDED; + icmp_ttl.code = 0; + icmp_ttl.checksum = 0; + icmp_ttl.unused = 0; + GNUNET_memcpy (&packet[off], + &icmp_ttl, + sizeof(struct icmp_ttl_exceeded_header)); + off += sizeof(struct icmp_ttl_exceeded_header); + + /* ip header of the presumably 'lost' udp packet */ + ip_pkt.vers_ihl = 0x45; + ip_pkt.tos = 0; + ip_pkt.pkt_len = + htons (sizeof(struct ip_header) + sizeof(struct icmp_echo_header)); + ip_pkt.id = htons (PACKET_ID); + ip_pkt.flags_frag_offset = 0; + ip_pkt.ttl = 1; /* real TTL would be 1 on a time exceeded packet */ + ip_pkt.proto = IPPROTO_ICMP; + ip_pkt.src_ip = other->s_addr; + ip_pkt.dst_ip = dummy.s_addr; + ip_pkt.checksum = 0; + ip_pkt.checksum = + htons (calc_checksum ((uint16_t *) &ip_pkt, sizeof(struct ip_header))); + GNUNET_memcpy (&packet[off], + &ip_pkt, + sizeof(struct ip_header)); + off += sizeof(struct ip_header); + + icmp_echo.type = ICMP_ECHO; + icmp_echo.code = 0; + icmp_echo.reserved = htonl (port); + icmp_echo.checksum = 0; + icmp_echo.checksum = + htons (calc_checksum + ((uint16_t *) &icmp_echo, sizeof(struct icmp_echo_header))); + GNUNET_memcpy (&packet[off], + &icmp_echo, + sizeof(struct icmp_echo_header)); + + /* no go back to calculate ICMP packet checksum */ + off = sizeof(struct ip_header); + icmp_ttl.checksum = + htons (calc_checksum + ((uint16_t *) &packet[off], + sizeof(struct icmp_ttl_exceeded_header) + + sizeof(struct ip_header) + sizeof(struct icmp_echo_header))); + GNUNET_memcpy (&packet[off], + &icmp_ttl, + sizeof(struct icmp_ttl_exceeded_header)); + + /* prepare for transmission */ + memset (&dst, 0, sizeof(dst)); + dst.sin_family = AF_INET; +#if HAVE_SOCKADDR_IN_SIN_LEN + dst.sin_len = sizeof(struct sockaddr_in); +#endif + dst.sin_addr = *other; + err = + sendto (rawsock, packet, sizeof(packet), 0, (struct sockaddr *) &dst, + sizeof(dst)); + if (err < 0) + { + fprintf (stderr, "sendto failed: %s\n", strerror (errno)); + } + else if (sizeof(packet) != (size_t) err) + { + fprintf (stderr, "Error: partial send of ICMP message\n"); + } +} + + +int +main (int argc, char *const *argv) +{ + const int one = 1; + struct in_addr external; + struct in_addr target; + uid_t uid; + unsigned int p; + int raw_eno; + int global_ret; + + /* Create an ICMP raw socket for writing (only operation that requires root) */ + rawsock = socket (AF_INET, SOCK_RAW, IPPROTO_RAW); + raw_eno = errno; /* for later error checking */ + + /* now drop root privileges */ + uid = getuid (); +#ifdef HAVE_SETRESUID + if (0 != setresuid (uid, uid, uid)) + { + fprintf (stderr, "Failed to setresuid: %s\n", strerror (errno)); + global_ret = 1; + goto cleanup; + } +#else + if (0 != (setuid (uid) | seteuid (uid))) + { + fprintf (stderr, "Failed to setuid: %s\n", strerror (errno)); + global_ret = 2; + goto cleanup; + } +#endif + if (-1 == rawsock) + { + fprintf (stderr, "Error opening RAW socket: %s\n", strerror (raw_eno)); + global_ret = 3; + goto cleanup; + } + if (0 != + setsockopt (rawsock, SOL_SOCKET, SO_BROADCAST, (char *) &one, + sizeof(one))) + { + fprintf (stderr, "setsockopt failed: %s\n", strerror (errno)); + global_ret = 4; + goto cleanup; + } + if (0 != + setsockopt (rawsock, IPPROTO_IP, IP_HDRINCL, (char *) &one, sizeof(one))) + { + fprintf (stderr, "setsockopt failed: %s\n", strerror (errno)); + global_ret = 5; + goto cleanup; + } + + if (4 != argc) + { + fprintf (stderr, + "This program must be started with our IP, the targets external IP, and our port as arguments.\n"); + global_ret = 6; + goto cleanup; + } + if ((1 != inet_pton (AF_INET, argv[1], &external)) || + (1 != inet_pton (AF_INET, argv[2], &target))) + { + fprintf (stderr, "Error parsing IPv4 address: %s\n", strerror (errno)); + global_ret = 7; + goto cleanup; + } + if ((1 != sscanf (argv[3], "%u", &p)) || (0 == p) || (0xFFFF < p)) + { + fprintf (stderr, "Error parsing port value `%s'\n", argv[3]); + global_ret = 8; + goto cleanup; + } + port = (uint16_t) p; + if (1 != inet_pton (AF_INET, DUMMY_IP, &dummy)) + { + fprintf (stderr, "Internal error converting dummy IP to binary.\n"); + global_ret = 9; + goto cleanup; + } + send_icmp (&external, &target); + send_icmp_udp (&external, &target); + global_ret = 0; +cleanup: + if (-1 != rawsock) + (void) close (rawsock); + return global_ret; +} + + +/* end of gnunet-helper-nat-client.c */ diff --git a/src/service/nat/gnunet-helper-nat-server.c b/src/service/nat/gnunet-helper-nat-server.c new file mode 100644 index 000000000..d190a5dba --- /dev/null +++ b/src/service/nat/gnunet-helper-nat-server.c @@ -0,0 +1,715 @@ +/* + This file is part of GNUnet. + Copyright (C) 2010 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 . + + SPDX-License-Identifier: AGPL3.0-or-later + */ + +/** + * @file src/nat/gnunet-helper-nat-server.c + * @brief Tool to help bypass NATs using ICMP method; must run as root (SUID will do) + * This code will work under GNU/Linux only (or maybe BSDs, but never W32) + * @author Christian Grothoff + * + * This program will send ONE ICMP message every 500 ms RAW sockets + * to a DUMMY IP address and also listens for ICMP replies. Since + * it uses RAW sockets, it must be installed SUID or run as 'root'. + * In order to keep the security risk of the resulting SUID binary + * minimal, the program ONLY opens the two RAW sockets with root + * privileges, then drops them and only then starts to process + * command line arguments. The code also does not link against + * any shared libraries (except libc) and is strictly minimal + * (except for checking for errors). The following list of people + * have reviewed this code and considered it safe since the last + * modification (if you reviewed it, please have your name added + * to the list): + * + * - Christian Grothoff + * - Nathan Evans + * - Benjamin Kuperman (22 Aug 2010) + * - Jacob Appelbaum (19 Dec 2011) + */ +#if HAVE_CONFIG_H +/* Just needed for HAVE_SOCKADDR_IN_SIN_LEN test macro! */ +#include "platform.h" +#include "gnunet_private_config.h" +#else +#define _GNU_SOURCE +#endif +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* The following constant is missing from FreeBSD 9.2 */ +#ifndef ICMP_TIME_EXCEEDED +#define ICMP_TIME_EXCEEDED 11 +#endif + +/** + * Call memcpy() but check for @a n being 0 first. In the latter + * case, it is now safe to pass NULL for @a src or @a dst. + * Unlike traditional memcpy(), returns nothing. + * + * @param dst destination of the copy, may be NULL if @a n is zero + * @param src source of the copy, may be NULL if @a n is zero + * @param n number of bytes to copy + */ +#define GNUNET_memcpy(dst, src, n) do { if (0 != n) { (void) memcpy (dst, src, \ + n); \ + } } while (0) + +/** + * Should we print some debug output? + */ +#define VERBOSE 0 + +/** + * Must match packet ID used by gnunet-helper-nat-client.c + */ +#define PACKET_ID 256 + +/** + * Must match IP given in the client. + */ +#define DUMMY_IP "192.0.2.86" + +/** + * Port for UDP + */ +#define NAT_TRAV_PORT 22225 + +/** + * How often do we send our ICMP messages to receive replies? + */ +#define ICMP_SEND_FREQUENCY_MS 500 + +/** + * IPv4 header. + */ +struct ip_header +{ + /** + * Version (4 bits) + Internet header length (4 bits) + */ + uint8_t vers_ihl; + + /** + * Type of service + */ + uint8_t tos; + + /** + * Total length + */ + uint16_t pkt_len; + + /** + * Identification + */ + uint16_t id; + + /** + * Flags (3 bits) + Fragment offset (13 bits) + */ + uint16_t flags_frag_offset; + + /** + * Time to live + */ + uint8_t ttl; + + /** + * Protocol + */ + uint8_t proto; + + /** + * Header checksum + */ + uint16_t checksum; + + /** + * Source address + */ + uint32_t src_ip; + + /** + * Destination address + */ + uint32_t dst_ip; +}; + +/** + * Format of ICMP packet. + */ +struct icmp_ttl_exceeded_header +{ + uint8_t type; + + uint8_t code; + + uint16_t checksum; + + uint32_t unused; + + /* followed by original payload */ +}; + +struct icmp_echo_header +{ + uint8_t type; + + uint8_t code; + + uint16_t checksum; + + uint32_t reserved; +}; + + +/** + * Beginning of UDP packet. + */ +struct udp_header +{ + uint16_t src_port; + + uint16_t dst_port; + + uint16_t length; + + uint16_t crc; +}; + +/** + * Socket we use to receive "fake" ICMP replies. + */ +static int icmpsock; + +/** + * Socket we use to send our ICMP requests. + */ +static int rawsock; + +/** + * Socket we use to send our UDP requests. + */ +static int udpsock; + +/** + * Target "dummy" address. + */ +static struct in_addr dummy; + + +/** + * CRC-16 for IP/ICMP headers. + * + * @param data what to calculate the CRC over + * @param bytes number of bytes in data (must be multiple of 2) + * @return the CRC 16. + */ +static uint16_t +calc_checksum (const uint16_t *data, unsigned int bytes) +{ + uint32_t sum; + unsigned int i; + + sum = 0; + for (i = 0; i < bytes / 2; i++) + sum += data[i]; + sum = (sum & 0xffff) + (sum >> 16); + sum = htons (0xffff - sum); + return sum; +} + + +/** + * Send an ICMP message to the dummy IP. + * + * @param my_ip source address (our ip address) + */ +static void +send_icmp_echo (const struct in_addr *my_ip) +{ + char packet[sizeof(struct ip_header) + sizeof(struct icmp_echo_header)]; + struct icmp_echo_header icmp_echo; + struct ip_header ip_pkt; + struct sockaddr_in dst; + size_t off; + int err; + + off = 0; + ip_pkt.vers_ihl = 0x45; + ip_pkt.tos = 0; + ip_pkt.pkt_len = htons (sizeof(packet)); + ip_pkt.id = htons (PACKET_ID); + ip_pkt.flags_frag_offset = 0; + ip_pkt.ttl = IPDEFTTL; + ip_pkt.proto = IPPROTO_ICMP; + ip_pkt.checksum = 0; + ip_pkt.src_ip = my_ip->s_addr; + ip_pkt.dst_ip = dummy.s_addr; + ip_pkt.checksum = + htons (calc_checksum ((uint16_t *) &ip_pkt, + sizeof(struct ip_header))); + GNUNET_memcpy (&packet[off], + &ip_pkt, + sizeof(struct ip_header)); + off += sizeof(struct ip_header); + + icmp_echo.type = ICMP_ECHO; + icmp_echo.code = 0; + icmp_echo.checksum = 0; + icmp_echo.reserved = 0; + icmp_echo.checksum = + htons (calc_checksum + ((uint16_t *) &icmp_echo, + sizeof(struct icmp_echo_header))); + GNUNET_memcpy (&packet[off], + &icmp_echo, + sizeof(struct icmp_echo_header)); + off += sizeof(struct icmp_echo_header); + + memset (&dst, 0, sizeof(dst)); + dst.sin_family = AF_INET; +#if HAVE_SOCKADDR_IN_SIN_LEN + dst.sin_len = sizeof(struct sockaddr_in); +#endif + dst.sin_addr = dummy; + err = sendto (rawsock, + packet, + off, + 0, + (struct sockaddr *) &dst, + sizeof(dst)); + if (err < 0) + { +#if VERBOSE + fprintf (stderr, + "sendto failed: %s\n", + strerror (errno)); +#endif + } + else if (sizeof(packet) != err) + { + fprintf (stderr, + "Error: partial send of ICMP message\n"); + } +} + + +/** + * Send a UDP message to the dummy IP. + */ +static void +send_udp () +{ + struct sockaddr_in dst; + ssize_t err; + + memset (&dst, 0, sizeof(dst)); + dst.sin_family = AF_INET; +#if HAVE_SOCKADDR_IN_SIN_LEN + dst.sin_len = sizeof(struct sockaddr_in); +#endif + dst.sin_addr = dummy; + dst.sin_port = htons (NAT_TRAV_PORT); + err = sendto (udpsock, + NULL, + 0, + 0, + (struct sockaddr *) &dst, + sizeof(dst)); + if (err < 0) + { +#if VERBOSE + fprintf (stderr, + "sendto failed: %s\n", + strerror (errno)); +#endif + } + else if (0 != err) + { + fprintf (stderr, + "Error: partial send of ICMP message\n"); + } +} + + +/** + * We've received an ICMP response. Process it. + */ +static void +process_icmp_response () +{ + char buf[65536]; + ssize_t have; + struct in_addr source_ip; + struct ip_header ip_pkt; + struct icmp_ttl_exceeded_header icmp_ttl; + struct icmp_echo_header icmp_echo; + struct udp_header udp_pkt; + size_t off; + uint16_t port; + + have = read (icmpsock, buf, sizeof(buf)); + if (-1 == have) + { + fprintf (stderr, + "Error reading raw socket: %s\n", + strerror (errno)); + return; + } +#if VERBOSE + fprintf (stderr, + "Received message of %u bytes\n", + (unsigned int) have); +#endif + if (have < + (ssize_t) (sizeof(struct ip_header) + + sizeof(struct icmp_ttl_exceeded_header) + + sizeof(struct ip_header))) + { + /* malformed */ + return; + } + off = 0; + GNUNET_memcpy (&ip_pkt, + &buf[off], + sizeof(struct ip_header)); + off += sizeof(struct ip_header); + GNUNET_memcpy (&icmp_ttl, + &buf[off], + sizeof(struct icmp_ttl_exceeded_header)); + off += sizeof(struct icmp_ttl_exceeded_header); + if ((ICMP_TIME_EXCEEDED != icmp_ttl.type) || (0 != icmp_ttl.code)) + { + /* different type than what we want */ + return; + } + /* grab source IP of 1st IP header */ + source_ip.s_addr = ip_pkt.src_ip; + + /* skip 2nd IP header */ + GNUNET_memcpy (&ip_pkt, + &buf[off], + sizeof(struct ip_header)); + off += sizeof(struct ip_header); + + switch (ip_pkt.proto) + { + case IPPROTO_ICMP: + if (have != + (sizeof(struct ip_header) * 2 + + sizeof(struct icmp_ttl_exceeded_header) + + sizeof(struct icmp_echo_header))) + { + /* malformed */ + return; + } + /* grab ICMP ECHO content */ + GNUNET_memcpy (&icmp_echo, + &buf[off], + sizeof(struct icmp_echo_header)); + port = (uint16_t) ntohl (icmp_echo.reserved); + break; + + case IPPROTO_UDP: + if (have != + (sizeof(struct ip_header) * 2 + + sizeof(struct icmp_ttl_exceeded_header) + sizeof(struct udp_header))) + { + /* malformed */ + return; + } + /* grab UDP content */ + GNUNET_memcpy (&udp_pkt, + &buf[off], + sizeof(struct udp_header)); + port = ntohs (udp_pkt.length); + break; + + default: + /* different type than what we want */ + return; + } + + if (port == 0) + fprintf (stdout, "%s\n", + inet_ntop (AF_INET, &source_ip, buf, sizeof(buf))); + else + fprintf (stdout, "%s:%u\n", + inet_ntop (AF_INET, &source_ip, buf, sizeof(buf)), + (unsigned int) port); + fflush (stdout); +} + + +/** + * Fully initialize the raw socket. + * + * @return -1 on error, 0 on success + */ +static int +setup_raw_socket () +{ + const int one = 1; + + if (-1 == + setsockopt (rawsock, + SOL_SOCKET, + SO_BROADCAST, + (char *) &one, + sizeof(one))) + { + fprintf (stderr, + "setsockopt failed: %s\n", + strerror (errno)); + return -1; + } + if (-1 == + setsockopt (rawsock, + IPPROTO_IP, + IP_HDRINCL, + (char *) &one, + sizeof(one))) + { + fprintf (stderr, + "setsockopt failed: %s\n", + strerror (errno)); + return -1; + } + return 0; +} + + +/** + * Create a UDP socket for writing. + * + * @param my_ip source address (our ip address) + * @return -1 on error + */ +static int +make_udp_socket (const struct in_addr *my_ip) +{ + int ret; + struct sockaddr_in addr; + + ret = socket (AF_INET, SOCK_DGRAM, 0); + if (-1 == ret) + { + fprintf (stderr, + "Error opening UDP socket: %s\n", + strerror (errno)); + return -1; + } + memset (&addr, 0, sizeof(addr)); + addr.sin_family = AF_INET; +#if HAVE_SOCKADDR_IN_SIN_LEN + addr.sin_len = sizeof(struct sockaddr_in); +#endif + addr.sin_addr = *my_ip; + addr.sin_port = htons (NAT_TRAV_PORT); + + if (0 != bind (ret, + (struct sockaddr *) &addr, + sizeof(addr))) + { + fprintf (stderr, + "Error binding UDP socket to port %u: %s\n", + NAT_TRAV_PORT, + strerror (errno)); + (void) close (ret); + return -1; + } + return ret; +} + + +int +main (int argc, + char *const *argv) +{ + struct in_addr external; + fd_set rs; + struct timeval tv; + uid_t uid; + unsigned int alt; + int icmp_eno; + int raw_eno; + int global_ret; + + /* Create an ICMP raw socket for reading (we'll check errors later) */ + icmpsock = socket (AF_INET, + SOCK_RAW, + IPPROTO_ICMP); + icmp_eno = errno; + + /* Create an (ICMP) raw socket for writing (we'll check errors later) */ + rawsock = socket (AF_INET, + SOCK_RAW, + IPPROTO_RAW); + raw_eno = errno; + udpsock = -1; + + /* drop root rights */ + uid = getuid (); +#ifdef HAVE_SETRESUID + if (0 != setresuid (uid, uid, uid)) + { + fprintf (stderr, + "Failed to setresuid: %s\n", + strerror (errno)); + global_ret = 1; + goto error_exit; + } +#else + if (0 != (setuid (uid) | seteuid (uid))) + { + fprintf (stderr, + "Failed to setuid: %s\n", + strerror (errno)); + global_ret = 2; + goto error_exit; + } +#endif + + /* Now that we run without root rights, we can do error checking... */ + if (2 != argc) + { + fprintf (stderr, + "This program must be started with our (internal NAT) IP as the only argument.\n"); + global_ret = 3; + goto error_exit; + } + if (1 != inet_pton (AF_INET, argv[1], &external)) + { + fprintf (stderr, + "Error parsing IPv4 address: %s\n", + strerror (errno)); + global_ret = 4; + goto error_exit; + } + if (1 != inet_pton (AF_INET, DUMMY_IP, &dummy)) + { + fprintf (stderr, + "Internal error converting dummy IP to binary.\n"); + global_ret = 5; + goto error_exit; + } + + /* error checking icmpsock */ + if (-1 == icmpsock) + { + fprintf (stderr, + "Error opening RAW socket: %s\n", + strerror (icmp_eno)); + global_ret = 6; + goto error_exit; + } + if (icmpsock >= FD_SETSIZE) + { + /* this could happen if we were started with a large number of already-open + file descriptors... */ + fprintf (stderr, + "Socket number too large (%d > %u)\n", + icmpsock, + (unsigned int) FD_SETSIZE); + global_ret = 7; + goto error_exit; + } + + /* error checking rawsock */ + if (-1 == rawsock) + { + fprintf (stderr, + "Error opening RAW socket: %s\n", + strerror (raw_eno)); + global_ret = 8; + goto error_exit; + } + /* no need to check 'rawsock' against FD_SETSIZE as it is never used + with 'select' */ + + if (0 != setup_raw_socket ()) + { + global_ret = 9; + goto error_exit; + } + + if (-1 == (udpsock = make_udp_socket (&external))) + { + global_ret = 10; + goto error_exit; + } + + alt = 0; + while (1) + { + FD_ZERO (&rs); + FD_SET (icmpsock, &rs); + tv.tv_sec = 0; + tv.tv_usec = ICMP_SEND_FREQUENCY_MS * 1000; + if (-1 == select (icmpsock + 1, &rs, NULL, NULL, &tv)) + { + if (errno == EINTR) + continue; + fprintf (stderr, + "select failed: %s\n", + strerror (errno)); + break; + } + if (1 == getppid ()) /* Check the parent process id, if 1 the parent has died, so we should die too */ + break; + if (FD_ISSET (icmpsock, &rs)) + { + process_icmp_response (); + continue; + } + if (0 == (++alt % 2)) + send_icmp_echo (&external); + else + send_udp (); + } + + /* select failed (internal error or OS out of resources) */ + global_ret = 11; +error_exit: + if (-1 != icmpsock) + (void) close (icmpsock); + if (-1 != rawsock) + (void) close (rawsock); + if (-1 != udpsock) + (void) close (udpsock); + return global_ret; +} + + +/* end of gnunet-helper-nat-server.c */ diff --git a/src/service/nat/gnunet-nat-client-script.sh b/src/service/nat/gnunet-nat-client-script.sh new file mode 100755 index 000000000..4e4ccafad --- /dev/null +++ b/src/service/nat/gnunet-nat-client-script.sh @@ -0,0 +1,4 @@ +#!/bin/sh +IP=`ifconfig | grep inet | head -n1 | awk '{print $2}' | sed -e "s/addr://"` +echo "Using IP $IP, trying to connect to $1" +./gnunet-nat-client-udp $IP $1 diff --git a/src/service/nat/gnunet-nat-server-script.sh b/src/service/nat/gnunet-nat-server-script.sh new file mode 100755 index 000000000..42a8e639a --- /dev/null +++ b/src/service/nat/gnunet-nat-server-script.sh @@ -0,0 +1,4 @@ +#!/bin/sh +IP=`ifconfig | grep inet | head -n1 | awk '{print $2}' | sed -e "s/addr://"` +echo "Using IP $IP" +./gnunet-nat-server $IP | sed -u -e "s/.*/.\/gnunet-nat-server-udp $IP &\&/" | sh diff --git a/src/service/nat/gnunet-service-nat.c b/src/service/nat/gnunet-service-nat.c new file mode 100644 index 000000000..dde269819 --- /dev/null +++ b/src/service/nat/gnunet-service-nat.c @@ -0,0 +1,2083 @@ +/* + This file is part of GNUnet. + Copyright (C) 2016, 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 . + + SPDX-License-Identifier: AGPL3.0-or-later + */ + +/** + * @file nat/gnunet-service-nat.c + * @brief network address translation traversal service + * @author Christian Grothoff + * + * The purpose of this service is to enable transports to + * traverse NAT routers, by providing traversal options and + * knowledge about the local network topology. + * + * TODO: + * - migrate test cases to new NAT service + * - add new traceroute-based logic for external IP detection + * + * - implement & test STUN processing to classify NAT; + * basically, open port & try different methods. + */ +#include "platform.h" +#include +#include "gnunet_util_lib.h" +#include "gnunet_protocols.h" +#include "gnunet_signatures.h" +#include "gnunet_statistics_service.h" +#include "gnunet_resolver_service.h" +#include "gnunet_nat_service.h" +#include "gnunet-service-nat.h" +#include "gnunet-service-nat_externalip.h" +#include "gnunet-service-nat_stun.h" +#include "gnunet-service-nat_mini.h" +#include "gnunet-service-nat_helper.h" +#include "nat.h" +#include + + +/** + * How often should we ask the OS about a list of active + * network interfaces? + */ +#define SCAN_FREQ GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS, 15) + +/** + * How long do we wait until we forcefully terminate autoconfiguration? + */ +#define AUTOCONFIG_TIMEOUT GNUNET_TIME_relative_multiply ( \ + GNUNET_TIME_UNIT_SECONDS, 5) + +/** + * How often do we scan for changes in how our external (dyndns) hostname resolves? + */ +#define DYNDNS_FREQUENCY GNUNET_TIME_relative_multiply ( \ + GNUNET_TIME_UNIT_MINUTES, 7) + + +/** + * Information we track per client address. + */ +struct ClientAddress +{ + /** + * Network address used by the client. + */ + struct sockaddr_storage ss; + + /** + * Handle to active UPnP request where we asked upnpc to open + * a port at the NAT. NULL if we do not have such a request + * pending. + */ + struct GNUNET_NAT_MiniHandle *mh; +}; + + +/** + * List of local addresses this system has. + */ +struct LocalAddressList +{ + /** + * This is a linked list. + */ + struct LocalAddressList *next; + + /** + * Previous entry. + */ + struct LocalAddressList *prev; + + /** + * Context for a gnunet-helper-nat-server used to listen + * for ICMP messages to this client for connection reversal. + */ + struct HelperContext *hc; + + /** + * The address itself (i.e. `struct sockaddr_in` or `struct + * sockaddr_in6`, in the respective byte order). + */ + struct sockaddr_storage addr; + + /** + * Address family. (FIXME: redundant, addr.ss_family! Remove!?) + */ + int af; + + /** + * #GNUNET_YES if we saw this one in the previous iteration, + * but not in the current iteration and thus might need to + * remove it at the end. + */ + int old; + + /** + * What type of address is this? + */ + enum GNUNET_NAT_AddressClass ac; +}; + + +/** + * Internal data structure we track for each of our clients. + */ +struct ClientHandle +{ + /** + * Kept in a DLL. + */ + struct ClientHandle *next; + + /** + * Kept in a DLL. + */ + struct ClientHandle *prev; + + /** + * Underlying handle for this client with the service. + */ + struct GNUNET_SERVICE_Client *client; + + /** + * Message queue for communicating with the client. + */ + struct GNUNET_MQ_Handle *mq; + + /** + * Array of addresses used by the service. + */ + struct ClientAddress *caddrs; + + /** + * External DNS name and port given by user due to manual + * hole punching. Special DNS name 'AUTO' is used to indicate + * desire for automatic determination of the external IP + * (instead of DNS or manual configuration, i.e. to be used + * if the IP keeps changing and we have no DynDNS, but we do + * have a hole punched). + */ + char *hole_external; + + /** + * Name of the configuration section this client cares about. + */ + char *section_name; + + /** + * Task for periodically re-running the @e ext_dns DNS lookup. + */ + struct GNUNET_SCHEDULER_Task *ext_dns_task; + + /** + * Handle for (DYN)DNS lookup of our external IP as given in + * @e hole_external. + */ + struct GNUNET_RESOLVER_RequestHandle *ext_dns; + + /** + * Handle for monitoring external IP changes. + */ + struct GN_ExternalIPMonitor *external_monitor; + + /** + * DLL of external IP addresses as given in @e hole_external. + */ + struct LocalAddressList *ext_addr_head; + + /** + * DLL of external IP addresses as given in @e hole_external. + */ + struct LocalAddressList *ext_addr_tail; + + /** + * Port number we found in @e hole_external. + */ + uint16_t ext_dns_port; + + /** + * What does this client care about? + */ + enum GNUNET_NAT_RegisterFlags flags; + + /** + * Is any of the @e caddrs in a reserved subnet for NAT? + */ + int natted_address; + + /** + * Number of addresses that this service is bound to. + * Length of the @e caddrs array. + */ + uint16_t num_caddrs; + + /** + * Client's IPPROTO, e.g. IPPROTO_UDP or IPPROTO_TCP. + */ + uint8_t proto; +}; + + +/** + * External IP address as given to us via some STUN server. + */ +struct StunExternalIP +{ + /** + * Kept in a DLL. + */ + struct StunExternalIP *next; + + /** + * Kept in a DLL. + */ + struct StunExternalIP *prev; + + /** + * Task we run to remove this entry when it is stale. + */ + struct GNUNET_SCHEDULER_Task *timeout_task; + + /** + * Our external IP address as reported by the + * STUN server. + */ + struct sockaddr_in external_addr; + + /** + * Address of the reporting STUN server. Used to + * detect when a STUN server changes its opinion + * to more quickly remove stale results. + */ + struct sockaddr_storage stun_server_addr; + + /** + * Number of bytes used in @e stun_server_addr. + */ + size_t stun_server_addr_len; +}; + + +/** + * Timeout to use when STUN data is considered stale. + */ +static struct GNUNET_TIME_Relative stun_stale_timeout; + +/** + * How often do we scan for changes in how our external (dyndns) hostname resolves? + */ +static struct GNUNET_TIME_Relative dyndns_frequency; + +/** + * Handle to our current configuration. + */ +static const struct GNUNET_CONFIGURATION_Handle *cfg; + +/** + * Handle to the statistics service. + */ +static struct GNUNET_STATISTICS_Handle *stats; + +/** + * Task scheduled to periodically scan our network interfaces. + */ +static struct GNUNET_SCHEDULER_Task *scan_task; + +/** + * Head of client DLL. + */ +static struct ClientHandle *ch_head; + +/** + * Tail of client DLL. + */ +static struct ClientHandle *ch_tail; + +/** + * Head of DLL of local addresses. + */ +static struct LocalAddressList *lal_head; + +/** + * Tail of DLL of local addresses. + */ +static struct LocalAddressList *lal_tail; + +/** + * Kept in a DLL. + */ +static struct StunExternalIP *se_head; + +/** + * Kept in a DLL. + */ +static struct StunExternalIP *se_tail; + +/** + * Is UPnP enabled? #GNUNET_YES if enabled, #GNUNET_NO if disabled, + * #GNUNET_SYSERR if configuration enabled but binary is unavailable. + */ +int enable_upnp; + +/** + * Is IP Scanning enabled? #GNUNET_YES if enabled, #GNUNET_NO if disabled, + * without, only explicitly specified IPs will be handled (HOLE_EXTERNAL) + */ +int enable_ipscan; + +/** + * Remove and free an entry from the #lal_head DLL. + * + * @param lal entry to free + */ +static void +free_lal (struct LocalAddressList *lal) +{ + GNUNET_CONTAINER_DLL_remove (lal_head, + lal_tail, + lal); + if (NULL != lal->hc) + { + GNUNET_log (GNUNET_ERROR_TYPE_MESSAGE, + "Lost NATed local address %s, stopping NAT server\n", + GNUNET_a2s ((const struct sockaddr *) &lal->addr, + sizeof(struct sockaddr_in))); + + GN_stop_gnunet_nat_server_ (lal->hc); + lal->hc = NULL; + } + GNUNET_free (lal); +} + + +/** + * Free the DLL starting at #lal_head. + */ +static void +destroy_lal () +{ + struct LocalAddressList *lal; + + while (NULL != (lal = lal_head)) + free_lal (lal); +} + + +/** + * Check validity of #GNUNET_MESSAGE_TYPE_NAT_REGISTER message from + * client. + * + * @param cls client who sent the message + * @param message the message received + * @return #GNUNET_OK if message is well-formed + */ +static int +check_register (void *cls, + const struct GNUNET_NAT_RegisterMessage *message) +{ + uint16_t num_addrs = ntohs (message->num_addrs); + const char *off = (const char *) &message[1]; + size_t left = ntohs (message->header.size) - sizeof(*message); + + for (unsigned int i = 0; i < num_addrs; i++) + { + size_t alen; + const struct sockaddr *sa = (const struct sockaddr *) off; + + if (sizeof(sa_family_t) > left) + { + GNUNET_break (0); + return GNUNET_SYSERR; + } + switch (sa->sa_family) + { + case AF_INET: + alen = sizeof(struct sockaddr_in); + break; + + case AF_INET6: + alen = sizeof(struct sockaddr_in6); + break; + +#if AF_UNIX + case AF_UNIX: + alen = sizeof(struct sockaddr_un); + break; +#endif + default: + GNUNET_break (0); + return GNUNET_SYSERR; + } + if (alen > left) + { + GNUNET_break (0); + return GNUNET_SYSERR; + } + off += alen; + left -= alen; + } + if (left != ntohs (message->str_len)) + { + GNUNET_break (0); + return GNUNET_SYSERR; + } + return GNUNET_OK; +} + + +/** + * Check if @a ip is in @a network with @a bits netmask. + * + * @param network to test + * @param ip IP address to test + * @param bits bitmask for the network + * @return #GNUNET_YES if @a ip is in @a network + */ +static int +match_ipv4 (const char *network, + const struct in_addr *ip, + uint8_t bits) +{ + struct in_addr net; + + if (0 == ip->s_addr) + return GNUNET_YES; + if (0 == bits) + return GNUNET_YES; + GNUNET_assert (1 == inet_pton (AF_INET, + network, + &net)); + return ! ((ip->s_addr ^ net.s_addr) & htonl (0xFFFFFFFFu << (32 - bits))); +} + + +/** + * Check if @a ip is in @a network with @a bits netmask. + * + * @param network to test + * @param ip IP address to test + * @param bits bitmask for the network + * @return #GNUNET_YES if @a ip is in @a network + */ +static int +match_ipv6 (const char *network, + const struct in6_addr *ip, + uint8_t bits) +{ + struct in6_addr net; + struct in6_addr mask; + unsigned int off; + + if (0 == bits) + return GNUNET_YES; + GNUNET_assert (1 == inet_pton (AF_INET6, + network, + &net)); + memset (&mask, 0, sizeof(mask)); + if (0 == GNUNET_memcmp (&mask, + ip)) + return GNUNET_YES; + off = 0; + while (bits > 8) + { + mask.s6_addr[off++] = 0xFF; + bits -= 8; + } + while (bits > 0) + { + mask.s6_addr[off] = (mask.s6_addr[off] >> 1) + 0x80; + bits--; + } + for (unsigned j = 0; j < sizeof(struct in6_addr) / sizeof(uint32_t); j++) + if (((((uint32_t *) ip)[j] & ((uint32_t *) &mask)[j])) != + (((uint32_t *) &net)[j] & ((int *) &mask)[j])) + return GNUNET_NO; + return GNUNET_YES; +} + + +/** + * Test if the given IPv4 address is in a known range + * for private networks. + * + * @param ip address to test + * @return #GNUNET_YES if @a ip is in a NAT range + */ +static int +is_nat_v4 (const struct in_addr *ip) +{ + return + match_ipv4 ("10.0.0.0", ip, 8) || /* RFC 1918 */ + match_ipv4 ("100.64.0.0", ip, 10) || /* CG-NAT, RFC 6598 */ + match_ipv4 ("192.168.0.0", ip, 12) || /* RFC 1918 */ + match_ipv4 ("169.254.0.0", ip, 16) || /* AUTO, RFC 3927 */ + match_ipv4 ("172.16.0.0", ip, 16); /* RFC 1918 */ +} + + +/** + * Test if the given IPv6 address is in a known range + * for private networks. + * + * @param ip address to test + * @return #GNUNET_YES if @a ip is in a NAT range + */ +static int +is_nat_v6 (const struct in6_addr *ip) +{ + return + match_ipv6 ("fc00::", ip, 7) || /* RFC 4193 */ + match_ipv6 ("fec0::", ip, 10) || /* RFC 3879 */ + match_ipv6 ("fe80::", ip, 10); /* RFC 4291, link-local */ +} + + +/** + * Closure for #ifc_proc. + */ +struct IfcProcContext +{ + /** + * Head of DLL of local addresses. + */ + struct LocalAddressList *lal_head; + + /** + * Tail of DLL of local addresses. + */ + struct LocalAddressList *lal_tail; +}; + + +/** + * Callback function invoked for each interface found. Adds them + * to our new address list. + * + * @param cls a `struct IfcProcContext *` + * @param name name of the interface (can be NULL for unknown) + * @param isDefault is this presumably the default interface + * @param addr address of this interface (can be NULL for unknown or unassigned) + * @param broadcast_addr the broadcast address (can be NULL for unknown or unassigned) + * @param netmask the network mask (can be NULL for unknown or unassigned) + * @param addrlen length of the address + * @return #GNUNET_OK to continue iteration, #GNUNET_SYSERR to abort + */ +static int +ifc_proc (void *cls, + const char *name, + int isDefault, + const struct sockaddr *addr, + const struct sockaddr *broadcast_addr, + const struct sockaddr *netmask, + socklen_t addrlen) +{ + struct IfcProcContext *ifc_ctx = cls; + struct LocalAddressList *lal; + size_t alen; + const struct in_addr *ip4; + const struct in6_addr *ip6; + enum GNUNET_NAT_AddressClass ac; + + switch (addr->sa_family) + { + case AF_INET: + alen = sizeof(struct sockaddr_in); + ip4 = &((const struct sockaddr_in *) addr)->sin_addr; + if (match_ipv4 ("127.0.0.0", ip4, 8)) + ac = GNUNET_NAT_AC_LOOPBACK; + else if (is_nat_v4 (ip4)) + ac = GNUNET_NAT_AC_LAN; + else + ac = GNUNET_NAT_AC_GLOBAL; + break; + + case AF_INET6: + alen = sizeof(struct sockaddr_in6); + ip6 = &((const struct sockaddr_in6 *) addr)->sin6_addr; + if (match_ipv6 ("::1", ip6, 128)) + ac = GNUNET_NAT_AC_LOOPBACK; + else if (is_nat_v6 (ip6)) + ac = GNUNET_NAT_AC_LAN; + else + ac = GNUNET_NAT_AC_GLOBAL; + if ((ip6->s6_addr[11] == 0xFF) && + (ip6->s6_addr[12] == 0xFE)) + { + /* contains a MAC, be extra careful! */ + ac |= GNUNET_NAT_AC_PRIVATE; + } + break; + +#if AF_UNIX + case AF_UNIX: + GNUNET_break (0); + return GNUNET_OK; +#endif + default: + GNUNET_break (0); + return GNUNET_OK; + } + lal = GNUNET_malloc (sizeof(*lal)); + lal->af = addr->sa_family; + lal->ac = ac; + GNUNET_memcpy (&lal->addr, + addr, + alen); + GNUNET_CONTAINER_DLL_insert (ifc_ctx->lal_head, + ifc_ctx->lal_tail, + lal); + return GNUNET_OK; +} + + +/** + * Notify client about a change in the list of addresses this peer + * has. + * + * @param ac address class of the entry in the list that changed + * @param ch client to contact + * @param add #GNUNET_YES to add, #GNUNET_NO to remove + * @param addr the address that changed + * @param addr_len number of bytes in @a addr + */ +static void +notify_client (enum GNUNET_NAT_AddressClass ac, + struct ClientHandle *ch, + int add, + const void *addr, + size_t addr_len) +{ + struct GNUNET_MQ_Envelope *env; + struct GNUNET_NAT_AddressChangeNotificationMessage *msg; + + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Notifying client about %s of IP %s\n", + add ? "addition" : "removal", + GNUNET_a2s (addr, + addr_len)); + env = GNUNET_MQ_msg_extra (msg, + addr_len, + GNUNET_MESSAGE_TYPE_NAT_ADDRESS_CHANGE); + msg->add_remove = htonl (add); + msg->addr_class = htonl (ac); + GNUNET_memcpy (&msg[1], + addr, + addr_len); + GNUNET_MQ_send (ch->mq, + env); +} + + +/** + * Check if we should bother to notify this client about this + * address change, and if so, do it. + * + * @param delta the entry in the list that changed + * @param ch client to check + * @param add #GNUNET_YES to add, #GNUNET_NO to remove + */ +static void +check_notify_client (struct LocalAddressList *delta, + struct ClientHandle *ch, + int add) +{ + size_t alen; + struct sockaddr_in v4; + struct sockaddr_in6 v6; + + if (0 == (ch->flags & GNUNET_NAT_RF_ADDRESSES)) + { + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Not notifying client as it does not care about addresses\n"); + return; + } + switch (delta->af) + { + case AF_INET: + alen = sizeof(struct sockaddr_in); + GNUNET_memcpy (&v4, + &delta->addr, + alen); + + /* Check for client notifications */ + for (unsigned int i = 0; i < ch->num_caddrs; i++) + { + const struct sockaddr_in *c4; + + if (AF_INET != ch->caddrs[i].ss.ss_family) + continue; /* IPv4 not relevant */ + c4 = (const struct sockaddr_in *) &ch->caddrs[i].ss; + if (match_ipv4 ("127.0.0.1", &c4->sin_addr, 8) && + (0 != c4->sin_addr.s_addr) && + (! match_ipv4 ("127.0.0.1", &v4.sin_addr, 8))) + continue; /* bound to loopback, but this is not loopback */ + if ((! match_ipv4 ("127.0.0.1", &c4->sin_addr, 8)) && + match_ipv4 ("127.0.0.1", &v4.sin_addr, 8)) + continue; /* bound to non-loopback, but this is loopback */ + if ((0 != (delta->ac & GNUNET_NAT_AC_EXTERN)) && + (0 != c4->sin_addr.s_addr) && + (! is_nat_v4 (&v4.sin_addr))) + continue; /* based on external-IP, but this IP is not + from private address range. */ + if ((0 != GNUNET_memcmp (&v4.sin_addr, + &c4->sin_addr)) && + (0 != c4->sin_addr.s_addr) && + (! is_nat_v4 (&c4->sin_addr))) + continue; /* this IP is not from private address range, + and IP does not match. */ + + /* OK, IP seems relevant, notify client */ + if (0 == htons (v4.sin_port)) + v4.sin_port = c4->sin_port; + notify_client (delta->ac, + ch, + add, + &v4, + alen); + } + break; + + case AF_INET6: + alen = sizeof(struct sockaddr_in6); + GNUNET_memcpy (&v6, + &delta->addr, + alen); + for (unsigned int i = 0; i < ch->num_caddrs; i++) + { + const struct sockaddr_in6 *c6; + + if (AF_INET6 != ch->caddrs[i].ss.ss_family) + continue; /* IPv4 not relevant */ + c6 = (const struct sockaddr_in6 *) &ch->caddrs[i].ss; + if (match_ipv6 ("::1", &c6->sin6_addr, 128) && + (0 != GNUNET_memcmp (&c6->sin6_addr, + &in6addr_any)) && + (! match_ipv6 ("::1", &v6.sin6_addr, 128))) + continue; /* bound to loopback, but this is not loopback */ + if ((! match_ipv6 ("::1", &c6->sin6_addr, 128)) && + match_ipv6 ("::1", &v6.sin6_addr, 128)) + continue; /* bound to non-loopback, but this is loopback */ + if ((0 != (delta->ac & GNUNET_NAT_AC_EXTERN)) && + (0 != GNUNET_memcmp (&c6->sin6_addr, + &in6addr_any)) && + (! is_nat_v6 (&v6.sin6_addr))) + continue; /* based on external-IP, but this IP is not + from private address range. */ + if ((0 != GNUNET_memcmp (&v6.sin6_addr, + &c6->sin6_addr)) && + (0 != GNUNET_memcmp (&c6->sin6_addr, + &in6addr_any)) && + (! is_nat_v6 (&c6->sin6_addr))) + continue; /* this IP is not from private address range, + and IP does not match. */ + if ((match_ipv6 ("fe80::", &c6->sin6_addr, 10)) && + (0 != GNUNET_memcmp (&c6->sin6_addr, + &in6addr_any)) && + (0 != GNUNET_memcmp (&v6.sin6_addr, + &c6->sin6_addr)) && + (0 == (delta->ac & GNUNET_NAT_AC_EXTERN))) + continue; /* client bound to link-local, and the other address + does not match and is not an external IP */ + + /* OK, IP seems relevant, notify client */ + if (0 == htons (v6.sin6_port)) + v6.sin6_port = c6->sin6_port; + notify_client (delta->ac, + ch, + add, + &v6, + alen); + } + break; + + default: + GNUNET_break (0); + return; + } +} + + +/** + * Notify all clients about a change in the list + * of addresses this peer has. + * + * @param delta the entry in the list that changed + * @param add #GNUNET_YES to add, #GNUNET_NO to remove + */ +static void +notify_clients (struct LocalAddressList *delta, + int add) +{ + for (struct ClientHandle *ch = ch_head; + NULL != ch; + ch = ch->next) + check_notify_client (delta, + ch, + add); +} + + +/** + * Tell relevant client about a change in our external + * IPv4 address. + * + * @param cls client to check if it cares and possibly notify + * @param v4 the external address that changed + * @param add #GNUNET_YES to add, #GNUNET_NO to remove + */ +static void +notify_client_external_ipv4_change (void *cls, + const struct in_addr *v4, + int add) +{ + struct ClientHandle *ch = cls; + struct sockaddr_in sa; + int have_v4; + + /* (0) check if this impacts 'hole_external' */ + if ((NULL != ch->hole_external) && + (0 == strcasecmp (ch->hole_external, + "AUTO"))) + { + struct LocalAddressList lal; + struct sockaddr_in *s4; + + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Detected eternal IP, can now back-fill AUTO:%u in hole punching specification of `%s'\n", + (unsigned int) ch->ext_dns_port, + ch->section_name); + memset (&lal, 0, sizeof(lal)); + s4 = (struct sockaddr_in *) &lal.addr; + s4->sin_family = AF_INET; + s4->sin_port = htons (ch->ext_dns_port); + s4->sin_addr = *v4; + lal.af = AF_INET; + lal.ac = GNUNET_NAT_AC_GLOBAL | GNUNET_NAT_AC_MANUAL; + check_notify_client (&lal, + ch, + add); + } + + /* (1) check if client cares. */ + if (! ch->natted_address) + return; + have_v4 = GNUNET_NO; + for (unsigned int i = 0; i < ch->num_caddrs; i++) + { + const struct sockaddr_storage *ss = &ch->caddrs[i].ss; + + if (AF_INET != ss->ss_family) + continue; + have_v4 = GNUNET_YES; + break; + } + if (GNUNET_NO == have_v4) + return; /* IPv6-only */ + + /* (2) build address info */ + memset (&sa, + 0, + sizeof(sa)); + sa.sin_family = AF_INET; + sa.sin_addr = *v4; + sa.sin_port = htons (0); + + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Detected eternal IP %s, notifying client of external IP (without port)\n", + GNUNET_a2s ((const struct sockaddr *) &sa, + sizeof(sa))); + /* (3) notify client of change */ + notify_client (is_nat_v4 (v4) + ? GNUNET_NAT_AC_EXTERN | GNUNET_NAT_AC_LAN + : GNUNET_NAT_AC_EXTERN | GNUNET_NAT_AC_GLOBAL, + ch, + add, + &sa, + sizeof(sa)); +} + + +/** + * We got a connection reversal request from another peer. + * Notify applicable clients. + * + * @param cls closure with the `struct LocalAddressList` + * @param ra IP address of the peer who wants us to connect to it + */ +static void +reversal_callback (void *cls, + const struct sockaddr_in *ra) +{ + struct LocalAddressList *lal = cls; + const struct sockaddr_in *l4; + + GNUNET_assert (AF_INET == lal->af); + l4 = (const struct sockaddr_in *) &lal->addr; + for (struct ClientHandle *ch = ch_head; + NULL != ch; + ch = ch->next) + { + struct GNUNET_NAT_ConnectionReversalRequestedMessage *crrm; + struct GNUNET_MQ_Envelope *env; + int match; + + /* Check if client is in applicable range for ICMP NAT traversal + for this local address */ + if (! ch->natted_address) + continue; + match = GNUNET_NO; + for (unsigned int i = 0; i < ch->num_caddrs; i++) + { + struct ClientAddress *ca = &ch->caddrs[i]; + const struct sockaddr_in *c4; + + if (AF_INET != ca->ss.ss_family) + continue; + c4 = (const struct sockaddr_in *) &ca->ss; + if ((0 != c4->sin_addr.s_addr) && + (l4->sin_addr.s_addr != c4->sin_addr.s_addr)) + continue; + match = GNUNET_YES; + break; + } + if (! match) + continue; + + /* Notify applicable client about connection reversal request */ + env = GNUNET_MQ_msg_extra (crrm, + sizeof(struct sockaddr_in), + GNUNET_MESSAGE_TYPE_NAT_CONNECTION_REVERSAL_REQUESTED); + GNUNET_memcpy (&crrm[1], + ra, + sizeof(struct sockaddr_in)); + GNUNET_MQ_send (ch->mq, + env); + } +} + + +/** + * Task we run periodically to scan for network interfaces. + * + * @param cls NULL + */ +static void +run_scan (void *cls) +{ + struct IfcProcContext ifc_ctx; + int found; + int have_nat; + struct LocalAddressList *lnext; + + scan_task = GNUNET_SCHEDULER_add_delayed (SCAN_FREQ, + &run_scan, + NULL); + memset (&ifc_ctx, + 0, + sizeof(ifc_ctx)); + GNUNET_OS_network_interfaces_list (&ifc_proc, + &ifc_ctx); + /* remove addresses that disappeared */ + for (struct LocalAddressList *lal = lal_head; + NULL != lal; + lal = lnext) + { + lnext = lal->next; + found = GNUNET_NO; + for (struct LocalAddressList *pos = ifc_ctx.lal_head; + NULL != pos; + pos = pos->next) + { + if ((pos->af == lal->af) && + (0 == memcmp (&lal->addr, + &pos->addr, + (AF_INET == lal->af) + ? sizeof(struct sockaddr_in) + : sizeof(struct sockaddr_in6)))) + { + found = GNUNET_YES; + } + } + if (GNUNET_NO == found) + { + notify_clients (lal, + GNUNET_NO); + free_lal (lal); + } + } + + /* add addresses that appeared */ + have_nat = GNUNET_NO; + for (struct LocalAddressList *pos = ifc_ctx.lal_head; + NULL != pos; + pos = ifc_ctx.lal_head) + { + found = GNUNET_NO; + if (GNUNET_NAT_AC_LAN == (GNUNET_NAT_AC_LAN & pos->ac)) + have_nat = GNUNET_YES; + for (struct LocalAddressList *lal = lal_head; + NULL != lal; + lal = lal->next) + { + if ((pos->af == lal->af) && + (0 == memcmp (&lal->addr, + &pos->addr, + (AF_INET == lal->af) + ? sizeof(struct sockaddr_in) + : sizeof(struct sockaddr_in6)))) + found = GNUNET_YES; + } + GNUNET_CONTAINER_DLL_remove (ifc_ctx.lal_head, + ifc_ctx.lal_tail, + pos); + if (GNUNET_YES == found) + { + GNUNET_free (pos); + } + else + { + notify_clients (pos, + GNUNET_YES); + GNUNET_CONTAINER_DLL_insert (lal_head, + lal_tail, + pos); + if ((AF_INET == pos->af) && + (NULL == pos->hc) && + (0 != (GNUNET_NAT_AC_LAN & pos->ac))) + { + const struct sockaddr_in *s4 + = (const struct sockaddr_in *) &pos->addr; + + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Found NATed local address %s, starting NAT server\n", + GNUNET_a2s ((const struct sockaddr *) &pos->addr, + sizeof(*s4))); + pos->hc = GN_start_gnunet_nat_server_ (&s4->sin_addr, + &reversal_callback, + pos, + cfg); + } + } + } + GN_nat_status_changed (have_nat); +} + + +/** + * Function called whenever our set of external addresses + * as created by `upnpc` changes. + * + * @param cls closure with our `struct ClientHandle *` + * @param add_remove #GNUNET_YES to mean the new public IP address, #GNUNET_NO to mean + * the previous (now invalid) one, #GNUNET_SYSERR indicates an error + * @param addr either the previous or the new public IP address + * @param addrlen actual length of the @a addr + * @param result #GNUNET_NAT_ERROR_SUCCESS on success, otherwise the specific error code + */ +static void +upnp_addr_change_cb (void *cls, + int add_remove, + const struct sockaddr *addr, + socklen_t addrlen, + enum GNUNET_NAT_StatusCode result) +{ + struct ClientHandle *ch = cls; + enum GNUNET_NAT_AddressClass ac; + + switch (result) + { + case GNUNET_NAT_ERROR_SUCCESS: + GNUNET_assert (NULL != addr); + break; + + case GNUNET_NAT_ERROR_UPNPC_FAILED: + case GNUNET_NAT_ERROR_UPNPC_TIMEOUT: + case GNUNET_NAT_ERROR_IPC_FAILURE: + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Running upnpc failed: %d\n", + result); + return; + + case GNUNET_NAT_ERROR_EXTERNAL_IP_UTILITY_NOT_FOUND: + GNUNET_log (GNUNET_ERROR_TYPE_INFO, + "external-ip binary not found\n"); + return; + + case GNUNET_NAT_ERROR_UPNPC_NOT_FOUND: + GNUNET_log (GNUNET_ERROR_TYPE_INFO, + "upnpc binary not found\n"); + return; + + case GNUNET_NAT_ERROR_EXTERNAL_IP_UTILITY_FAILED: + GNUNET_log (GNUNET_ERROR_TYPE_WARNING, + "external-ip binary could not be run\n"); + return; + + case GNUNET_NAT_ERROR_UPNPC_PORTMAP_FAILED: + GNUNET_log (GNUNET_ERROR_TYPE_WARNING, + "upnpc failed to create port mapping\n"); + return; + + case GNUNET_NAT_ERROR_EXTERNAL_IP_UTILITY_OUTPUT_INVALID: + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Invalid output from upnpc\n"); + return; + + case GNUNET_NAT_ERROR_EXTERNAL_IP_ADDRESS_INVALID: + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Invalid address returned by upnpc\n"); + return; + + default: + GNUNET_break (0); /* should not be possible */ + return; + } + switch (addr->sa_family) + { + case AF_INET: + ac = is_nat_v4 (&((const struct sockaddr_in *) addr)->sin_addr) + ? GNUNET_NAT_AC_LAN + : GNUNET_NAT_AC_EXTERN; + break; + + case AF_INET6: + ac = is_nat_v6 (&((const struct sockaddr_in6 *) addr)->sin6_addr) + ? GNUNET_NAT_AC_LAN + : GNUNET_NAT_AC_EXTERN; + break; + + default: + GNUNET_break (0); + return; + } + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "upnp external address %s: %s\n", + add_remove ? "added" : "removed", + GNUNET_a2s (addr, + addrlen)); + notify_client (ac, + ch, + add_remove, + addr, + addrlen); +} + + +/** + * Resolve the `hole_external` name to figure out our + * external address from a manually punched hole. The + * port number has already been parsed, this task is + * responsible for periodically doing a DNS lookup. + * + * @param cls client handle to act upon + */ +static void +dyndns_lookup (void *cls); + + +/** + * Our (external) hostname was resolved. Update lists of + * current external IPs (note that DNS may return multiple + * addresses!) and notify client accordingly. + * + * @param cls the `struct ClientHandle` + * @param addr NULL on error, otherwise result of DNS lookup + * @param addrlen number of bytes in @a addr + */ +static void +process_external_ip (void *cls, + const struct sockaddr *addr, + socklen_t addrlen) +{ + struct ClientHandle *ch = cls; + struct LocalAddressList *lal; + struct sockaddr_storage ss; + struct sockaddr_in *v4; + struct sockaddr_in6 *v6; + + if (NULL == addr) + { + struct LocalAddressList *laln; + + ch->ext_dns = NULL; + ch->ext_dns_task + = GNUNET_SCHEDULER_add_delayed (dyndns_frequency, + &dyndns_lookup, + ch); + /* Current iteration is over, remove 'old' IPs now */ + for (lal = ch->ext_addr_head; NULL != lal; lal = laln) + { + laln = lal->next; + if (GNUNET_YES == lal->old) + { + GNUNET_CONTAINER_DLL_remove (ch->ext_addr_head, + ch->ext_addr_tail, + lal); + check_notify_client (lal, + ch, + GNUNET_NO); + GNUNET_free (lal); + } + } + return; + } + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Got IP `%s' for external address `%s'\n", + GNUNET_a2s (addr, + addrlen), + ch->hole_external); + + /* build sockaddr storage with port number */ + memset (&ss, + 0, + sizeof(ss)); + GNUNET_memcpy (&ss, + addr, + addrlen); + switch (addr->sa_family) + { + case AF_INET: + v4 = (struct sockaddr_in *) &ss; + v4->sin_port = htons (ch->ext_dns_port); + break; + + case AF_INET6: + v6 = (struct sockaddr_in6 *) &ss; + v6->sin6_port = htons (ch->ext_dns_port); + break; + + default: + GNUNET_break (0); + return; + } + /* See if 'ss' matches any of our known addresses */ + for (lal = ch->ext_addr_head; NULL != lal; lal = lal->next) + { + if (GNUNET_NO == lal->old) + continue; /* already processed, skip */ + if ((addr->sa_family == lal->addr.ss_family) && + (0 == memcmp (&ss, + &lal->addr, + addrlen))) + { + /* Address unchanged, remember so we do not remove */ + lal->old = GNUNET_NO; + return; /* done here */ + } + } + /* notify client, and remember IP for later removal! */ + lal = GNUNET_new (struct LocalAddressList); + lal->addr = ss; + lal->af = ss.ss_family; + lal->ac = GNUNET_NAT_AC_GLOBAL | GNUNET_NAT_AC_MANUAL; + GNUNET_CONTAINER_DLL_insert (ch->ext_addr_head, + ch->ext_addr_tail, + lal); + check_notify_client (lal, + ch, + GNUNET_YES); +} + + +/** + * Resolve the `hole_external` name to figure out our + * external address from a manually punched hole. The + * port number has already been parsed, this task is + * responsible for periodically doing a DNS lookup. + * + * @param ch client handle to act upon + */ +static void +dyndns_lookup (void *cls) +{ + struct ClientHandle *ch = cls; + struct LocalAddressList *lal; + + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Performing DNS lookup for punched hole given for `%s' as `%s:%u'\n", + ch->section_name, + ch->hole_external, + (unsigned int) ch->ext_dns_port); + for (lal = ch->ext_addr_head; NULL != lal; lal = lal->next) + lal->old = GNUNET_YES; + ch->ext_dns_task = NULL; + ch->ext_dns = GNUNET_RESOLVER_ip_get (ch->hole_external, + AF_UNSPEC, + GNUNET_TIME_UNIT_MINUTES, + &process_external_ip, + ch); +} + + +/** + * Resolve the `hole_external` name to figure out our + * external address from a manually punched hole. The + * given name may be "AUTO" in which case we should use + * the IP address(es) we have from upnpc or other methods. + * The name can also be an IP address, in which case we + * do not need to do DNS resolution. Finally, we also + * need to parse the port number. + * + * @param ch client handle to act upon + */ +static void +lookup_hole_external (struct ClientHandle *ch) +{ + char *port; + unsigned int pnum; + struct sockaddr_in *s4; + struct LocalAddressList *lal; + + port = strrchr (ch->hole_external, ':'); + if (NULL == port) + { + GNUNET_log (GNUNET_ERROR_TYPE_WARNING, + _ ("Malformed punched hole specification `%s' (lacks port)\n"), + ch->hole_external); + return; + } + if ((1 != sscanf (port + 1, + "%u", + &pnum)) || + (pnum > 65535)) + { + GNUNET_log (GNUNET_ERROR_TYPE_WARNING, + _ ( + "Invalid port number in punched hole specification `%s' (lacks port)\n"), + port + 1); + return; + } + ch->ext_dns_port = (uint16_t) pnum; + *port = '\0'; + + lal = GNUNET_new (struct LocalAddressList); + if ('[' == *ch->hole_external) + { + struct sockaddr_in6 *s6 = (struct sockaddr_in6 *) &lal->addr; + + s6->sin6_family = AF_INET6; + if (']' != (ch->hole_external[strlen (ch->hole_external) - 1])) + { + GNUNET_log (GNUNET_ERROR_TYPE_WARNING, + _ ("Malformed punched hole specification `%s' (lacks `]')\n"), + ch->hole_external); + GNUNET_free (lal); + return; + } + ch->hole_external[strlen (ch->hole_external) - 1] = '\0'; + if (1 != inet_pton (AF_INET6, + ch->hole_external + 1, + &s6->sin6_addr)) + { + GNUNET_log (GNUNET_ERROR_TYPE_WARNING, + _ ( + "Malformed punched hole specification `%s' (IPv6 address invalid)"), + ch->hole_external + 1); + GNUNET_free (lal); + return; + } + s6->sin6_port = htons (ch->ext_dns_port); + lal->af = AF_INET6; + lal->ac = GNUNET_NAT_AC_GLOBAL | GNUNET_NAT_AC_MANUAL; + GNUNET_CONTAINER_DLL_insert (ch->ext_addr_head, + ch->ext_addr_tail, + lal); + check_notify_client (lal, + ch, + GNUNET_YES); + return; + } + + s4 = (struct sockaddr_in *) &lal->addr; + s4->sin_family = AF_INET; + if (1 == inet_pton (AF_INET, + ch->hole_external, + &s4->sin_addr)) + { + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "IPv4 punched hole given for `%s' via `%s:%u'\n", + ch->section_name, + ch->hole_external, + (unsigned int) ch->ext_dns_port); + s4->sin_port = htons (ch->ext_dns_port); + lal->af = AF_INET; + lal->ac = GNUNET_NAT_AC_GLOBAL | GNUNET_NAT_AC_MANUAL; + GNUNET_CONTAINER_DLL_insert (ch->ext_addr_head, + ch->ext_addr_tail, + lal); + check_notify_client (lal, + ch, + GNUNET_YES); + return; + } + if (0 == strcasecmp (ch->hole_external, + "AUTO")) + { + /* handled in #notify_client_external_ipv4_change() */ + GNUNET_free (lal); + return; + } + /* got a DNS name, trigger lookup! */ + GNUNET_free (lal); + ch->ext_dns_task + = GNUNET_SCHEDULER_add_now (&dyndns_lookup, + ch); +} + + +/** + * Handler for #GNUNET_MESSAGE_TYPE_NAT_REGISTER message from client. + * We remember the client for updates upon future NAT events. + * + * @param cls client who sent the message + * @param message the message received + */ +static void +handle_register (void *cls, + const struct GNUNET_NAT_RegisterMessage *message) +{ + struct ClientHandle *ch = cls; + const char *off; + size_t left; + + if ((0 != ch->proto) || + (NULL != ch->caddrs)) + { + /* double registration not allowed */ + GNUNET_break (0); + GNUNET_SERVICE_client_drop (ch->client); + return; + } + ch->flags = message->flags; + ch->proto = message->proto; + ch->num_caddrs = ntohs (message->num_addrs); + ch->caddrs = GNUNET_new_array (ch->num_caddrs, + struct ClientAddress); + left = ntohs (message->header.size) - sizeof(*message); + off = (const char *) &message[1]; + for (unsigned int i = 0; i < ch->num_caddrs; i++) + { + const struct sockaddr *sa = (const struct sockaddr *) off; + size_t alen; + uint16_t port; + int is_nat; + + if (sizeof(sa_family_t) > left) + { + GNUNET_break (0); + GNUNET_SERVICE_client_drop (ch->client); + return; + } + is_nat = GNUNET_NO; + switch (sa->sa_family) + { + case AF_INET: + { + struct sockaddr_in s4; + + GNUNET_memcpy (&s4, + off, + sizeof(struct sockaddr_in)); + alen = sizeof(struct sockaddr_in); + if (is_nat_v4 (&s4.sin_addr)) + is_nat = GNUNET_YES; + port = ntohs (s4.sin_port); + } + break; + + case AF_INET6: + { + struct sockaddr_in6 s6; + + GNUNET_memcpy (&s6, + off, + sizeof(struct sockaddr_in6)); + alen = sizeof(struct sockaddr_in6); + if (is_nat_v6 (&s6.sin6_addr)) + is_nat = GNUNET_YES; + port = ntohs (s6.sin6_port); + } + break; + +#if AF_UNIX + case AF_UNIX: + alen = sizeof(struct sockaddr_un); + port = 0; + break; +#endif + default: + GNUNET_break (0); + GNUNET_SERVICE_client_drop (ch->client); + return; + } + /* store address */ + GNUNET_assert (alen <= left); + GNUNET_assert (alen <= sizeof(struct sockaddr_storage)); + GNUNET_memcpy (&ch->caddrs[i].ss, + off, + alen); + + /* If applicable, try UPNPC NAT punching */ + if ((is_nat) && + (enable_upnp) && + ((IPPROTO_TCP == ch->proto) || + (IPPROTO_UDP == ch->proto))) + { + ch->natted_address = GNUNET_YES; + ch->caddrs[i].mh + = GNUNET_NAT_mini_map_start (port, + IPPROTO_TCP == ch->proto, + &upnp_addr_change_cb, + ch); + } + + off += alen; + } + + ch->section_name + = GNUNET_strndup (off, + ntohs (message->str_len)); + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Received REGISTER message from client for subsystem `%s'\n", + ch->section_name); + if (GNUNET_OK == + GNUNET_CONFIGURATION_get_value_string (cfg, + ch->section_name, + "HOLE_EXTERNAL", + &ch->hole_external)) + lookup_hole_external (ch); + + /* Actually send IP address list to client */ + for (struct LocalAddressList *lal = lal_head; + NULL != lal; + lal = lal->next) + { + check_notify_client (lal, + ch, + GNUNET_YES); + } + /* Also consider IPv4 determined by `external-ip` */ + ch->external_monitor + = GN_external_ipv4_monitor_start (¬ify_client_external_ipv4_change, + ch); + GNUNET_SERVICE_client_continue (ch->client); +} + + +/** + * Check validity of #GNUNET_MESSAGE_TYPE_NAT_HANDLE_STUN message from + * client. + * + * @param cls client who sent the message + * @param message the message received + * @return #GNUNET_OK if message is well-formed + */ +static int +check_stun (void *cls, + const struct GNUNET_NAT_HandleStunMessage *message) +{ + size_t sa_len = ntohs (message->sender_addr_size); + size_t expect = sa_len + ntohs (message->payload_size); + + if (ntohs (message->header.size) - sizeof(*message) != expect) + { + GNUNET_break (0); + return GNUNET_SYSERR; + } + if (sa_len < sizeof(sa_family_t)) + { + GNUNET_break (0); + return GNUNET_SYSERR; + } + return GNUNET_OK; +} + + +/** + * Notify all clients about our external IP address + * as reported by the STUN server. + * + * @param ip the external IP + * @param add #GNUNET_YES to add, #GNUNET_NO to remove + */ +static void +notify_clients_stun_change (const struct sockaddr_in *ip, + int add) +{ + for (struct ClientHandle *ch = ch_head; + NULL != ch; + ch = ch->next) + { + struct sockaddr_in v4; + struct GNUNET_NAT_AddressChangeNotificationMessage *msg; + struct GNUNET_MQ_Envelope *env; + + if (! ch->natted_address) + continue; + v4 = *ip; + v4.sin_port = htons (0); + env = GNUNET_MQ_msg_extra (msg, + sizeof(v4), + GNUNET_MESSAGE_TYPE_NAT_ADDRESS_CHANGE); + msg->add_remove = htonl ((int32_t) add); + msg->addr_class = htonl (GNUNET_NAT_AC_EXTERN + | GNUNET_NAT_AC_GLOBAL); + GNUNET_memcpy (&msg[1], + &v4, + sizeof(v4)); + GNUNET_MQ_send (ch->mq, + env); + } +} + + +/** + * Function to be called when we decide that an + * external IP address as told to us by a STUN + * server has gone stale. + * + * @param cls the `struct StunExternalIP` to drop + */ +static void +stun_ip_timeout (void *cls) +{ + struct StunExternalIP *se = cls; + + se->timeout_task = NULL; + notify_clients_stun_change (&se->external_addr, + GNUNET_NO); + GNUNET_CONTAINER_DLL_remove (se_head, + se_tail, + se); + GNUNET_free (se); +} + + +/** + * Handler for #GNUNET_MESSAGE_TYPE_NAT_HANDLE_STUN message from + * client. + * + * @param cls client who sent the message + * @param message the message received + */ +static void +handle_stun (void *cls, + const struct GNUNET_NAT_HandleStunMessage *message) +{ + struct ClientHandle *ch = cls; + const char *buf = (const char *) &message[1]; + const struct sockaddr *sa; + const void *payload; + size_t sa_len; + size_t payload_size; + struct sockaddr_in external_addr; + + sa_len = ntohs (message->sender_addr_size); + payload_size = ntohs (message->payload_size); + sa = (const struct sockaddr *) &buf[0]; + payload = (const struct sockaddr *) &buf[sa_len]; + switch (sa->sa_family) + { + case AF_INET: + if (sa_len != sizeof(struct sockaddr_in)) + { + GNUNET_break (0); + GNUNET_SERVICE_client_drop (ch->client); + return; + } + break; + + case AF_INET6: + if (sa_len != sizeof(struct sockaddr_in6)) + { + GNUNET_break (0); + GNUNET_SERVICE_client_drop (ch->client); + return; + } + break; + } + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Received HANDLE_STUN message from client\n"); + if (GNUNET_OK == + GNUNET_NAT_stun_handle_packet_ (payload, + payload_size, + &external_addr)) + { + /* We now know that a server at "sa" claims that + we are visible at IP "external_addr". + + We should (for some fixed period of time) tell + all of our clients that listen to a NAT'ed address + that they might want to consider the given 'external_ip' + as their public IP address (this includes TCP and UDP + clients, even if only UDP sends STUN requests). + + If we do not get a renewal, the "external_addr" should be + removed again. The timeout frequency should be configurable + (with a sane default), so that the UDP plugin can tell how + often to re-request STUN. + */struct StunExternalIP *se; + + /* Check if we had a prior response from this STUN server */ + for (se = se_head; NULL != se; se = se->next) + { + if ((se->stun_server_addr_len != sa_len) || + (0 != memcmp (sa, + &se->stun_server_addr, + sa_len))) + continue; /* different STUN server */ + if (0 != GNUNET_memcmp (&external_addr, + &se->external_addr)) + { + /* external IP changed, update! */ + notify_clients_stun_change (&se->external_addr, + GNUNET_NO); + se->external_addr = external_addr; + notify_clients_stun_change (&se->external_addr, + GNUNET_YES); + } + /* update timeout */ + GNUNET_SCHEDULER_cancel (se->timeout_task); + se->timeout_task + = GNUNET_SCHEDULER_add_delayed (stun_stale_timeout, + &stun_ip_timeout, + se); + return; + } + /* STUN server is completely new, create fresh entry */ + se = GNUNET_new (struct StunExternalIP); + se->external_addr = external_addr; + GNUNET_memcpy (&se->stun_server_addr, + sa, + sa_len); + se->stun_server_addr_len = sa_len; + se->timeout_task = GNUNET_SCHEDULER_add_delayed (stun_stale_timeout, + &stun_ip_timeout, + se); + GNUNET_CONTAINER_DLL_insert (se_head, + se_tail, + se); + notify_clients_stun_change (&se->external_addr, + GNUNET_NO); + } + GNUNET_SERVICE_client_continue (ch->client); +} + + +/** + * Check validity of + * #GNUNET_MESSAGE_TYPE_NAT_REQUEST_CONNECTION_REVERSAL message from + * client. + * + * @param cls client who sent the message + * @param message the message received + * @return #GNUNET_OK if message is well-formed + */ +static int +check_request_connection_reversal (void *cls, + const struct + GNUNET_NAT_RequestConnectionReversalMessage * + message) +{ + size_t expect; + + expect = ntohs (message->local_addr_size) + + ntohs (message->remote_addr_size); + if (ntohs (message->header.size) - sizeof(*message) != expect) + { + GNUNET_break (0); + return GNUNET_SYSERR; + } + return GNUNET_OK; +} + + +/** + * Handler for #GNUNET_MESSAGE_TYPE_NAT_REQUEST_CONNECTION_REVERSAL + * message from client. + * + * @param cls client who sent the message + * @param message the message received + */ +static void +handle_request_connection_reversal (void *cls, + const struct + GNUNET_NAT_RequestConnectionReversalMessage + *message) +{ + struct ClientHandle *ch = cls; + const char *buf = (const char *) &message[1]; + size_t local_sa_len = ntohs (message->local_addr_size); + size_t remote_sa_len = ntohs (message->remote_addr_size); + struct sockaddr_in l4; + struct sockaddr_in r4; + int ret; + + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Received REQUEST CONNECTION REVERSAL message from client\n"); + if (local_sa_len != sizeof(struct sockaddr_in)) + { + GNUNET_break_op (0); + GNUNET_SERVICE_client_drop (ch->client); + return; + } + if (remote_sa_len != sizeof(struct sockaddr_in)) + { + GNUNET_break_op (0); + GNUNET_SERVICE_client_drop (ch->client); + return; + } + GNUNET_memcpy (&l4, + buf, + sizeof(struct sockaddr_in)); + GNUNET_break_op (AF_INET == l4.sin_family); + buf += sizeof(struct sockaddr_in); + GNUNET_memcpy (&r4, + buf, + sizeof(struct sockaddr_in)); + GNUNET_break_op (AF_INET == r4.sin_family); + ret = GN_request_connection_reversal (&l4.sin_addr, + ntohs (l4.sin_port), + &r4.sin_addr, + cfg); + if (GNUNET_OK != ret) + GNUNET_log (GNUNET_ERROR_TYPE_WARNING, + _ ("Connection reversal request failed\n")); + GNUNET_SERVICE_client_continue (ch->client); +} + + +/** + * Task run during shutdown. + * + * @param cls unused + */ +static void +shutdown_task (void *cls) +{ + struct StunExternalIP *se; + + while (NULL != (se = se_head)) + { + GNUNET_CONTAINER_DLL_remove (se_head, + se_tail, + se); + GNUNET_SCHEDULER_cancel (se->timeout_task); + GNUNET_free (se); + } + GN_nat_status_changed (GNUNET_NO); + if (NULL != scan_task) + { + GNUNET_SCHEDULER_cancel (scan_task); + scan_task = NULL; + } + if (NULL != stats) + { + GNUNET_STATISTICS_destroy (stats, + GNUNET_NO); + stats = NULL; + } + destroy_lal (); +} + + +/** + * Setup NAT service. + * + * @param cls closure + * @param c configuration to use + * @param service the initialized service + */ +static void +run (void *cls, + const struct GNUNET_CONFIGURATION_Handle *c, + struct GNUNET_SERVICE_Handle *service) +{ + cfg = c; + if (GNUNET_OK != + GNUNET_CONFIGURATION_get_value_time (cfg, + "NAT", + "STUN_STALE", + &stun_stale_timeout)) + stun_stale_timeout = GNUNET_TIME_UNIT_HOURS; + + /* Check for UPnP */ + enable_upnp + = GNUNET_CONFIGURATION_get_value_yesno (cfg, + "NAT", + "ENABLE_UPNP"); + if (GNUNET_YES == enable_upnp) + { + /* check if it works */ + if (GNUNET_SYSERR == + GNUNET_OS_check_helper_binary ("upnpc", + GNUNET_NO, + NULL)) + { + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + _ ( + "UPnP enabled in configuration, but UPnP client `upnpc` command not found, disabling UPnP\n")); + enable_upnp = GNUNET_SYSERR; + } + } + if (GNUNET_OK != + GNUNET_CONFIGURATION_get_value_time (cfg, + "nat", + "DYNDNS_FREQUENCY", + &dyndns_frequency)) + dyndns_frequency = DYNDNS_FREQUENCY; + + enable_ipscan + = GNUNET_CONFIGURATION_get_value_yesno (cfg, + "NAT", + "ENABLE_IPSCAN"); + + GNUNET_SCHEDULER_add_shutdown (&shutdown_task, + NULL); + stats = GNUNET_STATISTICS_create ("nat", + cfg); + if (GNUNET_YES == enable_ipscan) + scan_task = GNUNET_SCHEDULER_add_now (&run_scan, + NULL); +} + + +/** + * 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 `struct ClientHandle` + */ +static void * +client_connect_cb (void *cls, + struct GNUNET_SERVICE_Client *c, + struct GNUNET_MQ_Handle *mq) +{ + struct ClientHandle *ch; + + ch = GNUNET_new (struct ClientHandle); + ch->mq = mq; + ch->client = c; + GNUNET_CONTAINER_DLL_insert (ch_head, + ch_tail, + ch); + return ch; +} + + +/** + * Callback called when a client disconnected from the service + * + * @param cls closure for the service + * @param c the client that disconnected + * @param internal_cls a `struct ClientHandle *` + */ +static void +client_disconnect_cb (void *cls, + struct GNUNET_SERVICE_Client *c, + void *internal_cls) +{ + struct ClientHandle *ch = internal_cls; + struct LocalAddressList *lal; + + GNUNET_CONTAINER_DLL_remove (ch_head, + ch_tail, + ch); + for (unsigned int i = 0; i < ch->num_caddrs; i++) + { + if (NULL != ch->caddrs[i].mh) + { + GNUNET_NAT_mini_map_stop (ch->caddrs[i].mh); + ch->caddrs[i].mh = NULL; + } + } + GNUNET_free (ch->caddrs); + while (NULL != (lal = ch->ext_addr_head)) + { + GNUNET_CONTAINER_DLL_remove (ch->ext_addr_head, + ch->ext_addr_tail, + lal); + GNUNET_free (lal); + } + if (NULL != ch->ext_dns_task) + { + GNUNET_SCHEDULER_cancel (ch->ext_dns_task); + ch->ext_dns_task = NULL; + } + if (NULL != ch->external_monitor) + { + GN_external_ipv4_monitor_stop (ch->external_monitor); + ch->external_monitor = NULL; + } + if (NULL != ch->ext_dns) + { + GNUNET_RESOLVER_request_cancel (ch->ext_dns); + ch->ext_dns = NULL; + } + GNUNET_free (ch->hole_external); + GNUNET_free (ch->section_name); + GNUNET_free (ch); +} + + +/** + * Define "main" method using service macro. + */ +GNUNET_SERVICE_MAIN + ("nat", + GNUNET_SERVICE_OPTION_NONE, + &run, + &client_connect_cb, + &client_disconnect_cb, + NULL, + GNUNET_MQ_hd_var_size (register, + GNUNET_MESSAGE_TYPE_NAT_REGISTER, + struct GNUNET_NAT_RegisterMessage, + NULL), + GNUNET_MQ_hd_var_size (stun, + GNUNET_MESSAGE_TYPE_NAT_HANDLE_STUN, + struct GNUNET_NAT_HandleStunMessage, + NULL), + GNUNET_MQ_hd_var_size (request_connection_reversal, + GNUNET_MESSAGE_TYPE_NAT_REQUEST_CONNECTION_REVERSAL, + struct GNUNET_NAT_RequestConnectionReversalMessage, + NULL), + GNUNET_MQ_handler_end ()); + + +#if defined(__linux__) && defined(__GLIBC__) +#include + +/** + * 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-service-nat.c */ diff --git a/src/service/nat/gnunet-service-nat.h b/src/service/nat/gnunet-service-nat.h new file mode 100644 index 000000000..5717306bb --- /dev/null +++ b/src/service/nat/gnunet-service-nat.h @@ -0,0 +1,35 @@ +/* + This file is part of GNUnet. + Copyright (C) 2016, 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 . + + SPDX-License-Identifier: AGPL3.0-or-later + */ + +/** + * @file nat/gnunet-service-nat.h + * @brief network address translation traversal service + * @author Christian Grothoff + */ +#ifndef GNUNET_SERVICE_NAT_H +#define GNUNET_SERVICE_NAT_H + +/** + * Is UPnP enabled? #GNUNET_YES if enabled, #GNUNET_NO if disabled, + * #GNUNET_SYSERR if configuration enabled but binary is unavailable. + */ +extern int enable_upnp; + +#endif diff --git a/src/service/nat/gnunet-service-nat_externalip.c b/src/service/nat/gnunet-service-nat_externalip.c new file mode 100644 index 000000000..c2625be2d --- /dev/null +++ b/src/service/nat/gnunet-service-nat_externalip.c @@ -0,0 +1,316 @@ +/* + This file is part of GNUnet. + Copyright (C) 2009, 2015, 2016, 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 . + + SPDX-License-Identifier: AGPL3.0-or-later + */ +/** + * Code to figure out what our external IPv4 address(es) might + * be (external IPv4s are what is seen on the rest of the Internet). + * + * This can be implemented using different methods, and we allow + * the main service to be notified about changes to what we believe + * is our external IPv4 address. + * + * Note that this is explicitly only about NATed systems; if one + * of our network interfaces has a global IP address this does + * not count as "external". + * + * @file nat/gnunet-service-nat_externalip.c + * @brief Functions for monitoring external IPv4 addresses + * @author Christian Grothoff + */ +#include "platform.h" +#include +#include "gnunet_util_lib.h" +#include "gnunet_protocols.h" +#include "gnunet_signatures.h" +#include "gnunet_statistics_service.h" +#include "gnunet_resolver_service.h" +#include "gnunet_nat_service.h" +#include "gnunet-service-nat.h" +#include "gnunet-service-nat_externalip.h" +#include "gnunet-service-nat_stun.h" +#include "gnunet-service-nat_mini.h" +#include "gnunet-service-nat_helper.h" +#include "nat.h" +#include + + +/** + * How long do we wait until we re-try running `external-ip` if the + * command failed to terminate nicely? + */ +#define EXTERN_IP_RETRY_TIMEOUT GNUNET_TIME_relative_multiply ( \ + GNUNET_TIME_UNIT_MINUTES, 15) + +/** + * How long do we wait until we re-try running `external-ip` if the + * command failed (but terminated)? + */ +#define EXTERN_IP_RETRY_FAILURE GNUNET_TIME_relative_multiply ( \ + GNUNET_TIME_UNIT_MINUTES, 30) + +/** + * How long do we wait until we re-try running `external-ip` if the + * command succeeded? + */ +#define EXTERN_IP_RETRY_SUCCESS GNUNET_TIME_relative_multiply ( \ + GNUNET_TIME_UNIT_MINUTES, 5) + + +/** + * Handle to monitor for external IP changes. + */ +struct GN_ExternalIPMonitor +{ + /** + * Kept in DLL. + */ + struct GN_ExternalIPMonitor *next; + + /** + * Kept in DLL. + */ + struct GN_ExternalIPMonitor *prev; + + /** + * Function to call when we believe our external IPv4 address changed. + */ + GN_NotifyExternalIPv4Change cb; + + /** + * Closure for @e cb. + */ + void *cb_cls; +}; + + +/** + * List of monitors, kept in DLL. + */ +static struct GN_ExternalIPMonitor *mon_head; + +/** + * List of monitors, kept in DLL. + */ +static struct GN_ExternalIPMonitor *mon_tail; + +/** + * Task run to obtain our external IP (if #enable_upnp is set + * and if we find we have a NATed IP address). + */ +static struct GNUNET_SCHEDULER_Task *probe_external_ip_task; + +/** + * Handle to our operation to run `external-ip`. + */ +static struct GNUNET_NAT_ExternalHandle *probe_external_ip_op; + +/** + * What is our external IP address as claimed by `external-ip`? + * 0 for unknown. + */ +static struct in_addr mini_external_ipv4; + + +/** + * Tell relevant clients about a change in our external + * IPv4 address. + * + * @param add #GNUNET_YES to add, #GNUNET_NO to remove + * @param v4 the external address that changed + */ +static void +notify_monitors_external_ipv4_change (int add, + const struct in_addr *v4) +{ + for (struct GN_ExternalIPMonitor *mon = mon_head; + NULL != mon; + mon = mon->next) + mon->cb (mon->cb_cls, + v4, + add); +} + + +/** + * Task used to run `external-ip` to get our external IPv4 + * address and pass it to NATed clients if possible. + * + * @param cls NULL + */ +static void +run_external_ip (void *cls); + + +/** + * We learn our current external IP address. If it changed, + * notify all of our applicable clients. Also re-schedule + * #run_external_ip with an appropriate timeout. + * + * @param cls NULL + * @param addr the address, NULL on errors + * @param result #GNUNET_NAT_ERROR_SUCCESS on success, otherwise the specific error code + */ +static void +handle_external_ip (void *cls, + const struct in_addr *addr, + enum GNUNET_NAT_StatusCode result) +{ + char buf[INET_ADDRSTRLEN]; + + probe_external_ip_op = NULL; + GNUNET_SCHEDULER_cancel (probe_external_ip_task); + probe_external_ip_task + = GNUNET_SCHEDULER_add_delayed ((NULL == addr) + ? EXTERN_IP_RETRY_FAILURE + : EXTERN_IP_RETRY_SUCCESS, + &run_external_ip, + NULL); + switch (result) + { + case GNUNET_NAT_ERROR_SUCCESS: + GNUNET_assert (NULL != addr); + if (addr->s_addr == mini_external_ipv4.s_addr) + return; /* not change */ + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Our external IP is now %s\n", + inet_ntop (AF_INET, + addr, + buf, + sizeof(buf))); + if (0 != mini_external_ipv4.s_addr) + notify_monitors_external_ipv4_change (GNUNET_NO, + &mini_external_ipv4); + mini_external_ipv4 = *addr; + notify_monitors_external_ipv4_change (GNUNET_YES, + &mini_external_ipv4); + break; + + default: + if (0 != mini_external_ipv4.s_addr) + notify_monitors_external_ipv4_change (GNUNET_NO, + &mini_external_ipv4); + mini_external_ipv4.s_addr = 0; + break; + } +} + + +/** + * Task used to run `external-ip` to get our external IPv4 + * address and pass it to NATed clients if possible. + * + * @param cls NULL + */ +static void +run_external_ip (void *cls) +{ + probe_external_ip_task + = GNUNET_SCHEDULER_add_delayed (EXTERN_IP_RETRY_TIMEOUT, + &run_external_ip, + NULL); + if (NULL != probe_external_ip_op) + { + GNUNET_NAT_mini_get_external_ipv4_cancel_ (probe_external_ip_op); + probe_external_ip_op = NULL; + } + probe_external_ip_op + = GNUNET_NAT_mini_get_external_ipv4_ (&handle_external_ip, + NULL); +} + + +/** + * We have changed our opinion about being NATed in the first + * place. Adapt our probing. + * + * @param have_nat #GNUNET_YES if we believe we are behind NAT + */ +void +GN_nat_status_changed (int have_nat) +{ + if (GNUNET_YES != enable_upnp) + return; + if ((GNUNET_YES == have_nat) && + (NULL == probe_external_ip_task) && + (NULL == probe_external_ip_op)) + { + probe_external_ip_task + = GNUNET_SCHEDULER_add_now (&run_external_ip, + NULL); + return; + } + if (GNUNET_NO == have_nat) + { + if (NULL != probe_external_ip_task) + { + GNUNET_SCHEDULER_cancel (probe_external_ip_task); + probe_external_ip_task = NULL; + } + if (NULL != probe_external_ip_op) + { + GNUNET_NAT_mini_get_external_ipv4_cancel_ (probe_external_ip_op); + probe_external_ip_op = NULL; + } + } +} + + +/** + * Start monitoring external IPv4 addresses. + * + * @param cb function to call on changes + * @param cb_cls closure for @a cb + * @return handle to cancel + */ +struct GN_ExternalIPMonitor * +GN_external_ipv4_monitor_start (GN_NotifyExternalIPv4Change cb, + void *cb_cls) +{ + struct GN_ExternalIPMonitor *mon; + + mon = GNUNET_new (struct GN_ExternalIPMonitor); + mon->cb = cb; + mon->cb_cls = cb_cls; + GNUNET_CONTAINER_DLL_insert (mon_head, + mon_tail, + mon); + if (0 != mini_external_ipv4.s_addr) + cb (cb_cls, + &mini_external_ipv4, + GNUNET_YES); + return mon; +} + + +/** + * Stop calling monitor. + * + * @param mon monitor to call + */ +void +GN_external_ipv4_monitor_stop (struct GN_ExternalIPMonitor *mon) +{ + GNUNET_CONTAINER_DLL_remove (mon_head, + mon_tail, + mon); + GNUNET_free (mon); +} + + +/* end of gnunet-service-nat_externalip.c */ diff --git a/src/service/nat/gnunet-service-nat_externalip.h b/src/service/nat/gnunet-service-nat_externalip.h new file mode 100644 index 000000000..6b3467fab --- /dev/null +++ b/src/service/nat/gnunet-service-nat_externalip.h @@ -0,0 +1,92 @@ +/* + This file is part of GNUnet. + Copyright (C) 2009, 2015, 2016, 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 . + + SPDX-License-Identifier: AGPL3.0-or-later + */ +/** + * Code to figure out what our external IPv4 address(es) might + * be (external IPv4s are what is seen on the rest of the Internet). + * + * This can be implemented using different methods, and we allow + * the main service to be notified about changes to what we believe + * is our external IPv4 address. + * + * Note that this is explicitly only about NATed systems; if one + * of our network interfaces has a global IP address this does + * not count as "external". + * + * @file nat/gnunet-service-nat_externalip.h + * @brief Functions for monitoring external IPv4 addresses + * @author Christian Grothoff + */ +#ifndef GNUNET_SERVICE_NAT_EXTERNALIP_H +#define GNUNET_SERVICE_NAT_EXTERNALIP_H + +#include "platform.h" + + +/** + * We have changed our opinion about being NATed in the first + * place. Adapt our probing. + * + * @param have_nat #GNUNET_YES if we believe we are behind NAT + */ +void +GN_nat_status_changed (int have_nat); + + +/** + * Function we call when we believe our external IPv4 address changed. + * + * @param cls closure + * @param ip address to add/remove + * @param add_remove #GNUNET_YES to add, #GNUNET_NO to remove + */ +typedef void +(*GN_NotifyExternalIPv4Change)(void *cls, + const struct in_addr *ip, + int add_remove); + + +/** + * Handle to monitor for external IP changes. + */ +struct GN_ExternalIPMonitor; + + +/** + * Start monitoring external IPv4 addresses. + * + * @param cb function to call on changes + * @param cb_cls closure for @a cb + * @return handle to cancel + */ +struct GN_ExternalIPMonitor * +GN_external_ipv4_monitor_start (GN_NotifyExternalIPv4Change cb, + void *cb_cls); + + +/** + * Stop calling monitor. + * + * @param mon monitor to call + */ +void +GN_external_ipv4_monitor_stop (struct GN_ExternalIPMonitor *mon); + + +#endif diff --git a/src/service/nat/gnunet-service-nat_helper.c b/src/service/nat/gnunet-service-nat_helper.c new file mode 100644 index 000000000..d92f5a99c --- /dev/null +++ b/src/service/nat/gnunet-service-nat_helper.c @@ -0,0 +1,391 @@ +/* + This file is part of GNUnet. + Copyright (C) 2009, 2010, 2011, 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 . + + SPDX-License-Identifier: AGPL3.0-or-later + */ + +/** + * @file nat/gnunet-service-nat_helper.c + * @brief runs the gnunet-helper-nat-server + * @author Milan Bouchet-Valat + * @author Christian Grothoff + */ +#include "platform.h" +#include "gnunet_util_lib.h" +#include "gnunet-service-nat_helper.h" + + +/** + * Information we keep per NAT helper process. + */ +struct HelperContext +{ + /** + * IP address we pass to the NAT helper. + */ + struct in_addr internal_address; + + /** + * Function to call if we receive a reversal request. + */ + GN_ReversalCallback cb; + + /** + * Closure for @e cb. + */ + void *cb_cls; + + /** + * How long do we wait for restarting a crashed gnunet-helper-nat-server? + */ + struct GNUNET_TIME_Relative server_retry_delay; + + /** + * ID of select gnunet-helper-nat-server stdout read task + */ + struct GNUNET_SCHEDULER_Task *server_read_task; + + /** + * The process id of the server process (if behind NAT) + */ + struct GNUNET_OS_Process *server_proc; + + /** + * stdout pipe handle for the gnunet-helper-nat-server process + */ + struct GNUNET_DISK_PipeHandle *server_stdout; + + /** + * stdout file handle (for reading) for the gnunet-helper-nat-server process + */ + const struct GNUNET_DISK_FileHandle *server_stdout_handle; + + /** + * Handle to the GNUnet configuration + */ + const struct GNUNET_CONFIGURATION_Handle *cfg; +}; + + +/** + * Task that restarts the gnunet-helper-nat-server process after a crash + * after a certain delay. + * + * @param cls a `struct HelperContext` + */ +static void +restart_nat_server (void *cls); + + +/** + * Try again starting the helper later + * + * @param h context of the helper + */ +static void +try_again (struct HelperContext *h) +{ + GNUNET_assert (NULL == h->server_read_task); + h->server_retry_delay = GNUNET_TIME_STD_BACKOFF (h->server_retry_delay); + h->server_read_task = GNUNET_SCHEDULER_add_delayed (h->server_retry_delay, + &restart_nat_server, + h); +} + + +/** + * We have been notified that gnunet-helper-nat-server has written + * something to stdout. Handle the output, then reschedule this + * function to be called again once more is available. + * + * @param cls the `struct HelperContext` + */ +static void +nat_server_read (void *cls) +{ + struct HelperContext *h = cls; + char mybuf[40]; + ssize_t bytes; + int port; + const char *port_start; + struct sockaddr_in sin_addr; + + h->server_read_task = NULL; + memset (mybuf, 0, sizeof(mybuf)); + bytes = + GNUNET_DISK_file_read (h->server_stdout_handle, mybuf, sizeof(mybuf)); + if (bytes < 1) + { + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Finished reading from server stdout with code: %d\n", + (int) bytes); + if (0 != GNUNET_OS_process_kill (h->server_proc, GNUNET_TERM_SIG)) + GNUNET_log_from_strerror (GNUNET_ERROR_TYPE_WARNING, "nat", "kill"); + GNUNET_OS_process_wait (h->server_proc); + GNUNET_OS_process_destroy (h->server_proc); + h->server_proc = NULL; + GNUNET_DISK_pipe_close (h->server_stdout); + h->server_stdout = NULL; + h->server_stdout_handle = NULL; + try_again (h); + return; + } + + port_start = NULL; + for (size_t i = 0; i < sizeof(mybuf); i++) + { + if (mybuf[i] == '\n') + { + mybuf[i] = '\0'; + break; + } + if ((mybuf[i] == ':') && (i + 1 < sizeof(mybuf))) + { + mybuf[i] = '\0'; + port_start = &mybuf[i + 1]; + } + } + + /* construct socket address of sender */ + memset (&sin_addr, 0, sizeof(sin_addr)); + sin_addr.sin_family = AF_INET; +#if HAVE_SOCKADDR_IN_SIN_LEN + sin_addr.sin_len = sizeof(sin_addr); +#endif + if ((NULL == port_start) || (1 != sscanf (port_start, "%d", &port)) || + (-1 == inet_pton (AF_INET, mybuf, &sin_addr.sin_addr))) + { + /* should we restart gnunet-helper-nat-server? */ + GNUNET_log (GNUNET_ERROR_TYPE_WARNING, + _ ( + "gnunet-helper-nat-server generated malformed address `%s'\n"), + mybuf); + h->server_read_task = + GNUNET_SCHEDULER_add_read_file (GNUNET_TIME_UNIT_FOREVER_REL, + h->server_stdout_handle, + &nat_server_read, + h); + return; + } + sin_addr.sin_port = htons ((uint16_t) port); + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "gnunet-helper-nat-server read: %s:%d\n", + mybuf, + port); + h->cb (h->cb_cls, &sin_addr); + h->server_read_task = + GNUNET_SCHEDULER_add_read_file (GNUNET_TIME_UNIT_FOREVER_REL, + h->server_stdout_handle, + &nat_server_read, + h); +} + + +/** + * Task that restarts the gnunet-helper-nat-server process after a crash + * after a certain delay. + * + * @param cls a `struct HelperContext` + */ +static void +restart_nat_server (void *cls) +{ + struct HelperContext *h = cls; + char *binary; + char ia[INET_ADDRSTRLEN]; + + h->server_read_task = NULL; + GNUNET_assert (NULL != + inet_ntop (AF_INET, &h->internal_address, ia, sizeof(ia))); + /* Start the server process */ + binary = GNUNET_OS_get_suid_binary_path (h->cfg, "gnunet-helper-nat-server"); + if (GNUNET_YES != GNUNET_OS_check_helper_binary (binary, GNUNET_YES, ia)) + { + /* move instantly to max delay, as this is unlikely to be fixed */ + h->server_retry_delay = GNUNET_TIME_STD_EXPONENTIAL_BACKOFF_THRESHOLD; + GNUNET_free (binary); + try_again (h); + return; + } + h->server_stdout = + GNUNET_DISK_pipe (GNUNET_DISK_PF_BLOCKING_RW); + if (NULL == h->server_stdout) + { + GNUNET_log_strerror (GNUNET_ERROR_TYPE_ERROR, "pipe"); + GNUNET_free (binary); + try_again (h); + return; + } + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Starting `%s' at `%s'\n", + "gnunet-helper-nat-server", + ia); + h->server_proc = GNUNET_OS_start_process (GNUNET_OS_INHERIT_STD_NONE, + NULL, + h->server_stdout, + NULL, + binary, + "gnunet-helper-nat-server", + ia, + NULL); + GNUNET_free (binary); + if (NULL == h->server_proc) + { + GNUNET_log (GNUNET_ERROR_TYPE_WARNING, + _ ("Failed to start %s\n"), + "gnunet-helper-nat-server"); + GNUNET_DISK_pipe_close (h->server_stdout); + h->server_stdout = NULL; + try_again (h); + return; + } + /* Close the write end of the read pipe */ + GNUNET_DISK_pipe_close_end (h->server_stdout, GNUNET_DISK_PIPE_END_WRITE); + h->server_stdout_handle = + GNUNET_DISK_pipe_handle (h->server_stdout, GNUNET_DISK_PIPE_END_READ); + h->server_read_task = + GNUNET_SCHEDULER_add_read_file (GNUNET_TIME_UNIT_FOREVER_REL, + h->server_stdout_handle, + &nat_server_read, + h); +} + + +struct HelperContext * +GN_start_gnunet_nat_server_ (const struct in_addr *internal_address, + GN_ReversalCallback cb, + void *cb_cls, + const struct GNUNET_CONFIGURATION_Handle *cfg) +{ + struct HelperContext *h; + + h = GNUNET_new (struct HelperContext); + h->cb = cb; + h->cb_cls = cb_cls; + h->internal_address = *internal_address; + h->cfg = cfg; + restart_nat_server (h); + if (NULL == h->server_stdout) + { + GN_stop_gnunet_nat_server_ (h); + return NULL; + } + return h; +} + + +/** + * Start the gnunet-helper-nat-server and process incoming + * requests. + * + * @param h helper context to stop + */ +void +GN_stop_gnunet_nat_server_ (struct HelperContext *h) +{ + if (NULL != h->server_read_task) + { + GNUNET_SCHEDULER_cancel (h->server_read_task); + h->server_read_task = NULL; + } + if (NULL != h->server_proc) + { + if (0 != GNUNET_OS_process_kill (h->server_proc, GNUNET_TERM_SIG)) + GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING, "kill"); + GNUNET_OS_process_wait (h->server_proc); + GNUNET_OS_process_destroy (h->server_proc); + h->server_proc = NULL; + GNUNET_DISK_pipe_close (h->server_stdout); + h->server_stdout = NULL; + h->server_stdout_handle = NULL; + } + if (NULL != h->server_stdout) + { + GNUNET_DISK_pipe_close (h->server_stdout); + h->server_stdout = NULL; + h->server_stdout_handle = NULL; + } + GNUNET_free (h); +} + + +/** + * We want to connect to a peer that is behind NAT. Run the + * gnunet-helper-nat-client to send dummy ICMP responses to cause + * that peer to connect to us (connection reversal). + * + * @param internal_address out internal address to use + * @param internal_port port to use + * @param remote_v4 the address of the peer (IPv4-only) + * @param cfg handle to the GNUnet configuration + * @return #GNUNET_SYSERR on error, + * #GNUNET_OK otherwise + */ +int +GN_request_connection_reversal (const struct in_addr *internal_address, + uint16_t internal_port, + const struct in_addr *remote_v4, + const struct GNUNET_CONFIGURATION_Handle *cfg) +{ + char intv4[INET_ADDRSTRLEN]; + char remv4[INET_ADDRSTRLEN]; + char port_as_string[6]; + struct GNUNET_OS_Process *proc; + char *binary; + + if (NULL == inet_ntop (AF_INET, internal_address, intv4, INET_ADDRSTRLEN)) + { + GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING, "inet_ntop"); + return GNUNET_SYSERR; + } + if (NULL == inet_ntop (AF_INET, remote_v4, remv4, INET_ADDRSTRLEN)) + { + GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING, "inet_ntop"); + return GNUNET_SYSERR; + } + GNUNET_snprintf (port_as_string, + sizeof(port_as_string), + "%d", + internal_port); + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Running gnunet-helper-nat-client %s %s %u\n", + intv4, + remv4, + internal_port); + binary = GNUNET_OS_get_suid_binary_path (cfg, "gnunet-helper-nat-client"); + proc = GNUNET_OS_start_process (GNUNET_OS_INHERIT_STD_NONE, + NULL, + NULL, + NULL, + binary, + "gnunet-helper-nat-client", + intv4, + remv4, + port_as_string, + NULL); + GNUNET_free (binary); + if (NULL == proc) + return GNUNET_SYSERR; + /* we know that the gnunet-helper-nat-client will terminate virtually + * instantly */ + GNUNET_OS_process_wait (proc); + GNUNET_OS_process_destroy (proc); + return GNUNET_OK; +} + + +/* end of gnunet-service-nat_helper.c */ diff --git a/src/service/nat/gnunet-service-nat_helper.h b/src/service/nat/gnunet-service-nat_helper.h new file mode 100644 index 000000000..dc6b9ae61 --- /dev/null +++ b/src/service/nat/gnunet-service-nat_helper.h @@ -0,0 +1,95 @@ +/* + This file is part of GNUnet. + Copyright (C) 2009, 2010, 2011, 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 . + + SPDX-License-Identifier: AGPL3.0-or-later + */ + +/** + * @file nat/gnunet-service-nat_helper.h + * @brief runs the gnunet-helper-nat-server + * @author Milan Bouchet-Valat + * @author Christian Grothoff + */ +#include "platform.h" +#include "gnunet_util_lib.h" + + +/** + * Information we keep per NAT helper process. + */ +struct HelperContext; + + +/** + * Function called whenever we get a connection reversal + * request from another peer. + * + * @param cls closure + * @param ra IP address of the peer who wants us to connect to it + */ +typedef void +(*GN_ReversalCallback) (void *cls, + const struct sockaddr_in *ra); + + +/** + * Start the gnunet-helper-nat-server and process incoming + * requests. + * + * @param internal_address + * @param cb function to call if we receive a request + * @param cb_cls closure for @a cb + * @param cfg handle to the GNUnet configuration + * @return NULL on error + */ +struct HelperContext * +GN_start_gnunet_nat_server_ (const struct in_addr *internal_address, + GN_ReversalCallback cb, + void *cb_cls, + const struct GNUNET_CONFIGURATION_Handle *cfg); + + +/** + * Start the gnunet-helper-nat-server and process incoming + * requests. + * + * @param h helper context to stop + */ +void +GN_stop_gnunet_nat_server_ (struct HelperContext *h); + + +/** + * We want to connect to a peer that is behind NAT. Run the + * gnunet-helper-nat-client to send dummy ICMP responses to cause + * that peer to connect to us (connection reversal). + * + * @param internal_address out internal address to use + * @param internal_port internal port to use + * @param remote_v4 the address of the peer (IPv4-only) + * @param cfg handle to the GNUnet configuration + * @return #GNUNET_SYSERR on error, + * #GNUNET_OK otherwise + */ +int +GN_request_connection_reversal (const struct in_addr *internal_address, + uint16_t internal_port, + const struct in_addr *remote_v4, + const struct GNUNET_CONFIGURATION_Handle *cfg); + + +/* end of gnunet-service-nat_helper.h */ diff --git a/src/service/nat/gnunet-service-nat_mini.c b/src/service/nat/gnunet-service-nat_mini.c new file mode 100644 index 000000000..1e13e3814 --- /dev/null +++ b/src/service/nat/gnunet-service-nat_mini.c @@ -0,0 +1,694 @@ +/* + This file is part of GNUnet. + Copyright (C) 2011-2014, 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 . + + SPDX-License-Identifier: AGPL3.0-or-later + */ + +/** + * @file nat/gnunet-service-nat_mini.c + * @brief functions for interaction with miniupnp; tested with miniupnpc 1.5 + * @author Christian Grothoff + */ +#include "platform.h" +#include "gnunet_util_lib.h" +#include "gnunet_nat_service.h" +#include "gnunet-service-nat_mini.h" +#include "nat.h" + +#define LOG(kind, ...) GNUNET_log_from (kind, "nat", __VA_ARGS__) + +/** + * How long do we give upnpc to create a mapping? + */ +#define MAP_TIMEOUT GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS, 15) + +/** + * How long do we give upnpc to remove a mapping? + */ +#define UNMAP_TIMEOUT \ + GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS, 1) + +/** + * How often do we check for changes in the mapping? + */ +#define MAP_REFRESH_FREQ \ + GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_MINUTES, 5) + + +/* ************************* external-ip calling ************************ */ + +/** + * Opaque handle to cancel "GNUNET_NAT_mini_get_external_ipv4" operation. + */ +struct GNUNET_NAT_ExternalHandle +{ + /** + * Function to call with the result. + */ + GNUNET_NAT_IPCallback cb; + + /** + * Closure for @e cb. + */ + void *cb_cls; + + /** + * Read task. + */ + struct GNUNET_SCHEDULER_Task *task; + + /** + * Handle to `external-ip` process. + */ + struct GNUNET_OS_Process *eip; + + /** + * Handle to stdout pipe of `external-ip`. + */ + struct GNUNET_DISK_PipeHandle *opipe; + + /** + * Read handle of @e opipe. + */ + const struct GNUNET_DISK_FileHandle *r; + + /** + * Number of bytes in @e buf that are valid. + */ + size_t off; + + /** + * Destination of our read operation (output of 'external-ip'). + */ + char buf[17]; + + /** + * Error code for better debugging and user feedback + */ + enum GNUNET_NAT_StatusCode ret; +}; + + +/** + * Read the output of `external-ip` into `buf`. When complete, parse + * the address and call our callback. + * + * @param cls the `struct GNUNET_NAT_ExternalHandle` + */ +static void +read_external_ipv4 (void *cls) +{ + struct GNUNET_NAT_ExternalHandle *eh = cls; + ssize_t ret; + struct in_addr addr; + + eh->task = NULL; + ret = GNUNET_DISK_file_read (eh->r, + &eh->buf[eh->off], + sizeof(eh->buf) - eh->off); + if (ret > 0) + { + /* try to read more */ + eh->off += ret; + eh->task = GNUNET_SCHEDULER_add_read_file (GNUNET_TIME_UNIT_FOREVER_REL, + eh->r, + &read_external_ipv4, + eh); + return; + } + eh->ret = GNUNET_NAT_ERROR_EXTERNAL_IP_UTILITY_OUTPUT_INVALID; + if ((eh->off > 7) && (eh->buf[eh->off - 1] == '\n')) + { + eh->buf[eh->off - 1] = '\0'; + if (1 == inet_pton (AF_INET, eh->buf, &addr)) + { + if (0 == addr.s_addr) + eh->ret = + GNUNET_NAT_ERROR_EXTERNAL_IP_ADDRESS_INVALID; /* got 0.0.0.0 */ + else + eh->ret = GNUNET_NAT_ERROR_SUCCESS; + } + } + eh->cb (eh->cb_cls, + (GNUNET_NAT_ERROR_SUCCESS == eh->ret) ? &addr : NULL, + eh->ret); + GNUNET_NAT_mini_get_external_ipv4_cancel_ (eh); +} + + +/** + * (Asynchronously) signal error invoking `external-ip` to client. + * + * @param cls the `struct GNUNET_NAT_ExternalHandle` (freed) + */ +static void +signal_external_ip_error (void *cls) +{ + struct GNUNET_NAT_ExternalHandle *eh = cls; + + eh->task = NULL; + eh->cb (eh->cb_cls, NULL, eh->ret); + GNUNET_free (eh); +} + + +/** + * Try to get the external IPv4 address of this peer. + * + * @param cb function to call with result + * @param cb_cls closure for @a cb + * @return handle for cancellation (can only be used until @a cb is called), never NULL + */ +struct GNUNET_NAT_ExternalHandle * +GNUNET_NAT_mini_get_external_ipv4_ (GNUNET_NAT_IPCallback cb, void *cb_cls) +{ + struct GNUNET_NAT_ExternalHandle *eh; + + eh = GNUNET_new (struct GNUNET_NAT_ExternalHandle); + eh->cb = cb; + eh->cb_cls = cb_cls; + eh->ret = GNUNET_NAT_ERROR_SUCCESS; + if (GNUNET_SYSERR == + GNUNET_OS_check_helper_binary ("external-ip", GNUNET_NO, NULL)) + { + LOG (GNUNET_ERROR_TYPE_INFO, _ ("`external-ip' command not found\n")); + eh->ret = GNUNET_NAT_ERROR_EXTERNAL_IP_UTILITY_NOT_FOUND; + eh->task = GNUNET_SCHEDULER_add_now (&signal_external_ip_error, eh); + return eh; + } + LOG (GNUNET_ERROR_TYPE_DEBUG, + "Running `external-ip' to determine our external IP\n"); + eh->opipe = GNUNET_DISK_pipe (GNUNET_DISK_PF_BLOCKING_RW); + if (NULL == eh->opipe) + { + eh->ret = GNUNET_NAT_ERROR_IPC_FAILURE; + eh->task = GNUNET_SCHEDULER_add_now (&signal_external_ip_error, eh); + return eh; + } + eh->eip = GNUNET_OS_start_process (GNUNET_OS_INHERIT_STD_NONE, + NULL, + eh->opipe, + NULL, + "external-ip", + "external-ip", + NULL); + if (NULL == eh->eip) + { + GNUNET_DISK_pipe_close (eh->opipe); + eh->ret = GNUNET_NAT_ERROR_EXTERNAL_IP_UTILITY_FAILED; + eh->task = GNUNET_SCHEDULER_add_now (&signal_external_ip_error, eh); + return eh; + } + GNUNET_DISK_pipe_close_end (eh->opipe, GNUNET_DISK_PIPE_END_WRITE); + eh->r = GNUNET_DISK_pipe_handle (eh->opipe, GNUNET_DISK_PIPE_END_READ); + eh->task = GNUNET_SCHEDULER_add_read_file (GNUNET_TIME_UNIT_FOREVER_REL, + eh->r, + &read_external_ipv4, + eh); + return eh; +} + + +/** + * Cancel operation. + * + * @param eh operation to cancel + */ +void +GNUNET_NAT_mini_get_external_ipv4_cancel_ (struct GNUNET_NAT_ExternalHandle *eh) +{ + if (NULL != eh->eip) + { + (void) GNUNET_OS_process_kill (eh->eip, SIGKILL); + GNUNET_break (GNUNET_OK == GNUNET_OS_process_wait (eh->eip)); + GNUNET_OS_process_destroy (eh->eip); + } + if (NULL != eh->opipe) + { + GNUNET_DISK_pipe_close (eh->opipe); + eh->opipe = NULL; + } + if (NULL != eh->task) + { + GNUNET_SCHEDULER_cancel (eh->task); + eh->task = NULL; + } + GNUNET_free (eh); +} + + +/* ************************* upnpc calling ************************ */ + + +/** + * Handle to a mapping created with upnpc. + */ +struct GNUNET_NAT_MiniHandle +{ + /** + * Function to call on mapping changes. + */ + GNUNET_NAT_MiniAddressCallback ac; + + /** + * Closure for @e ac. + */ + void *ac_cls; + + /** + * Command used to install the map. + */ + struct GNUNET_OS_CommandHandle *map_cmd; + + /** + * Command used to refresh our map information. + */ + struct GNUNET_OS_CommandHandle *refresh_cmd; + + /** + * Command used to remove the mapping. + */ + struct GNUNET_OS_CommandHandle *unmap_cmd; + + /** + * Our current external mapping (if we have one). + */ + struct sockaddr_in current_addr; + + /** + * We check the mapping periodically to see if it + * still works. This task triggers the check. + */ + struct GNUNET_SCHEDULER_Task *refresh_task; + + /** + * Are we mapping TCP or UDP? + */ + int is_tcp; + + /** + * Did we succeed with creating a mapping? + */ + int did_map; + + /** + * Did we find our mapping during refresh scan? + */ + int found; + + /** + * Which port are we mapping? + */ + uint16_t port; +}; + + +/** + * Run "upnpc -l" to find out if our mapping changed. + * + * @param cls the `struct GNUNET_NAT_MiniHandle` + */ +static void +do_refresh (void *cls); + + +/** + * Process the output from the "upnpc -r" command. + * + * @param cls the `struct GNUNET_NAT_MiniHandle` + * @param line line of output, NULL at the end + */ +static void +process_map_output (void *cls, const char *line); + + +/** + * Run "upnpc -r" to map our internal port. + * + * @param mini our handle + */ +static void +run_upnpc_r (struct GNUNET_NAT_MiniHandle *mini) +{ + char pstr[6]; + + GNUNET_snprintf (pstr, sizeof(pstr), "%u", (unsigned int) mini->port); + mini->map_cmd = GNUNET_OS_command_run (&process_map_output, + mini, + MAP_TIMEOUT, + "upnpc", + "upnpc", + "-r", + pstr, + mini->is_tcp ? "tcp" : "udp", + NULL); + if (NULL == mini->map_cmd) + { + mini->ac (mini->ac_cls, + GNUNET_SYSERR, + NULL, + 0, + GNUNET_NAT_ERROR_UPNPC_FAILED); + return; + } +} + + +/** + * Process the output from "upnpc -l" to see if our + * external mapping changed. If so, do the notifications. + * + * @param cls the `struct GNUNET_NAT_MiniHandle` + * @param line line of output, NULL at the end + */ +static void +process_refresh_output (void *cls, const char *line) +{ + struct GNUNET_NAT_MiniHandle *mini = cls; + char pstr[9]; + const char *s; + unsigned int nport; + struct in_addr exip; + + if (NULL == line) + { + GNUNET_OS_command_stop (mini->refresh_cmd); + mini->refresh_cmd = NULL; + if (GNUNET_NO == mini->found) + { + /* mapping disappeared, try to re-create */ + if (GNUNET_YES == mini->did_map) + { + mini->ac (mini->ac_cls, + GNUNET_NO, + (const struct sockaddr *) &mini->current_addr, + sizeof(mini->current_addr), + GNUNET_NAT_ERROR_SUCCESS); + mini->did_map = GNUNET_NO; + } + run_upnpc_r (mini); + } + return; + } + if (! mini->did_map) + return; /* never mapped, won't find our mapping anyway */ + + /* we're looking for output of the form: + * "ExternalIPAddress = 12.134.41.124" */ + + s = strstr (line, "ExternalIPAddress = "); + if (NULL != s) + { + s += strlen ("ExternalIPAddress = "); + if (1 != inet_pton (AF_INET, s, &exip)) + return; /* skip */ + if (exip.s_addr == mini->current_addr.sin_addr.s_addr) + return; /* no change */ + /* update mapping */ + mini->ac (mini->ac_cls, + GNUNET_NO, + (const struct sockaddr *) &mini->current_addr, + sizeof(mini->current_addr), + GNUNET_NAT_ERROR_SUCCESS); + mini->current_addr.sin_addr = exip; + mini->ac (mini->ac_cls, + GNUNET_YES, + (const struct sockaddr *) &mini->current_addr, + sizeof(mini->current_addr), + GNUNET_NAT_ERROR_SUCCESS); + return; + } + /* + * we're looking for output of the form: + * + * "0 TCP 3000->192.168.2.150:3000 'libminiupnpc' ''" + * "1 UDP 3001->192.168.2.150:3001 'libminiupnpc' ''" + * + * the pattern we look for is: + * + * "%s TCP PORT->STRING:OURPORT *" or + * "%s UDP PORT->STRING:OURPORT *" + */GNUNET_snprintf (pstr, sizeof(pstr), ":%u ", mini->port); + if (NULL == (s = strstr (line, "->"))) + return; /* skip */ + if (NULL == strstr (s, pstr)) + return; /* skip */ + if (1 != sscanf (line, + (mini->is_tcp) ? "%*u TCP %u->%*s:%*u %*s" + : "%*u UDP %u->%*s:%*u %*s", + &nport)) + return; /* skip */ + mini->found = GNUNET_YES; + if (nport == ntohs (mini->current_addr.sin_port)) + return; /* no change */ + + /* external port changed, update mapping */ + mini->ac (mini->ac_cls, + GNUNET_NO, + (const struct sockaddr *) &mini->current_addr, + sizeof(mini->current_addr), + GNUNET_NAT_ERROR_SUCCESS); + mini->current_addr.sin_port = htons ((uint16_t) nport); + mini->ac (mini->ac_cls, + GNUNET_YES, + (const struct sockaddr *) &mini->current_addr, + sizeof(mini->current_addr), + GNUNET_NAT_ERROR_SUCCESS); +} + + +static void +do_refresh (void *cls) +{ + struct GNUNET_NAT_MiniHandle *mini = cls; + int ac; + + mini->refresh_task = + GNUNET_SCHEDULER_add_delayed (MAP_REFRESH_FREQ, &do_refresh, mini); + LOG (GNUNET_ERROR_TYPE_DEBUG, + "Running `upnpc' to check if our mapping still exists\n"); + mini->found = GNUNET_NO; + ac = GNUNET_NO; + if (NULL != mini->map_cmd) + { + /* took way too long, abort it! */ + GNUNET_OS_command_stop (mini->map_cmd); + mini->map_cmd = NULL; + ac = GNUNET_YES; + } + if (NULL != mini->refresh_cmd) + { + /* took way too long, abort it! */ + GNUNET_OS_command_stop (mini->refresh_cmd); + mini->refresh_cmd = NULL; + ac = GNUNET_YES; + } + mini->refresh_cmd = GNUNET_OS_command_run (&process_refresh_output, + mini, + MAP_TIMEOUT, + "upnpc", + "upnpc", + "-l", + NULL); + if (GNUNET_YES == ac) + mini->ac (mini->ac_cls, + GNUNET_SYSERR, + NULL, + 0, + GNUNET_NAT_ERROR_UPNPC_TIMEOUT); +} + + +/** + * Process the output from the 'upnpc -r' command. + * + * @param cls the `struct GNUNET_NAT_MiniHandle` + * @param line line of output, NULL at the end + */ +static void +process_map_output (void *cls, const char *line) +{ + struct GNUNET_NAT_MiniHandle *mini = cls; + const char *ipaddr; + char *ipa; + const char *pstr; + unsigned int port; + + if (NULL == line) + { + GNUNET_OS_command_stop (mini->map_cmd); + mini->map_cmd = NULL; + if (GNUNET_YES != mini->did_map) + mini->ac (mini->ac_cls, + GNUNET_SYSERR, + NULL, + 0, + GNUNET_NAT_ERROR_UPNPC_PORTMAP_FAILED); + if (NULL == mini->refresh_task) + mini->refresh_task = + GNUNET_SCHEDULER_add_delayed (MAP_REFRESH_FREQ, &do_refresh, mini); + return; + } + /* + * The upnpc output we're after looks like this: + * + * "external 87.123.42.204:3000 TCP is redirected to internal 192.168.2.150:3000" + */if ((NULL == (ipaddr = strstr (line, " "))) || + (NULL == (pstr = strstr (ipaddr, ":"))) || + (1 != sscanf (pstr + 1, "%u", &port))) + { + return; /* skip line */ + } + ipa = GNUNET_strdup (ipaddr + 1); + strstr (ipa, ":")[0] = '\0'; + if (1 != inet_pton (AF_INET, ipa, &mini->current_addr.sin_addr)) + { + GNUNET_free (ipa); + return; /* skip line */ + } + GNUNET_free (ipa); + + mini->current_addr.sin_port = htons (port); + mini->current_addr.sin_family = AF_INET; +#if HAVE_SOCKADDR_IN_SIN_LEN + mini->current_addr.sin_len = sizeof(struct sockaddr_in); +#endif + mini->did_map = GNUNET_YES; + mini->ac (mini->ac_cls, + GNUNET_YES, + (const struct sockaddr *) &mini->current_addr, + sizeof(mini->current_addr), + GNUNET_NAT_ERROR_SUCCESS); +} + + +/** + * Start mapping the given port using (mini)upnpc. This function + * should typically not be used directly (it is used within the + * general-purpose #GNUNET_NAT_register() code). However, it can be + * used if specifically UPnP-based NAT traversal is to be used or + * tested. + * + * @param port port to map + * @param is_tcp #GNUNET_YES to map TCP, #GNUNET_NO for UDP + * @param ac function to call with mapping result + * @param ac_cls closure for @a ac + * @return NULL on error (no 'upnpc' installed) + */ +struct GNUNET_NAT_MiniHandle * +GNUNET_NAT_mini_map_start (uint16_t port, + int is_tcp, + GNUNET_NAT_MiniAddressCallback ac, + void *ac_cls) +{ + struct GNUNET_NAT_MiniHandle *ret; + + if (GNUNET_SYSERR == GNUNET_OS_check_helper_binary ("upnpc", GNUNET_NO, NULL)) + { + LOG (GNUNET_ERROR_TYPE_INFO, _ ("`upnpc' command not found\n")); + ac (ac_cls, GNUNET_SYSERR, NULL, 0, GNUNET_NAT_ERROR_UPNPC_NOT_FOUND); + return NULL; + } + LOG (GNUNET_ERROR_TYPE_DEBUG, "Running `upnpc' to install mapping\n"); + ret = GNUNET_new (struct GNUNET_NAT_MiniHandle); + ret->ac = ac; + ret->ac_cls = ac_cls; + ret->is_tcp = is_tcp; + ret->port = port; + ret->refresh_task = + GNUNET_SCHEDULER_add_delayed (MAP_REFRESH_FREQ, &do_refresh, ret); + run_upnpc_r (ret); + return ret; +} + + +/** + * Process output from our 'unmap' command. + * + * @param cls the `struct GNUNET_NAT_MiniHandle` + * @param line line of output, NULL at the end + */ +static void +process_unmap_output (void *cls, const char *line) +{ + struct GNUNET_NAT_MiniHandle *mini = cls; + + if (NULL == line) + { + LOG (GNUNET_ERROR_TYPE_DEBUG, "UPnP unmap done\n"); + GNUNET_OS_command_stop (mini->unmap_cmd); + mini->unmap_cmd = NULL; + GNUNET_free (mini); + return; + } + /* we don't really care about the output... */ +} + + +void +GNUNET_NAT_mini_map_stop (struct GNUNET_NAT_MiniHandle *mini) +{ + char pstr[6]; + + if (NULL != mini->refresh_task) + { + GNUNET_SCHEDULER_cancel (mini->refresh_task); + mini->refresh_task = NULL; + } + if (NULL != mini->refresh_cmd) + { + GNUNET_OS_command_stop (mini->refresh_cmd); + mini->refresh_cmd = NULL; + } + if (NULL != mini->map_cmd) + { + GNUNET_OS_command_stop (mini->map_cmd); + mini->map_cmd = NULL; + } + if (GNUNET_NO == mini->did_map) + { + GNUNET_free (mini); + return; + } + mini->ac (mini->ac_cls, + GNUNET_NO, + (const struct sockaddr *) &mini->current_addr, + sizeof(mini->current_addr), + GNUNET_NAT_ERROR_SUCCESS); + /* Note: oddly enough, deletion uses the external port whereas + * addition uses the internal port; this rarely matters since they + * often are the same, but it might... */ + GNUNET_snprintf (pstr, + sizeof(pstr), + "%u", + (unsigned int) ntohs (mini->current_addr.sin_port)); + LOG (GNUNET_ERROR_TYPE_DEBUG, + "Unmapping port %u with UPnP\n", + ntohs (mini->current_addr.sin_port)); + mini->unmap_cmd = GNUNET_OS_command_run (&process_unmap_output, + mini, + UNMAP_TIMEOUT, + "upnpc", + "upnpc", + "-d", + pstr, + mini->is_tcp ? "tcp" : "udp", + NULL); +} + + +/* end of gnunet-service-nat_mini.c */ diff --git a/src/service/nat/gnunet-service-nat_mini.h b/src/service/nat/gnunet-service-nat_mini.h new file mode 100644 index 000000000..dffc9758a --- /dev/null +++ b/src/service/nat/gnunet-service-nat_mini.h @@ -0,0 +1,128 @@ +/* + This file is part of GNUnet. + Copyright (C) 2011-2014, 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 . + + SPDX-License-Identifier: AGPL3.0-or-later + */ + +/** + * @file nat/gnunet-service-nat_mini.c + * @brief functions for interaction with miniupnp; tested with miniupnpc 1.5 + * @author Christian Grothoff + */ +#ifndef GNUNET_SERVICE_NAT_MINI_H +#define GNUNET_SERVICE_NAT_MINI_H + + +/** + * Signature of a callback that is given an IP address. + * + * @param cls closure + * @param addr the address, NULL on errors + * @param result #GNUNET_NAT_ERROR_SUCCESS on success, otherwise the specific error code + */ +typedef void +(*GNUNET_NAT_IPCallback) (void *cls, + const struct in_addr *addr, + enum GNUNET_NAT_StatusCode result); + + +/** + * Opaque handle to cancel #GNUNET_NAT_mini_get_external_ipv4() operation. + */ +struct GNUNET_NAT_ExternalHandle; + + +/** + * Try to get the external IPv4 address of this peer. + * + * @param cb function to call with result + * @param cb_cls closure for @a cb + * @return handle for cancellation (can only be used until @a cb is called), NULL on error + */ +struct GNUNET_NAT_ExternalHandle * +GNUNET_NAT_mini_get_external_ipv4_ (GNUNET_NAT_IPCallback cb, + void *cb_cls); + + +/** + * Cancel operation. + * + * @param eh operation to cancel + */ +void +GNUNET_NAT_mini_get_external_ipv4_cancel_ (struct + GNUNET_NAT_ExternalHandle *eh); + + +/** + * Handle to a mapping created with upnpc. + */ +struct GNUNET_NAT_MiniHandle; + + +/** + * Signature of the callback passed to #GNUNET_NAT_register() for + * a function to call whenever our set of 'valid' addresses changes. + * + * @param cls closure + * @param add_remove #GNUNET_YES to mean the new public IP address, #GNUNET_NO to mean + * the previous (now invalid) one, #GNUNET_SYSERR indicates an error + * @param addr either the previous or the new public IP address + * @param addrlen actual length of the @a addr + * @param result #GNUNET_NAT_ERROR_SUCCESS on success, otherwise the specific error code + */ +typedef void +(*GNUNET_NAT_MiniAddressCallback) (void *cls, + int add_remove, + const struct sockaddr *addr, + socklen_t addrlen, + enum GNUNET_NAT_StatusCode result); + + +/** + * Start mapping the given port using (mini)upnpc. This function + * should typically not be used directly (it is used within the + * general-purpose #GNUNET_NAT_register() code). However, it can be + * used if specifically UPnP-based NAT traversal is to be used or + * tested. + * + * @param port port to map + * @param is_tcp #GNUNET_YES to map TCP, #GNUNET_NO for UDP + * @param ac function to call with mapping result + * @param ac_cls closure for @a ac + * @return NULL on error + */ +struct GNUNET_NAT_MiniHandle * +GNUNET_NAT_mini_map_start (uint16_t port, + int is_tcp, + GNUNET_NAT_MiniAddressCallback ac, + void *ac_cls); + + +/** + * Remove a mapping created with (mini)upnpc. Calling + * this function will give 'upnpc' 1s to remove the mapping, + * so while this function is non-blocking, a task will be + * left with the scheduler for up to 1s past this call. + * + * @param mini the handle + */ +void +GNUNET_NAT_mini_map_stop (struct GNUNET_NAT_MiniHandle *mini); + + +#endif diff --git a/src/service/nat/gnunet-service-nat_stun.c b/src/service/nat/gnunet-service-nat_stun.c new file mode 100644 index 000000000..203728ebf --- /dev/null +++ b/src/service/nat/gnunet-service-nat_stun.c @@ -0,0 +1,215 @@ +/* + This file is part of GNUnet. + Copyright (C) 2009, 2015, 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 . + + SPDX-License-Identifier: AGPL3.0-or-later + */ +/** + * This code provides some support for doing STUN transactions. We + * receive the simplest possible packet as the STUN server and try + * to respond properly. + * + * All STUN packets start with a simple header made of a type, + * length (excluding the header) and a 16-byte random transaction id. + * Following the header we may have zero or more attributes, each + * structured as a type, length and a value (whose format depends + * on the type, but often contains addresses). + * Of course all fields are in network format. + * + * This code was based on ministun.c. + * + * @file nat/gnunet-service-nat_stun.c + * @brief Functions for STUN functionality + * @author Bruno Souza Cabral + */ + +#include "platform.h" +#include "gnunet_util_lib.h" +#include "nat_stun.h" + +#define LOG(kind, ...) GNUNET_log_from (kind, "stun", __VA_ARGS__) + + +/** + * Context for #stun_get_mapped(). + * Used to store state across processing attributes. + */ +struct StunState +{ + uint16_t attr; +}; + + +/** + * Extract the STUN_MAPPED_ADDRESS from the stun response. + * This is used as a callback for stun_handle_response + * when called from stun_request. + * + * @param[out] st pointer where we will set the type + * @param attr received stun attribute + * @param magic Magic cookie + * @param[out] arg pointer to a sockaddr_in where we will set the reported IP and port + * @return #GNUNET_OK if @a arg was initialized + */ +static int +stun_get_mapped (struct StunState *st, + const struct stun_attr *attr, + uint32_t magic, + struct sockaddr_in *arg) +{ + const struct stun_addr *returned_addr; + struct sockaddr_in *sa = (struct sockaddr_in *) arg; + uint16_t type = ntohs (attr->attr); + + switch (type) + { + case STUN_MAPPED_ADDRESS: + if ((st->attr == STUN_XOR_MAPPED_ADDRESS) || + (st->attr == STUN_MS_XOR_MAPPED_ADDRESS)) + return GNUNET_NO; + magic = 0; + break; + + case STUN_MS_XOR_MAPPED_ADDRESS: + if (st->attr == STUN_XOR_MAPPED_ADDRESS) + return GNUNET_NO; + break; + + case STUN_XOR_MAPPED_ADDRESS: + break; + + default: + return GNUNET_NO; + } + + if (ntohs (attr->len) < sizeof(struct stun_addr)) + return GNUNET_NO; + returned_addr = (const struct stun_addr *) (attr + 1); + if (AF_INET != returned_addr->family) + return GNUNET_NO; + st->attr = type; + sa->sin_family = AF_INET; + sa->sin_port = returned_addr->port ^ htons (ntohl (magic) >> 16); + sa->sin_addr.s_addr = returned_addr->addr ^ magic; + return GNUNET_OK; +} + + +/** + * Handle an incoming STUN response. Do some basic sanity checks on + * packet size and content, try to extract information. + * At the moment this only processes BIND requests, + * and returns the externally visible address of the original + * request. + * + * @param data the packet + * @param len the length of the packet in @a data + * @param[out] arg sockaddr_in where we will set our discovered address + * @return #GNUNET_OK on success, + * #GNUNET_NO if the packet is invalid (not a stun packet) + */ +int +GNUNET_NAT_stun_handle_packet_ (const void *data, + size_t len, + struct sockaddr_in *arg) +{ + const struct stun_header *hdr; + const struct stun_attr *attr; + struct StunState st; + uint32_t advertised_message_size; + uint32_t message_magic_cookie; + int ret = GNUNET_SYSERR; + + /* On entry, 'len' is the length of the UDP payload. After the + * initial checks it becomes the size of unprocessed options, + * while 'data' is advanced accordingly. + */ + if (len < sizeof(struct stun_header)) + { + LOG (GNUNET_ERROR_TYPE_DEBUG, + "Packet too short to be a STUN packet\n"); + return GNUNET_NO; + } + hdr = data; + /* Skip header as it is already in hdr */ + len -= sizeof(struct stun_header); + data += sizeof(struct stun_header); + + /* len as advertised in the message */ + advertised_message_size = ntohs (hdr->msglen); + message_magic_cookie = ntohl (hdr->magic); + /* Compare if the cookie match */ + if (STUN_MAGIC_COOKIE != message_magic_cookie) + { + LOG (GNUNET_ERROR_TYPE_DEBUG, + "Invalid magic cookie for STUN packet\n"); + return GNUNET_NO; + } + + LOG (GNUNET_ERROR_TYPE_INFO, + "STUN Packet, msg %s (%04x), length: %d\n", + stun_msg2str (ntohs (hdr->msgtype)), + ntohs (hdr->msgtype), + advertised_message_size); + if (advertised_message_size > len) + { + LOG (GNUNET_ERROR_TYPE_INFO, + "Scrambled STUN packet length (got %d, expecting %d)\n", + advertised_message_size, + (int) len); + return GNUNET_NO; + } + len = advertised_message_size; + memset (&st, 0, sizeof(st)); + + while (len > 0) + { + if (len < sizeof(struct stun_attr)) + { + LOG (GNUNET_ERROR_TYPE_INFO, + "Attribute too short (got %d, expecting %d)\n", + (int) len, + (int) sizeof(struct stun_attr)); + break; + } + attr = (const struct stun_attr *) data; + + /* compute total attribute length */ + advertised_message_size = ntohs (attr->len) + sizeof(struct stun_attr); + + /* Check if we still have space in our buffer */ + if (advertised_message_size > len) + { + LOG (GNUNET_ERROR_TYPE_INFO, + "Inconsistent attribute (length %d exceeds remaining msg len %d)\n", + advertised_message_size, + (int) len); + break; + } + if (GNUNET_OK == + stun_get_mapped (&st, + attr, + hdr->magic, + arg)) + ret = GNUNET_OK; + data += advertised_message_size; + len -= advertised_message_size; + } + return ret; +} + + +/* end of gnunet-service-nat_stun.c */ diff --git a/src/service/nat/gnunet-service-nat_stun.h b/src/service/nat/gnunet-service-nat_stun.h new file mode 100644 index 000000000..912fc3b46 --- /dev/null +++ b/src/service/nat/gnunet-service-nat_stun.h @@ -0,0 +1,61 @@ +/* + This file is part of GNUnet. + Copyright (C) 2009, 2015, 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 . + + SPDX-License-Identifier: AGPL3.0-or-later + */ +/** + * This code provides some support for doing STUN transactions. We + * receive the simplest possible packet as the STUN server and try + * to respond properly. + * + * All STUN packets start with a simple header made of a type, + * length (excluding the header) and a 16-byte random transaction id. + * Following the header we may have zero or more attributes, each + * structured as a type, length and a value (whose format depends + * on the type, but often contains addresses). + * Of course all fields are in network format. + * + * This code was based on ministun.c. + * + * @file nat/gnunet-service-nat_stun.h + * @brief Functions for STUN functionality + * @author Bruno Souza Cabral + */ +#ifndef GNUNET_SERVICE_NAT_STUN_H +#define GNUNET_SERVICE_NAT_STUN_H + +#include "platform.h" + +/** + * Handle an incoming STUN response. Do some basic sanity checks on + * packet size and content, try to extract information. + * At the moment this only processes BIND requests, + * and returns the externally visible address of the original + * request. + * + * @param data the packet + * @param len the length of the packet in @a data + * @param[out] arg sockaddr_in where we will set our discovered address + * @return #GNUNET_OK on success, + * #GNUNET_NO if the packet is invalid (not a stun packet) + */ +int +GNUNET_NAT_stun_handle_packet_ (const void *data, + size_t len, + struct sockaddr_in *arg); + +#endif diff --git a/src/service/nat/meson.build b/src/service/nat/meson.build new file mode 100644 index 000000000..e629f6411 --- /dev/null +++ b/src/service/nat/meson.build @@ -0,0 +1,75 @@ +libgnunetnat_src = ['nat_api.c', + 'nat_api_stun.c'] + +gnunetservicenat_src = ['gnunet-service-nat.c', + 'gnunet-service-nat.c', + 'gnunet-service-nat_externalip.c', + 'gnunet-service-nat_stun.c', + 'gnunet-service-nat_mini.c', + 'gnunet-service-nat_helper.c'] + +configure_file(input : 'nat.conf.in', + output : 'nat.conf', + configuration : cdata, + install: true, + install_dir: pkgcfgdir) + + +if get_option('monolith') + foreach p : libgnunetnat_src + gnunetservicenat_src + gnunet_src += 'nat/' + p + endforeach + subdir_done() +endif + +libgnunetnat = library('gnunetnat', + libgnunetnat_src, + soversion: '2', + version: '2.0.0', + dependencies: libgnunetutil_dep, + include_directories: [incdir, configuration_inc], + install: true, + install_dir: get_option('libdir')) +libgnunetnat_dep = declare_dependency(link_with : libgnunetnat) +pkg.generate(libgnunetnat, url: 'https://www.gnunet.org', + description : 'Provides API for accessing the NAT service') +libgnunetnat = library('gnunetnatnew', + ['nat_api.c', + 'nat_api_stun.c'], + soversion: '2', + version: '2.0.0', + dependencies: libgnunetutil_dep, + include_directories: [incdir, configuration_inc], + install: true, + install_dir: get_option('libdir')) +libgnunetnat_dep = declare_dependency(link_with : libgnunetnat) + +executable ('gnunet-nat', + ['gnunet-nat.c'], + dependencies: [libgnunetnat_dep, libgnunetutil_dep], + include_directories: [incdir, configuration_inc], + install: true, + install_dir: get_option('bindir')) + +executable ('gnunet-service-nat', + gnunetservicenat_src, + dependencies: [libgnunetnat_dep, libgnunetutil_dep, + libgnunetstatistics_dep], + include_directories: [incdir, configuration_inc], + install: true, + install_dir: get_option('libdir')/'gnunet'/'libexec') +executable ('gnunet-helper-nat-server', + ['gnunet-helper-nat-server.c'], + dependencies: [libgnunetnat_dep, libgnunetutil_dep, + libgnunetstatistics_dep], + include_directories: [incdir, configuration_inc], + install: true, + install_dir: get_option('libdir')/'gnunet'/'libexec') +executable ('gnunet-helper-nat-client', + ['gnunet-helper-nat-client.c'], + dependencies: [libgnunetnat_dep, libgnunetutil_dep, + libgnunetstatistics_dep], + include_directories: [incdir, configuration_inc], + install: true, + install_dir: get_option('libdir')/'gnunet'/'libexec') + diff --git a/src/service/nat/nat.conf.in b/src/service/nat/nat.conf.in new file mode 100644 index 000000000..4c068c394 --- /dev/null +++ b/src/service/nat/nat.conf.in @@ -0,0 +1,43 @@ +[nat] +START_ON_DEMAND = @START_ON_DEMAND@ +@UNIXONLY@ PORT = 2121 +HOSTNAME = localhost +BINARY = gnunet-service-nat +ACCEPT_FROM = 127.0.0.1; +ACCEPT_FROM6 = ::1; +UNIXPATH = $GNUNET_RUNTIME_DIR/gnunet-service-nat.sock +UNIX_MATCH_UID = YES +UNIX_MATCH_GID = YES + +# Enable UPNP by default? +ENABLE_UPNP = YES + +# Enable scanning for all system IP addresses? +ENABLE_IPSCAN = YES + +# Disable IPv6 support +# FIXME: move entirely to transport plugins! +DISABLEV6 = NO + +# How often do we query the DNS resolver +# for our hostname (to get our own IP) +HOSTNAME_DNS_FREQUENCY = 20 min + +# How often do we iterate over our +# network interfaces to check for changes +# in our IP address? +IFC_SCAN_FREQUENCY = 15 min + +# How often do we query the DNS resolver +# for our hostname (to get our own IP) +DYNDNS_FREQUENCY = 7 min + +# SHOULD USE STUN ? +USE_STUN = YES +STUN_FREQUENCY = 5 min +# Default list of stun servers +STUN_SERVERS = stun.gnunet.org stun.services.mozilla.com:3478 stun.ekiga.net:3478 + +# After how long do we consider STUN data stale? +STUN_STALE = 60 min + diff --git a/src/service/nat/nat.h b/src/service/nat/nat.h new file mode 100644 index 000000000..1d8648aaf --- /dev/null +++ b/src/service/nat/nat.h @@ -0,0 +1,223 @@ +/* + This file is part of GNUnet. + Copyright (C) 2011, 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 . + + SPDX-License-Identifier: AGPL3.0-or-later + */ + +/** + * @file src/nat/nat.h + * @brief Messages for interaction with gnunet-nat-server and gnunet-nat-service + * @author Christian Grothoff + * + */ +#ifndef NAT_H +#define NAT_H +#include "gnunet_util_lib.h" + + +GNUNET_NETWORK_STRUCT_BEGIN + +/** + * Request to test NAT traversal, sent to the gnunet-nat-server + * (not the service!). + */ +struct GNUNET_NAT_TestMessage +{ + /** + * Header with type #GNUNET_MESSAGE_TYPE_NAT_TEST + */ + struct GNUNET_MessageHeader header; + + /** + * IPv4 target IP address + */ + uint32_t dst_ipv4; + + /** + * Port to use, 0 to send dummy ICMP response. + */ + uint16_t dport; + + /** + * Data to send OR advertised-port (in NBO) to use for dummy ICMP. + */ + uint16_t data; + + /** + * #GNUNET_YES for TCP, #GNUNET_NO for UDP. + */ + int32_t is_tcp; +}; + + +/** + * Flags specifying the events this client would be + * interested in being told about. + */ +enum GNUNET_NAT_RegisterFlags +{ + /** + * This client does not want any notifications. + */ + GNUNET_NAT_RF_NONE = 0, + + /** + * This client wants to be informed about changes to our + * applicable addresses. + */ + GNUNET_NAT_RF_ADDRESSES = 1, + + /** + * This client supports address reversal. + */ + GNUNET_NAT_RF_REVERSAL = 2 +}; + + +/** + * Message sent by a client to register with its addresses. + */ +struct GNUNET_NAT_RegisterMessage +{ + /** + * Header with type #GNUNET_MESSAGE_TYPE_NAT_REGISTER + */ + struct GNUNET_MessageHeader header; + + /** + * An `enum GNUNET_NAT_RegisterFlags`. + */ + uint8_t flags; + + /** + * Client's IPPROTO, e.g. IPPROTO_UDP or IPPROTO_TCP. + */ + uint8_t proto; + + /** + * Number of bytes in the string that follow which + * specifies a section name in the configuration. + */ + uint16_t str_len GNUNET_PACKED; + + /** + * Number of addresses that this service is bound to that follow. + * Given as an array of "struct sockaddr" entries, the size of + * each entry being determined by the "sa_family" at the beginning. + */ + uint16_t num_addrs GNUNET_PACKED; + + /* Followed by @e num_addrs addresses of type 'struct + sockaddr' */ + + /* Followed by @e str_len section name to use for options */ +}; + + +/** + * Client telling the service to (possibly) handle a STUN message. + */ +struct GNUNET_NAT_HandleStunMessage +{ + /** + * Header with type #GNUNET_MESSAGE_TYPE_NAT_HANDLE_STUN + */ + struct GNUNET_MessageHeader header; + + /** + * Size of the sender address included, in NBO. + */ + uint16_t sender_addr_size; + + /** + * Number of bytes of payload included, in NBO. + */ + uint16_t payload_size; + + /* followed by a `struct sockaddr` of @e sender_addr_size bytes */ + + /* followed by payload with @e payload_size bytes */ +}; + + +/** + * Client asking the service to initiate connection reversal. + */ +struct GNUNET_NAT_RequestConnectionReversalMessage +{ + /** + * Header with type #GNUNET_MESSAGE_TYPE_NAT_REQUEST_CONNECTION_REVERSAL + */ + struct GNUNET_MessageHeader header; + + /** + * Size of the local address included, in NBO. + */ + uint16_t local_addr_size; + + /** + * Size of the remote address included, in NBO. + */ + uint16_t remote_addr_size; + + /* followed by a `struct sockaddr` of @e local_addr_size bytes */ + + /* followed by a `struct sockaddr` of @e remote_addr_size bytes */ +}; + + +/** + * Service telling a client that connection reversal was requested. + */ +struct GNUNET_NAT_ConnectionReversalRequestedMessage +{ + /** + * Header with type #GNUNET_MESSAGE_TYPE_NAT_CONNECTION_REVERSAL_REQUESTED + */ + struct GNUNET_MessageHeader header; + + /* followed by a `struct sockaddr_in` */ +}; + + +/** + * Service notifying the client about changes in the set of + * addresses it has. + */ +struct GNUNET_NAT_AddressChangeNotificationMessage +{ + /** + * Header with type #GNUNET_MESSAGE_TYPE_NAT_ADDRESS_CHANGE + */ + struct GNUNET_MessageHeader header; + + /** + * #GNUNET_YES to add, #GNUNET_NO to remove the address from the list. + */ + int32_t add_remove GNUNET_PACKED; + + /** + * Type of the address, an `enum GNUNET_NAT_AddressClass` in NBO. + */ + uint32_t addr_class GNUNET_PACKED; + /* followed by a `struct sockaddr` */ +}; + + +GNUNET_NETWORK_STRUCT_END + +#endif diff --git a/src/service/nat/nat_api.c b/src/service/nat/nat_api.c new file mode 100644 index 000000000..31f8f388d --- /dev/null +++ b/src/service/nat/nat_api.c @@ -0,0 +1,702 @@ +/* + This file is part of GNUnet. + Copyright (C) 2007-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 . + + SPDX-License-Identifier: AGPL3.0-or-later + */ + +/** + * @author Christian Grothoff + * @author Milan Bouchet-Valat + * + * @file nat/nat_api.c + * Service for handling UPnP and NAT-PMP port forwarding + * and external IP address retrieval + */ +#include "platform.h" +#include "gnunet_nat_service.h" +#include "nat.h" +#include "nat_stun.h" + + +/** + * Entry in DLL of addresses of this peer. + */ +struct AddrEntry +{ + /** + * DLL. + */ + struct AddrEntry *next; + + /** + * DLL. + */ + struct AddrEntry *prev; + + /** + * Place where the application can store data (on add, + * and retrieve on remove). + */ + void *app_ctx; + + /** + * Address class of the address. + */ + enum GNUNET_NAT_AddressClass ac; + + /** + * Number of bytes that follow. + */ + socklen_t addrlen; +}; + + +/** + * Handle for active NAT registrations. + */ +struct GNUNET_NAT_Handle +{ + /** + * Configuration we use. + */ + const struct GNUNET_CONFIGURATION_Handle *cfg; + + /** + * Message queue for communicating with the NAT service. + */ + struct GNUNET_MQ_Handle *mq; + + /** + * Our registration message. + */ + struct GNUNET_MessageHeader *reg; + + /** + * Head of address DLL. + */ + struct AddrEntry *ae_head; + + /** + * Tail of address DLL. + */ + struct AddrEntry *ae_tail; + + /** + * Function to call when our addresses change. + */ + GNUNET_NAT_AddressCallback address_callback; + + /** + * Function to call when another peer requests connection reversal. + */ + GNUNET_NAT_ReversalCallback reversal_callback; + + /** + * Closure for the various callbacks. + */ + void *callback_cls; + + /** + * Task scheduled to reconnect to the service. + */ + struct GNUNET_SCHEDULER_Task *reconnect_task; + + /** + * How long to wait until we reconnect. + */ + struct GNUNET_TIME_Relative reconnect_delay; +}; + + +/** + * Task to connect to the NAT service. + * + * @param cls our `struct GNUNET_NAT_Handle *` + */ +static void +do_connect (void *cls); + + +/** + * Task to connect to the NAT service. + * + * @param nh handle to reconnect + */ +static void +reconnect (struct GNUNET_NAT_Handle *nh) +{ + struct AddrEntry *ae; + + if (NULL != nh->mq) + { + GNUNET_MQ_destroy (nh->mq); + nh->mq = NULL; + } + while (NULL != (ae = nh->ae_head)) + { + GNUNET_CONTAINER_DLL_remove (nh->ae_head, nh->ae_tail, ae); + nh->address_callback (nh->callback_cls, + &ae->app_ctx, + GNUNET_NO, + ae->ac, + (const struct sockaddr *) &ae[1], + ae->addrlen); + GNUNET_free (ae); + } + nh->reconnect_delay = GNUNET_TIME_STD_BACKOFF (nh->reconnect_delay); + nh->reconnect_task = + GNUNET_SCHEDULER_add_delayed (nh->reconnect_delay, &do_connect, nh); +} + + +/** + * Check connection reversal request. + * + * @param cls our `struct GNUNET_NAT_Handle` + * @param crm the message + * @return #GNUNET_OK if @a crm is well-formed + */ +static int +check_connection_reversal_request ( + void *cls, + const struct GNUNET_NAT_ConnectionReversalRequestedMessage *crm) +{ + if (ntohs (crm->header.size) != sizeof(*crm) + sizeof(struct sockaddr_in)) + { + GNUNET_break (0); + return GNUNET_SYSERR; + } + return GNUNET_OK; +} + + +/** + * Handle connection reversal request. + * + * @param cls our `struct GNUNET_NAT_Handle` + * @param crm the message + */ +static void +handle_connection_reversal_request ( + void *cls, + const struct GNUNET_NAT_ConnectionReversalRequestedMessage *crm) +{ + struct GNUNET_NAT_Handle *nh = cls; + + nh->reversal_callback (nh->callback_cls, + (const struct sockaddr *) &crm[1], + sizeof(struct sockaddr_in)); +} + + +/** + * Check address change notification. + * + * @param cls our `struct GNUNET_NAT_Handle` + * @param acn the message + * @return #GNUNET_OK if @a crm is well-formed + */ +static int +check_address_change_notification ( + void *cls, + const struct GNUNET_NAT_AddressChangeNotificationMessage *acn) +{ + size_t alen = ntohs (acn->header.size) - sizeof(*acn); + + switch (alen) + { + case sizeof(struct sockaddr_in): { + const struct sockaddr_in *s4 = (const struct sockaddr_in *) &acn[1]; + if (AF_INET != s4->sin_family) + { + GNUNET_break (0); + return GNUNET_SYSERR; + } + } + break; + + case sizeof(struct sockaddr_in6): { + const struct sockaddr_in6 *s6 = (const struct sockaddr_in6 *) &acn[1]; + if (AF_INET6 != s6->sin6_family) + { + GNUNET_break (0); + return GNUNET_SYSERR; + } + } + break; + + default: + GNUNET_break (0); + return GNUNET_SYSERR; + } + return GNUNET_OK; +} + + +/** + * Handle connection reversal request. + * + * @param cls our `struct GNUNET_NAT_Handle` + * @param acn the message + */ +static void +handle_address_change_notification ( + void *cls, + const struct GNUNET_NAT_AddressChangeNotificationMessage *acn) +{ + struct GNUNET_NAT_Handle *nh = cls; + size_t alen = ntohs (acn->header.size) - sizeof(*acn); + const struct sockaddr *sa = (const struct sockaddr *) &acn[1]; + enum GNUNET_NAT_AddressClass ac; + struct AddrEntry *ae; + + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Received address change notification\n"); + ac = (enum GNUNET_NAT_AddressClass) ntohl (acn->addr_class); + if (GNUNET_YES == ntohl (acn->add_remove)) + { + ae = GNUNET_malloc (sizeof(*ae) + alen); + ae->ac = ac; + ae->addrlen = alen; + GNUNET_memcpy (&ae[1], sa, alen); + GNUNET_CONTAINER_DLL_insert (nh->ae_head, nh->ae_tail, ae); + nh->address_callback (nh->callback_cls, + &ae->app_ctx, + ntohl (acn->add_remove), + ac, + sa, + alen); + } + else + { + for (ae = nh->ae_head; NULL != ae; ae = ae->next) + if ((ae->addrlen == alen) && (0 == memcmp (&ae[1], sa, alen))) + break; + if (NULL == ae) + { + GNUNET_break (0); + reconnect (nh); + return; + } + GNUNET_CONTAINER_DLL_remove (nh->ae_head, nh->ae_tail, ae); + nh->address_callback (nh->callback_cls, + &ae->app_ctx, + ntohl (acn->add_remove), + ac, + sa, + alen); + GNUNET_free (ae); + } +} + + +/** + * Handle queue errors by reconnecting to NAT. + * + * @param cls the `struct GNUNET_NAT_Handle *` + * @param error details about the error + */ +static void +mq_error_handler (void *cls, + enum GNUNET_MQ_Error error) +{ + struct GNUNET_NAT_Handle *nh = cls; + + reconnect (nh); +} + + +/** + * Task to connect to the NAT service. + * + * @param cls our `struct GNUNET_NAT_Handle *` + */ +static void +do_connect (void *cls) +{ + struct GNUNET_NAT_Handle *nh = cls; + struct GNUNET_MQ_MessageHandler handlers[] = { + GNUNET_MQ_hd_var_size ( + connection_reversal_request, + GNUNET_MESSAGE_TYPE_NAT_CONNECTION_REVERSAL_REQUESTED, + struct GNUNET_NAT_ConnectionReversalRequestedMessage, + nh), + GNUNET_MQ_hd_var_size ( + address_change_notification, + GNUNET_MESSAGE_TYPE_NAT_ADDRESS_CHANGE, + struct GNUNET_NAT_AddressChangeNotificationMessage, + nh), + GNUNET_MQ_handler_end () + }; + struct GNUNET_MQ_Envelope *env; + + nh->reconnect_task = NULL; + nh->mq = + GNUNET_CLIENT_connect (nh->cfg, + "nat", + handlers, + &mq_error_handler, + nh); + if (NULL == nh->mq) + { + reconnect (nh); + return; + } + env = GNUNET_MQ_msg_copy (nh->reg); + GNUNET_MQ_send (nh->mq, + env); +} + + +struct GNUNET_NAT_Handle * +GNUNET_NAT_register (const struct GNUNET_CONFIGURATION_Handle *cfg, + const char *config_section, + uint8_t proto, + unsigned int num_addrs, + const struct sockaddr **addrs, + const socklen_t *addrlens, + GNUNET_NAT_AddressCallback address_callback, + GNUNET_NAT_ReversalCallback reversal_callback, + void *callback_cls) +{ + struct GNUNET_NAT_Handle *nh; + struct GNUNET_NAT_RegisterMessage *rm; + size_t len; + size_t str_len; + char *off; + + len = 0; + for (unsigned int i = 0; i < num_addrs; i++) + len += addrlens[i]; + str_len = strlen (config_section) + 1; + len += str_len; + if ( (len > GNUNET_MAX_MESSAGE_SIZE - sizeof(*rm)) || + (num_addrs > UINT16_MAX) || + (str_len > UINT16_MAX) ) + { + GNUNET_break (0); + return NULL; + } + rm = GNUNET_malloc (sizeof(*rm) + len); + rm->header.size = htons (sizeof(*rm) + len); + rm->header.type = htons (GNUNET_MESSAGE_TYPE_NAT_REGISTER); + rm->flags = GNUNET_NAT_RF_NONE; + if (NULL != address_callback) + rm->flags |= GNUNET_NAT_RF_ADDRESSES; + if (NULL != reversal_callback) + rm->flags |= GNUNET_NAT_RF_REVERSAL; + rm->proto = proto; + rm->str_len = htons (str_len); + rm->num_addrs = htons ((uint16_t) num_addrs); + off = (char *) &rm[1]; + for (unsigned int i = 0; i < num_addrs; i++) + { + switch (addrs[i]->sa_family) + { + case AF_INET: + if (sizeof(struct sockaddr_in) != addrlens[i]) + { + GNUNET_break (0); + GNUNET_free (rm); + return NULL; + } + break; + + case AF_INET6: + if (sizeof(struct sockaddr_in6) != addrlens[i]) + { + GNUNET_break (0); + GNUNET_free (rm); + return NULL; + } + break; + +#if AF_UNIX + case AF_UNIX: + if (sizeof(struct sockaddr_un) != addrlens[i]) + { + GNUNET_break (0); + GNUNET_free (rm); + return NULL; + } + break; +#endif + default: + GNUNET_break (0); + GNUNET_free (rm); + return NULL; + } + GNUNET_memcpy (off, addrs[i], addrlens[i]); + off += addrlens[i]; + } + GNUNET_memcpy (off, config_section, str_len); + + nh = GNUNET_new (struct GNUNET_NAT_Handle); + nh->reg = &rm->header; + nh->cfg = cfg; + nh->address_callback = address_callback; + nh->reversal_callback = reversal_callback; + nh->callback_cls = callback_cls; + do_connect (nh); + return nh; +} + + +/** + * Check if an incoming message is a STUN message. + * + * @param data the packet + * @param len the length of the packet in @a data + * @return #GNUNET_YES if @a data is a STUN packet, + * #GNUNET_NO if the packet is invalid (not a stun packet) + */ +static enum GNUNET_GenericReturnValue +test_stun_packet (const void *data, size_t len) +{ + const struct stun_header *hdr; + const struct stun_attr *attr; + uint32_t advertised_message_size; + uint32_t message_magic_cookie; + + /* On entry, 'len' is the length of the UDP payload. After the + * initial checks it becomes the size of unprocessed options, + * while 'data' is advanced accordingly. + */ + if (len < sizeof(struct stun_header)) + { + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "STUN packet too short (only %d, wanting at least %d)\n", + (int) len, + (int) sizeof(struct stun_header)); + return GNUNET_NO; + } + hdr = (const struct stun_header *) data; + /* Skip header as it is already in hdr */ + len -= sizeof(struct stun_header); + data += sizeof(struct stun_header); + + /* len as advertised in the message */ + advertised_message_size = ntohs (hdr->msglen); + + message_magic_cookie = ntohl (hdr->magic); + /* Compare if the cookie match */ + if (STUN_MAGIC_COOKIE != message_magic_cookie) + { + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Invalid magic cookie for STUN\n"); + return GNUNET_NO; + } + + if (advertised_message_size > len) + { + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Scrambled STUN packet length (got %d, expecting %d)\n", + advertised_message_size, + (int) len); + return GNUNET_NO; + } + len = advertised_message_size; + while (len > 0) + { + if (len < sizeof(struct stun_attr)) + { + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Attribute too short in STUN packet (got %d, expecting %d)\n", + (int) len, + (int) sizeof(struct stun_attr)); + return GNUNET_NO; + } + attr = (const struct stun_attr *) data; + + /* compute total attribute length */ + advertised_message_size = ntohs (attr->len) + sizeof(struct stun_attr); + + /* Check if we still have space in our buffer */ + if (advertised_message_size > len) + { + GNUNET_log ( + GNUNET_ERROR_TYPE_DEBUG, + "Inconsistent Attribute (length %d exceeds remaining msg len %d)\n", + advertised_message_size, + (int) len); + return GNUNET_NO; + } + data += advertised_message_size; + len -= advertised_message_size; + } + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "STUN Packet, msg %04x, length: %d\n", + ntohs (hdr->msgtype), + advertised_message_size); + return GNUNET_OK; +} + + +/** + * Handle an incoming STUN message. This function is useful as + * some GNUnet service may be listening on a UDP port and might + * thus receive STUN messages while trying to receive other data. + * In this case, this function can be used to process replies + * to STUN requests. + * + * The function does some basic sanity checks on packet size and + * content, try to extract a bit of information. + * + * At the moment this only processes BIND requests, and returns the + * externally visible address of the request to the rest of the + * NAT logic. + * + * @param nh handle to the NAT service + * @param sender_addr address from which we got @a data + * @param sender_addr_len number of bytes in @a sender_addr + * @param data the packet + * @param data_size number of bytes in @a data + * @return #GNUNET_OK on success + * #GNUNET_NO if the packet is not a STUN packet + * #GNUNET_SYSERR on internal error handling the packet + */ +int +GNUNET_NAT_stun_handle_packet (struct GNUNET_NAT_Handle *nh, + const struct sockaddr *sender_addr, + size_t sender_addr_len, + const void *data, + size_t data_size) +{ + struct GNUNET_MQ_Envelope *env; + struct GNUNET_NAT_HandleStunMessage *hsn; + char *buf; + + if (GNUNET_YES != test_stun_packet (data, data_size)) + return GNUNET_NO; + if (NULL == nh->mq) + return GNUNET_SYSERR; + env = GNUNET_MQ_msg_extra (hsn, + data_size + sender_addr_len, + GNUNET_MESSAGE_TYPE_NAT_HANDLE_STUN); + hsn->sender_addr_size = htons ((uint16_t) sender_addr_len); + hsn->payload_size = htons ((uint16_t) data_size); + buf = (char *) &hsn[1]; + GNUNET_memcpy (buf, sender_addr, sender_addr_len); + buf += sender_addr_len; + GNUNET_memcpy (buf, data, data_size); + GNUNET_MQ_send (nh->mq, env); + return GNUNET_OK; +} + + +/** + * Test if the given address is (currently) a plausible IP address for + * this peer. Mostly a convenience function so that clients do not + * have to explicitly track all IPs that the #GNUNET_NAT_AddressCallback + * has returned so far. + * + * @param nh the handle returned by register + * @param addr IP address to test (IPv4 or IPv6) + * @param addrlen number of bytes in @a addr + * @return #GNUNET_YES if the address is plausible, + * #GNUNET_NO if the address is not plausible, + * #GNUNET_SYSERR if the address is malformed + */ +int +GNUNET_NAT_test_address (struct GNUNET_NAT_Handle *nh, + const void *addr, + socklen_t addrlen) +{ + struct AddrEntry *ae; + + if ((addrlen != sizeof(struct sockaddr_in)) && + (addrlen != sizeof(struct sockaddr_in6))) + { + GNUNET_break (0); + return GNUNET_SYSERR; + } + for (ae = nh->ae_head; NULL != ae; ae = ae->next) + if ((addrlen == ae->addrlen) && (0 == memcmp (addr, &ae[1], addrlen))) + return GNUNET_YES; + return GNUNET_NO; +} + + +/** + * We learned about a peer (possibly behind NAT) so run the + * gnunet-nat-client to send dummy ICMP responses to cause + * that peer to connect to us (connection reversal). + * + * @param nh handle (used for configuration) + * @param local_sa our local address of the peer (IPv4-only) + * @param remote_sa the remote address of the peer (IPv4-only) + * @return #GNUNET_SYSERR on error, + * #GNUNET_NO if connection reversal is unavailable, + * #GNUNET_OK otherwise (presumably in progress) + */ +int +GNUNET_NAT_request_reversal (struct GNUNET_NAT_Handle *nh, + const struct sockaddr_in *local_sa, + const struct sockaddr_in *remote_sa) +{ + struct GNUNET_MQ_Envelope *env; + struct GNUNET_NAT_RequestConnectionReversalMessage *req; + char *buf; + + if (NULL == nh->mq) + return GNUNET_SYSERR; + GNUNET_break (AF_INET == local_sa->sin_family); + GNUNET_break (AF_INET == remote_sa->sin_family); + env = + GNUNET_MQ_msg_extra (req, + 2 * sizeof(struct sockaddr_in), + GNUNET_MESSAGE_TYPE_NAT_REQUEST_CONNECTION_REVERSAL); + req->local_addr_size = htons (sizeof(struct sockaddr_in)); + req->remote_addr_size = htons (sizeof(struct sockaddr_in)); + buf = (char *) &req[1]; + GNUNET_memcpy (buf, local_sa, sizeof(struct sockaddr_in)); + buf += sizeof(struct sockaddr_in); + GNUNET_memcpy (buf, remote_sa, sizeof(struct sockaddr_in)); + GNUNET_MQ_send (nh->mq, env); + return GNUNET_OK; +} + + +void +GNUNET_NAT_unregister (struct GNUNET_NAT_Handle *nh) +{ + struct AddrEntry *ae; + struct AddrEntry *next; + + if (NULL != nh->mq) + { + GNUNET_MQ_destroy (nh->mq); + nh->mq = NULL; + } + if (NULL != nh->reconnect_task) + { + GNUNET_SCHEDULER_cancel (nh->reconnect_task); + nh->reconnect_task = NULL; + } + next = nh->ae_head; + while (NULL != next) + { + ae = next; + next = next->next; + GNUNET_CONTAINER_DLL_remove (nh->ae_head, nh->ae_tail, ae); + GNUNET_free (ae); + } + GNUNET_free (nh->reg); + GNUNET_free (nh); +} + + +/* end of nat_api.c */ diff --git a/src/service/nat/nat_api_stun.c b/src/service/nat/nat_api_stun.c new file mode 100644 index 000000000..94adc3d6c --- /dev/null +++ b/src/service/nat/nat_api_stun.c @@ -0,0 +1,259 @@ +/* + This file is part of GNUnet. + Copyright (C) 2009, 2015, 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 . + + SPDX-License-Identifier: AGPL3.0-or-later + */ +/** + * This code provides some support for doing STUN transactions. + * We send simplest possible packet ia REQUEST with BIND to a STUN server. + * + * All STUN packets start with a simple header made of a type, + * length (excluding the header) and a 16-byte random transaction id. + * Following the header we may have zero or more attributes, each + * structured as a type, length and a value (whose format depends + * on the type, but often contains addresses). + * Of course all fields are in network format. + * + * This code was based on ministun.c. + * + * @file nat/nat_api_stun.c + * @brief Functions for STUN functionality + * @author Bruno Souza Cabral + */ + +#include "platform.h" +#include "gnunet_util_lib.h" +#include "gnunet_resolver_service.h" +#include "gnunet_nat_service.h" + + +#include "nat_stun.h" + +#define LOG(kind, ...) GNUNET_log_from (kind, "stun", __VA_ARGS__) + +#define TIMEOUT GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS, 15) + + +/** + * Handle to a request given to the resolver. Can be used to cancel + * the request prior to the timeout or successful execution. Also + * used to track our internal state for the request. + */ +struct GNUNET_NAT_STUN_Handle +{ + /** + * Handle to a pending DNS lookup request. + */ + struct GNUNET_RESOLVER_RequestHandle *dns_active; + + /** + * Handle to the listen socket + */ + struct GNUNET_NETWORK_Handle *sock; + + /** + * Stun server address + */ + char *stun_server; + + /** + * Function to call when a error occurs + */ + GNUNET_NAT_TestCallback cb; + + /** + * Closure for @e cb. + */ + void *cb_cls; + + /** + * Do we got a DNS resolution successfully? + */ + int dns_success; + + /** + * STUN port + */ + uint16_t stun_port; +}; + + +/** + * Encode a class and method to a compatible STUN format + * + * @param msg_class class to be converted + * @param method method to be converted + * @return message in a STUN compatible format + */ +static int +encode_message (enum StunClasses msg_class, + enum StunMethods method) +{ + return ((msg_class & 1) << 4) | ((msg_class & 2) << 7) + | (method & 0x000f) | ((method & 0x0070) << 1) | ((method & 0x0f800) + << 2); +} + + +/** + * Fill the stun_header with a random request_id + * + * @param req stun header to be filled + */ +static void +generate_request_id (struct stun_header *req) +{ + req->magic = htonl (STUN_MAGIC_COOKIE); + for (unsigned int x = 0; x < 3; x++) + req->id.id[x] = GNUNET_CRYPTO_random_u32 (GNUNET_CRYPTO_QUALITY_NONCE, + UINT32_MAX); +} + + +/** + * Try to establish a connection given the specified address. + * + * @param cls our `struct GNUNET_NAT_STUN_Handle *` + * @param addr address to try, NULL for "last call" + * @param addrlen length of @a addr + */ +static void +stun_dns_callback (void *cls, + const struct sockaddr *addr, + socklen_t addrlen) +{ + struct GNUNET_NAT_STUN_Handle *rh = cls; + struct stun_header req; + struct sockaddr_in server; + + if (NULL == addr) + { + rh->dns_active = NULL; + if (GNUNET_NO == rh->dns_success) + { + LOG (GNUNET_ERROR_TYPE_INFO, + "Error resolving host %s\n", + rh->stun_server); + rh->cb (rh->cb_cls, + GNUNET_NAT_ERROR_NOT_ONLINE); + } + else if (GNUNET_SYSERR == rh->dns_success) + { + rh->cb (rh->cb_cls, + GNUNET_NAT_ERROR_INTERNAL_NETWORK_ERROR); + } + else + { + rh->cb (rh->cb_cls, + GNUNET_NAT_ERROR_SUCCESS); + } + GNUNET_NAT_stun_make_request_cancel (rh); + return; + } + + rh->dns_success = GNUNET_YES; + memset (&server, 0, sizeof(server)); + server.sin_family = AF_INET; + server.sin_addr = ((struct sockaddr_in *) addr)->sin_addr; + server.sin_port = htons (rh->stun_port); +#if HAVE_SOCKADDR_IN_SIN_LEN + server.sin_len = (u_char) sizeof(struct sockaddr_in); +#endif + + /* Craft the simplest possible STUN packet. A request binding */ + generate_request_id (&req); + req.msglen = htons (0); + req.msgtype = htons (encode_message (STUN_REQUEST, + STUN_BINDING)); + + /* Send the packet */ + if (-1 == + GNUNET_NETWORK_socket_sendto (rh->sock, + &req, + sizeof(req), + (const struct sockaddr *) &server, + sizeof(server))) + { + GNUNET_log_strerror (GNUNET_ERROR_TYPE_ERROR, + "sendto"); + rh->dns_success = GNUNET_SYSERR; + return; + } +} + + +/** + * Make Generic STUN request. Sends a generic stun request to the + * server specified using the specified socket. + * + * @param server the address of the stun server + * @param port port of the stun server, in host byte order + * @param sock the socket used to send the request + * @param cb callback in case of error + * @param cb_cls closure for @a cb + * @return NULL on error + */ +struct GNUNET_NAT_STUN_Handle * +GNUNET_NAT_stun_make_request (const char *server, + uint16_t port, + struct GNUNET_NETWORK_Handle *sock, + GNUNET_NAT_TestCallback cb, + void *cb_cls) +{ + struct GNUNET_NAT_STUN_Handle *rh; + + rh = GNUNET_new (struct GNUNET_NAT_STUN_Handle); + rh->sock = sock; + rh->cb = cb; + rh->cb_cls = cb_cls; + rh->stun_server = GNUNET_strdup (server); + rh->stun_port = port; + rh->dns_success = GNUNET_NO; + rh->dns_active = GNUNET_RESOLVER_ip_get (rh->stun_server, + AF_INET, + TIMEOUT, + &stun_dns_callback, + rh); + if (NULL == rh->dns_active) + { + GNUNET_NAT_stun_make_request_cancel (rh); + return NULL; + } + return rh; +} + + +/** + * Cancel active STUN request. Frees associated resources + * and ensures that the callback is no longer invoked. + * + * @param rh request to cancel + */ +void +GNUNET_NAT_stun_make_request_cancel (struct GNUNET_NAT_STUN_Handle *rh) +{ + if (NULL != rh->dns_active) + { + GNUNET_RESOLVER_request_cancel (rh->dns_active); + rh->dns_active = NULL; + } + GNUNET_free (rh->stun_server); + GNUNET_free (rh); +} + + +/* end of nat_stun.c */ diff --git a/src/service/nat/nat_stun.h b/src/service/nat/nat_stun.h new file mode 100644 index 000000000..a31e0f8d6 --- /dev/null +++ b/src/service/nat/nat_stun.h @@ -0,0 +1,267 @@ +/* + This file is part of GNUnet. + Copyright (C) 2009, 2015, 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 . + + SPDX-License-Identifier: AGPL3.0-or-later + */ +/** + * Message types for STUN server resolution + * + * @file nat/nat_stun.h + * @brief Testcase for STUN library + * @author Bruno Souza Cabral + * @author Mark Spencer (Original code borrowed from Asterisk) + * @author Christian Grothoff + */ + + +#define STUN_IGNORE (0) +#define STUN_ACCEPT (1) + +#define STUN_MAGIC_COOKIE 0x2112A442 + +typedef struct +{ + uint32_t id[3]; +} GNUNET_PACKED stun_trans_id; + + +struct stun_header +{ + uint16_t msgtype; + uint16_t msglen; + uint32_t magic; + stun_trans_id id; +} GNUNET_PACKED; + + +struct stun_attr +{ + uint16_t attr; + uint16_t len; +} GNUNET_PACKED; + + +/** + * The format normally used for addresses carried by STUN messages. + */ +struct stun_addr +{ + uint8_t unused; + + /** + * Address family, we expect AF_INET. + */ + uint8_t family; + + /** + * Port number. + */ + uint16_t port; + + /** + * IPv4 address. Should this be "struct in_addr"? + */ + uint32_t addr; +} GNUNET_PACKED; + + +/** + * STUN message classes + */ +enum StunClasses +{ + INVALID_CLASS = 0, + STUN_REQUEST = 0x0000, + STUN_INDICATION = 0x0001, + STUN_RESPONSE = 0x0002, + STUN_ERROR_RESPONSE = 0x0003 +}; + +enum StunMethods +{ + INVALID_METHOD = 0, + STUN_BINDING = 0x0001, + STUN_SHARED_SECRET = 0x0002, + STUN_ALLOCATE = 0x0003, + STUN_REFRESH = 0x0004, + STUN_SEND = 0x0006, + STUN_DATA = 0x0007, + STUN_CREATE_PERMISSION = 0x0008, + STUN_CHANNEL_BIND = 0x0009 +}; + + +/** + * Basic attribute types in stun messages. + * Messages can also contain custom attributes (codes above 0x7fff) + */ +enum StunAttributes +{ + STUN_MAPPED_ADDRESS = 0x0001, + STUN_RESPONSE_ADDRESS = 0x0002, + STUN_CHANGE_ADDRESS = 0x0003, + STUN_SOURCE_ADDRESS = 0x0004, + STUN_CHANGED_ADDRESS = 0x0005, + STUN_USERNAME = 0x0006, + STUN_PASSWORD = 0x0007, + STUN_MESSAGE_INTEGRITY = 0x0008, + STUN_ERROR_CODE = 0x0009, + STUN_UNKNOWN_ATTRIBUTES = 0x000a, + STUN_REFLECTED_FROM = 0x000b, + STUN_REALM = 0x0014, + STUN_NONCE = 0x0015, + STUN_XOR_MAPPED_ADDRESS = 0x0020, + STUN_MS_VERSION = 0x8008, + STUN_MS_XOR_MAPPED_ADDRESS = 0x8020, + STUN_SOFTWARE = 0x8022, + STUN_ALTERNATE_SERVER = 0x8023, + STUN_FINGERPRINT = 0x8028 +}; + + +/** + * Convert a message to a StunClass + * + * @param msg the received message + * @return the converted StunClass + */ +static enum StunClasses +decode_class (int msg) +{ + /* Sorry for the magic, but this maps the class according to rfc5245 */ + return (enum StunClasses) ((msg & 0x0010) >> 4) | ((msg & 0x0100) >> 7); +} + + +/** + * Convert a message to a StunMethod + * + * @param msg the received message + * @return the converted StunMethod + */ +static enum StunMethods +decode_method (int msg) +{ + return (enum StunMethods) (msg & 0x000f) | ((msg & 0x00e0) >> 1) | ((msg + & 0x3e00) + >> 2); +} + + +/** + * Print a class and method from a STUN message + * + * @param msg + * @return string with the message class and method + */ +GNUNET_UNUSED +static const char * +stun_msg2str (int msg) +{ + static const struct + { + enum StunClasses value; + const char *name; + } classes[] = { + { STUN_REQUEST, "Request" }, + { STUN_INDICATION, "Indication" }, + { STUN_RESPONSE, "Response" }, + { STUN_ERROR_RESPONSE, "Error Response" }, + { INVALID_CLASS, NULL } + }; + static const struct + { + enum StunMethods value; + const char *name; + } methods[] = { + { STUN_BINDING, "Binding" }, + { INVALID_METHOD, NULL } + }; + static char result[64]; + const char *msg_class = NULL; + const char *method = NULL; + enum StunClasses cvalue; + enum StunMethods mvalue; + + cvalue = decode_class (msg); + for (unsigned int i = 0; classes[i].name; i++) + if (classes[i].value == cvalue) + { + msg_class = classes[i].name; + break; + } + mvalue = decode_method (msg); + for (unsigned int i = 0; methods[i].name; i++) + if (methods[i].value == mvalue) + { + method = methods[i].name; + break; + } + GNUNET_snprintf (result, + sizeof(result), + "%s %s", + method ? : "Unknown Method", + msg_class ? : "Unknown Class Message"); + return result; +} + + +/** + * Print attribute name + * + * @param msg with a attribute type + * @return string with the attribute name + */ +GNUNET_UNUSED +static const char * +stun_attr2str (enum StunAttributes msg) +{ + static const struct + { + enum StunAttributes value; + const char *name; + } attrs[] = { + { STUN_MAPPED_ADDRESS, "Mapped Address" }, + { STUN_RESPONSE_ADDRESS, "Response Address" }, + { STUN_CHANGE_ADDRESS, "Change Address" }, + { STUN_SOURCE_ADDRESS, "Source Address" }, + { STUN_CHANGED_ADDRESS, "Changed Address" }, + { STUN_USERNAME, "Username" }, + { STUN_PASSWORD, "Password" }, + { STUN_MESSAGE_INTEGRITY, "Message Integrity" }, + { STUN_ERROR_CODE, "Error Code" }, + { STUN_UNKNOWN_ATTRIBUTES, "Unknown Attributes" }, + { STUN_REFLECTED_FROM, "Reflected From" }, + { STUN_REALM, "Realm" }, + { STUN_NONCE, "Nonce" }, + { STUN_XOR_MAPPED_ADDRESS, "XOR Mapped Address" }, + { STUN_MS_VERSION, "MS Version" }, + { STUN_MS_XOR_MAPPED_ADDRESS, "MS XOR Mapped Address" }, + { STUN_SOFTWARE, "Software" }, + { STUN_ALTERNATE_SERVER, "Alternate Server" }, + { STUN_FINGERPRINT, "Fingerprint" }, + { 0, NULL } + }; + + for (unsigned int i = 0; attrs[i].name; i++) + if (attrs[i].value == msg) + return attrs[i].name; + return "Unknown Attribute"; +} + + +/* end of nat_stun.h */ diff --git a/src/service/nat/test_nat.c b/src/service/nat/test_nat.c new file mode 100644 index 000000000..a3072f712 --- /dev/null +++ b/src/service/nat/test_nat.c @@ -0,0 +1,192 @@ +/* + This file is part of GNUnet. + Copyright (C) 2009, 2011 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 . + + SPDX-License-Identifier: AGPL3.0-or-later + */ + +/** + * Testcase for port redirection and public IP address retrieval. + * This test never fails, because there need to be a NAT box set up for that. + * So we only get IP address and open the 2086 port using any NAT traversal + * method available, wait for 30s, close ports and return. + * Have a look at the logs and use NMAP to check that it works with your box. + * + * @file nat/test_nat.c + * @brief Testcase for NAT library + * @author Milan Bouchet-Valat + * @author Christian Grothoff + * + * TODO: actually use ARM to start resolver service to make DNS work! + */ + +#include "platform.h" +#include "gnunet_util_lib.h" +#include "gnunet_program_lib.h" +#include "gnunet_scheduler_lib.h" +#include "gnunet_nat_lib.h" + + +/** + * Time to wait before stopping NAT, in seconds + */ +#define TIMEOUT GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS, 5) + + +/** + * Function called on each address that the NAT service + * believes to be valid for the transport. + */ +static void +addr_callback (void *cls, int add_remove, const struct sockaddr *addr, + socklen_t addrlen) +{ + GNUNET_log (GNUNET_ERROR_TYPE_INFO, + "Address changed: %s `%s' (%u bytes)\n", + add_remove == GNUNET_YES ? "added" : "removed", + GNUNET_a2s (addr, + addrlen), + (unsigned int) addrlen); +} + + +/** + * Function that terminates the test. + */ +static void +stop (void *cls) +{ + struct GNUNET_NAT_Handle *nat = cls; + + GNUNET_log (GNUNET_ERROR_TYPE_INFO, + "Stopping NAT and quitting...\n"); + GNUNET_NAT_unregister (nat); +} + + +struct addr_cls +{ + struct sockaddr *addr; + socklen_t addrlen; +}; + + +/** + * Return the address of the default interface, + * or any interface with a valid address if the default is not valid + * + * @param cls the 'struct addr_cls' + * @param name name of the interface + * @param isDefault do we think this may be our default interface + * @param addr address of the interface + * @param addrlen number of bytes in @a addr + * @return #GNUNET_OK to continue iterating + */ +static int +process_if (void *cls, + const char *name, + int isDefault, + const struct sockaddr *addr, + const struct sockaddr *broadcast_addr, + const struct sockaddr *netmask, + socklen_t addrlen) +{ + struct addr_cls *data = cls; + + if (addr == NULL) + return GNUNET_OK; + GNUNET_free (data->addr); + data->addr = GNUNET_malloc (addrlen); + GNUNET_memcpy (data->addr, addr, addrlen); + data->addrlen = addrlen; + if (isDefault) + return GNUNET_SYSERR; + return GNUNET_OK; +} + + +/** + * Main function run with scheduler. + */ +static void +run (void *cls, + char *const *args, + const char *cfgfile, + const struct GNUNET_CONFIGURATION_Handle *cfg) +{ + struct GNUNET_NAT_Handle *nat; + struct addr_cls data; + struct sockaddr *addr; + + data.addr = NULL; + GNUNET_OS_network_interfaces_list (process_if, &data); + if (NULL == data.addr) + { + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "Could not find a valid interface address!\n"); + exit (77); /* marks test as skipped */ + } + addr = data.addr; + GNUNET_assert (addr->sa_family == AF_INET || addr->sa_family == AF_INET6); + if (addr->sa_family == AF_INET) + ((struct sockaddr_in *) addr)->sin_port = htons (2086); + else + ((struct sockaddr_in6 *) addr)->sin6_port = htons (2086); + + GNUNET_log (GNUNET_ERROR_TYPE_INFO, + "Requesting NAT redirection from address %s...\n", + GNUNET_a2s (addr, data.addrlen)); + + nat = GNUNET_NAT_register (cfg, GNUNET_YES /* tcp */, + 2086, 1, (const struct sockaddr **) &addr, + &data.addrlen, &addr_callback, NULL, NULL, NULL); + GNUNET_free (addr); + GNUNET_SCHEDULER_add_delayed (TIMEOUT, &stop, nat); +} + + +int +main (int argc, char *const argv[]) +{ + struct GNUNET_GETOPT_CommandLineOption options[] = { + GNUNET_GETOPT_OPTION_END + }; + + char *const argv_prog[] = { + "test-nat", + "-c", + "test_nat_data.conf", + NULL + }; + + GNUNET_log_setup ("test-nat", + "WARNING", + NULL); + GNUNET_log (GNUNET_ERROR_TYPE_INFO, + "Testing NAT library, timeout set to %s\n", + GNUNET_STRINGS_relative_time_to_string (TIMEOUT, + GNUNET_YES)); + GNUNET_PROGRAM_run (3, + argv_prog, + "test-nat", + "nohelp", + options, + &run, NULL); + return 0; +} + + +/* end of test_nat.c */ diff --git a/src/service/nat/test_nat_data.conf b/src/service/nat/test_nat_data.conf new file mode 100644 index 000000000..e139d0c05 --- /dev/null +++ b/src/service/nat/test_nat_data.conf @@ -0,0 +1,36 @@ +[PATHS] +GNUNET_TEST_HOME = $GNUNET_TMP/nat-test +# GNUNET_TEST_HOME = /var/lib/gnunet/ +# configuration file is assumed to be the default, +# which is what we want by default... + +[nat] +# Are we behind NAT? +BEHIND_NAT = YES + +# Is the NAT hole-punched? +PUNCHED_NAT = NO + +# Disable UPNP by default until it gets cleaner! +ENABLE_UPNP = YES + +# Use addresses from the local network interfaces (including loopback, but also others) +USE_LOCALADDR = YES + +# External IP address of the NAT box (if known); IPv4 dotted-decimal ONLY at this time (should allow DynDNS!) +# normal interface IP address for non-NATed peers; +# possibly auto-detected (using UPnP) if possible if not specified +# EXTERNAL_ADDRESS = + +# Should we use ICMP-based NAT traversal to try connect to NATed peers +# or, if we are behind NAT, to allow connections to us? +ENABLE_ICMP_CLIENT = NO +ENABLE_ICMP_SERVER = NO + +# IP address of the interface connected to the NAT box; IPv4 dotted-decimal ONLY; +# normal interface IP address for non-NATed peers; +# likely auto-detected (via interface list) if not specified (!) +# INTERNAL_ADDRESS = + +# Disable IPv6 support +DISABLEV6 = NO diff --git a/src/service/nat/test_nat_mini.c b/src/service/nat/test_nat_mini.c new file mode 100644 index 000000000..528815e1a --- /dev/null +++ b/src/service/nat/test_nat_mini.c @@ -0,0 +1,134 @@ +/* + This file is part of GNUnet. + Copyright (C) 2009, 2011 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 . + + SPDX-License-Identifier: AGPL3.0-or-later + */ +/** + * @file nat/test_nat_mini.c + * @brief Testcase for NAT library - mini + * @author Christian Grothoff + * + * Testcase for port redirection and public IP address retrieval. + * This test never fails, because there need to be a NAT box set up for that + * + * TODO: actually use ARM to start resolver service to make DNS work! + */ + +#include "platform.h" +#include "gnunet_util_lib.h" +#include "gnunet_program_lib.h" +#include "gnunet_scheduler_lib.h" +#include "gnunet_nat_lib.h" + +/* Time to wait before stopping NAT, in seconds */ +#define TIMEOUT GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS, 5) + +/** + * Function called on each address that the NAT service + * believes to be valid for the transport. + */ +static void +addr_callback (void *cls, int add_remove, + const struct sockaddr *addr, + socklen_t addrlen, + enum GNUNET_NAT_StatusCode ret) +{ + if (GNUNET_NAT_ERROR_SUCCESS == ret) + { + fprintf (stderr, + "Address changed: %s `%s' (%u bytes)\n", + add_remove == GNUNET_YES + ? "added" : "removed", + GNUNET_a2s (addr, + addrlen), + (unsigned int) addrlen); + } + else + ; + // TODO: proper error handling! +} + + +/** + * Function that terminates the test. + */ +static void +stop (void *cls) +{ + struct GNUNET_NAT_MiniHandle *mini = cls; + + GNUNET_log (GNUNET_ERROR_TYPE_INFO, "Stopping NAT and quitting...\n"); + GNUNET_NAT_mini_map_stop (mini); +} + + +#define PORT 10000 + +/** + * Main function run with scheduler. + */ +static void +run (void *cls, char *const *args, const char *cfgfile, + const struct GNUNET_CONFIGURATION_Handle *cfg) +{ + struct GNUNET_NAT_MiniHandle *mini; + + GNUNET_log (GNUNET_ERROR_TYPE_INFO, + "Requesting NAT redirection for port %u...\n", + PORT); + mini = GNUNET_NAT_mini_map_start (PORT, GNUNET_YES /* tcp */, + &addr_callback, NULL); + if (NULL == mini) + { + GNUNET_log (GNUNET_ERROR_TYPE_INFO, "Could not start UPnP interaction\n"); + return; + } + GNUNET_SCHEDULER_add_delayed (TIMEOUT, &stop, mini); +} + + +int +main (int argc, char *const argv[]) +{ + struct GNUNET_GETOPT_CommandLineOption options[] = { + GNUNET_GETOPT_OPTION_END + }; + + char *const argv_prog[] = { + "test-nat-mini", + "-c", + "test_nat_data.conf", + "-L", + "WARNING", + NULL + }; + + GNUNET_log_setup ("test-nat-mini", + "WARNING", + NULL); + + GNUNET_log (GNUNET_ERROR_TYPE_INFO, + "UPnP test for NAT library, timeout set to %s\n", + GNUNET_STRINGS_relative_time_to_string (TIMEOUT, + GNUNET_YES)); + GNUNET_PROGRAM_run (5, argv_prog, "test-nat-mini", "nohelp", options, &run, + NULL); + return 0; +} + + +/* end of test_nat_mini.c */ diff --git a/src/service/nat/test_nat_test.c b/src/service/nat/test_nat_test.c new file mode 100644 index 000000000..2abab4d5f --- /dev/null +++ b/src/service/nat/test_nat_test.c @@ -0,0 +1,142 @@ +/* + This file is part of GNUnet. + Copyright (C) 2009, 2011, 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 . + + SPDX-License-Identifier: AGPL3.0-or-later + */ +/** + * @file nat/test_nat_test.c + * @brief Testcase for NAT testing functions + * @author Christian Grothoff + */ +#include "platform.h" +#include "gnunet_util_lib.h" +#include "gnunet_nat_lib.h" + +/** + * Time to wait before stopping NAT test, in seconds + */ +#define TIMEOUT GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS, 15) + + +static int ret = 1; + +static struct GNUNET_NAT_Test *tst; + +static struct GNUNET_SCHEDULER_Task *tsk; + + +static void +report_result (void *cls, + enum GNUNET_NAT_StatusCode aret) +{ + if (GNUNET_NAT_ERROR_TIMEOUT == aret) + fprintf (stderr, + "NAT test timed out\n"); + else if (GNUNET_NAT_ERROR_SUCCESS != aret) + fprintf (stderr, + "NAT test reported error %d\n", aret); + else + ret = 0; + GNUNET_NAT_test_stop (tst); + tst = NULL; + GNUNET_SCHEDULER_cancel (tsk); + tsk = NULL; +} + + +static void +failed_timeout (void *cls) +{ + tsk = NULL; + fprintf (stderr, + "NAT test failed to terminate on timeout\n"); + ret = 2; + GNUNET_NAT_test_stop (tst); + tst = NULL; +} + + +/** + * Main function run with scheduler. + */ +static void +run (void *cls, char *const *args, const char *cfgfile, + const struct GNUNET_CONFIGURATION_Handle *cfg) +{ + tst = + GNUNET_NAT_test_start (cfg, GNUNET_YES, 1285, 1285, TIMEOUT, + &report_result, + NULL); + tsk = GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_relative_multiply (TIMEOUT, + 2), + &failed_timeout, + NULL); +} + + +int +main (int argc, char *const argv[]) +{ + struct GNUNET_GETOPT_CommandLineOption options[] = { + GNUNET_GETOPT_OPTION_END + }; + struct GNUNET_OS_Process *gns; + int nat_res; + char *const argv_prog[] = { + "test-nat-test", + "-c", + "test_nat_test_data.conf", + NULL + }; + + GNUNET_log_setup ("test-nat-test", + "WARNING", + NULL); + + nat_res = GNUNET_OS_check_helper_binary ("gnunet-nat-server", GNUNET_NO, + NULL); + if (GNUNET_SYSERR == nat_res) + { + GNUNET_log (GNUNET_ERROR_TYPE_WARNING, + "Cannot run NAT test: `%s' file not found\n", + "gnunet-nat-server"); + return 0; + } + + gns = GNUNET_OS_start_process (GNUNET_OS_INHERIT_STD_OUT_AND_ERR + | GNUNET_OS_USE_PIPE_CONTROL, + NULL, NULL, NULL, + "gnunet-nat-server", + "gnunet-nat-server", + "-c", "test_nat_test_data.conf", + "12345", NULL); + GNUNET_assert (NULL != gns); + GNUNET_PROGRAM_run (3, argv_prog, + "test-nat-test", "nohelp", + options, &run, + NULL); + GNUNET_break (0 == GNUNET_OS_process_kill (gns, GNUNET_TERM_SIG)); + GNUNET_break (GNUNET_OK == GNUNET_OS_process_wait (gns)); + GNUNET_OS_process_destroy (gns); + if (0 != ret) + fprintf (stderr, + "NAT test failed to report success\n"); + return ret; +} + + +/* end of test_nat_test.c */ diff --git a/src/service/nat/test_nat_test_data.conf b/src/service/nat/test_nat_test_data.conf new file mode 100644 index 000000000..ca78ca9f3 --- /dev/null +++ b/src/service/nat/test_nat_test_data.conf @@ -0,0 +1,45 @@ +[PATHS] +GNUNET_TEST_HOME = $GNUNET_TMP/nat-test +# GNUNET_TEST_HOME = /var/lib/gnunet/ +# configuration file is assumed to be the default, +# which is what we want by default... + +[gnunet-nat-server] +HOSTNAME = localhost +PORT = 57315 + +[nat] +# Are we behind NAT? +BEHIND_NAT = NO + +# Is the NAT hole-punched? +PUNCHED_NAT = YES + +# Disable UPNP by default until it gets cleaner! +ENABLE_UPNP = NO + +# Use addresses from the local network interfaces (including loopback, but also others) +USE_LOCALADDR = YES +RETURN_LOCAL_ADDRESSES = YES + +# External IP address of the NAT box (if known); IPv4 dotted-decimal ONLY at this time (should allow DynDNS!) +# normal interface IP address for non-NATed peers; +# possibly auto-detected (using UPnP) if possible if not specified +# EXTERNAL_ADDRESS = + +# Should we use ICMP-based NAT traversal to try connect to NATed peers +# or, if we are behind NAT, to allow connections to us? +ENABLE_ICMP_CLIENT = NO +ENABLE_ICMP_SERVER = NO + +# IP address of the interface connected to the NAT box; IPv4 dotted-decimal ONLY; +# normal interface IP address for non-NATed peers; +# likely auto-detected (via interface list) if not specified (!) +INTERNAL_ADDRESS = 127.0.0.1 + +# Disable IPv6 support +DISABLEV6 = YES + + +[nse] +START_ON_DEMAND = NO diff --git a/src/service/nat/test_stun.c b/src/service/nat/test_stun.c new file mode 100644 index 000000000..75eb877b3 --- /dev/null +++ b/src/service/nat/test_stun.c @@ -0,0 +1,313 @@ +/* + This file is part of GNUnet. + Copyright (C) 2009, 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 . + + SPDX-License-Identifier: AGPL3.0-or-later + */ + +/** + * Testcase for STUN server resolution + * + * @file nat/test_stun.c + * @brief Testcase for STUN library + * @author Bruno Souza Cabral + * @author Christian Grothoff + */ + + +#include "platform.h" +#include "gnunet_util_lib.h" +#include "gnunet_program_lib.h" +#include "gnunet_scheduler_lib.h" +#include "gnunet_nat_lib.h" + + +#define LOG(kind, ...) GNUNET_log_from (kind, "test-stun", __VA_ARGS__) + +/** + * Time to wait before stopping NAT, in seconds + */ +#define TIMEOUT GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS, 5) + + +/** + * The port the test service is running on (default 7895) + */ +static unsigned long port = 7895; + +static int ret = 1; + +static const char *stun_server = "stun.gnunet.org"; + +static int stun_port = 3478; + +/** + * The listen socket of the service for IPv4 + */ +static struct GNUNET_NETWORK_Handle *lsock4; + +/** + * The listen task ID for IPv4 + */ +static struct GNUNET_SCHEDULER_Task *ltask4; + +/** + * Handle for the STUN request. + */ +static struct GNUNET_NAT_STUN_Handle *rh; + + +static void +print_answer (struct sockaddr_in*answer) +{ + GNUNET_log (GNUNET_ERROR_TYPE_INFO, + "External IP is: %s , with port %d\n", + inet_ntoa (answer->sin_addr), + ntohs (answer->sin_port)); +} + + +/** + * Function that terminates the test. + */ +static void +stop () +{ + GNUNET_log (GNUNET_ERROR_TYPE_INFO, + "Stopping NAT and quitting...\n"); + if (NULL != ltask4) + { + GNUNET_SCHEDULER_cancel (ltask4); + ltask4 = NULL; + } + if (NULL != lsock4) + { + GNUNET_NETWORK_socket_close (lsock4); + lsock4 = NULL; + } + if (NULL != rh) + { + GNUNET_NAT_stun_make_request_cancel (rh); + rh = NULL; + } +} + + +/** + * Activity on our incoming socket. Read data from the + * incoming connection. + * + * @param cls + */ +static void +do_udp_read (void *cls) +{ + // struct GNUNET_NAT_Test *tst = cls; + unsigned char reply_buf[1024]; + ssize_t rlen; + struct sockaddr_in answer; + const struct GNUNET_SCHEDULER_TaskContext *tc; + + ltask4 = NULL; + tc = GNUNET_SCHEDULER_get_task_context (); + if ((0 == (tc->reason & GNUNET_SCHEDULER_REASON_READ_READY)) || + (! GNUNET_NETWORK_fdset_isset (tc->read_ready, + lsock4))) + { + fprintf (stderr, + "Timeout waiting for STUN response\n"); + stop (); + } + rlen = GNUNET_NETWORK_socket_recv (lsock4, + reply_buf, + sizeof(reply_buf)); + memset (&answer, + 0, + sizeof(struct sockaddr_in)); + if (GNUNET_OK != + GNUNET_NAT_stun_handle_packet (reply_buf, + rlen, + &answer)) + { + fprintf (stderr, + "Unexpected UDP packet, trying to read more\n"); + ltask4 = GNUNET_SCHEDULER_add_read_net (TIMEOUT, + lsock4, + &do_udp_read, NULL); + return; + } + ret = 0; + print_answer (&answer); + stop (); +} + + +/** + * Create an IPv4 listen socket bound to our port. + * + * @return NULL on error + */ +static struct GNUNET_NETWORK_Handle * +bind_v4 () +{ + struct GNUNET_NETWORK_Handle *ls; + struct sockaddr_in sa4; + int eno; + + memset (&sa4, 0, sizeof(sa4)); + sa4.sin_family = AF_INET; + sa4.sin_port = htons (port); +#if HAVE_SOCKADDR_IN_SIN_LEN + sa4.sin_len = sizeof(sa4); +#endif + ls = GNUNET_NETWORK_socket_create (AF_INET, + SOCK_DGRAM, + 0); + if (NULL == ls) + return NULL; + if (GNUNET_OK != + GNUNET_NETWORK_socket_bind (ls, + (const struct sockaddr *) &sa4, + sizeof(sa4))) + { + eno = errno; + GNUNET_NETWORK_socket_close (ls); + errno = eno; + return NULL; + } + return ls; +} + + +/** + * Function called with the result of the STUN request transmission attempt. + * + * @param cls unused + * @param error status code from STUN + */ +static void +request_callback (void *cls, + enum GNUNET_NAT_StatusCode error) +{ + rh = NULL; + if (GNUNET_NAT_ERROR_SUCCESS == error) + { + /* all good, start to receive */ + ltask4 = GNUNET_SCHEDULER_add_read_net (TIMEOUT, + lsock4, + &do_udp_read, + NULL); + return; + } + if (error == GNUNET_NAT_ERROR_NOT_ONLINE) + { + ret = 77; /* report 'skip' */ + fprintf (stderr, + "System is offline, cannot test STUN request.\n"); + } + else + { + ret = error; + } + stop (); +} + + +/** + * Main function run with scheduler. + */ +static void +run (void *cls, + char *const *args, + const char *cfgfile, + const struct GNUNET_CONFIGURATION_Handle *cfg) +{ + // Lets create the socket + lsock4 = bind_v4 (); + if (NULL == lsock4) + { + GNUNET_log_strerror (GNUNET_ERROR_TYPE_ERROR, + "bind"); + GNUNET_SCHEDULER_shutdown (); + return; + } + GNUNET_log (GNUNET_ERROR_TYPE_INFO, + "Service listens on port %u\n", + (unsigned int) port); + rh = GNUNET_NAT_stun_make_request (stun_server, + stun_port, + lsock4, + &request_callback, NULL); + GNUNET_SCHEDULER_add_delayed (TIMEOUT, + &stop, NULL); +} + + +int +main (int argc, char *const argv[]) +{ + struct GNUNET_GETOPT_CommandLineOption options[] = { + GNUNET_GETOPT_OPTION_END + }; + char *const argv_prog[] = { + "test-stun", + "-c", + "test_stun.conf", + NULL + }; + char *fn; + struct GNUNET_OS_Process *proc; + + GNUNET_log_setup ("test-stun", + "WARNING", + NULL); + + /* Lets start resolver */ + fn = GNUNET_OS_get_libexec_binary_path ("gnunet-service-resolver"); + proc = GNUNET_OS_start_process (GNUNET_OS_INHERIT_STD_OUT_AND_ERR + | GNUNET_OS_USE_PIPE_CONTROL, + NULL, NULL, NULL, + fn, + "gnunet-service-resolver", + "-c", "test_stun.conf", NULL); + + if (NULL == proc) + { + GNUNET_log (GNUNET_ERROR_TYPE_INFO, + "This test was unable to start gnunet-service-resolver, and it is required to run ...\n"); + exit (1); + } + + GNUNET_PROGRAM_run (3, argv_prog, + "test-stun", "nohelp", + options, + &run, NULL); + + /* Now kill the resolver */ + if (0 != GNUNET_OS_process_kill (proc, GNUNET_TERM_SIG)) + { + GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING, "kill"); + } + GNUNET_OS_process_wait (proc); + GNUNET_OS_process_destroy (proc); + proc = NULL; + GNUNET_free (fn); + + return ret; +} + + +/* end of test_stun.c */ diff --git a/src/service/nat/test_stun.conf b/src/service/nat/test_stun.conf new file mode 100644 index 000000000..b03bbfff3 --- /dev/null +++ b/src/service/nat/test_stun.conf @@ -0,0 +1,7 @@ +[PATHS] +GNUNET_TEST_HOME = $GNUNET_TMP/test-stun + +[resolver] +PORT = 22354 +HOSTNAME = localhost + diff --git a/src/service/nse/.gitignore b/src/service/nse/.gitignore new file mode 100644 index 000000000..a2575673c --- /dev/null +++ b/src/service/nse/.gitignore @@ -0,0 +1,5 @@ +gnunet-service-nse +gnunet-nse +gnunet-nse-profiler +test_nse_api +perf_kdf diff --git a/src/service/nse/Makefile.am b/src/service/nse/Makefile.am new file mode 100644 index 000000000..7b89ef635 --- /dev/null +++ b/src/service/nse/Makefile.am @@ -0,0 +1,99 @@ +# This Makefile.am is in the public domain +AM_CPPFLAGS = -I$(top_srcdir)/src/include + +if USE_COVERAGE + AM_CFLAGS = --coverage -O0 + XLIB = -lgcov +endif + +pkgcfgdir= $(pkgdatadir)/config.d/ + +libexecdir= $(pkglibdir)/libexec/ + +pkgcfg_DATA = \ + nse.conf + +lib_LTLIBRARIES = libgnunetnse.la + +libgnunetnse_la_SOURCES = \ + nse_api.c nse.h +libgnunetnse_la_LIBADD = \ + $(top_builddir)/src/lib/util/libgnunetutil.la \ + $(GN_LIBINTL) $(XLIB) +libgnunetnse_la_LDFLAGS = \ + $(GN_LIB_LDFLAGS) \ + -version-info 0:0:0 + + +libexec_PROGRAMS = \ + gnunet-service-nse + +#noinst_PROGRAMS = \ +# gnunet-nse-profiler + +# FIXME no testbed in TNG +#gnunet_nse_profiler_SOURCES = \ +# gnunet-nse-profiler.c +#gnunet_nse_profiler_LDADD = -lm \ +# libgnunetnse.la \ +# $(top_builddir)/src/lib/util/libgnunetutil.la \ +# $(top_builddir)/src/service/statistics/libgnunetstatistics.la \ +# $(top_builddir)/src/service/testing/libgnunettesting.la \ +# $(top_builddir)/src/testbed/libgnunettestbed.la \ +# $(GN_LIBINTL) + +gnunet_service_nse_SOURCES = \ + gnunet-service-nse.c +gnunet_service_nse_LDADD = \ + libgnunetnse.la \ + $(top_builddir)/src/lib/util/libgnunetutil.la \ + $(top_builddir)/src/service/core/libgnunetcore.la \ + $(top_builddir)/src/service/statistics/libgnunetstatistics.la \ + $(LIBGCRYPT_LIBS) \ + -lm -lgcrypt \ + $(GN_LIBINTL) +if ENABLE_NSE_HISTOGRAM + gnunet_service_nse_LDADD += \ + $(top_builddir)/src/testbed-logger/libgnunettestbedlogger.la +endif + + +if HAVE_BENCHMARKS + MULTIPEER_TEST = test_nse_multipeer +endif + +check_PROGRAMS = \ + test_nse_api \ + perf_kdf \ + $(MULTIPEER_TEST) + +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_nse_api_SOURCES = \ + test_nse_api.c +test_nse_api_LDADD = \ + libgnunetnse.la \ + $(top_builddir)/src/service/testing/libgnunettesting.la \ + $(top_builddir)/src/lib/util/libgnunetutil.la + +test_nse_multipeer_SOURCES = \ + test_nse_multipeer.c +test_nse_multipeer_LDADD = \ + libgnunetnse.la \ + $(top_builddir)/src/lib/util/libgnunetutil.la \ + $(top_builddir)/src/testbed/libgnunettestbed.la \ + -lm + +perf_kdf_SOURCES = \ + perf_kdf.c +perf_kdf_LDADD = \ + $(top_builddir)/src/lib/util/libgnunetutil.la \ + $(LIBGCRYPT_LIBS) \ + -lgcrypt + +EXTRA_DIST = \ + test_nse.conf \ + nse_profiler_test.conf diff --git a/src/service/nse/gnunet-nse-profiler.c b/src/service/nse/gnunet-nse-profiler.c new file mode 100644 index 000000000..4b256bc52 --- /dev/null +++ b/src/service/nse/gnunet-nse-profiler.c @@ -0,0 +1,921 @@ +/* + 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 . + + SPDX-License-Identifier: AGPL3.0-or-later + */ +/** + * @file nse/gnunet-nse-profiler.c + * + * @brief Profiling driver for the network size estimation service. + * Generally, the profiler starts a given number of peers, + * then churns some off, waits a certain amount of time, then + * churns again, and repeats. + * @author Christian Grothoff + * @author Nathan Evans + * @author Sree Harsha Totakura + */ + +#include "platform.h" +#include "gnunet_testbed_service.h" +#include "gnunet_nse_service.h" + +/** + * Generic loggins shorthand + */ +#define LOG(kind, ...) GNUNET_log (kind, __VA_ARGS__) + +/** + * Debug logging shorthand + */ +#define LOG_DEBUG(...) LOG (GNUNET_ERROR_TYPE_DEBUG, __VA_ARGS__) + + +/** + * Information we track for a peer in the testbed. + */ +struct NSEPeer +{ + /** + * Prev reference in DLL. + */ + struct NSEPeer *prev; + + /** + * Next reference in DLL. + */ + struct NSEPeer *next; + + /** + * Handle with testbed. + */ + struct GNUNET_TESTBED_Peer *daemon; + + /** + * Testbed operation to connect to NSE service. + */ + struct GNUNET_TESTBED_Operation *nse_op; + + /** + * Testbed operation to connect to statistics service + */ + struct GNUNET_TESTBED_Operation *stat_op; + + /** + * Handle to the statistics service + */ + struct GNUNET_STATISTICS_Handle *sh; +}; + + +/** + * Operation map entry + */ +struct OpListEntry +{ + /** + * DLL next ptr + */ + struct OpListEntry *next; + + /** + * DLL prev ptr + */ + struct OpListEntry *prev; + + /** + * The testbed operation + */ + struct GNUNET_TESTBED_Operation *op; + + /** + * Depending on whether we start or stop NSE service at the peer set this to 1 + * or -1 + */ + int delta; +}; + + +/** + * Head of DLL of peers we monitor closely. + */ +static struct NSEPeer *peer_head; + +/** + * Tail of DLL of peers we monitor closely. + */ +static struct NSEPeer *peer_tail; + +/** + * Return value from 'main' (0 == success) + */ +static int ok; + +/** + * Be verbose (configuration option) + */ +static unsigned int verbose; + +/** + * Name of the file with the hosts to run the test over (configuration option) + */ +static char *hosts_file; + +/** + * Maximum number of peers in the test. + */ +static unsigned int num_peers; + +/** + * Total number of rounds to execute. + */ +static unsigned int num_rounds; + +/** + * Current round we are in. + */ +static unsigned int current_round; + +/** + * Array of size 'num_rounds' with the requested number of peers in the given round. + */ +static unsigned int *num_peers_in_round; + +/** + * How many peers are running right now? + */ +static unsigned int peers_running; + +/** + * Specification for the numbers of peers to have in each round. + */ +static char *num_peer_spec; + +/** + * Handles to all of the running peers. + */ +static struct GNUNET_TESTBED_Peer **daemons; + +/** + * Global configuration file + */ +static struct GNUNET_CONFIGURATION_Handle *testing_cfg; + +/** + * Maximum number of connections to NSE services. + */ +static unsigned int connection_limit; + +/** + * Total number of connections in the whole network. + */ +static unsigned int total_connections; + +/** + * File to report results to. + */ +static struct GNUNET_DISK_FileHandle *output_file; + +/** + * Filename to log results to. + */ +static char *output_filename; + +/** + * File to log connection info, statistics to. + */ +static struct GNUNET_DISK_FileHandle *data_file; + +/** + * Filename to log connection info, statistics to. + */ +static char *data_filename; + +/** + * How long to wait before triggering next round? + * Default: 60 s. + */ +static struct GNUNET_TIME_Relative wait_time = { 60 * 1000 }; + +/** + * DLL head for operation list + */ +static struct OpListEntry *oplist_head; + +/** + * DLL tail for operation list + */ +static struct OpListEntry *oplist_tail; + +/** + * Task running each round of the experiment. + */ +static struct GNUNET_SCHEDULER_Task *round_task; + + +/** + * Clean up all of the monitoring connections to NSE and + * STATISTICS that we keep to selected peers. + */ +static void +close_monitor_connections () +{ + struct NSEPeer *pos; + struct OpListEntry *oplist_entry; + + while (NULL != (pos = peer_head)) + { + if (NULL != pos->nse_op) + GNUNET_TESTBED_operation_done (pos->nse_op); + if (NULL != pos->stat_op) + GNUNET_TESTBED_operation_done (pos->stat_op); + GNUNET_CONTAINER_DLL_remove (peer_head, peer_tail, pos); + GNUNET_free (pos); + } + while (NULL != (oplist_entry = oplist_head)) + { + GNUNET_CONTAINER_DLL_remove (oplist_head, oplist_tail, oplist_entry); + GNUNET_TESTBED_operation_done (oplist_entry->op); + GNUNET_free (oplist_entry); + } +} + + +/** + * Task run on shutdown; cleans up everything. + * + * @param cls unused + */ +static void +shutdown_task (void *cls) +{ + LOG_DEBUG ("Ending test.\n"); + close_monitor_connections (); + if (NULL != round_task) + { + GNUNET_SCHEDULER_cancel (round_task); + round_task = NULL; + } + if (NULL != data_file) + { + GNUNET_DISK_file_close (data_file); + data_file = NULL; + } + if (NULL != output_file) + { + GNUNET_DISK_file_close (output_file); + output_file = NULL; + } + if (NULL != testing_cfg) + { + GNUNET_CONFIGURATION_destroy (testing_cfg); + testing_cfg = NULL; + } +} + + +/** + * Callback to call when network size estimate is updated. + * + * @param cls closure with the 'struct NSEPeer' providing the update + * @param timestamp server timestamp + * @param estimate the value of the current network size estimate + * @param std_dev standard deviation (rounded down to nearest integer) + * of the size estimation values seen + */ +static void +handle_estimate (void *cls, + struct GNUNET_TIME_Absolute timestamp, + double estimate, + double std_dev) +{ + struct NSEPeer *peer = cls; + char output_buffer[512]; + size_t size; + + if (NULL == output_file) + { + fprintf (stderr, + "Received network size estimate from peer %p. Size: %f std.dev. %f\n", + peer, + estimate, + std_dev); + return; + } + size = GNUNET_snprintf (output_buffer, + sizeof(output_buffer), + "%p %u %llu %f %f %f\n", + peer, + peers_running, + (unsigned long long) timestamp.abs_value_us, + GNUNET_NSE_log_estimate_to_n (estimate), + estimate, + std_dev); + if (size != GNUNET_DISK_file_write (output_file, output_buffer, size)) + GNUNET_log (GNUNET_ERROR_TYPE_WARNING, "Unable to write to file!\n"); +} + + +/** + * Adapter function called to establish a connection to + * NSE service. + * + * @param cls closure (the 'struct NSEPeer') + * @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 * +nse_connect_adapter (void *cls, const struct GNUNET_CONFIGURATION_Handle *cfg) +{ + struct NSEPeer *current_peer = cls; + + return GNUNET_NSE_connect (cfg, &handle_estimate, current_peer); +} + + +/** + * Adapter function called to destroy a connection to + * NSE service. + * + * @param cls closure + * @param op_result service handle returned from the connect adapter + */ +static void +nse_disconnect_adapter (void *cls, void *op_result) +{ + GNUNET_NSE_disconnect (op_result); +} + + +/** + * Callback function to process statistic values. + * + * @param cls `struct NSEPeer` + * @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 +stat_iterator (void *cls, + const char *subsystem, + const char *name, + uint64_t value, + int is_persistent) +{ + char *output_buffer; + struct GNUNET_TIME_Absolute now; + int size; + unsigned int flag; + + GNUNET_assert (NULL != data_file); + now = GNUNET_TIME_absolute_get (); + flag = strcasecmp (subsystem, "core"); + if (0 != flag) + flag = 1; + size = GNUNET_asprintf (&output_buffer, + "%llu %llu %u\n", + (unsigned long long) now.abs_value_us / 1000LL / 1000LL, + (unsigned long long) value, + flag); + if (0 > size) + { + GNUNET_log (GNUNET_ERROR_TYPE_WARNING, "Error formatting output buffer.\n"); + GNUNET_free (output_buffer); + return GNUNET_SYSERR; + } + if (size != GNUNET_DISK_file_write (data_file, output_buffer, (size_t) size)) + { + GNUNET_log (GNUNET_ERROR_TYPE_WARNING, "Unable to write to file!\n"); + GNUNET_free (output_buffer); + return GNUNET_SYSERR; + } + GNUNET_free (output_buffer); + return GNUNET_OK; +} + + +/** + * Called to open a connection to the peer's statistics + * + * @param cls peer context + * @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 * +stat_connect_adapter (void *cls, const struct GNUNET_CONFIGURATION_Handle *cfg) +{ + struct NSEPeer *peer = cls; + + peer->sh = GNUNET_STATISTICS_create ("nse-profiler", cfg); + return peer->sh; +} + + +/** + * Called to disconnect from peer's statistics service + * + * @param cls peer context + * @param op_result service handle returned from the connect adapter + */ +static void +stat_disconnect_adapter (void *cls, void *op_result) +{ + struct NSEPeer *peer = cls; + + GNUNET_break (GNUNET_OK == + GNUNET_STATISTICS_watch_cancel (peer->sh, + "core", + "# peers connected", + stat_iterator, + peer)); + GNUNET_break (GNUNET_OK == + GNUNET_STATISTICS_watch_cancel (peer->sh, + "nse", + "# peers connected", + stat_iterator, + peer)); + GNUNET_STATISTICS_destroy (op_result, GNUNET_NO); + peer->sh = NULL; +} + + +/** + * Called after successfully opening a connection to a peer's statistics + * service; we register statistics monitoring for CORE and NSE here. + * + * @param cls the callback closure from functions generating an operation + * @param op the operation that has been finished + * @param ca_result the service handle returned from GNUNET_TESTBED_ConnectAdapter() + * @param emsg error message in case the operation has failed; will be NULL if + * operation has executed successfully. + */ +static void +stat_comp_cb (void *cls, + struct GNUNET_TESTBED_Operation *op, + void *ca_result, + const char *emsg) +{ + struct GNUNET_STATISTICS_Handle *sh = ca_result; + struct NSEPeer *peer = cls; + + if (NULL != emsg) + { + GNUNET_break (0); + return; + } + GNUNET_break (GNUNET_OK == GNUNET_STATISTICS_watch (sh, + "core", + "# peers connected", + stat_iterator, + peer)); + GNUNET_break (GNUNET_OK == GNUNET_STATISTICS_watch (sh, + "nse", + "# peers connected", + stat_iterator, + peer)); +} + + +/** + * Task run to connect to the NSE and statistics services to a subset of + * all of the running peers. + */ +static void +connect_nse_service () +{ + struct NSEPeer *current_peer; + unsigned int i; + unsigned int connections; + + if (0 == connection_limit) + return; + LOG_DEBUG ("Connecting to nse service of peers\n"); + connections = 0; + for (i = 0; i < num_peers_in_round[current_round]; i++) + { + if ((num_peers_in_round[current_round] > connection_limit) && + (0 != (i % (num_peers_in_round[current_round] / connection_limit)))) + continue; + LOG_DEBUG ("Connecting to nse service of peer %d\n", i); + current_peer = GNUNET_new (struct NSEPeer); + current_peer->daemon = daemons[i]; + current_peer->nse_op = + GNUNET_TESTBED_service_connect (NULL, + current_peer->daemon, + "nse", + NULL, + NULL, + &nse_connect_adapter, + &nse_disconnect_adapter, + current_peer); + if (NULL != data_file) + current_peer->stat_op = + GNUNET_TESTBED_service_connect (NULL, + current_peer->daemon, + "statistics", + stat_comp_cb, + current_peer, + &stat_connect_adapter, + &stat_disconnect_adapter, + current_peer); + GNUNET_CONTAINER_DLL_insert (peer_head, peer_tail, current_peer); + if (++connections == connection_limit) + break; + } +} + + +/** + * Task that starts/stops peers to move to the next round. + * + * @param cls NULL, unused + */ +static void +next_round (void *cls); + + +/** + * We're at the end of a round. Stop monitoring, write total + * number of connections to log and get full stats. Then trigger + * the next round. + * + * @param cls unused, NULL + */ +static void +finish_round (void *cls) +{ + LOG (GNUNET_ERROR_TYPE_INFO, "Have %u connections\n", total_connections); + close_monitor_connections (); + round_task = GNUNET_SCHEDULER_add_now (&next_round, NULL); +} + + +/** + * We have reached the desired number of peers for the current round. + * Run it (by connecting and monitoring a few peers and waiting the + * specified delay before finishing the round). + */ +static void +run_round () +{ + LOG_DEBUG ("Running round %u\n", current_round); + connect_nse_service (); + GNUNET_SCHEDULER_add_delayed (wait_time, &finish_round, NULL); +} + + +/** + * Creates an oplist entry and adds it to the oplist DLL + */ +static struct OpListEntry * +make_oplist_entry () +{ + struct OpListEntry *entry; + + entry = GNUNET_new (struct OpListEntry); + GNUNET_CONTAINER_DLL_insert_tail (oplist_head, oplist_tail, entry); + return entry; +} + + +/** + * Callback to be called when NSE service is started or stopped at peers + * + * @param cls NULL + * @param op the operation handle + * @param emsg NULL on success; otherwise an error description + */ +static void +manage_service_cb (void *cls, + struct GNUNET_TESTBED_Operation *op, + const char *emsg) +{ + struct OpListEntry *entry = cls; + + GNUNET_TESTBED_operation_done (entry->op); + if (NULL != emsg) + { + LOG (GNUNET_ERROR_TYPE_ERROR, "Failed to start/stop NSE at a peer\n"); + GNUNET_SCHEDULER_shutdown (); + return; + } + GNUNET_assert (0 != entry->delta); + peers_running += entry->delta; + GNUNET_CONTAINER_DLL_remove (oplist_head, oplist_tail, entry); + GNUNET_free (entry); + if (num_peers_in_round[current_round] == peers_running) + run_round (); +} + + +/** + * Adjust the number of running peers to match the required number of running + * peers for the round + */ +static void +adjust_running_peers () +{ + struct OpListEntry *entry; + unsigned int i; + + /* start peers if we have too few */ + for (i = peers_running; i < num_peers_in_round[current_round]; i++) + { + entry = make_oplist_entry (); + entry->delta = 1; + entry->op = GNUNET_TESTBED_peer_manage_service (NULL, + daemons[i], + "nse", + &manage_service_cb, + entry, + 1); + } + /* stop peers if we have too many */ + for (i = num_peers_in_round[current_round]; i < peers_running; i++) + { + entry = make_oplist_entry (); + entry->delta = -1; + entry->op = GNUNET_TESTBED_peer_manage_service (NULL, + daemons[i], + "nse", + &manage_service_cb, + entry, + 0); + } +} + + +/** + * Task run at the end of a round. Disconnect from all monitored + * peers; then get statistics from *all* peers. + * + * @param cls NULL, unused + */ +static void +next_round (void *cls) +{ + round_task = NULL; + LOG_DEBUG ("Disconnecting nse service of peers\n"); + current_round++; + if (current_round == num_rounds) + { + /* this was the last round, terminate */ + ok = 0; + GNUNET_SCHEDULER_shutdown (); + return; + } + if (num_peers_in_round[current_round] == peers_running) + { + /* no need to churn, just run next round */ + run_round (); + return; + } + adjust_running_peers (); +} + + +/** + * 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: + total_connections++; + break; + + case GNUNET_TESTBED_ET_DISCONNECT: + total_connections--; + break; + + default: + break; + } +} + + +/** + * 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 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 **peers, + unsigned int links_succeeded, + unsigned int links_failed) +{ + if (NULL == peers) + { + GNUNET_SCHEDULER_shutdown (); + return; + } + daemons = peers; + GNUNET_break (num_peers_ == num_peers); + peers_running = num_peers; + if (num_peers_in_round[current_round] == peers_running) + { + /* no need to churn, just run the starting round */ + run_round (); + return; + } + adjust_running_peers (); +} + + +/** + * Actual main function that runs the emulation. + * + * @param cls unused + * @param args remaining args, unused + * @param cfgfile name of the configuration + * @param cfg configuration handle + */ +static void +run (void *cls, + char *const *args, + const char *cfgfile, + const struct GNUNET_CONFIGURATION_Handle *cfg) +{ + char *tok; + uint64_t event_mask; + unsigned int num; + + ok = 1; + testing_cfg = GNUNET_CONFIGURATION_dup (cfg); + LOG_DEBUG ("Starting daemons.\n"); + if (NULL == num_peer_spec) + { + fprintf (stderr, "You need to specify the number of peers to run\n"); + return; + } + for (tok = strtok (num_peer_spec, ","); NULL != tok; tok = strtok (NULL, ",")) + { + if (1 != sscanf (tok, "%u", &num)) + { + fprintf (stderr, "You need to specify numbers, not `%s'\n", tok); + return; + } + if (0 == num) + { + fprintf (stderr, "Refusing to run a round with 0 peers\n"); + return; + } + GNUNET_array_append (num_peers_in_round, num_rounds, num); + num_peers = GNUNET_MAX (num_peers, num); + } + if (0 == num_peers) + { + fprintf (stderr, "Refusing to run a testbed with no rounds\n"); + return; + } + 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); + + if ((NULL != output_filename) && + (NULL == + (output_file = GNUNET_DISK_file_open (output_filename, + GNUNET_DISK_OPEN_READWRITE + | GNUNET_DISK_OPEN_CREATE, + GNUNET_DISK_PERM_USER_READ + | GNUNET_DISK_PERM_USER_WRITE)))) + GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_ERROR, "open", output_filename); + event_mask = 0LL; + 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); + GNUNET_TESTBED_run (hosts_file, + cfg, + num_peers, + event_mask, + master_controller_cb, + NULL, /* master_controller_cb cls */ + &test_master, + NULL); /* test_master cls */ + GNUNET_SCHEDULER_add_shutdown (&shutdown_task, NULL); +} + + +/** + * Main function. + * + * @return 0 on success + */ +int +main (int argc, char *const *argv) +{ + struct GNUNET_GETOPT_CommandLineOption options[] = + { GNUNET_GETOPT_option_uint ( + 'C', + "connections", + "COUNT", + gettext_noop ( + "limit to the number of connections to NSE services, 0 for none"), + &connection_limit), + GNUNET_GETOPT_option_string ( + 'd', + "details", + "FILENAME", + gettext_noop ( + "name of the file for writing connection information and statistics"), + &data_filename), + + GNUNET_GETOPT_option_string ( + 'H', + "hosts", + "FILENAME", + gettext_noop ( + "name of the file with the login information for the testbed"), + &hosts_file), + + GNUNET_GETOPT_option_string ( + 'o', + "output", + "FILENAME", + gettext_noop ("name of the file for writing the main results"), + &output_filename), + + + GNUNET_GETOPT_option_string ( + 'p', + "peers", + "NETWORKSIZESPEC", + gettext_noop ( + "Number of peers to run in each round, separated by commas"), + &num_peer_spec), + + GNUNET_GETOPT_option_increment_uint ( + 'V', + "verbose", + gettext_noop ("be verbose (print progress information)"), + &verbose), + + GNUNET_GETOPT_option_relative_time ('w', + "wait", + "DELAY", + gettext_noop ("delay between rounds"), + &wait_time), + GNUNET_GETOPT_OPTION_END }; + + if (GNUNET_OK != GNUNET_STRINGS_get_utf8_args (argc, argv, &argc, &argv)) + return 2; + if ( + GNUNET_OK != + GNUNET_PROGRAM_run (argc, + argv, + "nse-profiler", + gettext_noop ( + "Measure quality and performance of the NSE service."), + options, + &run, + NULL)) + ok = 1; + return ok; +} + + +/* end of nse-profiler.c */ diff --git a/src/service/nse/gnunet-service-nse.c b/src/service/nse/gnunet-service-nse.c new file mode 100644 index 000000000..ee1cb025f --- /dev/null +++ b/src/service/nse/gnunet-service-nse.c @@ -0,0 +1,1587 @@ +/* + This file is part of GNUnet. + Copyright (C) 2009, 2010, 2011, 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 . + + SPDX-License-Identifier: AGPL3.0-or-later + */ + +/** + * @file nse/gnunet-service-nse.c + * @brief network size estimation service + * @author Nathan Evans + * @author Christian Grothoff + * + * The purpose of this service is to estimate the size of the network. + * Given a specified interval, each peer hashes the most recent + * timestamp which is evenly divisible by that interval. This hash is + * compared in distance to the peer identity to choose an offset. The + * closer the peer identity to the hashed timestamp, the earlier the + * peer sends out a "nearest peer" message. The closest peer's + * message should thus be received before any others, which stops + * those peer from sending their messages at a later duration. So + * every peer should receive the same nearest peer message, and from + * this can calculate the expected number of peers in the network. + */ +#include "platform.h" +#include +#include "gnunet_util_lib.h" +#include "gnunet_constants.h" +#include "gnunet_protocols.h" +#include "gnunet_signatures.h" +#include "gnunet_statistics_service.h" +#include "gnunet_core_service.h" +#include "gnunet_nse_service.h" +#if ENABLE_NSE_HISTOGRAM +#include "gnunet_testbed_logger_service.h" +#endif +#include "nse.h" +#include + + +/** + * Should messages be delayed randomly? This option should be set to + * #GNUNET_NO only for experiments, not in production. + */ +#define USE_RANDOM_DELAYS GNUNET_YES + +/** + * Generate extensive debug-level log messages? + */ +#define DEBUG_NSE GNUNET_NO + +/** + * Over how many values do we calculate the weighted average? + */ +#define HISTORY_SIZE 64 + +/** + * Message priority to use. No real rush, reliability not + * required. Corking OK. + */ +#define NSE_PRIORITY \ + (GNUNET_MQ_PRIO_BACKGROUND | GNUNET_MQ_PREF_UNRELIABLE \ + | GNUNET_MQ_PREF_CORK_ALLOWED) + +#ifdef BSD +#define log2(a) (log (a) / log (2)) +#endif + +/** + * Amount of work required (W-bit collisions) for NSE proofs, in collision-bits. + */ +static unsigned long long nse_work_required; + +/** + * Interval for sending network size estimation flood requests. + */ +static struct GNUNET_TIME_Relative gnunet_nse_interval; + +/** + * Interval between proof find runs. + */ +static struct GNUNET_TIME_Relative proof_find_delay; + +#if ENABLE_NSE_HISTOGRAM + +/** + * Handle to test if testbed logger service is running or not + */ +struct GNUNET_CLIENT_TestHandle *logger_test; + +/** + * Handle for writing when we received messages to disk. + */ +static struct GNUNET_TESTBED_LOGGER_Handle *lh; + +/** + * Handle for writing message received timestamp information to disk. + */ +static struct GNUNET_BIO_WriteHandle *histogram; + +#endif + +/** + * Salt for PoW calcualations. + */ +static struct GNUNET_CRYPTO_PowSalt salt = { "gnunet-nse-proof" }; + + +/** + * Per-peer information. + */ +struct NSEPeerEntry +{ + /** + * Core handle for sending messages to this peer. + */ + struct GNUNET_MQ_Handle *mq; + + /** + * What is the identity of the peer? + */ + const struct GNUNET_PeerIdentity *id; + + /** + * Task scheduled to send message to this peer. + */ + struct GNUNET_SCHEDULER_Task *transmit_task; + + /** + * Did we receive or send a message about the previous round + * to this peer yet? #GNUNET_YES if the previous round has + * been taken care of. + */ + int previous_round; + +#if ENABLE_NSE_HISTOGRAM + /** + * Amount of messages received from this peer on this round. + */ + unsigned int received_messages; + + /** + * Amount of messages transmitted to this peer on this round. + */ + unsigned int transmitted_messages; + + /** + * Which size did we tell the peer the network is? + */ + unsigned int last_transmitted_size; +#endif +}; + + +GNUNET_NETWORK_STRUCT_BEGIN + +/** + * Network size estimate reply; sent when "this" + * peer's timer has run out before receiving a + * valid reply from another peer. + */ +struct GNUNET_NSE_FloodMessage +{ + /** + * Type: #GNUNET_MESSAGE_TYPE_NSE_P2P_FLOOD + */ + struct GNUNET_MessageHeader header; + + /** + * Number of hops this message has taken so far. + */ + uint32_t hop_count GNUNET_PACKED; + + /** + * Purpose. + */ + struct GNUNET_CRYPTO_EccSignaturePurpose purpose; + + /** + * The current timestamp value (which all + * peers should agree on). + */ + struct GNUNET_TIME_AbsoluteNBO timestamp; + + /** + * Number of matching bits between the hash + * of timestamp and the initiator's public + * key. + */ + uint32_t matching_bits GNUNET_PACKED; + + /** + * Public key of the originator. + */ + struct GNUNET_PeerIdentity origin; + + /** + * Proof of work, causing leading zeros when hashed with pkey. + */ + uint64_t proof_of_work GNUNET_PACKED; + + /** + * Signature (over range specified in purpose). + */ + struct GNUNET_CRYPTO_EddsaSignature signature; +}; +GNUNET_NETWORK_STRUCT_END + +/** + * Handle to our current configuration. + */ +static const struct GNUNET_CONFIGURATION_Handle *cfg; + +/** + * Handle to the statistics service. + */ +static struct GNUNET_STATISTICS_Handle *stats; + +/** + * Handle to the core service. + */ +static struct GNUNET_CORE_Handle *core_api; + +/** + * Map of all connected peers. + */ +static struct GNUNET_CONTAINER_MultiPeerMap *peers; + +/** + * The current network size estimate. Number of bits matching on + * average thus far. + */ +static double current_size_estimate; + +/** + * The standard deviation of the last #HISTORY_SIZE network + * size estimates. + */ +static double current_std_dev = NAN; + +/** + * Current hop counter estimate (estimate for network diameter). + */ +static uint32_t hop_count_max; + +/** + * Message for the next round, if we got any. + */ +static struct GNUNET_NSE_FloodMessage next_message; + +/** + * Array of recent size estimate messages. + */ +static struct GNUNET_NSE_FloodMessage size_estimate_messages[HISTORY_SIZE]; + +/** + * Index of most recent estimate. + */ +static unsigned int estimate_index; + +/** + * Number of valid entries in the history. + */ +static unsigned int estimate_count; + +/** + * Task scheduled to update our flood message for the next round. + */ +static struct GNUNET_SCHEDULER_Task *flood_task; + +/** + * Task scheduled to compute our proof. + */ +static struct GNUNET_SCHEDULER_Task *proof_task; + +/** + * Notification context, simplifies client broadcasts. + */ +static struct GNUNET_NotificationContext *nc; + +/** + * The next major time. + */ +static struct GNUNET_TIME_Absolute next_timestamp; + +/** + * The current major time. + */ +static struct GNUNET_TIME_Absolute current_timestamp; + +/** + * The private key of this peer. + */ +static struct GNUNET_CRYPTO_EddsaPrivateKey *my_private_key; + +/** + * The peer identity of this peer. + */ +static struct GNUNET_PeerIdentity my_identity; + +/** + * Proof of work for this peer. + */ +static uint64_t my_proof; + + +/** + * Initialize a message to clients with the current network + * size estimate. + * + * @param em message to fill in + */ +static void +setup_estimate_message (struct GNUNET_NSE_ClientMessage *em) +{ + double mean; + double sum; + double std_dev; + double variance; + double val; + double nsize; + +#define WEST 1 + /* Weighted incremental algorithm for stddev according to West (1979) */ +#if WEST + double sumweight; + double weight; + double q; + double r; + double temp; + + mean = 0.0; + sum = 0.0; + sumweight = 0.0; + variance = 0.0; + for (unsigned int i = 0; i < estimate_count; i++) + { + unsigned int j = (estimate_index - i + HISTORY_SIZE) % HISTORY_SIZE; + + val = htonl (size_estimate_messages[j].matching_bits); + weight = estimate_count + 1 - i; + + temp = weight + sumweight; + q = val - mean; + r = q * weight / temp; + mean += r; + sum += sumweight * q * r; + sumweight = temp; + } + if (estimate_count > 0) + variance = (sum / sumweight) * estimate_count / (estimate_count - 1.0); +#else + /* trivial version for debugging */ + double vsq; + + /* non-weighted trivial version */ + sum = 0.0; + vsq = 0.0; + variance = 0.0; + mean = 0.0; + + for (unsigned int i = 0; i < estimate_count; i++) + { + unsigned int j = (estimate_index - i + HISTORY_SIZE) % HISTORY_SIZE; + + val = htonl (size_estimate_messages[j].matching_bits); + sum += val; + vsq += val * val; + } + if (0 != estimate_count) + { + mean = sum / estimate_count; + variance = (vsq - mean * sum) + / (estimate_count - 1.0); // terrible for numerical stability... + } +#endif + if (variance >= 0) + std_dev = sqrt (variance); + else + std_dev = variance; /* return NaN (due to estimate_count == 0 causing 0.0/0.0) */ + current_std_dev = std_dev; + current_size_estimate = mean; + + em->header.size = htons (sizeof(struct GNUNET_NSE_ClientMessage)); + em->header.type = htons (GNUNET_MESSAGE_TYPE_NSE_ESTIMATE); + em->reserved = htonl (0); + em->timestamp = GNUNET_TIME_absolute_hton (GNUNET_TIME_absolute_get ()); + { + double se = mean - 0.332747; + unsigned int j = GNUNET_CONTAINER_multipeermap_size (peers); + if (0 == j) + j = 1; /* Avoid log2(0); can only happen if CORE didn't report + connection to self yet */ + nsize = log2 (j); + em->size_estimate = GNUNET_hton_double (GNUNET_MAX (se, nsize)); + em->std_deviation = GNUNET_hton_double (std_dev); + GNUNET_STATISTICS_set (stats, + "# nodes in the network (estimate)", + (uint64_t) pow (2, GNUNET_MAX (se, nsize)), + GNUNET_NO); + } +} + + +/** + * Handler for START message from client, triggers an + * immediate current network estimate notification. + * Also, we remember the client for updates upon future + * estimate measurements. + * + * @param cls client who sent the message + * @param message the message received + */ +static void +handle_start (void *cls, const struct GNUNET_MessageHeader *message) +{ + struct GNUNET_SERVICE_Client *client = cls; + struct GNUNET_MQ_Handle *mq; + struct GNUNET_NSE_ClientMessage em; + struct GNUNET_MQ_Envelope *env; + + (void) message; + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Received START message from client\n"); + mq = GNUNET_SERVICE_client_get_mq (client); + GNUNET_notification_context_add (nc, mq); + setup_estimate_message (&em); + env = GNUNET_MQ_msg_copy (&em.header); + GNUNET_MQ_send (mq, env); + GNUNET_SERVICE_client_continue (client); +} + + +/** + * How long should we delay a message to go the given number of + * matching bits? + * + * @param matching_bits number of matching bits to consider + */ +static double +get_matching_bits_delay (uint32_t matching_bits) +{ + /* Calculated as: S + f/2 - (f / pi) * (atan(x - p')) */ + // S is next_timestamp (ignored in return value) + // f is frequency (gnunet_nse_interval) + // x is matching_bits + // p' is current_size_estimate + return ((double) gnunet_nse_interval.rel_value_us / (double) 2.0) + - ((gnunet_nse_interval.rel_value_us / M_PI) + * atan (matching_bits - current_size_estimate)); +} + + +/** + * What delay randomization should we apply for a given number of matching bits? + * + * @param matching_bits number of matching bits + * @return random delay to apply + */ +static struct GNUNET_TIME_Relative +get_delay_randomization (uint32_t matching_bits) +{ +#if USE_RANDOM_DELAYS + struct GNUNET_TIME_Relative ret; + uint32_t i; + double d; + + d = get_matching_bits_delay (matching_bits); + i = (uint32_t) (d / (double) (hop_count_max + 1)); + ret.rel_value_us = i; + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Randomizing flood using latencies up to %s\n", + GNUNET_STRINGS_relative_time_to_string (ret, GNUNET_YES)); + ret.rel_value_us = + GNUNET_CRYPTO_random_u32 (GNUNET_CRYPTO_QUALITY_WEAK, i + 1); + return ret; +#else + return GNUNET_TIME_UNIT_ZERO; +#endif +} + + +/** + * Get the number of matching bits that the given timestamp has to the given peer ID. + * + * @param timestamp time to generate key + * @param id peer identity to compare with + * @return number of matching bits + */ +static uint32_t +get_matching_bits (struct GNUNET_TIME_Absolute timestamp, + const struct GNUNET_PeerIdentity *id) +{ + struct GNUNET_HashCode timestamp_hash; + struct GNUNET_HashCode pid_hash; + struct GNUNET_HashCode xor; + + GNUNET_CRYPTO_hash (×tamp.abs_value_us, + sizeof(timestamp.abs_value_us), + ×tamp_hash); + GNUNET_CRYPTO_hash (id, + sizeof(struct GNUNET_PeerIdentity), + &pid_hash); + GNUNET_CRYPTO_hash_xor (&pid_hash, + ×tamp_hash, + &xor); + return GNUNET_CRYPTO_hash_count_leading_zeros (&xor); +} + + +/** + * Get the transmission delay that should be applied for a + * particular round. + * + * @param round_offset -1 for the previous round (random delay between 0 and 50ms) + * 0 for the current round (based on our proximity to time key) + * @return delay that should be applied + */ +static struct GNUNET_TIME_Relative +get_transmit_delay (int round_offset) +{ + struct GNUNET_TIME_Relative ret; + struct GNUNET_TIME_Absolute tgt; + double dist_delay; + uint32_t matching_bits; + + switch (round_offset) + { + case -1: + /* previous round is randomized between 0 and 50 ms */ +#if USE_RANDOM_DELAYS + ret.rel_value_us = + GNUNET_CRYPTO_random_u64 (GNUNET_CRYPTO_QUALITY_WEAK, 50); +#else + ret = GNUNET_TIME_UNIT_ZERO; +#endif + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Transmitting previous round behind schedule in %s\n", + GNUNET_STRINGS_relative_time_to_string (ret, GNUNET_YES)); + return ret; + + case 0: + /* current round is based on best-known matching_bits */ + matching_bits = + ntohl (size_estimate_messages[estimate_index].matching_bits); + dist_delay = get_matching_bits_delay (matching_bits); + dist_delay += get_delay_randomization (matching_bits).rel_value_us; + ret.rel_value_us = (uint64_t) dist_delay; + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "For round %s, delay for %u matching bits is %s\n", + GNUNET_STRINGS_absolute_time_to_string (current_timestamp), + (unsigned int) matching_bits, + GNUNET_STRINGS_relative_time_to_string (ret, GNUNET_YES)); + /* now consider round start time and add delay to it */ + tgt = GNUNET_TIME_absolute_add (current_timestamp, ret); + return GNUNET_TIME_absolute_get_remaining (tgt); + } + GNUNET_break (0); + return GNUNET_TIME_UNIT_FOREVER_REL; +} + + +/** + * Task that triggers a NSE P2P transmission. + * + * @param cls the `struct NSEPeerEntry *` + */ +static void +transmit_task_cb (void *cls) +{ + struct NSEPeerEntry *peer_entry = cls; + unsigned int idx; + struct GNUNET_MQ_Envelope *env; + + peer_entry->transmit_task = NULL; + idx = estimate_index; + if (GNUNET_NO == peer_entry->previous_round) + { + idx = (idx + HISTORY_SIZE - 1) % HISTORY_SIZE; + peer_entry->previous_round = GNUNET_YES; + peer_entry->transmit_task = + GNUNET_SCHEDULER_add_delayed (get_transmit_delay (0), + &transmit_task_cb, + peer_entry); + } + if ((0 == ntohl (size_estimate_messages[idx].hop_count)) && + (NULL != proof_task)) + { + GNUNET_STATISTICS_update (stats, + "# flood messages not generated (no proof yet)", + 1, + GNUNET_NO); + return; + } + if (0 == ntohs (size_estimate_messages[idx].header.size)) + { + GNUNET_STATISTICS_update (stats, + "# flood messages not generated (lack of history)", + 1, + GNUNET_NO); + return; + } + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "In round %s, sending to `%s' estimate with %u bits\n", + GNUNET_STRINGS_absolute_time_to_string ( + GNUNET_TIME_absolute_ntoh ( + size_estimate_messages[idx].timestamp)), + GNUNET_i2s (peer_entry->id), + (unsigned int) ntohl (size_estimate_messages[idx].matching_bits)); + if (0 == ntohl (size_estimate_messages[idx].hop_count)) + GNUNET_STATISTICS_update (stats, "# flood messages started", 1, GNUNET_NO); + GNUNET_STATISTICS_update (stats, + "# flood messages transmitted", + 1, + GNUNET_NO); +#if ENABLE_NSE_HISTOGRAM + peer_entry->transmitted_messages++; + peer_entry->last_transmitted_size = + ntohl (size_estimate_messages[idx].matching_bits); +#endif + env = GNUNET_MQ_msg_copy (&size_estimate_messages[idx].header); + GNUNET_MQ_send (peer_entry->mq, env); +} + + +/** + * We've sent on our flood message or one that we received which was + * validated and closer than ours. Update the global list of recent + * messages and the average. Also re-broadcast the message to any + * clients. + */ +static void +update_network_size_estimate () +{ + struct GNUNET_NSE_ClientMessage em; + + setup_estimate_message (&em); + GNUNET_notification_context_broadcast (nc, &em.header, GNUNET_YES); +} + + +/** + * Setup a flood message in our history array at the given + * slot offset for the given timestamp. + * + * @param slot index to use + * @param ts timestamp to use + */ +static void +setup_flood_message (unsigned int slot, struct GNUNET_TIME_Absolute ts) +{ + struct GNUNET_NSE_FloodMessage *fm; + uint32_t matching_bits; + + matching_bits = get_matching_bits (ts, &my_identity); + fm = &size_estimate_messages[slot]; + fm->header.size = htons (sizeof(struct GNUNET_NSE_FloodMessage)); + fm->header.type = htons (GNUNET_MESSAGE_TYPE_NSE_P2P_FLOOD); + fm->hop_count = htonl (0); + fm->purpose.purpose = htonl (GNUNET_SIGNATURE_PURPOSE_NSE_SEND); + fm->purpose.size = + htonl (sizeof(struct GNUNET_NSE_FloodMessage) + - sizeof(struct GNUNET_MessageHeader) - sizeof(uint32_t) + - sizeof(struct GNUNET_CRYPTO_EddsaSignature)); + fm->matching_bits = htonl (matching_bits); + fm->timestamp = GNUNET_TIME_absolute_hton (ts); + fm->origin = my_identity; + fm->proof_of_work = my_proof; + if (nse_work_required > 0) + GNUNET_assert (GNUNET_OK == + GNUNET_CRYPTO_eddsa_sign_ (my_private_key, + &fm->purpose, + &fm->signature)); + else + memset (&fm->signature, 0, sizeof(fm->signature)); +} + + +/** + * Schedule transmission for the given peer for the current round based + * on what we know about the desired delay. + * + * @param cls unused + * @param key hash of peer identity + * @param value the `struct NSEPeerEntry` + * @return #GNUNET_OK (continue to iterate) + */ +static int +schedule_current_round (void *cls, + const struct GNUNET_PeerIdentity *key, + void *value) +{ + struct NSEPeerEntry *peer_entry = value; + struct GNUNET_TIME_Relative delay; + + (void) cls; + (void) key; + if (NULL != peer_entry->transmit_task) + { + GNUNET_SCHEDULER_cancel (peer_entry->transmit_task); + peer_entry->previous_round = GNUNET_NO; + } +#if ENABLE_NSE_HISTOGRAM + if (peer_entry->received_messages > 1) + GNUNET_STATISTICS_update (stats, + "# extra messages", + peer_entry->received_messages - 1, + GNUNET_NO); + peer_entry->transmitted_messages = 0; + peer_entry->last_transmitted_size = 0; + peer_entry->received_messages = 0; +#endif + delay = + get_transmit_delay ((GNUNET_NO == peer_entry->previous_round) ? -1 : 0); + peer_entry->transmit_task = + GNUNET_SCHEDULER_add_delayed (delay, &transmit_task_cb, peer_entry); + return GNUNET_OK; +} + + +/** + * Update our flood message to be sent (and our timestamps). + * + * @param cls unused + */ +static void +update_flood_message (void *cls) +{ + struct GNUNET_TIME_Relative offset; + + (void) cls; + flood_task = NULL; + offset = GNUNET_TIME_absolute_get_remaining (next_timestamp); + if (0 != offset.rel_value_us) + { + /* somehow run early, delay more */ + flood_task = + GNUNET_SCHEDULER_add_delayed (offset, &update_flood_message, NULL); + return; + } + estimate_index = (estimate_index + 1) % HISTORY_SIZE; + if (estimate_count < HISTORY_SIZE) + estimate_count++; + current_timestamp = next_timestamp; + next_timestamp = + GNUNET_TIME_absolute_add (current_timestamp, gnunet_nse_interval); + if ((current_timestamp.abs_value_us == + GNUNET_TIME_absolute_ntoh (next_message.timestamp).abs_value_us) && + (get_matching_bits (current_timestamp, &my_identity) < + ntohl (next_message.matching_bits))) + { + /* we received a message for this round way early, use it! */ + size_estimate_messages[estimate_index] = next_message; + size_estimate_messages[estimate_index].hop_count = + htonl (1 + ntohl (next_message.hop_count)); + } + else + setup_flood_message (estimate_index, current_timestamp); + next_message.matching_bits = htonl (0); /* reset for 'next' round */ + hop_count_max = 0; + for (unsigned int i = 0; i < HISTORY_SIZE; i++) + hop_count_max = + GNUNET_MAX (ntohl (size_estimate_messages[i].hop_count), hop_count_max); + GNUNET_CONTAINER_multipeermap_iterate (peers, &schedule_current_round, NULL); + flood_task = + GNUNET_SCHEDULER_add_at (next_timestamp, &update_flood_message, NULL); +} + + +/** + * Check whether the given public key and integer are a valid proof of + * work. + * + * @param pkey the public key + * @param val the integer + * @return #GNUNET_YES if valid, #GNUNET_NO if not + */ +static enum GNUNET_GenericReturnValue +check_proof_of_work (const struct GNUNET_CRYPTO_EddsaPublicKey *pkey, + uint64_t val) +{ + char buf[sizeof(struct GNUNET_CRYPTO_EddsaPublicKey) + + sizeof(val)] GNUNET_ALIGN; + struct GNUNET_HashCode result; + + GNUNET_memcpy (buf, &val, sizeof(val)); + GNUNET_memcpy (&buf[sizeof(val)], + pkey, + sizeof(struct GNUNET_CRYPTO_EddsaPublicKey)); + GNUNET_CRYPTO_pow_hash (&salt, + buf, + sizeof(buf), + &result); + return (GNUNET_CRYPTO_hash_count_leading_zeros (&result) >= + nse_work_required) + ? GNUNET_YES + : GNUNET_NO; +} + + +/** + * Write our current proof to disk. + */ +static void +write_proof (void) +{ + char *proof; + + if (GNUNET_OK != + GNUNET_CONFIGURATION_get_value_filename (cfg, + "NSE", + "PROOFFILE", + &proof)) + return; + (void) GNUNET_DISK_directory_remove (proof); + if (GNUNET_OK != + GNUNET_DISK_fn_write (proof, + &my_proof, + sizeof(my_proof), + GNUNET_DISK_PERM_USER_READ + | GNUNET_DISK_PERM_USER_WRITE)) + GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING, + "write", + proof); + GNUNET_free (proof); +} + + +/** + * Find our proof of work. + * + * @param cls closure (unused) + */ +static void +find_proof (void *cls) +{ +#define ROUND_SIZE 10 + uint64_t counter; + char buf[sizeof(struct GNUNET_CRYPTO_EddsaPublicKey) + + sizeof(uint64_t)] GNUNET_ALIGN; + struct GNUNET_HashCode result; + unsigned int i; + + (void) cls; + proof_task = NULL; + GNUNET_memcpy (&buf[sizeof(uint64_t)], + &my_identity, + sizeof(struct GNUNET_PeerIdentity)); + i = 0; + counter = my_proof; + while ((counter != UINT64_MAX) && (i < ROUND_SIZE)) + { + GNUNET_memcpy (buf, &counter, sizeof(uint64_t)); + GNUNET_CRYPTO_pow_hash (&salt, + buf, + sizeof(buf), + &result); + if (nse_work_required <= + GNUNET_CRYPTO_hash_count_leading_zeros (&result)) + { + my_proof = counter; + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Proof of work found: %llu!\n", + (unsigned long long) GNUNET_ntohll (counter)); + write_proof (); + setup_flood_message (estimate_index, current_timestamp); + return; + } + counter++; + i++; + } + if (my_proof / (100 * ROUND_SIZE) < counter / (100 * ROUND_SIZE)) + { + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Testing proofs currently at %llu\n", + (unsigned long long) counter); + /* remember progress every 100 rounds */ + my_proof = counter; + write_proof (); + } + else + { + my_proof = counter; + } + proof_task = + GNUNET_SCHEDULER_add_delayed_with_priority (proof_find_delay, + GNUNET_SCHEDULER_PRIORITY_IDLE, + &find_proof, + NULL); +} + + +/** + * An incoming flood message has been received which claims + * to have more bits matching than any we know in this time + * period. Verify the signature and/or proof of work. + * + * @param incoming_flood the message to verify + * @return #GNUNET_YES if the message is verified + * #GNUNET_NO if the key/signature don't verify + */ +static int +verify_message_crypto (const struct GNUNET_NSE_FloodMessage *incoming_flood) +{ + if (GNUNET_YES != check_proof_of_work (&incoming_flood->origin.public_key, + incoming_flood->proof_of_work)) + { + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Proof of work invalid: %llu!\n", + (unsigned long long) GNUNET_ntohll ( + incoming_flood->proof_of_work)); + GNUNET_break_op (0); + return GNUNET_NO; + } + if ((nse_work_required > 0) && + (GNUNET_OK != + GNUNET_CRYPTO_eddsa_verify_ (GNUNET_SIGNATURE_PURPOSE_NSE_SEND, + &incoming_flood->purpose, + &incoming_flood->signature, + &incoming_flood->origin.public_key))) + { + GNUNET_break_op (0); + return GNUNET_NO; + } + return GNUNET_YES; +} + + +/** + * Update transmissions for the given peer for the current round based + * on updated proximity information. + * + * @param cls peer entry to exclude from updates + * @param key hash of peer identity + * @param value the `struct NSEPeerEntry *` of a peer to transmit to + * @return #GNUNET_OK (continue to iterate) + */ +static int +update_flood_times (void *cls, + const struct GNUNET_PeerIdentity *key, + void *value) +{ + struct NSEPeerEntry *exclude = cls; + struct NSEPeerEntry *peer_entry = value; + struct GNUNET_TIME_Relative delay; + + (void) key; + if (peer_entry == exclude) + return GNUNET_OK; /* trigger of the update */ + if (GNUNET_NO == peer_entry->previous_round) + { + /* still stuck in previous round, no point to update, check that + * we are active here though... */ + if (NULL == peer_entry->transmit_task) + { + GNUNET_break (0); + } + return GNUNET_OK; + } + if (NULL != peer_entry->transmit_task) + { + GNUNET_SCHEDULER_cancel (peer_entry->transmit_task); + peer_entry->transmit_task = NULL; + } + delay = get_transmit_delay (0); + peer_entry->transmit_task = + GNUNET_SCHEDULER_add_delayed (delay, &transmit_task_cb, peer_entry); + return GNUNET_OK; +} + + +/** + * Core handler for size estimate flooding messages. + * + * @param cls peer this message is from + * @param incoming_flood received message + */ +static void +handle_p2p_estimate (void *cls, + const struct GNUNET_NSE_FloodMessage *incoming_flood) +{ + struct NSEPeerEntry *peer_entry = cls; + struct GNUNET_TIME_Absolute ts; + uint32_t matching_bits; + unsigned int idx; + +#if ENABLE_NSE_HISTOGRAM + { + uint64_t t; + + t = GNUNET_TIME_absolute_get ().abs_value_us; + if (NULL != lh) + GNUNET_TESTBED_LOGGER_write (lh, &t, sizeof(uint64_t)); + if (NULL != histogram) + GNUNET_BIO_write_int64 (histogram, "histogram-time", t); + } +#endif + GNUNET_STATISTICS_update (stats, "# flood messages received", 1, GNUNET_NO); + matching_bits = ntohl (incoming_flood->matching_bits); +#if DEBUG_NSE + { + char origin[5]; + char pred[5]; + struct GNUNET_PeerIdentity os; + + GNUNET_snprintf (origin, + sizeof(origin), + "%s", + GNUNET_i2s (&incoming_flood->origin)); + GNUNET_snprintf (pred, sizeof(pred), "%s", GNUNET_i2s (peer_entry->id)); + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Flood at %s from `%s' via `%s' at `%s' with bits %u\n", + GNUNET_STRINGS_absolute_time_to_string ( + GNUNET_TIME_absolute_ntoh (incoming_flood->timestamp)), + origin, + pred, + GNUNET_i2s (&my_identity), + (unsigned int) matching_bits); + } +#endif + +#if ENABLE_NSE_HISTOGRAM + peer_entry->received_messages++; + if ((peer_entry->transmitted_messages > 0) && + (peer_entry->last_transmitted_size >= matching_bits) ) + GNUNET_STATISTICS_update (stats, "# cross messages", 1, GNUNET_NO); +#endif + + ts = GNUNET_TIME_absolute_ntoh (incoming_flood->timestamp); + if (ts.abs_value_us == current_timestamp.abs_value_us) + idx = estimate_index; + else if (ts.abs_value_us == + current_timestamp.abs_value_us - gnunet_nse_interval.rel_value_us) + idx = (estimate_index + HISTORY_SIZE - 1) % HISTORY_SIZE; + else if (ts.abs_value_us == next_timestamp.abs_value_us) + { + if (matching_bits <= ntohl (next_message.matching_bits)) + return; /* ignore, simply too early/late */ + if (GNUNET_YES != verify_message_crypto (incoming_flood)) + { + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "Peer %s is likely ill-configured!\n", + GNUNET_i2s (peer_entry->id)); + GNUNET_break_op (0); + return; + } + next_message = *incoming_flood; + return; + } + else + { + GNUNET_STATISTICS_update (stats, + "# flood messages discarded (clock skew too large)", + 1, + GNUNET_NO); + return; + } + if (0 == (GNUNET_memcmp (peer_entry->id, &my_identity))) + { + /* send to self, update our own estimate IF this also comes from us! */ + if (0 == GNUNET_memcmp (&incoming_flood->origin, &my_identity)) + update_network_size_estimate (); + return; + } + if (matching_bits == ntohl (size_estimate_messages[idx].matching_bits)) + { + /* Cancel transmission in the other direction, as this peer clearly has + up-to-date information already. Even if we didn't talk to this peer in + the previous round, we should no longer send it stale information as it + told us about the current round! */ + peer_entry->previous_round = GNUNET_YES; + if (idx != estimate_index) + { + /* do not transmit information for the previous round to this peer + anymore (but allow current round) */ + return; + } + /* got up-to-date information for current round, cancel transmission to + * this peer altogether */ + if (NULL != peer_entry->transmit_task) + { + GNUNET_SCHEDULER_cancel (peer_entry->transmit_task); + peer_entry->transmit_task = NULL; + } + return; + } + if (matching_bits < ntohl (size_estimate_messages[idx].matching_bits)) + { + if ((idx < estimate_index) && (peer_entry->previous_round == GNUNET_YES)) + { + peer_entry->previous_round = GNUNET_NO; + } + /* push back our result now, that peer is spreading bad information... */ + if (NULL != peer_entry->transmit_task) + GNUNET_SCHEDULER_cancel (peer_entry->transmit_task); + peer_entry->transmit_task = + GNUNET_SCHEDULER_add_now (&transmit_task_cb, peer_entry); + /* Not closer than our most recent message, no need to do work here */ + GNUNET_STATISTICS_update (stats, + "# flood messages ignored (had closer already)", + 1, + GNUNET_NO); + return; + } + if (GNUNET_YES != verify_message_crypto (incoming_flood)) + { + GNUNET_break_op (0); + return; + } + GNUNET_assert (matching_bits > + ntohl (size_estimate_messages[idx].matching_bits)); + /* Cancel transmission in the other direction, as this peer clearly has + * up-to-date information already. + */ + peer_entry->previous_round = GNUNET_YES; + if (idx == estimate_index) + { + /* cancel any activity for current round */ + if (NULL != peer_entry->transmit_task) + { + GNUNET_SCHEDULER_cancel (peer_entry->transmit_task); + peer_entry->transmit_task = NULL; + } + } + size_estimate_messages[idx] = *incoming_flood; + size_estimate_messages[idx].hop_count = + htonl (ntohl (incoming_flood->hop_count) + 1); + hop_count_max = + GNUNET_MAX (ntohl (incoming_flood->hop_count) + 1, hop_count_max); + GNUNET_STATISTICS_set (stats, + "# estimated network diameter", + hop_count_max, + GNUNET_NO); + + /* have a new, better size estimate, inform clients */ + update_network_size_estimate (); + + /* flood to rest */ + GNUNET_CONTAINER_multipeermap_iterate (peers, + &update_flood_times, + peer_entry); +} + + +/** + * Method called whenever a peer connects. Sets up the PeerEntry and + * schedules the initial size info transmission to this peer. + * + * @param cls closure + * @param peer peer identity this notification is about + */ +static void * +handle_core_connect (void *cls, + const struct GNUNET_PeerIdentity *peer, + struct GNUNET_MQ_Handle *mq) +{ + struct NSEPeerEntry *peer_entry; + + (void) cls; + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Peer `%s' connected to us\n", + GNUNET_i2s (peer)); + /* set our default transmission options */ + GNUNET_MQ_set_options (mq, NSE_PRIORITY); + /* create our peer entry for this peer */ + peer_entry = GNUNET_new (struct NSEPeerEntry); + peer_entry->id = peer; + peer_entry->mq = mq; + GNUNET_assert (GNUNET_OK == + GNUNET_CONTAINER_multipeermap_put ( + peers, + peer_entry->id, + peer_entry, + GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_ONLY)); + peer_entry->transmit_task = + GNUNET_SCHEDULER_add_delayed (get_transmit_delay (-1), + &transmit_task_cb, + peer_entry); + GNUNET_STATISTICS_update (stats, "# peers connected", 1, GNUNET_NO); + return peer_entry; +} + + +/** + * Method called whenever a peer disconnects. Deletes the PeerEntry and cancels + * any pending transmission requests to that peer. + * + * @param cls closure + * @param peer peer identity this notification is about + * @param internal_cls the `struct NSEPeerEntry` for the @a peer + */ +static void +handle_core_disconnect (void *cls, + const struct GNUNET_PeerIdentity *peer, + void *internal_cls) +{ + struct NSEPeerEntry *pos = internal_cls; + + (void) cls; + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Peer `%s' disconnected from us\n", + GNUNET_i2s (peer)); + GNUNET_assert (GNUNET_YES == + GNUNET_CONTAINER_multipeermap_remove (peers, peer, pos)); + if (NULL != pos->transmit_task) + { + GNUNET_SCHEDULER_cancel (pos->transmit_task); + pos->transmit_task = NULL; + } + GNUNET_free (pos); + GNUNET_STATISTICS_update (stats, "# peers connected", -1, GNUNET_NO); +} + + +#if ENABLE_NSE_HISTOGRAM +/** + * Functions of this type are called to notify a successful transmission of the + * message to the logger service + * + * @param cls NULL + * @param size the amount of data sent (ignored) + */ +static void +flush_comp_cb (void *cls, size_t size) +{ + (void) cls; + (void) size; + GNUNET_TESTBED_LOGGER_disconnect (lh); + lh = NULL; +} + + +#endif + + +/** + * Task run during shutdown. + * + * @param cls unused + */ +static void +shutdown_task (void *cls) +{ + (void) cls; + if (NULL != flood_task) + { + GNUNET_SCHEDULER_cancel (flood_task); + flood_task = NULL; + } + if (NULL != proof_task) + { + GNUNET_SCHEDULER_cancel (proof_task); + proof_task = NULL; + write_proof (); /* remember progress */ + } + if (NULL != nc) + { + GNUNET_notification_context_destroy (nc); + nc = NULL; + } + if (NULL != core_api) + { + GNUNET_CORE_disconnect (core_api); + core_api = NULL; + } + if (NULL != stats) + { + GNUNET_STATISTICS_destroy (stats, GNUNET_NO); + stats = NULL; + } + if (NULL != peers) + { + GNUNET_CONTAINER_multipeermap_destroy (peers); + peers = NULL; + } + if (NULL != my_private_key) + { + GNUNET_free (my_private_key); + my_private_key = NULL; + } +#if ENABLE_NSE_HISTOGRAM + if (NULL != logger_test) + { + GNUNET_CLIENT_service_test_cancel (logger_test); + logger_test = NULL; + } + if (NULL != lh) + { + GNUNET_TESTBED_LOGGER_flush (lh, &flush_comp_cb, NULL); + } + if (NULL != histogram) + { + GNUNET_BIO_write_close (histogram, NULL); + histogram = NULL; + } +#endif +} + + +/** + * Called on core init/fail. + * + * @param cls service closure + * @param identity the public identity of this peer + */ +static void +core_init (void *cls, const struct GNUNET_PeerIdentity *identity) +{ + struct GNUNET_TIME_Absolute now; + struct GNUNET_TIME_Absolute prev_time; + + (void) cls; + if (NULL == identity) + { + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Connection to core FAILED!\n"); + GNUNET_SCHEDULER_shutdown (); + return; + } + GNUNET_assert (0 == GNUNET_memcmp (&my_identity, identity)); + now = GNUNET_TIME_absolute_get (); + current_timestamp.abs_value_us = + (now.abs_value_us / gnunet_nse_interval.rel_value_us) + * gnunet_nse_interval.rel_value_us; + next_timestamp = + GNUNET_TIME_absolute_add (current_timestamp, gnunet_nse_interval); + estimate_index = HISTORY_SIZE - 1; + estimate_count = 0; + if (GNUNET_YES == check_proof_of_work (&my_identity.public_key, my_proof)) + { + int idx = (estimate_index + HISTORY_SIZE - 1) % HISTORY_SIZE; + prev_time.abs_value_us = + current_timestamp.abs_value_us - gnunet_nse_interval.rel_value_us; + setup_flood_message (idx, prev_time); + setup_flood_message (estimate_index, current_timestamp); + estimate_count++; + } + flood_task = + GNUNET_SCHEDULER_add_at (next_timestamp, &update_flood_message, NULL); +} + + +#if ENABLE_NSE_HISTOGRAM +/** + * Function called with the status of the testbed logger service + * + * @param cls NULL + * @param status #GNUNET_YES if the service is running, + * #GNUNET_NO if the service is not running + * #GNUNET_SYSERR if the configuration is invalid + */ +static void +status_cb (void *cls, int status) +{ + (void) cls; + logger_test = NULL; + if (GNUNET_YES != status) + { + GNUNET_log (GNUNET_ERROR_TYPE_WARNING, "Testbed logger not running\n"); + return; + } + if (NULL == (lh = GNUNET_TESTBED_LOGGER_connect (cfg))) + { + GNUNET_log (GNUNET_ERROR_TYPE_WARNING, + "Cannot connect to the testbed logger. Exiting.\n"); + GNUNET_SCHEDULER_shutdown (); + } +} + + +#endif + + +/** + * Handle network size estimate clients. + * + * @param cls closure + * @param c configuration to use + * @param service the initialized service + */ +static void +run (void *cls, + const struct GNUNET_CONFIGURATION_Handle *c, + struct GNUNET_SERVICE_Handle *service) +{ + struct GNUNET_MQ_MessageHandler core_handlers[] = + { GNUNET_MQ_hd_fixed_size (p2p_estimate, + GNUNET_MESSAGE_TYPE_NSE_P2P_FLOOD, + struct GNUNET_NSE_FloodMessage, + NULL), + GNUNET_MQ_handler_end () }; + char *proof; + struct GNUNET_CRYPTO_EddsaPrivateKey *pk; + + (void) cls; + (void) service; + cfg = c; + if (GNUNET_OK != GNUNET_CONFIGURATION_get_value_time (cfg, + "NSE", + "INTERVAL", + &gnunet_nse_interval)) + { + GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR, "NSE", "INTERVAL"); + GNUNET_SCHEDULER_shutdown (); + return; + } + if (GNUNET_OK != GNUNET_CONFIGURATION_get_value_time (cfg, + "NSE", + "WORKDELAY", + &proof_find_delay)) + { + GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR, "NSE", "WORKDELAY"); + GNUNET_SCHEDULER_shutdown (); + return; + } + if (GNUNET_OK != GNUNET_CONFIGURATION_get_value_number (cfg, + "NSE", + "WORKBITS", + &nse_work_required)) + { + GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR, "NSE", "WORKBITS"); + GNUNET_SCHEDULER_shutdown (); + return; + } + if (nse_work_required >= sizeof(struct GNUNET_HashCode) * 8) + { + GNUNET_log_config_invalid (GNUNET_ERROR_TYPE_ERROR, + "NSE", + "WORKBITS", + _ ("Value is too large.\n")); + GNUNET_SCHEDULER_shutdown (); + return; + } + +#if ENABLE_NSE_HISTOGRAM + { + char *histogram_dir; + char *histogram_fn; + + if (GNUNET_OK == GNUNET_CONFIGURATION_get_value_filename (cfg, + "NSE", + "HISTOGRAM_DIR", + &histogram_dir)) + { + GNUNET_assert ( + 0 < GNUNET_asprintf (&histogram_fn, "%s/timestamps", histogram_dir)); + GNUNET_free (histogram_dir); + histogram = GNUNET_BIO_write_open_file (histogram_fn); + if (NULL == histogram) + GNUNET_log (GNUNET_ERROR_TYPE_WARNING, + "Unable to open histogram file `%s'\n", + histogram_fn); + GNUNET_free (histogram_fn); + } + logger_test = GNUNET_CLIENT_service_test ("testbed-logger", + cfg, + GNUNET_TIME_UNIT_SECONDS, + &status_cb, + NULL); + } +#endif + + GNUNET_SCHEDULER_add_shutdown (&shutdown_task, NULL); + pk = GNUNET_CRYPTO_eddsa_key_create_from_configuration (cfg); + GNUNET_assert (NULL != pk); + my_private_key = pk; + GNUNET_CRYPTO_eddsa_key_get_public (my_private_key, &my_identity.public_key); + if (GNUNET_OK != + GNUNET_CONFIGURATION_get_value_filename (cfg, "NSE", "PROOFFILE", &proof)) + { + GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR, "NSE", "PROOFFILE"); + GNUNET_free (my_private_key); + my_private_key = NULL; + GNUNET_SCHEDULER_shutdown (); + return; + } + if ((GNUNET_YES != GNUNET_DISK_file_test (proof)) || + (sizeof(my_proof) != + GNUNET_DISK_fn_read (proof, &my_proof, sizeof(my_proof)))) + my_proof = 0; + GNUNET_free (proof); + proof_task = + GNUNET_SCHEDULER_add_with_priority (GNUNET_SCHEDULER_PRIORITY_IDLE, + &find_proof, + NULL); + + peers = GNUNET_CONTAINER_multipeermap_create (128, GNUNET_YES); + nc = GNUNET_notification_context_create (1); + /* Connect to core service and register core handlers */ + core_api = + GNUNET_CORE_connect (cfg, /* Main configuration */ + NULL, /* Closure passed to functions */ + &core_init, /* Call core_init once connected */ + &handle_core_connect, /* Handle connects */ + &handle_core_disconnect, /* Handle disconnects */ + core_handlers); /* Register these handlers */ + if (NULL == core_api) + { + GNUNET_SCHEDULER_shutdown (); + return; + } + stats = GNUNET_STATISTICS_create ("nse", 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) +{ + (void) cls; + (void) mq; + return c; +} + + +/** + * 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) +{ + (void) cls; + GNUNET_assert (c == internal_cls); +} + + +/** + * Define "main" method using service macro. + */ +GNUNET_SERVICE_MAIN ("nse", + GNUNET_SERVICE_OPTION_NONE, + &run, + &client_connect_cb, + &client_disconnect_cb, + NULL, + GNUNET_MQ_hd_fixed_size (start, + GNUNET_MESSAGE_TYPE_NSE_START, + struct GNUNET_MessageHeader, + NULL), + GNUNET_MQ_handler_end ()); + + +#if defined(__linux__) && defined(__GLIBC__) +#include + +/** + * 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-service-nse.c */ diff --git a/src/service/nse/hostkeys.dat b/src/service/nse/hostkeys.dat new file mode 100644 index 000000000..ab407c167 Binary files /dev/null and b/src/service/nse/hostkeys.dat differ diff --git a/src/service/nse/meson.build b/src/service/nse/meson.build new file mode 100644 index 000000000..7490214a3 --- /dev/null +++ b/src/service/nse/meson.build @@ -0,0 +1,47 @@ +libgnunetnse_src = ['nse_api.c'] + +gnunetservicense_src = ['gnunet-service-nse.c'] + +configure_file(input : 'nse.conf.in', + output : 'nse.conf', + configuration : cdata, + install: true, + install_dir: pkgcfgdir) + + +if get_option('monolith') + foreach p : libgnunetnse_src + gnunetservicense_src + gnunet_src += 'nse/' + p + endforeach + subdir_done() +endif + +libgnunetnse = library('gnunetnse', + libgnunetnse_src, + soversion: '0', + version: '0.0.0', + dependencies: libgnunetutil_dep, + include_directories: [incdir, configuration_inc], + install: true, + install_dir: get_option('libdir')) +libgnunetnse_dep = declare_dependency(link_with : libgnunetnse) +pkg.generate(libgnunetnse, url: 'https://www.gnunet.org', + description : 'Provides API for accessing the NSE service') + +executable ('gnunet-nse', + ['gnunet-nse.c'], + dependencies: [libgnunetnse_dep, m_dep, libgnunetutil_dep], + include_directories: [incdir, configuration_inc], + install: true, + install_dir: get_option('bindir')) + +executable ('gnunet-service-nse', + gnunetservicense_src, + dependencies: [libgnunetnse_dep, libgnunetutil_dep, + libgnunetcore_dep, + m_dep, + libgnunetstatistics_dep], + include_directories: [incdir, configuration_inc], + install: true, + install_dir: get_option('libdir') / 'gnunet' / 'libexec') + diff --git a/src/service/nse/nse.conf.in b/src/service/nse/nse.conf.in new file mode 100644 index 000000000..0ac5621aa --- /dev/null +++ b/src/service/nse/nse.conf.in @@ -0,0 +1,38 @@ +[nse] +START_ON_DEMAND = @START_ON_DEMAND@ +IMMEDIATE_START = YES +@JAVAPORT@PORT = 2097 +HOSTNAME = localhost +BINARY = gnunet-service-nse +ACCEPT_FROM = 127.0.0.1; +ACCEPT_FROM6 = ::1; +UNIXPATH = $GNUNET_RUNTIME_DIR/gnunet-service-nse.sock +UNIX_MATCH_UID = NO +UNIX_MATCH_GID = YES +PROOFFILE = $GNUNET_DATA_HOME/nse/proof.dat + +# The directory where the NSE services logs timestamps every time +# a size estime flooding message is received +# This option is only used for benchmarking, not in production. +HISTOGRAM_DIR = $GNUNET_CACHE_HOME/nse/histogram + +# How 'slowly' should the proof-of-work be constructed (delay +# between rounds); sane values between 0 and ~1000. +# It should rarely make sense to change this value. +# Only systems with slow CPUs where 5ms is a long time might +# want it to be reduced. +WORKDELAY = 5 ms + +# Note: changing any of the values below will make this peer +# completely incompatible with other peers! + +# How often do peers exchange network size messages? +# Note that all peers MUST use the same interval. +# DO NOT CHANGE THIS VALUE, doing so will break the protocol! +INTERVAL = 1 h + +# 2^22 hash operations take about 2-3h on a first-generation i7 (single-core) +# for SCRYPT; with 2ms/op and 5ms workdelay, we can expect +# the POW calculation to be done by a high-end peer in about 6h +# DO NOT CHANGE THIS VALUE, doing so will break the protocol! +WORKBITS = 15 diff --git a/src/service/nse/nse.h b/src/service/nse/nse.h new file mode 100644 index 000000000..5d1fccb7e --- /dev/null +++ b/src/service/nse/nse.h @@ -0,0 +1,73 @@ +/* + This file is part of GNUnet. + Copyright (C) 2001-2011 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 . + + SPDX-License-Identifier: AGPL3.0-or-later + */ + +/** + * @author Nathan Evans + * @file nse/nse.h + * + * @brief Common type definitions for the network size estimation + * service and API. + */ +#ifndef NSE_H +#define NSE_H + +#include "gnunet_common.h" + +GNUNET_NETWORK_STRUCT_BEGIN + +/** + * Network size estimate sent from the service + * to clients. Contains the current size estimate + * (or 0 if none has been calculated) and the + * standard deviation of known estimates. + * + */ +struct GNUNET_NSE_ClientMessage +{ + /** + * Type: GNUNET_MESSAGE_TYPE_NSE_ESTIMATE + */ + struct GNUNET_MessageHeader header; + + /** + * For alignment. + */ + uint32_t reserved GNUNET_PACKED; + + /** + * Timestamp at which the server received the message. + */ + struct GNUNET_TIME_AbsoluteNBO timestamp; + + /** + * The current estimated network size. + */ + double size_estimate GNUNET_PACKED; + + /** + * The standard deviation (rounded down + * to the nearest integer) of size + * estimations. + */ + double std_deviation GNUNET_PACKED; +}; +GNUNET_NETWORK_STRUCT_END + +#endif diff --git a/src/service/nse/nse_api.c b/src/service/nse/nse_api.c new file mode 100644 index 000000000..7f3e03b98 --- /dev/null +++ b/src/service/nse/nse_api.c @@ -0,0 +1,208 @@ +/* + This file is part of GNUnet. + Copyright (C) 2009, 2010, 2011, 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 . + + SPDX-License-Identifier: AGPL3.0-or-later + */ + +/** + * @file nse/nse_api.c + * @brief api to get information from the network size estimation service + * @author Nathan Evans + */ +#include "platform.h" +#include "gnunet_constants.h" +#include "gnunet_arm_service.h" +#include "gnunet_protocols.h" +#include "gnunet_util_lib.h" +#include "gnunet_nse_service.h" +#include "nse.h" + +#define LOG(kind, ...) GNUNET_log_from (kind, "nse-api", __VA_ARGS__) + +/** + * Handle for talking with the NSE service. + */ +struct GNUNET_NSE_Handle +{ + /** + * Configuration to use. + */ + const struct GNUNET_CONFIGURATION_Handle *cfg; + + /** + * Message queue (if available). + */ + struct GNUNET_MQ_Handle *mq; + + /** + * Task doing exponential back-off trying to reconnect. + */ + struct GNUNET_SCHEDULER_Task *reconnect_task; + + /** + * Time for next connect retry. + */ + struct GNUNET_TIME_Relative reconnect_delay; + + /** + * Callback function to call when message is received. + */ + GNUNET_NSE_Callback recv_cb; + + /** + * Closure to pass to @e recv_cb callback. + */ + void *recv_cb_cls; +}; + + +/** + * Try again to connect to network size estimation service. + * + * @param cls closure with the `struct GNUNET_NSE_Handle *` + */ +static void +reconnect (void *cls); + + +/** + * Generic error handler, called with the appropriate + * error code and the same closure specified at the creation of + * the message queue. + * Not every message queue implementation supports an error handler. + * + * @param cls closure with the `struct GNUNET_NSE_Handle *` + * @param error error code + */ +static void +mq_error_handler (void *cls, enum GNUNET_MQ_Error error) +{ + struct GNUNET_NSE_Handle *h = cls; + + (void) error; + GNUNET_MQ_destroy (h->mq); + h->mq = NULL; + h->reconnect_task = + GNUNET_SCHEDULER_add_delayed (h->reconnect_delay, &reconnect, h); + h->reconnect_delay = GNUNET_TIME_STD_BACKOFF (h->reconnect_delay); +} + + +/** + * Type of a function to call when we receive a message + * from the service. + * + * @param cls closure + * @param client_msg message received + */ +static void +handle_estimate (void *cls, const struct GNUNET_NSE_ClientMessage *client_msg) +{ + struct GNUNET_NSE_Handle *h = cls; + + h->reconnect_delay = GNUNET_TIME_UNIT_ZERO; + h->recv_cb (h->recv_cb_cls, + GNUNET_TIME_absolute_ntoh (client_msg->timestamp), + GNUNET_ntoh_double (client_msg->size_estimate), + GNUNET_ntoh_double (client_msg->std_deviation)); +} + + +/** + * Try again to connect to network size estimation service. + * + * @param cls the `struct GNUNET_NSE_Handle *` + */ +static void +reconnect (void *cls) +{ + struct GNUNET_NSE_Handle *h = cls; + struct GNUNET_MQ_MessageHandler handlers[] = + { GNUNET_MQ_hd_fixed_size (estimate, + GNUNET_MESSAGE_TYPE_NSE_ESTIMATE, + struct GNUNET_NSE_ClientMessage, + h), + GNUNET_MQ_handler_end () }; + struct GNUNET_MessageHeader *msg; + struct GNUNET_MQ_Envelope *env; + + h->reconnect_task = NULL; + LOG (GNUNET_ERROR_TYPE_DEBUG, + "Connecting to network size estimation service.\n"); + GNUNET_assert (NULL == h->mq); + h->mq = GNUNET_CLIENT_connect (h->cfg, "nse", handlers, &mq_error_handler, h); + if (NULL == h->mq) + return; + env = GNUNET_MQ_msg (msg, GNUNET_MESSAGE_TYPE_NSE_START); + GNUNET_MQ_send (h->mq, env); +} + + +/** + * Connect to the network size estimation service. + * + * @param cfg the configuration to use + * @param func function to call with network size estimate + * @param func_cls closure to pass to @a func + * @return handle to use + */ +struct GNUNET_NSE_Handle * +GNUNET_NSE_connect (const struct GNUNET_CONFIGURATION_Handle *cfg, + GNUNET_NSE_Callback func, + void *func_cls) +{ + struct GNUNET_NSE_Handle *h; + + GNUNET_assert (NULL != func); + h = GNUNET_new (struct GNUNET_NSE_Handle); + h->cfg = cfg; + h->recv_cb = func; + h->recv_cb_cls = func_cls; + h->reconnect_delay = GNUNET_TIME_UNIT_ZERO; + reconnect (h); + if (NULL == h->mq) + { + GNUNET_free (h); + return NULL; + } + return h; +} + + +/** + * Disconnect from network size estimation service + * + * @param h handle to destroy + */ +void +GNUNET_NSE_disconnect (struct GNUNET_NSE_Handle *h) +{ + if (NULL != h->reconnect_task) + { + GNUNET_SCHEDULER_cancel (h->reconnect_task); + h->reconnect_task = NULL; + } + if (NULL != h->mq) + { + GNUNET_MQ_destroy (h->mq); + h->mq = NULL; + } + GNUNET_free (h); +} + + +/* end of nse_api.c */ diff --git a/src/service/nse/nse_infiniband.conf b/src/service/nse/nse_infiniband.conf new file mode 100644 index 000000000..d2c97567d --- /dev/null +++ b/src/service/nse/nse_infiniband.conf @@ -0,0 +1,77 @@ +[PATHS] +GNUNET_TEST_HOME = $GNUNET_TMP/nse-profiler/ + +[testbed] +START_ON_DEMAND = NO +ORT = 12113 +ACCEPT_FROM = 127.0.0.1; 10.6.0.0/16; 192.168.0.0/16; +HOSTNAME = localhost +MAX_PARALLEL_OPERATIONS = 400 +OVERLAY_TOPOLOGY = 2D_TORUS +OVERLAY_RANDOM_LINKS = 1000 +OPERATION_TIMEOUT = 45 s +SETUP_TIMEOUT = 30m +STATS_DIR= /home/totakura/nse/test/load + +[nse] +IMMEDIATE_START = YES +# Overriding network settings for faster testing (do NOT use +# these values in production just because they are here) +WORKDELAY = 60 s +INTERVAL = 10 s +WORKBITS = 0 +PROOFFILE = $GNUNET_TEST_HOME/nse.proof +HISTOGRAM_DIR = /home/totakura/nse/test/histograms + +[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 + +[transport] +plugins = udp + +[transport-udp] +PORT = 12116 + +[nse-profiler] +OUTPUT_FILE = nse_output_2000_peers.dat +TOPOLOGY_OUTPUT_FILE = nse_topo_2000_peers +DATA_OUTPUT_FILE = nse_stats_2000_peers +ROUND0 = 1000 +#ROUND1 = 2000 +ROUND2 = 2000 +ROUND3 = 2000 +ROUND4 = 2000 +ROUND5 = 2000 +ROUND6 = 2000 +ROUND7 = 2000 +ROUND8 = 2000 +ROUND9 = 2000 +ROUND10 = 2000 +ROUND11 = 1000 +ROUND12 = 1000 +ROUND13 = 1000 +ROUND14 = 1000 +ROUND15 = 1000 +ROUND16 = 1000 +ROUND17 = 1000 +ROUND18 = 1000 +ROUND19 = 1000 +ROUND20 = 1000 +ROUND21 = 2000 +ROUND22 = 2000 +ROUND23 = 2000 +ROUND24 = 2000 +ROUND25 = 2000 +ROUND26 = 2000 +ROUND27 = 2000 +ROUND28 = 2000 +ROUND29 = 2000 +ROUND30 = 2000 +WAIT_TIME = 1920 s +CONNECTION_LIMIT = 10 diff --git a/src/service/nse/nse_profiler_test.conf b/src/service/nse/nse_profiler_test.conf new file mode 100644 index 000000000..f702faae2 --- /dev/null +++ b/src/service/nse/nse_profiler_test.conf @@ -0,0 +1,72 @@ +[PATHS] +GNUNET_TEST_HOME = $GNUNET_TMP/nse-profiler/ + +[testbed] +START_ON_DEMAND = NO +PORT = 12113 +ACCEPT_FROM = 127.0.0.1; 10.6.0.0/16; +HOSTNAME = localhost +MAX_PARALLEL_TOPOLOGY_CONFIG_OPERATIONS = 5 +OVERLAY_TOPOLOGY = RANDOM +OVERLAY_RANDOM_LINKS = 1000 +OPERATION_TIMEOUT = 45 s + +[nse] +IMMEDIATE_START = YES +START_ON_DEMAND = NO +# Overriding network settings for faster testing (do NOT use +# these values in production just because they are here) +WORKDELAY = 60 s +INTERVAL = 10 s +WORKBITS = 0 +PROOFFILE = $GNUNET_TEST_HOME/nse.proof + +[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 + +[transport] +plugins = udp + +[nse-profiler] +OUTPUT_FILE = nse_output_2000_peers.dat +TOPOLOGY_OUTPUT_FILE = nse_topo_2000_peers +DATA_OUTPUT_FILE = nse_stats_2000_peers +ROUND0 = 1000 +#ROUND1 = 2000 +ROUND2 = 2000 +ROUND3 = 2000 +ROUND4 = 2000 +ROUND5 = 2000 +ROUND6 = 2000 +ROUND7 = 2000 +ROUND8 = 2000 +ROUND9 = 2000 +ROUND10 = 2000 +ROUND11 = 1000 +ROUND12 = 1000 +ROUND13 = 1000 +ROUND14 = 1000 +ROUND15 = 1000 +ROUND16 = 1000 +ROUND17 = 1000 +ROUND18 = 1000 +ROUND19 = 1000 +ROUND20 = 1000 +ROUND21 = 2000 +ROUND22 = 2000 +ROUND23 = 2000 +ROUND24 = 2000 +ROUND25 = 2000 +ROUND26 = 2000 +ROUND27 = 2000 +ROUND28 = 2000 +ROUND29 = 2000 +ROUND30 = 2000 +WAIT_TIME = 1920 s +CONNECTION_LIMIT = 10 diff --git a/src/service/nse/perf_kdf.c b/src/service/nse/perf_kdf.c new file mode 100644 index 000000000..10207675f --- /dev/null +++ b/src/service/nse/perf_kdf.c @@ -0,0 +1,67 @@ +/* + This file is part of GNUnet. + Copyright (C) 2002, 2003, 2004, 2006, 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 . + + SPDX-License-Identifier: AGPL3.0-or-later + */ + +/** + * @author Christian Grothoff + * @file nse/perf_kdf.c + * @brief measure performance of KDF hash function + */ +#include "platform.h" +#include "gnunet_util_lib.h" +#include +#include + + +static void +perfHash () +{ + struct GNUNET_HashCode hc; + char buf[64]; + struct GNUNET_CRYPTO_PowSalt salt = { "gnunet-nse-proof" }; + + memset (buf, 1, sizeof(buf)); + for (unsigned int i = 0; i < 1024; i++) + GNUNET_CRYPTO_pow_hash (&salt, + buf, + sizeof(buf), + &hc); +} + + +int +main (int argc, char *argv[]) +{ + struct GNUNET_TIME_Absolute start; + + start = GNUNET_TIME_absolute_get (); + perfHash (); + printf ("Hash perf took %s\n", + GNUNET_STRINGS_relative_time_to_string ( + GNUNET_TIME_absolute_get_duration (start), + GNUNET_YES)); + GAUGER ("NSE", "Proof-of-work hashing", + 1024.0 / (1.0 + + GNUNET_TIME_absolute_get_duration + (start).rel_value_us / 1000.0), "hashes/ms"); + return 0; +} + + +/* end of perf_kdf.c */ diff --git a/src/service/nse/test_nse.conf b/src/service/nse/test_nse.conf new file mode 100644 index 000000000..6b5aaff49 --- /dev/null +++ b/src/service/nse/test_nse.conf @@ -0,0 +1,26 @@ +@INLINE@ ../../contrib/conf/gnunet/no_forcestart.conf +@INLINE@ ../../contrib/conf/gnunet/no_autostart_above_core.conf + +[PATHS] +GNUNET_TEST_HOME = $GNUNET_TMP/test-nse-multipeer/ + +[nse] +#PREFIX = valgrind --leak-check=full --log-file=valgrind_nse.%p +IMMEDIATE_START = YES +PROOFFILE = $GNUNET_TEST_HOME/proof.nse +# Overriding network settings for faster testing (do NOT use +# these values in production just because they are here) +WORKDELAY = 1 ms +INTERVAL = 60 s +WORKBITS = 1 +HISTOGRAM = $GNUNET_TEST_HOME/nse-histogram + +[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/service/nse/test_nse_api.c b/src/service/nse/test_nse_api.c new file mode 100644 index 000000000..f1b7c652b --- /dev/null +++ b/src/service/nse/test_nse_api.c @@ -0,0 +1,107 @@ +/* + This file is part of GNUnet. + Copyright (C) 2011 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 . + + SPDX-License-Identifier: AGPL3.0-or-later + */ +/** + * @file nse/test_nse_api.c + * @brief testcase for nse_api.c + */ +#include "platform.h" +#include "gnunet_util_lib.h" +#include "gnunet_nse_service.h" +#include "gnunet_testing_lib.h" + + +static struct GNUNET_NSE_Handle *h; + +static struct GNUNET_SCHEDULER_Task *die_task; + + +/** + * Signature of the main function of a task. + * + * @param cls closure + */ +static void +end_test (void *cls) +{ + if (h != NULL) + { + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Disconnecting from NSE service.\n"); + GNUNET_NSE_disconnect (h); + } +} + + +/** + * Callback to call when network size estimate is updated. + * + * @param cls unused + * @param timestamp time when the estimate was received from the server (or created by the server) + * @param estimate the value of the current network size estimate + * @param std_dev standard deviation (rounded down to nearest integer) + * of the size estimation values seen + * + */ +static void +check_nse_message (void *cls, struct GNUNET_TIME_Absolute timestamp, + double estimate, double std_dev) +{ + int *ok = cls; + + fprintf (stderr, + "Received NSE message, estimate %f, standard deviation %f.\n", + estimate, std_dev); + /* Fantastic check below. Expect NaN, the only thing not equal to itself. */ + (*ok) = 0; + if (die_task != NULL) + GNUNET_SCHEDULER_cancel (die_task); + die_task = GNUNET_SCHEDULER_add_now (&end_test, NULL); +} + + +static void +run (void *cls, + const struct GNUNET_CONFIGURATION_Handle *cfg, + struct GNUNET_TESTING_Peer *peer) +{ + die_task = + GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_relative_multiply + (GNUNET_TIME_UNIT_MINUTES, 1), &end_test, + NULL); + + h = GNUNET_NSE_connect (cfg, &check_nse_message, cls); + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Connecting to NSE service.\n"); + GNUNET_assert (h != NULL); +} + + +int +main (int argc, char *argv[]) +{ + int ok = 1; + + if (0 != GNUNET_TESTING_peer_run ("test_nse_api", + "test_nse.conf", + &run, &ok)) + return 1; + return ok; +} + + +/* end of test_nse_api.c */ diff --git a/src/service/nse/test_nse_multipeer.c b/src/service/nse/test_nse_multipeer.c new file mode 100644 index 000000000..6ee03b3fa --- /dev/null +++ b/src/service/nse/test_nse_multipeer.c @@ -0,0 +1,235 @@ +/* + This file is part of GNUnet. + Copyright (C) 2009, 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 . + + SPDX-License-Identifier: AGPL3.0-or-later + */ +/** + * @file nse/test_nse_multipeer.c + * @brief Testcase for the network size estimation service. Starts + * a peergroup with a given number of peers, then waits to + * receive size estimates from each peer. Expects to wait + * for one message from each peer. + */ +#include "platform.h" +#include "gnunet_testbed_service.h" +#include "gnunet_nse_service.h" + + +/** + * How many peers do we start? + */ +#define NUM_PEERS 4 + +/** + * How long do we run the test? + */ +#define TIMEOUT GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS, 300) + + +/** + * Information we track for each peer. + */ +struct NSEPeer +{ + /** + * Handle for NSE connect operation. + */ + struct GNUNET_TESTBED_Operation *op; + + /** + * Handle to NSE service. + */ + struct GNUNET_NSE_Handle *nse_handle; +}; + + +/** + * Information for all the peers. + */ +static struct NSEPeer nse_peers[NUM_PEERS]; + +/** + * Return value from 'main'. + */ +static int ok; + + +/** + * Task run on timeout to shut everything down. + */ +static void +shutdown_task (void *cls) +{ + unsigned int i; + + for (i = 0; i < NUM_PEERS; i++) + GNUNET_TESTBED_operation_done (nse_peers[i].op); + GNUNET_SCHEDULER_shutdown (); +} + + +/** + * Callback to call when network size estimate is updated. + * + * @param cls closure + * @param timestamp server timestamp + * @param estimate the value of the current network size estimate + * @param std_dev standard deviation (rounded down to nearest integer) + * of the size estimation values seen + * + */ +static void +handle_estimate (void *cls, struct GNUNET_TIME_Absolute timestamp, + double estimate, double std_dev) +{ + struct NSEPeer *peer = cls; + + fprintf (stderr, + "Received network size estimate from peer %u. logSize: %f std.dev. %f (%f/%u)\n", + (unsigned int) (peer - nse_peers), + estimate, std_dev, + GNUNET_NSE_log_estimate_to_n (estimate), + NUM_PEERS); + ok = 0; +} + + +/** + * Callback to be called when NSE service connect operation is completed + * + * @param cls the callback closure from functions generating an operation + * @param op the operation that has been finished + * @param ca_result the NSE service handle returned from nse_connect_adapter + * @param emsg error message in case the operation has failed; will be NULL if + * operation has executed successfully. + */ +static void +nse_connect_complete_cb (void *cls, + struct GNUNET_TESTBED_Operation *op, + void *ca_result, + const char *emsg) +{ + struct NSEPeer *peer = cls; + struct GNUNET_NSE_Handle *nse = ca_result; + + GNUNET_assert (op == peer->op); + if (NULL != emsg) + { + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "Failed to connect to NSE service: %s\n", + emsg); + ok = 1; + GNUNET_SCHEDULER_shutdown (); + return; + } + peer->nse_handle = nse; +} + + +/** + * Adapter function called to establish a connection to + * the NSE 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 * +nse_connect_adapter (void *cls, + const struct GNUNET_CONFIGURATION_Handle *cfg) +{ + return GNUNET_NSE_connect (cfg, + &handle_estimate, + cls); +} + + +/** + * Adapter function called to destroy connection to + * NSE service. + * + * @param cls closure + * @param op_result service handle returned from the connect adapter + */ +static void +nse_disconnect_adapter (void *cls, + void *op_result) +{ + GNUNET_NSE_disconnect (op_result); +} + + +/** + * Actual "main" function for the testcase. + * + * @param cls closure + * @param h the run handle + * @param num_peers number of peers in 'peers' + * @param peers handle to peers run in the testbed + * @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 +run (void *cls, + struct GNUNET_TESTBED_RunHandle *h, + unsigned int num_peers, + struct GNUNET_TESTBED_Peer **peers, + unsigned int links_succeeded, + unsigned int links_failed) +{ + unsigned int i; + + GNUNET_assert (NUM_PEERS == num_peers); + for (i = 0; i < num_peers; i++) + nse_peers[i].op = GNUNET_TESTBED_service_connect (&nse_peers[i], + peers[i], + "nse", + &nse_connect_complete_cb, + &nse_peers[i], + &nse_connect_adapter, + &nse_disconnect_adapter, + &nse_peers[i]); + GNUNET_SCHEDULER_add_delayed (TIMEOUT, + &shutdown_task, NULL); +} + + +/** + * Entry point for the testcase, sets up the testbed. + * + * @param argc unused + * @param argv unused + * @return 0 on success + */ +int +main (int argc, char *argv[]) +{ + ok = 1; + (void) GNUNET_TESTBED_test_run ("test-nse-multipeer", + "test_nse.conf", + NUM_PEERS, + 0, NULL, NULL, + &run, NULL); + return ok; +} + + +/* end of test_nse_multipeer.c */ diff --git a/src/service/peerstore/.gitignore b/src/service/peerstore/.gitignore new file mode 100644 index 000000000..7fc22bbdb --- /dev/null +++ b/src/service/peerstore/.gitignore @@ -0,0 +1,9 @@ +gnunet-service-peerstore +gnunet-peerstore +perf_peerstore_store +test_peerstore_api_iterate +test_peerstore_api_store +test_peerstore_api_sync +test_peerstore_api_watch +test_plugin_peerstore_sqlite +test_plugin_peerstore_flat diff --git a/src/service/peerstore/Makefile.am b/src/service/peerstore/Makefile.am new file mode 100644 index 000000000..28948b7db --- /dev/null +++ b/src/service/peerstore/Makefile.am @@ -0,0 +1,150 @@ +# This Makefile.am is in the public domain +AM_CPPFLAGS = -I$(top_srcdir)/src/include + +plugindir = $(libdir)/gnunet + +pkgcfgdir= $(pkgdatadir)/config.d/ + +libexecdir= $(pkglibdir)/libexec/ + +dist_pkgcfg_DATA = \ + peerstore.conf + +if USE_COVERAGE + AM_CFLAGS = -fprofile-arcs -ftest-coverage +endif + +# This program does not do anything. +noinst_PROGRAMS = \ + gnunet-peerstore + +libexec_PROGRAMS = \ + gnunet-service-peerstore + +lib_LTLIBRARIES = \ + libgnunetpeerstore.la + +gnunet_peerstore_SOURCES = \ + gnunet-peerstore.c +gnunet_peerstore_LDADD = \ + $(top_builddir)/src/lib/util/libgnunetutil.la \ + libgnunetpeerstore.la \ + $(GN_LIBINTL) + +gnunet_service_peerstore_SOURCES = \ + gnunet-service-peerstore.c \ + peerstore_common.c peerstore_common.h \ + peerstore.h +gnunet_service_peerstore_CFLAGS = $(AM_CFLAGS) +gnunet_service_peerstore_LDADD = \ + $(top_builddir)/src/lib/util/libgnunetutil.la \ + $(top_builddir)/src/lib/hello/libgnunethello.la \ + $(GN_LIBINTL) + +libgnunetpeerstore_la_SOURCES = \ + peerstore_api.c \ + peerstore_common.c +libgnunetpeerstore_la_LIBADD = \ + $(top_builddir)/src/lib/util/libgnunetutil.la \ + $(top_builddir)/src/lib/hello/libgnunethello.la +libgnunetpeerstore_la_LDFLAGS = \ + $(GN_LIBINTL) \ + $(GN_LIB_LDFLAGS) + +if HAVE_EXPERIMENTAL +FLAT_PLUGIN = libgnunet_plugin_peerstore_flat.la +FLAT_TESTS = test_plugin_peerstore_flat +libgnunet_plugin_peerstore_flat_la_SOURCES = \ + plugin_peerstore_flat.c +libgnunet_plugin_peerstore_flat_la_LIBADD = \ + libgnunetpeerstore.la \ + $(top_builddir)/src/lib/util/libgnunetutil.la $(XLIBS) \ + $(LTLIBINTL) +libgnunet_plugin_peerstore_flat_la_LDFLAGS = \ + $(GN_PLUGIN_LDFLAGS) +endif + +if HAVE_SQLITE +SQLITE_PLUGIN = libgnunet_plugin_peerstore_sqlite.la +SQLITE_TESTS = test_plugin_peerstore_sqlite +libgnunet_plugin_peerstore_sqlite_la_SOURCES = \ + plugin_peerstore_sqlite.c +libgnunet_plugin_peerstore_sqlite_la_LIBADD = \ + libgnunetpeerstore.la \ + $(top_builddir)/src/lib/sq/libgnunetsq.la \ + $(top_builddir)/src/lib/util/libgnunetutil.la \ + $(XLIBS) -lsqlite3 \ + $(LTLIBINTL) +libgnunet_plugin_peerstore_sqlite_la_LDFLAGS = \ + $(GN_PLUGIN_LDFLAGS) +endif + +plugin_LTLIBRARIES = \ + $(SQLITE_PLUGIN) \ + $(FLAT_PLUGIN) + +test_plugin_peerstore_sqlite_SOURCES = \ + test_plugin_peerstore.c +test_plugin_peerstore_sqlite_LDADD = \ + $(top_builddir)/src/service/testing/libgnunettesting.la \ + $(top_builddir)/src/lib/util/libgnunetutil.la + +test_plugin_peerstore_flat_SOURCES = \ + test_plugin_peerstore.c +test_plugin_peerstore_flat_LDADD = \ + $(top_builddir)/src/service/testing/libgnunettesting.la \ + $(top_builddir)/src/lib/util/libgnunetutil.la + +check_PROGRAMS = \ + test_peerstore_api_store \ + test_peerstore_api_iterate \ + test_peerstore_api_watch \ + test_peerstore_api_sync \ + perf_peerstore_store \ + $(SQLITE_TESTS) \ + $(FLAT_TESTS) + +EXTRA_DIST = \ + test_peerstore_api_data.conf \ + test_plugin_peerstore_flat.conf \ + test_plugin_peerstore_sqlite.conf + +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_peerstore_api_store_SOURCES = \ + test_peerstore_api_store.c +test_peerstore_api_store_LDADD = \ + libgnunetpeerstore.la \ + $(top_builddir)/src/service/testing/libgnunettesting.la \ + $(top_builddir)/src/lib/util/libgnunetutil.la + +test_peerstore_api_iterate_SOURCES = \ + test_peerstore_api_iterate.c +test_peerstore_api_iterate_LDADD = \ + libgnunetpeerstore.la \ + $(top_builddir)/src/service/testing/libgnunettesting.la \ + $(top_builddir)/src/lib/util/libgnunetutil.la + +test_peerstore_api_watch_SOURCES = \ + test_peerstore_api_watch.c +test_peerstore_api_watch_LDADD = \ + libgnunetpeerstore.la \ + $(top_builddir)/src/service/testing/libgnunettesting.la \ + $(top_builddir)/src/lib/util/libgnunetutil.la + +test_peerstore_api_sync_SOURCES = \ + test_peerstore_api_sync.c +test_peerstore_api_sync_LDADD = \ + libgnunetpeerstore.la \ + $(top_builddir)/src/service/testing/libgnunettesting.la \ + $(top_builddir)/src/lib/util/libgnunetutil.la + +perf_peerstore_store_SOURCES = \ + perf_peerstore_store.c +perf_peerstore_store_LDADD = \ + libgnunetpeerstore.la \ + $(top_builddir)/src/service/testing/libgnunettesting.la \ + $(top_builddir)/src/lib/util/libgnunetutil.la diff --git a/src/service/peerstore/gnunet-peerstore.c b/src/service/peerstore/gnunet-peerstore.c new file mode 100644 index 000000000..11313b5d3 --- /dev/null +++ b/src/service/peerstore/gnunet-peerstore.c @@ -0,0 +1,97 @@ +/* + This file is part of GNUnet. + Copyright (C) + + 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 . + + SPDX-License-Identifier: AGPL3.0-or-later + */ + +/** + * @file peerstore/gnunet-peerstore.c + * @brief peerstore tool + * @author Omar Tarabai + */ +#include "platform.h" +#include "gnunet_util_lib.h" +#include "gnunet_peerstore_service.h" + +static int ret; + +/* + * Handle to PEERSTORE service + */ +static struct GNUNET_PEERSTORE_Handle *peerstore_handle; + + +/** + * Run on shutdown + * + * @param cls unused + */ +static void +shutdown_task (void *cls) +{ + if (NULL != peerstore_handle) + { + GNUNET_PEERSTORE_disconnect (peerstore_handle, GNUNET_YES); + peerstore_handle = NULL; + } +} + + +/** + * 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, + const char *cfgfile, + const struct GNUNET_CONFIGURATION_Handle *cfg) +{ + GNUNET_SCHEDULER_add_shutdown (&shutdown_task, + NULL); + peerstore_handle = GNUNET_PEERSTORE_connect (cfg); + GNUNET_assert (NULL != peerstore_handle); + ret = 0; +} + + +/** + * The main function to peerstore. + * + * @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 + }; + + return (GNUNET_OK == + GNUNET_PROGRAM_run (argc, argv, "gnunet-peerstore [options [value]]", + gettext_noop ("peerstore"), options, &run, + NULL)) ? ret : 1; +} + + +/* end of gnunet-peerstore.c */ diff --git a/src/service/peerstore/gnunet-service-peerstore.c b/src/service/peerstore/gnunet-service-peerstore.c new file mode 100644 index 000000000..514b173bb --- /dev/null +++ b/src/service/peerstore/gnunet-service-peerstore.c @@ -0,0 +1,724 @@ +/* + This file is part of GNUnet. + Copyright (C) 2014, 2015, 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 . + + SPDX-License-Identifier: AGPL3.0-or-later + */ + +/** + * @file peerstore/gnunet-service-peerstore.c + * @brief peerstore service implementation + * @author Omar Tarabai + */ +#include "platform.h" +#include "gnunet_util_lib.h" +#include "peerstore.h" +#include "gnunet_peerstore_plugin.h" +#include "peerstore_common.h" +#include "gnunet_hello_uri_lib.h" + + +/** + * Interval for expired records cleanup (in seconds) + */ +#define EXPIRED_RECORDS_CLEANUP_INTERVAL 300 /* 5mins */ + +/** + * Our configuration. + */ +static const struct GNUNET_CONFIGURATION_Handle *cfg; + +/** + * Database plugin library name + */ +static char *db_lib_name; + +/** + * Database handle + */ +static struct GNUNET_PEERSTORE_PluginFunctions *db; + +/** + * Hashmap with all watch requests + */ +static struct GNUNET_CONTAINER_MultiHashMap *watchers; + +/** + * Task run to clean up expired records. + */ +static struct GNUNET_SCHEDULER_Task *expire_task; + +/** + * Are we in the process of shutting down the service? #GNUNET_YES / #GNUNET_NO + */ +static int in_shutdown; + +/** + * Number of connected clients. + */ +static unsigned int num_clients; + + +/** + * Perform the actual shutdown operations + */ +static void +do_shutdown () +{ + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Shutting down peerstore, bye.\n"); + if (NULL != db_lib_name) + { + GNUNET_break (NULL == GNUNET_PLUGIN_unload (db_lib_name, db)); + GNUNET_free (db_lib_name); + db_lib_name = NULL; + } + if (NULL != watchers) + { + GNUNET_CONTAINER_multihashmap_destroy (watchers); + watchers = NULL; + } + if (NULL != expire_task) + { + GNUNET_SCHEDULER_cancel (expire_task); + expire_task = NULL; + } + GNUNET_SCHEDULER_shutdown (); +} + + +/** + * Task run during shutdown. + * + * @param cls unused + */ +static void +shutdown_task (void *cls) +{ + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Priming PEERSTORE for shutdown.\n"); + in_shutdown = GNUNET_YES; + if (0 == num_clients) /* Only when no connected clients. */ + do_shutdown (); +} + + +/* Forward declaration */ +static void +expire_records_continuation (void *cls, int success); + + +/** + * Deletes any expired records from storage + */ +static void +cleanup_expired_records (void *cls) +{ + int ret; + + expire_task = NULL; + GNUNET_assert (NULL != db); + ret = db->expire_records (db->cls, + GNUNET_TIME_absolute_get (), + &expire_records_continuation, + NULL); + if (GNUNET_OK != ret) + { + GNUNET_assert (NULL == expire_task); + expire_task = GNUNET_SCHEDULER_add_delayed ( + GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS, + EXPIRED_RECORDS_CLEANUP_INTERVAL), + &cleanup_expired_records, + NULL); + } +} + + +/** + * Continuation to expire_records called by the peerstore plugin + * + * @param cls unused + * @param success count of records deleted or #GNUNET_SYSERR + */ +static void +expire_records_continuation (void *cls, int success) +{ + if (success > 0) + GNUNET_log (GNUNET_ERROR_TYPE_INFO, "%d records expired.\n", success); + GNUNET_assert (NULL == expire_task); + expire_task = GNUNET_SCHEDULER_add_delayed ( + GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS, + EXPIRED_RECORDS_CLEANUP_INTERVAL), + &cleanup_expired_records, + NULL); +} + + +/** + * A client disconnected. Remove all of its data structure entries. + * + * @param cls closure, NULL + * @param client identification of the client + * @param mq the message queue + * @return + */ +static void * +client_connect_cb (void *cls, + struct GNUNET_SERVICE_Client *client, + struct GNUNET_MQ_Handle *mq) +{ + num_clients++; + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "A client connected (now %u)\n", num_clients); + return client; +} + + +/** + * Search for a disconnected client and remove it + * + * @param cls closuer, a `struct GNUNET_SERVICE_Client` + * @param key hash of record key + * @param value the watcher client, a `struct GNUNET_SERVICE_Client *` + * @return #GNUNET_OK to continue iterating + */ +static int +client_disconnect_it (void *cls, const struct GNUNET_HashCode *key, void *value) +{ + if (value == cls) + { + GNUNET_assert (GNUNET_YES == + GNUNET_CONTAINER_multihashmap_remove (watchers, key, value)); + num_clients++; /* Watchers do not count */ + } + return GNUNET_OK; +} + + +/** + * A client disconnected. Remove all of its data structure entries. + * + * @param cls closure, NULL + * @param client identification of the client + */ +static void +client_disconnect_cb (void *cls, + struct GNUNET_SERVICE_Client *client, + void *app_cls) +{ + num_clients--; + if (NULL != watchers) + GNUNET_CONTAINER_multihashmap_iterate (watchers, + &client_disconnect_it, + client); + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "A client disconnected (%u remaining).\n", + num_clients); + if ((0 == num_clients) && in_shutdown) + do_shutdown (); +} + + +/** + * Function called by for each matching record. + * + * @param cls closure + * @param record peerstore record found + * @param emsg error message or NULL if no errors + * @return #GNUNET_YES to continue iteration + */ +static void +record_iterator (void *cls, + const struct GNUNET_PEERSTORE_Record *record, + const char *emsg) +{ + struct GNUNET_PEERSTORE_Record *cls_record = cls; + struct GNUNET_MQ_Envelope *env; + + if (NULL == record) + { + /* No more records */ + struct GNUNET_MessageHeader *endmsg; + + env = GNUNET_MQ_msg (endmsg, GNUNET_MESSAGE_TYPE_PEERSTORE_ITERATE_END); + GNUNET_MQ_send (GNUNET_SERVICE_client_get_mq (cls_record->client), env); + if (NULL == emsg) + { + GNUNET_SERVICE_client_continue (cls_record->client); + } + else + { + GNUNET_break (0); + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Failed to iterate: %s\n", emsg); + GNUNET_SERVICE_client_drop (cls_record->client); + } + PEERSTORE_destroy_record (cls_record); + return; + } + + env = PEERSTORE_create_record_mq_envelope ( + record->sub_system, + &record->peer, + record->key, + record->value, + record->value_size, + record->expiry, + 0, + GNUNET_MESSAGE_TYPE_PEERSTORE_ITERATE_RECORD); + GNUNET_MQ_send (GNUNET_SERVICE_client_get_mq (cls_record->client), env); +} + + +/** + * Iterator over all watcher clients + * to notify them of a new record + * + * @param cls closure, a `struct GNUNET_PEERSTORE_Record *` + * @param key hash of record key + * @param value the watcher client, a `struct GNUNET_SERVICE_Client *` + * @return #GNUNET_YES to continue iterating + */ +static int +watch_notifier_it (void *cls, const struct GNUNET_HashCode *key, void *value) +{ + struct GNUNET_PEERSTORE_Record *record = cls; + struct GNUNET_SERVICE_Client *client = value; + struct GNUNET_MQ_Envelope *env; + + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Found a watcher to update.\n"); + env = PEERSTORE_create_record_mq_envelope ( + record->sub_system, + &record->peer, + record->key, + record->value, + record->value_size, + record->expiry, + 0, + GNUNET_MESSAGE_TYPE_PEERSTORE_WATCH_RECORD); + GNUNET_MQ_send (GNUNET_SERVICE_client_get_mq (client), env); + return GNUNET_YES; +} + + +/** + * Given a new record, notifies watchers + * + * @param record changed record to update watchers with + */ +static void +watch_notifier (struct GNUNET_PEERSTORE_Record *record) +{ + struct GNUNET_HashCode keyhash; + + PEERSTORE_hash_key (record->sub_system, &record->peer, record->key, &keyhash); + GNUNET_CONTAINER_multihashmap_get_multiple (watchers, + &keyhash, + &watch_notifier_it, + record); +} + + +/** + * Handle a watch cancel request from client + * + * @param cls identification of the client + * @param hm the actual message + */ +static void +handle_watch_cancel (void *cls, const struct StoreKeyHashMessage *hm) +{ + struct GNUNET_SERVICE_Client *client = cls; + + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Received a watch cancel request.\n"); + if (GNUNET_OK != + GNUNET_CONTAINER_multihashmap_remove (watchers, &hm->keyhash, client)) + { + GNUNET_break (0); + GNUNET_SERVICE_client_drop (client); + return; + } + num_clients++; + GNUNET_SERVICE_client_continue (client); +} + + +/** + * Handle a watch request from client + * + * @param cls identification of the client + * @param hm the actual message + */ +static void +handle_watch (void *cls, const struct StoreKeyHashMessage *hm) +{ + struct GNUNET_SERVICE_Client *client = cls; + + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Received a watch request.\n"); + num_clients--; /* do not count watchers */ + GNUNET_SERVICE_client_mark_monitor (client); + GNUNET_CONTAINER_multihashmap_put (watchers, + &hm->keyhash, + client, + GNUNET_CONTAINER_MULTIHASHMAPOPTION_MULTIPLE); + GNUNET_SERVICE_client_continue (client); +} + + +/** + * Check an iterate request from client + * + * @param cls client identification of the client + * @param srm the actual message + * @return #GNUNET_OK if @a srm is well-formed + */ +static int +check_iterate (void *cls, const struct StoreRecordMessage *srm) +{ + struct GNUNET_PEERSTORE_Record *record; + + record = PEERSTORE_parse_record_message (srm); + if (NULL == record) + { + GNUNET_break (0); + return GNUNET_SYSERR; + } + if (NULL == record->sub_system) + { + GNUNET_break (0); + PEERSTORE_destroy_record (record); + return GNUNET_SYSERR; + } + PEERSTORE_destroy_record (record); + return GNUNET_OK; +} + + +/** + * Handle an iterate request from client + * + * @param cls identification of the client + * @param srm the actual message + */ +static void +handle_iterate (void *cls, const struct StoreRecordMessage *srm) +{ + struct GNUNET_SERVICE_Client *client = cls; + struct GNUNET_PEERSTORE_Record *record; + + record = PEERSTORE_parse_record_message (srm); + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Iterate request: ss `%s', peer `%s', key `%s'\n", + record->sub_system, + GNUNET_i2s (&record->peer), + (NULL == record->key) ? "NULL" : record->key); + record->client = client; + if (GNUNET_OK != + db->iterate_records (db->cls, + record->sub_system, + (ntohs (srm->peer_set)) ? &record->peer : NULL, + record->key, + &record_iterator, + record)) + { + GNUNET_break (0); + GNUNET_SERVICE_client_drop (client); + PEERSTORE_destroy_record (record); + } +} + + +/** + * Continuation of store_record called by the peerstore plugin + * + * @param cls closure + * @param success result + */ +static void +store_record_continuation (void *cls, int success) +{ + struct GNUNET_PEERSTORE_Record *record = cls; + + if (GNUNET_OK == success) + { + watch_notifier (record); + GNUNET_SERVICE_client_continue (record->client); + } + else + { + GNUNET_break (0); + GNUNET_SERVICE_client_drop (record->client); + } + PEERSTORE_destroy_record (record); +} + + +/** + * Check a store request from client + * + * @param cls client identification of the client + * @param srm the actual message + * @return #GNUNET_OK if @a srm is well-formed + */ +static int +check_store (void *cls, const struct StoreRecordMessage *srm) +{ + struct GNUNET_PEERSTORE_Record *record; + + record = PEERSTORE_parse_record_message (srm); + if (NULL == record) + { + GNUNET_break (0); + return GNUNET_SYSERR; + } + if ((NULL == record->sub_system) || (NULL == record->key)) + { + GNUNET_break (0); + PEERSTORE_destroy_record (record); + return GNUNET_SYSERR; + } + PEERSTORE_destroy_record (record); + return GNUNET_OK; +} + + +/** + * Handle a store request from client + * + * @param cls client identification of the client + * @param srm the actual message + */ +static void +handle_store (void *cls, const struct StoreRecordMessage *srm) +{ + struct GNUNET_SERVICE_Client *client = cls; + struct GNUNET_PEERSTORE_Record *record; + + record = PEERSTORE_parse_record_message (srm); + GNUNET_log ( + GNUNET_ERROR_TYPE_INFO, + "Received a store request. Sub system `%s' Peer `%s Key `%s' Options: %u.\n", + record->sub_system, + GNUNET_i2s (&record->peer), + record->key, + (uint32_t) ntohl (srm->options)); + record->client = client; + if (GNUNET_OK != db->store_record (db->cls, + record->sub_system, + &record->peer, + record->key, + record->value, + record->value_size, + record->expiry, + ntohl (srm->options), + &store_record_continuation, + record)) + { + GNUNET_break (0); + PEERSTORE_destroy_record (record); + GNUNET_SERVICE_client_drop (client); + return; + } +} + +static void +store_hello_continuation (void *cls, int success) +{ + (void) cls; + + if (GNUNET_OK != success) + { + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "Error storing bootstrap hello!\n"); + GNUNET_break (0); + } +} + + +static int +hosts_directory_scan_callback (void *cls, const char *fullname) +{ + (void) cls; + ssize_t size_total; + char buffer[GNUNET_MAX_MESSAGE_SIZE - 1] GNUNET_ALIGN; + const struct GNUNET_MessageHeader *hello; + struct GNUNET_HELLO_Builder *builder; + struct GNUNET_PeerIdentity *pid; + + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "1 hosts_directory_scan_callback filename %s\n", + fullname); + + if (GNUNET_YES != GNUNET_DISK_file_test (fullname)) + return GNUNET_OK; /* ignore non-files */ + + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "2 hosts_directory_scan_callback filename %s\n", + fullname); + + /* filename = strrchr (fullname, DIR_SEPARATOR); */ + /* if ((NULL == filename) || (1 > strlen (filename))) */ + /* filename = fullname; */ + /* else */ + /* filename++; */ + + /* GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, */ + /* "3 hosts_directory_scan_callback filename %s\n", */ + /* filename); */ + + /* if (GNUNET_YES != GNUNET_DISK_file_test (filename)) */ + /* return GNUNET_OK; */ + size_total = GNUNET_DISK_fn_read (fullname, buffer, sizeof(buffer)); + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Read %d bytes from `%s'\n", + (int) size_total, + fullname); + if ((size_total < 0) || + (((size_t) size_total) < sizeof(struct GNUNET_MessageHeader))) + { + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + _ ("Failed to parse HELLO in file `%s': %s\n"), + fullname, + "File has invalid size"); + return GNUNET_OK; + } + hello = (const struct GNUNET_MessageHeader *) &buffer[0]; + builder = GNUNET_HELLO_builder_from_msg (hello); + pid = GNUNET_HELLO_builder_get_id (builder); + + if (GNUNET_OK != db->store_record (db->cls, + "peerstore", + pid, + "", + hello, + size_total, + GNUNET_TIME_UNIT_FOREVER_ABS, + GNUNET_PEERSTORE_STOREOPTION_REPLACE, + &store_hello_continuation, + NULL)) + { + GNUNET_break (0); + } + GNUNET_HELLO_builder_free (builder); + return GNUNET_OK; +} + + +/** + * Peerstore service runner. + * + * @param cls closure + * @param c configuration to use + * @param service the initialized service + */ +static void +run (void *cls, + const struct GNUNET_CONFIGURATION_Handle *c, + struct GNUNET_SERVICE_Handle *service) +{ + char *database; + int use_included; + char *ip; + char *peerdir; + + in_shutdown = GNUNET_NO; + num_clients = 0; + cfg = c; + + if (GNUNET_OK != GNUNET_CONFIGURATION_get_value_string (cfg, + "peerstore", + "DATABASE", + &database)) + { + GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR, + "peerstore", + "DATABASE"); + GNUNET_SCHEDULER_shutdown (); + return; + } + GNUNET_asprintf (&db_lib_name, "libgnunet_plugin_peerstore_%s", database); + db = GNUNET_PLUGIN_load (db_lib_name, (void *) cfg); + GNUNET_free (database); + if (NULL == db) + { + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + _ ("Could not load database backend `%s'\n"), + db_lib_name); + GNUNET_SCHEDULER_shutdown (); + return; + } + watchers = GNUNET_CONTAINER_multihashmap_create (10, GNUNET_NO); + expire_task = GNUNET_SCHEDULER_add_now (&cleanup_expired_records, NULL); + + use_included = GNUNET_CONFIGURATION_get_value_yesno (cfg, + "peerstore", + "USE_INCLUDED_HELLOS"); + if (GNUNET_SYSERR == use_included) + use_included = GNUNET_NO; + if (GNUNET_YES == use_included) + { + ip = GNUNET_OS_installation_get_path (GNUNET_OS_IPK_DATADIR); + GNUNET_asprintf (&peerdir, "%shellos", ip); + GNUNET_free (ip); + + GNUNET_log (GNUNET_ERROR_TYPE_INFO, + _ ("Importing HELLOs from `%s'\n"), + peerdir); + + GNUNET_DISK_directory_scan (peerdir, + &hosts_directory_scan_callback, + NULL); + GNUNET_free (peerdir); + } + else + { + GNUNET_log (GNUNET_ERROR_TYPE_INFO, + _ ("Skipping import of included HELLOs\n")); + } + + GNUNET_SCHEDULER_add_shutdown (&shutdown_task, NULL); +} + + +/** + * Define "main" method using service macro. + */ +GNUNET_SERVICE_MAIN ( + "peerstore", + GNUNET_SERVICE_OPTION_SOFT_SHUTDOWN, + &run, + &client_connect_cb, + &client_disconnect_cb, + NULL, + GNUNET_MQ_hd_var_size (store, + GNUNET_MESSAGE_TYPE_PEERSTORE_STORE, + struct StoreRecordMessage, + NULL), + GNUNET_MQ_hd_var_size (iterate, + GNUNET_MESSAGE_TYPE_PEERSTORE_ITERATE, + struct StoreRecordMessage, + NULL), + GNUNET_MQ_hd_fixed_size (watch, + GNUNET_MESSAGE_TYPE_PEERSTORE_WATCH, + struct StoreKeyHashMessage, + NULL), + GNUNET_MQ_hd_fixed_size (watch_cancel, + GNUNET_MESSAGE_TYPE_PEERSTORE_WATCH_CANCEL, + struct StoreKeyHashMessage, + NULL), + GNUNET_MQ_handler_end ()); + + +/* end of gnunet-service-peerstore.c */ diff --git a/src/service/peerstore/meson.build b/src/service/peerstore/meson.build new file mode 100644 index 000000000..6c9853852 --- /dev/null +++ b/src/service/peerstore/meson.build @@ -0,0 +1,50 @@ +libgnunetpeerstore_src = ['peerstore_api.c', + 'peerstore_common.c'] + +gnunetservicepeerstore_src = ['gnunet-service-peerstore.c'] + +configure_file(input : 'peerstore.conf.in', + output : 'peerstore.conf', + configuration : cdata, + install: true, + install_dir: pkgcfgdir) + + +if get_option('monolith') + foreach p : libgnunetpeerstore_src + gnunetservicepeerstore_src + gnunet_src += 'peerstore/' + p + endforeach + subdir_done() +endif + +libgnunetpeerstore = library('gnunetpeerstore', + libgnunetpeerstore_src, + soversion: '0', + version: '0.0.0', + dependencies: [libgnunetutil_dep, libgnunethello_dep], + include_directories: [incdir, configuration_inc], + install: true, + install_dir: get_option('libdir')) +pkg.generate(libgnunetpeerstore, url: 'https://www.gnunet.org', + description : 'Provides API for accessing the peerstore service') +libgnunetpeerstore_dep = declare_dependency(link_with : libgnunetpeerstore) + +shared_module('gnunet_plugin_peerstore_sqlite', + ['plugin_peerstore_sqlite.c'], + dependencies: [libgnunetutil_dep, + libgnunetpeerstore_dep, + libgnunetsq_dep, + sqlite_dep], + include_directories: [incdir, configuration_inc], + install: true, + install_dir: get_option('libdir')/'gnunet') + +executable ('gnunet-service-peerstore', + gnunetservicepeerstore_src, + dependencies: [libgnunetpeerstore_dep, + libgnunetutil_dep, + libgnunethello_dep], + include_directories: [incdir, configuration_inc], + install: true, + install_dir: get_option('libdir') / 'gnunet' / 'libexec') + diff --git a/src/service/peerstore/peerstore.conf.in b/src/service/peerstore/peerstore.conf.in new file mode 100644 index 000000000..a3a7c672b --- /dev/null +++ b/src/service/peerstore/peerstore.conf.in @@ -0,0 +1,13 @@ +[peerstore] +START_ON_DEMAND = @START_ON_DEMAND@ +@JAVAPORT@PORT = 2110 +HOSTNAME = localhost +BINARY = gnunet-service-peerstore +UNIXPATH = $GNUNET_RUNTIME_DIR/gnunet-service-peerstore.sock +UNIX_MATCH_UID = NO +UNIX_MATCH_GID = YES +DATABASE = sqlite + +[peerstore-sqlite] +FILENAME = $GNUNET_DATA_HOME/peerstore/sqlite.db + diff --git a/src/service/peerstore/peerstore.h b/src/service/peerstore/peerstore.h new file mode 100644 index 000000000..0dec03443 --- /dev/null +++ b/src/service/peerstore/peerstore.h @@ -0,0 +1,108 @@ +/* + 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 . + + SPDX-License-Identifier: AGPL3.0-or-later + */ +/** + * @file peerstore/peerstore.h + * @brief IPC messages + * @author Omar Tarabai + */ + +#ifndef PEERSTORE_H_ +#define PEERSTORE_H_ + +#include "gnunet_peerstore_service.h" + + +GNUNET_NETWORK_STRUCT_BEGIN +/** + * Message carrying a PEERSTORE record message + */ +struct StoreRecordMessage +{ + /** + * GNUnet message header + */ + struct GNUNET_MessageHeader header; + + /** + * #GNUNET_YES if peer id value set, #GNUNET_NO otherwise + */ + uint16_t peer_set GNUNET_PACKED; + + /** + * Size of the sub_system string + * Allocated at position 0 after this struct + */ + uint16_t sub_system_size GNUNET_PACKED; + + /** + * Peer Identity + */ + struct GNUNET_PeerIdentity peer; + + /** + * Expiry time of entry + */ + struct GNUNET_TIME_AbsoluteNBO expiry; + + /** + * Size of the key string + * Allocated at position 1 after this struct + */ + uint16_t key_size GNUNET_PACKED; + + /** + * Size of value blob + * Allocated at position 2 after this struct + */ + uint16_t value_size GNUNET_PACKED; + + /** + * Options, needed only in case of a + * store operation + */ + uint32_t /* enum GNUNET_PEERSTORE_StoreOption */ options GNUNET_PACKED; + + /* Followed by key and value */ +}; + + +/** + * Message carrying record key hash + */ +struct StoreKeyHashMessage +{ + /** + * GNUnet message header + */ + struct GNUNET_MessageHeader header; + + /** + * Always 0, for alignment. + */ + uint32_t reserved GNUNET_PACKED; + + /** + * Hash of a record key + */ + struct GNUNET_HashCode keyhash; +}; + +GNUNET_NETWORK_STRUCT_END +#endif diff --git a/src/service/peerstore/peerstore_api.c b/src/service/peerstore/peerstore_api.c new file mode 100644 index 000000000..8770c36e4 --- /dev/null +++ b/src/service/peerstore/peerstore_api.c @@ -0,0 +1,1391 @@ +/* + This file is part of GNUnet. + Copyright (C) 2013-2016, 2019 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 . + + SPDX-License-Identifier: AGPL3.0-or-later + */ +/** + * @file peerstore/peerstore_api.c + * @brief API for peerstore + * @author Omar Tarabai + * @author Christian Grothoff + */ +#include "platform.h" +#include "gnunet_util_lib.h" +#include "gnunet_hello_uri_lib.h" +#include "peerstore.h" +#include "peerstore_common.h" +#include "gnunet_peerstore_service.h" + +#define LOG(kind, ...) GNUNET_log_from (kind, "peerstore-api", __VA_ARGS__) + +/******************************************************************************/ +/************************ DATA STRUCTURES ****************************/ +/******************************************************************************/ + +/** + * Handle to the PEERSTORE service. + */ +struct GNUNET_PEERSTORE_Handle +{ + /** + * Our configuration. + */ + const struct GNUNET_CONFIGURATION_Handle *cfg; + + /** + * Message queue + */ + struct GNUNET_MQ_Handle *mq; + + /** + * Head of active STORE requests. + */ + struct GNUNET_PEERSTORE_StoreContext *store_head; + + /** + * Tail of active STORE requests. + */ + struct GNUNET_PEERSTORE_StoreContext *store_tail; + + /** + * Head of active ITERATE requests. + */ + struct GNUNET_PEERSTORE_IterateContext *iterate_head; + + /** + * Tail of active ITERATE requests. + */ + struct GNUNET_PEERSTORE_IterateContext *iterate_tail; + + /** + * Hashmap of watch requests + */ + struct GNUNET_CONTAINER_MultiHashMap *watches; + + /** + * ID of the task trying to reconnect to the service. + */ + struct GNUNET_SCHEDULER_Task *reconnect_task; + + /** + * Delay until we try to reconnect. + */ + struct GNUNET_TIME_Relative reconnect_delay; + + /** + * Are we in the process of disconnecting but need to sync first? + */ + int disconnecting; +}; + +/** + * Context for a store request + */ +struct GNUNET_PEERSTORE_StoreContext +{ + /** + * Kept in a DLL. + */ + struct GNUNET_PEERSTORE_StoreContext *next; + + /** + * Kept in a DLL. + */ + struct GNUNET_PEERSTORE_StoreContext *prev; + + /** + * Handle to the PEERSTORE service. + */ + struct GNUNET_PEERSTORE_Handle *h; + + /** + * Continuation called with service response + */ + GNUNET_PEERSTORE_Continuation cont; + + /** + * Closure for @e cont + */ + void *cont_cls; + + /** + * Which subsystem does the store? + */ + char *sub_system; + + /** + * Key for the store operation. + */ + char *key; + + /** + * Contains @e size bytes. + */ + void *value; + + /** + * Peer the store is for. + */ + struct GNUNET_PeerIdentity peer; + + /** + * Number of bytes in @e value. + */ + size_t size; + + /** + * When does the value expire? + */ + struct GNUNET_TIME_Absolute expiry; + + /** + * Options for the store operation. + */ + enum GNUNET_PEERSTORE_StoreOption options; +}; + +/** + * Closure for store callback when storing hello uris. + */ +struct StoreHelloCls +{ + /** + * The corresponding store context. + */ + struct GNUNET_PEERSTORE_StoreContext *sc; + + /** + * The corresponding hello uri add request. + */ + struct GNUNET_PEERSTORE_StoreHelloContext *huc; +}; + +/** + * Context for a iterate request + */ +struct GNUNET_PEERSTORE_IterateContext +{ + /** + * Kept in a DLL. + */ + struct GNUNET_PEERSTORE_IterateContext *next; + + /** + * Kept in a DLL. + */ + struct GNUNET_PEERSTORE_IterateContext *prev; + + /** + * Handle to the PEERSTORE service. + */ + struct GNUNET_PEERSTORE_Handle *h; + + /** + * Which subsystem does the store? + */ + char *sub_system; + + /** + * Peer the store is for. + */ + struct GNUNET_PeerIdentity peer; + + /** + * Key for the store operation. + */ + char *key; + + /** + * Callback with each matching record + */ + GNUNET_PEERSTORE_Processor callback; + + /** + * Closure for @e callback + */ + void *callback_cls; + + /** + * #GNUNET_YES if we are currently processing records. + */ + int iterating; +}; + +/** + * Context for a watch request + */ +struct GNUNET_PEERSTORE_WatchContext +{ + /** + * Kept in a DLL. + */ + struct GNUNET_PEERSTORE_WatchContext *next; + + /** + * Kept in a DLL. + */ + struct GNUNET_PEERSTORE_WatchContext *prev; + + /** + * Handle to the PEERSTORE service. + */ + struct GNUNET_PEERSTORE_Handle *h; + + /** + * Callback with each record received + */ + GNUNET_PEERSTORE_Processor callback; + + /** + * Closure for @e callback + */ + void *callback_cls; + + /** + * Hash of the combined key + */ + struct GNUNET_HashCode keyhash; + + /** + * The iteration context to deliver the actual values for the key. + */ + struct GNUNET_PEERSTORE_IterateContext *ic; + + /** + * The peer we are watching for values. + */ + const struct GNUNET_PeerIdentity *peer; + + /** + * The key we like to watch for values. + */ + const char *key; + + /** + * The sub system requested the watch. + */ + const char *sub_system; +}; + +/** + * Context for the info handler. + */ +struct GNUNET_PEERSTORE_NotifyContext +{ + /** + * Peerstore handle. + */ + struct GNUNET_PEERSTORE_Handle *h; + + /** + * Function to call with information. + */ + GNUNET_PEERSTORE_hello_notify_cb callback; + + /** + * Closure for @e callback. + */ + void *callback_cls; + + /** + * The watch for this context. + */ + struct GNUNET_PEERSTORE_WatchContext *wc; + + /** + * Is this request canceled. + */ + unsigned int canceled; +}; + +/** + * Context for a add hello uri request. + */ +struct GNUNET_PEERSTORE_StoreHelloContext +{ + /** + * Peerstore handle. + */ + struct GNUNET_PEERSTORE_Handle *h; + + /** + * Function to call with information. + */ + GNUNET_PEERSTORE_Continuation cont; + + /** + * Closure for @e callback. + */ + void *cont_cls; + + /** + * Map with all store contexts started during adding hello. + */ + struct GNUNET_CONTAINER_MultiPeerMap *store_context_map; + + /** + * Active watch to be notified about conflicting hello uri add requests. + */ + struct GNUNET_PEERSTORE_WatchContext *wc; + + /** + * Hello uri which was request for storing. + */ + struct GNUNET_MessageHeader *hello; + + /** + * The peer id for the hello. + */ + struct GNUNET_PeerIdentity *pid; + + /** + * Was this request successful. + */ + int success; +}; + +/******************************************************************************/ +/******************* DECLARATIONS *********************/ +/******************************************************************************/ + +/** + * Close the existing connection to PEERSTORE and reconnect. + * + * @param cls a `struct GNUNET_PEERSTORE_Handle *h` + */ +static void +reconnect (void *cls); + + +/** + * Disconnect from the peerstore service. + * + * @param h peerstore handle to disconnect + */ +static void +disconnect (struct GNUNET_PEERSTORE_Handle *h) +{ + struct GNUNET_PEERSTORE_IterateContext *next; + + for (struct GNUNET_PEERSTORE_IterateContext *ic = h->iterate_head; NULL != ic; + ic = next) + { + next = ic->next; + if (GNUNET_YES == ic->iterating) + { + GNUNET_PEERSTORE_Processor icb; + void *icb_cls; + + icb = ic->callback; + icb_cls = ic->callback_cls; + GNUNET_PEERSTORE_iterate_cancel (ic); + if (NULL != icb) + icb (icb_cls, NULL, "Iteration canceled due to reconnection"); + } + } + + if (NULL != h->mq) + { + GNUNET_MQ_destroy (h->mq); + h->mq = NULL; + } +} + + +/** + * Function that will schedule the job that will try + * to connect us again to the client. + * + * @param h peerstore to reconnect + */ +static void +disconnect_and_schedule_reconnect (struct GNUNET_PEERSTORE_Handle *h) +{ + GNUNET_assert (NULL == h->reconnect_task); + disconnect (h); + LOG (GNUNET_ERROR_TYPE_DEBUG, + "Scheduling task to reconnect to PEERSTORE service in %s.\n", + GNUNET_STRINGS_relative_time_to_string (h->reconnect_delay, GNUNET_YES)); + h->reconnect_task = + GNUNET_SCHEDULER_add_delayed (h->reconnect_delay, &reconnect, h); + h->reconnect_delay = GNUNET_TIME_STD_BACKOFF (h->reconnect_delay); +} + + +/** + * Callback after MQ envelope is sent + * + * @param cls a `struct GNUNET_PEERSTORE_StoreContext *` + */ +static void +store_request_sent (void *cls) +{ + struct GNUNET_PEERSTORE_StoreContext *sc = cls; + GNUNET_PEERSTORE_Continuation cont; + void *cont_cls; + + if (NULL != sc) + { + cont = sc->cont; + cont_cls = sc->cont_cls; + GNUNET_PEERSTORE_store_cancel (sc); + if (NULL != cont) + cont (cont_cls, GNUNET_OK); + } +} + + +/******************************************************************************/ +/******************* CONNECTION FUNCTIONS *********************/ +/******************************************************************************/ + + +/** + * Function called when we had trouble talking to the service. + */ +static void +handle_client_error (void *cls, enum GNUNET_MQ_Error error) +{ + struct GNUNET_PEERSTORE_Handle *h = cls; + + LOG (GNUNET_ERROR_TYPE_ERROR, + "Received an error notification from MQ of type: %d\n", + error); + disconnect_and_schedule_reconnect (h); +} + + +/** + * Iterator over previous watches to resend them + * + * @param cls the `struct GNUNET_PEERSTORE_Handle` + * @param key key for the watch + * @param value the `struct GNUNET_PEERSTORE_WatchContext *` + * @return #GNUNET_YES (continue to iterate) + */ +static int +rewatch_it (void *cls, const struct GNUNET_HashCode *key, void *value) +{ + struct GNUNET_PEERSTORE_Handle *h = cls; + struct GNUNET_PEERSTORE_WatchContext *wc = value; + struct StoreKeyHashMessage *hm; + struct GNUNET_MQ_Envelope *ev; + + ev = GNUNET_MQ_msg (hm, GNUNET_MESSAGE_TYPE_PEERSTORE_WATCH); + hm->keyhash = wc->keyhash; + GNUNET_MQ_send (h->mq, ev); + return GNUNET_YES; +} + + +/** + * Iterator over watch requests to cancel them. + * + * @param cls unused + * @param key key to the watch request + * @param value watch context + * @return #GNUNET_YES to continue iteration + */ +static int +destroy_watch (void *cls, const struct GNUNET_HashCode *key, void *value) +{ + struct GNUNET_PEERSTORE_WatchContext *wc = value; + + GNUNET_PEERSTORE_watch_cancel (wc); + return GNUNET_YES; +} + + +/** + * Kill the connection to the service. This can be delayed in case of pending + * STORE requests and the user explicitly asked to sync first. Otherwise it is + * performed instantly. + * + * @param h Handle to the service. + */ +static void +final_disconnect (struct GNUNET_PEERSTORE_Handle *h) +{ + if (NULL != h->mq) + { + GNUNET_MQ_destroy (h->mq); + h->mq = NULL; + } + GNUNET_free (h); +} + + +/** + * Connect to the PEERSTORE service. + * + * @param cfg configuration to use + * @return NULL on error + */ +struct GNUNET_PEERSTORE_Handle * +GNUNET_PEERSTORE_connect (const struct GNUNET_CONFIGURATION_Handle *cfg) +{ + struct GNUNET_PEERSTORE_Handle *h; + + h = GNUNET_new (struct GNUNET_PEERSTORE_Handle); + h->cfg = cfg; + h->disconnecting = GNUNET_NO; + reconnect (h); + if (NULL == h->mq) + { + GNUNET_free (h); + return NULL; + } + return h; +} + + +/** + * Disconnect from the PEERSTORE service. Any pending ITERATE and WATCH requests + * will be canceled. + * Any pending STORE requests will depend on @e snyc_first flag. + * + * @param h handle to disconnect + * @param sync_first send any pending STORE requests before disconnecting + */ +void +GNUNET_PEERSTORE_disconnect (struct GNUNET_PEERSTORE_Handle *h, int sync_first) +{ + struct GNUNET_PEERSTORE_IterateContext *ic; + struct GNUNET_PEERSTORE_StoreContext *sc; + + LOG (GNUNET_ERROR_TYPE_DEBUG, "Disconnecting.\n"); + if (NULL != h->watches) + { + GNUNET_CONTAINER_multihashmap_iterate (h->watches, &destroy_watch, NULL); + GNUNET_CONTAINER_multihashmap_destroy (h->watches); + h->watches = NULL; + } + while (NULL != (ic = h->iterate_head)) + { + GNUNET_break (0); + GNUNET_PEERSTORE_iterate_cancel (ic); + } + if (NULL != h->store_head) + { + if (GNUNET_YES == sync_first) + { + LOG (GNUNET_ERROR_TYPE_DEBUG, + "Delaying disconnection due to pending store requests.\n"); + h->disconnecting = GNUNET_YES; + return; + } + while (NULL != (sc = h->store_head)) + GNUNET_PEERSTORE_store_cancel (sc); + } + final_disconnect (h); +} + + +/******************************************************************************/ +/******************* STORE FUNCTIONS *********************/ +/******************************************************************************/ + + +/** + * Cancel a store request + * + * @param sc Store request context + */ +void +GNUNET_PEERSTORE_store_cancel (struct GNUNET_PEERSTORE_StoreContext *sc) +{ + struct GNUNET_PEERSTORE_Handle *h = sc->h; + + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "store cancel with sc %p \n", + sc); + GNUNET_CONTAINER_DLL_remove (sc->h->store_head, sc->h->store_tail, sc); + GNUNET_free (sc->sub_system); + GNUNET_free (sc->value); + GNUNET_free (sc->key); + GNUNET_free (sc); + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "store cancel with sc %p is null\n", + sc); + if ((GNUNET_YES == h->disconnecting) && (NULL == h->store_head)) + final_disconnect (h); +} + + +/** + * Store a new entry in the PEERSTORE. + * Note that stored entries can be lost in some cases + * such as power failure. + * + * @param h Handle to the PEERSTORE service + * @param sub_system name of the sub system + * @param peer Peer Identity + * @param key entry key + * @param value entry value BLOB + * @param size size of @e value + * @param expiry absolute time after which the entry is (possibly) deleted + * @param options options specific to the storage operation + * @param cont Continuation function after the store request is sent + * @param cont_cls Closure for @a cont + */ +struct GNUNET_PEERSTORE_StoreContext * +GNUNET_PEERSTORE_store (struct GNUNET_PEERSTORE_Handle *h, + const char *sub_system, + const struct GNUNET_PeerIdentity *peer, + const char *key, + const void *value, + size_t size, + struct GNUNET_TIME_Absolute expiry, + enum GNUNET_PEERSTORE_StoreOption options, + GNUNET_PEERSTORE_Continuation cont, + void *cont_cls) +{ + struct GNUNET_MQ_Envelope *ev; + struct GNUNET_PEERSTORE_StoreContext *sc; + + LOG (GNUNET_ERROR_TYPE_DEBUG, + "Storing value (size: %lu) for subsystem `%s', peer `%s', key `%s'\n", + size, + sub_system, + GNUNET_i2s (peer), + key); + ev = + PEERSTORE_create_record_mq_envelope (sub_system, + peer, + key, + value, + size, + expiry, + options, + GNUNET_MESSAGE_TYPE_PEERSTORE_STORE); + sc = GNUNET_new (struct GNUNET_PEERSTORE_StoreContext); + + sc->sub_system = GNUNET_strdup (sub_system); + sc->peer = *peer; + sc->key = GNUNET_strdup (key); + sc->value = GNUNET_memdup (value, size); + sc->size = size; + sc->expiry = expiry; + sc->options = options; + sc->cont = cont; + sc->cont_cls = cont_cls; + sc->h = h; + + GNUNET_CONTAINER_DLL_insert_tail (h->store_head, h->store_tail, sc); + GNUNET_MQ_notify_sent (ev, &store_request_sent, sc); + GNUNET_MQ_send (h->mq, ev); + return sc; +} + + +/******************************************************************************/ +/******************* ITERATE FUNCTIONS *********************/ +/******************************************************************************/ + + +/** + * When a response for iterate request is received + * + * @param cls a `struct GNUNET_PEERSTORE_Handle *` + * @param msg message received + */ +static void +handle_iterate_end (void *cls, const struct GNUNET_MessageHeader *msg) +{ + struct GNUNET_PEERSTORE_Handle *h = cls; + struct GNUNET_PEERSTORE_IterateContext *ic; + GNUNET_PEERSTORE_Processor callback; + void *callback_cls; + + ic = h->iterate_head; + if (NULL == ic) + { + LOG (GNUNET_ERROR_TYPE_ERROR, + _ ("Unexpected iteration response, this should not happen.\n")); + disconnect_and_schedule_reconnect (h); + return; + } + callback = ic->callback; + callback_cls = ic->callback_cls; + ic->iterating = GNUNET_NO; + GNUNET_PEERSTORE_iterate_cancel (ic); + /* NOTE: set this here and not after callback because callback may free h */ + h->reconnect_delay = GNUNET_TIME_UNIT_ZERO; + if (NULL != callback) + callback (callback_cls, NULL, NULL); +} + + +/** + * When a response for iterate request is received, check the + * message is well-formed. + * + * @param cls a `struct GNUNET_PEERSTORE_Handle *` + * @param msg message received + */ +static int +check_iterate_result (void *cls, const struct StoreRecordMessage *msg) +{ + /* we defer validation to #handle_iterate_result */ + return GNUNET_OK; +} + + +/** + * When a response for iterate request is received + * + * @param cls a `struct GNUNET_PEERSTORE_Handle *` + * @param msg message received + */ +static void +handle_iterate_result (void *cls, const struct StoreRecordMessage *msg) +{ + struct GNUNET_PEERSTORE_Handle *h = cls; + struct GNUNET_PEERSTORE_IterateContext *ic; + GNUNET_PEERSTORE_Processor callback; + void *callback_cls; + struct GNUNET_PEERSTORE_Record *record; + + ic = h->iterate_head; + if (NULL == ic) + { + LOG (GNUNET_ERROR_TYPE_ERROR, + _ ("Unexpected iteration response, this should not happen.\n")); + disconnect_and_schedule_reconnect (h); + return; + } + ic->iterating = GNUNET_YES; + callback = ic->callback; + callback_cls = ic->callback_cls; + if (NULL == callback) + return; + record = PEERSTORE_parse_record_message (msg); + if (NULL == record) + { + callback (callback_cls, + NULL, + _ ("Received a malformed response from service.")); + } + else + { + callback (callback_cls, record, NULL); + PEERSTORE_destroy_record (record); + } +} + + +/** + * Cancel an iterate request + * Please do not call after the iterate request is done + * + * @param ic Iterate request context as returned by GNUNET_PEERSTORE_iterate() + */ +void +GNUNET_PEERSTORE_iterate_cancel (struct GNUNET_PEERSTORE_IterateContext *ic) +{ + if (GNUNET_NO == ic->iterating) + { + GNUNET_CONTAINER_DLL_remove (ic->h->iterate_head, ic->h->iterate_tail, ic); + GNUNET_free (ic->sub_system); + GNUNET_free (ic->key); + GNUNET_free (ic); + } + else + ic->callback = NULL; +} + + +struct GNUNET_PEERSTORE_IterateContext * +GNUNET_PEERSTORE_iterate (struct GNUNET_PEERSTORE_Handle *h, + const char *sub_system, + const struct GNUNET_PeerIdentity *peer, + const char *key, + GNUNET_PEERSTORE_Processor callback, + void *callback_cls) +{ + struct GNUNET_MQ_Envelope *ev; + struct GNUNET_PEERSTORE_IterateContext *ic; + + ev = + PEERSTORE_create_record_mq_envelope (sub_system, + peer, + key, + NULL, + 0, + GNUNET_TIME_UNIT_FOREVER_ABS, + 0, + GNUNET_MESSAGE_TYPE_PEERSTORE_ITERATE); + ic = GNUNET_new (struct GNUNET_PEERSTORE_IterateContext); + ic->callback = callback; + ic->callback_cls = callback_cls; + ic->h = h; + ic->sub_system = GNUNET_strdup (sub_system); + if (NULL != peer) + ic->peer = *peer; + if (NULL != key) + ic->key = GNUNET_strdup (key); + GNUNET_CONTAINER_DLL_insert_tail (h->iterate_head, h->iterate_tail, ic); + LOG (GNUNET_ERROR_TYPE_DEBUG, + "Sending an iterate request for sub system `%s'\n", + sub_system); + GNUNET_MQ_send (h->mq, ev); + return ic; +} + + +/******************************************************************************/ +/******************* WATCH FUNCTIONS *********************/ +/******************************************************************************/ + +/** + * When a watch record is received, validate it is well-formed. + * + * @param cls a `struct GNUNET_PEERSTORE_Handle *` + * @param msg message received + */ +static int +check_watch_record (void *cls, const struct StoreRecordMessage *msg) +{ + /* we defer validation to #handle_watch_result */ + return GNUNET_OK; +} + + +/** + * When a watch record is received, process it. + * + * @param cls a `struct GNUNET_PEERSTORE_Handle *` + * @param msg message received + */ +static void +handle_watch_record (void *cls, const struct StoreRecordMessage *msg) +{ + struct GNUNET_PEERSTORE_Handle *h = cls; + struct GNUNET_PEERSTORE_Record *record; + struct GNUNET_HashCode keyhash; + struct GNUNET_PEERSTORE_WatchContext *wc; + + LOG (GNUNET_ERROR_TYPE_DEBUG, "Received a watch record from service.\n"); + record = PEERSTORE_parse_record_message (msg); + if (NULL == record) + { + disconnect_and_schedule_reconnect (h); + return; + } + PEERSTORE_hash_key (record->sub_system, &record->peer, record->key, &keyhash); + // FIXME: what if there are multiple watches for the same key? + wc = GNUNET_CONTAINER_multihashmap_get (h->watches, &keyhash); + if (NULL == wc) + { + LOG (GNUNET_ERROR_TYPE_ERROR, + _ ("Received a watch result for a non existing watch.\n")); + PEERSTORE_destroy_record (record); + disconnect_and_schedule_reconnect (h); + return; + } + h->reconnect_delay = GNUNET_TIME_UNIT_ZERO; + if (NULL != wc->callback) + wc->callback (wc->callback_cls, record, NULL); + PEERSTORE_destroy_record (record); +} + + +/** + * Close the existing connection to PEERSTORE and reconnect. + * + * @param cls a `struct GNUNET_PEERSTORE_Handle *` + */ +static void +reconnect (void *cls) +{ + struct GNUNET_PEERSTORE_Handle *h = cls; + struct GNUNET_MQ_MessageHandler mq_handlers[] = + { GNUNET_MQ_hd_fixed_size (iterate_end, + GNUNET_MESSAGE_TYPE_PEERSTORE_ITERATE_END, + struct GNUNET_MessageHeader, + h), + GNUNET_MQ_hd_var_size (iterate_result, + GNUNET_MESSAGE_TYPE_PEERSTORE_ITERATE_RECORD, + struct StoreRecordMessage, + h), + GNUNET_MQ_hd_var_size (watch_record, + GNUNET_MESSAGE_TYPE_PEERSTORE_WATCH_RECORD, + struct StoreRecordMessage, + h), + GNUNET_MQ_handler_end () }; + struct GNUNET_MQ_Envelope *ev; + + h->reconnect_task = NULL; + LOG (GNUNET_ERROR_TYPE_DEBUG, "Reconnecting...\n"); + h->mq = GNUNET_CLIENT_connect (h->cfg, + "peerstore", + mq_handlers, + &handle_client_error, + h); + if (NULL == h->mq) + { + h->reconnect_task = + GNUNET_SCHEDULER_add_delayed (h->reconnect_delay, &reconnect, h); + h->reconnect_delay = GNUNET_TIME_STD_BACKOFF (h->reconnect_delay); + return; + } + LOG (GNUNET_ERROR_TYPE_DEBUG, + "Resending pending requests after reconnect.\n"); + if (NULL != h->watches) + GNUNET_CONTAINER_multihashmap_iterate (h->watches, &rewatch_it, h); + for (struct GNUNET_PEERSTORE_IterateContext *ic = h->iterate_head; NULL != ic; + ic = ic->next) + { + ev = + PEERSTORE_create_record_mq_envelope (ic->sub_system, + &ic->peer, + ic->key, + NULL, + 0, + GNUNET_TIME_UNIT_FOREVER_ABS, + 0, + GNUNET_MESSAGE_TYPE_PEERSTORE_ITERATE); + GNUNET_MQ_send (h->mq, ev); + } + for (struct GNUNET_PEERSTORE_StoreContext *sc = h->store_head; NULL != sc; + sc = sc->next) + { + ev = + PEERSTORE_create_record_mq_envelope (sc->sub_system, + &sc->peer, + sc->key, + sc->value, + sc->size, + sc->expiry, + sc->options, + GNUNET_MESSAGE_TYPE_PEERSTORE_STORE); + GNUNET_MQ_notify_sent (ev, &store_request_sent, sc); + GNUNET_MQ_send (h->mq, ev); + } +} + + +/** + * Cancel a watch request + * + * @param wc handle to the watch request + */ +void +GNUNET_PEERSTORE_watch_cancel (struct GNUNET_PEERSTORE_WatchContext *wc) +{ + struct GNUNET_PEERSTORE_Handle *h = wc->h; + struct GNUNET_MQ_Envelope *ev; + struct StoreKeyHashMessage *hm; + + LOG (GNUNET_ERROR_TYPE_DEBUG, "Canceling watch.\n"); + if (NULL != wc->ic) + { + GNUNET_PEERSTORE_iterate_cancel (wc->ic); + GNUNET_free (wc); + return; + } + + ev = GNUNET_MQ_msg (hm, GNUNET_MESSAGE_TYPE_PEERSTORE_WATCH_CANCEL); + hm->keyhash = wc->keyhash; + GNUNET_MQ_send (h->mq, ev); + GNUNET_assert ( + GNUNET_YES == + GNUNET_CONTAINER_multihashmap_remove (h->watches, &wc->keyhash, wc)); + GNUNET_free (wc); +} + + +static void +watch_iterate (void *cls, + const struct GNUNET_PEERSTORE_Record *record, + const char *emsg) +{ + struct GNUNET_PEERSTORE_WatchContext *wc = cls; + struct GNUNET_PEERSTORE_Handle *h = wc->h; + struct StoreKeyHashMessage *hm; + + if (NULL != emsg) + { + GNUNET_log (GNUNET_ERROR_TYPE_WARNING, + "Got failure from PEERSTORE: %s\n", + emsg); + wc->callback (wc->callback_cls, NULL, emsg); + return; + } + if (NULL == record) + { + struct GNUNET_MQ_Envelope *ev; + const struct GNUNET_PeerIdentity *peer; + + if (NULL == wc->peer) + peer = GNUNET_new (struct GNUNET_PeerIdentity); + else + peer = wc->peer; + ev = GNUNET_MQ_msg (hm, GNUNET_MESSAGE_TYPE_PEERSTORE_WATCH); + PEERSTORE_hash_key (wc->sub_system, peer, wc->key, &hm->keyhash); + LOG (GNUNET_ERROR_TYPE_DEBUG, + "Hash key we watch for %s\n", + GNUNET_h2s_full (&hm->keyhash)); + wc->keyhash = hm->keyhash; + if (NULL == h->watches) + h->watches = GNUNET_CONTAINER_multihashmap_create (5, GNUNET_NO); + GNUNET_assert (GNUNET_OK == GNUNET_CONTAINER_multihashmap_put ( + h->watches, + &wc->keyhash, + wc, + GNUNET_CONTAINER_MULTIHASHMAPOPTION_MULTIPLE)); + LOG (GNUNET_ERROR_TYPE_DEBUG, + "Sending a watch request for subsystem `%s', peer `%s', key `%s'.\n", + wc->sub_system, + GNUNET_i2s (peer), + wc->key); + GNUNET_MQ_send (h->mq, ev); + wc->ic = NULL; + if (NULL != wc->callback) + wc->callback (wc->callback_cls, record, NULL); + return; + } + + if (NULL != wc->callback) + wc->callback (wc->callback_cls, record, NULL); +} + + +/** + * Request watching a given key + * User will be notified with any new values added to key, + * all existing entries are supplied beforehand. + * + * @param h handle to the PEERSTORE service + * @param sub_system name of sub system + * @param peer Peer identity + * @param key entry key string + * @param callback function called with each new value + * @param callback_cls closure for @a callback + * @return Handle to watch request + */ +struct GNUNET_PEERSTORE_WatchContext * +GNUNET_PEERSTORE_watch (struct GNUNET_PEERSTORE_Handle *h, + const char *sub_system, + const struct GNUNET_PeerIdentity *peer, + const char *key, + GNUNET_PEERSTORE_Processor callback, + void *callback_cls) +{ + struct GNUNET_PEERSTORE_IterateContext *ic; + struct GNUNET_PEERSTORE_WatchContext *wc; + + wc = GNUNET_new (struct GNUNET_PEERSTORE_WatchContext); + wc->callback = callback; + wc->callback_cls = callback_cls; + wc->h = h; + wc->ic = ic; + wc->key = key; + wc->peer = peer; + wc->sub_system = sub_system; + + ic = GNUNET_PEERSTORE_iterate (h, + sub_system, + peer, + key, + &watch_iterate, + wc); + + return wc; +} + + +/******************************************************************************/ +/******************* HELLO FUNCTIONS *********************/ +/******************************************************************************/ + + +static void +hello_updated (void *cls, + const struct GNUNET_PEERSTORE_Record *record, + const char *emsg) +{ + struct GNUNET_PEERSTORE_NotifyContext *nc = cls; + const struct GNUNET_MessageHeader *hello; + struct GNUNET_HELLO_Builder *builder; + + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "hello_updated\n"); + if (NULL != emsg) + { + GNUNET_log (GNUNET_ERROR_TYPE_WARNING, + "Got failure from PEERSTORE: %s\n", + emsg); + nc->callback (nc->callback_cls, NULL, NULL, emsg); + return; + } + if (NULL == record) + return; + hello = record->value; + builder = GNUNET_HELLO_builder_from_msg (hello); + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "hello_updated with expired %s and size %u for peer %s\n", + GNUNET_STRINGS_absolute_time_to_string (GNUNET_HELLO_builder_get_expiration_time (hello)), + ntohs (hello->size), + GNUNET_i2s (&record->peer)); + if ((0 == record->value_size)) + { + GNUNET_break (0); + return; + } + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "hello_updated call callback\n"); + nc->callback (nc->callback_cls, &record->peer, hello, NULL); +} + + +struct GNUNET_PEERSTORE_NotifyContext * +GNUNET_PEERSTORE_hello_changed_notify (struct GNUNET_PEERSTORE_Handle *h, + int include_friend_only, + GNUNET_PEERSTORE_hello_notify_cb callback, + void *callback_cls) +{ + struct GNUNET_PEERSTORE_NotifyContext *nc; + + nc = GNUNET_new (struct GNUNET_PEERSTORE_NotifyContext); + nc->callback = callback; + nc->callback_cls = callback_cls; + nc->h = h; + + nc->wc = GNUNET_PEERSTORE_watch (h, + "peerstore", + NULL, + GNUNET_PEERSTORE_HELLO_KEY, + &hello_updated, + nc); + + return nc; +} + + +/** + * Stop notifying about changes. + * + * @param nc context to stop notifying + */ +void +GNUNET_PEERSTORE_hello_changed_notify_cancel (struct + GNUNET_PEERSTORE_NotifyContext *nc) +{ + if (NULL != nc->wc) + { + GNUNET_PEERSTORE_watch_cancel (nc->wc); + nc->wc = NULL; + } +} + + +static void +merge_success (void *cls, int success) +{ + struct StoreHelloCls *shu_cls = cls; + struct GNUNET_PEERSTORE_StoreHelloContext *huc = shu_cls->huc; + + if (GNUNET_OK != success) + { + GNUNET_log (GNUNET_ERROR_TYPE_WARNING, + "Storing hello uri failed\n"); + huc->cont (huc->cont_cls, success); + GNUNET_free (huc->hello); + GNUNET_free (huc->pid); + GNUNET_free (huc); + return; + } + GNUNET_CONTAINER_multipeermap_remove (huc->store_context_map, huc->pid, shu_cls->sc); + if (0 == GNUNET_CONTAINER_multipeermap_size (huc->store_context_map)) + { + GNUNET_PEERSTORE_watch_cancel (huc->wc); + huc->wc = NULL; + huc->cont (huc->cont_cls, GNUNET_OK); + huc->success = GNUNET_OK; + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Storing hello uri succeeded for peer %s!\n", + GNUNET_i2s (huc->pid)); + GNUNET_free (huc->hello); + GNUNET_free (huc->pid); + GNUNET_free (huc); + return; + } + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Got notified during storing hello uri for peer %s!\n", + GNUNET_i2s (huc->pid)); +} + + +static void +store_hello (struct GNUNET_PEERSTORE_StoreHelloContext *huc, + const struct GNUNET_MessageHeader *hello) +{ + struct GNUNET_PEERSTORE_Handle *h = huc->h; + struct GNUNET_PEERSTORE_StoreContext *sc; + struct StoreHelloCls *shu_cls = GNUNET_new (struct StoreHelloCls); + struct GNUNET_TIME_Absolute hello_exp; + + shu_cls->huc = huc; + hello_exp = GNUNET_HELLO_builder_get_expiration_time (hello); + sc = GNUNET_PEERSTORE_store (h, + "peerstore", + huc->pid, + GNUNET_PEERSTORE_HELLO_KEY, + hello, + ntohs (hello->size), + hello_exp, + GNUNET_PEERSTORE_STOREOPTION_MULTIPLE, + merge_success, + shu_cls); + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "store_hello with expiration %s\n", + GNUNET_STRINGS_absolute_time_to_string (hello_exp)); + GNUNET_CONTAINER_multipeermap_put (huc->store_context_map, + huc->pid, + sc, + GNUNET_CONTAINER_MULTIHASHMAPOPTION_MULTIPLE); + shu_cls->sc = sc; +} + + +static void +merge_uri (void *cls, + const struct GNUNET_PEERSTORE_Record *record, + const char *emsg) +{ + struct GNUNET_PEERSTORE_StoreHelloContext *huc = cls; + struct GNUNET_MessageHeader *hello; + struct GNUNET_TIME_Absolute huc_hello_exp_time; + struct GNUNET_TIME_Absolute record_hello_exp_time; + + if (NULL != emsg) + { + GNUNET_log (GNUNET_ERROR_TYPE_WARNING, + "Got failure from PEERSTORE: %s\n", + emsg); + return; + } + + if (NULL == record && GNUNET_NO == huc->success) + { + huc_hello_exp_time = GNUNET_HELLO_builder_get_expiration_time (huc->hello); + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "merge_uri just store for peer %s with expiration %s\n", + GNUNET_i2s (huc->pid), + GNUNET_STRINGS_absolute_time_to_string (huc_hello_exp_time)); + store_hello (huc, huc->hello); + } + else if (GNUNET_NO == huc->success && 0 == GNUNET_memcmp (huc->pid, &record->peer)) + { + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "merge_uri record for peer %s\n", + GNUNET_i2s (&record->peer)); + hello = record->value; + if ((0 == record->value_size)) + { + GNUNET_break (0); + return; + } + + huc_hello_exp_time = GNUNET_HELLO_builder_get_expiration_time (huc->hello); + record_hello_exp_time = GNUNET_HELLO_builder_get_expiration_time (hello); + + if (GNUNET_TIME_absolute_cmp (huc_hello_exp_time, >, record_hello_exp_time)) + store_hello (huc, huc->hello); + } +} + + +struct GNUNET_PEERSTORE_StoreHelloContext * +GNUNET_PEERSTORE_hello_add (struct GNUNET_PEERSTORE_Handle *h, + const struct GNUNET_MessageHeader *msg, + GNUNET_PEERSTORE_Continuation cont, + void *cont_cls) +{ + struct GNUNET_HELLO_Builder *builder = GNUNET_HELLO_builder_from_msg (msg); + struct GNUNET_PEERSTORE_StoreHelloContext *huc; + struct GNUNET_PeerIdentity *pid; + struct GNUNET_TIME_Absolute now = GNUNET_TIME_absolute_get (); + struct GNUNET_TIME_Absolute hello_exp = + GNUNET_HELLO_builder_get_expiration_time (msg); + struct GNUNET_TIME_Absolute huc_exp; + uint16_t pid_size; + uint16_t size_msg = ntohs (msg->size); + + if (NULL == builder) + return NULL; + if (GNUNET_TIME_absolute_cmp (hello_exp, <, now)) + return NULL; + + huc = GNUNET_new (struct GNUNET_PEERSTORE_StoreHelloContext); + huc->store_context_map = GNUNET_CONTAINER_multipeermap_create (1, GNUNET_NO); + huc->h = h; + huc->cont = cont; + huc->cont_cls = cont_cls; + huc->hello = GNUNET_malloc (size_msg); + GNUNET_memcpy (huc->hello, msg, size_msg); + huc_exp = + GNUNET_HELLO_builder_get_expiration_time (huc->hello); + pid = GNUNET_HELLO_builder_get_id (builder); + pid_size = sizeof (struct GNUNET_PeerIdentity); + huc->pid = GNUNET_malloc (pid_size); + GNUNET_memcpy (huc->pid, pid, pid_size); + LOG (GNUNET_ERROR_TYPE_DEBUG, + "Adding hello for peer %s with expiration %s msg size %u\n", + GNUNET_i2s (huc->pid), + GNUNET_STRINGS_absolute_time_to_string (huc_exp), + size_msg); + huc->wc = GNUNET_PEERSTORE_watch (h, + "peerstore", + NULL, + GNUNET_PEERSTORE_HELLO_KEY, + &merge_uri, + huc); + GNUNET_HELLO_builder_free (builder); + + return huc; +} + + +static enum GNUNET_GenericReturnValue +free_store_context(void *cls, + const struct GNUNET_PeerIdentity *key, + void *value) +{ + (void) cls; + + GNUNET_PEERSTORE_store_cancel ((struct GNUNET_PEERSTORE_StoreContext *) value); + return GNUNET_YES; // FIXME why is this a map anyway +} + + +void +GNUNET_PEERSTORE_hello_add_cancel (struct + GNUNET_PEERSTORE_StoreHelloContext *huc) +{ + GNUNET_PEERSTORE_watch_cancel (huc->wc); + GNUNET_CONTAINER_multipeermap_iterate (huc->store_context_map, + free_store_context, + NULL); + GNUNET_free (huc->hello); + GNUNET_free (huc->pid); + GNUNET_free (huc); +} + + +/* end of peerstore_api.c */ diff --git a/src/service/peerstore/peerstore_common.c b/src/service/peerstore/peerstore_common.c new file mode 100644 index 000000000..e3bb77d86 --- /dev/null +++ b/src/service/peerstore/peerstore_common.c @@ -0,0 +1,188 @@ +/* + 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 . + + SPDX-License-Identifier: AGPL3.0-or-later + */ +/** + * @file peerstore/peerstore_common.c + * @brief Helper peerstore functions + * @author Omar Tarabai + */ + +#include "platform.h" +#include "peerstore_common.h" + +/** + * Creates a hash of the given key combination + * + */ +void +PEERSTORE_hash_key (const char *sub_system, + const struct GNUNET_PeerIdentity *peer, + const char *key, + struct GNUNET_HashCode *ret) +{ + size_t sssize; + size_t psize; + size_t ksize; + size_t totalsize; + void *block; + void *blockptr; + + sssize = strlen (sub_system) + 1; + psize = sizeof(struct GNUNET_PeerIdentity); + ksize = strlen (key) + 1; + totalsize = sssize + psize + ksize; + block = GNUNET_malloc (totalsize); + blockptr = block; + GNUNET_memcpy (blockptr, sub_system, sssize); + blockptr += sssize; + GNUNET_memcpy (blockptr, peer, psize); + blockptr += psize; + GNUNET_memcpy (blockptr, key, ksize); + GNUNET_CRYPTO_hash (block, totalsize, ret); + GNUNET_free (block); +} + + +/** + * Creates a MQ envelope for a single record + * + * @param sub_system sub system string + * @param peer Peer identity (can be NULL) + * @param key record key string (can be NULL) + * @param value record value BLOB (can be NULL) + * @param value_size record value size in bytes (set to 0 if value is NULL) + * @param expiry time after which the record expires + * @param options options specific to the storage operation + * @param msg_type message type to be set in header + * @return pointer to record message struct + */ +struct GNUNET_MQ_Envelope * +PEERSTORE_create_record_mq_envelope (const char *sub_system, + const struct GNUNET_PeerIdentity *peer, + const char *key, + const void *value, + size_t value_size, + struct GNUNET_TIME_Absolute expiry, + enum GNUNET_PEERSTORE_StoreOption options, + uint16_t msg_type) +{ + struct StoreRecordMessage *srm; + struct GNUNET_MQ_Envelope *ev; + size_t ss_size; + size_t key_size; + size_t msg_size; + void *dummy; + + GNUNET_assert (NULL != sub_system); + ss_size = strlen (sub_system) + 1; + if (NULL == key) + key_size = 0; + else + key_size = strlen (key) + 1; + msg_size = ss_size + key_size + value_size; + ev = GNUNET_MQ_msg_extra (srm, msg_size, msg_type); + srm->key_size = htons (key_size); + srm->expiry = GNUNET_TIME_absolute_hton (expiry); + if (NULL == peer) + srm->peer_set = htons (GNUNET_NO); + else + { + srm->peer_set = htons (GNUNET_YES); + srm->peer = *peer; + } + srm->sub_system_size = htons (ss_size); + srm->value_size = htons (value_size); + srm->options = htonl (options); + dummy = &srm[1]; + GNUNET_memcpy (dummy, sub_system, ss_size); + dummy += ss_size; + GNUNET_memcpy (dummy, key, key_size); + dummy += key_size; + GNUNET_memcpy (dummy, value, value_size); + return ev; +} + + +struct GNUNET_PEERSTORE_Record * +PEERSTORE_parse_record_message (const struct StoreRecordMessage *srm) +{ + struct GNUNET_PEERSTORE_Record *record; + uint16_t req_size; + uint16_t ss_size; + uint16_t key_size; + uint16_t value_size; + char *dummy; + + req_size = ntohs (srm->header.size) - sizeof(*srm); + ss_size = ntohs (srm->sub_system_size); + key_size = ntohs (srm->key_size); + value_size = ntohs (srm->value_size); + if (ss_size + key_size + value_size != req_size) + { + GNUNET_break (0); + return NULL; + } + record = GNUNET_new (struct GNUNET_PEERSTORE_Record); + if (GNUNET_YES == ntohs (srm->peer_set)) + { + record->peer = srm->peer; + } + record->expiry = GNUNET_TIME_absolute_ntoh (srm->expiry); + dummy = (char *) &srm[1]; + if (ss_size > 0) + { + record->sub_system = GNUNET_strdup (dummy); + dummy += ss_size; + } + if (key_size > 0) + { + record->key = GNUNET_strdup (dummy); + dummy += key_size; + } + if (value_size > 0) + { + record->value = GNUNET_malloc (value_size); + GNUNET_memcpy (record->value, + dummy, + value_size); + } + record->value_size = value_size; + return record; +} + + +/** + * Free any memory allocated for this record + * + * @param record + */ +void +PEERSTORE_destroy_record (struct GNUNET_PEERSTORE_Record *record) +{ + if (NULL != record->sub_system) + GNUNET_free (record->sub_system); + if (NULL != record->key) + GNUNET_free (record->key); + if (NULL != record->value) + { + GNUNET_free (record->value); + record->value = 0; + } + GNUNET_free (record); +} diff --git a/src/service/peerstore/peerstore_common.h b/src/service/peerstore/peerstore_common.h new file mode 100644 index 000000000..f5352f5a5 --- /dev/null +++ b/src/service/peerstore/peerstore_common.h @@ -0,0 +1,82 @@ +/* + This file is part of GNUnet + Copyright (C) 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 . + + SPDX-License-Identifier: AGPL3.0-or-later + */ + +/** + * @file peerstore/peerstore_common.h + * @brief Helper peerstore functions + * @author Omar Tarabai + */ +#include "platform.h" +#include "peerstore.h" + +/** + * Creates a hash of the given key combination + * + */ +void +PEERSTORE_hash_key (const char *sub_system, + const struct GNUNET_PeerIdentity *peer, + const char *key, + struct GNUNET_HashCode *ret); + + +/** + * Creates a MQ envelope for a single record + * + * @param sub_system sub system string + * @param peer Peer identity (can be NULL) + * @param key record key string (can be NULL) + * @param value record value BLOB (can be NULL) + * @param value_size record value size in bytes (set to 0 if value is NULL) + * @param expiry time after which the record expires + * @param options options specific to the storage operation + * @param msg_type message type to be set in header + * @return pointer to record message struct + */ +struct GNUNET_MQ_Envelope * +PEERSTORE_create_record_mq_envelope (const char *sub_system, + const struct GNUNET_PeerIdentity *peer, + const char *key, + const void *value, + size_t value_size, + struct GNUNET_TIME_Absolute expiry, + enum GNUNET_PEERSTORE_StoreOption options, + uint16_t msg_type); + + +/** + * Parses a message carrying a record + * + * @param srm the actual message + * @return Pointer to record or NULL on error + */ +struct GNUNET_PEERSTORE_Record * +PEERSTORE_parse_record_message (const struct StoreRecordMessage *srm); + + +/** + * Free any memory allocated for this record + * + * @param record + */ +void +PEERSTORE_destroy_record (struct GNUNET_PEERSTORE_Record *record); + +/* end of peerstore_common.h */ diff --git a/src/service/peerstore/perf_peerstore_store.c b/src/service/peerstore/perf_peerstore_store.c new file mode 100644 index 000000000..24c7e4f01 --- /dev/null +++ b/src/service/peerstore/perf_peerstore_store.c @@ -0,0 +1,109 @@ +/* + This file is part of GNUnet. + Copyright (C) + + 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 . + + SPDX-License-Identifier: AGPL3.0-or-later + */ +/** + * @file peerstore/perf_peerstore_store.c + * @brief performance test for peerstore store operation + */ +#include "platform.h" +#include "gnunet_util_lib.h" +#include "gnunet_testing_lib.h" +#include "gnunet_peerstore_service.h" + +#define STORES 10000 + +static int ok = 1; + +static struct GNUNET_PEERSTORE_Handle *h; + +static char *ss = "test_peerstore_stress"; +static struct GNUNET_PeerIdentity p; +static char *k = "test_peerstore_stress_key"; +static char *v = "test_peerstore_stress_val"; + +static int count = 0; + +static void +disconnect () +{ + if (NULL != h) + GNUNET_PEERSTORE_disconnect (h, GNUNET_YES); + GNUNET_SCHEDULER_shutdown (); +} + + +static void +store () +{ + GNUNET_PEERSTORE_store (h, ss, &p, k, v, strlen (v) + 1, + GNUNET_TIME_UNIT_FOREVER_ABS, + (count == + 0) ? GNUNET_PEERSTORE_STOREOPTION_REPLACE : + GNUNET_PEERSTORE_STOREOPTION_MULTIPLE, NULL, NULL); + count++; +} + + +static void +watch_cb (void *cls, const struct GNUNET_PEERSTORE_Record *record, + const char *emsg) +{ + GNUNET_assert (NULL == emsg); + if (STORES == count) + { + ok = 0; + disconnect (); + } + else + store (); +} + + +static void +run (void *cls, const struct GNUNET_CONFIGURATION_Handle *cfg, + struct GNUNET_TESTING_Peer *peer) +{ + memset (&p, 5, sizeof(p)); + h = GNUNET_PEERSTORE_connect (cfg); + GNUNET_assert (NULL != h); + GNUNET_PEERSTORE_watch (h, ss, &p, k, &watch_cb, NULL); + store (); +} + + +int +main (int argc, char *argv[]) +{ + struct GNUNET_TIME_Absolute start; + struct GNUNET_TIME_Relative diff; + + start = GNUNET_TIME_absolute_get (); + if (0 != + GNUNET_TESTING_service_run ("perf-peerstore-store", "peerstore", + "test_peerstore_api_data.conf", &run, NULL)) + return 1; + diff = GNUNET_TIME_absolute_get_duration (start); + fprintf (stderr, "Stored and retrieved %d records in %s (%s).\n", STORES, + GNUNET_STRINGS_relative_time_to_string (diff, GNUNET_YES), + GNUNET_STRINGS_relative_time_to_string (diff, GNUNET_NO)); + return ok; +} + + +/* end of perf_peerstore_store.c */ diff --git a/src/service/peerstore/plugin_peerstore_flat.c b/src/service/peerstore/plugin_peerstore_flat.c new file mode 100644 index 000000000..cc5b8b76b --- /dev/null +++ b/src/service/peerstore/plugin_peerstore_flat.c @@ -0,0 +1,606 @@ +/* + * This file is part of GNUnet + * Copyright (C) 2015 Christian Grothoff (and other contributing authors) + * + * 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 . + + SPDX-License-Identifier: AGPL3.0-or-later + */ + +/** + * @file peerstore/plugin_peerstore_flat.c + * @brief flat file-based peerstore backend + * @author Martin Schanzenbach + */ + +#include "platform.h" +#include "gnunet_peerstore_plugin.h" +#include "gnunet_peerstore_service.h" +#include "peerstore.h" + +/** + * Context for all functions in this plugin. + */ +struct Plugin +{ + /** + * Configuration handle + */ + const struct GNUNET_CONFIGURATION_Handle *cfg; + + /** + * HashMap + */ + struct GNUNET_CONTAINER_MultiHashMap *hm; + + /** + * Iterator + */ + GNUNET_PEERSTORE_Processor iter; + + /** + * Iterator cls + */ + void *iter_cls; + + /** + * iterator key + */ + const char *iter_key; + + /** + * Iterator peer + */ + const struct GNUNET_PeerIdentity *iter_peer; + + /** + * Iterator subsystem + */ + const char *iter_sub_system; + + /** + * Iterator time + */ + struct GNUNET_TIME_Absolute iter_now; + + /** + * Deleted entries + */ + uint64_t deleted_entries; + + /** + * Expired entries + */ + uint64_t exp_changes; + + /** + * Database filename. + */ + char *fn; + + /** + * Result found bool + */ + int iter_result_found; +}; + + +static int +delete_entries (void *cls, + const struct GNUNET_HashCode *key, + void *value) +{ + struct Plugin *plugin = cls; + struct GNUNET_PEERSTORE_Record *entry = value; + + if (0 != strcmp (plugin->iter_key, entry->key)) + return GNUNET_YES; + if (0 != memcmp (plugin->iter_peer, + &entry->peer, + sizeof(struct GNUNET_PeerIdentity))) + return GNUNET_YES; + if (0 != strcmp (plugin->iter_sub_system, entry->sub_system)) + return GNUNET_YES; + + GNUNET_CONTAINER_multihashmap_remove (plugin->hm, key, value); + plugin->deleted_entries++; + return GNUNET_YES; +} + + +/** + * Delete records with the given key + * + * @param cls closure (internal context for the plugin) + * @param sub_system name of sub system + * @param peer Peer identity (can be NULL) + * @param key entry key string (can be NULL) + * @return number of deleted records + */ +static int +peerstore_flat_delete_records (void *cls, const char *sub_system, + const struct GNUNET_PeerIdentity *peer, + const char *key) +{ + struct Plugin *plugin = cls; + + plugin->iter_sub_system = sub_system; + plugin->iter_peer = peer; + plugin->iter_key = key; + plugin->deleted_entries = 0; + + GNUNET_CONTAINER_multihashmap_iterate (plugin->hm, + &delete_entries, + plugin); + return plugin->deleted_entries; +} + + +static int +expire_entries (void *cls, + const struct GNUNET_HashCode *key, + void *value) +{ + struct Plugin *plugin = cls; + struct GNUNET_PEERSTORE_Record *entry = value; + + if (entry->expiry.abs_value_us < plugin->iter_now.abs_value_us) + { + GNUNET_CONTAINER_multihashmap_remove (plugin->hm, key, value); + plugin->exp_changes++; + } + return GNUNET_YES; +} + + +/** + * Delete expired records (expiry < now) + * + * @param cls closure (internal context for the plugin) + * @param now time to use as reference + * @param cont continuation called with the number of records expired + * @param cont_cls continuation closure + * @return #GNUNET_OK on success, #GNUNET_SYSERR on error and cont is not + * called + */ +static int +peerstore_flat_expire_records (void *cls, struct GNUNET_TIME_Absolute now, + GNUNET_PEERSTORE_Continuation cont, + void *cont_cls) +{ + struct Plugin *plugin = cls; + + plugin->exp_changes = 0; + plugin->iter_now = now; + + GNUNET_CONTAINER_multihashmap_iterate (plugin->hm, + &expire_entries, + plugin); + if (NULL != cont) + { + cont (cont_cls, plugin->exp_changes); + } + return GNUNET_OK; +} + + +static int +iterate_entries (void *cls, + const struct GNUNET_HashCode *key, + void *value) +{ + struct Plugin *plugin = cls; + struct GNUNET_PEERSTORE_Record *entry = value; + + if ((NULL != plugin->iter_peer) && + (0 != memcmp (plugin->iter_peer, + &entry->peer, + sizeof(struct GNUNET_PeerIdentity)))) + { + return GNUNET_YES; + } + if ((NULL != plugin->iter_key) && + (0 != strcmp (plugin->iter_key, + entry->key))) + { + return GNUNET_YES; + } + if (NULL != plugin->iter) + plugin->iter (plugin->iter_cls, entry, NULL); + plugin->iter_result_found = GNUNET_YES; + return GNUNET_YES; +} + + +/** + * Iterate over the records given an optional peer id + * and/or key. + * + * @param cls closure (internal context for the plugin) + * @param sub_system name of sub system + * @param peer Peer identity (can be NULL) + * @param key entry key string (can be NULL) + * @param iter function to call asynchronously with the results, terminated + * by a NULL result + * @param iter_cls closure for @a iter + * @return #GNUNET_OK on success, #GNUNET_SYSERR on error and iter is not + * called + */ +static int +peerstore_flat_iterate_records (void *cls, const char *sub_system, + const struct GNUNET_PeerIdentity *peer, + const char *key, + GNUNET_PEERSTORE_Processor iter, + void *iter_cls) +{ + struct Plugin *plugin = cls; + + plugin->iter = iter; + plugin->iter_cls = iter_cls; + plugin->iter_peer = peer; + plugin->iter_sub_system = sub_system; + plugin->iter_key = key; + + GNUNET_CONTAINER_multihashmap_iterate (plugin->hm, + &iterate_entries, + plugin); + if (NULL != iter) + iter (iter_cls, NULL, NULL); + return GNUNET_OK; +} + + +/** + * Store a record in the peerstore. + * Key is the combination of sub system and peer identity. + * One key can store multiple values. + * + * @param cls closure (internal context for the plugin) + * @param sub_system name of the GNUnet sub system responsible + * @param peer peer identity + * @param key record key string + * @param value value to be stored + * @param size size of value to be stored + * @param expiry absolute time after which the record is (possibly) deleted + * @param options options related to the store operation + * @param cont continuation called when record is stored + * @param cont_cls continuation closure + * @return #GNUNET_OK on success, else #GNUNET_SYSERR and cont is not called + */ +static int +peerstore_flat_store_record (void *cls, const char *sub_system, + const struct GNUNET_PeerIdentity *peer, + const char *key, const void *value, size_t size, + struct GNUNET_TIME_Absolute expiry, + enum GNUNET_PEERSTORE_StoreOption options, + GNUNET_PEERSTORE_Continuation cont, + void *cont_cls) +{ + struct Plugin *plugin = cls; + struct GNUNET_HashCode hkey; + struct GNUNET_PEERSTORE_Record *entry; + const char *peer_id; + + + entry = GNUNET_new (struct GNUNET_PEERSTORE_Record); + entry->sub_system = GNUNET_strdup (sub_system); + entry->key = GNUNET_strdup (key); + entry->value = GNUNET_malloc (size); + GNUNET_memcpy (entry->value, value, size); + entry->value_size = size; + entry->peer = *peer; + entry->expiry = expiry; + + peer_id = GNUNET_i2s (peer); + GNUNET_CRYPTO_hash (peer_id, + strlen (peer_id), + &hkey); + + if (GNUNET_PEERSTORE_STOREOPTION_REPLACE == options) + { + peerstore_flat_delete_records (cls, sub_system, peer, key); + } + + GNUNET_CONTAINER_multihashmap_put (plugin->hm, + &hkey, + entry, + GNUNET_CONTAINER_MULTIHASHMAPOPTION_MULTIPLE); + if (NULL != cont) + { + cont (cont_cls, GNUNET_OK); + } + return GNUNET_OK; +} + + +/** + * Initialize the database connections and associated + * data structures (create tables and indices + * as needed as well). + * + * @param plugin the plugin context (state for this module) + * @return GNUNET_OK on success + */ +static int +database_setup (struct Plugin *plugin) +{ + char *afsdir; + char *key; + char *sub_system; + const char *peer_id; + char *peer; + char *value; + char *expiry; + struct GNUNET_DISK_FileHandle *fh; + struct GNUNET_PEERSTORE_Record *entry; + struct GNUNET_HashCode hkey; + uint64_t size; + char *buffer; + char *line; + + if (GNUNET_OK != + GNUNET_CONFIGURATION_get_value_filename (plugin->cfg, "peerstore-flat", + "FILENAME", &afsdir)) + { + GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR, "peerstore-flat", + "FILENAME"); + return GNUNET_SYSERR; + } + if (GNUNET_OK != GNUNET_DISK_file_test (afsdir)) + { + if (GNUNET_OK != GNUNET_DISK_directory_create_for_file (afsdir)) + { + GNUNET_break (0); + GNUNET_free (afsdir); + return GNUNET_SYSERR; + } + } + /* afsdir should be UTF-8-encoded. If it isn't, it's a bug */ + plugin->fn = afsdir; + + fh = GNUNET_DISK_file_open (afsdir, + GNUNET_DISK_OPEN_CREATE + | GNUNET_DISK_OPEN_READWRITE, + GNUNET_DISK_PERM_USER_WRITE + | GNUNET_DISK_PERM_USER_READ); + if (NULL == fh) + { + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + _ ("Unable to initialize file: %s.\n"), + afsdir); + return GNUNET_SYSERR; + } + + /* Load data from file into hashmap */ + plugin->hm = GNUNET_CONTAINER_multihashmap_create (10, + GNUNET_NO); + + if (GNUNET_SYSERR == GNUNET_DISK_file_size (afsdir, + &size, + GNUNET_YES, + GNUNET_YES)) + { + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + _ ("Unable to get filesize: %s.\n"), + afsdir); + return GNUNET_SYSERR; + } + + buffer = GNUNET_malloc (size + 1); + + if (GNUNET_SYSERR == GNUNET_DISK_file_read (fh, + buffer, + size)) + { + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + _ ("Unable to read file: %s.\n"), + afsdir); + GNUNET_DISK_file_close (fh); + GNUNET_free (buffer); + return GNUNET_SYSERR; + } + + buffer[size] = '\0'; + GNUNET_DISK_file_close (fh); + if (0 < size) + { + line = strtok (buffer, "\n"); + while (line != NULL) + { + sub_system = strtok (line, ","); + if (NULL == sub_system) + break; + peer = strtok (NULL, ","); + if (NULL == peer) + break; + key = strtok (NULL, ","); + if (NULL == key) + break; + value = strtok (NULL, ","); + if (NULL == value) + break; + expiry = strtok (NULL, ","); + if (NULL == expiry) + break; + entry = GNUNET_new (struct GNUNET_PEERSTORE_Record); + entry->sub_system = GNUNET_strdup (sub_system); + entry->key = GNUNET_strdup (key); + { + size_t s; + char *o; + + o = NULL; + s = GNUNET_STRINGS_base64_decode (peer, + strlen (peer), + (void **) &o); + if (sizeof(struct GNUNET_PeerIdentity) == s) + GNUNET_memcpy (&entry->peer, + o, + s); + else + GNUNET_break (0); + GNUNET_free (o); + } + entry->value_size = GNUNET_STRINGS_base64_decode (value, + strlen (value), + (void **) &entry->value); + if (GNUNET_SYSERR == + GNUNET_STRINGS_fancy_time_to_absolute (expiry, + &entry->expiry)) + { + GNUNET_free (entry->sub_system); + GNUNET_free (entry->key); + GNUNET_free (entry); + break; + } + peer_id = GNUNET_i2s (&entry->peer); + GNUNET_CRYPTO_hash (peer_id, + strlen (peer_id), + &hkey); + + GNUNET_assert (GNUNET_OK == GNUNET_CONTAINER_multihashmap_put (plugin->hm, + &hkey, + entry, + GNUNET_CONTAINER_MULTIHASHMAPOPTION_MULTIPLE)); + } + } + GNUNET_free (buffer); + return GNUNET_OK; +} + + +static int +store_and_free_entries (void *cls, + const struct GNUNET_HashCode *key, + void *value) +{ + struct GNUNET_DISK_FileHandle *fh = cls; + struct GNUNET_PEERSTORE_Record *entry = value; + char *line; + char *peer; + const char *expiry; + char *val; + + GNUNET_STRINGS_base64_encode (entry->value, + entry->value_size, + &val); + expiry = GNUNET_STRINGS_absolute_time_to_string (entry->expiry); + GNUNET_STRINGS_base64_encode ((char *) &entry->peer, + sizeof(struct GNUNET_PeerIdentity), + &peer); + GNUNET_asprintf (&line, + "%s,%s,%s,%s,%s", + entry->sub_system, + peer, + entry->key, + val, + expiry); + GNUNET_free (val); + GNUNET_free (peer); + GNUNET_DISK_file_write (fh, + line, + strlen (line)); + GNUNET_free (entry->sub_system); + GNUNET_free (entry->key); + GNUNET_free (entry->value); + GNUNET_free (entry); + GNUNET_free (line); + return GNUNET_YES; +} + + +/** + * Shutdown database connection and associate data + * structures. + * @param plugin the plugin context (state for this module) + */ +static void +database_shutdown (struct Plugin *plugin) +{ + struct GNUNET_DISK_FileHandle *fh; + + fh = GNUNET_DISK_file_open (plugin->fn, + GNUNET_DISK_OPEN_CREATE + | GNUNET_DISK_OPEN_TRUNCATE + | GNUNET_DISK_OPEN_READWRITE, + GNUNET_DISK_PERM_USER_WRITE + | GNUNET_DISK_PERM_USER_READ); + if (NULL == fh) + { + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + _ ("Unable to initialize file: %s.\n"), + plugin->fn); + return; + } + GNUNET_CONTAINER_multihashmap_iterate (plugin->hm, + &store_and_free_entries, + fh); + GNUNET_CONTAINER_multihashmap_destroy (plugin->hm); + GNUNET_DISK_file_close (fh); +} + + +/** + * Entry point for the plugin. + * + * @param cls The struct GNUNET_CONFIGURATION_Handle. + * @return NULL on error, otherwise the plugin context + */ +void * +libgnunet_plugin_peerstore_flat_init (void *cls) +{ + static struct Plugin plugin; + const struct GNUNET_CONFIGURATION_Handle *cfg = cls; + struct GNUNET_PEERSTORE_PluginFunctions *api; + + if (NULL != plugin.cfg) + return NULL; /* can only initialize once! */ + memset (&plugin, 0, sizeof(struct Plugin)); + plugin.cfg = cfg; + if (GNUNET_OK != database_setup (&plugin)) + { + database_shutdown (&plugin); + return NULL; + } + api = GNUNET_new (struct GNUNET_PEERSTORE_PluginFunctions); + api->cls = &plugin; + api->store_record = &peerstore_flat_store_record; + api->iterate_records = &peerstore_flat_iterate_records; + api->expire_records = &peerstore_flat_expire_records; + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Flat plugin is running\n"); + return api; +} + + +/** + * Exit point from the plugin. + * + * @param cls The plugin context (as returned by "init") + * @return Always NULL + */ +void * +libgnunet_plugin_peerstore_flat_done (void *cls) +{ + struct GNUNET_PEERSTORE_PluginFunctions *api = cls; + struct Plugin *plugin = api->cls; + + database_shutdown (plugin); + plugin->cfg = NULL; + GNUNET_free (api); + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Flat plugin is finished\n"); + return NULL; +} + + +/* end of plugin_peerstore_sqlite.c */ diff --git a/src/service/peerstore/plugin_peerstore_sqlite.c b/src/service/peerstore/plugin_peerstore_sqlite.c new file mode 100644 index 000000000..ad69efdf4 --- /dev/null +++ b/src/service/peerstore/plugin_peerstore_sqlite.c @@ -0,0 +1,702 @@ +/* + * This file is part of GNUnet + * Copyright (C) 2013, 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 . + + SPDX-License-Identifier: AGPL3.0-or-later + */ + +/** + * @file peerstore/plugin_peerstore_sqlite.c + * @brief sqlite-based peerstore backend + * @author Omar Tarabai + * @author Christian Grothoff + */ + +#include "platform.h" +#include "gnunet_peerstore_plugin.h" +#include "gnunet_peerstore_service.h" +#include "gnunet_sq_lib.h" +#include "peerstore.h" +#include + +/** + * After how many ms "busy" should a DB operation fail for good? A + * low value makes sure that we are more responsive to requests + * (especially PUTs). A high value guarantees a higher success rate + * (SELECTs in iterate can take several seconds despite LIMIT=1). + * + * The default value of 1s should ensure that users do not experience + * huge latencies while at the same time allowing operations to + * succeed with reasonable probability. + */ +#define BUSY_TIMEOUT_MS 1000 + +/** + * Log an error message at log-level 'level' that indicates + * a failure of the command 'cmd' on file 'filename' + * with the message given by strerror(errno). + */ +#define LOG_SQLITE(db, level, cmd) do { GNUNET_log_from (level, \ + "peerstore-sqlite", _ ( \ + "`%s' failed at %s:%d with error: %s\n"), \ + cmd, \ + __FILE__, __LINE__, \ + sqlite3_errmsg ( \ + db->dbh)); \ +} while (0) + +#define LOG(kind, ...) GNUNET_log_from (kind, "peerstore-sqlite", __VA_ARGS__) + +/** + * Context for all functions in this plugin. + */ +struct Plugin +{ + /** + * Configuration handle + */ + const struct GNUNET_CONFIGURATION_Handle *cfg; + + /** + * Database filename. + */ + char *fn; + + /** + * Native SQLite database handle. + */ + sqlite3 *dbh; + + /** + * Precompiled SQL for inserting into peerstoredata + */ + sqlite3_stmt *insert_peerstoredata; + + /** + * Precompiled SQL for selecting from peerstoredata + */ + sqlite3_stmt *select_peerstoredata; + + /** + * Precompiled SQL for selecting from peerstoredata + */ + sqlite3_stmt *select_peerstoredata_by_pid; + + /** + * Precompiled SQL for selecting from peerstoredata + */ + sqlite3_stmt *select_peerstoredata_by_key; + + /** + * Precompiled SQL for selecting from peerstoredata + */ + sqlite3_stmt *select_peerstoredata_by_all; + + /** + * Precompiled SQL for deleting expired + * records from peerstoredata + */ + sqlite3_stmt *expire_peerstoredata; + + /** + * Precompiled SQL for deleting records + * with given key + */ + sqlite3_stmt *delete_peerstoredata; +}; + + +/** + * Delete records with the given key + * + * @param cls closure (internal context for the plugin) + * @param sub_system name of sub system + * @param peer Peer identity (can be NULL) + * @param key entry key string (can be NULL) + * @return number of deleted records, #GNUNE_SYSERR on error + */ +static int +peerstore_sqlite_delete_records (void *cls, + const char *sub_system, + const struct GNUNET_PeerIdentity *peer, + const char *key) +{ + struct Plugin *plugin = cls; + sqlite3_stmt *stmt = plugin->delete_peerstoredata; + struct GNUNET_SQ_QueryParam params[] = { + GNUNET_SQ_query_param_string (sub_system), + GNUNET_SQ_query_param_auto_from_type (peer), + GNUNET_SQ_query_param_string (key), + GNUNET_SQ_query_param_end + }; + int ret; + + if (GNUNET_OK != + GNUNET_SQ_bind (stmt, + params)) + { + LOG_SQLITE (plugin, + GNUNET_ERROR_TYPE_ERROR | GNUNET_ERROR_TYPE_BULK, + "sqlite3_bind"); + GNUNET_SQ_reset (plugin->dbh, + stmt); + return GNUNET_SYSERR; + } + if (SQLITE_DONE != + sqlite3_step (stmt)) + { + LOG_SQLITE (plugin, + GNUNET_ERROR_TYPE_ERROR | GNUNET_ERROR_TYPE_BULK, + "sqlite3_step"); + ret = GNUNET_SYSERR; + } + else + { + ret = sqlite3_changes (plugin->dbh); + } + GNUNET_SQ_reset (plugin->dbh, + stmt); + return ret; +} + + +/** + * Delete expired records (expiry < now) + * + * @param cls closure (internal context for the plugin) + * @param now time to use as reference + * @param cont continuation called with the number of records expired + * @param cont_cls continuation closure + * @return #GNUNET_OK on success, #GNUNET_SYSERR on error and cont is not + * called + */ +static int +peerstore_sqlite_expire_records (void *cls, struct GNUNET_TIME_Absolute now, + GNUNET_PEERSTORE_Continuation cont, + void *cont_cls) +{ + struct Plugin *plugin = cls; + sqlite3_stmt *stmt = plugin->expire_peerstoredata; + struct GNUNET_SQ_QueryParam params[] = { + GNUNET_SQ_query_param_absolute_time (&now), + GNUNET_SQ_query_param_end + }; + + if (GNUNET_OK != + GNUNET_SQ_bind (stmt, + params)) + { + LOG_SQLITE (plugin, + GNUNET_ERROR_TYPE_ERROR | GNUNET_ERROR_TYPE_BULK, + "sqlite3_bind"); + GNUNET_SQ_reset (plugin->dbh, + stmt); + return GNUNET_SYSERR; + } + if (SQLITE_DONE != sqlite3_step (stmt)) + { + LOG_SQLITE (plugin, + GNUNET_ERROR_TYPE_ERROR | GNUNET_ERROR_TYPE_BULK, + "sqlite3_step"); + GNUNET_SQ_reset (plugin->dbh, + stmt); + return GNUNET_SYSERR; + } + if (NULL != cont) + cont (cont_cls, + sqlite3_changes (plugin->dbh)); + GNUNET_SQ_reset (plugin->dbh, + stmt); + return GNUNET_OK; +} + + +/** + * Iterate over the records given an optional peer id + * and/or key. + * + * @param cls closure (internal context for the plugin) + * @param sub_system name of sub system + * @param peer Peer identity (can be NULL) + * @param key entry key string (can be NULL) + * @param iter function to call asynchronously with the results, terminated + * by a NULL result + * @param iter_cls closure for @a iter + * @return #GNUNET_OK on success, #GNUNET_SYSERR on error and iter is not + * called + */ +static int +peerstore_sqlite_iterate_records (void *cls, + const char *sub_system, + const struct GNUNET_PeerIdentity *peer, + const char *key, + GNUNET_PEERSTORE_Processor iter, + void *iter_cls) +{ + struct Plugin *plugin = cls; + sqlite3_stmt *stmt; + int err = 0; + int sret; + struct GNUNET_PEERSTORE_Record rec; + + LOG (GNUNET_ERROR_TYPE_DEBUG, + "Executing iterate request on sqlite db.\n"); + if (NULL == peer) + { + if (NULL == key) + { + struct GNUNET_SQ_QueryParam params[] = { + GNUNET_SQ_query_param_string (sub_system), + GNUNET_SQ_query_param_end + }; + + stmt = plugin->select_peerstoredata; + err = GNUNET_SQ_bind (stmt, + params); + } + else + { + struct GNUNET_SQ_QueryParam params[] = { + GNUNET_SQ_query_param_string (sub_system), + GNUNET_SQ_query_param_string (key), + GNUNET_SQ_query_param_end + }; + + stmt = plugin->select_peerstoredata_by_key; + err = GNUNET_SQ_bind (stmt, + params); + } + } + else + { + if (NULL == key) + { + struct GNUNET_SQ_QueryParam params[] = { + GNUNET_SQ_query_param_string (sub_system), + GNUNET_SQ_query_param_auto_from_type (peer), + GNUNET_SQ_query_param_end + }; + + stmt = plugin->select_peerstoredata_by_pid; + err = GNUNET_SQ_bind (stmt, + params); + } + else + { + struct GNUNET_SQ_QueryParam params[] = { + GNUNET_SQ_query_param_string (sub_system), + GNUNET_SQ_query_param_auto_from_type (peer), + GNUNET_SQ_query_param_string (key), + GNUNET_SQ_query_param_end + }; + + stmt = plugin->select_peerstoredata_by_all; + err = GNUNET_SQ_bind (stmt, + params); + } + } + + if (GNUNET_OK != err) + { + LOG_SQLITE (plugin, + GNUNET_ERROR_TYPE_ERROR | GNUNET_ERROR_TYPE_BULK, + "sqlite3_bind_XXXX"); + GNUNET_SQ_reset (plugin->dbh, + stmt); + return GNUNET_SYSERR; + } + + err = 0; + while (SQLITE_ROW == (sret = sqlite3_step (stmt))) + { + LOG (GNUNET_ERROR_TYPE_DEBUG, + "Returning a matched record.\n"); + struct GNUNET_SQ_ResultSpec rs[] = { + GNUNET_SQ_result_spec_string (&rec.sub_system), + GNUNET_SQ_result_spec_auto_from_type (&rec.peer), + GNUNET_SQ_result_spec_string (&rec.key), + GNUNET_SQ_result_spec_variable_size (&rec.value, &rec.value_size), + GNUNET_SQ_result_spec_absolute_time (&rec.expiry), + GNUNET_SQ_result_spec_end + }; + + if (GNUNET_OK != + GNUNET_SQ_extract_result (stmt, + rs)) + { + GNUNET_break (0); + break; + } + if (NULL != iter) + iter (iter_cls, + &rec, + NULL); + GNUNET_SQ_cleanup_result (rs); + } + if (SQLITE_DONE != sret) + { + LOG_SQLITE (plugin, + GNUNET_ERROR_TYPE_ERROR, + "sqlite_step"); + err = 1; + } + GNUNET_SQ_reset (plugin->dbh, + stmt); + if (NULL != iter) + iter (iter_cls, + NULL, + err ? "sqlite error" : NULL); + return GNUNET_OK; +} + + +/** + * Store a record in the peerstore. + * Key is the combination of sub system and peer identity. + * One key can store multiple values. + * + * @param cls closure (internal context for the plugin) + * @param sub_system name of the GNUnet sub system responsible + * @param peer peer identity + * @param key record key string + * @param value value to be stored + * @param size size of value to be stored + * @param expiry absolute time after which the record is (possibly) deleted + * @param options options related to the store operation + * @param cont continuation called when record is stored + * @param cont_cls continuation closure + * @return #GNUNET_OK on success, else #GNUNET_SYSERR and cont is not called + */ +static int +peerstore_sqlite_store_record (void *cls, + const char *sub_system, + const struct GNUNET_PeerIdentity *peer, + const char *key, + const void *value, + size_t size, + struct GNUNET_TIME_Absolute expiry, + enum GNUNET_PEERSTORE_StoreOption options, + GNUNET_PEERSTORE_Continuation cont, + void *cont_cls) +{ + struct Plugin *plugin = cls; + sqlite3_stmt *stmt = plugin->insert_peerstoredata; + struct GNUNET_SQ_QueryParam params[] = { + GNUNET_SQ_query_param_string (sub_system), + GNUNET_SQ_query_param_auto_from_type (peer), + GNUNET_SQ_query_param_string (key), + GNUNET_SQ_query_param_fixed_size (value, size), + GNUNET_SQ_query_param_absolute_time (&expiry), + GNUNET_SQ_query_param_end + }; + + if (GNUNET_PEERSTORE_STOREOPTION_REPLACE == options) + { + peerstore_sqlite_delete_records (cls, + sub_system, + peer, + key); + } + if (GNUNET_OK != + GNUNET_SQ_bind (stmt, + params)) + LOG_SQLITE (plugin, + GNUNET_ERROR_TYPE_ERROR | GNUNET_ERROR_TYPE_BULK, + "sqlite3_bind"); + else if (SQLITE_DONE != sqlite3_step (stmt)) + { + LOG_SQLITE (plugin, + GNUNET_ERROR_TYPE_ERROR | GNUNET_ERROR_TYPE_BULK, + "sqlite3_step"); + } + GNUNET_SQ_reset (plugin->dbh, + stmt); + if (NULL != cont) + cont (cont_cls, + GNUNET_OK); + return GNUNET_OK; +} + + +/** + * @brief Prepare a SQL statement + * + * @param dbh handle to the database + * @param sql SQL statement, UTF-8 encoded + * @return 0 on success + */ +static int +sql_exec (sqlite3 *dbh, + const char *sql) +{ + int result; + + result = sqlite3_exec (dbh, + sql, + NULL, + NULL, + NULL); + LOG (GNUNET_ERROR_TYPE_DEBUG, + "Executed `%s' / %d\n", + sql, + result); + if (SQLITE_OK != result) + LOG (GNUNET_ERROR_TYPE_ERROR, + _ ("Error executing SQL query: %s\n %s\n"), + sqlite3_errmsg (dbh), + sql); + return result; +} + + +/** + * @brief Prepare a SQL statement + * + * @param dbh handle to the database + * @param sql SQL statement, UTF-8 encoded + * @param stmt set to the prepared statement + * @return 0 on success + */ +static int +sql_prepare (sqlite3 *dbh, + const char *sql, + sqlite3_stmt **stmt) +{ + char *tail; + int result; + + result = sqlite3_prepare_v2 (dbh, + sql, + strlen (sql), + stmt, + (const char **) &tail); + LOG (GNUNET_ERROR_TYPE_DEBUG, + "Prepared `%s' / %p: %d\n", + sql, + *stmt, + result); + if (SQLITE_OK != result) + LOG (GNUNET_ERROR_TYPE_ERROR, + _ ("Error preparing SQL query: %s\n %s\n"), + sqlite3_errmsg (dbh), + sql); + return result; +} + + +/** + * Initialize the database connections and associated + * data structures (create tables and indices + * as needed as well). + * + * @param plugin the plugin context (state for this module) + * @return #GNUNET_OK on success + */ +static int +database_setup (struct Plugin *plugin) +{ + char *filename; + + if (GNUNET_OK != + GNUNET_CONFIGURATION_get_value_filename (plugin->cfg, + "peerstore-sqlite", + "FILENAME", + &filename)) + { + GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR, + "peerstore-sqlite", + "FILENAME"); + return GNUNET_SYSERR; + } + if (GNUNET_OK != GNUNET_DISK_file_test (filename)) + { + if (GNUNET_OK != GNUNET_DISK_directory_create_for_file (filename)) + { + GNUNET_break (0); + GNUNET_free (filename); + return GNUNET_SYSERR; + } + } + /* filename should be UTF-8-encoded. If it isn't, it's a bug */ + plugin->fn = filename; + /* Open database and precompile statements */ + if (SQLITE_OK != sqlite3_open (plugin->fn, + &plugin->dbh)) + { + LOG (GNUNET_ERROR_TYPE_ERROR, + _ ("Unable to initialize SQLite: %s.\n"), + sqlite3_errmsg (plugin->dbh)); + return GNUNET_SYSERR; + } + sql_exec (plugin->dbh, + "PRAGMA temp_store=MEMORY"); + sql_exec (plugin->dbh, + "PRAGMA synchronous=OFF"); + sql_exec (plugin->dbh, + "PRAGMA legacy_file_format=OFF"); + sql_exec (plugin->dbh, + "PRAGMA auto_vacuum=INCREMENTAL"); + sql_exec (plugin->dbh, + "PRAGMA encoding=\"UTF-8\""); + sql_exec (plugin->dbh, + "PRAGMA page_size=4096"); + sqlite3_busy_timeout (plugin->dbh, + BUSY_TIMEOUT_MS); + /* Create tables */ + sql_exec (plugin->dbh, + "CREATE TABLE IF NOT EXISTS peerstoredata (\n" + " sub_system TEXT NOT NULL,\n" + " peer_id BLOB NOT NULL,\n" + " key TEXT NOT NULL,\n" + " value BLOB NULL,\n" + " expiry INT8 NOT NULL" ");"); + /* Create Indices */ + if (SQLITE_OK != + sqlite3_exec (plugin->dbh, + "CREATE INDEX IF NOT EXISTS peerstoredata_key_index ON peerstoredata (sub_system, peer_id, key)", + NULL, + NULL, + NULL)) + { + LOG (GNUNET_ERROR_TYPE_ERROR, + _ ("Unable to create indices: %s.\n"), + sqlite3_errmsg (plugin->dbh)); + return GNUNET_SYSERR; + } + /* Prepare statements */ + + sql_prepare (plugin->dbh, + "INSERT INTO peerstoredata (sub_system, peer_id, key, value, expiry)" + " VALUES (?,?,?,?,?);", + &plugin->insert_peerstoredata); + sql_prepare (plugin->dbh, + "SELECT sub_system,peer_id,key,value,expiry FROM peerstoredata" + " WHERE sub_system = ?", + &plugin->select_peerstoredata); + sql_prepare (plugin->dbh, + "SELECT sub_system,peer_id,key,value,expiry FROM peerstoredata" + " WHERE sub_system = ?" + " AND peer_id = ?", + &plugin->select_peerstoredata_by_pid); + sql_prepare (plugin->dbh, + "SELECT sub_system,peer_id,key,value,expiry FROM peerstoredata" + " WHERE sub_system = ?" + " AND key = ?", + &plugin->select_peerstoredata_by_key); + sql_prepare (plugin->dbh, + "SELECT sub_system,peer_id,key,value,expiry FROM peerstoredata" + " WHERE sub_system = ?" + " AND peer_id = ?" " AND key = ?", + &plugin->select_peerstoredata_by_all); + sql_prepare (plugin->dbh, + "DELETE FROM peerstoredata" + " WHERE expiry < ?", + &plugin->expire_peerstoredata); + sql_prepare (plugin->dbh, + "DELETE FROM peerstoredata" + " WHERE sub_system = ?" + " AND peer_id = ?" " AND key = ?", + &plugin->delete_peerstoredata); + return GNUNET_OK; +} + + +/** + * Shutdown database connection and associate data + * structures. + * @param plugin the plugin context (state for this module) + */ +static void +database_shutdown (struct Plugin *plugin) +{ + int result; + sqlite3_stmt *stmt; + + while (NULL != (stmt = sqlite3_next_stmt (plugin->dbh, + NULL))) + { + result = sqlite3_finalize (stmt); + if (SQLITE_OK != result) + LOG (GNUNET_ERROR_TYPE_WARNING, + "Failed to close statement %p: %d\n", + stmt, + result); + } + if (SQLITE_OK != sqlite3_close (plugin->dbh)) + LOG_SQLITE (plugin, + GNUNET_ERROR_TYPE_ERROR, + "sqlite3_close"); + GNUNET_free (plugin->fn); +} + + +/** + * Entry point for the plugin. + * + * @param cls The struct GNUNET_CONFIGURATION_Handle. + * @return NULL on error, otherwise the plugin context + */ +void * +libgnunet_plugin_peerstore_sqlite_init (void *cls) +{ + static struct Plugin plugin; + const struct GNUNET_CONFIGURATION_Handle *cfg = cls; + struct GNUNET_PEERSTORE_PluginFunctions *api; + + if (NULL != plugin.cfg) + return NULL; /* can only initialize once! */ + memset (&plugin, + 0, + sizeof(struct Plugin)); + plugin.cfg = cfg; + if (GNUNET_OK != database_setup (&plugin)) + { + database_shutdown (&plugin); + return NULL; + } + api = GNUNET_new (struct GNUNET_PEERSTORE_PluginFunctions); + api->cls = &plugin; + api->store_record = &peerstore_sqlite_store_record; + api->iterate_records = &peerstore_sqlite_iterate_records; + api->expire_records = &peerstore_sqlite_expire_records; + LOG (GNUNET_ERROR_TYPE_DEBUG, + "Sqlite plugin is running\n"); + return api; +} + + +/** + * Exit point from the plugin. + * + * @param cls The plugin context (as returned by "init") + * @return Always NULL + */ +void * +libgnunet_plugin_peerstore_sqlite_done (void *cls) +{ + struct GNUNET_PEERSTORE_PluginFunctions *api = cls; + struct Plugin *plugin = api->cls; + + database_shutdown (plugin); + plugin->cfg = NULL; + GNUNET_free (api); + LOG (GNUNET_ERROR_TYPE_DEBUG, + "Sqlite plugin is finished\n"); + return NULL; +} + + +/* end of plugin_peerstore_sqlite.c */ diff --git a/src/service/peerstore/test_peerstore_api_data.conf b/src/service/peerstore/test_peerstore_api_data.conf new file mode 100644 index 000000000..614d0cf5b --- /dev/null +++ b/src/service/peerstore/test_peerstore_api_data.conf @@ -0,0 +1,14 @@ +[PATHS] +GNUNET_TEST_HOME = $GNUNET_TMP/test-gnunet-peerstore + +[peerstore] +START_ON_DEMAND = YES +BINARY = gnunet-service-peerstore +UNIXPATH = $GNUNET_TMP/gnunet-service-peerstore.sock +DATABASE = sqlite +UNIX_MATCH_UID = NO +UNIX_MATCH_GID = YES +#PREFIX = xterm -e gdb --args + +[peerstore-sqlite] +FILENAME = $GNUNET_TEST_HOME/gnunet-peerstore-sqlite.db diff --git a/src/service/peerstore/test_peerstore_api_iterate.c b/src/service/peerstore/test_peerstore_api_iterate.c new file mode 100644 index 000000000..b6cd51906 --- /dev/null +++ b/src/service/peerstore/test_peerstore_api_iterate.c @@ -0,0 +1,176 @@ +/* + This file is part of GNUnet. + Copyright (C) 2013-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 . + + SPDX-License-Identifier: AGPL3.0-or-later + */ +/** + * @file peerstore/test_peerstore_api_iterate.c + * @brief testcase for peerstore iteration operation + */ +#include "platform.h" +#include "gnunet_util_lib.h" +#include "gnunet_testing_lib.h" +#include "gnunet_peerstore_service.h" + +static int ok = 1; + +static struct GNUNET_PEERSTORE_Handle *h; +static struct GNUNET_PEERSTORE_IterateContext *ic; + +static char *ss = "test_peerstore_api_iterate"; +static struct GNUNET_PeerIdentity p1; +static struct GNUNET_PeerIdentity p2; +static char *k1 = "test_peerstore_api_iterate_key1"; +static char *k2 = "test_peerstore_api_iterate_key2"; +static char *k3 = "test_peerstore_api_iterate_key3"; +static char *val = "test_peerstore_api_iterate_val"; +static int count = 0; + + +static void +iter3_cb (void *cls, + const struct GNUNET_PEERSTORE_Record *record, + const char *emsg) +{ + if (NULL != emsg) + { + GNUNET_PEERSTORE_iterate_cancel (ic); + return; + } + if (NULL != record) + { + count++; + return; + } + GNUNET_assert (count == 3); + ok = 0; + GNUNET_PEERSTORE_disconnect (h, GNUNET_NO); + GNUNET_SCHEDULER_shutdown (); +} + + +static void +iter2_cb (void *cls, + const struct GNUNET_PEERSTORE_Record *record, + const char *emsg) +{ + if (NULL != emsg) + { + GNUNET_PEERSTORE_iterate_cancel (ic); + return; + } + if (NULL != record) + { + count++; + return; + } + GNUNET_assert (count == 2); + count = 0; + ic = GNUNET_PEERSTORE_iterate (h, + ss, + NULL, + NULL, + &iter3_cb, + NULL); +} + + +static void +iter1_cb (void *cls, + const struct GNUNET_PEERSTORE_Record *record, + const char *emsg) +{ + if (NULL != emsg) + { + GNUNET_PEERSTORE_iterate_cancel (ic); + return; + } + if (NULL != record) + { + count++; + return; + } + GNUNET_assert (count == 1); + count = 0; + ic = GNUNET_PEERSTORE_iterate (h, + ss, + &p1, + NULL, + &iter2_cb, + NULL); +} + + +static void +run (void *cls, + const struct GNUNET_CONFIGURATION_Handle *cfg, + struct GNUNET_TESTING_Peer *peer) +{ + h = GNUNET_PEERSTORE_connect (cfg); + GNUNET_assert (NULL != h); + memset (&p1, 1, sizeof(p1)); + memset (&p2, 2, sizeof(p2)); + GNUNET_PEERSTORE_store (h, + ss, + &p1, + k1, + val, + strlen (val) + 1, + GNUNET_TIME_UNIT_FOREVER_ABS, + GNUNET_PEERSTORE_STOREOPTION_REPLACE, + NULL, + NULL); + GNUNET_PEERSTORE_store (h, + ss, + &p1, + k2, + val, + strlen (val) + 1, + GNUNET_TIME_UNIT_FOREVER_ABS, + GNUNET_PEERSTORE_STOREOPTION_REPLACE, + NULL, + NULL); + GNUNET_PEERSTORE_store (h, + ss, + &p2, + k3, + val, + strlen (val) + 1, + GNUNET_TIME_UNIT_FOREVER_ABS, + GNUNET_PEERSTORE_STOREOPTION_REPLACE, + NULL, + NULL); + ic = GNUNET_PEERSTORE_iterate (h, + ss, + &p1, + k1, + &iter1_cb, NULL); +} + + +int +main (int argc, char *argv[]) +{ + if (0 != + GNUNET_TESTING_service_run ("test-gnunet-peerstore", "peerstore", + "test_peerstore_api_data.conf", &run, NULL)) + return 1; + return ok; +} + + +/* end of test_peerstore_api_iterate.c */ diff --git a/src/service/peerstore/test_peerstore_api_store.c b/src/service/peerstore/test_peerstore_api_store.c new file mode 100644 index 000000000..77e8a17c1 --- /dev/null +++ b/src/service/peerstore/test_peerstore_api_store.c @@ -0,0 +1,233 @@ +/* + This file is part of GNUnet. + Copyright (C) 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 . + + SPDX-License-Identifier: AGPL3.0-or-later + */ +/** + * @file peerstore/test_peerstore_api_store.c + * @brief testcase for peerstore store operation + */ +#include "platform.h" +#include "gnunet_peerstore_service.h" +#include "gnunet_testing_lib.h" + + +static int ok = 1; + +static struct GNUNET_PEERSTORE_Handle *h; + +static char *subsystem = "test_peerstore_api_store"; +static struct GNUNET_PeerIdentity pid; +static char *key = "test_peerstore_api_store_key"; +static char *val1 = "test_peerstore_api_store_val1"; +static char *val2 = "test_peerstore_api_store_val2-"; +static char *val3 = "test_peerstore_api_store_val3--"; + +static int count = 0; + + +static void +test3_cont2 (void *cls, + const struct GNUNET_PEERSTORE_Record *record, + const char *emsg) +{ + if (NULL != emsg) + return; + if (NULL != record) + { + GNUNET_assert ((strlen (val3) + 1) == record->value_size); + GNUNET_assert (0 == strcmp ((char *) val3, + (char *) record->value)); + count++; + return; + } + GNUNET_assert (count == 1); + ok = 0; + GNUNET_PEERSTORE_disconnect (h, GNUNET_YES); + GNUNET_SCHEDULER_shutdown (); +} + + +static void +test3_cont (void *cls, + int success) +{ + if (GNUNET_YES != success) + return; + count = 0; + GNUNET_PEERSTORE_iterate (h, + subsystem, + &pid, + key, + &test3_cont2, + NULL); +} + + +/** + * Replace the previous 2 records + */ +static void +test3 () +{ + GNUNET_PEERSTORE_store (h, + subsystem, + &pid, + key, + val3, + strlen (val3) + 1, + GNUNET_TIME_UNIT_FOREVER_ABS, + GNUNET_PEERSTORE_STOREOPTION_REPLACE, + &test3_cont, + NULL); +} + + +static void +test2_cont2 (void *cls, + const struct GNUNET_PEERSTORE_Record *record, + const char *emsg) +{ + if (NULL != emsg) + return; + if (NULL != record) + { + GNUNET_assert (((strlen (val1) + 1) == record->value_size) || + ((strlen (val2) + 1) == record->value_size)); + GNUNET_assert ((0 == strcmp ((char *) val1, (char *) record->value)) || + (0 == strcmp ((char *) val2, (char *) record->value))); + count++; + return; + } + GNUNET_assert (count == 2); + count = 0; + test3 (); +} + + +static void +test2_cont (void *cls, int success) +{ + if (GNUNET_YES != success) + return; + count = 0; + GNUNET_PEERSTORE_iterate (h, + subsystem, + &pid, key, + &test2_cont2, + NULL); +} + + +/** + * Test storing a second value with the same key + */ +void +test2 () +{ + GNUNET_PEERSTORE_store (h, + subsystem, + &pid, + key, + val2, + strlen (val2) + 1, + GNUNET_TIME_UNIT_FOREVER_ABS, + GNUNET_PEERSTORE_STOREOPTION_MULTIPLE, + &test2_cont, + NULL); +} + + +static void +test1_cont2 (void *cls, + const struct GNUNET_PEERSTORE_Record *record, + const char *emsg) +{ + if (NULL != emsg) + return; + if (NULL != record) + { + GNUNET_assert ((strlen (val1) + 1) == record->value_size); + GNUNET_assert (0 == strcmp ((char *) val1, (char *) record->value)); + count++; + return; + } + GNUNET_assert (count == 1); + count = 0; + test2 (); +} + + +static void +test1_cont (void *cls, int success) +{ + if (GNUNET_YES != success) + return; + count = 0; + GNUNET_PEERSTORE_iterate (h, + subsystem, + &pid, + key, + &test1_cont2, + NULL); +} + + +/** + * Store a single record + */ +static void +test1 () +{ + GNUNET_PEERSTORE_store (h, + subsystem, + &pid, + key, + val1, + strlen (val1) + 1, + GNUNET_TIME_UNIT_FOREVER_ABS, + GNUNET_PEERSTORE_STOREOPTION_REPLACE, + &test1_cont, + NULL); +} + + +static void +run (void *cls, const struct GNUNET_CONFIGURATION_Handle *cfg, + struct GNUNET_TESTING_Peer *peer) +{ + h = GNUNET_PEERSTORE_connect (cfg); + GNUNET_assert (NULL != h); + memset (&pid, 1, sizeof(pid)); + test1 (); +} + + +int +main (int argc, char *argv[]) +{ + if (0 != + GNUNET_TESTING_service_run ("test-gnunet-peerstore", + "peerstore", + "test_peerstore_api_data.conf", + &run, NULL)) + return 1; + return ok; +} + + +/* end of test_peerstore_api_store.c */ diff --git a/src/service/peerstore/test_peerstore_api_sync.c b/src/service/peerstore/test_peerstore_api_sync.c new file mode 100644 index 000000000..5057c98b5 --- /dev/null +++ b/src/service/peerstore/test_peerstore_api_sync.c @@ -0,0 +1,253 @@ +/* + This file is part of GNUnet. + Copyright (C) 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 . + + SPDX-License-Identifier: AGPL3.0-or-later + */ +/** + * @file peerstore/test_peerstore_api_sync.c + * @brief testcase for peerstore sync-on-disconnect feature. Stores + * a value just before disconnecting, and then checks that + * this value is actually stored. + * @author Omar Tarabai + * @author Christian Grothoff (minor fix, comments) + */ +#include "platform.h" +#include "gnunet_util_lib.h" +#include "gnunet_testing_lib.h" +#include "gnunet_peerstore_service.h" + +/** + * Overall result, 0 for success. + */ +static int ok = 404; + +/** + * Configuration we use. + */ +static const struct GNUNET_CONFIGURATION_Handle *cfg; + +/** + * handle to talk to the peerstore. + */ +static struct GNUNET_PEERSTORE_Handle *h; + +/** + * Subsystem we store the value for. + */ +static const char *subsystem = "test_peerstore_api_sync"; + +/** + * Fake PID under which we store the value. + */ +static struct GNUNET_PeerIdentity pid; + +/** + * Test key we're storing the test value under. + */ +static const char *key = "test_peerstore_api_store_key"; + +/** + * Test value we are storing. + */ +static const char *val = "test_peerstore_api_store_val"; + + +/** + * Timeout + */ +#define TIMEOUT GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS, 10) + +/** + * Timeout task + */ +static struct GNUNET_SCHEDULER_Task *to; + +/** + * Iterate handle + */ +static struct GNUNET_PEERSTORE_IterateContext *it; + +static void +test_cont (void *cls); + +/** + * Function that should be called with the result of the + * lookup, and finally once with NULL to signal the end + * of the iteration. + * + * Upon the first call, we set "ok" to success. On the + * second call (end of iteration) we terminate the test. + * + * @param cls NULL + * @param record the information stored in the peerstore + * @param emsg any error message + * @return #GNUNET_YES (all good, continue) + */ +static void +iterate_cb (void *cls, + const struct GNUNET_PEERSTORE_Record *record, + const char *emsg) +{ + const char *rec_val; + + GNUNET_break (NULL == emsg); + if (NULL == record) + { + it = NULL; + if (0 == ok) + { + GNUNET_PEERSTORE_disconnect (h, + GNUNET_NO); + if (NULL != to) + { + GNUNET_SCHEDULER_cancel (to); + to = NULL; + } + GNUNET_SCHEDULER_shutdown (); + return; + } + /** + * Try again + */ + GNUNET_SCHEDULER_add_now (&test_cont, + NULL); + return; + } + rec_val = record->value; + GNUNET_break (0 == strcmp (rec_val, val)); + ok = 0; +} + + +static void +timeout_task (void *cls) +{ + to = NULL; + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "Timeout reached\n"); + if (NULL != it) + GNUNET_PEERSTORE_iterate_cancel (it); + it = NULL; + GNUNET_PEERSTORE_disconnect (h, + GNUNET_NO); + GNUNET_SCHEDULER_shutdown (); + return; +} + + +/** + * Run the 2nd stage of the test where we fetch the + * data that should have been stored. + * + * @param cls NULL + */ +static void +test_cont (void *cls) +{ + it = GNUNET_PEERSTORE_iterate (h, + subsystem, + &pid, key, + &iterate_cb, + NULL); +} + + +static void +disc_cont (void *cls) +{ + GNUNET_PEERSTORE_disconnect (h, GNUNET_YES); + h = GNUNET_PEERSTORE_connect (cfg); + GNUNET_SCHEDULER_add_now (&test_cont, + NULL); +} + + +static void +store_cont (void *cls, int success) +{ + ok = success; + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Success: %s\n", + (GNUNET_SYSERR == ok) ? "no" : "yes"); + /* We need to wait a little bit to give the disconnect + a chance to actually finish the operation; otherwise, + the test may fail non-deterministically if the new + connection is faster than the cleanup routine of the + old one. */ + GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_UNIT_SECONDS, + &disc_cont, + NULL); +} + + +/** + * Actually run the test. + */ +static void +test1 () +{ + h = GNUNET_PEERSTORE_connect (cfg); + GNUNET_PEERSTORE_store (h, + subsystem, + &pid, + key, + val, strlen (val) + 1, + GNUNET_TIME_UNIT_FOREVER_ABS, + GNUNET_PEERSTORE_STOREOPTION_REPLACE, + &store_cont, NULL); +} + + +/** + * Initialize globals and launch the test. + * + * @param cls NULL + * @param c configuration to use + * @param peer handle to our peer (unused) + */ +static void +run (void *cls, + const struct GNUNET_CONFIGURATION_Handle *c, + struct GNUNET_TESTING_Peer *peer) +{ + cfg = c; + memset (&pid, 1, sizeof(pid)); + to = GNUNET_SCHEDULER_add_delayed (TIMEOUT, + &timeout_task, + NULL); + GNUNET_SCHEDULER_add_now (&test1, NULL); +} + + +int +main (int argc, char *argv[]) +{ + if (0 != + GNUNET_TESTING_service_run ("test-gnunet-peerstore-sync", + "peerstore", + "peerstore.conf", + &run, NULL)) + return 1; + if (0 != ok) + fprintf (stderr, + "Test failed: %d\n", + ok); + return ok; +} + + +/* end of test_peerstore_api_sync.c */ diff --git a/src/service/peerstore/test_peerstore_api_watch.c b/src/service/peerstore/test_peerstore_api_watch.c new file mode 100644 index 000000000..126b321df --- /dev/null +++ b/src/service/peerstore/test_peerstore_api_watch.c @@ -0,0 +1,102 @@ +/* + This file is part of GNUnet. + Copyright (C) 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 . + + SPDX-License-Identifier: AGPL3.0-or-later + */ +/** + * @file peerstore/test_peerstore_api_watch.c + * @brief testcase for peerstore watch functionality + */ +#include "platform.h" +#include "gnunet_util_lib.h" +#include "gnunet_testing_lib.h" +#include "gnunet_peerstore_service.h" + + +static int ok = 1; + +static struct GNUNET_PEERSTORE_Handle *h; + +static char *ss = "test_peerstore_api_watch"; + +static char *k = "test_peerstore_api_watch_key"; + +static char *val = "test_peerstore_api_watch_val"; + + +static void +watch_cb (void *cls, + const struct GNUNET_PEERSTORE_Record *record, + const char *emsg) +{ + GNUNET_assert (NULL == emsg); + GNUNET_assert (0 == strcmp (val, + (char *) record->value)); + ok = 0; + GNUNET_PEERSTORE_disconnect (h, + GNUNET_NO); + GNUNET_SCHEDULER_shutdown (); +} + + +static void +run (void *cls, + const struct GNUNET_CONFIGURATION_Handle *cfg, + struct GNUNET_TESTING_Peer *peer) +{ + struct GNUNET_PeerIdentity p; + + h = GNUNET_PEERSTORE_connect (cfg); + GNUNET_assert (NULL != h); + memset (&p, + 4, + sizeof(p)); + GNUNET_PEERSTORE_watch (h, + ss, + &p, + k, + &watch_cb, + NULL); + GNUNET_PEERSTORE_store (h, + ss, + &p, + k, + val, + strlen (val) + 1, + GNUNET_TIME_UNIT_FOREVER_ABS, + GNUNET_PEERSTORE_STOREOPTION_REPLACE, + NULL, + NULL); +} + + +int +main (int argc, + char *argv[]) +{ + if (0 != + GNUNET_TESTING_service_run ("test-gnunet-peerstore", + "peerstore", + "test_peerstore_api_data.conf", + &run, + NULL)) + return 1; + return ok; +} + + +/* end of test_peerstore_api_watch.c */ diff --git a/src/service/peerstore/test_plugin_peerstore.c b/src/service/peerstore/test_plugin_peerstore.c new file mode 100644 index 000000000..bce62dda9 --- /dev/null +++ b/src/service/peerstore/test_plugin_peerstore.c @@ -0,0 +1,224 @@ +/* + This file is part of GNUnet. + Copyright (C) 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 . + + SPDX-License-Identifier: AGPL3.0-or-later + */ +/* + * @file namestore/test_plugin_namestore.c + * @brief Test for the namestore plugins + * @author Christian Grothoff + */ +#include "platform.h" +#include "gnunet_util_lib.h" +#include "gnunet_peerstore_plugin.h" +#include "gnunet_testing_lib.h" + + +static int ok; + +/** + * Name of plugin under test. + */ +static const char *plugin_name; + + +static struct GNUNET_PEERSTORE_PluginFunctions *psp; + +static struct GNUNET_PeerIdentity p1; + + +/** + * Function called when the service shuts down. Unloads our namestore + * plugin. + * + * @param api api to unload + */ +static void +unload_plugin (struct GNUNET_PEERSTORE_PluginFunctions *api) +{ + char *libname; + + GNUNET_asprintf (&libname, + "libgnunet_plugin_peer_%s", + plugin_name); + GNUNET_break (NULL == + GNUNET_PLUGIN_unload (libname, + api)); + GNUNET_free (libname); +} + + +/** + * Load the namestore plugin. + * + * @param cfg configuration to pass + * @return NULL on error + */ +static struct GNUNET_PEERSTORE_PluginFunctions * +load_plugin (const struct GNUNET_CONFIGURATION_Handle *cfg) +{ + struct GNUNET_PEERSTORE_PluginFunctions *ret; + char *libname; + + GNUNET_log (GNUNET_ERROR_TYPE_INFO, + _ ("Loading `%s' peer plugin\n"), + plugin_name); + GNUNET_asprintf (&libname, + "libgnunet_plugin_peerstore_%s", + plugin_name); + if (NULL == (ret = GNUNET_PLUGIN_load (libname, + (void *) cfg))) + { + fprintf (stderr, + "Failed to load plugin `%s'!\n", + plugin_name); + GNUNET_free (libname); + return NULL; + } + GNUNET_free (libname); + return ret; +} + + +static void +test_record (void *cls, + const struct GNUNET_PEERSTORE_Record *record, + const char *error) +{ + const struct GNUNET_PeerIdentity *id = cls; + const char*testval = "test_val"; + + if (NULL == record) + { + unload_plugin (psp); + return; + } + GNUNET_assert (0 == memcmp (&record->peer, + id, + sizeof(struct GNUNET_PeerIdentity))); + GNUNET_assert (0 == strcmp ("subsys", + record->sub_system)); + GNUNET_assert (0 == strcmp ("key", + record->key)); + GNUNET_assert (0 == memcmp (testval, + record->value, + strlen (testval))); + ok = 0; +} + + +static void +get_record (struct GNUNET_PEERSTORE_PluginFunctions *psp, + const struct GNUNET_PeerIdentity *identity) +{ + GNUNET_assert (GNUNET_OK == + psp->iterate_records (psp->cls, + "subsys", + identity, + "key", + &test_record, + (void *) identity)); +} + + +static void +store_cont (void *cls, + int status) +{ + GNUNET_assert (GNUNET_OK == status); + get_record (psp, + &p1); +} + + +static void +put_record (struct GNUNET_PEERSTORE_PluginFunctions *psp, + const struct GNUNET_PeerIdentity *identity) +{ + GNUNET_assert (GNUNET_OK == + psp->store_record (psp->cls, + "subsys", + identity, + "key", + "test_value", + strlen ("test_value"), + GNUNET_TIME_absolute_get (), + GNUNET_PEERSTORE_STOREOPTION_REPLACE, + &store_cont, + NULL)); +} + + +static void +run (void *cls, + char *const *args, + const char *cfgfile, + const struct GNUNET_CONFIGURATION_Handle *cfg) +{ + ok = 1; + psp = load_plugin (cfg); + if (NULL == psp) + { + fprintf (stderr, + "%s", + "Failed to initialize peerstore. Database likely not setup, skipping test.\n"); + return; + } + memset (&p1, 1, sizeof(p1)); + put_record (psp, + &p1); +} + + +int +main (int argc, char *argv[]) +{ + char cfg_name[PATH_MAX]; + char *const xargv[] = { + "test-plugin-peerstore", + "-c", + cfg_name, + NULL + }; + struct GNUNET_GETOPT_CommandLineOption options[] = { + GNUNET_GETOPT_OPTION_END + }; + + GNUNET_log_setup ("test-plugin-peerstore", + "WARNING", + NULL); + plugin_name = GNUNET_STRINGS_get_suffix_from_binary_name (argv[0]); + GNUNET_snprintf (cfg_name, + sizeof(cfg_name), + "test_plugin_peerstore_%s.conf", + plugin_name); + GNUNET_PROGRAM_run ((sizeof(xargv) / sizeof(char *)) - 1, + xargv, + "test-plugin-peerstore", + "nohelp", + options, + &run, + NULL); + if (ok != 0) + fprintf (stderr, + "Missed some testcases: %d\n", + ok); + return ok; +} + + +/* end of test_plugin_peerstore.c */ diff --git a/src/service/peerstore/test_plugin_peerstore_flat.conf b/src/service/peerstore/test_plugin_peerstore_flat.conf new file mode 100644 index 000000000..c55b1e9d6 --- /dev/null +++ b/src/service/peerstore/test_plugin_peerstore_flat.conf @@ -0,0 +1,5 @@ +[peerstore-flat] +FILENAME = $GNUNET_TMP/gnunet-test-plugin-namestore-flat/flatdb + +[peerstore] +# PREFIX = valgrind --log-file=/home/schanzen/dev/gnunet/src/peerstore/vg_log diff --git a/src/service/peerstore/test_plugin_peerstore_sqlite.conf b/src/service/peerstore/test_plugin_peerstore_sqlite.conf new file mode 100644 index 000000000..dcf1fc1a4 --- /dev/null +++ b/src/service/peerstore/test_plugin_peerstore_sqlite.conf @@ -0,0 +1,2 @@ +[peerstore-sqlite] +FILENAME = $GNUNET_TMP/gnunet-test-plugin-peerstore-sqlite/sqlite.db diff --git a/src/service/statistics/.gitignore b/src/service/statistics/.gitignore new file mode 100644 index 000000000..f1f567149 --- /dev/null +++ b/src/service/statistics/.gitignore @@ -0,0 +1,7 @@ +gnunet-statistics +gnunet-service-statistics +test_gnunet_statistics.py +test_statistics_api +test_statistics_api_loop +test_statistics_api_watch +test_statistics_api_watch_zero_value diff --git a/src/service/statistics/Makefile.am b/src/service/statistics/Makefile.am new file mode 100644 index 000000000..55cd266b0 --- /dev/null +++ b/src/service/statistics/Makefile.am @@ -0,0 +1,89 @@ +# This Makefile.am is in the public domain +AM_CPPFLAGS = -I$(top_srcdir)/src/include + +if USE_COVERAGE + AM_CFLAGS = --coverage -O0 + XLIB = -lgcov +endif + +pkgcfgdir= $(pkgdatadir)/config.d/ + +libexecdir= $(pkglibdir)/libexec/ + +pkgcfg_DATA = \ + statistics.conf + +lib_LTLIBRARIES = libgnunetstatistics.la + +libgnunetstatistics_la_SOURCES = \ + statistics_api.c statistics.h +libgnunetstatistics_la_LIBADD = \ + $(top_builddir)/src/lib/util/libgnunetutil.la \ + $(GN_LIBINTL) $(XLIB) +libgnunetstatistics_la_LDFLAGS = \ + $(GN_LIB_LDFLAGS) \ + -version-info 2:0:0 + +libexec_PROGRAMS = \ + gnunet-service-statistics + +gnunet_service_statistics_SOURCES = \ + gnunet-service-statistics.c +gnunet_service_statistics_LDADD = \ + libgnunetstatistics.la \ + $(top_builddir)/src/lib/util/libgnunetutil.la \ + $(GN_LIBINTL) + +check_PROGRAMS = \ + test_statistics_api \ + test_statistics_api_loop \ + test_statistics_api_watch \ + test_statistics_api_watch_zero_value + +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) $(check_SCRIPTS) +endif + +test_statistics_api_SOURCES = \ + test_statistics_api.c +test_statistics_api_LDADD = \ + libgnunetstatistics.la \ + $(top_builddir)/src/lib/util/libgnunetutil.la + +test_statistics_api_loop_SOURCES = \ + test_statistics_api_loop.c +test_statistics_api_loop_LDADD = \ + libgnunetstatistics.la \ + $(top_builddir)/src/lib/util/libgnunetutil.la + +test_statistics_api_watch_SOURCES = \ + test_statistics_api_watch.c +test_statistics_api_watch_LDADD = \ + libgnunetstatistics.la \ + $(top_builddir)/src/lib/util/libgnunetutil.la + +test_statistics_api_watch_zero_value_SOURCES = \ + test_statistics_api_watch_zero_value.c +test_statistics_api_watch_zero_value_LDADD = \ + libgnunetstatistics.la \ + $(top_builddir)/src/lib/util/libgnunetutil.la + +# Needs to be done with CLI +#if HAVE_PYTHON +#check_SCRIPTS = \ +# test_gnunet_statistics.py +#endif +# +#SUFFIXES = .py.in .py +#.py.in.py: +# $(AWK) -v bdir="$(bindir)" -v py="$(PYTHON)" -v awkay="$(AWK_BINARY)" -v pfx="$(prefix)" -v prl="$(PERL)" -v sysconfdirectory="$(sysconfdir)" -v pkgdatadirectory="$(pkgdatadir)" -f $(top_srcdir)/bin/dosubst.awk < $(srcdir)/$< > $@ +# chmod +x $@ +# +#test_gnunet_statistics.py: test_gnunet_statistics.py.in Makefile +# $(AWK) -v bdir="$(bindir)" -v py="$(PYTHON)" -v awkay="$(AWK_BINARY)" -v pfx="$(prefix)" -v prl="$(PERL)" -v sysconfdirectory="$(sysconfdir)" -v pkgdatadirectory="$(pkgdatadir)" -f $(top_srcdir)/bin/dosubst.awk < $(srcdir)/test_gnunet_statistics.py.in > test_gnunet_statistics.py +# chmod +x test_gnunet_statistics.py +# +#EXTRA_DIST = \ +# test_statistics_api_data.conf \ +# test_gnunet_statistics.py.in diff --git a/src/service/statistics/gnunet-service-statistics.c b/src/service/statistics/gnunet-service-statistics.c new file mode 100644 index 000000000..a6c897a79 --- /dev/null +++ b/src/service/statistics/gnunet-service-statistics.c @@ -0,0 +1,1059 @@ +/* + This file is part of GNUnet. + Copyright (C) 2009, 2010, 2012, 2014, 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 . + + SPDX-License-Identifier: AGPL3.0-or-later + */ + +/** + * @file statistics/gnunet-service-statistics.c + * @brief program that tracks statistics + * @author Christian Grothoff + */ +#include "platform.h" +#include "gnunet_util_lib.h" +#include "gnunet_protocols.h" +#include "gnunet_statistics_service.h" +#include "gnunet_time_lib.h" +#include "statistics.h" + +/** + * Watch entry. + */ +struct WatchEntry +{ + /** + * Watch entries are kept in a linked list. + */ + struct WatchEntry *next; + + /** + * Watch entries are kept in a linked list. + */ + struct WatchEntry *prev; + + /** + * For which client is this watch entry? + */ + struct ClientEntry *ce; + + /** + * Last value we communicated to the client for this watch entry. + */ + uint64_t last_value; + + /** + * Unique watch number for this client and this watched value. + */ + uint32_t wid; + + /** + * Is last_value valid + * #GNUNET_NO : last_value is n/a, #GNUNET_YES: last_value is valid + */ + int last_value_set; +}; + + +/** + * We keep the statistics organized by subsystem for faster + * lookup during SET operations. + */ +struct SubsystemEntry; + + +/** + * Entry in the statistics list. + */ +struct StatsEntry +{ + /** + * This is a linked list. + */ + struct StatsEntry *next; + + /** + * This is a linked list. + */ + struct StatsEntry *prev; + + /** + * Subsystem this entry belongs to. + */ + struct SubsystemEntry *subsystem; + + /** + * Name for the value stored by this entry, allocated at the end of + * this struct. + */ + const char *name; + + /** + * Watch context for changes to this value, or NULL for none. + */ + struct WatchEntry *we_head; + + /** + * Watch context for changes to this value, or NULL for none. + */ + struct WatchEntry *we_tail; + + /** + * Our value. + */ + uint64_t value; + + /** + * Unique ID. + */ + uint32_t uid; + + /** + * Is this value persistent? + */ + int persistent; + + /** + * Is this value set? + * #GNUNET_NO: value is n/a, #GNUNET_YES: value is valid + */ + int set; +}; + + +/** + * We keep the statistics organized by subsystem for faster + * lookup during SET operations. + */ +struct SubsystemEntry +{ + /** + * Subsystems are kept in a DLL. + */ + struct SubsystemEntry *next; + + /** + * Subsystems are kept in a DLL. + */ + struct SubsystemEntry *prev; + + /** + * Head of list of values kept for this subsystem. + */ + struct StatsEntry *stat_head; + + /** + * Tail of list of values kept for this subsystem. + */ + struct StatsEntry *stat_tail; + + /** + * Name of the subsystem this entry is for, allocated at + * the end of this struct, do not free(). + */ + const char *service; +}; + + +/** + * Client entry. + */ +struct ClientEntry +{ + /** + * Corresponding server handle. + */ + struct GNUNET_SERVICE_Client *client; + + /** + * Corresponding message queue. + */ + struct GNUNET_MQ_Handle *mq; + + /** + * Which subsystem is this client writing to (SET/UPDATE)? + */ + struct SubsystemEntry *subsystem; + + /** + * Maximum watch ID used by this client so far. + */ + uint32_t max_wid; +}; + + +/** + * Our configuration. + */ +static const struct GNUNET_CONFIGURATION_Handle *cfg; + +/** + * Head of linked list of subsystems with active statistics. + */ +static struct SubsystemEntry *sub_head; + +/** + * Tail of linked list of subsystems with active statistics. + */ +static struct SubsystemEntry *sub_tail; + +/** + * Number of connected clients. + */ +static unsigned int client_count; + +/** + * Our notification context. + */ +static struct GNUNET_NotificationContext *nc; + +/** + * Counter used to generate unique values. + */ +static uint32_t uidgen; + +/** + * Set to #GNUNET_YES if we are shutting down as soon as possible. + */ +static int in_shutdown; + + +/** + * Write persistent statistics to disk. + */ +static void +save () +{ + struct SubsystemEntry *se; + struct StatsEntry *pos; + char *fn; + struct GNUNET_BIO_WriteHandle *wh; + uint16_t size; + unsigned long long total; + size_t nlen; + size_t slen; + struct GNUNET_STATISTICS_SetMessage *msg; + + if (GNUNET_OK != GNUNET_CONFIGURATION_get_value_filename (cfg, + "STATISTICS", + "DATABASE", + &fn)) + { + GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR, + "STATISTICS", + "DATABASE"); + return; + } + (void) GNUNET_DISK_directory_create_for_file (fn); + wh = GNUNET_BIO_write_open_file (fn); + total = 0; + while (NULL != (se = sub_head)) + { + GNUNET_CONTAINER_DLL_remove (sub_head, sub_tail, se); + slen = strlen (se->service) + 1; + while (NULL != (pos = se->stat_head)) + { + GNUNET_CONTAINER_DLL_remove (se->stat_head, se->stat_tail, pos); + if ((pos->persistent) && (NULL != wh)) + { + nlen = strlen (pos->name) + 1; + size = sizeof(struct GNUNET_STATISTICS_SetMessage) + nlen + slen; + GNUNET_assert (size < UINT16_MAX); + msg = GNUNET_malloc (size); + + msg->header.size = htons ((uint16_t) size); + msg->header.type = htons (GNUNET_MESSAGE_TYPE_STATISTICS_SET); + GNUNET_assert (nlen + slen == + GNUNET_STRINGS_buffer_fill ((char *) &msg[1], + nlen + slen, + 2, + se->service, + pos->name)); + msg->flags = + htonl (pos->persistent ? GNUNET_STATISTICS_SETFLAG_PERSISTENT : 0); + msg->value = GNUNET_htonll (pos->value); + if (GNUNET_OK != GNUNET_BIO_write (wh, "statistics-save-msg", msg, + size)) + { + GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING, "write", fn); + if (GNUNET_OK != GNUNET_BIO_write_close (wh, NULL)) + GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING, "close", fn); + wh = NULL; + } + else + { + total += size; + } + GNUNET_free (msg); + } + GNUNET_free (pos); + } + GNUNET_free (se); + } + if (NULL != wh) + { + if (GNUNET_OK != GNUNET_BIO_write_close (wh, NULL)) + GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING, "close", fn); + if (0 == total) + GNUNET_break (0 == unlink (fn)); + else + GNUNET_log (GNUNET_ERROR_TYPE_INFO, + _ ("Wrote %llu bytes of statistics to `%s'\n"), + total, + fn); + } + GNUNET_free (fn); +} + + +/** + * Transmit the given stats value. + * + * @param ce receiver of the value + * @param e value to transmit + */ +static void +transmit (struct ClientEntry *ce, const struct StatsEntry *e) +{ + struct GNUNET_MQ_Envelope *env; + struct GNUNET_STATISTICS_ReplyMessage *m; + size_t size; + + size = strlen (e->subsystem->service) + 1 + strlen (e->name) + 1; + GNUNET_assert (size < GNUNET_MAX_MESSAGE_SIZE); + env = GNUNET_MQ_msg_extra (m, size, GNUNET_MESSAGE_TYPE_STATISTICS_VALUE); + m->uid = htonl (e->uid); + if (e->persistent) + m->uid |= htonl (GNUNET_STATISTICS_PERSIST_BIT); + m->value = GNUNET_htonll (e->value); + GNUNET_assert (size == GNUNET_STRINGS_buffer_fill ((char *) &m[1], + size, + 2, + e->subsystem->service, + e->name)); + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Transmitting value for `%s:%s' (%d): %llu\n", + e->subsystem->service, + e->name, + e->persistent, + (unsigned long long) e->value); + GNUNET_MQ_send (ce->mq, env); +} + + +/** + * 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; + client_count++; + GNUNET_notification_context_add (nc, mq); + return ce; +} + + +/** + * Check integrity of GET-message. + * + * @param cls identification of the client + * @param message the actual message + * @return #GNUNET_OK if @a message is well-formed + */ +static int +check_get (void *cls, const struct GNUNET_MessageHeader *message) +{ + const char *service; + const char *name; + size_t size; + + size = ntohs (message->size) - sizeof(struct GNUNET_MessageHeader); + if (size != GNUNET_STRINGS_buffer_tokenize ((const char *) &message[1], + size, + 2, + &service, + &name)) + { + GNUNET_break (0); + return GNUNET_SYSERR; + } + return GNUNET_OK; +} + + +/** + * Handle GET-message. + * + * @param cls identification of the client + * @param message the actual message + */ +static void +handle_get (void *cls, const struct GNUNET_MessageHeader *message) +{ + struct ClientEntry *ce = cls; + struct GNUNET_MQ_Envelope *env; + struct GNUNET_MessageHeader *end; + const char *service; + const char *name; + size_t slen; + size_t nlen; + struct SubsystemEntry *se; + struct StatsEntry *pos; + size_t size; + + size = ntohs (message->size) - sizeof(struct GNUNET_MessageHeader); + GNUNET_assert (size == + GNUNET_STRINGS_buffer_tokenize ((const char *) &message[1], + size, + 2, + &service, + &name)); + slen = strlen (service); + nlen = strlen (name); + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Received request for statistics on `%s:%s'\n", + slen ? service : "*", + nlen ? name : "*"); + for (se = sub_head; NULL != se; se = se->next) + { + if (! ((0 == slen) || (0 == strcmp (service, se->service)))) + continue; + for (pos = se->stat_head; NULL != pos; pos = pos->next) + { + if (! ((0 == nlen) || (0 == strcmp (name, pos->name)))) + continue; + transmit (ce, pos); + } + } + env = GNUNET_MQ_msg (end, GNUNET_MESSAGE_TYPE_STATISTICS_END); + GNUNET_MQ_send (ce->mq, env); + GNUNET_SERVICE_client_continue (ce->client); +} + + +/** + * Notify all clients listening about a change to a value. + * + * @param se value that changed + */ +static void +notify_change (struct StatsEntry *se) +{ + struct GNUNET_MQ_Envelope *env; + struct GNUNET_STATISTICS_WatchValueMessage *wvm; + struct WatchEntry *pos; + + for (pos = se->we_head; NULL != pos; pos = pos->next) + { + if (GNUNET_YES == pos->last_value_set) + { + if (pos->last_value == se->value) + continue; + } + else + { + pos->last_value_set = GNUNET_YES; + } + env = GNUNET_MQ_msg (wvm, GNUNET_MESSAGE_TYPE_STATISTICS_WATCH_VALUE); + wvm->flags = + htonl (se->persistent ? GNUNET_STATISTICS_SETFLAG_PERSISTENT : 0); + wvm->wid = htonl (pos->wid); + wvm->reserved = htonl (0); + wvm->value = GNUNET_htonll (se->value); + GNUNET_MQ_send (pos->ce->mq, env); + pos->last_value = se->value; + } +} + + +/** + * Find the subsystem entry of the given name for the specified client. + * + * @param ce client looking for the subsystem, may contain a hint + * to find the entry faster, can be NULL + * @param service name of the subsystem to look for + * @return subsystem entry, never NULL (subsystem entry is created if necessary) + */ +static struct SubsystemEntry * +find_subsystem_entry (struct ClientEntry *ce, const char *service) +{ + size_t slen; + struct SubsystemEntry *se; + + if (NULL != ce) + se = ce->subsystem; + else + se = NULL; + if ((NULL == se) || (0 != strcmp (service, se->service))) + { + for (se = sub_head; NULL != se; se = se->next) + if (0 == strcmp (service, se->service)) + break; + if (NULL != ce) + ce->subsystem = se; + } + if (NULL != se) + return se; + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Allocating new subsystem entry `%s'\n", + service); + slen = strlen (service) + 1; + se = GNUNET_malloc (sizeof(struct SubsystemEntry) + slen); + GNUNET_memcpy (&se[1], service, slen); + se->service = (const char *) &se[1]; + GNUNET_CONTAINER_DLL_insert (sub_head, sub_tail, se); + if (NULL != ce) + ce->subsystem = se; + return se; +} + + +/** + * Find the statistics entry of the given subsystem. + * + * @param se subsystem to look in + * @param name name of the entry to look for + * @return statistics entry, or NULL if not found + */ +static struct StatsEntry * +find_stat_entry (struct SubsystemEntry *se, const char *name) +{ + struct StatsEntry *pos; + + for (pos = se->stat_head; NULL != pos; pos = pos->next) + if (0 == strcmp (name, pos->name)) + return pos; + return NULL; +} + + +/** + * Check format of SET-message. + * + * @param cls the `struct ClientEntry` + * @param msg the actual message + * @return #GNUNET_OK if message is well-formed + */ +static int +check_set (void *cls, const struct GNUNET_STATISTICS_SetMessage *msg) +{ + const char *service; + const char *name; + size_t msize; + + msize = ntohs (msg->header.size) - sizeof(*msg); + if (msize != GNUNET_STRINGS_buffer_tokenize ((const char *) &msg[1], + msize, + 2, + &service, + &name)) + { + GNUNET_break (0); + return GNUNET_SYSERR; + } + return GNUNET_OK; +} + + +/** + * Handle SET-message. + * + * @param cls the `struct ClientEntry` + * @param msg the actual message + */ +static void +handle_set (void *cls, const struct GNUNET_STATISTICS_SetMessage *msg) +{ + struct ClientEntry *ce = cls; + const char *service; + const char *name; + size_t nlen; + uint16_t msize; + uint16_t size; + struct SubsystemEntry *se; + struct StatsEntry *pos; + uint32_t flags; + uint64_t value; + int64_t delta; + int changed; + int initial_set; + + msize = ntohs (msg->header.size); + size = msize - sizeof(struct GNUNET_STATISTICS_SetMessage); + GNUNET_assert (size == GNUNET_STRINGS_buffer_tokenize ((const char *) &msg[1], + size, + 2, + &service, + &name)); + se = find_subsystem_entry (ce, service); + flags = ntohl (msg->flags); + value = GNUNET_ntohll (msg->value); + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Received request to update statistic on `%s:%s' (%u) to/by %llu\n", + service, + name, + (unsigned int) flags, + (unsigned long long) value); + pos = find_stat_entry (se, name); + if (NULL != pos) + { + initial_set = 0; + if (0 == (flags & GNUNET_STATISTICS_SETFLAG_RELATIVE)) + { + changed = (pos->value != value); + pos->value = value; + } + else + { + delta = (int64_t) value; + if ((delta < 0) && (pos->value < -delta)) + { + changed = (0 != pos->value); + pos->value = 0; + } + else + { + changed = (0 != delta); + GNUNET_break ((delta <= 0) || (pos->value + delta > pos->value)); + pos->value += delta; + } + } + if (GNUNET_NO == pos->set) + { + pos->set = GNUNET_YES; + initial_set = 1; + } + pos->persistent = (0 != (flags & GNUNET_STATISTICS_SETFLAG_PERSISTENT)); + if (pos != se->stat_head) + { + /* move to front for faster setting next time! */ + GNUNET_CONTAINER_DLL_remove (se->stat_head, se->stat_tail, pos); + GNUNET_CONTAINER_DLL_insert (se->stat_head, se->stat_tail, pos); + } + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Statistic `%s:%s' updated to value %llu (%d).\n", + service, + name, + (unsigned long long) pos->value, + pos->persistent); + if ((changed) || (1 == initial_set)) + notify_change (pos); + GNUNET_SERVICE_client_continue (ce->client); + return; + } + /* not found, create a new entry */ + nlen = strlen (name) + 1; + pos = GNUNET_malloc (sizeof(struct StatsEntry) + nlen); + GNUNET_memcpy (&pos[1], name, nlen); + pos->name = (const char *) &pos[1]; + pos->subsystem = se; + if ((0 == (flags & GNUNET_STATISTICS_SETFLAG_RELATIVE)) || + (0 < (int64_t) GNUNET_ntohll (msg->value))) + { + pos->value = GNUNET_ntohll (msg->value); + pos->set = GNUNET_YES; + } + else + { + pos->set = GNUNET_NO; + } + pos->uid = uidgen++; + pos->persistent = (0 != (flags & GNUNET_STATISTICS_SETFLAG_PERSISTENT)); + GNUNET_CONTAINER_DLL_insert (se->stat_head, se->stat_tail, pos); + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "New statistic on `%s:%s' with value %llu created.\n", + service, + name, + (unsigned long long) pos->value); + if (NULL != ce) + GNUNET_SERVICE_client_continue (ce->client); +} + + +/** + * Check integrity of WATCH-message. + * + * @param cls the `struct ClientEntry *` + * @param message the actual message + * @return #GNUNET_OK if message is well-formed + */ +static int +check_watch (void *cls, const struct GNUNET_MessageHeader *message) +{ + size_t size; + const char *service; + const char *name; + + size = ntohs (message->size) - sizeof(struct GNUNET_MessageHeader); + if (size != GNUNET_STRINGS_buffer_tokenize ((const char *) &message[1], + size, + 2, + &service, + &name)) + { + GNUNET_break (0); + return GNUNET_SYSERR; + } + return GNUNET_OK; +} + + +/** + * Handle WATCH-message. + * + * @param cls the `struct ClientEntry *` + * @param message the actual message + */ +static void +handle_watch (void *cls, const struct GNUNET_MessageHeader *message) +{ + struct ClientEntry *ce = cls; + const char *service; + const char *name; + uint16_t msize; + uint16_t size; + struct SubsystemEntry *se; + struct StatsEntry *pos; + struct WatchEntry *we; + size_t nlen; + + if (NULL == nc) + { + GNUNET_SERVICE_client_drop (ce->client); + return; + } + GNUNET_SERVICE_client_mark_monitor (ce->client); + msize = ntohs (message->size); + size = msize - sizeof(struct GNUNET_MessageHeader); + GNUNET_assert (size == + GNUNET_STRINGS_buffer_tokenize ((const char *) &message[1], + size, + 2, + &service, + &name)); + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Received request to watch statistic on `%s:%s'\n", + service, + name); + se = find_subsystem_entry (ce, service); + pos = find_stat_entry (se, name); + if (NULL == pos) + { + nlen = strlen (name) + 1; + pos = GNUNET_malloc (sizeof(struct StatsEntry) + nlen); + GNUNET_memcpy (&pos[1], name, nlen); + pos->name = (const char *) &pos[1]; + pos->subsystem = se; + GNUNET_CONTAINER_DLL_insert (se->stat_head, se->stat_tail, pos); + pos->uid = uidgen++; + pos->set = GNUNET_NO; + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "New statistic on `%s:%s' with value %llu created.\n", + service, + name, + (unsigned long long) pos->value); + } + we = GNUNET_new (struct WatchEntry); + we->ce = ce; + we->last_value_set = GNUNET_NO; + we->wid = ce->max_wid++; + GNUNET_CONTAINER_DLL_insert (pos->we_head, pos->we_tail, we); + if (0 != pos->value) + notify_change (pos); + GNUNET_SERVICE_client_continue (ce->client); +} + + +/** + * Handle DISCONNECT-message. Sync to disk and send + * back a #GNUNET_MESSAGE_TYPE_STATISTICS_DISCONNECT_CONFIRM + * message. + * + * @param cls the `struct ClientEntry *` + * @param message the actual message + */ +static void +handle_disconnect (void *cls, const struct GNUNET_MessageHeader *message) +{ + struct ClientEntry *ce = cls; + struct GNUNET_MQ_Envelope *env; + struct GNUNET_MessageHeader *msg; + + env = GNUNET_MQ_msg (msg, GNUNET_MESSAGE_TYPE_STATISTICS_DISCONNECT_CONFIRM); + GNUNET_MQ_send (ce->mq, env); + GNUNET_SERVICE_client_continue (ce->client); +} + + +/** + * Actually perform the shutdown. + */ +static void +do_shutdown () +{ + struct WatchEntry *we; + struct StatsEntry *pos; + struct SubsystemEntry *se; + + if (NULL == nc) + return; + save (); + GNUNET_notification_context_destroy (nc); + nc = NULL; + GNUNET_assert (0 == client_count); + while (NULL != (se = sub_head)) + { + GNUNET_CONTAINER_DLL_remove (sub_head, sub_tail, se); + while (NULL != (pos = se->stat_head)) + { + GNUNET_CONTAINER_DLL_remove (se->stat_head, se->stat_tail, pos); + while (NULL != (we = pos->we_head)) + { + GNUNET_break (0); + GNUNET_CONTAINER_DLL_remove (pos->we_head, pos->we_tail, we); + GNUNET_free (we); + } + GNUNET_free (pos); + } + GNUNET_free (se); + } +} + + +/** + * Task run during shutdown. + * + * @param cls unused + */ +static void +shutdown_task (void *cls) +{ + in_shutdown = GNUNET_YES; + if (0 != client_count) + return; + do_shutdown (); +} + + +/** + * A client disconnected. Remove all of its data structure entries. + * + * @param cls closure, NULL + * @param client identification of the client + * @param app_cls the `struct ClientEntry *` + */ +static void +client_disconnect_cb (void *cls, + struct GNUNET_SERVICE_Client *client, + void *app_cls) +{ + struct ClientEntry *ce = app_cls; + struct WatchEntry *we; + struct WatchEntry *wen; + struct StatsEntry *pos; + struct SubsystemEntry *se; + + client_count--; + for (se = sub_head; NULL != se; se = se->next) + { + for (pos = se->stat_head; NULL != pos; pos = pos->next) + { + wen = pos->we_head; + while (NULL != (we = wen)) + { + wen = we->next; + if (we->ce != ce) + continue; + GNUNET_CONTAINER_DLL_remove (pos->we_head, pos->we_tail, we); + GNUNET_free (we); + } + } + } + GNUNET_free (ce); + if ((0 == client_count) && (GNUNET_YES == in_shutdown)) + do_shutdown (); +} + + +/** + * We've read a `struct GNUNET_STATISTICS_SetMessage *` from + * disk. Check that it is well-formed, and if so pass it to + * the handler for set messages. + * + * @param cls NULL + * @param message the message found on disk + * @return #GNUNET_OK on success, + * #GNUNET_NO to stop further processing (no error) + * #GNUNET_SYSERR to stop further processing with error + */ +static int +inject_message (void *cls, const struct GNUNET_MessageHeader *message) +{ + uint16_t msize = ntohs (message->size); + const struct GNUNET_STATISTICS_SetMessage *sm; + + sm = (const struct GNUNET_STATISTICS_SetMessage *) message; + if ((sizeof(struct GNUNET_STATISTICS_SetMessage) > msize) || + (GNUNET_OK != check_set (NULL, sm))) + { + GNUNET_break (0); + return GNUNET_SYSERR; + } + handle_set (NULL, sm); + return GNUNET_OK; +} + + +/** + * Load persistent values from disk. Disk format is exactly the same + * format that we also use for setting the values over the network. + */ +static void +load () +{ + char *fn; + struct GNUNET_BIO_ReadHandle *rh; + uint64_t fsize; + char *buf; + struct GNUNET_MessageStreamTokenizer *mst; + + if (GNUNET_OK != GNUNET_CONFIGURATION_get_value_filename (cfg, + "STATISTICS", + "DATABASE", + &fn)) + { + GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR, + "STATISTICS", + "DATABASE"); + return; + } + if ((GNUNET_OK != + GNUNET_DISK_file_size (fn, &fsize, GNUNET_NO, GNUNET_YES)) || + (0 == fsize)) + { + GNUNET_free (fn); + return; + } + buf = GNUNET_malloc (fsize); + rh = GNUNET_BIO_read_open_file (fn); + if (! rh) + { + GNUNET_free (buf); + GNUNET_free (fn); + return; + } + if (GNUNET_OK != GNUNET_BIO_read (rh, fn, buf, fsize)) + { + GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING, "read", fn); + GNUNET_break (GNUNET_OK == GNUNET_BIO_read_close (rh, NULL)); + GNUNET_free (buf); + GNUNET_free (fn); + return; + } + GNUNET_log (GNUNET_ERROR_TYPE_INFO, + _ ("Loading %llu bytes of statistics from `%s'\n"), + (unsigned long long) fsize, + fn); + mst = GNUNET_MST_create (&inject_message, NULL); + GNUNET_break ( + GNUNET_OK == + GNUNET_MST_from_buffer (mst, buf, (size_t) fsize, GNUNET_YES, GNUNET_NO)); + GNUNET_MST_destroy (mst); + GNUNET_free (buf); + GNUNET_break (GNUNET_OK == GNUNET_BIO_read_close (rh, NULL)); + GNUNET_free (fn); +} + + +/** + * Process statistics requests. + * + * @param cls closure + * @param c configuration to use + * @param service the initialized service + */ +static void +run (void *cls, + const struct GNUNET_CONFIGURATION_Handle *c, + struct GNUNET_SERVICE_Handle *service) +{ + cfg = c; + nc = GNUNET_notification_context_create (16); + load (); + GNUNET_SCHEDULER_add_shutdown (&shutdown_task, NULL); +} + + +/** + * Define "main" method using service macro. + */ +GNUNET_SERVICE_MAIN ( + "statistics", + GNUNET_SERVICE_OPTION_SOFT_SHUTDOWN, + &run, + &client_connect_cb, + &client_disconnect_cb, + NULL, + GNUNET_MQ_hd_var_size (set, + GNUNET_MESSAGE_TYPE_STATISTICS_SET, + struct GNUNET_STATISTICS_SetMessage, + NULL), + GNUNET_MQ_hd_var_size (get, + GNUNET_MESSAGE_TYPE_STATISTICS_GET, + struct GNUNET_MessageHeader, + NULL), + GNUNET_MQ_hd_var_size (watch, + GNUNET_MESSAGE_TYPE_STATISTICS_WATCH, + struct GNUNET_MessageHeader, + NULL), + GNUNET_MQ_hd_fixed_size (disconnect, + GNUNET_MESSAGE_TYPE_STATISTICS_DISCONNECT, + struct GNUNET_MessageHeader, + NULL), + GNUNET_MQ_handler_end ()); + + +#if defined(__linux__) && defined(__GLIBC__) +#include + +/** + * MINIMIZE heap size (way below 128k) since this process doesn't need much. + */ +void __attribute__ ((constructor)) +GNUNET_STATISTICS_memory_init () +{ + mallopt (M_TRIM_THRESHOLD, 4 * 1024); + mallopt (M_TOP_PAD, 1 * 1024); + malloc_trim (0); +} + + +#endif + + +/* end of gnunet-service-statistics.c */ diff --git a/src/service/statistics/meson.build b/src/service/statistics/meson.build new file mode 100644 index 000000000..14cdb0ac3 --- /dev/null +++ b/src/service/statistics/meson.build @@ -0,0 +1,44 @@ +libgnunetstatistics_src = ['statistics_api.c'] + +gnunetservicestatistics_src = ['gnunet-service-statistics.c'] + +configure_file(input : 'statistics.conf.in', + output : 'statistics.conf', + configuration : cdata, + install: true, + install_dir: pkgcfgdir) + + +if get_option('monolith') + foreach p : libgnunetstatistics_src + gnunetservicestatistics_src + gnunet_src += 'statistics/' + p + endforeach + subdir_done() +endif + +libgnunetstatistics = library('gnunetstatistics', + libgnunetstatistics_src, + soversion: '2', + version: '2.0.0', + dependencies: libgnunetutil_dep, + include_directories: [incdir, configuration_inc], + install: true, + install_dir: get_option('libdir')) +libgnunetstatistics_dep = declare_dependency(link_with : libgnunetstatistics) +pkg.generate(libgnunetstatistics, url: 'https://www.gnunet.org', + description : 'Provides API of GNUnet statistics service') + +executable ('gnunet-service-statistics', + gnunetservicestatistics_src, + dependencies: [libgnunetstatistics_dep, libgnunetutil_dep], + include_directories: [incdir, configuration_inc], + install: true, + install_dir: get_option('libdir') / 'gnunet' / 'libexec') +executable ('gnunet-statistics', + ['gnunet-statistics.c'], + dependencies: [libgnunetstatistics_dep, libgnunetutil_dep], + include_directories: [incdir, configuration_inc], + install: true, + install_dir: get_option('bindir')) + + diff --git a/src/service/statistics/statistics.conf.in b/src/service/statistics/statistics.conf.in new file mode 100644 index 000000000..36aca538f --- /dev/null +++ b/src/service/statistics/statistics.conf.in @@ -0,0 +1,21 @@ +[statistics] +START_ON_DEMAND = @START_ON_DEMAND@ +@JAVAPORT@PORT = 2088 +HOSTNAME = localhost +BINARY = gnunet-service-statistics +ACCEPT_FROM = 127.0.0.1; +ACCEPT_FROM6 = ::1; +UNIXPATH = $GNUNET_RUNTIME_DIR/gnunet-service-statistics.sock +UNIX_MATCH_UID = NO +UNIX_MATCH_GID = YES +DATABASE = $GNUNET_DATA_HOME/statistics.dat +# DISABLE_SOCKET_FORWARDING = NO +# USERNAME = +# MAXBUF = +# TIMEOUT = +# DISABLEV6 = +# BINDTO = +# REJECT_FROM = +# REJECT_FROM6 = +# PREFIX = + diff --git a/src/service/statistics/statistics.h b/src/service/statistics/statistics.h new file mode 100644 index 000000000..6eb75cc99 --- /dev/null +++ b/src/service/statistics/statistics.h @@ -0,0 +1,149 @@ +/* + This file is part of GNUnet. + Copyright (C) 2001-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 . + + SPDX-License-Identifier: AGPL3.0-or-later + */ + +/** + * @author Christian Grothoff + * @file statistics/statistics.h + */ +#ifndef STATISTICS_H +#define STATISTICS_H + +#include "gnunet_common.h" + + +GNUNET_NETWORK_STRUCT_BEGIN + +/** + * Statistics message. Contains how long the system is up + * and one value. + * + * The struct is be followed by the service name and + * name of the statistic, both 0-terminated. + */ +struct GNUNET_STATISTICS_ReplyMessage +{ + /** + * Type: GNUNET_MESSAGE_TYPE_STATISTICS_VALUE + */ + struct GNUNET_MessageHeader header; + + /** + * Unique numerical identifier for the value (will + * not change during the same client-session). Highest + * bit will be set for persistent values (see + * #GNUNET_STATISTICS_PERSIST_BIT). + */ + uint32_t uid GNUNET_PACKED; + + /** + * The value. + */ + uint64_t value GNUNET_PACKED; +}; + +/** + * Flag for the `struct GNUNET_STATISTICS_ReplyMessage` UID only. + * Note that other messages use #GNUNET_STATISTICS_SETFLAG_PERSISTENT. + */ +#define GNUNET_STATISTICS_PERSIST_BIT ((uint32_t) (1LLU << 31)) + +/** + * The value being set is an absolute change. + */ +#define GNUNET_STATISTICS_SETFLAG_ABSOLUTE 0 + +/** + * The value being set is a relative change. + */ +#define GNUNET_STATISTICS_SETFLAG_RELATIVE 1 + +/** + * The value being set is to be persistent (note that + * this bit can be combined with #GNUNET_STATISTICS_SETFLAG_RELATIVE). + * This value must not be used for the `uid` member of + * `struct GNUNET_STATISTICS_ReplyMessage`! + */ +#define GNUNET_STATISTICS_SETFLAG_PERSISTENT 2 + + +/** + * Message to set a statistic. Followed + * by the subsystem name and the name of + * the statistic (each 0-terminated). + */ +struct GNUNET_STATISTICS_SetMessage +{ + /** + * Type: #GNUNET_MESSAGE_TYPE_STATISTICS_SET + */ + struct GNUNET_MessageHeader header; + + /** + * 0 for absolute value, 1 for relative value; 2 to make persistent + * (see GNUNET_STATISTICS_SETFLAG_*). + */ + uint32_t flags GNUNET_PACKED; + + /** + * Value. Note that if this is a relative value, it will + * be signed even though the type given here is unsigned. + */ + uint64_t value GNUNET_PACKED; +}; + + +/** + * Message transmitted if a watched value changes. + */ +struct GNUNET_STATISTICS_WatchValueMessage +{ + /** + * Type: #GNUNET_MESSAGE_TYPE_STATISTICS_WATCH_VALUE + */ + struct GNUNET_MessageHeader header; + + /** + * 0 for absolute value, 1 for relative value; 2 to make persistent + * (see GNUNET_STATISTICS_SETFLAG_*). + */ + uint32_t flags GNUNET_PACKED; + + /** + * Unique watch identification number (watch + * requests are enumerated in the order they + * are received, the first request having + * a wid of zero). + */ + uint32_t wid GNUNET_PACKED; + + /** + * Reserved (always 0). + */ + uint32_t reserved GNUNET_PACKED; + + /** + * Value. Note that if this is a relative value, it will + * be signed even though the type given here is unsigned. + */ + uint64_t value GNUNET_PACKED; +}; +GNUNET_NETWORK_STRUCT_END + +#endif diff --git a/src/service/statistics/statistics_api.c b/src/service/statistics/statistics_api.c new file mode 100644 index 000000000..88f127da8 --- /dev/null +++ b/src/service/statistics/statistics_api.c @@ -0,0 +1,1342 @@ +/* + This file is part of GNUnet. + Copyright (C) 2009, 2010, 2011, 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 . + + SPDX-License-Identifier: AGPL3.0-or-later + */ + +/** + * @file statistics/statistics_api.c + * @brief API of the statistics service + * @author Christian Grothoff + */ +#include "platform.h" +#include "gnunet_util_lib.h" +#include "gnunet_constants.h" +#include "gnunet_protocols.h" +#include "gnunet_statistics_service.h" +#include "statistics.h" + +/** + * How long do we wait until a statistics request for setting + * a value times out? (The update will be lost if the + * service does not react within this timeframe). + */ +#define SET_TRANSMIT_TIMEOUT GNUNET_TIME_relative_multiply ( \ + GNUNET_TIME_UNIT_SECONDS, 2) + +#define LOG(kind, ...) GNUNET_log_from (kind, "statistics-api", __VA_ARGS__) + +/** + * Types of actions. + */ +enum ActionType +{ + /** + * Get a value. + */ + ACTION_GET, + + /** + * Set a value. + */ + ACTION_SET, + + /** + * Update a value. + */ + ACTION_UPDATE, + + /** + * Watch a value. + */ + ACTION_WATCH +}; + + +/** + * Entry kept for each value we are watching. + */ +struct GNUNET_STATISTICS_WatchEntry +{ + /** + * What subsystem is this action about? (never NULL) + */ + char *subsystem; + + /** + * What value is this action about? (never NULL) + */ + char *name; + + /** + * Function to call + */ + GNUNET_STATISTICS_Iterator proc; + + /** + * Closure for @e proc + */ + void *proc_cls; +}; + + +/** + * Linked list of things we still need to do. + */ +struct GNUNET_STATISTICS_GetHandle +{ + /** + * This is a doubly linked list. + */ + struct GNUNET_STATISTICS_GetHandle *next; + + /** + * This is a doubly linked list. + */ + struct GNUNET_STATISTICS_GetHandle *prev; + + /** + * Main statistics handle. + */ + struct GNUNET_STATISTICS_Handle *sh; + + /** + * What subsystem is this action about? (can be NULL) + */ + char *subsystem; + + /** + * What value is this action about? (can be NULL) + */ + char *name; + + /** + * Continuation to call once action is complete. + */ + GNUNET_STATISTICS_Callback cont; + + /** + * Function to call (for GET actions only). + */ + GNUNET_STATISTICS_Iterator proc; + + /** + * Closure for @e proc and @e cont. + */ + void *cls; + + /** + * Timeout for this action. + */ + struct GNUNET_TIME_Absolute timeout; + + /** + * Associated value. + */ + uint64_t value; + + /** + * Flag for SET/UPDATE actions. + */ + int make_persistent; + + /** + * Has the current iteration been aborted; for GET actions. + */ + int aborted; + + /** + * Is this a #ACTION_GET, #ACTION_SET, #ACTION_UPDATE or #ACTION_WATCH? + */ + enum ActionType type; + + /** + * Size of the message that we will be transmitting. + */ + uint16_t msize; +}; + + +/** + * Handle for the service. + */ +struct GNUNET_STATISTICS_Handle +{ + /** + * Name of our subsystem. + */ + char *subsystem; + + /** + * Configuration to use. + */ + const struct GNUNET_CONFIGURATION_Handle *cfg; + + /** + * Message queue to the service. + */ + struct GNUNET_MQ_Handle *mq; + + /** + * Head of the linked list of pending actions (first action + * to be performed). + */ + struct GNUNET_STATISTICS_GetHandle *action_head; + + /** + * Tail of the linked list of actions (for fast append). + */ + struct GNUNET_STATISTICS_GetHandle *action_tail; + + /** + * Action we are currently busy with (action request has been + * transmitted, we're now receiving the response from the + * service). + */ + struct GNUNET_STATISTICS_GetHandle *current; + + /** + * Array of watch entries. + */ + struct GNUNET_STATISTICS_WatchEntry **watches; + + /** + * Task doing exponential back-off trying to reconnect. + */ + struct GNUNET_SCHEDULER_Task *backoff_task; + + /** + * Task for running #do_destroy(). + */ + struct GNUNET_SCHEDULER_Task *destroy_task; + + /** + * Time for next connect retry. + */ + struct GNUNET_TIME_Relative backoff; + + /** + * Maximum heap size observed so far (if available). + */ + uint64_t peak_heap_size; + + /** + * Maximum resident set side observed so far (if available). + */ + uint64_t peak_rss; + + /** + * Size of the @e watches array. + */ + unsigned int watches_size; + + /** + * Should this handle auto-destruct once all actions have + * been processed? + */ + int do_destroy; + + /** + * Are we currently receiving from the service? + */ + int receiving; +}; + + +/** + * Obtain statistics about this process's memory consumption and + * report those as well (if they changed). + */ +static void +update_memory_statistics (struct GNUNET_STATISTICS_Handle *h) +{ +#if ENABLE_HEAP_STATISTICS + uint64_t current_heap_size = 0; + uint64_t current_rss = 0; + + if (GNUNET_NO != h->do_destroy) + return; +#if HAVE_MALLINFO2 + { + struct mallinfo2 mi; + + mi = mallinfo2 (); + current_heap_size = mi.uordblks + mi.fordblks; + } +#endif +#if HAVE_GETRUSAGE + { + struct rusage ru; + + if (0 == getrusage (RUSAGE_SELF, &ru)) + { + current_rss = 1024LL * ru.ru_maxrss; + } + } +#endif + if (current_heap_size > h->peak_heap_size) + { + h->peak_heap_size = current_heap_size; + GNUNET_STATISTICS_set (h, + "# peak heap size", + current_heap_size, + GNUNET_NO); + } + if (current_rss > h->peak_rss) + { + h->peak_rss = current_rss; + GNUNET_STATISTICS_set (h, + "# peak resident set size", + current_rss, + GNUNET_NO); + } +#endif +} + + +/** + * Reconnect at a later time, respecting back-off. + * + * @param h statistics handle + */ +static void +reconnect_later (struct GNUNET_STATISTICS_Handle *h); + + +/** + * Schedule the next action to be performed. + * + * @param cls statistics handle to reconnect + */ +static void +schedule_action (void *cls); + + +/** + * Transmit request to service that we want to watch + * the development of a particular value. + * + * @param h statistics handle + * @param watch watch entry of the value to watch + */ +static void +schedule_watch_request (struct GNUNET_STATISTICS_Handle *h, + struct GNUNET_STATISTICS_WatchEntry *watch) +{ + struct GNUNET_STATISTICS_GetHandle *ai; + size_t slen; + size_t nlen; + size_t nsize; + + slen = strlen (watch->subsystem) + 1; + nlen = strlen (watch->name) + 1; + nsize = sizeof(struct GNUNET_MessageHeader) + slen + nlen; + if (nsize >= GNUNET_MAX_MESSAGE_SIZE) + { + GNUNET_break (0); + return; + } + ai = GNUNET_new (struct GNUNET_STATISTICS_GetHandle); + ai->sh = h; + ai->subsystem = GNUNET_strdup (watch->subsystem); + ai->name = GNUNET_strdup (watch->name); + ai->timeout = GNUNET_TIME_UNIT_FOREVER_ABS; + ai->msize = nsize; + ai->type = ACTION_WATCH; + ai->proc = watch->proc; + ai->cls = watch->proc_cls; + GNUNET_CONTAINER_DLL_insert_tail (h->action_head, + h->action_tail, + ai); + schedule_action (h); +} + + +/** + * Free memory associated with the given action item. + * + * @param gh action item to free + */ +static void +free_action_item (struct GNUNET_STATISTICS_GetHandle *gh) +{ + GNUNET_free (gh->subsystem); + GNUNET_free (gh->name); + GNUNET_free (gh); +} + + +/** + * Disconnect from the statistics service. + * + * @param h statistics handle to disconnect from + */ +static void +do_disconnect (struct GNUNET_STATISTICS_Handle *h) +{ + struct GNUNET_STATISTICS_GetHandle *c; + + h->receiving = GNUNET_NO; + if (NULL != (c = h->current)) + { + h->current = NULL; + if ((NULL != c->cont) && + (GNUNET_YES != c->aborted)) + { + c->cont (c->cls, + GNUNET_SYSERR); + c->cont = NULL; + } + free_action_item (c); + } + if (NULL != h->mq) + { + GNUNET_MQ_destroy (h->mq); + h->mq = NULL; + } +} + + +/** + * Process a #GNUNET_MESSAGE_TYPE_STATISTICS_VALUE message. + * + * @param cls statistics handle + * @param smsg message received from the service, never NULL + * @return #GNUNET_OK if the message was well-formed + */ +static int +check_statistics_value (void *cls, + const struct GNUNET_STATISTICS_ReplyMessage *smsg) +{ + const char *service; + const char *name; + uint16_t size; + + size = ntohs (smsg->header.size); + size -= sizeof(struct GNUNET_STATISTICS_ReplyMessage); + if (size != + GNUNET_STRINGS_buffer_tokenize ((const char *) &smsg[1], + size, + 2, + &service, + &name)) + { + GNUNET_break (0); + return GNUNET_SYSERR; + } + return GNUNET_OK; +} + + +/** + * Process a #GNUNET_MESSAGE_TYPE_STATISTICS_VALUE message. + * + * @param cls statistics handle + * @param smsg message received from the service, never NULL + * @return #GNUNET_OK if the message was well-formed + */ +static void +handle_statistics_value (void *cls, + const struct GNUNET_STATISTICS_ReplyMessage *smsg) +{ + struct GNUNET_STATISTICS_Handle *h = cls; + const char *service; + const char *name; + uint16_t size; + + if (h->current->aborted) + return; /* iteration aborted, don't bother */ + + size = ntohs (smsg->header.size); + size -= sizeof(struct GNUNET_STATISTICS_ReplyMessage); + GNUNET_assert (size == + GNUNET_STRINGS_buffer_tokenize ((const char *) &smsg[1], + size, + 2, + &service, + &name)); + LOG (GNUNET_ERROR_TYPE_DEBUG, + "Received valid statistic on `%s:%s': %llu\n", + service, name, + (unsigned long long) GNUNET_ntohll (smsg->value)); + if (GNUNET_OK != + h->current->proc (h->current->cls, + service, + name, + GNUNET_ntohll (smsg->value), + (0 != + (ntohl (smsg->uid) & GNUNET_STATISTICS_PERSIST_BIT)) )) + { + LOG (GNUNET_ERROR_TYPE_DEBUG, + "Processing of remaining statistics aborted by client.\n"); + h->current->aborted = GNUNET_YES; + } +} + + +/** + * We have received a watch value from the service. Process it. + * + * @param cls statistics handle + * @param wvm the watch value message + */ +static void +handle_statistics_watch_value (void *cls, + const struct + GNUNET_STATISTICS_WatchValueMessage *wvm) +{ + struct GNUNET_STATISTICS_Handle *h = cls; + struct GNUNET_STATISTICS_WatchEntry *w; + uint32_t wid; + + GNUNET_break (0 == ntohl (wvm->reserved)); + wid = ntohl (wvm->wid); + if (wid >= h->watches_size) + { + do_disconnect (h); + reconnect_later (h); + return; + } + w = h->watches[wid]; + if (NULL == w) + return; + (void) w->proc (w->proc_cls, + w->subsystem, + w->name, + GNUNET_ntohll (wvm->value), + 0 != (ntohl (wvm->flags) & GNUNET_STATISTICS_PERSIST_BIT)); +} + + +/** + * Generic error handler, called with the appropriate error code and + * the same closure specified at the creation of the message queue. + * Not every message queue implementation supports an error handler. + * + * @param cls closure with the `struct GNUNET_STATISTICS_Handle *` + * @param error error code + */ +static void +mq_error_handler (void *cls, + enum GNUNET_MQ_Error error) +{ + struct GNUNET_STATISTICS_Handle *h = cls; + + if (GNUNET_NO != h->do_destroy) + { + h->do_destroy = GNUNET_NO; + if (NULL != h->destroy_task) + { + GNUNET_SCHEDULER_cancel (h->destroy_task); + h->destroy_task = NULL; + } + GNUNET_STATISTICS_destroy (h, + GNUNET_NO); + return; + } + do_disconnect (h); + reconnect_later (h); +} + + +/** + * Task used to destroy the statistics handle. + * + * @param cls the `struct GNUNET_STATISTICS_Handle` + */ +static void +do_destroy (void *cls) +{ + struct GNUNET_STATISTICS_Handle *h = cls; + + h->destroy_task = NULL; + h->do_destroy = GNUNET_NO; + LOG (GNUNET_ERROR_TYPE_DEBUG, + "Running final destruction\n"); + GNUNET_STATISTICS_destroy (h, + GNUNET_NO); +} + + +/** + * Handle a #GNUNET_MESSAGE_TYPE_STATISTICS_DISCONNECT_CONFIRM + * message. We receive this message at the end of the shutdown when + * the service confirms that all data has been written to disk. + * + * @param cls our `struct GNUNET_STATISTICS_Handle *` + * @param msg the message + */ +static void +handle_disconnect_confirm (void *cls, + const struct GNUNET_MessageHeader *msg) +{ + struct GNUNET_STATISTICS_Handle *h = cls; + + if (GNUNET_SYSERR != h->do_destroy) + { + /* not in shutdown, why do we get 'TEST'? */ + GNUNET_break (0); + do_disconnect (h); + reconnect_later (h); + return; + } + LOG (GNUNET_ERROR_TYPE_DEBUG, + "Received DISCONNNECT_CONFIRM message from statistics, can complete disconnect\n"); + if (NULL != h->destroy_task) + GNUNET_SCHEDULER_cancel (h->destroy_task); + h->destroy_task = GNUNET_SCHEDULER_add_now (&do_destroy, + h); +} + + +/** + * Handle a #GNUNET_MESSAGE_TYPE_STATISTICS_END message. We receive + * this message in response to a query to indicate that there are no + * further matching results. + * + * @param cls our `struct GNUNET_STATISTICS_Handle *` + * @param msg the message + */ +static void +handle_statistics_end (void *cls, + const struct GNUNET_MessageHeader *msg) +{ + struct GNUNET_STATISTICS_Handle *h = cls; + struct GNUNET_STATISTICS_GetHandle *c; + + LOG (GNUNET_ERROR_TYPE_DEBUG, + "Received end of statistics marker\n"); + if (NULL == (c = h->current)) + { + GNUNET_break (0); + do_disconnect (h); + reconnect_later (h); + return; + } + h->backoff = GNUNET_TIME_UNIT_MILLISECONDS; + h->current = NULL; + schedule_action (h); + if (NULL != c->cont) + { + c->cont (c->cls, + GNUNET_OK); + c->cont = NULL; + } + free_action_item (c); +} + + +/** + * Try to (re)connect to the statistics service. + * + * @param h statistics handle to reconnect + * @return #GNUNET_YES on success, #GNUNET_NO on failure. + */ +static int +try_connect (struct GNUNET_STATISTICS_Handle *h) +{ + struct GNUNET_MQ_MessageHandler handlers[] = { + GNUNET_MQ_hd_fixed_size (disconnect_confirm, + GNUNET_MESSAGE_TYPE_STATISTICS_DISCONNECT_CONFIRM, + struct GNUNET_MessageHeader, + h), + GNUNET_MQ_hd_fixed_size (statistics_end, + GNUNET_MESSAGE_TYPE_STATISTICS_END, + struct GNUNET_MessageHeader, + h), + GNUNET_MQ_hd_var_size (statistics_value, + GNUNET_MESSAGE_TYPE_STATISTICS_VALUE, + struct GNUNET_STATISTICS_ReplyMessage, + h), + GNUNET_MQ_hd_fixed_size (statistics_watch_value, + GNUNET_MESSAGE_TYPE_STATISTICS_WATCH_VALUE, + struct GNUNET_STATISTICS_WatchValueMessage, + h), + GNUNET_MQ_handler_end () + }; + struct GNUNET_STATISTICS_GetHandle *gh; + struct GNUNET_STATISTICS_GetHandle *gn; + + if (NULL != h->backoff_task) + return GNUNET_NO; + if (NULL != h->mq) + return GNUNET_YES; + h->mq = GNUNET_CLIENT_connect (h->cfg, + "statistics", + handlers, + &mq_error_handler, + h); + if (NULL == h->mq) + { + LOG (GNUNET_ERROR_TYPE_DEBUG, + "Failed to connect to statistics service!\n"); + return GNUNET_NO; + } + gn = h->action_head; + while (NULL != (gh = gn)) + { + gn = gh->next; + if (gh->type == ACTION_WATCH) + { + GNUNET_CONTAINER_DLL_remove (h->action_head, + h->action_tail, + gh); + free_action_item (gh); + } + } + for (unsigned int i = 0; i < h->watches_size; i++) + if (NULL != h->watches[i]) + schedule_watch_request (h, + h->watches[i]); + return GNUNET_YES; +} + + +/** + * We've waited long enough, reconnect now. + * + * @param cls the `struct GNUNET_STATISTICS_Handle` to reconnect + */ +static void +reconnect_task (void *cls) +{ + struct GNUNET_STATISTICS_Handle *h = cls; + + h->backoff_task = NULL; + schedule_action (h); +} + + +/** + * Reconnect at a later time, respecting back-off. + * + * @param h statistics handle + */ +static void +reconnect_later (struct GNUNET_STATISTICS_Handle *h) +{ + int loss; + struct GNUNET_STATISTICS_GetHandle *gh; + + GNUNET_assert (NULL == h->backoff_task); + if (GNUNET_YES == h->do_destroy) + { + /* So we are shutting down and the service is not reachable. + * Chances are that it's down for good and we are not going to connect to + * it anymore. + * Give up and don't sync the rest of the data. + */loss = GNUNET_NO; + for (gh = h->action_head; NULL != gh; gh = gh->next) + if ((gh->make_persistent) && + (ACTION_SET == gh->type)) + loss = GNUNET_YES; + if (GNUNET_YES == loss) + GNUNET_log (GNUNET_ERROR_TYPE_WARNING, + _ ("Could not save some persistent statistics\n")); + if (NULL != h->destroy_task) + GNUNET_SCHEDULER_cancel (h->destroy_task); + h->destroy_task = GNUNET_SCHEDULER_add_now (&do_destroy, + h); + return; + } + h->backoff_task + = GNUNET_SCHEDULER_add_delayed (h->backoff, + &reconnect_task, + h); + h->backoff = GNUNET_TIME_STD_BACKOFF (h->backoff); +} + + +/** + * Transmit a GET request (and if successful, start to receive + * the response). + * + * @param handle statistics handle + */ +static void +transmit_get (struct GNUNET_STATISTICS_Handle *handle) +{ + struct GNUNET_STATISTICS_GetHandle *c; + struct GNUNET_MessageHeader *hdr; + struct GNUNET_MQ_Envelope *env; + size_t slen1; + size_t slen2; + + GNUNET_assert (NULL != (c = handle->current)); + slen1 = strlen (c->subsystem) + 1; + slen2 = strlen (c->name) + 1; + env = GNUNET_MQ_msg_extra (hdr, + slen1 + slen2, + GNUNET_MESSAGE_TYPE_STATISTICS_GET); + GNUNET_assert (slen1 + slen2 == + GNUNET_STRINGS_buffer_fill ((char *) &hdr[1], + slen1 + slen2, + 2, + c->subsystem, + c->name)); + GNUNET_MQ_notify_sent (env, + &schedule_action, + handle); + GNUNET_MQ_send (handle->mq, + env); +} + + +/** + * Transmit a WATCH request (and if successful, start to receive + * the response). + * + * @param handle statistics handle + */ +static void +transmit_watch (struct GNUNET_STATISTICS_Handle *handle) +{ + struct GNUNET_MessageHeader *hdr; + struct GNUNET_MQ_Envelope *env; + size_t slen1; + size_t slen2; + + LOG (GNUNET_ERROR_TYPE_DEBUG, + "Transmitting watch request for `%s'\n", + handle->current->name); + slen1 = strlen (handle->current->subsystem) + 1; + slen2 = strlen (handle->current->name) + 1; + env = GNUNET_MQ_msg_extra (hdr, + slen1 + slen2, + GNUNET_MESSAGE_TYPE_STATISTICS_WATCH); + GNUNET_assert (slen1 + slen2 == + GNUNET_STRINGS_buffer_fill ((char *) &hdr[1], + slen1 + slen2, + 2, + handle->current->subsystem, + handle->current->name)); + GNUNET_MQ_notify_sent (env, + &schedule_action, + handle); + GNUNET_MQ_send (handle->mq, + env); + GNUNET_assert (NULL == handle->current->cont); + free_action_item (handle->current); + handle->current = NULL; + schedule_action (handle); +} + + +/** + * Transmit a SET/UPDATE request. + * + * @param handle statistics handle + */ +static void +transmit_set (struct GNUNET_STATISTICS_Handle *handle) +{ + struct GNUNET_STATISTICS_SetMessage *r; + struct GNUNET_MQ_Envelope *env; + size_t slen; + size_t nlen; + + slen = strlen (handle->current->subsystem) + 1; + nlen = strlen (handle->current->name) + 1; + env = GNUNET_MQ_msg_extra (r, + slen + nlen, + GNUNET_MESSAGE_TYPE_STATISTICS_SET); + r->flags = 0; + r->value = GNUNET_htonll (handle->current->value); + if (handle->current->make_persistent) + r->flags |= htonl (GNUNET_STATISTICS_SETFLAG_PERSISTENT); + if (handle->current->type == ACTION_UPDATE) + r->flags |= htonl (GNUNET_STATISTICS_SETFLAG_RELATIVE); + GNUNET_assert (slen + nlen == + GNUNET_STRINGS_buffer_fill ((char *) &r[1], + slen + nlen, + 2, + handle->current->subsystem, + handle->current->name)); + GNUNET_assert (NULL == handle->current->cont); + free_action_item (handle->current); + handle->current = NULL; + update_memory_statistics (handle); + GNUNET_MQ_notify_sent (env, + &schedule_action, + handle); + GNUNET_MQ_send (handle->mq, + env); +} + + +/** + * Get handle for the statistics service. + * + * @param subsystem name of subsystem using the service + * @param cfg services configuration in use + * @return handle to use + */ +struct GNUNET_STATISTICS_Handle * +GNUNET_STATISTICS_create (const char *subsystem, + const struct GNUNET_CONFIGURATION_Handle *cfg) +{ + struct GNUNET_STATISTICS_Handle *h; + + if (GNUNET_YES == + GNUNET_CONFIGURATION_get_value_yesno (cfg, + "statistics", + "DISABLE")) + return NULL; + h = GNUNET_new (struct GNUNET_STATISTICS_Handle); + h->cfg = cfg; + h->subsystem = GNUNET_strdup (subsystem); + h->backoff = GNUNET_TIME_UNIT_MILLISECONDS; + return h; +} + + +/** + * Destroy a handle (free all state associated with + * it). + * + * @param h statistics handle to destroy + * @param sync_first set to #GNUNET_YES if pending SET requests should + * be completed + */ +void +GNUNET_STATISTICS_destroy (struct GNUNET_STATISTICS_Handle *h, + int sync_first) +{ + struct GNUNET_STATISTICS_GetHandle *pos; + struct GNUNET_STATISTICS_GetHandle *next; + + if (NULL == h) + return; + GNUNET_assert (GNUNET_NO == h->do_destroy); /* Don't call twice. */ + if ((sync_first) && + (NULL != h->mq) && + (0 != GNUNET_MQ_get_length (h->mq))) + { + if ((NULL != h->current) && + (ACTION_GET == h->current->type)) + h->current->aborted = GNUNET_YES; + next = h->action_head; + while (NULL != (pos = next)) + { + next = pos->next; + if ((ACTION_GET == pos->type) || + (ACTION_WATCH == pos->type)) + { + GNUNET_CONTAINER_DLL_remove (h->action_head, + h->action_tail, + pos); + free_action_item (pos); + } + } + h->do_destroy = GNUNET_YES; + schedule_action (h); + GNUNET_assert (NULL == h->destroy_task); + h->destroy_task + = GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_relative_multiply (h->backoff, + 5), + &do_destroy, + h); + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Deferring destruction\n"); + return; /* do not finish destruction just yet */ + } + /* do clean up all */ + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Cleaning all up\n"); + while (NULL != (pos = h->action_head)) + { + GNUNET_CONTAINER_DLL_remove (h->action_head, + h->action_tail, + pos); + free_action_item (pos); + } + do_disconnect (h); + if (NULL != h->backoff_task) + { + GNUNET_SCHEDULER_cancel (h->backoff_task); + h->backoff_task = NULL; + } + if (NULL != h->destroy_task) + { + GNUNET_break (0); + GNUNET_SCHEDULER_cancel (h->destroy_task); + h->destroy_task = NULL; + } + for (unsigned int i = 0; i < h->watches_size; i++) + { + if (NULL == h->watches[i]) + continue; + GNUNET_free (h->watches[i]->subsystem); + GNUNET_free (h->watches[i]->name); + GNUNET_free (h->watches[i]); + } + GNUNET_array_grow (h->watches, + h->watches_size, + 0); + GNUNET_free (h->subsystem); + GNUNET_free (h); +} + + +/** + * Schedule the next action to be performed. + * + * @param cls statistics handle + */ +static void +schedule_action (void *cls) +{ + struct GNUNET_STATISTICS_Handle *h = cls; + + if (NULL != h->backoff_task) + return; /* action already pending */ + if (GNUNET_YES != try_connect (h)) + { + reconnect_later (h); + return; + } + if (0 < GNUNET_MQ_get_length (h->mq)) + return; /* Wait for queue to be reduced more */ + /* schedule next action */ + while (NULL == h->current) + { + h->current = h->action_head; + if (NULL == h->current) + { + struct GNUNET_MessageHeader *hdr; + struct GNUNET_MQ_Envelope *env; + + if (GNUNET_YES != h->do_destroy) + return; /* nothing to do */ + /* let service know that we're done */ + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Notifying service that we are done\n"); + h->do_destroy = GNUNET_SYSERR; /* in 'TEST' mode */ + env = GNUNET_MQ_msg (hdr, + GNUNET_MESSAGE_TYPE_STATISTICS_DISCONNECT); + GNUNET_MQ_notify_sent (env, + &schedule_action, + h); + GNUNET_MQ_send (h->mq, + env); + return; + } + GNUNET_CONTAINER_DLL_remove (h->action_head, + h->action_tail, + h->current); + switch (h->current->type) + { + case ACTION_GET: + transmit_get (h); + break; + + case ACTION_SET: + case ACTION_UPDATE: + transmit_set (h); + break; + + case ACTION_WATCH: + transmit_watch (h); + break; + + default: + GNUNET_assert (0); + break; + } + } +} + + +struct GNUNET_STATISTICS_GetHandle * +GNUNET_STATISTICS_get (struct GNUNET_STATISTICS_Handle *handle, + const char *subsystem, + const char *name, + GNUNET_STATISTICS_Callback cont, + GNUNET_STATISTICS_Iterator proc, + void *cls) +{ + size_t slen1; + size_t slen2; + struct GNUNET_STATISTICS_GetHandle *ai; + + if (NULL == handle) + return NULL; + GNUNET_assert (NULL != proc); + GNUNET_assert (GNUNET_NO == handle->do_destroy); + if (NULL == subsystem) + subsystem = ""; + if (NULL == name) + name = ""; + slen1 = strlen (subsystem) + 1; + slen2 = strlen (name) + 1; + GNUNET_assert (slen1 + slen2 + sizeof(struct GNUNET_MessageHeader) < + GNUNET_MAX_MESSAGE_SIZE); + ai = GNUNET_new (struct GNUNET_STATISTICS_GetHandle); + ai->sh = handle; + ai->subsystem = GNUNET_strdup (subsystem); + ai->name = GNUNET_strdup (name); + ai->cont = cont; + ai->proc = proc; + ai->cls = cls; + ai->type = ACTION_GET; + ai->msize = slen1 + slen2 + sizeof(struct GNUNET_MessageHeader); + GNUNET_CONTAINER_DLL_insert_tail (handle->action_head, + handle->action_tail, + ai); + schedule_action (handle); + return ai; +} + + +void +GNUNET_STATISTICS_get_cancel (struct GNUNET_STATISTICS_GetHandle *gh) +{ + if (NULL == gh) + return; + gh->cont = NULL; + if (gh->sh->current == gh) + { + gh->aborted = GNUNET_YES; + return; + } + GNUNET_CONTAINER_DLL_remove (gh->sh->action_head, + gh->sh->action_tail, + gh); + GNUNET_free (gh->name); + GNUNET_free (gh->subsystem); + GNUNET_free (gh); +} + + +/** + * Watch statistics from the peer (be notified whenever they change). + * + * @param handle identification of the statistics service + * @param subsystem limit to the specified subsystem, never NULL + * @param name name of the statistic value, never NULL + * @param proc function to call on each value + * @param proc_cls closure for @a proc + * @return #GNUNET_OK on success, #GNUNET_SYSERR on error + */ +int +GNUNET_STATISTICS_watch (struct GNUNET_STATISTICS_Handle *handle, + const char *subsystem, + const char *name, + GNUNET_STATISTICS_Iterator proc, + void *proc_cls) +{ + struct GNUNET_STATISTICS_WatchEntry *w; + + if (NULL == handle) + return GNUNET_SYSERR; + w = GNUNET_new (struct GNUNET_STATISTICS_WatchEntry); + w->subsystem = GNUNET_strdup (subsystem); + w->name = GNUNET_strdup (name); + w->proc = proc; + w->proc_cls = proc_cls; + GNUNET_array_append (handle->watches, + handle->watches_size, + w); + schedule_watch_request (handle, + w); + return GNUNET_OK; +} + + +/** + * Stop watching statistics from the peer. + * + * @param handle identification of the statistics service + * @param subsystem limit to the specified subsystem, never NULL + * @param name name of the statistic value, never NULL + * @param proc function to call on each value + * @param proc_cls closure for @a proc + * @return #GNUNET_OK on success, #GNUNET_SYSERR on error (no such watch) + */ +int +GNUNET_STATISTICS_watch_cancel (struct GNUNET_STATISTICS_Handle *handle, + const char *subsystem, + const char *name, + GNUNET_STATISTICS_Iterator proc, + void *proc_cls) +{ + struct GNUNET_STATISTICS_WatchEntry *w; + + if (NULL == handle) + return GNUNET_SYSERR; + for (unsigned int i = 0; i < handle->watches_size; i++) + { + w = handle->watches[i]; + if (NULL == w) + continue; + if ((w->proc == proc) && + (w->proc_cls == proc_cls) && + (0 == strcmp (w->name, + name)) && + (0 == strcmp (w->subsystem, + subsystem))) + { + GNUNET_free (w->name); + GNUNET_free (w->subsystem); + GNUNET_free (w); + handle->watches[i] = NULL; + return GNUNET_OK; + } + } + return GNUNET_SYSERR; +} + + +/** + * Queue a request to change a statistic. + * + * @param h statistics handle + * @param name name of the value + * @param make_persistent should the value be kept across restarts? + * @param value new value or change + * @param type type of the action (#ACTION_SET or #ACTION_UPDATE) + */ +static void +add_setter_action (struct GNUNET_STATISTICS_Handle *h, + const char *name, + int make_persistent, + uint64_t value, + enum ActionType type) +{ + struct GNUNET_STATISTICS_GetHandle *ai; + size_t slen; + size_t nlen; + size_t nsize; + int64_t delta; + + slen = strlen (h->subsystem) + 1; + nlen = strlen (name) + 1; + nsize = sizeof(struct GNUNET_STATISTICS_SetMessage) + slen + nlen; + if (nsize >= GNUNET_MAX_MESSAGE_SIZE) + { + GNUNET_break (0); + return; + } + for (ai = h->action_head; NULL != ai; ai = ai->next) + { + if (! ((0 == strcmp (ai->subsystem, + h->subsystem)) && + (0 == strcmp (ai->name, + name)) && + ((ACTION_UPDATE == ai->type) || + (ACTION_SET == ai->type)))) + continue; + if (ACTION_SET == ai->type) + { + if (ACTION_UPDATE == type) + { + delta = (int64_t) value; + if (delta > 0) + { + /* update old set by new delta */ + ai->value += delta; + } + else + { + /* update old set by new delta, but never go negative */ + if (ai->value < -delta) + ai->value = 0; + else + ai->value += delta; + } + } + else + { + /* new set overrides old set */ + ai->value = value; + } + } + else + { + if (ACTION_UPDATE == type) + { + /* make delta cumulative */ + delta = (int64_t) value; + ai->value += delta; + } + else + { + /* drop old 'update', use new 'set' instead */ + ai->value = value; + ai->type = type; + } + } + ai->timeout + = GNUNET_TIME_relative_to_absolute (SET_TRANSMIT_TIMEOUT); + ai->make_persistent + = make_persistent; + return; + } + /* no existing entry matches, create a fresh one */ + ai = GNUNET_new (struct GNUNET_STATISTICS_GetHandle); + ai->sh = h; + ai->subsystem = GNUNET_strdup (h->subsystem); + ai->name = GNUNET_strdup (name); + ai->timeout = GNUNET_TIME_relative_to_absolute (SET_TRANSMIT_TIMEOUT); + ai->make_persistent = make_persistent; + ai->msize = nsize; + ai->value = value; + ai->type = type; + GNUNET_CONTAINER_DLL_insert_tail (h->action_head, + h->action_tail, + ai); + schedule_action (h); +} + + +void +GNUNET_STATISTICS_set (struct GNUNET_STATISTICS_Handle *handle, + const char *name, + uint64_t value, + int make_persistent) +{ + if (NULL == handle) + return; + GNUNET_assert (GNUNET_NO == handle->do_destroy); + add_setter_action (handle, + name, + make_persistent, + value, + ACTION_SET); +} + + +void +GNUNET_STATISTICS_update (struct GNUNET_STATISTICS_Handle *handle, + const char *name, + int64_t delta, + int make_persistent) +{ + if (NULL == handle) + return; + if (0 == delta) + return; + GNUNET_assert (GNUNET_NO == handle->do_destroy); + add_setter_action (handle, + name, + make_persistent, + (uint64_t) delta, + ACTION_UPDATE); +} + + +/* end of statistics_api.c */ diff --git a/src/service/statistics/test_gnunet_statistics.py.in b/src/service/statistics/test_gnunet_statistics.py.in new file mode 100644 index 000000000..bf6ba6ef2 --- /dev/null +++ b/src/service/statistics/test_gnunet_statistics.py.in @@ -0,0 +1,171 @@ +#!@PYTHONEXE@ + +import os +import sys +import shutil +import re +import subprocess +import time + +raw_tmp = True +if os.name == "nt": + tmp = os.getenv("TEMP") +elif None != os.environ.get("TMPDIR"): + tmp = os.getenv("TMPDIR") +elif None != os.environ.get("TMP"): + tmp = os.getenv("TMP") +else: + raw_tmp = False + tmp = subprocess.check_output(["gnunet-config", "-f", "-s", "PATHS", "-o", "GNUNET_TMP"], + text=True) + +if os.name == 'nt': + st = './gnunet-statistics.exe' + arm = 'gnunet-arm.exe' +else: + st = './gnunet-statistics' + arm = 'gnunet-arm' + +run_st = [st, '-c', 'test_statistics_api_data.conf'] +run_arm = [arm, '-c', 'test_statistics_api_data.conf'] +debug = os.getenv('DEBUG') +if debug: + run_arm += [debug.split(' ')] + + +if raw_tmp: + cleanup_path = "gnunet/test-gnunet-statistics" +else: + cleanup_path = "test-gnunet-statistics" + + +def cleanup(): + shutil.rmtree(os.path.join(tmp, cleanup_path), True) + + +def sub_run(args, want_stdo=True, want_stde=False, nofail=False): + if want_stdo: + stdo = subprocess.PIPE + else: + stdo = None + if want_stde: + stde = subprocess.PIPE + else: + stde = None + p = subprocess.Popen(args, stdout=stdo, stderr=stde) + stdo, stde = p.communicate() + if not nofail: + if p.returncode != 0: + sys.exit(p.returncode) + return (p.returncode, stdo, stde) + + +def fail(result): + print(result) + r_arm(['-e'], want_stdo=False) + sys.exit(1) + + +def r_arm(extra_args, **kw): + rc, stdo, stde = sub_run(run_arm + extra_args, **kw) + if rc != 0: + fail("FAIL: error running {}".format(run_arm)) + return (rc, stdo, stde) + + +def r_st(extra_args, normal=True, **kw): + rc, stdo, stde = sub_run(run_st + extra_args, **kw) + if normal: + if rc != 0: + fail("FAIL: error running {}".format(run_st)) + else: + if rc == 0: + fail("FAIL: expected error while running {}".format(run_st)) + return (rc, stdo, stde) + + +def restart(): + print("Restarting service...") + t = r_arm(['-k', 'statistics']) + time.sleep(1) + t = r_arm(['-i', 'statistics']) + time.sleep(1) + + +cleanup() + +print("Preparing: Starting service...") +t = r_arm(['-s'], want_stdo=False) +time.sleep(1) +t = r_arm(['-i', 'statistics'], want_stdo=False) +time.sleep(1) + +print("TEST: Bad argument checking...", end='') +r_st(['-x'], normal=False, nofail=True, want_stdo=False, want_stde=True) +print("PASS") + +print("TEST: Set value...", end='') +r_st(['-n', 'test', '-s', 'subsystem', b'42'], nofail=True, want_stdo=False) +print("PASS") + +print("TEST: Set another value...", end='') +r_st(['-n', 'other', '-s', 'osystem', b'43'], nofail=True, want_stdo=False) +print("PASS") + +#print("TEST: Viewing all stats...", end='') +#rc, stdo, stde = r_st([], nofail=True, want_stdo=True) +#if len(stdo.splitlines()) != 2: +# fail("FAIL: unexpected output:\n{}".format(stdo)) +#print("PASS") + +print("TEST: Viewing stats by name...", end='') +rc, stdo, stde = r_st(['-n', 'other'], nofail=True, want_stdo=True) +if len([x for x in stdo.splitlines() if re.search(b'43', x)]) != 1: + fail("FAIL: unexpected output:\n{}".format(stdo)) +print("PASS") + +print("TEST: Viewing stats by subsystem...", end='') +rc, stdo, stde = r_st(['-s', 'subsystem'], nofail=True, want_stdo=True) +if len([x for x in stdo.splitlines() if re.search(b'42', x)]) != 1: + fail("FAIL: unexpected output:\n{}".format(stdo)) +print("PASS") + +print("TEST: Set persistent value...", end='') +rc, stdo, stde = r_st(['-n', 'lasting', '-s', 'subsystem', '40', '-p'], + nofail=True, + want_stdo=False) +rc, stdo, stde = r_st([], nofail=True, want_stdo=True) +if len([x for x in stdo.splitlines() if re.search(b'40', x)]) != 1: + fail("FAIL: unexpected output:\n{}".format(stdo)) +print("PASS") + +restart() + +print("TEST: Checking persistence...", end='') +rc, stdo, stde = r_st([], nofail=True, want_stdo=True) +if len([x for x in stdo.splitlines() if re.search(b'40', x)]) != 1: + fail("FAIL: unexpected output:\n{}".format(stdo)) +print("PASS") + +print("TEST: Removing persistence...", end='') +rc, stdo, stde = r_st(['-n', 'lasting', '-s', 'subsystem', '40'], + nofail=True, + want_stdo=False) +rc, stdo, stde = r_st([], nofail=True, want_stdo=True) +if len([x for x in stdo.splitlines() if re.search(b'!', x)]) != 0: + fail("FAIL: unexpected output:\n{}".format(stdo)) +print("PASS") + +restart() + +print("TEST: Checking removed persistence...", end='') +rc, stdo, stde = r_st([], nofail=True, want_stdo=True) +if len([x for x in stdo.splitlines() if re.search(b'40', x)]) != 0: + fail("FAIL: unexpected output:\n{}".format(stdo)) +print("PASS") + +print("Stopping service...") +t = r_arm(['-e'], want_stdo=False) +time.sleep(1) + +cleanup() diff --git a/src/service/statistics/test_statistics_api.c b/src/service/statistics/test_statistics_api.c new file mode 100644 index 000000000..c9e568870 --- /dev/null +++ b/src/service/statistics/test_statistics_api.c @@ -0,0 +1,253 @@ +/* + This file is part of GNUnet. + Copyright (C) 2009, 2012, 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 . + + SPDX-License-Identifier: AGPL3.0-or-later + */ +/** + * @file statistics/test_statistics_api.c + * @brief testcase for statistics_api.c + * @author Christian Grothoff + */ +#include "platform.h" +#include "gnunet_util_lib.h" +#include "gnunet_statistics_service.h" + + +static struct GNUNET_STATISTICS_Handle *h; + +static struct GNUNET_STATISTICS_GetHandle *g; + + +static void +do_shutdown () +{ + if (NULL != g) + { + GNUNET_STATISTICS_get_cancel (g); + g = NULL; + } + GNUNET_STATISTICS_destroy (h, GNUNET_NO); + h = NULL; +} + + +static int +check_1 (void *cls, + const char *subsystem, + const char *name, + uint64_t value, + int is_persistent) +{ + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Received value %llu for `%s:%s\n", + (unsigned long long) value, + subsystem, + name); + GNUNET_assert (0 == strcmp (name, "test-1")); + GNUNET_assert (0 == strcmp (subsystem, "test-statistics-api")); + GNUNET_assert (value == 1); + GNUNET_assert (is_persistent == GNUNET_NO); + return GNUNET_OK; +} + + +static int +check_2 (void *cls, + const char *subsystem, + const char *name, + uint64_t value, + int is_persistent) +{ + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Received value %llu for `%s:%s\n", + (unsigned long long) value, + subsystem, + name); + GNUNET_assert (0 == strcmp (name, "test-2")); + GNUNET_assert (0 == strcmp (subsystem, "test-statistics-api")); + GNUNET_assert (value == 2); + GNUNET_assert (is_persistent == GNUNET_NO); + return GNUNET_OK; +} + + +static int +check_3 (void *cls, + const char *subsystem, + const char *name, + uint64_t value, + int is_persistent) +{ + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Received value %llu for `%s:%s\n", + (unsigned long long) value, + subsystem, + name); + GNUNET_assert (0 == strcmp (name, "test-3")); + GNUNET_assert (0 == strcmp (subsystem, "test-statistics-api")); + GNUNET_assert (value == 3); + GNUNET_assert (is_persistent == GNUNET_YES); + return GNUNET_OK; +} + + +static void +next_fin (void *cls, + int success) +{ + int *ok = cls; + + g = NULL; + GNUNET_SCHEDULER_shutdown (); + GNUNET_assert (success == GNUNET_OK); + *ok = 0; +} + + +static void +next (void *cls, int success) +{ + g = NULL; + GNUNET_assert (success == GNUNET_OK); + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Issuing GET request\n"); + GNUNET_break (NULL != + GNUNET_STATISTICS_get (h, NULL, "test-2", + &next_fin, + &check_2, cls)); +} + + +static void +run (void *cls, + char *const *args, + const char *cfgfile, + const struct GNUNET_CONFIGURATION_Handle *cfg) +{ + h = GNUNET_STATISTICS_create ("test-statistics-api", cfg); + if (NULL == h) + { + GNUNET_break (0); + return; + } + GNUNET_SCHEDULER_add_shutdown (&do_shutdown, + NULL); + GNUNET_STATISTICS_set (h, "test-1", 1, GNUNET_NO); + GNUNET_STATISTICS_set (h, "test-2", 2, GNUNET_NO); + GNUNET_STATISTICS_set (h, "test-3", 2, GNUNET_NO); + GNUNET_STATISTICS_update (h, "test-3", 1, GNUNET_YES); + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Issuing GET request\n"); + GNUNET_break (NULL != + (g = GNUNET_STATISTICS_get (h, NULL, "test-1", + &next, + &check_1, cls))); +} + + +static void +run_more (void *cls, + char *const *args, + const char *cfgfile, + const struct GNUNET_CONFIGURATION_Handle *cfg) +{ + h = GNUNET_STATISTICS_create ("test-statistics-api", + cfg); + GNUNET_SCHEDULER_add_shutdown (&do_shutdown, + NULL); + GNUNET_break (NULL != + (g = GNUNET_STATISTICS_get (h, NULL, + "test-3", + &next_fin, + &check_3, cls))); +} + + +int +main (int argc, char *argv_ign[]) +{ + int ok = 1; + char *const argv[] = { "test-statistics-api", + "-c", + "test_statistics_api_data.conf", + "-L", "WARNING", + NULL }; + struct GNUNET_GETOPT_CommandLineOption options[] = { + GNUNET_GETOPT_OPTION_END + }; + struct GNUNET_OS_Process *proc; + char *binary; + + GNUNET_log_setup ("test_statistics_api", + "WARNING", + NULL); + binary = GNUNET_OS_get_libexec_binary_path ("gnunet-service-statistics"); + proc = + GNUNET_OS_start_process (GNUNET_OS_INHERIT_STD_OUT_AND_ERR + | GNUNET_OS_USE_PIPE_CONTROL, + NULL, NULL, NULL, + binary, + "gnunet-service-statistics", + "-c", "test_statistics_api_data.conf", NULL); + GNUNET_assert (NULL != proc); + GNUNET_PROGRAM_run (5, argv, + "test-statistics-api", "nohelp", + options, &run, + &ok); + if (0 != GNUNET_OS_process_kill (proc, + GNUNET_TERM_SIG)) + { + GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING, "kill"); + ok = 1; + } + GNUNET_OS_process_wait (proc); + GNUNET_OS_process_destroy (proc); + proc = NULL; + if (ok != 0) + { + GNUNET_free (binary); + return ok; + } + ok = 1; + /* restart to check persistence! */ + proc = + GNUNET_OS_start_process (GNUNET_OS_INHERIT_STD_OUT_AND_ERR + | GNUNET_OS_USE_PIPE_CONTROL, + NULL, NULL, NULL, + binary, + "gnunet-service-statistics", + "-c", "test_statistics_api_data.conf", + NULL); + GNUNET_PROGRAM_run (5, argv, + "test-statistics-api", "nohelp", + options, + &run_more, &ok); + if (0 != GNUNET_OS_process_kill (proc, + GNUNET_TERM_SIG)) + { + GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING, "kill"); + ok = 1; + } + GNUNET_OS_process_wait (proc); + GNUNET_OS_process_destroy (proc); + proc = NULL; + GNUNET_free (binary); + return ok; +} + + +/* end of test_statistics_api.c */ diff --git a/src/service/statistics/test_statistics_api_data.conf b/src/service/statistics/test_statistics_api_data.conf new file mode 100644 index 000000000..d437c2aa8 --- /dev/null +++ b/src/service/statistics/test_statistics_api_data.conf @@ -0,0 +1,5 @@ +@INLINE@ ../../contrib/conf/gnunet/no_forcestart.conf +@INLINE@ ../../contrib/conf/gnunet/no_autostart_above_core.conf + +[PATHS] +GNUNET_TEST_HOME = $GNUNET_TMP/test-gnunet-statistics/ diff --git a/src/service/statistics/test_statistics_api_loop.c b/src/service/statistics/test_statistics_api_loop.c new file mode 100644 index 000000000..ad273287d --- /dev/null +++ b/src/service/statistics/test_statistics_api_loop.c @@ -0,0 +1,123 @@ +/* + This file is part of GNUnet. + Copyright (C) 2009 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 . + + SPDX-License-Identifier: AGPL3.0-or-later + */ +/** + * @file statistics/test_statistics_api_loop.c + * @brief testcase for statistics_api.c + */ +#include "platform.h" +#include "gnunet_util_lib.h" +#include "gnunet_statistics_service.h" + +#define ROUNDS (1024 * 1024) + +static struct GNUNET_STATISTICS_Handle *h; + + +static int +check_1 (void *cls, + const char *subsystem, + const char *name, + uint64_t value, + int is_persistent) +{ + GNUNET_assert (0 == strcmp (name, "test-0")); + GNUNET_assert (0 == strcmp (subsystem, "test-statistics-api-loop")); + GNUNET_assert (is_persistent == GNUNET_NO); + return GNUNET_OK; +} + + +static void +next (void *cls, + int success) +{ + int *ok = cls; + + GNUNET_STATISTICS_destroy (h, GNUNET_NO); + GNUNET_assert (success == GNUNET_OK); + *ok = 0; +} + + +static void +run (void *cls, + char *const *args, + const char *cfgfile, + const struct GNUNET_CONFIGURATION_Handle *cfg) +{ + unsigned int i; + char name[128]; + + h = GNUNET_STATISTICS_create ("test-statistics-api-loop", cfg); + for (i = 0; i < ROUNDS; i++) + { + GNUNET_snprintf (name, sizeof(name), "test-%d", i % 32); + GNUNET_STATISTICS_set (h, name, i, GNUNET_NO); + GNUNET_snprintf (name, sizeof(name), "test-%d", i % 16); + GNUNET_STATISTICS_update (h, name, 1, GNUNET_NO); + } + i = 0; + GNUNET_break (NULL != + GNUNET_STATISTICS_get (h, NULL, "test-0", + &next, + &check_1, cls)); +} + + +int +main (int argc, char *argv_ign[]) +{ + int ok = 1; + + char *const argv[] = { "test-statistics-api", + "-c", + "test_statistics_api_data.conf", + NULL }; + struct GNUNET_GETOPT_CommandLineOption options[] = { + GNUNET_GETOPT_OPTION_END + }; + struct GNUNET_OS_Process *proc; + char *binary; + + binary = GNUNET_OS_get_libexec_binary_path ("gnunet-service-statistics"); + proc = + GNUNET_OS_start_process (GNUNET_OS_INHERIT_STD_OUT_AND_ERR + | GNUNET_OS_USE_PIPE_CONTROL, + NULL, NULL, NULL, + binary, + "gnunet-service-statistics", + "-c", "test_statistics_api_data.conf", NULL); + GNUNET_assert (NULL != proc); + GNUNET_PROGRAM_run (3, argv, "test-statistics-api", "nohelp", options, &run, + &ok); + if (0 != GNUNET_OS_process_kill (proc, GNUNET_TERM_SIG)) + { + GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING, "kill"); + ok = 1; + } + GNUNET_OS_process_wait (proc); + GNUNET_OS_process_destroy (proc); + proc = NULL; + GNUNET_free (binary); + return ok; +} + + +/* end of test_statistics_api_loop.c */ diff --git a/src/service/statistics/test_statistics_api_watch.c b/src/service/statistics/test_statistics_api_watch.c new file mode 100644 index 000000000..2d9d08305 --- /dev/null +++ b/src/service/statistics/test_statistics_api_watch.c @@ -0,0 +1,156 @@ +/* + This file is part of GNUnet. + Copyright (C) 2009, 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 . + + SPDX-License-Identifier: AGPL3.0-or-later + */ +/** + * @file statistics/test_statistics_api_watch.c + * @brief testcase for statistics_api.c watch functions + * @author Christian Grothoff + */ +#include "platform.h" +#include "gnunet_util_lib.h" +#include "gnunet_statistics_service.h" + + +static int ok; + +static struct GNUNET_STATISTICS_Handle *h; + +static struct GNUNET_STATISTICS_Handle *h2; + +static struct GNUNET_SCHEDULER_Task *shutdown_task; + + +static void +force_shutdown (void *cls) +{ + fprintf (stderr, "Timeout, failed to receive notifications: %d\n", ok); + GNUNET_STATISTICS_destroy (h, GNUNET_NO); + GNUNET_STATISTICS_destroy (h2, GNUNET_NO); + ok = 7; +} + + +static void +normal_shutdown (void *cls) +{ + GNUNET_STATISTICS_destroy (h, GNUNET_NO); + GNUNET_STATISTICS_destroy (h2, GNUNET_NO); +} + + +static int +watch_1 (void *cls, + const char *subsystem, + const char *name, + uint64_t value, + int is_persistent) +{ + GNUNET_assert (value == 42); + GNUNET_assert (0 == strcmp (name, "test-1")); + ok &= ~1; + if (0 == ok) + { + GNUNET_SCHEDULER_cancel (shutdown_task); + GNUNET_SCHEDULER_add_now (&normal_shutdown, NULL); + } + return GNUNET_OK; +} + + +static int +watch_2 (void *cls, + const char *subsystem, + const char *name, + uint64_t value, + int is_persistent) +{ + GNUNET_assert (value == 43); + GNUNET_assert (0 == strcmp (name, "test-2")); + ok &= ~2; + if (0 == ok) + { + GNUNET_SCHEDULER_cancel (shutdown_task); + GNUNET_SCHEDULER_add_now (&normal_shutdown, NULL); + } + return GNUNET_OK; +} + + +static void +run (void *cls, + char *const *args, + const char *cfgfile, + const struct GNUNET_CONFIGURATION_Handle *cfg) +{ + h = GNUNET_STATISTICS_create ("dummy", cfg); + GNUNET_assert (GNUNET_OK == + GNUNET_STATISTICS_watch (h, "test-statistics-api-watch", + "test-1", &watch_1, NULL)); + GNUNET_assert (GNUNET_OK == + GNUNET_STATISTICS_watch (h, "test-statistics-api-watch", + "test-2", &watch_2, NULL)); + h2 = GNUNET_STATISTICS_create ("test-statistics-api-watch", cfg); + GNUNET_STATISTICS_set (h2, "test-1", 42, GNUNET_NO); + GNUNET_STATISTICS_set (h2, "test-2", 43, GNUNET_NO); + shutdown_task = + GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_UNIT_MINUTES, + &force_shutdown, + NULL); +} + + +int +main (int argc, char *argv_ign[]) +{ + char *const argv[] = { "test-statistics-api", + "-c", + "test_statistics_api_data.conf", + NULL }; + struct GNUNET_GETOPT_CommandLineOption options[] = { + GNUNET_GETOPT_OPTION_END + }; + struct GNUNET_OS_Process *proc; + char *binary; + + binary = GNUNET_OS_get_libexec_binary_path ("gnunet-service-statistics"); + proc = + GNUNET_OS_start_process (GNUNET_OS_INHERIT_STD_OUT_AND_ERR + | GNUNET_OS_USE_PIPE_CONTROL, + NULL, NULL, NULL, + binary, + "gnunet-service-statistics", + "-c", "test_statistics_api_data.conf", NULL); + GNUNET_assert (NULL != proc); + ok = 3; + GNUNET_PROGRAM_run (3, argv, "test-statistics-api", "nohelp", options, &run, + NULL); + if (0 != GNUNET_OS_process_kill (proc, GNUNET_TERM_SIG)) + { + GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING, "kill"); + ok = 1; + } + GNUNET_OS_process_wait (proc); + GNUNET_OS_process_destroy (proc); + proc = NULL; + GNUNET_free (binary); + return ok; +} + + +/* end of test_statistics_api_watch.c */ diff --git a/src/service/statistics/test_statistics_api_watch_zero_value.c b/src/service/statistics/test_statistics_api_watch_zero_value.c new file mode 100644 index 000000000..cb2694f8f --- /dev/null +++ b/src/service/statistics/test_statistics_api_watch_zero_value.c @@ -0,0 +1,197 @@ +/* + This file is part of GNUnet. + Copyright (C) 2009, 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 . + + SPDX-License-Identifier: AGPL3.0-or-later + */ +/** + * @file statistics/test_statistics_api_watch_zero_value.c + * @brief testcase for statistics_api.c watch functions with initial 0 value + */ +#include "platform.h" +#include "gnunet_util_lib.h" +#include "gnunet_statistics_service.h" + +static int ok; + +static int ok2; + +static struct GNUNET_STATISTICS_Handle *h; + +static struct GNUNET_STATISTICS_Handle *h2; + +static struct GNUNET_SCHEDULER_Task *shutdown_task; + + +static void +force_shutdown (void *cls) +{ + fprintf (stderr, "Timeout, failed to receive notifications: %d\n", ok); + GNUNET_STATISTICS_destroy (h, GNUNET_NO); + GNUNET_STATISTICS_destroy (h2, GNUNET_NO); + ok = 7; +} + + +static void +normal_shutdown (void *cls) +{ + GNUNET_STATISTICS_destroy (h, GNUNET_NO); + GNUNET_STATISTICS_destroy (h2, GNUNET_NO); +} + + +static int +watch_1 (void *cls, const char *subsystem, const char *name, uint64_t value, + int is_persistent) +{ + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Received value `%s' `%s' %llu\n", + subsystem, + name, + (unsigned long long) value); + GNUNET_assert (0 == strcmp (name, "test-1")); + if ((0 == value) && (3 == ok)) + { + ok--; + GNUNET_STATISTICS_set (h, "test-1", 42, GNUNET_NO); + } + + if ((42 == value) && (2 == ok)) + { + ok--; + GNUNET_STATISTICS_set (h, "test-1", 0, GNUNET_NO); + } + + if ((0 == value) && (1 == ok)) + { + ok--; + } + if ((0 == ok) && (0 == ok2)) + { + GNUNET_SCHEDULER_cancel (shutdown_task); + GNUNET_SCHEDULER_add_now (&normal_shutdown, NULL); + } + + return GNUNET_OK; +} + + +static int +watch_2 (void *cls, + const char *subsystem, + const char *name, + uint64_t value, + int is_persistent) +{ + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Received value `%s' `%s' %llu\n", + subsystem, + name, + (unsigned long long) value); + + GNUNET_assert (0 == strcmp (name, "test-2")); + if ((42 == value) && (1 == ok2)) + { + ok2 = 0; + if (0 == ok) + { + GNUNET_SCHEDULER_cancel (shutdown_task); + GNUNET_SCHEDULER_add_now (&normal_shutdown, NULL); + } + } + else + { + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "Received unexpected value %llu\n", + (unsigned long long) value); + + GNUNET_break (0); + GNUNET_SCHEDULER_cancel (shutdown_task); + GNUNET_SCHEDULER_add_now (&normal_shutdown, NULL); + } + + return GNUNET_OK; +} + + +static void +run (void *cls, char *const *args, const char *cfgfile, + const struct GNUNET_CONFIGURATION_Handle *cfg) +{ + h = GNUNET_STATISTICS_create ("dummy", cfg); + h2 = GNUNET_STATISTICS_create ("dummy-2", cfg); + GNUNET_assert (GNUNET_OK == + GNUNET_STATISTICS_watch (h, "dummy", + "test-1", &watch_1, NULL)); + + GNUNET_assert (GNUNET_OK == + GNUNET_STATISTICS_watch (h2, "dummy-2", + "test-2", &watch_2, NULL)); + + /* Set initial value to 0 */ + GNUNET_STATISTICS_set (h, "test-1", 0, GNUNET_NO); + GNUNET_STATISTICS_set (h2, "test-2", 42, GNUNET_NO); + + shutdown_task = + GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_UNIT_MINUTES, + &force_shutdown, + NULL); +} + + +int +main (int argc, char *argv_ign[]) +{ + char *const argv[] = { "test-statistics-api", + "-c", + "test_statistics_api_data.conf", + NULL }; + struct GNUNET_GETOPT_CommandLineOption options[] = { + GNUNET_GETOPT_OPTION_END + }; + struct GNUNET_OS_Process *proc; + char *binary; + + binary = GNUNET_OS_get_libexec_binary_path ("gnunet-service-statistics"); + proc = + GNUNET_OS_start_process (GNUNET_OS_INHERIT_STD_OUT_AND_ERR + | GNUNET_OS_USE_PIPE_CONTROL, + NULL, NULL, NULL, + binary, + "gnunet-service-statistics", + "-c", "test_statistics_api_data.conf", NULL); + GNUNET_assert (NULL != proc); + ok = 3; + ok2 = 1; + GNUNET_PROGRAM_run (3, argv, "test-statistics-api", "nohelp", options, &run, + NULL); + if (0 != GNUNET_OS_process_kill (proc, GNUNET_TERM_SIG)) + { + GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING, "kill"); + ok = 1; + } + GNUNET_OS_process_wait (proc); + GNUNET_OS_process_destroy (proc); + proc = NULL; + GNUNET_free (binary); + if ((0 == ok) && (0 == ok2)) + return 0; + return 1; +} + + +/* end of test_statistics_api_watch_zero_value.c */ diff --git a/src/service/transport/.gitignore b/src/service/transport/.gitignore new file mode 100644 index 000000000..163ffbd5d --- /dev/null +++ b/src/service/transport/.gitignore @@ -0,0 +1,94 @@ +gnunet-transport-wlan-sender +gnunet-helper-transport-bluetooth +gnunet-helper-transport-wlan +gnunet-helper-transport-wlan-dummy +gnunet-service-transport +gnunet-transport +gnunet-transport-certificate-creation +gnunet-transport-profiler +gnunet-transport-wlan-receiver +https_cert_qutoa_p2.crt +https_key_quota_p2.key +test_http_common +test_plugin_bluetooth +test_plugin_http_client +test_plugin_http_server +test_plugin_https_client +test_plugin_https_server +test_plugin_tcp +test_plugin_udp +test_plugin_unix +test_plugin_wlan +test_quota_compliance_bluetooth +test_quota_compliance_bluetooth_asymmetric +test_quota_compliance_http +test_quota_compliance_http_asymmetric +test_quota_compliance_https +test_quota_compliance_https_asymmetric +test_quota_compliance_tcp +test_quota_compliance_tcp_asymmetric +test_quota_compliance_udp +test_quota_compliance_unix +test_quota_compliance_unix_asymmetric +test_quota_compliance_wlan +test_quota_compliance_wlan_asymmetric +test_transport_address_switch_http +test_transport_address_switch_https +test_transport_address_switch_tcp +test_transport_address_switch_udp +test_transport_api_blacklisting_tcp +test_transport_api_bluetooth +test_transport_api_disconnect_tcp +test_transport_api_http +test_transport_api_http_reverse +test_transport_api_https +test_transport_api_limited_sockets_tcp +test_transport_api_manipulation_cfg +test_transport_api_manipulation_recv_tcp +test_transport_api_manipulation_send_tcp +test_transport_api_monitor_peers +test_transport_api_multi +test_transport_api_reliability_bluetooth +test_transport_api_reliability_http +test_transport_api_reliability_http_xhr +test_transport_api_reliability_https +test_transport_api_reliability_https_xhr +test_transport_api_reliability_tcp +test_transport_api_reliability_tcp_nat +test_transport_api_reliability_udp +test_transport_api_reliability_unix +test_transport_api_reliability_wlan +test_transport_api_restart_1peer +test_transport_api_restart_2peers +test_transport_api_slow_ats +test_transport_api_tcp +test_transport_api_tcp_nat +test_transport_api_timeout_bluetooth +test_transport_api_timeout_http +test_transport_api_timeout_https +test_transport_api_timeout_tcp +test_transport_api_timeout_udp +test_transport_api_timeout_unix +test_transport_api_timeout_wlan +test_transport_api_udp +test_transport_api_udp_nat +test_transport_api_unix +test_transport_api_unix_abstract +test_transport_api_wlan +test_transport_blacklisting_inbound_bl_full +test_transport_blacklisting_inbound_bl_plugin +test_transport_blacklisting_multiple_plugins +test_transport_blacklisting_no_bl +test_transport_blacklisting_outbound_bl_full +test_transport_blacklisting_outbound_bl_plugin +test_transport_testing_restart +test_transport_testing_startstop +gnunet-communicator-unix +gnunet-service-tng +gnunet-communicator-tcp +gnunet-communicator-udp +test_communicator_basic-* +test_communicator_rekey-* +test_transport_start_with_config +test_transport_api2_tcp +gnunet-communicator-quic diff --git a/src/service/transport/Makefile.am b/src/service/transport/Makefile.am new file mode 100644 index 000000000..e5a3fe9bd --- /dev/null +++ b/src/service/transport/Makefile.am @@ -0,0 +1,460 @@ +# This Makefile.am is in the public domain +AM_CPPFLAGS = -I$(top_srcdir)/src/include -I$(top_builddir)/src/include + +plugindir = $(libdir)/gnunet + +pkgcfgdir= $(pkgdatadir)/config.d/ + +libexecdir= $(pkglibdir)/libexec/ + +pkgcfg_DATA = \ + transport.conf + +if USE_COVERAGE + AM_CFLAGS = --coverage -O0 +endif + +noinst_PROGRAMS = \ + test_transport_start_with_config \ + gnunet-communicator-udp + +TESTING_LIBS = \ + libgnunettransporttesting2.la + +lib_LTLIBRARIES = \ + libgnunettransportapplication.la \ + libgnunettransportcore.la \ + libgnunettransportcommunicator.la \ + libgnunettransportmonitor.la \ + $(TESTING_LIBS) + +libgnunettransporttesting2_la_SOURCES = \ + transport_api_traits.c \ + transport_api_cmd_connecting_peers.c \ + transport_api_cmd_backchannel_check.c \ + transport_api_cmd_start_peer.c \ + transport_api_cmd_stop_peer.c \ + transport_api_cmd_send_simple.c \ + transport_api_cmd_send_simple_performance.c \ + transport-testing2.c transport-testing2.h \ + transport-testing-cmds.h \ + transport-testing-filenames2.c \ + transport-testing-loggers2.c \ + transport-testing-main2.c \ + transport-testing-send2.c \ + transport-testing-communicator.c transport-testing-communicator.h +libgnunettransporttesting2_la_LIBADD = \ + libgnunettransportapplication.la \ + libgnunettransportcore.la \ + $(top_builddir)/src/service/arm/libgnunetarm.la \ + $(top_builddir)/src/service/testing/libgnunettesting.la \ + $(top_builddir)/src/lib/hello/libgnunethello.la \ + $(top_builddir)/src/service/peerstore/libgnunetpeerstore.la \ + $(top_builddir)/src/lib/util/libgnunetutil.la +libgnunettransporttesting2_la_LDFLAGS = \ + $(GN_LIBINTL) \ + $(GN_LIB_LDFLAGS) \ + -version-info 0:0:0 + +libgnunettransportapplication_la_SOURCES = \ + transport_api2_application.c +libgnunettransportapplication_la_LIBADD = \ + $(top_builddir)/src/lib/util/libgnunetutil.la \ + $(LTLIBINTL) +libgnunettransportapplication_la_LDFLAGS = \ + $(GN_LIB_LDFLAGS) \ + -version-info 0:0:0 + + +libgnunettransportcore_la_SOURCES = \ + transport_api2_core.c +libgnunettransportcore_la_LIBADD = \ + $(top_builddir)/src/lib/util/libgnunetutil.la \ + $(GN_LIBINTL) +libgnunettransportcore_la_LDFLAGS = \ + $(GN_LIB_LDFLAGS) \ + -version-info 0:0:0 + +libgnunettransportcommunicator_la_SOURCES = \ + transport_api2_communication.c +libgnunettransportcommunicator_la_LIBADD = \ + $(top_builddir)/src/lib/util/libgnunetutil.la \ + $(GN_LIBINTL) +libgnunettransportcommunicator_la_LDFLAGS = \ + $(GN_LIB_LDFLAGS) \ + -version-info 0:0:0 + + +libgnunettransportmonitor_la_SOURCES = \ + transport_api2_monitor.c +libgnunettransportmonitor_la_LIBADD = \ + $(top_builddir)/src/lib/util/libgnunetutil.la \ + $(GN_LIBINTL) +libgnunettransportmonitor_la_LDFLAGS = \ + $(GN_LIB_LDFLAGS) \ + -version-info 0:0:0 + + +libexec_PROGRAMS = \ + gnunet-service-transport \ + gnunet-communicator-unix \ + gnunet-communicator-udp \ + gnunet-communicator-tcp +if HAVE_EXPERIMENTAL +if HAVE_QUICHE +libexec_PROGRAMS += \ + gnunet-communicator-quic +endif +endif + + +#bin_PROGRAMS = \ +# gnunet-transport + +bin_SCRIPTS = \ + gnunet-transport-certificate-creation + +# See: https://www.gnu.org/software/automake/manual/html_node/Scripts.html#Scripts +do_subst = sed -e 's,[@]pkgdatadir[@],$(pkgdatadir),g' + + +gnunet-transport-certificate-creation: gnunet-transport-certificate-creation.in Makefile + $(AWK) -v bdir="$(bindir)" -v py="$(PYTHON)" -v awkay="$(AWK_BINARY)" -v pfx="$(prefix)" -v prl="$(PERL)" -v sysconfdirectory="$(sysconfdir)" -v pkgdatadirectory="$(pkgdatadir)" -f $(top_srcdir)/bin/dosubst.awk < $(srcdir)/gnunet-transport-certificate-creation.in > gnunet-transport-certificate-creation + @chmod +x gnunet-transport-certificate-creation + + + + +gnunet_communicator_unix_SOURCES = \ + gnunet-communicator-unix.c +gnunet_communicator_unix_LDADD = \ + libgnunettransportcommunicator.la \ + $(top_builddir)/src/service/statistics/libgnunetstatistics.la \ + $(top_builddir)/src/lib/util/libgnunetutil.la + +gnunet_communicator_tcp_SOURCES = \ + gnunet-communicator-tcp.c +gnunet_communicator_tcp_LDADD = \ + libgnunettransportcommunicator.la \ + $(top_builddir)/src/service/peerstore/libgnunetpeerstore.la \ + $(top_builddir)/src/service/nat/libgnunetnatnew.la \ + $(top_builddir)/src/service/statistics/libgnunetstatistics.la \ + $(top_builddir)/src/lib/util/libgnunetutil.la \ + $(LIBGCRYPT_LIBS) + +gnunet_communicator_udp_SOURCES = \ + gnunet-communicator-udp.c +gnunet_communicator_udp_LDADD = \ + libgnunettransportapplication.la \ + libgnunettransportcommunicator.la \ + $(top_builddir)/src/service/nat/libgnunetnatnew.la \ + $(top_builddir)/src/service/statistics/libgnunetstatistics.la \ + $(top_builddir)/src/lib/util/libgnunetutil.la \ + $(LIBGCRYPT_LIBS) + +if HAVE_EXPERIMENTAL +if HAVE_QUICHE +gnunet_communicator_quic_SOURCES = \ + gnunet-communicator-quic.c +gnunet_communicator_quic_LDADD = \ + libgnunettransportapplication.la \ + libgnunettransportcommunicator.la \ + $(top_builddir)/src/service/peerstore/libgnunetpeerstore.la \ + $(top_builddir)/src/service/nat/libgnunetnatnew.la \ + $(top_builddir)/src/service/statistics/libgnunetstatistics.la \ + $(top_builddir)/src/lib/util/libgnunetutil.la \ + -lquiche \ + $(LIBGCRYPT_LIBS) +endif +endif + +#gnunet_transport_SOURCES = \ +# gnunet-transport.c +#gnunet_transport_LDADD = \ +# libgnunettransport.la \ +# $(top_builddir)/src/lib/hello/libgnunethello.la \ +# $(top_builddir)/src/lib/util/libgnunetutil.la \ +# $(GN_LIBINTL) + +gnunet_service_transport_SOURCES = \ + gnunet-service-transport.c transport.h +gnunet_service_transport_LDADD = \ + $(top_builddir)/src/service/peerstore/libgnunetpeerstore.la \ + $(top_builddir)/src/lib/hello/libgnunethello.la \ + $(top_builddir)/src/service/statistics/libgnunetstatistics.la \ + $(top_builddir)/src/lib/util/libgnunetutil.la \ + $(LIBGCRYPT_LIBS) \ + $(GN_LIBINTL) + +plugin_LTLIBRARIES = \ + libgnunet_test_transport_plugin_cmd_simple_send_performance.la \ + libgnunet_test_transport_plugin_cmd_nat_upnp.la \ + libgnunet_test_transport_plugin_cmd_simple_send.la \ + libgnunet_test_transport_plugin_cmd_simple_send_broadcast.la \ + libgnunet_test_transport_plugin_cmd_simple_send_dv.la \ + libgnunet_test_transport_plugin_cmd_udp_backchannel.la + +libgnunet_test_transport_plugin_cmd_nat_upnp_la_SOURCES = \ + test_transport_plugin_cmd_nat_upnp.c +libgnunet_test_transport_plugin_cmd_nat_upnp_la_LIBADD = \ + libgnunettransporttesting2.la \ + libgnunettransportapplication.la \ + libgnunettransportcore.la \ + $(top_builddir)/src/service/testing/libgnunettesting.la \ + $(top_builddir)/src/service/peerstore/libgnunetpeerstore.la \ + $(top_builddir)/src/service/statistics/libgnunetstatistics.la \ + $(top_builddir)/src/lib/hello/libgnunethello.la \ + $(top_builddir)/src/service/arm/libgnunetarm.la \ + $(top_builddir)/src/lib/util/libgnunetutil.la \ + $(LTLIBINTL) +libgnunet_test_transport_plugin_cmd_nat_upnp_la_LDFLAGS = \ + $(GN_PLUGIN_LDFLAGS) + +libgnunet_test_transport_plugin_cmd_udp_backchannel_la_SOURCES = \ + test_transport_plugin_cmd_udp_backchannel.c +libgnunet_test_transport_plugin_cmd_udp_backchannel_la_LIBADD = \ + libgnunettransporttesting2.la \ + libgnunettransportapplication.la \ + libgnunettransportcore.la \ + $(top_builddir)/src/service/testing/libgnunettesting.la \ + $(top_builddir)/src/service/peerstore/libgnunetpeerstore.la \ + $(top_builddir)/src/service/statistics/libgnunetstatistics.la \ + $(top_builddir)/src/lib/hello/libgnunethello.la \ + $(top_builddir)/src/service/arm/libgnunetarm.la \ + $(top_builddir)/src/lib/util/libgnunetutil.la \ + $(LTLIBINTL) +libgnunet_test_transport_plugin_cmd_udp_backchannel_la_LDFLAGS = \ + $(GN_PLUGIN_LDFLAGS) + +libgnunet_test_transport_plugin_cmd_simple_send_la_SOURCES = \ + test_transport_plugin_cmd_simple_send.c +libgnunet_test_transport_plugin_cmd_simple_send_la_LIBADD = \ + libgnunettransporttesting2.la \ + libgnunettransportapplication.la \ + libgnunettransportcore.la \ + $(top_builddir)/src/service/testing/libgnunettesting.la \ + $(top_builddir)/src/service/peerstore/libgnunetpeerstore.la \ + $(top_builddir)/src/service/statistics/libgnunetstatistics.la \ + $(top_builddir)/src/lib/hello/libgnunethello.la \ + $(top_builddir)/src/service/arm/libgnunetarm.la \ + $(top_builddir)/src/lib/util/libgnunetutil.la \ + $(LTLIBINTL) +libgnunet_test_transport_plugin_cmd_simple_send_la_LDFLAGS = \ + $(GN_PLUGIN_LDFLAGS) + +libgnunet_test_transport_plugin_cmd_simple_send_performance_la_SOURCES = \ + test_transport_plugin_cmd_simple_send_performance.c +libgnunet_test_transport_plugin_cmd_simple_send_performance_la_LIBADD = \ + libgnunettransporttesting2.la \ + libgnunettransportapplication.la \ + libgnunettransportcore.la \ + $(top_builddir)/src/service/testing/libgnunettesting.la \ + $(top_builddir)/src/service/peerstore/libgnunetpeerstore.la \ + $(top_builddir)/src/service/statistics/libgnunetstatistics.la \ + $(top_builddir)/src/lib/hello/libgnunethello.la \ + $(top_builddir)/src/service/arm/libgnunetarm.la \ + $(top_builddir)/src/lib/util/libgnunetutil.la \ + $(LTLIBINTL) +libgnunet_test_transport_plugin_cmd_simple_send_performance_la_LDFLAGS = \ + $(GN_PLUGIN_LDFLAGS) + +libgnunet_test_transport_plugin_cmd_simple_send_broadcast_la_SOURCES = \ + test_transport_plugin_cmd_simple_send_broadcast.c +libgnunet_test_transport_plugin_cmd_simple_send_broadcast_la_LIBADD = \ + libgnunettransporttesting2.la \ + libgnunettransportapplication.la \ + libgnunettransportcore.la \ + $(top_builddir)/src/service/testing/libgnunettesting.la \ + $(top_builddir)/src/service/peerstore/libgnunetpeerstore.la \ + $(top_builddir)/src/service/statistics/libgnunetstatistics.la \ + $(top_builddir)/src/lib/hello/libgnunethello.la \ + $(top_builddir)/src/service/arm/libgnunetarm.la \ + $(top_builddir)/src/lib/util/libgnunetutil.la \ + $(LTLIBINTL) +libgnunet_test_transport_plugin_cmd_simple_send_broadcast_la_LDFLAGS = \ + $(GN_PLUGIN_LDFLAGS) + +libgnunet_test_transport_plugin_cmd_simple_send_dv_la_SOURCES = \ + test_transport_plugin_cmd_simple_send_dv.c +libgnunet_test_transport_plugin_cmd_simple_send_dv_la_LIBADD = \ + libgnunettransporttesting2.la \ + libgnunettransportapplication.la \ + libgnunettransportcore.la \ + $(top_builddir)/src/service/testing/libgnunettesting.la \ + $(top_builddir)/src/service/peerstore/libgnunetpeerstore.la \ + $(top_builddir)/src/service/statistics/libgnunetstatistics.la \ + $(top_builddir)/src/lib/hello/libgnunethello.la \ + $(top_builddir)/src/service/arm/libgnunetarm.la \ + $(top_builddir)/src/lib/util/libgnunetutil.la \ + $(LTLIBINTL) +libgnunet_test_transport_plugin_cmd_simple_send_dv_la_LDFLAGS = \ + $(GN_PLUGIN_LDFLAGS) + +check_PROGRAMS = \ + test_communicator_basic-unix \ + test_communicator_basic-tcp \ + test_communicator_basic-udp \ + test_communicator_basic-quic \ + test_communicator_rekey-tcp \ + test_communicator_rekey-udp \ + test_communicator_backchannel-udp \ + test_communicator_bidirect-tcp + +check_SCRIPTS= \ + test_transport_start_testcase.sh \ + test_transport_simple_send_performance.sh \ + test_transport_nat_icmp_tcp.sh \ + test_transport_nat_upnp.sh \ + test_transport_simple_send_string.sh \ + test_transport_simple_send.sh \ + test_transport_simple_send_broadcast.sh \ + test_transport_udp_backchannel.sh \ + test_transport_simple_send_dv_circle.sh \ + test_transport_simple_send_dv_inverse.sh + +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_SCRIPTS) \ + test_communicator_basic-unix \ + test_communicator_basic-tcp \ + test_communicator_basic-quic \ + test_communicator_basic-udp \ + test_communicator_rekey-tcp \ + test_communicator_rekey-udp \ + test_communicator_backchannel-udp \ + test_communicator_bidirect-tcp +endif + + +test_transport_start_with_config_SOURCES = \ + test_transport_start_with_config.c +test_transport_start_with_config_LDADD = \ + $(top_builddir)/src/service/testing/libgnunettesting.la \ + $(top_builddir)/src/lib/util/libgnunetutil.la \ + $(top_builddir)/src/lib/hello/libgnunethello.la \ + libgnunettransportcore.la \ + libgnunettransporttesting2.la + +test_communicator_basic_unix_SOURCES = \ + test_communicator_basic.c +test_communicator_basic_unix_LDADD = \ + libgnunettransporttesting2.la \ + $(top_builddir)/src/service/testing/libgnunettesting.la \ + $(top_builddir)/src/lib/util/libgnunetutil.la \ + $(top_builddir)/src/service/statistics/libgnunetstatistics.la + +test_communicator_basic_tcp_SOURCES = \ + test_communicator_basic.c +test_communicator_basic_tcp_LDADD = \ + libgnunettransporttesting2.la \ + $(top_builddir)/src/service/testing/libgnunettesting.la \ + $(top_builddir)/src/lib/util/libgnunetutil.la \ + $(top_builddir)/src/service/statistics/libgnunetstatistics.la + +test_communicator_basic_udp_SOURCES = \ + test_communicator_basic.c +test_communicator_basic_udp_LDADD = \ + libgnunettransporttesting2.la \ + $(top_builddir)/src/service/testing/libgnunettesting.la \ + $(top_builddir)/src/lib/util/libgnunetutil.la \ + $(top_builddir)/src/service/statistics/libgnunetstatistics.la + +test_communicator_basic_quic_SOURCES = \ + test_communicator_basic.c +test_communicator_basic_quic_LDADD = \ + libgnunettransporttesting2.la \ + $(top_builddir)/src/service/testing/libgnunettesting.la \ + $(top_builddir)/src/lib/util/libgnunetutil.la \ + $(top_builddir)/src/service/statistics/libgnunetstatistics.la + +test_communicator_rekey_tcp_SOURCES = \ + test_communicator_basic.c +test_communicator_rekey_tcp_LDADD = \ + libgnunettransporttesting2.la \ + $(top_builddir)/src/service/testing/libgnunettesting.la \ + $(top_builddir)/src/lib/util/libgnunetutil.la \ + $(top_builddir)/src/service/statistics/libgnunetstatistics.la + +test_communicator_rekey_udp_SOURCES = \ + test_communicator_basic.c +test_communicator_rekey_udp_LDADD = \ + libgnunettransporttesting2.la \ + $(top_builddir)/src/service/testing/libgnunettesting.la \ + $(top_builddir)/src/lib/util/libgnunetutil.la \ + $(top_builddir)/src/service/statistics/libgnunetstatistics.la + +test_communicator_backchannel_udp_SOURCES = \ + test_communicator_basic.c +test_communicator_backchannel_udp_LDADD = \ + libgnunettransporttesting2.la \ + $(top_builddir)/src/service/testing/libgnunettesting.la \ + $(top_builddir)/src/lib/util/libgnunetutil.la \ + $(top_builddir)/src/service/statistics/libgnunetstatistics.la + +test_communicator_bidirect_tcp_SOURCES = \ + test_communicator_basic.c +test_communicator_bidirect_tcp_LDADD = \ + libgnunettransporttesting2.la \ + $(top_builddir)/src/service/testing/libgnunettesting.la \ + $(top_builddir)/src/lib/util/libgnunetutil.la \ + $(top_builddir)/src/service/statistics/libgnunetstatistics.la + +test_transport_api2_tcp_SOURCES = \ + test_transport_api2.c +test_transport_api2_tcp_LDADD = \ + $(top_builddir)/src/lib/hello/libgnunethello.la \ + $(top_builddir)/src/lib/util/libgnunetutil.la \ + libgnunettransporttesting2.la + +EXTRA_DIST = \ +test_transport_start_testcase.sh \ +test_transport_simple_send_performance.sh \ +test_transport_nat_icmp_tcp.sh \ +test_transport_nat_upnp.sh \ +test_transport_simple_send_string.sh \ +test_transport_simple_send.sh \ +test_transport_simple_send_broadcast.sh \ +test_transport_udp_backchannel.sh \ +test_transport_simple_send_dv_circle.sh \ +test_transport_simple_send_dv_inverse.sh \ +gnunet-transport-certificate-creation.in \ +test_plugin_hostkey \ +test_plugin_hostkey.ecc \ +test_delay \ +template_cfg_peer1.conf\ +template_cfg_peer2.conf\ +test_transport_api_data.conf\ +test_transport_api_multi_peer1.conf\ +test_transport_api_multi_peer2.conf\ +test_transport_api_tcp_nat_peer1.conf\ +test_transport_api_tcp_nat_peer2.conf\ +test_transport_api_tcp_peer1.conf\ +test_transport_api_tcp_peer2.conf\ +test_transport_api2_tcp_peer1.conf\ +test_transport_api2_tcp_peer2.conf\ +test_transport_api_udp_nat_peer1.conf\ +test_transport_api_udp_nat_peer2.conf\ +test_transport_api_udp_peer1.conf\ +test_transport_api_udp_peer2.conf\ +test_transport_api_unix_peer1.conf\ +test_transport_api_unix_peer2.conf\ +test_transport_api_monitor_peers_peer1.conf\ +test_transport_api_monitor_peers_peer2.conf\ +test_transport_api_monitor_validation_peer1.conf\ +test_transport_api_monitor_validation_peer2.conf\ +test_transport_defaults.conf\ +test_communicator_unix_basic_peer1.conf \ +test_communicator_unix_basic_peer2.conf \ +test_communicator_tcp_basic_peer1.conf \ +test_communicator_tcp_basic_peer2.conf \ +test_communicator_udp_basic_peer1.conf \ +test_communicator_udp_basic_peer2.conf \ +test_communicator_tcp_rekey_peer1.conf \ +test_communicator_tcp_rekey_peer2.conf \ +test_communicator_udp_rekey_peer1.conf \ +test_communicator_udp_rekey_peer2.conf \ +test_communicator_udp_backchannel_peer1.conf \ +test_communicator_udp_backchannel_peer2.conf \ +test_communicator_tcp_bidirect_peer1.conf \ +test_communicator_tcp_bidirect_peer2.conf diff --git a/src/service/transport/NOTES b/src/service/transport/NOTES new file mode 100644 index 000000000..41404e1f9 --- /dev/null +++ b/src/service/transport/NOTES @@ -0,0 +1,46 @@ +KEY DESIGN CHOICES: + - who decides which connections to keep/create? + => higher level session/key management! + - who enforces things like F2F topology, etc? + => higher level session/key management! + - who tracks all known HELLOs & validates? + => We validate, PEERINFO tracks! + - who advertises our HELLO? + => us! (need background job; previously: advertising) + - who advertises other peers HELLOs? + => higher level (core?) + - who does bootstrapping? + => bootstrap service (external!) + - who enforces inbound bandwidth limits? + => transport-service and plugins! (previously: core); + either by limiting reads (TCP) or discarding packets + (transport-service) + - who enforces outbound bandwidth limits? + => transport_api! + - who decides outbound bandwidth limits? + => other peer, via core (need authenticated limits!) + - who decides inbound bandwidth limits? + => core / apps above core (need trust info) + - cost function for transports is latency estimate in ms + => plugin provides latency data, transport-service + selects plugin(s) for transmission + - who is responsible for fragmentation? + => plugins! (may use common shared library) + - should we require UDP to be reliable? + => NO. There are other places that may (rarely) + use messages that we can not fix + - how do we access the 50% of service that we need for TCP/UDP + from service.c without code replication or getting 50% + that we do not want (i.e. shutdown, pid-file-writing, etc.) + => use GNUNET_SERVICE_start/stop functions! + - At what level do we manage timeouts? + => At the plugin (TCP connections), + transport-service (neighbours) and + core (sessions) level! + => All plugins have to disconnect before service-level + disconnect occurs + => We can have a plugin-connection die, but the session + survives! + => We can have a session die (no further authenticated + communication) even if the plugin thinks it is still + up! diff --git a/src/service/transport/benchmark.sh b/src/service/transport/benchmark.sh new file mode 100755 index 000000000..a29e6ec2d --- /dev/null +++ b/src/service/transport/benchmark.sh @@ -0,0 +1,13 @@ +#!/bin/sh + +for i in $(seq 1 0) +do + echo RUN $i + ./test_transport_api_reliability_http +done + +for i in $(seq 1 100) +do + echo RUN $i + ./test_transport_api_reliability_https +done diff --git a/src/service/transport/communicator.h b/src/service/transport/communicator.h new file mode 100644 index 000000000..5ef43597d --- /dev/null +++ b/src/service/transport/communicator.h @@ -0,0 +1,138 @@ +/* + This file is part of GNUnet. + Copyright (C) 2009-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 . + + SPDX-License-Identifier: AGPL3.0-or-later + */ + +/** + * @file transport/communicator.h + * @brief common internal definitions for communicator services + * @author Christian Grothoff + */ +#ifndef COMMUNICATOR_H +#define COMMUNICAOTR_H + +#include "gnunet_util_lib.h" +#include "gnunet_protocols.h" + +GNUNET_NETWORK_STRUCT_BEGIN + +/** + * Message used to tell a communicator about a successful + * key exchange. + * + * Note that this style of KX acknowledgement typically only applies + * for communicators where the underlying network protocol is + * unidirectional and/or lacks cryptography. Furthermore, this is + * just the recommended "generic" style, communicators are always free + * to implement original designs that better fit their requirements. + */ +struct GNUNET_TRANSPORT_CommunicatorGenericKXConfirmation +{ + /** + * Type is #GNUNET_MESSAGE_TYPE_TRANSPORT_COMMUNICATOR_KX_CONFIRMATION + */ + struct GNUNET_MessageHeader header; + + /** + * Timestamp from the original sender which identifies the original KX. + */ + struct GNUNET_TIME_AbsoluteNBO monotonic_time; + + /** + * How long does the receiver of the KX believe that the address + * on which the KX was received will continue to be valid. + */ + struct GNUNET_TIME_RelativeNBO validity; + + /** + * Hash of the shared secret. Specific hash function may depend on + * the communicator's protocol details. + */ + struct GNUNET_HashCode token; +}; + + +/** + * Message used to tell a communicator about the receiver's + * flow control limits and to acknowledge receipt of certain + * messages. + * + * Note that a sender MAY choose to violate the flow-control + * limits provided in this message by a receiver, which may + * result in messages being lost (after all, transport is an + * unreliable channel). So if the sender violates these + * constraints, it should expect that the receive will simply + * discard the (partially) received "old" messages. + * + * This way, if a sender or receiver crashes, there is no protocol + * violation. + * + * Note that this style of flow control typically only applies + * for communicators where the underlying network protocol does + * not already implement flow control. Furthermore, this is + * just the recommended "generic" style, communicators are always + * free to implement original designs that better fit their + * requirements. + */ +struct GNUNET_TRANSPORT_CommunicatorGenericFCLimits +{ + /** + * Type is #GNUNET_MESSAGE_TYPE_TRANSPORT_COMMUNICATOR_FC_LIMITS + */ + struct GNUNET_MessageHeader header; + + /** + * Maximum number of messages beyond the acknowledged message + * number that can still be transmitted concurrently without + * further acknowledgements. + */ + uint32_t msg_window_size; + + /** + * Up to which message number were all messages received. + */ + uint64_t msg_cummulative_ack; + + /** + * Maximum number of payload bytes beyond the acknowledged + * number of bytes can still be transmitted without further + * acknowledgements. + */ + uint64_t bytes_window_size; + + /** + * Cumulative acknowledgement for number of bytes received. + */ + uint64_t bytes_cummulative_ack; + + /** + * Followed by a variable-size bitfield for messages received + * beyond @e msg_cummulative_ack. Index at offset 0 must thus + * be zero, otherwise @e msg_cummulative_ack should be + * increased. Note that this field can be overall of 0 bytes. + * The variable-size bitfield must be a multiple of 64 bits + * long. + */ + /* uint64_t msg_selective_ack_field[]; */ +}; + + +GNUNET_NETWORK_STRUCT_END + +/* end of communicator.h */ +#endif diff --git a/src/service/transport/gnunet-communicator-quic.c b/src/service/transport/gnunet-communicator-quic.c new file mode 100644 index 000000000..0a7e511eb --- /dev/null +++ b/src/service/transport/gnunet-communicator-quic.c @@ -0,0 +1,1795 @@ +/* + This file is part of GNUnet + Copyright (C) 2010-2014, 2018, 2019 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 . + + SPDX-License-Identifier: AGPL3.0-or-later + */ + +/** + * @file transport/gnunet-communicator-quic.c + * @brief Transport plugin using QUIC. + * @author Marshall Stone + * @author Martin Schanzenbach + * + * TODO: + * - Automatically generate self-signed x509 certificates and load from config + * - Figure out MTU and how we have to handle fragmentation in Quiche. + * - Mandate timeouts + * - Setup stats handler properly + * - Doxygen documentation of methods + * - Refactor code shared with UDP and TCP communicator + * - Performance testing + * - Check for memory leaks with coverity/valgrind + */ +#include "gnunet_common.h" +#include "gnunet_util_lib.h" +#include "gnunet_core_service.h" +#include "quiche.h" +#include "platform.h" +#include "gnunet_protocols.h" +#include "gnunet_signatures.h" +#include "gnunet_constants.h" +#include "gnunet_statistics_service.h" +#include "gnunet_transport_application_service.h" +#include "gnunet_transport_communication_service.h" +#include "gnunet_nat_service.h" +#include "stdint.h" +#include "inttypes.h" + +#define COMMUNICATOR_CONFIG_SECTION "communicator-quic" +#define COMMUNICATOR_ADDRESS_PREFIX "quic" +#define MAX_DATAGRAM_SIZE 1350 + + +/* FIXME: Review all static lengths/contents below. Maybe this can be done smarter */ +/* Currently equivalent to QUICHE_MAX_CONN_ID_LEN */ +#define LOCAL_CONN_ID_LEN 20 +#define MAX_TOKEN_LEN \ + sizeof("quiche") - 1 \ + + sizeof(struct sockaddr_storage) \ + + QUICHE_MAX_CONN_ID_LEN +#define CID_LEN sizeof(uint8_t) * QUICHE_MAX_CONN_ID_LEN +#define TOKEN_LEN sizeof (uint8_t) * MAX_TOKEN_LEN + + +/* FIXME: Why 4? + Generic, bidirectional, client-initiated quic stream id */ +#define STREAMID_BI 4 + +/** + * How long do we believe our addresses to remain up (before + * the other peer should revalidate). + */ +#define ADDRESS_VALIDITY_PERIOD GNUNET_TIME_UNIT_HOURS + +/** + * Map of DCID (uint8_t) -> quic_conn for quickly retrieving connections to other peers. + */ +struct GNUNET_CONTAINER_MultiHashMap *conn_map; + +/** + * Map of sockaddr -> struct PeerAddress + */ +struct GNUNET_CONTAINER_MultiHashMap *addr_map; + +/** + * Handle to the config + */ +static const struct GNUNET_CONFIGURATION_Handle *cfg; + +/** + * FIXME undocumented + */ +static struct GNUNET_TIME_Relative rekey_interval; + +/** + * FIXME undocumented + */ +static struct GNUNET_NETWORK_Handle *udp_sock; + +/** + * FIXME undocumented + */ +static struct GNUNET_SCHEDULER_Task *read_task; + +/** + * FIXME undocumented + */ +static struct GNUNET_TRANSPORT_CommunicatorHandle *ch; + +/** + * FIXME undocumented + */ +static struct GNUNET_TRANSPORT_ApplicationHandle *ah; + +/** + * FIXME undocumented + */ +static int have_v6_socket; + +/** + * FIXME undocumented + */ +static uint16_t my_port; + +/** + * FIXME undocumented + */ +static unsigned long long rekey_max_bytes; + +/** + * FIXME undocumented + */ +static quiche_config *config = NULL; + +/** + * Our peer identity +*/ +struct GNUNET_PeerIdentity my_identity; + +/** + * Our private key. + */ +static struct GNUNET_CRYPTO_EddsaPrivateKey *my_private_key; + +/** + * Connection to NAT service. + */ +static struct GNUNET_NAT_Handle *nat; + +/** + * Information we track per peer we have recently been in contact with. + * + * (Since quiche handles crypto, handshakes, etc. we don't differentiate + * between SenderAddress and ReceiverAddress) + * FIXME: But we do a handshake as well. The flag in this struct seems to + * indicate this. Update comment! + */ +struct PeerAddress +{ + /** + * To whom are we talking to. + */ + struct GNUNET_PeerIdentity target; + + /** + * Flag to indicate whether we know the PeerIdentity (target) yet + */ + int id_rcvd; + + /** + * Flag to indicate whether we have sent OUR PeerIdentity to this peer + */ + int id_sent; + + /** + * Flag to indicate if we are the initiator of the connection + */ + int is_receiver; + + /** + * Address of the receiver in the human-readable format + * with the #COMMUNICATOR_ADDRESS_PREFIX. + */ + char *foreign_addr; + + /** + * Address of the other peer. + */ + struct sockaddr *address; + + /** + * Length of the address. + */ + socklen_t address_len; + + /** + * The QUIC connection associated with this peer + */ + struct quic_conn *conn; + + /** + * Default message queue we are providing for the #ch. + */ + struct GNUNET_MQ_Handle *d_mq; + + /** + * handle for default queue with the #ch. + */ + struct GNUNET_TRANSPORT_QueueHandle *d_qh; + + /** + * Timeout for this peer address. + */ + struct GNUNET_TIME_Absolute timeout; + + /** + * MTU we allowed transport for this peer's default queue. + * FIXME: MTU from quiche + */ + size_t d_mtu; + + /** + * Which network type does this queue use? + */ + enum GNUNET_NetworkType nt; + + /** + * receiver_destroy already called on receiver. + */ + int peer_destroy_called; + + /** + * FIXME implementation missing + * Entry in sender expiration heap. + */ + // struct GNUNET_CONTAINER_HeapNode *hn; +}; + +// /** +// * FIXME: Implementation missing +// * Expiration heap for peers (contains `struct PeerAddress`) +// */ +// static struct GNUNET_CONTAINER_Heap *peers_heap; + +/** + * ID of timeout task + */ +static struct GNUNET_SCHEDULER_Task *timeout_task; + +/** + * Network scanner to determine network types. + */ +static struct GNUNET_NT_InterfaceScanner *is; + +/** + * For logging statistics. + */ +static struct GNUNET_STATISTICS_Handle *stats; + +/** + * QUIC connection object. A connection has a unique SCID/DCID pair. Here we store our SCID + * (incoming packet DCID field == outgoing packet SCID field) for a given connection. This + * is hashed for each unique quic_conn. +*/ +struct quic_conn +{ + uint8_t cid[LOCAL_CONN_ID_LEN]; + + quiche_conn *conn; +}; + +/** + * QUIC_header is used to store information received from an incoming QUIC packet +*/ +struct QUIC_header +{ + uint8_t type; + uint32_t version; + + uint8_t scid[QUICHE_MAX_CONN_ID_LEN]; + size_t scid_len; + + uint8_t dcid[QUICHE_MAX_CONN_ID_LEN]; + size_t dcid_len; + + uint8_t odcid[QUICHE_MAX_CONN_ID_LEN]; + size_t odcid_len; + + uint8_t token[MAX_TOKEN_LEN]; + size_t token_len; +}; + + +/** + * Given a PeerAddress, receive data from streams after doing connection logic. + * ASSUMES: connection is established to peer +*/ +static void +recv_from_streams (struct PeerAddress *peer) +{ + char stream_buf[UINT16_MAX]; + size_t buf_size = UINT16_MAX; + char *buf_ptr = stream_buf; + struct GNUNET_MessageHeader *hdr; + + uint64_t s = 0; + quiche_stream_iter *readable; + bool fin; + ssize_t recv_len; + + readable = quiche_conn_readable (peer->conn->conn); + while (quiche_stream_iter_next (readable, &s)) + { + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "stream %" PRIu64 " is readable\n", + s); + fin = false; + recv_len = quiche_conn_stream_recv (peer->conn->conn, s, + (uint8_t *) stream_buf, buf_size, + &fin); + if (recv_len < 0) + { + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "error while receiving data from stream %" PRIu64 "\n", s); + break; + } + /** + * FIXME: Do not use implicit booleans. Use GNUNET_YES, GNUNET_NO, GNUNET_SYSERR + * and check for that. + * + * Initial packet should contain peerid if they are the initiator + */ + if (! peer->is_receiver && GNUNET_NO == peer->id_rcvd) + { + if (recv_len < sizeof(struct GNUNET_PeerIdentity)) + { + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "message recv len of %zd less than length of peer identity\n", + recv_len); + return; + } + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "received peer identity\n"); + struct GNUNET_PeerIdentity *pid = (struct + GNUNET_PeerIdentity *) stream_buf; + peer->target = *pid; + peer->id_rcvd = GNUNET_YES; + buf_ptr += sizeof(struct GNUNET_PeerIdentity); + recv_len -= sizeof(struct GNUNET_PeerIdentity); + } + /** + * Parse messages to pass to communicator + */ + while (recv_len >= sizeof(struct GNUNET_MessageHeader)) + { + hdr = (struct GNUNET_MessageHeader *) buf_ptr; + if (ntohs (hdr->size) > recv_len) + { + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "message size stated (%d) is greater than length of rcvd data (%zd)!\n", + ntohs (hdr->size), recv_len); + return; + } + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "passing %zd bytes to core\n", + recv_len); + GNUNET_TRANSPORT_communicator_receive (ch, &peer->target, hdr, + ADDRESS_VALIDITY_PERIOD, NULL, + NULL); + recv_len -= ntohs (hdr->size); + buf_ptr += ntohs (hdr->size); + } + /** + * Check for leftover bytes + */ + if (0 != recv_len) + { + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "message recv len of %zd less than length of message header\n", + recv_len); + } + /** + * FIXME: comment useless + * fin + */ + if (fin) + { + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "fin received, closing connection\n"); + if (0 > quiche_conn_close (peer->conn->conn, true, 0, NULL, 0)) + { + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "quiche failed to close connection to peer\n"); + } + } + } + quiche_stream_iter_free (readable); +} + + +/** + * FIXME: review token generation, assure tokens are generated properly. doxygen + */ +static void +mint_token (const uint8_t *dcid, size_t dcid_len, + struct sockaddr_storage *addr, socklen_t addr_len, + uint8_t *token, size_t *token_len) +{ + GNUNET_memcpy (token, "quiche", sizeof("quiche") - 1); + GNUNET_memcpy (token + sizeof("quiche") - 1, addr, addr_len); + GNUNET_memcpy (token + sizeof("quiche") - 1 + addr_len, dcid, dcid_len); + + *token_len = sizeof("quiche") - 1 + addr_len + dcid_len; +} + + +static enum GNUNET_GenericReturnValue +validate_token (const uint8_t *token, size_t token_len, + struct sockaddr_storage *addr, socklen_t addr_len, + uint8_t *odcid, size_t *odcid_len) +{ + if ((token_len < sizeof("quiche") - 1) || + memcmp (token, "quiche", sizeof("quiche") - 1)) + { + return GNUNET_NO; + } + + token += sizeof("quiche") - 1; + token_len -= sizeof("quiche") - 1; + + if ((token_len < addr_len) || memcmp (token, addr, addr_len)) + { + return GNUNET_NO; + } + + token += addr_len; + token_len -= addr_len; + + if (*odcid_len < token_len) + { + return GNUNET_NO; + } + + memcpy (odcid, token, token_len); + *odcid_len = token_len; + + return GNUNET_OK; +} + + +static struct quic_conn* +create_conn (uint8_t *scid, size_t scid_len, + uint8_t *odcid, size_t odcid_len, + struct sockaddr *local_addr, + socklen_t local_addr_len, + struct sockaddr_storage *peer_addr, + socklen_t peer_addr_len) +{ + struct quic_conn *conn; + quiche_conn *q_conn; + conn = GNUNET_new (struct quic_conn); + if (scid_len != LOCAL_CONN_ID_LEN) + { + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "error while creating connection, scid length too short: %zu\n", + scid_len); + return NULL; + } + + GNUNET_memcpy (conn->cid, scid, LOCAL_CONN_ID_LEN); + q_conn = quiche_accept (conn->cid, LOCAL_CONN_ID_LEN, + odcid, odcid_len, + local_addr, + local_addr_len, + (struct sockaddr *) peer_addr, + peer_addr_len, + config); + if (NULL == q_conn) + { + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "quiche failed to create connection after call to quiche_accept\n"); + return NULL; + } + conn->conn = q_conn; + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "new quic connection created\n"); + return conn; +} + + +static void +flush_egress (struct quic_conn *conn) +{ + static uint8_t out[MAX_DATAGRAM_SIZE]; + quiche_send_info send_info; + + ssize_t written; + ssize_t sent; + + while (1) + { + written = quiche_conn_send (conn->conn, out, sizeof(out), &send_info); + if (QUICHE_ERR_DONE == written) + { + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "done writing quic packets\n"); + break; + } + if (0 > written) + { + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "quiche failed to create packet. quiche error: %zd\n", + written); + return; + } + sent = GNUNET_NETWORK_socket_sendto (udp_sock, out, written, + (struct sockaddr *) &send_info.to, + send_info.to_len); + if (sent != written) + { + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "quiche failed to send data to peer\n"); + return; + } + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "sent %zd bytes\n", sent); + } +} + + +/** + * Increment receiver timeout due to activity. + * + * @param receiver address for which the timeout should be rescheduled + */ +static void +reschedule_peer_timeout (struct PeerAddress *peer) +{ + peer->timeout = + GNUNET_TIME_relative_to_absolute (GNUNET_CONSTANTS_IDLE_CONNECTION_TIMEOUT); + // GNUNET_CONTAINER_heap_update_cost (peer->hn, + // peer->timeout.abs_value_us); +} + + +/** + * Destroys a receiving state due to timeout or shutdown. + * + * @param receiver entity to close down + */ +static void +peer_destroy (struct PeerAddress *peer) +{ + struct GNUNET_HashCode addr_key; + + peer->peer_destroy_called = GNUNET_YES; + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Disconnecting peer for peer `%s'\n", + GNUNET_i2s (&peer->target)); + if (NULL != peer->d_qh) + { + GNUNET_TRANSPORT_communicator_mq_del (peer->d_qh); + peer->d_qh = NULL; + } + // GNUNET_assert (peer == GNUNET_CONTAINER_heap_remove_node (peer->hn)); + /** + * Remove peer from hashmap + */ + GNUNET_CRYPTO_hash (peer->address, peer->address_len, &addr_key); + if (GNUNET_NO == GNUNET_CONTAINER_multihashmap_remove (addr_map, &addr_key, + peer)) + { + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "tried to remove non-existent peer from addr map\n"); + return; + } + GNUNET_STATISTICS_set (stats, + "# peers active", + GNUNET_CONTAINER_multihashmap_size (addr_map), + GNUNET_NO); + quiche_conn_free (peer->conn->conn); + GNUNET_free (peer->address); + GNUNET_free (peer->foreign_addr); + GNUNET_free (peer->conn); + GNUNET_free (peer); +} + + +/** + * Iterator over all peers to clean up. + * + * @param cls NULL + * @param key peer->address + * @param value the peer to destroy + * @return #GNUNET_OK to continue to iterate + */ +static int +get_peer_delete_it (void *cls, + const struct GNUNET_HashCode *key, + void *value) +{ + struct PeerAddress *peer = value; + (void) cls; + (void) key; + peer_destroy (peer); + return GNUNET_OK; +} + + +/** + * Signature of functions implementing the sending functionality of a + * message queue. + * + * @param mq the message queue + * @param msg the message to send + * @param impl_state our `struct PeerAddress` + */ +static void +mq_send_d (struct GNUNET_MQ_Handle *mq, + const struct GNUNET_MessageHeader *msg, + void *impl_state) +{ + struct PeerAddress *peer = impl_state; + uint16_t msize = ntohs (msg->size); + ssize_t send_len; + + if (NULL == peer->conn->conn) + { + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "peer never established quic connection\n"); + return; + } + + GNUNET_assert (mq == peer->d_mq); + if (msize > peer->d_mtu) + { + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "msize: %u, mtu: %lu\n", + msize, + peer->d_mtu); + GNUNET_break (0); + if (GNUNET_YES != peer->peer_destroy_called) + { + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "peer destroy called, destroying peer\n"); + peer_destroy (peer); + } + return; + } + reschedule_peer_timeout (peer); + + send_len = quiche_conn_stream_send (peer->conn->conn, 4, (uint8_t *) msg, + msize, false); + if (send_len != msize) + { + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "tried to send message and quiche returned %zd", send_len); + return; + } + flush_egress (peer->conn); + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "sent a message of %zd bytes\n", send_len); + GNUNET_MQ_impl_send_continue (mq); +} + + +/** + * Signature of functions implementing the destruction of a message + * queue. Implementations must not free @a mq, but should take care + * of @a impl_state. + * + * @param mq the message queue to destroy + * @param impl_state our `struct PeerAddress` + */ +static void +mq_destroy_d (struct GNUNET_MQ_Handle *mq, void *impl_state) +{ + struct PeerAddress *peer = impl_state; + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Default MQ destroyed\n"); + if (mq == peer->d_mq) + { + peer->d_mq = NULL; + if (GNUNET_YES != peer->peer_destroy_called) + peer_destroy (peer); + } +} + + +/** + * Implementation function that cancels the currently sent message. + * + * @param mq message queue + * @param impl_state our `struct PeerAddress` + */ +static void +mq_cancel (struct GNUNET_MQ_Handle *mq, void *impl_state) +{ + /* Cancellation is impossible with QUIC; bail */ + GNUNET_assert (0); +} + + +/** + * Generic error handler, called with the appropriate + * error code and the same closure specified at the creation of + * the message queue. + * Not every message queue implementation supports an error handler. + * + * @param cls our `struct ReceiverAddress` + * @param error error code + */ +static void +mq_error (void *cls, enum GNUNET_MQ_Error error) +{ + struct PeerAddress *peer = cls; + + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "MQ error in queue to %s: %d\n", + GNUNET_i2s (&peer->target), + (int) error); + peer_destroy (peer); +} + + +/** + * Convert UDP bind specification to a `struct sockaddr *` + * + * @param bindto bind specification to convert + * @param[out] sock_len set to the length of the address + * @return converted bindto specification + */ +static struct sockaddr * +udp_address_to_sockaddr (const char *bindto, socklen_t *sock_len) +{ + struct sockaddr *in; + unsigned int port; + char dummy[2]; + char *colon; + char *cp; + + if (1 == sscanf (bindto, "%u%1s", &port, dummy)) + { + /* interpreting value as just a PORT number */ + if (port > UINT16_MAX) + { + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "BINDTO specification `%s' invalid: value too large for port\n", + bindto); + return NULL; + } + if ((GNUNET_NO == GNUNET_NETWORK_test_pf (PF_INET6)) || + (GNUNET_YES == + GNUNET_CONFIGURATION_get_value_yesno (cfg, + COMMUNICATOR_CONFIG_SECTION, + "DISABLE_V6"))) + { + struct sockaddr_in *i4; + + i4 = GNUNET_malloc (sizeof(struct sockaddr_in)); + i4->sin_family = AF_INET; + i4->sin_port = htons ((uint16_t) port); + *sock_len = sizeof(struct sockaddr_in); + in = (struct sockaddr *) i4; + } + else + { + struct sockaddr_in6 *i6; + + i6 = GNUNET_malloc (sizeof(struct sockaddr_in6)); + i6->sin6_family = AF_INET6; + i6->sin6_port = htons ((uint16_t) port); + *sock_len = sizeof(struct sockaddr_in6); + in = (struct sockaddr *) i6; + } + return in; + } + cp = GNUNET_strdup (bindto); + colon = strrchr (cp, ':'); + if (NULL != colon) + { + /* interpret value after colon as port */ + *colon = '\0'; + colon++; + if (1 == sscanf (colon, "%u%1s", &port, dummy)) + { + /* interpreting value as just a PORT number */ + if (port > UINT16_MAX) + { + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "BINDTO specification `%s' invalid: value too large for port\n", + bindto); + GNUNET_free (cp); + return NULL; + } + } + else + { + GNUNET_log ( + GNUNET_ERROR_TYPE_ERROR, + "BINDTO specification `%s' invalid: last ':' not followed by number\n", + bindto); + GNUNET_free (cp); + return NULL; + } + } + else + { + /* interpret missing port as 0, aka pick any free one */ + port = 0; + } + { + /* try IPv4 */ + struct sockaddr_in v4; + + memset (&v4, 0, sizeof(v4)); + if (1 == inet_pton (AF_INET, cp, &v4.sin_addr)) + { + v4.sin_family = AF_INET; + v4.sin_port = htons ((uint16_t) port); +#if HAVE_SOCKADDR_IN_SIN_LEN + v4.sin_len = sizeof(struct sockaddr_in); +#endif + in = GNUNET_memdup (&v4, sizeof(struct sockaddr_in)); + *sock_len = sizeof(struct sockaddr_in); + GNUNET_free (cp); + return in; + } + } + { + /* try IPv6 */ + struct sockaddr_in6 v6; + const char *start; + + memset (&v6, 0, sizeof(v6)); + start = cp; + if (('[' == *cp) && (']' == cp[strlen (cp) - 1])) + { + start++; /* skip over '[' */ + cp[strlen (cp) - 1] = '\0'; /* eat ']' */ + } + if (1 == inet_pton (AF_INET6, start, &v6.sin6_addr)) + { + v6.sin6_family = AF_INET6; + v6.sin6_port = htons ((uint16_t) port); +#if HAVE_SOCKADDR_IN_SIN_LEN + v6.sin6_len = sizeof(sizeof(struct sockaddr_in6)); +#endif + in = GNUNET_memdup (&v6, sizeof(v6)); + *sock_len = sizeof(v6); + GNUNET_free (cp); + return in; + } + } + /* #5528 FIXME (feature!): maybe also try getnameinfo()? */ + GNUNET_free (cp); + return NULL; +} + + +/** + * Setup the MQ for the @a peer. If a queue exists, + * the existing one is destroyed. Then the MTU is + * recalculated and a fresh queue is initialized. + * + * @param peer peer to setup MQ for + */ +static void +setup_peer_mq (struct PeerAddress *peer) +{ + size_t base_mtu; + + switch (peer->address->sa_family) + { + case AF_INET: + base_mtu = 1480 /* Ethernet MTU, 1500 - Ethernet header - VLAN tag */ + - sizeof(struct GNUNET_TUN_IPv4Header) /* 20 */ + - sizeof(struct GNUNET_TUN_UdpHeader) /* 8 */; + break; + + case AF_INET6: + base_mtu = 1280 /* Minimum MTU required by IPv6 */ + - sizeof(struct GNUNET_TUN_IPv6Header) /* 40 */ + - sizeof(struct GNUNET_TUN_UdpHeader) /* 8 */; + break; + + default: + GNUNET_assert (0); + break; + } + /* MTU == base_mtu */ + peer->d_mtu = base_mtu; + + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Setting up MQs and QHs\n"); + /* => Effective MTU for CORE will range from 1080 (IPv6 + KX) to + 1404 (IPv4 + Box) bytes, depending on circumstances... */ + + if (NULL == peer->d_mq) + peer->d_mq = GNUNET_MQ_queue_for_callbacks (&mq_send_d, + &mq_destroy_d, + &mq_cancel, + peer, + NULL, + &mq_error, + peer); + peer->d_qh = + GNUNET_TRANSPORT_communicator_mq_add (ch, + &peer->target, + peer->foreign_addr, + 1000, + GNUNET_TRANSPORT_QUEUE_LENGTH_UNLIMITED, + 0, /* Priority */ + peer->nt, + GNUNET_TRANSPORT_CS_OUTBOUND, + peer->d_mq); +} + + +/** + * Taken from: UDP communicator + * Converts @a address to the address string format used by this + * communicator in HELLOs. + * + * @param address the address to convert, must be AF_INET or AF_INET6. + * @param address_len number of bytes in @a address + * @return string representation of @a address + */ +static char * +sockaddr_to_udpaddr_string (const struct sockaddr *address, + socklen_t address_len) +{ + char *ret; + + switch (address->sa_family) + { + case AF_INET: + GNUNET_asprintf (&ret, + "%s-%s", + COMMUNICATOR_ADDRESS_PREFIX, + GNUNET_a2s (address, address_len)); + break; + + case AF_INET6: + GNUNET_asprintf (&ret, + "%s-%s", + COMMUNICATOR_ADDRESS_PREFIX, + GNUNET_a2s (address, address_len)); + break; + + default: + GNUNET_assert (0); + } + return ret; +} + + +/** + * Function called when the transport service has received a + * backchannel message for this communicator (!) via a different return + * path. Should be an acknowledgement. + * + * @param cls closure, NULL + * @param sender which peer sent the notification + * @param msg payload + */ +static void +notify_cb (void *cls, + const struct GNUNET_PeerIdentity *sender, + const struct GNUNET_MessageHeader *msg) +{ + // const struct UDPAck *ack; + + // (void) cls; + // GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + // "Storing UDPAck received from backchannel from %s\n", + // GNUNET_i2s_full (sender)); + // if ((ntohs (msg->type) != GNUNET_MESSAGE_TYPE_COMMUNICATOR_UDP_ACK) || + // (ntohs (msg->size) != sizeof(struct UDPAck))) + // { + // GNUNET_break_op (0); + // return; + // } + // ack = (const struct UDPAck *) msg; + // GNUNET_CONTAINER_multipeermap_get_multiple (receivers, + // sender, + // &handle_ack, + // (void *) ack); +} + + +/** + * Task run to check #receiver_heap and #sender_heap for timeouts. + * + * @param cls unused, NULL + */ +static void +check_timeouts (void *cls) +{ + // struct GNUNET_TIME_Relative st; + // struct GNUNET_TIME_Relative rt; + // struct GNUNET_TIME_Relative delay; + // struct ReceiverAddress *receiver; + // struct SenderAddress *sender; + + // (void) cls; + // timeout_task = NULL; + // rt = GNUNET_TIME_UNIT_FOREVER_REL; + // while (NULL != (receiver = GNUNET_CONTAINER_heap_peek (receivers_heap))) + // { + // /* if (GNUNET_YES != receiver->receiver_destroy_called) */ + // /* { */ + // rt = GNUNET_TIME_absolute_get_remaining (receiver->timeout); + // if (0 != rt.rel_value_us) + // break; + // GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + // "Receiver timed out\n"); + // receiver_destroy (receiver); + // // } + // } + // st = GNUNET_TIME_UNIT_FOREVER_REL; + // while (NULL != (sender = GNUNET_CONTAINER_heap_peek (senders_heap))) + // { + // if (GNUNET_YES != sender->sender_destroy_called) + // { + // st = GNUNET_TIME_absolute_get_remaining (sender->timeout); + // if (0 != st.rel_value_us) + // break; + // sender_destroy (sender); + // } + // } + // delay = GNUNET_TIME_relative_min (rt, st); + // if (delay.rel_value_us < GNUNET_TIME_UNIT_FOREVER_REL.rel_value_us) + // timeout_task = GNUNET_SCHEDULER_add_delayed (delay, &check_timeouts, NULL); +} + + +/** + * Function called by the transport service to initialize a + * message queue given address information about another peer. + * If and when the communication channel is established, the + * communicator must call #GNUNET_TRANSPORT_communicator_mq_add() + * to notify the service that the channel is now up. It is + * the responsibility of the communicator to manage sane + * retries and timeouts for any @a peer/@a address combination + * provided by the transport service. Timeouts and retries + * do not need to be signalled to the transport service. + * + * @param cls closure + * @param peer identity of the other peer + * @param address where to send the message, human-readable + * communicator-specific format, 0-terminated, UTF-8 + * @return #GNUNET_OK on success, #GNUNET_SYSERR if the provided address is + * invalid + */ +static int +mq_init (void *cls, const struct GNUNET_PeerIdentity *peer_id, const + char *address) +{ + struct PeerAddress *peer; + const char *path; + struct sockaddr *in; + socklen_t in_len; + struct GNUNET_HashCode addr_key; + uint8_t scid[LOCAL_CONN_ID_LEN]; + + struct quic_conn *q_conn; + char *bindto; + socklen_t local_in_len; + struct sockaddr *local_addr; + + if (GNUNET_OK != + GNUNET_CONFIGURATION_get_value_string (cfg, + COMMUNICATOR_CONFIG_SECTION, + "BINDTO", + &bindto)) + { + GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR, + COMMUNICATOR_CONFIG_SECTION, + "BINDTO"); + return GNUNET_SYSERR; + } + local_addr = udp_address_to_sockaddr (bindto, &local_in_len); + + if (0 != strncmp (address, + COMMUNICATOR_ADDRESS_PREFIX "-", + strlen (COMMUNICATOR_ADDRESS_PREFIX "-"))) + { + GNUNET_break_op (0); + return GNUNET_SYSERR; + } + path = &address[strlen (COMMUNICATOR_ADDRESS_PREFIX "-")]; + in = udp_address_to_sockaddr (path, &in_len); + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "mq_init in_len length before: %d\n", + in_len); + /** + * If we already have a queue with this peer, ignore + */ + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "address string in mq_init: %s\n", + address); + GNUNET_CRYPTO_hash (address, strlen (address), &addr_key); + peer = GNUNET_CONTAINER_multihashmap_get (addr_map, &addr_key); + if (NULL != peer) + { + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "ignoring transport service mq request, we already have an mq with this peer (address)\n"); + return GNUNET_SYSERR; + } + peer = GNUNET_new (struct PeerAddress); + peer->address = in; + peer->address_len = in_len; + peer->target = *peer_id; + peer->id_rcvd = GNUNET_YES; + peer->is_receiver = GNUNET_YES; + peer->nt = GNUNET_NT_scanner_get_type (is, in, in_len); + peer->timeout = + GNUNET_TIME_relative_to_absolute (GNUNET_CONSTANTS_IDLE_CONNECTION_TIMEOUT); + GNUNET_STATISTICS_set (stats, + "# peers active", + GNUNET_CONTAINER_multihashmap_size (addr_map), + GNUNET_NO); + peer->foreign_addr = + sockaddr_to_udpaddr_string (peer->address, peer->address_len); + /** + * Insert peer into hashmap + */ + GNUNET_CONTAINER_multihashmap_put (addr_map, &addr_key, + peer, + GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_ONLY); + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "mq_init added new peer to the addr map\n"); + /** + * Before setting up peer mq, initiate a quic connection to the target (perform handshake w/ quiche) + */ + GNUNET_CRYPTO_random_block (GNUNET_CRYPTO_QUALITY_STRONG, scid, + LOCAL_CONN_ID_LEN); + q_conn = GNUNET_new (struct quic_conn); + GNUNET_memcpy (q_conn->cid, scid, LOCAL_CONN_ID_LEN); + peer->conn = q_conn; + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "attempting to perform QUIC handshake with peer\n"); + q_conn->conn = quiche_connect (peer->foreign_addr, scid, LOCAL_CONN_ID_LEN, + local_addr, + local_in_len, peer->address, peer->address_len, + config); + flush_egress (peer->conn); + GNUNET_free (local_addr); + return GNUNET_OK; + /** + * TODO: handle this + */ + // if (NULL == timeout_task) + // timeout_task = GNUNET_SCHEDULER_add_now (&check_timeouts, NULL); +} + + +static void +try_connection_reversal (void *cls, + const struct sockaddr *addr, + socklen_t addrlen) +{ + /* FIXME: support reversal: #5529 */ + GNUNET_log (GNUNET_ERROR_TYPE_INFO, + "No connection reversal implemented!"); +} + + +/** + * Signature of the callback passed to #GNUNET_NAT_register() for + * a function to call whenever our set of 'valid' addresses changes. + * + * @param cls closure + * @param app_ctx[in,out] location where the app can store stuff + * on add and retrieve it on remove + * @param add_remove #GNUNET_YES to add a new public IP address, + * #GNUNET_NO to remove a previous (now invalid) one + * @param ac address class the address belongs to + * @param addr either the previous or the new public IP address + * @param addrlen actual length of the @a addr + */ +static void +nat_address_cb (void *cls, + void **app_ctx, + int add_remove, + enum GNUNET_NAT_AddressClass ac, + const struct sockaddr *addr, + socklen_t addrlen) +{ + char *my_addr; + struct GNUNET_TRANSPORT_AddressIdentifier *ai; + + if (GNUNET_YES == add_remove) + { + enum GNUNET_NetworkType nt; + + GNUNET_asprintf (&my_addr, + "%s-%s", + COMMUNICATOR_ADDRESS_PREFIX, + GNUNET_a2s (addr, addrlen)); + nt = GNUNET_NT_scanner_get_type (is, addr, addrlen); + ai = + GNUNET_TRANSPORT_communicator_address_add (ch, + my_addr, + nt, + GNUNET_TIME_UNIT_FOREVER_REL); + GNUNET_free (my_addr); + *app_ctx = ai; + } + else + { + ai = *app_ctx; + GNUNET_TRANSPORT_communicator_address_remove (ai); + *app_ctx = NULL; + } +} + + +/** + * Shutdown the QUIC communicator. + * + * @param cls NULL (always) + */ +static void +do_shutdown (void *cls) +{ + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "do_shutdown\n"); + GNUNET_CONTAINER_multihashmap_iterate (addr_map, &get_peer_delete_it, NULL); + GNUNET_CONTAINER_multihashmap_destroy (addr_map); + quiche_config_free (config); + + if (NULL != timeout_task) + { + GNUNET_SCHEDULER_cancel (timeout_task); + timeout_task = NULL; + } + if (NULL != read_task) + { + GNUNET_SCHEDULER_cancel (read_task); + read_task = NULL; + } + if (NULL != udp_sock) + { + GNUNET_break (GNUNET_OK == + GNUNET_NETWORK_socket_close (udp_sock)); + udp_sock = NULL; + } + if (NULL != ch) + { + GNUNET_TRANSPORT_communicator_disconnect (ch); + ch = NULL; + } + if (NULL != ah) + { + GNUNET_TRANSPORT_application_done (ah); + ah = NULL; + } + if (NULL != my_private_key) + { + GNUNET_free (my_private_key); + my_private_key = NULL; + } + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "do_shutdown finished\n"); +} + + +static void +sock_read (void *cls) +{ + struct sockaddr_storage sa; + struct sockaddr_in *addr_verify; + socklen_t salen = sizeof(sa); + uint8_t buf[UINT16_MAX]; + uint8_t out[MAX_DATAGRAM_SIZE]; + ssize_t rcvd; + + ssize_t process_pkt; + struct QUIC_header quic_header; + uint8_t new_cid[LOCAL_CONN_ID_LEN]; + + struct PeerAddress *peer; + struct GNUNET_HashCode addr_key; + + (void) cls; + quic_header.scid_len = sizeof(quic_header.scid); + quic_header.dcid_len = sizeof(quic_header.dcid); + quic_header.odcid_len = sizeof(quic_header.odcid); + quic_header.token_len = sizeof(quic_header.token); + /** + * Get local_addr, in_len for quiche + */ + char *bindto; + socklen_t in_len; + if (GNUNET_OK != + GNUNET_CONFIGURATION_get_value_string (cfg, + COMMUNICATOR_CONFIG_SECTION, + "BINDTO", + &bindto)) + { + GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR, + COMMUNICATOR_CONFIG_SECTION, + "BINDTO"); + return; + } + struct sockaddr *local_addr = udp_address_to_sockaddr (bindto, &in_len); + + read_task = GNUNET_SCHEDULER_add_read_net (GNUNET_TIME_UNIT_FOREVER_REL, + udp_sock, + &sock_read, + NULL); + while (1) + { + rcvd = GNUNET_NETWORK_socket_recvfrom (udp_sock, + buf, + sizeof(buf), + (struct sockaddr *) &sa, + &salen); + if (-1 == rcvd) + { + if (EAGAIN == errno) + break; // We are done reading data + GNUNET_log_strerror (GNUNET_ERROR_TYPE_DEBUG, "recv"); + return; + } + + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Read %lu bytes\n", rcvd); + + if (-1 == rcvd) + { + GNUNET_log_strerror (GNUNET_ERROR_TYPE_DEBUG, "recv"); + return; + } + /** + * FIXME: hashing address string vs ip/port. It is not ideal that + * we hash the string, instead of the binary representation, but + * for now it is certainly less code. + * Note that simply hashing the sockaddr does NOT work because the + * the struct is not portable. + */ + const char *addr_string = sockaddr_to_udpaddr_string ((const struct + sockaddr *) &sa, + salen); + GNUNET_CRYPTO_hash (addr_string, strlen (addr_string), + &addr_key); + GNUNET_free (addr_string); + peer = GNUNET_CONTAINER_multihashmap_get (addr_map, &addr_key); + + if (NULL == peer) + { + /** + * Create new PeerAddress (receiver) with id_rcvd = false + */ + peer = GNUNET_new (struct PeerAddress); + peer->address = GNUNET_memdup (&sa, salen); + peer->address_len = salen; + peer->id_rcvd = GNUNET_NO; + peer->id_sent = GNUNET_NO; + peer->is_receiver = GNUNET_NO; + peer->conn = NULL; + peer->foreign_addr = sockaddr_to_udpaddr_string (peer->address, + peer->address_len); + /** + * TODO: after connection established + */ + // setup_peer_mq (peer); + if (GNUNET_SYSERR == GNUNET_CONTAINER_multihashmap_put (addr_map, + &addr_key, + peer, + GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_ONLY)) + { + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "tried to add duplicate address into address map\n"); + return; + } + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "sock_read added new peer to address map\n"); + } + + /** + * Parse QUIC info + */ + int rc = quiche_header_info (buf, rcvd, LOCAL_CONN_ID_LEN, + &quic_header.version, + &quic_header.type, quic_header.scid, + &quic_header.scid_len, quic_header.dcid, + &quic_header.dcid_len, + quic_header.token, &quic_header.token_len); + if (0 > rc) + { + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "failed to parse quic header: %d\n", + rc); + return; + } + + /** + * New QUIC connection with peer + */ + if (NULL == peer->conn) + { + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "attempting to create new connection\n"); + if (0 == quiche_version_is_supported (quic_header.version)) + { + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "quic version negotiation initiated\n"); + /** + * FIXME variables are redeclared often. Refactor either + * to declare variables once in the beginning or refactor into + * method. + * + * Write a version negotiation packet to "out" + */ + ssize_t written = quiche_negotiate_version (quic_header.scid, + quic_header.scid_len, + quic_header.dcid, + quic_header.dcid_len, + out, sizeof(out)); + if (0 > written) + { + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "quiche failed to generate version negotiation packet\n"); + return; + } + ssize_t sent = GNUNET_NETWORK_socket_sendto (udp_sock, + out, + written, + (struct sockaddr*) &sa, + salen); + if (sent != written) + { + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "failed to send version negotiation packet to peer\n"); + return; + } + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "sent %zd bytes to peer during version negotiation\n", + sent); + return; + } + + if (0 == quic_header.token_len) + { + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "quic stateless retry\n"); + mint_token (quic_header.dcid, quic_header.dcid_len, &sa, salen, + quic_header.token, &quic_header.token_len); + + uint8_t new_cid[LOCAL_CONN_ID_LEN]; + GNUNET_CRYPTO_random_block (GNUNET_CRYPTO_QUALITY_STRONG, new_cid, + LOCAL_CONN_ID_LEN); + + ssize_t written = quiche_retry (quic_header.scid, quic_header.scid_len, + quic_header.dcid, quic_header.dcid_len, + new_cid, LOCAL_CONN_ID_LEN, + quic_header.token, + quic_header.token_len, + quic_header.version, out, sizeof(out)); + if (0 > written) + { + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "quiche failed to write retry packet\n"); + return; + } + ssize_t sent = GNUNET_NETWORK_socket_sendto (udp_sock, + out, + written, + (struct sockaddr*) &sa, + salen); + if (written != sent) + { + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "failed to send retry packet\n"); + return; + } + + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "sent %zd bytes\n", sent); + continue; + } + + if (GNUNET_OK != validate_token (quic_header.token, quic_header.token_len, + &sa, salen, + quic_header.odcid, + &quic_header.odcid_len)) + { + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "invalid address validation token created\n"); + return; + } + peer->conn = create_conn (quic_header.dcid, quic_header.dcid_len, + quic_header.odcid, quic_header.odcid_len, + local_addr, in_len, + &sa, salen); + if (NULL == peer->conn) + { + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "failed to create quic connection with peer\n"); + return; + } + } // null connection + + quiche_recv_info recv_info = { + (struct sockaddr *) &sa, + salen, + + local_addr, + in_len, + }; + /** + * Send our PeerIdentity if the connection is established now + */ + if (quiche_conn_is_established (peer->conn->conn) && ! peer->id_sent && + peer->is_receiver) + { + ssize_t send_len; + + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "handshake established with peer, sending our peer id\n"); + send_len = quiche_conn_stream_send (peer->conn->conn, STREAMID_BI, + (const uint8_t *) &my_identity, + sizeof(my_identity), + false); + if (0 > send_len) + { + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "failed to write peer identity packet. quiche error: %zd\n", + send_len); + return; + } + flush_egress (peer->conn); + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "peer identity sent to peer\n"); + peer->id_sent = GNUNET_YES; + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "setting up peer mq\n"); + setup_peer_mq (peer); + /** + * After this, we should be all good to send/recv data + */ + } + process_pkt = quiche_conn_recv (peer->conn->conn, buf, rcvd, &recv_info); + if (0 > process_pkt) + { + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "quiche failed to process received packet: %zd\n", + process_pkt); + return; + } + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "quiche processed %zd bytes\n", process_pkt); + // Check for data on all available streams if the connection is established + if (GNUNET_YES == quiche_conn_is_established (peer->conn->conn)) + { + recv_from_streams (peer); + } + /** + * TODO: Should we use a list instead of hashmap? + * Overhead for hashing function, O(1) retrieval vs O(n) iteration with n=30? + * + * TODO: Is iteration necessary as in the quiche server example? + */ + quiche_stats stats; + quiche_path_stats path_stats; + + flush_egress (peer->conn); + + if (quiche_conn_is_closed (peer->conn->conn)) + { + quiche_conn_stats (peer->conn->conn, &stats); + quiche_conn_path_stats (peer->conn->conn, 0, &path_stats); + + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "connection closed. quiche stats: sent=%zu, recv=%zu\n", + stats.sent, stats.recv); + peer_destroy (peer); + } + } + GNUNET_free (local_addr); +} + + +/** + * Setup communicator and launch network interactions. + * + * @param cls NULL (always) + * @param args remaining command-line arguments + * @param cfgfile name of the configuration file used (for saving, can be NULL!) + * @param c configuration + */ +static void +run (void *cls, + char *const *args, + const char *cfgfile, + const struct GNUNET_CONFIGURATION_Handle *c) +{ + char *bindto; + struct sockaddr *in; + socklen_t in_len; + struct sockaddr_storage in_sto; + socklen_t sto_len; + + (void) cls; + cfg = c; + + if (GNUNET_OK != + GNUNET_CONFIGURATION_get_value_string (cfg, + COMMUNICATOR_CONFIG_SECTION, + "BINDTO", + &bindto)) + { + GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR, + COMMUNICATOR_CONFIG_SECTION, + "BINDTO"); + return; + } + + in = udp_address_to_sockaddr (bindto, &in_len); + + if (NULL == in) + { + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "Failed to setup UDP socket address with path `%s'\n", + bindto); + GNUNET_free (bindto); + return; + } + udp_sock = + GNUNET_NETWORK_socket_create (in->sa_family, + SOCK_DGRAM, + IPPROTO_UDP); + if (NULL == udp_sock) + { + GNUNET_log_strerror (GNUNET_ERROR_TYPE_ERROR, "socket"); + GNUNET_free (in); + GNUNET_free (bindto); + return; + } + if (AF_INET6 == in->sa_family) + have_v6_socket = GNUNET_YES; + if (GNUNET_OK != + GNUNET_NETWORK_socket_bind (udp_sock, + in, + in_len)) + { + GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_ERROR, + "bind", + bindto); + GNUNET_NETWORK_socket_close (udp_sock); + udp_sock = NULL; + GNUNET_free (in); + GNUNET_free (bindto); + return; + } + sto_len = sizeof(in_sto); + if (0 != getsockname (GNUNET_NETWORK_get_fd (udp_sock), + (struct sockaddr *) &in_sto, + &sto_len)) + { + memcpy (&in_sto, in, in_len); + sto_len = in_len; + } + GNUNET_free (in); + GNUNET_free (bindto); + in = (struct sockaddr *) &in_sto; + in_len = sto_len; + GNUNET_log_from_nocheck (GNUNET_ERROR_TYPE_DEBUG, + "transport", + "Bound to `%s'\n", + GNUNET_a2s ((const struct sockaddr *) &in_sto, + sto_len)); + switch (in->sa_family) + { + case AF_INET: + my_port = ntohs (((struct sockaddr_in *) in)->sin_port); + break; + + case AF_INET6: + my_port = ntohs (((struct sockaddr_in6 *) in)->sin6_port); + break; + + default: + GNUNET_break (0); + my_port = 0; + } + GNUNET_SCHEDULER_add_shutdown (&do_shutdown, NULL); + /** + * Setup QUICHE configuration + */ + config = quiche_config_new (QUICHE_PROTOCOL_VERSION); + quiche_config_verify_peer (config, false); + /** + * TODO: configure TLS cert + */ + quiche_config_load_cert_chain_from_pem_file (config, "./cert.crt"); + quiche_config_load_priv_key_from_pem_file (config, "./cert.key"); + quiche_config_set_application_protos (config, + (uint8_t *) + "\x0ahq-interop\x05hq-29\x05hq-28\x05hq-27\x08http/0.9", + 38); + quiche_config_set_max_idle_timeout (config, 5000); + quiche_config_set_max_recv_udp_payload_size (config, 1200); + quiche_config_set_max_send_udp_payload_size (config, 1200); + quiche_config_set_initial_max_data (config, 10000000); + quiche_config_set_initial_max_stream_data_bidi_local (config, 1000000); + quiche_config_set_initial_max_stream_data_bidi_remote (config, 1000000); + quiche_config_set_initial_max_stream_data_uni (config, 1000000); + quiche_config_set_initial_max_streams_bidi (config, 100); + quiche_config_set_initial_max_streams_uni (config, 100); + quiche_config_set_cc_algorithm (config, QUICHE_CC_RENO); + quiche_config_set_disable_active_migration (config, true); + addr_map = GNUNET_CONTAINER_multihashmap_create (2, GNUNET_NO); + /** + * Get our public key for initial packet + */ + my_private_key = GNUNET_CRYPTO_eddsa_key_create_from_configuration (cfg); + if (NULL == my_private_key) + { + GNUNET_log ( + GNUNET_ERROR_TYPE_ERROR, + _ ( + "Transport service is lacking key configuration settings. Exiting.\n")); + GNUNET_SCHEDULER_shutdown (); + return; + } + GNUNET_CRYPTO_eddsa_key_get_public (my_private_key, &my_identity.public_key); + /* start reading */ + read_task = GNUNET_SCHEDULER_add_read_net (GNUNET_TIME_UNIT_FOREVER_REL, + udp_sock, + &sock_read, + NULL); + ch = GNUNET_TRANSPORT_communicator_connect (cfg, + COMMUNICATOR_CONFIG_SECTION, + COMMUNICATOR_ADDRESS_PREFIX, + GNUNET_TRANSPORT_CC_RELIABLE, + &mq_init, + NULL, + ¬ify_cb, + NULL); + is = GNUNET_NT_scanner_init (); + nat = GNUNET_NAT_register (cfg, + COMMUNICATOR_CONFIG_SECTION, + IPPROTO_UDP, + 1 /* one address */, + (const struct sockaddr **) &in, + &in_len, + &nat_address_cb, + try_connection_reversal, + NULL /* closure */); + if (NULL == ch) + { + GNUNET_break (0); + GNUNET_SCHEDULER_shutdown (); + return; + } + ah = GNUNET_TRANSPORT_application_init (cfg); + if (NULL == ah) + { + GNUNET_break (0); + GNUNET_SCHEDULER_shutdown (); + return; + } + + /* start broadcasting */ + // if (GNUNET_YES != + // GNUNET_CONFIGURATION_get_value_yesno (cfg, + // COMMUNICATOR_CONFIG_SECTION, + // "DISABLE_BROADCAST")) + // { + // broadcast_task = GNUNET_SCHEDULER_add_now (&do_broadcast, NULL); + // } +} + + +int +main (int argc, char *const *argv) +{ + static const struct GNUNET_GETOPT_CommandLineOption options[] = { + GNUNET_GETOPT_OPTION_END + }; + int ret; + + GNUNET_log_from_nocheck (GNUNET_ERROR_TYPE_DEBUG, + "transport", + "Starting quic communicator\n"); + if (GNUNET_OK != GNUNET_STRINGS_get_utf8_args (argc, argv, &argc, &argv)) + return 2; + + ret = (GNUNET_OK == GNUNET_PROGRAM_run (argc, + argv, + "gnunet-communicator-quic", + _ ("GNUnet QUIC communicator"), + options, + &run, + NULL)) + ? 0 + : 1; + GNUNET_free_nz ((void *) argv); + return ret; +} diff --git a/src/service/transport/gnunet-communicator-tcp.c b/src/service/transport/gnunet-communicator-tcp.c new file mode 100644 index 000000000..e7d989021 --- /dev/null +++ b/src/service/transport/gnunet-communicator-tcp.c @@ -0,0 +1,4082 @@ +/* + This file is part of GNUnet + Copyright (C) 2010-2014, 2018, 2019 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 . + + SPDX-License-Identifier: AGPL3.0-or-later + */ + +/** + * @file transport/gnunet-communicator-tcp.c + * @brief Transport plugin using TCP. + * @author Christian Grothoff + * + * TODO: + * - support NAT connection reversal method (#5529) + * - support other TCP-specific NAT traversal methods (#5531) + */ +#include "platform.h" +#include "gnunet_util_lib.h" +#include "gnunet_core_service.h" +#include "gnunet_peerstore_service.h" +#include "gnunet_protocols.h" +#include "gnunet_signatures.h" +#include "gnunet_constants.h" +#include "gnunet_nat_service.h" +#include "gnunet_statistics_service.h" +#include "gnunet_transport_communication_service.h" +#include "gnunet_resolver_service.h" + + +/** + * How long until we give up on establishing an NAT connection? + * Must be > 4 RTT + */ +#define NAT_TIMEOUT GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS, 10) + +/** + * How long do we believe our addresses to remain up (before + * the other peer should revalidate). + */ +#define ADDRESS_VALIDITY_PERIOD \ + GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_HOURS, 4) + +/** + * How many messages do we keep at most in the queue to the + * transport service before we start to drop (default, + * can be changed via the configuration file). + * Should be _below_ the level of the communicator API, as + * otherwise we may read messages just to have them dropped + * by the communicator API. + */ +#define DEFAULT_MAX_QUEUE_LENGTH 8 + +/** + * Size of our IO buffers for ciphertext data. Must be at + * least UINT_MAX + sizeof (struct TCPBox). + */ +#define BUF_SIZE (2 * 64 * 1024 + sizeof(struct TCPBox)) + +/** + * How often do we rekey based on time (at least) + */ +#define DEFAULT_REKEY_INTERVAL GNUNET_TIME_UNIT_DAYS + +/** + * How long do we wait until we must have received the initial KX? + */ +#define PROTO_QUEUE_TIMEOUT GNUNET_TIME_UNIT_MINUTES + +/** + * How often do we rekey based on number of bytes transmitted? + * (additionally randomized). Currently 400 MB + */ +#define REKEY_MAX_BYTES (1024LLU * 1024 * 400) + +/** + * Size of the initial key exchange message sent first in both + * directions. + */ +#define INITIAL_KX_SIZE \ + (sizeof(struct GNUNET_CRYPTO_EcdhePublicKey) \ + + sizeof(struct TCPConfirmation)) + +/** + * Size of the initial core key exchange messages. + */ +#define INITIAL_CORE_KX_SIZE \ + (sizeof(struct EphemeralKeyMessage) \ + + sizeof(struct PingMessage) \ + + sizeof(struct PongMessage)) + +/** + * Address prefix used by the communicator. + */ +#define COMMUNICATOR_ADDRESS_PREFIX "tcp" + +/** + * Configuration section used by the communicator. + */ +#define COMMUNICATOR_CONFIG_SECTION "communicator-tcp" + +GNUNET_NETWORK_STRUCT_BEGIN + + +/** + * Signature we use to verify that the ephemeral key was really chosen by + * the specified sender. + */ +struct TcpHandshakeSignature +{ + /** + * Purpose must be #GNUNET_SIGNATURE_PURPOSE_COMMUNICATOR_TCP_HANDSHAKE + */ + struct GNUNET_CRYPTO_EccSignaturePurpose purpose; + + /** + * Identity of the inititor of the TCP connection (TCP client). + */ + struct GNUNET_PeerIdentity sender; + + /** + * Presumed identity of the target of the TCP connection (TCP server) + */ + struct GNUNET_PeerIdentity receiver; + + /** + * Ephemeral key used by the @e sender. + */ + struct GNUNET_CRYPTO_EcdhePublicKey ephemeral; + + /** + * Monotonic time of @e sender, to possibly help detect replay attacks + * (if receiver persists times by sender). + */ + struct GNUNET_TIME_AbsoluteNBO monotonic_time; + + /** + * Challenge value used to protect against replay attack, if there is no stored monotonic time value. + */ + struct GNUNET_CRYPTO_ChallengeNonceP challenge; +}; + +/** + * Signature we use to verify that the ack from the receiver of the ephemeral key was really send by + * the specified sender. + */ +struct TcpHandshakeAckSignature +{ + /** + * Purpose must be #GNUNET_SIGNATURE_PURPOSE_COMMUNICATOR_TCP_HANDSHAKE_ACK + */ + struct GNUNET_CRYPTO_EccSignaturePurpose purpose; + + /** + * Identity of the inititor of the TCP connection (TCP client). + */ + struct GNUNET_PeerIdentity sender; + + /** + * Presumed identity of the target of the TCP connection (TCP server) + */ + struct GNUNET_PeerIdentity receiver; + + /** + * Monotonic time of @e sender, to possibly help detect replay attacks + * (if receiver persists times by sender). + */ + struct GNUNET_TIME_AbsoluteNBO monotonic_time; + + /** + * Challenge value used to protect against replay attack, if there is no stored monotonic time value. + */ + struct GNUNET_CRYPTO_ChallengeNonceP challenge; +}; + +/** + * Encrypted continuation of TCP initial handshake. + */ +struct TCPConfirmation +{ + /** + * Sender's identity + */ + struct GNUNET_PeerIdentity sender; + + /** + * Sender's signature of type #GNUNET_SIGNATURE_PURPOSE_COMMUNICATOR_TCP_HANDSHAKE + */ + struct GNUNET_CRYPTO_EddsaSignature sender_sig; + + /** + * Monotonic time of @e sender, to possibly help detect replay attacks + * (if receiver persists times by sender). + */ + struct GNUNET_TIME_AbsoluteNBO monotonic_time; + + /** + * Challenge value used to protect against replay attack, if there is no stored monotonic time value. + */ + struct GNUNET_CRYPTO_ChallengeNonceP challenge; + +}; + +/** + * Ack for the encrypted continuation of TCP initial handshake. + */ +struct TCPConfirmationAck +{ + + + /** + * Type is #GNUNET_MESSAGE_TYPE_COMMUNICATOR_TCP_CONFIRMATION_ACK. + */ + struct GNUNET_MessageHeader header; + + /** + * Sender's identity + */ + struct GNUNET_PeerIdentity sender; + + /** + * Sender's signature of type #GNUNET_SIGNATURE_PURPOSE_COMMUNICATOR_TCP_HANDSHAKE_ACK + */ + struct GNUNET_CRYPTO_EddsaSignature sender_sig; + + /** + * Monotonic time of @e sender, to possibly help detect replay attacks + * (if receiver persists times by sender). + */ + struct GNUNET_TIME_AbsoluteNBO monotonic_time; + + /** + * Challenge value used to protect against replay attack, if there is no stored monotonic time value. + */ + struct GNUNET_CRYPTO_ChallengeNonceP challenge; + +}; + +/** + * TCP message box. Always sent encrypted! + */ +struct TCPBox +{ + /** + * Type is #GNUNET_MESSAGE_TYPE_COMMUNICATOR_TCP_BOX. Warning: the + * header size EXCLUDES the size of the `struct TCPBox`. We usually + * never do this, but here the payload may truly be 64k *after* the + * TCPBox (as we have no MTU)!! + */ + struct GNUNET_MessageHeader header; + + /** + * HMAC for the following encrypted message. Yes, we MUST use + * mac-then-encrypt here, as we want to hide the message sizes on + * the wire (zero plaintext design!). Using CTR mode, padding oracle + * attacks do not apply. Besides, due to the use of ephemeral keys + * (hopefully with effective replay protection from monotonic time!) + * the attacker is limited in using the oracle. + */ + struct GNUNET_ShortHashCode hmac; + + /* followed by as may bytes of payload as indicated in @e header, + excluding the TCPBox itself! */ +}; + + +/** + * TCP rekey message box. Always sent encrypted! Data after + * this message will use the new key. + */ +struct TCPRekey +{ + /** + * Type is #GNUNET_MESSAGE_TYPE_COMMUNICATOR_TCP_REKEY. + */ + struct GNUNET_MessageHeader header; + + /** + * HMAC for the following encrypted message. Yes, we MUST use + * mac-then-encrypt here, as we want to hide the message sizes on + * the wire (zero plaintext design!). Using CTR mode padding oracle + * attacks do not apply. Besides, due to the use of ephemeral keys + * (hopefully with effective replay protection from monotonic time!) + * the attacker is limited in using the oracle. + */ + struct GNUNET_ShortHashCode hmac; + + /** + * New ephemeral key. + */ + struct GNUNET_CRYPTO_EcdhePublicKey ephemeral; + + /** + * Sender's signature of type #GNUNET_SIGNATURE_PURPOSE_COMMUNICATOR_TCP_REKEY + */ + struct GNUNET_CRYPTO_EddsaSignature sender_sig; + + /** + * Monotonic time of @e sender, to possibly help detect replay attacks + * (if receiver persists times by sender). + */ + struct GNUNET_TIME_AbsoluteNBO monotonic_time; +}; + +/** + * Signature we use to verify that the ephemeral key was really chosen by + * the specified sender. + */ +struct TcpRekeySignature +{ + /** + * Purpose must be #GNUNET_SIGNATURE_PURPOSE_COMMUNICATOR_TCP_REKEY + */ + struct GNUNET_CRYPTO_EccSignaturePurpose purpose; + + /** + * Identity of the inititor of the TCP connection (TCP client). + */ + struct GNUNET_PeerIdentity sender; + + /** + * Presumed identity of the target of the TCP connection (TCP server) + */ + struct GNUNET_PeerIdentity receiver; + + /** + * Ephemeral key used by the @e sender. + */ + struct GNUNET_CRYPTO_EcdhePublicKey ephemeral; + + /** + * Monotonic time of @e sender, to possibly help detect replay attacks + * (if receiver persists times by sender). + */ + struct GNUNET_TIME_AbsoluteNBO monotonic_time; +}; + +/** + * TCP finish. Sender asks for the connection to be closed. + * Needed/useful in case we drop RST/FIN packets on the GNUnet + * port due to the possibility of malicious RST/FIN injection. + */ +struct TCPFinish +{ + /** + * Type is #GNUNET_MESSAGE_TYPE_COMMUNICATOR_TCP_FINISH. + */ + struct GNUNET_MessageHeader header; + + /** + * HMAC for the following encrypted message. Yes, we MUST use + * mac-then-encrypt here, as we want to hide the message sizes on + * the wire (zero plaintext design!). Using CTR mode padding oracle + * attacks do not apply. Besides, due to the use of ephemeral keys + * (hopefully with effective replay protection from monotonic time!) + * the attacker is limited in using the oracle. + */ + struct GNUNET_ShortHashCode hmac; +}; + +/** + * Basically a WELCOME message, but with the purpose + * of giving the waiting peer a client handle to use + */ +struct TCPNATProbeMessage +{ + /** + * Type is #GNUNET_MESSAGE_TYPE_TRANSPORT_TCP_NAT_PROBE. + */ + struct GNUNET_MessageHeader header; + + /** + * Identity of the sender of the message. + */ + struct GNUNET_PeerIdentity clientIdentity; +}; + +GNUNET_NETWORK_STRUCT_END + +/** + * Struct for pending nat reversals. + */ +struct PendingReversal +{ + /* + * Timeout task. + */ + struct GNUNET_SCHEDULER_Task *timeout_task; + + /** + * To whom are we like to talk to. + */ + struct GNUNET_PeerIdentity target; + + /** + * Address the reversal was send to. + */ + struct sockaddr *in; +}; + +/** + * Struct to use as closure. + */ +struct ListenTask +{ + /** + * ID of listen task + */ + struct GNUNET_SCHEDULER_Task *listen_task; + + /** + * Listen socket. + */ + struct GNUNET_NETWORK_Handle *listen_sock; +}; + +/** + * Handle for a queue. + */ +struct Queue +{ + /** + * To whom are we talking to. + */ + struct GNUNET_PeerIdentity target; + + /** + * Listen socket. + */ + struct GNUNET_NETWORK_Handle *listen_sock; + + /** + * socket that we transmit all data with on this queue + */ + struct GNUNET_NETWORK_Handle *sock; + + /** + * cipher for decryption of incoming data. + */ + gcry_cipher_hd_t in_cipher; + + /** + * cipher for encryption of outgoing data. + */ + gcry_cipher_hd_t out_cipher; + + /** + * Shared secret for HMAC verification on incoming data. + */ + struct GNUNET_HashCode in_hmac; + + /** + * Shared secret for HMAC generation on outgoing data, ratcheted after + * each operation. + */ + struct GNUNET_HashCode out_hmac; + + /** + * ID of read task for this connection. + */ + struct GNUNET_SCHEDULER_Task *read_task; + + /** + * ID of write task for this connection. + */ + struct GNUNET_SCHEDULER_Task *write_task; + + /** + * Address of the other peer. + */ + struct sockaddr *address; + + /** + * How many more bytes may we sent with the current @e out_cipher + * before we should rekey? + */ + uint64_t rekey_left_bytes; + + /** + * Until what time may we sent with the current @e out_cipher + * before we should rekey? + */ + struct GNUNET_TIME_Absolute rekey_time; + + /** + * Length of the address. + */ + socklen_t address_len; + + /** + * Message queue we are providing for the #ch. + */ + struct GNUNET_MQ_Handle *mq; + + /** + * handle for this queue with the #ch. + */ + struct GNUNET_TRANSPORT_QueueHandle *qh; + + /** + * Number of bytes we currently have in our write queue. + */ + unsigned long long bytes_in_queue; + + /** + * Buffer for reading ciphertext from network into. + */ + char cread_buf[BUF_SIZE]; + + /** + * buffer for writing ciphertext to network. + */ + char cwrite_buf[BUF_SIZE]; + + /** + * Plaintext buffer for decrypted plaintext. + */ + char pread_buf[UINT16_MAX + 1 + sizeof(struct TCPBox)]; + + /** + * Plaintext buffer for messages to be encrypted. + */ + char pwrite_buf[UINT16_MAX + 1 + sizeof(struct TCPBox)]; + + /** + * At which offset in the ciphertext read buffer should we + * append more ciphertext for transmission next? + */ + size_t cread_off; + + /** + * At which offset in the ciphertext write buffer should we + * append more ciphertext from reading next? + */ + size_t cwrite_off; + + /** + * At which offset in the plaintext input buffer should we + * append more plaintext from decryption next? + */ + size_t pread_off; + + /** + * At which offset in the plaintext output buffer should we + * append more plaintext for encryption next? + */ + size_t pwrite_off; + + /** + * Timeout for this queue. + */ + struct GNUNET_TIME_Absolute timeout; + + /** + * How may messages did we pass from this queue to CORE for which we + * have yet to receive an acknoweldgement that CORE is done with + * them? If "large" (or even just non-zero), we should throttle + * reading to provide flow control. See also #DEFAULT_MAX_QUEUE_LENGTH + * and #max_queue_length. + */ + unsigned int backpressure; + + /** + * Which network type does this queue use? + */ + enum GNUNET_NetworkType nt; + + /** + * The connection status of this queue. + */ + enum GNUNET_TRANSPORT_ConnectionStatus cs; + + /** + * Is MQ awaiting a #GNUNET_MQ_impl_send_continue() call? + */ + int mq_awaits_continue; + + /** + * Did we enqueue a finish message and are closing down the queue? + */ + int finishing; + + /** + * Did we technically destroy this queue, but kept the allocation + * around because of @e backpressure not being zero yet? Used + * simply to delay the final #GNUNET_free() operation until + * #core_read_finished_cb() has been called. + */ + int destroyed; + + /** + * #GNUNET_YES if we just rekeyed and must thus possibly + * re-decrypt ciphertext. + */ + int rekeyed; + + /** + * Monotonic time value for rekey message. + */ + struct GNUNET_TIME_AbsoluteNBO rekey_monotonic_time; + + /** + * Monotonic time value for handshake message. + */ + struct GNUNET_TIME_AbsoluteNBO handshake_monotonic_time; + + /** + * Monotonic time value for handshake ack message. + */ + struct GNUNET_TIME_AbsoluteNBO handshake_ack_monotonic_time; + + /** + * Challenge value used to protect against replay attack, if there is no stored monotonic time value. + */ + struct GNUNET_CRYPTO_ChallengeNonceP challenge; + + /** + * Challenge value received. In case of inbound connection we have to remember the value, because we send the challenge back later after we received the GNUNET_MESSAGE_TYPE_COMMUNICATOR_TCP_CONFIRMATION_ACK. + */ + struct GNUNET_CRYPTO_ChallengeNonceP challenge_received; + + /** + * Iteration Context for retrieving the monotonic time send with key for rekeying. + */ + struct GNUNET_PEERSTORE_IterateContext *rekey_monotime_get; + + /** + * Iteration Context for retrieving the monotonic time send with the handshake. + */ + struct GNUNET_PEERSTORE_IterateContext *handshake_monotime_get; + + /** + * Iteration Context for retrieving the monotonic time send with the handshake ack. + */ + struct GNUNET_PEERSTORE_IterateContext *handshake_ack_monotime_get; + + /** + * Store Context for retrieving the monotonic time send with key for rekeying. + */ + struct GNUNET_PEERSTORE_StoreContext *rekey_monotime_sc; + + /** + * Store Context for retrieving the monotonic time send with the handshake. + */ + struct GNUNET_PEERSTORE_StoreContext *handshake_monotime_sc; + + /** + * Store Context for retrieving the monotonic time send with the handshake ack. + */ + struct GNUNET_PEERSTORE_StoreContext *handshake_ack_monotime_sc; + + /** + * Size of data received without KX challenge played back. + */ + // TODO remove? + size_t unverified_size; + + /** + * Has the initial (core) handshake already happened? + */ + int initial_core_kx_done; +}; + + +/** + * Handle for an incoming connection where we do not yet have enough + * information to setup a full queue. + */ +struct ProtoQueue +{ + /** + * Kept in a DLL. + */ + struct ProtoQueue *next; + + /** + * Kept in a DLL. + */ + struct ProtoQueue *prev; + + /** + * Listen socket. + */ + struct GNUNET_NETWORK_Handle *listen_sock; + + /** + * socket that we transmit all data with on this queue + */ + struct GNUNET_NETWORK_Handle *sock; + + /** + * ID of write task for this connection. + */ + struct GNUNET_SCHEDULER_Task *write_task; + + /** + * buffer for writing struct TCPNATProbeMessage to network. + */ + char write_buf[sizeof (struct TCPNATProbeMessage)]; + + /** + * Offset of the buffer? + */ + size_t write_off; + + /** + * ID of read task for this connection. + */ + struct GNUNET_SCHEDULER_Task *read_task; + + /** + * Address of the other peer. + */ + struct sockaddr *address; + + /** + * Length of the address. + */ + socklen_t address_len; + + /** + * Timeout for this protoqueue. + */ + struct GNUNET_TIME_Absolute timeout; + + /** + * Buffer for reading all the information we need to upgrade from + * protoqueue to queue. + */ + char ibuf[INITIAL_KX_SIZE]; + + /** + * Current offset for reading into @e ibuf. + */ + size_t ibuf_off; +}; + +/** + * In case of port only configuration we like to bind to ipv4 and ipv6 addresses. + */ +struct PortOnlyIpv4Ipv6 +{ + /** + * Ipv4 address we like to bind to. + */ + struct sockaddr *addr_ipv4; + + /** + * Length of ipv4 address. + */ + socklen_t addr_len_ipv4; + + /** + * Ipv6 address we like to bind to. + */ + struct sockaddr *addr_ipv6; + + /** + * Length of ipv6 address. + */ + socklen_t addr_len_ipv6; + +}; + +/** + * DLL to store the addresses we like to register at NAT service. + */ +struct Addresses +{ + /** + * Kept in a DLL. + */ + struct Addresses *next; + + /** + * Kept in a DLL. + */ + struct Addresses *prev; + + /** + * Address we like to register at NAT service. + */ + struct sockaddr *addr; + + /** + * Length of address we like to register at NAT service. + */ + socklen_t addr_len; + +}; + + +/** + * Maximum queue length before we stop reading towards the transport service. + */ +static unsigned long long max_queue_length; + +/** + * For logging statistics. + */ +static struct GNUNET_STATISTICS_Handle *stats; + +/** + * Our environment. + */ +static struct GNUNET_TRANSPORT_CommunicatorHandle *ch; + +/** + * Queues (map from peer identity to `struct Queue`) + */ +static struct GNUNET_CONTAINER_MultiPeerMap *queue_map; + +/** + * ListenTasks (map from socket to `struct ListenTask`) + */ +static struct GNUNET_CONTAINER_MultiHashMap *lt_map; + +/** + * Our public key. + */ +static struct GNUNET_PeerIdentity my_identity; + +/** + * The rekey interval + */ +static struct GNUNET_TIME_Relative rekey_interval; + +/** + * Our private key. + */ +static struct GNUNET_CRYPTO_EddsaPrivateKey *my_private_key; + +/** + * Our configuration. + */ +static const struct GNUNET_CONFIGURATION_Handle *cfg; + +/** + * Network scanner to determine network types. + */ +static struct GNUNET_NT_InterfaceScanner *is; + +/** + * Connection to NAT service. + */ +static struct GNUNET_NAT_Handle *nat; + +/** + * Protoqueues DLL head. + */ +static struct ProtoQueue *proto_head; + +/** + * Protoqueues DLL tail. + */ +static struct ProtoQueue *proto_tail; + +/** + * Handle for DNS lookup of bindto address + */ +struct GNUNET_RESOLVER_RequestHandle *resolve_request_handle; + +/** + * Head of DLL with addresses we like to register at NAT servcie. + */ +struct Addresses *addrs_head; + +/** + * Head of DLL with addresses we like to register at NAT servcie. + */ +struct Addresses *addrs_tail; + +/** + * Head of DLL with ListenTasks. + */ +struct ListenTask *lts_head; + +/** + * Head of DLL with ListenTask. + */ +struct ListenTask *lts_tail; + +/** + * Number of addresses in the DLL for register at NAT service. + */ +int addrs_lens; + + +/** + * Database for peer's HELLOs. + */ +static struct GNUNET_PEERSTORE_Handle *peerstore; + +/** + * A flag indicating we are already doing a shutdown. + */ +int shutdown_running = GNUNET_NO; + +/** + * The port the communicator should be assigned to. + */ +unsigned int bind_port; + +/** + * Map of pending reversals. + */ +struct GNUNET_CONTAINER_MultiHashMap *pending_reversals; + +/** + * We have been notified that our listen socket has something to + * read. Do the read and reschedule this function to be called again + * once more is available. + * + * @param cls NULL + */ +static void +listen_cb (void *cls); + +/** + * Functions with this signature are called whenever we need + * to close a queue due to a disconnect or failure to + * establish a connection. + * + * @param queue queue to close down + */ +static void +queue_destroy (struct Queue *queue) +{ + struct ListenTask *lt = NULL; + struct GNUNET_HashCode h_sock; + int sockfd; + + if (NULL != queue->listen_sock) + { + sockfd = GNUNET_NETWORK_get_fd (queue->listen_sock); + GNUNET_CRYPTO_hash (&sockfd, + sizeof(int), + &h_sock); + + lt = GNUNET_CONTAINER_multihashmap_get (lt_map, &h_sock); + } + + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Disconnecting queue for peer `%s'\n", + GNUNET_i2s (&queue->target)); + if (NULL != queue->rekey_monotime_sc) + { + GNUNET_PEERSTORE_store_cancel (queue->rekey_monotime_sc); + queue->rekey_monotime_sc = NULL; + } + if (NULL != queue->handshake_monotime_sc) + { + GNUNET_PEERSTORE_store_cancel (queue->handshake_monotime_sc); + queue->handshake_monotime_sc = NULL; + } + if (NULL != queue->handshake_ack_monotime_sc) + { + GNUNET_PEERSTORE_store_cancel (queue->handshake_ack_monotime_sc); + queue->handshake_ack_monotime_sc = NULL; + } + if (NULL != queue->rekey_monotime_get) + { + GNUNET_PEERSTORE_iterate_cancel (queue->rekey_monotime_get); + queue->rekey_monotime_get = NULL; + } + if (NULL != queue->handshake_monotime_get) + { + GNUNET_PEERSTORE_iterate_cancel (queue->handshake_monotime_get); + queue->handshake_monotime_get = NULL; + } + if (NULL != queue->handshake_ack_monotime_get) + { + GNUNET_PEERSTORE_iterate_cancel (queue->handshake_ack_monotime_get); + queue->handshake_ack_monotime_get = NULL; + } + if (NULL != queue->qh) + { + GNUNET_TRANSPORT_communicator_mq_del (queue->qh); + queue->qh = NULL; + } + GNUNET_assert ( + GNUNET_YES == + GNUNET_CONTAINER_multipeermap_remove (queue_map, &queue->target, queue)); + GNUNET_STATISTICS_set (stats, + "# queues active", + GNUNET_CONTAINER_multipeermap_size (queue_map), + GNUNET_NO); + if (NULL != queue->read_task) + { + GNUNET_SCHEDULER_cancel (queue->read_task); + queue->read_task = NULL; + } + if (NULL != queue->write_task) + { + GNUNET_SCHEDULER_cancel (queue->write_task); + queue->write_task = NULL; + } + if (GNUNET_SYSERR == GNUNET_NETWORK_socket_close (queue->sock)) + { + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "closing socket failed\n"); + } + gcry_cipher_close (queue->in_cipher); + gcry_cipher_close (queue->out_cipher); + GNUNET_free (queue->address); + if (0 != queue->backpressure) + queue->destroyed = GNUNET_YES; + else + GNUNET_free (queue); + + if (NULL == lt) + return; + + if ((! shutdown_running) && (NULL == lt->listen_task)) + { + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "add read net listen\n"); + lt->listen_task = GNUNET_SCHEDULER_add_read_net ( + GNUNET_TIME_UNIT_FOREVER_REL, + lt->listen_sock, + &listen_cb, + lt); + } + else + GNUNET_free (lt); +} + + +/** + * Compute @a mac over @a buf, and ratched the @a hmac_secret. + * + * @param[in,out] hmac_secret secret for HMAC calculation + * @param buf buffer to MAC + * @param buf_size number of bytes in @a buf + * @param[out] smac where to write the HMAC + */ +static void +calculate_hmac (struct GNUNET_HashCode *hmac_secret, + const void *buf, + size_t buf_size, + struct GNUNET_ShortHashCode *smac) +{ + struct GNUNET_HashCode mac; + + GNUNET_CRYPTO_hmac_raw (hmac_secret, + sizeof(struct GNUNET_HashCode), + buf, + buf_size, + &mac); + /* truncate to `struct GNUNET_ShortHashCode` */ + memcpy (smac, &mac, sizeof(struct GNUNET_ShortHashCode)); + /* ratchet hmac key */ + GNUNET_CRYPTO_hash (hmac_secret, + sizeof(struct GNUNET_HashCode), + hmac_secret); +} + + +/** + * Append a 'finish' message to the outgoing transmission. Once the + * finish has been transmitted, destroy the queue. + * + * @param queue queue to shut down nicely + */ +static void +queue_finish (struct Queue *queue) +{ + struct TCPFinish fin; + + memset (&fin, 0, sizeof(fin)); + fin.header.size = htons (sizeof(fin)); + fin.header.type = htons (GNUNET_MESSAGE_TYPE_COMMUNICATOR_TCP_FINISH); + calculate_hmac (&queue->out_hmac, &fin, sizeof(fin), &fin.hmac); + /* if there is any message left in pwrite_buf, we + overwrite it (possibly dropping the last message + from CORE hard here) */ + memcpy (queue->pwrite_buf, &fin, sizeof(fin)); + queue->pwrite_off = sizeof(fin); + /* This flag will ensure that #queue_write() no longer + notifies CORE about the possibility of sending + more data, and that #queue_write() will call + #queue_destroy() once the @c fin was fully written. */ + queue->finishing = GNUNET_YES; +} + + +/** + * Increment queue timeout due to activity. We do not immediately + * notify the monitor here as that might generate excessive + * signalling. + * + * @param queue queue for which the timeout should be rescheduled + */ +static void +reschedule_queue_timeout (struct Queue *queue) +{ + queue->timeout = + GNUNET_TIME_relative_to_absolute (GNUNET_CONSTANTS_IDLE_CONNECTION_TIMEOUT); +} + + +/** + * Queue read task. If we hit the timeout, disconnect it + * + * @param cls the `struct Queue *` to disconnect + */ +static void +queue_read (void *cls); + + +/** + * Core tells us it is done processing a message that transport + * received on a queue with status @a success. + * + * @param cls a `struct Queue *` where the message originally came from + * @param success #GNUNET_OK on success + */ +static void +core_read_finished_cb (void *cls, int success) +{ + struct Queue *queue = cls; + if (GNUNET_OK != success) + GNUNET_STATISTICS_update (stats, + "# messages lost in communicator API towards CORE", + 1, + GNUNET_NO); + if (NULL == queue) + return; + + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "backpressure %u\n", + queue->backpressure); + + queue->backpressure--; + /* handle deferred queue destruction */ + if ((queue->destroyed) && (0 == queue->backpressure)) + { + GNUNET_free (queue); + return; + } + else if (GNUNET_YES != queue->destroyed) + { + reschedule_queue_timeout (queue); + /* possibly unchoke reading, now that CORE made progress */ + if (NULL == queue->read_task) + queue->read_task = + GNUNET_SCHEDULER_add_read_net (GNUNET_TIME_absolute_get_remaining ( + queue->timeout), + queue->sock, + &queue_read, + queue); + } +} + + +/** + * We received @a plaintext_len bytes of @a plaintext on @a queue. + * Pass it on to CORE. If transmission is actually happening, + * increase backpressure counter. + * + * @param queue the queue that received the plaintext + * @param plaintext the plaintext that was received + * @param plaintext_len number of bytes of plaintext received + */ +static void +pass_plaintext_to_core (struct Queue *queue, + const void *plaintext, + size_t plaintext_len) +{ + const struct GNUNET_MessageHeader *hdr = plaintext; + int ret; + + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "pass message from %s to core\n", + GNUNET_i2s (&queue->target)); + + if (ntohs (hdr->size) != plaintext_len) + { + /* NOTE: If we ever allow multiple CORE messages in one + BOX, this will have to change! */ + GNUNET_break (0); + return; + } + ret = GNUNET_TRANSPORT_communicator_receive (ch, + &queue->target, + hdr, + ADDRESS_VALIDITY_PERIOD, + &core_read_finished_cb, + queue); + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "passed to core\n"); + if (GNUNET_OK == ret) + queue->backpressure++; + GNUNET_break (GNUNET_NO != ret); /* backpressure not working!? */ + if (GNUNET_SYSERR == ret) + GNUNET_STATISTICS_update (stats, + "# bytes lost due to CORE not running", + plaintext_len, + GNUNET_NO); +} + + +/** + * Setup @a cipher based on shared secret @a dh and decrypting + * peer @a pid. + * + * @param dh shared secret + * @param pid decrypting peer's identity + * @param[out] cipher cipher to initialize + * @param[out] hmac_key HMAC key to initialize + */ +static void +setup_cipher (const struct GNUNET_HashCode *dh, + const struct GNUNET_PeerIdentity *pid, + gcry_cipher_hd_t *cipher, + struct GNUNET_HashCode *hmac_key) +{ + char key[256 / 8]; + char ctr[128 / 8]; + + GNUNET_assert (0 == gcry_cipher_open (cipher, + GCRY_CIPHER_AES256 /* low level: go for speed */, + GCRY_CIPHER_MODE_CTR, + 0 /* flags */)); + GNUNET_assert (GNUNET_YES == GNUNET_CRYPTO_kdf (key, + sizeof(key), + "TCP-key", + strlen ("TCP-key"), + dh, + sizeof(*dh), + pid, + sizeof(*pid), + NULL, + 0)); + GNUNET_assert (0 == gcry_cipher_setkey (*cipher, key, sizeof(key))); + GNUNET_assert (GNUNET_YES == GNUNET_CRYPTO_kdf (ctr, + sizeof(ctr), + "TCP-ctr", + strlen ("TCP-ctr"), + dh, + sizeof(*dh), + pid, + sizeof(*pid), + NULL, + 0)); + gcry_cipher_setctr (*cipher, ctr, sizeof(ctr)); + GNUNET_assert (GNUNET_YES == + GNUNET_CRYPTO_kdf (hmac_key, + sizeof(struct GNUNET_HashCode), + "TCP-hmac", + strlen ("TCP-hmac"), + dh, + sizeof(*dh), + pid, + sizeof(*pid), + NULL, + 0)); +} + + +/** + * Callback called when peerstore store operation for rekey monotime value is finished. + * @param cls Queue context the store operation was executed. + * @param success Store operation was successful (GNUNET_OK) or not. + */ +static void +rekey_monotime_store_cb (void *cls, int success) +{ + struct Queue *queue = cls; + if (GNUNET_OK != success) + { + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "Failed to store rekey monotonic time in PEERSTORE!\n"); + } + queue->rekey_monotime_sc = NULL; +} + + +/** + * Callback called by peerstore when records for GNUNET_PEERSTORE_TRANSPORT_TCP_COMMUNICATOR_REKEY + * where found. + * @param cls Queue context the store operation was executed. + * @param record The record found or NULL if there is no record left. + * @param emsg Message from peerstore. + */ +static void +rekey_monotime_cb (void *cls, + const struct GNUNET_PEERSTORE_Record *record, + const char *emsg) +{ + struct Queue *queue = cls; + struct GNUNET_TIME_AbsoluteNBO *mtbe; + struct GNUNET_TIME_Absolute mt; + const struct GNUNET_PeerIdentity *pid; + struct GNUNET_TIME_AbsoluteNBO *rekey_monotonic_time; + + (void) emsg; + + rekey_monotonic_time = &queue->rekey_monotonic_time; + pid = &queue->target; + if (NULL == record) + { + queue->rekey_monotime_get = NULL; + return; + } + if (sizeof(*mtbe) != record->value_size) + { + GNUNET_break (0); + return; + } + mtbe = record->value; + mt = GNUNET_TIME_absolute_ntoh (*mtbe); + if (mt.abs_value_us > GNUNET_TIME_absolute_ntoh ( + queue->rekey_monotonic_time).abs_value_us) + { + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "Queue from %s dropped, rekey monotime in the past\n", + GNUNET_i2s (&queue->target)); + GNUNET_break (0); + queue_finish (queue); + return; + } + queue->rekey_monotime_sc = GNUNET_PEERSTORE_store (peerstore, + "transport_tcp_communicator", + pid, + GNUNET_PEERSTORE_TRANSPORT_TCP_COMMUNICATOR_REKEY, + rekey_monotonic_time, + sizeof(* + rekey_monotonic_time), + GNUNET_TIME_UNIT_FOREVER_ABS, + GNUNET_PEERSTORE_STOREOPTION_REPLACE, + &rekey_monotime_store_cb, + queue); +} + + +/** + * Setup cipher of @a queue for decryption. + * + * @param ephemeral ephemeral key we received from the other peer + * @param[in,out] queue queue to initialize decryption cipher for + */ +static void +setup_in_cipher (const struct GNUNET_CRYPTO_EcdhePublicKey *ephemeral, + struct Queue *queue) +{ + struct GNUNET_HashCode k; + + GNUNET_CRYPTO_eddsa_kem_decaps (my_private_key, ephemeral, &k); + setup_cipher (&k, &my_identity, &queue->in_cipher, &queue->in_hmac); +} + + +/** + * Handle @a rekey message on @a queue. The message was already + * HMAC'ed, but we should additionally still check the signature. + * Then we need to stop the old cipher and start afresh. + * + * @param queue the queue @a rekey was received on + * @param rekey the rekey message + */ +static void +do_rekey (struct Queue *queue, const struct TCPRekey *rekey) +{ + struct TcpRekeySignature thp; + + thp.purpose.purpose = htonl (GNUNET_SIGNATURE_PURPOSE_COMMUNICATOR_TCP_REKEY); + thp.purpose.size = htonl (sizeof(thp)); + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "do_rekey size %u\n", + thp.purpose.size); + thp.sender = queue->target; + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "sender %s\n", + GNUNET_p2s (&thp.sender.public_key)); + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "sender %s\n", + GNUNET_p2s (&queue->target.public_key)); + thp.receiver = my_identity; + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "receiver %s\n", + GNUNET_p2s (&thp.receiver.public_key)); + thp.ephemeral = rekey->ephemeral; + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "ephemeral %s\n", + GNUNET_e2s (&thp.ephemeral)); + thp.monotonic_time = rekey->monotonic_time; + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "time %s\n", + GNUNET_STRINGS_absolute_time_to_string ( + GNUNET_TIME_absolute_ntoh (thp.monotonic_time))); + GNUNET_assert (ntohl ((&thp)->purpose.size) == sizeof (*(&thp))); + if (GNUNET_OK != + GNUNET_CRYPTO_eddsa_verify ( + GNUNET_SIGNATURE_PURPOSE_COMMUNICATOR_TCP_REKEY, + &thp, + &rekey->sender_sig, + &queue->target.public_key)) + { + GNUNET_break (0); + queue_finish (queue); + return; + } + queue->rekey_monotonic_time = rekey->monotonic_time; + queue->rekey_monotime_get = GNUNET_PEERSTORE_iterate (peerstore, + "transport_tcp_communicator", + &queue->target, + GNUNET_PEERSTORE_TRANSPORT_TCP_COMMUNICATOR_REKEY, + &rekey_monotime_cb, + queue); + gcry_cipher_close (queue->in_cipher); + queue->rekeyed = GNUNET_YES; + setup_in_cipher (&rekey->ephemeral, queue); +} + + +/** + * Callback called when peerstore store operation for handshake ack monotime value is finished. + * @param cls Queue context the store operation was executed. + * @param success Store operation was successful (GNUNET_OK) or not. + */ +static void +handshake_ack_monotime_store_cb (void *cls, int success) +{ + struct Queue *queue = cls; + + if (GNUNET_OK != success) + { + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "Failed to store handshake ack monotonic time in PEERSTORE!\n"); + } + queue->handshake_ack_monotime_sc = NULL; +} + + +/** + * Callback called by peerstore when records for GNUNET_PEERSTORE_TRANSPORT_TCP_COMMUNICATOR_HANDSHAKE_ACK + * where found. + * @param cls Queue context the store operation was executed. + * @param record The record found or NULL if there is no record left. + * @param emsg Message from peerstore. + */ +static void +handshake_ack_monotime_cb (void *cls, + const struct GNUNET_PEERSTORE_Record *record, + const char *emsg) +{ + struct Queue *queue = cls; + struct GNUNET_TIME_AbsoluteNBO *mtbe; + struct GNUNET_TIME_Absolute mt; + const struct GNUNET_PeerIdentity *pid; + struct GNUNET_TIME_AbsoluteNBO *handshake_ack_monotonic_time; + + (void) emsg; + + handshake_ack_monotonic_time = &queue->handshake_ack_monotonic_time; + pid = &queue->target; + if (NULL == record) + { + queue->handshake_ack_monotime_get = NULL; + return; + } + if (sizeof(*mtbe) != record->value_size) + { + GNUNET_break (0); + return; + } + mtbe = record->value; + mt = GNUNET_TIME_absolute_ntoh (*mtbe); + if (mt.abs_value_us > GNUNET_TIME_absolute_ntoh ( + queue->handshake_ack_monotonic_time).abs_value_us) + { + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "Queue from %s dropped, handshake ack monotime in the past\n", + GNUNET_i2s (&queue->target)); + GNUNET_break (0); + queue_finish (queue); + return; + } + queue->handshake_ack_monotime_sc = + GNUNET_PEERSTORE_store (peerstore, + "transport_tcp_communicator", + pid, + GNUNET_PEERSTORE_TRANSPORT_TCP_COMMUNICATOR_HANDSHAKE_ACK, + handshake_ack_monotonic_time, + sizeof(*handshake_ack_monotonic_time), + GNUNET_TIME_UNIT_FOREVER_ABS, + GNUNET_PEERSTORE_STOREOPTION_REPLACE, + & + handshake_ack_monotime_store_cb, + queue); +} + + +/** + * Sending challenge with TcpConfirmationAck back to sender of ephemeral key. + * + * @param tc The TCPConfirmation originally send. + * @param queue The queue context. + */ +static void +send_challenge (struct GNUNET_CRYPTO_ChallengeNonceP challenge, + struct Queue *queue) +{ + struct TCPConfirmationAck tca; + struct TcpHandshakeAckSignature thas; + + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "sending challenge\n"); + + tca.header.type = ntohs ( + GNUNET_MESSAGE_TYPE_COMMUNICATOR_TCP_CONFIRMATION_ACK); + tca.header.size = ntohs (sizeof(tca)); + tca.challenge = challenge; + tca.sender = my_identity; + tca.monotonic_time = + GNUNET_TIME_absolute_hton (GNUNET_TIME_absolute_get_monotonic (cfg)); + thas.purpose.purpose = htonl ( + GNUNET_SIGNATURE_PURPOSE_COMMUNICATOR_TCP_HANDSHAKE_ACK); + thas.purpose.size = htonl (sizeof(thas)); + thas.sender = my_identity; + thas.receiver = queue->target; + thas.monotonic_time = tca.monotonic_time; + thas.challenge = tca.challenge; + GNUNET_CRYPTO_eddsa_sign (my_private_key, + &thas, + &tca.sender_sig); + GNUNET_assert (0 == + gcry_cipher_encrypt (queue->out_cipher, + &queue->cwrite_buf[queue->cwrite_off], + sizeof(tca), + &tca, + sizeof(tca))); + queue->cwrite_off += sizeof(tca); + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "sending challenge done\n"); +} + + +/** + * Setup cipher for outgoing data stream based on target and + * our ephemeral private key. + * + * @param queue queue to setup outgoing (encryption) cipher for + */ +static void +setup_out_cipher (struct Queue *queue, struct GNUNET_HashCode *dh) +{ + setup_cipher (dh, &queue->target, &queue->out_cipher, &queue->out_hmac); + queue->rekey_time = GNUNET_TIME_relative_to_absolute (rekey_interval); + queue->rekey_left_bytes = + GNUNET_CRYPTO_random_u64 (GNUNET_CRYPTO_QUALITY_WEAK, REKEY_MAX_BYTES); +} + + +/** + * Inject a `struct TCPRekey` message into the queue's plaintext + * buffer. + * + * @param queue queue to perform rekeying on + */ +static void +inject_rekey (struct Queue *queue) +{ + struct TCPRekey rekey; + struct TcpRekeySignature thp; + struct GNUNET_HashCode k; + + GNUNET_assert (0 == queue->pwrite_off); + memset (&rekey, 0, sizeof(rekey)); + GNUNET_CRYPTO_eddsa_kem_encaps (&queue->target.public_key, &rekey.ephemeral, + &k); + rekey.header.type = ntohs (GNUNET_MESSAGE_TYPE_COMMUNICATOR_TCP_REKEY); + rekey.header.size = ntohs (sizeof(rekey)); + rekey.monotonic_time = + GNUNET_TIME_absolute_hton (GNUNET_TIME_absolute_get_monotonic (cfg)); + thp.purpose.purpose = htonl (GNUNET_SIGNATURE_PURPOSE_COMMUNICATOR_TCP_REKEY); + thp.purpose.size = htonl (sizeof(thp)); + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "inject_rekey size %u\n", + thp.purpose.size); + thp.sender = my_identity; + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "sender %s\n", + GNUNET_p2s (&thp.sender.public_key)); + thp.receiver = queue->target; + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "receiver %s\n", + GNUNET_p2s (&thp.receiver.public_key)); + thp.ephemeral = rekey.ephemeral; + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "ephemeral %s\n", + GNUNET_e2s (&thp.ephemeral)); + thp.monotonic_time = rekey.monotonic_time; + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "time %s\n", + GNUNET_STRINGS_absolute_time_to_string ( + GNUNET_TIME_absolute_ntoh (thp.monotonic_time))); + GNUNET_CRYPTO_eddsa_sign (my_private_key, + &thp, + &rekey.sender_sig); + calculate_hmac (&queue->out_hmac, &rekey, sizeof(rekey), &rekey.hmac); + /* Encrypt rekey message with 'old' cipher */ + GNUNET_assert (0 == + gcry_cipher_encrypt (queue->out_cipher, + &queue->cwrite_buf[queue->cwrite_off], + sizeof(rekey), + &rekey, + sizeof(rekey))); + queue->cwrite_off += sizeof(rekey); + /* Setup new cipher for successive messages */ + gcry_cipher_close (queue->out_cipher); + setup_out_cipher (queue, &k); +} + + +static int +pending_reversals_delete_it (void *cls, + const struct GNUNET_HashCode *key, + void *value) +{ + (void) cls; + struct PendingReversal *pending_reversal = value; + + if (NULL != pending_reversal->timeout_task) + { + GNUNET_SCHEDULER_cancel (pending_reversal->timeout_task); + pending_reversal->timeout_task = NULL; + } + GNUNET_assert (GNUNET_YES == GNUNET_CONTAINER_multihashmap_remove ( + pending_reversals, + key, + pending_reversal)); + GNUNET_free (pending_reversal->in); + GNUNET_free (pending_reversal); + return GNUNET_OK; +} + + +static void +check_and_remove_pending_reversal (struct sockaddr *in, sa_family_t sa_family, + struct GNUNET_PeerIdentity *sender) +{ + if (AF_INET == sa_family) + { + struct PendingReversal *pending_reversal; + struct GNUNET_HashCode key; + struct sockaddr_in *natted_address; + + natted_address = GNUNET_memdup (in, sizeof (struct sockaddr)); + natted_address->sin_port = 0; + GNUNET_CRYPTO_hash (natted_address, + sizeof(struct sockaddr), + &key); + + pending_reversal = GNUNET_CONTAINER_multihashmap_get (pending_reversals, + &key); + if (NULL != pending_reversal && (NULL == sender || + 0 != memcmp (sender, + &pending_reversal->target, + sizeof(struct + GNUNET_PeerIdentity)))) + { + GNUNET_log (GNUNET_ERROR_TYPE_WARNING, + "Removing invalid pending reversal for `%s'at `%s'\n", + GNUNET_i2s (&pending_reversal->target), + GNUNET_a2s (in, sizeof (struct sockaddr))); + pending_reversals_delete_it (NULL, &key, pending_reversal); + } + GNUNET_free (natted_address); + } +} + + +/** + * Closes socket and frees memory associated with @a pq. + * + * @param pq proto queue to free + */ +static void +free_proto_queue (struct ProtoQueue *pq) +{ + if (NULL != pq->listen_sock) + { + GNUNET_break (GNUNET_OK == GNUNET_NETWORK_socket_close (pq->listen_sock)); + pq->listen_sock = NULL; + } + if (NULL != pq->read_task) + { + GNUNET_SCHEDULER_cancel (pq->read_task); + pq->read_task = NULL; + } + if (NULL != pq->write_task) + { + GNUNET_SCHEDULER_cancel (pq->write_task); + pq->write_task = NULL; + } + check_and_remove_pending_reversal (pq->address, pq->address->sa_family, NULL); + GNUNET_NETWORK_socket_close (pq->sock); + GNUNET_free (pq->address); + GNUNET_CONTAINER_DLL_remove (proto_head, proto_tail, pq); + GNUNET_free (pq); +} + + +/** + * We have been notified that our socket is ready to write. + * Then reschedule this function to be called again once more is available. + * + * @param cls a `struct ProtoQueue` + */ +static void +proto_queue_write (void *cls) +{ + struct ProtoQueue *pq = cls; + ssize_t sent; + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "In proto queue write\n"); + pq->write_task = NULL; + if (0 != pq->write_off) + { + sent = GNUNET_NETWORK_socket_send (pq->sock, + pq->write_buf, + pq->write_off); + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Sent %lu bytes to TCP queue\n", sent); + if ((-1 == sent) && (EAGAIN != errno) && (EINTR != errno)) + { + GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING, "send"); + free_proto_queue (pq); + return; + } + if (sent > 0) + { + size_t usent = (size_t) sent; + pq->write_off -= usent; + memmove (pq->write_buf, + &pq->write_buf[usent], + pq->write_off); + } + } + /* do we care to write more? */ + if ((0 < pq->write_off)) + pq->write_task = + GNUNET_SCHEDULER_add_write_net (GNUNET_TIME_UNIT_FOREVER_REL, + pq->sock, + &proto_queue_write, + pq); +} + + +/** + * We have been notified that our socket is ready to write. + * Then reschedule this function to be called again once more is available. + * + * @param cls a `struct Queue` + */ +static void +queue_write (void *cls) +{ + struct Queue *queue = cls; + ssize_t sent; + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "In queue write\n"); + queue->write_task = NULL; + if (0 != queue->cwrite_off) + { + sent = GNUNET_NETWORK_socket_send (queue->sock, + queue->cwrite_buf, + queue->cwrite_off); + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Sent %lu bytes to TCP queue\n", sent); + if ((-1 == sent) && (EAGAIN != errno) && (EINTR != errno)) + { + GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING, "send"); + queue_destroy (queue); + return; + } + if (sent > 0) + { + size_t usent = (size_t) sent; + queue->cwrite_off -= usent; + memmove (queue->cwrite_buf, + &queue->cwrite_buf[usent], + queue->cwrite_off); + reschedule_queue_timeout (queue); + } + } + /* can we encrypt more? (always encrypt full messages, needed + such that #mq_cancel() can work!) */ + unsigned int we_do_not_need_to_rekey = (0 < queue->rekey_left_bytes + - (queue->cwrite_off + + queue->pwrite_off + + sizeof (struct TCPRekey))); + if (we_do_not_need_to_rekey && + (queue->pwrite_off > 0) && + (queue->cwrite_off + queue->pwrite_off <= BUF_SIZE)) + { + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Encrypting %lu bytes\n", queue->pwrite_off); + GNUNET_assert (0 == + gcry_cipher_encrypt (queue->out_cipher, + &queue->cwrite_buf[queue->cwrite_off], + queue->pwrite_off, + queue->pwrite_buf, + queue->pwrite_off)); + if (queue->rekey_left_bytes > queue->pwrite_off) + queue->rekey_left_bytes -= queue->pwrite_off; + else + queue->rekey_left_bytes = 0; + queue->cwrite_off += queue->pwrite_off; + queue->pwrite_off = 0; + } + // if ((-1 != unverified_size)&& ((0 == queue->pwrite_off) && + if (((0 == queue->rekey_left_bytes) || + (0 == GNUNET_TIME_absolute_get_remaining ( + queue->rekey_time).rel_value_us)) && + (((0 == queue->pwrite_off) || ! we_do_not_need_to_rekey) && + (queue->cwrite_off + sizeof (struct TCPRekey) <= BUF_SIZE))) + { + inject_rekey (queue); + } + if ((0 == queue->pwrite_off) && (! queue->finishing) && + (GNUNET_YES == queue->mq_awaits_continue)) + { + queue->mq_awaits_continue = GNUNET_NO; + GNUNET_MQ_impl_send_continue (queue->mq); + } + /* did we just finish writing 'finish'? */ + if ((0 == queue->cwrite_off) && (GNUNET_YES == queue->finishing)) + { + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Finishing queue\n"); + queue_destroy (queue); + return; + } + /* do we care to write more? */ + if ((0 < queue->cwrite_off) || (0 < queue->pwrite_off)) + queue->write_task = + GNUNET_SCHEDULER_add_write_net (GNUNET_TIME_UNIT_FOREVER_REL, + queue->sock, + &queue_write, + queue); +} + + +/** + * Test if we have received a full message in plaintext. + * If so, handle it. + * + * @param queue queue to process inbound plaintext for + * @return number of bytes of plaintext handled, 0 for none + */ +static size_t +try_handle_plaintext (struct Queue *queue) +{ + const struct GNUNET_MessageHeader *hdr; + const struct TCPConfirmationAck *tca; + const struct TCPBox *box; + const struct TCPRekey *rekey; + const struct TCPFinish *fin; + struct TCPRekey rekeyz; + struct TCPFinish finz; + struct GNUNET_ShortHashCode tmac; + uint16_t type; + size_t size = 0; + struct TcpHandshakeAckSignature thas; + const struct GNUNET_CRYPTO_ChallengeNonceP challenge = queue->challenge; + + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "try handle plaintext!\n"); + + hdr = (const struct GNUNET_MessageHeader *) queue->pread_buf; + if ((sizeof(*hdr) > queue->pread_off)) + { + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Handling plaintext, not even a header!\n"); + return 0; /* not even a header */ + } + + if ((GNUNET_YES != queue->initial_core_kx_done) && (queue->unverified_size > + INITIAL_CORE_KX_SIZE)) + { + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "Already received data of size %lu bigger than KX size %lu!\n", + queue->unverified_size, + INITIAL_CORE_KX_SIZE); + GNUNET_break_op (0); + queue_finish (queue); + return 0; + } + + type = ntohs (hdr->type); + switch (type) + { + case GNUNET_MESSAGE_TYPE_COMMUNICATOR_TCP_CONFIRMATION_ACK: + tca = (const struct TCPConfirmationAck *) queue->pread_buf; + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "start processing ack\n"); + if (sizeof(*tca) > queue->pread_off) + { + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Handling plaintext size of tca greater than pread offset.\n"); + return 0; + } + if (ntohs (hdr->size) != sizeof(*tca)) + { + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Handling plaintext size does not match message type.\n"); + GNUNET_break_op (0); + queue_finish (queue); + return 0; + } + + thas.purpose.purpose = htonl ( + GNUNET_SIGNATURE_PURPOSE_COMMUNICATOR_TCP_HANDSHAKE_ACK); + thas.purpose.size = htonl (sizeof(thas)); + thas.sender = tca->sender; + thas.receiver = my_identity; + thas.monotonic_time = tca->monotonic_time; + thas.challenge = tca->challenge; + + if (GNUNET_SYSERR == GNUNET_CRYPTO_eddsa_verify ( + GNUNET_SIGNATURE_PURPOSE_COMMUNICATOR_TCP_HANDSHAKE_ACK, + &thas, + &tca->sender_sig, + &tca->sender.public_key)) + { + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "Verification of signature failed!\n"); + GNUNET_break (0); + queue_finish (queue); + return 0; + } + if (0 != GNUNET_memcmp (&tca->challenge, &challenge)) + { + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "Challenge in TCPConfirmationAck not correct!\n"); + GNUNET_break (0); + queue_finish (queue); + return 0; + } + + queue->handshake_ack_monotime_get = GNUNET_PEERSTORE_iterate (peerstore, + "transport_tcp_communicator", + &queue->target, + GNUNET_PEERSTORE_TRANSPORT_TCP_COMMUNICATOR_HANDSHAKE_ACK, + & + handshake_ack_monotime_cb, + queue); + + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Handling plaintext, ack processed!\n"); + + if (GNUNET_TRANSPORT_CS_INBOUND == queue->cs) + { + send_challenge (queue->challenge_received, queue); + queue->write_task = + GNUNET_SCHEDULER_add_write_net (GNUNET_TIME_UNIT_FOREVER_REL, + queue->sock, + &queue_write, + queue); + } + else if (GNUNET_TRANSPORT_CS_OUTBOUND == queue->cs) + { + check_and_remove_pending_reversal (queue->address, + queue->address->sa_family, NULL); + } + + /** + * Once we received this ack, we consider this a verified connection. + * FIXME: I am not sure this logic is sane here. + */ + queue->initial_core_kx_done = GNUNET_YES; + + char *foreign_addr; + + switch (queue->address->sa_family) + { + case AF_INET: + GNUNET_asprintf (&foreign_addr, + "%s-%s", + COMMUNICATOR_ADDRESS_PREFIX, + GNUNET_a2s (queue->address, queue->address_len)); + break; + + case AF_INET6: + GNUNET_asprintf (&foreign_addr, + "%s-%s", + COMMUNICATOR_ADDRESS_PREFIX, + GNUNET_a2s (queue->address, queue->address_len)); + break; + + default: + GNUNET_assert (0); + } + + queue->qh = GNUNET_TRANSPORT_communicator_mq_add (ch, + &queue->target, + foreign_addr, + UINT16_MAX, /* no MTU */ + GNUNET_TRANSPORT_QUEUE_LENGTH_UNLIMITED, + 0, /* Priority */ + queue->nt, + queue->cs, + queue->mq); + + GNUNET_free (foreign_addr); + + size = ntohs (hdr->size); + break; + case GNUNET_MESSAGE_TYPE_COMMUNICATOR_TCP_BOX: + /* Special case: header size excludes box itself! */ + box = (const struct TCPBox *) queue->pread_buf; + if (ntohs (hdr->size) + sizeof(struct TCPBox) > queue->pread_off) + return 0; + calculate_hmac (&queue->in_hmac, &box[1], ntohs (hdr->size), &tmac); + if (0 != memcmp (&tmac, &box->hmac, sizeof(tmac))) + { + GNUNET_break_op (0); + queue_finish (queue); + return 0; + } + pass_plaintext_to_core (queue, (const void *) &box[1], ntohs (hdr->size)); + size = ntohs (hdr->size) + sizeof(*box); + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Handling plaintext, box processed!\n"); + break; + + case GNUNET_MESSAGE_TYPE_COMMUNICATOR_TCP_REKEY: + rekey = (const struct TCPRekey *) queue->pread_buf; + if (sizeof(*rekey) > queue->pread_off) + return 0; + if (ntohs (hdr->size) != sizeof(*rekey)) + { + GNUNET_break_op (0); + queue_finish (queue); + return 0; + } + rekeyz = *rekey; + memset (&rekeyz.hmac, 0, sizeof(rekeyz.hmac)); + calculate_hmac (&queue->in_hmac, &rekeyz, sizeof(rekeyz), &tmac); + if (0 != memcmp (&tmac, &rekey->hmac, sizeof(tmac))) + { + GNUNET_break_op (0); + queue_finish (queue); + return 0; + } + do_rekey (queue, rekey); + size = ntohs (hdr->size); + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Handling plaintext, rekey processed!\n"); + break; + + case GNUNET_MESSAGE_TYPE_COMMUNICATOR_TCP_FINISH: + fin = (const struct TCPFinish *) queue->pread_buf; + if (sizeof(*fin) > queue->pread_off) + return 0; + if (ntohs (hdr->size) != sizeof(*fin)) + { + GNUNET_break_op (0); + queue_finish (queue); + return 0; + } + finz = *fin; + memset (&finz.hmac, 0, sizeof(finz.hmac)); + calculate_hmac (&queue->in_hmac, &rekeyz, sizeof(rekeyz), &tmac); + if (0 != memcmp (&tmac, &fin->hmac, sizeof(tmac))) + { + GNUNET_break_op (0); + queue_finish (queue); + return 0; + } + /* handle FINISH by destroying queue */ + queue_destroy (queue); + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Handling plaintext, finish processed!\n"); + break; + + default: + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Handling plaintext, nothing processed!\n"); + GNUNET_break_op (0); + queue_finish (queue); + return 0; + } + GNUNET_assert (0 != size); + if (-1 != queue->unverified_size) + queue->unverified_size += size; + return size; +} + + +/** + * Queue read task. If we hit the timeout, disconnect it + * + * @param cls the `struct Queue *` to disconnect + */ +static void +queue_read (void *cls) +{ + struct Queue *queue = cls; + struct GNUNET_TIME_Relative left; + ssize_t rcvd; + + queue->read_task = NULL; + rcvd = GNUNET_NETWORK_socket_recv (queue->sock, + &queue->cread_buf[queue->cread_off], + BUF_SIZE - queue->cread_off); + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Received %zd bytes from TCP queue\n", rcvd); + if (-1 == rcvd) + { + if ((EAGAIN != errno) && (EINTR != errno)) + { + GNUNET_log_strerror (GNUNET_ERROR_TYPE_DEBUG, "recv"); + queue_finish (queue); + return; + } + /* try again */ + left = GNUNET_TIME_absolute_get_remaining (queue->timeout); + queue->read_task = + GNUNET_SCHEDULER_add_read_net (left, queue->sock, &queue_read, queue); + return; + } + if (0 != rcvd) + reschedule_queue_timeout (queue); + queue->cread_off += rcvd; + while ((queue->pread_off < sizeof(queue->pread_buf)) && + (queue->cread_off > 0)) + { + size_t max = GNUNET_MIN (sizeof(queue->pread_buf) - queue->pread_off, + queue->cread_off); + size_t done; + size_t total; + size_t old_pread_off = queue->pread_off; + + GNUNET_assert (0 == + gcry_cipher_decrypt (queue->in_cipher, + &queue->pread_buf[queue->pread_off], + max, + queue->cread_buf, + max)); + queue->pread_off += max; + total = 0; + while (0 != (done = try_handle_plaintext (queue))) + { + /* 'done' bytes of plaintext were used, shift buffer */ + GNUNET_assert (done <= queue->pread_off); + /* NOTE: this memmove() could possibly sometimes be + avoided if we pass 'total' into try_handle_plaintext() + and use it at an offset into the buffer there! */ + memmove (queue->pread_buf, + &queue->pread_buf[done], + queue->pread_off - done); + queue->pread_off -= done; + total += done; + /* The last plaintext was a rekey, abort for now */ + if (GNUNET_YES == queue->rekeyed) + break; + } + /* when we encounter a rekey message, the decryption above uses the + wrong key for everything after the rekey; in that case, we have + to re-do the decryption at 'total' instead of at 'max'. + However, we have to take into account that the plaintext buffer may have + already contained data and not jumped too far ahead in the ciphertext. + If there is no rekey and the last message is incomplete (max > total), + it is safe to keep the decryption so we shift by 'max' */ + if (GNUNET_YES == queue->rekeyed) + { + max = total - old_pread_off; + queue->rekeyed = GNUNET_NO; + queue->pread_off = 0; + } + memmove (queue->cread_buf, &queue->cread_buf[max], queue->cread_off - max); + queue->cread_off -= max; + } + if (BUF_SIZE == queue->cread_off) + return; /* buffer full, suspend reading */ + left = GNUNET_TIME_absolute_get_remaining (queue->timeout); + if (0 != left.rel_value_us) + { + if (max_queue_length > queue->backpressure) + { + /* continue reading */ + left = GNUNET_TIME_absolute_get_remaining (queue->timeout); + queue->read_task = + GNUNET_SCHEDULER_add_read_net (left, queue->sock, &queue_read, queue); + } + return; + } + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Queue %p was idle for %s, disconnecting\n", + queue, + GNUNET_STRINGS_relative_time_to_string ( + GNUNET_CONSTANTS_IDLE_CONNECTION_TIMEOUT, + GNUNET_YES)); + queue_finish (queue); +} + + +/** + * Convert a `struct sockaddr_in6 to a `struct sockaddr *` + * + * @param[out] sock_len set to the length of the address. + * @param v6 The sockaddr_in6 to be converted. + * @return The struct sockaddr *. + */ +static struct sockaddr * +tcp_address_to_sockaddr_numeric_v6 (socklen_t *sock_len, + struct sockaddr_in6 v6, + unsigned int port) +{ + struct sockaddr *in; + + v6.sin6_family = AF_INET6; + v6.sin6_port = htons ((uint16_t) port); +#if HAVE_SOCKADDR_IN_SIN_LEN + v6.sin6_len = sizeof(sizeof(struct sockaddr_in6)); +#endif + v6.sin6_flowinfo = 0; + v6.sin6_scope_id = 0; + in = GNUNET_memdup (&v6, sizeof(v6)); + *sock_len = sizeof(struct sockaddr_in6); + + return in; +} + + +/** + * Convert a `struct sockaddr_in4 to a `struct sockaddr *` + * + * @param[out] sock_len set to the length of the address. + * @param v4 The sockaddr_in4 to be converted. + * @return The struct sockaddr *. + */ +static struct sockaddr * +tcp_address_to_sockaddr_numeric_v4 (socklen_t *sock_len, + struct sockaddr_in v4, + unsigned int port) +{ + struct sockaddr *in; + + v4.sin_family = AF_INET; + v4.sin_port = htons ((uint16_t) port); +#if HAVE_SOCKADDR_IN_SIN_LEN + v4.sin_len = sizeof(struct sockaddr_in); +#endif + in = GNUNET_memdup (&v4, sizeof(v4)); + *sock_len = sizeof(struct sockaddr_in); + return in; +} + + +/** + * Convert TCP bind specification to a `struct PortOnlyIpv4Ipv6 *` + * + * @param bindto bind specification to convert. + * @return The converted bindto specification. + */ +static struct PortOnlyIpv4Ipv6 * +tcp_address_to_sockaddr_port_only (const char *bindto, unsigned int *port) +{ + struct PortOnlyIpv4Ipv6 *po; + struct sockaddr_in *i4; + struct sockaddr_in6 *i6; + socklen_t sock_len_ipv4; + socklen_t sock_len_ipv6; + + /* interpreting value as just a PORT number */ + if (*port > UINT16_MAX) + { + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "BINDTO specification `%s' invalid: value too large for port\n", + bindto); + return NULL; + } + + po = GNUNET_new (struct PortOnlyIpv4Ipv6); + + if ((GNUNET_NO == GNUNET_NETWORK_test_pf (PF_INET6)) || + (GNUNET_YES == + GNUNET_CONFIGURATION_get_value_yesno (cfg, + COMMUNICATOR_CONFIG_SECTION, + "DISABLE_V6"))) + { + i4 = GNUNET_malloc (sizeof(struct sockaddr_in)); + po->addr_ipv4 = tcp_address_to_sockaddr_numeric_v4 (&sock_len_ipv4, *i4, + *port); + po->addr_len_ipv4 = sock_len_ipv4; + } + else + { + + i4 = GNUNET_malloc (sizeof(struct sockaddr_in)); + po->addr_ipv4 = tcp_address_to_sockaddr_numeric_v4 (&sock_len_ipv4, *i4, + *port); + po->addr_len_ipv4 = sock_len_ipv4; + + i6 = GNUNET_malloc (sizeof(struct sockaddr_in6)); + po->addr_ipv6 = tcp_address_to_sockaddr_numeric_v6 (&sock_len_ipv6, *i6, + *port); + + po->addr_len_ipv6 = sock_len_ipv6; + + GNUNET_free (i6); + } + + GNUNET_free (i4); + + return po; +} + + +/** + * This Method extracts the address part of the BINDTO string. + * + * @param bindto String we extract the address part from. + * @return The extracted address string. + */ +static char * +extract_address (const char *bindto) +{ + char *addr; + char *start; + char *token; + char *cp; + char *rest = NULL; + char *res; + + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "extract address with bindto %s\n", + bindto); + + if (NULL == bindto) + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "bindto is NULL\n"); + + cp = GNUNET_strdup (bindto); + + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "extract address 2\n"); + + start = cp; + if (('[' == *cp) && (']' == cp[strlen (cp) - 1])) + { + start++; /* skip over '['*/ + cp[strlen (cp) - 1] = '\0'; /* eat ']'*/ + addr = GNUNET_strdup (start); + } + else + { + token = strtok_r (cp, "]", &rest); + if (strlen (bindto) == strlen (token)) + { + token = strtok_r (cp, ":", &rest); + addr = GNUNET_strdup (token); + } + else + { + token++; + res = GNUNET_strdup (token); + addr = GNUNET_strdup (res); + } + } + + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "tcp address: %s\n", + addr); + GNUNET_free (cp); + return addr; +} + + +/** + * This Method extracts the port part of the BINDTO string. + * + * @param addr_and_port String we extract the port from. + * @return The extracted port as unsigned int. + */ +static unsigned int +extract_port (const char *addr_and_port) +{ + unsigned int port; + char dummy[2]; + char *token; + char *addr; + char *colon; + char *cp; + char *rest = NULL; + + if (NULL != addr_and_port) + { + cp = GNUNET_strdup (addr_and_port); + token = strtok_r (cp, "]", &rest); + if (strlen (addr_and_port) == strlen (token)) + { + colon = strrchr (cp, ':'); + if (NULL == colon) + { + GNUNET_free (cp); + return 0; + } + addr = colon; + addr++; + } + else + { + token = strtok_r (NULL, "]", &rest); + if (NULL == token) + { + GNUNET_free (cp); + return 0; + } + else + { + addr = token; + addr++; + } + } + + + if (1 == sscanf (addr, "%u%1s", &port, dummy)) + { + /* interpreting value as just a PORT number */ + if (port > UINT16_MAX) + { + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "Port `%u' invalid: value too large for port\n", + port); + GNUNET_free (cp); + return 0; + } + } + else + { + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "BINDTO specification invalid: last ':' not followed by number\n"); + GNUNET_free (cp); + return 0; + } + GNUNET_free (cp); + } + else + { + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "return 0\n"); + /* interpret missing port as 0, aka pick any free one */ + port = 0; + } + + return port; +} + + +/** + * Convert TCP bind specification to a `struct sockaddr *` + * + * @param bindto bind specification to convert + * @param[out] sock_len set to the length of the address + * @return converted bindto specification + */ +static struct sockaddr * +tcp_address_to_sockaddr (const char *bindto, socklen_t *sock_len) +{ + struct sockaddr *in; + unsigned int port; + struct sockaddr_in v4; + struct sockaddr_in6 v6; + char *start; + + memset (&v4, 0, sizeof(v4)); + start = extract_address (bindto); + GNUNET_assert (NULL != start); + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "start %s\n", + start); + + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "!bindto %s\n", + bindto); + + + if (1 == inet_pton (AF_INET, start, &v4.sin_addr)) + { + // colon = strrchr (cp, ':'); + port = extract_port (bindto); + + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "port %u\n", + port); + + in = tcp_address_to_sockaddr_numeric_v4 (sock_len, v4, port); + } + else if (1 == inet_pton (AF_INET6, start, &v6.sin6_addr)) + { + // colon = strrchr (cp, ':'); + port = extract_port (bindto); + in = tcp_address_to_sockaddr_numeric_v6 (sock_len, v6, port); + } + else + { + GNUNET_assert (0); + } + + GNUNET_free (start); + return in; +} + + +/** + * Signature of functions implementing the sending functionality of a + * message queue. + * + * @param mq the message queue + * @param msg the message to send + * @param impl_state our `struct Queue` + */ +static void +mq_send (struct GNUNET_MQ_Handle *mq, + const struct GNUNET_MessageHeader *msg, + void *impl_state) +{ + struct Queue *queue = impl_state; + uint16_t msize = ntohs (msg->size); + struct TCPBox box; + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "In MQ send. Queue finishing: %s; write task running: %s\n", + (GNUNET_YES == queue->finishing) ? "yes" : "no", + (NULL == queue->write_task) ? "yes" : "no"); + GNUNET_assert (mq == queue->mq); + queue->mq_awaits_continue = GNUNET_YES; + if (GNUNET_YES == queue->finishing) + return; /* this queue is dying, drop msg */ + GNUNET_assert (0 == queue->pwrite_off); + box.header.type = htons (GNUNET_MESSAGE_TYPE_COMMUNICATOR_TCP_BOX); + box.header.size = htons (msize); + calculate_hmac (&queue->out_hmac, msg, msize, &box.hmac); + memcpy (&queue->pwrite_buf[queue->pwrite_off], &box, sizeof(box)); + queue->pwrite_off += sizeof(box); + memcpy (&queue->pwrite_buf[queue->pwrite_off], msg, msize); + queue->pwrite_off += msize; + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "%lu bytes of plaintext to send\n", queue->pwrite_off); + GNUNET_assert (NULL != queue->sock); + if (NULL == queue->write_task) + queue->write_task = + GNUNET_SCHEDULER_add_write_net (GNUNET_TIME_UNIT_FOREVER_REL, + queue->sock, + &queue_write, + queue); +} + + +/** + * Signature of functions implementing the destruction of a message + * queue. Implementations must not free @a mq, but should take care + * of @a impl_state. + * + * @param mq the message queue to destroy + * @param impl_state our `struct Queue` + */ +static void +mq_destroy (struct GNUNET_MQ_Handle *mq, void *impl_state) +{ + struct Queue *queue = impl_state; + + if (mq == queue->mq) + { + queue->mq = NULL; + queue_finish (queue); + } +} + + +/** + * Implementation function that cancels the currently sent message. + * + * @param mq message queue + * @param impl_state our `struct Queue` + */ +static void +mq_cancel (struct GNUNET_MQ_Handle *mq, void *impl_state) +{ + struct Queue *queue = impl_state; + + GNUNET_assert (0 != queue->pwrite_off); + queue->pwrite_off = 0; +} + + +/** + * Generic error handler, called with the appropriate + * error code and the same closure specified at the creation of + * the message queue. + * Not every message queue implementation supports an error handler. + * + * @param cls our `struct Queue` + * @param error error code + */ +static void +mq_error (void *cls, enum GNUNET_MQ_Error error) +{ + struct Queue *queue = cls; + + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "MQ error in queue to %s: %d\n", + GNUNET_i2s (&queue->target), + (int) error); + queue_finish (queue); +} + + +/** + * Add the given @a queue to our internal data structure. Setup the + * MQ processing and inform transport that the queue is ready. Must + * be called after the KX for outgoing messages has been bootstrapped. + * + * @param queue queue to boot + */ +static void +boot_queue (struct Queue *queue) +{ + queue->nt = + GNUNET_NT_scanner_get_type (is, queue->address, queue->address_len); + (void) GNUNET_CONTAINER_multipeermap_put ( + queue_map, + &queue->target, + queue, + GNUNET_CONTAINER_MULTIHASHMAPOPTION_MULTIPLE); + GNUNET_STATISTICS_set (stats, + "# queues active", + GNUNET_CONTAINER_multipeermap_size (queue_map), + GNUNET_NO); + queue->timeout = + GNUNET_TIME_relative_to_absolute (GNUNET_CONSTANTS_IDLE_CONNECTION_TIMEOUT); + queue->mq = GNUNET_MQ_queue_for_callbacks (&mq_send, + &mq_destroy, + &mq_cancel, + queue, + NULL, + &mq_error, + queue); +} + + +/** + * Generate and transmit our ephemeral key and the signature for + * the initial KX with the other peer. Must be called first, before + * any other bytes are ever written to the output buffer. Note that + * our cipher must already be initialized when calling this function. + * Helper function for #start_initial_kx_out(). + * + * @param queue queue to do KX for + * @param epub our public key for the KX + */ +static void +transmit_kx (struct Queue *queue, + const struct GNUNET_CRYPTO_EcdhePublicKey *epub) +{ + struct TcpHandshakeSignature ths; + struct TCPConfirmation tc; + + memcpy (queue->cwrite_buf, epub, sizeof(*epub)); + queue->cwrite_off = sizeof(*epub); + /* compute 'tc' and append in encrypted format to cwrite_buf */ + tc.sender = my_identity; + tc.monotonic_time = + GNUNET_TIME_absolute_hton (GNUNET_TIME_absolute_get_monotonic (cfg)); + GNUNET_CRYPTO_random_block (GNUNET_CRYPTO_QUALITY_NONCE, + &tc.challenge, + sizeof(tc.challenge)); + ths.purpose.purpose = htonl ( + GNUNET_SIGNATURE_PURPOSE_COMMUNICATOR_TCP_HANDSHAKE); + ths.purpose.size = htonl (sizeof(ths)); + ths.sender = my_identity; + ths.receiver = queue->target; + ths.ephemeral = *epub; + ths.monotonic_time = tc.monotonic_time; + ths.challenge = tc.challenge; + GNUNET_CRYPTO_eddsa_sign (my_private_key, + &ths, + &tc.sender_sig); + GNUNET_assert (0 == + gcry_cipher_encrypt (queue->out_cipher, + &queue->cwrite_buf[queue->cwrite_off], + sizeof(tc), + &tc, + sizeof(tc))); + queue->challenge = tc.challenge; + queue->cwrite_off += sizeof(tc); + + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "handshake written\n"); +} + + +/** + * Initialize our key material for outgoing transmissions and + * inform the other peer about it. Must be called first before + * any data is sent. + * + * @param queue the queue to setup + */ +static void +start_initial_kx_out (struct Queue *queue) +{ + struct GNUNET_CRYPTO_EcdhePublicKey epub; + struct GNUNET_HashCode k; + + GNUNET_CRYPTO_eddsa_kem_encaps (&queue->target.public_key, &epub, &k); + setup_out_cipher (queue, &k); + transmit_kx (queue, &epub); +} + + +/** + * Callback called when peerstore store operation for handshake monotime is finished. + * @param cls Queue context the store operation was executed. + * @param success Store operation was successful (GNUNET_OK) or not. + */ +static void +handshake_monotime_store_cb (void *cls, int success) +{ + struct Queue *queue = cls; + if (GNUNET_OK != success) + { + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "Failed to store handshake monotonic time in PEERSTORE!\n"); + } + queue->handshake_monotime_sc = NULL; +} + + +/** + * Callback called by peerstore when records for GNUNET_PEERSTORE_TRANSPORT_TCP_COMMUNICATOR_HANDSHAKE + * where found. + * @param cls Queue context the store operation was executed. + * @param record The record found or NULL if there is no record left. + * @param emsg Message from peerstore. + */ +static void +handshake_monotime_cb (void *cls, + const struct GNUNET_PEERSTORE_Record *record, + const char *emsg) +{ + struct Queue *queue = cls; + struct GNUNET_TIME_AbsoluteNBO *mtbe; + struct GNUNET_TIME_Absolute mt; + const struct GNUNET_PeerIdentity *pid; + struct GNUNET_TIME_AbsoluteNBO *handshake_monotonic_time; + + (void) emsg; + + handshake_monotonic_time = &queue->handshake_monotonic_time; + pid = &queue->target; + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "tcp handshake with us %s\n", + GNUNET_i2s (&my_identity)); + if (NULL == record) + { + queue->handshake_monotime_get = NULL; + return; + } + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "tcp handshake from peer %s\n", + GNUNET_i2s (pid)); + if (sizeof(*mtbe) != record->value_size) + { + GNUNET_break (0); + return; + } + mtbe = record->value; + mt = GNUNET_TIME_absolute_ntoh (*mtbe); + if (mt.abs_value_us > GNUNET_TIME_absolute_ntoh ( + queue->handshake_monotonic_time).abs_value_us) + { + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "Queue from %s dropped, handshake monotime in the past\n", + GNUNET_i2s (&queue->target)); + GNUNET_break (0); + queue_finish (queue); + return; + } + queue->handshake_monotime_sc = GNUNET_PEERSTORE_store (peerstore, + "transport_tcp_communicator", + pid, + GNUNET_PEERSTORE_TRANSPORT_TCP_COMMUNICATOR_HANDSHAKE, + handshake_monotonic_time, + sizeof(* + handshake_monotonic_time), + GNUNET_TIME_UNIT_FOREVER_ABS, + GNUNET_PEERSTORE_STOREOPTION_REPLACE, + & + handshake_monotime_store_cb, + queue); +} + + +/** + * We have received the first bytes from the other side on a @a queue. + * Decrypt the @a tc contained in @a ibuf and check the signature. + * Note that #setup_in_cipher() must have already been called. + * + * @param queue queue to decrypt initial bytes from other peer for + * @param[out] tc where to store the result + * @param ibuf incoming data, of size + * `INITIAL_KX_SIZE` + * @return #GNUNET_OK if the signature was OK, #GNUNET_SYSERR if not + */ +static int +decrypt_and_check_tc (struct Queue *queue, + struct TCPConfirmation *tc, + char *ibuf) +{ + struct TcpHandshakeSignature ths; + enum GNUNET_GenericReturnValue ret; + + GNUNET_assert ( + 0 == + gcry_cipher_decrypt (queue->in_cipher, + tc, + sizeof(*tc), + &ibuf[sizeof(struct GNUNET_CRYPTO_EcdhePublicKey)], + sizeof(*tc))); + ths.purpose.purpose = htonl ( + GNUNET_SIGNATURE_PURPOSE_COMMUNICATOR_TCP_HANDSHAKE); + ths.purpose.size = htonl (sizeof(ths)); + ths.sender = tc->sender; + ths.receiver = my_identity; + memcpy (&ths.ephemeral, ibuf, sizeof(struct GNUNET_CRYPTO_EcdhePublicKey)); + ths.monotonic_time = tc->monotonic_time; + ths.challenge = tc->challenge; + ret = GNUNET_CRYPTO_eddsa_verify ( + GNUNET_SIGNATURE_PURPOSE_COMMUNICATOR_TCP_HANDSHAKE, + &ths, + &tc->sender_sig, + &tc->sender.public_key); + if (GNUNET_YES == ret) + queue->handshake_monotime_get = + GNUNET_PEERSTORE_iterate (peerstore, + "transport_tcp_communicator", + &queue->target, + GNUNET_PEERSTORE_TRANSPORT_TCP_COMMUNICATOR_HANDSHAKE, + &handshake_monotime_cb, + queue); + return ret; +} + + +/** + * Read from the socket of the queue until we have enough data + * to initialize the decryption logic and can switch to regular + * reading. + * + * @param cls a `struct Queue` + */ +static void +queue_read_kx (void *cls) +{ + struct Queue *queue = cls; + ssize_t rcvd; + struct GNUNET_TIME_Relative left; + struct TCPConfirmation tc; + + queue->read_task = NULL; + left = GNUNET_TIME_absolute_get_remaining (queue->timeout); + if (0 == left.rel_value_us) + { + queue_destroy (queue); + return; + } + rcvd = GNUNET_NETWORK_socket_recv (queue->sock, + &queue->cread_buf[queue->cread_off], + BUF_SIZE - queue->cread_off); + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Received %lu bytes for KX\n", + rcvd); + if (-1 == rcvd) + { + if ((EAGAIN != errno) && (EINTR != errno)) + { + GNUNET_log_strerror (GNUNET_ERROR_TYPE_DEBUG, "recv"); + queue_destroy (queue); + return; + } + queue->read_task = + GNUNET_SCHEDULER_add_read_net (left, queue->sock, &queue_read_kx, queue); + return; + } + queue->cread_off += rcvd; + if (queue->cread_off < INITIAL_KX_SIZE) + { + /* read more */ + queue->read_task = + GNUNET_SCHEDULER_add_read_net (left, queue->sock, &queue_read_kx, queue); + return; + } + /* we got all the data, let's find out who we are talking to! */ + setup_in_cipher ((const struct GNUNET_CRYPTO_EcdhePublicKey *) + queue->cread_buf, + queue); + if (GNUNET_OK != decrypt_and_check_tc (queue, &tc, queue->cread_buf)) + { + GNUNET_log (GNUNET_ERROR_TYPE_INFO, + "Invalid TCP KX received from %s\n", + GNUNET_a2s (queue->address, queue->address_len)); + queue_destroy (queue); + return; + } + if (0 != + memcmp (&tc.sender, &queue->target, sizeof(struct GNUNET_PeerIdentity))) + { + GNUNET_log (GNUNET_ERROR_TYPE_WARNING, + "Invalid sender in TCP KX received from %s\n", + GNUNET_a2s (queue->address, queue->address_len)); + queue_destroy (queue); + return; + } + send_challenge (tc.challenge, queue); + queue->write_task = + GNUNET_SCHEDULER_add_write_net (GNUNET_TIME_UNIT_FOREVER_REL, + queue->sock, + &queue_write, + queue); + + /* update queue timeout */ + reschedule_queue_timeout (queue); + /* prepare to continue with regular read task immediately */ + memmove (queue->cread_buf, + &queue->cread_buf[INITIAL_KX_SIZE], + queue->cread_off - (INITIAL_KX_SIZE)); + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "cread_off is %lu bytes before adjusting\n", + queue->cread_off); + queue->cread_off -= INITIAL_KX_SIZE; + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "cread_off set to %lu bytes\n", + queue->cread_off); + queue->read_task = GNUNET_SCHEDULER_add_now (&queue_read, queue); +} + + +/** + * Read from the socket of the proto queue until we have enough data + * to upgrade to full queue. + * + * @param cls a `struct ProtoQueue` + */ +static void +proto_read_kx (void *cls) +{ + struct ProtoQueue *pq = cls; + ssize_t rcvd; + struct GNUNET_TIME_Relative left; + struct Queue *queue; + struct TCPConfirmation tc; + GNUNET_SCHEDULER_TaskCallback read_task; + + pq->read_task = NULL; + left = GNUNET_TIME_absolute_get_remaining (pq->timeout); + if (0 == left.rel_value_us) + { + free_proto_queue (pq); + return; + } + rcvd = GNUNET_NETWORK_socket_recv (pq->sock, + &pq->ibuf[pq->ibuf_off], + sizeof(pq->ibuf) - pq->ibuf_off); + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Proto received %lu bytes for KX\n", rcvd); + if (-1 == rcvd) + { + if ((EAGAIN != errno) && (EINTR != errno)) + { + GNUNET_log_strerror (GNUNET_ERROR_TYPE_DEBUG, "recv"); + free_proto_queue (pq); + return; + } + /* try again */ + pq->read_task = + GNUNET_SCHEDULER_add_read_net (left, pq->sock, &proto_read_kx, pq); + return; + } + pq->ibuf_off += rcvd; + if (sizeof (struct TCPNATProbeMessage) == pq->ibuf_off) + { + struct TCPNATProbeMessage *pm = (struct TCPNATProbeMessage *) pq->ibuf; + + check_and_remove_pending_reversal (pq->address, pq->address->sa_family, + &pm->clientIdentity); + + queue = GNUNET_new (struct Queue); + queue->target = pm->clientIdentity; + queue->cs = GNUNET_TRANSPORT_CS_OUTBOUND; + read_task = &queue_read_kx; + } + else if (pq->ibuf_off > sizeof(pq->ibuf)) + { + /* read more */ + pq->read_task = + GNUNET_SCHEDULER_add_read_net (left, pq->sock, &proto_read_kx, pq); + return; + } + else + { + /* we got all the data, let's find out who we are talking to! */ + queue = GNUNET_new (struct Queue); + setup_in_cipher ((const struct GNUNET_CRYPTO_EcdhePublicKey *) pq->ibuf, + queue); + if (GNUNET_OK != decrypt_and_check_tc (queue, &tc, pq->ibuf)) + { + GNUNET_log (GNUNET_ERROR_TYPE_INFO, + "Invalid TCP KX received from %s\n", + GNUNET_a2s (pq->address, pq->address_len)); + gcry_cipher_close (queue->in_cipher); + GNUNET_free (queue); + free_proto_queue (pq); + return; + } + queue->target = tc.sender; + queue->cs = GNUNET_TRANSPORT_CS_INBOUND; + read_task = &queue_read; + } + queue->address = pq->address; /* steals reference */ + queue->address_len = pq->address_len; + queue->listen_sock = pq->listen_sock; + queue->sock = pq->sock; + + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "created queue with target %s\n", + GNUNET_i2s (&queue->target)); + + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "start kx proto\n"); + + start_initial_kx_out (queue); + boot_queue (queue); + queue->read_task = + GNUNET_SCHEDULER_add_read_net (GNUNET_CONSTANTS_IDLE_CONNECTION_TIMEOUT, + queue->sock, + read_task, + queue); + queue->write_task = + GNUNET_SCHEDULER_add_write_net (GNUNET_TIME_UNIT_FOREVER_REL, + queue->sock, + &queue_write, + queue); + // TODO To early! Move it somewhere else. + // send_challenge (tc.challenge, queue); + queue->challenge_received = tc.challenge; + + GNUNET_CONTAINER_DLL_remove (proto_head, proto_tail, pq); + GNUNET_free (pq); +} + + +static struct ProtoQueue * +create_proto_queue (struct GNUNET_NETWORK_Handle *sock, + struct sockaddr *in, + socklen_t addrlen) +{ + struct ProtoQueue *pq = GNUNET_new (struct ProtoQueue); + + if (NULL == sock) + { + // sock = GNUNET_CONNECTION_create_from_sockaddr (AF_INET, addr, addrlen); + sock = GNUNET_NETWORK_socket_create (in->sa_family, SOCK_STREAM, 0); + if (NULL == sock) + { + GNUNET_log (GNUNET_ERROR_TYPE_WARNING, + "socket(%d) failed: %s", + in->sa_family, + strerror (errno)); + GNUNET_free (in); + GNUNET_free (pq); + return NULL; + } + if ((GNUNET_OK != GNUNET_NETWORK_socket_connect (sock, in, addrlen)) && + (errno != EINPROGRESS)) + { + GNUNET_log (GNUNET_ERROR_TYPE_WARNING, + "connect to `%s' failed: %s", + GNUNET_a2s (in, addrlen), + strerror (errno)); + GNUNET_NETWORK_socket_close (sock); + GNUNET_free (in); + return NULL; + } + } + pq->address_len = addrlen; + pq->address = in; + pq->timeout = GNUNET_TIME_relative_to_absolute (PROTO_QUEUE_TIMEOUT); + pq->sock = sock; + pq->read_task = GNUNET_SCHEDULER_add_read_net (PROTO_QUEUE_TIMEOUT, + pq->sock, + &proto_read_kx, + pq); + GNUNET_CONTAINER_DLL_insert (proto_head, proto_tail, pq); + + return pq; +} + + +/** + * We have been notified that our listen socket has something to + * read. Do the read and reschedule this function to be called again + * once more is available. + * + * @param cls ListenTask with listening socket and task + */ +static void +listen_cb (void *cls) +{ + struct sockaddr_storage in; + socklen_t addrlen; + struct GNUNET_NETWORK_Handle *sock; + struct ListenTask *lt; + struct sockaddr *in_addr; + + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "listen_cb\n"); + + lt = cls; + + lt->listen_task = NULL; + GNUNET_assert (NULL != lt->listen_sock); + addrlen = sizeof(in); + memset (&in, 0, sizeof(in)); + sock = GNUNET_NETWORK_socket_accept (lt->listen_sock, + (struct sockaddr*) &in, + &addrlen); + if ((NULL == sock) && ((EMFILE == errno) || (ENFILE == errno))) + return; /* system limit reached, wait until connection goes down */ + lt->listen_task = GNUNET_SCHEDULER_add_read_net (GNUNET_TIME_UNIT_FOREVER_REL, + lt->listen_sock, + &listen_cb, + lt); + if ((NULL == sock) && ((EAGAIN == errno) || (ENOBUFS == errno))) + return; + if (NULL == sock) + { + GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING, "accept"); + return; + } + in_addr = GNUNET_memdup (&in, addrlen); + create_proto_queue (sock, in_addr, addrlen); +} + + +static void +try_connection_reversal (void *cls, + const struct sockaddr *addr, + socklen_t addrlen) +{ + (void) cls; + struct TCPNATProbeMessage pm; + struct ProtoQueue *pq; + struct sockaddr *in_addr; + + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "addr->sa_family %d\n", + addr->sa_family); + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Try to connect back\n"); + in_addr = GNUNET_memdup (addr, addrlen); + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "in_addr->sa_family %d\n", + in_addr->sa_family); + pq = create_proto_queue (NULL, in_addr, addrlen); + if (NULL != pq) + { + pm.header.size = htons (sizeof(struct TCPNATProbeMessage)); + pm.header.type = htons (GNUNET_MESSAGE_TYPE_TRANSPORT_TCP_NAT_PROBE); + pm.clientIdentity = my_identity; + memcpy (pq->write_buf, &pm, sizeof(struct TCPNATProbeMessage)); + pq->write_off = sizeof(struct TCPNATProbeMessage); + pq->write_task = GNUNET_SCHEDULER_add_write_net (PROTO_QUEUE_TIMEOUT, + pq->sock, + &proto_queue_write, + pq); + } + else + { + GNUNET_log (GNUNET_ERROR_TYPE_WARNING, + "Couldn't create ProtoQueue for sending TCPNATProbeMessage\n"); + } +} + + +static void +pending_reversal_timeout (void *cls) +{ + struct sockaddr *in = cls; + struct PendingReversal *pending_reversal; + struct GNUNET_HashCode key; + + GNUNET_CRYPTO_hash (in, + sizeof(struct sockaddr), + &key); + pending_reversal = GNUNET_CONTAINER_multihashmap_get (pending_reversals, + &key); + + GNUNET_assert (NULL != pending_reversal); + + if (GNUNET_NO == GNUNET_CONTAINER_multihashmap_remove (pending_reversals, + &key, + pending_reversal)) + GNUNET_log (GNUNET_ERROR_TYPE_WARNING, + "No pending reversal found for address %s\n", + GNUNET_a2s (in, sizeof (struct sockaddr))); + GNUNET_free (pending_reversal->in); + GNUNET_free (pending_reversal); +} + + +/** + * Function called by the transport service to initialize a + * message queue given address information about another peer. + * If and when the communication channel is established, the + * communicator must call #GNUNET_TRANSPORT_communicator_mq_add() + * to notify the service that the channel is now up. It is + * the responsibility of the communicator to manage sane + * retries and timeouts for any @a peer/@a address combination + * provided by the transport service. Timeouts and retries + * do not need to be signalled to the transport service. + * + * @param cls closure + * @param peer identity of the other peer + * @param address where to send the message, human-readable + * communicator-specific format, 0-terminated, UTF-8 + * @return #GNUNET_OK on success, #GNUNET_SYSERR if the provided address is + * invalid + */ +static int +mq_init (void *cls, const struct GNUNET_PeerIdentity *peer, const char *address) +{ + struct sockaddr *in; + socklen_t in_len = 0; + const char *path; + struct sockaddr_in *v4; + struct sockaddr_in6 *v6; + unsigned int is_natd = GNUNET_NO; + struct GNUNET_HashCode key; + + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Connecting to %s at %s\n", + GNUNET_i2s (peer), + address); + if (0 != strncmp (address, + COMMUNICATOR_ADDRESS_PREFIX "-", + strlen (COMMUNICATOR_ADDRESS_PREFIX "-"))) + { + GNUNET_break_op (0); + return GNUNET_SYSERR; + } + path = &address[strlen (COMMUNICATOR_ADDRESS_PREFIX "-")]; + in = tcp_address_to_sockaddr (path, &in_len); + + if (NULL == in) + { + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "Failed to setup TCP socket address\n"); + return GNUNET_SYSERR; + } + + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "in %s\n", + GNUNET_a2s (in, in_len)); + + switch (in->sa_family) + { + case AF_INET: + v4 = (struct sockaddr_in *) in; + if (0 == v4->sin_port) + { + is_natd = GNUNET_YES; + GNUNET_CRYPTO_hash (in, + sizeof(struct sockaddr), + &key); + if (GNUNET_YES == GNUNET_CONTAINER_multihashmap_contains ( + pending_reversals, + &key)) + { + GNUNET_log (GNUNET_ERROR_TYPE_WARNING, + "There is already a request reversal for `%s'at `%s'\n", + GNUNET_i2s (peer), + address); + GNUNET_free (in); + return GNUNET_SYSERR; + } + } + break; + + case AF_INET6: + v6 = (struct sockaddr_in6 *) in; + if (0 == v6->sin6_port) + { + GNUNET_log (GNUNET_ERROR_TYPE_WARNING, + "Request reversal for `%s' at `%s' not possible for an IPv6 address\n", + GNUNET_i2s (peer), + address); + GNUNET_free (in); + return GNUNET_SYSERR; + } + break; + + default: + GNUNET_assert (0); + } + + if (GNUNET_YES == is_natd) + { + struct sockaddr_in local_sa; + struct PendingReversal *pending_reversal; + + memset (&local_sa, 0, sizeof(local_sa)); + local_sa.sin_family = AF_INET; + local_sa.sin_port = htons (bind_port); + /* We leave sin_address at 0, let the kernel figure it out, + even if our bind() is more specific. (May want to reconsider + later.) */ + if (GNUNET_OK != GNUNET_NAT_request_reversal (nat, &local_sa, v4)) + { + GNUNET_log (GNUNET_ERROR_TYPE_WARNING, + "request reversal for `%s' at `%s' failed\n", + GNUNET_i2s (peer), + address); + GNUNET_free (in); + return GNUNET_SYSERR; + } + pending_reversal = GNUNET_new (struct PendingReversal); + pending_reversal->in = in; + GNUNET_assert (GNUNET_OK == + GNUNET_CONTAINER_multihashmap_put (pending_reversals, + &key, + pending_reversal, + GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_ONLY)); + pending_reversal->target = *peer; + pending_reversal->timeout_task = GNUNET_SCHEDULER_add_delayed (NAT_TIMEOUT, + & + pending_reversal_timeout, + in); + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Created NAT WAIT connection to `%s' at `%s'\n", + GNUNET_i2s (peer), + GNUNET_a2s (in, sizeof (struct sockaddr))); + } + else + { + struct GNUNET_NETWORK_Handle *sock; + struct Queue *queue; + + sock = GNUNET_NETWORK_socket_create (in->sa_family, SOCK_STREAM, + IPPROTO_TCP); + if (NULL == sock) + { + GNUNET_log (GNUNET_ERROR_TYPE_WARNING, + "socket(%d) failed: %s", + in->sa_family, + strerror (errno)); + GNUNET_free (in); + return GNUNET_SYSERR; + } + if ((GNUNET_OK != GNUNET_NETWORK_socket_connect (sock, in, in_len)) && + (errno != EINPROGRESS)) + { + GNUNET_log (GNUNET_ERROR_TYPE_WARNING, + "connect to `%s' failed: %s", + address, + strerror (errno)); + GNUNET_NETWORK_socket_close (sock); + GNUNET_free (in); + return GNUNET_SYSERR; + } + + queue = GNUNET_new (struct Queue); + queue->target = *peer; + queue->address = in; + queue->address_len = in_len; + queue->sock = sock; + queue->cs = GNUNET_TRANSPORT_CS_OUTBOUND; + boot_queue (queue); + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "booted queue with target %s\n", + GNUNET_i2s (&queue->target)); + // queue->mq_awaits_continue = GNUNET_YES; + queue->read_task = + GNUNET_SCHEDULER_add_read_net (GNUNET_CONSTANTS_IDLE_CONNECTION_TIMEOUT, + queue->sock, + &queue_read_kx, + queue); + + + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "start kx mq_init\n"); + + start_initial_kx_out (queue); + queue->write_task = + GNUNET_SCHEDULER_add_write_net (GNUNET_TIME_UNIT_FOREVER_REL, + queue->sock, + &queue_write, + queue); + } + + return GNUNET_OK; +} + + +/** + * Iterator over all ListenTasks to clean up. + * + * @param cls NULL + * @param key unused + * @param value the ListenTask to cancel. + * @return #GNUNET_OK to continue to iterate + */ +static int +get_lt_delete_it (void *cls, + const struct GNUNET_HashCode *key, + void *value) +{ + struct ListenTask *lt = value; + + (void) cls; + (void) key; + if (NULL != lt->listen_task) + { + GNUNET_SCHEDULER_cancel (lt->listen_task); + lt->listen_task = NULL; + } + if (NULL != lt->listen_sock) + { + GNUNET_break (GNUNET_OK == GNUNET_NETWORK_socket_close (lt->listen_sock)); + lt->listen_sock = NULL; + } + GNUNET_free (lt); + return GNUNET_OK; +} + + +/** + * Iterator over all message queues to clean up. + * + * @param cls NULL + * @param target unused + * @param value the queue to destroy + * @return #GNUNET_OK to continue to iterate + */ +static int +get_queue_delete_it (void *cls, + const struct GNUNET_PeerIdentity *target, + void *value) +{ + struct Queue *queue = value; + + (void) cls; + (void) target; + queue_destroy (queue); + return GNUNET_OK; +} + + +/** + * Shutdown the UNIX communicator. + * + * @param cls NULL (always) + */ +static void +do_shutdown (void *cls) +{ + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Shutdown %s!\n", + shutdown_running ? "running" : "not running"); + + if (GNUNET_YES == shutdown_running) + return; + else + shutdown_running = GNUNET_YES; + + while (NULL != proto_head) + free_proto_queue (proto_head); + if (NULL != nat) + { + GNUNET_NAT_unregister (nat); + nat = NULL; + } + GNUNET_CONTAINER_multihashmap_iterate (pending_reversals, + &pending_reversals_delete_it, NULL); + GNUNET_CONTAINER_multihashmap_destroy (pending_reversals); + GNUNET_CONTAINER_multihashmap_iterate (lt_map, &get_lt_delete_it, NULL); + GNUNET_CONTAINER_multihashmap_destroy (lt_map); + GNUNET_CONTAINER_multipeermap_iterate (queue_map, &get_queue_delete_it, NULL); + GNUNET_CONTAINER_multipeermap_destroy (queue_map); + if (NULL != ch) + { + GNUNET_TRANSPORT_communicator_address_remove_all (ch); + GNUNET_TRANSPORT_communicator_disconnect (ch); + ch = NULL; + } + if (NULL != stats) + { + GNUNET_STATISTICS_destroy (stats, GNUNET_NO); + stats = NULL; + } + if (NULL != my_private_key) + { + GNUNET_free (my_private_key); + my_private_key = NULL; + } + if (NULL != is) + { + GNUNET_NT_scanner_done (is); + is = NULL; + } + if (NULL != peerstore) + { + GNUNET_PEERSTORE_disconnect (peerstore, GNUNET_NO); + peerstore = NULL; + } + if (NULL != resolve_request_handle) + { + GNUNET_RESOLVER_request_cancel (resolve_request_handle); + resolve_request_handle = NULL; + } + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Shutdown done!\n"); +} + + +/** + * Function called when the transport service has received an + * acknowledgement for this communicator (!) via a different return + * path. + * + * Not applicable for TCP. + * + * @param cls closure + * @param sender which peer sent the notification + * @param msg payload + */ +static void +enc_notify_cb (void *cls, + const struct GNUNET_PeerIdentity *sender, + const struct GNUNET_MessageHeader *msg) +{ + (void) cls; + (void) sender; + (void) msg; + GNUNET_break_op (0); +} + + +/** + * Signature of the callback passed to #GNUNET_NAT_register() for + * a function to call whenever our set of 'valid' addresses changes. + * + * @param cls closure + * @param[in,out] app_ctx location where the app can store stuff + * on add and retrieve it on remove + * @param add_remove #GNUNET_YES to add a new public IP address, + * #GNUNET_NO to remove a previous (now invalid) one + * @param ac address class the address belongs to + * @param addr either the previous or the new public IP address + * @param addrlen actual length of the @a addr + */ +static void +nat_address_cb (void *cls, + void **app_ctx, + int add_remove, + enum GNUNET_NAT_AddressClass ac, + const struct sockaddr *addr, + socklen_t addrlen) +{ + char *my_addr; + struct GNUNET_TRANSPORT_AddressIdentifier *ai; + + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "nat address cb %s %s\n", + add_remove ? "add" : "remove", + GNUNET_a2s (addr, addrlen)); + + if (GNUNET_YES == add_remove) + { + enum GNUNET_NetworkType nt; + + GNUNET_asprintf (&my_addr, + "%s-%s", + COMMUNICATOR_ADDRESS_PREFIX, + GNUNET_a2s (addr, addrlen)); + nt = GNUNET_NT_scanner_get_type (is, addr, addrlen); + ai = + GNUNET_TRANSPORT_communicator_address_add (ch, + my_addr, + nt, + GNUNET_TIME_UNIT_FOREVER_REL); + GNUNET_free (my_addr); + *app_ctx = ai; + } + else + { + ai = *app_ctx; + GNUNET_TRANSPORT_communicator_address_remove (ai); + *app_ctx = NULL; + } +} + + +/** + * This method adds addresses to the DLL, that are later register at the NAT service. + */ +static void +add_addr (struct sockaddr *in, socklen_t in_len) +{ + + struct Addresses *saddrs; + + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "add address %s\n", + GNUNET_a2s (in, in_len)); + + saddrs = GNUNET_new (struct Addresses); + saddrs->addr = in; + saddrs->addr_len = in_len; + GNUNET_CONTAINER_DLL_insert (addrs_head, addrs_tail, saddrs); + + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "after add address %s\n", + GNUNET_a2s (in, in_len)); + + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "add address %s\n", + GNUNET_a2s (saddrs->addr, saddrs->addr_len)); + + addrs_lens++; +} + + +/** + * This method launch network interactions for each address we like to bind to. + * + * @param addr The address we will listen to. + * @param in_len The length of the address we will listen to. + * @return GNUNET_SYSERR in case of error. GNUNET_OK in case we are successfully listen to the address. + */ +static int +init_socket (struct sockaddr *addr, + socklen_t in_len) +{ + struct sockaddr_storage in_sto; + socklen_t sto_len; + struct GNUNET_NETWORK_Handle *listen_sock; + struct ListenTask *lt; + int sockfd; + struct GNUNET_HashCode h_sock; + + if (NULL == addr) + { + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "Address is NULL.\n"); + return GNUNET_SYSERR; + } + + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "address %s\n", + GNUNET_a2s (addr, in_len)); + + listen_sock = + GNUNET_NETWORK_socket_create (addr->sa_family, SOCK_STREAM, IPPROTO_TCP); + if (NULL == listen_sock) + { + GNUNET_log_strerror (GNUNET_ERROR_TYPE_ERROR, "socket"); + return GNUNET_SYSERR; + } + + if (GNUNET_OK != GNUNET_NETWORK_socket_bind (listen_sock, addr, in_len)) + { + GNUNET_log_strerror (GNUNET_ERROR_TYPE_ERROR, "bind"); + GNUNET_NETWORK_socket_close (listen_sock); + listen_sock = NULL; + return GNUNET_SYSERR; + } + + if (GNUNET_OK != + GNUNET_NETWORK_socket_listen (listen_sock, + 5)) + { + GNUNET_log_strerror (GNUNET_ERROR_TYPE_ERROR, + "listen"); + GNUNET_NETWORK_socket_close (listen_sock); + listen_sock = NULL; + return GNUNET_SYSERR; + } + + /* We might have bound to port 0, allowing the OS to figure it out; + thus, get the real IN-address from the socket */ + sto_len = sizeof(in_sto); + + if (0 != getsockname (GNUNET_NETWORK_get_fd (listen_sock), + (struct sockaddr *) &in_sto, + &sto_len)) + { + memcpy (&in_sto, addr, in_len); + sto_len = in_len; + } + + // addr = (struct sockaddr *) &in_sto; + in_len = sto_len; + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Bound to `%s'\n", + GNUNET_a2s ((const struct sockaddr *) &in_sto, sto_len)); + stats = GNUNET_STATISTICS_create ("C-TCP", cfg); + + if (NULL == is) + is = GNUNET_NT_scanner_init (); + + if (NULL == my_private_key) + my_private_key = GNUNET_CRYPTO_eddsa_key_create_from_configuration (cfg); + if (NULL == my_private_key) + { + GNUNET_log ( + GNUNET_ERROR_TYPE_ERROR, + _ ( + "Transport service is lacking key configuration settings. Exiting.\n")); + if (NULL != resolve_request_handle) + GNUNET_RESOLVER_request_cancel (resolve_request_handle); + GNUNET_SCHEDULER_shutdown (); + return GNUNET_SYSERR; + } + GNUNET_CRYPTO_eddsa_key_get_public (my_private_key, &my_identity.public_key); + /* start listening */ + + lt = GNUNET_new (struct ListenTask); + lt->listen_sock = listen_sock; + + lt->listen_task = GNUNET_SCHEDULER_add_read_net (GNUNET_TIME_UNIT_FOREVER_REL, + listen_sock, + &listen_cb, + lt); + + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "creating hash\n"); + sockfd = GNUNET_NETWORK_get_fd (lt->listen_sock); + GNUNET_CRYPTO_hash (&sockfd, + sizeof(int), + &h_sock); + + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "creating map\n"); + if (NULL == lt_map) + lt_map = GNUNET_CONTAINER_multihashmap_create (2, GNUNET_NO); + + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "creating map entry\n"); + GNUNET_assert (GNUNET_OK == + GNUNET_CONTAINER_multihashmap_put (lt_map, + &h_sock, + lt, + GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_ONLY)); + + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "map entry created\n"); + + if (NULL == queue_map) + queue_map = GNUNET_CONTAINER_multipeermap_create (10, GNUNET_NO); + + if (NULL == ch) + ch = GNUNET_TRANSPORT_communicator_connect (cfg, + COMMUNICATOR_CONFIG_SECTION, + COMMUNICATOR_ADDRESS_PREFIX, + GNUNET_TRANSPORT_CC_RELIABLE, + &mq_init, + NULL, + &enc_notify_cb, + NULL); + + if (NULL == ch) + { + GNUNET_break (0); + if (NULL != resolve_request_handle) + GNUNET_RESOLVER_request_cancel (resolve_request_handle); + GNUNET_SCHEDULER_shutdown (); + return GNUNET_SYSERR; + } + + add_addr (addr, in_len); + return GNUNET_OK; + +} + + +/** + * This method reads from the DLL addrs_head to register them at the NAT service. + */ +static void +nat_register () +{ + struct sockaddr **saddrs; + socklen_t *saddr_lens; + int i; + size_t len; + + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "starting nat register!\n"); + len = 0; + i = 0; + saddrs = GNUNET_malloc ((addrs_lens) * sizeof(struct sockaddr *)); + saddr_lens = GNUNET_malloc ((addrs_lens) * sizeof(socklen_t)); + for (struct Addresses *pos = addrs_head; NULL != pos; pos = pos->next) + { + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "registering address %s\n", + GNUNET_a2s (addrs_head->addr, addrs_head->addr_len)); + + saddr_lens[i] = addrs_head->addr_len; + len += saddr_lens[i]; + saddrs[i] = GNUNET_memdup (addrs_head->addr, saddr_lens[i]); + i++; + } + + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "registering addresses %lu %lu %lu %lu\n", + (addrs_lens) * sizeof(struct sockaddr *), + (addrs_lens) * sizeof(socklen_t), + len, + sizeof(COMMUNICATOR_CONFIG_SECTION)); + nat = GNUNET_NAT_register (cfg, + COMMUNICATOR_CONFIG_SECTION, + IPPROTO_TCP, + addrs_lens, + (const struct sockaddr **) saddrs, + saddr_lens, + &nat_address_cb, + try_connection_reversal, + NULL /* closure */); + for (i = addrs_lens - 1; i >= 0; i--) + GNUNET_free (saddrs[i]); + GNUNET_free (saddrs); + GNUNET_free (saddr_lens); + + if (NULL == nat) + { + GNUNET_break (0); + if (NULL != resolve_request_handle) + GNUNET_RESOLVER_request_cancel (resolve_request_handle); + GNUNET_SCHEDULER_shutdown (); + } +} + + +/** + * This method is the callback called by the resolver API, and wraps method init_socket. + * + * @param cls The port we will bind to. + * @param addr The address we will bind to. + * @param in_len The length of the address we will bind to. + */ +static void +init_socket_resolv (void *cls, + const struct sockaddr *addr, + socklen_t in_len) +{ + struct sockaddr_in *v4; + struct sockaddr_in6 *v6; + struct sockaddr *in; + + (void) cls; + if (NULL != addr) + { + if (AF_INET == addr->sa_family) + { + v4 = (struct sockaddr_in *) addr; + in = tcp_address_to_sockaddr_numeric_v4 (&in_len, *v4, bind_port);// _global); + } + else if (AF_INET6 == addr->sa_family) + { + v6 = (struct sockaddr_in6 *) addr; + in = tcp_address_to_sockaddr_numeric_v6 (&in_len, *v6, bind_port);// _global); + } + else + { + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "Address family %u not suitable (not AF_INET %u nor AF_INET6 %u \n", + addr->sa_family, + AF_INET, + AF_INET6); + return; + } + init_socket (in, in_len); + } + else + { + GNUNET_log (GNUNET_ERROR_TYPE_INFO, + "Address is NULL. This might be an error or the resolver finished resolving.\n"); + if (NULL == addrs_head) + { + GNUNET_log (GNUNET_ERROR_TYPE_WARNING, + "Resolver finished resolving, but we do not listen to an address!.\n"); + return; + } + nat_register (); + } +} + + +/** + * Setup communicator and launch network interactions. + * + * @param cls NULL (always) + * @param args remaining command-line arguments + * @param cfgfile name of the configuration file used (for saving, can be NULL!) + * @param c configuration + */ +static void +run (void *cls, + char *const *args, + const char *cfgfile, + const struct GNUNET_CONFIGURATION_Handle *c) +{ + char *bindto; + struct sockaddr *in; + socklen_t in_len; + struct sockaddr_in v4; + struct sockaddr_in6 v6; + char *start; + unsigned int port; + char dummy[2]; + char *rest = NULL; + struct PortOnlyIpv4Ipv6 *po; + socklen_t addr_len_ipv4; + socklen_t addr_len_ipv6; + + (void) cls; + + pending_reversals = GNUNET_CONTAINER_multihashmap_create (16, GNUNET_NO); + memset (&v4,0,sizeof(struct sockaddr_in)); + memset (&v6,0,sizeof(struct sockaddr_in6)); + cfg = c; + if (GNUNET_OK != + GNUNET_CONFIGURATION_get_value_string (cfg, + COMMUNICATOR_CONFIG_SECTION, + "BINDTO", + &bindto)) + { + GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR, + COMMUNICATOR_CONFIG_SECTION, + "BINDTO"); + return; + } + if (GNUNET_OK != + GNUNET_CONFIGURATION_get_value_number (cfg, + COMMUNICATOR_CONFIG_SECTION, + "MAX_QUEUE_LENGTH", + &max_queue_length)) + max_queue_length = DEFAULT_MAX_QUEUE_LENGTH; + if (GNUNET_OK != + GNUNET_CONFIGURATION_get_value_time (cfg, + COMMUNICATOR_CONFIG_SECTION, + "REKEY_INTERVAL", + &rekey_interval)) + rekey_interval = DEFAULT_REKEY_INTERVAL; + + peerstore = GNUNET_PEERSTORE_connect (cfg); + if (NULL == peerstore) + { + GNUNET_free (bindto); + GNUNET_break (0); + GNUNET_SCHEDULER_shutdown (); + return; + } + + GNUNET_SCHEDULER_add_shutdown (&do_shutdown, NULL); + + if (1 == sscanf (bindto, "%u%1s", &bind_port, dummy)) + { + po = tcp_address_to_sockaddr_port_only (bindto, &bind_port); + addr_len_ipv4 = po->addr_len_ipv4; + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "address po %s\n", + GNUNET_a2s (po->addr_ipv4, addr_len_ipv4)); + if (NULL != po->addr_ipv4) + { + init_socket (po->addr_ipv4, addr_len_ipv4); + } + if (NULL != po->addr_ipv6) + { + addr_len_ipv6 = po->addr_len_ipv6; + init_socket (po->addr_ipv6, addr_len_ipv6); + } + GNUNET_free (po); + nat_register (); + GNUNET_free (bindto); + return; + } + + start = extract_address (bindto); + // FIXME: check for NULL == start... + if (1 == inet_pton (AF_INET, start, &v4.sin_addr)) + { + bind_port = extract_port (bindto); + + in = tcp_address_to_sockaddr_numeric_v4 (&in_len, v4, bind_port); + init_socket (in, in_len); + nat_register (); + GNUNET_free (start); + GNUNET_free (bindto); + return; + } + + if (1 == inet_pton (AF_INET6, start, &v6.sin6_addr)) + { + bind_port = extract_port (bindto); + in = tcp_address_to_sockaddr_numeric_v6 (&in_len, v6, bind_port); + init_socket (in, in_len); + nat_register (); + GNUNET_free (start); + GNUNET_free (bindto); + return; + } + + bind_port = extract_port (bindto); + resolve_request_handle = GNUNET_RESOLVER_ip_get (strtok_r (bindto, + ":", + &rest), + AF_UNSPEC, + GNUNET_TIME_UNIT_MINUTES, + &init_socket_resolv, + &port); + + GNUNET_free (bindto); + GNUNET_free (start); +} + + +/** + * The main function for the UNIX communicator. + * + * @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 + }; + int ret; + + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Starting tcp communicator\n"); + if (GNUNET_OK != + GNUNET_STRINGS_get_utf8_args (argc, argv, + &argc, &argv)) + return 2; + + ret = (GNUNET_OK == + GNUNET_PROGRAM_run (argc, + argv, + "gnunet-communicator-tcp", + _ ("GNUnet TCP communicator"), + options, + &run, + NULL)) + ? 0 + : 1; + GNUNET_free_nz ((void *) argv); + return ret; +} + + +/* end of gnunet-communicator-tcp.c */ diff --git a/src/service/transport/gnunet-communicator-udp.c b/src/service/transport/gnunet-communicator-udp.c new file mode 100644 index 000000000..f9bbe91f6 --- /dev/null +++ b/src/service/transport/gnunet-communicator-udp.c @@ -0,0 +1,3444 @@ +/* + This file is part of GNUnet + Copyright (C) 2010-2014, 2018, 2019 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 . + + SPDX-License-Identifier: AGPL3.0-or-later + */ + +/** + * @file transport/gnunet-communicator-udp.c + * @brief Transport plugin using UDP. + * @author Christian Grothoff + * + * TODO: + * - consider imposing transmission limits in the absence + * of ACKs; or: maybe this should be done at TNG service level? + * (at least the receiver might want to enforce limits on + * KX/DH operations per sender in here) (#5552) + * - overall, we should look more into flow control support + * (either in backchannel, or general solution in TNG service) + * - handle addresses discovered from broadcasts (#5551) + * (think: what was the story again on address validation? + * where is the API for that!?!) + * - support DNS names in BINDTO option (#5528) + * - support NAT connection reversal method (#5529) + * - support other UDP-specific NAT traversal methods (#) + */ +#include "platform.h" +#include "gnunet_common.h" +#include "gnunet_util_lib.h" +#include "gnunet_protocols.h" +#include "gnunet_signatures.h" +#include "gnunet_constants.h" +#include "gnunet_nat_service.h" +#include "gnunet_statistics_service.h" +#include "gnunet_transport_application_service.h" +#include "gnunet_transport_communication_service.h" + +/** + * How often do we rekey based on time (at least) + */ +#define DEFAULT_REKEY_TIME_INTERVAL GNUNET_TIME_UNIT_DAYS + +/** + * How long do we wait until we must have received the initial KX? + */ +#define PROTO_QUEUE_TIMEOUT GNUNET_TIME_UNIT_MINUTES + +/** + * How often do we broadcast our presence on the LAN? + */ +#define BROADCAST_FREQUENCY GNUNET_TIME_UNIT_MINUTES + +/** + * How often do we scan for changes to our network interfaces? + */ +#define INTERFACE_SCAN_FREQUENCY \ + GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_MINUTES, 5) + +/** + * How long do we believe our addresses to remain up (before + * the other peer should revalidate). + */ +#define ADDRESS_VALIDITY_PERIOD GNUNET_TIME_UNIT_HOURS + +#define WORKING_QUEUE_INTERVALL \ + GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_MICROSECONDS,1) + +/** + * AES key size. + */ +#define AES_KEY_SIZE (256 / 8) + +/** + * AES (GCM) IV size. + */ +#define AES_IV_SIZE (96 / 8) + +/** + * Size of the GCM tag. + */ +#define GCM_TAG_SIZE (128 / 8) + +#define GENERATE_AT_ONCE 16 + +/** + * If we fall below this number of available KCNs, + * we generate additional ACKs until we reach + * #KCN_TARGET. + * Should be large enough that we don't generate ACKs all + * the time and still have enough time for the ACK to + * arrive before the sender runs out. So really this + * should ideally be based on the RTT. + */ +#define KCN_THRESHOLD 96 + +/** + * How many KCNs do we keep around *after* we hit + * the #KCN_THRESHOLD? Should be larger than + * #KCN_THRESHOLD so we do not generate just one + * ACK at the time. + */ +#define KCN_TARGET 128 + +/** + * What is the maximum delta between KCN sequence numbers + * that we allow. Used to expire 'ancient' KCNs that likely + * were dropped by the network. Must be larger than + * KCN_TARGET (otherwise we generate new KCNs all the time), + * but not too large (otherwise packet loss may cause + * sender to fall back to KX needlessly when sender runs + * out of ACK'ed KCNs due to losses). + */ +#define MAX_SQN_DELTA 160 + +/** + * How many shared master secrets do we keep around + * at most per sender? Should be large enough so + * that we generally have a chance of sending an ACK + * before the sender already rotated out the master + * secret. Generally values around #KCN_TARGET make + * sense. Might make sense to adapt to RTT if we had + * a good measurement... + */ +#define MAX_SECRETS 256 + +/** + * Default value for how often we do rekey based on number of bytes transmitted? + * (additionally randomized). + */ +#define DEFAULT_REKEY_MAX_BYTES (1024LLU * 1024 * 1024 * 4LLU) + +/** + * Address prefix used by the communicator. + */ + +#define COMMUNICATOR_ADDRESS_PREFIX "udp" + +/** + * Configuration section used by the communicator. + */ +#define COMMUNICATOR_CONFIG_SECTION "communicator-udp" + +GNUNET_NETWORK_STRUCT_BEGIN + + +/** + * Signature we use to verify that the ephemeral key was really chosen by + * the specified sender. If possible, the receiver should respond with + * a `struct UDPAck` (possibly via backchannel). + */ +struct UdpHandshakeSignature +{ + /** + * Purpose must be #GNUNET_SIGNATURE_PURPOSE_COMMUNICATOR_UDP_HANDSHAKE + */ + struct GNUNET_CRYPTO_EccSignaturePurpose purpose; + + /** + * Identity of the inititor of the UDP connection (UDP client). + */ + struct GNUNET_PeerIdentity sender; + + /** + * Presumed identity of the target of the UDP connection (UDP server) + */ + struct GNUNET_PeerIdentity receiver; + + /** + * Ephemeral key used by the @e sender. + */ + struct GNUNET_CRYPTO_EcdhePublicKey ephemeral; + + /** + * Monotonic time of @e sender, to possibly help detect replay attacks + * (if receiver persists times by sender). + */ + struct GNUNET_TIME_AbsoluteNBO monotonic_time; +}; + + +/** + * "Plaintext" header at beginning of KX message. Followed + * by encrypted `struct UDPConfirmation`. + */ +struct InitialKX +{ + /** + * Ephemeral key for KX. + */ + struct GNUNET_CRYPTO_EcdhePublicKey ephemeral; + + /** + * HMAC for the following encrypted message, using GCM. HMAC uses + * key derived from the handshake with sequence number zero. + */ + uint8_t gcm_tag[GCM_TAG_SIZE]; + +}; + + +/** + * Encrypted continuation of UDP initial handshake, followed + * by message header with payload. + */ +struct UDPConfirmation +{ + /** + * Sender's identity + */ + struct GNUNET_PeerIdentity sender; + + /** + * Sender's signature of type #GNUNET_SIGNATURE_PURPOSE_COMMUNICATOR_UDP_HANDSHAKE + */ + struct GNUNET_CRYPTO_EddsaSignature sender_sig; + + /** + * Monotonic time of @e sender, to possibly help detect replay attacks + * (if receiver persists times by sender). + */ + struct GNUNET_TIME_AbsoluteNBO monotonic_time; + + /* followed by messages */ + + /* padding may follow actual messages */ +}; + + +/** + * UDP key acknowledgement. May be sent via backchannel. Allows the + * sender to use `struct UDPBox` with the acknowledge key henceforth. + */ +struct UDPAck +{ + /** + * Type is #GNUNET_MESSAGE_TYPE_COMMUNICATOR_UDP_ACK. + */ + struct GNUNET_MessageHeader header; + + /** + * Sequence acknowledgement limit. Specifies current maximum sequence + * number supported by receiver. + */ + uint32_t sequence_ack GNUNET_PACKED; + + /** + * CMAC of the base key being acknowledged. + */ + struct GNUNET_HashCode cmac; +}; + + +/** + * Signature we use to verify that the broadcast was really made by + * the peer that claims to have made it. Basically, affirms that the + * peer is really using this IP address (albeit possibly not in _our_ + * LAN). Makes it difficult for peers in the LAN to claim to + * be just any global peer -- an attacker must have at least + * shared a LAN with the peer they're pretending to be here. + */ +struct UdpBroadcastSignature +{ + /** + * Purpose must be #GNUNET_SIGNATURE_PURPOSE_COMMUNICATOR_UDP_BROADCAST + */ + struct GNUNET_CRYPTO_EccSignaturePurpose purpose; + + /** + * Identity of the inititor of the UDP broadcast. + */ + struct GNUNET_PeerIdentity sender; + + /** + * Hash of the sender's UDP address. + */ + struct GNUNET_HashCode h_address; +}; + + +/** + * Broadcast by peer in LAN announcing its presence. Unusual in that + * we don't pad these to full MTU, as we cannot prevent being + * recognized in LAN as GNUnet peers if this feature is enabled + * anyway. Also, the entire message is in cleartext. + */ +struct UDPBroadcast +{ + /** + * Sender's peer identity. + */ + struct GNUNET_PeerIdentity sender; + + /** + * Sender's signature of type + * #GNUNET_SIGNATURE_PURPOSE_COMMUNICATOR_UDP_BROADCAST + */ + struct GNUNET_CRYPTO_EddsaSignature sender_sig; +}; + + +/** + * UDP message box. Always sent encrypted, only allowed after + * the receiver sent a `struct UDPAck` for the base key! + */ +struct UDPBox +{ + /** + * Key and IV identification code. KDF applied to an acknowledged + * base key and a sequence number. Sequence numbers must be used + * monotonically increasing up to the maximum specified in + * `struct UDPAck`. Without further `struct UDPAck`s, the sender + * must fall back to sending handshakes! + */ + struct GNUNET_ShortHashCode kid; + + /** + * 128-bit authentication tag for the following encrypted message, + * from GCM. MAC starts at the @e body_start that follows and + * extends until the end of the UDP payload. If the @e hmac is + * wrong, the receiver should check if the message might be a + * `struct UdpHandshakeSignature`. + */ + uint8_t gcm_tag[GCM_TAG_SIZE]; + +}; + +/** + * Plaintext of a rekey payload in a UDPBox. + */ +struct UDPRekey +{ + /** + * Type is #GNUNET_MESSAGE_TYPE_COMMUNICATOR_UDP_REKEY. + */ + struct GNUNET_MessageHeader header; + + /** + * Ephemeral key to rekey with. + */ + struct GNUNET_CRYPTO_EcdhePublicKey ephemeral; +}; + +GNUNET_NETWORK_STRUCT_END + +/** + * Shared secret we generated for a particular sender or receiver. + */ +struct SharedSecret; + + +/** + * Pre-generated "kid" code (key and IV identification code) to + * quickly derive master key for a `struct UDPBox`. + */ +struct KeyCacheEntry +{ + /** + * Kept in a DLL. + */ + struct KeyCacheEntry *next; + + /** + * Kept in a DLL. + */ + struct KeyCacheEntry *prev; + + /** + * Key and IV identification code. KDF applied to an acknowledged + * base key and a sequence number. Sequence numbers must be used + * monotonically increasing up to the maximum specified in + * `struct UDPAck`. Without further `struct UDPAck`s, the sender + * must fall back to sending handshakes! + */ + struct GNUNET_ShortHashCode kid; + + /** + * Corresponding shared secret. + */ + struct SharedSecret *ss; + + /** + * Sequence number used to derive this entry from master key. + */ + uint32_t sequence_number; +}; + + +/** + * Information we track per sender address we have recently been + * in contact with (decryption from sender). + */ +struct SenderAddress; + +/** + * Information we track per receiving address we have recently been + * in contact with (encryption to receiver). + */ +struct ReceiverAddress; + +/** + * Shared secret we generated for a particular sender or receiver. + */ +struct SharedSecret +{ + /** + * Kept in a DLL. + */ + struct SharedSecret *next; + + /** + * Kept in a DLL. + */ + struct SharedSecret *prev; + + /** + * Kept in a DLL, sorted by sequence number. Only if we are decrypting. + */ + struct KeyCacheEntry *kce_head; + + /** + * Kept in a DLL, sorted by sequence number. Only if we are decrypting. + */ + struct KeyCacheEntry *kce_tail; + + /** + * Sender we use this shared secret with, or NULL. + */ + struct SenderAddress *sender; + + /** + * Receiver we use this shared secret with, or NULL. + */ + struct ReceiverAddress *receiver; + + /** + * Master shared secret. + */ + struct GNUNET_HashCode master; + + /** + * CMAC is used to identify @e master in ACKs. + */ + struct GNUNET_HashCode cmac; + + /** + * Up to which sequence number did we use this @e master already? + * (for encrypting only) + */ + uint32_t sequence_used; + + /** + * Up to which sequence number did the other peer allow us to use + * this key, or up to which number did we allow the other peer to + * use this key? + */ + uint32_t sequence_allowed; + + /** + * Number of active KCN entries. + */ + unsigned int active_kce_count; + + /** + * Bytes sent with this shared secret + */ + size_t bytes_sent; + + /** + * rekey initiated for this secret? + */ + int rekey_initiated; + + /** + * Also precompute keys despite sufficient acks (for rekey) + */ + int override_available_acks; +}; + + +/** + * Information we track per sender address we have recently been + * in contact with (we decrypt messages from the sender). + */ +struct SenderAddress +{ + /** + * To whom are we talking to. + */ + struct GNUNET_PeerIdentity target; + + /** + * Entry in sender expiration heap. + */ + struct GNUNET_CONTAINER_HeapNode *hn; + + /** + * Shared secrets we used with @e target, first used is head. + */ + struct SharedSecret *ss_head; + + /** + * Shared secrets we used with @e target, last used is tail. + */ + struct SharedSecret *ss_tail; + + /** + * Address of the other peer. + */ + struct sockaddr *address; + + /** + * Length of the address. + */ + socklen_t address_len; + + /** + * Timeout for this sender. + */ + struct GNUNET_TIME_Absolute timeout; + + /** + * Length of the DLL at @a ss_head. + */ + unsigned int num_secrets; + + /** + * Number of BOX keys from ACKs we have currently + * available for this sender. + */ + unsigned int acks_available; + + /** + * Which network type does this queue use? + */ + enum GNUNET_NetworkType nt; + + /** + * sender_destroy already called on sender. + */ + int sender_destroy_called; + + /** + * ID of kce working queue task + */ + struct GNUNET_SCHEDULER_Task *kce_task; + + /** + * Is the kce_task finished? + */ + int kce_task_finished; + + /** + * When KCE finishes, send ACK if GNUNET_YES + */ + int kce_send_ack_on_finish; +}; + + +/** + * Information we track per receiving address we have recently been + * in contact with (encryption to receiver). + */ +struct ReceiverAddress +{ + /** + * Timeout for this receiver address. + */ + struct GNUNET_TIME_Absolute rekey_timeout; + + /** + * To whom are we talking to. + */ + struct GNUNET_PeerIdentity target; + + /** + * Shared secrets we received from @e target, first used is head. + */ + struct SharedSecret *ss_head; + + /** + * Shared secrets we received with @e target, last used is tail. + */ + struct SharedSecret *ss_tail; + + /** + * Address of the receiver in the human-readable format + * with the #COMMUNICATOR_ADDRESS_PREFIX. + */ + char *foreign_addr; + + /** + * Address of the other peer. + */ + struct sockaddr *address; + + /** + * Length of the address. + */ + socklen_t address_len; + + /** + * Entry in sender expiration heap. + */ + struct GNUNET_CONTAINER_HeapNode *hn; + + /** + * KX message queue we are providing for the #ch. + */ + struct GNUNET_MQ_Handle *kx_mq; + + /** + * Default message queue we are providing for the #ch. + */ + struct GNUNET_MQ_Handle *d_mq; + + /** + * handle for KX queue with the #ch. + */ + struct GNUNET_TRANSPORT_QueueHandle *kx_qh; + + /** + * handle for default queue with the #ch. + */ + struct GNUNET_TRANSPORT_QueueHandle *d_qh; + + /** + * Timeout for this receiver address. + */ + struct GNUNET_TIME_Absolute timeout; + + /** + * MTU we allowed transport for this receiver's KX queue. + */ + size_t kx_mtu; + + /** + * MTU we allowed transport for this receiver's default queue. + */ + size_t d_mtu; + + /** + * Length of the DLL at @a ss_head. + */ + unsigned int num_secrets; + + /** + * Number of BOX keys from ACKs we have currently + * available for this receiver. + */ + unsigned int acks_available; + + /** + * Which network type does this queue use? + */ + enum GNUNET_NetworkType nt; + + /** + * receiver_destroy already called on receiver. + */ + int receiver_destroy_called; +}; + +/** + * Interface we broadcast our presence on. + */ +struct BroadcastInterface +{ + /** + * Kept in a DLL. + */ + struct BroadcastInterface *next; + + /** + * Kept in a DLL. + */ + struct BroadcastInterface *prev; + + /** + * Task for this broadcast interface. + */ + struct GNUNET_SCHEDULER_Task *broadcast_task; + + /** + * Sender's address of the interface. + */ + struct sockaddr *sa; + + /** + * Broadcast address to use on the interface. + */ + struct sockaddr *ba; + + /** + * Message we broadcast on this interface. + */ + struct UDPBroadcast bcm; + + /** + * If this is an IPv6 interface, this is the request + * we use to join/leave the group. + */ + struct ipv6_mreq mcreq; + + /** + * Number of bytes in @e sa. + */ + socklen_t salen; + + /** + * Was this interface found in the last #iface_proc() scan? + */ + int found; +}; + +/** + * The rekey interval + */ +static struct GNUNET_TIME_Relative rekey_interval; + +/** + * How often we do rekey based on number of bytes transmitted + */ +static unsigned long long rekey_max_bytes; + +/** + * Cache of pre-generated key IDs. + */ +static struct GNUNET_CONTAINER_MultiShortmap *key_cache; + +/** + * ID of read task + */ +static struct GNUNET_SCHEDULER_Task *read_task; + +/** + * ID of timeout task + */ +static struct GNUNET_SCHEDULER_Task *timeout_task; + +/** + * ID of master broadcast task + */ +static struct GNUNET_SCHEDULER_Task *broadcast_task; + +/** + * For logging statistics. + */ +static struct GNUNET_STATISTICS_Handle *stats; + +/** + * Our environment. + */ +static struct GNUNET_TRANSPORT_CommunicatorHandle *ch; + +/** + * Receivers (map from peer identity to `struct ReceiverAddress`) + */ +static struct GNUNET_CONTAINER_MultiPeerMap *receivers; + +/** + * Senders (map from peer identity to `struct SenderAddress`) + */ +static struct GNUNET_CONTAINER_MultiPeerMap *senders; + +/** + * Expiration heap for senders (contains `struct SenderAddress`) + */ +static struct GNUNET_CONTAINER_Heap *senders_heap; + +/** + * Expiration heap for receivers (contains `struct ReceiverAddress`) + */ +static struct GNUNET_CONTAINER_Heap *receivers_heap; + +/** + * Broadcast interface tasks. Kept in a DLL. + */ +static struct BroadcastInterface *bi_head; + +/** + * Broadcast interface tasks. Kept in a DLL. + */ +static struct BroadcastInterface *bi_tail; + +/** + * Our socket. + */ +static struct GNUNET_NETWORK_Handle *udp_sock; + +/** + * #GNUNET_YES if #udp_sock supports IPv6. + */ +static int have_v6_socket; + +/** + * Our public key. + */ +static struct GNUNET_PeerIdentity my_identity; + +/** + * Our private key. + */ +static struct GNUNET_CRYPTO_EddsaPrivateKey *my_private_key; + +/** + * Our configuration. + */ +static const struct GNUNET_CONFIGURATION_Handle *cfg; + +/** + * Our handle to report addresses for validation to TRANSPORT. + */ +static struct GNUNET_TRANSPORT_ApplicationHandle *ah; + +/** + * Network scanner to determine network types. + */ +static struct GNUNET_NT_InterfaceScanner *is; + +/** + * Connection to NAT service. + */ +static struct GNUNET_NAT_Handle *nat; + +/** + * Port number to which we are actually bound. + */ +static uint16_t my_port; + + +/** + * An interface went away, stop broadcasting on it. + * + * @param bi entity to close down + */ +static void +bi_destroy (struct BroadcastInterface *bi) +{ + if (AF_INET6 == bi->sa->sa_family) + { + /* Leave the multicast group */ + if (GNUNET_OK != GNUNET_NETWORK_socket_setsockopt (udp_sock, + IPPROTO_IPV6, + IPV6_LEAVE_GROUP, + &bi->mcreq, + sizeof(bi->mcreq))) + { + GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING, "setsockopt"); + } + } + GNUNET_CONTAINER_DLL_remove (bi_head, bi_tail, bi); + GNUNET_SCHEDULER_cancel (bi->broadcast_task); + GNUNET_free (bi->sa); + GNUNET_free (bi->ba); + GNUNET_free (bi); +} + + +/** + * Destroys a receiving state due to timeout or shutdown. + * + * @param receiver entity to close down + */ +static void +receiver_destroy (struct ReceiverAddress *receiver) +{ + + receiver->receiver_destroy_called = GNUNET_YES; + + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Disconnecting receiver for peer `%s'\n", + GNUNET_i2s (&receiver->target)); + if (NULL != receiver->kx_qh) + { + GNUNET_TRANSPORT_communicator_mq_del (receiver->kx_qh); + receiver->kx_qh = NULL; + receiver->kx_mq = NULL; + } + if (NULL != receiver->d_qh) + { + GNUNET_TRANSPORT_communicator_mq_del (receiver->d_qh); + receiver->d_qh = NULL; + } + GNUNET_assert (GNUNET_YES == + GNUNET_CONTAINER_multipeermap_remove (receivers, + &receiver->target, + receiver)); + GNUNET_assert (receiver == GNUNET_CONTAINER_heap_remove_node (receiver->hn)); + GNUNET_STATISTICS_set (stats, + "# receivers active", + GNUNET_CONTAINER_multipeermap_size (receivers), + GNUNET_NO); + GNUNET_free (receiver->address); + GNUNET_free (receiver->foreign_addr); + GNUNET_free (receiver); +} + + +/** + * Free memory used by key cache entry. + * + * @param kce the key cache entry + */ +static void +kce_destroy (struct KeyCacheEntry *kce) +{ + struct SharedSecret *ss = kce->ss; + + ss->active_kce_count--; + ss->sender->acks_available--; + GNUNET_CONTAINER_DLL_remove (ss->kce_head, ss->kce_tail, kce); + GNUNET_assert (GNUNET_YES == GNUNET_CONTAINER_multishortmap_remove (key_cache, + &kce->kid, + kce)); + GNUNET_free (kce); +} + + +/** + * Compute @a kid. + * + * @param msec master secret for HMAC calculation + * @param serial number for the @a smac calculation + * @param[out] kid where to write the key ID + */ +static void +get_kid (const struct GNUNET_HashCode *msec, + uint32_t serial, + struct GNUNET_ShortHashCode *kid) +{ + uint32_t sid = htonl (serial); + + GNUNET_CRYPTO_hkdf (kid, + sizeof(*kid), + GCRY_MD_SHA512, + GCRY_MD_SHA256, + &sid, + sizeof(sid), + msec, + sizeof(*msec), + "UDP-KID", + strlen ("UDP-KID"), + NULL, + 0); +} + + +/** + * Setup key cache entry for sequence number @a seq and shared secret @a ss. + * + * @param ss shared secret + * @param seq sequence number for the key cache entry + */ +static void +kce_generate (struct SharedSecret *ss, uint32_t seq) +{ + struct KeyCacheEntry *kce; + + GNUNET_assert (0 < seq); + kce = GNUNET_new (struct KeyCacheEntry); + kce->ss = ss; + kce->sequence_number = seq; + get_kid (&ss->master, seq, &kce->kid); + GNUNET_CONTAINER_DLL_insert (ss->kce_head, ss->kce_tail, kce); + ss->active_kce_count++; + ss->sender->acks_available++; + (void) GNUNET_CONTAINER_multishortmap_put ( + key_cache, + &kce->kid, + kce, + GNUNET_CONTAINER_MULTIHASHMAPOPTION_MULTIPLE); + GNUNET_STATISTICS_set (stats, + "# KIDs active", + GNUNET_CONTAINER_multishortmap_size (key_cache), + GNUNET_NO); +} + + +/** + * Destroy @a ss and associated key cache entries. + * + * @param ss shared secret to destroy + * @param withoutKce If GNUNET_YES shared secrets with kce will not be destroyed. + */ +static int +secret_destroy (struct SharedSecret *ss) +{ + struct SenderAddress *sender; + struct ReceiverAddress *receiver; + struct KeyCacheEntry *kce; + + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "secret %s destroy %u\n", + GNUNET_h2s (&ss->master), + ss->sequence_allowed); + if (NULL != (sender = ss->sender)) + { + GNUNET_CONTAINER_DLL_remove (sender->ss_head, sender->ss_tail, ss); + sender->num_secrets--; + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "%u sender->num_secrets\n", + receiver->num_secrets); + if (NULL != ss->sender->kce_task) + { + GNUNET_SCHEDULER_cancel (ss->sender->kce_task); + ss->sender->kce_task = NULL; + } + } + if (NULL != (receiver = ss->receiver)) + { + GNUNET_CONTAINER_DLL_remove (receiver->ss_head, receiver->ss_tail, ss); + receiver->num_secrets--; + receiver->acks_available -= (ss->sequence_allowed - ss->sequence_used); + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "%u receiver->num_secrets\n", + receiver->num_secrets); + } + while (NULL != (kce = ss->kce_head)) + kce_destroy (kce); + GNUNET_STATISTICS_update (stats, "# Secrets active", -1, GNUNET_NO); + GNUNET_STATISTICS_set (stats, + "# KIDs active", + GNUNET_CONTAINER_multishortmap_size (key_cache), + GNUNET_NO); + GNUNET_free (ss); + return GNUNET_YES; +} + + +/** + * Functions with this signature are called whenever we need + * to close a sender's state due to timeout. + * + * @param sender entity to close down + */ +static void +sender_destroy (struct SenderAddress *sender) +{ + sender->sender_destroy_called = GNUNET_YES; + GNUNET_assert ( + GNUNET_YES == + GNUNET_CONTAINER_multipeermap_remove (senders, &sender->target, sender)); + GNUNET_assert (sender == GNUNET_CONTAINER_heap_remove_node (sender->hn)); + GNUNET_STATISTICS_set (stats, + "# senders active", + GNUNET_CONTAINER_multipeermap_size (senders), + GNUNET_NO); + GNUNET_free (sender->address); + GNUNET_free (sender); +} + + +/** + * Compute @a key and @a iv. + * + * @param msec master secret for calculation + * @param serial number for the @a smac calculation + * @param[out] key where to write the decryption key + * @param[out] iv where to write the IV + */ +static void +get_iv_key (const struct GNUNET_HashCode *msec, + uint32_t serial, + char key[AES_KEY_SIZE], + char iv[AES_IV_SIZE]) +{ + uint32_t sid = htonl (serial); + char res[AES_KEY_SIZE + AES_IV_SIZE]; + + GNUNET_CRYPTO_hkdf (res, + sizeof(res), + GCRY_MD_SHA512, + GCRY_MD_SHA256, + &sid, + sizeof(sid), + msec, + sizeof(*msec), + "UDP-IV-KEY", + strlen ("UDP-IV-KEY"), + NULL, + 0); + memcpy (key, res, AES_KEY_SIZE); + memcpy (iv, &res[AES_KEY_SIZE], AES_IV_SIZE); +} + + +/** + * Increment sender timeout due to activity. + * + * @param sender address for which the timeout should be rescheduled + */ +static void +reschedule_sender_timeout (struct SenderAddress *sender) +{ + sender->timeout = + GNUNET_TIME_relative_to_absolute (GNUNET_CONSTANTS_IDLE_CONNECTION_TIMEOUT); + GNUNET_CONTAINER_heap_update_cost (sender->hn, sender->timeout.abs_value_us); +} + + +/** + * Increment receiver timeout due to activity. + * + * @param receiver address for which the timeout should be rescheduled + */ +static void +reschedule_receiver_timeout (struct ReceiverAddress *receiver) +{ + receiver->timeout = + GNUNET_TIME_relative_to_absolute (GNUNET_CONSTANTS_IDLE_CONNECTION_TIMEOUT); + GNUNET_CONTAINER_heap_update_cost (receiver->hn, + receiver->timeout.abs_value_us); +} + + +/** + * Task run to check #receiver_heap and #sender_heap for timeouts. + * + * @param cls unused, NULL + */ +static void +check_timeouts (void *cls) +{ + struct GNUNET_TIME_Relative st; + struct GNUNET_TIME_Relative rt; + struct GNUNET_TIME_Relative delay; + struct ReceiverAddress *receiver; + struct SenderAddress *sender; + + (void) cls; + timeout_task = NULL; + rt = GNUNET_TIME_UNIT_FOREVER_REL; + while (NULL != (receiver = GNUNET_CONTAINER_heap_peek (receivers_heap))) + { + rt = GNUNET_TIME_absolute_get_remaining (receiver->timeout); + if (0 != rt.rel_value_us) + break; + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Receiver timed out\n"); + receiver_destroy (receiver); + } + st = GNUNET_TIME_UNIT_FOREVER_REL; + while (NULL != (sender = GNUNET_CONTAINER_heap_peek (senders_heap))) + { + if (GNUNET_YES != sender->sender_destroy_called) + { + st = GNUNET_TIME_absolute_get_remaining (sender->timeout); + if (0 != st.rel_value_us) + break; + sender_destroy (sender); + } + } + delay = GNUNET_TIME_relative_min (rt, st); + if (delay.rel_value_us < GNUNET_TIME_UNIT_FOREVER_REL.rel_value_us) + timeout_task = GNUNET_SCHEDULER_add_delayed (delay, &check_timeouts, NULL); +} + + +/** + * Calculate cmac from master in @a ss. + * + * @param[in,out] ss data structure to complete + */ +static void +calculate_cmac (struct SharedSecret *ss) +{ + GNUNET_CRYPTO_hkdf (&ss->cmac, + sizeof(ss->cmac), + GCRY_MD_SHA512, + GCRY_MD_SHA256, + "CMAC", + strlen ("CMAC"), + &ss->master, + sizeof(ss->master), + "UDP-CMAC", + strlen ("UDP-CMAC"), + NULL, + 0); +} + + +/** + * We received @a plaintext_len bytes of @a plaintext from a @a sender. + * Pass it on to CORE. + * + * @param queue the queue that received the plaintext + * @param plaintext the plaintext that was received + * @param plaintext_len number of bytes of plaintext received + */ +static void +pass_plaintext_to_core (struct SenderAddress *sender, + const void *plaintext, + size_t plaintext_len) +{ + const struct GNUNET_MessageHeader *hdr = plaintext; + const char *pos = plaintext; + + while (ntohs (hdr->size) <= plaintext_len) + { + GNUNET_STATISTICS_update (stats, + "# bytes given to core", + ntohs (hdr->size), + GNUNET_NO); + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Giving %u bytes to TNG\n", ntohs (hdr->size)); + GNUNET_assert (GNUNET_SYSERR != + GNUNET_TRANSPORT_communicator_receive (ch, + &sender->target, + hdr, + ADDRESS_VALIDITY_PERIOD, + NULL /* no flow control possible */ + , + NULL)); + /* move on to next message, if any */ + plaintext_len -= ntohs (hdr->size); + if (plaintext_len < sizeof(*hdr)) + break; + pos += ntohs (hdr->size); + hdr = (const struct GNUNET_MessageHeader *) pos; + // TODO for now..., we do not actually sen >1msg or have a way of telling + // if we are done + break; + } + GNUNET_STATISTICS_update (stats, + "# bytes padding discarded", + plaintext_len, + GNUNET_NO); +} + + +/** + * Setup @a cipher based on shared secret @a msec and + * serial number @a serial. + * + * @param msec master shared secret + * @param serial serial number of cipher to set up + * @param cipher[out] cipher to initialize + */ +static void +setup_cipher (const struct GNUNET_HashCode *msec, + uint32_t serial, + gcry_cipher_hd_t *cipher) +{ + char key[AES_KEY_SIZE]; + char iv[AES_IV_SIZE]; + int rc; + + GNUNET_assert (0 == + gcry_cipher_open (cipher, + GCRY_CIPHER_AES256 /* low level: go for speed */, + GCRY_CIPHER_MODE_GCM, + 0 /* flags */)); + get_iv_key (msec, serial, key, iv); + rc = gcry_cipher_setkey (*cipher, key, sizeof(key)); + GNUNET_assert ((0 == rc) || ((char) rc == GPG_ERR_WEAK_KEY)); + rc = gcry_cipher_setiv (*cipher, iv, sizeof(iv)); + GNUNET_assert ((0 == rc) || ((char) rc == GPG_ERR_WEAK_KEY)); +} + + +/** + * Try to decrypt @a buf using shared secret @a ss and key/iv + * derived using @a serial. + * + * @param ss shared secret + * @param tag GCM authentication tag + * @param serial serial number to use + * @param in_buf input buffer to decrypt + * @param in_buf_size number of bytes in @a in_buf and available in @a out_buf + * @param out_buf where to write the result + * @return #GNUNET_OK on success + */ +static int +try_decrypt (const struct SharedSecret *ss, + const uint8_t *tag, + uint32_t serial, + const char *in_buf, + size_t in_buf_size, + char *out_buf) +{ + gcry_cipher_hd_t cipher; + + setup_cipher (&ss->master, serial, &cipher); + GNUNET_assert ( + 0 == + gcry_cipher_decrypt (cipher, out_buf, in_buf_size, in_buf, in_buf_size)); + if (0 != gcry_cipher_checktag (cipher, tag, GCM_TAG_SIZE)) + { + gcry_cipher_close (cipher); + GNUNET_STATISTICS_update (stats, + "# AEAD authentication failures", + 1, + GNUNET_NO); + return GNUNET_SYSERR; + } + gcry_cipher_close (cipher); + return GNUNET_OK; +} + + +/** + * Setup shared secret for decryption. + * + * @param ephemeral ephemeral key we received from the other peer + * @return new shared secret + */ +static struct SharedSecret * +setup_shared_secret_dec (const struct GNUNET_CRYPTO_EcdhePublicKey *ephemeral) +{ + struct SharedSecret *ss; + + ss = GNUNET_new (struct SharedSecret); + GNUNET_CRYPTO_eddsa_kem_decaps (my_private_key, ephemeral, &ss->master); + calculate_cmac (ss); + return ss; +} + + +/** + * Setup new shared secret for encryption using KEM. + * + * @param[out] ephemeral ephemeral key to be sent to other peer (encapsulated key from KEM) + * @param[in,out] receiver queue to initialize encryption key for + * @return new shared secret + */ +static struct SharedSecret * +setup_shared_secret_ephemeral (struct GNUNET_CRYPTO_EcdhePublicKey *ephemeral, + struct ReceiverAddress *receiver) +{ + struct SharedSecret *ss; + struct GNUNET_HashCode k; + + GNUNET_CRYPTO_eddsa_kem_encaps (&receiver->target.public_key, ephemeral, &k); + ss = GNUNET_new (struct SharedSecret); + memcpy (&ss->master, &k, sizeof (k)); + calculate_cmac (ss); + ss->receiver = receiver; + GNUNET_CONTAINER_DLL_insert (receiver->ss_head, receiver->ss_tail, ss); + receiver->num_secrets++; + GNUNET_STATISTICS_update (stats, "# Secrets active", 1, GNUNET_NO); + return ss; +} + + +/** + * Setup the MQ for the @a receiver. If a queue exists, + * the existing one is destroyed. Then the MTU is + * recalculated and a fresh queue is initialized. + * + * @param receiver receiver to setup MQ for + */ +static void +setup_receiver_mq (struct ReceiverAddress *receiver); + + +/** + * Best effort try to purge some secrets. + * Ideally those, not ACKed. + * + * @param ss_list_tail the oldest secret in the list of interest. + * @return number of deleted secrets. + */ +unsigned int +purge_secrets (struct SharedSecret *ss_list_tail) +{ + struct SharedSecret *pos; + struct SharedSecret *ss_to_purge; + unsigned int deleted = 0; + + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Purging secrets.\n"); + pos = ss_list_tail; + while (NULL != pos) + { + ss_to_purge = pos; + pos = pos->prev; + + // FIXME we may also want to purge old unacked. + if (rekey_max_bytes <= ss_to_purge->bytes_sent) + { + secret_destroy (ss_to_purge); + deleted++; + } + } + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Finished purging all, deleted %u.\n", deleted); + return deleted; +} + + +static void +add_acks (struct SharedSecret *ss, int acks_to_add) +{ + + struct ReceiverAddress *receiver = ss->receiver; + + GNUNET_assert (NULL != ss); + GNUNET_assert (NULL != receiver); + + if (NULL == receiver->d_qh) + { + receiver->d_qh = + GNUNET_TRANSPORT_communicator_mq_add (ch, + &receiver->target, + receiver->foreign_addr, + receiver->d_mtu, + acks_to_add, + 1, /* Priority */ + receiver->nt, + GNUNET_TRANSPORT_CS_OUTBOUND, + receiver->d_mq); + } + else + { + GNUNET_TRANSPORT_communicator_mq_update (ch, + receiver->d_qh, + acks_to_add, + 1); + } + + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Tell transport we have %u more acks!\n", + acks_to_add); + + // Until here for alternativ 1 + + /* move ss to head to avoid discarding it anytime soon! */ + + GNUNET_CONTAINER_DLL_remove (receiver->ss_head, receiver->ss_tail, ss); + GNUNET_CONTAINER_DLL_insert (receiver->ss_head, receiver->ss_tail, ss); +} + + +/** + * We received an ACK for @a pid. Check if it is for + * the receiver in @a value and if so, handle it and + * return #GNUNET_NO. Otherwise, return #GNUNET_YES. + * + * @param cls a `const struct UDPAck` + * @param pid peer the ACK is from + * @param value a `struct ReceiverAddress` + * @return #GNUNET_YES to continue to iterate + */ +static int +handle_ack (void *cls, const struct GNUNET_PeerIdentity *pid, void *value) +{ + const struct UDPAck *ack = cls; + struct ReceiverAddress *receiver = value; + uint32_t acks_to_add; + uint32_t allowed; + + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "in handle ack with cmac %s\n", + GNUNET_h2s (&ack->cmac)); + + (void) pid; + for (struct SharedSecret *ss = receiver->ss_head; NULL != ss; ss = ss->next) + { + if (0 == memcmp (&ack->cmac, &ss->cmac, sizeof(struct GNUNET_HashCode))) + { + + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Found matching cmac\n"); + + allowed = ntohl (ack->sequence_ack); + + if (allowed <= ss->sequence_allowed) + { + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Ignoring ack, not giving us increased window\n."); + return GNUNET_NO; + } + acks_to_add = (allowed - ss->sequence_allowed); + GNUNET_assert (0 != acks_to_add); + receiver->acks_available += (allowed - ss->sequence_allowed); + ss->sequence_allowed = allowed; + add_acks (ss, acks_to_add); + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "New sequence allows until %u (+%u). Acks available to us: %u. For secret %s\n", + allowed, + acks_to_add, + receiver->acks_available, + GNUNET_h2s (&ss->master)); + return GNUNET_NO; + } + } + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Matching cmac not found for ack!\n"); + return GNUNET_YES; +} + + +/** + * We established a shared secret with a sender. We should try to send + * the sender an `struct UDPAck` at the next opportunity to allow the + * sender to use @a ss longer (assuming we did not yet already + * recently). + * + * @param ss shared secret to generate ACKs for + */ +static void +consider_ss_ack (struct SharedSecret *ss) +{ + struct UDPAck ack; + GNUNET_assert (NULL != ss->sender); + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Considering SS UDPAck %s\n", + GNUNET_i2s_full (&ss->sender->target)); + + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Sender has %u acks available.\n", + ss->sender->acks_available); + /* drop ancient KeyCacheEntries */ + while ((NULL != ss->kce_head) && + (MAX_SQN_DELTA < + ss->kce_head->sequence_number - ss->kce_tail->sequence_number)) + kce_destroy (ss->kce_tail); + + + ack.header.type = htons (GNUNET_MESSAGE_TYPE_COMMUNICATOR_UDP_ACK); + ack.header.size = htons (sizeof(ack)); + ack.sequence_ack = htonl (ss->sequence_allowed); + ack.cmac = ss->cmac; + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Notifying transport with UDPAck %s, sequence %u and master %s\n", + GNUNET_i2s_full (&ss->sender->target), + ss->sequence_allowed, + GNUNET_h2s (&(ss->master))); + GNUNET_TRANSPORT_communicator_notify (ch, + &ss->sender->target, + COMMUNICATOR_ADDRESS_PREFIX, + &ack.header); +} + + +static void +kce_generate_cb (void *cls) +{ + struct SharedSecret *ss = cls; + ss->sender->kce_task = NULL; + + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Precomputing %u keys for master %s\n", + GENERATE_AT_ONCE, + GNUNET_h2s (&(ss->master))); + if ((ss->override_available_acks != GNUNET_YES) && + (KCN_TARGET < ss->sender->acks_available)) + return; + for (int i = 0; i < GENERATE_AT_ONCE; i++) + kce_generate (ss, ++ss->sequence_allowed); + + /** + * As long as we loose over 30% of max acks in reschedule, + * We keep generating acks for this ss. + */ + if (KCN_TARGET > ss->sender->acks_available) + { + ss->sender->kce_task = GNUNET_SCHEDULER_add_delayed ( + WORKING_QUEUE_INTERVALL, + kce_generate_cb, + ss); + return; + } + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "We have enough keys (ACKs: %u).\n", ss->sender->acks_available); + ss->sender->kce_task_finished = GNUNET_YES; + ss->override_available_acks = GNUNET_NO; + if (ss->sender->kce_send_ack_on_finish == GNUNET_YES) + consider_ss_ack (ss); +} + + +/** + * Test if we have received a valid message in plaintext. + * If so, handle it. + * + * @param sender peer to process inbound plaintext for + * @param buf buffer we received + * @param buf_size number of bytes in @a buf + */ +static void +try_handle_plaintext (struct SenderAddress *sender, + const void *buf, + size_t buf_size) +{ + const struct GNUNET_MessageHeader *hdr; + const struct UDPAck *ack; + const struct UDPRekey *rekey; + struct SharedSecret *ss_rekey; + const char *buf_pos = buf; + size_t bytes_remaining = buf_size; + uint16_t type; + + hdr = (struct GNUNET_MessageHeader*) buf_pos; + if (sizeof(*hdr) > bytes_remaining) + { + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Plaintext too short, dropping...\n"); + return; /* no data left */ + } + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "try_handle_plaintext of size %lu (%u %lu) and type %u\n", + bytes_remaining, + ntohs (hdr->size), + sizeof(*hdr), + ntohs (hdr->type)); + if (ntohs (hdr->size) > bytes_remaining) + return; /* buffer too short for indicated message length */ + type = ntohs (hdr->type); + switch (type) + { + case GNUNET_MESSAGE_TYPE_COMMUNICATOR_UDP_REKEY: + rekey = (struct UDPRekey*) buf_pos; + ss_rekey = setup_shared_secret_dec (&rekey->ephemeral); + ss_rekey->sender = sender; + GNUNET_CONTAINER_DLL_insert (sender->ss_head, sender->ss_tail, ss_rekey); + sender->num_secrets++; + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "Received rekey secret with cmac %s\n", + GNUNET_h2s (&(ss_rekey->cmac))); + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Received secret with master %s.\n", + GNUNET_h2s (&(ss_rekey->master))); + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "We have %u sequence_allowed.\n", + ss_rekey->sequence_allowed); + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "We have a sender %p\n", + ss_rekey->sender); + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "We have %u acks available.\n", + ss_rekey->sender->acks_available); + ss_rekey->sender->kce_send_ack_on_finish = GNUNET_YES; + ss_rekey->override_available_acks = GNUNET_YES; + // FIXME + ss_rekey->sender->kce_task = GNUNET_SCHEDULER_add_delayed ( + WORKING_QUEUE_INTERVALL, + kce_generate_cb, + ss_rekey); + // FIXME: Theoretically, this could be an Ack + buf_pos += ntohs (hdr->size); + bytes_remaining -= ntohs (hdr->size); + pass_plaintext_to_core (sender, buf_pos, bytes_remaining); + if (sender->num_secrets > MAX_SECRETS) + { + if (0 == purge_secrets (sender->ss_tail)) + { + // No secret purged. Delete oldest. + secret_destroy (sender->ss_tail); + } + } + break; + case GNUNET_MESSAGE_TYPE_COMMUNICATOR_UDP_ACK: + /* lookup master secret by 'cmac', then update sequence_max */ + ack = (struct UDPAck*) buf_pos; + GNUNET_CONTAINER_multipeermap_get_multiple (receivers, + &sender->target, + &handle_ack, + (void *) ack); + /* There could be more messages after the ACK, handle those as well */ + buf_pos += ntohs (hdr->size); + bytes_remaining -= ntohs (hdr->size); + pass_plaintext_to_core (sender, buf_pos, bytes_remaining); + break; + + case GNUNET_MESSAGE_TYPE_COMMUNICATOR_UDP_PAD: + /* skip padding */ + break; + + default: + pass_plaintext_to_core (sender, buf_pos, bytes_remaining); + } + return; +} + + +/** + * We received a @a box with matching @a kce. Decrypt and process it. + * + * @param box the data we received + * @param box_len number of bytes in @a box + * @param kce key index to decrypt @a box + */ +static void +decrypt_box (const struct UDPBox *box, + size_t box_len, + struct KeyCacheEntry *kce) +{ + struct SharedSecret *ss = kce->ss; + char out_buf[box_len - sizeof(*box)]; + + GNUNET_assert (NULL != ss->sender); + if (GNUNET_OK != try_decrypt (ss, + box->gcm_tag, + kce->sequence_number, + (const char *) &box[1], + sizeof(out_buf), + out_buf)) + { + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Failed decryption.\n"); + GNUNET_STATISTICS_update (stats, + "# Decryption failures with valid KCE", + 1, + GNUNET_NO); + kce_destroy (kce); + return; + } + kce_destroy (kce); + kce = NULL; + GNUNET_STATISTICS_update (stats, + "# bytes decrypted with BOX", + sizeof(out_buf), + GNUNET_NO); + GNUNET_STATISTICS_update (stats, + "# messages decrypted with BOX", + 1, + GNUNET_NO); + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "decrypted UDPBox with kid %s\n", + GNUNET_sh2s (&box->kid)); + try_handle_plaintext (ss->sender, out_buf, sizeof(out_buf)); + if ((KCN_THRESHOLD > ss->sender->acks_available) && + (NULL == ss->sender->kce_task) && + (GNUNET_YES == ss->sender->kce_task_finished)) + { + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Sender has %u ack left which is under threshold.\n", + ss->sender->acks_available); + ss->sender->kce_send_ack_on_finish = GNUNET_YES; + ss->sender->kce_task = GNUNET_SCHEDULER_add_now ( + kce_generate_cb, + ss); + } +} + + +/** + * Closure for #find_sender_by_address() + */ +struct SearchContext +{ + /** + * Address we are looking for. + */ + const struct sockaddr *address; + + /** + * Number of bytes in @e address. + */ + socklen_t address_len; + + /** + * Return value to set if we found a match. + */ + struct SenderAddress *sender; +}; + + +/** + * Find existing `struct SenderAddress` by matching addresses. + * + * @param cls a `struct SearchContext` + * @param key ignored, must match already + * @param value a `struct SenderAddress` + * @return #GNUNET_YES if not found (continue to search), #GNUNET_NO if found + */ +static int +find_sender_by_address (void *cls, + const struct GNUNET_PeerIdentity *key, + void *value) +{ + struct SearchContext *sc = cls; + struct SenderAddress *sender = value; + + if ((sender->address_len == sc->address_len) && + (0 == memcmp (sender->address, sc->address, sender->address_len))) + { + sc->sender = sender; + return GNUNET_NO; /* stop iterating! */ + } + return GNUNET_YES; +} + + +/** + * Create sender address for @a target. Note that we + * might already have one, so a fresh one is only allocated + * if one does not yet exist for @a address. + * + * @param target peer to generate address for + * @param address target address + * @param address_len number of bytes in @a address + * @return data structure to keep track of key material for + * decrypting data from @a target + */ +static struct SenderAddress * +setup_sender (const struct GNUNET_PeerIdentity *target, + const struct sockaddr *address, + socklen_t address_len) +{ + struct SenderAddress *sender; + struct SearchContext sc = { .address = address, + .address_len = address_len, + .sender = NULL }; + + GNUNET_CONTAINER_multipeermap_get_multiple (senders, + target, + &find_sender_by_address, + &sc); + if (NULL != sc.sender) + { + reschedule_sender_timeout (sc.sender); + return sc.sender; + } + sender = GNUNET_new (struct SenderAddress); + sender->target = *target; + sender->address = GNUNET_memdup (address, address_len); + sender->address_len = address_len; + (void) GNUNET_CONTAINER_multipeermap_put ( + senders, + &sender->target, + sender, + GNUNET_CONTAINER_MULTIHASHMAPOPTION_MULTIPLE); + GNUNET_STATISTICS_set (stats, + "# senders active", + GNUNET_CONTAINER_multipeermap_size (receivers), + GNUNET_NO); + sender->timeout = + GNUNET_TIME_relative_to_absolute (GNUNET_CONSTANTS_IDLE_CONNECTION_TIMEOUT); + sender->hn = GNUNET_CONTAINER_heap_insert (senders_heap, + sender, + sender->timeout.abs_value_us); + sender->nt = GNUNET_NT_scanner_get_type (is, address, address_len); + if (NULL == timeout_task) + timeout_task = GNUNET_SCHEDULER_add_now (&check_timeouts, NULL); + return sender; +} + + +/** + * Check signature from @a uc against @a ephemeral. + * + * @param ephemeral key that is signed + * @param uc signature of claimant + * @return #GNUNET_OK if signature is valid + */ +static int +verify_confirmation (const struct GNUNET_CRYPTO_EcdhePublicKey *ephemeral, + const struct UDPConfirmation *uc) +{ + struct UdpHandshakeSignature uhs; + + uhs.purpose.purpose = htonl ( + GNUNET_SIGNATURE_PURPOSE_COMMUNICATOR_UDP_HANDSHAKE); + uhs.purpose.size = htonl (sizeof(uhs)); + uhs.sender = uc->sender; + uhs.receiver = my_identity; + uhs.ephemeral = *ephemeral; + uhs.monotonic_time = uc->monotonic_time; + return GNUNET_CRYPTO_eddsa_verify ( + GNUNET_SIGNATURE_PURPOSE_COMMUNICATOR_UDP_HANDSHAKE, + &uhs, + &uc->sender_sig, + &uc->sender.public_key); +} + + +/** + * Converts @a address to the address string format used by this + * communicator in HELLOs. + * + * @param address the address to convert, must be AF_INET or AF_INET6. + * @param address_len number of bytes in @a address + * @return string representation of @a address + */ +static char * +sockaddr_to_udpaddr_string (const struct sockaddr *address, + socklen_t address_len) +{ + char *ret; + + switch (address->sa_family) + { + case AF_INET: + GNUNET_asprintf (&ret, + "%s-%s", + COMMUNICATOR_ADDRESS_PREFIX, + GNUNET_a2s (address, address_len)); + break; + + case AF_INET6: + GNUNET_asprintf (&ret, + "%s-%s", + COMMUNICATOR_ADDRESS_PREFIX, + GNUNET_a2s (address, address_len)); + break; + + default: + GNUNET_assert (0); + } + return ret; +} + + +/** + * Socket read task. + * + * @param cls NULL + */ +static void +sock_read (void *cls) +{ + struct sockaddr_storage sa; + struct sockaddr_in *addr_verify; + socklen_t salen = sizeof(sa); + char buf[UINT16_MAX]; + ssize_t rcvd; + + (void) cls; + read_task = GNUNET_SCHEDULER_add_read_net (GNUNET_TIME_UNIT_FOREVER_REL, + udp_sock, + &sock_read, + NULL); + while (1) + { + rcvd = GNUNET_NETWORK_socket_recvfrom (udp_sock, + buf, + sizeof(buf), + (struct sockaddr *) &sa, + &salen); + if (-1 == rcvd) + { + if (EAGAIN == errno) + break; // We are done reading data + GNUNET_log_strerror (GNUNET_ERROR_TYPE_DEBUG, "recv"); + return; + } + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Read %lu bytes\n", rcvd); + + /* first, see if it is a UDPBox */ + if (rcvd > sizeof(struct UDPBox)) + { + const struct UDPBox *box; + struct KeyCacheEntry *kce; + + box = (const struct UDPBox *) buf; + kce = GNUNET_CONTAINER_multishortmap_get (key_cache, &box->kid); + if (NULL != kce) + { + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Found KCE with kid %s\n", + GNUNET_sh2s (&box->kid)); + decrypt_box (box, (size_t) rcvd, kce); + continue; + } + } + + /* next, check if it is a broadcast */ + if (sizeof(struct UDPBroadcast) == rcvd) + { + const struct UDPBroadcast *ub; + struct UdpBroadcastSignature uhs; + struct GNUNET_PeerIdentity sender; + + addr_verify = GNUNET_memdup (&sa, salen); + addr_verify->sin_port = 0; + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "received UDPBroadcast from %s\n", + GNUNET_a2s ((const struct sockaddr *) addr_verify, salen)); + ub = (const struct UDPBroadcast *) buf; + uhs.purpose.purpose = htonl ( + GNUNET_SIGNATURE_PURPOSE_COMMUNICATOR_UDP_BROADCAST); + uhs.purpose.size = htonl (sizeof(uhs)); + uhs.sender = ub->sender; + sender = ub->sender; + if (0 == memcmp (&sender, &my_identity, sizeof (struct + GNUNET_PeerIdentity))) + { + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Received our own broadcast\n"); + GNUNET_free (addr_verify); + continue; + } + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "checking UDPBroadcastSignature for %s\n", + GNUNET_i2s (&sender)); + GNUNET_CRYPTO_hash ((struct sockaddr *) addr_verify, salen, + &uhs.h_address); + if (GNUNET_OK == + GNUNET_CRYPTO_eddsa_verify ( + GNUNET_SIGNATURE_PURPOSE_COMMUNICATOR_UDP_BROADCAST, + &uhs, + &ub->sender_sig, + &ub->sender.public_key)) + { + char *addr_s; + enum GNUNET_NetworkType nt; + + addr_s = + sockaddr_to_udpaddr_string ((const struct sockaddr *) &sa, salen); + GNUNET_STATISTICS_update (stats, "# broadcasts received", 1, GNUNET_NO); + /* use our own mechanism to determine network type */ + nt = + GNUNET_NT_scanner_get_type (is, (const struct sockaddr *) &sa, salen); + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "validating address %s received from UDPBroadcast\n", + GNUNET_i2s (&sender)); + GNUNET_TRANSPORT_application_validate (ah, &sender, nt, addr_s); + GNUNET_free (addr_s); + GNUNET_free (addr_verify); + continue; + } + else + { + GNUNET_log (GNUNET_ERROR_TYPE_WARNING, + "VerifyingPeer %s is verifying UDPBroadcast\n", + GNUNET_i2s (&my_identity)); + GNUNET_log (GNUNET_ERROR_TYPE_WARNING, + "Verifying UDPBroadcast from %s failed\n", + GNUNET_i2s (&ub->sender)); + } + GNUNET_free (addr_verify); + /* continue with KX, mostly for statistics... */ + } + + + /* finally, test if it is a KX */ + if (rcvd < sizeof(struct UDPConfirmation) + sizeof(struct InitialKX)) + { + GNUNET_STATISTICS_update (stats, + "# messages dropped (no kid, too small for KX)", + 1, + GNUNET_NO); + continue; + } + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Got KX\n"); + { + const struct InitialKX *kx; + struct SharedSecret *ss; + char pbuf[rcvd - sizeof(struct InitialKX)]; + const struct UDPConfirmation *uc; + struct SenderAddress *sender; + + kx = (const struct InitialKX *) buf; + ss = setup_shared_secret_dec (&kx->ephemeral); + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Before DEC\n"); + + if (GNUNET_OK != try_decrypt (ss, + kx->gcm_tag, + 0, + &buf[sizeof(*kx)], + sizeof(pbuf), + pbuf)) + { + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Unable to decrypt tag, dropping...\n"); + GNUNET_free (ss); + GNUNET_STATISTICS_update ( + stats, + "# messages dropped (no kid, AEAD decryption failed)", + 1, + GNUNET_NO); + continue; + } + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Before VERIFY\n"); + + uc = (const struct UDPConfirmation *) pbuf; + if (GNUNET_OK != verify_confirmation (&kx->ephemeral, uc)) + { + GNUNET_break_op (0); + GNUNET_free (ss); + GNUNET_STATISTICS_update (stats, + "# messages dropped (sender signature invalid)", + 1, + GNUNET_NO); + continue; + } + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Before SETUP_SENDER\n"); + + calculate_cmac (ss); + sender = setup_sender (&uc->sender, (const struct sockaddr *) &sa, salen); + ss->sender = sender; + GNUNET_CONTAINER_DLL_insert (sender->ss_head, sender->ss_tail, ss); + if ((KCN_THRESHOLD > ss->sender->acks_available) && + (NULL == ss->sender->kce_task) && + (GNUNET_NO == ss->sender->kce_task_finished)) + { + // TODO This task must be per sender! FIXME: This is a nice todo, but I do not know what must be done here to fix. + ss->sender->kce_send_ack_on_finish = GNUNET_YES; + ss->sender->kce_task = GNUNET_SCHEDULER_add_now ( + kce_generate_cb, + ss); + } + sender->num_secrets++; + GNUNET_STATISTICS_update (stats, "# Secrets active", 1, GNUNET_NO); + GNUNET_STATISTICS_update (stats, + "# messages decrypted without BOX", + 1, + GNUNET_NO); + try_handle_plaintext (sender, &uc[1], sizeof(pbuf) - sizeof(*uc)); + if (sender->num_secrets > MAX_SECRETS) + { + if (0 == purge_secrets (sender->ss_tail)) + { + // No secret purged. Delete oldest. + secret_destroy (sender->ss_tail); + } + } + } + } +} + + +/** + * Convert UDP bind specification to a `struct sockaddr *` + * + * @param bindto bind specification to convert + * @param[out] sock_len set to the length of the address + * @return converted bindto specification + */ +static struct sockaddr * +udp_address_to_sockaddr (const char *bindto, socklen_t *sock_len) +{ + struct sockaddr *in; + unsigned int port; + char dummy[2]; + char *colon; + char *cp; + + if (1 == sscanf (bindto, "%u%1s", &port, dummy)) + { + /* interpreting value as just a PORT number */ + if (port > UINT16_MAX) + { + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "BINDTO specification `%s' invalid: value too large for port\n", + bindto); + return NULL; + } + if ((GNUNET_NO == GNUNET_NETWORK_test_pf (PF_INET6)) || + (GNUNET_YES == + GNUNET_CONFIGURATION_get_value_yesno (cfg, + COMMUNICATOR_CONFIG_SECTION, + "DISABLE_V6"))) + { + struct sockaddr_in *i4; + + i4 = GNUNET_malloc (sizeof(struct sockaddr_in)); + i4->sin_family = AF_INET; + i4->sin_port = htons ((uint16_t) port); + *sock_len = sizeof(struct sockaddr_in); + in = (struct sockaddr *) i4; + } + else + { + struct sockaddr_in6 *i6; + + i6 = GNUNET_malloc (sizeof(struct sockaddr_in6)); + i6->sin6_family = AF_INET6; + i6->sin6_port = htons ((uint16_t) port); + *sock_len = sizeof(struct sockaddr_in6); + in = (struct sockaddr *) i6; + } + return in; + } + cp = GNUNET_strdup (bindto); + colon = strrchr (cp, ':'); + if (NULL != colon) + { + /* interpret value after colon as port */ + *colon = '\0'; + colon++; + if (1 == sscanf (colon, "%u%1s", &port, dummy)) + { + /* interpreting value as just a PORT number */ + if (port > UINT16_MAX) + { + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "BINDTO specification `%s' invalid: value too large for port\n", + bindto); + GNUNET_free (cp); + return NULL; + } + } + else + { + GNUNET_log ( + GNUNET_ERROR_TYPE_ERROR, + "BINDTO specification `%s' invalid: last ':' not followed by number\n", + bindto); + GNUNET_free (cp); + return NULL; + } + } + else + { + /* interpret missing port as 0, aka pick any free one */ + port = 0; + } + { + /* try IPv4 */ + struct sockaddr_in v4; + + memset (&v4, 0, sizeof(v4)); + if (1 == inet_pton (AF_INET, cp, &v4.sin_addr)) + { + v4.sin_family = AF_INET; + v4.sin_port = htons ((uint16_t) port); +#if HAVE_SOCKADDR_IN_SIN_LEN + v4.sin_len = sizeof(struct sockaddr_in); +#endif + in = GNUNET_memdup (&v4, sizeof(struct sockaddr_in)); + *sock_len = sizeof(struct sockaddr_in); + GNUNET_free (cp); + return in; + } + } + { + /* try IPv6 */ + struct sockaddr_in6 v6; + const char *start; + + memset (&v6, 0, sizeof(v6)); + start = cp; + if (('[' == *cp) && (']' == cp[strlen (cp) - 1])) + { + start++; /* skip over '[' */ + cp[strlen (cp) - 1] = '\0'; /* eat ']' */ + } + if (1 == inet_pton (AF_INET6, start, &v6.sin6_addr)) + { + v6.sin6_family = AF_INET6; + v6.sin6_port = htons ((uint16_t) port); +#if HAVE_SOCKADDR_IN_SIN_LEN + v6.sin6_len = sizeof(sizeof(struct sockaddr_in6)); +#endif + in = GNUNET_memdup (&v6, sizeof(v6)); + *sock_len = sizeof(v6); + GNUNET_free (cp); + return in; + } + } + /* #5528 FIXME (feature!): maybe also try getnameinfo()? */ + GNUNET_free (cp); + return NULL; +} + + +/** + * Pad @a dgram by @a pad_size using @a out_cipher. + * + * @param out_cipher cipher to use + * @param dgram datagram to pad + * @param pad_size number of bytes of padding to append + */ +static void +do_pad (gcry_cipher_hd_t out_cipher, char *dgram, size_t pad_size) +{ + char pad[pad_size]; + + GNUNET_CRYPTO_random_block (GNUNET_CRYPTO_QUALITY_WEAK, pad, sizeof(pad)); + if (sizeof(pad) > sizeof(struct GNUNET_MessageHeader)) + { + struct GNUNET_MessageHeader hdr = + { .size = htons (sizeof(pad)), + .type = htons (GNUNET_MESSAGE_TYPE_COMMUNICATOR_UDP_PAD) }; + + memcpy (pad, &hdr, sizeof(hdr)); + } + GNUNET_assert ( + 0 == + gcry_cipher_encrypt (out_cipher, dgram, sizeof(pad), pad, sizeof(pad))); +} + + +static void +send_msg_with_kx (const struct GNUNET_MessageHeader *msg, struct + ReceiverAddress *receiver) +{ + uint16_t msize = ntohs (msg->size); + struct UdpHandshakeSignature uhs; + struct UDPConfirmation uc; + struct InitialKX kx; + char dgram[receiver->kx_mtu + sizeof(uc) + sizeof(kx)]; + size_t dpos; + gcry_cipher_hd_t out_cipher; + struct SharedSecret *ss; + + if (msize > receiver->kx_mtu) + { + GNUNET_break (0); + if (GNUNET_YES != receiver->receiver_destroy_called) + receiver_destroy (receiver); + return; + } + reschedule_receiver_timeout (receiver); + + /* setup key material */ + + ss = setup_shared_secret_ephemeral (&uhs.ephemeral, receiver); + + if (receiver->num_secrets > MAX_SECRETS) + { + if (0 == purge_secrets (receiver->ss_tail)) + { + // No secret purged. Delete oldest. + secret_destroy (receiver->ss_tail); + } + } + + setup_cipher (&ss->master, 0, &out_cipher); + /* compute 'uc' */ + uc.sender = my_identity; + uc.monotonic_time = + GNUNET_TIME_absolute_hton (GNUNET_TIME_absolute_get_monotonic (cfg)); + uhs.purpose.purpose = htonl ( + GNUNET_SIGNATURE_PURPOSE_COMMUNICATOR_UDP_HANDSHAKE); + uhs.purpose.size = htonl (sizeof(uhs)); + uhs.sender = my_identity; + uhs.receiver = receiver->target; + uhs.monotonic_time = uc.monotonic_time; + GNUNET_CRYPTO_eddsa_sign (my_private_key, + &uhs, + &uc.sender_sig); + /* Leave space for kx */ + dpos = sizeof(kx); + /* Append encrypted uc to dgram */ + GNUNET_assert (0 == gcry_cipher_encrypt (out_cipher, + &dgram[dpos], + sizeof(uc), + &uc, + sizeof(uc))); + dpos += sizeof(uc); + /* Append encrypted payload to dgram */ + GNUNET_assert ( + 0 == gcry_cipher_encrypt (out_cipher, &dgram[dpos], msize, msg, msize)); + dpos += msize; + do_pad (out_cipher, &dgram[dpos], sizeof(dgram) - dpos); + /* Datagram starts with kx */ + kx.ephemeral = uhs.ephemeral; + GNUNET_assert ( + 0 == gcry_cipher_gettag (out_cipher, kx.gcm_tag, sizeof(kx.gcm_tag))); + gcry_cipher_close (out_cipher); + memcpy (dgram, &kx, sizeof(kx)); + if (-1 == GNUNET_NETWORK_socket_sendto (udp_sock, + dgram, + sizeof(dgram), + receiver->address, + receiver->address_len)) + GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING, "send"); + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Sending KX with payload size %u to %s\n", + msize, + GNUNET_a2s (receiver->address, + receiver->address_len)); +} + + +/** + * Signature of functions implementing the sending functionality of a + * message queue. + * + * @param mq the message queue + * @param msg the message to send + * @param impl_state our `struct ReceiverAddress` + */ +static void +mq_send_kx (struct GNUNET_MQ_Handle *mq, + const struct GNUNET_MessageHeader *msg, + void *impl_state) +{ + struct ReceiverAddress *receiver = impl_state; + + GNUNET_assert (mq == receiver->kx_mq); + send_msg_with_kx (msg, receiver); + GNUNET_MQ_impl_send_continue (mq); +} + + +static void +create_rekey (struct ReceiverAddress *receiver, struct SharedSecret *ss, struct + UDPRekey *rekey) +{ + struct SharedSecret *ss_rekey; + + ss->rekey_initiated = GNUNET_YES; + /* setup key material */ + ss_rekey = setup_shared_secret_ephemeral (&rekey->ephemeral, + receiver); + ss_rekey->sequence_allowed = 0; + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Setup secret with k = %s\n", + GNUNET_h2s (&(ss_rekey->master))); + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Setup secret with H(k) = %s\n", + GNUNET_h2s (&(ss_rekey->cmac))); + + /* Append encrypted payload to dgram */ + rekey->header.type = htons (GNUNET_MESSAGE_TYPE_COMMUNICATOR_UDP_REKEY); + rekey->header.size = htons (sizeof (struct UDPRekey)); +} + + +/** + * Signature of functions implementing the sending functionality of a + * message queue. + * + * @param mq the message queue + * @param msg the message to send + * @param impl_state our `struct ReceiverAddress` + */ +static void +mq_send_d (struct GNUNET_MQ_Handle *mq, + const struct GNUNET_MessageHeader *msg, + void *impl_state) +{ + struct ReceiverAddress *receiver = impl_state; + struct UDPRekey rekey; + struct SharedSecret *ss; + int inject_rekey = GNUNET_NO; + uint16_t msize = ntohs (msg->size); + + GNUNET_assert (mq == receiver->d_mq); + if ((msize > receiver->d_mtu) || + (0 == receiver->acks_available)) + { + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "msize: %u, mtu: %lu, acks: %u\n", + msize, + receiver->d_mtu, + receiver->acks_available); + + GNUNET_break (0); + if (GNUNET_YES != receiver->receiver_destroy_called) + receiver_destroy (receiver); + return; + } + reschedule_receiver_timeout (receiver); + + if (receiver->num_secrets > MAX_SECRETS) + { + if (0 == purge_secrets (receiver->ss_tail)) + { + // No secret purged. Delete oldest. + secret_destroy (receiver->ss_tail); + } + } + /* begin "BOX" encryption method, scan for ACKs from tail! */ + for (ss = receiver->ss_tail; NULL != ss; ss = ss->prev) + { + size_t payload_len = sizeof(struct UDPBox) + receiver->d_mtu; + if (ss->sequence_used >= ss->sequence_allowed) + { + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Skipping ss because no acks to use.\n"); + continue; + } + if (ss->bytes_sent >= rekey_max_bytes) + { + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Skipping ss because rekey bytes reached.\n"); + // FIXME cleanup ss with too many bytes sent! + continue; + } + if (ss->bytes_sent > rekey_max_bytes * 0.7) + { + if (ss->rekey_initiated == GNUNET_NO) + { + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "Injecting rekey for ss with byte sent %lu\n", + (unsigned long) ss->bytes_sent); + create_rekey (receiver, ss, &rekey); + inject_rekey = GNUNET_YES; + payload_len += sizeof (rekey); + ss->rekey_initiated = GNUNET_YES; + } + } + if (0 < ss->sequence_used) + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Trying to send UDPBox with shared secrect %s sequence_used %u and ss->sequence_allowed %u\n", + GNUNET_h2s (&ss->master), + ss->sequence_used, + ss->sequence_allowed); + + char dgram[payload_len]; + struct UDPBox *box; + gcry_cipher_hd_t out_cipher; + size_t dpos; + + box = (struct UDPBox *) dgram; + ss->sequence_used++; + get_kid (&ss->master, ss->sequence_used, &box->kid); + setup_cipher (&ss->master, ss->sequence_used, &out_cipher); + /* Append encrypted payload to dgram */ + dpos = sizeof(struct UDPBox); + if (GNUNET_YES == inject_rekey) + { + GNUNET_assert ( + 0 == gcry_cipher_encrypt (out_cipher, &dgram[dpos], sizeof (rekey), + &rekey, sizeof (rekey))); + dpos += sizeof (rekey); + } + GNUNET_assert ( + 0 == gcry_cipher_encrypt (out_cipher, &dgram[dpos], msize, msg, msize)); + dpos += msize; + do_pad (out_cipher, &dgram[dpos], sizeof(dgram) - dpos); + GNUNET_assert (0 == gcry_cipher_gettag (out_cipher, + box->gcm_tag, + sizeof(box->gcm_tag))); + gcry_cipher_close (out_cipher); + + if (-1 == GNUNET_NETWORK_socket_sendto (udp_sock, + dgram, + payload_len, // FIXME why always send sizeof dgram? + receiver->address, + receiver->address_len)) + GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING, "send"); + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Sending UDPBox with payload size %u, %u acks left, %lu bytes sent\n", + msize, + receiver->acks_available, + (unsigned long) ss->bytes_sent); + ss->bytes_sent += sizeof (dgram); + receiver->acks_available--; + GNUNET_MQ_impl_send_continue (mq); + return; + } + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "No suitable ss found, sending as KX...\n"); + send_msg_with_kx (msg, receiver); + GNUNET_MQ_impl_send_continue (mq); +} + + +/** + * Signature of functions implementing the destruction of a message + * queue. Implementations must not free @a mq, but should take care + * of @a impl_state. + * + * @param mq the message queue to destroy + * @param impl_state our `struct ReceiverAddress` + */ +static void +mq_destroy_d (struct GNUNET_MQ_Handle *mq, void *impl_state) +{ + struct ReceiverAddress *receiver = impl_state; + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Default MQ destroyed\n"); + if (mq == receiver->d_mq) + { + receiver->d_mq = NULL; + if (GNUNET_YES != receiver->receiver_destroy_called) + receiver_destroy (receiver); + } +} + + +/** + * Signature of functions implementing the destruction of a message + * queue. Implementations must not free @a mq, but should take care + * of @a impl_state. + * + * @param mq the message queue to destroy + * @param impl_state our `struct ReceiverAddress` + */ +static void +mq_destroy_kx (struct GNUNET_MQ_Handle *mq, void *impl_state) +{ + struct ReceiverAddress *receiver = impl_state; + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "KX MQ destroyed\n"); + if (mq == receiver->kx_mq) + { + receiver->kx_mq = NULL; + if (GNUNET_YES != receiver->receiver_destroy_called) + receiver_destroy (receiver); + } +} + + +/** + * Implementation function that cancels the currently sent message. + * + * @param mq message queue + * @param impl_state our `struct RecvierAddress` + */ +static void +mq_cancel (struct GNUNET_MQ_Handle *mq, void *impl_state) +{ + /* Cancellation is impossible with UDP; bail */ + GNUNET_assert (0); +} + + +/** + * Generic error handler, called with the appropriate + * error code and the same closure specified at the creation of + * the message queue. + * Not every message queue implementation supports an error handler. + * + * @param cls our `struct ReceiverAddress` + * @param error error code + */ +static void +mq_error (void *cls, enum GNUNET_MQ_Error error) +{ + struct ReceiverAddress *receiver = cls; + + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "MQ error in queue to %s: %d\n", + GNUNET_i2s (&receiver->target), + (int) error); + receiver_destroy (receiver); +} + + +/** + * Setup the MQ for the @a receiver. If a queue exists, + * the existing one is destroyed. Then the MTU is + * recalculated and a fresh queue is initialized. + * + * @param receiver receiver to setup MQ for + */ +static void +setup_receiver_mq (struct ReceiverAddress *receiver) +{ + size_t base_mtu; + + switch (receiver->address->sa_family) + { + case AF_INET: + base_mtu = 1480 /* Ethernet MTU, 1500 - Ethernet header - VLAN tag */ + - sizeof(struct GNUNET_TUN_IPv4Header) /* 20 */ + - sizeof(struct GNUNET_TUN_UdpHeader) /* 8 */; + break; + + case AF_INET6: + base_mtu = 1280 /* Minimum MTU required by IPv6 */ + - sizeof(struct GNUNET_TUN_IPv6Header) /* 40 */ + - sizeof(struct GNUNET_TUN_UdpHeader) /* 8 */; + break; + + default: + GNUNET_assert (0); + break; + } + /* MTU based on full KX messages */ + receiver->kx_mtu = base_mtu - sizeof(struct InitialKX) /* 48 */ + - sizeof(struct UDPConfirmation); /* 104 */ + /* MTU based on BOXed messages */ + receiver->d_mtu = base_mtu - sizeof(struct UDPBox); + + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Setting up MQs and QHs\n"); + /* => Effective MTU for CORE will range from 1080 (IPv6 + KX) to + 1404 (IPv4 + Box) bytes, depending on circumstances... */ + if (NULL == receiver->kx_mq) + receiver->kx_mq = GNUNET_MQ_queue_for_callbacks (&mq_send_kx, + &mq_destroy_kx, + &mq_cancel, + receiver, + NULL, + &mq_error, + receiver); + if (NULL == receiver->d_mq) + receiver->d_mq = GNUNET_MQ_queue_for_callbacks (&mq_send_d, + &mq_destroy_d, + &mq_cancel, + receiver, + NULL, + &mq_error, + receiver); + + receiver->kx_qh = + GNUNET_TRANSPORT_communicator_mq_add (ch, + &receiver->target, + receiver->foreign_addr, + receiver->kx_mtu, + GNUNET_TRANSPORT_QUEUE_LENGTH_UNLIMITED, + 0, /* Priority */ + receiver->nt, + GNUNET_TRANSPORT_CS_OUTBOUND, + receiver->kx_mq); +} + + +/** + * Function called by the transport service to initialize a + * message queue given address information about another peer. + * If and when the communication channel is established, the + * communicator must call #GNUNET_TRANSPORT_communicator_mq_add() + * to notify the service that the channel is now up. It is + * the responsibility of the communicator to manage sane + * retries and timeouts for any @a peer/@a address combination + * provided by the transport service. Timeouts and retries + * do not need to be signalled to the transport service. + * + * @param cls closure + * @param peer identity of the other peer + * @param address where to send the message, human-readable + * communicator-specific format, 0-terminated, UTF-8 + * @return #GNUNET_OK on success, #GNUNET_SYSERR if the provided address is + * invalid + */ +static int +mq_init (void *cls, const struct GNUNET_PeerIdentity *peer, const char *address) +{ + struct ReceiverAddress *receiver; + const char *path; + struct sockaddr *in; + socklen_t in_len; + + if (0 != strncmp (address, + COMMUNICATOR_ADDRESS_PREFIX "-", + strlen (COMMUNICATOR_ADDRESS_PREFIX "-"))) + { + GNUNET_break_op (0); + return GNUNET_SYSERR; + } + path = &address[strlen (COMMUNICATOR_ADDRESS_PREFIX "-")]; + in = udp_address_to_sockaddr (path, &in_len); + + receiver = GNUNET_new (struct ReceiverAddress); + receiver->address = in; + receiver->address_len = in_len; + receiver->target = *peer; + receiver->nt = GNUNET_NT_scanner_get_type (is, in, in_len); + (void) GNUNET_CONTAINER_multipeermap_put ( + receivers, + &receiver->target, + receiver, + GNUNET_CONTAINER_MULTIHASHMAPOPTION_MULTIPLE); + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Added %s to receivers\n", + GNUNET_i2s_full (&receiver->target)); + receiver->timeout = + GNUNET_TIME_relative_to_absolute (GNUNET_CONSTANTS_IDLE_CONNECTION_TIMEOUT); + receiver->hn = GNUNET_CONTAINER_heap_insert (receivers_heap, + receiver, + receiver->timeout.abs_value_us); + GNUNET_STATISTICS_set (stats, + "# receivers active", + GNUNET_CONTAINER_multipeermap_size (receivers), + GNUNET_NO); + receiver->foreign_addr = + sockaddr_to_udpaddr_string (receiver->address, receiver->address_len); + setup_receiver_mq (receiver); + if (NULL == timeout_task) + timeout_task = GNUNET_SCHEDULER_add_now (&check_timeouts, NULL); + return GNUNET_OK; +} + + +/** + * Iterator over all receivers to clean up. + * + * @param cls NULL + * @param target unused + * @param value the queue to destroy + * @return #GNUNET_OK to continue to iterate + */ +static int +get_receiver_delete_it (void *cls, + const struct GNUNET_PeerIdentity *target, + void *value) +{ + struct ReceiverAddress *receiver = value; + + (void) cls; + (void) target; + receiver_destroy (receiver); + return GNUNET_OK; +} + + +/** + * Iterator over all senders to clean up. + * + * @param cls NULL + * @param target unused + * @param value the queue to destroy + * @return #GNUNET_OK to continue to iterate + */ +static int +get_sender_delete_it (void *cls, + const struct GNUNET_PeerIdentity *target, + void *value) +{ + struct SenderAddress *sender = value; + + (void) cls; + (void) target; + + + sender_destroy (sender); + return GNUNET_OK; +} + + +/** + * Shutdown the UNIX communicator. + * + * @param cls NULL (always) + */ +static void +do_shutdown (void *cls) +{ + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "do_shutdown\n"); + if (NULL != nat) + { + GNUNET_NAT_unregister (nat); + nat = NULL; + } + while (NULL != bi_head) + bi_destroy (bi_head); + if (NULL != broadcast_task) + { + GNUNET_SCHEDULER_cancel (broadcast_task); + broadcast_task = NULL; + } + if (NULL != timeout_task) + { + GNUNET_SCHEDULER_cancel (timeout_task); + timeout_task = NULL; + } + if (NULL != read_task) + { + GNUNET_SCHEDULER_cancel (read_task); + read_task = NULL; + } + if (NULL != udp_sock) + { + GNUNET_break (GNUNET_OK == + GNUNET_NETWORK_socket_close (udp_sock)); + udp_sock = NULL; + } + GNUNET_CONTAINER_multipeermap_iterate (receivers, + &get_receiver_delete_it, + NULL); + GNUNET_CONTAINER_multipeermap_destroy (receivers); + GNUNET_CONTAINER_multipeermap_iterate (senders, + &get_sender_delete_it, + NULL); + GNUNET_CONTAINER_multipeermap_destroy (senders); + GNUNET_CONTAINER_multishortmap_destroy (key_cache); + GNUNET_CONTAINER_heap_destroy (senders_heap); + GNUNET_CONTAINER_heap_destroy (receivers_heap); + if (NULL != timeout_task) + { + GNUNET_SCHEDULER_cancel (timeout_task); + timeout_task = NULL; + } + if (NULL != ch) + { + GNUNET_TRANSPORT_communicator_disconnect (ch); + ch = NULL; + } + if (NULL != ah) + { + GNUNET_TRANSPORT_application_done (ah); + ah = NULL; + } + if (NULL != stats) + { + GNUNET_STATISTICS_destroy (stats, GNUNET_NO); + stats = NULL; + } + if (NULL != my_private_key) + { + GNUNET_free (my_private_key); + my_private_key = NULL; + } + if (NULL != is) + { + GNUNET_NT_scanner_done (is); + is = NULL; + } + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "do_shutdown finished\n"); +} + + +/** + * Function called when the transport service has received a + * backchannel message for this communicator (!) via a different return + * path. Should be an acknowledgement. + * + * @param cls closure, NULL + * @param sender which peer sent the notification + * @param msg payload + */ +static void +enc_notify_cb (void *cls, + const struct GNUNET_PeerIdentity *sender, + const struct GNUNET_MessageHeader *msg) +{ + const struct UDPAck *ack; + + (void) cls; + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Storing UDPAck received from backchannel from %s\n", + GNUNET_i2s_full (sender)); + if ((ntohs (msg->type) != GNUNET_MESSAGE_TYPE_COMMUNICATOR_UDP_ACK) || + (ntohs (msg->size) != sizeof(struct UDPAck))) + { + GNUNET_break_op (0); + return; + } + ack = (const struct UDPAck *) msg; + GNUNET_CONTAINER_multipeermap_get_multiple (receivers, + sender, + &handle_ack, + (void *) ack); +} + + +/** + * Signature of the callback passed to #GNUNET_NAT_register() for + * a function to call whenever our set of 'valid' addresses changes. + * + * @param cls closure + * @param app_ctx[in,out] location where the app can store stuff + * on add and retrieve it on remove + * @param add_remove #GNUNET_YES to add a new public IP address, + * #GNUNET_NO to remove a previous (now invalid) one + * @param ac address class the address belongs to + * @param addr either the previous or the new public IP address + * @param addrlen actual length of the @a addr + */ +static void +nat_address_cb (void *cls, + void **app_ctx, + int add_remove, + enum GNUNET_NAT_AddressClass ac, + const struct sockaddr *addr, + socklen_t addrlen) +{ + char *my_addr; + struct GNUNET_TRANSPORT_AddressIdentifier *ai; + + if (GNUNET_YES == add_remove) + { + enum GNUNET_NetworkType nt; + + GNUNET_asprintf (&my_addr, + "%s-%s", + COMMUNICATOR_ADDRESS_PREFIX, + GNUNET_a2s (addr, addrlen)); + nt = GNUNET_NT_scanner_get_type (is, addr, addrlen); + ai = + GNUNET_TRANSPORT_communicator_address_add (ch, + my_addr, + nt, + GNUNET_TIME_UNIT_FOREVER_REL); + GNUNET_free (my_addr); + *app_ctx = ai; + } + else + { + ai = *app_ctx; + GNUNET_TRANSPORT_communicator_address_remove (ai); + *app_ctx = NULL; + } +} + + +/** + * Broadcast our presence on one of our interfaces. + * + * @param cls a `struct BroadcastInterface` + */ +static void +ifc_broadcast (void *cls) +{ + struct BroadcastInterface *bi = cls; + struct GNUNET_TIME_Relative delay; + + delay = BROADCAST_FREQUENCY; + delay.rel_value_us = + GNUNET_CRYPTO_random_u64 (GNUNET_CRYPTO_QUALITY_WEAK, delay.rel_value_us); + bi->broadcast_task = + GNUNET_SCHEDULER_add_delayed (delay, &ifc_broadcast, bi); + + switch (bi->sa->sa_family) + { + case AF_INET: { + static int yes = 1; + static int no = 0; + ssize_t sent; + + if (GNUNET_OK != + GNUNET_NETWORK_socket_setsockopt (udp_sock, + SOL_SOCKET, + SO_BROADCAST, + &yes, + sizeof(int))) + GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING, + "setsockopt"); + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "creating UDPBroadcast from %s\n", + GNUNET_i2s (&(bi->bcm.sender))); + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "sending UDPBroadcast to add %s\n", + GNUNET_a2s (bi->ba, bi->salen)); + sent = GNUNET_NETWORK_socket_sendto (udp_sock, + &bi->bcm, + sizeof(bi->bcm), + bi->ba, + bi->salen); + if (-1 == sent) + GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING, + "sendto"); + if (GNUNET_OK != GNUNET_NETWORK_socket_setsockopt (udp_sock, + SOL_SOCKET, + SO_BROADCAST, + &no, + sizeof(int))) + GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING, + "setsockopt"); + break; + } + + case AF_INET6: { + ssize_t sent; + struct sockaddr_in6 dst; + + dst.sin6_family = AF_INET6; + dst.sin6_port = htons (my_port); + dst.sin6_addr = bi->mcreq.ipv6mr_multiaddr; + dst.sin6_scope_id = ((struct sockaddr_in6 *) bi->ba)->sin6_scope_id; + + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "sending UDPBroadcast\n"); + sent = GNUNET_NETWORK_socket_sendto (udp_sock, + &bi->bcm, + sizeof(bi->bcm), + (const struct sockaddr *) &dst, + sizeof(dst)); + if (-1 == sent) + GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING, "sendto"); + break; + } + + default: + GNUNET_break (0); + break; + } +} + + +/** + * Callback function invoked for each interface found. + * Activates/deactivates broadcast interfaces. + * + * @param cls NULL + * @param name name of the interface (can be NULL for unknown) + * @param isDefault is this presumably the default interface + * @param addr address of this interface (can be NULL for unknown or unassigned) + * @param broadcast_addr the broadcast address (can be NULL for unknown or + * unassigned) + * @param netmask the network mask (can be NULL for unknown or unassigned) + * @param addrlen length of the address + * @return #GNUNET_OK to continue iteration, #GNUNET_SYSERR to abort + */ +static int +iface_proc (void *cls, + const char *name, + int isDefault, + const struct sockaddr *addr, + const struct sockaddr *broadcast_addr, + const struct sockaddr *netmask, + socklen_t addrlen) +{ + struct BroadcastInterface *bi; + enum GNUNET_NetworkType network; + struct UdpBroadcastSignature ubs; + + (void) cls; + (void) netmask; + if (NULL == addr) + return GNUNET_YES; /* need to know our address! */ + network = GNUNET_NT_scanner_get_type (is, addr, addrlen); + if (GNUNET_NT_LOOPBACK == network) + { + /* Broadcasting on loopback does not make sense */ + return GNUNET_YES; + } + for (bi = bi_head; NULL != bi; bi = bi->next) + { + if ((bi->salen == addrlen) && (0 == memcmp (addr, bi->sa, addrlen))) + { + bi->found = GNUNET_YES; + return GNUNET_OK; + } + } + + if ((AF_INET6 == addr->sa_family) && (NULL == broadcast_addr)) + return GNUNET_OK; /* broadcast_addr is required for IPv6! */ + if ((AF_INET6 == addr->sa_family) && (GNUNET_YES != have_v6_socket)) + return GNUNET_OK; /* not using IPv6 */ + + bi = GNUNET_new (struct BroadcastInterface); + bi->sa = GNUNET_memdup (addr, + addrlen); + if ( (NULL != broadcast_addr) && + (addrlen == sizeof (struct sockaddr_in)) ) + { + struct sockaddr_in *ba; + + ba = GNUNET_memdup (broadcast_addr, + addrlen); + ba->sin_port = htons (2086); /* always GNUnet port, ignore configuration! */ + bi->ba = (struct sockaddr *) ba; + } + bi->salen = addrlen; + bi->found = GNUNET_YES; + bi->bcm.sender = my_identity; + ubs.purpose.purpose = htonl ( + GNUNET_SIGNATURE_PURPOSE_COMMUNICATOR_UDP_BROADCAST); + ubs.purpose.size = htonl (sizeof(ubs)); + ubs.sender = my_identity; + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "creating UDPBroadcastSignature for %s\n", + GNUNET_a2s (addr, addrlen)); + GNUNET_CRYPTO_hash (addr, addrlen, &ubs.h_address); + GNUNET_CRYPTO_eddsa_sign (my_private_key, + &ubs, + &bi->bcm.sender_sig); + if (NULL != bi->ba) + { + bi->broadcast_task = GNUNET_SCHEDULER_add_now (&ifc_broadcast, bi); + GNUNET_CONTAINER_DLL_insert (bi_head, bi_tail, bi); + } + if ((AF_INET6 == addr->sa_family) && (NULL != broadcast_addr)) + { + /* Create IPv6 multicast request */ + const struct sockaddr_in6 *s6 = + (const struct sockaddr_in6 *) broadcast_addr; + + GNUNET_assert ( + 1 == inet_pton (AF_INET6, "FF05::13B", &bi->mcreq.ipv6mr_multiaddr)); + + /* http://tools.ietf.org/html/rfc2553#section-5.2: + * + * IPV6_JOIN_GROUP + * + * Join a multicast group on a specified local interface. If the + * interface index is specified as 0, the kernel chooses the local + * interface. For example, some kernels look up the multicast + * group in the normal IPv6 routing table and using the resulting + * interface; we do this for each interface, so no need to use + * zero (anymore...). + */ + bi->mcreq.ipv6mr_interface = s6->sin6_scope_id; + + /* Join the multicast group */ + if (GNUNET_OK != GNUNET_NETWORK_socket_setsockopt (udp_sock, + IPPROTO_IPV6, + IPV6_JOIN_GROUP, + &bi->mcreq, + sizeof(bi->mcreq))) + { + GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING, "setsockopt"); + } + } + return GNUNET_OK; +} + + +/** + * Scan interfaces to broadcast our presence on the LAN. + * + * @param cls NULL, unused + */ +static void +do_broadcast (void *cls) +{ + struct BroadcastInterface *bin; + + (void) cls; + for (struct BroadcastInterface *bi = bi_head; NULL != bi; bi = bi->next) + bi->found = GNUNET_NO; + GNUNET_OS_network_interfaces_list (&iface_proc, NULL); + for (struct BroadcastInterface *bi = bi_head; NULL != bi; bi = bin) + { + bin = bi->next; + if (GNUNET_NO == bi->found) + bi_destroy (bi); + } + broadcast_task = GNUNET_SCHEDULER_add_delayed (INTERFACE_SCAN_FREQUENCY, + &do_broadcast, + NULL); +} + + +static void +try_connection_reversal (void *cls, + const struct sockaddr *addr, + socklen_t addrlen) +{ + /* FIXME: support reversal: #5529 */ + GNUNET_log (GNUNET_ERROR_TYPE_INFO, + "No connection reversal implemented!"); +} + + +/** + * Setup communicator and launch network interactions. + * + * @param cls NULL (always) + * @param args remaining command-line arguments + * @param cfgfile name of the configuration file used (for saving, can be NULL!) + * @param c configuration + */ +static void +run (void *cls, + char *const *args, + const char *cfgfile, + const struct GNUNET_CONFIGURATION_Handle *c) +{ + char *bindto; + struct sockaddr *in; + socklen_t in_len; + struct sockaddr_storage in_sto; + socklen_t sto_len; + + (void) cls; + cfg = c; + if (GNUNET_OK != + GNUNET_CONFIGURATION_get_value_string (cfg, + COMMUNICATOR_CONFIG_SECTION, + "BINDTO", + &bindto)) + { + GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR, + COMMUNICATOR_CONFIG_SECTION, + "BINDTO"); + return; + } + + if (GNUNET_OK != + GNUNET_CONFIGURATION_get_value_time (cfg, + COMMUNICATOR_CONFIG_SECTION, + "REKEY_INTERVAL", + &rekey_interval)) + rekey_interval = DEFAULT_REKEY_TIME_INTERVAL; + + if (GNUNET_OK != + GNUNET_CONFIGURATION_get_value_size (cfg, + COMMUNICATOR_CONFIG_SECTION, + "REKEY_MAX_BYTES", + &rekey_max_bytes)) + rekey_max_bytes = DEFAULT_REKEY_MAX_BYTES; + + in = udp_address_to_sockaddr (bindto, &in_len); + if (NULL == in) + { + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "Failed to setup UDP socket address with path `%s'\n", + bindto); + GNUNET_free (bindto); + return; + } + udp_sock = + GNUNET_NETWORK_socket_create (in->sa_family, + SOCK_DGRAM, + IPPROTO_UDP); + if (NULL == udp_sock) + { + GNUNET_log_strerror (GNUNET_ERROR_TYPE_ERROR, "socket"); + GNUNET_free (in); + GNUNET_free (bindto); + return; + } + if (AF_INET6 == in->sa_family) + have_v6_socket = GNUNET_YES; + if (GNUNET_OK != + GNUNET_NETWORK_socket_bind (udp_sock, + in, + in_len)) + { + GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_ERROR, + "bind", + bindto); + GNUNET_NETWORK_socket_close (udp_sock); + udp_sock = NULL; + GNUNET_free (in); + GNUNET_free (bindto); + return; + } + + /* We might have bound to port 0, allowing the OS to figure it out; + thus, get the real IN-address from the socket */ + sto_len = sizeof(in_sto); + if (0 != getsockname (GNUNET_NETWORK_get_fd (udp_sock), + (struct sockaddr *) &in_sto, + &sto_len)) + { + memcpy (&in_sto, in, in_len); + sto_len = in_len; + } + GNUNET_free (in); + GNUNET_free (bindto); + in = (struct sockaddr *) &in_sto; + in_len = sto_len; + GNUNET_log_from_nocheck (GNUNET_ERROR_TYPE_DEBUG, + "transport", + "Bound to `%s'\n", + GNUNET_a2s ((const struct sockaddr *) &in_sto, + sto_len)); + switch (in->sa_family) + { + case AF_INET: + my_port = ntohs (((struct sockaddr_in *) in)->sin_port); + break; + + case AF_INET6: + my_port = ntohs (((struct sockaddr_in6 *) in)->sin6_port); + break; + + default: + GNUNET_break (0); + my_port = 0; + } + stats = GNUNET_STATISTICS_create ("C-UDP", cfg); + senders = GNUNET_CONTAINER_multipeermap_create (32, GNUNET_YES); + receivers = GNUNET_CONTAINER_multipeermap_create (32, GNUNET_YES); + senders_heap = GNUNET_CONTAINER_heap_create (GNUNET_CONTAINER_HEAP_ORDER_MIN); + receivers_heap = + GNUNET_CONTAINER_heap_create (GNUNET_CONTAINER_HEAP_ORDER_MIN); + key_cache = GNUNET_CONTAINER_multishortmap_create (1024, GNUNET_YES); + GNUNET_SCHEDULER_add_shutdown (&do_shutdown, NULL); + is = GNUNET_NT_scanner_init (); + my_private_key = GNUNET_CRYPTO_eddsa_key_create_from_configuration (cfg); + if (NULL == my_private_key) + { + GNUNET_log ( + GNUNET_ERROR_TYPE_ERROR, + _ ( + "Transport service is lacking key configuration settings. Exiting.\n")); + GNUNET_SCHEDULER_shutdown (); + return; + } + GNUNET_CRYPTO_eddsa_key_get_public (my_private_key, &my_identity.public_key); + /* start reading */ + read_task = GNUNET_SCHEDULER_add_read_net (GNUNET_TIME_UNIT_FOREVER_REL, + udp_sock, + &sock_read, + NULL); + ch = GNUNET_TRANSPORT_communicator_connect (cfg, + COMMUNICATOR_CONFIG_SECTION, + COMMUNICATOR_ADDRESS_PREFIX, + GNUNET_TRANSPORT_CC_UNRELIABLE, + &mq_init, + NULL, + &enc_notify_cb, + NULL); + if (NULL == ch) + { + GNUNET_break (0); + GNUNET_SCHEDULER_shutdown (); + return; + } + ah = GNUNET_TRANSPORT_application_init (cfg); + if (NULL == ah) + { + GNUNET_break (0); + GNUNET_SCHEDULER_shutdown (); + return; + } + /* start broadcasting */ + if (GNUNET_YES != + GNUNET_CONFIGURATION_get_value_yesno (cfg, + COMMUNICATOR_CONFIG_SECTION, + "DISABLE_BROADCAST")) + { + broadcast_task = GNUNET_SCHEDULER_add_now (&do_broadcast, NULL); + } + nat = GNUNET_NAT_register (cfg, + COMMUNICATOR_CONFIG_SECTION, + IPPROTO_UDP, + 1 /* one address */, + (const struct sockaddr **) &in, + &in_len, + &nat_address_cb, + try_connection_reversal, + NULL /* closure */); +} + + +/** + * The main function for the UNIX communicator. + * + * @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 + }; + int ret; + + GNUNET_log_from_nocheck (GNUNET_ERROR_TYPE_DEBUG, + "transport", + "Starting udp communicator\n"); + if (GNUNET_OK != GNUNET_STRINGS_get_utf8_args (argc, argv, &argc, &argv)) + return 2; + + ret = (GNUNET_OK == GNUNET_PROGRAM_run (argc, + argv, + "gnunet-communicator-udp", + _ ("GNUnet UDP communicator"), + options, + &run, + NULL)) + ? 0 + : 1; + GNUNET_free_nz ((void *) argv); + return ret; +} + + +/* end of gnunet-communicator-udp.c */ diff --git a/src/service/transport/gnunet-communicator-unix.c b/src/service/transport/gnunet-communicator-unix.c new file mode 100644 index 000000000..0ff16ab08 --- /dev/null +++ b/src/service/transport/gnunet-communicator-unix.c @@ -0,0 +1,1166 @@ +/* + This file is part of GNUnet + Copyright (C) 2010-2014, 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 . + + SPDX-License-Identifier: AGPL3.0-or-later + */ + +/** + * @file transport/gnunet-communicator-unix.c + * @brief Transport plugin using unix domain sockets (!) + * Clearly, can only be used locally on Unix/Linux hosts... + * ONLY INTENDED FOR TESTING!!! + * @author Christian Grothoff + * @author Nathan Evans + */ +#include "platform.h" +#include "gnunet_util_lib.h" +#include "gnunet_protocols.h" +#include "gnunet_constants.h" +#include "gnunet_statistics_service.h" +#include "gnunet_transport_communication_service.h" + +/** + * How many messages do we keep at most in the queue to the + * transport service before we start to drop (default, + * can be changed via the configuration file). + * Should be _below_ the level of the communicator API, as + * otherwise we may read messages just to have them dropped + * by the communicator API. + */ +#define DEFAULT_MAX_QUEUE_LENGTH 8000 + +/** + * Address prefix used by the communicator. + */ +#define COMMUNICATOR_ADDRESS_PREFIX "unix" + +/** + * Configuration section used by the communicator. + */ +#define COMMUNICATOR_CONFIG_SECTION "communicator-unix" + +/** + * Our MTU. + */ +#ifndef DARWIN +#define UNIX_MTU UINT16_MAX +#else +#define UNIX_MTU 2048 +#endif + +GNUNET_NETWORK_STRUCT_BEGIN + +/** + * UNIX Message-Packet header. + */ +struct UNIXMessage +{ + /** + * Message header. + */ + struct GNUNET_MessageHeader header; + + /** + * What is the identity of the sender (GNUNET_hash of public key) + */ + struct GNUNET_PeerIdentity sender; +}; + +GNUNET_NETWORK_STRUCT_END + + +/** + * Handle for a queue. + */ +struct Queue +{ + /** + * Queues with pending messages (!) are kept in a DLL. + */ + struct Queue *next; + + /** + * Queues with pending messages (!) are kept in a DLL. + */ + struct Queue *prev; + + /** + * To whom are we talking to. + */ + struct GNUNET_PeerIdentity target; + + /** + * Address of the other peer. + */ + struct sockaddr_un *address; + + /** + * Length of the address. + */ + socklen_t address_len; + + /** + * Message currently scheduled for transmission, non-NULL if and only + * if this queue is in the #queue_head DLL. + */ + struct UNIXMessage *msg; + + /** + * Message queue we are providing for the #ch. + */ + struct GNUNET_MQ_Handle *mq; + + /** + * handle for this queue with the #ch. + */ + struct GNUNET_TRANSPORT_QueueHandle *qh; + + /** + * Number of bytes we currently have in our write queue. + */ + unsigned long long bytes_in_queue; + + /** + * Timeout for this queue. + */ + struct GNUNET_TIME_Absolute timeout; + + /** + * Queue timeout task. + */ + struct GNUNET_SCHEDULER_Task *timeout_task; +}; + +/** + * My Peer Identity + */ +static struct GNUNET_PeerIdentity my_identity; + +/** + * ID of read task + */ +static struct GNUNET_SCHEDULER_Task *read_task; + +/** + * ID of write task + */ +static struct GNUNET_SCHEDULER_Task *write_task; + +/** + * Number of messages we currently have in our queues towards the transport service. + */ +static unsigned long long delivering_messages; + +/** + * Maximum queue length before we stop reading towards the transport service. + */ +static unsigned long long max_queue_length; + +/** + * For logging statistics. + */ +static struct GNUNET_STATISTICS_Handle *stats; + +/** + * Our environment. + */ +static struct GNUNET_TRANSPORT_CommunicatorHandle *ch; + +/** + * Queues (map from peer identity to `struct Queue`) + */ +static struct GNUNET_CONTAINER_MultiPeerMap *queue_map; + +/** + * Head of queue of messages to transmit. + */ +static struct Queue *queue_head; + +/** + * Tail of queue of messages to transmit. + */ +static struct Queue *queue_tail; + +/** + * socket that we transmit all data with + */ +static struct GNUNET_NETWORK_Handle *unix_sock; + +/** + * Handle to the operation that publishes our address. + */ +static struct GNUNET_TRANSPORT_AddressIdentifier *ai; + + +/** + * Functions with this signature are called whenever we need + * to close a queue due to a disconnect or failure to + * establish a connection. + * + * @param queue queue to close down + */ +static void +queue_destroy (struct Queue *queue) +{ + struct GNUNET_MQ_Handle *mq; + + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Disconnecting queue for peer `%s'\n", + GNUNET_i2s (&queue->target)); + if (0 != queue->bytes_in_queue) + { + GNUNET_CONTAINER_DLL_remove (queue_head, queue_tail, queue); + queue->bytes_in_queue = 0; + } + if (NULL != (mq = queue->mq)) + { + queue->mq = NULL; + GNUNET_MQ_destroy (mq); + } + GNUNET_assert ( + GNUNET_YES == + GNUNET_CONTAINER_multipeermap_remove (queue_map, &queue->target, queue)); + GNUNET_STATISTICS_set (stats, + "# queues active", + GNUNET_CONTAINER_multipeermap_size (queue_map), + GNUNET_NO); + if (NULL != queue->timeout_task) + { + GNUNET_SCHEDULER_cancel (queue->timeout_task); + queue->timeout_task = NULL; + } + GNUNET_free (queue->address); + GNUNET_free (queue); +} + + +/** + * Queue was idle for too long, so disconnect it + * + * @param cls the `struct Queue *` to disconnect + */ +static void +queue_timeout (void *cls) +{ + struct Queue *queue = cls; + struct GNUNET_TIME_Relative left; + + queue->timeout_task = NULL; + left = GNUNET_TIME_absolute_get_remaining (queue->timeout); + if (0 != left.rel_value_us) + { + /* not actually our turn yet, but let's at least update + the monitor, it may think we're about to die ... */ + queue->timeout_task = + GNUNET_SCHEDULER_add_delayed (left, &queue_timeout, queue); + return; + } + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Queue %p was idle for %s, disconnecting\n", + queue, + GNUNET_STRINGS_relative_time_to_string ( + GNUNET_CONSTANTS_IDLE_CONNECTION_TIMEOUT, + GNUNET_YES)); + queue_destroy (queue); +} + + +/** + * Increment queue timeout due to activity. We do not immediately + * notify the monitor here as that might generate excessive + * signalling. + * + * @param queue queue for which the timeout should be rescheduled + */ +static void +reschedule_queue_timeout (struct Queue *queue) +{ + GNUNET_assert (NULL != queue->timeout_task); + queue->timeout = + GNUNET_TIME_relative_to_absolute (GNUNET_CONSTANTS_IDLE_CONNECTION_TIMEOUT); +} + + +/** + * Convert unix path to a `struct sockaddr_un *` + * + * @param unixpath path to convert + * @param[out] sock_len set to the length of the address + * @param is_abstract is this an abstract @a unixpath + * @return converted unix path + */ +static struct sockaddr_un * +unix_address_to_sockaddr (const char *unixpath, socklen_t *sock_len) +{ + struct sockaddr_un *un; + size_t slen; + + GNUNET_assert (0 < strlen (unixpath)); /* sanity check */ + un = GNUNET_new (struct sockaddr_un); + un->sun_family = AF_UNIX; + slen = strlen (unixpath); + if (slen >= sizeof(un->sun_path)) + slen = sizeof(un->sun_path) - 1; + GNUNET_memcpy (un->sun_path, unixpath, slen); + un->sun_path[slen] = '\0'; + slen = sizeof(struct sockaddr_un); +#if HAVE_SOCKADDR_UN_SUN_LEN + un->sun_len = (u_char) slen; +#endif + (*sock_len) = slen; + if ('@' == un->sun_path[0]) + un->sun_path[0] = '\0'; + return un; +} + + +/** + * Closure to #lookup_queue_it(). + */ +struct LookupCtx +{ + /** + * Location to store the queue, if found. + */ + struct Queue *res; + + /** + * Address we are looking for. + */ + const struct sockaddr_un *un; + + /** + * Number of bytes in @a un + */ + socklen_t un_len; +}; + + +/** + * Function called to find a queue by address. + * + * @param cls the `struct LookupCtx *` + * @param key peer we are looking for (unused) + * @param value a queue + * @return #GNUNET_YES if not found (continue looking), #GNUNET_NO on success + */ +static int +lookup_queue_it (void *cls, const struct GNUNET_PeerIdentity *key, void *value) +{ + struct LookupCtx *lctx = cls; + struct Queue *queue = value; + + if ((queue->address_len == lctx->un_len) && + (0 == memcmp (lctx->un, queue->address, queue->address_len))) + { + lctx->res = queue; + return GNUNET_NO; + } + return GNUNET_YES; +} + + +/** + * Find an existing queue by address. + * + * @param plugin the plugin + * @param address the address to find + * @return NULL if queue was not found + */ +static struct Queue * +lookup_queue (const struct GNUNET_PeerIdentity *peer, + const struct sockaddr_un *un, + socklen_t un_len) +{ + struct LookupCtx lctx; + + lctx.un = un; + lctx.un_len = un_len; + lctx.res = NULL; + GNUNET_CONTAINER_multipeermap_get_multiple (queue_map, + peer, + &lookup_queue_it, + &lctx); + return lctx.res; +} + + +/** + * We have been notified that our socket is ready to write. + * Then reschedule this function to be called again once more is available. + * + * @param cls NULL + */ +static void +select_write_cb (void *cls) +{ + struct Queue *queue = queue_tail; + const struct GNUNET_MessageHeader *msg = &queue->msg->header; + size_t msg_size = ntohs (msg->size); + ssize_t sent; + + /* take queue of the ready list */ + write_task = NULL; +resend: + /* Send the data */ + sent = GNUNET_NETWORK_socket_sendto (unix_sock, + msg, + msg_size, + (const struct sockaddr *) queue->address, + queue->address_len); + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "UNIX transmitted message to %s (%d/%u: %s)\n", + GNUNET_i2s (&queue->target), + (int) sent, + (unsigned int) msg_size, + (sent < 0) ? strerror (errno) : "ok"); + if (-1 != sent) + { + GNUNET_CONTAINER_DLL_remove (queue_head, queue_tail, queue); + if (NULL != queue_head) + write_task = GNUNET_SCHEDULER_add_write_net (GNUNET_TIME_UNIT_FOREVER_REL, + unix_sock, + &select_write_cb, + NULL); + + /* send 'msg' */ + GNUNET_free (queue->msg); + queue->msg = NULL; + GNUNET_MQ_impl_send_continue (queue->mq); + GNUNET_STATISTICS_update (stats, + "# bytes sent", + (long long) sent, + GNUNET_NO); + reschedule_queue_timeout (queue); + return; /* all good */ + } + GNUNET_STATISTICS_update (stats, + "# network transmission failures", + 1, + GNUNET_NO); + write_task = GNUNET_SCHEDULER_add_write_net (GNUNET_TIME_UNIT_FOREVER_REL, + unix_sock, + &select_write_cb, + NULL); + switch (errno) + { + case EAGAIN: + case ENOBUFS: + /* We should retry later... */ + GNUNET_log_strerror (GNUNET_ERROR_TYPE_DEBUG, "send"); + return; + + case EMSGSIZE: { + socklen_t size = 0; + socklen_t len = sizeof(size); + + GNUNET_NETWORK_socket_getsockopt (unix_sock, + SOL_SOCKET, + SO_SNDBUF, + &size, + &len); + if (size > ntohs (msg->size)) + { + /* Buffer is bigger than message: error, no retry + * This should never happen!*/ + GNUNET_break (0); + return; + } + GNUNET_log ( + GNUNET_ERROR_TYPE_WARNING, + "Trying to increase socket buffer size from %u to %u for message size %u\n", + (unsigned int) size, + (unsigned int) ((msg_size / 1000) + 2) * 1000, + (unsigned int) msg_size); + size = ((msg_size / 1000) + 2) * 1000; + if (GNUNET_OK == GNUNET_NETWORK_socket_setsockopt (unix_sock, + SOL_SOCKET, + SO_SNDBUF, + &size, + sizeof(size))) + goto resend; /* Increased buffer size, retry sending */ + /* Ok, then just try very modest increase */ + size = msg_size; + if (GNUNET_OK == GNUNET_NETWORK_socket_setsockopt (unix_sock, + SOL_SOCKET, + SO_SNDBUF, + &size, + sizeof(size))) + goto resend; /* Increased buffer size, retry sending */ + /* Could not increase buffer size: error, no retry */ + GNUNET_log_strerror (GNUNET_ERROR_TYPE_ERROR, "setsockopt"); + return; + } + + default: + GNUNET_log_strerror (GNUNET_ERROR_TYPE_ERROR, "send"); + return; + } +} + + +/** + * Signature of functions implementing the sending functionality of a + * message queue. + * + * @param mq the message queue + * @param msg the message to send + * @param impl_state our `struct Queue` + */ +static void +mq_send (struct GNUNET_MQ_Handle *mq, + const struct GNUNET_MessageHeader *msg, + void *impl_state) +{ + struct Queue *queue = impl_state; + size_t msize = ntohs (msg->size); + + GNUNET_assert (mq == queue->mq); + GNUNET_assert (NULL == queue->msg); + // Convert to UNIXMessage + queue->msg = GNUNET_malloc (msize + sizeof (struct UNIXMessage)); + queue->msg->header.size = htons (msize + sizeof (struct UNIXMessage)); + queue->msg->sender = my_identity; + memcpy (&queue->msg[1], msg, msize); + GNUNET_CONTAINER_DLL_insert (queue_head, queue_tail, queue); + GNUNET_assert (NULL != unix_sock); + if (NULL == write_task) + write_task = GNUNET_SCHEDULER_add_write_net (GNUNET_TIME_UNIT_FOREVER_REL, + unix_sock, + &select_write_cb, + NULL); +} + + +/** + * Signature of functions implementing the destruction of a message + * queue. Implementations must not free @a mq, but should take care + * of @a impl_state. + * + * @param mq the message queue to destroy + * @param impl_state our `struct Queue` + */ +static void +mq_destroy (struct GNUNET_MQ_Handle *mq, void *impl_state) +{ + struct Queue *queue = impl_state; + + if (mq == queue->mq) + { + queue->mq = NULL; + queue_destroy (queue); + } +} + + +/** + * Implementation function that cancels the currently sent message. + * + * @param mq message queue + * @param impl_state our `struct Queue` + */ +static void +mq_cancel (struct GNUNET_MQ_Handle *mq, void *impl_state) +{ + struct Queue *queue = impl_state; + + GNUNET_assert (NULL != queue->msg); + queue->msg = NULL; + GNUNET_CONTAINER_DLL_remove (queue_head, queue_tail, queue); + GNUNET_assert (NULL != write_task); + if (NULL == queue_head) + { + GNUNET_SCHEDULER_cancel (write_task); + write_task = NULL; + } +} + + +/** + * Generic error handler, called with the appropriate + * error code and the same closure specified at the creation of + * the message queue. + * Not every message queue implementation supports an error handler. + * + * @param cls our `struct Queue` + * @param error error code + */ +static void +mq_error (void *cls, enum GNUNET_MQ_Error error) +{ + struct Queue *queue = cls; + + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "UNIX MQ error in queue to %s: %d\n", + GNUNET_i2s (&queue->target), + (int) error); + queue_destroy (queue); +} + + +/** + * Creates a new outbound queue the transport service will use to send + * data to another peer. + * + * @param peer the target peer + * @param cs inbound or outbound queue + * @param un the address + * @param un_len number of bytes in @a un + * @return the queue or NULL of max connections exceeded + */ +static struct Queue * +setup_queue (const struct GNUNET_PeerIdentity *target, + enum GNUNET_TRANSPORT_ConnectionStatus cs, + const struct sockaddr_un *un, + socklen_t un_len) +{ + struct Queue *queue; + + queue = GNUNET_new (struct Queue); + queue->target = *target; + queue->address = GNUNET_memdup (un, un_len); + queue->address_len = un_len; + (void) GNUNET_CONTAINER_multipeermap_put ( + queue_map, + &queue->target, + queue, + GNUNET_CONTAINER_MULTIHASHMAPOPTION_MULTIPLE); + GNUNET_STATISTICS_set (stats, + "# queues active", + GNUNET_CONTAINER_multipeermap_size (queue_map), + GNUNET_NO); + queue->timeout = + GNUNET_TIME_relative_to_absolute (GNUNET_CONSTANTS_IDLE_CONNECTION_TIMEOUT); + queue->timeout_task = + GNUNET_SCHEDULER_add_delayed (GNUNET_CONSTANTS_IDLE_CONNECTION_TIMEOUT, + &queue_timeout, + queue); + queue->mq = GNUNET_MQ_queue_for_callbacks (&mq_send, + &mq_destroy, + &mq_cancel, + queue, + NULL, + &mq_error, + queue); + { + char *foreign_addr; + + if ('\0' == un->sun_path[0]) + GNUNET_asprintf (&foreign_addr, + "%s-@%s", + COMMUNICATOR_ADDRESS_PREFIX, + &un->sun_path[1]); + else + GNUNET_asprintf (&foreign_addr, + "%s-%s", + COMMUNICATOR_ADDRESS_PREFIX, + un->sun_path); + queue->qh = GNUNET_TRANSPORT_communicator_mq_add (ch, + &queue->target, + foreign_addr, + UNIX_MTU - sizeof (struct + UNIXMessage), + GNUNET_TRANSPORT_QUEUE_LENGTH_UNLIMITED, + 0, + GNUNET_NT_LOOPBACK, + cs, + queue->mq); + GNUNET_free (foreign_addr); + } + return queue; +} + + +/** + * We have been notified that our socket has something to read. Do the + * read and reschedule this function to be called again once more is + * available. + * + * @param cls NULL + */ +static void +select_read_cb (void *cls); + + +/** + * Function called when message was successfully passed to + * transport service. Continue read activity. + * + * @param cls NULL + * @param success #GNUNET_OK on success + */ +static void +receive_complete_cb (void *cls, int success) +{ + (void) cls; + delivering_messages--; + if (GNUNET_OK != success) + GNUNET_STATISTICS_update (stats, + "# transport transmission failures", + 1, + GNUNET_NO); + if ((NULL == read_task) && (delivering_messages < max_queue_length) && + (NULL != unix_sock)) + read_task = GNUNET_SCHEDULER_add_read_net (GNUNET_TIME_UNIT_FOREVER_REL, + unix_sock, + &select_read_cb, + NULL); +} + + +/** + * We have been notified that our socket has something to read. Do the + * read and reschedule this function to be called again once more is + * available. + * + * @param cls NULL + */ +static void +select_read_cb (void *cls) +{ + char buf[65536] GNUNET_ALIGN; + struct Queue *queue; + const struct UNIXMessage *msg; + struct sockaddr_un un; + socklen_t addrlen; + ssize_t ret; + uint16_t msize; + + GNUNET_assert (NULL != unix_sock); + read_task = GNUNET_SCHEDULER_add_read_net (GNUNET_TIME_UNIT_FOREVER_REL, + unix_sock, + &select_read_cb, + NULL); + addrlen = sizeof(un); + memset (&un, 0, sizeof(un)); + ret = GNUNET_NETWORK_socket_recvfrom (unix_sock, + buf, + sizeof(buf), + (struct sockaddr *) &un, + &addrlen); + if ((-1 == ret) && ((EAGAIN == errno) || (ENOBUFS == errno))) + return; + if (-1 == ret) + { + GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING, "recvfrom"); + return; + } + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Read %d bytes from socket %s\n", + (int) ret, + un.sun_path); + GNUNET_assert (AF_UNIX == (un.sun_family)); + msg = (struct UNIXMessage *) buf; + msize = ntohs (msg->header.size); + if ((msize < sizeof(struct UNIXMessage)) || (msize > ret)) + { + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "Wrong message size: %d bytes\n", + msize); + GNUNET_break_op (0); + return; + } + queue = lookup_queue (&msg->sender, &un, addrlen); + if (NULL == queue) + queue = + setup_queue (&msg->sender, GNUNET_TRANSPORT_CS_INBOUND, &un, addrlen); + else + reschedule_queue_timeout (queue); + if (NULL == queue) + { + GNUNET_log ( + GNUNET_ERROR_TYPE_ERROR, + _ ( + "Maximum number of UNIX connections exceeded, dropping incoming message\n")); + return; + } + + { + uint16_t tsize = msize - sizeof(struct UNIXMessage); + + const struct GNUNET_MessageHeader *currhdr; + struct GNUNET_MessageHeader al_hdr; + + currhdr = (const struct GNUNET_MessageHeader *) &msg[1]; + /* ensure aligned access */ + memcpy (&al_hdr, currhdr, sizeof(al_hdr)); + if ((tsize < sizeof(struct GNUNET_MessageHeader)) || + (tsize != ntohs (al_hdr.size))) + { + GNUNET_break_op (0); + return; + } + ret = GNUNET_TRANSPORT_communicator_receive (ch, + &msg->sender, + currhdr, + GNUNET_TIME_UNIT_FOREVER_REL, + &receive_complete_cb, + NULL); + if (GNUNET_SYSERR == ret) + { + GNUNET_log (GNUNET_ERROR_TYPE_WARNING, + "Transport not up!\n"); + return; /* transport not up */ + } + if (GNUNET_NO == ret) + { + GNUNET_log (GNUNET_ERROR_TYPE_WARNING, + "Error sending message to transport\n"); + return; + } + delivering_messages++; + } + if (delivering_messages >= max_queue_length) + { + GNUNET_log (GNUNET_ERROR_TYPE_WARNING, + "Back pressure %llu\n", delivering_messages); + + /* we should try to apply 'back pressure' */ + GNUNET_SCHEDULER_cancel (read_task); + read_task = NULL; + } +} + + +/** + * Function called by the transport service to initialize a + * message queue given address information about another peer. + * If and when the communication channel is established, the + * communicator must call #GNUNET_TRANSPORT_communicator_mq_add() + * to notify the service that the channel is now up. It is + * the responsibility of the communicator to manage sane + * retries and timeouts for any @a peer/@a address combination + * provided by the transport service. Timeouts and retries + * do not need to be signalled to the transport service. + * + * @param cls closure + * @param peer identity of the other peer + * @param address where to send the message, human-readable + * communicator-specific format, 0-terminated, UTF-8 + * @return #GNUNET_OK on success, #GNUNET_SYSERR if the provided address is invalid + */ +static int +mq_init (void *cls, const struct GNUNET_PeerIdentity *peer, const char *address) +{ + struct Queue *queue; + const char *path; + struct sockaddr_un *un; + socklen_t un_len; + + (void) cls; + if (0 != strncmp (address, + COMMUNICATOR_ADDRESS_PREFIX "-", + strlen (COMMUNICATOR_ADDRESS_PREFIX "-"))) + { + GNUNET_break_op (0); + return GNUNET_SYSERR; + } + path = &address[strlen (COMMUNICATOR_ADDRESS_PREFIX "-")]; + un = unix_address_to_sockaddr (path, &un_len); + queue = lookup_queue (peer, un, un_len); + if (NULL != queue) + { + GNUNET_log (GNUNET_ERROR_TYPE_INFO, + "Address `%s' for %s ignored, queue exists\n", + path, + GNUNET_i2s (peer)); + GNUNET_free (un); + return GNUNET_OK; + } + queue = setup_queue (peer, GNUNET_TRANSPORT_CS_OUTBOUND, un, un_len); + GNUNET_free (un); + if (NULL == queue) + { + GNUNET_log (GNUNET_ERROR_TYPE_INFO, + "Failed to setup queue to %s at `%s'\n", + GNUNET_i2s (peer), + path); + return GNUNET_NO; + } + return GNUNET_OK; +} + + +/** + * Iterator over all message queues to clean up. + * + * @param cls NULL + * @param target unused + * @param value the queue to destroy + * @return #GNUNET_OK to continue to iterate + */ +static int +get_queue_delete_it (void *cls, + const struct GNUNET_PeerIdentity *target, + void *value) +{ + struct Queue *queue = value; + + (void) cls; + (void) target; + queue_destroy (queue); + return GNUNET_OK; +} + + +/** + * Shutdown the UNIX communicator. + * + * @param cls NULL (always) + */ +static void +do_shutdown (void *cls) +{ + if (NULL != read_task) + { + GNUNET_SCHEDULER_cancel (read_task); + read_task = NULL; + } + if (NULL != write_task) + { + GNUNET_SCHEDULER_cancel (write_task); + write_task = NULL; + } + if (NULL != unix_sock) + { + GNUNET_break (GNUNET_OK == GNUNET_NETWORK_socket_close (unix_sock)); + unix_sock = NULL; + } + GNUNET_CONTAINER_multipeermap_iterate (queue_map, &get_queue_delete_it, NULL); + GNUNET_CONTAINER_multipeermap_destroy (queue_map); + if (NULL != ai) + { + GNUNET_TRANSPORT_communicator_address_remove (ai); + ai = NULL; + } + if (NULL != ch) + { + GNUNET_TRANSPORT_communicator_disconnect (ch); + ch = NULL; + } + if (NULL != stats) + { + GNUNET_STATISTICS_destroy (stats, GNUNET_NO); + stats = NULL; + } +} + + +/** + * Function called when the transport service has received an + * acknowledgement for this communicator (!) via a different return + * path. + * + * Not applicable for UNIX. + * + * @param cls closure + * @param sender which peer sent the notification + * @param msg payload + */ +static void +enc_notify_cb (void *cls, + const struct GNUNET_PeerIdentity *sender, + const struct GNUNET_MessageHeader *msg) +{ + (void) cls; + (void) sender; + (void) msg; + GNUNET_break_op (0); +} + + +/** + * Setup communicator and launch network interactions. + * + * @param cls NULL (always) + * @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, + const char *cfgfile, + const struct GNUNET_CONFIGURATION_Handle *cfg) +{ + char *unix_socket_path; + struct sockaddr_un *un; + socklen_t un_len; + char *my_addr; + struct GNUNET_CRYPTO_EddsaPrivateKey *my_private_key; + + (void) cls; + delivering_messages = 0; + + my_private_key = GNUNET_CRYPTO_eddsa_key_create_from_configuration (cfg); + if (NULL == my_private_key) + { + GNUNET_log ( + GNUNET_ERROR_TYPE_ERROR, + _ ( + "UNIX communicator is lacking key configuration settings. Exiting.\n")); + GNUNET_SCHEDULER_shutdown (); + return; + } + GNUNET_CRYPTO_eddsa_key_get_public (my_private_key, &my_identity.public_key); + + if (GNUNET_OK != + GNUNET_CONFIGURATION_get_value_filename (cfg, + COMMUNICATOR_CONFIG_SECTION, + "UNIXPATH", + &unix_socket_path)) + { + GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR, + COMMUNICATOR_CONFIG_SECTION, + "UNIXPATH"); + return; + } + if (GNUNET_OK != + GNUNET_CONFIGURATION_get_value_number (cfg, + COMMUNICATOR_CONFIG_SECTION, + "MAX_QUEUE_LENGTH", + &max_queue_length)) + max_queue_length = DEFAULT_MAX_QUEUE_LENGTH; + + un = unix_address_to_sockaddr (unix_socket_path, &un_len); + if (NULL == un) + { + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "Failed to setup UNIX domain socket address with path `%s'\n", + unix_socket_path); + GNUNET_free (unix_socket_path); + return; + } + unix_sock = GNUNET_NETWORK_socket_create (AF_UNIX, SOCK_DGRAM, 0); + if (NULL == unix_sock) + { + GNUNET_log_strerror (GNUNET_ERROR_TYPE_ERROR, "socket"); + GNUNET_free (un); + GNUNET_free (unix_socket_path); + return; + } + if (('\0' != un->sun_path[0]) && + (GNUNET_OK != GNUNET_DISK_directory_create_for_file (un->sun_path))) + { + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + _ ("Cannot create path to `%s'\n"), + un->sun_path); + GNUNET_NETWORK_socket_close (unix_sock); + unix_sock = NULL; + GNUNET_free (un); + GNUNET_free (unix_socket_path); + return; + } + if (GNUNET_OK != GNUNET_NETWORK_socket_bind (unix_sock, + (const struct sockaddr *) un, + un_len)) + { + GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_ERROR, "bind", un->sun_path); + GNUNET_NETWORK_socket_close (unix_sock); + unix_sock = NULL; + GNUNET_free (un); + GNUNET_free (unix_socket_path); + return; + } + GNUNET_free (un); + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Bound to `%s'\n", unix_socket_path); + stats = GNUNET_STATISTICS_create ("C-UNIX", cfg); + GNUNET_SCHEDULER_add_shutdown (&do_shutdown, NULL); + read_task = GNUNET_SCHEDULER_add_read_net (GNUNET_TIME_UNIT_FOREVER_REL, + unix_sock, + &select_read_cb, + NULL); + queue_map = GNUNET_CONTAINER_multipeermap_create (10, GNUNET_NO); + ch = GNUNET_TRANSPORT_communicator_connect (cfg, + COMMUNICATOR_CONFIG_SECTION, + COMMUNICATOR_ADDRESS_PREFIX, + GNUNET_TRANSPORT_CC_RELIABLE, + &mq_init, + NULL, + &enc_notify_cb, + NULL); + if (NULL == ch) + { + GNUNET_break (0); + GNUNET_SCHEDULER_shutdown (); + GNUNET_free (unix_socket_path); + return; + } + GNUNET_asprintf (&my_addr, + "%s-%s", + COMMUNICATOR_ADDRESS_PREFIX, + unix_socket_path); + GNUNET_free (unix_socket_path); + ai = GNUNET_TRANSPORT_communicator_address_add (ch, + my_addr, + GNUNET_NT_LOOPBACK, + GNUNET_TIME_UNIT_FOREVER_REL); + GNUNET_free (my_addr); +} + + +/** + * The main function for the UNIX communicator. + * + * @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 + }; + int ret; + + if (GNUNET_OK != GNUNET_STRINGS_get_utf8_args (argc, argv, &argc, &argv)) + return 2; + + ret = (GNUNET_OK == + GNUNET_PROGRAM_run (argc, + argv, + "gnunet-communicator-unix", + _ ("GNUnet UNIX domain socket communicator"), + options, + &run, + NULL)) + ? 0 + : 1; + GNUNET_free_nz ((void *) argv); + return ret; +} + + +#if defined(__linux__) && defined(__GLIBC__) +#include + +/** + * 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-communicator-unix.c */ diff --git a/src/service/transport/gnunet-service-transport.c b/src/service/transport/gnunet-service-transport.c new file mode 100644 index 000000000..ec3019161 --- /dev/null +++ b/src/service/transport/gnunet-service-transport.c @@ -0,0 +1,11750 @@ +/* + This file is part of GNUnet. + Copyright (C) 2010-2016, 2018, 2019 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 . + + SPDX-License-Identifier: AGPL3.0-or-later + */ +/** + * @file transport/gnunet-service-transport.c + * @brief main for gnunet-service-transport + * @author Christian Grothoff + * + * TODO: + * Implement next: + * - review retransmission logic, right now there is no smartness there! + * => congestion control, etc [PERFORMANCE-BASICS] + * + * Optimizations-Statistics: + * - Track ACK losses based on ACK-counter [ROUTING] + * - Need to track total bandwidth per VirtualLink and adjust how frequently + * we send FC messages based on bandwidth-delay-product (and relation + * to the window size!). See OPTIMIZE-FC-BDP. + * - Consider more statistics in #check_connection_quality() [FIXME-CONQ-STATISTICS] + * - Adapt available_fc_window_size, using larger values for high-bandwidth + * and high-latency links *if* we have the RAM [GOODPUT / utilization / stalls] + * - Set last_window_consum_limit promise properly based on + * latency and bandwidth of the respective connection [GOODPUT / utilization / stalls] + * + * Optimizations-DV: + * - When forwarding DV learn messages, if a peer is reached that + * has a *bidirectional* link to the origin beyond 1st hop, + * do NOT forward it to peers _other_ than the origin, as + * there is clearly a better path directly from the origin to + * whatever else we could reach. + * - When we passively learned DV (with unconfirmed freshness), we + * right now add the path to our list but with a zero path_valid_until + * time and only use it for unconfirmed routes. However, we could consider + * triggering an explicit validation mechanism ourselves, specifically routing + * a challenge-response message over the path [ROUTING] + * = if available, try to confirm unconfirmed DV paths when trying to establish + * virtual link for a `struct IncomingRequest`. (i.e. if DVH is + * unconfirmed, incoming requests cause us to try to validate a passively + * learned path (requires new message type!)) + * + * Optimizations-Fragmentation: + * - Fragments send over a reliable channel could do without the + * AcknowledgementUUIDP altogether, as they won't be acked! [BANDWIDTH] + * (-> have 2nd type of acknowledgment message; low priority, as we + * do not have an MTU-limited *reliable* communicator) [FIXME-FRAG-REL-UUID] + * - if messages are below MTU, consider adding ACKs and other stuff + * to the same transmission to avoid tiny messages (requires planning at + * receiver, and additional MST-style demultiplex at receiver!) [PACKET COUNT] + * + * Optimizations-internals: + * - queue_send_msg by API design has to make a copy + * of the payload, and route_message on top of that requires a malloc/free. + * Change design to approximate "zero" copy better... [CPU] + * - could avoid copying body of message into each fragment and keep + * fragments as just pointers into the original message and only + * fully build fragments just before transmission (optimization, should + * reduce CPU and memory use) [CPU, MEMORY] + */ +#include "platform.h" +#include "gnunet_util_lib.h" +#include "gnunet_statistics_service.h" +#include "gnunet_transport_monitor_service.h" +#include "gnunet_peerstore_service.h" +#include "gnunet_hello_lib.h" +#include "gnunet_hello_uri_lib.h" +#include "gnunet_signatures.h" +#include "transport.h" + +/** + * Size of ring buffer to cache CORE and forwarded DVBox messages. + */ +#define RING_BUFFER_SIZE 16 + +/** + * Maximum number of FC retransmissions for a running retransmission task. + */ +#define MAX_FC_RETRANSMIT_COUNT 1000 + +/** + * Maximum number of messages we acknowledge together in one + * cumulative ACK. Larger values may save a bit of bandwidth. + */ +#define MAX_CUMMULATIVE_ACKS 64 + +/** + * What is the 1:n chance that we send a Flow control response when + * receiving a flow control message that did not change anything for + * us? Basically, this is used in the case where both peers are stuck + * on flow control (no window changes), but one might continue sending + * flow control messages to the other peer as the first FC message + * when things stalled got lost, and then subsequently the other peer + * does *usually* not respond as nothing changed. So to ensure that + * eventually the FC messages stop, we do send with 1/8th probability + * an FC message even if nothing changed. That prevents one peer + * being stuck in sending (useless) FC messages "forever". + */ +#define FC_NO_CHANGE_REPLY_PROBABILITY 8 + +/** + * What is the size we assume for a read operation in the + * absence of an MTU for the purpose of flow control? + */ +#define IN_PACKET_SIZE_WITHOUT_MTU 128 + +/** + * Number of slots we keep of historic data for computation of + * goodput / message loss ratio. + */ +#define GOODPUT_AGING_SLOTS 4 + +/** + * How big is the flow control window size by default; + * limits per-neighbour RAM utilization. + */ +#define DEFAULT_WINDOW_SIZE (128 * 1024) + +/** + * For how many incoming connections do we try to create a + * virtual link for (at the same time!). This does NOT + * limit the number of incoming connections, just the number + * for which we are actively trying to find working addresses + * in the absence (!) of our own applications wanting the + * link to go up. + */ +#define MAX_INCOMING_REQUEST 16 + +/** + * Maximum number of peers we select for forwarding DVInit + * messages at the same time (excluding initiator). + */ +#define MAX_DV_DISCOVERY_SELECTION 16 + +/** + * Window size. How many messages to the same target do we pass + * to CORE without a RECV_OK in between? Small values limit + * thoughput, large values will increase latency. + * + * FIXME-OPTIMIZE: find out what good values are experimentally, + * maybe set adaptively (i.e. to observed available bandwidth). + */ +#define RECV_WINDOW_SIZE 4 + +/** + * Minimum number of hops we should forward DV learn messages + * even if they are NOT useful for us in hope of looping + * back to the initiator? + * + * FIXME: allow initiator some control here instead? + */ +#define MIN_DV_PATH_LENGTH_FOR_INITIATOR 3 + +/** + * Maximum DV distance allowed ever. + */ +#define MAX_DV_HOPS_ALLOWED 16 + +/** + * Maximum number of DV learning activities we may + * have pending at the same time. + */ +#define MAX_DV_LEARN_PENDING 64 + +/** + * Maximum number of DV paths we keep simultaneously to the same target. + */ +#define MAX_DV_PATHS_TO_TARGET 3 + +/** + * If a queue delays the next message by more than this number + * of seconds we log a warning. Note: this is for testing, + * the value chosen here might be too aggressively low! + */ +#define DELAY_WARN_THRESHOLD \ + GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS, 5) + +/** + * If a DVBox could not be forwarded after this number of + * seconds we drop it. + */ +#define DV_FORWARD_TIMEOUT \ + GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS, 60) + +/** + * Default value for how long we wait for reliability ack. + */ +#define DEFAULT_ACK_WAIT_DURATION \ + GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS, 1) + +/** + * We only consider queues as "quality" connections when + * suppressing the generation of DV initiation messages if + * the latency of the queue is below this threshold. + */ +#define DV_QUALITY_RTT_THRESHOLD \ + GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS, 1) + +/** + * How long do we consider a DV path valid if we see no + * further updates on it? Note: the value chosen here might be too low! + */ +#define DV_PATH_VALIDITY_TIMEOUT \ + GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_MINUTES, 5) + +/** + * How long do we cache backchannel (struct Backtalker) information + * after a backchannel goes inactive? + */ +#define BACKCHANNEL_INACTIVITY_TIMEOUT \ + GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_MINUTES, 5) + +/** + * How long before paths expire would we like to (re)discover DV paths? Should + * be below #DV_PATH_VALIDITY_TIMEOUT. + */ +#define DV_PATH_DISCOVERY_FREQUENCY \ + GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_MINUTES, 4) + +/** + * How long are ephemeral keys valid? + */ +#define EPHEMERAL_VALIDITY \ + GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_HOURS, 4) + +/** + * How long do we keep partially reassembled messages around before giving up? + */ +#define REASSEMBLY_EXPIRATION \ + GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_MINUTES, 4) + +/** + * What is the fastest rate at which we send challenges *if* we keep learning + * an address (gossip, DHT, etc.)? + */ +#define FAST_VALIDATION_CHALLENGE_FREQ \ + GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_MINUTES, 1) + +/** + * What is the slowest rate at which we send challenges? + */ +#define MAX_VALIDATION_CHALLENGE_FREQ \ + GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_DAYS, 1) + +/** + * How long until we forget about historic accumulators and thus + * reset the ACK counter? Should exceed the maximum time an + * active connection experiences without an ACK. + */ +#define ACK_CUMMULATOR_TIMEOUT \ + GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_HOURS, 4) + +/** + * What is the non-randomized base frequency at which we + * would initiate DV learn messages? + */ +#define DV_LEARN_BASE_FREQUENCY GNUNET_TIME_UNIT_MINUTES + +/** + * How many good connections (confirmed, bi-directional, not DV) + * do we need to have to suppress initiating DV learn messages? + */ +#define DV_LEARN_QUALITY_THRESHOLD 100 + +/** + * When do we forget an invalid address for sure? + */ +#define MAX_ADDRESS_VALID_UNTIL \ + GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_MONTHS, 1) + +/** + * How long do we consider an address valid if we just checked? + */ +#define ADDRESS_VALIDATION_LIFETIME \ + GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_HOURS, 4) + +/** + * What is the maximum frequency at which we do address validation? + * A random value between 0 and this value is added when scheduling + * the #validation_task (both to ensure we do not validate too often, + * and to randomize a bit). + */ +#define MIN_DELAY_ADDRESS_VALIDATION GNUNET_TIME_UNIT_MILLISECONDS + +/** + * How many network RTTs before an address validation expires should we begin + * trying to revalidate? (Note that the RTT used here is the one that we + * experienced during the last validation, not necessarily the latest RTT + * observed). + */ +#define VALIDATION_RTT_BUFFER_FACTOR 3 + +/** + * How many messages can we have pending for a given communicator + * process before we start to throttle that communicator? + * + * Used if a communicator might be CPU-bound and cannot handle the traffic. + */ +#define COMMUNICATOR_TOTAL_QUEUE_LIMIT 512 + +/** + * How many messages can we have pending for a given queue (queue to + * a particular peer via a communicator) process before we start to + * throttle that queue? + */ +#define QUEUE_LENGTH_LIMIT 32 + + +GNUNET_NETWORK_STRUCT_BEGIN + +/** + * Unique identifier we attach to a message. + */ +struct MessageUUIDP +{ + /** + * Unique value, generated by incrementing the + * `message_uuid_ctr` of `struct Neighbour`. + */ + uint64_t uuid GNUNET_PACKED; +}; + + +/** + * Unique identifier to map an acknowledgement to a transmission. + */ +struct AcknowledgementUUIDP +{ + /** + * The UUID value. + */ + struct GNUNET_Uuid value; +}; + +/** + * Outer layer of an encapsulated backchannel message. + */ +struct TransportBackchannelEncapsulationMessage +{ + /** + * Type is #GNUNET_MESSAGE_TYPE_TRANSPORT_BACKCHANNEL_ENCAPSULATION. + */ + struct GNUNET_MessageHeader header; + + /* Followed by *another* message header which is the message to + the communicator */ + + /* Followed by a 0-terminated name of the communicator */ +}; + + +/** + * Body by which a peer confirms that it is using an ephemeral key. + */ +struct EphemeralConfirmationPS +{ + /** + * Purpose is #GNUNET_SIGNATURE_PURPOSE_TRANSPORT_EPHEMERAL + */ + struct GNUNET_CRYPTO_EccSignaturePurpose purpose; + + /** + * How long is this signature over the ephemeral key valid? + * + * Note that the receiver MUST IGNORE the absolute time, and only interpret + * the value as a mononic time and reject "older" values than the last one + * observed. This is necessary as we do not want to require synchronized + * clocks and may not have a bidirectional communication channel. + * + * Even with this, there is no real guarantee against replay achieved here, + * unless the latest timestamp is persisted. While persistence should be + * provided via PEERSTORE, we do not consider the mechanism reliable! Thus, + * communicators must protect against replay attacks when using backchannel + * communication! + */ + struct GNUNET_TIME_AbsoluteNBO sender_monotonic_time; + + /** + * Target's peer identity. + */ + struct GNUNET_PeerIdentity target; + + /** + * Ephemeral key setup by the sender for @e target, used + * to encrypt the payload. + */ + struct GNUNET_CRYPTO_EcdhePublicKey ephemeral_key; +}; + + +/** + * Plaintext of the variable-size payload that is encrypted + * within a `struct TransportBackchannelEncapsulationMessage` + */ +struct TransportDVBoxPayloadP +{ + /** + * Sender's peer identity. + */ + struct GNUNET_PeerIdentity sender; + + /** + * Signature of the sender over an + * #GNUNET_SIGNATURE_PURPOSE_TRANSPORT_EPHEMERAL. + */ + struct GNUNET_CRYPTO_EddsaSignature sender_sig; + + /** + * Current monotonic time of the sending transport service. Used to + * detect replayed messages. Note that the receiver should remember + * a list of the recently seen timestamps and only reject messages + * if the timestamp is in the list, or the list is "full" and the + * timestamp is smaller than the lowest in the list. + * + * Like the @e ephemeral_validity, the list of timestamps per peer should be + * persisted to guard against replays after restarts. + */ + struct GNUNET_TIME_AbsoluteNBO monotonic_time; + + /* Followed by a `struct GNUNET_MessageHeader` with a message + for the target peer */ +}; + + +/** + * Outer layer of an encapsulated unfragmented application message sent + * over an unreliable channel. + */ +struct TransportReliabilityBoxMessage +{ + /** + * Type is #GNUNET_MESSAGE_TYPE_TRANSPORT_RELIABILITY_BOX + */ + struct GNUNET_MessageHeader header; + + /** + * Number of messages still to be sent before a commulative + * ACK is requested. Zero if an ACK is requested immediately. + * In NBO. Note that the receiver may send the ACK faster + * if it believes that is reasonable. + */ + uint32_t ack_countdown GNUNET_PACKED; + + /** + * Unique ID of the message used for signalling receipt of + * messages sent over possibly unreliable channels. Should + * be a random. + */ + struct AcknowledgementUUIDP ack_uuid; +}; + + +/** + * Acknowledgement payload. + */ +struct TransportCummulativeAckPayloadP +{ + /** + * How long was the ACK delayed for generating cumulative ACKs? + * Used to calculate the correct network RTT by taking the receipt + * time of the ack minus the transmission time of the sender minus + * this value. + */ + struct GNUNET_TIME_RelativeNBO ack_delay; + + /** + * UUID of a message being acknowledged. + */ + struct AcknowledgementUUIDP ack_uuid; +}; + + +/** + * Confirmation that the receiver got a + * #GNUNET_MESSAGE_TYPE_TRANSPORT_RELIABILITY_BOX. Note that the + * confirmation may be transmitted over a completely different queue, + * so ACKs are identified by a combination of PID of sender and + * message UUID, without the queue playing any role! + */ +struct TransportReliabilityAckMessage +{ + /** + * Type is #GNUNET_MESSAGE_TYPE_TRANSPORT_RELIABILITY_ACK + */ + struct GNUNET_MessageHeader header; + + /** + * Counter of ACKs transmitted by the sender to us. Incremented + * by one for each ACK, used to detect how many ACKs were lost. + */ + uint32_t ack_counter GNUNET_PACKED; + + /* followed by any number of `struct TransportCummulativeAckPayloadP` + messages providing ACKs */ +}; + + +/** + * Outer layer of an encapsulated fragmented application message. + */ +struct TransportFragmentBoxMessage +{ + /** + * Type is #GNUNET_MESSAGE_TYPE_TRANSPORT_FRAGMENT + */ + struct GNUNET_MessageHeader header; + + /** + * Offset of this fragment in the overall message. + */ + uint16_t frag_off GNUNET_PACKED; + + /** + * Total size of the message that is being fragmented. + */ + uint16_t msg_size GNUNET_PACKED; + + /** + * Unique ID of this fragment (and fragment transmission!). Will + * change even if a fragment is retransmitted to make each + * transmission attempt unique! If a client receives a duplicate + * fragment (same @e frag_off for same @a msg_uuid, it must send + * #GNUNET_MESSAGE_TYPE_TRANSPORT_RELIABILITY_ACK immediately. + */ + struct AcknowledgementUUIDP ack_uuid; + + /** + * Original message ID for of the message that all the fragments + * belong to. Must be the same for all fragments. + */ + struct MessageUUIDP msg_uuid; +}; + + +/** + * Content signed by the initator during DV learning. + * + * The signature is required to prevent DDoS attacks. A peer sending out this + * message is potentially generating a lot of traffic that will go back to the + * initator, as peers receiving this message will try to let the initiator + * know that they got the message. + * + * Without this signature, an attacker could abuse this mechanism for traffic + * amplification, sending a lot of traffic to a peer by putting out this type + * of message with the victim's peer identity. + * + * Even with just a signature, traffic amplification would be possible via + * replay attacks. The @e monotonic_time limits such replay attacks, as every + * potential amplificator will check the @e monotonic_time and only respond + * (at most) once per message. + */ +struct DvInitPS +{ + /** + * Purpose is #GNUNET_SIGNATURE_PURPOSE_TRANSPORT_DV_INITIATOR + */ + struct GNUNET_CRYPTO_EccSignaturePurpose purpose; + + /** + * Time at the initiator when generating the signature. + * + * Note that the receiver MUST IGNORE the absolute time, and only interpret + * the value as a mononic time and reject "older" values than the last one + * observed. This is necessary as we do not want to require synchronized + * clocks and may not have a bidirectional communication channel. + * + * Even with this, there is no real guarantee against replay achieved here, + * unless the latest timestamp is persisted. Persistence should be + * provided via PEERSTORE if possible. + */ + struct GNUNET_TIME_AbsoluteNBO monotonic_time; + + /** + * Challenge value used by the initiator to re-identify the path. + */ + struct GNUNET_CRYPTO_ChallengeNonceP challenge; +}; + + +/** + * Content signed by each peer during DV learning. + * + * This assues the initiator of the DV learning operation that the hop from @e + * pred via the signing peer to @e succ actually exists. This makes it + * impossible for an adversary to supply the network with bogus routes. + * + * The @e challenge is included to provide replay protection for the + * initiator. This way, the initiator knows that the hop existed after the + * original @e challenge was first transmitted, providing a freshness metric. + * + * Peers other than the initiator that passively learn paths by observing + * these messages do NOT benefit from this. Here, an adversary may indeed + * replay old messages. Thus, passively learned paths should always be + * immediately marked as "potentially stale". + */ +struct DvHopPS +{ + /** + * Purpose is #GNUNET_SIGNATURE_PURPOSE_TRANSPORT_DV_HOP + */ + struct GNUNET_CRYPTO_EccSignaturePurpose purpose; + + /** + * Identity of the previous peer on the path. + */ + struct GNUNET_PeerIdentity pred; + + /** + * Identity of the next peer on the path. + */ + struct GNUNET_PeerIdentity succ; + + /** + * Challenge value used by the initiator to re-identify the path. + */ + struct GNUNET_CRYPTO_ChallengeNonceP challenge; +}; + + +/** + * An entry describing a peer on a path in a + * `struct TransportDVLearnMessage` message. + */ +struct DVPathEntryP +{ + /** + * Identity of a peer on the path. + */ + struct GNUNET_PeerIdentity hop; + + /** + * Signature of this hop over the path, of purpose + * #GNUNET_SIGNATURE_PURPOSE_TRANSPORT_DV_HOP + */ + struct GNUNET_CRYPTO_EddsaSignature hop_sig; +}; + + +/** + * Internal message used by transport for distance vector learning. + * If @e num_hops does not exceed the threshold, peers should append + * themselves to the peer list and flood the message (possibly only + * to a subset of their neighbours to limit discoverability of the + * network topology). To the extend that the @e bidirectional bits + * are set, peers may learn the inverse paths even if they did not + * initiate. + * + * Unless received on a bidirectional queue and @e num_hops just + * zero, peers that can forward to the initator should always try to + * forward to the initiator. + */ +struct TransportDVLearnMessage +{ + /** + * Type is #GNUNET_MESSAGE_TYPE_TRANSPORT_DV_LEARN + */ + struct GNUNET_MessageHeader header; + + /** + * Number of hops this messages has travelled, in NBO. Zero if + * sent by initiator. + */ + uint16_t num_hops GNUNET_PACKED; + + /** + * Bitmask of the last 16 hops indicating whether they are confirmed + * available (without DV) in both directions or not, in NBO. Used + * to possibly instantly learn a path in both directions. Each peer + * should shift this value by one to the left, and then set the + * lowest bit IF the current sender can be reached from it (without + * DV routing). + */ + uint16_t bidirectional GNUNET_PACKED; + + /** + * Peers receiving this message and delaying forwarding to other + * peers for any reason should increment this value by the non-network + * delay created by the peer. + */ + struct GNUNET_TIME_RelativeNBO non_network_delay; + + /** + * Time at the initiator when generating the signature. + * + * Note that the receiver MUST IGNORE the absolute time, and only interpret + * the value as a mononic time and reject "older" values than the last one + * observed. This is necessary as we do not want to require synchronized + * clocks and may not have a bidirectional communication channel. + * + * Even with this, there is no real guarantee against replay achieved here, + * unless the latest timestamp is persisted. Persistence should be + * provided via PEERSTORE if possible. + */ + struct GNUNET_TIME_AbsoluteNBO monotonic_time; + + /** + * Signature of this hop over the path, of purpose + * #GNUNET_SIGNATURE_PURPOSE_TRANSPORT_DV_INITIATOR + */ + struct GNUNET_CRYPTO_EddsaSignature init_sig; + + /** + * Identity of the peer that started this learning activity. + */ + struct GNUNET_PeerIdentity initiator; + + /** + * Challenge value used by the initiator to re-identify the path. + */ + struct GNUNET_CRYPTO_ChallengeNonceP challenge; + + /* Followed by @e num_hops `struct DVPathEntryP` values, + excluding the initiator of the DV trace; the last entry is the + current sender; the current peer must not be included. */ +}; + + +/** + * Outer layer of an encapsulated message send over multiple hops. + * The path given only includes the identities of the subsequent + * peers, i.e. it will be empty if we are the receiver. Each + * forwarding peer should scan the list from the end, and if it can, + * forward to the respective peer. The list should then be shortened + * by all the entries up to and including that peer. Each hop should + * also increment @e total_hops to allow the receiver to get a precise + * estimate on the number of hops the message travelled. Senders must + * provide a learned path that thus should work, but intermediaries + * know of a shortcut, they are allowed to send the message via that + * shortcut. + * + * If a peer finds itself still on the list, it must drop the message. + * + * The payload of the box can only be decrypted and verified by the + * ultimate receiver. Intermediaries do not learn the sender's + * identity and the path the message has taken. However, the first + * hop does learn the sender as @e total_hops would be zero and thus + * the predecessor must be the origin (so this is not really useful + * for anonymization). + */ +struct TransportDVBoxMessage +{ + /** + * Type is #GNUNET_MESSAGE_TYPE_TRANSPORT_DV_BOX + */ + struct GNUNET_MessageHeader header; + + /** + * Flag if the payload is a control message. In NBO. + */ + unsigned int without_fc; + + /** + * Number of total hops this messages travelled. In NBO. + * @e origin sets this to zero, to be incremented at + * each hop. Peers should limit the @e total_hops value + * they accept from other peers. + */ + uint16_t total_hops GNUNET_PACKED; + + /** + * Number of hops this messages includes. In NBO. Reduced by one + * or more at each hop. Peers should limit the @e num_hops value + * they accept from other peers. + */ + uint16_t num_hops GNUNET_PACKED; + + /** + * Ephemeral key setup by the sender for target, used to encrypt the + * payload. Intermediaries must not change this value. + */ + struct GNUNET_CRYPTO_EcdhePublicKey ephemeral_key; + + /** + * We use an IV here as the @e ephemeral_key is re-used for + * #EPHEMERAL_VALIDITY time to avoid re-signing it all the time. + * Intermediaries must not change this value. + */ + struct GNUNET_ShortHashCode iv; + + /** + * HMAC over the ciphertext of the encrypted, variable-size body + * that follows. Verified via DH of target and @e ephemeral_key. + * Intermediaries must not change this value. + */ + struct GNUNET_HashCode hmac; + + /** + * Size this msg had initially. This is needed to calculate the hmac at the target. + * The header size can not be used for that, because the box size is getting smaller at each hop. + * + */ + uint16_t orig_size GNUNET_PACKED; + + /* Followed by @e num_hops `struct GNUNET_PeerIdentity` values; + excluding the @e origin and the current peer, the last must be + the ultimate target; if @e num_hops is zero, the receiver of this + message is the ultimate target. */ + + /* Followed by encrypted, variable-size payload, which + must begin with a `struct TransportDVBoxPayloadP` */ + + /* Followed by the actual message, which itself must not be a + a DV_LEARN or DV_BOX message! */ +}; + + +/** + * Message send to another peer to validate that it can indeed + * receive messages at a particular address. + */ +struct TransportValidationChallengeMessage +{ + /** + * Type is #GNUNET_MESSAGE_TYPE_TRANSPORT_ADDRESS_VALIDATION_CHALLENGE + */ + struct GNUNET_MessageHeader header; + + /** + * Always zero. + */ + uint32_t reserved GNUNET_PACKED; + + /** + * Challenge to be signed by the receiving peer. + */ + struct GNUNET_CRYPTO_ChallengeNonceP challenge; + + /** + * Timestamp of the sender, to be copied into the reply to allow + * sender to calculate RTT. Must be monotonically increasing! + */ + struct GNUNET_TIME_AbsoluteNBO sender_time; +}; + + +/** + * Message signed by a peer to confirm that it can indeed + * receive messages at a particular address. + */ +struct TransportValidationPS +{ + /** + * Purpose is #GNUNET_SIGNATURE_PURPOSE_TRANSPORT_CHALLENGE + */ + struct GNUNET_CRYPTO_EccSignaturePurpose purpose; + + /** + * How long does the sender believe the address on + * which the challenge was received to remain valid? + */ + struct GNUNET_TIME_RelativeNBO validity_duration; + + /** + * Challenge signed by the receiving peer. + */ + struct GNUNET_CRYPTO_ChallengeNonceP challenge; +}; + + +/** + * Message send to a peer to respond to a + * #GNUNET_MESSAGE_TYPE_ADDRESS_VALIDATION_CHALLENGE + */ +struct TransportValidationResponseMessage +{ + /** + * Type is #GNUNET_MESSAGE_TYPE_TRANSPORT_ADDRESS_VALIDATION_RESPONSE + */ + struct GNUNET_MessageHeader header; + + /** + * Always zero. + */ + uint32_t reserved GNUNET_PACKED; + + /** + * The peer's signature matching the + * #GNUNET_SIGNATURE_PURPOSE_TRANSPORT_CHALLENGE purpose. + */ + struct GNUNET_CRYPTO_EddsaSignature signature; + + /** + * The challenge that was signed by the receiving peer. + */ + struct GNUNET_CRYPTO_ChallengeNonceP challenge; + + /** + * Original timestamp of the sender (was @code{sender_time}), + * copied into the reply to allow sender to calculate RTT. + */ + struct GNUNET_TIME_AbsoluteNBO origin_time; + + /** + * How long does the sender believe this address to remain + * valid? + */ + struct GNUNET_TIME_RelativeNBO validity_duration; +}; + + +/** + * Message for Transport-to-Transport Flow control. Specifies the size + * of the flow control window, including how much we believe to have + * consumed (at transmission time), how much we believe to be allowed + * (at transmission time), and how much the other peer is allowed to + * send to us, and how much data we already received from the other + * peer. + */ +struct TransportFlowControlMessage +{ + /** + * Type is #GNUNET_MESSAGE_TYPE_TRANSPORT_FLOW_CONTROL + */ + struct GNUNET_MessageHeader header; + + /** + * Sequence number of the flow control message. Incremented by one + * for each message. Starts at zero when a virtual link goes up. + * Used to detect one-sided connection drops. On wrap-around, the + * flow control counters will be reset as if the connection had + * dropped. + */ + uint32_t seq GNUNET_PACKED; + + /** + * Flow control window size in bytes, in NBO. + * The receiver can send this many bytes at most. + */ + uint64_t inbound_window_size GNUNET_PACKED; + + /** + * How many bytes has the sender sent that count for flow control at + * this time. Used to allow the receiver to estimate the packet + * loss rate. + */ + uint64_t outbound_sent GNUNET_PACKED; + + /** + * Latest flow control window size we learned from the other peer, + * in bytes, in NBO. We are limited to sending at most this many + * bytes to the other peer. May help the other peer detect when + * flow control messages were lost and should thus be retransmitted. + * In particular, if the delta to @e outbound_sent is too small, + * this signals that we are stalled. + */ + uint64_t outbound_window_size GNUNET_PACKED; + + /** + * Timestamp of the sender. Must be monotonically increasing! + * Used to enable receiver to ignore out-of-order packets in + * combination with the @e seq. Note that @e seq will go down + * (back to zero) whenever either side believes the connection + * was dropped, allowing the peers to detect that they need to + * reset the counters for the number of bytes sent! + */ + struct GNUNET_TIME_AbsoluteNBO sender_time; +}; + + +GNUNET_NETWORK_STRUCT_END + + +/** + * What type of client is the `struct TransportClient` about? + */ +enum ClientType +{ + /** + * We do not know yet (client is fresh). + */ + CT_NONE = 0, + + /** + * Is the CORE service, we need to forward traffic to it. + */ + CT_CORE = 1, + + /** + * It is a monitor, forward monitor data. + */ + CT_MONITOR = 2, + + /** + * It is a communicator, use for communication. + */ + CT_COMMUNICATOR = 3, + + /** + * "Application" telling us where to connect (i.e. TOPOLOGY, DHT or CADET). + */ + CT_APPLICATION = 4 +}; + + +/** + * Which transmission options are allowable for transmission? + * Interpreted bit-wise! + */ +enum RouteMessageOptions +{ + /** + * Only confirmed, non-DV direct neighbours. + */ + RMO_NONE = 0, + + /** + * We are allowed to use DV routing for this @a hdr + */ + RMO_DV_ALLOWED = 1, + + /** + * We are allowed to use unconfirmed queues or DV routes for this message + */ + RMO_UNCONFIRMED_ALLOWED = 2, + + /** + * Reliable and unreliable, DV and non-DV are all acceptable. + */ + RMO_ANYTHING_GOES = (RMO_DV_ALLOWED | RMO_UNCONFIRMED_ALLOWED), + + /** + * If we have multiple choices, it is OK to send this message + * over multiple channels at the same time to improve loss tolerance. + * (We do at most 2 transmissions.) + */ + RMO_REDUNDANT = 4 +}; + + +/** + * When did we launch this DV learning activity? + */ +struct LearnLaunchEntry +{ + /** + * Kept (also) in a DLL sorted by launch time. + */ + struct LearnLaunchEntry *prev; + + /** + * Kept (also) in a DLL sorted by launch time. + */ + struct LearnLaunchEntry *next; + + /** + * Challenge that uniquely identifies this activity. + */ + struct GNUNET_CRYPTO_ChallengeNonceP challenge; + + /** + * When did we transmit the DV learn message (used to calculate RTT) and + * determine freshness of paths learned via this operation. + */ + struct GNUNET_TIME_Absolute launch_time; +}; + + +/** + * Information we keep per #GOODPUT_AGING_SLOTS about historic + * (or current) transmission performance. + */ +struct TransmissionHistoryEntry +{ + /** + * Number of bytes actually sent in the interval. + */ + uint64_t bytes_sent; + + /** + * Number of bytes received and acknowledged by the other peer in + * the interval. + */ + uint64_t bytes_received; +}; + + +/** + * Performance data for a transmission possibility. + */ +struct PerformanceData +{ + /** + * Weighted average for the RTT. + */ + struct GNUNET_TIME_Relative aged_rtt; + + /** + * Historic performance data, using a ring buffer of#GOODPUT_AGING_SLOTS + * entries. + */ + struct TransmissionHistoryEntry the[GOODPUT_AGING_SLOTS]; + + /** + * What was the last age when we wrote to @e the? Used to clear + * old entries when the age advances. + */ + unsigned int last_age; +}; + + +/** + * Client connected to the transport service. + */ +struct TransportClient; + +/** + * A neighbour that at least one communicator is connected to. + */ +struct Neighbour; + +/** + * Entry in our #dv_routes table, representing a (set of) distance + * vector routes to a particular peer. + */ +struct DistanceVector; + +/** + * A queue is a message queue provided by a communicator + * via which we can reach a particular neighbour. + */ +struct Queue; + +/** + * Message awaiting transmission. See detailed comments below. + */ +struct PendingMessage; + +/** + * One possible hop towards a DV target. + */ +struct DistanceVectorHop; + +/** + * A virtual link is another reachable peer that is known to CORE. It + * can be either a `struct Neighbour` with at least one confirmed + * `struct Queue`, or a `struct DistanceVector` with at least one + * confirmed `struct DistanceVectorHop`. With a virtual link we track + * data that is per neighbour that is not specific to how the + * connectivity is established. + */ +struct VirtualLink; + + +/** + * Context from #handle_incoming_msg(). Closure for many + * message handlers below. + */ +struct CommunicatorMessageContext +{ + /** + * Kept in a DLL of `struct VirtualLink` if waiting for CORE + * flow control to unchoke. + */ + struct CommunicatorMessageContext *next; + + /** + * Kept in a DLL of `struct VirtualLink` if waiting for CORE + * flow control to unchoke. + */ + struct CommunicatorMessageContext *prev; + + /** + * Which communicator provided us with the message. + */ + struct TransportClient *tc; + + /** + * Additional information for flow control and about the sender. + */ + struct GNUNET_TRANSPORT_IncomingMessage im; + + /** + * The message to demultiplex. + */ + const struct GNUNET_MessageHeader *mh; + + /** + * Number of hops the message has travelled (if DV-routed). + * FIXME: make use of this in ACK handling! + */ + uint16_t total_hops; +}; + + +/** + * Entry for the ring buffer caching messages send to core, when virtual link is avaliable. + **/ +struct RingBufferEntry +{ + /** + * Communicator context for this ring buffer entry. + **/ + struct CommunicatorMessageContext *cmc; + + /** + * The message in this entry. + **/ + struct GNUNET_MessageHeader *mh; +}; + + +/** + * Closure for #core_env_sent_cb. + */ +struct CoreSentContext +{ + /** + * Kept in a DLL to clear @e vl in case @e vl is lost. + */ + struct CoreSentContext *next; + + /** + * Kept in a DLL to clear @e vl in case @e vl is lost. + */ + struct CoreSentContext *prev; + + /** + * Virtual link this is about. + */ + struct VirtualLink *vl; + + /** + * How big was the message. + */ + uint16_t size; + + /** + * By how much should we increment @e vl's + * incoming_fc_window_size_used once we are done sending to CORE? + * Use to ensure we do not increment twice if there is more than one + * CORE client. + */ + uint16_t isize; +}; + + +/** + * Information we keep for a message that we are reassembling. + */ +struct ReassemblyContext +{ + /** + * Original message ID for of the message that all the fragments + * belong to. + */ + struct MessageUUIDP msg_uuid; + + /** + * Which neighbour is this context for? + */ + struct VirtualLink *virtual_link; + + /** + * Entry in the reassembly heap (sorted by expiration). + */ + struct GNUNET_CONTAINER_HeapNode *hn; + + /** + * Bitfield with @e msg_size bits representing the positions + * where we have received fragments. When we receive a fragment, + * we check the bits in @e bitfield before incrementing @e msg_missing. + * + * Allocated after the reassembled message. + */ + uint8_t *bitfield; + + /** + * At what time will we give up reassembly of this message? + */ + struct GNUNET_TIME_Absolute reassembly_timeout; + + /** + * Time we received the last fragment. @e avg_ack_delay must be + * incremented by now - @e last_frag multiplied by @e num_acks. + */ + struct GNUNET_TIME_Absolute last_frag; + + /** + * How big is the message we are reassembling in total? + */ + uint16_t msg_size; + + /** + * How many bytes of the message are still missing? Defragmentation + * is complete when @e msg_missing == 0. + */ + uint16_t msg_missing; + + /* Followed by @e msg_size bytes of the (partially) defragmented original + * message */ + + /* Followed by @e bitfield data */ +}; + + +/** + * A virtual link is another reachable peer that is known to CORE. It + * can be either a `struct Neighbour` with at least one confirmed + * `struct Queue`, or a `struct DistanceVector` with at least one + * confirmed `struct DistanceVectorHop`. With a virtual link we track + * data that is per neighbour that is not specific to how the + * connectivity is established. + */ +struct VirtualLink +{ + /** + * Identity of the peer at the other end of the link. + */ + struct GNUNET_PeerIdentity target; + + /** + * Map with `struct ReassemblyContext` structs for fragments under + * reassembly. May be NULL if we currently have no fragments from + * this @e pid (lazy initialization). + */ + struct GNUNET_CONTAINER_MultiHashMap32 *reassembly_map; + + /** + * Heap with `struct ReassemblyContext` structs for fragments under + * reassembly. May be NULL if we currently have no fragments from + * this @e pid (lazy initialization). + */ + struct GNUNET_CONTAINER_Heap *reassembly_heap; + + /** + * Task to free old entries from the @e reassembly_heap and @e reassembly_map. + */ + struct GNUNET_SCHEDULER_Task *reassembly_timeout_task; + + /** + * Communicators blocked for receiving on @e target as we are waiting + * on the @e core_recv_window to increase. + */ + struct CommunicatorMessageContext *cmc_head; + + /** + * Communicators blocked for receiving on @e target as we are waiting + * on the @e core_recv_window to increase. + */ + struct CommunicatorMessageContext *cmc_tail; + + /** + * Head of list of messages pending for this VL. + */ + struct PendingMessage *pending_msg_head; + + /** + * Tail of list of messages pending for this VL. + */ + struct PendingMessage *pending_msg_tail; + + /** + * Kept in a DLL to clear @e vl in case @e vl is lost. + */ + struct CoreSentContext *csc_tail; + + /** + * Kept in a DLL to clear @e vl in case @e vl is lost. + */ + struct CoreSentContext *csc_head; + + /** + * Task scheduled to possibly notfiy core that this peer is no + * longer counting as confirmed. Runs the #core_visibility_check(), + * which checks that some DV-path or a queue exists that is still + * considered confirmed. + */ + struct GNUNET_SCHEDULER_Task *visibility_task; + + /** + * Task scheduled to periodically retransmit FC messages (in + * case one got lost). + */ + struct GNUNET_SCHEDULER_Task *fc_retransmit_task; + + /** + * Number of FC retransmissions for this running task. + */ + unsigned int fc_retransmit_count; + + /** + * Is this VirtualLink confirmed. + * A unconfirmed VirtualLink might exist, if we got a FC from that target. + */ + unsigned int confirmed; + + /** + * Neighbour used by this virtual link, NULL if @e dv is used. + */ + struct Neighbour *n; + + /** + * Distance vector used by this virtual link, NULL if @e n is used. + */ + struct DistanceVector *dv; + + /** + * Sender timestamp of @e n_challenge, used to generate out-of-order + * challenges (as sender's timestamps must be monotonically + * increasing). FIXME: where do we need this? + */ + struct GNUNET_TIME_Absolute n_challenge_time; + + /** + * When did we last send a + * #GNUNET_MESSAGE_TYPE_TRANSPORT_FLOW_CONTROL message? + * Used to determine whether it is time to re-transmit the message. + */ + struct GNUNET_TIME_Absolute last_fc_transmission; + + /** + * Sender timestamp of the last + * #GNUNET_MESSAGE_TYPE_TRANSPORT_FLOW_CONTROL message we have + * received. Note that we do not persist this monotonic time as we + * do not really have to worry about ancient flow control window + * sizes after restarts. + */ + struct GNUNET_TIME_Absolute last_fc_timestamp; + + /** + * Expected RTT from the last FC transmission. (Zero if the last + * attempt failed, but could theoretically be zero even on success.) + */ + struct GNUNET_TIME_Relative last_fc_rtt; + + /** + * Used to generate unique UUIDs for messages that are being + * fragmented. + */ + uint64_t message_uuid_ctr; + + /** + * Memory allocated for this virtual link. Expresses how much RAM + * we are willing to allocate to this virtual link. OPTIMIZE-ME: + * Can be adapted to dedicate more RAM to links that need it, while + * sticking to some overall RAM limit. For now, set to + * #DEFAULT_WINDOW_SIZE. + */ + uint64_t available_fc_window_size; + + /** + * Memory actually used to buffer packets on this virtual link. + * Expresses how much RAM we are currently using for virtual link. + * Note that once CORE is done with a packet, we decrement the value + * here. + */ + uint64_t incoming_fc_window_size_ram; + + /** + * Last flow control window size we provided to the other peer, in + * bytes. We are allowing the other peer to send this + * many bytes. + */ + uint64_t incoming_fc_window_size; + + /** + * How much of the window did the other peer successfully use (and + * we already passed it on to CORE)? Must be below @e + * incoming_fc_window_size. We should effectively signal the + * other peer that the window is this much bigger at the next + * opportunity / challenge. + */ + uint64_t incoming_fc_window_size_used; + + /** + * What is our current estimate on the message loss rate for the sender? + * Based on the difference between how much the sender sent according + * to the last #GNUNET_MESSAGE_TYPE_TRANSPORT_FLOW_CONTROL message + * (@e outbound_sent field) and how much we actually received at that + * time (@e incoming_fc_window_size_used). This delta is then + * added onto the @e incoming_fc_window_size when determining the + * @e outbound_window_size we send to the other peer. Initially zero. + * May be negative if we (due to out-of-order delivery) actually received + * more than the sender claims to have sent in its last FC message. + */ + int64_t incoming_fc_window_size_loss; + + /** + * Our current flow control window size in bytes. We + * are allowed to transmit this many bytes to @a n. + */ + uint64_t outbound_fc_window_size; + + /** + * How much of our current flow control window size have we + * used (in bytes). Must be below + * @e outbound_fc_window_size. + */ + uint64_t outbound_fc_window_size_used; + + /** + * What is the most recent FC window the other peer sent us + * in `outbound_window_size`? This is basically the window + * size value the other peer has definitively received from + * us. If it matches @e incoming_fc_window_size, we should + * not send a FC message to increase the FC window. However, + * we may still send an FC message to notify the other peer + * that we received the other peer's FC message. + */ + uint64_t last_outbound_window_size_received; + + /** + * Generator for the sequence numbers of + * #GNUNET_MESSAGE_TYPE_TRANSPORT_FLOW_CONTROL messages we send. + */ + uint32_t fc_seq_gen; + + /** + * Last sequence number of a + * #GNUNET_MESSAGE_TYPE_TRANSPORT_FLOW_CONTROL message we have + * received. + */ + uint32_t last_fc_seq; + + /** + * How many more messages can we send to CORE before we exhaust + * the receive window of CORE for this peer? If this hits zero, + * we must tell communicators to stop providing us more messages + * for this peer. In fact, the window can go negative as we + * have multiple communicators, so per communicator we can go + * down by one into the negative range. Furthermore, we count + * delivery per CORE client, so if we had multiple cores, that + * might also cause a negative window size here (as one message + * would decrement the window by one per CORE client). + */ + int core_recv_window; +}; + + +/** + * Data structure kept when we are waiting for an acknowledgement. + */ +struct PendingAcknowledgement +{ + /** + * If @e pm is non-NULL, this is the DLL in which this acknowledgement + * is kept in relation to its pending message. + */ + struct PendingAcknowledgement *next_pm; + + /** + * If @e pm is non-NULL, this is the DLL in which this acknowledgement + * is kept in relation to its pending message. + */ + struct PendingAcknowledgement *prev_pm; + + /** + * If @e queue is non-NULL, this is the DLL in which this acknowledgement + * is kept in relation to the queue that was used to transmit the + * @a pm. + */ + struct PendingAcknowledgement *next_queue; + + /** + * If @e queue is non-NULL, this is the DLL in which this acknowledgement + * is kept in relation to the queue that was used to transmit the + * @a pm. + */ + struct PendingAcknowledgement *prev_queue; + + /** + * If @e dvh is non-NULL, this is the DLL in which this acknowledgement + * is kept in relation to the DVH that was used to transmit the + * @a pm. + */ + struct PendingAcknowledgement *next_dvh; + + /** + * If @e dvh is non-NULL, this is the DLL in which this acknowledgement + * is kept in relation to the DVH that was used to transmit the + * @a pm. + */ + struct PendingAcknowledgement *prev_dvh; + + /** + * Pointers for the DLL of all pending acknowledgements. + * This list is sorted by @e transmission time. If the list gets too + * long, the oldest entries are discarded. + */ + struct PendingAcknowledgement *next_pa; + + /** + * Pointers for the DLL of all pending acknowledgements. + * This list is sorted by @e transmission time. If the list gets too + * long, the oldest entries are discarded. + */ + struct PendingAcknowledgement *prev_pa; + + /** + * Unique identifier for this transmission operation. + */ + struct AcknowledgementUUIDP ack_uuid; + + /** + * Message that was transmitted, may be NULL if the message was ACKed + * via another channel. + */ + struct PendingMessage *pm; + + /** + * Distance vector path chosen for this transmission, NULL if transmission + * was to a direct neighbour OR if the path was forgotten in the meantime. + */ + struct DistanceVectorHop *dvh; + + /** + * Queue used for transmission, NULL if the queue has been destroyed + * (which may happen before we get an acknowledgement). + */ + struct Queue *queue; + + /** + * Time of the transmission, for RTT calculation. + */ + struct GNUNET_TIME_Absolute transmission_time; + + /** + * Number of bytes of the original message (to calculate bandwidth). + */ + uint16_t message_size; + + /** + * How often the PendingMessage was send via the Queue of this PendingAcknowledgement. + */ + unsigned int num_send; +}; + + +/** + * One possible hop towards a DV target. + */ +struct DistanceVectorHop +{ + /** + * Kept in a MDLL, sorted by @e timeout. + */ + struct DistanceVectorHop *next_dv; + + /** + * Kept in a MDLL, sorted by @e timeout. + */ + struct DistanceVectorHop *prev_dv; + + /** + * Kept in a MDLL. + */ + struct DistanceVectorHop *next_neighbour; + + /** + * Kept in a MDLL. + */ + struct DistanceVectorHop *prev_neighbour; + + /** + * Head of DLL of PAs that used our @a path. + */ + struct PendingAcknowledgement *pa_head; + + /** + * Tail of DLL of PAs that used our @a path. + */ + struct PendingAcknowledgement *pa_tail; + + /** + * What would be the next hop to @e target? + */ + struct Neighbour *next_hop; + + /** + * Distance vector entry this hop belongs with. + */ + struct DistanceVector *dv; + + /** + * Array of @e distance hops to the target, excluding @e next_hop. + * NULL if the entire path is us to @e next_hop to `target`. Allocated + * at the end of this struct. Excludes the target itself! + */ + const struct GNUNET_PeerIdentity *path; + + /** + * At what time do we forget about this path unless we see it again + * while learning? + */ + struct GNUNET_TIME_Absolute timeout; + + /** + * For how long is the validation of this path considered + * valid? + * Set to ZERO if the path is learned by snooping on DV learn messages + * initiated by other peers, and to the time at which we generated the + * challenge for DV learn operations this peer initiated. + */ + struct GNUNET_TIME_Absolute path_valid_until; + + /** + * Performance data for this transmission possibility. + */ + struct PerformanceData pd; + + /** + * Number of hops in total to the `target` (excluding @e next_hop and `target` + * itself). Thus 0 still means a distance of 2 hops (to @e next_hop and then + * to `target`). + */ + unsigned int distance; +}; + + +/** + * Entry in our #dv_routes table, representing a (set of) distance + * vector routes to a particular peer. + */ +struct DistanceVector +{ + /** + * To which peer is this a route? + */ + struct GNUNET_PeerIdentity target; + + /** + * Known paths to @e target. + */ + struct DistanceVectorHop *dv_head; + + /** + * Known paths to @e target. + */ + struct DistanceVectorHop *dv_tail; + + /** + * Task scheduled to purge expired paths from @e dv_head MDLL. + */ + struct GNUNET_SCHEDULER_Task *timeout_task; + + /** + * Do we have a confirmed working queue and are thus visible to + * CORE? If so, this is the virtual link, otherwise NULL. + */ + struct VirtualLink *vl; + + /** + * Signature affirming @e ephemeral_key of type + * #GNUNET_SIGNATURE_PURPOSE_TRANSPORT_EPHEMERAL + */ + struct GNUNET_CRYPTO_EddsaSignature sender_sig; + + /** + * How long is @e sender_sig valid + */ + struct GNUNET_TIME_Absolute ephemeral_validity; + + /** + * What time was @e sender_sig created + */ + struct GNUNET_TIME_Absolute monotime; + + /** + * Our ephemeral key. + */ + struct GNUNET_CRYPTO_EcdhePublicKey ephemeral_key; + +}; + + +/** + * Entry identifying transmission in one of our `struct + * Queue` which still awaits an ACK. This is used to + * ensure we do not overwhelm a communicator and limit the number of + * messages outstanding per communicator (say in case communicator is + * CPU bound) and per queue (in case bandwidth allocation exceeds + * what the communicator can actually provide towards a particular + * peer/target). + */ +struct QueueEntry +{ + /** + * Kept as a DLL. + */ + struct QueueEntry *next; + + /** + * Kept as a DLL. + */ + struct QueueEntry *prev; + + /** + * Queue this entry is queued with. + */ + struct Queue *queue; + + /** + * Pending message this entry is for, or NULL for none. + */ + struct PendingMessage *pm; + + /** + * Message ID used for this message with the queue used for transmission. + */ + uint64_t mid; +}; + + +/** + * A queue is a message queue provided by a communicator + * via which we can reach a particular neighbour. + */ +struct Queue +{ + /** + * Kept in a MDLL. + */ + struct Queue *next_neighbour; + + /** + * Kept in a MDLL. + */ + struct Queue *prev_neighbour; + + /** + * Kept in a MDLL. + */ + struct Queue *prev_client; + + /** + * Kept in a MDLL. + */ + struct Queue *next_client; + + /** + * Head of DLL of PAs that used this queue. + */ + struct PendingAcknowledgement *pa_head; + + /** + * Tail of DLL of PAs that used this queue. + */ + struct PendingAcknowledgement *pa_tail; + + /** + * Head of DLL of unacked transmission requests. + */ + struct QueueEntry *queue_head; + + /** + * End of DLL of unacked transmission requests. + */ + struct QueueEntry *queue_tail; + + /** + * Which neighbour is this queue for? + */ + struct Neighbour *neighbour; + + /** + * Which communicator offers this queue? + */ + struct TransportClient *tc; + + /** + * Address served by the queue. + */ + const char *address; + + /** + * Is this queue of unlimited length. + */ + unsigned int unlimited_length; + + /** + * Task scheduled for the time when this queue can (likely) transmit the + * next message. + */ + struct GNUNET_SCHEDULER_Task *transmit_task; + + /** + * How long do *we* consider this @e address to be valid? In the past or + * zero if we have not yet validated it. Can be updated based on + * challenge-response validations (via address validation logic), or when we + * receive ACKs that we can definitively map to transmissions via this + * queue. + */ + struct GNUNET_TIME_Absolute validated_until; + + /** + * Performance data for this queue. + */ + struct PerformanceData pd; + + /** + * Message ID generator for transmissions on this queue to the + * communicator. + */ + uint64_t mid_gen; + + /** + * Unique identifier of this queue with the communicator. + */ + uint32_t qid; + + /** + * Maximum transmission unit supported by this queue. + */ + uint32_t mtu; + + /** + * Messages pending. + */ + uint32_t num_msg_pending; + + /** + * Bytes pending. + */ + uint32_t num_bytes_pending; + + /** + * Length of the DLL starting at @e queue_head. + */ + unsigned int queue_length; + + /** + * Capacity of the queue. + */ + uint64_t q_capacity; + + /** + * Queue priority + */ + uint32_t priority; + + /** + * Network type offered by this queue. + */ + enum GNUNET_NetworkType nt; + + /** + * Connection status for this queue. + */ + enum GNUNET_TRANSPORT_ConnectionStatus cs; + + /** + * Set to #GNUNET_YES if this queue is idle waiting for some + * virtual link to give it a pending message. + */ + int idle; +}; + + +/** + * A neighbour that at least one communicator is connected to. + */ +struct Neighbour +{ + /** + * Which peer is this about? + */ + struct GNUNET_PeerIdentity pid; + + /** + * Head of MDLL of DV hops that have this neighbour as next hop. Must be + * purged if this neighbour goes down. + */ + struct DistanceVectorHop *dv_head; + + /** + * Tail of MDLL of DV hops that have this neighbour as next hop. Must be + * purged if this neighbour goes down. + */ + struct DistanceVectorHop *dv_tail; + + /** + * Head of DLL of queues to this peer. + */ + struct Queue *queue_head; + + /** + * Tail of DLL of queues to this peer. + */ + struct Queue *queue_tail; + + /** + * Handle for an operation to fetch @e last_dv_learn_monotime information from + * the PEERSTORE, or NULL. + */ + struct GNUNET_PEERSTORE_IterateContext *get; + + /** + * Handle to a PEERSTORE store operation to store this @e pid's @e + * @e last_dv_learn_monotime. NULL if no PEERSTORE operation is pending. + */ + struct GNUNET_PEERSTORE_StoreContext *sc; + + /** + * Do we have a confirmed working queue and are thus visible to + * CORE? If so, this is the virtual link, otherwise NULL. + */ + struct VirtualLink *vl; + + /** + * Latest DVLearn monotonic time seen from this peer. Initialized only + * if @e dl_monotime_available is #GNUNET_YES. + */ + struct GNUNET_TIME_Absolute last_dv_learn_monotime; + + /** + * Do we have the latest value for @e last_dv_learn_monotime from + * PEERSTORE yet, or are we still waiting for a reply of PEERSTORE? + */ + int dv_monotime_available; +}; + + +/** + * Another peer attempted to talk to us, we should try to establish + * a connection in the other direction. + */ +struct IncomingRequest +{ + /** + * Kept in a DLL. + */ + struct IncomingRequest *next; + + /** + * Kept in a DLL. + */ + struct IncomingRequest *prev; + + /** + * Notify context for new HELLOs. + */ + struct GNUNET_PEERSTORE_NotifyContext *nc; + + /** + * Which peer is this about? + */ + struct GNUNET_PeerIdentity pid; +}; + + +/** + * A peer that an application (client) would like us to talk to directly. + */ +struct PeerRequest +{ + /** + * Which peer is this about? + */ + struct GNUNET_PeerIdentity pid; + + /** + * Client responsible for the request. + */ + struct TransportClient *tc; + + /** + * Notify context for new HELLOs. + */ + struct GNUNET_PEERSTORE_NotifyContext *nc; + + /** + * What kind of performance preference does this @e tc have? + * + * TODO: use this! + */ + enum GNUNET_MQ_PriorityPreferences pk; + + /** + * How much bandwidth would this @e tc like to see? + */ + struct GNUNET_BANDWIDTH_Value32NBO bw; +}; + + +/** + * Types of different pending messages. + */ +enum PendingMessageType +{ + /** + * Ordinary message received from the CORE service. + */ + PMT_CORE = 0, + + /** + * Fragment box. + */ + PMT_FRAGMENT_BOX = 1, + + /** + * Reliability box. + */ + PMT_RELIABILITY_BOX = 2, + + /** + * Pending message created during #forward_dv_box(). + */ + PMT_DV_BOX = 3 +}; + + +/** + * Transmission request that is awaiting delivery. The original + * transmission requests from CORE may be too big for some queues. + * In this case, a *tree* of fragments is created. At each + * level of the tree, fragments are kept in a DLL ordered by which + * fragment should be sent next (at the head). The tree is searched + * top-down, with the original message at the root. + * + * To select a node for transmission, first it is checked if the + * current node's message fits with the MTU. If it does not, we + * either calculate the next fragment (based on @e frag_off) from the + * current node, or, if all fragments have already been created, + * descend to the @e head_frag. Even though the node was already + * fragmented, the fragment may be too big if the fragment was + * generated for a queue with a larger MTU. In this case, the node + * may be fragmented again, thus creating a tree. + * + * When acknowledgements for fragments are received, the tree + * must be pruned, removing those parts that were already + * acknowledged. When fragments are sent over a reliable + * channel, they can be immediately removed. + * + * If a message is ever fragmented, then the original "full" message + * is never again transmitted (even if it fits below the MTU), and + * only (remaining) fragments are sent. + */ +struct PendingMessage +{ + /** + * Kept in a MDLL of messages for this @a vl. + */ + struct PendingMessage *next_vl; + + /** + * Kept in a MDLL of messages for this @a vl. + */ + struct PendingMessage *prev_vl; + + /** + * Kept in a MDLL of messages from this @a client (if @e pmt is #PMT_CORE) + */ + struct PendingMessage *next_client; + + /** + * Kept in a MDLL of messages from this @a client (if @e pmt is #PMT_CORE) + */ + struct PendingMessage *prev_client; + + /** + * Kept in a MDLL of messages from this @a cpm (if @e pmt is + * #PMT_FRAGMENT_BOx) + */ + struct PendingMessage *next_frag; + + /** + * Kept in a MDLL of messages from this @a cpm (if @e pmt is + * #PMT_FRAGMENT_BOX) + */ + struct PendingMessage *prev_frag; + + /** + * Head of DLL of PAs for this pending message. + */ + struct PendingAcknowledgement *pa_head; + + /** + * Tail of DLL of PAs for this pending message. + */ + struct PendingAcknowledgement *pa_tail; + + /** + * This message, reliability *or* DV-boxed. Only possibly available + * if @e pmt is #PMT_CORE. + */ + struct PendingMessage *bpm; + + /** + * Target of the request (always the ultimate destination!). + * Might be NULL in case of a forwarded DVBox we have no validated neighbour. + */ + struct VirtualLink *vl; + + /** + * In case of a not validated neighbour, we store the target peer. + **/ + struct GNUNET_PeerIdentity target; + + /** + * Set to non-NULL value if this message is currently being given to a + * communicator and we are awaiting that communicator's acknowledgement. + * Note that we must not retransmit a pending message while we're still + * in the process of giving it to a communicator. If a pending message + * is free'd while this entry is non-NULL, the @e qe reference to us + * should simply be set to NULL. + */ + struct QueueEntry *qe; + + /** + * Client that issued the transmission request, if @e pmt is #PMT_CORE. + */ + struct TransportClient *client; + + /** + * Head of a MDLL of fragments created for this core message. + */ + struct PendingMessage *head_frag; + + /** + * Tail of a MDLL of fragments created for this core message. + */ + struct PendingMessage *tail_frag; + + /** + * Our parent in the fragmentation tree. + */ + struct PendingMessage *frag_parent; + + /** + * At what time should we give up on the transmission (and no longer retry)? + */ + struct GNUNET_TIME_Absolute timeout; + + /** + * What is the earliest time for us to retry transmission of this message? + */ + struct GNUNET_TIME_Absolute next_attempt; + + /** + * UUID to use for this message (used for reassembly of fragments, only + * initialized if @e msg_uuid_set is #GNUNET_YES). + */ + struct MessageUUIDP msg_uuid; + + /** + * UUID we use to identify this message in our logs. + * Generated by incrementing the "logging_uuid_gen". + */ + unsigned long long logging_uuid; + + /** + * Type of the pending message. + */ + enum PendingMessageType pmt; + + /** + * Preferences for this message. + * TODO: actually use this! + */ + enum GNUNET_MQ_PriorityPreferences prefs; + + /** + * If pmt is of type PMT_DV_BOX we store the used path here. + */ + struct DistanceVectorHop *used_dvh; + + /** + * Size of the original message. + */ + uint16_t bytes_msg; + + /** + * Offset at which we should generate the next fragment. + */ + uint16_t frag_off; + + /** + * Are we sending fragments at the moment? + */ + unsigned int frags_in_flight; + + /** + * How many fragments do we have? + **/ + uint16_t frag_count; + + /** + * #GNUNET_YES once @e msg_uuid was initialized + */ + int16_t msg_uuid_set; + + /* Followed by @e bytes_msg to transmit */ +}; + + +/** + * Acknowledgement payload. + */ +struct TransportCummulativeAckPayload +{ + /** + * When did we receive the message we are ACKing? Used to calculate + * the delay we introduced by cummulating ACKs. + */ + struct GNUNET_TIME_Absolute receive_time; + + /** + * UUID of a message being acknowledged. + */ + struct AcknowledgementUUIDP ack_uuid; +}; + + +/** + * Data structure in which we track acknowledgements still to + * be sent to the + */ +struct AcknowledgementCummulator +{ + /** + * Target peer for which we are accumulating ACKs here. + */ + struct GNUNET_PeerIdentity target; + + /** + * ACK data being accumulated. Only @e num_acks slots are valid. + */ + struct TransportCummulativeAckPayload ack_uuids[MAX_CUMMULATIVE_ACKS]; + + /** + * Task scheduled either to transmit the cumulative ACK message, + * or to clean up this data structure after extended periods of + * inactivity (if @e num_acks is zero). + */ + struct GNUNET_SCHEDULER_Task *task; + + /** + * When is @e task run (only used if @e num_acks is non-zero)? + */ + struct GNUNET_TIME_Absolute min_transmission_time; + + /** + * Counter to produce the `ack_counter` in the `struct + * TransportReliabilityAckMessage`. Allows the receiver to detect + * lost ACK messages. Incremented by @e num_acks upon transmission. + */ + uint32_t ack_counter; + + /** + * Number of entries used in @e ack_uuids. Reset to 0 upon transmission. + */ + unsigned int num_acks; +}; + + +/** + * One of the addresses of this peer. + */ +struct AddressListEntry +{ + /** + * Kept in a DLL. + */ + struct AddressListEntry *next; + + /** + * Kept in a DLL. + */ + struct AddressListEntry *prev; + + /** + * Which communicator provides this address? + */ + struct TransportClient *tc; + + /** + * The actual address. + */ + const char *address; + + /** + * Current context for storing this address in the peerstore. + */ + struct GNUNET_PEERSTORE_StoreContext *sc; + + /** + * Task to periodically do @e st operation. + */ + struct GNUNET_SCHEDULER_Task *st; + + /** + * What is a typical lifetime the communicator expects this + * address to have? (Always from now.) + */ + struct GNUNET_TIME_Relative expiration; + + /** + * Address identifier used by the communicator. + */ + uint32_t aid; + + /** + * Network type offered by this address. + */ + enum GNUNET_NetworkType nt; +}; + + +/** + * Client connected to the transport service. + */ +struct TransportClient +{ + /** + * Kept in a DLL. + */ + struct TransportClient *next; + + /** + * Kept in a DLL. + */ + struct TransportClient *prev; + + /** + * Handle to the client. + */ + struct GNUNET_SERVICE_Client *client; + + /** + * Message queue to the client. + */ + struct GNUNET_MQ_Handle *mq; + + /** + * What type of client is this? + */ + enum ClientType type; + + union + { + /** + * Information for @e type #CT_CORE. + */ + struct + { + /** + * Head of list of messages pending for this client, sorted by + * transmission time ("next_attempt" + possibly internal prioritization). + */ + struct PendingMessage *pending_msg_head; + + /** + * Tail of list of messages pending for this client. + */ + struct PendingMessage *pending_msg_tail; + } core; + + /** + * Information for @e type #CT_MONITOR. + */ + struct + { + /** + * Peer identity to monitor the addresses of. + * Zero to monitor all neighbours. Valid if + * @e type is #CT_MONITOR. + */ + struct GNUNET_PeerIdentity peer; + + /** + * Is this a one-shot monitor? + */ + int one_shot; + } monitor; + + + /** + * Information for @e type #CT_COMMUNICATOR. + */ + struct + { + /** + * If @e type is #CT_COMMUNICATOR, this communicator + * supports communicating using these addresses. + */ + char *address_prefix; + + /** + * Head of DLL of queues offered by this communicator. + */ + struct Queue *queue_head; + + /** + * Tail of DLL of queues offered by this communicator. + */ + struct Queue *queue_tail; + + /** + * Head of list of the addresses of this peer offered by this + * communicator. + */ + struct AddressListEntry *addr_head; + + /** + * Tail of list of the addresses of this peer offered by this + * communicator. + */ + struct AddressListEntry *addr_tail; + + /** + * Number of queue entries in all queues to this communicator. Used + * throttle sending to a communicator if we see that the communicator + * is globally unable to keep up. + */ + unsigned int total_queue_length; + + /** + * Characteristics of this communicator. + */ + enum GNUNET_TRANSPORT_CommunicatorCharacteristics cc; + } communicator; + + /** + * Information for @e type #CT_APPLICATION + */ + struct + { + /** + * Map of requests for peers the given client application would like to + * see connections for. Maps from PIDs to `struct PeerRequest`. + */ + struct GNUNET_CONTAINER_MultiPeerMap *requests; + } application; + } details; +}; + + +/** + * State we keep for validation activities. Each of these + * is both in the #validation_heap and the #validation_map. + */ +struct ValidationState +{ + /** + * For which peer is @a address to be validated (or possibly valid)? + * Serves as key in the #validation_map. + */ + struct GNUNET_PeerIdentity pid; + + /** + * How long did the peer claim this @e address to be valid? Capped at + * minimum of #MAX_ADDRESS_VALID_UNTIL relative to the time where we last + * were told about the address and the value claimed by the other peer at + * that time. May be updated similarly when validation succeeds. + */ + struct GNUNET_TIME_Absolute valid_until; + + /** + * How long do *we* consider this @e address to be valid? + * In the past or zero if we have not yet validated it. + */ + struct GNUNET_TIME_Absolute validated_until; + + /** + * When did we FIRST use the current @e challenge in a message? + * Used to sanity-check @code{origin_time} in the response when + * calculating the RTT. If the @code{origin_time} is not in + * the expected range, the response is discarded as malicious. + */ + struct GNUNET_TIME_Absolute first_challenge_use; + + /** + * When did we LAST use the current @e challenge in a message? + * Used to sanity-check @code{origin_time} in the response when + * calculating the RTT. If the @code{origin_time} is not in + * the expected range, the response is discarded as malicious. + */ + struct GNUNET_TIME_Absolute last_challenge_use; + + /** + * Next time we will send the @e challenge to the peer, if this time is past + * @e valid_until, this validation state is released at this time. If the + * address is valid, @e next_challenge is set to @e validated_until MINUS @e + * validation_delay * #VALIDATION_RTT_BUFFER_FACTOR, such that we will try + * to re-validate before the validity actually expires. + */ + struct GNUNET_TIME_Absolute next_challenge; + + /** + * Current backoff factor we're applying for sending the @a challenge. + * Reset to 0 if the @a challenge is confirmed upon validation. + * Reduced to minimum of #FAST_VALIDATION_CHALLENGE_FREQ and half of the + * existing value if we receive an unvalidated address again over + * another channel (and thus should consider the information "fresh"). + * Maximum is #MAX_VALIDATION_CHALLENGE_FREQ. + */ + struct GNUNET_TIME_Relative challenge_backoff; + + /** + * Initially set to "forever". Once @e validated_until is set, this value is + * set to the RTT that tells us how long it took to receive the validation. + */ + struct GNUNET_TIME_Relative validation_rtt; + + /** + * The challenge we sent to the peer to get it to validate the address. Note + * that we rotate the challenge whenever we update @e validated_until to + * avoid attacks where a peer simply replays an old challenge in the future. + * (We must not rotate more often as otherwise we may discard valid answers + * due to packet losses, latency and reorderings on the network). + */ + struct GNUNET_CRYPTO_ChallengeNonceP challenge; + + /** + * Claimed address of the peer. + */ + char *address; + + /** + * Entry in the #validation_heap, which is sorted by @e next_challenge. The + * heap is used to figure out when the next validation activity should be + * run. + */ + struct GNUNET_CONTAINER_HeapNode *hn; + + /** + * Handle to a PEERSTORE store operation for this @e address. NULL if + * no PEERSTORE operation is pending. + */ + struct GNUNET_PEERSTORE_StoreContext *sc; + + /** + * Self-imposed limit on the previous flow control window. (May be zero, + * if we never used data from the previous window or are establishing the + * connection for the first time). + */ + uint32_t last_window_consum_limit; + + /** + * We are technically ready to send the challenge, but we are waiting for + * the respective queue to become available for transmission. + */ + int awaiting_queue; +}; + + +/** + * A Backtalker is a peer sending us backchannel messages. We use this + * struct to detect monotonic time violations, cache ephemeral key + * material (to avoid repeatedly checking signatures), and to synchronize + * monotonic time with the PEERSTORE. + */ +struct Backtalker +{ + /** + * Peer this is about. + */ + struct GNUNET_PeerIdentity pid; + + /** + * Last (valid) monotonic time received from this sender. + */ + struct GNUNET_TIME_Absolute monotonic_time; + + /** + * When will this entry time out? + */ + struct GNUNET_TIME_Absolute timeout; + + /** + * Last (valid) ephemeral key received from this sender. + */ + struct GNUNET_CRYPTO_EcdhePublicKey last_ephemeral; + + /** + * Task associated with this backtalker. Can be for timeout, + * or other asynchronous operations. + */ + struct GNUNET_SCHEDULER_Task *task; + + /** + * Communicator context waiting on this backchannel's @e get, or NULL. + */ + struct CommunicatorMessageContext *cmc; + + /** + * Handle for an operation to fetch @e monotonic_time information from the + * PEERSTORE, or NULL. + */ + struct GNUNET_PEERSTORE_IterateContext *get; + + /** + * Handle to a PEERSTORE store operation for this @e pid's @e + * monotonic_time. NULL if no PEERSTORE operation is pending. + */ + struct GNUNET_PEERSTORE_StoreContext *sc; + + /** + * Number of bytes of the original message body that follows after this + * struct. + */ + size_t body_size; +}; + + +/** + * Ring buffer for a CORE message we did not deliver to CORE, because of missing virtual link to sender. + */ +static struct RingBufferEntry *ring_buffer[RING_BUFFER_SIZE]; + +/** + * Head of the ring buffer. + */ +static unsigned int ring_buffer_head; + +/** + * Is the ring buffer filled up to RING_BUFFER_SIZE. + */ +static unsigned int is_ring_buffer_full; + +/** + * Ring buffer for a forwarded DVBox message we did not deliver to the next hop, because of missing virtual link that hop. + */ +static struct PendingMessage *ring_buffer_dv[RING_BUFFER_SIZE]; + +/** + * Head of the ring buffer. + */ +static unsigned int ring_buffer_dv_head; + +/** + * Is the ring buffer filled up to RING_BUFFER_SIZE. + */ +static unsigned int is_ring_buffer_dv_full; + +/** + * Head of linked list of all clients to this service. + */ +static struct TransportClient *clients_head; + +/** + * Tail of linked list of all clients to this service. + */ +static struct TransportClient *clients_tail; + +/** + * Statistics handle. + */ +static struct GNUNET_STATISTICS_Handle *GST_stats; + +/** + * Configuration handle. + */ +static const struct GNUNET_CONFIGURATION_Handle *GST_cfg; + +/** + * Our public key. + */ +static struct GNUNET_PeerIdentity GST_my_identity; + +/** + * Our HELLO + */ +struct GNUNET_HELLO_Builder *GST_my_hello; + +/** + * Our private key. + */ +static struct GNUNET_CRYPTO_EddsaPrivateKey *GST_my_private_key; + +/** + * Map from PIDs to `struct Neighbour` entries. A peer is + * a neighbour if we have an MQ to it from some communicator. + */ +static struct GNUNET_CONTAINER_MultiPeerMap *neighbours; + +/** + * Map from PIDs to `struct Backtalker` entries. A peer is + * a backtalker if it recently send us backchannel messages. + */ +static struct GNUNET_CONTAINER_MultiPeerMap *backtalkers; + +/** + * Map from PIDs to `struct AcknowledgementCummulator`s. + * Here we track the cumulative ACKs for transmission. + */ +static struct GNUNET_CONTAINER_MultiPeerMap *ack_cummulators; + +/** + * Map of pending acknowledgements, mapping `struct AcknowledgementUUID` to + * a `struct PendingAcknowledgement`. + */ +static struct GNUNET_CONTAINER_MultiUuidmap *pending_acks; + +/** + * Map from PIDs to `struct DistanceVector` entries describing + * known paths to the peer. + */ +static struct GNUNET_CONTAINER_MultiPeerMap *dv_routes; + +/** + * Map from PIDs to `struct ValidationState` entries describing + * addresses we are aware of and their validity state. + */ +static struct GNUNET_CONTAINER_MultiPeerMap *validation_map; + +/** + * Map from PIDs to `struct VirtualLink` entries describing + * links CORE knows to exist. + */ +static struct GNUNET_CONTAINER_MultiPeerMap *links; + +/** + * Map from challenges to `struct LearnLaunchEntry` values. + */ +static struct GNUNET_CONTAINER_MultiShortmap *dvlearn_map; + +/** + * Head of a DLL sorted by launch time. + */ +static struct LearnLaunchEntry *lle_head = NULL; + +/** + * Tail of a DLL sorted by launch time. + */ +static struct LearnLaunchEntry *lle_tail = NULL; + +/** + * MIN Heap sorted by "next_challenge" to `struct ValidationState` entries + * sorting addresses we are aware of by when we should next try to (re)validate + * (or expire) them. + */ +static struct GNUNET_CONTAINER_Heap *validation_heap; + +/** + * Database for peer's HELLOs. + */ +static struct GNUNET_PEERSTORE_Handle *peerstore; + +/** + * Task run to initiate DV learning. + */ +static struct GNUNET_SCHEDULER_Task *dvlearn_task; + +/** + * Task to run address validation. + */ +static struct GNUNET_SCHEDULER_Task *validation_task; + +/** + * List of incoming connections where we are trying + * to get a connection back established. Length + * kept in #ir_total. + */ +static struct IncomingRequest *ir_head; + +/** + * Tail of DLL starting at #ir_head. + */ +static struct IncomingRequest *ir_tail; + +/** + * Length of the DLL starting at #ir_head. + */ +static unsigned int ir_total; + +/** + * Generator of `logging_uuid` in `struct PendingMessage`. + */ +static unsigned long long logging_uuid_gen; + +/** + * Monotonic time we use for HELLOs generated at this time. TODO: we + * should increase this value from time to time (i.e. whenever a + * `struct AddressListEntry` actually expires), but IF we do this, we + * must also update *all* (remaining) addresses in the PEERSTORE at + * that time! (So for now only increased when the peer is restarted, + * which hopefully roughly matches whenever our addresses change.) + */ +static struct GNUNET_TIME_Absolute hello_mono_time; + +/** + * Indication if we have received a shutdown signal + * and are in the process of cleaning up. + */ +static int in_shutdown; + +/** + * Get an offset into the transmission history buffer for `struct + * PerformanceData`. Note that the caller must perform the required + * modulo #GOODPUT_AGING_SLOTS operation before indexing into the + * array! + * + * An 'age' lasts 15 minute slots. + * + * @return current age of the world + */ +static unsigned int +get_age () +{ + struct GNUNET_TIME_Absolute now; + + now = GNUNET_TIME_absolute_get (); + return now.abs_value_us / GNUNET_TIME_UNIT_MINUTES.rel_value_us / 15; +} + + +/** + * Release @a ir data structure. + * + * @param ir data structure to release + */ +static void +free_incoming_request (struct IncomingRequest *ir) +{ + GNUNET_CONTAINER_DLL_remove (ir_head, ir_tail, ir); + GNUNET_assert (ir_total > 0); + ir_total--; + GNUNET_PEERSTORE_hello_changed_notify_cancel (ir->nc); + ir->nc = NULL; + GNUNET_free (ir); +} + + +/** + * Release @a pa data structure. + * + * @param pa data structure to release + */ +static void +free_pending_acknowledgement (struct PendingAcknowledgement *pa) +{ + struct Queue *q = pa->queue; + struct PendingMessage *pm = pa->pm; + struct DistanceVectorHop *dvh = pa->dvh; + + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "free_pending_acknowledgement\n"); + if (NULL != q) + { + GNUNET_CONTAINER_MDLL_remove (queue, q->pa_head, q->pa_tail, pa); + pa->queue = NULL; + } + if (NULL != pm) + { + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "remove pa from message\n"); + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "remove pa from message %llu\n", + pm->logging_uuid); + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "remove pa from message %u\n", + pm->pmt); + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "remove pa from message %s\n", + GNUNET_uuid2s (&pa->ack_uuid.value)); + GNUNET_CONTAINER_MDLL_remove (pm, pm->pa_head, pm->pa_tail, pa); + pa->pm = NULL; + } + if (NULL != dvh) + { + GNUNET_CONTAINER_MDLL_remove (dvh, dvh->pa_head, dvh->pa_tail, pa); + pa->queue = NULL; + } + GNUNET_assert (GNUNET_YES == + GNUNET_CONTAINER_multiuuidmap_remove (pending_acks, + &pa->ack_uuid.value, + pa)); + GNUNET_free (pa); +} + + +/** + * Free fragment tree below @e root, excluding @e root itself. + * FIXME: this does NOT seem to have the intended semantics + * based on how this is called. Seems we generally DO expect + * @a root to be free'ed itself as well! + * + * @param root root of the tree to free + */ +static void +free_fragment_tree (struct PendingMessage *root) +{ + struct PendingMessage *frag; + + while (NULL != (frag = root->head_frag)) + { + struct PendingAcknowledgement *pa; + + free_fragment_tree (frag); + while (NULL != (pa = frag->pa_head)) + { + GNUNET_CONTAINER_MDLL_remove (pm, frag->pa_head, frag->pa_tail, pa); + pa->pm = NULL; + } + GNUNET_CONTAINER_MDLL_remove (frag, root->head_frag, root->tail_frag, frag); + if (NULL != frag->qe) + { + GNUNET_assert (frag == frag->qe->pm); + frag->qe->pm = NULL; + } + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Free frag %p\n", + frag); + GNUNET_free (frag); + } +} + + +/** + * Release memory associated with @a pm and remove @a pm from associated + * data structures. @a pm must be a top-level pending message and not + * a fragment in the tree. The entire tree is freed (if applicable). + * + * @param pm the pending message to free + */ +static void +free_pending_message (struct PendingMessage *pm) +{ + struct TransportClient *tc = pm->client; + struct VirtualLink *vl = pm->vl; + struct PendingAcknowledgement *pa; + + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Freeing pm %p\n", + pm); + if (NULL != tc) + { + GNUNET_CONTAINER_MDLL_remove (client, + tc->details.core.pending_msg_head, + tc->details.core.pending_msg_tail, + pm); + } + if ((NULL != vl) && (NULL == pm->frag_parent)) + { + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Removing pm %llu\n", + pm->logging_uuid); + GNUNET_CONTAINER_MDLL_remove (vl, + vl->pending_msg_head, + vl->pending_msg_tail, + pm); + } + while (NULL != (pa = pm->pa_head)) + { + if (NULL == pa) + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "free pending pa null\n"); + if (NULL == pm->pa_tail) + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "free pending pa_tail null\n"); + if (NULL == pa->prev_pa) + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "free pending pa prev null\n"); + if (NULL == pa->next_pa) + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "free pending pa next null\n"); + GNUNET_CONTAINER_MDLL_remove (pm, pm->pa_head, pm->pa_tail, pa); + pa->pm = NULL; + } + + free_fragment_tree (pm); + if (NULL != pm->qe) + { + GNUNET_assert (pm == pm->qe->pm); + pm->qe->pm = NULL; + } + if (NULL != pm->bpm) + { + free_fragment_tree (pm->bpm); + if (NULL != pm->bpm->qe) + { + struct QueueEntry *qe = pm->bpm->qe; + qe->pm = NULL; + } + GNUNET_free (pm->bpm); + } + + GNUNET_free (pm); + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Freeing pm done\n"); +} + + +/** + * Free @a rc + * + * @param rc data structure to free + */ +static void +free_reassembly_context (struct ReassemblyContext *rc) +{ + struct VirtualLink *vl = rc->virtual_link; + + GNUNET_assert (rc == GNUNET_CONTAINER_heap_remove_node (rc->hn)); + GNUNET_assert (GNUNET_OK == + GNUNET_CONTAINER_multihashmap32_remove (vl->reassembly_map, + rc->msg_uuid.uuid, + rc)); + GNUNET_free (rc); +} + + +/** + * Task run to clean up reassembly context of a neighbour that have expired. + * + * @param cls a `struct Neighbour` + */ +static void +reassembly_cleanup_task (void *cls) +{ + struct VirtualLink *vl = cls; + struct ReassemblyContext *rc; + + vl->reassembly_timeout_task = NULL; + while (NULL != (rc = GNUNET_CONTAINER_heap_peek (vl->reassembly_heap))) + { + if (0 == GNUNET_TIME_absolute_get_remaining (rc->reassembly_timeout) + .rel_value_us) + { + free_reassembly_context (rc); + continue; + } + GNUNET_assert (NULL == vl->reassembly_timeout_task); + vl->reassembly_timeout_task = + GNUNET_SCHEDULER_add_at (rc->reassembly_timeout, + &reassembly_cleanup_task, + vl); + return; + } +} + + +/** + * function called to #free_reassembly_context(). + * + * @param cls NULL + * @param key unused + * @param value a `struct ReassemblyContext` to free + * @return #GNUNET_OK (continue iteration) + */ +static int +free_reassembly_cb (void *cls, uint32_t key, void *value) +{ + struct ReassemblyContext *rc = value; + + (void) cls; + (void) key; + free_reassembly_context (rc); + return GNUNET_OK; +} + + +/** + * Free virtual link. + * + * @param vl link data to free + */ +static void +free_virtual_link (struct VirtualLink *vl) +{ + struct PendingMessage *pm; + struct CoreSentContext *csc; + + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "free virtual link %p\n", + vl); + + if (NULL != vl->reassembly_map) + { + GNUNET_CONTAINER_multihashmap32_iterate (vl->reassembly_map, + &free_reassembly_cb, + NULL); + GNUNET_CONTAINER_multihashmap32_destroy (vl->reassembly_map); + vl->reassembly_map = NULL; + GNUNET_CONTAINER_heap_destroy (vl->reassembly_heap); + vl->reassembly_heap = NULL; + } + if (NULL != vl->reassembly_timeout_task) + { + GNUNET_SCHEDULER_cancel (vl->reassembly_timeout_task); + vl->reassembly_timeout_task = NULL; + } + while (NULL != (pm = vl->pending_msg_head)) + free_pending_message (pm); + GNUNET_assert (GNUNET_YES == + GNUNET_CONTAINER_multipeermap_remove (links, &vl->target, vl)); + if (NULL != vl->visibility_task) + { + GNUNET_SCHEDULER_cancel (vl->visibility_task); + vl->visibility_task = NULL; + } + if (NULL != vl->fc_retransmit_task) + { + GNUNET_SCHEDULER_cancel (vl->fc_retransmit_task); + vl->fc_retransmit_task = NULL; + } + while (NULL != (csc = vl->csc_head)) + { + GNUNET_CONTAINER_DLL_remove (vl->csc_head, vl->csc_tail, csc); + GNUNET_assert (vl == csc->vl); + csc->vl = NULL; + } + GNUNET_break (NULL == vl->n); + GNUNET_break (NULL == vl->dv); + GNUNET_free (vl); +} + + +/** + * Free validation state. + * + * @param vs validation state to free + */ +static void +free_validation_state (struct ValidationState *vs) +{ + GNUNET_assert ( + GNUNET_YES == + GNUNET_CONTAINER_multipeermap_remove (validation_map, &vs->pid, vs)); + GNUNET_CONTAINER_heap_remove_node (vs->hn); + vs->hn = NULL; + if (NULL != vs->sc) + { + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "store cancel\n"); + GNUNET_PEERSTORE_store_cancel (vs->sc); + vs->sc = NULL; + } + GNUNET_free (vs->address); + GNUNET_free (vs); +} + + +/** + * Lookup neighbour for peer @a pid. + * + * @param pid neighbour to look for + * @return NULL if we do not have this peer as a neighbour + */ +static struct Neighbour * +lookup_neighbour (const struct GNUNET_PeerIdentity *pid) +{ + return GNUNET_CONTAINER_multipeermap_get (neighbours, pid); +} + + +/** + * Lookup virtual link for peer @a pid. + * + * @param pid virtual link to look for + * @return NULL if we do not have this peer as a virtual link + */ +static struct VirtualLink * +lookup_virtual_link (const struct GNUNET_PeerIdentity *pid) +{ + return GNUNET_CONTAINER_multipeermap_get (links, pid); +} + + +/** + * Details about what to notify monitors about. + */ +struct MonitorEvent +{ + /** + * @deprecated To be discussed if we keep these... + */ + struct GNUNET_TIME_Absolute last_validation; + struct GNUNET_TIME_Absolute valid_until; + struct GNUNET_TIME_Absolute next_validation; + + /** + * Current round-trip time estimate. + */ + struct GNUNET_TIME_Relative rtt; + + /** + * Connection status. + */ + enum GNUNET_TRANSPORT_ConnectionStatus cs; + + /** + * Messages pending. + */ + uint32_t num_msg_pending; + + /** + * Bytes pending. + */ + uint32_t num_bytes_pending; +}; + + +/** + * Free a @a dvh. Callers MAY want to check if this was the last path to the + * `target`, and if so call #free_dv_route to also free the associated DV + * entry in #dv_routes (if not, the associated scheduler job should eventually + * take care of it). + * + * @param dvh hop to free + */ +static void +free_distance_vector_hop (struct DistanceVectorHop *dvh) +{ + struct Neighbour *n = dvh->next_hop; + struct DistanceVector *dv = dvh->dv; + struct PendingAcknowledgement *pa; + + while (NULL != (pa = dvh->pa_head)) + { + GNUNET_CONTAINER_MDLL_remove (dvh, dvh->pa_head, dvh->pa_tail, pa); + pa->dvh = NULL; + } + GNUNET_CONTAINER_MDLL_remove (neighbour, n->dv_head, n->dv_tail, dvh); + GNUNET_CONTAINER_MDLL_remove (dv, dv->dv_head, dv->dv_tail, dvh); + GNUNET_free (dvh); +} + + +/** + * Task run to check whether the hops of the @a cls still + * are validated, or if we need to core about disconnection. + * + * @param cls a `struct VirtualLink` + */ +static void +check_link_down (void *cls); + + +/** + * Send message to CORE clients that we lost a connection. + * + * @param pid peer the connection was for + */ +static void +cores_send_disconnect_info (const struct GNUNET_PeerIdentity *pid) +{ + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Informing CORE clients about disconnect from %s\n", + GNUNET_i2s (pid)); + for (struct TransportClient *tc = clients_head; NULL != tc; tc = tc->next) + { + struct GNUNET_MQ_Envelope *env; + struct DisconnectInfoMessage *dim; + + if (CT_CORE != tc->type) + continue; + env = GNUNET_MQ_msg (dim, GNUNET_MESSAGE_TYPE_TRANSPORT_DISCONNECT); + dim->peer = *pid; + GNUNET_MQ_send (tc->mq, env); + } +} + + +/** + * Free entry in #dv_routes. First frees all hops to the target, and + * if there are no entries left, frees @a dv as well. + * + * @param dv route to free + */ +static void +free_dv_route (struct DistanceVector *dv) +{ + struct DistanceVectorHop *dvh; + + while (NULL != (dvh = dv->dv_head)) + free_distance_vector_hop (dvh); + if (NULL == dv->dv_head) + { + struct VirtualLink *vl; + + GNUNET_assert ( + GNUNET_YES == + GNUNET_CONTAINER_multipeermap_remove (dv_routes, &dv->target, dv)); + if (NULL != (vl = dv->vl)) + { + GNUNET_assert (dv == vl->dv); + vl->dv = NULL; + if (NULL == vl->n) + { + cores_send_disconnect_info (&dv->target); + free_virtual_link (vl); + } + else + { + GNUNET_SCHEDULER_cancel (vl->visibility_task); + vl->visibility_task = GNUNET_SCHEDULER_add_now (&check_link_down, vl); + } + dv->vl = NULL; + } + + if (NULL != dv->timeout_task) + { + GNUNET_SCHEDULER_cancel (dv->timeout_task); + dv->timeout_task = NULL; + } + GNUNET_free (dv); + } +} + + +/** + * Notify monitor @a tc about an event. That @a tc + * cares about the event has already been checked. + * + * Send @a tc information in @a me about a @a peer's status with + * respect to some @a address to all monitors that care. + * + * @param tc monitor to inform + * @param peer peer the information is about + * @param address address the information is about + * @param nt network type associated with @a address + * @param me detailed information to transmit + */ +static void +notify_monitor (struct TransportClient *tc, + const struct GNUNET_PeerIdentity *peer, + const char *address, + enum GNUNET_NetworkType nt, + const struct MonitorEvent *me) +{ + struct GNUNET_MQ_Envelope *env; + struct GNUNET_TRANSPORT_MonitorData *md; + size_t addr_len = strlen (address) + 1; + + env = GNUNET_MQ_msg_extra (md, + addr_len, + GNUNET_MESSAGE_TYPE_TRANSPORT_MONITOR_DATA); + md->nt = htonl ((uint32_t) nt); + md->peer = *peer; + md->last_validation = GNUNET_TIME_absolute_hton (me->last_validation); + md->valid_until = GNUNET_TIME_absolute_hton (me->valid_until); + md->next_validation = GNUNET_TIME_absolute_hton (me->next_validation); + md->rtt = GNUNET_TIME_relative_hton (me->rtt); + md->cs = htonl ((uint32_t) me->cs); + md->num_msg_pending = htonl (me->num_msg_pending); + md->num_bytes_pending = htonl (me->num_bytes_pending); + memcpy (&md[1], address, addr_len); + GNUNET_MQ_send (tc->mq, env); +} + + +/** + * Send information in @a me about a @a peer's status with respect + * to some @a address to all monitors that care. + * + * @param peer peer the information is about + * @param address address the information is about + * @param nt network type associated with @a address + * @param me detailed information to transmit + */ +static void +notify_monitors (const struct GNUNET_PeerIdentity *peer, + const char *address, + enum GNUNET_NetworkType nt, + const struct MonitorEvent *me) +{ + for (struct TransportClient *tc = clients_head; NULL != tc; tc = tc->next) + { + if (CT_MONITOR != tc->type) + continue; + if (tc->details.monitor.one_shot) + continue; + if ((GNUNET_NO == GNUNET_is_zero (&tc->details.monitor.peer)) && + (0 != GNUNET_memcmp (&tc->details.monitor.peer, peer))) + continue; + notify_monitor (tc, peer, address, nt, me); + } +} + + +/** + * Called whenever a client connects. Allocates our + * data structures associated with that client. + * + * @param cls closure, NULL + * @param client identification of the client + * @param mq message queue for the client + * @return our `struct TransportClient` + */ +static void * +client_connect_cb (void *cls, + struct GNUNET_SERVICE_Client *client, + struct GNUNET_MQ_Handle *mq) +{ + struct TransportClient *tc; + + (void) cls; + tc = GNUNET_new (struct TransportClient); + tc->client = client; + tc->mq = mq; + GNUNET_CONTAINER_DLL_insert (clients_head, clients_tail, tc); + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Client %p of type %u connected\n", + tc, + tc->type); + return tc; +} + + +/** + * Release memory used by @a neighbour. + * + * @param neighbour neighbour entry to free + */ +static void +free_neighbour (struct Neighbour *neighbour) +{ + struct DistanceVectorHop *dvh; + struct VirtualLink *vl; + + GNUNET_assert (NULL == neighbour->queue_head); + GNUNET_assert (GNUNET_YES == + GNUNET_CONTAINER_multipeermap_remove (neighbours, + &neighbour->pid, + neighbour)); + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Freeing neighbour\n"); + while (NULL != (dvh = neighbour->dv_head)) + { + struct DistanceVector *dv = dvh->dv; + + free_distance_vector_hop (dvh); + if (NULL == dv->dv_head) + free_dv_route (dv); + } + if (NULL != neighbour->get) + { + GNUNET_PEERSTORE_iterate_cancel (neighbour->get); + neighbour->get = NULL; + } + if (NULL != neighbour->sc) + { + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "store cancel\n"); + GNUNET_PEERSTORE_store_cancel (neighbour->sc); + neighbour->sc = NULL; + } + if (NULL != (vl = neighbour->vl)) + { + GNUNET_assert (neighbour == vl->n); + vl->n = NULL; + if (NULL == vl->dv) + { + cores_send_disconnect_info (&vl->target); + free_virtual_link (vl); + } + else + { + GNUNET_SCHEDULER_cancel (vl->visibility_task); + vl->visibility_task = GNUNET_SCHEDULER_add_now (&check_link_down, vl); + } + neighbour->vl = NULL; + } + GNUNET_free (neighbour); +} + + +/** + * Send message to CORE clients that we lost a connection. + * + * @param tc client to inform (must be CORE client) + * @param pid peer the connection is for + */ +static void +core_send_connect_info (struct TransportClient *tc, + const struct GNUNET_PeerIdentity *pid) +{ + struct GNUNET_MQ_Envelope *env; + struct ConnectInfoMessage *cim; + + GNUNET_assert (CT_CORE == tc->type); + env = GNUNET_MQ_msg (cim, GNUNET_MESSAGE_TYPE_TRANSPORT_CONNECT); + cim->id = *pid; + GNUNET_MQ_send (tc->mq, env); +} + + +/** + * Send message to CORE clients that we gained a connection + * + * @param pid peer the queue was for + */ +static void +cores_send_connect_info (const struct GNUNET_PeerIdentity *pid) +{ + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Informing CORE clients about connection to %s\n", + GNUNET_i2s (pid)); + for (struct TransportClient *tc = clients_head; NULL != tc; tc = tc->next) + { + if (CT_CORE != tc->type) + continue; + core_send_connect_info (tc, pid); + } +} + + +/** + * We believe we are ready to transmit a message on a queue. Gives the + * message to the communicator for transmission (updating the tracker, + * and re-scheduling itself if applicable). + * + * @param cls the `struct Queue` to process transmissions for + */ +static void +transmit_on_queue (void *cls); + + +/** + * Check if the communicator has another queue with higher prio ready for sending. + */ +static unsigned int +check_for_queue_with_higher_prio (struct Queue *queue, struct Queue *queue_head) +{ + for (struct Queue *s = queue_head; NULL != s; + s = s->next_client) + { + if (s->tc->details.communicator.address_prefix != + queue->tc->details.communicator.address_prefix) + { + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "queue address %s qid %u compare with queue: address %s qid %u\n", + queue->address, + queue->qid, + s->address, + s->qid); + if ((s->priority > queue->priority) && (0 < s->q_capacity) && + (QUEUE_LENGTH_LIMIT > s->queue_length) ) + return GNUNET_YES; + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Lower prio\n"); + } + } + return GNUNET_NO; +} + + +/** + * Called whenever something changed that might effect when we + * try to do the next transmission on @a queue using #transmit_on_queue(). + * + * @param queue the queue to do scheduling for + * @param p task priority to use, if @a queue is scheduled + */ +static void +schedule_transmit_on_queue (struct GNUNET_TIME_Relative delay, + struct Queue *queue, + enum GNUNET_SCHEDULER_Priority p) +{ + if (check_for_queue_with_higher_prio (queue, + queue->tc->details.communicator. + queue_head)) + return; + + if (queue->tc->details.communicator.total_queue_length >= + COMMUNICATOR_TOTAL_QUEUE_LIMIT) + { + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Transmission throttled due to communicator queue limit\n"); + GNUNET_STATISTICS_update ( + GST_stats, + "# Transmission throttled due to communicator queue limit", + 1, + GNUNET_NO); + queue->idle = GNUNET_NO; + return; + } + if (queue->queue_length >= QUEUE_LENGTH_LIMIT) + { + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Transmission throttled due to communicator queue length limit\n"); + GNUNET_STATISTICS_update (GST_stats, + "# Transmission throttled due to queue queue limit", + 1, + GNUNET_NO); + queue->idle = GNUNET_NO; + return; + } + if (0 == queue->q_capacity) + { + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Transmission throttled due to communicator message queue qid %u has capacity %" + PRIu64 ".\n", + queue->qid, + queue->q_capacity); + GNUNET_STATISTICS_update (GST_stats, + "# Transmission throttled due to message queue capacity", + 1, + GNUNET_NO); + queue->idle = GNUNET_NO; + return; + } + /* queue might indeed be ready, schedule it */ + if (NULL != queue->transmit_task) + GNUNET_SCHEDULER_cancel (queue->transmit_task); + queue->transmit_task = + GNUNET_SCHEDULER_add_delayed_with_priority (delay, p, &transmit_on_queue, + queue); + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Considering transmission on queue `%s' QID %llu to %s\n", + queue->address, + (unsigned long long) queue->qid, + GNUNET_i2s (&queue->neighbour->pid)); +} + + +/** + * Task run to check whether the hops of the @a cls still + * are validated, or if we need to core about disconnection. + * + * @param cls a `struct VirtualLink` + */ +static void +check_link_down (void *cls) +{ + struct VirtualLink *vl = cls; + struct DistanceVector *dv = vl->dv; + struct Neighbour *n = vl->n; + struct GNUNET_TIME_Absolute dvh_timeout; + struct GNUNET_TIME_Absolute q_timeout; + + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Checking if link is down\n"); + vl->visibility_task = NULL; + dvh_timeout = GNUNET_TIME_UNIT_ZERO_ABS; + if (NULL != dv) + { + for (struct DistanceVectorHop *pos = dv->dv_head; NULL != pos; + pos = pos->next_dv) + dvh_timeout = GNUNET_TIME_absolute_max (dvh_timeout, + pos->path_valid_until); + if (0 == GNUNET_TIME_absolute_get_remaining (dvh_timeout).rel_value_us) + { + vl->dv->vl = NULL; + vl->dv = NULL; + } + } + q_timeout = GNUNET_TIME_UNIT_ZERO_ABS; + for (struct Queue *q = n->queue_head; NULL != q; q = q->next_neighbour) + q_timeout = GNUNET_TIME_absolute_max (q_timeout, q->validated_until); + if (0 == GNUNET_TIME_absolute_get_remaining (q_timeout).rel_value_us) + { + vl->n->vl = NULL; + vl->n = NULL; + } + if ((NULL == vl->n) && (NULL == vl->dv)) + { + cores_send_disconnect_info (&vl->target); + free_virtual_link (vl); + return; + } + vl->visibility_task = + GNUNET_SCHEDULER_add_at (GNUNET_TIME_absolute_max (q_timeout, dvh_timeout), + &check_link_down, + vl); +} + + +/** + * Free @a queue. + * + * @param queue the queue to free + */ +static void +free_queue (struct Queue *queue) +{ + struct Neighbour *neighbour = queue->neighbour; + struct TransportClient *tc = queue->tc; + struct MonitorEvent me = { .cs = GNUNET_TRANSPORT_CS_DOWN, + .rtt = GNUNET_TIME_UNIT_FOREVER_REL }; + struct QueueEntry *qe; + int maxxed; + struct PendingAcknowledgement *pa; + struct VirtualLink *vl; + + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Cleaning up queue %u\n", queue->qid); + if (NULL != queue->transmit_task) + { + GNUNET_SCHEDULER_cancel (queue->transmit_task); + queue->transmit_task = NULL; + } + while (NULL != (pa = queue->pa_head)) + { + GNUNET_CONTAINER_MDLL_remove (queue, queue->pa_head, queue->pa_tail, pa); + pa->queue = NULL; + } + + GNUNET_CONTAINER_MDLL_remove (neighbour, + neighbour->queue_head, + neighbour->queue_tail, + queue); + GNUNET_CONTAINER_MDLL_remove (client, + tc->details.communicator.queue_head, + tc->details.communicator.queue_tail, + queue); + maxxed = (COMMUNICATOR_TOTAL_QUEUE_LIMIT <= + tc->details.communicator. + total_queue_length); + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Cleaning up queue with length %u\n", + queue->queue_length); + while (NULL != (qe = queue->queue_head)) + { + GNUNET_CONTAINER_DLL_remove (queue->queue_head, queue->queue_tail, qe); + queue->queue_length--; + tc->details.communicator.total_queue_length--; + if (NULL != qe->pm) + { + GNUNET_assert (qe == qe->pm->qe); + qe->pm->qe = NULL; + } + GNUNET_free (qe); + } + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Cleaning up queue with length %u\n", + queue->queue_length); + GNUNET_assert (0 == queue->queue_length); + if ((maxxed) && (COMMUNICATOR_TOTAL_QUEUE_LIMIT > + tc->details.communicator.total_queue_length)) + { + /* Communicator dropped below threshold, resume all _other_ queues */ + GNUNET_STATISTICS_update ( + GST_stats, + "# Transmission throttled due to communicator queue limit", + -1, + GNUNET_NO); + for (struct Queue *s = tc->details.communicator.queue_head; NULL != s; + s = s->next_client) + schedule_transmit_on_queue (GNUNET_TIME_UNIT_ZERO, + s, + GNUNET_SCHEDULER_PRIORITY_DEFAULT); + } + notify_monitors (&neighbour->pid, queue->address, queue->nt, &me); + GNUNET_free (queue); + + vl = lookup_virtual_link (&neighbour->pid); + if ((NULL != vl) && (neighbour == vl->n)) + { + GNUNET_SCHEDULER_cancel (vl->visibility_task); + check_link_down (vl); + } + if (NULL == neighbour->queue_head) + { + free_neighbour (neighbour); + } +} + + +/** + * Free @a ale + * + * @param ale address list entry to free + */ +static void +free_address_list_entry (struct AddressListEntry *ale) +{ + struct TransportClient *tc = ale->tc; + + GNUNET_CONTAINER_DLL_remove (tc->details.communicator.addr_head, + tc->details.communicator.addr_tail, + ale); + if (NULL != ale->sc) + { + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "store cancel\n"); + GNUNET_PEERSTORE_store_cancel (ale->sc); + ale->sc = NULL; + } + if (NULL != ale->st) + { + GNUNET_SCHEDULER_cancel (ale->st); + ale->st = NULL; + } + GNUNET_free (ale); +} + + +/** + * Stop the peer request in @a value. + * + * @param cls a `struct TransportClient` that no longer makes the request + * @param pid the peer's identity + * @param value a `struct PeerRequest` + * @return #GNUNET_YES (always) + */ +static int +stop_peer_request (void *cls, + const struct GNUNET_PeerIdentity *pid, + void *value) +{ + struct TransportClient *tc = cls; + struct PeerRequest *pr = value; + + GNUNET_PEERSTORE_hello_changed_notify_cancel (pr->nc); + pr->nc = NULL; + GNUNET_assert ( + GNUNET_YES == + GNUNET_CONTAINER_multipeermap_remove (tc->details.application.requests, + pid, + pr)); + GNUNET_free (pr); + + return GNUNET_OK; +} + + +static void +do_shutdown (void *cls); + +/** + * Called whenever a client is disconnected. Frees our + * resources associated with that client. + * + * @param cls closure, NULL + * @param client identification of the client + * @param app_ctx our `struct TransportClient` + */ +static void +client_disconnect_cb (void *cls, + struct GNUNET_SERVICE_Client *client, + void *app_ctx) +{ + struct TransportClient *tc = app_ctx; + + (void) cls; + (void) client; + GNUNET_CONTAINER_DLL_remove (clients_head, clients_tail, tc); + switch (tc->type) + { + case CT_NONE: + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Unknown Client %p disconnected, cleaning up.\n", + tc); + break; + + case CT_CORE: { + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "CORE Client %p disconnected, cleaning up.\n", + tc); + + struct PendingMessage *pm; + + while (NULL != (pm = tc->details.core.pending_msg_head)) + { + GNUNET_CONTAINER_MDLL_remove (client, + tc->details.core.pending_msg_head, + tc->details.core.pending_msg_tail, + pm); + pm->client = NULL; + } + } + break; + + case CT_MONITOR: + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "MONITOR Client %p disconnected, cleaning up.\n", + tc); + + break; + + case CT_COMMUNICATOR: { + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "COMMUNICATOR Client %p disconnected, cleaning up.\n", + tc); + + struct Queue *q; + struct AddressListEntry *ale; + + while (NULL != (q = tc->details.communicator.queue_head)) + free_queue (q); + while (NULL != (ale = tc->details.communicator.addr_head)) + free_address_list_entry (ale); + GNUNET_free (tc->details.communicator.address_prefix); + } + break; + + case CT_APPLICATION: + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "APPLICATION Client %p disconnected, cleaning up.\n", + tc); + + GNUNET_CONTAINER_multipeermap_iterate (tc->details.application.requests, + &stop_peer_request, + tc); + GNUNET_CONTAINER_multipeermap_destroy (tc->details.application.requests); + break; + } + GNUNET_free (tc); + if ((GNUNET_YES == in_shutdown) && (NULL == clients_head)) + { + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "Our last client disconnected\n"); + do_shutdown (cls); + } +} + + +/** + * Iterator telling new CORE client about all existing + * connections to peers. + * + * @param cls the new `struct TransportClient` + * @param pid a connected peer + * @param value the `struct Neighbour` with more information + * @return #GNUNET_OK (continue to iterate) + */ +static int +notify_client_connect_info (void *cls, + const struct GNUNET_PeerIdentity *pid, + void *value) +{ + struct TransportClient *tc = cls; + struct Neighbour *n = value; + struct VirtualLink *vl = n->vl; + + if ((NULL == vl) || (GNUNET_NO == vl->confirmed)) + return GNUNET_OK; + + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Telling new CORE client about existing connection to %s\n", + GNUNET_i2s (pid)); + core_send_connect_info (tc, pid); + return GNUNET_OK; +} + + +/** + * Initialize a "CORE" client. We got a start message from this + * client, so add it to the list of clients for broadcasting of + * inbound messages. + * + * @param cls the client + * @param start the start message that was sent + */ +static void +handle_client_start (void *cls, const struct StartMessage *start) +{ + struct TransportClient *tc = cls; + uint32_t options; + + options = ntohl (start->options); + if ((0 != (1 & options)) && + (0 != GNUNET_memcmp (&start->self, &GST_my_identity))) + { + /* client thinks this is a different peer, reject */ + GNUNET_break (0); + GNUNET_SERVICE_client_drop (tc->client); + return; + } + if (CT_NONE != tc->type) + { + GNUNET_break (0); + GNUNET_SERVICE_client_drop (tc->client); + return; + } + tc->type = CT_CORE; + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "New CORE client with PID %s registered\n", + GNUNET_i2s (&start->self)); + GNUNET_CONTAINER_multipeermap_iterate (neighbours, + ¬ify_client_connect_info, + tc); + GNUNET_SERVICE_client_continue (tc->client); +} + + +/** + * Client asked for transmission to a peer. Process the request. + * + * @param cls the client + * @param obm the send message that was sent + */ +static int +check_client_send (void *cls, const struct OutboundMessage *obm) +{ + struct TransportClient *tc = cls; + uint16_t size; + const struct GNUNET_MessageHeader *obmm; + + if (CT_CORE != tc->type) + { + GNUNET_break (0); + return GNUNET_SYSERR; + } + size = ntohs (obm->header.size) - sizeof(struct OutboundMessage); + if (size < sizeof(struct GNUNET_MessageHeader)) + { + GNUNET_break (0); + return GNUNET_SYSERR; + } + obmm = (const struct GNUNET_MessageHeader *) &obm[1]; + if (size != ntohs (obmm->size)) + { + GNUNET_break (0); + return GNUNET_SYSERR; + } + return GNUNET_OK; +} + + +/** + * Send a response to the @a pm that we have processed a "send" + * request. Sends a confirmation to the "core" client responsible for + * the original request and free's @a pm. + * + * @param pm handle to the original pending message + */ +static void +client_send_response (struct PendingMessage *pm) +{ + struct TransportClient *tc = pm->client; + struct VirtualLink *vl = pm->vl; + + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "client send response\n"); + if (NULL != tc) + { + struct GNUNET_MQ_Envelope *env; + struct SendOkMessage *so_msg; + + env = GNUNET_MQ_msg (so_msg, GNUNET_MESSAGE_TYPE_TRANSPORT_SEND_OK); + so_msg->peer = vl->target; + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Confirming transmission of <%llu> to %s\n", + pm->logging_uuid, + GNUNET_i2s (&vl->target)); + GNUNET_MQ_send (tc->mq, env); + } + free_pending_message (pm); +} + + +/** + * Pick @a hops_array_length random DV paths satisfying @a options + * + * @param dv data structure to pick paths from + * @param options constraints to satisfy + * @param[out] hops_array set to the result + * @param hops_array_length length of the @a hops_array + * @return number of entries set in @a hops_array + */ +static unsigned int +pick_random_dv_hops (const struct DistanceVector *dv, + enum RouteMessageOptions options, + struct DistanceVectorHop **hops_array, + unsigned int hops_array_length) +{ + uint64_t choices[hops_array_length]; + uint64_t num_dv; + unsigned int dv_count; + + /* Pick random vectors, but weighted by distance, giving more weight + to shorter vectors */ + num_dv = 0; + dv_count = 0; + for (struct DistanceVectorHop *pos = dv->dv_head; NULL != pos; + pos = pos->next_dv) + { + if ((0 == (options & RMO_UNCONFIRMED_ALLOWED)) && + (GNUNET_TIME_absolute_get_remaining (pos->path_valid_until) + .rel_value_us == 0)) + continue; /* pos unconfirmed and confirmed required */ + num_dv += MAX_DV_HOPS_ALLOWED - pos->distance; + dv_count++; + } + if (0 == dv_count) + return 0; + if (dv_count <= hops_array_length) + { + dv_count = 0; + for (struct DistanceVectorHop *pos = dv->dv_head; NULL != pos; + pos = pos->next_dv) + hops_array[dv_count++] = pos; + return dv_count; + } + for (unsigned int i = 0; i < hops_array_length; i++) + { + int ok = GNUNET_NO; + while (GNUNET_NO == ok) + { + choices[i] = + GNUNET_CRYPTO_random_u64 (GNUNET_CRYPTO_QUALITY_WEAK, num_dv); + ok = GNUNET_YES; + for (unsigned int j = 0; j < i; j++) + if (choices[i] == choices[j]) + { + ok = GNUNET_NO; + break; + } + } + } + dv_count = 0; + num_dv = 0; + for (struct DistanceVectorHop *pos = dv->dv_head; NULL != pos; + pos = pos->next_dv) + { + uint32_t delta = MAX_DV_HOPS_ALLOWED - pos->distance; + + if ((0 == (options & RMO_UNCONFIRMED_ALLOWED)) && + (GNUNET_TIME_absolute_get_remaining (pos->path_valid_until) + .rel_value_us == 0)) + continue; /* pos unconfirmed and confirmed required */ + for (unsigned int i = 0; i < hops_array_length; i++) + if ((num_dv <= choices[i]) && (num_dv + delta > choices[i])) + hops_array[dv_count++] = pos; + num_dv += delta; + } + return dv_count; +} + + +/** + * Communicator started. Test message is well-formed. + * + * @param cls the client + * @param cam the send message that was sent + */ +static int +check_communicator_available ( + void *cls, + const struct GNUNET_TRANSPORT_CommunicatorAvailableMessage *cam) +{ + struct TransportClient *tc = cls; + uint16_t size; + + if (CT_NONE != tc->type) + { + GNUNET_break (0); + return GNUNET_SYSERR; + } + tc->type = CT_COMMUNICATOR; + size = ntohs (cam->header.size) - sizeof(*cam); + if (0 == size) + return GNUNET_OK; /* receive-only communicator */ + GNUNET_MQ_check_zero_termination (cam); + return GNUNET_OK; +} + + +/** + * Send ACK to communicator (if requested) and free @a cmc. + * + * @param cmc context for which we are done handling the message + */ +static void +finish_cmc_handling_with_continue (struct CommunicatorMessageContext *cmc, + unsigned + int continue_client) +{ + if (0 != ntohl (cmc->im.fc_on)) + { + /* send ACK when done to communicator for flow control! */ + struct GNUNET_MQ_Envelope *env; + struct GNUNET_TRANSPORT_IncomingMessageAck *ack; + + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Acknowledge message with flow control id %" PRIu64 "\n", + cmc->im.fc_id); + env = GNUNET_MQ_msg (ack, GNUNET_MESSAGE_TYPE_TRANSPORT_INCOMING_MSG_ACK); + ack->reserved = htonl (0); + ack->fc_id = cmc->im.fc_id; + ack->sender = cmc->im.neighbour_sender; + GNUNET_MQ_send (cmc->tc->mq, env); + } + + if (GNUNET_YES == continue_client) + { + GNUNET_SERVICE_client_continue (cmc->tc->client); + } + GNUNET_free (cmc); +} + + +static void +finish_cmc_handling (struct CommunicatorMessageContext *cmc) +{ + finish_cmc_handling_with_continue (cmc, GNUNET_YES); +} + + +/** + * Client confirms that it is done handling message(s) to a particular + * peer. We may now provide more messages to CORE for this peer. + * + * Notifies the respective queues that more messages can now be received. + * + * @param cls the client + * @param rom the message that was sent + */ +static void +handle_client_recv_ok (void *cls, const struct RecvOkMessage *rom) +{ + struct TransportClient *tc = cls; + struct VirtualLink *vl; + uint32_t delta; + struct CommunicatorMessageContext *cmc; + + if (CT_CORE != tc->type) + { + GNUNET_break (0); + GNUNET_SERVICE_client_drop (tc->client); + return; + } + vl = lookup_virtual_link (&rom->peer); + if ((NULL == vl) || (GNUNET_NO == vl->confirmed)) + { + GNUNET_STATISTICS_update (GST_stats, + "# RECV_OK dropped: virtual link unknown", + 1, + GNUNET_NO); + GNUNET_SERVICE_client_continue (tc->client); + return; + } + delta = ntohl (rom->increase_window_delta); + vl->core_recv_window += delta; + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "CORE ack receiving message, increased CORE recv window to %d\n", + vl->core_recv_window); + GNUNET_SERVICE_client_continue (tc->client); + if (vl->core_recv_window <= 0) + return; + /* resume communicators */ + while (NULL != (cmc = vl->cmc_tail)) + { + GNUNET_CONTAINER_DLL_remove (vl->cmc_head, vl->cmc_tail, cmc); + finish_cmc_handling (cmc); + } +} + + +/** + * Communicator started. Process the request. + * + * @param cls the client + * @param cam the send message that was sent + */ +static void +handle_communicator_available ( + void *cls, + const struct GNUNET_TRANSPORT_CommunicatorAvailableMessage *cam) +{ + struct TransportClient *tc = cls; + uint16_t size; + + size = ntohs (cam->header.size) - sizeof(*cam); + if (0 == size) + { + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Receive-only communicator connected\n"); + return; /* receive-only communicator */ + } + tc->details.communicator.address_prefix = + GNUNET_strdup ((const char *) &cam[1]); + tc->details.communicator.cc = + (enum GNUNET_TRANSPORT_CommunicatorCharacteristics) ntohl (cam->cc); + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Communicator with prefix `%s' connected\n", + tc->details.communicator.address_prefix); + GNUNET_SERVICE_client_continue (tc->client); +} + + +/** + * Communicator requests backchannel transmission. Check the request. + * + * @param cls the client + * @param cb the send message that was sent + * @return #GNUNET_OK if message is well-formed + */ +static int +check_communicator_backchannel ( + void *cls, + const struct GNUNET_TRANSPORT_CommunicatorBackchannel *cb) +{ + const struct GNUNET_MessageHeader *inbox; + const char *is; + uint16_t msize; + uint16_t isize; + + (void) cls; + msize = ntohs (cb->header.size) - sizeof(*cb); + inbox = (const struct GNUNET_MessageHeader *) &cb[1]; + isize = ntohs (inbox->size); + if (isize >= msize) + { + GNUNET_break (0); + return GNUNET_SYSERR; + } + is = (const char *) inbox; + is += isize; + msize -= isize; + GNUNET_assert (0 < msize); + if ('\0' != is[msize - 1]) + { + GNUNET_break (0); + return GNUNET_SYSERR; + } + return GNUNET_OK; +} + + +/** + * Sign ephemeral keys in our @a dv are current. + * + * @param[in,out] dv virtual link to update ephemeral for + */ +static void +sign_ephemeral (struct DistanceVector *dv) +{ + struct EphemeralConfirmationPS ec; + + dv->monotime = GNUNET_TIME_absolute_get_monotonic (GST_cfg); + dv->ephemeral_validity = + GNUNET_TIME_absolute_add (dv->monotime, EPHEMERAL_VALIDITY); + ec.purpose.purpose = htonl (GNUNET_SIGNATURE_PURPOSE_TRANSPORT_EPHEMERAL); + ec.target = dv->target; + ec.ephemeral_key = dv->ephemeral_key; + ec.sender_monotonic_time = GNUNET_TIME_absolute_hton (dv->monotime); + ec.purpose.size = htonl (sizeof(ec)); + GNUNET_CRYPTO_eddsa_sign (GST_my_private_key, + &ec, + &dv->sender_sig); +} + + +/** + * Send the message @a payload on @a queue. + * + * @param queue the queue to use for transmission + * @param pm pending message to update once transmission is done, may be NULL! + * @param payload the payload to send (encapsulated in a + * #GNUNET_MESSAGE_TYPE_TRANSPORT_SEND_MSG). + * @param payload_size number of bytes in @a payload + */ +static void +queue_send_msg (struct Queue *queue, + struct PendingMessage *pm, + const void *payload, + size_t payload_size) +{ + struct Neighbour *n = queue->neighbour; + struct GNUNET_TRANSPORT_SendMessageTo *smt; + struct GNUNET_MQ_Envelope *env; + struct PendingAcknowledgement *pa; + + GNUNET_log ( + GNUNET_ERROR_TYPE_DEBUG, + "Queueing %u bytes of payload for transmission <%llu> on queue %llu to %s\n", + (unsigned int) payload_size, + (NULL == pm) ? 0 : pm->logging_uuid, + (unsigned long long) queue->qid, + GNUNET_i2s (&queue->neighbour->pid)); + env = GNUNET_MQ_msg_extra (smt, + payload_size, + GNUNET_MESSAGE_TYPE_TRANSPORT_SEND_MSG); + smt->qid = htonl (queue->qid); + smt->mid = GNUNET_htonll (queue->mid_gen); + smt->receiver = n->pid; + memcpy (&smt[1], payload, payload_size); + { + /* Pass the env to the communicator of queue for transmission. */ + struct QueueEntry *qe; + + qe = GNUNET_new (struct QueueEntry); + qe->mid = queue->mid_gen; + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Create QueueEntry with MID %" PRIu64 + " and QID %u and prefix %s\n", + qe->mid, + queue->qid, + queue->tc->details.communicator.address_prefix); + queue->mid_gen++; + qe->queue = queue; + if (NULL != pm) + { + qe->pm = pm; + // TODO Why do we have a retransmission. When we know, make decision if we still want this. + // GNUNET_assert (NULL == pm->qe); + if (NULL != pm->qe) + { + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Retransmitting message <%llu> remove pm from qe with MID: %llu \n", + pm->logging_uuid, + (unsigned long long) pm->qe->mid); + pm->qe->pm = NULL; + } + pm->qe = qe; + } + GNUNET_CONTAINER_DLL_insert (queue->queue_head, queue->queue_tail, qe); + GNUNET_assert (CT_COMMUNICATOR == queue->tc->type); + if (0 == queue->q_capacity) + { + // Messages without FC or fragments can get here. + if (NULL != pm) + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Message %llu (pm type %u) was not send because queue has no capacity.\n", + pm->logging_uuid, + pm->pmt); + GNUNET_free (env); + return; + } + queue->queue_length++; + queue->tc->details.communicator.total_queue_length++; + if (GNUNET_NO == queue->unlimited_length) + queue->q_capacity--; + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Queue %s with qid %u has capacity %" PRIu64 "\n", + queue->address, + queue->qid, + queue->q_capacity); + if (COMMUNICATOR_TOTAL_QUEUE_LIMIT == + queue->tc->details.communicator.total_queue_length) + queue->idle = GNUNET_NO; + if (QUEUE_LENGTH_LIMIT == queue->queue_length) + queue->idle = GNUNET_NO; + if (0 == queue->q_capacity) + queue->idle = GNUNET_NO; + + if (NULL != pm && NULL != (pa = pm->pa_head)) + { + while (pm != pa->pm) + pa = pa->next_pa; + pa->num_send++; + } + // GNUNET_CONTAINER_multiuuidmap_get (pending_acks, &ack[i].ack_uuid.value); + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Sending message MID %" PRIu64 + " of type %u (%u) and size %lu with MQ %p QID %u\n", + GNUNET_ntohll (smt->mid), + ntohs (((const struct GNUNET_MessageHeader *) payload)->type), + ntohs (smt->header.size), + (unsigned long) payload_size, + queue->tc->mq, + queue->qid); + GNUNET_MQ_send (queue->tc->mq, env); + } +} + + +/** + * Pick a queue of @a n under constraints @a options and schedule + * transmission of @a hdr. + * + * @param n neighbour to send to + * @param hdr message to send as payload + * @param options whether queues must be confirmed or not, + * and whether we may pick multiple (2) queues + * @return expected RTT for transmission, #GNUNET_TIME_UNIT_FOREVER_REL if sending failed + */ +static struct GNUNET_TIME_Relative +route_via_neighbour (const struct Neighbour *n, + const struct GNUNET_MessageHeader *hdr, + enum RouteMessageOptions options) +{ + struct GNUNET_TIME_Absolute now; + unsigned int candidates; + unsigned int sel1; + unsigned int sel2; + struct GNUNET_TIME_Relative rtt; + + /* Pick one or two 'random' queues from n (under constraints of options) */ + now = GNUNET_TIME_absolute_get (); + /* FIXME-OPTIMIZE: give queues 'weights' and pick proportional to + weight in the future; weight could be assigned by observed + bandwidth (note: not sure if we should do this for this type + of control traffic though). */ + candidates = 0; + for (struct Queue *pos = n->queue_head; NULL != pos; + pos = pos->next_neighbour) + { + if ((0 != (options & RMO_UNCONFIRMED_ALLOWED)) || + (pos->validated_until.abs_value_us > now.abs_value_us)) + candidates++; + } + if (0 == candidates) + { + /* This can happen rarely if the last confirmed queue timed + out just as we were beginning to process this message. */ + GNUNET_log (GNUNET_ERROR_TYPE_INFO, + "Could not route message of type %u to %s: no valid queue\n", + ntohs (hdr->type), + GNUNET_i2s (&n->pid)); + GNUNET_STATISTICS_update (GST_stats, + "# route selection failed (all no valid queue)", + 1, + GNUNET_NO); + return GNUNET_TIME_UNIT_FOREVER_REL; + } + + rtt = GNUNET_TIME_UNIT_FOREVER_REL; + sel1 = GNUNET_CRYPTO_random_u32 (GNUNET_CRYPTO_QUALITY_WEAK, candidates); + if (0 == (options & RMO_REDUNDANT)) + sel2 = candidates; /* picks none! */ + else + sel2 = GNUNET_CRYPTO_random_u32 (GNUNET_CRYPTO_QUALITY_WEAK, candidates); + candidates = 0; + for (struct Queue *pos = n->queue_head; NULL != pos; + pos = pos->next_neighbour) + { + if ((0 != (options & RMO_UNCONFIRMED_ALLOWED)) || + (pos->validated_until.abs_value_us > now.abs_value_us)) + { + if ((sel1 == candidates) || (sel2 == candidates)) + { + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Routing message of type %u to %s using %s (#%u)\n", + ntohs (hdr->type), + GNUNET_i2s (&n->pid), + pos->address, + (sel1 == candidates) ? 1 : 2); + rtt = GNUNET_TIME_relative_min (rtt, pos->pd.aged_rtt); + queue_send_msg (pos, NULL, hdr, ntohs (hdr->size)); + } + candidates++; + } + } + return rtt; +} + + +/** + * Structure of the key material used to encrypt backchannel messages. + */ +struct DVKeyState +{ + /** + * State of our block cipher. + */ + gcry_cipher_hd_t cipher; + + /** + * Actual key material. + */ + struct + { + /** + * Key used for HMAC calculations (via #GNUNET_CRYPTO_hmac()). + */ + struct GNUNET_CRYPTO_AuthKey hmac_key; + + /** + * Symmetric key to use for encryption. + */ + char aes_key[256 / 8]; + + /** + * Counter value to use during setup. + */ + char aes_ctr[128 / 8]; + } material; +}; + + +/** + * Given the key material in @a km and the initialization vector + * @a iv, setup the key material for the backchannel in @a key. + * + * @param km raw master secret + * @param iv initialization vector + * @param[out] key symmetric cipher and HMAC state to generate + */ +static void +dv_setup_key_state_from_km (const struct GNUNET_HashCode *km, + const struct GNUNET_ShortHashCode *iv, + struct DVKeyState *key) +{ + /* must match what we defive from decapsulated key */ + GNUNET_assert (GNUNET_YES == + GNUNET_CRYPTO_kdf (&key->material, + sizeof(key->material), + "transport-backchannel-key", + strlen ("transport-backchannel-key"), + km, + sizeof(*km), + iv, + sizeof(*iv), + NULL)); + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Deriving backchannel key based on KM %s and IV %s\n", + GNUNET_h2s (km), + GNUNET_sh2s (iv)); + GNUNET_assert (0 == gcry_cipher_open (&key->cipher, + GCRY_CIPHER_AES256 /* low level: go for speed */, + GCRY_CIPHER_MODE_CTR, + 0 /* flags */)); + GNUNET_assert (0 == gcry_cipher_setkey (key->cipher, + &key->material.aes_key, + sizeof(key->material.aes_key))); + gcry_cipher_setctr (key->cipher, + &key->material.aes_ctr, + sizeof(key->material.aes_ctr)); +} + + +/** + * Do HMAC calculation for backchannel messages over @a data using key + * material from @a key. + * + * @param key key material (from DH) + * @param[out] hmac set to the HMAC + * @param data data to perform HMAC calculation over + * @param data_size number of bytes in @a data + */ +static void +dv_hmac (const struct DVKeyState *key, + struct GNUNET_HashCode *hmac, + const void *data, + size_t data_size) +{ + GNUNET_CRYPTO_hmac (&key->material.hmac_key, data, data_size, hmac); +} + + +/** + * Perform backchannel encryption using symmetric secret in @a key + * to encrypt data from @a in to @a dst. + * + * @param[in,out] key key material to use + * @param dst where to write the result + * @param in input data to encrypt (plaintext) + * @param in_size number of bytes of input in @a in and available at @a dst + */ +static void +dv_encrypt (struct DVKeyState *key, const void *in, void *dst, size_t in_size) +{ + GNUNET_assert (0 == + gcry_cipher_encrypt (key->cipher, dst, in_size, in, in_size)); +} + + +/** + * Perform backchannel encryption using symmetric secret in @a key + * to encrypt data from @a in to @a dst. + * + * @param[in,out] key key material to use + * @param ciph cipher text to decrypt + * @param[out] out output data to generate (plaintext) + * @param out_size number of bytes of input in @a ciph and available in @a out + * @return GNUNET_OK on success + */ +static enum GNUNET_GenericReturnValue +dv_decrypt (struct DVKeyState *key, + void *out, + const void *ciph, + size_t out_size) +{ + return (0 == + gcry_cipher_decrypt (key->cipher, + out, out_size, + ciph, out_size)) ? GNUNET_OK : GNUNET_SYSERR; +} + + +/** + * Clean up key material in @a key. + * + * @param key key material to clean up (memory must not be free'd!) + */ +static void +dv_key_clean (struct DVKeyState *key) +{ + gcry_cipher_close (key->cipher); + GNUNET_CRYPTO_zero_keys (&key->material, sizeof(key->material)); +} + + +/** + * Function to call to further operate on the now DV encapsulated + * message @a hdr, forwarding it via @a next_hop under respect of + * @a options. + * + * @param cls closure + * @param next_hop next hop of the DV path + * @param hdr encapsulated message, technically a `struct TransportDFBoxMessage` + * @param options options of the original message + */ +typedef void (*DVMessageHandler) (void *cls, + struct Neighbour *next_hop, + const struct GNUNET_MessageHeader *hdr, + enum RouteMessageOptions options); + +/** + * Pick a path of @a dv under constraints @a options and schedule + * transmission of @a hdr. + * + * @param target neighbour to ultimately send to + * @param num_dvhs length of the @a dvhs array + * @param dvhs array of hops to send the message to + * @param hdr message to send as payload + * @param use function to call with the encapsulated message + * @param use_cls closure for @a use + * @param options whether path must be confirmed or not, to be passed to @a use + * @param without_fc shall this TransportDVBoxMessage be forwarded without flow control. + * @return expected RTT for transmission, #GNUNET_TIME_UNIT_FOREVER_REL if sending failed + */ +static struct GNUNET_TIME_Relative +encapsulate_for_dv (struct DistanceVector *dv, + unsigned int num_dvhs, + struct DistanceVectorHop **dvhs, + const struct GNUNET_MessageHeader *hdr, + DVMessageHandler use, + void *use_cls, + enum RouteMessageOptions options, + enum GNUNET_GenericReturnValue without_fc) +{ + struct TransportDVBoxMessage box_hdr; + struct TransportDVBoxPayloadP payload_hdr; + uint16_t enc_body_size = ntohs (hdr->size); + char enc[sizeof(struct TransportDVBoxPayloadP) + enc_body_size] GNUNET_ALIGN; + struct DVKeyState *key; + struct GNUNET_TIME_Relative rtt; + struct GNUNET_HashCode k; + + key = GNUNET_new (struct DVKeyState); + /* Encrypt payload */ + box_hdr.header.type = htons (GNUNET_MESSAGE_TYPE_TRANSPORT_DV_BOX); + box_hdr.total_hops = htons (0); + box_hdr.without_fc = htons (without_fc); + // update_ephemeral (dv); + if (0 == + GNUNET_TIME_absolute_get_remaining (dv->ephemeral_validity).rel_value_us) + { + GNUNET_CRYPTO_eddsa_kem_encaps (&dv->target.public_key, + &dv->ephemeral_key, + &k); + sign_ephemeral (dv); + } + box_hdr.ephemeral_key = dv->ephemeral_key; + payload_hdr.sender_sig = dv->sender_sig; + + GNUNET_CRYPTO_random_block (GNUNET_CRYPTO_QUALITY_NONCE, + &box_hdr.iv, + sizeof(box_hdr.iv)); + // We are creating this key, so this must work. + // FIXME: Possibly also add return values here. We are processing + // Input from other peers... + dv_setup_key_state_from_km (&k, &box_hdr.iv, key); + payload_hdr.sender = GST_my_identity; + payload_hdr.monotonic_time = GNUNET_TIME_absolute_hton (dv->monotime); + dv_encrypt (key, &payload_hdr, enc, sizeof(payload_hdr)); + dv_encrypt (key, + hdr, + &enc[sizeof(struct TransportDVBoxPayloadP)], + enc_body_size); + dv_hmac (key, &box_hdr.hmac, enc, sizeof(enc)); + dv_key_clean (key); + rtt = GNUNET_TIME_UNIT_FOREVER_REL; + /* For each selected path, take the pre-computed header and body + and add the path in the middle of the message; then send it. */ + for (unsigned int i = 0; i < num_dvhs; i++) + { + struct DistanceVectorHop *dvh = dvhs[i]; + unsigned int num_hops = dvh->distance + 1; + char buf[sizeof(struct TransportDVBoxMessage) + + sizeof(struct GNUNET_PeerIdentity) * num_hops + + sizeof(struct TransportDVBoxPayloadP) + + enc_body_size] GNUNET_ALIGN; + struct GNUNET_PeerIdentity *dhops; + + box_hdr.header.size = htons (sizeof(buf)); + box_hdr.orig_size = htons (sizeof(buf)); + box_hdr.num_hops = htons (num_hops); + memcpy (buf, &box_hdr, sizeof(box_hdr)); + dhops = (struct GNUNET_PeerIdentity *) &buf[sizeof(box_hdr)]; + memcpy (dhops, + dvh->path, + dvh->distance * sizeof(struct GNUNET_PeerIdentity)); + dhops[dvh->distance] = dv->target; + if (GNUNET_EXTRA_LOGGING > 0) + { + char *path; + + path = GNUNET_strdup (GNUNET_i2s (&GST_my_identity)); + for (unsigned int j = 0; j < num_hops; j++) + { + char *tmp; + + GNUNET_asprintf (&tmp, "%s-%s", path, GNUNET_i2s (&dhops[j])); + GNUNET_free (path); + path = tmp; + } + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Routing message of type %u to %s using DV (#%u/%u) via %s\n", + ntohs (hdr->type), + GNUNET_i2s (&dv->target), + i + 1, + num_dvhs, + path); + GNUNET_free (path); + } + rtt = GNUNET_TIME_relative_min (rtt, dvh->pd.aged_rtt); + memcpy (&dhops[num_hops], enc, sizeof(enc)); + use (use_cls, + dvh->next_hop, + (const struct GNUNET_MessageHeader *) buf, + options); + GNUNET_free (key); + } + return rtt; +} + + +/** + * Wrapper around #route_via_neighbour() that matches the + * #DVMessageHandler structure. + * + * @param cls unused + * @param next_hop where to send next + * @param hdr header of the message to send + * @param options message options for queue selection + */ +static void +send_dv_to_neighbour (void *cls, + struct Neighbour *next_hop, + const struct GNUNET_MessageHeader *hdr, + enum RouteMessageOptions options) +{ + (void) cls; + (void) route_via_neighbour (next_hop, hdr, RMO_UNCONFIRMED_ALLOWED); +} + + +/** + * We need to transmit @a hdr to @a target. If necessary, this may + * involve DV routing. This function routes without applying flow + * control or congestion control and should only be used for control + * traffic. + * + * @param target peer to receive @a hdr + * @param hdr header of the message to route and #GNUNET_free() + * @param options which transmission channels are allowed + * @return expected RTT for transmission, #GNUNET_TIME_UNIT_FOREVER_REL if sending failed + */ +static struct GNUNET_TIME_Relative +route_control_message_without_fc (struct VirtualLink *vl, +// route_control_message_without_fc (const struct GNUNET_PeerIdentity *target, + const struct GNUNET_MessageHeader *hdr, + enum RouteMessageOptions options) +{ + // struct VirtualLink *vl; + struct Neighbour *n; + struct DistanceVector *dv; + struct GNUNET_TIME_Relative rtt1; + struct GNUNET_TIME_Relative rtt2; + const struct GNUNET_PeerIdentity *target = &vl->target; + + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Trying to route message of type %u to %s without fc\n", + ntohs (hdr->type), + GNUNET_i2s (target)); + + // TODO Do this elsewhere. vl should be given as parameter to method. + // vl = lookup_virtual_link (target); + GNUNET_assert (NULL != vl && GNUNET_YES == vl->confirmed); + if (NULL == vl) + return GNUNET_TIME_UNIT_FOREVER_REL; + n = vl->n; + dv = (0 != (options & RMO_DV_ALLOWED)) ? vl->dv : NULL; + if (0 == (options & RMO_UNCONFIRMED_ALLOWED)) + { + /* if confirmed is required, and we do not have anything + confirmed, drop respective options */ + if (NULL == n) + n = lookup_neighbour (target); + if ((NULL == dv) && (0 != (options & RMO_DV_ALLOWED))) + dv = GNUNET_CONTAINER_multipeermap_get (dv_routes, target); + } + if ((NULL == n) && (NULL == dv)) + { + GNUNET_log (GNUNET_ERROR_TYPE_INFO, + "Cannot route message of type %u to %s: no route\n", + ntohs (hdr->type), + GNUNET_i2s (target)); + GNUNET_STATISTICS_update (GST_stats, + "# Messages dropped in routing: no acceptable method", + 1, + GNUNET_NO); + return GNUNET_TIME_UNIT_FOREVER_REL; + } + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Routing message of type %u to %s with options %X\n", + ntohs (hdr->type), + GNUNET_i2s (target), + (unsigned int) options); + /* If both dv and n are possible and we must choose: + flip a coin for the choice between the two; for now 50/50 */ + if ((NULL != n) && (NULL != dv) && (0 == (options & RMO_REDUNDANT))) + { + if (0 == GNUNET_CRYPTO_random_u32 (GNUNET_CRYPTO_QUALITY_WEAK, 2)) + n = NULL; + else + dv = NULL; + } + if ((NULL != n) && (NULL != dv)) + options &= ~RMO_REDUNDANT; /* We will do one DV and one direct, that's + enough for redundancy, so clear the flag. */ + rtt1 = GNUNET_TIME_UNIT_FOREVER_REL; + rtt2 = GNUNET_TIME_UNIT_FOREVER_REL; + if (NULL != n) + { + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Try to route message of type %u to %s without fc via neighbour\n", + ntohs (hdr->type), + GNUNET_i2s (target)); + rtt1 = route_via_neighbour (n, hdr, options); + } + if (NULL != dv) + { + struct DistanceVectorHop *hops[2]; + unsigned int res; + + res = pick_random_dv_hops (dv, + options, + hops, + (0 == (options & RMO_REDUNDANT)) ? 1 : 2); + if (0 == res) + { + GNUNET_log (GNUNET_ERROR_TYPE_INFO, + "Failed to route message, could not determine DV path\n"); + return rtt1; + } + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "encapsulate_for_dv 1\n"); + rtt2 = encapsulate_for_dv (dv, + res, + hops, + hdr, + &send_dv_to_neighbour, + NULL, + options & (~RMO_REDUNDANT), + GNUNET_YES); + } + return GNUNET_TIME_relative_min (rtt1, rtt2); +} + + +static void +consider_sending_fc (void *cls); + +/** + * Something changed on the virtual link with respect to flow + * control. Consider retransmitting the FC window size. + * + * @param cls a `struct VirtualLink` to work with + */ +static void +task_consider_sending_fc (void *cls) +{ + struct VirtualLink *vl = cls; + vl->fc_retransmit_task = NULL; + consider_sending_fc (cls); +} + + +/** + * Something changed on the virtual link with respect to flow + * control. Consider retransmitting the FC window size. + * + * @param cls a `struct VirtualLink` to work with + */ +static void +consider_sending_fc (void *cls) +{ + struct VirtualLink *vl = cls; + struct GNUNET_TIME_Absolute monotime; + struct TransportFlowControlMessage fc; + struct GNUNET_TIME_Relative duration; + struct GNUNET_TIME_Relative rtt; + + duration = GNUNET_TIME_absolute_get_duration (vl->last_fc_transmission); + /* OPTIMIZE-FC-BDP: decide sane criteria on when to do this, instead of doing + it always! */ + /* For example, we should probably ONLY do this if a bit more than + an RTT has passed, or if the window changed "significantly" since + then. See vl->last_fc_rtt! NOTE: to do this properly, we also + need an estimate for the bandwidth-delay-product for the entire + VL, as that determines "significantly". We have the delay, but + the bandwidth statistics need to be added for the VL!*/(void) duration; + + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Sending FC seq %u to %s with new window %llu\n", + (unsigned int) vl->fc_seq_gen, + GNUNET_i2s (&vl->target), + (unsigned long long) vl->incoming_fc_window_size); + monotime = GNUNET_TIME_absolute_get_monotonic (GST_cfg); + vl->last_fc_transmission = monotime; + fc.header.type = htons (GNUNET_MESSAGE_TYPE_TRANSPORT_FLOW_CONTROL); + fc.header.size = htons (sizeof(fc)); + fc.seq = htonl (vl->fc_seq_gen++); + fc.inbound_window_size = GNUNET_htonll (vl->incoming_fc_window_size + + vl->incoming_fc_window_size_used + + vl->incoming_fc_window_size_loss); + fc.outbound_sent = GNUNET_htonll (vl->outbound_fc_window_size_used); + fc.outbound_window_size = GNUNET_htonll (vl->outbound_fc_window_size); + fc.sender_time = GNUNET_TIME_absolute_hton (monotime); + rtt = route_control_message_without_fc (vl, &fc.header, RMO_DV_ALLOWED); + if (GNUNET_TIME_UNIT_FOREVER_REL.rel_value_us == rtt.rel_value_us) + { + rtt = GNUNET_TIME_UNIT_SECONDS; + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "FC retransmission to %s failed, will retry in %s\n", + GNUNET_i2s (&vl->target), + GNUNET_STRINGS_relative_time_to_string (rtt, GNUNET_YES)); + vl->last_fc_rtt = GNUNET_TIME_UNIT_ZERO; + } + else + { + /* OPTIMIZE-FC-BDP: rtt is not ideal, we can do better! */ + vl->last_fc_rtt = rtt; + } + if (NULL != vl->fc_retransmit_task) + GNUNET_SCHEDULER_cancel (vl->fc_retransmit_task); + if (MAX_FC_RETRANSMIT_COUNT == vl->fc_retransmit_count) + { + rtt = GNUNET_TIME_UNIT_MINUTES; + vl->fc_retransmit_count = 0; + } + vl->fc_retransmit_task = + GNUNET_SCHEDULER_add_delayed (rtt, &task_consider_sending_fc, vl); + vl->fc_retransmit_count++; +} + + +/** + * There is a message at the head of the pending messages for @a vl + * which may be ready for transmission. Check if a queue is ready to + * take it. + * + * This function must (1) check for flow control to ensure that we can + * right now send to @a vl, (2) check that the pending message in the + * queue is actually eligible, (3) determine if any applicable queue + * (direct neighbour or DVH path) is ready to accept messages, and + * (4) prioritize based on the preferences associated with the + * pending message. + * + * So yeah, easy. + * + * @param vl virtual link where we should check for transmission + */ +static void +check_vl_transmission (struct VirtualLink *vl) +{ + struct Neighbour *n = vl->n; + struct DistanceVector *dv = vl->dv; + struct GNUNET_TIME_Absolute now; + struct VirtualLink *vl_next_hop; + int elig; + + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "check_vl_transmission to target %s\n", + GNUNET_i2s (&vl->target)); + /* Check that we have an eligible pending message! + (cheaper than having #transmit_on_queue() find out!) */ + elig = GNUNET_NO; + for (struct PendingMessage *pm = vl->pending_msg_head; NULL != pm; + pm = pm->next_vl) + { + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "check_vl_transmission loop\n"); + if (NULL != pm->qe) + continue; /* not eligible, is in a queue! */ + if (pm->bytes_msg + vl->outbound_fc_window_size_used > + vl->outbound_fc_window_size) + { + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Stalled message %llu transmission on VL %s due to flow control: %llu < %llu\n", + pm->logging_uuid, + GNUNET_i2s (&vl->target), + (unsigned long long) vl->outbound_fc_window_size, + (unsigned long long) (pm->bytes_msg + + vl->outbound_fc_window_size_used)); + consider_sending_fc (vl); + return; /* We have a message, but flow control says "nope" */ + } + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Target window on VL %s not stalled. Scheduling transmission on queue\n", + GNUNET_i2s (&vl->target)); + /* Notify queues at direct neighbours that we are interested */ + now = GNUNET_TIME_absolute_get (); + if (NULL != n) + { + for (struct Queue *queue = n->queue_head; NULL != queue; + queue = queue->next_neighbour) + { + if ((GNUNET_YES == queue->idle) && + (queue->validated_until.abs_value_us > now.abs_value_us)) + { + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Direct neighbour %s not stalled\n", + GNUNET_i2s (&n->pid)); + schedule_transmit_on_queue (GNUNET_TIME_UNIT_ZERO, + queue, + GNUNET_SCHEDULER_PRIORITY_DEFAULT); + elig = GNUNET_YES; + } + else + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Neighbour Queue QID: %u (%u) busy or invalid\n", + queue->qid, + queue->idle); + } + } + /* Notify queues via DV that we are interested */ + if (NULL != dv) + { + /* Do DV with lower scheduler priority, which effectively means that + IF a neighbour exists and is available, we prefer it. */ + for (struct DistanceVectorHop *pos = dv->dv_head; NULL != pos; + pos = pos->next_dv) + { + struct Neighbour *nh = pos->next_hop; + + + if (pos->path_valid_until.abs_value_us <= now.abs_value_us) + continue; /* skip this one: path not validated */ + else + { + vl_next_hop = lookup_virtual_link (&nh->pid); + GNUNET_assert (NULL != vl_next_hop); + if (pm->bytes_msg + vl_next_hop->outbound_fc_window_size_used > + vl_next_hop->outbound_fc_window_size) + { + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Stalled message %llu transmission on next hop %s due to flow control: %llu < %llu\n", + pm->logging_uuid, + GNUNET_i2s (&vl_next_hop->target), + (unsigned long + long) vl_next_hop->outbound_fc_window_size, + (unsigned long long) (pm->bytes_msg + + vl_next_hop-> + outbound_fc_window_size_used)); + consider_sending_fc (vl_next_hop); + continue; /* We have a message, but flow control says "nope" for the first hop of this path */ + } + for (struct Queue *queue = nh->queue_head; NULL != queue; + queue = queue->next_neighbour) + if ((GNUNET_YES == queue->idle) && + (queue->validated_until.abs_value_us > now.abs_value_us)) + { + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Next hop neighbour %s not stalled\n", + GNUNET_i2s (&nh->pid)); + schedule_transmit_on_queue (GNUNET_TIME_UNIT_ZERO, + queue, + GNUNET_SCHEDULER_PRIORITY_BACKGROUND); + elig = GNUNET_YES; + } + else + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "DV Queue QID: %u (%u) busy or invalid\n", + queue->qid, + queue->idle); + } + } + } + if (GNUNET_YES == elig) + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Eligible message %llu of size %u to %s: %llu/%llu\n", + pm->logging_uuid, + pm->bytes_msg, + GNUNET_i2s (&vl->target), + (unsigned long long) vl->outbound_fc_window_size, + (unsigned long long) (pm->bytes_msg + + vl->outbound_fc_window_size_used)); + break; + } +} + + +/** + * Client asked for transmission to a peer. Process the request. + * + * @param cls the client + * @param obm the send message that was sent + */ +static void +handle_client_send (void *cls, const struct OutboundMessage *obm) +{ + struct TransportClient *tc = cls; + struct PendingMessage *pm; + const struct GNUNET_MessageHeader *obmm; + uint32_t bytes_msg; + struct VirtualLink *vl; + enum GNUNET_MQ_PriorityPreferences pp; + + GNUNET_assert (CT_CORE == tc->type); + obmm = (const struct GNUNET_MessageHeader *) &obm[1]; + bytes_msg = ntohs (obmm->size); + pp = (enum GNUNET_MQ_PriorityPreferences) ntohl (obm->priority); + vl = lookup_virtual_link (&obm->peer); + if ((NULL == vl) || (GNUNET_NO == vl->confirmed)) + { + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Don't have %s as a neighbour (anymore).\n", + GNUNET_i2s (&obm->peer)); + /* Failure: don't have this peer as a neighbour (anymore). + Might have gone down asynchronously, so this is NOT + a protocol violation by CORE. Still count the event, + as this should be rare. */ + GNUNET_SERVICE_client_continue (tc->client); + GNUNET_STATISTICS_update (GST_stats, + "# messages dropped (neighbour unknown)", + 1, + GNUNET_NO); + return; + } + + pm = GNUNET_malloc (sizeof(struct PendingMessage) + bytes_msg); + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "1 created pm %p storing vl %p\n", + pm, + vl); + pm->logging_uuid = logging_uuid_gen++; + pm->prefs = pp; + pm->client = tc; + pm->vl = vl; + pm->bytes_msg = bytes_msg; + memcpy (&pm[1], obmm, bytes_msg); + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Sending message of type %u with %u bytes as <%llu> to %s\n", + ntohs (obmm->type), + bytes_msg, + pm->logging_uuid, + GNUNET_i2s (&obm->peer)); + GNUNET_CONTAINER_MDLL_insert (client, + tc->details.core.pending_msg_head, + tc->details.core.pending_msg_tail, + pm); + GNUNET_CONTAINER_MDLL_insert (vl, + vl->pending_msg_head, + vl->pending_msg_tail, + pm); + check_vl_transmission (vl); + GNUNET_SERVICE_client_continue (tc->client); +} + + +/** + * Communicator requests backchannel transmission. Process the request. + * Just repacks it into our `struct TransportBackchannelEncapsulationMessage *` + * (which for now has exactly the same format, only a different message type) + * and passes it on for routing. + * + * @param cls the client + * @param cb the send message that was sent + */ +static void +handle_communicator_backchannel ( + void *cls, + const struct GNUNET_TRANSPORT_CommunicatorBackchannel *cb) +{ + struct Neighbour *n; + struct VirtualLink *vl; + struct TransportClient *tc = cls; + const struct GNUNET_MessageHeader *inbox = + (const struct GNUNET_MessageHeader *) &cb[1]; + uint16_t isize = ntohs (inbox->size); + const char *is = ((const char *) &cb[1]) + isize; + size_t slen = strlen (is) + 1; + char + mbuf[slen + isize + + sizeof(struct + TransportBackchannelEncapsulationMessage)] GNUNET_ALIGN; + struct TransportBackchannelEncapsulationMessage *be = + (struct TransportBackchannelEncapsulationMessage *) mbuf; + + /* 0-termination of 'is' was checked already in + #check_communicator_backchannel() */ + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Preparing backchannel transmission to %s:%s of type %u and size %u\n", + GNUNET_i2s (&cb->pid), + is, + ntohs (inbox->type), + ntohs (inbox->size)); + /* encapsulate and encrypt message */ + be->header.type = + htons (GNUNET_MESSAGE_TYPE_TRANSPORT_BACKCHANNEL_ENCAPSULATION); + be->header.size = htons (sizeof(mbuf)); + memcpy (&be[1], inbox, isize); + memcpy (&mbuf[sizeof(struct TransportBackchannelEncapsulationMessage) + + isize], + is, + strlen (is) + 1); + // route_control_message_without_fc (&cb->pid, &be->header, RMO_DV_ALLOWED); + vl = lookup_virtual_link (&cb->pid); + if ((NULL != vl) && (GNUNET_YES == vl->confirmed)) + { + route_control_message_without_fc (vl, &be->header, RMO_DV_ALLOWED); + } + else + { + /* Use route via neighbour */ + n = lookup_neighbour (&cb->pid); + if (NULL != n) + route_via_neighbour ( + n, + &be->header, + RMO_NONE); + } + GNUNET_SERVICE_client_continue (tc->client); +} + + +/** + * Address of our peer added. Test message is well-formed. + * + * @param cls the client + * @param aam the send message that was sent + * @return #GNUNET_OK if message is well-formed + */ +static int +check_add_address (void *cls, + const struct GNUNET_TRANSPORT_AddAddressMessage *aam) +{ + struct TransportClient *tc = cls; + + if (CT_COMMUNICATOR != tc->type) + { + GNUNET_break (0); + return GNUNET_SYSERR; + } + GNUNET_MQ_check_zero_termination (aam); + return GNUNET_OK; +} + + +/** + * Ask peerstore to store our address. + * + * @param cls an `struct AddressListEntry *` + */ +static void +store_pi (void *cls); + + +/** + * Function called when peerstore is done storing our address. + * + * @param cls a `struct AddressListEntry` + * @param success #GNUNET_YES if peerstore was successful + */ +static void +peerstore_store_own_cb (void *cls, int success) +{ + struct AddressListEntry *ale = cls; + + ale->sc = NULL; + if (GNUNET_YES != success) + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "Failed to store our own address `%s' in peerstore!\n", + ale->address); + else + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Successfully stored our own address `%s' in peerstore!\n", + ale->address); + /* refresh period is 1/4 of expiration time, that should be plenty + without being excessive. */ + ale->st = + GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_relative_divide (ale->expiration, + 4ULL), + &store_pi, + ale); +} + + +static void +shc_cont (void *cls, int success) +{ + GNUNET_free (cls); +} + + +/** + * Ask peerstore to store our address. + * + * @param cls an `struct AddressListEntry *` + */ +static void +store_pi (void *cls) +{ + struct AddressListEntry *ale = cls; + struct GNUNET_PEERSTORE_StoreHelloContext *shc; + void *addr; + size_t addr_len; + struct GNUNET_TIME_Absolute expiration; + enum GNUNET_GenericReturnValue add_result; + struct GNUNET_MQ_Envelope *env; + const struct GNUNET_MessageHeader *msg; + const char *dash; + char *prefix = GNUNET_HELLO_address_to_prefix (ale->address); + char *address_uri; + + dash = strchr (ale->address, '-'); + dash++; + GNUNET_asprintf (&address_uri, + "%s://%s", + prefix, + dash); + ale->st = NULL; + expiration = GNUNET_TIME_relative_to_absolute (ale->expiration); + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Storing our address `%s' in peerstore until %s!\n", + ale->address, + GNUNET_STRINGS_absolute_time_to_string (expiration)); + add_result = GNUNET_HELLO_builder_add_address (GST_my_hello, + address_uri); + env = GNUNET_HELLO_builder_to_env (GST_my_hello, + GST_my_private_key, + GNUNET_TIME_UNIT_ZERO); + msg = GNUNET_MQ_env_get_msg (env); + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "store_pi 1\n"); + if (GNUNET_YES == add_result) + shc = GNUNET_PEERSTORE_hello_add (peerstore, + msg, + shc_cont, + shc); + else if (GNUNET_SYSERR == add_result) + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "Error adding address to peerstore hello!\n"); + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "store_pi 2\n"); + GNUNET_HELLO_sign_address (ale->address, + ale->nt, + hello_mono_time, + GST_my_private_key, + &addr, + &addr_len); + ale->sc = GNUNET_PEERSTORE_store (peerstore, + "transport", + &GST_my_identity, + GNUNET_PEERSTORE_TRANSPORT_HELLO_KEY, + addr, + addr_len, + expiration, + GNUNET_PEERSTORE_STOREOPTION_MULTIPLE, + &peerstore_store_own_cb, + ale); + GNUNET_free (addr); + GNUNET_free (env); + GNUNET_free (prefix); + GNUNET_free (address_uri); + if (NULL == ale->sc) + { + GNUNET_log (GNUNET_ERROR_TYPE_WARNING, + "Failed to store our address `%s' with peerstore\n", + ale->address); + ale->st = + GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_UNIT_SECONDS, &store_pi, ale); + } +} + + +/** + * Address of our peer added. Process the request. + * + * @param cls the client + * @param aam the send message that was sent + */ +static void +handle_add_address (void *cls, + const struct GNUNET_TRANSPORT_AddAddressMessage *aam) +{ + struct TransportClient *tc = cls; + struct AddressListEntry *ale; + size_t slen; + + /* 0-termination of &aam[1] was checked in #check_add_address */ + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Communicator added address `%s'!\n", + (const char *) &aam[1]); + slen = ntohs (aam->header.size) - sizeof(*aam); + ale = GNUNET_malloc (sizeof(struct AddressListEntry) + slen); + ale->tc = tc; + ale->address = (const char *) &ale[1]; + ale->expiration = GNUNET_TIME_relative_ntoh (aam->expiration); + ale->aid = aam->aid; + ale->nt = (enum GNUNET_NetworkType) ntohl (aam->nt); + memcpy (&ale[1], &aam[1], slen); + GNUNET_CONTAINER_DLL_insert (tc->details.communicator.addr_head, + tc->details.communicator.addr_tail, + ale); + ale->st = GNUNET_SCHEDULER_add_now (&store_pi, ale); + GNUNET_SERVICE_client_continue (tc->client); +} + + +/** + * Address of our peer deleted. Process the request. + * + * @param cls the client + * @param dam the send message that was sent + */ +static void +handle_del_address (void *cls, + const struct GNUNET_TRANSPORT_DelAddressMessage *dam) +{ + struct TransportClient *tc = cls; + struct AddressListEntry *alen; + + if (CT_COMMUNICATOR != tc->type) + { + GNUNET_break (0); + GNUNET_SERVICE_client_drop (tc->client); + return; + } + for (struct AddressListEntry *ale = tc->details.communicator.addr_head; + NULL != ale; + ale = alen) + { + alen = ale->next; + if (dam->aid != ale->aid) + continue; + GNUNET_assert (ale->tc == tc); + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Communicator deleted address `%s'!\n", + ale->address); + free_address_list_entry (ale); + GNUNET_SERVICE_client_continue (tc->client); + return; + } + GNUNET_log (GNUNET_ERROR_TYPE_WARNING, + "Communicator removed address we did not even have.\n"); + GNUNET_SERVICE_client_continue (tc->client); + // GNUNET_SERVICE_client_drop (tc->client); +} + + +/** + * Given an inbound message @a msg from a communicator @a cmc, + * demultiplex it based on the type calling the right handler. + * + * @param cmc context for demultiplexing + * @param msg message to demultiplex + */ +static void +demultiplex_with_cmc (struct CommunicatorMessageContext *cmc); + + +/** + * Function called when we are done giving a message of a certain + * size to CORE and should thus decrement the number of bytes of + * RAM reserved for that peer's MQ. + * + * @param cls a `struct CoreSentContext` + */ +static void +core_env_sent_cb (void *cls) +{ + struct CoreSentContext *ctx = cls; + struct VirtualLink *vl = ctx->vl; + + if (NULL == vl) + { + /* lost the link in the meantime, ignore */ + GNUNET_free (ctx); + return; + } + GNUNET_CONTAINER_DLL_remove (vl->csc_head, vl->csc_tail, ctx); + GNUNET_assert (vl->incoming_fc_window_size_ram >= ctx->size); + vl->incoming_fc_window_size_ram -= ctx->size; + vl->incoming_fc_window_size_used += ctx->isize; + consider_sending_fc (vl); + GNUNET_free (ctx); +} + + +static void +finish_handling_raw_message (struct VirtualLink *vl, + const struct GNUNET_MessageHeader *mh, + struct CommunicatorMessageContext *cmc, + unsigned int continue_client) +{ + uint16_t size = ntohs (mh->size); + int have_core; + + if (vl->incoming_fc_window_size_ram > UINT_MAX - size) + { + GNUNET_STATISTICS_update (GST_stats, + "# CORE messages dropped (FC arithmetic overflow)", + 1, + GNUNET_NO); + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "CORE messages of type %u with %u bytes dropped (FC arithmetic overflow)\n", + (unsigned int) ntohs (mh->type), + (unsigned int) ntohs (mh->size)); + finish_cmc_handling_with_continue (cmc, continue_client); + return; + } + if (vl->incoming_fc_window_size_ram + size > vl->available_fc_window_size) + { + GNUNET_STATISTICS_update (GST_stats, + "# CORE messages dropped (FC window overflow)", + 1, + GNUNET_NO); + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "CORE messages of type %u with %u bytes dropped (FC window overflow)\n", + (unsigned int) ntohs (mh->type), + (unsigned int) ntohs (mh->size)); + finish_cmc_handling_with_continue (cmc, continue_client); + return; + } + + /* Forward to all CORE clients */ + have_core = GNUNET_NO; + for (struct TransportClient *tc = clients_head; NULL != tc; tc = tc->next) + { + struct GNUNET_MQ_Envelope *env; + struct InboundMessage *im; + struct CoreSentContext *ctx; + + if (CT_CORE != tc->type) + continue; + vl->incoming_fc_window_size_ram += size; + env = GNUNET_MQ_msg_extra (im, size, GNUNET_MESSAGE_TYPE_TRANSPORT_RECV); + ctx = GNUNET_new (struct CoreSentContext); + ctx->vl = vl; + ctx->size = size; + ctx->isize = (GNUNET_NO == have_core) ? size : 0; + have_core = GNUNET_YES; + GNUNET_CONTAINER_DLL_insert (vl->csc_head, vl->csc_tail, ctx); + GNUNET_MQ_notify_sent (env, &core_env_sent_cb, ctx); + im->peer = cmc->im.sender; + memcpy (&im[1], mh, size); + GNUNET_MQ_send (tc->mq, env); + vl->core_recv_window--; + } + if (GNUNET_NO == have_core) + { + GNUNET_log (GNUNET_ERROR_TYPE_WARNING, + "Dropped message to CORE: no CORE client connected!\n"); + /* Nevertheless, count window as used, as it is from the + perspective of the other peer! */ + vl->incoming_fc_window_size_used += size; + /* TODO-M1 */ + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Dropped message of type %u with %u bytes to CORE: no CORE client connected!\n", + (unsigned int) ntohs (mh->type), + (unsigned int) ntohs (mh->size)); + finish_cmc_handling_with_continue (cmc, continue_client); + return; + } + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Delivered message from %s of type %u to CORE recv window %d\n", + GNUNET_i2s (&cmc->im.sender), + ntohs (mh->type), + vl->core_recv_window); + if (vl->core_recv_window > 0) + { + finish_cmc_handling_with_continue (cmc, continue_client); + return; + } + /* Wait with calling #finish_cmc_handling(cmc) until the message + was processed by CORE MQs (for CORE flow control)! */ + GNUNET_CONTAINER_DLL_insert (vl->cmc_head, vl->cmc_tail, cmc); +} + + +/** + * Communicator gave us an unencapsulated message to pass as-is to + * CORE. Process the request. + * + * @param cls a `struct CommunicatorMessageContext` (must call + * #finish_cmc_handling() when done) + * @param mh the message that was received + */ +static void +handle_raw_message (void *cls, const struct GNUNET_MessageHeader *mh) +{ + struct CommunicatorMessageContext *cmc = cls; + // struct CommunicatorMessageContext *cmc_copy = + // GNUNET_new (struct CommunicatorMessageContext); + struct GNUNET_MessageHeader *mh_copy; + struct RingBufferEntry *rbe; + struct VirtualLink *vl; + uint16_t size = ntohs (mh->size); + + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Handling raw message of type %u with %u bytes\n", + (unsigned int) ntohs (mh->type), + (unsigned int) ntohs (mh->size)); + + if ((size > UINT16_MAX - sizeof(struct InboundMessage)) || + (size < sizeof(struct GNUNET_MessageHeader))) + { + struct GNUNET_SERVICE_Client *client = cmc->tc->client; + + GNUNET_break (0); + finish_cmc_handling (cmc); + GNUNET_SERVICE_client_drop (client); + return; + } + vl = lookup_virtual_link (&cmc->im.sender); + if ((NULL == vl) || (GNUNET_NO == vl->confirmed)) + { + /* FIXME: sender is giving us messages for CORE but we don't have + the link up yet! I *suspect* this can happen right now (i.e. + sender has verified us, but we didn't verify sender), but if + we pass this on, CORE would be confused (link down, messages + arrive). We should investigate more if this happens often, + or in a persistent manner, and possibly do "something" about + it. Thus logging as error for now. */ + + mh_copy = GNUNET_malloc (size); + rbe = GNUNET_new (struct RingBufferEntry); + rbe->cmc = cmc; + /*cmc_copy->tc = cmc->tc; + cmc_copy->im = cmc->im;*/ + GNUNET_memcpy (mh_copy, mh, size); + + rbe->mh = mh_copy; + + ring_buffer[ring_buffer_head] = rbe;// cmc_copy; + // cmc_copy->mh = (const struct GNUNET_MessageHeader *) mh_copy; + cmc->mh = (const struct GNUNET_MessageHeader *) mh_copy; + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Storing message for %s and type %u (%u) in ring buffer\n", + GNUNET_i2s (&cmc->im.sender), + (unsigned int) ntohs (mh->type), + (unsigned int) ntohs (mh_copy->type)); + if (RING_BUFFER_SIZE - 1 == ring_buffer_head) + { + ring_buffer_head = 0; + is_ring_buffer_full = GNUNET_YES; + } + else + ring_buffer_head++; + + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "%u items stored in ring buffer\n", + ring_buffer_head); + + /*GNUNET_break_op (0); + GNUNET_STATISTICS_update (GST_stats, + "# CORE messages dropped (virtual link still down)", + 1, + GNUNET_NO); + + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "CORE messages of type %u with %u bytes dropped (virtual link still down)\n", + (unsigned int) ntohs (mh->type), + (unsigned int) ntohs (mh->size)); + finish_cmc_handling (cmc);*/ + GNUNET_SERVICE_client_continue (cmc->tc->client); + // GNUNET_free (cmc); + return; + } + finish_handling_raw_message (vl, mh, cmc, GNUNET_YES); +} + + +/** + * Communicator gave us a fragment box. Check the message. + * + * @param cls a `struct CommunicatorMessageContext` + * @param fb the send message that was sent + * @return #GNUNET_YES if message is well-formed + */ +static int +check_fragment_box (void *cls, const struct TransportFragmentBoxMessage *fb) +{ + uint16_t size = ntohs (fb->header.size); + uint16_t bsize = size - sizeof(*fb); + + (void) cls; + if (0 == bsize) + { + GNUNET_break_op (0); + return GNUNET_SYSERR; + } + if (bsize + ntohs (fb->frag_off) > ntohs (fb->msg_size)) + { + GNUNET_break_op (0); + return GNUNET_SYSERR; + } + if (ntohs (fb->frag_off) >= ntohs (fb->msg_size)) + { + GNUNET_break_op (0); + return GNUNET_SYSERR; + } + return GNUNET_YES; +} + + +/** + * Clean up an idle cumulative acknowledgement data structure. + * + * @param cls a `struct AcknowledgementCummulator *` + */ +static void +destroy_ack_cummulator (void *cls) +{ + struct AcknowledgementCummulator *ac = cls; + + ac->task = NULL; + GNUNET_assert (0 == ac->num_acks); + GNUNET_assert ( + GNUNET_YES == + GNUNET_CONTAINER_multipeermap_remove (ack_cummulators, &ac->target, ac)); + GNUNET_free (ac); +} + + +/** + * Do the transmission of a cumulative acknowledgement now. + * + * @param cls a `struct AcknowledgementCummulator *` + */ +static void +transmit_cummulative_ack_cb (void *cls) +{ + struct Neighbour *n; + struct VirtualLink *vl; + struct AcknowledgementCummulator *ac = cls; + char buf[sizeof(struct TransportReliabilityAckMessage) + + ac->num_acks + * sizeof(struct TransportCummulativeAckPayloadP)] GNUNET_ALIGN; + struct TransportReliabilityAckMessage *ack = + (struct TransportReliabilityAckMessage *) buf; + struct TransportCummulativeAckPayloadP *ap; + + ac->task = NULL; + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Sending ACK with %u components to %s\n", + ac->num_acks, + GNUNET_i2s (&ac->target)); + GNUNET_assert (0 < ac->num_acks); + ack->header.type = htons (GNUNET_MESSAGE_TYPE_TRANSPORT_RELIABILITY_ACK); + ack->header.size = + htons (sizeof(*ack) + + ac->num_acks * sizeof(struct TransportCummulativeAckPayloadP)); + ack->ack_counter = htonl (ac->ack_counter += ac->num_acks); + ap = (struct TransportCummulativeAckPayloadP *) &ack[1]; + for (unsigned int i = 0; i < ac->num_acks; i++) + { + ap[i].ack_uuid = ac->ack_uuids[i].ack_uuid; + ap[i].ack_delay = GNUNET_TIME_relative_hton ( + GNUNET_TIME_absolute_get_duration (ac->ack_uuids[i].receive_time)); + } + /*route_control_message_without_fc ( + &ac->target, + &ack->header, + RMO_DV_ALLOWED);*/ + vl = lookup_virtual_link (&ac->target); + if ((NULL != vl) && (GNUNET_YES == vl->confirmed)) + { + route_control_message_without_fc ( + vl, + &ack->header, + RMO_DV_ALLOWED); + } + else + { + /* Use route via neighbour */ + n = lookup_neighbour (&ac->target); + if (NULL != n) + route_via_neighbour ( + n, + &ack->header, + RMO_NONE); + } + ac->num_acks = 0; + ac->task = GNUNET_SCHEDULER_add_delayed (ACK_CUMMULATOR_TIMEOUT, + &destroy_ack_cummulator, + ac); +} + + +/** + * Transmit an acknowledgement for @a ack_uuid to @a pid delaying + * transmission by at most @a ack_delay. + * + * @param pid target peer + * @param ack_uuid UUID to ack + * @param max_delay how long can the ACK wait + */ +static void +cummulative_ack (const struct GNUNET_PeerIdentity *pid, + const struct AcknowledgementUUIDP *ack_uuid, + struct GNUNET_TIME_Absolute max_delay) +{ + struct AcknowledgementCummulator *ac; + + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Scheduling ACK %s for transmission to %s\n", + GNUNET_uuid2s (&ack_uuid->value), + GNUNET_i2s (pid)); + ac = GNUNET_CONTAINER_multipeermap_get (ack_cummulators, pid); + if (NULL == ac) + { + ac = GNUNET_new (struct AcknowledgementCummulator); + ac->target = *pid; + ac->min_transmission_time = max_delay; + GNUNET_assert (GNUNET_YES == + GNUNET_CONTAINER_multipeermap_put ( + ack_cummulators, + &ac->target, + ac, + GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_ONLY)); + } + else + { + if (MAX_CUMMULATIVE_ACKS == ac->num_acks) + { + /* must run immediately, ack buffer full! */ + transmit_cummulative_ack_cb (ac); + } + GNUNET_SCHEDULER_cancel (ac->task); + ac->min_transmission_time = + GNUNET_TIME_absolute_min (ac->min_transmission_time, max_delay); + } + GNUNET_assert (ac->num_acks < MAX_CUMMULATIVE_ACKS); + ac->ack_uuids[ac->num_acks].receive_time = GNUNET_TIME_absolute_get (); + ac->ack_uuids[ac->num_acks].ack_uuid = *ack_uuid; + ac->num_acks++; + ac->task = GNUNET_SCHEDULER_add_at (ac->min_transmission_time, + &transmit_cummulative_ack_cb, + ac); +} + + +/** + * Closure for #find_by_message_uuid. + */ +struct FindByMessageUuidContext +{ + /** + * UUID to look for. + */ + struct MessageUUIDP message_uuid; + + /** + * Set to the reassembly context if found. + */ + struct ReassemblyContext *rc; +}; + + +/** + * Iterator called to find a reassembly context by the message UUID in the + * multihashmap32. + * + * @param cls a `struct FindByMessageUuidContext` + * @param key a key (unused) + * @param value a `struct ReassemblyContext` + * @return #GNUNET_YES if not found, #GNUNET_NO if found + */ +static int +find_by_message_uuid (void *cls, uint32_t key, void *value) +{ + struct FindByMessageUuidContext *fc = cls; + struct ReassemblyContext *rc = value; + + (void) key; + if (0 == GNUNET_memcmp (&fc->message_uuid, &rc->msg_uuid)) + { + fc->rc = rc; + return GNUNET_NO; + } + return GNUNET_YES; +} + + +/** + * Communicator gave us a fragment. Process the request. + * + * @param cls a `struct CommunicatorMessageContext` (must call + * #finish_cmc_handling() when done) + * @param fb the message that was received + */ +static void +handle_fragment_box (void *cls, const struct TransportFragmentBoxMessage *fb) +{ + struct CommunicatorMessageContext *cmc = cls; + struct VirtualLink *vl; + struct ReassemblyContext *rc; + const struct GNUNET_MessageHeader *msg; + uint16_t msize; + uint16_t fsize; + uint16_t frag_off; + char *target; + struct GNUNET_TIME_Relative cdelay; + struct FindByMessageUuidContext fc; + + vl = lookup_virtual_link (&cmc->im.sender); + if ((NULL == vl) || (GNUNET_NO == vl->confirmed)) + { + struct GNUNET_SERVICE_Client *client = cmc->tc->client; + + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "No virtual link for %s to handle fragment\n", + GNUNET_i2s (&cmc->im.sender)); + GNUNET_break (0); + finish_cmc_handling (cmc); + GNUNET_SERVICE_client_drop (client); + return; + } + if (NULL == vl->reassembly_map) + { + vl->reassembly_map = GNUNET_CONTAINER_multihashmap32_create (8); + vl->reassembly_heap = + GNUNET_CONTAINER_heap_create (GNUNET_CONTAINER_HEAP_ORDER_MIN); + vl->reassembly_timeout_task = + GNUNET_SCHEDULER_add_delayed (REASSEMBLY_EXPIRATION, + &reassembly_cleanup_task, + vl); + } + msize = ntohs (fb->msg_size); + fc.message_uuid = fb->msg_uuid; + fc.rc = NULL; + (void) GNUNET_CONTAINER_multihashmap32_get_multiple (vl->reassembly_map, + fb->msg_uuid.uuid, + &find_by_message_uuid, + &fc); + fsize = ntohs (fb->header.size) - sizeof(*fb); + if (NULL == (rc = fc.rc)) + { + rc = GNUNET_malloc (sizeof(*rc) + msize /* reassembly payload buffer */ + + (msize + 7) / 8 * sizeof(uint8_t) /* bitfield */); + rc->msg_uuid = fb->msg_uuid; + rc->virtual_link = vl; + rc->msg_size = msize; + rc->reassembly_timeout = + GNUNET_TIME_relative_to_absolute (REASSEMBLY_EXPIRATION); + rc->last_frag = GNUNET_TIME_absolute_get (); + rc->hn = GNUNET_CONTAINER_heap_insert (vl->reassembly_heap, + rc, + rc->reassembly_timeout.abs_value_us); + GNUNET_assert (GNUNET_OK == + GNUNET_CONTAINER_multihashmap32_put ( + vl->reassembly_map, + rc->msg_uuid.uuid, + rc, + GNUNET_CONTAINER_MULTIHASHMAPOPTION_MULTIPLE)); + target = (char *) &rc[1]; + rc->bitfield = (uint8_t *) (target + rc->msg_size); + if (fsize != rc->msg_size) + rc->msg_missing = rc->msg_size; + else + rc->msg_missing = 0; + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Received fragment with size %u at offset %u/%u %u bytes missing from %s for NEW message %u\n", + fsize, + ntohs (fb->frag_off), + msize, + rc->msg_missing, + GNUNET_i2s (&cmc->im.sender), + (unsigned int) fb->msg_uuid.uuid); + } + else + { + target = (char *) &rc[1]; + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Received fragment at offset %u/%u from %s for message %u\n", + ntohs (fb->frag_off), + msize, + GNUNET_i2s (&cmc->im.sender), + (unsigned int) fb->msg_uuid.uuid); + } + if (msize != rc->msg_size) + { + GNUNET_break (0); + finish_cmc_handling (cmc); + return; + } + + /* reassemble */ + if (0 == fsize) + { + GNUNET_break (0); + finish_cmc_handling (cmc); + return; + } + frag_off = ntohs (fb->frag_off); + if (frag_off + fsize > msize) + { + /* Fragment (plus fragment size) exceeds message size! */ + GNUNET_break_op (0); + finish_cmc_handling (cmc); + return; + } + memcpy (&target[frag_off], &fb[1], fsize); + /* update bitfield and msg_missing */ + for (unsigned int i = frag_off; i < frag_off + fsize; i++) + { + if (0 == (rc->bitfield[i / 8] & (1 << (i % 8)))) + { + rc->bitfield[i / 8] |= (1 << (i % 8)); + rc->msg_missing--; + } + } + + /* Compute cumulative ACK */ + cdelay = GNUNET_TIME_absolute_get_duration (rc->last_frag); + cdelay = GNUNET_TIME_relative_multiply (cdelay, rc->msg_missing / fsize); + if (0 == rc->msg_missing) + cdelay = GNUNET_TIME_UNIT_ZERO; + cummulative_ack (&cmc->im.sender, + &fb->ack_uuid, + GNUNET_TIME_relative_to_absolute (cdelay)); + rc->last_frag = GNUNET_TIME_absolute_get (); + /* is reassembly complete? */ + if (0 != rc->msg_missing) + { + finish_cmc_handling (cmc); + return; + } + /* reassembly is complete, verify result */ + msg = (const struct GNUNET_MessageHeader *) &rc[1]; + if (ntohs (msg->size) != rc->msg_size) + { + GNUNET_break (0); + free_reassembly_context (rc); + finish_cmc_handling (cmc); + return; + } + /* successful reassembly */ + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Fragment reassembly complete for message %u\n", + (unsigned int) fb->msg_uuid.uuid); + /* FIXME: check that the resulting msg is NOT a + DV Box or Reliability Box, as that is NOT allowed! */ + cmc->mh = msg; + demultiplex_with_cmc (cmc); + /* FIXME-OPTIMIZE: really free here? Might be bad if fragments are still + en-route and we forget that we finished this reassembly immediately! + -> keep around until timeout? + -> shorten timeout based on ACK? */ + free_reassembly_context (rc); +} + + +/** + * Communicator gave us a reliability box. Check the message. + * + * @param cls a `struct CommunicatorMessageContext` + * @param rb the send message that was sent + * @return #GNUNET_YES if message is well-formed + */ +static int +check_reliability_box (void *cls, + const struct TransportReliabilityBoxMessage *rb) +{ + (void) cls; + const struct GNUNET_MessageHeader *inbox = (const struct + GNUNET_MessageHeader *) &rb[1]; + + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "check_send_msg with size %u: inner msg type %u and size %u (%lu %lu)\n", + ntohs (rb->header.size), + ntohs (inbox->type), + ntohs (inbox->size), + sizeof (struct TransportReliabilityBoxMessage), + sizeof (struct GNUNET_MessageHeader)); + GNUNET_MQ_check_boxed_message (rb); + return GNUNET_YES; +} + + +/** + * Communicator gave us a reliability box. Process the request. + * + * @param cls a `struct CommunicatorMessageContext` (must call + * #finish_cmc_handling() when done) + * @param rb the message that was received + */ +static void +handle_reliability_box (void *cls, + const struct TransportReliabilityBoxMessage *rb) +{ + struct CommunicatorMessageContext *cmc = cls; + const struct GNUNET_MessageHeader *inbox = + (const struct GNUNET_MessageHeader *) &rb[1]; + struct GNUNET_TIME_Relative rtt; + + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Received reliability box from %s with UUID %s of type %u\n", + GNUNET_i2s (&cmc->im.sender), + GNUNET_uuid2s (&rb->ack_uuid.value), + (unsigned int) ntohs (inbox->type)); + rtt = GNUNET_TIME_UNIT_SECONDS; /* FIXME: should base this on "RTT", but we + do not really have an RTT for the + * incoming* queue (should we have + the sender add it to the rb message?) */ + cummulative_ack ( + &cmc->im.sender, + &rb->ack_uuid, + (0 == ntohl (rb->ack_countdown)) + ? GNUNET_TIME_UNIT_ZERO_ABS + : GNUNET_TIME_relative_to_absolute ( + GNUNET_TIME_relative_divide (rtt, 8 /* FIXME: magic constant */))); + /* continue with inner message */ + /* FIXME: check that inbox is NOT a DV Box, fragment or another + reliability box (not allowed!) */ + cmc->mh = inbox; + demultiplex_with_cmc (cmc); +} + + +/** + * Check if we have advanced to another age since the last time. If + * so, purge ancient statistics (more than GOODPUT_AGING_SLOTS before + * the current age) + * + * @param[in,out] pd data to update + * @param age current age + */ +static void +update_pd_age (struct PerformanceData *pd, unsigned int age) +{ + unsigned int sage; + + if (age == pd->last_age) + return; /* nothing to do */ + sage = GNUNET_MAX (pd->last_age, age - 2 * GOODPUT_AGING_SLOTS); + for (unsigned int i = sage; i <= age - GOODPUT_AGING_SLOTS; i++) + { + struct TransmissionHistoryEntry *the = &pd->the[i % GOODPUT_AGING_SLOTS]; + + the->bytes_sent = 0; + the->bytes_received = 0; + } + pd->last_age = age; +} + + +/** + * Update @a pd based on the latest @a rtt and the number of bytes + * that were confirmed to be successfully transmitted. + * + * @param[in,out] pd data to update + * @param rtt latest round-trip time + * @param bytes_transmitted_ok number of bytes receiver confirmed as received + */ +static void +update_performance_data (struct PerformanceData *pd, + struct GNUNET_TIME_Relative rtt, + uint16_t bytes_transmitted_ok) +{ + uint64_t nval = rtt.rel_value_us; + uint64_t oval = pd->aged_rtt.rel_value_us; + unsigned int age = get_age (); + struct TransmissionHistoryEntry *the = &pd->the[age % GOODPUT_AGING_SLOTS]; + + if (oval == GNUNET_TIME_UNIT_FOREVER_REL.rel_value_us) + pd->aged_rtt = rtt; + else + pd->aged_rtt.rel_value_us = (nval + 7 * oval) / 8; + update_pd_age (pd, age); + the->bytes_received += bytes_transmitted_ok; +} + + +/** + * We have successfully transmitted data via @a q, update metrics. + * + * @param q queue to update + * @param rtt round trip time observed + * @param bytes_transmitted_ok number of bytes successfully transmitted + */ +static void +update_queue_performance (struct Queue *q, + struct GNUNET_TIME_Relative rtt, + uint16_t bytes_transmitted_ok) +{ + update_performance_data (&q->pd, rtt, bytes_transmitted_ok); +} + + +/** + * We have successfully transmitted data via @a dvh, update metrics. + * + * @param dvh distance vector path data to update + * @param rtt round trip time observed + * @param bytes_transmitted_ok number of bytes successfully transmitted + */ +static void +update_dvh_performance (struct DistanceVectorHop *dvh, + struct GNUNET_TIME_Relative rtt, + uint16_t bytes_transmitted_ok) +{ + update_performance_data (&dvh->pd, rtt, bytes_transmitted_ok); +} + + +/** + * We have completed transmission of @a pm, remove it from + * the transmission queues (and if it is a fragment, continue + * up the tree as necessary). + * + * @param pm pending message that was transmitted + */ +static void +completed_pending_message (struct PendingMessage *pm) +{ + struct PendingMessage *pos; + + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Complete transmission of message %llu %u\n", + pm->logging_uuid, + pm->pmt); + switch (pm->pmt) + { + case PMT_CORE: + case PMT_RELIABILITY_BOX: + /* Full message sent, we are done */ + client_send_response (pm); + return; + + case PMT_FRAGMENT_BOX: + /* Fragment sent over reliable channel */ + pos = pm->frag_parent; + GNUNET_CONTAINER_MDLL_remove (frag, pos->head_frag, pos->tail_frag, pm); + free_pending_message (pm); + /* check if subtree is done */ + while ((NULL == pos->head_frag) && (pos->frag_off == (pos->bytes_msg + - sizeof(struct + TransportFragmentBoxMessage))) + && + (NULL != pos->frag_parent)) + { + pm = pos; + pos = pm->frag_parent; + if ((NULL == pos) && (PMT_DV_BOX == pm->pmt)) + { + client_send_response (pm); + return; + } + else if (PMT_DV_BOX == pm->pmt) + { + client_send_response (pos); + return; + } + GNUNET_CONTAINER_MDLL_remove (frag, pos->head_frag, pos->tail_frag, pm); + free_pending_message (pm); + } + + /* Was this the last applicable fragment? */ + if ((NULL == pos->head_frag) && (NULL == pos->frag_parent) && + (pos->frag_off == pos->bytes_msg)) + client_send_response (pos); + return; + + case PMT_DV_BOX: + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Completed transmission of message %llu (DV Box)\n", + pm->logging_uuid); + if (NULL != pm->frag_parent) + { + if (NULL != pm->bpm) + { + GNUNET_free (pm->bpm); + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Freed bpm\n"); + } + pos = pm->frag_parent; + free_pending_message (pm); + pos->bpm = NULL; + client_send_response (pos); + } + else + client_send_response (pm); + return; + } +} + + +/** + * The @a pa was acknowledged, process the acknowledgement. + * + * @param pa the pending acknowledgement that was satisfied + * @param ack_delay artificial delay from cumulative acks created by the + * other peer + */ +static void +handle_acknowledged (struct PendingAcknowledgement *pa, + struct GNUNET_TIME_Relative ack_delay) +{ + struct GNUNET_TIME_Relative delay; + + delay = GNUNET_TIME_absolute_get_duration (pa->transmission_time); + delay = GNUNET_TIME_relative_subtract (delay, ack_delay); + if (NULL != pa->queue && 1 == pa->num_send) + update_queue_performance (pa->queue, delay, pa->message_size); + if (NULL != pa->dvh && 1 == pa->num_send) + update_dvh_performance (pa->dvh, delay, pa->message_size); + if (NULL != pa->pm) + completed_pending_message (pa->pm); + free_pending_acknowledgement (pa); +} + + +/** + * Communicator gave us a reliability ack. Check it is well-formed. + * + * @param cls a `struct CommunicatorMessageContext` (unused) + * @param ra the message that was received + * @return #GNUNET_Ok if @a ra is well-formed + */ +static int +check_reliability_ack (void *cls, + const struct TransportReliabilityAckMessage *ra) +{ + unsigned int n_acks; + + (void) cls; + n_acks = (ntohs (ra->header.size) - sizeof(*ra)) + / sizeof(struct TransportCummulativeAckPayloadP); + if (0 == n_acks) + { + GNUNET_break_op (0); + return GNUNET_SYSERR; + } + if ((ntohs (ra->header.size) - sizeof(*ra)) != + n_acks * sizeof(struct TransportCummulativeAckPayloadP)) + { + GNUNET_break_op (0); + return GNUNET_SYSERR; + } + return GNUNET_OK; +} + + +/** + * Communicator gave us a reliability ack. Process the request. + * + * @param cls a `struct CommunicatorMessageContext` (must call + * #finish_cmc_handling() when done) + * @param ra the message that was received + */ +static void +handle_reliability_ack (void *cls, + const struct TransportReliabilityAckMessage *ra) +{ + struct CommunicatorMessageContext *cmc = cls; + const struct TransportCummulativeAckPayloadP *ack; + unsigned int n_acks; + uint32_t ack_counter; + + n_acks = (ntohs (ra->header.size) - sizeof(*ra)) + / sizeof(struct TransportCummulativeAckPayloadP); + ack = (const struct TransportCummulativeAckPayloadP *) &ra[1]; + for (unsigned int i = 0; i < n_acks; i++) + { + struct PendingAcknowledgement *pa = + GNUNET_CONTAINER_multiuuidmap_get (pending_acks, &ack[i].ack_uuid.value); + if (NULL == pa) + { + GNUNET_log (GNUNET_ERROR_TYPE_INFO, + "Received ACK from %s with UUID %s which is unknown to us!\n", + GNUNET_i2s (&cmc->im.sender), + GNUNET_uuid2s (&ack[i].ack_uuid.value)); + GNUNET_STATISTICS_update ( + GST_stats, + "# FRAGMENT_ACKS dropped, no matching pending message", + 1, + GNUNET_NO); + continue; + } + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Received ACK from %s with UUID %s\n", + GNUNET_i2s (&cmc->im.sender), + GNUNET_uuid2s (&ack[i].ack_uuid.value)); + handle_acknowledged (pa, GNUNET_TIME_relative_ntoh (ack[i].ack_delay)); + } + + ack_counter = htonl (ra->ack_counter); + (void) ack_counter; /* silence compiler warning for now */ + // FIXME-OPTIMIZE: track ACK losses based on ack_counter somewhere! + // (DV and/or Neighbour?) + finish_cmc_handling (cmc); +} + + +/** + * Communicator gave us a backchannel encapsulation. Check the message. + * + * @param cls a `struct CommunicatorMessageContext` + * @param be the send message that was sent + * @return #GNUNET_YES if message is well-formed + */ +static int +check_backchannel_encapsulation ( + void *cls, + const struct TransportBackchannelEncapsulationMessage *be) +{ + uint16_t size = ntohs (be->header.size) - sizeof(*be); + const struct GNUNET_MessageHeader *inbox = + (const struct GNUNET_MessageHeader *) &be[1]; + const char *is; + uint16_t isize; + + (void) cls; + if (ntohs (inbox->size) >= size) + { + GNUNET_break_op (0); + return GNUNET_SYSERR; + } + isize = ntohs (inbox->size); + is = ((const char *) inbox) + isize; + size -= isize; + if ('\0' != is[size - 1]) + { + GNUNET_break_op (0); + return GNUNET_SYSERR; + } + return GNUNET_YES; +} + + +/** + * Communicator gave us a backchannel encapsulation. Process the request. + * (We are the destination of the backchannel here.) + * + * @param cls a `struct CommunicatorMessageContext` (must call + * #finish_cmc_handling() when done) + * @param be the message that was received + */ +static void +handle_backchannel_encapsulation ( + void *cls, + const struct TransportBackchannelEncapsulationMessage *be) +{ + struct CommunicatorMessageContext *cmc = cls; + struct GNUNET_TRANSPORT_CommunicatorBackchannelIncoming *cbi; + struct GNUNET_MQ_Envelope *env; + struct TransportClient *tc; + const struct GNUNET_MessageHeader *inbox = + (const struct GNUNET_MessageHeader *) &be[1]; + uint16_t isize = ntohs (inbox->size); + const char *target_communicator = ((const char *) inbox) + isize; + char *sender; + char *self; + + GNUNET_asprintf (&sender, + "%s", + GNUNET_i2s (&cmc->im.sender)); + GNUNET_asprintf (&self, + "%s", + GNUNET_i2s (&GST_my_identity)); + + /* Find client providing this communicator */ + for (tc = clients_head; NULL != tc; tc = tc->next) + if ((CT_COMMUNICATOR == tc->type) && + (0 == + strcmp (tc->details.communicator.address_prefix, target_communicator))) + break; + if (NULL == tc) + { + char *stastr; + + GNUNET_asprintf ( + &stastr, + "# Backchannel message dropped: target communicator `%s' unknown", + target_communicator); + GNUNET_STATISTICS_update (GST_stats, stastr, 1, GNUNET_NO); + GNUNET_free (stastr); + finish_cmc_handling (cmc); + return; + } + /* Finally, deliver backchannel message to communicator */ + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Delivering backchannel message from %s to %s of type %u to %s\n", + sender, + self, + ntohs (inbox->type), + target_communicator); + env = GNUNET_MQ_msg_extra ( + cbi, + isize, + GNUNET_MESSAGE_TYPE_TRANSPORT_COMMUNICATOR_BACKCHANNEL_INCOMING); + cbi->pid = cmc->im.sender; + memcpy (&cbi[1], inbox, isize); + GNUNET_MQ_send (tc->mq, env); + finish_cmc_handling (cmc); +} + + +/** + * Task called when we should check if any of the DV paths + * we have learned to a target are due for garbage collection. + * + * Collects stale paths, and possibly frees the entire DV + * entry if no paths are left. Otherwise re-schedules itself. + * + * @param cls a `struct DistanceVector` + */ +static void +path_cleanup_cb (void *cls) +{ + struct DistanceVector *dv = cls; + struct DistanceVectorHop *pos; + + dv->timeout_task = NULL; + while (NULL != (pos = dv->dv_head)) + { + GNUNET_assert (dv == pos->dv); + if (GNUNET_TIME_absolute_get_remaining (pos->timeout).rel_value_us > 0) + break; + free_distance_vector_hop (pos); + } + if (NULL == pos) + { + free_dv_route (dv); + return; + } + dv->timeout_task = + GNUNET_SCHEDULER_add_at (pos->timeout, &path_cleanup_cb, dv); +} + + +static void +send_msg_from_cache (struct VirtualLink *vl) +{ + + const struct GNUNET_PeerIdentity target = vl->target; + + + if ((GNUNET_YES == is_ring_buffer_full) || (0 < ring_buffer_head)) + { + struct RingBufferEntry *ring_buffer_copy[RING_BUFFER_SIZE]; + unsigned int tail = GNUNET_YES == is_ring_buffer_full ? ring_buffer_head : + 0; + unsigned int head = GNUNET_YES == is_ring_buffer_full ? RING_BUFFER_SIZE : + ring_buffer_head; + struct GNUNET_TRANSPORT_IncomingMessage im; + struct CommunicatorMessageContext *cmc; + struct RingBufferEntry *rbe; + struct GNUNET_MessageHeader *mh; + + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Sending from ring buffer, which has %u items\n", + ring_buffer_head); + + ring_buffer_head = 0; + for (unsigned int i = 0; i < head; i++) + { + rbe = ring_buffer[(i + tail) % RING_BUFFER_SIZE]; + cmc = rbe->cmc; + mh = rbe->mh; + + im = cmc->im; + // mh = cmc->mh; + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Sending to ring buffer target %s using vl target %s\n", + GNUNET_i2s (&im.sender), + GNUNET_i2s2 (&target)); + if (0 == GNUNET_memcmp (&target, &im.sender)) + { + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Finish handling message of type %u and size %u\n", + (unsigned int) ntohs (mh->type), + (unsigned int) ntohs (mh->size)); + finish_handling_raw_message (vl, mh, cmc, GNUNET_NO); + GNUNET_free (mh); + } + else + { + ring_buffer_copy[i] = rbe; + ring_buffer_head++; + } + } + + if ((GNUNET_YES == is_ring_buffer_full) && (RING_BUFFER_SIZE - 1 > + ring_buffer_head)) + { + is_ring_buffer_full = GNUNET_NO; + } + + for (unsigned int i = 0; i < ring_buffer_head; i++) + { + ring_buffer[i] = ring_buffer_copy[i]; + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "ring_buffer_copy[i]->mh->type for i %u %u\n", + i, + ring_buffer_copy[i]->mh->type); + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "ring_buffer[i]->mh->type for i %u %u\n", + i, + ring_buffer[i]->mh->type); + } + + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "%u items still in ring buffer\n", + ring_buffer_head); + } + + if ((GNUNET_YES == is_ring_buffer_full) || (0 < ring_buffer_dv_head)) + { + struct PendingMessage *ring_buffer_dv_copy[RING_BUFFER_SIZE]; + struct PendingMessage *pm; + unsigned int tail = GNUNET_YES == is_ring_buffer_dv_full ? + ring_buffer_dv_head : + 0; + unsigned int head = GNUNET_YES == is_ring_buffer_dv_full ? + RING_BUFFER_SIZE : + ring_buffer_dv_head; + + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Sending from ring buffer dv, which has %u items\n", + ring_buffer_dv_head); + + ring_buffer_dv_head = 0; + for (unsigned int i = 0; i < head; i++) + { + pm = ring_buffer_dv[(i + tail) % RING_BUFFER_SIZE]; + + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Sending to ring buffer target %s using vl target %s\n", + GNUNET_i2s (&pm->target), + GNUNET_i2s2 (&target)); + if (0 == GNUNET_memcmp (&target, &pm->target)) + { + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Adding PendingMessage to vl, checking transmission.\n"); + pm->vl = vl; + GNUNET_CONTAINER_MDLL_insert (vl, + vl->pending_msg_head, + vl->pending_msg_tail, + pm); + + check_vl_transmission (vl); + } + else + { + ring_buffer_dv_copy[i] = pm; + ring_buffer_dv_head++; + } + } + + if (is_ring_buffer_dv_full && (RING_BUFFER_SIZE - 1 > ring_buffer_dv_head)) + { + is_ring_buffer_dv_full = GNUNET_NO; + } + + for (unsigned int i = 0; i < ring_buffer_dv_head; i++) + ring_buffer_dv[i] = ring_buffer_dv_copy[i]; + + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "%u items still in ring buffer dv.\n", + ring_buffer_dv_head); + + } +} + + +/** + * The @a hop is a validated path to the respective target + * peer and we should tell core about it -- and schedule + * a job to revoke the state. + * + * @param hop a path to some peer that is the reason for activation + */ +static void +activate_core_visible_dv_path (struct DistanceVectorHop *hop) +{ + struct DistanceVector *dv = hop->dv; + struct VirtualLink *vl; + + vl = lookup_virtual_link (&dv->target); + if (NULL == vl) + { + + vl = GNUNET_new (struct VirtualLink); + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Creating new virtual link %p to %s using DV!\n", + vl, + GNUNET_i2s (&dv->target)); + vl->confirmed = GNUNET_YES; + vl->message_uuid_ctr = + GNUNET_CRYPTO_random_u64 (GNUNET_CRYPTO_QUALITY_WEAK, UINT64_MAX); + vl->target = dv->target; + vl->core_recv_window = RECV_WINDOW_SIZE; + vl->available_fc_window_size = DEFAULT_WINDOW_SIZE; + vl->incoming_fc_window_size = DEFAULT_WINDOW_SIZE; + GNUNET_break (GNUNET_YES == + GNUNET_CONTAINER_multipeermap_put ( + links, + &vl->target, + vl, + GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_ONLY)); + vl->dv = dv; + dv->vl = vl; + vl->visibility_task = + GNUNET_SCHEDULER_add_at (hop->path_valid_until, &check_link_down, vl); + consider_sending_fc (vl); + /* We lacked a confirmed connection to the target + before, so tell CORE about it (finally!) */ + cores_send_connect_info (&dv->target); + send_msg_from_cache (vl); + } + else + { + /* Link was already up, remember dv is also now available and we are done */ + vl->dv = dv; + dv->vl = vl; + if (GNUNET_NO == vl->confirmed) + { + vl->confirmed = GNUNET_YES; + vl->visibility_task = + GNUNET_SCHEDULER_add_at (hop->path_valid_until, &check_link_down, vl); + consider_sending_fc (vl); + /* We lacked a confirmed connection to the target + before, so tell CORE about it (finally!) */ + cores_send_connect_info (&dv->target); + send_msg_from_cache (vl); + } + else + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Virtual link to %s could now also use DV!\n", + GNUNET_i2s (&dv->target)); + } +} + + +/** + * We have learned a @a path through the network to some other peer, add it to + * our DV data structure (returning #GNUNET_YES on success). + * + * We do not add paths if we have a sufficient number of shorter + * paths to this target already (returning #GNUNET_NO). + * + * We also do not add problematic paths, like those where we lack the first + * hop in our neighbour list (i.e. due to a topology change) or where some + * non-first hop is in our neighbour list (returning #GNUNET_SYSERR). + * + * @param path the path we learned, path[0] should be us, + * and then path contains a valid path from us to + * `path[path_len-1]` path[1] should be a direct neighbour (we should check!) + * @param path_len number of entries on the @a path, at least three! + * @param network_latency how long does the message take from us to + * `path[path_len-1]`? set to "forever" if unknown + * @param path_valid_until how long is this path considered validated? Maybe + * be zero. + * @return #GNUNET_YES on success, + * #GNUNET_NO if we have better path(s) to the target + * #GNUNET_SYSERR if the path is useless and/or invalid + * (i.e. path[1] not a direct neighbour + * or path[i+1] is a direct neighbour for i>0) + */ +static int +learn_dv_path (const struct GNUNET_PeerIdentity *path, + unsigned int path_len, + struct GNUNET_TIME_Relative network_latency, + struct GNUNET_TIME_Absolute path_valid_until) +{ + struct DistanceVectorHop *hop; + struct DistanceVector *dv; + struct Neighbour *next_hop; + unsigned int shorter_distance; + + if (path_len < 3) + { + /* what a boring path! not allowed! */ + GNUNET_break (0); + return GNUNET_SYSERR; + } + GNUNET_assert (0 == GNUNET_memcmp (&GST_my_identity, &path[0])); + next_hop = lookup_neighbour (&path[1]); + if (NULL == next_hop) + { + /* next hop must be a neighbour, otherwise this whole thing is useless! */ + GNUNET_break (0); + return GNUNET_SYSERR; + } + for (unsigned int i = 2; i < path_len; i++) + if (NULL != lookup_neighbour (&path[i])) + { + /* Useless path: we have a direct connection to some hop + in the middle of the path, so this one is not even + terribly useful for redundancy */ + GNUNET_log (GNUNET_ERROR_TYPE_INFO, + "Path of %u hops useless: directly link to hop %u (%s)\n", + path_len, + i, + GNUNET_i2s (&path[i])); + GNUNET_STATISTICS_update (GST_stats, + "# Useless DV path ignored: hop is neighbour", + 1, + GNUNET_NO); + return GNUNET_SYSERR; + } + dv = GNUNET_CONTAINER_multipeermap_get (dv_routes, &path[path_len - 1]); + if (NULL == dv) + { + dv = GNUNET_new (struct DistanceVector); + dv->target = path[path_len - 1]; + dv->timeout_task = GNUNET_SCHEDULER_add_delayed (DV_PATH_VALIDITY_TIMEOUT, + &path_cleanup_cb, + dv); + GNUNET_assert (GNUNET_OK == + GNUNET_CONTAINER_multipeermap_put ( + dv_routes, + &dv->target, + dv, + GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_ONLY)); + } + /* Check if we have this path already! */ + shorter_distance = 0; + for (struct DistanceVectorHop *pos = dv->dv_head; NULL != pos; + pos = pos->next_dv) + { + if (pos->distance < path_len - 3) + shorter_distance++; + /* Note that the distances in 'pos' excludes us (path[0]), + the next_hop (path[1]) and the target so we need to subtract three + and check next_hop explicitly */ + if ((pos->distance == path_len - 3) && (pos->next_hop == next_hop)) + { + int match = GNUNET_YES; + + for (unsigned int i = 0; i < pos->distance; i++) + { + if (0 != GNUNET_memcmp (&pos->path[i], &path[i + 2])) + { + match = GNUNET_NO; + break; + } + } + if (GNUNET_YES == match) + { + struct GNUNET_TIME_Relative last_timeout; + + /* Re-discovered known path, update timeout */ + GNUNET_STATISTICS_update (GST_stats, + "# Known DV path refreshed", + 1, + GNUNET_NO); + last_timeout = GNUNET_TIME_absolute_get_remaining (pos->timeout); + pos->timeout = + GNUNET_TIME_relative_to_absolute (DV_PATH_VALIDITY_TIMEOUT); + pos->path_valid_until = + GNUNET_TIME_absolute_max (pos->path_valid_until, path_valid_until); + GNUNET_CONTAINER_MDLL_remove (dv, dv->dv_head, dv->dv_tail, pos); + GNUNET_CONTAINER_MDLL_insert (dv, dv->dv_head, dv->dv_tail, pos); + if (0 < + GNUNET_TIME_absolute_get_remaining (path_valid_until).rel_value_us) + activate_core_visible_dv_path (pos); + if (last_timeout.rel_value_us < + GNUNET_TIME_relative_subtract (DV_PATH_VALIDITY_TIMEOUT, + DV_PATH_DISCOVERY_FREQUENCY) + .rel_value_us) + { + /* Some peer send DV learn messages too often, we are learning + the same path faster than it would be useful; do not forward! */ + GNUNET_log (GNUNET_ERROR_TYPE_INFO, + "Rediscovered path too quickly, not forwarding further\n"); + return GNUNET_NO; + } + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Refreshed known path to %s valid until %s, forwarding further\n", + GNUNET_i2s (&dv->target), + GNUNET_STRINGS_absolute_time_to_string ( + pos->path_valid_until)); + return GNUNET_YES; + } + } + } + /* Count how many shorter paths we have (incl. direct + neighbours) before simply giving up on this one! */ + if (shorter_distance >= MAX_DV_PATHS_TO_TARGET) + { + /* We have a shorter path already! */ + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Have many shorter DV paths %s, not forwarding further\n", + GNUNET_i2s (&dv->target)); + return GNUNET_NO; + } + /* create new DV path entry */ + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Discovered new DV path to %s valid until %s\n", + GNUNET_i2s (&dv->target), + GNUNET_STRINGS_absolute_time_to_string (path_valid_until)); + hop = GNUNET_malloc (sizeof(struct DistanceVectorHop) + + sizeof(struct GNUNET_PeerIdentity) * (path_len - 3)); + hop->next_hop = next_hop; + hop->dv = dv; + hop->path = (const struct GNUNET_PeerIdentity *) &hop[1]; + memcpy (&hop[1], + &path[2], + sizeof(struct GNUNET_PeerIdentity) * (path_len - 3)); + hop->timeout = GNUNET_TIME_relative_to_absolute (DV_PATH_VALIDITY_TIMEOUT); + hop->path_valid_until = path_valid_until; + hop->distance = path_len - 3; + hop->pd.aged_rtt = network_latency; + GNUNET_CONTAINER_MDLL_insert (dv, dv->dv_head, dv->dv_tail, hop); + GNUNET_CONTAINER_MDLL_insert (neighbour, + next_hop->dv_head, + next_hop->dv_tail, + hop); + if (0 < GNUNET_TIME_absolute_get_remaining (path_valid_until).rel_value_us) + activate_core_visible_dv_path (hop); + return GNUNET_YES; +} + + +/** + * Communicator gave us a DV learn message. Check the message. + * + * @param cls a `struct CommunicatorMessageContext` + * @param dvl the send message that was sent + * @return #GNUNET_YES if message is well-formed + */ +static int +check_dv_learn (void *cls, const struct TransportDVLearnMessage *dvl) +{ + uint16_t size = ntohs (dvl->header.size); + uint16_t num_hops = ntohs (dvl->num_hops); + const struct DVPathEntryP *hops = (const struct DVPathEntryP *) &dvl[1]; + + (void) cls; + if (size != sizeof(*dvl) + num_hops * sizeof(struct DVPathEntryP)) + { + GNUNET_break_op (0); + return GNUNET_SYSERR; + } + if (num_hops > MAX_DV_HOPS_ALLOWED) + { + GNUNET_break_op (0); + return GNUNET_SYSERR; + } + for (unsigned int i = 0; i < num_hops; i++) + { + if (0 == GNUNET_memcmp (&dvl->initiator, &hops[i].hop)) + { + GNUNET_break_op (0); + return GNUNET_SYSERR; + } + if (0 == GNUNET_memcmp (&GST_my_identity, &hops[i].hop)) + { + GNUNET_break_op (0); + return GNUNET_SYSERR; + } + } + return GNUNET_YES; +} + + +/** + * Build and forward a DV learn message to @a next_hop. + * + * @param next_hop peer to send the message to + * @param msg message received + * @param bi_history bitmask specifying hops on path that were bidirectional + * @param nhops length of the @a hops array + * @param hops path the message traversed so far + * @param in_time when did we receive the message, used to calculate network + * delay + */ +static void +forward_dv_learn (const struct GNUNET_PeerIdentity *next_hop, + const struct TransportDVLearnMessage *msg, + uint16_t bi_history, + uint16_t nhops, + const struct DVPathEntryP *hops, + struct GNUNET_TIME_Absolute in_time) +{ + struct Neighbour *n; + struct VirtualLink *vl; + struct DVPathEntryP *dhops; + char buf[sizeof(struct TransportDVLearnMessage) + + (nhops + 1) * sizeof(struct DVPathEntryP)] GNUNET_ALIGN; + struct TransportDVLearnMessage *fwd = (struct TransportDVLearnMessage *) buf; + struct GNUNET_TIME_Relative nnd; + + /* compute message for forwarding */ + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Forwarding DV learn message originating from %s to %s\n", + GNUNET_i2s (&msg->initiator), + GNUNET_i2s2 (next_hop)); + GNUNET_assert (nhops < MAX_DV_HOPS_ALLOWED); + fwd->header.type = htons (GNUNET_MESSAGE_TYPE_TRANSPORT_DV_LEARN); + fwd->header.size = htons (sizeof(struct TransportDVLearnMessage) + + (nhops + 1) * sizeof(struct DVPathEntryP)); + fwd->num_hops = htons (nhops + 1); + fwd->bidirectional = htons (bi_history); + nnd = GNUNET_TIME_relative_add (GNUNET_TIME_absolute_get_duration (in_time), + GNUNET_TIME_relative_ntoh ( + msg->non_network_delay)); + fwd->non_network_delay = GNUNET_TIME_relative_hton (nnd); + fwd->init_sig = msg->init_sig; + fwd->initiator = msg->initiator; + fwd->challenge = msg->challenge; + fwd->monotonic_time = msg->monotonic_time; + dhops = (struct DVPathEntryP *) &fwd[1]; + GNUNET_memcpy (dhops, hops, sizeof(struct DVPathEntryP) * nhops); + dhops[nhops].hop = GST_my_identity; + { + struct DvHopPS dhp = { + .purpose.purpose = htonl (GNUNET_SIGNATURE_PURPOSE_TRANSPORT_DV_HOP), + .purpose.size = htonl (sizeof(dhp)), + .pred = (0 == nhops) ? msg->initiator : dhops[nhops - 1].hop, + .succ = *next_hop, + .challenge = msg->challenge + }; + GNUNET_CRYPTO_eddsa_sign (GST_my_private_key, + &dhp, + &dhops[nhops].hop_sig); + } + /*route_control_message_without_fc (next_hop, + &fwd->header, + RMO_UNCONFIRMED_ALLOWED);*/ + vl = lookup_virtual_link (next_hop); + if ((NULL != vl) && (GNUNET_YES == vl->confirmed)) + { + route_control_message_without_fc (vl, + &fwd->header, + RMO_UNCONFIRMED_ALLOWED); + } + else + { + /* Use route via neighbour */ + n = lookup_neighbour (next_hop); + if (NULL != n) + route_via_neighbour ( + n, + &fwd->header, + RMO_UNCONFIRMED_ALLOWED); + } +} + + +/** + * Check signature of type #GNUNET_SIGNATURE_PURPOSE_TRANSPORT_DV_INITIATOR + * + * @param sender_monotonic_time monotonic time of the initiator + * @param init the signer + * @param challenge the challenge that was signed + * @param init_sig signature presumably by @a init + * @return #GNUNET_OK if the signature is valid + */ +static int +validate_dv_initiator_signature ( + struct GNUNET_TIME_AbsoluteNBO sender_monotonic_time, + const struct GNUNET_PeerIdentity *init, + const struct GNUNET_CRYPTO_ChallengeNonceP *challenge, + const struct GNUNET_CRYPTO_EddsaSignature *init_sig) +{ + struct DvInitPS ip = { .purpose.purpose = htonl ( + GNUNET_SIGNATURE_PURPOSE_TRANSPORT_DV_INITIATOR), + .purpose.size = htonl (sizeof(ip)), + .monotonic_time = sender_monotonic_time, + .challenge = *challenge }; + + if ( + GNUNET_OK != + GNUNET_CRYPTO_eddsa_verify (GNUNET_SIGNATURE_PURPOSE_TRANSPORT_DV_INITIATOR, + &ip, + init_sig, + &init->public_key)) + { + GNUNET_break_op (0); + return GNUNET_SYSERR; + } + return GNUNET_OK; +} + + +/** + * Closure for #dv_neighbour_selection and #dv_neighbour_transmission. + */ +struct NeighbourSelectionContext +{ + /** + * Original message we received. + */ + const struct TransportDVLearnMessage *dvl; + + /** + * The hops taken. + */ + const struct DVPathEntryP *hops; + + /** + * Time we received the message. + */ + struct GNUNET_TIME_Absolute in_time; + + /** + * Offsets of the selected peers. + */ + uint32_t selections[MAX_DV_DISCOVERY_SELECTION]; + + /** + * Number of peers eligible for selection. + */ + unsigned int num_eligible; + + /** + * Number of peers that were selected for forwarding. + */ + unsigned int num_selections; + + /** + * Number of hops in @e hops + */ + uint16_t nhops; + + /** + * Bitmap of bidirectional connections encountered. + */ + uint16_t bi_history; +}; + + +/** + * Function called for each neighbour during #handle_dv_learn. + * + * @param cls a `struct NeighbourSelectionContext *` + * @param pid identity of the peer + * @param value a `struct Neighbour` + * @return #GNUNET_YES (always) + */ +static int +dv_neighbour_selection (void *cls, + const struct GNUNET_PeerIdentity *pid, + void *value) +{ + struct NeighbourSelectionContext *nsc = cls; + + (void) value; + if (0 == GNUNET_memcmp (pid, &nsc->dvl->initiator)) + return GNUNET_YES; /* skip initiator */ + for (unsigned int i = 0; i < nsc->nhops; i++) + if (0 == GNUNET_memcmp (pid, &nsc->hops[i].hop)) + return GNUNET_YES; + /* skip peers on path */ + nsc->num_eligible++; + return GNUNET_YES; +} + + +/** + * Function called for each neighbour during #handle_dv_learn. + * We call #forward_dv_learn() on the neighbour(s) selected + * during #dv_neighbour_selection(). + * + * @param cls a `struct NeighbourSelectionContext *` + * @param pid identity of the peer + * @param value a `struct Neighbour` + * @return #GNUNET_YES (always) + */ +static int +dv_neighbour_transmission (void *cls, + const struct GNUNET_PeerIdentity *pid, + void *value) +{ + struct NeighbourSelectionContext *nsc = cls; + + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "transmission %s\n", + GNUNET_i2s (pid)); + (void) value; + if (0 == GNUNET_memcmp (pid, &nsc->dvl->initiator)) + return GNUNET_YES; /* skip initiator */ + for (unsigned int i = 0; i < nsc->nhops; i++) + if (0 == GNUNET_memcmp (pid, &nsc->hops[i].hop)) + return GNUNET_YES; + /* skip peers on path */ + for (unsigned int i = 0; i < nsc->num_selections; i++) + { + if (nsc->selections[i] == nsc->num_eligible) + { + forward_dv_learn (pid, + nsc->dvl, + nsc->bi_history, + nsc->nhops, + nsc->hops, + nsc->in_time); + break; + } + } + nsc->num_eligible++; + return GNUNET_YES; +} + + +/** + * Computes the number of neighbours we should forward a DVInit + * message to given that it has so far taken @a hops_taken hops + * though the network and that the number of neighbours we have + * in total is @a neighbour_count, out of which @a eligible_count + * are not yet on the path. + * + * NOTE: technically we might want to include NSE in the formula to + * get a better grip on the overall network size. However, for now + * using NSE here would create a dependency issue in the build system. + * => Left for later, hardcoded to 50 for now. + * + * The goal of the fomula is that we want to reach a total of LOG(NSE) + * peers via DV (`target_total`). We want the reach to be spread out + * over various distances to the origin, with a bias towards shorter + * distances. + * + * We make the strong assumption that the network topology looks + * "similar" at other hops, in particular the @a neighbour_count + * should be comparable at other hops. + * + * If the local neighbourhood is densely connected, we expect that @a + * eligible_count is close to @a neighbour_count minus @a hops_taken + * as a lot of the path is already known. In that case, we should + * forward to few(er) peers to try to find a path out of the + * neighbourhood. OTOH, if @a eligible_count is close to @a + * neighbour_count, we should forward to many peers as we are either + * still close to the origin (i.e. @a hops_taken is small) or because + * we managed to get beyond a local cluster. We express this as + * the `boost_factor` using the square of the fraction of eligible + * neighbours (so if only 50% are eligible, we boost by 1/4, but if + * 99% are eligible, the 'boost' will be almost 1). + * + * Second, the more hops we have taken, the larger the problem of an + * exponential traffic explosion gets. So we take the `target_total`, + * and compute our degree such that at each distance d 2^{-d} peers + * are selected (corrected by the `boost_factor`). + * + * @param hops_taken number of hops DVInit has travelled so far + * @param neighbour_count number of neighbours we have in total + * @param eligible_count number of neighbours we could in + * theory forward to + */ +static unsigned int +calculate_fork_degree (unsigned int hops_taken, + unsigned int neighbour_count, + unsigned int eligible_count) +{ + double target_total = 50.0; /* FIXME: use LOG(NSE)? */ + double eligible_ratio = + ((double) eligible_count) / ((double) neighbour_count); + double boost_factor = eligible_ratio * eligible_ratio; + unsigned int rnd; + double left; + + if (hops_taken >= 64) + { + GNUNET_break (0); + return 0; /* precaution given bitshift below */ + } + for (unsigned int i = 1; i < hops_taken; i++) + { + /* For each hop, subtract the expected number of targets + reached at distance d (so what remains divided by 2^d) */ + target_total -= (target_total * boost_factor / (1LLU << i)); + } + rnd = + (unsigned int) floor (target_total * boost_factor / (1LLU << hops_taken)); + /* round up or down probabilistically depending on how close we were + when floor()ing to rnd */ + left = target_total - (double) rnd; + if (UINT32_MAX * left > + GNUNET_CRYPTO_random_u64 (GNUNET_CRYPTO_QUALITY_WEAK, UINT32_MAX)) + rnd++; /* round up */ + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Forwarding DV learn message of %u hops %u(/%u/%u) times\n", + hops_taken, + rnd, + eligible_count, + neighbour_count); + return rnd; +} + + +/** + * Function called when peerstore is done storing a DV monotonic time. + * + * @param cls a `struct Neighbour` + * @param success #GNUNET_YES if peerstore was successful + */ +static void +neighbour_store_dvmono_cb (void *cls, int success) +{ + struct Neighbour *n = cls; + + n->sc = NULL; + if (GNUNET_YES != success) + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "Failed to store other peer's monotonic time in peerstore!\n"); +} + + +/** + * Communicator gave us a DV learn message. Process the request. + * + * @param cls a `struct CommunicatorMessageContext` (must call + * #finish_cmc_handling() when done) + * @param dvl the message that was received + */ +static void +handle_dv_learn (void *cls, const struct TransportDVLearnMessage *dvl) +{ + struct CommunicatorMessageContext *cmc = cls; + enum GNUNET_TRANSPORT_CommunicatorCharacteristics cc; + int bi_hop; + uint16_t nhops; + uint16_t bi_history; + const struct DVPathEntryP *hops; + int do_fwd; + int did_initiator; + struct GNUNET_TIME_Absolute in_time; + struct Neighbour *n; + + nhops = ntohs (dvl->num_hops); /* 0 = sender is initiator */ + bi_history = ntohs (dvl->bidirectional); + hops = (const struct DVPathEntryP *) &dvl[1]; + if (0 == nhops) + { + /* sanity check */ + if (0 != GNUNET_memcmp (&dvl->initiator, &cmc->im.sender)) + { + GNUNET_break (0); + finish_cmc_handling (cmc); + return; + } + } + else + { + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "handle dv learn message last hop %s\n", + GNUNET_i2s (&hops[nhops - 1].hop)); + /* sanity check */ + if (0 != GNUNET_memcmp (&hops[nhops - 1].hop, &cmc->im.sender)) + { + GNUNET_break (0); + finish_cmc_handling (cmc); + return; + } + } + + GNUNET_assert (CT_COMMUNICATOR == cmc->tc->type); + cc = cmc->tc->details.communicator.cc; + bi_hop = (GNUNET_TRANSPORT_CC_RELIABLE == + cc); // FIXME: add bi-directional flag to cc? + in_time = GNUNET_TIME_absolute_get (); + + /* continue communicator here, everything else can happen asynchronous! */ + finish_cmc_handling (cmc); + + n = lookup_neighbour (&dvl->initiator); + if (NULL != n) + { + if ((n->dv_monotime_available == GNUNET_YES) && + (GNUNET_TIME_absolute_ntoh (dvl->monotonic_time).abs_value_us < + n->last_dv_learn_monotime.abs_value_us)) + { + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "DV learn from %s discarded due to time travel", + GNUNET_i2s (&dvl->initiator)); + GNUNET_STATISTICS_update (GST_stats, + "# DV learn discarded due to time travel", + 1, + GNUNET_NO); + return; + } + if (GNUNET_OK != validate_dv_initiator_signature (dvl->monotonic_time, + &dvl->initiator, + &dvl->challenge, + &dvl->init_sig)) + { + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "DV learn signature from %s invalid\n", + GNUNET_i2s (&dvl->initiator)); + GNUNET_break_op (0); + return; + } + n->last_dv_learn_monotime = GNUNET_TIME_absolute_ntoh (dvl->monotonic_time); + if (GNUNET_YES == n->dv_monotime_available) + { + if (NULL != n->sc) + { + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "store cancel\n"); + GNUNET_PEERSTORE_store_cancel (n->sc); + } + n->sc = + GNUNET_PEERSTORE_store (peerstore, + "transport", + &dvl->initiator, + GNUNET_PEERSTORE_TRANSPORT_DVLEARN_MONOTIME, + &dvl->monotonic_time, + sizeof(dvl->monotonic_time), + GNUNET_TIME_UNIT_FOREVER_ABS, + GNUNET_PEERSTORE_STOREOPTION_REPLACE, + &neighbour_store_dvmono_cb, + n); + } + } + /* OPTIMIZE-FIXME: asynchronously (!) verify signatures!, + If signature verification load too high, implement random drop strategy */ + for (unsigned int i = 0; i < nhops; i++) + { + struct DvHopPS dhp = { .purpose.purpose = + htonl (GNUNET_SIGNATURE_PURPOSE_TRANSPORT_DV_HOP), + .purpose.size = htonl (sizeof(dhp)), + .pred = (0 == i) ? dvl->initiator : hops[i - 1].hop, + .succ = (nhops == i + 1) ? GST_my_identity + : hops[i + 1].hop, + .challenge = dvl->challenge }; + + if (GNUNET_OK != + GNUNET_CRYPTO_eddsa_verify (GNUNET_SIGNATURE_PURPOSE_TRANSPORT_DV_HOP, + &dhp, + &hops[i].hop_sig, + &hops[i].hop.public_key)) + { + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "DV learn from %s signature of hop %u invalid\n", + GNUNET_i2s (&dvl->initiator), + i); + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "signature of hop %s invalid\n", + GNUNET_i2s (&hops[i].hop)); + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "pred %s\n", + GNUNET_i2s (&dhp.pred)); + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "succ %s\n", + GNUNET_i2s (&dhp.succ)); + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "hash %s\n", + GNUNET_sh2s (&dhp.challenge.value)); + GNUNET_break_op (0); + return; + } + } + if (GNUNET_EXTRA_LOGGING > 0) + { + char *path; + + path = GNUNET_strdup (GNUNET_i2s (&dvl->initiator)); + for (unsigned int i = 0; i < nhops; i++) + { + char *tmp; + + GNUNET_asprintf (&tmp, + "%s%s%s", + path, + (bi_history & (1 << (nhops - i))) ? "<->" : "-->", + GNUNET_i2s (&hops[i].hop)); + GNUNET_free (path); + path = tmp; + } + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Received DVInit via %s%s%s\n", + path, + bi_hop ? "<->" : "-->", + GNUNET_i2s (&GST_my_identity)); + GNUNET_free (path); + } + do_fwd = GNUNET_YES; + if (0 == GNUNET_memcmp (&GST_my_identity, &dvl->initiator)) + { + struct GNUNET_PeerIdentity path[nhops + 1]; + struct GNUNET_TIME_Relative host_latency_sum; + struct GNUNET_TIME_Relative latency; + struct GNUNET_TIME_Relative network_latency; + + /* We initiated this, learn the forward path! */ + path[0] = GST_my_identity; + path[1] = hops[0].hop; + host_latency_sum = GNUNET_TIME_relative_ntoh (dvl->non_network_delay); + + // Need also something to lookup initiation time + // to compute RTT! -> add RTT argument here? + latency = GNUNET_TIME_absolute_get_duration (GNUNET_TIME_absolute_ntoh ( + dvl->monotonic_time)); + GNUNET_assert (latency.rel_value_us >= host_latency_sum.rel_value_us); + // latency = GNUNET_TIME_UNIT_FOREVER_REL; // FIXME: initialize properly + // (based on dvl->challenge, we can identify time of origin!) + + network_latency = GNUNET_TIME_relative_subtract (latency, host_latency_sum); + /* assumption: latency on all links is the same */ + network_latency = GNUNET_TIME_relative_divide (network_latency, nhops); + + for (unsigned int i = 2; i <= nhops; i++) + { + struct GNUNET_TIME_Relative ilat; + + /* assumption: linear latency increase per hop */ + ilat = GNUNET_TIME_relative_multiply (network_latency, i); + path[i] = hops[i - 1].hop; + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Learned path with %u hops to %s with latency %s\n", + i, + GNUNET_i2s (&path[i]), + GNUNET_STRINGS_relative_time_to_string (ilat, GNUNET_YES)); + learn_dv_path (path, + i + 1, + ilat, + GNUNET_TIME_relative_to_absolute ( + ADDRESS_VALIDATION_LIFETIME)); + } + /* as we initiated, do not forward again (would be circular!) */ + do_fwd = GNUNET_NO; + return; + } + if (bi_hop) + { + /* last hop was bi-directional, we could learn something here! */ + struct GNUNET_PeerIdentity path[nhops + 2]; + + path[0] = GST_my_identity; + path[1] = hops[nhops - 1].hop; /* direct neighbour == predecessor! */ + for (unsigned int i = 0; i < nhops; i++) + { + int iret; + + if (0 == (bi_history & (1 << i))) + break; /* i-th hop not bi-directional, stop learning! */ + if (i == nhops - 1) + { + path[i + 2] = dvl->initiator; + } + else + { + path[i + 2] = hops[nhops - i - 2].hop; + } + + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Learned inverse path with %u hops to %s\n", + i + 2, + GNUNET_i2s (&path[i + 2])); + iret = learn_dv_path (path, + i + 3, + GNUNET_TIME_UNIT_FOREVER_REL, + GNUNET_TIME_relative_to_absolute ( + ADDRESS_VALIDATION_LIFETIME)); + if (GNUNET_SYSERR == iret) + { + /* path invalid or too long to be interesting for US, thus should also + not be interesting to our neighbours, cut path when forwarding to + 'i' hops, except of course for the one that goes back to the + initiator */ + GNUNET_STATISTICS_update (GST_stats, + "# DV learn not forwarded due invalidity of path", + 1, + GNUNET_NO); + do_fwd = GNUNET_NO; + break; + } + if ((GNUNET_NO == iret) && (nhops == i + 1)) + { + /* we have better paths, and this is the longest target, + so there cannot be anything interesting later */ + GNUNET_STATISTICS_update (GST_stats, + "# DV learn not forwarded, got better paths", + 1, + GNUNET_NO); + do_fwd = GNUNET_NO; + break; + } + } + } + if (MAX_DV_HOPS_ALLOWED == nhops) + { + /* At limit, we're out of here! */ + return; + } + + /* Forward to initiator, if path non-trivial and possible */ + bi_history = (bi_history << 1) | (bi_hop ? 1 : 0); + did_initiator = GNUNET_NO; + if ((1 <= nhops) && + (GNUNET_YES == + GNUNET_CONTAINER_multipeermap_contains (neighbours, &dvl->initiator))) + { + /* send back to origin! */ + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Sending DVL back to initiator %s\n", + GNUNET_i2s (&dvl->initiator)); + forward_dv_learn (&dvl->initiator, dvl, bi_history, nhops, hops, in_time); + did_initiator = GNUNET_YES; + } + /* We forward under two conditions: either we still learned something + ourselves (do_fwd), or the path was darn short and thus the initiator is + likely to still be very interested in this (and we did NOT already + send it back to the initiator) */ + if ((do_fwd) || ((nhops < MIN_DV_PATH_LENGTH_FOR_INITIATOR) && + (GNUNET_NO == did_initiator))) + { + /* Pick random neighbours that are not yet on the path */ + struct NeighbourSelectionContext nsc; + unsigned int n_cnt; + + n_cnt = GNUNET_CONTAINER_multipeermap_size (neighbours); + nsc.nhops = nhops; + nsc.dvl = dvl; + nsc.bi_history = bi_history; + nsc.hops = hops; + nsc.in_time = in_time; + nsc.num_eligible = 0; + GNUNET_CONTAINER_multipeermap_iterate (neighbours, + &dv_neighbour_selection, + &nsc); + if (0 == nsc.num_eligible) + return; /* done here, cannot forward to anyone else */ + nsc.num_selections = calculate_fork_degree (nhops, n_cnt, nsc.num_eligible); + nsc.num_selections = + GNUNET_MIN (MAX_DV_DISCOVERY_SELECTION, nsc.num_selections); + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Forwarding DVL to %u other peers\n", + nsc.num_selections); + for (unsigned int i = 0; i < nsc.num_selections; i++) + nsc.selections[i] = + (nsc.num_selections == n_cnt) + ? i /* all were selected, avoid collisions by chance */ + : GNUNET_CRYPTO_random_u32 (GNUNET_CRYPTO_QUALITY_WEAK, n_cnt); + nsc.num_eligible = 0; + GNUNET_CONTAINER_multipeermap_iterate (neighbours, + &dv_neighbour_transmission, + &nsc); + } +} + + +/** + * Communicator gave us a DV box. Check the message. + * + * @param cls a `struct CommunicatorMessageContext` + * @param dvb the send message that was sent + * @return #GNUNET_YES if message is well-formed + */ +static int +check_dv_box (void *cls, const struct TransportDVBoxMessage *dvb) +{ + uint16_t size = ntohs (dvb->header.size); + uint16_t num_hops = ntohs (dvb->num_hops); + const struct GNUNET_PeerIdentity *hops = + (const struct GNUNET_PeerIdentity *) &dvb[1]; + + (void) cls; + if (size < sizeof(*dvb) + num_hops * sizeof(struct GNUNET_PeerIdentity) + + sizeof(struct GNUNET_MessageHeader)) + { + GNUNET_break_op (0); + return GNUNET_SYSERR; + } + /* This peer must not be on the path */ + for (unsigned int i = 0; i < num_hops; i++) + if (0 == GNUNET_memcmp (&hops[i], &GST_my_identity)) + { + GNUNET_break_op (0); + return GNUNET_SYSERR; + } + return GNUNET_YES; +} + + +/** + * Create a DV Box message and queue it for transmission to + * @a next_hop. + * + * @param next_hop peer to receive the message next + * @param total_hops how many hops did the message take so far + * @param num_hops length of the @a hops array + * @param origin origin of the message + * @param hops next peer(s) to the destination, including destination + * @param payload payload of the box + * @param payload_size number of bytes in @a payload + */ +static void +forward_dv_box (struct Neighbour *next_hop, + struct TransportDVBoxMessage *hdr, + uint16_t total_hops, + uint16_t num_hops, + const struct GNUNET_PeerIdentity *hops, + const void *enc_payload, + uint16_t enc_payload_size) +{ + struct VirtualLink *vl = next_hop->vl; + struct PendingMessage *pm; + size_t msg_size = sizeof(struct TransportDVBoxMessage) + + num_hops * sizeof(struct GNUNET_PeerIdentity) + + enc_payload_size; + char *buf; + char msg_buf[msg_size] GNUNET_ALIGN; + struct GNUNET_PeerIdentity *dhops; + + hdr->num_hops = htons (num_hops); + hdr->total_hops = htons (total_hops); + hdr->header.size = htons (msg_size); + memcpy (msg_buf, hdr, sizeof(*hdr)); + dhops = (struct GNUNET_PeerIdentity *) &msg_buf[sizeof(struct + TransportDVBoxMessage)]; + memcpy (dhops, hops, num_hops * sizeof(struct GNUNET_PeerIdentity)); + memcpy (&dhops[num_hops], enc_payload, enc_payload_size); + + if (GNUNET_YES == ntohs (hdr->without_fc)) + { + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Forwarding control message (payload size %u) in DV Box to next hop %s (%u/%u) \n", + enc_payload_size, + GNUNET_i2s (&next_hop->pid), + (unsigned int) num_hops, + (unsigned int) total_hops); + route_via_neighbour (next_hop, (const struct + GNUNET_MessageHeader *) msg_buf, + RMO_ANYTHING_GOES); + } + else + { + pm = GNUNET_malloc (sizeof(struct PendingMessage) + msg_size); + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "2 created pm %p storing vl %p \n", + pm, + vl); + pm->pmt = PMT_DV_BOX; + pm->vl = vl; + pm->target = next_hop->pid; + pm->timeout = GNUNET_TIME_relative_to_absolute (DV_FORWARD_TIMEOUT); + pm->logging_uuid = logging_uuid_gen++; + pm->prefs = GNUNET_MQ_PRIO_BACKGROUND; + pm->bytes_msg = msg_size; + buf = (char *) &pm[1]; + memcpy (buf, msg_buf, msg_size); + + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Created pending message %llu for DV Box with next hop %s (%u/%u)\n", + pm->logging_uuid, + GNUNET_i2s (&next_hop->pid), + (unsigned int) num_hops, + (unsigned int) total_hops); + + if ((NULL != vl) && (GNUNET_YES == vl->confirmed)) + { + GNUNET_CONTAINER_MDLL_insert (vl, + vl->pending_msg_head, + vl->pending_msg_tail, + pm); + + check_vl_transmission (vl); + } + else + { + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "The virtual link is not ready for forwarding a DV Box with payload, storing PendingMessage in ring buffer.\n"); + + ring_buffer_dv[ring_buffer_dv_head] = pm; + if (RING_BUFFER_SIZE - 1 == ring_buffer_dv_head) + { + ring_buffer_dv_head = 0; + is_ring_buffer_dv_full = GNUNET_YES; + } + else + ring_buffer_dv_head++; + + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "%u items stored in DV ring buffer\n", + ring_buffer_dv_head); + } + } +} + + +/** + * Free data structures associated with @a b. + * + * @param b data structure to release + */ +static void +free_backtalker (struct Backtalker *b) +{ + if (NULL != b->get) + { + GNUNET_PEERSTORE_iterate_cancel (b->get); + b->get = NULL; + GNUNET_assert (NULL != b->cmc); + finish_cmc_handling (b->cmc); + b->cmc = NULL; + } + if (NULL != b->task) + { + GNUNET_SCHEDULER_cancel (b->task); + b->task = NULL; + } + if (NULL != b->sc) + { + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "store cancel\n"); + GNUNET_PEERSTORE_store_cancel (b->sc); + b->sc = NULL; + } + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Removing backtalker for %s\n", + GNUNET_i2s (&b->pid)); + GNUNET_assert ( + GNUNET_YES == + GNUNET_CONTAINER_multipeermap_remove (backtalkers, &b->pid, b)); + GNUNET_free (b); +} + + +/** + * Callback to free backtalker records. + * + * @param cls NULL + * @param pid unused + * @param value a `struct Backtalker` + * @return #GNUNET_OK (always) + */ +static int +free_backtalker_cb (void *cls, + const struct GNUNET_PeerIdentity *pid, + void *value) +{ + struct Backtalker *b = value; + + (void) cls; + (void) pid; + free_backtalker (b); + return GNUNET_OK; +} + + +/** + * Function called when it is time to clean up a backtalker. + * + * @param cls a `struct Backtalker` + */ +static void +backtalker_timeout_cb (void *cls) +{ + struct Backtalker *b = cls; + + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "backtalker timeout.\n"); + b->task = NULL; + if (0 != GNUNET_TIME_absolute_get_remaining (b->timeout).rel_value_us) + { + b->task = GNUNET_SCHEDULER_add_at (b->timeout, &backtalker_timeout_cb, b); + return; + } + GNUNET_assert (NULL == b->sc); + free_backtalker (b); +} + + +/** + * Function called with the monotonic time of a backtalker + * by PEERSTORE. Updates the time and continues processing. + * + * @param cls a `struct Backtalker` + * @param record the information found, NULL for the last call + * @param emsg error message + */ +static void +backtalker_monotime_cb (void *cls, + const struct GNUNET_PEERSTORE_Record *record, + const char *emsg) +{ + struct Backtalker *b = cls; + struct GNUNET_TIME_AbsoluteNBO *mtbe; + struct GNUNET_TIME_Absolute mt; + + (void) emsg; + if (NULL == record) + { + /* we're done with #backtalker_monotime_cb() invocations, + continue normal processing */ + b->get = NULL; + GNUNET_assert (NULL != b->cmc); + b->cmc->mh = (const struct GNUNET_MessageHeader *) &b[1]; + if (0 != b->body_size) + demultiplex_with_cmc (b->cmc); + else + finish_cmc_handling (b->cmc); + b->cmc = NULL; + return; + } + if (sizeof(*mtbe) != record->value_size) + { + GNUNET_break (0); + return; + } + mtbe = record->value; + mt = GNUNET_TIME_absolute_ntoh (*mtbe); + if (mt.abs_value_us > b->monotonic_time.abs_value_us) + { + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Backtalker message from %s dropped, monotime in the past\n", + GNUNET_i2s (&b->pid)); + GNUNET_STATISTICS_update ( + GST_stats, + "# Backchannel messages dropped: monotonic time not increasing", + 1, + GNUNET_NO); + b->monotonic_time = mt; + /* Setting body_size to 0 prevents call to #forward_backchannel_payload() + */ + b->body_size = 0; + return; + } +} + + +/** + * Function called by PEERSTORE when the store operation of + * a backtalker's monotonic time is complete. + * + * @param cls the `struct Backtalker` + * @param success #GNUNET_OK on success + */ +static void +backtalker_monotime_store_cb (void *cls, int success) +{ + struct Backtalker *b = cls; + + if (GNUNET_OK != success) + { + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "Failed to store backtalker's monotonic time in PEERSTORE!\n"); + } + b->sc = NULL; + if (NULL != b->task) + { + GNUNET_SCHEDULER_cancel (b->task); + b->task = NULL; + } + b->task = GNUNET_SCHEDULER_add_at (b->timeout, &backtalker_timeout_cb, b); +} + + +/** + * The backtalker @a b monotonic time changed. Update PEERSTORE. + * + * @param b a backtalker with updated monotonic time + */ +static void +update_backtalker_monotime (struct Backtalker *b) +{ + struct GNUNET_TIME_AbsoluteNBO mtbe; + + if (NULL != b->sc) + { + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "store cancel before store with sc %p\n", + b->sc); + /*GNUNET_PEERSTORE_store_cancel (b->sc); + b->sc = NULL;*/ + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "store cancel before store with sc %p is null\n", + b->sc); + } + else + { + GNUNET_SCHEDULER_cancel (b->task); + b->task = NULL; + } + mtbe = GNUNET_TIME_absolute_hton (b->monotonic_time); + b->sc = + GNUNET_PEERSTORE_store (peerstore, + "transport", + &b->pid, + GNUNET_PEERSTORE_TRANSPORT_BACKCHANNEL_MONOTIME, + &mtbe, + sizeof(mtbe), + GNUNET_TIME_UNIT_FOREVER_ABS, + GNUNET_PEERSTORE_STOREOPTION_REPLACE, + &backtalker_monotime_store_cb, + b); +} + + +/** + * Communicator gave us a DV box. Process the request. + * + * @param cls a `struct CommunicatorMessageContext` (must call + * #finish_cmc_handling() when done) + * @param dvb the message that was received + */ +static void +handle_dv_box (void *cls, const struct TransportDVBoxMessage *dvb) +{ + struct CommunicatorMessageContext *cmc = cls; + uint16_t size = ntohs (dvb->header.size) - sizeof(*dvb); + uint16_t num_hops = ntohs (dvb->num_hops); + const struct GNUNET_PeerIdentity *hops = + (const struct GNUNET_PeerIdentity *) &dvb[1]; + const char *enc_payload = (const char *) &hops[num_hops]; + uint16_t enc_payload_size = + size - (num_hops * sizeof(struct GNUNET_PeerIdentity)); + struct DVKeyState key; + struct GNUNET_HashCode hmac; + const char *hdr; + size_t hdr_len; + + if (GNUNET_EXTRA_LOGGING > 0) + { + char *path; + + path = GNUNET_strdup (GNUNET_i2s (&GST_my_identity)); + for (unsigned int i = 0; i < num_hops; i++) + { + char *tmp; + + GNUNET_asprintf (&tmp, "%s->%s", path, GNUNET_i2s (&hops[i])); + GNUNET_free (path); + path = tmp; + } + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Received DVBox with remaining path %s\n", + path); + GNUNET_free (path); + } + + if (num_hops > 0) + { + /* We're trying from the end of the hops array, as we may be + able to find a shortcut unknown to the origin that way */ + for (int i = num_hops - 1; i >= 0; i--) + { + struct Neighbour *n; + + if (0 == GNUNET_memcmp (&hops[i], &GST_my_identity)) + { + GNUNET_break_op (0); + finish_cmc_handling (cmc); + return; + } + n = lookup_neighbour (&hops[i]); + if (NULL == n) + continue; + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Skipping %u/%u hops ahead while routing DV Box\n", + i, + num_hops); + + forward_dv_box (n, + (struct TransportDVBoxMessage *) dvb, + ntohs (dvb->total_hops) + 1, + num_hops - i - 1, /* number of hops left */ + &hops[i + 1], /* remaining hops */ + enc_payload, + enc_payload_size); + GNUNET_STATISTICS_update (GST_stats, + "# DV hops skipped routing boxes", + i, + GNUNET_NO); + GNUNET_STATISTICS_update (GST_stats, + "# DV boxes routed (total)", + 1, + GNUNET_NO); + finish_cmc_handling (cmc); + return; + } + /* Woopsie, next hop not in neighbours, drop! */ + GNUNET_STATISTICS_update (GST_stats, + "# DV Boxes dropped: next hop unknown", + 1, + GNUNET_NO); + finish_cmc_handling (cmc); + return; + } + /* We are the target. Unbox and handle message. */ + GNUNET_STATISTICS_update (GST_stats, + "# DV boxes opened (ultimate target)", + 1, + GNUNET_NO); + cmc->total_hops = ntohs (dvb->total_hops); + + // DH key derivation with received DV, could be garbage. + struct GNUNET_HashCode km; + + if (GNUNET_YES != GNUNET_CRYPTO_eddsa_kem_decaps (GST_my_private_key, + &dvb->ephemeral_key, + &km)) + { + GNUNET_break_op (0); + finish_cmc_handling (cmc); + return; + } + dv_setup_key_state_from_km (&km, &dvb->iv, &key); + hdr = (const char *) &dvb[1]; + hdr_len = ntohs (dvb->orig_size) - sizeof(*dvb) - sizeof(struct + GNUNET_PeerIdentity) + * ntohs (dvb->total_hops); + + dv_hmac (&key, &hmac, hdr, hdr_len); + if (0 != GNUNET_memcmp (&hmac, &dvb->hmac)) + { + /* HMAC mismatch, discard! */ + GNUNET_break_op (0); + finish_cmc_handling (cmc); + return; + } + /* begin actual decryption */ + { + struct Backtalker *b; + struct GNUNET_TIME_Absolute monotime; + struct TransportDVBoxPayloadP ppay; + char body[hdr_len - sizeof(ppay)] GNUNET_ALIGN; + const struct GNUNET_MessageHeader *mh; + + GNUNET_assert (hdr_len >= + sizeof(ppay) + sizeof(struct GNUNET_MessageHeader)); + if (GNUNET_OK != dv_decrypt (&key, &ppay, hdr, sizeof(ppay))) + { + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "Error decrypting DV payload header\n"); + GNUNET_break_op (0); + finish_cmc_handling (cmc); + return; + } + if (GNUNET_OK != dv_decrypt (&key, body, + &hdr[sizeof(ppay)], hdr_len - sizeof(ppay))) + { + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "Error decrypting DV payload\n"); + GNUNET_break_op (0); + finish_cmc_handling (cmc); + return; + } + mh = (const struct GNUNET_MessageHeader *) body; + dv_key_clean (&key); + if (ntohs (mh->size) != sizeof(body)) + { + GNUNET_break_op (0); + finish_cmc_handling (cmc); + return; + } + /* need to prevent box-in-a-box (and DV_LEARN) so check inbox type! */ + switch (ntohs (mh->type)) + { + case GNUNET_MESSAGE_TYPE_TRANSPORT_DV_BOX: + GNUNET_break_op (0); + finish_cmc_handling (cmc); + return; + + case GNUNET_MESSAGE_TYPE_TRANSPORT_DV_LEARN: + GNUNET_break_op (0); + finish_cmc_handling (cmc); + return; + + default: + /* permitted, continue */ + break; + } + monotime = GNUNET_TIME_absolute_ntoh (ppay.monotonic_time); + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Decrypted backtalk from %s\n", + GNUNET_i2s (&ppay.sender)); + b = GNUNET_CONTAINER_multipeermap_get (backtalkers, &ppay.sender); + if ((NULL != b) && (monotime.abs_value_us < b->monotonic_time.abs_value_us)) + { + GNUNET_STATISTICS_update ( + GST_stats, + "# Backchannel messages dropped: monotonic time not increasing", + 1, + GNUNET_NO); + finish_cmc_handling (cmc); + return; + } + if ((NULL == b) || + (0 != GNUNET_memcmp (&b->last_ephemeral, &dvb->ephemeral_key))) + { + /* Check signature */ + struct EphemeralConfirmationPS ec; + + ec.purpose.purpose = htonl (GNUNET_SIGNATURE_PURPOSE_TRANSPORT_EPHEMERAL); + ec.target = GST_my_identity; + ec.ephemeral_key = dvb->ephemeral_key; + ec.purpose.size = htonl (sizeof(ec)); + ec.sender_monotonic_time = ppay.monotonic_time; + if ( + GNUNET_OK != + GNUNET_CRYPTO_eddsa_verify ( + GNUNET_SIGNATURE_PURPOSE_TRANSPORT_EPHEMERAL, + &ec, + &ppay.sender_sig, + &ppay.sender.public_key)) + { + /* Signature invalid, discard! */ + GNUNET_break_op (0); + finish_cmc_handling (cmc); + return; + } + } + /* Update sender, we now know the real origin! */ + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "DVBox received for me from %s via %s\n", + GNUNET_i2s2 (&ppay.sender), + GNUNET_i2s (&cmc->im.sender)); + cmc->im.sender = ppay.sender; + + if (NULL != b) + { + /* update key cache and mono time */ + b->last_ephemeral = dvb->ephemeral_key; + b->monotonic_time = monotime; + update_backtalker_monotime (b); + b->timeout = + GNUNET_TIME_relative_to_absolute (BACKCHANNEL_INACTIVITY_TIMEOUT); + cmc->mh = mh; + demultiplex_with_cmc (cmc); + return; + } + /* setup data structure to cache signature AND check + monotonic time with PEERSTORE before forwarding backchannel payload */ + b = GNUNET_malloc (sizeof(struct Backtalker) + sizeof(body)); + b->pid = ppay.sender; + b->body_size = sizeof(body); + memcpy (&b[1], body, sizeof(body)); + GNUNET_assert (GNUNET_YES == + GNUNET_CONTAINER_multipeermap_put ( + backtalkers, + &b->pid, + b, + GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_ONLY)); + b->monotonic_time = monotime; /* NOTE: to be checked still! */ + b->cmc = cmc; + b->timeout = + GNUNET_TIME_relative_to_absolute (BACKCHANNEL_INACTIVITY_TIMEOUT); + b->task = GNUNET_SCHEDULER_add_at (b->timeout, &backtalker_timeout_cb, b); + b->get = + GNUNET_PEERSTORE_iterate (peerstore, + "transport", + &b->pid, + GNUNET_PEERSTORE_TRANSPORT_BACKCHANNEL_MONOTIME, + &backtalker_monotime_cb, + b); + } /* end actual decryption */ +} + + +/** + * Client notified us about transmission from a peer. Process the request. + * + * @param cls a `struct TransportClient` which sent us the message + * @param im the send message that was sent + * @return #GNUNET_YES if message is well-formed + */ +static int +check_incoming_msg (void *cls, + const struct GNUNET_TRANSPORT_IncomingMessage *im) +{ + struct TransportClient *tc = cls; + + if (CT_COMMUNICATOR != tc->type) + { + GNUNET_break (0); + return GNUNET_SYSERR; + } + GNUNET_MQ_check_boxed_message (im); + return GNUNET_OK; +} + + +/** + * Closure for #check_known_address. + */ +struct CheckKnownAddressContext +{ + /** + * Set to the address we are looking for. + */ + const char *address; + + /** + * Set to a matching validation state, if one was found. + */ + struct ValidationState *vs; +}; + + +/** + * Test if the validation state in @a value matches the + * address from @a cls. + * + * @param cls a `struct CheckKnownAddressContext` + * @param pid unused (must match though) + * @param value a `struct ValidationState` + * @return #GNUNET_OK if not matching, #GNUNET_NO if match found + */ +static int +check_known_address (void *cls, + const struct GNUNET_PeerIdentity *pid, + void *value) +{ + struct CheckKnownAddressContext *ckac = cls; + struct ValidationState *vs = value; + + (void) pid; + if (0 != strcmp (vs->address, ckac->address)) + return GNUNET_OK; + ckac->vs = vs; + return GNUNET_NO; +} + + +/** + * Task run periodically to validate some address based on #validation_heap. + * + * @param cls NULL + */ +static void +validation_start_cb (void *cls); + + +/** + * Set the time for next_challenge of @a vs to @a new_time. + * Updates the heap and if necessary reschedules the job. + * + * @param vs validation state to update + * @param new_time new time for revalidation + */ +static void +update_next_challenge_time (struct ValidationState *vs, + struct GNUNET_TIME_Absolute new_time) +{ + struct GNUNET_TIME_Relative delta; + + if (new_time.abs_value_us == vs->next_challenge.abs_value_us) + return; /* be lazy */ + vs->next_challenge = new_time; + if (NULL == vs->hn) + vs->hn = + GNUNET_CONTAINER_heap_insert (validation_heap, vs, new_time.abs_value_us); + else + GNUNET_CONTAINER_heap_update_cost (vs->hn, new_time.abs_value_us); + if ((vs != GNUNET_CONTAINER_heap_peek (validation_heap)) && + (NULL != validation_task)) + return; + if (NULL != validation_task) + GNUNET_SCHEDULER_cancel (validation_task); + /* randomize a bit */ + delta.rel_value_us = + GNUNET_CRYPTO_random_u64 (GNUNET_CRYPTO_QUALITY_WEAK, + MIN_DELAY_ADDRESS_VALIDATION.rel_value_us); + new_time = GNUNET_TIME_absolute_add (new_time, delta); + validation_task = + GNUNET_SCHEDULER_add_at (new_time, &validation_start_cb, NULL); +} + + +/** + * Start address validation. + * + * @param pid peer the @a address is for + * @param address an address to reach @a pid (presumably) + */ +static void +start_address_validation (const struct GNUNET_PeerIdentity *pid, + const char *address) +{ + struct GNUNET_TIME_Absolute now; + struct ValidationState *vs; + struct CheckKnownAddressContext ckac = { .address = address, .vs = NULL }; + + (void) GNUNET_CONTAINER_multipeermap_get_multiple (validation_map, + pid, + &check_known_address, + &ckac); + if (NULL != (vs = ckac.vs)) + { + /* if 'vs' is not currently valid, we need to speed up retrying the + * validation */ + if (vs->validated_until.abs_value_us < vs->next_challenge.abs_value_us) + { + /* reduce backoff as we got a fresh advertisement */ + vs->challenge_backoff = + GNUNET_TIME_relative_min (FAST_VALIDATION_CHALLENGE_FREQ, + GNUNET_TIME_relative_divide ( + vs->challenge_backoff, + 2)); + update_next_challenge_time (vs, + GNUNET_TIME_relative_to_absolute ( + vs->challenge_backoff)); + } + return; + } + now = GNUNET_TIME_absolute_get_monotonic (GST_cfg); + vs = GNUNET_new (struct ValidationState); + vs->pid = *pid; + vs->valid_until = + GNUNET_TIME_relative_to_absolute (ADDRESS_VALIDATION_LIFETIME); + vs->first_challenge_use = now; + vs->validation_rtt = GNUNET_TIME_UNIT_FOREVER_REL; + GNUNET_CRYPTO_random_block (GNUNET_CRYPTO_QUALITY_NONCE, + &vs->challenge, + sizeof(vs->challenge)); + vs->address = GNUNET_strdup (address); + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Starting address validation `%s' of peer %s using challenge %s\n", + address, + GNUNET_i2s (pid), + GNUNET_sh2s (&vs->challenge.value)); + GNUNET_assert (GNUNET_YES == + GNUNET_CONTAINER_multipeermap_put ( + validation_map, + &vs->pid, + vs, + GNUNET_CONTAINER_MULTIHASHMAPOPTION_MULTIPLE)); + update_next_challenge_time (vs, now); +} + + +static void +hello_for_incoming_cb (void *cls, + const char *uri) +{ + const struct GNUNET_PeerIdentity *peer = cls; + int pfx_len; + const char *eou; + char *address; + + eou = strstr (uri, + "://"); + pfx_len = eou - uri; + eou += 3; + GNUNET_asprintf (&address, + "%.*s-%s", + pfx_len, + uri, + eou); + + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "helo for client %s\n", + address); + + start_address_validation (peer, address); + GNUNET_free (address); +} + + +/** + * Function called by PEERSTORE for each matching record. + * + * @param cls closure, a `struct IncomingRequest` + * @param record peerstore record information + * @param emsg error message, or NULL if no errors + */ +static void +handle_hello_for_incoming (void *cls, + const struct GNUNET_PeerIdentity *peer, + const struct GNUNET_MessageHeader *hello, + const char *emsg) +{ + struct IncomingRequest *ir = cls; + struct GNUNET_HELLO_Builder *builder; + + if (NULL != emsg) + { + GNUNET_log (GNUNET_ERROR_TYPE_WARNING, + "Got failure from PEERSTORE: %s\n", + emsg); + return; + } + if (0 == GNUNET_memcmp (peer, &GST_my_identity)) + return; + builder = GNUNET_HELLO_builder_new (peer); + GNUNET_HELLO_builder_iterate (builder, + (struct GNUNET_PeerIdentity *) peer, + hello_for_incoming_cb, + (struct GNUNET_PeerIdentity *) peer); +} + + +/** + * Communicator gave us a transport address validation challenge. Process the + * request. + * + * @param cls a `struct CommunicatorMessageContext` (must call + * #finish_cmc_handling() when done) + * @param tvc the message that was received + */ +static void +handle_validation_challenge ( + void *cls, + const struct TransportValidationChallengeMessage *tvc) +{ + struct CommunicatorMessageContext *cmc = cls; + struct TransportValidationResponseMessage tvr; + struct VirtualLink *vl; + struct GNUNET_TIME_RelativeNBO validity_duration; + struct IncomingRequest *ir; + struct Neighbour *n; + struct GNUNET_PeerIdentity sender; + + /* DV-routed messages are not allowed for validation challenges */ + if (cmc->total_hops > 0) + { + GNUNET_break_op (0); + finish_cmc_handling (cmc); + return; + } + validity_duration = cmc->im.expected_address_validity; + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Received address validation challenge %s\n", + GNUNET_sh2s (&tvc->challenge.value)); + /* If we have a virtual link, we use this mechanism to signal the + size of the flow control window, and to allow the sender + to ask for increases. If for us the virtual link is still down, + we will always give a window size of zero. */ + tvr.header.type = + htons (GNUNET_MESSAGE_TYPE_TRANSPORT_ADDRESS_VALIDATION_RESPONSE); + tvr.header.size = htons (sizeof(tvr)); + tvr.reserved = htonl (0); + tvr.challenge = tvc->challenge; + tvr.origin_time = tvc->sender_time; + tvr.validity_duration = validity_duration; + { + /* create signature */ + struct TransportValidationPS tvp = { + .purpose.purpose = htonl (GNUNET_SIGNATURE_PURPOSE_TRANSPORT_CHALLENGE), + .purpose.size = htonl (sizeof(tvp)), + .validity_duration = validity_duration, + .challenge = tvc->challenge + }; + + GNUNET_CRYPTO_eddsa_sign (GST_my_private_key, + &tvp, + &tvr.signature); + } + sender = cmc->im.sender; + vl = lookup_virtual_link (&sender); + if ((NULL != vl) && (GNUNET_YES == vl->confirmed)) + { + // route_control_message_without_fc (&cmc->im.sender, + route_control_message_without_fc (vl, + &tvr.header, + RMO_ANYTHING_GOES | RMO_REDUNDANT); + } + else + { + /* Use route via neighbour */ + n = lookup_neighbour (&sender); + if (NULL != n) + route_via_neighbour (n, &tvr.header, + RMO_ANYTHING_GOES | RMO_REDUNDANT + | RMO_UNCONFIRMED_ALLOWED); + } + + finish_cmc_handling (cmc); + if (NULL != vl) + return; + + /* For us, the link is still down, but we need bi-directional + connections (for flow-control and for this to be useful for + CORE), so we must try to bring the link up! */ + + /* (1) Check existing queues, if any, we may be lucky! */ + n = lookup_neighbour (&sender); + if (NULL != n) + for (struct Queue *q = n->queue_head; NULL != q; q = q->next_neighbour) + start_address_validation (&sender, q->address); + /* (2) Also try to see if we have addresses in PEERSTORE for this peer + we could use */ + for (ir = ir_head; NULL != ir; ir = ir->next) + if (0 == GNUNET_memcmp (&ir->pid, &sender)) + return; + /* we are already trying */ + ir = GNUNET_new (struct IncomingRequest); + ir->pid = sender; + GNUNET_CONTAINER_DLL_insert (ir_head, ir_tail, ir); + + ir->nc = GNUNET_PEERSTORE_hello_changed_notify (peerstore, + GNUNET_NO, + &handle_hello_for_incoming, + NULL); + ir_total++; + /* Bound attempts we do in parallel here, might otherwise get excessive */ + while (ir_total > MAX_INCOMING_REQUEST) + free_incoming_request (ir_head); +} + + +/** + * Closure for #check_known_challenge. + */ +struct CheckKnownChallengeContext +{ + /** + * Set to the challenge we are looking for. + */ + const struct GNUNET_CRYPTO_ChallengeNonceP *challenge; + + /** + * Set to a matching validation state, if one was found. + */ + struct ValidationState *vs; +}; + + +/** + * Test if the validation state in @a value matches the + * challenge from @a cls. + * + * @param cls a `struct CheckKnownChallengeContext` + * @param pid unused (must match though) + * @param value a `struct ValidationState` + * @return #GNUNET_OK if not matching, #GNUNET_NO if match found + */ +static int +check_known_challenge (void *cls, + const struct GNUNET_PeerIdentity *pid, + void *value) +{ + struct CheckKnownChallengeContext *ckac = cls; + struct ValidationState *vs = value; + + (void) pid; + if (0 != GNUNET_memcmp (&vs->challenge, ckac->challenge)) + return GNUNET_OK; + ckac->vs = vs; + return GNUNET_NO; +} + + +/** + * Function called when peerstore is done storing a + * validated address. + * + * @param cls a `struct ValidationState` + * @param success #GNUNET_YES on success + */ +static void +peerstore_store_validation_cb (void *cls, int success) +{ + struct ValidationState *vs = cls; + + vs->sc = NULL; + if (GNUNET_YES == success) + return; + GNUNET_STATISTICS_update (GST_stats, + "# Peerstore failed to store foreign address", + 1, + GNUNET_NO); +} + + +/** + * Find the queue matching @a pid and @a address. + * + * @param pid peer the queue must go to + * @param address address the queue must use + * @return NULL if no such queue exists + */ +static struct Queue * +find_queue (const struct GNUNET_PeerIdentity *pid, const char *address) +{ + struct Neighbour *n; + + n = lookup_neighbour (pid); + if (NULL == n) + return NULL; + for (struct Queue *pos = n->queue_head; NULL != pos; + pos = pos->next_neighbour) + { + if (0 == strcmp (pos->address, address)) + return pos; + } + return NULL; +} + + +/** + * Communicator gave us a transport address validation response. Process the + * request. + * + * @param cls a `struct CommunicatorMessageContext` (must call + * #finish_cmc_handling() when done) + * @param tvr the message that was received + */ +static void +handle_validation_response ( + void *cls, + const struct TransportValidationResponseMessage *tvr) +{ + struct CommunicatorMessageContext *cmc = cls; + struct ValidationState *vs; + struct CheckKnownChallengeContext ckac = { .challenge = &tvr->challenge, + .vs = NULL}; + struct GNUNET_TIME_Absolute origin_time; + struct Queue *q; + struct Neighbour *n; + struct VirtualLink *vl; + const struct GNUNET_TIME_Absolute now = GNUNET_TIME_absolute_get_monotonic (GST_cfg); + + /* check this is one of our challenges */ + (void) GNUNET_CONTAINER_multipeermap_get_multiple (validation_map, + &cmc->im.sender, + &check_known_challenge, + &ckac); + if (NULL == (vs = ckac.vs)) + { + /* This can happen simply if we 'forgot' the challenge by now, + i.e. because we received the validation response twice */ + GNUNET_STATISTICS_update (GST_stats, + "# Validations dropped, challenge unknown", + 1, + GNUNET_NO); + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Validation response %s dropped, challenge unknown\n", + GNUNET_sh2s (&tvr->challenge.value)); + finish_cmc_handling (cmc); + return; + } + + /* sanity check on origin time */ + origin_time = GNUNET_TIME_absolute_ntoh (tvr->origin_time); + if ((origin_time.abs_value_us < vs->first_challenge_use.abs_value_us) || + (origin_time.abs_value_us > vs->last_challenge_use.abs_value_us)) + { + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Diff first use %" PRIu64 " and last use %" PRIu64 "\n", + vs->first_challenge_use.abs_value_us - origin_time.abs_value_us, + origin_time.abs_value_us - vs->last_challenge_use.abs_value_us); + GNUNET_break_op (0); + finish_cmc_handling (cmc); + return; + } + + { + /* check signature */ + struct TransportValidationPS tvp = { + .purpose.purpose = htonl (GNUNET_SIGNATURE_PURPOSE_TRANSPORT_CHALLENGE), + .purpose.size = htonl (sizeof(tvp)), + .validity_duration = tvr->validity_duration, + .challenge = tvr->challenge + }; + + if ( + GNUNET_OK != + GNUNET_CRYPTO_eddsa_verify (GNUNET_SIGNATURE_PURPOSE_TRANSPORT_CHALLENGE, + &tvp, + &tvr->signature, + &cmc->im.sender.public_key)) + { + GNUNET_break_op (0); + finish_cmc_handling (cmc); + return; + } + } + + /* validity is capped by our willingness to keep track of the + validation entry and the maximum the other peer allows */ + vs->valid_until = GNUNET_TIME_relative_to_absolute ( + GNUNET_TIME_relative_min (GNUNET_TIME_relative_ntoh ( + tvr->validity_duration), + MAX_ADDRESS_VALID_UNTIL)); + vs->validated_until = + GNUNET_TIME_absolute_min (vs->valid_until, + GNUNET_TIME_relative_to_absolute ( + ADDRESS_VALIDATION_LIFETIME)); + vs->validation_rtt = GNUNET_TIME_absolute_get_duration (origin_time); + vs->challenge_backoff = GNUNET_TIME_UNIT_ZERO; + GNUNET_CRYPTO_random_block (GNUNET_CRYPTO_QUALITY_NONCE, + &vs->challenge, + sizeof(vs->challenge)); + vs->first_challenge_use = GNUNET_TIME_absolute_subtract ( + vs->validated_until, + GNUNET_TIME_relative_multiply (vs->validation_rtt, + VALIDATION_RTT_BUFFER_FACTOR)); + if (GNUNET_TIME_absolute_cmp (vs->first_challenge_use, <, now)) + { + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "First challenge use is now %" PRIu64 " %s \n", + vs->first_challenge_use.abs_value_us, + GNUNET_sh2s (&vs->challenge.value)); + vs->first_challenge_use = now; + } + else + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "First challenge use is later %" PRIu64 " %s \n", + vs->first_challenge_use.abs_value_us, + GNUNET_sh2s (&vs->challenge.value)); + vs->last_challenge_use = + GNUNET_TIME_UNIT_ZERO_ABS; /* challenge was not yet used */ + update_next_challenge_time (vs, vs->first_challenge_use); + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Validation response %s from %s accepted, address valid until %s\n", + GNUNET_sh2s (&tvr->challenge.value), + GNUNET_i2s (&cmc->im.sender), + GNUNET_STRINGS_absolute_time_to_string (vs->valid_until)); + vs->sc = GNUNET_PEERSTORE_store (peerstore, + "transport", + &cmc->im.sender, + GNUNET_PEERSTORE_TRANSPORT_URLADDRESS_KEY, + vs->address, + strlen (vs->address) + 1, + vs->valid_until, + GNUNET_PEERSTORE_STOREOPTION_MULTIPLE, + &peerstore_store_validation_cb, + vs); + finish_cmc_handling (cmc); + + /* Finally, we now possibly have a confirmed (!) working queue, + update queue status (if queue still is around) */ + q = find_queue (&vs->pid, vs->address); + if (NULL == q) + { + GNUNET_STATISTICS_update (GST_stats, + "# Queues lost at time of successful validation", + 1, + GNUNET_NO); + return; + } + q->validated_until = vs->validated_until; + q->pd.aged_rtt = vs->validation_rtt; + n = q->neighbour; + vl = lookup_virtual_link (&vs->pid); + if (NULL == vl) + { + vl = GNUNET_new (struct VirtualLink); + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Creating new virtual link %p to %s using direct neighbour!\n", + vl, + GNUNET_i2s (&vs->pid)); + vl->confirmed = GNUNET_YES; + vl->message_uuid_ctr = + GNUNET_CRYPTO_random_u64 (GNUNET_CRYPTO_QUALITY_WEAK, UINT64_MAX); + vl->target = n->pid; + vl->core_recv_window = RECV_WINDOW_SIZE; + vl->available_fc_window_size = DEFAULT_WINDOW_SIZE; + vl->incoming_fc_window_size = DEFAULT_WINDOW_SIZE; + GNUNET_break (GNUNET_YES == + GNUNET_CONTAINER_multipeermap_put ( + links, + &vl->target, + vl, + GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_ONLY)); + vl->n = n; + n->vl = vl; + q->idle = GNUNET_YES; + vl->visibility_task = + GNUNET_SCHEDULER_add_at (q->validated_until, &check_link_down, vl); + consider_sending_fc (vl); + /* We lacked a confirmed connection to the target + before, so tell CORE about it (finally!) */ + cores_send_connect_info (&n->pid); + send_msg_from_cache (vl); + } + else + { + /* Link was already up, remember n is also now available and we are done */ + if (NULL == vl->n) + { + vl->n = n; + n->vl = vl; + if (GNUNET_YES == vl->confirmed) + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Virtual link to %s could now also use direct neighbour!\n", + GNUNET_i2s (&vs->pid)); + } + else + { + GNUNET_assert (n == vl->n); + } + if (GNUNET_NO == vl->confirmed) + { + vl->confirmed = GNUNET_YES; + q->idle = GNUNET_YES; + vl->visibility_task = + GNUNET_SCHEDULER_add_at (q->validated_until, &check_link_down, vl); + consider_sending_fc (vl); + /* We lacked a confirmed connection to the target + before, so tell CORE about it (finally!) */ + cores_send_connect_info (&n->pid); + send_msg_from_cache (vl); + } + } +} + + +/** + * Incoming meessage. Process the request. + * + * @param im the send message that was received + */ +static void +handle_incoming_msg (void *cls, + const struct GNUNET_TRANSPORT_IncomingMessage *im) +{ + struct TransportClient *tc = cls; + struct CommunicatorMessageContext *cmc = + GNUNET_new (struct CommunicatorMessageContext); + + cmc->tc = tc; + cmc->im = *im; + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Received message with size %u and flow control id %" PRIu64 + " via communicator from peer %s\n", + ntohs (im->header.size), + im->fc_id, + GNUNET_i2s (&im->sender)); + cmc->im.neighbour_sender = cmc->im.sender; + cmc->mh = (const struct GNUNET_MessageHeader *) &im[1]; + demultiplex_with_cmc (cmc); +} + + +/** + * Communicator gave us a transport address validation response. Process the + * request. + * + * @param cls a `struct CommunicatorMessageContext` (must call + * #finish_cmc_handling() when done) + * @param fc the message that was received + */ +static void +handle_flow_control (void *cls, const struct TransportFlowControlMessage *fc) +{ + struct CommunicatorMessageContext *cmc = cls; + struct VirtualLink *vl; + uint32_t seq; + struct GNUNET_TIME_Absolute st; + uint64_t os; + uint64_t wnd; + uint32_t random; + + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Received FC from %s\n", GNUNET_i2s (&cmc->im.sender)); + vl = lookup_virtual_link (&cmc->im.sender); + if (NULL == vl) + { + vl = GNUNET_new (struct VirtualLink); + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "No virtual link for %p FC creating new unconfirmed virtual link to %s!\n", + vl, + GNUNET_i2s (&cmc->im.sender)); + vl->confirmed = GNUNET_NO; + vl->message_uuid_ctr = + GNUNET_CRYPTO_random_u64 (GNUNET_CRYPTO_QUALITY_WEAK, UINT64_MAX); + vl->target = cmc->im.sender; + vl->core_recv_window = RECV_WINDOW_SIZE; + vl->available_fc_window_size = DEFAULT_WINDOW_SIZE; + vl->incoming_fc_window_size = DEFAULT_WINDOW_SIZE; + GNUNET_break (GNUNET_YES == + GNUNET_CONTAINER_multipeermap_put ( + links, + &vl->target, + vl, + GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_ONLY)); + } + st = GNUNET_TIME_absolute_ntoh (fc->sender_time); + if (st.abs_value_us < vl->last_fc_timestamp.abs_value_us) + { + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "FC dropped: Message out of order\n"); + /* out of order, drop */ + GNUNET_STATISTICS_update (GST_stats, + "# FC dropped: message out of order", + 1, + GNUNET_NO); + finish_cmc_handling (cmc); + return; + } + seq = ntohl (fc->seq); + if (seq < vl->last_fc_seq) + { + /* Wrap-around/reset of other peer; start all counters from zero */ + vl->outbound_fc_window_size_used = 0; + } + vl->last_fc_seq = seq; + vl->last_fc_timestamp = st; + vl->outbound_fc_window_size = GNUNET_ntohll (fc->inbound_window_size); + os = GNUNET_ntohll (fc->outbound_sent); + vl->incoming_fc_window_size_loss = + (int64_t) (os - vl->incoming_fc_window_size_used); + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Received FC from %s, seq %u, new window %llu (loss at %lld)\n", + GNUNET_i2s (&vl->target), + (unsigned int) seq, + (unsigned long long) vl->outbound_fc_window_size, + (long long) vl->incoming_fc_window_size_loss); + wnd = GNUNET_ntohll (fc->outbound_window_size); + random = GNUNET_CRYPTO_random_u32 (GNUNET_CRYPTO_QUALITY_WEAK, + UINT32_MAX); + if ((GNUNET_YES == vl->confirmed) && ((wnd < vl->incoming_fc_window_size + + vl->incoming_fc_window_size_used + + vl->incoming_fc_window_size_loss) || + (vl->last_outbound_window_size_received + != wnd) || + (0 == random + % FC_NO_CHANGE_REPLY_PROBABILITY))) + { + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Consider re-sending our FC message, as clearly the other peer's idea of the window is not up-to-date (%llu vs %llu) or %llu last received differs, or random reply %u\n", + (unsigned long long) wnd, + (unsigned long long) vl->incoming_fc_window_size, + (unsigned long long) vl->last_outbound_window_size_received, + random % FC_NO_CHANGE_REPLY_PROBABILITY); + consider_sending_fc (vl); + } + if ((wnd == vl->incoming_fc_window_size) && + (vl->last_outbound_window_size_received == wnd) && + (NULL != vl->fc_retransmit_task)) + { + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Stopping FC retransmission to %s: peer is current at window %llu\n", + GNUNET_i2s (&vl->target), + (unsigned long long) wnd); + GNUNET_SCHEDULER_cancel (vl->fc_retransmit_task); + vl->fc_retransmit_task = NULL; + vl->fc_retransmit_count = 0; + } + vl->last_outbound_window_size_received = wnd; + /* FC window likely increased, check transmission possibilities! */ + check_vl_transmission (vl); + finish_cmc_handling (cmc); +} + + +/** + * Given an inbound message @a msg from a communicator @a cmc, + * demultiplex it based on the type calling the right handler. + * + * @param cmc context for demultiplexing + * @param msg message to demultiplex + */ +static void +demultiplex_with_cmc (struct CommunicatorMessageContext *cmc) +{ + struct GNUNET_MQ_MessageHandler handlers[] = + { GNUNET_MQ_hd_var_size (fragment_box, + GNUNET_MESSAGE_TYPE_TRANSPORT_FRAGMENT, + struct TransportFragmentBoxMessage, + cmc), + GNUNET_MQ_hd_var_size (reliability_box, + GNUNET_MESSAGE_TYPE_TRANSPORT_RELIABILITY_BOX, + struct TransportReliabilityBoxMessage, + cmc), + GNUNET_MQ_hd_var_size (reliability_ack, + GNUNET_MESSAGE_TYPE_TRANSPORT_RELIABILITY_ACK, + struct TransportReliabilityAckMessage, + cmc), + GNUNET_MQ_hd_var_size (backchannel_encapsulation, + GNUNET_MESSAGE_TYPE_TRANSPORT_BACKCHANNEL_ENCAPSULATION, + struct TransportBackchannelEncapsulationMessage, + cmc), + GNUNET_MQ_hd_var_size (dv_learn, + GNUNET_MESSAGE_TYPE_TRANSPORT_DV_LEARN, + struct TransportDVLearnMessage, + cmc), + GNUNET_MQ_hd_var_size (dv_box, + GNUNET_MESSAGE_TYPE_TRANSPORT_DV_BOX, + struct TransportDVBoxMessage, + cmc), + GNUNET_MQ_hd_fixed_size ( + validation_challenge, + GNUNET_MESSAGE_TYPE_TRANSPORT_ADDRESS_VALIDATION_CHALLENGE, + struct TransportValidationChallengeMessage, + cmc), + GNUNET_MQ_hd_fixed_size (flow_control, + GNUNET_MESSAGE_TYPE_TRANSPORT_FLOW_CONTROL, + struct TransportFlowControlMessage, + cmc), + GNUNET_MQ_hd_fixed_size ( + validation_response, + GNUNET_MESSAGE_TYPE_TRANSPORT_ADDRESS_VALIDATION_RESPONSE, + struct TransportValidationResponseMessage, + cmc), + GNUNET_MQ_handler_end () }; + int ret; + const struct GNUNET_MessageHeader *msg = cmc->mh; + + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Handling message of type %u with %u bytes\n", + (unsigned int) ntohs (msg->type), + (unsigned int) ntohs (msg->size)); + ret = GNUNET_MQ_handle_message (handlers, msg); + if (GNUNET_SYSERR == ret) + { + GNUNET_break (0); + GNUNET_SERVICE_client_drop (cmc->tc->client); + GNUNET_free (cmc); + return; + } + if (GNUNET_NO == ret) + { + /* unencapsulated 'raw' message */ + handle_raw_message (cmc, msg); + } +} + + +/** + * New queue became available. Check message. + * + * @param cls the client + * @param aqm the send message that was sent + */ +static int +check_add_queue_message (void *cls, + const struct GNUNET_TRANSPORT_AddQueueMessage *aqm) +{ + struct TransportClient *tc = cls; + + if (CT_COMMUNICATOR != tc->type) + { + GNUNET_break (0); + return GNUNET_SYSERR; + } + GNUNET_MQ_check_zero_termination (aqm); + return GNUNET_OK; +} + + +/** + * If necessary, generates the UUID for a @a pm + * + * @param pm pending message to generate UUID for. + */ +static void +set_pending_message_uuid (struct PendingMessage *pm) +{ + if (pm->msg_uuid_set) + return; + pm->msg_uuid.uuid = pm->vl->message_uuid_ctr++; + pm->msg_uuid_set = GNUNET_YES; +} + + +/** + * Setup data structure waiting for acknowledgements. + * + * @param queue queue the @a pm will be sent over + * @param dvh path the message will take, may be NULL + * @param pm the pending message for transmission + * @return corresponding fresh pending acknowledgement + */ +static struct PendingAcknowledgement * +prepare_pending_acknowledgement (struct Queue *queue, + struct DistanceVectorHop *dvh, + struct PendingMessage *pm) +{ + struct PendingAcknowledgement *pa; + + pa = GNUNET_new (struct PendingAcknowledgement); + pa->queue = queue; + pa->dvh = dvh; + pa->pm = pm; + do + { + GNUNET_CRYPTO_random_block (GNUNET_CRYPTO_QUALITY_NONCE, + &pa->ack_uuid, + sizeof(pa->ack_uuid)); + } + while (GNUNET_YES != GNUNET_CONTAINER_multiuuidmap_put ( + pending_acks, + &pa->ack_uuid.value, + pa, + GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_ONLY)); + GNUNET_CONTAINER_MDLL_insert (queue, queue->pa_head, queue->pa_tail, pa); + GNUNET_CONTAINER_MDLL_insert (pm, pm->pa_head, pm->pa_tail, pa); + if (NULL != dvh) + GNUNET_CONTAINER_MDLL_insert (dvh, dvh->pa_head, dvh->pa_tail, pa); + pa->transmission_time = GNUNET_TIME_absolute_get (); + pa->message_size = pm->bytes_msg; + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Waiting for ACKnowledgment `%s' for <%llu>\n", + GNUNET_uuid2s (&pa->ack_uuid.value), + pm->logging_uuid); + return pa; +} + + +/** + * Fragment the given @a pm to the given @a mtu. Adds + * additional fragments to the neighbour as well. If the + * @a mtu is too small, generates and error for the @a pm + * and returns NULL. + * + * @param queue which queue to fragment for + * @param dvh path the message will take, or NULL + * @param pm pending message to fragment for transmission + * @return new message to transmit + */ +static struct PendingMessage * +fragment_message (struct Queue *queue, + struct DistanceVectorHop *dvh, + struct PendingMessage *pm) +{ + struct PendingAcknowledgement *pa; + struct PendingMessage *ff; + uint16_t mtu; + uint16_t msize; + + mtu = (UINT16_MAX == queue->mtu) + ? UINT16_MAX - sizeof(struct GNUNET_TRANSPORT_SendMessageTo) + : queue->mtu; + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Fragmenting message <%llu> with size %u to %s for MTU %u\n", + pm->logging_uuid, + pm->bytes_msg, + GNUNET_i2s (&pm->vl->target), + (unsigned int) mtu); + set_pending_message_uuid (pm); + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Fragmenting message %llu <%llu> with size %u to %s for MTU %u\n", + (unsigned long long) pm->msg_uuid.uuid, + pm->logging_uuid, + pm->bytes_msg, + GNUNET_i2s (&pm->vl->target), + (unsigned int) mtu); + + /* This invariant is established in #handle_add_queue_message() */ + GNUNET_assert (mtu > sizeof(struct TransportFragmentBoxMessage)); + + /* select fragment for transmission, descending the tree if it has + been expanded until we are at a leaf or at a fragment that is small + enough + */ + ff = pm; + msize = ff->bytes_msg; + + while (((ff->bytes_msg > mtu) || (pm == ff)) && + (ff->frag_off == msize) && (NULL != ff->head_frag)) + { + ff = ff->head_frag; /* descent into fragmented fragments */ + msize = ff->bytes_msg - sizeof(struct TransportFragmentBoxMessage); + } + + if (((ff->bytes_msg > mtu) || (pm == ff)) && (ff->frag_off < msize)) + { + /* Did not yet calculate all fragments, calculate next fragment */ + struct PendingMessage *frag; + struct TransportFragmentBoxMessage tfb; + const char *orig; + char *msg; + uint16_t fragmax; + uint16_t fragsize; + uint16_t msize; + uint16_t xoff = 0; + + orig = (const char *) &ff[1]; + msize = ff->bytes_msg; + if (pm != ff) + { + const struct TransportFragmentBoxMessage *tfbo; + + tfbo = (const struct TransportFragmentBoxMessage *) orig; + orig += sizeof(struct TransportFragmentBoxMessage); + msize -= sizeof(struct TransportFragmentBoxMessage); + xoff = ntohs (tfbo->frag_off); + } + fragmax = mtu - sizeof(struct TransportFragmentBoxMessage); + fragsize = GNUNET_MIN (msize - ff->frag_off, fragmax); + frag = + GNUNET_malloc (sizeof(struct PendingMessage) + + sizeof(struct TransportFragmentBoxMessage) + fragsize); + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "3 created pm %p from pm %p storing vl %p from pm %p\n", + frag, + ff, + pm->vl, + pm); + frag->logging_uuid = logging_uuid_gen++; + frag->vl = pm->vl; + frag->frag_parent = ff; + frag->timeout = pm->timeout; + frag->bytes_msg = sizeof(struct TransportFragmentBoxMessage) + fragsize; + frag->pmt = PMT_FRAGMENT_BOX; + msg = (char *) &frag[1]; + tfb.header.type = htons (GNUNET_MESSAGE_TYPE_TRANSPORT_FRAGMENT); + tfb.header.size = + htons (sizeof(struct TransportFragmentBoxMessage) + fragsize); + pa = prepare_pending_acknowledgement (queue, dvh, frag); + tfb.ack_uuid = pa->ack_uuid; + tfb.msg_uuid = pm->msg_uuid; + tfb.frag_off = htons (ff->frag_off + xoff); + tfb.msg_size = htons (pm->bytes_msg); + memcpy (msg, &tfb, sizeof(tfb)); + memcpy (&msg[sizeof(tfb)], &orig[ff->frag_off], fragsize); + GNUNET_CONTAINER_MDLL_insert (frag, ff->head_frag, + ff->tail_frag, frag); + ff->frag_off += fragsize; + ff = frag; + } + + /* Move head to the tail and return it */ + GNUNET_CONTAINER_MDLL_remove (frag, + ff->frag_parent->head_frag, + ff->frag_parent->tail_frag, + ff); + GNUNET_CONTAINER_MDLL_insert_tail (frag, + ff->frag_parent->head_frag, + ff->frag_parent->tail_frag, + ff); + + return ff; +} + + +/** + * Reliability-box the given @a pm. On error (can there be any), NULL + * may be returned, otherwise the "replacement" for @a pm (which + * should then be added to the respective neighbour's queue instead of + * @a pm). If the @a pm is already fragmented or reliability boxed, + * or itself an ACK, this function simply returns @a pm. + * + * @param queue which queue to prepare transmission for + * @param dvh path the message will take, or NULL + * @param pm pending message to box for transmission over unreliabile queue + * @return new message to transmit + */ +static struct PendingMessage * +reliability_box_message (struct Queue *queue, + struct DistanceVectorHop *dvh, + struct PendingMessage *pm) +{ + struct TransportReliabilityBoxMessage rbox; + struct PendingAcknowledgement *pa; + struct PendingMessage *bpm; + char *msg; + + if ((PMT_CORE != pm->pmt) && (PMT_DV_BOX != pm->pmt)) + return pm; /* already fragmented or reliability boxed, or control message: + do nothing */ + if (NULL != pm->bpm) + return pm->bpm; /* already computed earlier: do nothing */ + // TODO I guess we do not need this assertion. We might have a DLL with + // fragments, because the MTU changed, and we do not need to fragment anymore. + // But we should keep the fragments until message was completed, because + // the MTU might change again. + // GNUNET_assert (NULL == pm->head_frag); + if (pm->bytes_msg + sizeof(rbox) > UINT16_MAX) + { + /* failed hard */ + GNUNET_break (0); + client_send_response (pm); + return NULL; + } + + pa = prepare_pending_acknowledgement (queue, dvh, pm); + + bpm = GNUNET_malloc (sizeof(struct PendingMessage) + sizeof(rbox) + + pm->bytes_msg); + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "4 created pm %p storing vl %p from pm %p\n", + bpm, + pm->vl, + pm); + bpm->logging_uuid = logging_uuid_gen++; + bpm->vl = pm->vl; + bpm->frag_parent = pm; + // Why was this needed? + // GNUNET_CONTAINER_MDLL_insert (frag, pm->head_frag, pm->tail_frag, bpm); + bpm->timeout = pm->timeout; + bpm->pmt = PMT_RELIABILITY_BOX; + bpm->bytes_msg = pm->bytes_msg + sizeof(rbox); + set_pending_message_uuid (bpm); + rbox.header.type = htons (GNUNET_MESSAGE_TYPE_TRANSPORT_RELIABILITY_BOX); + rbox.header.size = htons (sizeof(rbox) + pm->bytes_msg); + rbox.ack_countdown = htonl (0); // FIXME: implement ACK countdown support + + rbox.ack_uuid = pa->ack_uuid; + msg = (char *) &bpm[1]; + memcpy (msg, &rbox, sizeof(rbox)); + memcpy (&msg[sizeof(rbox)], &pm[1], pm->bytes_msg); + pm->bpm = bpm; + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Preparing reliability box for message <%llu> of size %d (%d) to %s on queue %s\n", + pm->logging_uuid, + pm->bytes_msg, + ntohs (((const struct GNUNET_MessageHeader *) &pm[1])->size), + GNUNET_i2s (&pm->vl->target), + queue->address); + return bpm; +} + + +static void +reorder_root_pm (struct PendingMessage *pm, + struct GNUNET_TIME_Absolute next_attempt) +{ + struct VirtualLink *vl = pm->vl; + struct PendingMessage *pos; + + /* re-insert sort in neighbour list */ + GNUNET_CONTAINER_MDLL_remove (vl, + vl->pending_msg_head, + vl->pending_msg_tail, + pm); + pos = vl->pending_msg_tail; + while ((NULL != pos) && + (next_attempt.abs_value_us > pos->next_attempt.abs_value_us)) + pos = pos->prev_vl; + GNUNET_CONTAINER_MDLL_insert_after (vl, + vl->pending_msg_head, + vl->pending_msg_tail, + pos, + pm); +} + + +static unsigned int +check_next_attempt_tree (struct PendingMessage *pm, + struct GNUNET_TIME_Absolute next_attempt) +{ + struct PendingMessage *pos; + + pos = pm->head_frag; + while (NULL != pos) + { + if (pos->next_attempt.abs_value_us != next_attempt.abs_value_us || + GNUNET_YES == check_next_attempt_tree (pos, next_attempt)) + return GNUNET_YES; + pos = pos->next_frag; + } + + return GNUNET_NO; +} + + +/** + * Change the value of the `next_attempt` field of @a pm + * to @a next_attempt and re-order @a pm in the transmission + * list as required by the new timestamp. + * + * @param pm a pending message to update + * @param next_attempt timestamp to use + */ +static void +update_pm_next_attempt (struct PendingMessage *pm, + struct GNUNET_TIME_Absolute next_attempt) +{ + if (NULL == pm->frag_parent) + { + pm->next_attempt = next_attempt; + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Next attempt for message <%" PRIu64 "> set to %" PRIu64 "\n", + pm->logging_uuid, + next_attempt.abs_value_us); + reorder_root_pm (pm, next_attempt); + } + else if ((PMT_RELIABILITY_BOX == pm->pmt) || (PMT_DV_BOX == pm->pmt))// || (PMT_FRAGMENT_BOX == pm->pmt)) + { + struct PendingMessage *root = pm->frag_parent; + + while (NULL != root->frag_parent) + root = root->frag_parent; + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Next attempt for root message <%" PRIu64 "> set to %s\n", + root->logging_uuid, + GNUNET_STRINGS_absolute_time_to_string (next_attempt)); + root->next_attempt = next_attempt; + reorder_root_pm (root, next_attempt); + } + else + { + struct PendingMessage *root = pm->frag_parent; + + while (NULL != root->frag_parent) + root = root->frag_parent; + + if (GNUNET_NO == root->frags_in_flight) + { + root->next_attempt = next_attempt; + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Next attempt for fragmented message <%" PRIu64 "> (<%" PRIu64 + ">)set to %" PRIu64 "\n", + pm->logging_uuid, + root->logging_uuid, + next_attempt.abs_value_us); + } + + pm->next_attempt = root->next_attempt; + + if (root->bytes_msg == root->frag_off) + root->frags_in_flight = check_next_attempt_tree (root, + root->next_attempt); + else + root->frags_in_flight = GNUNET_YES; + + if (GNUNET_NO == root->frags_in_flight) + { + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "We have no fragments in flight for message %" PRIu64 + ", reorder root! Next attempt is %" PRIu64 "\n", + root->logging_uuid, + root->next_attempt.abs_value_us); + reorder_root_pm (root, root->next_attempt); + root->frag_count = 0; + root->next_attempt = GNUNET_TIME_UNIT_ZERO_ABS; + } + else + { + double factor = (root->frag_count - 1) / root->frag_count; + struct GNUNET_TIME_Relative s1; + struct GNUNET_TIME_Relative s2; + struct GNUNET_TIME_Relative plus_mean = + GNUNET_TIME_absolute_get_duration (root->next_attempt); + struct GNUNET_TIME_Relative plus = GNUNET_TIME_absolute_get_duration ( + next_attempt); + + s1 = GNUNET_TIME_relative_multiply (plus_mean, + factor); + s2 = GNUNET_TIME_relative_divide (plus, + root->frag_count); + plus_mean = GNUNET_TIME_relative_add (s1, s2); + root->next_attempt = GNUNET_TIME_relative_to_absolute (plus_mean); + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "We have fragments in flight for message %" PRIu64 + ", do not reorder root! Actual next attempt %" PRIu64 "\n", + root->logging_uuid, + root->next_attempt.abs_value_us); + } + } +} + + +/** + * Context for #select_best_pending_from_link(). + */ +struct PendingMessageScoreContext +{ + /** + * Set to the best message that was found, NULL for none. + */ + struct PendingMessage *best; + + /** + * DVH that @e best should take, or NULL for direct transmission. + */ + struct DistanceVectorHop *dvh; + + /** + * What is the estimated total overhead for this message? + */ + size_t real_overhead; + + /** + * Number of pending messages we seriously considered this time. + */ + unsigned int consideration_counter; + + /** + * Did we have to fragment? + */ + int frag; + + /** + * Did we have to reliability box? + */ + int relb; + + /** + * There are pending messages, but it was to early to send one of them. + */ + int to_early; + + /** + * There is a pending messages we are sending fragments at the moment. + */ + unsigned int frags_in_flight; + + /** + * When will we try to transmit the message again for which it was to early to retry. + */ + struct GNUNET_TIME_Relative to_early_retry_delay; +}; + + +/** + * Select the best pending message from @a vl for transmission + * via @a queue. + * + * @param[in,out] sc best message so far (NULL for none), plus scoring data + * @param queue the queue that will be used for transmission + * @param vl the virtual link providing the messages + * @param dvh path we are currently considering, or NULL for none + * @param overhead number of bytes of overhead to be expected + * from DV encapsulation (0 for without DV) + */ +static void +select_best_pending_from_link (struct PendingMessageScoreContext *sc, + struct Queue *queue, + struct VirtualLink *vl, + struct DistanceVectorHop *dvh, + size_t overhead) +{ + struct GNUNET_TIME_Absolute now; + + now = GNUNET_TIME_absolute_get (); + sc->to_early = GNUNET_NO; + sc->frags_in_flight = GNUNET_NO; + for (struct PendingMessage *pos = vl->pending_msg_head; NULL != pos; + pos = pos->next_vl) + { + size_t real_overhead = overhead; + int frag; + int relb; + + if ((NULL != dvh) && (PMT_DV_BOX == pos->pmt)) + { + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "DV messages must not be DV-routed to next hop!\n"); + continue; /* DV messages must not be DV-routed to next hop! */ + } + if (pos->next_attempt.abs_value_us > now.abs_value_us) + { + if (GNUNET_YES == pos->frags_in_flight) + { + sc->frags_in_flight = GNUNET_YES; + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Fragments in flight for message %llu\n", + pos->logging_uuid); + } + else + { + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Maybe too early, because message are sorted by next_attempt, if there are no fragments in flight.Checked message %llu\n", + pos->logging_uuid); + sc->to_early = GNUNET_YES; + sc->to_early_retry_delay = GNUNET_TIME_absolute_get_remaining ( + pos->next_attempt); + continue; + } + // break; /* too early for all messages, they are sorted by next_attempt */ + } + if (NULL != pos->qe) + { + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "not eligible\n"); + continue; /* not eligible */ + } + sc->consideration_counter++; + /* determine if we have to fragment, if so add fragmentation + overhead! */ + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "check %llu for sc->best\n", + pos->logging_uuid); + frag = GNUNET_NO; + if (((0 != queue->mtu) && + (pos->bytes_msg + real_overhead > queue->mtu)) || + (pos->bytes_msg > UINT16_MAX - sizeof(struct + GNUNET_TRANSPORT_SendMessageTo)) + || + (NULL != pos->head_frag /* fragments already exist, should + respect that even if MTU is UINT16_MAX for + this queue */)) + { + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "fragment msg with size %u, realoverhead is %lu\n", + pos->bytes_msg, + real_overhead); + frag = GNUNET_YES; + if (GNUNET_TRANSPORT_CC_RELIABLE == queue->tc->details.communicator.cc) + { + /* FIXME-FRAG-REL-UUID: we could use an optimized, shorter fragmentation + header without the ACK UUID when using a *reliable* channel! */ + } + real_overhead = overhead + sizeof(struct TransportFragmentBoxMessage); + } + /* determine if we have to reliability-box, if so add reliability box + overhead */ + relb = GNUNET_NO; + if ((GNUNET_NO == frag) && + (0 == (pos->prefs & GNUNET_MQ_PREF_UNRELIABLE)) && + (GNUNET_TRANSPORT_CC_RELIABLE != queue->tc->details.communicator.cc)) + { + real_overhead += sizeof(struct TransportReliabilityBoxMessage); + + if ((0 != queue->mtu) && (pos->bytes_msg + real_overhead > queue->mtu)) + { + frag = GNUNET_YES; + real_overhead = overhead + sizeof(struct TransportFragmentBoxMessage); + } + else + { + relb = GNUNET_YES; + } + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Create reliability box of msg with size %u, realoverhead is %lu %u %u %u\n", + pos->bytes_msg, + real_overhead, + queue->mtu, + frag, + relb); + } + + /* Finally, compare to existing 'best' in sc to see if this 'pos' pending + message would beat it! */ + if (GNUNET_NO == sc->frags_in_flight && NULL != sc->best) + { + /* CHECK if pos fits queue BETTER (=smaller) than pm, if not: continue; + OPTIMIZE-ME: This is a heuristic, which so far has NOT been + experimentally validated. There may be some huge potential for + improvement here. Also, we right now only compare how well the + given message fits _this_ queue, and do not consider how well other + queues might suit the message. Taking other queues into consideration + may further improve the result, but could also be expensive + in terms of CPU time. */ + long long sc_score = sc->frag * 40 + sc->relb * 20 + sc->real_overhead; + long long pm_score = frag * 40 + relb * 20 + real_overhead; + long long time_delta = + (sc->best->next_attempt.abs_value_us - pos->next_attempt.abs_value_us) + / 1000LL; + + /* "time_delta" considers which message has been 'ready' for transmission + for longer, if a message has a preference for low latency, increase + the weight of the time_delta by 10x if it is favorable for that message */ + if ((0 != (pos->prefs & GNUNET_MQ_PREF_LOW_LATENCY)) && + (0 != (sc->best->prefs & GNUNET_MQ_PREF_LOW_LATENCY))) + time_delta *= 10; /* increase weight (always, both are low latency) */ + else if ((0 != (pos->prefs & GNUNET_MQ_PREF_LOW_LATENCY)) && + (time_delta > 0)) + time_delta *= 10; /* increase weight, favors 'pos', which is low latency */ + else if ((0 != (sc->best->prefs & GNUNET_MQ_PREF_LOW_LATENCY)) && + (time_delta < 0)) + time_delta *= 10; /* increase weight, favors 'sc->best', which is low latency */ + if (0 != queue->mtu) + { + /* Grant bonus if we are below MTU, larger bonus the closer we will + be to the MTU */ + if (queue->mtu > sc->real_overhead + sc->best->bytes_msg) + sc_score -= queue->mtu - (sc->real_overhead + sc->best->bytes_msg); + if (queue->mtu > real_overhead + pos->bytes_msg) + pm_score -= queue->mtu - (real_overhead + pos->bytes_msg); + } + if (sc_score + time_delta > pm_score) + { + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "sc_score of %llu larger, keep sc->best %llu\n", + pos->logging_uuid, + sc->best->logging_uuid); + continue; /* sc_score larger, keep sc->best */ + } + } + sc->best = pos; + sc->dvh = dvh; + sc->frag = frag; + sc->relb = relb; + sc->real_overhead = real_overhead; + } +} + + +/** + * Function to call to further operate on the now DV encapsulated + * message @a hdr, forwarding it via @a next_hop under respect of + * @a options. + * + * @param cls a `struct PendingMessageScoreContext` + * @param next_hop next hop of the DV path + * @param hdr encapsulated message, technically a `struct TransportDVBoxMessage` + * @param options options of the original message + */ +static void +extract_box_cb (void *cls, + struct Neighbour *next_hop, + const struct GNUNET_MessageHeader *hdr, + enum RouteMessageOptions options) +{ + struct PendingMessageScoreContext *sc = cls; + struct PendingMessage *pm = sc->best; + struct PendingMessage *bpm; + uint16_t bsize = ntohs (hdr->size); + + GNUNET_assert (NULL == pm->bpm); + bpm = GNUNET_malloc (sizeof(struct PendingMessage) + bsize); + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "5 created pm %p storing vl %p from pm %p\n", + bpm, + pm->vl, + pm); + bpm->logging_uuid = logging_uuid_gen++; + bpm->pmt = PMT_DV_BOX; + bpm->vl = pm->vl; + bpm->timeout = pm->timeout; + bpm->bytes_msg = bsize; + bpm->frag_parent = pm; + set_pending_message_uuid (bpm); + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Creating DV Box %llu for original message %llu (next hop is %s)\n", + bpm->logging_uuid, + pm->logging_uuid, + GNUNET_i2s (&next_hop->pid)); + memcpy (&bpm[1], hdr, bsize); + pm->bpm = bpm; +} + + +/** + * We believe we are ready to transmit a `struct PendingMessage` on a + * queue, the big question is which one! We need to see if there is + * one pending that is allowed by flow control and congestion control + * and (ideally) matches our queue's performance profile. + * + * If such a message is found, we give the message to the communicator + * for transmission (updating the tracker, and re-scheduling ourselves + * if applicable). + * + * If no such message is found, the queue's `idle` field must be set + * to #GNUNET_YES. + * + * @param cls the `struct Queue` to process transmissions for + */ +static void +transmit_on_queue (void *cls) +{ + struct Queue *queue = cls; + struct Neighbour *n = queue->neighbour; + struct PendingMessageScoreContext sc; + struct PendingMessage *pm; + + queue->transmit_task = NULL; + if (NULL == n->vl) + { + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Virtual link `%s' is down, cannot have PM for queue `%s'\n", + GNUNET_i2s (&n->pid), + queue->address); + queue->idle = GNUNET_YES; + return; + } + memset (&sc, 0, sizeof(sc)); + select_best_pending_from_link (&sc, queue, n->vl, NULL, 0); + if (NULL == sc.best) + { + /* Also look at DVH that have the n as first hop! */ + for (struct DistanceVectorHop *dvh = n->dv_head; NULL != dvh; + dvh = dvh->next_neighbour) + { + select_best_pending_from_link (&sc, + queue, + dvh->dv->vl, + dvh, + sizeof(struct GNUNET_PeerIdentity) + * (1 + dvh->distance) + + sizeof(struct TransportDVBoxMessage) + + sizeof(struct TransportDVBoxPayloadP)); + } + } + if (NULL == sc.best) + { + /* no message pending, nothing to do here! */ + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "No pending messages, queue `%s' to %s now idle\n", + queue->address, + GNUNET_i2s (&n->pid)); + if (GNUNET_YES == sc.to_early) + schedule_transmit_on_queue (sc.to_early_retry_delay, + queue, + GNUNET_SCHEDULER_PRIORITY_DEFAULT); + queue->idle = GNUNET_YES; + return; + } + /* There is a message pending, we are certainly not idle */ + queue->idle = GNUNET_NO; + + /* Given selection in `sc`, do transmission */ + pm = sc.best; + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Selected message <%llu>\n", + pm->logging_uuid); + if (NULL != sc.dvh) + { + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Is this %u a DV box?\n", + pm->pmt); + GNUNET_assert (PMT_DV_BOX != pm->pmt); + if ((NULL != sc.best->bpm) && (sc.best->bpm->used_dvh != sc.dvh)) + { + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Discard old box, because we have a new DV path.\n"); + free_pending_message (sc.best->bpm); + sc.best->bpm = NULL; + } + + if (NULL == sc.best->bpm) + { + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "encapsulate_for_dv 2\n"); + encapsulate_for_dv (sc.dvh->dv, + 1, + &sc.dvh, + (const struct GNUNET_MessageHeader *) &sc.best[1], + &extract_box_cb, + &sc, + RMO_NONE, + GNUNET_NO); + GNUNET_assert (NULL != sc.best->bpm); + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "%lu %lu %lu %lu %u\n", + sizeof(struct GNUNET_PeerIdentity), + sizeof(struct TransportDVBoxMessage), + sizeof(struct TransportDVBoxPayloadP), + sizeof(struct TransportFragmentBoxMessage), + ((const struct GNUNET_MessageHeader *) &sc.best[1])->size); + sc.best->bpm->used_dvh = sc.dvh; + } + pm = sc.best->bpm; + } + if (GNUNET_YES == sc.frag) + { + pm = fragment_message (queue, sc.dvh, pm); + if (NULL == pm) + { + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Fragmentation failed queue %s to %s for <%llu>, trying again\n", + queue->address, + GNUNET_i2s (&n->pid), + sc.best->logging_uuid); + schedule_transmit_on_queue (GNUNET_TIME_UNIT_ZERO, + queue, + GNUNET_SCHEDULER_PRIORITY_DEFAULT); + return; + } + } + else if (GNUNET_YES == sc.relb) + { + pm = reliability_box_message (queue, sc.dvh, pm); + if (NULL == pm) + { + /* Reliability boxing failed, try next message... */ + GNUNET_log ( + GNUNET_ERROR_TYPE_DEBUG, + "Reliability boxing failed queue %s to %s for <%llu>, trying again\n", + queue->address, + GNUNET_i2s (&n->pid), + sc.best->logging_uuid); + schedule_transmit_on_queue (GNUNET_TIME_UNIT_ZERO, + queue, + GNUNET_SCHEDULER_PRIORITY_DEFAULT); + return; + } + } + + /* Pass 'pm' for transission to the communicator */ + GNUNET_log ( + GNUNET_ERROR_TYPE_DEBUG, + "Passing message <%llu> to queue %s for peer %s (considered %u others)\n", + pm->logging_uuid, + queue->address, + GNUNET_i2s (&n->pid), + sc.consideration_counter); + + /* Flow control: increment amount of traffic sent; if we are routing + via DV (and thus the ultimate target of the pending message is for + a different virtual link than the one of the queue), then we need + to use up not only the window of the direct link but also the + flow control window for the DV link! */ + pm->vl->outbound_fc_window_size_used += pm->bytes_msg; + + if (pm->vl != queue->neighbour->vl) + { + /* If the virtual link of the queue differs, this better be distance + vector routing! */ + GNUNET_assert (NULL != sc.dvh); + /* If we do distance vector routing, we better not do this for a + message that was itself DV-routed */ + GNUNET_assert (PMT_DV_BOX != sc.best->pmt); + /* We use the size of the unboxed message here, to avoid counting + the DV-Box header which is eaten up on the way by intermediaries */ + queue->neighbour->vl->outbound_fc_window_size_used += sc.best->bytes_msg; + } + else + { + GNUNET_assert (NULL == sc.dvh); + } + + queue_send_msg (queue, pm, &pm[1], pm->bytes_msg); + + /* Check if this transmission somehow conclusively finished handing 'pm' + even without any explicit ACKs */ + if ((PMT_CORE == pm->pmt) || + (GNUNET_TRANSPORT_CC_RELIABLE == queue->tc->details.communicator.cc)) + { + completed_pending_message (pm); + } + else + { + struct GNUNET_TIME_Relative wait_duration; + unsigned int wait_multiplier; + + if (PMT_FRAGMENT_BOX == pm->pmt) + { + struct PendingMessage *root; + + root = pm->frag_parent; + while (NULL != root->frag_parent) + root = root->frag_parent; + + root->frag_count++; + wait_multiplier = (unsigned int) ceil (root->bytes_msg + / (root->frag_off + / root->frag_count)) * 4; + } + else + { + // No fragments, we use 4 RTT before retransmitting. + wait_multiplier = 4; + } + + // Depending on how much pending message the VirtualLink is queueing, we wait longer. + // wait_multiplier = wait_multiplier * pm->vl->pending_msg_num; + + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Wait multiplier %u\n", + wait_multiplier); + + /* Message not finished, waiting for acknowledgement. + Update time by which we might retransmit 's' based on queue + characteristics (i.e. RTT); it takes one RTT for the message to + arrive and the ACK to come back in the best case; but the other + side is allowed to delay ACKs by 2 RTTs, so we use 4 RTT before + retransmitting. + + OPTIMIZE: Note that in the future this heuristic should likely + be improved further (measure RTT stability, consider message + urgency and size when delaying ACKs, etc.) */ + + if (GNUNET_TIME_UNIT_FOREVER_REL.rel_value_us != + queue->pd.aged_rtt.rel_value_us) + wait_duration = queue->pd.aged_rtt; + else + { + wait_duration = DEFAULT_ACK_WAIT_DURATION; + wait_multiplier = 4; + } + struct GNUNET_TIME_Absolute next = GNUNET_TIME_relative_to_absolute ( + GNUNET_TIME_relative_multiply ( + wait_duration, wait_multiplier)); + struct GNUNET_TIME_Relative plus = GNUNET_TIME_relative_multiply ( + wait_duration, wait_multiplier); + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Waiting %s (%s) for ACK until %s\n", + GNUNET_STRINGS_relative_time_to_string ( + GNUNET_TIME_relative_multiply ( + queue->pd.aged_rtt, wait_multiplier), GNUNET_NO), + GNUNET_STRINGS_relative_time_to_string (plus, GNUNET_YES), + GNUNET_STRINGS_absolute_time_to_string (next)); + update_pm_next_attempt (pm, + GNUNET_TIME_relative_to_absolute ( + GNUNET_TIME_relative_multiply (wait_duration, + wait_multiplier))); + } + /* finally, re-schedule queue transmission task itself */ + schedule_transmit_on_queue (GNUNET_TIME_UNIT_ZERO, + queue, + GNUNET_SCHEDULER_PRIORITY_DEFAULT); +} + + +/** + * Queue to a peer went down. Process the request. + * + * @param cls the client + * @param dqm the send message that was sent + */ +static void +handle_del_queue_message (void *cls, + const struct GNUNET_TRANSPORT_DelQueueMessage *dqm) +{ + struct TransportClient *tc = cls; + + if (CT_COMMUNICATOR != tc->type) + { + GNUNET_break (0); + GNUNET_SERVICE_client_drop (tc->client); + return; + } + for (struct Queue *queue = tc->details.communicator.queue_head; NULL != queue; + queue = queue->next_client) + { + struct Neighbour *neighbour = queue->neighbour; + + if ((ntohl (dqm->qid) != queue->qid) || + (0 != GNUNET_memcmp (&dqm->receiver, &neighbour->pid))) + continue; + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Dropped queue %s to peer %s\n", + queue->address, + GNUNET_i2s (&neighbour->pid)); + free_queue (queue); + GNUNET_SERVICE_client_continue (tc->client); + return; + } + GNUNET_break (0); + GNUNET_SERVICE_client_drop (tc->client); +} + + +/** + * Message was transmitted. Process the request. + * + * @param cls the client + * @param sma the send message that was sent + */ +static void +handle_send_message_ack (void *cls, + const struct GNUNET_TRANSPORT_SendMessageToAck *sma) +{ + struct TransportClient *tc = cls; + struct QueueEntry *qe; + struct PendingMessage *pm; + + if (CT_COMMUNICATOR != tc->type) + { + GNUNET_break (0); + GNUNET_SERVICE_client_drop (tc->client); + return; + } + + /* find our queue entry matching the ACK */ + qe = NULL; + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Looking for queue for PID %s\n", + GNUNET_i2s (&sma->receiver)); + for (struct Queue *queue = tc->details.communicator.queue_head; NULL != queue; + queue = queue->next_client) + { + if (0 != GNUNET_memcmp (&queue->neighbour->pid, &sma->receiver)) + continue; + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Found PID %s\n", + GNUNET_i2s (&queue->neighbour->pid)); + + + for (struct QueueEntry *qep = queue->queue_head; NULL != qep; + qep = qep->next) + { + if (qep->mid != GNUNET_ntohll (sma->mid) || queue->qid != ntohl ( + sma->qid)) + continue; + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "QueueEntry MID: %" PRIu64 " on queue QID: %u, Ack MID: %" + PRIu64 " Ack QID %u\n", + qep->mid, + queue->qid, + GNUNET_ntohll (sma->mid), + ntohl (sma->qid)); + qe = qep; + if ((NULL != qe->pm) && (qe->pm->qe != qe)) + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "For pending message %" PRIu64 " we had retransmissions.\n", + qe->pm->logging_uuid); + break; + } + } + if (NULL == qe) + { + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "No QueueEntry found for Ack MID %" PRIu64 " QID: %u\n", + GNUNET_ntohll (sma->mid), + ntohl (sma->qid)); + // TODO I guess this can happen, if the Ack from the peer comes before the Ack from the queue. + // Update: Maybe QueueEntry was accidentally freed during freeing PendingMessage. + /* this should never happen */ + GNUNET_break (0); + //GNUNET_SERVICE_client_drop (tc->client); + GNUNET_SERVICE_client_continue (tc->client); + return; + } + GNUNET_CONTAINER_DLL_remove (qe->queue->queue_head, + qe->queue->queue_tail, + qe); + qe->queue->queue_length--; + tc->details.communicator.total_queue_length--; + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Received ACK on queue %s to peer %s (new length: %u/%u)\n", + qe->queue->address, + GNUNET_i2s (&qe->queue->neighbour->pid), + qe->queue->queue_length, + tc->details.communicator.total_queue_length); + GNUNET_SERVICE_client_continue (tc->client); + + /* if applicable, resume transmissions that waited on ACK */ + if (COMMUNICATOR_TOTAL_QUEUE_LIMIT - 1 == + tc->details.communicator.total_queue_length) + { + /* Communicator dropped below threshold, resume all queues + incident with this client! */ + GNUNET_STATISTICS_update ( + GST_stats, + "# Transmission throttled due to communicator queue limit", + -1, + GNUNET_NO); + for (struct Queue *queue = tc->details.communicator.queue_head; + NULL != queue; + queue = queue->next_client) + { + schedule_transmit_on_queue (GNUNET_TIME_UNIT_ZERO, + queue, + GNUNET_SCHEDULER_PRIORITY_DEFAULT); + } + } + else if (QUEUE_LENGTH_LIMIT - 1 == qe->queue->queue_length) + { + /* queue dropped below threshold; only resume this one queue */ + GNUNET_STATISTICS_update (GST_stats, + "# Transmission throttled due to queue queue limit", + -1, + GNUNET_NO); + schedule_transmit_on_queue (GNUNET_TIME_UNIT_ZERO, + qe->queue, + GNUNET_SCHEDULER_PRIORITY_DEFAULT); + } + else if (1 == qe->queue->q_capacity) + { + // TODO I guess this will never happen, because the communicator triggers this by updating its queue length itself. + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Transmission rescheduled due to communicator message queue with qid %u has capacity %" + PRIu64 ".\n", + qe->queue->qid, + qe->queue->q_capacity); + /* message queue has capacity; only resume this one queue */ + /* queue dropped below threshold; only resume this one queue */ + GNUNET_STATISTICS_update (GST_stats, + "# Transmission throttled due to message queue capacity", + -1, + GNUNET_NO); + schedule_transmit_on_queue (GNUNET_TIME_UNIT_ZERO, + qe->queue, + GNUNET_SCHEDULER_PRIORITY_DEFAULT); + } + + if (NULL != (pm = qe->pm)) + { + struct VirtualLink *vl; + + // GNUNET_assert (qe == pm->qe); + pm->qe = NULL; + /* If waiting for this communicator may have blocked transmission + of pm on other queues for this neighbour, force schedule + transmit on queue for queues of the neighbour */ + if (NULL == pm->frag_parent) + { + vl = pm->vl; + if ((NULL != vl) && + (NULL != vl->pending_msg_head) && + (vl->pending_msg_head == pm)) + check_vl_transmission (vl); + } + } + GNUNET_free (qe); +} + + +/** + * Iterator telling new MONITOR client about all existing + * queues to peers. + * + * @param cls the new `struct TransportClient` + * @param pid a connected peer + * @param value the `struct Neighbour` with more information + * @return #GNUNET_OK (continue to iterate) + */ +static int +notify_client_queues (void *cls, + const struct GNUNET_PeerIdentity *pid, + void *value) +{ + struct TransportClient *tc = cls; + struct Neighbour *neighbour = value; + + GNUNET_assert (CT_MONITOR == tc->type); + for (struct Queue *q = neighbour->queue_head; NULL != q; + q = q->next_neighbour) + { + struct MonitorEvent me = { .rtt = q->pd.aged_rtt, + .cs = q->cs, + .num_msg_pending = q->num_msg_pending, + .num_bytes_pending = q->num_bytes_pending }; + + notify_monitor (tc, pid, q->address, q->nt, &me); + } + return GNUNET_OK; +} + + +/** + * Initialize a monitor client. + * + * @param cls the client + * @param start the start message that was sent + */ +static void +handle_monitor_start (void *cls, + const struct GNUNET_TRANSPORT_MonitorStart *start) +{ + struct TransportClient *tc = cls; + + if (CT_NONE != tc->type) + { + GNUNET_break (0); + GNUNET_SERVICE_client_drop (tc->client); + return; + } + tc->type = CT_MONITOR; + tc->details.monitor.peer = start->peer; + tc->details.monitor.one_shot = ntohl (start->one_shot); + GNUNET_CONTAINER_multipeermap_iterate (neighbours, ¬ify_client_queues, tc); + GNUNET_SERVICE_client_mark_monitor (tc->client); + GNUNET_SERVICE_client_continue (tc->client); +} + + +/** + * Find transport client providing communication service + * for the protocol @a prefix. + * + * @param prefix communicator name + * @return NULL if no such transport client is available + */ +static struct TransportClient * +lookup_communicator (const char *prefix) +{ + for (struct TransportClient *tc = clients_head; NULL != tc; tc = tc->next) + { + if (CT_COMMUNICATOR != tc->type) + continue; + if (0 == strcmp (prefix, tc->details.communicator.address_prefix)) + return tc; + } + GNUNET_log ( + GNUNET_ERROR_TYPE_WARNING, + "Somone suggested use of communicator for `%s', but we do not have such a communicator!\n", + prefix); + return NULL; +} + + +/** + * Signature of a function called with a communicator @a address of a peer + * @a pid that an application wants us to connect to. + * + * @param pid target peer + * @param address the address to try + */ +static void +suggest_to_connect (const struct GNUNET_PeerIdentity *pid, const char *address) +{ + static uint32_t idgen; + struct TransportClient *tc; + char *prefix; + struct GNUNET_TRANSPORT_CreateQueue *cqm; + struct GNUNET_MQ_Envelope *env; + size_t alen; + + prefix = GNUNET_HELLO_address_to_prefix (address); + if (NULL == prefix) + { + GNUNET_break (0); /* We got an invalid address!? */ + return; + } + tc = lookup_communicator (prefix); + if (NULL == tc) + { + GNUNET_STATISTICS_update (GST_stats, + "# Suggestions ignored due to missing communicator", + 1, + GNUNET_NO); + GNUNET_log (GNUNET_ERROR_TYPE_INFO, + "Cannot connect to %s at `%s', no matching communicator present\n", + GNUNET_i2s (pid), + address); + GNUNET_free (prefix); + return; + } + /* forward suggestion for queue creation to communicator */ + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Request #%u for `%s' communicator to create queue to `%s' at `%s'\n", + (unsigned int) idgen, + prefix, + GNUNET_i2s (pid), + address); + GNUNET_free (prefix); + alen = strlen (address) + 1; + env = + GNUNET_MQ_msg_extra (cqm, alen, GNUNET_MESSAGE_TYPE_TRANSPORT_QUEUE_CREATE); + cqm->request_id = htonl (idgen++); + cqm->receiver = *pid; + memcpy (&cqm[1], address, alen); + GNUNET_MQ_send (tc->mq, env); +} + + +/** + * The queue @a q (which matches the peer and address in @a vs) is + * ready for queueing. We should now queue the validation request. + * + * @param q queue to send on + * @param vs state to derive validation challenge from + */ +static void +validation_transmit_on_queue (struct Queue *q, struct ValidationState *vs) +{ + struct TransportValidationChallengeMessage tvc; + + vs->last_challenge_use = GNUNET_TIME_absolute_get_monotonic (GST_cfg); + tvc.header.type = + htons (GNUNET_MESSAGE_TYPE_TRANSPORT_ADDRESS_VALIDATION_CHALLENGE); + tvc.header.size = htons (sizeof(tvc)); + tvc.reserved = htonl (0); + tvc.challenge = vs->challenge; + tvc.sender_time = GNUNET_TIME_absolute_hton (vs->last_challenge_use); + GNUNET_log (GNUNET_ERROR_TYPE_INFO, + "Sending address validation challenge %s to %s\n", + GNUNET_sh2s (&tvc.challenge.value), + GNUNET_i2s (&q->neighbour->pid)); + queue_send_msg (q, NULL, &tvc, sizeof(tvc)); +} + + +/** + * Task run periodically to validate some address based on #validation_heap. + * + * @param cls NULL + */ +static void +validation_start_cb (void *cls) +{ + struct ValidationState *vs; + struct Queue *q; + const struct GNUNET_TIME_Absolute now = GNUNET_TIME_absolute_get_monotonic ( + GST_cfg); + + (void) cls; + validation_task = NULL; + vs = GNUNET_CONTAINER_heap_peek (validation_heap); + /* drop validations past their expiration */ + while ( + (NULL != vs) && + (0 == GNUNET_TIME_absolute_get_remaining (vs->valid_until).rel_value_us)) + { + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Validation response %s cleaned up\n", + GNUNET_sh2s (&vs->challenge.value)); + free_validation_state (vs); + vs = GNUNET_CONTAINER_heap_peek (validation_heap); + } + if (NULL == vs) + { + GNUNET_log (GNUNET_ERROR_TYPE_INFO, + "Address validation task not scheduled anymore, nothing to do\n"); + return; /* woopsie, no more addresses known, should only + happen if we're really a lonely peer */ + } + q = find_queue (&vs->pid, vs->address); + if (GNUNET_TIME_absolute_cmp (vs->first_challenge_use, >, now)) + { + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "To early to start next address validation for challenge %s\n", + GNUNET_sh2s (&vs->challenge.value)); + return; + } + if (NULL == q) + { + vs->awaiting_queue = GNUNET_YES; + suggest_to_connect (&vs->pid, vs->address); + } + else + validation_transmit_on_queue (q, vs); + /* Finally, reschedule next attempt */ + vs->challenge_backoff = + GNUNET_TIME_randomized_backoff (vs->challenge_backoff, + MAX_VALIDATION_CHALLENGE_FREQ); + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Address validation task will run again in %s\n", + GNUNET_STRINGS_relative_time_to_string (vs->challenge_backoff, + GNUNET_YES)); + update_next_challenge_time (vs, + GNUNET_TIME_relative_to_absolute ( + vs->challenge_backoff)); +} + + +/** + * Closure for #check_connection_quality. + */ +struct QueueQualityContext +{ + /** + * Set to the @e k'th queue encountered. + */ + struct Queue *q; + + /** + * Set to the number of quality queues encountered. + */ + unsigned int quality_count; + + /** + * Set to the total number of queues encountered. + */ + unsigned int num_queues; + + /** + * Decremented for each queue, for selection of the + * k-th queue in @e q. + */ + unsigned int k; +}; + + +/** + * Check whether any queue to the given neighbour is + * of a good "quality" and if so, increment the counter. + * Also counts the total number of queues, and returns + * the k-th queue found. + * + * @param cls a `struct QueueQualityContext *` with counters + * @param pid peer this is about + * @param value a `struct Neighbour` + * @return #GNUNET_OK (continue to iterate) + */ +static int +check_connection_quality (void *cls, + const struct GNUNET_PeerIdentity *pid, + void *value) +{ + struct QueueQualityContext *ctx = cls; + struct Neighbour *n = value; + int do_inc; + + (void) pid; + do_inc = GNUNET_NO; + for (struct Queue *q = n->queue_head; NULL != q; q = q->next_neighbour) + { + ctx->num_queues++; + if (0 == ctx->k--) + ctx->q = q; + /* FIXME-CONQ-STATISTICS: in the future, add reliability / goodput + statistics and consider those as well here? */ + if (q->pd.aged_rtt.rel_value_us < DV_QUALITY_RTT_THRESHOLD.rel_value_us) + do_inc = GNUNET_YES; + } + if (GNUNET_YES == do_inc) + ctx->quality_count++; + return GNUNET_OK; +} + + +/** + * Task run when we CONSIDER initiating a DV learn + * process. We first check that sending out a message is + * even possible (queues exist), then that it is desirable + * (if not, reschedule the task for later), and finally + * we may then begin the job. If there are too many + * entries in the #dvlearn_map, we purge the oldest entry + * using #lle_tail. + * + * @param cls NULL + */ +static void +start_dv_learn (void *cls) +{ + struct LearnLaunchEntry *lle; + struct QueueQualityContext qqc; + struct TransportDVLearnMessage dvl; + + (void) cls; + dvlearn_task = NULL; + if (0 == GNUNET_CONTAINER_multipeermap_size (neighbours)) + return; /* lost all connectivity, cannot do learning */ + qqc.quality_count = 0; + qqc.num_queues = 0; + qqc.k = GNUNET_CONTAINER_multipeermap_size (neighbours); + GNUNET_CONTAINER_multipeermap_iterate (neighbours, + &check_connection_quality, + &qqc); + if (qqc.quality_count > DV_LEARN_QUALITY_THRESHOLD) + { + struct GNUNET_TIME_Relative delay; + unsigned int factor; + + /* scale our retries by how far we are above the threshold */ + factor = qqc.quality_count / DV_LEARN_QUALITY_THRESHOLD; + delay = GNUNET_TIME_relative_multiply (DV_LEARN_BASE_FREQUENCY, factor); + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "At connection quality %u, will launch DV learn in %s\n", + qqc.quality_count, + GNUNET_STRINGS_relative_time_to_string (delay, GNUNET_YES)); + dvlearn_task = GNUNET_SCHEDULER_add_delayed (delay, &start_dv_learn, NULL); + return; + } + /* remove old entries in #dvlearn_map if it has grown too big */ + while (MAX_DV_LEARN_PENDING <= + GNUNET_CONTAINER_multishortmap_size (dvlearn_map)) + { + lle = lle_tail; + GNUNET_assert (GNUNET_YES == + GNUNET_CONTAINER_multishortmap_remove (dvlearn_map, + &lle->challenge.value, + lle)); + GNUNET_CONTAINER_DLL_remove (lle_head, lle_tail, lle); + GNUNET_free (lle); + } + /* setup data structure for learning */ + lle = GNUNET_new (struct LearnLaunchEntry); + GNUNET_CRYPTO_random_block (GNUNET_CRYPTO_QUALITY_NONCE, + &lle->challenge, + sizeof(lle->challenge)); + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Starting launch DV learn with challenge %s\n", + GNUNET_sh2s (&lle->challenge.value)); + GNUNET_CONTAINER_DLL_insert (lle_head, lle_tail, lle); + GNUNET_break (GNUNET_YES == + GNUNET_CONTAINER_multishortmap_put ( + dvlearn_map, + &lle->challenge.value, + lle, + GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_ONLY)); + dvl.header.type = htons (GNUNET_MESSAGE_TYPE_TRANSPORT_DV_LEARN); + dvl.header.size = htons (sizeof(dvl)); + dvl.num_hops = htons (0); + dvl.bidirectional = htons (0); + dvl.non_network_delay = GNUNET_TIME_relative_hton (GNUNET_TIME_UNIT_ZERO); + dvl.monotonic_time = + GNUNET_TIME_absolute_hton (GNUNET_TIME_absolute_get_monotonic (GST_cfg)); + { + struct DvInitPS dvip = { + .purpose.purpose = htonl ( + GNUNET_SIGNATURE_PURPOSE_TRANSPORT_DV_INITIATOR), + .purpose.size = htonl (sizeof(dvip)), + .monotonic_time = dvl.monotonic_time, + .challenge = lle->challenge + }; + + GNUNET_CRYPTO_eddsa_sign (GST_my_private_key, + &dvip, + &dvl.init_sig); + } + dvl.initiator = GST_my_identity; + dvl.challenge = lle->challenge; + + qqc.quality_count = 0; + qqc.k = GNUNET_CRYPTO_random_u32 (GNUNET_CRYPTO_QUALITY_WEAK, qqc.num_queues); + qqc.num_queues = 0; + qqc.q = NULL; + GNUNET_CONTAINER_multipeermap_iterate (neighbours, + &check_connection_quality, + &qqc); + GNUNET_assert (NULL != qqc.q); + + /* Do this as close to transmission time as possible! */ + lle->launch_time = GNUNET_TIME_absolute_get (); + + queue_send_msg (qqc.q, NULL, &dvl, sizeof(dvl)); + /* reschedule this job, randomizing the time it runs (but no + actual backoff!) */ + dvlearn_task = GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_randomize ( + DV_LEARN_BASE_FREQUENCY), + &start_dv_learn, + NULL); +} + + +/** + * Get the IP address without the port number. + * + * @param address The string contains a communicator prefix, IP address and port + * like this 'tcp-92.68.150.1:55452'. + * @return String with IP address only. + */ +static char * +get_address_without_port (const char *address) +{ + const char *colon; + char *colon_rest; + size_t colon_rest_length; + char *address_without_port; + + colon = strchr (address,':'); + colon_rest = GNUNET_strndup (address, colon - address); + colon_rest_length = strlen (colon_rest); + address_without_port = GNUNET_strndup (&colon_rest[4], colon_rest_length - 4); + GNUNET_free (colon_rest); + + return address_without_port; +} + + +/** + * A new queue has been created, check if any address validation + * requests have been waiting for it. + * + * @param cls a `struct Queue` + * @param pid peer concerned (unused) + * @param value a `struct ValidationState` + * @return #GNUNET_NO if a match was found and we can stop looking + */ +static int +check_validation_request_pending (void *cls, + const struct GNUNET_PeerIdentity *pid, + void *value) +{ + struct Queue *q = cls; + struct ValidationState *vs = value; + char *address_without_port_vs; + char *address_without_port_q; + int success = GNUNET_YES; + + address_without_port_vs = get_address_without_port (vs->address); + address_without_port_q = get_address_without_port (q->address); + + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Check validation request pending for `%s' at `%s'/`%s' (vs)/(q)\n", + GNUNET_i2s (pid), + address_without_port_vs, + address_without_port_q); + (void) pid; + if ((GNUNET_YES == vs->awaiting_queue) && + (0 == strcmp (address_without_port_vs, address_without_port_q))) + { + + vs->awaiting_queue = GNUNET_NO; + validation_transmit_on_queue (q, vs); + success = GNUNET_NO; + } + + GNUNET_free (address_without_port_vs); + GNUNET_free (address_without_port_q); + return success; +} + + +/** + * Function called with the monotonic time of a DV initiator + * by PEERSTORE. Updates the time. + * + * @param cls a `struct Neighbour` + * @param record the information found, NULL for the last call + * @param emsg error message + */ +static void +neighbour_dv_monotime_cb (void *cls, + const struct GNUNET_PEERSTORE_Record *record, + const char *emsg) +{ + struct Neighbour *n = cls; + struct GNUNET_TIME_AbsoluteNBO *mtbe; + + (void) emsg; + if (NULL == record) + { + /* we're done with #neighbour_dv_monotime_cb() invocations, + continue normal processing */ + n->get = NULL; + n->dv_monotime_available = GNUNET_YES; + return; + } + if (sizeof(*mtbe) != record->value_size) + { + GNUNET_break (0); + return; + } + mtbe = record->value; + n->last_dv_learn_monotime = + GNUNET_TIME_absolute_max (n->last_dv_learn_monotime, + GNUNET_TIME_absolute_ntoh (*mtbe)); +} + + +/** + * New queue became available. Process the request. + * + * @param cls the client + * @param aqm the send message that was sent + */ +static void +handle_add_queue_message (void *cls, + const struct GNUNET_TRANSPORT_AddQueueMessage *aqm) +{ + struct TransportClient *tc = cls; + struct Queue *queue; + struct Neighbour *neighbour; + struct GNUNET_TIME_Absolute validated_until = GNUNET_TIME_UNIT_ZERO_ABS; + const char *addr; + uint16_t addr_len; + + if (ntohl (aqm->mtu) <= sizeof(struct TransportFragmentBoxMessage)) + { + /* MTU so small as to be useless for transmissions, + required for #fragment_message()! */ + GNUNET_break_op (0); + GNUNET_SERVICE_client_drop (tc->client); + return; + } + /* This may simply be a queue update */ + for (queue = tc->details.communicator.queue_head; + NULL != queue; + queue = queue->next_client) + { + validated_until = queue->validated_until; + if (queue->qid != ntohl (aqm->qid)) + continue; + break; + } + + if (NULL != queue) + { + neighbour = queue->neighbour; + } + else + { + neighbour = lookup_neighbour (&aqm->receiver); + if (NULL == neighbour) + { + neighbour = GNUNET_new (struct Neighbour); + neighbour->pid = aqm->receiver; + GNUNET_assert (GNUNET_OK == + GNUNET_CONTAINER_multipeermap_put ( + neighbours, + &neighbour->pid, + neighbour, + GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_ONLY)); + neighbour->get = + GNUNET_PEERSTORE_iterate (peerstore, + "transport", + &neighbour->pid, + GNUNET_PEERSTORE_TRANSPORT_DVLEARN_MONOTIME, + &neighbour_dv_monotime_cb, + neighbour); + } + addr_len = ntohs (aqm->header.size) - sizeof(*aqm); + addr = (const char *) &aqm[1]; + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "New queue %s to %s available with QID %u and q_len %" PRIu64 + " and mtu %u\n", + addr, + GNUNET_i2s (&aqm->receiver), + ntohl (aqm->qid), + GNUNET_ntohll (aqm->q_len), + ntohl (aqm->mtu)); + queue = GNUNET_malloc (sizeof(struct Queue) + addr_len); + queue->tc = tc; + if (GNUNET_TIME_UNIT_ZERO_ABS.abs_value_us != validated_until.abs_value_us) + { + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "New queue with QID %u inherit validated until\n", + ntohl (aqm->qid)); + queue->validated_until = validated_until; + } + queue->address = (const char *) &queue[1]; + queue->pd.aged_rtt = GNUNET_TIME_UNIT_FOREVER_REL; + queue->qid = ntohl (aqm->qid); + queue->neighbour = neighbour; + if (GNUNET_TRANSPORT_QUEUE_LENGTH_UNLIMITED == GNUNET_ntohll (aqm->q_len)) + queue->unlimited_length = GNUNET_YES; + queue->q_capacity = GNUNET_ntohll (aqm->q_len); + memcpy (&queue[1], addr, addr_len); + /* notify monitors about new queue */ + { + struct MonitorEvent me = { .rtt = queue->pd.aged_rtt, .cs = queue->cs }; + + notify_monitors (&neighbour->pid, queue->address, queue->nt, &me); + } + GNUNET_CONTAINER_MDLL_insert (neighbour, + neighbour->queue_head, + neighbour->queue_tail, + queue); + GNUNET_CONTAINER_MDLL_insert (client, + tc->details.communicator.queue_head, + tc->details.communicator.queue_tail, + queue); + + } + queue->mtu = ntohl (aqm->mtu); + queue->nt = (enum GNUNET_NetworkType) ntohl (aqm->nt); + queue->cs = (enum GNUNET_TRANSPORT_ConnectionStatus) ntohl (aqm->cs); + queue->idle = GNUNET_YES; + /* check if valdiations are waiting for the queue */ + (void) + GNUNET_CONTAINER_multipeermap_get_multiple (validation_map, + &aqm->receiver, + &check_validation_request_pending, + queue); + /* look for traffic for this queue */ + schedule_transmit_on_queue (GNUNET_TIME_UNIT_ZERO, + queue, GNUNET_SCHEDULER_PRIORITY_DEFAULT); + /* might be our first queue, try launching DV learning */ + if (NULL == dvlearn_task) + dvlearn_task = GNUNET_SCHEDULER_add_now (&start_dv_learn, NULL); + GNUNET_SERVICE_client_continue (tc->client); +} + + +/** + * @brief Handle updates to queues. + * + * @param cls the transport client. + * @param msg Message struct. + */ +static void +handle_update_queue_message (void *cls, + const struct + GNUNET_TRANSPORT_UpdateQueueMessage *msg) +{ + struct TransportClient *tc = cls; + struct Queue *target_queue = NULL; + + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Received queue update message for %u with q_len %llu and mtu %u\n", + ntohl (msg->qid), + (unsigned long long) GNUNET_ntohll (msg->q_len), + ntohl (msg->mtu)); + for (target_queue = tc->details.communicator.queue_head; + NULL != target_queue; + target_queue = target_queue->next_client) + { + if (ntohl (msg->qid) == target_queue->qid) + break; + } + if (NULL == target_queue) + { + GNUNET_log (GNUNET_ERROR_TYPE_WARNING, + "Queue to update no longer exists! Discarding update.\n"); + return; + } + + target_queue->nt = msg->nt; + target_queue->mtu = ntohl (msg->mtu); + target_queue->cs = msg->cs; + target_queue->priority = ntohl (msg->priority); + /* The update message indicates how many messages + * the queue should be able to handle. + */ + if (GNUNET_TRANSPORT_QUEUE_LENGTH_UNLIMITED == GNUNET_ntohll (msg->q_len)) + target_queue->unlimited_length = GNUNET_YES; + else + target_queue->unlimited_length = GNUNET_NO; + target_queue->q_capacity += GNUNET_ntohll (msg->q_len); + if (0 < target_queue->q_capacity) + schedule_transmit_on_queue (GNUNET_TIME_UNIT_ZERO, + target_queue, + GNUNET_SCHEDULER_PRIORITY_DEFAULT); + GNUNET_SERVICE_client_continue (tc->client); +} + + +/** + * Communicator tells us that our request to create a queue "worked", that + * is setting up the queue is now in process. + * + * @param cls the `struct TransportClient` + * @param cqr confirmation message + */ +static void +handle_queue_create_ok (void *cls, + const struct GNUNET_TRANSPORT_CreateQueueResponse *cqr) +{ + struct TransportClient *tc = cls; + + if (CT_COMMUNICATOR != tc->type) + { + GNUNET_break (0); + GNUNET_SERVICE_client_drop (tc->client); + return; + } + GNUNET_STATISTICS_update (GST_stats, + "# Suggestions succeeded at communicator", + 1, + GNUNET_NO); + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Request #%u for communicator to create queue succeeded\n", + (unsigned int) ntohs (cqr->request_id)); + GNUNET_SERVICE_client_continue (tc->client); +} + + +/** + * Communicator tells us that our request to create a queue failed. This + * usually indicates that the provided address is simply invalid or that the + * communicator's resources are exhausted. + * + * @param cls the `struct TransportClient` + * @param cqr failure message + */ +static void +handle_queue_create_fail ( + void *cls, + const struct GNUNET_TRANSPORT_CreateQueueResponse *cqr) +{ + struct TransportClient *tc = cls; + + if (CT_COMMUNICATOR != tc->type) + { + GNUNET_break (0); + GNUNET_SERVICE_client_drop (tc->client); + return; + } + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Request #%u for communicator to create queue failed\n", + (unsigned int) ntohs (cqr->request_id)); + GNUNET_STATISTICS_update (GST_stats, + "# Suggestions failed in queue creation at communicator", + 1, + GNUNET_NO); + GNUNET_SERVICE_client_continue (tc->client); +} + + +/** + * We have received a `struct ExpressPreferenceMessage` from an application + * client. + * + * @param cls handle to the client + * @param msg the start message + */ +static void +handle_suggest_cancel (void *cls, const struct ExpressPreferenceMessage *msg) +{ + struct TransportClient *tc = cls; + struct PeerRequest *pr; + + if (CT_APPLICATION != tc->type) + { + GNUNET_break (0); + GNUNET_SERVICE_client_drop (tc->client); + return; + } + pr = GNUNET_CONTAINER_multipeermap_get (tc->details.application.requests, + &msg->peer); + if (NULL == pr) + { + GNUNET_break (0); + GNUNET_SERVICE_client_drop (tc->client); + return; + } + (void) stop_peer_request (tc, &pr->pid, pr); + GNUNET_SERVICE_client_continue (tc->client); +} + + +static void +hello_for_client_cb (void *cls, + const char *uri) +{ + const struct GNUNET_PeerIdentity *peer = cls; + int pfx_len; + const char *eou; + char *address; + + eou = strstr (uri, + "://"); + pfx_len = eou - uri; + eou += 3; + GNUNET_asprintf (&address, + "%.*s-%s", + pfx_len, + uri, + eou); + + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "hello for client %s\n", + address); + + start_address_validation (peer, address); + GNUNET_free (address); +} + + +/** + * Function called by PEERSTORE for each matching record. + * + * @param cls closure, a `struct PeerRequest` + * @param record peerstore record information + * @param emsg error message, or NULL if no errors + */ +static void +handle_hello_for_client (void *cls, + const struct GNUNET_PeerIdentity *peer, + const struct GNUNET_MessageHeader *hello, + const char *emsg) +{ + (void) cls; + const char *val; + struct GNUNET_HELLO_Builder *builder; + + if (NULL != emsg) + { + GNUNET_log (GNUNET_ERROR_TYPE_WARNING, + "Got failure from PEERSTORE: %s\n", + emsg); + return; + } + if (0 == GNUNET_memcmp (peer, &GST_my_identity)) + return; + builder = GNUNET_HELLO_builder_new (peer); + GNUNET_HELLO_builder_iterate (builder, + (struct GNUNET_PeerIdentity *) peer, + hello_for_client_cb, + NULL); +} + + +/** + * We have received a `struct ExpressPreferenceMessage` from an application + * client. + * + * @param cls handle to the client + * @param msg the start message + */ +static void +handle_suggest (void *cls, const struct ExpressPreferenceMessage *msg) +{ + struct TransportClient *tc = cls; + struct PeerRequest *pr; + + if (CT_NONE == tc->type) + { + tc->type = CT_APPLICATION; + tc->details.application.requests = + GNUNET_CONTAINER_multipeermap_create (16, GNUNET_YES); + } + if (CT_APPLICATION != tc->type) + { + GNUNET_break (0); + GNUNET_SERVICE_client_drop (tc->client); + return; + } + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Client suggested we talk to %s with preference %d at rate %u\n", + GNUNET_i2s (&msg->peer), + (int) ntohl (msg->pk), + (int) ntohl (msg->bw.value__)); + pr = GNUNET_new (struct PeerRequest); + pr->tc = tc; + pr->pid = msg->peer; + pr->bw = msg->bw; + pr->pk = (enum GNUNET_MQ_PriorityPreferences) ntohl (msg->pk); + if (GNUNET_YES != GNUNET_CONTAINER_multipeermap_put ( + tc->details.application.requests, + &pr->pid, + pr, + GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_ONLY)) + { + GNUNET_break (0); + GNUNET_free (pr); + GNUNET_SERVICE_client_drop (tc->client); + return; + } + pr->nc = GNUNET_PEERSTORE_hello_changed_notify (peerstore, + GNUNET_NO, + &handle_hello_for_client, + NULL); + GNUNET_SERVICE_client_continue (tc->client); +} + + +/** + * Check #GNUNET_MESSAGE_TYPE_TRANSPORT_REQUEST_HELLO_VALIDATION + * messages. + * + * @param cls a `struct TransportClient *` + * @param m message to verify + * @return #GNUNET_OK on success + */ +static int +check_request_hello_validation (void *cls, + const struct RequestHelloValidationMessage *m) +{ + (void) cls; + GNUNET_MQ_check_zero_termination (m); + return GNUNET_OK; +} + + +/** + * A client encountered an address of another peer. Consider validating it, + * and if validation succeeds, persist it to PEERSTORE. + * + * @param cls a `struct TransportClient *` + * @param m message to verify + */ +static void +handle_request_hello_validation (void *cls, + const struct RequestHelloValidationMessage *m) +{ + struct TransportClient *tc = cls; + + start_address_validation (&m->peer, (const char *) &m[1]); + GNUNET_SERVICE_client_continue (tc->client); +} + + +/** + * Free neighbour entry. + * + * @param cls NULL + * @param pid unused + * @param value a `struct Neighbour` + * @return #GNUNET_OK (always) + */ +static int +free_neighbour_cb (void *cls, + const struct GNUNET_PeerIdentity *pid, + void *value) +{ + struct Neighbour *neighbour = value; + + (void) cls; + (void) pid; + GNUNET_break (0); // should this ever happen? + free_neighbour (neighbour); + + return GNUNET_OK; +} + + +/** + * Free DV route entry. + * + * @param cls NULL + * @param pid unused + * @param value a `struct DistanceVector` + * @return #GNUNET_OK (always) + */ +static int +free_dv_routes_cb (void *cls, + const struct GNUNET_PeerIdentity *pid, + void *value) +{ + struct DistanceVector *dv = value; + + (void) cls; + (void) pid; + free_dv_route (dv); + + return GNUNET_OK; +} + + +/** + * Free validation state. + * + * @param cls NULL + * @param pid unused + * @param value a `struct ValidationState` + * @return #GNUNET_OK (always) + */ +static int +free_validation_state_cb (void *cls, + const struct GNUNET_PeerIdentity *pid, + void *value) +{ + struct ValidationState *vs = value; + + (void) cls; + (void) pid; + free_validation_state (vs); + return GNUNET_OK; +} + + +/** + * Free pending acknowledgement. + * + * @param cls NULL + * @param key unused + * @param value a `struct PendingAcknowledgement` + * @return #GNUNET_OK (always) + */ +static int +free_pending_ack_cb (void *cls, const struct GNUNET_Uuid *key, void *value) +{ + struct PendingAcknowledgement *pa = value; + + (void) cls; + (void) key; + free_pending_acknowledgement (pa); + return GNUNET_OK; +} + + +/** + * Free acknowledgement cummulator. + * + * @param cls NULL + * @param pid unused + * @param value a `struct AcknowledgementCummulator` + * @return #GNUNET_OK (always) + */ +static int +free_ack_cummulator_cb (void *cls, + const struct GNUNET_PeerIdentity *pid, + void *value) +{ + struct AcknowledgementCummulator *ac = value; + + (void) cls; + (void) pid; + GNUNET_SCHEDULER_cancel (ac->task); + GNUNET_free (ac); + return GNUNET_OK; +} + + +/** + * Function called when the service shuts down. Unloads our plugins + * and cancels pending validations. + * + * @param cls closure, unused + */ +static void +do_shutdown (void *cls) +{ + struct LearnLaunchEntry *lle; + + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "shutdown logic\n"); + (void) cls; + GNUNET_CONTAINER_multipeermap_iterate (neighbours, + &free_neighbour_cb, NULL); + if (NULL != validation_task) + { + GNUNET_SCHEDULER_cancel (validation_task); + validation_task = NULL; + } + if (NULL != dvlearn_task) + { + GNUNET_SCHEDULER_cancel (dvlearn_task); + dvlearn_task = NULL; + } + if (NULL != GST_stats) + { + GNUNET_STATISTICS_destroy (GST_stats, GNUNET_NO); + GST_stats = NULL; + } + if (NULL != GST_my_hello) + { + GNUNET_HELLO_builder_free (GST_my_hello); + GST_my_hello = NULL; + } + if (NULL != GST_my_private_key) + { + GNUNET_free (GST_my_private_key); + GST_my_private_key = NULL; + } + GNUNET_CONTAINER_multipeermap_iterate (ack_cummulators, + &free_ack_cummulator_cb, + NULL); + GNUNET_CONTAINER_multipeermap_destroy (ack_cummulators); + ack_cummulators = NULL; + GNUNET_CONTAINER_multiuuidmap_iterate (pending_acks, + &free_pending_ack_cb, + NULL); + GNUNET_CONTAINER_multiuuidmap_destroy (pending_acks); + pending_acks = NULL; + GNUNET_break (0 == GNUNET_CONTAINER_multipeermap_size (neighbours)); + GNUNET_CONTAINER_multipeermap_destroy (neighbours); + neighbours = NULL; + GNUNET_break (0 == GNUNET_CONTAINER_multipeermap_size (links)); + GNUNET_CONTAINER_multipeermap_destroy (links); + links = NULL; + GNUNET_CONTAINER_multipeermap_iterate (backtalkers, + &free_backtalker_cb, + NULL); + GNUNET_CONTAINER_multipeermap_destroy (backtalkers); + backtalkers = NULL; + GNUNET_CONTAINER_multipeermap_iterate (validation_map, + &free_validation_state_cb, + NULL); + GNUNET_CONTAINER_multipeermap_destroy (validation_map); + validation_map = NULL; + while (NULL != ir_head) + free_incoming_request (ir_head); + GNUNET_assert (0 == ir_total); + while (NULL != (lle = lle_head)) + { + GNUNET_CONTAINER_DLL_remove (lle_head, lle_tail, lle); + GNUNET_free (lle); + } + if (NULL != peerstore) + { + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Disconnecting from PEERSTORE service\n"); + GNUNET_PEERSTORE_disconnect (peerstore, GNUNET_NO); + peerstore = NULL; + } + GNUNET_CONTAINER_multishortmap_destroy (dvlearn_map); + dvlearn_map = NULL; + GNUNET_CONTAINER_heap_destroy (validation_heap); + validation_heap = NULL; + GNUNET_CONTAINER_multipeermap_iterate (dv_routes, &free_dv_routes_cb, NULL); + GNUNET_CONTAINER_multipeermap_destroy (dv_routes); + dv_routes = NULL; + GNUNET_SCHEDULER_shutdown (); +} + + +static void +shutdown_task (void *cls) +{ + in_shutdown = GNUNET_YES; + + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Shutdown task executed\n"); + if (NULL != clients_head) + { + for (struct TransportClient *tc = clients_head; NULL != tc; tc = tc->next) + { + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "client still connected: %u\n", + tc->type); + } + } + else + do_shutdown (cls); + +} + + +/** + * Initiate transport service. + * + * @param cls closure + * @param c configuration to use + * @param service the initialized service + */ +static void +run (void *cls, + const struct GNUNET_CONFIGURATION_Handle *c, + struct GNUNET_SERVICE_Handle *service) +{ + (void) cls; + (void) service; + /* setup globals */ + hello_mono_time = GNUNET_TIME_absolute_get_monotonic (c); + in_shutdown = GNUNET_NO; + GST_cfg = c; + backtalkers = GNUNET_CONTAINER_multipeermap_create (16, GNUNET_YES); + pending_acks = GNUNET_CONTAINER_multiuuidmap_create (32768, GNUNET_YES); + ack_cummulators = GNUNET_CONTAINER_multipeermap_create (256, GNUNET_YES); + neighbours = GNUNET_CONTAINER_multipeermap_create (1024, GNUNET_YES); + links = GNUNET_CONTAINER_multipeermap_create (512, GNUNET_YES); + dv_routes = GNUNET_CONTAINER_multipeermap_create (1024, GNUNET_YES); + dvlearn_map = GNUNET_CONTAINER_multishortmap_create (2 * MAX_DV_LEARN_PENDING, + GNUNET_YES); + validation_map = GNUNET_CONTAINER_multipeermap_create (1024, GNUNET_YES); + validation_heap = + GNUNET_CONTAINER_heap_create (GNUNET_CONTAINER_HEAP_ORDER_MIN); + GST_my_private_key = + GNUNET_CRYPTO_eddsa_key_create_from_configuration (GST_cfg); + if (NULL == GST_my_private_key) + { + GNUNET_log ( + GNUNET_ERROR_TYPE_ERROR, + _ ( + "Transport service is lacking key configuration settings. Exiting.\n")); + GNUNET_SCHEDULER_shutdown (); + return; + } + GNUNET_CRYPTO_eddsa_key_get_public (GST_my_private_key, + &GST_my_identity.public_key); + GNUNET_log (GNUNET_ERROR_TYPE_INFO, + "My identity is `%s'\n", + GNUNET_i2s_full (&GST_my_identity)); + GST_my_hello = GNUNET_HELLO_builder_new (&GST_my_identity); + GST_stats = GNUNET_STATISTICS_create ("transport", GST_cfg); + GNUNET_SCHEDULER_add_shutdown (&shutdown_task, NULL); + peerstore = GNUNET_PEERSTORE_connect (GST_cfg); + if (NULL == peerstore) + { + GNUNET_break (0); + GNUNET_SCHEDULER_shutdown (); + return; + } +} + + +/** + * Define "main" method using service macro. + */ +GNUNET_SERVICE_MAIN ( + "transport", + GNUNET_SERVICE_OPTION_SOFT_SHUTDOWN, + &run, + &client_connect_cb, + &client_disconnect_cb, + NULL, + /* communication with applications */ + GNUNET_MQ_hd_fixed_size (suggest, + GNUNET_MESSAGE_TYPE_TRANSPORT_SUGGEST, + struct ExpressPreferenceMessage, + NULL), + GNUNET_MQ_hd_fixed_size (suggest_cancel, + GNUNET_MESSAGE_TYPE_TRANSPORT_SUGGEST_CANCEL, + struct ExpressPreferenceMessage, + NULL), + GNUNET_MQ_hd_var_size (request_hello_validation, + GNUNET_MESSAGE_TYPE_TRANSPORT_REQUEST_HELLO_VALIDATION, + struct RequestHelloValidationMessage, + NULL), + /* communication with core */ + GNUNET_MQ_hd_fixed_size (client_start, + GNUNET_MESSAGE_TYPE_TRANSPORT_START, + struct StartMessage, + NULL), + GNUNET_MQ_hd_var_size (client_send, + GNUNET_MESSAGE_TYPE_TRANSPORT_SEND, + struct OutboundMessage, + NULL), + GNUNET_MQ_hd_fixed_size (client_recv_ok, + GNUNET_MESSAGE_TYPE_TRANSPORT_RECV_OK, + struct RecvOkMessage, + NULL), + /* communication with communicators */ + GNUNET_MQ_hd_var_size (communicator_available, + GNUNET_MESSAGE_TYPE_TRANSPORT_NEW_COMMUNICATOR, + struct GNUNET_TRANSPORT_CommunicatorAvailableMessage, + NULL), + GNUNET_MQ_hd_var_size (communicator_backchannel, + GNUNET_MESSAGE_TYPE_TRANSPORT_COMMUNICATOR_BACKCHANNEL, + struct GNUNET_TRANSPORT_CommunicatorBackchannel, + NULL), + GNUNET_MQ_hd_var_size (add_address, + GNUNET_MESSAGE_TYPE_TRANSPORT_ADD_ADDRESS, + struct GNUNET_TRANSPORT_AddAddressMessage, + NULL), + GNUNET_MQ_hd_fixed_size (del_address, + GNUNET_MESSAGE_TYPE_TRANSPORT_DEL_ADDRESS, + struct GNUNET_TRANSPORT_DelAddressMessage, + NULL), + GNUNET_MQ_hd_var_size (incoming_msg, + GNUNET_MESSAGE_TYPE_TRANSPORT_INCOMING_MSG, + struct GNUNET_TRANSPORT_IncomingMessage, + NULL), + GNUNET_MQ_hd_fixed_size (queue_create_ok, + GNUNET_MESSAGE_TYPE_TRANSPORT_QUEUE_CREATE_OK, + struct GNUNET_TRANSPORT_CreateQueueResponse, + NULL), + GNUNET_MQ_hd_fixed_size (queue_create_fail, + GNUNET_MESSAGE_TYPE_TRANSPORT_QUEUE_CREATE_FAIL, + struct GNUNET_TRANSPORT_CreateQueueResponse, + NULL), + GNUNET_MQ_hd_var_size (add_queue_message, + GNUNET_MESSAGE_TYPE_TRANSPORT_QUEUE_SETUP, + struct GNUNET_TRANSPORT_AddQueueMessage, + NULL), + GNUNET_MQ_hd_fixed_size (update_queue_message, + GNUNET_MESSAGE_TYPE_TRANSPORT_QUEUE_UPDATE, + struct GNUNET_TRANSPORT_UpdateQueueMessage, + NULL), + GNUNET_MQ_hd_fixed_size (del_queue_message, + GNUNET_MESSAGE_TYPE_TRANSPORT_QUEUE_TEARDOWN, + struct GNUNET_TRANSPORT_DelQueueMessage, + NULL), + GNUNET_MQ_hd_fixed_size (send_message_ack, + GNUNET_MESSAGE_TYPE_TRANSPORT_SEND_MSG_ACK, + struct GNUNET_TRANSPORT_SendMessageToAck, + NULL), + /* communication with monitors */ + GNUNET_MQ_hd_fixed_size (monitor_start, + GNUNET_MESSAGE_TYPE_TRANSPORT_MONITOR_START, + struct GNUNET_TRANSPORT_MonitorStart, + NULL), + GNUNET_MQ_handler_end ()); + + +/* end of file gnunet-service-transport.c */ diff --git a/src/service/transport/gnunet-service-transport.h b/src/service/transport/gnunet-service-transport.h new file mode 100644 index 000000000..ea9e71e4b --- /dev/null +++ b/src/service/transport/gnunet-service-transport.h @@ -0,0 +1,234 @@ +/* + This file is part of GNUnet. + Copyright (C) 2010,2011 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 . + + SPDX-License-Identifier: AGPL3.0-or-later + */ + +/** + * @file transport/gnunet-service-transport.h + * @brief globals + * @author Christian Grothoff + */ +#ifndef GNUNET_SERVICE_TRANSPORT_H +#define GNUNET_SERVICE_TRANSPORT_H + +#include "gnunet_util_lib.h" +#include "gnunet_statistics_service.h" +#include "gnunet_ats_service.h" +#include "gnunet_transport_service.h" + +#define VERBOSE_VALIDATION GNUNET_YES + +/** + * Statistics handle. + */ +extern struct GNUNET_STATISTICS_Handle *GST_stats; + +/** + * Configuration handle. + */ +extern const struct GNUNET_CONFIGURATION_Handle *GST_cfg; + +/** + * Configuration handle. + */ +extern struct GNUNET_PeerIdentity GST_my_identity; + +/** + * Handle to peerinfo service. + */ +extern struct GNUNET_PEERINFO_Handle *GST_peerinfo; + +/** + * Our private key. + */ +extern struct GNUNET_CRYPTO_EddsaPrivateKey GST_my_private_key; + +/** + * ATS handle. + */ +extern struct GNUNET_ATS_SchedulingHandle *GST_ats; + +/** + * ATS connectivity handle. + */ +extern struct GNUNET_ATS_ConnectivityHandle *GST_ats_connect; + +/** + * Interface scanner determines our LAN address range(s). + */ +extern struct GNUNET_NT_InterfaceScanner *GST_is; + + +/** + * Function to call when a peer's address has changed + * + * @param cls closure + * @param peer peer this update is about, + * @param address address, NULL for disconnect notification + */ +typedef void +(*GNUNET_TRANSPORT_NeighbourChangeCallback) (void *cls, + const struct + GNUNET_PeerIdentity *peer, + const struct + GNUNET_HELLO_Address *address, + enum GNUNET_TRANSPORT_PeerState + state, + struct GNUNET_TIME_Absolute + state_timeout, + struct GNUNET_BANDWIDTH_Value32NBO + bandwidth_in, + struct GNUNET_BANDWIDTH_Value32NBO + bandwidth_out); + + +/** + * Continuation called from a blacklist test. + * + * @param cls closure + * @param peer identity of peer that was tested + * @param address address associated with the request + * @param session session associated with the request + * @param result #GNUNET_OK if the connection is allowed, + * #GNUNET_NO if not, + * #GNUNET_SYSERR if operation was aborted + */ +typedef void +(*GST_BlacklistTestContinuation) (void *cls, + const struct GNUNET_PeerIdentity *peer, + const struct GNUNET_HELLO_Address *address, + struct GNUNET_ATS_Session *session, + int result); + + +/** + * Add the given peer to the blacklist (for the given transport). + * + * @param peer peer to blacklist + * @param transport_name transport to blacklist for this peer, NULL for all + */ +void +GST_blacklist_add_peer (const struct GNUNET_PeerIdentity *peer, + const char *transport_name); + + +/** + * Handle to an active blacklist check. + */ +struct GST_BlacklistCheck; + + +/** + * Test if a peer/transport combination is blacklisted. + * + * @param peer the identity of the peer to test + * @param transport_name name of the transport to test, never NULL + * @param cont function to call with result + * @param cont_cls closure for @a cont + * @param address address to pass back to @a cont, can be NULL + * @param session session to pass back to @a cont, can be NULL + * @return handle to the blacklist check, NULL if the decision + * was made instantly and @a cont was already called + */ +struct GST_BlacklistCheck * +GST_blacklist_test_allowed (const struct GNUNET_PeerIdentity *peer, + const char *transport_name, + GST_BlacklistTestContinuation cont, + void *cont_cls, + const struct GNUNET_HELLO_Address *address, + struct GNUNET_ATS_Session *session); + + +/** + * Abort blacklist if @a address and @a session match. + * + * @param address address used to abort matching checks + * @param session session used to abort matching checks + */ +void +GST_blacklist_abort_matching (const struct GNUNET_HELLO_Address *address, + struct GNUNET_ATS_Session *session); + +/** + * Cancel a blacklist check. + * + * @param bc check to cancel + */ +void +GST_blacklist_test_cancel (struct GST_BlacklistCheck *bc); + + +/** + * Function called by the transport for each received message. + * + * @param cls closure, const char* with the name of the plugin we received the message from + * @param address address and (claimed) identity of the other peer + * @param session identifier used for this session (NULL for plugins + * that do not offer bi-directional communication to the sender + * using the same "connection") + * @param message the message, NULL if we only care about + * learning about the delay until we should receive again + * @return how long the plugin should wait until receiving more data + * (plugins that do not support this, can ignore the return value) + */ +struct GNUNET_TIME_Relative +GST_receive_callback (void *cls, + const struct GNUNET_HELLO_Address *address, + struct GNUNET_ATS_Session *session, + const struct GNUNET_MessageHeader *message); + +/** + * Broadcast the given message to all of our clients. + * + * @param msg message to broadcast + * @param may_drop #GNUNET_YES if the message can be dropped / is payload + */ +void +GST_clients_broadcast (const struct GNUNET_MessageHeader *msg, + int may_drop); + + +/** + * Broadcast the new active address to all clients monitoring the peer. + * + * @param peer peer this update is about (never NULL) + * @param address address, NULL on disconnect + * @param state the current state of the peer + * @param state_timeout the time out for the state + */ +void +GST_clients_broadcast_peer_notification (const struct GNUNET_PeerIdentity *peer, + const struct + GNUNET_HELLO_Address *address, + enum GNUNET_TRANSPORT_PeerState state, + struct GNUNET_TIME_Absolute + state_timeout); + + +/** + * Notify all clients about a disconnect, and cancel + * pending SEND_OK messages for this peer. + * + * @param peer peer that disconnected + */ +void +GST_clients_broadcast_disconnect (const struct GNUNET_PeerIdentity *peer); + + +#endif +/* end of file gnunet-service-transport_plugins.h */ diff --git a/src/service/transport/gnunet-transport-certificate-creation.in b/src/service/transport/gnunet-transport-certificate-creation.in new file mode 100644 index 000000000..771422a7a --- /dev/null +++ b/src/service/transport/gnunet-transport-certificate-creation.in @@ -0,0 +1,158 @@ +#!/bin/sh +# +# This shell script will generate an X509 certificate for +# your gnunet-transport HTTPS +# +# The current version partially reuses and recycles +# code from build.sh by NetBSD (although not entirely +# used because it needs debugging): +# +# Copyright (c) 2001-2011 The NetBSD Foundation, Inc. +# All rights reserved. +# +# This code is derived from software contributed to +# The NetBSD Foundation by Todd Vierling and Luke Mewburn. + +# Redistribution and use in source and binary forms, with or +# without modification, are permitted provided that the following +# conditions are met: +# 1. Redistributions of source code must retain the above +# copyright notice, this list of conditions and the following +# disclaimer. +# 2. Redistributions in binary form must reproduce the above +# copyright notice, this list of conditions and the following +# disclaimer in the documentation and/or other materials +# provided with the distribution. + +# THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND +# CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, +# INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +# MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +# DISCLAIMED. +# IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS BE LIABLE FOR +# ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +# PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +# ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +# LIABILITY, OR TORT +# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF +# THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY +# OF SUCH DAMAGE. + +progname=${0##*/} + +existence() { + command -v "$1" >/dev/null 2>&1 +} + +setdefaults() +{ + verbosity=0 + runcmd= +} + +statusmsg() +{ + ${runcmd} echo " $@" +} + +infomsg() +{ + if [ x$verbosity = x1 ]; then + statusmsg "INFO: $@" + fi +} + +warningmsg() +{ + statusmsg "WARNING: $@" +} + +errormsg() +{ + statusmsg "ERROR: $@" +} + +linemsg() +{ + statusmsg "=========================================" +} + + +usage() +{ + if [ -n "$*" ]; then + echo "" + echo "${progname}: $*" + fi + cat <<_usage_ + +Usage: ${progname} [-hv] [-c FILE] [...] + +Options: + -c FILE Use the configuration file FILE. + -h Print this help message. + -v Print the version and exit. + -V be verbose + +_usage_ + exit 1 +} + + +generate_cert_key() +{ + echo "" + infomsg "Generating Cert and Key" + + CERTTOOL="" + GNUTLS_CA_TEMPLATE=@PKGDATADIRECTORY@/gnunet-gns-proxy-ca.template + OPENSSL=0 + if test -x $(existence gnutls-certtool) + #if test -z "`gnutls-certtool --version`" > /dev/null + then + if test -z "`certtool --version | grep gnutls`" > /dev/null + then + warningmsg "'gnutls-certtool' or 'certtool' command not found. Trying openssl." + # if test -z "`openssl version`" > /dev/null + if test -x $(existence openssl) + then + OPENSSL=1 + else + warningmsg "Install either gnutls certtool or openssl for certificate generation!" + statusmsg "Cleaning up." + exit 1 + fi + fi + CERTTOOL="certtool" + else + CERTTOOL="gnutls-certtool" + fi + mkdir -p `dirname $KEYFILE` + + if test 1 -eq $OPENSSL + then + openssl genrsa -out $KEYFILE 1024 + openssl req -batch -days 365 -out $CERTFILE -new -x509 -key $KEYFILE + else + $CERTTOOL --generate-privkey --outfile $KEYFILE 2>/dev/null + $CERTTOOL --template $GNUTLS_CA_TEMPLATE --generate-self-signed --load-privkey $KEYFILE --outfile $CERTFILE 2>/dev/null + fi + } + +print_version() +{ + GNUNET_ARM_VERSION=`gnunet-arm -v` + echo $GNUNET_ARM_VERSION +} + +main() +{ + KEYFILE=$1 + CERTFILE=$2 + setdefaults + generate_cert_key +} + +main "$@" diff --git a/src/service/transport/gnunet-transport.c b/src/service/transport/gnunet-transport.c new file mode 100644 index 000000000..b5ad43770 --- /dev/null +++ b/src/service/transport/gnunet-transport.c @@ -0,0 +1,1437 @@ +/* + This file is part of GNUnet. + Copyright (C) 2011-2014, 2016, 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 . + + SPDX-License-Identifier: AGPL3.0-or-later + */ + +/** + * @file src/transport/gnunet-transport.c + * @brief Tool to help configure, measure and control the transport subsystem. + * @author Christian Grothoff + * @author Nathan Evans + */ +#include "platform.h" +#include "gnunet_util_lib.h" +#include "gnunet_resolver_service.h" +#include "gnunet_protocols.h" +#include "gnunet_transport_service.h" + +/** + * Timeout for a name resolution + */ +#define RESOLUTION_TIMEOUT \ + GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS, 30) + +/** + * Timeout for an operation + */ +#define OP_TIMEOUT GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS, 30) + + +/** + * Context to store name resolutions for valiation + */ +struct ValidationResolutionContext +{ + /** + * Next in DLL + */ + struct ValidationResolutionContext *next; + + /** + * Previous in DLL + */ + struct ValidationResolutionContext *prev; + + /** + * Address to resolve + */ + struct GNUNET_HELLO_Address *addrcp; + + /** + * Time of last validation + */ + struct GNUNET_TIME_Absolute last_validation; + + /** + * Address is valid until + */ + struct GNUNET_TIME_Absolute valid_until; + + /** + * Time of next validation + */ + struct GNUNET_TIME_Absolute next_validation; + + /** + * Transport conversion handle + */ + struct GNUNET_TRANSPORT_AddressToStringContext *asc; + + /** + * plugin name + */ + char *transport; + + /** + * was the entry printed + */ + int printed; +}; + +/** + * Struct to store information about peers in monitor mode + */ +struct MonitoredPeer +{ + /** + * State of the peer + */ + enum GNUNET_TRANSPORT_PeerState state; + + /** + * Timeout + */ + struct GNUNET_TIME_Absolute state_timeout; + + /** + * The address to convert + */ + struct GNUNET_HELLO_Address *address; +}; + +/** + * Context to store name resolutions for valiation + */ +struct PeerResolutionContext +{ + /** + * Next in DLL + */ + struct PeerResolutionContext *next; + + /** + * Prev in DLL + */ + struct PeerResolutionContext *prev; + + /** + * address to resolve + */ + struct GNUNET_HELLO_Address *addrcp; + + /** + * transport conversiion context + */ + struct GNUNET_TRANSPORT_AddressToStringContext *asc; + + /** + * peer state + */ + enum GNUNET_TRANSPORT_PeerState state; + + /** + * state timeout + */ + struct GNUNET_TIME_Absolute state_timeout; + + /** + * transport plugin + */ + char *transport; + + /** + * was the entry printed + */ + int printed; +}; + + +/** + * Benchmarking block size in KB + */ +#define BLOCKSIZE 4 + +/** + * Handle to transport service. + */ +static struct GNUNET_TRANSPORT_CoreHandle *handle; + +/** + * Configuration handle + */ +static struct GNUNET_CONFIGURATION_Handle *cfg; + +/** + * Blacklisting handle + */ +struct GNUNET_TRANSPORT_Blacklist *blacklist; + +/** + * Option -s. + */ +static int benchmark_send; + +/** + * Option -b. + */ +static int benchmark_receive; + +/** + * Option -l. + */ +static int benchmark_receive; + +/** + * Option -i. + */ +static int iterate_connections; + +/** + * Option -a. + */ +static int iterate_all; + +/** + * Option -c. + */ +static int monitor_connects; + +/** + * Option -m. + */ +static int monitor_connections; + +/** + * Option -P. + */ +static int monitor_plugins; + +/** + * Option -D. + */ +static int do_disconnect; + +/** + * Option -n. + */ +static int numeric; + +/** + * Global return value (0 success). + */ +static int ret; + +/** + * Current number of connections in monitor mode + */ +static int monitor_connect_counter; + +/** + * Number of bytes of traffic we received so far. + */ +static unsigned long long traffic_received; + +/** + * Number of bytes of traffic we sent so far. + */ +static unsigned long long traffic_sent; + +/** + * Starting time of transmitting/receiving data. + */ +static struct GNUNET_TIME_Absolute start_time; + +/** + * Map storing information about monitored peers + */ +static struct GNUNET_CONTAINER_MultiPeerMap *monitored_peers; + +/** + * Map storing information about monitored plugins's sessions. + */ +static struct GNUNET_CONTAINER_MultiPeerMap *monitored_plugins; + +/** + * Handle if we are monitoring peers at the transport level. + */ +static struct GNUNET_TRANSPORT_PeerMonitoringContext *pic; + +/** + * Handle if we are monitoring plugin session activity. + */ +static struct GNUNET_TRANSPORT_PluginMonitor *pm; + +/** + * Identity of the peer we transmit to / connect to. + * ('-p' command-line option). + */ +static struct GNUNET_PeerIdentity pid; + +/** + * Task for operation timeout + */ +static struct GNUNET_SCHEDULER_Task *op_timeout; + +/** + * Selected level of verbosity. + */ +static unsigned int verbosity; + +/** + * Resolver process handle. + */ +struct GNUNET_OS_Process *resolver; + +/** + * Number of address resolutions pending + */ +static unsigned int address_resolutions; + +/** + * DLL: head of validation resolution entries + */ +static struct ValidationResolutionContext *vc_head; + +/** + * DLL: tail of validation resolution entries + */ +static struct ValidationResolutionContext *vc_tail; + +/** + * DLL: head of resolution entries + */ +static struct PeerResolutionContext *rc_head; + +/** + * DLL: head of resolution entries + */ +static struct PeerResolutionContext *rc_tail; + + +/** + * Function called to release data stored in the #monitored_peers map. + * + * @param cls unused + * @param key the peer identity + * @param value a `struct MonitoredPeer` to release + * @return #GNUNET_OK (continue to iterate) + */ +static int +destroy_it (void *cls, const struct GNUNET_PeerIdentity *key, void *value) +{ + struct MonitoredPeer *m = value; + + GNUNET_assert ( + GNUNET_OK == + GNUNET_CONTAINER_multipeermap_remove (monitored_peers, key, value)); + GNUNET_free (m->address); + GNUNET_free (value); + return GNUNET_OK; +} + + +/** + * Task run in monitor mode when the user presses CTRL-C to abort. + * Stops monitoring activity. + * + * @param cls NULL + */ +static void +shutdown_task (void *cls) +{ + struct GNUNET_TIME_Relative duration; + struct ValidationResolutionContext *cur; + struct ValidationResolutionContext *next; + struct PeerResolutionContext *rc; + + if (NULL != op_timeout) + { + GNUNET_SCHEDULER_cancel (op_timeout); + op_timeout = NULL; + } + if (NULL != pic) + { + GNUNET_TRANSPORT_monitor_peers_cancel (pic); + pic = NULL; + } + if (NULL != pm) + { + GNUNET_TRANSPORT_monitor_plugins_cancel (pm); + pm = NULL; + } + + next = vc_head; + for (cur = next; NULL != cur; cur = next) + { + next = cur->next; + + GNUNET_TRANSPORT_address_to_string_cancel (cur->asc); + GNUNET_CONTAINER_DLL_remove (vc_head, vc_tail, cur); + GNUNET_free (cur->transport); + GNUNET_HELLO_address_free (cur->addrcp); + GNUNET_free (cur); + } + while (NULL != (rc = rc_head)) + { + GNUNET_CONTAINER_DLL_remove (rc_head, rc_tail, rc); + GNUNET_TRANSPORT_address_to_string_cancel (rc->asc); + GNUNET_free (rc->transport); + GNUNET_free (rc->addrcp); + GNUNET_free (rc); + } + if (NULL != handle) + { + GNUNET_TRANSPORT_core_disconnect (handle); + handle = NULL; + } + if (benchmark_send) + { + duration = GNUNET_TIME_absolute_get_duration (start_time); + fprintf (stdout, + _ ("Transmitted %llu bytes/s (%llu bytes in %s)\n"), + 1000LL * 1000LL * traffic_sent / (1 + duration.rel_value_us), + traffic_sent, + GNUNET_STRINGS_relative_time_to_string (duration, GNUNET_YES)); + } + if (benchmark_receive) + { + duration = GNUNET_TIME_absolute_get_duration (start_time); + fprintf (stdout, + _ ("Received %llu bytes/s (%llu bytes in %s)\n"), + 1000LL * 1000LL * traffic_received / (1 + duration.rel_value_us), + traffic_received, + GNUNET_STRINGS_relative_time_to_string (duration, GNUNET_YES)); + } + + if (NULL != monitored_peers) + { + GNUNET_CONTAINER_multipeermap_iterate (monitored_peers, &destroy_it, NULL); + GNUNET_CONTAINER_multipeermap_destroy (monitored_peers); + monitored_peers = NULL; + } + if (NULL != monitored_plugins) + { + GNUNET_break (0 == GNUNET_CONTAINER_multipeermap_size (monitored_plugins)); + GNUNET_CONTAINER_multipeermap_destroy (monitored_plugins); + monitored_plugins = NULL; + } + if (NULL != blacklist) + { + GNUNET_TRANSPORT_blacklist_cancel (blacklist); + blacklist = NULL; + ret = 0; + } +} + + +/** + * We are done, shut down. + */ +static void +operation_timeout (void *cls) +{ + struct PeerResolutionContext *cur; + struct PeerResolutionContext *next; + + op_timeout = NULL; + if ((benchmark_send) || (benchmark_receive)) + { + fprintf (stdout, _ ("Failed to connect to `%s'\n"), GNUNET_i2s_full (&pid)); + GNUNET_SCHEDULER_shutdown (); + ret = 1; + return; + } + if (iterate_connections) + { + next = rc_head; + while (NULL != (cur = next)) + { + next = cur->next; + fprintf (stdout, + _ ("Failed to resolve address for peer `%s'\n"), + GNUNET_i2s (&cur->addrcp->peer)); + + GNUNET_CONTAINER_DLL_remove (rc_head, rc_tail, cur); + GNUNET_TRANSPORT_address_to_string_cancel (cur->asc); + GNUNET_free (cur->transport); + GNUNET_free (cur->addrcp); + GNUNET_free (cur); + } + fprintf (stdout, + "%s", + _ ("Failed to list connections, timeout occurred\n")); + GNUNET_SCHEDULER_shutdown (); + ret = 1; + return; + } +} + + +/** + * Function called to notify a client about the socket + * begin ready to queue more data. Sends another message. + * + * @param cls closure with the message queue + */ +static void +do_send (void *cls) +{ + struct GNUNET_MQ_Handle *mq = cls; + struct GNUNET_MessageHeader *m; + struct GNUNET_MQ_Envelope *env; + + env = GNUNET_MQ_msg_extra (m, BLOCKSIZE * 1024, GNUNET_MESSAGE_TYPE_DUMMY); + memset (&m[1], 52, BLOCKSIZE * 1024 - sizeof(struct GNUNET_MessageHeader)); + traffic_sent += BLOCKSIZE * 1024; + GNUNET_MQ_notify_sent (env, &do_send, mq); + if (verbosity > 0) + fprintf (stdout, + _ ("Transmitting %u bytes\n"), + (unsigned int) BLOCKSIZE * 1024); + GNUNET_MQ_send (mq, env); +} + + +/** + * Function called to notify transport users that another + * peer connected to us. + * + * @param cls closure + * @param peer the peer that connected + * @param mq message queue for sending to @a peer + */ +static void * +notify_connect (void *cls, + const struct GNUNET_PeerIdentity *peer, + struct GNUNET_MQ_Handle *mq) +{ + if (0 != memcmp (&pid, peer, sizeof(struct GNUNET_PeerIdentity))) + return NULL; + ret = 0; + if (! benchmark_send) + return NULL; + if (NULL != op_timeout) + { + GNUNET_SCHEDULER_cancel (op_timeout); + op_timeout = NULL; + } + if (verbosity > 0) + fprintf ( + stdout, + _ ( + "Successfully connected to `%s', starting to send benchmark data in %u Kb blocks\n"), + GNUNET_i2s (peer), + BLOCKSIZE); + start_time = GNUNET_TIME_absolute_get (); + do_send (mq); + return mq; +} + + +/** + * Function called to notify transport users that another + * peer disconnected from us. + * + * @param cls closure + * @param peer the peer that disconnected + * @param internal_cls what we returned from #notify_connect() + */ +static void +notify_disconnect (void *cls, + const struct GNUNET_PeerIdentity *peer, + void *internal_cls) +{ + if (0 != memcmp (&pid, peer, sizeof(struct GNUNET_PeerIdentity))) + return; + if (NULL == internal_cls) + return; /* not about target peer */ + if (! benchmark_send) + return; /* not transmitting */ + fprintf (stdout, + _ ("Disconnected from peer `%s' while benchmarking\n"), + GNUNET_i2s (&pid)); +} + + +/** + * Function called to notify transport users that another + * peer connected to us. + * + * @param cls closure + * @param peer the peer that connected + * @param mq for sending messages to @a peer + * @return NULL + */ +static void * +monitor_notify_connect (void *cls, + const struct GNUNET_PeerIdentity *peer, + struct GNUNET_MQ_Handle *mq) +{ + struct GNUNET_TIME_Absolute now = GNUNET_TIME_absolute_get (); + const char *now_str = GNUNET_STRINGS_absolute_time_to_string (now); + + monitor_connect_counter++; + fprintf (stdout, + _ ("%24s: %-17s %4s (%u connections in total)\n"), + now_str, + _ ("Connected to"), + GNUNET_i2s (peer), + monitor_connect_counter); + return NULL; +} + + +/** + * Function called to notify transport users that another + * peer disconnected from us. + * + * @param cls closure + * @param peer the peer that disconnected + * @param internal_cls what we returned from #monitor_notify_connect() + */ +static void +monitor_notify_disconnect (void *cls, + const struct GNUNET_PeerIdentity *peer, + void *internal_cls) +{ + struct GNUNET_TIME_Absolute now = GNUNET_TIME_absolute_get (); + const char *now_str = GNUNET_STRINGS_absolute_time_to_string (now); + + GNUNET_assert (monitor_connect_counter > 0); + monitor_connect_counter--; + + fprintf (stdout, + _ ("%24s: %-17s %4s (%u connections in total)\n"), + now_str, + _ ("Disconnected from"), + GNUNET_i2s (peer), + monitor_connect_counter); +} + + +/** + * Function called by the transport for each received message. + * + * @param cls closure + * @param message the message + * @return #GNUNET_OK + */ +static int +check_dummy (void *cls, const struct GNUNET_MessageHeader *message) +{ + return GNUNET_OK; /* all messages are fine */ +} + + +/** + * Function called by the transport for each received message. + * + * @param cls closure + * @param message the message + */ +static void +handle_dummy (void *cls, const struct GNUNET_MessageHeader *message) +{ + if (! benchmark_receive) + return; + if (verbosity > 0) + fprintf (stdout, + _ ("Received %u bytes\n"), + (unsigned int) ntohs (message->size)); + if (0 == traffic_received) + start_time = GNUNET_TIME_absolute_get (); + traffic_received += ntohs (message->size); +} + + +/** + * Convert address to a printable format. + * + * @param address the address + * @param numeric #GNUNET_YES to convert to numeric format, #GNUNET_NO + * to try to use reverse DNS + * @param state state the peer is in + * @param state_timeout when will the peer's state expire + */ +static void +resolve_peer_address (const struct GNUNET_HELLO_Address *address, + int numeric, + enum GNUNET_TRANSPORT_PeerState state, + struct GNUNET_TIME_Absolute state_timeout); + + +static void +print_info (const struct GNUNET_PeerIdentity *id, + const char *transport, + const char *addr, + enum GNUNET_TRANSPORT_PeerState state, + struct GNUNET_TIME_Absolute state_timeout) +{ + if (((GNUNET_YES == iterate_connections) && (GNUNET_YES == iterate_all)) || + (GNUNET_YES == monitor_connections)) + { + fprintf (stdout, + _ ("Peer `%s': %s %s in state `%s' until %s\n"), + GNUNET_i2s (id), + (NULL == transport) ? "" : transport, + (NULL == transport) ? "" : addr, + GNUNET_TRANSPORT_ps2s (state), + GNUNET_STRINGS_absolute_time_to_string (state_timeout)); + } + else if ((GNUNET_YES == iterate_connections) && + (GNUNET_TRANSPORT_is_connected (state))) + { + /* Only connected peers, skip state */ + fprintf (stdout, + _ ("Peer `%s': %s %s\n"), + GNUNET_i2s (id), + transport, + addr); + } +} + + +/** + * Function called with a textual representation of an address. This + * function will be called several times with different possible + * textual representations, and a last time with @a address being NULL + * to signal the end of the iteration. Note that @a address NULL + * always is the last call, regardless of the value in @a res. + * + * @param cls closure + * @param address NULL on end of iteration, + * otherwise 0-terminated printable UTF-8 string, + * in particular an empty string if @a res is #GNUNET_NO + * @param res result of the address to string conversion: + * if #GNUNET_OK: conversion successful + * if #GNUNET_NO: address was invalid (or not supported) + * if #GNUNET_SYSERR: communication error (IPC error) + */ +static void +process_peer_string (void *cls, const char *address, int res) +{ + struct PeerResolutionContext *rc = cls; + + if (NULL != address) + { + if (GNUNET_SYSERR == res) + { + fprintf ( + stderr, + "Failed to convert address for peer `%s' plugin `%s' length %u to string \n", + GNUNET_i2s (&rc->addrcp->peer), + rc->addrcp->transport_name, + (unsigned int) rc->addrcp->address_length); + print_info (&rc->addrcp->peer, + rc->transport, + NULL, + rc->state, + rc->state_timeout); + rc->printed = GNUNET_YES; + return; + } + if (GNUNET_OK == res) + { + print_info (&rc->addrcp->peer, + rc->transport, + address, + rc->state, + rc->state_timeout); + rc->printed = GNUNET_YES; + return; /* Wait for done call */ + } + /* GNUNET_NO == res: ignore, was simply not supported */ + return; + } + /* NULL == address, last call, we are done */ + + rc->asc = NULL; + GNUNET_assert (address_resolutions > 0); + address_resolutions--; + if (GNUNET_NO == rc->printed) + { + if (numeric == GNUNET_NO) + { + /* Failed to resolve address, try numeric lookup + (note: this should not be needed, as transport + should fallback to numeric conversion if DNS takes + too long) */ + resolve_peer_address (rc->addrcp, + GNUNET_YES, + rc->state, + rc->state_timeout); + } + else + { + print_info (&rc->addrcp->peer, + rc->transport, + NULL, + rc->state, + rc->state_timeout); + } + } + GNUNET_free (rc->transport); + GNUNET_free (rc->addrcp); + GNUNET_CONTAINER_DLL_remove (rc_head, rc_tail, rc); + GNUNET_free (rc); + if ((0 == address_resolutions) && (iterate_connections)) + { + if (NULL != op_timeout) + { + GNUNET_SCHEDULER_cancel (op_timeout); + op_timeout = NULL; + } + ret = 0; + GNUNET_SCHEDULER_shutdown (); + } +} + + +/** + * Convert address to a printable format and print it + * together with the given state data. + * + * @param address the address + * @param numeric #GNUNET_YES to convert to numeric format, #GNUNET_NO + * to try to use reverse DNS + * @param state state the peer is in + * @param state_timeout when will the peer's state expire + */ +static void +resolve_peer_address (const struct GNUNET_HELLO_Address *address, + int numeric, + enum GNUNET_TRANSPORT_PeerState state, + struct GNUNET_TIME_Absolute state_timeout) +{ + struct PeerResolutionContext *rc; + + rc = GNUNET_new (struct PeerResolutionContext); + GNUNET_CONTAINER_DLL_insert (rc_head, rc_tail, rc); + address_resolutions++; + rc->transport = GNUNET_strdup (address->transport_name); + rc->addrcp = GNUNET_HELLO_address_copy (address); + rc->printed = GNUNET_NO; + rc->state = state; + rc->state_timeout = state_timeout; + /* Resolve address to string */ + rc->asc = GNUNET_TRANSPORT_address_to_string (cfg, + address, + numeric, + RESOLUTION_TIMEOUT, + &process_peer_string, + rc); +} + + +/** + * Function called with information about a peers during a one shot iteration + * + * @param cls closure + * @param peer identity of the peer, NULL for final callback when operation done + * @param address binary address used to communicate with this peer, + * NULL on disconnect or when done + * @param state current state this peer is in + * @param state_timeout time out for the current state + */ +static void +process_peer_iteration_cb (void *cls, + const struct GNUNET_PeerIdentity *peer, + const struct GNUNET_HELLO_Address *address, + enum GNUNET_TRANSPORT_PeerState state, + struct GNUNET_TIME_Absolute state_timeout) +{ + if (NULL == peer) + { + /* done */ + pic = NULL; + return; + } + + if ((GNUNET_NO == iterate_all) && + (GNUNET_NO == GNUNET_TRANSPORT_is_connected (state))) + return; /* Display only connected peers */ + + if (NULL != op_timeout) + GNUNET_SCHEDULER_cancel (op_timeout); + op_timeout = + GNUNET_SCHEDULER_add_delayed (OP_TIMEOUT, &operation_timeout, NULL); + + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Received address for peer `%s': %s\n", + GNUNET_i2s (peer), + address ? address->transport_name : ""); + + if (NULL != address) + resolve_peer_address (address, numeric, state, state_timeout); + else + print_info (peer, NULL, NULL, state, state_timeout); +} + + +/** + * Context for address resolution by #plugin_monitoring_cb(). + */ +struct PluginMonitorAddress +{ + /** + * Ongoing resolution request. + */ + struct GNUNET_TRANSPORT_AddressToStringContext *asc; + + /** + * Resolved address as string. + */ + char *str; + + /** + * Last event we got and did not yet print because + * @e str was NULL (address not yet resolved). + */ + struct GNUNET_TRANSPORT_SessionInfo si; +}; + + +/** + * Print information about a plugin monitoring event. + * + * @param addr out internal context + * @param info the monitoring information + */ +static void +print_plugin_event_info (struct PluginMonitorAddress *addr, + const struct GNUNET_TRANSPORT_SessionInfo *info) +{ + const char *state; + + switch (info->state) + { + case GNUNET_TRANSPORT_SS_INIT: + state = "INIT"; + break; + + case GNUNET_TRANSPORT_SS_HANDSHAKE: + state = "HANDSHAKE"; + break; + + case GNUNET_TRANSPORT_SS_UP: + state = "UP"; + break; + + case GNUNET_TRANSPORT_SS_UPDATE: + state = "UPDATE"; + break; + + case GNUNET_TRANSPORT_SS_DONE: + state = "DONE"; + break; + + default: + state = "UNKNOWN"; + break; + } + fprintf (stdout, + "%s: state %s timeout in %s @ %s%s\n", + GNUNET_i2s (&info->address->peer), + state, + GNUNET_STRINGS_relative_time_to_string ( + GNUNET_TIME_absolute_get_remaining (info->session_timeout), + GNUNET_YES), + addr->str, + (info->is_inbound == GNUNET_YES) ? " (INBOUND)" : ""); + fprintf (stdout, + "%s: queue has %3u messages and %6u bytes\n", + GNUNET_i2s (&info->address->peer), + info->num_msg_pending, + info->num_bytes_pending); + if (0 != + GNUNET_TIME_absolute_get_remaining (info->receive_delay).rel_value_us) + fprintf (stdout, + "%s: receiving blocked until %s\n", + GNUNET_i2s (&info->address->peer), + GNUNET_STRINGS_absolute_time_to_string (info->receive_delay)); +} + + +/** + * Function called with a textual representation of an address. This + * function will be called several times with different possible + * textual representations, and a last time with @a address being NULL + * to signal the end of the iteration. Note that @a address NULL + * always is the last call, regardless of the value in @a res. + * + * @param cls closure + * @param address NULL on end of iteration, + * otherwise 0-terminated printable UTF-8 string, + * in particular an empty string if @a res is #GNUNET_NO + * @param res result of the address to string conversion: + * if #GNUNET_OK: conversion successful + * if #GNUNET_NO: address was invalid (or not supported) + * if #GNUNET_SYSERR: communication error (IPC error) + */ +static void +address_cb (void *cls, const char *address, int res) +{ + struct PluginMonitorAddress *addr = cls; + + if (NULL == address) + { + addr->asc = NULL; + return; + } + if (NULL != addr->str) + return; + addr->str = GNUNET_strdup (address); + print_plugin_event_info (addr, &addr->si); +} + + +/** + * Function called by the plugin with information about the + * current sessions managed by the plugin (for monitoring). + * + * @param cls closure (NULL) + * @param session session handle this information is about, + * NULL to indicate that we are "in sync" (initial + * iteration complete) + * @param session_ctx storage location where the application + * can store data; will point to NULL on #GNUNET_TRANSPORT_SS_INIT, + * and must be reset to NULL on #GNUNET_TRANSPORT_SS_DONE + * @param info information about the state of the session, + * NULL if @a session is also NULL and we are + * merely signalling that the initial iteration is over; + * NULL with @a session being non-NULL if the monitor + * was being cancelled while sessions were active + */ +static void +plugin_monitoring_cb (void *cls, + struct GNUNET_TRANSPORT_PluginSession *session, + void **session_ctx, + const struct GNUNET_TRANSPORT_SessionInfo *info) +{ + struct PluginMonitorAddress *addr; + + if ((NULL == info) && (NULL == session)) + return; /* in sync with transport service */ + addr = *session_ctx; + if (NULL == info) + { + if (NULL != addr) + { + if (NULL != addr->asc) + { + GNUNET_TRANSPORT_address_to_string_cancel (addr->asc); + addr->asc = NULL; + } + GNUNET_free (addr->str); + GNUNET_free (addr); + *session_ctx = NULL; + } + return; /* shutdown */ + } + if (0 != + memcmp (&info->address->peer, &pid, sizeof(struct GNUNET_PeerIdentity))) + return; /* filtered */ + if (NULL == addr) + { + addr = GNUNET_new (struct PluginMonitorAddress); + addr->asc = + GNUNET_TRANSPORT_address_to_string (cfg, + info->address, + numeric, + GNUNET_TIME_UNIT_FOREVER_REL, + &address_cb, + addr); + *session_ctx = addr; + } + if (NULL == addr->str) + addr->si = *info; + else + print_plugin_event_info (addr, info); + if (GNUNET_TRANSPORT_SS_DONE == info->state) + { + if (NULL != addr->asc) + { + GNUNET_TRANSPORT_address_to_string_cancel (addr->asc); + addr->asc = NULL; + } + GNUNET_free (addr->str); + GNUNET_free (addr); + *session_ctx = NULL; + } +} + + +/** + * Function called with information about a peers + * + * @param cls closure, NULL + * @param peer identity of the peer, NULL for final callback when operation done + * @param address binary address used to communicate with this peer, + * NULL on disconnect or when done + * @param state current state this peer is in + * @param state_timeout time out for the current state + */ +static void +process_peer_monitoring_cb (void *cls, + const struct GNUNET_PeerIdentity *peer, + const struct GNUNET_HELLO_Address *address, + enum GNUNET_TRANSPORT_PeerState state, + struct GNUNET_TIME_Absolute state_timeout) +{ + struct MonitoredPeer *m; + + if (NULL == peer) + { + fprintf (stdout, + "%s", + _ ( + "Monitor disconnected from transport service. Reconnecting.\n")); + return; + } + + if (NULL != op_timeout) + GNUNET_SCHEDULER_cancel (op_timeout); + op_timeout = + GNUNET_SCHEDULER_add_delayed (OP_TIMEOUT, &operation_timeout, NULL); + + if (NULL == (m = GNUNET_CONTAINER_multipeermap_get (monitored_peers, peer))) + { + m = GNUNET_new (struct MonitoredPeer); + GNUNET_CONTAINER_multipeermap_put ( + monitored_peers, + peer, + m, + GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_FAST); + } + else + { + if ((m->state == state) && + (m->state_timeout.abs_value_us == state_timeout.abs_value_us) && + (NULL == address) && (NULL == m->address)) + { + return; /* No real change */ + } + if ((m->state == state) && (NULL != address) && (NULL != m->address) && + (0 == GNUNET_HELLO_address_cmp (m->address, address))) + return; /* No real change */ + } + + if (NULL != m->address) + { + GNUNET_free (m->address); + m->address = NULL; + } + if (NULL != address) + m->address = GNUNET_HELLO_address_copy (address); + m->state = state; + m->state_timeout = state_timeout; + + if (NULL != address) + resolve_peer_address (m->address, numeric, m->state, m->state_timeout); + else + print_info (peer, NULL, NULL, m->state, m->state_timeout); +} + + +/** + * Function called with the transport service checking if we + * want to blacklist a peer. Return #GNUNET_SYSERR for the + * peer that we should disconnect from. + * + * @param cls NULL + * @param cpid peer to check blacklisting for + * @return #GNUNET_OK if the connection is allowed, #GNUNET_SYSERR if not + */ +static int +blacklist_cb (void *cls, const struct GNUNET_PeerIdentity *cpid) +{ + if (0 == memcmp (cpid, &pid, sizeof(struct GNUNET_PeerIdentity))) + return GNUNET_SYSERR; + return GNUNET_OK; +} + + +/** + * 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 mycfg configuration + */ +static void +run (void *cls, + char *const *args, + const char *cfgfile, + const struct GNUNET_CONFIGURATION_Handle *mycfg) +{ + static struct GNUNET_PeerIdentity zero_pid; + int counter = 0; + + ret = 1; + + cfg = (struct GNUNET_CONFIGURATION_Handle *) mycfg; + + counter = benchmark_send + benchmark_receive + iterate_connections + + monitor_connections + monitor_connects + do_disconnect + + monitor_plugins; + + if (1 < counter) + { + fprintf ( + stderr, + _ ( + "Multiple operations given. Please choose only one operation: %s, %s, %s, %s, %s, %s %s\n"), + "disconnect", + "benchmark send", + "benchmark receive", + "information", + "monitor", + "events", + "plugins"); + return; + } + if (0 == counter) + { + fprintf ( + stderr, + _ ( + "No operation given. Please choose one operation: %s, %s, %s, %s, %s, %s, %s\n"), + "disconnect", + "benchmark send", + "benchmark receive", + "information", + "monitor", + "events", + "plugins"); + return; + } + + if (do_disconnect) /* -D: Disconnect from peer */ + { + if (0 == memcmp (&zero_pid, &pid, sizeof(pid))) + { + fprintf (stderr, + _ ("Option `%s' makes no sense without option `%s'.\n"), + "-D", + "-p"); + ret = 1; + return; + } + blacklist = GNUNET_TRANSPORT_blacklist (cfg, &blacklist_cb, NULL); + if (NULL == blacklist) + { + fprintf (stderr, + "%s", + _ ( + "Failed to connect to transport service for disconnection\n")); + ret = 1; + return; + } + fprintf (stdout, + "%s", + _ ("Blacklisting request in place, stop with CTRL-C\n")); + } + else if (benchmark_send) /* -s: Benchmark sending */ + { + if (0 == memcmp (&zero_pid, &pid, sizeof(pid))) + { + fprintf (stderr, + _ ("Option `%s' makes no sense without option `%s'.\n"), + "-s", + "-p"); + ret = 1; + return; + } + handle = GNUNET_TRANSPORT_core_connect (cfg, + NULL, + NULL, + NULL, + ¬ify_connect, + ¬ify_disconnect, + NULL); + if (NULL == handle) + { + fprintf (stderr, "%s", _ ("Failed to connect to transport service\n")); + ret = 1; + return; + } + start_time = GNUNET_TIME_absolute_get (); + op_timeout = + GNUNET_SCHEDULER_add_delayed (OP_TIMEOUT, &operation_timeout, NULL); + } + else if (benchmark_receive) /* -b: Benchmark receiving */ + { + struct GNUNET_MQ_MessageHandler handlers[] = + { GNUNET_MQ_hd_var_size (dummy, + GNUNET_MESSAGE_TYPE_DUMMY, + struct GNUNET_MessageHeader, + NULL), + GNUNET_MQ_handler_end () }; + + handle = GNUNET_TRANSPORT_core_connect (cfg, + NULL, + handlers, + NULL, + NULL, + NULL, + NULL); + if (NULL == handle) + { + fprintf (stderr, "%s", _ ("Failed to connect to transport service\n")); + ret = 1; + return; + } + if (verbosity > 0) + fprintf (stdout, "%s", _ ("Starting to receive benchmark data\n")); + start_time = GNUNET_TIME_absolute_get (); + } + else if (iterate_connections) /* -i: List information about peers once */ + { + pic = GNUNET_TRANSPORT_monitor_peers (cfg, + &pid, + GNUNET_YES, + &process_peer_iteration_cb, + (void *) cfg); + op_timeout = + GNUNET_SCHEDULER_add_delayed (OP_TIMEOUT, &operation_timeout, NULL); + } + else if (monitor_connections) /* -m: List information about peers continuously + */ + { + monitored_peers = GNUNET_CONTAINER_multipeermap_create (10, GNUNET_NO); + pic = GNUNET_TRANSPORT_monitor_peers (cfg, + &pid, + GNUNET_NO, + &process_peer_monitoring_cb, + NULL); + } + else if (monitor_plugins) /* -P: List information about plugins continuously + */ + { + monitored_plugins = GNUNET_CONTAINER_multipeermap_create (10, GNUNET_NO); + pm = GNUNET_TRANSPORT_monitor_plugins (cfg, &plugin_monitoring_cb, NULL); + } + else if (monitor_connects) /* -e : Monitor (dis)connect events continuously */ + { + monitor_connect_counter = 0; + handle = GNUNET_TRANSPORT_core_connect (cfg, + NULL, + NULL, + NULL, + &monitor_notify_connect, + &monitor_notify_disconnect, + NULL); + if (NULL == handle) + { + fprintf (stderr, "%s", _ ("Failed to connect to transport service\n")); + ret = 1; + return; + } + ret = 0; + } + else + { + GNUNET_break (0); + return; + } + + GNUNET_SCHEDULER_add_shutdown (&shutdown_task, NULL); +} + + +int +main (int argc, char *const *argv) +{ + int res; + struct GNUNET_GETOPT_CommandLineOption options[] = + { GNUNET_GETOPT_option_flag ( + 'a', + "all", + gettext_noop ( + "print information for all peers (instead of only connected peers)"), + &iterate_all), + GNUNET_GETOPT_option_flag ( + 'b', + "benchmark", + gettext_noop ( + "measure how fast we are receiving data from all peers (until CTRL-C)"), + &benchmark_receive), + GNUNET_GETOPT_option_flag ('D', + "disconnect", + gettext_noop ("disconnect from a peer"), + &do_disconnect), + GNUNET_GETOPT_option_flag ( + 'i', + "information", + gettext_noop ( + "provide information about all current connections (once)"), + &iterate_connections), + GNUNET_GETOPT_option_flag ( + 'm', + "monitor", + gettext_noop ( + "provide information about all current connections (continuously)"), + &monitor_connections), + GNUNET_GETOPT_option_flag ( + 'e', + "events", + gettext_noop ( + "provide information about all connects and disconnect events (continuously)"), + &monitor_connects), + GNUNET_GETOPT_option_flag ('n', + "numeric", + gettext_noop ("do not resolve hostnames"), + &numeric), + GNUNET_GETOPT_option_base32_auto ('p', + "peer", + "PEER", + gettext_noop ("peer identity"), + &pid), + GNUNET_GETOPT_option_flag ('P', + "plugins", + gettext_noop ("monitor plugin sessions"), + &monitor_plugins), + GNUNET_GETOPT_option_flag ( + 's', + "send", + gettext_noop ( + "send data for benchmarking to the other peer (until CTRL-C)"), + &benchmark_send), + GNUNET_GETOPT_option_verbose (&verbosity), + GNUNET_GETOPT_OPTION_END }; + + if (GNUNET_OK != GNUNET_STRINGS_get_utf8_args (argc, argv, &argc, &argv)) + return 2; + + res = + GNUNET_PROGRAM_run (argc, + argv, + "gnunet-transport", + gettext_noop ("Direct access to transport service."), + options, + &run, + NULL); + GNUNET_free_nz ((void *) argv); + if (GNUNET_OK == res) + return ret; + return 1; +} + + +/* end of gnunet-transport.c */ diff --git a/src/service/transport/ieee80211_radiotap.h b/src/service/transport/ieee80211_radiotap.h new file mode 100644 index 000000000..9351da9a5 --- /dev/null +++ b/src/service/transport/ieee80211_radiotap.h @@ -0,0 +1,276 @@ +/* + * Copyright (c) 2003, 2004 David Young. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. The name of David Young may not be used to endorse or promote + * products derived from this software without specific prior + * written permission. + * + * THIS SOFTWARE IS PROVIDED BY DAVID YOUNG ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A + * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL DAVID + * YOUNG BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED + * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY + * OF SUCH DAMAGE. + */ + +/* + * Modifications to fit into the linux IEEE 802.11 stack, + * Mike Kershaw (dragorn@kismetwireless.net) + */ + +#ifndef IEEE80211RADIOTAP_H +#define IEEE80211RADIOTAP_H + +#include +#include +// #include + +/* Base version of the radiotap packet header data */ +#define PKTHDR_RADIOTAP_VERSION 0 + +/* A generic radio capture format is desirable. There is one for + * Linux, but it is neither rigidly defined (there were not even + * units given for some fields) nor easily extensible. + * + * I suggest the following extensible radio capture format. It is + * based on a bitmap indicating which fields are present. + * + * I am trying to describe precisely what the application programmer + * should expect in the following, and for that reason I tell the + * units and origin of each measurement (where it applies), or else I + * use sufficiently weaselly language ("is a monotonically nondecreasing + * function of...") that I cannot set false expectations for lawyerly + * readers. + */ + +/* + * The radio capture header precedes the 802.11 header. + * All data in the header is little endian on all platforms. + */ +struct ieee80211_radiotap_header +{ + u8 it_version; /* Version 0. Only increases + * for drastic changes, + * introduction of compatible + * new fields does not count. + */ + u8 it_pad; + __le16 it_len; /* length of the whole + * header in bytes, including + * it_version, it_pad, + * it_len, and data fields. + */ + __le32 it_present; /* A bitmap telling which + * fields are present. Set bit 31 + * (0x80000000) to extend the + * bitmap by another 32 bits. + * Additional extensions are made + * by setting bit 31. + */ +} __packed; + +/* Name Data type Units + * ---- --------- ----- + * + * IEEE80211_RADIOTAP_TSFT __le64 microseconds + * + * Value in microseconds of the MAC's 64-bit 802.11 Time + * Synchronization Function timer when the first bit of the + * MPDU arrived at the MAC. For received frames, only. + * + * IEEE80211_RADIOTAP_CHANNEL 2 x __le16 MHz, bitmap + * + * Tx/Rx frequency in MHz, followed by flags (see below). + * + * IEEE80211_RADIOTAP_FHSS __le16 see below + * + * For frequency-hopping radios, the hop set (first byte) + * and pattern (second byte). + * + * IEEE80211_RADIOTAP_RATE u8 500kb/s + * + * Tx/Rx data rate + * + * IEEE80211_RADIOTAP_DBM_ANTSIGNAL s8 decibels from + * one milliwatt (dBm) + * + * RF signal power at the antenna, decibel difference from + * one milliwatt. + * + * IEEE80211_RADIOTAP_DBM_ANTNOISE s8 decibels from + * one milliwatt (dBm) + * + * RF noise power at the antenna, decibel difference from one + * milliwatt. + * + * IEEE80211_RADIOTAP_DB_ANTSIGNAL u8 decibel (dB) + * + * RF signal power at the antenna, decibel difference from an + * arbitrary, fixed reference. + * + * IEEE80211_RADIOTAP_DB_ANTNOISE u8 decibel (dB) + * + * RF noise power at the antenna, decibel difference from an + * arbitrary, fixed reference point. + * + * IEEE80211_RADIOTAP_LOCK_QUALITY __le16 unitless + * + * Quality of Barker code lock. Unitless. Monotonically + * nondecreasing with "better" lock strength. Called "Signal + * Quality" in datasheets. (Is there a standard way to measure + * this?) + * + * IEEE80211_RADIOTAP_TX_ATTENUATION __le16 unitless + * + * Transmit power expressed as unitless distance from max + * power set at factory calibration. 0 is max power. + * Monotonically nondecreasing with lower power levels. + * + * IEEE80211_RADIOTAP_DB_TX_ATTENUATION __le16 decibels (dB) + * + * Transmit power expressed as decibel distance from max power + * set at factory calibration. 0 is max power. Monotonically + * nondecreasing with lower power levels. + * + * IEEE80211_RADIOTAP_DBM_TX_POWER s8 decibels from + * one milliwatt (dBm) + * + * Transmit power expressed as dBm (decibels from a 1 milliwatt + * reference). This is the absolute power level measured at + * the antenna port. + * + * IEEE80211_RADIOTAP_FLAGS u8 bitmap + * + * Properties of transmitted and received frames. See flags + * defined below. + * + * IEEE80211_RADIOTAP_ANTENNA u8 antenna index + * + * Unitless indication of the Rx/Tx antenna for this packet. + * The first antenna is antenna 0. + * + * IEEE80211_RADIOTAP_RX_FLAGS __le16 bitmap + * + * Properties of received frames. See flags defined below. + * + * IEEE80211_RADIOTAP_TX_FLAGS __le16 bitmap + * + * Properties of transmitted frames. See flags defined below. + * + * IEEE80211_RADIOTAP_RTS_RETRIES u8 data + * + * Number of rts retries a transmitted frame used. + * + * IEEE80211_RADIOTAP_DATA_RETRIES u8 data + * + * Number of unicast retries a transmitted frame used. + * + */ +enum ieee80211_radiotap_type +{ + IEEE80211_RADIOTAP_TSFT = 0, + IEEE80211_RADIOTAP_FLAGS = 1, + IEEE80211_RADIOTAP_RATE = 2, + IEEE80211_RADIOTAP_CHANNEL = 3, + IEEE80211_RADIOTAP_FHSS = 4, + IEEE80211_RADIOTAP_DBM_ANTSIGNAL = 5, + IEEE80211_RADIOTAP_DBM_ANTNOISE = 6, + IEEE80211_RADIOTAP_LOCK_QUALITY = 7, + IEEE80211_RADIOTAP_TX_ATTENUATION = 8, + IEEE80211_RADIOTAP_DB_TX_ATTENUATION = 9, + IEEE80211_RADIOTAP_DBM_TX_POWER = 10, + IEEE80211_RADIOTAP_ANTENNA = 11, + IEEE80211_RADIOTAP_DB_ANTSIGNAL = 12, + IEEE80211_RADIOTAP_DB_ANTNOISE = 13, + IEEE80211_RADIOTAP_RX_FLAGS = 14, + IEEE80211_RADIOTAP_TX_FLAGS = 15, + IEEE80211_RADIOTAP_RTS_RETRIES = 16, + IEEE80211_RADIOTAP_DATA_RETRIES = 17, + + /* valid in every it_present bitmap, even vendor namespaces */ + IEEE80211_RADIOTAP_RADIOTAP_NAMESPACE = 29, + IEEE80211_RADIOTAP_VENDOR_NAMESPACE = 30, + IEEE80211_RADIOTAP_EXT = 31 +}; + +/* Channel flags. */ +#define IEEE80211_CHAN_TURBO 0x0010 /* Turbo channel */ +#define IEEE80211_CHAN_CCK 0x0020 /* CCK channel */ +#define IEEE80211_CHAN_OFDM 0x0040 /* OFDM channel */ +#define IEEE80211_CHAN_2GHZ 0x0080 /* 2 GHz spectrum channel. */ +#define IEEE80211_CHAN_5GHZ 0x0100 /* 5 GHz spectrum channel */ +#define IEEE80211_CHAN_PASSIVE 0x0200 /* Only passive scan allowed */ +#define IEEE80211_CHAN_DYN 0x0400 /* Dynamic CCK-OFDM channel */ +#define IEEE80211_CHAN_GFSK 0x0800 /* GFSK channel (FHSS PHY) */ + +/* For IEEE80211_RADIOTAP_FLAGS */ +#define IEEE80211_RADIOTAP_F_CFP 0x01 /* sent/received + * during CFP + */ +#define IEEE80211_RADIOTAP_F_SHORTPRE 0x02 /* sent/received + * with short + * preamble + */ +#define IEEE80211_RADIOTAP_F_WEP 0x04 /* sent/received + * with WEP encryption + */ +#define IEEE80211_RADIOTAP_F_FRAG 0x08 /* sent/received + * with fragmentation + */ +#define IEEE80211_RADIOTAP_F_FCS 0x10 /* frame includes FCS */ +#define IEEE80211_RADIOTAP_F_DATAPAD 0x20 /* frame has padding between + * 802.11 header and payload + * (to 32-bit boundary) + */ +#define IEEE80211_RADIOTAP_F_BADFCS 0x40 /* bad FCS */ + +/* For IEEE80211_RADIOTAP_RX_FLAGS */ +#define IEEE80211_RADIOTAP_F_RX_BADPLCP 0x0002 /* frame has bad PLCP */ + +/* For IEEE80211_RADIOTAP_TX_FLAGS */ +#define IEEE80211_RADIOTAP_F_TX_FAIL 0x0001 /* failed due to excessive + * retries */ +#define IEEE80211_RADIOTAP_F_TX_CTS 0x0002 /* used cts 'protection' */ +#define IEEE80211_RADIOTAP_F_TX_RTS 0x0004 /* used rts/cts handshake */ + +/* Ugly macro to convert literal channel numbers into their mhz equivalents + * There are certainly some conditions that will break this (like feeding it '30') + * but they shouldn't arise since nothing talks on channel 30. */ +#define ieee80211chan2mhz(x) \ + (((x) <= 14) ? \ + (((x) == 14) ? 2484 : ((x) * 5) + 2407) : \ + ((x) + 1000) * 5) + +/* helpers */ +static inline u16 +get_unaligned_le16 (const u8 *p) +{ + return p[0] | p[1] << 8; +} + + +static inline int +ieee80211_get_radiotap_len (unsigned char *data) +{ + struct ieee80211_radiotap_header *hdr = + (struct ieee80211_radiotap_header *) data; + + return get_unaligned_le16 ((const u8 *) &hdr->it_len); +} + + +#endif /* IEEE80211_RADIOTAP_H */ diff --git a/src/service/transport/meson.build b/src/service/transport/meson.build new file mode 100644 index 000000000..65a2beeb3 --- /dev/null +++ b/src/service/transport/meson.build @@ -0,0 +1,261 @@ +libgnunettransportapplication_src = ['transport_api2_application.c'] +libgnunettransportcore_src = ['transport_api2_core.c'] +libgnunettransportcommunicator_src = ['transport_api2_communication.c'] +libgnunettransportmonitor_src = ['transport_api2_monitor.c'] + +gnunetservicetransport_src = ['gnunet-service-transport.c'] +gnunetcommunicatortcp_src = ['gnunet-communicator-tcp.c'] +gnunetcommunicatorudp_src = ['gnunet-communicator-udp.c'] +gnunetcommunicatorunix_src = ['gnunet-communicator-unix.c'] + +configure_file(input : 'transport.conf.in', + output : 'transport.conf', + configuration : cdata, + install: true, + install_dir: pkgcfgdir) + +configure_file(input : 'gnunet-transport-certificate-creation.in', + output : 'gnunet-transport-certificate-creation', + configuration : cdata, + install: true, + install_dir: get_option('bindir')) + +if get_option('monolith') + foreach p : libgnunettransportapplication_src + libgnunettransportcore_src + libgnunettransportcommunicator_src + libgnunettransportmonitor_src + gnunetservicetransport_src + gnunet_src += 'transport/' + p + endforeach + subdir_done() +endif + +libgnunettransportapplication = library('gnunettransportapplication', + libgnunettransportapplication_src, + soversion: '0', + version: '0.0.0', + dependencies: libgnunetutil_dep, + include_directories: [incdir, configuration_inc], + install: true, + install_dir: get_option('libdir')) +pkg.generate(libgnunettransportapplication, url: 'https://www.gnunet.org', + description : 'Provides application APIs for accessing the transport service') +libgnunettransportapplication_dep = declare_dependency(link_with : libgnunettransportapplication) + +libgnunettransportcore = library('gnunettransportcore', + libgnunettransportcore_src, + soversion: '0', + version: '0.0.0', + dependencies: libgnunetutil_dep, + include_directories: [incdir, configuration_inc], + install: true, + install_dir: get_option('libdir')) +pkg.generate(libgnunettransportcore, url: 'https://www.gnunet.org', + description : 'Provides core API for accessing the transport service') +libgnunettransportcore_dep = declare_dependency(link_with : libgnunettransportcore) + +libgnunettransportcommunicator = library('gnunettransportcommunicator', + libgnunettransportcommunicator_src, + soversion: '0', + version: '0.0.0', + dependencies: libgnunetutil_dep, + include_directories: [incdir, configuration_inc], + install: true, + install_dir: get_option('libdir')) +pkg.generate(libgnunettransportcommunicator, url: 'https://www.gnunet.org', + description : 'Provides communicator API for accessing the transport service') +libgnunettransportcommunicator_dep = declare_dependency(link_with : libgnunettransportcommunicator) + +libgnunettransportmonitor = library('gnunettransportmonitor', + libgnunettransportmonitor_src, + soversion: '0', + version: '0.0.0', + dependencies: libgnunetutil_dep, + include_directories: [incdir, configuration_inc], + install: true, + install_dir: get_option('libdir')) +pkg.generate(libgnunettransportmonitor, url: 'https://www.gnunet.org', + description : 'Provides monitor API for accessing the transport service') +libgnunettransportmonitor_dep = declare_dependency(link_with : libgnunettransportmonitor) + +libgnunettransporttesting2 = library('gnunettransporttesting2', + [ + 'transport_api_traits.c', + 'transport_api_cmd_connecting_peers.c', + 'transport_api_cmd_backchannel_check.c', + 'transport_api_cmd_start_peer.c', + 'transport_api_cmd_stop_peer.c', + 'transport_api_cmd_send_simple.c', + 'transport_api_cmd_send_simple_performance.c', + 'transport-testing2.c', + 'transport-testing-filenames2.c', + 'transport-testing-loggers2.c', + 'transport-testing-main2.c', + 'transport-testing-send2.c', + 'transport-testing-communicator.c', + ], + soversion: '0', + version: '0.0.0', + dependencies: [libgnunetutil_dep, + libgnunettransportcore_dep, + libgnunettransportapplication_dep, + libgnunetpeerstore_dep, + libgnunettesting_dep, + libgnunethello_dep], + include_directories: [incdir, configuration_inc], + install: true, + install_dir: get_option('libdir')) +libgnunettransporttesting2_dep = declare_dependency(link_with : libgnunettransporttesting2) + +executable ('gnunet-service-transport', + gnunetservicetransport_src, + dependencies: [libgnunettransportcommunicator_dep, + libgnunetpeerstore_dep, + libgnunetstatistics_dep, + libgnunethello_dep, + libgnunetnat_dep, + gcrypt_dep, + libgnunetutil_dep], + include_directories: [incdir, configuration_inc], + install: true, + install_dir: get_option('libdir') / 'gnunet' / 'libexec') + +executable ('gnunet-communicator-unix', + gnunetcommunicatorunix_src, + dependencies: [libgnunettransportcommunicator_dep, + libgnunetpeerstore_dep, + libgnunetstatistics_dep, + libgnunetnat_dep, + gcrypt_dep, + libgnunetutil_dep], + include_directories: [incdir, configuration_inc], + install: true, + install_dir: get_option('libdir') / 'gnunet' / 'libexec') +executable ('gnunet-communicator-udp', + gnunetcommunicatorudp_src, + dependencies: [libgnunettransportcommunicator_dep, + libgnunettransportapplication_dep, + libgnunetpeerstore_dep, + libgnunetstatistics_dep, + libgnunetnat_dep, + gcrypt_dep, + libgnunetutil_dep], + include_directories: [incdir, configuration_inc], + install: true, + install_dir: get_option('libdir') / 'gnunet' / 'libexec') +executable ('gnunet-communicator-tcp', + gnunetcommunicatortcp_src, + dependencies: [libgnunettransportcommunicator_dep, + libgnunetpeerstore_dep, + libgnunetstatistics_dep, + libgnunetnat_dep, + gcrypt_dep, + libgnunetutil_dep], + include_directories: [incdir, configuration_inc], + install: true, + install_dir: get_option('libdir') / 'gnunet' / 'libexec') + +libgnunettesttransport_cmd_simplesend = library('gnunet_test_transport_plugin_cmd_simple_send', + ['test_transport_plugin_cmd_simple_send.c'], + dependencies: [ + libgnunetutil_dep, + libgnunettransportapplication_dep, + libgnunettransporttesting2_dep, + libgnunettransportcore_dep, + libgnunettesting_dep, + libgnunetpeerstore_dep, + libgnunetstatistics_dep, + libgnunethello_dep, + libgnunetarm_dep, + libgnunetutil_dep + ], + include_directories: [incdir, configuration_inc], + install: true, + install_dir: get_option('libdir')/'gnunet') + +libgnunettesttransport_cmd_simplesendbc = library('gnunet_test_transport_plugin_cmd_simple_send_broadcast', + ['test_transport_plugin_cmd_simple_send_broadcast.c'], + dependencies: [ + libgnunetutil_dep, + libgnunettransportapplication_dep, + libgnunettransporttesting2_dep, + libgnunettransportcore_dep, + libgnunettesting_dep, + libgnunetpeerstore_dep, + libgnunetstatistics_dep, + libgnunethello_dep, + libgnunetarm_dep, + libgnunetutil_dep + ], + include_directories: [incdir, configuration_inc], + install: true, + install_dir: get_option('libdir')/'gnunet') + +libgnunettesttransport_cmd_simplesenddv = library('gnunet_test_transport_plugin_cmd_simple_send_dv', + ['test_transport_plugin_cmd_simple_send_dv.c'], + dependencies: [ + libgnunetutil_dep, + libgnunettransportapplication_dep, + libgnunettransporttesting2_dep, + libgnunettransportcore_dep, + libgnunettesting_dep, + libgnunetpeerstore_dep, + libgnunetstatistics_dep, + libgnunethello_dep, + libgnunetarm_dep, + libgnunetutil_dep + ], + include_directories: [incdir, configuration_inc], + install: true, + install_dir: get_option('libdir')/'gnunet') + +libgnunettesttransport_cmd_simplesendperf = library('gnunet_test_transport_plugin_cmd_simple_send_performance', + ['test_transport_plugin_cmd_simple_send_performance.c'], + dependencies: [ + libgnunetutil_dep, + libgnunettransportapplication_dep, + libgnunettransporttesting2_dep, + libgnunettransportcore_dep, + libgnunettesting_dep, + libgnunetpeerstore_dep, + libgnunetstatistics_dep, + libgnunethello_dep, + libgnunetarm_dep, + libgnunetutil_dep + ], + include_directories: [incdir, configuration_inc], + install: true, + install_dir: get_option('libdir')/'gnunet') + +libgnunettesttransport_cmd_udpback = library('gnunet_test_transport_plugin_cmd_udp_backchannel', + ['test_transport_plugin_cmd_udp_backchannel.c'], + dependencies: [ + libgnunetutil_dep, + libgnunettransporttesting2_dep, + libgnunettransportapplication_dep, + libgnunettransportcore_dep, + libgnunettesting_dep, + libgnunetpeerstore_dep, + libgnunetstatistics_dep, + libgnunethello_dep, + libgnunetarm_dep, + libgnunetutil_dep + ], + include_directories: [incdir, configuration_inc], + install: true, + install_dir: get_option('libdir')/'gnunet') + +libgnunettesttransport_cmd_natupnp = library('gnunet_test_transport_plugin_cmd_nat_upnp', + ['test_transport_plugin_cmd_nat_upnp.c'], + dependencies: [ + libgnunetutil_dep, + libgnunettransportapplication_dep, + libgnunettransporttesting2_dep, + libgnunettransportcore_dep, + libgnunettesting_dep, + libgnunetpeerstore_dep, + libgnunetstatistics_dep, + libgnunethello_dep, + libgnunetarm_dep, + libgnunetutil_dep + ], + include_directories: [incdir, configuration_inc], + install: true, + install_dir: get_option('libdir')/'gnunet') diff --git a/src/service/transport/template_cfg_peer1.conf b/src/service/transport/template_cfg_peer1.conf new file mode 100644 index 000000000..5ba198450 --- /dev/null +++ b/src/service/transport/template_cfg_peer1.conf @@ -0,0 +1,50 @@ +@INLINE@ test_transport_defaults.conf +[PATHS] +GNUNET_TEST_HOME = $GNUNET_TMP/test-transport/api-tcp-p1/ +GNUNET_RUNTIME_DIR = $GNUNET_TEST_HOME/runtime/ +GNUNET_USER_RUNTIME_DIR = $GNUNET_TEST_HOME/runtime/ + +[nat] +RETURN_LOCAL_ADDRESSES = YES +DISABLEV6 = NO + +[transport-tcp] +PORT = 12000 +TIMEOUT = 5 s + +[transport-udp] +BROADCAST = NO + +[transport-unix] +PORT = 12007 + +[arm] +PORT = 12005 +UNIXPATH = $GNUNET_RUNTIME_DIR/gnunet-p1-service-arm.sock + +[statistics] +PORT = 12004 +UNIXPATH = $GNUNET_RUNTIME_DIR/gnunet-p1-service-statistics.sock + +[resolver] +PORT = 12003 +UNIXPATH = $GNUNET_RUNTIME_DIR/gnunet-p1-service-resolver.sock + +[peerinfo] +PORT = 12002 +UNIXPATH = $GNUNET_RUNTIME_DIR/gnunet-p1-service-peerinfo.sock +USE_INCLUDED_HELLOS = NO + +[transport] +#PREFIX = valgrind --leak-check=full +PORT = 12001 +UNIXPATH = $GNUNET_RUNTIME_DIR/gnunet-p1-service-transport.sock + +[ats] +WAN_QUOTA_IN = unlimited +WAN_QUOTA_OUT = unlimited +PORT = 12006 +UNIXPATH = $GNUNET_RUNTIME_DIR/gnunet-p1-service-ats.sock + +[hostlist] +SERVERS = dummy diff --git a/src/service/transport/template_cfg_peer2.conf b/src/service/transport/template_cfg_peer2.conf new file mode 100644 index 000000000..6ac610fec --- /dev/null +++ b/src/service/transport/template_cfg_peer2.conf @@ -0,0 +1,58 @@ +@INLINE@ test_transport_defaults.conf +[PATHS] +GNUNET_TEST_HOME = $GNUNET_TMP/test-transport/api-tcp-p2/ +GNUNET_RUNTIME_DIR = $GNUNET_TEST_HOME/runtime/ +GNUNET_USER_RUNTIME_DIR = $GNUNET_TEST_HOME/runtime/ + +[nat] +RETURN_LOCAL_ADDRESSES = YES +DISABLEV6 = NO + +[transport-tcp] +PORT = 12100 +TIMEOUT = 5 s + +[transport-udp] +BROADCAST = NO + +[transport-unix] +PORT = 12017 + +[transport-http_server] +PORT = 12018 +USE_IPv6 = YES + +[transport-https_server] +PORT = 12019 +KEY_FILE = $GNUNET_TEST_HOME/https_key_p1.key +CERT_FILE = $GNUNET_TEST_HOME/https_cert_p1.crt +USE_IPv6 = YES + +[arm] +PORT = 12014 +UNIXPATH = $GNUNET_RUNTIME_DIR/gnunet-p2-service-arm.sock + +[statistics] +PORT = 12013 +UNIXPATH = $GNUNET_RUNTIME_DIR/gnunet-p2-service-statistics.sock + +[resolver] +PORT = 12012 +UNIXPATH = $GNUNET_RUNTIME_DIR/gnunet-p2-service-resolver.sock + +[peerinfo] +PORT = 12011 +UNIXPATH = $GNUNET_RUNTIME_DIR/gnunet-p2-service-peerinfo.sock +USE_INCLUDED_HELLOS = NO + +[transport] +#PREFIX = valgrind --leak-check=full +PORT = 12010 +UNIXPATH = $GNUNET_RUNTIME_DIR/gnunet-p2-service-transport.sock + +[ats] +PORT = 12016 +UNIXPATH = $GNUNET_RUNTIME_DIR/gnunet-p2-service-ats.sock + +[hostlist] +SERVERS = dummy diff --git a/src/service/transport/template_tng_cfg_peer1.conf b/src/service/transport/template_tng_cfg_peer1.conf new file mode 100644 index 000000000..b6198d72c --- /dev/null +++ b/src/service/transport/template_tng_cfg_peer1.conf @@ -0,0 +1,34 @@ +@INLINE@ test_tng_defaults.conf +[PATHS] +GNUNET_TEST_HOME = $GNUNET_TMP/test-transport/api-tcp-p1/ +GNUNET_RUNTIME_DIR = $GNUNET_TEST_HOME/runtime/ +GNUNET_USER_RUNTIME_DIR = $GNUNET_TEST_HOME/runtime/ + +[nat] +RETURN_LOCAL_ADDRESSES = YES +DISABLEV6 = NO + +[arm] +PORT = 12005 +UNIXPATH = $GNUNET_RUNTIME_DIR/gnunet-p1-service-arm.sock + +[statistics] +PORT = 12004 +UNIXPATH = $GNUNET_RUNTIME_DIR/gnunet-p1-service-statistics.sock + +[resolver] +PORT = 12003 +UNIXPATH = $GNUNET_RUNTIME_DIR/gnunet-p1-service-resolver.sock + +[peerinfo] +PORT = 12002 +UNIXPATH = $GNUNET_RUNTIME_DIR/gnunet-p1-service-peerinfo.sock +USE_INCLUDED_HELLOS = NO + +[transport] +#PREFIX = valgrind --leak-check=full +PORT = 12001 +UNIXPATH = $GNUNET_RUNTIME_DIR/gnunet-p1-service-transport.sock + +[hostlist] +SERVERS = dummy diff --git a/src/service/transport/test_communicator_basic.c b/src/service/transport/test_communicator_basic.c new file mode 100644 index 000000000..fdbad0957 --- /dev/null +++ b/src/service/transport/test_communicator_basic.c @@ -0,0 +1,1224 @@ +/* + This file is part of GNUnet. + Copyright (C) 2019 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 . + + SPDX-License-Identifier: AGPL3.0-or-later +*/ + +/** +* @file transport/test_communicator_basic.c +* @brief test the communicators +* @author Julius Bünger +* @author Martin Schanzenbach +*/ +#include "platform.h" +#include "gnunet_util_lib.h" +#include "transport-testing-communicator.h" +#include "gnunet_ats_transport_service.h" +#include "gnunet_signatures.h" +#include "gnunet_testing_lib.h" +#include "transport.h" +#include "gnunet_statistics_service.h" + +#include + + +#define LOG(kind, ...) GNUNET_log_from (kind, \ + "test_transport_communicator", \ + __VA_ARGS__) + +#define NUM_PEERS 2 + +static struct GNUNET_SCHEDULER_Task *to_task[NUM_PEERS]; + +static int queue_est = GNUNET_NO; + +static struct GNUNET_PeerIdentity peer_id[NUM_PEERS]; + +static char *communicator_binary; + +static struct +GNUNET_TRANSPORT_TESTING_TransportCommunicatorHandle *tc_hs[NUM_PEERS]; + +static struct GNUNET_CONFIGURATION_Handle *cfg_peers[NUM_PEERS]; + +static struct GNUNET_STATISTICS_Handle *stats[NUM_PEERS]; + +static char *cfg_peers_name[NUM_PEERS]; + +static int finished[NUM_PEERS]; + +static int ret; + +static int bidirect = GNUNET_NO; + +static size_t long_message_size; + +static struct GNUNET_TIME_Absolute start_short[NUM_PEERS]; + +static struct GNUNET_TIME_Absolute start_long[NUM_PEERS]; + +static struct GNUNET_TIME_Absolute timeout[NUM_PEERS]; + +// static struct GNUNET_TRANSPORT_TESTING_TransportCommunicatorHandle *my_tc; + +static char *communicator_name; + +static char *test_name; + +static struct GNUNET_STATISTICS_GetHandle *box_stats[NUM_PEERS]; + +static struct GNUNET_STATISTICS_GetHandle *rekey_stats[NUM_PEERS]; + +#define TEST_SECTION "test-setup" + +#define SHORT_MESSAGE_SIZE 128 + +#define LONG_MESSAGE_SIZE 32000 /* FIXME */ + +#define ALLOWED_PACKET_LOSS 91 + +#define BURST_PACKETS 5000 + +#define TOTAL_ITERATIONS 1 + +#define PEER_A 0 + +#define PEER_B 1 + +static unsigned int iterations_left[NUM_PEERS]; + +#define TIMEOUT_MULTIPLIER 1 + +#define DELAY \ + GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_MICROSECONDS,200) + +#define SHORT_BURST_WINDOW \ + GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS,2) + +#define LONG_BURST_WINDOW \ + GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS,2) + +enum TestPhase +{ + TP_INIT, + TP_BURST_SHORT, + TP_BURST_LONG, + TP_SIZE_CHECK +}; + +static unsigned int phase_short[NUM_PEERS]; + +static unsigned int phase_long[NUM_PEERS]; + +static unsigned int phase_size[NUM_PEERS]; + +static long long unsigned int allowed_packet_loss_short; + +static long long unsigned int allowed_packet_loss_long; + +static long long unsigned int burst_packets_short; + +static long long unsigned int burst_packets_long; + +static long long unsigned int delay_long_value; + +static long long unsigned int delay_short_value; + +static struct GNUNET_TIME_Relative delay_short; + +static struct GNUNET_TIME_Relative delay_long; + +static size_t num_sent_short[NUM_PEERS]; + +static size_t num_sent_long[NUM_PEERS]; + +static size_t num_sent_size[NUM_PEERS]; + +static uint32_t ack[NUM_PEERS]; + +static enum TestPhase phase[NUM_PEERS]; + +static size_t num_received_short[NUM_PEERS]; + +static size_t num_received_long[NUM_PEERS]; + +static size_t num_received_size[NUM_PEERS]; + +static uint64_t avg_latency[NUM_PEERS]; + +static void +communicator_available_cb ( + void *cls, + struct + GNUNET_TRANSPORT_TESTING_TransportCommunicatorHandle *tc_h, + enum GNUNET_TRANSPORT_CommunicatorCharacteristics cc, + char *address_prefix) +{ + LOG (GNUNET_ERROR_TYPE_INFO, + "Communicator available. (cc: %u, prefix: %s)\n", + cc, + address_prefix); +} + + +static void +open_queue (void *cls) +{ + const char *address = cls; + + if (NULL != tc_hs[PEER_A]->c_mq) + { + queue_est = GNUNET_YES; + GNUNET_TRANSPORT_TESTING_transport_communicator_open_queue (tc_hs[PEER_A], + &peer_id[PEER_B], + address); + } + else + { + GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_UNIT_SECONDS, + &open_queue, + (void *) address); + } +} + + +static void +add_address_cb ( + void *cls, + struct GNUNET_TRANSPORT_TESTING_TransportCommunicatorHandle *tc_h, + const char *address, + struct GNUNET_TIME_Relative expiration, + uint32_t aid, + enum GNUNET_NetworkType nt) +{ + LOG (GNUNET_ERROR_TYPE_DEBUG, + "New address. (addr: %s, expir: %s, ID: %" PRIu32 ", nt: %u\n", + address, + GNUNET_STRINGS_relative_time_to_string (expiration, + GNUNET_NO), + aid, + (int) nt); + // addresses[1] = GNUNET_strdup (address); + if ((0 == strcmp ((char*) cls, cfg_peers_name[PEER_B])) && + (GNUNET_NO == queue_est)) + { + open_queue ((void *) address); + } +} + + +/** + * @brief Callback that informs whether the requested queue will be + * established + * + * Implements #GNUNET_TRANSPORT_TESTING_QueueCreateReplyCallback. + * + * @param cls Closure - unused + * @param tc_h Communicator handle - unused + * @param will_try #GNUNET_YES if queue will be established + * #GNUNET_NO if queue will not be established (bogous address) + */ +static void +queue_create_reply_cb ( + void *cls, + struct GNUNET_TRANSPORT_TESTING_TransportCommunicatorHandle *tc_h, + int will_try) +{ + (void) cls; + (void) tc_h; + if (GNUNET_YES == will_try) + LOG (GNUNET_ERROR_TYPE_DEBUG, + "Queue will be established!\n"); + else + LOG (GNUNET_ERROR_TYPE_WARNING, + "Queue won't be established (bougus address?)!\n"); +} + + +static struct GNUNET_TRANSPORT_TESTING_TransportCommunicatorHandle * +handle_backchannel_cb (void *cls, + struct GNUNET_MessageHeader *msg, + struct GNUNET_PeerIdentity *pid) +{ + struct GNUNET_TRANSPORT_TESTING_TransportCommunicatorHandle *tc_h = cls; + + (void) tc_h; + (void) msg; + LOG (GNUNET_ERROR_TYPE_DEBUG, "Handling BC message...\n"); + if (0 == memcmp (&peer_id[PEER_A], pid, sizeof (*pid))) + return tc_hs[PEER_A]; + else + return tc_hs[PEER_B]; +} + + +static char* +make_payload (size_t payload_size) +{ + struct GNUNET_TIME_Absolute ts; + struct GNUNET_TIME_AbsoluteNBO ts_n; + char *payload = GNUNET_malloc (payload_size); + + LOG (GNUNET_ERROR_TYPE_DEBUG, + "Making payload of size %lu\n", payload_size); + GNUNET_assert (payload_size >= 8); // So that out timestamp fits + ts = GNUNET_TIME_absolute_get (); + ts_n = GNUNET_TIME_absolute_hton (ts); + memset (payload, 'a', payload_size); + memcpy (payload, &ts_n, sizeof (struct GNUNET_TIME_AbsoluteNBO)); + return payload; +} + + +static struct GNUNET_TRANSPORT_TESTING_TransportCommunicatorHandle * +get_tc_h (unsigned int peer_nr) +{ + LOG (GNUNET_ERROR_TYPE_DEBUG, + "Got peer %u\n", + peer_nr); + + LOG (GNUNET_ERROR_TYPE_DEBUG, + "Handle %p peer 0\n", + tc_hs[0]); + + LOG (GNUNET_ERROR_TYPE_DEBUG, + "Handle %p peer 1\n", + tc_hs[1]); + + LOG (GNUNET_ERROR_TYPE_DEBUG, + "Handle %p get\n", + tc_hs[peer_nr]); + + return tc_hs[peer_nr]; +} + + +static unsigned int +get_peer_nr_from_tc (struct + GNUNET_TRANSPORT_TESTING_TransportCommunicatorHandle *tc_h) +{ + if (tc_h == get_tc_h (0)) + return PEER_A; + else + return PEER_B; +} + + +static unsigned int +get_peer_nr (void *cls, unsigned int get_the_other_one) +{ + if (0 == strcmp ((char*) cls, cfg_peers_name[0])) + return get_the_other_one ? PEER_B : PEER_A; + else + return get_the_other_one ? PEER_A : PEER_B; +} + + +static void +process_statistics_box_done (void *cls, int success) +{ + struct GNUNET_TRANSPORT_TESTING_TransportCommunicatorHandle *tc_h = cls; + unsigned int peer_nr; + + peer_nr = get_peer_nr_from_tc (tc_h); + + if (NULL != box_stats[peer_nr]) + box_stats[peer_nr] = NULL; + if (NULL == rekey_stats[peer_nr]) + { + LOG (GNUNET_ERROR_TYPE_DEBUG, + "Finished\n"); + GNUNET_SCHEDULER_shutdown (); + } +} + + +static void +process_statistics_rekey_done (void *cls, int success) +{ + struct GNUNET_TRANSPORT_TESTING_TransportCommunicatorHandle *tc_h = cls; + unsigned int peer_nr; + + peer_nr = get_peer_nr_from_tc (tc_h); + + if (NULL != rekey_stats[peer_nr]) + rekey_stats[peer_nr] = NULL; + if (NULL == box_stats[peer_nr]) + { + LOG (GNUNET_ERROR_TYPE_DEBUG, + "Finished\n"); + GNUNET_SCHEDULER_shutdown (); + } +} + + +static int +process_statistics (void *cls, + const char *subsystem, + const char *name, + uint64_t value, + int is_persistent) +{ + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Statistic: Name %s and value %lu\n", + name, + value); + if ((0 == strcmp ("rekey", test_name)) && (0 == strcmp ( + "# rekeying successful", + name)) && (0 == value)) + { + ret = 2; + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "No successful rekeying!\n"); + GNUNET_SCHEDULER_shutdown (); + } + if ((0 == strcmp ("backchannel", test_name)) && + (0 == strcmp ( + "# messages decrypted with BOX", + name)) + && (9000 > value)) + { + ret = 2; + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "Not enough BOX messages! (want: %u, have %llu)\n", + 9000, value); + GNUNET_SCHEDULER_shutdown (); + } + if ((0 == strcmp ("rekey", test_name)) && + (0 == strcmp ( + "# messages decrypted with BOX", + name)) + && (6000 > value)) + { + ret = 2; + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "Not enough BOX messages! (want: %u, have %llu)\n", + 6000, value); + GNUNET_SCHEDULER_shutdown (); + } + return GNUNET_OK; +} + + +static void +short_test (void *cls); + +static void +short_test_cb (void *cls) +{ + struct GNUNET_TRANSPORT_TESTING_TransportCommunicatorHandle *tc_h = cls; + unsigned int peer_nr; + char *payload; + + peer_nr = get_peer_nr_from_tc (tc_h); + + LOG (GNUNET_ERROR_TYPE_DEBUG, + "short_test_cb %u/%u for peer %u and handle %p\n", + (unsigned int) num_sent_short[peer_nr], + (unsigned int) num_received_short[peer_nr], + peer_nr, + tc_h); + payload = make_payload (SHORT_MESSAGE_SIZE); + num_sent_short[peer_nr]++; + if (burst_packets_short == num_sent_short[peer_nr]) + tc_h->cont = NULL; + else + tc_h->cont = short_test; + tc_h->cont_cls = cls; + GNUNET_TRANSPORT_TESTING_transport_communicator_send (tc_h, + NULL, + cls, + payload, + SHORT_MESSAGE_SIZE); + GNUNET_free (payload); + timeout[peer_nr] = GNUNET_TIME_relative_to_absolute ( + GNUNET_TIME_relative_multiply ( + GNUNET_TIME_UNIT_SECONDS, + TIMEOUT_MULTIPLIER)); +} + + +static void +short_test (void *cls) +{ + GNUNET_SCHEDULER_add_delayed (delay_short, + &short_test_cb, + cls); +} + + +static void +size_test (void *cls) +{ + unsigned int peer_nr; + char *payload; + size_t max_size = 64000; + struct GNUNET_TRANSPORT_TESTING_TransportCommunicatorHandle *tc_h = cls; + + peer_nr = get_peer_nr_from_tc (tc_h); + LOG (GNUNET_ERROR_TYPE_DEBUG, + "size_test_cb %u\n", + (unsigned int) num_sent_size[peer_nr]); + GNUNET_assert (TP_SIZE_CHECK == phase[peer_nr]); + if (LONG_MESSAGE_SIZE != long_message_size) + max_size = long_message_size; + if (ack[peer_nr] + 10 > max_size) + return; /* Leave some room for our protocol, so not 2^16 exactly */ + ack[peer_nr] += 10; + payload = make_payload (ack[peer_nr]); + num_sent_size[peer_nr]++; + if (ack[peer_nr] >= max_size) + tc_h->cont = NULL; + else + tc_h->cont = size_test; + tc_h->cont_cls = cls; + GNUNET_TRANSPORT_TESTING_transport_communicator_send (tc_h, + NULL, + cls, + payload, + ack[peer_nr]); + GNUNET_free (payload); + timeout[peer_nr] = GNUNET_TIME_relative_to_absolute ( + GNUNET_TIME_relative_multiply ( + GNUNET_TIME_UNIT_SECONDS, + TIMEOUT_MULTIPLIER)); +} + + +static void +long_test (void *cls); + +static void +long_test_cb (void *cls) +{ + unsigned int peer_nr; + char *payload; + struct GNUNET_TRANSPORT_TESTING_TransportCommunicatorHandle *tc_h = cls; + + peer_nr = get_peer_nr_from_tc (tc_h); + + LOG (GNUNET_ERROR_TYPE_DEBUG, + "long_test_cb %u/%u\n", + (unsigned int) num_sent_long[peer_nr], + (unsigned int) num_received_long[peer_nr]); + payload = make_payload (long_message_size); + num_sent_long[peer_nr]++; + if (burst_packets_long == num_sent_long[peer_nr]) + tc_h->cont = NULL; + else + tc_h->cont = long_test; + tc_h->cont_cls = cls; + GNUNET_TRANSPORT_TESTING_transport_communicator_send (tc_h, + NULL, + cls, + payload, + long_message_size); + GNUNET_free (payload); + timeout[peer_nr] = GNUNET_TIME_relative_to_absolute ( + GNUNET_TIME_relative_multiply ( + GNUNET_TIME_UNIT_SECONDS, + TIMEOUT_MULTIPLIER)); +} + + +static void +long_test (void *cls) +{ + GNUNET_SCHEDULER_add_delayed (delay_long, + &long_test_cb, + cls); +} + + +static void +choose_phase (struct GNUNET_TRANSPORT_TESTING_TransportCommunicatorHandle *tc_h) +{ + unsigned int peer_nr; + + peer_nr = get_peer_nr_from_tc (tc_h); + + if (GNUNET_YES == phase_short[peer_nr]) + { + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Choose phase short with peer %u and Handle %p\n", + peer_nr, + tc_h); + phase[peer_nr] = TP_BURST_SHORT; + start_short[peer_nr] = GNUNET_TIME_absolute_get (); + short_test (tc_h); + } + else if (GNUNET_YES == phase_long[peer_nr]) + { + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Choose phase long with peer %u\n", + peer_nr); + phase[peer_nr] = TP_BURST_LONG; + start_long[peer_nr] = GNUNET_TIME_absolute_get (); + long_test (tc_h); + } + else if (GNUNET_YES == phase_size[peer_nr]) + { + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Choose phase size\n"); + phase[peer_nr] = TP_SIZE_CHECK; + size_test (tc_h); + } + else + { + if ((0 == strcmp ("udp", communicator_name)) && ((0 == strcmp ("rekey", + test_name)) + || (0 == strcmp ( + "backchannel", + test_name))) ) + { + if (NULL != box_stats[peer_nr]) + GNUNET_STATISTICS_get_cancel (box_stats[peer_nr]); + box_stats[peer_nr] = GNUNET_STATISTICS_get (stats[1], + "C-UDP", + "# messages decrypted with BOX", + process_statistics_box_done, + &process_statistics, + tc_h); + if (NULL != rekey_stats[peer_nr]) + GNUNET_STATISTICS_get_cancel (rekey_stats[peer_nr]); + rekey_stats[peer_nr] = GNUNET_STATISTICS_get (stats[0], + "C-UDP", + "# rekeying successful", + process_statistics_rekey_done, + &process_statistics, + tc_h); + } + else + { + if ((GNUNET_NO == bidirect) || (((PEER_A == peer_nr) && + finished[PEER_B]) || ((PEER_B == + peer_nr) && + finished + [PEER_A]))) + { + LOG (GNUNET_ERROR_TYPE_DEBUG, + "Finished\n"); + GNUNET_SCHEDULER_shutdown (); + } + finished[peer_nr] = GNUNET_YES; + } + } +} + + +static void +finish_phase_long (unsigned int peer_nr) +{ + static struct GNUNET_TIME_Relative duration; + + duration = GNUNET_TIME_absolute_get_duration (start_long[peer_nr]); + LOG (GNUNET_ERROR_TYPE_MESSAGE, + "Long size packet test for peer %u done.\n", + peer_nr); + char *goodput = GNUNET_STRINGS_byte_size_fancy ( + (long_message_size * num_received_long[peer_nr] * 1000 * 1000) + / duration. + rel_value_us); + + LOG (GNUNET_ERROR_TYPE_MESSAGE, + "%lu/%lu packets in %llu us (%s/s) -- avg latency: %llu us\n", + (unsigned long) num_received_long[peer_nr], + (unsigned long) num_sent_long[peer_nr], + (unsigned long long) duration.rel_value_us, + goodput, + (unsigned long long) avg_latency[peer_nr]); + GNUNET_free (goodput); + ack[peer_nr] = 0; + // phase = TP_SIZE_CHECK; + // num_received = 0; + // num_sent_long = 0; + avg_latency[peer_nr] = 0; + // size_test (NULL); + phase_long[peer_nr] = GNUNET_NO; + choose_phase (get_tc_h (peer_nr)); +} + + +static void +finish_phase_short (unsigned int peer_nr) +{ + static struct GNUNET_TIME_Relative duration; + + duration = GNUNET_TIME_absolute_get_duration (start_short[peer_nr]); + LOG (GNUNET_ERROR_TYPE_MESSAGE, + "Short size packet test for peer %u done.\n", + peer_nr); + char *goodput = GNUNET_STRINGS_byte_size_fancy ( + (SHORT_MESSAGE_SIZE * num_received_short[peer_nr] * 1000 * 1000) + / duration.rel_value_us); + LOG (GNUNET_ERROR_TYPE_MESSAGE, + "%lu/%lu packets in %llu us (%s/s) -- avg latency: %llu us\n", + (unsigned long) num_received_short[peer_nr], + (unsigned long) num_sent_short[peer_nr], + (unsigned long long) duration.rel_value_us, + goodput, + (unsigned long long) avg_latency[peer_nr]); + GNUNET_free (goodput); + // start_long = GNUNET_TIME_absolute_get (); + // phase = TP_BURST_LONG; + // num_sent_short = 0; + avg_latency[peer_nr] = 0; + // num_received = 0; + phase_short[peer_nr] = GNUNET_NO; + choose_phase (get_tc_h (peer_nr)); + // long_test (NULL); +} + + +static void +latency_timeout (void *cls) +{ + + struct GNUNET_TRANSPORT_TESTING_TransportCommunicatorHandle *tc_h = cls; + unsigned int peer_nr; + size_t num_sent = 0; + size_t num_received = 0; + + peer_nr = get_peer_nr_from_tc (tc_h); + to_task[peer_nr] = NULL; + + switch (phase[peer_nr]) + { + case TP_INIT: + GNUNET_assert (0); + break; + case TP_BURST_SHORT: + num_sent = num_sent_short[peer_nr]; + num_received = num_received_short[peer_nr]; + if ((num_sent_short[peer_nr] == burst_packets_short) && + (num_received_short[peer_nr] > + burst_packets_short + / 100 + * + allowed_packet_loss_short) ) + { + finish_phase_short (peer_nr); + to_task[peer_nr] = GNUNET_SCHEDULER_add_at (timeout[peer_nr], + &latency_timeout, + cls); + return; + } + break; + case TP_BURST_LONG: + num_sent = num_sent_long[peer_nr]; + num_received = num_received_long[peer_nr]; + if ((num_sent_long[peer_nr] == burst_packets_long) && + (num_received_long[peer_nr] > + burst_packets_long + / 100 + * + allowed_packet_loss_long) ) + { + finish_phase_long (peer_nr); + to_task[peer_nr] = GNUNET_SCHEDULER_add_at (timeout[peer_nr], + &latency_timeout, + cls); + return; + } + break; + case TP_SIZE_CHECK: + num_sent = num_sent_size[peer_nr]; + num_received = num_received_size[peer_nr]; + break; + } + if (GNUNET_TIME_absolute_get_remaining (timeout[peer_nr]).rel_value_us > 0) + { + to_task[peer_nr] = GNUNET_SCHEDULER_add_at (timeout[peer_nr], + &latency_timeout, + cls); + return; + } + LOG (GNUNET_ERROR_TYPE_ERROR, + "Latency too high. Test failed. (Phase: %d. Sent: %lu, Received: %lu)\n", + phase[peer_nr], num_sent, num_received); + ret = 2; + GNUNET_SCHEDULER_shutdown (); +} + + +/** + * @brief Handle opening of queue + * + * Issues sending of test data + * + * Implements #GNUNET_TRANSPORT_TESTING_AddQueueCallback + * + * @param cls Closure + * @param tc_h Communicator handle + * @param tc_queue Handle to newly opened queue + */ +static void +add_queue_cb (void *cls, + struct GNUNET_TRANSPORT_TESTING_TransportCommunicatorHandle *tc_h, + struct GNUNET_TRANSPORT_TESTING_TransportCommunicatorQueue * + tc_queue, + size_t mtu) +{ + + unsigned int peer_nr; + + peer_nr = get_peer_nr (cls, GNUNET_NO); + LOG (GNUNET_ERROR_TYPE_DEBUG, + "Handle %p add %u %u\n", + tc_h, + peer_nr, + get_peer_nr_from_tc (tc_h)); + if ((GNUNET_NO == bidirect) && (0 != strcmp ((char*) cls, cfg_peers_name[0]))) + { + LOG (GNUNET_ERROR_TYPE_DEBUG, + "Queue available at receiving peer\n"); + return; // TODO? + } + else if (TP_INIT != phase[peer_nr]) + return; + LOG (GNUNET_ERROR_TYPE_DEBUG, + "Queue established, starting test...\n"); + if (UINT16_MAX != mtu) /* Message header overhead */ + long_message_size = mtu - sizeof(struct GNUNET_TRANSPORT_SendMessageTo) + - sizeof(struct GNUNET_MessageHeader); + else + long_message_size = LONG_MESSAGE_SIZE; + timeout[peer_nr] = GNUNET_TIME_relative_to_absolute ( + GNUNET_TIME_relative_multiply ( + GNUNET_TIME_UNIT_SECONDS, + TIMEOUT_MULTIPLIER)); + GNUNET_assert (NULL == to_task[peer_nr]); + to_task[peer_nr] = GNUNET_SCHEDULER_add_delayed ( + GNUNET_TIME_relative_multiply ( + GNUNET_TIME_UNIT_SECONDS, + TIMEOUT_MULTIPLIER), + &latency_timeout, + tc_h); + choose_phase (tc_h); +} + + +static void +update_avg_latency (const char *payload, unsigned int peer_nr) +{ + struct GNUNET_TIME_AbsoluteNBO *ts_n; + struct GNUNET_TIME_Absolute ts; + struct GNUNET_TIME_Relative latency; + size_t num_received = 0; + + ts_n = (struct GNUNET_TIME_AbsoluteNBO *) payload; + ts = GNUNET_TIME_absolute_ntoh (*ts_n); + latency = GNUNET_TIME_absolute_get_duration (ts); + + switch (phase[peer_nr]) + { + case TP_INIT: + GNUNET_assert (0); + break; + case TP_BURST_SHORT: + num_received = num_received_short[peer_nr]; + break; + case TP_BURST_LONG: + num_received = num_received_long[peer_nr]; + break; + case TP_SIZE_CHECK: + num_received = num_received_size[peer_nr]; + break; + } + if (1 >= num_received) + avg_latency[peer_nr] = latency.rel_value_us; + else + avg_latency[peer_nr] = ((avg_latency[peer_nr] * (num_received - 1)) + + latency.rel_value_us) + / num_received; + LOG (GNUNET_ERROR_TYPE_DEBUG, + "Latency of received packet by peer %u: %s with avg latency %lu\n", + peer_nr, + GNUNET_STRINGS_relative_time_to_string (latency, + GNUNET_YES), + avg_latency[peer_nr]); +} + + +static void +load_phase_config () +{ + + phase_short[0] = GNUNET_CONFIGURATION_get_value_yesno (cfg_peers[0], + TEST_SECTION, + "PHASE_SHORT"); + if (GNUNET_SYSERR == phase_short[0]) + phase_short[0] = GNUNET_YES; + + phase_short[1] = phase_short[0]; + + phase_long[0] = GNUNET_CONFIGURATION_get_value_yesno (cfg_peers[0], + TEST_SECTION, + "PHASE_LONG"); + + if (GNUNET_SYSERR == phase_long[0]) + phase_long[0] = GNUNET_YES; + + phase_long[1] = phase_long[0]; + + phase_size[0] = GNUNET_CONFIGURATION_get_value_yesno (cfg_peers[0], + TEST_SECTION, + "PHASE_SIZE"); + + if (GNUNET_SYSERR == phase_size[0]) + phase_size[0] = GNUNET_YES; + + phase_size[1] = phase_size[0]; +} + + +/** + * @brief Handle an incoming message + * + * Implements #GNUNET_TRANSPORT_TESTING_IncomingMessageCallback + + * @param cls Closure + * @param tc_h Handle to the receiving communicator + * @param msg Received message + */ +static void +incoming_message_cb ( + void *cls, + struct GNUNET_TRANSPORT_TESTING_TransportCommunicatorHandle *tc_h, + const char *payload, + size_t payload_len) +{ + unsigned int peer_nr; + + + peer_nr = get_peer_nr (cls, GNUNET_YES); + + if ((GNUNET_NO == bidirect) && (0 != strcmp ((char*) cls, + cfg_peers_name[NUM_PEERS - 1]))) + { + LOG (GNUNET_ERROR_TYPE_WARNING, + "unexpected receiver...\n"); + return; + } + /* Reset timeout */ + timeout[peer_nr] = GNUNET_TIME_relative_to_absolute ( + GNUNET_TIME_relative_multiply ( + GNUNET_TIME_UNIT_SECONDS, + TIMEOUT_MULTIPLIER)); + switch (phase[peer_nr]) + { + case TP_INIT: + GNUNET_break (0); + break; + case TP_BURST_SHORT: + { + GNUNET_assert (SHORT_MESSAGE_SIZE == payload_len); + num_received_short[peer_nr]++; + + update_avg_latency (payload, peer_nr); + if ((num_sent_short[peer_nr] == burst_packets_short) && + (num_received_short[peer_nr] == + burst_packets_short)) + { + finish_phase_short (peer_nr); + } + break; + } + case TP_BURST_LONG: + { + if (long_message_size != payload_len) + { + LOG (GNUNET_ERROR_TYPE_WARNING, + "Ignoring packet with wrong length (have: %lu, want: %lu)\n", + payload_len, long_message_size); + return; // Ignore + } + num_received_long[peer_nr]++; + + update_avg_latency (payload, peer_nr); + if ((num_sent_long[peer_nr] == burst_packets_long) && + (num_received_long[peer_nr] > + burst_packets_long)) + { + finish_phase_long (peer_nr); + } + break; + } + case TP_SIZE_CHECK: + { + size_t max_size = 64000; + + GNUNET_assert (TP_SIZE_CHECK == phase[peer_nr]); + if (LONG_MESSAGE_SIZE != long_message_size) + max_size = long_message_size; + num_received_size[peer_nr]++; + update_avg_latency (payload, peer_nr); + if ((GNUNET_YES == phase_size[peer_nr]) && (num_received_size[peer_nr] >= + (max_size) / 10) ) + { + LOG (GNUNET_ERROR_TYPE_MESSAGE, + "Size packet test for peer %u done.\n", + peer_nr); + LOG (GNUNET_ERROR_TYPE_MESSAGE, + "%lu/%lu packets -- avg latency: %llu us\n", + (unsigned long) num_received_size[peer_nr], + (unsigned long) num_sent_size[peer_nr], + (unsigned long long) avg_latency[peer_nr]); + iterations_left[peer_nr]--; + phase_size[peer_nr] = GNUNET_NO; + if (0 != iterations_left[peer_nr]) + { + // start_short = GNUNET_TIME_absolute_get (); + // phase = TP_BURST_SHORT; + num_received_size[peer_nr] = 0; + num_sent_size[peer_nr] = 0; + avg_latency[peer_nr] = 0; + num_sent_short[peer_nr] = 0; + num_sent_long[peer_nr] = 0; + num_received_short[peer_nr] = 0; + num_received_long[peer_nr] = 0; + // short_test (NULL); + if (((PEER_A == peer_nr) && finished[PEER_B]) || ((PEER_B == + peer_nr) && + finished[PEER_A])) + { + load_phase_config (); + } + } + choose_phase (get_tc_h (peer_nr)); + } + break; + } + } +} + + +static void +do_shutdown (void *cls) +{ + LOG (GNUNET_ERROR_TYPE_DEBUG, + "shutting down test.\n"); + + for (unsigned int i = 0; i < NUM_PEERS; i++) + { + if (NULL != box_stats[i]) + { + GNUNET_STATISTICS_get_cancel (box_stats[i]); + box_stats[i] = NULL; + } + if (NULL != rekey_stats[i]) + { + GNUNET_STATISTICS_get_cancel (rekey_stats[i]); + rekey_stats[i] = NULL; + } + if (NULL != to_task[i]) + { + GNUNET_SCHEDULER_cancel (to_task[i]); + to_task[i] = NULL; + } + GNUNET_TRANSPORT_TESTING_transport_communicator_service_stop (tc_hs[i]); + GNUNET_STATISTICS_destroy (stats[i], GNUNET_NO); + } +} + + +/** + * @brief Main function called by the scheduler + * + * @param cls Closure - Handle to confiation + */ +static void +run (void *cls) +{ + ret = 0; + // num_received = 0; + // num_sent = 0; + for (unsigned int i = 0; i < NUM_PEERS; i++) + { + tc_hs[i] = GNUNET_TRANSPORT_TESTING_transport_communicator_service_start ( + "transport", + communicator_binary, + cfg_peers_name[i], + &peer_id[i], + &communicator_available_cb, + &add_address_cb, + &queue_create_reply_cb, + &add_queue_cb, + &incoming_message_cb, + &handle_backchannel_cb, + cfg_peers_name[i]); /* cls */ + + if ((0 == strcmp ("udp", communicator_name)) && ((0 == strcmp ("rekey", + test_name)) + || + (0 == strcmp ( + "backchannel", + test_name))) ) + { + stats[i] = GNUNET_STATISTICS_create ("C-UDP", + cfg_peers[i]); + } + else if ((0 == strcmp ("bidirect", test_name))) + { + bidirect = GNUNET_YES; + } + } + GNUNET_SCHEDULER_add_shutdown (&do_shutdown, + NULL); +} + + +int +main (int argc, + char *const *argv) +{ + struct GNUNET_CRYPTO_EddsaPrivateKey *private_key; + char *test_mode; + char *cfg_peer; + + iterations_left[0] = TOTAL_ITERATIONS; + iterations_left[1] = TOTAL_ITERATIONS; + phase[0] = TP_INIT; + phase[1] = TP_INIT; + ret = 1; + test_name = GNUNET_STRINGS_get_suffix_from_binary_name (argv[0]); + communicator_name = strchr (test_name, '-'); + communicator_name[0] = '\0'; + communicator_name++; + test_mode = test_name; + + GNUNET_asprintf (&communicator_binary, + "gnunet-communicator-%s", + communicator_name); + + if (GNUNET_OK != + GNUNET_log_setup ("test_communicator_basic", + "DEBUG", + NULL)) + { + fprintf (stderr, "Unable to setup log\n"); + GNUNET_break (0); + return 2; + } + for (unsigned int i = 0; i < NUM_PEERS; i++) + { + GNUNET_asprintf ((&cfg_peer), + "test_communicator_%s_%s_peer%u.conf", + communicator_name, test_mode, i + 1); + cfg_peers_name[i] = cfg_peer; + cfg_peers[i] = GNUNET_CONFIGURATION_create (); + if (GNUNET_YES == + GNUNET_DISK_file_test (cfg_peers_name[i])) + { + if (GNUNET_SYSERR == + GNUNET_CONFIGURATION_load (cfg_peers[i], + cfg_peers_name[i])) + { + fprintf (stderr, + "Malformed configuration file `%s', exiting ...\n", + cfg_peers_name[i]); + return 1; + } + } + else + { + if (GNUNET_SYSERR == + GNUNET_CONFIGURATION_load (cfg_peers[i], + NULL)) + { + fprintf (stderr, + "Configuration file %s does not exist, exiting ...\n", + cfg_peers_name[i]); + return 1; + } + } + private_key = + GNUNET_CRYPTO_eddsa_key_create_from_configuration (cfg_peers[i]); + if (NULL == private_key) + { + LOG (GNUNET_ERROR_TYPE_ERROR, + "Unable to get peer ID\n"); + return 1; + } + GNUNET_CRYPTO_eddsa_key_get_public (private_key, + &peer_id[i].public_key); + GNUNET_free (private_key); + LOG (GNUNET_ERROR_TYPE_INFO, + "Identity of peer %u is %s\n", + i, + GNUNET_i2s_full (&peer_id[i])); + } + if (GNUNET_OK != + GNUNET_CONFIGURATION_get_value_number (cfg_peers[0], + TEST_SECTION, + "ALLOWED_PACKET_LOSS_SHORT", + &allowed_packet_loss_short)) + allowed_packet_loss_short = ALLOWED_PACKET_LOSS; + if (GNUNET_OK != + GNUNET_CONFIGURATION_get_value_number (cfg_peers[0], + TEST_SECTION, + "ALLOWED_PACKET_LOSS_LONG", + &allowed_packet_loss_long)) + allowed_packet_loss_long = ALLOWED_PACKET_LOSS; + if (GNUNET_OK != + GNUNET_CONFIGURATION_get_value_number (cfg_peers[0], + TEST_SECTION, + "BURST_PACKETS_SHORT", + &burst_packets_short)) + burst_packets_short = BURST_PACKETS; + if (GNUNET_OK != + GNUNET_CONFIGURATION_get_value_number (cfg_peers[0], + TEST_SECTION, + "BURST_PACKETS_LONG", + &burst_packets_long)) + burst_packets_long = BURST_PACKETS; + if (GNUNET_OK != + GNUNET_CONFIGURATION_get_value_number (cfg_peers[0], + TEST_SECTION, + "DELAY_SHORT", + &delay_short_value)) + delay_short = DELAY; + else + GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_MICROSECONDS, + delay_short_value); + if (GNUNET_OK != + GNUNET_CONFIGURATION_get_value_number (cfg_peers[0], + TEST_SECTION, + "DELAY_SHORT", + &delay_long_value)) + delay_long = DELAY; + else + GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_MICROSECONDS, + delay_long_value); + load_phase_config (); + LOG (GNUNET_ERROR_TYPE_MESSAGE, "Starting test...\n"); + LOG (GNUNET_ERROR_TYPE_DEBUG, + "argv[0]: %s\n", + argv[0]); + LOG (GNUNET_ERROR_TYPE_DEBUG, + "test_name: %s\n", + test_name); + LOG (GNUNET_ERROR_TYPE_DEBUG, + "communicator_name: %s\n", + communicator_name); + LOG (GNUNET_ERROR_TYPE_DEBUG, + "communicator_binary: %s\n", + communicator_binary); + GNUNET_SCHEDULER_run (&run, + NULL); + return ret; +} diff --git a/src/service/transport/test_communicator_quic_basic_peer1.conf b/src/service/transport/test_communicator_quic_basic_peer1.conf new file mode 100644 index 000000000..d43752ecc --- /dev/null +++ b/src/service/transport/test_communicator_quic_basic_peer1.conf @@ -0,0 +1,45 @@ +@INLINE@ test_transport_defaults.conf + +[PATHS] +GNUNET_TEST_HOME = $GNUNET_TMP/test-communicator-unix-1/ + +[PEER] +PRIVATE_KEY = $GNUNET_TMP/test-communicator-unix-1/private.key + +[transport-tcp] +PORT = 52400 + +[transport-udp] +PORT = 52401 + +[transport-quic] +PORT = 52402 + +[transport] +#PORT = 60000 +UNIXPATH = $GNUNET_RUNTIME_DIR/gnunet-service-transport_test_1.sock + +[nat] +UNIXPATH = $GNUNET_TMP/test-communicator-unix-1/nat.sock +ENABLE_IPSCAN = YES + +[peerstore] +UNIXPATH = $GNUNET_TMP/test-communicator-unix-1/peerstore.sock + +[statistics] +PORT = 22461 +UNIXPATH = $GNUNET_RUNTIME_DIR/gnunet-service-statistics_test_1.sock + +[resolver] +PORT = 62089 +UNIXPATH = $GNUNET_RUNTIME_DIR/gnunet-service-resolver_test_1.sock + +[communicator-udp] +# PREFIX = valgrind --leak-check=full --track-origins=yes --log-file=/tmp/vg_com1 +BINDTO = 60002 +DISABLE_V6 = YES +MAX_QUEUE_LENGTH=5000 + +[communicator-quic] +BINDTO = 60002 +DISABLE_V6 = YES diff --git a/src/service/transport/test_communicator_quic_basic_peer2.conf b/src/service/transport/test_communicator_quic_basic_peer2.conf new file mode 100644 index 000000000..4fabc8a86 --- /dev/null +++ b/src/service/transport/test_communicator_quic_basic_peer2.conf @@ -0,0 +1,45 @@ +@INLINE@ test_transport_defaults.conf + +[PATHS] +GNUNET_TEST_HOME = $GNUNET_TMP/test-gnunetd-plugin-transport/ + +[PEER] +PRIVATE_KEY = $GNUNET_TMP/test-communicator-unix-2/private.key + +[transport-tcp] +PORT = 52400 + +[transport-udp] +PORT = 52402 + +[transport-quic] +PORT = 52403 + +[transport] +#PORT = 60001 +UNIXPATH = $GNUNET_RUNTIME_DIR/gnunet-service-transport_test_2.sock + +[nat] +UNIXPATH = $GNUNET_TMP/test-communicator-unix-2/nat.sock + + +[peerstore] +UNIXPATH = $GNUNET_TMP/test-communicator-unix-2/peerstore.sock + +[statistics] +PORT = 22462 +UNIXPATH = $GNUNET_RUNTIME_DIR/gnunet-service-statistics_test_2.sock + +[resolver] +PORT = 62090 +UNIXPATH = $GNUNET_RUNTIME_DIR/gnunet-service-resolver_test_2.sock + +[communicator-udp] +# PREFIX = valgrind --leak-check=full --track-origins=yes --log-file=/tmp/vg_com2 +BINDTO = 60003 +DISABLE_V6 = YES +MAX_QUEUE_LENGTH=5000 + +[communicator-quic] +BINDTO = 60003 +DISABLE_V6 = YES \ No newline at end of file diff --git a/src/service/transport/test_communicator_tcp_basic_peer1.conf b/src/service/transport/test_communicator_tcp_basic_peer1.conf new file mode 100644 index 000000000..dbc227ac6 --- /dev/null +++ b/src/service/transport/test_communicator_tcp_basic_peer1.conf @@ -0,0 +1,48 @@ +@INLINE@ test_transport_defaults.conf + +[test-setup] +#PHASE_LONG=NO +#PHASE_SIZE=NO +#BURST_PACKETS_SHORT=1 + +[PATHS] +GNUNET_TEST_HOME = $GNUNET_TMP/test-communicator-unix-1/ + +[PEER] +PRIVATE_KEY = $GNUNET_TMP/test-communicator-unix-1/private.key + +[transport-tcp] +PORT = 52400 + +[transport-udp] +PORT = 52401 + +[transport] +PORT = 60000 +UNIXPATH = $GNUNET_RUNTIME_DIR/gnunet-service-transport_test_1.sock + +[nat] +UNIXPATH = $GNUNET_TMP/test-communicator-unix-1/nat.sock + +[peerstore] +UNIXPATH = $GNUNET_TMP/test-communicator-unix-1/peerstore.sock + +[statistics] +PORT = 22461 +UNIXPATH = $GNUNET_RUNTIME_DIR/gnunet-service-statistics_test_1.sock + +[communicator-unix] +UNIXPATH = $GNUNET_RUNTIME_DIR/test_gnunet-communicator-unix_1.sock + +[communicator-tcp] +#PREFIX = xterm -geometry 100x85 -T peer1 -e gdb --args +#PREFIX = valgrind --leak-check=full --track-origins=yes +BINDTO = 60002 +DISABLE_V6 = YES + +[communicator-udp] +BINDTO = 60002 + +[resolver] +PORT = 62089 +UNIXPATH = $GNUNET_RUNTIME_DIR/gnunet-service-resolver_test_1.sock diff --git a/src/service/transport/test_communicator_tcp_basic_peer2.conf b/src/service/transport/test_communicator_tcp_basic_peer2.conf new file mode 100644 index 000000000..b73157f0d --- /dev/null +++ b/src/service/transport/test_communicator_tcp_basic_peer2.conf @@ -0,0 +1,44 @@ +@INLINE@ test_transport_defaults.conf + +[PATHS] +GNUNET_TEST_HOME = $GNUNET_TMP/test-gnunetd-plugin-transport/ + +[PEER] +PRIVATE_KEY = $GNUNET_TMP/test-communicator-unix-2/private.key + + +[transport-tcp] +PORT = 52400 + +[transport-udp] +PORT = 52401 + +[transport] +PORT = 60001 +UNIXPATH = $GNUNET_RUNTIME_DIR/gnunet-service-transport_test_2.sock + +[nat] +UNIXPATH = $GNUNET_TMP/test-communicator-unix-2/nat.sock + +[peerstore] +UNIXPATH = $GNUNET_TMP/test-communicator-unix-2/peerstore.sock + +[statistics] +PORT = 22462 +UNIXPATH = $GNUNET_RUNTIME_DIR/gnunet-service-statistics_test_2.sock + +[communicator-unix] +UNIXPATH = $GNUNET_RUNTIME_DIR/test_gnunet-communicator-unix_2.sock + +[communicator-tcp] +#PREFIX = xterm -geometry 100x85 -T peer2 -e gdb --args +#PREFIX = valgrind --leak-check=full --track-origins=yes +BINDTO = 60003 +DISABLE_V6 = YES + +[communicator-udp] +BINDTO = 60003 + +[resolver] +PORT = 62090 +UNIXPATH = $GNUNET_RUNTIME_DIR/gnunet-service-resolver_test_2.sock diff --git a/src/service/transport/test_communicator_tcp_bidirect_peer1.conf b/src/service/transport/test_communicator_tcp_bidirect_peer1.conf new file mode 100644 index 000000000..dbc227ac6 --- /dev/null +++ b/src/service/transport/test_communicator_tcp_bidirect_peer1.conf @@ -0,0 +1,48 @@ +@INLINE@ test_transport_defaults.conf + +[test-setup] +#PHASE_LONG=NO +#PHASE_SIZE=NO +#BURST_PACKETS_SHORT=1 + +[PATHS] +GNUNET_TEST_HOME = $GNUNET_TMP/test-communicator-unix-1/ + +[PEER] +PRIVATE_KEY = $GNUNET_TMP/test-communicator-unix-1/private.key + +[transport-tcp] +PORT = 52400 + +[transport-udp] +PORT = 52401 + +[transport] +PORT = 60000 +UNIXPATH = $GNUNET_RUNTIME_DIR/gnunet-service-transport_test_1.sock + +[nat] +UNIXPATH = $GNUNET_TMP/test-communicator-unix-1/nat.sock + +[peerstore] +UNIXPATH = $GNUNET_TMP/test-communicator-unix-1/peerstore.sock + +[statistics] +PORT = 22461 +UNIXPATH = $GNUNET_RUNTIME_DIR/gnunet-service-statistics_test_1.sock + +[communicator-unix] +UNIXPATH = $GNUNET_RUNTIME_DIR/test_gnunet-communicator-unix_1.sock + +[communicator-tcp] +#PREFIX = xterm -geometry 100x85 -T peer1 -e gdb --args +#PREFIX = valgrind --leak-check=full --track-origins=yes +BINDTO = 60002 +DISABLE_V6 = YES + +[communicator-udp] +BINDTO = 60002 + +[resolver] +PORT = 62089 +UNIXPATH = $GNUNET_RUNTIME_DIR/gnunet-service-resolver_test_1.sock diff --git a/src/service/transport/test_communicator_tcp_bidirect_peer2.conf b/src/service/transport/test_communicator_tcp_bidirect_peer2.conf new file mode 100644 index 000000000..b73157f0d --- /dev/null +++ b/src/service/transport/test_communicator_tcp_bidirect_peer2.conf @@ -0,0 +1,44 @@ +@INLINE@ test_transport_defaults.conf + +[PATHS] +GNUNET_TEST_HOME = $GNUNET_TMP/test-gnunetd-plugin-transport/ + +[PEER] +PRIVATE_KEY = $GNUNET_TMP/test-communicator-unix-2/private.key + + +[transport-tcp] +PORT = 52400 + +[transport-udp] +PORT = 52401 + +[transport] +PORT = 60001 +UNIXPATH = $GNUNET_RUNTIME_DIR/gnunet-service-transport_test_2.sock + +[nat] +UNIXPATH = $GNUNET_TMP/test-communicator-unix-2/nat.sock + +[peerstore] +UNIXPATH = $GNUNET_TMP/test-communicator-unix-2/peerstore.sock + +[statistics] +PORT = 22462 +UNIXPATH = $GNUNET_RUNTIME_DIR/gnunet-service-statistics_test_2.sock + +[communicator-unix] +UNIXPATH = $GNUNET_RUNTIME_DIR/test_gnunet-communicator-unix_2.sock + +[communicator-tcp] +#PREFIX = xterm -geometry 100x85 -T peer2 -e gdb --args +#PREFIX = valgrind --leak-check=full --track-origins=yes +BINDTO = 60003 +DISABLE_V6 = YES + +[communicator-udp] +BINDTO = 60003 + +[resolver] +PORT = 62090 +UNIXPATH = $GNUNET_RUNTIME_DIR/gnunet-service-resolver_test_2.sock diff --git a/src/service/transport/test_communicator_tcp_rekey_peer1.conf b/src/service/transport/test_communicator_tcp_rekey_peer1.conf new file mode 100644 index 000000000..82fbf353a --- /dev/null +++ b/src/service/transport/test_communicator_tcp_rekey_peer1.conf @@ -0,0 +1,45 @@ +@INLINE@ test_transport_defaults.conf + +[PATHS] +GNUNET_TEST_HOME = $GNUNET_TMP/test-communicator-unix-1/ + +[PEER] +PRIVATE_KEY = $GNUNET_TMP/test-communicator-unix-1/private.key + +[transport-tcp] +PORT = 52400 + +[transport-udp] +PORT = 52401 + +[transport] +PORT = 60000 +UNIXPATH = $GNUNET_RUNTIME_DIR/gnunet-service-transport_test_1.sock + +[nat] +UNIXPATH = $GNUNET_TMP/test-communicator-unix-1/nat.sock +ENABLE_IPSCAN = YES + +[peerstore] +UNIXPATH = $GNUNET_TMP/test-communicator-unix-1/peerstore.sock + +[statistics] +PORT = 22461 +UNIXPATH = $GNUNET_RUNTIME_DIR/gnunet-service-statistics_test_1.sock + +[resolver] +PORT = 62089 +UNIXPATH = $GNUNET_RUNTIME_DIR/gnunet-service-resolver_test_1.sock + +[communicator-unix] +UNIXPATH = $GNUNET_RUNTIME_DIR/test_gnunet-communicator-unix_1.sock + +[communicator-tcp] +#PREFIX = xterm -geometry 100x85 -T peer1 -e gdb --args +#PREFIX = valgrind --leak-check=full --track-origins=yes +BINDTO = 60002 +DISABLE_V6 = YES +REKEY_INTERVAL = 100ms + +[communicator-udp] +BINDTO = 60002 diff --git a/src/service/transport/test_communicator_tcp_rekey_peer2.conf b/src/service/transport/test_communicator_tcp_rekey_peer2.conf new file mode 100644 index 000000000..086a996ae --- /dev/null +++ b/src/service/transport/test_communicator_tcp_rekey_peer2.conf @@ -0,0 +1,45 @@ +@INLINE@ test_transport_defaults.conf + +[PATHS] +GNUNET_TEST_HOME = $GNUNET_TMP/test-gnunetd-plugin-transport/ + +[PEER] +PRIVATE_KEY = $GNUNET_TMP/test-communicator-unix-2/private.key + + +[transport-tcp] +PORT = 52400 + +[transport-udp] +PORT = 52401 + +[transport] +PORT = 60001 +UNIXPATH = $GNUNET_RUNTIME_DIR/gnunet-service-transport_test_2.sock + +[nat] +UNIXPATH = $GNUNET_TMP/test-communicator-unix-2/nat.sock + +[peerstore] +UNIXPATH = $GNUNET_TMP/test-communicator-unix-2/peerstore.sock + +[statistics] +PORT = 22462 +UNIXPATH = $GNUNET_RUNTIME_DIR/gnunet-service-statistics_test_2.sock + +[resolver] +PORT = 62090 +UNIXPATH = $GNUNET_RUNTIME_DIR/gnunet-service-resolver_test_2.sock + +[communicator-unix] +UNIXPATH = $GNUNET_RUNTIME_DIR/test_gnunet-communicator-unix_2.sock + +[communicator-tcp] +#PREFIX = xterm -geometry 100x85 -T peer2 -e gdb --args +#PREFIX = valgrind --leak-check=full --track-origins=yes +BINDTO = 60003 +DISABLE_V6 = YES +REKEY_INTERVAL = 100ms + +[communicator-udp] +BINDTO = 60003 diff --git a/src/service/transport/test_communicator_udp_backchannel_peer1.conf b/src/service/transport/test_communicator_udp_backchannel_peer1.conf new file mode 100644 index 000000000..65f33bd6b --- /dev/null +++ b/src/service/transport/test_communicator_udp_backchannel_peer1.conf @@ -0,0 +1,48 @@ +@INLINE@ test_transport_defaults.conf + +[PATHS] +GNUNET_TEST_HOME = $GNUNET_TMP/test-communicator-unix-1/ + +[PEER] +PRIVATE_KEY = $GNUNET_TMP/test-communicator-unix-1/private.key + +[transport-tcp] +PORT = 52400 + +[transport-udp] +PORT = 52401 + +[transport] +PORT = 60000 +UNIXPATH = $GNUNET_RUNTIME_DIR/gnunet-service-transport_test_1.sock + +[nat] +UNIXPATH = $GNUNET_TMP/test-communicator-unix-1/nat.sock +ENABLE_IPSCAN = YES + +[peerstore] +UNIXPATH = $GNUNET_TMP/test-communicator-unix-1/peerstore.sock + +[statistics] +PORT = 22461 +UNIXPATH = $GNUNET_RUNTIME_DIR/gnunet-service-statistics_test_1.sock + +[resolver] +PORT = 62089 +UNIXPATH = $GNUNET_RUNTIME_DIR/gnunet-service-resolver_test_1.sock + +[communicator-unix] +UNIXPATH = $GNUNET_RUNTIME_DIR/test_gnunet-communicator-unix_1.sock + +[communicator-tcp] +BINDTO = 60002 +DISABLE_V6 = YES + +[communicator-udp] +# PREFIX = valgrind --leak-check=full --track-origins=yes --log-file=/tmp/vg_comm1 +BINDTO = 60002 +DISABLE_V6 = YES +MAX_QUEUE_LENGTH=5000 + +[communicator-test] +BACKCHANNEL_ENABLED = YES diff --git a/src/service/transport/test_communicator_udp_backchannel_peer2.conf b/src/service/transport/test_communicator_udp_backchannel_peer2.conf new file mode 100644 index 000000000..9875af724 --- /dev/null +++ b/src/service/transport/test_communicator_udp_backchannel_peer2.conf @@ -0,0 +1,48 @@ +@INLINE@ test_transport_defaults.conf + +[PATHS] +GNUNET_TEST_HOME = $GNUNET_TMP/test-gnunetd-plugin-transport/ + +[PEER] +PRIVATE_KEY = $GNUNET_TMP/test-communicator-unix-2/private.key + + +[transport-tcp] +PORT = 52400 + +[transport-udp] +PORT = 52401 + +[transport] +PORT = 60001 +UNIXPATH = $GNUNET_RUNTIME_DIR/gnunet-service-transport_test_2.sock + +[nat] +UNIXPATH = $GNUNET_TMP/test-communicator-unix-2/nat.sock + +[peerstore] +UNIXPATH = $GNUNET_TMP/test-communicator-unix-2/peerstore.sock + +[statistics] +PORT = 22462 +UNIXPATH = $GNUNET_RUNTIME_DIR/gnunet-service-statistics_test_2.sock + +[resolver] +PORT = 62090 +UNIXPATH = $GNUNET_RUNTIME_DIR/gnunet-service-resolver_test_2.sock + +[communicator-unix] +UNIXPATH = $GNUNET_RUNTIME_DIR/test_gnunet-communicator-unix_2.sock + +[communicator-tcp] +BINDTO = 60003 +DISABLE_V6 = YES + +[communicator-udp] +# PREFIX = valgrind --leak-check=full --track-origins=yes --log-file=/tmp/vg_comm2 +BINDTO = 60003 +DISABLE_V6 = YES +MAX_QUEUE_LENGTH=5000 + +[communicator-test] +BACKCHANNEL_ENABLED = YES diff --git a/src/service/transport/test_communicator_udp_basic_peer1.conf b/src/service/transport/test_communicator_udp_basic_peer1.conf new file mode 100644 index 000000000..6968b3aef --- /dev/null +++ b/src/service/transport/test_communicator_udp_basic_peer1.conf @@ -0,0 +1,39 @@ +@INLINE@ test_transport_defaults.conf + +[PATHS] +GNUNET_TEST_HOME = $GNUNET_TMP/test-communicator-unix-1/ + +[PEER] +PRIVATE_KEY = $GNUNET_TMP/test-communicator-unix-1/private.key + +[transport-tcp] +PORT = 52400 + +[transport-udp] +PORT = 52401 + +[transport] +#PORT = 60000 +UNIXPATH = $GNUNET_RUNTIME_DIR/gnunet-service-transport_test_1.sock + +[nat] +UNIXPATH = $GNUNET_TMP/test-communicator-unix-1/nat.sock +ENABLE_IPSCAN = YES + +[peerstore] +UNIXPATH = $GNUNET_TMP/test-communicator-unix-1/peerstore.sock + +[statistics] +PORT = 22461 +UNIXPATH = $GNUNET_RUNTIME_DIR/gnunet-service-statistics_test_1.sock + +[resolver] +PORT = 62089 +UNIXPATH = $GNUNET_RUNTIME_DIR/gnunet-service-resolver_test_1.sock + +[communicator-udp] +# PREFIX = valgrind --leak-check=full --track-origins=yes --log-file=/tmp/vg_com1 +BINDTO = 60002 +DISABLE_V6 = YES +MAX_QUEUE_LENGTH=5000 + diff --git a/src/service/transport/test_communicator_udp_basic_peer2.conf b/src/service/transport/test_communicator_udp_basic_peer2.conf new file mode 100644 index 000000000..781bfa7da --- /dev/null +++ b/src/service/transport/test_communicator_udp_basic_peer2.conf @@ -0,0 +1,39 @@ +@INLINE@ test_transport_defaults.conf + +[PATHS] +GNUNET_TEST_HOME = $GNUNET_TMP/test-gnunetd-plugin-transport/ + +[PEER] +PRIVATE_KEY = $GNUNET_TMP/test-communicator-unix-2/private.key + + +[transport-tcp] +PORT = 52400 + +[transport-udp] +PORT = 52402 + +[transport] +#PORT = 60001 +UNIXPATH = $GNUNET_RUNTIME_DIR/gnunet-service-transport_test_2.sock + +[nat] +UNIXPATH = $GNUNET_TMP/test-communicator-unix-2/nat.sock + + +[peerstore] +UNIXPATH = $GNUNET_TMP/test-communicator-unix-2/peerstore.sock + +[statistics] +PORT = 22462 +UNIXPATH = $GNUNET_RUNTIME_DIR/gnunet-service-statistics_test_2.sock + +[resolver] +PORT = 62090 +UNIXPATH = $GNUNET_RUNTIME_DIR/gnunet-service-resolver_test_2.sock + +[communicator-udp] +# PREFIX = valgrind --leak-check=full --track-origins=yes --log-file=/tmp/vg_com2 +BINDTO = 60003 +DISABLE_V6 = YES +MAX_QUEUE_LENGTH=5000 diff --git a/src/service/transport/test_communicator_udp_rekey_peer1.conf b/src/service/transport/test_communicator_udp_rekey_peer1.conf new file mode 100644 index 000000000..9b0fa7497 --- /dev/null +++ b/src/service/transport/test_communicator_udp_rekey_peer1.conf @@ -0,0 +1,52 @@ +@INLINE@ test_transport_defaults.conf + +[PATHS] +GNUNET_TEST_HOME = $GNUNET_TMP/test-communicator-unix-1/ + +[PEER] +PRIVATE_KEY = $GNUNET_TMP/test-communicator-unix-1/private.key + +[transport-tcp] +PORT = 52400 + +[transport-udp] +PORT = 52401 + +[transport] +PORT = 60000 +UNIXPATH = $GNUNET_RUNTIME_DIR/gnunet-service-transport_test_1.sock + +[nat] +UNIXPATH = $GNUNET_TMP/test-communicator-unix-1/nat.sock +ENABLE_IPSCAN = YES + +[peerstore] +UNIXPATH = $GNUNET_TMP/test-communicator-unix-1/peerstore.sock + +[statistics] +PORT = 22461 +UNIXPATH = $GNUNET_RUNTIME_DIR/gnunet-service-statistics_test_1.sock + +[resolver] +PORT = 62089 +UNIXPATH = $GNUNET_RUNTIME_DIR/gnunet-service-resolver_test_1.sock + +[communicator-unix] +UNIXPATH = $GNUNET_RUNTIME_DIR/test_gnunet-communicator-unix_1.sock + +[communicator-tcp] +BINDTO = 60002 +DISABLE_V6 = YES +REKEY_INTERVAL = 100ms + +[communicator-udp] +#PREFIX = xterm -geometry 100x85 -T peer1 -e gdb --args +#PREFIX = valgrind --leak-check=full --track-origins=yes +BINDTO = 60002 +DISABLE_V6 = YES +MAX_QUEUE_LENGTH=5000 +REKEY_INTERVAL = 100ms +REKEY_MAX_BYTES=9KiB + +[communicator-test] +BACKCHANNEL_ENABLED = YES diff --git a/src/service/transport/test_communicator_udp_rekey_peer2.conf b/src/service/transport/test_communicator_udp_rekey_peer2.conf new file mode 100644 index 000000000..383ab19d2 --- /dev/null +++ b/src/service/transport/test_communicator_udp_rekey_peer2.conf @@ -0,0 +1,52 @@ +@INLINE@ test_transport_defaults.conf + +[PATHS] +GNUNET_TEST_HOME = $GNUNET_TMP/test-gnunetd-plugin-transport/ + +[PEER] +PRIVATE_KEY = $GNUNET_TMP/test-communicator-unix-2/private.key + + +[transport-tcp] +PORT = 52400 + +[transport-udp] +PORT = 52401 + +[transport] +PORT = 60001 +UNIXPATH = $GNUNET_RUNTIME_DIR/gnunet-service-transport_test_2.sock + +[nat] +UNIXPATH = $GNUNET_TMP/test-communicator-unix-2/nat.sock + +[peerstore] +UNIXPATH = $GNUNET_TMP/test-communicator-unix-2/peerstore.sock + +[statistics] +PORT = 22462 +UNIXPATH = $GNUNET_RUNTIME_DIR/gnunet-service-statistics_test_2.sock + +[resolver] +PORT = 62090 +UNIXPATH = $GNUNET_RUNTIME_DIR/gnunet-service-resolver_test_2.sock + +[communicator-unix] +UNIXPATH = $GNUNET_RUNTIME_DIR/test_gnunet-communicator-unix_2.sock + +[communicator-tcp] +BINDTO = 60003 +DISABLE_V6 = YES +REKEY_INTERVAL = 100ms + +[communicator-udp] +#PREFIX = xterm -geometry 100x85 -T peer1 -e gdb --args +#PREFIX = valgrind --leak-check=full --track-origins=yes +BINDTO = 60003 +DISABLE_V6 = YES +MAX_QUEUE_LENGTH=5000 +REKEY_INTERVAL = 100ms +REKEY_MAX_BYTES=9KiB + +[communicator-test] +BACKCHANNEL_ENABLED = YES diff --git a/src/service/transport/test_communicator_unix_basic_peer1.conf b/src/service/transport/test_communicator_unix_basic_peer1.conf new file mode 100644 index 000000000..13ba2d16b --- /dev/null +++ b/src/service/transport/test_communicator_unix_basic_peer1.conf @@ -0,0 +1,43 @@ +@INLINE@ test_transport_defaults.conf + +[PATHS] +GNUNET_TEST_HOME = $GNUNET_TMP/test-communicator-unix-1/ + +[PEER] +PRIVATE_KEY = $GNUNET_TMP/test-communicator-unix-1/private.key + +[transport-tcp] +PORT = 52400 + +[transport-udp] +PORT = 52401 + +[transport] +PORT = 60000 +UNIXPATH = $GNUNET_RUNTIME_DIR/gnunet-service-transport_test_1.sock + +[nat] +UNIXPATH = $GNUNET_TMP/communicator-unix-1/nat.sock +ENABLE_IPSCAN = YES + +[peerstore] +UNIXPATH = $GNUNET_TMP/test-communicator-unix-1/peerstore.sock + +[statistics] +PORT = 22461 +UNIXPATH = $GNUNET_RUNTIME_DIR/gnunet-service-statistics_test_1.sock + +[resolver] +PORT = 62089 +UNIXPATH = $GNUNET_RUNTIME_DIR/gnunet-service-resolver_test_1.sock + +[communicator-unix] +#PREFIX = xterm -geometry 100x85 -T peer1 -e gdb --args +#PREFIX = valgrind --leak-check=full --track-origins=yes +UNIXPATH = $GNUNET_RUNTIME_DIR/communicator-unix-1.sock + +[communicator-tcp] +BINDTO = 60002 + +[communicator-udp] +BINDTO = 60002 diff --git a/src/service/transport/test_communicator_unix_basic_peer2.conf b/src/service/transport/test_communicator_unix_basic_peer2.conf new file mode 100644 index 000000000..727e844a7 --- /dev/null +++ b/src/service/transport/test_communicator_unix_basic_peer2.conf @@ -0,0 +1,43 @@ +@INLINE@ test_transport_defaults.conf + +[PATHS] +GNUNET_TEST_HOME = $GNUNET_TMP/test-gnunetd-plugin-transport/ + +[PEER] +PRIVATE_KEY = $GNUNET_TMP/test-communicator-unix-2/private.key + + +[transport-tcp] +PORT = 52400 + +[transport-udp] +PORT = 52401 + +[transport] +PORT = 60001 +UNIXPATH = $GNUNET_RUNTIME_DIR/gnunet-service-transport_test_2.sock + +[nat] +UNIXPATH = $GNUNET_TMP/communicator-unix-2/nat.sock + +[peerstore] +UNIXPATH = $GNUNET_TMP/test-communicator-unix-2/peerstore.sock + +[statistics] +PORT = 22462 +UNIXPATH = $GNUNET_RUNTIME_DIR/gnunet-service-statistics_test_2.sock + +[resolver] +PORT = 62090 +UNIXPATH = $GNUNET_RUNTIME_DIR/gnunet-service-resolver_test_2.sock + +[communicator-unix] +#PREFIX = xterm -geometry 100x85 -T peer2 -e gdb --args +#PREFIX = valgrind --leak-check=full --track-origins=yes +UNIXPATH = $GNUNET_RUNTIME_DIR/communicator-unix-2.sock + +[communicator-tcp] +BINDTO = 60003 + +[communicator-udp] +BINDTO = 60003 diff --git a/src/service/transport/test_delay b/src/service/transport/test_delay new file mode 100755 index 000000000..5f82b65fb --- /dev/null +++ b/src/service/transport/test_delay @@ -0,0 +1,19 @@ +#!/bin/sh + +TEMP=$(getopt t: "$*") + +if [ $? != 0 ] ; then + exit 1 +fi + +eval set -- "$TEMP" + +while true ; do + case "$1" in + (-t) sleep "$2" ; shift 2 ;; + (--) shift ; break ;; + (*) echo "Error parsing getopt output" ; exit 1 ;; + esac +done +echo "exec $@" +exec "$@" diff --git a/src/service/transport/test_plugin_hostkey b/src/service/transport/test_plugin_hostkey new file mode 100644 index 000000000..e7b1ad012 Binary files /dev/null and b/src/service/transport/test_plugin_hostkey differ diff --git a/src/service/transport/test_plugin_hostkey.ecc b/src/service/transport/test_plugin_hostkey.ecc new file mode 100644 index 000000000..18641b798 --- /dev/null +++ b/src/service/transport/test_plugin_hostkey.ecc @@ -0,0 +1 @@ +‚”ˆÖ’ÛËy¢/HÒc ȃ§¿±;¼»f?¶@…~áJ \ No newline at end of file diff --git a/src/service/transport/test_tng_defaults.conf b/src/service/transport/test_tng_defaults.conf new file mode 100644 index 000000000..e37dab25e --- /dev/null +++ b/src/service/transport/test_tng_defaults.conf @@ -0,0 +1,14 @@ +@INLINE@ ../../contrib/conf/gnunet/no_forcestart.conf + +[PATHS] +GNUNET_TEST_HOME = $GNUNET_TMP/test-tng/ + +[transport] +# PREFIX = valgrind + +[nat] +DISABLEV6 = NO +RETURN_LOCAL_ADDRESSES = YES +BINDTO = 127.0.0.1 +INTERNAL_ADDRESS = 127.0.0.1 +EXTERNAL_ADDRESS = 127.0.0.1 diff --git a/src/service/transport/test_transport_address_switch.c b/src/service/transport/test_transport_address_switch.c new file mode 100644 index 000000000..ce5117bd1 --- /dev/null +++ b/src/service/transport/test_transport_address_switch.c @@ -0,0 +1,433 @@ +/* + This file is part of GNUnet. + Copyright (C) 2009, 2010, 2011, 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 . + + SPDX-License-Identifier: AGPL3.0-or-later + */ +/** + * @file transport/test_transport_address_switch.c + * @brief base test case for transport implementations + * + * This test case tests if peers can successfully switch addresses when + * connected for plugins supporting multiple addresses by monitoring transport's + * statistic values. + * + * This test starts 2 peers and connects them. When connected test messages + * are transmitted from peer 2 to peer 1. The test monitors transport's + * statistics values for information about address switch attempts. + * + * The test passes with success if one of the peers could successfully switch + * addresses in connected state and a test message was successfully transmitted + * after this switch. + * + * Since it is not possible to trigger an address switch from outside, + * the test returns "77" (skipped) when no address switching attempt + * takes place. It fails if an address switch attempt fails. + * + * NOTE: The test seems largely useless right now, as we simply NEVER + * switch addresses under the test conditions. However, it may be a + * good starting point for a future test. For now, it always times + * out and returns "77" (skipped), so we set the timeout suitably low. + */ +#include "platform.h" +#include "gnunet_transport_service.h" +#include "gnunet_ats_service.h" +#include "transport-testing.h" + + +/** + * Testcase timeout (set aggressively as we know this test doesn't work right now) + */ +#define TIMEOUT GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS, 30) + + +static struct GNUNET_TRANSPORT_TESTING_ConnectCheckContext *ccc; + +static struct GNUNET_SCHEDULER_Task *measure_task; + + +/** + * Statistics we track per peer. + */ +struct PeerStats +{ + struct GNUNET_STATISTICS_Handle *stat; + + unsigned int addresses_avail; + + unsigned int switch_attempts; + + unsigned int switch_success; + + unsigned int switch_fail; +}; + +static struct PeerStats stats[2]; + +/* Amount of data transferred since last switch attempt */ +static unsigned long long bytes_sent_after_switch; + +static unsigned long long bytes_recv_after_switch; + + +static int +stat_start_attempt_cb (void *cls, + const char *subsystem, + const char *name, + uint64_t value, + int is_persistent) +{ + struct PeerStats *stat = cls; + + stat->switch_attempts++; + GNUNET_log (GNUNET_ERROR_TYPE_INFO, "Switch attempted (%p)", stat); + bytes_recv_after_switch = 0; + bytes_sent_after_switch = 0; + + return GNUNET_OK; +} + + +static int +stat_success_attempt_cb (void *cls, + const char *subsystem, + const char *name, + uint64_t value, + int is_persistent) +{ + struct PeerStats *stat = cls; + + stat->switch_success++; + GNUNET_log (GNUNET_ERROR_TYPE_INFO, "Switch succeeded (%p)", stat); + return GNUNET_OK; +} + + +static int +stat_fail_attempt_cb (void *cls, + const char *subsystem, + const char *name, + uint64_t value, + int is_persistent) +{ + struct PeerStats *stat = cls; + + if (value == 0) + return GNUNET_OK; + + stat->switch_fail++; + GNUNET_log (GNUNET_ERROR_TYPE_WARNING, "Switch failed (%p)", stat); + return GNUNET_OK; +} + + +static int +stat_addresses_available (void *cls, + const char *subsystem, + const char *name, + uint64_t value, + int is_persistent) +{ + struct PeerStats *stat = cls; + + stat->addresses_avail++; + return GNUNET_OK; +} + + +/** + * List of statistics entries we care about. + */ +static struct WatchEntry +{ + /** + * Name of the statistic we watch. + */ + const char *stat_name; + + /** + * Handler to register; + */ + GNUNET_STATISTICS_Iterator stat_handler; +} watches[] = +{ { "# Attempts to switch addresses", &stat_start_attempt_cb }, + { "# Successful attempts to switch addresses", &stat_success_attempt_cb }, + { "# Failed attempts to switch addresses (failed to send CONNECT CONT)", + &stat_fail_attempt_cb }, + { "# Failed attempts to switch addresses (failed to send CONNECT)", + &stat_fail_attempt_cb }, + { "# Failed attempts to switch addresses (no response)", + &stat_fail_attempt_cb }, + { "# transport addresses", &stat_addresses_available }, + { NULL, NULL } }; + + +static void +custom_shutdown (void *cls) +{ + int result; + + if (NULL != measure_task) + { + GNUNET_SCHEDULER_cancel (measure_task); + measure_task = NULL; + } + if (0 == stats[0].switch_attempts + stats[1].switch_attempts) + { + GNUNET_log (GNUNET_ERROR_TYPE_WARNING, + "Test did not work, as peers didn't switch (flawed testcase)!\n"); + ccc->global_ret = 77; + } + else + { + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "Fail (timeout)! No transmission after switch! Stopping peers\n"); + ccc->global_ret = 77; /* GNUNET_SYSERR; */ + } + + /* stop statistics */ + for (unsigned int i = 0; i < 2; i++) + { + if (NULL != stats[i].stat) + { + for (unsigned int j = 0; NULL != watches[j].stat_name; j++) + GNUNET_assert (GNUNET_OK == + GNUNET_STATISTICS_watch_cancel (stats[i].stat, + "transport", + watches[j].stat_name, + watches[j].stat_handler, + &stats[i])); + GNUNET_STATISTICS_destroy (stats[i].stat, GNUNET_NO); + stats[i].stat = NULL; + } + } + + result = 0; + fprintf (stderr, "\n"); + if (stats[0].switch_attempts > 0) + { + fprintf ( + stderr, + "Peer 1 tried %u times to switch and succeeded %u times, failed %u times\n", + stats[0].switch_attempts, + stats[0].switch_success, + stats[0].switch_fail); + if (stats[0].switch_success != stats[0].switch_attempts) + { + GNUNET_break (0); + result++; + } + } + else if (stats[0].addresses_avail > 1) + { + fprintf (stderr, + "Peer 1 had %u addresses available, but did not try to switch\n", + stats[0].addresses_avail); + } + if (stats[1].switch_attempts > 0) + { + fprintf ( + stderr, + "Peer 2 tried %u times to switch and succeeded %u times, failed %u times\n", + stats[1].switch_attempts, + stats[1].switch_success, + stats[1].switch_fail); + if (stats[1].switch_success != stats[1].switch_attempts) + { + GNUNET_break (0); + result++; + } + } + else if (stats[1].addresses_avail > 1) + { + fprintf (stderr, + "Peer 2 had %u addresses available, but did not try to switch\n", + stats[1].addresses_avail); + } + + if (((stats[0].switch_attempts > 0) || (stats[1].switch_attempts > 0)) && + (bytes_sent_after_switch == 0)) + { + fprintf (stderr, "No data sent after switching!\n"); + GNUNET_break (0); + result++; + } + if (((stats[0].switch_attempts > 0) || (stats[1].switch_attempts > 0)) && + (bytes_recv_after_switch == 0)) + { + fprintf (stderr, "No data received after switching!\n"); + GNUNET_break (0); + result++; + } +#if 0 + /* This test is not really expected to pass right now... */ + if (0 != result) + ccc->global_ret = GNUNET_SYSERR; +#endif +} + + +static void +notify_receive (void *cls, + struct GNUNET_TRANSPORT_TESTING_PeerContext *receiver, + const struct GNUNET_PeerIdentity *sender, + const struct GNUNET_TRANSPORT_TESTING_TestMessage *hdr) +{ + if (GNUNET_TRANSPORT_TESTING_SIMPLE_MTYPE != ntohs (hdr->header.type)) + return; + + { + char *ps = GNUNET_strdup (GNUNET_i2s (&receiver->id)); + + GNUNET_log (GNUNET_ERROR_TYPE_INFO, + "Peer %u (`%s') got message %u of size %u from peer (`%s')\n", + receiver->no, + ps, + (uint32_t) ntohl (hdr->num), + ntohs (hdr->header.size), + GNUNET_i2s (sender)); + GNUNET_free (ps); + } + if (((stats[0].switch_attempts >= 1) || (stats[1].switch_attempts >= 1)) && + (stats[0].switch_attempts == + stats[0].switch_fail + stats[0].switch_success) && + (stats[1].switch_attempts == + stats[1].switch_fail + stats[1].switch_success)) + { + bytes_recv_after_switch += ntohs (hdr->header.size); + if ((bytes_sent_after_switch > 0) && (bytes_recv_after_switch > 0)) + { + /* A peer switched addresses and sent and received data after the + * switch operations */ + GNUNET_SCHEDULER_shutdown (); + } + } +} + + +static void +notify_send (void *cls) +{ + static uint32_t cnt; + + GNUNET_assert ( + GNUNET_OK == + GNUNET_TRANSPORT_TESTING_send (ccc->p[1], + ccc->p[0], + GNUNET_TRANSPORT_TESTING_SIMPLE_MTYPE, + GNUNET_TRANSPORT_TESTING_LARGE_MESSAGE_SIZE, + ++cnt, + ¬ify_send, + NULL)); + if (((stats[0].switch_attempts >= 1) || (stats[1].switch_attempts >= 1)) && + (stats[0].switch_attempts == + stats[0].switch_fail + stats[0].switch_success) && + (stats[1].switch_attempts == + stats[1].switch_fail + stats[1].switch_success)) + { + bytes_sent_after_switch += GNUNET_TRANSPORT_TESTING_LARGE_MESSAGE_SIZE; + } +} + + +static void +progress_indicator (void *cls) +{ + static int counter; + + measure_task = NULL; + counter++; + if ((TIMEOUT.rel_value_us / 1000 / 1000LL) < counter) + { + fprintf (stderr, "%s", ".\n"); + } + else + { + fprintf (stderr, "%s", "."); + measure_task = GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_UNIT_SECONDS, + &progress_indicator, + NULL); + } +} + + +static void +connected_cb (void *cls) +{ + for (unsigned int i = 0; i < 2; i++) + { + stats[i].stat = GNUNET_STATISTICS_create ("transport", ccc->p[i]->cfg); + if (NULL == stats[i].stat) + { + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "Fail! Could not create statistics for peers!\n"); + ccc->global_ret = GNUNET_SYSERR; + GNUNET_SCHEDULER_shutdown (); + return; + } + for (unsigned int j = 0; NULL != watches[j].stat_name; j++) + { + GNUNET_STATISTICS_watch (stats[i].stat, + "transport", + watches[j].stat_name, + watches[j].stat_handler, + &stats[i]); + } + } + /* Show progress */ + ccc->global_ret = GNUNET_OK; + measure_task = GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_UNIT_SECONDS, + &progress_indicator, + NULL); + /* Peers are connected, start transmit test messages */ + GNUNET_assert ( + GNUNET_OK == + GNUNET_TRANSPORT_TESTING_send (ccc->p[1], + ccc->p[0], + GNUNET_TRANSPORT_TESTING_SIMPLE_MTYPE, + GNUNET_TRANSPORT_TESTING_LARGE_MESSAGE_SIZE, + 0, + ¬ify_send, + NULL)); +} + + +int +main (int argc, char *argv[]) +{ + struct GNUNET_TRANSPORT_TESTING_ConnectCheckContext my_ccc = + { .connect_continuation = &connected_cb, + .config_file = "test_transport_api_data.conf", + .rec = ¬ify_receive, + .nc = &GNUNET_TRANSPORT_TESTING_log_connect, + .shutdown_task = &custom_shutdown, + .timeout = TIMEOUT }; + + ccc = &my_ccc; + int ret; + + ret = GNUNET_TRANSPORT_TESTING_main (2, + &GNUNET_TRANSPORT_TESTING_connect_check, + ccc); + if (77 == ret) + return 77; + if (GNUNET_OK != ret) + return 1; + return 0; +} + + +/* end of test_transport_address_switch.c */ diff --git a/src/service/transport/test_transport_address_switch_tcp_peer1.conf b/src/service/transport/test_transport_address_switch_tcp_peer1.conf new file mode 100644 index 000000000..cfcbfe41c --- /dev/null +++ b/src/service/transport/test_transport_address_switch_tcp_peer1.conf @@ -0,0 +1,48 @@ +@INLINE@ template_cfg_peer1.conf +[PATHS] +GNUNET_TEST_HOME = $GNUNET_TMP/test-transport/api-tcp-p1/ + +[ats] +UNSPECIFIED_QUOTA_IN = 8 KiB +UNSPECIFIED_QUOTA_OUT = 8 KiB +# LOOPBACK +LOOPBACK_QUOTA_IN = 8 KiB +LOOPBACK_QUOTA_OUT = 8 KiB +# LAN +LAN_QUOTA_IN = 8 KiB +LAN_QUOTA_OUT = 8 KiB +# WAN +WAN_QUOTA_IN = 8 KiB +WAN_QUOTA_OUT = 8 KiB +# WLAN +WLAN_QUOTA_IN = 8 KiB +WLAN_QUOTA_OUT = 8 KiB +# BLUETOOTH +BLUETOOTH_QUOTA_IN = 8 KiB +BLUETOOTH_QUOTA_OUT = 8 KiB + +[transport-tcp] +PORT = 12000 +TIMEOUT = 5 s + +[arm] +PORT = 12005 +UNIXPATH = $GNUNET_RUNTIME_DIR/gnunet-p1-service-arm.sock + +[statistics] +PORT = 12004 +UNIXPATH = $GNUNET_RUNTIME_DIR/gnunet-p1-service-statistics.sock + +[resolver] +PORT = 12003 +UNIXPATH = $GNUNET_RUNTIME_DIR/gnunet-p1-service-resolver.sock + +[peerinfo] +PORT = 12002 +UNIXPATH = $GNUNET_RUNTIME_DIR/gnunet-p1-service-peerinfo.sock + +[transport] +PORT = 12001 +UNIXPATH = $GNUNET_RUNTIME_DIR/gnunet-p1-service-transport.sock +PLUGINS = tcp + diff --git a/src/service/transport/test_transport_address_switch_tcp_peer2.conf b/src/service/transport/test_transport_address_switch_tcp_peer2.conf new file mode 100644 index 000000000..bda2354b6 --- /dev/null +++ b/src/service/transport/test_transport_address_switch_tcp_peer2.conf @@ -0,0 +1,48 @@ +@INLINE@ template_cfg_peer2.conf +[PATHS] +GNUNET_TEST_HOME = $GNUNET_TMP/test-transport/api-tcp-p2/ + +[ats] +UNSPECIFIED_QUOTA_IN = 8 KiB +UNSPECIFIED_QUOTA_OUT = 8 KiB +# LOOPBACK +LOOPBACK_QUOTA_IN = 8 KiB +LOOPBACK_QUOTA_OUT = 8 KiB +# LAN +LAN_QUOTA_IN = 8 KiB +LAN_QUOTA_OUT = 8 KiB +# WAN +WAN_QUOTA_IN = 8 KiB +WAN_QUOTA_OUT = 8 KiB +# WLAN +WLAN_QUOTA_IN = 8 KiB +WLAN_QUOTA_OUT = 8 KiB +# BLUETOOTH +BLUETOOTH_QUOTA_IN = 8 KiB +BLUETOOTH_QUOTA_OUT = 8 KiB + +[transport-tcp] +PORT = 12015 +TIMEOUT = 5 s + +[arm] +PORT = 12014 +UNIXPATH = $GNUNET_RUNTIME_DIR/gnunet-p2-service-arm.sock + +[statistics] +PORT = 12013 +UNIXPATH = $GNUNET_RUNTIME_DIR/gnunet-p2-service-statistics.sock + +[resolver] +PORT = 12012 +UNIXPATH = $GNUNET_RUNTIME_DIR/gnunet-p2-service-resolver.sock + +[peerinfo] +PORT = 12011 +UNIXPATH = $GNUNET_RUNTIME_DIR/gnunet-p2-service-peerinfo.sock + +[transport] +PORT = 12010 +PLUGINS = tcp +UNIXPATH = $GNUNET_RUNTIME_DIR/gnunet-p2-service-transport.sock + diff --git a/src/service/transport/test_transport_address_switch_udp_peer1.conf b/src/service/transport/test_transport_address_switch_udp_peer1.conf new file mode 100644 index 000000000..bdc3a2253 --- /dev/null +++ b/src/service/transport/test_transport_address_switch_udp_peer1.conf @@ -0,0 +1,48 @@ +@INLINE@ template_cfg_peer1.conf +[PATHS] +GNUNET_TEST_HOME = $GNUNET_TMP/test-transport/api-tcp-p1/ + +[ats] +UNSPECIFIED_QUOTA_IN = 8 KiB +UNSPECIFIED_QUOTA_OUT = 8 KiB +# LOOPBACK +LOOPBACK_QUOTA_IN = 8 KiB +LOOPBACK_QUOTA_OUT = 8 KiB +# LAN +LAN_QUOTA_IN = 8 KiB +LAN_QUOTA_OUT = 8 KiB +# WAN +WAN_QUOTA_IN = 8 KiB +WAN_QUOTA_OUT = 8 KiB +# WLAN +WLAN_QUOTA_IN = 8 KiB +WLAN_QUOTA_OUT = 8 KiB +# BLUETOOTH +BLUETOOTH_QUOTA_IN = 8 KiB +BLUETOOTH_QUOTA_OUT = 8 KiB + +[transport-tcp] +PORT = 12000 +TIMEOUT = 5 s + +[arm] +PORT = 12005 +UNIXPATH = $GNUNET_RUNTIME_DIR/gnunet-p1-service-arm.sock + +[statistics] +PORT = 12004 +UNIXPATH = $GNUNET_RUNTIME_DIR/gnunet-p1-service-statistics.sock + +[resolver] +PORT = 12003 +UNIXPATH = $GNUNET_RUNTIME_DIR/gnunet-p1-service-resolver.sock + +[peerinfo] +PORT = 12002 +UNIXPATH = $GNUNET_RUNTIME_DIR/gnunet-p1-service-peerinfo.sock + +[transport] +PORT = 12001 +UNIXPATH = $GNUNET_RUNTIME_DIR/gnunet-p1-service-transport.sock +PLUGINS = udp + diff --git a/src/service/transport/test_transport_address_switch_udp_peer2.conf b/src/service/transport/test_transport_address_switch_udp_peer2.conf new file mode 100644 index 000000000..ae6397db4 --- /dev/null +++ b/src/service/transport/test_transport_address_switch_udp_peer2.conf @@ -0,0 +1,48 @@ +@INLINE@ template_cfg_peer2.conf +[PATHS] +GNUNET_TEST_HOME = $GNUNET_TMP/test-transport/api-tcp-p2/ + +[ats] +UNSPECIFIED_QUOTA_IN = 8 KiB +UNSPECIFIED_QUOTA_OUT = 8 KiB +# LOOPBACK +LOOPBACK_QUOTA_IN = 8 KiB +LOOPBACK_QUOTA_OUT = 8 KiB +# LAN +LAN_QUOTA_IN = 8 KiB +LAN_QUOTA_OUT = 8 KiB +# WAN +WAN_QUOTA_IN = 8 KiB +WAN_QUOTA_OUT = 8 KiB +# WLAN +WLAN_QUOTA_IN = 8 KiB +WLAN_QUOTA_OUT = 8 KiB +# BLUETOOTH +BLUETOOTH_QUOTA_IN = 8 KiB +BLUETOOTH_QUOTA_OUT = 8 KiB + +[transport-tcp] +PORT = 12015 +TIMEOUT = 5 s + +[arm] +PORT = 12014 +UNIXPATH = $GNUNET_RUNTIME_DIR/gnunet-p2-service-arm.sock + +[statistics] +PORT = 12013 +UNIXPATH = $GNUNET_RUNTIME_DIR/gnunet-p2-service-statistics.sock + +[resolver] +PORT = 12012 +UNIXPATH = $GNUNET_RUNTIME_DIR/gnunet-p2-service-resolver.sock + +[peerinfo] +PORT = 12011 +UNIXPATH = $GNUNET_RUNTIME_DIR/gnunet-p2-service-peerinfo.sock + +[transport] +PORT = 12010 +PLUGINS = udp +UNIXPATH = $GNUNET_RUNTIME_DIR/gnunet-p2-service-transport.sock + diff --git a/src/service/transport/test_transport_api.c b/src/service/transport/test_transport_api.c new file mode 100644 index 000000000..5f5e03a9e --- /dev/null +++ b/src/service/transport/test_transport_api.c @@ -0,0 +1,126 @@ +/* + This file is part of GNUnet. + Copyright (C) 2009, 2010, 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 . + + SPDX-License-Identifier: AGPL3.0-or-later + */ +/** + * @file transport/test_transport_api.c + * @brief base test case for transport implementations + * @author Christian Grothoff + * + * This test case serves as a base for tcp, udp, and udp-nat + * transport test cases. Based on the executable being run + * the correct test case will be performed. Conservation of + * C code apparently. + */ +#include "platform.h" +#include "gnunet_transport_service.h" +#include "transport-testing.h" + +/** + * How long until we give up on transmitting the message? + */ +#define TIMEOUT GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS, 30) + +static struct GNUNET_TRANSPORT_TESTING_ConnectCheckContext *ccc; + + +static void +notify_receive (void *cls, + struct GNUNET_TRANSPORT_TESTING_PeerContext *receiver, + const struct GNUNET_PeerIdentity *sender, + const struct GNUNET_TRANSPORT_TESTING_TestMessage *message) +{ + { + char *ps = GNUNET_strdup (GNUNET_i2s (&receiver->id)); + + GNUNET_log (GNUNET_ERROR_TYPE_INFO, + "Peer %u (`%s') received message of type %d and size %u size from peer %s!\n", + receiver->no, + ps, + ntohs (message->header.type), + ntohs (message->header.size), + GNUNET_i2s (sender)); + GNUNET_free (ps); + } + + if ((GNUNET_TRANSPORT_TESTING_SIMPLE_MTYPE == ntohs (message->header.type)) && + (GNUNET_TRANSPORT_TESTING_LARGE_MESSAGE_SIZE == ntohs ( + message->header.size))) + { + ccc->global_ret = GNUNET_OK; + GNUNET_SCHEDULER_shutdown (); + } + else + { + GNUNET_break (0); + ccc->global_ret = GNUNET_SYSERR; + GNUNET_SCHEDULER_shutdown (); + } +} + + +/** + * Runs the test. + * + * @param argv the argv argument from main() + * @param bi_directional should we try to establish connections + * in both directions simultaneously? + */ +static int +test (char *argv[], + int bi_directional) +{ + struct GNUNET_TRANSPORT_TESTING_SendClosure sc = { + .num_messages = 1 + }; + struct GNUNET_TRANSPORT_TESTING_ConnectCheckContext my_ccc = { + .connect_continuation = &GNUNET_TRANSPORT_TESTING_large_send, + .connect_continuation_cls = &sc, + .config_file = "test_transport_api_data.conf", + .rec = ¬ify_receive, + .nc = &GNUNET_TRANSPORT_TESTING_log_connect, + .nd = &GNUNET_TRANSPORT_TESTING_log_disconnect, + .timeout = TIMEOUT, + .bi_directional = bi_directional + }; + + ccc = &my_ccc; + sc.ccc = ccc; + if (GNUNET_OK != + GNUNET_TRANSPORT_TESTING_main (2, + &GNUNET_TRANSPORT_TESTING_connect_check, + ccc)) + return 1; + return 0; +} + + +int +main (int argc, + char *argv[]) +{ + if ((0 != test (argv, + GNUNET_NO)) || + (0 != test (argv, + GNUNET_YES))) + return 1; + return 0; +} + + +/* end of test_transport_api.c */ diff --git a/src/service/transport/test_transport_api2.c b/src/service/transport/test_transport_api2.c new file mode 100644 index 000000000..4d423f7c2 --- /dev/null +++ b/src/service/transport/test_transport_api2.c @@ -0,0 +1,126 @@ +/* + This file is part of GNUnet. + Copyright (C) 2009, 2010, 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 . + + SPDX-License-Identifier: AGPL3.0-or-later + */ +/** + * @file transport/test_transport_api.c + * @brief base test case for transport implementations + * @author Christian Grothoff + * + * This test case serves as a base for tcp, udp, and udp-nat + * transport test cases. Based on the executable being run + * the correct test case will be performed. Conservation of + * C code apparently. + */ +#include "platform.h" +// #include "gnunet_transport_service.h" +#include "transport-testing2.h" + +/** + * How long until we give up on transmitting the message? + */ +#define TIMEOUT GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS, 30) + +static struct GNUNET_TRANSPORT_TESTING_ConnectCheckContext *ccc; + + +static void +notify_receive (void *cls, + struct GNUNET_TRANSPORT_TESTING_PeerContext *receiver, + const struct GNUNET_PeerIdentity *sender, + const struct GNUNET_TRANSPORT_TESTING_TestMessage *message) +{ + { + char *ps = GNUNET_strdup (GNUNET_i2s (&receiver->id)); + + GNUNET_log (GNUNET_ERROR_TYPE_INFO, + "Peer %u (`%s') received message of type %d and size %u size from peer %s!\n", + receiver->no, + ps, + ntohs (message->header.type), + ntohs (message->header.size), + GNUNET_i2s (sender)); + GNUNET_free (ps); + } + + if ((GNUNET_TRANSPORT_TESTING_SIMPLE_MTYPE == ntohs (message->header.type)) && + (GNUNET_TRANSPORT_TESTING_LARGE_MESSAGE_SIZE == ntohs ( + message->header.size))) + { + ccc->global_ret = GNUNET_OK; + GNUNET_SCHEDULER_shutdown (); + } + else + { + GNUNET_break (0); + ccc->global_ret = GNUNET_SYSERR; + GNUNET_SCHEDULER_shutdown (); + } +} + + +/** + * Runs the test. + * + * @param argv the argv argument from main() + * @param bi_directional should we try to establish connections + * in both directions simultaneously? + */ +static int +test (char *argv[], + int bi_directional) +{ + struct GNUNET_TRANSPORT_TESTING_SendClosure sc = { + .num_messages = 1 + }; + struct GNUNET_TRANSPORT_TESTING_ConnectCheckContext my_ccc = { + .connect_continuation = &GNUNET_TRANSPORT_TESTING_large_send, + .connect_continuation_cls = &sc, + .config_file = "test_transport_api_data.conf", + .rec = ¬ify_receive, + .nc = &GNUNET_TRANSPORT_TESTING_log_connect, + .nd = &GNUNET_TRANSPORT_TESTING_log_disconnect, + .timeout = TIMEOUT, + .bi_directional = bi_directional + }; + + ccc = &my_ccc; + sc.ccc = ccc; + if (GNUNET_OK != + GNUNET_TRANSPORT_TESTING_main (2, + &GNUNET_TRANSPORT_TESTING_connect_check, + ccc)) + return 1; + return 0; +} + + +int +main (int argc, + char *argv[]) +{ + if ((0 != test (argv, + GNUNET_NO)) || + (0 != test (argv, + GNUNET_YES))) + return 1; + return 0; +} + + +/* end of test_transport_api.c */ diff --git a/src/service/transport/test_transport_api2_tcp_node1.conf b/src/service/transport/test_transport_api2_tcp_node1.conf new file mode 100644 index 000000000..463ec61d8 --- /dev/null +++ b/src/service/transport/test_transport_api2_tcp_node1.conf @@ -0,0 +1,35 @@ +@INLINE@ template_cfg_peer1.conf +[PATHS] +GNUNET_TEST_HOME = $GNUNET_TMP/test-transport/api-tcp-p1/ + +[transport] +BINARY = gnunet-service-transport +PLUGINS = tcp +#PREFIX = valgrind --leak-check=full --track-origins=yes --trace-children=yes --log-file=$GNUNET_TEST_HOME/vg_peer1-%p +UNIXPATH = $GNUNET_RUNTIME_DIR/tng-p1.sock + +[PEER] +PRIVATE_KEY = $GNUNET_RUNTIME_DIR/private.key + +[communicator-tcp] +BINARY = gnunet-communicator-tcp +BINDTO = 192.168.15.1:60002 +DISABLE_V6 = YES +IMMEDIATE_START = YES +UNIXPATH = $GNUNET_RUNTIME_DIR/tcp-comm-p1.sock +#PREFIX = valgrind --leak-check=full --track-origins=yes --trace-children=yes --log-file=$GNUNET_TEST_HOME/vg_ctpeer1-%p +#PREFIX = xterm -geometry 100x85 -T peer1 -e gdb --args + +[communicator-udp] +#PREFIX = valgrind --leak-check=full --track-origins=yes --trace-children=yes --log-file=$GNUNET_TEST_HOME/vg_cupeer1-%p +BINARY = gnunet-communicator-udp +BINDTO = 192.168.15.1:60002 +DISABLE_V6 = YES +IMMEDIATE_START = YES +UNIXPATH = $GNUNET_RUNTIME_DIR/udp-comm-p1.sock + +[peerstore] +IMMEDIATE_START = YES + +#[transport] +#PREFIX = valgrind diff --git a/src/service/transport/test_transport_api2_tcp_node2.conf b/src/service/transport/test_transport_api2_tcp_node2.conf new file mode 100644 index 000000000..b7f92869e --- /dev/null +++ b/src/service/transport/test_transport_api2_tcp_node2.conf @@ -0,0 +1,22 @@ +@INLINE@ template_cfg_peer2.conf +[PATHS] +GNUNET_TEST_HOME = $GNUNET_TMP/test-transport/api-tcp-p2/ + +[transport] +BINARY = gnunet-service-transport +#PREFIX = valgrind --log-file=$GNUNET_TEST_HOME/vg_peer2-%p +UNIXPATH = $GNUNET_RUNTIME_DIR/tng-p2.sock + +[communicator-tcp] +BINARY = gnunet-communicator-tcp +BINDTO = 192.168.15.2:60003 +DISABLE_V6 = YES +IMMEDIATE_START = YES +#PREFIX = valgrind --log-file=$GNUNET_TEST_HOME/vg_comm2-%p +UNIXPATH = $GNUNET_RUNTIME_DIR/tcp-comm-p2.sock + +[peerstore] +IMMEDIATE_START = YES + +#[transport] +#PREFIX = valgrind diff --git a/src/service/transport/test_transport_api2_tcp_peer1.conf b/src/service/transport/test_transport_api2_tcp_peer1.conf new file mode 100644 index 000000000..54ec9fd9f --- /dev/null +++ b/src/service/transport/test_transport_api2_tcp_peer1.conf @@ -0,0 +1,23 @@ +@INLINE@ template_cfg_peer1.conf +[PATHS] +GNUNET_TEST_HOME = $GNUNET_TMP/test-transport/api-tcp-p1/ + +[transport] +BINARY = gnunet-service-transport +PLUGINS = tcp +#PREFIX = valgrind --log-file=$GNUNET_TEST_HOME/vg_peer1-%p +UNIXPATH = $GNUNET_RUNTIME_DIR/tng-p1.sock + +[communicator-tcp] +BINARY = gnunet-communicator-tcp +BINDTO = 60002 +DISABLE_V6 = YES +IMMEDIATE_START = YES +UNIXPATH = $GNUNET_RUNTIME_DIR/tcp-comm-p1.sock +#PREFIX = valgrind --log-file=$GNUNET_TEST_HOME/vg_cpeer1-%p + +[peerstore] +IMMEDIATE_START = YES + +#[transport] +#PREFIX = valgrind diff --git a/src/service/transport/test_transport_api2_tcp_peer2.conf b/src/service/transport/test_transport_api2_tcp_peer2.conf new file mode 100644 index 000000000..7ae5ac697 --- /dev/null +++ b/src/service/transport/test_transport_api2_tcp_peer2.conf @@ -0,0 +1,22 @@ +@INLINE@ template_cfg_peer2.conf +[PATHS] +GNUNET_TEST_HOME = $GNUNET_TMP/test-transport/api-tcp-p2/ + +[transport] +BINARY = gnunet-service-transport +#PREFIX = valgrind --log-file=$GNUNET_TEST_HOME/vg_peer2-%p +UNIXPATH = $GNUNET_RUNTIME_DIR/tng-p2.sock + +[communicator-tcp] +BINARY = gnunet-communicator-tcp +BINDTO = 60003 +DISABLE_V6 = YES +IMMEDIATE_START = YES +#PREFIX = valgrind --log-file=$GNUNET_TEST_HOME/vg_comm2-%p +UNIXPATH = $GNUNET_RUNTIME_DIR/tcp-comm-p2.sock + +[peerstore] +IMMEDIATE_START = YES + +#[transport] +#PREFIX = valgrind diff --git a/src/service/transport/test_transport_api2_tng_node.conf b/src/service/transport/test_transport_api2_tng_node.conf new file mode 100644 index 000000000..c50ccf662 --- /dev/null +++ b/src/service/transport/test_transport_api2_tng_node.conf @@ -0,0 +1,40 @@ +@INLINE@ template_tng_cfg_peer1.conf +[PATHS] +GNUNET_TEST_HOME = $GNUNET_TMP/test-transport/api-tcp-p1/ + +[transport] +BINARY = gnunet-service-transport +#PREFIX = valgrind --leak-check=full --track-origins=yes --trace-children=yes --log-file=$GNUNET_TEST_HOME/vg_peer1-%p +UNIXPATH = $GNUNET_RUNTIME_DIR/tng-p1.sock + +[PEER] +PRIVATE_KEY = $GNUNET_RUNTIME_DIR/private.key + +[communicator-tcp] +BINARY = gnunet-communicator-tcp +BINDTO = 192.168.15.1:60002 +DISABLE_V6 = YES +IMMEDIATE_START = YES +UNIXPATH = $GNUNET_RUNTIME_DIR/tcp-comm-p1.sock +#PREFIX = valgrind --leak-check=full --track-origins=yes --trace-children=yes --log-file=$GNUNET_TEST_HOME/vg_ctpeer1-%p +#PREFIX = xterm -geometry 100x85 -T peer1 -e gdb --args + +[communicator-udp] +#PREFIX = valgrind --leak-check=full --track-origins=yes --trace-children=yes --log-file=$GNUNET_TEST_HOME/vg_cupeer1-%p +BINARY = gnunet-communicator-udp +BINDTO = 192.168.15.1:60002 +DISABLE_V6 = YES +IMMEDIATE_START = YES +UNIXPATH = $GNUNET_RUNTIME_DIR/udp-comm-p1.sock + +[peerstore] +IMMEDIATE_START = YES + +[topology] +IMMEDIATE_START = YES + +[dht] +IMMEDIATE_START = YES + +[fs] +IMMEDIATE_START = YES diff --git a/src/service/transport/test_transport_api_data.conf b/src/service/transport/test_transport_api_data.conf new file mode 100644 index 000000000..c06235a0a --- /dev/null +++ b/src/service/transport/test_transport_api_data.conf @@ -0,0 +1,9 @@ +@INLINE@ test_transport_defaults.conf +[PATHS] + +[transport-tcp] +PORT = 52094 + +[transport-udp] +PORT = 52094 + diff --git a/src/service/transport/test_transport_api_monitor_peers.c b/src/service/transport/test_transport_api_monitor_peers.c new file mode 100644 index 000000000..c09e3782d --- /dev/null +++ b/src/service/transport/test_transport_api_monitor_peers.c @@ -0,0 +1,226 @@ +/* + This file is part of GNUnet. + Copyright (C) 2009, 2010, 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 . + + SPDX-License-Identifier: AGPL3.0-or-later + */ +/** + * @file transport/test_transport_api_monitor_peers.c + * @brief base test case for transport peer monitor API + */ +#include "platform.h" +#include "gnunet_transport_service.h" +#include "transport-testing.h" + +/** + * How long until we give up on transmitting the message? + */ +#define TIMEOUT GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS, 120) + +/** + * How long until we give up on transmitting the message? + */ +#define TIMEOUT_TRANSMIT GNUNET_TIME_relative_multiply ( \ + GNUNET_TIME_UNIT_SECONDS, 30) + +#define TEST_MESSAGE_SIZE 2600 + +#define TEST_MESSAGE_TYPE 12345 + +static struct GNUNET_TRANSPORT_TESTING_ConnectCheckContext *ccc; + +static struct GNUNET_TRANSPORT_PeerMonitoringContext *pmc_p1; + +static struct GNUNET_TRANSPORT_PeerMonitoringContext *pmc_p2; + +static int p1_c; + +static int p2_c; + +static int p1_c_notify; + +static int p2_c_notify; + + +static void +custom_shutdown (void *cls) +{ + if (NULL != pmc_p1) + { + GNUNET_TRANSPORT_monitor_peers_cancel (pmc_p1); + pmc_p1 = NULL; + } + if (NULL != pmc_p2) + { + GNUNET_TRANSPORT_monitor_peers_cancel (pmc_p2); + pmc_p2 = NULL; + } +} + + +static void +notify_receive (void *cls, + struct GNUNET_TRANSPORT_TESTING_PeerContext *receiver, + const struct GNUNET_PeerIdentity *sender, + const struct GNUNET_TRANSPORT_TESTING_TestMessage *message) +{ + char *ps = GNUNET_strdup (GNUNET_i2s (&receiver->id)); + + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "Peer %u (`%s') received message of type %d and size %u size from peer %s!\n", + receiver->no, + ps, + ntohs (message->header.type), + ntohs (message->header.size), + GNUNET_i2s (sender)); + GNUNET_free (ps); +} + + +static void +sendtask (void *cls) +{ + /* intentionally empty */ +} + + +static void +check_done () +{ + if ((GNUNET_YES == p1_c) && + (GNUNET_YES == p2_c) && + p1_c_notify && + p2_c_notify) + { + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Both peers state to be connected\n"); + ccc->global_ret = GNUNET_OK; + GNUNET_SCHEDULER_shutdown (); + } +} + + +static void +notify_connect (void *cls, + struct GNUNET_TRANSPORT_TESTING_PeerContext *me, + const struct GNUNET_PeerIdentity *other) +{ + GNUNET_TRANSPORT_TESTING_log_connect (cls, + me, + other); + if (0 == memcmp (other, &ccc->p[0]->id, sizeof(struct GNUNET_PeerIdentity))) + { + p1_c_notify = GNUNET_YES; + } + if (0 == memcmp (other, &ccc->p[1]->id, sizeof(struct GNUNET_PeerIdentity))) + { + p2_c_notify = GNUNET_YES; + } + check_done (); +} + + +static void +monitor1_cb (void *cls, + const struct GNUNET_PeerIdentity *peer, + const struct GNUNET_HELLO_Address *address, + enum GNUNET_TRANSPORT_PeerState state, + struct GNUNET_TIME_Absolute state_timeout) +{ + if ((NULL == address) || (NULL == ccc->p[0])) + return; + + GNUNET_log (GNUNET_ERROR_TYPE_INFO, + "Monitor 1: %s %s %s\n", + GNUNET_i2s (&address->peer), + GNUNET_TRANSPORT_ps2s (state), + GNUNET_STRINGS_absolute_time_to_string (state_timeout)); + if ((0 == memcmp (&address->peer, &ccc->p[1]->id, sizeof(ccc->p[1]->id))) && + (GNUNET_YES == GNUNET_TRANSPORT_is_connected (state)) && + (GNUNET_NO == p1_c)) + { + p1_c = GNUNET_YES; + check_done (); + } +} + + +static void +monitor2_cb (void *cls, + const struct GNUNET_PeerIdentity *peer, + const struct GNUNET_HELLO_Address *address, + enum GNUNET_TRANSPORT_PeerState state, + struct GNUNET_TIME_Absolute state_timeout) +{ + if ((NULL == address) || (NULL == ccc->p[1])) + return; + + GNUNET_log (GNUNET_ERROR_TYPE_INFO, + "Monitor 2: %s %s %s\n", + GNUNET_i2s (&address->peer), + GNUNET_TRANSPORT_ps2s (state), + GNUNET_STRINGS_absolute_time_to_string (state_timeout)); + if ((0 == memcmp (&address->peer, &ccc->p[0]->id, sizeof(ccc->p[0]->id))) && + (GNUNET_YES == GNUNET_TRANSPORT_is_connected (state)) && + (GNUNET_NO == p2_c)) + { + p2_c = GNUNET_YES; + check_done (); + } +} + + +static void +start_monitors (void *cls) +{ + pmc_p1 = GNUNET_TRANSPORT_monitor_peers (ccc->p[0]->cfg, + NULL, + GNUNET_NO, + &monitor1_cb, + NULL); + pmc_p2 = GNUNET_TRANSPORT_monitor_peers (ccc->p[1]->cfg, + NULL, + GNUNET_NO, + &monitor2_cb, + NULL); +} + + +int +main (int argc, char *argv[]) +{ + struct GNUNET_TRANSPORT_TESTING_ConnectCheckContext my_ccc = { + .pre_connect_task = &start_monitors, + .connect_continuation = &sendtask, + .config_file = "test_transport_api_data.conf", + .rec = ¬ify_receive, + .nc = ¬ify_connect, + .nd = &GNUNET_TRANSPORT_TESTING_log_disconnect, + .shutdown_task = &custom_shutdown, + .timeout = TIMEOUT + }; + + ccc = &my_ccc; + if (GNUNET_OK != + GNUNET_TRANSPORT_TESTING_main (2, + &GNUNET_TRANSPORT_TESTING_connect_check, + ccc)) + return 1; + return 0; +} + + +/* end of test_transport_api_monitor_peers.c */ diff --git a/src/service/transport/test_transport_api_monitor_peers_peer1.conf b/src/service/transport/test_transport_api_monitor_peers_peer1.conf new file mode 100644 index 000000000..bc9eee19b --- /dev/null +++ b/src/service/transport/test_transport_api_monitor_peers_peer1.conf @@ -0,0 +1,4 @@ +@INLINE@ template_cfg_peer1.conf +[PATHS] +GNUNET_TEST_HOME = $GNUNET_TMP/test-transport/api-monitoring-p1/ + diff --git a/src/service/transport/test_transport_api_monitor_peers_peer2.conf b/src/service/transport/test_transport_api_monitor_peers_peer2.conf new file mode 100644 index 000000000..5225a5a87 --- /dev/null +++ b/src/service/transport/test_transport_api_monitor_peers_peer2.conf @@ -0,0 +1,5 @@ +@INLINE@ template_cfg_peer2.conf +[PATHS] +GNUNET_TEST_HOME = $GNUNET_TMP/test-transport/api-monitoring-p2/ + + diff --git a/src/service/transport/test_transport_api_monitor_validation_peer1.conf b/src/service/transport/test_transport_api_monitor_validation_peer1.conf new file mode 100644 index 000000000..02f7bc2f0 --- /dev/null +++ b/src/service/transport/test_transport_api_monitor_validation_peer1.conf @@ -0,0 +1,6 @@ +@INLINE@ template_cfg_peer1.conf +[PATHS] +GNUNET_TEST_HOME = $GNUNET_TMP/test-transport/api-monitoring-val-p1/ + +[transport] +PLUGINS=tcp diff --git a/src/service/transport/test_transport_api_monitor_validation_peer2.conf b/src/service/transport/test_transport_api_monitor_validation_peer2.conf new file mode 100644 index 000000000..d4e0874f0 --- /dev/null +++ b/src/service/transport/test_transport_api_monitor_validation_peer2.conf @@ -0,0 +1,7 @@ +@INLINE@ template_cfg_peer2.conf +[PATHS] +GNUNET_TEST_HOME = $GNUNET_TMP/test-transport/api-monitoring-val-p2/ + +[TRANSPORT] +PLUGINS=tcp + diff --git a/src/service/transport/test_transport_api_multi_peer1.conf b/src/service/transport/test_transport_api_multi_peer1.conf new file mode 100644 index 000000000..b7cb98777 --- /dev/null +++ b/src/service/transport/test_transport_api_multi_peer1.conf @@ -0,0 +1,7 @@ +@INLINE@ template_cfg_peer1.conf +[PATHS] +GNUNET_TEST_HOME = $GNUNET_TMP/test-transport/api-multi-p1/ + +[transport] +PLUGINS = tcp udp + diff --git a/src/service/transport/test_transport_api_multi_peer2.conf b/src/service/transport/test_transport_api_multi_peer2.conf new file mode 100644 index 000000000..f69e0abd9 --- /dev/null +++ b/src/service/transport/test_transport_api_multi_peer2.conf @@ -0,0 +1,9 @@ +@INLINE@ template_cfg_peer2.conf +[PATHS] +GNUNET_TEST_HOME = $GNUNET_TMP/test-transport/api-multi-p2/ + +[nat] +ALLOW_NAT = NO + +[transport] +PLUGINS = tcp udp diff --git a/src/service/transport/test_transport_api_tcp_nat_peer1.conf b/src/service/transport/test_transport_api_tcp_nat_peer1.conf new file mode 100644 index 000000000..fb2fcecc6 --- /dev/null +++ b/src/service/transport/test_transport_api_tcp_nat_peer1.conf @@ -0,0 +1,34 @@ +@INLINE@ template_cfg_peer1.conf +[PATHS] +GNUNET_TEST_HOME = $GNUNET_TMP/test-transport/api-tcp-nat-p1/ + +[nat] +BEHIND_NAT = YES +ENABLE_NAT_SERVER = YES +DISABLEV6 = YES + +[transport-tcp] +PORT = 0 +TIMEOUT = 5 s + +[arm] +PORT = 51204 +UNIXPATH = $GNUNET_RUNTIME_DIR/gnunet-p1-service-arm.sock + +[statistics] +PORT = 12023 +UNIXPATH = $GNUNET_RUNTIME_DIR/gnunet-p1-service-statistics.sock + +[resolver] +PORT = 12022 +UNIXPATH = $GNUNET_RUNTIME_DIR/gnunet-p1-service-resolver.sock + +[peerinfo] +PORT = 12021 +UNIXPATH = $GNUNET_RUNTIME_DIR/gnunet-p1-service-peerinfo.sock + +[transport] +PORT = 29542 +PLUGINS = tcp +UNIXPATH = $GNUNET_RUNTIME_DIR/gnunet-p1-service-transport.sock + diff --git a/src/service/transport/test_transport_api_tcp_nat_peer2.conf b/src/service/transport/test_transport_api_tcp_nat_peer2.conf new file mode 100644 index 000000000..1faefc195 --- /dev/null +++ b/src/service/transport/test_transport_api_tcp_nat_peer2.conf @@ -0,0 +1,32 @@ +@INLINE@ template_cfg_peer2.conf +[PATHS] +GNUNET_TEST_HOME = $GNUNET_TMP/test-transport/api-tcp-nat-p2/ + +[nat] +DISABLEV6 = YES +ENABLE_NAT_CLIENT = YES + +[transport-tcp] +PORT = 12030 +TIMEOUT = 5 s + +[arm] +PORT = 12034 +UNIXPATH = $GNUNET_RUNTIME_DIR/gnunet-p2-service-arm.sock + +[statistics] +PORT = 12033 +UNIXPATH = $GNUNET_RUNTIME_DIR/gnunet-p2-service-statistics.sock + +[resolver] +PORT = 12032 +UNIXPATH = $GNUNET_RUNTIME_DIR/gnunet-p2-service-resolver.sock + +[peerinfo] +PORT = 12031 +UNIXPATH = $GNUNET_RUNTIME_DIR/gnunet-p2-service-peerinfo.sock + +[transport] +PORT = 45923 +PLUGINS = tcp +UNIXPATH = $GNUNET_RUNTIME_DIR/gnunet-p2-service-transport.sock diff --git a/src/service/transport/test_transport_api_tcp_peer1.conf b/src/service/transport/test_transport_api_tcp_peer1.conf new file mode 100644 index 000000000..eabd6b701 --- /dev/null +++ b/src/service/transport/test_transport_api_tcp_peer1.conf @@ -0,0 +1,9 @@ +@INLINE@ template_cfg_peer1.conf +[PATHS] +GNUNET_TEST_HOME = $GNUNET_TMP/test-transport/api-tcp-p1/ + +[transport] +PLUGINS = tcp + +#[transport] +#PREFIX = valgrind diff --git a/src/service/transport/test_transport_api_tcp_peer2.conf b/src/service/transport/test_transport_api_tcp_peer2.conf new file mode 100644 index 000000000..58ce0777f --- /dev/null +++ b/src/service/transport/test_transport_api_tcp_peer2.conf @@ -0,0 +1,9 @@ +@INLINE@ template_cfg_peer2.conf +[PATHS] +GNUNET_TEST_HOME = $GNUNET_TMP/test-transport/api-tcp-p2/ + +[transport] +PLUGINS = tcp + +#[transport] +#PREFIX = valgrind diff --git a/src/service/transport/test_transport_api_udp_nat_peer1.conf b/src/service/transport/test_transport_api_udp_nat_peer1.conf new file mode 100644 index 000000000..7bbcaa628 --- /dev/null +++ b/src/service/transport/test_transport_api_udp_nat_peer1.conf @@ -0,0 +1,34 @@ +@INLINE@ template_cfg_peer1.conf +[PATHS] +GNUNET_TEST_HOME = $GNUNET_TMP/test-transport/api-udp-nat-p1/ + +[nat] +BEHIND_NAT = YES +ALLOW_NAT = NO +ONLY_NAT_ADDRESSES = YES + +[transport-udp] +PORT = 0 + +[arm] +PORT = 12065 +UNIXPATH = $GNUNET_RUNTIME_DIR/gnunet-p1-service-arm.sock + +[statistics] +PORT = 12064 +UNIXPATH = $GNUNET_RUNTIME_DIR/gnunet-p1-service-statistics.sock + +[resolver] +PORT = 12063 +UNIXPATH = $GNUNET_RUNTIME_DIR/gnunet-p1-service-resolver.sock + +[peerinfo] +PORT = 12062 +UNIXPATH = $GNUNET_RUNTIME_DIR/gnunet-p1-service-peerinfo.sock + +[transport] +PORT = 12061 +PLUGINS = udp +UNIXPATH = $GNUNET_RUNTIME_DIR/gnunet-p1-service-transport.sock + + diff --git a/src/service/transport/test_transport_api_udp_nat_peer2.conf b/src/service/transport/test_transport_api_udp_nat_peer2.conf new file mode 100644 index 000000000..8bdb8c293 --- /dev/null +++ b/src/service/transport/test_transport_api_udp_nat_peer2.conf @@ -0,0 +1,32 @@ +@INLINE@ template_cfg_peer2.conf +[PATHS] +GNUNET_TEST_HOME = $GNUNET_TMP/test-transport/api-udp-nat-p2/ + +[nat] +ALLOW_NAT = YES + +[transport-udp] +PORT = 12070 + +[arm] +PORT = 12075 +UNIXPATH = $GNUNET_RUNTIME_DIR/gnunet-p2-service-arm.sock + +[statistics] +PORT = 12074 +UNIXPATH = $GNUNET_RUNTIME_DIR/gnunet-p2-service-statistics.sock + +[resolver] +PORT = 12073 +UNIXPATH = $GNUNET_RUNTIME_DIR/gnunet-p2-service-resolver.sock + +[peerinfo] +PORT = 12072 +UNIXPATH = $GNUNET_RUNTIME_DIR/gnunet-p2-service-peerinfo.sock + +[transport] +PORT = 12071 +PLUGINS = udp +UNIXPATH = $GNUNET_RUNTIME_DIR/gnunet-p2-service-transport.sock + + diff --git a/src/service/transport/test_transport_api_udp_peer1.conf b/src/service/transport/test_transport_api_udp_peer1.conf new file mode 100644 index 000000000..4684b77f2 --- /dev/null +++ b/src/service/transport/test_transport_api_udp_peer1.conf @@ -0,0 +1,17 @@ +@INLINE@ template_cfg_peer1.conf +[PATHS] +GNUNET_TEST_HOME = $GNUNET_TMP/test-transport/api-udp-p1/ + +[transport-udp] +PORT = 0 +BROADCAST = NO +MAX_BPS = 50000000 +BINDTO = 127.0.0.1 +BINDTO6 = ::1 + +[transport] +PORT = 12041 +PLUGINS = udp + + + diff --git a/src/service/transport/test_transport_api_udp_peer2.conf b/src/service/transport/test_transport_api_udp_peer2.conf new file mode 100644 index 000000000..96706ce49 --- /dev/null +++ b/src/service/transport/test_transport_api_udp_peer2.conf @@ -0,0 +1,15 @@ +@INLINE@ template_cfg_peer2.conf +[PATHS] +GNUNET_TEST_HOME = $GNUNET_TMP/test-transport/api-udp-p2/ + +[transport-udp] +PORT = 12050 +BROADCAST = NO +MAX_BPS = 50000000 +BINDTO = 127.0.0.1 +BINDTO6 = ::1 + +[transport] +PLUGINS = udp + + diff --git a/src/service/transport/test_transport_api_unix_peer1.conf b/src/service/transport/test_transport_api_unix_peer1.conf new file mode 100644 index 000000000..005558bcc --- /dev/null +++ b/src/service/transport/test_transport_api_unix_peer1.conf @@ -0,0 +1,10 @@ +@INLINE@ template_cfg_peer1.conf +[PATHS] +GNUNET_TEST_HOME = $GNUNET_TMP/test-transport/api-unix-p1/ + +[transport] +PLUGINS = unix + +[transport-unix] +PORT = 12120 + diff --git a/src/service/transport/test_transport_api_unix_peer2.conf b/src/service/transport/test_transport_api_unix_peer2.conf new file mode 100644 index 000000000..840ed81c7 --- /dev/null +++ b/src/service/transport/test_transport_api_unix_peer2.conf @@ -0,0 +1,7 @@ +@INLINE@ template_cfg_peer2.conf +[PATHS] +GNUNET_TEST_HOME = $GNUNET_TMP/test-transport/api-unix-p2/ + +[transport] +PLUGINS = unix + diff --git a/src/service/transport/test_transport_defaults.conf b/src/service/transport/test_transport_defaults.conf new file mode 100644 index 000000000..3aed73f0c --- /dev/null +++ b/src/service/transport/test_transport_defaults.conf @@ -0,0 +1,20 @@ +@INLINE@ ../../contrib/conf/gnunet/no_forcestart.conf + +[PATHS] +GNUNET_TEST_HOME = $GNUNET_TMP/test-tng/ + +[transport-tcp] +TIMEOUT = 300 s + +[transport] +# PREFIX = valgrind + +[nat] +DISABLEV6 = NO +RETURN_LOCAL_ADDRESSES = YES +BINDTO = 127.0.0.1 +INTERNAL_ADDRESS = 127.0.0.1 +EXTERNAL_ADDRESS = 127.0.0.1 + +[transport-udp] +BROADCAST_RECEIVE = no diff --git a/src/service/transport/test_transport_distance_vector_circle_topo.conf b/src/service/transport/test_transport_distance_vector_circle_topo.conf new file mode 100644 index 000000000..e8d694c3a --- /dev/null +++ b/src/service/transport/test_transport_distance_vector_circle_topo.conf @@ -0,0 +1,11 @@ +M:1 +N:3 +X:0 +B:0 +T:libgnunet_test_transport_plugin_cmd_simple_send_dv +R:1|{tcp_port:0}|{udp_port:1} +R:2|{tcp_port:0}|{udp_port:1} +R:3|{tcp_port:0}|{udp_port:1} +P:1:1|{connect:{P:2:1:udp}} +P:2:1|{connect:{P:3:1:udp}} +P:3:1|{connect:{P:1:1:udp}} diff --git a/src/service/transport/test_transport_distance_vector_inverse_topo.conf b/src/service/transport/test_transport_distance_vector_inverse_topo.conf new file mode 100644 index 000000000..e062b3e2e --- /dev/null +++ b/src/service/transport/test_transport_distance_vector_inverse_topo.conf @@ -0,0 +1,13 @@ +M:1 +N:4 +X:0 +AC:1 +T:libgnunet_test_transport_plugin_cmd_simple_send_dv +R:1|{tcp_port:1}|{udp_port:0} +R:2|{tcp_port:1}|{udp_port:0} +R:3|{tcp_port:1}|{udp_port:0} +R:4|{tcp_port:1}|{udp_port:0} +P:1:1|{connect:{P:2:1:tcp}}|{AC:2} +P:2:1|{connect:{P:3:1:tcp}}|{AC:2} +P:3:1|{connect:{P:4:1:tcp}}|{AC:2} +P:4:1|{AC:3} \ No newline at end of file diff --git a/src/service/transport/test_transport_distance_vector_topo.conf b/src/service/transport/test_transport_distance_vector_topo.conf new file mode 100644 index 000000000..ead3e0a0a --- /dev/null +++ b/src/service/transport/test_transport_distance_vector_topo.conf @@ -0,0 +1,8 @@ +M:2 +N:2 +X:0 +T:libgnunet_test_transport_plugin_cmd_simple_send_dv +R:1|{tcp_port:1}|{udp_port:0} +R:2|{tcp_port:1}|{udp_port:0} +P:1:1|{connect:{P:2:1:tcp}} +P:2:1|{connect:{P:1:1:tcp}} diff --git a/src/service/transport/test_transport_just_run_topo.conf b/src/service/transport/test_transport_just_run_topo.conf new file mode 100644 index 000000000..d27a2fc77 --- /dev/null +++ b/src/service/transport/test_transport_just_run_topo.conf @@ -0,0 +1,6 @@ +M:2 +N:1 +X:0 +T:libgnunet_test_transport_plugin_cmd_just_run +P:1:1|{connect:{P:1:2:tcp}|{P:1:2:udp}} +P:1:2|{connect:{P:1:1:tcp}|{P:1:1:udp}} \ No newline at end of file diff --git a/src/service/transport/test_transport_nat_icmp_tcp.sh b/src/service/transport/test_transport_nat_icmp_tcp.sh new file mode 100755 index 000000000..41cdde487 --- /dev/null +++ b/src/service/transport/test_transport_nat_icmp_tcp.sh @@ -0,0 +1,12 @@ +#!/bin/bash +if ! [ -d "/run/netns" ]; then + echo You have to create the directory /run/netns. +fi +if [ -f /proc/sys/kernel/unprivileged_userns_clone ]; then + if [ "$(cat /proc/sys/kernel/unprivileged_userns_clone)" != 1 ]; then + echo -e "Error during test setup: The kernel parameter kernel.unprivileged_userns_clone has to be set to 1! One has to execute\n\n sysctl kernel.unprivileged_userns_clone=1\n" + exit 78 + fi +fi +exec unshare -r -nmU bash -c "mount -t tmpfs --make-rshared tmpfs /run/netns; ./test_transport_start_with_config test_transport_nat_icmp_tcp_topo.conf" +#sudo valgrind --vgdb=yes --leak-check=full --track-origins=yes --trace-children=yes --trace-children-skip=/usr/bin/awk,/usr/bin/cut,/usr/bin/seq,/sbin/ip/sed/bash ./test_transport_start_with_config test_transport_nat_upnp_topo.conf diff --git a/src/service/transport/test_transport_nat_icmp_tcp_topo.conf b/src/service/transport/test_transport_nat_icmp_tcp_topo.conf new file mode 100644 index 000000000..37738c80f --- /dev/null +++ b/src/service/transport/test_transport_nat_icmp_tcp_topo.conf @@ -0,0 +1,7 @@ +M:1 +N:1 +X:1 +T:libgnunet_test_transport_plugin_cmd_nat_upnp +K:1|{connect:{P:1:1:tcp_natted}} +R:1|{tcp_port:0}|{udp_port:0} +P:1:1 \ No newline at end of file diff --git a/src/service/transport/test_transport_nat_upnp.sh b/src/service/transport/test_transport_nat_upnp.sh new file mode 100755 index 000000000..df43ef320 --- /dev/null +++ b/src/service/transport/test_transport_nat_upnp.sh @@ -0,0 +1,12 @@ +#!/bin/bash +if ! [ -d "/run/netns" ]; then + echo You have to create the directory /run/netns. +fi +if [ -f /proc/sys/kernel/unprivileged_userns_clone ]; then + if [ "$(cat /proc/sys/kernel/unprivileged_userns_clone)" != 1 ]; then + echo -e "Error during test setup: The kernel parameter kernel.unprivileged_userns_clone has to be set to 1! One has to execute\n\n sysctl kernel.unprivileged_userns_clone=1\n" + exit 78 + fi +fi +exec unshare -r -nmU bash -c "mount -t tmpfs --make-rshared tmpfs /run/netns; ./test_transport_start_with_config test_transport_nat_upnp_topo.conf" +#sudo valgrind --vgdb=yes --leak-check=full --track-origins=yes --trace-children=yes --trace-children-skip=/usr/bin/awk,/usr/bin/cut,/usr/bin/seq,/sbin/ip/sed/bash ./test_transport_start_with_config test_transport_nat_upnp_topo.conf diff --git a/src/service/transport/test_transport_nat_upnp_topo.conf b/src/service/transport/test_transport_nat_upnp_topo.conf new file mode 100644 index 000000000..e02633d4b --- /dev/null +++ b/src/service/transport/test_transport_nat_upnp_topo.conf @@ -0,0 +1,7 @@ +M:1 +N:1 +X:1 +T:libgnunet_test_transport_plugin_cmd_nat_upnp +K:1|{connect:{P:1:1:tcp}} +R:1|{tcp_port:0}|{udp_port:0}|{script:upnp.sh} +P:1:1|{connect:{K:1:udp}} \ No newline at end of file diff --git a/src/service/transport/test_transport_plugin_cmd_just_run.c b/src/service/transport/test_transport_plugin_cmd_just_run.c new file mode 100644 index 000000000..df7484884 --- /dev/null +++ b/src/service/transport/test_transport_plugin_cmd_just_run.c @@ -0,0 +1,494 @@ +/* + This file is part of GNUnet + Copyright (C) 2021 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 . + + SPDX-License-Identifier: AGPL3.0-or-later + */ + +/** + * @file testbed/plugin_cmd_simple_send.c + * @brief a plugin to provide the API for running test cases. + * @author t3sserakt + */ +#include "platform.h" +#include "gnunet_testing_barrier.h" +#include "gnunet_testing_netjail_lib.h" +#include "gnunet_util_lib.h" +#include "gnunet_transport_application_service.h" +#include "transport-testing2.h" +#include "transport-testing-cmds.h" +#include "gnunet_testing_barrier.h" +#include "gnunet_core_service.h" + +/** + * Generic logging shortcut + */ +#define LOG(kind, ...) GNUNET_log (kind, __VA_ARGS__) + +#define BASE_DIR "testdir" + +#define TOPOLOGY_CONFIG "test_transport_simple_send_topo.conf" + +#define TIMEOUT GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS, 600) + +#define MAX_RECEIVED 1000 + +#define MESSAGE_SIZE 65000 + +static struct GNUNET_TESTING_Command block_script; + +static struct GNUNET_TESTING_Command connect_peers; + +static struct GNUNET_TESTING_Command local_prepared; + +static struct GNUNET_TESTING_Command start_peer; + +static struct GNUNET_TESTING_Interpreter *is; + +static struct GNUNET_CONTAINER_MultiPeerMap *senders; + +struct Sender +{ + /** + * Number of received messages from sender. + */ + unsigned long long num_received; + + /** + * Sample mean time the message traveled. + */ + struct GNUNET_TIME_Relative mean_time; + + /** + * Time the first message was send. + */ + struct GNUNET_TIME_Absolute time_first; +}; + +/** + * Function called to check a message of type GNUNET_TRANSPORT_TESTING_SIMPLE_MTYPE being + * received. + * + */ +static int +check_test (void *cls, + const struct + GNUNET_TRANSPORT_TESTING_PerformanceTestMessage *message) +{ + return GNUNET_OK; +} + + +struct GNUNET_TESTING_BarrierList* +get_waiting_for_barriers () +{ + struct GNUNET_TESTING_BarrierList*barriers; + struct GNUNET_TESTING_BarrierListEntry *ble; + + barriers = GNUNET_new (struct GNUNET_TESTING_BarrierList); + ble = GNUNET_new (struct GNUNET_TESTING_BarrierListEntry); + ble->barrier_name = "ready-to-connect"; + ble->expected_reaches = 1; + GNUNET_CONTAINER_DLL_insert (barriers->head, + barriers->tail, + ble); + + ble = GNUNET_new (struct GNUNET_TESTING_BarrierListEntry); + ble->barrier_name = "test-case-finished"; + ble->expected_reaches = 1; + GNUNET_CONTAINER_DLL_insert (barriers->head, + barriers->tail, + ble); + return barriers; +} + + +/** + * Callback to set the flag indicating all peers started. Will be called via the plugin api. + * + */ +static void +all_peers_started () +{ +} + + +/** + * Function called with the final result of the test. + * + * @param cls the `struct MainParams` + * @param rv #GNUNET_OK if the test passed + */ +static void +handle_result (void *cls, + enum GNUNET_GenericReturnValue rv) +{ + struct TestState *ts = cls; + + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Local test exits with status %d\n", + rv); + + ts->finished_cb (rv); + GNUNET_free (ts->testdir); + GNUNET_free (ts->cfgname); + GNUNET_TESTING_free_topology (ts->topology); + GNUNET_free (ts); +} + + +/** + * Callback from start peer cmd for signaling a peer got connected. + * + * +static void * +notify_connect (struct GNUNET_TESTING_Interpreter *is, + const struct GNUNET_PeerIdentity *peer) +{ + const struct ConnectPeersState *cps; + const struct GNUNET_TESTING_Command *cmd; + + cmd = GNUNET_TESTING_interpreter_lookup_command (is, + "connect-peers"); + GNUNET_TRANSPORT_get_trait_connect_peer_state (cmd, + &cps); + void *ret = NULL; + + cps->notify_connect (is, + peer); + return ret; + }*/ + + +/** + * Callback to set the flag indicating all peers are prepared to finish. Will be called via the plugin api. + */ +static void +all_local_tests_prepared () +{ + const struct GNUNET_TESTING_LocalPreparedState *lfs; + + GNUNET_TESTING_get_trait_local_prepared_state (&local_prepared, + &lfs); + GNUNET_assert (NULL != &lfs->ac); + if (NULL == lfs->ac.cont) + GNUNET_TESTING_async_fail ((struct GNUNET_TESTING_AsyncContext *) &lfs->ac); + else + GNUNET_TESTING_async_finish ((struct + GNUNET_TESTING_AsyncContext *) &lfs->ac); +} + + +static void +child_completed_callback (void *cls, + enum GNUNET_OS_ProcessStatusType type, + long unsigned int exit_code) +{ + +} + + +/** + * Function called to check a message being + * received. + * + */ +static int +check_encrypted (void *cls, struct GNUNET_MessageHeader *header) +{ + return GNUNET_OK; +} + + +static void +core_receive_continue (struct GNUNET_PeerIdentity *peer) +{ + const struct GNUNET_TESTING_StartPeerState *sps; + + GNUNET_TRANSPORT_get_trait_state (&start_peer, + &sps); + + LOG (GNUNET_ERROR_TYPE_DEBUG, + "Executing core receive continue\n"); + + GNUNET_TRANSPORT_core_receive_continue (sps->th, peer); +} + + +/*static void +handle_core (void *cls, struct GNUNET_MessageHeader *header) +{ + struct GNUNET_PeerIdentity *peer = cls; + + core_receive_continue (peer); + }*/ + + +/** + * Function called to handle a message being received. + * + */ +static void +handle_encrypted (void *cls, struct GNUNET_MessageHeader *header) +{ + struct GNUNET_PeerIdentity *peer = cls; + + core_receive_continue (peer); +} + + +static void +handle_ephemeral_key (void *cls, struct GNUNET_MessageHeader *header) +{ + struct GNUNET_PeerIdentity *peer = cls; + + core_receive_continue (peer); +} + + +static void +handle_ping (void *cls, struct GNUNET_MessageHeader *header) +{ + struct GNUNET_PeerIdentity *peer = cls; + + core_receive_continue (peer); +} + + +static void +handle_pong (void *cls, struct GNUNET_MessageHeader *header) +{ + struct GNUNET_PeerIdentity *peer = cls; + + core_receive_continue (peer); +} + +/** + * Function to start a local test case. + * + * @param write_message Callback to send a message to the master loop. + * @param router_ip Global address of the network namespace. + * @param node_ip The IP address of the node. + * @param m The number of the node in a network namespace. + * @param n The number of the network namespace. + * @param local_m The number of nodes in a network namespace. + * @param topology_data A file name for the file containing the topology configuration, or a string containing + * the topology configuration. + * @param read_file If read_file is GNUNET_YES this string is the filename for the topology configuration, + * if read_file is GNUNET_NO the string contains the topology configuration. + * @param finish_cb Callback function which writes a message from the helper process running on a netjail + * node to the master process * signaling that the test case running on the netjail node finished. + * @return Returns the struct GNUNET_TESTING_Interpreter of the command loop running on this netjail node. + */ +static struct GNUNET_TESTING_Interpreter * +start_testcase (GNUNET_TESTING_cmd_helper_write_cb write_message, + const char *router_ip, + const char *node_ip, + const char *m, + const char *n, + const char *local_m, + const char *topology_data, + unsigned int *read_file, + GNUNET_TESTING_cmd_helper_finish_cb finished_cb) +{ + + unsigned int n_int; + unsigned int m_int; + unsigned int local_m_int; + unsigned int num; + struct TestState *ts = GNUNET_new (struct TestState); + struct GNUNET_TESTING_NetjailTopology *topology; + unsigned int sscanf_ret = 0; + char **argv = NULL; + int argc = 0; + + ts->finished_cb = finished_cb; + LOG (GNUNET_ERROR_TYPE_ERROR, + "n %s m %s\n", + n, + m); + + if (GNUNET_YES == *read_file) + { + LOG (GNUNET_ERROR_TYPE_DEBUG, + "read from file\n"); + topology = GNUNET_TESTING_get_topo_from_file (topology_data); + } + else + topology = GNUNET_TESTING_get_topo_from_string (topology_data); + + ts->topology = topology; + + errno = 0; + sscanf_ret = sscanf (m, "%u", &m_int); + if (errno != 0) + { + GNUNET_log_strerror (GNUNET_ERROR_TYPE_ERROR, "sscanf"); + } + GNUNET_assert (0 < sscanf_ret); + errno = 0; + sscanf_ret = sscanf (n, "%u", &n_int); + if (errno != 0) + { + GNUNET_log_strerror (GNUNET_ERROR_TYPE_ERROR, "sscanf"); + } + GNUNET_assert (0 < sscanf_ret); + errno = 0; + sscanf_ret = sscanf (local_m, "%u", &local_m_int); + if (errno != 0) + { + GNUNET_log_strerror (GNUNET_ERROR_TYPE_ERROR, "sscanf"); + } + GNUNET_assert (0 < sscanf_ret); + + if (0 == n_int) + num = m_int; + else + num = (n_int - 1) * local_m_int + m_int + topology->nodes_x; + + block_script = GNUNET_TESTING_cmd_block_until_external_trigger ( + "block-script"); + connect_peers = GNUNET_TRANSPORT_cmd_connect_peers ("connect-peers", + "start-peer", + "system-create", + num, + topology, + 0, + GNUNET_NO); + local_prepared = GNUNET_TESTING_cmd_local_test_prepared ( + "local-test-prepared", + write_message); + + + GNUNET_asprintf (&ts->cfgname, + "test_transport_api2_tcp_node1.conf"); + + LOG (GNUNET_ERROR_TYPE_DEBUG, + "plugin cfgname: %s\n", + ts->cfgname); + + LOG (GNUNET_ERROR_TYPE_DEBUG, + "node ip: %s\n", + node_ip); + + GNUNET_asprintf (&ts->testdir, + "%s%s%s", + BASE_DIR, + m, + n); + + /*struct GNUNET_MQ_MessageHandler handlers[] = { + GNUNET_MQ_hd_fixed_size (ephemeral_key, + GNUNET_MESSAGE_TYPE_CORE_EPHEMERAL_KEY, + struct EphemeralKeyMessage, + NULL), + GNUNET_MQ_hd_fixed_size (ping, + GNUNET_MESSAGE_TYPE_CORE_PING, + struct PingMessage, + NULL), + GNUNET_MQ_hd_fixed_size (pong, + GNUNET_MESSAGE_TYPE_CORE_PONG, + struct PongMessage, + NULL), + GNUNET_MQ_handler_end () + };*/ + + start_peer = GNUNET_TESTING_cmd_start_peer ("start-peer", + "system-create", + num, + node_ip, + ts->cfgname, + GNUNET_NO); + + struct GNUNET_TESTING_Command commands[] = { + GNUNET_TESTING_cmd_system_create ("system-create", + ts->testdir), + start_peer, + GNUNET_TESTING_cmd_barrier_reached ("ready-to-connect-reached", + "ready-to-connect", + GNUNET_NO, + num, + GNUNET_NO, + write_message), + connect_peers, + GNUNET_TESTING_cmd_exec_bash_script ("script", + "block.sh", + argv, + argc, + &child_completed_callback), + block_script, + GNUNET_TESTING_cmd_barrier_reached ("test-case-finished-reached", + "test-case-finished", + GNUNET_NO, + num, + GNUNET_NO, + write_message), + GNUNET_TRANSPORT_cmd_stop_peer ("stop-peer", + "start-peer"), + GNUNET_TESTING_cmd_system_destroy ("system-destroy", + "system-create"), + GNUNET_TESTING_cmd_end () + }; + + ts->write_message = write_message; + + is = GNUNET_TESTING_run (commands, + TIMEOUT, + &handle_result, + ts); + return is; +} + + +/** + * Entry point for the plugin. + * + * @param cls NULL + * @return the exported block API + */ +void * +libgnunet_test_transport_plugin_cmd_just_run_init (void *cls) +{ + struct GNUNET_TESTING_PluginFunctions *api; + + GNUNET_log_setup ("simple-send", + "DEBUG", + NULL); + + api = GNUNET_new (struct GNUNET_TESTING_PluginFunctions); + api->start_testcase = &start_testcase; + api->get_waiting_for_barriers = get_waiting_for_barriers; + return api; +} + + +/** + * Exit point from the plugin. + * + * @param cls the return value from #libgnunet_test_transport_plugin_just_run_init + * @return NULL + */ +void * +libgnunet_test_transport_plugin_cmd_just_run_done (void *cls) +{ + struct GNUNET_TESTING_PluginFunctions *api = cls; + + GNUNET_free (api); + return NULL; +} + + +/* end of plugin_cmd_simple_send.c */ diff --git a/src/service/transport/test_transport_plugin_cmd_nat_upnp.c b/src/service/transport/test_transport_plugin_cmd_nat_upnp.c new file mode 100644 index 000000000..9d7c5d856 --- /dev/null +++ b/src/service/transport/test_transport_plugin_cmd_nat_upnp.c @@ -0,0 +1,368 @@ +/* + This file is part of GNUnet + Copyright (C) 2021 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 . + + SPDX-License-Identifier: AGPL3.0-or-later + */ + +/** + * @file testbed/plugin_cmd_simple_send.c + * @brief a plugin to provide the API for running test cases. + * @author t3sserakt + */ +#include "platform.h" +#include "gnunet_testing_barrier.h" +#include "gnunet_testing_netjail_lib.h" +#include "gnunet_util_lib.h" +#include "gnunet_transport_application_service.h" +#include "transport-testing2.h" +#include "transport-testing-cmds.h" +#include "gnunet_testing_barrier.h" + +/** + * Generic logging shortcut + */ +#define LOG(kind, ...) GNUNET_log (kind, __VA_ARGS__) + +#define BASE_DIR "testdir" + +#define TOPOLOGY_CONFIG "test_transport_simple_send_topo.conf" + +#define TIMEOUT GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS, 600) + +static struct GNUNET_TESTING_Command block_send; + +static struct GNUNET_TESTING_Command block_receive; + +static struct GNUNET_TESTING_Command connect_peers; + +static struct GNUNET_TESTING_Command local_prepared; + +static struct GNUNET_TESTING_Interpreter *is; + +/** + * Function called to check a message of type GNUNET_TRANSPORT_TESTING_SIMPLE_MTYPE being + * received. + * + */ +static int +check_test (void *cls, + const struct GNUNET_TRANSPORT_TESTING_TestMessage *message) +{ + return GNUNET_OK; +} + + +/** + * Function called to handle a message of type GNUNET_TRANSPORT_TESTING_SIMPLE_MTYPE + * being received. + * + */ +static void +handle_test (void *cls, + const struct GNUNET_TRANSPORT_TESTING_TestMessage *message) +{ + struct GNUNET_TESTING_AsyncContext *ac; + + GNUNET_TESTING_get_trait_async_context (&block_receive, + &ac); + GNUNET_assert (NULL != ac); + if (NULL == ac->cont) + GNUNET_TESTING_async_fail ((struct GNUNET_TESTING_AsyncContext *) ac); + else + GNUNET_TESTING_async_finish ((struct GNUNET_TESTING_AsyncContext *) ac); +} + + +struct GNUNET_TESTING_BarrierList* +get_waiting_for_barriers () +{ + struct GNUNET_TESTING_BarrierList*barriers; + struct GNUNET_TESTING_BarrierListEntry *ble; + + barriers = GNUNET_new (struct GNUNET_TESTING_BarrierList); + ble = GNUNET_new (struct GNUNET_TESTING_BarrierListEntry); + ble->barrier_name = "ready-to-connect"; + ble->expected_reaches = 1; + GNUNET_CONTAINER_DLL_insert (barriers->head, + barriers->tail, + ble); + + ble = GNUNET_new (struct GNUNET_TESTING_BarrierListEntry); + ble->barrier_name = "test-case-finished"; + ble->expected_reaches = 1; + GNUNET_CONTAINER_DLL_insert (barriers->head, + barriers->tail, + ble); + return barriers; +} + + +/** + * Function called with the final result of the test. + * + * @param cls the `struct MainParams` + * @param rv #GNUNET_OK if the test passed + */ +static void +handle_result (void *cls, + enum GNUNET_GenericReturnValue rv) +{ + struct TestState *ts = cls; + + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Local test exits with status %d\n", + rv); + + ts->finished_cb (rv); + GNUNET_free (ts->testdir); + GNUNET_free (ts->cfgname); + GNUNET_TESTING_free_topology (ts->topology); + GNUNET_free (ts); +} + + +/** + * Callback from start peer cmd for signaling a peer got connected. + * + */ +static void * +notify_connect (struct GNUNET_TESTING_Interpreter *is, + const struct GNUNET_PeerIdentity *peer) +{ + const struct ConnectPeersState *cps; + const struct GNUNET_TESTING_Command *cmd; + + cmd = GNUNET_TESTING_interpreter_lookup_command (is, + "connect-peers"); + GNUNET_TRANSPORT_get_trait_connect_peer_state (cmd, + &cps); + void *ret = NULL; + + cps->notify_connect (is, + peer); + return ret; +} + + +/** + * Function to start a local test case. + * + * @param write_message Callback to send a message to the master loop. + * @param router_ip Global address of the network namespace. + * @param node_ip The IP address of the node. + * @param m The number of the node in a network namespace. + * @param n The number of the network namespace. + * @param local_m The number of nodes in a network namespace. + * @param topology_data A file name for the file containing the topology configuration, or a string containing + * the topology configuration. + * @param read_file If read_file is GNUNET_YES this string is the filename for the topology configuration, + * if read_file is GNUNET_NO the string contains the topology configuration. + * @param finish_cb Callback function which writes a message from the helper process running on a netjail + * node to the master process * signaling that the test case running on the netjail node finished. + * @return Returns the struct GNUNET_TESTING_Interpreter of the command loop running on this netjail node. + */ +static struct GNUNET_TESTING_Interpreter * +start_testcase (GNUNET_TESTING_cmd_helper_write_cb write_message, + const char *router_ip, + const char *node_ip, + const char *m, + const char *n, + const char *local_m, + const char *topology_data, + unsigned int *read_file, + GNUNET_TESTING_cmd_helper_finish_cb finished_cb) +{ + + unsigned int n_int; + unsigned int m_int; + unsigned int local_m_int; + unsigned int num; + struct TestState *ts = GNUNET_new (struct TestState); + struct GNUNET_TESTING_NetjailTopology *topology; + unsigned int sscanf_ret = 0; + + ts->finished_cb = finished_cb; + LOG (GNUNET_ERROR_TYPE_ERROR, + "n %s m %s\n", + n, + m); + + if (GNUNET_YES == *read_file) + { + LOG (GNUNET_ERROR_TYPE_DEBUG, + "read from file\n"); + topology = GNUNET_TESTING_get_topo_from_file (topology_data); + } + else + topology = GNUNET_TESTING_get_topo_from_string (topology_data); + + ts->topology = topology; + + errno = 0; + sscanf_ret = sscanf (m, "%u", &m_int); + if (errno != 0) + { + GNUNET_log_strerror (GNUNET_ERROR_TYPE_ERROR, "sscanf"); + } + GNUNET_assert (0 < sscanf_ret); + errno = 0; + sscanf_ret = sscanf (n, "%u", &n_int); + if (errno != 0) + { + GNUNET_log_strerror (GNUNET_ERROR_TYPE_ERROR, "sscanf"); + } + GNUNET_assert (0 < sscanf_ret); + errno = 0; + sscanf_ret = sscanf (local_m, "%u", &local_m_int); + if (errno != 0) + { + GNUNET_log_strerror (GNUNET_ERROR_TYPE_ERROR, "sscanf"); + } + GNUNET_assert (0 < sscanf_ret); + + if (0 == n_int) + num = m_int; + else + num = (n_int - 1) * local_m_int + m_int + topology->nodes_x; + + block_send = GNUNET_TESTING_cmd_block_until_external_trigger ( + "block"); + block_receive = GNUNET_TESTING_cmd_block_until_external_trigger ( + "block-receive"); + connect_peers = GNUNET_TRANSPORT_cmd_connect_peers ("connect-peers", + "start-peer", + "system-create", + num, + topology, + 0, + GNUNET_YES); + local_prepared = GNUNET_TESTING_cmd_local_test_prepared ( + "local-test-prepared", + write_message); + + + GNUNET_asprintf (&ts->cfgname, + "test_transport_api2_tcp_node1.conf"); + + LOG (GNUNET_ERROR_TYPE_DEBUG, + "plugin cfgname: %s\n", + ts->cfgname); + + LOG (GNUNET_ERROR_TYPE_DEBUG, + "node ip: %s\n", + node_ip); + + GNUNET_asprintf (&ts->testdir, + "%s%s%s", + BASE_DIR, + m, + n); + + struct GNUNET_MQ_MessageHandler handlers[] = { + GNUNET_MQ_hd_var_size (test, + GNUNET_TRANSPORT_TESTING_SIMPLE_MTYPE, + struct GNUNET_TRANSPORT_TESTING_TestMessage, + ts), + GNUNET_MQ_handler_end () + }; + + struct GNUNET_TESTING_Command commands[] = { + GNUNET_TESTING_cmd_system_create ("system-create", + ts->testdir), + GNUNET_TRANSPORT_cmd_start_peer ("start-peer", + "system-create", + num, + node_ip, + handlers, + ts->cfgname, + notify_connect, + GNUNET_NO), + GNUNET_TESTING_cmd_barrier_reached ("ready-to-connect-reached", + "ready-to-connect", + GNUNET_NO, + num, + GNUNET_NO, + write_message), + connect_peers, + GNUNET_TRANSPORT_cmd_send_simple ("send-simple", + "start-peer", + "system-create", + num, + topology), + block_receive, + GNUNET_TESTING_cmd_barrier_reached ("test-case-finished-reached", + "test-case-finished", + GNUNET_NO, + num, + GNUNET_NO, + write_message), + GNUNET_TRANSPORT_cmd_stop_peer ("stop-peer", + "start-peer"), + GNUNET_TESTING_cmd_system_destroy ("system-destroy", + "system-create"), + GNUNET_TESTING_cmd_end () + }; + + ts->write_message = write_message; + + is = GNUNET_TESTING_run (commands, + TIMEOUT, + &handle_result, + ts); + return is; +} + + +/** + * Entry point for the plugin. + * + * @param cls NULL + * @return the exported block API + */ +void * +libgnunet_test_transport_plugin_cmd_nat_upnp_init (void *cls) +{ + struct GNUNET_TESTING_PluginFunctions *api; + + GNUNET_log_setup ("simple-send", + "DEBUG", + NULL); + + api = GNUNET_new (struct GNUNET_TESTING_PluginFunctions); + api->start_testcase = &start_testcase; + api->get_waiting_for_barriers = get_waiting_for_barriers; + return api; +} + + +/** + * Exit point from the plugin. + * + * @param cls the return value from #libgnunet_test_transport_plugin_nat_upnp_init + * @return NULL + */ +void * +libgnunet_test_transport_plugin_cmd_nat_upnp_done (void *cls) +{ + struct GNUNET_TESTING_PluginFunctions *api = cls; + + GNUNET_free (api); + return NULL; +} + + +/* end of plugin_cmd_simple_send.c */ diff --git a/src/service/transport/test_transport_plugin_cmd_simple_send.c b/src/service/transport/test_transport_plugin_cmd_simple_send.c new file mode 100644 index 000000000..c6d75cce4 --- /dev/null +++ b/src/service/transport/test_transport_plugin_cmd_simple_send.c @@ -0,0 +1,377 @@ +/* + This file is part of GNUnet + Copyright (C) 2021 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 . + + SPDX-License-Identifier: AGPL3.0-or-later + */ + +/** + * @file testbed/plugin_cmd_simple_send.c + * @brief a plugin to provide the API for running test cases. + * @author t3sserakt + */ +#include "platform.h" +#include "gnunet_testing_barrier.h" +#include "gnunet_testing_netjail_lib.h" +#include "gnunet_util_lib.h" +#include "gnunet_transport_application_service.h" +#include "transport-testing2.h" +#include "transport-testing-cmds.h" +#include "gnunet_testing_barrier.h" + +/** + * Generic logging shortcut + */ +#define LOG(kind, ...) GNUNET_log (kind, __VA_ARGS__) + +#define BASE_DIR "testdir" + +#define TOPOLOGY_CONFIG "test_transport_simple_send_topo.conf" + +#define TIMEOUT GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS, 600) + +static struct GNUNET_TESTING_Command block_send; + +static struct GNUNET_TESTING_Command block_receive; + +static struct GNUNET_TESTING_Command connect_peers; + +static struct GNUNET_TESTING_Command local_prepared; + +static struct GNUNET_TESTING_Interpreter *is; + +/** + * Function called to check a message of type GNUNET_TRANSPORT_TESTING_SIMPLE_MTYPE being + * received. + * + */ +static int +check_test (void *cls, + const struct GNUNET_TRANSPORT_TESTING_TestMessage *message) +{ + return GNUNET_OK; +} + + +/** + * Function called to handle a message of type GNUNET_TRANSPORT_TESTING_SIMPLE_MTYPE + * being received. + * + */ +static void +handle_test (void *cls, + const struct GNUNET_TRANSPORT_TESTING_TestMessage *message) +{ + struct GNUNET_TESTING_AsyncContext *ac; + + GNUNET_TESTING_get_trait_async_context (&block_receive, + &ac); + GNUNET_assert (NULL != ac); + if (NULL == ac->cont) + GNUNET_TESTING_async_fail ((struct GNUNET_TESTING_AsyncContext *) ac); + else + GNUNET_TESTING_async_finish ((struct GNUNET_TESTING_AsyncContext *) ac); +} + + +struct GNUNET_TESTING_BarrierList * +get_waiting_for_barriers () +{ + // No Barrier + return GNUNET_new (struct GNUNET_TESTING_BarrierList); +} + + +/** + * Callback to set the flag indicating all peers started. Will be called via the plugin api. + * + */ +static void +all_peers_started () +{ + struct GNUNET_TESTING_AsyncContext *ac; + + GNUNET_TESTING_get_trait_async_context (&block_send, + &ac); + GNUNET_assert (NULL != ac); + if (NULL == ac->cont) + GNUNET_TESTING_async_fail ((struct GNUNET_TESTING_AsyncContext *) ac); + else + GNUNET_TESTING_async_finish ((struct GNUNET_TESTING_AsyncContext *) ac); +} + + +/** + * Function called with the final result of the test. + * + * @param cls the `struct MainParams` + * @param rv #GNUNET_OK if the test passed + */ +static void +handle_result (void *cls, + enum GNUNET_GenericReturnValue rv) +{ + struct TestState *ts = cls; + + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Local test exits with status %d\n", + rv); + + ts->finished_cb (rv); + GNUNET_free (ts->testdir); + GNUNET_free (ts->cfgname); + GNUNET_TESTING_free_topology (ts->topology); + GNUNET_free (ts); +} + + +/** + * Callback from start peer cmd for signaling a peer got connected. + * + */ +static void * +notify_connect (struct GNUNET_TESTING_Interpreter *is, + const struct GNUNET_PeerIdentity *peer) +{ + const struct ConnectPeersState *cps; + const struct GNUNET_TESTING_Command *cmd; + + cmd = GNUNET_TESTING_interpreter_lookup_command (is, + "connect-peers"); + GNUNET_TRANSPORT_get_trait_connect_peer_state (cmd, + &cps); + void *ret = NULL; + + cps->notify_connect (is, + peer); + return ret; +} + + +/** + * Callback to set the flag indicating all peers are prepared to finish. Will be called via the plugin api. + */ +static void +all_local_tests_prepared () +{ + const struct GNUNET_TESTING_LocalPreparedState *lfs; + + GNUNET_TESTING_get_trait_local_prepared_state (&local_prepared, + &lfs); + GNUNET_assert (NULL != &lfs->ac); + if (NULL == lfs->ac.cont) + GNUNET_TESTING_async_fail ((struct GNUNET_TESTING_AsyncContext *) &lfs->ac); + else + GNUNET_TESTING_async_finish ((struct + GNUNET_TESTING_AsyncContext *) &lfs->ac); +} + + +/** + * Function to start a local test case. + * + * @param write_message Callback to send a message to the master loop. + * @param router_ip Global address of the network namespace. + * @param node_ip The IP address of the node. + * @param m The number of the node in a network namespace. + * @param n The number of the network namespace. + * @param local_m The number of nodes in a network namespace. + */ +static struct GNUNET_TESTING_Interpreter * +start_testcase (GNUNET_TESTING_cmd_helper_write_cb write_message, + const char *router_ip, + const char *node_ip, + const char *m, + const char *n, + const char *local_m, + const char *topology_data, + unsigned int *read_file, + GNUNET_TESTING_cmd_helper_finish_cb finished_cb) +{ + + unsigned int n_int; + unsigned int m_int; + unsigned int local_m_int; + unsigned int num; + struct TestState *ts = GNUNET_new (struct TestState); + struct GNUNET_TESTING_NetjailTopology *topology; + unsigned int sscanf_ret = 0; + + ts->finished_cb = finished_cb; + LOG (GNUNET_ERROR_TYPE_ERROR, + "n %s m %s\n", + n, + m); + + if (GNUNET_YES == *read_file) + { + LOG (GNUNET_ERROR_TYPE_DEBUG, + "read from file\n"); + topology = GNUNET_TESTING_get_topo_from_file (topology_data); + } + else + topology = GNUNET_TESTING_get_topo_from_string (topology_data); + + ts->topology = topology; + + errno = 0; + sscanf_ret = sscanf (m, "%u", &m_int); + if (errno != 0) + { + GNUNET_log_strerror (GNUNET_ERROR_TYPE_ERROR, "sscanf"); + } + GNUNET_assert (0 < sscanf_ret); + errno = 0; + sscanf_ret = sscanf (n, "%u", &n_int); + if (errno != 0) + { + GNUNET_log_strerror (GNUNET_ERROR_TYPE_ERROR, "sscanf"); + } + GNUNET_assert (0 < sscanf_ret); + errno = 0; + sscanf_ret = sscanf (local_m, "%u", &local_m_int); + if (errno != 0) + { + GNUNET_log_strerror (GNUNET_ERROR_TYPE_ERROR, "sscanf"); + } + GNUNET_assert (0 < sscanf_ret); + + if (0 == n_int) + num = m_int; + else + num = (n_int - 1) * local_m_int + m_int + topology->nodes_x; + + block_send = GNUNET_TESTING_cmd_block_until_external_trigger ( + "block"); + block_receive = GNUNET_TESTING_cmd_block_until_external_trigger ( + "block-receive"); + connect_peers = GNUNET_TRANSPORT_cmd_connect_peers ("connect-peers", + "start-peer", + "system-create", + num, + topology, + 0, + GNUNET_YES); + local_prepared = GNUNET_TESTING_cmd_local_test_prepared ( + "local-test-prepared", + write_message); + + + GNUNET_asprintf (&ts->cfgname, + "test_transport_api2_tcp_node1.conf"); + + LOG (GNUNET_ERROR_TYPE_DEBUG, + "plugin cfgname: %s\n", + ts->cfgname); + + LOG (GNUNET_ERROR_TYPE_DEBUG, + "node ip: %s\n", + node_ip); + + GNUNET_asprintf (&ts->testdir, + "%s%s%s", + BASE_DIR, + m, + n); + + struct GNUNET_MQ_MessageHandler handlers[] = { + GNUNET_MQ_hd_var_size (test, + GNUNET_TRANSPORT_TESTING_SIMPLE_MTYPE, + struct GNUNET_TRANSPORT_TESTING_TestMessage, + ts), + GNUNET_MQ_handler_end () + }; + + struct GNUNET_TESTING_Command commands[] = { + GNUNET_TESTING_cmd_system_create ("system-create", + ts->testdir), + GNUNET_TRANSPORT_cmd_start_peer ("start-peer", + "system-create", + num, + node_ip, + handlers, + ts->cfgname, + notify_connect, + GNUNET_NO), + GNUNET_TESTING_cmd_send_peer_ready ("send-peer-ready", + write_message), + block_send, + connect_peers, + GNUNET_TRANSPORT_cmd_send_simple ("send-simple", + "start-peer", + "system-create", + num, + topology), + block_receive, + local_prepared, + GNUNET_TRANSPORT_cmd_stop_peer ("stop-peer", + "start-peer"), + GNUNET_TESTING_cmd_system_destroy ("system-destroy", + "system-create"), + GNUNET_TESTING_cmd_end () + }; + + ts->write_message = write_message; + + is = GNUNET_TESTING_run (commands, + TIMEOUT, + &handle_result, + ts); + return is; +} + + +/** + * Entry point for the plugin. + * + * @param cls NULL + * @return the exported block API + */ +void * +libgnunet_test_transport_plugin_cmd_simple_send_init (void *cls) +{ + struct GNUNET_TESTING_PluginFunctions *api; + + GNUNET_log_setup ("simple-send", + "DEBUG", + NULL); + + api = GNUNET_new (struct GNUNET_TESTING_PluginFunctions); + api->start_testcase = &start_testcase; + api->all_peers_started = &all_peers_started; + api->all_local_tests_prepared = all_local_tests_prepared; + api->get_waiting_for_barriers = get_waiting_for_barriers; + return api; +} + + +/** + * Exit point from the plugin. + * + * @param cls the return value from #libgnunet_test_transport_plugin_block_test_init + * @return NULL + */ +void * +libgnunet_test_transport_plugin_cmd_simple_send_done (void *cls) +{ + struct GNUNET_TESTING_PluginFunctions *api = cls; + + GNUNET_free (api); + return NULL; +} + + +/* end of plugin_cmd_simple_send.c */ diff --git a/src/service/transport/test_transport_plugin_cmd_simple_send_broadcast.c b/src/service/transport/test_transport_plugin_cmd_simple_send_broadcast.c new file mode 100644 index 000000000..d2870ab34 --- /dev/null +++ b/src/service/transport/test_transport_plugin_cmd_simple_send_broadcast.c @@ -0,0 +1,402 @@ +/* + This file is part of GNUnet + Copyright (C) 2021 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 . + + SPDX-License-Identifier: AGPL3.0-or-later + */ + +/** + * @file testbed/plugin_cmd_simple_send_broadcast.c + * @brief a plugin to provide the API for running test cases. + * @author t3sserakt + */ +#include "platform.h" +#include "gnunet_testing_barrier.h" +#include "gnunet_testing_netjail_lib.h" +#include "gnunet_util_lib.h" +#include "gnunet_transport_application_service.h" +#include "transport-testing2.h" +#include "transport-testing-cmds.h" +#include "gnunet_testing_barrier.h" + +/** + * Generic logging shortcut + */ +#define LOG(kind, ...) GNUNET_log (kind, __VA_ARGS__) + +#define BASE_DIR "testdir" + +#define TOPOLOGY_CONFIG "test_transport_simple_send_topo.conf" + +#define TIMEOUT GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS, 600) + +static struct GNUNET_TESTING_Command block_send; + +static struct GNUNET_TESTING_Command block_receive; + +static struct GNUNET_TESTING_Command connect_peers; + +static struct GNUNET_TESTING_Command local_prepared; + +static struct GNUNET_TESTING_Interpreter *is; + +/** + * Function called to check a message of type GNUNET_TRANSPORT_TESTING_SIMPLE_MTYPE being + * received. + * + */ +static int +check_test (void *cls, + const struct GNUNET_TRANSPORT_TESTING_TestMessage *message) +{ + return GNUNET_OK; +} + + +/** + * Function called to handle a message of type GNUNET_TRANSPORT_TESTING_SIMPLE_MTYPE + * being received. + * + */ +static void +handle_test (void *cls, + const struct GNUNET_TRANSPORT_TESTING_TestMessage *message) +{ + struct GNUNET_TESTING_AsyncContext *ac; + + GNUNET_TESTING_get_trait_async_context (&block_receive, + &ac); + GNUNET_assert (NULL != ac); + if ((GNUNET_NO == ac->finished) && (NULL == ac->cont)) + GNUNET_TESTING_async_fail ((struct GNUNET_TESTING_AsyncContext *) ac); + else if (GNUNET_NO == ac->finished) + GNUNET_TESTING_async_finish ((struct GNUNET_TESTING_AsyncContext *) ac); +} + + +struct GNUNET_TESTING_BarrierList * +get_waiting_for_barriers () +{ + // No Barrier + return GNUNET_new (struct GNUNET_TESTING_BarrierList); +} + + +/** + * Callback to set the flag indicating all peers started. Will be called via the plugin api. + * + */ +static void +all_peers_started () +{ + struct GNUNET_TESTING_AsyncContext *ac; + + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Received message\n"); + GNUNET_TESTING_get_trait_async_context (&block_send, + &ac); + GNUNET_assert (NULL != ac); + if (NULL == ac->cont) + GNUNET_TESTING_async_fail ((struct GNUNET_TESTING_AsyncContext *) ac); + else + GNUNET_TESTING_async_finish ((struct GNUNET_TESTING_AsyncContext *) ac); +} + + +/** + * Function called with the final result of the test. + * + * @param cls the `struct MainParams` + * @param rv #GNUNET_OK if the test passed + */ +static void +handle_result (void *cls, + enum GNUNET_GenericReturnValue rv) +{ + struct TestState *ts = cls; + + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Local test exits with status %d\n", + rv); + ts->finished_cb (rv); + GNUNET_free (ts->testdir); + GNUNET_free (ts->cfgname); + GNUNET_TESTING_free_topology (ts->topology); + GNUNET_free (ts); +} + + +/** + * Callback from start peer cmd for signaling a peer got connected. + * + */ +static void * +notify_connect (struct GNUNET_TESTING_Interpreter *is, + const struct GNUNET_PeerIdentity *peer) +{ + struct GNUNET_TESTING_AsyncContext *ac; + void *ret = NULL; + const struct GNUNET_TESTING_Command *cmd; + struct GNUNET_TESTING_BlockState *bs; + + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "notify_connect\n"); + + GNUNET_TESTING_get_trait_async_context (&connect_peers, + &ac); + + if (NULL != ac->is) + { + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "notify_connect running\n"); + GNUNET_assert (NULL != ac); + if (NULL == ac->cont) + GNUNET_TESTING_async_fail (ac); + else + GNUNET_TESTING_async_finish (ac); + } + else + { + cmd = GNUNET_TESTING_interpreter_lookup_future_command (is, + "connect-peers"); + + LOG (GNUNET_ERROR_TYPE_DEBUG, + "block state %s\n", + cmd->label); + GNUNET_TESTING_get_trait_block_state ( + cmd, + &bs); + + LOG (GNUNET_ERROR_TYPE_DEBUG, + "block state %u\n", + bs->asynchronous_finish); + bs->asynchronous_finish = GNUNET_YES; + LOG (GNUNET_ERROR_TYPE_DEBUG, + "block state %u\n", + bs->asynchronous_finish); + } + + return ret; +} + + +/** + * Callback to set the flag indicating all peers are prepared to finish. Will be called via the plugin api. + */ +static void +all_local_tests_prepared () +{ + const struct GNUNET_TESTING_LocalPreparedState *lfs; + + GNUNET_TESTING_get_trait_local_prepared_state (&local_prepared, + &lfs); + GNUNET_assert (NULL != &lfs->ac); + if (NULL == lfs->ac.cont) + GNUNET_TESTING_async_fail ((struct GNUNET_TESTING_AsyncContext *) &lfs->ac); + else + GNUNET_TESTING_async_finish ((struct + GNUNET_TESTING_AsyncContext *) &lfs->ac); +} + + +/** + * Function to start a local test case. + * + * @param write_message Callback to send a message to the master loop. + * @param router_ip Global address of the network namespace. + * @param node_ip The IP address of the node. + * @param m The number of the node in a network namespace. + * @param n The number of the network namespace. + * @param local_m The number of nodes in a network namespace. + */ +static struct GNUNET_TESTING_Interpreter * +start_testcase (GNUNET_TESTING_cmd_helper_write_cb write_message, + const char *router_ip, + const char *node_ip, + const char *m, + const char *n, + const char *local_m, + const char *topology_data, + unsigned int *read_file, + GNUNET_TESTING_cmd_helper_finish_cb finished_cb) +{ + unsigned int n_int; + unsigned int m_int; + unsigned int local_m_int; + unsigned int num; + struct TestState *ts = GNUNET_new (struct TestState); + struct GNUNET_TESTING_NetjailTopology *topology; + unsigned int sscanf_ret = 0; + + ts->finished_cb = finished_cb; + LOG (GNUNET_ERROR_TYPE_ERROR, + "n %s m %s\n", + n, + m); + + if (GNUNET_YES == *read_file) + { + LOG (GNUNET_ERROR_TYPE_DEBUG, + "read from file\n"); + topology = GNUNET_TESTING_get_topo_from_file (topology_data); + } + else + topology = GNUNET_TESTING_get_topo_from_string (topology_data); + + ts->topology = topology; + + errno = 0; + sscanf_ret = sscanf (m, "%u", &m_int); + if (errno != 0) + { + GNUNET_log_strerror (GNUNET_ERROR_TYPE_ERROR, "sscanf"); + } + GNUNET_assert (0 < sscanf_ret); + errno = 0; + sscanf_ret = sscanf (n, "%u", &n_int); + if (errno != 0) + { + GNUNET_log_strerror (GNUNET_ERROR_TYPE_ERROR, "sscanf"); + } + GNUNET_assert (0 < sscanf_ret); + errno = 0; + sscanf_ret = sscanf (local_m, "%u", &local_m_int); + if (errno != 0) + { + GNUNET_log_strerror (GNUNET_ERROR_TYPE_ERROR, "sscanf"); + } + GNUNET_assert (0 < sscanf_ret); + + if (0 == n_int) + num = m_int; + else + num = (n_int - 1) * local_m_int + m_int + topology->nodes_x; + + block_send = GNUNET_TESTING_cmd_block_until_external_trigger ("block"); + block_receive = GNUNET_TESTING_cmd_block_until_external_trigger ( + "block-receive"); + connect_peers = GNUNET_TESTING_cmd_block_until_external_trigger ( + "connect-peers"); + local_prepared = GNUNET_TESTING_cmd_local_test_prepared ( + "local-test-prepared", + write_message); + + + GNUNET_asprintf (&ts->cfgname, + "test_transport_api2_tcp_node1.conf"); + + LOG (GNUNET_ERROR_TYPE_DEBUG, + "plugin cfgname: %s\n", + ts->cfgname); + + LOG (GNUNET_ERROR_TYPE_DEBUG, + "node ip: %s\n", + node_ip); + + GNUNET_asprintf (&ts->testdir, + "%s%s%s", + BASE_DIR, + m, + n); + + struct GNUNET_MQ_MessageHandler handlers[] = { + GNUNET_MQ_hd_var_size (test, + GNUNET_TRANSPORT_TESTING_SIMPLE_MTYPE, + struct GNUNET_TRANSPORT_TESTING_TestMessage, + ts), + GNUNET_MQ_handler_end () + }; + + struct GNUNET_TESTING_Command commands[] = { + GNUNET_TESTING_cmd_system_create ("system-create", + ts->testdir), + GNUNET_TRANSPORT_cmd_start_peer ("start-peer", + "system-create", + num, + node_ip, + handlers, + ts->cfgname, + notify_connect, + GNUNET_YES), + GNUNET_TESTING_cmd_send_peer_ready ("send-peer-ready", + write_message), + block_send, + connect_peers, + GNUNET_TRANSPORT_cmd_send_simple ("send-simple", + "start-peer", + "system-create", + num, + topology), + block_receive, + local_prepared, + GNUNET_TRANSPORT_cmd_stop_peer ("stop-peer", + "start-peer"), + GNUNET_TESTING_cmd_system_destroy ("system-destroy", + "system-create"), + GNUNET_TESTING_cmd_end () + }; + + ts->write_message = write_message; + + is = GNUNET_TESTING_run (commands, + TIMEOUT, + &handle_result, + ts); + return is; +} + + +/** + * Entry point for the plugin. + * + * @param cls NULL + * @return the exported block API + */ +void * +libgnunet_test_transport_plugin_cmd_simple_send_broadcast_init (void *cls) +{ + struct GNUNET_TESTING_PluginFunctions *api; + + GNUNET_log_setup ("simple-send", + "DEBUG", + NULL); + + api = GNUNET_new (struct GNUNET_TESTING_PluginFunctions); + api->start_testcase = &start_testcase; + api->all_peers_started = &all_peers_started; + api->all_local_tests_prepared = all_local_tests_prepared; + api->get_waiting_for_barriers = get_waiting_for_barriers; + return api; +} + + +/** + * Exit point from the plugin. + * + * @param cls the return value from #libgnunet_test_transport_plugin_block_test_init + * @return NULL + */ +void * +libgnunet_test_transport_plugin_cmd_simple_send_broadcast_done (void *cls) +{ + struct GNUNET_TESTING_PluginFunctions *api = cls; + + GNUNET_free (api); + return NULL; +} + + +/* end of plugin_cmd_simple_send_broadcast.c */ diff --git a/src/service/transport/test_transport_plugin_cmd_simple_send_dv.c b/src/service/transport/test_transport_plugin_cmd_simple_send_dv.c new file mode 100644 index 000000000..d35672cd9 --- /dev/null +++ b/src/service/transport/test_transport_plugin_cmd_simple_send_dv.c @@ -0,0 +1,434 @@ +/* + This file is part of GNUnet + Copyright (C) 2021 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 . + + SPDX-License-Identifier: AGPL3.0-or-later + */ + +/** + * @file testbed/plugin_cmd_simple_send_broadcast.c + * @brief a plugin to provide the API for running test cases. + * @author t3sserakt + */ +#include "platform.h" +#include "gnunet_testing_barrier.h" +#include "gnunet_testing_netjail_lib.h" +#include "gnunet_util_lib.h" +#include "gnunet_transport_application_service.h" +#include "transport-testing2.h" +#include "transport-testing-cmds.h" +#include "gnunet_testing_barrier.h" + +/** + * Generic logging shortcut + */ +#define LOG(kind, ...) GNUNET_log (kind, __VA_ARGS__) + +#define BASE_DIR "testdir" + +#define TOPOLOGY_CONFIG "test_transport_simple_send_topo.conf" + +#define TIMEOUT GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS, 600) + +/** + * The number of messages received. + */ +static unsigned int number_received; + +static struct GNUNET_TESTING_Command block_send; + +static struct GNUNET_TESTING_Command block_receive; + +static struct GNUNET_TESTING_Command connect_peers; + +static struct GNUNET_TESTING_Command local_prepared; + +static struct GNUNET_TESTING_Command start_peer; + +static struct GNUNET_TESTING_Interpreter *is; + +/** + * Function called to check a message of type GNUNET_TRANSPORT_TESTING_SIMPLE_MTYPE being + * received. + * + */ +static int +check_test (void *cls, + const struct GNUNET_TRANSPORT_TESTING_TestMessage *message) +{ + GNUNET_assert (NULL != cls); + return GNUNET_OK; +} + + +/** + * Function called to handle a message of type GNUNET_TRANSPORT_TESTING_SIMPLE_MTYPE + * being received. + * + */ +static void +handle_test (void *cls, + const struct GNUNET_TRANSPORT_TESTING_TestMessage *message) +{ + struct GNUNET_PeerIdentity *peer = cls; + struct GNUNET_TESTING_AsyncContext *ac_block; + const struct GNUNET_CONTAINER_MultiShortmap *connected_peers_map; + unsigned int connected; + struct GNUNET_TESTING_BlockState *bs; + struct GNUNET_TRANSPORT_CoreHandle *ch; + const struct GNUNET_TESTING_StartPeerState *sps; + + + GNUNET_TRANSPORT_get_trait_state (&start_peer, + &sps); + ch = sps->th; + GNUNET_TRANSPORT_get_trait_connected_peers_map (&start_peer, + &connected_peers_map); + + if (NULL != connected_peers_map) + { + connected = GNUNET_CONTAINER_multishortmap_size ( + connected_peers_map); + + number_received++; + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Received %u test message(s) from %s, %u connected peer(s)\n", + number_received, + GNUNET_i2s (peer), + connected); + + GNUNET_TESTING_get_trait_async_context (&block_receive, + &ac_block); + + if (connected == number_received) + { + if (NULL != ac_block->is) + { + GNUNET_assert (NULL != ac_block); + if (NULL == ac_block->cont) + GNUNET_TESTING_async_fail ((struct + GNUNET_TESTING_AsyncContext *) ac_block); + else + GNUNET_TESTING_async_finish ((struct + GNUNET_TESTING_AsyncContext *) ac_block); + } + else + { + GNUNET_TESTING_get_trait_block_state ( + &block_receive, + &bs); + bs->asynchronous_finish = GNUNET_YES; + } + + } + } + GNUNET_TRANSPORT_core_receive_continue (ch, peer); +} + + +struct GNUNET_TESTING_BarrierList * +get_waiting_for_barriers () +{ + // No Barrier + return GNUNET_new (struct GNUNET_TESTING_BarrierList); +} + + +/** + * Callback to set the flag indicating all peers started. Will be called via the plugin api. + * + */ +static void +all_peers_started () +{ + struct GNUNET_TESTING_AsyncContext *ac; + + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Received message\n"); + GNUNET_TESTING_get_trait_async_context (&block_send, + &ac); + GNUNET_assert (NULL != ac); + if (NULL == ac->cont) + GNUNET_TESTING_async_fail ((struct GNUNET_TESTING_AsyncContext *) ac); + else + GNUNET_TESTING_async_finish ((struct GNUNET_TESTING_AsyncContext *) ac); +} + + +/** + * Function called with the final result of the test. + * + * @param cls the `struct MainParams` + * @param rv #GNUNET_OK if the test passed + */ +static void +handle_result (void *cls, + enum GNUNET_GenericReturnValue rv) +{ + struct TestState *ts = cls; + + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Local test exits with status %d\n", + rv); + ts->finished_cb (rv); + GNUNET_free (ts->testdir); + GNUNET_free (ts->cfgname); + GNUNET_TESTING_free_topology (ts->topology); + GNUNET_free (ts); +} + + +/** + * Callback from start peer cmd for signaling a peer got connected. + * + */ +static void * +notify_connect (struct GNUNET_TESTING_Interpreter *is, + const struct GNUNET_PeerIdentity *peer) +{ + const struct ConnectPeersState *cps; + const struct GNUNET_TESTING_Command *cmd; + + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "notify_connect peer %s\n", + GNUNET_i2s (peer)); + // FIXME: modifying future is a bit unclean, not easy to follow logic; + // might be better to when reaching the future command to look into + // the past... + cmd = GNUNET_TESTING_interpreter_lookup_command_all (is, + "connect-peers"); + // FIXME: check return value! + GNUNET_TRANSPORT_get_trait_connect_peer_state (cmd, + &cps); + cps->notify_connect (is, + peer); + return NULL; +} + + +/** + * Callback to set the flag indicating all peers are prepared to finish. Will be called via the plugin api. + */ +static void +all_local_tests_prepared () +{ + const struct GNUNET_TESTING_LocalPreparedState *lfs; + + GNUNET_TESTING_get_trait_local_prepared_state (&local_prepared, + &lfs); + GNUNET_assert (NULL != &lfs->ac); + if (NULL == lfs->ac.cont) + GNUNET_TESTING_async_fail ((struct GNUNET_TESTING_AsyncContext *) &lfs->ac); + else + GNUNET_TESTING_async_finish ((struct + GNUNET_TESTING_AsyncContext *) &lfs->ac); +} + + +/** + * Function to start a local test case. + * + * @param write_message Callback to send a message to the master loop. + * @param router_ip Global address of the network namespace. + * @param node_ip The IP address of the node. + * @param m The number of the node in a network namespace. + * @param n The number of the network namespace. + * @param local_m The number of nodes in a network namespace. + */ +static struct GNUNET_TESTING_Interpreter * +start_testcase (GNUNET_TESTING_cmd_helper_write_cb write_message, + const char *router_ip, + const char *node_ip, + const char *m, + const char *n, + const char *local_m, + const char *topology_data, + unsigned int *read_file, + GNUNET_TESTING_cmd_helper_finish_cb finished_cb) +{ + unsigned int n_int; + unsigned int m_int; + unsigned int local_m_int; + unsigned int num; + struct TestState *ts = GNUNET_new (struct TestState); + struct GNUNET_TESTING_NetjailTopology *topology; + struct GNUNET_MQ_MessageHandler handlers[] = { + GNUNET_MQ_hd_var_size (test, + GNUNET_TRANSPORT_TESTING_SIMPLE_MTYPE, + struct GNUNET_TRANSPORT_TESTING_TestMessage, + ts), + GNUNET_MQ_handler_end () + }; + unsigned int sscanf_ret = 0; + + ts->finished_cb = finished_cb; + LOG (GNUNET_ERROR_TYPE_ERROR, + "n %s m %s\n", + n, + m); + + if (GNUNET_YES == *read_file) + { + LOG (GNUNET_ERROR_TYPE_DEBUG, + "read from file\n"); + topology = GNUNET_TESTING_get_topo_from_file (topology_data); + } + else + topology = GNUNET_TESTING_get_topo_from_string (topology_data); + + ts->topology = topology; + + errno = 0; + sscanf_ret = sscanf (m, "%u", &m_int); + if (errno != 0) + { + GNUNET_log_strerror (GNUNET_ERROR_TYPE_ERROR, "sscanf"); + } + GNUNET_assert (0 < sscanf_ret); + errno = 0; + sscanf_ret = sscanf (n, "%u", &n_int); + if (errno != 0) + { + GNUNET_log_strerror (GNUNET_ERROR_TYPE_ERROR, "sscanf"); + } + GNUNET_assert (0 < sscanf_ret); + errno = 0; + sscanf_ret = sscanf (local_m, "%u", &local_m_int); + if (errno != 0) + { + GNUNET_log_strerror (GNUNET_ERROR_TYPE_ERROR, "sscanf"); + } + GNUNET_assert (0 < sscanf_ret); + + if (0 == n_int) + num = m_int; + else + num = (n_int - 1) * local_m_int + m_int + topology->nodes_x; + + block_send = GNUNET_TESTING_cmd_block_until_external_trigger ("block"); + block_receive = GNUNET_TESTING_cmd_block_until_external_trigger ( + "block-receive"); + connect_peers = GNUNET_TRANSPORT_cmd_connect_peers ( + "connect-peers", + "start-peer", + "system-create", + num, + topology, + topology->additional_connects, + GNUNET_YES); + local_prepared = GNUNET_TESTING_cmd_local_test_prepared ( + "local-test-prepared", + write_message); + + + GNUNET_asprintf (&ts->cfgname, + "test_transport_api2_tcp_node1.conf"); + + LOG (GNUNET_ERROR_TYPE_DEBUG, + "plugin cfgname: %s\n", + ts->cfgname); + + LOG (GNUNET_ERROR_TYPE_DEBUG, + "node ip: %s\n", + node_ip); + + GNUNET_asprintf (&ts->testdir, + "%s%s%s", + BASE_DIR, + m, + n); + + start_peer = GNUNET_TRANSPORT_cmd_start_peer ("start-peer", + "system-create", + num, + node_ip, + handlers, + ts->cfgname, + notify_connect, + GNUNET_NO); + struct GNUNET_TESTING_Command commands[] = { + GNUNET_TESTING_cmd_system_create ("system-create", + ts->testdir), + start_peer, + GNUNET_TESTING_cmd_send_peer_ready ("send-peer-ready", + write_message), + block_send, + connect_peers, + GNUNET_TRANSPORT_cmd_send_simple ("send-simple", + "start-peer", + "system-create", + num, + topology), + block_receive, + local_prepared, + GNUNET_TRANSPORT_cmd_stop_peer ("stop-peer", + "start-peer"), + GNUNET_TESTING_cmd_system_destroy ("system-destroy", + "system-create"), + GNUNET_TESTING_cmd_end () + }; + + ts->write_message = write_message; + + is = GNUNET_TESTING_run (commands, + TIMEOUT, + &handle_result, + ts); + return is; +} + + +/** + * Entry point for the plugin. + * + * @param cls NULL + * @return the exported block API + */ +void * +libgnunet_test_transport_plugin_cmd_simple_send_dv_init (void *cls) +{ + struct GNUNET_TESTING_PluginFunctions *api; + + GNUNET_log_setup ("simple-send", + "DEBUG", + NULL); + + api = GNUNET_new (struct GNUNET_TESTING_PluginFunctions); + api->start_testcase = &start_testcase; + api->all_peers_started = &all_peers_started; + api->all_local_tests_prepared = all_local_tests_prepared; + api->get_waiting_for_barriers = get_waiting_for_barriers; + return api; +} + + +/** + * Exit point from the plugin. + * + * @param cls the return value from #libgnunet_test_transport_plugin_block_test_init + * @return NULL + */ +void * +libgnunet_test_transport_plugin_cmd_simple_send_dv_done (void *cls) +{ + struct GNUNET_TESTING_PluginFunctions *api = cls; + + GNUNET_free (api); + return NULL; +} + + +/* end of plugin_cmd_simple_send_broadcast.c */ diff --git a/src/service/transport/test_transport_plugin_cmd_simple_send_performance.c b/src/service/transport/test_transport_plugin_cmd_simple_send_performance.c new file mode 100644 index 000000000..2baa7b5b4 --- /dev/null +++ b/src/service/transport/test_transport_plugin_cmd_simple_send_performance.c @@ -0,0 +1,480 @@ +/* + This file is part of GNUnet + Copyright (C) 2021 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 . + + SPDX-License-Identifier: AGPL3.0-or-later + */ + +/** + * @file testbed/plugin_cmd_simple_send.c + * @brief a plugin to provide the API for running test cases. + * @author t3sserakt + */ +#include "platform.h" +#include "gnunet_testing_barrier.h" +#include "gnunet_testing_netjail_lib.h" +#include "gnunet_util_lib.h" +#include "gnunet_transport_application_service.h" +#include "transport-testing2.h" +#include "transport-testing-cmds.h" +#include "gnunet_testing_barrier.h" + +/** + * Generic logging shortcut + */ +#define LOG(kind, ...) GNUNET_log (kind, __VA_ARGS__) + +#define BASE_DIR "testdir" + +#define TOPOLOGY_CONFIG "test_transport_simple_send_topo.conf" + +#define TIMEOUT GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS, 600) + +#define MAX_RECEIVED 1000 + +#define MESSAGE_SIZE 65000 + +static struct GNUNET_TESTING_Command block_send; + +static struct GNUNET_TESTING_Command block_receive; + +static struct GNUNET_TESTING_Command connect_peers; + +static struct GNUNET_TESTING_Command local_prepared; + +static struct GNUNET_TESTING_Command start_peer; + +static struct GNUNET_TESTING_Interpreter *is; + +static struct GNUNET_CONTAINER_MultiPeerMap *senders; + +struct Sender +{ + /** + * Number of received messages from sender. + */ + unsigned long long num_received; + + /** + * Sample mean time the message traveled. + */ + struct GNUNET_TIME_Relative mean_time; + + /** + * Time the first message was send. + */ + struct GNUNET_TIME_Absolute time_first; +}; + +/** + * Function called to check a message of type GNUNET_TRANSPORT_TESTING_SIMPLE_MTYPE being + * received. + * + */ +static int +check_test (void *cls, + const struct + GNUNET_TRANSPORT_TESTING_PerformanceTestMessage *message) +{ + return GNUNET_OK; +} + + +/** + * Function called to handle a message of type GNUNET_TRANSPORT_TESTING_SIMPLE_MTYPE + * being received. + * + */ +static void +handle_test (void *cls, + const struct + GNUNET_TRANSPORT_TESTING_PerformanceTestMessage *message) +{ + struct GNUNET_PeerIdentity *peer = cls; + struct GNUNET_TESTING_AsyncContext *ac; + struct Sender *sender; + struct GNUNET_TIME_Absolute time_send; + struct GNUNET_TIME_Absolute now; + struct GNUNET_TIME_Relative time_traveled; + uint32_t num; + struct GNUNET_TRANSPORT_CoreHandle *ch; + const struct GNUNET_TESTING_StartPeerState *sps; + + + GNUNET_TRANSPORT_get_trait_state (&start_peer, + &sps); + ch = sps->th; + num = ntohl (message->num); + GNUNET_TESTING_get_trait_async_context (&block_receive, + &ac); + GNUNET_assert (NULL != ac); + + sender = GNUNET_CONTAINER_multipeermap_get (senders, peer); + + now = GNUNET_TIME_absolute_get (); + time_send = GNUNET_TIME_absolute_ntoh (message->time_send); + + time_traveled = GNUNET_TIME_absolute_get_difference (time_send, now); + + if (NULL == sender) + { + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "time traveled init %s\n", + GNUNET_i2s (peer)); + sender = GNUNET_new (struct Sender); + sender->time_first = time_send; + sender->mean_time = GNUNET_TIME_UNIT_ZERO; + GNUNET_assert (GNUNET_OK == GNUNET_CONTAINER_multipeermap_put (senders, + peer, sender, + GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_ONLY)); + } + + if (GNUNET_TIME_UNIT_ZERO.rel_value_us == sender->mean_time.rel_value_us) + { + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "time traveld mean zero\n"); + sender->mean_time = time_traveled; + } + else + { + double factor = (double) sender->num_received + / ((double) sender->num_received + 1.0); + struct GNUNET_TIME_Relative s1; + struct GNUNET_TIME_Relative s2; + + s1 = GNUNET_TIME_relative_multiply (sender->mean_time, + factor); + s2 = GNUNET_TIME_relative_divide (time_traveled, + sender->num_received + 1); + sender->mean_time = GNUNET_TIME_relative_add (s1, s2); + } + + sender->num_received++; + + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "time traveled: %llu\n", + time_traveled.rel_value_us); + GNUNET_log (GNUNET_ERROR_TYPE_INFO, + "mean time traveled: %s %llu messages received with message number %u\n", + GNUNET_STRINGS_relative_time_to_string (sender->mean_time, + GNUNET_NO), + sender->num_received, + num); + if (floor (MAX_RECEIVED * (1 - 1.0 / 200)) < sender->num_received && NULL == + ac->cont) + { + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "time traveled failed\n"); + // GNUNET_TESTING_async_fail ((struct GNUNET_TESTING_AsyncContext *) ac); + } + else if (floor (MAX_RECEIVED * (1 - 1.0 / 200)) < sender->num_received && + GNUNET_NO == ac->finished) + { + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "time traveled finish\n"); + GNUNET_TESTING_async_finish ((struct GNUNET_TESTING_AsyncContext *) ac); + } + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "time traveled end\n"); + GNUNET_TRANSPORT_core_receive_continue (ch, peer); +} + + +struct GNUNET_TESTING_BarrierList* +get_waiting_for_barriers () +{ + struct GNUNET_TESTING_BarrierList*barriers; + struct GNUNET_TESTING_BarrierListEntry *ble; + + barriers = GNUNET_new (struct GNUNET_TESTING_BarrierList); + ble = GNUNET_new (struct GNUNET_TESTING_BarrierListEntry); + ble->barrier_name = "ready-to-connect"; + ble->expected_reaches = 1; + GNUNET_CONTAINER_DLL_insert (barriers->head, + barriers->tail, + ble); + + ble = GNUNET_new (struct GNUNET_TESTING_BarrierListEntry); + ble->barrier_name = "test-case-finished"; + ble->expected_reaches = 1; + GNUNET_CONTAINER_DLL_insert (barriers->head, + barriers->tail, + ble); + return barriers; +} + + +/** + * Function called with the final result of the test. + * + * @param cls the `struct MainParams` + * @param rv #GNUNET_OK if the test passed + */ +static void +handle_result (void *cls, + enum GNUNET_GenericReturnValue rv) +{ + struct TestState *ts = cls; + + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Local test exits with status %d\n", + rv); + + ts->finished_cb (rv); + GNUNET_free (ts->testdir); + GNUNET_free (ts->cfgname); + GNUNET_TESTING_free_topology (ts->topology); + GNUNET_free (ts); +} + + +/** + * Callback from start peer cmd for signaling a peer got connected. + * + */ +static void * +notify_connect (struct GNUNET_TESTING_Interpreter *is, + const struct GNUNET_PeerIdentity *peer) +{ + const struct ConnectPeersState *cps; + const struct GNUNET_TESTING_Command *cmd; + + cmd = GNUNET_TESTING_interpreter_lookup_command (is, + "connect-peers"); + GNUNET_TRANSPORT_get_trait_connect_peer_state (cmd, + &cps); + void *ret = NULL; + + cps->notify_connect (is, + peer); + return ret; +} + + +/** + * Function to start a local test case. + * + * @param write_message Callback to send a message to the master loop. + * @param router_ip Global address of the network namespace. + * @param node_ip The IP address of the node. + * @param m The number of the node in a network namespace. + * @param n The number of the network namespace. + * @param local_m The number of nodes in a network namespace. + * @param topology_data A file name for the file containing the topology configuration, or a string containing + * the topology configuration. + * @param read_file If read_file is GNUNET_YES this string is the filename for the topology configuration, + * if read_file is GNUNET_NO the string contains the topology configuration. + * @param finish_cb Callback function which writes a message from the helper process running on a netjail + * node to the master process * signaling that the test case running on the netjail node finished. + * @return Returns the struct GNUNET_TESTING_Interpreter of the command loop running on this netjail node. + */ +static struct GNUNET_TESTING_Interpreter * +start_testcase (GNUNET_TESTING_cmd_helper_write_cb write_message, + const char *router_ip, + const char *node_ip, + const char *m, + const char *n, + const char *local_m, + const char *topology_data, + unsigned int *read_file, + GNUNET_TESTING_cmd_helper_finish_cb finished_cb) +{ + + unsigned int n_int; + unsigned int m_int; + unsigned int local_m_int; + unsigned int num; + struct TestState *ts = GNUNET_new (struct TestState); + struct GNUNET_TESTING_NetjailTopology *topology; + unsigned int sscanf_ret = 0; + + senders = GNUNET_CONTAINER_multipeermap_create (1, GNUNET_NO); + ts->finished_cb = finished_cb; + LOG (GNUNET_ERROR_TYPE_ERROR, + "n %s m %s\n", + n, + m); + + if (GNUNET_YES == *read_file) + { + LOG (GNUNET_ERROR_TYPE_DEBUG, + "read from file\n"); + topology = GNUNET_TESTING_get_topo_from_file (topology_data); + } + else + topology = GNUNET_TESTING_get_topo_from_string (topology_data); + + ts->topology = topology; + + errno = 0; + sscanf_ret = sscanf (m, "%u", &m_int); + if (errno != 0) + { + GNUNET_log_strerror (GNUNET_ERROR_TYPE_ERROR, "sscanf"); + } + GNUNET_assert (0 < sscanf_ret); + errno = 0; + sscanf_ret = sscanf (n, "%u", &n_int); + if (errno != 0) + { + GNUNET_log_strerror (GNUNET_ERROR_TYPE_ERROR, "sscanf"); + } + GNUNET_assert (0 < sscanf_ret); + errno = 0; + sscanf_ret = sscanf (local_m, "%u", &local_m_int); + if (errno != 0) + { + GNUNET_log_strerror (GNUNET_ERROR_TYPE_ERROR, "sscanf"); + } + GNUNET_assert (0 < sscanf_ret); + + if (0 == n_int) + num = m_int; + else + num = (n_int - 1) * local_m_int + m_int + topology->nodes_x; + + block_send = GNUNET_TESTING_cmd_block_until_external_trigger ( + "block"); + block_receive = GNUNET_TESTING_cmd_block_until_external_trigger ( + "block-receive"); + connect_peers = GNUNET_TRANSPORT_cmd_connect_peers ("connect-peers", + "start-peer", + "system-create", + num, + topology, + 0, + GNUNET_YES); + local_prepared = GNUNET_TESTING_cmd_local_test_prepared ( + "local-test-prepared", + write_message); + + + GNUNET_asprintf (&ts->cfgname, + "test_transport_api2_tcp_node1.conf"); + + LOG (GNUNET_ERROR_TYPE_DEBUG, + "plugin cfgname: %s\n", + ts->cfgname); + + LOG (GNUNET_ERROR_TYPE_DEBUG, + "node ip: %s\n", + node_ip); + + GNUNET_asprintf (&ts->testdir, + "%s%s%s", + BASE_DIR, + m, + n); + + struct GNUNET_MQ_MessageHandler handlers[] = { + GNUNET_MQ_hd_var_size (test, + GNUNET_TRANSPORT_TESTING_SIMPLE_PERFORMANCE_MTYPE, + struct + GNUNET_TRANSPORT_TESTING_PerformanceTestMessage, + ts), + GNUNET_MQ_handler_end () + }; + + start_peer = GNUNET_TRANSPORT_cmd_start_peer ("start-peer", + "system-create", + num, + node_ip, + handlers, + ts->cfgname, + notify_connect, + GNUNET_NO); + + struct GNUNET_TESTING_Command commands[] = { + GNUNET_TESTING_cmd_system_create ("system-create", + ts->testdir), + start_peer, + GNUNET_TESTING_cmd_barrier_reached ("ready-to-connect-reached", + "ready-to-connect", + GNUNET_NO, + num, + GNUNET_NO, + write_message), + connect_peers, + GNUNET_TRANSPORT_cmd_send_simple_performance ("send-simple", + "start-peer", + "system-create", + num, + MESSAGE_SIZE, + MAX_RECEIVED, + topology), + block_receive, + GNUNET_TESTING_cmd_barrier_reached ("test-case-finished-reached", + "test-case-finished", + GNUNET_NO, + num, + GNUNET_NO, + write_message), + GNUNET_TRANSPORT_cmd_stop_peer ("stop-peer", + "start-peer"), + GNUNET_TESTING_cmd_system_destroy ("system-destroy", + "system-create"), + GNUNET_TESTING_cmd_end () + }; + + ts->write_message = write_message; + + is = GNUNET_TESTING_run (commands, + TIMEOUT, + &handle_result, + ts); + return is; +} + + +/** + * Entry point for the plugin. + * + * @param cls NULL + * @return the exported block API + */ +void * +libgnunet_test_transport_plugin_cmd_simple_send_performance_init (void *cls) +{ + struct GNUNET_TESTING_PluginFunctions *api; + + GNUNET_log_setup ("simple-send", + "DEBUG", + NULL); + + api = GNUNET_new (struct GNUNET_TESTING_PluginFunctions); + api->start_testcase = &start_testcase; + api->get_waiting_for_barriers = get_waiting_for_barriers; + return api; +} + + +/** + * Exit point from the plugin. + * + * @param cls the return value from #libgnunet_test_transport_plugin_simple_send_performance_init + * @return NULL + */ +void * +libgnunet_test_transport_plugin_cmd_simple_send_performance_done (void *cls) +{ + struct GNUNET_TESTING_PluginFunctions *api = cls; + + GNUNET_free (api); + return NULL; +} + + +/* end of plugin_cmd_simple_send.c */ diff --git a/src/service/transport/test_transport_plugin_cmd_udp_backchannel.c b/src/service/transport/test_transport_plugin_cmd_udp_backchannel.c new file mode 100644 index 000000000..378caf7df --- /dev/null +++ b/src/service/transport/test_transport_plugin_cmd_udp_backchannel.c @@ -0,0 +1,371 @@ +/* + This file is part of GNUnet + Copyright (C) 2021 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 . + + SPDX-License-Identifier: AGPL3.0-or-later + */ + +/** + * @file testbed/plugin_cmd_simple_send.c + * @brief a plugin to provide the API for running test cases. + * @author t3sserakt + */ +#include "platform.h" +#include "gnunet_testing_barrier.h" +#include "gnunet_testing_netjail_lib.h" +#include "gnunet_util_lib.h" +#include "gnunet_transport_application_service.h" +#include "transport-testing2.h" +#include "transport-testing-cmds.h" +#include "gnunet_testing_barrier.h" + +/** + * Generic logging shortcut + */ +#define LOG(kind, ...) GNUNET_log_from (kind, "udp-backchannel", __VA_ARGS__) + +#define BASE_DIR "testdir" + +#define TOPOLOGY_CONFIG "test_transport_udp_backchannel_topo.conf" + +#define TIMEOUT GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS, 600) + +static struct GNUNET_TESTING_Command block_send; + +static struct GNUNET_TESTING_Command connect_peers; + +static struct GNUNET_TESTING_Command local_prepared; + +static struct GNUNET_TESTING_Interpreter *is; + + +/** + * Function called to check a message of type GNUNET_TRANSPORT_TESTING_SIMPLE_MTYPE being + * received. + * + */ +static int +check_test (void *cls, + const struct GNUNET_TRANSPORT_TESTING_TestMessage *message) +{ + return GNUNET_OK; +} + + +/** + * Function called to handle a message of type GNUNET_TRANSPORT_TESTING_SIMPLE_MTYPE + * being received. + * + */ +static void +handle_test (void *cls, + const struct GNUNET_TRANSPORT_TESTING_TestMessage *message) +{ + // struct GNUNET_TESTING_AsyncContext *ac; + + /*GNUNET_TESTING_get_trait_async_context (&block_receive, + &ac); + if ((NULL == ac) || (NULL == ac->cont)) + GNUNET_TESTING_async_fail (ac); + else + GNUNET_TESTING_async_finish (ac);*/ +} + + +struct GNUNET_TESTING_BarrierList * +get_waiting_for_barriers () +{ + // No Barrier + return GNUNET_new (struct GNUNET_TESTING_BarrierList); +} + + +/** + * Callback to set the flag indicating all peers started. Will be called via the plugin api. + * + */ +static void +all_peers_started () +{ + struct GNUNET_TESTING_AsyncContext *ac; + + GNUNET_TESTING_get_trait_async_context (&block_send, + &ac); + GNUNET_assert (NULL != ac); + if ((NULL == ac->cont)) + GNUNET_TESTING_async_fail ((struct GNUNET_TESTING_AsyncContext *) ac); + else + GNUNET_TESTING_async_finish ((struct GNUNET_TESTING_AsyncContext *) ac); +} + + +/** +* Function called with the final result of the test. +* +* @param cls the `struct MainParams` +* @param rv #GNUNET_OK if the test passed +*/ +static void +handle_result (void *cls, + enum GNUNET_GenericReturnValue rv) +{ + struct TestState *ts = cls; + + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Local test exits with status %d\n", + rv); + + ts->finished_cb (rv); + GNUNET_free (ts->testdir); + GNUNET_free (ts->cfgname); + GNUNET_TESTING_free_topology (ts->topology); + GNUNET_free (ts); +} + + +/** + * Callback from start peer cmd for signaling a peer got connected. + * + */ +static void * +notify_connect (struct GNUNET_TESTING_Interpreter *is, + const struct GNUNET_PeerIdentity *peer) +{ + const struct ConnectPeersState *cps; + + GNUNET_TRANSPORT_get_trait_connect_peer_state (&connect_peers, + &cps); + void *ret = NULL; + + cps->notify_connect (is, + peer); + return ret; +} + + +/** + * Callback to set the flag indicating all peers are prepared to finish. Will be called via the plugin api. + */ +static void +all_local_tests_prepared () +{ + const struct GNUNET_TESTING_LocalPreparedState *lfs; + + GNUNET_TESTING_get_trait_local_prepared_state (&local_prepared, + &lfs); + GNUNET_assert (NULL != &lfs->ac); + if (NULL == lfs->ac.cont) + GNUNET_TESTING_async_fail ((struct GNUNET_TESTING_AsyncContext *) &lfs->ac); + else + GNUNET_TESTING_async_finish ((struct + GNUNET_TESTING_AsyncContext *) &lfs->ac); +} + + +/** + * Function to start a local test case. + * + * @param write_message Callback to send a message to the master loop. + * @param router_ip Global address of the network namespace. + * @param node_ip The IP address of the node. + * @param m The number of the node in a network namespace. + * @param n The number of the network namespace. + * @param local_m The number of nodes in a network namespace. + */ +static struct GNUNET_TESTING_Interpreter * +start_testcase (GNUNET_TESTING_cmd_helper_write_cb write_message, + const char *router_ip, + const char *node_ip, + const char *m, + const char *n, + const char *local_m, + const char *topology_data, + unsigned int *read_file, + GNUNET_TESTING_cmd_helper_finish_cb finished_cb) +{ + + unsigned int n_int; + unsigned int m_int; + unsigned int local_m_int; + unsigned int num; + struct TestState *ts = GNUNET_new (struct TestState); + struct GNUNET_TESTING_NetjailTopology *topology; + unsigned int sscanf_ret = 0; + + ts->finished_cb = finished_cb; + LOG (GNUNET_ERROR_TYPE_ERROR, + "n %s m %s\n", + n, + m); + + if (GNUNET_YES == *read_file) + { + LOG (GNUNET_ERROR_TYPE_DEBUG, + "read from file\n"); + topology = GNUNET_TESTING_get_topo_from_file (topology_data); + } + else + topology = GNUNET_TESTING_get_topo_from_string (topology_data); + + ts->topology = topology; + + errno = 0; + sscanf_ret = sscanf (m, "%u", &m_int); + if (errno != 0) + { + GNUNET_log_strerror (GNUNET_ERROR_TYPE_ERROR, "sscanf"); + } + GNUNET_assert (0 < sscanf_ret); + errno = 0; + sscanf_ret = sscanf (n, "%u", &n_int); + if (errno != 0) + { + GNUNET_log_strerror (GNUNET_ERROR_TYPE_ERROR, "sscanf"); + } + GNUNET_assert (0 < sscanf_ret); + errno = 0; + sscanf_ret = sscanf (local_m, "%u", &local_m_int); + if (errno != 0) + { + GNUNET_log_strerror (GNUNET_ERROR_TYPE_ERROR, "sscanf"); + } + GNUNET_assert (0 < sscanf_ret); + + + if (0 == n_int) + num = m_int; + else + num = (n_int - 1) * local_m_int + m_int + topology->nodes_x; + + block_send = GNUNET_TESTING_cmd_block_until_external_trigger ( + "block"); + connect_peers = GNUNET_TRANSPORT_cmd_connect_peers ("connect-peers", + "start-peer", + "system-create", + num, + topology, + 0, + GNUNET_YES); + local_prepared = GNUNET_TESTING_cmd_local_test_prepared ( + "local-test-prepared", + write_message); + + GNUNET_asprintf (&ts->cfgname, + "test_transport_api2_tcp_node1.conf"); + + LOG (GNUNET_ERROR_TYPE_DEBUG, + "plugin cfgname: %s\n", + ts->cfgname); + + LOG (GNUNET_ERROR_TYPE_DEBUG, + "node ip: %s\n", + node_ip); + + GNUNET_asprintf (&ts->testdir, + "%s%s%s", + BASE_DIR, + m, + n); + + struct GNUNET_MQ_MessageHandler handlers[] = { + GNUNET_MQ_hd_var_size (test, + GNUNET_TRANSPORT_TESTING_SIMPLE_MTYPE, + struct GNUNET_TRANSPORT_TESTING_TestMessage, + NULL), + GNUNET_MQ_handler_end () + }; + + struct GNUNET_TESTING_Command commands[] = { + GNUNET_TESTING_cmd_system_create ("system-create", + ts->testdir), + GNUNET_TRANSPORT_cmd_start_peer ("start-peer", + "system-create", + num, + node_ip, + handlers, + ts->cfgname, + notify_connect, + GNUNET_NO), + GNUNET_TESTING_cmd_send_peer_ready ("send-peer-ready", + write_message), + block_send, + connect_peers, + GNUNET_TRANSPORT_cmd_backchannel_check ("backchannel-check", + "start-peer", + "system-create", + num, + m_int, + n_int, + topology), + local_prepared, + GNUNET_TRANSPORT_cmd_stop_peer ("stop-peer", + "start-peer"), + GNUNET_TESTING_cmd_system_destroy ("system-destroy", + "system-create"), + GNUNET_TESTING_cmd_end () + }; + + ts->write_message = write_message; + + is = GNUNET_TESTING_run (commands, + TIMEOUT, + &handle_result, + ts); + return is; +} + + +/** + * Entry point for the plugin. + * + * @param cls NULL + * @return the exported block API + */ +void * +libgnunet_test_transport_plugin_cmd_udp_backchannel_init (void *cls) +{ + struct GNUNET_TESTING_PluginFunctions *api; + + GNUNET_log_setup ("udp-backchannel", + "DEBUG", + "plugin.out"); + + api = GNUNET_new (struct GNUNET_TESTING_PluginFunctions); + api->start_testcase = &start_testcase; + api->all_peers_started = &all_peers_started; + api->all_local_tests_prepared = all_local_tests_prepared; + api->get_waiting_for_barriers = get_waiting_for_barriers; + return api; +} + + +/** + * Exit point from the plugin. + * + * @param cls the return value from #libgnunet_test_transport_plugin_block_test_init + * @return NULL + */ +void * +libgnunet_test_transport_plugin_cmd_udp_backchannel_done (void *cls) +{ + struct GNUNET_TESTING_PluginFunctions *api = cls; + + GNUNET_free (api); + return NULL; +} + + +/* end of plugin_cmd_simple_send.c */ diff --git a/src/service/transport/test_transport_simple_send.sh b/src/service/transport/test_transport_simple_send.sh new file mode 100755 index 000000000..0250070be --- /dev/null +++ b/src/service/transport/test_transport_simple_send.sh @@ -0,0 +1,11 @@ +#!/bin/bash +if ! [ -d "/run/netns" ]; then + echo You have to create the directory /run/netns. +fi +if [ -f /proc/sys/kernel/unprivileged_userns_clone ]; then + if [ "$(cat /proc/sys/kernel/unprivileged_userns_clone)" != 1 ]; then + echo -e "Error during test setup: The kernel parameter kernel.unprivileged_userns_clone has to be set to 1! One has to execute\n\n sysctl kernel.unprivileged_userns_clone=1\n" + exit 78 + fi +fi +exec unshare -r -nmU bash -c "mount -t tmpfs --make-rshared tmpfs /run/netns; ./test_transport_start_with_config test_transport_simple_send_topo.conf" diff --git a/src/service/transport/test_transport_simple_send_broadcast.sh b/src/service/transport/test_transport_simple_send_broadcast.sh new file mode 100755 index 000000000..a4801bf70 --- /dev/null +++ b/src/service/transport/test_transport_simple_send_broadcast.sh @@ -0,0 +1,11 @@ +#!/bin/bash +if ! [ -d "/run/netns" ]; then + echo You have to create the directory /run/netns. +fi +if [ -f /proc/sys/kernel/unprivileged_userns_clone ]; then + if [ "$(cat /proc/sys/kernel/unprivileged_userns_clone)" != 1 ]; then + echo -e "Error during test setup: The kernel parameter kernel.unprivileged_userns_clone has to be set to 1! One has to execute\n\n sysctl kernel.unprivileged_userns_clone=1\n" + exit 78 + fi +fi +exec unshare -r -nmU bash -c "mount -t tmpfs --make-rshared tmpfs /run/netns; ./test_transport_start_with_config test_transport_simple_send_broadcast_topo.conf" diff --git a/src/service/transport/test_transport_simple_send_broadcast_topo.conf b/src/service/transport/test_transport_simple_send_broadcast_topo.conf new file mode 100644 index 000000000..aa4964f81 --- /dev/null +++ b/src/service/transport/test_transport_simple_send_broadcast_topo.conf @@ -0,0 +1,4 @@ +M:2 +N:1 +X:0 +T:libgnunet_test_transport_plugin_cmd_simple_send_broadcast \ No newline at end of file diff --git a/src/service/transport/test_transport_simple_send_dv_circle.sh b/src/service/transport/test_transport_simple_send_dv_circle.sh new file mode 100755 index 000000000..bd5f00abe --- /dev/null +++ b/src/service/transport/test_transport_simple_send_dv_circle.sh @@ -0,0 +1,11 @@ +#!/bin/bash +if ! [ -d "/run/netns" ]; then + echo You have to create the directory /run/netns. +fi +if [ -f /proc/sys/kernel/unprivileged_userns_clone ]; then + if [ "$(cat /proc/sys/kernel/unprivileged_userns_clone)" != 1 ]; then + echo -e "Error during test setup: The kernel parameter kernel.unprivileged_userns_clone has to be set to 1! One has to execute\n\n sysctl kernel.unprivileged_userns_clone=1\n" + exit 78 + fi +fi +exec unshare -r -nmU bash -c "mount -t tmpfs --make-rshared tmpfs /run/netns; ./test_transport_start_with_config test_transport_distance_vector_circle_topo.conf" diff --git a/src/service/transport/test_transport_simple_send_dv_inverse.sh b/src/service/transport/test_transport_simple_send_dv_inverse.sh new file mode 100755 index 000000000..cf7b863a0 --- /dev/null +++ b/src/service/transport/test_transport_simple_send_dv_inverse.sh @@ -0,0 +1,12 @@ +#!/bin/bash +if ! [ -d "/run/netns" ]; then + echo You have to create the directory /run/netns. +fi +if [ -f /proc/sys/kernel/unprivileged_userns_clone ]; then + if [ "$(cat /proc/sys/kernel/unprivileged_userns_clone)" != 1 ]; then + echo -e "Error during test setup: The kernel parameter kernel.unprivileged_userns_clone has to be set to 1! One has to execute\n\n sysctl kernel.unprivileged_userns_clone=1\n" + exit 78 + fi +fi +# exec unshare -r -nmU bash -c "mount -t tmpfs --make-rshared tmpfs /run/netns; valgrind --leak-check=full --track-origins=yes --trace-children=yes --trace-children-skip=/usr/bin/awk,/usr/bin/cut,/usr/bin/seq,/sbin/ip/sed/bash ./test_transport_start_with_config test_transport_distance_vector_inverse_topo.conf" +exec unshare -r -nmU bash -c "mount -t tmpfs --make-rshared tmpfs /run/netns; ./test_transport_start_with_config test_transport_distance_vector_inverse_topo.conf" diff --git a/src/service/transport/test_transport_simple_send_performance.sh b/src/service/transport/test_transport_simple_send_performance.sh new file mode 100755 index 000000000..12798c2f0 --- /dev/null +++ b/src/service/transport/test_transport_simple_send_performance.sh @@ -0,0 +1,11 @@ +#!/bin/bash +if ! [ -d "/run/netns" ]; then + echo You have to create the directory /run/netns. +fi +if [ -f /proc/sys/kernel/unprivileged_userns_clone ]; then + if [ "$(cat /proc/sys/kernel/unprivileged_userns_clone)" != 1 ]; then + echo -e "Error during test setup: The kernel parameter kernel.unprivileged_userns_clone has to be set to 1! One has to execute\n\n sysctl kernel.unprivileged_userns_clone=1\n" + exit 78 + fi +fi +exec unshare -r -nmU bash -c "mount -t tmpfs --make-rshared tmpfs /run/netns; ./test_transport_start_with_config test_transport_simple_send_performance_topo.conf" diff --git a/src/service/transport/test_transport_simple_send_performance_topo.conf b/src/service/transport/test_transport_simple_send_performance_topo.conf new file mode 100644 index 000000000..9f39cd9bc --- /dev/null +++ b/src/service/transport/test_transport_simple_send_performance_topo.conf @@ -0,0 +1,6 @@ +M:2 +N:1 +X:0 +T:libgnunet_test_transport_plugin_cmd_simple_send_performance +P:1:1|{connect:{P:1:2:tcp}|{P:1:2:udp}} +P:1:2|{connect:{P:1:1:tcp}|{P:1:1:udp}} \ No newline at end of file diff --git a/src/service/transport/test_transport_simple_send_string.sh b/src/service/transport/test_transport_simple_send_string.sh new file mode 100755 index 000000000..211abb494 --- /dev/null +++ b/src/service/transport/test_transport_simple_send_string.sh @@ -0,0 +1,21 @@ +#!/bin/bash +string=$(cat << EOF +M:2 +N:1 +X:0 +T:libgnunet_test_transport_plugin_cmd_simple_send +P:1:1|{connect:{P:1:2:tcp}} +P:1:2|{connect:{P:1:1:tcp}} +EOF + ) +if ! [ -d "/run/netns" ]; then + echo You have to create the directory /run/netns. +fi +if [ -f /proc/sys/kernel/unprivileged_userns_clone ]; then + if [ "$(cat /proc/sys/kernel/unprivileged_userns_clone)" != 1 ]; then + echo -e "Error during test setup: The kernel parameter kernel.unprivileged_userns_clone has to be set to 1! One has to execute\n\n sysctl kernel.unprivileged_userns_clone=1\n" + exit 78 + fi +fi + +exec unshare -r -nmU bash -c "mount -t tmpfs --make-rshared tmpfs /run/netns; ./test_transport_start_with_config -s '$string'" diff --git a/src/service/transport/test_transport_simple_send_topo.conf b/src/service/transport/test_transport_simple_send_topo.conf new file mode 100644 index 000000000..2c16201f5 --- /dev/null +++ b/src/service/transport/test_transport_simple_send_topo.conf @@ -0,0 +1,6 @@ +M:2 +N:1 +X:0 +T:libgnunet_test_transport_plugin_cmd_simple_send +P:1:1|{connect:{P:1:2:tcp}} +P:1:2|{connect:{P:1:1:tcp}} \ No newline at end of file diff --git a/src/service/transport/test_transport_start_testcase.sh b/src/service/transport/test_transport_start_testcase.sh new file mode 100755 index 000000000..028c8f248 --- /dev/null +++ b/src/service/transport/test_transport_start_testcase.sh @@ -0,0 +1,12 @@ +#!/bin/bash +read -p "Test case configuration to use:" conf +if ! [ -d "/run/netns" ]; then + echo You have to create the directory /run/netns. +fi +if [ -f /proc/sys/kernel/unprivileged_userns_clone ]; then + if [ "$(cat /proc/sys/kernel/unprivileged_userns_clone)" != 1 ]; then + echo -e "Error during test setup: The kernel parameter kernel.unprivileged_userns_clone has to be set to 1! One has to execute\n\n sysctl kernel.unprivileged_userns_clone=1\n" + exit 78 + fi +fi +exec unshare -r -nmU bash -c "mount -t tmpfs --make-rshared tmpfs /run/netns; ./test_transport_start_with_config $conf" diff --git a/src/service/transport/test_transport_start_with_config.c b/src/service/transport/test_transport_start_with_config.c new file mode 100644 index 000000000..349cd65a5 --- /dev/null +++ b/src/service/transport/test_transport_start_with_config.c @@ -0,0 +1,121 @@ +/* + This file is part of GNUnet + Copyright (C) 2021 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 . + + SPDX-License-Identifier: AGPL3.0-or-later + */ + +/** + * @file transport/test_transport_start_with_config.c + * @brief Generic program to start testcases in an configurable topology. + * @author t3sserakt + */ +#include "platform.h" +#include "gnunet_testing_ng_lib.h" +#include "gnunet_testing_netjail_lib.h" +#include "gnunet_util_lib.h" + +#define TIMEOUT GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS, 600) + + +int +main (int argc, + char *const *argv) +{ + char *topology_data; + char *topology_data_script; + struct GNUNET_TESTING_NetjailTopology *topology; + unsigned int read_file = GNUNET_YES; + int ret; + char *rest = NULL; + char *token; + size_t single_line_len; + size_t data_len; + + GNUNET_log_setup ("test-netjail", + "INFO", + NULL); + + if (0 == strcmp ("-s", argv[1])) + { + data_len = strlen (argv[2]); + topology_data = GNUNET_malloc (data_len); + topology_data_script = GNUNET_malloc (data_len); + token = strtok_r (argv[2], "\n", &rest); + while (NULL != token) + { + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "token1 %s\n", + token); + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "token2 %s\n", + token); + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "topology_data %s\n", + topology_data); + strcat (topology_data_script, token); + strcat (topology_data_script, " "); + strcat (topology_data, token); + strcat (topology_data, "\n"); + token = strtok_r (NULL, "\n", &rest); + } + single_line_len = strlen (topology_data); + topology_data_script [single_line_len - 1] = '\0'; + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "read from string\n"); + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "topology_data %s\n", + topology_data); + read_file = GNUNET_NO; + topology = GNUNET_TESTING_get_topo_from_string (topology_data); + } + else + { + topology_data = argv[1]; + topology_data_script = argv[1]; + topology = GNUNET_TESTING_get_topo_from_file (topology_data); + } + + struct GNUNET_TESTING_Command commands[] = { + GNUNET_TESTING_cmd_netjail_start ("netjail-start", + topology_data_script, + &read_file), + GNUNET_TESTING_cmd_netjail_start_cmds_helper ("netjail-start-testbed", + topology, + &read_file, + topology_data_script, + TIMEOUT), + GNUNET_TESTING_cmd_stop_cmds_helper ("stop-testbed", + "netjail-start-testbed", + topology), + GNUNET_TESTING_cmd_netjail_stop ("netjail-stop", + topology_data_script, + &read_file), + GNUNET_TESTING_cmd_end () + }; + + ret = GNUNET_TESTING_main (commands, + TIMEOUT); + + if (0 == strcmp ("-s", argv[1])) + { + GNUNET_free (topology_data_script); + GNUNET_free (topology_data); + } + GNUNET_TESTING_free_topology (topology); + + return ret; +} diff --git a/src/service/transport/test_transport_test_transport_address_switch_tcp_peer1.conf b/src/service/transport/test_transport_test_transport_address_switch_tcp_peer1.conf new file mode 100644 index 000000000..920e8d199 --- /dev/null +++ b/src/service/transport/test_transport_test_transport_address_switch_tcp_peer1.conf @@ -0,0 +1,29 @@ +@INLINE@ template_cfg_peer1.conf + +[ats] +UNSPECIFIED_QUOTA_IN = 8 KiB +UNSPECIFIED_QUOTA_OUT = 8 KiB +# LOOPBACK +LOOPBACK_QUOTA_IN = 8 KiB +LOOPBACK_QUOTA_OUT = 8 KiB +# LAN +LAN_QUOTA_IN = 8 KiB +LAN_QUOTA_OUT = 8 KiB +# WAN +WAN_QUOTA_IN = 8 KiB +WAN_QUOTA_OUT = 8 KiB +# WLAN +WLAN_QUOTA_IN = 8 KiB +WLAN_QUOTA_OUT = 8 KiB +# BLUETOOTH +BLUETOOTH_QUOTA_IN = 8 KiB +BLUETOOTH_QUOTA_OUT = 8 KiB + +[PATHS] +GNUNET_TEST_HOME = $GNUNET_TMP/test-transport/quota-tcp-p1/ + +[transport] +PLUGINS = tcp + + + diff --git a/src/service/transport/test_transport_test_transport_address_switch_tcp_peer2.conf b/src/service/transport/test_transport_test_transport_address_switch_tcp_peer2.conf new file mode 100644 index 000000000..6855cd00d --- /dev/null +++ b/src/service/transport/test_transport_test_transport_address_switch_tcp_peer2.conf @@ -0,0 +1,29 @@ +@INLINE@ template_cfg_peer2.conf + +[ats] +UNSPECIFIED_QUOTA_IN = 8 KiB +UNSPECIFIED_QUOTA_OUT = 8 KiB +# LOOPBACK +LOOPBACK_QUOTA_IN = 8 KiB +LOOPBACK_QUOTA_OUT = 8 KiB +# LAN +LAN_QUOTA_IN = 8 KiB +LAN_QUOTA_OUT = 8 KiB +# WAN +WAN_QUOTA_IN = 8 KiB +WAN_QUOTA_OUT = 8 KiB +# WLAN +WLAN_QUOTA_IN = 8 KiB +WLAN_QUOTA_OUT = 8 KiB +# BLUETOOTH +BLUETOOTH_QUOTA_IN = 8 KiB +BLUETOOTH_QUOTA_OUT = 8 KiB + + +[PATHS] +GNUNET_TEST_HOME = $GNUNET_TMP/test-transport/api-tcp-p2/ + +[transport] +PLUGINS = tcp + + diff --git a/src/service/transport/test_transport_testing_startstop.c b/src/service/transport/test_transport_testing_startstop.c new file mode 100644 index 000000000..4783c1813 --- /dev/null +++ b/src/service/transport/test_transport_testing_startstop.c @@ -0,0 +1,138 @@ +/* + This file is part of GNUnet. + Copyright (C) 2009, 2010, 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 . + + SPDX-License-Identifier: AGPL3.0-or-later + */ +/** + * @file transport/test_transport_testing_startstop.c + * @brief test case for transport testing library: + * start the peer, get the HELLO message and stop the peer + */ +#include "platform.h" +#include "gnunet_transport_service.h" +#include "transport-testing.h" + +#define TIMEOUT GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS, 30) + + +static struct GNUNET_SCHEDULER_Task *timeout_task; + +static struct GNUNET_TRANSPORT_TESTING_PeerContext *p; + +static struct GNUNET_TRANSPORT_TESTING_Handle *tth; + +static int ret; + + +static void +end () +{ + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Stopping peers\n"); + if (NULL != timeout_task) + GNUNET_SCHEDULER_cancel (timeout_task); + if (NULL != p) + GNUNET_TRANSPORT_TESTING_stop_peer (p); + if (NULL != tth) + GNUNET_TRANSPORT_TESTING_done (tth); +} + + +static void +end_badly () +{ + timeout_task = NULL; + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "Timeout!\n"); + end (); + ret = GNUNET_SYSERR; +} + + +static void +start_cb (void *cls) +{ + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Peer %u (`%s') successfully started\n", + p->no, + GNUNET_i2s (&p->id)); + ret = 0; + GNUNET_SCHEDULER_add_now (&end, + NULL); +} + + +static void +run (void *cls, + char *const *args, + const char *cfgfile, + const struct GNUNET_CONFIGURATION_Handle *cfg) +{ + ret = 1; + tth = GNUNET_TRANSPORT_TESTING_init (); + GNUNET_assert (NULL != tth); + + timeout_task = + GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_UNIT_MINUTES, + &end_badly, + NULL); + + p = GNUNET_TRANSPORT_TESTING_start_peer (tth, + cfgfile, + 1, + NULL, /* receive cb */ + NULL, /* connect cb */ + NULL, /* disconnect cb */ + NULL, /* nc/nd closure */ + &start_cb, /* startup cb */ + NULL); /* closure */ + if (NULL == p) + { + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Failed to start peer\n"); + if (timeout_task != NULL) + GNUNET_SCHEDULER_cancel (timeout_task); + timeout_task = GNUNET_SCHEDULER_add_now (&end_badly, NULL); + } +} + + +int +main (int argc, char *argv[]) +{ + char *const argv_1[] = { "test_transport_testing", + "-c", + "test_transport_api_data.conf", + NULL }; + struct GNUNET_GETOPT_CommandLineOption options[] = { + GNUNET_GETOPT_OPTION_END + }; + + GNUNET_log_setup ("test_transport_testing_startstop", + "WARNING", + NULL); + GNUNET_PROGRAM_run ((sizeof(argv_1) / sizeof(char *)) - 1, + argv_1, + "test_transport_testing_startstop", "nohelp", + options, + &run, + &ret); + + return ret; +} + + +/* end of test_transport_testing_startstop.c */ diff --git a/src/service/transport/test_transport_udp_backchannel.sh b/src/service/transport/test_transport_udp_backchannel.sh new file mode 100755 index 000000000..1a7c83385 --- /dev/null +++ b/src/service/transport/test_transport_udp_backchannel.sh @@ -0,0 +1,14 @@ +#!/bin/bash +if [ -f "test.out" ]; then + rm test.out +fi +if ! [ -d "/run/netns" ]; then + echo You have to create the directory /run/netns. +fi +if [ -f /proc/sys/kernel/unprivileged_userns_clone ]; then + if [ "$(cat /proc/sys/kernel/unprivileged_userns_clone)" != 1 ]; then + echo -e "Error during test setup: The kernel parameter kernel.unprivileged_userns_clone has to be set to 1! One has to execute\n\n sysctl kernel.unprivileged_userns_clone=1\n" + exit 78 + fi +fi +exec unshare -r -nmU bash -c "mount -t tmpfs --make-rshared tmpfs /run/netns; GNUNET_FORCE_LOG=';;;;DEBUG' GNUNET_FORCE_LOGFILE='test.out' ./test_transport_start_with_config test_transport_udp_backchannel_topo.conf" diff --git a/src/service/transport/test_transport_udp_backchannel_topo.conf b/src/service/transport/test_transport_udp_backchannel_topo.conf new file mode 100644 index 000000000..ad35bde0a --- /dev/null +++ b/src/service/transport/test_transport_udp_backchannel_topo.conf @@ -0,0 +1,7 @@ +M:1 +N:1 +X:1 +T:libgnunet_test_transport_plugin_cmd_udp_backchannel +K:1|{connect:{P:1:1:tcp}} +R:1|{tcp_port:1}|{udp_port:0} +P:1:1|{connect:{K:1:udp}} diff --git a/src/service/transport/transport-testing-cmds.h b/src/service/transport/transport-testing-cmds.h new file mode 100644 index 000000000..6b6fcf4f1 --- /dev/null +++ b/src/service/transport/transport-testing-cmds.h @@ -0,0 +1,245 @@ +/* + This file is part of GNUnet. + Copyright (C) 2021 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 . + + SPDX-License-Identifier: AGPL3.0-or-later + */ + +/** + * @file transport-testing.h + * @brief testing lib for transport service + * @author Matthias Wachs + * @author Christian Grothoff + */ +#ifndef TRANSPORT_TESTING_CMDS_H +#define TRANSPORT_TESTING_CMDS_H +#include "gnunet_testing_ng_lib.h" +#include "gnunet_testing_plugin.h" + +typedef void * +(*GNUNET_TRANSPORT_notify_connect_cb) (struct GNUNET_TESTING_Interpreter *is, + const struct GNUNET_PeerIdentity *peer); + + + +struct TestState +{ + /** + * Callback to write messages to the master loop. + * + */ + GNUNET_TESTING_cmd_helper_write_cb write_message; + + /** + * Callback to notify the helper test case has finished. + */ + GNUNET_TESTING_cmd_helper_finish_cb finished_cb; + + /** + * The name for a specific test environment directory. + * + */ + char *testdir; + + /** + * The name for the configuration file of the specific node. + * + */ + char *cfgname; + + /** + * The complete topology information. + */ + struct GNUNET_TESTING_NetjailTopology *topology; +}; + + +/** + * Create command. + * + * @param label name for command. + * @param system_label Label of the cmd to setup a test environment. + * @param no Decimal number representing the last byte of the IP address of this peer. + * @param node_ip The IP address of this node. + * @param handlers Handler for messages received by this peer. + * @param cfgname Configuration file name for this peer. + * @param notify_connect Method which will be called, when a peer connects. + * @param broadcast Flag indicating, if broadcast should be switched on. + * @return command. + */ +struct GNUNET_TESTING_Command +GNUNET_TRANSPORT_cmd_start_peer (const char *label, + const char *system_label, + uint32_t no, + const char *node_ip, + struct GNUNET_MQ_MessageHandler *handlers, + const char *cfgname, + GNUNET_TRANSPORT_notify_connect_cb + notify_connect, + unsigned int broadcast); + + +struct GNUNET_TESTING_Command +GNUNET_TRANSPORT_cmd_stop_peer (const char *label, + const char *start_label); + + +/** + * Create command + * + * @param label name for command + * @param start_peer_label Label of the cmd to start a peer. + * @param create_label Label of the cmd which started the test system. + * @param num Number globally identifying the node. + * @param topology The topology for the test setup. + * @param additional_connects Number of additional connects this cmd will wait for not triggered by this cmd. + * @return command. + */ +struct GNUNET_TESTING_Command +GNUNET_TRANSPORT_cmd_connect_peers ( + const char *label, + const char *start_peer_label, + const char *create_label, + uint32_t num, + struct GNUNET_TESTING_NetjailTopology *topology, + unsigned int additional_connects, + unsigned int wait_for_connect); + + +/** + * Create command. + * + * @param label name for command. + * @param start_peer_label Label of the cmd to start a peer. + * @param create_label Label of the cmd which started the test system. + * @param num Number globally identifying the node. + * @param topology The topology for the test setup. + * @return command. + */ +struct GNUNET_TESTING_Command +GNUNET_TRANSPORT_cmd_send_simple (const char *label, + const char *start_peer_label, + const char *create_label, + uint32_t num, + struct GNUNET_TESTING_NetjailTopology * + topology); + +/** + * + * + * @param label name for command. + * @param start_peer_label Label of the cmd to start a peer. + * @param create_label Label of the cmd which started the test system. + * @param num Number globally identifying the node. + * @param size The size of the test message to send. + * @param max_send The number of messages to send. + * @param topology The topology for the test setup. + * @return command. + */ +struct GNUNET_TESTING_Command +GNUNET_TRANSPORT_cmd_send_simple_performance (const char *label, + const char *start_peer_label, + const char *create_label, + uint32_t num, + int size, + int max_send, + struct GNUNET_TESTING_NetjailTopology * + topology); + + +/** + * Create command. + * + * @param label name for command. + * @param start_peer_label Label of the cmd to start a peer. + * @param create_label Label of the cmd to create the testing system. + * @param num Number globally identifying the node. + * @param node_n The number of the node in a network namespace. + * @param namespace_n The number of the network namespace. + * @param topology The topology for the test setup. + * @return command. + */ +struct GNUNET_TESTING_Command +GNUNET_TRANSPORT_cmd_backchannel_check (const char *label, + const char *start_peer_label, + const char *create_label, + uint32_t num, + unsigned int node_n, + unsigned int namespace_n, + struct GNUNET_TESTING_NetjailTopology * + topology); + + +/** + * Create headers for a trait with name @a name for + * statically allocated data of type @a type. + */ +#define GNUNET_TRANSPORT_MAKE_DECL_SIMPLE_TRAIT(name,type) \ + enum GNUNET_GenericReturnValue \ + GNUNET_TRANSPORT_get_trait_ ## name ( \ + const struct GNUNET_TESTING_Command *cmd, \ + type **ret); \ + struct GNUNET_TESTING_Trait \ + GNUNET_TRANSPORT_make_trait_ ## name ( \ + type * value); + + +/** + * Create C implementation for a trait with name @a name for statically + * allocated data of type @a type. + */ +#define GNUNET_TRANSPORT_MAKE_IMPL_SIMPLE_TRAIT(name,type) \ + enum GNUNET_GenericReturnValue \ + GNUNET_TRANSPORT_get_trait_ ## name ( \ + const struct GNUNET_TESTING_Command *cmd, \ + type **ret) \ + { \ + if (NULL == cmd->traits) return GNUNET_SYSERR; \ + return cmd->traits (cmd->cls, \ + (const void **) ret, \ + GNUNET_S (name), \ + 0); \ + } \ + struct GNUNET_TESTING_Trait \ + GNUNET_TRANSPORT_make_trait_ ## name ( \ + type * value) \ + { \ + struct GNUNET_TESTING_Trait ret = { \ + .trait_name = GNUNET_S (name), \ + .ptr = (const void *) value \ + }; \ + return ret; \ + } + + +/** + * Call #op on all simple traits. + */ +#define GNUNET_TRANSPORT_SIMPLE_TRAITS(op) \ + op (peer_id, const struct GNUNET_PeerIdentity) \ + op (connected_peers_map, const struct GNUNET_CONTAINER_MultiShortmap) \ + op (hello_size, const size_t) \ + op (hello, const char) \ + op (application_handle, const struct GNUNET_TRANSPORT_ApplicationHandle) \ + op (connect_peer_state, const struct ConnectPeersState) \ + op (state, const struct GNUNET_TESTING_StartPeerState) \ + op (broadcast, const enum GNUNET_GenericReturnValue) + +GNUNET_TRANSPORT_SIMPLE_TRAITS (GNUNET_TRANSPORT_MAKE_DECL_SIMPLE_TRAIT) + + +#endif +/* end of transport_testing.h */ diff --git a/src/service/transport/transport-testing-communicator.c b/src/service/transport/transport-testing-communicator.c new file mode 100644 index 000000000..553426b66 --- /dev/null +++ b/src/service/transport/transport-testing-communicator.c @@ -0,0 +1,1238 @@ +/* + This file is part of GNUnet. + Copyright (C) 2019 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 . + + SPDX-License-Identifier: AGPL3.0-or-later + */ + +/** + * @file transport/transport-testing-communicator.c + * @brief functions related to testing-tng + * @author Christian Grothoff + * @author Julius Bünger + */ +#include "platform.h" +#include "gnunet_util_lib.h" +#include "gnunet_protocols.h" +#include "gnunet_constants.h" +#include "transport-testing-communicator.h" +#include "gnunet_hello_lib.h" +#include "gnunet_signatures.h" +#include "transport.h" +#include + +#define LOG(kind, ...) GNUNET_log_from (kind, "transport-testing2", __VA_ARGS__) + +struct MyClient +{ + struct MyClient *prev; + struct MyClient *next; + /** + * @brief Handle to the client + */ + struct GNUNET_SERVICE_Client *client; + + /** + * @brief Handle to the client + */ + struct GNUNET_MQ_Handle *c_mq; + + /** + * The TCH + */ + struct GNUNET_TRANSPORT_TESTING_TransportCommunicatorHandle *tc; + +}; + +/** + * @brief Queue of a communicator and some context + */ +struct GNUNET_TRANSPORT_TESTING_TransportCommunicatorQueue +{ + /** + * @brief Handle to the TransportCommunicator + */ + struct GNUNET_TRANSPORT_TESTING_TransportCommunicatorHandle *tc_h; + + /** + * @brief Envelope to a message that requests the opening of the queue. + * + * If the client already requests queue(s), but the communicator is not yet + * connected, we cannot send the request to open the queue. Save it until the + * communicator becomes available and send it then. + */ + struct GNUNET_MQ_Envelope *open_queue_env; + + /** + * @brief Peer ID of the peer on the other side of the queue + */ + struct GNUNET_PeerIdentity peer_id; + + /** + * @brief Queue ID + */ + uint32_t qid; + + /** + * @brief Current message id + */ + uint64_t mid; + + /** + * An `enum GNUNET_NetworkType` in NBO. + */ + uint32_t nt; + + /** + * Maximum transmission unit. UINT32_MAX for unlimited. + */ + uint32_t mtu; + + /** + * Queue length. UINT64_MAX for unlimited. + */ + uint64_t q_len; + + /** + * Queue prio + */ + uint32_t priority; + + /** + * An `enum GNUNET_TRANSPORT_ConnectionStatus` in NBO. + */ + uint32_t cs; + + /** + * @brief Next element inside a DLL + */ + struct GNUNET_TRANSPORT_TESTING_TransportCommunicatorQueue *next; + + /** + * @brief Previous element inside a DLL + */ + struct GNUNET_TRANSPORT_TESTING_TransportCommunicatorQueue *prev; +}; + + +/** + * @brief Handle/Context to a single transmission + */ +struct GNUNET_TRANSPORT_TESTING_TransportCommunicatorTransmission +{ +}; + + +/** + * @brief Check whether incoming msg indicating available communicator is + * correct + * + * @param cls Closure + * @param msg Message struct + * + * @return GNUNET_YES in case message is correct + */ +static int +check_communicator_available ( + void *cls, + const struct GNUNET_TRANSPORT_CommunicatorAvailableMessage *msg) +{ + uint16_t size; + + size = ntohs (msg->header.size) - sizeof(*msg); + if (0 == size) + return GNUNET_OK; /* receive-only communicator */ + GNUNET_MQ_check_zero_termination (msg); + return GNUNET_OK; +} + + +/** + * @brief Handle new communicator + * + * Store characteristics of communicator, call respective client callback. + * + * @param cls Closure - communicator handle + * @param msg Message struct + */ +static void +handle_communicator_available ( + void *cls, + const struct GNUNET_TRANSPORT_CommunicatorAvailableMessage *msg) +{ + struct MyClient *client = cls; + struct GNUNET_TRANSPORT_TESTING_TransportCommunicatorHandle *tc_h = + client->tc; + uint16_t size; + tc_h->c_mq = client->c_mq; + + size = ntohs (msg->header.size) - sizeof(*msg); + if (0 == size) + { + GNUNET_SERVICE_client_continue (client->client); + return; /* receive-only communicator */ + } + tc_h->c_characteristics = ntohl (msg->cc); + tc_h->c_addr_prefix = GNUNET_strdup ((const char *) &msg[1]); + if (NULL != tc_h->communicator_available_cb) + { + LOG (GNUNET_ERROR_TYPE_DEBUG, "calling communicator_available_cb()\n"); + tc_h->communicator_available_cb (tc_h->cb_cls, + tc_h, + tc_h->c_characteristics, + tc_h->c_addr_prefix); + } + GNUNET_SERVICE_client_continue (client->client); + LOG (GNUNET_ERROR_TYPE_DEBUG, "finished communicator_available_cb()\n"); + +} + + +/** + * Incoming message. Test message is well-formed. + * + * @param cls the client + * @param msg the send message that was sent + * @return #GNUNET_OK if message is well-formed + */ +static int +check_communicator_backchannel (void *cls, + const struct + GNUNET_TRANSPORT_CommunicatorBackchannel *msg) +{ + // struct TransportClient *tc = cls; + + // if (CT_COMMUNICATOR != tc->type) + // { + // GNUNET_break (0); + // return GNUNET_SYSERR; + // } + // GNUNET_MQ_check_boxed_message (msg); + return GNUNET_OK; +} + + +/** + * @brief Receive an incoming message. + * + * Pass the message to the client. + * + * @param cls Closure - communicator handle + * @param bc_msg Message + */ +static void +handle_communicator_backchannel (void *cls, + const struct + GNUNET_TRANSPORT_CommunicatorBackchannel * + bc_msg) +{ + struct MyClient *client = cls; + struct GNUNET_TRANSPORT_TESTING_TransportCommunicatorHandle *tc_h = + client->tc; + struct GNUNET_TRANSPORT_TESTING_TransportCommunicatorHandle *other_tc_h; + struct GNUNET_MessageHeader *msg; + msg = (struct GNUNET_MessageHeader *) &bc_msg[1]; + uint16_t isize = ntohs (msg->size); + const char *target_communicator = ((const char *) msg) + isize; + struct GNUNET_TRANSPORT_CommunicatorBackchannelIncoming *cbi; + struct GNUNET_MQ_Envelope *env; + + LOG (GNUNET_ERROR_TYPE_DEBUG, + "Received backchannel message\n"); + if (tc_h->bc_enabled != GNUNET_YES) + { + GNUNET_SERVICE_client_continue (client->client); + return; + } + /* Find client providing this communicator */ + /* Finally, deliver backchannel message to communicator */ + LOG (GNUNET_ERROR_TYPE_DEBUG, + "Delivering backchannel message of type %u to %s\n", + ntohs (msg->type), + target_communicator); + other_tc_h = tc_h->bc_cb (tc_h, msg, (struct + GNUNET_PeerIdentity*) &bc_msg->pid); + env = GNUNET_MQ_msg_extra ( + cbi, + isize, + GNUNET_MESSAGE_TYPE_TRANSPORT_COMMUNICATOR_BACKCHANNEL_INCOMING); + cbi->pid = tc_h->peer_id; + memcpy (&cbi[1], msg, isize); + + + GNUNET_MQ_send (other_tc_h->c_mq, env); + GNUNET_SERVICE_client_continue (client->client); +} + + +/** + * Address of our peer added. Test message is well-formed. + * + * @param cls the client + * @param msg the send message that was sent + * @return #GNUNET_OK if message is well-formed + */ +static int +check_add_address (void *cls, + const struct GNUNET_TRANSPORT_AddAddressMessage *msg) +{ + // if (CT_COMMUNICATOR != tc->type) + // { + // GNUNET_break (0); + // return GNUNET_SYSERR; + // } + GNUNET_MQ_check_zero_termination (msg); + return GNUNET_OK; +} + + +/** + * @brief The communicator informs about an address. + * + * Store address and call client callback. + * + * @param cls Closure - communicator handle + * @param msg Message + */ +static void +handle_add_address (void *cls, + const struct GNUNET_TRANSPORT_AddAddressMessage *msg) +{ + struct MyClient *client = cls; + struct GNUNET_TRANSPORT_TESTING_TransportCommunicatorHandle *tc_h = + client->tc; + uint16_t size; + size = ntohs (msg->header.size) - sizeof(*msg); + LOG (GNUNET_ERROR_TYPE_DEBUG, "received add address cb %u\n", size); + if (0 == size) + return; /* receive-only communicator */ + LOG (GNUNET_ERROR_TYPE_DEBUG, "received add address cb %u\n", size); + tc_h->c_address = GNUNET_strdup ((const char *) &msg[1]); + if (NULL != tc_h->add_address_cb) + { + LOG (GNUNET_ERROR_TYPE_DEBUG, "calling add_address_cb()\n"); + tc_h->add_address_cb (tc_h->cb_cls, + tc_h, + tc_h->c_address, + GNUNET_TIME_relative_ntoh (msg->expiration), + msg->aid, + ntohl (msg->nt)); + } + GNUNET_SERVICE_client_continue (client->client); +} + + +/** + * Incoming message. Test message is well-formed. + * + * @param cls the client + * @param msg the send message that was sent + * @return #GNUNET_OK if message is well-formed + */ +static int +check_incoming_msg (void *cls, + const struct GNUNET_TRANSPORT_IncomingMessage *msg) +{ + // struct TransportClient *tc = cls; + + // if (CT_COMMUNICATOR != tc->type) + // { + // GNUNET_break (0); + // return GNUNET_SYSERR; + // } + GNUNET_MQ_check_boxed_message (msg); + return GNUNET_OK; +} + + +/** + * @brief Receive an incoming message. + * + * Pass the message to the client. + * + * @param cls Closure - communicator handle + * @param inc_msg Message + */ +static void +handle_incoming_msg (void *cls, + const struct GNUNET_TRANSPORT_IncomingMessage *inc_msg) +{ + struct MyClient *client = cls; + struct GNUNET_TRANSPORT_TESTING_TransportCommunicatorHandle *tc_h = + client->tc; + struct GNUNET_MessageHeader *msg; + msg = (struct GNUNET_MessageHeader *) &inc_msg[1]; + size_t payload_len = ntohs (msg->size) - sizeof (struct + GNUNET_MessageHeader); + if (NULL != tc_h->incoming_msg_cb) + { + tc_h->incoming_msg_cb (tc_h->cb_cls, + tc_h, + (char*) &msg[1], + payload_len); + } + else + { + LOG (GNUNET_ERROR_TYPE_WARNING, + "Incoming message from communicator but no handler!\n"); + } + if (GNUNET_YES == ntohl (inc_msg->fc_on)) + { + /* send ACK when done to communicator for flow control! */ + struct GNUNET_MQ_Envelope *env; + struct GNUNET_TRANSPORT_IncomingMessageAck *ack; + + env = GNUNET_MQ_msg (ack, GNUNET_MESSAGE_TYPE_TRANSPORT_INCOMING_MSG_ACK); + GNUNET_assert (NULL != env); + ack->reserved = htonl (0); + ack->fc_id = inc_msg->fc_id; + ack->sender = inc_msg->sender; + GNUNET_MQ_send (tc_h->c_mq, env); + } + + GNUNET_SERVICE_client_continue (client->client); +} + + +/** + * @brief Communicator informs that it tries to establish requested queue + * + * @param cls Closure - communicator handle + * @param msg Message + */ +static void +handle_queue_create_ok (void *cls, + const struct GNUNET_TRANSPORT_CreateQueueResponse *msg) +{ + struct MyClient *client = cls; + struct GNUNET_TRANSPORT_TESTING_TransportCommunicatorHandle *tc_h = + client->tc; + + if (NULL != tc_h->queue_create_reply_cb) + { + tc_h->queue_create_reply_cb (tc_h->cb_cls, tc_h, GNUNET_YES); + } + GNUNET_SERVICE_client_continue (client->client); +} + + +/** + * @brief Communicator informs that it won't try establishing requested queue. + * + * It will not do so probably because the address is bougus (see comment to + * #GNUNET_MESSAGE_TYPE_TRANSPORT_QUEUE_CREATE_FAIL) + * + * @param cls Closure - communicator handle + * @param msg Message + */ +static void +handle_queue_create_fail ( + void *cls, + const struct GNUNET_TRANSPORT_CreateQueueResponse *msg) +{ + struct MyClient *client = cls; + struct GNUNET_TRANSPORT_TESTING_TransportCommunicatorHandle *tc_h = + client->tc; + + if (NULL != tc_h->queue_create_reply_cb) + { + tc_h->queue_create_reply_cb (tc_h->cb_cls, tc_h, GNUNET_NO); + } + GNUNET_SERVICE_client_continue (client->client); +} + + +/** + * New queue became available. Check message. + * + * @param cls the client + * @param aqm the send message that was sent + */ +static int +check_add_queue_message (void *cls, + const struct GNUNET_TRANSPORT_AddQueueMessage *aqm) +{ + GNUNET_MQ_check_zero_termination (aqm); + return GNUNET_OK; +} + + +/** + * @brief Handle new queue + * + * Store context and call client callback. + * + * @param cls Closure - communicator handle + * @param msg Message struct + */ +static void +handle_add_queue_message (void *cls, + const struct GNUNET_TRANSPORT_AddQueueMessage *msg) +{ + struct MyClient *client = cls; + struct GNUNET_TRANSPORT_TESTING_TransportCommunicatorHandle *tc_h = + client->tc; + struct GNUNET_TRANSPORT_TESTING_TransportCommunicatorQueue *tc_queue; + + LOG (GNUNET_ERROR_TYPE_DEBUG, + "Got queue with ID %u\n", msg->qid); + for (tc_queue = tc_h->queue_head; NULL != tc_queue; tc_queue = tc_queue->next) + { + if (tc_queue->qid == msg->qid) + break; + } + if (NULL == tc_queue) + { + tc_queue = + GNUNET_new (struct GNUNET_TRANSPORT_TESTING_TransportCommunicatorQueue); + tc_queue->tc_h = tc_h; + tc_queue->qid = msg->qid; + tc_queue->peer_id = msg->receiver; + GNUNET_CONTAINER_DLL_insert (tc_h->queue_head, tc_h->queue_tail, tc_queue); + } + GNUNET_assert (tc_queue->qid == msg->qid); + GNUNET_assert (0 == GNUNET_memcmp (&tc_queue->peer_id, &msg->receiver)); + tc_queue->nt = msg->nt; + tc_queue->mtu = ntohl (msg->mtu); + tc_queue->cs = msg->cs; + tc_queue->priority = ntohl (msg->priority); + tc_queue->q_len = GNUNET_ntohll (msg->q_len); + if (NULL != tc_h->add_queue_cb) + { + tc_h->add_queue_cb (tc_h->cb_cls, tc_h, tc_queue, tc_queue->mtu); + } + GNUNET_SERVICE_client_continue (client->client); +} + + +/** + * @brief Handle new queue + * + * Store context and call client callback. + * + * @param cls Closure - communicator handle + * @param msg Message struct + */ +static void +handle_update_queue_message (void *cls, + const struct + GNUNET_TRANSPORT_UpdateQueueMessage *msg) +{ + struct MyClient *client = cls; + struct GNUNET_TRANSPORT_TESTING_TransportCommunicatorHandle *tc_h = + client->tc; + struct GNUNET_TRANSPORT_TESTING_TransportCommunicatorQueue *tc_queue; + + LOG (GNUNET_ERROR_TYPE_DEBUG, + "Received queue update message for %u with q_len %" PRIu64 "\n", + msg->qid, GNUNET_ntohll (msg->q_len)); + tc_queue = tc_h->queue_head; + if (NULL != tc_queue) + { + while (tc_queue->qid != msg->qid) + { + tc_queue = tc_queue->next; + } + } + if (NULL == tc_queue) + { + GNUNET_SERVICE_client_continue (client->client); + return; + } + GNUNET_assert (tc_queue->qid == msg->qid); + GNUNET_assert (0 == GNUNET_memcmp (&tc_queue->peer_id, &msg->receiver)); + tc_queue->nt = msg->nt; + tc_queue->mtu = ntohl (msg->mtu); + tc_queue->cs = msg->cs; + tc_queue->priority = ntohl (msg->priority); + // Uncomment this for alternativ 1 of backchannel functionality + tc_queue->q_len += GNUNET_ntohll (msg->q_len); + // Until here for alternativ 1 + // Uncomment this for alternativ 2 of backchannel functionality + // tc_queue->q_len = GNUNET_ntohll (msg->q_len); + // Until here for alternativ 2 + GNUNET_SERVICE_client_continue (client->client); +} + + +/** + * @brief Shut down the service + * + * @param cls Closure - Handle to the service + */ +static void +shutdown_service (void *cls) +{ + struct GNUNET_SERVICE_Handle *h = cls; + + LOG (GNUNET_ERROR_TYPE_DEBUG, + "Shutting down service!\n"); + + GNUNET_SERVICE_stop (h); +} + + +/** + * @brief Callback called when new Client (Communicator) connects + * + * @param cls Closure - TransporCommmunicator Handle + * @param client Client + * @param mq Messagequeue + * + * @return TransportCommunicator Handle + */ +static void * +connect_cb (void *cls, + struct GNUNET_SERVICE_Client *client, + struct GNUNET_MQ_Handle *mq) +{ + struct GNUNET_TRANSPORT_TESTING_TransportCommunicatorHandle *tc_h = cls; + struct MyClient *new_c; + + LOG (GNUNET_ERROR_TYPE_DEBUG, "Client %p connected to %p.\n", + client, tc_h); + new_c = GNUNET_new (struct MyClient); + new_c->client = client; + new_c->c_mq = mq; + new_c->tc = tc_h; + GNUNET_CONTAINER_DLL_insert (tc_h->client_head, + tc_h->client_tail, + new_c); + + if (NULL == tc_h->queue_head) + return new_c; + /* Iterate over queues. They are yet to be opened. Request opening. */ + for (struct + GNUNET_TRANSPORT_TESTING_TransportCommunicatorQueue *tc_queue_iter = + tc_h->queue_head; + NULL != tc_queue_iter; + tc_queue_iter = tc_queue_iter->next) + { + if (NULL == tc_queue_iter->open_queue_env) + continue; + /* Send the previously created mq envelope to request the creation of the + * queue. */ + GNUNET_MQ_send (tc_h->c_mq, + tc_queue_iter->open_queue_env); + tc_queue_iter->open_queue_env = NULL; + } + return new_c; +} + + +/** + * @brief Callback called when Client disconnects + * + * @param cls Closure - TransportCommunicator Handle + * @param client Client + * @param internal_cls TransporCommmunicator Handle + */ +static void +disconnect_cb (void *cls, + struct GNUNET_SERVICE_Client *client, + void *internal_cls) +{ + struct MyClient *cl = cls; + struct GNUNET_TRANSPORT_TESTING_TransportCommunicatorHandle *tc_h = cls; + + for (cl = tc_h->client_head; NULL != cl; cl = cl->next) + { + if (cl->client != client) + continue; + GNUNET_CONTAINER_DLL_remove (tc_h->client_head, + tc_h->client_tail, + cl); + if (cl->c_mq == tc_h->c_mq) + tc_h->c_mq = NULL; + GNUNET_free (cl); + break; + } + LOG (GNUNET_ERROR_TYPE_DEBUG, "Client disconnected.\n"); +} + + +/** + * Message was transmitted. Process the request. + * + * @param cls the client + * @param sma the send message that was sent + */ +static void +handle_send_message_ack (void *cls, + const struct GNUNET_TRANSPORT_SendMessageToAck *sma) +{ + struct MyClient *client = cls; + struct GNUNET_TRANSPORT_TESTING_TransportCommunicatorHandle *tc_h = + client->tc; + static int mtr = 0; + mtr++; + if (tc_h->cont != NULL) + tc_h->cont (tc_h->cont_cls); + GNUNET_SERVICE_client_continue (client->client); +} + + +/** + * @brief Start the communicator part of the transport service + * + * @param communicator_available Callback to be called when a new communicator + * becomes available + * @param cfg Configuration + */ +static void +transport_communicator_start ( + struct GNUNET_TRANSPORT_TESTING_TransportCommunicatorHandle *tc_h) +{ + struct GNUNET_MQ_MessageHandler mh[] = { + GNUNET_MQ_hd_var_size (communicator_available, + GNUNET_MESSAGE_TYPE_TRANSPORT_NEW_COMMUNICATOR, + struct GNUNET_TRANSPORT_CommunicatorAvailableMessage, + tc_h), + GNUNET_MQ_hd_var_size (communicator_backchannel, + GNUNET_MESSAGE_TYPE_TRANSPORT_COMMUNICATOR_BACKCHANNEL, + struct GNUNET_TRANSPORT_CommunicatorBackchannel, + tc_h), + GNUNET_MQ_hd_var_size (add_address, + GNUNET_MESSAGE_TYPE_TRANSPORT_ADD_ADDRESS, + struct GNUNET_TRANSPORT_AddAddressMessage, + tc_h), + // GNUNET_MQ_hd_fixed_size (del_address, + // GNUNET_MESSAGE_TYPE_TRANSPORT_DEL_ADDRESS, + // struct GNUNET_TRANSPORT_DelAddressMessage, + // NULL), + GNUNET_MQ_hd_var_size (incoming_msg, + GNUNET_MESSAGE_TYPE_TRANSPORT_INCOMING_MSG, + struct GNUNET_TRANSPORT_IncomingMessage, + tc_h), + GNUNET_MQ_hd_fixed_size (queue_create_ok, + GNUNET_MESSAGE_TYPE_TRANSPORT_QUEUE_CREATE_OK, + struct GNUNET_TRANSPORT_CreateQueueResponse, + tc_h), + GNUNET_MQ_hd_fixed_size (queue_create_fail, + GNUNET_MESSAGE_TYPE_TRANSPORT_QUEUE_CREATE_FAIL, + struct GNUNET_TRANSPORT_CreateQueueResponse, + tc_h), + GNUNET_MQ_hd_var_size (add_queue_message, + GNUNET_MESSAGE_TYPE_TRANSPORT_QUEUE_SETUP, + struct GNUNET_TRANSPORT_AddQueueMessage, + tc_h), + GNUNET_MQ_hd_fixed_size (update_queue_message, + GNUNET_MESSAGE_TYPE_TRANSPORT_QUEUE_UPDATE, + struct GNUNET_TRANSPORT_UpdateQueueMessage, + tc_h), + // GNUNET_MQ_hd_fixed_size (del_queue_message, + // GNUNET_MESSAGE_TYPE_TRANSPORT_QUEUE_TEARDOWN, + // struct GNUNET_TRANSPORT_DelQueueMessage, + // NULL), + GNUNET_MQ_hd_fixed_size (send_message_ack, + GNUNET_MESSAGE_TYPE_TRANSPORT_SEND_MSG_ACK, + struct GNUNET_TRANSPORT_SendMessageToAck, + tc_h), + GNUNET_MQ_handler_end () + }; + + + tc_h->sh = GNUNET_SERVICE_start ("transport", + tc_h->cfg, + &connect_cb, + &disconnect_cb, + tc_h, + mh); + GNUNET_assert (NULL != tc_h->sh); +} + + +/** + * @brief Task run at shutdown to kill communicator and clean up + * + * @param cls Closure - Process of communicator + */ +static void +shutdown_process (struct GNUNET_OS_Process *proc) +{ + if (0 != GNUNET_OS_process_kill (proc, SIGTERM)) + { + LOG (GNUNET_ERROR_TYPE_WARNING, + "Error shutting down process with SIGERM, trying SIGKILL\n"); + if (0 != GNUNET_OS_process_kill (proc, SIGKILL)) + { + LOG (GNUNET_ERROR_TYPE_ERROR, + "Error shutting down process with SIGERM and SIGKILL\n"); + } + } + GNUNET_OS_process_destroy (proc); +} + + +/** + * @brief Task run at shutdown to kill the statistics process + * + * @param cls Closure - Process of communicator + */ +static void +shutdown_statistics (void *cls) +{ + struct GNUNET_OS_Process *proc = cls; + shutdown_process (proc); +} + + +/** + * @brief Task run at shutdown to kill the peerstore process + * + * @param cls Closure - Process of communicator + */ +static void +shutdown_peerstore (void *cls) +{ + struct GNUNET_OS_Process *proc = cls; + shutdown_process (proc); +} + + +/** + * @brief Task run at shutdown to kill a communicator process + * + * @param cls Closure - Process of communicator + */ +static void +shutdown_communicator (void *cls) +{ + struct GNUNET_OS_Process *proc = cls; + shutdown_process (proc); +} + + +/** + * @brief Start the communicator + * + * @param cfgname Name of the communicator + */ +static void +communicator_start ( + struct GNUNET_TRANSPORT_TESTING_TransportCommunicatorHandle *tc_h, + const char *binary_name) +{ + char *binary; + char *loprefix; + char *section_name; + + LOG (GNUNET_ERROR_TYPE_DEBUG, "communicator_start\n"); + + section_name = strchr (binary_name, '-'); + section_name++; + + if (GNUNET_OK != GNUNET_CONFIGURATION_get_value_string (tc_h->cfg, + section_name, + "PREFIX", + &loprefix)) + loprefix = GNUNET_strdup (""); + + + binary = GNUNET_OS_get_libexec_binary_path (binary_name); + tc_h->c_proc = GNUNET_OS_start_process_s (GNUNET_OS_INHERIT_STD_OUT_AND_ERR, + NULL, + loprefix, + binary, + binary_name, + "-c", + tc_h->cfg_filename, + NULL); + if (NULL == tc_h->c_proc) + { + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Failed to start communicator!"); + return; + } + LOG (GNUNET_ERROR_TYPE_INFO, "started communicator\n"); + GNUNET_free (binary); +} + + +/** + * @brief Task run at shutdown to kill communicator and clean up + * + * @param cls Closure - Process of communicator + */ +static void +shutdown_nat (void *cls) +{ + struct GNUNET_OS_Process *proc = cls; + shutdown_process (proc); +} + + +/** + * @brief Task run at shutdown to kill the resolver process + * + * @param cls Closure - Process of communicator + */ +static void +shutdown_resolver (void *cls) +{ + struct GNUNET_OS_Process *proc = cls; + shutdown_process (proc); +} + + +/** + * @brief Start Resolver + * + */ +static void +resolver_start (struct + GNUNET_TRANSPORT_TESTING_TransportCommunicatorHandle *tc_h) +{ + char *binary; + + LOG (GNUNET_ERROR_TYPE_DEBUG, "resolver_start\n"); + binary = GNUNET_OS_get_libexec_binary_path ("gnunet-service-resolver"); + tc_h->resolver_proc = GNUNET_OS_start_process ( + GNUNET_OS_INHERIT_STD_OUT_AND_ERR + | GNUNET_OS_USE_PIPE_CONTROL, + NULL, + NULL, + NULL, + binary, + "gnunet-service-resolver", + "-c", + tc_h->cfg_filename, + NULL); + if (NULL == tc_h->resolver_proc) + { + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Failed to start resolver service!"); + return; + } + LOG (GNUNET_ERROR_TYPE_INFO, "started resolver service\n"); + GNUNET_free (binary); + +} + + +/** + * @brief Start Statistics + * + */ +static void +statistics_start ( + struct GNUNET_TRANSPORT_TESTING_TransportCommunicatorHandle *tc_h) +{ + char *binary; + + binary = GNUNET_OS_get_libexec_binary_path ("gnunet-service-statistics"); + tc_h->stat_proc = GNUNET_OS_start_process (GNUNET_OS_INHERIT_STD_OUT_AND_ERR, + NULL, + NULL, + NULL, + binary, + "gnunet-service-statistics", + "-c", + tc_h->cfg_filename, + NULL); + if (NULL == tc_h->stat_proc) + { + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Failed to start Statistics!"); + return; + } + LOG (GNUNET_ERROR_TYPE_INFO, "started Statistics\n"); + GNUNET_free (binary); +} + + +/** + * @brief Start Peerstore + * + */ +static void +peerstore_start ( + struct GNUNET_TRANSPORT_TESTING_TransportCommunicatorHandle *tc_h) +{ + char *binary; + + binary = GNUNET_OS_get_libexec_binary_path ("gnunet-service-peerstore"); + tc_h->ps_proc = GNUNET_OS_start_process (GNUNET_OS_INHERIT_STD_OUT_AND_ERR, + NULL, + NULL, + NULL, + binary, + "gnunet-service-peerstore", + "-c", + tc_h->cfg_filename, + NULL); + if (NULL == tc_h->ps_proc) + { + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Failed to start Peerstore!"); + return; + } + LOG (GNUNET_ERROR_TYPE_INFO, "started Peerstore\n"); + GNUNET_free (binary); +} + + +/** + * @brief Start NAT + * + */ +static void +nat_start ( + struct GNUNET_TRANSPORT_TESTING_TransportCommunicatorHandle *tc_h) +{ + char *binary; + + LOG (GNUNET_ERROR_TYPE_DEBUG, "nat_start\n"); + binary = GNUNET_OS_get_libexec_binary_path ("gnunet-service-nat"); + tc_h->nat_proc = GNUNET_OS_start_process (GNUNET_OS_INHERIT_STD_OUT_AND_ERR + | GNUNET_OS_USE_PIPE_CONTROL, + NULL, + NULL, + NULL, + binary, + "gnunet-service-nat", + "-c", + tc_h->cfg_filename, + NULL); + if (NULL == tc_h->nat_proc) + { + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Failed to start NAT!"); + return; + } + LOG (GNUNET_ERROR_TYPE_INFO, "started NAT\n"); + GNUNET_free (binary); +} + + +/** + * @brief Start communicator part of transport service and communicator + * + * @param service_name Name of the service + * @param cfg Configuration handle + * @param communicator_available_cb Callback that is called when a new + * @param add_address_cb Callback that is called when a new + * communicator becomes available + * @param cb_cls Closure to @a communicator_available_cb and @a + * + * @return Handle to the communicator duo + */ +struct GNUNET_TRANSPORT_TESTING_TransportCommunicatorHandle * +GNUNET_TRANSPORT_TESTING_transport_communicator_service_start ( + const char *service_name, + const char *binary_name, + const char *cfg_filename, + const struct GNUNET_PeerIdentity *peer_id, + GNUNET_TRANSPORT_TESTING_CommunicatorAvailableCallback + communicator_available_cb, + GNUNET_TRANSPORT_TESTING_AddAddressCallback add_address_cb, + GNUNET_TRANSPORT_TESTING_QueueCreateReplyCallback queue_create_reply_cb, + GNUNET_TRANSPORT_TESTING_AddQueueCallback add_queue_cb, + GNUNET_TRANSPORT_TESTING_IncomingMessageCallback incoming_message_cb, + GNUNET_TRANSPORT_TESTING_BackchannelCallback bc_cb, + void *cb_cls) +{ + struct GNUNET_TRANSPORT_TESTING_TransportCommunicatorHandle *tc_h; + + LOG (GNUNET_ERROR_TYPE_DEBUG, + "Starting new transport/communicator combo with config %s\n", + cfg_filename); + tc_h = + GNUNET_new (struct GNUNET_TRANSPORT_TESTING_TransportCommunicatorHandle); + tc_h->cfg_filename = GNUNET_strdup (cfg_filename); + tc_h->cfg = GNUNET_CONFIGURATION_create (); + if ((GNUNET_SYSERR == GNUNET_CONFIGURATION_load (tc_h->cfg, cfg_filename))) + { + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + _ ("Malformed configuration file `%s', exit ...\n"), + cfg_filename); + GNUNET_free (tc_h->cfg_filename); + GNUNET_CONFIGURATION_destroy (tc_h->cfg); + GNUNET_free (tc_h); + return NULL; + } + tc_h->bc_enabled = GNUNET_CONFIGURATION_get_value_yesno (tc_h->cfg, + "communicator-test", + "BACKCHANNEL_ENABLED"); + tc_h->communicator_available_cb = communicator_available_cb; + tc_h->add_address_cb = add_address_cb; + tc_h->queue_create_reply_cb = queue_create_reply_cb; + tc_h->add_queue_cb = add_queue_cb; + tc_h->incoming_msg_cb = incoming_message_cb; + tc_h->bc_cb = bc_cb; + tc_h->peer_id = *peer_id; + tc_h->cb_cls = cb_cls; + + /* Start communicator part of service */ + transport_communicator_start (tc_h); + /* Start NAT */ + nat_start (tc_h); + /* Start resolver service */ + resolver_start (tc_h); + /* Start peerstore service */ + peerstore_start (tc_h); + /* Start statistic service */ + statistics_start (tc_h); + /* Schedule start communicator */ + communicator_start (tc_h, + binary_name); + return tc_h; +} + + +void +GNUNET_TRANSPORT_TESTING_transport_communicator_service_stop ( + struct GNUNET_TRANSPORT_TESTING_TransportCommunicatorHandle *tc_h) +{ + shutdown_communicator (tc_h->c_proc); + shutdown_service (tc_h->sh); + shutdown_nat (tc_h->nat_proc); + shutdown_resolver (tc_h->resolver_proc); + shutdown_peerstore (tc_h->ps_proc); + shutdown_statistics (tc_h->stat_proc); + GNUNET_CONFIGURATION_destroy (tc_h->cfg); + GNUNET_free (tc_h); +} + + +/** + * @brief Instruct communicator to open a queue + * + * @param tc_h Handle to communicator which shall open queue + * @param peer_id Towards which peer + * @param address For which address + */ +void +GNUNET_TRANSPORT_TESTING_transport_communicator_open_queue ( + struct GNUNET_TRANSPORT_TESTING_TransportCommunicatorHandle *tc_h, + const struct GNUNET_PeerIdentity *peer_id, + const char *address) +{ + struct GNUNET_TRANSPORT_TESTING_TransportCommunicatorQueue *tc_queue; + static uint32_t idgen; + char *prefix; + struct GNUNET_TRANSPORT_CreateQueue *msg; + struct GNUNET_MQ_Envelope *env; + size_t alen; + + tc_queue = + GNUNET_new (struct GNUNET_TRANSPORT_TESTING_TransportCommunicatorQueue); + tc_queue->tc_h = tc_h; + prefix = GNUNET_HELLO_address_to_prefix (address); + if (NULL == prefix) + { + GNUNET_break (0); /* We got an invalid address!? */ + GNUNET_free (tc_queue); + return; + } + GNUNET_free (prefix); + alen = strlen (address) + 1; + env = + GNUNET_MQ_msg_extra (msg, alen, GNUNET_MESSAGE_TYPE_TRANSPORT_QUEUE_CREATE); + msg->request_id = htonl (idgen++); + tc_queue->qid = msg->request_id; + msg->receiver = *peer_id; + tc_queue->peer_id = *peer_id; + memcpy (&msg[1], address, alen); + if (NULL != tc_h->c_mq) + { + LOG (GNUNET_ERROR_TYPE_DEBUG, + "Sending queue create immediately\n"); + GNUNET_MQ_send (tc_h->c_mq, env); + } + else + { + tc_queue->open_queue_env = env; + } + GNUNET_CONTAINER_DLL_insert (tc_h->queue_head, tc_h->queue_tail, tc_queue); +} + + +void +GNUNET_TRANSPORT_TESTING_transport_communicator_send + (struct GNUNET_TRANSPORT_TESTING_TransportCommunicatorHandle *tc_h, + GNUNET_SCHEDULER_TaskCallback cont, + void *cont_cls, + const void *payload, + size_t payload_size) +{ + struct GNUNET_MessageHeader *mh; + struct GNUNET_TRANSPORT_SendMessageTo *msg; + struct GNUNET_MQ_Envelope *env; + size_t inbox_size; + struct GNUNET_TRANSPORT_TESTING_TransportCommunicatorQueue *tc_queue; + struct GNUNET_TRANSPORT_TESTING_TransportCommunicatorQueue *tc_queue_tmp; + static struct GNUNET_TRANSPORT_TESTING_TransportCommunicatorQueue *last_queue; + tc_queue = NULL; + + for (tc_queue_tmp = tc_h->queue_head; + NULL != tc_queue_tmp; + tc_queue_tmp = tc_queue_tmp->next) + { + if (tc_queue_tmp->q_len <= 0) + continue; + if (NULL == tc_queue) + { + LOG (GNUNET_ERROR_TYPE_DEBUG, + "Selecting queue with prio %u, len %" PRIu64 " and MTU %u\n", + tc_queue_tmp->priority, + tc_queue_tmp->q_len, + tc_queue_tmp->mtu); + tc_queue = tc_queue_tmp; + continue; + } + if (tc_queue->priority < tc_queue_tmp->priority) + { + LOG (GNUNET_ERROR_TYPE_DEBUG, + "Selecting queue with prio %u, len %" PRIu64 " and MTU %u\n", + tc_queue_tmp->priority, + tc_queue_tmp->q_len, + tc_queue_tmp->mtu); + tc_queue = tc_queue_tmp; + } + } + if (last_queue != tc_queue) + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Selected sending queue changed to %u with length %lu and MTU %u\n", + ntohl (tc_queue->qid), (unsigned long) tc_queue->q_len, tc_queue->mtu); + GNUNET_assert (NULL != tc_queue); + last_queue = tc_queue; + // Uncomment this for alternativ 1 of backchannel functionality + if (tc_queue->q_len != GNUNET_TRANSPORT_QUEUE_LENGTH_UNLIMITED) + tc_queue->q_len--; + // Until here for alternativ 1 + static int msg_count = 0; + msg_count++; + if (msg_count % 100 == 0) + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Sending %u-th (%lu-th for queue) message on queue %u\n", + msg_count, (unsigned long) tc_queue->mid, ntohl (tc_queue->qid)); + inbox_size = sizeof (struct GNUNET_MessageHeader) + payload_size; + env = GNUNET_MQ_msg_extra (msg, + inbox_size, + GNUNET_MESSAGE_TYPE_TRANSPORT_SEND_MSG); + GNUNET_assert (NULL != env); + msg->qid = tc_queue->qid; + msg->mid = tc_queue->mid++; + msg->receiver = tc_queue->peer_id; + mh = (struct GNUNET_MessageHeader *) &msg[1]; + mh->size = htons (inbox_size); + mh->type = GNUNET_MESSAGE_TYPE_DUMMY; + memcpy (&mh[1], + payload, + payload_size); + if (NULL != cont) + GNUNET_MQ_notify_sent (env, + cont, + cont_cls); + GNUNET_MQ_send (tc_queue->tc_h->c_mq, + env); +} diff --git a/src/service/transport/transport-testing-communicator.h b/src/service/transport/transport-testing-communicator.h new file mode 100644 index 000000000..7460aab8e --- /dev/null +++ b/src/service/transport/transport-testing-communicator.h @@ -0,0 +1,370 @@ +/* + This file is part of GNUnet. + Copyright (C) 2019 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 . + + SPDX-License-Identifier: AGPL3.0-or-later + */ + +/** + * @file transport/transport-testing-communicator.h + * @brief functions and structures related to testing-tng + * @author Christian Grothoff + * @author Julius Bünger + */ +#include "platform.h" +#include "gnunet_util_lib.h" +#include "gnunet_transport_communication_service.h" +#include "transport.h" + +/** + * @brief Queue of a communicator and some context + */ +struct GNUNET_TRANSPORT_TESTING_TransportCommunicatorQueue; + + +/** + * @brief Handle/Context to a single transmission + */ +struct GNUNET_TRANSPORT_TESTING_TransportCommunicatorTransmission; + +/** + * @brief Function signature for callbacks that are called when new + * backchannel message arrived + * + * @param cls Closure + * @param msg Backchannel message + * @param pid Target peer + */ +typedef struct GNUNET_TRANSPORT_TESTING_TransportCommunicatorHandle * +(*GNUNET_TRANSPORT_TESTING_BackchannelCallback)(void *cls, + struct GNUNET_MessageHeader *msg, + struct GNUNET_PeerIdentity *pid); + + +/** + * @brief Function signature for callbacks that are called when new + * communicators become available + * + * @param cls Closure + * @param tc_h Communicator handle + * @param cc Characteristics of communicator + * @param address_prefix Prefix of the address + */ +typedef void +(*GNUNET_TRANSPORT_TESTING_CommunicatorAvailableCallback)(void *cls, + struct + GNUNET_TRANSPORT_TESTING_TransportCommunicatorHandle + *tc_h, + enum + GNUNET_TRANSPORT_CommunicatorCharacteristics + cc, + char *address_prefix); + + +/** + * @brief Receive information about the address of a communicator. + * + * @param cls Closure + * @param tc_h Communicator handle + * @param address Address represented as string + * @param expiration Expiration + * @param aid Aid + * @param nt Network Type + */ +typedef void +(*GNUNET_TRANSPORT_TESTING_AddAddressCallback)(void *cls, + struct + GNUNET_TRANSPORT_TESTING_TransportCommunicatorHandle + *tc_h, + const char *address, + struct GNUNET_TIME_Relative + expiration, + uint32_t aid, + enum GNUNET_NetworkType nt); + + +/** + * @brief Get informed about the success of a queue request. + * + * @param cls Closure + * @param tc_h Communicator handle + * @param will_try #GNUNET_YES if communicator will try to create queue + */ +typedef void +(*GNUNET_TRANSPORT_TESTING_QueueCreateReplyCallback)(void *cls, + struct + GNUNET_TRANSPORT_TESTING_TransportCommunicatorHandle + *tc_h, + int will_try); + + +/** + * @brief Handle opening of queue + * + * @param cls Closure + * @param tc_h Communicator handle + * @param tc_queue Handle to newly opened queue + */ +typedef void +(*GNUNET_TRANSPORT_TESTING_AddQueueCallback)(void *cls, + struct + GNUNET_TRANSPORT_TESTING_TransportCommunicatorHandle + *tc_h, + struct + GNUNET_TRANSPORT_TESTING_TransportCommunicatorQueue + *tc_queue, + size_t mtu); + + +/** + * @brief Handle an incoming message + * + * @param cls Closure + * @param tc_h Handle to the receiving communicator + * @param msg Received message + */ +typedef void +(*GNUNET_TRANSPORT_TESTING_IncomingMessageCallback)(void *cls, + struct + GNUNET_TRANSPORT_TESTING_TransportCommunicatorHandle + *tc_h, + const char*payload, + size_t payload_len); + +/** + * @brief Handle to a transport communicator + */ +struct GNUNET_TRANSPORT_TESTING_TransportCommunicatorHandle +{ + /** + * Clients + */ + struct MyClient *client_head; + struct MyClient *client_tail; + + /** + * @brief Handle to the client + */ + struct GNUNET_MQ_Handle *c_mq; + + /** + * @brief Handle to the configuration + */ + struct GNUNET_CONFIGURATION_Handle *cfg; + + /** + * @brief File name of configuration file + */ + char *cfg_filename; + + struct GNUNET_PeerIdentity peer_id; + + /** + * @brief Handle to the transport service + */ + struct GNUNET_SERVICE_Handle *tsh; + + /** + * @brief Task that will be run on shutdown to stop and clean transport + * service + */ + struct GNUNET_SCHEDULER_Task *ts_shutdown_task; + + + /** + * @brief Process of the communicator + */ + struct GNUNET_OS_Process *c_proc; + + /** + * NAT process + */ + struct GNUNET_OS_Process *nat_proc; + + /** + * resolver service process + */ + struct GNUNET_OS_Process *resolver_proc; + + /** + * statistics service process + */ + struct GNUNET_OS_Process *stat_proc; + + /** + * peerstore service process + */ + struct GNUNET_OS_Process *ps_proc; + + /** + * @brief Task that will be run on shutdown to stop and clean communicator + */ + struct GNUNET_SCHEDULER_Task *c_shutdown_task; + + /** + * @brief Characteristics of the communicator + */ + enum GNUNET_TRANSPORT_CommunicatorCharacteristics c_characteristics; + + /** + * @brief Specifies supported addresses + */ + char *c_addr_prefix; + + /** + * @brief Specifies supported addresses + */ + char *c_address; + + /** + * @brief Head of the DLL of queues associated with this communicator + */ + struct GNUNET_TRANSPORT_TESTING_TransportCommunicatorQueue *queue_head; + + /** + * @brief Tail of the DLL of queues associated with this communicator + */ + struct GNUNET_TRANSPORT_TESTING_TransportCommunicatorQueue *queue_tail; + + /* Callbacks + Closures */ + /** + * @brief Callback called when a new communicator connects + */ + GNUNET_TRANSPORT_TESTING_CommunicatorAvailableCallback + communicator_available_cb; + + /** + * @brief Callback called when a new communicator connects + */ + GNUNET_TRANSPORT_TESTING_AddAddressCallback add_address_cb; + + /** + * @brief Callback called when a new communicator connects + */ + GNUNET_TRANSPORT_TESTING_QueueCreateReplyCallback queue_create_reply_cb; + + /** + * @brief Callback called when a new communicator connects + */ + GNUNET_TRANSPORT_TESTING_AddQueueCallback add_queue_cb; + + /** + * @brief Callback called when a new communicator connects + */ + GNUNET_TRANSPORT_TESTING_IncomingMessageCallback incoming_msg_cb; + + /** + * @brief Backchannel callback + */ + GNUNET_TRANSPORT_TESTING_BackchannelCallback bc_cb; + + /** + * Our service handle + */ + struct GNUNET_SERVICE_Handle *sh; + + /** + * @brief Closure to the callback + */ + void *cb_cls; + + /** + * Callback to call when message ack received. + */ + GNUNET_SCHEDULER_TaskCallback cont; + + /** + * Closure for cont + */ + void *cont_cls; + + /** + * Backchannel supported + */ + int bc_enabled; +}; + +/** + * @brief Start communicator part of transport service and communicator + * + * @param service_name Name of the service + * @param cfg Configuration handle + * @param communicator_available Callback that is called when a new + * communicator becomes available + * @param add_address_cb Callback handling new addresses + * @param queue_create_reply_cb Callback handling success of queue requests + * @param add_queue_cb Callback handling freshly created queues + * @param incoming_message_cb Callback handling incoming messages + * @param cb_cls Closure to @p communicator_available + * + * @return Handle to the communicator duo + */ +struct GNUNET_TRANSPORT_TESTING_TransportCommunicatorHandle * +GNUNET_TRANSPORT_TESTING_transport_communicator_service_start ( + const char *service_name, + const char *binary_name, + const char *cfg_filename, + const struct GNUNET_PeerIdentity *peer_id, + GNUNET_TRANSPORT_TESTING_CommunicatorAvailableCallback + communicator_available_cb, + GNUNET_TRANSPORT_TESTING_AddAddressCallback add_address_cb, + GNUNET_TRANSPORT_TESTING_QueueCreateReplyCallback queue_create_reply_cb, + GNUNET_TRANSPORT_TESTING_AddQueueCallback add_queue_cb, + GNUNET_TRANSPORT_TESTING_IncomingMessageCallback incoming_message_cb, + GNUNET_TRANSPORT_TESTING_BackchannelCallback bc_cb, + void *cb_cls); + + +void +GNUNET_TRANSPORT_TESTING_transport_communicator_service_stop ( + struct GNUNET_TRANSPORT_TESTING_TransportCommunicatorHandle *tc_h); + + +/** + * @brief Instruct communicator to open a queue + * + * @param tc_h Handle to communicator which shall open queue + * @param peer_id Towards which peer + * @param address For which address + */ +void +GNUNET_TRANSPORT_TESTING_transport_communicator_open_queue (struct + GNUNET_TRANSPORT_TESTING_TransportCommunicatorHandle + *tc_h, + const struct + GNUNET_PeerIdentity + *peer_id, + const char *address); + + +/** + * @brief Instruct communicator to send data + * + * @param tc_queue The queue to use for sending + * @param cont function to call when done sending + * @param cont_cls closure for @a cont + * @param payload Data to send + * @param payload_size Size of the @a payload + */ +void +GNUNET_TRANSPORT_TESTING_transport_communicator_send (struct + GNUNET_TRANSPORT_TESTING_TransportCommunicatorHandle + *tc_h, + GNUNET_SCHEDULER_TaskCallback + cont, + void *cont_cls, + const void *payload, + size_t payload_size); diff --git a/src/service/transport/transport-testing-filenames2.c b/src/service/transport/transport-testing-filenames2.c new file mode 100644 index 000000000..59fa1ebd5 --- /dev/null +++ b/src/service/transport/transport-testing-filenames2.c @@ -0,0 +1,203 @@ +/* + This file is part of GNUnet. + Copyright (C) 2006, 2009, 2015, 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 . + + SPDX-License-Identifier: AGPL3.0-or-later + */ +/** + * @file transport-testing-filenames.c + * @brief convenience string manipulation functions for tests + * @author Matthias Wachs + * @author Christian Grothoff + */ +#include "platform.h" +#include "transport-testing2.h" + + +/** + * Removes all directory separators from absolute filename + * + * @param file the absolute file name, e.g. as found in argv[0] + * @return extracted file name, has to be freed by caller + */ +static char * +extract_filename (const char *file) +{ + char *pch = GNUNET_strdup (file); + char *backup = pch; + char *filename = NULL; + char *res; + + if (NULL != strstr (pch, "/")) + { + pch = strtok (pch, "/"); + while (pch != NULL) + { + pch = strtok (NULL, "/"); + if (pch != NULL) + { + filename = pch; + } + } + } + else + filename = pch; + + res = GNUNET_strdup (filename); + GNUNET_free (backup); + return res; +} + + +/** + * Extracts the test filename from an absolute file name and removes + * the extension + * + * @param file absolute file name + * @return the result + */ +char * +GNUNET_TRANSPORT_TESTING_get_test_name (const char *file) +{ + char *backup = extract_filename (file); + char *filename = backup; + char *dotexe; + char *ret; + + if (NULL == filename) + return NULL; + + /* remove "lt-" */ + filename = strstr (filename, "test"); + if (NULL == filename) + { + GNUNET_free (backup); + return NULL; + } + + /* remove ".exe" */ + if (NULL != (dotexe = strstr (filename, ".exe"))) + dotexe[0] = '\0'; + ret = GNUNET_strdup (filename); + GNUNET_free (backup); + return ret; +} + + +/** + * Extracts the filename from an absolute file name and removes the extension + * + * @param file absolute file name + * @return the result + */ +char * +GNUNET_TRANSPORT_TESTING_get_test_source_name (const char *file) +{ + char *src = extract_filename (file); + char *split; + + split = strstr (src, "."); + if (NULL != split) + split[0] = '\0'; + return src; +} + + +/** + * Extracts the plugin name from an absolute file name and the test name + * + * @param file absolute file name + * @param test test name + * @return the result + */ +char * +GNUNET_TRANSPORT_TESTING_get_test_plugin_name (const char *file, + const char *test) +{ + char *filename; + char *dotexe; + char *e = extract_filename (file); + char *t = extract_filename (test); + char *ret; + + if (NULL == e) + goto fail; + /* remove "lt-" */ + filename = strstr (e, "tes"); + if (NULL == filename) + goto fail; + /* remove ".exe" */ + if (NULL != (dotexe = strstr (filename, ".exe"))) + dotexe[0] = '\0'; + + /* find last _ */ + filename = strstr (filename, t); + if (NULL == filename) + goto fail; + /* copy plugin */ + filename += strlen (t); + if ('\0' != *filename) + filename++; + ret = GNUNET_strdup (filename); + goto suc; +fail: + ret = NULL; +suc: + GNUNET_free (t); + GNUNET_free (e); + return ret; +} + + +/** + * This function takes the filename (e.g. argv[0), removes a "lt-"-prefix and + * if existing ".exe"-prefix and adds the peer-number + * + * @param file filename of the test, e.g. argv[0] + * @param count peer number + * @return the result + */ +char * +GNUNET_TRANSPORT_TESTING_get_config_name (const char *file, + int count) +{ + char *filename = extract_filename (file); + char *backup = filename; + char *dotexe; + char *ret; + + if (NULL == filename) + return NULL; + /* remove "lt-" */ + filename = strstr (filename, "test"); + if (NULL == filename) + goto fail; + /* remove ".exe" */ + if (NULL != (dotexe = strstr (filename, ".exe"))) + dotexe[0] = '\0'; + GNUNET_asprintf (&ret, + "%s_peer%u.conf", + filename, + count); + GNUNET_free (backup); + return ret; +fail: + GNUNET_free (backup); + return NULL; +} + + +/* end of transport-testing-filenames.c */ diff --git a/src/service/transport/transport-testing-loggers2.c b/src/service/transport/transport-testing-loggers2.c new file mode 100644 index 000000000..e6c79b78a --- /dev/null +++ b/src/service/transport/transport-testing-loggers2.c @@ -0,0 +1,81 @@ +/* + This file is part of GNUnet. + Copyright (C) 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 . + + SPDX-License-Identifier: AGPL3.0-or-later + */ +/** + * @file transport-testing-loggers.c + * @brief convenience functions for logging common events in tests + * @author Christian Grothoff + */ +#include "platform.h" +#include "transport-testing2.h" + + +/** + * Log a connect event. + * + * @param cls NULL + * @param me peer that had the event + * @param other peer that connected. + */ +void +GNUNET_TRANSPORT_TESTING_log_connect (void *cls, + struct + GNUNET_TRANSPORT_TESTING_PeerContext *me, + const struct GNUNET_PeerIdentity *other) +{ + char *ps; + + ps = GNUNET_strdup (GNUNET_i2s (&me->id)); + GNUNET_log (GNUNET_ERROR_TYPE_INFO, + "Peer %s connected to %u (%s)!\n", + GNUNET_i2s (other), + me->no, + ps); + GNUNET_free (ps); +} + + +/** + * Log a disconnect event. + * + * @param cls NULL + * @param me peer that had the event + * @param other peer that disconnected. + */ +void +GNUNET_TRANSPORT_TESTING_log_disconnect (void *cls, + struct + GNUNET_TRANSPORT_TESTING_PeerContext * + me, + const struct + GNUNET_PeerIdentity *other) +{ + char *ps; + + ps = GNUNET_strdup (GNUNET_i2s (&me->id)); + GNUNET_log (GNUNET_ERROR_TYPE_INFO, + "Peer `%s' disconnected from %u (%s)!\n", + GNUNET_i2s (other), + me->no, + ps); + GNUNET_free (ps); +} + + +/* end of transport-testing-loggers.c */ diff --git a/src/service/transport/transport-testing-main2.c b/src/service/transport/transport-testing-main2.c new file mode 100644 index 000000000..0a1710922 --- /dev/null +++ b/src/service/transport/transport-testing-main2.c @@ -0,0 +1,614 @@ +/* + This file is part of GNUnet. + Copyright (C) 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 . + + SPDX-License-Identifier: AGPL3.0-or-later + */ +/** + * @file transport-testing-main.c + * @brief convenience main function for tests + * @author Christian Grothoff + */ +#include "platform.h" +#include "transport-testing2.h" + + +/** + * Closure for #connect_cb. + */ +struct GNUNET_TRANSPORT_TESTING_ConnectRequestList +{ + /** + * Stored in a DLL. + */ + struct GNUNET_TRANSPORT_TESTING_ConnectRequestList *next; + + /** + * Stored in a DLL. + */ + struct GNUNET_TRANSPORT_TESTING_ConnectRequestList *prev; + + /** + * Overall context we are in. + */ + struct GNUNET_TRANSPORT_TESTING_ConnectCheckContext *ccc; + + /** + * Connect request this is about. + */ + struct GNUNET_TRANSPORT_TESTING_ConnectRequest *cr; + + /** + * Peer being connected. + */ + struct GNUNET_TRANSPORT_TESTING_PeerContext *p1; + + /** + * Peer being connected. + */ + struct GNUNET_TRANSPORT_TESTING_PeerContext *p2; +}; + + +/** + * Shutdown function for the test. Stops all peers. + * + * @param cls our `struct GNUNET_TRANSPORT_TESTING_ConnectCheckContext *` + */ +static void +do_shutdown (void *cls) +{ + struct GNUNET_TRANSPORT_TESTING_ConnectCheckContext *ccc = cls; + struct GNUNET_TRANSPORT_TESTING_ConnectRequestList *crl; + + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Testcase shutting down\n"); + if (NULL != ccc->shutdown_task) + ccc->shutdown_task (ccc->shutdown_task_cls); + if (NULL != ccc->timeout_task) + { + GNUNET_SCHEDULER_cancel (ccc->timeout_task); + ccc->timeout_task = NULL; + } + if (NULL != ccc->connect_task) + { + GNUNET_SCHEDULER_cancel (ccc->connect_task); + ccc->connect_task = NULL; + } + while (NULL != (crl = ccc->crl_head)) + { + GNUNET_CONTAINER_DLL_remove (ccc->crl_head, + ccc->crl_tail, + crl); + GNUNET_TRANSPORT_TESTING_connect_peers_cancel (crl->cr); + GNUNET_free (crl); + } + for (unsigned int i = 0; i < ccc->num_peers; i++) + { + if (NULL != ccc->p[i]) + { + GNUNET_TRANSPORT_TESTING_stop_peer (ccc->p[i]); + ccc->p[i] = NULL; + } + } +} + + +/** + * Testcase hit timeout, shut it down with error. + * + * @param cls our `struct GNUNET_TRANSPORT_TESTING_ConnectCheckContext *` + */ +static void +do_timeout (void *cls) +{ + struct GNUNET_TRANSPORT_TESTING_ConnectCheckContext *ccc = cls; + + ccc->timeout_task = NULL; + GNUNET_log (GNUNET_ERROR_TYPE_WARNING, + "Testcase timed out\n"); + ccc->global_ret = GNUNET_SYSERR; + GNUNET_SCHEDULER_shutdown (); +} + + +/** + * Internal data structure. Closure for + * #connect_cb, #disconnect_cb, #my_nc and #start_cb. + * Allows us to identify which peer this is about. + */ +struct GNUNET_TRANSPORT_TESTING_InternalPeerContext +{ + /** + * Overall context of the callback. + */ + struct GNUNET_TRANSPORT_TESTING_ConnectCheckContext *ccc; + + /** + * Offset of the peer this is about. + */ + unsigned int off; +}; + + +/** + * Information tracked per connected peer. + */ +struct ConnectPairInfo +{ + /** + * Peer this is about. + */ + const struct GNUNET_PeerIdentity *sender; + + /** + * Information about the receiving peer. + */ + struct GNUNET_TRANSPORT_TESTING_InternalPeerContext *ipi; +}; + + +/** + * Function called when we connected two peers. Once we have gotten + * to the clique, launch test-specific logic. + * + * @param cls our `struct GNUNET_TRANSPORT_TESTING_ConnectRequestList *` + */ +static void +connect_cb (void *cls) +{ + struct GNUNET_TRANSPORT_TESTING_ConnectRequestList *crl = cls; + struct GNUNET_TRANSPORT_TESTING_ConnectCheckContext *ccc = crl->ccc; + + GNUNET_CONTAINER_DLL_remove (ccc->crl_head, + ccc->crl_tail, + crl); + { + char *p1_c = GNUNET_strdup (GNUNET_i2s (&crl->p1->id)); + + GNUNET_log (GNUNET_ERROR_TYPE_INFO, + "Peers connected: %u (%s) <-> %u (%s)\n", + crl->p1->no, + p1_c, + crl->p2->no, + GNUNET_i2s (&crl->p2->id)); + GNUNET_free (p1_c); + GNUNET_free (crl); + } + if (NULL == ccc->crl_head) + { + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "All connections UP, launching custom test logic.\n"); + GNUNET_SCHEDULER_add_now (ccc->connect_continuation, + ccc->connect_continuation_cls); + } +} + + +/** + * Find peer by peer ID. + * + * @param ccc context to search + * @param peer peer to look for + * @return NULL if @a peer was not found + */ +struct GNUNET_TRANSPORT_TESTING_PeerContext * +GNUNET_TRANSPORT_TESTING_find_peer (struct + GNUNET_TRANSPORT_TESTING_ConnectCheckContext + *ccc, + const struct GNUNET_PeerIdentity *peer) +{ + for (unsigned int i = 0; i < ccc->num_peers; i++) + if ((NULL != ccc->p[i]) && + (0 == memcmp (peer, + &ccc->p[i]->id, + sizeof(*peer)))) + return ccc->p[i]; + return NULL; +} + + +/** + * Wrapper around peers connecting. Calls client's nc function. + * + * @param cls our `struct GNUNET_TRANSPORT_TESTING_InternalPeerContext *` + * @param peer peer we got connected to + * @param mq message queue for transmissions to @a peer + * @return closure for message handlers + */ +static void * +my_nc (void *cls, + const struct GNUNET_PeerIdentity *peer, + struct GNUNET_MQ_Handle *mq) +{ + struct GNUNET_TRANSPORT_TESTING_InternalPeerContext *ipi = cls; + struct GNUNET_TRANSPORT_TESTING_ConnectCheckContext *ccc = ipi->ccc; + struct ConnectPairInfo *cpi; + + if (NULL != ccc->nc) + ccc->nc (ccc->cls, + ccc->p[ipi->off], + peer); + cpi = GNUNET_new (struct ConnectPairInfo); + cpi->ipi = ipi; + cpi->sender = peer; /* valid until disconnect */ + return cpi; +} + + +/** + * Wrapper around peers disconnecting. Calls client's nd function. + * + * @param cls our `struct GNUNET_TRANSPORT_TESTING_InternalPeerContext *` + * @param peer peer we got disconnected from + * @param custom_cls return value from @a my_nc + */ +static void +my_nd (void *cls, + const struct GNUNET_PeerIdentity *peer, + void *custom_cls) +{ + struct GNUNET_TRANSPORT_TESTING_InternalPeerContext *ipi = cls; + struct GNUNET_TRANSPORT_TESTING_ConnectCheckContext *ccc = ipi->ccc; + struct ConnectPairInfo *cpi = custom_cls; + + if (NULL != ccc->nd) + ccc->nd (ccc->cls, + ccc->p[ipi->off], + peer); + GNUNET_free (cpi); +} + + +/** + * Wrapper around receiving data. Calls client's rec function. + * + * @param cls our `struct ConnectPairInfo *` + * @param message message we received + * @return #GNUNET_OK (all messages are fine) + */ +static int +check_test (void *cls, + const struct GNUNET_TRANSPORT_TESTING_TestMessage *message) +{ + return GNUNET_OK; +} + + +/** + * Wrapper around receiving data. Calls client's rec function. + * + * @param cls our `struct ConnectPairInfo *` + * @param message message we received + */ +static void +handle_test (void *cls, + const struct GNUNET_TRANSPORT_TESTING_TestMessage *message) +{ + struct ConnectPairInfo *cpi = cls; + struct GNUNET_TRANSPORT_TESTING_InternalPeerContext *ipi = cpi->ipi; + struct GNUNET_TRANSPORT_TESTING_ConnectCheckContext *ccc = ipi->ccc; + + if (NULL != ccc->rec) + ccc->rec (ccc->cls, + ccc->p[ipi->off], + cpi->sender, + message); +} + + +/** + * Wrapper around receiving data. Calls client's rec function. + * + * @param cls our `struct ConnectPairInfo *` + * @param message message we received + * @return #GNUNET_OK (all messages are fine) + */ +static int +check_test2 (void *cls, + const struct GNUNET_TRANSPORT_TESTING_TestMessage *message) +{ + return GNUNET_OK; +} + + +/** + * Wrapper around receiving data. Calls client's rec function. + * + * @param cls our `struct ConnectPairInfo *` + * @param message message we received + */ +static void +handle_test2 (void *cls, + const struct GNUNET_TRANSPORT_TESTING_TestMessage *message) +{ + struct ConnectPairInfo *cpi = cls; + struct GNUNET_TRANSPORT_TESTING_InternalPeerContext *ipi = cpi->ipi; + struct GNUNET_TRANSPORT_TESTING_ConnectCheckContext *ccc = ipi->ccc; + + if (NULL != ccc->rec) + ccc->rec (ccc->cls, + ccc->p[ipi->off], + cpi->sender, + message); +} + + +/** + * Connect the peers as a clique. + * + * @param cls our `struct GNUNET_TRANSPORT_TESTING_ConnectCheckContext` + */ +static void +do_connect (void *cls) +{ + struct GNUNET_TRANSPORT_TESTING_ConnectCheckContext *ccc = cls; + + ccc->connect_task = NULL; + for (unsigned int i = 0; i < ccc->num_peers; i++) + for (unsigned int j = (ccc->bi_directional ? 0 : i + 1); j < ccc->num_peers; + j++) + { + struct GNUNET_TRANSPORT_TESTING_ConnectRequestList *crl; + + if (i == j) + continue; + crl = GNUNET_new (struct GNUNET_TRANSPORT_TESTING_ConnectRequestList); + GNUNET_CONTAINER_DLL_insert (ccc->crl_head, + ccc->crl_tail, + crl); + crl->ccc = ccc; + crl->p1 = ccc->p[i]; + crl->p2 = ccc->p[j]; + { + char *sender_c = GNUNET_strdup (GNUNET_i2s (&ccc->p[0]->id)); + + GNUNET_log (GNUNET_ERROR_TYPE_INFO, + "Test tries to connect peer %u (`%s') -> peer %u (`%s')\n", + ccc->p[0]->no, + sender_c, + ccc->p[1]->no, + GNUNET_i2s (&ccc->p[1]->id)); + GNUNET_free (sender_c); + } + crl->cr = GNUNET_TRANSPORT_TESTING_connect_peers (ccc->p[i], + ccc->p[j], + &connect_cb, + crl); + } +} + + +/** + * Function called once we have successfully launched a peer. + * Once all peers have been launched, we connect all of them + * in a clique. + * + * @param cls our `struct GNUNET_TRANSPORT_TESTING_InternalPeerContext *` + */ +static void +start_cb (void *cls) +{ + struct GNUNET_TRANSPORT_TESTING_InternalPeerContext *ipi = cls; + struct GNUNET_TRANSPORT_TESTING_ConnectCheckContext *ccc = ipi->ccc; + struct GNUNET_TRANSPORT_TESTING_PeerContext *p = ccc->p[ipi->off]; + + ccc->started++; + GNUNET_log (GNUNET_ERROR_TYPE_INFO, + "Peer %u (`%s') started\n", + p->no, + GNUNET_i2s (&p->id)); + if (ccc->started != ccc->num_peers) + return; + if (NULL != ccc->pre_connect_task) + { + /* Run the custom per-connect job, then give it a second to + go into effect before we continue connecting peers. */ + ccc->pre_connect_task (ccc->pre_connect_task_cls); + ccc->connect_task = GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_UNIT_SECONDS, + &do_connect, + ccc); + } + else + { + do_connect (ccc); + } +} + + +/** + * Function run from #GNUNET_TRANSPORT_TESTING_connect_check + * once the scheduler is up. Should launch the peers and + * then in the continuations try to connect them. + * + * @param cls our `struct GNUNET_TRANSPORT_TESTING_ConnectCheckContext *` + * @param args ignored + * @param cfgfile ignored + * @param cfg configuration + */ +static void +connect_check_run (void *cls, + char *const *args, + const char *cfgfile, + const struct GNUNET_CONFIGURATION_Handle *cfg) +{ + struct GNUNET_TRANSPORT_TESTING_ConnectCheckContext *ccc = cls; + int ok; + + ccc->cfg = cfg; + ccc->timeout_task = GNUNET_SCHEDULER_add_delayed (ccc->timeout, + &do_timeout, + ccc); + GNUNET_SCHEDULER_add_shutdown (&do_shutdown, + ccc); + ok = GNUNET_OK; + for (unsigned int i = 0; i < ccc->num_peers; i++) + { + struct GNUNET_MQ_MessageHandler handlers[] = { + GNUNET_MQ_hd_var_size (test, + GNUNET_TRANSPORT_TESTING_SIMPLE_MTYPE, + struct GNUNET_TRANSPORT_TESTING_TestMessage, + NULL), + GNUNET_MQ_hd_var_size (test2, + GNUNET_TRANSPORT_TESTING_SIMPLE_MTYPE2, + struct GNUNET_TRANSPORT_TESTING_TestMessage, + NULL), + GNUNET_MQ_handler_end () + }; + ccc->p[i] = GNUNET_TRANSPORT_TESTING_start_peer (ccc->tth, + ccc->cfg_files[i], + i + 1, + handlers, + &my_nc, + &my_nd, + &ccc->ip[i], + &start_cb, + &ccc->ip[i]); + if (NULL == ccc->p[i]) + ok = GNUNET_SYSERR; + } + if (GNUNET_OK != ok) + { + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "Fail! Could not start peers!\n"); + GNUNET_SCHEDULER_shutdown (); + } +} + + +/** + * Common implementation of the #GNUNET_TRANSPORT_TESTING_CheckCallback. + * Starts and connects the two peers, then invokes the + * `connect_continuation` from @a cls. Sets up a timeout to + * abort the test, and a shutdown handler to clean up properly + * on exit. + * + * @param cls closure of type `struct GNUNET_TRANSPORT_TESTING_ConnectCheckContext` + * @param tth_ initialized testing handle + * @param test_plugin_ name of the plugin + * @param test_name_ name of the test + * @param num_peers number of entries in the @a cfg_file array + * @param cfg_files array of names of configuration files for the peers + * @return #GNUNET_SYSERR on error + */ +int +GNUNET_TRANSPORT_TESTING_connect_check (void *cls, + struct GNUNET_TRANSPORT_TESTING_Handle * + tth_, + const char *test_plugin_, + const char *test_name_, + unsigned int num_peers, + char *cfg_files[]) +{ + static struct GNUNET_GETOPT_CommandLineOption options[] = { + GNUNET_GETOPT_OPTION_END + }; + struct GNUNET_TRANSPORT_TESTING_ConnectCheckContext *ccc = cls; + struct GNUNET_TRANSPORT_TESTING_PeerContext *p[num_peers]; + struct GNUNET_TRANSPORT_TESTING_InternalPeerContext ip[num_peers]; + char *argv[] = { + (char *) test_name_, + "-c", + (char *) ccc->config_file, + NULL + }; + + ccc->num_peers = num_peers; + ccc->cfg_files = cfg_files; + ccc->test_plugin = test_plugin_; + ccc->test_name = test_name_; + ccc->tth = tth_; + ccc->global_ret = GNUNET_OK; + ccc->p = p; + ccc->ip = ip; + for (unsigned int i = 0; i < num_peers; i++) + { + ip[i].off = i; + ip[i].ccc = ccc; + } + if (GNUNET_OK != + GNUNET_PROGRAM_run ((sizeof(argv) / sizeof(char *)) - 1, + argv, + test_name_, + "nohelp", + options, + &connect_check_run, + ccc)) + return GNUNET_SYSERR; + return ccc->global_ret; +} + + +/** + * Setup testcase. Calls @a check with the data the test needs. + * + * @param argv0 binary name (argv[0]) + * @param filename source file name (__FILE__) + * @param num_peers number of peers to start + * @param check main function to run + * @param check_cls closure for @a check + * @return #GNUNET_OK on success + */ +int +GNUNET_TRANSPORT_TESTING_main_ (const char *argv0, + const char *filename, + unsigned int num_peers, + GNUNET_TRANSPORT_TESTING_CheckCallback check, + void *check_cls) +{ + struct GNUNET_TRANSPORT_TESTING_Handle *tth; + char *test_name; + char *test_source; + char *test_plugin; + char *cfg_names[num_peers]; + int ret; + + ret = GNUNET_OK; + test_name = GNUNET_TRANSPORT_TESTING_get_test_name (argv0); + GNUNET_log_setup (test_name, + "WARNING", + NULL); + test_source = GNUNET_TRANSPORT_TESTING_get_test_source_name (filename); + test_plugin = GNUNET_TRANSPORT_TESTING_get_test_plugin_name (argv0, + test_source); + for (unsigned int i = 0; i < num_peers; i++) + cfg_names[i] = GNUNET_TRANSPORT_TESTING_get_config_name (argv0, + i + 1); + tth = GNUNET_TRANSPORT_TESTING_init (); + if (NULL == tth) + { + ret = GNUNET_SYSERR; + } + else + { + ret = check (check_cls, + tth, + test_plugin, + test_name, + num_peers, + cfg_names); + GNUNET_TRANSPORT_TESTING_done (tth); + } + for (unsigned int i = 0; i < num_peers; i++) + GNUNET_free (cfg_names[i]); + GNUNET_free (test_source); + GNUNET_free (test_plugin); + GNUNET_free (test_name); + return ret; +} + + +/* end of transport-testing-main.c */ diff --git a/src/service/transport/transport-testing-send2.c b/src/service/transport/transport-testing-send2.c new file mode 100644 index 000000000..c48dc3a4a --- /dev/null +++ b/src/service/transport/transport-testing-send2.c @@ -0,0 +1,241 @@ +/* + This file is part of GNUnet. + Copyright (C) 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 . + + SPDX-License-Identifier: AGPL3.0-or-later + */ +/** + * @file transport-testing-send.c + * @brief convenience transmission function for tests + * @author Christian Grothoff + */ +#include "platform.h" +#include "transport-testing2.h" + +/** + * Acceptable transmission delay. + */ +#define TIMEOUT_TRANSMIT GNUNET_TIME_relative_multiply ( \ + GNUNET_TIME_UNIT_SECONDS, 30) + + +/** + * Return @a cx in @a cls. + */ +static void +find_cr (void *cls, + struct GNUNET_TRANSPORT_TESTING_ConnectRequest *cx) +{ + struct GNUNET_TRANSPORT_TESTING_ConnectRequest **cr = cls; + + if (GNUNET_NO == cx->connected) + return; + *cr = cx; +} + + +/** + * Send a test message of type @a mtype and size @a msize from + * peer @a sender to peer @a receiver. The peers should be + * connected when this function is called. + * + * @param sender the sending peer + * @param receiver the receiving peer + * @param mtype message type to use + * @param msize size of the message, at least `sizeof (struct GNUNET_TRANSPORT_TESTING_TestMessage)` + * @param num unique message number + * @param cont continuation to call after transmission + * @param cont_cls closure for @a cont + * @return #GNUNET_OK if message was queued, + * #GNUNET_NO if peers are not connected + * #GNUNET_SYSERR if @a msize is illegal + */ +int +GNUNET_TRANSPORT_TESTING_send (struct + GNUNET_TRANSPORT_TESTING_PeerContext *sender, + struct GNUNET_TRANSPORT_TESTING_PeerContext * + receiver, + uint16_t mtype, + uint16_t msize, + uint32_t num, + GNUNET_SCHEDULER_TaskCallback cont, + void *cont_cls) +{ + struct GNUNET_TRANSPORT_TESTING_ConnectRequest *cr; + struct GNUNET_MQ_Envelope *env; + struct GNUNET_TRANSPORT_TESTING_TestMessage *test; + + if (msize < sizeof(struct GNUNET_TRANSPORT_TESTING_TestMessage)) + { + GNUNET_break (0); + return GNUNET_SYSERR; + } + cr = NULL; + GNUNET_TRANSPORT_TESTING_find_connecting_context (sender, + receiver, + &find_cr, + &cr); + if (NULL == cr) + GNUNET_TRANSPORT_TESTING_find_connecting_context (receiver, + sender, + &find_cr, + &cr); + if (NULL == cr) + { + GNUNET_break (0); + return GNUNET_NO; + } + if (NULL == cr->mq) + { + GNUNET_break (0); + return GNUNET_NO; + } + { + char *receiver_s = GNUNET_strdup (GNUNET_i2s (&receiver->id)); + + GNUNET_log (GNUNET_ERROR_TYPE_INFO, + "Sending message from peer %u (`%s') -> peer %u (`%s') !\n", + sender->no, + GNUNET_i2s (&sender->id), + receiver->no, + receiver_s); + GNUNET_free (receiver_s); + } + env = GNUNET_MQ_msg_extra (test, + msize - sizeof(*test), + mtype); + test->num = htonl (num); + memset (&test[1], + num, + msize - sizeof(*test)); + GNUNET_MQ_notify_sent (env, + cont, + cont_cls); + GNUNET_MQ_send (cr->mq, + env); + return GNUNET_OK; +} + + +/** + * Task that sends a test message from the + * first peer to the second peer. + * + * @param ccc context which should contain at least two peers, the + * first two of which should be currently connected + * @param size desired message size + * @param cont continuation to call after transmission + * @param cont_cls closure for @a cont + */ +static void +do_send (struct GNUNET_TRANSPORT_TESTING_ConnectCheckContext *ccc, + uint16_t size, + GNUNET_SCHEDULER_TaskCallback cont, + void *cont_cls) +{ + int ret; + + ccc->global_ret = GNUNET_SYSERR; + ret = GNUNET_TRANSPORT_TESTING_send (ccc->p[0], + ccc->p[1], + GNUNET_TRANSPORT_TESTING_SIMPLE_MTYPE, + size, + ccc->send_num_gen++, + cont, + cont_cls); + GNUNET_assert (GNUNET_SYSERR != ret); + if (GNUNET_NO == ret) + { + GNUNET_break (0); + ccc->global_ret = GNUNET_SYSERR; + GNUNET_SCHEDULER_shutdown (); + } +} + + +/** + * Task that sends a minimalistic test message from the + * first peer to the second peer. + * + * @param cls the `struct GNUNET_TRANSPORT_TESTING_ConnectCheckContext` + * which should contain at least two peers, the first two + * of which should be currently connected + */ +void +GNUNET_TRANSPORT_TESTING_simple_send (void *cls) +{ + struct GNUNET_TRANSPORT_TESTING_SendClosure *sc = cls; + int done; + size_t msize; + + if (0 < sc->num_messages) + { + sc->num_messages--; + done = (0 == sc->num_messages); + } + else + { + done = 0; /* infinite loop */ + } + msize = sizeof(struct GNUNET_TRANSPORT_TESTING_TestMessage); + if (NULL != sc->get_size_cb) + msize = sc->get_size_cb (sc->num_messages); + /* if this was the last message, call the continuation, + otherwise call this function again */ + do_send (sc->ccc, + msize, + done ? sc->cont : &GNUNET_TRANSPORT_TESTING_simple_send, + done ? sc->cont_cls : sc); +} + + +/** + * Task that sends a large test message from the + * first peer to the second peer. + * + * @param cls the `struct GNUNET_TRANSPORT_TESTING_ConnectCheckContext` + * which should contain at least two peers, the first two + * of which should be currently connected + */ +void +GNUNET_TRANSPORT_TESTING_large_send (void *cls) +{ + struct GNUNET_TRANSPORT_TESTING_SendClosure *sc = cls; + int done; + size_t msize; + + if (0 < sc->num_messages) + { + sc->num_messages--; + done = (0 == sc->num_messages); + } + else + { + done = 0; /* infinite loop */ + } + msize = 2600; + if (NULL != sc->get_size_cb) + msize = sc->get_size_cb (sc->num_messages); + /* if this was the last message, call the continuation, + otherwise call this function again */ + do_send (sc->ccc, + msize, + done ? sc->cont : &GNUNET_TRANSPORT_TESTING_large_send, + done ? sc->cont_cls : sc); +} + + +/* end of transport-testing-send.c */ diff --git a/src/service/transport/transport-testing2.c b/src/service/transport/transport-testing2.c new file mode 100644 index 000000000..afa0b0ad4 --- /dev/null +++ b/src/service/transport/transport-testing2.c @@ -0,0 +1,924 @@ +/* + This file is part of GNUnet. + Copyright (C) 2006, 2009, 2015, 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 . + + SPDX-License-Identifier: AGPL3.0-or-later + */ +/** + * @file transport-testing.c + * @brief testing lib for transport service + * @author Matthias Wachs + * @author Christian Grothoff + */ +#include "platform.h" +#include "transport-testing2.h" + + +#define LOG(kind, ...) GNUNET_log_from (kind, "transport-testing", __VA_ARGS__) + + +static struct GNUNET_TRANSPORT_TESTING_PeerContext * +find_peer_context (struct GNUNET_TRANSPORT_TESTING_Handle *tth, + const struct GNUNET_PeerIdentity *peer) +{ + struct GNUNET_TRANSPORT_TESTING_PeerContext *t; + + for (t = tth->p_head; NULL != t; t = t->next) + if (0 == memcmp (&t->id, + peer, + sizeof(struct GNUNET_PeerIdentity))) + return t; + return NULL; +} + + +/** + * Find any connecting context matching the given pair of peers. + * + * @param p1 first peer + * @param p2 second peer + * @param cb function to call + * @param cb_cls closure for @a cb + */ +void +GNUNET_TRANSPORT_TESTING_find_connecting_context (struct + GNUNET_TRANSPORT_TESTING_PeerContext + *p1, + struct + GNUNET_TRANSPORT_TESTING_PeerContext + *p2, + GNUNET_TRANSPORT_TESTING_ConnectContextCallback + cb, + void *cb_cls) +{ + struct GNUNET_TRANSPORT_TESTING_Handle *tth = p1->tth; + struct GNUNET_TRANSPORT_TESTING_ConnectRequest *cc; + struct GNUNET_TRANSPORT_TESTING_ConnectRequest *ccn; + + for (cc = tth->cc_head; NULL != cc; cc = ccn) + { + ccn = cc->next; + if ((cc->p1 == p1) && + (cc->p2 == p2)) + cb (cb_cls, + cc); + } +} + + +static void +set_p1c (void *cls, + struct GNUNET_TRANSPORT_TESTING_ConnectRequest *cx) +{ + int *found = cls; + + if (NULL != found) + *found = GNUNET_YES; + cx->p1_c = GNUNET_YES; +} + + +static void +set_mq (void *cls, + struct GNUNET_TRANSPORT_TESTING_ConnectRequest *cx) +{ + struct GNUNET_MQ_Handle *mq = cls; + + cx->mq = mq; +} + + +static void +set_p2c (void *cls, + struct GNUNET_TRANSPORT_TESTING_ConnectRequest *cx) +{ + int *found = cls; + + if (NULL != found) + *found = GNUNET_YES; + cx->p2_c = GNUNET_YES; +} + + +static void +clear_p1c (void *cls, + struct GNUNET_TRANSPORT_TESTING_ConnectRequest *cx) +{ + int *found = cls; + + if (NULL != found) + *found = GNUNET_YES; + cx->p1_c = GNUNET_NO; +} + + +static void +clear_p2c (void *cls, + struct GNUNET_TRANSPORT_TESTING_ConnectRequest *cx) +{ + int *found = cls; + + if (NULL != found) + *found = GNUNET_YES; + cx->p2_c = GNUNET_NO; +} + + +static void * +notify_connect (void *cls, + const struct GNUNET_PeerIdentity *peer, + struct GNUNET_MQ_Handle *mq) +{ + struct GNUNET_TRANSPORT_TESTING_PeerContext *p = cls; + struct GNUNET_TRANSPORT_TESTING_Handle *tth = p->tth; + char *p2_s; + struct GNUNET_TRANSPORT_TESTING_PeerContext *p2; + struct GNUNET_TRANSPORT_TESTING_ConnectRequest *cc; + struct GNUNET_TRANSPORT_TESTING_ConnectRequest *ccn; + int found; + void *ret; + + p2 = find_peer_context (p->tth, + peer); + if (NULL != p->nc) + ret = p->nc (p->cb_cls, + peer, + mq); + else + ret = NULL; + + if (NULL != p2) + GNUNET_asprintf (&p2_s, + "%u (`%s')", + p2->no, + GNUNET_i2s (&p2->id)); + else + GNUNET_asprintf (&p2_s, + "`%s'", + GNUNET_i2s (peer)); + LOG (GNUNET_ERROR_TYPE_DEBUG, + "Peers %s connected to peer %u (`%s')\n", + p2_s, + p->no, + GNUNET_i2s (&p->id)); + GNUNET_free (p2_s); + /* update flags in connecting contexts */ + found = GNUNET_NO; + GNUNET_TRANSPORT_TESTING_find_connecting_context (p, + p2, + &set_p1c, + &found); + if (GNUNET_NO == found) + { + cc = GNUNET_new (struct GNUNET_TRANSPORT_TESTING_ConnectRequest); + cc->p1 = p; + cc->p2 = p2; + cc->p1_c = GNUNET_YES; + GNUNET_CONTAINER_DLL_insert (tth->cc_head, + tth->cc_tail, + cc); + } + found = GNUNET_NO; + GNUNET_TRANSPORT_TESTING_find_connecting_context (p2, + p, + &set_p2c, + &found); + if (GNUNET_NO == found) + { + cc = GNUNET_new (struct GNUNET_TRANSPORT_TESTING_ConnectRequest); + cc->p1 = p2; + cc->p2 = p; + cc->p1_c = GNUNET_YES; + GNUNET_CONTAINER_DLL_insert (tth->cc_head, + tth->cc_tail, + cc); + } + GNUNET_TRANSPORT_TESTING_find_connecting_context (p, + p2, + &set_mq, + mq); + /* update set connected flag for all requests */ + for (cc = tth->cc_head; NULL != cc; cc = cc->next) + { + if (GNUNET_YES == cc->connected) + continue; + if ((GNUNET_YES == cc->p1_c) && + (GNUNET_YES == cc->p2_c)) + { + cc->connected = GNUNET_YES; + /* stop trying to connect */ + if (NULL != cc->tct) + { + GNUNET_SCHEDULER_cancel (cc->tct); + cc->tct = NULL; + } + if (NULL != cc->ah_sh) + { + GNUNET_TRANSPORT_application_suggest_cancel (cc->ah_sh); + cc->ah_sh = NULL; + } + } + } + /* then notify application */ + for (cc = tth->cc_head; NULL != cc; cc = ccn) + { + ccn = cc->next; + if ((GNUNET_YES == cc->connected) && + (NULL != cc->cb)) + { + cc->cb (cc->cb_cls); + cc->cb = NULL; /* only notify once! */ + } + } + return ret; +} + + +/** + * Offer the current HELLO of P2 to P1. + * + * @param cls our `struct GNUNET_TRANSPORT_TESTING_ConnectRequest` + */ +static void +offer_hello (void *cls); + + +static void +notify_disconnect (void *cls, + const struct GNUNET_PeerIdentity *peer, + void *handler_cls) +{ + struct GNUNET_TRANSPORT_TESTING_PeerContext *p = cls; + struct GNUNET_TRANSPORT_TESTING_Handle *tth = p->tth; + char *p2_s; + /* Find PeerContext */ + int no = 0; + struct GNUNET_TRANSPORT_TESTING_PeerContext *p2 = NULL; + struct GNUNET_TRANSPORT_TESTING_ConnectRequest *cc; + + p2 = find_peer_context (p->tth, + peer); + no = p->no; + if (NULL != p2) + GNUNET_asprintf (&p2_s, + "%u (`%s')", + p2->no, + GNUNET_i2s (&p2->id)); + else + GNUNET_asprintf (&p2_s, + "`%s'", + GNUNET_i2s (peer)); + LOG (GNUNET_ERROR_TYPE_DEBUG, + "Peers %s disconnected from peer %u (`%s')\n", + p2_s, + no, + GNUNET_i2s (&p->id)); + GNUNET_free (p2_s); + /* notify about disconnect */ + if (NULL != p->nd) + p->nd (p->cb_cls, + peer, + handler_cls); + if (NULL == p2) + return; + /* clear MQ, it is now invalid */ + GNUNET_TRANSPORT_TESTING_find_connecting_context (p, + p2, + &set_mq, + NULL); + /* update set connected flags for all requests */ + GNUNET_TRANSPORT_TESTING_find_connecting_context (p, + p2, + &clear_p1c, + NULL); + GNUNET_TRANSPORT_TESTING_find_connecting_context (p2, + p, + &clear_p2c, + NULL); + /* resume connectivity requests as necessary */ + for (cc = tth->cc_head; NULL != cc; cc = cc->next) + { + if (GNUNET_NO == cc->connected) + continue; + if ((GNUNET_YES != cc->p1_c) || + (GNUNET_YES != cc->p2_c)) + { + cc->connected = GNUNET_NO; + /* start trying to connect */ + if (NULL == cc->tct) + cc->tct = GNUNET_SCHEDULER_add_now (&offer_hello, + cc); + if (NULL == cc->ah_sh) + cc->ah_sh = GNUNET_TRANSPORT_application_suggest (cc->p1->ah, + &p2->id, + GNUNET_MQ_PRIO_BEST_EFFORT, + GNUNET_BANDWIDTH_ZERO); + } + } +} + + +static void +retrieve_hello (void *cls); + +static void +hello_iter_cb (void *cb_cls, + const struct GNUNET_PEERSTORE_Record *record, + const char *emsg) +{ + struct GNUNET_TRANSPORT_TESTING_PeerContext *p = cb_cls; + if (NULL == record) + { + p->pic = NULL; + if (NULL != p->start_cb) + p->rh_task = GNUNET_SCHEDULER_add_now (retrieve_hello, p); + return; + } + // Check record type et al? + p->hello_size = record->value_size; + p->hello = GNUNET_malloc (p->hello_size); + memcpy (p->hello, record->value, p->hello_size); + p->hello[p->hello_size - 1] = '\0'; + + GNUNET_PEERSTORE_iterate_cancel (p->pic); + p->pic = NULL; + if (NULL != p->start_cb) + { + LOG (GNUNET_ERROR_TYPE_DEBUG, + "Peer %u (`%s') successfully started\n", + p->no, + GNUNET_i2s (&p->id)); + p->start_cb (p->start_cb_cls); + p->start_cb = NULL; + } +} + + +static void +retrieve_hello (void *cls) +{ + struct GNUNET_TRANSPORT_TESTING_PeerContext *p = cls; + p->rh_task = NULL; + p->pic = GNUNET_PEERSTORE_iterate (p->ph, + "transport", + &p->id, + GNUNET_PEERSTORE_TRANSPORT_HELLO_KEY, + hello_iter_cb, + p); + +} + + +struct GNUNET_TRANSPORT_TESTING_PeerContext * +GNUNET_TRANSPORT_TESTING_start_peer (struct + GNUNET_TRANSPORT_TESTING_Handle *tth, + const char *cfgname, + int peer_id, + const struct + GNUNET_MQ_MessageHandler *handlers, + GNUNET_TRANSPORT_NotifyConnect nc, + GNUNET_TRANSPORT_NotifyDisconnect nd, + void *cb_cls, + GNUNET_SCHEDULER_TaskCallback start_cb, + void *start_cb_cls) +{ + char *emsg = NULL; + struct GNUNET_TRANSPORT_TESTING_PeerContext *p; + struct GNUNET_PeerIdentity dummy; + unsigned int i; + + if (GNUNET_NO == GNUNET_DISK_file_test (cfgname)) + { + LOG (GNUNET_ERROR_TYPE_ERROR, + "File not found: `%s'\n", + cfgname); + return NULL; + } + + p = GNUNET_new (struct GNUNET_TRANSPORT_TESTING_PeerContext); + p->tth = tth; + p->nc = nc; + p->nd = nd; + if (NULL != handlers) + { + for (i = 0; NULL != handlers[i].cb; i++) + ; + p->handlers = GNUNET_new_array (i + 1, + struct GNUNET_MQ_MessageHandler); + GNUNET_memcpy (p->handlers, + handlers, + i * sizeof(struct GNUNET_MQ_MessageHandler)); + } + if (NULL != cb_cls) + p->cb_cls = cb_cls; + else + p->cb_cls = p; + p->start_cb = start_cb; + if (NULL != start_cb_cls) + p->start_cb_cls = start_cb_cls; + else + p->start_cb_cls = p; + GNUNET_CONTAINER_DLL_insert (tth->p_head, + tth->p_tail, + p); + + /* Create configuration and call testing lib to modify it */ + p->cfg = GNUNET_CONFIGURATION_create (); + GNUNET_assert (GNUNET_OK == + GNUNET_CONFIGURATION_load (p->cfg, cfgname)); + if (GNUNET_SYSERR == + GNUNET_TESTING_configuration_create (tth->tl_system, + p->cfg)) + { + LOG (GNUNET_ERROR_TYPE_ERROR, + "Testing library failed to create unique configuration based on `%s'\n", + cfgname); + GNUNET_CONFIGURATION_destroy (p->cfg); + GNUNET_free (p); + return NULL; + } + + p->no = peer_id; + /* Configure peer with configuration */ + p->peer = GNUNET_TESTING_peer_configure (tth->tl_system, + p->cfg, + p->no, + NULL, + &emsg); + if (NULL == p->peer) + { + LOG (GNUNET_ERROR_TYPE_ERROR, + "Testing library failed to create unique configuration based on `%s': `%s'\n", + cfgname, + emsg); + GNUNET_TRANSPORT_TESTING_stop_peer (p); + GNUNET_free (emsg); + return NULL; + } + + if (GNUNET_OK != GNUNET_TESTING_peer_start (p->peer)) + { + LOG (GNUNET_ERROR_TYPE_ERROR, + "Testing library failed to create unique configuration based on `%s'\n", + cfgname); + GNUNET_TRANSPORT_TESTING_stop_peer (p); + return NULL; + } + + memset (&dummy, + '\0', + sizeof(dummy)); + GNUNET_TESTING_peer_get_identity (p->peer, + &p->id); + if (0 == memcmp (&dummy, + &p->id, + sizeof(struct GNUNET_PeerIdentity))) + { + LOG (GNUNET_ERROR_TYPE_ERROR, + "Testing library failed to obtain peer identity for peer %u\n", + p->no); + GNUNET_TRANSPORT_TESTING_stop_peer (p); + return NULL; + } + LOG (GNUNET_ERROR_TYPE_DEBUG, + "Peer %u configured with identity `%s'\n", + p->no, + GNUNET_i2s_full (&p->id)); + p->th = GNUNET_TRANSPORT_core_connect (p->cfg, + NULL, + handlers, + p, + ¬ify_connect, + ¬ify_disconnect); + if (NULL == p->th) + { + LOG (GNUNET_ERROR_TYPE_ERROR, + "Failed to connect to transport service for peer `%s': `%s'\n", + cfgname, + emsg); + GNUNET_TRANSPORT_TESTING_stop_peer (p); + GNUNET_free (emsg); + return NULL; + } + p->ah = GNUNET_TRANSPORT_application_init (p->cfg); + if (NULL == p->ah) + { + LOG (GNUNET_ERROR_TYPE_ERROR, + "Failed to connect to TNG service for peer `%s': `%s'\n", + cfgname, + emsg); + GNUNET_TRANSPORT_TESTING_stop_peer (p); + GNUNET_free (emsg); + return NULL; + } + p->ph = GNUNET_PEERSTORE_connect (p->cfg); + // FIXME Error handling + p->rh_task = GNUNET_SCHEDULER_add_now (retrieve_hello, p); + + return p; +} + + +int +GNUNET_TRANSPORT_TESTING_restart_peer (struct + GNUNET_TRANSPORT_TESTING_PeerContext *p, + GNUNET_SCHEDULER_TaskCallback restart_cb, + void *restart_cb_cls) +{ + struct GNUNET_TRANSPORT_TESTING_ConnectRequest *cc; + struct GNUNET_TRANSPORT_TESTING_ConnectRequest *ccn; + + /* shutdown */ + LOG (GNUNET_ERROR_TYPE_DEBUG, + "Stopping peer %u (`%s')\n", + p->no, + GNUNET_i2s (&p->id)); + if (NULL != p->pic) + { + GNUNET_PEERSTORE_iterate_cancel (p->pic); + p->pic = NULL; + } + if (NULL != p->th) + { + GNUNET_TRANSPORT_core_disconnect (p->th); + p->th = NULL; + } + for (cc = p->tth->cc_head; NULL != cc; cc = ccn) + { + ccn = cc->next; + if ((cc->p1 == p) || + (cc->p2 == p)) + GNUNET_TRANSPORT_TESTING_connect_peers_cancel (cc); + } + if (NULL != p->ah) + { + GNUNET_TRANSPORT_application_done (p->ah); + p->ah = NULL; + } + if (GNUNET_SYSERR == + GNUNET_TESTING_peer_stop (p->peer)) + { + LOG (GNUNET_ERROR_TYPE_ERROR, + "Failed to stop peer %u (`%s')\n", + p->no, + GNUNET_i2s (&p->id)); + return GNUNET_SYSERR; + } + + sleep (5); // YUCK! + + LOG (GNUNET_ERROR_TYPE_DEBUG, + "Restarting peer %u (`%s')\n", + p->no, + GNUNET_i2s (&p->id)); + /* restart */ + if (GNUNET_SYSERR == GNUNET_TESTING_peer_start (p->peer)) + { + LOG (GNUNET_ERROR_TYPE_ERROR, + "Failed to restart peer %u (`%s')\n", + p->no, + GNUNET_i2s (&p->id)); + return GNUNET_SYSERR; + } + + GNUNET_assert (NULL == p->start_cb); + p->start_cb = restart_cb; + p->start_cb_cls = restart_cb_cls; + + p->th = GNUNET_TRANSPORT_core_connect (p->cfg, + NULL, + p->handlers, + p, + ¬ify_connect, + ¬ify_disconnect); + GNUNET_assert (NULL != p->th); + p->ah = GNUNET_TRANSPORT_application_init (p->cfg); + p->pic = GNUNET_PEERSTORE_iterate (p->ph, + "transport", + &p->id, + GNUNET_PEERSTORE_TRANSPORT_HELLO_KEY, + hello_iter_cb, + p); + GNUNET_assert (NULL != p->pic); + return GNUNET_OK; +} + + +/** + * Shutdown the given peer + * + * @param p the peer + */ +void +GNUNET_TRANSPORT_TESTING_stop_peer (struct + GNUNET_TRANSPORT_TESTING_PeerContext *p) +{ + struct GNUNET_TRANSPORT_TESTING_Handle *tth = p->tth; + struct GNUNET_TRANSPORT_TESTING_ConnectRequest *cc; + struct GNUNET_TRANSPORT_TESTING_ConnectRequest *ccn; + /* shutdown */ + LOG (GNUNET_ERROR_TYPE_DEBUG, + "Stopping peer %u (`%s')\n", + p->no, + GNUNET_i2s (&p->id)); + + for (cc = tth->cc_head; NULL != cc; cc = ccn) + { + ccn = cc->next; + if ((cc->p1 == p) || + (cc->p2 == p)) + GNUNET_TRANSPORT_TESTING_connect_peers_cancel (cc); + } + if (NULL != p->pic) + { + GNUNET_PEERSTORE_iterate_cancel (p->pic); + p->pic = NULL; + } + if (NULL != p->th) + { + GNUNET_TRANSPORT_core_disconnect (p->th); + p->th = NULL; + } + if (NULL != p->ah) + { + GNUNET_TRANSPORT_application_done (p->ah); + p->ah = NULL; + } + if (NULL != p->ph) + { + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Disconnecting from PEERSTORE service\n"); + GNUNET_PEERSTORE_disconnect (p->ph, GNUNET_NO); + p->ph = NULL; + } + + if (NULL != p->peer) + { + if (GNUNET_OK != + GNUNET_TESTING_peer_stop (p->peer)) + { + LOG (GNUNET_ERROR_TYPE_DEBUG, + "Testing lib failed to stop peer %u (`%s')\n", + p->no, + GNUNET_i2s (&p->id)); + } + GNUNET_TESTING_peer_destroy (p->peer); + p->peer = NULL; + } + if (NULL != p->hello) + { + GNUNET_free (p->hello); + p->hello = NULL; + } + if (NULL != p->cfg) + { + GNUNET_CONFIGURATION_destroy (p->cfg); + p->cfg = NULL; + } + if (NULL != p->handlers) + { + GNUNET_free (p->handlers); + p->handlers = NULL; + } + GNUNET_CONTAINER_DLL_remove (tth->p_head, + tth->p_tail, + p); + LOG (GNUNET_ERROR_TYPE_DEBUG, + "Peer %u (`%s') stopped\n", + p->no, + GNUNET_i2s (&p->id)); + if (NULL != p->rh_task) + GNUNET_SCHEDULER_cancel (p->rh_task); + p->rh_task = NULL; + GNUNET_free (p); +} + + +/** + * Function called after the HELLO was passed to the + * transport service. + * FIXME maybe schedule the application_validate somehow + */ +/* + static void + hello_offered (void *cls) + { + struct GNUNET_TRANSPORT_TESTING_ConnectRequest *cc = cls; + + cc->oh = NULL; + cc->tct = GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_UNIT_SECONDS, + &offer_hello, + cc); + }*/ + + +static void +offer_hello (void *cls) +{ + struct GNUNET_TRANSPORT_TESTING_ConnectRequest *cc = cls; + struct GNUNET_TRANSPORT_TESTING_PeerContext *p1 = cc->p1; + struct GNUNET_TRANSPORT_TESTING_PeerContext *p2 = cc->p2; + struct GNUNET_TIME_Absolute t; + enum GNUNET_NetworkType nt = 0; + char *addr; + + cc->tct = NULL; + { + char *p2_s = GNUNET_strdup (GNUNET_i2s (&p2->id)); + + LOG (GNUNET_ERROR_TYPE_DEBUG, + "Asking peer %u (`%s') to connect peer %u (`%s'), providing HELLO with %s\n", + p1->no, + GNUNET_i2s (&p1->id), + p2->no, + p2_s, + p2->hello); + GNUNET_free (p2_s); + } + + addr = GNUNET_HELLO_extract_address (p2->hello, + p2->hello_size, + &p2->id, + &nt, + &t); + GNUNET_assert (NULL != addr); + GNUNET_assert (NULL != p1->hello); + GNUNET_TRANSPORT_application_validate (p1->ah, + &p2->id, + nt, + addr); + GNUNET_free (addr); +} + + +/** + * Initiate a connection from p1 to p2 by offering p1 p2's HELLO message + * + * Remarks: start_peer's notify_connect callback can be called before. + * + * @param tth transport testing handle + * @param p1 peer 1 + * @param p2 peer 2 + * @param cb the callback to call when both peers notified that they are connected + * @param cls callback cls + * @return a connect request handle + */ +struct GNUNET_TRANSPORT_TESTING_ConnectRequest * +GNUNET_TRANSPORT_TESTING_connect_peers (struct + GNUNET_TRANSPORT_TESTING_PeerContext *p1, + struct + GNUNET_TRANSPORT_TESTING_PeerContext *p2, + GNUNET_SCHEDULER_TaskCallback cb, + void *cls) +{ + struct GNUNET_TRANSPORT_TESTING_Handle *tth = p1->tth; + struct GNUNET_TRANSPORT_TESTING_ConnectRequest *cc; + struct GNUNET_TRANSPORT_TESTING_ConnectRequest *ccn; + + ccn = NULL; + for (cc = tth->cc_head; NULL != cc; cc = cc->next) + { + if ((cc->p1 == p1) && + (cc->p2 == p2)) + { + ccn = cc; + break; + } + } + + cc = GNUNET_new (struct GNUNET_TRANSPORT_TESTING_ConnectRequest); + cc->p1 = p1; + cc->p2 = p2; + cc->cb = cb; + if (NULL != cls) + cc->cb_cls = cls; + else + cc->cb_cls = cc; + if (NULL != ccn) + { + cc->p1_c = ccn->p1_c; + cc->p2_c = ccn->p2_c; + cc->connected = ccn->connected; + } + GNUNET_CONTAINER_DLL_insert (tth->cc_head, + tth->cc_tail, + cc); + cc->tct = GNUNET_SCHEDULER_add_now (&offer_hello, + cc); + cc->ah_sh = GNUNET_TRANSPORT_application_suggest (cc->p1->ah, + &p2->id, + GNUNET_MQ_PRIO_BEST_EFFORT, + GNUNET_BANDWIDTH_ZERO); + LOG (GNUNET_ERROR_TYPE_DEBUG, + "New connect request %p\n", + cc); + return cc; +} + + +void +GNUNET_TRANSPORT_TESTING_connect_peers_cancel (struct + GNUNET_TRANSPORT_TESTING_ConnectRequest + *cc) +{ + struct GNUNET_TRANSPORT_TESTING_Handle *tth = cc->p1->tth; + + LOG (GNUNET_ERROR_TYPE_DEBUG, + "Canceling connect request!\n"); + if (NULL != cc->tct) + { + GNUNET_SCHEDULER_cancel (cc->tct); + cc->tct = NULL; + } + if (NULL != cc->ah_sh) + { + GNUNET_TRANSPORT_application_suggest_cancel (cc->ah_sh); + cc->ah_sh = NULL; + } + GNUNET_CONTAINER_DLL_remove (tth->cc_head, + tth->cc_tail, + cc); + GNUNET_free (cc); +} + + +/** + * Clean up the transport testing + * + * @param tth transport testing handle + */ +void +GNUNET_TRANSPORT_TESTING_done (struct GNUNET_TRANSPORT_TESTING_Handle *tth) +{ + struct GNUNET_TRANSPORT_TESTING_ConnectRequest *cc; + struct GNUNET_TRANSPORT_TESTING_ConnectRequest *ct; + struct GNUNET_TRANSPORT_TESTING_PeerContext *p; + struct GNUNET_TRANSPORT_TESTING_PeerContext *t; + + if (NULL == tth) + return; + cc = tth->cc_head; + while (NULL != cc) + { + ct = cc->next; + LOG (GNUNET_ERROR_TYPE_ERROR, + "Developer forgot to cancel connect request!\n"); + GNUNET_TRANSPORT_TESTING_connect_peers_cancel (cc); + cc = ct; + } + p = tth->p_head; + while (NULL != p) + { + t = p->next; + LOG (GNUNET_ERROR_TYPE_ERROR, + "Developer forgot to stop peer!\n"); + GNUNET_TRANSPORT_TESTING_stop_peer (p); + p = t; + } + GNUNET_TESTING_system_destroy (tth->tl_system, + GNUNET_YES); + + GNUNET_free (tth); +} + + +/** + * Initialize the transport testing + * + * @return transport testing handle + */ +struct GNUNET_TRANSPORT_TESTING_Handle * +GNUNET_TRANSPORT_TESTING_init () +{ + struct GNUNET_TRANSPORT_TESTING_Handle *tth; + + tth = GNUNET_new (struct GNUNET_TRANSPORT_TESTING_Handle); + tth->tl_system = GNUNET_TESTING_system_create ("transport-testing", + NULL, + NULL, + NULL); + if (NULL == tth->tl_system) + { + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "Failed to initialize testing library!\n"); + GNUNET_free (tth); + return NULL; + } + return tth; +} + + +/* end of transport-testing.c */ diff --git a/src/service/transport/transport-testing2.h b/src/service/transport/transport-testing2.h new file mode 100644 index 000000000..8f1071e8f --- /dev/null +++ b/src/service/transport/transport-testing2.h @@ -0,0 +1,941 @@ +/* + This file is part of GNUnet. + Copyright (C) 2006, 2009, 2015, 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 . + + SPDX-License-Identifier: AGPL3.0-or-later + */ + +/** + * @file transport-testing.h + * @brief testing lib for transport service + * @author Matthias Wachs + * @author Christian Grothoff + */ +#ifndef TRANSPORT_TESTING_H +#define TRANSPORT_TESTING_H +#include "platform.h" +#include "gnunet_util_lib.h" +#include "gnunet_hello_lib.h" +#include "gnunet_peerstore_service.h" +#include "gnunet_transport_core_service.h" +#include "gnunet_transport_application_service.h" +#include "gnunet_testing_lib.h" + + +/* ************* Basic functions for starting/stopping/connecting *********** */ + +/** + * Context for a single peer + */ +struct GNUNET_TRANSPORT_TESTING_PeerContext; + +/** + * Definition for a transport testing handle + */ +struct GNUNET_TRANSPORT_TESTING_Handle; + + +/** + * Context for a single peer + */ +struct GNUNET_TRANSPORT_TESTING_PeerContext +{ + /** + * Next element in the DLL + */ + struct GNUNET_TRANSPORT_TESTING_PeerContext *next; + + /** + * Previous element in the DLL + */ + struct GNUNET_TRANSPORT_TESTING_PeerContext *prev; + + /** + * Transport testing handle this peer belongs to + */ + struct GNUNET_TRANSPORT_TESTING_Handle *tth; + + /** + * Application handle + */ + struct GNUNET_TRANSPORT_ApplicationHandle *ah; + + /** + * Peer's configuration + */ + struct GNUNET_CONFIGURATION_Handle *cfg; + + /** + * Peer's transport service handle + */ + struct GNUNET_TRANSPORT_CoreHandle *th; + + /** + * Peer's PEERSTORE Handle + */ + struct GNUNET_PEERSTORE_Handle *ph; + + /** + * Peer's transport get hello handle to retrieve peer's HELLO message + */ + struct GNUNET_PEERSTORE_IterateContext *pic; + + /** + * Hello + */ + char *hello; + + /** + * Hello size + */ + size_t hello_size; + + /** + * Peer's testing handle + */ + struct GNUNET_TESTING_Peer *peer; + + /** + * Peer identity + */ + struct GNUNET_PeerIdentity id; + + /** + * Handle for the peer's ARM process + */ + struct GNUNET_OS_Process *arm_proc; + + /** + * Receive callback + */ + struct GNUNET_MQ_MessageHandler *handlers; + + /** + * Notify connect callback + */ + GNUNET_TRANSPORT_NotifyConnect nc; + + /** + * Notify disconnect callback + */ + GNUNET_TRANSPORT_NotifyDisconnect nd; + + /** + * Startup completed callback + */ + GNUNET_SCHEDULER_TaskCallback start_cb; + + /** + * Hello get task + */ + struct GNUNET_SCHEDULER_Task *rh_task; + + /** + * Closure for the @a nc and @a nd callbacks + */ + void *cb_cls; + + /** + * Closure for @e start_cb. + */ + void *start_cb_cls; + + /** + * An unique number to identify the peer + */ + unsigned int no; +}; + + +/** + * Handle for a request to connect two peers. + */ +struct GNUNET_TRANSPORT_TESTING_ConnectRequest +{ + /** + * Kept in a DLL. + */ + struct GNUNET_TRANSPORT_TESTING_ConnectRequest *next; + + /** + * Kept in a DLL. + */ + struct GNUNET_TRANSPORT_TESTING_ConnectRequest *prev; + + /** + * Peer we want to connect. + */ + struct GNUNET_TRANSPORT_TESTING_PeerContext *p1; + + /** + * Peer we want to connect. + */ + struct GNUNET_TRANSPORT_TESTING_PeerContext *p2; + + /** + * Task by which we accomplish the connection. + */ + struct GNUNET_SCHEDULER_Task *tct; + + /** + * Handle by which we ask TNG to facilitate the connection. + */ + struct GNUNET_TRANSPORT_ApplicationSuggestHandle *ah_sh; + + /** + * Function to call upon completion. + */ + GNUNET_SCHEDULER_TaskCallback cb; + + /** + * Closure for @e cb. + */ + void *cb_cls; + + /** + * Message queue for sending from @a p1 to @a p2. + */ + struct GNUNET_MQ_Handle *mq; + + /** + * Set if peer1 says the connection is up to peer2. + */ + int p1_c; + + /** + * Set if peer2 says the connection is up to peer1. + */ + int p2_c; + + /** + * #GNUNET_YES if both @e p1_c and @e p2_c are #GNUNET_YES. + */ + int connected; +}; + + +/** + * Handle for a test run. + */ +struct GNUNET_TRANSPORT_TESTING_Handle +{ + /** + * Testing library system handle + */ + struct GNUNET_TESTING_System *tl_system; + + /** + * head DLL of connect contexts + */ + struct GNUNET_TRANSPORT_TESTING_ConnectRequest *cc_head; + + /** + * head DLL of connect contexts + */ + struct GNUNET_TRANSPORT_TESTING_ConnectRequest *cc_tail; + + /** + * head DLL of peers + */ + struct GNUNET_TRANSPORT_TESTING_PeerContext *p_head; + + /** + * tail DLL of peers + */ + struct GNUNET_TRANSPORT_TESTING_PeerContext *p_tail; +}; + + +/** + * Initialize the transport testing + * + * @return transport testing handle + */ +struct GNUNET_TRANSPORT_TESTING_Handle * +GNUNET_TRANSPORT_TESTING_init (void); + + +/** + * Clean up the transport testing + * + * @param tth transport testing handle + */ +void +GNUNET_TRANSPORT_TESTING_done (struct GNUNET_TRANSPORT_TESTING_Handle *tth); + + +/** + * Start a peer with the given configuration + * + * @param tth the testing handle + * @param cfgname configuration file + * @param peer_id an identification number for the peer + * @param handlers functions for receiving messages + * @param nc connect callback + * @param nd disconnect callback + * @param cb_cls closure for @a nc and @a nd callback + * @param start_cb start callback + * @param start_cb_cls closure for @a start_cb + * @return the peer context + */ +struct GNUNET_TRANSPORT_TESTING_PeerContext * +GNUNET_TRANSPORT_TESTING_start_peer ( + struct GNUNET_TRANSPORT_TESTING_Handle *tth, + const char *cfgname, + int peer_id, + const struct GNUNET_MQ_MessageHandler *handlers, + GNUNET_TRANSPORT_NotifyConnect nc, + GNUNET_TRANSPORT_NotifyDisconnect nd, + void *cb_cls, + GNUNET_SCHEDULER_TaskCallback start_cb, + void *start_cb_cls); + + +/** + * Shutdown the given peer + * + * @param p the peer + */ +void +GNUNET_TRANSPORT_TESTING_stop_peer ( + struct GNUNET_TRANSPORT_TESTING_PeerContext *pc); + + +/** + * Stops and restarts the given peer, sleeping (!) for 5s in between. + * + * @param p the peer + * @param restart_cb restart callback + * @param restart_cb_cls callback closure + * @return #GNUNET_OK in success otherwise #GNUNET_SYSERR + */ +int +GNUNET_TRANSPORT_TESTING_restart_peer ( + struct GNUNET_TRANSPORT_TESTING_PeerContext *p, + GNUNET_SCHEDULER_TaskCallback restart_cb, + void *restart_cb_cls); + + +/** + * Connect the given peers and call the callback when both peers + * report the inbound connection. Remarks: start_peer's notify_connect + * callback can be called before. + * + * @param p1 peer 1 + * @param p2 peer 2 + * @param cb the callback to call when both peers notified that they are + * connected + * @param cls callback cls + * @return a connect request handle + */ +struct GNUNET_TRANSPORT_TESTING_ConnectRequest * +GNUNET_TRANSPORT_TESTING_connect_peers ( + struct GNUNET_TRANSPORT_TESTING_PeerContext *p1, + struct GNUNET_TRANSPORT_TESTING_PeerContext *p2, + GNUNET_SCHEDULER_TaskCallback cb, + void *cls); + + +/** + * Cancel the request to connect two peers. You MUST cancel the + * request if you stop the peers before the peers connected + * successfully. + * + * @param cc a connect request handle + */ +void +GNUNET_TRANSPORT_TESTING_connect_peers_cancel ( + struct GNUNET_TRANSPORT_TESTING_ConnectRequest *cc); + + +/** + * Function called on matching connect requests. + * + * @param cls closure + * @param cc request matching the query + */ +typedef void (*GNUNET_TRANSPORT_TESTING_ConnectContextCallback) ( + void *cls, + struct GNUNET_TRANSPORT_TESTING_ConnectRequest *cc); + + +/** + * Find any connecting context matching the given pair of peers. + * + * @param p1 first peer + * @param p2 second peer + * @param cb function to call + * @param cb_cls closure for @a cb + */ +void +GNUNET_TRANSPORT_TESTING_find_connecting_context ( + struct GNUNET_TRANSPORT_TESTING_PeerContext *p1, + struct GNUNET_TRANSPORT_TESTING_PeerContext *p2, + GNUNET_TRANSPORT_TESTING_ConnectContextCallback cb, + void *cb_cls); + + +/* ********************** high-level process functions *************** */ + + +/** + * Function called once the peers have been launched and + * connected by #GNUNET_TRANSPORT_TESTING_connect_check(). + * + * @param cls closure + * @param num_peers size of the @a p array + * @param p the peers that were launched + */ +typedef void (*GNUNET_TRANSPORT_TESTING_ConnectContinuation) ( + void *cls, + unsigned int num_peers, + struct GNUNET_TRANSPORT_TESTING_PeerContext *p[]); + + +/** + * Internal data structure. + */ +struct GNUNET_TRANSPORT_TESTING_ConnectRequestList; + +/** + * Internal data structure. + */ +struct GNUNET_TRANSPORT_TESTING_InternalPeerContext; + + +GNUNET_NETWORK_STRUCT_BEGIN +struct GNUNET_TRANSPORT_TESTING_TestMessage +{ + /** + * Type is (usually) #GNUNET_TRANSPORT_TESTING_SIMPLE_MTYPE. + */ + struct GNUNET_MessageHeader header; + + /** + * Monotonically increasing counter throughout the test. + */ + uint32_t num GNUNET_PACKED; +}; + +struct GNUNET_TRANSPORT_TESTING_PerformanceTestMessage +{ + /** + * Type is (usually) #GNUNET_TRANSPORT_TESTING_SIMPLE_PERFORMANCE_MTYPE. + */ + struct GNUNET_MessageHeader header; + + /** + * Time this message was send via transport api. + */ + struct GNUNET_TIME_AbsoluteNBO time_send; + + /** + * Monotonically increasing counter throughout the test. + */ + uint32_t num GNUNET_PACKED; +}; + +GNUNET_NETWORK_STRUCT_END + + +/** + * Function called by the transport for each received message. + * + * @param cls closure + * @param receiver receiver of the message + * @param sender sender of the message + * @param message the message + */ +typedef void (*GNUNET_TRANSPORT_TESTING_ReceiveCallback) ( + void *cls, + struct GNUNET_TRANSPORT_TESTING_PeerContext *receiver, + const struct GNUNET_PeerIdentity *sender, + const struct GNUNET_TRANSPORT_TESTING_TestMessage *message); + + +/** + * Function called to notify transport users that another + * peer connected to us. + * + * @param cls closure + * @param me peer experiencing the event + * @param other peer that connected to @a me + */ +typedef void (*GNUNET_TRANSPORT_TESTING_NotifyConnect) ( + void *cls, + struct GNUNET_TRANSPORT_TESTING_PeerContext *me, + const struct GNUNET_PeerIdentity *other); + + +/** + * Function called to notify transport users that another + * peer disconnected from us. + * + * @param cls closure + * @param me peer experiencing the event + * @param other peer that disconnected from @a me + */ +typedef void (*GNUNET_TRANSPORT_TESTING_NotifyDisconnect) ( + void *cls, + struct GNUNET_TRANSPORT_TESTING_PeerContext *me, + const struct GNUNET_PeerIdentity *other); + + +/** + * Closure that must be passed to + * #GNUNET_TRANSPORT_TESTING_connect_check. + */ +struct GNUNET_TRANSPORT_TESTING_ConnectCheckContext +{ + /** + * How should we continue after the connect? + */ + GNUNET_SCHEDULER_TaskCallback connect_continuation; + + /** + * Closure for @e connect_continuation. + */ + void *connect_continuation_cls; + + /** + * Which configuration file should we pass to the + * #GNUNET_PROGRAM_run() of the testcase? + */ + const char *config_file; + + /** + * Receiver argument to give for peers we start. + */ + GNUNET_TRANSPORT_TESTING_ReceiveCallback rec; + + /** + * Notify connect argument to give for peers we start. + */ + GNUNET_TRANSPORT_TESTING_NotifyConnect nc; + + /** + * Notify disconnect argument to give for peers we start. + */ + GNUNET_TRANSPORT_TESTING_NotifyDisconnect nd; + + /** + * Closure for @e rec, @e nc and @e nd. + */ + void *cls; + + /** + * Custom task to run on shutdown. + */ + GNUNET_SCHEDULER_TaskCallback shutdown_task; + + /** + * Closure for @e shutdown_task. + */ + void *shutdown_task_cls; + + /** + * Custom task to run after peers were started but before we try to + * connect them. If this function is set, we wait ONE second after + * running this function until we continue with connecting the + * peers. + */ + GNUNET_SCHEDULER_TaskCallback pre_connect_task; + + /** + * Closure for @e shutdown_task. + */ + void *pre_connect_task_cls; + + /** + * When should the testcase time out? + */ + struct GNUNET_TIME_Relative timeout; + + /** + * Should we try to create connections in both directions? + */ + int bi_directional; + + /* ******* fields set by #GNUNET_TRANSPORT_TESTING_connect_check **** */ + + /** + * Number of peers involved in the test. + */ + unsigned int num_peers; + + /** + * Configuration files we have, array with @e num_peers entries. + */ + char **cfg_files; + + /** + * Array with @e num_peers entries. + */ + struct GNUNET_TRANSPORT_TESTING_PeerContext **p; + + /** + * Name of the plugin. + */ + const char *test_plugin; + + /** + * Name of the testcase. + */ + const char *test_name; + + /** + * Configuration object for the testcase. + */ + const struct GNUNET_CONFIGURATION_Handle *cfg; + + /** + * Main testing handle. + */ + struct GNUNET_TRANSPORT_TESTING_Handle *tth; + + /** + * Result from the main function, set to #GNUNET_OK on success. + * Clients should set to #GNUNET_SYSERR to indicate test failure. + */ + int global_ret; + + /** + * Generator for the `num` field in test messages. Incremented each + * time #GNUNET_TRANSPORT_TESTING_simple_send or + * #GNUNET_TRANSPORT_TESTING_large_send are used to transmit a + * message. + */ + uint32_t send_num_gen; + + /* ******* internal state, clients should not mess with this **** */ + + /** + * Task run on timeout. + */ + struct GNUNET_SCHEDULER_Task *timeout_task; + + /** + * Task run to connect peers. + */ + struct GNUNET_SCHEDULER_Task *connect_task; + + /** + * Number of peers that have been started. + */ + unsigned int started; + + /** + * DLL of active connect requests. + */ + struct GNUNET_TRANSPORT_TESTING_ConnectRequestList *crl_head; + + /** + * DLL of active connect requests. + */ + struct GNUNET_TRANSPORT_TESTING_ConnectRequestList *crl_tail; + + /** + * Array with @e num_peers entries. + */ + struct GNUNET_TRANSPORT_TESTING_InternalPeerContext *ip; +}; + + +/** + * Find peer by peer ID. + * + * @param ccc context to search + * @param peer peer to look for + * @return NULL if @a peer was not found + */ +struct GNUNET_TRANSPORT_TESTING_PeerContext * +GNUNET_TRANSPORT_TESTING_find_peer ( + struct GNUNET_TRANSPORT_TESTING_ConnectCheckContext *ccc, + const struct GNUNET_PeerIdentity *peer); + + +/** + * Common implementation of the #GNUNET_TRANSPORT_TESTING_CheckCallback. + * Starts and connects the two peers, then invokes the + * `connect_continuation` from @a cls. Sets up a timeout to + * abort the test, and a shutdown handler to clean up properly + * on exit. + * + * @param cls closure of type `struct + * GNUNET_TRANSPORT_TESTING_ConnectCheckContext` + * @param tth_ initialized testing handle + * @param test_plugin_ name of the plugin + * @param test_name_ name of the test + * @param num_peers number of entries in the @a cfg_file array + * @param cfg_files array of names of configuration files for the peers + * @return #GNUNET_SYSERR on error + */ +int +GNUNET_TRANSPORT_TESTING_connect_check ( + void *cls, + struct GNUNET_TRANSPORT_TESTING_Handle *tth_, + const char *test_plugin_, + const char *test_name_, + unsigned int num_peers, + char *cfg_files[]); + + +/** + * Main function of a testcase. Called with the initial setup data + * for the test as derived from the source name and the binary name. + * + * @param cls closure + * @param tth_ initialized testing handle + * @param test_plugin_ name of the plugin + * @param test_name_ name of the test + * @param num_peers number of entries in the @a cfg_file array + * @param cfg_files array of names of configuration files for the peers + * @return #GNUNET_SYSERR on error + */ +typedef int (*GNUNET_TRANSPORT_TESTING_CheckCallback) ( + void *cls, + struct GNUNET_TRANSPORT_TESTING_Handle *tth_, + const char *test_plugin_, + const char *test_name_, + unsigned int num_peers, + char *cfg_files[]); + + +/** + * Setup testcase. Calls @a check with the data the test needs. + * + * @param argv0 binary name (argv[0]) + * @param filename source file name (__FILE__) + * @param num_peers number of peers to start + * @param check main function to run + * @param check_cls closure for @a check + * @return #GNUNET_OK on success + */ +int +GNUNET_TRANSPORT_TESTING_main_ (const char *argv0, + const char *filename, + unsigned int num_peers, + GNUNET_TRANSPORT_TESTING_CheckCallback check, + void *check_cls); + + +/** + * Setup testcase. Calls @a check with the data the test needs. + * + * @param num_peers number of peers to start + * @param check main function to run + * @param check_cls closure for @a check + * @return #GNUNET_OK on success + */ +#define GNUNET_TRANSPORT_TESTING_main(num_peers, check, check_cls) \ + GNUNET_TRANSPORT_TESTING_main_ (argv[0], \ + __FILE__, \ + num_peers, \ + check, \ + check_cls) + +/* ***************** Convenience functions for sending ********* */ + +/** + * Send a test message of type @a mtype and size @a msize from + * peer @a sender to peer @a receiver. The peers should be + * connected when this function is called. + * + * @param sender the sending peer + * @param receiver the receiving peer + * @param mtype message type to use + * @param msize size of the message, at least `sizeof (struct + * GNUNET_TRANSPORT_TESTING_TestMessage)` + * @param num unique message number + * @param cont continuation to call after transmission + * @param cont_cls closure for @a cont + * @return #GNUNET_OK if message was queued, + * #GNUNET_NO if peers are not connected + * #GNUNET_SYSERR if @a msize is illegal + */ +int +GNUNET_TRANSPORT_TESTING_send ( + struct GNUNET_TRANSPORT_TESTING_PeerContext *sender, + struct GNUNET_TRANSPORT_TESTING_PeerContext *receiver, + uint16_t mtype, + uint16_t msize, + uint32_t num, + GNUNET_SCHEDULER_TaskCallback cont, + void *cont_cls); + + +/** + * Message type used by #GNUNET_TRANSPORT_TESTING_simple_send(). + */ +#define GNUNET_TRANSPORT_TESTING_SIMPLE_MTYPE 12345 + +/** + * Alternative message type for tests. + */ +#define GNUNET_TRANSPORT_TESTING_SIMPLE_MTYPE2 12346 + +/** + * Message type used by #(). + */ +#define GNUNET_TRANSPORT_TESTING_SIMPLE_PERFORMANCE_MTYPE 12347 + +/** + * Type of the closure argument to pass to + * #GNUNET_TRANSPORT_TESTING_simple_send() and + * #GNUNET_TRANSPORT_TESTING_large_send(). + */ +struct GNUNET_TRANSPORT_TESTING_SendClosure +{ + /** + * Context for the transmission. + */ + struct GNUNET_TRANSPORT_TESTING_ConnectCheckContext *ccc; + + /** + * Function that returns the desired message size. Overrides + * the message size, can be NULL in which case the message + * size is the default. + */ + size_t (*get_size_cb) (unsigned int n); + + /** + * Number of messages to be transmitted in a loop. + * Use zero for "forever" (until external shutdown). + */ + unsigned int num_messages; + + /** + * Function to call after all transmissions, can be NULL. + */ + GNUNET_SCHEDULER_TaskCallback cont; + + /** + * Closure for @e cont. + */ + void *cont_cls; +}; + + +/** + * Task that sends a minimalistic test message from the + * first peer to the second peer. + * + * @param cls the `struct GNUNET_TRANSPORT_TESTING_SendClosure` + * which should contain at least two peers, the first two + * of which should be currently connected + */ +void +GNUNET_TRANSPORT_TESTING_simple_send (void *cls); + +/** + * Size of a message sent with + * #GNUNET_TRANSPORT_TESTING_large_send(). Big enough + * to usually force defragmentation. + */ +#define GNUNET_TRANSPORT_TESTING_LARGE_MESSAGE_SIZE 2600 + +/** + * Task that sends a large test message from the + * first peer to the second peer. + * + * @param cls the `struct GNUNET_TRANSPORT_TESTING_SendClosure` + * which should contain at least two peers, the first two + * of which should be currently connected + */ +void +GNUNET_TRANSPORT_TESTING_large_send (void *cls); + + +/* ********************** log-only convenience functions ************* */ + + +/** + * Log a connect event. + * + * @param cls NULL + * @param me peer that had the event + * @param other peer that connected. + */ +void +GNUNET_TRANSPORT_TESTING_log_connect ( + void *cls, + struct GNUNET_TRANSPORT_TESTING_PeerContext *me, + const struct GNUNET_PeerIdentity *other); + + +/** + * Log a disconnect event. + * + * @param cls NULL + * @param me peer that had the event + * @param other peer that disconnected. + */ +void +GNUNET_TRANSPORT_TESTING_log_disconnect ( + void *cls, + struct GNUNET_TRANSPORT_TESTING_PeerContext *me, + const struct GNUNET_PeerIdentity *other); + + +/* ********************** low-level filename functions *************** */ + + +/** + * Extracts the test filename from an absolute file name and removes + * the extension. + * + * @param file absolute file name + * @return resulting test name + */ +char * +GNUNET_TRANSPORT_TESTING_get_test_name (const char *file); + + +/** + * This function takes the filename (e.g. argv[0), removes a "lt-"-prefix and + * if existing ".exe"-prefix and adds the peer-number + * + * @param file filename of the test, e.g. argv[0] + * @param count peer number + * @return configuration name to use + */ +char * +GNUNET_TRANSPORT_TESTING_get_config_name (const char *file, int count); + + +/** + * Extracts the plugin anme from an absolute file name and the test name + * @param file absolute file name + * @param test test name + * @return the plugin name + */ +char * +GNUNET_TRANSPORT_TESTING_get_test_plugin_name (const char *executable, + const char *testname); + + +/** + * Extracts the filename from an absolute file name and removes the + * extension + * + * @param file absolute file name + * @return the source name + */ +char * +GNUNET_TRANSPORT_TESTING_get_test_source_name (const char *file); + +#endif +/* end of transport_testing.h */ diff --git a/src/service/transport/transport.conf.in b/src/service/transport/transport.conf.in new file mode 100644 index 000000000..de4855c6a --- /dev/null +++ b/src/service/transport/transport.conf.in @@ -0,0 +1,248 @@ +[transport] +START_ON_DEMAND = @START_ON_DEMAND@ +@JAVAPORT@PORT = 2091 +HOSTNAME = localhost +BINARY = gnunet-service-transport +# PREFIX = valgrind + +# Maximum number of neighbours PER PLUGIN (not in total). +NEIGHBOUR_LIMIT = 50 +ACCEPT_FROM = 127.0.0.1; +ACCEPT_FROM6 = ::1; +# TCP is the only transport plugin known to work "reliably" +PLUGINS = tcp +UNIXPATH = $GNUNET_RUNTIME_DIR/gnunet-service-transport.sock +BLACKLIST_FILE = $GNUNET_CONFIG_HOME/transport/blacklist +UNIX_MATCH_UID = NO +UNIX_MATCH_GID = YES +# DISABLE_SOCKET_FORWARDING = NO +# USERNAME = +# MAXBUF = +# TIMEOUT = +# DISABLEV6 = +# BINDTO = +# REJECT_FROM = +# REJECT_FROM6 = +# PREFIX = valgrind --leak-check=full + +# Configuration settings related to traffic manipulation for testing purposes +# Distance +# MANIPULATE_DISTANCE_IN = 1 +# MANIPULATE_DISTANCE_OUT = 1 +# Delay; WARNING: to large values may lead to peers not connecting! +# MANIPULATE_DELAY_IN = 1 ms +# MANIPULATE_DELAY_OUT = 1 ms + + +[transport-unix] +UNIXPATH = $GNUNET_RUNTIME_DIR/gnunet-transport-plugin-unix.sock +TESTING_IGNORE_KEYS = ACCEPT_FROM; + +[transport-tcp] +# Use 0 to ONLY advertise as a peer behind NAT (no port binding) +PORT = 2086 + +# Obsolete option, to be replaced by HOLE_EXTERNAL (soon) +ADVERTISED_PORT = 2086 + +# If we have a manually punched NAT, what is the external IP and port? +# Can use DNS names for DynDNS-based detection of external IP. +# Can use IPv6 addresses ([fefc::]:PORT). +# Use "AUTO" for the hostname to automatically detect external IP. +# Do not set if NAT is not manually punched. +# HOLE_EXTERNAL = AUTO:2086 + +TESTING_IGNORE_KEYS = ACCEPT_FROM; + +# Maximum number of open TCP connections allowed +MAX_CONNECTIONS = 128 + +TIMEOUT = 5 s +# ACCEPT_FROM = +# ACCEPT_FROM6 = +# REJECT_FROM = +# REJECT_FROM6 = +# BINDTO = +MAX_CONNECTIONS = 128 + +# Enable TCP stealth? +TCP_STEALTH = NO + + + +[transport-xt] +# Use 0 to ONLY advertise as a peer behind NAT (no port binding) +PORT = 2087 + +# Obsolete option, to be replaced by HOLE_EXTERNAL (soon) +ADVERTISED_PORT = 2087 + +# If we have a manually punched NAT, what is the external IP and port? +# Can use DNS names for DynDNS-based detection of external IP. +# Can use IPv6 addresses ([fefc::]:PORT). +# Use "AUTO" for the hostname to automatically detect external IP. +# Do not set if NAT is not manually punched. +# HOLE_EXTERNAL = AUTO:2087 + +TESTING_IGNORE_KEYS = ACCEPT_FROM; + +# Maximum number of open TCP connections allowed +MAX_CONNECTIONS = 128 + +TIMEOUT = 5 s +# ACCEPT_FROM = +# ACCEPT_FROM6 = +# REJECT_FROM = +# REJECT_FROM6 = +# BINDTO = +MAX_CONNECTIONS = 128 + +# Enable TCP stealth? +TCP_STEALTH = NO + + +[transport-udp] +# Use PORT = 0 to autodetect a port available +PORT = 2086 +BROADCAST = YES +BROADCAST_RECEIVE = YES +BROADCAST_INTERVAL = 30 s + +# This limits UDP to 1MB/s for SENDING. Higher values are advised +# for benchmarking or well-connected systems. Note that this quota +# applies IN ADDITION to the system-wide transport-wide WAN/LAN +# quotas. +MAX_BPS = 1000000 +TESTING_IGNORE_KEYS = ACCEPT_FROM; + +# If we have a manually punched NAT, what is the external IP and port? +# Can use DNS names for DynDNS-based detection of external IP. +# Can use IPv6 addresses ([fefc::]:PORT). +# Use "AUTO" for the hostname to automatically detect external IP. +# Do not set if NAT is not manually punched. +# HOLE_EXTERNAL = AUTO:2086 + + +[transport-xu] +# Use PORT = 0 to autodetect a port available +PORT = 2087 + + +[transport-http_client] +MAX_CONNECTIONS = 128 +TESTING_IGNORE_KEYS = ACCEPT_FROM; +# Hostname or IP of proxy server +# PROXY = + +# User name for proxy server +# PROXY_USERNAME = +# User password for proxy server +# PROXY_PASSWORD = + +# Type of proxy server, +# Valid values: HTTP, SOCKS4, SOCKS5, SOCKS4A, SOCKS5_HOSTNAME +# Default: HTTP +# PROXY_TYPE = HTTP + +# Enable tunneling proxy request instead of having proxy server evaluate it +# Experimental, default: NO +# PROXY_HTTP_TUNNELING = NO + + +[transport-http_server] +#EXTERNAL_HOSTNAME = +PORT = 1080 + +# Obsolete option, to be replaced by HOLE_EXTERNAL (soon) +ADVERTISED_PORT = 1080 + +# If we have a manually punched NAT, what is the external IP and port? +# Can use DNS names for DynDNS-based detection of external IP. +# Can use IPv6 addresses ([fefc::]:PORT). +# Use "AUTO" for the hostname to automatically detect external IP. +# Do not set if NAT is not manually punched. +# HOLE_EXTERNAL = AUTO:1080 + +MAX_CONNECTIONS = 128 +TESTING_IGNORE_KEYS = ACCEPT_FROM; + +# Enable TCP stealth? +TCP_STEALTH = NO + + +[transport-https_client] +MAX_CONNECTIONS = 128 +TESTING_IGNORE_KEYS = ACCEPT_FROM; +# Hostname or IP of proxy server +# PROXY = + +# User name for proxy server +# PROXY_USERNAME = +# User password for proxy server +# PROXY_PASSWORD = + +# Type of proxy server, +# Valid values: HTTP, SOCKS4, SOCKS5, SOCKS4A, SOCKS5_HOSTNAME +# Default: HTTP +# PROXY_TYPE = HTTP + +# Enable tunneling proxy request instead of having proxy server evaluate it +# Experimental, default: NO +# PROXY_HTTP_TUNNELING = NO + + +[transport-https_server] +# EXTERNAL_HOSTNAME = +# EXTERNAL_HOSTNAME_ONLY = YES +# If you have a valid SSL certificate for your external hostname tell, +# clients to verify it +# VERIFY_EXTERNAL_HOSTNAME = YES +# Does the external hostname use the same port? +# EXTERNAL_HOSTNAME_USE_PORT = YES +PORT = 4433 + +# Obsolete option, to be replaced by HOLE_EXTERNAL (soon) +ADVERTISED_PORT = 4433 + +# If we have a manually punched NAT, what is the external IP and port? +# Can use DNS names for DynDNS-based detection of external IP. +# Can use IPv6 addresses ([fefc::]:PORT). +# Use "AUTO" for the hostname to automatically detect external IP. +# Do not set if NAT is not manually punched. +# HOLE_EXTERNAL = AUTO:4433 + +CRYPTO_INIT = NORMAL +KEY_FILE = $GNUNET_DATA_HOME/transport/https.key +CERT_FILE = $GNUNET_DATA_HOME/transport/https.cert +MAX_CONNECTIONS = 128 +TESTING_IGNORE_KEYS = ACCEPT_FROM; + +# Enable TCP stealth? +TCP_STEALTH = NO + + +[transport-wlan] +# Name of the interface in monitor mode (typically monX) +INTERFACE = mon0 +# Real hardware, no testing +TESTMODE = 0 +TESTING_IGNORE_KEYS = ACCEPT_FROM; + + +[transport-bluetooth] +# Name of the interface (typically hciX) +INTERFACE = hci0 +# Real hardware, no testing +TESTMODE = 0 +TESTING_IGNORE_KEYS = ACCEPT_FROM; + +[communicator-tcp] +#PREFIX = xterm -geometry 100x85 -T peer1 -e gdb --args +#PREFIX = valgrind --leak-check=full --track-origins=yes +BINDTO = 2086 +DISABLE_V6 = NO +BINARY = gnunet-communicator-tcp +IMMEDIATE_START = YES + +[communicator-quic] +BINDTO = 127.0.0.1 diff --git a/src/service/transport/transport.h b/src/service/transport/transport.h new file mode 100644 index 000000000..66f17ee5b --- /dev/null +++ b/src/service/transport/transport.h @@ -0,0 +1,828 @@ +/* + This file is part of GNUnet. + Copyright (C) 2009-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 . + + SPDX-License-Identifier: AGPL3.0-or-later + */ + +/** + * @file transport/transport.h + * @brief common internal definitions for transport service + * @author Christian Grothoff + */ +#ifndef TRANSPORT_H +#define TRANSPORT_H + +#include "gnunet_util_lib.h" +#include "gnunet_time_lib.h" +#include "gnunet_constants.h" + +#define DEBUG_TRANSPORT GNUNET_EXTRA_LOGGING + + +/** + * Similar to GNUNET_TRANSPORT_NotifyDisconnect but in and out quotas are + * included here. These values are not required outside transport_api + * + * @param cls closure + * @param peer the peer that connected + * @param bandwidth_in inbound bandwidth in NBO + * @param bandwidth_out outbound bandwidth in NBO + * + */ +typedef void (*NotifyConnect) ( + void *cls, + const struct GNUNET_PeerIdentity *peer, + struct GNUNET_BANDWIDTH_Value32NBO bandwidth_in, + struct GNUNET_BANDWIDTH_Value32NBO bandwidth_out); + + +GNUNET_NETWORK_STRUCT_BEGIN + + +/** + * Message from the transport service to the library + * asking to check if both processes agree about this + * peers identity. + */ +struct StartMessage +{ + /** + * Type will be #GNUNET_MESSAGE_TYPE_TRANSPORT_START + */ + struct GNUNET_MessageHeader header; + + /** + * 0: no options + * 1: The @e self field should be checked + * 2: this client is interested in payload traffic + */ + uint32_t options; + + /** + * Identity we think we have. If it does not match, the + * receiver should print out an error message and disconnect. + */ + struct GNUNET_PeerIdentity self; +}; + + +/** + * Message from the transport service to the library + * informing about neighbors. + */ +struct ConnectInfoMessage +{ + /** + * Type will be #GNUNET_MESSAGE_TYPE_TRANSPORT_CONNECT + */ + struct GNUNET_MessageHeader header; + +#if (defined(GNUNET_TRANSPORT_COMMUNICATION_VERSION) || \ + defined(GNUNET_TRANSPORT_CORE_VERSION)) + + /** + * Always zero, for alignment. + */ + uint32_t reserved GNUNET_PACKED; +#else + /** + * Current outbound quota for this peer + */ + struct GNUNET_BANDWIDTH_Value32NBO quota_out; +#endif + + /** + * Identity of the new neighbour. + */ + struct GNUNET_PeerIdentity id; +}; + + +/** + * Message from the transport service to the library + * informing about disconnects. + */ +struct DisconnectInfoMessage +{ + /** + * Type will be #GNUNET_MESSAGE_TYPE_TRANSPORT_DISCONNECT + */ + struct GNUNET_MessageHeader header; + + /** + * Reserved, always zero. + */ + uint32_t reserved GNUNET_PACKED; + + /** + * Who got disconnected? + */ + struct GNUNET_PeerIdentity peer; +}; + + +/** + * Message used to notify the transport API about a message + * received from the network. The actual message follows. + */ +struct InboundMessage +{ + /** + * Type will be #GNUNET_MESSAGE_TYPE_TRANSPORT_RECV + */ + struct GNUNET_MessageHeader header; + + /** + * Which peer sent the message? + */ + struct GNUNET_PeerIdentity peer; +}; + + +/** + * Message used to notify the transport API that it can + * send another message to the transport service. + */ +struct SendOkMessage +{ + /** + * Type will be #GNUNET_MESSAGE_TYPE_TRANSPORT_SEND_OK + */ + struct GNUNET_MessageHeader header; + +#if (defined(GNUNET_TRANSPORT_COMMUNICATION_VERSION) || \ + defined(GNUNET_TRANSPORT_CORE_VERSION)) + + uint32_t reserved GNUNET_PACKED; +#else + /** + * #GNUNET_OK if the transmission succeeded, + * #GNUNET_SYSERR if it failed (i.e. network disconnect); + * in either case, it is now OK for this client to + * send us another message for the given peer. + */ + uint16_t success GNUNET_PACKED; + + /** + * Size of message sent + */ + uint16_t bytes_msg GNUNET_PACKED; + + /** + * Size of message sent over wire. + * Includes plugin and protocol specific overheads. + */ + uint32_t bytes_physical GNUNET_PACKED; +#endif + + /** + * Which peer can send more now? + */ + struct GNUNET_PeerIdentity peer; +}; + + +/** + * Message used to notify the transport API that it can + * send another message to the transport service. + * (Used to implement flow control.) + */ +struct RecvOkMessage +{ + /** + * Type will be #GNUNET_MESSAGE_TYPE_TRANSPORT_RECV_OK + */ + struct GNUNET_MessageHeader header; + + /** + * Number of messages by which to increase the window, greater or + * equal to one. + */ + uint32_t increase_window_delta GNUNET_PACKED; + + /** + * Which peer can CORE handle more from now? + */ + struct GNUNET_PeerIdentity peer; +}; + + +/** + * Message used to notify the transport service about a message + * to be transmitted to another peer. The actual message follows. + */ +struct OutboundMessage +{ + /** + * Type will be #GNUNET_MESSAGE_TYPE_TRANSPORT_SEND + */ + struct GNUNET_MessageHeader header; + + /** + * An `enum GNUNET_MQ_PriorityPreferences` in NBO. + */ + uint32_t priority GNUNET_PACKED; + +#if ! (defined(GNUNET_TRANSPORT_COMMUNICATION_VERSION) || \ + defined(GNUNET_TRANSPORT_CORE_VERSION)) + + /** + * Allowed delay. + */ + struct GNUNET_TIME_RelativeNBO timeout; +#endif + + /** + * Which peer should receive the message? + */ + struct GNUNET_PeerIdentity peer; +}; + + +/* *********************** TNG messages ***************** */ + +/** + * Communicator goes online. Note which addresses it can + * work with. + */ +struct GNUNET_TRANSPORT_CommunicatorAvailableMessage +{ + /** + * Type will be #GNUNET_MESSAGE_TYPE_TRANSPORT_NEW_COMMUNICATOR. + */ + struct GNUNET_MessageHeader header; + + /** + * NBO encoding of `enum GNUNET_TRANSPORT_CommunicatorCharacteristics` + */ + uint32_t cc; + + /* Followed by the address prefix of the communicator */ +}; + + +/** + * Add address to the list. + */ +struct GNUNET_TRANSPORT_AddAddressMessage +{ + /** + * Type will be #GNUNET_MESSAGE_TYPE_TRANSPORT_ADD_ADDRESS. + */ + struct GNUNET_MessageHeader header; + + /** + * Address identifier (used during deletion). + */ + uint32_t aid GNUNET_PACKED; + + /** + * When does the address expire? + */ + struct GNUNET_TIME_RelativeNBO expiration; + + /** + * An `enum GNUNET_NetworkType` in NBO. + */ + uint32_t nt; + + /* followed by UTF-8 encoded, 0-terminated human-readable address */ +}; + + +/** + * Remove address from the list. + */ +struct GNUNET_TRANSPORT_DelAddressMessage +{ + /** + * Type will be #GNUNET_MESSAGE_TYPE_TRANSPORT_DEL_ADDRESS. + */ + struct GNUNET_MessageHeader header; + + /** + * Address identifier. + */ + uint32_t aid GNUNET_PACKED; +}; + + +/** + * Inform transport about an incoming message. + */ +struct GNUNET_TRANSPORT_IncomingMessage +{ + /** + * Type will be #GNUNET_MESSAGE_TYPE_TRANSPORT_INCOMING_MSG. + */ + struct GNUNET_MessageHeader header; + + /** + * Do we use flow control or not? + */ + uint32_t fc_on GNUNET_PACKED; + + /** + * 64-bit number to identify the matching ACK. + */ + uint64_t fc_id GNUNET_PACKED; + + /** + * How long does the communicator believe the address on which + * the message was received to remain valid? + */ + struct GNUNET_TIME_RelativeNBO expected_address_validity; + + /** + * Sender identifier. + */ + struct GNUNET_PeerIdentity sender; + + /** + * Direct neighbour sender identifier. + */ + struct GNUNET_PeerIdentity neighbour_sender; + + /* followed by the message */ +}; + + +/** + * Transport informs us about being done with an incoming message. + * (only sent if fc_on was set). + */ +struct GNUNET_TRANSPORT_IncomingMessageAck +{ + /** + * Type will be #GNUNET_MESSAGE_TYPE_TRANSPORT_INCOMING_MSG_ACK. + */ + struct GNUNET_MessageHeader header; + + /** + * Reserved (0) + */ + uint32_t reserved GNUNET_PACKED; + + /** + * Which message is being ACKed? + */ + uint64_t fc_id GNUNET_PACKED; + + /** + * Sender identifier of the original message. + */ + struct GNUNET_PeerIdentity sender; +}; + + +/** + * Add queue to the transport + */ +struct GNUNET_TRANSPORT_AddQueueMessage +{ + /** + * Type will be #GNUNET_MESSAGE_TYPE_TRANSPORT_QUEUE_SETUP. + */ + struct GNUNET_MessageHeader header; + + /** + * Queue identifier (used to identify the queue). + */ + uint32_t qid GNUNET_PACKED; + + /** + * Receiver that can be addressed via the queue. + */ + struct GNUNET_PeerIdentity receiver; + + /** + * An `enum GNUNET_NetworkType` in NBO. + */ + uint32_t nt; + + /** + * Maximum transmission unit, in NBO. UINT32_MAX for unlimited. + */ + uint32_t mtu; + + /** + * Queue length, in NBO. Defines how many messages may be + * send through this queue. UINT64_MAX for unlimited. + */ + uint64_t q_len; + + /** + * Priority of the queue in relation to other queues. + */ + uint32_t priority; + + /** + * An `enum GNUNET_TRANSPORT_ConnectionStatus` in NBO. + */ + uint32_t cs; + + /* followed by UTF-8 encoded, 0-terminated human-readable address */ +}; + + +/** + * Update queue + */ +struct GNUNET_TRANSPORT_UpdateQueueMessage +{ + /** + * Type will be #GNUNET_MESSAGE_TYPE_TRANSPORT_QUEUE_SETUP. + */ + struct GNUNET_MessageHeader header; + + /** + * Queue identifier (used to identify the queue). + */ + uint32_t qid GNUNET_PACKED; + + /** + * Receiver that can be addressed via the queue. + */ + struct GNUNET_PeerIdentity receiver; + + /** + * An `enum GNUNET_NetworkType` in NBO. + */ + uint32_t nt; + + /** + * Maximum transmission unit, in NBO. UINT32_MAX for unlimited. + */ + uint32_t mtu; + + /** + * Queue length, in NBO. Defines how many messages may be + * send through this queue. UINT64_MAX for unlimited. + */ + uint64_t q_len; + + /** + * Priority of the queue in relation to other queues. + */ + uint32_t priority; + + /** + * An `enum GNUNET_TRANSPORT_ConnectionStatus` in NBO. + */ + uint32_t cs; +}; + + +/** + * Remove queue, it is no longer available. + */ +struct GNUNET_TRANSPORT_DelQueueMessage +{ + /** + * Type will be #GNUNET_MESSAGE_TYPE_TRANSPORT_QUEUE_TEARDOWN. + */ + struct GNUNET_MessageHeader header; + + /** + * Address identifier. + */ + uint32_t qid GNUNET_PACKED; + + /** + * Receiver that can be addressed via the queue. + */ + struct GNUNET_PeerIdentity receiver; +}; + + +/** + * Transport tells communicator that it wants a new queue. + */ +struct GNUNET_TRANSPORT_CreateQueue +{ + /** + * Type will be #GNUNET_MESSAGE_TYPE_TRANSPORT_QUEUE_CREATE. + */ + struct GNUNET_MessageHeader header; + + /** + * Unique ID for the request. + */ + uint32_t request_id GNUNET_PACKED; + + /** + * Receiver that can be addressed via the queue. + */ + struct GNUNET_PeerIdentity receiver; + + /* followed by UTF-8 encoded, 0-terminated human-readable address */ +}; + + +/** + * Communicator tells transport how queue creation went down. + */ +struct GNUNET_TRANSPORT_CreateQueueResponse +{ + /** + * Type will be #GNUNET_MESSAGE_TYPE_TRANSPORT_QUEUE_CREATE_OK or + * #GNUNET_MESSAGE_TYPE_TRANSPORT_QUEUE_CREATE_FAIL. + */ + struct GNUNET_MessageHeader header; + + /** + * Unique ID for the request. + */ + uint32_t request_id GNUNET_PACKED; +}; + + +/** + * Inform communicator about transport's desire to send a message. + */ +struct GNUNET_TRANSPORT_SendMessageTo +{ + /** + * Type will be #GNUNET_MESSAGE_TYPE_TRANSPORT_SEND_MSG. + */ + struct GNUNET_MessageHeader header; + + /** + * Which queue should we use? + */ + uint32_t qid GNUNET_PACKED; + + /** + * Message ID, used for flow control. + */ + uint64_t mid GNUNET_PACKED; + + /** + * Receiver identifier. + */ + struct GNUNET_PeerIdentity receiver; + + /* followed by the message */ +}; + + +/** + * Inform transport that message was sent. + */ +struct GNUNET_TRANSPORT_SendMessageToAck +{ + /** + * Type will be #GNUNET_MESSAGE_TYPE_TRANSPORT_SEND_MSG_ACK. + */ + struct GNUNET_MessageHeader header; + + /** + * Success (#GNUNET_OK), failure (#GNUNET_SYSERR). + */ + uint32_t status GNUNET_PACKED; + + /** + * Message ID of the original message. + */ + uint64_t mid GNUNET_PACKED; + + /** + * Queue ID for the queue which was used to send the message. + */ + uint32_t qid GNUNET_PACKED; + + /** + * Receiver identifier. + */ + struct GNUNET_PeerIdentity receiver; +}; + + +/** + * Message from communicator to transport service asking for + * transmission of a backchannel message with the given peer @e pid + * and communicator. + */ +struct GNUNET_TRANSPORT_CommunicatorBackchannel +{ + /** + * Type will be #GNUNET_MESSAGE_TYPE_TRANSPORT_COMMUNICATOR_BACKCHANNEL + */ + struct GNUNET_MessageHeader header; + + /** + * Always zero, for alignment. + */ + uint32_t reserved; + + /** + * Target peer. + */ + struct GNUNET_PeerIdentity pid; + + /* Followed by a `struct GNUNET_MessageHeader` with the encapsulated + message to the communicator */ + + /* Followed by the 0-terminated string specifying the desired + communicator at the target (@e pid) peer */ +}; + + +/** + * Message from transport to communicator passing along a backchannel + * message from the given peer @e pid. + */ +struct GNUNET_TRANSPORT_CommunicatorBackchannelIncoming +{ + /** + * Type will be + * #GNUNET_MESSAGE_TYPE_TRANSPORT_COMMUNICATOR_BACKCHANNEL_INCOMING + */ + struct GNUNET_MessageHeader header; + + /** + * Always zero, for alignment. + */ + uint32_t reserved; + + /** + * Origin peer. + */ + struct GNUNET_PeerIdentity pid; + + /* Followed by a `struct GNUNET_MessageHeader` with the encapsulated + message to the communicator */ +}; + + +/** + * Request to start monitoring. + */ +struct GNUNET_TRANSPORT_MonitorStart +{ + /** + * Type will be #GNUNET_MESSAGE_TYPE_TRANSPORT_MONITOR_START. + */ + struct GNUNET_MessageHeader header; + + /** + * #GNUNET_YES for one-shot montoring, #GNUNET_NO for continuous monitoring. + */ + uint32_t one_shot; + + /** + * Target identifier to monitor, all zeros for "all peers". + */ + struct GNUNET_PeerIdentity peer; +}; + + +/** + * Monitoring data. + */ +struct GNUNET_TRANSPORT_MonitorData +{ + /** + * Type will be #GNUNET_MESSAGE_TYPE_TRANSPORT_MONITOR_DATA. + */ + struct GNUNET_MessageHeader header; + + /** + * Network type (an `enum GNUNET_NetworkType` in NBO). + */ + uint32_t nt GNUNET_PACKED; + + /** + * Target identifier. + */ + struct GNUNET_PeerIdentity peer; + + /** + * @deprecated To be discussed if we keep these... + */ + struct GNUNET_TIME_AbsoluteNBO last_validation; + struct GNUNET_TIME_AbsoluteNBO valid_until; + struct GNUNET_TIME_AbsoluteNBO next_validation; + + /** + * Current round-trip time estimate. + */ + struct GNUNET_TIME_RelativeNBO rtt; + + /** + * Connection status (in NBO). + */ + uint32_t cs GNUNET_PACKED; + + /** + * Messages pending (in NBO). + */ + uint32_t num_msg_pending GNUNET_PACKED; + + /** + * Bytes pending (in NBO). + */ + uint32_t num_bytes_pending GNUNET_PACKED; + + /* Followed by 0-terminated address of the peer */ +}; + + +/** + * Request to verify address. + */ +struct GNUNET_TRANSPORT_AddressToVerify +{ + /** + * Type will be #GNUNET_MESSAGE_TYPE_TRANSPORT_ADDRESS_CONSIDER_VERIFY. + */ + struct GNUNET_MessageHeader header; + + /** + * Reserved. 0. + */ + uint32_t reserved; + + /** + * Peer the address is from. + */ + struct GNUNET_PeerIdentity peer; + + /* followed by variable-size raw address */ +}; + + +/** + * Application client to TRANSPORT service: we would like to have + * address suggestions for this peer. + */ +struct ExpressPreferenceMessage +{ + /** + * Type is #GNUNET_MESSAGE_TYPE_TRANSPORT_SUGGEST or + * #GNUNET_MESSAGE_TYPE_TRANSPORT_SUGGEST_CANCEL to stop + * suggestions. + */ + struct GNUNET_MessageHeader header; + + /** + * What type of performance preference does the client have? + * A `enum GNUNET_MQ_PreferenceKind` in NBO. + */ + uint32_t pk GNUNET_PACKED; + + /** + * Peer to get address suggestions for. + */ + struct GNUNET_PeerIdentity peer; + + /** + * How much bandwidth in bytes/second does the application expect? + */ + struct GNUNET_BANDWIDTH_Value32NBO bw; +}; + + +/** + * We got an address of another peer, TRANSPORT service + * should validate it. There is no response. + */ +struct RequestHelloValidationMessage +{ + /** + * Type is #GNUNET_MESSAGE_TYPE_TRANSPORT_REQUEST_HELLO_VALIDATION. + */ + struct GNUNET_MessageHeader header; + + /** + * What type of network does the other peer claim this is? + * A `enum GNUNET_NetworkType` in NBO. + */ + uint32_t nt GNUNET_PACKED; + + /** + * Peer to the address is presumably for. + */ + struct GNUNET_PeerIdentity peer; + + /* followed by 0-terminated address to validate */ +}; + +GNUNET_NETWORK_STRUCT_END + +/* end of transport.h */ +#endif diff --git a/src/service/transport/transport_api2_application.c b/src/service/transport/transport_api2_application.c new file mode 100644 index 000000000..00f5f62eb --- /dev/null +++ b/src/service/transport/transport_api2_application.c @@ -0,0 +1,397 @@ +/* + This file is part of GNUnet. + Copyright (C) 2010--2019 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 . + + SPDX-License-Identifier: AGPL3.0-or-later + */ +/** + * @file transport/transport_api2_application.c + * @brief enable clients to ask TRANSPORT about establishing connections to peers + * @author Christian Grothoff + * @author Matthias Wachs + */ +#include "platform.h" +#include "gnunet_transport_application_service.h" +#include "gnunet_transport_core_service.h" +#include "transport.h" + + +#define LOG(kind, ...) \ + GNUNET_log_from (kind, "transport-application-api", __VA_ARGS__) + + +/** + * Handle for TRANSPORT address suggestion requests. + */ +struct GNUNET_TRANSPORT_ApplicationSuggestHandle +{ + /** + * ID of the peer for which address suggestion was requested. + */ + struct GNUNET_PeerIdentity id; + + /** + * Connecitivity handle this suggestion handle belongs to. + */ + struct GNUNET_TRANSPORT_ApplicationHandle *ch; + + /** + * What preference is being expressed? + */ + enum GNUNET_MQ_PriorityPreferences pk; + + /** + * How much bandwidth does the client expect? + */ + struct GNUNET_BANDWIDTH_Value32NBO bw; +}; + + +/** + * Handle to the TRANSPORT subsystem for application management. + */ +struct GNUNET_TRANSPORT_ApplicationHandle +{ + /** + * Our configuration. + */ + const struct GNUNET_CONFIGURATION_Handle *cfg; + + /** + * Map with the identities of all the peers for which we would + * like to have address suggestions. The key is the PID, the + * value is currently the `struct GNUNET_TRANSPORT_ApplicationSuggestHandle` + */ + struct GNUNET_CONTAINER_MultiPeerMap *sug_requests; + + /** + * Message queue for sending requests to the TRANSPORT service. + */ + struct GNUNET_MQ_Handle *mq; + + /** + * Task to trigger reconnect. + */ + struct GNUNET_SCHEDULER_Task *task; + + /** + * Reconnect backoff delay. + */ + struct GNUNET_TIME_Relative backoff; +}; + + +/** + * Re-establish the connection to the TRANSPORT service. + * + * @param ch handle to use to re-connect. + */ +static void +reconnect (struct GNUNET_TRANSPORT_ApplicationHandle *ch); + + +/** + * Re-establish the connection to the TRANSPORT service. + * + * @param cls handle to use to re-connect. + */ +static void +reconnect_task (void *cls) +{ + struct GNUNET_TRANSPORT_ApplicationHandle *ch = cls; + + ch->task = NULL; + reconnect (ch); +} + + +/** + * Disconnect from TRANSPORT and then reconnect. + * + * @param ch our handle + */ +static void +force_reconnect (struct GNUNET_TRANSPORT_ApplicationHandle *ch) +{ + if (NULL != ch->mq) + { + GNUNET_MQ_destroy (ch->mq); + ch->mq = NULL; + } + ch->backoff = GNUNET_TIME_STD_BACKOFF (ch->backoff); + ch->task = GNUNET_SCHEDULER_add_delayed (ch->backoff, &reconnect_task, ch); +} + + +/** + * We encountered an error handling the MQ to the + * TRANSPORT service. Reconnect. + * + * @param cls the `struct GNUNET_TRANSPORT_ApplicationHandle` + * @param error details about the error + */ +static void +error_handler (void *cls, enum GNUNET_MQ_Error error) +{ + struct GNUNET_TRANSPORT_ApplicationHandle *ch = cls; + + LOG (GNUNET_ERROR_TYPE_DEBUG, + "TRANSPORT connection died (code %d), reconnecting\n", + (int) error); + force_reconnect (ch); +} + + +/** + * Transmit request for an address suggestion. + * + * @param cls the `struct GNUNET_TRANSPORT_ApplicationHandle` + * @param peer peer to ask for an address suggestion for + * @param value the `struct GNUNET_TRANSPORT_SuggestHandle` + * @return #GNUNET_OK (continue to iterate), #GNUNET_SYSERR on + * failure (message queue no longer exists) + */ +static int +transmit_suggestion (void *cls, + const struct GNUNET_PeerIdentity *peer, + void *value) +{ + struct GNUNET_TRANSPORT_ApplicationHandle *ch = cls; + struct GNUNET_TRANSPORT_ApplicationSuggestHandle *sh = value; + struct GNUNET_MQ_Envelope *ev; + struct ExpressPreferenceMessage *m; + + if (NULL == ch->mq) + return GNUNET_SYSERR; + ev = GNUNET_MQ_msg (m, GNUNET_MESSAGE_TYPE_TRANSPORT_SUGGEST); + m->pk = htonl ((uint32_t) sh->pk); + m->bw = sh->bw; + m->peer = *peer; + GNUNET_MQ_send (ch->mq, ev); + return GNUNET_OK; +} + + +/** + * Re-establish the connection to the TRANSPORT service. + * + * @param ch handle to use to re-connect. + */ +static void +reconnect (struct GNUNET_TRANSPORT_ApplicationHandle *ch) +{ + static const struct GNUNET_MQ_MessageHandler handlers[] = { { NULL, 0, 0 } }; + + GNUNET_assert (NULL == ch->mq); + ch->mq = + GNUNET_CLIENT_connect (ch->cfg, "transport", handlers, &error_handler, ch); + if (NULL == ch->mq) + { + force_reconnect (ch); + return; + } + GNUNET_CONTAINER_multipeermap_iterate (ch->sug_requests, + &transmit_suggestion, + ch); +} + + +/** + * Initialize the TRANSPORT application suggestion client handle. + * + * @param cfg configuration to use + * @return transport application handle, NULL on error + */ +struct GNUNET_TRANSPORT_ApplicationHandle * +GNUNET_TRANSPORT_application_init ( + const struct GNUNET_CONFIGURATION_Handle *cfg) +{ + struct GNUNET_TRANSPORT_ApplicationHandle *ch; + + ch = GNUNET_new (struct GNUNET_TRANSPORT_ApplicationHandle); + ch->cfg = cfg; + ch->sug_requests = GNUNET_CONTAINER_multipeermap_create (32, GNUNET_YES); + reconnect (ch); + return ch; +} + + +/** + * Function called to free all `struct GNUNET_TRANSPORT_ApplicationSuggestHandle`s + * in the map. + * + * @param cls NULL + * @param key the key + * @param value the value to free + * @return #GNUNET_OK (continue to iterate) + */ +static int +free_sug_handle (void *cls, const struct GNUNET_PeerIdentity *key, void *value) +{ + struct GNUNET_TRANSPORT_ApplicationSuggestHandle *cur = value; + + GNUNET_free (cur); + return GNUNET_OK; +} + + +/** + * Client is done with TRANSPORT application management, release resources. + * + * @param ch handle to release + */ +void +GNUNET_TRANSPORT_application_done ( + struct GNUNET_TRANSPORT_ApplicationHandle *ch) +{ + if (NULL != ch->mq) + { + GNUNET_MQ_destroy (ch->mq); + ch->mq = NULL; + } + if (NULL != ch->task) + { + GNUNET_SCHEDULER_cancel (ch->task); + ch->task = NULL; + } + GNUNET_CONTAINER_multipeermap_iterate (ch->sug_requests, + &free_sug_handle, + NULL); + GNUNET_CONTAINER_multipeermap_destroy (ch->sug_requests); + GNUNET_free (ch); +} + + +/** + * An application would like TRANSPORT to connect to a peer. + * + * @param ch handle + * @param peer identity of the peer we need an address for + * @param pk what kind of application will the application require (can be + * #GNUNET_MQ_PRIO_BACKGROUND, we will still try to connect) + * @param bw desired bandwidth, can be zero (we will still try to connect) + * @return suggest handle, NULL if a request is already pending + */ +struct GNUNET_TRANSPORT_ApplicationSuggestHandle * +GNUNET_TRANSPORT_application_suggest ( + struct GNUNET_TRANSPORT_ApplicationHandle *ch, + const struct GNUNET_PeerIdentity *peer, + enum GNUNET_MQ_PriorityPreferences pk, + struct GNUNET_BANDWIDTH_Value32NBO bw) +{ + struct GNUNET_TRANSPORT_ApplicationSuggestHandle *s; + + s = GNUNET_new (struct GNUNET_TRANSPORT_ApplicationSuggestHandle); + s->ch = ch; + s->id = *peer; + s->pk = pk; + s->bw = bw; + (void) GNUNET_CONTAINER_multipeermap_put ( + ch->sug_requests, + &s->id, + s, + GNUNET_CONTAINER_MULTIHASHMAPOPTION_MULTIPLE); + LOG (GNUNET_ERROR_TYPE_DEBUG, + "Requesting TRANSPORT to suggest address for `%s'\n", + GNUNET_i2s (peer)); + if (NULL == ch->mq) + return s; + GNUNET_assert (GNUNET_OK == transmit_suggestion (ch, &s->id, s)); + return s; +} + + +/** + * We no longer care about being connected to a peer. + * + * @param sh handle to stop + */ +void +GNUNET_TRANSPORT_application_suggest_cancel ( + struct GNUNET_TRANSPORT_ApplicationSuggestHandle *sh) +{ + struct GNUNET_TRANSPORT_ApplicationHandle *ch = sh->ch; + struct GNUNET_MQ_Envelope *ev; + struct ExpressPreferenceMessage *m; + + LOG (GNUNET_ERROR_TYPE_DEBUG, + "Telling TRANSPORT we no longer care for an address for `%s'\n", + GNUNET_i2s (&sh->id)); + GNUNET_assert ( + GNUNET_OK == + GNUNET_CONTAINER_multipeermap_remove (ch->sug_requests, &sh->id, sh)); + if (NULL == ch->mq) + { + GNUNET_free (sh); + return; + } + ev = GNUNET_MQ_msg (m, GNUNET_MESSAGE_TYPE_TRANSPORT_SUGGEST_CANCEL); + m->pk = htonl ((uint32_t) sh->pk); + m->bw = sh->bw; + m->peer = sh->id; + GNUNET_MQ_send (ch->mq, ev); + GNUNET_free (sh); +} + + +/** + * An application (or a communicator) has received a HELLO (or other address + * data of another peer) and wants TRANSPORT to validate that the address is + * correct. The result is NOT returned, in fact TRANSPORT may do nothing + * (i.e. if it has too many active validations or recently tried this one + * already). If the @a addr validates, TRANSPORT will persist the address + * with PEERSTORE. + * + * @param ch handle + * @param peer identity of the peer we have an address for + * @param nt network type of @a addr (as claimed by the other peer); + * used by TRANSPORT to avoid trying @a addr's that really cannot work + * due to network type mismatches + * @param addr address to validate + */ +void +GNUNET_TRANSPORT_application_validate ( + struct GNUNET_TRANSPORT_ApplicationHandle *ch, + const struct GNUNET_PeerIdentity *peer, + enum GNUNET_NetworkType nt, + const char *addr) +{ + struct GNUNET_MQ_Envelope *ev; + struct RequestHelloValidationMessage *m; + size_t alen; + + if (NULL == ch->mq) + { + GNUNET_log ( + GNUNET_ERROR_TYPE_WARNING, + "Address validation for %s:%s skipped as transport is not connected\n", + GNUNET_i2s (peer), + addr); + return; + } + alen = strlen (addr) + 1; + ev = + GNUNET_MQ_msg_extra (m, + alen, + GNUNET_MESSAGE_TYPE_TRANSPORT_REQUEST_HELLO_VALIDATION); + m->peer = *peer; + m->nt = htonl ((uint32_t) nt); + memcpy (&m[1], addr, alen); + GNUNET_MQ_send (ch->mq, ev); +} + + +/* end of transport_api2_application.c */ diff --git a/src/service/transport/transport_api2_communication.c b/src/service/transport/transport_api2_communication.c new file mode 100644 index 000000000..0a7636843 --- /dev/null +++ b/src/service/transport/transport_api2_communication.c @@ -0,0 +1,1114 @@ +/* + This file is part of GNUnet. + Copyright (C) 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 . + + SPDX-License-Identifier: AGPL3.0-or-later + */ + +/** + * @file transport/transport_api2_communication.c + * @brief implementation of the gnunet_transport_communication_service.h API + * @author Christian Grothoff + */ +#include "platform.h" +#include "gnunet_util_lib.h" +#include "gnunet_protocols.h" +#include "gnunet_transport_communication_service.h" +#include "transport.h" + + +/** + * How many messages do we keep at most in the queue to the + * transport service before we start to drop (default, + * can be changed via the configuration file). + */ +#define DEFAULT_MAX_QUEUE_LENGTH 16 + + +/** + * Information we track per packet to enable flow control. + */ +struct FlowControl +{ + /** + * Kept in a DLL. + */ + struct FlowControl *next; + + /** + * Kept in a DLL. + */ + struct FlowControl *prev; + + /** + * Function to call once the message was processed. + */ + GNUNET_TRANSPORT_MessageCompletedCallback cb; + + /** + * Closure for @e cb + */ + void *cb_cls; + + /** + * Which peer is this about? + */ + struct GNUNET_PeerIdentity sender; + + /** + * More-or-less unique ID for the message. + */ + uint64_t id; +}; + + +/** + * Information we track per message to tell the transport about + * success or failures. + */ +struct AckPending +{ + /** + * Kept in a DLL. + */ + struct AckPending *next; + + /** + * Kept in a DLL. + */ + struct AckPending *prev; + + /** + * Communicator this entry belongs to. + */ + struct GNUNET_TRANSPORT_CommunicatorHandle *ch; + + /** + * Which peer is this about? + */ + struct GNUNET_PeerIdentity receiver; + + /** + * More-or-less unique ID for the message. + */ + uint64_t mid; + + /** + * Queue ID of the queue which will be used for the message. + */ + uint32_t qid; +}; + + +/** + * Opaque handle to the transport service for communicators. + */ +struct GNUNET_TRANSPORT_CommunicatorHandle +{ + /** + * Head of DLL of addresses this communicator offers to the transport service. + */ + struct GNUNET_TRANSPORT_AddressIdentifier *ai_head; + + /** + * Tail of DLL of addresses this communicator offers to the transport service. + */ + struct GNUNET_TRANSPORT_AddressIdentifier *ai_tail; + + /** + * DLL of messages awaiting flow control confirmation (ack). + */ + struct FlowControl *fc_head; + + /** + * DLL of messages awaiting flow control confirmation (ack). + */ + struct FlowControl *fc_tail; + + /** + * DLL of messages awaiting transmission confirmation (ack). + */ + struct AckPending *ap_head; + + /** + * DLL of messages awaiting transmission confirmation (ack). + */ + struct AckPending *ap_tail; + + /** + * DLL of queues we offer. + */ + struct GNUNET_TRANSPORT_QueueHandle *queue_head; + + /** + * DLL of queues we offer. + */ + struct GNUNET_TRANSPORT_QueueHandle *queue_tail; + + /** + * Our configuration. + */ + const struct GNUNET_CONFIGURATION_Handle *cfg; + + /** + * Config section to use. + */ + const char *config_section; + + /** + * Address prefix to use. + */ + const char *addr_prefix; + + /** + * Function to call when the transport service wants us to initiate + * a communication channel with another peer. + */ + GNUNET_TRANSPORT_CommunicatorMqInit mq_init; + + /** + * Closure for @e mq_init. + */ + void *mq_init_cls; + + /** + * Function to call when the transport service receives messages + * for a communicator (i.e. for NAT traversal or for non-bidirectional + * communicators). + */ + GNUNET_TRANSPORT_CommunicatorNotify notify_cb; + + /** + * Closure for @e notify_Cb. + */ + void *notify_cb_cls; + + /** + * Queue to talk to the transport service. + */ + struct GNUNET_MQ_Handle *mq; + + /** + * Maximum permissible queue length. + */ + unsigned long long max_queue_length; + + /** + * Flow-control identifier generator. + */ + uint64_t fc_gen; + + /** + * Internal UUID for the address used in communication with the + * transport service. + */ + uint32_t aid_gen; + + /** + * Queue identifier generator. + */ + uint32_t queue_gen; + + /** + * Characteristics of the communicator. + */ + enum GNUNET_TRANSPORT_CommunicatorCharacteristics cc; +}; + + +/** + * Handle returned to identify the internal data structure the transport + * API has created to manage a message queue to a particular peer. + */ +struct GNUNET_TRANSPORT_QueueHandle +{ + /** + * Kept in a DLL. + */ + struct GNUNET_TRANSPORT_QueueHandle *next; + + /** + * Kept in a DLL. + */ + struct GNUNET_TRANSPORT_QueueHandle *prev; + + /** + * Handle this queue belongs to. + */ + struct GNUNET_TRANSPORT_CommunicatorHandle *ch; + + /** + * Address used by the communication queue. + */ + char *address; + + /** + * The queue itself. + */ + struct GNUNET_MQ_Handle *mq; + + /** + * Which peer we can communciate with. + */ + struct GNUNET_PeerIdentity peer; + + /** + * Network type of the communication queue. + */ + enum GNUNET_NetworkType nt; + + /** + * Communication status of the queue. + */ + enum GNUNET_TRANSPORT_ConnectionStatus cs; + + /** + * ID for this queue when talking to the transport service. + */ + uint32_t queue_id; + + /** + * Maximum transmission unit for the queue. + */ + uint32_t mtu; + + /** + * Queue length. + */ + uint64_t q_len; + /** + * Queue priority. + */ + uint32_t priority; +}; + + +/** + * Internal representation of an address a communicator is + * currently providing for the transport service. + */ +struct GNUNET_TRANSPORT_AddressIdentifier +{ + /** + * Kept in a DLL. + */ + struct GNUNET_TRANSPORT_AddressIdentifier *next; + + /** + * Kept in a DLL. + */ + struct GNUNET_TRANSPORT_AddressIdentifier *prev; + + /** + * Transport handle where the address was added. + */ + struct GNUNET_TRANSPORT_CommunicatorHandle *ch; + + /** + * The actual address. + */ + char *address; + + /** + * When does the address expire? (Expected lifetime of the + * address.) + */ + struct GNUNET_TIME_Relative expiration; + + /** + * Internal UUID for the address used in communication with the + * transport service. + */ + uint32_t aid; + + /** + * Network type for the address. + */ + enum GNUNET_NetworkType nt; +}; + + +/** + * (re)connect our communicator to the transport service + * + * @param ch handle to reconnect + */ +static void +reconnect (struct GNUNET_TRANSPORT_CommunicatorHandle *ch); + + +/** + * Send message to the transport service about address @a ai + * being now available. + * + * @param ai address to add + */ +static void +send_add_address (struct GNUNET_TRANSPORT_AddressIdentifier *ai) +{ + struct GNUNET_MQ_Envelope *env; + struct GNUNET_TRANSPORT_AddAddressMessage *aam; + + if (NULL == ai->ch->mq) + return; + env = GNUNET_MQ_msg_extra (aam, + strlen (ai->address) + 1, + GNUNET_MESSAGE_TYPE_TRANSPORT_ADD_ADDRESS); + aam->expiration = GNUNET_TIME_relative_hton (ai->expiration); + aam->nt = htonl ((uint32_t) ai->nt); + memcpy (&aam[1], ai->address, strlen (ai->address) + 1); + GNUNET_MQ_send (ai->ch->mq, env); +} + + +/** + * Send message to the transport service about address @a ai + * being no longer available. + * + * @param ai address to delete + */ +static void +send_del_address (struct GNUNET_TRANSPORT_AddressIdentifier *ai) +{ + struct GNUNET_MQ_Envelope *env; + struct GNUNET_TRANSPORT_DelAddressMessage *dam; + + if (NULL == ai->ch->mq) + return; + env = GNUNET_MQ_msg (dam, GNUNET_MESSAGE_TYPE_TRANSPORT_DEL_ADDRESS); + dam->aid = htonl (ai->aid); + GNUNET_MQ_send (ai->ch->mq, env); +} + + +/** + * Send message to the transport service about queue @a qh + * being now available. + * + * @param qh queue to add + */ +static void +send_add_queue (struct GNUNET_TRANSPORT_QueueHandle *qh) +{ + struct GNUNET_MQ_Envelope *env; + struct GNUNET_TRANSPORT_AddQueueMessage *aqm; + + if (NULL == qh->ch->mq) + return; + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Sending `GNUNET_MESSAGE_TYPE_TRANSPORT_QUEUE_SETUP` message\n"); + env = GNUNET_MQ_msg_extra (aqm, + strlen (qh->address) + 1, + GNUNET_MESSAGE_TYPE_TRANSPORT_QUEUE_SETUP); + aqm->qid = htonl (qh->queue_id); + aqm->receiver = qh->peer; + aqm->nt = htonl ((uint32_t) qh->nt); + aqm->mtu = htonl (qh->mtu); + aqm->q_len = GNUNET_htonll (qh->q_len); + aqm->priority = htonl (qh->priority); + aqm->cs = htonl ((uint32_t) qh->cs); + memcpy (&aqm[1], qh->address, strlen (qh->address) + 1); + GNUNET_MQ_send (qh->ch->mq, env); +} + + +/** + * Send message to the transport service about queue @a qh + * updated. + * + * @param qh queue to add + */ +static void +send_update_queue (struct GNUNET_TRANSPORT_QueueHandle *qh) +{ + struct GNUNET_MQ_Envelope *env; + struct GNUNET_TRANSPORT_UpdateQueueMessage *uqm; + + if (NULL == qh->ch->mq) + return; + env = GNUNET_MQ_msg (uqm, GNUNET_MESSAGE_TYPE_TRANSPORT_QUEUE_UPDATE); + uqm->qid = htonl (qh->queue_id); + uqm->receiver = qh->peer; + uqm->nt = htonl ((uint32_t) qh->nt); + uqm->mtu = htonl (qh->mtu); + uqm->q_len = GNUNET_htonll (qh->q_len); + uqm->priority = htonl (qh->priority); + uqm->cs = htonl ((uint32_t) qh->cs); + GNUNET_MQ_send (qh->ch->mq, env); +} + + +/** + * Send message to the transport service about queue @a qh + * being no longer available. + * + * @param qh queue to delete + */ +static void +send_del_queue (struct GNUNET_TRANSPORT_QueueHandle *qh) +{ + struct GNUNET_MQ_Envelope *env; + struct GNUNET_TRANSPORT_DelQueueMessage *dqm; + + if (NULL == qh->ch->mq) + return; + env = GNUNET_MQ_msg (dqm, GNUNET_MESSAGE_TYPE_TRANSPORT_QUEUE_TEARDOWN); + dqm->qid = htonl (qh->queue_id); + dqm->receiver = qh->peer; + GNUNET_MQ_send (qh->ch->mq, env); +} + + +/** + * Disconnect from the transport service. Purges + * all flow control entries as we will no longer receive + * the ACKs. Purges the ack pending entries as the + * transport will no longer expect the confirmations. + * + * @param ch service to disconnect from + */ +static void +disconnect (struct GNUNET_TRANSPORT_CommunicatorHandle *ch) +{ + struct FlowControl *fcn; + struct AckPending *apn; + + for (struct FlowControl *fc = ch->fc_head; NULL != fc; fc = fcn) + { + fcn = fc->next; + GNUNET_CONTAINER_DLL_remove (ch->fc_head, ch->fc_tail, fc); + fc->cb (fc->cb_cls, GNUNET_SYSERR); + GNUNET_free (fc); + } + for (struct AckPending *ap = ch->ap_head; NULL != ap; ap = apn) + { + apn = ap->next; + GNUNET_CONTAINER_DLL_remove (ch->ap_head, ch->ap_tail, ap); + GNUNET_free (ap); + } + if (NULL == ch->mq) + return; + GNUNET_MQ_destroy (ch->mq); + ch->mq = NULL; +} + + +/** + * Function called on MQ errors. + */ +static void +error_handler (void *cls, enum GNUNET_MQ_Error error) +{ + struct GNUNET_TRANSPORT_CommunicatorHandle *ch = cls; + + GNUNET_log (GNUNET_ERROR_TYPE_INFO, + "MQ failure %d, reconnecting to transport service.\n", + error); + disconnect (ch); + /* TODO: maybe do this with exponential backoff/delay */ + reconnect (ch); +} + + +/** + * Transport service acknowledged a message we gave it + * (with flow control enabled). Tell the communicator. + * + * @param cls our `struct GNUNET_TRANSPORT_CommunicatorHandle *` + * @param incoming_ack the ack + */ +static void +handle_incoming_ack ( + void *cls, + const struct GNUNET_TRANSPORT_IncomingMessageAck *incoming_ack) +{ + struct GNUNET_TRANSPORT_CommunicatorHandle *ch = cls; + + for (struct FlowControl *fc = ch->fc_head; NULL != fc; fc = fc->next) + { + if ((fc->id == incoming_ack->fc_id) && + (0 == memcmp (&fc->sender, + &incoming_ack->sender, + sizeof(struct GNUNET_PeerIdentity)))) + { + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Done with message with flow control id %" PRIu64 + " for sender %s from sender %s\n", + incoming_ack->fc_id, + GNUNET_i2s (&fc->sender), + GNUNET_i2s (&incoming_ack->sender)); + GNUNET_CONTAINER_DLL_remove (ch->fc_head, ch->fc_tail, fc); + fc->cb (fc->cb_cls, GNUNET_OK); + GNUNET_free (fc); + return; + } + } + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Message with flow control id %" PRIu64 + " from sender %s not found\n", + incoming_ack->fc_id, + GNUNET_i2s (&incoming_ack->sender)); + GNUNET_break (0); + disconnect (ch); + /* TODO: maybe do this with exponential backoff/delay */ + reconnect (ch); +} + + +/** + * Transport service wants us to create a queue. Check if @a cq + * is well-formed. + * + * @param cls our `struct GNUNET_TRANSPORT_CommunicatorHandle *` + * @param cq the queue creation request + * @return #GNUNET_OK if @a smt is well-formed + */ +static int +check_create_queue (void *cls, const struct GNUNET_TRANSPORT_CreateQueue *cq) +{ + (void) cls; + GNUNET_MQ_check_zero_termination (cq); + return GNUNET_OK; +} + + +/** + * Transport service wants us to create a queue. Tell the communicator. + * + * @param cls our `struct GNUNET_TRANSPORT_CommunicatorHandle *` + * @param cq the queue creation request + */ +static void +handle_create_queue (void *cls, const struct GNUNET_TRANSPORT_CreateQueue *cq) +{ + struct GNUNET_TRANSPORT_CommunicatorHandle *ch = cls; + const char *addr = (const char *) &cq[1]; + struct GNUNET_TRANSPORT_CreateQueueResponse *cqr; + struct GNUNET_MQ_Envelope *env; + + if (GNUNET_OK != ch->mq_init (ch->mq_init_cls, &cq->receiver, addr)) + { + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "Address `%s' invalid for this communicator\n", + addr); + env = GNUNET_MQ_msg (cqr, GNUNET_MESSAGE_TYPE_TRANSPORT_QUEUE_CREATE_FAIL); + } + else + { + env = GNUNET_MQ_msg (cqr, GNUNET_MESSAGE_TYPE_TRANSPORT_QUEUE_CREATE_OK); + } + cqr->request_id = cq->request_id; + GNUNET_MQ_send (ch->mq, env); +} + + +/** + * Transport service wants us to send a message. Check if @a smt + * is well-formed. + * + * @param cls our `struct GNUNET_TRANSPORT_CommunicatorHandle *` + * @param smt the transmission request + * @return #GNUNET_OK if @a smt is well-formed + */ +static int +check_send_msg (void *cls, const struct GNUNET_TRANSPORT_SendMessageTo *smt) +{ + (void) cls; + GNUNET_MQ_check_boxed_message (smt); + return GNUNET_OK; +} + + +/** + * Notify transport service about @a status of a message with + * @a mid sent to @a receiver. + * + * @param ch handle + * @param status #GNUNET_OK on success, #GNUNET_SYSERR on failure + * @param receiver which peer was the receiver + * @param mid message that the ack is about + */ +static void +send_ack (struct GNUNET_TRANSPORT_CommunicatorHandle *ch, + int status, + const struct GNUNET_PeerIdentity *receiver, + uint64_t mid, + uint32_t qid) +{ + struct GNUNET_MQ_Envelope *env; + struct GNUNET_TRANSPORT_SendMessageToAck *ack; + + env = GNUNET_MQ_msg (ack, GNUNET_MESSAGE_TYPE_TRANSPORT_SEND_MSG_ACK); + ack->status = htonl (status); + ack->mid = mid; + ack->qid = qid; + ack->receiver = *receiver; + GNUNET_MQ_send (ch->mq, env); +} + + +/** + * Message queue transmission by communicator was successful, + * notify transport service. + * + * @param cls an `struct AckPending *` + */ +static void +send_ack_cb (void *cls) +{ + struct AckPending *ap = cls; + struct GNUNET_TRANSPORT_CommunicatorHandle *ch = ap->ch; + + GNUNET_CONTAINER_DLL_remove (ch->ap_head, ch->ap_tail, ap); + send_ack (ch, GNUNET_OK, &ap->receiver, ap->mid, ap->qid); + GNUNET_free (ap); +} + + +/** + * Transport service wants us to send a message. Tell the communicator. + * + * @param cls our `struct GNUNET_TRANSPORT_CommunicatorHandle *` + * @param smt the transmission request + */ +static void +handle_send_msg (void *cls, const struct GNUNET_TRANSPORT_SendMessageTo *smt) +{ + struct GNUNET_TRANSPORT_CommunicatorHandle *ch = cls; + const struct GNUNET_MessageHeader *mh; + struct GNUNET_MQ_Envelope *env; + struct AckPending *ap; + struct GNUNET_TRANSPORT_QueueHandle *qh; + + for (qh = ch->queue_head; NULL != qh; qh = qh->next) + if ((qh->queue_id == ntohl (smt->qid)) && + (0 == memcmp (&qh->peer, + &smt->receiver, + sizeof(struct GNUNET_PeerIdentity)))) + break; + if (NULL == qh) + { + /* queue is already gone, tell transport this one failed */ + GNUNET_log (GNUNET_ERROR_TYPE_INFO, + "Transmission failed, queue no longer exists.\n"); + send_ack (ch, GNUNET_NO, &smt->receiver, smt->mid, smt->qid); + return; + } + ap = GNUNET_new (struct AckPending); + ap->ch = ch; + ap->receiver = smt->receiver; + ap->mid = smt->mid; + ap->qid = smt->qid; + GNUNET_CONTAINER_DLL_insert (ch->ap_head, ch->ap_tail, ap); + mh = (const struct GNUNET_MessageHeader *) &smt[1]; + env = GNUNET_MQ_msg_copy (mh); + GNUNET_MQ_notify_sent (env, &send_ack_cb, ap); + GNUNET_MQ_send (qh->mq, env); +} + + +/** + * Transport service gives us backchannel message. Check if @a bi + * is well-formed. + * + * @param cls our `struct GNUNET_TRANSPORT_CommunicatorHandle *` + * @param bi the backchannel message + * @return #GNUNET_OK if @a smt is well-formed + */ +static int +check_backchannel_incoming ( + void *cls, + const struct GNUNET_TRANSPORT_CommunicatorBackchannelIncoming *bi) +{ + (void) cls; + GNUNET_MQ_check_boxed_message (bi); + return GNUNET_OK; +} + + +/** + * Transport service gives us backchannel message. Handle it. + * + * @param cls our `struct GNUNET_TRANSPORT_CommunicatorHandle *` + * @param bi the backchannel message + */ +static void +handle_backchannel_incoming ( + void *cls, + const struct GNUNET_TRANSPORT_CommunicatorBackchannelIncoming *bi) +{ + struct GNUNET_TRANSPORT_CommunicatorHandle *ch = cls; + if (NULL != ch->notify_cb) + ch->notify_cb (ch->notify_cb_cls, + &bi->pid, + (const struct GNUNET_MessageHeader *) &bi[1]); + else + GNUNET_log ( + GNUNET_ERROR_TYPE_INFO, + _ ("Dropped backchanel message: handler not provided by communicator\n")); +} + + +/** + * (re)connect our communicator to the transport service + * + * @param ch handle to reconnect + */ +static void +reconnect (struct GNUNET_TRANSPORT_CommunicatorHandle *ch) +{ + struct GNUNET_MQ_MessageHandler handlers[] = + { GNUNET_MQ_hd_fixed_size (incoming_ack, + GNUNET_MESSAGE_TYPE_TRANSPORT_INCOMING_MSG_ACK, + struct GNUNET_TRANSPORT_IncomingMessageAck, + ch), + GNUNET_MQ_hd_var_size (create_queue, + GNUNET_MESSAGE_TYPE_TRANSPORT_QUEUE_CREATE, + struct GNUNET_TRANSPORT_CreateQueue, + ch), + GNUNET_MQ_hd_var_size (send_msg, + GNUNET_MESSAGE_TYPE_TRANSPORT_SEND_MSG, + struct GNUNET_TRANSPORT_SendMessageTo, + ch), + GNUNET_MQ_hd_var_size ( + backchannel_incoming, + GNUNET_MESSAGE_TYPE_TRANSPORT_COMMUNICATOR_BACKCHANNEL_INCOMING, + struct GNUNET_TRANSPORT_CommunicatorBackchannelIncoming, + ch), + GNUNET_MQ_handler_end () }; + struct GNUNET_TRANSPORT_CommunicatorAvailableMessage *cam; + struct GNUNET_MQ_Envelope *env; + + ch->mq = + GNUNET_CLIENT_connect (ch->cfg, "transport", handlers, &error_handler, ch); + if (NULL == ch->mq) + return; + env = GNUNET_MQ_msg_extra (cam, + strlen (ch->addr_prefix) + 1, + GNUNET_MESSAGE_TYPE_TRANSPORT_NEW_COMMUNICATOR); + cam->cc = htonl ((uint32_t) ch->cc); + memcpy (&cam[1], ch->addr_prefix, strlen (ch->addr_prefix) + 1); + GNUNET_MQ_send (ch->mq, env); + for (struct GNUNET_TRANSPORT_AddressIdentifier *ai = ch->ai_head; NULL != ai; + ai = ai->next) + send_add_address (ai); + for (struct GNUNET_TRANSPORT_QueueHandle *qh = ch->queue_head; NULL != qh; + qh = qh->next) + send_add_queue (qh); +} + + +struct GNUNET_TRANSPORT_CommunicatorHandle * +GNUNET_TRANSPORT_communicator_connect ( + const struct GNUNET_CONFIGURATION_Handle *cfg, + const char *config_section, + const char *addr_prefix, + enum GNUNET_TRANSPORT_CommunicatorCharacteristics cc, + GNUNET_TRANSPORT_CommunicatorMqInit mq_init, + void *mq_init_cls, + GNUNET_TRANSPORT_CommunicatorNotify notify_cb, + void *notify_cb_cls) +{ + struct GNUNET_TRANSPORT_CommunicatorHandle *ch; + + ch = GNUNET_new (struct GNUNET_TRANSPORT_CommunicatorHandle); + ch->cfg = cfg; + ch->config_section = config_section; + ch->addr_prefix = addr_prefix; + ch->mq_init = mq_init; + ch->mq_init_cls = mq_init_cls; + ch->notify_cb = notify_cb; + ch->notify_cb_cls = notify_cb_cls; + ch->cc = cc; + reconnect (ch); + if (GNUNET_OK != + GNUNET_CONFIGURATION_get_value_number (cfg, + config_section, + "MAX_QUEUE_LENGTH", + &ch->max_queue_length)) + ch->max_queue_length = DEFAULT_MAX_QUEUE_LENGTH; + if (NULL == ch->mq) + { + GNUNET_free (ch); + return NULL; + } + return ch; +} + + +/** + * Disconnect from the transport service. + * + * @param ch handle returned from connect + */ +void +GNUNET_TRANSPORT_communicator_disconnect ( + struct GNUNET_TRANSPORT_CommunicatorHandle *ch) +{ + disconnect (ch); + while (NULL != ch->ai_head) + { + GNUNET_break (0); /* communicator forgot to remove address, warn! */ + GNUNET_TRANSPORT_communicator_address_remove (ch->ai_head); + } + GNUNET_free (ch); +} + + +/* ************************* Receiving *************************** */ + + +int +GNUNET_TRANSPORT_communicator_receive ( + struct GNUNET_TRANSPORT_CommunicatorHandle *ch, + const struct GNUNET_PeerIdentity *sender, + const struct GNUNET_MessageHeader *msg, + struct GNUNET_TIME_Relative expected_addr_validity, + GNUNET_TRANSPORT_MessageCompletedCallback cb, + void *cb_cls) +{ + struct GNUNET_MQ_Envelope *env; + struct GNUNET_TRANSPORT_IncomingMessage *im; + uint16_t msize; + + + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "communicator receive\n"); + + if (NULL == ch->mq) + return GNUNET_SYSERR; + if ((NULL == cb) && (GNUNET_MQ_get_length (ch->mq) >= ch->max_queue_length)) + { + GNUNET_log ( + GNUNET_ERROR_TYPE_WARNING, + "Dropping message: transport is too slow, queue length %llu exceeded\n", + ch->max_queue_length); + return GNUNET_NO; + } + + msize = ntohs (msg->size); + env = + GNUNET_MQ_msg_extra (im, msize, GNUNET_MESSAGE_TYPE_TRANSPORT_INCOMING_MSG); + if (NULL == env) + { + GNUNET_break (0); + return GNUNET_SYSERR; + } + im->expected_address_validity = + GNUNET_TIME_relative_hton (expected_addr_validity); + im->sender = *sender; + // FIXME: this is expensive, would be better if we would + // re-design the API to allow us to create the envelope first, + // and then have the application fill in the body so we do + // not have to memcpy() + memcpy (&im[1], msg, msize); + im->fc_on = htonl (GNUNET_NO); + if (NULL != cb) + { + struct FlowControl *fc; + + im->fc_on = htonl (GNUNET_YES); + im->fc_id = ch->fc_gen++; + fc = GNUNET_new (struct FlowControl); + fc->sender = *sender; + fc->id = im->fc_id; + fc->cb = cb; + fc->cb_cls = cb_cls; + GNUNET_CONTAINER_DLL_insert (ch->fc_head, ch->fc_tail, fc); + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Created flow control id %" PRIu64 " for sender %s\n", + fc->id, + GNUNET_i2s (&fc->sender)); + } + GNUNET_MQ_send (ch->mq, env); + return GNUNET_OK; +} + + +/* ************************* Discovery *************************** */ + + +struct GNUNET_TRANSPORT_QueueHandle * +GNUNET_TRANSPORT_communicator_mq_add ( + struct GNUNET_TRANSPORT_CommunicatorHandle *ch, + const struct GNUNET_PeerIdentity *peer, + const char *address, + uint32_t mtu, + uint64_t q_len, + uint32_t priority, + enum GNUNET_NetworkType nt, + enum GNUNET_TRANSPORT_ConnectionStatus cs, + struct GNUNET_MQ_Handle *mq) +{ + struct GNUNET_TRANSPORT_QueueHandle *qh; + + // Do not notify the service if there is no intial capacity. + GNUNET_assert (0 < q_len); + + qh = GNUNET_new (struct GNUNET_TRANSPORT_QueueHandle); + qh->ch = ch; + qh->peer = *peer; + qh->address = GNUNET_strdup (address); + qh->nt = nt; + qh->mtu = mtu; + qh->q_len = q_len; + qh->priority = priority; + qh->cs = cs; + qh->mq = mq; + qh->queue_id = ch->queue_gen++; + GNUNET_CONTAINER_DLL_insert (ch->queue_head, ch->queue_tail, qh); + send_add_queue (qh); + return qh; +} + + +void +GNUNET_TRANSPORT_communicator_mq_update ( + struct GNUNET_TRANSPORT_CommunicatorHandle *ch, + const struct GNUNET_TRANSPORT_QueueHandle *u_qh, + uint64_t q_len, + uint32_t priority) +{ + struct GNUNET_TRANSPORT_QueueHandle *qh; + + for (qh = ch->queue_head; NULL != qh; qh = qh->next) + { + if (u_qh == qh) + break; + } + GNUNET_assert (NULL != qh); + qh->q_len = q_len; + qh->priority = priority; + send_update_queue (qh); +} + + +/** + * Notify transport service that an MQ became unavailable due to a + * disconnect or timeout. + * + * @param qh handle for the queue that must be invalidated + */ +void +GNUNET_TRANSPORT_communicator_mq_del (struct GNUNET_TRANSPORT_QueueHandle *qh) +{ + struct GNUNET_TRANSPORT_CommunicatorHandle *ch = qh->ch; + + send_del_queue (qh); + GNUNET_CONTAINER_DLL_remove (ch->queue_head, ch->queue_tail, qh); + GNUNET_MQ_destroy (qh->mq); + GNUNET_free (qh->address); + GNUNET_free (qh); +} + + +/** + * Notify transport service about an address that this communicator + * provides for this peer. + * + * @param ch connection to transport service + * @param address our address in human-readable format, 0-terminated, UTF-8 + * @param nt which network type does the address belong to? + * @param expiration when does the communicator forsee this address expiring? + */ +struct GNUNET_TRANSPORT_AddressIdentifier * +GNUNET_TRANSPORT_communicator_address_add ( + struct GNUNET_TRANSPORT_CommunicatorHandle *ch, + const char *address, + enum GNUNET_NetworkType nt, + struct GNUNET_TIME_Relative expiration) +{ + struct GNUNET_TRANSPORT_AddressIdentifier *ai; + + ai = GNUNET_new (struct GNUNET_TRANSPORT_AddressIdentifier); + ai->ch = ch; + ai->address = GNUNET_strdup (address); + ai->nt = nt; + ai->expiration = expiration; + ai->aid = ch->aid_gen++; + GNUNET_CONTAINER_DLL_insert (ch->ai_head, ch->ai_tail, ai); + send_add_address (ai); + return ai; +} + + +/** + * Notify transport service about an address that this communicator no + * longer provides for this peer. + * + * @param ai address that is no longer provided + */ +void +GNUNET_TRANSPORT_communicator_address_remove ( + struct GNUNET_TRANSPORT_AddressIdentifier *ai) +{ + struct GNUNET_TRANSPORT_CommunicatorHandle *ch = ai->ch; + + send_del_address (ai); + GNUNET_CONTAINER_DLL_remove (ch->ai_head, ch->ai_tail, ai); + GNUNET_free (ai->address); + GNUNET_free (ai); + ai = NULL; +} + + +/** + * Notify transport service that this communicator no longer provides all its addresses for this peer. + * + * @param ch The communicator handle. + */ +void +GNUNET_TRANSPORT_communicator_address_remove_all ( + struct GNUNET_TRANSPORT_CommunicatorHandle *ch) +{ + struct GNUNET_TRANSPORT_AddressIdentifier *ai = ch->ai_head; + while (NULL != ai) + { + struct GNUNET_TRANSPORT_AddressIdentifier *ai_next = ai->next; + GNUNET_TRANSPORT_communicator_address_remove (ai); + ai = ai_next; + } +} + + +/* ************************* Backchannel *************************** */ + + +void +GNUNET_TRANSPORT_communicator_notify ( + struct GNUNET_TRANSPORT_CommunicatorHandle *ch, + const struct GNUNET_PeerIdentity *pid, + const char *comm, + const struct GNUNET_MessageHeader *header) +{ + struct GNUNET_MQ_Envelope *env; + struct GNUNET_TRANSPORT_CommunicatorBackchannel *cb; + size_t slen = strlen (comm) + 1; + uint16_t mlen = ntohs (header->size); + + GNUNET_assert (mlen + slen + sizeof(*cb) < UINT16_MAX); + env = + GNUNET_MQ_msg_extra (cb, + slen + mlen, + GNUNET_MESSAGE_TYPE_TRANSPORT_COMMUNICATOR_BACKCHANNEL); + cb->pid = *pid; + memcpy (&cb[1], header, mlen); + memcpy (((char *) &cb[1]) + mlen, comm, slen); + GNUNET_MQ_send (ch->mq, env); +} + + +/* end of transport_api2_communication.c */ diff --git a/src/service/transport/transport_api2_core.c b/src/service/transport/transport_api2_core.c new file mode 100644 index 000000000..0d2a0ac7f --- /dev/null +++ b/src/service/transport/transport_api2_core.c @@ -0,0 +1,826 @@ +/* + This file is part of GNUnet. + Copyright (C) 2009-2013, 2016, 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 . + + SPDX-License-Identifier: AGPL3.0-or-later + */ + +/** + * @file transport/transport_api_core.c + * @brief library to access the transport service for message exchange + * @author Christian Grothoff + */ +#include "platform.h" +#include "gnunet_util_lib.h" +#include "gnunet_constants.h" +#include "gnunet_arm_service.h" +#include "gnunet_hello_lib.h" +#include "gnunet_protocols.h" +#include "gnunet_transport_core_service.h" +#include "transport.h" + +#define LOG(kind, ...) GNUNET_log_from (kind, "transport-api-core", __VA_ARGS__) + +/** + * How large to start with for the hashmap of neighbours. + */ +#define STARTING_NEIGHBOURS_SIZE 16 + +/** + * Window size. How many messages to the same target do we pass + * to TRANSPORT without a SEND_OK in between? Small values limit + * thoughput, large values will increase latency. + * + * FIXME-OPTIMIZE: find out what good values are experimentally, + * maybe set adaptively (i.e. to observed available bandwidth). + */ +#define SEND_WINDOW_SIZE 4 + + +/** + * Entry in hash table of all of our current (connected) neighbours. + */ +struct Neighbour +{ + /** + * Identity of this neighbour. + */ + struct GNUNET_PeerIdentity id; + + /** + * Overall transport handle. + */ + struct GNUNET_TRANSPORT_CoreHandle *h; + + /** + * Active message queue for the peer. + */ + struct GNUNET_MQ_Handle *mq; + + /** + * Envelope with the message we are currently transmitting (or NULL). + */ + struct GNUNET_MQ_Envelope *env; + + /** + * Closure for @e mq handlers. + */ + void *handlers_cls; + + /** + * How many messages can we still send to this peer before we should + * throttle? + */ + unsigned int ready_window; + + /** + * Used to indicate our status if @e env is non-NULL. Set to + * #GNUNET_YES if we did pass a message to the MQ and are waiting + * for the call to #notify_send_done(). Set to #GNUNET_NO if the @e + * ready_window is 0 and @e env is waiting for a + * #GNUNET_MESSAGE_TYPE_TRANSPORT_RECV_OK? + */ + int16_t awaiting_done; + + /** + * Size of the message in @e env. + */ + uint16_t env_size; +}; + + +/** + * Handle for the transport service (includes all of the + * state for the transport service). + */ +struct GNUNET_TRANSPORT_CoreHandle +{ + /** + * Closure for the callbacks. + */ + void *cls; + + /** + * Functions to call for received data (template for + * new message queues). + */ + struct GNUNET_MQ_MessageHandler *handlers; + + /** + * function to call on connect events + */ + GNUNET_TRANSPORT_NotifyConnect nc_cb; + + /** + * function to call on disconnect events + */ + GNUNET_TRANSPORT_NotifyDisconnect nd_cb; + + /** + * My client connection to the transport service. + */ + struct GNUNET_MQ_Handle *mq; + + /** + * My configuration. + */ + const struct GNUNET_CONFIGURATION_Handle *cfg; + + /** + * Hash map of the current connected neighbours of this peer. + * Maps peer identities to `struct Neighbour` entries. + */ + struct GNUNET_CONTAINER_MultiPeerMap *neighbours; + + /** + * Peer identity as assumed by this process, or all zeros. + */ + struct GNUNET_PeerIdentity self; + + /** + * ID of the task trying to reconnect to the service. + */ + struct GNUNET_SCHEDULER_Task *reconnect_task; + + /** + * Delay until we try to reconnect. + */ + struct GNUNET_TIME_Relative reconnect_delay; + + /** + * Should we check that @e self matches what the service thinks? + * (if #GNUNET_NO, then @e self is all zeros!). + */ + int check_self; +}; + + +/** + * Function that will schedule the job that will try + * to connect us again to the client. + * + * @param h transport service to reconnect + */ +static void +disconnect_and_schedule_reconnect (struct GNUNET_TRANSPORT_CoreHandle *h); + + +/** + * Get the neighbour list entry for the given peer + * + * @param h our context + * @param peer peer to look up + * @return NULL if no such peer entry exists + */ +static struct Neighbour * +neighbour_find (struct GNUNET_TRANSPORT_CoreHandle *h, + const struct GNUNET_PeerIdentity *peer) +{ + return GNUNET_CONTAINER_multipeermap_get (h->neighbours, peer); +} + + +/** + * Iterator over hash map entries, for deleting state of a neighbour. + * + * @param cls the `struct GNUNET_TRANSPORT_CoreHandle *` + * @param key peer identity + * @param value value in the hash map, the neighbour entry to delete + * @return #GNUNET_YES if we should continue to + * iterate, + * #GNUNET_NO if not. + */ +static int +neighbour_delete (void *cls, const struct GNUNET_PeerIdentity *key, void *value) +{ + struct GNUNET_TRANSPORT_CoreHandle *handle = cls; + struct Neighbour *n = value; + + LOG (GNUNET_ERROR_TYPE_DEBUG, + "Dropping entry for neighbour `%s'.\n", + GNUNET_i2s (key)); + if (NULL != handle->nd_cb) + handle->nd_cb (handle->cls, &n->id, n->handlers_cls); + if (NULL != n->env) + { + GNUNET_MQ_send_cancel (n->env); + n->env = NULL; + } + GNUNET_MQ_destroy (n->mq); + GNUNET_assert (NULL == n->mq); + GNUNET_assert ( + GNUNET_YES == + GNUNET_CONTAINER_multipeermap_remove (handle->neighbours, key, n)); + GNUNET_free (n); + return GNUNET_YES; +} + + +/** + * Generic error handler, called with the appropriate + * error code and the same closure specified at the creation of + * the message queue. + * Not every message queue implementation supports an error handler. + * + * @param cls closure with the `struct GNUNET_TRANSPORT_CoreHandle *` + * @param error error code + */ +static void +mq_error_handler (void *cls, enum GNUNET_MQ_Error error) +{ + struct GNUNET_TRANSPORT_CoreHandle *h = cls; + + LOG (GNUNET_ERROR_TYPE_DEBUG, + "Error %u received from transport service, disconnecting temporarily.\n", + error); + disconnect_and_schedule_reconnect (h); +} + + +/** + * A message from the handler's message queue to a neighbour was + * transmitted. Now trigger (possibly delayed) notification of the + * neighbour's message queue that we are done and thus ready for + * the next message. Note that the MQ being ready is independent + * of the send window, as we may queue many messages and simply + * not pass them to TRANSPORT if the send window is insufficient. + * + * @param cls the `struct Neighbour` where the message was sent + */ +static void +notify_send_done (void *cls) +{ + struct Neighbour *n = cls; + + n->awaiting_done = GNUNET_NO; + n->env = NULL; + if (0 < n->ready_window) + GNUNET_MQ_impl_send_continue (n->mq); +} + + +/** + * We have an envelope waiting for transmission at @a n, and + * our transmission window is positive. Perform the transmission. + * + * @param n neighbour to perform transmission for + */ +static void +do_send (struct Neighbour *n) +{ + GNUNET_assert (0 < n->ready_window); + GNUNET_assert (NULL != n->env); + n->ready_window--; + n->awaiting_done = GNUNET_YES; + GNUNET_MQ_notify_sent (n->env, ¬ify_send_done, n); + GNUNET_MQ_send (n->h->mq, n->env); + LOG (GNUNET_ERROR_TYPE_DEBUG, + "Passed message of type %u for neighbour `%s' to TRANSPORT. ready_window %u\n", + ntohs (GNUNET_MQ_env_get_msg (n->env)->type), + GNUNET_i2s (&n->id), + n->ready_window); +} + + +/** + * Implement sending functionality of a message queue. + * Called one message at a time. Should send the @a msg + * to the transport service and then notify the queue + * once we are ready for the next one. + * + * @param mq the message queue + * @param msg the message to send + * @param impl_state state of the implementation + */ +static void +mq_send_impl (struct GNUNET_MQ_Handle *mq, + const struct GNUNET_MessageHeader *msg, + void *impl_state) +{ + struct Neighbour *n = impl_state; + struct OutboundMessage *obm; + uint16_t msize; + + msize = ntohs (msg->size); + if (msize >= GNUNET_MAX_MESSAGE_SIZE - sizeof(*obm)) + { + GNUNET_break (0); + GNUNET_MQ_impl_send_continue (mq); + return; + } + LOG (GNUNET_ERROR_TYPE_DEBUG, + "CORE requested transmission of message of type %u to neighbour `%s'.\n", + ntohs (msg->type), + GNUNET_i2s (&n->id)); + + GNUNET_assert (NULL == n->env); + n->env = + GNUNET_MQ_msg_nested_mh (obm, GNUNET_MESSAGE_TYPE_TRANSPORT_SEND, msg); + n->env_size = ntohs (msg->size); + { + struct GNUNET_MQ_Envelope *env; + + env = GNUNET_MQ_get_current_envelope (mq); + obm->priority = htonl ((uint32_t) GNUNET_MQ_env_get_options (env)); + } + obm->peer = n->id; + if (0 == n->ready_window) + { + LOG (GNUNET_ERROR_TYPE_DEBUG, + "Flow control delays transmission to CORE until we see SEND_OK.\n"); + return; /* can't send yet, need to wait for SEND_OK */ + } + do_send (n); +} + + +/** + * Handle destruction of a message queue. Implementations must not + * free @a mq, but should take care of @a impl_state. + * + * @param mq the message queue to destroy + * @param impl_state state of the implementation + */ +static void +mq_destroy_impl (struct GNUNET_MQ_Handle *mq, void *impl_state) +{ + struct Neighbour *n = impl_state; + + GNUNET_assert (mq == n->mq); + n->mq = NULL; +} + + +/** + * Implementation function that cancels the currently sent message. + * Should basically undo whatever #mq_send_impl() did. + * + * @param mq message queue + * @param impl_state state specific to the implementation + */ +static void +mq_cancel_impl (struct GNUNET_MQ_Handle *mq, void *impl_state) +{ + struct Neighbour *n = impl_state; + + n->ready_window++; + if (GNUNET_YES == n->awaiting_done) + { + GNUNET_MQ_send_cancel (n->env); + n->env = NULL; + n->awaiting_done = GNUNET_NO; + } + else + { + GNUNET_assert (0 == n->ready_window); + n->env = NULL; + } +} + + +/** + * We had an error processing a message we forwarded from a peer to + * the CORE service. We should just complain about it but otherwise + * continue processing. + * + * @param cls closure + * @param error error code + */ +static void +peer_mq_error_handler (void *cls, enum GNUNET_MQ_Error error) +{ + struct Neighbour *n = cls; + + if (GNUNET_MQ_ERROR_MALFORMED == error) + GNUNET_break_op (0); + //TODO Look into bug #7887 + + GNUNET_TRANSPORT_core_receive_continue (n->h, &n->id); +} + + +/** + * Function we use for handling incoming connect messages. + * + * @param cls closure, a `struct GNUNET_TRANSPORT_Handle *` + * @param cim message received + */ +static void +handle_connect (void *cls, const struct ConnectInfoMessage *cim) +{ + struct GNUNET_TRANSPORT_CoreHandle *h = cls; + struct Neighbour *n; + + LOG (GNUNET_ERROR_TYPE_DEBUG, + "Receiving CONNECT message for `%s'\n", + GNUNET_i2s (&cim->id)); + n = neighbour_find (h, &cim->id); + if (NULL != n) + { + GNUNET_break (0); + disconnect_and_schedule_reconnect (h); + return; + } + n = GNUNET_new (struct Neighbour); + n->id = cim->id; + n->h = h; + n->ready_window = SEND_WINDOW_SIZE; + GNUNET_assert (GNUNET_OK == + GNUNET_CONTAINER_multipeermap_put ( + h->neighbours, + &n->id, + n, + GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_ONLY)); + + n->mq = GNUNET_MQ_queue_for_callbacks (&mq_send_impl, + &mq_destroy_impl, + &mq_cancel_impl, + n, + h->handlers, + &peer_mq_error_handler, + n); + if (NULL != h->nc_cb) + { + n->handlers_cls = h->nc_cb (h->cls, &n->id, n->mq); + GNUNET_MQ_set_handlers_closure (n->mq, n->handlers_cls); + } +} + + +/** + * Function we use for handling incoming disconnect messages. + * + * @param cls closure, a `struct GNUNET_TRANSPORT_CoreHandle *` + * @param dim message received + */ +static void +handle_disconnect (void *cls, const struct DisconnectInfoMessage *dim) +{ + struct GNUNET_TRANSPORT_CoreHandle *h = cls; + struct Neighbour *n; + + GNUNET_break (ntohl (dim->reserved) == 0); + LOG (GNUNET_ERROR_TYPE_DEBUG, + "Receiving DISCONNECT message for `%s'.\n", + GNUNET_i2s (&dim->peer)); + n = neighbour_find (h, &dim->peer); + if (NULL == n) + { + GNUNET_break (0); + disconnect_and_schedule_reconnect (h); + return; + } + GNUNET_assert (GNUNET_YES == neighbour_delete (h, &dim->peer, n)); +} + + +/** + * Function we use for handling incoming send-ok messages. + * + * @param cls closure, a `struct GNUNET_TRANSPORT_CoreHandle *` + * @param okm message received + */ +static void +handle_send_ok (void *cls, const struct SendOkMessage *okm) +{ + struct GNUNET_TRANSPORT_CoreHandle *h = cls; + struct Neighbour *n; + + LOG (GNUNET_ERROR_TYPE_DEBUG, + "Receiving SEND_OK message for transmission to %s\n", + GNUNET_i2s (&okm->peer)); + + n = neighbour_find (h, &okm->peer); + + if (NULL == n) + { + /* We should never get a 'SEND_OK' for a peer that we are not + connected to */ + GNUNET_break (0); + disconnect_and_schedule_reconnect (h); + return; + } + + if ((GNUNET_NO == n->awaiting_done) && + (NULL != n->env) && + (0 == n->ready_window)) + { + n->ready_window++; + do_send (n); + return; + } + else if ((GNUNET_NO == n->awaiting_done) && + (0 == n->ready_window)) + { + n->ready_window++; + GNUNET_MQ_impl_send_continue (n->mq); + return; + } + n->ready_window++; +} + + +/** + * Function we use for checking incoming "inbound" messages. + * + * @param cls closure, a `struct GNUNET_TRANSPORT_CoreHandle *` + * @param im message received + */ +static int +check_recv (void *cls, const struct InboundMessage *im) +{ + const struct GNUNET_MessageHeader *imm; + uint16_t size; + + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "check_recv\n"); + size = ntohs (im->header.size) - sizeof(*im); + if (size < sizeof(struct GNUNET_MessageHeader)) + { + GNUNET_break (0); + return GNUNET_SYSERR; + } + imm = (const struct GNUNET_MessageHeader *) &im[1]; + if (ntohs (imm->size) != size) + { + GNUNET_break (0); + return GNUNET_SYSERR; + } + return GNUNET_OK; +} + + +/** + * Function we use for handling incoming messages. + * + * @param cls closure, a `struct GNUNET_TRANSPORT_CoreHandle *` + * @param im message received + */ +static void +handle_recv (void *cls, const struct InboundMessage *im) +{ + struct GNUNET_TRANSPORT_CoreHandle *h = cls; + const struct GNUNET_MessageHeader *imm = + (const struct GNUNET_MessageHeader *) &im[1]; + struct Neighbour *n; + + LOG (GNUNET_ERROR_TYPE_DEBUG, + "Received message of type %u with %u bytes from `%s'.\n", + (unsigned int) ntohs (imm->type), + (unsigned int) ntohs (imm->size), + GNUNET_i2s (&im->peer)); + n = neighbour_find (h, &im->peer); + if (NULL == n) + { + GNUNET_break (0); + disconnect_and_schedule_reconnect (h); + return; + } + GNUNET_MQ_inject_message (n->mq, imm); +} + + +/** + * Try again to connect to transport service. + * + * @param cls the handle to the transport service + */ +static void +reconnect (void *cls) +{ + struct GNUNET_TRANSPORT_CoreHandle *h = cls; + struct GNUNET_MQ_MessageHandler handlers[] = + { GNUNET_MQ_hd_fixed_size (connect, + GNUNET_MESSAGE_TYPE_TRANSPORT_CONNECT, + struct ConnectInfoMessage, + h), + GNUNET_MQ_hd_fixed_size (disconnect, + GNUNET_MESSAGE_TYPE_TRANSPORT_DISCONNECT, + struct DisconnectInfoMessage, + h), + GNUNET_MQ_hd_fixed_size (send_ok, + GNUNET_MESSAGE_TYPE_TRANSPORT_SEND_OK, + struct SendOkMessage, + h), + GNUNET_MQ_hd_var_size (recv, + GNUNET_MESSAGE_TYPE_TRANSPORT_RECV, + struct InboundMessage, + h), + GNUNET_MQ_handler_end () }; + struct GNUNET_MQ_Envelope *env; + struct StartMessage *s; + uint32_t options; + + h->reconnect_task = NULL; + LOG (GNUNET_ERROR_TYPE_DEBUG, "Connecting to transport service.\n"); + GNUNET_assert (NULL == h->mq); + h->mq = + GNUNET_CLIENT_connect (h->cfg, "transport", handlers, &mq_error_handler, h); + if (NULL == h->mq) + return; + env = GNUNET_MQ_msg (s, GNUNET_MESSAGE_TYPE_TRANSPORT_START); + options = 0; + if (h->check_self) + options |= 1; + if (NULL != h->handlers) + options |= 2; + s->options = htonl (options); + s->self = h->self; + GNUNET_MQ_send (h->mq, env); +} + + +/** + * Disconnect from the transport service. + * + * @param h transport service to reconnect + */ +static void +disconnect (struct GNUNET_TRANSPORT_CoreHandle *h) +{ + GNUNET_CONTAINER_multipeermap_iterate (h->neighbours, &neighbour_delete, h); + if (NULL != h->mq) + { + GNUNET_MQ_destroy (h->mq); + h->mq = NULL; + } +} + + +/** + * Function that will schedule the job that will try + * to connect us again to the client. + * + * @param h transport service to reconnect + */ +static void +disconnect_and_schedule_reconnect (struct GNUNET_TRANSPORT_CoreHandle *h) +{ + GNUNET_assert (NULL == h->reconnect_task); + disconnect (h); + LOG (GNUNET_ERROR_TYPE_DEBUG, + "Scheduling task to reconnect to transport service in %s.\n", + GNUNET_STRINGS_relative_time_to_string (h->reconnect_delay, GNUNET_YES)); + h->reconnect_task = + GNUNET_SCHEDULER_add_delayed (h->reconnect_delay, &reconnect, h); + h->reconnect_delay = GNUNET_TIME_STD_BACKOFF (h->reconnect_delay); +} + + +/** + * Checks if a given peer is connected to us and get the message queue. + * + * @param handle connection to transport service + * @param peer the peer to check + * @return NULL if disconnected, otherwise message queue for @a peer + */ +struct GNUNET_MQ_Handle * +GNUNET_TRANSPORT_core_get_mq (struct GNUNET_TRANSPORT_CoreHandle *handle, + const struct GNUNET_PeerIdentity *peer) +{ + struct Neighbour *n; + + n = neighbour_find (handle, peer); + if (NULL == n) + return NULL; + return n->mq; +} + + +/** + * Notification from the CORE service to the TRANSPORT service + * that the CORE service has finished processing a message from + * TRANSPORT (via the @code{handlers} of #GNUNET_TRANSPORT_core_connect()) + * and that it is thus now OK for TRANSPORT to send more messages + * for @a pid. + * + * Used to provide flow control, this is our equivalent to + * #GNUNET_SERVICE_client_continue() of an ordinary service. + * + * Note that due to the use of a window, TRANSPORT may send multiple + * messages destined for the same peer even without an intermediate + * call to this function. However, CORE must still call this function + * once per message received, as otherwise eventually the window will + * be full and TRANSPORT will stop providing messages to CORE for @a + * pid. + * + * @param ch core handle + * @param pid which peer was the message from that was fully processed by CORE + */ +void +GNUNET_TRANSPORT_core_receive_continue (struct GNUNET_TRANSPORT_CoreHandle *ch, + const struct GNUNET_PeerIdentity *pid) +{ + struct GNUNET_MQ_Envelope *env; + struct RecvOkMessage *rok; + + LOG (GNUNET_ERROR_TYPE_DEBUG, + "Message for %s finished CORE processing, sending RECV_OK.\n", + GNUNET_i2s (pid)); + if (NULL == ch->mq) + return; + env = GNUNET_MQ_msg (rok, GNUNET_MESSAGE_TYPE_TRANSPORT_RECV_OK); + rok->increase_window_delta = htonl (1); + rok->peer = *pid; + GNUNET_MQ_send (ch->mq, env); +} + + +/** + * Connect to the transport service. Note that the connection may + * complete (or fail) asynchronously. + * + * @param cfg configuration to use + * @param self our own identity (API should check that it matches + * the identity found by transport), or NULL (no check) + * @param cls closure for the callbacks + * @param rec receive function to call + * @param nc function to call on connect events + * @param nd function to call on disconnect events + * @return NULL on error + */ +struct GNUNET_TRANSPORT_CoreHandle * +GNUNET_TRANSPORT_core_connect (const struct GNUNET_CONFIGURATION_Handle *cfg, + const struct GNUNET_PeerIdentity *self, + const struct GNUNET_MQ_MessageHandler *handlers, + void *cls, + GNUNET_TRANSPORT_NotifyConnect nc, + GNUNET_TRANSPORT_NotifyDisconnect nd) +{ + struct GNUNET_TRANSPORT_CoreHandle *h; + unsigned int i; + + h = GNUNET_new (struct GNUNET_TRANSPORT_CoreHandle); + if (NULL != self) + { + h->self = *self; + h->check_self = GNUNET_YES; + } + h->cfg = cfg; + h->cls = cls; + h->nc_cb = nc; + h->nd_cb = nd; + h->reconnect_delay = GNUNET_TIME_UNIT_ZERO; + if (NULL != handlers) + { + for (i = 0; NULL != handlers[i].cb; i++) + ; + h->handlers = GNUNET_new_array (i + 1, struct GNUNET_MQ_MessageHandler); + GNUNET_memcpy (h->handlers, + handlers, + i * sizeof(struct GNUNET_MQ_MessageHandler)); + } + LOG (GNUNET_ERROR_TYPE_DEBUG, "Connecting to transport service\n"); + reconnect (h); + if (NULL == h->mq) + { + GNUNET_free (h->handlers); + GNUNET_free (h); + return NULL; + } + h->neighbours = + GNUNET_CONTAINER_multipeermap_create (STARTING_NEIGHBOURS_SIZE, GNUNET_YES); + return h; +} + + +/** + * Disconnect from the transport service. + * + * @param handle handle to the service as returned from + * #GNUNET_TRANSPORT_core_connect() + */ +void +GNUNET_TRANSPORT_core_disconnect (struct GNUNET_TRANSPORT_CoreHandle *handle) +{ + LOG (GNUNET_ERROR_TYPE_DEBUG, "Transport disconnect called!\n"); + /* this disconnects all neighbours... */ + disconnect (handle); + /* and now we stop trying to connect again... */ + if (NULL != handle->reconnect_task) + { + GNUNET_SCHEDULER_cancel (handle->reconnect_task); + handle->reconnect_task = NULL; + } + GNUNET_CONTAINER_multipeermap_destroy (handle->neighbours); + handle->neighbours = NULL; + GNUNET_free (handle->handlers); + handle->handlers = NULL; + GNUNET_free (handle); +} + + +/* end of transport_api_core.c */ diff --git a/src/service/transport/transport_api2_monitor.c b/src/service/transport/transport_api2_monitor.c new file mode 100644 index 000000000..67aa1985e --- /dev/null +++ b/src/service/transport/transport_api2_monitor.c @@ -0,0 +1,292 @@ +/* + This file is part of GNUnet. + Copyright (C) 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 . + + SPDX-License-Identifier: AGPL3.0-or-later + */ + +/** + * @file transport/transport_api2_monitor.c + * @brief implementation of the gnunet_transport_monitor_service.h API + * @author Christian Grothoff + */ +#include "platform.h" +#include "gnunet_util_lib.h" +#include "gnunet_protocols.h" +#include "gnunet_transport_monitor_service.h" +#include "transport.h" + + +/** + * Opaque handle to the transport service for monitors. + */ +struct GNUNET_TRANSPORT_MonitorContext +{ + /** + * Our configuration. + */ + const struct GNUNET_CONFIGURATION_Handle *cfg; + + /** + * Queue to talk to the transport service. + */ + struct GNUNET_MQ_Handle *mq; + + /** + * Peer we monitor, all zeros for "all" + */ + struct GNUNET_PeerIdentity peer; + + /** + * #GNUNET_YES to return the current state and then end. + */ + int one_shot; + + /** + * Function to call with monitor data. + */ + GNUNET_TRANSPORT_MonitorCallback cb; + + /** + * Closure for @e cb. + */ + void *cb_cls; +}; + + +/** + * (re)connect our monitor to the transport service + * + * @param mc handle to reconnect + */ +static void +reconnect (struct GNUNET_TRANSPORT_MonitorContext *mc); + + +/** + * Send message to the transport service about our montoring + * desire. + * + * @param ai address to delete + */ +static void +send_start_monitor (struct GNUNET_TRANSPORT_MonitorContext *mc) +{ + struct GNUNET_MQ_Envelope *env; + struct GNUNET_TRANSPORT_MonitorStart *smm; + + if (NULL == mc->mq) + return; + env = GNUNET_MQ_msg (smm, GNUNET_MESSAGE_TYPE_TRANSPORT_MONITOR_START); + smm->one_shot = htonl ((uint32_t) mc->one_shot); + smm->peer = mc->peer; + GNUNET_MQ_send (mc->mq, env); +} + + +/** + * Disconnect from the transport service. + * + * @param mc service to disconnect from + */ +static void +disconnect (struct GNUNET_TRANSPORT_MonitorContext *mc) +{ + if (NULL == mc->mq) + return; + GNUNET_MQ_destroy (mc->mq); + mc->mq = NULL; +} + + +/** + * Function called on MQ errors. Reconnects to the service. + * + * @param cls our `struct GNUNET_TRANSPORT_MonitorContext *` + * @param error what error happened? + */ +static void +error_handler (void *cls, enum GNUNET_MQ_Error error) +{ + struct GNUNET_TRANSPORT_MonitorContext *mc = cls; + + GNUNET_log (GNUNET_ERROR_TYPE_INFO, + "MQ failure %d, reconnecting to transport service.\n", + error); + disconnect (mc); + /* TODO: maybe do this with exponential backoff/delay */ + reconnect (mc); +} + + +/** + * Transport service sends us information about what is going on. + * Check if @a md is well-formed. + * + * @param cls our `struct GNUNET_TRANSPORT_MonitorContext *` + * @param md the monitor data we got + * @return #GNUNET_OK if @a smt is well-formed + */ +static int +check_monitor_data (void *cls, const struct GNUNET_TRANSPORT_MonitorData *md) +{ + (void) cls; + GNUNET_MQ_check_zero_termination (md); + return GNUNET_OK; +} + + +/** + * Transport service sends us information about what is going on. + * + * @param cls our `struct GNUNET_TRANSPORT_MonitorContext *` + * @param md monitor data + */ +static void +handle_monitor_data (void *cls, const struct GNUNET_TRANSPORT_MonitorData *md) +{ + struct GNUNET_TRANSPORT_MonitorContext *mc = cls; + struct GNUNET_TRANSPORT_MonitorInformation mi; + + mi.address = (const char *) &md[1]; + mi.nt = (enum GNUNET_NetworkType) ntohl (md->nt); + mi.cs = (enum GNUNET_TRANSPORT_ConnectionStatus) ntohl (md->cs); + mi.num_msg_pending = ntohl (md->num_msg_pending); + mi.num_bytes_pending = ntohl (md->num_bytes_pending); + mi.last_validation = GNUNET_TIME_absolute_ntoh (md->last_validation); + mi.valid_until = GNUNET_TIME_absolute_ntoh (md->valid_until); + mi.next_validation = GNUNET_TIME_absolute_ntoh (md->next_validation); + mi.rtt = GNUNET_TIME_relative_ntoh (md->rtt); + mc->cb (mc->cb_cls, &md->peer, &mi); +} + + +/** + * One shot was requested, and transport service is done. + * + * @param cls our `struct GNUNET_TRANSPORT_MonitorContext *` + * @param me end message + */ +static void +handle_monitor_end (void *cls, const struct GNUNET_MessageHeader *me) +{ + struct GNUNET_TRANSPORT_MonitorContext *mc = cls; + + if (GNUNET_YES != mc->one_shot) + { + GNUNET_break (0); + disconnect (mc); + reconnect (mc); + return; + } + mc->cb (mc->cb_cls, NULL, NULL); + GNUNET_TRANSPORT_monitor_cancel (mc); +} + + +/** + * (re)connect our monitor to the transport service + * + * @param mc handle to reconnect + */ +static void +reconnect (struct GNUNET_TRANSPORT_MonitorContext *mc) +{ + struct GNUNET_MQ_MessageHandler handlers[] = + { GNUNET_MQ_hd_var_size (monitor_data, + GNUNET_MESSAGE_TYPE_TRANSPORT_MONITOR_DATA, + struct GNUNET_TRANSPORT_MonitorData, + mc), + GNUNET_MQ_hd_fixed_size (monitor_end, + GNUNET_MESSAGE_TYPE_TRANSPORT_MONITOR_END, + struct GNUNET_MessageHeader, + mc), + GNUNET_MQ_handler_end () }; + + mc->mq = + GNUNET_CLIENT_connect (mc->cfg, "transport", handlers, &error_handler, mc); + if (NULL == mc->mq) + return; + send_start_monitor (mc); +} + + +/** + * Return information about a specific peer or all peers currently known to + * transport service once or in monitoring mode. To obtain information about + * a specific peer, a peer identity can be passed. To obtain information about + * all peers currently known to transport service, NULL can be passed as peer + * identity. + * + * For each peer, the callback is called with information about the address used + * to communicate with this peer, the state this peer is currently in and the + * the current timeout for this state. + * + * Upon completion, the #GNUNET_TRANSPORT_PeerIterateCallback is called one + * more time with `NULL`. After this, the operation must no longer be + * explicitly canceled. + * + * The #GNUNET_TRANSPORT_monitor_peers_cancel call MUST not be called in the + * the peer_callback! + * + * @param cfg configuration to use + * @param peer a specific peer identity to obtain information for, + * NULL for all peers + * @param one_shot #GNUNET_YES to return the current state and then end (with NULL+NULL), + * #GNUNET_NO to monitor peers continuously + * @param cb function to call with the results + * @param cb_cls closure for @a mc + */ +struct GNUNET_TRANSPORT_MonitorContext * +GNUNET_TRANSPORT_monitor (const struct GNUNET_CONFIGURATION_Handle *cfg, + const struct GNUNET_PeerIdentity *peer, + int one_shot, + GNUNET_TRANSPORT_MonitorCallback cb, + void *cb_cls) +{ + struct GNUNET_TRANSPORT_MonitorContext *mc; + + mc = GNUNET_new (struct GNUNET_TRANSPORT_MonitorContext); + mc->cfg = cfg; + if (NULL != peer) + mc->peer = *peer; + mc->one_shot = one_shot; + mc->cb = cb; + mc->cb_cls = cb_cls; + reconnect (mc); + if (NULL == mc->mq) + { + GNUNET_free (mc); + return NULL; + } + return mc; +} + + +/** + * Cancel request to monitor peers + * + * @param pmc handle for the request to cancel + */ +void +GNUNET_TRANSPORT_monitor_cancel (struct GNUNET_TRANSPORT_MonitorContext *mc) +{ + disconnect (mc); + GNUNET_free (mc); +} + + +/* end of transport_api2_monitor.c */ diff --git a/src/service/transport/transport_api_cmd_backchannel_check.c b/src/service/transport/transport_api_cmd_backchannel_check.c new file mode 100644 index 000000000..68bdae69c --- /dev/null +++ b/src/service/transport/transport_api_cmd_backchannel_check.c @@ -0,0 +1,554 @@ +/* + This file is part of GNUnet + Copyright (C) 2021 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 . + + SPDX-License-Identifier: AGPL3.0-or-later + */ + +/** + * @file testing_api_cmd_backchannel_check.c + * @brief cmd to start a peer. + * @author t3sserakt + */ +#include "platform.h" +#include "gnunet_common.h" +#include "gnunet_util_lib.h" +#include "gnunet_testing_ng_lib.h" +#include "gnunet_testing_netjail_lib.h" +#include "gnunet_transport_application_service.h" +#include "gnunet_hello_lib.h" +#include "transport-testing-cmds.h" + +/** + * Generic logging shortcut + */ +#define LOG(kind, ...) GNUNET_log_from (kind, "udp-backchannel",__VA_ARGS__) + +#define UDP "udp" + +/** + * Maximum length allowed for line input. + */ +#define MAX_LINE_LENGTH 1024 + +/** + * Struct to store information needed in callbacks. + * + */ +struct CheckState +{ + /** + * Context for our asynchronous completion. + */ + struct GNUNET_TESTING_AsyncContext ac; + + /** + * The number of the node in a network namespace. + */ + unsigned int node_n; + + /** + * The number of the network namespace. + */ + unsigned int namespace_n; + + /** + * The testing system of this node. + */ + const struct GNUNET_TESTING_System *tl_system; + + // Label of the cmd which started the test system. + const char *create_label; + + /** + * Number globally identifying the node. + * + */ + uint32_t num; + + /** + * Label of the cmd to start a peer. + * + */ + const char *start_peer_label; + + /** + * The topology of the test setup. + */ + struct GNUNET_TESTING_NetjailTopology *topology; + + /** + * Connections to other peers. + */ + struct GNUNET_TESTING_NodeConnection *node_connections_head; + + /** + * Number of connections. + */ + unsigned int con_num; + + /** + * Number of received backchannel messages. + */ + unsigned int received_backchannel_msgs; + + /** + * Array with search strings. + */ + char **search_string; + + /** + * File handle for log file. + */ + struct GNUNET_DISK_FileHandle *fh; + + /** + * Task which handles the reading + */ + struct GNUNET_SCHEDULER_Task *task; + + /** + * Stream to read log file lines. + */ + FILE *stream; +}; + +/** + * + * @param cls The cmd state CheckState. + */ +static void +read_from_log (void *cls) +{ + struct CheckState *cs = cls; + char line[MAX_LINE_LENGTH + 1]; + char *search_string; + + + LOG (GNUNET_ERROR_TYPE_DEBUG, + "read_from_log\n"); + + cs->fh = GNUNET_DISK_file_open ("test.out", + GNUNET_DISK_OPEN_READ, + GNUNET_DISK_PERM_USER_READ); + + cs->task = NULL; + + /* read message from line and handle it */ + cs->stream = fdopen (cs->fh->fd, "r"); + memset (line, 0, MAX_LINE_LENGTH + 1); + + // fgets (line, MAX_LINE_LENGTH, cs->stream); + // while (NULL != line && 0 != strcmp (line, ""))// '\0' != line[0]) + while (NULL != fgets (line, MAX_LINE_LENGTH, cs->stream)) + { + /*LOG (GNUNET_ERROR_TYPE_DEBUG, + "cs->received_backchannel_msgs: %u\n", + cs->received_backchannel_msgs);*/ + /*if (NULL == strstr (line, "line")) + LOG (GNUNET_ERROR_TYPE_DEBUG, + "line: %s", + line);*/ + + + for (int i = 0; i < cs->con_num; i++) + { + search_string = cs->search_string[i]; + /*LOG (GNUNET_ERROR_TYPE_DEBUG, + "search %u %u: %s %p\n", + i, + cs->con_num, + cs->search_string[i], + cs->search_string); + fprintf (stderr, + line);*/ + if (NULL != strstr (line, + search_string)) + // "Delivering backchannel message from 4TTC to F7B5 of type 1460 to udp")) + // cs->search_string[i])) + { + cs->received_backchannel_msgs++; + LOG (GNUNET_ERROR_TYPE_DEBUG, + "received_backchannel_msgs %u con_num %u\n", + cs->received_backchannel_msgs, + cs->con_num); + if (cs->received_backchannel_msgs == cs->con_num) + { + LOG (GNUNET_ERROR_TYPE_DEBUG, + "search finished %lu %lu %u\n", + strlen (cs->search_string[i]), + strlen ( + "Delivering backchannel message from 4TTC to F7B5 of type 1460 to udp"), + strcmp ( + "Delivering backchannel message from 4TTC to F7B5 of type 1460 to udp", + cs->search_string[i])); + GNUNET_TESTING_async_finish (&cs->ac); + fclose (cs->stream); + return; + } + } + } + } + LOG (GNUNET_ERROR_TYPE_DEBUG, + "read_from_log end\n"); + fclose (cs->stream); + GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_UNIT_MINUTES, + &read_from_log, + cs); + /*if (NULL == fgets (line, MAX_LINE_LENGTH, cs->stream)) + { + LOG (GNUNET_ERROR_TYPE_DEBUG, + "read null\n"); + GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_UNIT_SECONDS, + &read_from_log, + cs); + return; + }*/ + /*else { + cs->task = + GNUNET_SCHEDULER_add_read_file (GNUNET_TIME_UNIT_FOREVER_REL, + cs->fh, + &read_from_log, + cs); + + + }*/ +} + + +static enum GNUNET_GenericReturnValue +will_the_other_node_connect_via_udp ( + struct CheckState *cs, + const struct GNUNET_TESTING_NetjailNode *node) +// struct GNUNET_TESTING_NodeConnection *connection) +{ + // struct GNUNET_TESTING_NetjailTopology *topology = cs->topology; + // unsigned int node_n = connection->node_n; + // unsigned int namespace_n = connection->namespace_n; + // struct GNUNET_HashCode hc; + // struct GNUNET_ShortHashCode *key = GNUNET_new (struct GNUNET_ShortHashCode); + // struct GNUNET_HashCode hc_namespace; + /*struct GNUNET_ShortHashCode *key_namespace = GNUNET_new (struct + GNUNET_ShortHashCode);*/ + // struct GNUNET_TESTING_NetjailNode *node; + struct GNUNET_TESTING_NodeConnection *pos_connection; + struct GNUNET_TESTING_AddressPrefix *pos_prefix; + // struct GNUNET_TESTING_NetjailNamespace *namespace; + // struct GNUNET_CONTAINER_MultiShortmap *map; + + /* if (0 == connection->namespace_n) */ + /* { */ + /* map = topology->map_globals; */ + /* } */ + /* else */ + /* { */ + /* GNUNET_CRYPTO_hash (&namespace_n, sizeof(namespace_n), &hc_namespace); */ + /* memcpy (key_namespace, */ + /* &hc_namespace, */ + /* sizeof (*key_namespace)); */ + /* if (GNUNET_YES == GNUNET_CONTAINER_multishortmap_contains ( */ + /* topology->map_namespaces, */ + /* key_namespace)) */ + /* { */ + /* namespace = GNUNET_CONTAINER_multishortmap_get (topology->map_namespaces, */ + /* key_namespace); */ + /* map = namespace->nodes; */ + /* } */ + /* else */ + /* GNUNET_assert (0); */ + /* } */ + + /* GNUNET_CRYPTO_hash (&node_n, sizeof(node_n), &hc); */ + /* memcpy (key, */ + /* &hc, */ + /* sizeof (*key)); */ + /* if (GNUNET_YES == GNUNET_CONTAINER_multishortmap_contains ( */ + /* map, */ + /* key)) */ + /* { */ + /* node = GNUNET_CONTAINER_multishortmap_get (cs->topology->map_globals, */ + /* key); */ + /* for (pos_connection = node->node_connections_head; NULL != pos_connection; */ + /* pos_connection = pos_connection->next) */ + /* { */ + /* if ((node->namespace_n == pos_connection->namespace_n) && */ + /* (node->node_n == pos_connection->node_n) ) */ + /* { */ + /* for (pos_prefix = pos_connection->address_prefixes_head; NULL != */ + /* pos_prefix; */ + /* pos_prefix = */ + /* pos_prefix->next) */ + /* { */ + /* if (0 == strcmp (UDP, pos_prefix->address_prefix)) */ + /* { */ + /* return GNUNET_YES; */ + /* } */ + /* } */ + /* } */ + /* } */ + /* } */ + + for (pos_connection = node->node_connections_head; NULL != pos_connection; + pos_connection = pos_connection->next) + { + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "connect via udp %u %u %u %u\n", + node->namespace_n, + cs->namespace_n, + node->node_n, + cs->node_n); + if ((pos_connection->namespace_n == cs->namespace_n) && + (pos_connection->node_n == cs->node_n) ) + { + for (pos_prefix = pos_connection->address_prefixes_head; NULL != + pos_prefix; + pos_prefix = + pos_prefix->next) + { + if (0 == strcmp (UDP, pos_prefix->address_prefix)) + { + return GNUNET_YES; + } + } + } + } + + return GNUNET_NO; +} + + +static void +add_search_string (struct CheckState *cs, const struct + GNUNET_TESTING_NetjailNode *node) +{ + unsigned int num; + struct GNUNET_PeerIdentity *peer; + struct GNUNET_PeerIdentity *us; + char *buf; + char *part_one = "Delivering backchannel message from "; + char *part_two = " to "; + char *part_three = " of type 1460 to udp"; + char *peer_id; + char *us_id; + + if (0 == node->namespace_n) + num = node->node_n; + else + num = (node->namespace_n - 1) * cs->topology->nodes_m + node->node_n + + cs->topology->nodes_x; + + // num = GNUNET_TESTING_calculate_num (pos_connection, cs->topology); + peer = GNUNET_TESTING_get_peer (num, cs->tl_system); + LOG (GNUNET_ERROR_TYPE_DEBUG, + "peer: %s num %u\n", + GNUNET_i2s (peer), + num); + us = GNUNET_TESTING_get_peer (cs->num, cs->tl_system); + LOG (GNUNET_ERROR_TYPE_DEBUG, + "us: %s cs->num %d\n", + GNUNET_i2s (us), + cs->num); + + GNUNET_asprintf (&peer_id, + "%s", + GNUNET_i2s (peer)); + GNUNET_asprintf (&us_id, + "%s", + GNUNET_i2s (us)); + + if (0 < GNUNET_asprintf (&buf, + "%s%s%s%s%s", + part_one, + us_id, + part_two, + peer_id, + part_three)) + { + GNUNET_array_append (cs->search_string, + cs->con_num, + buf); + /*LOG (GNUNET_ERROR_TYPE_DEBUG, + "con_num: %u search: %s %p\n", + cs->con_num, + cs->search_string[cs->con_num - 1], + cs->search_string);*/ + } + else + GNUNET_assert (0); + GNUNET_free (peer); + GNUNET_free (us); +} + + +/** + * The run method of this cmd will connect to peers. + * + */ +static void +backchannel_check_run (void *cls, + struct GNUNET_TESTING_Interpreter *is) +{ + struct CheckState *cs = cls; + const struct GNUNET_TESTING_Command *system_cmd; + const struct GNUNET_TESTING_System *tl_system; + const struct GNUNET_TESTING_Command *peer1_cmd; + const struct GNUNET_TRANSPORT_ApplicationHandle *ah; + struct GNUNET_CONTAINER_MultiShortmapIterator *node_it; + struct GNUNET_CONTAINER_MultiShortmapIterator *namespace_it; + struct GNUNET_ShortHashCode node_key; + struct GNUNET_ShortHashCode namespace_key; + const struct GNUNET_TESTING_NetjailNode *node; + const struct GNUNET_TESTING_NetjailNamespace *namespace; + + LOG (GNUNET_ERROR_TYPE_DEBUG, + "check run 1\n"); + + peer1_cmd = GNUNET_TESTING_interpreter_lookup_command (is, + cs->start_peer_label); + GNUNET_TRANSPORT_get_trait_application_handle (peer1_cmd, + &ah); + + system_cmd = GNUNET_TESTING_interpreter_lookup_command (is, + cs->create_label); + GNUNET_TESTING_get_trait_test_system (system_cmd, + &tl_system); + + cs->tl_system = tl_system; + + cs->node_connections_head = GNUNET_TESTING_get_connections (cs->num, + cs->topology); + + LOG (GNUNET_ERROR_TYPE_DEBUG, + "check run 2\n"); + + + node_it = GNUNET_CONTAINER_multishortmap_iterator_create ( + cs->topology->map_globals); + + while (GNUNET_YES == GNUNET_CONTAINER_multishortmap_iterator_next (node_it, + &node_key, + (const + void**) & + node)) + { + LOG (GNUNET_ERROR_TYPE_DEBUG, + "namespace_n %u node_n %u\n", + node->namespace_n, + node->node_n); + if (GNUNET_YES == will_the_other_node_connect_via_udp (cs, node)) + { + add_search_string (cs, node); + } + } + GNUNET_free (node_it); + namespace_it = GNUNET_CONTAINER_multishortmap_iterator_create ( + cs->topology->map_namespaces); + while (GNUNET_YES == GNUNET_CONTAINER_multishortmap_iterator_next ( + namespace_it, + &namespace_key, + (const + void**) &namespace)) + { + LOG (GNUNET_ERROR_TYPE_DEBUG, + "namespace_n %u\n", + node->namespace_n); + node_it = GNUNET_CONTAINER_multishortmap_iterator_create ( + namespace->nodes); + while (GNUNET_YES == GNUNET_CONTAINER_multishortmap_iterator_next (node_it, + &node_key, + (const + void**) + &node)) + { + LOG (GNUNET_ERROR_TYPE_DEBUG, + "namespace_n %u node_n %u\n", + node->namespace_n, + node->node_n); + if (GNUNET_YES == will_the_other_node_connect_via_udp (cs, node)) + { + add_search_string (cs, node); + } + } + GNUNET_free (node_it); + } + + if (0 != cs->con_num) + { + cs->task = + GNUNET_SCHEDULER_add_now (&read_from_log, + cs); + } + else + GNUNET_TESTING_async_finish (&cs->ac); + + GNUNET_free (namespace_it); +} + + +/** + * Trait function of this cmd does nothing. + * + */ +static int +backchannel_check_traits (void *cls, + const void **ret, + const char *trait, + unsigned int index) +{ + return GNUNET_OK; +} + + +/** + * The cleanup function of this cmd frees resources the cmd allocated. + * + */ +static void +backchannel_check_cleanup (void *cls) +{ + struct ConnectPeersState *cs = cls; + + GNUNET_free (cs); +} + + +struct GNUNET_TESTING_Command +GNUNET_TRANSPORT_cmd_backchannel_check (const char *label, + const char *start_peer_label, + const char *create_label, + uint32_t num, + unsigned int node_n, + unsigned int namespace_n, + struct GNUNET_TESTING_NetjailTopology * + topology) +{ + struct CheckState *cs; + + cs = GNUNET_new (struct CheckState); + cs->start_peer_label = start_peer_label; + cs->num = num; + cs->create_label = create_label; + cs->topology = topology; + cs->node_n = node_n; + cs->namespace_n = namespace_n; + + return GNUNET_TESTING_command_new (cs, + label, + &backchannel_check_run, + &backchannel_check_cleanup, + &backchannel_check_traits, + &cs->ac); +} diff --git a/src/service/transport/transport_api_cmd_connecting_peers.c b/src/service/transport/transport_api_cmd_connecting_peers.c new file mode 100644 index 000000000..c59c4b006 --- /dev/null +++ b/src/service/transport/transport_api_cmd_connecting_peers.c @@ -0,0 +1,311 @@ +/* + This file is part of GNUnet + Copyright (C) 2021 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 . + + SPDX-License-Identifier: AGPL3.0-or-later + */ + +/** + * @file testing_api_cmd_start_peer.c + * @brief cmd to start a peer. + * @author t3sserakt + */ +#include "platform.h" +#include "gnunet_util_lib.h" +#include "gnunet_testing_ng_lib.h" +#include "gnunet_testing_netjail_lib.h" +#include "gnunet_transport_application_service.h" +#include "gnunet_hello_lib.h" +#include "transport-testing-cmds.h" + +/** + * Generic logging shortcut + */ +#define LOG(kind, ...) GNUNET_log (kind, __VA_ARGS__) + +/** + * The run method of this cmd will connect to peers. + * + */ +static void +connect_peers_run (void *cls, + struct GNUNET_TESTING_Interpreter *is) +{ + struct ConnectPeersState *cps = cls; + const struct GNUNET_TESTING_Command *system_cmd; + const struct GNUNET_TESTING_System *tl_system; + + + const struct GNUNET_TESTING_Command *peer1_cmd; + const struct GNUNET_TRANSPORT_ApplicationHandle *ah; + struct GNUNET_PeerIdentity *peer; + char *addr; + char *addr_and_port; + enum GNUNET_NetworkType nt = 0; + uint32_t num; + struct GNUNET_TESTING_NodeConnection *pos_connection; + struct GNUNET_TESTING_AddressPrefix *pos_prefix; + unsigned int con_num = 0; + const enum GNUNET_GenericReturnValue *broadcast; + + cps->is = is; + peer1_cmd = GNUNET_TESTING_interpreter_lookup_command (is, + cps->start_peer_label); + if (GNUNET_YES == cps->wait_for_connect) + { + LOG (GNUNET_ERROR_TYPE_DEBUG, + "Wait for connect.\n"); + GNUNET_TRANSPORT_get_trait_application_handle (peer1_cmd, + &ah); + } + else + { + LOG (GNUNET_ERROR_TYPE_DEBUG, + "Not waiting for connect.\n"); + GNUNET_TESTING_get_trait_application_handle (peer1_cmd, + &ah); + } + + GNUNET_TRANSPORT_get_trait_broadcast (peer1_cmd, + &broadcast); + + system_cmd = GNUNET_TESTING_interpreter_lookup_command (is, + cps->create_label); + GNUNET_TESTING_get_trait_test_system (system_cmd, + &tl_system); + + cps->tl_system = tl_system; + + LOG (GNUNET_ERROR_TYPE_DEBUG, + "cps->num: %u \n", + cps->num); + + cps->node_connections_head = GNUNET_TESTING_get_connections (cps->num, + cps->topology); + + for (pos_connection = cps->node_connections_head; NULL != pos_connection; + pos_connection = pos_connection->next) + { + con_num++; + num = GNUNET_TESTING_calculate_num (pos_connection, cps->topology); + for (pos_prefix = pos_connection->address_prefixes_head; NULL != pos_prefix; + pos_prefix = + pos_prefix->next) + { + addr = GNUNET_TESTING_get_address (pos_connection, + pos_prefix->address_prefix); + if (NULL != addr) + { + char *natted_p = strstr (pos_prefix->address_prefix, "_"); + + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "0 validating peer number %s %s %s\n", + natted_p, + pos_prefix->address_prefix, + addr); + if (0 == GNUNET_memcmp (pos_prefix->address_prefix, "udp")) + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "validating memcmp\n"); + if (GNUNET_YES == *broadcast) + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "validating broadcast\n"); + if ((0 == GNUNET_memcmp (pos_prefix->address_prefix, "udp")) && + (GNUNET_YES == *broadcast) ) + GNUNET_asprintf (&addr_and_port, + "%s:2086", + addr); + else if (NULL == natted_p) + GNUNET_asprintf (&addr_and_port, + "%s:60002", + addr); + else if (NULL != natted_p) + { + char *prefix; + char *rest; + char *rest2; + char *address; + + prefix = strtok (addr, "_"); + rest = strtok (NULL, "_"); + rest2 = strtok (rest, "-"); + address = strtok (NULL, "-"); + + GNUNET_asprintf (&addr_and_port, + "%s-%s:0", + prefix, + address); + + } + peer = GNUNET_TESTING_get_peer (num, tl_system); + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "validating peer number %u with identity %s and address %s %u %s and handle %p\n", + num, + GNUNET_i2s (peer), + addr_and_port, + *broadcast, + pos_prefix->address_prefix, + ah); + GNUNET_TRANSPORT_application_validate ((struct + GNUNET_TRANSPORT_ApplicationHandle + *) ah, + peer, + nt, + addr_and_port); + GNUNET_free (peer); + GNUNET_free (addr); + GNUNET_free (addr_and_port); + } + } + } + cps->con_num = con_num; +} + + +/** + * Callback from start peer cmd for signaling a peer got connected. + * + */ +static void * +notify_connect (struct GNUNET_TESTING_Interpreter *is, + const struct GNUNET_PeerIdentity *peer) +{ + const struct GNUNET_TESTING_Command *cmd; + struct ConnectPeersState *cps; + struct GNUNET_PeerIdentity *peer_connection; + unsigned int num; + unsigned int con_num; + void *ret = NULL; + + cmd = GNUNET_TESTING_interpreter_lookup_command_all (is, + "connect-peers"); + cps = cmd->cls; // WTF? Never go directly into cls of another command! FIXME! + con_num = cps->con_num_notified; + for (struct GNUNET_TESTING_NodeConnection *pos_connection = + cps->node_connections_head; + NULL != pos_connection; + pos_connection = pos_connection->next) + { + num = GNUNET_TESTING_calculate_num (pos_connection, + cps->topology); + peer_connection = GNUNET_TESTING_get_peer (num, + cps->tl_system); + if (0 == GNUNET_memcmp (peer, + peer_connection)) + cps->con_num_notified++; + GNUNET_free (peer_connection); + } + if (cps->con_num_notified == con_num) + cps->additional_connects_notified++; + + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "con_num: %u add: %u num_notified: %u add_notified: %u peer: %s\n", + cps->con_num, + cps->additional_connects, + cps->con_num_notified, + cps->additional_connects_notified, + GNUNET_i2s (peer)); + if ((cps->con_num == cps->con_num_notified) && + (cps->additional_connects <= cps->additional_connects_notified)) + { + GNUNET_TESTING_async_finish (&cps->ac); + } + return ret; +} + + +/** + * The cleanup function of this cmd frees resources the cmd allocated. + * + */ +static void +connect_peers_cleanup (void *cls) +{ + struct ConnectPeersState *cps = cls; + + GNUNET_free (cps); +} + + +/** + * This function prepares an array with traits. + * + */ +enum GNUNET_GenericReturnValue +connect_peers_traits (void *cls, + const void **ret, + const char *trait, + unsigned int index) +{ + struct ConnectPeersState *cps = cls; + struct GNUNET_TESTING_Trait traits[] = { + GNUNET_TRANSPORT_make_trait_connect_peer_state ((const void *) cps), + GNUNET_TESTING_trait_end () + }; + return GNUNET_TESTING_get_trait (traits, + ret, + trait, + index); +} + + +struct GNUNET_TESTING_Command +GNUNET_TRANSPORT_cmd_connect_peers (const char *label, + const char *start_peer_label, + const char *create_label, + uint32_t num, + struct GNUNET_TESTING_NetjailTopology * + topology, + unsigned int additional_connects, + unsigned int wait_for_connect) +{ + struct ConnectPeersState *cps; + unsigned int node_additional_connects; + + node_additional_connects = GNUNET_TESTING_get_additional_connects (num, + topology); + + LOG (GNUNET_ERROR_TYPE_DEBUG, + "global: %u and local: %u additional_connects\n", + additional_connects, + node_additional_connects); + + if (0 != node_additional_connects) + additional_connects = node_additional_connects; + + cps = GNUNET_new (struct ConnectPeersState); + cps->start_peer_label = start_peer_label; + cps->num = num; + cps->create_label = create_label; + cps->topology = topology; + cps->notify_connect = notify_connect; + cps->additional_connects = additional_connects; + cps->wait_for_connect = wait_for_connect; + + if (GNUNET_YES == wait_for_connect) + return GNUNET_TESTING_command_new (cps, + label, + &connect_peers_run, + &connect_peers_cleanup, + &connect_peers_traits, + &cps->ac); + else + return GNUNET_TESTING_command_new (cps, + label, + &connect_peers_run, + &connect_peers_cleanup, + &connect_peers_traits, + NULL); +} diff --git a/src/service/transport/transport_api_cmd_send_simple.c b/src/service/transport/transport_api_cmd_send_simple.c new file mode 100644 index 000000000..2671727c0 --- /dev/null +++ b/src/service/transport/transport_api_cmd_send_simple.c @@ -0,0 +1,162 @@ +/* + This file is part of GNUnet + Copyright (C) 2021 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 . + + SPDX-License-Identifier: AGPL3.0-or-later + */ + +/** + * @file testing_api_cmd_start_peer.c + * @brief cmd to start a peer. + * @author t3sserakt + */ +#include "platform.h" +#include "gnunet_util_lib.h" +#include "gnunet_testing_ng_lib.h" +#include "gnunet_testing_netjail_lib.h" +#include "transport-testing2.h" +#include "transport-testing-cmds.h" + +/** + * Generic logging shortcut + */ +#define LOG(kind, ...) GNUNET_log (kind, __VA_ARGS__) + +/** + * Struct to hold information for callbacks. + * + */ +struct SendSimpleState +{ + /** + * Number globally identifying the node. + * + */ + uint32_t num; + + /** + * Label of the cmd to start a peer. + * + */ + const char *start_peer_label; + + /** + * Label of the cmd which started the test system. + * + */ + const char *create_label; + + /** + * The topology we get the connected nodes from. + */ + struct GNUNET_TESTING_NetjailTopology *topology; +}; + + +/** + * The cleanup function of this cmd frees resources the cmd allocated. + * + */ +static void +send_simple_cleanup (void *cls) +{ + struct SendSimpleState *sss = cls; + + GNUNET_free (sss); +} + + +static int +send_simple_cb (void *cls, + const struct GNUNET_ShortHashCode *key, + void *value) +{ + struct SendSimpleState *sss = cls; + struct GNUNET_MQ_Handle *mq = value; + struct GNUNET_MQ_Envelope *env; + struct GNUNET_TRANSPORT_TESTING_TestMessage *test; + + LOG (GNUNET_ERROR_TYPE_DEBUG, + "Sending simple test message with mq %p\n", + mq); + + env = GNUNET_MQ_msg_extra (test, + 1000 - sizeof(*test), + GNUNET_TRANSPORT_TESTING_SIMPLE_MTYPE); + test->num = htonl (sss->num); + memset (&test[1], + sss->num, + 1000 - sizeof(*test)); + GNUNET_MQ_send (mq, + env); + return GNUNET_OK; +} + + +/** + * The run method of this cmd will send a simple message to the connected peers. + * + */ +static void +send_simple_run (void *cls, + struct GNUNET_TESTING_Interpreter *is) +{ + struct SendSimpleState *sss = cls; + const struct GNUNET_CONTAINER_MultiShortmap *connected_peers_map; + const struct GNUNET_TESTING_Command *peer1_cmd; + const struct GNUNET_TESTING_Command *system_cmd; + const struct GNUNET_TESTING_System *tl_system; + + peer1_cmd = GNUNET_TESTING_interpreter_lookup_command (is, + sss->start_peer_label); + GNUNET_TRANSPORT_get_trait_connected_peers_map (peer1_cmd, + &connected_peers_map); + + system_cmd = GNUNET_TESTING_interpreter_lookup_command (is, + sss->create_label); + GNUNET_TESTING_get_trait_test_system (system_cmd, + &tl_system); + + GNUNET_CONTAINER_multishortmap_iterate ( + (struct GNUNET_CONTAINER_MultiShortmap *) + connected_peers_map, send_simple_cb, + sss); +} + + +struct GNUNET_TESTING_Command +GNUNET_TRANSPORT_cmd_send_simple (const char *label, + const char *start_peer_label, + const char *create_label, + uint32_t num, + struct GNUNET_TESTING_NetjailTopology * + topology) +{ + struct SendSimpleState *sss; + + sss = GNUNET_new (struct SendSimpleState); + sss->num = num; + sss->start_peer_label = start_peer_label; + sss->create_label = create_label; + sss->topology = topology; + + return GNUNET_TESTING_command_new (sss, + label, + &send_simple_run, + &send_simple_cleanup, + NULL, + NULL); +} diff --git a/src/service/transport/transport_api_cmd_send_simple_performance.c b/src/service/transport/transport_api_cmd_send_simple_performance.c new file mode 100644 index 000000000..7ce3b8bf7 --- /dev/null +++ b/src/service/transport/transport_api_cmd_send_simple_performance.c @@ -0,0 +1,220 @@ +/* + This file is part of GNUnet + Copyright (C) 2021 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 . + + SPDX-License-Identifier: AGPL3.0-or-later + */ + +/** + * @file testing_api_cmd_start_peer.c + * @brief cmd to start a peer. + * @author t3sserakt + */ +#include "platform.h" +#include "gnunet_util_lib.h" +#include "gnunet_testing_ng_lib.h" +#include "gnunet_testing_netjail_lib.h" +#include "transport-testing2.h" +#include "transport-testing-cmds.h" + +/** + * Generic logging shortcut + */ +#define LOG(kind, ...) GNUNET_log (kind, __VA_ARGS__) + +/** + * Struct to hold information for callbacks. + * + */ +struct SendSimplePerfState +{ + /** + * Context for our asynchronous completion. + */ + struct GNUNET_TESTING_AsyncContext ac; + + /** + * Label of the cmd to start a peer. + * + */ + const char *start_peer_label; + + /** + * Label of the cmd which started the test system. + * + */ + const char *create_label; + + /** + * The topology we get the connected nodes from. + */ + struct GNUNET_TESTING_NetjailTopology *topology; + + /** + * Size of the message in bytes. + */ + unsigned int size; + + /** + * Maximum number of messages per peer. + */ + unsigned int max_send; +}; + +struct MQWrapper +{ + /** + * State of the command. + */ + struct SendSimplePerfState *sss; + + /** + * Message queue for a peer. + */ + struct GNUNET_MQ_Handle *mq; + + /** + * Number of messages allready send. + */ + uint32_t num_send; +}; + +/** + * The cleanup function of this cmd frees resources the cmd allocated. + * + */ +static void +send_simple_cleanup (void *cls) +{ + struct SendSimpleState *sss = cls; + + GNUNET_free (sss); +} + + +static void +send_simple_single (void *cls) +{ + struct MQWrapper *mq_wrapper = cls; + struct GNUNET_MQ_Envelope *env; + struct GNUNET_TRANSPORT_TESTING_PerformanceTestMessage *test; + struct GNUNET_TIME_Absolute now; + + now = GNUNET_TIME_absolute_get (); + mq_wrapper->num_send++; + LOG (GNUNET_ERROR_TYPE_DEBUG, + "Sending simple test message with size %u number %u with mq %p max %u\n", + mq_wrapper->sss->size, + mq_wrapper->num_send, + mq_wrapper->mq, + mq_wrapper->sss->max_send); + + env = GNUNET_MQ_msg_extra (test, + mq_wrapper->sss->size - sizeof(*test), + GNUNET_TRANSPORT_TESTING_SIMPLE_PERFORMANCE_MTYPE); + test->num = htonl (mq_wrapper->num_send); + test->time_send = GNUNET_TIME_absolute_hton (now); + memset (&test[1], + '1', + mq_wrapper->sss->size - sizeof(*test)); + GNUNET_MQ_send (mq_wrapper->mq, + env); + if (mq_wrapper->sss->max_send > mq_wrapper->num_send) + GNUNET_SCHEDULER_add_now (&send_simple_single, mq_wrapper); + else + GNUNET_TESTING_async_finish (&mq_wrapper->sss->ac); +} + + +static int +send_simple_cb (void *cls, + const struct GNUNET_ShortHashCode *key, + void *value) +{ + struct SendSimplePerfState *sss = cls; + struct GNUNET_MQ_Handle *mq = value; + struct MQWrapper *mq_wrapper = GNUNET_new (struct MQWrapper); + + mq_wrapper->sss = sss; + mq_wrapper->mq = mq; + send_simple_single (mq_wrapper); + + return GNUNET_OK; +} + + +/** + * The run method of this cmd will send a simple message to the connected peers. + * + */ +static void +send_simple_run (void *cls, + struct GNUNET_TESTING_Interpreter *is) +{ + struct SendSimplePerfState *sss = cls; + const struct GNUNET_CONTAINER_MultiShortmap *connected_peers_map; + const struct GNUNET_TESTING_Command *peer1_cmd; + const struct GNUNET_TESTING_Command *system_cmd; + const struct GNUNET_TESTING_System *tl_system; + + + peer1_cmd = GNUNET_TESTING_interpreter_lookup_command (is, + sss->start_peer_label); + GNUNET_TRANSPORT_get_trait_connected_peers_map (peer1_cmd, + &connected_peers_map); + + system_cmd = GNUNET_TESTING_interpreter_lookup_command (is, + sss->create_label); + GNUNET_TESTING_get_trait_test_system (system_cmd, + &tl_system); + + GNUNET_CONTAINER_multishortmap_iterate ( + (struct GNUNET_CONTAINER_MultiShortmap *) + connected_peers_map, send_simple_cb, + sss); +} + + +struct GNUNET_TESTING_Command +GNUNET_TRANSPORT_cmd_send_simple_performance (const char *label, + const char *start_peer_label, + const char *create_label, + uint32_t num, + int size, + int max_send, + struct + GNUNET_TESTING_NetjailTopology * + topology) +{ + struct SendSimplePerfState *sss; + struct GNUNET_TESTING_Command cmd; + + sss = GNUNET_new (struct SendSimplePerfState); + sss->start_peer_label = start_peer_label; + sss->create_label = create_label; + sss->topology = topology; + sss->size = size; + sss->max_send = max_send; + + cmd = GNUNET_TESTING_command_new (sss, + label, + &send_simple_run, + &send_simple_cleanup, + NULL, + &sss->ac); + cmd.asynchronous_finish = GNUNET_YES; + return cmd; +} diff --git a/src/service/transport/transport_api_cmd_start_peer.c b/src/service/transport/transport_api_cmd_start_peer.c new file mode 100644 index 000000000..54e204a21 --- /dev/null +++ b/src/service/transport/transport_api_cmd_start_peer.c @@ -0,0 +1,483 @@ +/* + This file is part of GNUnet + Copyright (C) 2021 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 . + + SPDX-License-Identifier: AGPL3.0-or-later + */ + +/** + * @file testing_api_cmd_start_peer.c + * @brief cmd to start a peer. + * @author t3sserakt + */ +#include "platform.h" +#include "gnunet_util_lib.h" +#include "gnunet_testing_ng_lib.h" +#include "gnunet_testing_netjail_lib.h" +#include "gnunet_peerstore_service.h" +#include "gnunet_transport_core_service.h" +#include "gnunet_transport_application_service.h" +#include "transport-testing-cmds.h" + +/** + * Generic logging shortcut + */ +#define LOG(kind, ...) GNUNET_log (kind, __VA_ARGS__) + + +static void +retrieve_hello (void *cls); + + +/** + * Callback delivering the hello of this peer from peerstore. + * + */ +static void +hello_iter_cb (void *cb_cls, + const struct GNUNET_PEERSTORE_Record *record, + const char *emsg) +{ + struct GNUNET_TESTING_StartPeerState *sps = cb_cls; + if (NULL == record) + { + sps->pic = NULL; + sps->rh_task = GNUNET_SCHEDULER_add_now (retrieve_hello, sps); + return; + } + // Check record type et al? + sps->hello_size = record->value_size; + sps->hello = GNUNET_malloc (sps->hello_size); + memcpy (sps->hello, record->value, sps->hello_size); + sps->hello[sps->hello_size - 1] = '\0'; + + LOG (GNUNET_ERROR_TYPE_DEBUG, + "Our hello %s\n", + sps->hello); + GNUNET_PEERSTORE_iterate_cancel (sps->pic); + sps->pic = NULL; + GNUNET_TESTING_async_finish (&sps->ac); +} + + +/** + * Function to start the retrieval task to retrieve the hello of this peer + * from the peerstore. + * + */ +static void +retrieve_hello (void *cls) +{ + struct GNUNET_TESTING_StartPeerState *sps = cls; + sps->rh_task = NULL; + sps->pic = GNUNET_PEERSTORE_iterate (sps->ph, + "transport", + &sps->id, + GNUNET_PEERSTORE_TRANSPORT_HELLO_KEY, + hello_iter_cb, + sps); + +} + + +/** + * Disconnect callback for the connection to the core service. + * + */ +static void +notify_disconnect (void *cls, + const struct GNUNET_PeerIdentity *peer, + void *handler_cls) +{ + struct GNUNET_TESTING_StartPeerState *sps = cls; + + LOG (GNUNET_ERROR_TYPE_DEBUG, + "Peer %s disconnected from peer %u (`%s')\n", + GNUNET_i2s (peer), + sps->no, + GNUNET_i2s (&sps->id)); + +} + + +/** + * Connect callback for the connection to the core service. + * + */ +static void * +notify_connect (void *cls, + const struct GNUNET_PeerIdentity *peer, + struct GNUNET_MQ_Handle *mq) +{ + struct GNUNET_TESTING_StartPeerState *sps = cls; + struct GNUNET_ShortHashCode *key = GNUNET_new (struct GNUNET_ShortHashCode); + struct GNUNET_HashCode hc; + struct GNUNET_CRYPTO_EddsaPublicKey public_key = peer->public_key; + + void *ret = (struct GNUNET_PeerIdentity *) peer; + + LOG (GNUNET_ERROR_TYPE_DEBUG, + "This Peer %s \n", + GNUNET_i2s (&sps->id)); + + + GNUNET_CRYPTO_hash (&public_key, sizeof(public_key), &hc); + + LOG (GNUNET_ERROR_TYPE_DEBUG, + "Peer %s connected to peer number %u with mq %p\n", + GNUNET_i2s (peer), + sps->no, + mq); + + + memcpy (key, + &hc, + sizeof (*key)); + GNUNET_CONTAINER_multishortmap_put (sps->connected_peers_map, + key, + mq, + GNUNET_CONTAINER_MULTIHASHMAPOPTION_MULTIPLE); + + GNUNET_free (key); + + sps->notify_connect (sps->ac.is, + peer); + + return ret; +} + + +/** + * The run method of this cmd will start all services of a peer to test the transport service. + * + */ +static void +start_peer_run (void *cls, + struct GNUNET_TESTING_Interpreter *is) +{ + struct GNUNET_TESTING_StartPeerState *sps = cls; + char *emsg = NULL; + struct GNUNET_PeerIdentity dummy; + const struct GNUNET_TESTING_Command *system_cmd; + const struct GNUNET_TESTING_System *tl_system; + char *home; + char *transport_unix_path; + char *tcp_communicator_unix_path; + char *udp_communicator_unix_path; + char *bindto; + char *bindto_udp; + + if (GNUNET_NO == GNUNET_DISK_file_test (sps->cfgname)) + { + LOG (GNUNET_ERROR_TYPE_ERROR, + "File not found: `%s'\n", + sps->cfgname); + GNUNET_TESTING_interpreter_fail (is); + return; + } + + + sps->cfg = GNUNET_CONFIGURATION_create (); + GNUNET_assert (GNUNET_OK == + GNUNET_CONFIGURATION_load (sps->cfg, sps->cfgname)); + + GNUNET_asprintf (&home, + "$GNUNET_TMP/test-transport/api-tcp-p%u", + sps->no); + + GNUNET_asprintf (&transport_unix_path, + "$GNUNET_RUNTIME_DIR/tng-p%u.sock", + sps->no); + + GNUNET_asprintf (&tcp_communicator_unix_path, + "$GNUNET_RUNTIME_DIR/tcp-comm-p%u.sock", + sps->no); + + GNUNET_asprintf (&udp_communicator_unix_path, + "$GNUNET_RUNTIME_DIR/tcp-comm-p%u.sock", + sps->no); + + GNUNET_asprintf (&bindto, + "%s:60002", + sps->node_ip); + + GNUNET_asprintf (&bindto_udp, + "2086"); + + LOG (GNUNET_ERROR_TYPE_DEBUG, + "node_ip %s\n", + bindto); + + LOG (GNUNET_ERROR_TYPE_DEBUG, + "bind_udp %s\n", + GNUNET_YES == sps->broadcast ? + bindto_udp : bindto); + + GNUNET_CONFIGURATION_set_value_string (sps->cfg, "PATHS", "GNUNET_TEST_HOME", + home); + GNUNET_CONFIGURATION_set_value_string (sps->cfg, "transport", "UNIXPATH", + transport_unix_path); + GNUNET_CONFIGURATION_set_value_string (sps->cfg, "communicator-tcp", + "BINDTO", + bindto); + GNUNET_CONFIGURATION_set_value_string (sps->cfg, "communicator-udp", + "BINDTO", + GNUNET_YES == sps->broadcast ? + bindto_udp : bindto); + GNUNET_CONFIGURATION_set_value_string (sps->cfg, "communicator-tcp", + "UNIXPATH", + tcp_communicator_unix_path); + GNUNET_CONFIGURATION_set_value_string (sps->cfg, "communicator-udp", + "UNIXPATH", + udp_communicator_unix_path); + + + system_cmd = GNUNET_TESTING_interpreter_lookup_command (is, + sps->system_label); + GNUNET_TESTING_get_trait_test_system (system_cmd, + &tl_system); + + sps->tl_system = tl_system; + + LOG (GNUNET_ERROR_TYPE_DEBUG, + "Creating testing library with key number %u\n", + sps->no); + + if (GNUNET_SYSERR == + GNUNET_TESTING_configuration_create ((struct + GNUNET_TESTING_System *) tl_system, + sps->cfg)) + { + LOG (GNUNET_ERROR_TYPE_DEBUG, + "Testing library failed to create unique configuration based on `%s'\n", + sps->cfgname); + GNUNET_CONFIGURATION_destroy (sps->cfg); + GNUNET_TESTING_interpreter_fail (is); + return; + } + + sps->peer = GNUNET_TESTING_peer_configure ((struct + GNUNET_TESTING_System *) sps-> + tl_system, + sps->cfg, + sps->no, + NULL, + &emsg); + if (NULL == sps->peer) + { + LOG (GNUNET_ERROR_TYPE_ERROR, + "Testing library failed to create unique configuration based on `%s': `%s' with key number %u\n", + sps->cfgname, + emsg, + sps->no); + GNUNET_free (emsg); + GNUNET_TESTING_interpreter_fail (is); + return; + } + + if (GNUNET_OK != GNUNET_TESTING_peer_start (sps->peer)) + { + LOG (GNUNET_ERROR_TYPE_ERROR, + "Testing library failed to create unique configuration based on `%s'\n", + sps->cfgname); + GNUNET_free (emsg); + GNUNET_TESTING_interpreter_fail (is); + return; + } + + memset (&dummy, + '\0', + sizeof(dummy)); + + GNUNET_TESTING_peer_get_identity (sps->peer, + &sps->id); + + if (0 == memcmp (&dummy, + &sps->id, + sizeof(struct GNUNET_PeerIdentity))) + { + LOG (GNUNET_ERROR_TYPE_ERROR, + "Testing library failed to obtain peer identity for peer %u\n", + sps->no); + GNUNET_free (emsg); + GNUNET_TESTING_interpreter_fail (is); + return; + } + LOG (GNUNET_ERROR_TYPE_DEBUG, + "Peer %u configured with identity `%s'\n", + sps->no, + GNUNET_i2s_full (&sps->id)); + + sps->th = GNUNET_TRANSPORT_core_connect (sps->cfg, + NULL, + sps->handlers, + sps, + ¬ify_connect, + ¬ify_disconnect); + if (NULL == sps->th) + { + LOG (GNUNET_ERROR_TYPE_ERROR, + "Failed to connect to transport service for peer `%s': `%s'\n", + sps->cfgname, + emsg); + GNUNET_free (emsg); + GNUNET_TESTING_interpreter_fail (is); + return; + } + + sps->ph = GNUNET_PEERSTORE_connect (sps->cfg); + if (NULL == sps->ph) + { + LOG (GNUNET_ERROR_TYPE_ERROR, + "Failed to connect to peerstore service for peer `%s': `%s'\n", + sps->cfgname, + emsg); + GNUNET_free (emsg); + GNUNET_TESTING_interpreter_fail (is); + return; + } + + sps->ah = GNUNET_TRANSPORT_application_init (sps->cfg); + if (NULL == sps->ah) + { + LOG (GNUNET_ERROR_TYPE_ERROR, + "Failed to initialize the TRANSPORT application suggestion client handle for peer `%s': `%s'\n", + sps->cfgname, + emsg); + GNUNET_free (emsg); + GNUNET_TESTING_interpreter_fail (is); + return; + } + sps->rh_task = GNUNET_SCHEDULER_add_now (retrieve_hello, sps); + GNUNET_free (home); + GNUNET_free (transport_unix_path); + GNUNET_free (tcp_communicator_unix_path); + GNUNET_free (udp_communicator_unix_path); + GNUNET_free (bindto); + GNUNET_free (bindto_udp); +} + + +/** + * The cleanup function of this cmd frees resources the cmd allocated. + * + */ +static void +start_peer_cleanup (void *cls) +{ + struct GNUNET_TESTING_StartPeerState *sps = cls; + + if (NULL != sps->handlers) + { + GNUNET_free (sps->handlers); + sps->handlers = NULL; + } + // TODO Investigate why this caused problems during shutdown. + /*if (NULL != sps->cfg) + { + GNUNET_CONFIGURATION_destroy (sps->cfg); + sps->cfg = NULL; + }*/ + GNUNET_free (sps->cfgname); + GNUNET_free (sps->node_ip); + GNUNET_free (sps->system_label); + GNUNET_free (sps->hello); + GNUNET_free (sps->connected_peers_map); + GNUNET_free (sps); +} + + +/** + * This function prepares an array with traits. + * + */ +static int +start_peer_traits (void *cls, + const void **ret, + const char *trait, + unsigned int index) +{ + struct GNUNET_TESTING_StartPeerState *sps = cls; + struct GNUNET_TRANSPORT_ApplicationHandle *ah = sps->ah; + struct GNUNET_PeerIdentity *id = &sps->id; + struct GNUNET_CONTAINER_MultiShortmap *connected_peers_map = + sps->connected_peers_map; + char *hello = sps->hello; + size_t hello_size = sps->hello_size; + + + struct GNUNET_TESTING_Trait traits[] = { + GNUNET_TRANSPORT_make_trait_application_handle ((const void *) ah), + GNUNET_TRANSPORT_make_trait_peer_id ((const void *) id), + GNUNET_TRANSPORT_make_trait_connected_peers_map ((const + void *) + connected_peers_map), + GNUNET_TRANSPORT_make_trait_hello ((const void *) hello), + GNUNET_TRANSPORT_make_trait_hello_size ((const void *) hello_size), + GNUNET_TRANSPORT_make_trait_state ((const void *) sps), + GNUNET_TRANSPORT_make_trait_broadcast ((const void *) &sps->broadcast), + GNUNET_TESTING_trait_end () + }; + + return GNUNET_TESTING_get_trait (traits, + ret, + trait, + index); +} + + +struct GNUNET_TESTING_Command +GNUNET_TRANSPORT_cmd_start_peer (const char *label, + const char *system_label, + uint32_t no, + const char *node_ip, + struct GNUNET_MQ_MessageHandler *handlers, + const char *cfgname, + GNUNET_TRANSPORT_notify_connect_cb + notify_connect, + unsigned int broadcast) +{ + struct GNUNET_TESTING_StartPeerState *sps; + struct GNUNET_CONTAINER_MultiShortmap *connected_peers_map = + GNUNET_CONTAINER_multishortmap_create (1,GNUNET_NO); + unsigned int i; + + sps = GNUNET_new (struct GNUNET_TESTING_StartPeerState); + sps->no = no; + sps->system_label = GNUNET_strdup (system_label); + sps->connected_peers_map = connected_peers_map; + sps->cfgname = GNUNET_strdup (cfgname); + sps->node_ip = GNUNET_strdup (node_ip); + sps->notify_connect = notify_connect; + sps->broadcast = broadcast; + + if (NULL != handlers) + { + for (i = 0; NULL != handlers[i].cb; i++) + ; + sps->handlers = GNUNET_new_array (i + 1, + struct GNUNET_MQ_MessageHandler); + GNUNET_memcpy (sps->handlers, + handlers, + i * sizeof(struct GNUNET_MQ_MessageHandler)); + } + return GNUNET_TESTING_command_new (sps, + label, + &start_peer_run, + &start_peer_cleanup, + &start_peer_traits, + &sps->ac); +} diff --git a/src/service/transport/transport_api_cmd_stop_peer.c b/src/service/transport/transport_api_cmd_stop_peer.c new file mode 100644 index 000000000..60d48c56b --- /dev/null +++ b/src/service/transport/transport_api_cmd_stop_peer.c @@ -0,0 +1,154 @@ +/* + This file is part of GNUnet + Copyright (C) 2021 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 . + + SPDX-License-Identifier: AGPL3.0-or-later + */ + +/** + * @file testing_api_cmd_stop_peer.c + * @brief cmd to stop a peer. + * @author t3sserakt + */ +#include "platform.h" +#include "gnunet_util_lib.h" +#include "gnunet_testing_ng_lib.h" +#include "gnunet_testing_netjail_lib.h" +#include "gnunet_peerstore_service.h" +#include "gnunet_transport_core_service.h" +#include "gnunet_transport_application_service.h" +#include "transport-testing-cmds.h" + +/** + * Generic logging shortcut + */ +#define LOG(kind, ...) GNUNET_log (kind, __VA_ARGS__) + + +/** + * Struct to hold information for callbacks. + * + */ +struct StopPeerState +{ + // Label of the cmd to start the peer. + const char *start_label; +}; + + +/** + * The run method of this cmd will stop all services of a peer which were used to test the transport service. + * + */ +static void +stop_peer_run (void *cls, + struct GNUNET_TESTING_Interpreter *is) +{ + struct StopPeerState *stop_ps = cls; + const struct GNUNET_TESTING_StartPeerState *sps; + const struct GNUNET_TESTING_Command *start_cmd; + + start_cmd = GNUNET_TESTING_interpreter_lookup_command (is, + stop_ps->start_label); + GNUNET_TRANSPORT_get_trait_state (start_cmd, + &sps); + + if (NULL != sps->pic) + { + GNUNET_PEERSTORE_iterate_cancel (sps->pic); + } + if (NULL != sps->th) + { + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Disconnecting from TRANSPORT service\n"); + GNUNET_TRANSPORT_core_disconnect (sps->th); + } + if (NULL != sps->ah) + { + GNUNET_TRANSPORT_application_done (sps->ah); + } + if (NULL != sps->ph) + { + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Disconnecting from PEERSTORE service\n"); + GNUNET_PEERSTORE_disconnect (sps->ph, GNUNET_NO); + } + if (NULL != sps->peer) + { + if (GNUNET_OK != + GNUNET_TESTING_peer_stop (sps->peer)) + { + LOG (GNUNET_ERROR_TYPE_ERROR, + "Testing lib failed to stop peer %u (`%s')\n", + sps->no, + GNUNET_i2s (&sps->id)); + } + GNUNET_TESTING_peer_destroy (sps->peer); + } + if (NULL != sps->rh_task) + GNUNET_SCHEDULER_cancel (sps->rh_task); +} + + +/** + * The cleanup function of this cmd frees resources the cmd allocated. + * + */ +static void +stop_peer_cleanup (void *cls) +{ + struct StopPeerState *sps = cls; + + GNUNET_free (sps); +} + + +/** + * Trait function of this cmd does nothing. + * + */ +static int +stop_peer_traits (void *cls, + const void **ret, + const char *trait, + unsigned int index) +{ + return GNUNET_OK; +} + + +/** + * Create command. + * + * @param label name for command. + * @param start_label Label of the cmd to start the peer. + * @return command. + */ +struct GNUNET_TESTING_Command +GNUNET_TRANSPORT_cmd_stop_peer (const char *label, + const char *start_label) +{ + struct StopPeerState *sps; + + sps = GNUNET_new (struct StopPeerState); + sps->start_label = start_label; + return GNUNET_TESTING_command_new (sps, + label, + &stop_peer_run, + &stop_peer_cleanup, + &stop_peer_traits, + NULL); +} diff --git a/src/service/transport/transport_api_traits.c b/src/service/transport/transport_api_traits.c new file mode 100644 index 000000000..7e66cc3d1 --- /dev/null +++ b/src/service/transport/transport_api_traits.c @@ -0,0 +1,32 @@ +/* + This file is part of GNUnet + Copyright (C) 2021 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 . + + SPDX-License-Identifier: AGPL3.0-or-later + */ + +/** + * @file transport/test_transport_start_with_config.c + * @brief Generic program to start testcases in an configurable topology. + * @author t3sserakt + */ +#include "platform.h" +#include "gnunet_testing_ng_lib.h" +#include "gnunet_testing_netjail_lib.h" +#include "transport-testing-cmds.h" +#include "gnunet_util_lib.h" + +GNUNET_TRANSPORT_SIMPLE_TRAITS (GNUNET_TRANSPORT_MAKE_IMPL_SIMPLE_TRAIT) diff --git a/src/service/transport/upnp.sh b/src/service/transport/upnp.sh new file mode 100755 index 000000000..d01a1a1a1 --- /dev/null +++ b/src/service/transport/upnp.sh @@ -0,0 +1,22 @@ +#!/bin/bash + +if [ $2 -eq 1 ] +then + if [ ! -d /tmp/netjail_scripts ] + then + mkdir /tmp/netjail_scripts + fi + + ext_ifname=$(ip addr |grep UP|grep "@"|awk -F: '{printf $2"\n"}'|tr -d " "|awk -F@ '{printf $1" "}'|awk '{printf $1}') + listening_ip=$(ip addr |grep UP|grep "@"|awk -F: '{printf $2"\n"}'|tr -d " "|awk -F@ '{printf $1" "}'|awk '{printf $2}') + uuid=$(uuidgen) + cat miniupnpd.conf |sed 's/#ext_ifname=eth1/ext_ifname='$ext_ifname'/g'|sed 's/#listening_ip=eth0/listening_ip='$listening_ip'/g'|sed 's/uuid=73a9cb68-a00b-4d2c-8412-75fc989f0c6/uuid='$uuid'/g'|grep -v "^#"|grep -v '^$' > /tmp/netjail_scripts/gargoyle.txt + miniupnpd -d -f /tmp/netjail_scripts/gargoyle.txt -P /tmp/netjail_scripts/miniupnpd_$1.pid & +else + kill $(cat /tmp/netjail_scripts/miniupnpd_$1.pid) +fi + + + + + diff --git a/src/set/Makefile.am b/src/set/Makefile.am index d8c47f45d..150da06e0 100644 --- a/src/set/Makefile.am +++ b/src/set/Makefile.am @@ -28,9 +28,9 @@ gnunet_set_profiler_SOURCES = \ gnunet-set-profiler.c gnunet_set_profiler_LDADD = \ $(top_builddir)/src/lib/util/libgnunetutil.la \ - $(top_builddir)/src/statistics/libgnunetstatistics.la \ + $(top_builddir)/src/service/statistics/libgnunetstatistics.la \ libgnunetset.la \ - $(top_builddir)/src/lib/testing/libgnunettesting.la \ + $(top_builddir)/src/service/testing/libgnunettesting.la \ $(GN_LIBINTL) @@ -50,7 +50,7 @@ gnunet_service_set_SOURCES = \ gnunet-service-set_protocol.h gnunet_service_set_LDADD = \ $(top_builddir)/src/lib/util/libgnunetutil.la \ - $(top_builddir)/src/statistics/libgnunetstatistics.la \ + $(top_builddir)/src/service/statistics/libgnunetstatistics.la \ $(top_builddir)/src/service/core/libgnunetcore.la \ $(top_builddir)/src/cadet/libgnunetcadet.la \ $(top_builddir)/src/lib/block/libgnunetblock.la \ @@ -80,28 +80,28 @@ test_set_api_SOURCES = \ test_set_api.c test_set_api_LDADD = \ $(top_builddir)/src/lib/util/libgnunetutil.la \ - $(top_builddir)/src/lib/testing/libgnunettesting.la \ + $(top_builddir)/src/service/testing/libgnunettesting.la \ libgnunetset.la test_set_union_result_symmetric_SOURCES = \ test_set_union_result_symmetric.c test_set_union_result_symmetric_LDADD = \ $(top_builddir)/src/lib/util/libgnunetutil.la \ - $(top_builddir)/src/lib/testing/libgnunettesting.la \ + $(top_builddir)/src/service/testing/libgnunettesting.la \ libgnunetset.la test_set_intersection_result_full_SOURCES = \ test_set_intersection_result_full.c test_set_intersection_result_full_LDADD = \ $(top_builddir)/src/lib/util/libgnunetutil.la \ - $(top_builddir)/src/lib/testing/libgnunettesting.la \ + $(top_builddir)/src/service/testing/libgnunettesting.la \ libgnunetset.la test_set_union_copy_SOURCES = \ test_set_union_copy.c test_set_union_copy_LDADD = \ $(top_builddir)/src/lib/util/libgnunetutil.la \ - $(top_builddir)/src/lib/testing/libgnunettesting.la \ + $(top_builddir)/src/service/testing/libgnunettesting.la \ libgnunetset.la plugin_LTLIBRARIES = \ diff --git a/src/seti/Makefile.am b/src/seti/Makefile.am index 96adf377d..46e200516 100644 --- a/src/seti/Makefile.am +++ b/src/seti/Makefile.am @@ -27,9 +27,9 @@ gnunet_seti_profiler_SOURCES = \ gnunet-seti-profiler.c gnunet_seti_profiler_LDADD = \ $(top_builddir)/src/lib/util/libgnunetutil.la \ - $(top_builddir)/src/statistics/libgnunetstatistics.la \ + $(top_builddir)/src/service/statistics/libgnunetstatistics.la \ libgnunetseti.la \ - $(top_builddir)/src/lib/testing/libgnunettesting.la \ + $(top_builddir)/src/service/testing/libgnunettesting.la \ $(GN_LIBINTL) @@ -38,7 +38,7 @@ gnunet_service_seti_SOURCES = \ gnunet-service-seti_protocol.h gnunet_service_seti_LDADD = \ $(top_builddir)/src/lib/util/libgnunetutil.la \ - $(top_builddir)/src/statistics/libgnunetstatistics.la \ + $(top_builddir)/src/service/statistics/libgnunetstatistics.la \ $(top_builddir)/src/service/core/libgnunetcore.la \ $(top_builddir)/src/cadet/libgnunetcadet.la \ $(top_builddir)/src/lib/block/libgnunetblock.la \ @@ -65,7 +65,7 @@ test_seti_api_SOURCES = \ test_seti_api.c test_seti_api_LDADD = \ $(top_builddir)/src/lib/util/libgnunetutil.la \ - $(top_builddir)/src/lib/testing/libgnunettesting.la \ + $(top_builddir)/src/service/testing/libgnunettesting.la \ libgnunetseti.la plugin_LTLIBRARIES = \ diff --git a/src/setu/Makefile.am b/src/setu/Makefile.am index 3ecd8f32a..f826c9e2a 100644 --- a/src/setu/Makefile.am +++ b/src/setu/Makefile.am @@ -28,9 +28,9 @@ gnunet_setu_profiler_SOURCES = \ gnunet-setu-profiler.c gnunet_setu_profiler_LDADD = \ $(top_builddir)/src/lib/util/libgnunetutil.la \ - $(top_builddir)/src/statistics/libgnunetstatistics.la \ + $(top_builddir)/src/service/statistics/libgnunetstatistics.la \ libgnunetsetu.la \ - $(top_builddir)/src/lib/testing/libgnunettesting.la \ + $(top_builddir)/src/service/testing/libgnunettesting.la \ $(GN_LIBINTL) @@ -48,7 +48,7 @@ gnunet_service_setu_SOURCES = \ gnunet-service-setu_protocol.h gnunet_service_setu_LDADD = \ $(top_builddir)/src/lib/util/libgnunetutil.la \ - $(top_builddir)/src/statistics/libgnunetstatistics.la \ + $(top_builddir)/src/service/statistics/libgnunetstatistics.la \ $(top_builddir)/src/service/core/libgnunetcore.la \ $(top_builddir)/src/cadet/libgnunetcadet.la \ $(top_builddir)/src/lib/block/libgnunetblock.la \ @@ -76,7 +76,7 @@ test_setu_api_SOURCES = \ test_setu_api.c test_setu_api_LDADD = \ $(top_builddir)/src/lib/util/libgnunetutil.la \ - $(top_builddir)/src/lib/testing/libgnunettesting.la \ + $(top_builddir)/src/service/testing/libgnunettesting.la \ libgnunetsetu.la @@ -84,7 +84,7 @@ perf_setu_api_SOURCES = \ perf_setu_api.c perf_setu_api_LDADD = \ $(top_builddir)/src/lib/util/libgnunetutil.la \ - $(top_builddir)/src/lib/testing/libgnunettesting.la \ + $(top_builddir)/src/service/testing/libgnunettesting.la \ libgnunetsetu.la diff --git a/src/statistics/.gitignore b/src/statistics/.gitignore deleted file mode 100644 index f1f567149..000000000 --- a/src/statistics/.gitignore +++ /dev/null @@ -1,7 +0,0 @@ -gnunet-statistics -gnunet-service-statistics -test_gnunet_statistics.py -test_statistics_api -test_statistics_api_loop -test_statistics_api_watch -test_statistics_api_watch_zero_value diff --git a/src/statistics/Makefile.am b/src/statistics/Makefile.am deleted file mode 100644 index ed4796274..000000000 --- a/src/statistics/Makefile.am +++ /dev/null @@ -1,98 +0,0 @@ -# This Makefile.am is in the public domain -AM_CPPFLAGS = -I$(top_srcdir)/src/include - -if USE_COVERAGE - AM_CFLAGS = --coverage -O0 - XLIB = -lgcov -endif - -pkgcfgdir= $(pkgdatadir)/config.d/ - -libexecdir= $(pkglibdir)/libexec/ - -pkgcfg_DATA = \ - statistics.conf - -lib_LTLIBRARIES = libgnunetstatistics.la - -libgnunetstatistics_la_SOURCES = \ - statistics_api.c statistics.h -libgnunetstatistics_la_LIBADD = \ - $(top_builddir)/src/lib/util/libgnunetutil.la \ - $(GN_LIBINTL) $(XLIB) -libgnunetstatistics_la_LDFLAGS = \ - $(GN_LIB_LDFLAGS) \ - -version-info 2:0:0 - -libexec_PROGRAMS = \ - gnunet-service-statistics - -bin_PROGRAMS = \ - gnunet-statistics - -gnunet_statistics_SOURCES = \ - gnunet-statistics.c -gnunet_statistics_LDADD = \ - libgnunetstatistics.la \ - $(top_builddir)/src/lib/util/libgnunetutil.la \ - $(GN_LIBINTL) - -gnunet_service_statistics_SOURCES = \ - gnunet-service-statistics.c -gnunet_service_statistics_LDADD = \ - libgnunetstatistics.la \ - $(top_builddir)/src/lib/util/libgnunetutil.la \ - $(GN_LIBINTL) - -check_PROGRAMS = \ - test_statistics_api \ - test_statistics_api_loop \ - test_statistics_api_watch \ - test_statistics_api_watch_zero_value - -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) $(check_SCRIPTS) -endif - -test_statistics_api_SOURCES = \ - test_statistics_api.c -test_statistics_api_LDADD = \ - libgnunetstatistics.la \ - $(top_builddir)/src/lib/util/libgnunetutil.la - -test_statistics_api_loop_SOURCES = \ - test_statistics_api_loop.c -test_statistics_api_loop_LDADD = \ - libgnunetstatistics.la \ - $(top_builddir)/src/lib/util/libgnunetutil.la - -test_statistics_api_watch_SOURCES = \ - test_statistics_api_watch.c -test_statistics_api_watch_LDADD = \ - libgnunetstatistics.la \ - $(top_builddir)/src/lib/util/libgnunetutil.la - -test_statistics_api_watch_zero_value_SOURCES = \ - test_statistics_api_watch_zero_value.c -test_statistics_api_watch_zero_value_LDADD = \ - libgnunetstatistics.la \ - $(top_builddir)/src/lib/util/libgnunetutil.la - -if HAVE_PYTHON -check_SCRIPTS = \ - test_gnunet_statistics.py -endif - -SUFFIXES = .py.in .py -.py.in.py: - $(AWK) -v bdir="$(bindir)" -v py="$(PYTHON)" -v awkay="$(AWK_BINARY)" -v pfx="$(prefix)" -v prl="$(PERL)" -v sysconfdirectory="$(sysconfdir)" -v pkgdatadirectory="$(pkgdatadir)" -f $(top_srcdir)/bin/dosubst.awk < $(srcdir)/$< > $@ - chmod +x $@ - -test_gnunet_statistics.py: test_gnunet_statistics.py.in Makefile - $(AWK) -v bdir="$(bindir)" -v py="$(PYTHON)" -v awkay="$(AWK_BINARY)" -v pfx="$(prefix)" -v prl="$(PERL)" -v sysconfdirectory="$(sysconfdir)" -v pkgdatadirectory="$(pkgdatadir)" -f $(top_srcdir)/bin/dosubst.awk < $(srcdir)/test_gnunet_statistics.py.in > test_gnunet_statistics.py - chmod +x test_gnunet_statistics.py - -EXTRA_DIST = \ - test_statistics_api_data.conf \ - test_gnunet_statistics.py.in diff --git a/src/statistics/gnunet-service-statistics.c b/src/statistics/gnunet-service-statistics.c deleted file mode 100644 index a6c897a79..000000000 --- a/src/statistics/gnunet-service-statistics.c +++ /dev/null @@ -1,1059 +0,0 @@ -/* - This file is part of GNUnet. - Copyright (C) 2009, 2010, 2012, 2014, 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 . - - SPDX-License-Identifier: AGPL3.0-or-later - */ - -/** - * @file statistics/gnunet-service-statistics.c - * @brief program that tracks statistics - * @author Christian Grothoff - */ -#include "platform.h" -#include "gnunet_util_lib.h" -#include "gnunet_protocols.h" -#include "gnunet_statistics_service.h" -#include "gnunet_time_lib.h" -#include "statistics.h" - -/** - * Watch entry. - */ -struct WatchEntry -{ - /** - * Watch entries are kept in a linked list. - */ - struct WatchEntry *next; - - /** - * Watch entries are kept in a linked list. - */ - struct WatchEntry *prev; - - /** - * For which client is this watch entry? - */ - struct ClientEntry *ce; - - /** - * Last value we communicated to the client for this watch entry. - */ - uint64_t last_value; - - /** - * Unique watch number for this client and this watched value. - */ - uint32_t wid; - - /** - * Is last_value valid - * #GNUNET_NO : last_value is n/a, #GNUNET_YES: last_value is valid - */ - int last_value_set; -}; - - -/** - * We keep the statistics organized by subsystem for faster - * lookup during SET operations. - */ -struct SubsystemEntry; - - -/** - * Entry in the statistics list. - */ -struct StatsEntry -{ - /** - * This is a linked list. - */ - struct StatsEntry *next; - - /** - * This is a linked list. - */ - struct StatsEntry *prev; - - /** - * Subsystem this entry belongs to. - */ - struct SubsystemEntry *subsystem; - - /** - * Name for the value stored by this entry, allocated at the end of - * this struct. - */ - const char *name; - - /** - * Watch context for changes to this value, or NULL for none. - */ - struct WatchEntry *we_head; - - /** - * Watch context for changes to this value, or NULL for none. - */ - struct WatchEntry *we_tail; - - /** - * Our value. - */ - uint64_t value; - - /** - * Unique ID. - */ - uint32_t uid; - - /** - * Is this value persistent? - */ - int persistent; - - /** - * Is this value set? - * #GNUNET_NO: value is n/a, #GNUNET_YES: value is valid - */ - int set; -}; - - -/** - * We keep the statistics organized by subsystem for faster - * lookup during SET operations. - */ -struct SubsystemEntry -{ - /** - * Subsystems are kept in a DLL. - */ - struct SubsystemEntry *next; - - /** - * Subsystems are kept in a DLL. - */ - struct SubsystemEntry *prev; - - /** - * Head of list of values kept for this subsystem. - */ - struct StatsEntry *stat_head; - - /** - * Tail of list of values kept for this subsystem. - */ - struct StatsEntry *stat_tail; - - /** - * Name of the subsystem this entry is for, allocated at - * the end of this struct, do not free(). - */ - const char *service; -}; - - -/** - * Client entry. - */ -struct ClientEntry -{ - /** - * Corresponding server handle. - */ - struct GNUNET_SERVICE_Client *client; - - /** - * Corresponding message queue. - */ - struct GNUNET_MQ_Handle *mq; - - /** - * Which subsystem is this client writing to (SET/UPDATE)? - */ - struct SubsystemEntry *subsystem; - - /** - * Maximum watch ID used by this client so far. - */ - uint32_t max_wid; -}; - - -/** - * Our configuration. - */ -static const struct GNUNET_CONFIGURATION_Handle *cfg; - -/** - * Head of linked list of subsystems with active statistics. - */ -static struct SubsystemEntry *sub_head; - -/** - * Tail of linked list of subsystems with active statistics. - */ -static struct SubsystemEntry *sub_tail; - -/** - * Number of connected clients. - */ -static unsigned int client_count; - -/** - * Our notification context. - */ -static struct GNUNET_NotificationContext *nc; - -/** - * Counter used to generate unique values. - */ -static uint32_t uidgen; - -/** - * Set to #GNUNET_YES if we are shutting down as soon as possible. - */ -static int in_shutdown; - - -/** - * Write persistent statistics to disk. - */ -static void -save () -{ - struct SubsystemEntry *se; - struct StatsEntry *pos; - char *fn; - struct GNUNET_BIO_WriteHandle *wh; - uint16_t size; - unsigned long long total; - size_t nlen; - size_t slen; - struct GNUNET_STATISTICS_SetMessage *msg; - - if (GNUNET_OK != GNUNET_CONFIGURATION_get_value_filename (cfg, - "STATISTICS", - "DATABASE", - &fn)) - { - GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR, - "STATISTICS", - "DATABASE"); - return; - } - (void) GNUNET_DISK_directory_create_for_file (fn); - wh = GNUNET_BIO_write_open_file (fn); - total = 0; - while (NULL != (se = sub_head)) - { - GNUNET_CONTAINER_DLL_remove (sub_head, sub_tail, se); - slen = strlen (se->service) + 1; - while (NULL != (pos = se->stat_head)) - { - GNUNET_CONTAINER_DLL_remove (se->stat_head, se->stat_tail, pos); - if ((pos->persistent) && (NULL != wh)) - { - nlen = strlen (pos->name) + 1; - size = sizeof(struct GNUNET_STATISTICS_SetMessage) + nlen + slen; - GNUNET_assert (size < UINT16_MAX); - msg = GNUNET_malloc (size); - - msg->header.size = htons ((uint16_t) size); - msg->header.type = htons (GNUNET_MESSAGE_TYPE_STATISTICS_SET); - GNUNET_assert (nlen + slen == - GNUNET_STRINGS_buffer_fill ((char *) &msg[1], - nlen + slen, - 2, - se->service, - pos->name)); - msg->flags = - htonl (pos->persistent ? GNUNET_STATISTICS_SETFLAG_PERSISTENT : 0); - msg->value = GNUNET_htonll (pos->value); - if (GNUNET_OK != GNUNET_BIO_write (wh, "statistics-save-msg", msg, - size)) - { - GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING, "write", fn); - if (GNUNET_OK != GNUNET_BIO_write_close (wh, NULL)) - GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING, "close", fn); - wh = NULL; - } - else - { - total += size; - } - GNUNET_free (msg); - } - GNUNET_free (pos); - } - GNUNET_free (se); - } - if (NULL != wh) - { - if (GNUNET_OK != GNUNET_BIO_write_close (wh, NULL)) - GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING, "close", fn); - if (0 == total) - GNUNET_break (0 == unlink (fn)); - else - GNUNET_log (GNUNET_ERROR_TYPE_INFO, - _ ("Wrote %llu bytes of statistics to `%s'\n"), - total, - fn); - } - GNUNET_free (fn); -} - - -/** - * Transmit the given stats value. - * - * @param ce receiver of the value - * @param e value to transmit - */ -static void -transmit (struct ClientEntry *ce, const struct StatsEntry *e) -{ - struct GNUNET_MQ_Envelope *env; - struct GNUNET_STATISTICS_ReplyMessage *m; - size_t size; - - size = strlen (e->subsystem->service) + 1 + strlen (e->name) + 1; - GNUNET_assert (size < GNUNET_MAX_MESSAGE_SIZE); - env = GNUNET_MQ_msg_extra (m, size, GNUNET_MESSAGE_TYPE_STATISTICS_VALUE); - m->uid = htonl (e->uid); - if (e->persistent) - m->uid |= htonl (GNUNET_STATISTICS_PERSIST_BIT); - m->value = GNUNET_htonll (e->value); - GNUNET_assert (size == GNUNET_STRINGS_buffer_fill ((char *) &m[1], - size, - 2, - e->subsystem->service, - e->name)); - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "Transmitting value for `%s:%s' (%d): %llu\n", - e->subsystem->service, - e->name, - e->persistent, - (unsigned long long) e->value); - GNUNET_MQ_send (ce->mq, env); -} - - -/** - * 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; - client_count++; - GNUNET_notification_context_add (nc, mq); - return ce; -} - - -/** - * Check integrity of GET-message. - * - * @param cls identification of the client - * @param message the actual message - * @return #GNUNET_OK if @a message is well-formed - */ -static int -check_get (void *cls, const struct GNUNET_MessageHeader *message) -{ - const char *service; - const char *name; - size_t size; - - size = ntohs (message->size) - sizeof(struct GNUNET_MessageHeader); - if (size != GNUNET_STRINGS_buffer_tokenize ((const char *) &message[1], - size, - 2, - &service, - &name)) - { - GNUNET_break (0); - return GNUNET_SYSERR; - } - return GNUNET_OK; -} - - -/** - * Handle GET-message. - * - * @param cls identification of the client - * @param message the actual message - */ -static void -handle_get (void *cls, const struct GNUNET_MessageHeader *message) -{ - struct ClientEntry *ce = cls; - struct GNUNET_MQ_Envelope *env; - struct GNUNET_MessageHeader *end; - const char *service; - const char *name; - size_t slen; - size_t nlen; - struct SubsystemEntry *se; - struct StatsEntry *pos; - size_t size; - - size = ntohs (message->size) - sizeof(struct GNUNET_MessageHeader); - GNUNET_assert (size == - GNUNET_STRINGS_buffer_tokenize ((const char *) &message[1], - size, - 2, - &service, - &name)); - slen = strlen (service); - nlen = strlen (name); - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "Received request for statistics on `%s:%s'\n", - slen ? service : "*", - nlen ? name : "*"); - for (se = sub_head; NULL != se; se = se->next) - { - if (! ((0 == slen) || (0 == strcmp (service, se->service)))) - continue; - for (pos = se->stat_head; NULL != pos; pos = pos->next) - { - if (! ((0 == nlen) || (0 == strcmp (name, pos->name)))) - continue; - transmit (ce, pos); - } - } - env = GNUNET_MQ_msg (end, GNUNET_MESSAGE_TYPE_STATISTICS_END); - GNUNET_MQ_send (ce->mq, env); - GNUNET_SERVICE_client_continue (ce->client); -} - - -/** - * Notify all clients listening about a change to a value. - * - * @param se value that changed - */ -static void -notify_change (struct StatsEntry *se) -{ - struct GNUNET_MQ_Envelope *env; - struct GNUNET_STATISTICS_WatchValueMessage *wvm; - struct WatchEntry *pos; - - for (pos = se->we_head; NULL != pos; pos = pos->next) - { - if (GNUNET_YES == pos->last_value_set) - { - if (pos->last_value == se->value) - continue; - } - else - { - pos->last_value_set = GNUNET_YES; - } - env = GNUNET_MQ_msg (wvm, GNUNET_MESSAGE_TYPE_STATISTICS_WATCH_VALUE); - wvm->flags = - htonl (se->persistent ? GNUNET_STATISTICS_SETFLAG_PERSISTENT : 0); - wvm->wid = htonl (pos->wid); - wvm->reserved = htonl (0); - wvm->value = GNUNET_htonll (se->value); - GNUNET_MQ_send (pos->ce->mq, env); - pos->last_value = se->value; - } -} - - -/** - * Find the subsystem entry of the given name for the specified client. - * - * @param ce client looking for the subsystem, may contain a hint - * to find the entry faster, can be NULL - * @param service name of the subsystem to look for - * @return subsystem entry, never NULL (subsystem entry is created if necessary) - */ -static struct SubsystemEntry * -find_subsystem_entry (struct ClientEntry *ce, const char *service) -{ - size_t slen; - struct SubsystemEntry *se; - - if (NULL != ce) - se = ce->subsystem; - else - se = NULL; - if ((NULL == se) || (0 != strcmp (service, se->service))) - { - for (se = sub_head; NULL != se; se = se->next) - if (0 == strcmp (service, se->service)) - break; - if (NULL != ce) - ce->subsystem = se; - } - if (NULL != se) - return se; - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "Allocating new subsystem entry `%s'\n", - service); - slen = strlen (service) + 1; - se = GNUNET_malloc (sizeof(struct SubsystemEntry) + slen); - GNUNET_memcpy (&se[1], service, slen); - se->service = (const char *) &se[1]; - GNUNET_CONTAINER_DLL_insert (sub_head, sub_tail, se); - if (NULL != ce) - ce->subsystem = se; - return se; -} - - -/** - * Find the statistics entry of the given subsystem. - * - * @param se subsystem to look in - * @param name name of the entry to look for - * @return statistics entry, or NULL if not found - */ -static struct StatsEntry * -find_stat_entry (struct SubsystemEntry *se, const char *name) -{ - struct StatsEntry *pos; - - for (pos = se->stat_head; NULL != pos; pos = pos->next) - if (0 == strcmp (name, pos->name)) - return pos; - return NULL; -} - - -/** - * Check format of SET-message. - * - * @param cls the `struct ClientEntry` - * @param msg the actual message - * @return #GNUNET_OK if message is well-formed - */ -static int -check_set (void *cls, const struct GNUNET_STATISTICS_SetMessage *msg) -{ - const char *service; - const char *name; - size_t msize; - - msize = ntohs (msg->header.size) - sizeof(*msg); - if (msize != GNUNET_STRINGS_buffer_tokenize ((const char *) &msg[1], - msize, - 2, - &service, - &name)) - { - GNUNET_break (0); - return GNUNET_SYSERR; - } - return GNUNET_OK; -} - - -/** - * Handle SET-message. - * - * @param cls the `struct ClientEntry` - * @param msg the actual message - */ -static void -handle_set (void *cls, const struct GNUNET_STATISTICS_SetMessage *msg) -{ - struct ClientEntry *ce = cls; - const char *service; - const char *name; - size_t nlen; - uint16_t msize; - uint16_t size; - struct SubsystemEntry *se; - struct StatsEntry *pos; - uint32_t flags; - uint64_t value; - int64_t delta; - int changed; - int initial_set; - - msize = ntohs (msg->header.size); - size = msize - sizeof(struct GNUNET_STATISTICS_SetMessage); - GNUNET_assert (size == GNUNET_STRINGS_buffer_tokenize ((const char *) &msg[1], - size, - 2, - &service, - &name)); - se = find_subsystem_entry (ce, service); - flags = ntohl (msg->flags); - value = GNUNET_ntohll (msg->value); - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "Received request to update statistic on `%s:%s' (%u) to/by %llu\n", - service, - name, - (unsigned int) flags, - (unsigned long long) value); - pos = find_stat_entry (se, name); - if (NULL != pos) - { - initial_set = 0; - if (0 == (flags & GNUNET_STATISTICS_SETFLAG_RELATIVE)) - { - changed = (pos->value != value); - pos->value = value; - } - else - { - delta = (int64_t) value; - if ((delta < 0) && (pos->value < -delta)) - { - changed = (0 != pos->value); - pos->value = 0; - } - else - { - changed = (0 != delta); - GNUNET_break ((delta <= 0) || (pos->value + delta > pos->value)); - pos->value += delta; - } - } - if (GNUNET_NO == pos->set) - { - pos->set = GNUNET_YES; - initial_set = 1; - } - pos->persistent = (0 != (flags & GNUNET_STATISTICS_SETFLAG_PERSISTENT)); - if (pos != se->stat_head) - { - /* move to front for faster setting next time! */ - GNUNET_CONTAINER_DLL_remove (se->stat_head, se->stat_tail, pos); - GNUNET_CONTAINER_DLL_insert (se->stat_head, se->stat_tail, pos); - } - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "Statistic `%s:%s' updated to value %llu (%d).\n", - service, - name, - (unsigned long long) pos->value, - pos->persistent); - if ((changed) || (1 == initial_set)) - notify_change (pos); - GNUNET_SERVICE_client_continue (ce->client); - return; - } - /* not found, create a new entry */ - nlen = strlen (name) + 1; - pos = GNUNET_malloc (sizeof(struct StatsEntry) + nlen); - GNUNET_memcpy (&pos[1], name, nlen); - pos->name = (const char *) &pos[1]; - pos->subsystem = se; - if ((0 == (flags & GNUNET_STATISTICS_SETFLAG_RELATIVE)) || - (0 < (int64_t) GNUNET_ntohll (msg->value))) - { - pos->value = GNUNET_ntohll (msg->value); - pos->set = GNUNET_YES; - } - else - { - pos->set = GNUNET_NO; - } - pos->uid = uidgen++; - pos->persistent = (0 != (flags & GNUNET_STATISTICS_SETFLAG_PERSISTENT)); - GNUNET_CONTAINER_DLL_insert (se->stat_head, se->stat_tail, pos); - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "New statistic on `%s:%s' with value %llu created.\n", - service, - name, - (unsigned long long) pos->value); - if (NULL != ce) - GNUNET_SERVICE_client_continue (ce->client); -} - - -/** - * Check integrity of WATCH-message. - * - * @param cls the `struct ClientEntry *` - * @param message the actual message - * @return #GNUNET_OK if message is well-formed - */ -static int -check_watch (void *cls, const struct GNUNET_MessageHeader *message) -{ - size_t size; - const char *service; - const char *name; - - size = ntohs (message->size) - sizeof(struct GNUNET_MessageHeader); - if (size != GNUNET_STRINGS_buffer_tokenize ((const char *) &message[1], - size, - 2, - &service, - &name)) - { - GNUNET_break (0); - return GNUNET_SYSERR; - } - return GNUNET_OK; -} - - -/** - * Handle WATCH-message. - * - * @param cls the `struct ClientEntry *` - * @param message the actual message - */ -static void -handle_watch (void *cls, const struct GNUNET_MessageHeader *message) -{ - struct ClientEntry *ce = cls; - const char *service; - const char *name; - uint16_t msize; - uint16_t size; - struct SubsystemEntry *se; - struct StatsEntry *pos; - struct WatchEntry *we; - size_t nlen; - - if (NULL == nc) - { - GNUNET_SERVICE_client_drop (ce->client); - return; - } - GNUNET_SERVICE_client_mark_monitor (ce->client); - msize = ntohs (message->size); - size = msize - sizeof(struct GNUNET_MessageHeader); - GNUNET_assert (size == - GNUNET_STRINGS_buffer_tokenize ((const char *) &message[1], - size, - 2, - &service, - &name)); - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "Received request to watch statistic on `%s:%s'\n", - service, - name); - se = find_subsystem_entry (ce, service); - pos = find_stat_entry (se, name); - if (NULL == pos) - { - nlen = strlen (name) + 1; - pos = GNUNET_malloc (sizeof(struct StatsEntry) + nlen); - GNUNET_memcpy (&pos[1], name, nlen); - pos->name = (const char *) &pos[1]; - pos->subsystem = se; - GNUNET_CONTAINER_DLL_insert (se->stat_head, se->stat_tail, pos); - pos->uid = uidgen++; - pos->set = GNUNET_NO; - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "New statistic on `%s:%s' with value %llu created.\n", - service, - name, - (unsigned long long) pos->value); - } - we = GNUNET_new (struct WatchEntry); - we->ce = ce; - we->last_value_set = GNUNET_NO; - we->wid = ce->max_wid++; - GNUNET_CONTAINER_DLL_insert (pos->we_head, pos->we_tail, we); - if (0 != pos->value) - notify_change (pos); - GNUNET_SERVICE_client_continue (ce->client); -} - - -/** - * Handle DISCONNECT-message. Sync to disk and send - * back a #GNUNET_MESSAGE_TYPE_STATISTICS_DISCONNECT_CONFIRM - * message. - * - * @param cls the `struct ClientEntry *` - * @param message the actual message - */ -static void -handle_disconnect (void *cls, const struct GNUNET_MessageHeader *message) -{ - struct ClientEntry *ce = cls; - struct GNUNET_MQ_Envelope *env; - struct GNUNET_MessageHeader *msg; - - env = GNUNET_MQ_msg (msg, GNUNET_MESSAGE_TYPE_STATISTICS_DISCONNECT_CONFIRM); - GNUNET_MQ_send (ce->mq, env); - GNUNET_SERVICE_client_continue (ce->client); -} - - -/** - * Actually perform the shutdown. - */ -static void -do_shutdown () -{ - struct WatchEntry *we; - struct StatsEntry *pos; - struct SubsystemEntry *se; - - if (NULL == nc) - return; - save (); - GNUNET_notification_context_destroy (nc); - nc = NULL; - GNUNET_assert (0 == client_count); - while (NULL != (se = sub_head)) - { - GNUNET_CONTAINER_DLL_remove (sub_head, sub_tail, se); - while (NULL != (pos = se->stat_head)) - { - GNUNET_CONTAINER_DLL_remove (se->stat_head, se->stat_tail, pos); - while (NULL != (we = pos->we_head)) - { - GNUNET_break (0); - GNUNET_CONTAINER_DLL_remove (pos->we_head, pos->we_tail, we); - GNUNET_free (we); - } - GNUNET_free (pos); - } - GNUNET_free (se); - } -} - - -/** - * Task run during shutdown. - * - * @param cls unused - */ -static void -shutdown_task (void *cls) -{ - in_shutdown = GNUNET_YES; - if (0 != client_count) - return; - do_shutdown (); -} - - -/** - * A client disconnected. Remove all of its data structure entries. - * - * @param cls closure, NULL - * @param client identification of the client - * @param app_cls the `struct ClientEntry *` - */ -static void -client_disconnect_cb (void *cls, - struct GNUNET_SERVICE_Client *client, - void *app_cls) -{ - struct ClientEntry *ce = app_cls; - struct WatchEntry *we; - struct WatchEntry *wen; - struct StatsEntry *pos; - struct SubsystemEntry *se; - - client_count--; - for (se = sub_head; NULL != se; se = se->next) - { - for (pos = se->stat_head; NULL != pos; pos = pos->next) - { - wen = pos->we_head; - while (NULL != (we = wen)) - { - wen = we->next; - if (we->ce != ce) - continue; - GNUNET_CONTAINER_DLL_remove (pos->we_head, pos->we_tail, we); - GNUNET_free (we); - } - } - } - GNUNET_free (ce); - if ((0 == client_count) && (GNUNET_YES == in_shutdown)) - do_shutdown (); -} - - -/** - * We've read a `struct GNUNET_STATISTICS_SetMessage *` from - * disk. Check that it is well-formed, and if so pass it to - * the handler for set messages. - * - * @param cls NULL - * @param message the message found on disk - * @return #GNUNET_OK on success, - * #GNUNET_NO to stop further processing (no error) - * #GNUNET_SYSERR to stop further processing with error - */ -static int -inject_message (void *cls, const struct GNUNET_MessageHeader *message) -{ - uint16_t msize = ntohs (message->size); - const struct GNUNET_STATISTICS_SetMessage *sm; - - sm = (const struct GNUNET_STATISTICS_SetMessage *) message; - if ((sizeof(struct GNUNET_STATISTICS_SetMessage) > msize) || - (GNUNET_OK != check_set (NULL, sm))) - { - GNUNET_break (0); - return GNUNET_SYSERR; - } - handle_set (NULL, sm); - return GNUNET_OK; -} - - -/** - * Load persistent values from disk. Disk format is exactly the same - * format that we also use for setting the values over the network. - */ -static void -load () -{ - char *fn; - struct GNUNET_BIO_ReadHandle *rh; - uint64_t fsize; - char *buf; - struct GNUNET_MessageStreamTokenizer *mst; - - if (GNUNET_OK != GNUNET_CONFIGURATION_get_value_filename (cfg, - "STATISTICS", - "DATABASE", - &fn)) - { - GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR, - "STATISTICS", - "DATABASE"); - return; - } - if ((GNUNET_OK != - GNUNET_DISK_file_size (fn, &fsize, GNUNET_NO, GNUNET_YES)) || - (0 == fsize)) - { - GNUNET_free (fn); - return; - } - buf = GNUNET_malloc (fsize); - rh = GNUNET_BIO_read_open_file (fn); - if (! rh) - { - GNUNET_free (buf); - GNUNET_free (fn); - return; - } - if (GNUNET_OK != GNUNET_BIO_read (rh, fn, buf, fsize)) - { - GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING, "read", fn); - GNUNET_break (GNUNET_OK == GNUNET_BIO_read_close (rh, NULL)); - GNUNET_free (buf); - GNUNET_free (fn); - return; - } - GNUNET_log (GNUNET_ERROR_TYPE_INFO, - _ ("Loading %llu bytes of statistics from `%s'\n"), - (unsigned long long) fsize, - fn); - mst = GNUNET_MST_create (&inject_message, NULL); - GNUNET_break ( - GNUNET_OK == - GNUNET_MST_from_buffer (mst, buf, (size_t) fsize, GNUNET_YES, GNUNET_NO)); - GNUNET_MST_destroy (mst); - GNUNET_free (buf); - GNUNET_break (GNUNET_OK == GNUNET_BIO_read_close (rh, NULL)); - GNUNET_free (fn); -} - - -/** - * Process statistics requests. - * - * @param cls closure - * @param c configuration to use - * @param service the initialized service - */ -static void -run (void *cls, - const struct GNUNET_CONFIGURATION_Handle *c, - struct GNUNET_SERVICE_Handle *service) -{ - cfg = c; - nc = GNUNET_notification_context_create (16); - load (); - GNUNET_SCHEDULER_add_shutdown (&shutdown_task, NULL); -} - - -/** - * Define "main" method using service macro. - */ -GNUNET_SERVICE_MAIN ( - "statistics", - GNUNET_SERVICE_OPTION_SOFT_SHUTDOWN, - &run, - &client_connect_cb, - &client_disconnect_cb, - NULL, - GNUNET_MQ_hd_var_size (set, - GNUNET_MESSAGE_TYPE_STATISTICS_SET, - struct GNUNET_STATISTICS_SetMessage, - NULL), - GNUNET_MQ_hd_var_size (get, - GNUNET_MESSAGE_TYPE_STATISTICS_GET, - struct GNUNET_MessageHeader, - NULL), - GNUNET_MQ_hd_var_size (watch, - GNUNET_MESSAGE_TYPE_STATISTICS_WATCH, - struct GNUNET_MessageHeader, - NULL), - GNUNET_MQ_hd_fixed_size (disconnect, - GNUNET_MESSAGE_TYPE_STATISTICS_DISCONNECT, - struct GNUNET_MessageHeader, - NULL), - GNUNET_MQ_handler_end ()); - - -#if defined(__linux__) && defined(__GLIBC__) -#include - -/** - * MINIMIZE heap size (way below 128k) since this process doesn't need much. - */ -void __attribute__ ((constructor)) -GNUNET_STATISTICS_memory_init () -{ - mallopt (M_TRIM_THRESHOLD, 4 * 1024); - mallopt (M_TOP_PAD, 1 * 1024); - malloc_trim (0); -} - - -#endif - - -/* end of gnunet-service-statistics.c */ diff --git a/src/statistics/gnunet-statistics.c b/src/statistics/gnunet-statistics.c deleted file mode 100644 index 5894ef68c..000000000 --- a/src/statistics/gnunet-statistics.c +++ /dev/null @@ -1,889 +0,0 @@ -/* - This file is part of GNUnet. - Copyright (C) 2001, 2002, 2004-2007, 2009, 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 . - - SPDX-License-Identifier: AGPL3.0-or-later - */ - -/** - * @file statistics/gnunet-statistics.c - * @brief tool to obtain statistics - * @author Christian Grothoff - * @author Igor Wronsky - */ -#include "platform.h" -#include "gnunet_util_lib.h" -#include "gnunet_statistics_service.h" -#include "statistics.h" - - -/** - * Final status code. - */ -static int ret; - -/** - * Set to subsystem that we're going to get stats for (or NULL for all). - */ -static char *subsystem; - -/** - * The path of the testbed data. - */ -static char *path_testbed; - -/** - * Set to the specific stat value that we are after (or NULL for all). - */ -static char *name; - -/** - * Make the value that is being set persistent. - */ -static int persistent; - -/** - * Watch value continuously - */ -static int watch; - -/** - * Quiet mode - */ -static int quiet; - -/** - * @brief Separator string for csv. - */ -static char *csv_separator; - -/** - * Remote host - */ -static char *remote_host; - -/** - * Remote host's port - */ -static unsigned long long remote_port; - -/** - * Value to set - */ -static unsigned long long set_val; - -/** - * Set operation - */ -static int set_value; - -/** - * @brief Representation of all (testbed) nodes. - */ -static struct Node -{ - /** - * @brief Index of the node in this array. - */ - unsigned index_node; - - /** - * @brief Configuration handle for this node - */ - struct GNUNET_CONFIGURATION_Handle *conf; - - /** - * Handle for pending GET operation. - */ - struct GNUNET_STATISTICS_GetHandle *gh; - - /** - * @brief Statistics handle nodes. - */ - struct GNUNET_STATISTICS_Handle *handle; - /** - * @brief Identifier for shutdown task for this node. - */ - struct GNUNET_SCHEDULER_Task *shutdown_task; -} *nodes; - -/** - * @brief Number of configurations of all (testbed) nodes. - */ -static unsigned num_nodes; - -/** - * @brief Set of values for a combination of subsystem and name. - */ -struct ValueSet -{ - /** - * @brief Subsystem of the valueset. - */ - char *subsystem; - - /** - * @brief Name of the valueset. - */ - char *name; - - /** - * @brief The values. - */ - uint64_t *values; - - /** - * @brief Persistence of the values. - */ - int is_persistent; -}; - - -/** - * @brief Collection of all values (represented with #ValueSet). - */ -static struct GNUNET_CONTAINER_MultiHashMap *values; - -/** - * @brief Number of nodes that have their values ready. - */ -static int num_nodes_ready; - -/** - * @brief Number of nodes that have their values ready. - */ -static int num_nodes_ready_shutdown; - - -/** - * @brief Create a new #ValueSet - * - * @param subsystem Subsystem of the valueset. - * @param name Name of the valueset. - * @param num_values Number of values in valueset - number of peers. - * @param is_persistent Persistence status of values. - * @return Newly allocated #ValueSet. - */ -static struct ValueSet * -new_value_set (const char *subsystem, - const char *name, - unsigned num_values, - int is_persistent) -{ - struct ValueSet *value_set; - - value_set = GNUNET_new (struct ValueSet); - value_set->subsystem = GNUNET_strdup (subsystem); - value_set->name = GNUNET_strdup (name); - value_set->values = GNUNET_new_array (num_values, - uint64_t); - value_set->is_persistent = persistent; - return value_set; -} - - -/** - * @brief Print the (collected) values. - * - * Implements #GNUNET_CONTAINER_HashMapIterator. - * - * @param cls Closure - unused - * @param key #GNUNET_HashCode key of #GNUNET_CONTAINER_MultiHashMap iterator - - * unused - * @param value Values represented as #ValueSet. - * @return #GNUNET_YES - continue iteration. - */ -static int -printer (void *cls, - const struct GNUNET_HashCode *key, - void *value) -{ - struct GNUNET_TIME_Absolute now = GNUNET_TIME_absolute_get (); - const char *now_str; - struct ValueSet *value_set = value; - - if (quiet == GNUNET_NO) - { - if (GNUNET_YES == watch) - { - now_str = GNUNET_STRINGS_absolute_time_to_string (now); - fprintf (stdout, - "%24s%s %s%s%12s%s %s%50s%s%s ", - now_str, - csv_separator, - value_set->is_persistent ? "!" : " ", - csv_separator, - value_set->subsystem, - csv_separator, - (0 == strlen (csv_separator) ? "" : "\""), /* quotes if csv */ - _ (value_set->name), - (0 == strlen (csv_separator) ? "" : "\""), /* quotes if csv */ - (0 == strlen (csv_separator) ? ":" : csv_separator)); - } - else - { - fprintf (stdout, - "%s%s%12s%s %s%50s%s%s ", - value_set->is_persistent ? "!" : " ", - csv_separator, - value_set->subsystem, - csv_separator, - (0 == strlen (csv_separator) ? "" : "\""), /* quotes if csv */ - _ (value_set->name), - (0 == strlen (csv_separator) ? "" : "\""), /* quotes if csv */ - (0 == strlen (csv_separator) ? ":" : csv_separator)); - } - } - for (unsigned i = 0; i < num_nodes; i++) - { - fprintf (stdout, - "%16llu%s", - (unsigned long long) value_set->values[i], - csv_separator); - } - fprintf (stdout, "\n"); - GNUNET_free (value_set->subsystem); - GNUNET_free (value_set->name); - GNUNET_free (value_set->values); - GNUNET_free (value_set); - return GNUNET_YES; -} - - -/** - * Callback function to process statistic values. - * - * @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 -printer_watch (void *cls, - const char *subsystem, - const char *name, - uint64_t value, - int is_persistent) -{ - struct GNUNET_TIME_Absolute now = GNUNET_TIME_absolute_get (); - const char *now_str; - - if (quiet == GNUNET_NO) - { - if (GNUNET_YES == watch) - { - now_str = GNUNET_STRINGS_absolute_time_to_string (now); - fprintf (stdout, - "%24s%s %s%s%12s%s %s%50s%s%s %16llu\n", - now_str, - csv_separator, - is_persistent ? "!" : " ", - csv_separator, - subsystem, - csv_separator, - (0 == strlen (csv_separator) ? "" : "\""), /* quotes if csv */ - _ (name), - (0 == strlen (csv_separator) ? "" : "\""), /* quotes if csv */ - (0 == strlen (csv_separator) ? ":" : csv_separator), - (unsigned long long) value); - } - else - { - fprintf (stdout, - "%s%s%12s%s %s%50s%s%s %16llu\n", - is_persistent ? "!" : " ", - csv_separator, - subsystem, - csv_separator, - (0 == strlen (csv_separator) ? "" : "\""), /* quotes if csv */ - _ (name), - (0 == strlen (csv_separator) ? "" : "\""), /* quotes if csv */ - (0 == strlen (csv_separator) ? ":" : csv_separator), - (unsigned long long) value); - } - } - else - fprintf (stdout, "%llu\n", (unsigned long long) value); - - return GNUNET_OK; -} - - -/** - * @brief Clean all data structures related to given node. - * - * Also clears global structures if we are the last node to clean. - * - * @param cls the index of the node - */ -static void -clean_node (void *cls) -{ - const unsigned index_node = *(unsigned *) cls; - struct GNUNET_STATISTICS_Handle *h; - struct GNUNET_STATISTICS_GetHandle *gh; - - if ((NULL != path_testbed) && /* were issued with -t option */ - (NULL != nodes[index_node].conf)) - { - GNUNET_CONFIGURATION_destroy (nodes[index_node].conf); - nodes[index_node].conf = NULL; - } - - h = nodes[index_node].handle; - gh = nodes[index_node].gh; - - if (NULL != gh) - { - GNUNET_STATISTICS_get_cancel (gh); - gh = NULL; - } - if (GNUNET_YES == watch) - { - GNUNET_assert ( - GNUNET_OK == - GNUNET_STATISTICS_watch_cancel (h, - subsystem, - name, - &printer_watch, - &nodes[index_node].index_node)); - } - - if (NULL != h) - { - GNUNET_STATISTICS_destroy (h, GNUNET_NO); - h = NULL; - } - - num_nodes_ready_shutdown++; -} - - -/** - * @brief Print and shutdown - * - * @param cls unused - */ -static void -print_finish (void *cls) -{ - GNUNET_CONTAINER_multihashmap_iterate (values, - &printer, - NULL); - GNUNET_CONTAINER_multihashmap_destroy (values); - GNUNET_SCHEDULER_shutdown (); -} - - -/** - * @brief Called once all statistic values are available. - * - * Implements #GNUNET_STATISTICS_Callback - * - * @param cls Closure - The index of the node. - * @param success Whether statistics were obtained successfully. - */ -static void -continuation_print (void *cls, - int success) -{ - const unsigned index_node = *(unsigned *) cls; - - nodes[index_node].gh = NULL; - if (GNUNET_OK != success) - { - if (NULL == remote_host) - fprintf (stderr, - "%s", - _ ("Failed to obtain statistics.\n")); - else - fprintf (stderr, - _ ("Failed to obtain statistics from host `%s:%llu'\n"), - remote_host, - remote_port); - ret = 1; - } - if (NULL != nodes[index_node].shutdown_task) - { - GNUNET_SCHEDULER_cancel (nodes[index_node].shutdown_task); - nodes[index_node].shutdown_task = NULL; - } - GNUNET_SCHEDULER_add_now (&clean_node, - &nodes[index_node].index_node); - num_nodes_ready++; - if (num_nodes_ready == num_nodes) - { - GNUNET_SCHEDULER_add_now (&print_finish, - NULL); - } -} - - -/** - * Function called last by the statistics code. - * - * @param cls closure - * @param success #GNUNET_OK if statistics were - * successfully obtained, #GNUNET_SYSERR if not. - */ -static void -cleanup (void *cls, - int success) -{ - for (unsigned i = 0; i < num_nodes; i++) - { - nodes[i].gh = NULL; - } - if (GNUNET_OK != success) - { - if (NULL == remote_host) - fprintf (stderr, "%s", _ ("Failed to obtain statistics.\n")); - else - fprintf (stderr, - _ ("Failed to obtain statistics from host `%s:%llu'\n"), - remote_host, - remote_port); - ret = 1; - } - GNUNET_SCHEDULER_shutdown (); -} - - -/** - * @brief Iterate over statistics values and store them in #values. - * They will be printed once all are available. - * - * @param cls Cosure - Node index. - * @param subsystem Subsystem of the value. - * @param name Name of the value. - * @param value Value itself. - * @param is_persistent Persistence. - * @return #GNUNET_OK - continue. - */ -static int -collector (void *cls, - const char *subsystem, - const char *name, - uint64_t value, - int is_persistent) -{ - const unsigned index_node = *(unsigned *) cls; - struct GNUNET_HashCode *key; - struct GNUNET_HashCode hc; - char *subsys_name; - unsigned len_subsys_name; - struct ValueSet *value_set; - - len_subsys_name = strlen (subsystem) + 3 + strlen (name) + 1; - subsys_name = GNUNET_malloc (len_subsys_name); - sprintf (subsys_name, "%s---%s", subsystem, name); - key = &hc; - GNUNET_CRYPTO_hash (subsys_name, len_subsys_name, key); - GNUNET_free (subsys_name); - if (GNUNET_YES == GNUNET_CONTAINER_multihashmap_contains (values, key)) - { - value_set = GNUNET_CONTAINER_multihashmap_get (values, key); - } - else - { - value_set = new_value_set (subsystem, name, num_nodes, is_persistent); - } - value_set->values[index_node] = value; - GNUNET_assert (GNUNET_YES == - GNUNET_CONTAINER_multihashmap_put ( - values, - key, - value_set, - GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_ONLY)); - return GNUNET_OK; -} - - -/** - * Main task that does the actual work. - * - * @param cls closure with our configuration - */ -static void -main_task (void *cls) -{ - unsigned index_node = *(unsigned *) cls; - const struct GNUNET_CONFIGURATION_Handle *cfg = nodes[index_node].conf; - - if (set_value) - { - if (NULL == subsystem) - { - fprintf (stderr, "%s", _ ("Missing argument: subsystem \n")); - ret = 1; - return; - } - if (NULL == name) - { - fprintf (stderr, "%s", _ ("Missing argument: name\n")); - ret = 1; - return; - } - nodes[index_node].handle = GNUNET_STATISTICS_create (subsystem, cfg); - if (NULL == nodes[index_node].handle) - { - ret = 1; - return; - } - GNUNET_STATISTICS_set (nodes[index_node].handle, - name, - (uint64_t) set_val, - persistent); - GNUNET_STATISTICS_destroy (nodes[index_node].handle, GNUNET_YES); - nodes[index_node].handle = NULL; - return; - } - if (NULL == (nodes[index_node].handle = - GNUNET_STATISTICS_create ("gnunet-statistics", cfg))) - { - ret = 1; - return; - } - if (GNUNET_NO == watch) - { - if (NULL == (nodes[index_node].gh = - GNUNET_STATISTICS_get (nodes[index_node].handle, - subsystem, - name, - &continuation_print, - &collector, - &nodes[index_node].index_node))) - cleanup (nodes[index_node].handle, GNUNET_SYSERR); - } - else - { - if ((NULL == subsystem) || (NULL == name)) - { - printf (_ ("No subsystem or name given\n")); - GNUNET_STATISTICS_destroy (nodes[index_node].handle, GNUNET_NO); - nodes[index_node].handle = NULL; - ret = 1; - return; - } - if (GNUNET_OK != GNUNET_STATISTICS_watch (nodes[index_node].handle, - subsystem, - name, - &printer_watch, - &nodes[index_node].index_node)) - { - fprintf (stderr, _ ("Failed to initialize watch routine\n")); - nodes[index_node].shutdown_task = - GNUNET_SCHEDULER_add_now (&clean_node, &nodes[index_node].index_node); - return; - } - } - nodes[index_node].shutdown_task = - GNUNET_SCHEDULER_add_shutdown (&clean_node, &nodes[index_node].index_node); -} - - -/** - * @brief Iter over content of a node's directory to check for existence of a - * config file. - * - * Implements #GNUNET_FileNameCallback - * - * @param cls pointer to indicate success - * @param filename filename inside the directory of the potential node - * - * @return to continue iteration or not to - */ -static int -iter_check_config (void *cls, - const char *filename) -{ - if (0 == strncmp (GNUNET_STRINGS_get_short_name (filename), "config", 6)) - { - /* Found the config - stop iteration successfully */ - GNUNET_array_grow (nodes, num_nodes, num_nodes + 1); - nodes[num_nodes - 1].conf = GNUNET_CONFIGURATION_create (); - nodes[num_nodes - 1].index_node = num_nodes - 1; - if (GNUNET_OK != - GNUNET_CONFIGURATION_load (nodes[num_nodes - 1].conf, filename)) - { - fprintf (stderr, "Failed loading config `%s'\n", filename); - return GNUNET_SYSERR; - } - return GNUNET_NO; - } - else - { - /* Continue iteration */ - return GNUNET_OK; - } -} - - -/** - * @brief Iterates over filenames in testbed directory. - * - * Implements #GNUNET_FileNameCallback - * - * Checks if the file is a directory for a testbed node - * and counts the nodes. - * - * @param cls counter of nodes - * @param filename full path of the file in testbed - * @return status whether to continue iteration - */ -static int -iter_testbed_path (void *cls, - const char *filename) -{ - unsigned index_node; - - GNUNET_assert (NULL != filename); - if (1 == sscanf (GNUNET_STRINGS_get_short_name (filename), - "%u", - &index_node)) - { - if (-1 == GNUNET_DISK_directory_scan (filename, - iter_check_config, - NULL)) - { - /* This is probably no directory for a testbed node - * Go on with iteration */ - return GNUNET_OK; - } - return GNUNET_OK; - } - return GNUNET_OK; -} - - -/** - * @brief Count the number of nodes running in the testbed - * - * @param path_testbed path to the testbed data - * - * @return number of running nodes - */ -static int -discover_testbed_nodes (const char *path_testbed) -{ - int num_dir_entries; - - num_dir_entries = - GNUNET_DISK_directory_scan (path_testbed, - &iter_testbed_path, - NULL); - if (-1 == num_dir_entries) - { - fprintf (stderr, - "Failure during scanning directory `%s'\n", - path_testbed); - return -1; - } - return 0; -} - - -/** - * 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, - const char *cfgfile, - const struct GNUNET_CONFIGURATION_Handle *cfg) -{ - struct GNUNET_CONFIGURATION_Handle *c; - - c = (struct GNUNET_CONFIGURATION_Handle *) cfg; - set_value = GNUNET_NO; - if (NULL == csv_separator) - csv_separator = ""; - if (NULL != args[0]) - { - if (1 != sscanf (args[0], "%llu", &set_val)) - { - fprintf (stderr, _ ("Invalid argument `%s'\n"), args[0]); - ret = 1; - return; - } - set_value = GNUNET_YES; - } - if (NULL != remote_host) - { - if (0 == remote_port) - { - if (GNUNET_SYSERR == - GNUNET_CONFIGURATION_get_value_number (cfg, - "statistics", - "PORT", - &remote_port)) - { - fprintf (stderr, - _ ("A port is required to connect to host `%s'\n"), - remote_host); - return; - } - } - else if (65535 <= remote_port) - { - fprintf (stderr, - _ ( - "A port has to be between 1 and 65535 to connect to host `%s'\n"), - remote_host); - return; - } - - /* Manipulate configuration */ - GNUNET_CONFIGURATION_set_value_string (c, - "statistics", - "UNIXPATH", - ""); - GNUNET_CONFIGURATION_set_value_string (c, - "statistics", - "HOSTNAME", - remote_host); - GNUNET_CONFIGURATION_set_value_number (c, - "statistics", - "PORT", - remote_port); - } - if (NULL == path_testbed) - { - values = GNUNET_CONTAINER_multihashmap_create (1, GNUNET_NO); - GNUNET_array_grow (nodes, num_nodes, 1); - nodes[0].index_node = 0; - nodes[0].conf = c; - GNUNET_SCHEDULER_add_now (&main_task, &nodes[0].index_node); - } - else - { - if (GNUNET_YES == watch) - { - printf ( - _ ("Not able to watch testbed nodes (yet - feel free to implement)\n")); - ret = 1; - return; - } - values = GNUNET_CONTAINER_multihashmap_create (4, GNUNET_NO); - if (-1 == discover_testbed_nodes (path_testbed)) - { - return; - } - /* For each config/node collect statistics */ - for (unsigned i = 0; i < num_nodes; i++) - { - GNUNET_SCHEDULER_add_now (&main_task, &nodes[i].index_node); - } - } -} - - -/** - * The main function to obtain statistics in GNUnet. - * - * @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 GNUNET_GETOPT_CommandLineOption options[] = { - GNUNET_GETOPT_option_string ('n', - "name", - "NAME", - gettext_noop ( - "limit output to statistics for the given NAME"), - &name), - GNUNET_GETOPT_option_flag ('p', - "persistent", - gettext_noop ( - "make the value being set persistent"), - &persistent), - GNUNET_GETOPT_option_string ('s', - "subsystem", - "SUBSYSTEM", - gettext_noop ( - "limit output to the given SUBSYSTEM"), - &subsystem), - GNUNET_GETOPT_option_string ('S', - "csv-separator", - "CSV_SEPARATOR", - gettext_noop ("use as csv separator"), - &csv_separator), - GNUNET_GETOPT_option_filename ('t', - "testbed", - "TESTBED", - gettext_noop ( - "path to the folder containing the testbed data"), - &path_testbed), - GNUNET_GETOPT_option_flag ('q', - "quiet", - gettext_noop ( - "just print the statistics value"), - &quiet), - GNUNET_GETOPT_option_flag ('w', - "watch", - gettext_noop ("watch value continuously"), - &watch), - GNUNET_GETOPT_option_string ('r', - "remote", - "REMOTE", - gettext_noop ("connect to remote host"), - &remote_host), - GNUNET_GETOPT_option_ulong ('o', - "port", - "PORT", - gettext_noop ("port for remote host"), - &remote_port), - GNUNET_GETOPT_OPTION_END - }; - - remote_port = 0; - remote_host = NULL; - if (GNUNET_OK != - GNUNET_STRINGS_get_utf8_args (argc, argv, - &argc, &argv)) - return 2; - - ret = (GNUNET_OK == - GNUNET_PROGRAM_run (argc, - argv, - "gnunet-statistics [options [value]]", - gettext_noop ( - "Print statistics about GNUnet operations."), - options, - &run, - NULL)) - ? ret - : 1; - GNUNET_array_grow (nodes, - num_nodes, - 0); - GNUNET_free (remote_host); - GNUNET_free_nz ((void *) argv); - return ret; -} - - -/* end of gnunet-statistics.c */ diff --git a/src/statistics/meson.build b/src/statistics/meson.build deleted file mode 100644 index 14cdb0ac3..000000000 --- a/src/statistics/meson.build +++ /dev/null @@ -1,44 +0,0 @@ -libgnunetstatistics_src = ['statistics_api.c'] - -gnunetservicestatistics_src = ['gnunet-service-statistics.c'] - -configure_file(input : 'statistics.conf.in', - output : 'statistics.conf', - configuration : cdata, - install: true, - install_dir: pkgcfgdir) - - -if get_option('monolith') - foreach p : libgnunetstatistics_src + gnunetservicestatistics_src - gnunet_src += 'statistics/' + p - endforeach - subdir_done() -endif - -libgnunetstatistics = library('gnunetstatistics', - libgnunetstatistics_src, - soversion: '2', - version: '2.0.0', - dependencies: libgnunetutil_dep, - include_directories: [incdir, configuration_inc], - install: true, - install_dir: get_option('libdir')) -libgnunetstatistics_dep = declare_dependency(link_with : libgnunetstatistics) -pkg.generate(libgnunetstatistics, url: 'https://www.gnunet.org', - description : 'Provides API of GNUnet statistics service') - -executable ('gnunet-service-statistics', - gnunetservicestatistics_src, - dependencies: [libgnunetstatistics_dep, libgnunetutil_dep], - include_directories: [incdir, configuration_inc], - install: true, - install_dir: get_option('libdir') / 'gnunet' / 'libexec') -executable ('gnunet-statistics', - ['gnunet-statistics.c'], - dependencies: [libgnunetstatistics_dep, libgnunetutil_dep], - include_directories: [incdir, configuration_inc], - install: true, - install_dir: get_option('bindir')) - - diff --git a/src/statistics/statistics.conf.in b/src/statistics/statistics.conf.in deleted file mode 100644 index 36aca538f..000000000 --- a/src/statistics/statistics.conf.in +++ /dev/null @@ -1,21 +0,0 @@ -[statistics] -START_ON_DEMAND = @START_ON_DEMAND@ -@JAVAPORT@PORT = 2088 -HOSTNAME = localhost -BINARY = gnunet-service-statistics -ACCEPT_FROM = 127.0.0.1; -ACCEPT_FROM6 = ::1; -UNIXPATH = $GNUNET_RUNTIME_DIR/gnunet-service-statistics.sock -UNIX_MATCH_UID = NO -UNIX_MATCH_GID = YES -DATABASE = $GNUNET_DATA_HOME/statistics.dat -# DISABLE_SOCKET_FORWARDING = NO -# USERNAME = -# MAXBUF = -# TIMEOUT = -# DISABLEV6 = -# BINDTO = -# REJECT_FROM = -# REJECT_FROM6 = -# PREFIX = - diff --git a/src/statistics/statistics.h b/src/statistics/statistics.h deleted file mode 100644 index 6eb75cc99..000000000 --- a/src/statistics/statistics.h +++ /dev/null @@ -1,149 +0,0 @@ -/* - This file is part of GNUnet. - Copyright (C) 2001-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 . - - SPDX-License-Identifier: AGPL3.0-or-later - */ - -/** - * @author Christian Grothoff - * @file statistics/statistics.h - */ -#ifndef STATISTICS_H -#define STATISTICS_H - -#include "gnunet_common.h" - - -GNUNET_NETWORK_STRUCT_BEGIN - -/** - * Statistics message. Contains how long the system is up - * and one value. - * - * The struct is be followed by the service name and - * name of the statistic, both 0-terminated. - */ -struct GNUNET_STATISTICS_ReplyMessage -{ - /** - * Type: GNUNET_MESSAGE_TYPE_STATISTICS_VALUE - */ - struct GNUNET_MessageHeader header; - - /** - * Unique numerical identifier for the value (will - * not change during the same client-session). Highest - * bit will be set for persistent values (see - * #GNUNET_STATISTICS_PERSIST_BIT). - */ - uint32_t uid GNUNET_PACKED; - - /** - * The value. - */ - uint64_t value GNUNET_PACKED; -}; - -/** - * Flag for the `struct GNUNET_STATISTICS_ReplyMessage` UID only. - * Note that other messages use #GNUNET_STATISTICS_SETFLAG_PERSISTENT. - */ -#define GNUNET_STATISTICS_PERSIST_BIT ((uint32_t) (1LLU << 31)) - -/** - * The value being set is an absolute change. - */ -#define GNUNET_STATISTICS_SETFLAG_ABSOLUTE 0 - -/** - * The value being set is a relative change. - */ -#define GNUNET_STATISTICS_SETFLAG_RELATIVE 1 - -/** - * The value being set is to be persistent (note that - * this bit can be combined with #GNUNET_STATISTICS_SETFLAG_RELATIVE). - * This value must not be used for the `uid` member of - * `struct GNUNET_STATISTICS_ReplyMessage`! - */ -#define GNUNET_STATISTICS_SETFLAG_PERSISTENT 2 - - -/** - * Message to set a statistic. Followed - * by the subsystem name and the name of - * the statistic (each 0-terminated). - */ -struct GNUNET_STATISTICS_SetMessage -{ - /** - * Type: #GNUNET_MESSAGE_TYPE_STATISTICS_SET - */ - struct GNUNET_MessageHeader header; - - /** - * 0 for absolute value, 1 for relative value; 2 to make persistent - * (see GNUNET_STATISTICS_SETFLAG_*). - */ - uint32_t flags GNUNET_PACKED; - - /** - * Value. Note that if this is a relative value, it will - * be signed even though the type given here is unsigned. - */ - uint64_t value GNUNET_PACKED; -}; - - -/** - * Message transmitted if a watched value changes. - */ -struct GNUNET_STATISTICS_WatchValueMessage -{ - /** - * Type: #GNUNET_MESSAGE_TYPE_STATISTICS_WATCH_VALUE - */ - struct GNUNET_MessageHeader header; - - /** - * 0 for absolute value, 1 for relative value; 2 to make persistent - * (see GNUNET_STATISTICS_SETFLAG_*). - */ - uint32_t flags GNUNET_PACKED; - - /** - * Unique watch identification number (watch - * requests are enumerated in the order they - * are received, the first request having - * a wid of zero). - */ - uint32_t wid GNUNET_PACKED; - - /** - * Reserved (always 0). - */ - uint32_t reserved GNUNET_PACKED; - - /** - * Value. Note that if this is a relative value, it will - * be signed even though the type given here is unsigned. - */ - uint64_t value GNUNET_PACKED; -}; -GNUNET_NETWORK_STRUCT_END - -#endif diff --git a/src/statistics/statistics_api.c b/src/statistics/statistics_api.c deleted file mode 100644 index 88f127da8..000000000 --- a/src/statistics/statistics_api.c +++ /dev/null @@ -1,1342 +0,0 @@ -/* - This file is part of GNUnet. - Copyright (C) 2009, 2010, 2011, 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 . - - SPDX-License-Identifier: AGPL3.0-or-later - */ - -/** - * @file statistics/statistics_api.c - * @brief API of the statistics service - * @author Christian Grothoff - */ -#include "platform.h" -#include "gnunet_util_lib.h" -#include "gnunet_constants.h" -#include "gnunet_protocols.h" -#include "gnunet_statistics_service.h" -#include "statistics.h" - -/** - * How long do we wait until a statistics request for setting - * a value times out? (The update will be lost if the - * service does not react within this timeframe). - */ -#define SET_TRANSMIT_TIMEOUT GNUNET_TIME_relative_multiply ( \ - GNUNET_TIME_UNIT_SECONDS, 2) - -#define LOG(kind, ...) GNUNET_log_from (kind, "statistics-api", __VA_ARGS__) - -/** - * Types of actions. - */ -enum ActionType -{ - /** - * Get a value. - */ - ACTION_GET, - - /** - * Set a value. - */ - ACTION_SET, - - /** - * Update a value. - */ - ACTION_UPDATE, - - /** - * Watch a value. - */ - ACTION_WATCH -}; - - -/** - * Entry kept for each value we are watching. - */ -struct GNUNET_STATISTICS_WatchEntry -{ - /** - * What subsystem is this action about? (never NULL) - */ - char *subsystem; - - /** - * What value is this action about? (never NULL) - */ - char *name; - - /** - * Function to call - */ - GNUNET_STATISTICS_Iterator proc; - - /** - * Closure for @e proc - */ - void *proc_cls; -}; - - -/** - * Linked list of things we still need to do. - */ -struct GNUNET_STATISTICS_GetHandle -{ - /** - * This is a doubly linked list. - */ - struct GNUNET_STATISTICS_GetHandle *next; - - /** - * This is a doubly linked list. - */ - struct GNUNET_STATISTICS_GetHandle *prev; - - /** - * Main statistics handle. - */ - struct GNUNET_STATISTICS_Handle *sh; - - /** - * What subsystem is this action about? (can be NULL) - */ - char *subsystem; - - /** - * What value is this action about? (can be NULL) - */ - char *name; - - /** - * Continuation to call once action is complete. - */ - GNUNET_STATISTICS_Callback cont; - - /** - * Function to call (for GET actions only). - */ - GNUNET_STATISTICS_Iterator proc; - - /** - * Closure for @e proc and @e cont. - */ - void *cls; - - /** - * Timeout for this action. - */ - struct GNUNET_TIME_Absolute timeout; - - /** - * Associated value. - */ - uint64_t value; - - /** - * Flag for SET/UPDATE actions. - */ - int make_persistent; - - /** - * Has the current iteration been aborted; for GET actions. - */ - int aborted; - - /** - * Is this a #ACTION_GET, #ACTION_SET, #ACTION_UPDATE or #ACTION_WATCH? - */ - enum ActionType type; - - /** - * Size of the message that we will be transmitting. - */ - uint16_t msize; -}; - - -/** - * Handle for the service. - */ -struct GNUNET_STATISTICS_Handle -{ - /** - * Name of our subsystem. - */ - char *subsystem; - - /** - * Configuration to use. - */ - const struct GNUNET_CONFIGURATION_Handle *cfg; - - /** - * Message queue to the service. - */ - struct GNUNET_MQ_Handle *mq; - - /** - * Head of the linked list of pending actions (first action - * to be performed). - */ - struct GNUNET_STATISTICS_GetHandle *action_head; - - /** - * Tail of the linked list of actions (for fast append). - */ - struct GNUNET_STATISTICS_GetHandle *action_tail; - - /** - * Action we are currently busy with (action request has been - * transmitted, we're now receiving the response from the - * service). - */ - struct GNUNET_STATISTICS_GetHandle *current; - - /** - * Array of watch entries. - */ - struct GNUNET_STATISTICS_WatchEntry **watches; - - /** - * Task doing exponential back-off trying to reconnect. - */ - struct GNUNET_SCHEDULER_Task *backoff_task; - - /** - * Task for running #do_destroy(). - */ - struct GNUNET_SCHEDULER_Task *destroy_task; - - /** - * Time for next connect retry. - */ - struct GNUNET_TIME_Relative backoff; - - /** - * Maximum heap size observed so far (if available). - */ - uint64_t peak_heap_size; - - /** - * Maximum resident set side observed so far (if available). - */ - uint64_t peak_rss; - - /** - * Size of the @e watches array. - */ - unsigned int watches_size; - - /** - * Should this handle auto-destruct once all actions have - * been processed? - */ - int do_destroy; - - /** - * Are we currently receiving from the service? - */ - int receiving; -}; - - -/** - * Obtain statistics about this process's memory consumption and - * report those as well (if they changed). - */ -static void -update_memory_statistics (struct GNUNET_STATISTICS_Handle *h) -{ -#if ENABLE_HEAP_STATISTICS - uint64_t current_heap_size = 0; - uint64_t current_rss = 0; - - if (GNUNET_NO != h->do_destroy) - return; -#if HAVE_MALLINFO2 - { - struct mallinfo2 mi; - - mi = mallinfo2 (); - current_heap_size = mi.uordblks + mi.fordblks; - } -#endif -#if HAVE_GETRUSAGE - { - struct rusage ru; - - if (0 == getrusage (RUSAGE_SELF, &ru)) - { - current_rss = 1024LL * ru.ru_maxrss; - } - } -#endif - if (current_heap_size > h->peak_heap_size) - { - h->peak_heap_size = current_heap_size; - GNUNET_STATISTICS_set (h, - "# peak heap size", - current_heap_size, - GNUNET_NO); - } - if (current_rss > h->peak_rss) - { - h->peak_rss = current_rss; - GNUNET_STATISTICS_set (h, - "# peak resident set size", - current_rss, - GNUNET_NO); - } -#endif -} - - -/** - * Reconnect at a later time, respecting back-off. - * - * @param h statistics handle - */ -static void -reconnect_later (struct GNUNET_STATISTICS_Handle *h); - - -/** - * Schedule the next action to be performed. - * - * @param cls statistics handle to reconnect - */ -static void -schedule_action (void *cls); - - -/** - * Transmit request to service that we want to watch - * the development of a particular value. - * - * @param h statistics handle - * @param watch watch entry of the value to watch - */ -static void -schedule_watch_request (struct GNUNET_STATISTICS_Handle *h, - struct GNUNET_STATISTICS_WatchEntry *watch) -{ - struct GNUNET_STATISTICS_GetHandle *ai; - size_t slen; - size_t nlen; - size_t nsize; - - slen = strlen (watch->subsystem) + 1; - nlen = strlen (watch->name) + 1; - nsize = sizeof(struct GNUNET_MessageHeader) + slen + nlen; - if (nsize >= GNUNET_MAX_MESSAGE_SIZE) - { - GNUNET_break (0); - return; - } - ai = GNUNET_new (struct GNUNET_STATISTICS_GetHandle); - ai->sh = h; - ai->subsystem = GNUNET_strdup (watch->subsystem); - ai->name = GNUNET_strdup (watch->name); - ai->timeout = GNUNET_TIME_UNIT_FOREVER_ABS; - ai->msize = nsize; - ai->type = ACTION_WATCH; - ai->proc = watch->proc; - ai->cls = watch->proc_cls; - GNUNET_CONTAINER_DLL_insert_tail (h->action_head, - h->action_tail, - ai); - schedule_action (h); -} - - -/** - * Free memory associated with the given action item. - * - * @param gh action item to free - */ -static void -free_action_item (struct GNUNET_STATISTICS_GetHandle *gh) -{ - GNUNET_free (gh->subsystem); - GNUNET_free (gh->name); - GNUNET_free (gh); -} - - -/** - * Disconnect from the statistics service. - * - * @param h statistics handle to disconnect from - */ -static void -do_disconnect (struct GNUNET_STATISTICS_Handle *h) -{ - struct GNUNET_STATISTICS_GetHandle *c; - - h->receiving = GNUNET_NO; - if (NULL != (c = h->current)) - { - h->current = NULL; - if ((NULL != c->cont) && - (GNUNET_YES != c->aborted)) - { - c->cont (c->cls, - GNUNET_SYSERR); - c->cont = NULL; - } - free_action_item (c); - } - if (NULL != h->mq) - { - GNUNET_MQ_destroy (h->mq); - h->mq = NULL; - } -} - - -/** - * Process a #GNUNET_MESSAGE_TYPE_STATISTICS_VALUE message. - * - * @param cls statistics handle - * @param smsg message received from the service, never NULL - * @return #GNUNET_OK if the message was well-formed - */ -static int -check_statistics_value (void *cls, - const struct GNUNET_STATISTICS_ReplyMessage *smsg) -{ - const char *service; - const char *name; - uint16_t size; - - size = ntohs (smsg->header.size); - size -= sizeof(struct GNUNET_STATISTICS_ReplyMessage); - if (size != - GNUNET_STRINGS_buffer_tokenize ((const char *) &smsg[1], - size, - 2, - &service, - &name)) - { - GNUNET_break (0); - return GNUNET_SYSERR; - } - return GNUNET_OK; -} - - -/** - * Process a #GNUNET_MESSAGE_TYPE_STATISTICS_VALUE message. - * - * @param cls statistics handle - * @param smsg message received from the service, never NULL - * @return #GNUNET_OK if the message was well-formed - */ -static void -handle_statistics_value (void *cls, - const struct GNUNET_STATISTICS_ReplyMessage *smsg) -{ - struct GNUNET_STATISTICS_Handle *h = cls; - const char *service; - const char *name; - uint16_t size; - - if (h->current->aborted) - return; /* iteration aborted, don't bother */ - - size = ntohs (smsg->header.size); - size -= sizeof(struct GNUNET_STATISTICS_ReplyMessage); - GNUNET_assert (size == - GNUNET_STRINGS_buffer_tokenize ((const char *) &smsg[1], - size, - 2, - &service, - &name)); - LOG (GNUNET_ERROR_TYPE_DEBUG, - "Received valid statistic on `%s:%s': %llu\n", - service, name, - (unsigned long long) GNUNET_ntohll (smsg->value)); - if (GNUNET_OK != - h->current->proc (h->current->cls, - service, - name, - GNUNET_ntohll (smsg->value), - (0 != - (ntohl (smsg->uid) & GNUNET_STATISTICS_PERSIST_BIT)) )) - { - LOG (GNUNET_ERROR_TYPE_DEBUG, - "Processing of remaining statistics aborted by client.\n"); - h->current->aborted = GNUNET_YES; - } -} - - -/** - * We have received a watch value from the service. Process it. - * - * @param cls statistics handle - * @param wvm the watch value message - */ -static void -handle_statistics_watch_value (void *cls, - const struct - GNUNET_STATISTICS_WatchValueMessage *wvm) -{ - struct GNUNET_STATISTICS_Handle *h = cls; - struct GNUNET_STATISTICS_WatchEntry *w; - uint32_t wid; - - GNUNET_break (0 == ntohl (wvm->reserved)); - wid = ntohl (wvm->wid); - if (wid >= h->watches_size) - { - do_disconnect (h); - reconnect_later (h); - return; - } - w = h->watches[wid]; - if (NULL == w) - return; - (void) w->proc (w->proc_cls, - w->subsystem, - w->name, - GNUNET_ntohll (wvm->value), - 0 != (ntohl (wvm->flags) & GNUNET_STATISTICS_PERSIST_BIT)); -} - - -/** - * Generic error handler, called with the appropriate error code and - * the same closure specified at the creation of the message queue. - * Not every message queue implementation supports an error handler. - * - * @param cls closure with the `struct GNUNET_STATISTICS_Handle *` - * @param error error code - */ -static void -mq_error_handler (void *cls, - enum GNUNET_MQ_Error error) -{ - struct GNUNET_STATISTICS_Handle *h = cls; - - if (GNUNET_NO != h->do_destroy) - { - h->do_destroy = GNUNET_NO; - if (NULL != h->destroy_task) - { - GNUNET_SCHEDULER_cancel (h->destroy_task); - h->destroy_task = NULL; - } - GNUNET_STATISTICS_destroy (h, - GNUNET_NO); - return; - } - do_disconnect (h); - reconnect_later (h); -} - - -/** - * Task used to destroy the statistics handle. - * - * @param cls the `struct GNUNET_STATISTICS_Handle` - */ -static void -do_destroy (void *cls) -{ - struct GNUNET_STATISTICS_Handle *h = cls; - - h->destroy_task = NULL; - h->do_destroy = GNUNET_NO; - LOG (GNUNET_ERROR_TYPE_DEBUG, - "Running final destruction\n"); - GNUNET_STATISTICS_destroy (h, - GNUNET_NO); -} - - -/** - * Handle a #GNUNET_MESSAGE_TYPE_STATISTICS_DISCONNECT_CONFIRM - * message. We receive this message at the end of the shutdown when - * the service confirms that all data has been written to disk. - * - * @param cls our `struct GNUNET_STATISTICS_Handle *` - * @param msg the message - */ -static void -handle_disconnect_confirm (void *cls, - const struct GNUNET_MessageHeader *msg) -{ - struct GNUNET_STATISTICS_Handle *h = cls; - - if (GNUNET_SYSERR != h->do_destroy) - { - /* not in shutdown, why do we get 'TEST'? */ - GNUNET_break (0); - do_disconnect (h); - reconnect_later (h); - return; - } - LOG (GNUNET_ERROR_TYPE_DEBUG, - "Received DISCONNNECT_CONFIRM message from statistics, can complete disconnect\n"); - if (NULL != h->destroy_task) - GNUNET_SCHEDULER_cancel (h->destroy_task); - h->destroy_task = GNUNET_SCHEDULER_add_now (&do_destroy, - h); -} - - -/** - * Handle a #GNUNET_MESSAGE_TYPE_STATISTICS_END message. We receive - * this message in response to a query to indicate that there are no - * further matching results. - * - * @param cls our `struct GNUNET_STATISTICS_Handle *` - * @param msg the message - */ -static void -handle_statistics_end (void *cls, - const struct GNUNET_MessageHeader *msg) -{ - struct GNUNET_STATISTICS_Handle *h = cls; - struct GNUNET_STATISTICS_GetHandle *c; - - LOG (GNUNET_ERROR_TYPE_DEBUG, - "Received end of statistics marker\n"); - if (NULL == (c = h->current)) - { - GNUNET_break (0); - do_disconnect (h); - reconnect_later (h); - return; - } - h->backoff = GNUNET_TIME_UNIT_MILLISECONDS; - h->current = NULL; - schedule_action (h); - if (NULL != c->cont) - { - c->cont (c->cls, - GNUNET_OK); - c->cont = NULL; - } - free_action_item (c); -} - - -/** - * Try to (re)connect to the statistics service. - * - * @param h statistics handle to reconnect - * @return #GNUNET_YES on success, #GNUNET_NO on failure. - */ -static int -try_connect (struct GNUNET_STATISTICS_Handle *h) -{ - struct GNUNET_MQ_MessageHandler handlers[] = { - GNUNET_MQ_hd_fixed_size (disconnect_confirm, - GNUNET_MESSAGE_TYPE_STATISTICS_DISCONNECT_CONFIRM, - struct GNUNET_MessageHeader, - h), - GNUNET_MQ_hd_fixed_size (statistics_end, - GNUNET_MESSAGE_TYPE_STATISTICS_END, - struct GNUNET_MessageHeader, - h), - GNUNET_MQ_hd_var_size (statistics_value, - GNUNET_MESSAGE_TYPE_STATISTICS_VALUE, - struct GNUNET_STATISTICS_ReplyMessage, - h), - GNUNET_MQ_hd_fixed_size (statistics_watch_value, - GNUNET_MESSAGE_TYPE_STATISTICS_WATCH_VALUE, - struct GNUNET_STATISTICS_WatchValueMessage, - h), - GNUNET_MQ_handler_end () - }; - struct GNUNET_STATISTICS_GetHandle *gh; - struct GNUNET_STATISTICS_GetHandle *gn; - - if (NULL != h->backoff_task) - return GNUNET_NO; - if (NULL != h->mq) - return GNUNET_YES; - h->mq = GNUNET_CLIENT_connect (h->cfg, - "statistics", - handlers, - &mq_error_handler, - h); - if (NULL == h->mq) - { - LOG (GNUNET_ERROR_TYPE_DEBUG, - "Failed to connect to statistics service!\n"); - return GNUNET_NO; - } - gn = h->action_head; - while (NULL != (gh = gn)) - { - gn = gh->next; - if (gh->type == ACTION_WATCH) - { - GNUNET_CONTAINER_DLL_remove (h->action_head, - h->action_tail, - gh); - free_action_item (gh); - } - } - for (unsigned int i = 0; i < h->watches_size; i++) - if (NULL != h->watches[i]) - schedule_watch_request (h, - h->watches[i]); - return GNUNET_YES; -} - - -/** - * We've waited long enough, reconnect now. - * - * @param cls the `struct GNUNET_STATISTICS_Handle` to reconnect - */ -static void -reconnect_task (void *cls) -{ - struct GNUNET_STATISTICS_Handle *h = cls; - - h->backoff_task = NULL; - schedule_action (h); -} - - -/** - * Reconnect at a later time, respecting back-off. - * - * @param h statistics handle - */ -static void -reconnect_later (struct GNUNET_STATISTICS_Handle *h) -{ - int loss; - struct GNUNET_STATISTICS_GetHandle *gh; - - GNUNET_assert (NULL == h->backoff_task); - if (GNUNET_YES == h->do_destroy) - { - /* So we are shutting down and the service is not reachable. - * Chances are that it's down for good and we are not going to connect to - * it anymore. - * Give up and don't sync the rest of the data. - */loss = GNUNET_NO; - for (gh = h->action_head; NULL != gh; gh = gh->next) - if ((gh->make_persistent) && - (ACTION_SET == gh->type)) - loss = GNUNET_YES; - if (GNUNET_YES == loss) - GNUNET_log (GNUNET_ERROR_TYPE_WARNING, - _ ("Could not save some persistent statistics\n")); - if (NULL != h->destroy_task) - GNUNET_SCHEDULER_cancel (h->destroy_task); - h->destroy_task = GNUNET_SCHEDULER_add_now (&do_destroy, - h); - return; - } - h->backoff_task - = GNUNET_SCHEDULER_add_delayed (h->backoff, - &reconnect_task, - h); - h->backoff = GNUNET_TIME_STD_BACKOFF (h->backoff); -} - - -/** - * Transmit a GET request (and if successful, start to receive - * the response). - * - * @param handle statistics handle - */ -static void -transmit_get (struct GNUNET_STATISTICS_Handle *handle) -{ - struct GNUNET_STATISTICS_GetHandle *c; - struct GNUNET_MessageHeader *hdr; - struct GNUNET_MQ_Envelope *env; - size_t slen1; - size_t slen2; - - GNUNET_assert (NULL != (c = handle->current)); - slen1 = strlen (c->subsystem) + 1; - slen2 = strlen (c->name) + 1; - env = GNUNET_MQ_msg_extra (hdr, - slen1 + slen2, - GNUNET_MESSAGE_TYPE_STATISTICS_GET); - GNUNET_assert (slen1 + slen2 == - GNUNET_STRINGS_buffer_fill ((char *) &hdr[1], - slen1 + slen2, - 2, - c->subsystem, - c->name)); - GNUNET_MQ_notify_sent (env, - &schedule_action, - handle); - GNUNET_MQ_send (handle->mq, - env); -} - - -/** - * Transmit a WATCH request (and if successful, start to receive - * the response). - * - * @param handle statistics handle - */ -static void -transmit_watch (struct GNUNET_STATISTICS_Handle *handle) -{ - struct GNUNET_MessageHeader *hdr; - struct GNUNET_MQ_Envelope *env; - size_t slen1; - size_t slen2; - - LOG (GNUNET_ERROR_TYPE_DEBUG, - "Transmitting watch request for `%s'\n", - handle->current->name); - slen1 = strlen (handle->current->subsystem) + 1; - slen2 = strlen (handle->current->name) + 1; - env = GNUNET_MQ_msg_extra (hdr, - slen1 + slen2, - GNUNET_MESSAGE_TYPE_STATISTICS_WATCH); - GNUNET_assert (slen1 + slen2 == - GNUNET_STRINGS_buffer_fill ((char *) &hdr[1], - slen1 + slen2, - 2, - handle->current->subsystem, - handle->current->name)); - GNUNET_MQ_notify_sent (env, - &schedule_action, - handle); - GNUNET_MQ_send (handle->mq, - env); - GNUNET_assert (NULL == handle->current->cont); - free_action_item (handle->current); - handle->current = NULL; - schedule_action (handle); -} - - -/** - * Transmit a SET/UPDATE request. - * - * @param handle statistics handle - */ -static void -transmit_set (struct GNUNET_STATISTICS_Handle *handle) -{ - struct GNUNET_STATISTICS_SetMessage *r; - struct GNUNET_MQ_Envelope *env; - size_t slen; - size_t nlen; - - slen = strlen (handle->current->subsystem) + 1; - nlen = strlen (handle->current->name) + 1; - env = GNUNET_MQ_msg_extra (r, - slen + nlen, - GNUNET_MESSAGE_TYPE_STATISTICS_SET); - r->flags = 0; - r->value = GNUNET_htonll (handle->current->value); - if (handle->current->make_persistent) - r->flags |= htonl (GNUNET_STATISTICS_SETFLAG_PERSISTENT); - if (handle->current->type == ACTION_UPDATE) - r->flags |= htonl (GNUNET_STATISTICS_SETFLAG_RELATIVE); - GNUNET_assert (slen + nlen == - GNUNET_STRINGS_buffer_fill ((char *) &r[1], - slen + nlen, - 2, - handle->current->subsystem, - handle->current->name)); - GNUNET_assert (NULL == handle->current->cont); - free_action_item (handle->current); - handle->current = NULL; - update_memory_statistics (handle); - GNUNET_MQ_notify_sent (env, - &schedule_action, - handle); - GNUNET_MQ_send (handle->mq, - env); -} - - -/** - * Get handle for the statistics service. - * - * @param subsystem name of subsystem using the service - * @param cfg services configuration in use - * @return handle to use - */ -struct GNUNET_STATISTICS_Handle * -GNUNET_STATISTICS_create (const char *subsystem, - const struct GNUNET_CONFIGURATION_Handle *cfg) -{ - struct GNUNET_STATISTICS_Handle *h; - - if (GNUNET_YES == - GNUNET_CONFIGURATION_get_value_yesno (cfg, - "statistics", - "DISABLE")) - return NULL; - h = GNUNET_new (struct GNUNET_STATISTICS_Handle); - h->cfg = cfg; - h->subsystem = GNUNET_strdup (subsystem); - h->backoff = GNUNET_TIME_UNIT_MILLISECONDS; - return h; -} - - -/** - * Destroy a handle (free all state associated with - * it). - * - * @param h statistics handle to destroy - * @param sync_first set to #GNUNET_YES if pending SET requests should - * be completed - */ -void -GNUNET_STATISTICS_destroy (struct GNUNET_STATISTICS_Handle *h, - int sync_first) -{ - struct GNUNET_STATISTICS_GetHandle *pos; - struct GNUNET_STATISTICS_GetHandle *next; - - if (NULL == h) - return; - GNUNET_assert (GNUNET_NO == h->do_destroy); /* Don't call twice. */ - if ((sync_first) && - (NULL != h->mq) && - (0 != GNUNET_MQ_get_length (h->mq))) - { - if ((NULL != h->current) && - (ACTION_GET == h->current->type)) - h->current->aborted = GNUNET_YES; - next = h->action_head; - while (NULL != (pos = next)) - { - next = pos->next; - if ((ACTION_GET == pos->type) || - (ACTION_WATCH == pos->type)) - { - GNUNET_CONTAINER_DLL_remove (h->action_head, - h->action_tail, - pos); - free_action_item (pos); - } - } - h->do_destroy = GNUNET_YES; - schedule_action (h); - GNUNET_assert (NULL == h->destroy_task); - h->destroy_task - = GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_relative_multiply (h->backoff, - 5), - &do_destroy, - h); - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "Deferring destruction\n"); - return; /* do not finish destruction just yet */ - } - /* do clean up all */ - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "Cleaning all up\n"); - while (NULL != (pos = h->action_head)) - { - GNUNET_CONTAINER_DLL_remove (h->action_head, - h->action_tail, - pos); - free_action_item (pos); - } - do_disconnect (h); - if (NULL != h->backoff_task) - { - GNUNET_SCHEDULER_cancel (h->backoff_task); - h->backoff_task = NULL; - } - if (NULL != h->destroy_task) - { - GNUNET_break (0); - GNUNET_SCHEDULER_cancel (h->destroy_task); - h->destroy_task = NULL; - } - for (unsigned int i = 0; i < h->watches_size; i++) - { - if (NULL == h->watches[i]) - continue; - GNUNET_free (h->watches[i]->subsystem); - GNUNET_free (h->watches[i]->name); - GNUNET_free (h->watches[i]); - } - GNUNET_array_grow (h->watches, - h->watches_size, - 0); - GNUNET_free (h->subsystem); - GNUNET_free (h); -} - - -/** - * Schedule the next action to be performed. - * - * @param cls statistics handle - */ -static void -schedule_action (void *cls) -{ - struct GNUNET_STATISTICS_Handle *h = cls; - - if (NULL != h->backoff_task) - return; /* action already pending */ - if (GNUNET_YES != try_connect (h)) - { - reconnect_later (h); - return; - } - if (0 < GNUNET_MQ_get_length (h->mq)) - return; /* Wait for queue to be reduced more */ - /* schedule next action */ - while (NULL == h->current) - { - h->current = h->action_head; - if (NULL == h->current) - { - struct GNUNET_MessageHeader *hdr; - struct GNUNET_MQ_Envelope *env; - - if (GNUNET_YES != h->do_destroy) - return; /* nothing to do */ - /* let service know that we're done */ - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "Notifying service that we are done\n"); - h->do_destroy = GNUNET_SYSERR; /* in 'TEST' mode */ - env = GNUNET_MQ_msg (hdr, - GNUNET_MESSAGE_TYPE_STATISTICS_DISCONNECT); - GNUNET_MQ_notify_sent (env, - &schedule_action, - h); - GNUNET_MQ_send (h->mq, - env); - return; - } - GNUNET_CONTAINER_DLL_remove (h->action_head, - h->action_tail, - h->current); - switch (h->current->type) - { - case ACTION_GET: - transmit_get (h); - break; - - case ACTION_SET: - case ACTION_UPDATE: - transmit_set (h); - break; - - case ACTION_WATCH: - transmit_watch (h); - break; - - default: - GNUNET_assert (0); - break; - } - } -} - - -struct GNUNET_STATISTICS_GetHandle * -GNUNET_STATISTICS_get (struct GNUNET_STATISTICS_Handle *handle, - const char *subsystem, - const char *name, - GNUNET_STATISTICS_Callback cont, - GNUNET_STATISTICS_Iterator proc, - void *cls) -{ - size_t slen1; - size_t slen2; - struct GNUNET_STATISTICS_GetHandle *ai; - - if (NULL == handle) - return NULL; - GNUNET_assert (NULL != proc); - GNUNET_assert (GNUNET_NO == handle->do_destroy); - if (NULL == subsystem) - subsystem = ""; - if (NULL == name) - name = ""; - slen1 = strlen (subsystem) + 1; - slen2 = strlen (name) + 1; - GNUNET_assert (slen1 + slen2 + sizeof(struct GNUNET_MessageHeader) < - GNUNET_MAX_MESSAGE_SIZE); - ai = GNUNET_new (struct GNUNET_STATISTICS_GetHandle); - ai->sh = handle; - ai->subsystem = GNUNET_strdup (subsystem); - ai->name = GNUNET_strdup (name); - ai->cont = cont; - ai->proc = proc; - ai->cls = cls; - ai->type = ACTION_GET; - ai->msize = slen1 + slen2 + sizeof(struct GNUNET_MessageHeader); - GNUNET_CONTAINER_DLL_insert_tail (handle->action_head, - handle->action_tail, - ai); - schedule_action (handle); - return ai; -} - - -void -GNUNET_STATISTICS_get_cancel (struct GNUNET_STATISTICS_GetHandle *gh) -{ - if (NULL == gh) - return; - gh->cont = NULL; - if (gh->sh->current == gh) - { - gh->aborted = GNUNET_YES; - return; - } - GNUNET_CONTAINER_DLL_remove (gh->sh->action_head, - gh->sh->action_tail, - gh); - GNUNET_free (gh->name); - GNUNET_free (gh->subsystem); - GNUNET_free (gh); -} - - -/** - * Watch statistics from the peer (be notified whenever they change). - * - * @param handle identification of the statistics service - * @param subsystem limit to the specified subsystem, never NULL - * @param name name of the statistic value, never NULL - * @param proc function to call on each value - * @param proc_cls closure for @a proc - * @return #GNUNET_OK on success, #GNUNET_SYSERR on error - */ -int -GNUNET_STATISTICS_watch (struct GNUNET_STATISTICS_Handle *handle, - const char *subsystem, - const char *name, - GNUNET_STATISTICS_Iterator proc, - void *proc_cls) -{ - struct GNUNET_STATISTICS_WatchEntry *w; - - if (NULL == handle) - return GNUNET_SYSERR; - w = GNUNET_new (struct GNUNET_STATISTICS_WatchEntry); - w->subsystem = GNUNET_strdup (subsystem); - w->name = GNUNET_strdup (name); - w->proc = proc; - w->proc_cls = proc_cls; - GNUNET_array_append (handle->watches, - handle->watches_size, - w); - schedule_watch_request (handle, - w); - return GNUNET_OK; -} - - -/** - * Stop watching statistics from the peer. - * - * @param handle identification of the statistics service - * @param subsystem limit to the specified subsystem, never NULL - * @param name name of the statistic value, never NULL - * @param proc function to call on each value - * @param proc_cls closure for @a proc - * @return #GNUNET_OK on success, #GNUNET_SYSERR on error (no such watch) - */ -int -GNUNET_STATISTICS_watch_cancel (struct GNUNET_STATISTICS_Handle *handle, - const char *subsystem, - const char *name, - GNUNET_STATISTICS_Iterator proc, - void *proc_cls) -{ - struct GNUNET_STATISTICS_WatchEntry *w; - - if (NULL == handle) - return GNUNET_SYSERR; - for (unsigned int i = 0; i < handle->watches_size; i++) - { - w = handle->watches[i]; - if (NULL == w) - continue; - if ((w->proc == proc) && - (w->proc_cls == proc_cls) && - (0 == strcmp (w->name, - name)) && - (0 == strcmp (w->subsystem, - subsystem))) - { - GNUNET_free (w->name); - GNUNET_free (w->subsystem); - GNUNET_free (w); - handle->watches[i] = NULL; - return GNUNET_OK; - } - } - return GNUNET_SYSERR; -} - - -/** - * Queue a request to change a statistic. - * - * @param h statistics handle - * @param name name of the value - * @param make_persistent should the value be kept across restarts? - * @param value new value or change - * @param type type of the action (#ACTION_SET or #ACTION_UPDATE) - */ -static void -add_setter_action (struct GNUNET_STATISTICS_Handle *h, - const char *name, - int make_persistent, - uint64_t value, - enum ActionType type) -{ - struct GNUNET_STATISTICS_GetHandle *ai; - size_t slen; - size_t nlen; - size_t nsize; - int64_t delta; - - slen = strlen (h->subsystem) + 1; - nlen = strlen (name) + 1; - nsize = sizeof(struct GNUNET_STATISTICS_SetMessage) + slen + nlen; - if (nsize >= GNUNET_MAX_MESSAGE_SIZE) - { - GNUNET_break (0); - return; - } - for (ai = h->action_head; NULL != ai; ai = ai->next) - { - if (! ((0 == strcmp (ai->subsystem, - h->subsystem)) && - (0 == strcmp (ai->name, - name)) && - ((ACTION_UPDATE == ai->type) || - (ACTION_SET == ai->type)))) - continue; - if (ACTION_SET == ai->type) - { - if (ACTION_UPDATE == type) - { - delta = (int64_t) value; - if (delta > 0) - { - /* update old set by new delta */ - ai->value += delta; - } - else - { - /* update old set by new delta, but never go negative */ - if (ai->value < -delta) - ai->value = 0; - else - ai->value += delta; - } - } - else - { - /* new set overrides old set */ - ai->value = value; - } - } - else - { - if (ACTION_UPDATE == type) - { - /* make delta cumulative */ - delta = (int64_t) value; - ai->value += delta; - } - else - { - /* drop old 'update', use new 'set' instead */ - ai->value = value; - ai->type = type; - } - } - ai->timeout - = GNUNET_TIME_relative_to_absolute (SET_TRANSMIT_TIMEOUT); - ai->make_persistent - = make_persistent; - return; - } - /* no existing entry matches, create a fresh one */ - ai = GNUNET_new (struct GNUNET_STATISTICS_GetHandle); - ai->sh = h; - ai->subsystem = GNUNET_strdup (h->subsystem); - ai->name = GNUNET_strdup (name); - ai->timeout = GNUNET_TIME_relative_to_absolute (SET_TRANSMIT_TIMEOUT); - ai->make_persistent = make_persistent; - ai->msize = nsize; - ai->value = value; - ai->type = type; - GNUNET_CONTAINER_DLL_insert_tail (h->action_head, - h->action_tail, - ai); - schedule_action (h); -} - - -void -GNUNET_STATISTICS_set (struct GNUNET_STATISTICS_Handle *handle, - const char *name, - uint64_t value, - int make_persistent) -{ - if (NULL == handle) - return; - GNUNET_assert (GNUNET_NO == handle->do_destroy); - add_setter_action (handle, - name, - make_persistent, - value, - ACTION_SET); -} - - -void -GNUNET_STATISTICS_update (struct GNUNET_STATISTICS_Handle *handle, - const char *name, - int64_t delta, - int make_persistent) -{ - if (NULL == handle) - return; - if (0 == delta) - return; - GNUNET_assert (GNUNET_NO == handle->do_destroy); - add_setter_action (handle, - name, - make_persistent, - (uint64_t) delta, - ACTION_UPDATE); -} - - -/* end of statistics_api.c */ diff --git a/src/statistics/test_gnunet_statistics.py.in b/src/statistics/test_gnunet_statistics.py.in deleted file mode 100644 index bf6ba6ef2..000000000 --- a/src/statistics/test_gnunet_statistics.py.in +++ /dev/null @@ -1,171 +0,0 @@ -#!@PYTHONEXE@ - -import os -import sys -import shutil -import re -import subprocess -import time - -raw_tmp = True -if os.name == "nt": - tmp = os.getenv("TEMP") -elif None != os.environ.get("TMPDIR"): - tmp = os.getenv("TMPDIR") -elif None != os.environ.get("TMP"): - tmp = os.getenv("TMP") -else: - raw_tmp = False - tmp = subprocess.check_output(["gnunet-config", "-f", "-s", "PATHS", "-o", "GNUNET_TMP"], - text=True) - -if os.name == 'nt': - st = './gnunet-statistics.exe' - arm = 'gnunet-arm.exe' -else: - st = './gnunet-statistics' - arm = 'gnunet-arm' - -run_st = [st, '-c', 'test_statistics_api_data.conf'] -run_arm = [arm, '-c', 'test_statistics_api_data.conf'] -debug = os.getenv('DEBUG') -if debug: - run_arm += [debug.split(' ')] - - -if raw_tmp: - cleanup_path = "gnunet/test-gnunet-statistics" -else: - cleanup_path = "test-gnunet-statistics" - - -def cleanup(): - shutil.rmtree(os.path.join(tmp, cleanup_path), True) - - -def sub_run(args, want_stdo=True, want_stde=False, nofail=False): - if want_stdo: - stdo = subprocess.PIPE - else: - stdo = None - if want_stde: - stde = subprocess.PIPE - else: - stde = None - p = subprocess.Popen(args, stdout=stdo, stderr=stde) - stdo, stde = p.communicate() - if not nofail: - if p.returncode != 0: - sys.exit(p.returncode) - return (p.returncode, stdo, stde) - - -def fail(result): - print(result) - r_arm(['-e'], want_stdo=False) - sys.exit(1) - - -def r_arm(extra_args, **kw): - rc, stdo, stde = sub_run(run_arm + extra_args, **kw) - if rc != 0: - fail("FAIL: error running {}".format(run_arm)) - return (rc, stdo, stde) - - -def r_st(extra_args, normal=True, **kw): - rc, stdo, stde = sub_run(run_st + extra_args, **kw) - if normal: - if rc != 0: - fail("FAIL: error running {}".format(run_st)) - else: - if rc == 0: - fail("FAIL: expected error while running {}".format(run_st)) - return (rc, stdo, stde) - - -def restart(): - print("Restarting service...") - t = r_arm(['-k', 'statistics']) - time.sleep(1) - t = r_arm(['-i', 'statistics']) - time.sleep(1) - - -cleanup() - -print("Preparing: Starting service...") -t = r_arm(['-s'], want_stdo=False) -time.sleep(1) -t = r_arm(['-i', 'statistics'], want_stdo=False) -time.sleep(1) - -print("TEST: Bad argument checking...", end='') -r_st(['-x'], normal=False, nofail=True, want_stdo=False, want_stde=True) -print("PASS") - -print("TEST: Set value...", end='') -r_st(['-n', 'test', '-s', 'subsystem', b'42'], nofail=True, want_stdo=False) -print("PASS") - -print("TEST: Set another value...", end='') -r_st(['-n', 'other', '-s', 'osystem', b'43'], nofail=True, want_stdo=False) -print("PASS") - -#print("TEST: Viewing all stats...", end='') -#rc, stdo, stde = r_st([], nofail=True, want_stdo=True) -#if len(stdo.splitlines()) != 2: -# fail("FAIL: unexpected output:\n{}".format(stdo)) -#print("PASS") - -print("TEST: Viewing stats by name...", end='') -rc, stdo, stde = r_st(['-n', 'other'], nofail=True, want_stdo=True) -if len([x for x in stdo.splitlines() if re.search(b'43', x)]) != 1: - fail("FAIL: unexpected output:\n{}".format(stdo)) -print("PASS") - -print("TEST: Viewing stats by subsystem...", end='') -rc, stdo, stde = r_st(['-s', 'subsystem'], nofail=True, want_stdo=True) -if len([x for x in stdo.splitlines() if re.search(b'42', x)]) != 1: - fail("FAIL: unexpected output:\n{}".format(stdo)) -print("PASS") - -print("TEST: Set persistent value...", end='') -rc, stdo, stde = r_st(['-n', 'lasting', '-s', 'subsystem', '40', '-p'], - nofail=True, - want_stdo=False) -rc, stdo, stde = r_st([], nofail=True, want_stdo=True) -if len([x for x in stdo.splitlines() if re.search(b'40', x)]) != 1: - fail("FAIL: unexpected output:\n{}".format(stdo)) -print("PASS") - -restart() - -print("TEST: Checking persistence...", end='') -rc, stdo, stde = r_st([], nofail=True, want_stdo=True) -if len([x for x in stdo.splitlines() if re.search(b'40', x)]) != 1: - fail("FAIL: unexpected output:\n{}".format(stdo)) -print("PASS") - -print("TEST: Removing persistence...", end='') -rc, stdo, stde = r_st(['-n', 'lasting', '-s', 'subsystem', '40'], - nofail=True, - want_stdo=False) -rc, stdo, stde = r_st([], nofail=True, want_stdo=True) -if len([x for x in stdo.splitlines() if re.search(b'!', x)]) != 0: - fail("FAIL: unexpected output:\n{}".format(stdo)) -print("PASS") - -restart() - -print("TEST: Checking removed persistence...", end='') -rc, stdo, stde = r_st([], nofail=True, want_stdo=True) -if len([x for x in stdo.splitlines() if re.search(b'40', x)]) != 0: - fail("FAIL: unexpected output:\n{}".format(stdo)) -print("PASS") - -print("Stopping service...") -t = r_arm(['-e'], want_stdo=False) -time.sleep(1) - -cleanup() diff --git a/src/statistics/test_statistics_api.c b/src/statistics/test_statistics_api.c deleted file mode 100644 index c9e568870..000000000 --- a/src/statistics/test_statistics_api.c +++ /dev/null @@ -1,253 +0,0 @@ -/* - This file is part of GNUnet. - Copyright (C) 2009, 2012, 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 . - - SPDX-License-Identifier: AGPL3.0-or-later - */ -/** - * @file statistics/test_statistics_api.c - * @brief testcase for statistics_api.c - * @author Christian Grothoff - */ -#include "platform.h" -#include "gnunet_util_lib.h" -#include "gnunet_statistics_service.h" - - -static struct GNUNET_STATISTICS_Handle *h; - -static struct GNUNET_STATISTICS_GetHandle *g; - - -static void -do_shutdown () -{ - if (NULL != g) - { - GNUNET_STATISTICS_get_cancel (g); - g = NULL; - } - GNUNET_STATISTICS_destroy (h, GNUNET_NO); - h = NULL; -} - - -static int -check_1 (void *cls, - const char *subsystem, - const char *name, - uint64_t value, - int is_persistent) -{ - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "Received value %llu for `%s:%s\n", - (unsigned long long) value, - subsystem, - name); - GNUNET_assert (0 == strcmp (name, "test-1")); - GNUNET_assert (0 == strcmp (subsystem, "test-statistics-api")); - GNUNET_assert (value == 1); - GNUNET_assert (is_persistent == GNUNET_NO); - return GNUNET_OK; -} - - -static int -check_2 (void *cls, - const char *subsystem, - const char *name, - uint64_t value, - int is_persistent) -{ - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "Received value %llu for `%s:%s\n", - (unsigned long long) value, - subsystem, - name); - GNUNET_assert (0 == strcmp (name, "test-2")); - GNUNET_assert (0 == strcmp (subsystem, "test-statistics-api")); - GNUNET_assert (value == 2); - GNUNET_assert (is_persistent == GNUNET_NO); - return GNUNET_OK; -} - - -static int -check_3 (void *cls, - const char *subsystem, - const char *name, - uint64_t value, - int is_persistent) -{ - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "Received value %llu for `%s:%s\n", - (unsigned long long) value, - subsystem, - name); - GNUNET_assert (0 == strcmp (name, "test-3")); - GNUNET_assert (0 == strcmp (subsystem, "test-statistics-api")); - GNUNET_assert (value == 3); - GNUNET_assert (is_persistent == GNUNET_YES); - return GNUNET_OK; -} - - -static void -next_fin (void *cls, - int success) -{ - int *ok = cls; - - g = NULL; - GNUNET_SCHEDULER_shutdown (); - GNUNET_assert (success == GNUNET_OK); - *ok = 0; -} - - -static void -next (void *cls, int success) -{ - g = NULL; - GNUNET_assert (success == GNUNET_OK); - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "Issuing GET request\n"); - GNUNET_break (NULL != - GNUNET_STATISTICS_get (h, NULL, "test-2", - &next_fin, - &check_2, cls)); -} - - -static void -run (void *cls, - char *const *args, - const char *cfgfile, - const struct GNUNET_CONFIGURATION_Handle *cfg) -{ - h = GNUNET_STATISTICS_create ("test-statistics-api", cfg); - if (NULL == h) - { - GNUNET_break (0); - return; - } - GNUNET_SCHEDULER_add_shutdown (&do_shutdown, - NULL); - GNUNET_STATISTICS_set (h, "test-1", 1, GNUNET_NO); - GNUNET_STATISTICS_set (h, "test-2", 2, GNUNET_NO); - GNUNET_STATISTICS_set (h, "test-3", 2, GNUNET_NO); - GNUNET_STATISTICS_update (h, "test-3", 1, GNUNET_YES); - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "Issuing GET request\n"); - GNUNET_break (NULL != - (g = GNUNET_STATISTICS_get (h, NULL, "test-1", - &next, - &check_1, cls))); -} - - -static void -run_more (void *cls, - char *const *args, - const char *cfgfile, - const struct GNUNET_CONFIGURATION_Handle *cfg) -{ - h = GNUNET_STATISTICS_create ("test-statistics-api", - cfg); - GNUNET_SCHEDULER_add_shutdown (&do_shutdown, - NULL); - GNUNET_break (NULL != - (g = GNUNET_STATISTICS_get (h, NULL, - "test-3", - &next_fin, - &check_3, cls))); -} - - -int -main (int argc, char *argv_ign[]) -{ - int ok = 1; - char *const argv[] = { "test-statistics-api", - "-c", - "test_statistics_api_data.conf", - "-L", "WARNING", - NULL }; - struct GNUNET_GETOPT_CommandLineOption options[] = { - GNUNET_GETOPT_OPTION_END - }; - struct GNUNET_OS_Process *proc; - char *binary; - - GNUNET_log_setup ("test_statistics_api", - "WARNING", - NULL); - binary = GNUNET_OS_get_libexec_binary_path ("gnunet-service-statistics"); - proc = - GNUNET_OS_start_process (GNUNET_OS_INHERIT_STD_OUT_AND_ERR - | GNUNET_OS_USE_PIPE_CONTROL, - NULL, NULL, NULL, - binary, - "gnunet-service-statistics", - "-c", "test_statistics_api_data.conf", NULL); - GNUNET_assert (NULL != proc); - GNUNET_PROGRAM_run (5, argv, - "test-statistics-api", "nohelp", - options, &run, - &ok); - if (0 != GNUNET_OS_process_kill (proc, - GNUNET_TERM_SIG)) - { - GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING, "kill"); - ok = 1; - } - GNUNET_OS_process_wait (proc); - GNUNET_OS_process_destroy (proc); - proc = NULL; - if (ok != 0) - { - GNUNET_free (binary); - return ok; - } - ok = 1; - /* restart to check persistence! */ - proc = - GNUNET_OS_start_process (GNUNET_OS_INHERIT_STD_OUT_AND_ERR - | GNUNET_OS_USE_PIPE_CONTROL, - NULL, NULL, NULL, - binary, - "gnunet-service-statistics", - "-c", "test_statistics_api_data.conf", - NULL); - GNUNET_PROGRAM_run (5, argv, - "test-statistics-api", "nohelp", - options, - &run_more, &ok); - if (0 != GNUNET_OS_process_kill (proc, - GNUNET_TERM_SIG)) - { - GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING, "kill"); - ok = 1; - } - GNUNET_OS_process_wait (proc); - GNUNET_OS_process_destroy (proc); - proc = NULL; - GNUNET_free (binary); - return ok; -} - - -/* end of test_statistics_api.c */ diff --git a/src/statistics/test_statistics_api_data.conf b/src/statistics/test_statistics_api_data.conf deleted file mode 100644 index d437c2aa8..000000000 --- a/src/statistics/test_statistics_api_data.conf +++ /dev/null @@ -1,5 +0,0 @@ -@INLINE@ ../../contrib/conf/gnunet/no_forcestart.conf -@INLINE@ ../../contrib/conf/gnunet/no_autostart_above_core.conf - -[PATHS] -GNUNET_TEST_HOME = $GNUNET_TMP/test-gnunet-statistics/ diff --git a/src/statistics/test_statistics_api_loop.c b/src/statistics/test_statistics_api_loop.c deleted file mode 100644 index ad273287d..000000000 --- a/src/statistics/test_statistics_api_loop.c +++ /dev/null @@ -1,123 +0,0 @@ -/* - This file is part of GNUnet. - Copyright (C) 2009 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 . - - SPDX-License-Identifier: AGPL3.0-or-later - */ -/** - * @file statistics/test_statistics_api_loop.c - * @brief testcase for statistics_api.c - */ -#include "platform.h" -#include "gnunet_util_lib.h" -#include "gnunet_statistics_service.h" - -#define ROUNDS (1024 * 1024) - -static struct GNUNET_STATISTICS_Handle *h; - - -static int -check_1 (void *cls, - const char *subsystem, - const char *name, - uint64_t value, - int is_persistent) -{ - GNUNET_assert (0 == strcmp (name, "test-0")); - GNUNET_assert (0 == strcmp (subsystem, "test-statistics-api-loop")); - GNUNET_assert (is_persistent == GNUNET_NO); - return GNUNET_OK; -} - - -static void -next (void *cls, - int success) -{ - int *ok = cls; - - GNUNET_STATISTICS_destroy (h, GNUNET_NO); - GNUNET_assert (success == GNUNET_OK); - *ok = 0; -} - - -static void -run (void *cls, - char *const *args, - const char *cfgfile, - const struct GNUNET_CONFIGURATION_Handle *cfg) -{ - unsigned int i; - char name[128]; - - h = GNUNET_STATISTICS_create ("test-statistics-api-loop", cfg); - for (i = 0; i < ROUNDS; i++) - { - GNUNET_snprintf (name, sizeof(name), "test-%d", i % 32); - GNUNET_STATISTICS_set (h, name, i, GNUNET_NO); - GNUNET_snprintf (name, sizeof(name), "test-%d", i % 16); - GNUNET_STATISTICS_update (h, name, 1, GNUNET_NO); - } - i = 0; - GNUNET_break (NULL != - GNUNET_STATISTICS_get (h, NULL, "test-0", - &next, - &check_1, cls)); -} - - -int -main (int argc, char *argv_ign[]) -{ - int ok = 1; - - char *const argv[] = { "test-statistics-api", - "-c", - "test_statistics_api_data.conf", - NULL }; - struct GNUNET_GETOPT_CommandLineOption options[] = { - GNUNET_GETOPT_OPTION_END - }; - struct GNUNET_OS_Process *proc; - char *binary; - - binary = GNUNET_OS_get_libexec_binary_path ("gnunet-service-statistics"); - proc = - GNUNET_OS_start_process (GNUNET_OS_INHERIT_STD_OUT_AND_ERR - | GNUNET_OS_USE_PIPE_CONTROL, - NULL, NULL, NULL, - binary, - "gnunet-service-statistics", - "-c", "test_statistics_api_data.conf", NULL); - GNUNET_assert (NULL != proc); - GNUNET_PROGRAM_run (3, argv, "test-statistics-api", "nohelp", options, &run, - &ok); - if (0 != GNUNET_OS_process_kill (proc, GNUNET_TERM_SIG)) - { - GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING, "kill"); - ok = 1; - } - GNUNET_OS_process_wait (proc); - GNUNET_OS_process_destroy (proc); - proc = NULL; - GNUNET_free (binary); - return ok; -} - - -/* end of test_statistics_api_loop.c */ diff --git a/src/statistics/test_statistics_api_watch.c b/src/statistics/test_statistics_api_watch.c deleted file mode 100644 index 2d9d08305..000000000 --- a/src/statistics/test_statistics_api_watch.c +++ /dev/null @@ -1,156 +0,0 @@ -/* - This file is part of GNUnet. - Copyright (C) 2009, 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 . - - SPDX-License-Identifier: AGPL3.0-or-later - */ -/** - * @file statistics/test_statistics_api_watch.c - * @brief testcase for statistics_api.c watch functions - * @author Christian Grothoff - */ -#include "platform.h" -#include "gnunet_util_lib.h" -#include "gnunet_statistics_service.h" - - -static int ok; - -static struct GNUNET_STATISTICS_Handle *h; - -static struct GNUNET_STATISTICS_Handle *h2; - -static struct GNUNET_SCHEDULER_Task *shutdown_task; - - -static void -force_shutdown (void *cls) -{ - fprintf (stderr, "Timeout, failed to receive notifications: %d\n", ok); - GNUNET_STATISTICS_destroy (h, GNUNET_NO); - GNUNET_STATISTICS_destroy (h2, GNUNET_NO); - ok = 7; -} - - -static void -normal_shutdown (void *cls) -{ - GNUNET_STATISTICS_destroy (h, GNUNET_NO); - GNUNET_STATISTICS_destroy (h2, GNUNET_NO); -} - - -static int -watch_1 (void *cls, - const char *subsystem, - const char *name, - uint64_t value, - int is_persistent) -{ - GNUNET_assert (value == 42); - GNUNET_assert (0 == strcmp (name, "test-1")); - ok &= ~1; - if (0 == ok) - { - GNUNET_SCHEDULER_cancel (shutdown_task); - GNUNET_SCHEDULER_add_now (&normal_shutdown, NULL); - } - return GNUNET_OK; -} - - -static int -watch_2 (void *cls, - const char *subsystem, - const char *name, - uint64_t value, - int is_persistent) -{ - GNUNET_assert (value == 43); - GNUNET_assert (0 == strcmp (name, "test-2")); - ok &= ~2; - if (0 == ok) - { - GNUNET_SCHEDULER_cancel (shutdown_task); - GNUNET_SCHEDULER_add_now (&normal_shutdown, NULL); - } - return GNUNET_OK; -} - - -static void -run (void *cls, - char *const *args, - const char *cfgfile, - const struct GNUNET_CONFIGURATION_Handle *cfg) -{ - h = GNUNET_STATISTICS_create ("dummy", cfg); - GNUNET_assert (GNUNET_OK == - GNUNET_STATISTICS_watch (h, "test-statistics-api-watch", - "test-1", &watch_1, NULL)); - GNUNET_assert (GNUNET_OK == - GNUNET_STATISTICS_watch (h, "test-statistics-api-watch", - "test-2", &watch_2, NULL)); - h2 = GNUNET_STATISTICS_create ("test-statistics-api-watch", cfg); - GNUNET_STATISTICS_set (h2, "test-1", 42, GNUNET_NO); - GNUNET_STATISTICS_set (h2, "test-2", 43, GNUNET_NO); - shutdown_task = - GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_UNIT_MINUTES, - &force_shutdown, - NULL); -} - - -int -main (int argc, char *argv_ign[]) -{ - char *const argv[] = { "test-statistics-api", - "-c", - "test_statistics_api_data.conf", - NULL }; - struct GNUNET_GETOPT_CommandLineOption options[] = { - GNUNET_GETOPT_OPTION_END - }; - struct GNUNET_OS_Process *proc; - char *binary; - - binary = GNUNET_OS_get_libexec_binary_path ("gnunet-service-statistics"); - proc = - GNUNET_OS_start_process (GNUNET_OS_INHERIT_STD_OUT_AND_ERR - | GNUNET_OS_USE_PIPE_CONTROL, - NULL, NULL, NULL, - binary, - "gnunet-service-statistics", - "-c", "test_statistics_api_data.conf", NULL); - GNUNET_assert (NULL != proc); - ok = 3; - GNUNET_PROGRAM_run (3, argv, "test-statistics-api", "nohelp", options, &run, - NULL); - if (0 != GNUNET_OS_process_kill (proc, GNUNET_TERM_SIG)) - { - GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING, "kill"); - ok = 1; - } - GNUNET_OS_process_wait (proc); - GNUNET_OS_process_destroy (proc); - proc = NULL; - GNUNET_free (binary); - return ok; -} - - -/* end of test_statistics_api_watch.c */ diff --git a/src/statistics/test_statistics_api_watch_zero_value.c b/src/statistics/test_statistics_api_watch_zero_value.c deleted file mode 100644 index cb2694f8f..000000000 --- a/src/statistics/test_statistics_api_watch_zero_value.c +++ /dev/null @@ -1,197 +0,0 @@ -/* - This file is part of GNUnet. - Copyright (C) 2009, 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 . - - SPDX-License-Identifier: AGPL3.0-or-later - */ -/** - * @file statistics/test_statistics_api_watch_zero_value.c - * @brief testcase for statistics_api.c watch functions with initial 0 value - */ -#include "platform.h" -#include "gnunet_util_lib.h" -#include "gnunet_statistics_service.h" - -static int ok; - -static int ok2; - -static struct GNUNET_STATISTICS_Handle *h; - -static struct GNUNET_STATISTICS_Handle *h2; - -static struct GNUNET_SCHEDULER_Task *shutdown_task; - - -static void -force_shutdown (void *cls) -{ - fprintf (stderr, "Timeout, failed to receive notifications: %d\n", ok); - GNUNET_STATISTICS_destroy (h, GNUNET_NO); - GNUNET_STATISTICS_destroy (h2, GNUNET_NO); - ok = 7; -} - - -static void -normal_shutdown (void *cls) -{ - GNUNET_STATISTICS_destroy (h, GNUNET_NO); - GNUNET_STATISTICS_destroy (h2, GNUNET_NO); -} - - -static int -watch_1 (void *cls, const char *subsystem, const char *name, uint64_t value, - int is_persistent) -{ - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "Received value `%s' `%s' %llu\n", - subsystem, - name, - (unsigned long long) value); - GNUNET_assert (0 == strcmp (name, "test-1")); - if ((0 == value) && (3 == ok)) - { - ok--; - GNUNET_STATISTICS_set (h, "test-1", 42, GNUNET_NO); - } - - if ((42 == value) && (2 == ok)) - { - ok--; - GNUNET_STATISTICS_set (h, "test-1", 0, GNUNET_NO); - } - - if ((0 == value) && (1 == ok)) - { - ok--; - } - if ((0 == ok) && (0 == ok2)) - { - GNUNET_SCHEDULER_cancel (shutdown_task); - GNUNET_SCHEDULER_add_now (&normal_shutdown, NULL); - } - - return GNUNET_OK; -} - - -static int -watch_2 (void *cls, - const char *subsystem, - const char *name, - uint64_t value, - int is_persistent) -{ - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "Received value `%s' `%s' %llu\n", - subsystem, - name, - (unsigned long long) value); - - GNUNET_assert (0 == strcmp (name, "test-2")); - if ((42 == value) && (1 == ok2)) - { - ok2 = 0; - if (0 == ok) - { - GNUNET_SCHEDULER_cancel (shutdown_task); - GNUNET_SCHEDULER_add_now (&normal_shutdown, NULL); - } - } - else - { - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - "Received unexpected value %llu\n", - (unsigned long long) value); - - GNUNET_break (0); - GNUNET_SCHEDULER_cancel (shutdown_task); - GNUNET_SCHEDULER_add_now (&normal_shutdown, NULL); - } - - return GNUNET_OK; -} - - -static void -run (void *cls, char *const *args, const char *cfgfile, - const struct GNUNET_CONFIGURATION_Handle *cfg) -{ - h = GNUNET_STATISTICS_create ("dummy", cfg); - h2 = GNUNET_STATISTICS_create ("dummy-2", cfg); - GNUNET_assert (GNUNET_OK == - GNUNET_STATISTICS_watch (h, "dummy", - "test-1", &watch_1, NULL)); - - GNUNET_assert (GNUNET_OK == - GNUNET_STATISTICS_watch (h2, "dummy-2", - "test-2", &watch_2, NULL)); - - /* Set initial value to 0 */ - GNUNET_STATISTICS_set (h, "test-1", 0, GNUNET_NO); - GNUNET_STATISTICS_set (h2, "test-2", 42, GNUNET_NO); - - shutdown_task = - GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_UNIT_MINUTES, - &force_shutdown, - NULL); -} - - -int -main (int argc, char *argv_ign[]) -{ - char *const argv[] = { "test-statistics-api", - "-c", - "test_statistics_api_data.conf", - NULL }; - struct GNUNET_GETOPT_CommandLineOption options[] = { - GNUNET_GETOPT_OPTION_END - }; - struct GNUNET_OS_Process *proc; - char *binary; - - binary = GNUNET_OS_get_libexec_binary_path ("gnunet-service-statistics"); - proc = - GNUNET_OS_start_process (GNUNET_OS_INHERIT_STD_OUT_AND_ERR - | GNUNET_OS_USE_PIPE_CONTROL, - NULL, NULL, NULL, - binary, - "gnunet-service-statistics", - "-c", "test_statistics_api_data.conf", NULL); - GNUNET_assert (NULL != proc); - ok = 3; - ok2 = 1; - GNUNET_PROGRAM_run (3, argv, "test-statistics-api", "nohelp", options, &run, - NULL); - if (0 != GNUNET_OS_process_kill (proc, GNUNET_TERM_SIG)) - { - GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING, "kill"); - ok = 1; - } - GNUNET_OS_process_wait (proc); - GNUNET_OS_process_destroy (proc); - proc = NULL; - GNUNET_free (binary); - if ((0 == ok) && (0 == ok2)) - return 0; - return 1; -} - - -/* end of test_statistics_api_watch_zero_value.c */ diff --git a/src/topology/Makefile.am b/src/topology/Makefile.am index 620d51d9a..7f5ad1893 100644 --- a/src/topology/Makefile.am +++ b/src/topology/Makefile.am @@ -20,9 +20,9 @@ gnunet_daemon_topology_SOURCES = \ gnunet-daemon-topology.c gnunet_daemon_topology_LDADD = \ $(top_builddir)/src/service/core/libgnunetcore.la \ - $(top_builddir)/src/peerstore/libgnunetpeerstore.la \ - $(top_builddir)/src/statistics/libgnunetstatistics.la \ - $(top_builddir)/src/transport/libgnunettransportapplication.la \ + $(top_builddir)/src/service/peerstore/libgnunetpeerstore.la \ + $(top_builddir)/src/service/statistics/libgnunetstatistics.la \ + $(top_builddir)/src/service/transport/libgnunettransportapplication.la \ $(top_builddir)/src/lib/hello/libgnunethello.la \ $(top_builddir)/src/lib/util/libgnunetutil.la \ $(GN_LIBINTL) @@ -40,7 +40,7 @@ test_gnunet_daemon_topology_SOURCES = \ test_gnunet_daemon_topology.c test_gnunet_daemon_topology_LDADD = \ $(top_builddir)/src/testbed/libgnunettestbed.la \ - $(top_builddir)/src/statistics/libgnunetstatistics.la \ + $(top_builddir)/src/service/statistics/libgnunetstatistics.la \ $(top_builddir)/src/lib/util/libgnunetutil.la EXTRA_DIST = \ diff --git a/src/transport/.gitignore b/src/transport/.gitignore deleted file mode 100644 index 163ffbd5d..000000000 --- a/src/transport/.gitignore +++ /dev/null @@ -1,94 +0,0 @@ -gnunet-transport-wlan-sender -gnunet-helper-transport-bluetooth -gnunet-helper-transport-wlan -gnunet-helper-transport-wlan-dummy -gnunet-service-transport -gnunet-transport -gnunet-transport-certificate-creation -gnunet-transport-profiler -gnunet-transport-wlan-receiver -https_cert_qutoa_p2.crt -https_key_quota_p2.key -test_http_common -test_plugin_bluetooth -test_plugin_http_client -test_plugin_http_server -test_plugin_https_client -test_plugin_https_server -test_plugin_tcp -test_plugin_udp -test_plugin_unix -test_plugin_wlan -test_quota_compliance_bluetooth -test_quota_compliance_bluetooth_asymmetric -test_quota_compliance_http -test_quota_compliance_http_asymmetric -test_quota_compliance_https -test_quota_compliance_https_asymmetric -test_quota_compliance_tcp -test_quota_compliance_tcp_asymmetric -test_quota_compliance_udp -test_quota_compliance_unix -test_quota_compliance_unix_asymmetric -test_quota_compliance_wlan -test_quota_compliance_wlan_asymmetric -test_transport_address_switch_http -test_transport_address_switch_https -test_transport_address_switch_tcp -test_transport_address_switch_udp -test_transport_api_blacklisting_tcp -test_transport_api_bluetooth -test_transport_api_disconnect_tcp -test_transport_api_http -test_transport_api_http_reverse -test_transport_api_https -test_transport_api_limited_sockets_tcp -test_transport_api_manipulation_cfg -test_transport_api_manipulation_recv_tcp -test_transport_api_manipulation_send_tcp -test_transport_api_monitor_peers -test_transport_api_multi -test_transport_api_reliability_bluetooth -test_transport_api_reliability_http -test_transport_api_reliability_http_xhr -test_transport_api_reliability_https -test_transport_api_reliability_https_xhr -test_transport_api_reliability_tcp -test_transport_api_reliability_tcp_nat -test_transport_api_reliability_udp -test_transport_api_reliability_unix -test_transport_api_reliability_wlan -test_transport_api_restart_1peer -test_transport_api_restart_2peers -test_transport_api_slow_ats -test_transport_api_tcp -test_transport_api_tcp_nat -test_transport_api_timeout_bluetooth -test_transport_api_timeout_http -test_transport_api_timeout_https -test_transport_api_timeout_tcp -test_transport_api_timeout_udp -test_transport_api_timeout_unix -test_transport_api_timeout_wlan -test_transport_api_udp -test_transport_api_udp_nat -test_transport_api_unix -test_transport_api_unix_abstract -test_transport_api_wlan -test_transport_blacklisting_inbound_bl_full -test_transport_blacklisting_inbound_bl_plugin -test_transport_blacklisting_multiple_plugins -test_transport_blacklisting_no_bl -test_transport_blacklisting_outbound_bl_full -test_transport_blacklisting_outbound_bl_plugin -test_transport_testing_restart -test_transport_testing_startstop -gnunet-communicator-unix -gnunet-service-tng -gnunet-communicator-tcp -gnunet-communicator-udp -test_communicator_basic-* -test_communicator_rekey-* -test_transport_start_with_config -test_transport_api2_tcp -gnunet-communicator-quic diff --git a/src/transport/Makefile.am b/src/transport/Makefile.am deleted file mode 100644 index 7f71b0967..000000000 --- a/src/transport/Makefile.am +++ /dev/null @@ -1,460 +0,0 @@ -# This Makefile.am is in the public domain -AM_CPPFLAGS = -I$(top_srcdir)/src/include -I$(top_builddir)/src/include - -plugindir = $(libdir)/gnunet - -pkgcfgdir= $(pkgdatadir)/config.d/ - -libexecdir= $(pkglibdir)/libexec/ - -pkgcfg_DATA = \ - transport.conf - -if USE_COVERAGE - AM_CFLAGS = --coverage -O0 -endif - -noinst_PROGRAMS = \ - test_transport_start_with_config \ - gnunet-communicator-udp - -TESTING_LIBS = \ - libgnunettransporttesting2.la - -lib_LTLIBRARIES = \ - libgnunettransportapplication.la \ - libgnunettransportcore.la \ - libgnunettransportcommunicator.la \ - libgnunettransportmonitor.la \ - $(TESTING_LIBS) - -libgnunettransporttesting2_la_SOURCES = \ - transport_api_traits.c \ - transport_api_cmd_connecting_peers.c \ - transport_api_cmd_backchannel_check.c \ - transport_api_cmd_start_peer.c \ - transport_api_cmd_stop_peer.c \ - transport_api_cmd_send_simple.c \ - transport_api_cmd_send_simple_performance.c \ - transport-testing2.c transport-testing2.h \ - transport-testing-cmds.h \ - transport-testing-filenames2.c \ - transport-testing-loggers2.c \ - transport-testing-main2.c \ - transport-testing-send2.c \ - transport-testing-communicator.c transport-testing-communicator.h -libgnunettransporttesting2_la_LIBADD = \ - libgnunettransportapplication.la \ - libgnunettransportcore.la \ - $(top_builddir)/src/service/arm/libgnunetarm.la \ - $(top_builddir)/src/lib/testing/libgnunettesting.la \ - $(top_builddir)/src/lib/hello/libgnunethello.la \ - $(top_builddir)/src/peerstore/libgnunetpeerstore.la \ - $(top_builddir)/src/lib/util/libgnunetutil.la -libgnunettransporttesting2_la_LDFLAGS = \ - $(GN_LIBINTL) \ - $(GN_LIB_LDFLAGS) \ - -version-info 0:0:0 - -libgnunettransportapplication_la_SOURCES = \ - transport_api2_application.c -libgnunettransportapplication_la_LIBADD = \ - $(top_builddir)/src/lib/util/libgnunetutil.la \ - $(LTLIBINTL) -libgnunettransportapplication_la_LDFLAGS = \ - $(GN_LIB_LDFLAGS) \ - -version-info 0:0:0 - - -libgnunettransportcore_la_SOURCES = \ - transport_api2_core.c -libgnunettransportcore_la_LIBADD = \ - $(top_builddir)/src/lib/util/libgnunetutil.la \ - $(GN_LIBINTL) -libgnunettransportcore_la_LDFLAGS = \ - $(GN_LIB_LDFLAGS) \ - -version-info 0:0:0 - -libgnunettransportcommunicator_la_SOURCES = \ - transport_api2_communication.c -libgnunettransportcommunicator_la_LIBADD = \ - $(top_builddir)/src/lib/util/libgnunetutil.la \ - $(GN_LIBINTL) -libgnunettransportcommunicator_la_LDFLAGS = \ - $(GN_LIB_LDFLAGS) \ - -version-info 0:0:0 - - -libgnunettransportmonitor_la_SOURCES = \ - transport_api2_monitor.c -libgnunettransportmonitor_la_LIBADD = \ - $(top_builddir)/src/lib/util/libgnunetutil.la \ - $(GN_LIBINTL) -libgnunettransportmonitor_la_LDFLAGS = \ - $(GN_LIB_LDFLAGS) \ - -version-info 0:0:0 - - -libexec_PROGRAMS = \ - gnunet-service-transport \ - gnunet-communicator-unix \ - gnunet-communicator-udp \ - gnunet-communicator-tcp -if HAVE_EXPERIMENTAL -if HAVE_QUICHE -libexec_PROGRAMS += \ - gnunet-communicator-quic -endif -endif - - -#bin_PROGRAMS = \ -# gnunet-transport - -bin_SCRIPTS = \ - gnunet-transport-certificate-creation - -# See: https://www.gnu.org/software/automake/manual/html_node/Scripts.html#Scripts -do_subst = sed -e 's,[@]pkgdatadir[@],$(pkgdatadir),g' - - -gnunet-transport-certificate-creation: gnunet-transport-certificate-creation.in Makefile - $(AWK) -v bdir="$(bindir)" -v py="$(PYTHON)" -v awkay="$(AWK_BINARY)" -v pfx="$(prefix)" -v prl="$(PERL)" -v sysconfdirectory="$(sysconfdir)" -v pkgdatadirectory="$(pkgdatadir)" -f $(top_srcdir)/bin/dosubst.awk < $(srcdir)/gnunet-transport-certificate-creation.in > gnunet-transport-certificate-creation - @chmod +x gnunet-transport-certificate-creation - - - - -gnunet_communicator_unix_SOURCES = \ - gnunet-communicator-unix.c -gnunet_communicator_unix_LDADD = \ - libgnunettransportcommunicator.la \ - $(top_builddir)/src/statistics/libgnunetstatistics.la \ - $(top_builddir)/src/lib/util/libgnunetutil.la - -gnunet_communicator_tcp_SOURCES = \ - gnunet-communicator-tcp.c -gnunet_communicator_tcp_LDADD = \ - libgnunettransportcommunicator.la \ - $(top_builddir)/src/peerstore/libgnunetpeerstore.la \ - $(top_builddir)/src/nat/libgnunetnatnew.la \ - $(top_builddir)/src/statistics/libgnunetstatistics.la \ - $(top_builddir)/src/lib/util/libgnunetutil.la \ - $(LIBGCRYPT_LIBS) - -gnunet_communicator_udp_SOURCES = \ - gnunet-communicator-udp.c -gnunet_communicator_udp_LDADD = \ - libgnunettransportapplication.la \ - libgnunettransportcommunicator.la \ - $(top_builddir)/src/nat/libgnunetnatnew.la \ - $(top_builddir)/src/statistics/libgnunetstatistics.la \ - $(top_builddir)/src/lib/util/libgnunetutil.la \ - $(LIBGCRYPT_LIBS) - -if HAVE_EXPERIMENTAL -if HAVE_QUICHE -gnunet_communicator_quic_SOURCES = \ - gnunet-communicator-quic.c -gnunet_communicator_quic_LDADD = \ - libgnunettransportapplication.la \ - libgnunettransportcommunicator.la \ - $(top_builddir)/src/peerstore/libgnunetpeerstore.la \ - $(top_builddir)/src/nat/libgnunetnatnew.la \ - $(top_builddir)/src/statistics/libgnunetstatistics.la \ - $(top_builddir)/src/lib/util/libgnunetutil.la \ - -lquiche \ - $(LIBGCRYPT_LIBS) -endif -endif - -#gnunet_transport_SOURCES = \ -# gnunet-transport.c -#gnunet_transport_LDADD = \ -# libgnunettransport.la \ -# $(top_builddir)/src/lib/hello/libgnunethello.la \ -# $(top_builddir)/src/lib/util/libgnunetutil.la \ -# $(GN_LIBINTL) - -gnunet_service_transport_SOURCES = \ - gnunet-service-transport.c transport.h -gnunet_service_transport_LDADD = \ - $(top_builddir)/src/peerstore/libgnunetpeerstore.la \ - $(top_builddir)/src/lib/hello/libgnunethello.la \ - $(top_builddir)/src/statistics/libgnunetstatistics.la \ - $(top_builddir)/src/lib/util/libgnunetutil.la \ - $(LIBGCRYPT_LIBS) \ - $(GN_LIBINTL) - -plugin_LTLIBRARIES = \ - libgnunet_test_transport_plugin_cmd_simple_send_performance.la \ - libgnunet_test_transport_plugin_cmd_nat_upnp.la \ - libgnunet_test_transport_plugin_cmd_simple_send.la \ - libgnunet_test_transport_plugin_cmd_simple_send_broadcast.la \ - libgnunet_test_transport_plugin_cmd_simple_send_dv.la \ - libgnunet_test_transport_plugin_cmd_udp_backchannel.la - -libgnunet_test_transport_plugin_cmd_nat_upnp_la_SOURCES = \ - test_transport_plugin_cmd_nat_upnp.c -libgnunet_test_transport_plugin_cmd_nat_upnp_la_LIBADD = \ - libgnunettransporttesting2.la \ - libgnunettransportapplication.la \ - libgnunettransportcore.la \ - $(top_builddir)/src/lib/testing/libgnunettesting.la \ - $(top_builddir)/src/peerstore/libgnunetpeerstore.la \ - $(top_builddir)/src/statistics/libgnunetstatistics.la \ - $(top_builddir)/src/lib/hello/libgnunethello.la \ - $(top_builddir)/src/service/arm/libgnunetarm.la \ - $(top_builddir)/src/lib/util/libgnunetutil.la \ - $(LTLIBINTL) -libgnunet_test_transport_plugin_cmd_nat_upnp_la_LDFLAGS = \ - $(GN_PLUGIN_LDFLAGS) - -libgnunet_test_transport_plugin_cmd_udp_backchannel_la_SOURCES = \ - test_transport_plugin_cmd_udp_backchannel.c -libgnunet_test_transport_plugin_cmd_udp_backchannel_la_LIBADD = \ - libgnunettransporttesting2.la \ - libgnunettransportapplication.la \ - libgnunettransportcore.la \ - $(top_builddir)/src/lib/testing/libgnunettesting.la \ - $(top_builddir)/src/peerstore/libgnunetpeerstore.la \ - $(top_builddir)/src/statistics/libgnunetstatistics.la \ - $(top_builddir)/src/lib/hello/libgnunethello.la \ - $(top_builddir)/src/service/arm/libgnunetarm.la \ - $(top_builddir)/src/lib/util/libgnunetutil.la \ - $(LTLIBINTL) -libgnunet_test_transport_plugin_cmd_udp_backchannel_la_LDFLAGS = \ - $(GN_PLUGIN_LDFLAGS) - -libgnunet_test_transport_plugin_cmd_simple_send_la_SOURCES = \ - test_transport_plugin_cmd_simple_send.c -libgnunet_test_transport_plugin_cmd_simple_send_la_LIBADD = \ - libgnunettransporttesting2.la \ - libgnunettransportapplication.la \ - libgnunettransportcore.la \ - $(top_builddir)/src/lib/testing/libgnunettesting.la \ - $(top_builddir)/src/peerstore/libgnunetpeerstore.la \ - $(top_builddir)/src/statistics/libgnunetstatistics.la \ - $(top_builddir)/src/lib/hello/libgnunethello.la \ - $(top_builddir)/src/service/arm/libgnunetarm.la \ - $(top_builddir)/src/lib/util/libgnunetutil.la \ - $(LTLIBINTL) -libgnunet_test_transport_plugin_cmd_simple_send_la_LDFLAGS = \ - $(GN_PLUGIN_LDFLAGS) - -libgnunet_test_transport_plugin_cmd_simple_send_performance_la_SOURCES = \ - test_transport_plugin_cmd_simple_send_performance.c -libgnunet_test_transport_plugin_cmd_simple_send_performance_la_LIBADD = \ - libgnunettransporttesting2.la \ - libgnunettransportapplication.la \ - libgnunettransportcore.la \ - $(top_builddir)/src/lib/testing/libgnunettesting.la \ - $(top_builddir)/src/peerstore/libgnunetpeerstore.la \ - $(top_builddir)/src/statistics/libgnunetstatistics.la \ - $(top_builddir)/src/lib/hello/libgnunethello.la \ - $(top_builddir)/src/service/arm/libgnunetarm.la \ - $(top_builddir)/src/lib/util/libgnunetutil.la \ - $(LTLIBINTL) -libgnunet_test_transport_plugin_cmd_simple_send_performance_la_LDFLAGS = \ - $(GN_PLUGIN_LDFLAGS) - -libgnunet_test_transport_plugin_cmd_simple_send_broadcast_la_SOURCES = \ - test_transport_plugin_cmd_simple_send_broadcast.c -libgnunet_test_transport_plugin_cmd_simple_send_broadcast_la_LIBADD = \ - libgnunettransporttesting2.la \ - libgnunettransportapplication.la \ - libgnunettransportcore.la \ - $(top_builddir)/src/lib/testing/libgnunettesting.la \ - $(top_builddir)/src/peerstore/libgnunetpeerstore.la \ - $(top_builddir)/src/statistics/libgnunetstatistics.la \ - $(top_builddir)/src/lib/hello/libgnunethello.la \ - $(top_builddir)/src/service/arm/libgnunetarm.la \ - $(top_builddir)/src/lib/util/libgnunetutil.la \ - $(LTLIBINTL) -libgnunet_test_transport_plugin_cmd_simple_send_broadcast_la_LDFLAGS = \ - $(GN_PLUGIN_LDFLAGS) - -libgnunet_test_transport_plugin_cmd_simple_send_dv_la_SOURCES = \ - test_transport_plugin_cmd_simple_send_dv.c -libgnunet_test_transport_plugin_cmd_simple_send_dv_la_LIBADD = \ - libgnunettransporttesting2.la \ - libgnunettransportapplication.la \ - libgnunettransportcore.la \ - $(top_builddir)/src/lib/testing/libgnunettesting.la \ - $(top_builddir)/src/peerstore/libgnunetpeerstore.la \ - $(top_builddir)/src/statistics/libgnunetstatistics.la \ - $(top_builddir)/src/lib/hello/libgnunethello.la \ - $(top_builddir)/src/service/arm/libgnunetarm.la \ - $(top_builddir)/src/lib/util/libgnunetutil.la \ - $(LTLIBINTL) -libgnunet_test_transport_plugin_cmd_simple_send_dv_la_LDFLAGS = \ - $(GN_PLUGIN_LDFLAGS) - -check_PROGRAMS = \ - test_communicator_basic-unix \ - test_communicator_basic-tcp \ - test_communicator_basic-udp \ - test_communicator_basic-quic \ - test_communicator_rekey-tcp \ - test_communicator_rekey-udp \ - test_communicator_backchannel-udp \ - test_communicator_bidirect-tcp - -check_SCRIPTS= \ - test_transport_start_testcase.sh \ - test_transport_simple_send_performance.sh \ - test_transport_nat_icmp_tcp.sh \ - test_transport_nat_upnp.sh \ - test_transport_simple_send_string.sh \ - test_transport_simple_send.sh \ - test_transport_simple_send_broadcast.sh \ - test_transport_udp_backchannel.sh \ - test_transport_simple_send_dv_circle.sh \ - test_transport_simple_send_dv_inverse.sh - -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_SCRIPTS) \ - test_communicator_basic-unix \ - test_communicator_basic-tcp \ - test_communicator_basic-quic \ - test_communicator_basic-udp \ - test_communicator_rekey-tcp \ - test_communicator_rekey-udp \ - test_communicator_backchannel-udp \ - test_communicator_bidirect-tcp -endif - - -test_transport_start_with_config_SOURCES = \ - test_transport_start_with_config.c -test_transport_start_with_config_LDADD = \ - $(top_builddir)/src/lib/testing/libgnunettesting.la \ - $(top_builddir)/src/lib/util/libgnunetutil.la \ - $(top_builddir)/src/lib/hello/libgnunethello.la \ - libgnunettransportcore.la \ - libgnunettransporttesting2.la - -test_communicator_basic_unix_SOURCES = \ - test_communicator_basic.c -test_communicator_basic_unix_LDADD = \ - libgnunettransporttesting2.la \ - $(top_builddir)/src/lib/testing/libgnunettesting.la \ - $(top_builddir)/src/lib/util/libgnunetutil.la \ - $(top_builddir)/src/statistics/libgnunetstatistics.la - -test_communicator_basic_tcp_SOURCES = \ - test_communicator_basic.c -test_communicator_basic_tcp_LDADD = \ - libgnunettransporttesting2.la \ - $(top_builddir)/src/lib/testing/libgnunettesting.la \ - $(top_builddir)/src/lib/util/libgnunetutil.la \ - $(top_builddir)/src/statistics/libgnunetstatistics.la - -test_communicator_basic_udp_SOURCES = \ - test_communicator_basic.c -test_communicator_basic_udp_LDADD = \ - libgnunettransporttesting2.la \ - $(top_builddir)/src/lib/testing/libgnunettesting.la \ - $(top_builddir)/src/lib/util/libgnunetutil.la \ - $(top_builddir)/src/statistics/libgnunetstatistics.la - -test_communicator_basic_quic_SOURCES = \ - test_communicator_basic.c -test_communicator_basic_quic_LDADD = \ - libgnunettransporttesting2.la \ - $(top_builddir)/src/lib/testing/libgnunettesting.la \ - $(top_builddir)/src/lib/util/libgnunetutil.la \ - $(top_builddir)/src/statistics/libgnunetstatistics.la - -test_communicator_rekey_tcp_SOURCES = \ - test_communicator_basic.c -test_communicator_rekey_tcp_LDADD = \ - libgnunettransporttesting2.la \ - $(top_builddir)/src/lib/testing/libgnunettesting.la \ - $(top_builddir)/src/lib/util/libgnunetutil.la \ - $(top_builddir)/src/statistics/libgnunetstatistics.la - -test_communicator_rekey_udp_SOURCES = \ - test_communicator_basic.c -test_communicator_rekey_udp_LDADD = \ - libgnunettransporttesting2.la \ - $(top_builddir)/src/lib/testing/libgnunettesting.la \ - $(top_builddir)/src/lib/util/libgnunetutil.la \ - $(top_builddir)/src/statistics/libgnunetstatistics.la - -test_communicator_backchannel_udp_SOURCES = \ - test_communicator_basic.c -test_communicator_backchannel_udp_LDADD = \ - libgnunettransporttesting2.la \ - $(top_builddir)/src/lib/testing/libgnunettesting.la \ - $(top_builddir)/src/lib/util/libgnunetutil.la \ - $(top_builddir)/src/statistics/libgnunetstatistics.la - -test_communicator_bidirect_tcp_SOURCES = \ - test_communicator_basic.c -test_communicator_bidirect_tcp_LDADD = \ - libgnunettransporttesting2.la \ - $(top_builddir)/src/lib/testing/libgnunettesting.la \ - $(top_builddir)/src/lib/util/libgnunetutil.la \ - $(top_builddir)/src/statistics/libgnunetstatistics.la - -test_transport_api2_tcp_SOURCES = \ - test_transport_api2.c -test_transport_api2_tcp_LDADD = \ - $(top_builddir)/src/lib/hello/libgnunethello.la \ - $(top_builddir)/src/lib/util/libgnunetutil.la \ - libgnunettransporttesting2.la - -EXTRA_DIST = \ -test_transport_start_testcase.sh \ -test_transport_simple_send_performance.sh \ -test_transport_nat_icmp_tcp.sh \ -test_transport_nat_upnp.sh \ -test_transport_simple_send_string.sh \ -test_transport_simple_send.sh \ -test_transport_simple_send_broadcast.sh \ -test_transport_udp_backchannel.sh \ -test_transport_simple_send_dv_circle.sh \ -test_transport_simple_send_dv_inverse.sh \ -gnunet-transport-certificate-creation.in \ -test_plugin_hostkey \ -test_plugin_hostkey.ecc \ -test_delay \ -template_cfg_peer1.conf\ -template_cfg_peer2.conf\ -test_transport_api_data.conf\ -test_transport_api_multi_peer1.conf\ -test_transport_api_multi_peer2.conf\ -test_transport_api_tcp_nat_peer1.conf\ -test_transport_api_tcp_nat_peer2.conf\ -test_transport_api_tcp_peer1.conf\ -test_transport_api_tcp_peer2.conf\ -test_transport_api2_tcp_peer1.conf\ -test_transport_api2_tcp_peer2.conf\ -test_transport_api_udp_nat_peer1.conf\ -test_transport_api_udp_nat_peer2.conf\ -test_transport_api_udp_peer1.conf\ -test_transport_api_udp_peer2.conf\ -test_transport_api_unix_peer1.conf\ -test_transport_api_unix_peer2.conf\ -test_transport_api_monitor_peers_peer1.conf\ -test_transport_api_monitor_peers_peer2.conf\ -test_transport_api_monitor_validation_peer1.conf\ -test_transport_api_monitor_validation_peer2.conf\ -test_transport_defaults.conf\ -test_communicator_unix_basic_peer1.conf \ -test_communicator_unix_basic_peer2.conf \ -test_communicator_tcp_basic_peer1.conf \ -test_communicator_tcp_basic_peer2.conf \ -test_communicator_udp_basic_peer1.conf \ -test_communicator_udp_basic_peer2.conf \ -test_communicator_tcp_rekey_peer1.conf \ -test_communicator_tcp_rekey_peer2.conf \ -test_communicator_udp_rekey_peer1.conf \ -test_communicator_udp_rekey_peer2.conf \ -test_communicator_udp_backchannel_peer1.conf \ -test_communicator_udp_backchannel_peer2.conf \ -test_communicator_tcp_bidirect_peer1.conf \ -test_communicator_tcp_bidirect_peer2.conf diff --git a/src/transport/NOTES b/src/transport/NOTES deleted file mode 100644 index 41404e1f9..000000000 --- a/src/transport/NOTES +++ /dev/null @@ -1,46 +0,0 @@ -KEY DESIGN CHOICES: - - who decides which connections to keep/create? - => higher level session/key management! - - who enforces things like F2F topology, etc? - => higher level session/key management! - - who tracks all known HELLOs & validates? - => We validate, PEERINFO tracks! - - who advertises our HELLO? - => us! (need background job; previously: advertising) - - who advertises other peers HELLOs? - => higher level (core?) - - who does bootstrapping? - => bootstrap service (external!) - - who enforces inbound bandwidth limits? - => transport-service and plugins! (previously: core); - either by limiting reads (TCP) or discarding packets - (transport-service) - - who enforces outbound bandwidth limits? - => transport_api! - - who decides outbound bandwidth limits? - => other peer, via core (need authenticated limits!) - - who decides inbound bandwidth limits? - => core / apps above core (need trust info) - - cost function for transports is latency estimate in ms - => plugin provides latency data, transport-service - selects plugin(s) for transmission - - who is responsible for fragmentation? - => plugins! (may use common shared library) - - should we require UDP to be reliable? - => NO. There are other places that may (rarely) - use messages that we can not fix - - how do we access the 50% of service that we need for TCP/UDP - from service.c without code replication or getting 50% - that we do not want (i.e. shutdown, pid-file-writing, etc.) - => use GNUNET_SERVICE_start/stop functions! - - At what level do we manage timeouts? - => At the plugin (TCP connections), - transport-service (neighbours) and - core (sessions) level! - => All plugins have to disconnect before service-level - disconnect occurs - => We can have a plugin-connection die, but the session - survives! - => We can have a session die (no further authenticated - communication) even if the plugin thinks it is still - up! diff --git a/src/transport/benchmark.sh b/src/transport/benchmark.sh deleted file mode 100755 index a29e6ec2d..000000000 --- a/src/transport/benchmark.sh +++ /dev/null @@ -1,13 +0,0 @@ -#!/bin/sh - -for i in $(seq 1 0) -do - echo RUN $i - ./test_transport_api_reliability_http -done - -for i in $(seq 1 100) -do - echo RUN $i - ./test_transport_api_reliability_https -done diff --git a/src/transport/communicator.h b/src/transport/communicator.h deleted file mode 100644 index 5ef43597d..000000000 --- a/src/transport/communicator.h +++ /dev/null @@ -1,138 +0,0 @@ -/* - This file is part of GNUnet. - Copyright (C) 2009-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 . - - SPDX-License-Identifier: AGPL3.0-or-later - */ - -/** - * @file transport/communicator.h - * @brief common internal definitions for communicator services - * @author Christian Grothoff - */ -#ifndef COMMUNICATOR_H -#define COMMUNICAOTR_H - -#include "gnunet_util_lib.h" -#include "gnunet_protocols.h" - -GNUNET_NETWORK_STRUCT_BEGIN - -/** - * Message used to tell a communicator about a successful - * key exchange. - * - * Note that this style of KX acknowledgement typically only applies - * for communicators where the underlying network protocol is - * unidirectional and/or lacks cryptography. Furthermore, this is - * just the recommended "generic" style, communicators are always free - * to implement original designs that better fit their requirements. - */ -struct GNUNET_TRANSPORT_CommunicatorGenericKXConfirmation -{ - /** - * Type is #GNUNET_MESSAGE_TYPE_TRANSPORT_COMMUNICATOR_KX_CONFIRMATION - */ - struct GNUNET_MessageHeader header; - - /** - * Timestamp from the original sender which identifies the original KX. - */ - struct GNUNET_TIME_AbsoluteNBO monotonic_time; - - /** - * How long does the receiver of the KX believe that the address - * on which the KX was received will continue to be valid. - */ - struct GNUNET_TIME_RelativeNBO validity; - - /** - * Hash of the shared secret. Specific hash function may depend on - * the communicator's protocol details. - */ - struct GNUNET_HashCode token; -}; - - -/** - * Message used to tell a communicator about the receiver's - * flow control limits and to acknowledge receipt of certain - * messages. - * - * Note that a sender MAY choose to violate the flow-control - * limits provided in this message by a receiver, which may - * result in messages being lost (after all, transport is an - * unreliable channel). So if the sender violates these - * constraints, it should expect that the receive will simply - * discard the (partially) received "old" messages. - * - * This way, if a sender or receiver crashes, there is no protocol - * violation. - * - * Note that this style of flow control typically only applies - * for communicators where the underlying network protocol does - * not already implement flow control. Furthermore, this is - * just the recommended "generic" style, communicators are always - * free to implement original designs that better fit their - * requirements. - */ -struct GNUNET_TRANSPORT_CommunicatorGenericFCLimits -{ - /** - * Type is #GNUNET_MESSAGE_TYPE_TRANSPORT_COMMUNICATOR_FC_LIMITS - */ - struct GNUNET_MessageHeader header; - - /** - * Maximum number of messages beyond the acknowledged message - * number that can still be transmitted concurrently without - * further acknowledgements. - */ - uint32_t msg_window_size; - - /** - * Up to which message number were all messages received. - */ - uint64_t msg_cummulative_ack; - - /** - * Maximum number of payload bytes beyond the acknowledged - * number of bytes can still be transmitted without further - * acknowledgements. - */ - uint64_t bytes_window_size; - - /** - * Cumulative acknowledgement for number of bytes received. - */ - uint64_t bytes_cummulative_ack; - - /** - * Followed by a variable-size bitfield for messages received - * beyond @e msg_cummulative_ack. Index at offset 0 must thus - * be zero, otherwise @e msg_cummulative_ack should be - * increased. Note that this field can be overall of 0 bytes. - * The variable-size bitfield must be a multiple of 64 bits - * long. - */ - /* uint64_t msg_selective_ack_field[]; */ -}; - - -GNUNET_NETWORK_STRUCT_END - -/* end of communicator.h */ -#endif diff --git a/src/transport/gnunet-communicator-quic.c b/src/transport/gnunet-communicator-quic.c deleted file mode 100644 index 0a7e511eb..000000000 --- a/src/transport/gnunet-communicator-quic.c +++ /dev/null @@ -1,1795 +0,0 @@ -/* - This file is part of GNUnet - Copyright (C) 2010-2014, 2018, 2019 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 . - - SPDX-License-Identifier: AGPL3.0-or-later - */ - -/** - * @file transport/gnunet-communicator-quic.c - * @brief Transport plugin using QUIC. - * @author Marshall Stone - * @author Martin Schanzenbach - * - * TODO: - * - Automatically generate self-signed x509 certificates and load from config - * - Figure out MTU and how we have to handle fragmentation in Quiche. - * - Mandate timeouts - * - Setup stats handler properly - * - Doxygen documentation of methods - * - Refactor code shared with UDP and TCP communicator - * - Performance testing - * - Check for memory leaks with coverity/valgrind - */ -#include "gnunet_common.h" -#include "gnunet_util_lib.h" -#include "gnunet_core_service.h" -#include "quiche.h" -#include "platform.h" -#include "gnunet_protocols.h" -#include "gnunet_signatures.h" -#include "gnunet_constants.h" -#include "gnunet_statistics_service.h" -#include "gnunet_transport_application_service.h" -#include "gnunet_transport_communication_service.h" -#include "gnunet_nat_service.h" -#include "stdint.h" -#include "inttypes.h" - -#define COMMUNICATOR_CONFIG_SECTION "communicator-quic" -#define COMMUNICATOR_ADDRESS_PREFIX "quic" -#define MAX_DATAGRAM_SIZE 1350 - - -/* FIXME: Review all static lengths/contents below. Maybe this can be done smarter */ -/* Currently equivalent to QUICHE_MAX_CONN_ID_LEN */ -#define LOCAL_CONN_ID_LEN 20 -#define MAX_TOKEN_LEN \ - sizeof("quiche") - 1 \ - + sizeof(struct sockaddr_storage) \ - + QUICHE_MAX_CONN_ID_LEN -#define CID_LEN sizeof(uint8_t) * QUICHE_MAX_CONN_ID_LEN -#define TOKEN_LEN sizeof (uint8_t) * MAX_TOKEN_LEN - - -/* FIXME: Why 4? - Generic, bidirectional, client-initiated quic stream id */ -#define STREAMID_BI 4 - -/** - * How long do we believe our addresses to remain up (before - * the other peer should revalidate). - */ -#define ADDRESS_VALIDITY_PERIOD GNUNET_TIME_UNIT_HOURS - -/** - * Map of DCID (uint8_t) -> quic_conn for quickly retrieving connections to other peers. - */ -struct GNUNET_CONTAINER_MultiHashMap *conn_map; - -/** - * Map of sockaddr -> struct PeerAddress - */ -struct GNUNET_CONTAINER_MultiHashMap *addr_map; - -/** - * Handle to the config - */ -static const struct GNUNET_CONFIGURATION_Handle *cfg; - -/** - * FIXME undocumented - */ -static struct GNUNET_TIME_Relative rekey_interval; - -/** - * FIXME undocumented - */ -static struct GNUNET_NETWORK_Handle *udp_sock; - -/** - * FIXME undocumented - */ -static struct GNUNET_SCHEDULER_Task *read_task; - -/** - * FIXME undocumented - */ -static struct GNUNET_TRANSPORT_CommunicatorHandle *ch; - -/** - * FIXME undocumented - */ -static struct GNUNET_TRANSPORT_ApplicationHandle *ah; - -/** - * FIXME undocumented - */ -static int have_v6_socket; - -/** - * FIXME undocumented - */ -static uint16_t my_port; - -/** - * FIXME undocumented - */ -static unsigned long long rekey_max_bytes; - -/** - * FIXME undocumented - */ -static quiche_config *config = NULL; - -/** - * Our peer identity -*/ -struct GNUNET_PeerIdentity my_identity; - -/** - * Our private key. - */ -static struct GNUNET_CRYPTO_EddsaPrivateKey *my_private_key; - -/** - * Connection to NAT service. - */ -static struct GNUNET_NAT_Handle *nat; - -/** - * Information we track per peer we have recently been in contact with. - * - * (Since quiche handles crypto, handshakes, etc. we don't differentiate - * between SenderAddress and ReceiverAddress) - * FIXME: But we do a handshake as well. The flag in this struct seems to - * indicate this. Update comment! - */ -struct PeerAddress -{ - /** - * To whom are we talking to. - */ - struct GNUNET_PeerIdentity target; - - /** - * Flag to indicate whether we know the PeerIdentity (target) yet - */ - int id_rcvd; - - /** - * Flag to indicate whether we have sent OUR PeerIdentity to this peer - */ - int id_sent; - - /** - * Flag to indicate if we are the initiator of the connection - */ - int is_receiver; - - /** - * Address of the receiver in the human-readable format - * with the #COMMUNICATOR_ADDRESS_PREFIX. - */ - char *foreign_addr; - - /** - * Address of the other peer. - */ - struct sockaddr *address; - - /** - * Length of the address. - */ - socklen_t address_len; - - /** - * The QUIC connection associated with this peer - */ - struct quic_conn *conn; - - /** - * Default message queue we are providing for the #ch. - */ - struct GNUNET_MQ_Handle *d_mq; - - /** - * handle for default queue with the #ch. - */ - struct GNUNET_TRANSPORT_QueueHandle *d_qh; - - /** - * Timeout for this peer address. - */ - struct GNUNET_TIME_Absolute timeout; - - /** - * MTU we allowed transport for this peer's default queue. - * FIXME: MTU from quiche - */ - size_t d_mtu; - - /** - * Which network type does this queue use? - */ - enum GNUNET_NetworkType nt; - - /** - * receiver_destroy already called on receiver. - */ - int peer_destroy_called; - - /** - * FIXME implementation missing - * Entry in sender expiration heap. - */ - // struct GNUNET_CONTAINER_HeapNode *hn; -}; - -// /** -// * FIXME: Implementation missing -// * Expiration heap for peers (contains `struct PeerAddress`) -// */ -// static struct GNUNET_CONTAINER_Heap *peers_heap; - -/** - * ID of timeout task - */ -static struct GNUNET_SCHEDULER_Task *timeout_task; - -/** - * Network scanner to determine network types. - */ -static struct GNUNET_NT_InterfaceScanner *is; - -/** - * For logging statistics. - */ -static struct GNUNET_STATISTICS_Handle *stats; - -/** - * QUIC connection object. A connection has a unique SCID/DCID pair. Here we store our SCID - * (incoming packet DCID field == outgoing packet SCID field) for a given connection. This - * is hashed for each unique quic_conn. -*/ -struct quic_conn -{ - uint8_t cid[LOCAL_CONN_ID_LEN]; - - quiche_conn *conn; -}; - -/** - * QUIC_header is used to store information received from an incoming QUIC packet -*/ -struct QUIC_header -{ - uint8_t type; - uint32_t version; - - uint8_t scid[QUICHE_MAX_CONN_ID_LEN]; - size_t scid_len; - - uint8_t dcid[QUICHE_MAX_CONN_ID_LEN]; - size_t dcid_len; - - uint8_t odcid[QUICHE_MAX_CONN_ID_LEN]; - size_t odcid_len; - - uint8_t token[MAX_TOKEN_LEN]; - size_t token_len; -}; - - -/** - * Given a PeerAddress, receive data from streams after doing connection logic. - * ASSUMES: connection is established to peer -*/ -static void -recv_from_streams (struct PeerAddress *peer) -{ - char stream_buf[UINT16_MAX]; - size_t buf_size = UINT16_MAX; - char *buf_ptr = stream_buf; - struct GNUNET_MessageHeader *hdr; - - uint64_t s = 0; - quiche_stream_iter *readable; - bool fin; - ssize_t recv_len; - - readable = quiche_conn_readable (peer->conn->conn); - while (quiche_stream_iter_next (readable, &s)) - { - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "stream %" PRIu64 " is readable\n", - s); - fin = false; - recv_len = quiche_conn_stream_recv (peer->conn->conn, s, - (uint8_t *) stream_buf, buf_size, - &fin); - if (recv_len < 0) - { - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - "error while receiving data from stream %" PRIu64 "\n", s); - break; - } - /** - * FIXME: Do not use implicit booleans. Use GNUNET_YES, GNUNET_NO, GNUNET_SYSERR - * and check for that. - * - * Initial packet should contain peerid if they are the initiator - */ - if (! peer->is_receiver && GNUNET_NO == peer->id_rcvd) - { - if (recv_len < sizeof(struct GNUNET_PeerIdentity)) - { - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - "message recv len of %zd less than length of peer identity\n", - recv_len); - return; - } - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "received peer identity\n"); - struct GNUNET_PeerIdentity *pid = (struct - GNUNET_PeerIdentity *) stream_buf; - peer->target = *pid; - peer->id_rcvd = GNUNET_YES; - buf_ptr += sizeof(struct GNUNET_PeerIdentity); - recv_len -= sizeof(struct GNUNET_PeerIdentity); - } - /** - * Parse messages to pass to communicator - */ - while (recv_len >= sizeof(struct GNUNET_MessageHeader)) - { - hdr = (struct GNUNET_MessageHeader *) buf_ptr; - if (ntohs (hdr->size) > recv_len) - { - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - "message size stated (%d) is greater than length of rcvd data (%zd)!\n", - ntohs (hdr->size), recv_len); - return; - } - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "passing %zd bytes to core\n", - recv_len); - GNUNET_TRANSPORT_communicator_receive (ch, &peer->target, hdr, - ADDRESS_VALIDITY_PERIOD, NULL, - NULL); - recv_len -= ntohs (hdr->size); - buf_ptr += ntohs (hdr->size); - } - /** - * Check for leftover bytes - */ - if (0 != recv_len) - { - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - "message recv len of %zd less than length of message header\n", - recv_len); - } - /** - * FIXME: comment useless - * fin - */ - if (fin) - { - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "fin received, closing connection\n"); - if (0 > quiche_conn_close (peer->conn->conn, true, 0, NULL, 0)) - { - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - "quiche failed to close connection to peer\n"); - } - } - } - quiche_stream_iter_free (readable); -} - - -/** - * FIXME: review token generation, assure tokens are generated properly. doxygen - */ -static void -mint_token (const uint8_t *dcid, size_t dcid_len, - struct sockaddr_storage *addr, socklen_t addr_len, - uint8_t *token, size_t *token_len) -{ - GNUNET_memcpy (token, "quiche", sizeof("quiche") - 1); - GNUNET_memcpy (token + sizeof("quiche") - 1, addr, addr_len); - GNUNET_memcpy (token + sizeof("quiche") - 1 + addr_len, dcid, dcid_len); - - *token_len = sizeof("quiche") - 1 + addr_len + dcid_len; -} - - -static enum GNUNET_GenericReturnValue -validate_token (const uint8_t *token, size_t token_len, - struct sockaddr_storage *addr, socklen_t addr_len, - uint8_t *odcid, size_t *odcid_len) -{ - if ((token_len < sizeof("quiche") - 1) || - memcmp (token, "quiche", sizeof("quiche") - 1)) - { - return GNUNET_NO; - } - - token += sizeof("quiche") - 1; - token_len -= sizeof("quiche") - 1; - - if ((token_len < addr_len) || memcmp (token, addr, addr_len)) - { - return GNUNET_NO; - } - - token += addr_len; - token_len -= addr_len; - - if (*odcid_len < token_len) - { - return GNUNET_NO; - } - - memcpy (odcid, token, token_len); - *odcid_len = token_len; - - return GNUNET_OK; -} - - -static struct quic_conn* -create_conn (uint8_t *scid, size_t scid_len, - uint8_t *odcid, size_t odcid_len, - struct sockaddr *local_addr, - socklen_t local_addr_len, - struct sockaddr_storage *peer_addr, - socklen_t peer_addr_len) -{ - struct quic_conn *conn; - quiche_conn *q_conn; - conn = GNUNET_new (struct quic_conn); - if (scid_len != LOCAL_CONN_ID_LEN) - { - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - "error while creating connection, scid length too short: %zu\n", - scid_len); - return NULL; - } - - GNUNET_memcpy (conn->cid, scid, LOCAL_CONN_ID_LEN); - q_conn = quiche_accept (conn->cid, LOCAL_CONN_ID_LEN, - odcid, odcid_len, - local_addr, - local_addr_len, - (struct sockaddr *) peer_addr, - peer_addr_len, - config); - if (NULL == q_conn) - { - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - "quiche failed to create connection after call to quiche_accept\n"); - return NULL; - } - conn->conn = q_conn; - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "new quic connection created\n"); - return conn; -} - - -static void -flush_egress (struct quic_conn *conn) -{ - static uint8_t out[MAX_DATAGRAM_SIZE]; - quiche_send_info send_info; - - ssize_t written; - ssize_t sent; - - while (1) - { - written = quiche_conn_send (conn->conn, out, sizeof(out), &send_info); - if (QUICHE_ERR_DONE == written) - { - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "done writing quic packets\n"); - break; - } - if (0 > written) - { - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - "quiche failed to create packet. quiche error: %zd\n", - written); - return; - } - sent = GNUNET_NETWORK_socket_sendto (udp_sock, out, written, - (struct sockaddr *) &send_info.to, - send_info.to_len); - if (sent != written) - { - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - "quiche failed to send data to peer\n"); - return; - } - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "sent %zd bytes\n", sent); - } -} - - -/** - * Increment receiver timeout due to activity. - * - * @param receiver address for which the timeout should be rescheduled - */ -static void -reschedule_peer_timeout (struct PeerAddress *peer) -{ - peer->timeout = - GNUNET_TIME_relative_to_absolute (GNUNET_CONSTANTS_IDLE_CONNECTION_TIMEOUT); - // GNUNET_CONTAINER_heap_update_cost (peer->hn, - // peer->timeout.abs_value_us); -} - - -/** - * Destroys a receiving state due to timeout or shutdown. - * - * @param receiver entity to close down - */ -static void -peer_destroy (struct PeerAddress *peer) -{ - struct GNUNET_HashCode addr_key; - - peer->peer_destroy_called = GNUNET_YES; - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "Disconnecting peer for peer `%s'\n", - GNUNET_i2s (&peer->target)); - if (NULL != peer->d_qh) - { - GNUNET_TRANSPORT_communicator_mq_del (peer->d_qh); - peer->d_qh = NULL; - } - // GNUNET_assert (peer == GNUNET_CONTAINER_heap_remove_node (peer->hn)); - /** - * Remove peer from hashmap - */ - GNUNET_CRYPTO_hash (peer->address, peer->address_len, &addr_key); - if (GNUNET_NO == GNUNET_CONTAINER_multihashmap_remove (addr_map, &addr_key, - peer)) - { - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - "tried to remove non-existent peer from addr map\n"); - return; - } - GNUNET_STATISTICS_set (stats, - "# peers active", - GNUNET_CONTAINER_multihashmap_size (addr_map), - GNUNET_NO); - quiche_conn_free (peer->conn->conn); - GNUNET_free (peer->address); - GNUNET_free (peer->foreign_addr); - GNUNET_free (peer->conn); - GNUNET_free (peer); -} - - -/** - * Iterator over all peers to clean up. - * - * @param cls NULL - * @param key peer->address - * @param value the peer to destroy - * @return #GNUNET_OK to continue to iterate - */ -static int -get_peer_delete_it (void *cls, - const struct GNUNET_HashCode *key, - void *value) -{ - struct PeerAddress *peer = value; - (void) cls; - (void) key; - peer_destroy (peer); - return GNUNET_OK; -} - - -/** - * Signature of functions implementing the sending functionality of a - * message queue. - * - * @param mq the message queue - * @param msg the message to send - * @param impl_state our `struct PeerAddress` - */ -static void -mq_send_d (struct GNUNET_MQ_Handle *mq, - const struct GNUNET_MessageHeader *msg, - void *impl_state) -{ - struct PeerAddress *peer = impl_state; - uint16_t msize = ntohs (msg->size); - ssize_t send_len; - - if (NULL == peer->conn->conn) - { - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - "peer never established quic connection\n"); - return; - } - - GNUNET_assert (mq == peer->d_mq); - if (msize > peer->d_mtu) - { - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "msize: %u, mtu: %lu\n", - msize, - peer->d_mtu); - GNUNET_break (0); - if (GNUNET_YES != peer->peer_destroy_called) - { - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "peer destroy called, destroying peer\n"); - peer_destroy (peer); - } - return; - } - reschedule_peer_timeout (peer); - - send_len = quiche_conn_stream_send (peer->conn->conn, 4, (uint8_t *) msg, - msize, false); - if (send_len != msize) - { - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - "tried to send message and quiche returned %zd", send_len); - return; - } - flush_egress (peer->conn); - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "sent a message of %zd bytes\n", send_len); - GNUNET_MQ_impl_send_continue (mq); -} - - -/** - * Signature of functions implementing the destruction of a message - * queue. Implementations must not free @a mq, but should take care - * of @a impl_state. - * - * @param mq the message queue to destroy - * @param impl_state our `struct PeerAddress` - */ -static void -mq_destroy_d (struct GNUNET_MQ_Handle *mq, void *impl_state) -{ - struct PeerAddress *peer = impl_state; - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "Default MQ destroyed\n"); - if (mq == peer->d_mq) - { - peer->d_mq = NULL; - if (GNUNET_YES != peer->peer_destroy_called) - peer_destroy (peer); - } -} - - -/** - * Implementation function that cancels the currently sent message. - * - * @param mq message queue - * @param impl_state our `struct PeerAddress` - */ -static void -mq_cancel (struct GNUNET_MQ_Handle *mq, void *impl_state) -{ - /* Cancellation is impossible with QUIC; bail */ - GNUNET_assert (0); -} - - -/** - * Generic error handler, called with the appropriate - * error code and the same closure specified at the creation of - * the message queue. - * Not every message queue implementation supports an error handler. - * - * @param cls our `struct ReceiverAddress` - * @param error error code - */ -static void -mq_error (void *cls, enum GNUNET_MQ_Error error) -{ - struct PeerAddress *peer = cls; - - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - "MQ error in queue to %s: %d\n", - GNUNET_i2s (&peer->target), - (int) error); - peer_destroy (peer); -} - - -/** - * Convert UDP bind specification to a `struct sockaddr *` - * - * @param bindto bind specification to convert - * @param[out] sock_len set to the length of the address - * @return converted bindto specification - */ -static struct sockaddr * -udp_address_to_sockaddr (const char *bindto, socklen_t *sock_len) -{ - struct sockaddr *in; - unsigned int port; - char dummy[2]; - char *colon; - char *cp; - - if (1 == sscanf (bindto, "%u%1s", &port, dummy)) - { - /* interpreting value as just a PORT number */ - if (port > UINT16_MAX) - { - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - "BINDTO specification `%s' invalid: value too large for port\n", - bindto); - return NULL; - } - if ((GNUNET_NO == GNUNET_NETWORK_test_pf (PF_INET6)) || - (GNUNET_YES == - GNUNET_CONFIGURATION_get_value_yesno (cfg, - COMMUNICATOR_CONFIG_SECTION, - "DISABLE_V6"))) - { - struct sockaddr_in *i4; - - i4 = GNUNET_malloc (sizeof(struct sockaddr_in)); - i4->sin_family = AF_INET; - i4->sin_port = htons ((uint16_t) port); - *sock_len = sizeof(struct sockaddr_in); - in = (struct sockaddr *) i4; - } - else - { - struct sockaddr_in6 *i6; - - i6 = GNUNET_malloc (sizeof(struct sockaddr_in6)); - i6->sin6_family = AF_INET6; - i6->sin6_port = htons ((uint16_t) port); - *sock_len = sizeof(struct sockaddr_in6); - in = (struct sockaddr *) i6; - } - return in; - } - cp = GNUNET_strdup (bindto); - colon = strrchr (cp, ':'); - if (NULL != colon) - { - /* interpret value after colon as port */ - *colon = '\0'; - colon++; - if (1 == sscanf (colon, "%u%1s", &port, dummy)) - { - /* interpreting value as just a PORT number */ - if (port > UINT16_MAX) - { - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - "BINDTO specification `%s' invalid: value too large for port\n", - bindto); - GNUNET_free (cp); - return NULL; - } - } - else - { - GNUNET_log ( - GNUNET_ERROR_TYPE_ERROR, - "BINDTO specification `%s' invalid: last ':' not followed by number\n", - bindto); - GNUNET_free (cp); - return NULL; - } - } - else - { - /* interpret missing port as 0, aka pick any free one */ - port = 0; - } - { - /* try IPv4 */ - struct sockaddr_in v4; - - memset (&v4, 0, sizeof(v4)); - if (1 == inet_pton (AF_INET, cp, &v4.sin_addr)) - { - v4.sin_family = AF_INET; - v4.sin_port = htons ((uint16_t) port); -#if HAVE_SOCKADDR_IN_SIN_LEN - v4.sin_len = sizeof(struct sockaddr_in); -#endif - in = GNUNET_memdup (&v4, sizeof(struct sockaddr_in)); - *sock_len = sizeof(struct sockaddr_in); - GNUNET_free (cp); - return in; - } - } - { - /* try IPv6 */ - struct sockaddr_in6 v6; - const char *start; - - memset (&v6, 0, sizeof(v6)); - start = cp; - if (('[' == *cp) && (']' == cp[strlen (cp) - 1])) - { - start++; /* skip over '[' */ - cp[strlen (cp) - 1] = '\0'; /* eat ']' */ - } - if (1 == inet_pton (AF_INET6, start, &v6.sin6_addr)) - { - v6.sin6_family = AF_INET6; - v6.sin6_port = htons ((uint16_t) port); -#if HAVE_SOCKADDR_IN_SIN_LEN - v6.sin6_len = sizeof(sizeof(struct sockaddr_in6)); -#endif - in = GNUNET_memdup (&v6, sizeof(v6)); - *sock_len = sizeof(v6); - GNUNET_free (cp); - return in; - } - } - /* #5528 FIXME (feature!): maybe also try getnameinfo()? */ - GNUNET_free (cp); - return NULL; -} - - -/** - * Setup the MQ for the @a peer. If a queue exists, - * the existing one is destroyed. Then the MTU is - * recalculated and a fresh queue is initialized. - * - * @param peer peer to setup MQ for - */ -static void -setup_peer_mq (struct PeerAddress *peer) -{ - size_t base_mtu; - - switch (peer->address->sa_family) - { - case AF_INET: - base_mtu = 1480 /* Ethernet MTU, 1500 - Ethernet header - VLAN tag */ - - sizeof(struct GNUNET_TUN_IPv4Header) /* 20 */ - - sizeof(struct GNUNET_TUN_UdpHeader) /* 8 */; - break; - - case AF_INET6: - base_mtu = 1280 /* Minimum MTU required by IPv6 */ - - sizeof(struct GNUNET_TUN_IPv6Header) /* 40 */ - - sizeof(struct GNUNET_TUN_UdpHeader) /* 8 */; - break; - - default: - GNUNET_assert (0); - break; - } - /* MTU == base_mtu */ - peer->d_mtu = base_mtu; - - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "Setting up MQs and QHs\n"); - /* => Effective MTU for CORE will range from 1080 (IPv6 + KX) to - 1404 (IPv4 + Box) bytes, depending on circumstances... */ - - if (NULL == peer->d_mq) - peer->d_mq = GNUNET_MQ_queue_for_callbacks (&mq_send_d, - &mq_destroy_d, - &mq_cancel, - peer, - NULL, - &mq_error, - peer); - peer->d_qh = - GNUNET_TRANSPORT_communicator_mq_add (ch, - &peer->target, - peer->foreign_addr, - 1000, - GNUNET_TRANSPORT_QUEUE_LENGTH_UNLIMITED, - 0, /* Priority */ - peer->nt, - GNUNET_TRANSPORT_CS_OUTBOUND, - peer->d_mq); -} - - -/** - * Taken from: UDP communicator - * Converts @a address to the address string format used by this - * communicator in HELLOs. - * - * @param address the address to convert, must be AF_INET or AF_INET6. - * @param address_len number of bytes in @a address - * @return string representation of @a address - */ -static char * -sockaddr_to_udpaddr_string (const struct sockaddr *address, - socklen_t address_len) -{ - char *ret; - - switch (address->sa_family) - { - case AF_INET: - GNUNET_asprintf (&ret, - "%s-%s", - COMMUNICATOR_ADDRESS_PREFIX, - GNUNET_a2s (address, address_len)); - break; - - case AF_INET6: - GNUNET_asprintf (&ret, - "%s-%s", - COMMUNICATOR_ADDRESS_PREFIX, - GNUNET_a2s (address, address_len)); - break; - - default: - GNUNET_assert (0); - } - return ret; -} - - -/** - * Function called when the transport service has received a - * backchannel message for this communicator (!) via a different return - * path. Should be an acknowledgement. - * - * @param cls closure, NULL - * @param sender which peer sent the notification - * @param msg payload - */ -static void -notify_cb (void *cls, - const struct GNUNET_PeerIdentity *sender, - const struct GNUNET_MessageHeader *msg) -{ - // const struct UDPAck *ack; - - // (void) cls; - // GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - // "Storing UDPAck received from backchannel from %s\n", - // GNUNET_i2s_full (sender)); - // if ((ntohs (msg->type) != GNUNET_MESSAGE_TYPE_COMMUNICATOR_UDP_ACK) || - // (ntohs (msg->size) != sizeof(struct UDPAck))) - // { - // GNUNET_break_op (0); - // return; - // } - // ack = (const struct UDPAck *) msg; - // GNUNET_CONTAINER_multipeermap_get_multiple (receivers, - // sender, - // &handle_ack, - // (void *) ack); -} - - -/** - * Task run to check #receiver_heap and #sender_heap for timeouts. - * - * @param cls unused, NULL - */ -static void -check_timeouts (void *cls) -{ - // struct GNUNET_TIME_Relative st; - // struct GNUNET_TIME_Relative rt; - // struct GNUNET_TIME_Relative delay; - // struct ReceiverAddress *receiver; - // struct SenderAddress *sender; - - // (void) cls; - // timeout_task = NULL; - // rt = GNUNET_TIME_UNIT_FOREVER_REL; - // while (NULL != (receiver = GNUNET_CONTAINER_heap_peek (receivers_heap))) - // { - // /* if (GNUNET_YES != receiver->receiver_destroy_called) */ - // /* { */ - // rt = GNUNET_TIME_absolute_get_remaining (receiver->timeout); - // if (0 != rt.rel_value_us) - // break; - // GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - // "Receiver timed out\n"); - // receiver_destroy (receiver); - // // } - // } - // st = GNUNET_TIME_UNIT_FOREVER_REL; - // while (NULL != (sender = GNUNET_CONTAINER_heap_peek (senders_heap))) - // { - // if (GNUNET_YES != sender->sender_destroy_called) - // { - // st = GNUNET_TIME_absolute_get_remaining (sender->timeout); - // if (0 != st.rel_value_us) - // break; - // sender_destroy (sender); - // } - // } - // delay = GNUNET_TIME_relative_min (rt, st); - // if (delay.rel_value_us < GNUNET_TIME_UNIT_FOREVER_REL.rel_value_us) - // timeout_task = GNUNET_SCHEDULER_add_delayed (delay, &check_timeouts, NULL); -} - - -/** - * Function called by the transport service to initialize a - * message queue given address information about another peer. - * If and when the communication channel is established, the - * communicator must call #GNUNET_TRANSPORT_communicator_mq_add() - * to notify the service that the channel is now up. It is - * the responsibility of the communicator to manage sane - * retries and timeouts for any @a peer/@a address combination - * provided by the transport service. Timeouts and retries - * do not need to be signalled to the transport service. - * - * @param cls closure - * @param peer identity of the other peer - * @param address where to send the message, human-readable - * communicator-specific format, 0-terminated, UTF-8 - * @return #GNUNET_OK on success, #GNUNET_SYSERR if the provided address is - * invalid - */ -static int -mq_init (void *cls, const struct GNUNET_PeerIdentity *peer_id, const - char *address) -{ - struct PeerAddress *peer; - const char *path; - struct sockaddr *in; - socklen_t in_len; - struct GNUNET_HashCode addr_key; - uint8_t scid[LOCAL_CONN_ID_LEN]; - - struct quic_conn *q_conn; - char *bindto; - socklen_t local_in_len; - struct sockaddr *local_addr; - - if (GNUNET_OK != - GNUNET_CONFIGURATION_get_value_string (cfg, - COMMUNICATOR_CONFIG_SECTION, - "BINDTO", - &bindto)) - { - GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR, - COMMUNICATOR_CONFIG_SECTION, - "BINDTO"); - return GNUNET_SYSERR; - } - local_addr = udp_address_to_sockaddr (bindto, &local_in_len); - - if (0 != strncmp (address, - COMMUNICATOR_ADDRESS_PREFIX "-", - strlen (COMMUNICATOR_ADDRESS_PREFIX "-"))) - { - GNUNET_break_op (0); - return GNUNET_SYSERR; - } - path = &address[strlen (COMMUNICATOR_ADDRESS_PREFIX "-")]; - in = udp_address_to_sockaddr (path, &in_len); - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "mq_init in_len length before: %d\n", - in_len); - /** - * If we already have a queue with this peer, ignore - */ - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "address string in mq_init: %s\n", - address); - GNUNET_CRYPTO_hash (address, strlen (address), &addr_key); - peer = GNUNET_CONTAINER_multihashmap_get (addr_map, &addr_key); - if (NULL != peer) - { - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "ignoring transport service mq request, we already have an mq with this peer (address)\n"); - return GNUNET_SYSERR; - } - peer = GNUNET_new (struct PeerAddress); - peer->address = in; - peer->address_len = in_len; - peer->target = *peer_id; - peer->id_rcvd = GNUNET_YES; - peer->is_receiver = GNUNET_YES; - peer->nt = GNUNET_NT_scanner_get_type (is, in, in_len); - peer->timeout = - GNUNET_TIME_relative_to_absolute (GNUNET_CONSTANTS_IDLE_CONNECTION_TIMEOUT); - GNUNET_STATISTICS_set (stats, - "# peers active", - GNUNET_CONTAINER_multihashmap_size (addr_map), - GNUNET_NO); - peer->foreign_addr = - sockaddr_to_udpaddr_string (peer->address, peer->address_len); - /** - * Insert peer into hashmap - */ - GNUNET_CONTAINER_multihashmap_put (addr_map, &addr_key, - peer, - GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_ONLY); - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "mq_init added new peer to the addr map\n"); - /** - * Before setting up peer mq, initiate a quic connection to the target (perform handshake w/ quiche) - */ - GNUNET_CRYPTO_random_block (GNUNET_CRYPTO_QUALITY_STRONG, scid, - LOCAL_CONN_ID_LEN); - q_conn = GNUNET_new (struct quic_conn); - GNUNET_memcpy (q_conn->cid, scid, LOCAL_CONN_ID_LEN); - peer->conn = q_conn; - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "attempting to perform QUIC handshake with peer\n"); - q_conn->conn = quiche_connect (peer->foreign_addr, scid, LOCAL_CONN_ID_LEN, - local_addr, - local_in_len, peer->address, peer->address_len, - config); - flush_egress (peer->conn); - GNUNET_free (local_addr); - return GNUNET_OK; - /** - * TODO: handle this - */ - // if (NULL == timeout_task) - // timeout_task = GNUNET_SCHEDULER_add_now (&check_timeouts, NULL); -} - - -static void -try_connection_reversal (void *cls, - const struct sockaddr *addr, - socklen_t addrlen) -{ - /* FIXME: support reversal: #5529 */ - GNUNET_log (GNUNET_ERROR_TYPE_INFO, - "No connection reversal implemented!"); -} - - -/** - * Signature of the callback passed to #GNUNET_NAT_register() for - * a function to call whenever our set of 'valid' addresses changes. - * - * @param cls closure - * @param app_ctx[in,out] location where the app can store stuff - * on add and retrieve it on remove - * @param add_remove #GNUNET_YES to add a new public IP address, - * #GNUNET_NO to remove a previous (now invalid) one - * @param ac address class the address belongs to - * @param addr either the previous or the new public IP address - * @param addrlen actual length of the @a addr - */ -static void -nat_address_cb (void *cls, - void **app_ctx, - int add_remove, - enum GNUNET_NAT_AddressClass ac, - const struct sockaddr *addr, - socklen_t addrlen) -{ - char *my_addr; - struct GNUNET_TRANSPORT_AddressIdentifier *ai; - - if (GNUNET_YES == add_remove) - { - enum GNUNET_NetworkType nt; - - GNUNET_asprintf (&my_addr, - "%s-%s", - COMMUNICATOR_ADDRESS_PREFIX, - GNUNET_a2s (addr, addrlen)); - nt = GNUNET_NT_scanner_get_type (is, addr, addrlen); - ai = - GNUNET_TRANSPORT_communicator_address_add (ch, - my_addr, - nt, - GNUNET_TIME_UNIT_FOREVER_REL); - GNUNET_free (my_addr); - *app_ctx = ai; - } - else - { - ai = *app_ctx; - GNUNET_TRANSPORT_communicator_address_remove (ai); - *app_ctx = NULL; - } -} - - -/** - * Shutdown the QUIC communicator. - * - * @param cls NULL (always) - */ -static void -do_shutdown (void *cls) -{ - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "do_shutdown\n"); - GNUNET_CONTAINER_multihashmap_iterate (addr_map, &get_peer_delete_it, NULL); - GNUNET_CONTAINER_multihashmap_destroy (addr_map); - quiche_config_free (config); - - if (NULL != timeout_task) - { - GNUNET_SCHEDULER_cancel (timeout_task); - timeout_task = NULL; - } - if (NULL != read_task) - { - GNUNET_SCHEDULER_cancel (read_task); - read_task = NULL; - } - if (NULL != udp_sock) - { - GNUNET_break (GNUNET_OK == - GNUNET_NETWORK_socket_close (udp_sock)); - udp_sock = NULL; - } - if (NULL != ch) - { - GNUNET_TRANSPORT_communicator_disconnect (ch); - ch = NULL; - } - if (NULL != ah) - { - GNUNET_TRANSPORT_application_done (ah); - ah = NULL; - } - if (NULL != my_private_key) - { - GNUNET_free (my_private_key); - my_private_key = NULL; - } - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "do_shutdown finished\n"); -} - - -static void -sock_read (void *cls) -{ - struct sockaddr_storage sa; - struct sockaddr_in *addr_verify; - socklen_t salen = sizeof(sa); - uint8_t buf[UINT16_MAX]; - uint8_t out[MAX_DATAGRAM_SIZE]; - ssize_t rcvd; - - ssize_t process_pkt; - struct QUIC_header quic_header; - uint8_t new_cid[LOCAL_CONN_ID_LEN]; - - struct PeerAddress *peer; - struct GNUNET_HashCode addr_key; - - (void) cls; - quic_header.scid_len = sizeof(quic_header.scid); - quic_header.dcid_len = sizeof(quic_header.dcid); - quic_header.odcid_len = sizeof(quic_header.odcid); - quic_header.token_len = sizeof(quic_header.token); - /** - * Get local_addr, in_len for quiche - */ - char *bindto; - socklen_t in_len; - if (GNUNET_OK != - GNUNET_CONFIGURATION_get_value_string (cfg, - COMMUNICATOR_CONFIG_SECTION, - "BINDTO", - &bindto)) - { - GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR, - COMMUNICATOR_CONFIG_SECTION, - "BINDTO"); - return; - } - struct sockaddr *local_addr = udp_address_to_sockaddr (bindto, &in_len); - - read_task = GNUNET_SCHEDULER_add_read_net (GNUNET_TIME_UNIT_FOREVER_REL, - udp_sock, - &sock_read, - NULL); - while (1) - { - rcvd = GNUNET_NETWORK_socket_recvfrom (udp_sock, - buf, - sizeof(buf), - (struct sockaddr *) &sa, - &salen); - if (-1 == rcvd) - { - if (EAGAIN == errno) - break; // We are done reading data - GNUNET_log_strerror (GNUNET_ERROR_TYPE_DEBUG, "recv"); - return; - } - - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "Read %lu bytes\n", rcvd); - - if (-1 == rcvd) - { - GNUNET_log_strerror (GNUNET_ERROR_TYPE_DEBUG, "recv"); - return; - } - /** - * FIXME: hashing address string vs ip/port. It is not ideal that - * we hash the string, instead of the binary representation, but - * for now it is certainly less code. - * Note that simply hashing the sockaddr does NOT work because the - * the struct is not portable. - */ - const char *addr_string = sockaddr_to_udpaddr_string ((const struct - sockaddr *) &sa, - salen); - GNUNET_CRYPTO_hash (addr_string, strlen (addr_string), - &addr_key); - GNUNET_free (addr_string); - peer = GNUNET_CONTAINER_multihashmap_get (addr_map, &addr_key); - - if (NULL == peer) - { - /** - * Create new PeerAddress (receiver) with id_rcvd = false - */ - peer = GNUNET_new (struct PeerAddress); - peer->address = GNUNET_memdup (&sa, salen); - peer->address_len = salen; - peer->id_rcvd = GNUNET_NO; - peer->id_sent = GNUNET_NO; - peer->is_receiver = GNUNET_NO; - peer->conn = NULL; - peer->foreign_addr = sockaddr_to_udpaddr_string (peer->address, - peer->address_len); - /** - * TODO: after connection established - */ - // setup_peer_mq (peer); - if (GNUNET_SYSERR == GNUNET_CONTAINER_multihashmap_put (addr_map, - &addr_key, - peer, - GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_ONLY)) - { - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - "tried to add duplicate address into address map\n"); - return; - } - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "sock_read added new peer to address map\n"); - } - - /** - * Parse QUIC info - */ - int rc = quiche_header_info (buf, rcvd, LOCAL_CONN_ID_LEN, - &quic_header.version, - &quic_header.type, quic_header.scid, - &quic_header.scid_len, quic_header.dcid, - &quic_header.dcid_len, - quic_header.token, &quic_header.token_len); - if (0 > rc) - { - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - "failed to parse quic header: %d\n", - rc); - return; - } - - /** - * New QUIC connection with peer - */ - if (NULL == peer->conn) - { - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "attempting to create new connection\n"); - if (0 == quiche_version_is_supported (quic_header.version)) - { - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "quic version negotiation initiated\n"); - /** - * FIXME variables are redeclared often. Refactor either - * to declare variables once in the beginning or refactor into - * method. - * - * Write a version negotiation packet to "out" - */ - ssize_t written = quiche_negotiate_version (quic_header.scid, - quic_header.scid_len, - quic_header.dcid, - quic_header.dcid_len, - out, sizeof(out)); - if (0 > written) - { - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - "quiche failed to generate version negotiation packet\n"); - return; - } - ssize_t sent = GNUNET_NETWORK_socket_sendto (udp_sock, - out, - written, - (struct sockaddr*) &sa, - salen); - if (sent != written) - { - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - "failed to send version negotiation packet to peer\n"); - return; - } - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "sent %zd bytes to peer during version negotiation\n", - sent); - return; - } - - if (0 == quic_header.token_len) - { - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "quic stateless retry\n"); - mint_token (quic_header.dcid, quic_header.dcid_len, &sa, salen, - quic_header.token, &quic_header.token_len); - - uint8_t new_cid[LOCAL_CONN_ID_LEN]; - GNUNET_CRYPTO_random_block (GNUNET_CRYPTO_QUALITY_STRONG, new_cid, - LOCAL_CONN_ID_LEN); - - ssize_t written = quiche_retry (quic_header.scid, quic_header.scid_len, - quic_header.dcid, quic_header.dcid_len, - new_cid, LOCAL_CONN_ID_LEN, - quic_header.token, - quic_header.token_len, - quic_header.version, out, sizeof(out)); - if (0 > written) - { - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - "quiche failed to write retry packet\n"); - return; - } - ssize_t sent = GNUNET_NETWORK_socket_sendto (udp_sock, - out, - written, - (struct sockaddr*) &sa, - salen); - if (written != sent) - { - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "failed to send retry packet\n"); - return; - } - - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "sent %zd bytes\n", sent); - continue; - } - - if (GNUNET_OK != validate_token (quic_header.token, quic_header.token_len, - &sa, salen, - quic_header.odcid, - &quic_header.odcid_len)) - { - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - "invalid address validation token created\n"); - return; - } - peer->conn = create_conn (quic_header.dcid, quic_header.dcid_len, - quic_header.odcid, quic_header.odcid_len, - local_addr, in_len, - &sa, salen); - if (NULL == peer->conn) - { - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - "failed to create quic connection with peer\n"); - return; - } - } // null connection - - quiche_recv_info recv_info = { - (struct sockaddr *) &sa, - salen, - - local_addr, - in_len, - }; - /** - * Send our PeerIdentity if the connection is established now - */ - if (quiche_conn_is_established (peer->conn->conn) && ! peer->id_sent && - peer->is_receiver) - { - ssize_t send_len; - - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "handshake established with peer, sending our peer id\n"); - send_len = quiche_conn_stream_send (peer->conn->conn, STREAMID_BI, - (const uint8_t *) &my_identity, - sizeof(my_identity), - false); - if (0 > send_len) - { - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - "failed to write peer identity packet. quiche error: %zd\n", - send_len); - return; - } - flush_egress (peer->conn); - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "peer identity sent to peer\n"); - peer->id_sent = GNUNET_YES; - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "setting up peer mq\n"); - setup_peer_mq (peer); - /** - * After this, we should be all good to send/recv data - */ - } - process_pkt = quiche_conn_recv (peer->conn->conn, buf, rcvd, &recv_info); - if (0 > process_pkt) - { - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - "quiche failed to process received packet: %zd\n", - process_pkt); - return; - } - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "quiche processed %zd bytes\n", process_pkt); - // Check for data on all available streams if the connection is established - if (GNUNET_YES == quiche_conn_is_established (peer->conn->conn)) - { - recv_from_streams (peer); - } - /** - * TODO: Should we use a list instead of hashmap? - * Overhead for hashing function, O(1) retrieval vs O(n) iteration with n=30? - * - * TODO: Is iteration necessary as in the quiche server example? - */ - quiche_stats stats; - quiche_path_stats path_stats; - - flush_egress (peer->conn); - - if (quiche_conn_is_closed (peer->conn->conn)) - { - quiche_conn_stats (peer->conn->conn, &stats); - quiche_conn_path_stats (peer->conn->conn, 0, &path_stats); - - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "connection closed. quiche stats: sent=%zu, recv=%zu\n", - stats.sent, stats.recv); - peer_destroy (peer); - } - } - GNUNET_free (local_addr); -} - - -/** - * Setup communicator and launch network interactions. - * - * @param cls NULL (always) - * @param args remaining command-line arguments - * @param cfgfile name of the configuration file used (for saving, can be NULL!) - * @param c configuration - */ -static void -run (void *cls, - char *const *args, - const char *cfgfile, - const struct GNUNET_CONFIGURATION_Handle *c) -{ - char *bindto; - struct sockaddr *in; - socklen_t in_len; - struct sockaddr_storage in_sto; - socklen_t sto_len; - - (void) cls; - cfg = c; - - if (GNUNET_OK != - GNUNET_CONFIGURATION_get_value_string (cfg, - COMMUNICATOR_CONFIG_SECTION, - "BINDTO", - &bindto)) - { - GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR, - COMMUNICATOR_CONFIG_SECTION, - "BINDTO"); - return; - } - - in = udp_address_to_sockaddr (bindto, &in_len); - - if (NULL == in) - { - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - "Failed to setup UDP socket address with path `%s'\n", - bindto); - GNUNET_free (bindto); - return; - } - udp_sock = - GNUNET_NETWORK_socket_create (in->sa_family, - SOCK_DGRAM, - IPPROTO_UDP); - if (NULL == udp_sock) - { - GNUNET_log_strerror (GNUNET_ERROR_TYPE_ERROR, "socket"); - GNUNET_free (in); - GNUNET_free (bindto); - return; - } - if (AF_INET6 == in->sa_family) - have_v6_socket = GNUNET_YES; - if (GNUNET_OK != - GNUNET_NETWORK_socket_bind (udp_sock, - in, - in_len)) - { - GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_ERROR, - "bind", - bindto); - GNUNET_NETWORK_socket_close (udp_sock); - udp_sock = NULL; - GNUNET_free (in); - GNUNET_free (bindto); - return; - } - sto_len = sizeof(in_sto); - if (0 != getsockname (GNUNET_NETWORK_get_fd (udp_sock), - (struct sockaddr *) &in_sto, - &sto_len)) - { - memcpy (&in_sto, in, in_len); - sto_len = in_len; - } - GNUNET_free (in); - GNUNET_free (bindto); - in = (struct sockaddr *) &in_sto; - in_len = sto_len; - GNUNET_log_from_nocheck (GNUNET_ERROR_TYPE_DEBUG, - "transport", - "Bound to `%s'\n", - GNUNET_a2s ((const struct sockaddr *) &in_sto, - sto_len)); - switch (in->sa_family) - { - case AF_INET: - my_port = ntohs (((struct sockaddr_in *) in)->sin_port); - break; - - case AF_INET6: - my_port = ntohs (((struct sockaddr_in6 *) in)->sin6_port); - break; - - default: - GNUNET_break (0); - my_port = 0; - } - GNUNET_SCHEDULER_add_shutdown (&do_shutdown, NULL); - /** - * Setup QUICHE configuration - */ - config = quiche_config_new (QUICHE_PROTOCOL_VERSION); - quiche_config_verify_peer (config, false); - /** - * TODO: configure TLS cert - */ - quiche_config_load_cert_chain_from_pem_file (config, "./cert.crt"); - quiche_config_load_priv_key_from_pem_file (config, "./cert.key"); - quiche_config_set_application_protos (config, - (uint8_t *) - "\x0ahq-interop\x05hq-29\x05hq-28\x05hq-27\x08http/0.9", - 38); - quiche_config_set_max_idle_timeout (config, 5000); - quiche_config_set_max_recv_udp_payload_size (config, 1200); - quiche_config_set_max_send_udp_payload_size (config, 1200); - quiche_config_set_initial_max_data (config, 10000000); - quiche_config_set_initial_max_stream_data_bidi_local (config, 1000000); - quiche_config_set_initial_max_stream_data_bidi_remote (config, 1000000); - quiche_config_set_initial_max_stream_data_uni (config, 1000000); - quiche_config_set_initial_max_streams_bidi (config, 100); - quiche_config_set_initial_max_streams_uni (config, 100); - quiche_config_set_cc_algorithm (config, QUICHE_CC_RENO); - quiche_config_set_disable_active_migration (config, true); - addr_map = GNUNET_CONTAINER_multihashmap_create (2, GNUNET_NO); - /** - * Get our public key for initial packet - */ - my_private_key = GNUNET_CRYPTO_eddsa_key_create_from_configuration (cfg); - if (NULL == my_private_key) - { - GNUNET_log ( - GNUNET_ERROR_TYPE_ERROR, - _ ( - "Transport service is lacking key configuration settings. Exiting.\n")); - GNUNET_SCHEDULER_shutdown (); - return; - } - GNUNET_CRYPTO_eddsa_key_get_public (my_private_key, &my_identity.public_key); - /* start reading */ - read_task = GNUNET_SCHEDULER_add_read_net (GNUNET_TIME_UNIT_FOREVER_REL, - udp_sock, - &sock_read, - NULL); - ch = GNUNET_TRANSPORT_communicator_connect (cfg, - COMMUNICATOR_CONFIG_SECTION, - COMMUNICATOR_ADDRESS_PREFIX, - GNUNET_TRANSPORT_CC_RELIABLE, - &mq_init, - NULL, - ¬ify_cb, - NULL); - is = GNUNET_NT_scanner_init (); - nat = GNUNET_NAT_register (cfg, - COMMUNICATOR_CONFIG_SECTION, - IPPROTO_UDP, - 1 /* one address */, - (const struct sockaddr **) &in, - &in_len, - &nat_address_cb, - try_connection_reversal, - NULL /* closure */); - if (NULL == ch) - { - GNUNET_break (0); - GNUNET_SCHEDULER_shutdown (); - return; - } - ah = GNUNET_TRANSPORT_application_init (cfg); - if (NULL == ah) - { - GNUNET_break (0); - GNUNET_SCHEDULER_shutdown (); - return; - } - - /* start broadcasting */ - // if (GNUNET_YES != - // GNUNET_CONFIGURATION_get_value_yesno (cfg, - // COMMUNICATOR_CONFIG_SECTION, - // "DISABLE_BROADCAST")) - // { - // broadcast_task = GNUNET_SCHEDULER_add_now (&do_broadcast, NULL); - // } -} - - -int -main (int argc, char *const *argv) -{ - static const struct GNUNET_GETOPT_CommandLineOption options[] = { - GNUNET_GETOPT_OPTION_END - }; - int ret; - - GNUNET_log_from_nocheck (GNUNET_ERROR_TYPE_DEBUG, - "transport", - "Starting quic communicator\n"); - if (GNUNET_OK != GNUNET_STRINGS_get_utf8_args (argc, argv, &argc, &argv)) - return 2; - - ret = (GNUNET_OK == GNUNET_PROGRAM_run (argc, - argv, - "gnunet-communicator-quic", - _ ("GNUnet QUIC communicator"), - options, - &run, - NULL)) - ? 0 - : 1; - GNUNET_free_nz ((void *) argv); - return ret; -} diff --git a/src/transport/gnunet-communicator-tcp.c b/src/transport/gnunet-communicator-tcp.c deleted file mode 100644 index e7d989021..000000000 --- a/src/transport/gnunet-communicator-tcp.c +++ /dev/null @@ -1,4082 +0,0 @@ -/* - This file is part of GNUnet - Copyright (C) 2010-2014, 2018, 2019 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 . - - SPDX-License-Identifier: AGPL3.0-or-later - */ - -/** - * @file transport/gnunet-communicator-tcp.c - * @brief Transport plugin using TCP. - * @author Christian Grothoff - * - * TODO: - * - support NAT connection reversal method (#5529) - * - support other TCP-specific NAT traversal methods (#5531) - */ -#include "platform.h" -#include "gnunet_util_lib.h" -#include "gnunet_core_service.h" -#include "gnunet_peerstore_service.h" -#include "gnunet_protocols.h" -#include "gnunet_signatures.h" -#include "gnunet_constants.h" -#include "gnunet_nat_service.h" -#include "gnunet_statistics_service.h" -#include "gnunet_transport_communication_service.h" -#include "gnunet_resolver_service.h" - - -/** - * How long until we give up on establishing an NAT connection? - * Must be > 4 RTT - */ -#define NAT_TIMEOUT GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS, 10) - -/** - * How long do we believe our addresses to remain up (before - * the other peer should revalidate). - */ -#define ADDRESS_VALIDITY_PERIOD \ - GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_HOURS, 4) - -/** - * How many messages do we keep at most in the queue to the - * transport service before we start to drop (default, - * can be changed via the configuration file). - * Should be _below_ the level of the communicator API, as - * otherwise we may read messages just to have them dropped - * by the communicator API. - */ -#define DEFAULT_MAX_QUEUE_LENGTH 8 - -/** - * Size of our IO buffers for ciphertext data. Must be at - * least UINT_MAX + sizeof (struct TCPBox). - */ -#define BUF_SIZE (2 * 64 * 1024 + sizeof(struct TCPBox)) - -/** - * How often do we rekey based on time (at least) - */ -#define DEFAULT_REKEY_INTERVAL GNUNET_TIME_UNIT_DAYS - -/** - * How long do we wait until we must have received the initial KX? - */ -#define PROTO_QUEUE_TIMEOUT GNUNET_TIME_UNIT_MINUTES - -/** - * How often do we rekey based on number of bytes transmitted? - * (additionally randomized). Currently 400 MB - */ -#define REKEY_MAX_BYTES (1024LLU * 1024 * 400) - -/** - * Size of the initial key exchange message sent first in both - * directions. - */ -#define INITIAL_KX_SIZE \ - (sizeof(struct GNUNET_CRYPTO_EcdhePublicKey) \ - + sizeof(struct TCPConfirmation)) - -/** - * Size of the initial core key exchange messages. - */ -#define INITIAL_CORE_KX_SIZE \ - (sizeof(struct EphemeralKeyMessage) \ - + sizeof(struct PingMessage) \ - + sizeof(struct PongMessage)) - -/** - * Address prefix used by the communicator. - */ -#define COMMUNICATOR_ADDRESS_PREFIX "tcp" - -/** - * Configuration section used by the communicator. - */ -#define COMMUNICATOR_CONFIG_SECTION "communicator-tcp" - -GNUNET_NETWORK_STRUCT_BEGIN - - -/** - * Signature we use to verify that the ephemeral key was really chosen by - * the specified sender. - */ -struct TcpHandshakeSignature -{ - /** - * Purpose must be #GNUNET_SIGNATURE_PURPOSE_COMMUNICATOR_TCP_HANDSHAKE - */ - struct GNUNET_CRYPTO_EccSignaturePurpose purpose; - - /** - * Identity of the inititor of the TCP connection (TCP client). - */ - struct GNUNET_PeerIdentity sender; - - /** - * Presumed identity of the target of the TCP connection (TCP server) - */ - struct GNUNET_PeerIdentity receiver; - - /** - * Ephemeral key used by the @e sender. - */ - struct GNUNET_CRYPTO_EcdhePublicKey ephemeral; - - /** - * Monotonic time of @e sender, to possibly help detect replay attacks - * (if receiver persists times by sender). - */ - struct GNUNET_TIME_AbsoluteNBO monotonic_time; - - /** - * Challenge value used to protect against replay attack, if there is no stored monotonic time value. - */ - struct GNUNET_CRYPTO_ChallengeNonceP challenge; -}; - -/** - * Signature we use to verify that the ack from the receiver of the ephemeral key was really send by - * the specified sender. - */ -struct TcpHandshakeAckSignature -{ - /** - * Purpose must be #GNUNET_SIGNATURE_PURPOSE_COMMUNICATOR_TCP_HANDSHAKE_ACK - */ - struct GNUNET_CRYPTO_EccSignaturePurpose purpose; - - /** - * Identity of the inititor of the TCP connection (TCP client). - */ - struct GNUNET_PeerIdentity sender; - - /** - * Presumed identity of the target of the TCP connection (TCP server) - */ - struct GNUNET_PeerIdentity receiver; - - /** - * Monotonic time of @e sender, to possibly help detect replay attacks - * (if receiver persists times by sender). - */ - struct GNUNET_TIME_AbsoluteNBO monotonic_time; - - /** - * Challenge value used to protect against replay attack, if there is no stored monotonic time value. - */ - struct GNUNET_CRYPTO_ChallengeNonceP challenge; -}; - -/** - * Encrypted continuation of TCP initial handshake. - */ -struct TCPConfirmation -{ - /** - * Sender's identity - */ - struct GNUNET_PeerIdentity sender; - - /** - * Sender's signature of type #GNUNET_SIGNATURE_PURPOSE_COMMUNICATOR_TCP_HANDSHAKE - */ - struct GNUNET_CRYPTO_EddsaSignature sender_sig; - - /** - * Monotonic time of @e sender, to possibly help detect replay attacks - * (if receiver persists times by sender). - */ - struct GNUNET_TIME_AbsoluteNBO monotonic_time; - - /** - * Challenge value used to protect against replay attack, if there is no stored monotonic time value. - */ - struct GNUNET_CRYPTO_ChallengeNonceP challenge; - -}; - -/** - * Ack for the encrypted continuation of TCP initial handshake. - */ -struct TCPConfirmationAck -{ - - - /** - * Type is #GNUNET_MESSAGE_TYPE_COMMUNICATOR_TCP_CONFIRMATION_ACK. - */ - struct GNUNET_MessageHeader header; - - /** - * Sender's identity - */ - struct GNUNET_PeerIdentity sender; - - /** - * Sender's signature of type #GNUNET_SIGNATURE_PURPOSE_COMMUNICATOR_TCP_HANDSHAKE_ACK - */ - struct GNUNET_CRYPTO_EddsaSignature sender_sig; - - /** - * Monotonic time of @e sender, to possibly help detect replay attacks - * (if receiver persists times by sender). - */ - struct GNUNET_TIME_AbsoluteNBO monotonic_time; - - /** - * Challenge value used to protect against replay attack, if there is no stored monotonic time value. - */ - struct GNUNET_CRYPTO_ChallengeNonceP challenge; - -}; - -/** - * TCP message box. Always sent encrypted! - */ -struct TCPBox -{ - /** - * Type is #GNUNET_MESSAGE_TYPE_COMMUNICATOR_TCP_BOX. Warning: the - * header size EXCLUDES the size of the `struct TCPBox`. We usually - * never do this, but here the payload may truly be 64k *after* the - * TCPBox (as we have no MTU)!! - */ - struct GNUNET_MessageHeader header; - - /** - * HMAC for the following encrypted message. Yes, we MUST use - * mac-then-encrypt here, as we want to hide the message sizes on - * the wire (zero plaintext design!). Using CTR mode, padding oracle - * attacks do not apply. Besides, due to the use of ephemeral keys - * (hopefully with effective replay protection from monotonic time!) - * the attacker is limited in using the oracle. - */ - struct GNUNET_ShortHashCode hmac; - - /* followed by as may bytes of payload as indicated in @e header, - excluding the TCPBox itself! */ -}; - - -/** - * TCP rekey message box. Always sent encrypted! Data after - * this message will use the new key. - */ -struct TCPRekey -{ - /** - * Type is #GNUNET_MESSAGE_TYPE_COMMUNICATOR_TCP_REKEY. - */ - struct GNUNET_MessageHeader header; - - /** - * HMAC for the following encrypted message. Yes, we MUST use - * mac-then-encrypt here, as we want to hide the message sizes on - * the wire (zero plaintext design!). Using CTR mode padding oracle - * attacks do not apply. Besides, due to the use of ephemeral keys - * (hopefully with effective replay protection from monotonic time!) - * the attacker is limited in using the oracle. - */ - struct GNUNET_ShortHashCode hmac; - - /** - * New ephemeral key. - */ - struct GNUNET_CRYPTO_EcdhePublicKey ephemeral; - - /** - * Sender's signature of type #GNUNET_SIGNATURE_PURPOSE_COMMUNICATOR_TCP_REKEY - */ - struct GNUNET_CRYPTO_EddsaSignature sender_sig; - - /** - * Monotonic time of @e sender, to possibly help detect replay attacks - * (if receiver persists times by sender). - */ - struct GNUNET_TIME_AbsoluteNBO monotonic_time; -}; - -/** - * Signature we use to verify that the ephemeral key was really chosen by - * the specified sender. - */ -struct TcpRekeySignature -{ - /** - * Purpose must be #GNUNET_SIGNATURE_PURPOSE_COMMUNICATOR_TCP_REKEY - */ - struct GNUNET_CRYPTO_EccSignaturePurpose purpose; - - /** - * Identity of the inititor of the TCP connection (TCP client). - */ - struct GNUNET_PeerIdentity sender; - - /** - * Presumed identity of the target of the TCP connection (TCP server) - */ - struct GNUNET_PeerIdentity receiver; - - /** - * Ephemeral key used by the @e sender. - */ - struct GNUNET_CRYPTO_EcdhePublicKey ephemeral; - - /** - * Monotonic time of @e sender, to possibly help detect replay attacks - * (if receiver persists times by sender). - */ - struct GNUNET_TIME_AbsoluteNBO monotonic_time; -}; - -/** - * TCP finish. Sender asks for the connection to be closed. - * Needed/useful in case we drop RST/FIN packets on the GNUnet - * port due to the possibility of malicious RST/FIN injection. - */ -struct TCPFinish -{ - /** - * Type is #GNUNET_MESSAGE_TYPE_COMMUNICATOR_TCP_FINISH. - */ - struct GNUNET_MessageHeader header; - - /** - * HMAC for the following encrypted message. Yes, we MUST use - * mac-then-encrypt here, as we want to hide the message sizes on - * the wire (zero plaintext design!). Using CTR mode padding oracle - * attacks do not apply. Besides, due to the use of ephemeral keys - * (hopefully with effective replay protection from monotonic time!) - * the attacker is limited in using the oracle. - */ - struct GNUNET_ShortHashCode hmac; -}; - -/** - * Basically a WELCOME message, but with the purpose - * of giving the waiting peer a client handle to use - */ -struct TCPNATProbeMessage -{ - /** - * Type is #GNUNET_MESSAGE_TYPE_TRANSPORT_TCP_NAT_PROBE. - */ - struct GNUNET_MessageHeader header; - - /** - * Identity of the sender of the message. - */ - struct GNUNET_PeerIdentity clientIdentity; -}; - -GNUNET_NETWORK_STRUCT_END - -/** - * Struct for pending nat reversals. - */ -struct PendingReversal -{ - /* - * Timeout task. - */ - struct GNUNET_SCHEDULER_Task *timeout_task; - - /** - * To whom are we like to talk to. - */ - struct GNUNET_PeerIdentity target; - - /** - * Address the reversal was send to. - */ - struct sockaddr *in; -}; - -/** - * Struct to use as closure. - */ -struct ListenTask -{ - /** - * ID of listen task - */ - struct GNUNET_SCHEDULER_Task *listen_task; - - /** - * Listen socket. - */ - struct GNUNET_NETWORK_Handle *listen_sock; -}; - -/** - * Handle for a queue. - */ -struct Queue -{ - /** - * To whom are we talking to. - */ - struct GNUNET_PeerIdentity target; - - /** - * Listen socket. - */ - struct GNUNET_NETWORK_Handle *listen_sock; - - /** - * socket that we transmit all data with on this queue - */ - struct GNUNET_NETWORK_Handle *sock; - - /** - * cipher for decryption of incoming data. - */ - gcry_cipher_hd_t in_cipher; - - /** - * cipher for encryption of outgoing data. - */ - gcry_cipher_hd_t out_cipher; - - /** - * Shared secret for HMAC verification on incoming data. - */ - struct GNUNET_HashCode in_hmac; - - /** - * Shared secret for HMAC generation on outgoing data, ratcheted after - * each operation. - */ - struct GNUNET_HashCode out_hmac; - - /** - * ID of read task for this connection. - */ - struct GNUNET_SCHEDULER_Task *read_task; - - /** - * ID of write task for this connection. - */ - struct GNUNET_SCHEDULER_Task *write_task; - - /** - * Address of the other peer. - */ - struct sockaddr *address; - - /** - * How many more bytes may we sent with the current @e out_cipher - * before we should rekey? - */ - uint64_t rekey_left_bytes; - - /** - * Until what time may we sent with the current @e out_cipher - * before we should rekey? - */ - struct GNUNET_TIME_Absolute rekey_time; - - /** - * Length of the address. - */ - socklen_t address_len; - - /** - * Message queue we are providing for the #ch. - */ - struct GNUNET_MQ_Handle *mq; - - /** - * handle for this queue with the #ch. - */ - struct GNUNET_TRANSPORT_QueueHandle *qh; - - /** - * Number of bytes we currently have in our write queue. - */ - unsigned long long bytes_in_queue; - - /** - * Buffer for reading ciphertext from network into. - */ - char cread_buf[BUF_SIZE]; - - /** - * buffer for writing ciphertext to network. - */ - char cwrite_buf[BUF_SIZE]; - - /** - * Plaintext buffer for decrypted plaintext. - */ - char pread_buf[UINT16_MAX + 1 + sizeof(struct TCPBox)]; - - /** - * Plaintext buffer for messages to be encrypted. - */ - char pwrite_buf[UINT16_MAX + 1 + sizeof(struct TCPBox)]; - - /** - * At which offset in the ciphertext read buffer should we - * append more ciphertext for transmission next? - */ - size_t cread_off; - - /** - * At which offset in the ciphertext write buffer should we - * append more ciphertext from reading next? - */ - size_t cwrite_off; - - /** - * At which offset in the plaintext input buffer should we - * append more plaintext from decryption next? - */ - size_t pread_off; - - /** - * At which offset in the plaintext output buffer should we - * append more plaintext for encryption next? - */ - size_t pwrite_off; - - /** - * Timeout for this queue. - */ - struct GNUNET_TIME_Absolute timeout; - - /** - * How may messages did we pass from this queue to CORE for which we - * have yet to receive an acknoweldgement that CORE is done with - * them? If "large" (or even just non-zero), we should throttle - * reading to provide flow control. See also #DEFAULT_MAX_QUEUE_LENGTH - * and #max_queue_length. - */ - unsigned int backpressure; - - /** - * Which network type does this queue use? - */ - enum GNUNET_NetworkType nt; - - /** - * The connection status of this queue. - */ - enum GNUNET_TRANSPORT_ConnectionStatus cs; - - /** - * Is MQ awaiting a #GNUNET_MQ_impl_send_continue() call? - */ - int mq_awaits_continue; - - /** - * Did we enqueue a finish message and are closing down the queue? - */ - int finishing; - - /** - * Did we technically destroy this queue, but kept the allocation - * around because of @e backpressure not being zero yet? Used - * simply to delay the final #GNUNET_free() operation until - * #core_read_finished_cb() has been called. - */ - int destroyed; - - /** - * #GNUNET_YES if we just rekeyed and must thus possibly - * re-decrypt ciphertext. - */ - int rekeyed; - - /** - * Monotonic time value for rekey message. - */ - struct GNUNET_TIME_AbsoluteNBO rekey_monotonic_time; - - /** - * Monotonic time value for handshake message. - */ - struct GNUNET_TIME_AbsoluteNBO handshake_monotonic_time; - - /** - * Monotonic time value for handshake ack message. - */ - struct GNUNET_TIME_AbsoluteNBO handshake_ack_monotonic_time; - - /** - * Challenge value used to protect against replay attack, if there is no stored monotonic time value. - */ - struct GNUNET_CRYPTO_ChallengeNonceP challenge; - - /** - * Challenge value received. In case of inbound connection we have to remember the value, because we send the challenge back later after we received the GNUNET_MESSAGE_TYPE_COMMUNICATOR_TCP_CONFIRMATION_ACK. - */ - struct GNUNET_CRYPTO_ChallengeNonceP challenge_received; - - /** - * Iteration Context for retrieving the monotonic time send with key for rekeying. - */ - struct GNUNET_PEERSTORE_IterateContext *rekey_monotime_get; - - /** - * Iteration Context for retrieving the monotonic time send with the handshake. - */ - struct GNUNET_PEERSTORE_IterateContext *handshake_monotime_get; - - /** - * Iteration Context for retrieving the monotonic time send with the handshake ack. - */ - struct GNUNET_PEERSTORE_IterateContext *handshake_ack_monotime_get; - - /** - * Store Context for retrieving the monotonic time send with key for rekeying. - */ - struct GNUNET_PEERSTORE_StoreContext *rekey_monotime_sc; - - /** - * Store Context for retrieving the monotonic time send with the handshake. - */ - struct GNUNET_PEERSTORE_StoreContext *handshake_monotime_sc; - - /** - * Store Context for retrieving the monotonic time send with the handshake ack. - */ - struct GNUNET_PEERSTORE_StoreContext *handshake_ack_monotime_sc; - - /** - * Size of data received without KX challenge played back. - */ - // TODO remove? - size_t unverified_size; - - /** - * Has the initial (core) handshake already happened? - */ - int initial_core_kx_done; -}; - - -/** - * Handle for an incoming connection where we do not yet have enough - * information to setup a full queue. - */ -struct ProtoQueue -{ - /** - * Kept in a DLL. - */ - struct ProtoQueue *next; - - /** - * Kept in a DLL. - */ - struct ProtoQueue *prev; - - /** - * Listen socket. - */ - struct GNUNET_NETWORK_Handle *listen_sock; - - /** - * socket that we transmit all data with on this queue - */ - struct GNUNET_NETWORK_Handle *sock; - - /** - * ID of write task for this connection. - */ - struct GNUNET_SCHEDULER_Task *write_task; - - /** - * buffer for writing struct TCPNATProbeMessage to network. - */ - char write_buf[sizeof (struct TCPNATProbeMessage)]; - - /** - * Offset of the buffer? - */ - size_t write_off; - - /** - * ID of read task for this connection. - */ - struct GNUNET_SCHEDULER_Task *read_task; - - /** - * Address of the other peer. - */ - struct sockaddr *address; - - /** - * Length of the address. - */ - socklen_t address_len; - - /** - * Timeout for this protoqueue. - */ - struct GNUNET_TIME_Absolute timeout; - - /** - * Buffer for reading all the information we need to upgrade from - * protoqueue to queue. - */ - char ibuf[INITIAL_KX_SIZE]; - - /** - * Current offset for reading into @e ibuf. - */ - size_t ibuf_off; -}; - -/** - * In case of port only configuration we like to bind to ipv4 and ipv6 addresses. - */ -struct PortOnlyIpv4Ipv6 -{ - /** - * Ipv4 address we like to bind to. - */ - struct sockaddr *addr_ipv4; - - /** - * Length of ipv4 address. - */ - socklen_t addr_len_ipv4; - - /** - * Ipv6 address we like to bind to. - */ - struct sockaddr *addr_ipv6; - - /** - * Length of ipv6 address. - */ - socklen_t addr_len_ipv6; - -}; - -/** - * DLL to store the addresses we like to register at NAT service. - */ -struct Addresses -{ - /** - * Kept in a DLL. - */ - struct Addresses *next; - - /** - * Kept in a DLL. - */ - struct Addresses *prev; - - /** - * Address we like to register at NAT service. - */ - struct sockaddr *addr; - - /** - * Length of address we like to register at NAT service. - */ - socklen_t addr_len; - -}; - - -/** - * Maximum queue length before we stop reading towards the transport service. - */ -static unsigned long long max_queue_length; - -/** - * For logging statistics. - */ -static struct GNUNET_STATISTICS_Handle *stats; - -/** - * Our environment. - */ -static struct GNUNET_TRANSPORT_CommunicatorHandle *ch; - -/** - * Queues (map from peer identity to `struct Queue`) - */ -static struct GNUNET_CONTAINER_MultiPeerMap *queue_map; - -/** - * ListenTasks (map from socket to `struct ListenTask`) - */ -static struct GNUNET_CONTAINER_MultiHashMap *lt_map; - -/** - * Our public key. - */ -static struct GNUNET_PeerIdentity my_identity; - -/** - * The rekey interval - */ -static struct GNUNET_TIME_Relative rekey_interval; - -/** - * Our private key. - */ -static struct GNUNET_CRYPTO_EddsaPrivateKey *my_private_key; - -/** - * Our configuration. - */ -static const struct GNUNET_CONFIGURATION_Handle *cfg; - -/** - * Network scanner to determine network types. - */ -static struct GNUNET_NT_InterfaceScanner *is; - -/** - * Connection to NAT service. - */ -static struct GNUNET_NAT_Handle *nat; - -/** - * Protoqueues DLL head. - */ -static struct ProtoQueue *proto_head; - -/** - * Protoqueues DLL tail. - */ -static struct ProtoQueue *proto_tail; - -/** - * Handle for DNS lookup of bindto address - */ -struct GNUNET_RESOLVER_RequestHandle *resolve_request_handle; - -/** - * Head of DLL with addresses we like to register at NAT servcie. - */ -struct Addresses *addrs_head; - -/** - * Head of DLL with addresses we like to register at NAT servcie. - */ -struct Addresses *addrs_tail; - -/** - * Head of DLL with ListenTasks. - */ -struct ListenTask *lts_head; - -/** - * Head of DLL with ListenTask. - */ -struct ListenTask *lts_tail; - -/** - * Number of addresses in the DLL for register at NAT service. - */ -int addrs_lens; - - -/** - * Database for peer's HELLOs. - */ -static struct GNUNET_PEERSTORE_Handle *peerstore; - -/** - * A flag indicating we are already doing a shutdown. - */ -int shutdown_running = GNUNET_NO; - -/** - * The port the communicator should be assigned to. - */ -unsigned int bind_port; - -/** - * Map of pending reversals. - */ -struct GNUNET_CONTAINER_MultiHashMap *pending_reversals; - -/** - * We have been notified that our listen socket has something to - * read. Do the read and reschedule this function to be called again - * once more is available. - * - * @param cls NULL - */ -static void -listen_cb (void *cls); - -/** - * Functions with this signature are called whenever we need - * to close a queue due to a disconnect or failure to - * establish a connection. - * - * @param queue queue to close down - */ -static void -queue_destroy (struct Queue *queue) -{ - struct ListenTask *lt = NULL; - struct GNUNET_HashCode h_sock; - int sockfd; - - if (NULL != queue->listen_sock) - { - sockfd = GNUNET_NETWORK_get_fd (queue->listen_sock); - GNUNET_CRYPTO_hash (&sockfd, - sizeof(int), - &h_sock); - - lt = GNUNET_CONTAINER_multihashmap_get (lt_map, &h_sock); - } - - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "Disconnecting queue for peer `%s'\n", - GNUNET_i2s (&queue->target)); - if (NULL != queue->rekey_monotime_sc) - { - GNUNET_PEERSTORE_store_cancel (queue->rekey_monotime_sc); - queue->rekey_monotime_sc = NULL; - } - if (NULL != queue->handshake_monotime_sc) - { - GNUNET_PEERSTORE_store_cancel (queue->handshake_monotime_sc); - queue->handshake_monotime_sc = NULL; - } - if (NULL != queue->handshake_ack_monotime_sc) - { - GNUNET_PEERSTORE_store_cancel (queue->handshake_ack_monotime_sc); - queue->handshake_ack_monotime_sc = NULL; - } - if (NULL != queue->rekey_monotime_get) - { - GNUNET_PEERSTORE_iterate_cancel (queue->rekey_monotime_get); - queue->rekey_monotime_get = NULL; - } - if (NULL != queue->handshake_monotime_get) - { - GNUNET_PEERSTORE_iterate_cancel (queue->handshake_monotime_get); - queue->handshake_monotime_get = NULL; - } - if (NULL != queue->handshake_ack_monotime_get) - { - GNUNET_PEERSTORE_iterate_cancel (queue->handshake_ack_monotime_get); - queue->handshake_ack_monotime_get = NULL; - } - if (NULL != queue->qh) - { - GNUNET_TRANSPORT_communicator_mq_del (queue->qh); - queue->qh = NULL; - } - GNUNET_assert ( - GNUNET_YES == - GNUNET_CONTAINER_multipeermap_remove (queue_map, &queue->target, queue)); - GNUNET_STATISTICS_set (stats, - "# queues active", - GNUNET_CONTAINER_multipeermap_size (queue_map), - GNUNET_NO); - if (NULL != queue->read_task) - { - GNUNET_SCHEDULER_cancel (queue->read_task); - queue->read_task = NULL; - } - if (NULL != queue->write_task) - { - GNUNET_SCHEDULER_cancel (queue->write_task); - queue->write_task = NULL; - } - if (GNUNET_SYSERR == GNUNET_NETWORK_socket_close (queue->sock)) - { - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - "closing socket failed\n"); - } - gcry_cipher_close (queue->in_cipher); - gcry_cipher_close (queue->out_cipher); - GNUNET_free (queue->address); - if (0 != queue->backpressure) - queue->destroyed = GNUNET_YES; - else - GNUNET_free (queue); - - if (NULL == lt) - return; - - if ((! shutdown_running) && (NULL == lt->listen_task)) - { - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "add read net listen\n"); - lt->listen_task = GNUNET_SCHEDULER_add_read_net ( - GNUNET_TIME_UNIT_FOREVER_REL, - lt->listen_sock, - &listen_cb, - lt); - } - else - GNUNET_free (lt); -} - - -/** - * Compute @a mac over @a buf, and ratched the @a hmac_secret. - * - * @param[in,out] hmac_secret secret for HMAC calculation - * @param buf buffer to MAC - * @param buf_size number of bytes in @a buf - * @param[out] smac where to write the HMAC - */ -static void -calculate_hmac (struct GNUNET_HashCode *hmac_secret, - const void *buf, - size_t buf_size, - struct GNUNET_ShortHashCode *smac) -{ - struct GNUNET_HashCode mac; - - GNUNET_CRYPTO_hmac_raw (hmac_secret, - sizeof(struct GNUNET_HashCode), - buf, - buf_size, - &mac); - /* truncate to `struct GNUNET_ShortHashCode` */ - memcpy (smac, &mac, sizeof(struct GNUNET_ShortHashCode)); - /* ratchet hmac key */ - GNUNET_CRYPTO_hash (hmac_secret, - sizeof(struct GNUNET_HashCode), - hmac_secret); -} - - -/** - * Append a 'finish' message to the outgoing transmission. Once the - * finish has been transmitted, destroy the queue. - * - * @param queue queue to shut down nicely - */ -static void -queue_finish (struct Queue *queue) -{ - struct TCPFinish fin; - - memset (&fin, 0, sizeof(fin)); - fin.header.size = htons (sizeof(fin)); - fin.header.type = htons (GNUNET_MESSAGE_TYPE_COMMUNICATOR_TCP_FINISH); - calculate_hmac (&queue->out_hmac, &fin, sizeof(fin), &fin.hmac); - /* if there is any message left in pwrite_buf, we - overwrite it (possibly dropping the last message - from CORE hard here) */ - memcpy (queue->pwrite_buf, &fin, sizeof(fin)); - queue->pwrite_off = sizeof(fin); - /* This flag will ensure that #queue_write() no longer - notifies CORE about the possibility of sending - more data, and that #queue_write() will call - #queue_destroy() once the @c fin was fully written. */ - queue->finishing = GNUNET_YES; -} - - -/** - * Increment queue timeout due to activity. We do not immediately - * notify the monitor here as that might generate excessive - * signalling. - * - * @param queue queue for which the timeout should be rescheduled - */ -static void -reschedule_queue_timeout (struct Queue *queue) -{ - queue->timeout = - GNUNET_TIME_relative_to_absolute (GNUNET_CONSTANTS_IDLE_CONNECTION_TIMEOUT); -} - - -/** - * Queue read task. If we hit the timeout, disconnect it - * - * @param cls the `struct Queue *` to disconnect - */ -static void -queue_read (void *cls); - - -/** - * Core tells us it is done processing a message that transport - * received on a queue with status @a success. - * - * @param cls a `struct Queue *` where the message originally came from - * @param success #GNUNET_OK on success - */ -static void -core_read_finished_cb (void *cls, int success) -{ - struct Queue *queue = cls; - if (GNUNET_OK != success) - GNUNET_STATISTICS_update (stats, - "# messages lost in communicator API towards CORE", - 1, - GNUNET_NO); - if (NULL == queue) - return; - - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "backpressure %u\n", - queue->backpressure); - - queue->backpressure--; - /* handle deferred queue destruction */ - if ((queue->destroyed) && (0 == queue->backpressure)) - { - GNUNET_free (queue); - return; - } - else if (GNUNET_YES != queue->destroyed) - { - reschedule_queue_timeout (queue); - /* possibly unchoke reading, now that CORE made progress */ - if (NULL == queue->read_task) - queue->read_task = - GNUNET_SCHEDULER_add_read_net (GNUNET_TIME_absolute_get_remaining ( - queue->timeout), - queue->sock, - &queue_read, - queue); - } -} - - -/** - * We received @a plaintext_len bytes of @a plaintext on @a queue. - * Pass it on to CORE. If transmission is actually happening, - * increase backpressure counter. - * - * @param queue the queue that received the plaintext - * @param plaintext the plaintext that was received - * @param plaintext_len number of bytes of plaintext received - */ -static void -pass_plaintext_to_core (struct Queue *queue, - const void *plaintext, - size_t plaintext_len) -{ - const struct GNUNET_MessageHeader *hdr = plaintext; - int ret; - - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "pass message from %s to core\n", - GNUNET_i2s (&queue->target)); - - if (ntohs (hdr->size) != plaintext_len) - { - /* NOTE: If we ever allow multiple CORE messages in one - BOX, this will have to change! */ - GNUNET_break (0); - return; - } - ret = GNUNET_TRANSPORT_communicator_receive (ch, - &queue->target, - hdr, - ADDRESS_VALIDITY_PERIOD, - &core_read_finished_cb, - queue); - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "passed to core\n"); - if (GNUNET_OK == ret) - queue->backpressure++; - GNUNET_break (GNUNET_NO != ret); /* backpressure not working!? */ - if (GNUNET_SYSERR == ret) - GNUNET_STATISTICS_update (stats, - "# bytes lost due to CORE not running", - plaintext_len, - GNUNET_NO); -} - - -/** - * Setup @a cipher based on shared secret @a dh and decrypting - * peer @a pid. - * - * @param dh shared secret - * @param pid decrypting peer's identity - * @param[out] cipher cipher to initialize - * @param[out] hmac_key HMAC key to initialize - */ -static void -setup_cipher (const struct GNUNET_HashCode *dh, - const struct GNUNET_PeerIdentity *pid, - gcry_cipher_hd_t *cipher, - struct GNUNET_HashCode *hmac_key) -{ - char key[256 / 8]; - char ctr[128 / 8]; - - GNUNET_assert (0 == gcry_cipher_open (cipher, - GCRY_CIPHER_AES256 /* low level: go for speed */, - GCRY_CIPHER_MODE_CTR, - 0 /* flags */)); - GNUNET_assert (GNUNET_YES == GNUNET_CRYPTO_kdf (key, - sizeof(key), - "TCP-key", - strlen ("TCP-key"), - dh, - sizeof(*dh), - pid, - sizeof(*pid), - NULL, - 0)); - GNUNET_assert (0 == gcry_cipher_setkey (*cipher, key, sizeof(key))); - GNUNET_assert (GNUNET_YES == GNUNET_CRYPTO_kdf (ctr, - sizeof(ctr), - "TCP-ctr", - strlen ("TCP-ctr"), - dh, - sizeof(*dh), - pid, - sizeof(*pid), - NULL, - 0)); - gcry_cipher_setctr (*cipher, ctr, sizeof(ctr)); - GNUNET_assert (GNUNET_YES == - GNUNET_CRYPTO_kdf (hmac_key, - sizeof(struct GNUNET_HashCode), - "TCP-hmac", - strlen ("TCP-hmac"), - dh, - sizeof(*dh), - pid, - sizeof(*pid), - NULL, - 0)); -} - - -/** - * Callback called when peerstore store operation for rekey monotime value is finished. - * @param cls Queue context the store operation was executed. - * @param success Store operation was successful (GNUNET_OK) or not. - */ -static void -rekey_monotime_store_cb (void *cls, int success) -{ - struct Queue *queue = cls; - if (GNUNET_OK != success) - { - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - "Failed to store rekey monotonic time in PEERSTORE!\n"); - } - queue->rekey_monotime_sc = NULL; -} - - -/** - * Callback called by peerstore when records for GNUNET_PEERSTORE_TRANSPORT_TCP_COMMUNICATOR_REKEY - * where found. - * @param cls Queue context the store operation was executed. - * @param record The record found or NULL if there is no record left. - * @param emsg Message from peerstore. - */ -static void -rekey_monotime_cb (void *cls, - const struct GNUNET_PEERSTORE_Record *record, - const char *emsg) -{ - struct Queue *queue = cls; - struct GNUNET_TIME_AbsoluteNBO *mtbe; - struct GNUNET_TIME_Absolute mt; - const struct GNUNET_PeerIdentity *pid; - struct GNUNET_TIME_AbsoluteNBO *rekey_monotonic_time; - - (void) emsg; - - rekey_monotonic_time = &queue->rekey_monotonic_time; - pid = &queue->target; - if (NULL == record) - { - queue->rekey_monotime_get = NULL; - return; - } - if (sizeof(*mtbe) != record->value_size) - { - GNUNET_break (0); - return; - } - mtbe = record->value; - mt = GNUNET_TIME_absolute_ntoh (*mtbe); - if (mt.abs_value_us > GNUNET_TIME_absolute_ntoh ( - queue->rekey_monotonic_time).abs_value_us) - { - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - "Queue from %s dropped, rekey monotime in the past\n", - GNUNET_i2s (&queue->target)); - GNUNET_break (0); - queue_finish (queue); - return; - } - queue->rekey_monotime_sc = GNUNET_PEERSTORE_store (peerstore, - "transport_tcp_communicator", - pid, - GNUNET_PEERSTORE_TRANSPORT_TCP_COMMUNICATOR_REKEY, - rekey_monotonic_time, - sizeof(* - rekey_monotonic_time), - GNUNET_TIME_UNIT_FOREVER_ABS, - GNUNET_PEERSTORE_STOREOPTION_REPLACE, - &rekey_monotime_store_cb, - queue); -} - - -/** - * Setup cipher of @a queue for decryption. - * - * @param ephemeral ephemeral key we received from the other peer - * @param[in,out] queue queue to initialize decryption cipher for - */ -static void -setup_in_cipher (const struct GNUNET_CRYPTO_EcdhePublicKey *ephemeral, - struct Queue *queue) -{ - struct GNUNET_HashCode k; - - GNUNET_CRYPTO_eddsa_kem_decaps (my_private_key, ephemeral, &k); - setup_cipher (&k, &my_identity, &queue->in_cipher, &queue->in_hmac); -} - - -/** - * Handle @a rekey message on @a queue. The message was already - * HMAC'ed, but we should additionally still check the signature. - * Then we need to stop the old cipher and start afresh. - * - * @param queue the queue @a rekey was received on - * @param rekey the rekey message - */ -static void -do_rekey (struct Queue *queue, const struct TCPRekey *rekey) -{ - struct TcpRekeySignature thp; - - thp.purpose.purpose = htonl (GNUNET_SIGNATURE_PURPOSE_COMMUNICATOR_TCP_REKEY); - thp.purpose.size = htonl (sizeof(thp)); - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "do_rekey size %u\n", - thp.purpose.size); - thp.sender = queue->target; - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "sender %s\n", - GNUNET_p2s (&thp.sender.public_key)); - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "sender %s\n", - GNUNET_p2s (&queue->target.public_key)); - thp.receiver = my_identity; - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "receiver %s\n", - GNUNET_p2s (&thp.receiver.public_key)); - thp.ephemeral = rekey->ephemeral; - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "ephemeral %s\n", - GNUNET_e2s (&thp.ephemeral)); - thp.monotonic_time = rekey->monotonic_time; - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "time %s\n", - GNUNET_STRINGS_absolute_time_to_string ( - GNUNET_TIME_absolute_ntoh (thp.monotonic_time))); - GNUNET_assert (ntohl ((&thp)->purpose.size) == sizeof (*(&thp))); - if (GNUNET_OK != - GNUNET_CRYPTO_eddsa_verify ( - GNUNET_SIGNATURE_PURPOSE_COMMUNICATOR_TCP_REKEY, - &thp, - &rekey->sender_sig, - &queue->target.public_key)) - { - GNUNET_break (0); - queue_finish (queue); - return; - } - queue->rekey_monotonic_time = rekey->monotonic_time; - queue->rekey_monotime_get = GNUNET_PEERSTORE_iterate (peerstore, - "transport_tcp_communicator", - &queue->target, - GNUNET_PEERSTORE_TRANSPORT_TCP_COMMUNICATOR_REKEY, - &rekey_monotime_cb, - queue); - gcry_cipher_close (queue->in_cipher); - queue->rekeyed = GNUNET_YES; - setup_in_cipher (&rekey->ephemeral, queue); -} - - -/** - * Callback called when peerstore store operation for handshake ack monotime value is finished. - * @param cls Queue context the store operation was executed. - * @param success Store operation was successful (GNUNET_OK) or not. - */ -static void -handshake_ack_monotime_store_cb (void *cls, int success) -{ - struct Queue *queue = cls; - - if (GNUNET_OK != success) - { - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - "Failed to store handshake ack monotonic time in PEERSTORE!\n"); - } - queue->handshake_ack_monotime_sc = NULL; -} - - -/** - * Callback called by peerstore when records for GNUNET_PEERSTORE_TRANSPORT_TCP_COMMUNICATOR_HANDSHAKE_ACK - * where found. - * @param cls Queue context the store operation was executed. - * @param record The record found or NULL if there is no record left. - * @param emsg Message from peerstore. - */ -static void -handshake_ack_monotime_cb (void *cls, - const struct GNUNET_PEERSTORE_Record *record, - const char *emsg) -{ - struct Queue *queue = cls; - struct GNUNET_TIME_AbsoluteNBO *mtbe; - struct GNUNET_TIME_Absolute mt; - const struct GNUNET_PeerIdentity *pid; - struct GNUNET_TIME_AbsoluteNBO *handshake_ack_monotonic_time; - - (void) emsg; - - handshake_ack_monotonic_time = &queue->handshake_ack_monotonic_time; - pid = &queue->target; - if (NULL == record) - { - queue->handshake_ack_monotime_get = NULL; - return; - } - if (sizeof(*mtbe) != record->value_size) - { - GNUNET_break (0); - return; - } - mtbe = record->value; - mt = GNUNET_TIME_absolute_ntoh (*mtbe); - if (mt.abs_value_us > GNUNET_TIME_absolute_ntoh ( - queue->handshake_ack_monotonic_time).abs_value_us) - { - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - "Queue from %s dropped, handshake ack monotime in the past\n", - GNUNET_i2s (&queue->target)); - GNUNET_break (0); - queue_finish (queue); - return; - } - queue->handshake_ack_monotime_sc = - GNUNET_PEERSTORE_store (peerstore, - "transport_tcp_communicator", - pid, - GNUNET_PEERSTORE_TRANSPORT_TCP_COMMUNICATOR_HANDSHAKE_ACK, - handshake_ack_monotonic_time, - sizeof(*handshake_ack_monotonic_time), - GNUNET_TIME_UNIT_FOREVER_ABS, - GNUNET_PEERSTORE_STOREOPTION_REPLACE, - & - handshake_ack_monotime_store_cb, - queue); -} - - -/** - * Sending challenge with TcpConfirmationAck back to sender of ephemeral key. - * - * @param tc The TCPConfirmation originally send. - * @param queue The queue context. - */ -static void -send_challenge (struct GNUNET_CRYPTO_ChallengeNonceP challenge, - struct Queue *queue) -{ - struct TCPConfirmationAck tca; - struct TcpHandshakeAckSignature thas; - - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "sending challenge\n"); - - tca.header.type = ntohs ( - GNUNET_MESSAGE_TYPE_COMMUNICATOR_TCP_CONFIRMATION_ACK); - tca.header.size = ntohs (sizeof(tca)); - tca.challenge = challenge; - tca.sender = my_identity; - tca.monotonic_time = - GNUNET_TIME_absolute_hton (GNUNET_TIME_absolute_get_monotonic (cfg)); - thas.purpose.purpose = htonl ( - GNUNET_SIGNATURE_PURPOSE_COMMUNICATOR_TCP_HANDSHAKE_ACK); - thas.purpose.size = htonl (sizeof(thas)); - thas.sender = my_identity; - thas.receiver = queue->target; - thas.monotonic_time = tca.monotonic_time; - thas.challenge = tca.challenge; - GNUNET_CRYPTO_eddsa_sign (my_private_key, - &thas, - &tca.sender_sig); - GNUNET_assert (0 == - gcry_cipher_encrypt (queue->out_cipher, - &queue->cwrite_buf[queue->cwrite_off], - sizeof(tca), - &tca, - sizeof(tca))); - queue->cwrite_off += sizeof(tca); - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "sending challenge done\n"); -} - - -/** - * Setup cipher for outgoing data stream based on target and - * our ephemeral private key. - * - * @param queue queue to setup outgoing (encryption) cipher for - */ -static void -setup_out_cipher (struct Queue *queue, struct GNUNET_HashCode *dh) -{ - setup_cipher (dh, &queue->target, &queue->out_cipher, &queue->out_hmac); - queue->rekey_time = GNUNET_TIME_relative_to_absolute (rekey_interval); - queue->rekey_left_bytes = - GNUNET_CRYPTO_random_u64 (GNUNET_CRYPTO_QUALITY_WEAK, REKEY_MAX_BYTES); -} - - -/** - * Inject a `struct TCPRekey` message into the queue's plaintext - * buffer. - * - * @param queue queue to perform rekeying on - */ -static void -inject_rekey (struct Queue *queue) -{ - struct TCPRekey rekey; - struct TcpRekeySignature thp; - struct GNUNET_HashCode k; - - GNUNET_assert (0 == queue->pwrite_off); - memset (&rekey, 0, sizeof(rekey)); - GNUNET_CRYPTO_eddsa_kem_encaps (&queue->target.public_key, &rekey.ephemeral, - &k); - rekey.header.type = ntohs (GNUNET_MESSAGE_TYPE_COMMUNICATOR_TCP_REKEY); - rekey.header.size = ntohs (sizeof(rekey)); - rekey.monotonic_time = - GNUNET_TIME_absolute_hton (GNUNET_TIME_absolute_get_monotonic (cfg)); - thp.purpose.purpose = htonl (GNUNET_SIGNATURE_PURPOSE_COMMUNICATOR_TCP_REKEY); - thp.purpose.size = htonl (sizeof(thp)); - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "inject_rekey size %u\n", - thp.purpose.size); - thp.sender = my_identity; - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "sender %s\n", - GNUNET_p2s (&thp.sender.public_key)); - thp.receiver = queue->target; - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "receiver %s\n", - GNUNET_p2s (&thp.receiver.public_key)); - thp.ephemeral = rekey.ephemeral; - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "ephemeral %s\n", - GNUNET_e2s (&thp.ephemeral)); - thp.monotonic_time = rekey.monotonic_time; - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "time %s\n", - GNUNET_STRINGS_absolute_time_to_string ( - GNUNET_TIME_absolute_ntoh (thp.monotonic_time))); - GNUNET_CRYPTO_eddsa_sign (my_private_key, - &thp, - &rekey.sender_sig); - calculate_hmac (&queue->out_hmac, &rekey, sizeof(rekey), &rekey.hmac); - /* Encrypt rekey message with 'old' cipher */ - GNUNET_assert (0 == - gcry_cipher_encrypt (queue->out_cipher, - &queue->cwrite_buf[queue->cwrite_off], - sizeof(rekey), - &rekey, - sizeof(rekey))); - queue->cwrite_off += sizeof(rekey); - /* Setup new cipher for successive messages */ - gcry_cipher_close (queue->out_cipher); - setup_out_cipher (queue, &k); -} - - -static int -pending_reversals_delete_it (void *cls, - const struct GNUNET_HashCode *key, - void *value) -{ - (void) cls; - struct PendingReversal *pending_reversal = value; - - if (NULL != pending_reversal->timeout_task) - { - GNUNET_SCHEDULER_cancel (pending_reversal->timeout_task); - pending_reversal->timeout_task = NULL; - } - GNUNET_assert (GNUNET_YES == GNUNET_CONTAINER_multihashmap_remove ( - pending_reversals, - key, - pending_reversal)); - GNUNET_free (pending_reversal->in); - GNUNET_free (pending_reversal); - return GNUNET_OK; -} - - -static void -check_and_remove_pending_reversal (struct sockaddr *in, sa_family_t sa_family, - struct GNUNET_PeerIdentity *sender) -{ - if (AF_INET == sa_family) - { - struct PendingReversal *pending_reversal; - struct GNUNET_HashCode key; - struct sockaddr_in *natted_address; - - natted_address = GNUNET_memdup (in, sizeof (struct sockaddr)); - natted_address->sin_port = 0; - GNUNET_CRYPTO_hash (natted_address, - sizeof(struct sockaddr), - &key); - - pending_reversal = GNUNET_CONTAINER_multihashmap_get (pending_reversals, - &key); - if (NULL != pending_reversal && (NULL == sender || - 0 != memcmp (sender, - &pending_reversal->target, - sizeof(struct - GNUNET_PeerIdentity)))) - { - GNUNET_log (GNUNET_ERROR_TYPE_WARNING, - "Removing invalid pending reversal for `%s'at `%s'\n", - GNUNET_i2s (&pending_reversal->target), - GNUNET_a2s (in, sizeof (struct sockaddr))); - pending_reversals_delete_it (NULL, &key, pending_reversal); - } - GNUNET_free (natted_address); - } -} - - -/** - * Closes socket and frees memory associated with @a pq. - * - * @param pq proto queue to free - */ -static void -free_proto_queue (struct ProtoQueue *pq) -{ - if (NULL != pq->listen_sock) - { - GNUNET_break (GNUNET_OK == GNUNET_NETWORK_socket_close (pq->listen_sock)); - pq->listen_sock = NULL; - } - if (NULL != pq->read_task) - { - GNUNET_SCHEDULER_cancel (pq->read_task); - pq->read_task = NULL; - } - if (NULL != pq->write_task) - { - GNUNET_SCHEDULER_cancel (pq->write_task); - pq->write_task = NULL; - } - check_and_remove_pending_reversal (pq->address, pq->address->sa_family, NULL); - GNUNET_NETWORK_socket_close (pq->sock); - GNUNET_free (pq->address); - GNUNET_CONTAINER_DLL_remove (proto_head, proto_tail, pq); - GNUNET_free (pq); -} - - -/** - * We have been notified that our socket is ready to write. - * Then reschedule this function to be called again once more is available. - * - * @param cls a `struct ProtoQueue` - */ -static void -proto_queue_write (void *cls) -{ - struct ProtoQueue *pq = cls; - ssize_t sent; - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "In proto queue write\n"); - pq->write_task = NULL; - if (0 != pq->write_off) - { - sent = GNUNET_NETWORK_socket_send (pq->sock, - pq->write_buf, - pq->write_off); - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "Sent %lu bytes to TCP queue\n", sent); - if ((-1 == sent) && (EAGAIN != errno) && (EINTR != errno)) - { - GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING, "send"); - free_proto_queue (pq); - return; - } - if (sent > 0) - { - size_t usent = (size_t) sent; - pq->write_off -= usent; - memmove (pq->write_buf, - &pq->write_buf[usent], - pq->write_off); - } - } - /* do we care to write more? */ - if ((0 < pq->write_off)) - pq->write_task = - GNUNET_SCHEDULER_add_write_net (GNUNET_TIME_UNIT_FOREVER_REL, - pq->sock, - &proto_queue_write, - pq); -} - - -/** - * We have been notified that our socket is ready to write. - * Then reschedule this function to be called again once more is available. - * - * @param cls a `struct Queue` - */ -static void -queue_write (void *cls) -{ - struct Queue *queue = cls; - ssize_t sent; - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "In queue write\n"); - queue->write_task = NULL; - if (0 != queue->cwrite_off) - { - sent = GNUNET_NETWORK_socket_send (queue->sock, - queue->cwrite_buf, - queue->cwrite_off); - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "Sent %lu bytes to TCP queue\n", sent); - if ((-1 == sent) && (EAGAIN != errno) && (EINTR != errno)) - { - GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING, "send"); - queue_destroy (queue); - return; - } - if (sent > 0) - { - size_t usent = (size_t) sent; - queue->cwrite_off -= usent; - memmove (queue->cwrite_buf, - &queue->cwrite_buf[usent], - queue->cwrite_off); - reschedule_queue_timeout (queue); - } - } - /* can we encrypt more? (always encrypt full messages, needed - such that #mq_cancel() can work!) */ - unsigned int we_do_not_need_to_rekey = (0 < queue->rekey_left_bytes - - (queue->cwrite_off - + queue->pwrite_off - + sizeof (struct TCPRekey))); - if (we_do_not_need_to_rekey && - (queue->pwrite_off > 0) && - (queue->cwrite_off + queue->pwrite_off <= BUF_SIZE)) - { - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "Encrypting %lu bytes\n", queue->pwrite_off); - GNUNET_assert (0 == - gcry_cipher_encrypt (queue->out_cipher, - &queue->cwrite_buf[queue->cwrite_off], - queue->pwrite_off, - queue->pwrite_buf, - queue->pwrite_off)); - if (queue->rekey_left_bytes > queue->pwrite_off) - queue->rekey_left_bytes -= queue->pwrite_off; - else - queue->rekey_left_bytes = 0; - queue->cwrite_off += queue->pwrite_off; - queue->pwrite_off = 0; - } - // if ((-1 != unverified_size)&& ((0 == queue->pwrite_off) && - if (((0 == queue->rekey_left_bytes) || - (0 == GNUNET_TIME_absolute_get_remaining ( - queue->rekey_time).rel_value_us)) && - (((0 == queue->pwrite_off) || ! we_do_not_need_to_rekey) && - (queue->cwrite_off + sizeof (struct TCPRekey) <= BUF_SIZE))) - { - inject_rekey (queue); - } - if ((0 == queue->pwrite_off) && (! queue->finishing) && - (GNUNET_YES == queue->mq_awaits_continue)) - { - queue->mq_awaits_continue = GNUNET_NO; - GNUNET_MQ_impl_send_continue (queue->mq); - } - /* did we just finish writing 'finish'? */ - if ((0 == queue->cwrite_off) && (GNUNET_YES == queue->finishing)) - { - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "Finishing queue\n"); - queue_destroy (queue); - return; - } - /* do we care to write more? */ - if ((0 < queue->cwrite_off) || (0 < queue->pwrite_off)) - queue->write_task = - GNUNET_SCHEDULER_add_write_net (GNUNET_TIME_UNIT_FOREVER_REL, - queue->sock, - &queue_write, - queue); -} - - -/** - * Test if we have received a full message in plaintext. - * If so, handle it. - * - * @param queue queue to process inbound plaintext for - * @return number of bytes of plaintext handled, 0 for none - */ -static size_t -try_handle_plaintext (struct Queue *queue) -{ - const struct GNUNET_MessageHeader *hdr; - const struct TCPConfirmationAck *tca; - const struct TCPBox *box; - const struct TCPRekey *rekey; - const struct TCPFinish *fin; - struct TCPRekey rekeyz; - struct TCPFinish finz; - struct GNUNET_ShortHashCode tmac; - uint16_t type; - size_t size = 0; - struct TcpHandshakeAckSignature thas; - const struct GNUNET_CRYPTO_ChallengeNonceP challenge = queue->challenge; - - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "try handle plaintext!\n"); - - hdr = (const struct GNUNET_MessageHeader *) queue->pread_buf; - if ((sizeof(*hdr) > queue->pread_off)) - { - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "Handling plaintext, not even a header!\n"); - return 0; /* not even a header */ - } - - if ((GNUNET_YES != queue->initial_core_kx_done) && (queue->unverified_size > - INITIAL_CORE_KX_SIZE)) - { - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - "Already received data of size %lu bigger than KX size %lu!\n", - queue->unverified_size, - INITIAL_CORE_KX_SIZE); - GNUNET_break_op (0); - queue_finish (queue); - return 0; - } - - type = ntohs (hdr->type); - switch (type) - { - case GNUNET_MESSAGE_TYPE_COMMUNICATOR_TCP_CONFIRMATION_ACK: - tca = (const struct TCPConfirmationAck *) queue->pread_buf; - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "start processing ack\n"); - if (sizeof(*tca) > queue->pread_off) - { - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "Handling plaintext size of tca greater than pread offset.\n"); - return 0; - } - if (ntohs (hdr->size) != sizeof(*tca)) - { - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "Handling plaintext size does not match message type.\n"); - GNUNET_break_op (0); - queue_finish (queue); - return 0; - } - - thas.purpose.purpose = htonl ( - GNUNET_SIGNATURE_PURPOSE_COMMUNICATOR_TCP_HANDSHAKE_ACK); - thas.purpose.size = htonl (sizeof(thas)); - thas.sender = tca->sender; - thas.receiver = my_identity; - thas.monotonic_time = tca->monotonic_time; - thas.challenge = tca->challenge; - - if (GNUNET_SYSERR == GNUNET_CRYPTO_eddsa_verify ( - GNUNET_SIGNATURE_PURPOSE_COMMUNICATOR_TCP_HANDSHAKE_ACK, - &thas, - &tca->sender_sig, - &tca->sender.public_key)) - { - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - "Verification of signature failed!\n"); - GNUNET_break (0); - queue_finish (queue); - return 0; - } - if (0 != GNUNET_memcmp (&tca->challenge, &challenge)) - { - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - "Challenge in TCPConfirmationAck not correct!\n"); - GNUNET_break (0); - queue_finish (queue); - return 0; - } - - queue->handshake_ack_monotime_get = GNUNET_PEERSTORE_iterate (peerstore, - "transport_tcp_communicator", - &queue->target, - GNUNET_PEERSTORE_TRANSPORT_TCP_COMMUNICATOR_HANDSHAKE_ACK, - & - handshake_ack_monotime_cb, - queue); - - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "Handling plaintext, ack processed!\n"); - - if (GNUNET_TRANSPORT_CS_INBOUND == queue->cs) - { - send_challenge (queue->challenge_received, queue); - queue->write_task = - GNUNET_SCHEDULER_add_write_net (GNUNET_TIME_UNIT_FOREVER_REL, - queue->sock, - &queue_write, - queue); - } - else if (GNUNET_TRANSPORT_CS_OUTBOUND == queue->cs) - { - check_and_remove_pending_reversal (queue->address, - queue->address->sa_family, NULL); - } - - /** - * Once we received this ack, we consider this a verified connection. - * FIXME: I am not sure this logic is sane here. - */ - queue->initial_core_kx_done = GNUNET_YES; - - char *foreign_addr; - - switch (queue->address->sa_family) - { - case AF_INET: - GNUNET_asprintf (&foreign_addr, - "%s-%s", - COMMUNICATOR_ADDRESS_PREFIX, - GNUNET_a2s (queue->address, queue->address_len)); - break; - - case AF_INET6: - GNUNET_asprintf (&foreign_addr, - "%s-%s", - COMMUNICATOR_ADDRESS_PREFIX, - GNUNET_a2s (queue->address, queue->address_len)); - break; - - default: - GNUNET_assert (0); - } - - queue->qh = GNUNET_TRANSPORT_communicator_mq_add (ch, - &queue->target, - foreign_addr, - UINT16_MAX, /* no MTU */ - GNUNET_TRANSPORT_QUEUE_LENGTH_UNLIMITED, - 0, /* Priority */ - queue->nt, - queue->cs, - queue->mq); - - GNUNET_free (foreign_addr); - - size = ntohs (hdr->size); - break; - case GNUNET_MESSAGE_TYPE_COMMUNICATOR_TCP_BOX: - /* Special case: header size excludes box itself! */ - box = (const struct TCPBox *) queue->pread_buf; - if (ntohs (hdr->size) + sizeof(struct TCPBox) > queue->pread_off) - return 0; - calculate_hmac (&queue->in_hmac, &box[1], ntohs (hdr->size), &tmac); - if (0 != memcmp (&tmac, &box->hmac, sizeof(tmac))) - { - GNUNET_break_op (0); - queue_finish (queue); - return 0; - } - pass_plaintext_to_core (queue, (const void *) &box[1], ntohs (hdr->size)); - size = ntohs (hdr->size) + sizeof(*box); - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "Handling plaintext, box processed!\n"); - break; - - case GNUNET_MESSAGE_TYPE_COMMUNICATOR_TCP_REKEY: - rekey = (const struct TCPRekey *) queue->pread_buf; - if (sizeof(*rekey) > queue->pread_off) - return 0; - if (ntohs (hdr->size) != sizeof(*rekey)) - { - GNUNET_break_op (0); - queue_finish (queue); - return 0; - } - rekeyz = *rekey; - memset (&rekeyz.hmac, 0, sizeof(rekeyz.hmac)); - calculate_hmac (&queue->in_hmac, &rekeyz, sizeof(rekeyz), &tmac); - if (0 != memcmp (&tmac, &rekey->hmac, sizeof(tmac))) - { - GNUNET_break_op (0); - queue_finish (queue); - return 0; - } - do_rekey (queue, rekey); - size = ntohs (hdr->size); - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "Handling plaintext, rekey processed!\n"); - break; - - case GNUNET_MESSAGE_TYPE_COMMUNICATOR_TCP_FINISH: - fin = (const struct TCPFinish *) queue->pread_buf; - if (sizeof(*fin) > queue->pread_off) - return 0; - if (ntohs (hdr->size) != sizeof(*fin)) - { - GNUNET_break_op (0); - queue_finish (queue); - return 0; - } - finz = *fin; - memset (&finz.hmac, 0, sizeof(finz.hmac)); - calculate_hmac (&queue->in_hmac, &rekeyz, sizeof(rekeyz), &tmac); - if (0 != memcmp (&tmac, &fin->hmac, sizeof(tmac))) - { - GNUNET_break_op (0); - queue_finish (queue); - return 0; - } - /* handle FINISH by destroying queue */ - queue_destroy (queue); - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "Handling plaintext, finish processed!\n"); - break; - - default: - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "Handling plaintext, nothing processed!\n"); - GNUNET_break_op (0); - queue_finish (queue); - return 0; - } - GNUNET_assert (0 != size); - if (-1 != queue->unverified_size) - queue->unverified_size += size; - return size; -} - - -/** - * Queue read task. If we hit the timeout, disconnect it - * - * @param cls the `struct Queue *` to disconnect - */ -static void -queue_read (void *cls) -{ - struct Queue *queue = cls; - struct GNUNET_TIME_Relative left; - ssize_t rcvd; - - queue->read_task = NULL; - rcvd = GNUNET_NETWORK_socket_recv (queue->sock, - &queue->cread_buf[queue->cread_off], - BUF_SIZE - queue->cread_off); - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "Received %zd bytes from TCP queue\n", rcvd); - if (-1 == rcvd) - { - if ((EAGAIN != errno) && (EINTR != errno)) - { - GNUNET_log_strerror (GNUNET_ERROR_TYPE_DEBUG, "recv"); - queue_finish (queue); - return; - } - /* try again */ - left = GNUNET_TIME_absolute_get_remaining (queue->timeout); - queue->read_task = - GNUNET_SCHEDULER_add_read_net (left, queue->sock, &queue_read, queue); - return; - } - if (0 != rcvd) - reschedule_queue_timeout (queue); - queue->cread_off += rcvd; - while ((queue->pread_off < sizeof(queue->pread_buf)) && - (queue->cread_off > 0)) - { - size_t max = GNUNET_MIN (sizeof(queue->pread_buf) - queue->pread_off, - queue->cread_off); - size_t done; - size_t total; - size_t old_pread_off = queue->pread_off; - - GNUNET_assert (0 == - gcry_cipher_decrypt (queue->in_cipher, - &queue->pread_buf[queue->pread_off], - max, - queue->cread_buf, - max)); - queue->pread_off += max; - total = 0; - while (0 != (done = try_handle_plaintext (queue))) - { - /* 'done' bytes of plaintext were used, shift buffer */ - GNUNET_assert (done <= queue->pread_off); - /* NOTE: this memmove() could possibly sometimes be - avoided if we pass 'total' into try_handle_plaintext() - and use it at an offset into the buffer there! */ - memmove (queue->pread_buf, - &queue->pread_buf[done], - queue->pread_off - done); - queue->pread_off -= done; - total += done; - /* The last plaintext was a rekey, abort for now */ - if (GNUNET_YES == queue->rekeyed) - break; - } - /* when we encounter a rekey message, the decryption above uses the - wrong key for everything after the rekey; in that case, we have - to re-do the decryption at 'total' instead of at 'max'. - However, we have to take into account that the plaintext buffer may have - already contained data and not jumped too far ahead in the ciphertext. - If there is no rekey and the last message is incomplete (max > total), - it is safe to keep the decryption so we shift by 'max' */ - if (GNUNET_YES == queue->rekeyed) - { - max = total - old_pread_off; - queue->rekeyed = GNUNET_NO; - queue->pread_off = 0; - } - memmove (queue->cread_buf, &queue->cread_buf[max], queue->cread_off - max); - queue->cread_off -= max; - } - if (BUF_SIZE == queue->cread_off) - return; /* buffer full, suspend reading */ - left = GNUNET_TIME_absolute_get_remaining (queue->timeout); - if (0 != left.rel_value_us) - { - if (max_queue_length > queue->backpressure) - { - /* continue reading */ - left = GNUNET_TIME_absolute_get_remaining (queue->timeout); - queue->read_task = - GNUNET_SCHEDULER_add_read_net (left, queue->sock, &queue_read, queue); - } - return; - } - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "Queue %p was idle for %s, disconnecting\n", - queue, - GNUNET_STRINGS_relative_time_to_string ( - GNUNET_CONSTANTS_IDLE_CONNECTION_TIMEOUT, - GNUNET_YES)); - queue_finish (queue); -} - - -/** - * Convert a `struct sockaddr_in6 to a `struct sockaddr *` - * - * @param[out] sock_len set to the length of the address. - * @param v6 The sockaddr_in6 to be converted. - * @return The struct sockaddr *. - */ -static struct sockaddr * -tcp_address_to_sockaddr_numeric_v6 (socklen_t *sock_len, - struct sockaddr_in6 v6, - unsigned int port) -{ - struct sockaddr *in; - - v6.sin6_family = AF_INET6; - v6.sin6_port = htons ((uint16_t) port); -#if HAVE_SOCKADDR_IN_SIN_LEN - v6.sin6_len = sizeof(sizeof(struct sockaddr_in6)); -#endif - v6.sin6_flowinfo = 0; - v6.sin6_scope_id = 0; - in = GNUNET_memdup (&v6, sizeof(v6)); - *sock_len = sizeof(struct sockaddr_in6); - - return in; -} - - -/** - * Convert a `struct sockaddr_in4 to a `struct sockaddr *` - * - * @param[out] sock_len set to the length of the address. - * @param v4 The sockaddr_in4 to be converted. - * @return The struct sockaddr *. - */ -static struct sockaddr * -tcp_address_to_sockaddr_numeric_v4 (socklen_t *sock_len, - struct sockaddr_in v4, - unsigned int port) -{ - struct sockaddr *in; - - v4.sin_family = AF_INET; - v4.sin_port = htons ((uint16_t) port); -#if HAVE_SOCKADDR_IN_SIN_LEN - v4.sin_len = sizeof(struct sockaddr_in); -#endif - in = GNUNET_memdup (&v4, sizeof(v4)); - *sock_len = sizeof(struct sockaddr_in); - return in; -} - - -/** - * Convert TCP bind specification to a `struct PortOnlyIpv4Ipv6 *` - * - * @param bindto bind specification to convert. - * @return The converted bindto specification. - */ -static struct PortOnlyIpv4Ipv6 * -tcp_address_to_sockaddr_port_only (const char *bindto, unsigned int *port) -{ - struct PortOnlyIpv4Ipv6 *po; - struct sockaddr_in *i4; - struct sockaddr_in6 *i6; - socklen_t sock_len_ipv4; - socklen_t sock_len_ipv6; - - /* interpreting value as just a PORT number */ - if (*port > UINT16_MAX) - { - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - "BINDTO specification `%s' invalid: value too large for port\n", - bindto); - return NULL; - } - - po = GNUNET_new (struct PortOnlyIpv4Ipv6); - - if ((GNUNET_NO == GNUNET_NETWORK_test_pf (PF_INET6)) || - (GNUNET_YES == - GNUNET_CONFIGURATION_get_value_yesno (cfg, - COMMUNICATOR_CONFIG_SECTION, - "DISABLE_V6"))) - { - i4 = GNUNET_malloc (sizeof(struct sockaddr_in)); - po->addr_ipv4 = tcp_address_to_sockaddr_numeric_v4 (&sock_len_ipv4, *i4, - *port); - po->addr_len_ipv4 = sock_len_ipv4; - } - else - { - - i4 = GNUNET_malloc (sizeof(struct sockaddr_in)); - po->addr_ipv4 = tcp_address_to_sockaddr_numeric_v4 (&sock_len_ipv4, *i4, - *port); - po->addr_len_ipv4 = sock_len_ipv4; - - i6 = GNUNET_malloc (sizeof(struct sockaddr_in6)); - po->addr_ipv6 = tcp_address_to_sockaddr_numeric_v6 (&sock_len_ipv6, *i6, - *port); - - po->addr_len_ipv6 = sock_len_ipv6; - - GNUNET_free (i6); - } - - GNUNET_free (i4); - - return po; -} - - -/** - * This Method extracts the address part of the BINDTO string. - * - * @param bindto String we extract the address part from. - * @return The extracted address string. - */ -static char * -extract_address (const char *bindto) -{ - char *addr; - char *start; - char *token; - char *cp; - char *rest = NULL; - char *res; - - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "extract address with bindto %s\n", - bindto); - - if (NULL == bindto) - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - "bindto is NULL\n"); - - cp = GNUNET_strdup (bindto); - - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "extract address 2\n"); - - start = cp; - if (('[' == *cp) && (']' == cp[strlen (cp) - 1])) - { - start++; /* skip over '['*/ - cp[strlen (cp) - 1] = '\0'; /* eat ']'*/ - addr = GNUNET_strdup (start); - } - else - { - token = strtok_r (cp, "]", &rest); - if (strlen (bindto) == strlen (token)) - { - token = strtok_r (cp, ":", &rest); - addr = GNUNET_strdup (token); - } - else - { - token++; - res = GNUNET_strdup (token); - addr = GNUNET_strdup (res); - } - } - - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "tcp address: %s\n", - addr); - GNUNET_free (cp); - return addr; -} - - -/** - * This Method extracts the port part of the BINDTO string. - * - * @param addr_and_port String we extract the port from. - * @return The extracted port as unsigned int. - */ -static unsigned int -extract_port (const char *addr_and_port) -{ - unsigned int port; - char dummy[2]; - char *token; - char *addr; - char *colon; - char *cp; - char *rest = NULL; - - if (NULL != addr_and_port) - { - cp = GNUNET_strdup (addr_and_port); - token = strtok_r (cp, "]", &rest); - if (strlen (addr_and_port) == strlen (token)) - { - colon = strrchr (cp, ':'); - if (NULL == colon) - { - GNUNET_free (cp); - return 0; - } - addr = colon; - addr++; - } - else - { - token = strtok_r (NULL, "]", &rest); - if (NULL == token) - { - GNUNET_free (cp); - return 0; - } - else - { - addr = token; - addr++; - } - } - - - if (1 == sscanf (addr, "%u%1s", &port, dummy)) - { - /* interpreting value as just a PORT number */ - if (port > UINT16_MAX) - { - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - "Port `%u' invalid: value too large for port\n", - port); - GNUNET_free (cp); - return 0; - } - } - else - { - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - "BINDTO specification invalid: last ':' not followed by number\n"); - GNUNET_free (cp); - return 0; - } - GNUNET_free (cp); - } - else - { - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - "return 0\n"); - /* interpret missing port as 0, aka pick any free one */ - port = 0; - } - - return port; -} - - -/** - * Convert TCP bind specification to a `struct sockaddr *` - * - * @param bindto bind specification to convert - * @param[out] sock_len set to the length of the address - * @return converted bindto specification - */ -static struct sockaddr * -tcp_address_to_sockaddr (const char *bindto, socklen_t *sock_len) -{ - struct sockaddr *in; - unsigned int port; - struct sockaddr_in v4; - struct sockaddr_in6 v6; - char *start; - - memset (&v4, 0, sizeof(v4)); - start = extract_address (bindto); - GNUNET_assert (NULL != start); - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "start %s\n", - start); - - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "!bindto %s\n", - bindto); - - - if (1 == inet_pton (AF_INET, start, &v4.sin_addr)) - { - // colon = strrchr (cp, ':'); - port = extract_port (bindto); - - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "port %u\n", - port); - - in = tcp_address_to_sockaddr_numeric_v4 (sock_len, v4, port); - } - else if (1 == inet_pton (AF_INET6, start, &v6.sin6_addr)) - { - // colon = strrchr (cp, ':'); - port = extract_port (bindto); - in = tcp_address_to_sockaddr_numeric_v6 (sock_len, v6, port); - } - else - { - GNUNET_assert (0); - } - - GNUNET_free (start); - return in; -} - - -/** - * Signature of functions implementing the sending functionality of a - * message queue. - * - * @param mq the message queue - * @param msg the message to send - * @param impl_state our `struct Queue` - */ -static void -mq_send (struct GNUNET_MQ_Handle *mq, - const struct GNUNET_MessageHeader *msg, - void *impl_state) -{ - struct Queue *queue = impl_state; - uint16_t msize = ntohs (msg->size); - struct TCPBox box; - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "In MQ send. Queue finishing: %s; write task running: %s\n", - (GNUNET_YES == queue->finishing) ? "yes" : "no", - (NULL == queue->write_task) ? "yes" : "no"); - GNUNET_assert (mq == queue->mq); - queue->mq_awaits_continue = GNUNET_YES; - if (GNUNET_YES == queue->finishing) - return; /* this queue is dying, drop msg */ - GNUNET_assert (0 == queue->pwrite_off); - box.header.type = htons (GNUNET_MESSAGE_TYPE_COMMUNICATOR_TCP_BOX); - box.header.size = htons (msize); - calculate_hmac (&queue->out_hmac, msg, msize, &box.hmac); - memcpy (&queue->pwrite_buf[queue->pwrite_off], &box, sizeof(box)); - queue->pwrite_off += sizeof(box); - memcpy (&queue->pwrite_buf[queue->pwrite_off], msg, msize); - queue->pwrite_off += msize; - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "%lu bytes of plaintext to send\n", queue->pwrite_off); - GNUNET_assert (NULL != queue->sock); - if (NULL == queue->write_task) - queue->write_task = - GNUNET_SCHEDULER_add_write_net (GNUNET_TIME_UNIT_FOREVER_REL, - queue->sock, - &queue_write, - queue); -} - - -/** - * Signature of functions implementing the destruction of a message - * queue. Implementations must not free @a mq, but should take care - * of @a impl_state. - * - * @param mq the message queue to destroy - * @param impl_state our `struct Queue` - */ -static void -mq_destroy (struct GNUNET_MQ_Handle *mq, void *impl_state) -{ - struct Queue *queue = impl_state; - - if (mq == queue->mq) - { - queue->mq = NULL; - queue_finish (queue); - } -} - - -/** - * Implementation function that cancels the currently sent message. - * - * @param mq message queue - * @param impl_state our `struct Queue` - */ -static void -mq_cancel (struct GNUNET_MQ_Handle *mq, void *impl_state) -{ - struct Queue *queue = impl_state; - - GNUNET_assert (0 != queue->pwrite_off); - queue->pwrite_off = 0; -} - - -/** - * Generic error handler, called with the appropriate - * error code and the same closure specified at the creation of - * the message queue. - * Not every message queue implementation supports an error handler. - * - * @param cls our `struct Queue` - * @param error error code - */ -static void -mq_error (void *cls, enum GNUNET_MQ_Error error) -{ - struct Queue *queue = cls; - - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - "MQ error in queue to %s: %d\n", - GNUNET_i2s (&queue->target), - (int) error); - queue_finish (queue); -} - - -/** - * Add the given @a queue to our internal data structure. Setup the - * MQ processing and inform transport that the queue is ready. Must - * be called after the KX for outgoing messages has been bootstrapped. - * - * @param queue queue to boot - */ -static void -boot_queue (struct Queue *queue) -{ - queue->nt = - GNUNET_NT_scanner_get_type (is, queue->address, queue->address_len); - (void) GNUNET_CONTAINER_multipeermap_put ( - queue_map, - &queue->target, - queue, - GNUNET_CONTAINER_MULTIHASHMAPOPTION_MULTIPLE); - GNUNET_STATISTICS_set (stats, - "# queues active", - GNUNET_CONTAINER_multipeermap_size (queue_map), - GNUNET_NO); - queue->timeout = - GNUNET_TIME_relative_to_absolute (GNUNET_CONSTANTS_IDLE_CONNECTION_TIMEOUT); - queue->mq = GNUNET_MQ_queue_for_callbacks (&mq_send, - &mq_destroy, - &mq_cancel, - queue, - NULL, - &mq_error, - queue); -} - - -/** - * Generate and transmit our ephemeral key and the signature for - * the initial KX with the other peer. Must be called first, before - * any other bytes are ever written to the output buffer. Note that - * our cipher must already be initialized when calling this function. - * Helper function for #start_initial_kx_out(). - * - * @param queue queue to do KX for - * @param epub our public key for the KX - */ -static void -transmit_kx (struct Queue *queue, - const struct GNUNET_CRYPTO_EcdhePublicKey *epub) -{ - struct TcpHandshakeSignature ths; - struct TCPConfirmation tc; - - memcpy (queue->cwrite_buf, epub, sizeof(*epub)); - queue->cwrite_off = sizeof(*epub); - /* compute 'tc' and append in encrypted format to cwrite_buf */ - tc.sender = my_identity; - tc.monotonic_time = - GNUNET_TIME_absolute_hton (GNUNET_TIME_absolute_get_monotonic (cfg)); - GNUNET_CRYPTO_random_block (GNUNET_CRYPTO_QUALITY_NONCE, - &tc.challenge, - sizeof(tc.challenge)); - ths.purpose.purpose = htonl ( - GNUNET_SIGNATURE_PURPOSE_COMMUNICATOR_TCP_HANDSHAKE); - ths.purpose.size = htonl (sizeof(ths)); - ths.sender = my_identity; - ths.receiver = queue->target; - ths.ephemeral = *epub; - ths.monotonic_time = tc.monotonic_time; - ths.challenge = tc.challenge; - GNUNET_CRYPTO_eddsa_sign (my_private_key, - &ths, - &tc.sender_sig); - GNUNET_assert (0 == - gcry_cipher_encrypt (queue->out_cipher, - &queue->cwrite_buf[queue->cwrite_off], - sizeof(tc), - &tc, - sizeof(tc))); - queue->challenge = tc.challenge; - queue->cwrite_off += sizeof(tc); - - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "handshake written\n"); -} - - -/** - * Initialize our key material for outgoing transmissions and - * inform the other peer about it. Must be called first before - * any data is sent. - * - * @param queue the queue to setup - */ -static void -start_initial_kx_out (struct Queue *queue) -{ - struct GNUNET_CRYPTO_EcdhePublicKey epub; - struct GNUNET_HashCode k; - - GNUNET_CRYPTO_eddsa_kem_encaps (&queue->target.public_key, &epub, &k); - setup_out_cipher (queue, &k); - transmit_kx (queue, &epub); -} - - -/** - * Callback called when peerstore store operation for handshake monotime is finished. - * @param cls Queue context the store operation was executed. - * @param success Store operation was successful (GNUNET_OK) or not. - */ -static void -handshake_monotime_store_cb (void *cls, int success) -{ - struct Queue *queue = cls; - if (GNUNET_OK != success) - { - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - "Failed to store handshake monotonic time in PEERSTORE!\n"); - } - queue->handshake_monotime_sc = NULL; -} - - -/** - * Callback called by peerstore when records for GNUNET_PEERSTORE_TRANSPORT_TCP_COMMUNICATOR_HANDSHAKE - * where found. - * @param cls Queue context the store operation was executed. - * @param record The record found or NULL if there is no record left. - * @param emsg Message from peerstore. - */ -static void -handshake_monotime_cb (void *cls, - const struct GNUNET_PEERSTORE_Record *record, - const char *emsg) -{ - struct Queue *queue = cls; - struct GNUNET_TIME_AbsoluteNBO *mtbe; - struct GNUNET_TIME_Absolute mt; - const struct GNUNET_PeerIdentity *pid; - struct GNUNET_TIME_AbsoluteNBO *handshake_monotonic_time; - - (void) emsg; - - handshake_monotonic_time = &queue->handshake_monotonic_time; - pid = &queue->target; - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "tcp handshake with us %s\n", - GNUNET_i2s (&my_identity)); - if (NULL == record) - { - queue->handshake_monotime_get = NULL; - return; - } - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "tcp handshake from peer %s\n", - GNUNET_i2s (pid)); - if (sizeof(*mtbe) != record->value_size) - { - GNUNET_break (0); - return; - } - mtbe = record->value; - mt = GNUNET_TIME_absolute_ntoh (*mtbe); - if (mt.abs_value_us > GNUNET_TIME_absolute_ntoh ( - queue->handshake_monotonic_time).abs_value_us) - { - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - "Queue from %s dropped, handshake monotime in the past\n", - GNUNET_i2s (&queue->target)); - GNUNET_break (0); - queue_finish (queue); - return; - } - queue->handshake_monotime_sc = GNUNET_PEERSTORE_store (peerstore, - "transport_tcp_communicator", - pid, - GNUNET_PEERSTORE_TRANSPORT_TCP_COMMUNICATOR_HANDSHAKE, - handshake_monotonic_time, - sizeof(* - handshake_monotonic_time), - GNUNET_TIME_UNIT_FOREVER_ABS, - GNUNET_PEERSTORE_STOREOPTION_REPLACE, - & - handshake_monotime_store_cb, - queue); -} - - -/** - * We have received the first bytes from the other side on a @a queue. - * Decrypt the @a tc contained in @a ibuf and check the signature. - * Note that #setup_in_cipher() must have already been called. - * - * @param queue queue to decrypt initial bytes from other peer for - * @param[out] tc where to store the result - * @param ibuf incoming data, of size - * `INITIAL_KX_SIZE` - * @return #GNUNET_OK if the signature was OK, #GNUNET_SYSERR if not - */ -static int -decrypt_and_check_tc (struct Queue *queue, - struct TCPConfirmation *tc, - char *ibuf) -{ - struct TcpHandshakeSignature ths; - enum GNUNET_GenericReturnValue ret; - - GNUNET_assert ( - 0 == - gcry_cipher_decrypt (queue->in_cipher, - tc, - sizeof(*tc), - &ibuf[sizeof(struct GNUNET_CRYPTO_EcdhePublicKey)], - sizeof(*tc))); - ths.purpose.purpose = htonl ( - GNUNET_SIGNATURE_PURPOSE_COMMUNICATOR_TCP_HANDSHAKE); - ths.purpose.size = htonl (sizeof(ths)); - ths.sender = tc->sender; - ths.receiver = my_identity; - memcpy (&ths.ephemeral, ibuf, sizeof(struct GNUNET_CRYPTO_EcdhePublicKey)); - ths.monotonic_time = tc->monotonic_time; - ths.challenge = tc->challenge; - ret = GNUNET_CRYPTO_eddsa_verify ( - GNUNET_SIGNATURE_PURPOSE_COMMUNICATOR_TCP_HANDSHAKE, - &ths, - &tc->sender_sig, - &tc->sender.public_key); - if (GNUNET_YES == ret) - queue->handshake_monotime_get = - GNUNET_PEERSTORE_iterate (peerstore, - "transport_tcp_communicator", - &queue->target, - GNUNET_PEERSTORE_TRANSPORT_TCP_COMMUNICATOR_HANDSHAKE, - &handshake_monotime_cb, - queue); - return ret; -} - - -/** - * Read from the socket of the queue until we have enough data - * to initialize the decryption logic and can switch to regular - * reading. - * - * @param cls a `struct Queue` - */ -static void -queue_read_kx (void *cls) -{ - struct Queue *queue = cls; - ssize_t rcvd; - struct GNUNET_TIME_Relative left; - struct TCPConfirmation tc; - - queue->read_task = NULL; - left = GNUNET_TIME_absolute_get_remaining (queue->timeout); - if (0 == left.rel_value_us) - { - queue_destroy (queue); - return; - } - rcvd = GNUNET_NETWORK_socket_recv (queue->sock, - &queue->cread_buf[queue->cread_off], - BUF_SIZE - queue->cread_off); - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "Received %lu bytes for KX\n", - rcvd); - if (-1 == rcvd) - { - if ((EAGAIN != errno) && (EINTR != errno)) - { - GNUNET_log_strerror (GNUNET_ERROR_TYPE_DEBUG, "recv"); - queue_destroy (queue); - return; - } - queue->read_task = - GNUNET_SCHEDULER_add_read_net (left, queue->sock, &queue_read_kx, queue); - return; - } - queue->cread_off += rcvd; - if (queue->cread_off < INITIAL_KX_SIZE) - { - /* read more */ - queue->read_task = - GNUNET_SCHEDULER_add_read_net (left, queue->sock, &queue_read_kx, queue); - return; - } - /* we got all the data, let's find out who we are talking to! */ - setup_in_cipher ((const struct GNUNET_CRYPTO_EcdhePublicKey *) - queue->cread_buf, - queue); - if (GNUNET_OK != decrypt_and_check_tc (queue, &tc, queue->cread_buf)) - { - GNUNET_log (GNUNET_ERROR_TYPE_INFO, - "Invalid TCP KX received from %s\n", - GNUNET_a2s (queue->address, queue->address_len)); - queue_destroy (queue); - return; - } - if (0 != - memcmp (&tc.sender, &queue->target, sizeof(struct GNUNET_PeerIdentity))) - { - GNUNET_log (GNUNET_ERROR_TYPE_WARNING, - "Invalid sender in TCP KX received from %s\n", - GNUNET_a2s (queue->address, queue->address_len)); - queue_destroy (queue); - return; - } - send_challenge (tc.challenge, queue); - queue->write_task = - GNUNET_SCHEDULER_add_write_net (GNUNET_TIME_UNIT_FOREVER_REL, - queue->sock, - &queue_write, - queue); - - /* update queue timeout */ - reschedule_queue_timeout (queue); - /* prepare to continue with regular read task immediately */ - memmove (queue->cread_buf, - &queue->cread_buf[INITIAL_KX_SIZE], - queue->cread_off - (INITIAL_KX_SIZE)); - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "cread_off is %lu bytes before adjusting\n", - queue->cread_off); - queue->cread_off -= INITIAL_KX_SIZE; - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "cread_off set to %lu bytes\n", - queue->cread_off); - queue->read_task = GNUNET_SCHEDULER_add_now (&queue_read, queue); -} - - -/** - * Read from the socket of the proto queue until we have enough data - * to upgrade to full queue. - * - * @param cls a `struct ProtoQueue` - */ -static void -proto_read_kx (void *cls) -{ - struct ProtoQueue *pq = cls; - ssize_t rcvd; - struct GNUNET_TIME_Relative left; - struct Queue *queue; - struct TCPConfirmation tc; - GNUNET_SCHEDULER_TaskCallback read_task; - - pq->read_task = NULL; - left = GNUNET_TIME_absolute_get_remaining (pq->timeout); - if (0 == left.rel_value_us) - { - free_proto_queue (pq); - return; - } - rcvd = GNUNET_NETWORK_socket_recv (pq->sock, - &pq->ibuf[pq->ibuf_off], - sizeof(pq->ibuf) - pq->ibuf_off); - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "Proto received %lu bytes for KX\n", rcvd); - if (-1 == rcvd) - { - if ((EAGAIN != errno) && (EINTR != errno)) - { - GNUNET_log_strerror (GNUNET_ERROR_TYPE_DEBUG, "recv"); - free_proto_queue (pq); - return; - } - /* try again */ - pq->read_task = - GNUNET_SCHEDULER_add_read_net (left, pq->sock, &proto_read_kx, pq); - return; - } - pq->ibuf_off += rcvd; - if (sizeof (struct TCPNATProbeMessage) == pq->ibuf_off) - { - struct TCPNATProbeMessage *pm = (struct TCPNATProbeMessage *) pq->ibuf; - - check_and_remove_pending_reversal (pq->address, pq->address->sa_family, - &pm->clientIdentity); - - queue = GNUNET_new (struct Queue); - queue->target = pm->clientIdentity; - queue->cs = GNUNET_TRANSPORT_CS_OUTBOUND; - read_task = &queue_read_kx; - } - else if (pq->ibuf_off > sizeof(pq->ibuf)) - { - /* read more */ - pq->read_task = - GNUNET_SCHEDULER_add_read_net (left, pq->sock, &proto_read_kx, pq); - return; - } - else - { - /* we got all the data, let's find out who we are talking to! */ - queue = GNUNET_new (struct Queue); - setup_in_cipher ((const struct GNUNET_CRYPTO_EcdhePublicKey *) pq->ibuf, - queue); - if (GNUNET_OK != decrypt_and_check_tc (queue, &tc, pq->ibuf)) - { - GNUNET_log (GNUNET_ERROR_TYPE_INFO, - "Invalid TCP KX received from %s\n", - GNUNET_a2s (pq->address, pq->address_len)); - gcry_cipher_close (queue->in_cipher); - GNUNET_free (queue); - free_proto_queue (pq); - return; - } - queue->target = tc.sender; - queue->cs = GNUNET_TRANSPORT_CS_INBOUND; - read_task = &queue_read; - } - queue->address = pq->address; /* steals reference */ - queue->address_len = pq->address_len; - queue->listen_sock = pq->listen_sock; - queue->sock = pq->sock; - - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "created queue with target %s\n", - GNUNET_i2s (&queue->target)); - - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "start kx proto\n"); - - start_initial_kx_out (queue); - boot_queue (queue); - queue->read_task = - GNUNET_SCHEDULER_add_read_net (GNUNET_CONSTANTS_IDLE_CONNECTION_TIMEOUT, - queue->sock, - read_task, - queue); - queue->write_task = - GNUNET_SCHEDULER_add_write_net (GNUNET_TIME_UNIT_FOREVER_REL, - queue->sock, - &queue_write, - queue); - // TODO To early! Move it somewhere else. - // send_challenge (tc.challenge, queue); - queue->challenge_received = tc.challenge; - - GNUNET_CONTAINER_DLL_remove (proto_head, proto_tail, pq); - GNUNET_free (pq); -} - - -static struct ProtoQueue * -create_proto_queue (struct GNUNET_NETWORK_Handle *sock, - struct sockaddr *in, - socklen_t addrlen) -{ - struct ProtoQueue *pq = GNUNET_new (struct ProtoQueue); - - if (NULL == sock) - { - // sock = GNUNET_CONNECTION_create_from_sockaddr (AF_INET, addr, addrlen); - sock = GNUNET_NETWORK_socket_create (in->sa_family, SOCK_STREAM, 0); - if (NULL == sock) - { - GNUNET_log (GNUNET_ERROR_TYPE_WARNING, - "socket(%d) failed: %s", - in->sa_family, - strerror (errno)); - GNUNET_free (in); - GNUNET_free (pq); - return NULL; - } - if ((GNUNET_OK != GNUNET_NETWORK_socket_connect (sock, in, addrlen)) && - (errno != EINPROGRESS)) - { - GNUNET_log (GNUNET_ERROR_TYPE_WARNING, - "connect to `%s' failed: %s", - GNUNET_a2s (in, addrlen), - strerror (errno)); - GNUNET_NETWORK_socket_close (sock); - GNUNET_free (in); - return NULL; - } - } - pq->address_len = addrlen; - pq->address = in; - pq->timeout = GNUNET_TIME_relative_to_absolute (PROTO_QUEUE_TIMEOUT); - pq->sock = sock; - pq->read_task = GNUNET_SCHEDULER_add_read_net (PROTO_QUEUE_TIMEOUT, - pq->sock, - &proto_read_kx, - pq); - GNUNET_CONTAINER_DLL_insert (proto_head, proto_tail, pq); - - return pq; -} - - -/** - * We have been notified that our listen socket has something to - * read. Do the read and reschedule this function to be called again - * once more is available. - * - * @param cls ListenTask with listening socket and task - */ -static void -listen_cb (void *cls) -{ - struct sockaddr_storage in; - socklen_t addrlen; - struct GNUNET_NETWORK_Handle *sock; - struct ListenTask *lt; - struct sockaddr *in_addr; - - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "listen_cb\n"); - - lt = cls; - - lt->listen_task = NULL; - GNUNET_assert (NULL != lt->listen_sock); - addrlen = sizeof(in); - memset (&in, 0, sizeof(in)); - sock = GNUNET_NETWORK_socket_accept (lt->listen_sock, - (struct sockaddr*) &in, - &addrlen); - if ((NULL == sock) && ((EMFILE == errno) || (ENFILE == errno))) - return; /* system limit reached, wait until connection goes down */ - lt->listen_task = GNUNET_SCHEDULER_add_read_net (GNUNET_TIME_UNIT_FOREVER_REL, - lt->listen_sock, - &listen_cb, - lt); - if ((NULL == sock) && ((EAGAIN == errno) || (ENOBUFS == errno))) - return; - if (NULL == sock) - { - GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING, "accept"); - return; - } - in_addr = GNUNET_memdup (&in, addrlen); - create_proto_queue (sock, in_addr, addrlen); -} - - -static void -try_connection_reversal (void *cls, - const struct sockaddr *addr, - socklen_t addrlen) -{ - (void) cls; - struct TCPNATProbeMessage pm; - struct ProtoQueue *pq; - struct sockaddr *in_addr; - - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "addr->sa_family %d\n", - addr->sa_family); - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "Try to connect back\n"); - in_addr = GNUNET_memdup (addr, addrlen); - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "in_addr->sa_family %d\n", - in_addr->sa_family); - pq = create_proto_queue (NULL, in_addr, addrlen); - if (NULL != pq) - { - pm.header.size = htons (sizeof(struct TCPNATProbeMessage)); - pm.header.type = htons (GNUNET_MESSAGE_TYPE_TRANSPORT_TCP_NAT_PROBE); - pm.clientIdentity = my_identity; - memcpy (pq->write_buf, &pm, sizeof(struct TCPNATProbeMessage)); - pq->write_off = sizeof(struct TCPNATProbeMessage); - pq->write_task = GNUNET_SCHEDULER_add_write_net (PROTO_QUEUE_TIMEOUT, - pq->sock, - &proto_queue_write, - pq); - } - else - { - GNUNET_log (GNUNET_ERROR_TYPE_WARNING, - "Couldn't create ProtoQueue for sending TCPNATProbeMessage\n"); - } -} - - -static void -pending_reversal_timeout (void *cls) -{ - struct sockaddr *in = cls; - struct PendingReversal *pending_reversal; - struct GNUNET_HashCode key; - - GNUNET_CRYPTO_hash (in, - sizeof(struct sockaddr), - &key); - pending_reversal = GNUNET_CONTAINER_multihashmap_get (pending_reversals, - &key); - - GNUNET_assert (NULL != pending_reversal); - - if (GNUNET_NO == GNUNET_CONTAINER_multihashmap_remove (pending_reversals, - &key, - pending_reversal)) - GNUNET_log (GNUNET_ERROR_TYPE_WARNING, - "No pending reversal found for address %s\n", - GNUNET_a2s (in, sizeof (struct sockaddr))); - GNUNET_free (pending_reversal->in); - GNUNET_free (pending_reversal); -} - - -/** - * Function called by the transport service to initialize a - * message queue given address information about another peer. - * If and when the communication channel is established, the - * communicator must call #GNUNET_TRANSPORT_communicator_mq_add() - * to notify the service that the channel is now up. It is - * the responsibility of the communicator to manage sane - * retries and timeouts for any @a peer/@a address combination - * provided by the transport service. Timeouts and retries - * do not need to be signalled to the transport service. - * - * @param cls closure - * @param peer identity of the other peer - * @param address where to send the message, human-readable - * communicator-specific format, 0-terminated, UTF-8 - * @return #GNUNET_OK on success, #GNUNET_SYSERR if the provided address is - * invalid - */ -static int -mq_init (void *cls, const struct GNUNET_PeerIdentity *peer, const char *address) -{ - struct sockaddr *in; - socklen_t in_len = 0; - const char *path; - struct sockaddr_in *v4; - struct sockaddr_in6 *v6; - unsigned int is_natd = GNUNET_NO; - struct GNUNET_HashCode key; - - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "Connecting to %s at %s\n", - GNUNET_i2s (peer), - address); - if (0 != strncmp (address, - COMMUNICATOR_ADDRESS_PREFIX "-", - strlen (COMMUNICATOR_ADDRESS_PREFIX "-"))) - { - GNUNET_break_op (0); - return GNUNET_SYSERR; - } - path = &address[strlen (COMMUNICATOR_ADDRESS_PREFIX "-")]; - in = tcp_address_to_sockaddr (path, &in_len); - - if (NULL == in) - { - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - "Failed to setup TCP socket address\n"); - return GNUNET_SYSERR; - } - - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "in %s\n", - GNUNET_a2s (in, in_len)); - - switch (in->sa_family) - { - case AF_INET: - v4 = (struct sockaddr_in *) in; - if (0 == v4->sin_port) - { - is_natd = GNUNET_YES; - GNUNET_CRYPTO_hash (in, - sizeof(struct sockaddr), - &key); - if (GNUNET_YES == GNUNET_CONTAINER_multihashmap_contains ( - pending_reversals, - &key)) - { - GNUNET_log (GNUNET_ERROR_TYPE_WARNING, - "There is already a request reversal for `%s'at `%s'\n", - GNUNET_i2s (peer), - address); - GNUNET_free (in); - return GNUNET_SYSERR; - } - } - break; - - case AF_INET6: - v6 = (struct sockaddr_in6 *) in; - if (0 == v6->sin6_port) - { - GNUNET_log (GNUNET_ERROR_TYPE_WARNING, - "Request reversal for `%s' at `%s' not possible for an IPv6 address\n", - GNUNET_i2s (peer), - address); - GNUNET_free (in); - return GNUNET_SYSERR; - } - break; - - default: - GNUNET_assert (0); - } - - if (GNUNET_YES == is_natd) - { - struct sockaddr_in local_sa; - struct PendingReversal *pending_reversal; - - memset (&local_sa, 0, sizeof(local_sa)); - local_sa.sin_family = AF_INET; - local_sa.sin_port = htons (bind_port); - /* We leave sin_address at 0, let the kernel figure it out, - even if our bind() is more specific. (May want to reconsider - later.) */ - if (GNUNET_OK != GNUNET_NAT_request_reversal (nat, &local_sa, v4)) - { - GNUNET_log (GNUNET_ERROR_TYPE_WARNING, - "request reversal for `%s' at `%s' failed\n", - GNUNET_i2s (peer), - address); - GNUNET_free (in); - return GNUNET_SYSERR; - } - pending_reversal = GNUNET_new (struct PendingReversal); - pending_reversal->in = in; - GNUNET_assert (GNUNET_OK == - GNUNET_CONTAINER_multihashmap_put (pending_reversals, - &key, - pending_reversal, - GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_ONLY)); - pending_reversal->target = *peer; - pending_reversal->timeout_task = GNUNET_SCHEDULER_add_delayed (NAT_TIMEOUT, - & - pending_reversal_timeout, - in); - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "Created NAT WAIT connection to `%s' at `%s'\n", - GNUNET_i2s (peer), - GNUNET_a2s (in, sizeof (struct sockaddr))); - } - else - { - struct GNUNET_NETWORK_Handle *sock; - struct Queue *queue; - - sock = GNUNET_NETWORK_socket_create (in->sa_family, SOCK_STREAM, - IPPROTO_TCP); - if (NULL == sock) - { - GNUNET_log (GNUNET_ERROR_TYPE_WARNING, - "socket(%d) failed: %s", - in->sa_family, - strerror (errno)); - GNUNET_free (in); - return GNUNET_SYSERR; - } - if ((GNUNET_OK != GNUNET_NETWORK_socket_connect (sock, in, in_len)) && - (errno != EINPROGRESS)) - { - GNUNET_log (GNUNET_ERROR_TYPE_WARNING, - "connect to `%s' failed: %s", - address, - strerror (errno)); - GNUNET_NETWORK_socket_close (sock); - GNUNET_free (in); - return GNUNET_SYSERR; - } - - queue = GNUNET_new (struct Queue); - queue->target = *peer; - queue->address = in; - queue->address_len = in_len; - queue->sock = sock; - queue->cs = GNUNET_TRANSPORT_CS_OUTBOUND; - boot_queue (queue); - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "booted queue with target %s\n", - GNUNET_i2s (&queue->target)); - // queue->mq_awaits_continue = GNUNET_YES; - queue->read_task = - GNUNET_SCHEDULER_add_read_net (GNUNET_CONSTANTS_IDLE_CONNECTION_TIMEOUT, - queue->sock, - &queue_read_kx, - queue); - - - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "start kx mq_init\n"); - - start_initial_kx_out (queue); - queue->write_task = - GNUNET_SCHEDULER_add_write_net (GNUNET_TIME_UNIT_FOREVER_REL, - queue->sock, - &queue_write, - queue); - } - - return GNUNET_OK; -} - - -/** - * Iterator over all ListenTasks to clean up. - * - * @param cls NULL - * @param key unused - * @param value the ListenTask to cancel. - * @return #GNUNET_OK to continue to iterate - */ -static int -get_lt_delete_it (void *cls, - const struct GNUNET_HashCode *key, - void *value) -{ - struct ListenTask *lt = value; - - (void) cls; - (void) key; - if (NULL != lt->listen_task) - { - GNUNET_SCHEDULER_cancel (lt->listen_task); - lt->listen_task = NULL; - } - if (NULL != lt->listen_sock) - { - GNUNET_break (GNUNET_OK == GNUNET_NETWORK_socket_close (lt->listen_sock)); - lt->listen_sock = NULL; - } - GNUNET_free (lt); - return GNUNET_OK; -} - - -/** - * Iterator over all message queues to clean up. - * - * @param cls NULL - * @param target unused - * @param value the queue to destroy - * @return #GNUNET_OK to continue to iterate - */ -static int -get_queue_delete_it (void *cls, - const struct GNUNET_PeerIdentity *target, - void *value) -{ - struct Queue *queue = value; - - (void) cls; - (void) target; - queue_destroy (queue); - return GNUNET_OK; -} - - -/** - * Shutdown the UNIX communicator. - * - * @param cls NULL (always) - */ -static void -do_shutdown (void *cls) -{ - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "Shutdown %s!\n", - shutdown_running ? "running" : "not running"); - - if (GNUNET_YES == shutdown_running) - return; - else - shutdown_running = GNUNET_YES; - - while (NULL != proto_head) - free_proto_queue (proto_head); - if (NULL != nat) - { - GNUNET_NAT_unregister (nat); - nat = NULL; - } - GNUNET_CONTAINER_multihashmap_iterate (pending_reversals, - &pending_reversals_delete_it, NULL); - GNUNET_CONTAINER_multihashmap_destroy (pending_reversals); - GNUNET_CONTAINER_multihashmap_iterate (lt_map, &get_lt_delete_it, NULL); - GNUNET_CONTAINER_multihashmap_destroy (lt_map); - GNUNET_CONTAINER_multipeermap_iterate (queue_map, &get_queue_delete_it, NULL); - GNUNET_CONTAINER_multipeermap_destroy (queue_map); - if (NULL != ch) - { - GNUNET_TRANSPORT_communicator_address_remove_all (ch); - GNUNET_TRANSPORT_communicator_disconnect (ch); - ch = NULL; - } - if (NULL != stats) - { - GNUNET_STATISTICS_destroy (stats, GNUNET_NO); - stats = NULL; - } - if (NULL != my_private_key) - { - GNUNET_free (my_private_key); - my_private_key = NULL; - } - if (NULL != is) - { - GNUNET_NT_scanner_done (is); - is = NULL; - } - if (NULL != peerstore) - { - GNUNET_PEERSTORE_disconnect (peerstore, GNUNET_NO); - peerstore = NULL; - } - if (NULL != resolve_request_handle) - { - GNUNET_RESOLVER_request_cancel (resolve_request_handle); - resolve_request_handle = NULL; - } - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "Shutdown done!\n"); -} - - -/** - * Function called when the transport service has received an - * acknowledgement for this communicator (!) via a different return - * path. - * - * Not applicable for TCP. - * - * @param cls closure - * @param sender which peer sent the notification - * @param msg payload - */ -static void -enc_notify_cb (void *cls, - const struct GNUNET_PeerIdentity *sender, - const struct GNUNET_MessageHeader *msg) -{ - (void) cls; - (void) sender; - (void) msg; - GNUNET_break_op (0); -} - - -/** - * Signature of the callback passed to #GNUNET_NAT_register() for - * a function to call whenever our set of 'valid' addresses changes. - * - * @param cls closure - * @param[in,out] app_ctx location where the app can store stuff - * on add and retrieve it on remove - * @param add_remove #GNUNET_YES to add a new public IP address, - * #GNUNET_NO to remove a previous (now invalid) one - * @param ac address class the address belongs to - * @param addr either the previous or the new public IP address - * @param addrlen actual length of the @a addr - */ -static void -nat_address_cb (void *cls, - void **app_ctx, - int add_remove, - enum GNUNET_NAT_AddressClass ac, - const struct sockaddr *addr, - socklen_t addrlen) -{ - char *my_addr; - struct GNUNET_TRANSPORT_AddressIdentifier *ai; - - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "nat address cb %s %s\n", - add_remove ? "add" : "remove", - GNUNET_a2s (addr, addrlen)); - - if (GNUNET_YES == add_remove) - { - enum GNUNET_NetworkType nt; - - GNUNET_asprintf (&my_addr, - "%s-%s", - COMMUNICATOR_ADDRESS_PREFIX, - GNUNET_a2s (addr, addrlen)); - nt = GNUNET_NT_scanner_get_type (is, addr, addrlen); - ai = - GNUNET_TRANSPORT_communicator_address_add (ch, - my_addr, - nt, - GNUNET_TIME_UNIT_FOREVER_REL); - GNUNET_free (my_addr); - *app_ctx = ai; - } - else - { - ai = *app_ctx; - GNUNET_TRANSPORT_communicator_address_remove (ai); - *app_ctx = NULL; - } -} - - -/** - * This method adds addresses to the DLL, that are later register at the NAT service. - */ -static void -add_addr (struct sockaddr *in, socklen_t in_len) -{ - - struct Addresses *saddrs; - - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "add address %s\n", - GNUNET_a2s (in, in_len)); - - saddrs = GNUNET_new (struct Addresses); - saddrs->addr = in; - saddrs->addr_len = in_len; - GNUNET_CONTAINER_DLL_insert (addrs_head, addrs_tail, saddrs); - - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "after add address %s\n", - GNUNET_a2s (in, in_len)); - - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "add address %s\n", - GNUNET_a2s (saddrs->addr, saddrs->addr_len)); - - addrs_lens++; -} - - -/** - * This method launch network interactions for each address we like to bind to. - * - * @param addr The address we will listen to. - * @param in_len The length of the address we will listen to. - * @return GNUNET_SYSERR in case of error. GNUNET_OK in case we are successfully listen to the address. - */ -static int -init_socket (struct sockaddr *addr, - socklen_t in_len) -{ - struct sockaddr_storage in_sto; - socklen_t sto_len; - struct GNUNET_NETWORK_Handle *listen_sock; - struct ListenTask *lt; - int sockfd; - struct GNUNET_HashCode h_sock; - - if (NULL == addr) - { - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - "Address is NULL.\n"); - return GNUNET_SYSERR; - } - - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "address %s\n", - GNUNET_a2s (addr, in_len)); - - listen_sock = - GNUNET_NETWORK_socket_create (addr->sa_family, SOCK_STREAM, IPPROTO_TCP); - if (NULL == listen_sock) - { - GNUNET_log_strerror (GNUNET_ERROR_TYPE_ERROR, "socket"); - return GNUNET_SYSERR; - } - - if (GNUNET_OK != GNUNET_NETWORK_socket_bind (listen_sock, addr, in_len)) - { - GNUNET_log_strerror (GNUNET_ERROR_TYPE_ERROR, "bind"); - GNUNET_NETWORK_socket_close (listen_sock); - listen_sock = NULL; - return GNUNET_SYSERR; - } - - if (GNUNET_OK != - GNUNET_NETWORK_socket_listen (listen_sock, - 5)) - { - GNUNET_log_strerror (GNUNET_ERROR_TYPE_ERROR, - "listen"); - GNUNET_NETWORK_socket_close (listen_sock); - listen_sock = NULL; - return GNUNET_SYSERR; - } - - /* We might have bound to port 0, allowing the OS to figure it out; - thus, get the real IN-address from the socket */ - sto_len = sizeof(in_sto); - - if (0 != getsockname (GNUNET_NETWORK_get_fd (listen_sock), - (struct sockaddr *) &in_sto, - &sto_len)) - { - memcpy (&in_sto, addr, in_len); - sto_len = in_len; - } - - // addr = (struct sockaddr *) &in_sto; - in_len = sto_len; - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "Bound to `%s'\n", - GNUNET_a2s ((const struct sockaddr *) &in_sto, sto_len)); - stats = GNUNET_STATISTICS_create ("C-TCP", cfg); - - if (NULL == is) - is = GNUNET_NT_scanner_init (); - - if (NULL == my_private_key) - my_private_key = GNUNET_CRYPTO_eddsa_key_create_from_configuration (cfg); - if (NULL == my_private_key) - { - GNUNET_log ( - GNUNET_ERROR_TYPE_ERROR, - _ ( - "Transport service is lacking key configuration settings. Exiting.\n")); - if (NULL != resolve_request_handle) - GNUNET_RESOLVER_request_cancel (resolve_request_handle); - GNUNET_SCHEDULER_shutdown (); - return GNUNET_SYSERR; - } - GNUNET_CRYPTO_eddsa_key_get_public (my_private_key, &my_identity.public_key); - /* start listening */ - - lt = GNUNET_new (struct ListenTask); - lt->listen_sock = listen_sock; - - lt->listen_task = GNUNET_SCHEDULER_add_read_net (GNUNET_TIME_UNIT_FOREVER_REL, - listen_sock, - &listen_cb, - lt); - - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "creating hash\n"); - sockfd = GNUNET_NETWORK_get_fd (lt->listen_sock); - GNUNET_CRYPTO_hash (&sockfd, - sizeof(int), - &h_sock); - - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "creating map\n"); - if (NULL == lt_map) - lt_map = GNUNET_CONTAINER_multihashmap_create (2, GNUNET_NO); - - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "creating map entry\n"); - GNUNET_assert (GNUNET_OK == - GNUNET_CONTAINER_multihashmap_put (lt_map, - &h_sock, - lt, - GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_ONLY)); - - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "map entry created\n"); - - if (NULL == queue_map) - queue_map = GNUNET_CONTAINER_multipeermap_create (10, GNUNET_NO); - - if (NULL == ch) - ch = GNUNET_TRANSPORT_communicator_connect (cfg, - COMMUNICATOR_CONFIG_SECTION, - COMMUNICATOR_ADDRESS_PREFIX, - GNUNET_TRANSPORT_CC_RELIABLE, - &mq_init, - NULL, - &enc_notify_cb, - NULL); - - if (NULL == ch) - { - GNUNET_break (0); - if (NULL != resolve_request_handle) - GNUNET_RESOLVER_request_cancel (resolve_request_handle); - GNUNET_SCHEDULER_shutdown (); - return GNUNET_SYSERR; - } - - add_addr (addr, in_len); - return GNUNET_OK; - -} - - -/** - * This method reads from the DLL addrs_head to register them at the NAT service. - */ -static void -nat_register () -{ - struct sockaddr **saddrs; - socklen_t *saddr_lens; - int i; - size_t len; - - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "starting nat register!\n"); - len = 0; - i = 0; - saddrs = GNUNET_malloc ((addrs_lens) * sizeof(struct sockaddr *)); - saddr_lens = GNUNET_malloc ((addrs_lens) * sizeof(socklen_t)); - for (struct Addresses *pos = addrs_head; NULL != pos; pos = pos->next) - { - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "registering address %s\n", - GNUNET_a2s (addrs_head->addr, addrs_head->addr_len)); - - saddr_lens[i] = addrs_head->addr_len; - len += saddr_lens[i]; - saddrs[i] = GNUNET_memdup (addrs_head->addr, saddr_lens[i]); - i++; - } - - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "registering addresses %lu %lu %lu %lu\n", - (addrs_lens) * sizeof(struct sockaddr *), - (addrs_lens) * sizeof(socklen_t), - len, - sizeof(COMMUNICATOR_CONFIG_SECTION)); - nat = GNUNET_NAT_register (cfg, - COMMUNICATOR_CONFIG_SECTION, - IPPROTO_TCP, - addrs_lens, - (const struct sockaddr **) saddrs, - saddr_lens, - &nat_address_cb, - try_connection_reversal, - NULL /* closure */); - for (i = addrs_lens - 1; i >= 0; i--) - GNUNET_free (saddrs[i]); - GNUNET_free (saddrs); - GNUNET_free (saddr_lens); - - if (NULL == nat) - { - GNUNET_break (0); - if (NULL != resolve_request_handle) - GNUNET_RESOLVER_request_cancel (resolve_request_handle); - GNUNET_SCHEDULER_shutdown (); - } -} - - -/** - * This method is the callback called by the resolver API, and wraps method init_socket. - * - * @param cls The port we will bind to. - * @param addr The address we will bind to. - * @param in_len The length of the address we will bind to. - */ -static void -init_socket_resolv (void *cls, - const struct sockaddr *addr, - socklen_t in_len) -{ - struct sockaddr_in *v4; - struct sockaddr_in6 *v6; - struct sockaddr *in; - - (void) cls; - if (NULL != addr) - { - if (AF_INET == addr->sa_family) - { - v4 = (struct sockaddr_in *) addr; - in = tcp_address_to_sockaddr_numeric_v4 (&in_len, *v4, bind_port);// _global); - } - else if (AF_INET6 == addr->sa_family) - { - v6 = (struct sockaddr_in6 *) addr; - in = tcp_address_to_sockaddr_numeric_v6 (&in_len, *v6, bind_port);// _global); - } - else - { - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - "Address family %u not suitable (not AF_INET %u nor AF_INET6 %u \n", - addr->sa_family, - AF_INET, - AF_INET6); - return; - } - init_socket (in, in_len); - } - else - { - GNUNET_log (GNUNET_ERROR_TYPE_INFO, - "Address is NULL. This might be an error or the resolver finished resolving.\n"); - if (NULL == addrs_head) - { - GNUNET_log (GNUNET_ERROR_TYPE_WARNING, - "Resolver finished resolving, but we do not listen to an address!.\n"); - return; - } - nat_register (); - } -} - - -/** - * Setup communicator and launch network interactions. - * - * @param cls NULL (always) - * @param args remaining command-line arguments - * @param cfgfile name of the configuration file used (for saving, can be NULL!) - * @param c configuration - */ -static void -run (void *cls, - char *const *args, - const char *cfgfile, - const struct GNUNET_CONFIGURATION_Handle *c) -{ - char *bindto; - struct sockaddr *in; - socklen_t in_len; - struct sockaddr_in v4; - struct sockaddr_in6 v6; - char *start; - unsigned int port; - char dummy[2]; - char *rest = NULL; - struct PortOnlyIpv4Ipv6 *po; - socklen_t addr_len_ipv4; - socklen_t addr_len_ipv6; - - (void) cls; - - pending_reversals = GNUNET_CONTAINER_multihashmap_create (16, GNUNET_NO); - memset (&v4,0,sizeof(struct sockaddr_in)); - memset (&v6,0,sizeof(struct sockaddr_in6)); - cfg = c; - if (GNUNET_OK != - GNUNET_CONFIGURATION_get_value_string (cfg, - COMMUNICATOR_CONFIG_SECTION, - "BINDTO", - &bindto)) - { - GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR, - COMMUNICATOR_CONFIG_SECTION, - "BINDTO"); - return; - } - if (GNUNET_OK != - GNUNET_CONFIGURATION_get_value_number (cfg, - COMMUNICATOR_CONFIG_SECTION, - "MAX_QUEUE_LENGTH", - &max_queue_length)) - max_queue_length = DEFAULT_MAX_QUEUE_LENGTH; - if (GNUNET_OK != - GNUNET_CONFIGURATION_get_value_time (cfg, - COMMUNICATOR_CONFIG_SECTION, - "REKEY_INTERVAL", - &rekey_interval)) - rekey_interval = DEFAULT_REKEY_INTERVAL; - - peerstore = GNUNET_PEERSTORE_connect (cfg); - if (NULL == peerstore) - { - GNUNET_free (bindto); - GNUNET_break (0); - GNUNET_SCHEDULER_shutdown (); - return; - } - - GNUNET_SCHEDULER_add_shutdown (&do_shutdown, NULL); - - if (1 == sscanf (bindto, "%u%1s", &bind_port, dummy)) - { - po = tcp_address_to_sockaddr_port_only (bindto, &bind_port); - addr_len_ipv4 = po->addr_len_ipv4; - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "address po %s\n", - GNUNET_a2s (po->addr_ipv4, addr_len_ipv4)); - if (NULL != po->addr_ipv4) - { - init_socket (po->addr_ipv4, addr_len_ipv4); - } - if (NULL != po->addr_ipv6) - { - addr_len_ipv6 = po->addr_len_ipv6; - init_socket (po->addr_ipv6, addr_len_ipv6); - } - GNUNET_free (po); - nat_register (); - GNUNET_free (bindto); - return; - } - - start = extract_address (bindto); - // FIXME: check for NULL == start... - if (1 == inet_pton (AF_INET, start, &v4.sin_addr)) - { - bind_port = extract_port (bindto); - - in = tcp_address_to_sockaddr_numeric_v4 (&in_len, v4, bind_port); - init_socket (in, in_len); - nat_register (); - GNUNET_free (start); - GNUNET_free (bindto); - return; - } - - if (1 == inet_pton (AF_INET6, start, &v6.sin6_addr)) - { - bind_port = extract_port (bindto); - in = tcp_address_to_sockaddr_numeric_v6 (&in_len, v6, bind_port); - init_socket (in, in_len); - nat_register (); - GNUNET_free (start); - GNUNET_free (bindto); - return; - } - - bind_port = extract_port (bindto); - resolve_request_handle = GNUNET_RESOLVER_ip_get (strtok_r (bindto, - ":", - &rest), - AF_UNSPEC, - GNUNET_TIME_UNIT_MINUTES, - &init_socket_resolv, - &port); - - GNUNET_free (bindto); - GNUNET_free (start); -} - - -/** - * The main function for the UNIX communicator. - * - * @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 - }; - int ret; - - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "Starting tcp communicator\n"); - if (GNUNET_OK != - GNUNET_STRINGS_get_utf8_args (argc, argv, - &argc, &argv)) - return 2; - - ret = (GNUNET_OK == - GNUNET_PROGRAM_run (argc, - argv, - "gnunet-communicator-tcp", - _ ("GNUnet TCP communicator"), - options, - &run, - NULL)) - ? 0 - : 1; - GNUNET_free_nz ((void *) argv); - return ret; -} - - -/* end of gnunet-communicator-tcp.c */ diff --git a/src/transport/gnunet-communicator-udp.c b/src/transport/gnunet-communicator-udp.c deleted file mode 100644 index f9bbe91f6..000000000 --- a/src/transport/gnunet-communicator-udp.c +++ /dev/null @@ -1,3444 +0,0 @@ -/* - This file is part of GNUnet - Copyright (C) 2010-2014, 2018, 2019 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 . - - SPDX-License-Identifier: AGPL3.0-or-later - */ - -/** - * @file transport/gnunet-communicator-udp.c - * @brief Transport plugin using UDP. - * @author Christian Grothoff - * - * TODO: - * - consider imposing transmission limits in the absence - * of ACKs; or: maybe this should be done at TNG service level? - * (at least the receiver might want to enforce limits on - * KX/DH operations per sender in here) (#5552) - * - overall, we should look more into flow control support - * (either in backchannel, or general solution in TNG service) - * - handle addresses discovered from broadcasts (#5551) - * (think: what was the story again on address validation? - * where is the API for that!?!) - * - support DNS names in BINDTO option (#5528) - * - support NAT connection reversal method (#5529) - * - support other UDP-specific NAT traversal methods (#) - */ -#include "platform.h" -#include "gnunet_common.h" -#include "gnunet_util_lib.h" -#include "gnunet_protocols.h" -#include "gnunet_signatures.h" -#include "gnunet_constants.h" -#include "gnunet_nat_service.h" -#include "gnunet_statistics_service.h" -#include "gnunet_transport_application_service.h" -#include "gnunet_transport_communication_service.h" - -/** - * How often do we rekey based on time (at least) - */ -#define DEFAULT_REKEY_TIME_INTERVAL GNUNET_TIME_UNIT_DAYS - -/** - * How long do we wait until we must have received the initial KX? - */ -#define PROTO_QUEUE_TIMEOUT GNUNET_TIME_UNIT_MINUTES - -/** - * How often do we broadcast our presence on the LAN? - */ -#define BROADCAST_FREQUENCY GNUNET_TIME_UNIT_MINUTES - -/** - * How often do we scan for changes to our network interfaces? - */ -#define INTERFACE_SCAN_FREQUENCY \ - GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_MINUTES, 5) - -/** - * How long do we believe our addresses to remain up (before - * the other peer should revalidate). - */ -#define ADDRESS_VALIDITY_PERIOD GNUNET_TIME_UNIT_HOURS - -#define WORKING_QUEUE_INTERVALL \ - GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_MICROSECONDS,1) - -/** - * AES key size. - */ -#define AES_KEY_SIZE (256 / 8) - -/** - * AES (GCM) IV size. - */ -#define AES_IV_SIZE (96 / 8) - -/** - * Size of the GCM tag. - */ -#define GCM_TAG_SIZE (128 / 8) - -#define GENERATE_AT_ONCE 16 - -/** - * If we fall below this number of available KCNs, - * we generate additional ACKs until we reach - * #KCN_TARGET. - * Should be large enough that we don't generate ACKs all - * the time and still have enough time for the ACK to - * arrive before the sender runs out. So really this - * should ideally be based on the RTT. - */ -#define KCN_THRESHOLD 96 - -/** - * How many KCNs do we keep around *after* we hit - * the #KCN_THRESHOLD? Should be larger than - * #KCN_THRESHOLD so we do not generate just one - * ACK at the time. - */ -#define KCN_TARGET 128 - -/** - * What is the maximum delta between KCN sequence numbers - * that we allow. Used to expire 'ancient' KCNs that likely - * were dropped by the network. Must be larger than - * KCN_TARGET (otherwise we generate new KCNs all the time), - * but not too large (otherwise packet loss may cause - * sender to fall back to KX needlessly when sender runs - * out of ACK'ed KCNs due to losses). - */ -#define MAX_SQN_DELTA 160 - -/** - * How many shared master secrets do we keep around - * at most per sender? Should be large enough so - * that we generally have a chance of sending an ACK - * before the sender already rotated out the master - * secret. Generally values around #KCN_TARGET make - * sense. Might make sense to adapt to RTT if we had - * a good measurement... - */ -#define MAX_SECRETS 256 - -/** - * Default value for how often we do rekey based on number of bytes transmitted? - * (additionally randomized). - */ -#define DEFAULT_REKEY_MAX_BYTES (1024LLU * 1024 * 1024 * 4LLU) - -/** - * Address prefix used by the communicator. - */ - -#define COMMUNICATOR_ADDRESS_PREFIX "udp" - -/** - * Configuration section used by the communicator. - */ -#define COMMUNICATOR_CONFIG_SECTION "communicator-udp" - -GNUNET_NETWORK_STRUCT_BEGIN - - -/** - * Signature we use to verify that the ephemeral key was really chosen by - * the specified sender. If possible, the receiver should respond with - * a `struct UDPAck` (possibly via backchannel). - */ -struct UdpHandshakeSignature -{ - /** - * Purpose must be #GNUNET_SIGNATURE_PURPOSE_COMMUNICATOR_UDP_HANDSHAKE - */ - struct GNUNET_CRYPTO_EccSignaturePurpose purpose; - - /** - * Identity of the inititor of the UDP connection (UDP client). - */ - struct GNUNET_PeerIdentity sender; - - /** - * Presumed identity of the target of the UDP connection (UDP server) - */ - struct GNUNET_PeerIdentity receiver; - - /** - * Ephemeral key used by the @e sender. - */ - struct GNUNET_CRYPTO_EcdhePublicKey ephemeral; - - /** - * Monotonic time of @e sender, to possibly help detect replay attacks - * (if receiver persists times by sender). - */ - struct GNUNET_TIME_AbsoluteNBO monotonic_time; -}; - - -/** - * "Plaintext" header at beginning of KX message. Followed - * by encrypted `struct UDPConfirmation`. - */ -struct InitialKX -{ - /** - * Ephemeral key for KX. - */ - struct GNUNET_CRYPTO_EcdhePublicKey ephemeral; - - /** - * HMAC for the following encrypted message, using GCM. HMAC uses - * key derived from the handshake with sequence number zero. - */ - uint8_t gcm_tag[GCM_TAG_SIZE]; - -}; - - -/** - * Encrypted continuation of UDP initial handshake, followed - * by message header with payload. - */ -struct UDPConfirmation -{ - /** - * Sender's identity - */ - struct GNUNET_PeerIdentity sender; - - /** - * Sender's signature of type #GNUNET_SIGNATURE_PURPOSE_COMMUNICATOR_UDP_HANDSHAKE - */ - struct GNUNET_CRYPTO_EddsaSignature sender_sig; - - /** - * Monotonic time of @e sender, to possibly help detect replay attacks - * (if receiver persists times by sender). - */ - struct GNUNET_TIME_AbsoluteNBO monotonic_time; - - /* followed by messages */ - - /* padding may follow actual messages */ -}; - - -/** - * UDP key acknowledgement. May be sent via backchannel. Allows the - * sender to use `struct UDPBox` with the acknowledge key henceforth. - */ -struct UDPAck -{ - /** - * Type is #GNUNET_MESSAGE_TYPE_COMMUNICATOR_UDP_ACK. - */ - struct GNUNET_MessageHeader header; - - /** - * Sequence acknowledgement limit. Specifies current maximum sequence - * number supported by receiver. - */ - uint32_t sequence_ack GNUNET_PACKED; - - /** - * CMAC of the base key being acknowledged. - */ - struct GNUNET_HashCode cmac; -}; - - -/** - * Signature we use to verify that the broadcast was really made by - * the peer that claims to have made it. Basically, affirms that the - * peer is really using this IP address (albeit possibly not in _our_ - * LAN). Makes it difficult for peers in the LAN to claim to - * be just any global peer -- an attacker must have at least - * shared a LAN with the peer they're pretending to be here. - */ -struct UdpBroadcastSignature -{ - /** - * Purpose must be #GNUNET_SIGNATURE_PURPOSE_COMMUNICATOR_UDP_BROADCAST - */ - struct GNUNET_CRYPTO_EccSignaturePurpose purpose; - - /** - * Identity of the inititor of the UDP broadcast. - */ - struct GNUNET_PeerIdentity sender; - - /** - * Hash of the sender's UDP address. - */ - struct GNUNET_HashCode h_address; -}; - - -/** - * Broadcast by peer in LAN announcing its presence. Unusual in that - * we don't pad these to full MTU, as we cannot prevent being - * recognized in LAN as GNUnet peers if this feature is enabled - * anyway. Also, the entire message is in cleartext. - */ -struct UDPBroadcast -{ - /** - * Sender's peer identity. - */ - struct GNUNET_PeerIdentity sender; - - /** - * Sender's signature of type - * #GNUNET_SIGNATURE_PURPOSE_COMMUNICATOR_UDP_BROADCAST - */ - struct GNUNET_CRYPTO_EddsaSignature sender_sig; -}; - - -/** - * UDP message box. Always sent encrypted, only allowed after - * the receiver sent a `struct UDPAck` for the base key! - */ -struct UDPBox -{ - /** - * Key and IV identification code. KDF applied to an acknowledged - * base key and a sequence number. Sequence numbers must be used - * monotonically increasing up to the maximum specified in - * `struct UDPAck`. Without further `struct UDPAck`s, the sender - * must fall back to sending handshakes! - */ - struct GNUNET_ShortHashCode kid; - - /** - * 128-bit authentication tag for the following encrypted message, - * from GCM. MAC starts at the @e body_start that follows and - * extends until the end of the UDP payload. If the @e hmac is - * wrong, the receiver should check if the message might be a - * `struct UdpHandshakeSignature`. - */ - uint8_t gcm_tag[GCM_TAG_SIZE]; - -}; - -/** - * Plaintext of a rekey payload in a UDPBox. - */ -struct UDPRekey -{ - /** - * Type is #GNUNET_MESSAGE_TYPE_COMMUNICATOR_UDP_REKEY. - */ - struct GNUNET_MessageHeader header; - - /** - * Ephemeral key to rekey with. - */ - struct GNUNET_CRYPTO_EcdhePublicKey ephemeral; -}; - -GNUNET_NETWORK_STRUCT_END - -/** - * Shared secret we generated for a particular sender or receiver. - */ -struct SharedSecret; - - -/** - * Pre-generated "kid" code (key and IV identification code) to - * quickly derive master key for a `struct UDPBox`. - */ -struct KeyCacheEntry -{ - /** - * Kept in a DLL. - */ - struct KeyCacheEntry *next; - - /** - * Kept in a DLL. - */ - struct KeyCacheEntry *prev; - - /** - * Key and IV identification code. KDF applied to an acknowledged - * base key and a sequence number. Sequence numbers must be used - * monotonically increasing up to the maximum specified in - * `struct UDPAck`. Without further `struct UDPAck`s, the sender - * must fall back to sending handshakes! - */ - struct GNUNET_ShortHashCode kid; - - /** - * Corresponding shared secret. - */ - struct SharedSecret *ss; - - /** - * Sequence number used to derive this entry from master key. - */ - uint32_t sequence_number; -}; - - -/** - * Information we track per sender address we have recently been - * in contact with (decryption from sender). - */ -struct SenderAddress; - -/** - * Information we track per receiving address we have recently been - * in contact with (encryption to receiver). - */ -struct ReceiverAddress; - -/** - * Shared secret we generated for a particular sender or receiver. - */ -struct SharedSecret -{ - /** - * Kept in a DLL. - */ - struct SharedSecret *next; - - /** - * Kept in a DLL. - */ - struct SharedSecret *prev; - - /** - * Kept in a DLL, sorted by sequence number. Only if we are decrypting. - */ - struct KeyCacheEntry *kce_head; - - /** - * Kept in a DLL, sorted by sequence number. Only if we are decrypting. - */ - struct KeyCacheEntry *kce_tail; - - /** - * Sender we use this shared secret with, or NULL. - */ - struct SenderAddress *sender; - - /** - * Receiver we use this shared secret with, or NULL. - */ - struct ReceiverAddress *receiver; - - /** - * Master shared secret. - */ - struct GNUNET_HashCode master; - - /** - * CMAC is used to identify @e master in ACKs. - */ - struct GNUNET_HashCode cmac; - - /** - * Up to which sequence number did we use this @e master already? - * (for encrypting only) - */ - uint32_t sequence_used; - - /** - * Up to which sequence number did the other peer allow us to use - * this key, or up to which number did we allow the other peer to - * use this key? - */ - uint32_t sequence_allowed; - - /** - * Number of active KCN entries. - */ - unsigned int active_kce_count; - - /** - * Bytes sent with this shared secret - */ - size_t bytes_sent; - - /** - * rekey initiated for this secret? - */ - int rekey_initiated; - - /** - * Also precompute keys despite sufficient acks (for rekey) - */ - int override_available_acks; -}; - - -/** - * Information we track per sender address we have recently been - * in contact with (we decrypt messages from the sender). - */ -struct SenderAddress -{ - /** - * To whom are we talking to. - */ - struct GNUNET_PeerIdentity target; - - /** - * Entry in sender expiration heap. - */ - struct GNUNET_CONTAINER_HeapNode *hn; - - /** - * Shared secrets we used with @e target, first used is head. - */ - struct SharedSecret *ss_head; - - /** - * Shared secrets we used with @e target, last used is tail. - */ - struct SharedSecret *ss_tail; - - /** - * Address of the other peer. - */ - struct sockaddr *address; - - /** - * Length of the address. - */ - socklen_t address_len; - - /** - * Timeout for this sender. - */ - struct GNUNET_TIME_Absolute timeout; - - /** - * Length of the DLL at @a ss_head. - */ - unsigned int num_secrets; - - /** - * Number of BOX keys from ACKs we have currently - * available for this sender. - */ - unsigned int acks_available; - - /** - * Which network type does this queue use? - */ - enum GNUNET_NetworkType nt; - - /** - * sender_destroy already called on sender. - */ - int sender_destroy_called; - - /** - * ID of kce working queue task - */ - struct GNUNET_SCHEDULER_Task *kce_task; - - /** - * Is the kce_task finished? - */ - int kce_task_finished; - - /** - * When KCE finishes, send ACK if GNUNET_YES - */ - int kce_send_ack_on_finish; -}; - - -/** - * Information we track per receiving address we have recently been - * in contact with (encryption to receiver). - */ -struct ReceiverAddress -{ - /** - * Timeout for this receiver address. - */ - struct GNUNET_TIME_Absolute rekey_timeout; - - /** - * To whom are we talking to. - */ - struct GNUNET_PeerIdentity target; - - /** - * Shared secrets we received from @e target, first used is head. - */ - struct SharedSecret *ss_head; - - /** - * Shared secrets we received with @e target, last used is tail. - */ - struct SharedSecret *ss_tail; - - /** - * Address of the receiver in the human-readable format - * with the #COMMUNICATOR_ADDRESS_PREFIX. - */ - char *foreign_addr; - - /** - * Address of the other peer. - */ - struct sockaddr *address; - - /** - * Length of the address. - */ - socklen_t address_len; - - /** - * Entry in sender expiration heap. - */ - struct GNUNET_CONTAINER_HeapNode *hn; - - /** - * KX message queue we are providing for the #ch. - */ - struct GNUNET_MQ_Handle *kx_mq; - - /** - * Default message queue we are providing for the #ch. - */ - struct GNUNET_MQ_Handle *d_mq; - - /** - * handle for KX queue with the #ch. - */ - struct GNUNET_TRANSPORT_QueueHandle *kx_qh; - - /** - * handle for default queue with the #ch. - */ - struct GNUNET_TRANSPORT_QueueHandle *d_qh; - - /** - * Timeout for this receiver address. - */ - struct GNUNET_TIME_Absolute timeout; - - /** - * MTU we allowed transport for this receiver's KX queue. - */ - size_t kx_mtu; - - /** - * MTU we allowed transport for this receiver's default queue. - */ - size_t d_mtu; - - /** - * Length of the DLL at @a ss_head. - */ - unsigned int num_secrets; - - /** - * Number of BOX keys from ACKs we have currently - * available for this receiver. - */ - unsigned int acks_available; - - /** - * Which network type does this queue use? - */ - enum GNUNET_NetworkType nt; - - /** - * receiver_destroy already called on receiver. - */ - int receiver_destroy_called; -}; - -/** - * Interface we broadcast our presence on. - */ -struct BroadcastInterface -{ - /** - * Kept in a DLL. - */ - struct BroadcastInterface *next; - - /** - * Kept in a DLL. - */ - struct BroadcastInterface *prev; - - /** - * Task for this broadcast interface. - */ - struct GNUNET_SCHEDULER_Task *broadcast_task; - - /** - * Sender's address of the interface. - */ - struct sockaddr *sa; - - /** - * Broadcast address to use on the interface. - */ - struct sockaddr *ba; - - /** - * Message we broadcast on this interface. - */ - struct UDPBroadcast bcm; - - /** - * If this is an IPv6 interface, this is the request - * we use to join/leave the group. - */ - struct ipv6_mreq mcreq; - - /** - * Number of bytes in @e sa. - */ - socklen_t salen; - - /** - * Was this interface found in the last #iface_proc() scan? - */ - int found; -}; - -/** - * The rekey interval - */ -static struct GNUNET_TIME_Relative rekey_interval; - -/** - * How often we do rekey based on number of bytes transmitted - */ -static unsigned long long rekey_max_bytes; - -/** - * Cache of pre-generated key IDs. - */ -static struct GNUNET_CONTAINER_MultiShortmap *key_cache; - -/** - * ID of read task - */ -static struct GNUNET_SCHEDULER_Task *read_task; - -/** - * ID of timeout task - */ -static struct GNUNET_SCHEDULER_Task *timeout_task; - -/** - * ID of master broadcast task - */ -static struct GNUNET_SCHEDULER_Task *broadcast_task; - -/** - * For logging statistics. - */ -static struct GNUNET_STATISTICS_Handle *stats; - -/** - * Our environment. - */ -static struct GNUNET_TRANSPORT_CommunicatorHandle *ch; - -/** - * Receivers (map from peer identity to `struct ReceiverAddress`) - */ -static struct GNUNET_CONTAINER_MultiPeerMap *receivers; - -/** - * Senders (map from peer identity to `struct SenderAddress`) - */ -static struct GNUNET_CONTAINER_MultiPeerMap *senders; - -/** - * Expiration heap for senders (contains `struct SenderAddress`) - */ -static struct GNUNET_CONTAINER_Heap *senders_heap; - -/** - * Expiration heap for receivers (contains `struct ReceiverAddress`) - */ -static struct GNUNET_CONTAINER_Heap *receivers_heap; - -/** - * Broadcast interface tasks. Kept in a DLL. - */ -static struct BroadcastInterface *bi_head; - -/** - * Broadcast interface tasks. Kept in a DLL. - */ -static struct BroadcastInterface *bi_tail; - -/** - * Our socket. - */ -static struct GNUNET_NETWORK_Handle *udp_sock; - -/** - * #GNUNET_YES if #udp_sock supports IPv6. - */ -static int have_v6_socket; - -/** - * Our public key. - */ -static struct GNUNET_PeerIdentity my_identity; - -/** - * Our private key. - */ -static struct GNUNET_CRYPTO_EddsaPrivateKey *my_private_key; - -/** - * Our configuration. - */ -static const struct GNUNET_CONFIGURATION_Handle *cfg; - -/** - * Our handle to report addresses for validation to TRANSPORT. - */ -static struct GNUNET_TRANSPORT_ApplicationHandle *ah; - -/** - * Network scanner to determine network types. - */ -static struct GNUNET_NT_InterfaceScanner *is; - -/** - * Connection to NAT service. - */ -static struct GNUNET_NAT_Handle *nat; - -/** - * Port number to which we are actually bound. - */ -static uint16_t my_port; - - -/** - * An interface went away, stop broadcasting on it. - * - * @param bi entity to close down - */ -static void -bi_destroy (struct BroadcastInterface *bi) -{ - if (AF_INET6 == bi->sa->sa_family) - { - /* Leave the multicast group */ - if (GNUNET_OK != GNUNET_NETWORK_socket_setsockopt (udp_sock, - IPPROTO_IPV6, - IPV6_LEAVE_GROUP, - &bi->mcreq, - sizeof(bi->mcreq))) - { - GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING, "setsockopt"); - } - } - GNUNET_CONTAINER_DLL_remove (bi_head, bi_tail, bi); - GNUNET_SCHEDULER_cancel (bi->broadcast_task); - GNUNET_free (bi->sa); - GNUNET_free (bi->ba); - GNUNET_free (bi); -} - - -/** - * Destroys a receiving state due to timeout or shutdown. - * - * @param receiver entity to close down - */ -static void -receiver_destroy (struct ReceiverAddress *receiver) -{ - - receiver->receiver_destroy_called = GNUNET_YES; - - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "Disconnecting receiver for peer `%s'\n", - GNUNET_i2s (&receiver->target)); - if (NULL != receiver->kx_qh) - { - GNUNET_TRANSPORT_communicator_mq_del (receiver->kx_qh); - receiver->kx_qh = NULL; - receiver->kx_mq = NULL; - } - if (NULL != receiver->d_qh) - { - GNUNET_TRANSPORT_communicator_mq_del (receiver->d_qh); - receiver->d_qh = NULL; - } - GNUNET_assert (GNUNET_YES == - GNUNET_CONTAINER_multipeermap_remove (receivers, - &receiver->target, - receiver)); - GNUNET_assert (receiver == GNUNET_CONTAINER_heap_remove_node (receiver->hn)); - GNUNET_STATISTICS_set (stats, - "# receivers active", - GNUNET_CONTAINER_multipeermap_size (receivers), - GNUNET_NO); - GNUNET_free (receiver->address); - GNUNET_free (receiver->foreign_addr); - GNUNET_free (receiver); -} - - -/** - * Free memory used by key cache entry. - * - * @param kce the key cache entry - */ -static void -kce_destroy (struct KeyCacheEntry *kce) -{ - struct SharedSecret *ss = kce->ss; - - ss->active_kce_count--; - ss->sender->acks_available--; - GNUNET_CONTAINER_DLL_remove (ss->kce_head, ss->kce_tail, kce); - GNUNET_assert (GNUNET_YES == GNUNET_CONTAINER_multishortmap_remove (key_cache, - &kce->kid, - kce)); - GNUNET_free (kce); -} - - -/** - * Compute @a kid. - * - * @param msec master secret for HMAC calculation - * @param serial number for the @a smac calculation - * @param[out] kid where to write the key ID - */ -static void -get_kid (const struct GNUNET_HashCode *msec, - uint32_t serial, - struct GNUNET_ShortHashCode *kid) -{ - uint32_t sid = htonl (serial); - - GNUNET_CRYPTO_hkdf (kid, - sizeof(*kid), - GCRY_MD_SHA512, - GCRY_MD_SHA256, - &sid, - sizeof(sid), - msec, - sizeof(*msec), - "UDP-KID", - strlen ("UDP-KID"), - NULL, - 0); -} - - -/** - * Setup key cache entry for sequence number @a seq and shared secret @a ss. - * - * @param ss shared secret - * @param seq sequence number for the key cache entry - */ -static void -kce_generate (struct SharedSecret *ss, uint32_t seq) -{ - struct KeyCacheEntry *kce; - - GNUNET_assert (0 < seq); - kce = GNUNET_new (struct KeyCacheEntry); - kce->ss = ss; - kce->sequence_number = seq; - get_kid (&ss->master, seq, &kce->kid); - GNUNET_CONTAINER_DLL_insert (ss->kce_head, ss->kce_tail, kce); - ss->active_kce_count++; - ss->sender->acks_available++; - (void) GNUNET_CONTAINER_multishortmap_put ( - key_cache, - &kce->kid, - kce, - GNUNET_CONTAINER_MULTIHASHMAPOPTION_MULTIPLE); - GNUNET_STATISTICS_set (stats, - "# KIDs active", - GNUNET_CONTAINER_multishortmap_size (key_cache), - GNUNET_NO); -} - - -/** - * Destroy @a ss and associated key cache entries. - * - * @param ss shared secret to destroy - * @param withoutKce If GNUNET_YES shared secrets with kce will not be destroyed. - */ -static int -secret_destroy (struct SharedSecret *ss) -{ - struct SenderAddress *sender; - struct ReceiverAddress *receiver; - struct KeyCacheEntry *kce; - - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "secret %s destroy %u\n", - GNUNET_h2s (&ss->master), - ss->sequence_allowed); - if (NULL != (sender = ss->sender)) - { - GNUNET_CONTAINER_DLL_remove (sender->ss_head, sender->ss_tail, ss); - sender->num_secrets--; - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "%u sender->num_secrets\n", - receiver->num_secrets); - if (NULL != ss->sender->kce_task) - { - GNUNET_SCHEDULER_cancel (ss->sender->kce_task); - ss->sender->kce_task = NULL; - } - } - if (NULL != (receiver = ss->receiver)) - { - GNUNET_CONTAINER_DLL_remove (receiver->ss_head, receiver->ss_tail, ss); - receiver->num_secrets--; - receiver->acks_available -= (ss->sequence_allowed - ss->sequence_used); - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "%u receiver->num_secrets\n", - receiver->num_secrets); - } - while (NULL != (kce = ss->kce_head)) - kce_destroy (kce); - GNUNET_STATISTICS_update (stats, "# Secrets active", -1, GNUNET_NO); - GNUNET_STATISTICS_set (stats, - "# KIDs active", - GNUNET_CONTAINER_multishortmap_size (key_cache), - GNUNET_NO); - GNUNET_free (ss); - return GNUNET_YES; -} - - -/** - * Functions with this signature are called whenever we need - * to close a sender's state due to timeout. - * - * @param sender entity to close down - */ -static void -sender_destroy (struct SenderAddress *sender) -{ - sender->sender_destroy_called = GNUNET_YES; - GNUNET_assert ( - GNUNET_YES == - GNUNET_CONTAINER_multipeermap_remove (senders, &sender->target, sender)); - GNUNET_assert (sender == GNUNET_CONTAINER_heap_remove_node (sender->hn)); - GNUNET_STATISTICS_set (stats, - "# senders active", - GNUNET_CONTAINER_multipeermap_size (senders), - GNUNET_NO); - GNUNET_free (sender->address); - GNUNET_free (sender); -} - - -/** - * Compute @a key and @a iv. - * - * @param msec master secret for calculation - * @param serial number for the @a smac calculation - * @param[out] key where to write the decryption key - * @param[out] iv where to write the IV - */ -static void -get_iv_key (const struct GNUNET_HashCode *msec, - uint32_t serial, - char key[AES_KEY_SIZE], - char iv[AES_IV_SIZE]) -{ - uint32_t sid = htonl (serial); - char res[AES_KEY_SIZE + AES_IV_SIZE]; - - GNUNET_CRYPTO_hkdf (res, - sizeof(res), - GCRY_MD_SHA512, - GCRY_MD_SHA256, - &sid, - sizeof(sid), - msec, - sizeof(*msec), - "UDP-IV-KEY", - strlen ("UDP-IV-KEY"), - NULL, - 0); - memcpy (key, res, AES_KEY_SIZE); - memcpy (iv, &res[AES_KEY_SIZE], AES_IV_SIZE); -} - - -/** - * Increment sender timeout due to activity. - * - * @param sender address for which the timeout should be rescheduled - */ -static void -reschedule_sender_timeout (struct SenderAddress *sender) -{ - sender->timeout = - GNUNET_TIME_relative_to_absolute (GNUNET_CONSTANTS_IDLE_CONNECTION_TIMEOUT); - GNUNET_CONTAINER_heap_update_cost (sender->hn, sender->timeout.abs_value_us); -} - - -/** - * Increment receiver timeout due to activity. - * - * @param receiver address for which the timeout should be rescheduled - */ -static void -reschedule_receiver_timeout (struct ReceiverAddress *receiver) -{ - receiver->timeout = - GNUNET_TIME_relative_to_absolute (GNUNET_CONSTANTS_IDLE_CONNECTION_TIMEOUT); - GNUNET_CONTAINER_heap_update_cost (receiver->hn, - receiver->timeout.abs_value_us); -} - - -/** - * Task run to check #receiver_heap and #sender_heap for timeouts. - * - * @param cls unused, NULL - */ -static void -check_timeouts (void *cls) -{ - struct GNUNET_TIME_Relative st; - struct GNUNET_TIME_Relative rt; - struct GNUNET_TIME_Relative delay; - struct ReceiverAddress *receiver; - struct SenderAddress *sender; - - (void) cls; - timeout_task = NULL; - rt = GNUNET_TIME_UNIT_FOREVER_REL; - while (NULL != (receiver = GNUNET_CONTAINER_heap_peek (receivers_heap))) - { - rt = GNUNET_TIME_absolute_get_remaining (receiver->timeout); - if (0 != rt.rel_value_us) - break; - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "Receiver timed out\n"); - receiver_destroy (receiver); - } - st = GNUNET_TIME_UNIT_FOREVER_REL; - while (NULL != (sender = GNUNET_CONTAINER_heap_peek (senders_heap))) - { - if (GNUNET_YES != sender->sender_destroy_called) - { - st = GNUNET_TIME_absolute_get_remaining (sender->timeout); - if (0 != st.rel_value_us) - break; - sender_destroy (sender); - } - } - delay = GNUNET_TIME_relative_min (rt, st); - if (delay.rel_value_us < GNUNET_TIME_UNIT_FOREVER_REL.rel_value_us) - timeout_task = GNUNET_SCHEDULER_add_delayed (delay, &check_timeouts, NULL); -} - - -/** - * Calculate cmac from master in @a ss. - * - * @param[in,out] ss data structure to complete - */ -static void -calculate_cmac (struct SharedSecret *ss) -{ - GNUNET_CRYPTO_hkdf (&ss->cmac, - sizeof(ss->cmac), - GCRY_MD_SHA512, - GCRY_MD_SHA256, - "CMAC", - strlen ("CMAC"), - &ss->master, - sizeof(ss->master), - "UDP-CMAC", - strlen ("UDP-CMAC"), - NULL, - 0); -} - - -/** - * We received @a plaintext_len bytes of @a plaintext from a @a sender. - * Pass it on to CORE. - * - * @param queue the queue that received the plaintext - * @param plaintext the plaintext that was received - * @param plaintext_len number of bytes of plaintext received - */ -static void -pass_plaintext_to_core (struct SenderAddress *sender, - const void *plaintext, - size_t plaintext_len) -{ - const struct GNUNET_MessageHeader *hdr = plaintext; - const char *pos = plaintext; - - while (ntohs (hdr->size) <= plaintext_len) - { - GNUNET_STATISTICS_update (stats, - "# bytes given to core", - ntohs (hdr->size), - GNUNET_NO); - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "Giving %u bytes to TNG\n", ntohs (hdr->size)); - GNUNET_assert (GNUNET_SYSERR != - GNUNET_TRANSPORT_communicator_receive (ch, - &sender->target, - hdr, - ADDRESS_VALIDITY_PERIOD, - NULL /* no flow control possible */ - , - NULL)); - /* move on to next message, if any */ - plaintext_len -= ntohs (hdr->size); - if (plaintext_len < sizeof(*hdr)) - break; - pos += ntohs (hdr->size); - hdr = (const struct GNUNET_MessageHeader *) pos; - // TODO for now..., we do not actually sen >1msg or have a way of telling - // if we are done - break; - } - GNUNET_STATISTICS_update (stats, - "# bytes padding discarded", - plaintext_len, - GNUNET_NO); -} - - -/** - * Setup @a cipher based on shared secret @a msec and - * serial number @a serial. - * - * @param msec master shared secret - * @param serial serial number of cipher to set up - * @param cipher[out] cipher to initialize - */ -static void -setup_cipher (const struct GNUNET_HashCode *msec, - uint32_t serial, - gcry_cipher_hd_t *cipher) -{ - char key[AES_KEY_SIZE]; - char iv[AES_IV_SIZE]; - int rc; - - GNUNET_assert (0 == - gcry_cipher_open (cipher, - GCRY_CIPHER_AES256 /* low level: go for speed */, - GCRY_CIPHER_MODE_GCM, - 0 /* flags */)); - get_iv_key (msec, serial, key, iv); - rc = gcry_cipher_setkey (*cipher, key, sizeof(key)); - GNUNET_assert ((0 == rc) || ((char) rc == GPG_ERR_WEAK_KEY)); - rc = gcry_cipher_setiv (*cipher, iv, sizeof(iv)); - GNUNET_assert ((0 == rc) || ((char) rc == GPG_ERR_WEAK_KEY)); -} - - -/** - * Try to decrypt @a buf using shared secret @a ss and key/iv - * derived using @a serial. - * - * @param ss shared secret - * @param tag GCM authentication tag - * @param serial serial number to use - * @param in_buf input buffer to decrypt - * @param in_buf_size number of bytes in @a in_buf and available in @a out_buf - * @param out_buf where to write the result - * @return #GNUNET_OK on success - */ -static int -try_decrypt (const struct SharedSecret *ss, - const uint8_t *tag, - uint32_t serial, - const char *in_buf, - size_t in_buf_size, - char *out_buf) -{ - gcry_cipher_hd_t cipher; - - setup_cipher (&ss->master, serial, &cipher); - GNUNET_assert ( - 0 == - gcry_cipher_decrypt (cipher, out_buf, in_buf_size, in_buf, in_buf_size)); - if (0 != gcry_cipher_checktag (cipher, tag, GCM_TAG_SIZE)) - { - gcry_cipher_close (cipher); - GNUNET_STATISTICS_update (stats, - "# AEAD authentication failures", - 1, - GNUNET_NO); - return GNUNET_SYSERR; - } - gcry_cipher_close (cipher); - return GNUNET_OK; -} - - -/** - * Setup shared secret for decryption. - * - * @param ephemeral ephemeral key we received from the other peer - * @return new shared secret - */ -static struct SharedSecret * -setup_shared_secret_dec (const struct GNUNET_CRYPTO_EcdhePublicKey *ephemeral) -{ - struct SharedSecret *ss; - - ss = GNUNET_new (struct SharedSecret); - GNUNET_CRYPTO_eddsa_kem_decaps (my_private_key, ephemeral, &ss->master); - calculate_cmac (ss); - return ss; -} - - -/** - * Setup new shared secret for encryption using KEM. - * - * @param[out] ephemeral ephemeral key to be sent to other peer (encapsulated key from KEM) - * @param[in,out] receiver queue to initialize encryption key for - * @return new shared secret - */ -static struct SharedSecret * -setup_shared_secret_ephemeral (struct GNUNET_CRYPTO_EcdhePublicKey *ephemeral, - struct ReceiverAddress *receiver) -{ - struct SharedSecret *ss; - struct GNUNET_HashCode k; - - GNUNET_CRYPTO_eddsa_kem_encaps (&receiver->target.public_key, ephemeral, &k); - ss = GNUNET_new (struct SharedSecret); - memcpy (&ss->master, &k, sizeof (k)); - calculate_cmac (ss); - ss->receiver = receiver; - GNUNET_CONTAINER_DLL_insert (receiver->ss_head, receiver->ss_tail, ss); - receiver->num_secrets++; - GNUNET_STATISTICS_update (stats, "# Secrets active", 1, GNUNET_NO); - return ss; -} - - -/** - * Setup the MQ for the @a receiver. If a queue exists, - * the existing one is destroyed. Then the MTU is - * recalculated and a fresh queue is initialized. - * - * @param receiver receiver to setup MQ for - */ -static void -setup_receiver_mq (struct ReceiverAddress *receiver); - - -/** - * Best effort try to purge some secrets. - * Ideally those, not ACKed. - * - * @param ss_list_tail the oldest secret in the list of interest. - * @return number of deleted secrets. - */ -unsigned int -purge_secrets (struct SharedSecret *ss_list_tail) -{ - struct SharedSecret *pos; - struct SharedSecret *ss_to_purge; - unsigned int deleted = 0; - - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "Purging secrets.\n"); - pos = ss_list_tail; - while (NULL != pos) - { - ss_to_purge = pos; - pos = pos->prev; - - // FIXME we may also want to purge old unacked. - if (rekey_max_bytes <= ss_to_purge->bytes_sent) - { - secret_destroy (ss_to_purge); - deleted++; - } - } - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "Finished purging all, deleted %u.\n", deleted); - return deleted; -} - - -static void -add_acks (struct SharedSecret *ss, int acks_to_add) -{ - - struct ReceiverAddress *receiver = ss->receiver; - - GNUNET_assert (NULL != ss); - GNUNET_assert (NULL != receiver); - - if (NULL == receiver->d_qh) - { - receiver->d_qh = - GNUNET_TRANSPORT_communicator_mq_add (ch, - &receiver->target, - receiver->foreign_addr, - receiver->d_mtu, - acks_to_add, - 1, /* Priority */ - receiver->nt, - GNUNET_TRANSPORT_CS_OUTBOUND, - receiver->d_mq); - } - else - { - GNUNET_TRANSPORT_communicator_mq_update (ch, - receiver->d_qh, - acks_to_add, - 1); - } - - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "Tell transport we have %u more acks!\n", - acks_to_add); - - // Until here for alternativ 1 - - /* move ss to head to avoid discarding it anytime soon! */ - - GNUNET_CONTAINER_DLL_remove (receiver->ss_head, receiver->ss_tail, ss); - GNUNET_CONTAINER_DLL_insert (receiver->ss_head, receiver->ss_tail, ss); -} - - -/** - * We received an ACK for @a pid. Check if it is for - * the receiver in @a value and if so, handle it and - * return #GNUNET_NO. Otherwise, return #GNUNET_YES. - * - * @param cls a `const struct UDPAck` - * @param pid peer the ACK is from - * @param value a `struct ReceiverAddress` - * @return #GNUNET_YES to continue to iterate - */ -static int -handle_ack (void *cls, const struct GNUNET_PeerIdentity *pid, void *value) -{ - const struct UDPAck *ack = cls; - struct ReceiverAddress *receiver = value; - uint32_t acks_to_add; - uint32_t allowed; - - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "in handle ack with cmac %s\n", - GNUNET_h2s (&ack->cmac)); - - (void) pid; - for (struct SharedSecret *ss = receiver->ss_head; NULL != ss; ss = ss->next) - { - if (0 == memcmp (&ack->cmac, &ss->cmac, sizeof(struct GNUNET_HashCode))) - { - - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "Found matching cmac\n"); - - allowed = ntohl (ack->sequence_ack); - - if (allowed <= ss->sequence_allowed) - { - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "Ignoring ack, not giving us increased window\n."); - return GNUNET_NO; - } - acks_to_add = (allowed - ss->sequence_allowed); - GNUNET_assert (0 != acks_to_add); - receiver->acks_available += (allowed - ss->sequence_allowed); - ss->sequence_allowed = allowed; - add_acks (ss, acks_to_add); - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "New sequence allows until %u (+%u). Acks available to us: %u. For secret %s\n", - allowed, - acks_to_add, - receiver->acks_available, - GNUNET_h2s (&ss->master)); - return GNUNET_NO; - } - } - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "Matching cmac not found for ack!\n"); - return GNUNET_YES; -} - - -/** - * We established a shared secret with a sender. We should try to send - * the sender an `struct UDPAck` at the next opportunity to allow the - * sender to use @a ss longer (assuming we did not yet already - * recently). - * - * @param ss shared secret to generate ACKs for - */ -static void -consider_ss_ack (struct SharedSecret *ss) -{ - struct UDPAck ack; - GNUNET_assert (NULL != ss->sender); - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "Considering SS UDPAck %s\n", - GNUNET_i2s_full (&ss->sender->target)); - - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "Sender has %u acks available.\n", - ss->sender->acks_available); - /* drop ancient KeyCacheEntries */ - while ((NULL != ss->kce_head) && - (MAX_SQN_DELTA < - ss->kce_head->sequence_number - ss->kce_tail->sequence_number)) - kce_destroy (ss->kce_tail); - - - ack.header.type = htons (GNUNET_MESSAGE_TYPE_COMMUNICATOR_UDP_ACK); - ack.header.size = htons (sizeof(ack)); - ack.sequence_ack = htonl (ss->sequence_allowed); - ack.cmac = ss->cmac; - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "Notifying transport with UDPAck %s, sequence %u and master %s\n", - GNUNET_i2s_full (&ss->sender->target), - ss->sequence_allowed, - GNUNET_h2s (&(ss->master))); - GNUNET_TRANSPORT_communicator_notify (ch, - &ss->sender->target, - COMMUNICATOR_ADDRESS_PREFIX, - &ack.header); -} - - -static void -kce_generate_cb (void *cls) -{ - struct SharedSecret *ss = cls; - ss->sender->kce_task = NULL; - - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "Precomputing %u keys for master %s\n", - GENERATE_AT_ONCE, - GNUNET_h2s (&(ss->master))); - if ((ss->override_available_acks != GNUNET_YES) && - (KCN_TARGET < ss->sender->acks_available)) - return; - for (int i = 0; i < GENERATE_AT_ONCE; i++) - kce_generate (ss, ++ss->sequence_allowed); - - /** - * As long as we loose over 30% of max acks in reschedule, - * We keep generating acks for this ss. - */ - if (KCN_TARGET > ss->sender->acks_available) - { - ss->sender->kce_task = GNUNET_SCHEDULER_add_delayed ( - WORKING_QUEUE_INTERVALL, - kce_generate_cb, - ss); - return; - } - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "We have enough keys (ACKs: %u).\n", ss->sender->acks_available); - ss->sender->kce_task_finished = GNUNET_YES; - ss->override_available_acks = GNUNET_NO; - if (ss->sender->kce_send_ack_on_finish == GNUNET_YES) - consider_ss_ack (ss); -} - - -/** - * Test if we have received a valid message in plaintext. - * If so, handle it. - * - * @param sender peer to process inbound plaintext for - * @param buf buffer we received - * @param buf_size number of bytes in @a buf - */ -static void -try_handle_plaintext (struct SenderAddress *sender, - const void *buf, - size_t buf_size) -{ - const struct GNUNET_MessageHeader *hdr; - const struct UDPAck *ack; - const struct UDPRekey *rekey; - struct SharedSecret *ss_rekey; - const char *buf_pos = buf; - size_t bytes_remaining = buf_size; - uint16_t type; - - hdr = (struct GNUNET_MessageHeader*) buf_pos; - if (sizeof(*hdr) > bytes_remaining) - { - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Plaintext too short, dropping...\n"); - return; /* no data left */ - } - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "try_handle_plaintext of size %lu (%u %lu) and type %u\n", - bytes_remaining, - ntohs (hdr->size), - sizeof(*hdr), - ntohs (hdr->type)); - if (ntohs (hdr->size) > bytes_remaining) - return; /* buffer too short for indicated message length */ - type = ntohs (hdr->type); - switch (type) - { - case GNUNET_MESSAGE_TYPE_COMMUNICATOR_UDP_REKEY: - rekey = (struct UDPRekey*) buf_pos; - ss_rekey = setup_shared_secret_dec (&rekey->ephemeral); - ss_rekey->sender = sender; - GNUNET_CONTAINER_DLL_insert (sender->ss_head, sender->ss_tail, ss_rekey); - sender->num_secrets++; - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - "Received rekey secret with cmac %s\n", - GNUNET_h2s (&(ss_rekey->cmac))); - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "Received secret with master %s.\n", - GNUNET_h2s (&(ss_rekey->master))); - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "We have %u sequence_allowed.\n", - ss_rekey->sequence_allowed); - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "We have a sender %p\n", - ss_rekey->sender); - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "We have %u acks available.\n", - ss_rekey->sender->acks_available); - ss_rekey->sender->kce_send_ack_on_finish = GNUNET_YES; - ss_rekey->override_available_acks = GNUNET_YES; - // FIXME - ss_rekey->sender->kce_task = GNUNET_SCHEDULER_add_delayed ( - WORKING_QUEUE_INTERVALL, - kce_generate_cb, - ss_rekey); - // FIXME: Theoretically, this could be an Ack - buf_pos += ntohs (hdr->size); - bytes_remaining -= ntohs (hdr->size); - pass_plaintext_to_core (sender, buf_pos, bytes_remaining); - if (sender->num_secrets > MAX_SECRETS) - { - if (0 == purge_secrets (sender->ss_tail)) - { - // No secret purged. Delete oldest. - secret_destroy (sender->ss_tail); - } - } - break; - case GNUNET_MESSAGE_TYPE_COMMUNICATOR_UDP_ACK: - /* lookup master secret by 'cmac', then update sequence_max */ - ack = (struct UDPAck*) buf_pos; - GNUNET_CONTAINER_multipeermap_get_multiple (receivers, - &sender->target, - &handle_ack, - (void *) ack); - /* There could be more messages after the ACK, handle those as well */ - buf_pos += ntohs (hdr->size); - bytes_remaining -= ntohs (hdr->size); - pass_plaintext_to_core (sender, buf_pos, bytes_remaining); - break; - - case GNUNET_MESSAGE_TYPE_COMMUNICATOR_UDP_PAD: - /* skip padding */ - break; - - default: - pass_plaintext_to_core (sender, buf_pos, bytes_remaining); - } - return; -} - - -/** - * We received a @a box with matching @a kce. Decrypt and process it. - * - * @param box the data we received - * @param box_len number of bytes in @a box - * @param kce key index to decrypt @a box - */ -static void -decrypt_box (const struct UDPBox *box, - size_t box_len, - struct KeyCacheEntry *kce) -{ - struct SharedSecret *ss = kce->ss; - char out_buf[box_len - sizeof(*box)]; - - GNUNET_assert (NULL != ss->sender); - if (GNUNET_OK != try_decrypt (ss, - box->gcm_tag, - kce->sequence_number, - (const char *) &box[1], - sizeof(out_buf), - out_buf)) - { - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Failed decryption.\n"); - GNUNET_STATISTICS_update (stats, - "# Decryption failures with valid KCE", - 1, - GNUNET_NO); - kce_destroy (kce); - return; - } - kce_destroy (kce); - kce = NULL; - GNUNET_STATISTICS_update (stats, - "# bytes decrypted with BOX", - sizeof(out_buf), - GNUNET_NO); - GNUNET_STATISTICS_update (stats, - "# messages decrypted with BOX", - 1, - GNUNET_NO); - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "decrypted UDPBox with kid %s\n", - GNUNET_sh2s (&box->kid)); - try_handle_plaintext (ss->sender, out_buf, sizeof(out_buf)); - if ((KCN_THRESHOLD > ss->sender->acks_available) && - (NULL == ss->sender->kce_task) && - (GNUNET_YES == ss->sender->kce_task_finished)) - { - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "Sender has %u ack left which is under threshold.\n", - ss->sender->acks_available); - ss->sender->kce_send_ack_on_finish = GNUNET_YES; - ss->sender->kce_task = GNUNET_SCHEDULER_add_now ( - kce_generate_cb, - ss); - } -} - - -/** - * Closure for #find_sender_by_address() - */ -struct SearchContext -{ - /** - * Address we are looking for. - */ - const struct sockaddr *address; - - /** - * Number of bytes in @e address. - */ - socklen_t address_len; - - /** - * Return value to set if we found a match. - */ - struct SenderAddress *sender; -}; - - -/** - * Find existing `struct SenderAddress` by matching addresses. - * - * @param cls a `struct SearchContext` - * @param key ignored, must match already - * @param value a `struct SenderAddress` - * @return #GNUNET_YES if not found (continue to search), #GNUNET_NO if found - */ -static int -find_sender_by_address (void *cls, - const struct GNUNET_PeerIdentity *key, - void *value) -{ - struct SearchContext *sc = cls; - struct SenderAddress *sender = value; - - if ((sender->address_len == sc->address_len) && - (0 == memcmp (sender->address, sc->address, sender->address_len))) - { - sc->sender = sender; - return GNUNET_NO; /* stop iterating! */ - } - return GNUNET_YES; -} - - -/** - * Create sender address for @a target. Note that we - * might already have one, so a fresh one is only allocated - * if one does not yet exist for @a address. - * - * @param target peer to generate address for - * @param address target address - * @param address_len number of bytes in @a address - * @return data structure to keep track of key material for - * decrypting data from @a target - */ -static struct SenderAddress * -setup_sender (const struct GNUNET_PeerIdentity *target, - const struct sockaddr *address, - socklen_t address_len) -{ - struct SenderAddress *sender; - struct SearchContext sc = { .address = address, - .address_len = address_len, - .sender = NULL }; - - GNUNET_CONTAINER_multipeermap_get_multiple (senders, - target, - &find_sender_by_address, - &sc); - if (NULL != sc.sender) - { - reschedule_sender_timeout (sc.sender); - return sc.sender; - } - sender = GNUNET_new (struct SenderAddress); - sender->target = *target; - sender->address = GNUNET_memdup (address, address_len); - sender->address_len = address_len; - (void) GNUNET_CONTAINER_multipeermap_put ( - senders, - &sender->target, - sender, - GNUNET_CONTAINER_MULTIHASHMAPOPTION_MULTIPLE); - GNUNET_STATISTICS_set (stats, - "# senders active", - GNUNET_CONTAINER_multipeermap_size (receivers), - GNUNET_NO); - sender->timeout = - GNUNET_TIME_relative_to_absolute (GNUNET_CONSTANTS_IDLE_CONNECTION_TIMEOUT); - sender->hn = GNUNET_CONTAINER_heap_insert (senders_heap, - sender, - sender->timeout.abs_value_us); - sender->nt = GNUNET_NT_scanner_get_type (is, address, address_len); - if (NULL == timeout_task) - timeout_task = GNUNET_SCHEDULER_add_now (&check_timeouts, NULL); - return sender; -} - - -/** - * Check signature from @a uc against @a ephemeral. - * - * @param ephemeral key that is signed - * @param uc signature of claimant - * @return #GNUNET_OK if signature is valid - */ -static int -verify_confirmation (const struct GNUNET_CRYPTO_EcdhePublicKey *ephemeral, - const struct UDPConfirmation *uc) -{ - struct UdpHandshakeSignature uhs; - - uhs.purpose.purpose = htonl ( - GNUNET_SIGNATURE_PURPOSE_COMMUNICATOR_UDP_HANDSHAKE); - uhs.purpose.size = htonl (sizeof(uhs)); - uhs.sender = uc->sender; - uhs.receiver = my_identity; - uhs.ephemeral = *ephemeral; - uhs.monotonic_time = uc->monotonic_time; - return GNUNET_CRYPTO_eddsa_verify ( - GNUNET_SIGNATURE_PURPOSE_COMMUNICATOR_UDP_HANDSHAKE, - &uhs, - &uc->sender_sig, - &uc->sender.public_key); -} - - -/** - * Converts @a address to the address string format used by this - * communicator in HELLOs. - * - * @param address the address to convert, must be AF_INET or AF_INET6. - * @param address_len number of bytes in @a address - * @return string representation of @a address - */ -static char * -sockaddr_to_udpaddr_string (const struct sockaddr *address, - socklen_t address_len) -{ - char *ret; - - switch (address->sa_family) - { - case AF_INET: - GNUNET_asprintf (&ret, - "%s-%s", - COMMUNICATOR_ADDRESS_PREFIX, - GNUNET_a2s (address, address_len)); - break; - - case AF_INET6: - GNUNET_asprintf (&ret, - "%s-%s", - COMMUNICATOR_ADDRESS_PREFIX, - GNUNET_a2s (address, address_len)); - break; - - default: - GNUNET_assert (0); - } - return ret; -} - - -/** - * Socket read task. - * - * @param cls NULL - */ -static void -sock_read (void *cls) -{ - struct sockaddr_storage sa; - struct sockaddr_in *addr_verify; - socklen_t salen = sizeof(sa); - char buf[UINT16_MAX]; - ssize_t rcvd; - - (void) cls; - read_task = GNUNET_SCHEDULER_add_read_net (GNUNET_TIME_UNIT_FOREVER_REL, - udp_sock, - &sock_read, - NULL); - while (1) - { - rcvd = GNUNET_NETWORK_socket_recvfrom (udp_sock, - buf, - sizeof(buf), - (struct sockaddr *) &sa, - &salen); - if (-1 == rcvd) - { - if (EAGAIN == errno) - break; // We are done reading data - GNUNET_log_strerror (GNUNET_ERROR_TYPE_DEBUG, "recv"); - return; - } - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "Read %lu bytes\n", rcvd); - - /* first, see if it is a UDPBox */ - if (rcvd > sizeof(struct UDPBox)) - { - const struct UDPBox *box; - struct KeyCacheEntry *kce; - - box = (const struct UDPBox *) buf; - kce = GNUNET_CONTAINER_multishortmap_get (key_cache, &box->kid); - if (NULL != kce) - { - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "Found KCE with kid %s\n", - GNUNET_sh2s (&box->kid)); - decrypt_box (box, (size_t) rcvd, kce); - continue; - } - } - - /* next, check if it is a broadcast */ - if (sizeof(struct UDPBroadcast) == rcvd) - { - const struct UDPBroadcast *ub; - struct UdpBroadcastSignature uhs; - struct GNUNET_PeerIdentity sender; - - addr_verify = GNUNET_memdup (&sa, salen); - addr_verify->sin_port = 0; - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "received UDPBroadcast from %s\n", - GNUNET_a2s ((const struct sockaddr *) addr_verify, salen)); - ub = (const struct UDPBroadcast *) buf; - uhs.purpose.purpose = htonl ( - GNUNET_SIGNATURE_PURPOSE_COMMUNICATOR_UDP_BROADCAST); - uhs.purpose.size = htonl (sizeof(uhs)); - uhs.sender = ub->sender; - sender = ub->sender; - if (0 == memcmp (&sender, &my_identity, sizeof (struct - GNUNET_PeerIdentity))) - { - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "Received our own broadcast\n"); - GNUNET_free (addr_verify); - continue; - } - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "checking UDPBroadcastSignature for %s\n", - GNUNET_i2s (&sender)); - GNUNET_CRYPTO_hash ((struct sockaddr *) addr_verify, salen, - &uhs.h_address); - if (GNUNET_OK == - GNUNET_CRYPTO_eddsa_verify ( - GNUNET_SIGNATURE_PURPOSE_COMMUNICATOR_UDP_BROADCAST, - &uhs, - &ub->sender_sig, - &ub->sender.public_key)) - { - char *addr_s; - enum GNUNET_NetworkType nt; - - addr_s = - sockaddr_to_udpaddr_string ((const struct sockaddr *) &sa, salen); - GNUNET_STATISTICS_update (stats, "# broadcasts received", 1, GNUNET_NO); - /* use our own mechanism to determine network type */ - nt = - GNUNET_NT_scanner_get_type (is, (const struct sockaddr *) &sa, salen); - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "validating address %s received from UDPBroadcast\n", - GNUNET_i2s (&sender)); - GNUNET_TRANSPORT_application_validate (ah, &sender, nt, addr_s); - GNUNET_free (addr_s); - GNUNET_free (addr_verify); - continue; - } - else - { - GNUNET_log (GNUNET_ERROR_TYPE_WARNING, - "VerifyingPeer %s is verifying UDPBroadcast\n", - GNUNET_i2s (&my_identity)); - GNUNET_log (GNUNET_ERROR_TYPE_WARNING, - "Verifying UDPBroadcast from %s failed\n", - GNUNET_i2s (&ub->sender)); - } - GNUNET_free (addr_verify); - /* continue with KX, mostly for statistics... */ - } - - - /* finally, test if it is a KX */ - if (rcvd < sizeof(struct UDPConfirmation) + sizeof(struct InitialKX)) - { - GNUNET_STATISTICS_update (stats, - "# messages dropped (no kid, too small for KX)", - 1, - GNUNET_NO); - continue; - } - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "Got KX\n"); - { - const struct InitialKX *kx; - struct SharedSecret *ss; - char pbuf[rcvd - sizeof(struct InitialKX)]; - const struct UDPConfirmation *uc; - struct SenderAddress *sender; - - kx = (const struct InitialKX *) buf; - ss = setup_shared_secret_dec (&kx->ephemeral); - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "Before DEC\n"); - - if (GNUNET_OK != try_decrypt (ss, - kx->gcm_tag, - 0, - &buf[sizeof(*kx)], - sizeof(pbuf), - pbuf)) - { - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "Unable to decrypt tag, dropping...\n"); - GNUNET_free (ss); - GNUNET_STATISTICS_update ( - stats, - "# messages dropped (no kid, AEAD decryption failed)", - 1, - GNUNET_NO); - continue; - } - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "Before VERIFY\n"); - - uc = (const struct UDPConfirmation *) pbuf; - if (GNUNET_OK != verify_confirmation (&kx->ephemeral, uc)) - { - GNUNET_break_op (0); - GNUNET_free (ss); - GNUNET_STATISTICS_update (stats, - "# messages dropped (sender signature invalid)", - 1, - GNUNET_NO); - continue; - } - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "Before SETUP_SENDER\n"); - - calculate_cmac (ss); - sender = setup_sender (&uc->sender, (const struct sockaddr *) &sa, salen); - ss->sender = sender; - GNUNET_CONTAINER_DLL_insert (sender->ss_head, sender->ss_tail, ss); - if ((KCN_THRESHOLD > ss->sender->acks_available) && - (NULL == ss->sender->kce_task) && - (GNUNET_NO == ss->sender->kce_task_finished)) - { - // TODO This task must be per sender! FIXME: This is a nice todo, but I do not know what must be done here to fix. - ss->sender->kce_send_ack_on_finish = GNUNET_YES; - ss->sender->kce_task = GNUNET_SCHEDULER_add_now ( - kce_generate_cb, - ss); - } - sender->num_secrets++; - GNUNET_STATISTICS_update (stats, "# Secrets active", 1, GNUNET_NO); - GNUNET_STATISTICS_update (stats, - "# messages decrypted without BOX", - 1, - GNUNET_NO); - try_handle_plaintext (sender, &uc[1], sizeof(pbuf) - sizeof(*uc)); - if (sender->num_secrets > MAX_SECRETS) - { - if (0 == purge_secrets (sender->ss_tail)) - { - // No secret purged. Delete oldest. - secret_destroy (sender->ss_tail); - } - } - } - } -} - - -/** - * Convert UDP bind specification to a `struct sockaddr *` - * - * @param bindto bind specification to convert - * @param[out] sock_len set to the length of the address - * @return converted bindto specification - */ -static struct sockaddr * -udp_address_to_sockaddr (const char *bindto, socklen_t *sock_len) -{ - struct sockaddr *in; - unsigned int port; - char dummy[2]; - char *colon; - char *cp; - - if (1 == sscanf (bindto, "%u%1s", &port, dummy)) - { - /* interpreting value as just a PORT number */ - if (port > UINT16_MAX) - { - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - "BINDTO specification `%s' invalid: value too large for port\n", - bindto); - return NULL; - } - if ((GNUNET_NO == GNUNET_NETWORK_test_pf (PF_INET6)) || - (GNUNET_YES == - GNUNET_CONFIGURATION_get_value_yesno (cfg, - COMMUNICATOR_CONFIG_SECTION, - "DISABLE_V6"))) - { - struct sockaddr_in *i4; - - i4 = GNUNET_malloc (sizeof(struct sockaddr_in)); - i4->sin_family = AF_INET; - i4->sin_port = htons ((uint16_t) port); - *sock_len = sizeof(struct sockaddr_in); - in = (struct sockaddr *) i4; - } - else - { - struct sockaddr_in6 *i6; - - i6 = GNUNET_malloc (sizeof(struct sockaddr_in6)); - i6->sin6_family = AF_INET6; - i6->sin6_port = htons ((uint16_t) port); - *sock_len = sizeof(struct sockaddr_in6); - in = (struct sockaddr *) i6; - } - return in; - } - cp = GNUNET_strdup (bindto); - colon = strrchr (cp, ':'); - if (NULL != colon) - { - /* interpret value after colon as port */ - *colon = '\0'; - colon++; - if (1 == sscanf (colon, "%u%1s", &port, dummy)) - { - /* interpreting value as just a PORT number */ - if (port > UINT16_MAX) - { - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - "BINDTO specification `%s' invalid: value too large for port\n", - bindto); - GNUNET_free (cp); - return NULL; - } - } - else - { - GNUNET_log ( - GNUNET_ERROR_TYPE_ERROR, - "BINDTO specification `%s' invalid: last ':' not followed by number\n", - bindto); - GNUNET_free (cp); - return NULL; - } - } - else - { - /* interpret missing port as 0, aka pick any free one */ - port = 0; - } - { - /* try IPv4 */ - struct sockaddr_in v4; - - memset (&v4, 0, sizeof(v4)); - if (1 == inet_pton (AF_INET, cp, &v4.sin_addr)) - { - v4.sin_family = AF_INET; - v4.sin_port = htons ((uint16_t) port); -#if HAVE_SOCKADDR_IN_SIN_LEN - v4.sin_len = sizeof(struct sockaddr_in); -#endif - in = GNUNET_memdup (&v4, sizeof(struct sockaddr_in)); - *sock_len = sizeof(struct sockaddr_in); - GNUNET_free (cp); - return in; - } - } - { - /* try IPv6 */ - struct sockaddr_in6 v6; - const char *start; - - memset (&v6, 0, sizeof(v6)); - start = cp; - if (('[' == *cp) && (']' == cp[strlen (cp) - 1])) - { - start++; /* skip over '[' */ - cp[strlen (cp) - 1] = '\0'; /* eat ']' */ - } - if (1 == inet_pton (AF_INET6, start, &v6.sin6_addr)) - { - v6.sin6_family = AF_INET6; - v6.sin6_port = htons ((uint16_t) port); -#if HAVE_SOCKADDR_IN_SIN_LEN - v6.sin6_len = sizeof(sizeof(struct sockaddr_in6)); -#endif - in = GNUNET_memdup (&v6, sizeof(v6)); - *sock_len = sizeof(v6); - GNUNET_free (cp); - return in; - } - } - /* #5528 FIXME (feature!): maybe also try getnameinfo()? */ - GNUNET_free (cp); - return NULL; -} - - -/** - * Pad @a dgram by @a pad_size using @a out_cipher. - * - * @param out_cipher cipher to use - * @param dgram datagram to pad - * @param pad_size number of bytes of padding to append - */ -static void -do_pad (gcry_cipher_hd_t out_cipher, char *dgram, size_t pad_size) -{ - char pad[pad_size]; - - GNUNET_CRYPTO_random_block (GNUNET_CRYPTO_QUALITY_WEAK, pad, sizeof(pad)); - if (sizeof(pad) > sizeof(struct GNUNET_MessageHeader)) - { - struct GNUNET_MessageHeader hdr = - { .size = htons (sizeof(pad)), - .type = htons (GNUNET_MESSAGE_TYPE_COMMUNICATOR_UDP_PAD) }; - - memcpy (pad, &hdr, sizeof(hdr)); - } - GNUNET_assert ( - 0 == - gcry_cipher_encrypt (out_cipher, dgram, sizeof(pad), pad, sizeof(pad))); -} - - -static void -send_msg_with_kx (const struct GNUNET_MessageHeader *msg, struct - ReceiverAddress *receiver) -{ - uint16_t msize = ntohs (msg->size); - struct UdpHandshakeSignature uhs; - struct UDPConfirmation uc; - struct InitialKX kx; - char dgram[receiver->kx_mtu + sizeof(uc) + sizeof(kx)]; - size_t dpos; - gcry_cipher_hd_t out_cipher; - struct SharedSecret *ss; - - if (msize > receiver->kx_mtu) - { - GNUNET_break (0); - if (GNUNET_YES != receiver->receiver_destroy_called) - receiver_destroy (receiver); - return; - } - reschedule_receiver_timeout (receiver); - - /* setup key material */ - - ss = setup_shared_secret_ephemeral (&uhs.ephemeral, receiver); - - if (receiver->num_secrets > MAX_SECRETS) - { - if (0 == purge_secrets (receiver->ss_tail)) - { - // No secret purged. Delete oldest. - secret_destroy (receiver->ss_tail); - } - } - - setup_cipher (&ss->master, 0, &out_cipher); - /* compute 'uc' */ - uc.sender = my_identity; - uc.monotonic_time = - GNUNET_TIME_absolute_hton (GNUNET_TIME_absolute_get_monotonic (cfg)); - uhs.purpose.purpose = htonl ( - GNUNET_SIGNATURE_PURPOSE_COMMUNICATOR_UDP_HANDSHAKE); - uhs.purpose.size = htonl (sizeof(uhs)); - uhs.sender = my_identity; - uhs.receiver = receiver->target; - uhs.monotonic_time = uc.monotonic_time; - GNUNET_CRYPTO_eddsa_sign (my_private_key, - &uhs, - &uc.sender_sig); - /* Leave space for kx */ - dpos = sizeof(kx); - /* Append encrypted uc to dgram */ - GNUNET_assert (0 == gcry_cipher_encrypt (out_cipher, - &dgram[dpos], - sizeof(uc), - &uc, - sizeof(uc))); - dpos += sizeof(uc); - /* Append encrypted payload to dgram */ - GNUNET_assert ( - 0 == gcry_cipher_encrypt (out_cipher, &dgram[dpos], msize, msg, msize)); - dpos += msize; - do_pad (out_cipher, &dgram[dpos], sizeof(dgram) - dpos); - /* Datagram starts with kx */ - kx.ephemeral = uhs.ephemeral; - GNUNET_assert ( - 0 == gcry_cipher_gettag (out_cipher, kx.gcm_tag, sizeof(kx.gcm_tag))); - gcry_cipher_close (out_cipher); - memcpy (dgram, &kx, sizeof(kx)); - if (-1 == GNUNET_NETWORK_socket_sendto (udp_sock, - dgram, - sizeof(dgram), - receiver->address, - receiver->address_len)) - GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING, "send"); - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "Sending KX with payload size %u to %s\n", - msize, - GNUNET_a2s (receiver->address, - receiver->address_len)); -} - - -/** - * Signature of functions implementing the sending functionality of a - * message queue. - * - * @param mq the message queue - * @param msg the message to send - * @param impl_state our `struct ReceiverAddress` - */ -static void -mq_send_kx (struct GNUNET_MQ_Handle *mq, - const struct GNUNET_MessageHeader *msg, - void *impl_state) -{ - struct ReceiverAddress *receiver = impl_state; - - GNUNET_assert (mq == receiver->kx_mq); - send_msg_with_kx (msg, receiver); - GNUNET_MQ_impl_send_continue (mq); -} - - -static void -create_rekey (struct ReceiverAddress *receiver, struct SharedSecret *ss, struct - UDPRekey *rekey) -{ - struct SharedSecret *ss_rekey; - - ss->rekey_initiated = GNUNET_YES; - /* setup key material */ - ss_rekey = setup_shared_secret_ephemeral (&rekey->ephemeral, - receiver); - ss_rekey->sequence_allowed = 0; - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "Setup secret with k = %s\n", - GNUNET_h2s (&(ss_rekey->master))); - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "Setup secret with H(k) = %s\n", - GNUNET_h2s (&(ss_rekey->cmac))); - - /* Append encrypted payload to dgram */ - rekey->header.type = htons (GNUNET_MESSAGE_TYPE_COMMUNICATOR_UDP_REKEY); - rekey->header.size = htons (sizeof (struct UDPRekey)); -} - - -/** - * Signature of functions implementing the sending functionality of a - * message queue. - * - * @param mq the message queue - * @param msg the message to send - * @param impl_state our `struct ReceiverAddress` - */ -static void -mq_send_d (struct GNUNET_MQ_Handle *mq, - const struct GNUNET_MessageHeader *msg, - void *impl_state) -{ - struct ReceiverAddress *receiver = impl_state; - struct UDPRekey rekey; - struct SharedSecret *ss; - int inject_rekey = GNUNET_NO; - uint16_t msize = ntohs (msg->size); - - GNUNET_assert (mq == receiver->d_mq); - if ((msize > receiver->d_mtu) || - (0 == receiver->acks_available)) - { - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "msize: %u, mtu: %lu, acks: %u\n", - msize, - receiver->d_mtu, - receiver->acks_available); - - GNUNET_break (0); - if (GNUNET_YES != receiver->receiver_destroy_called) - receiver_destroy (receiver); - return; - } - reschedule_receiver_timeout (receiver); - - if (receiver->num_secrets > MAX_SECRETS) - { - if (0 == purge_secrets (receiver->ss_tail)) - { - // No secret purged. Delete oldest. - secret_destroy (receiver->ss_tail); - } - } - /* begin "BOX" encryption method, scan for ACKs from tail! */ - for (ss = receiver->ss_tail; NULL != ss; ss = ss->prev) - { - size_t payload_len = sizeof(struct UDPBox) + receiver->d_mtu; - if (ss->sequence_used >= ss->sequence_allowed) - { - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "Skipping ss because no acks to use.\n"); - continue; - } - if (ss->bytes_sent >= rekey_max_bytes) - { - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "Skipping ss because rekey bytes reached.\n"); - // FIXME cleanup ss with too many bytes sent! - continue; - } - if (ss->bytes_sent > rekey_max_bytes * 0.7) - { - if (ss->rekey_initiated == GNUNET_NO) - { - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - "Injecting rekey for ss with byte sent %lu\n", - (unsigned long) ss->bytes_sent); - create_rekey (receiver, ss, &rekey); - inject_rekey = GNUNET_YES; - payload_len += sizeof (rekey); - ss->rekey_initiated = GNUNET_YES; - } - } - if (0 < ss->sequence_used) - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "Trying to send UDPBox with shared secrect %s sequence_used %u and ss->sequence_allowed %u\n", - GNUNET_h2s (&ss->master), - ss->sequence_used, - ss->sequence_allowed); - - char dgram[payload_len]; - struct UDPBox *box; - gcry_cipher_hd_t out_cipher; - size_t dpos; - - box = (struct UDPBox *) dgram; - ss->sequence_used++; - get_kid (&ss->master, ss->sequence_used, &box->kid); - setup_cipher (&ss->master, ss->sequence_used, &out_cipher); - /* Append encrypted payload to dgram */ - dpos = sizeof(struct UDPBox); - if (GNUNET_YES == inject_rekey) - { - GNUNET_assert ( - 0 == gcry_cipher_encrypt (out_cipher, &dgram[dpos], sizeof (rekey), - &rekey, sizeof (rekey))); - dpos += sizeof (rekey); - } - GNUNET_assert ( - 0 == gcry_cipher_encrypt (out_cipher, &dgram[dpos], msize, msg, msize)); - dpos += msize; - do_pad (out_cipher, &dgram[dpos], sizeof(dgram) - dpos); - GNUNET_assert (0 == gcry_cipher_gettag (out_cipher, - box->gcm_tag, - sizeof(box->gcm_tag))); - gcry_cipher_close (out_cipher); - - if (-1 == GNUNET_NETWORK_socket_sendto (udp_sock, - dgram, - payload_len, // FIXME why always send sizeof dgram? - receiver->address, - receiver->address_len)) - GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING, "send"); - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "Sending UDPBox with payload size %u, %u acks left, %lu bytes sent\n", - msize, - receiver->acks_available, - (unsigned long) ss->bytes_sent); - ss->bytes_sent += sizeof (dgram); - receiver->acks_available--; - GNUNET_MQ_impl_send_continue (mq); - return; - } - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "No suitable ss found, sending as KX...\n"); - send_msg_with_kx (msg, receiver); - GNUNET_MQ_impl_send_continue (mq); -} - - -/** - * Signature of functions implementing the destruction of a message - * queue. Implementations must not free @a mq, but should take care - * of @a impl_state. - * - * @param mq the message queue to destroy - * @param impl_state our `struct ReceiverAddress` - */ -static void -mq_destroy_d (struct GNUNET_MQ_Handle *mq, void *impl_state) -{ - struct ReceiverAddress *receiver = impl_state; - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "Default MQ destroyed\n"); - if (mq == receiver->d_mq) - { - receiver->d_mq = NULL; - if (GNUNET_YES != receiver->receiver_destroy_called) - receiver_destroy (receiver); - } -} - - -/** - * Signature of functions implementing the destruction of a message - * queue. Implementations must not free @a mq, but should take care - * of @a impl_state. - * - * @param mq the message queue to destroy - * @param impl_state our `struct ReceiverAddress` - */ -static void -mq_destroy_kx (struct GNUNET_MQ_Handle *mq, void *impl_state) -{ - struct ReceiverAddress *receiver = impl_state; - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "KX MQ destroyed\n"); - if (mq == receiver->kx_mq) - { - receiver->kx_mq = NULL; - if (GNUNET_YES != receiver->receiver_destroy_called) - receiver_destroy (receiver); - } -} - - -/** - * Implementation function that cancels the currently sent message. - * - * @param mq message queue - * @param impl_state our `struct RecvierAddress` - */ -static void -mq_cancel (struct GNUNET_MQ_Handle *mq, void *impl_state) -{ - /* Cancellation is impossible with UDP; bail */ - GNUNET_assert (0); -} - - -/** - * Generic error handler, called with the appropriate - * error code and the same closure specified at the creation of - * the message queue. - * Not every message queue implementation supports an error handler. - * - * @param cls our `struct ReceiverAddress` - * @param error error code - */ -static void -mq_error (void *cls, enum GNUNET_MQ_Error error) -{ - struct ReceiverAddress *receiver = cls; - - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - "MQ error in queue to %s: %d\n", - GNUNET_i2s (&receiver->target), - (int) error); - receiver_destroy (receiver); -} - - -/** - * Setup the MQ for the @a receiver. If a queue exists, - * the existing one is destroyed. Then the MTU is - * recalculated and a fresh queue is initialized. - * - * @param receiver receiver to setup MQ for - */ -static void -setup_receiver_mq (struct ReceiverAddress *receiver) -{ - size_t base_mtu; - - switch (receiver->address->sa_family) - { - case AF_INET: - base_mtu = 1480 /* Ethernet MTU, 1500 - Ethernet header - VLAN tag */ - - sizeof(struct GNUNET_TUN_IPv4Header) /* 20 */ - - sizeof(struct GNUNET_TUN_UdpHeader) /* 8 */; - break; - - case AF_INET6: - base_mtu = 1280 /* Minimum MTU required by IPv6 */ - - sizeof(struct GNUNET_TUN_IPv6Header) /* 40 */ - - sizeof(struct GNUNET_TUN_UdpHeader) /* 8 */; - break; - - default: - GNUNET_assert (0); - break; - } - /* MTU based on full KX messages */ - receiver->kx_mtu = base_mtu - sizeof(struct InitialKX) /* 48 */ - - sizeof(struct UDPConfirmation); /* 104 */ - /* MTU based on BOXed messages */ - receiver->d_mtu = base_mtu - sizeof(struct UDPBox); - - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "Setting up MQs and QHs\n"); - /* => Effective MTU for CORE will range from 1080 (IPv6 + KX) to - 1404 (IPv4 + Box) bytes, depending on circumstances... */ - if (NULL == receiver->kx_mq) - receiver->kx_mq = GNUNET_MQ_queue_for_callbacks (&mq_send_kx, - &mq_destroy_kx, - &mq_cancel, - receiver, - NULL, - &mq_error, - receiver); - if (NULL == receiver->d_mq) - receiver->d_mq = GNUNET_MQ_queue_for_callbacks (&mq_send_d, - &mq_destroy_d, - &mq_cancel, - receiver, - NULL, - &mq_error, - receiver); - - receiver->kx_qh = - GNUNET_TRANSPORT_communicator_mq_add (ch, - &receiver->target, - receiver->foreign_addr, - receiver->kx_mtu, - GNUNET_TRANSPORT_QUEUE_LENGTH_UNLIMITED, - 0, /* Priority */ - receiver->nt, - GNUNET_TRANSPORT_CS_OUTBOUND, - receiver->kx_mq); -} - - -/** - * Function called by the transport service to initialize a - * message queue given address information about another peer. - * If and when the communication channel is established, the - * communicator must call #GNUNET_TRANSPORT_communicator_mq_add() - * to notify the service that the channel is now up. It is - * the responsibility of the communicator to manage sane - * retries and timeouts for any @a peer/@a address combination - * provided by the transport service. Timeouts and retries - * do not need to be signalled to the transport service. - * - * @param cls closure - * @param peer identity of the other peer - * @param address where to send the message, human-readable - * communicator-specific format, 0-terminated, UTF-8 - * @return #GNUNET_OK on success, #GNUNET_SYSERR if the provided address is - * invalid - */ -static int -mq_init (void *cls, const struct GNUNET_PeerIdentity *peer, const char *address) -{ - struct ReceiverAddress *receiver; - const char *path; - struct sockaddr *in; - socklen_t in_len; - - if (0 != strncmp (address, - COMMUNICATOR_ADDRESS_PREFIX "-", - strlen (COMMUNICATOR_ADDRESS_PREFIX "-"))) - { - GNUNET_break_op (0); - return GNUNET_SYSERR; - } - path = &address[strlen (COMMUNICATOR_ADDRESS_PREFIX "-")]; - in = udp_address_to_sockaddr (path, &in_len); - - receiver = GNUNET_new (struct ReceiverAddress); - receiver->address = in; - receiver->address_len = in_len; - receiver->target = *peer; - receiver->nt = GNUNET_NT_scanner_get_type (is, in, in_len); - (void) GNUNET_CONTAINER_multipeermap_put ( - receivers, - &receiver->target, - receiver, - GNUNET_CONTAINER_MULTIHASHMAPOPTION_MULTIPLE); - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "Added %s to receivers\n", - GNUNET_i2s_full (&receiver->target)); - receiver->timeout = - GNUNET_TIME_relative_to_absolute (GNUNET_CONSTANTS_IDLE_CONNECTION_TIMEOUT); - receiver->hn = GNUNET_CONTAINER_heap_insert (receivers_heap, - receiver, - receiver->timeout.abs_value_us); - GNUNET_STATISTICS_set (stats, - "# receivers active", - GNUNET_CONTAINER_multipeermap_size (receivers), - GNUNET_NO); - receiver->foreign_addr = - sockaddr_to_udpaddr_string (receiver->address, receiver->address_len); - setup_receiver_mq (receiver); - if (NULL == timeout_task) - timeout_task = GNUNET_SCHEDULER_add_now (&check_timeouts, NULL); - return GNUNET_OK; -} - - -/** - * Iterator over all receivers to clean up. - * - * @param cls NULL - * @param target unused - * @param value the queue to destroy - * @return #GNUNET_OK to continue to iterate - */ -static int -get_receiver_delete_it (void *cls, - const struct GNUNET_PeerIdentity *target, - void *value) -{ - struct ReceiverAddress *receiver = value; - - (void) cls; - (void) target; - receiver_destroy (receiver); - return GNUNET_OK; -} - - -/** - * Iterator over all senders to clean up. - * - * @param cls NULL - * @param target unused - * @param value the queue to destroy - * @return #GNUNET_OK to continue to iterate - */ -static int -get_sender_delete_it (void *cls, - const struct GNUNET_PeerIdentity *target, - void *value) -{ - struct SenderAddress *sender = value; - - (void) cls; - (void) target; - - - sender_destroy (sender); - return GNUNET_OK; -} - - -/** - * Shutdown the UNIX communicator. - * - * @param cls NULL (always) - */ -static void -do_shutdown (void *cls) -{ - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "do_shutdown\n"); - if (NULL != nat) - { - GNUNET_NAT_unregister (nat); - nat = NULL; - } - while (NULL != bi_head) - bi_destroy (bi_head); - if (NULL != broadcast_task) - { - GNUNET_SCHEDULER_cancel (broadcast_task); - broadcast_task = NULL; - } - if (NULL != timeout_task) - { - GNUNET_SCHEDULER_cancel (timeout_task); - timeout_task = NULL; - } - if (NULL != read_task) - { - GNUNET_SCHEDULER_cancel (read_task); - read_task = NULL; - } - if (NULL != udp_sock) - { - GNUNET_break (GNUNET_OK == - GNUNET_NETWORK_socket_close (udp_sock)); - udp_sock = NULL; - } - GNUNET_CONTAINER_multipeermap_iterate (receivers, - &get_receiver_delete_it, - NULL); - GNUNET_CONTAINER_multipeermap_destroy (receivers); - GNUNET_CONTAINER_multipeermap_iterate (senders, - &get_sender_delete_it, - NULL); - GNUNET_CONTAINER_multipeermap_destroy (senders); - GNUNET_CONTAINER_multishortmap_destroy (key_cache); - GNUNET_CONTAINER_heap_destroy (senders_heap); - GNUNET_CONTAINER_heap_destroy (receivers_heap); - if (NULL != timeout_task) - { - GNUNET_SCHEDULER_cancel (timeout_task); - timeout_task = NULL; - } - if (NULL != ch) - { - GNUNET_TRANSPORT_communicator_disconnect (ch); - ch = NULL; - } - if (NULL != ah) - { - GNUNET_TRANSPORT_application_done (ah); - ah = NULL; - } - if (NULL != stats) - { - GNUNET_STATISTICS_destroy (stats, GNUNET_NO); - stats = NULL; - } - if (NULL != my_private_key) - { - GNUNET_free (my_private_key); - my_private_key = NULL; - } - if (NULL != is) - { - GNUNET_NT_scanner_done (is); - is = NULL; - } - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "do_shutdown finished\n"); -} - - -/** - * Function called when the transport service has received a - * backchannel message for this communicator (!) via a different return - * path. Should be an acknowledgement. - * - * @param cls closure, NULL - * @param sender which peer sent the notification - * @param msg payload - */ -static void -enc_notify_cb (void *cls, - const struct GNUNET_PeerIdentity *sender, - const struct GNUNET_MessageHeader *msg) -{ - const struct UDPAck *ack; - - (void) cls; - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "Storing UDPAck received from backchannel from %s\n", - GNUNET_i2s_full (sender)); - if ((ntohs (msg->type) != GNUNET_MESSAGE_TYPE_COMMUNICATOR_UDP_ACK) || - (ntohs (msg->size) != sizeof(struct UDPAck))) - { - GNUNET_break_op (0); - return; - } - ack = (const struct UDPAck *) msg; - GNUNET_CONTAINER_multipeermap_get_multiple (receivers, - sender, - &handle_ack, - (void *) ack); -} - - -/** - * Signature of the callback passed to #GNUNET_NAT_register() for - * a function to call whenever our set of 'valid' addresses changes. - * - * @param cls closure - * @param app_ctx[in,out] location where the app can store stuff - * on add and retrieve it on remove - * @param add_remove #GNUNET_YES to add a new public IP address, - * #GNUNET_NO to remove a previous (now invalid) one - * @param ac address class the address belongs to - * @param addr either the previous or the new public IP address - * @param addrlen actual length of the @a addr - */ -static void -nat_address_cb (void *cls, - void **app_ctx, - int add_remove, - enum GNUNET_NAT_AddressClass ac, - const struct sockaddr *addr, - socklen_t addrlen) -{ - char *my_addr; - struct GNUNET_TRANSPORT_AddressIdentifier *ai; - - if (GNUNET_YES == add_remove) - { - enum GNUNET_NetworkType nt; - - GNUNET_asprintf (&my_addr, - "%s-%s", - COMMUNICATOR_ADDRESS_PREFIX, - GNUNET_a2s (addr, addrlen)); - nt = GNUNET_NT_scanner_get_type (is, addr, addrlen); - ai = - GNUNET_TRANSPORT_communicator_address_add (ch, - my_addr, - nt, - GNUNET_TIME_UNIT_FOREVER_REL); - GNUNET_free (my_addr); - *app_ctx = ai; - } - else - { - ai = *app_ctx; - GNUNET_TRANSPORT_communicator_address_remove (ai); - *app_ctx = NULL; - } -} - - -/** - * Broadcast our presence on one of our interfaces. - * - * @param cls a `struct BroadcastInterface` - */ -static void -ifc_broadcast (void *cls) -{ - struct BroadcastInterface *bi = cls; - struct GNUNET_TIME_Relative delay; - - delay = BROADCAST_FREQUENCY; - delay.rel_value_us = - GNUNET_CRYPTO_random_u64 (GNUNET_CRYPTO_QUALITY_WEAK, delay.rel_value_us); - bi->broadcast_task = - GNUNET_SCHEDULER_add_delayed (delay, &ifc_broadcast, bi); - - switch (bi->sa->sa_family) - { - case AF_INET: { - static int yes = 1; - static int no = 0; - ssize_t sent; - - if (GNUNET_OK != - GNUNET_NETWORK_socket_setsockopt (udp_sock, - SOL_SOCKET, - SO_BROADCAST, - &yes, - sizeof(int))) - GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING, - "setsockopt"); - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "creating UDPBroadcast from %s\n", - GNUNET_i2s (&(bi->bcm.sender))); - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "sending UDPBroadcast to add %s\n", - GNUNET_a2s (bi->ba, bi->salen)); - sent = GNUNET_NETWORK_socket_sendto (udp_sock, - &bi->bcm, - sizeof(bi->bcm), - bi->ba, - bi->salen); - if (-1 == sent) - GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING, - "sendto"); - if (GNUNET_OK != GNUNET_NETWORK_socket_setsockopt (udp_sock, - SOL_SOCKET, - SO_BROADCAST, - &no, - sizeof(int))) - GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING, - "setsockopt"); - break; - } - - case AF_INET6: { - ssize_t sent; - struct sockaddr_in6 dst; - - dst.sin6_family = AF_INET6; - dst.sin6_port = htons (my_port); - dst.sin6_addr = bi->mcreq.ipv6mr_multiaddr; - dst.sin6_scope_id = ((struct sockaddr_in6 *) bi->ba)->sin6_scope_id; - - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "sending UDPBroadcast\n"); - sent = GNUNET_NETWORK_socket_sendto (udp_sock, - &bi->bcm, - sizeof(bi->bcm), - (const struct sockaddr *) &dst, - sizeof(dst)); - if (-1 == sent) - GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING, "sendto"); - break; - } - - default: - GNUNET_break (0); - break; - } -} - - -/** - * Callback function invoked for each interface found. - * Activates/deactivates broadcast interfaces. - * - * @param cls NULL - * @param name name of the interface (can be NULL for unknown) - * @param isDefault is this presumably the default interface - * @param addr address of this interface (can be NULL for unknown or unassigned) - * @param broadcast_addr the broadcast address (can be NULL for unknown or - * unassigned) - * @param netmask the network mask (can be NULL for unknown or unassigned) - * @param addrlen length of the address - * @return #GNUNET_OK to continue iteration, #GNUNET_SYSERR to abort - */ -static int -iface_proc (void *cls, - const char *name, - int isDefault, - const struct sockaddr *addr, - const struct sockaddr *broadcast_addr, - const struct sockaddr *netmask, - socklen_t addrlen) -{ - struct BroadcastInterface *bi; - enum GNUNET_NetworkType network; - struct UdpBroadcastSignature ubs; - - (void) cls; - (void) netmask; - if (NULL == addr) - return GNUNET_YES; /* need to know our address! */ - network = GNUNET_NT_scanner_get_type (is, addr, addrlen); - if (GNUNET_NT_LOOPBACK == network) - { - /* Broadcasting on loopback does not make sense */ - return GNUNET_YES; - } - for (bi = bi_head; NULL != bi; bi = bi->next) - { - if ((bi->salen == addrlen) && (0 == memcmp (addr, bi->sa, addrlen))) - { - bi->found = GNUNET_YES; - return GNUNET_OK; - } - } - - if ((AF_INET6 == addr->sa_family) && (NULL == broadcast_addr)) - return GNUNET_OK; /* broadcast_addr is required for IPv6! */ - if ((AF_INET6 == addr->sa_family) && (GNUNET_YES != have_v6_socket)) - return GNUNET_OK; /* not using IPv6 */ - - bi = GNUNET_new (struct BroadcastInterface); - bi->sa = GNUNET_memdup (addr, - addrlen); - if ( (NULL != broadcast_addr) && - (addrlen == sizeof (struct sockaddr_in)) ) - { - struct sockaddr_in *ba; - - ba = GNUNET_memdup (broadcast_addr, - addrlen); - ba->sin_port = htons (2086); /* always GNUnet port, ignore configuration! */ - bi->ba = (struct sockaddr *) ba; - } - bi->salen = addrlen; - bi->found = GNUNET_YES; - bi->bcm.sender = my_identity; - ubs.purpose.purpose = htonl ( - GNUNET_SIGNATURE_PURPOSE_COMMUNICATOR_UDP_BROADCAST); - ubs.purpose.size = htonl (sizeof(ubs)); - ubs.sender = my_identity; - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "creating UDPBroadcastSignature for %s\n", - GNUNET_a2s (addr, addrlen)); - GNUNET_CRYPTO_hash (addr, addrlen, &ubs.h_address); - GNUNET_CRYPTO_eddsa_sign (my_private_key, - &ubs, - &bi->bcm.sender_sig); - if (NULL != bi->ba) - { - bi->broadcast_task = GNUNET_SCHEDULER_add_now (&ifc_broadcast, bi); - GNUNET_CONTAINER_DLL_insert (bi_head, bi_tail, bi); - } - if ((AF_INET6 == addr->sa_family) && (NULL != broadcast_addr)) - { - /* Create IPv6 multicast request */ - const struct sockaddr_in6 *s6 = - (const struct sockaddr_in6 *) broadcast_addr; - - GNUNET_assert ( - 1 == inet_pton (AF_INET6, "FF05::13B", &bi->mcreq.ipv6mr_multiaddr)); - - /* http://tools.ietf.org/html/rfc2553#section-5.2: - * - * IPV6_JOIN_GROUP - * - * Join a multicast group on a specified local interface. If the - * interface index is specified as 0, the kernel chooses the local - * interface. For example, some kernels look up the multicast - * group in the normal IPv6 routing table and using the resulting - * interface; we do this for each interface, so no need to use - * zero (anymore...). - */ - bi->mcreq.ipv6mr_interface = s6->sin6_scope_id; - - /* Join the multicast group */ - if (GNUNET_OK != GNUNET_NETWORK_socket_setsockopt (udp_sock, - IPPROTO_IPV6, - IPV6_JOIN_GROUP, - &bi->mcreq, - sizeof(bi->mcreq))) - { - GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING, "setsockopt"); - } - } - return GNUNET_OK; -} - - -/** - * Scan interfaces to broadcast our presence on the LAN. - * - * @param cls NULL, unused - */ -static void -do_broadcast (void *cls) -{ - struct BroadcastInterface *bin; - - (void) cls; - for (struct BroadcastInterface *bi = bi_head; NULL != bi; bi = bi->next) - bi->found = GNUNET_NO; - GNUNET_OS_network_interfaces_list (&iface_proc, NULL); - for (struct BroadcastInterface *bi = bi_head; NULL != bi; bi = bin) - { - bin = bi->next; - if (GNUNET_NO == bi->found) - bi_destroy (bi); - } - broadcast_task = GNUNET_SCHEDULER_add_delayed (INTERFACE_SCAN_FREQUENCY, - &do_broadcast, - NULL); -} - - -static void -try_connection_reversal (void *cls, - const struct sockaddr *addr, - socklen_t addrlen) -{ - /* FIXME: support reversal: #5529 */ - GNUNET_log (GNUNET_ERROR_TYPE_INFO, - "No connection reversal implemented!"); -} - - -/** - * Setup communicator and launch network interactions. - * - * @param cls NULL (always) - * @param args remaining command-line arguments - * @param cfgfile name of the configuration file used (for saving, can be NULL!) - * @param c configuration - */ -static void -run (void *cls, - char *const *args, - const char *cfgfile, - const struct GNUNET_CONFIGURATION_Handle *c) -{ - char *bindto; - struct sockaddr *in; - socklen_t in_len; - struct sockaddr_storage in_sto; - socklen_t sto_len; - - (void) cls; - cfg = c; - if (GNUNET_OK != - GNUNET_CONFIGURATION_get_value_string (cfg, - COMMUNICATOR_CONFIG_SECTION, - "BINDTO", - &bindto)) - { - GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR, - COMMUNICATOR_CONFIG_SECTION, - "BINDTO"); - return; - } - - if (GNUNET_OK != - GNUNET_CONFIGURATION_get_value_time (cfg, - COMMUNICATOR_CONFIG_SECTION, - "REKEY_INTERVAL", - &rekey_interval)) - rekey_interval = DEFAULT_REKEY_TIME_INTERVAL; - - if (GNUNET_OK != - GNUNET_CONFIGURATION_get_value_size (cfg, - COMMUNICATOR_CONFIG_SECTION, - "REKEY_MAX_BYTES", - &rekey_max_bytes)) - rekey_max_bytes = DEFAULT_REKEY_MAX_BYTES; - - in = udp_address_to_sockaddr (bindto, &in_len); - if (NULL == in) - { - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - "Failed to setup UDP socket address with path `%s'\n", - bindto); - GNUNET_free (bindto); - return; - } - udp_sock = - GNUNET_NETWORK_socket_create (in->sa_family, - SOCK_DGRAM, - IPPROTO_UDP); - if (NULL == udp_sock) - { - GNUNET_log_strerror (GNUNET_ERROR_TYPE_ERROR, "socket"); - GNUNET_free (in); - GNUNET_free (bindto); - return; - } - if (AF_INET6 == in->sa_family) - have_v6_socket = GNUNET_YES; - if (GNUNET_OK != - GNUNET_NETWORK_socket_bind (udp_sock, - in, - in_len)) - { - GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_ERROR, - "bind", - bindto); - GNUNET_NETWORK_socket_close (udp_sock); - udp_sock = NULL; - GNUNET_free (in); - GNUNET_free (bindto); - return; - } - - /* We might have bound to port 0, allowing the OS to figure it out; - thus, get the real IN-address from the socket */ - sto_len = sizeof(in_sto); - if (0 != getsockname (GNUNET_NETWORK_get_fd (udp_sock), - (struct sockaddr *) &in_sto, - &sto_len)) - { - memcpy (&in_sto, in, in_len); - sto_len = in_len; - } - GNUNET_free (in); - GNUNET_free (bindto); - in = (struct sockaddr *) &in_sto; - in_len = sto_len; - GNUNET_log_from_nocheck (GNUNET_ERROR_TYPE_DEBUG, - "transport", - "Bound to `%s'\n", - GNUNET_a2s ((const struct sockaddr *) &in_sto, - sto_len)); - switch (in->sa_family) - { - case AF_INET: - my_port = ntohs (((struct sockaddr_in *) in)->sin_port); - break; - - case AF_INET6: - my_port = ntohs (((struct sockaddr_in6 *) in)->sin6_port); - break; - - default: - GNUNET_break (0); - my_port = 0; - } - stats = GNUNET_STATISTICS_create ("C-UDP", cfg); - senders = GNUNET_CONTAINER_multipeermap_create (32, GNUNET_YES); - receivers = GNUNET_CONTAINER_multipeermap_create (32, GNUNET_YES); - senders_heap = GNUNET_CONTAINER_heap_create (GNUNET_CONTAINER_HEAP_ORDER_MIN); - receivers_heap = - GNUNET_CONTAINER_heap_create (GNUNET_CONTAINER_HEAP_ORDER_MIN); - key_cache = GNUNET_CONTAINER_multishortmap_create (1024, GNUNET_YES); - GNUNET_SCHEDULER_add_shutdown (&do_shutdown, NULL); - is = GNUNET_NT_scanner_init (); - my_private_key = GNUNET_CRYPTO_eddsa_key_create_from_configuration (cfg); - if (NULL == my_private_key) - { - GNUNET_log ( - GNUNET_ERROR_TYPE_ERROR, - _ ( - "Transport service is lacking key configuration settings. Exiting.\n")); - GNUNET_SCHEDULER_shutdown (); - return; - } - GNUNET_CRYPTO_eddsa_key_get_public (my_private_key, &my_identity.public_key); - /* start reading */ - read_task = GNUNET_SCHEDULER_add_read_net (GNUNET_TIME_UNIT_FOREVER_REL, - udp_sock, - &sock_read, - NULL); - ch = GNUNET_TRANSPORT_communicator_connect (cfg, - COMMUNICATOR_CONFIG_SECTION, - COMMUNICATOR_ADDRESS_PREFIX, - GNUNET_TRANSPORT_CC_UNRELIABLE, - &mq_init, - NULL, - &enc_notify_cb, - NULL); - if (NULL == ch) - { - GNUNET_break (0); - GNUNET_SCHEDULER_shutdown (); - return; - } - ah = GNUNET_TRANSPORT_application_init (cfg); - if (NULL == ah) - { - GNUNET_break (0); - GNUNET_SCHEDULER_shutdown (); - return; - } - /* start broadcasting */ - if (GNUNET_YES != - GNUNET_CONFIGURATION_get_value_yesno (cfg, - COMMUNICATOR_CONFIG_SECTION, - "DISABLE_BROADCAST")) - { - broadcast_task = GNUNET_SCHEDULER_add_now (&do_broadcast, NULL); - } - nat = GNUNET_NAT_register (cfg, - COMMUNICATOR_CONFIG_SECTION, - IPPROTO_UDP, - 1 /* one address */, - (const struct sockaddr **) &in, - &in_len, - &nat_address_cb, - try_connection_reversal, - NULL /* closure */); -} - - -/** - * The main function for the UNIX communicator. - * - * @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 - }; - int ret; - - GNUNET_log_from_nocheck (GNUNET_ERROR_TYPE_DEBUG, - "transport", - "Starting udp communicator\n"); - if (GNUNET_OK != GNUNET_STRINGS_get_utf8_args (argc, argv, &argc, &argv)) - return 2; - - ret = (GNUNET_OK == GNUNET_PROGRAM_run (argc, - argv, - "gnunet-communicator-udp", - _ ("GNUnet UDP communicator"), - options, - &run, - NULL)) - ? 0 - : 1; - GNUNET_free_nz ((void *) argv); - return ret; -} - - -/* end of gnunet-communicator-udp.c */ diff --git a/src/transport/gnunet-communicator-unix.c b/src/transport/gnunet-communicator-unix.c deleted file mode 100644 index 0ff16ab08..000000000 --- a/src/transport/gnunet-communicator-unix.c +++ /dev/null @@ -1,1166 +0,0 @@ -/* - This file is part of GNUnet - Copyright (C) 2010-2014, 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 . - - SPDX-License-Identifier: AGPL3.0-or-later - */ - -/** - * @file transport/gnunet-communicator-unix.c - * @brief Transport plugin using unix domain sockets (!) - * Clearly, can only be used locally on Unix/Linux hosts... - * ONLY INTENDED FOR TESTING!!! - * @author Christian Grothoff - * @author Nathan Evans - */ -#include "platform.h" -#include "gnunet_util_lib.h" -#include "gnunet_protocols.h" -#include "gnunet_constants.h" -#include "gnunet_statistics_service.h" -#include "gnunet_transport_communication_service.h" - -/** - * How many messages do we keep at most in the queue to the - * transport service before we start to drop (default, - * can be changed via the configuration file). - * Should be _below_ the level of the communicator API, as - * otherwise we may read messages just to have them dropped - * by the communicator API. - */ -#define DEFAULT_MAX_QUEUE_LENGTH 8000 - -/** - * Address prefix used by the communicator. - */ -#define COMMUNICATOR_ADDRESS_PREFIX "unix" - -/** - * Configuration section used by the communicator. - */ -#define COMMUNICATOR_CONFIG_SECTION "communicator-unix" - -/** - * Our MTU. - */ -#ifndef DARWIN -#define UNIX_MTU UINT16_MAX -#else -#define UNIX_MTU 2048 -#endif - -GNUNET_NETWORK_STRUCT_BEGIN - -/** - * UNIX Message-Packet header. - */ -struct UNIXMessage -{ - /** - * Message header. - */ - struct GNUNET_MessageHeader header; - - /** - * What is the identity of the sender (GNUNET_hash of public key) - */ - struct GNUNET_PeerIdentity sender; -}; - -GNUNET_NETWORK_STRUCT_END - - -/** - * Handle for a queue. - */ -struct Queue -{ - /** - * Queues with pending messages (!) are kept in a DLL. - */ - struct Queue *next; - - /** - * Queues with pending messages (!) are kept in a DLL. - */ - struct Queue *prev; - - /** - * To whom are we talking to. - */ - struct GNUNET_PeerIdentity target; - - /** - * Address of the other peer. - */ - struct sockaddr_un *address; - - /** - * Length of the address. - */ - socklen_t address_len; - - /** - * Message currently scheduled for transmission, non-NULL if and only - * if this queue is in the #queue_head DLL. - */ - struct UNIXMessage *msg; - - /** - * Message queue we are providing for the #ch. - */ - struct GNUNET_MQ_Handle *mq; - - /** - * handle for this queue with the #ch. - */ - struct GNUNET_TRANSPORT_QueueHandle *qh; - - /** - * Number of bytes we currently have in our write queue. - */ - unsigned long long bytes_in_queue; - - /** - * Timeout for this queue. - */ - struct GNUNET_TIME_Absolute timeout; - - /** - * Queue timeout task. - */ - struct GNUNET_SCHEDULER_Task *timeout_task; -}; - -/** - * My Peer Identity - */ -static struct GNUNET_PeerIdentity my_identity; - -/** - * ID of read task - */ -static struct GNUNET_SCHEDULER_Task *read_task; - -/** - * ID of write task - */ -static struct GNUNET_SCHEDULER_Task *write_task; - -/** - * Number of messages we currently have in our queues towards the transport service. - */ -static unsigned long long delivering_messages; - -/** - * Maximum queue length before we stop reading towards the transport service. - */ -static unsigned long long max_queue_length; - -/** - * For logging statistics. - */ -static struct GNUNET_STATISTICS_Handle *stats; - -/** - * Our environment. - */ -static struct GNUNET_TRANSPORT_CommunicatorHandle *ch; - -/** - * Queues (map from peer identity to `struct Queue`) - */ -static struct GNUNET_CONTAINER_MultiPeerMap *queue_map; - -/** - * Head of queue of messages to transmit. - */ -static struct Queue *queue_head; - -/** - * Tail of queue of messages to transmit. - */ -static struct Queue *queue_tail; - -/** - * socket that we transmit all data with - */ -static struct GNUNET_NETWORK_Handle *unix_sock; - -/** - * Handle to the operation that publishes our address. - */ -static struct GNUNET_TRANSPORT_AddressIdentifier *ai; - - -/** - * Functions with this signature are called whenever we need - * to close a queue due to a disconnect or failure to - * establish a connection. - * - * @param queue queue to close down - */ -static void -queue_destroy (struct Queue *queue) -{ - struct GNUNET_MQ_Handle *mq; - - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "Disconnecting queue for peer `%s'\n", - GNUNET_i2s (&queue->target)); - if (0 != queue->bytes_in_queue) - { - GNUNET_CONTAINER_DLL_remove (queue_head, queue_tail, queue); - queue->bytes_in_queue = 0; - } - if (NULL != (mq = queue->mq)) - { - queue->mq = NULL; - GNUNET_MQ_destroy (mq); - } - GNUNET_assert ( - GNUNET_YES == - GNUNET_CONTAINER_multipeermap_remove (queue_map, &queue->target, queue)); - GNUNET_STATISTICS_set (stats, - "# queues active", - GNUNET_CONTAINER_multipeermap_size (queue_map), - GNUNET_NO); - if (NULL != queue->timeout_task) - { - GNUNET_SCHEDULER_cancel (queue->timeout_task); - queue->timeout_task = NULL; - } - GNUNET_free (queue->address); - GNUNET_free (queue); -} - - -/** - * Queue was idle for too long, so disconnect it - * - * @param cls the `struct Queue *` to disconnect - */ -static void -queue_timeout (void *cls) -{ - struct Queue *queue = cls; - struct GNUNET_TIME_Relative left; - - queue->timeout_task = NULL; - left = GNUNET_TIME_absolute_get_remaining (queue->timeout); - if (0 != left.rel_value_us) - { - /* not actually our turn yet, but let's at least update - the monitor, it may think we're about to die ... */ - queue->timeout_task = - GNUNET_SCHEDULER_add_delayed (left, &queue_timeout, queue); - return; - } - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "Queue %p was idle for %s, disconnecting\n", - queue, - GNUNET_STRINGS_relative_time_to_string ( - GNUNET_CONSTANTS_IDLE_CONNECTION_TIMEOUT, - GNUNET_YES)); - queue_destroy (queue); -} - - -/** - * Increment queue timeout due to activity. We do not immediately - * notify the monitor here as that might generate excessive - * signalling. - * - * @param queue queue for which the timeout should be rescheduled - */ -static void -reschedule_queue_timeout (struct Queue *queue) -{ - GNUNET_assert (NULL != queue->timeout_task); - queue->timeout = - GNUNET_TIME_relative_to_absolute (GNUNET_CONSTANTS_IDLE_CONNECTION_TIMEOUT); -} - - -/** - * Convert unix path to a `struct sockaddr_un *` - * - * @param unixpath path to convert - * @param[out] sock_len set to the length of the address - * @param is_abstract is this an abstract @a unixpath - * @return converted unix path - */ -static struct sockaddr_un * -unix_address_to_sockaddr (const char *unixpath, socklen_t *sock_len) -{ - struct sockaddr_un *un; - size_t slen; - - GNUNET_assert (0 < strlen (unixpath)); /* sanity check */ - un = GNUNET_new (struct sockaddr_un); - un->sun_family = AF_UNIX; - slen = strlen (unixpath); - if (slen >= sizeof(un->sun_path)) - slen = sizeof(un->sun_path) - 1; - GNUNET_memcpy (un->sun_path, unixpath, slen); - un->sun_path[slen] = '\0'; - slen = sizeof(struct sockaddr_un); -#if HAVE_SOCKADDR_UN_SUN_LEN - un->sun_len = (u_char) slen; -#endif - (*sock_len) = slen; - if ('@' == un->sun_path[0]) - un->sun_path[0] = '\0'; - return un; -} - - -/** - * Closure to #lookup_queue_it(). - */ -struct LookupCtx -{ - /** - * Location to store the queue, if found. - */ - struct Queue *res; - - /** - * Address we are looking for. - */ - const struct sockaddr_un *un; - - /** - * Number of bytes in @a un - */ - socklen_t un_len; -}; - - -/** - * Function called to find a queue by address. - * - * @param cls the `struct LookupCtx *` - * @param key peer we are looking for (unused) - * @param value a queue - * @return #GNUNET_YES if not found (continue looking), #GNUNET_NO on success - */ -static int -lookup_queue_it (void *cls, const struct GNUNET_PeerIdentity *key, void *value) -{ - struct LookupCtx *lctx = cls; - struct Queue *queue = value; - - if ((queue->address_len == lctx->un_len) && - (0 == memcmp (lctx->un, queue->address, queue->address_len))) - { - lctx->res = queue; - return GNUNET_NO; - } - return GNUNET_YES; -} - - -/** - * Find an existing queue by address. - * - * @param plugin the plugin - * @param address the address to find - * @return NULL if queue was not found - */ -static struct Queue * -lookup_queue (const struct GNUNET_PeerIdentity *peer, - const struct sockaddr_un *un, - socklen_t un_len) -{ - struct LookupCtx lctx; - - lctx.un = un; - lctx.un_len = un_len; - lctx.res = NULL; - GNUNET_CONTAINER_multipeermap_get_multiple (queue_map, - peer, - &lookup_queue_it, - &lctx); - return lctx.res; -} - - -/** - * We have been notified that our socket is ready to write. - * Then reschedule this function to be called again once more is available. - * - * @param cls NULL - */ -static void -select_write_cb (void *cls) -{ - struct Queue *queue = queue_tail; - const struct GNUNET_MessageHeader *msg = &queue->msg->header; - size_t msg_size = ntohs (msg->size); - ssize_t sent; - - /* take queue of the ready list */ - write_task = NULL; -resend: - /* Send the data */ - sent = GNUNET_NETWORK_socket_sendto (unix_sock, - msg, - msg_size, - (const struct sockaddr *) queue->address, - queue->address_len); - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "UNIX transmitted message to %s (%d/%u: %s)\n", - GNUNET_i2s (&queue->target), - (int) sent, - (unsigned int) msg_size, - (sent < 0) ? strerror (errno) : "ok"); - if (-1 != sent) - { - GNUNET_CONTAINER_DLL_remove (queue_head, queue_tail, queue); - if (NULL != queue_head) - write_task = GNUNET_SCHEDULER_add_write_net (GNUNET_TIME_UNIT_FOREVER_REL, - unix_sock, - &select_write_cb, - NULL); - - /* send 'msg' */ - GNUNET_free (queue->msg); - queue->msg = NULL; - GNUNET_MQ_impl_send_continue (queue->mq); - GNUNET_STATISTICS_update (stats, - "# bytes sent", - (long long) sent, - GNUNET_NO); - reschedule_queue_timeout (queue); - return; /* all good */ - } - GNUNET_STATISTICS_update (stats, - "# network transmission failures", - 1, - GNUNET_NO); - write_task = GNUNET_SCHEDULER_add_write_net (GNUNET_TIME_UNIT_FOREVER_REL, - unix_sock, - &select_write_cb, - NULL); - switch (errno) - { - case EAGAIN: - case ENOBUFS: - /* We should retry later... */ - GNUNET_log_strerror (GNUNET_ERROR_TYPE_DEBUG, "send"); - return; - - case EMSGSIZE: { - socklen_t size = 0; - socklen_t len = sizeof(size); - - GNUNET_NETWORK_socket_getsockopt (unix_sock, - SOL_SOCKET, - SO_SNDBUF, - &size, - &len); - if (size > ntohs (msg->size)) - { - /* Buffer is bigger than message: error, no retry - * This should never happen!*/ - GNUNET_break (0); - return; - } - GNUNET_log ( - GNUNET_ERROR_TYPE_WARNING, - "Trying to increase socket buffer size from %u to %u for message size %u\n", - (unsigned int) size, - (unsigned int) ((msg_size / 1000) + 2) * 1000, - (unsigned int) msg_size); - size = ((msg_size / 1000) + 2) * 1000; - if (GNUNET_OK == GNUNET_NETWORK_socket_setsockopt (unix_sock, - SOL_SOCKET, - SO_SNDBUF, - &size, - sizeof(size))) - goto resend; /* Increased buffer size, retry sending */ - /* Ok, then just try very modest increase */ - size = msg_size; - if (GNUNET_OK == GNUNET_NETWORK_socket_setsockopt (unix_sock, - SOL_SOCKET, - SO_SNDBUF, - &size, - sizeof(size))) - goto resend; /* Increased buffer size, retry sending */ - /* Could not increase buffer size: error, no retry */ - GNUNET_log_strerror (GNUNET_ERROR_TYPE_ERROR, "setsockopt"); - return; - } - - default: - GNUNET_log_strerror (GNUNET_ERROR_TYPE_ERROR, "send"); - return; - } -} - - -/** - * Signature of functions implementing the sending functionality of a - * message queue. - * - * @param mq the message queue - * @param msg the message to send - * @param impl_state our `struct Queue` - */ -static void -mq_send (struct GNUNET_MQ_Handle *mq, - const struct GNUNET_MessageHeader *msg, - void *impl_state) -{ - struct Queue *queue = impl_state; - size_t msize = ntohs (msg->size); - - GNUNET_assert (mq == queue->mq); - GNUNET_assert (NULL == queue->msg); - // Convert to UNIXMessage - queue->msg = GNUNET_malloc (msize + sizeof (struct UNIXMessage)); - queue->msg->header.size = htons (msize + sizeof (struct UNIXMessage)); - queue->msg->sender = my_identity; - memcpy (&queue->msg[1], msg, msize); - GNUNET_CONTAINER_DLL_insert (queue_head, queue_tail, queue); - GNUNET_assert (NULL != unix_sock); - if (NULL == write_task) - write_task = GNUNET_SCHEDULER_add_write_net (GNUNET_TIME_UNIT_FOREVER_REL, - unix_sock, - &select_write_cb, - NULL); -} - - -/** - * Signature of functions implementing the destruction of a message - * queue. Implementations must not free @a mq, but should take care - * of @a impl_state. - * - * @param mq the message queue to destroy - * @param impl_state our `struct Queue` - */ -static void -mq_destroy (struct GNUNET_MQ_Handle *mq, void *impl_state) -{ - struct Queue *queue = impl_state; - - if (mq == queue->mq) - { - queue->mq = NULL; - queue_destroy (queue); - } -} - - -/** - * Implementation function that cancels the currently sent message. - * - * @param mq message queue - * @param impl_state our `struct Queue` - */ -static void -mq_cancel (struct GNUNET_MQ_Handle *mq, void *impl_state) -{ - struct Queue *queue = impl_state; - - GNUNET_assert (NULL != queue->msg); - queue->msg = NULL; - GNUNET_CONTAINER_DLL_remove (queue_head, queue_tail, queue); - GNUNET_assert (NULL != write_task); - if (NULL == queue_head) - { - GNUNET_SCHEDULER_cancel (write_task); - write_task = NULL; - } -} - - -/** - * Generic error handler, called with the appropriate - * error code and the same closure specified at the creation of - * the message queue. - * Not every message queue implementation supports an error handler. - * - * @param cls our `struct Queue` - * @param error error code - */ -static void -mq_error (void *cls, enum GNUNET_MQ_Error error) -{ - struct Queue *queue = cls; - - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - "UNIX MQ error in queue to %s: %d\n", - GNUNET_i2s (&queue->target), - (int) error); - queue_destroy (queue); -} - - -/** - * Creates a new outbound queue the transport service will use to send - * data to another peer. - * - * @param peer the target peer - * @param cs inbound or outbound queue - * @param un the address - * @param un_len number of bytes in @a un - * @return the queue or NULL of max connections exceeded - */ -static struct Queue * -setup_queue (const struct GNUNET_PeerIdentity *target, - enum GNUNET_TRANSPORT_ConnectionStatus cs, - const struct sockaddr_un *un, - socklen_t un_len) -{ - struct Queue *queue; - - queue = GNUNET_new (struct Queue); - queue->target = *target; - queue->address = GNUNET_memdup (un, un_len); - queue->address_len = un_len; - (void) GNUNET_CONTAINER_multipeermap_put ( - queue_map, - &queue->target, - queue, - GNUNET_CONTAINER_MULTIHASHMAPOPTION_MULTIPLE); - GNUNET_STATISTICS_set (stats, - "# queues active", - GNUNET_CONTAINER_multipeermap_size (queue_map), - GNUNET_NO); - queue->timeout = - GNUNET_TIME_relative_to_absolute (GNUNET_CONSTANTS_IDLE_CONNECTION_TIMEOUT); - queue->timeout_task = - GNUNET_SCHEDULER_add_delayed (GNUNET_CONSTANTS_IDLE_CONNECTION_TIMEOUT, - &queue_timeout, - queue); - queue->mq = GNUNET_MQ_queue_for_callbacks (&mq_send, - &mq_destroy, - &mq_cancel, - queue, - NULL, - &mq_error, - queue); - { - char *foreign_addr; - - if ('\0' == un->sun_path[0]) - GNUNET_asprintf (&foreign_addr, - "%s-@%s", - COMMUNICATOR_ADDRESS_PREFIX, - &un->sun_path[1]); - else - GNUNET_asprintf (&foreign_addr, - "%s-%s", - COMMUNICATOR_ADDRESS_PREFIX, - un->sun_path); - queue->qh = GNUNET_TRANSPORT_communicator_mq_add (ch, - &queue->target, - foreign_addr, - UNIX_MTU - sizeof (struct - UNIXMessage), - GNUNET_TRANSPORT_QUEUE_LENGTH_UNLIMITED, - 0, - GNUNET_NT_LOOPBACK, - cs, - queue->mq); - GNUNET_free (foreign_addr); - } - return queue; -} - - -/** - * We have been notified that our socket has something to read. Do the - * read and reschedule this function to be called again once more is - * available. - * - * @param cls NULL - */ -static void -select_read_cb (void *cls); - - -/** - * Function called when message was successfully passed to - * transport service. Continue read activity. - * - * @param cls NULL - * @param success #GNUNET_OK on success - */ -static void -receive_complete_cb (void *cls, int success) -{ - (void) cls; - delivering_messages--; - if (GNUNET_OK != success) - GNUNET_STATISTICS_update (stats, - "# transport transmission failures", - 1, - GNUNET_NO); - if ((NULL == read_task) && (delivering_messages < max_queue_length) && - (NULL != unix_sock)) - read_task = GNUNET_SCHEDULER_add_read_net (GNUNET_TIME_UNIT_FOREVER_REL, - unix_sock, - &select_read_cb, - NULL); -} - - -/** - * We have been notified that our socket has something to read. Do the - * read and reschedule this function to be called again once more is - * available. - * - * @param cls NULL - */ -static void -select_read_cb (void *cls) -{ - char buf[65536] GNUNET_ALIGN; - struct Queue *queue; - const struct UNIXMessage *msg; - struct sockaddr_un un; - socklen_t addrlen; - ssize_t ret; - uint16_t msize; - - GNUNET_assert (NULL != unix_sock); - read_task = GNUNET_SCHEDULER_add_read_net (GNUNET_TIME_UNIT_FOREVER_REL, - unix_sock, - &select_read_cb, - NULL); - addrlen = sizeof(un); - memset (&un, 0, sizeof(un)); - ret = GNUNET_NETWORK_socket_recvfrom (unix_sock, - buf, - sizeof(buf), - (struct sockaddr *) &un, - &addrlen); - if ((-1 == ret) && ((EAGAIN == errno) || (ENOBUFS == errno))) - return; - if (-1 == ret) - { - GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING, "recvfrom"); - return; - } - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "Read %d bytes from socket %s\n", - (int) ret, - un.sun_path); - GNUNET_assert (AF_UNIX == (un.sun_family)); - msg = (struct UNIXMessage *) buf; - msize = ntohs (msg->header.size); - if ((msize < sizeof(struct UNIXMessage)) || (msize > ret)) - { - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - "Wrong message size: %d bytes\n", - msize); - GNUNET_break_op (0); - return; - } - queue = lookup_queue (&msg->sender, &un, addrlen); - if (NULL == queue) - queue = - setup_queue (&msg->sender, GNUNET_TRANSPORT_CS_INBOUND, &un, addrlen); - else - reschedule_queue_timeout (queue); - if (NULL == queue) - { - GNUNET_log ( - GNUNET_ERROR_TYPE_ERROR, - _ ( - "Maximum number of UNIX connections exceeded, dropping incoming message\n")); - return; - } - - { - uint16_t tsize = msize - sizeof(struct UNIXMessage); - - const struct GNUNET_MessageHeader *currhdr; - struct GNUNET_MessageHeader al_hdr; - - currhdr = (const struct GNUNET_MessageHeader *) &msg[1]; - /* ensure aligned access */ - memcpy (&al_hdr, currhdr, sizeof(al_hdr)); - if ((tsize < sizeof(struct GNUNET_MessageHeader)) || - (tsize != ntohs (al_hdr.size))) - { - GNUNET_break_op (0); - return; - } - ret = GNUNET_TRANSPORT_communicator_receive (ch, - &msg->sender, - currhdr, - GNUNET_TIME_UNIT_FOREVER_REL, - &receive_complete_cb, - NULL); - if (GNUNET_SYSERR == ret) - { - GNUNET_log (GNUNET_ERROR_TYPE_WARNING, - "Transport not up!\n"); - return; /* transport not up */ - } - if (GNUNET_NO == ret) - { - GNUNET_log (GNUNET_ERROR_TYPE_WARNING, - "Error sending message to transport\n"); - return; - } - delivering_messages++; - } - if (delivering_messages >= max_queue_length) - { - GNUNET_log (GNUNET_ERROR_TYPE_WARNING, - "Back pressure %llu\n", delivering_messages); - - /* we should try to apply 'back pressure' */ - GNUNET_SCHEDULER_cancel (read_task); - read_task = NULL; - } -} - - -/** - * Function called by the transport service to initialize a - * message queue given address information about another peer. - * If and when the communication channel is established, the - * communicator must call #GNUNET_TRANSPORT_communicator_mq_add() - * to notify the service that the channel is now up. It is - * the responsibility of the communicator to manage sane - * retries and timeouts for any @a peer/@a address combination - * provided by the transport service. Timeouts and retries - * do not need to be signalled to the transport service. - * - * @param cls closure - * @param peer identity of the other peer - * @param address where to send the message, human-readable - * communicator-specific format, 0-terminated, UTF-8 - * @return #GNUNET_OK on success, #GNUNET_SYSERR if the provided address is invalid - */ -static int -mq_init (void *cls, const struct GNUNET_PeerIdentity *peer, const char *address) -{ - struct Queue *queue; - const char *path; - struct sockaddr_un *un; - socklen_t un_len; - - (void) cls; - if (0 != strncmp (address, - COMMUNICATOR_ADDRESS_PREFIX "-", - strlen (COMMUNICATOR_ADDRESS_PREFIX "-"))) - { - GNUNET_break_op (0); - return GNUNET_SYSERR; - } - path = &address[strlen (COMMUNICATOR_ADDRESS_PREFIX "-")]; - un = unix_address_to_sockaddr (path, &un_len); - queue = lookup_queue (peer, un, un_len); - if (NULL != queue) - { - GNUNET_log (GNUNET_ERROR_TYPE_INFO, - "Address `%s' for %s ignored, queue exists\n", - path, - GNUNET_i2s (peer)); - GNUNET_free (un); - return GNUNET_OK; - } - queue = setup_queue (peer, GNUNET_TRANSPORT_CS_OUTBOUND, un, un_len); - GNUNET_free (un); - if (NULL == queue) - { - GNUNET_log (GNUNET_ERROR_TYPE_INFO, - "Failed to setup queue to %s at `%s'\n", - GNUNET_i2s (peer), - path); - return GNUNET_NO; - } - return GNUNET_OK; -} - - -/** - * Iterator over all message queues to clean up. - * - * @param cls NULL - * @param target unused - * @param value the queue to destroy - * @return #GNUNET_OK to continue to iterate - */ -static int -get_queue_delete_it (void *cls, - const struct GNUNET_PeerIdentity *target, - void *value) -{ - struct Queue *queue = value; - - (void) cls; - (void) target; - queue_destroy (queue); - return GNUNET_OK; -} - - -/** - * Shutdown the UNIX communicator. - * - * @param cls NULL (always) - */ -static void -do_shutdown (void *cls) -{ - if (NULL != read_task) - { - GNUNET_SCHEDULER_cancel (read_task); - read_task = NULL; - } - if (NULL != write_task) - { - GNUNET_SCHEDULER_cancel (write_task); - write_task = NULL; - } - if (NULL != unix_sock) - { - GNUNET_break (GNUNET_OK == GNUNET_NETWORK_socket_close (unix_sock)); - unix_sock = NULL; - } - GNUNET_CONTAINER_multipeermap_iterate (queue_map, &get_queue_delete_it, NULL); - GNUNET_CONTAINER_multipeermap_destroy (queue_map); - if (NULL != ai) - { - GNUNET_TRANSPORT_communicator_address_remove (ai); - ai = NULL; - } - if (NULL != ch) - { - GNUNET_TRANSPORT_communicator_disconnect (ch); - ch = NULL; - } - if (NULL != stats) - { - GNUNET_STATISTICS_destroy (stats, GNUNET_NO); - stats = NULL; - } -} - - -/** - * Function called when the transport service has received an - * acknowledgement for this communicator (!) via a different return - * path. - * - * Not applicable for UNIX. - * - * @param cls closure - * @param sender which peer sent the notification - * @param msg payload - */ -static void -enc_notify_cb (void *cls, - const struct GNUNET_PeerIdentity *sender, - const struct GNUNET_MessageHeader *msg) -{ - (void) cls; - (void) sender; - (void) msg; - GNUNET_break_op (0); -} - - -/** - * Setup communicator and launch network interactions. - * - * @param cls NULL (always) - * @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, - const char *cfgfile, - const struct GNUNET_CONFIGURATION_Handle *cfg) -{ - char *unix_socket_path; - struct sockaddr_un *un; - socklen_t un_len; - char *my_addr; - struct GNUNET_CRYPTO_EddsaPrivateKey *my_private_key; - - (void) cls; - delivering_messages = 0; - - my_private_key = GNUNET_CRYPTO_eddsa_key_create_from_configuration (cfg); - if (NULL == my_private_key) - { - GNUNET_log ( - GNUNET_ERROR_TYPE_ERROR, - _ ( - "UNIX communicator is lacking key configuration settings. Exiting.\n")); - GNUNET_SCHEDULER_shutdown (); - return; - } - GNUNET_CRYPTO_eddsa_key_get_public (my_private_key, &my_identity.public_key); - - if (GNUNET_OK != - GNUNET_CONFIGURATION_get_value_filename (cfg, - COMMUNICATOR_CONFIG_SECTION, - "UNIXPATH", - &unix_socket_path)) - { - GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR, - COMMUNICATOR_CONFIG_SECTION, - "UNIXPATH"); - return; - } - if (GNUNET_OK != - GNUNET_CONFIGURATION_get_value_number (cfg, - COMMUNICATOR_CONFIG_SECTION, - "MAX_QUEUE_LENGTH", - &max_queue_length)) - max_queue_length = DEFAULT_MAX_QUEUE_LENGTH; - - un = unix_address_to_sockaddr (unix_socket_path, &un_len); - if (NULL == un) - { - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - "Failed to setup UNIX domain socket address with path `%s'\n", - unix_socket_path); - GNUNET_free (unix_socket_path); - return; - } - unix_sock = GNUNET_NETWORK_socket_create (AF_UNIX, SOCK_DGRAM, 0); - if (NULL == unix_sock) - { - GNUNET_log_strerror (GNUNET_ERROR_TYPE_ERROR, "socket"); - GNUNET_free (un); - GNUNET_free (unix_socket_path); - return; - } - if (('\0' != un->sun_path[0]) && - (GNUNET_OK != GNUNET_DISK_directory_create_for_file (un->sun_path))) - { - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - _ ("Cannot create path to `%s'\n"), - un->sun_path); - GNUNET_NETWORK_socket_close (unix_sock); - unix_sock = NULL; - GNUNET_free (un); - GNUNET_free (unix_socket_path); - return; - } - if (GNUNET_OK != GNUNET_NETWORK_socket_bind (unix_sock, - (const struct sockaddr *) un, - un_len)) - { - GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_ERROR, "bind", un->sun_path); - GNUNET_NETWORK_socket_close (unix_sock); - unix_sock = NULL; - GNUNET_free (un); - GNUNET_free (unix_socket_path); - return; - } - GNUNET_free (un); - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Bound to `%s'\n", unix_socket_path); - stats = GNUNET_STATISTICS_create ("C-UNIX", cfg); - GNUNET_SCHEDULER_add_shutdown (&do_shutdown, NULL); - read_task = GNUNET_SCHEDULER_add_read_net (GNUNET_TIME_UNIT_FOREVER_REL, - unix_sock, - &select_read_cb, - NULL); - queue_map = GNUNET_CONTAINER_multipeermap_create (10, GNUNET_NO); - ch = GNUNET_TRANSPORT_communicator_connect (cfg, - COMMUNICATOR_CONFIG_SECTION, - COMMUNICATOR_ADDRESS_PREFIX, - GNUNET_TRANSPORT_CC_RELIABLE, - &mq_init, - NULL, - &enc_notify_cb, - NULL); - if (NULL == ch) - { - GNUNET_break (0); - GNUNET_SCHEDULER_shutdown (); - GNUNET_free (unix_socket_path); - return; - } - GNUNET_asprintf (&my_addr, - "%s-%s", - COMMUNICATOR_ADDRESS_PREFIX, - unix_socket_path); - GNUNET_free (unix_socket_path); - ai = GNUNET_TRANSPORT_communicator_address_add (ch, - my_addr, - GNUNET_NT_LOOPBACK, - GNUNET_TIME_UNIT_FOREVER_REL); - GNUNET_free (my_addr); -} - - -/** - * The main function for the UNIX communicator. - * - * @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 - }; - int ret; - - if (GNUNET_OK != GNUNET_STRINGS_get_utf8_args (argc, argv, &argc, &argv)) - return 2; - - ret = (GNUNET_OK == - GNUNET_PROGRAM_run (argc, - argv, - "gnunet-communicator-unix", - _ ("GNUnet UNIX domain socket communicator"), - options, - &run, - NULL)) - ? 0 - : 1; - GNUNET_free_nz ((void *) argv); - return ret; -} - - -#if defined(__linux__) && defined(__GLIBC__) -#include - -/** - * 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-communicator-unix.c */ diff --git a/src/transport/gnunet-service-transport.c b/src/transport/gnunet-service-transport.c deleted file mode 100644 index ec3019161..000000000 --- a/src/transport/gnunet-service-transport.c +++ /dev/null @@ -1,11750 +0,0 @@ -/* - This file is part of GNUnet. - Copyright (C) 2010-2016, 2018, 2019 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 . - - SPDX-License-Identifier: AGPL3.0-or-later - */ -/** - * @file transport/gnunet-service-transport.c - * @brief main for gnunet-service-transport - * @author Christian Grothoff - * - * TODO: - * Implement next: - * - review retransmission logic, right now there is no smartness there! - * => congestion control, etc [PERFORMANCE-BASICS] - * - * Optimizations-Statistics: - * - Track ACK losses based on ACK-counter [ROUTING] - * - Need to track total bandwidth per VirtualLink and adjust how frequently - * we send FC messages based on bandwidth-delay-product (and relation - * to the window size!). See OPTIMIZE-FC-BDP. - * - Consider more statistics in #check_connection_quality() [FIXME-CONQ-STATISTICS] - * - Adapt available_fc_window_size, using larger values for high-bandwidth - * and high-latency links *if* we have the RAM [GOODPUT / utilization / stalls] - * - Set last_window_consum_limit promise properly based on - * latency and bandwidth of the respective connection [GOODPUT / utilization / stalls] - * - * Optimizations-DV: - * - When forwarding DV learn messages, if a peer is reached that - * has a *bidirectional* link to the origin beyond 1st hop, - * do NOT forward it to peers _other_ than the origin, as - * there is clearly a better path directly from the origin to - * whatever else we could reach. - * - When we passively learned DV (with unconfirmed freshness), we - * right now add the path to our list but with a zero path_valid_until - * time and only use it for unconfirmed routes. However, we could consider - * triggering an explicit validation mechanism ourselves, specifically routing - * a challenge-response message over the path [ROUTING] - * = if available, try to confirm unconfirmed DV paths when trying to establish - * virtual link for a `struct IncomingRequest`. (i.e. if DVH is - * unconfirmed, incoming requests cause us to try to validate a passively - * learned path (requires new message type!)) - * - * Optimizations-Fragmentation: - * - Fragments send over a reliable channel could do without the - * AcknowledgementUUIDP altogether, as they won't be acked! [BANDWIDTH] - * (-> have 2nd type of acknowledgment message; low priority, as we - * do not have an MTU-limited *reliable* communicator) [FIXME-FRAG-REL-UUID] - * - if messages are below MTU, consider adding ACKs and other stuff - * to the same transmission to avoid tiny messages (requires planning at - * receiver, and additional MST-style demultiplex at receiver!) [PACKET COUNT] - * - * Optimizations-internals: - * - queue_send_msg by API design has to make a copy - * of the payload, and route_message on top of that requires a malloc/free. - * Change design to approximate "zero" copy better... [CPU] - * - could avoid copying body of message into each fragment and keep - * fragments as just pointers into the original message and only - * fully build fragments just before transmission (optimization, should - * reduce CPU and memory use) [CPU, MEMORY] - */ -#include "platform.h" -#include "gnunet_util_lib.h" -#include "gnunet_statistics_service.h" -#include "gnunet_transport_monitor_service.h" -#include "gnunet_peerstore_service.h" -#include "gnunet_hello_lib.h" -#include "gnunet_hello_uri_lib.h" -#include "gnunet_signatures.h" -#include "transport.h" - -/** - * Size of ring buffer to cache CORE and forwarded DVBox messages. - */ -#define RING_BUFFER_SIZE 16 - -/** - * Maximum number of FC retransmissions for a running retransmission task. - */ -#define MAX_FC_RETRANSMIT_COUNT 1000 - -/** - * Maximum number of messages we acknowledge together in one - * cumulative ACK. Larger values may save a bit of bandwidth. - */ -#define MAX_CUMMULATIVE_ACKS 64 - -/** - * What is the 1:n chance that we send a Flow control response when - * receiving a flow control message that did not change anything for - * us? Basically, this is used in the case where both peers are stuck - * on flow control (no window changes), but one might continue sending - * flow control messages to the other peer as the first FC message - * when things stalled got lost, and then subsequently the other peer - * does *usually* not respond as nothing changed. So to ensure that - * eventually the FC messages stop, we do send with 1/8th probability - * an FC message even if nothing changed. That prevents one peer - * being stuck in sending (useless) FC messages "forever". - */ -#define FC_NO_CHANGE_REPLY_PROBABILITY 8 - -/** - * What is the size we assume for a read operation in the - * absence of an MTU for the purpose of flow control? - */ -#define IN_PACKET_SIZE_WITHOUT_MTU 128 - -/** - * Number of slots we keep of historic data for computation of - * goodput / message loss ratio. - */ -#define GOODPUT_AGING_SLOTS 4 - -/** - * How big is the flow control window size by default; - * limits per-neighbour RAM utilization. - */ -#define DEFAULT_WINDOW_SIZE (128 * 1024) - -/** - * For how many incoming connections do we try to create a - * virtual link for (at the same time!). This does NOT - * limit the number of incoming connections, just the number - * for which we are actively trying to find working addresses - * in the absence (!) of our own applications wanting the - * link to go up. - */ -#define MAX_INCOMING_REQUEST 16 - -/** - * Maximum number of peers we select for forwarding DVInit - * messages at the same time (excluding initiator). - */ -#define MAX_DV_DISCOVERY_SELECTION 16 - -/** - * Window size. How many messages to the same target do we pass - * to CORE without a RECV_OK in between? Small values limit - * thoughput, large values will increase latency. - * - * FIXME-OPTIMIZE: find out what good values are experimentally, - * maybe set adaptively (i.e. to observed available bandwidth). - */ -#define RECV_WINDOW_SIZE 4 - -/** - * Minimum number of hops we should forward DV learn messages - * even if they are NOT useful for us in hope of looping - * back to the initiator? - * - * FIXME: allow initiator some control here instead? - */ -#define MIN_DV_PATH_LENGTH_FOR_INITIATOR 3 - -/** - * Maximum DV distance allowed ever. - */ -#define MAX_DV_HOPS_ALLOWED 16 - -/** - * Maximum number of DV learning activities we may - * have pending at the same time. - */ -#define MAX_DV_LEARN_PENDING 64 - -/** - * Maximum number of DV paths we keep simultaneously to the same target. - */ -#define MAX_DV_PATHS_TO_TARGET 3 - -/** - * If a queue delays the next message by more than this number - * of seconds we log a warning. Note: this is for testing, - * the value chosen here might be too aggressively low! - */ -#define DELAY_WARN_THRESHOLD \ - GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS, 5) - -/** - * If a DVBox could not be forwarded after this number of - * seconds we drop it. - */ -#define DV_FORWARD_TIMEOUT \ - GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS, 60) - -/** - * Default value for how long we wait for reliability ack. - */ -#define DEFAULT_ACK_WAIT_DURATION \ - GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS, 1) - -/** - * We only consider queues as "quality" connections when - * suppressing the generation of DV initiation messages if - * the latency of the queue is below this threshold. - */ -#define DV_QUALITY_RTT_THRESHOLD \ - GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS, 1) - -/** - * How long do we consider a DV path valid if we see no - * further updates on it? Note: the value chosen here might be too low! - */ -#define DV_PATH_VALIDITY_TIMEOUT \ - GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_MINUTES, 5) - -/** - * How long do we cache backchannel (struct Backtalker) information - * after a backchannel goes inactive? - */ -#define BACKCHANNEL_INACTIVITY_TIMEOUT \ - GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_MINUTES, 5) - -/** - * How long before paths expire would we like to (re)discover DV paths? Should - * be below #DV_PATH_VALIDITY_TIMEOUT. - */ -#define DV_PATH_DISCOVERY_FREQUENCY \ - GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_MINUTES, 4) - -/** - * How long are ephemeral keys valid? - */ -#define EPHEMERAL_VALIDITY \ - GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_HOURS, 4) - -/** - * How long do we keep partially reassembled messages around before giving up? - */ -#define REASSEMBLY_EXPIRATION \ - GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_MINUTES, 4) - -/** - * What is the fastest rate at which we send challenges *if* we keep learning - * an address (gossip, DHT, etc.)? - */ -#define FAST_VALIDATION_CHALLENGE_FREQ \ - GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_MINUTES, 1) - -/** - * What is the slowest rate at which we send challenges? - */ -#define MAX_VALIDATION_CHALLENGE_FREQ \ - GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_DAYS, 1) - -/** - * How long until we forget about historic accumulators and thus - * reset the ACK counter? Should exceed the maximum time an - * active connection experiences without an ACK. - */ -#define ACK_CUMMULATOR_TIMEOUT \ - GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_HOURS, 4) - -/** - * What is the non-randomized base frequency at which we - * would initiate DV learn messages? - */ -#define DV_LEARN_BASE_FREQUENCY GNUNET_TIME_UNIT_MINUTES - -/** - * How many good connections (confirmed, bi-directional, not DV) - * do we need to have to suppress initiating DV learn messages? - */ -#define DV_LEARN_QUALITY_THRESHOLD 100 - -/** - * When do we forget an invalid address for sure? - */ -#define MAX_ADDRESS_VALID_UNTIL \ - GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_MONTHS, 1) - -/** - * How long do we consider an address valid if we just checked? - */ -#define ADDRESS_VALIDATION_LIFETIME \ - GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_HOURS, 4) - -/** - * What is the maximum frequency at which we do address validation? - * A random value between 0 and this value is added when scheduling - * the #validation_task (both to ensure we do not validate too often, - * and to randomize a bit). - */ -#define MIN_DELAY_ADDRESS_VALIDATION GNUNET_TIME_UNIT_MILLISECONDS - -/** - * How many network RTTs before an address validation expires should we begin - * trying to revalidate? (Note that the RTT used here is the one that we - * experienced during the last validation, not necessarily the latest RTT - * observed). - */ -#define VALIDATION_RTT_BUFFER_FACTOR 3 - -/** - * How many messages can we have pending for a given communicator - * process before we start to throttle that communicator? - * - * Used if a communicator might be CPU-bound and cannot handle the traffic. - */ -#define COMMUNICATOR_TOTAL_QUEUE_LIMIT 512 - -/** - * How many messages can we have pending for a given queue (queue to - * a particular peer via a communicator) process before we start to - * throttle that queue? - */ -#define QUEUE_LENGTH_LIMIT 32 - - -GNUNET_NETWORK_STRUCT_BEGIN - -/** - * Unique identifier we attach to a message. - */ -struct MessageUUIDP -{ - /** - * Unique value, generated by incrementing the - * `message_uuid_ctr` of `struct Neighbour`. - */ - uint64_t uuid GNUNET_PACKED; -}; - - -/** - * Unique identifier to map an acknowledgement to a transmission. - */ -struct AcknowledgementUUIDP -{ - /** - * The UUID value. - */ - struct GNUNET_Uuid value; -}; - -/** - * Outer layer of an encapsulated backchannel message. - */ -struct TransportBackchannelEncapsulationMessage -{ - /** - * Type is #GNUNET_MESSAGE_TYPE_TRANSPORT_BACKCHANNEL_ENCAPSULATION. - */ - struct GNUNET_MessageHeader header; - - /* Followed by *another* message header which is the message to - the communicator */ - - /* Followed by a 0-terminated name of the communicator */ -}; - - -/** - * Body by which a peer confirms that it is using an ephemeral key. - */ -struct EphemeralConfirmationPS -{ - /** - * Purpose is #GNUNET_SIGNATURE_PURPOSE_TRANSPORT_EPHEMERAL - */ - struct GNUNET_CRYPTO_EccSignaturePurpose purpose; - - /** - * How long is this signature over the ephemeral key valid? - * - * Note that the receiver MUST IGNORE the absolute time, and only interpret - * the value as a mononic time and reject "older" values than the last one - * observed. This is necessary as we do not want to require synchronized - * clocks and may not have a bidirectional communication channel. - * - * Even with this, there is no real guarantee against replay achieved here, - * unless the latest timestamp is persisted. While persistence should be - * provided via PEERSTORE, we do not consider the mechanism reliable! Thus, - * communicators must protect against replay attacks when using backchannel - * communication! - */ - struct GNUNET_TIME_AbsoluteNBO sender_monotonic_time; - - /** - * Target's peer identity. - */ - struct GNUNET_PeerIdentity target; - - /** - * Ephemeral key setup by the sender for @e target, used - * to encrypt the payload. - */ - struct GNUNET_CRYPTO_EcdhePublicKey ephemeral_key; -}; - - -/** - * Plaintext of the variable-size payload that is encrypted - * within a `struct TransportBackchannelEncapsulationMessage` - */ -struct TransportDVBoxPayloadP -{ - /** - * Sender's peer identity. - */ - struct GNUNET_PeerIdentity sender; - - /** - * Signature of the sender over an - * #GNUNET_SIGNATURE_PURPOSE_TRANSPORT_EPHEMERAL. - */ - struct GNUNET_CRYPTO_EddsaSignature sender_sig; - - /** - * Current monotonic time of the sending transport service. Used to - * detect replayed messages. Note that the receiver should remember - * a list of the recently seen timestamps and only reject messages - * if the timestamp is in the list, or the list is "full" and the - * timestamp is smaller than the lowest in the list. - * - * Like the @e ephemeral_validity, the list of timestamps per peer should be - * persisted to guard against replays after restarts. - */ - struct GNUNET_TIME_AbsoluteNBO monotonic_time; - - /* Followed by a `struct GNUNET_MessageHeader` with a message - for the target peer */ -}; - - -/** - * Outer layer of an encapsulated unfragmented application message sent - * over an unreliable channel. - */ -struct TransportReliabilityBoxMessage -{ - /** - * Type is #GNUNET_MESSAGE_TYPE_TRANSPORT_RELIABILITY_BOX - */ - struct GNUNET_MessageHeader header; - - /** - * Number of messages still to be sent before a commulative - * ACK is requested. Zero if an ACK is requested immediately. - * In NBO. Note that the receiver may send the ACK faster - * if it believes that is reasonable. - */ - uint32_t ack_countdown GNUNET_PACKED; - - /** - * Unique ID of the message used for signalling receipt of - * messages sent over possibly unreliable channels. Should - * be a random. - */ - struct AcknowledgementUUIDP ack_uuid; -}; - - -/** - * Acknowledgement payload. - */ -struct TransportCummulativeAckPayloadP -{ - /** - * How long was the ACK delayed for generating cumulative ACKs? - * Used to calculate the correct network RTT by taking the receipt - * time of the ack minus the transmission time of the sender minus - * this value. - */ - struct GNUNET_TIME_RelativeNBO ack_delay; - - /** - * UUID of a message being acknowledged. - */ - struct AcknowledgementUUIDP ack_uuid; -}; - - -/** - * Confirmation that the receiver got a - * #GNUNET_MESSAGE_TYPE_TRANSPORT_RELIABILITY_BOX. Note that the - * confirmation may be transmitted over a completely different queue, - * so ACKs are identified by a combination of PID of sender and - * message UUID, without the queue playing any role! - */ -struct TransportReliabilityAckMessage -{ - /** - * Type is #GNUNET_MESSAGE_TYPE_TRANSPORT_RELIABILITY_ACK - */ - struct GNUNET_MessageHeader header; - - /** - * Counter of ACKs transmitted by the sender to us. Incremented - * by one for each ACK, used to detect how many ACKs were lost. - */ - uint32_t ack_counter GNUNET_PACKED; - - /* followed by any number of `struct TransportCummulativeAckPayloadP` - messages providing ACKs */ -}; - - -/** - * Outer layer of an encapsulated fragmented application message. - */ -struct TransportFragmentBoxMessage -{ - /** - * Type is #GNUNET_MESSAGE_TYPE_TRANSPORT_FRAGMENT - */ - struct GNUNET_MessageHeader header; - - /** - * Offset of this fragment in the overall message. - */ - uint16_t frag_off GNUNET_PACKED; - - /** - * Total size of the message that is being fragmented. - */ - uint16_t msg_size GNUNET_PACKED; - - /** - * Unique ID of this fragment (and fragment transmission!). Will - * change even if a fragment is retransmitted to make each - * transmission attempt unique! If a client receives a duplicate - * fragment (same @e frag_off for same @a msg_uuid, it must send - * #GNUNET_MESSAGE_TYPE_TRANSPORT_RELIABILITY_ACK immediately. - */ - struct AcknowledgementUUIDP ack_uuid; - - /** - * Original message ID for of the message that all the fragments - * belong to. Must be the same for all fragments. - */ - struct MessageUUIDP msg_uuid; -}; - - -/** - * Content signed by the initator during DV learning. - * - * The signature is required to prevent DDoS attacks. A peer sending out this - * message is potentially generating a lot of traffic that will go back to the - * initator, as peers receiving this message will try to let the initiator - * know that they got the message. - * - * Without this signature, an attacker could abuse this mechanism for traffic - * amplification, sending a lot of traffic to a peer by putting out this type - * of message with the victim's peer identity. - * - * Even with just a signature, traffic amplification would be possible via - * replay attacks. The @e monotonic_time limits such replay attacks, as every - * potential amplificator will check the @e monotonic_time and only respond - * (at most) once per message. - */ -struct DvInitPS -{ - /** - * Purpose is #GNUNET_SIGNATURE_PURPOSE_TRANSPORT_DV_INITIATOR - */ - struct GNUNET_CRYPTO_EccSignaturePurpose purpose; - - /** - * Time at the initiator when generating the signature. - * - * Note that the receiver MUST IGNORE the absolute time, and only interpret - * the value as a mononic time and reject "older" values than the last one - * observed. This is necessary as we do not want to require synchronized - * clocks and may not have a bidirectional communication channel. - * - * Even with this, there is no real guarantee against replay achieved here, - * unless the latest timestamp is persisted. Persistence should be - * provided via PEERSTORE if possible. - */ - struct GNUNET_TIME_AbsoluteNBO monotonic_time; - - /** - * Challenge value used by the initiator to re-identify the path. - */ - struct GNUNET_CRYPTO_ChallengeNonceP challenge; -}; - - -/** - * Content signed by each peer during DV learning. - * - * This assues the initiator of the DV learning operation that the hop from @e - * pred via the signing peer to @e succ actually exists. This makes it - * impossible for an adversary to supply the network with bogus routes. - * - * The @e challenge is included to provide replay protection for the - * initiator. This way, the initiator knows that the hop existed after the - * original @e challenge was first transmitted, providing a freshness metric. - * - * Peers other than the initiator that passively learn paths by observing - * these messages do NOT benefit from this. Here, an adversary may indeed - * replay old messages. Thus, passively learned paths should always be - * immediately marked as "potentially stale". - */ -struct DvHopPS -{ - /** - * Purpose is #GNUNET_SIGNATURE_PURPOSE_TRANSPORT_DV_HOP - */ - struct GNUNET_CRYPTO_EccSignaturePurpose purpose; - - /** - * Identity of the previous peer on the path. - */ - struct GNUNET_PeerIdentity pred; - - /** - * Identity of the next peer on the path. - */ - struct GNUNET_PeerIdentity succ; - - /** - * Challenge value used by the initiator to re-identify the path. - */ - struct GNUNET_CRYPTO_ChallengeNonceP challenge; -}; - - -/** - * An entry describing a peer on a path in a - * `struct TransportDVLearnMessage` message. - */ -struct DVPathEntryP -{ - /** - * Identity of a peer on the path. - */ - struct GNUNET_PeerIdentity hop; - - /** - * Signature of this hop over the path, of purpose - * #GNUNET_SIGNATURE_PURPOSE_TRANSPORT_DV_HOP - */ - struct GNUNET_CRYPTO_EddsaSignature hop_sig; -}; - - -/** - * Internal message used by transport for distance vector learning. - * If @e num_hops does not exceed the threshold, peers should append - * themselves to the peer list and flood the message (possibly only - * to a subset of their neighbours to limit discoverability of the - * network topology). To the extend that the @e bidirectional bits - * are set, peers may learn the inverse paths even if they did not - * initiate. - * - * Unless received on a bidirectional queue and @e num_hops just - * zero, peers that can forward to the initator should always try to - * forward to the initiator. - */ -struct TransportDVLearnMessage -{ - /** - * Type is #GNUNET_MESSAGE_TYPE_TRANSPORT_DV_LEARN - */ - struct GNUNET_MessageHeader header; - - /** - * Number of hops this messages has travelled, in NBO. Zero if - * sent by initiator. - */ - uint16_t num_hops GNUNET_PACKED; - - /** - * Bitmask of the last 16 hops indicating whether they are confirmed - * available (without DV) in both directions or not, in NBO. Used - * to possibly instantly learn a path in both directions. Each peer - * should shift this value by one to the left, and then set the - * lowest bit IF the current sender can be reached from it (without - * DV routing). - */ - uint16_t bidirectional GNUNET_PACKED; - - /** - * Peers receiving this message and delaying forwarding to other - * peers for any reason should increment this value by the non-network - * delay created by the peer. - */ - struct GNUNET_TIME_RelativeNBO non_network_delay; - - /** - * Time at the initiator when generating the signature. - * - * Note that the receiver MUST IGNORE the absolute time, and only interpret - * the value as a mononic time and reject "older" values than the last one - * observed. This is necessary as we do not want to require synchronized - * clocks and may not have a bidirectional communication channel. - * - * Even with this, there is no real guarantee against replay achieved here, - * unless the latest timestamp is persisted. Persistence should be - * provided via PEERSTORE if possible. - */ - struct GNUNET_TIME_AbsoluteNBO monotonic_time; - - /** - * Signature of this hop over the path, of purpose - * #GNUNET_SIGNATURE_PURPOSE_TRANSPORT_DV_INITIATOR - */ - struct GNUNET_CRYPTO_EddsaSignature init_sig; - - /** - * Identity of the peer that started this learning activity. - */ - struct GNUNET_PeerIdentity initiator; - - /** - * Challenge value used by the initiator to re-identify the path. - */ - struct GNUNET_CRYPTO_ChallengeNonceP challenge; - - /* Followed by @e num_hops `struct DVPathEntryP` values, - excluding the initiator of the DV trace; the last entry is the - current sender; the current peer must not be included. */ -}; - - -/** - * Outer layer of an encapsulated message send over multiple hops. - * The path given only includes the identities of the subsequent - * peers, i.e. it will be empty if we are the receiver. Each - * forwarding peer should scan the list from the end, and if it can, - * forward to the respective peer. The list should then be shortened - * by all the entries up to and including that peer. Each hop should - * also increment @e total_hops to allow the receiver to get a precise - * estimate on the number of hops the message travelled. Senders must - * provide a learned path that thus should work, but intermediaries - * know of a shortcut, they are allowed to send the message via that - * shortcut. - * - * If a peer finds itself still on the list, it must drop the message. - * - * The payload of the box can only be decrypted and verified by the - * ultimate receiver. Intermediaries do not learn the sender's - * identity and the path the message has taken. However, the first - * hop does learn the sender as @e total_hops would be zero and thus - * the predecessor must be the origin (so this is not really useful - * for anonymization). - */ -struct TransportDVBoxMessage -{ - /** - * Type is #GNUNET_MESSAGE_TYPE_TRANSPORT_DV_BOX - */ - struct GNUNET_MessageHeader header; - - /** - * Flag if the payload is a control message. In NBO. - */ - unsigned int without_fc; - - /** - * Number of total hops this messages travelled. In NBO. - * @e origin sets this to zero, to be incremented at - * each hop. Peers should limit the @e total_hops value - * they accept from other peers. - */ - uint16_t total_hops GNUNET_PACKED; - - /** - * Number of hops this messages includes. In NBO. Reduced by one - * or more at each hop. Peers should limit the @e num_hops value - * they accept from other peers. - */ - uint16_t num_hops GNUNET_PACKED; - - /** - * Ephemeral key setup by the sender for target, used to encrypt the - * payload. Intermediaries must not change this value. - */ - struct GNUNET_CRYPTO_EcdhePublicKey ephemeral_key; - - /** - * We use an IV here as the @e ephemeral_key is re-used for - * #EPHEMERAL_VALIDITY time to avoid re-signing it all the time. - * Intermediaries must not change this value. - */ - struct GNUNET_ShortHashCode iv; - - /** - * HMAC over the ciphertext of the encrypted, variable-size body - * that follows. Verified via DH of target and @e ephemeral_key. - * Intermediaries must not change this value. - */ - struct GNUNET_HashCode hmac; - - /** - * Size this msg had initially. This is needed to calculate the hmac at the target. - * The header size can not be used for that, because the box size is getting smaller at each hop. - * - */ - uint16_t orig_size GNUNET_PACKED; - - /* Followed by @e num_hops `struct GNUNET_PeerIdentity` values; - excluding the @e origin and the current peer, the last must be - the ultimate target; if @e num_hops is zero, the receiver of this - message is the ultimate target. */ - - /* Followed by encrypted, variable-size payload, which - must begin with a `struct TransportDVBoxPayloadP` */ - - /* Followed by the actual message, which itself must not be a - a DV_LEARN or DV_BOX message! */ -}; - - -/** - * Message send to another peer to validate that it can indeed - * receive messages at a particular address. - */ -struct TransportValidationChallengeMessage -{ - /** - * Type is #GNUNET_MESSAGE_TYPE_TRANSPORT_ADDRESS_VALIDATION_CHALLENGE - */ - struct GNUNET_MessageHeader header; - - /** - * Always zero. - */ - uint32_t reserved GNUNET_PACKED; - - /** - * Challenge to be signed by the receiving peer. - */ - struct GNUNET_CRYPTO_ChallengeNonceP challenge; - - /** - * Timestamp of the sender, to be copied into the reply to allow - * sender to calculate RTT. Must be monotonically increasing! - */ - struct GNUNET_TIME_AbsoluteNBO sender_time; -}; - - -/** - * Message signed by a peer to confirm that it can indeed - * receive messages at a particular address. - */ -struct TransportValidationPS -{ - /** - * Purpose is #GNUNET_SIGNATURE_PURPOSE_TRANSPORT_CHALLENGE - */ - struct GNUNET_CRYPTO_EccSignaturePurpose purpose; - - /** - * How long does the sender believe the address on - * which the challenge was received to remain valid? - */ - struct GNUNET_TIME_RelativeNBO validity_duration; - - /** - * Challenge signed by the receiving peer. - */ - struct GNUNET_CRYPTO_ChallengeNonceP challenge; -}; - - -/** - * Message send to a peer to respond to a - * #GNUNET_MESSAGE_TYPE_ADDRESS_VALIDATION_CHALLENGE - */ -struct TransportValidationResponseMessage -{ - /** - * Type is #GNUNET_MESSAGE_TYPE_TRANSPORT_ADDRESS_VALIDATION_RESPONSE - */ - struct GNUNET_MessageHeader header; - - /** - * Always zero. - */ - uint32_t reserved GNUNET_PACKED; - - /** - * The peer's signature matching the - * #GNUNET_SIGNATURE_PURPOSE_TRANSPORT_CHALLENGE purpose. - */ - struct GNUNET_CRYPTO_EddsaSignature signature; - - /** - * The challenge that was signed by the receiving peer. - */ - struct GNUNET_CRYPTO_ChallengeNonceP challenge; - - /** - * Original timestamp of the sender (was @code{sender_time}), - * copied into the reply to allow sender to calculate RTT. - */ - struct GNUNET_TIME_AbsoluteNBO origin_time; - - /** - * How long does the sender believe this address to remain - * valid? - */ - struct GNUNET_TIME_RelativeNBO validity_duration; -}; - - -/** - * Message for Transport-to-Transport Flow control. Specifies the size - * of the flow control window, including how much we believe to have - * consumed (at transmission time), how much we believe to be allowed - * (at transmission time), and how much the other peer is allowed to - * send to us, and how much data we already received from the other - * peer. - */ -struct TransportFlowControlMessage -{ - /** - * Type is #GNUNET_MESSAGE_TYPE_TRANSPORT_FLOW_CONTROL - */ - struct GNUNET_MessageHeader header; - - /** - * Sequence number of the flow control message. Incremented by one - * for each message. Starts at zero when a virtual link goes up. - * Used to detect one-sided connection drops. On wrap-around, the - * flow control counters will be reset as if the connection had - * dropped. - */ - uint32_t seq GNUNET_PACKED; - - /** - * Flow control window size in bytes, in NBO. - * The receiver can send this many bytes at most. - */ - uint64_t inbound_window_size GNUNET_PACKED; - - /** - * How many bytes has the sender sent that count for flow control at - * this time. Used to allow the receiver to estimate the packet - * loss rate. - */ - uint64_t outbound_sent GNUNET_PACKED; - - /** - * Latest flow control window size we learned from the other peer, - * in bytes, in NBO. We are limited to sending at most this many - * bytes to the other peer. May help the other peer detect when - * flow control messages were lost and should thus be retransmitted. - * In particular, if the delta to @e outbound_sent is too small, - * this signals that we are stalled. - */ - uint64_t outbound_window_size GNUNET_PACKED; - - /** - * Timestamp of the sender. Must be monotonically increasing! - * Used to enable receiver to ignore out-of-order packets in - * combination with the @e seq. Note that @e seq will go down - * (back to zero) whenever either side believes the connection - * was dropped, allowing the peers to detect that they need to - * reset the counters for the number of bytes sent! - */ - struct GNUNET_TIME_AbsoluteNBO sender_time; -}; - - -GNUNET_NETWORK_STRUCT_END - - -/** - * What type of client is the `struct TransportClient` about? - */ -enum ClientType -{ - /** - * We do not know yet (client is fresh). - */ - CT_NONE = 0, - - /** - * Is the CORE service, we need to forward traffic to it. - */ - CT_CORE = 1, - - /** - * It is a monitor, forward monitor data. - */ - CT_MONITOR = 2, - - /** - * It is a communicator, use for communication. - */ - CT_COMMUNICATOR = 3, - - /** - * "Application" telling us where to connect (i.e. TOPOLOGY, DHT or CADET). - */ - CT_APPLICATION = 4 -}; - - -/** - * Which transmission options are allowable for transmission? - * Interpreted bit-wise! - */ -enum RouteMessageOptions -{ - /** - * Only confirmed, non-DV direct neighbours. - */ - RMO_NONE = 0, - - /** - * We are allowed to use DV routing for this @a hdr - */ - RMO_DV_ALLOWED = 1, - - /** - * We are allowed to use unconfirmed queues or DV routes for this message - */ - RMO_UNCONFIRMED_ALLOWED = 2, - - /** - * Reliable and unreliable, DV and non-DV are all acceptable. - */ - RMO_ANYTHING_GOES = (RMO_DV_ALLOWED | RMO_UNCONFIRMED_ALLOWED), - - /** - * If we have multiple choices, it is OK to send this message - * over multiple channels at the same time to improve loss tolerance. - * (We do at most 2 transmissions.) - */ - RMO_REDUNDANT = 4 -}; - - -/** - * When did we launch this DV learning activity? - */ -struct LearnLaunchEntry -{ - /** - * Kept (also) in a DLL sorted by launch time. - */ - struct LearnLaunchEntry *prev; - - /** - * Kept (also) in a DLL sorted by launch time. - */ - struct LearnLaunchEntry *next; - - /** - * Challenge that uniquely identifies this activity. - */ - struct GNUNET_CRYPTO_ChallengeNonceP challenge; - - /** - * When did we transmit the DV learn message (used to calculate RTT) and - * determine freshness of paths learned via this operation. - */ - struct GNUNET_TIME_Absolute launch_time; -}; - - -/** - * Information we keep per #GOODPUT_AGING_SLOTS about historic - * (or current) transmission performance. - */ -struct TransmissionHistoryEntry -{ - /** - * Number of bytes actually sent in the interval. - */ - uint64_t bytes_sent; - - /** - * Number of bytes received and acknowledged by the other peer in - * the interval. - */ - uint64_t bytes_received; -}; - - -/** - * Performance data for a transmission possibility. - */ -struct PerformanceData -{ - /** - * Weighted average for the RTT. - */ - struct GNUNET_TIME_Relative aged_rtt; - - /** - * Historic performance data, using a ring buffer of#GOODPUT_AGING_SLOTS - * entries. - */ - struct TransmissionHistoryEntry the[GOODPUT_AGING_SLOTS]; - - /** - * What was the last age when we wrote to @e the? Used to clear - * old entries when the age advances. - */ - unsigned int last_age; -}; - - -/** - * Client connected to the transport service. - */ -struct TransportClient; - -/** - * A neighbour that at least one communicator is connected to. - */ -struct Neighbour; - -/** - * Entry in our #dv_routes table, representing a (set of) distance - * vector routes to a particular peer. - */ -struct DistanceVector; - -/** - * A queue is a message queue provided by a communicator - * via which we can reach a particular neighbour. - */ -struct Queue; - -/** - * Message awaiting transmission. See detailed comments below. - */ -struct PendingMessage; - -/** - * One possible hop towards a DV target. - */ -struct DistanceVectorHop; - -/** - * A virtual link is another reachable peer that is known to CORE. It - * can be either a `struct Neighbour` with at least one confirmed - * `struct Queue`, or a `struct DistanceVector` with at least one - * confirmed `struct DistanceVectorHop`. With a virtual link we track - * data that is per neighbour that is not specific to how the - * connectivity is established. - */ -struct VirtualLink; - - -/** - * Context from #handle_incoming_msg(). Closure for many - * message handlers below. - */ -struct CommunicatorMessageContext -{ - /** - * Kept in a DLL of `struct VirtualLink` if waiting for CORE - * flow control to unchoke. - */ - struct CommunicatorMessageContext *next; - - /** - * Kept in a DLL of `struct VirtualLink` if waiting for CORE - * flow control to unchoke. - */ - struct CommunicatorMessageContext *prev; - - /** - * Which communicator provided us with the message. - */ - struct TransportClient *tc; - - /** - * Additional information for flow control and about the sender. - */ - struct GNUNET_TRANSPORT_IncomingMessage im; - - /** - * The message to demultiplex. - */ - const struct GNUNET_MessageHeader *mh; - - /** - * Number of hops the message has travelled (if DV-routed). - * FIXME: make use of this in ACK handling! - */ - uint16_t total_hops; -}; - - -/** - * Entry for the ring buffer caching messages send to core, when virtual link is avaliable. - **/ -struct RingBufferEntry -{ - /** - * Communicator context for this ring buffer entry. - **/ - struct CommunicatorMessageContext *cmc; - - /** - * The message in this entry. - **/ - struct GNUNET_MessageHeader *mh; -}; - - -/** - * Closure for #core_env_sent_cb. - */ -struct CoreSentContext -{ - /** - * Kept in a DLL to clear @e vl in case @e vl is lost. - */ - struct CoreSentContext *next; - - /** - * Kept in a DLL to clear @e vl in case @e vl is lost. - */ - struct CoreSentContext *prev; - - /** - * Virtual link this is about. - */ - struct VirtualLink *vl; - - /** - * How big was the message. - */ - uint16_t size; - - /** - * By how much should we increment @e vl's - * incoming_fc_window_size_used once we are done sending to CORE? - * Use to ensure we do not increment twice if there is more than one - * CORE client. - */ - uint16_t isize; -}; - - -/** - * Information we keep for a message that we are reassembling. - */ -struct ReassemblyContext -{ - /** - * Original message ID for of the message that all the fragments - * belong to. - */ - struct MessageUUIDP msg_uuid; - - /** - * Which neighbour is this context for? - */ - struct VirtualLink *virtual_link; - - /** - * Entry in the reassembly heap (sorted by expiration). - */ - struct GNUNET_CONTAINER_HeapNode *hn; - - /** - * Bitfield with @e msg_size bits representing the positions - * where we have received fragments. When we receive a fragment, - * we check the bits in @e bitfield before incrementing @e msg_missing. - * - * Allocated after the reassembled message. - */ - uint8_t *bitfield; - - /** - * At what time will we give up reassembly of this message? - */ - struct GNUNET_TIME_Absolute reassembly_timeout; - - /** - * Time we received the last fragment. @e avg_ack_delay must be - * incremented by now - @e last_frag multiplied by @e num_acks. - */ - struct GNUNET_TIME_Absolute last_frag; - - /** - * How big is the message we are reassembling in total? - */ - uint16_t msg_size; - - /** - * How many bytes of the message are still missing? Defragmentation - * is complete when @e msg_missing == 0. - */ - uint16_t msg_missing; - - /* Followed by @e msg_size bytes of the (partially) defragmented original - * message */ - - /* Followed by @e bitfield data */ -}; - - -/** - * A virtual link is another reachable peer that is known to CORE. It - * can be either a `struct Neighbour` with at least one confirmed - * `struct Queue`, or a `struct DistanceVector` with at least one - * confirmed `struct DistanceVectorHop`. With a virtual link we track - * data that is per neighbour that is not specific to how the - * connectivity is established. - */ -struct VirtualLink -{ - /** - * Identity of the peer at the other end of the link. - */ - struct GNUNET_PeerIdentity target; - - /** - * Map with `struct ReassemblyContext` structs for fragments under - * reassembly. May be NULL if we currently have no fragments from - * this @e pid (lazy initialization). - */ - struct GNUNET_CONTAINER_MultiHashMap32 *reassembly_map; - - /** - * Heap with `struct ReassemblyContext` structs for fragments under - * reassembly. May be NULL if we currently have no fragments from - * this @e pid (lazy initialization). - */ - struct GNUNET_CONTAINER_Heap *reassembly_heap; - - /** - * Task to free old entries from the @e reassembly_heap and @e reassembly_map. - */ - struct GNUNET_SCHEDULER_Task *reassembly_timeout_task; - - /** - * Communicators blocked for receiving on @e target as we are waiting - * on the @e core_recv_window to increase. - */ - struct CommunicatorMessageContext *cmc_head; - - /** - * Communicators blocked for receiving on @e target as we are waiting - * on the @e core_recv_window to increase. - */ - struct CommunicatorMessageContext *cmc_tail; - - /** - * Head of list of messages pending for this VL. - */ - struct PendingMessage *pending_msg_head; - - /** - * Tail of list of messages pending for this VL. - */ - struct PendingMessage *pending_msg_tail; - - /** - * Kept in a DLL to clear @e vl in case @e vl is lost. - */ - struct CoreSentContext *csc_tail; - - /** - * Kept in a DLL to clear @e vl in case @e vl is lost. - */ - struct CoreSentContext *csc_head; - - /** - * Task scheduled to possibly notfiy core that this peer is no - * longer counting as confirmed. Runs the #core_visibility_check(), - * which checks that some DV-path or a queue exists that is still - * considered confirmed. - */ - struct GNUNET_SCHEDULER_Task *visibility_task; - - /** - * Task scheduled to periodically retransmit FC messages (in - * case one got lost). - */ - struct GNUNET_SCHEDULER_Task *fc_retransmit_task; - - /** - * Number of FC retransmissions for this running task. - */ - unsigned int fc_retransmit_count; - - /** - * Is this VirtualLink confirmed. - * A unconfirmed VirtualLink might exist, if we got a FC from that target. - */ - unsigned int confirmed; - - /** - * Neighbour used by this virtual link, NULL if @e dv is used. - */ - struct Neighbour *n; - - /** - * Distance vector used by this virtual link, NULL if @e n is used. - */ - struct DistanceVector *dv; - - /** - * Sender timestamp of @e n_challenge, used to generate out-of-order - * challenges (as sender's timestamps must be monotonically - * increasing). FIXME: where do we need this? - */ - struct GNUNET_TIME_Absolute n_challenge_time; - - /** - * When did we last send a - * #GNUNET_MESSAGE_TYPE_TRANSPORT_FLOW_CONTROL message? - * Used to determine whether it is time to re-transmit the message. - */ - struct GNUNET_TIME_Absolute last_fc_transmission; - - /** - * Sender timestamp of the last - * #GNUNET_MESSAGE_TYPE_TRANSPORT_FLOW_CONTROL message we have - * received. Note that we do not persist this monotonic time as we - * do not really have to worry about ancient flow control window - * sizes after restarts. - */ - struct GNUNET_TIME_Absolute last_fc_timestamp; - - /** - * Expected RTT from the last FC transmission. (Zero if the last - * attempt failed, but could theoretically be zero even on success.) - */ - struct GNUNET_TIME_Relative last_fc_rtt; - - /** - * Used to generate unique UUIDs for messages that are being - * fragmented. - */ - uint64_t message_uuid_ctr; - - /** - * Memory allocated for this virtual link. Expresses how much RAM - * we are willing to allocate to this virtual link. OPTIMIZE-ME: - * Can be adapted to dedicate more RAM to links that need it, while - * sticking to some overall RAM limit. For now, set to - * #DEFAULT_WINDOW_SIZE. - */ - uint64_t available_fc_window_size; - - /** - * Memory actually used to buffer packets on this virtual link. - * Expresses how much RAM we are currently using for virtual link. - * Note that once CORE is done with a packet, we decrement the value - * here. - */ - uint64_t incoming_fc_window_size_ram; - - /** - * Last flow control window size we provided to the other peer, in - * bytes. We are allowing the other peer to send this - * many bytes. - */ - uint64_t incoming_fc_window_size; - - /** - * How much of the window did the other peer successfully use (and - * we already passed it on to CORE)? Must be below @e - * incoming_fc_window_size. We should effectively signal the - * other peer that the window is this much bigger at the next - * opportunity / challenge. - */ - uint64_t incoming_fc_window_size_used; - - /** - * What is our current estimate on the message loss rate for the sender? - * Based on the difference between how much the sender sent according - * to the last #GNUNET_MESSAGE_TYPE_TRANSPORT_FLOW_CONTROL message - * (@e outbound_sent field) and how much we actually received at that - * time (@e incoming_fc_window_size_used). This delta is then - * added onto the @e incoming_fc_window_size when determining the - * @e outbound_window_size we send to the other peer. Initially zero. - * May be negative if we (due to out-of-order delivery) actually received - * more than the sender claims to have sent in its last FC message. - */ - int64_t incoming_fc_window_size_loss; - - /** - * Our current flow control window size in bytes. We - * are allowed to transmit this many bytes to @a n. - */ - uint64_t outbound_fc_window_size; - - /** - * How much of our current flow control window size have we - * used (in bytes). Must be below - * @e outbound_fc_window_size. - */ - uint64_t outbound_fc_window_size_used; - - /** - * What is the most recent FC window the other peer sent us - * in `outbound_window_size`? This is basically the window - * size value the other peer has definitively received from - * us. If it matches @e incoming_fc_window_size, we should - * not send a FC message to increase the FC window. However, - * we may still send an FC message to notify the other peer - * that we received the other peer's FC message. - */ - uint64_t last_outbound_window_size_received; - - /** - * Generator for the sequence numbers of - * #GNUNET_MESSAGE_TYPE_TRANSPORT_FLOW_CONTROL messages we send. - */ - uint32_t fc_seq_gen; - - /** - * Last sequence number of a - * #GNUNET_MESSAGE_TYPE_TRANSPORT_FLOW_CONTROL message we have - * received. - */ - uint32_t last_fc_seq; - - /** - * How many more messages can we send to CORE before we exhaust - * the receive window of CORE for this peer? If this hits zero, - * we must tell communicators to stop providing us more messages - * for this peer. In fact, the window can go negative as we - * have multiple communicators, so per communicator we can go - * down by one into the negative range. Furthermore, we count - * delivery per CORE client, so if we had multiple cores, that - * might also cause a negative window size here (as one message - * would decrement the window by one per CORE client). - */ - int core_recv_window; -}; - - -/** - * Data structure kept when we are waiting for an acknowledgement. - */ -struct PendingAcknowledgement -{ - /** - * If @e pm is non-NULL, this is the DLL in which this acknowledgement - * is kept in relation to its pending message. - */ - struct PendingAcknowledgement *next_pm; - - /** - * If @e pm is non-NULL, this is the DLL in which this acknowledgement - * is kept in relation to its pending message. - */ - struct PendingAcknowledgement *prev_pm; - - /** - * If @e queue is non-NULL, this is the DLL in which this acknowledgement - * is kept in relation to the queue that was used to transmit the - * @a pm. - */ - struct PendingAcknowledgement *next_queue; - - /** - * If @e queue is non-NULL, this is the DLL in which this acknowledgement - * is kept in relation to the queue that was used to transmit the - * @a pm. - */ - struct PendingAcknowledgement *prev_queue; - - /** - * If @e dvh is non-NULL, this is the DLL in which this acknowledgement - * is kept in relation to the DVH that was used to transmit the - * @a pm. - */ - struct PendingAcknowledgement *next_dvh; - - /** - * If @e dvh is non-NULL, this is the DLL in which this acknowledgement - * is kept in relation to the DVH that was used to transmit the - * @a pm. - */ - struct PendingAcknowledgement *prev_dvh; - - /** - * Pointers for the DLL of all pending acknowledgements. - * This list is sorted by @e transmission time. If the list gets too - * long, the oldest entries are discarded. - */ - struct PendingAcknowledgement *next_pa; - - /** - * Pointers for the DLL of all pending acknowledgements. - * This list is sorted by @e transmission time. If the list gets too - * long, the oldest entries are discarded. - */ - struct PendingAcknowledgement *prev_pa; - - /** - * Unique identifier for this transmission operation. - */ - struct AcknowledgementUUIDP ack_uuid; - - /** - * Message that was transmitted, may be NULL if the message was ACKed - * via another channel. - */ - struct PendingMessage *pm; - - /** - * Distance vector path chosen for this transmission, NULL if transmission - * was to a direct neighbour OR if the path was forgotten in the meantime. - */ - struct DistanceVectorHop *dvh; - - /** - * Queue used for transmission, NULL if the queue has been destroyed - * (which may happen before we get an acknowledgement). - */ - struct Queue *queue; - - /** - * Time of the transmission, for RTT calculation. - */ - struct GNUNET_TIME_Absolute transmission_time; - - /** - * Number of bytes of the original message (to calculate bandwidth). - */ - uint16_t message_size; - - /** - * How often the PendingMessage was send via the Queue of this PendingAcknowledgement. - */ - unsigned int num_send; -}; - - -/** - * One possible hop towards a DV target. - */ -struct DistanceVectorHop -{ - /** - * Kept in a MDLL, sorted by @e timeout. - */ - struct DistanceVectorHop *next_dv; - - /** - * Kept in a MDLL, sorted by @e timeout. - */ - struct DistanceVectorHop *prev_dv; - - /** - * Kept in a MDLL. - */ - struct DistanceVectorHop *next_neighbour; - - /** - * Kept in a MDLL. - */ - struct DistanceVectorHop *prev_neighbour; - - /** - * Head of DLL of PAs that used our @a path. - */ - struct PendingAcknowledgement *pa_head; - - /** - * Tail of DLL of PAs that used our @a path. - */ - struct PendingAcknowledgement *pa_tail; - - /** - * What would be the next hop to @e target? - */ - struct Neighbour *next_hop; - - /** - * Distance vector entry this hop belongs with. - */ - struct DistanceVector *dv; - - /** - * Array of @e distance hops to the target, excluding @e next_hop. - * NULL if the entire path is us to @e next_hop to `target`. Allocated - * at the end of this struct. Excludes the target itself! - */ - const struct GNUNET_PeerIdentity *path; - - /** - * At what time do we forget about this path unless we see it again - * while learning? - */ - struct GNUNET_TIME_Absolute timeout; - - /** - * For how long is the validation of this path considered - * valid? - * Set to ZERO if the path is learned by snooping on DV learn messages - * initiated by other peers, and to the time at which we generated the - * challenge for DV learn operations this peer initiated. - */ - struct GNUNET_TIME_Absolute path_valid_until; - - /** - * Performance data for this transmission possibility. - */ - struct PerformanceData pd; - - /** - * Number of hops in total to the `target` (excluding @e next_hop and `target` - * itself). Thus 0 still means a distance of 2 hops (to @e next_hop and then - * to `target`). - */ - unsigned int distance; -}; - - -/** - * Entry in our #dv_routes table, representing a (set of) distance - * vector routes to a particular peer. - */ -struct DistanceVector -{ - /** - * To which peer is this a route? - */ - struct GNUNET_PeerIdentity target; - - /** - * Known paths to @e target. - */ - struct DistanceVectorHop *dv_head; - - /** - * Known paths to @e target. - */ - struct DistanceVectorHop *dv_tail; - - /** - * Task scheduled to purge expired paths from @e dv_head MDLL. - */ - struct GNUNET_SCHEDULER_Task *timeout_task; - - /** - * Do we have a confirmed working queue and are thus visible to - * CORE? If so, this is the virtual link, otherwise NULL. - */ - struct VirtualLink *vl; - - /** - * Signature affirming @e ephemeral_key of type - * #GNUNET_SIGNATURE_PURPOSE_TRANSPORT_EPHEMERAL - */ - struct GNUNET_CRYPTO_EddsaSignature sender_sig; - - /** - * How long is @e sender_sig valid - */ - struct GNUNET_TIME_Absolute ephemeral_validity; - - /** - * What time was @e sender_sig created - */ - struct GNUNET_TIME_Absolute monotime; - - /** - * Our ephemeral key. - */ - struct GNUNET_CRYPTO_EcdhePublicKey ephemeral_key; - -}; - - -/** - * Entry identifying transmission in one of our `struct - * Queue` which still awaits an ACK. This is used to - * ensure we do not overwhelm a communicator and limit the number of - * messages outstanding per communicator (say in case communicator is - * CPU bound) and per queue (in case bandwidth allocation exceeds - * what the communicator can actually provide towards a particular - * peer/target). - */ -struct QueueEntry -{ - /** - * Kept as a DLL. - */ - struct QueueEntry *next; - - /** - * Kept as a DLL. - */ - struct QueueEntry *prev; - - /** - * Queue this entry is queued with. - */ - struct Queue *queue; - - /** - * Pending message this entry is for, or NULL for none. - */ - struct PendingMessage *pm; - - /** - * Message ID used for this message with the queue used for transmission. - */ - uint64_t mid; -}; - - -/** - * A queue is a message queue provided by a communicator - * via which we can reach a particular neighbour. - */ -struct Queue -{ - /** - * Kept in a MDLL. - */ - struct Queue *next_neighbour; - - /** - * Kept in a MDLL. - */ - struct Queue *prev_neighbour; - - /** - * Kept in a MDLL. - */ - struct Queue *prev_client; - - /** - * Kept in a MDLL. - */ - struct Queue *next_client; - - /** - * Head of DLL of PAs that used this queue. - */ - struct PendingAcknowledgement *pa_head; - - /** - * Tail of DLL of PAs that used this queue. - */ - struct PendingAcknowledgement *pa_tail; - - /** - * Head of DLL of unacked transmission requests. - */ - struct QueueEntry *queue_head; - - /** - * End of DLL of unacked transmission requests. - */ - struct QueueEntry *queue_tail; - - /** - * Which neighbour is this queue for? - */ - struct Neighbour *neighbour; - - /** - * Which communicator offers this queue? - */ - struct TransportClient *tc; - - /** - * Address served by the queue. - */ - const char *address; - - /** - * Is this queue of unlimited length. - */ - unsigned int unlimited_length; - - /** - * Task scheduled for the time when this queue can (likely) transmit the - * next message. - */ - struct GNUNET_SCHEDULER_Task *transmit_task; - - /** - * How long do *we* consider this @e address to be valid? In the past or - * zero if we have not yet validated it. Can be updated based on - * challenge-response validations (via address validation logic), or when we - * receive ACKs that we can definitively map to transmissions via this - * queue. - */ - struct GNUNET_TIME_Absolute validated_until; - - /** - * Performance data for this queue. - */ - struct PerformanceData pd; - - /** - * Message ID generator for transmissions on this queue to the - * communicator. - */ - uint64_t mid_gen; - - /** - * Unique identifier of this queue with the communicator. - */ - uint32_t qid; - - /** - * Maximum transmission unit supported by this queue. - */ - uint32_t mtu; - - /** - * Messages pending. - */ - uint32_t num_msg_pending; - - /** - * Bytes pending. - */ - uint32_t num_bytes_pending; - - /** - * Length of the DLL starting at @e queue_head. - */ - unsigned int queue_length; - - /** - * Capacity of the queue. - */ - uint64_t q_capacity; - - /** - * Queue priority - */ - uint32_t priority; - - /** - * Network type offered by this queue. - */ - enum GNUNET_NetworkType nt; - - /** - * Connection status for this queue. - */ - enum GNUNET_TRANSPORT_ConnectionStatus cs; - - /** - * Set to #GNUNET_YES if this queue is idle waiting for some - * virtual link to give it a pending message. - */ - int idle; -}; - - -/** - * A neighbour that at least one communicator is connected to. - */ -struct Neighbour -{ - /** - * Which peer is this about? - */ - struct GNUNET_PeerIdentity pid; - - /** - * Head of MDLL of DV hops that have this neighbour as next hop. Must be - * purged if this neighbour goes down. - */ - struct DistanceVectorHop *dv_head; - - /** - * Tail of MDLL of DV hops that have this neighbour as next hop. Must be - * purged if this neighbour goes down. - */ - struct DistanceVectorHop *dv_tail; - - /** - * Head of DLL of queues to this peer. - */ - struct Queue *queue_head; - - /** - * Tail of DLL of queues to this peer. - */ - struct Queue *queue_tail; - - /** - * Handle for an operation to fetch @e last_dv_learn_monotime information from - * the PEERSTORE, or NULL. - */ - struct GNUNET_PEERSTORE_IterateContext *get; - - /** - * Handle to a PEERSTORE store operation to store this @e pid's @e - * @e last_dv_learn_monotime. NULL if no PEERSTORE operation is pending. - */ - struct GNUNET_PEERSTORE_StoreContext *sc; - - /** - * Do we have a confirmed working queue and are thus visible to - * CORE? If so, this is the virtual link, otherwise NULL. - */ - struct VirtualLink *vl; - - /** - * Latest DVLearn monotonic time seen from this peer. Initialized only - * if @e dl_monotime_available is #GNUNET_YES. - */ - struct GNUNET_TIME_Absolute last_dv_learn_monotime; - - /** - * Do we have the latest value for @e last_dv_learn_monotime from - * PEERSTORE yet, or are we still waiting for a reply of PEERSTORE? - */ - int dv_monotime_available; -}; - - -/** - * Another peer attempted to talk to us, we should try to establish - * a connection in the other direction. - */ -struct IncomingRequest -{ - /** - * Kept in a DLL. - */ - struct IncomingRequest *next; - - /** - * Kept in a DLL. - */ - struct IncomingRequest *prev; - - /** - * Notify context for new HELLOs. - */ - struct GNUNET_PEERSTORE_NotifyContext *nc; - - /** - * Which peer is this about? - */ - struct GNUNET_PeerIdentity pid; -}; - - -/** - * A peer that an application (client) would like us to talk to directly. - */ -struct PeerRequest -{ - /** - * Which peer is this about? - */ - struct GNUNET_PeerIdentity pid; - - /** - * Client responsible for the request. - */ - struct TransportClient *tc; - - /** - * Notify context for new HELLOs. - */ - struct GNUNET_PEERSTORE_NotifyContext *nc; - - /** - * What kind of performance preference does this @e tc have? - * - * TODO: use this! - */ - enum GNUNET_MQ_PriorityPreferences pk; - - /** - * How much bandwidth would this @e tc like to see? - */ - struct GNUNET_BANDWIDTH_Value32NBO bw; -}; - - -/** - * Types of different pending messages. - */ -enum PendingMessageType -{ - /** - * Ordinary message received from the CORE service. - */ - PMT_CORE = 0, - - /** - * Fragment box. - */ - PMT_FRAGMENT_BOX = 1, - - /** - * Reliability box. - */ - PMT_RELIABILITY_BOX = 2, - - /** - * Pending message created during #forward_dv_box(). - */ - PMT_DV_BOX = 3 -}; - - -/** - * Transmission request that is awaiting delivery. The original - * transmission requests from CORE may be too big for some queues. - * In this case, a *tree* of fragments is created. At each - * level of the tree, fragments are kept in a DLL ordered by which - * fragment should be sent next (at the head). The tree is searched - * top-down, with the original message at the root. - * - * To select a node for transmission, first it is checked if the - * current node's message fits with the MTU. If it does not, we - * either calculate the next fragment (based on @e frag_off) from the - * current node, or, if all fragments have already been created, - * descend to the @e head_frag. Even though the node was already - * fragmented, the fragment may be too big if the fragment was - * generated for a queue with a larger MTU. In this case, the node - * may be fragmented again, thus creating a tree. - * - * When acknowledgements for fragments are received, the tree - * must be pruned, removing those parts that were already - * acknowledged. When fragments are sent over a reliable - * channel, they can be immediately removed. - * - * If a message is ever fragmented, then the original "full" message - * is never again transmitted (even if it fits below the MTU), and - * only (remaining) fragments are sent. - */ -struct PendingMessage -{ - /** - * Kept in a MDLL of messages for this @a vl. - */ - struct PendingMessage *next_vl; - - /** - * Kept in a MDLL of messages for this @a vl. - */ - struct PendingMessage *prev_vl; - - /** - * Kept in a MDLL of messages from this @a client (if @e pmt is #PMT_CORE) - */ - struct PendingMessage *next_client; - - /** - * Kept in a MDLL of messages from this @a client (if @e pmt is #PMT_CORE) - */ - struct PendingMessage *prev_client; - - /** - * Kept in a MDLL of messages from this @a cpm (if @e pmt is - * #PMT_FRAGMENT_BOx) - */ - struct PendingMessage *next_frag; - - /** - * Kept in a MDLL of messages from this @a cpm (if @e pmt is - * #PMT_FRAGMENT_BOX) - */ - struct PendingMessage *prev_frag; - - /** - * Head of DLL of PAs for this pending message. - */ - struct PendingAcknowledgement *pa_head; - - /** - * Tail of DLL of PAs for this pending message. - */ - struct PendingAcknowledgement *pa_tail; - - /** - * This message, reliability *or* DV-boxed. Only possibly available - * if @e pmt is #PMT_CORE. - */ - struct PendingMessage *bpm; - - /** - * Target of the request (always the ultimate destination!). - * Might be NULL in case of a forwarded DVBox we have no validated neighbour. - */ - struct VirtualLink *vl; - - /** - * In case of a not validated neighbour, we store the target peer. - **/ - struct GNUNET_PeerIdentity target; - - /** - * Set to non-NULL value if this message is currently being given to a - * communicator and we are awaiting that communicator's acknowledgement. - * Note that we must not retransmit a pending message while we're still - * in the process of giving it to a communicator. If a pending message - * is free'd while this entry is non-NULL, the @e qe reference to us - * should simply be set to NULL. - */ - struct QueueEntry *qe; - - /** - * Client that issued the transmission request, if @e pmt is #PMT_CORE. - */ - struct TransportClient *client; - - /** - * Head of a MDLL of fragments created for this core message. - */ - struct PendingMessage *head_frag; - - /** - * Tail of a MDLL of fragments created for this core message. - */ - struct PendingMessage *tail_frag; - - /** - * Our parent in the fragmentation tree. - */ - struct PendingMessage *frag_parent; - - /** - * At what time should we give up on the transmission (and no longer retry)? - */ - struct GNUNET_TIME_Absolute timeout; - - /** - * What is the earliest time for us to retry transmission of this message? - */ - struct GNUNET_TIME_Absolute next_attempt; - - /** - * UUID to use for this message (used for reassembly of fragments, only - * initialized if @e msg_uuid_set is #GNUNET_YES). - */ - struct MessageUUIDP msg_uuid; - - /** - * UUID we use to identify this message in our logs. - * Generated by incrementing the "logging_uuid_gen". - */ - unsigned long long logging_uuid; - - /** - * Type of the pending message. - */ - enum PendingMessageType pmt; - - /** - * Preferences for this message. - * TODO: actually use this! - */ - enum GNUNET_MQ_PriorityPreferences prefs; - - /** - * If pmt is of type PMT_DV_BOX we store the used path here. - */ - struct DistanceVectorHop *used_dvh; - - /** - * Size of the original message. - */ - uint16_t bytes_msg; - - /** - * Offset at which we should generate the next fragment. - */ - uint16_t frag_off; - - /** - * Are we sending fragments at the moment? - */ - unsigned int frags_in_flight; - - /** - * How many fragments do we have? - **/ - uint16_t frag_count; - - /** - * #GNUNET_YES once @e msg_uuid was initialized - */ - int16_t msg_uuid_set; - - /* Followed by @e bytes_msg to transmit */ -}; - - -/** - * Acknowledgement payload. - */ -struct TransportCummulativeAckPayload -{ - /** - * When did we receive the message we are ACKing? Used to calculate - * the delay we introduced by cummulating ACKs. - */ - struct GNUNET_TIME_Absolute receive_time; - - /** - * UUID of a message being acknowledged. - */ - struct AcknowledgementUUIDP ack_uuid; -}; - - -/** - * Data structure in which we track acknowledgements still to - * be sent to the - */ -struct AcknowledgementCummulator -{ - /** - * Target peer for which we are accumulating ACKs here. - */ - struct GNUNET_PeerIdentity target; - - /** - * ACK data being accumulated. Only @e num_acks slots are valid. - */ - struct TransportCummulativeAckPayload ack_uuids[MAX_CUMMULATIVE_ACKS]; - - /** - * Task scheduled either to transmit the cumulative ACK message, - * or to clean up this data structure after extended periods of - * inactivity (if @e num_acks is zero). - */ - struct GNUNET_SCHEDULER_Task *task; - - /** - * When is @e task run (only used if @e num_acks is non-zero)? - */ - struct GNUNET_TIME_Absolute min_transmission_time; - - /** - * Counter to produce the `ack_counter` in the `struct - * TransportReliabilityAckMessage`. Allows the receiver to detect - * lost ACK messages. Incremented by @e num_acks upon transmission. - */ - uint32_t ack_counter; - - /** - * Number of entries used in @e ack_uuids. Reset to 0 upon transmission. - */ - unsigned int num_acks; -}; - - -/** - * One of the addresses of this peer. - */ -struct AddressListEntry -{ - /** - * Kept in a DLL. - */ - struct AddressListEntry *next; - - /** - * Kept in a DLL. - */ - struct AddressListEntry *prev; - - /** - * Which communicator provides this address? - */ - struct TransportClient *tc; - - /** - * The actual address. - */ - const char *address; - - /** - * Current context for storing this address in the peerstore. - */ - struct GNUNET_PEERSTORE_StoreContext *sc; - - /** - * Task to periodically do @e st operation. - */ - struct GNUNET_SCHEDULER_Task *st; - - /** - * What is a typical lifetime the communicator expects this - * address to have? (Always from now.) - */ - struct GNUNET_TIME_Relative expiration; - - /** - * Address identifier used by the communicator. - */ - uint32_t aid; - - /** - * Network type offered by this address. - */ - enum GNUNET_NetworkType nt; -}; - - -/** - * Client connected to the transport service. - */ -struct TransportClient -{ - /** - * Kept in a DLL. - */ - struct TransportClient *next; - - /** - * Kept in a DLL. - */ - struct TransportClient *prev; - - /** - * Handle to the client. - */ - struct GNUNET_SERVICE_Client *client; - - /** - * Message queue to the client. - */ - struct GNUNET_MQ_Handle *mq; - - /** - * What type of client is this? - */ - enum ClientType type; - - union - { - /** - * Information for @e type #CT_CORE. - */ - struct - { - /** - * Head of list of messages pending for this client, sorted by - * transmission time ("next_attempt" + possibly internal prioritization). - */ - struct PendingMessage *pending_msg_head; - - /** - * Tail of list of messages pending for this client. - */ - struct PendingMessage *pending_msg_tail; - } core; - - /** - * Information for @e type #CT_MONITOR. - */ - struct - { - /** - * Peer identity to monitor the addresses of. - * Zero to monitor all neighbours. Valid if - * @e type is #CT_MONITOR. - */ - struct GNUNET_PeerIdentity peer; - - /** - * Is this a one-shot monitor? - */ - int one_shot; - } monitor; - - - /** - * Information for @e type #CT_COMMUNICATOR. - */ - struct - { - /** - * If @e type is #CT_COMMUNICATOR, this communicator - * supports communicating using these addresses. - */ - char *address_prefix; - - /** - * Head of DLL of queues offered by this communicator. - */ - struct Queue *queue_head; - - /** - * Tail of DLL of queues offered by this communicator. - */ - struct Queue *queue_tail; - - /** - * Head of list of the addresses of this peer offered by this - * communicator. - */ - struct AddressListEntry *addr_head; - - /** - * Tail of list of the addresses of this peer offered by this - * communicator. - */ - struct AddressListEntry *addr_tail; - - /** - * Number of queue entries in all queues to this communicator. Used - * throttle sending to a communicator if we see that the communicator - * is globally unable to keep up. - */ - unsigned int total_queue_length; - - /** - * Characteristics of this communicator. - */ - enum GNUNET_TRANSPORT_CommunicatorCharacteristics cc; - } communicator; - - /** - * Information for @e type #CT_APPLICATION - */ - struct - { - /** - * Map of requests for peers the given client application would like to - * see connections for. Maps from PIDs to `struct PeerRequest`. - */ - struct GNUNET_CONTAINER_MultiPeerMap *requests; - } application; - } details; -}; - - -/** - * State we keep for validation activities. Each of these - * is both in the #validation_heap and the #validation_map. - */ -struct ValidationState -{ - /** - * For which peer is @a address to be validated (or possibly valid)? - * Serves as key in the #validation_map. - */ - struct GNUNET_PeerIdentity pid; - - /** - * How long did the peer claim this @e address to be valid? Capped at - * minimum of #MAX_ADDRESS_VALID_UNTIL relative to the time where we last - * were told about the address and the value claimed by the other peer at - * that time. May be updated similarly when validation succeeds. - */ - struct GNUNET_TIME_Absolute valid_until; - - /** - * How long do *we* consider this @e address to be valid? - * In the past or zero if we have not yet validated it. - */ - struct GNUNET_TIME_Absolute validated_until; - - /** - * When did we FIRST use the current @e challenge in a message? - * Used to sanity-check @code{origin_time} in the response when - * calculating the RTT. If the @code{origin_time} is not in - * the expected range, the response is discarded as malicious. - */ - struct GNUNET_TIME_Absolute first_challenge_use; - - /** - * When did we LAST use the current @e challenge in a message? - * Used to sanity-check @code{origin_time} in the response when - * calculating the RTT. If the @code{origin_time} is not in - * the expected range, the response is discarded as malicious. - */ - struct GNUNET_TIME_Absolute last_challenge_use; - - /** - * Next time we will send the @e challenge to the peer, if this time is past - * @e valid_until, this validation state is released at this time. If the - * address is valid, @e next_challenge is set to @e validated_until MINUS @e - * validation_delay * #VALIDATION_RTT_BUFFER_FACTOR, such that we will try - * to re-validate before the validity actually expires. - */ - struct GNUNET_TIME_Absolute next_challenge; - - /** - * Current backoff factor we're applying for sending the @a challenge. - * Reset to 0 if the @a challenge is confirmed upon validation. - * Reduced to minimum of #FAST_VALIDATION_CHALLENGE_FREQ and half of the - * existing value if we receive an unvalidated address again over - * another channel (and thus should consider the information "fresh"). - * Maximum is #MAX_VALIDATION_CHALLENGE_FREQ. - */ - struct GNUNET_TIME_Relative challenge_backoff; - - /** - * Initially set to "forever". Once @e validated_until is set, this value is - * set to the RTT that tells us how long it took to receive the validation. - */ - struct GNUNET_TIME_Relative validation_rtt; - - /** - * The challenge we sent to the peer to get it to validate the address. Note - * that we rotate the challenge whenever we update @e validated_until to - * avoid attacks where a peer simply replays an old challenge in the future. - * (We must not rotate more often as otherwise we may discard valid answers - * due to packet losses, latency and reorderings on the network). - */ - struct GNUNET_CRYPTO_ChallengeNonceP challenge; - - /** - * Claimed address of the peer. - */ - char *address; - - /** - * Entry in the #validation_heap, which is sorted by @e next_challenge. The - * heap is used to figure out when the next validation activity should be - * run. - */ - struct GNUNET_CONTAINER_HeapNode *hn; - - /** - * Handle to a PEERSTORE store operation for this @e address. NULL if - * no PEERSTORE operation is pending. - */ - struct GNUNET_PEERSTORE_StoreContext *sc; - - /** - * Self-imposed limit on the previous flow control window. (May be zero, - * if we never used data from the previous window or are establishing the - * connection for the first time). - */ - uint32_t last_window_consum_limit; - - /** - * We are technically ready to send the challenge, but we are waiting for - * the respective queue to become available for transmission. - */ - int awaiting_queue; -}; - - -/** - * A Backtalker is a peer sending us backchannel messages. We use this - * struct to detect monotonic time violations, cache ephemeral key - * material (to avoid repeatedly checking signatures), and to synchronize - * monotonic time with the PEERSTORE. - */ -struct Backtalker -{ - /** - * Peer this is about. - */ - struct GNUNET_PeerIdentity pid; - - /** - * Last (valid) monotonic time received from this sender. - */ - struct GNUNET_TIME_Absolute monotonic_time; - - /** - * When will this entry time out? - */ - struct GNUNET_TIME_Absolute timeout; - - /** - * Last (valid) ephemeral key received from this sender. - */ - struct GNUNET_CRYPTO_EcdhePublicKey last_ephemeral; - - /** - * Task associated with this backtalker. Can be for timeout, - * or other asynchronous operations. - */ - struct GNUNET_SCHEDULER_Task *task; - - /** - * Communicator context waiting on this backchannel's @e get, or NULL. - */ - struct CommunicatorMessageContext *cmc; - - /** - * Handle for an operation to fetch @e monotonic_time information from the - * PEERSTORE, or NULL. - */ - struct GNUNET_PEERSTORE_IterateContext *get; - - /** - * Handle to a PEERSTORE store operation for this @e pid's @e - * monotonic_time. NULL if no PEERSTORE operation is pending. - */ - struct GNUNET_PEERSTORE_StoreContext *sc; - - /** - * Number of bytes of the original message body that follows after this - * struct. - */ - size_t body_size; -}; - - -/** - * Ring buffer for a CORE message we did not deliver to CORE, because of missing virtual link to sender. - */ -static struct RingBufferEntry *ring_buffer[RING_BUFFER_SIZE]; - -/** - * Head of the ring buffer. - */ -static unsigned int ring_buffer_head; - -/** - * Is the ring buffer filled up to RING_BUFFER_SIZE. - */ -static unsigned int is_ring_buffer_full; - -/** - * Ring buffer for a forwarded DVBox message we did not deliver to the next hop, because of missing virtual link that hop. - */ -static struct PendingMessage *ring_buffer_dv[RING_BUFFER_SIZE]; - -/** - * Head of the ring buffer. - */ -static unsigned int ring_buffer_dv_head; - -/** - * Is the ring buffer filled up to RING_BUFFER_SIZE. - */ -static unsigned int is_ring_buffer_dv_full; - -/** - * Head of linked list of all clients to this service. - */ -static struct TransportClient *clients_head; - -/** - * Tail of linked list of all clients to this service. - */ -static struct TransportClient *clients_tail; - -/** - * Statistics handle. - */ -static struct GNUNET_STATISTICS_Handle *GST_stats; - -/** - * Configuration handle. - */ -static const struct GNUNET_CONFIGURATION_Handle *GST_cfg; - -/** - * Our public key. - */ -static struct GNUNET_PeerIdentity GST_my_identity; - -/** - * Our HELLO - */ -struct GNUNET_HELLO_Builder *GST_my_hello; - -/** - * Our private key. - */ -static struct GNUNET_CRYPTO_EddsaPrivateKey *GST_my_private_key; - -/** - * Map from PIDs to `struct Neighbour` entries. A peer is - * a neighbour if we have an MQ to it from some communicator. - */ -static struct GNUNET_CONTAINER_MultiPeerMap *neighbours; - -/** - * Map from PIDs to `struct Backtalker` entries. A peer is - * a backtalker if it recently send us backchannel messages. - */ -static struct GNUNET_CONTAINER_MultiPeerMap *backtalkers; - -/** - * Map from PIDs to `struct AcknowledgementCummulator`s. - * Here we track the cumulative ACKs for transmission. - */ -static struct GNUNET_CONTAINER_MultiPeerMap *ack_cummulators; - -/** - * Map of pending acknowledgements, mapping `struct AcknowledgementUUID` to - * a `struct PendingAcknowledgement`. - */ -static struct GNUNET_CONTAINER_MultiUuidmap *pending_acks; - -/** - * Map from PIDs to `struct DistanceVector` entries describing - * known paths to the peer. - */ -static struct GNUNET_CONTAINER_MultiPeerMap *dv_routes; - -/** - * Map from PIDs to `struct ValidationState` entries describing - * addresses we are aware of and their validity state. - */ -static struct GNUNET_CONTAINER_MultiPeerMap *validation_map; - -/** - * Map from PIDs to `struct VirtualLink` entries describing - * links CORE knows to exist. - */ -static struct GNUNET_CONTAINER_MultiPeerMap *links; - -/** - * Map from challenges to `struct LearnLaunchEntry` values. - */ -static struct GNUNET_CONTAINER_MultiShortmap *dvlearn_map; - -/** - * Head of a DLL sorted by launch time. - */ -static struct LearnLaunchEntry *lle_head = NULL; - -/** - * Tail of a DLL sorted by launch time. - */ -static struct LearnLaunchEntry *lle_tail = NULL; - -/** - * MIN Heap sorted by "next_challenge" to `struct ValidationState` entries - * sorting addresses we are aware of by when we should next try to (re)validate - * (or expire) them. - */ -static struct GNUNET_CONTAINER_Heap *validation_heap; - -/** - * Database for peer's HELLOs. - */ -static struct GNUNET_PEERSTORE_Handle *peerstore; - -/** - * Task run to initiate DV learning. - */ -static struct GNUNET_SCHEDULER_Task *dvlearn_task; - -/** - * Task to run address validation. - */ -static struct GNUNET_SCHEDULER_Task *validation_task; - -/** - * List of incoming connections where we are trying - * to get a connection back established. Length - * kept in #ir_total. - */ -static struct IncomingRequest *ir_head; - -/** - * Tail of DLL starting at #ir_head. - */ -static struct IncomingRequest *ir_tail; - -/** - * Length of the DLL starting at #ir_head. - */ -static unsigned int ir_total; - -/** - * Generator of `logging_uuid` in `struct PendingMessage`. - */ -static unsigned long long logging_uuid_gen; - -/** - * Monotonic time we use for HELLOs generated at this time. TODO: we - * should increase this value from time to time (i.e. whenever a - * `struct AddressListEntry` actually expires), but IF we do this, we - * must also update *all* (remaining) addresses in the PEERSTORE at - * that time! (So for now only increased when the peer is restarted, - * which hopefully roughly matches whenever our addresses change.) - */ -static struct GNUNET_TIME_Absolute hello_mono_time; - -/** - * Indication if we have received a shutdown signal - * and are in the process of cleaning up. - */ -static int in_shutdown; - -/** - * Get an offset into the transmission history buffer for `struct - * PerformanceData`. Note that the caller must perform the required - * modulo #GOODPUT_AGING_SLOTS operation before indexing into the - * array! - * - * An 'age' lasts 15 minute slots. - * - * @return current age of the world - */ -static unsigned int -get_age () -{ - struct GNUNET_TIME_Absolute now; - - now = GNUNET_TIME_absolute_get (); - return now.abs_value_us / GNUNET_TIME_UNIT_MINUTES.rel_value_us / 15; -} - - -/** - * Release @a ir data structure. - * - * @param ir data structure to release - */ -static void -free_incoming_request (struct IncomingRequest *ir) -{ - GNUNET_CONTAINER_DLL_remove (ir_head, ir_tail, ir); - GNUNET_assert (ir_total > 0); - ir_total--; - GNUNET_PEERSTORE_hello_changed_notify_cancel (ir->nc); - ir->nc = NULL; - GNUNET_free (ir); -} - - -/** - * Release @a pa data structure. - * - * @param pa data structure to release - */ -static void -free_pending_acknowledgement (struct PendingAcknowledgement *pa) -{ - struct Queue *q = pa->queue; - struct PendingMessage *pm = pa->pm; - struct DistanceVectorHop *dvh = pa->dvh; - - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "free_pending_acknowledgement\n"); - if (NULL != q) - { - GNUNET_CONTAINER_MDLL_remove (queue, q->pa_head, q->pa_tail, pa); - pa->queue = NULL; - } - if (NULL != pm) - { - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "remove pa from message\n"); - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "remove pa from message %llu\n", - pm->logging_uuid); - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "remove pa from message %u\n", - pm->pmt); - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "remove pa from message %s\n", - GNUNET_uuid2s (&pa->ack_uuid.value)); - GNUNET_CONTAINER_MDLL_remove (pm, pm->pa_head, pm->pa_tail, pa); - pa->pm = NULL; - } - if (NULL != dvh) - { - GNUNET_CONTAINER_MDLL_remove (dvh, dvh->pa_head, dvh->pa_tail, pa); - pa->queue = NULL; - } - GNUNET_assert (GNUNET_YES == - GNUNET_CONTAINER_multiuuidmap_remove (pending_acks, - &pa->ack_uuid.value, - pa)); - GNUNET_free (pa); -} - - -/** - * Free fragment tree below @e root, excluding @e root itself. - * FIXME: this does NOT seem to have the intended semantics - * based on how this is called. Seems we generally DO expect - * @a root to be free'ed itself as well! - * - * @param root root of the tree to free - */ -static void -free_fragment_tree (struct PendingMessage *root) -{ - struct PendingMessage *frag; - - while (NULL != (frag = root->head_frag)) - { - struct PendingAcknowledgement *pa; - - free_fragment_tree (frag); - while (NULL != (pa = frag->pa_head)) - { - GNUNET_CONTAINER_MDLL_remove (pm, frag->pa_head, frag->pa_tail, pa); - pa->pm = NULL; - } - GNUNET_CONTAINER_MDLL_remove (frag, root->head_frag, root->tail_frag, frag); - if (NULL != frag->qe) - { - GNUNET_assert (frag == frag->qe->pm); - frag->qe->pm = NULL; - } - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "Free frag %p\n", - frag); - GNUNET_free (frag); - } -} - - -/** - * Release memory associated with @a pm and remove @a pm from associated - * data structures. @a pm must be a top-level pending message and not - * a fragment in the tree. The entire tree is freed (if applicable). - * - * @param pm the pending message to free - */ -static void -free_pending_message (struct PendingMessage *pm) -{ - struct TransportClient *tc = pm->client; - struct VirtualLink *vl = pm->vl; - struct PendingAcknowledgement *pa; - - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "Freeing pm %p\n", - pm); - if (NULL != tc) - { - GNUNET_CONTAINER_MDLL_remove (client, - tc->details.core.pending_msg_head, - tc->details.core.pending_msg_tail, - pm); - } - if ((NULL != vl) && (NULL == pm->frag_parent)) - { - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "Removing pm %llu\n", - pm->logging_uuid); - GNUNET_CONTAINER_MDLL_remove (vl, - vl->pending_msg_head, - vl->pending_msg_tail, - pm); - } - while (NULL != (pa = pm->pa_head)) - { - if (NULL == pa) - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "free pending pa null\n"); - if (NULL == pm->pa_tail) - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "free pending pa_tail null\n"); - if (NULL == pa->prev_pa) - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "free pending pa prev null\n"); - if (NULL == pa->next_pa) - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "free pending pa next null\n"); - GNUNET_CONTAINER_MDLL_remove (pm, pm->pa_head, pm->pa_tail, pa); - pa->pm = NULL; - } - - free_fragment_tree (pm); - if (NULL != pm->qe) - { - GNUNET_assert (pm == pm->qe->pm); - pm->qe->pm = NULL; - } - if (NULL != pm->bpm) - { - free_fragment_tree (pm->bpm); - if (NULL != pm->bpm->qe) - { - struct QueueEntry *qe = pm->bpm->qe; - qe->pm = NULL; - } - GNUNET_free (pm->bpm); - } - - GNUNET_free (pm); - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "Freeing pm done\n"); -} - - -/** - * Free @a rc - * - * @param rc data structure to free - */ -static void -free_reassembly_context (struct ReassemblyContext *rc) -{ - struct VirtualLink *vl = rc->virtual_link; - - GNUNET_assert (rc == GNUNET_CONTAINER_heap_remove_node (rc->hn)); - GNUNET_assert (GNUNET_OK == - GNUNET_CONTAINER_multihashmap32_remove (vl->reassembly_map, - rc->msg_uuid.uuid, - rc)); - GNUNET_free (rc); -} - - -/** - * Task run to clean up reassembly context of a neighbour that have expired. - * - * @param cls a `struct Neighbour` - */ -static void -reassembly_cleanup_task (void *cls) -{ - struct VirtualLink *vl = cls; - struct ReassemblyContext *rc; - - vl->reassembly_timeout_task = NULL; - while (NULL != (rc = GNUNET_CONTAINER_heap_peek (vl->reassembly_heap))) - { - if (0 == GNUNET_TIME_absolute_get_remaining (rc->reassembly_timeout) - .rel_value_us) - { - free_reassembly_context (rc); - continue; - } - GNUNET_assert (NULL == vl->reassembly_timeout_task); - vl->reassembly_timeout_task = - GNUNET_SCHEDULER_add_at (rc->reassembly_timeout, - &reassembly_cleanup_task, - vl); - return; - } -} - - -/** - * function called to #free_reassembly_context(). - * - * @param cls NULL - * @param key unused - * @param value a `struct ReassemblyContext` to free - * @return #GNUNET_OK (continue iteration) - */ -static int -free_reassembly_cb (void *cls, uint32_t key, void *value) -{ - struct ReassemblyContext *rc = value; - - (void) cls; - (void) key; - free_reassembly_context (rc); - return GNUNET_OK; -} - - -/** - * Free virtual link. - * - * @param vl link data to free - */ -static void -free_virtual_link (struct VirtualLink *vl) -{ - struct PendingMessage *pm; - struct CoreSentContext *csc; - - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "free virtual link %p\n", - vl); - - if (NULL != vl->reassembly_map) - { - GNUNET_CONTAINER_multihashmap32_iterate (vl->reassembly_map, - &free_reassembly_cb, - NULL); - GNUNET_CONTAINER_multihashmap32_destroy (vl->reassembly_map); - vl->reassembly_map = NULL; - GNUNET_CONTAINER_heap_destroy (vl->reassembly_heap); - vl->reassembly_heap = NULL; - } - if (NULL != vl->reassembly_timeout_task) - { - GNUNET_SCHEDULER_cancel (vl->reassembly_timeout_task); - vl->reassembly_timeout_task = NULL; - } - while (NULL != (pm = vl->pending_msg_head)) - free_pending_message (pm); - GNUNET_assert (GNUNET_YES == - GNUNET_CONTAINER_multipeermap_remove (links, &vl->target, vl)); - if (NULL != vl->visibility_task) - { - GNUNET_SCHEDULER_cancel (vl->visibility_task); - vl->visibility_task = NULL; - } - if (NULL != vl->fc_retransmit_task) - { - GNUNET_SCHEDULER_cancel (vl->fc_retransmit_task); - vl->fc_retransmit_task = NULL; - } - while (NULL != (csc = vl->csc_head)) - { - GNUNET_CONTAINER_DLL_remove (vl->csc_head, vl->csc_tail, csc); - GNUNET_assert (vl == csc->vl); - csc->vl = NULL; - } - GNUNET_break (NULL == vl->n); - GNUNET_break (NULL == vl->dv); - GNUNET_free (vl); -} - - -/** - * Free validation state. - * - * @param vs validation state to free - */ -static void -free_validation_state (struct ValidationState *vs) -{ - GNUNET_assert ( - GNUNET_YES == - GNUNET_CONTAINER_multipeermap_remove (validation_map, &vs->pid, vs)); - GNUNET_CONTAINER_heap_remove_node (vs->hn); - vs->hn = NULL; - if (NULL != vs->sc) - { - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "store cancel\n"); - GNUNET_PEERSTORE_store_cancel (vs->sc); - vs->sc = NULL; - } - GNUNET_free (vs->address); - GNUNET_free (vs); -} - - -/** - * Lookup neighbour for peer @a pid. - * - * @param pid neighbour to look for - * @return NULL if we do not have this peer as a neighbour - */ -static struct Neighbour * -lookup_neighbour (const struct GNUNET_PeerIdentity *pid) -{ - return GNUNET_CONTAINER_multipeermap_get (neighbours, pid); -} - - -/** - * Lookup virtual link for peer @a pid. - * - * @param pid virtual link to look for - * @return NULL if we do not have this peer as a virtual link - */ -static struct VirtualLink * -lookup_virtual_link (const struct GNUNET_PeerIdentity *pid) -{ - return GNUNET_CONTAINER_multipeermap_get (links, pid); -} - - -/** - * Details about what to notify monitors about. - */ -struct MonitorEvent -{ - /** - * @deprecated To be discussed if we keep these... - */ - struct GNUNET_TIME_Absolute last_validation; - struct GNUNET_TIME_Absolute valid_until; - struct GNUNET_TIME_Absolute next_validation; - - /** - * Current round-trip time estimate. - */ - struct GNUNET_TIME_Relative rtt; - - /** - * Connection status. - */ - enum GNUNET_TRANSPORT_ConnectionStatus cs; - - /** - * Messages pending. - */ - uint32_t num_msg_pending; - - /** - * Bytes pending. - */ - uint32_t num_bytes_pending; -}; - - -/** - * Free a @a dvh. Callers MAY want to check if this was the last path to the - * `target`, and if so call #free_dv_route to also free the associated DV - * entry in #dv_routes (if not, the associated scheduler job should eventually - * take care of it). - * - * @param dvh hop to free - */ -static void -free_distance_vector_hop (struct DistanceVectorHop *dvh) -{ - struct Neighbour *n = dvh->next_hop; - struct DistanceVector *dv = dvh->dv; - struct PendingAcknowledgement *pa; - - while (NULL != (pa = dvh->pa_head)) - { - GNUNET_CONTAINER_MDLL_remove (dvh, dvh->pa_head, dvh->pa_tail, pa); - pa->dvh = NULL; - } - GNUNET_CONTAINER_MDLL_remove (neighbour, n->dv_head, n->dv_tail, dvh); - GNUNET_CONTAINER_MDLL_remove (dv, dv->dv_head, dv->dv_tail, dvh); - GNUNET_free (dvh); -} - - -/** - * Task run to check whether the hops of the @a cls still - * are validated, or if we need to core about disconnection. - * - * @param cls a `struct VirtualLink` - */ -static void -check_link_down (void *cls); - - -/** - * Send message to CORE clients that we lost a connection. - * - * @param pid peer the connection was for - */ -static void -cores_send_disconnect_info (const struct GNUNET_PeerIdentity *pid) -{ - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "Informing CORE clients about disconnect from %s\n", - GNUNET_i2s (pid)); - for (struct TransportClient *tc = clients_head; NULL != tc; tc = tc->next) - { - struct GNUNET_MQ_Envelope *env; - struct DisconnectInfoMessage *dim; - - if (CT_CORE != tc->type) - continue; - env = GNUNET_MQ_msg (dim, GNUNET_MESSAGE_TYPE_TRANSPORT_DISCONNECT); - dim->peer = *pid; - GNUNET_MQ_send (tc->mq, env); - } -} - - -/** - * Free entry in #dv_routes. First frees all hops to the target, and - * if there are no entries left, frees @a dv as well. - * - * @param dv route to free - */ -static void -free_dv_route (struct DistanceVector *dv) -{ - struct DistanceVectorHop *dvh; - - while (NULL != (dvh = dv->dv_head)) - free_distance_vector_hop (dvh); - if (NULL == dv->dv_head) - { - struct VirtualLink *vl; - - GNUNET_assert ( - GNUNET_YES == - GNUNET_CONTAINER_multipeermap_remove (dv_routes, &dv->target, dv)); - if (NULL != (vl = dv->vl)) - { - GNUNET_assert (dv == vl->dv); - vl->dv = NULL; - if (NULL == vl->n) - { - cores_send_disconnect_info (&dv->target); - free_virtual_link (vl); - } - else - { - GNUNET_SCHEDULER_cancel (vl->visibility_task); - vl->visibility_task = GNUNET_SCHEDULER_add_now (&check_link_down, vl); - } - dv->vl = NULL; - } - - if (NULL != dv->timeout_task) - { - GNUNET_SCHEDULER_cancel (dv->timeout_task); - dv->timeout_task = NULL; - } - GNUNET_free (dv); - } -} - - -/** - * Notify monitor @a tc about an event. That @a tc - * cares about the event has already been checked. - * - * Send @a tc information in @a me about a @a peer's status with - * respect to some @a address to all monitors that care. - * - * @param tc monitor to inform - * @param peer peer the information is about - * @param address address the information is about - * @param nt network type associated with @a address - * @param me detailed information to transmit - */ -static void -notify_monitor (struct TransportClient *tc, - const struct GNUNET_PeerIdentity *peer, - const char *address, - enum GNUNET_NetworkType nt, - const struct MonitorEvent *me) -{ - struct GNUNET_MQ_Envelope *env; - struct GNUNET_TRANSPORT_MonitorData *md; - size_t addr_len = strlen (address) + 1; - - env = GNUNET_MQ_msg_extra (md, - addr_len, - GNUNET_MESSAGE_TYPE_TRANSPORT_MONITOR_DATA); - md->nt = htonl ((uint32_t) nt); - md->peer = *peer; - md->last_validation = GNUNET_TIME_absolute_hton (me->last_validation); - md->valid_until = GNUNET_TIME_absolute_hton (me->valid_until); - md->next_validation = GNUNET_TIME_absolute_hton (me->next_validation); - md->rtt = GNUNET_TIME_relative_hton (me->rtt); - md->cs = htonl ((uint32_t) me->cs); - md->num_msg_pending = htonl (me->num_msg_pending); - md->num_bytes_pending = htonl (me->num_bytes_pending); - memcpy (&md[1], address, addr_len); - GNUNET_MQ_send (tc->mq, env); -} - - -/** - * Send information in @a me about a @a peer's status with respect - * to some @a address to all monitors that care. - * - * @param peer peer the information is about - * @param address address the information is about - * @param nt network type associated with @a address - * @param me detailed information to transmit - */ -static void -notify_monitors (const struct GNUNET_PeerIdentity *peer, - const char *address, - enum GNUNET_NetworkType nt, - const struct MonitorEvent *me) -{ - for (struct TransportClient *tc = clients_head; NULL != tc; tc = tc->next) - { - if (CT_MONITOR != tc->type) - continue; - if (tc->details.monitor.one_shot) - continue; - if ((GNUNET_NO == GNUNET_is_zero (&tc->details.monitor.peer)) && - (0 != GNUNET_memcmp (&tc->details.monitor.peer, peer))) - continue; - notify_monitor (tc, peer, address, nt, me); - } -} - - -/** - * Called whenever a client connects. Allocates our - * data structures associated with that client. - * - * @param cls closure, NULL - * @param client identification of the client - * @param mq message queue for the client - * @return our `struct TransportClient` - */ -static void * -client_connect_cb (void *cls, - struct GNUNET_SERVICE_Client *client, - struct GNUNET_MQ_Handle *mq) -{ - struct TransportClient *tc; - - (void) cls; - tc = GNUNET_new (struct TransportClient); - tc->client = client; - tc->mq = mq; - GNUNET_CONTAINER_DLL_insert (clients_head, clients_tail, tc); - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "Client %p of type %u connected\n", - tc, - tc->type); - return tc; -} - - -/** - * Release memory used by @a neighbour. - * - * @param neighbour neighbour entry to free - */ -static void -free_neighbour (struct Neighbour *neighbour) -{ - struct DistanceVectorHop *dvh; - struct VirtualLink *vl; - - GNUNET_assert (NULL == neighbour->queue_head); - GNUNET_assert (GNUNET_YES == - GNUNET_CONTAINER_multipeermap_remove (neighbours, - &neighbour->pid, - neighbour)); - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "Freeing neighbour\n"); - while (NULL != (dvh = neighbour->dv_head)) - { - struct DistanceVector *dv = dvh->dv; - - free_distance_vector_hop (dvh); - if (NULL == dv->dv_head) - free_dv_route (dv); - } - if (NULL != neighbour->get) - { - GNUNET_PEERSTORE_iterate_cancel (neighbour->get); - neighbour->get = NULL; - } - if (NULL != neighbour->sc) - { - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "store cancel\n"); - GNUNET_PEERSTORE_store_cancel (neighbour->sc); - neighbour->sc = NULL; - } - if (NULL != (vl = neighbour->vl)) - { - GNUNET_assert (neighbour == vl->n); - vl->n = NULL; - if (NULL == vl->dv) - { - cores_send_disconnect_info (&vl->target); - free_virtual_link (vl); - } - else - { - GNUNET_SCHEDULER_cancel (vl->visibility_task); - vl->visibility_task = GNUNET_SCHEDULER_add_now (&check_link_down, vl); - } - neighbour->vl = NULL; - } - GNUNET_free (neighbour); -} - - -/** - * Send message to CORE clients that we lost a connection. - * - * @param tc client to inform (must be CORE client) - * @param pid peer the connection is for - */ -static void -core_send_connect_info (struct TransportClient *tc, - const struct GNUNET_PeerIdentity *pid) -{ - struct GNUNET_MQ_Envelope *env; - struct ConnectInfoMessage *cim; - - GNUNET_assert (CT_CORE == tc->type); - env = GNUNET_MQ_msg (cim, GNUNET_MESSAGE_TYPE_TRANSPORT_CONNECT); - cim->id = *pid; - GNUNET_MQ_send (tc->mq, env); -} - - -/** - * Send message to CORE clients that we gained a connection - * - * @param pid peer the queue was for - */ -static void -cores_send_connect_info (const struct GNUNET_PeerIdentity *pid) -{ - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "Informing CORE clients about connection to %s\n", - GNUNET_i2s (pid)); - for (struct TransportClient *tc = clients_head; NULL != tc; tc = tc->next) - { - if (CT_CORE != tc->type) - continue; - core_send_connect_info (tc, pid); - } -} - - -/** - * We believe we are ready to transmit a message on a queue. Gives the - * message to the communicator for transmission (updating the tracker, - * and re-scheduling itself if applicable). - * - * @param cls the `struct Queue` to process transmissions for - */ -static void -transmit_on_queue (void *cls); - - -/** - * Check if the communicator has another queue with higher prio ready for sending. - */ -static unsigned int -check_for_queue_with_higher_prio (struct Queue *queue, struct Queue *queue_head) -{ - for (struct Queue *s = queue_head; NULL != s; - s = s->next_client) - { - if (s->tc->details.communicator.address_prefix != - queue->tc->details.communicator.address_prefix) - { - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "queue address %s qid %u compare with queue: address %s qid %u\n", - queue->address, - queue->qid, - s->address, - s->qid); - if ((s->priority > queue->priority) && (0 < s->q_capacity) && - (QUEUE_LENGTH_LIMIT > s->queue_length) ) - return GNUNET_YES; - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "Lower prio\n"); - } - } - return GNUNET_NO; -} - - -/** - * Called whenever something changed that might effect when we - * try to do the next transmission on @a queue using #transmit_on_queue(). - * - * @param queue the queue to do scheduling for - * @param p task priority to use, if @a queue is scheduled - */ -static void -schedule_transmit_on_queue (struct GNUNET_TIME_Relative delay, - struct Queue *queue, - enum GNUNET_SCHEDULER_Priority p) -{ - if (check_for_queue_with_higher_prio (queue, - queue->tc->details.communicator. - queue_head)) - return; - - if (queue->tc->details.communicator.total_queue_length >= - COMMUNICATOR_TOTAL_QUEUE_LIMIT) - { - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "Transmission throttled due to communicator queue limit\n"); - GNUNET_STATISTICS_update ( - GST_stats, - "# Transmission throttled due to communicator queue limit", - 1, - GNUNET_NO); - queue->idle = GNUNET_NO; - return; - } - if (queue->queue_length >= QUEUE_LENGTH_LIMIT) - { - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "Transmission throttled due to communicator queue length limit\n"); - GNUNET_STATISTICS_update (GST_stats, - "# Transmission throttled due to queue queue limit", - 1, - GNUNET_NO); - queue->idle = GNUNET_NO; - return; - } - if (0 == queue->q_capacity) - { - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "Transmission throttled due to communicator message queue qid %u has capacity %" - PRIu64 ".\n", - queue->qid, - queue->q_capacity); - GNUNET_STATISTICS_update (GST_stats, - "# Transmission throttled due to message queue capacity", - 1, - GNUNET_NO); - queue->idle = GNUNET_NO; - return; - } - /* queue might indeed be ready, schedule it */ - if (NULL != queue->transmit_task) - GNUNET_SCHEDULER_cancel (queue->transmit_task); - queue->transmit_task = - GNUNET_SCHEDULER_add_delayed_with_priority (delay, p, &transmit_on_queue, - queue); - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "Considering transmission on queue `%s' QID %llu to %s\n", - queue->address, - (unsigned long long) queue->qid, - GNUNET_i2s (&queue->neighbour->pid)); -} - - -/** - * Task run to check whether the hops of the @a cls still - * are validated, or if we need to core about disconnection. - * - * @param cls a `struct VirtualLink` - */ -static void -check_link_down (void *cls) -{ - struct VirtualLink *vl = cls; - struct DistanceVector *dv = vl->dv; - struct Neighbour *n = vl->n; - struct GNUNET_TIME_Absolute dvh_timeout; - struct GNUNET_TIME_Absolute q_timeout; - - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "Checking if link is down\n"); - vl->visibility_task = NULL; - dvh_timeout = GNUNET_TIME_UNIT_ZERO_ABS; - if (NULL != dv) - { - for (struct DistanceVectorHop *pos = dv->dv_head; NULL != pos; - pos = pos->next_dv) - dvh_timeout = GNUNET_TIME_absolute_max (dvh_timeout, - pos->path_valid_until); - if (0 == GNUNET_TIME_absolute_get_remaining (dvh_timeout).rel_value_us) - { - vl->dv->vl = NULL; - vl->dv = NULL; - } - } - q_timeout = GNUNET_TIME_UNIT_ZERO_ABS; - for (struct Queue *q = n->queue_head; NULL != q; q = q->next_neighbour) - q_timeout = GNUNET_TIME_absolute_max (q_timeout, q->validated_until); - if (0 == GNUNET_TIME_absolute_get_remaining (q_timeout).rel_value_us) - { - vl->n->vl = NULL; - vl->n = NULL; - } - if ((NULL == vl->n) && (NULL == vl->dv)) - { - cores_send_disconnect_info (&vl->target); - free_virtual_link (vl); - return; - } - vl->visibility_task = - GNUNET_SCHEDULER_add_at (GNUNET_TIME_absolute_max (q_timeout, dvh_timeout), - &check_link_down, - vl); -} - - -/** - * Free @a queue. - * - * @param queue the queue to free - */ -static void -free_queue (struct Queue *queue) -{ - struct Neighbour *neighbour = queue->neighbour; - struct TransportClient *tc = queue->tc; - struct MonitorEvent me = { .cs = GNUNET_TRANSPORT_CS_DOWN, - .rtt = GNUNET_TIME_UNIT_FOREVER_REL }; - struct QueueEntry *qe; - int maxxed; - struct PendingAcknowledgement *pa; - struct VirtualLink *vl; - - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "Cleaning up queue %u\n", queue->qid); - if (NULL != queue->transmit_task) - { - GNUNET_SCHEDULER_cancel (queue->transmit_task); - queue->transmit_task = NULL; - } - while (NULL != (pa = queue->pa_head)) - { - GNUNET_CONTAINER_MDLL_remove (queue, queue->pa_head, queue->pa_tail, pa); - pa->queue = NULL; - } - - GNUNET_CONTAINER_MDLL_remove (neighbour, - neighbour->queue_head, - neighbour->queue_tail, - queue); - GNUNET_CONTAINER_MDLL_remove (client, - tc->details.communicator.queue_head, - tc->details.communicator.queue_tail, - queue); - maxxed = (COMMUNICATOR_TOTAL_QUEUE_LIMIT <= - tc->details.communicator. - total_queue_length); - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "Cleaning up queue with length %u\n", - queue->queue_length); - while (NULL != (qe = queue->queue_head)) - { - GNUNET_CONTAINER_DLL_remove (queue->queue_head, queue->queue_tail, qe); - queue->queue_length--; - tc->details.communicator.total_queue_length--; - if (NULL != qe->pm) - { - GNUNET_assert (qe == qe->pm->qe); - qe->pm->qe = NULL; - } - GNUNET_free (qe); - } - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "Cleaning up queue with length %u\n", - queue->queue_length); - GNUNET_assert (0 == queue->queue_length); - if ((maxxed) && (COMMUNICATOR_TOTAL_QUEUE_LIMIT > - tc->details.communicator.total_queue_length)) - { - /* Communicator dropped below threshold, resume all _other_ queues */ - GNUNET_STATISTICS_update ( - GST_stats, - "# Transmission throttled due to communicator queue limit", - -1, - GNUNET_NO); - for (struct Queue *s = tc->details.communicator.queue_head; NULL != s; - s = s->next_client) - schedule_transmit_on_queue (GNUNET_TIME_UNIT_ZERO, - s, - GNUNET_SCHEDULER_PRIORITY_DEFAULT); - } - notify_monitors (&neighbour->pid, queue->address, queue->nt, &me); - GNUNET_free (queue); - - vl = lookup_virtual_link (&neighbour->pid); - if ((NULL != vl) && (neighbour == vl->n)) - { - GNUNET_SCHEDULER_cancel (vl->visibility_task); - check_link_down (vl); - } - if (NULL == neighbour->queue_head) - { - free_neighbour (neighbour); - } -} - - -/** - * Free @a ale - * - * @param ale address list entry to free - */ -static void -free_address_list_entry (struct AddressListEntry *ale) -{ - struct TransportClient *tc = ale->tc; - - GNUNET_CONTAINER_DLL_remove (tc->details.communicator.addr_head, - tc->details.communicator.addr_tail, - ale); - if (NULL != ale->sc) - { - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "store cancel\n"); - GNUNET_PEERSTORE_store_cancel (ale->sc); - ale->sc = NULL; - } - if (NULL != ale->st) - { - GNUNET_SCHEDULER_cancel (ale->st); - ale->st = NULL; - } - GNUNET_free (ale); -} - - -/** - * Stop the peer request in @a value. - * - * @param cls a `struct TransportClient` that no longer makes the request - * @param pid the peer's identity - * @param value a `struct PeerRequest` - * @return #GNUNET_YES (always) - */ -static int -stop_peer_request (void *cls, - const struct GNUNET_PeerIdentity *pid, - void *value) -{ - struct TransportClient *tc = cls; - struct PeerRequest *pr = value; - - GNUNET_PEERSTORE_hello_changed_notify_cancel (pr->nc); - pr->nc = NULL; - GNUNET_assert ( - GNUNET_YES == - GNUNET_CONTAINER_multipeermap_remove (tc->details.application.requests, - pid, - pr)); - GNUNET_free (pr); - - return GNUNET_OK; -} - - -static void -do_shutdown (void *cls); - -/** - * Called whenever a client is disconnected. Frees our - * resources associated with that client. - * - * @param cls closure, NULL - * @param client identification of the client - * @param app_ctx our `struct TransportClient` - */ -static void -client_disconnect_cb (void *cls, - struct GNUNET_SERVICE_Client *client, - void *app_ctx) -{ - struct TransportClient *tc = app_ctx; - - (void) cls; - (void) client; - GNUNET_CONTAINER_DLL_remove (clients_head, clients_tail, tc); - switch (tc->type) - { - case CT_NONE: - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "Unknown Client %p disconnected, cleaning up.\n", - tc); - break; - - case CT_CORE: { - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "CORE Client %p disconnected, cleaning up.\n", - tc); - - struct PendingMessage *pm; - - while (NULL != (pm = tc->details.core.pending_msg_head)) - { - GNUNET_CONTAINER_MDLL_remove (client, - tc->details.core.pending_msg_head, - tc->details.core.pending_msg_tail, - pm); - pm->client = NULL; - } - } - break; - - case CT_MONITOR: - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "MONITOR Client %p disconnected, cleaning up.\n", - tc); - - break; - - case CT_COMMUNICATOR: { - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "COMMUNICATOR Client %p disconnected, cleaning up.\n", - tc); - - struct Queue *q; - struct AddressListEntry *ale; - - while (NULL != (q = tc->details.communicator.queue_head)) - free_queue (q); - while (NULL != (ale = tc->details.communicator.addr_head)) - free_address_list_entry (ale); - GNUNET_free (tc->details.communicator.address_prefix); - } - break; - - case CT_APPLICATION: - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "APPLICATION Client %p disconnected, cleaning up.\n", - tc); - - GNUNET_CONTAINER_multipeermap_iterate (tc->details.application.requests, - &stop_peer_request, - tc); - GNUNET_CONTAINER_multipeermap_destroy (tc->details.application.requests); - break; - } - GNUNET_free (tc); - if ((GNUNET_YES == in_shutdown) && (NULL == clients_head)) - { - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - "Our last client disconnected\n"); - do_shutdown (cls); - } -} - - -/** - * Iterator telling new CORE client about all existing - * connections to peers. - * - * @param cls the new `struct TransportClient` - * @param pid a connected peer - * @param value the `struct Neighbour` with more information - * @return #GNUNET_OK (continue to iterate) - */ -static int -notify_client_connect_info (void *cls, - const struct GNUNET_PeerIdentity *pid, - void *value) -{ - struct TransportClient *tc = cls; - struct Neighbour *n = value; - struct VirtualLink *vl = n->vl; - - if ((NULL == vl) || (GNUNET_NO == vl->confirmed)) - return GNUNET_OK; - - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "Telling new CORE client about existing connection to %s\n", - GNUNET_i2s (pid)); - core_send_connect_info (tc, pid); - return GNUNET_OK; -} - - -/** - * Initialize a "CORE" client. We got a start message from this - * client, so add it to the list of clients for broadcasting of - * inbound messages. - * - * @param cls the client - * @param start the start message that was sent - */ -static void -handle_client_start (void *cls, const struct StartMessage *start) -{ - struct TransportClient *tc = cls; - uint32_t options; - - options = ntohl (start->options); - if ((0 != (1 & options)) && - (0 != GNUNET_memcmp (&start->self, &GST_my_identity))) - { - /* client thinks this is a different peer, reject */ - GNUNET_break (0); - GNUNET_SERVICE_client_drop (tc->client); - return; - } - if (CT_NONE != tc->type) - { - GNUNET_break (0); - GNUNET_SERVICE_client_drop (tc->client); - return; - } - tc->type = CT_CORE; - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "New CORE client with PID %s registered\n", - GNUNET_i2s (&start->self)); - GNUNET_CONTAINER_multipeermap_iterate (neighbours, - ¬ify_client_connect_info, - tc); - GNUNET_SERVICE_client_continue (tc->client); -} - - -/** - * Client asked for transmission to a peer. Process the request. - * - * @param cls the client - * @param obm the send message that was sent - */ -static int -check_client_send (void *cls, const struct OutboundMessage *obm) -{ - struct TransportClient *tc = cls; - uint16_t size; - const struct GNUNET_MessageHeader *obmm; - - if (CT_CORE != tc->type) - { - GNUNET_break (0); - return GNUNET_SYSERR; - } - size = ntohs (obm->header.size) - sizeof(struct OutboundMessage); - if (size < sizeof(struct GNUNET_MessageHeader)) - { - GNUNET_break (0); - return GNUNET_SYSERR; - } - obmm = (const struct GNUNET_MessageHeader *) &obm[1]; - if (size != ntohs (obmm->size)) - { - GNUNET_break (0); - return GNUNET_SYSERR; - } - return GNUNET_OK; -} - - -/** - * Send a response to the @a pm that we have processed a "send" - * request. Sends a confirmation to the "core" client responsible for - * the original request and free's @a pm. - * - * @param pm handle to the original pending message - */ -static void -client_send_response (struct PendingMessage *pm) -{ - struct TransportClient *tc = pm->client; - struct VirtualLink *vl = pm->vl; - - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "client send response\n"); - if (NULL != tc) - { - struct GNUNET_MQ_Envelope *env; - struct SendOkMessage *so_msg; - - env = GNUNET_MQ_msg (so_msg, GNUNET_MESSAGE_TYPE_TRANSPORT_SEND_OK); - so_msg->peer = vl->target; - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "Confirming transmission of <%llu> to %s\n", - pm->logging_uuid, - GNUNET_i2s (&vl->target)); - GNUNET_MQ_send (tc->mq, env); - } - free_pending_message (pm); -} - - -/** - * Pick @a hops_array_length random DV paths satisfying @a options - * - * @param dv data structure to pick paths from - * @param options constraints to satisfy - * @param[out] hops_array set to the result - * @param hops_array_length length of the @a hops_array - * @return number of entries set in @a hops_array - */ -static unsigned int -pick_random_dv_hops (const struct DistanceVector *dv, - enum RouteMessageOptions options, - struct DistanceVectorHop **hops_array, - unsigned int hops_array_length) -{ - uint64_t choices[hops_array_length]; - uint64_t num_dv; - unsigned int dv_count; - - /* Pick random vectors, but weighted by distance, giving more weight - to shorter vectors */ - num_dv = 0; - dv_count = 0; - for (struct DistanceVectorHop *pos = dv->dv_head; NULL != pos; - pos = pos->next_dv) - { - if ((0 == (options & RMO_UNCONFIRMED_ALLOWED)) && - (GNUNET_TIME_absolute_get_remaining (pos->path_valid_until) - .rel_value_us == 0)) - continue; /* pos unconfirmed and confirmed required */ - num_dv += MAX_DV_HOPS_ALLOWED - pos->distance; - dv_count++; - } - if (0 == dv_count) - return 0; - if (dv_count <= hops_array_length) - { - dv_count = 0; - for (struct DistanceVectorHop *pos = dv->dv_head; NULL != pos; - pos = pos->next_dv) - hops_array[dv_count++] = pos; - return dv_count; - } - for (unsigned int i = 0; i < hops_array_length; i++) - { - int ok = GNUNET_NO; - while (GNUNET_NO == ok) - { - choices[i] = - GNUNET_CRYPTO_random_u64 (GNUNET_CRYPTO_QUALITY_WEAK, num_dv); - ok = GNUNET_YES; - for (unsigned int j = 0; j < i; j++) - if (choices[i] == choices[j]) - { - ok = GNUNET_NO; - break; - } - } - } - dv_count = 0; - num_dv = 0; - for (struct DistanceVectorHop *pos = dv->dv_head; NULL != pos; - pos = pos->next_dv) - { - uint32_t delta = MAX_DV_HOPS_ALLOWED - pos->distance; - - if ((0 == (options & RMO_UNCONFIRMED_ALLOWED)) && - (GNUNET_TIME_absolute_get_remaining (pos->path_valid_until) - .rel_value_us == 0)) - continue; /* pos unconfirmed and confirmed required */ - for (unsigned int i = 0; i < hops_array_length; i++) - if ((num_dv <= choices[i]) && (num_dv + delta > choices[i])) - hops_array[dv_count++] = pos; - num_dv += delta; - } - return dv_count; -} - - -/** - * Communicator started. Test message is well-formed. - * - * @param cls the client - * @param cam the send message that was sent - */ -static int -check_communicator_available ( - void *cls, - const struct GNUNET_TRANSPORT_CommunicatorAvailableMessage *cam) -{ - struct TransportClient *tc = cls; - uint16_t size; - - if (CT_NONE != tc->type) - { - GNUNET_break (0); - return GNUNET_SYSERR; - } - tc->type = CT_COMMUNICATOR; - size = ntohs (cam->header.size) - sizeof(*cam); - if (0 == size) - return GNUNET_OK; /* receive-only communicator */ - GNUNET_MQ_check_zero_termination (cam); - return GNUNET_OK; -} - - -/** - * Send ACK to communicator (if requested) and free @a cmc. - * - * @param cmc context for which we are done handling the message - */ -static void -finish_cmc_handling_with_continue (struct CommunicatorMessageContext *cmc, - unsigned - int continue_client) -{ - if (0 != ntohl (cmc->im.fc_on)) - { - /* send ACK when done to communicator for flow control! */ - struct GNUNET_MQ_Envelope *env; - struct GNUNET_TRANSPORT_IncomingMessageAck *ack; - - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "Acknowledge message with flow control id %" PRIu64 "\n", - cmc->im.fc_id); - env = GNUNET_MQ_msg (ack, GNUNET_MESSAGE_TYPE_TRANSPORT_INCOMING_MSG_ACK); - ack->reserved = htonl (0); - ack->fc_id = cmc->im.fc_id; - ack->sender = cmc->im.neighbour_sender; - GNUNET_MQ_send (cmc->tc->mq, env); - } - - if (GNUNET_YES == continue_client) - { - GNUNET_SERVICE_client_continue (cmc->tc->client); - } - GNUNET_free (cmc); -} - - -static void -finish_cmc_handling (struct CommunicatorMessageContext *cmc) -{ - finish_cmc_handling_with_continue (cmc, GNUNET_YES); -} - - -/** - * Client confirms that it is done handling message(s) to a particular - * peer. We may now provide more messages to CORE for this peer. - * - * Notifies the respective queues that more messages can now be received. - * - * @param cls the client - * @param rom the message that was sent - */ -static void -handle_client_recv_ok (void *cls, const struct RecvOkMessage *rom) -{ - struct TransportClient *tc = cls; - struct VirtualLink *vl; - uint32_t delta; - struct CommunicatorMessageContext *cmc; - - if (CT_CORE != tc->type) - { - GNUNET_break (0); - GNUNET_SERVICE_client_drop (tc->client); - return; - } - vl = lookup_virtual_link (&rom->peer); - if ((NULL == vl) || (GNUNET_NO == vl->confirmed)) - { - GNUNET_STATISTICS_update (GST_stats, - "# RECV_OK dropped: virtual link unknown", - 1, - GNUNET_NO); - GNUNET_SERVICE_client_continue (tc->client); - return; - } - delta = ntohl (rom->increase_window_delta); - vl->core_recv_window += delta; - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "CORE ack receiving message, increased CORE recv window to %d\n", - vl->core_recv_window); - GNUNET_SERVICE_client_continue (tc->client); - if (vl->core_recv_window <= 0) - return; - /* resume communicators */ - while (NULL != (cmc = vl->cmc_tail)) - { - GNUNET_CONTAINER_DLL_remove (vl->cmc_head, vl->cmc_tail, cmc); - finish_cmc_handling (cmc); - } -} - - -/** - * Communicator started. Process the request. - * - * @param cls the client - * @param cam the send message that was sent - */ -static void -handle_communicator_available ( - void *cls, - const struct GNUNET_TRANSPORT_CommunicatorAvailableMessage *cam) -{ - struct TransportClient *tc = cls; - uint16_t size; - - size = ntohs (cam->header.size) - sizeof(*cam); - if (0 == size) - { - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "Receive-only communicator connected\n"); - return; /* receive-only communicator */ - } - tc->details.communicator.address_prefix = - GNUNET_strdup ((const char *) &cam[1]); - tc->details.communicator.cc = - (enum GNUNET_TRANSPORT_CommunicatorCharacteristics) ntohl (cam->cc); - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "Communicator with prefix `%s' connected\n", - tc->details.communicator.address_prefix); - GNUNET_SERVICE_client_continue (tc->client); -} - - -/** - * Communicator requests backchannel transmission. Check the request. - * - * @param cls the client - * @param cb the send message that was sent - * @return #GNUNET_OK if message is well-formed - */ -static int -check_communicator_backchannel ( - void *cls, - const struct GNUNET_TRANSPORT_CommunicatorBackchannel *cb) -{ - const struct GNUNET_MessageHeader *inbox; - const char *is; - uint16_t msize; - uint16_t isize; - - (void) cls; - msize = ntohs (cb->header.size) - sizeof(*cb); - inbox = (const struct GNUNET_MessageHeader *) &cb[1]; - isize = ntohs (inbox->size); - if (isize >= msize) - { - GNUNET_break (0); - return GNUNET_SYSERR; - } - is = (const char *) inbox; - is += isize; - msize -= isize; - GNUNET_assert (0 < msize); - if ('\0' != is[msize - 1]) - { - GNUNET_break (0); - return GNUNET_SYSERR; - } - return GNUNET_OK; -} - - -/** - * Sign ephemeral keys in our @a dv are current. - * - * @param[in,out] dv virtual link to update ephemeral for - */ -static void -sign_ephemeral (struct DistanceVector *dv) -{ - struct EphemeralConfirmationPS ec; - - dv->monotime = GNUNET_TIME_absolute_get_monotonic (GST_cfg); - dv->ephemeral_validity = - GNUNET_TIME_absolute_add (dv->monotime, EPHEMERAL_VALIDITY); - ec.purpose.purpose = htonl (GNUNET_SIGNATURE_PURPOSE_TRANSPORT_EPHEMERAL); - ec.target = dv->target; - ec.ephemeral_key = dv->ephemeral_key; - ec.sender_monotonic_time = GNUNET_TIME_absolute_hton (dv->monotime); - ec.purpose.size = htonl (sizeof(ec)); - GNUNET_CRYPTO_eddsa_sign (GST_my_private_key, - &ec, - &dv->sender_sig); -} - - -/** - * Send the message @a payload on @a queue. - * - * @param queue the queue to use for transmission - * @param pm pending message to update once transmission is done, may be NULL! - * @param payload the payload to send (encapsulated in a - * #GNUNET_MESSAGE_TYPE_TRANSPORT_SEND_MSG). - * @param payload_size number of bytes in @a payload - */ -static void -queue_send_msg (struct Queue *queue, - struct PendingMessage *pm, - const void *payload, - size_t payload_size) -{ - struct Neighbour *n = queue->neighbour; - struct GNUNET_TRANSPORT_SendMessageTo *smt; - struct GNUNET_MQ_Envelope *env; - struct PendingAcknowledgement *pa; - - GNUNET_log ( - GNUNET_ERROR_TYPE_DEBUG, - "Queueing %u bytes of payload for transmission <%llu> on queue %llu to %s\n", - (unsigned int) payload_size, - (NULL == pm) ? 0 : pm->logging_uuid, - (unsigned long long) queue->qid, - GNUNET_i2s (&queue->neighbour->pid)); - env = GNUNET_MQ_msg_extra (smt, - payload_size, - GNUNET_MESSAGE_TYPE_TRANSPORT_SEND_MSG); - smt->qid = htonl (queue->qid); - smt->mid = GNUNET_htonll (queue->mid_gen); - smt->receiver = n->pid; - memcpy (&smt[1], payload, payload_size); - { - /* Pass the env to the communicator of queue for transmission. */ - struct QueueEntry *qe; - - qe = GNUNET_new (struct QueueEntry); - qe->mid = queue->mid_gen; - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "Create QueueEntry with MID %" PRIu64 - " and QID %u and prefix %s\n", - qe->mid, - queue->qid, - queue->tc->details.communicator.address_prefix); - queue->mid_gen++; - qe->queue = queue; - if (NULL != pm) - { - qe->pm = pm; - // TODO Why do we have a retransmission. When we know, make decision if we still want this. - // GNUNET_assert (NULL == pm->qe); - if (NULL != pm->qe) - { - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "Retransmitting message <%llu> remove pm from qe with MID: %llu \n", - pm->logging_uuid, - (unsigned long long) pm->qe->mid); - pm->qe->pm = NULL; - } - pm->qe = qe; - } - GNUNET_CONTAINER_DLL_insert (queue->queue_head, queue->queue_tail, qe); - GNUNET_assert (CT_COMMUNICATOR == queue->tc->type); - if (0 == queue->q_capacity) - { - // Messages without FC or fragments can get here. - if (NULL != pm) - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "Message %llu (pm type %u) was not send because queue has no capacity.\n", - pm->logging_uuid, - pm->pmt); - GNUNET_free (env); - return; - } - queue->queue_length++; - queue->tc->details.communicator.total_queue_length++; - if (GNUNET_NO == queue->unlimited_length) - queue->q_capacity--; - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "Queue %s with qid %u has capacity %" PRIu64 "\n", - queue->address, - queue->qid, - queue->q_capacity); - if (COMMUNICATOR_TOTAL_QUEUE_LIMIT == - queue->tc->details.communicator.total_queue_length) - queue->idle = GNUNET_NO; - if (QUEUE_LENGTH_LIMIT == queue->queue_length) - queue->idle = GNUNET_NO; - if (0 == queue->q_capacity) - queue->idle = GNUNET_NO; - - if (NULL != pm && NULL != (pa = pm->pa_head)) - { - while (pm != pa->pm) - pa = pa->next_pa; - pa->num_send++; - } - // GNUNET_CONTAINER_multiuuidmap_get (pending_acks, &ack[i].ack_uuid.value); - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "Sending message MID %" PRIu64 - " of type %u (%u) and size %lu with MQ %p QID %u\n", - GNUNET_ntohll (smt->mid), - ntohs (((const struct GNUNET_MessageHeader *) payload)->type), - ntohs (smt->header.size), - (unsigned long) payload_size, - queue->tc->mq, - queue->qid); - GNUNET_MQ_send (queue->tc->mq, env); - } -} - - -/** - * Pick a queue of @a n under constraints @a options and schedule - * transmission of @a hdr. - * - * @param n neighbour to send to - * @param hdr message to send as payload - * @param options whether queues must be confirmed or not, - * and whether we may pick multiple (2) queues - * @return expected RTT for transmission, #GNUNET_TIME_UNIT_FOREVER_REL if sending failed - */ -static struct GNUNET_TIME_Relative -route_via_neighbour (const struct Neighbour *n, - const struct GNUNET_MessageHeader *hdr, - enum RouteMessageOptions options) -{ - struct GNUNET_TIME_Absolute now; - unsigned int candidates; - unsigned int sel1; - unsigned int sel2; - struct GNUNET_TIME_Relative rtt; - - /* Pick one or two 'random' queues from n (under constraints of options) */ - now = GNUNET_TIME_absolute_get (); - /* FIXME-OPTIMIZE: give queues 'weights' and pick proportional to - weight in the future; weight could be assigned by observed - bandwidth (note: not sure if we should do this for this type - of control traffic though). */ - candidates = 0; - for (struct Queue *pos = n->queue_head; NULL != pos; - pos = pos->next_neighbour) - { - if ((0 != (options & RMO_UNCONFIRMED_ALLOWED)) || - (pos->validated_until.abs_value_us > now.abs_value_us)) - candidates++; - } - if (0 == candidates) - { - /* This can happen rarely if the last confirmed queue timed - out just as we were beginning to process this message. */ - GNUNET_log (GNUNET_ERROR_TYPE_INFO, - "Could not route message of type %u to %s: no valid queue\n", - ntohs (hdr->type), - GNUNET_i2s (&n->pid)); - GNUNET_STATISTICS_update (GST_stats, - "# route selection failed (all no valid queue)", - 1, - GNUNET_NO); - return GNUNET_TIME_UNIT_FOREVER_REL; - } - - rtt = GNUNET_TIME_UNIT_FOREVER_REL; - sel1 = GNUNET_CRYPTO_random_u32 (GNUNET_CRYPTO_QUALITY_WEAK, candidates); - if (0 == (options & RMO_REDUNDANT)) - sel2 = candidates; /* picks none! */ - else - sel2 = GNUNET_CRYPTO_random_u32 (GNUNET_CRYPTO_QUALITY_WEAK, candidates); - candidates = 0; - for (struct Queue *pos = n->queue_head; NULL != pos; - pos = pos->next_neighbour) - { - if ((0 != (options & RMO_UNCONFIRMED_ALLOWED)) || - (pos->validated_until.abs_value_us > now.abs_value_us)) - { - if ((sel1 == candidates) || (sel2 == candidates)) - { - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "Routing message of type %u to %s using %s (#%u)\n", - ntohs (hdr->type), - GNUNET_i2s (&n->pid), - pos->address, - (sel1 == candidates) ? 1 : 2); - rtt = GNUNET_TIME_relative_min (rtt, pos->pd.aged_rtt); - queue_send_msg (pos, NULL, hdr, ntohs (hdr->size)); - } - candidates++; - } - } - return rtt; -} - - -/** - * Structure of the key material used to encrypt backchannel messages. - */ -struct DVKeyState -{ - /** - * State of our block cipher. - */ - gcry_cipher_hd_t cipher; - - /** - * Actual key material. - */ - struct - { - /** - * Key used for HMAC calculations (via #GNUNET_CRYPTO_hmac()). - */ - struct GNUNET_CRYPTO_AuthKey hmac_key; - - /** - * Symmetric key to use for encryption. - */ - char aes_key[256 / 8]; - - /** - * Counter value to use during setup. - */ - char aes_ctr[128 / 8]; - } material; -}; - - -/** - * Given the key material in @a km and the initialization vector - * @a iv, setup the key material for the backchannel in @a key. - * - * @param km raw master secret - * @param iv initialization vector - * @param[out] key symmetric cipher and HMAC state to generate - */ -static void -dv_setup_key_state_from_km (const struct GNUNET_HashCode *km, - const struct GNUNET_ShortHashCode *iv, - struct DVKeyState *key) -{ - /* must match what we defive from decapsulated key */ - GNUNET_assert (GNUNET_YES == - GNUNET_CRYPTO_kdf (&key->material, - sizeof(key->material), - "transport-backchannel-key", - strlen ("transport-backchannel-key"), - km, - sizeof(*km), - iv, - sizeof(*iv), - NULL)); - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "Deriving backchannel key based on KM %s and IV %s\n", - GNUNET_h2s (km), - GNUNET_sh2s (iv)); - GNUNET_assert (0 == gcry_cipher_open (&key->cipher, - GCRY_CIPHER_AES256 /* low level: go for speed */, - GCRY_CIPHER_MODE_CTR, - 0 /* flags */)); - GNUNET_assert (0 == gcry_cipher_setkey (key->cipher, - &key->material.aes_key, - sizeof(key->material.aes_key))); - gcry_cipher_setctr (key->cipher, - &key->material.aes_ctr, - sizeof(key->material.aes_ctr)); -} - - -/** - * Do HMAC calculation for backchannel messages over @a data using key - * material from @a key. - * - * @param key key material (from DH) - * @param[out] hmac set to the HMAC - * @param data data to perform HMAC calculation over - * @param data_size number of bytes in @a data - */ -static void -dv_hmac (const struct DVKeyState *key, - struct GNUNET_HashCode *hmac, - const void *data, - size_t data_size) -{ - GNUNET_CRYPTO_hmac (&key->material.hmac_key, data, data_size, hmac); -} - - -/** - * Perform backchannel encryption using symmetric secret in @a key - * to encrypt data from @a in to @a dst. - * - * @param[in,out] key key material to use - * @param dst where to write the result - * @param in input data to encrypt (plaintext) - * @param in_size number of bytes of input in @a in and available at @a dst - */ -static void -dv_encrypt (struct DVKeyState *key, const void *in, void *dst, size_t in_size) -{ - GNUNET_assert (0 == - gcry_cipher_encrypt (key->cipher, dst, in_size, in, in_size)); -} - - -/** - * Perform backchannel encryption using symmetric secret in @a key - * to encrypt data from @a in to @a dst. - * - * @param[in,out] key key material to use - * @param ciph cipher text to decrypt - * @param[out] out output data to generate (plaintext) - * @param out_size number of bytes of input in @a ciph and available in @a out - * @return GNUNET_OK on success - */ -static enum GNUNET_GenericReturnValue -dv_decrypt (struct DVKeyState *key, - void *out, - const void *ciph, - size_t out_size) -{ - return (0 == - gcry_cipher_decrypt (key->cipher, - out, out_size, - ciph, out_size)) ? GNUNET_OK : GNUNET_SYSERR; -} - - -/** - * Clean up key material in @a key. - * - * @param key key material to clean up (memory must not be free'd!) - */ -static void -dv_key_clean (struct DVKeyState *key) -{ - gcry_cipher_close (key->cipher); - GNUNET_CRYPTO_zero_keys (&key->material, sizeof(key->material)); -} - - -/** - * Function to call to further operate on the now DV encapsulated - * message @a hdr, forwarding it via @a next_hop under respect of - * @a options. - * - * @param cls closure - * @param next_hop next hop of the DV path - * @param hdr encapsulated message, technically a `struct TransportDFBoxMessage` - * @param options options of the original message - */ -typedef void (*DVMessageHandler) (void *cls, - struct Neighbour *next_hop, - const struct GNUNET_MessageHeader *hdr, - enum RouteMessageOptions options); - -/** - * Pick a path of @a dv under constraints @a options and schedule - * transmission of @a hdr. - * - * @param target neighbour to ultimately send to - * @param num_dvhs length of the @a dvhs array - * @param dvhs array of hops to send the message to - * @param hdr message to send as payload - * @param use function to call with the encapsulated message - * @param use_cls closure for @a use - * @param options whether path must be confirmed or not, to be passed to @a use - * @param without_fc shall this TransportDVBoxMessage be forwarded without flow control. - * @return expected RTT for transmission, #GNUNET_TIME_UNIT_FOREVER_REL if sending failed - */ -static struct GNUNET_TIME_Relative -encapsulate_for_dv (struct DistanceVector *dv, - unsigned int num_dvhs, - struct DistanceVectorHop **dvhs, - const struct GNUNET_MessageHeader *hdr, - DVMessageHandler use, - void *use_cls, - enum RouteMessageOptions options, - enum GNUNET_GenericReturnValue without_fc) -{ - struct TransportDVBoxMessage box_hdr; - struct TransportDVBoxPayloadP payload_hdr; - uint16_t enc_body_size = ntohs (hdr->size); - char enc[sizeof(struct TransportDVBoxPayloadP) + enc_body_size] GNUNET_ALIGN; - struct DVKeyState *key; - struct GNUNET_TIME_Relative rtt; - struct GNUNET_HashCode k; - - key = GNUNET_new (struct DVKeyState); - /* Encrypt payload */ - box_hdr.header.type = htons (GNUNET_MESSAGE_TYPE_TRANSPORT_DV_BOX); - box_hdr.total_hops = htons (0); - box_hdr.without_fc = htons (without_fc); - // update_ephemeral (dv); - if (0 == - GNUNET_TIME_absolute_get_remaining (dv->ephemeral_validity).rel_value_us) - { - GNUNET_CRYPTO_eddsa_kem_encaps (&dv->target.public_key, - &dv->ephemeral_key, - &k); - sign_ephemeral (dv); - } - box_hdr.ephemeral_key = dv->ephemeral_key; - payload_hdr.sender_sig = dv->sender_sig; - - GNUNET_CRYPTO_random_block (GNUNET_CRYPTO_QUALITY_NONCE, - &box_hdr.iv, - sizeof(box_hdr.iv)); - // We are creating this key, so this must work. - // FIXME: Possibly also add return values here. We are processing - // Input from other peers... - dv_setup_key_state_from_km (&k, &box_hdr.iv, key); - payload_hdr.sender = GST_my_identity; - payload_hdr.monotonic_time = GNUNET_TIME_absolute_hton (dv->monotime); - dv_encrypt (key, &payload_hdr, enc, sizeof(payload_hdr)); - dv_encrypt (key, - hdr, - &enc[sizeof(struct TransportDVBoxPayloadP)], - enc_body_size); - dv_hmac (key, &box_hdr.hmac, enc, sizeof(enc)); - dv_key_clean (key); - rtt = GNUNET_TIME_UNIT_FOREVER_REL; - /* For each selected path, take the pre-computed header and body - and add the path in the middle of the message; then send it. */ - for (unsigned int i = 0; i < num_dvhs; i++) - { - struct DistanceVectorHop *dvh = dvhs[i]; - unsigned int num_hops = dvh->distance + 1; - char buf[sizeof(struct TransportDVBoxMessage) - + sizeof(struct GNUNET_PeerIdentity) * num_hops - + sizeof(struct TransportDVBoxPayloadP) - + enc_body_size] GNUNET_ALIGN; - struct GNUNET_PeerIdentity *dhops; - - box_hdr.header.size = htons (sizeof(buf)); - box_hdr.orig_size = htons (sizeof(buf)); - box_hdr.num_hops = htons (num_hops); - memcpy (buf, &box_hdr, sizeof(box_hdr)); - dhops = (struct GNUNET_PeerIdentity *) &buf[sizeof(box_hdr)]; - memcpy (dhops, - dvh->path, - dvh->distance * sizeof(struct GNUNET_PeerIdentity)); - dhops[dvh->distance] = dv->target; - if (GNUNET_EXTRA_LOGGING > 0) - { - char *path; - - path = GNUNET_strdup (GNUNET_i2s (&GST_my_identity)); - for (unsigned int j = 0; j < num_hops; j++) - { - char *tmp; - - GNUNET_asprintf (&tmp, "%s-%s", path, GNUNET_i2s (&dhops[j])); - GNUNET_free (path); - path = tmp; - } - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "Routing message of type %u to %s using DV (#%u/%u) via %s\n", - ntohs (hdr->type), - GNUNET_i2s (&dv->target), - i + 1, - num_dvhs, - path); - GNUNET_free (path); - } - rtt = GNUNET_TIME_relative_min (rtt, dvh->pd.aged_rtt); - memcpy (&dhops[num_hops], enc, sizeof(enc)); - use (use_cls, - dvh->next_hop, - (const struct GNUNET_MessageHeader *) buf, - options); - GNUNET_free (key); - } - return rtt; -} - - -/** - * Wrapper around #route_via_neighbour() that matches the - * #DVMessageHandler structure. - * - * @param cls unused - * @param next_hop where to send next - * @param hdr header of the message to send - * @param options message options for queue selection - */ -static void -send_dv_to_neighbour (void *cls, - struct Neighbour *next_hop, - const struct GNUNET_MessageHeader *hdr, - enum RouteMessageOptions options) -{ - (void) cls; - (void) route_via_neighbour (next_hop, hdr, RMO_UNCONFIRMED_ALLOWED); -} - - -/** - * We need to transmit @a hdr to @a target. If necessary, this may - * involve DV routing. This function routes without applying flow - * control or congestion control and should only be used for control - * traffic. - * - * @param target peer to receive @a hdr - * @param hdr header of the message to route and #GNUNET_free() - * @param options which transmission channels are allowed - * @return expected RTT for transmission, #GNUNET_TIME_UNIT_FOREVER_REL if sending failed - */ -static struct GNUNET_TIME_Relative -route_control_message_without_fc (struct VirtualLink *vl, -// route_control_message_without_fc (const struct GNUNET_PeerIdentity *target, - const struct GNUNET_MessageHeader *hdr, - enum RouteMessageOptions options) -{ - // struct VirtualLink *vl; - struct Neighbour *n; - struct DistanceVector *dv; - struct GNUNET_TIME_Relative rtt1; - struct GNUNET_TIME_Relative rtt2; - const struct GNUNET_PeerIdentity *target = &vl->target; - - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "Trying to route message of type %u to %s without fc\n", - ntohs (hdr->type), - GNUNET_i2s (target)); - - // TODO Do this elsewhere. vl should be given as parameter to method. - // vl = lookup_virtual_link (target); - GNUNET_assert (NULL != vl && GNUNET_YES == vl->confirmed); - if (NULL == vl) - return GNUNET_TIME_UNIT_FOREVER_REL; - n = vl->n; - dv = (0 != (options & RMO_DV_ALLOWED)) ? vl->dv : NULL; - if (0 == (options & RMO_UNCONFIRMED_ALLOWED)) - { - /* if confirmed is required, and we do not have anything - confirmed, drop respective options */ - if (NULL == n) - n = lookup_neighbour (target); - if ((NULL == dv) && (0 != (options & RMO_DV_ALLOWED))) - dv = GNUNET_CONTAINER_multipeermap_get (dv_routes, target); - } - if ((NULL == n) && (NULL == dv)) - { - GNUNET_log (GNUNET_ERROR_TYPE_INFO, - "Cannot route message of type %u to %s: no route\n", - ntohs (hdr->type), - GNUNET_i2s (target)); - GNUNET_STATISTICS_update (GST_stats, - "# Messages dropped in routing: no acceptable method", - 1, - GNUNET_NO); - return GNUNET_TIME_UNIT_FOREVER_REL; - } - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "Routing message of type %u to %s with options %X\n", - ntohs (hdr->type), - GNUNET_i2s (target), - (unsigned int) options); - /* If both dv and n are possible and we must choose: - flip a coin for the choice between the two; for now 50/50 */ - if ((NULL != n) && (NULL != dv) && (0 == (options & RMO_REDUNDANT))) - { - if (0 == GNUNET_CRYPTO_random_u32 (GNUNET_CRYPTO_QUALITY_WEAK, 2)) - n = NULL; - else - dv = NULL; - } - if ((NULL != n) && (NULL != dv)) - options &= ~RMO_REDUNDANT; /* We will do one DV and one direct, that's - enough for redundancy, so clear the flag. */ - rtt1 = GNUNET_TIME_UNIT_FOREVER_REL; - rtt2 = GNUNET_TIME_UNIT_FOREVER_REL; - if (NULL != n) - { - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "Try to route message of type %u to %s without fc via neighbour\n", - ntohs (hdr->type), - GNUNET_i2s (target)); - rtt1 = route_via_neighbour (n, hdr, options); - } - if (NULL != dv) - { - struct DistanceVectorHop *hops[2]; - unsigned int res; - - res = pick_random_dv_hops (dv, - options, - hops, - (0 == (options & RMO_REDUNDANT)) ? 1 : 2); - if (0 == res) - { - GNUNET_log (GNUNET_ERROR_TYPE_INFO, - "Failed to route message, could not determine DV path\n"); - return rtt1; - } - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "encapsulate_for_dv 1\n"); - rtt2 = encapsulate_for_dv (dv, - res, - hops, - hdr, - &send_dv_to_neighbour, - NULL, - options & (~RMO_REDUNDANT), - GNUNET_YES); - } - return GNUNET_TIME_relative_min (rtt1, rtt2); -} - - -static void -consider_sending_fc (void *cls); - -/** - * Something changed on the virtual link with respect to flow - * control. Consider retransmitting the FC window size. - * - * @param cls a `struct VirtualLink` to work with - */ -static void -task_consider_sending_fc (void *cls) -{ - struct VirtualLink *vl = cls; - vl->fc_retransmit_task = NULL; - consider_sending_fc (cls); -} - - -/** - * Something changed on the virtual link with respect to flow - * control. Consider retransmitting the FC window size. - * - * @param cls a `struct VirtualLink` to work with - */ -static void -consider_sending_fc (void *cls) -{ - struct VirtualLink *vl = cls; - struct GNUNET_TIME_Absolute monotime; - struct TransportFlowControlMessage fc; - struct GNUNET_TIME_Relative duration; - struct GNUNET_TIME_Relative rtt; - - duration = GNUNET_TIME_absolute_get_duration (vl->last_fc_transmission); - /* OPTIMIZE-FC-BDP: decide sane criteria on when to do this, instead of doing - it always! */ - /* For example, we should probably ONLY do this if a bit more than - an RTT has passed, or if the window changed "significantly" since - then. See vl->last_fc_rtt! NOTE: to do this properly, we also - need an estimate for the bandwidth-delay-product for the entire - VL, as that determines "significantly". We have the delay, but - the bandwidth statistics need to be added for the VL!*/(void) duration; - - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "Sending FC seq %u to %s with new window %llu\n", - (unsigned int) vl->fc_seq_gen, - GNUNET_i2s (&vl->target), - (unsigned long long) vl->incoming_fc_window_size); - monotime = GNUNET_TIME_absolute_get_monotonic (GST_cfg); - vl->last_fc_transmission = monotime; - fc.header.type = htons (GNUNET_MESSAGE_TYPE_TRANSPORT_FLOW_CONTROL); - fc.header.size = htons (sizeof(fc)); - fc.seq = htonl (vl->fc_seq_gen++); - fc.inbound_window_size = GNUNET_htonll (vl->incoming_fc_window_size - + vl->incoming_fc_window_size_used - + vl->incoming_fc_window_size_loss); - fc.outbound_sent = GNUNET_htonll (vl->outbound_fc_window_size_used); - fc.outbound_window_size = GNUNET_htonll (vl->outbound_fc_window_size); - fc.sender_time = GNUNET_TIME_absolute_hton (monotime); - rtt = route_control_message_without_fc (vl, &fc.header, RMO_DV_ALLOWED); - if (GNUNET_TIME_UNIT_FOREVER_REL.rel_value_us == rtt.rel_value_us) - { - rtt = GNUNET_TIME_UNIT_SECONDS; - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "FC retransmission to %s failed, will retry in %s\n", - GNUNET_i2s (&vl->target), - GNUNET_STRINGS_relative_time_to_string (rtt, GNUNET_YES)); - vl->last_fc_rtt = GNUNET_TIME_UNIT_ZERO; - } - else - { - /* OPTIMIZE-FC-BDP: rtt is not ideal, we can do better! */ - vl->last_fc_rtt = rtt; - } - if (NULL != vl->fc_retransmit_task) - GNUNET_SCHEDULER_cancel (vl->fc_retransmit_task); - if (MAX_FC_RETRANSMIT_COUNT == vl->fc_retransmit_count) - { - rtt = GNUNET_TIME_UNIT_MINUTES; - vl->fc_retransmit_count = 0; - } - vl->fc_retransmit_task = - GNUNET_SCHEDULER_add_delayed (rtt, &task_consider_sending_fc, vl); - vl->fc_retransmit_count++; -} - - -/** - * There is a message at the head of the pending messages for @a vl - * which may be ready for transmission. Check if a queue is ready to - * take it. - * - * This function must (1) check for flow control to ensure that we can - * right now send to @a vl, (2) check that the pending message in the - * queue is actually eligible, (3) determine if any applicable queue - * (direct neighbour or DVH path) is ready to accept messages, and - * (4) prioritize based on the preferences associated with the - * pending message. - * - * So yeah, easy. - * - * @param vl virtual link where we should check for transmission - */ -static void -check_vl_transmission (struct VirtualLink *vl) -{ - struct Neighbour *n = vl->n; - struct DistanceVector *dv = vl->dv; - struct GNUNET_TIME_Absolute now; - struct VirtualLink *vl_next_hop; - int elig; - - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "check_vl_transmission to target %s\n", - GNUNET_i2s (&vl->target)); - /* Check that we have an eligible pending message! - (cheaper than having #transmit_on_queue() find out!) */ - elig = GNUNET_NO; - for (struct PendingMessage *pm = vl->pending_msg_head; NULL != pm; - pm = pm->next_vl) - { - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "check_vl_transmission loop\n"); - if (NULL != pm->qe) - continue; /* not eligible, is in a queue! */ - if (pm->bytes_msg + vl->outbound_fc_window_size_used > - vl->outbound_fc_window_size) - { - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "Stalled message %llu transmission on VL %s due to flow control: %llu < %llu\n", - pm->logging_uuid, - GNUNET_i2s (&vl->target), - (unsigned long long) vl->outbound_fc_window_size, - (unsigned long long) (pm->bytes_msg - + vl->outbound_fc_window_size_used)); - consider_sending_fc (vl); - return; /* We have a message, but flow control says "nope" */ - } - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "Target window on VL %s not stalled. Scheduling transmission on queue\n", - GNUNET_i2s (&vl->target)); - /* Notify queues at direct neighbours that we are interested */ - now = GNUNET_TIME_absolute_get (); - if (NULL != n) - { - for (struct Queue *queue = n->queue_head; NULL != queue; - queue = queue->next_neighbour) - { - if ((GNUNET_YES == queue->idle) && - (queue->validated_until.abs_value_us > now.abs_value_us)) - { - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "Direct neighbour %s not stalled\n", - GNUNET_i2s (&n->pid)); - schedule_transmit_on_queue (GNUNET_TIME_UNIT_ZERO, - queue, - GNUNET_SCHEDULER_PRIORITY_DEFAULT); - elig = GNUNET_YES; - } - else - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "Neighbour Queue QID: %u (%u) busy or invalid\n", - queue->qid, - queue->idle); - } - } - /* Notify queues via DV that we are interested */ - if (NULL != dv) - { - /* Do DV with lower scheduler priority, which effectively means that - IF a neighbour exists and is available, we prefer it. */ - for (struct DistanceVectorHop *pos = dv->dv_head; NULL != pos; - pos = pos->next_dv) - { - struct Neighbour *nh = pos->next_hop; - - - if (pos->path_valid_until.abs_value_us <= now.abs_value_us) - continue; /* skip this one: path not validated */ - else - { - vl_next_hop = lookup_virtual_link (&nh->pid); - GNUNET_assert (NULL != vl_next_hop); - if (pm->bytes_msg + vl_next_hop->outbound_fc_window_size_used > - vl_next_hop->outbound_fc_window_size) - { - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "Stalled message %llu transmission on next hop %s due to flow control: %llu < %llu\n", - pm->logging_uuid, - GNUNET_i2s (&vl_next_hop->target), - (unsigned long - long) vl_next_hop->outbound_fc_window_size, - (unsigned long long) (pm->bytes_msg - + vl_next_hop-> - outbound_fc_window_size_used)); - consider_sending_fc (vl_next_hop); - continue; /* We have a message, but flow control says "nope" for the first hop of this path */ - } - for (struct Queue *queue = nh->queue_head; NULL != queue; - queue = queue->next_neighbour) - if ((GNUNET_YES == queue->idle) && - (queue->validated_until.abs_value_us > now.abs_value_us)) - { - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "Next hop neighbour %s not stalled\n", - GNUNET_i2s (&nh->pid)); - schedule_transmit_on_queue (GNUNET_TIME_UNIT_ZERO, - queue, - GNUNET_SCHEDULER_PRIORITY_BACKGROUND); - elig = GNUNET_YES; - } - else - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "DV Queue QID: %u (%u) busy or invalid\n", - queue->qid, - queue->idle); - } - } - } - if (GNUNET_YES == elig) - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "Eligible message %llu of size %u to %s: %llu/%llu\n", - pm->logging_uuid, - pm->bytes_msg, - GNUNET_i2s (&vl->target), - (unsigned long long) vl->outbound_fc_window_size, - (unsigned long long) (pm->bytes_msg - + vl->outbound_fc_window_size_used)); - break; - } -} - - -/** - * Client asked for transmission to a peer. Process the request. - * - * @param cls the client - * @param obm the send message that was sent - */ -static void -handle_client_send (void *cls, const struct OutboundMessage *obm) -{ - struct TransportClient *tc = cls; - struct PendingMessage *pm; - const struct GNUNET_MessageHeader *obmm; - uint32_t bytes_msg; - struct VirtualLink *vl; - enum GNUNET_MQ_PriorityPreferences pp; - - GNUNET_assert (CT_CORE == tc->type); - obmm = (const struct GNUNET_MessageHeader *) &obm[1]; - bytes_msg = ntohs (obmm->size); - pp = (enum GNUNET_MQ_PriorityPreferences) ntohl (obm->priority); - vl = lookup_virtual_link (&obm->peer); - if ((NULL == vl) || (GNUNET_NO == vl->confirmed)) - { - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "Don't have %s as a neighbour (anymore).\n", - GNUNET_i2s (&obm->peer)); - /* Failure: don't have this peer as a neighbour (anymore). - Might have gone down asynchronously, so this is NOT - a protocol violation by CORE. Still count the event, - as this should be rare. */ - GNUNET_SERVICE_client_continue (tc->client); - GNUNET_STATISTICS_update (GST_stats, - "# messages dropped (neighbour unknown)", - 1, - GNUNET_NO); - return; - } - - pm = GNUNET_malloc (sizeof(struct PendingMessage) + bytes_msg); - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "1 created pm %p storing vl %p\n", - pm, - vl); - pm->logging_uuid = logging_uuid_gen++; - pm->prefs = pp; - pm->client = tc; - pm->vl = vl; - pm->bytes_msg = bytes_msg; - memcpy (&pm[1], obmm, bytes_msg); - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "Sending message of type %u with %u bytes as <%llu> to %s\n", - ntohs (obmm->type), - bytes_msg, - pm->logging_uuid, - GNUNET_i2s (&obm->peer)); - GNUNET_CONTAINER_MDLL_insert (client, - tc->details.core.pending_msg_head, - tc->details.core.pending_msg_tail, - pm); - GNUNET_CONTAINER_MDLL_insert (vl, - vl->pending_msg_head, - vl->pending_msg_tail, - pm); - check_vl_transmission (vl); - GNUNET_SERVICE_client_continue (tc->client); -} - - -/** - * Communicator requests backchannel transmission. Process the request. - * Just repacks it into our `struct TransportBackchannelEncapsulationMessage *` - * (which for now has exactly the same format, only a different message type) - * and passes it on for routing. - * - * @param cls the client - * @param cb the send message that was sent - */ -static void -handle_communicator_backchannel ( - void *cls, - const struct GNUNET_TRANSPORT_CommunicatorBackchannel *cb) -{ - struct Neighbour *n; - struct VirtualLink *vl; - struct TransportClient *tc = cls; - const struct GNUNET_MessageHeader *inbox = - (const struct GNUNET_MessageHeader *) &cb[1]; - uint16_t isize = ntohs (inbox->size); - const char *is = ((const char *) &cb[1]) + isize; - size_t slen = strlen (is) + 1; - char - mbuf[slen + isize - + sizeof(struct - TransportBackchannelEncapsulationMessage)] GNUNET_ALIGN; - struct TransportBackchannelEncapsulationMessage *be = - (struct TransportBackchannelEncapsulationMessage *) mbuf; - - /* 0-termination of 'is' was checked already in - #check_communicator_backchannel() */ - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "Preparing backchannel transmission to %s:%s of type %u and size %u\n", - GNUNET_i2s (&cb->pid), - is, - ntohs (inbox->type), - ntohs (inbox->size)); - /* encapsulate and encrypt message */ - be->header.type = - htons (GNUNET_MESSAGE_TYPE_TRANSPORT_BACKCHANNEL_ENCAPSULATION); - be->header.size = htons (sizeof(mbuf)); - memcpy (&be[1], inbox, isize); - memcpy (&mbuf[sizeof(struct TransportBackchannelEncapsulationMessage) - + isize], - is, - strlen (is) + 1); - // route_control_message_without_fc (&cb->pid, &be->header, RMO_DV_ALLOWED); - vl = lookup_virtual_link (&cb->pid); - if ((NULL != vl) && (GNUNET_YES == vl->confirmed)) - { - route_control_message_without_fc (vl, &be->header, RMO_DV_ALLOWED); - } - else - { - /* Use route via neighbour */ - n = lookup_neighbour (&cb->pid); - if (NULL != n) - route_via_neighbour ( - n, - &be->header, - RMO_NONE); - } - GNUNET_SERVICE_client_continue (tc->client); -} - - -/** - * Address of our peer added. Test message is well-formed. - * - * @param cls the client - * @param aam the send message that was sent - * @return #GNUNET_OK if message is well-formed - */ -static int -check_add_address (void *cls, - const struct GNUNET_TRANSPORT_AddAddressMessage *aam) -{ - struct TransportClient *tc = cls; - - if (CT_COMMUNICATOR != tc->type) - { - GNUNET_break (0); - return GNUNET_SYSERR; - } - GNUNET_MQ_check_zero_termination (aam); - return GNUNET_OK; -} - - -/** - * Ask peerstore to store our address. - * - * @param cls an `struct AddressListEntry *` - */ -static void -store_pi (void *cls); - - -/** - * Function called when peerstore is done storing our address. - * - * @param cls a `struct AddressListEntry` - * @param success #GNUNET_YES if peerstore was successful - */ -static void -peerstore_store_own_cb (void *cls, int success) -{ - struct AddressListEntry *ale = cls; - - ale->sc = NULL; - if (GNUNET_YES != success) - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - "Failed to store our own address `%s' in peerstore!\n", - ale->address); - else - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "Successfully stored our own address `%s' in peerstore!\n", - ale->address); - /* refresh period is 1/4 of expiration time, that should be plenty - without being excessive. */ - ale->st = - GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_relative_divide (ale->expiration, - 4ULL), - &store_pi, - ale); -} - - -static void -shc_cont (void *cls, int success) -{ - GNUNET_free (cls); -} - - -/** - * Ask peerstore to store our address. - * - * @param cls an `struct AddressListEntry *` - */ -static void -store_pi (void *cls) -{ - struct AddressListEntry *ale = cls; - struct GNUNET_PEERSTORE_StoreHelloContext *shc; - void *addr; - size_t addr_len; - struct GNUNET_TIME_Absolute expiration; - enum GNUNET_GenericReturnValue add_result; - struct GNUNET_MQ_Envelope *env; - const struct GNUNET_MessageHeader *msg; - const char *dash; - char *prefix = GNUNET_HELLO_address_to_prefix (ale->address); - char *address_uri; - - dash = strchr (ale->address, '-'); - dash++; - GNUNET_asprintf (&address_uri, - "%s://%s", - prefix, - dash); - ale->st = NULL; - expiration = GNUNET_TIME_relative_to_absolute (ale->expiration); - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "Storing our address `%s' in peerstore until %s!\n", - ale->address, - GNUNET_STRINGS_absolute_time_to_string (expiration)); - add_result = GNUNET_HELLO_builder_add_address (GST_my_hello, - address_uri); - env = GNUNET_HELLO_builder_to_env (GST_my_hello, - GST_my_private_key, - GNUNET_TIME_UNIT_ZERO); - msg = GNUNET_MQ_env_get_msg (env); - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "store_pi 1\n"); - if (GNUNET_YES == add_result) - shc = GNUNET_PEERSTORE_hello_add (peerstore, - msg, - shc_cont, - shc); - else if (GNUNET_SYSERR == add_result) - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - "Error adding address to peerstore hello!\n"); - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "store_pi 2\n"); - GNUNET_HELLO_sign_address (ale->address, - ale->nt, - hello_mono_time, - GST_my_private_key, - &addr, - &addr_len); - ale->sc = GNUNET_PEERSTORE_store (peerstore, - "transport", - &GST_my_identity, - GNUNET_PEERSTORE_TRANSPORT_HELLO_KEY, - addr, - addr_len, - expiration, - GNUNET_PEERSTORE_STOREOPTION_MULTIPLE, - &peerstore_store_own_cb, - ale); - GNUNET_free (addr); - GNUNET_free (env); - GNUNET_free (prefix); - GNUNET_free (address_uri); - if (NULL == ale->sc) - { - GNUNET_log (GNUNET_ERROR_TYPE_WARNING, - "Failed to store our address `%s' with peerstore\n", - ale->address); - ale->st = - GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_UNIT_SECONDS, &store_pi, ale); - } -} - - -/** - * Address of our peer added. Process the request. - * - * @param cls the client - * @param aam the send message that was sent - */ -static void -handle_add_address (void *cls, - const struct GNUNET_TRANSPORT_AddAddressMessage *aam) -{ - struct TransportClient *tc = cls; - struct AddressListEntry *ale; - size_t slen; - - /* 0-termination of &aam[1] was checked in #check_add_address */ - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "Communicator added address `%s'!\n", - (const char *) &aam[1]); - slen = ntohs (aam->header.size) - sizeof(*aam); - ale = GNUNET_malloc (sizeof(struct AddressListEntry) + slen); - ale->tc = tc; - ale->address = (const char *) &ale[1]; - ale->expiration = GNUNET_TIME_relative_ntoh (aam->expiration); - ale->aid = aam->aid; - ale->nt = (enum GNUNET_NetworkType) ntohl (aam->nt); - memcpy (&ale[1], &aam[1], slen); - GNUNET_CONTAINER_DLL_insert (tc->details.communicator.addr_head, - tc->details.communicator.addr_tail, - ale); - ale->st = GNUNET_SCHEDULER_add_now (&store_pi, ale); - GNUNET_SERVICE_client_continue (tc->client); -} - - -/** - * Address of our peer deleted. Process the request. - * - * @param cls the client - * @param dam the send message that was sent - */ -static void -handle_del_address (void *cls, - const struct GNUNET_TRANSPORT_DelAddressMessage *dam) -{ - struct TransportClient *tc = cls; - struct AddressListEntry *alen; - - if (CT_COMMUNICATOR != tc->type) - { - GNUNET_break (0); - GNUNET_SERVICE_client_drop (tc->client); - return; - } - for (struct AddressListEntry *ale = tc->details.communicator.addr_head; - NULL != ale; - ale = alen) - { - alen = ale->next; - if (dam->aid != ale->aid) - continue; - GNUNET_assert (ale->tc == tc); - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "Communicator deleted address `%s'!\n", - ale->address); - free_address_list_entry (ale); - GNUNET_SERVICE_client_continue (tc->client); - return; - } - GNUNET_log (GNUNET_ERROR_TYPE_WARNING, - "Communicator removed address we did not even have.\n"); - GNUNET_SERVICE_client_continue (tc->client); - // GNUNET_SERVICE_client_drop (tc->client); -} - - -/** - * Given an inbound message @a msg from a communicator @a cmc, - * demultiplex it based on the type calling the right handler. - * - * @param cmc context for demultiplexing - * @param msg message to demultiplex - */ -static void -demultiplex_with_cmc (struct CommunicatorMessageContext *cmc); - - -/** - * Function called when we are done giving a message of a certain - * size to CORE and should thus decrement the number of bytes of - * RAM reserved for that peer's MQ. - * - * @param cls a `struct CoreSentContext` - */ -static void -core_env_sent_cb (void *cls) -{ - struct CoreSentContext *ctx = cls; - struct VirtualLink *vl = ctx->vl; - - if (NULL == vl) - { - /* lost the link in the meantime, ignore */ - GNUNET_free (ctx); - return; - } - GNUNET_CONTAINER_DLL_remove (vl->csc_head, vl->csc_tail, ctx); - GNUNET_assert (vl->incoming_fc_window_size_ram >= ctx->size); - vl->incoming_fc_window_size_ram -= ctx->size; - vl->incoming_fc_window_size_used += ctx->isize; - consider_sending_fc (vl); - GNUNET_free (ctx); -} - - -static void -finish_handling_raw_message (struct VirtualLink *vl, - const struct GNUNET_MessageHeader *mh, - struct CommunicatorMessageContext *cmc, - unsigned int continue_client) -{ - uint16_t size = ntohs (mh->size); - int have_core; - - if (vl->incoming_fc_window_size_ram > UINT_MAX - size) - { - GNUNET_STATISTICS_update (GST_stats, - "# CORE messages dropped (FC arithmetic overflow)", - 1, - GNUNET_NO); - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "CORE messages of type %u with %u bytes dropped (FC arithmetic overflow)\n", - (unsigned int) ntohs (mh->type), - (unsigned int) ntohs (mh->size)); - finish_cmc_handling_with_continue (cmc, continue_client); - return; - } - if (vl->incoming_fc_window_size_ram + size > vl->available_fc_window_size) - { - GNUNET_STATISTICS_update (GST_stats, - "# CORE messages dropped (FC window overflow)", - 1, - GNUNET_NO); - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "CORE messages of type %u with %u bytes dropped (FC window overflow)\n", - (unsigned int) ntohs (mh->type), - (unsigned int) ntohs (mh->size)); - finish_cmc_handling_with_continue (cmc, continue_client); - return; - } - - /* Forward to all CORE clients */ - have_core = GNUNET_NO; - for (struct TransportClient *tc = clients_head; NULL != tc; tc = tc->next) - { - struct GNUNET_MQ_Envelope *env; - struct InboundMessage *im; - struct CoreSentContext *ctx; - - if (CT_CORE != tc->type) - continue; - vl->incoming_fc_window_size_ram += size; - env = GNUNET_MQ_msg_extra (im, size, GNUNET_MESSAGE_TYPE_TRANSPORT_RECV); - ctx = GNUNET_new (struct CoreSentContext); - ctx->vl = vl; - ctx->size = size; - ctx->isize = (GNUNET_NO == have_core) ? size : 0; - have_core = GNUNET_YES; - GNUNET_CONTAINER_DLL_insert (vl->csc_head, vl->csc_tail, ctx); - GNUNET_MQ_notify_sent (env, &core_env_sent_cb, ctx); - im->peer = cmc->im.sender; - memcpy (&im[1], mh, size); - GNUNET_MQ_send (tc->mq, env); - vl->core_recv_window--; - } - if (GNUNET_NO == have_core) - { - GNUNET_log (GNUNET_ERROR_TYPE_WARNING, - "Dropped message to CORE: no CORE client connected!\n"); - /* Nevertheless, count window as used, as it is from the - perspective of the other peer! */ - vl->incoming_fc_window_size_used += size; - /* TODO-M1 */ - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "Dropped message of type %u with %u bytes to CORE: no CORE client connected!\n", - (unsigned int) ntohs (mh->type), - (unsigned int) ntohs (mh->size)); - finish_cmc_handling_with_continue (cmc, continue_client); - return; - } - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "Delivered message from %s of type %u to CORE recv window %d\n", - GNUNET_i2s (&cmc->im.sender), - ntohs (mh->type), - vl->core_recv_window); - if (vl->core_recv_window > 0) - { - finish_cmc_handling_with_continue (cmc, continue_client); - return; - } - /* Wait with calling #finish_cmc_handling(cmc) until the message - was processed by CORE MQs (for CORE flow control)! */ - GNUNET_CONTAINER_DLL_insert (vl->cmc_head, vl->cmc_tail, cmc); -} - - -/** - * Communicator gave us an unencapsulated message to pass as-is to - * CORE. Process the request. - * - * @param cls a `struct CommunicatorMessageContext` (must call - * #finish_cmc_handling() when done) - * @param mh the message that was received - */ -static void -handle_raw_message (void *cls, const struct GNUNET_MessageHeader *mh) -{ - struct CommunicatorMessageContext *cmc = cls; - // struct CommunicatorMessageContext *cmc_copy = - // GNUNET_new (struct CommunicatorMessageContext); - struct GNUNET_MessageHeader *mh_copy; - struct RingBufferEntry *rbe; - struct VirtualLink *vl; - uint16_t size = ntohs (mh->size); - - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "Handling raw message of type %u with %u bytes\n", - (unsigned int) ntohs (mh->type), - (unsigned int) ntohs (mh->size)); - - if ((size > UINT16_MAX - sizeof(struct InboundMessage)) || - (size < sizeof(struct GNUNET_MessageHeader))) - { - struct GNUNET_SERVICE_Client *client = cmc->tc->client; - - GNUNET_break (0); - finish_cmc_handling (cmc); - GNUNET_SERVICE_client_drop (client); - return; - } - vl = lookup_virtual_link (&cmc->im.sender); - if ((NULL == vl) || (GNUNET_NO == vl->confirmed)) - { - /* FIXME: sender is giving us messages for CORE but we don't have - the link up yet! I *suspect* this can happen right now (i.e. - sender has verified us, but we didn't verify sender), but if - we pass this on, CORE would be confused (link down, messages - arrive). We should investigate more if this happens often, - or in a persistent manner, and possibly do "something" about - it. Thus logging as error for now. */ - - mh_copy = GNUNET_malloc (size); - rbe = GNUNET_new (struct RingBufferEntry); - rbe->cmc = cmc; - /*cmc_copy->tc = cmc->tc; - cmc_copy->im = cmc->im;*/ - GNUNET_memcpy (mh_copy, mh, size); - - rbe->mh = mh_copy; - - ring_buffer[ring_buffer_head] = rbe;// cmc_copy; - // cmc_copy->mh = (const struct GNUNET_MessageHeader *) mh_copy; - cmc->mh = (const struct GNUNET_MessageHeader *) mh_copy; - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "Storing message for %s and type %u (%u) in ring buffer\n", - GNUNET_i2s (&cmc->im.sender), - (unsigned int) ntohs (mh->type), - (unsigned int) ntohs (mh_copy->type)); - if (RING_BUFFER_SIZE - 1 == ring_buffer_head) - { - ring_buffer_head = 0; - is_ring_buffer_full = GNUNET_YES; - } - else - ring_buffer_head++; - - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "%u items stored in ring buffer\n", - ring_buffer_head); - - /*GNUNET_break_op (0); - GNUNET_STATISTICS_update (GST_stats, - "# CORE messages dropped (virtual link still down)", - 1, - GNUNET_NO); - - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "CORE messages of type %u with %u bytes dropped (virtual link still down)\n", - (unsigned int) ntohs (mh->type), - (unsigned int) ntohs (mh->size)); - finish_cmc_handling (cmc);*/ - GNUNET_SERVICE_client_continue (cmc->tc->client); - // GNUNET_free (cmc); - return; - } - finish_handling_raw_message (vl, mh, cmc, GNUNET_YES); -} - - -/** - * Communicator gave us a fragment box. Check the message. - * - * @param cls a `struct CommunicatorMessageContext` - * @param fb the send message that was sent - * @return #GNUNET_YES if message is well-formed - */ -static int -check_fragment_box (void *cls, const struct TransportFragmentBoxMessage *fb) -{ - uint16_t size = ntohs (fb->header.size); - uint16_t bsize = size - sizeof(*fb); - - (void) cls; - if (0 == bsize) - { - GNUNET_break_op (0); - return GNUNET_SYSERR; - } - if (bsize + ntohs (fb->frag_off) > ntohs (fb->msg_size)) - { - GNUNET_break_op (0); - return GNUNET_SYSERR; - } - if (ntohs (fb->frag_off) >= ntohs (fb->msg_size)) - { - GNUNET_break_op (0); - return GNUNET_SYSERR; - } - return GNUNET_YES; -} - - -/** - * Clean up an idle cumulative acknowledgement data structure. - * - * @param cls a `struct AcknowledgementCummulator *` - */ -static void -destroy_ack_cummulator (void *cls) -{ - struct AcknowledgementCummulator *ac = cls; - - ac->task = NULL; - GNUNET_assert (0 == ac->num_acks); - GNUNET_assert ( - GNUNET_YES == - GNUNET_CONTAINER_multipeermap_remove (ack_cummulators, &ac->target, ac)); - GNUNET_free (ac); -} - - -/** - * Do the transmission of a cumulative acknowledgement now. - * - * @param cls a `struct AcknowledgementCummulator *` - */ -static void -transmit_cummulative_ack_cb (void *cls) -{ - struct Neighbour *n; - struct VirtualLink *vl; - struct AcknowledgementCummulator *ac = cls; - char buf[sizeof(struct TransportReliabilityAckMessage) - + ac->num_acks - * sizeof(struct TransportCummulativeAckPayloadP)] GNUNET_ALIGN; - struct TransportReliabilityAckMessage *ack = - (struct TransportReliabilityAckMessage *) buf; - struct TransportCummulativeAckPayloadP *ap; - - ac->task = NULL; - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "Sending ACK with %u components to %s\n", - ac->num_acks, - GNUNET_i2s (&ac->target)); - GNUNET_assert (0 < ac->num_acks); - ack->header.type = htons (GNUNET_MESSAGE_TYPE_TRANSPORT_RELIABILITY_ACK); - ack->header.size = - htons (sizeof(*ack) - + ac->num_acks * sizeof(struct TransportCummulativeAckPayloadP)); - ack->ack_counter = htonl (ac->ack_counter += ac->num_acks); - ap = (struct TransportCummulativeAckPayloadP *) &ack[1]; - for (unsigned int i = 0; i < ac->num_acks; i++) - { - ap[i].ack_uuid = ac->ack_uuids[i].ack_uuid; - ap[i].ack_delay = GNUNET_TIME_relative_hton ( - GNUNET_TIME_absolute_get_duration (ac->ack_uuids[i].receive_time)); - } - /*route_control_message_without_fc ( - &ac->target, - &ack->header, - RMO_DV_ALLOWED);*/ - vl = lookup_virtual_link (&ac->target); - if ((NULL != vl) && (GNUNET_YES == vl->confirmed)) - { - route_control_message_without_fc ( - vl, - &ack->header, - RMO_DV_ALLOWED); - } - else - { - /* Use route via neighbour */ - n = lookup_neighbour (&ac->target); - if (NULL != n) - route_via_neighbour ( - n, - &ack->header, - RMO_NONE); - } - ac->num_acks = 0; - ac->task = GNUNET_SCHEDULER_add_delayed (ACK_CUMMULATOR_TIMEOUT, - &destroy_ack_cummulator, - ac); -} - - -/** - * Transmit an acknowledgement for @a ack_uuid to @a pid delaying - * transmission by at most @a ack_delay. - * - * @param pid target peer - * @param ack_uuid UUID to ack - * @param max_delay how long can the ACK wait - */ -static void -cummulative_ack (const struct GNUNET_PeerIdentity *pid, - const struct AcknowledgementUUIDP *ack_uuid, - struct GNUNET_TIME_Absolute max_delay) -{ - struct AcknowledgementCummulator *ac; - - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "Scheduling ACK %s for transmission to %s\n", - GNUNET_uuid2s (&ack_uuid->value), - GNUNET_i2s (pid)); - ac = GNUNET_CONTAINER_multipeermap_get (ack_cummulators, pid); - if (NULL == ac) - { - ac = GNUNET_new (struct AcknowledgementCummulator); - ac->target = *pid; - ac->min_transmission_time = max_delay; - GNUNET_assert (GNUNET_YES == - GNUNET_CONTAINER_multipeermap_put ( - ack_cummulators, - &ac->target, - ac, - GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_ONLY)); - } - else - { - if (MAX_CUMMULATIVE_ACKS == ac->num_acks) - { - /* must run immediately, ack buffer full! */ - transmit_cummulative_ack_cb (ac); - } - GNUNET_SCHEDULER_cancel (ac->task); - ac->min_transmission_time = - GNUNET_TIME_absolute_min (ac->min_transmission_time, max_delay); - } - GNUNET_assert (ac->num_acks < MAX_CUMMULATIVE_ACKS); - ac->ack_uuids[ac->num_acks].receive_time = GNUNET_TIME_absolute_get (); - ac->ack_uuids[ac->num_acks].ack_uuid = *ack_uuid; - ac->num_acks++; - ac->task = GNUNET_SCHEDULER_add_at (ac->min_transmission_time, - &transmit_cummulative_ack_cb, - ac); -} - - -/** - * Closure for #find_by_message_uuid. - */ -struct FindByMessageUuidContext -{ - /** - * UUID to look for. - */ - struct MessageUUIDP message_uuid; - - /** - * Set to the reassembly context if found. - */ - struct ReassemblyContext *rc; -}; - - -/** - * Iterator called to find a reassembly context by the message UUID in the - * multihashmap32. - * - * @param cls a `struct FindByMessageUuidContext` - * @param key a key (unused) - * @param value a `struct ReassemblyContext` - * @return #GNUNET_YES if not found, #GNUNET_NO if found - */ -static int -find_by_message_uuid (void *cls, uint32_t key, void *value) -{ - struct FindByMessageUuidContext *fc = cls; - struct ReassemblyContext *rc = value; - - (void) key; - if (0 == GNUNET_memcmp (&fc->message_uuid, &rc->msg_uuid)) - { - fc->rc = rc; - return GNUNET_NO; - } - return GNUNET_YES; -} - - -/** - * Communicator gave us a fragment. Process the request. - * - * @param cls a `struct CommunicatorMessageContext` (must call - * #finish_cmc_handling() when done) - * @param fb the message that was received - */ -static void -handle_fragment_box (void *cls, const struct TransportFragmentBoxMessage *fb) -{ - struct CommunicatorMessageContext *cmc = cls; - struct VirtualLink *vl; - struct ReassemblyContext *rc; - const struct GNUNET_MessageHeader *msg; - uint16_t msize; - uint16_t fsize; - uint16_t frag_off; - char *target; - struct GNUNET_TIME_Relative cdelay; - struct FindByMessageUuidContext fc; - - vl = lookup_virtual_link (&cmc->im.sender); - if ((NULL == vl) || (GNUNET_NO == vl->confirmed)) - { - struct GNUNET_SERVICE_Client *client = cmc->tc->client; - - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "No virtual link for %s to handle fragment\n", - GNUNET_i2s (&cmc->im.sender)); - GNUNET_break (0); - finish_cmc_handling (cmc); - GNUNET_SERVICE_client_drop (client); - return; - } - if (NULL == vl->reassembly_map) - { - vl->reassembly_map = GNUNET_CONTAINER_multihashmap32_create (8); - vl->reassembly_heap = - GNUNET_CONTAINER_heap_create (GNUNET_CONTAINER_HEAP_ORDER_MIN); - vl->reassembly_timeout_task = - GNUNET_SCHEDULER_add_delayed (REASSEMBLY_EXPIRATION, - &reassembly_cleanup_task, - vl); - } - msize = ntohs (fb->msg_size); - fc.message_uuid = fb->msg_uuid; - fc.rc = NULL; - (void) GNUNET_CONTAINER_multihashmap32_get_multiple (vl->reassembly_map, - fb->msg_uuid.uuid, - &find_by_message_uuid, - &fc); - fsize = ntohs (fb->header.size) - sizeof(*fb); - if (NULL == (rc = fc.rc)) - { - rc = GNUNET_malloc (sizeof(*rc) + msize /* reassembly payload buffer */ - + (msize + 7) / 8 * sizeof(uint8_t) /* bitfield */); - rc->msg_uuid = fb->msg_uuid; - rc->virtual_link = vl; - rc->msg_size = msize; - rc->reassembly_timeout = - GNUNET_TIME_relative_to_absolute (REASSEMBLY_EXPIRATION); - rc->last_frag = GNUNET_TIME_absolute_get (); - rc->hn = GNUNET_CONTAINER_heap_insert (vl->reassembly_heap, - rc, - rc->reassembly_timeout.abs_value_us); - GNUNET_assert (GNUNET_OK == - GNUNET_CONTAINER_multihashmap32_put ( - vl->reassembly_map, - rc->msg_uuid.uuid, - rc, - GNUNET_CONTAINER_MULTIHASHMAPOPTION_MULTIPLE)); - target = (char *) &rc[1]; - rc->bitfield = (uint8_t *) (target + rc->msg_size); - if (fsize != rc->msg_size) - rc->msg_missing = rc->msg_size; - else - rc->msg_missing = 0; - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "Received fragment with size %u at offset %u/%u %u bytes missing from %s for NEW message %u\n", - fsize, - ntohs (fb->frag_off), - msize, - rc->msg_missing, - GNUNET_i2s (&cmc->im.sender), - (unsigned int) fb->msg_uuid.uuid); - } - else - { - target = (char *) &rc[1]; - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "Received fragment at offset %u/%u from %s for message %u\n", - ntohs (fb->frag_off), - msize, - GNUNET_i2s (&cmc->im.sender), - (unsigned int) fb->msg_uuid.uuid); - } - if (msize != rc->msg_size) - { - GNUNET_break (0); - finish_cmc_handling (cmc); - return; - } - - /* reassemble */ - if (0 == fsize) - { - GNUNET_break (0); - finish_cmc_handling (cmc); - return; - } - frag_off = ntohs (fb->frag_off); - if (frag_off + fsize > msize) - { - /* Fragment (plus fragment size) exceeds message size! */ - GNUNET_break_op (0); - finish_cmc_handling (cmc); - return; - } - memcpy (&target[frag_off], &fb[1], fsize); - /* update bitfield and msg_missing */ - for (unsigned int i = frag_off; i < frag_off + fsize; i++) - { - if (0 == (rc->bitfield[i / 8] & (1 << (i % 8)))) - { - rc->bitfield[i / 8] |= (1 << (i % 8)); - rc->msg_missing--; - } - } - - /* Compute cumulative ACK */ - cdelay = GNUNET_TIME_absolute_get_duration (rc->last_frag); - cdelay = GNUNET_TIME_relative_multiply (cdelay, rc->msg_missing / fsize); - if (0 == rc->msg_missing) - cdelay = GNUNET_TIME_UNIT_ZERO; - cummulative_ack (&cmc->im.sender, - &fb->ack_uuid, - GNUNET_TIME_relative_to_absolute (cdelay)); - rc->last_frag = GNUNET_TIME_absolute_get (); - /* is reassembly complete? */ - if (0 != rc->msg_missing) - { - finish_cmc_handling (cmc); - return; - } - /* reassembly is complete, verify result */ - msg = (const struct GNUNET_MessageHeader *) &rc[1]; - if (ntohs (msg->size) != rc->msg_size) - { - GNUNET_break (0); - free_reassembly_context (rc); - finish_cmc_handling (cmc); - return; - } - /* successful reassembly */ - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "Fragment reassembly complete for message %u\n", - (unsigned int) fb->msg_uuid.uuid); - /* FIXME: check that the resulting msg is NOT a - DV Box or Reliability Box, as that is NOT allowed! */ - cmc->mh = msg; - demultiplex_with_cmc (cmc); - /* FIXME-OPTIMIZE: really free here? Might be bad if fragments are still - en-route and we forget that we finished this reassembly immediately! - -> keep around until timeout? - -> shorten timeout based on ACK? */ - free_reassembly_context (rc); -} - - -/** - * Communicator gave us a reliability box. Check the message. - * - * @param cls a `struct CommunicatorMessageContext` - * @param rb the send message that was sent - * @return #GNUNET_YES if message is well-formed - */ -static int -check_reliability_box (void *cls, - const struct TransportReliabilityBoxMessage *rb) -{ - (void) cls; - const struct GNUNET_MessageHeader *inbox = (const struct - GNUNET_MessageHeader *) &rb[1]; - - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "check_send_msg with size %u: inner msg type %u and size %u (%lu %lu)\n", - ntohs (rb->header.size), - ntohs (inbox->type), - ntohs (inbox->size), - sizeof (struct TransportReliabilityBoxMessage), - sizeof (struct GNUNET_MessageHeader)); - GNUNET_MQ_check_boxed_message (rb); - return GNUNET_YES; -} - - -/** - * Communicator gave us a reliability box. Process the request. - * - * @param cls a `struct CommunicatorMessageContext` (must call - * #finish_cmc_handling() when done) - * @param rb the message that was received - */ -static void -handle_reliability_box (void *cls, - const struct TransportReliabilityBoxMessage *rb) -{ - struct CommunicatorMessageContext *cmc = cls; - const struct GNUNET_MessageHeader *inbox = - (const struct GNUNET_MessageHeader *) &rb[1]; - struct GNUNET_TIME_Relative rtt; - - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "Received reliability box from %s with UUID %s of type %u\n", - GNUNET_i2s (&cmc->im.sender), - GNUNET_uuid2s (&rb->ack_uuid.value), - (unsigned int) ntohs (inbox->type)); - rtt = GNUNET_TIME_UNIT_SECONDS; /* FIXME: should base this on "RTT", but we - do not really have an RTT for the - * incoming* queue (should we have - the sender add it to the rb message?) */ - cummulative_ack ( - &cmc->im.sender, - &rb->ack_uuid, - (0 == ntohl (rb->ack_countdown)) - ? GNUNET_TIME_UNIT_ZERO_ABS - : GNUNET_TIME_relative_to_absolute ( - GNUNET_TIME_relative_divide (rtt, 8 /* FIXME: magic constant */))); - /* continue with inner message */ - /* FIXME: check that inbox is NOT a DV Box, fragment or another - reliability box (not allowed!) */ - cmc->mh = inbox; - demultiplex_with_cmc (cmc); -} - - -/** - * Check if we have advanced to another age since the last time. If - * so, purge ancient statistics (more than GOODPUT_AGING_SLOTS before - * the current age) - * - * @param[in,out] pd data to update - * @param age current age - */ -static void -update_pd_age (struct PerformanceData *pd, unsigned int age) -{ - unsigned int sage; - - if (age == pd->last_age) - return; /* nothing to do */ - sage = GNUNET_MAX (pd->last_age, age - 2 * GOODPUT_AGING_SLOTS); - for (unsigned int i = sage; i <= age - GOODPUT_AGING_SLOTS; i++) - { - struct TransmissionHistoryEntry *the = &pd->the[i % GOODPUT_AGING_SLOTS]; - - the->bytes_sent = 0; - the->bytes_received = 0; - } - pd->last_age = age; -} - - -/** - * Update @a pd based on the latest @a rtt and the number of bytes - * that were confirmed to be successfully transmitted. - * - * @param[in,out] pd data to update - * @param rtt latest round-trip time - * @param bytes_transmitted_ok number of bytes receiver confirmed as received - */ -static void -update_performance_data (struct PerformanceData *pd, - struct GNUNET_TIME_Relative rtt, - uint16_t bytes_transmitted_ok) -{ - uint64_t nval = rtt.rel_value_us; - uint64_t oval = pd->aged_rtt.rel_value_us; - unsigned int age = get_age (); - struct TransmissionHistoryEntry *the = &pd->the[age % GOODPUT_AGING_SLOTS]; - - if (oval == GNUNET_TIME_UNIT_FOREVER_REL.rel_value_us) - pd->aged_rtt = rtt; - else - pd->aged_rtt.rel_value_us = (nval + 7 * oval) / 8; - update_pd_age (pd, age); - the->bytes_received += bytes_transmitted_ok; -} - - -/** - * We have successfully transmitted data via @a q, update metrics. - * - * @param q queue to update - * @param rtt round trip time observed - * @param bytes_transmitted_ok number of bytes successfully transmitted - */ -static void -update_queue_performance (struct Queue *q, - struct GNUNET_TIME_Relative rtt, - uint16_t bytes_transmitted_ok) -{ - update_performance_data (&q->pd, rtt, bytes_transmitted_ok); -} - - -/** - * We have successfully transmitted data via @a dvh, update metrics. - * - * @param dvh distance vector path data to update - * @param rtt round trip time observed - * @param bytes_transmitted_ok number of bytes successfully transmitted - */ -static void -update_dvh_performance (struct DistanceVectorHop *dvh, - struct GNUNET_TIME_Relative rtt, - uint16_t bytes_transmitted_ok) -{ - update_performance_data (&dvh->pd, rtt, bytes_transmitted_ok); -} - - -/** - * We have completed transmission of @a pm, remove it from - * the transmission queues (and if it is a fragment, continue - * up the tree as necessary). - * - * @param pm pending message that was transmitted - */ -static void -completed_pending_message (struct PendingMessage *pm) -{ - struct PendingMessage *pos; - - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "Complete transmission of message %llu %u\n", - pm->logging_uuid, - pm->pmt); - switch (pm->pmt) - { - case PMT_CORE: - case PMT_RELIABILITY_BOX: - /* Full message sent, we are done */ - client_send_response (pm); - return; - - case PMT_FRAGMENT_BOX: - /* Fragment sent over reliable channel */ - pos = pm->frag_parent; - GNUNET_CONTAINER_MDLL_remove (frag, pos->head_frag, pos->tail_frag, pm); - free_pending_message (pm); - /* check if subtree is done */ - while ((NULL == pos->head_frag) && (pos->frag_off == (pos->bytes_msg - - sizeof(struct - TransportFragmentBoxMessage))) - && - (NULL != pos->frag_parent)) - { - pm = pos; - pos = pm->frag_parent; - if ((NULL == pos) && (PMT_DV_BOX == pm->pmt)) - { - client_send_response (pm); - return; - } - else if (PMT_DV_BOX == pm->pmt) - { - client_send_response (pos); - return; - } - GNUNET_CONTAINER_MDLL_remove (frag, pos->head_frag, pos->tail_frag, pm); - free_pending_message (pm); - } - - /* Was this the last applicable fragment? */ - if ((NULL == pos->head_frag) && (NULL == pos->frag_parent) && - (pos->frag_off == pos->bytes_msg)) - client_send_response (pos); - return; - - case PMT_DV_BOX: - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "Completed transmission of message %llu (DV Box)\n", - pm->logging_uuid); - if (NULL != pm->frag_parent) - { - if (NULL != pm->bpm) - { - GNUNET_free (pm->bpm); - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "Freed bpm\n"); - } - pos = pm->frag_parent; - free_pending_message (pm); - pos->bpm = NULL; - client_send_response (pos); - } - else - client_send_response (pm); - return; - } -} - - -/** - * The @a pa was acknowledged, process the acknowledgement. - * - * @param pa the pending acknowledgement that was satisfied - * @param ack_delay artificial delay from cumulative acks created by the - * other peer - */ -static void -handle_acknowledged (struct PendingAcknowledgement *pa, - struct GNUNET_TIME_Relative ack_delay) -{ - struct GNUNET_TIME_Relative delay; - - delay = GNUNET_TIME_absolute_get_duration (pa->transmission_time); - delay = GNUNET_TIME_relative_subtract (delay, ack_delay); - if (NULL != pa->queue && 1 == pa->num_send) - update_queue_performance (pa->queue, delay, pa->message_size); - if (NULL != pa->dvh && 1 == pa->num_send) - update_dvh_performance (pa->dvh, delay, pa->message_size); - if (NULL != pa->pm) - completed_pending_message (pa->pm); - free_pending_acknowledgement (pa); -} - - -/** - * Communicator gave us a reliability ack. Check it is well-formed. - * - * @param cls a `struct CommunicatorMessageContext` (unused) - * @param ra the message that was received - * @return #GNUNET_Ok if @a ra is well-formed - */ -static int -check_reliability_ack (void *cls, - const struct TransportReliabilityAckMessage *ra) -{ - unsigned int n_acks; - - (void) cls; - n_acks = (ntohs (ra->header.size) - sizeof(*ra)) - / sizeof(struct TransportCummulativeAckPayloadP); - if (0 == n_acks) - { - GNUNET_break_op (0); - return GNUNET_SYSERR; - } - if ((ntohs (ra->header.size) - sizeof(*ra)) != - n_acks * sizeof(struct TransportCummulativeAckPayloadP)) - { - GNUNET_break_op (0); - return GNUNET_SYSERR; - } - return GNUNET_OK; -} - - -/** - * Communicator gave us a reliability ack. Process the request. - * - * @param cls a `struct CommunicatorMessageContext` (must call - * #finish_cmc_handling() when done) - * @param ra the message that was received - */ -static void -handle_reliability_ack (void *cls, - const struct TransportReliabilityAckMessage *ra) -{ - struct CommunicatorMessageContext *cmc = cls; - const struct TransportCummulativeAckPayloadP *ack; - unsigned int n_acks; - uint32_t ack_counter; - - n_acks = (ntohs (ra->header.size) - sizeof(*ra)) - / sizeof(struct TransportCummulativeAckPayloadP); - ack = (const struct TransportCummulativeAckPayloadP *) &ra[1]; - for (unsigned int i = 0; i < n_acks; i++) - { - struct PendingAcknowledgement *pa = - GNUNET_CONTAINER_multiuuidmap_get (pending_acks, &ack[i].ack_uuid.value); - if (NULL == pa) - { - GNUNET_log (GNUNET_ERROR_TYPE_INFO, - "Received ACK from %s with UUID %s which is unknown to us!\n", - GNUNET_i2s (&cmc->im.sender), - GNUNET_uuid2s (&ack[i].ack_uuid.value)); - GNUNET_STATISTICS_update ( - GST_stats, - "# FRAGMENT_ACKS dropped, no matching pending message", - 1, - GNUNET_NO); - continue; - } - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "Received ACK from %s with UUID %s\n", - GNUNET_i2s (&cmc->im.sender), - GNUNET_uuid2s (&ack[i].ack_uuid.value)); - handle_acknowledged (pa, GNUNET_TIME_relative_ntoh (ack[i].ack_delay)); - } - - ack_counter = htonl (ra->ack_counter); - (void) ack_counter; /* silence compiler warning for now */ - // FIXME-OPTIMIZE: track ACK losses based on ack_counter somewhere! - // (DV and/or Neighbour?) - finish_cmc_handling (cmc); -} - - -/** - * Communicator gave us a backchannel encapsulation. Check the message. - * - * @param cls a `struct CommunicatorMessageContext` - * @param be the send message that was sent - * @return #GNUNET_YES if message is well-formed - */ -static int -check_backchannel_encapsulation ( - void *cls, - const struct TransportBackchannelEncapsulationMessage *be) -{ - uint16_t size = ntohs (be->header.size) - sizeof(*be); - const struct GNUNET_MessageHeader *inbox = - (const struct GNUNET_MessageHeader *) &be[1]; - const char *is; - uint16_t isize; - - (void) cls; - if (ntohs (inbox->size) >= size) - { - GNUNET_break_op (0); - return GNUNET_SYSERR; - } - isize = ntohs (inbox->size); - is = ((const char *) inbox) + isize; - size -= isize; - if ('\0' != is[size - 1]) - { - GNUNET_break_op (0); - return GNUNET_SYSERR; - } - return GNUNET_YES; -} - - -/** - * Communicator gave us a backchannel encapsulation. Process the request. - * (We are the destination of the backchannel here.) - * - * @param cls a `struct CommunicatorMessageContext` (must call - * #finish_cmc_handling() when done) - * @param be the message that was received - */ -static void -handle_backchannel_encapsulation ( - void *cls, - const struct TransportBackchannelEncapsulationMessage *be) -{ - struct CommunicatorMessageContext *cmc = cls; - struct GNUNET_TRANSPORT_CommunicatorBackchannelIncoming *cbi; - struct GNUNET_MQ_Envelope *env; - struct TransportClient *tc; - const struct GNUNET_MessageHeader *inbox = - (const struct GNUNET_MessageHeader *) &be[1]; - uint16_t isize = ntohs (inbox->size); - const char *target_communicator = ((const char *) inbox) + isize; - char *sender; - char *self; - - GNUNET_asprintf (&sender, - "%s", - GNUNET_i2s (&cmc->im.sender)); - GNUNET_asprintf (&self, - "%s", - GNUNET_i2s (&GST_my_identity)); - - /* Find client providing this communicator */ - for (tc = clients_head; NULL != tc; tc = tc->next) - if ((CT_COMMUNICATOR == tc->type) && - (0 == - strcmp (tc->details.communicator.address_prefix, target_communicator))) - break; - if (NULL == tc) - { - char *stastr; - - GNUNET_asprintf ( - &stastr, - "# Backchannel message dropped: target communicator `%s' unknown", - target_communicator); - GNUNET_STATISTICS_update (GST_stats, stastr, 1, GNUNET_NO); - GNUNET_free (stastr); - finish_cmc_handling (cmc); - return; - } - /* Finally, deliver backchannel message to communicator */ - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "Delivering backchannel message from %s to %s of type %u to %s\n", - sender, - self, - ntohs (inbox->type), - target_communicator); - env = GNUNET_MQ_msg_extra ( - cbi, - isize, - GNUNET_MESSAGE_TYPE_TRANSPORT_COMMUNICATOR_BACKCHANNEL_INCOMING); - cbi->pid = cmc->im.sender; - memcpy (&cbi[1], inbox, isize); - GNUNET_MQ_send (tc->mq, env); - finish_cmc_handling (cmc); -} - - -/** - * Task called when we should check if any of the DV paths - * we have learned to a target are due for garbage collection. - * - * Collects stale paths, and possibly frees the entire DV - * entry if no paths are left. Otherwise re-schedules itself. - * - * @param cls a `struct DistanceVector` - */ -static void -path_cleanup_cb (void *cls) -{ - struct DistanceVector *dv = cls; - struct DistanceVectorHop *pos; - - dv->timeout_task = NULL; - while (NULL != (pos = dv->dv_head)) - { - GNUNET_assert (dv == pos->dv); - if (GNUNET_TIME_absolute_get_remaining (pos->timeout).rel_value_us > 0) - break; - free_distance_vector_hop (pos); - } - if (NULL == pos) - { - free_dv_route (dv); - return; - } - dv->timeout_task = - GNUNET_SCHEDULER_add_at (pos->timeout, &path_cleanup_cb, dv); -} - - -static void -send_msg_from_cache (struct VirtualLink *vl) -{ - - const struct GNUNET_PeerIdentity target = vl->target; - - - if ((GNUNET_YES == is_ring_buffer_full) || (0 < ring_buffer_head)) - { - struct RingBufferEntry *ring_buffer_copy[RING_BUFFER_SIZE]; - unsigned int tail = GNUNET_YES == is_ring_buffer_full ? ring_buffer_head : - 0; - unsigned int head = GNUNET_YES == is_ring_buffer_full ? RING_BUFFER_SIZE : - ring_buffer_head; - struct GNUNET_TRANSPORT_IncomingMessage im; - struct CommunicatorMessageContext *cmc; - struct RingBufferEntry *rbe; - struct GNUNET_MessageHeader *mh; - - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "Sending from ring buffer, which has %u items\n", - ring_buffer_head); - - ring_buffer_head = 0; - for (unsigned int i = 0; i < head; i++) - { - rbe = ring_buffer[(i + tail) % RING_BUFFER_SIZE]; - cmc = rbe->cmc; - mh = rbe->mh; - - im = cmc->im; - // mh = cmc->mh; - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "Sending to ring buffer target %s using vl target %s\n", - GNUNET_i2s (&im.sender), - GNUNET_i2s2 (&target)); - if (0 == GNUNET_memcmp (&target, &im.sender)) - { - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "Finish handling message of type %u and size %u\n", - (unsigned int) ntohs (mh->type), - (unsigned int) ntohs (mh->size)); - finish_handling_raw_message (vl, mh, cmc, GNUNET_NO); - GNUNET_free (mh); - } - else - { - ring_buffer_copy[i] = rbe; - ring_buffer_head++; - } - } - - if ((GNUNET_YES == is_ring_buffer_full) && (RING_BUFFER_SIZE - 1 > - ring_buffer_head)) - { - is_ring_buffer_full = GNUNET_NO; - } - - for (unsigned int i = 0; i < ring_buffer_head; i++) - { - ring_buffer[i] = ring_buffer_copy[i]; - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "ring_buffer_copy[i]->mh->type for i %u %u\n", - i, - ring_buffer_copy[i]->mh->type); - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "ring_buffer[i]->mh->type for i %u %u\n", - i, - ring_buffer[i]->mh->type); - } - - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "%u items still in ring buffer\n", - ring_buffer_head); - } - - if ((GNUNET_YES == is_ring_buffer_full) || (0 < ring_buffer_dv_head)) - { - struct PendingMessage *ring_buffer_dv_copy[RING_BUFFER_SIZE]; - struct PendingMessage *pm; - unsigned int tail = GNUNET_YES == is_ring_buffer_dv_full ? - ring_buffer_dv_head : - 0; - unsigned int head = GNUNET_YES == is_ring_buffer_dv_full ? - RING_BUFFER_SIZE : - ring_buffer_dv_head; - - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "Sending from ring buffer dv, which has %u items\n", - ring_buffer_dv_head); - - ring_buffer_dv_head = 0; - for (unsigned int i = 0; i < head; i++) - { - pm = ring_buffer_dv[(i + tail) % RING_BUFFER_SIZE]; - - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "Sending to ring buffer target %s using vl target %s\n", - GNUNET_i2s (&pm->target), - GNUNET_i2s2 (&target)); - if (0 == GNUNET_memcmp (&target, &pm->target)) - { - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "Adding PendingMessage to vl, checking transmission.\n"); - pm->vl = vl; - GNUNET_CONTAINER_MDLL_insert (vl, - vl->pending_msg_head, - vl->pending_msg_tail, - pm); - - check_vl_transmission (vl); - } - else - { - ring_buffer_dv_copy[i] = pm; - ring_buffer_dv_head++; - } - } - - if (is_ring_buffer_dv_full && (RING_BUFFER_SIZE - 1 > ring_buffer_dv_head)) - { - is_ring_buffer_dv_full = GNUNET_NO; - } - - for (unsigned int i = 0; i < ring_buffer_dv_head; i++) - ring_buffer_dv[i] = ring_buffer_dv_copy[i]; - - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "%u items still in ring buffer dv.\n", - ring_buffer_dv_head); - - } -} - - -/** - * The @a hop is a validated path to the respective target - * peer and we should tell core about it -- and schedule - * a job to revoke the state. - * - * @param hop a path to some peer that is the reason for activation - */ -static void -activate_core_visible_dv_path (struct DistanceVectorHop *hop) -{ - struct DistanceVector *dv = hop->dv; - struct VirtualLink *vl; - - vl = lookup_virtual_link (&dv->target); - if (NULL == vl) - { - - vl = GNUNET_new (struct VirtualLink); - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "Creating new virtual link %p to %s using DV!\n", - vl, - GNUNET_i2s (&dv->target)); - vl->confirmed = GNUNET_YES; - vl->message_uuid_ctr = - GNUNET_CRYPTO_random_u64 (GNUNET_CRYPTO_QUALITY_WEAK, UINT64_MAX); - vl->target = dv->target; - vl->core_recv_window = RECV_WINDOW_SIZE; - vl->available_fc_window_size = DEFAULT_WINDOW_SIZE; - vl->incoming_fc_window_size = DEFAULT_WINDOW_SIZE; - GNUNET_break (GNUNET_YES == - GNUNET_CONTAINER_multipeermap_put ( - links, - &vl->target, - vl, - GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_ONLY)); - vl->dv = dv; - dv->vl = vl; - vl->visibility_task = - GNUNET_SCHEDULER_add_at (hop->path_valid_until, &check_link_down, vl); - consider_sending_fc (vl); - /* We lacked a confirmed connection to the target - before, so tell CORE about it (finally!) */ - cores_send_connect_info (&dv->target); - send_msg_from_cache (vl); - } - else - { - /* Link was already up, remember dv is also now available and we are done */ - vl->dv = dv; - dv->vl = vl; - if (GNUNET_NO == vl->confirmed) - { - vl->confirmed = GNUNET_YES; - vl->visibility_task = - GNUNET_SCHEDULER_add_at (hop->path_valid_until, &check_link_down, vl); - consider_sending_fc (vl); - /* We lacked a confirmed connection to the target - before, so tell CORE about it (finally!) */ - cores_send_connect_info (&dv->target); - send_msg_from_cache (vl); - } - else - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "Virtual link to %s could now also use DV!\n", - GNUNET_i2s (&dv->target)); - } -} - - -/** - * We have learned a @a path through the network to some other peer, add it to - * our DV data structure (returning #GNUNET_YES on success). - * - * We do not add paths if we have a sufficient number of shorter - * paths to this target already (returning #GNUNET_NO). - * - * We also do not add problematic paths, like those where we lack the first - * hop in our neighbour list (i.e. due to a topology change) or where some - * non-first hop is in our neighbour list (returning #GNUNET_SYSERR). - * - * @param path the path we learned, path[0] should be us, - * and then path contains a valid path from us to - * `path[path_len-1]` path[1] should be a direct neighbour (we should check!) - * @param path_len number of entries on the @a path, at least three! - * @param network_latency how long does the message take from us to - * `path[path_len-1]`? set to "forever" if unknown - * @param path_valid_until how long is this path considered validated? Maybe - * be zero. - * @return #GNUNET_YES on success, - * #GNUNET_NO if we have better path(s) to the target - * #GNUNET_SYSERR if the path is useless and/or invalid - * (i.e. path[1] not a direct neighbour - * or path[i+1] is a direct neighbour for i>0) - */ -static int -learn_dv_path (const struct GNUNET_PeerIdentity *path, - unsigned int path_len, - struct GNUNET_TIME_Relative network_latency, - struct GNUNET_TIME_Absolute path_valid_until) -{ - struct DistanceVectorHop *hop; - struct DistanceVector *dv; - struct Neighbour *next_hop; - unsigned int shorter_distance; - - if (path_len < 3) - { - /* what a boring path! not allowed! */ - GNUNET_break (0); - return GNUNET_SYSERR; - } - GNUNET_assert (0 == GNUNET_memcmp (&GST_my_identity, &path[0])); - next_hop = lookup_neighbour (&path[1]); - if (NULL == next_hop) - { - /* next hop must be a neighbour, otherwise this whole thing is useless! */ - GNUNET_break (0); - return GNUNET_SYSERR; - } - for (unsigned int i = 2; i < path_len; i++) - if (NULL != lookup_neighbour (&path[i])) - { - /* Useless path: we have a direct connection to some hop - in the middle of the path, so this one is not even - terribly useful for redundancy */ - GNUNET_log (GNUNET_ERROR_TYPE_INFO, - "Path of %u hops useless: directly link to hop %u (%s)\n", - path_len, - i, - GNUNET_i2s (&path[i])); - GNUNET_STATISTICS_update (GST_stats, - "# Useless DV path ignored: hop is neighbour", - 1, - GNUNET_NO); - return GNUNET_SYSERR; - } - dv = GNUNET_CONTAINER_multipeermap_get (dv_routes, &path[path_len - 1]); - if (NULL == dv) - { - dv = GNUNET_new (struct DistanceVector); - dv->target = path[path_len - 1]; - dv->timeout_task = GNUNET_SCHEDULER_add_delayed (DV_PATH_VALIDITY_TIMEOUT, - &path_cleanup_cb, - dv); - GNUNET_assert (GNUNET_OK == - GNUNET_CONTAINER_multipeermap_put ( - dv_routes, - &dv->target, - dv, - GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_ONLY)); - } - /* Check if we have this path already! */ - shorter_distance = 0; - for (struct DistanceVectorHop *pos = dv->dv_head; NULL != pos; - pos = pos->next_dv) - { - if (pos->distance < path_len - 3) - shorter_distance++; - /* Note that the distances in 'pos' excludes us (path[0]), - the next_hop (path[1]) and the target so we need to subtract three - and check next_hop explicitly */ - if ((pos->distance == path_len - 3) && (pos->next_hop == next_hop)) - { - int match = GNUNET_YES; - - for (unsigned int i = 0; i < pos->distance; i++) - { - if (0 != GNUNET_memcmp (&pos->path[i], &path[i + 2])) - { - match = GNUNET_NO; - break; - } - } - if (GNUNET_YES == match) - { - struct GNUNET_TIME_Relative last_timeout; - - /* Re-discovered known path, update timeout */ - GNUNET_STATISTICS_update (GST_stats, - "# Known DV path refreshed", - 1, - GNUNET_NO); - last_timeout = GNUNET_TIME_absolute_get_remaining (pos->timeout); - pos->timeout = - GNUNET_TIME_relative_to_absolute (DV_PATH_VALIDITY_TIMEOUT); - pos->path_valid_until = - GNUNET_TIME_absolute_max (pos->path_valid_until, path_valid_until); - GNUNET_CONTAINER_MDLL_remove (dv, dv->dv_head, dv->dv_tail, pos); - GNUNET_CONTAINER_MDLL_insert (dv, dv->dv_head, dv->dv_tail, pos); - if (0 < - GNUNET_TIME_absolute_get_remaining (path_valid_until).rel_value_us) - activate_core_visible_dv_path (pos); - if (last_timeout.rel_value_us < - GNUNET_TIME_relative_subtract (DV_PATH_VALIDITY_TIMEOUT, - DV_PATH_DISCOVERY_FREQUENCY) - .rel_value_us) - { - /* Some peer send DV learn messages too often, we are learning - the same path faster than it would be useful; do not forward! */ - GNUNET_log (GNUNET_ERROR_TYPE_INFO, - "Rediscovered path too quickly, not forwarding further\n"); - return GNUNET_NO; - } - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "Refreshed known path to %s valid until %s, forwarding further\n", - GNUNET_i2s (&dv->target), - GNUNET_STRINGS_absolute_time_to_string ( - pos->path_valid_until)); - return GNUNET_YES; - } - } - } - /* Count how many shorter paths we have (incl. direct - neighbours) before simply giving up on this one! */ - if (shorter_distance >= MAX_DV_PATHS_TO_TARGET) - { - /* We have a shorter path already! */ - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "Have many shorter DV paths %s, not forwarding further\n", - GNUNET_i2s (&dv->target)); - return GNUNET_NO; - } - /* create new DV path entry */ - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "Discovered new DV path to %s valid until %s\n", - GNUNET_i2s (&dv->target), - GNUNET_STRINGS_absolute_time_to_string (path_valid_until)); - hop = GNUNET_malloc (sizeof(struct DistanceVectorHop) - + sizeof(struct GNUNET_PeerIdentity) * (path_len - 3)); - hop->next_hop = next_hop; - hop->dv = dv; - hop->path = (const struct GNUNET_PeerIdentity *) &hop[1]; - memcpy (&hop[1], - &path[2], - sizeof(struct GNUNET_PeerIdentity) * (path_len - 3)); - hop->timeout = GNUNET_TIME_relative_to_absolute (DV_PATH_VALIDITY_TIMEOUT); - hop->path_valid_until = path_valid_until; - hop->distance = path_len - 3; - hop->pd.aged_rtt = network_latency; - GNUNET_CONTAINER_MDLL_insert (dv, dv->dv_head, dv->dv_tail, hop); - GNUNET_CONTAINER_MDLL_insert (neighbour, - next_hop->dv_head, - next_hop->dv_tail, - hop); - if (0 < GNUNET_TIME_absolute_get_remaining (path_valid_until).rel_value_us) - activate_core_visible_dv_path (hop); - return GNUNET_YES; -} - - -/** - * Communicator gave us a DV learn message. Check the message. - * - * @param cls a `struct CommunicatorMessageContext` - * @param dvl the send message that was sent - * @return #GNUNET_YES if message is well-formed - */ -static int -check_dv_learn (void *cls, const struct TransportDVLearnMessage *dvl) -{ - uint16_t size = ntohs (dvl->header.size); - uint16_t num_hops = ntohs (dvl->num_hops); - const struct DVPathEntryP *hops = (const struct DVPathEntryP *) &dvl[1]; - - (void) cls; - if (size != sizeof(*dvl) + num_hops * sizeof(struct DVPathEntryP)) - { - GNUNET_break_op (0); - return GNUNET_SYSERR; - } - if (num_hops > MAX_DV_HOPS_ALLOWED) - { - GNUNET_break_op (0); - return GNUNET_SYSERR; - } - for (unsigned int i = 0; i < num_hops; i++) - { - if (0 == GNUNET_memcmp (&dvl->initiator, &hops[i].hop)) - { - GNUNET_break_op (0); - return GNUNET_SYSERR; - } - if (0 == GNUNET_memcmp (&GST_my_identity, &hops[i].hop)) - { - GNUNET_break_op (0); - return GNUNET_SYSERR; - } - } - return GNUNET_YES; -} - - -/** - * Build and forward a DV learn message to @a next_hop. - * - * @param next_hop peer to send the message to - * @param msg message received - * @param bi_history bitmask specifying hops on path that were bidirectional - * @param nhops length of the @a hops array - * @param hops path the message traversed so far - * @param in_time when did we receive the message, used to calculate network - * delay - */ -static void -forward_dv_learn (const struct GNUNET_PeerIdentity *next_hop, - const struct TransportDVLearnMessage *msg, - uint16_t bi_history, - uint16_t nhops, - const struct DVPathEntryP *hops, - struct GNUNET_TIME_Absolute in_time) -{ - struct Neighbour *n; - struct VirtualLink *vl; - struct DVPathEntryP *dhops; - char buf[sizeof(struct TransportDVLearnMessage) - + (nhops + 1) * sizeof(struct DVPathEntryP)] GNUNET_ALIGN; - struct TransportDVLearnMessage *fwd = (struct TransportDVLearnMessage *) buf; - struct GNUNET_TIME_Relative nnd; - - /* compute message for forwarding */ - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "Forwarding DV learn message originating from %s to %s\n", - GNUNET_i2s (&msg->initiator), - GNUNET_i2s2 (next_hop)); - GNUNET_assert (nhops < MAX_DV_HOPS_ALLOWED); - fwd->header.type = htons (GNUNET_MESSAGE_TYPE_TRANSPORT_DV_LEARN); - fwd->header.size = htons (sizeof(struct TransportDVLearnMessage) - + (nhops + 1) * sizeof(struct DVPathEntryP)); - fwd->num_hops = htons (nhops + 1); - fwd->bidirectional = htons (bi_history); - nnd = GNUNET_TIME_relative_add (GNUNET_TIME_absolute_get_duration (in_time), - GNUNET_TIME_relative_ntoh ( - msg->non_network_delay)); - fwd->non_network_delay = GNUNET_TIME_relative_hton (nnd); - fwd->init_sig = msg->init_sig; - fwd->initiator = msg->initiator; - fwd->challenge = msg->challenge; - fwd->monotonic_time = msg->monotonic_time; - dhops = (struct DVPathEntryP *) &fwd[1]; - GNUNET_memcpy (dhops, hops, sizeof(struct DVPathEntryP) * nhops); - dhops[nhops].hop = GST_my_identity; - { - struct DvHopPS dhp = { - .purpose.purpose = htonl (GNUNET_SIGNATURE_PURPOSE_TRANSPORT_DV_HOP), - .purpose.size = htonl (sizeof(dhp)), - .pred = (0 == nhops) ? msg->initiator : dhops[nhops - 1].hop, - .succ = *next_hop, - .challenge = msg->challenge - }; - GNUNET_CRYPTO_eddsa_sign (GST_my_private_key, - &dhp, - &dhops[nhops].hop_sig); - } - /*route_control_message_without_fc (next_hop, - &fwd->header, - RMO_UNCONFIRMED_ALLOWED);*/ - vl = lookup_virtual_link (next_hop); - if ((NULL != vl) && (GNUNET_YES == vl->confirmed)) - { - route_control_message_without_fc (vl, - &fwd->header, - RMO_UNCONFIRMED_ALLOWED); - } - else - { - /* Use route via neighbour */ - n = lookup_neighbour (next_hop); - if (NULL != n) - route_via_neighbour ( - n, - &fwd->header, - RMO_UNCONFIRMED_ALLOWED); - } -} - - -/** - * Check signature of type #GNUNET_SIGNATURE_PURPOSE_TRANSPORT_DV_INITIATOR - * - * @param sender_monotonic_time monotonic time of the initiator - * @param init the signer - * @param challenge the challenge that was signed - * @param init_sig signature presumably by @a init - * @return #GNUNET_OK if the signature is valid - */ -static int -validate_dv_initiator_signature ( - struct GNUNET_TIME_AbsoluteNBO sender_monotonic_time, - const struct GNUNET_PeerIdentity *init, - const struct GNUNET_CRYPTO_ChallengeNonceP *challenge, - const struct GNUNET_CRYPTO_EddsaSignature *init_sig) -{ - struct DvInitPS ip = { .purpose.purpose = htonl ( - GNUNET_SIGNATURE_PURPOSE_TRANSPORT_DV_INITIATOR), - .purpose.size = htonl (sizeof(ip)), - .monotonic_time = sender_monotonic_time, - .challenge = *challenge }; - - if ( - GNUNET_OK != - GNUNET_CRYPTO_eddsa_verify (GNUNET_SIGNATURE_PURPOSE_TRANSPORT_DV_INITIATOR, - &ip, - init_sig, - &init->public_key)) - { - GNUNET_break_op (0); - return GNUNET_SYSERR; - } - return GNUNET_OK; -} - - -/** - * Closure for #dv_neighbour_selection and #dv_neighbour_transmission. - */ -struct NeighbourSelectionContext -{ - /** - * Original message we received. - */ - const struct TransportDVLearnMessage *dvl; - - /** - * The hops taken. - */ - const struct DVPathEntryP *hops; - - /** - * Time we received the message. - */ - struct GNUNET_TIME_Absolute in_time; - - /** - * Offsets of the selected peers. - */ - uint32_t selections[MAX_DV_DISCOVERY_SELECTION]; - - /** - * Number of peers eligible for selection. - */ - unsigned int num_eligible; - - /** - * Number of peers that were selected for forwarding. - */ - unsigned int num_selections; - - /** - * Number of hops in @e hops - */ - uint16_t nhops; - - /** - * Bitmap of bidirectional connections encountered. - */ - uint16_t bi_history; -}; - - -/** - * Function called for each neighbour during #handle_dv_learn. - * - * @param cls a `struct NeighbourSelectionContext *` - * @param pid identity of the peer - * @param value a `struct Neighbour` - * @return #GNUNET_YES (always) - */ -static int -dv_neighbour_selection (void *cls, - const struct GNUNET_PeerIdentity *pid, - void *value) -{ - struct NeighbourSelectionContext *nsc = cls; - - (void) value; - if (0 == GNUNET_memcmp (pid, &nsc->dvl->initiator)) - return GNUNET_YES; /* skip initiator */ - for (unsigned int i = 0; i < nsc->nhops; i++) - if (0 == GNUNET_memcmp (pid, &nsc->hops[i].hop)) - return GNUNET_YES; - /* skip peers on path */ - nsc->num_eligible++; - return GNUNET_YES; -} - - -/** - * Function called for each neighbour during #handle_dv_learn. - * We call #forward_dv_learn() on the neighbour(s) selected - * during #dv_neighbour_selection(). - * - * @param cls a `struct NeighbourSelectionContext *` - * @param pid identity of the peer - * @param value a `struct Neighbour` - * @return #GNUNET_YES (always) - */ -static int -dv_neighbour_transmission (void *cls, - const struct GNUNET_PeerIdentity *pid, - void *value) -{ - struct NeighbourSelectionContext *nsc = cls; - - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "transmission %s\n", - GNUNET_i2s (pid)); - (void) value; - if (0 == GNUNET_memcmp (pid, &nsc->dvl->initiator)) - return GNUNET_YES; /* skip initiator */ - for (unsigned int i = 0; i < nsc->nhops; i++) - if (0 == GNUNET_memcmp (pid, &nsc->hops[i].hop)) - return GNUNET_YES; - /* skip peers on path */ - for (unsigned int i = 0; i < nsc->num_selections; i++) - { - if (nsc->selections[i] == nsc->num_eligible) - { - forward_dv_learn (pid, - nsc->dvl, - nsc->bi_history, - nsc->nhops, - nsc->hops, - nsc->in_time); - break; - } - } - nsc->num_eligible++; - return GNUNET_YES; -} - - -/** - * Computes the number of neighbours we should forward a DVInit - * message to given that it has so far taken @a hops_taken hops - * though the network and that the number of neighbours we have - * in total is @a neighbour_count, out of which @a eligible_count - * are not yet on the path. - * - * NOTE: technically we might want to include NSE in the formula to - * get a better grip on the overall network size. However, for now - * using NSE here would create a dependency issue in the build system. - * => Left for later, hardcoded to 50 for now. - * - * The goal of the fomula is that we want to reach a total of LOG(NSE) - * peers via DV (`target_total`). We want the reach to be spread out - * over various distances to the origin, with a bias towards shorter - * distances. - * - * We make the strong assumption that the network topology looks - * "similar" at other hops, in particular the @a neighbour_count - * should be comparable at other hops. - * - * If the local neighbourhood is densely connected, we expect that @a - * eligible_count is close to @a neighbour_count minus @a hops_taken - * as a lot of the path is already known. In that case, we should - * forward to few(er) peers to try to find a path out of the - * neighbourhood. OTOH, if @a eligible_count is close to @a - * neighbour_count, we should forward to many peers as we are either - * still close to the origin (i.e. @a hops_taken is small) or because - * we managed to get beyond a local cluster. We express this as - * the `boost_factor` using the square of the fraction of eligible - * neighbours (so if only 50% are eligible, we boost by 1/4, but if - * 99% are eligible, the 'boost' will be almost 1). - * - * Second, the more hops we have taken, the larger the problem of an - * exponential traffic explosion gets. So we take the `target_total`, - * and compute our degree such that at each distance d 2^{-d} peers - * are selected (corrected by the `boost_factor`). - * - * @param hops_taken number of hops DVInit has travelled so far - * @param neighbour_count number of neighbours we have in total - * @param eligible_count number of neighbours we could in - * theory forward to - */ -static unsigned int -calculate_fork_degree (unsigned int hops_taken, - unsigned int neighbour_count, - unsigned int eligible_count) -{ - double target_total = 50.0; /* FIXME: use LOG(NSE)? */ - double eligible_ratio = - ((double) eligible_count) / ((double) neighbour_count); - double boost_factor = eligible_ratio * eligible_ratio; - unsigned int rnd; - double left; - - if (hops_taken >= 64) - { - GNUNET_break (0); - return 0; /* precaution given bitshift below */ - } - for (unsigned int i = 1; i < hops_taken; i++) - { - /* For each hop, subtract the expected number of targets - reached at distance d (so what remains divided by 2^d) */ - target_total -= (target_total * boost_factor / (1LLU << i)); - } - rnd = - (unsigned int) floor (target_total * boost_factor / (1LLU << hops_taken)); - /* round up or down probabilistically depending on how close we were - when floor()ing to rnd */ - left = target_total - (double) rnd; - if (UINT32_MAX * left > - GNUNET_CRYPTO_random_u64 (GNUNET_CRYPTO_QUALITY_WEAK, UINT32_MAX)) - rnd++; /* round up */ - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "Forwarding DV learn message of %u hops %u(/%u/%u) times\n", - hops_taken, - rnd, - eligible_count, - neighbour_count); - return rnd; -} - - -/** - * Function called when peerstore is done storing a DV monotonic time. - * - * @param cls a `struct Neighbour` - * @param success #GNUNET_YES if peerstore was successful - */ -static void -neighbour_store_dvmono_cb (void *cls, int success) -{ - struct Neighbour *n = cls; - - n->sc = NULL; - if (GNUNET_YES != success) - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - "Failed to store other peer's monotonic time in peerstore!\n"); -} - - -/** - * Communicator gave us a DV learn message. Process the request. - * - * @param cls a `struct CommunicatorMessageContext` (must call - * #finish_cmc_handling() when done) - * @param dvl the message that was received - */ -static void -handle_dv_learn (void *cls, const struct TransportDVLearnMessage *dvl) -{ - struct CommunicatorMessageContext *cmc = cls; - enum GNUNET_TRANSPORT_CommunicatorCharacteristics cc; - int bi_hop; - uint16_t nhops; - uint16_t bi_history; - const struct DVPathEntryP *hops; - int do_fwd; - int did_initiator; - struct GNUNET_TIME_Absolute in_time; - struct Neighbour *n; - - nhops = ntohs (dvl->num_hops); /* 0 = sender is initiator */ - bi_history = ntohs (dvl->bidirectional); - hops = (const struct DVPathEntryP *) &dvl[1]; - if (0 == nhops) - { - /* sanity check */ - if (0 != GNUNET_memcmp (&dvl->initiator, &cmc->im.sender)) - { - GNUNET_break (0); - finish_cmc_handling (cmc); - return; - } - } - else - { - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "handle dv learn message last hop %s\n", - GNUNET_i2s (&hops[nhops - 1].hop)); - /* sanity check */ - if (0 != GNUNET_memcmp (&hops[nhops - 1].hop, &cmc->im.sender)) - { - GNUNET_break (0); - finish_cmc_handling (cmc); - return; - } - } - - GNUNET_assert (CT_COMMUNICATOR == cmc->tc->type); - cc = cmc->tc->details.communicator.cc; - bi_hop = (GNUNET_TRANSPORT_CC_RELIABLE == - cc); // FIXME: add bi-directional flag to cc? - in_time = GNUNET_TIME_absolute_get (); - - /* continue communicator here, everything else can happen asynchronous! */ - finish_cmc_handling (cmc); - - n = lookup_neighbour (&dvl->initiator); - if (NULL != n) - { - if ((n->dv_monotime_available == GNUNET_YES) && - (GNUNET_TIME_absolute_ntoh (dvl->monotonic_time).abs_value_us < - n->last_dv_learn_monotime.abs_value_us)) - { - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "DV learn from %s discarded due to time travel", - GNUNET_i2s (&dvl->initiator)); - GNUNET_STATISTICS_update (GST_stats, - "# DV learn discarded due to time travel", - 1, - GNUNET_NO); - return; - } - if (GNUNET_OK != validate_dv_initiator_signature (dvl->monotonic_time, - &dvl->initiator, - &dvl->challenge, - &dvl->init_sig)) - { - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "DV learn signature from %s invalid\n", - GNUNET_i2s (&dvl->initiator)); - GNUNET_break_op (0); - return; - } - n->last_dv_learn_monotime = GNUNET_TIME_absolute_ntoh (dvl->monotonic_time); - if (GNUNET_YES == n->dv_monotime_available) - { - if (NULL != n->sc) - { - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "store cancel\n"); - GNUNET_PEERSTORE_store_cancel (n->sc); - } - n->sc = - GNUNET_PEERSTORE_store (peerstore, - "transport", - &dvl->initiator, - GNUNET_PEERSTORE_TRANSPORT_DVLEARN_MONOTIME, - &dvl->monotonic_time, - sizeof(dvl->monotonic_time), - GNUNET_TIME_UNIT_FOREVER_ABS, - GNUNET_PEERSTORE_STOREOPTION_REPLACE, - &neighbour_store_dvmono_cb, - n); - } - } - /* OPTIMIZE-FIXME: asynchronously (!) verify signatures!, - If signature verification load too high, implement random drop strategy */ - for (unsigned int i = 0; i < nhops; i++) - { - struct DvHopPS dhp = { .purpose.purpose = - htonl (GNUNET_SIGNATURE_PURPOSE_TRANSPORT_DV_HOP), - .purpose.size = htonl (sizeof(dhp)), - .pred = (0 == i) ? dvl->initiator : hops[i - 1].hop, - .succ = (nhops == i + 1) ? GST_my_identity - : hops[i + 1].hop, - .challenge = dvl->challenge }; - - if (GNUNET_OK != - GNUNET_CRYPTO_eddsa_verify (GNUNET_SIGNATURE_PURPOSE_TRANSPORT_DV_HOP, - &dhp, - &hops[i].hop_sig, - &hops[i].hop.public_key)) - { - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "DV learn from %s signature of hop %u invalid\n", - GNUNET_i2s (&dvl->initiator), - i); - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "signature of hop %s invalid\n", - GNUNET_i2s (&hops[i].hop)); - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "pred %s\n", - GNUNET_i2s (&dhp.pred)); - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "succ %s\n", - GNUNET_i2s (&dhp.succ)); - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "hash %s\n", - GNUNET_sh2s (&dhp.challenge.value)); - GNUNET_break_op (0); - return; - } - } - if (GNUNET_EXTRA_LOGGING > 0) - { - char *path; - - path = GNUNET_strdup (GNUNET_i2s (&dvl->initiator)); - for (unsigned int i = 0; i < nhops; i++) - { - char *tmp; - - GNUNET_asprintf (&tmp, - "%s%s%s", - path, - (bi_history & (1 << (nhops - i))) ? "<->" : "-->", - GNUNET_i2s (&hops[i].hop)); - GNUNET_free (path); - path = tmp; - } - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "Received DVInit via %s%s%s\n", - path, - bi_hop ? "<->" : "-->", - GNUNET_i2s (&GST_my_identity)); - GNUNET_free (path); - } - do_fwd = GNUNET_YES; - if (0 == GNUNET_memcmp (&GST_my_identity, &dvl->initiator)) - { - struct GNUNET_PeerIdentity path[nhops + 1]; - struct GNUNET_TIME_Relative host_latency_sum; - struct GNUNET_TIME_Relative latency; - struct GNUNET_TIME_Relative network_latency; - - /* We initiated this, learn the forward path! */ - path[0] = GST_my_identity; - path[1] = hops[0].hop; - host_latency_sum = GNUNET_TIME_relative_ntoh (dvl->non_network_delay); - - // Need also something to lookup initiation time - // to compute RTT! -> add RTT argument here? - latency = GNUNET_TIME_absolute_get_duration (GNUNET_TIME_absolute_ntoh ( - dvl->monotonic_time)); - GNUNET_assert (latency.rel_value_us >= host_latency_sum.rel_value_us); - // latency = GNUNET_TIME_UNIT_FOREVER_REL; // FIXME: initialize properly - // (based on dvl->challenge, we can identify time of origin!) - - network_latency = GNUNET_TIME_relative_subtract (latency, host_latency_sum); - /* assumption: latency on all links is the same */ - network_latency = GNUNET_TIME_relative_divide (network_latency, nhops); - - for (unsigned int i = 2; i <= nhops; i++) - { - struct GNUNET_TIME_Relative ilat; - - /* assumption: linear latency increase per hop */ - ilat = GNUNET_TIME_relative_multiply (network_latency, i); - path[i] = hops[i - 1].hop; - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "Learned path with %u hops to %s with latency %s\n", - i, - GNUNET_i2s (&path[i]), - GNUNET_STRINGS_relative_time_to_string (ilat, GNUNET_YES)); - learn_dv_path (path, - i + 1, - ilat, - GNUNET_TIME_relative_to_absolute ( - ADDRESS_VALIDATION_LIFETIME)); - } - /* as we initiated, do not forward again (would be circular!) */ - do_fwd = GNUNET_NO; - return; - } - if (bi_hop) - { - /* last hop was bi-directional, we could learn something here! */ - struct GNUNET_PeerIdentity path[nhops + 2]; - - path[0] = GST_my_identity; - path[1] = hops[nhops - 1].hop; /* direct neighbour == predecessor! */ - for (unsigned int i = 0; i < nhops; i++) - { - int iret; - - if (0 == (bi_history & (1 << i))) - break; /* i-th hop not bi-directional, stop learning! */ - if (i == nhops - 1) - { - path[i + 2] = dvl->initiator; - } - else - { - path[i + 2] = hops[nhops - i - 2].hop; - } - - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "Learned inverse path with %u hops to %s\n", - i + 2, - GNUNET_i2s (&path[i + 2])); - iret = learn_dv_path (path, - i + 3, - GNUNET_TIME_UNIT_FOREVER_REL, - GNUNET_TIME_relative_to_absolute ( - ADDRESS_VALIDATION_LIFETIME)); - if (GNUNET_SYSERR == iret) - { - /* path invalid or too long to be interesting for US, thus should also - not be interesting to our neighbours, cut path when forwarding to - 'i' hops, except of course for the one that goes back to the - initiator */ - GNUNET_STATISTICS_update (GST_stats, - "# DV learn not forwarded due invalidity of path", - 1, - GNUNET_NO); - do_fwd = GNUNET_NO; - break; - } - if ((GNUNET_NO == iret) && (nhops == i + 1)) - { - /* we have better paths, and this is the longest target, - so there cannot be anything interesting later */ - GNUNET_STATISTICS_update (GST_stats, - "# DV learn not forwarded, got better paths", - 1, - GNUNET_NO); - do_fwd = GNUNET_NO; - break; - } - } - } - if (MAX_DV_HOPS_ALLOWED == nhops) - { - /* At limit, we're out of here! */ - return; - } - - /* Forward to initiator, if path non-trivial and possible */ - bi_history = (bi_history << 1) | (bi_hop ? 1 : 0); - did_initiator = GNUNET_NO; - if ((1 <= nhops) && - (GNUNET_YES == - GNUNET_CONTAINER_multipeermap_contains (neighbours, &dvl->initiator))) - { - /* send back to origin! */ - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "Sending DVL back to initiator %s\n", - GNUNET_i2s (&dvl->initiator)); - forward_dv_learn (&dvl->initiator, dvl, bi_history, nhops, hops, in_time); - did_initiator = GNUNET_YES; - } - /* We forward under two conditions: either we still learned something - ourselves (do_fwd), or the path was darn short and thus the initiator is - likely to still be very interested in this (and we did NOT already - send it back to the initiator) */ - if ((do_fwd) || ((nhops < MIN_DV_PATH_LENGTH_FOR_INITIATOR) && - (GNUNET_NO == did_initiator))) - { - /* Pick random neighbours that are not yet on the path */ - struct NeighbourSelectionContext nsc; - unsigned int n_cnt; - - n_cnt = GNUNET_CONTAINER_multipeermap_size (neighbours); - nsc.nhops = nhops; - nsc.dvl = dvl; - nsc.bi_history = bi_history; - nsc.hops = hops; - nsc.in_time = in_time; - nsc.num_eligible = 0; - GNUNET_CONTAINER_multipeermap_iterate (neighbours, - &dv_neighbour_selection, - &nsc); - if (0 == nsc.num_eligible) - return; /* done here, cannot forward to anyone else */ - nsc.num_selections = calculate_fork_degree (nhops, n_cnt, nsc.num_eligible); - nsc.num_selections = - GNUNET_MIN (MAX_DV_DISCOVERY_SELECTION, nsc.num_selections); - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "Forwarding DVL to %u other peers\n", - nsc.num_selections); - for (unsigned int i = 0; i < nsc.num_selections; i++) - nsc.selections[i] = - (nsc.num_selections == n_cnt) - ? i /* all were selected, avoid collisions by chance */ - : GNUNET_CRYPTO_random_u32 (GNUNET_CRYPTO_QUALITY_WEAK, n_cnt); - nsc.num_eligible = 0; - GNUNET_CONTAINER_multipeermap_iterate (neighbours, - &dv_neighbour_transmission, - &nsc); - } -} - - -/** - * Communicator gave us a DV box. Check the message. - * - * @param cls a `struct CommunicatorMessageContext` - * @param dvb the send message that was sent - * @return #GNUNET_YES if message is well-formed - */ -static int -check_dv_box (void *cls, const struct TransportDVBoxMessage *dvb) -{ - uint16_t size = ntohs (dvb->header.size); - uint16_t num_hops = ntohs (dvb->num_hops); - const struct GNUNET_PeerIdentity *hops = - (const struct GNUNET_PeerIdentity *) &dvb[1]; - - (void) cls; - if (size < sizeof(*dvb) + num_hops * sizeof(struct GNUNET_PeerIdentity) - + sizeof(struct GNUNET_MessageHeader)) - { - GNUNET_break_op (0); - return GNUNET_SYSERR; - } - /* This peer must not be on the path */ - for (unsigned int i = 0; i < num_hops; i++) - if (0 == GNUNET_memcmp (&hops[i], &GST_my_identity)) - { - GNUNET_break_op (0); - return GNUNET_SYSERR; - } - return GNUNET_YES; -} - - -/** - * Create a DV Box message and queue it for transmission to - * @a next_hop. - * - * @param next_hop peer to receive the message next - * @param total_hops how many hops did the message take so far - * @param num_hops length of the @a hops array - * @param origin origin of the message - * @param hops next peer(s) to the destination, including destination - * @param payload payload of the box - * @param payload_size number of bytes in @a payload - */ -static void -forward_dv_box (struct Neighbour *next_hop, - struct TransportDVBoxMessage *hdr, - uint16_t total_hops, - uint16_t num_hops, - const struct GNUNET_PeerIdentity *hops, - const void *enc_payload, - uint16_t enc_payload_size) -{ - struct VirtualLink *vl = next_hop->vl; - struct PendingMessage *pm; - size_t msg_size = sizeof(struct TransportDVBoxMessage) - + num_hops * sizeof(struct GNUNET_PeerIdentity) - + enc_payload_size; - char *buf; - char msg_buf[msg_size] GNUNET_ALIGN; - struct GNUNET_PeerIdentity *dhops; - - hdr->num_hops = htons (num_hops); - hdr->total_hops = htons (total_hops); - hdr->header.size = htons (msg_size); - memcpy (msg_buf, hdr, sizeof(*hdr)); - dhops = (struct GNUNET_PeerIdentity *) &msg_buf[sizeof(struct - TransportDVBoxMessage)]; - memcpy (dhops, hops, num_hops * sizeof(struct GNUNET_PeerIdentity)); - memcpy (&dhops[num_hops], enc_payload, enc_payload_size); - - if (GNUNET_YES == ntohs (hdr->without_fc)) - { - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "Forwarding control message (payload size %u) in DV Box to next hop %s (%u/%u) \n", - enc_payload_size, - GNUNET_i2s (&next_hop->pid), - (unsigned int) num_hops, - (unsigned int) total_hops); - route_via_neighbour (next_hop, (const struct - GNUNET_MessageHeader *) msg_buf, - RMO_ANYTHING_GOES); - } - else - { - pm = GNUNET_malloc (sizeof(struct PendingMessage) + msg_size); - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "2 created pm %p storing vl %p \n", - pm, - vl); - pm->pmt = PMT_DV_BOX; - pm->vl = vl; - pm->target = next_hop->pid; - pm->timeout = GNUNET_TIME_relative_to_absolute (DV_FORWARD_TIMEOUT); - pm->logging_uuid = logging_uuid_gen++; - pm->prefs = GNUNET_MQ_PRIO_BACKGROUND; - pm->bytes_msg = msg_size; - buf = (char *) &pm[1]; - memcpy (buf, msg_buf, msg_size); - - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "Created pending message %llu for DV Box with next hop %s (%u/%u)\n", - pm->logging_uuid, - GNUNET_i2s (&next_hop->pid), - (unsigned int) num_hops, - (unsigned int) total_hops); - - if ((NULL != vl) && (GNUNET_YES == vl->confirmed)) - { - GNUNET_CONTAINER_MDLL_insert (vl, - vl->pending_msg_head, - vl->pending_msg_tail, - pm); - - check_vl_transmission (vl); - } - else - { - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "The virtual link is not ready for forwarding a DV Box with payload, storing PendingMessage in ring buffer.\n"); - - ring_buffer_dv[ring_buffer_dv_head] = pm; - if (RING_BUFFER_SIZE - 1 == ring_buffer_dv_head) - { - ring_buffer_dv_head = 0; - is_ring_buffer_dv_full = GNUNET_YES; - } - else - ring_buffer_dv_head++; - - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "%u items stored in DV ring buffer\n", - ring_buffer_dv_head); - } - } -} - - -/** - * Free data structures associated with @a b. - * - * @param b data structure to release - */ -static void -free_backtalker (struct Backtalker *b) -{ - if (NULL != b->get) - { - GNUNET_PEERSTORE_iterate_cancel (b->get); - b->get = NULL; - GNUNET_assert (NULL != b->cmc); - finish_cmc_handling (b->cmc); - b->cmc = NULL; - } - if (NULL != b->task) - { - GNUNET_SCHEDULER_cancel (b->task); - b->task = NULL; - } - if (NULL != b->sc) - { - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "store cancel\n"); - GNUNET_PEERSTORE_store_cancel (b->sc); - b->sc = NULL; - } - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "Removing backtalker for %s\n", - GNUNET_i2s (&b->pid)); - GNUNET_assert ( - GNUNET_YES == - GNUNET_CONTAINER_multipeermap_remove (backtalkers, &b->pid, b)); - GNUNET_free (b); -} - - -/** - * Callback to free backtalker records. - * - * @param cls NULL - * @param pid unused - * @param value a `struct Backtalker` - * @return #GNUNET_OK (always) - */ -static int -free_backtalker_cb (void *cls, - const struct GNUNET_PeerIdentity *pid, - void *value) -{ - struct Backtalker *b = value; - - (void) cls; - (void) pid; - free_backtalker (b); - return GNUNET_OK; -} - - -/** - * Function called when it is time to clean up a backtalker. - * - * @param cls a `struct Backtalker` - */ -static void -backtalker_timeout_cb (void *cls) -{ - struct Backtalker *b = cls; - - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "backtalker timeout.\n"); - b->task = NULL; - if (0 != GNUNET_TIME_absolute_get_remaining (b->timeout).rel_value_us) - { - b->task = GNUNET_SCHEDULER_add_at (b->timeout, &backtalker_timeout_cb, b); - return; - } - GNUNET_assert (NULL == b->sc); - free_backtalker (b); -} - - -/** - * Function called with the monotonic time of a backtalker - * by PEERSTORE. Updates the time and continues processing. - * - * @param cls a `struct Backtalker` - * @param record the information found, NULL for the last call - * @param emsg error message - */ -static void -backtalker_monotime_cb (void *cls, - const struct GNUNET_PEERSTORE_Record *record, - const char *emsg) -{ - struct Backtalker *b = cls; - struct GNUNET_TIME_AbsoluteNBO *mtbe; - struct GNUNET_TIME_Absolute mt; - - (void) emsg; - if (NULL == record) - { - /* we're done with #backtalker_monotime_cb() invocations, - continue normal processing */ - b->get = NULL; - GNUNET_assert (NULL != b->cmc); - b->cmc->mh = (const struct GNUNET_MessageHeader *) &b[1]; - if (0 != b->body_size) - demultiplex_with_cmc (b->cmc); - else - finish_cmc_handling (b->cmc); - b->cmc = NULL; - return; - } - if (sizeof(*mtbe) != record->value_size) - { - GNUNET_break (0); - return; - } - mtbe = record->value; - mt = GNUNET_TIME_absolute_ntoh (*mtbe); - if (mt.abs_value_us > b->monotonic_time.abs_value_us) - { - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "Backtalker message from %s dropped, monotime in the past\n", - GNUNET_i2s (&b->pid)); - GNUNET_STATISTICS_update ( - GST_stats, - "# Backchannel messages dropped: monotonic time not increasing", - 1, - GNUNET_NO); - b->monotonic_time = mt; - /* Setting body_size to 0 prevents call to #forward_backchannel_payload() - */ - b->body_size = 0; - return; - } -} - - -/** - * Function called by PEERSTORE when the store operation of - * a backtalker's monotonic time is complete. - * - * @param cls the `struct Backtalker` - * @param success #GNUNET_OK on success - */ -static void -backtalker_monotime_store_cb (void *cls, int success) -{ - struct Backtalker *b = cls; - - if (GNUNET_OK != success) - { - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - "Failed to store backtalker's monotonic time in PEERSTORE!\n"); - } - b->sc = NULL; - if (NULL != b->task) - { - GNUNET_SCHEDULER_cancel (b->task); - b->task = NULL; - } - b->task = GNUNET_SCHEDULER_add_at (b->timeout, &backtalker_timeout_cb, b); -} - - -/** - * The backtalker @a b monotonic time changed. Update PEERSTORE. - * - * @param b a backtalker with updated monotonic time - */ -static void -update_backtalker_monotime (struct Backtalker *b) -{ - struct GNUNET_TIME_AbsoluteNBO mtbe; - - if (NULL != b->sc) - { - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "store cancel before store with sc %p\n", - b->sc); - /*GNUNET_PEERSTORE_store_cancel (b->sc); - b->sc = NULL;*/ - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "store cancel before store with sc %p is null\n", - b->sc); - } - else - { - GNUNET_SCHEDULER_cancel (b->task); - b->task = NULL; - } - mtbe = GNUNET_TIME_absolute_hton (b->monotonic_time); - b->sc = - GNUNET_PEERSTORE_store (peerstore, - "transport", - &b->pid, - GNUNET_PEERSTORE_TRANSPORT_BACKCHANNEL_MONOTIME, - &mtbe, - sizeof(mtbe), - GNUNET_TIME_UNIT_FOREVER_ABS, - GNUNET_PEERSTORE_STOREOPTION_REPLACE, - &backtalker_monotime_store_cb, - b); -} - - -/** - * Communicator gave us a DV box. Process the request. - * - * @param cls a `struct CommunicatorMessageContext` (must call - * #finish_cmc_handling() when done) - * @param dvb the message that was received - */ -static void -handle_dv_box (void *cls, const struct TransportDVBoxMessage *dvb) -{ - struct CommunicatorMessageContext *cmc = cls; - uint16_t size = ntohs (dvb->header.size) - sizeof(*dvb); - uint16_t num_hops = ntohs (dvb->num_hops); - const struct GNUNET_PeerIdentity *hops = - (const struct GNUNET_PeerIdentity *) &dvb[1]; - const char *enc_payload = (const char *) &hops[num_hops]; - uint16_t enc_payload_size = - size - (num_hops * sizeof(struct GNUNET_PeerIdentity)); - struct DVKeyState key; - struct GNUNET_HashCode hmac; - const char *hdr; - size_t hdr_len; - - if (GNUNET_EXTRA_LOGGING > 0) - { - char *path; - - path = GNUNET_strdup (GNUNET_i2s (&GST_my_identity)); - for (unsigned int i = 0; i < num_hops; i++) - { - char *tmp; - - GNUNET_asprintf (&tmp, "%s->%s", path, GNUNET_i2s (&hops[i])); - GNUNET_free (path); - path = tmp; - } - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "Received DVBox with remaining path %s\n", - path); - GNUNET_free (path); - } - - if (num_hops > 0) - { - /* We're trying from the end of the hops array, as we may be - able to find a shortcut unknown to the origin that way */ - for (int i = num_hops - 1; i >= 0; i--) - { - struct Neighbour *n; - - if (0 == GNUNET_memcmp (&hops[i], &GST_my_identity)) - { - GNUNET_break_op (0); - finish_cmc_handling (cmc); - return; - } - n = lookup_neighbour (&hops[i]); - if (NULL == n) - continue; - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "Skipping %u/%u hops ahead while routing DV Box\n", - i, - num_hops); - - forward_dv_box (n, - (struct TransportDVBoxMessage *) dvb, - ntohs (dvb->total_hops) + 1, - num_hops - i - 1, /* number of hops left */ - &hops[i + 1], /* remaining hops */ - enc_payload, - enc_payload_size); - GNUNET_STATISTICS_update (GST_stats, - "# DV hops skipped routing boxes", - i, - GNUNET_NO); - GNUNET_STATISTICS_update (GST_stats, - "# DV boxes routed (total)", - 1, - GNUNET_NO); - finish_cmc_handling (cmc); - return; - } - /* Woopsie, next hop not in neighbours, drop! */ - GNUNET_STATISTICS_update (GST_stats, - "# DV Boxes dropped: next hop unknown", - 1, - GNUNET_NO); - finish_cmc_handling (cmc); - return; - } - /* We are the target. Unbox and handle message. */ - GNUNET_STATISTICS_update (GST_stats, - "# DV boxes opened (ultimate target)", - 1, - GNUNET_NO); - cmc->total_hops = ntohs (dvb->total_hops); - - // DH key derivation with received DV, could be garbage. - struct GNUNET_HashCode km; - - if (GNUNET_YES != GNUNET_CRYPTO_eddsa_kem_decaps (GST_my_private_key, - &dvb->ephemeral_key, - &km)) - { - GNUNET_break_op (0); - finish_cmc_handling (cmc); - return; - } - dv_setup_key_state_from_km (&km, &dvb->iv, &key); - hdr = (const char *) &dvb[1]; - hdr_len = ntohs (dvb->orig_size) - sizeof(*dvb) - sizeof(struct - GNUNET_PeerIdentity) - * ntohs (dvb->total_hops); - - dv_hmac (&key, &hmac, hdr, hdr_len); - if (0 != GNUNET_memcmp (&hmac, &dvb->hmac)) - { - /* HMAC mismatch, discard! */ - GNUNET_break_op (0); - finish_cmc_handling (cmc); - return; - } - /* begin actual decryption */ - { - struct Backtalker *b; - struct GNUNET_TIME_Absolute monotime; - struct TransportDVBoxPayloadP ppay; - char body[hdr_len - sizeof(ppay)] GNUNET_ALIGN; - const struct GNUNET_MessageHeader *mh; - - GNUNET_assert (hdr_len >= - sizeof(ppay) + sizeof(struct GNUNET_MessageHeader)); - if (GNUNET_OK != dv_decrypt (&key, &ppay, hdr, sizeof(ppay))) - { - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - "Error decrypting DV payload header\n"); - GNUNET_break_op (0); - finish_cmc_handling (cmc); - return; - } - if (GNUNET_OK != dv_decrypt (&key, body, - &hdr[sizeof(ppay)], hdr_len - sizeof(ppay))) - { - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - "Error decrypting DV payload\n"); - GNUNET_break_op (0); - finish_cmc_handling (cmc); - return; - } - mh = (const struct GNUNET_MessageHeader *) body; - dv_key_clean (&key); - if (ntohs (mh->size) != sizeof(body)) - { - GNUNET_break_op (0); - finish_cmc_handling (cmc); - return; - } - /* need to prevent box-in-a-box (and DV_LEARN) so check inbox type! */ - switch (ntohs (mh->type)) - { - case GNUNET_MESSAGE_TYPE_TRANSPORT_DV_BOX: - GNUNET_break_op (0); - finish_cmc_handling (cmc); - return; - - case GNUNET_MESSAGE_TYPE_TRANSPORT_DV_LEARN: - GNUNET_break_op (0); - finish_cmc_handling (cmc); - return; - - default: - /* permitted, continue */ - break; - } - monotime = GNUNET_TIME_absolute_ntoh (ppay.monotonic_time); - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "Decrypted backtalk from %s\n", - GNUNET_i2s (&ppay.sender)); - b = GNUNET_CONTAINER_multipeermap_get (backtalkers, &ppay.sender); - if ((NULL != b) && (monotime.abs_value_us < b->monotonic_time.abs_value_us)) - { - GNUNET_STATISTICS_update ( - GST_stats, - "# Backchannel messages dropped: monotonic time not increasing", - 1, - GNUNET_NO); - finish_cmc_handling (cmc); - return; - } - if ((NULL == b) || - (0 != GNUNET_memcmp (&b->last_ephemeral, &dvb->ephemeral_key))) - { - /* Check signature */ - struct EphemeralConfirmationPS ec; - - ec.purpose.purpose = htonl (GNUNET_SIGNATURE_PURPOSE_TRANSPORT_EPHEMERAL); - ec.target = GST_my_identity; - ec.ephemeral_key = dvb->ephemeral_key; - ec.purpose.size = htonl (sizeof(ec)); - ec.sender_monotonic_time = ppay.monotonic_time; - if ( - GNUNET_OK != - GNUNET_CRYPTO_eddsa_verify ( - GNUNET_SIGNATURE_PURPOSE_TRANSPORT_EPHEMERAL, - &ec, - &ppay.sender_sig, - &ppay.sender.public_key)) - { - /* Signature invalid, discard! */ - GNUNET_break_op (0); - finish_cmc_handling (cmc); - return; - } - } - /* Update sender, we now know the real origin! */ - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "DVBox received for me from %s via %s\n", - GNUNET_i2s2 (&ppay.sender), - GNUNET_i2s (&cmc->im.sender)); - cmc->im.sender = ppay.sender; - - if (NULL != b) - { - /* update key cache and mono time */ - b->last_ephemeral = dvb->ephemeral_key; - b->monotonic_time = monotime; - update_backtalker_monotime (b); - b->timeout = - GNUNET_TIME_relative_to_absolute (BACKCHANNEL_INACTIVITY_TIMEOUT); - cmc->mh = mh; - demultiplex_with_cmc (cmc); - return; - } - /* setup data structure to cache signature AND check - monotonic time with PEERSTORE before forwarding backchannel payload */ - b = GNUNET_malloc (sizeof(struct Backtalker) + sizeof(body)); - b->pid = ppay.sender; - b->body_size = sizeof(body); - memcpy (&b[1], body, sizeof(body)); - GNUNET_assert (GNUNET_YES == - GNUNET_CONTAINER_multipeermap_put ( - backtalkers, - &b->pid, - b, - GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_ONLY)); - b->monotonic_time = monotime; /* NOTE: to be checked still! */ - b->cmc = cmc; - b->timeout = - GNUNET_TIME_relative_to_absolute (BACKCHANNEL_INACTIVITY_TIMEOUT); - b->task = GNUNET_SCHEDULER_add_at (b->timeout, &backtalker_timeout_cb, b); - b->get = - GNUNET_PEERSTORE_iterate (peerstore, - "transport", - &b->pid, - GNUNET_PEERSTORE_TRANSPORT_BACKCHANNEL_MONOTIME, - &backtalker_monotime_cb, - b); - } /* end actual decryption */ -} - - -/** - * Client notified us about transmission from a peer. Process the request. - * - * @param cls a `struct TransportClient` which sent us the message - * @param im the send message that was sent - * @return #GNUNET_YES if message is well-formed - */ -static int -check_incoming_msg (void *cls, - const struct GNUNET_TRANSPORT_IncomingMessage *im) -{ - struct TransportClient *tc = cls; - - if (CT_COMMUNICATOR != tc->type) - { - GNUNET_break (0); - return GNUNET_SYSERR; - } - GNUNET_MQ_check_boxed_message (im); - return GNUNET_OK; -} - - -/** - * Closure for #check_known_address. - */ -struct CheckKnownAddressContext -{ - /** - * Set to the address we are looking for. - */ - const char *address; - - /** - * Set to a matching validation state, if one was found. - */ - struct ValidationState *vs; -}; - - -/** - * Test if the validation state in @a value matches the - * address from @a cls. - * - * @param cls a `struct CheckKnownAddressContext` - * @param pid unused (must match though) - * @param value a `struct ValidationState` - * @return #GNUNET_OK if not matching, #GNUNET_NO if match found - */ -static int -check_known_address (void *cls, - const struct GNUNET_PeerIdentity *pid, - void *value) -{ - struct CheckKnownAddressContext *ckac = cls; - struct ValidationState *vs = value; - - (void) pid; - if (0 != strcmp (vs->address, ckac->address)) - return GNUNET_OK; - ckac->vs = vs; - return GNUNET_NO; -} - - -/** - * Task run periodically to validate some address based on #validation_heap. - * - * @param cls NULL - */ -static void -validation_start_cb (void *cls); - - -/** - * Set the time for next_challenge of @a vs to @a new_time. - * Updates the heap and if necessary reschedules the job. - * - * @param vs validation state to update - * @param new_time new time for revalidation - */ -static void -update_next_challenge_time (struct ValidationState *vs, - struct GNUNET_TIME_Absolute new_time) -{ - struct GNUNET_TIME_Relative delta; - - if (new_time.abs_value_us == vs->next_challenge.abs_value_us) - return; /* be lazy */ - vs->next_challenge = new_time; - if (NULL == vs->hn) - vs->hn = - GNUNET_CONTAINER_heap_insert (validation_heap, vs, new_time.abs_value_us); - else - GNUNET_CONTAINER_heap_update_cost (vs->hn, new_time.abs_value_us); - if ((vs != GNUNET_CONTAINER_heap_peek (validation_heap)) && - (NULL != validation_task)) - return; - if (NULL != validation_task) - GNUNET_SCHEDULER_cancel (validation_task); - /* randomize a bit */ - delta.rel_value_us = - GNUNET_CRYPTO_random_u64 (GNUNET_CRYPTO_QUALITY_WEAK, - MIN_DELAY_ADDRESS_VALIDATION.rel_value_us); - new_time = GNUNET_TIME_absolute_add (new_time, delta); - validation_task = - GNUNET_SCHEDULER_add_at (new_time, &validation_start_cb, NULL); -} - - -/** - * Start address validation. - * - * @param pid peer the @a address is for - * @param address an address to reach @a pid (presumably) - */ -static void -start_address_validation (const struct GNUNET_PeerIdentity *pid, - const char *address) -{ - struct GNUNET_TIME_Absolute now; - struct ValidationState *vs; - struct CheckKnownAddressContext ckac = { .address = address, .vs = NULL }; - - (void) GNUNET_CONTAINER_multipeermap_get_multiple (validation_map, - pid, - &check_known_address, - &ckac); - if (NULL != (vs = ckac.vs)) - { - /* if 'vs' is not currently valid, we need to speed up retrying the - * validation */ - if (vs->validated_until.abs_value_us < vs->next_challenge.abs_value_us) - { - /* reduce backoff as we got a fresh advertisement */ - vs->challenge_backoff = - GNUNET_TIME_relative_min (FAST_VALIDATION_CHALLENGE_FREQ, - GNUNET_TIME_relative_divide ( - vs->challenge_backoff, - 2)); - update_next_challenge_time (vs, - GNUNET_TIME_relative_to_absolute ( - vs->challenge_backoff)); - } - return; - } - now = GNUNET_TIME_absolute_get_monotonic (GST_cfg); - vs = GNUNET_new (struct ValidationState); - vs->pid = *pid; - vs->valid_until = - GNUNET_TIME_relative_to_absolute (ADDRESS_VALIDATION_LIFETIME); - vs->first_challenge_use = now; - vs->validation_rtt = GNUNET_TIME_UNIT_FOREVER_REL; - GNUNET_CRYPTO_random_block (GNUNET_CRYPTO_QUALITY_NONCE, - &vs->challenge, - sizeof(vs->challenge)); - vs->address = GNUNET_strdup (address); - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "Starting address validation `%s' of peer %s using challenge %s\n", - address, - GNUNET_i2s (pid), - GNUNET_sh2s (&vs->challenge.value)); - GNUNET_assert (GNUNET_YES == - GNUNET_CONTAINER_multipeermap_put ( - validation_map, - &vs->pid, - vs, - GNUNET_CONTAINER_MULTIHASHMAPOPTION_MULTIPLE)); - update_next_challenge_time (vs, now); -} - - -static void -hello_for_incoming_cb (void *cls, - const char *uri) -{ - const struct GNUNET_PeerIdentity *peer = cls; - int pfx_len; - const char *eou; - char *address; - - eou = strstr (uri, - "://"); - pfx_len = eou - uri; - eou += 3; - GNUNET_asprintf (&address, - "%.*s-%s", - pfx_len, - uri, - eou); - - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "helo for client %s\n", - address); - - start_address_validation (peer, address); - GNUNET_free (address); -} - - -/** - * Function called by PEERSTORE for each matching record. - * - * @param cls closure, a `struct IncomingRequest` - * @param record peerstore record information - * @param emsg error message, or NULL if no errors - */ -static void -handle_hello_for_incoming (void *cls, - const struct GNUNET_PeerIdentity *peer, - const struct GNUNET_MessageHeader *hello, - const char *emsg) -{ - struct IncomingRequest *ir = cls; - struct GNUNET_HELLO_Builder *builder; - - if (NULL != emsg) - { - GNUNET_log (GNUNET_ERROR_TYPE_WARNING, - "Got failure from PEERSTORE: %s\n", - emsg); - return; - } - if (0 == GNUNET_memcmp (peer, &GST_my_identity)) - return; - builder = GNUNET_HELLO_builder_new (peer); - GNUNET_HELLO_builder_iterate (builder, - (struct GNUNET_PeerIdentity *) peer, - hello_for_incoming_cb, - (struct GNUNET_PeerIdentity *) peer); -} - - -/** - * Communicator gave us a transport address validation challenge. Process the - * request. - * - * @param cls a `struct CommunicatorMessageContext` (must call - * #finish_cmc_handling() when done) - * @param tvc the message that was received - */ -static void -handle_validation_challenge ( - void *cls, - const struct TransportValidationChallengeMessage *tvc) -{ - struct CommunicatorMessageContext *cmc = cls; - struct TransportValidationResponseMessage tvr; - struct VirtualLink *vl; - struct GNUNET_TIME_RelativeNBO validity_duration; - struct IncomingRequest *ir; - struct Neighbour *n; - struct GNUNET_PeerIdentity sender; - - /* DV-routed messages are not allowed for validation challenges */ - if (cmc->total_hops > 0) - { - GNUNET_break_op (0); - finish_cmc_handling (cmc); - return; - } - validity_duration = cmc->im.expected_address_validity; - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "Received address validation challenge %s\n", - GNUNET_sh2s (&tvc->challenge.value)); - /* If we have a virtual link, we use this mechanism to signal the - size of the flow control window, and to allow the sender - to ask for increases. If for us the virtual link is still down, - we will always give a window size of zero. */ - tvr.header.type = - htons (GNUNET_MESSAGE_TYPE_TRANSPORT_ADDRESS_VALIDATION_RESPONSE); - tvr.header.size = htons (sizeof(tvr)); - tvr.reserved = htonl (0); - tvr.challenge = tvc->challenge; - tvr.origin_time = tvc->sender_time; - tvr.validity_duration = validity_duration; - { - /* create signature */ - struct TransportValidationPS tvp = { - .purpose.purpose = htonl (GNUNET_SIGNATURE_PURPOSE_TRANSPORT_CHALLENGE), - .purpose.size = htonl (sizeof(tvp)), - .validity_duration = validity_duration, - .challenge = tvc->challenge - }; - - GNUNET_CRYPTO_eddsa_sign (GST_my_private_key, - &tvp, - &tvr.signature); - } - sender = cmc->im.sender; - vl = lookup_virtual_link (&sender); - if ((NULL != vl) && (GNUNET_YES == vl->confirmed)) - { - // route_control_message_without_fc (&cmc->im.sender, - route_control_message_without_fc (vl, - &tvr.header, - RMO_ANYTHING_GOES | RMO_REDUNDANT); - } - else - { - /* Use route via neighbour */ - n = lookup_neighbour (&sender); - if (NULL != n) - route_via_neighbour (n, &tvr.header, - RMO_ANYTHING_GOES | RMO_REDUNDANT - | RMO_UNCONFIRMED_ALLOWED); - } - - finish_cmc_handling (cmc); - if (NULL != vl) - return; - - /* For us, the link is still down, but we need bi-directional - connections (for flow-control and for this to be useful for - CORE), so we must try to bring the link up! */ - - /* (1) Check existing queues, if any, we may be lucky! */ - n = lookup_neighbour (&sender); - if (NULL != n) - for (struct Queue *q = n->queue_head; NULL != q; q = q->next_neighbour) - start_address_validation (&sender, q->address); - /* (2) Also try to see if we have addresses in PEERSTORE for this peer - we could use */ - for (ir = ir_head; NULL != ir; ir = ir->next) - if (0 == GNUNET_memcmp (&ir->pid, &sender)) - return; - /* we are already trying */ - ir = GNUNET_new (struct IncomingRequest); - ir->pid = sender; - GNUNET_CONTAINER_DLL_insert (ir_head, ir_tail, ir); - - ir->nc = GNUNET_PEERSTORE_hello_changed_notify (peerstore, - GNUNET_NO, - &handle_hello_for_incoming, - NULL); - ir_total++; - /* Bound attempts we do in parallel here, might otherwise get excessive */ - while (ir_total > MAX_INCOMING_REQUEST) - free_incoming_request (ir_head); -} - - -/** - * Closure for #check_known_challenge. - */ -struct CheckKnownChallengeContext -{ - /** - * Set to the challenge we are looking for. - */ - const struct GNUNET_CRYPTO_ChallengeNonceP *challenge; - - /** - * Set to a matching validation state, if one was found. - */ - struct ValidationState *vs; -}; - - -/** - * Test if the validation state in @a value matches the - * challenge from @a cls. - * - * @param cls a `struct CheckKnownChallengeContext` - * @param pid unused (must match though) - * @param value a `struct ValidationState` - * @return #GNUNET_OK if not matching, #GNUNET_NO if match found - */ -static int -check_known_challenge (void *cls, - const struct GNUNET_PeerIdentity *pid, - void *value) -{ - struct CheckKnownChallengeContext *ckac = cls; - struct ValidationState *vs = value; - - (void) pid; - if (0 != GNUNET_memcmp (&vs->challenge, ckac->challenge)) - return GNUNET_OK; - ckac->vs = vs; - return GNUNET_NO; -} - - -/** - * Function called when peerstore is done storing a - * validated address. - * - * @param cls a `struct ValidationState` - * @param success #GNUNET_YES on success - */ -static void -peerstore_store_validation_cb (void *cls, int success) -{ - struct ValidationState *vs = cls; - - vs->sc = NULL; - if (GNUNET_YES == success) - return; - GNUNET_STATISTICS_update (GST_stats, - "# Peerstore failed to store foreign address", - 1, - GNUNET_NO); -} - - -/** - * Find the queue matching @a pid and @a address. - * - * @param pid peer the queue must go to - * @param address address the queue must use - * @return NULL if no such queue exists - */ -static struct Queue * -find_queue (const struct GNUNET_PeerIdentity *pid, const char *address) -{ - struct Neighbour *n; - - n = lookup_neighbour (pid); - if (NULL == n) - return NULL; - for (struct Queue *pos = n->queue_head; NULL != pos; - pos = pos->next_neighbour) - { - if (0 == strcmp (pos->address, address)) - return pos; - } - return NULL; -} - - -/** - * Communicator gave us a transport address validation response. Process the - * request. - * - * @param cls a `struct CommunicatorMessageContext` (must call - * #finish_cmc_handling() when done) - * @param tvr the message that was received - */ -static void -handle_validation_response ( - void *cls, - const struct TransportValidationResponseMessage *tvr) -{ - struct CommunicatorMessageContext *cmc = cls; - struct ValidationState *vs; - struct CheckKnownChallengeContext ckac = { .challenge = &tvr->challenge, - .vs = NULL}; - struct GNUNET_TIME_Absolute origin_time; - struct Queue *q; - struct Neighbour *n; - struct VirtualLink *vl; - const struct GNUNET_TIME_Absolute now = GNUNET_TIME_absolute_get_monotonic (GST_cfg); - - /* check this is one of our challenges */ - (void) GNUNET_CONTAINER_multipeermap_get_multiple (validation_map, - &cmc->im.sender, - &check_known_challenge, - &ckac); - if (NULL == (vs = ckac.vs)) - { - /* This can happen simply if we 'forgot' the challenge by now, - i.e. because we received the validation response twice */ - GNUNET_STATISTICS_update (GST_stats, - "# Validations dropped, challenge unknown", - 1, - GNUNET_NO); - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "Validation response %s dropped, challenge unknown\n", - GNUNET_sh2s (&tvr->challenge.value)); - finish_cmc_handling (cmc); - return; - } - - /* sanity check on origin time */ - origin_time = GNUNET_TIME_absolute_ntoh (tvr->origin_time); - if ((origin_time.abs_value_us < vs->first_challenge_use.abs_value_us) || - (origin_time.abs_value_us > vs->last_challenge_use.abs_value_us)) - { - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "Diff first use %" PRIu64 " and last use %" PRIu64 "\n", - vs->first_challenge_use.abs_value_us - origin_time.abs_value_us, - origin_time.abs_value_us - vs->last_challenge_use.abs_value_us); - GNUNET_break_op (0); - finish_cmc_handling (cmc); - return; - } - - { - /* check signature */ - struct TransportValidationPS tvp = { - .purpose.purpose = htonl (GNUNET_SIGNATURE_PURPOSE_TRANSPORT_CHALLENGE), - .purpose.size = htonl (sizeof(tvp)), - .validity_duration = tvr->validity_duration, - .challenge = tvr->challenge - }; - - if ( - GNUNET_OK != - GNUNET_CRYPTO_eddsa_verify (GNUNET_SIGNATURE_PURPOSE_TRANSPORT_CHALLENGE, - &tvp, - &tvr->signature, - &cmc->im.sender.public_key)) - { - GNUNET_break_op (0); - finish_cmc_handling (cmc); - return; - } - } - - /* validity is capped by our willingness to keep track of the - validation entry and the maximum the other peer allows */ - vs->valid_until = GNUNET_TIME_relative_to_absolute ( - GNUNET_TIME_relative_min (GNUNET_TIME_relative_ntoh ( - tvr->validity_duration), - MAX_ADDRESS_VALID_UNTIL)); - vs->validated_until = - GNUNET_TIME_absolute_min (vs->valid_until, - GNUNET_TIME_relative_to_absolute ( - ADDRESS_VALIDATION_LIFETIME)); - vs->validation_rtt = GNUNET_TIME_absolute_get_duration (origin_time); - vs->challenge_backoff = GNUNET_TIME_UNIT_ZERO; - GNUNET_CRYPTO_random_block (GNUNET_CRYPTO_QUALITY_NONCE, - &vs->challenge, - sizeof(vs->challenge)); - vs->first_challenge_use = GNUNET_TIME_absolute_subtract ( - vs->validated_until, - GNUNET_TIME_relative_multiply (vs->validation_rtt, - VALIDATION_RTT_BUFFER_FACTOR)); - if (GNUNET_TIME_absolute_cmp (vs->first_challenge_use, <, now)) - { - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "First challenge use is now %" PRIu64 " %s \n", - vs->first_challenge_use.abs_value_us, - GNUNET_sh2s (&vs->challenge.value)); - vs->first_challenge_use = now; - } - else - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "First challenge use is later %" PRIu64 " %s \n", - vs->first_challenge_use.abs_value_us, - GNUNET_sh2s (&vs->challenge.value)); - vs->last_challenge_use = - GNUNET_TIME_UNIT_ZERO_ABS; /* challenge was not yet used */ - update_next_challenge_time (vs, vs->first_challenge_use); - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "Validation response %s from %s accepted, address valid until %s\n", - GNUNET_sh2s (&tvr->challenge.value), - GNUNET_i2s (&cmc->im.sender), - GNUNET_STRINGS_absolute_time_to_string (vs->valid_until)); - vs->sc = GNUNET_PEERSTORE_store (peerstore, - "transport", - &cmc->im.sender, - GNUNET_PEERSTORE_TRANSPORT_URLADDRESS_KEY, - vs->address, - strlen (vs->address) + 1, - vs->valid_until, - GNUNET_PEERSTORE_STOREOPTION_MULTIPLE, - &peerstore_store_validation_cb, - vs); - finish_cmc_handling (cmc); - - /* Finally, we now possibly have a confirmed (!) working queue, - update queue status (if queue still is around) */ - q = find_queue (&vs->pid, vs->address); - if (NULL == q) - { - GNUNET_STATISTICS_update (GST_stats, - "# Queues lost at time of successful validation", - 1, - GNUNET_NO); - return; - } - q->validated_until = vs->validated_until; - q->pd.aged_rtt = vs->validation_rtt; - n = q->neighbour; - vl = lookup_virtual_link (&vs->pid); - if (NULL == vl) - { - vl = GNUNET_new (struct VirtualLink); - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "Creating new virtual link %p to %s using direct neighbour!\n", - vl, - GNUNET_i2s (&vs->pid)); - vl->confirmed = GNUNET_YES; - vl->message_uuid_ctr = - GNUNET_CRYPTO_random_u64 (GNUNET_CRYPTO_QUALITY_WEAK, UINT64_MAX); - vl->target = n->pid; - vl->core_recv_window = RECV_WINDOW_SIZE; - vl->available_fc_window_size = DEFAULT_WINDOW_SIZE; - vl->incoming_fc_window_size = DEFAULT_WINDOW_SIZE; - GNUNET_break (GNUNET_YES == - GNUNET_CONTAINER_multipeermap_put ( - links, - &vl->target, - vl, - GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_ONLY)); - vl->n = n; - n->vl = vl; - q->idle = GNUNET_YES; - vl->visibility_task = - GNUNET_SCHEDULER_add_at (q->validated_until, &check_link_down, vl); - consider_sending_fc (vl); - /* We lacked a confirmed connection to the target - before, so tell CORE about it (finally!) */ - cores_send_connect_info (&n->pid); - send_msg_from_cache (vl); - } - else - { - /* Link was already up, remember n is also now available and we are done */ - if (NULL == vl->n) - { - vl->n = n; - n->vl = vl; - if (GNUNET_YES == vl->confirmed) - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "Virtual link to %s could now also use direct neighbour!\n", - GNUNET_i2s (&vs->pid)); - } - else - { - GNUNET_assert (n == vl->n); - } - if (GNUNET_NO == vl->confirmed) - { - vl->confirmed = GNUNET_YES; - q->idle = GNUNET_YES; - vl->visibility_task = - GNUNET_SCHEDULER_add_at (q->validated_until, &check_link_down, vl); - consider_sending_fc (vl); - /* We lacked a confirmed connection to the target - before, so tell CORE about it (finally!) */ - cores_send_connect_info (&n->pid); - send_msg_from_cache (vl); - } - } -} - - -/** - * Incoming meessage. Process the request. - * - * @param im the send message that was received - */ -static void -handle_incoming_msg (void *cls, - const struct GNUNET_TRANSPORT_IncomingMessage *im) -{ - struct TransportClient *tc = cls; - struct CommunicatorMessageContext *cmc = - GNUNET_new (struct CommunicatorMessageContext); - - cmc->tc = tc; - cmc->im = *im; - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "Received message with size %u and flow control id %" PRIu64 - " via communicator from peer %s\n", - ntohs (im->header.size), - im->fc_id, - GNUNET_i2s (&im->sender)); - cmc->im.neighbour_sender = cmc->im.sender; - cmc->mh = (const struct GNUNET_MessageHeader *) &im[1]; - demultiplex_with_cmc (cmc); -} - - -/** - * Communicator gave us a transport address validation response. Process the - * request. - * - * @param cls a `struct CommunicatorMessageContext` (must call - * #finish_cmc_handling() when done) - * @param fc the message that was received - */ -static void -handle_flow_control (void *cls, const struct TransportFlowControlMessage *fc) -{ - struct CommunicatorMessageContext *cmc = cls; - struct VirtualLink *vl; - uint32_t seq; - struct GNUNET_TIME_Absolute st; - uint64_t os; - uint64_t wnd; - uint32_t random; - - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "Received FC from %s\n", GNUNET_i2s (&cmc->im.sender)); - vl = lookup_virtual_link (&cmc->im.sender); - if (NULL == vl) - { - vl = GNUNET_new (struct VirtualLink); - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "No virtual link for %p FC creating new unconfirmed virtual link to %s!\n", - vl, - GNUNET_i2s (&cmc->im.sender)); - vl->confirmed = GNUNET_NO; - vl->message_uuid_ctr = - GNUNET_CRYPTO_random_u64 (GNUNET_CRYPTO_QUALITY_WEAK, UINT64_MAX); - vl->target = cmc->im.sender; - vl->core_recv_window = RECV_WINDOW_SIZE; - vl->available_fc_window_size = DEFAULT_WINDOW_SIZE; - vl->incoming_fc_window_size = DEFAULT_WINDOW_SIZE; - GNUNET_break (GNUNET_YES == - GNUNET_CONTAINER_multipeermap_put ( - links, - &vl->target, - vl, - GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_ONLY)); - } - st = GNUNET_TIME_absolute_ntoh (fc->sender_time); - if (st.abs_value_us < vl->last_fc_timestamp.abs_value_us) - { - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "FC dropped: Message out of order\n"); - /* out of order, drop */ - GNUNET_STATISTICS_update (GST_stats, - "# FC dropped: message out of order", - 1, - GNUNET_NO); - finish_cmc_handling (cmc); - return; - } - seq = ntohl (fc->seq); - if (seq < vl->last_fc_seq) - { - /* Wrap-around/reset of other peer; start all counters from zero */ - vl->outbound_fc_window_size_used = 0; - } - vl->last_fc_seq = seq; - vl->last_fc_timestamp = st; - vl->outbound_fc_window_size = GNUNET_ntohll (fc->inbound_window_size); - os = GNUNET_ntohll (fc->outbound_sent); - vl->incoming_fc_window_size_loss = - (int64_t) (os - vl->incoming_fc_window_size_used); - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "Received FC from %s, seq %u, new window %llu (loss at %lld)\n", - GNUNET_i2s (&vl->target), - (unsigned int) seq, - (unsigned long long) vl->outbound_fc_window_size, - (long long) vl->incoming_fc_window_size_loss); - wnd = GNUNET_ntohll (fc->outbound_window_size); - random = GNUNET_CRYPTO_random_u32 (GNUNET_CRYPTO_QUALITY_WEAK, - UINT32_MAX); - if ((GNUNET_YES == vl->confirmed) && ((wnd < vl->incoming_fc_window_size - + vl->incoming_fc_window_size_used - + vl->incoming_fc_window_size_loss) || - (vl->last_outbound_window_size_received - != wnd) || - (0 == random - % FC_NO_CHANGE_REPLY_PROBABILITY))) - { - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "Consider re-sending our FC message, as clearly the other peer's idea of the window is not up-to-date (%llu vs %llu) or %llu last received differs, or random reply %u\n", - (unsigned long long) wnd, - (unsigned long long) vl->incoming_fc_window_size, - (unsigned long long) vl->last_outbound_window_size_received, - random % FC_NO_CHANGE_REPLY_PROBABILITY); - consider_sending_fc (vl); - } - if ((wnd == vl->incoming_fc_window_size) && - (vl->last_outbound_window_size_received == wnd) && - (NULL != vl->fc_retransmit_task)) - { - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "Stopping FC retransmission to %s: peer is current at window %llu\n", - GNUNET_i2s (&vl->target), - (unsigned long long) wnd); - GNUNET_SCHEDULER_cancel (vl->fc_retransmit_task); - vl->fc_retransmit_task = NULL; - vl->fc_retransmit_count = 0; - } - vl->last_outbound_window_size_received = wnd; - /* FC window likely increased, check transmission possibilities! */ - check_vl_transmission (vl); - finish_cmc_handling (cmc); -} - - -/** - * Given an inbound message @a msg from a communicator @a cmc, - * demultiplex it based on the type calling the right handler. - * - * @param cmc context for demultiplexing - * @param msg message to demultiplex - */ -static void -demultiplex_with_cmc (struct CommunicatorMessageContext *cmc) -{ - struct GNUNET_MQ_MessageHandler handlers[] = - { GNUNET_MQ_hd_var_size (fragment_box, - GNUNET_MESSAGE_TYPE_TRANSPORT_FRAGMENT, - struct TransportFragmentBoxMessage, - cmc), - GNUNET_MQ_hd_var_size (reliability_box, - GNUNET_MESSAGE_TYPE_TRANSPORT_RELIABILITY_BOX, - struct TransportReliabilityBoxMessage, - cmc), - GNUNET_MQ_hd_var_size (reliability_ack, - GNUNET_MESSAGE_TYPE_TRANSPORT_RELIABILITY_ACK, - struct TransportReliabilityAckMessage, - cmc), - GNUNET_MQ_hd_var_size (backchannel_encapsulation, - GNUNET_MESSAGE_TYPE_TRANSPORT_BACKCHANNEL_ENCAPSULATION, - struct TransportBackchannelEncapsulationMessage, - cmc), - GNUNET_MQ_hd_var_size (dv_learn, - GNUNET_MESSAGE_TYPE_TRANSPORT_DV_LEARN, - struct TransportDVLearnMessage, - cmc), - GNUNET_MQ_hd_var_size (dv_box, - GNUNET_MESSAGE_TYPE_TRANSPORT_DV_BOX, - struct TransportDVBoxMessage, - cmc), - GNUNET_MQ_hd_fixed_size ( - validation_challenge, - GNUNET_MESSAGE_TYPE_TRANSPORT_ADDRESS_VALIDATION_CHALLENGE, - struct TransportValidationChallengeMessage, - cmc), - GNUNET_MQ_hd_fixed_size (flow_control, - GNUNET_MESSAGE_TYPE_TRANSPORT_FLOW_CONTROL, - struct TransportFlowControlMessage, - cmc), - GNUNET_MQ_hd_fixed_size ( - validation_response, - GNUNET_MESSAGE_TYPE_TRANSPORT_ADDRESS_VALIDATION_RESPONSE, - struct TransportValidationResponseMessage, - cmc), - GNUNET_MQ_handler_end () }; - int ret; - const struct GNUNET_MessageHeader *msg = cmc->mh; - - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "Handling message of type %u with %u bytes\n", - (unsigned int) ntohs (msg->type), - (unsigned int) ntohs (msg->size)); - ret = GNUNET_MQ_handle_message (handlers, msg); - if (GNUNET_SYSERR == ret) - { - GNUNET_break (0); - GNUNET_SERVICE_client_drop (cmc->tc->client); - GNUNET_free (cmc); - return; - } - if (GNUNET_NO == ret) - { - /* unencapsulated 'raw' message */ - handle_raw_message (cmc, msg); - } -} - - -/** - * New queue became available. Check message. - * - * @param cls the client - * @param aqm the send message that was sent - */ -static int -check_add_queue_message (void *cls, - const struct GNUNET_TRANSPORT_AddQueueMessage *aqm) -{ - struct TransportClient *tc = cls; - - if (CT_COMMUNICATOR != tc->type) - { - GNUNET_break (0); - return GNUNET_SYSERR; - } - GNUNET_MQ_check_zero_termination (aqm); - return GNUNET_OK; -} - - -/** - * If necessary, generates the UUID for a @a pm - * - * @param pm pending message to generate UUID for. - */ -static void -set_pending_message_uuid (struct PendingMessage *pm) -{ - if (pm->msg_uuid_set) - return; - pm->msg_uuid.uuid = pm->vl->message_uuid_ctr++; - pm->msg_uuid_set = GNUNET_YES; -} - - -/** - * Setup data structure waiting for acknowledgements. - * - * @param queue queue the @a pm will be sent over - * @param dvh path the message will take, may be NULL - * @param pm the pending message for transmission - * @return corresponding fresh pending acknowledgement - */ -static struct PendingAcknowledgement * -prepare_pending_acknowledgement (struct Queue *queue, - struct DistanceVectorHop *dvh, - struct PendingMessage *pm) -{ - struct PendingAcknowledgement *pa; - - pa = GNUNET_new (struct PendingAcknowledgement); - pa->queue = queue; - pa->dvh = dvh; - pa->pm = pm; - do - { - GNUNET_CRYPTO_random_block (GNUNET_CRYPTO_QUALITY_NONCE, - &pa->ack_uuid, - sizeof(pa->ack_uuid)); - } - while (GNUNET_YES != GNUNET_CONTAINER_multiuuidmap_put ( - pending_acks, - &pa->ack_uuid.value, - pa, - GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_ONLY)); - GNUNET_CONTAINER_MDLL_insert (queue, queue->pa_head, queue->pa_tail, pa); - GNUNET_CONTAINER_MDLL_insert (pm, pm->pa_head, pm->pa_tail, pa); - if (NULL != dvh) - GNUNET_CONTAINER_MDLL_insert (dvh, dvh->pa_head, dvh->pa_tail, pa); - pa->transmission_time = GNUNET_TIME_absolute_get (); - pa->message_size = pm->bytes_msg; - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "Waiting for ACKnowledgment `%s' for <%llu>\n", - GNUNET_uuid2s (&pa->ack_uuid.value), - pm->logging_uuid); - return pa; -} - - -/** - * Fragment the given @a pm to the given @a mtu. Adds - * additional fragments to the neighbour as well. If the - * @a mtu is too small, generates and error for the @a pm - * and returns NULL. - * - * @param queue which queue to fragment for - * @param dvh path the message will take, or NULL - * @param pm pending message to fragment for transmission - * @return new message to transmit - */ -static struct PendingMessage * -fragment_message (struct Queue *queue, - struct DistanceVectorHop *dvh, - struct PendingMessage *pm) -{ - struct PendingAcknowledgement *pa; - struct PendingMessage *ff; - uint16_t mtu; - uint16_t msize; - - mtu = (UINT16_MAX == queue->mtu) - ? UINT16_MAX - sizeof(struct GNUNET_TRANSPORT_SendMessageTo) - : queue->mtu; - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "Fragmenting message <%llu> with size %u to %s for MTU %u\n", - pm->logging_uuid, - pm->bytes_msg, - GNUNET_i2s (&pm->vl->target), - (unsigned int) mtu); - set_pending_message_uuid (pm); - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "Fragmenting message %llu <%llu> with size %u to %s for MTU %u\n", - (unsigned long long) pm->msg_uuid.uuid, - pm->logging_uuid, - pm->bytes_msg, - GNUNET_i2s (&pm->vl->target), - (unsigned int) mtu); - - /* This invariant is established in #handle_add_queue_message() */ - GNUNET_assert (mtu > sizeof(struct TransportFragmentBoxMessage)); - - /* select fragment for transmission, descending the tree if it has - been expanded until we are at a leaf or at a fragment that is small - enough - */ - ff = pm; - msize = ff->bytes_msg; - - while (((ff->bytes_msg > mtu) || (pm == ff)) && - (ff->frag_off == msize) && (NULL != ff->head_frag)) - { - ff = ff->head_frag; /* descent into fragmented fragments */ - msize = ff->bytes_msg - sizeof(struct TransportFragmentBoxMessage); - } - - if (((ff->bytes_msg > mtu) || (pm == ff)) && (ff->frag_off < msize)) - { - /* Did not yet calculate all fragments, calculate next fragment */ - struct PendingMessage *frag; - struct TransportFragmentBoxMessage tfb; - const char *orig; - char *msg; - uint16_t fragmax; - uint16_t fragsize; - uint16_t msize; - uint16_t xoff = 0; - - orig = (const char *) &ff[1]; - msize = ff->bytes_msg; - if (pm != ff) - { - const struct TransportFragmentBoxMessage *tfbo; - - tfbo = (const struct TransportFragmentBoxMessage *) orig; - orig += sizeof(struct TransportFragmentBoxMessage); - msize -= sizeof(struct TransportFragmentBoxMessage); - xoff = ntohs (tfbo->frag_off); - } - fragmax = mtu - sizeof(struct TransportFragmentBoxMessage); - fragsize = GNUNET_MIN (msize - ff->frag_off, fragmax); - frag = - GNUNET_malloc (sizeof(struct PendingMessage) - + sizeof(struct TransportFragmentBoxMessage) + fragsize); - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "3 created pm %p from pm %p storing vl %p from pm %p\n", - frag, - ff, - pm->vl, - pm); - frag->logging_uuid = logging_uuid_gen++; - frag->vl = pm->vl; - frag->frag_parent = ff; - frag->timeout = pm->timeout; - frag->bytes_msg = sizeof(struct TransportFragmentBoxMessage) + fragsize; - frag->pmt = PMT_FRAGMENT_BOX; - msg = (char *) &frag[1]; - tfb.header.type = htons (GNUNET_MESSAGE_TYPE_TRANSPORT_FRAGMENT); - tfb.header.size = - htons (sizeof(struct TransportFragmentBoxMessage) + fragsize); - pa = prepare_pending_acknowledgement (queue, dvh, frag); - tfb.ack_uuid = pa->ack_uuid; - tfb.msg_uuid = pm->msg_uuid; - tfb.frag_off = htons (ff->frag_off + xoff); - tfb.msg_size = htons (pm->bytes_msg); - memcpy (msg, &tfb, sizeof(tfb)); - memcpy (&msg[sizeof(tfb)], &orig[ff->frag_off], fragsize); - GNUNET_CONTAINER_MDLL_insert (frag, ff->head_frag, - ff->tail_frag, frag); - ff->frag_off += fragsize; - ff = frag; - } - - /* Move head to the tail and return it */ - GNUNET_CONTAINER_MDLL_remove (frag, - ff->frag_parent->head_frag, - ff->frag_parent->tail_frag, - ff); - GNUNET_CONTAINER_MDLL_insert_tail (frag, - ff->frag_parent->head_frag, - ff->frag_parent->tail_frag, - ff); - - return ff; -} - - -/** - * Reliability-box the given @a pm. On error (can there be any), NULL - * may be returned, otherwise the "replacement" for @a pm (which - * should then be added to the respective neighbour's queue instead of - * @a pm). If the @a pm is already fragmented or reliability boxed, - * or itself an ACK, this function simply returns @a pm. - * - * @param queue which queue to prepare transmission for - * @param dvh path the message will take, or NULL - * @param pm pending message to box for transmission over unreliabile queue - * @return new message to transmit - */ -static struct PendingMessage * -reliability_box_message (struct Queue *queue, - struct DistanceVectorHop *dvh, - struct PendingMessage *pm) -{ - struct TransportReliabilityBoxMessage rbox; - struct PendingAcknowledgement *pa; - struct PendingMessage *bpm; - char *msg; - - if ((PMT_CORE != pm->pmt) && (PMT_DV_BOX != pm->pmt)) - return pm; /* already fragmented or reliability boxed, or control message: - do nothing */ - if (NULL != pm->bpm) - return pm->bpm; /* already computed earlier: do nothing */ - // TODO I guess we do not need this assertion. We might have a DLL with - // fragments, because the MTU changed, and we do not need to fragment anymore. - // But we should keep the fragments until message was completed, because - // the MTU might change again. - // GNUNET_assert (NULL == pm->head_frag); - if (pm->bytes_msg + sizeof(rbox) > UINT16_MAX) - { - /* failed hard */ - GNUNET_break (0); - client_send_response (pm); - return NULL; - } - - pa = prepare_pending_acknowledgement (queue, dvh, pm); - - bpm = GNUNET_malloc (sizeof(struct PendingMessage) + sizeof(rbox) - + pm->bytes_msg); - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "4 created pm %p storing vl %p from pm %p\n", - bpm, - pm->vl, - pm); - bpm->logging_uuid = logging_uuid_gen++; - bpm->vl = pm->vl; - bpm->frag_parent = pm; - // Why was this needed? - // GNUNET_CONTAINER_MDLL_insert (frag, pm->head_frag, pm->tail_frag, bpm); - bpm->timeout = pm->timeout; - bpm->pmt = PMT_RELIABILITY_BOX; - bpm->bytes_msg = pm->bytes_msg + sizeof(rbox); - set_pending_message_uuid (bpm); - rbox.header.type = htons (GNUNET_MESSAGE_TYPE_TRANSPORT_RELIABILITY_BOX); - rbox.header.size = htons (sizeof(rbox) + pm->bytes_msg); - rbox.ack_countdown = htonl (0); // FIXME: implement ACK countdown support - - rbox.ack_uuid = pa->ack_uuid; - msg = (char *) &bpm[1]; - memcpy (msg, &rbox, sizeof(rbox)); - memcpy (&msg[sizeof(rbox)], &pm[1], pm->bytes_msg); - pm->bpm = bpm; - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "Preparing reliability box for message <%llu> of size %d (%d) to %s on queue %s\n", - pm->logging_uuid, - pm->bytes_msg, - ntohs (((const struct GNUNET_MessageHeader *) &pm[1])->size), - GNUNET_i2s (&pm->vl->target), - queue->address); - return bpm; -} - - -static void -reorder_root_pm (struct PendingMessage *pm, - struct GNUNET_TIME_Absolute next_attempt) -{ - struct VirtualLink *vl = pm->vl; - struct PendingMessage *pos; - - /* re-insert sort in neighbour list */ - GNUNET_CONTAINER_MDLL_remove (vl, - vl->pending_msg_head, - vl->pending_msg_tail, - pm); - pos = vl->pending_msg_tail; - while ((NULL != pos) && - (next_attempt.abs_value_us > pos->next_attempt.abs_value_us)) - pos = pos->prev_vl; - GNUNET_CONTAINER_MDLL_insert_after (vl, - vl->pending_msg_head, - vl->pending_msg_tail, - pos, - pm); -} - - -static unsigned int -check_next_attempt_tree (struct PendingMessage *pm, - struct GNUNET_TIME_Absolute next_attempt) -{ - struct PendingMessage *pos; - - pos = pm->head_frag; - while (NULL != pos) - { - if (pos->next_attempt.abs_value_us != next_attempt.abs_value_us || - GNUNET_YES == check_next_attempt_tree (pos, next_attempt)) - return GNUNET_YES; - pos = pos->next_frag; - } - - return GNUNET_NO; -} - - -/** - * Change the value of the `next_attempt` field of @a pm - * to @a next_attempt and re-order @a pm in the transmission - * list as required by the new timestamp. - * - * @param pm a pending message to update - * @param next_attempt timestamp to use - */ -static void -update_pm_next_attempt (struct PendingMessage *pm, - struct GNUNET_TIME_Absolute next_attempt) -{ - if (NULL == pm->frag_parent) - { - pm->next_attempt = next_attempt; - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "Next attempt for message <%" PRIu64 "> set to %" PRIu64 "\n", - pm->logging_uuid, - next_attempt.abs_value_us); - reorder_root_pm (pm, next_attempt); - } - else if ((PMT_RELIABILITY_BOX == pm->pmt) || (PMT_DV_BOX == pm->pmt))// || (PMT_FRAGMENT_BOX == pm->pmt)) - { - struct PendingMessage *root = pm->frag_parent; - - while (NULL != root->frag_parent) - root = root->frag_parent; - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "Next attempt for root message <%" PRIu64 "> set to %s\n", - root->logging_uuid, - GNUNET_STRINGS_absolute_time_to_string (next_attempt)); - root->next_attempt = next_attempt; - reorder_root_pm (root, next_attempt); - } - else - { - struct PendingMessage *root = pm->frag_parent; - - while (NULL != root->frag_parent) - root = root->frag_parent; - - if (GNUNET_NO == root->frags_in_flight) - { - root->next_attempt = next_attempt; - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "Next attempt for fragmented message <%" PRIu64 "> (<%" PRIu64 - ">)set to %" PRIu64 "\n", - pm->logging_uuid, - root->logging_uuid, - next_attempt.abs_value_us); - } - - pm->next_attempt = root->next_attempt; - - if (root->bytes_msg == root->frag_off) - root->frags_in_flight = check_next_attempt_tree (root, - root->next_attempt); - else - root->frags_in_flight = GNUNET_YES; - - if (GNUNET_NO == root->frags_in_flight) - { - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "We have no fragments in flight for message %" PRIu64 - ", reorder root! Next attempt is %" PRIu64 "\n", - root->logging_uuid, - root->next_attempt.abs_value_us); - reorder_root_pm (root, root->next_attempt); - root->frag_count = 0; - root->next_attempt = GNUNET_TIME_UNIT_ZERO_ABS; - } - else - { - double factor = (root->frag_count - 1) / root->frag_count; - struct GNUNET_TIME_Relative s1; - struct GNUNET_TIME_Relative s2; - struct GNUNET_TIME_Relative plus_mean = - GNUNET_TIME_absolute_get_duration (root->next_attempt); - struct GNUNET_TIME_Relative plus = GNUNET_TIME_absolute_get_duration ( - next_attempt); - - s1 = GNUNET_TIME_relative_multiply (plus_mean, - factor); - s2 = GNUNET_TIME_relative_divide (plus, - root->frag_count); - plus_mean = GNUNET_TIME_relative_add (s1, s2); - root->next_attempt = GNUNET_TIME_relative_to_absolute (plus_mean); - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "We have fragments in flight for message %" PRIu64 - ", do not reorder root! Actual next attempt %" PRIu64 "\n", - root->logging_uuid, - root->next_attempt.abs_value_us); - } - } -} - - -/** - * Context for #select_best_pending_from_link(). - */ -struct PendingMessageScoreContext -{ - /** - * Set to the best message that was found, NULL for none. - */ - struct PendingMessage *best; - - /** - * DVH that @e best should take, or NULL for direct transmission. - */ - struct DistanceVectorHop *dvh; - - /** - * What is the estimated total overhead for this message? - */ - size_t real_overhead; - - /** - * Number of pending messages we seriously considered this time. - */ - unsigned int consideration_counter; - - /** - * Did we have to fragment? - */ - int frag; - - /** - * Did we have to reliability box? - */ - int relb; - - /** - * There are pending messages, but it was to early to send one of them. - */ - int to_early; - - /** - * There is a pending messages we are sending fragments at the moment. - */ - unsigned int frags_in_flight; - - /** - * When will we try to transmit the message again for which it was to early to retry. - */ - struct GNUNET_TIME_Relative to_early_retry_delay; -}; - - -/** - * Select the best pending message from @a vl for transmission - * via @a queue. - * - * @param[in,out] sc best message so far (NULL for none), plus scoring data - * @param queue the queue that will be used for transmission - * @param vl the virtual link providing the messages - * @param dvh path we are currently considering, or NULL for none - * @param overhead number of bytes of overhead to be expected - * from DV encapsulation (0 for without DV) - */ -static void -select_best_pending_from_link (struct PendingMessageScoreContext *sc, - struct Queue *queue, - struct VirtualLink *vl, - struct DistanceVectorHop *dvh, - size_t overhead) -{ - struct GNUNET_TIME_Absolute now; - - now = GNUNET_TIME_absolute_get (); - sc->to_early = GNUNET_NO; - sc->frags_in_flight = GNUNET_NO; - for (struct PendingMessage *pos = vl->pending_msg_head; NULL != pos; - pos = pos->next_vl) - { - size_t real_overhead = overhead; - int frag; - int relb; - - if ((NULL != dvh) && (PMT_DV_BOX == pos->pmt)) - { - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "DV messages must not be DV-routed to next hop!\n"); - continue; /* DV messages must not be DV-routed to next hop! */ - } - if (pos->next_attempt.abs_value_us > now.abs_value_us) - { - if (GNUNET_YES == pos->frags_in_flight) - { - sc->frags_in_flight = GNUNET_YES; - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "Fragments in flight for message %llu\n", - pos->logging_uuid); - } - else - { - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "Maybe too early, because message are sorted by next_attempt, if there are no fragments in flight.Checked message %llu\n", - pos->logging_uuid); - sc->to_early = GNUNET_YES; - sc->to_early_retry_delay = GNUNET_TIME_absolute_get_remaining ( - pos->next_attempt); - continue; - } - // break; /* too early for all messages, they are sorted by next_attempt */ - } - if (NULL != pos->qe) - { - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "not eligible\n"); - continue; /* not eligible */ - } - sc->consideration_counter++; - /* determine if we have to fragment, if so add fragmentation - overhead! */ - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "check %llu for sc->best\n", - pos->logging_uuid); - frag = GNUNET_NO; - if (((0 != queue->mtu) && - (pos->bytes_msg + real_overhead > queue->mtu)) || - (pos->bytes_msg > UINT16_MAX - sizeof(struct - GNUNET_TRANSPORT_SendMessageTo)) - || - (NULL != pos->head_frag /* fragments already exist, should - respect that even if MTU is UINT16_MAX for - this queue */)) - { - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "fragment msg with size %u, realoverhead is %lu\n", - pos->bytes_msg, - real_overhead); - frag = GNUNET_YES; - if (GNUNET_TRANSPORT_CC_RELIABLE == queue->tc->details.communicator.cc) - { - /* FIXME-FRAG-REL-UUID: we could use an optimized, shorter fragmentation - header without the ACK UUID when using a *reliable* channel! */ - } - real_overhead = overhead + sizeof(struct TransportFragmentBoxMessage); - } - /* determine if we have to reliability-box, if so add reliability box - overhead */ - relb = GNUNET_NO; - if ((GNUNET_NO == frag) && - (0 == (pos->prefs & GNUNET_MQ_PREF_UNRELIABLE)) && - (GNUNET_TRANSPORT_CC_RELIABLE != queue->tc->details.communicator.cc)) - { - real_overhead += sizeof(struct TransportReliabilityBoxMessage); - - if ((0 != queue->mtu) && (pos->bytes_msg + real_overhead > queue->mtu)) - { - frag = GNUNET_YES; - real_overhead = overhead + sizeof(struct TransportFragmentBoxMessage); - } - else - { - relb = GNUNET_YES; - } - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "Create reliability box of msg with size %u, realoverhead is %lu %u %u %u\n", - pos->bytes_msg, - real_overhead, - queue->mtu, - frag, - relb); - } - - /* Finally, compare to existing 'best' in sc to see if this 'pos' pending - message would beat it! */ - if (GNUNET_NO == sc->frags_in_flight && NULL != sc->best) - { - /* CHECK if pos fits queue BETTER (=smaller) than pm, if not: continue; - OPTIMIZE-ME: This is a heuristic, which so far has NOT been - experimentally validated. There may be some huge potential for - improvement here. Also, we right now only compare how well the - given message fits _this_ queue, and do not consider how well other - queues might suit the message. Taking other queues into consideration - may further improve the result, but could also be expensive - in terms of CPU time. */ - long long sc_score = sc->frag * 40 + sc->relb * 20 + sc->real_overhead; - long long pm_score = frag * 40 + relb * 20 + real_overhead; - long long time_delta = - (sc->best->next_attempt.abs_value_us - pos->next_attempt.abs_value_us) - / 1000LL; - - /* "time_delta" considers which message has been 'ready' for transmission - for longer, if a message has a preference for low latency, increase - the weight of the time_delta by 10x if it is favorable for that message */ - if ((0 != (pos->prefs & GNUNET_MQ_PREF_LOW_LATENCY)) && - (0 != (sc->best->prefs & GNUNET_MQ_PREF_LOW_LATENCY))) - time_delta *= 10; /* increase weight (always, both are low latency) */ - else if ((0 != (pos->prefs & GNUNET_MQ_PREF_LOW_LATENCY)) && - (time_delta > 0)) - time_delta *= 10; /* increase weight, favors 'pos', which is low latency */ - else if ((0 != (sc->best->prefs & GNUNET_MQ_PREF_LOW_LATENCY)) && - (time_delta < 0)) - time_delta *= 10; /* increase weight, favors 'sc->best', which is low latency */ - if (0 != queue->mtu) - { - /* Grant bonus if we are below MTU, larger bonus the closer we will - be to the MTU */ - if (queue->mtu > sc->real_overhead + sc->best->bytes_msg) - sc_score -= queue->mtu - (sc->real_overhead + sc->best->bytes_msg); - if (queue->mtu > real_overhead + pos->bytes_msg) - pm_score -= queue->mtu - (real_overhead + pos->bytes_msg); - } - if (sc_score + time_delta > pm_score) - { - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "sc_score of %llu larger, keep sc->best %llu\n", - pos->logging_uuid, - sc->best->logging_uuid); - continue; /* sc_score larger, keep sc->best */ - } - } - sc->best = pos; - sc->dvh = dvh; - sc->frag = frag; - sc->relb = relb; - sc->real_overhead = real_overhead; - } -} - - -/** - * Function to call to further operate on the now DV encapsulated - * message @a hdr, forwarding it via @a next_hop under respect of - * @a options. - * - * @param cls a `struct PendingMessageScoreContext` - * @param next_hop next hop of the DV path - * @param hdr encapsulated message, technically a `struct TransportDVBoxMessage` - * @param options options of the original message - */ -static void -extract_box_cb (void *cls, - struct Neighbour *next_hop, - const struct GNUNET_MessageHeader *hdr, - enum RouteMessageOptions options) -{ - struct PendingMessageScoreContext *sc = cls; - struct PendingMessage *pm = sc->best; - struct PendingMessage *bpm; - uint16_t bsize = ntohs (hdr->size); - - GNUNET_assert (NULL == pm->bpm); - bpm = GNUNET_malloc (sizeof(struct PendingMessage) + bsize); - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "5 created pm %p storing vl %p from pm %p\n", - bpm, - pm->vl, - pm); - bpm->logging_uuid = logging_uuid_gen++; - bpm->pmt = PMT_DV_BOX; - bpm->vl = pm->vl; - bpm->timeout = pm->timeout; - bpm->bytes_msg = bsize; - bpm->frag_parent = pm; - set_pending_message_uuid (bpm); - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "Creating DV Box %llu for original message %llu (next hop is %s)\n", - bpm->logging_uuid, - pm->logging_uuid, - GNUNET_i2s (&next_hop->pid)); - memcpy (&bpm[1], hdr, bsize); - pm->bpm = bpm; -} - - -/** - * We believe we are ready to transmit a `struct PendingMessage` on a - * queue, the big question is which one! We need to see if there is - * one pending that is allowed by flow control and congestion control - * and (ideally) matches our queue's performance profile. - * - * If such a message is found, we give the message to the communicator - * for transmission (updating the tracker, and re-scheduling ourselves - * if applicable). - * - * If no such message is found, the queue's `idle` field must be set - * to #GNUNET_YES. - * - * @param cls the `struct Queue` to process transmissions for - */ -static void -transmit_on_queue (void *cls) -{ - struct Queue *queue = cls; - struct Neighbour *n = queue->neighbour; - struct PendingMessageScoreContext sc; - struct PendingMessage *pm; - - queue->transmit_task = NULL; - if (NULL == n->vl) - { - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "Virtual link `%s' is down, cannot have PM for queue `%s'\n", - GNUNET_i2s (&n->pid), - queue->address); - queue->idle = GNUNET_YES; - return; - } - memset (&sc, 0, sizeof(sc)); - select_best_pending_from_link (&sc, queue, n->vl, NULL, 0); - if (NULL == sc.best) - { - /* Also look at DVH that have the n as first hop! */ - for (struct DistanceVectorHop *dvh = n->dv_head; NULL != dvh; - dvh = dvh->next_neighbour) - { - select_best_pending_from_link (&sc, - queue, - dvh->dv->vl, - dvh, - sizeof(struct GNUNET_PeerIdentity) - * (1 + dvh->distance) - + sizeof(struct TransportDVBoxMessage) - + sizeof(struct TransportDVBoxPayloadP)); - } - } - if (NULL == sc.best) - { - /* no message pending, nothing to do here! */ - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "No pending messages, queue `%s' to %s now idle\n", - queue->address, - GNUNET_i2s (&n->pid)); - if (GNUNET_YES == sc.to_early) - schedule_transmit_on_queue (sc.to_early_retry_delay, - queue, - GNUNET_SCHEDULER_PRIORITY_DEFAULT); - queue->idle = GNUNET_YES; - return; - } - /* There is a message pending, we are certainly not idle */ - queue->idle = GNUNET_NO; - - /* Given selection in `sc`, do transmission */ - pm = sc.best; - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "Selected message <%llu>\n", - pm->logging_uuid); - if (NULL != sc.dvh) - { - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "Is this %u a DV box?\n", - pm->pmt); - GNUNET_assert (PMT_DV_BOX != pm->pmt); - if ((NULL != sc.best->bpm) && (sc.best->bpm->used_dvh != sc.dvh)) - { - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "Discard old box, because we have a new DV path.\n"); - free_pending_message (sc.best->bpm); - sc.best->bpm = NULL; - } - - if (NULL == sc.best->bpm) - { - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "encapsulate_for_dv 2\n"); - encapsulate_for_dv (sc.dvh->dv, - 1, - &sc.dvh, - (const struct GNUNET_MessageHeader *) &sc.best[1], - &extract_box_cb, - &sc, - RMO_NONE, - GNUNET_NO); - GNUNET_assert (NULL != sc.best->bpm); - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "%lu %lu %lu %lu %u\n", - sizeof(struct GNUNET_PeerIdentity), - sizeof(struct TransportDVBoxMessage), - sizeof(struct TransportDVBoxPayloadP), - sizeof(struct TransportFragmentBoxMessage), - ((const struct GNUNET_MessageHeader *) &sc.best[1])->size); - sc.best->bpm->used_dvh = sc.dvh; - } - pm = sc.best->bpm; - } - if (GNUNET_YES == sc.frag) - { - pm = fragment_message (queue, sc.dvh, pm); - if (NULL == pm) - { - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "Fragmentation failed queue %s to %s for <%llu>, trying again\n", - queue->address, - GNUNET_i2s (&n->pid), - sc.best->logging_uuid); - schedule_transmit_on_queue (GNUNET_TIME_UNIT_ZERO, - queue, - GNUNET_SCHEDULER_PRIORITY_DEFAULT); - return; - } - } - else if (GNUNET_YES == sc.relb) - { - pm = reliability_box_message (queue, sc.dvh, pm); - if (NULL == pm) - { - /* Reliability boxing failed, try next message... */ - GNUNET_log ( - GNUNET_ERROR_TYPE_DEBUG, - "Reliability boxing failed queue %s to %s for <%llu>, trying again\n", - queue->address, - GNUNET_i2s (&n->pid), - sc.best->logging_uuid); - schedule_transmit_on_queue (GNUNET_TIME_UNIT_ZERO, - queue, - GNUNET_SCHEDULER_PRIORITY_DEFAULT); - return; - } - } - - /* Pass 'pm' for transission to the communicator */ - GNUNET_log ( - GNUNET_ERROR_TYPE_DEBUG, - "Passing message <%llu> to queue %s for peer %s (considered %u others)\n", - pm->logging_uuid, - queue->address, - GNUNET_i2s (&n->pid), - sc.consideration_counter); - - /* Flow control: increment amount of traffic sent; if we are routing - via DV (and thus the ultimate target of the pending message is for - a different virtual link than the one of the queue), then we need - to use up not only the window of the direct link but also the - flow control window for the DV link! */ - pm->vl->outbound_fc_window_size_used += pm->bytes_msg; - - if (pm->vl != queue->neighbour->vl) - { - /* If the virtual link of the queue differs, this better be distance - vector routing! */ - GNUNET_assert (NULL != sc.dvh); - /* If we do distance vector routing, we better not do this for a - message that was itself DV-routed */ - GNUNET_assert (PMT_DV_BOX != sc.best->pmt); - /* We use the size of the unboxed message here, to avoid counting - the DV-Box header which is eaten up on the way by intermediaries */ - queue->neighbour->vl->outbound_fc_window_size_used += sc.best->bytes_msg; - } - else - { - GNUNET_assert (NULL == sc.dvh); - } - - queue_send_msg (queue, pm, &pm[1], pm->bytes_msg); - - /* Check if this transmission somehow conclusively finished handing 'pm' - even without any explicit ACKs */ - if ((PMT_CORE == pm->pmt) || - (GNUNET_TRANSPORT_CC_RELIABLE == queue->tc->details.communicator.cc)) - { - completed_pending_message (pm); - } - else - { - struct GNUNET_TIME_Relative wait_duration; - unsigned int wait_multiplier; - - if (PMT_FRAGMENT_BOX == pm->pmt) - { - struct PendingMessage *root; - - root = pm->frag_parent; - while (NULL != root->frag_parent) - root = root->frag_parent; - - root->frag_count++; - wait_multiplier = (unsigned int) ceil (root->bytes_msg - / (root->frag_off - / root->frag_count)) * 4; - } - else - { - // No fragments, we use 4 RTT before retransmitting. - wait_multiplier = 4; - } - - // Depending on how much pending message the VirtualLink is queueing, we wait longer. - // wait_multiplier = wait_multiplier * pm->vl->pending_msg_num; - - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "Wait multiplier %u\n", - wait_multiplier); - - /* Message not finished, waiting for acknowledgement. - Update time by which we might retransmit 's' based on queue - characteristics (i.e. RTT); it takes one RTT for the message to - arrive and the ACK to come back in the best case; but the other - side is allowed to delay ACKs by 2 RTTs, so we use 4 RTT before - retransmitting. - - OPTIMIZE: Note that in the future this heuristic should likely - be improved further (measure RTT stability, consider message - urgency and size when delaying ACKs, etc.) */ - - if (GNUNET_TIME_UNIT_FOREVER_REL.rel_value_us != - queue->pd.aged_rtt.rel_value_us) - wait_duration = queue->pd.aged_rtt; - else - { - wait_duration = DEFAULT_ACK_WAIT_DURATION; - wait_multiplier = 4; - } - struct GNUNET_TIME_Absolute next = GNUNET_TIME_relative_to_absolute ( - GNUNET_TIME_relative_multiply ( - wait_duration, wait_multiplier)); - struct GNUNET_TIME_Relative plus = GNUNET_TIME_relative_multiply ( - wait_duration, wait_multiplier); - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "Waiting %s (%s) for ACK until %s\n", - GNUNET_STRINGS_relative_time_to_string ( - GNUNET_TIME_relative_multiply ( - queue->pd.aged_rtt, wait_multiplier), GNUNET_NO), - GNUNET_STRINGS_relative_time_to_string (plus, GNUNET_YES), - GNUNET_STRINGS_absolute_time_to_string (next)); - update_pm_next_attempt (pm, - GNUNET_TIME_relative_to_absolute ( - GNUNET_TIME_relative_multiply (wait_duration, - wait_multiplier))); - } - /* finally, re-schedule queue transmission task itself */ - schedule_transmit_on_queue (GNUNET_TIME_UNIT_ZERO, - queue, - GNUNET_SCHEDULER_PRIORITY_DEFAULT); -} - - -/** - * Queue to a peer went down. Process the request. - * - * @param cls the client - * @param dqm the send message that was sent - */ -static void -handle_del_queue_message (void *cls, - const struct GNUNET_TRANSPORT_DelQueueMessage *dqm) -{ - struct TransportClient *tc = cls; - - if (CT_COMMUNICATOR != tc->type) - { - GNUNET_break (0); - GNUNET_SERVICE_client_drop (tc->client); - return; - } - for (struct Queue *queue = tc->details.communicator.queue_head; NULL != queue; - queue = queue->next_client) - { - struct Neighbour *neighbour = queue->neighbour; - - if ((ntohl (dqm->qid) != queue->qid) || - (0 != GNUNET_memcmp (&dqm->receiver, &neighbour->pid))) - continue; - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "Dropped queue %s to peer %s\n", - queue->address, - GNUNET_i2s (&neighbour->pid)); - free_queue (queue); - GNUNET_SERVICE_client_continue (tc->client); - return; - } - GNUNET_break (0); - GNUNET_SERVICE_client_drop (tc->client); -} - - -/** - * Message was transmitted. Process the request. - * - * @param cls the client - * @param sma the send message that was sent - */ -static void -handle_send_message_ack (void *cls, - const struct GNUNET_TRANSPORT_SendMessageToAck *sma) -{ - struct TransportClient *tc = cls; - struct QueueEntry *qe; - struct PendingMessage *pm; - - if (CT_COMMUNICATOR != tc->type) - { - GNUNET_break (0); - GNUNET_SERVICE_client_drop (tc->client); - return; - } - - /* find our queue entry matching the ACK */ - qe = NULL; - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "Looking for queue for PID %s\n", - GNUNET_i2s (&sma->receiver)); - for (struct Queue *queue = tc->details.communicator.queue_head; NULL != queue; - queue = queue->next_client) - { - if (0 != GNUNET_memcmp (&queue->neighbour->pid, &sma->receiver)) - continue; - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "Found PID %s\n", - GNUNET_i2s (&queue->neighbour->pid)); - - - for (struct QueueEntry *qep = queue->queue_head; NULL != qep; - qep = qep->next) - { - if (qep->mid != GNUNET_ntohll (sma->mid) || queue->qid != ntohl ( - sma->qid)) - continue; - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "QueueEntry MID: %" PRIu64 " on queue QID: %u, Ack MID: %" - PRIu64 " Ack QID %u\n", - qep->mid, - queue->qid, - GNUNET_ntohll (sma->mid), - ntohl (sma->qid)); - qe = qep; - if ((NULL != qe->pm) && (qe->pm->qe != qe)) - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "For pending message %" PRIu64 " we had retransmissions.\n", - qe->pm->logging_uuid); - break; - } - } - if (NULL == qe) - { - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "No QueueEntry found for Ack MID %" PRIu64 " QID: %u\n", - GNUNET_ntohll (sma->mid), - ntohl (sma->qid)); - // TODO I guess this can happen, if the Ack from the peer comes before the Ack from the queue. - // Update: Maybe QueueEntry was accidentally freed during freeing PendingMessage. - /* this should never happen */ - GNUNET_break (0); - //GNUNET_SERVICE_client_drop (tc->client); - GNUNET_SERVICE_client_continue (tc->client); - return; - } - GNUNET_CONTAINER_DLL_remove (qe->queue->queue_head, - qe->queue->queue_tail, - qe); - qe->queue->queue_length--; - tc->details.communicator.total_queue_length--; - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "Received ACK on queue %s to peer %s (new length: %u/%u)\n", - qe->queue->address, - GNUNET_i2s (&qe->queue->neighbour->pid), - qe->queue->queue_length, - tc->details.communicator.total_queue_length); - GNUNET_SERVICE_client_continue (tc->client); - - /* if applicable, resume transmissions that waited on ACK */ - if (COMMUNICATOR_TOTAL_QUEUE_LIMIT - 1 == - tc->details.communicator.total_queue_length) - { - /* Communicator dropped below threshold, resume all queues - incident with this client! */ - GNUNET_STATISTICS_update ( - GST_stats, - "# Transmission throttled due to communicator queue limit", - -1, - GNUNET_NO); - for (struct Queue *queue = tc->details.communicator.queue_head; - NULL != queue; - queue = queue->next_client) - { - schedule_transmit_on_queue (GNUNET_TIME_UNIT_ZERO, - queue, - GNUNET_SCHEDULER_PRIORITY_DEFAULT); - } - } - else if (QUEUE_LENGTH_LIMIT - 1 == qe->queue->queue_length) - { - /* queue dropped below threshold; only resume this one queue */ - GNUNET_STATISTICS_update (GST_stats, - "# Transmission throttled due to queue queue limit", - -1, - GNUNET_NO); - schedule_transmit_on_queue (GNUNET_TIME_UNIT_ZERO, - qe->queue, - GNUNET_SCHEDULER_PRIORITY_DEFAULT); - } - else if (1 == qe->queue->q_capacity) - { - // TODO I guess this will never happen, because the communicator triggers this by updating its queue length itself. - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "Transmission rescheduled due to communicator message queue with qid %u has capacity %" - PRIu64 ".\n", - qe->queue->qid, - qe->queue->q_capacity); - /* message queue has capacity; only resume this one queue */ - /* queue dropped below threshold; only resume this one queue */ - GNUNET_STATISTICS_update (GST_stats, - "# Transmission throttled due to message queue capacity", - -1, - GNUNET_NO); - schedule_transmit_on_queue (GNUNET_TIME_UNIT_ZERO, - qe->queue, - GNUNET_SCHEDULER_PRIORITY_DEFAULT); - } - - if (NULL != (pm = qe->pm)) - { - struct VirtualLink *vl; - - // GNUNET_assert (qe == pm->qe); - pm->qe = NULL; - /* If waiting for this communicator may have blocked transmission - of pm on other queues for this neighbour, force schedule - transmit on queue for queues of the neighbour */ - if (NULL == pm->frag_parent) - { - vl = pm->vl; - if ((NULL != vl) && - (NULL != vl->pending_msg_head) && - (vl->pending_msg_head == pm)) - check_vl_transmission (vl); - } - } - GNUNET_free (qe); -} - - -/** - * Iterator telling new MONITOR client about all existing - * queues to peers. - * - * @param cls the new `struct TransportClient` - * @param pid a connected peer - * @param value the `struct Neighbour` with more information - * @return #GNUNET_OK (continue to iterate) - */ -static int -notify_client_queues (void *cls, - const struct GNUNET_PeerIdentity *pid, - void *value) -{ - struct TransportClient *tc = cls; - struct Neighbour *neighbour = value; - - GNUNET_assert (CT_MONITOR == tc->type); - for (struct Queue *q = neighbour->queue_head; NULL != q; - q = q->next_neighbour) - { - struct MonitorEvent me = { .rtt = q->pd.aged_rtt, - .cs = q->cs, - .num_msg_pending = q->num_msg_pending, - .num_bytes_pending = q->num_bytes_pending }; - - notify_monitor (tc, pid, q->address, q->nt, &me); - } - return GNUNET_OK; -} - - -/** - * Initialize a monitor client. - * - * @param cls the client - * @param start the start message that was sent - */ -static void -handle_monitor_start (void *cls, - const struct GNUNET_TRANSPORT_MonitorStart *start) -{ - struct TransportClient *tc = cls; - - if (CT_NONE != tc->type) - { - GNUNET_break (0); - GNUNET_SERVICE_client_drop (tc->client); - return; - } - tc->type = CT_MONITOR; - tc->details.monitor.peer = start->peer; - tc->details.monitor.one_shot = ntohl (start->one_shot); - GNUNET_CONTAINER_multipeermap_iterate (neighbours, ¬ify_client_queues, tc); - GNUNET_SERVICE_client_mark_monitor (tc->client); - GNUNET_SERVICE_client_continue (tc->client); -} - - -/** - * Find transport client providing communication service - * for the protocol @a prefix. - * - * @param prefix communicator name - * @return NULL if no such transport client is available - */ -static struct TransportClient * -lookup_communicator (const char *prefix) -{ - for (struct TransportClient *tc = clients_head; NULL != tc; tc = tc->next) - { - if (CT_COMMUNICATOR != tc->type) - continue; - if (0 == strcmp (prefix, tc->details.communicator.address_prefix)) - return tc; - } - GNUNET_log ( - GNUNET_ERROR_TYPE_WARNING, - "Somone suggested use of communicator for `%s', but we do not have such a communicator!\n", - prefix); - return NULL; -} - - -/** - * Signature of a function called with a communicator @a address of a peer - * @a pid that an application wants us to connect to. - * - * @param pid target peer - * @param address the address to try - */ -static void -suggest_to_connect (const struct GNUNET_PeerIdentity *pid, const char *address) -{ - static uint32_t idgen; - struct TransportClient *tc; - char *prefix; - struct GNUNET_TRANSPORT_CreateQueue *cqm; - struct GNUNET_MQ_Envelope *env; - size_t alen; - - prefix = GNUNET_HELLO_address_to_prefix (address); - if (NULL == prefix) - { - GNUNET_break (0); /* We got an invalid address!? */ - return; - } - tc = lookup_communicator (prefix); - if (NULL == tc) - { - GNUNET_STATISTICS_update (GST_stats, - "# Suggestions ignored due to missing communicator", - 1, - GNUNET_NO); - GNUNET_log (GNUNET_ERROR_TYPE_INFO, - "Cannot connect to %s at `%s', no matching communicator present\n", - GNUNET_i2s (pid), - address); - GNUNET_free (prefix); - return; - } - /* forward suggestion for queue creation to communicator */ - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "Request #%u for `%s' communicator to create queue to `%s' at `%s'\n", - (unsigned int) idgen, - prefix, - GNUNET_i2s (pid), - address); - GNUNET_free (prefix); - alen = strlen (address) + 1; - env = - GNUNET_MQ_msg_extra (cqm, alen, GNUNET_MESSAGE_TYPE_TRANSPORT_QUEUE_CREATE); - cqm->request_id = htonl (idgen++); - cqm->receiver = *pid; - memcpy (&cqm[1], address, alen); - GNUNET_MQ_send (tc->mq, env); -} - - -/** - * The queue @a q (which matches the peer and address in @a vs) is - * ready for queueing. We should now queue the validation request. - * - * @param q queue to send on - * @param vs state to derive validation challenge from - */ -static void -validation_transmit_on_queue (struct Queue *q, struct ValidationState *vs) -{ - struct TransportValidationChallengeMessage tvc; - - vs->last_challenge_use = GNUNET_TIME_absolute_get_monotonic (GST_cfg); - tvc.header.type = - htons (GNUNET_MESSAGE_TYPE_TRANSPORT_ADDRESS_VALIDATION_CHALLENGE); - tvc.header.size = htons (sizeof(tvc)); - tvc.reserved = htonl (0); - tvc.challenge = vs->challenge; - tvc.sender_time = GNUNET_TIME_absolute_hton (vs->last_challenge_use); - GNUNET_log (GNUNET_ERROR_TYPE_INFO, - "Sending address validation challenge %s to %s\n", - GNUNET_sh2s (&tvc.challenge.value), - GNUNET_i2s (&q->neighbour->pid)); - queue_send_msg (q, NULL, &tvc, sizeof(tvc)); -} - - -/** - * Task run periodically to validate some address based on #validation_heap. - * - * @param cls NULL - */ -static void -validation_start_cb (void *cls) -{ - struct ValidationState *vs; - struct Queue *q; - const struct GNUNET_TIME_Absolute now = GNUNET_TIME_absolute_get_monotonic ( - GST_cfg); - - (void) cls; - validation_task = NULL; - vs = GNUNET_CONTAINER_heap_peek (validation_heap); - /* drop validations past their expiration */ - while ( - (NULL != vs) && - (0 == GNUNET_TIME_absolute_get_remaining (vs->valid_until).rel_value_us)) - { - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "Validation response %s cleaned up\n", - GNUNET_sh2s (&vs->challenge.value)); - free_validation_state (vs); - vs = GNUNET_CONTAINER_heap_peek (validation_heap); - } - if (NULL == vs) - { - GNUNET_log (GNUNET_ERROR_TYPE_INFO, - "Address validation task not scheduled anymore, nothing to do\n"); - return; /* woopsie, no more addresses known, should only - happen if we're really a lonely peer */ - } - q = find_queue (&vs->pid, vs->address); - if (GNUNET_TIME_absolute_cmp (vs->first_challenge_use, >, now)) - { - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "To early to start next address validation for challenge %s\n", - GNUNET_sh2s (&vs->challenge.value)); - return; - } - if (NULL == q) - { - vs->awaiting_queue = GNUNET_YES; - suggest_to_connect (&vs->pid, vs->address); - } - else - validation_transmit_on_queue (q, vs); - /* Finally, reschedule next attempt */ - vs->challenge_backoff = - GNUNET_TIME_randomized_backoff (vs->challenge_backoff, - MAX_VALIDATION_CHALLENGE_FREQ); - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "Address validation task will run again in %s\n", - GNUNET_STRINGS_relative_time_to_string (vs->challenge_backoff, - GNUNET_YES)); - update_next_challenge_time (vs, - GNUNET_TIME_relative_to_absolute ( - vs->challenge_backoff)); -} - - -/** - * Closure for #check_connection_quality. - */ -struct QueueQualityContext -{ - /** - * Set to the @e k'th queue encountered. - */ - struct Queue *q; - - /** - * Set to the number of quality queues encountered. - */ - unsigned int quality_count; - - /** - * Set to the total number of queues encountered. - */ - unsigned int num_queues; - - /** - * Decremented for each queue, for selection of the - * k-th queue in @e q. - */ - unsigned int k; -}; - - -/** - * Check whether any queue to the given neighbour is - * of a good "quality" and if so, increment the counter. - * Also counts the total number of queues, and returns - * the k-th queue found. - * - * @param cls a `struct QueueQualityContext *` with counters - * @param pid peer this is about - * @param value a `struct Neighbour` - * @return #GNUNET_OK (continue to iterate) - */ -static int -check_connection_quality (void *cls, - const struct GNUNET_PeerIdentity *pid, - void *value) -{ - struct QueueQualityContext *ctx = cls; - struct Neighbour *n = value; - int do_inc; - - (void) pid; - do_inc = GNUNET_NO; - for (struct Queue *q = n->queue_head; NULL != q; q = q->next_neighbour) - { - ctx->num_queues++; - if (0 == ctx->k--) - ctx->q = q; - /* FIXME-CONQ-STATISTICS: in the future, add reliability / goodput - statistics and consider those as well here? */ - if (q->pd.aged_rtt.rel_value_us < DV_QUALITY_RTT_THRESHOLD.rel_value_us) - do_inc = GNUNET_YES; - } - if (GNUNET_YES == do_inc) - ctx->quality_count++; - return GNUNET_OK; -} - - -/** - * Task run when we CONSIDER initiating a DV learn - * process. We first check that sending out a message is - * even possible (queues exist), then that it is desirable - * (if not, reschedule the task for later), and finally - * we may then begin the job. If there are too many - * entries in the #dvlearn_map, we purge the oldest entry - * using #lle_tail. - * - * @param cls NULL - */ -static void -start_dv_learn (void *cls) -{ - struct LearnLaunchEntry *lle; - struct QueueQualityContext qqc; - struct TransportDVLearnMessage dvl; - - (void) cls; - dvlearn_task = NULL; - if (0 == GNUNET_CONTAINER_multipeermap_size (neighbours)) - return; /* lost all connectivity, cannot do learning */ - qqc.quality_count = 0; - qqc.num_queues = 0; - qqc.k = GNUNET_CONTAINER_multipeermap_size (neighbours); - GNUNET_CONTAINER_multipeermap_iterate (neighbours, - &check_connection_quality, - &qqc); - if (qqc.quality_count > DV_LEARN_QUALITY_THRESHOLD) - { - struct GNUNET_TIME_Relative delay; - unsigned int factor; - - /* scale our retries by how far we are above the threshold */ - factor = qqc.quality_count / DV_LEARN_QUALITY_THRESHOLD; - delay = GNUNET_TIME_relative_multiply (DV_LEARN_BASE_FREQUENCY, factor); - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "At connection quality %u, will launch DV learn in %s\n", - qqc.quality_count, - GNUNET_STRINGS_relative_time_to_string (delay, GNUNET_YES)); - dvlearn_task = GNUNET_SCHEDULER_add_delayed (delay, &start_dv_learn, NULL); - return; - } - /* remove old entries in #dvlearn_map if it has grown too big */ - while (MAX_DV_LEARN_PENDING <= - GNUNET_CONTAINER_multishortmap_size (dvlearn_map)) - { - lle = lle_tail; - GNUNET_assert (GNUNET_YES == - GNUNET_CONTAINER_multishortmap_remove (dvlearn_map, - &lle->challenge.value, - lle)); - GNUNET_CONTAINER_DLL_remove (lle_head, lle_tail, lle); - GNUNET_free (lle); - } - /* setup data structure for learning */ - lle = GNUNET_new (struct LearnLaunchEntry); - GNUNET_CRYPTO_random_block (GNUNET_CRYPTO_QUALITY_NONCE, - &lle->challenge, - sizeof(lle->challenge)); - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "Starting launch DV learn with challenge %s\n", - GNUNET_sh2s (&lle->challenge.value)); - GNUNET_CONTAINER_DLL_insert (lle_head, lle_tail, lle); - GNUNET_break (GNUNET_YES == - GNUNET_CONTAINER_multishortmap_put ( - dvlearn_map, - &lle->challenge.value, - lle, - GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_ONLY)); - dvl.header.type = htons (GNUNET_MESSAGE_TYPE_TRANSPORT_DV_LEARN); - dvl.header.size = htons (sizeof(dvl)); - dvl.num_hops = htons (0); - dvl.bidirectional = htons (0); - dvl.non_network_delay = GNUNET_TIME_relative_hton (GNUNET_TIME_UNIT_ZERO); - dvl.monotonic_time = - GNUNET_TIME_absolute_hton (GNUNET_TIME_absolute_get_monotonic (GST_cfg)); - { - struct DvInitPS dvip = { - .purpose.purpose = htonl ( - GNUNET_SIGNATURE_PURPOSE_TRANSPORT_DV_INITIATOR), - .purpose.size = htonl (sizeof(dvip)), - .monotonic_time = dvl.monotonic_time, - .challenge = lle->challenge - }; - - GNUNET_CRYPTO_eddsa_sign (GST_my_private_key, - &dvip, - &dvl.init_sig); - } - dvl.initiator = GST_my_identity; - dvl.challenge = lle->challenge; - - qqc.quality_count = 0; - qqc.k = GNUNET_CRYPTO_random_u32 (GNUNET_CRYPTO_QUALITY_WEAK, qqc.num_queues); - qqc.num_queues = 0; - qqc.q = NULL; - GNUNET_CONTAINER_multipeermap_iterate (neighbours, - &check_connection_quality, - &qqc); - GNUNET_assert (NULL != qqc.q); - - /* Do this as close to transmission time as possible! */ - lle->launch_time = GNUNET_TIME_absolute_get (); - - queue_send_msg (qqc.q, NULL, &dvl, sizeof(dvl)); - /* reschedule this job, randomizing the time it runs (but no - actual backoff!) */ - dvlearn_task = GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_randomize ( - DV_LEARN_BASE_FREQUENCY), - &start_dv_learn, - NULL); -} - - -/** - * Get the IP address without the port number. - * - * @param address The string contains a communicator prefix, IP address and port - * like this 'tcp-92.68.150.1:55452'. - * @return String with IP address only. - */ -static char * -get_address_without_port (const char *address) -{ - const char *colon; - char *colon_rest; - size_t colon_rest_length; - char *address_without_port; - - colon = strchr (address,':'); - colon_rest = GNUNET_strndup (address, colon - address); - colon_rest_length = strlen (colon_rest); - address_without_port = GNUNET_strndup (&colon_rest[4], colon_rest_length - 4); - GNUNET_free (colon_rest); - - return address_without_port; -} - - -/** - * A new queue has been created, check if any address validation - * requests have been waiting for it. - * - * @param cls a `struct Queue` - * @param pid peer concerned (unused) - * @param value a `struct ValidationState` - * @return #GNUNET_NO if a match was found and we can stop looking - */ -static int -check_validation_request_pending (void *cls, - const struct GNUNET_PeerIdentity *pid, - void *value) -{ - struct Queue *q = cls; - struct ValidationState *vs = value; - char *address_without_port_vs; - char *address_without_port_q; - int success = GNUNET_YES; - - address_without_port_vs = get_address_without_port (vs->address); - address_without_port_q = get_address_without_port (q->address); - - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "Check validation request pending for `%s' at `%s'/`%s' (vs)/(q)\n", - GNUNET_i2s (pid), - address_without_port_vs, - address_without_port_q); - (void) pid; - if ((GNUNET_YES == vs->awaiting_queue) && - (0 == strcmp (address_without_port_vs, address_without_port_q))) - { - - vs->awaiting_queue = GNUNET_NO; - validation_transmit_on_queue (q, vs); - success = GNUNET_NO; - } - - GNUNET_free (address_without_port_vs); - GNUNET_free (address_without_port_q); - return success; -} - - -/** - * Function called with the monotonic time of a DV initiator - * by PEERSTORE. Updates the time. - * - * @param cls a `struct Neighbour` - * @param record the information found, NULL for the last call - * @param emsg error message - */ -static void -neighbour_dv_monotime_cb (void *cls, - const struct GNUNET_PEERSTORE_Record *record, - const char *emsg) -{ - struct Neighbour *n = cls; - struct GNUNET_TIME_AbsoluteNBO *mtbe; - - (void) emsg; - if (NULL == record) - { - /* we're done with #neighbour_dv_monotime_cb() invocations, - continue normal processing */ - n->get = NULL; - n->dv_monotime_available = GNUNET_YES; - return; - } - if (sizeof(*mtbe) != record->value_size) - { - GNUNET_break (0); - return; - } - mtbe = record->value; - n->last_dv_learn_monotime = - GNUNET_TIME_absolute_max (n->last_dv_learn_monotime, - GNUNET_TIME_absolute_ntoh (*mtbe)); -} - - -/** - * New queue became available. Process the request. - * - * @param cls the client - * @param aqm the send message that was sent - */ -static void -handle_add_queue_message (void *cls, - const struct GNUNET_TRANSPORT_AddQueueMessage *aqm) -{ - struct TransportClient *tc = cls; - struct Queue *queue; - struct Neighbour *neighbour; - struct GNUNET_TIME_Absolute validated_until = GNUNET_TIME_UNIT_ZERO_ABS; - const char *addr; - uint16_t addr_len; - - if (ntohl (aqm->mtu) <= sizeof(struct TransportFragmentBoxMessage)) - { - /* MTU so small as to be useless for transmissions, - required for #fragment_message()! */ - GNUNET_break_op (0); - GNUNET_SERVICE_client_drop (tc->client); - return; - } - /* This may simply be a queue update */ - for (queue = tc->details.communicator.queue_head; - NULL != queue; - queue = queue->next_client) - { - validated_until = queue->validated_until; - if (queue->qid != ntohl (aqm->qid)) - continue; - break; - } - - if (NULL != queue) - { - neighbour = queue->neighbour; - } - else - { - neighbour = lookup_neighbour (&aqm->receiver); - if (NULL == neighbour) - { - neighbour = GNUNET_new (struct Neighbour); - neighbour->pid = aqm->receiver; - GNUNET_assert (GNUNET_OK == - GNUNET_CONTAINER_multipeermap_put ( - neighbours, - &neighbour->pid, - neighbour, - GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_ONLY)); - neighbour->get = - GNUNET_PEERSTORE_iterate (peerstore, - "transport", - &neighbour->pid, - GNUNET_PEERSTORE_TRANSPORT_DVLEARN_MONOTIME, - &neighbour_dv_monotime_cb, - neighbour); - } - addr_len = ntohs (aqm->header.size) - sizeof(*aqm); - addr = (const char *) &aqm[1]; - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "New queue %s to %s available with QID %u and q_len %" PRIu64 - " and mtu %u\n", - addr, - GNUNET_i2s (&aqm->receiver), - ntohl (aqm->qid), - GNUNET_ntohll (aqm->q_len), - ntohl (aqm->mtu)); - queue = GNUNET_malloc (sizeof(struct Queue) + addr_len); - queue->tc = tc; - if (GNUNET_TIME_UNIT_ZERO_ABS.abs_value_us != validated_until.abs_value_us) - { - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "New queue with QID %u inherit validated until\n", - ntohl (aqm->qid)); - queue->validated_until = validated_until; - } - queue->address = (const char *) &queue[1]; - queue->pd.aged_rtt = GNUNET_TIME_UNIT_FOREVER_REL; - queue->qid = ntohl (aqm->qid); - queue->neighbour = neighbour; - if (GNUNET_TRANSPORT_QUEUE_LENGTH_UNLIMITED == GNUNET_ntohll (aqm->q_len)) - queue->unlimited_length = GNUNET_YES; - queue->q_capacity = GNUNET_ntohll (aqm->q_len); - memcpy (&queue[1], addr, addr_len); - /* notify monitors about new queue */ - { - struct MonitorEvent me = { .rtt = queue->pd.aged_rtt, .cs = queue->cs }; - - notify_monitors (&neighbour->pid, queue->address, queue->nt, &me); - } - GNUNET_CONTAINER_MDLL_insert (neighbour, - neighbour->queue_head, - neighbour->queue_tail, - queue); - GNUNET_CONTAINER_MDLL_insert (client, - tc->details.communicator.queue_head, - tc->details.communicator.queue_tail, - queue); - - } - queue->mtu = ntohl (aqm->mtu); - queue->nt = (enum GNUNET_NetworkType) ntohl (aqm->nt); - queue->cs = (enum GNUNET_TRANSPORT_ConnectionStatus) ntohl (aqm->cs); - queue->idle = GNUNET_YES; - /* check if valdiations are waiting for the queue */ - (void) - GNUNET_CONTAINER_multipeermap_get_multiple (validation_map, - &aqm->receiver, - &check_validation_request_pending, - queue); - /* look for traffic for this queue */ - schedule_transmit_on_queue (GNUNET_TIME_UNIT_ZERO, - queue, GNUNET_SCHEDULER_PRIORITY_DEFAULT); - /* might be our first queue, try launching DV learning */ - if (NULL == dvlearn_task) - dvlearn_task = GNUNET_SCHEDULER_add_now (&start_dv_learn, NULL); - GNUNET_SERVICE_client_continue (tc->client); -} - - -/** - * @brief Handle updates to queues. - * - * @param cls the transport client. - * @param msg Message struct. - */ -static void -handle_update_queue_message (void *cls, - const struct - GNUNET_TRANSPORT_UpdateQueueMessage *msg) -{ - struct TransportClient *tc = cls; - struct Queue *target_queue = NULL; - - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "Received queue update message for %u with q_len %llu and mtu %u\n", - ntohl (msg->qid), - (unsigned long long) GNUNET_ntohll (msg->q_len), - ntohl (msg->mtu)); - for (target_queue = tc->details.communicator.queue_head; - NULL != target_queue; - target_queue = target_queue->next_client) - { - if (ntohl (msg->qid) == target_queue->qid) - break; - } - if (NULL == target_queue) - { - GNUNET_log (GNUNET_ERROR_TYPE_WARNING, - "Queue to update no longer exists! Discarding update.\n"); - return; - } - - target_queue->nt = msg->nt; - target_queue->mtu = ntohl (msg->mtu); - target_queue->cs = msg->cs; - target_queue->priority = ntohl (msg->priority); - /* The update message indicates how many messages - * the queue should be able to handle. - */ - if (GNUNET_TRANSPORT_QUEUE_LENGTH_UNLIMITED == GNUNET_ntohll (msg->q_len)) - target_queue->unlimited_length = GNUNET_YES; - else - target_queue->unlimited_length = GNUNET_NO; - target_queue->q_capacity += GNUNET_ntohll (msg->q_len); - if (0 < target_queue->q_capacity) - schedule_transmit_on_queue (GNUNET_TIME_UNIT_ZERO, - target_queue, - GNUNET_SCHEDULER_PRIORITY_DEFAULT); - GNUNET_SERVICE_client_continue (tc->client); -} - - -/** - * Communicator tells us that our request to create a queue "worked", that - * is setting up the queue is now in process. - * - * @param cls the `struct TransportClient` - * @param cqr confirmation message - */ -static void -handle_queue_create_ok (void *cls, - const struct GNUNET_TRANSPORT_CreateQueueResponse *cqr) -{ - struct TransportClient *tc = cls; - - if (CT_COMMUNICATOR != tc->type) - { - GNUNET_break (0); - GNUNET_SERVICE_client_drop (tc->client); - return; - } - GNUNET_STATISTICS_update (GST_stats, - "# Suggestions succeeded at communicator", - 1, - GNUNET_NO); - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "Request #%u for communicator to create queue succeeded\n", - (unsigned int) ntohs (cqr->request_id)); - GNUNET_SERVICE_client_continue (tc->client); -} - - -/** - * Communicator tells us that our request to create a queue failed. This - * usually indicates that the provided address is simply invalid or that the - * communicator's resources are exhausted. - * - * @param cls the `struct TransportClient` - * @param cqr failure message - */ -static void -handle_queue_create_fail ( - void *cls, - const struct GNUNET_TRANSPORT_CreateQueueResponse *cqr) -{ - struct TransportClient *tc = cls; - - if (CT_COMMUNICATOR != tc->type) - { - GNUNET_break (0); - GNUNET_SERVICE_client_drop (tc->client); - return; - } - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "Request #%u for communicator to create queue failed\n", - (unsigned int) ntohs (cqr->request_id)); - GNUNET_STATISTICS_update (GST_stats, - "# Suggestions failed in queue creation at communicator", - 1, - GNUNET_NO); - GNUNET_SERVICE_client_continue (tc->client); -} - - -/** - * We have received a `struct ExpressPreferenceMessage` from an application - * client. - * - * @param cls handle to the client - * @param msg the start message - */ -static void -handle_suggest_cancel (void *cls, const struct ExpressPreferenceMessage *msg) -{ - struct TransportClient *tc = cls; - struct PeerRequest *pr; - - if (CT_APPLICATION != tc->type) - { - GNUNET_break (0); - GNUNET_SERVICE_client_drop (tc->client); - return; - } - pr = GNUNET_CONTAINER_multipeermap_get (tc->details.application.requests, - &msg->peer); - if (NULL == pr) - { - GNUNET_break (0); - GNUNET_SERVICE_client_drop (tc->client); - return; - } - (void) stop_peer_request (tc, &pr->pid, pr); - GNUNET_SERVICE_client_continue (tc->client); -} - - -static void -hello_for_client_cb (void *cls, - const char *uri) -{ - const struct GNUNET_PeerIdentity *peer = cls; - int pfx_len; - const char *eou; - char *address; - - eou = strstr (uri, - "://"); - pfx_len = eou - uri; - eou += 3; - GNUNET_asprintf (&address, - "%.*s-%s", - pfx_len, - uri, - eou); - - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "hello for client %s\n", - address); - - start_address_validation (peer, address); - GNUNET_free (address); -} - - -/** - * Function called by PEERSTORE for each matching record. - * - * @param cls closure, a `struct PeerRequest` - * @param record peerstore record information - * @param emsg error message, or NULL if no errors - */ -static void -handle_hello_for_client (void *cls, - const struct GNUNET_PeerIdentity *peer, - const struct GNUNET_MessageHeader *hello, - const char *emsg) -{ - (void) cls; - const char *val; - struct GNUNET_HELLO_Builder *builder; - - if (NULL != emsg) - { - GNUNET_log (GNUNET_ERROR_TYPE_WARNING, - "Got failure from PEERSTORE: %s\n", - emsg); - return; - } - if (0 == GNUNET_memcmp (peer, &GST_my_identity)) - return; - builder = GNUNET_HELLO_builder_new (peer); - GNUNET_HELLO_builder_iterate (builder, - (struct GNUNET_PeerIdentity *) peer, - hello_for_client_cb, - NULL); -} - - -/** - * We have received a `struct ExpressPreferenceMessage` from an application - * client. - * - * @param cls handle to the client - * @param msg the start message - */ -static void -handle_suggest (void *cls, const struct ExpressPreferenceMessage *msg) -{ - struct TransportClient *tc = cls; - struct PeerRequest *pr; - - if (CT_NONE == tc->type) - { - tc->type = CT_APPLICATION; - tc->details.application.requests = - GNUNET_CONTAINER_multipeermap_create (16, GNUNET_YES); - } - if (CT_APPLICATION != tc->type) - { - GNUNET_break (0); - GNUNET_SERVICE_client_drop (tc->client); - return; - } - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "Client suggested we talk to %s with preference %d at rate %u\n", - GNUNET_i2s (&msg->peer), - (int) ntohl (msg->pk), - (int) ntohl (msg->bw.value__)); - pr = GNUNET_new (struct PeerRequest); - pr->tc = tc; - pr->pid = msg->peer; - pr->bw = msg->bw; - pr->pk = (enum GNUNET_MQ_PriorityPreferences) ntohl (msg->pk); - if (GNUNET_YES != GNUNET_CONTAINER_multipeermap_put ( - tc->details.application.requests, - &pr->pid, - pr, - GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_ONLY)) - { - GNUNET_break (0); - GNUNET_free (pr); - GNUNET_SERVICE_client_drop (tc->client); - return; - } - pr->nc = GNUNET_PEERSTORE_hello_changed_notify (peerstore, - GNUNET_NO, - &handle_hello_for_client, - NULL); - GNUNET_SERVICE_client_continue (tc->client); -} - - -/** - * Check #GNUNET_MESSAGE_TYPE_TRANSPORT_REQUEST_HELLO_VALIDATION - * messages. - * - * @param cls a `struct TransportClient *` - * @param m message to verify - * @return #GNUNET_OK on success - */ -static int -check_request_hello_validation (void *cls, - const struct RequestHelloValidationMessage *m) -{ - (void) cls; - GNUNET_MQ_check_zero_termination (m); - return GNUNET_OK; -} - - -/** - * A client encountered an address of another peer. Consider validating it, - * and if validation succeeds, persist it to PEERSTORE. - * - * @param cls a `struct TransportClient *` - * @param m message to verify - */ -static void -handle_request_hello_validation (void *cls, - const struct RequestHelloValidationMessage *m) -{ - struct TransportClient *tc = cls; - - start_address_validation (&m->peer, (const char *) &m[1]); - GNUNET_SERVICE_client_continue (tc->client); -} - - -/** - * Free neighbour entry. - * - * @param cls NULL - * @param pid unused - * @param value a `struct Neighbour` - * @return #GNUNET_OK (always) - */ -static int -free_neighbour_cb (void *cls, - const struct GNUNET_PeerIdentity *pid, - void *value) -{ - struct Neighbour *neighbour = value; - - (void) cls; - (void) pid; - GNUNET_break (0); // should this ever happen? - free_neighbour (neighbour); - - return GNUNET_OK; -} - - -/** - * Free DV route entry. - * - * @param cls NULL - * @param pid unused - * @param value a `struct DistanceVector` - * @return #GNUNET_OK (always) - */ -static int -free_dv_routes_cb (void *cls, - const struct GNUNET_PeerIdentity *pid, - void *value) -{ - struct DistanceVector *dv = value; - - (void) cls; - (void) pid; - free_dv_route (dv); - - return GNUNET_OK; -} - - -/** - * Free validation state. - * - * @param cls NULL - * @param pid unused - * @param value a `struct ValidationState` - * @return #GNUNET_OK (always) - */ -static int -free_validation_state_cb (void *cls, - const struct GNUNET_PeerIdentity *pid, - void *value) -{ - struct ValidationState *vs = value; - - (void) cls; - (void) pid; - free_validation_state (vs); - return GNUNET_OK; -} - - -/** - * Free pending acknowledgement. - * - * @param cls NULL - * @param key unused - * @param value a `struct PendingAcknowledgement` - * @return #GNUNET_OK (always) - */ -static int -free_pending_ack_cb (void *cls, const struct GNUNET_Uuid *key, void *value) -{ - struct PendingAcknowledgement *pa = value; - - (void) cls; - (void) key; - free_pending_acknowledgement (pa); - return GNUNET_OK; -} - - -/** - * Free acknowledgement cummulator. - * - * @param cls NULL - * @param pid unused - * @param value a `struct AcknowledgementCummulator` - * @return #GNUNET_OK (always) - */ -static int -free_ack_cummulator_cb (void *cls, - const struct GNUNET_PeerIdentity *pid, - void *value) -{ - struct AcknowledgementCummulator *ac = value; - - (void) cls; - (void) pid; - GNUNET_SCHEDULER_cancel (ac->task); - GNUNET_free (ac); - return GNUNET_OK; -} - - -/** - * Function called when the service shuts down. Unloads our plugins - * and cancels pending validations. - * - * @param cls closure, unused - */ -static void -do_shutdown (void *cls) -{ - struct LearnLaunchEntry *lle; - - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "shutdown logic\n"); - (void) cls; - GNUNET_CONTAINER_multipeermap_iterate (neighbours, - &free_neighbour_cb, NULL); - if (NULL != validation_task) - { - GNUNET_SCHEDULER_cancel (validation_task); - validation_task = NULL; - } - if (NULL != dvlearn_task) - { - GNUNET_SCHEDULER_cancel (dvlearn_task); - dvlearn_task = NULL; - } - if (NULL != GST_stats) - { - GNUNET_STATISTICS_destroy (GST_stats, GNUNET_NO); - GST_stats = NULL; - } - if (NULL != GST_my_hello) - { - GNUNET_HELLO_builder_free (GST_my_hello); - GST_my_hello = NULL; - } - if (NULL != GST_my_private_key) - { - GNUNET_free (GST_my_private_key); - GST_my_private_key = NULL; - } - GNUNET_CONTAINER_multipeermap_iterate (ack_cummulators, - &free_ack_cummulator_cb, - NULL); - GNUNET_CONTAINER_multipeermap_destroy (ack_cummulators); - ack_cummulators = NULL; - GNUNET_CONTAINER_multiuuidmap_iterate (pending_acks, - &free_pending_ack_cb, - NULL); - GNUNET_CONTAINER_multiuuidmap_destroy (pending_acks); - pending_acks = NULL; - GNUNET_break (0 == GNUNET_CONTAINER_multipeermap_size (neighbours)); - GNUNET_CONTAINER_multipeermap_destroy (neighbours); - neighbours = NULL; - GNUNET_break (0 == GNUNET_CONTAINER_multipeermap_size (links)); - GNUNET_CONTAINER_multipeermap_destroy (links); - links = NULL; - GNUNET_CONTAINER_multipeermap_iterate (backtalkers, - &free_backtalker_cb, - NULL); - GNUNET_CONTAINER_multipeermap_destroy (backtalkers); - backtalkers = NULL; - GNUNET_CONTAINER_multipeermap_iterate (validation_map, - &free_validation_state_cb, - NULL); - GNUNET_CONTAINER_multipeermap_destroy (validation_map); - validation_map = NULL; - while (NULL != ir_head) - free_incoming_request (ir_head); - GNUNET_assert (0 == ir_total); - while (NULL != (lle = lle_head)) - { - GNUNET_CONTAINER_DLL_remove (lle_head, lle_tail, lle); - GNUNET_free (lle); - } - if (NULL != peerstore) - { - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "Disconnecting from PEERSTORE service\n"); - GNUNET_PEERSTORE_disconnect (peerstore, GNUNET_NO); - peerstore = NULL; - } - GNUNET_CONTAINER_multishortmap_destroy (dvlearn_map); - dvlearn_map = NULL; - GNUNET_CONTAINER_heap_destroy (validation_heap); - validation_heap = NULL; - GNUNET_CONTAINER_multipeermap_iterate (dv_routes, &free_dv_routes_cb, NULL); - GNUNET_CONTAINER_multipeermap_destroy (dv_routes); - dv_routes = NULL; - GNUNET_SCHEDULER_shutdown (); -} - - -static void -shutdown_task (void *cls) -{ - in_shutdown = GNUNET_YES; - - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "Shutdown task executed\n"); - if (NULL != clients_head) - { - for (struct TransportClient *tc = clients_head; NULL != tc; tc = tc->next) - { - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - "client still connected: %u\n", - tc->type); - } - } - else - do_shutdown (cls); - -} - - -/** - * Initiate transport service. - * - * @param cls closure - * @param c configuration to use - * @param service the initialized service - */ -static void -run (void *cls, - const struct GNUNET_CONFIGURATION_Handle *c, - struct GNUNET_SERVICE_Handle *service) -{ - (void) cls; - (void) service; - /* setup globals */ - hello_mono_time = GNUNET_TIME_absolute_get_monotonic (c); - in_shutdown = GNUNET_NO; - GST_cfg = c; - backtalkers = GNUNET_CONTAINER_multipeermap_create (16, GNUNET_YES); - pending_acks = GNUNET_CONTAINER_multiuuidmap_create (32768, GNUNET_YES); - ack_cummulators = GNUNET_CONTAINER_multipeermap_create (256, GNUNET_YES); - neighbours = GNUNET_CONTAINER_multipeermap_create (1024, GNUNET_YES); - links = GNUNET_CONTAINER_multipeermap_create (512, GNUNET_YES); - dv_routes = GNUNET_CONTAINER_multipeermap_create (1024, GNUNET_YES); - dvlearn_map = GNUNET_CONTAINER_multishortmap_create (2 * MAX_DV_LEARN_PENDING, - GNUNET_YES); - validation_map = GNUNET_CONTAINER_multipeermap_create (1024, GNUNET_YES); - validation_heap = - GNUNET_CONTAINER_heap_create (GNUNET_CONTAINER_HEAP_ORDER_MIN); - GST_my_private_key = - GNUNET_CRYPTO_eddsa_key_create_from_configuration (GST_cfg); - if (NULL == GST_my_private_key) - { - GNUNET_log ( - GNUNET_ERROR_TYPE_ERROR, - _ ( - "Transport service is lacking key configuration settings. Exiting.\n")); - GNUNET_SCHEDULER_shutdown (); - return; - } - GNUNET_CRYPTO_eddsa_key_get_public (GST_my_private_key, - &GST_my_identity.public_key); - GNUNET_log (GNUNET_ERROR_TYPE_INFO, - "My identity is `%s'\n", - GNUNET_i2s_full (&GST_my_identity)); - GST_my_hello = GNUNET_HELLO_builder_new (&GST_my_identity); - GST_stats = GNUNET_STATISTICS_create ("transport", GST_cfg); - GNUNET_SCHEDULER_add_shutdown (&shutdown_task, NULL); - peerstore = GNUNET_PEERSTORE_connect (GST_cfg); - if (NULL == peerstore) - { - GNUNET_break (0); - GNUNET_SCHEDULER_shutdown (); - return; - } -} - - -/** - * Define "main" method using service macro. - */ -GNUNET_SERVICE_MAIN ( - "transport", - GNUNET_SERVICE_OPTION_SOFT_SHUTDOWN, - &run, - &client_connect_cb, - &client_disconnect_cb, - NULL, - /* communication with applications */ - GNUNET_MQ_hd_fixed_size (suggest, - GNUNET_MESSAGE_TYPE_TRANSPORT_SUGGEST, - struct ExpressPreferenceMessage, - NULL), - GNUNET_MQ_hd_fixed_size (suggest_cancel, - GNUNET_MESSAGE_TYPE_TRANSPORT_SUGGEST_CANCEL, - struct ExpressPreferenceMessage, - NULL), - GNUNET_MQ_hd_var_size (request_hello_validation, - GNUNET_MESSAGE_TYPE_TRANSPORT_REQUEST_HELLO_VALIDATION, - struct RequestHelloValidationMessage, - NULL), - /* communication with core */ - GNUNET_MQ_hd_fixed_size (client_start, - GNUNET_MESSAGE_TYPE_TRANSPORT_START, - struct StartMessage, - NULL), - GNUNET_MQ_hd_var_size (client_send, - GNUNET_MESSAGE_TYPE_TRANSPORT_SEND, - struct OutboundMessage, - NULL), - GNUNET_MQ_hd_fixed_size (client_recv_ok, - GNUNET_MESSAGE_TYPE_TRANSPORT_RECV_OK, - struct RecvOkMessage, - NULL), - /* communication with communicators */ - GNUNET_MQ_hd_var_size (communicator_available, - GNUNET_MESSAGE_TYPE_TRANSPORT_NEW_COMMUNICATOR, - struct GNUNET_TRANSPORT_CommunicatorAvailableMessage, - NULL), - GNUNET_MQ_hd_var_size (communicator_backchannel, - GNUNET_MESSAGE_TYPE_TRANSPORT_COMMUNICATOR_BACKCHANNEL, - struct GNUNET_TRANSPORT_CommunicatorBackchannel, - NULL), - GNUNET_MQ_hd_var_size (add_address, - GNUNET_MESSAGE_TYPE_TRANSPORT_ADD_ADDRESS, - struct GNUNET_TRANSPORT_AddAddressMessage, - NULL), - GNUNET_MQ_hd_fixed_size (del_address, - GNUNET_MESSAGE_TYPE_TRANSPORT_DEL_ADDRESS, - struct GNUNET_TRANSPORT_DelAddressMessage, - NULL), - GNUNET_MQ_hd_var_size (incoming_msg, - GNUNET_MESSAGE_TYPE_TRANSPORT_INCOMING_MSG, - struct GNUNET_TRANSPORT_IncomingMessage, - NULL), - GNUNET_MQ_hd_fixed_size (queue_create_ok, - GNUNET_MESSAGE_TYPE_TRANSPORT_QUEUE_CREATE_OK, - struct GNUNET_TRANSPORT_CreateQueueResponse, - NULL), - GNUNET_MQ_hd_fixed_size (queue_create_fail, - GNUNET_MESSAGE_TYPE_TRANSPORT_QUEUE_CREATE_FAIL, - struct GNUNET_TRANSPORT_CreateQueueResponse, - NULL), - GNUNET_MQ_hd_var_size (add_queue_message, - GNUNET_MESSAGE_TYPE_TRANSPORT_QUEUE_SETUP, - struct GNUNET_TRANSPORT_AddQueueMessage, - NULL), - GNUNET_MQ_hd_fixed_size (update_queue_message, - GNUNET_MESSAGE_TYPE_TRANSPORT_QUEUE_UPDATE, - struct GNUNET_TRANSPORT_UpdateQueueMessage, - NULL), - GNUNET_MQ_hd_fixed_size (del_queue_message, - GNUNET_MESSAGE_TYPE_TRANSPORT_QUEUE_TEARDOWN, - struct GNUNET_TRANSPORT_DelQueueMessage, - NULL), - GNUNET_MQ_hd_fixed_size (send_message_ack, - GNUNET_MESSAGE_TYPE_TRANSPORT_SEND_MSG_ACK, - struct GNUNET_TRANSPORT_SendMessageToAck, - NULL), - /* communication with monitors */ - GNUNET_MQ_hd_fixed_size (monitor_start, - GNUNET_MESSAGE_TYPE_TRANSPORT_MONITOR_START, - struct GNUNET_TRANSPORT_MonitorStart, - NULL), - GNUNET_MQ_handler_end ()); - - -/* end of file gnunet-service-transport.c */ diff --git a/src/transport/gnunet-service-transport.h b/src/transport/gnunet-service-transport.h deleted file mode 100644 index ea9e71e4b..000000000 --- a/src/transport/gnunet-service-transport.h +++ /dev/null @@ -1,234 +0,0 @@ -/* - This file is part of GNUnet. - Copyright (C) 2010,2011 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 . - - SPDX-License-Identifier: AGPL3.0-or-later - */ - -/** - * @file transport/gnunet-service-transport.h - * @brief globals - * @author Christian Grothoff - */ -#ifndef GNUNET_SERVICE_TRANSPORT_H -#define GNUNET_SERVICE_TRANSPORT_H - -#include "gnunet_util_lib.h" -#include "gnunet_statistics_service.h" -#include "gnunet_ats_service.h" -#include "gnunet_transport_service.h" - -#define VERBOSE_VALIDATION GNUNET_YES - -/** - * Statistics handle. - */ -extern struct GNUNET_STATISTICS_Handle *GST_stats; - -/** - * Configuration handle. - */ -extern const struct GNUNET_CONFIGURATION_Handle *GST_cfg; - -/** - * Configuration handle. - */ -extern struct GNUNET_PeerIdentity GST_my_identity; - -/** - * Handle to peerinfo service. - */ -extern struct GNUNET_PEERINFO_Handle *GST_peerinfo; - -/** - * Our private key. - */ -extern struct GNUNET_CRYPTO_EddsaPrivateKey GST_my_private_key; - -/** - * ATS handle. - */ -extern struct GNUNET_ATS_SchedulingHandle *GST_ats; - -/** - * ATS connectivity handle. - */ -extern struct GNUNET_ATS_ConnectivityHandle *GST_ats_connect; - -/** - * Interface scanner determines our LAN address range(s). - */ -extern struct GNUNET_NT_InterfaceScanner *GST_is; - - -/** - * Function to call when a peer's address has changed - * - * @param cls closure - * @param peer peer this update is about, - * @param address address, NULL for disconnect notification - */ -typedef void -(*GNUNET_TRANSPORT_NeighbourChangeCallback) (void *cls, - const struct - GNUNET_PeerIdentity *peer, - const struct - GNUNET_HELLO_Address *address, - enum GNUNET_TRANSPORT_PeerState - state, - struct GNUNET_TIME_Absolute - state_timeout, - struct GNUNET_BANDWIDTH_Value32NBO - bandwidth_in, - struct GNUNET_BANDWIDTH_Value32NBO - bandwidth_out); - - -/** - * Continuation called from a blacklist test. - * - * @param cls closure - * @param peer identity of peer that was tested - * @param address address associated with the request - * @param session session associated with the request - * @param result #GNUNET_OK if the connection is allowed, - * #GNUNET_NO if not, - * #GNUNET_SYSERR if operation was aborted - */ -typedef void -(*GST_BlacklistTestContinuation) (void *cls, - const struct GNUNET_PeerIdentity *peer, - const struct GNUNET_HELLO_Address *address, - struct GNUNET_ATS_Session *session, - int result); - - -/** - * Add the given peer to the blacklist (for the given transport). - * - * @param peer peer to blacklist - * @param transport_name transport to blacklist for this peer, NULL for all - */ -void -GST_blacklist_add_peer (const struct GNUNET_PeerIdentity *peer, - const char *transport_name); - - -/** - * Handle to an active blacklist check. - */ -struct GST_BlacklistCheck; - - -/** - * Test if a peer/transport combination is blacklisted. - * - * @param peer the identity of the peer to test - * @param transport_name name of the transport to test, never NULL - * @param cont function to call with result - * @param cont_cls closure for @a cont - * @param address address to pass back to @a cont, can be NULL - * @param session session to pass back to @a cont, can be NULL - * @return handle to the blacklist check, NULL if the decision - * was made instantly and @a cont was already called - */ -struct GST_BlacklistCheck * -GST_blacklist_test_allowed (const struct GNUNET_PeerIdentity *peer, - const char *transport_name, - GST_BlacklistTestContinuation cont, - void *cont_cls, - const struct GNUNET_HELLO_Address *address, - struct GNUNET_ATS_Session *session); - - -/** - * Abort blacklist if @a address and @a session match. - * - * @param address address used to abort matching checks - * @param session session used to abort matching checks - */ -void -GST_blacklist_abort_matching (const struct GNUNET_HELLO_Address *address, - struct GNUNET_ATS_Session *session); - -/** - * Cancel a blacklist check. - * - * @param bc check to cancel - */ -void -GST_blacklist_test_cancel (struct GST_BlacklistCheck *bc); - - -/** - * Function called by the transport for each received message. - * - * @param cls closure, const char* with the name of the plugin we received the message from - * @param address address and (claimed) identity of the other peer - * @param session identifier used for this session (NULL for plugins - * that do not offer bi-directional communication to the sender - * using the same "connection") - * @param message the message, NULL if we only care about - * learning about the delay until we should receive again - * @return how long the plugin should wait until receiving more data - * (plugins that do not support this, can ignore the return value) - */ -struct GNUNET_TIME_Relative -GST_receive_callback (void *cls, - const struct GNUNET_HELLO_Address *address, - struct GNUNET_ATS_Session *session, - const struct GNUNET_MessageHeader *message); - -/** - * Broadcast the given message to all of our clients. - * - * @param msg message to broadcast - * @param may_drop #GNUNET_YES if the message can be dropped / is payload - */ -void -GST_clients_broadcast (const struct GNUNET_MessageHeader *msg, - int may_drop); - - -/** - * Broadcast the new active address to all clients monitoring the peer. - * - * @param peer peer this update is about (never NULL) - * @param address address, NULL on disconnect - * @param state the current state of the peer - * @param state_timeout the time out for the state - */ -void -GST_clients_broadcast_peer_notification (const struct GNUNET_PeerIdentity *peer, - const struct - GNUNET_HELLO_Address *address, - enum GNUNET_TRANSPORT_PeerState state, - struct GNUNET_TIME_Absolute - state_timeout); - - -/** - * Notify all clients about a disconnect, and cancel - * pending SEND_OK messages for this peer. - * - * @param peer peer that disconnected - */ -void -GST_clients_broadcast_disconnect (const struct GNUNET_PeerIdentity *peer); - - -#endif -/* end of file gnunet-service-transport_plugins.h */ diff --git a/src/transport/gnunet-transport-certificate-creation.in b/src/transport/gnunet-transport-certificate-creation.in deleted file mode 100644 index 771422a7a..000000000 --- a/src/transport/gnunet-transport-certificate-creation.in +++ /dev/null @@ -1,158 +0,0 @@ -#!/bin/sh -# -# This shell script will generate an X509 certificate for -# your gnunet-transport HTTPS -# -# The current version partially reuses and recycles -# code from build.sh by NetBSD (although not entirely -# used because it needs debugging): -# -# Copyright (c) 2001-2011 The NetBSD Foundation, Inc. -# All rights reserved. -# -# This code is derived from software contributed to -# The NetBSD Foundation by Todd Vierling and Luke Mewburn. - -# Redistribution and use in source and binary forms, with or -# without modification, are permitted provided that the following -# conditions are met: -# 1. Redistributions of source code must retain the above -# copyright notice, this list of conditions and the following -# disclaimer. -# 2. Redistributions in binary form must reproduce the above -# copyright notice, this list of conditions and the following -# disclaimer in the documentation and/or other materials -# provided with the distribution. - -# THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND -# CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, -# INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF -# MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -# DISCLAIMED. -# IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS BE LIABLE FOR -# ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR -# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, -# PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND -# ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT -# LIABILITY, OR TORT -# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF -# THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY -# OF SUCH DAMAGE. - -progname=${0##*/} - -existence() { - command -v "$1" >/dev/null 2>&1 -} - -setdefaults() -{ - verbosity=0 - runcmd= -} - -statusmsg() -{ - ${runcmd} echo " $@" -} - -infomsg() -{ - if [ x$verbosity = x1 ]; then - statusmsg "INFO: $@" - fi -} - -warningmsg() -{ - statusmsg "WARNING: $@" -} - -errormsg() -{ - statusmsg "ERROR: $@" -} - -linemsg() -{ - statusmsg "=========================================" -} - - -usage() -{ - if [ -n "$*" ]; then - echo "" - echo "${progname}: $*" - fi - cat <<_usage_ - -Usage: ${progname} [-hv] [-c FILE] [...] - -Options: - -c FILE Use the configuration file FILE. - -h Print this help message. - -v Print the version and exit. - -V be verbose - -_usage_ - exit 1 -} - - -generate_cert_key() -{ - echo "" - infomsg "Generating Cert and Key" - - CERTTOOL="" - GNUTLS_CA_TEMPLATE=@PKGDATADIRECTORY@/gnunet-gns-proxy-ca.template - OPENSSL=0 - if test -x $(existence gnutls-certtool) - #if test -z "`gnutls-certtool --version`" > /dev/null - then - if test -z "`certtool --version | grep gnutls`" > /dev/null - then - warningmsg "'gnutls-certtool' or 'certtool' command not found. Trying openssl." - # if test -z "`openssl version`" > /dev/null - if test -x $(existence openssl) - then - OPENSSL=1 - else - warningmsg "Install either gnutls certtool or openssl for certificate generation!" - statusmsg "Cleaning up." - exit 1 - fi - fi - CERTTOOL="certtool" - else - CERTTOOL="gnutls-certtool" - fi - mkdir -p `dirname $KEYFILE` - - if test 1 -eq $OPENSSL - then - openssl genrsa -out $KEYFILE 1024 - openssl req -batch -days 365 -out $CERTFILE -new -x509 -key $KEYFILE - else - $CERTTOOL --generate-privkey --outfile $KEYFILE 2>/dev/null - $CERTTOOL --template $GNUTLS_CA_TEMPLATE --generate-self-signed --load-privkey $KEYFILE --outfile $CERTFILE 2>/dev/null - fi - } - -print_version() -{ - GNUNET_ARM_VERSION=`gnunet-arm -v` - echo $GNUNET_ARM_VERSION -} - -main() -{ - KEYFILE=$1 - CERTFILE=$2 - setdefaults - generate_cert_key -} - -main "$@" diff --git a/src/transport/gnunet-transport.c b/src/transport/gnunet-transport.c deleted file mode 100644 index b5ad43770..000000000 --- a/src/transport/gnunet-transport.c +++ /dev/null @@ -1,1437 +0,0 @@ -/* - This file is part of GNUnet. - Copyright (C) 2011-2014, 2016, 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 . - - SPDX-License-Identifier: AGPL3.0-or-later - */ - -/** - * @file src/transport/gnunet-transport.c - * @brief Tool to help configure, measure and control the transport subsystem. - * @author Christian Grothoff - * @author Nathan Evans - */ -#include "platform.h" -#include "gnunet_util_lib.h" -#include "gnunet_resolver_service.h" -#include "gnunet_protocols.h" -#include "gnunet_transport_service.h" - -/** - * Timeout for a name resolution - */ -#define RESOLUTION_TIMEOUT \ - GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS, 30) - -/** - * Timeout for an operation - */ -#define OP_TIMEOUT GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS, 30) - - -/** - * Context to store name resolutions for valiation - */ -struct ValidationResolutionContext -{ - /** - * Next in DLL - */ - struct ValidationResolutionContext *next; - - /** - * Previous in DLL - */ - struct ValidationResolutionContext *prev; - - /** - * Address to resolve - */ - struct GNUNET_HELLO_Address *addrcp; - - /** - * Time of last validation - */ - struct GNUNET_TIME_Absolute last_validation; - - /** - * Address is valid until - */ - struct GNUNET_TIME_Absolute valid_until; - - /** - * Time of next validation - */ - struct GNUNET_TIME_Absolute next_validation; - - /** - * Transport conversion handle - */ - struct GNUNET_TRANSPORT_AddressToStringContext *asc; - - /** - * plugin name - */ - char *transport; - - /** - * was the entry printed - */ - int printed; -}; - -/** - * Struct to store information about peers in monitor mode - */ -struct MonitoredPeer -{ - /** - * State of the peer - */ - enum GNUNET_TRANSPORT_PeerState state; - - /** - * Timeout - */ - struct GNUNET_TIME_Absolute state_timeout; - - /** - * The address to convert - */ - struct GNUNET_HELLO_Address *address; -}; - -/** - * Context to store name resolutions for valiation - */ -struct PeerResolutionContext -{ - /** - * Next in DLL - */ - struct PeerResolutionContext *next; - - /** - * Prev in DLL - */ - struct PeerResolutionContext *prev; - - /** - * address to resolve - */ - struct GNUNET_HELLO_Address *addrcp; - - /** - * transport conversiion context - */ - struct GNUNET_TRANSPORT_AddressToStringContext *asc; - - /** - * peer state - */ - enum GNUNET_TRANSPORT_PeerState state; - - /** - * state timeout - */ - struct GNUNET_TIME_Absolute state_timeout; - - /** - * transport plugin - */ - char *transport; - - /** - * was the entry printed - */ - int printed; -}; - - -/** - * Benchmarking block size in KB - */ -#define BLOCKSIZE 4 - -/** - * Handle to transport service. - */ -static struct GNUNET_TRANSPORT_CoreHandle *handle; - -/** - * Configuration handle - */ -static struct GNUNET_CONFIGURATION_Handle *cfg; - -/** - * Blacklisting handle - */ -struct GNUNET_TRANSPORT_Blacklist *blacklist; - -/** - * Option -s. - */ -static int benchmark_send; - -/** - * Option -b. - */ -static int benchmark_receive; - -/** - * Option -l. - */ -static int benchmark_receive; - -/** - * Option -i. - */ -static int iterate_connections; - -/** - * Option -a. - */ -static int iterate_all; - -/** - * Option -c. - */ -static int monitor_connects; - -/** - * Option -m. - */ -static int monitor_connections; - -/** - * Option -P. - */ -static int monitor_plugins; - -/** - * Option -D. - */ -static int do_disconnect; - -/** - * Option -n. - */ -static int numeric; - -/** - * Global return value (0 success). - */ -static int ret; - -/** - * Current number of connections in monitor mode - */ -static int monitor_connect_counter; - -/** - * Number of bytes of traffic we received so far. - */ -static unsigned long long traffic_received; - -/** - * Number of bytes of traffic we sent so far. - */ -static unsigned long long traffic_sent; - -/** - * Starting time of transmitting/receiving data. - */ -static struct GNUNET_TIME_Absolute start_time; - -/** - * Map storing information about monitored peers - */ -static struct GNUNET_CONTAINER_MultiPeerMap *monitored_peers; - -/** - * Map storing information about monitored plugins's sessions. - */ -static struct GNUNET_CONTAINER_MultiPeerMap *monitored_plugins; - -/** - * Handle if we are monitoring peers at the transport level. - */ -static struct GNUNET_TRANSPORT_PeerMonitoringContext *pic; - -/** - * Handle if we are monitoring plugin session activity. - */ -static struct GNUNET_TRANSPORT_PluginMonitor *pm; - -/** - * Identity of the peer we transmit to / connect to. - * ('-p' command-line option). - */ -static struct GNUNET_PeerIdentity pid; - -/** - * Task for operation timeout - */ -static struct GNUNET_SCHEDULER_Task *op_timeout; - -/** - * Selected level of verbosity. - */ -static unsigned int verbosity; - -/** - * Resolver process handle. - */ -struct GNUNET_OS_Process *resolver; - -/** - * Number of address resolutions pending - */ -static unsigned int address_resolutions; - -/** - * DLL: head of validation resolution entries - */ -static struct ValidationResolutionContext *vc_head; - -/** - * DLL: tail of validation resolution entries - */ -static struct ValidationResolutionContext *vc_tail; - -/** - * DLL: head of resolution entries - */ -static struct PeerResolutionContext *rc_head; - -/** - * DLL: head of resolution entries - */ -static struct PeerResolutionContext *rc_tail; - - -/** - * Function called to release data stored in the #monitored_peers map. - * - * @param cls unused - * @param key the peer identity - * @param value a `struct MonitoredPeer` to release - * @return #GNUNET_OK (continue to iterate) - */ -static int -destroy_it (void *cls, const struct GNUNET_PeerIdentity *key, void *value) -{ - struct MonitoredPeer *m = value; - - GNUNET_assert ( - GNUNET_OK == - GNUNET_CONTAINER_multipeermap_remove (monitored_peers, key, value)); - GNUNET_free (m->address); - GNUNET_free (value); - return GNUNET_OK; -} - - -/** - * Task run in monitor mode when the user presses CTRL-C to abort. - * Stops monitoring activity. - * - * @param cls NULL - */ -static void -shutdown_task (void *cls) -{ - struct GNUNET_TIME_Relative duration; - struct ValidationResolutionContext *cur; - struct ValidationResolutionContext *next; - struct PeerResolutionContext *rc; - - if (NULL != op_timeout) - { - GNUNET_SCHEDULER_cancel (op_timeout); - op_timeout = NULL; - } - if (NULL != pic) - { - GNUNET_TRANSPORT_monitor_peers_cancel (pic); - pic = NULL; - } - if (NULL != pm) - { - GNUNET_TRANSPORT_monitor_plugins_cancel (pm); - pm = NULL; - } - - next = vc_head; - for (cur = next; NULL != cur; cur = next) - { - next = cur->next; - - GNUNET_TRANSPORT_address_to_string_cancel (cur->asc); - GNUNET_CONTAINER_DLL_remove (vc_head, vc_tail, cur); - GNUNET_free (cur->transport); - GNUNET_HELLO_address_free (cur->addrcp); - GNUNET_free (cur); - } - while (NULL != (rc = rc_head)) - { - GNUNET_CONTAINER_DLL_remove (rc_head, rc_tail, rc); - GNUNET_TRANSPORT_address_to_string_cancel (rc->asc); - GNUNET_free (rc->transport); - GNUNET_free (rc->addrcp); - GNUNET_free (rc); - } - if (NULL != handle) - { - GNUNET_TRANSPORT_core_disconnect (handle); - handle = NULL; - } - if (benchmark_send) - { - duration = GNUNET_TIME_absolute_get_duration (start_time); - fprintf (stdout, - _ ("Transmitted %llu bytes/s (%llu bytes in %s)\n"), - 1000LL * 1000LL * traffic_sent / (1 + duration.rel_value_us), - traffic_sent, - GNUNET_STRINGS_relative_time_to_string (duration, GNUNET_YES)); - } - if (benchmark_receive) - { - duration = GNUNET_TIME_absolute_get_duration (start_time); - fprintf (stdout, - _ ("Received %llu bytes/s (%llu bytes in %s)\n"), - 1000LL * 1000LL * traffic_received / (1 + duration.rel_value_us), - traffic_received, - GNUNET_STRINGS_relative_time_to_string (duration, GNUNET_YES)); - } - - if (NULL != monitored_peers) - { - GNUNET_CONTAINER_multipeermap_iterate (monitored_peers, &destroy_it, NULL); - GNUNET_CONTAINER_multipeermap_destroy (monitored_peers); - monitored_peers = NULL; - } - if (NULL != monitored_plugins) - { - GNUNET_break (0 == GNUNET_CONTAINER_multipeermap_size (monitored_plugins)); - GNUNET_CONTAINER_multipeermap_destroy (monitored_plugins); - monitored_plugins = NULL; - } - if (NULL != blacklist) - { - GNUNET_TRANSPORT_blacklist_cancel (blacklist); - blacklist = NULL; - ret = 0; - } -} - - -/** - * We are done, shut down. - */ -static void -operation_timeout (void *cls) -{ - struct PeerResolutionContext *cur; - struct PeerResolutionContext *next; - - op_timeout = NULL; - if ((benchmark_send) || (benchmark_receive)) - { - fprintf (stdout, _ ("Failed to connect to `%s'\n"), GNUNET_i2s_full (&pid)); - GNUNET_SCHEDULER_shutdown (); - ret = 1; - return; - } - if (iterate_connections) - { - next = rc_head; - while (NULL != (cur = next)) - { - next = cur->next; - fprintf (stdout, - _ ("Failed to resolve address for peer `%s'\n"), - GNUNET_i2s (&cur->addrcp->peer)); - - GNUNET_CONTAINER_DLL_remove (rc_head, rc_tail, cur); - GNUNET_TRANSPORT_address_to_string_cancel (cur->asc); - GNUNET_free (cur->transport); - GNUNET_free (cur->addrcp); - GNUNET_free (cur); - } - fprintf (stdout, - "%s", - _ ("Failed to list connections, timeout occurred\n")); - GNUNET_SCHEDULER_shutdown (); - ret = 1; - return; - } -} - - -/** - * Function called to notify a client about the socket - * begin ready to queue more data. Sends another message. - * - * @param cls closure with the message queue - */ -static void -do_send (void *cls) -{ - struct GNUNET_MQ_Handle *mq = cls; - struct GNUNET_MessageHeader *m; - struct GNUNET_MQ_Envelope *env; - - env = GNUNET_MQ_msg_extra (m, BLOCKSIZE * 1024, GNUNET_MESSAGE_TYPE_DUMMY); - memset (&m[1], 52, BLOCKSIZE * 1024 - sizeof(struct GNUNET_MessageHeader)); - traffic_sent += BLOCKSIZE * 1024; - GNUNET_MQ_notify_sent (env, &do_send, mq); - if (verbosity > 0) - fprintf (stdout, - _ ("Transmitting %u bytes\n"), - (unsigned int) BLOCKSIZE * 1024); - GNUNET_MQ_send (mq, env); -} - - -/** - * Function called to notify transport users that another - * peer connected to us. - * - * @param cls closure - * @param peer the peer that connected - * @param mq message queue for sending to @a peer - */ -static void * -notify_connect (void *cls, - const struct GNUNET_PeerIdentity *peer, - struct GNUNET_MQ_Handle *mq) -{ - if (0 != memcmp (&pid, peer, sizeof(struct GNUNET_PeerIdentity))) - return NULL; - ret = 0; - if (! benchmark_send) - return NULL; - if (NULL != op_timeout) - { - GNUNET_SCHEDULER_cancel (op_timeout); - op_timeout = NULL; - } - if (verbosity > 0) - fprintf ( - stdout, - _ ( - "Successfully connected to `%s', starting to send benchmark data in %u Kb blocks\n"), - GNUNET_i2s (peer), - BLOCKSIZE); - start_time = GNUNET_TIME_absolute_get (); - do_send (mq); - return mq; -} - - -/** - * Function called to notify transport users that another - * peer disconnected from us. - * - * @param cls closure - * @param peer the peer that disconnected - * @param internal_cls what we returned from #notify_connect() - */ -static void -notify_disconnect (void *cls, - const struct GNUNET_PeerIdentity *peer, - void *internal_cls) -{ - if (0 != memcmp (&pid, peer, sizeof(struct GNUNET_PeerIdentity))) - return; - if (NULL == internal_cls) - return; /* not about target peer */ - if (! benchmark_send) - return; /* not transmitting */ - fprintf (stdout, - _ ("Disconnected from peer `%s' while benchmarking\n"), - GNUNET_i2s (&pid)); -} - - -/** - * Function called to notify transport users that another - * peer connected to us. - * - * @param cls closure - * @param peer the peer that connected - * @param mq for sending messages to @a peer - * @return NULL - */ -static void * -monitor_notify_connect (void *cls, - const struct GNUNET_PeerIdentity *peer, - struct GNUNET_MQ_Handle *mq) -{ - struct GNUNET_TIME_Absolute now = GNUNET_TIME_absolute_get (); - const char *now_str = GNUNET_STRINGS_absolute_time_to_string (now); - - monitor_connect_counter++; - fprintf (stdout, - _ ("%24s: %-17s %4s (%u connections in total)\n"), - now_str, - _ ("Connected to"), - GNUNET_i2s (peer), - monitor_connect_counter); - return NULL; -} - - -/** - * Function called to notify transport users that another - * peer disconnected from us. - * - * @param cls closure - * @param peer the peer that disconnected - * @param internal_cls what we returned from #monitor_notify_connect() - */ -static void -monitor_notify_disconnect (void *cls, - const struct GNUNET_PeerIdentity *peer, - void *internal_cls) -{ - struct GNUNET_TIME_Absolute now = GNUNET_TIME_absolute_get (); - const char *now_str = GNUNET_STRINGS_absolute_time_to_string (now); - - GNUNET_assert (monitor_connect_counter > 0); - monitor_connect_counter--; - - fprintf (stdout, - _ ("%24s: %-17s %4s (%u connections in total)\n"), - now_str, - _ ("Disconnected from"), - GNUNET_i2s (peer), - monitor_connect_counter); -} - - -/** - * Function called by the transport for each received message. - * - * @param cls closure - * @param message the message - * @return #GNUNET_OK - */ -static int -check_dummy (void *cls, const struct GNUNET_MessageHeader *message) -{ - return GNUNET_OK; /* all messages are fine */ -} - - -/** - * Function called by the transport for each received message. - * - * @param cls closure - * @param message the message - */ -static void -handle_dummy (void *cls, const struct GNUNET_MessageHeader *message) -{ - if (! benchmark_receive) - return; - if (verbosity > 0) - fprintf (stdout, - _ ("Received %u bytes\n"), - (unsigned int) ntohs (message->size)); - if (0 == traffic_received) - start_time = GNUNET_TIME_absolute_get (); - traffic_received += ntohs (message->size); -} - - -/** - * Convert address to a printable format. - * - * @param address the address - * @param numeric #GNUNET_YES to convert to numeric format, #GNUNET_NO - * to try to use reverse DNS - * @param state state the peer is in - * @param state_timeout when will the peer's state expire - */ -static void -resolve_peer_address (const struct GNUNET_HELLO_Address *address, - int numeric, - enum GNUNET_TRANSPORT_PeerState state, - struct GNUNET_TIME_Absolute state_timeout); - - -static void -print_info (const struct GNUNET_PeerIdentity *id, - const char *transport, - const char *addr, - enum GNUNET_TRANSPORT_PeerState state, - struct GNUNET_TIME_Absolute state_timeout) -{ - if (((GNUNET_YES == iterate_connections) && (GNUNET_YES == iterate_all)) || - (GNUNET_YES == monitor_connections)) - { - fprintf (stdout, - _ ("Peer `%s': %s %s in state `%s' until %s\n"), - GNUNET_i2s (id), - (NULL == transport) ? "" : transport, - (NULL == transport) ? "" : addr, - GNUNET_TRANSPORT_ps2s (state), - GNUNET_STRINGS_absolute_time_to_string (state_timeout)); - } - else if ((GNUNET_YES == iterate_connections) && - (GNUNET_TRANSPORT_is_connected (state))) - { - /* Only connected peers, skip state */ - fprintf (stdout, - _ ("Peer `%s': %s %s\n"), - GNUNET_i2s (id), - transport, - addr); - } -} - - -/** - * Function called with a textual representation of an address. This - * function will be called several times with different possible - * textual representations, and a last time with @a address being NULL - * to signal the end of the iteration. Note that @a address NULL - * always is the last call, regardless of the value in @a res. - * - * @param cls closure - * @param address NULL on end of iteration, - * otherwise 0-terminated printable UTF-8 string, - * in particular an empty string if @a res is #GNUNET_NO - * @param res result of the address to string conversion: - * if #GNUNET_OK: conversion successful - * if #GNUNET_NO: address was invalid (or not supported) - * if #GNUNET_SYSERR: communication error (IPC error) - */ -static void -process_peer_string (void *cls, const char *address, int res) -{ - struct PeerResolutionContext *rc = cls; - - if (NULL != address) - { - if (GNUNET_SYSERR == res) - { - fprintf ( - stderr, - "Failed to convert address for peer `%s' plugin `%s' length %u to string \n", - GNUNET_i2s (&rc->addrcp->peer), - rc->addrcp->transport_name, - (unsigned int) rc->addrcp->address_length); - print_info (&rc->addrcp->peer, - rc->transport, - NULL, - rc->state, - rc->state_timeout); - rc->printed = GNUNET_YES; - return; - } - if (GNUNET_OK == res) - { - print_info (&rc->addrcp->peer, - rc->transport, - address, - rc->state, - rc->state_timeout); - rc->printed = GNUNET_YES; - return; /* Wait for done call */ - } - /* GNUNET_NO == res: ignore, was simply not supported */ - return; - } - /* NULL == address, last call, we are done */ - - rc->asc = NULL; - GNUNET_assert (address_resolutions > 0); - address_resolutions--; - if (GNUNET_NO == rc->printed) - { - if (numeric == GNUNET_NO) - { - /* Failed to resolve address, try numeric lookup - (note: this should not be needed, as transport - should fallback to numeric conversion if DNS takes - too long) */ - resolve_peer_address (rc->addrcp, - GNUNET_YES, - rc->state, - rc->state_timeout); - } - else - { - print_info (&rc->addrcp->peer, - rc->transport, - NULL, - rc->state, - rc->state_timeout); - } - } - GNUNET_free (rc->transport); - GNUNET_free (rc->addrcp); - GNUNET_CONTAINER_DLL_remove (rc_head, rc_tail, rc); - GNUNET_free (rc); - if ((0 == address_resolutions) && (iterate_connections)) - { - if (NULL != op_timeout) - { - GNUNET_SCHEDULER_cancel (op_timeout); - op_timeout = NULL; - } - ret = 0; - GNUNET_SCHEDULER_shutdown (); - } -} - - -/** - * Convert address to a printable format and print it - * together with the given state data. - * - * @param address the address - * @param numeric #GNUNET_YES to convert to numeric format, #GNUNET_NO - * to try to use reverse DNS - * @param state state the peer is in - * @param state_timeout when will the peer's state expire - */ -static void -resolve_peer_address (const struct GNUNET_HELLO_Address *address, - int numeric, - enum GNUNET_TRANSPORT_PeerState state, - struct GNUNET_TIME_Absolute state_timeout) -{ - struct PeerResolutionContext *rc; - - rc = GNUNET_new (struct PeerResolutionContext); - GNUNET_CONTAINER_DLL_insert (rc_head, rc_tail, rc); - address_resolutions++; - rc->transport = GNUNET_strdup (address->transport_name); - rc->addrcp = GNUNET_HELLO_address_copy (address); - rc->printed = GNUNET_NO; - rc->state = state; - rc->state_timeout = state_timeout; - /* Resolve address to string */ - rc->asc = GNUNET_TRANSPORT_address_to_string (cfg, - address, - numeric, - RESOLUTION_TIMEOUT, - &process_peer_string, - rc); -} - - -/** - * Function called with information about a peers during a one shot iteration - * - * @param cls closure - * @param peer identity of the peer, NULL for final callback when operation done - * @param address binary address used to communicate with this peer, - * NULL on disconnect or when done - * @param state current state this peer is in - * @param state_timeout time out for the current state - */ -static void -process_peer_iteration_cb (void *cls, - const struct GNUNET_PeerIdentity *peer, - const struct GNUNET_HELLO_Address *address, - enum GNUNET_TRANSPORT_PeerState state, - struct GNUNET_TIME_Absolute state_timeout) -{ - if (NULL == peer) - { - /* done */ - pic = NULL; - return; - } - - if ((GNUNET_NO == iterate_all) && - (GNUNET_NO == GNUNET_TRANSPORT_is_connected (state))) - return; /* Display only connected peers */ - - if (NULL != op_timeout) - GNUNET_SCHEDULER_cancel (op_timeout); - op_timeout = - GNUNET_SCHEDULER_add_delayed (OP_TIMEOUT, &operation_timeout, NULL); - - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "Received address for peer `%s': %s\n", - GNUNET_i2s (peer), - address ? address->transport_name : ""); - - if (NULL != address) - resolve_peer_address (address, numeric, state, state_timeout); - else - print_info (peer, NULL, NULL, state, state_timeout); -} - - -/** - * Context for address resolution by #plugin_monitoring_cb(). - */ -struct PluginMonitorAddress -{ - /** - * Ongoing resolution request. - */ - struct GNUNET_TRANSPORT_AddressToStringContext *asc; - - /** - * Resolved address as string. - */ - char *str; - - /** - * Last event we got and did not yet print because - * @e str was NULL (address not yet resolved). - */ - struct GNUNET_TRANSPORT_SessionInfo si; -}; - - -/** - * Print information about a plugin monitoring event. - * - * @param addr out internal context - * @param info the monitoring information - */ -static void -print_plugin_event_info (struct PluginMonitorAddress *addr, - const struct GNUNET_TRANSPORT_SessionInfo *info) -{ - const char *state; - - switch (info->state) - { - case GNUNET_TRANSPORT_SS_INIT: - state = "INIT"; - break; - - case GNUNET_TRANSPORT_SS_HANDSHAKE: - state = "HANDSHAKE"; - break; - - case GNUNET_TRANSPORT_SS_UP: - state = "UP"; - break; - - case GNUNET_TRANSPORT_SS_UPDATE: - state = "UPDATE"; - break; - - case GNUNET_TRANSPORT_SS_DONE: - state = "DONE"; - break; - - default: - state = "UNKNOWN"; - break; - } - fprintf (stdout, - "%s: state %s timeout in %s @ %s%s\n", - GNUNET_i2s (&info->address->peer), - state, - GNUNET_STRINGS_relative_time_to_string ( - GNUNET_TIME_absolute_get_remaining (info->session_timeout), - GNUNET_YES), - addr->str, - (info->is_inbound == GNUNET_YES) ? " (INBOUND)" : ""); - fprintf (stdout, - "%s: queue has %3u messages and %6u bytes\n", - GNUNET_i2s (&info->address->peer), - info->num_msg_pending, - info->num_bytes_pending); - if (0 != - GNUNET_TIME_absolute_get_remaining (info->receive_delay).rel_value_us) - fprintf (stdout, - "%s: receiving blocked until %s\n", - GNUNET_i2s (&info->address->peer), - GNUNET_STRINGS_absolute_time_to_string (info->receive_delay)); -} - - -/** - * Function called with a textual representation of an address. This - * function will be called several times with different possible - * textual representations, and a last time with @a address being NULL - * to signal the end of the iteration. Note that @a address NULL - * always is the last call, regardless of the value in @a res. - * - * @param cls closure - * @param address NULL on end of iteration, - * otherwise 0-terminated printable UTF-8 string, - * in particular an empty string if @a res is #GNUNET_NO - * @param res result of the address to string conversion: - * if #GNUNET_OK: conversion successful - * if #GNUNET_NO: address was invalid (or not supported) - * if #GNUNET_SYSERR: communication error (IPC error) - */ -static void -address_cb (void *cls, const char *address, int res) -{ - struct PluginMonitorAddress *addr = cls; - - if (NULL == address) - { - addr->asc = NULL; - return; - } - if (NULL != addr->str) - return; - addr->str = GNUNET_strdup (address); - print_plugin_event_info (addr, &addr->si); -} - - -/** - * Function called by the plugin with information about the - * current sessions managed by the plugin (for monitoring). - * - * @param cls closure (NULL) - * @param session session handle this information is about, - * NULL to indicate that we are "in sync" (initial - * iteration complete) - * @param session_ctx storage location where the application - * can store data; will point to NULL on #GNUNET_TRANSPORT_SS_INIT, - * and must be reset to NULL on #GNUNET_TRANSPORT_SS_DONE - * @param info information about the state of the session, - * NULL if @a session is also NULL and we are - * merely signalling that the initial iteration is over; - * NULL with @a session being non-NULL if the monitor - * was being cancelled while sessions were active - */ -static void -plugin_monitoring_cb (void *cls, - struct GNUNET_TRANSPORT_PluginSession *session, - void **session_ctx, - const struct GNUNET_TRANSPORT_SessionInfo *info) -{ - struct PluginMonitorAddress *addr; - - if ((NULL == info) && (NULL == session)) - return; /* in sync with transport service */ - addr = *session_ctx; - if (NULL == info) - { - if (NULL != addr) - { - if (NULL != addr->asc) - { - GNUNET_TRANSPORT_address_to_string_cancel (addr->asc); - addr->asc = NULL; - } - GNUNET_free (addr->str); - GNUNET_free (addr); - *session_ctx = NULL; - } - return; /* shutdown */ - } - if (0 != - memcmp (&info->address->peer, &pid, sizeof(struct GNUNET_PeerIdentity))) - return; /* filtered */ - if (NULL == addr) - { - addr = GNUNET_new (struct PluginMonitorAddress); - addr->asc = - GNUNET_TRANSPORT_address_to_string (cfg, - info->address, - numeric, - GNUNET_TIME_UNIT_FOREVER_REL, - &address_cb, - addr); - *session_ctx = addr; - } - if (NULL == addr->str) - addr->si = *info; - else - print_plugin_event_info (addr, info); - if (GNUNET_TRANSPORT_SS_DONE == info->state) - { - if (NULL != addr->asc) - { - GNUNET_TRANSPORT_address_to_string_cancel (addr->asc); - addr->asc = NULL; - } - GNUNET_free (addr->str); - GNUNET_free (addr); - *session_ctx = NULL; - } -} - - -/** - * Function called with information about a peers - * - * @param cls closure, NULL - * @param peer identity of the peer, NULL for final callback when operation done - * @param address binary address used to communicate with this peer, - * NULL on disconnect or when done - * @param state current state this peer is in - * @param state_timeout time out for the current state - */ -static void -process_peer_monitoring_cb (void *cls, - const struct GNUNET_PeerIdentity *peer, - const struct GNUNET_HELLO_Address *address, - enum GNUNET_TRANSPORT_PeerState state, - struct GNUNET_TIME_Absolute state_timeout) -{ - struct MonitoredPeer *m; - - if (NULL == peer) - { - fprintf (stdout, - "%s", - _ ( - "Monitor disconnected from transport service. Reconnecting.\n")); - return; - } - - if (NULL != op_timeout) - GNUNET_SCHEDULER_cancel (op_timeout); - op_timeout = - GNUNET_SCHEDULER_add_delayed (OP_TIMEOUT, &operation_timeout, NULL); - - if (NULL == (m = GNUNET_CONTAINER_multipeermap_get (monitored_peers, peer))) - { - m = GNUNET_new (struct MonitoredPeer); - GNUNET_CONTAINER_multipeermap_put ( - monitored_peers, - peer, - m, - GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_FAST); - } - else - { - if ((m->state == state) && - (m->state_timeout.abs_value_us == state_timeout.abs_value_us) && - (NULL == address) && (NULL == m->address)) - { - return; /* No real change */ - } - if ((m->state == state) && (NULL != address) && (NULL != m->address) && - (0 == GNUNET_HELLO_address_cmp (m->address, address))) - return; /* No real change */ - } - - if (NULL != m->address) - { - GNUNET_free (m->address); - m->address = NULL; - } - if (NULL != address) - m->address = GNUNET_HELLO_address_copy (address); - m->state = state; - m->state_timeout = state_timeout; - - if (NULL != address) - resolve_peer_address (m->address, numeric, m->state, m->state_timeout); - else - print_info (peer, NULL, NULL, m->state, m->state_timeout); -} - - -/** - * Function called with the transport service checking if we - * want to blacklist a peer. Return #GNUNET_SYSERR for the - * peer that we should disconnect from. - * - * @param cls NULL - * @param cpid peer to check blacklisting for - * @return #GNUNET_OK if the connection is allowed, #GNUNET_SYSERR if not - */ -static int -blacklist_cb (void *cls, const struct GNUNET_PeerIdentity *cpid) -{ - if (0 == memcmp (cpid, &pid, sizeof(struct GNUNET_PeerIdentity))) - return GNUNET_SYSERR; - return GNUNET_OK; -} - - -/** - * 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 mycfg configuration - */ -static void -run (void *cls, - char *const *args, - const char *cfgfile, - const struct GNUNET_CONFIGURATION_Handle *mycfg) -{ - static struct GNUNET_PeerIdentity zero_pid; - int counter = 0; - - ret = 1; - - cfg = (struct GNUNET_CONFIGURATION_Handle *) mycfg; - - counter = benchmark_send + benchmark_receive + iterate_connections - + monitor_connections + monitor_connects + do_disconnect - + monitor_plugins; - - if (1 < counter) - { - fprintf ( - stderr, - _ ( - "Multiple operations given. Please choose only one operation: %s, %s, %s, %s, %s, %s %s\n"), - "disconnect", - "benchmark send", - "benchmark receive", - "information", - "monitor", - "events", - "plugins"); - return; - } - if (0 == counter) - { - fprintf ( - stderr, - _ ( - "No operation given. Please choose one operation: %s, %s, %s, %s, %s, %s, %s\n"), - "disconnect", - "benchmark send", - "benchmark receive", - "information", - "monitor", - "events", - "plugins"); - return; - } - - if (do_disconnect) /* -D: Disconnect from peer */ - { - if (0 == memcmp (&zero_pid, &pid, sizeof(pid))) - { - fprintf (stderr, - _ ("Option `%s' makes no sense without option `%s'.\n"), - "-D", - "-p"); - ret = 1; - return; - } - blacklist = GNUNET_TRANSPORT_blacklist (cfg, &blacklist_cb, NULL); - if (NULL == blacklist) - { - fprintf (stderr, - "%s", - _ ( - "Failed to connect to transport service for disconnection\n")); - ret = 1; - return; - } - fprintf (stdout, - "%s", - _ ("Blacklisting request in place, stop with CTRL-C\n")); - } - else if (benchmark_send) /* -s: Benchmark sending */ - { - if (0 == memcmp (&zero_pid, &pid, sizeof(pid))) - { - fprintf (stderr, - _ ("Option `%s' makes no sense without option `%s'.\n"), - "-s", - "-p"); - ret = 1; - return; - } - handle = GNUNET_TRANSPORT_core_connect (cfg, - NULL, - NULL, - NULL, - ¬ify_connect, - ¬ify_disconnect, - NULL); - if (NULL == handle) - { - fprintf (stderr, "%s", _ ("Failed to connect to transport service\n")); - ret = 1; - return; - } - start_time = GNUNET_TIME_absolute_get (); - op_timeout = - GNUNET_SCHEDULER_add_delayed (OP_TIMEOUT, &operation_timeout, NULL); - } - else if (benchmark_receive) /* -b: Benchmark receiving */ - { - struct GNUNET_MQ_MessageHandler handlers[] = - { GNUNET_MQ_hd_var_size (dummy, - GNUNET_MESSAGE_TYPE_DUMMY, - struct GNUNET_MessageHeader, - NULL), - GNUNET_MQ_handler_end () }; - - handle = GNUNET_TRANSPORT_core_connect (cfg, - NULL, - handlers, - NULL, - NULL, - NULL, - NULL); - if (NULL == handle) - { - fprintf (stderr, "%s", _ ("Failed to connect to transport service\n")); - ret = 1; - return; - } - if (verbosity > 0) - fprintf (stdout, "%s", _ ("Starting to receive benchmark data\n")); - start_time = GNUNET_TIME_absolute_get (); - } - else if (iterate_connections) /* -i: List information about peers once */ - { - pic = GNUNET_TRANSPORT_monitor_peers (cfg, - &pid, - GNUNET_YES, - &process_peer_iteration_cb, - (void *) cfg); - op_timeout = - GNUNET_SCHEDULER_add_delayed (OP_TIMEOUT, &operation_timeout, NULL); - } - else if (monitor_connections) /* -m: List information about peers continuously - */ - { - monitored_peers = GNUNET_CONTAINER_multipeermap_create (10, GNUNET_NO); - pic = GNUNET_TRANSPORT_monitor_peers (cfg, - &pid, - GNUNET_NO, - &process_peer_monitoring_cb, - NULL); - } - else if (monitor_plugins) /* -P: List information about plugins continuously - */ - { - monitored_plugins = GNUNET_CONTAINER_multipeermap_create (10, GNUNET_NO); - pm = GNUNET_TRANSPORT_monitor_plugins (cfg, &plugin_monitoring_cb, NULL); - } - else if (monitor_connects) /* -e : Monitor (dis)connect events continuously */ - { - monitor_connect_counter = 0; - handle = GNUNET_TRANSPORT_core_connect (cfg, - NULL, - NULL, - NULL, - &monitor_notify_connect, - &monitor_notify_disconnect, - NULL); - if (NULL == handle) - { - fprintf (stderr, "%s", _ ("Failed to connect to transport service\n")); - ret = 1; - return; - } - ret = 0; - } - else - { - GNUNET_break (0); - return; - } - - GNUNET_SCHEDULER_add_shutdown (&shutdown_task, NULL); -} - - -int -main (int argc, char *const *argv) -{ - int res; - struct GNUNET_GETOPT_CommandLineOption options[] = - { GNUNET_GETOPT_option_flag ( - 'a', - "all", - gettext_noop ( - "print information for all peers (instead of only connected peers)"), - &iterate_all), - GNUNET_GETOPT_option_flag ( - 'b', - "benchmark", - gettext_noop ( - "measure how fast we are receiving data from all peers (until CTRL-C)"), - &benchmark_receive), - GNUNET_GETOPT_option_flag ('D', - "disconnect", - gettext_noop ("disconnect from a peer"), - &do_disconnect), - GNUNET_GETOPT_option_flag ( - 'i', - "information", - gettext_noop ( - "provide information about all current connections (once)"), - &iterate_connections), - GNUNET_GETOPT_option_flag ( - 'm', - "monitor", - gettext_noop ( - "provide information about all current connections (continuously)"), - &monitor_connections), - GNUNET_GETOPT_option_flag ( - 'e', - "events", - gettext_noop ( - "provide information about all connects and disconnect events (continuously)"), - &monitor_connects), - GNUNET_GETOPT_option_flag ('n', - "numeric", - gettext_noop ("do not resolve hostnames"), - &numeric), - GNUNET_GETOPT_option_base32_auto ('p', - "peer", - "PEER", - gettext_noop ("peer identity"), - &pid), - GNUNET_GETOPT_option_flag ('P', - "plugins", - gettext_noop ("monitor plugin sessions"), - &monitor_plugins), - GNUNET_GETOPT_option_flag ( - 's', - "send", - gettext_noop ( - "send data for benchmarking to the other peer (until CTRL-C)"), - &benchmark_send), - GNUNET_GETOPT_option_verbose (&verbosity), - GNUNET_GETOPT_OPTION_END }; - - if (GNUNET_OK != GNUNET_STRINGS_get_utf8_args (argc, argv, &argc, &argv)) - return 2; - - res = - GNUNET_PROGRAM_run (argc, - argv, - "gnunet-transport", - gettext_noop ("Direct access to transport service."), - options, - &run, - NULL); - GNUNET_free_nz ((void *) argv); - if (GNUNET_OK == res) - return ret; - return 1; -} - - -/* end of gnunet-transport.c */ diff --git a/src/transport/ieee80211_radiotap.h b/src/transport/ieee80211_radiotap.h deleted file mode 100644 index 9351da9a5..000000000 --- a/src/transport/ieee80211_radiotap.h +++ /dev/null @@ -1,276 +0,0 @@ -/* - * Copyright (c) 2003, 2004 David Young. All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 3. The name of David Young may not be used to endorse or promote - * products derived from this software without specific prior - * written permission. - * - * THIS SOFTWARE IS PROVIDED BY DAVID YOUNG ``AS IS'' AND ANY - * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, - * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A - * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL DAVID - * YOUNG BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, - * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED - * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND - * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, - * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY - * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY - * OF SUCH DAMAGE. - */ - -/* - * Modifications to fit into the linux IEEE 802.11 stack, - * Mike Kershaw (dragorn@kismetwireless.net) - */ - -#ifndef IEEE80211RADIOTAP_H -#define IEEE80211RADIOTAP_H - -#include -#include -// #include - -/* Base version of the radiotap packet header data */ -#define PKTHDR_RADIOTAP_VERSION 0 - -/* A generic radio capture format is desirable. There is one for - * Linux, but it is neither rigidly defined (there were not even - * units given for some fields) nor easily extensible. - * - * I suggest the following extensible radio capture format. It is - * based on a bitmap indicating which fields are present. - * - * I am trying to describe precisely what the application programmer - * should expect in the following, and for that reason I tell the - * units and origin of each measurement (where it applies), or else I - * use sufficiently weaselly language ("is a monotonically nondecreasing - * function of...") that I cannot set false expectations for lawyerly - * readers. - */ - -/* - * The radio capture header precedes the 802.11 header. - * All data in the header is little endian on all platforms. - */ -struct ieee80211_radiotap_header -{ - u8 it_version; /* Version 0. Only increases - * for drastic changes, - * introduction of compatible - * new fields does not count. - */ - u8 it_pad; - __le16 it_len; /* length of the whole - * header in bytes, including - * it_version, it_pad, - * it_len, and data fields. - */ - __le32 it_present; /* A bitmap telling which - * fields are present. Set bit 31 - * (0x80000000) to extend the - * bitmap by another 32 bits. - * Additional extensions are made - * by setting bit 31. - */ -} __packed; - -/* Name Data type Units - * ---- --------- ----- - * - * IEEE80211_RADIOTAP_TSFT __le64 microseconds - * - * Value in microseconds of the MAC's 64-bit 802.11 Time - * Synchronization Function timer when the first bit of the - * MPDU arrived at the MAC. For received frames, only. - * - * IEEE80211_RADIOTAP_CHANNEL 2 x __le16 MHz, bitmap - * - * Tx/Rx frequency in MHz, followed by flags (see below). - * - * IEEE80211_RADIOTAP_FHSS __le16 see below - * - * For frequency-hopping radios, the hop set (first byte) - * and pattern (second byte). - * - * IEEE80211_RADIOTAP_RATE u8 500kb/s - * - * Tx/Rx data rate - * - * IEEE80211_RADIOTAP_DBM_ANTSIGNAL s8 decibels from - * one milliwatt (dBm) - * - * RF signal power at the antenna, decibel difference from - * one milliwatt. - * - * IEEE80211_RADIOTAP_DBM_ANTNOISE s8 decibels from - * one milliwatt (dBm) - * - * RF noise power at the antenna, decibel difference from one - * milliwatt. - * - * IEEE80211_RADIOTAP_DB_ANTSIGNAL u8 decibel (dB) - * - * RF signal power at the antenna, decibel difference from an - * arbitrary, fixed reference. - * - * IEEE80211_RADIOTAP_DB_ANTNOISE u8 decibel (dB) - * - * RF noise power at the antenna, decibel difference from an - * arbitrary, fixed reference point. - * - * IEEE80211_RADIOTAP_LOCK_QUALITY __le16 unitless - * - * Quality of Barker code lock. Unitless. Monotonically - * nondecreasing with "better" lock strength. Called "Signal - * Quality" in datasheets. (Is there a standard way to measure - * this?) - * - * IEEE80211_RADIOTAP_TX_ATTENUATION __le16 unitless - * - * Transmit power expressed as unitless distance from max - * power set at factory calibration. 0 is max power. - * Monotonically nondecreasing with lower power levels. - * - * IEEE80211_RADIOTAP_DB_TX_ATTENUATION __le16 decibels (dB) - * - * Transmit power expressed as decibel distance from max power - * set at factory calibration. 0 is max power. Monotonically - * nondecreasing with lower power levels. - * - * IEEE80211_RADIOTAP_DBM_TX_POWER s8 decibels from - * one milliwatt (dBm) - * - * Transmit power expressed as dBm (decibels from a 1 milliwatt - * reference). This is the absolute power level measured at - * the antenna port. - * - * IEEE80211_RADIOTAP_FLAGS u8 bitmap - * - * Properties of transmitted and received frames. See flags - * defined below. - * - * IEEE80211_RADIOTAP_ANTENNA u8 antenna index - * - * Unitless indication of the Rx/Tx antenna for this packet. - * The first antenna is antenna 0. - * - * IEEE80211_RADIOTAP_RX_FLAGS __le16 bitmap - * - * Properties of received frames. See flags defined below. - * - * IEEE80211_RADIOTAP_TX_FLAGS __le16 bitmap - * - * Properties of transmitted frames. See flags defined below. - * - * IEEE80211_RADIOTAP_RTS_RETRIES u8 data - * - * Number of rts retries a transmitted frame used. - * - * IEEE80211_RADIOTAP_DATA_RETRIES u8 data - * - * Number of unicast retries a transmitted frame used. - * - */ -enum ieee80211_radiotap_type -{ - IEEE80211_RADIOTAP_TSFT = 0, - IEEE80211_RADIOTAP_FLAGS = 1, - IEEE80211_RADIOTAP_RATE = 2, - IEEE80211_RADIOTAP_CHANNEL = 3, - IEEE80211_RADIOTAP_FHSS = 4, - IEEE80211_RADIOTAP_DBM_ANTSIGNAL = 5, - IEEE80211_RADIOTAP_DBM_ANTNOISE = 6, - IEEE80211_RADIOTAP_LOCK_QUALITY = 7, - IEEE80211_RADIOTAP_TX_ATTENUATION = 8, - IEEE80211_RADIOTAP_DB_TX_ATTENUATION = 9, - IEEE80211_RADIOTAP_DBM_TX_POWER = 10, - IEEE80211_RADIOTAP_ANTENNA = 11, - IEEE80211_RADIOTAP_DB_ANTSIGNAL = 12, - IEEE80211_RADIOTAP_DB_ANTNOISE = 13, - IEEE80211_RADIOTAP_RX_FLAGS = 14, - IEEE80211_RADIOTAP_TX_FLAGS = 15, - IEEE80211_RADIOTAP_RTS_RETRIES = 16, - IEEE80211_RADIOTAP_DATA_RETRIES = 17, - - /* valid in every it_present bitmap, even vendor namespaces */ - IEEE80211_RADIOTAP_RADIOTAP_NAMESPACE = 29, - IEEE80211_RADIOTAP_VENDOR_NAMESPACE = 30, - IEEE80211_RADIOTAP_EXT = 31 -}; - -/* Channel flags. */ -#define IEEE80211_CHAN_TURBO 0x0010 /* Turbo channel */ -#define IEEE80211_CHAN_CCK 0x0020 /* CCK channel */ -#define IEEE80211_CHAN_OFDM 0x0040 /* OFDM channel */ -#define IEEE80211_CHAN_2GHZ 0x0080 /* 2 GHz spectrum channel. */ -#define IEEE80211_CHAN_5GHZ 0x0100 /* 5 GHz spectrum channel */ -#define IEEE80211_CHAN_PASSIVE 0x0200 /* Only passive scan allowed */ -#define IEEE80211_CHAN_DYN 0x0400 /* Dynamic CCK-OFDM channel */ -#define IEEE80211_CHAN_GFSK 0x0800 /* GFSK channel (FHSS PHY) */ - -/* For IEEE80211_RADIOTAP_FLAGS */ -#define IEEE80211_RADIOTAP_F_CFP 0x01 /* sent/received - * during CFP - */ -#define IEEE80211_RADIOTAP_F_SHORTPRE 0x02 /* sent/received - * with short - * preamble - */ -#define IEEE80211_RADIOTAP_F_WEP 0x04 /* sent/received - * with WEP encryption - */ -#define IEEE80211_RADIOTAP_F_FRAG 0x08 /* sent/received - * with fragmentation - */ -#define IEEE80211_RADIOTAP_F_FCS 0x10 /* frame includes FCS */ -#define IEEE80211_RADIOTAP_F_DATAPAD 0x20 /* frame has padding between - * 802.11 header and payload - * (to 32-bit boundary) - */ -#define IEEE80211_RADIOTAP_F_BADFCS 0x40 /* bad FCS */ - -/* For IEEE80211_RADIOTAP_RX_FLAGS */ -#define IEEE80211_RADIOTAP_F_RX_BADPLCP 0x0002 /* frame has bad PLCP */ - -/* For IEEE80211_RADIOTAP_TX_FLAGS */ -#define IEEE80211_RADIOTAP_F_TX_FAIL 0x0001 /* failed due to excessive - * retries */ -#define IEEE80211_RADIOTAP_F_TX_CTS 0x0002 /* used cts 'protection' */ -#define IEEE80211_RADIOTAP_F_TX_RTS 0x0004 /* used rts/cts handshake */ - -/* Ugly macro to convert literal channel numbers into their mhz equivalents - * There are certainly some conditions that will break this (like feeding it '30') - * but they shouldn't arise since nothing talks on channel 30. */ -#define ieee80211chan2mhz(x) \ - (((x) <= 14) ? \ - (((x) == 14) ? 2484 : ((x) * 5) + 2407) : \ - ((x) + 1000) * 5) - -/* helpers */ -static inline u16 -get_unaligned_le16 (const u8 *p) -{ - return p[0] | p[1] << 8; -} - - -static inline int -ieee80211_get_radiotap_len (unsigned char *data) -{ - struct ieee80211_radiotap_header *hdr = - (struct ieee80211_radiotap_header *) data; - - return get_unaligned_le16 ((const u8 *) &hdr->it_len); -} - - -#endif /* IEEE80211_RADIOTAP_H */ diff --git a/src/transport/meson.build b/src/transport/meson.build deleted file mode 100644 index 65a2beeb3..000000000 --- a/src/transport/meson.build +++ /dev/null @@ -1,261 +0,0 @@ -libgnunettransportapplication_src = ['transport_api2_application.c'] -libgnunettransportcore_src = ['transport_api2_core.c'] -libgnunettransportcommunicator_src = ['transport_api2_communication.c'] -libgnunettransportmonitor_src = ['transport_api2_monitor.c'] - -gnunetservicetransport_src = ['gnunet-service-transport.c'] -gnunetcommunicatortcp_src = ['gnunet-communicator-tcp.c'] -gnunetcommunicatorudp_src = ['gnunet-communicator-udp.c'] -gnunetcommunicatorunix_src = ['gnunet-communicator-unix.c'] - -configure_file(input : 'transport.conf.in', - output : 'transport.conf', - configuration : cdata, - install: true, - install_dir: pkgcfgdir) - -configure_file(input : 'gnunet-transport-certificate-creation.in', - output : 'gnunet-transport-certificate-creation', - configuration : cdata, - install: true, - install_dir: get_option('bindir')) - -if get_option('monolith') - foreach p : libgnunettransportapplication_src + libgnunettransportcore_src + libgnunettransportcommunicator_src + libgnunettransportmonitor_src + gnunetservicetransport_src - gnunet_src += 'transport/' + p - endforeach - subdir_done() -endif - -libgnunettransportapplication = library('gnunettransportapplication', - libgnunettransportapplication_src, - soversion: '0', - version: '0.0.0', - dependencies: libgnunetutil_dep, - include_directories: [incdir, configuration_inc], - install: true, - install_dir: get_option('libdir')) -pkg.generate(libgnunettransportapplication, url: 'https://www.gnunet.org', - description : 'Provides application APIs for accessing the transport service') -libgnunettransportapplication_dep = declare_dependency(link_with : libgnunettransportapplication) - -libgnunettransportcore = library('gnunettransportcore', - libgnunettransportcore_src, - soversion: '0', - version: '0.0.0', - dependencies: libgnunetutil_dep, - include_directories: [incdir, configuration_inc], - install: true, - install_dir: get_option('libdir')) -pkg.generate(libgnunettransportcore, url: 'https://www.gnunet.org', - description : 'Provides core API for accessing the transport service') -libgnunettransportcore_dep = declare_dependency(link_with : libgnunettransportcore) - -libgnunettransportcommunicator = library('gnunettransportcommunicator', - libgnunettransportcommunicator_src, - soversion: '0', - version: '0.0.0', - dependencies: libgnunetutil_dep, - include_directories: [incdir, configuration_inc], - install: true, - install_dir: get_option('libdir')) -pkg.generate(libgnunettransportcommunicator, url: 'https://www.gnunet.org', - description : 'Provides communicator API for accessing the transport service') -libgnunettransportcommunicator_dep = declare_dependency(link_with : libgnunettransportcommunicator) - -libgnunettransportmonitor = library('gnunettransportmonitor', - libgnunettransportmonitor_src, - soversion: '0', - version: '0.0.0', - dependencies: libgnunetutil_dep, - include_directories: [incdir, configuration_inc], - install: true, - install_dir: get_option('libdir')) -pkg.generate(libgnunettransportmonitor, url: 'https://www.gnunet.org', - description : 'Provides monitor API for accessing the transport service') -libgnunettransportmonitor_dep = declare_dependency(link_with : libgnunettransportmonitor) - -libgnunettransporttesting2 = library('gnunettransporttesting2', - [ - 'transport_api_traits.c', - 'transport_api_cmd_connecting_peers.c', - 'transport_api_cmd_backchannel_check.c', - 'transport_api_cmd_start_peer.c', - 'transport_api_cmd_stop_peer.c', - 'transport_api_cmd_send_simple.c', - 'transport_api_cmd_send_simple_performance.c', - 'transport-testing2.c', - 'transport-testing-filenames2.c', - 'transport-testing-loggers2.c', - 'transport-testing-main2.c', - 'transport-testing-send2.c', - 'transport-testing-communicator.c', - ], - soversion: '0', - version: '0.0.0', - dependencies: [libgnunetutil_dep, - libgnunettransportcore_dep, - libgnunettransportapplication_dep, - libgnunetpeerstore_dep, - libgnunettesting_dep, - libgnunethello_dep], - include_directories: [incdir, configuration_inc], - install: true, - install_dir: get_option('libdir')) -libgnunettransporttesting2_dep = declare_dependency(link_with : libgnunettransporttesting2) - -executable ('gnunet-service-transport', - gnunetservicetransport_src, - dependencies: [libgnunettransportcommunicator_dep, - libgnunetpeerstore_dep, - libgnunetstatistics_dep, - libgnunethello_dep, - libgnunetnat_dep, - gcrypt_dep, - libgnunetutil_dep], - include_directories: [incdir, configuration_inc], - install: true, - install_dir: get_option('libdir') / 'gnunet' / 'libexec') - -executable ('gnunet-communicator-unix', - gnunetcommunicatorunix_src, - dependencies: [libgnunettransportcommunicator_dep, - libgnunetpeerstore_dep, - libgnunetstatistics_dep, - libgnunetnat_dep, - gcrypt_dep, - libgnunetutil_dep], - include_directories: [incdir, configuration_inc], - install: true, - install_dir: get_option('libdir') / 'gnunet' / 'libexec') -executable ('gnunet-communicator-udp', - gnunetcommunicatorudp_src, - dependencies: [libgnunettransportcommunicator_dep, - libgnunettransportapplication_dep, - libgnunetpeerstore_dep, - libgnunetstatistics_dep, - libgnunetnat_dep, - gcrypt_dep, - libgnunetutil_dep], - include_directories: [incdir, configuration_inc], - install: true, - install_dir: get_option('libdir') / 'gnunet' / 'libexec') -executable ('gnunet-communicator-tcp', - gnunetcommunicatortcp_src, - dependencies: [libgnunettransportcommunicator_dep, - libgnunetpeerstore_dep, - libgnunetstatistics_dep, - libgnunetnat_dep, - gcrypt_dep, - libgnunetutil_dep], - include_directories: [incdir, configuration_inc], - install: true, - install_dir: get_option('libdir') / 'gnunet' / 'libexec') - -libgnunettesttransport_cmd_simplesend = library('gnunet_test_transport_plugin_cmd_simple_send', - ['test_transport_plugin_cmd_simple_send.c'], - dependencies: [ - libgnunetutil_dep, - libgnunettransportapplication_dep, - libgnunettransporttesting2_dep, - libgnunettransportcore_dep, - libgnunettesting_dep, - libgnunetpeerstore_dep, - libgnunetstatistics_dep, - libgnunethello_dep, - libgnunetarm_dep, - libgnunetutil_dep - ], - include_directories: [incdir, configuration_inc], - install: true, - install_dir: get_option('libdir')/'gnunet') - -libgnunettesttransport_cmd_simplesendbc = library('gnunet_test_transport_plugin_cmd_simple_send_broadcast', - ['test_transport_plugin_cmd_simple_send_broadcast.c'], - dependencies: [ - libgnunetutil_dep, - libgnunettransportapplication_dep, - libgnunettransporttesting2_dep, - libgnunettransportcore_dep, - libgnunettesting_dep, - libgnunetpeerstore_dep, - libgnunetstatistics_dep, - libgnunethello_dep, - libgnunetarm_dep, - libgnunetutil_dep - ], - include_directories: [incdir, configuration_inc], - install: true, - install_dir: get_option('libdir')/'gnunet') - -libgnunettesttransport_cmd_simplesenddv = library('gnunet_test_transport_plugin_cmd_simple_send_dv', - ['test_transport_plugin_cmd_simple_send_dv.c'], - dependencies: [ - libgnunetutil_dep, - libgnunettransportapplication_dep, - libgnunettransporttesting2_dep, - libgnunettransportcore_dep, - libgnunettesting_dep, - libgnunetpeerstore_dep, - libgnunetstatistics_dep, - libgnunethello_dep, - libgnunetarm_dep, - libgnunetutil_dep - ], - include_directories: [incdir, configuration_inc], - install: true, - install_dir: get_option('libdir')/'gnunet') - -libgnunettesttransport_cmd_simplesendperf = library('gnunet_test_transport_plugin_cmd_simple_send_performance', - ['test_transport_plugin_cmd_simple_send_performance.c'], - dependencies: [ - libgnunetutil_dep, - libgnunettransportapplication_dep, - libgnunettransporttesting2_dep, - libgnunettransportcore_dep, - libgnunettesting_dep, - libgnunetpeerstore_dep, - libgnunetstatistics_dep, - libgnunethello_dep, - libgnunetarm_dep, - libgnunetutil_dep - ], - include_directories: [incdir, configuration_inc], - install: true, - install_dir: get_option('libdir')/'gnunet') - -libgnunettesttransport_cmd_udpback = library('gnunet_test_transport_plugin_cmd_udp_backchannel', - ['test_transport_plugin_cmd_udp_backchannel.c'], - dependencies: [ - libgnunetutil_dep, - libgnunettransporttesting2_dep, - libgnunettransportapplication_dep, - libgnunettransportcore_dep, - libgnunettesting_dep, - libgnunetpeerstore_dep, - libgnunetstatistics_dep, - libgnunethello_dep, - libgnunetarm_dep, - libgnunetutil_dep - ], - include_directories: [incdir, configuration_inc], - install: true, - install_dir: get_option('libdir')/'gnunet') - -libgnunettesttransport_cmd_natupnp = library('gnunet_test_transport_plugin_cmd_nat_upnp', - ['test_transport_plugin_cmd_nat_upnp.c'], - dependencies: [ - libgnunetutil_dep, - libgnunettransportapplication_dep, - libgnunettransporttesting2_dep, - libgnunettransportcore_dep, - libgnunettesting_dep, - libgnunetpeerstore_dep, - libgnunetstatistics_dep, - libgnunethello_dep, - libgnunetarm_dep, - libgnunetutil_dep - ], - include_directories: [incdir, configuration_inc], - install: true, - install_dir: get_option('libdir')/'gnunet') diff --git a/src/transport/template_cfg_peer1.conf b/src/transport/template_cfg_peer1.conf deleted file mode 100644 index 5ba198450..000000000 --- a/src/transport/template_cfg_peer1.conf +++ /dev/null @@ -1,50 +0,0 @@ -@INLINE@ test_transport_defaults.conf -[PATHS] -GNUNET_TEST_HOME = $GNUNET_TMP/test-transport/api-tcp-p1/ -GNUNET_RUNTIME_DIR = $GNUNET_TEST_HOME/runtime/ -GNUNET_USER_RUNTIME_DIR = $GNUNET_TEST_HOME/runtime/ - -[nat] -RETURN_LOCAL_ADDRESSES = YES -DISABLEV6 = NO - -[transport-tcp] -PORT = 12000 -TIMEOUT = 5 s - -[transport-udp] -BROADCAST = NO - -[transport-unix] -PORT = 12007 - -[arm] -PORT = 12005 -UNIXPATH = $GNUNET_RUNTIME_DIR/gnunet-p1-service-arm.sock - -[statistics] -PORT = 12004 -UNIXPATH = $GNUNET_RUNTIME_DIR/gnunet-p1-service-statistics.sock - -[resolver] -PORT = 12003 -UNIXPATH = $GNUNET_RUNTIME_DIR/gnunet-p1-service-resolver.sock - -[peerinfo] -PORT = 12002 -UNIXPATH = $GNUNET_RUNTIME_DIR/gnunet-p1-service-peerinfo.sock -USE_INCLUDED_HELLOS = NO - -[transport] -#PREFIX = valgrind --leak-check=full -PORT = 12001 -UNIXPATH = $GNUNET_RUNTIME_DIR/gnunet-p1-service-transport.sock - -[ats] -WAN_QUOTA_IN = unlimited -WAN_QUOTA_OUT = unlimited -PORT = 12006 -UNIXPATH = $GNUNET_RUNTIME_DIR/gnunet-p1-service-ats.sock - -[hostlist] -SERVERS = dummy diff --git a/src/transport/template_cfg_peer2.conf b/src/transport/template_cfg_peer2.conf deleted file mode 100644 index 6ac610fec..000000000 --- a/src/transport/template_cfg_peer2.conf +++ /dev/null @@ -1,58 +0,0 @@ -@INLINE@ test_transport_defaults.conf -[PATHS] -GNUNET_TEST_HOME = $GNUNET_TMP/test-transport/api-tcp-p2/ -GNUNET_RUNTIME_DIR = $GNUNET_TEST_HOME/runtime/ -GNUNET_USER_RUNTIME_DIR = $GNUNET_TEST_HOME/runtime/ - -[nat] -RETURN_LOCAL_ADDRESSES = YES -DISABLEV6 = NO - -[transport-tcp] -PORT = 12100 -TIMEOUT = 5 s - -[transport-udp] -BROADCAST = NO - -[transport-unix] -PORT = 12017 - -[transport-http_server] -PORT = 12018 -USE_IPv6 = YES - -[transport-https_server] -PORT = 12019 -KEY_FILE = $GNUNET_TEST_HOME/https_key_p1.key -CERT_FILE = $GNUNET_TEST_HOME/https_cert_p1.crt -USE_IPv6 = YES - -[arm] -PORT = 12014 -UNIXPATH = $GNUNET_RUNTIME_DIR/gnunet-p2-service-arm.sock - -[statistics] -PORT = 12013 -UNIXPATH = $GNUNET_RUNTIME_DIR/gnunet-p2-service-statistics.sock - -[resolver] -PORT = 12012 -UNIXPATH = $GNUNET_RUNTIME_DIR/gnunet-p2-service-resolver.sock - -[peerinfo] -PORT = 12011 -UNIXPATH = $GNUNET_RUNTIME_DIR/gnunet-p2-service-peerinfo.sock -USE_INCLUDED_HELLOS = NO - -[transport] -#PREFIX = valgrind --leak-check=full -PORT = 12010 -UNIXPATH = $GNUNET_RUNTIME_DIR/gnunet-p2-service-transport.sock - -[ats] -PORT = 12016 -UNIXPATH = $GNUNET_RUNTIME_DIR/gnunet-p2-service-ats.sock - -[hostlist] -SERVERS = dummy diff --git a/src/transport/template_tng_cfg_peer1.conf b/src/transport/template_tng_cfg_peer1.conf deleted file mode 100644 index b6198d72c..000000000 --- a/src/transport/template_tng_cfg_peer1.conf +++ /dev/null @@ -1,34 +0,0 @@ -@INLINE@ test_tng_defaults.conf -[PATHS] -GNUNET_TEST_HOME = $GNUNET_TMP/test-transport/api-tcp-p1/ -GNUNET_RUNTIME_DIR = $GNUNET_TEST_HOME/runtime/ -GNUNET_USER_RUNTIME_DIR = $GNUNET_TEST_HOME/runtime/ - -[nat] -RETURN_LOCAL_ADDRESSES = YES -DISABLEV6 = NO - -[arm] -PORT = 12005 -UNIXPATH = $GNUNET_RUNTIME_DIR/gnunet-p1-service-arm.sock - -[statistics] -PORT = 12004 -UNIXPATH = $GNUNET_RUNTIME_DIR/gnunet-p1-service-statistics.sock - -[resolver] -PORT = 12003 -UNIXPATH = $GNUNET_RUNTIME_DIR/gnunet-p1-service-resolver.sock - -[peerinfo] -PORT = 12002 -UNIXPATH = $GNUNET_RUNTIME_DIR/gnunet-p1-service-peerinfo.sock -USE_INCLUDED_HELLOS = NO - -[transport] -#PREFIX = valgrind --leak-check=full -PORT = 12001 -UNIXPATH = $GNUNET_RUNTIME_DIR/gnunet-p1-service-transport.sock - -[hostlist] -SERVERS = dummy diff --git a/src/transport/test_communicator_basic.c b/src/transport/test_communicator_basic.c deleted file mode 100644 index fdbad0957..000000000 --- a/src/transport/test_communicator_basic.c +++ /dev/null @@ -1,1224 +0,0 @@ -/* - This file is part of GNUnet. - Copyright (C) 2019 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 . - - SPDX-License-Identifier: AGPL3.0-or-later -*/ - -/** -* @file transport/test_communicator_basic.c -* @brief test the communicators -* @author Julius Bünger -* @author Martin Schanzenbach -*/ -#include "platform.h" -#include "gnunet_util_lib.h" -#include "transport-testing-communicator.h" -#include "gnunet_ats_transport_service.h" -#include "gnunet_signatures.h" -#include "gnunet_testing_lib.h" -#include "transport.h" -#include "gnunet_statistics_service.h" - -#include - - -#define LOG(kind, ...) GNUNET_log_from (kind, \ - "test_transport_communicator", \ - __VA_ARGS__) - -#define NUM_PEERS 2 - -static struct GNUNET_SCHEDULER_Task *to_task[NUM_PEERS]; - -static int queue_est = GNUNET_NO; - -static struct GNUNET_PeerIdentity peer_id[NUM_PEERS]; - -static char *communicator_binary; - -static struct -GNUNET_TRANSPORT_TESTING_TransportCommunicatorHandle *tc_hs[NUM_PEERS]; - -static struct GNUNET_CONFIGURATION_Handle *cfg_peers[NUM_PEERS]; - -static struct GNUNET_STATISTICS_Handle *stats[NUM_PEERS]; - -static char *cfg_peers_name[NUM_PEERS]; - -static int finished[NUM_PEERS]; - -static int ret; - -static int bidirect = GNUNET_NO; - -static size_t long_message_size; - -static struct GNUNET_TIME_Absolute start_short[NUM_PEERS]; - -static struct GNUNET_TIME_Absolute start_long[NUM_PEERS]; - -static struct GNUNET_TIME_Absolute timeout[NUM_PEERS]; - -// static struct GNUNET_TRANSPORT_TESTING_TransportCommunicatorHandle *my_tc; - -static char *communicator_name; - -static char *test_name; - -static struct GNUNET_STATISTICS_GetHandle *box_stats[NUM_PEERS]; - -static struct GNUNET_STATISTICS_GetHandle *rekey_stats[NUM_PEERS]; - -#define TEST_SECTION "test-setup" - -#define SHORT_MESSAGE_SIZE 128 - -#define LONG_MESSAGE_SIZE 32000 /* FIXME */ - -#define ALLOWED_PACKET_LOSS 91 - -#define BURST_PACKETS 5000 - -#define TOTAL_ITERATIONS 1 - -#define PEER_A 0 - -#define PEER_B 1 - -static unsigned int iterations_left[NUM_PEERS]; - -#define TIMEOUT_MULTIPLIER 1 - -#define DELAY \ - GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_MICROSECONDS,200) - -#define SHORT_BURST_WINDOW \ - GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS,2) - -#define LONG_BURST_WINDOW \ - GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS,2) - -enum TestPhase -{ - TP_INIT, - TP_BURST_SHORT, - TP_BURST_LONG, - TP_SIZE_CHECK -}; - -static unsigned int phase_short[NUM_PEERS]; - -static unsigned int phase_long[NUM_PEERS]; - -static unsigned int phase_size[NUM_PEERS]; - -static long long unsigned int allowed_packet_loss_short; - -static long long unsigned int allowed_packet_loss_long; - -static long long unsigned int burst_packets_short; - -static long long unsigned int burst_packets_long; - -static long long unsigned int delay_long_value; - -static long long unsigned int delay_short_value; - -static struct GNUNET_TIME_Relative delay_short; - -static struct GNUNET_TIME_Relative delay_long; - -static size_t num_sent_short[NUM_PEERS]; - -static size_t num_sent_long[NUM_PEERS]; - -static size_t num_sent_size[NUM_PEERS]; - -static uint32_t ack[NUM_PEERS]; - -static enum TestPhase phase[NUM_PEERS]; - -static size_t num_received_short[NUM_PEERS]; - -static size_t num_received_long[NUM_PEERS]; - -static size_t num_received_size[NUM_PEERS]; - -static uint64_t avg_latency[NUM_PEERS]; - -static void -communicator_available_cb ( - void *cls, - struct - GNUNET_TRANSPORT_TESTING_TransportCommunicatorHandle *tc_h, - enum GNUNET_TRANSPORT_CommunicatorCharacteristics cc, - char *address_prefix) -{ - LOG (GNUNET_ERROR_TYPE_INFO, - "Communicator available. (cc: %u, prefix: %s)\n", - cc, - address_prefix); -} - - -static void -open_queue (void *cls) -{ - const char *address = cls; - - if (NULL != tc_hs[PEER_A]->c_mq) - { - queue_est = GNUNET_YES; - GNUNET_TRANSPORT_TESTING_transport_communicator_open_queue (tc_hs[PEER_A], - &peer_id[PEER_B], - address); - } - else - { - GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_UNIT_SECONDS, - &open_queue, - (void *) address); - } -} - - -static void -add_address_cb ( - void *cls, - struct GNUNET_TRANSPORT_TESTING_TransportCommunicatorHandle *tc_h, - const char *address, - struct GNUNET_TIME_Relative expiration, - uint32_t aid, - enum GNUNET_NetworkType nt) -{ - LOG (GNUNET_ERROR_TYPE_DEBUG, - "New address. (addr: %s, expir: %s, ID: %" PRIu32 ", nt: %u\n", - address, - GNUNET_STRINGS_relative_time_to_string (expiration, - GNUNET_NO), - aid, - (int) nt); - // addresses[1] = GNUNET_strdup (address); - if ((0 == strcmp ((char*) cls, cfg_peers_name[PEER_B])) && - (GNUNET_NO == queue_est)) - { - open_queue ((void *) address); - } -} - - -/** - * @brief Callback that informs whether the requested queue will be - * established - * - * Implements #GNUNET_TRANSPORT_TESTING_QueueCreateReplyCallback. - * - * @param cls Closure - unused - * @param tc_h Communicator handle - unused - * @param will_try #GNUNET_YES if queue will be established - * #GNUNET_NO if queue will not be established (bogous address) - */ -static void -queue_create_reply_cb ( - void *cls, - struct GNUNET_TRANSPORT_TESTING_TransportCommunicatorHandle *tc_h, - int will_try) -{ - (void) cls; - (void) tc_h; - if (GNUNET_YES == will_try) - LOG (GNUNET_ERROR_TYPE_DEBUG, - "Queue will be established!\n"); - else - LOG (GNUNET_ERROR_TYPE_WARNING, - "Queue won't be established (bougus address?)!\n"); -} - - -static struct GNUNET_TRANSPORT_TESTING_TransportCommunicatorHandle * -handle_backchannel_cb (void *cls, - struct GNUNET_MessageHeader *msg, - struct GNUNET_PeerIdentity *pid) -{ - struct GNUNET_TRANSPORT_TESTING_TransportCommunicatorHandle *tc_h = cls; - - (void) tc_h; - (void) msg; - LOG (GNUNET_ERROR_TYPE_DEBUG, "Handling BC message...\n"); - if (0 == memcmp (&peer_id[PEER_A], pid, sizeof (*pid))) - return tc_hs[PEER_A]; - else - return tc_hs[PEER_B]; -} - - -static char* -make_payload (size_t payload_size) -{ - struct GNUNET_TIME_Absolute ts; - struct GNUNET_TIME_AbsoluteNBO ts_n; - char *payload = GNUNET_malloc (payload_size); - - LOG (GNUNET_ERROR_TYPE_DEBUG, - "Making payload of size %lu\n", payload_size); - GNUNET_assert (payload_size >= 8); // So that out timestamp fits - ts = GNUNET_TIME_absolute_get (); - ts_n = GNUNET_TIME_absolute_hton (ts); - memset (payload, 'a', payload_size); - memcpy (payload, &ts_n, sizeof (struct GNUNET_TIME_AbsoluteNBO)); - return payload; -} - - -static struct GNUNET_TRANSPORT_TESTING_TransportCommunicatorHandle * -get_tc_h (unsigned int peer_nr) -{ - LOG (GNUNET_ERROR_TYPE_DEBUG, - "Got peer %u\n", - peer_nr); - - LOG (GNUNET_ERROR_TYPE_DEBUG, - "Handle %p peer 0\n", - tc_hs[0]); - - LOG (GNUNET_ERROR_TYPE_DEBUG, - "Handle %p peer 1\n", - tc_hs[1]); - - LOG (GNUNET_ERROR_TYPE_DEBUG, - "Handle %p get\n", - tc_hs[peer_nr]); - - return tc_hs[peer_nr]; -} - - -static unsigned int -get_peer_nr_from_tc (struct - GNUNET_TRANSPORT_TESTING_TransportCommunicatorHandle *tc_h) -{ - if (tc_h == get_tc_h (0)) - return PEER_A; - else - return PEER_B; -} - - -static unsigned int -get_peer_nr (void *cls, unsigned int get_the_other_one) -{ - if (0 == strcmp ((char*) cls, cfg_peers_name[0])) - return get_the_other_one ? PEER_B : PEER_A; - else - return get_the_other_one ? PEER_A : PEER_B; -} - - -static void -process_statistics_box_done (void *cls, int success) -{ - struct GNUNET_TRANSPORT_TESTING_TransportCommunicatorHandle *tc_h = cls; - unsigned int peer_nr; - - peer_nr = get_peer_nr_from_tc (tc_h); - - if (NULL != box_stats[peer_nr]) - box_stats[peer_nr] = NULL; - if (NULL == rekey_stats[peer_nr]) - { - LOG (GNUNET_ERROR_TYPE_DEBUG, - "Finished\n"); - GNUNET_SCHEDULER_shutdown (); - } -} - - -static void -process_statistics_rekey_done (void *cls, int success) -{ - struct GNUNET_TRANSPORT_TESTING_TransportCommunicatorHandle *tc_h = cls; - unsigned int peer_nr; - - peer_nr = get_peer_nr_from_tc (tc_h); - - if (NULL != rekey_stats[peer_nr]) - rekey_stats[peer_nr] = NULL; - if (NULL == box_stats[peer_nr]) - { - LOG (GNUNET_ERROR_TYPE_DEBUG, - "Finished\n"); - GNUNET_SCHEDULER_shutdown (); - } -} - - -static int -process_statistics (void *cls, - const char *subsystem, - const char *name, - uint64_t value, - int is_persistent) -{ - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "Statistic: Name %s and value %lu\n", - name, - value); - if ((0 == strcmp ("rekey", test_name)) && (0 == strcmp ( - "# rekeying successful", - name)) && (0 == value)) - { - ret = 2; - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - "No successful rekeying!\n"); - GNUNET_SCHEDULER_shutdown (); - } - if ((0 == strcmp ("backchannel", test_name)) && - (0 == strcmp ( - "# messages decrypted with BOX", - name)) - && (9000 > value)) - { - ret = 2; - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - "Not enough BOX messages! (want: %u, have %llu)\n", - 9000, value); - GNUNET_SCHEDULER_shutdown (); - } - if ((0 == strcmp ("rekey", test_name)) && - (0 == strcmp ( - "# messages decrypted with BOX", - name)) - && (6000 > value)) - { - ret = 2; - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - "Not enough BOX messages! (want: %u, have %llu)\n", - 6000, value); - GNUNET_SCHEDULER_shutdown (); - } - return GNUNET_OK; -} - - -static void -short_test (void *cls); - -static void -short_test_cb (void *cls) -{ - struct GNUNET_TRANSPORT_TESTING_TransportCommunicatorHandle *tc_h = cls; - unsigned int peer_nr; - char *payload; - - peer_nr = get_peer_nr_from_tc (tc_h); - - LOG (GNUNET_ERROR_TYPE_DEBUG, - "short_test_cb %u/%u for peer %u and handle %p\n", - (unsigned int) num_sent_short[peer_nr], - (unsigned int) num_received_short[peer_nr], - peer_nr, - tc_h); - payload = make_payload (SHORT_MESSAGE_SIZE); - num_sent_short[peer_nr]++; - if (burst_packets_short == num_sent_short[peer_nr]) - tc_h->cont = NULL; - else - tc_h->cont = short_test; - tc_h->cont_cls = cls; - GNUNET_TRANSPORT_TESTING_transport_communicator_send (tc_h, - NULL, - cls, - payload, - SHORT_MESSAGE_SIZE); - GNUNET_free (payload); - timeout[peer_nr] = GNUNET_TIME_relative_to_absolute ( - GNUNET_TIME_relative_multiply ( - GNUNET_TIME_UNIT_SECONDS, - TIMEOUT_MULTIPLIER)); -} - - -static void -short_test (void *cls) -{ - GNUNET_SCHEDULER_add_delayed (delay_short, - &short_test_cb, - cls); -} - - -static void -size_test (void *cls) -{ - unsigned int peer_nr; - char *payload; - size_t max_size = 64000; - struct GNUNET_TRANSPORT_TESTING_TransportCommunicatorHandle *tc_h = cls; - - peer_nr = get_peer_nr_from_tc (tc_h); - LOG (GNUNET_ERROR_TYPE_DEBUG, - "size_test_cb %u\n", - (unsigned int) num_sent_size[peer_nr]); - GNUNET_assert (TP_SIZE_CHECK == phase[peer_nr]); - if (LONG_MESSAGE_SIZE != long_message_size) - max_size = long_message_size; - if (ack[peer_nr] + 10 > max_size) - return; /* Leave some room for our protocol, so not 2^16 exactly */ - ack[peer_nr] += 10; - payload = make_payload (ack[peer_nr]); - num_sent_size[peer_nr]++; - if (ack[peer_nr] >= max_size) - tc_h->cont = NULL; - else - tc_h->cont = size_test; - tc_h->cont_cls = cls; - GNUNET_TRANSPORT_TESTING_transport_communicator_send (tc_h, - NULL, - cls, - payload, - ack[peer_nr]); - GNUNET_free (payload); - timeout[peer_nr] = GNUNET_TIME_relative_to_absolute ( - GNUNET_TIME_relative_multiply ( - GNUNET_TIME_UNIT_SECONDS, - TIMEOUT_MULTIPLIER)); -} - - -static void -long_test (void *cls); - -static void -long_test_cb (void *cls) -{ - unsigned int peer_nr; - char *payload; - struct GNUNET_TRANSPORT_TESTING_TransportCommunicatorHandle *tc_h = cls; - - peer_nr = get_peer_nr_from_tc (tc_h); - - LOG (GNUNET_ERROR_TYPE_DEBUG, - "long_test_cb %u/%u\n", - (unsigned int) num_sent_long[peer_nr], - (unsigned int) num_received_long[peer_nr]); - payload = make_payload (long_message_size); - num_sent_long[peer_nr]++; - if (burst_packets_long == num_sent_long[peer_nr]) - tc_h->cont = NULL; - else - tc_h->cont = long_test; - tc_h->cont_cls = cls; - GNUNET_TRANSPORT_TESTING_transport_communicator_send (tc_h, - NULL, - cls, - payload, - long_message_size); - GNUNET_free (payload); - timeout[peer_nr] = GNUNET_TIME_relative_to_absolute ( - GNUNET_TIME_relative_multiply ( - GNUNET_TIME_UNIT_SECONDS, - TIMEOUT_MULTIPLIER)); -} - - -static void -long_test (void *cls) -{ - GNUNET_SCHEDULER_add_delayed (delay_long, - &long_test_cb, - cls); -} - - -static void -choose_phase (struct GNUNET_TRANSPORT_TESTING_TransportCommunicatorHandle *tc_h) -{ - unsigned int peer_nr; - - peer_nr = get_peer_nr_from_tc (tc_h); - - if (GNUNET_YES == phase_short[peer_nr]) - { - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "Choose phase short with peer %u and Handle %p\n", - peer_nr, - tc_h); - phase[peer_nr] = TP_BURST_SHORT; - start_short[peer_nr] = GNUNET_TIME_absolute_get (); - short_test (tc_h); - } - else if (GNUNET_YES == phase_long[peer_nr]) - { - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "Choose phase long with peer %u\n", - peer_nr); - phase[peer_nr] = TP_BURST_LONG; - start_long[peer_nr] = GNUNET_TIME_absolute_get (); - long_test (tc_h); - } - else if (GNUNET_YES == phase_size[peer_nr]) - { - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "Choose phase size\n"); - phase[peer_nr] = TP_SIZE_CHECK; - size_test (tc_h); - } - else - { - if ((0 == strcmp ("udp", communicator_name)) && ((0 == strcmp ("rekey", - test_name)) - || (0 == strcmp ( - "backchannel", - test_name))) ) - { - if (NULL != box_stats[peer_nr]) - GNUNET_STATISTICS_get_cancel (box_stats[peer_nr]); - box_stats[peer_nr] = GNUNET_STATISTICS_get (stats[1], - "C-UDP", - "# messages decrypted with BOX", - process_statistics_box_done, - &process_statistics, - tc_h); - if (NULL != rekey_stats[peer_nr]) - GNUNET_STATISTICS_get_cancel (rekey_stats[peer_nr]); - rekey_stats[peer_nr] = GNUNET_STATISTICS_get (stats[0], - "C-UDP", - "# rekeying successful", - process_statistics_rekey_done, - &process_statistics, - tc_h); - } - else - { - if ((GNUNET_NO == bidirect) || (((PEER_A == peer_nr) && - finished[PEER_B]) || ((PEER_B == - peer_nr) && - finished - [PEER_A]))) - { - LOG (GNUNET_ERROR_TYPE_DEBUG, - "Finished\n"); - GNUNET_SCHEDULER_shutdown (); - } - finished[peer_nr] = GNUNET_YES; - } - } -} - - -static void -finish_phase_long (unsigned int peer_nr) -{ - static struct GNUNET_TIME_Relative duration; - - duration = GNUNET_TIME_absolute_get_duration (start_long[peer_nr]); - LOG (GNUNET_ERROR_TYPE_MESSAGE, - "Long size packet test for peer %u done.\n", - peer_nr); - char *goodput = GNUNET_STRINGS_byte_size_fancy ( - (long_message_size * num_received_long[peer_nr] * 1000 * 1000) - / duration. - rel_value_us); - - LOG (GNUNET_ERROR_TYPE_MESSAGE, - "%lu/%lu packets in %llu us (%s/s) -- avg latency: %llu us\n", - (unsigned long) num_received_long[peer_nr], - (unsigned long) num_sent_long[peer_nr], - (unsigned long long) duration.rel_value_us, - goodput, - (unsigned long long) avg_latency[peer_nr]); - GNUNET_free (goodput); - ack[peer_nr] = 0; - // phase = TP_SIZE_CHECK; - // num_received = 0; - // num_sent_long = 0; - avg_latency[peer_nr] = 0; - // size_test (NULL); - phase_long[peer_nr] = GNUNET_NO; - choose_phase (get_tc_h (peer_nr)); -} - - -static void -finish_phase_short (unsigned int peer_nr) -{ - static struct GNUNET_TIME_Relative duration; - - duration = GNUNET_TIME_absolute_get_duration (start_short[peer_nr]); - LOG (GNUNET_ERROR_TYPE_MESSAGE, - "Short size packet test for peer %u done.\n", - peer_nr); - char *goodput = GNUNET_STRINGS_byte_size_fancy ( - (SHORT_MESSAGE_SIZE * num_received_short[peer_nr] * 1000 * 1000) - / duration.rel_value_us); - LOG (GNUNET_ERROR_TYPE_MESSAGE, - "%lu/%lu packets in %llu us (%s/s) -- avg latency: %llu us\n", - (unsigned long) num_received_short[peer_nr], - (unsigned long) num_sent_short[peer_nr], - (unsigned long long) duration.rel_value_us, - goodput, - (unsigned long long) avg_latency[peer_nr]); - GNUNET_free (goodput); - // start_long = GNUNET_TIME_absolute_get (); - // phase = TP_BURST_LONG; - // num_sent_short = 0; - avg_latency[peer_nr] = 0; - // num_received = 0; - phase_short[peer_nr] = GNUNET_NO; - choose_phase (get_tc_h (peer_nr)); - // long_test (NULL); -} - - -static void -latency_timeout (void *cls) -{ - - struct GNUNET_TRANSPORT_TESTING_TransportCommunicatorHandle *tc_h = cls; - unsigned int peer_nr; - size_t num_sent = 0; - size_t num_received = 0; - - peer_nr = get_peer_nr_from_tc (tc_h); - to_task[peer_nr] = NULL; - - switch (phase[peer_nr]) - { - case TP_INIT: - GNUNET_assert (0); - break; - case TP_BURST_SHORT: - num_sent = num_sent_short[peer_nr]; - num_received = num_received_short[peer_nr]; - if ((num_sent_short[peer_nr] == burst_packets_short) && - (num_received_short[peer_nr] > - burst_packets_short - / 100 - * - allowed_packet_loss_short) ) - { - finish_phase_short (peer_nr); - to_task[peer_nr] = GNUNET_SCHEDULER_add_at (timeout[peer_nr], - &latency_timeout, - cls); - return; - } - break; - case TP_BURST_LONG: - num_sent = num_sent_long[peer_nr]; - num_received = num_received_long[peer_nr]; - if ((num_sent_long[peer_nr] == burst_packets_long) && - (num_received_long[peer_nr] > - burst_packets_long - / 100 - * - allowed_packet_loss_long) ) - { - finish_phase_long (peer_nr); - to_task[peer_nr] = GNUNET_SCHEDULER_add_at (timeout[peer_nr], - &latency_timeout, - cls); - return; - } - break; - case TP_SIZE_CHECK: - num_sent = num_sent_size[peer_nr]; - num_received = num_received_size[peer_nr]; - break; - } - if (GNUNET_TIME_absolute_get_remaining (timeout[peer_nr]).rel_value_us > 0) - { - to_task[peer_nr] = GNUNET_SCHEDULER_add_at (timeout[peer_nr], - &latency_timeout, - cls); - return; - } - LOG (GNUNET_ERROR_TYPE_ERROR, - "Latency too high. Test failed. (Phase: %d. Sent: %lu, Received: %lu)\n", - phase[peer_nr], num_sent, num_received); - ret = 2; - GNUNET_SCHEDULER_shutdown (); -} - - -/** - * @brief Handle opening of queue - * - * Issues sending of test data - * - * Implements #GNUNET_TRANSPORT_TESTING_AddQueueCallback - * - * @param cls Closure - * @param tc_h Communicator handle - * @param tc_queue Handle to newly opened queue - */ -static void -add_queue_cb (void *cls, - struct GNUNET_TRANSPORT_TESTING_TransportCommunicatorHandle *tc_h, - struct GNUNET_TRANSPORT_TESTING_TransportCommunicatorQueue * - tc_queue, - size_t mtu) -{ - - unsigned int peer_nr; - - peer_nr = get_peer_nr (cls, GNUNET_NO); - LOG (GNUNET_ERROR_TYPE_DEBUG, - "Handle %p add %u %u\n", - tc_h, - peer_nr, - get_peer_nr_from_tc (tc_h)); - if ((GNUNET_NO == bidirect) && (0 != strcmp ((char*) cls, cfg_peers_name[0]))) - { - LOG (GNUNET_ERROR_TYPE_DEBUG, - "Queue available at receiving peer\n"); - return; // TODO? - } - else if (TP_INIT != phase[peer_nr]) - return; - LOG (GNUNET_ERROR_TYPE_DEBUG, - "Queue established, starting test...\n"); - if (UINT16_MAX != mtu) /* Message header overhead */ - long_message_size = mtu - sizeof(struct GNUNET_TRANSPORT_SendMessageTo) - - sizeof(struct GNUNET_MessageHeader); - else - long_message_size = LONG_MESSAGE_SIZE; - timeout[peer_nr] = GNUNET_TIME_relative_to_absolute ( - GNUNET_TIME_relative_multiply ( - GNUNET_TIME_UNIT_SECONDS, - TIMEOUT_MULTIPLIER)); - GNUNET_assert (NULL == to_task[peer_nr]); - to_task[peer_nr] = GNUNET_SCHEDULER_add_delayed ( - GNUNET_TIME_relative_multiply ( - GNUNET_TIME_UNIT_SECONDS, - TIMEOUT_MULTIPLIER), - &latency_timeout, - tc_h); - choose_phase (tc_h); -} - - -static void -update_avg_latency (const char *payload, unsigned int peer_nr) -{ - struct GNUNET_TIME_AbsoluteNBO *ts_n; - struct GNUNET_TIME_Absolute ts; - struct GNUNET_TIME_Relative latency; - size_t num_received = 0; - - ts_n = (struct GNUNET_TIME_AbsoluteNBO *) payload; - ts = GNUNET_TIME_absolute_ntoh (*ts_n); - latency = GNUNET_TIME_absolute_get_duration (ts); - - switch (phase[peer_nr]) - { - case TP_INIT: - GNUNET_assert (0); - break; - case TP_BURST_SHORT: - num_received = num_received_short[peer_nr]; - break; - case TP_BURST_LONG: - num_received = num_received_long[peer_nr]; - break; - case TP_SIZE_CHECK: - num_received = num_received_size[peer_nr]; - break; - } - if (1 >= num_received) - avg_latency[peer_nr] = latency.rel_value_us; - else - avg_latency[peer_nr] = ((avg_latency[peer_nr] * (num_received - 1)) - + latency.rel_value_us) - / num_received; - LOG (GNUNET_ERROR_TYPE_DEBUG, - "Latency of received packet by peer %u: %s with avg latency %lu\n", - peer_nr, - GNUNET_STRINGS_relative_time_to_string (latency, - GNUNET_YES), - avg_latency[peer_nr]); -} - - -static void -load_phase_config () -{ - - phase_short[0] = GNUNET_CONFIGURATION_get_value_yesno (cfg_peers[0], - TEST_SECTION, - "PHASE_SHORT"); - if (GNUNET_SYSERR == phase_short[0]) - phase_short[0] = GNUNET_YES; - - phase_short[1] = phase_short[0]; - - phase_long[0] = GNUNET_CONFIGURATION_get_value_yesno (cfg_peers[0], - TEST_SECTION, - "PHASE_LONG"); - - if (GNUNET_SYSERR == phase_long[0]) - phase_long[0] = GNUNET_YES; - - phase_long[1] = phase_long[0]; - - phase_size[0] = GNUNET_CONFIGURATION_get_value_yesno (cfg_peers[0], - TEST_SECTION, - "PHASE_SIZE"); - - if (GNUNET_SYSERR == phase_size[0]) - phase_size[0] = GNUNET_YES; - - phase_size[1] = phase_size[0]; -} - - -/** - * @brief Handle an incoming message - * - * Implements #GNUNET_TRANSPORT_TESTING_IncomingMessageCallback - - * @param cls Closure - * @param tc_h Handle to the receiving communicator - * @param msg Received message - */ -static void -incoming_message_cb ( - void *cls, - struct GNUNET_TRANSPORT_TESTING_TransportCommunicatorHandle *tc_h, - const char *payload, - size_t payload_len) -{ - unsigned int peer_nr; - - - peer_nr = get_peer_nr (cls, GNUNET_YES); - - if ((GNUNET_NO == bidirect) && (0 != strcmp ((char*) cls, - cfg_peers_name[NUM_PEERS - 1]))) - { - LOG (GNUNET_ERROR_TYPE_WARNING, - "unexpected receiver...\n"); - return; - } - /* Reset timeout */ - timeout[peer_nr] = GNUNET_TIME_relative_to_absolute ( - GNUNET_TIME_relative_multiply ( - GNUNET_TIME_UNIT_SECONDS, - TIMEOUT_MULTIPLIER)); - switch (phase[peer_nr]) - { - case TP_INIT: - GNUNET_break (0); - break; - case TP_BURST_SHORT: - { - GNUNET_assert (SHORT_MESSAGE_SIZE == payload_len); - num_received_short[peer_nr]++; - - update_avg_latency (payload, peer_nr); - if ((num_sent_short[peer_nr] == burst_packets_short) && - (num_received_short[peer_nr] == - burst_packets_short)) - { - finish_phase_short (peer_nr); - } - break; - } - case TP_BURST_LONG: - { - if (long_message_size != payload_len) - { - LOG (GNUNET_ERROR_TYPE_WARNING, - "Ignoring packet with wrong length (have: %lu, want: %lu)\n", - payload_len, long_message_size); - return; // Ignore - } - num_received_long[peer_nr]++; - - update_avg_latency (payload, peer_nr); - if ((num_sent_long[peer_nr] == burst_packets_long) && - (num_received_long[peer_nr] > - burst_packets_long)) - { - finish_phase_long (peer_nr); - } - break; - } - case TP_SIZE_CHECK: - { - size_t max_size = 64000; - - GNUNET_assert (TP_SIZE_CHECK == phase[peer_nr]); - if (LONG_MESSAGE_SIZE != long_message_size) - max_size = long_message_size; - num_received_size[peer_nr]++; - update_avg_latency (payload, peer_nr); - if ((GNUNET_YES == phase_size[peer_nr]) && (num_received_size[peer_nr] >= - (max_size) / 10) ) - { - LOG (GNUNET_ERROR_TYPE_MESSAGE, - "Size packet test for peer %u done.\n", - peer_nr); - LOG (GNUNET_ERROR_TYPE_MESSAGE, - "%lu/%lu packets -- avg latency: %llu us\n", - (unsigned long) num_received_size[peer_nr], - (unsigned long) num_sent_size[peer_nr], - (unsigned long long) avg_latency[peer_nr]); - iterations_left[peer_nr]--; - phase_size[peer_nr] = GNUNET_NO; - if (0 != iterations_left[peer_nr]) - { - // start_short = GNUNET_TIME_absolute_get (); - // phase = TP_BURST_SHORT; - num_received_size[peer_nr] = 0; - num_sent_size[peer_nr] = 0; - avg_latency[peer_nr] = 0; - num_sent_short[peer_nr] = 0; - num_sent_long[peer_nr] = 0; - num_received_short[peer_nr] = 0; - num_received_long[peer_nr] = 0; - // short_test (NULL); - if (((PEER_A == peer_nr) && finished[PEER_B]) || ((PEER_B == - peer_nr) && - finished[PEER_A])) - { - load_phase_config (); - } - } - choose_phase (get_tc_h (peer_nr)); - } - break; - } - } -} - - -static void -do_shutdown (void *cls) -{ - LOG (GNUNET_ERROR_TYPE_DEBUG, - "shutting down test.\n"); - - for (unsigned int i = 0; i < NUM_PEERS; i++) - { - if (NULL != box_stats[i]) - { - GNUNET_STATISTICS_get_cancel (box_stats[i]); - box_stats[i] = NULL; - } - if (NULL != rekey_stats[i]) - { - GNUNET_STATISTICS_get_cancel (rekey_stats[i]); - rekey_stats[i] = NULL; - } - if (NULL != to_task[i]) - { - GNUNET_SCHEDULER_cancel (to_task[i]); - to_task[i] = NULL; - } - GNUNET_TRANSPORT_TESTING_transport_communicator_service_stop (tc_hs[i]); - GNUNET_STATISTICS_destroy (stats[i], GNUNET_NO); - } -} - - -/** - * @brief Main function called by the scheduler - * - * @param cls Closure - Handle to confiation - */ -static void -run (void *cls) -{ - ret = 0; - // num_received = 0; - // num_sent = 0; - for (unsigned int i = 0; i < NUM_PEERS; i++) - { - tc_hs[i] = GNUNET_TRANSPORT_TESTING_transport_communicator_service_start ( - "transport", - communicator_binary, - cfg_peers_name[i], - &peer_id[i], - &communicator_available_cb, - &add_address_cb, - &queue_create_reply_cb, - &add_queue_cb, - &incoming_message_cb, - &handle_backchannel_cb, - cfg_peers_name[i]); /* cls */ - - if ((0 == strcmp ("udp", communicator_name)) && ((0 == strcmp ("rekey", - test_name)) - || - (0 == strcmp ( - "backchannel", - test_name))) ) - { - stats[i] = GNUNET_STATISTICS_create ("C-UDP", - cfg_peers[i]); - } - else if ((0 == strcmp ("bidirect", test_name))) - { - bidirect = GNUNET_YES; - } - } - GNUNET_SCHEDULER_add_shutdown (&do_shutdown, - NULL); -} - - -int -main (int argc, - char *const *argv) -{ - struct GNUNET_CRYPTO_EddsaPrivateKey *private_key; - char *test_mode; - char *cfg_peer; - - iterations_left[0] = TOTAL_ITERATIONS; - iterations_left[1] = TOTAL_ITERATIONS; - phase[0] = TP_INIT; - phase[1] = TP_INIT; - ret = 1; - test_name = GNUNET_STRINGS_get_suffix_from_binary_name (argv[0]); - communicator_name = strchr (test_name, '-'); - communicator_name[0] = '\0'; - communicator_name++; - test_mode = test_name; - - GNUNET_asprintf (&communicator_binary, - "gnunet-communicator-%s", - communicator_name); - - if (GNUNET_OK != - GNUNET_log_setup ("test_communicator_basic", - "DEBUG", - NULL)) - { - fprintf (stderr, "Unable to setup log\n"); - GNUNET_break (0); - return 2; - } - for (unsigned int i = 0; i < NUM_PEERS; i++) - { - GNUNET_asprintf ((&cfg_peer), - "test_communicator_%s_%s_peer%u.conf", - communicator_name, test_mode, i + 1); - cfg_peers_name[i] = cfg_peer; - cfg_peers[i] = GNUNET_CONFIGURATION_create (); - if (GNUNET_YES == - GNUNET_DISK_file_test (cfg_peers_name[i])) - { - if (GNUNET_SYSERR == - GNUNET_CONFIGURATION_load (cfg_peers[i], - cfg_peers_name[i])) - { - fprintf (stderr, - "Malformed configuration file `%s', exiting ...\n", - cfg_peers_name[i]); - return 1; - } - } - else - { - if (GNUNET_SYSERR == - GNUNET_CONFIGURATION_load (cfg_peers[i], - NULL)) - { - fprintf (stderr, - "Configuration file %s does not exist, exiting ...\n", - cfg_peers_name[i]); - return 1; - } - } - private_key = - GNUNET_CRYPTO_eddsa_key_create_from_configuration (cfg_peers[i]); - if (NULL == private_key) - { - LOG (GNUNET_ERROR_TYPE_ERROR, - "Unable to get peer ID\n"); - return 1; - } - GNUNET_CRYPTO_eddsa_key_get_public (private_key, - &peer_id[i].public_key); - GNUNET_free (private_key); - LOG (GNUNET_ERROR_TYPE_INFO, - "Identity of peer %u is %s\n", - i, - GNUNET_i2s_full (&peer_id[i])); - } - if (GNUNET_OK != - GNUNET_CONFIGURATION_get_value_number (cfg_peers[0], - TEST_SECTION, - "ALLOWED_PACKET_LOSS_SHORT", - &allowed_packet_loss_short)) - allowed_packet_loss_short = ALLOWED_PACKET_LOSS; - if (GNUNET_OK != - GNUNET_CONFIGURATION_get_value_number (cfg_peers[0], - TEST_SECTION, - "ALLOWED_PACKET_LOSS_LONG", - &allowed_packet_loss_long)) - allowed_packet_loss_long = ALLOWED_PACKET_LOSS; - if (GNUNET_OK != - GNUNET_CONFIGURATION_get_value_number (cfg_peers[0], - TEST_SECTION, - "BURST_PACKETS_SHORT", - &burst_packets_short)) - burst_packets_short = BURST_PACKETS; - if (GNUNET_OK != - GNUNET_CONFIGURATION_get_value_number (cfg_peers[0], - TEST_SECTION, - "BURST_PACKETS_LONG", - &burst_packets_long)) - burst_packets_long = BURST_PACKETS; - if (GNUNET_OK != - GNUNET_CONFIGURATION_get_value_number (cfg_peers[0], - TEST_SECTION, - "DELAY_SHORT", - &delay_short_value)) - delay_short = DELAY; - else - GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_MICROSECONDS, - delay_short_value); - if (GNUNET_OK != - GNUNET_CONFIGURATION_get_value_number (cfg_peers[0], - TEST_SECTION, - "DELAY_SHORT", - &delay_long_value)) - delay_long = DELAY; - else - GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_MICROSECONDS, - delay_long_value); - load_phase_config (); - LOG (GNUNET_ERROR_TYPE_MESSAGE, "Starting test...\n"); - LOG (GNUNET_ERROR_TYPE_DEBUG, - "argv[0]: %s\n", - argv[0]); - LOG (GNUNET_ERROR_TYPE_DEBUG, - "test_name: %s\n", - test_name); - LOG (GNUNET_ERROR_TYPE_DEBUG, - "communicator_name: %s\n", - communicator_name); - LOG (GNUNET_ERROR_TYPE_DEBUG, - "communicator_binary: %s\n", - communicator_binary); - GNUNET_SCHEDULER_run (&run, - NULL); - return ret; -} diff --git a/src/transport/test_communicator_quic_basic_peer1.conf b/src/transport/test_communicator_quic_basic_peer1.conf deleted file mode 100644 index d43752ecc..000000000 --- a/src/transport/test_communicator_quic_basic_peer1.conf +++ /dev/null @@ -1,45 +0,0 @@ -@INLINE@ test_transport_defaults.conf - -[PATHS] -GNUNET_TEST_HOME = $GNUNET_TMP/test-communicator-unix-1/ - -[PEER] -PRIVATE_KEY = $GNUNET_TMP/test-communicator-unix-1/private.key - -[transport-tcp] -PORT = 52400 - -[transport-udp] -PORT = 52401 - -[transport-quic] -PORT = 52402 - -[transport] -#PORT = 60000 -UNIXPATH = $GNUNET_RUNTIME_DIR/gnunet-service-transport_test_1.sock - -[nat] -UNIXPATH = $GNUNET_TMP/test-communicator-unix-1/nat.sock -ENABLE_IPSCAN = YES - -[peerstore] -UNIXPATH = $GNUNET_TMP/test-communicator-unix-1/peerstore.sock - -[statistics] -PORT = 22461 -UNIXPATH = $GNUNET_RUNTIME_DIR/gnunet-service-statistics_test_1.sock - -[resolver] -PORT = 62089 -UNIXPATH = $GNUNET_RUNTIME_DIR/gnunet-service-resolver_test_1.sock - -[communicator-udp] -# PREFIX = valgrind --leak-check=full --track-origins=yes --log-file=/tmp/vg_com1 -BINDTO = 60002 -DISABLE_V6 = YES -MAX_QUEUE_LENGTH=5000 - -[communicator-quic] -BINDTO = 60002 -DISABLE_V6 = YES diff --git a/src/transport/test_communicator_quic_basic_peer2.conf b/src/transport/test_communicator_quic_basic_peer2.conf deleted file mode 100644 index 4fabc8a86..000000000 --- a/src/transport/test_communicator_quic_basic_peer2.conf +++ /dev/null @@ -1,45 +0,0 @@ -@INLINE@ test_transport_defaults.conf - -[PATHS] -GNUNET_TEST_HOME = $GNUNET_TMP/test-gnunetd-plugin-transport/ - -[PEER] -PRIVATE_KEY = $GNUNET_TMP/test-communicator-unix-2/private.key - -[transport-tcp] -PORT = 52400 - -[transport-udp] -PORT = 52402 - -[transport-quic] -PORT = 52403 - -[transport] -#PORT = 60001 -UNIXPATH = $GNUNET_RUNTIME_DIR/gnunet-service-transport_test_2.sock - -[nat] -UNIXPATH = $GNUNET_TMP/test-communicator-unix-2/nat.sock - - -[peerstore] -UNIXPATH = $GNUNET_TMP/test-communicator-unix-2/peerstore.sock - -[statistics] -PORT = 22462 -UNIXPATH = $GNUNET_RUNTIME_DIR/gnunet-service-statistics_test_2.sock - -[resolver] -PORT = 62090 -UNIXPATH = $GNUNET_RUNTIME_DIR/gnunet-service-resolver_test_2.sock - -[communicator-udp] -# PREFIX = valgrind --leak-check=full --track-origins=yes --log-file=/tmp/vg_com2 -BINDTO = 60003 -DISABLE_V6 = YES -MAX_QUEUE_LENGTH=5000 - -[communicator-quic] -BINDTO = 60003 -DISABLE_V6 = YES \ No newline at end of file diff --git a/src/transport/test_communicator_tcp_basic_peer1.conf b/src/transport/test_communicator_tcp_basic_peer1.conf deleted file mode 100644 index dbc227ac6..000000000 --- a/src/transport/test_communicator_tcp_basic_peer1.conf +++ /dev/null @@ -1,48 +0,0 @@ -@INLINE@ test_transport_defaults.conf - -[test-setup] -#PHASE_LONG=NO -#PHASE_SIZE=NO -#BURST_PACKETS_SHORT=1 - -[PATHS] -GNUNET_TEST_HOME = $GNUNET_TMP/test-communicator-unix-1/ - -[PEER] -PRIVATE_KEY = $GNUNET_TMP/test-communicator-unix-1/private.key - -[transport-tcp] -PORT = 52400 - -[transport-udp] -PORT = 52401 - -[transport] -PORT = 60000 -UNIXPATH = $GNUNET_RUNTIME_DIR/gnunet-service-transport_test_1.sock - -[nat] -UNIXPATH = $GNUNET_TMP/test-communicator-unix-1/nat.sock - -[peerstore] -UNIXPATH = $GNUNET_TMP/test-communicator-unix-1/peerstore.sock - -[statistics] -PORT = 22461 -UNIXPATH = $GNUNET_RUNTIME_DIR/gnunet-service-statistics_test_1.sock - -[communicator-unix] -UNIXPATH = $GNUNET_RUNTIME_DIR/test_gnunet-communicator-unix_1.sock - -[communicator-tcp] -#PREFIX = xterm -geometry 100x85 -T peer1 -e gdb --args -#PREFIX = valgrind --leak-check=full --track-origins=yes -BINDTO = 60002 -DISABLE_V6 = YES - -[communicator-udp] -BINDTO = 60002 - -[resolver] -PORT = 62089 -UNIXPATH = $GNUNET_RUNTIME_DIR/gnunet-service-resolver_test_1.sock diff --git a/src/transport/test_communicator_tcp_basic_peer2.conf b/src/transport/test_communicator_tcp_basic_peer2.conf deleted file mode 100644 index b73157f0d..000000000 --- a/src/transport/test_communicator_tcp_basic_peer2.conf +++ /dev/null @@ -1,44 +0,0 @@ -@INLINE@ test_transport_defaults.conf - -[PATHS] -GNUNET_TEST_HOME = $GNUNET_TMP/test-gnunetd-plugin-transport/ - -[PEER] -PRIVATE_KEY = $GNUNET_TMP/test-communicator-unix-2/private.key - - -[transport-tcp] -PORT = 52400 - -[transport-udp] -PORT = 52401 - -[transport] -PORT = 60001 -UNIXPATH = $GNUNET_RUNTIME_DIR/gnunet-service-transport_test_2.sock - -[nat] -UNIXPATH = $GNUNET_TMP/test-communicator-unix-2/nat.sock - -[peerstore] -UNIXPATH = $GNUNET_TMP/test-communicator-unix-2/peerstore.sock - -[statistics] -PORT = 22462 -UNIXPATH = $GNUNET_RUNTIME_DIR/gnunet-service-statistics_test_2.sock - -[communicator-unix] -UNIXPATH = $GNUNET_RUNTIME_DIR/test_gnunet-communicator-unix_2.sock - -[communicator-tcp] -#PREFIX = xterm -geometry 100x85 -T peer2 -e gdb --args -#PREFIX = valgrind --leak-check=full --track-origins=yes -BINDTO = 60003 -DISABLE_V6 = YES - -[communicator-udp] -BINDTO = 60003 - -[resolver] -PORT = 62090 -UNIXPATH = $GNUNET_RUNTIME_DIR/gnunet-service-resolver_test_2.sock diff --git a/src/transport/test_communicator_tcp_bidirect_peer1.conf b/src/transport/test_communicator_tcp_bidirect_peer1.conf deleted file mode 100644 index dbc227ac6..000000000 --- a/src/transport/test_communicator_tcp_bidirect_peer1.conf +++ /dev/null @@ -1,48 +0,0 @@ -@INLINE@ test_transport_defaults.conf - -[test-setup] -#PHASE_LONG=NO -#PHASE_SIZE=NO -#BURST_PACKETS_SHORT=1 - -[PATHS] -GNUNET_TEST_HOME = $GNUNET_TMP/test-communicator-unix-1/ - -[PEER] -PRIVATE_KEY = $GNUNET_TMP/test-communicator-unix-1/private.key - -[transport-tcp] -PORT = 52400 - -[transport-udp] -PORT = 52401 - -[transport] -PORT = 60000 -UNIXPATH = $GNUNET_RUNTIME_DIR/gnunet-service-transport_test_1.sock - -[nat] -UNIXPATH = $GNUNET_TMP/test-communicator-unix-1/nat.sock - -[peerstore] -UNIXPATH = $GNUNET_TMP/test-communicator-unix-1/peerstore.sock - -[statistics] -PORT = 22461 -UNIXPATH = $GNUNET_RUNTIME_DIR/gnunet-service-statistics_test_1.sock - -[communicator-unix] -UNIXPATH = $GNUNET_RUNTIME_DIR/test_gnunet-communicator-unix_1.sock - -[communicator-tcp] -#PREFIX = xterm -geometry 100x85 -T peer1 -e gdb --args -#PREFIX = valgrind --leak-check=full --track-origins=yes -BINDTO = 60002 -DISABLE_V6 = YES - -[communicator-udp] -BINDTO = 60002 - -[resolver] -PORT = 62089 -UNIXPATH = $GNUNET_RUNTIME_DIR/gnunet-service-resolver_test_1.sock diff --git a/src/transport/test_communicator_tcp_bidirect_peer2.conf b/src/transport/test_communicator_tcp_bidirect_peer2.conf deleted file mode 100644 index b73157f0d..000000000 --- a/src/transport/test_communicator_tcp_bidirect_peer2.conf +++ /dev/null @@ -1,44 +0,0 @@ -@INLINE@ test_transport_defaults.conf - -[PATHS] -GNUNET_TEST_HOME = $GNUNET_TMP/test-gnunetd-plugin-transport/ - -[PEER] -PRIVATE_KEY = $GNUNET_TMP/test-communicator-unix-2/private.key - - -[transport-tcp] -PORT = 52400 - -[transport-udp] -PORT = 52401 - -[transport] -PORT = 60001 -UNIXPATH = $GNUNET_RUNTIME_DIR/gnunet-service-transport_test_2.sock - -[nat] -UNIXPATH = $GNUNET_TMP/test-communicator-unix-2/nat.sock - -[peerstore] -UNIXPATH = $GNUNET_TMP/test-communicator-unix-2/peerstore.sock - -[statistics] -PORT = 22462 -UNIXPATH = $GNUNET_RUNTIME_DIR/gnunet-service-statistics_test_2.sock - -[communicator-unix] -UNIXPATH = $GNUNET_RUNTIME_DIR/test_gnunet-communicator-unix_2.sock - -[communicator-tcp] -#PREFIX = xterm -geometry 100x85 -T peer2 -e gdb --args -#PREFIX = valgrind --leak-check=full --track-origins=yes -BINDTO = 60003 -DISABLE_V6 = YES - -[communicator-udp] -BINDTO = 60003 - -[resolver] -PORT = 62090 -UNIXPATH = $GNUNET_RUNTIME_DIR/gnunet-service-resolver_test_2.sock diff --git a/src/transport/test_communicator_tcp_rekey_peer1.conf b/src/transport/test_communicator_tcp_rekey_peer1.conf deleted file mode 100644 index 82fbf353a..000000000 --- a/src/transport/test_communicator_tcp_rekey_peer1.conf +++ /dev/null @@ -1,45 +0,0 @@ -@INLINE@ test_transport_defaults.conf - -[PATHS] -GNUNET_TEST_HOME = $GNUNET_TMP/test-communicator-unix-1/ - -[PEER] -PRIVATE_KEY = $GNUNET_TMP/test-communicator-unix-1/private.key - -[transport-tcp] -PORT = 52400 - -[transport-udp] -PORT = 52401 - -[transport] -PORT = 60000 -UNIXPATH = $GNUNET_RUNTIME_DIR/gnunet-service-transport_test_1.sock - -[nat] -UNIXPATH = $GNUNET_TMP/test-communicator-unix-1/nat.sock -ENABLE_IPSCAN = YES - -[peerstore] -UNIXPATH = $GNUNET_TMP/test-communicator-unix-1/peerstore.sock - -[statistics] -PORT = 22461 -UNIXPATH = $GNUNET_RUNTIME_DIR/gnunet-service-statistics_test_1.sock - -[resolver] -PORT = 62089 -UNIXPATH = $GNUNET_RUNTIME_DIR/gnunet-service-resolver_test_1.sock - -[communicator-unix] -UNIXPATH = $GNUNET_RUNTIME_DIR/test_gnunet-communicator-unix_1.sock - -[communicator-tcp] -#PREFIX = xterm -geometry 100x85 -T peer1 -e gdb --args -#PREFIX = valgrind --leak-check=full --track-origins=yes -BINDTO = 60002 -DISABLE_V6 = YES -REKEY_INTERVAL = 100ms - -[communicator-udp] -BINDTO = 60002 diff --git a/src/transport/test_communicator_tcp_rekey_peer2.conf b/src/transport/test_communicator_tcp_rekey_peer2.conf deleted file mode 100644 index 086a996ae..000000000 --- a/src/transport/test_communicator_tcp_rekey_peer2.conf +++ /dev/null @@ -1,45 +0,0 @@ -@INLINE@ test_transport_defaults.conf - -[PATHS] -GNUNET_TEST_HOME = $GNUNET_TMP/test-gnunetd-plugin-transport/ - -[PEER] -PRIVATE_KEY = $GNUNET_TMP/test-communicator-unix-2/private.key - - -[transport-tcp] -PORT = 52400 - -[transport-udp] -PORT = 52401 - -[transport] -PORT = 60001 -UNIXPATH = $GNUNET_RUNTIME_DIR/gnunet-service-transport_test_2.sock - -[nat] -UNIXPATH = $GNUNET_TMP/test-communicator-unix-2/nat.sock - -[peerstore] -UNIXPATH = $GNUNET_TMP/test-communicator-unix-2/peerstore.sock - -[statistics] -PORT = 22462 -UNIXPATH = $GNUNET_RUNTIME_DIR/gnunet-service-statistics_test_2.sock - -[resolver] -PORT = 62090 -UNIXPATH = $GNUNET_RUNTIME_DIR/gnunet-service-resolver_test_2.sock - -[communicator-unix] -UNIXPATH = $GNUNET_RUNTIME_DIR/test_gnunet-communicator-unix_2.sock - -[communicator-tcp] -#PREFIX = xterm -geometry 100x85 -T peer2 -e gdb --args -#PREFIX = valgrind --leak-check=full --track-origins=yes -BINDTO = 60003 -DISABLE_V6 = YES -REKEY_INTERVAL = 100ms - -[communicator-udp] -BINDTO = 60003 diff --git a/src/transport/test_communicator_udp_backchannel_peer1.conf b/src/transport/test_communicator_udp_backchannel_peer1.conf deleted file mode 100644 index 65f33bd6b..000000000 --- a/src/transport/test_communicator_udp_backchannel_peer1.conf +++ /dev/null @@ -1,48 +0,0 @@ -@INLINE@ test_transport_defaults.conf - -[PATHS] -GNUNET_TEST_HOME = $GNUNET_TMP/test-communicator-unix-1/ - -[PEER] -PRIVATE_KEY = $GNUNET_TMP/test-communicator-unix-1/private.key - -[transport-tcp] -PORT = 52400 - -[transport-udp] -PORT = 52401 - -[transport] -PORT = 60000 -UNIXPATH = $GNUNET_RUNTIME_DIR/gnunet-service-transport_test_1.sock - -[nat] -UNIXPATH = $GNUNET_TMP/test-communicator-unix-1/nat.sock -ENABLE_IPSCAN = YES - -[peerstore] -UNIXPATH = $GNUNET_TMP/test-communicator-unix-1/peerstore.sock - -[statistics] -PORT = 22461 -UNIXPATH = $GNUNET_RUNTIME_DIR/gnunet-service-statistics_test_1.sock - -[resolver] -PORT = 62089 -UNIXPATH = $GNUNET_RUNTIME_DIR/gnunet-service-resolver_test_1.sock - -[communicator-unix] -UNIXPATH = $GNUNET_RUNTIME_DIR/test_gnunet-communicator-unix_1.sock - -[communicator-tcp] -BINDTO = 60002 -DISABLE_V6 = YES - -[communicator-udp] -# PREFIX = valgrind --leak-check=full --track-origins=yes --log-file=/tmp/vg_comm1 -BINDTO = 60002 -DISABLE_V6 = YES -MAX_QUEUE_LENGTH=5000 - -[communicator-test] -BACKCHANNEL_ENABLED = YES diff --git a/src/transport/test_communicator_udp_backchannel_peer2.conf b/src/transport/test_communicator_udp_backchannel_peer2.conf deleted file mode 100644 index 9875af724..000000000 --- a/src/transport/test_communicator_udp_backchannel_peer2.conf +++ /dev/null @@ -1,48 +0,0 @@ -@INLINE@ test_transport_defaults.conf - -[PATHS] -GNUNET_TEST_HOME = $GNUNET_TMP/test-gnunetd-plugin-transport/ - -[PEER] -PRIVATE_KEY = $GNUNET_TMP/test-communicator-unix-2/private.key - - -[transport-tcp] -PORT = 52400 - -[transport-udp] -PORT = 52401 - -[transport] -PORT = 60001 -UNIXPATH = $GNUNET_RUNTIME_DIR/gnunet-service-transport_test_2.sock - -[nat] -UNIXPATH = $GNUNET_TMP/test-communicator-unix-2/nat.sock - -[peerstore] -UNIXPATH = $GNUNET_TMP/test-communicator-unix-2/peerstore.sock - -[statistics] -PORT = 22462 -UNIXPATH = $GNUNET_RUNTIME_DIR/gnunet-service-statistics_test_2.sock - -[resolver] -PORT = 62090 -UNIXPATH = $GNUNET_RUNTIME_DIR/gnunet-service-resolver_test_2.sock - -[communicator-unix] -UNIXPATH = $GNUNET_RUNTIME_DIR/test_gnunet-communicator-unix_2.sock - -[communicator-tcp] -BINDTO = 60003 -DISABLE_V6 = YES - -[communicator-udp] -# PREFIX = valgrind --leak-check=full --track-origins=yes --log-file=/tmp/vg_comm2 -BINDTO = 60003 -DISABLE_V6 = YES -MAX_QUEUE_LENGTH=5000 - -[communicator-test] -BACKCHANNEL_ENABLED = YES diff --git a/src/transport/test_communicator_udp_basic_peer1.conf b/src/transport/test_communicator_udp_basic_peer1.conf deleted file mode 100644 index 6968b3aef..000000000 --- a/src/transport/test_communicator_udp_basic_peer1.conf +++ /dev/null @@ -1,39 +0,0 @@ -@INLINE@ test_transport_defaults.conf - -[PATHS] -GNUNET_TEST_HOME = $GNUNET_TMP/test-communicator-unix-1/ - -[PEER] -PRIVATE_KEY = $GNUNET_TMP/test-communicator-unix-1/private.key - -[transport-tcp] -PORT = 52400 - -[transport-udp] -PORT = 52401 - -[transport] -#PORT = 60000 -UNIXPATH = $GNUNET_RUNTIME_DIR/gnunet-service-transport_test_1.sock - -[nat] -UNIXPATH = $GNUNET_TMP/test-communicator-unix-1/nat.sock -ENABLE_IPSCAN = YES - -[peerstore] -UNIXPATH = $GNUNET_TMP/test-communicator-unix-1/peerstore.sock - -[statistics] -PORT = 22461 -UNIXPATH = $GNUNET_RUNTIME_DIR/gnunet-service-statistics_test_1.sock - -[resolver] -PORT = 62089 -UNIXPATH = $GNUNET_RUNTIME_DIR/gnunet-service-resolver_test_1.sock - -[communicator-udp] -# PREFIX = valgrind --leak-check=full --track-origins=yes --log-file=/tmp/vg_com1 -BINDTO = 60002 -DISABLE_V6 = YES -MAX_QUEUE_LENGTH=5000 - diff --git a/src/transport/test_communicator_udp_basic_peer2.conf b/src/transport/test_communicator_udp_basic_peer2.conf deleted file mode 100644 index 781bfa7da..000000000 --- a/src/transport/test_communicator_udp_basic_peer2.conf +++ /dev/null @@ -1,39 +0,0 @@ -@INLINE@ test_transport_defaults.conf - -[PATHS] -GNUNET_TEST_HOME = $GNUNET_TMP/test-gnunetd-plugin-transport/ - -[PEER] -PRIVATE_KEY = $GNUNET_TMP/test-communicator-unix-2/private.key - - -[transport-tcp] -PORT = 52400 - -[transport-udp] -PORT = 52402 - -[transport] -#PORT = 60001 -UNIXPATH = $GNUNET_RUNTIME_DIR/gnunet-service-transport_test_2.sock - -[nat] -UNIXPATH = $GNUNET_TMP/test-communicator-unix-2/nat.sock - - -[peerstore] -UNIXPATH = $GNUNET_TMP/test-communicator-unix-2/peerstore.sock - -[statistics] -PORT = 22462 -UNIXPATH = $GNUNET_RUNTIME_DIR/gnunet-service-statistics_test_2.sock - -[resolver] -PORT = 62090 -UNIXPATH = $GNUNET_RUNTIME_DIR/gnunet-service-resolver_test_2.sock - -[communicator-udp] -# PREFIX = valgrind --leak-check=full --track-origins=yes --log-file=/tmp/vg_com2 -BINDTO = 60003 -DISABLE_V6 = YES -MAX_QUEUE_LENGTH=5000 diff --git a/src/transport/test_communicator_udp_rekey_peer1.conf b/src/transport/test_communicator_udp_rekey_peer1.conf deleted file mode 100644 index 9b0fa7497..000000000 --- a/src/transport/test_communicator_udp_rekey_peer1.conf +++ /dev/null @@ -1,52 +0,0 @@ -@INLINE@ test_transport_defaults.conf - -[PATHS] -GNUNET_TEST_HOME = $GNUNET_TMP/test-communicator-unix-1/ - -[PEER] -PRIVATE_KEY = $GNUNET_TMP/test-communicator-unix-1/private.key - -[transport-tcp] -PORT = 52400 - -[transport-udp] -PORT = 52401 - -[transport] -PORT = 60000 -UNIXPATH = $GNUNET_RUNTIME_DIR/gnunet-service-transport_test_1.sock - -[nat] -UNIXPATH = $GNUNET_TMP/test-communicator-unix-1/nat.sock -ENABLE_IPSCAN = YES - -[peerstore] -UNIXPATH = $GNUNET_TMP/test-communicator-unix-1/peerstore.sock - -[statistics] -PORT = 22461 -UNIXPATH = $GNUNET_RUNTIME_DIR/gnunet-service-statistics_test_1.sock - -[resolver] -PORT = 62089 -UNIXPATH = $GNUNET_RUNTIME_DIR/gnunet-service-resolver_test_1.sock - -[communicator-unix] -UNIXPATH = $GNUNET_RUNTIME_DIR/test_gnunet-communicator-unix_1.sock - -[communicator-tcp] -BINDTO = 60002 -DISABLE_V6 = YES -REKEY_INTERVAL = 100ms - -[communicator-udp] -#PREFIX = xterm -geometry 100x85 -T peer1 -e gdb --args -#PREFIX = valgrind --leak-check=full --track-origins=yes -BINDTO = 60002 -DISABLE_V6 = YES -MAX_QUEUE_LENGTH=5000 -REKEY_INTERVAL = 100ms -REKEY_MAX_BYTES=9KiB - -[communicator-test] -BACKCHANNEL_ENABLED = YES diff --git a/src/transport/test_communicator_udp_rekey_peer2.conf b/src/transport/test_communicator_udp_rekey_peer2.conf deleted file mode 100644 index 383ab19d2..000000000 --- a/src/transport/test_communicator_udp_rekey_peer2.conf +++ /dev/null @@ -1,52 +0,0 @@ -@INLINE@ test_transport_defaults.conf - -[PATHS] -GNUNET_TEST_HOME = $GNUNET_TMP/test-gnunetd-plugin-transport/ - -[PEER] -PRIVATE_KEY = $GNUNET_TMP/test-communicator-unix-2/private.key - - -[transport-tcp] -PORT = 52400 - -[transport-udp] -PORT = 52401 - -[transport] -PORT = 60001 -UNIXPATH = $GNUNET_RUNTIME_DIR/gnunet-service-transport_test_2.sock - -[nat] -UNIXPATH = $GNUNET_TMP/test-communicator-unix-2/nat.sock - -[peerstore] -UNIXPATH = $GNUNET_TMP/test-communicator-unix-2/peerstore.sock - -[statistics] -PORT = 22462 -UNIXPATH = $GNUNET_RUNTIME_DIR/gnunet-service-statistics_test_2.sock - -[resolver] -PORT = 62090 -UNIXPATH = $GNUNET_RUNTIME_DIR/gnunet-service-resolver_test_2.sock - -[communicator-unix] -UNIXPATH = $GNUNET_RUNTIME_DIR/test_gnunet-communicator-unix_2.sock - -[communicator-tcp] -BINDTO = 60003 -DISABLE_V6 = YES -REKEY_INTERVAL = 100ms - -[communicator-udp] -#PREFIX = xterm -geometry 100x85 -T peer1 -e gdb --args -#PREFIX = valgrind --leak-check=full --track-origins=yes -BINDTO = 60003 -DISABLE_V6 = YES -MAX_QUEUE_LENGTH=5000 -REKEY_INTERVAL = 100ms -REKEY_MAX_BYTES=9KiB - -[communicator-test] -BACKCHANNEL_ENABLED = YES diff --git a/src/transport/test_communicator_unix_basic_peer1.conf b/src/transport/test_communicator_unix_basic_peer1.conf deleted file mode 100644 index 13ba2d16b..000000000 --- a/src/transport/test_communicator_unix_basic_peer1.conf +++ /dev/null @@ -1,43 +0,0 @@ -@INLINE@ test_transport_defaults.conf - -[PATHS] -GNUNET_TEST_HOME = $GNUNET_TMP/test-communicator-unix-1/ - -[PEER] -PRIVATE_KEY = $GNUNET_TMP/test-communicator-unix-1/private.key - -[transport-tcp] -PORT = 52400 - -[transport-udp] -PORT = 52401 - -[transport] -PORT = 60000 -UNIXPATH = $GNUNET_RUNTIME_DIR/gnunet-service-transport_test_1.sock - -[nat] -UNIXPATH = $GNUNET_TMP/communicator-unix-1/nat.sock -ENABLE_IPSCAN = YES - -[peerstore] -UNIXPATH = $GNUNET_TMP/test-communicator-unix-1/peerstore.sock - -[statistics] -PORT = 22461 -UNIXPATH = $GNUNET_RUNTIME_DIR/gnunet-service-statistics_test_1.sock - -[resolver] -PORT = 62089 -UNIXPATH = $GNUNET_RUNTIME_DIR/gnunet-service-resolver_test_1.sock - -[communicator-unix] -#PREFIX = xterm -geometry 100x85 -T peer1 -e gdb --args -#PREFIX = valgrind --leak-check=full --track-origins=yes -UNIXPATH = $GNUNET_RUNTIME_DIR/communicator-unix-1.sock - -[communicator-tcp] -BINDTO = 60002 - -[communicator-udp] -BINDTO = 60002 diff --git a/src/transport/test_communicator_unix_basic_peer2.conf b/src/transport/test_communicator_unix_basic_peer2.conf deleted file mode 100644 index 727e844a7..000000000 --- a/src/transport/test_communicator_unix_basic_peer2.conf +++ /dev/null @@ -1,43 +0,0 @@ -@INLINE@ test_transport_defaults.conf - -[PATHS] -GNUNET_TEST_HOME = $GNUNET_TMP/test-gnunetd-plugin-transport/ - -[PEER] -PRIVATE_KEY = $GNUNET_TMP/test-communicator-unix-2/private.key - - -[transport-tcp] -PORT = 52400 - -[transport-udp] -PORT = 52401 - -[transport] -PORT = 60001 -UNIXPATH = $GNUNET_RUNTIME_DIR/gnunet-service-transport_test_2.sock - -[nat] -UNIXPATH = $GNUNET_TMP/communicator-unix-2/nat.sock - -[peerstore] -UNIXPATH = $GNUNET_TMP/test-communicator-unix-2/peerstore.sock - -[statistics] -PORT = 22462 -UNIXPATH = $GNUNET_RUNTIME_DIR/gnunet-service-statistics_test_2.sock - -[resolver] -PORT = 62090 -UNIXPATH = $GNUNET_RUNTIME_DIR/gnunet-service-resolver_test_2.sock - -[communicator-unix] -#PREFIX = xterm -geometry 100x85 -T peer2 -e gdb --args -#PREFIX = valgrind --leak-check=full --track-origins=yes -UNIXPATH = $GNUNET_RUNTIME_DIR/communicator-unix-2.sock - -[communicator-tcp] -BINDTO = 60003 - -[communicator-udp] -BINDTO = 60003 diff --git a/src/transport/test_delay b/src/transport/test_delay deleted file mode 100755 index 5f82b65fb..000000000 --- a/src/transport/test_delay +++ /dev/null @@ -1,19 +0,0 @@ -#!/bin/sh - -TEMP=$(getopt t: "$*") - -if [ $? != 0 ] ; then - exit 1 -fi - -eval set -- "$TEMP" - -while true ; do - case "$1" in - (-t) sleep "$2" ; shift 2 ;; - (--) shift ; break ;; - (*) echo "Error parsing getopt output" ; exit 1 ;; - esac -done -echo "exec $@" -exec "$@" diff --git a/src/transport/test_plugin_hostkey b/src/transport/test_plugin_hostkey deleted file mode 100644 index e7b1ad012..000000000 Binary files a/src/transport/test_plugin_hostkey and /dev/null differ diff --git a/src/transport/test_plugin_hostkey.ecc b/src/transport/test_plugin_hostkey.ecc deleted file mode 100644 index 18641b798..000000000 --- a/src/transport/test_plugin_hostkey.ecc +++ /dev/null @@ -1 +0,0 @@ -‚”ˆÖ’ÛËy¢/HÒc ȃ§¿±;¼»f?¶@…~áJ \ No newline at end of file diff --git a/src/transport/test_tng_defaults.conf b/src/transport/test_tng_defaults.conf deleted file mode 100644 index e37dab25e..000000000 --- a/src/transport/test_tng_defaults.conf +++ /dev/null @@ -1,14 +0,0 @@ -@INLINE@ ../../contrib/conf/gnunet/no_forcestart.conf - -[PATHS] -GNUNET_TEST_HOME = $GNUNET_TMP/test-tng/ - -[transport] -# PREFIX = valgrind - -[nat] -DISABLEV6 = NO -RETURN_LOCAL_ADDRESSES = YES -BINDTO = 127.0.0.1 -INTERNAL_ADDRESS = 127.0.0.1 -EXTERNAL_ADDRESS = 127.0.0.1 diff --git a/src/transport/test_transport_address_switch.c b/src/transport/test_transport_address_switch.c deleted file mode 100644 index ce5117bd1..000000000 --- a/src/transport/test_transport_address_switch.c +++ /dev/null @@ -1,433 +0,0 @@ -/* - This file is part of GNUnet. - Copyright (C) 2009, 2010, 2011, 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 . - - SPDX-License-Identifier: AGPL3.0-or-later - */ -/** - * @file transport/test_transport_address_switch.c - * @brief base test case for transport implementations - * - * This test case tests if peers can successfully switch addresses when - * connected for plugins supporting multiple addresses by monitoring transport's - * statistic values. - * - * This test starts 2 peers and connects them. When connected test messages - * are transmitted from peer 2 to peer 1. The test monitors transport's - * statistics values for information about address switch attempts. - * - * The test passes with success if one of the peers could successfully switch - * addresses in connected state and a test message was successfully transmitted - * after this switch. - * - * Since it is not possible to trigger an address switch from outside, - * the test returns "77" (skipped) when no address switching attempt - * takes place. It fails if an address switch attempt fails. - * - * NOTE: The test seems largely useless right now, as we simply NEVER - * switch addresses under the test conditions. However, it may be a - * good starting point for a future test. For now, it always times - * out and returns "77" (skipped), so we set the timeout suitably low. - */ -#include "platform.h" -#include "gnunet_transport_service.h" -#include "gnunet_ats_service.h" -#include "transport-testing.h" - - -/** - * Testcase timeout (set aggressively as we know this test doesn't work right now) - */ -#define TIMEOUT GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS, 30) - - -static struct GNUNET_TRANSPORT_TESTING_ConnectCheckContext *ccc; - -static struct GNUNET_SCHEDULER_Task *measure_task; - - -/** - * Statistics we track per peer. - */ -struct PeerStats -{ - struct GNUNET_STATISTICS_Handle *stat; - - unsigned int addresses_avail; - - unsigned int switch_attempts; - - unsigned int switch_success; - - unsigned int switch_fail; -}; - -static struct PeerStats stats[2]; - -/* Amount of data transferred since last switch attempt */ -static unsigned long long bytes_sent_after_switch; - -static unsigned long long bytes_recv_after_switch; - - -static int -stat_start_attempt_cb (void *cls, - const char *subsystem, - const char *name, - uint64_t value, - int is_persistent) -{ - struct PeerStats *stat = cls; - - stat->switch_attempts++; - GNUNET_log (GNUNET_ERROR_TYPE_INFO, "Switch attempted (%p)", stat); - bytes_recv_after_switch = 0; - bytes_sent_after_switch = 0; - - return GNUNET_OK; -} - - -static int -stat_success_attempt_cb (void *cls, - const char *subsystem, - const char *name, - uint64_t value, - int is_persistent) -{ - struct PeerStats *stat = cls; - - stat->switch_success++; - GNUNET_log (GNUNET_ERROR_TYPE_INFO, "Switch succeeded (%p)", stat); - return GNUNET_OK; -} - - -static int -stat_fail_attempt_cb (void *cls, - const char *subsystem, - const char *name, - uint64_t value, - int is_persistent) -{ - struct PeerStats *stat = cls; - - if (value == 0) - return GNUNET_OK; - - stat->switch_fail++; - GNUNET_log (GNUNET_ERROR_TYPE_WARNING, "Switch failed (%p)", stat); - return GNUNET_OK; -} - - -static int -stat_addresses_available (void *cls, - const char *subsystem, - const char *name, - uint64_t value, - int is_persistent) -{ - struct PeerStats *stat = cls; - - stat->addresses_avail++; - return GNUNET_OK; -} - - -/** - * List of statistics entries we care about. - */ -static struct WatchEntry -{ - /** - * Name of the statistic we watch. - */ - const char *stat_name; - - /** - * Handler to register; - */ - GNUNET_STATISTICS_Iterator stat_handler; -} watches[] = -{ { "# Attempts to switch addresses", &stat_start_attempt_cb }, - { "# Successful attempts to switch addresses", &stat_success_attempt_cb }, - { "# Failed attempts to switch addresses (failed to send CONNECT CONT)", - &stat_fail_attempt_cb }, - { "# Failed attempts to switch addresses (failed to send CONNECT)", - &stat_fail_attempt_cb }, - { "# Failed attempts to switch addresses (no response)", - &stat_fail_attempt_cb }, - { "# transport addresses", &stat_addresses_available }, - { NULL, NULL } }; - - -static void -custom_shutdown (void *cls) -{ - int result; - - if (NULL != measure_task) - { - GNUNET_SCHEDULER_cancel (measure_task); - measure_task = NULL; - } - if (0 == stats[0].switch_attempts + stats[1].switch_attempts) - { - GNUNET_log (GNUNET_ERROR_TYPE_WARNING, - "Test did not work, as peers didn't switch (flawed testcase)!\n"); - ccc->global_ret = 77; - } - else - { - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - "Fail (timeout)! No transmission after switch! Stopping peers\n"); - ccc->global_ret = 77; /* GNUNET_SYSERR; */ - } - - /* stop statistics */ - for (unsigned int i = 0; i < 2; i++) - { - if (NULL != stats[i].stat) - { - for (unsigned int j = 0; NULL != watches[j].stat_name; j++) - GNUNET_assert (GNUNET_OK == - GNUNET_STATISTICS_watch_cancel (stats[i].stat, - "transport", - watches[j].stat_name, - watches[j].stat_handler, - &stats[i])); - GNUNET_STATISTICS_destroy (stats[i].stat, GNUNET_NO); - stats[i].stat = NULL; - } - } - - result = 0; - fprintf (stderr, "\n"); - if (stats[0].switch_attempts > 0) - { - fprintf ( - stderr, - "Peer 1 tried %u times to switch and succeeded %u times, failed %u times\n", - stats[0].switch_attempts, - stats[0].switch_success, - stats[0].switch_fail); - if (stats[0].switch_success != stats[0].switch_attempts) - { - GNUNET_break (0); - result++; - } - } - else if (stats[0].addresses_avail > 1) - { - fprintf (stderr, - "Peer 1 had %u addresses available, but did not try to switch\n", - stats[0].addresses_avail); - } - if (stats[1].switch_attempts > 0) - { - fprintf ( - stderr, - "Peer 2 tried %u times to switch and succeeded %u times, failed %u times\n", - stats[1].switch_attempts, - stats[1].switch_success, - stats[1].switch_fail); - if (stats[1].switch_success != stats[1].switch_attempts) - { - GNUNET_break (0); - result++; - } - } - else if (stats[1].addresses_avail > 1) - { - fprintf (stderr, - "Peer 2 had %u addresses available, but did not try to switch\n", - stats[1].addresses_avail); - } - - if (((stats[0].switch_attempts > 0) || (stats[1].switch_attempts > 0)) && - (bytes_sent_after_switch == 0)) - { - fprintf (stderr, "No data sent after switching!\n"); - GNUNET_break (0); - result++; - } - if (((stats[0].switch_attempts > 0) || (stats[1].switch_attempts > 0)) && - (bytes_recv_after_switch == 0)) - { - fprintf (stderr, "No data received after switching!\n"); - GNUNET_break (0); - result++; - } -#if 0 - /* This test is not really expected to pass right now... */ - if (0 != result) - ccc->global_ret = GNUNET_SYSERR; -#endif -} - - -static void -notify_receive (void *cls, - struct GNUNET_TRANSPORT_TESTING_PeerContext *receiver, - const struct GNUNET_PeerIdentity *sender, - const struct GNUNET_TRANSPORT_TESTING_TestMessage *hdr) -{ - if (GNUNET_TRANSPORT_TESTING_SIMPLE_MTYPE != ntohs (hdr->header.type)) - return; - - { - char *ps = GNUNET_strdup (GNUNET_i2s (&receiver->id)); - - GNUNET_log (GNUNET_ERROR_TYPE_INFO, - "Peer %u (`%s') got message %u of size %u from peer (`%s')\n", - receiver->no, - ps, - (uint32_t) ntohl (hdr->num), - ntohs (hdr->header.size), - GNUNET_i2s (sender)); - GNUNET_free (ps); - } - if (((stats[0].switch_attempts >= 1) || (stats[1].switch_attempts >= 1)) && - (stats[0].switch_attempts == - stats[0].switch_fail + stats[0].switch_success) && - (stats[1].switch_attempts == - stats[1].switch_fail + stats[1].switch_success)) - { - bytes_recv_after_switch += ntohs (hdr->header.size); - if ((bytes_sent_after_switch > 0) && (bytes_recv_after_switch > 0)) - { - /* A peer switched addresses and sent and received data after the - * switch operations */ - GNUNET_SCHEDULER_shutdown (); - } - } -} - - -static void -notify_send (void *cls) -{ - static uint32_t cnt; - - GNUNET_assert ( - GNUNET_OK == - GNUNET_TRANSPORT_TESTING_send (ccc->p[1], - ccc->p[0], - GNUNET_TRANSPORT_TESTING_SIMPLE_MTYPE, - GNUNET_TRANSPORT_TESTING_LARGE_MESSAGE_SIZE, - ++cnt, - ¬ify_send, - NULL)); - if (((stats[0].switch_attempts >= 1) || (stats[1].switch_attempts >= 1)) && - (stats[0].switch_attempts == - stats[0].switch_fail + stats[0].switch_success) && - (stats[1].switch_attempts == - stats[1].switch_fail + stats[1].switch_success)) - { - bytes_sent_after_switch += GNUNET_TRANSPORT_TESTING_LARGE_MESSAGE_SIZE; - } -} - - -static void -progress_indicator (void *cls) -{ - static int counter; - - measure_task = NULL; - counter++; - if ((TIMEOUT.rel_value_us / 1000 / 1000LL) < counter) - { - fprintf (stderr, "%s", ".\n"); - } - else - { - fprintf (stderr, "%s", "."); - measure_task = GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_UNIT_SECONDS, - &progress_indicator, - NULL); - } -} - - -static void -connected_cb (void *cls) -{ - for (unsigned int i = 0; i < 2; i++) - { - stats[i].stat = GNUNET_STATISTICS_create ("transport", ccc->p[i]->cfg); - if (NULL == stats[i].stat) - { - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - "Fail! Could not create statistics for peers!\n"); - ccc->global_ret = GNUNET_SYSERR; - GNUNET_SCHEDULER_shutdown (); - return; - } - for (unsigned int j = 0; NULL != watches[j].stat_name; j++) - { - GNUNET_STATISTICS_watch (stats[i].stat, - "transport", - watches[j].stat_name, - watches[j].stat_handler, - &stats[i]); - } - } - /* Show progress */ - ccc->global_ret = GNUNET_OK; - measure_task = GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_UNIT_SECONDS, - &progress_indicator, - NULL); - /* Peers are connected, start transmit test messages */ - GNUNET_assert ( - GNUNET_OK == - GNUNET_TRANSPORT_TESTING_send (ccc->p[1], - ccc->p[0], - GNUNET_TRANSPORT_TESTING_SIMPLE_MTYPE, - GNUNET_TRANSPORT_TESTING_LARGE_MESSAGE_SIZE, - 0, - ¬ify_send, - NULL)); -} - - -int -main (int argc, char *argv[]) -{ - struct GNUNET_TRANSPORT_TESTING_ConnectCheckContext my_ccc = - { .connect_continuation = &connected_cb, - .config_file = "test_transport_api_data.conf", - .rec = ¬ify_receive, - .nc = &GNUNET_TRANSPORT_TESTING_log_connect, - .shutdown_task = &custom_shutdown, - .timeout = TIMEOUT }; - - ccc = &my_ccc; - int ret; - - ret = GNUNET_TRANSPORT_TESTING_main (2, - &GNUNET_TRANSPORT_TESTING_connect_check, - ccc); - if (77 == ret) - return 77; - if (GNUNET_OK != ret) - return 1; - return 0; -} - - -/* end of test_transport_address_switch.c */ diff --git a/src/transport/test_transport_address_switch_tcp_peer1.conf b/src/transport/test_transport_address_switch_tcp_peer1.conf deleted file mode 100644 index cfcbfe41c..000000000 --- a/src/transport/test_transport_address_switch_tcp_peer1.conf +++ /dev/null @@ -1,48 +0,0 @@ -@INLINE@ template_cfg_peer1.conf -[PATHS] -GNUNET_TEST_HOME = $GNUNET_TMP/test-transport/api-tcp-p1/ - -[ats] -UNSPECIFIED_QUOTA_IN = 8 KiB -UNSPECIFIED_QUOTA_OUT = 8 KiB -# LOOPBACK -LOOPBACK_QUOTA_IN = 8 KiB -LOOPBACK_QUOTA_OUT = 8 KiB -# LAN -LAN_QUOTA_IN = 8 KiB -LAN_QUOTA_OUT = 8 KiB -# WAN -WAN_QUOTA_IN = 8 KiB -WAN_QUOTA_OUT = 8 KiB -# WLAN -WLAN_QUOTA_IN = 8 KiB -WLAN_QUOTA_OUT = 8 KiB -# BLUETOOTH -BLUETOOTH_QUOTA_IN = 8 KiB -BLUETOOTH_QUOTA_OUT = 8 KiB - -[transport-tcp] -PORT = 12000 -TIMEOUT = 5 s - -[arm] -PORT = 12005 -UNIXPATH = $GNUNET_RUNTIME_DIR/gnunet-p1-service-arm.sock - -[statistics] -PORT = 12004 -UNIXPATH = $GNUNET_RUNTIME_DIR/gnunet-p1-service-statistics.sock - -[resolver] -PORT = 12003 -UNIXPATH = $GNUNET_RUNTIME_DIR/gnunet-p1-service-resolver.sock - -[peerinfo] -PORT = 12002 -UNIXPATH = $GNUNET_RUNTIME_DIR/gnunet-p1-service-peerinfo.sock - -[transport] -PORT = 12001 -UNIXPATH = $GNUNET_RUNTIME_DIR/gnunet-p1-service-transport.sock -PLUGINS = tcp - diff --git a/src/transport/test_transport_address_switch_tcp_peer2.conf b/src/transport/test_transport_address_switch_tcp_peer2.conf deleted file mode 100644 index bda2354b6..000000000 --- a/src/transport/test_transport_address_switch_tcp_peer2.conf +++ /dev/null @@ -1,48 +0,0 @@ -@INLINE@ template_cfg_peer2.conf -[PATHS] -GNUNET_TEST_HOME = $GNUNET_TMP/test-transport/api-tcp-p2/ - -[ats] -UNSPECIFIED_QUOTA_IN = 8 KiB -UNSPECIFIED_QUOTA_OUT = 8 KiB -# LOOPBACK -LOOPBACK_QUOTA_IN = 8 KiB -LOOPBACK_QUOTA_OUT = 8 KiB -# LAN -LAN_QUOTA_IN = 8 KiB -LAN_QUOTA_OUT = 8 KiB -# WAN -WAN_QUOTA_IN = 8 KiB -WAN_QUOTA_OUT = 8 KiB -# WLAN -WLAN_QUOTA_IN = 8 KiB -WLAN_QUOTA_OUT = 8 KiB -# BLUETOOTH -BLUETOOTH_QUOTA_IN = 8 KiB -BLUETOOTH_QUOTA_OUT = 8 KiB - -[transport-tcp] -PORT = 12015 -TIMEOUT = 5 s - -[arm] -PORT = 12014 -UNIXPATH = $GNUNET_RUNTIME_DIR/gnunet-p2-service-arm.sock - -[statistics] -PORT = 12013 -UNIXPATH = $GNUNET_RUNTIME_DIR/gnunet-p2-service-statistics.sock - -[resolver] -PORT = 12012 -UNIXPATH = $GNUNET_RUNTIME_DIR/gnunet-p2-service-resolver.sock - -[peerinfo] -PORT = 12011 -UNIXPATH = $GNUNET_RUNTIME_DIR/gnunet-p2-service-peerinfo.sock - -[transport] -PORT = 12010 -PLUGINS = tcp -UNIXPATH = $GNUNET_RUNTIME_DIR/gnunet-p2-service-transport.sock - diff --git a/src/transport/test_transport_address_switch_udp_peer1.conf b/src/transport/test_transport_address_switch_udp_peer1.conf deleted file mode 100644 index bdc3a2253..000000000 --- a/src/transport/test_transport_address_switch_udp_peer1.conf +++ /dev/null @@ -1,48 +0,0 @@ -@INLINE@ template_cfg_peer1.conf -[PATHS] -GNUNET_TEST_HOME = $GNUNET_TMP/test-transport/api-tcp-p1/ - -[ats] -UNSPECIFIED_QUOTA_IN = 8 KiB -UNSPECIFIED_QUOTA_OUT = 8 KiB -# LOOPBACK -LOOPBACK_QUOTA_IN = 8 KiB -LOOPBACK_QUOTA_OUT = 8 KiB -# LAN -LAN_QUOTA_IN = 8 KiB -LAN_QUOTA_OUT = 8 KiB -# WAN -WAN_QUOTA_IN = 8 KiB -WAN_QUOTA_OUT = 8 KiB -# WLAN -WLAN_QUOTA_IN = 8 KiB -WLAN_QUOTA_OUT = 8 KiB -# BLUETOOTH -BLUETOOTH_QUOTA_IN = 8 KiB -BLUETOOTH_QUOTA_OUT = 8 KiB - -[transport-tcp] -PORT = 12000 -TIMEOUT = 5 s - -[arm] -PORT = 12005 -UNIXPATH = $GNUNET_RUNTIME_DIR/gnunet-p1-service-arm.sock - -[statistics] -PORT = 12004 -UNIXPATH = $GNUNET_RUNTIME_DIR/gnunet-p1-service-statistics.sock - -[resolver] -PORT = 12003 -UNIXPATH = $GNUNET_RUNTIME_DIR/gnunet-p1-service-resolver.sock - -[peerinfo] -PORT = 12002 -UNIXPATH = $GNUNET_RUNTIME_DIR/gnunet-p1-service-peerinfo.sock - -[transport] -PORT = 12001 -UNIXPATH = $GNUNET_RUNTIME_DIR/gnunet-p1-service-transport.sock -PLUGINS = udp - diff --git a/src/transport/test_transport_address_switch_udp_peer2.conf b/src/transport/test_transport_address_switch_udp_peer2.conf deleted file mode 100644 index ae6397db4..000000000 --- a/src/transport/test_transport_address_switch_udp_peer2.conf +++ /dev/null @@ -1,48 +0,0 @@ -@INLINE@ template_cfg_peer2.conf -[PATHS] -GNUNET_TEST_HOME = $GNUNET_TMP/test-transport/api-tcp-p2/ - -[ats] -UNSPECIFIED_QUOTA_IN = 8 KiB -UNSPECIFIED_QUOTA_OUT = 8 KiB -# LOOPBACK -LOOPBACK_QUOTA_IN = 8 KiB -LOOPBACK_QUOTA_OUT = 8 KiB -# LAN -LAN_QUOTA_IN = 8 KiB -LAN_QUOTA_OUT = 8 KiB -# WAN -WAN_QUOTA_IN = 8 KiB -WAN_QUOTA_OUT = 8 KiB -# WLAN -WLAN_QUOTA_IN = 8 KiB -WLAN_QUOTA_OUT = 8 KiB -# BLUETOOTH -BLUETOOTH_QUOTA_IN = 8 KiB -BLUETOOTH_QUOTA_OUT = 8 KiB - -[transport-tcp] -PORT = 12015 -TIMEOUT = 5 s - -[arm] -PORT = 12014 -UNIXPATH = $GNUNET_RUNTIME_DIR/gnunet-p2-service-arm.sock - -[statistics] -PORT = 12013 -UNIXPATH = $GNUNET_RUNTIME_DIR/gnunet-p2-service-statistics.sock - -[resolver] -PORT = 12012 -UNIXPATH = $GNUNET_RUNTIME_DIR/gnunet-p2-service-resolver.sock - -[peerinfo] -PORT = 12011 -UNIXPATH = $GNUNET_RUNTIME_DIR/gnunet-p2-service-peerinfo.sock - -[transport] -PORT = 12010 -PLUGINS = udp -UNIXPATH = $GNUNET_RUNTIME_DIR/gnunet-p2-service-transport.sock - diff --git a/src/transport/test_transport_api.c b/src/transport/test_transport_api.c deleted file mode 100644 index 5f5e03a9e..000000000 --- a/src/transport/test_transport_api.c +++ /dev/null @@ -1,126 +0,0 @@ -/* - This file is part of GNUnet. - Copyright (C) 2009, 2010, 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 . - - SPDX-License-Identifier: AGPL3.0-or-later - */ -/** - * @file transport/test_transport_api.c - * @brief base test case for transport implementations - * @author Christian Grothoff - * - * This test case serves as a base for tcp, udp, and udp-nat - * transport test cases. Based on the executable being run - * the correct test case will be performed. Conservation of - * C code apparently. - */ -#include "platform.h" -#include "gnunet_transport_service.h" -#include "transport-testing.h" - -/** - * How long until we give up on transmitting the message? - */ -#define TIMEOUT GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS, 30) - -static struct GNUNET_TRANSPORT_TESTING_ConnectCheckContext *ccc; - - -static void -notify_receive (void *cls, - struct GNUNET_TRANSPORT_TESTING_PeerContext *receiver, - const struct GNUNET_PeerIdentity *sender, - const struct GNUNET_TRANSPORT_TESTING_TestMessage *message) -{ - { - char *ps = GNUNET_strdup (GNUNET_i2s (&receiver->id)); - - GNUNET_log (GNUNET_ERROR_TYPE_INFO, - "Peer %u (`%s') received message of type %d and size %u size from peer %s!\n", - receiver->no, - ps, - ntohs (message->header.type), - ntohs (message->header.size), - GNUNET_i2s (sender)); - GNUNET_free (ps); - } - - if ((GNUNET_TRANSPORT_TESTING_SIMPLE_MTYPE == ntohs (message->header.type)) && - (GNUNET_TRANSPORT_TESTING_LARGE_MESSAGE_SIZE == ntohs ( - message->header.size))) - { - ccc->global_ret = GNUNET_OK; - GNUNET_SCHEDULER_shutdown (); - } - else - { - GNUNET_break (0); - ccc->global_ret = GNUNET_SYSERR; - GNUNET_SCHEDULER_shutdown (); - } -} - - -/** - * Runs the test. - * - * @param argv the argv argument from main() - * @param bi_directional should we try to establish connections - * in both directions simultaneously? - */ -static int -test (char *argv[], - int bi_directional) -{ - struct GNUNET_TRANSPORT_TESTING_SendClosure sc = { - .num_messages = 1 - }; - struct GNUNET_TRANSPORT_TESTING_ConnectCheckContext my_ccc = { - .connect_continuation = &GNUNET_TRANSPORT_TESTING_large_send, - .connect_continuation_cls = &sc, - .config_file = "test_transport_api_data.conf", - .rec = ¬ify_receive, - .nc = &GNUNET_TRANSPORT_TESTING_log_connect, - .nd = &GNUNET_TRANSPORT_TESTING_log_disconnect, - .timeout = TIMEOUT, - .bi_directional = bi_directional - }; - - ccc = &my_ccc; - sc.ccc = ccc; - if (GNUNET_OK != - GNUNET_TRANSPORT_TESTING_main (2, - &GNUNET_TRANSPORT_TESTING_connect_check, - ccc)) - return 1; - return 0; -} - - -int -main (int argc, - char *argv[]) -{ - if ((0 != test (argv, - GNUNET_NO)) || - (0 != test (argv, - GNUNET_YES))) - return 1; - return 0; -} - - -/* end of test_transport_api.c */ diff --git a/src/transport/test_transport_api2.c b/src/transport/test_transport_api2.c deleted file mode 100644 index 4d423f7c2..000000000 --- a/src/transport/test_transport_api2.c +++ /dev/null @@ -1,126 +0,0 @@ -/* - This file is part of GNUnet. - Copyright (C) 2009, 2010, 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 . - - SPDX-License-Identifier: AGPL3.0-or-later - */ -/** - * @file transport/test_transport_api.c - * @brief base test case for transport implementations - * @author Christian Grothoff - * - * This test case serves as a base for tcp, udp, and udp-nat - * transport test cases. Based on the executable being run - * the correct test case will be performed. Conservation of - * C code apparently. - */ -#include "platform.h" -// #include "gnunet_transport_service.h" -#include "transport-testing2.h" - -/** - * How long until we give up on transmitting the message? - */ -#define TIMEOUT GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS, 30) - -static struct GNUNET_TRANSPORT_TESTING_ConnectCheckContext *ccc; - - -static void -notify_receive (void *cls, - struct GNUNET_TRANSPORT_TESTING_PeerContext *receiver, - const struct GNUNET_PeerIdentity *sender, - const struct GNUNET_TRANSPORT_TESTING_TestMessage *message) -{ - { - char *ps = GNUNET_strdup (GNUNET_i2s (&receiver->id)); - - GNUNET_log (GNUNET_ERROR_TYPE_INFO, - "Peer %u (`%s') received message of type %d and size %u size from peer %s!\n", - receiver->no, - ps, - ntohs (message->header.type), - ntohs (message->header.size), - GNUNET_i2s (sender)); - GNUNET_free (ps); - } - - if ((GNUNET_TRANSPORT_TESTING_SIMPLE_MTYPE == ntohs (message->header.type)) && - (GNUNET_TRANSPORT_TESTING_LARGE_MESSAGE_SIZE == ntohs ( - message->header.size))) - { - ccc->global_ret = GNUNET_OK; - GNUNET_SCHEDULER_shutdown (); - } - else - { - GNUNET_break (0); - ccc->global_ret = GNUNET_SYSERR; - GNUNET_SCHEDULER_shutdown (); - } -} - - -/** - * Runs the test. - * - * @param argv the argv argument from main() - * @param bi_directional should we try to establish connections - * in both directions simultaneously? - */ -static int -test (char *argv[], - int bi_directional) -{ - struct GNUNET_TRANSPORT_TESTING_SendClosure sc = { - .num_messages = 1 - }; - struct GNUNET_TRANSPORT_TESTING_ConnectCheckContext my_ccc = { - .connect_continuation = &GNUNET_TRANSPORT_TESTING_large_send, - .connect_continuation_cls = &sc, - .config_file = "test_transport_api_data.conf", - .rec = ¬ify_receive, - .nc = &GNUNET_TRANSPORT_TESTING_log_connect, - .nd = &GNUNET_TRANSPORT_TESTING_log_disconnect, - .timeout = TIMEOUT, - .bi_directional = bi_directional - }; - - ccc = &my_ccc; - sc.ccc = ccc; - if (GNUNET_OK != - GNUNET_TRANSPORT_TESTING_main (2, - &GNUNET_TRANSPORT_TESTING_connect_check, - ccc)) - return 1; - return 0; -} - - -int -main (int argc, - char *argv[]) -{ - if ((0 != test (argv, - GNUNET_NO)) || - (0 != test (argv, - GNUNET_YES))) - return 1; - return 0; -} - - -/* end of test_transport_api.c */ diff --git a/src/transport/test_transport_api2_tcp_node1.conf b/src/transport/test_transport_api2_tcp_node1.conf deleted file mode 100644 index 463ec61d8..000000000 --- a/src/transport/test_transport_api2_tcp_node1.conf +++ /dev/null @@ -1,35 +0,0 @@ -@INLINE@ template_cfg_peer1.conf -[PATHS] -GNUNET_TEST_HOME = $GNUNET_TMP/test-transport/api-tcp-p1/ - -[transport] -BINARY = gnunet-service-transport -PLUGINS = tcp -#PREFIX = valgrind --leak-check=full --track-origins=yes --trace-children=yes --log-file=$GNUNET_TEST_HOME/vg_peer1-%p -UNIXPATH = $GNUNET_RUNTIME_DIR/tng-p1.sock - -[PEER] -PRIVATE_KEY = $GNUNET_RUNTIME_DIR/private.key - -[communicator-tcp] -BINARY = gnunet-communicator-tcp -BINDTO = 192.168.15.1:60002 -DISABLE_V6 = YES -IMMEDIATE_START = YES -UNIXPATH = $GNUNET_RUNTIME_DIR/tcp-comm-p1.sock -#PREFIX = valgrind --leak-check=full --track-origins=yes --trace-children=yes --log-file=$GNUNET_TEST_HOME/vg_ctpeer1-%p -#PREFIX = xterm -geometry 100x85 -T peer1 -e gdb --args - -[communicator-udp] -#PREFIX = valgrind --leak-check=full --track-origins=yes --trace-children=yes --log-file=$GNUNET_TEST_HOME/vg_cupeer1-%p -BINARY = gnunet-communicator-udp -BINDTO = 192.168.15.1:60002 -DISABLE_V6 = YES -IMMEDIATE_START = YES -UNIXPATH = $GNUNET_RUNTIME_DIR/udp-comm-p1.sock - -[peerstore] -IMMEDIATE_START = YES - -#[transport] -#PREFIX = valgrind diff --git a/src/transport/test_transport_api2_tcp_node2.conf b/src/transport/test_transport_api2_tcp_node2.conf deleted file mode 100644 index b7f92869e..000000000 --- a/src/transport/test_transport_api2_tcp_node2.conf +++ /dev/null @@ -1,22 +0,0 @@ -@INLINE@ template_cfg_peer2.conf -[PATHS] -GNUNET_TEST_HOME = $GNUNET_TMP/test-transport/api-tcp-p2/ - -[transport] -BINARY = gnunet-service-transport -#PREFIX = valgrind --log-file=$GNUNET_TEST_HOME/vg_peer2-%p -UNIXPATH = $GNUNET_RUNTIME_DIR/tng-p2.sock - -[communicator-tcp] -BINARY = gnunet-communicator-tcp -BINDTO = 192.168.15.2:60003 -DISABLE_V6 = YES -IMMEDIATE_START = YES -#PREFIX = valgrind --log-file=$GNUNET_TEST_HOME/vg_comm2-%p -UNIXPATH = $GNUNET_RUNTIME_DIR/tcp-comm-p2.sock - -[peerstore] -IMMEDIATE_START = YES - -#[transport] -#PREFIX = valgrind diff --git a/src/transport/test_transport_api2_tcp_peer1.conf b/src/transport/test_transport_api2_tcp_peer1.conf deleted file mode 100644 index 54ec9fd9f..000000000 --- a/src/transport/test_transport_api2_tcp_peer1.conf +++ /dev/null @@ -1,23 +0,0 @@ -@INLINE@ template_cfg_peer1.conf -[PATHS] -GNUNET_TEST_HOME = $GNUNET_TMP/test-transport/api-tcp-p1/ - -[transport] -BINARY = gnunet-service-transport -PLUGINS = tcp -#PREFIX = valgrind --log-file=$GNUNET_TEST_HOME/vg_peer1-%p -UNIXPATH = $GNUNET_RUNTIME_DIR/tng-p1.sock - -[communicator-tcp] -BINARY = gnunet-communicator-tcp -BINDTO = 60002 -DISABLE_V6 = YES -IMMEDIATE_START = YES -UNIXPATH = $GNUNET_RUNTIME_DIR/tcp-comm-p1.sock -#PREFIX = valgrind --log-file=$GNUNET_TEST_HOME/vg_cpeer1-%p - -[peerstore] -IMMEDIATE_START = YES - -#[transport] -#PREFIX = valgrind diff --git a/src/transport/test_transport_api2_tcp_peer2.conf b/src/transport/test_transport_api2_tcp_peer2.conf deleted file mode 100644 index 7ae5ac697..000000000 --- a/src/transport/test_transport_api2_tcp_peer2.conf +++ /dev/null @@ -1,22 +0,0 @@ -@INLINE@ template_cfg_peer2.conf -[PATHS] -GNUNET_TEST_HOME = $GNUNET_TMP/test-transport/api-tcp-p2/ - -[transport] -BINARY = gnunet-service-transport -#PREFIX = valgrind --log-file=$GNUNET_TEST_HOME/vg_peer2-%p -UNIXPATH = $GNUNET_RUNTIME_DIR/tng-p2.sock - -[communicator-tcp] -BINARY = gnunet-communicator-tcp -BINDTO = 60003 -DISABLE_V6 = YES -IMMEDIATE_START = YES -#PREFIX = valgrind --log-file=$GNUNET_TEST_HOME/vg_comm2-%p -UNIXPATH = $GNUNET_RUNTIME_DIR/tcp-comm-p2.sock - -[peerstore] -IMMEDIATE_START = YES - -#[transport] -#PREFIX = valgrind diff --git a/src/transport/test_transport_api2_tng_node.conf b/src/transport/test_transport_api2_tng_node.conf deleted file mode 100644 index c50ccf662..000000000 --- a/src/transport/test_transport_api2_tng_node.conf +++ /dev/null @@ -1,40 +0,0 @@ -@INLINE@ template_tng_cfg_peer1.conf -[PATHS] -GNUNET_TEST_HOME = $GNUNET_TMP/test-transport/api-tcp-p1/ - -[transport] -BINARY = gnunet-service-transport -#PREFIX = valgrind --leak-check=full --track-origins=yes --trace-children=yes --log-file=$GNUNET_TEST_HOME/vg_peer1-%p -UNIXPATH = $GNUNET_RUNTIME_DIR/tng-p1.sock - -[PEER] -PRIVATE_KEY = $GNUNET_RUNTIME_DIR/private.key - -[communicator-tcp] -BINARY = gnunet-communicator-tcp -BINDTO = 192.168.15.1:60002 -DISABLE_V6 = YES -IMMEDIATE_START = YES -UNIXPATH = $GNUNET_RUNTIME_DIR/tcp-comm-p1.sock -#PREFIX = valgrind --leak-check=full --track-origins=yes --trace-children=yes --log-file=$GNUNET_TEST_HOME/vg_ctpeer1-%p -#PREFIX = xterm -geometry 100x85 -T peer1 -e gdb --args - -[communicator-udp] -#PREFIX = valgrind --leak-check=full --track-origins=yes --trace-children=yes --log-file=$GNUNET_TEST_HOME/vg_cupeer1-%p -BINARY = gnunet-communicator-udp -BINDTO = 192.168.15.1:60002 -DISABLE_V6 = YES -IMMEDIATE_START = YES -UNIXPATH = $GNUNET_RUNTIME_DIR/udp-comm-p1.sock - -[peerstore] -IMMEDIATE_START = YES - -[topology] -IMMEDIATE_START = YES - -[dht] -IMMEDIATE_START = YES - -[fs] -IMMEDIATE_START = YES diff --git a/src/transport/test_transport_api_data.conf b/src/transport/test_transport_api_data.conf deleted file mode 100644 index c06235a0a..000000000 --- a/src/transport/test_transport_api_data.conf +++ /dev/null @@ -1,9 +0,0 @@ -@INLINE@ test_transport_defaults.conf -[PATHS] - -[transport-tcp] -PORT = 52094 - -[transport-udp] -PORT = 52094 - diff --git a/src/transport/test_transport_api_monitor_peers.c b/src/transport/test_transport_api_monitor_peers.c deleted file mode 100644 index c09e3782d..000000000 --- a/src/transport/test_transport_api_monitor_peers.c +++ /dev/null @@ -1,226 +0,0 @@ -/* - This file is part of GNUnet. - Copyright (C) 2009, 2010, 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 . - - SPDX-License-Identifier: AGPL3.0-or-later - */ -/** - * @file transport/test_transport_api_monitor_peers.c - * @brief base test case for transport peer monitor API - */ -#include "platform.h" -#include "gnunet_transport_service.h" -#include "transport-testing.h" - -/** - * How long until we give up on transmitting the message? - */ -#define TIMEOUT GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS, 120) - -/** - * How long until we give up on transmitting the message? - */ -#define TIMEOUT_TRANSMIT GNUNET_TIME_relative_multiply ( \ - GNUNET_TIME_UNIT_SECONDS, 30) - -#define TEST_MESSAGE_SIZE 2600 - -#define TEST_MESSAGE_TYPE 12345 - -static struct GNUNET_TRANSPORT_TESTING_ConnectCheckContext *ccc; - -static struct GNUNET_TRANSPORT_PeerMonitoringContext *pmc_p1; - -static struct GNUNET_TRANSPORT_PeerMonitoringContext *pmc_p2; - -static int p1_c; - -static int p2_c; - -static int p1_c_notify; - -static int p2_c_notify; - - -static void -custom_shutdown (void *cls) -{ - if (NULL != pmc_p1) - { - GNUNET_TRANSPORT_monitor_peers_cancel (pmc_p1); - pmc_p1 = NULL; - } - if (NULL != pmc_p2) - { - GNUNET_TRANSPORT_monitor_peers_cancel (pmc_p2); - pmc_p2 = NULL; - } -} - - -static void -notify_receive (void *cls, - struct GNUNET_TRANSPORT_TESTING_PeerContext *receiver, - const struct GNUNET_PeerIdentity *sender, - const struct GNUNET_TRANSPORT_TESTING_TestMessage *message) -{ - char *ps = GNUNET_strdup (GNUNET_i2s (&receiver->id)); - - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - "Peer %u (`%s') received message of type %d and size %u size from peer %s!\n", - receiver->no, - ps, - ntohs (message->header.type), - ntohs (message->header.size), - GNUNET_i2s (sender)); - GNUNET_free (ps); -} - - -static void -sendtask (void *cls) -{ - /* intentionally empty */ -} - - -static void -check_done () -{ - if ((GNUNET_YES == p1_c) && - (GNUNET_YES == p2_c) && - p1_c_notify && - p2_c_notify) - { - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "Both peers state to be connected\n"); - ccc->global_ret = GNUNET_OK; - GNUNET_SCHEDULER_shutdown (); - } -} - - -static void -notify_connect (void *cls, - struct GNUNET_TRANSPORT_TESTING_PeerContext *me, - const struct GNUNET_PeerIdentity *other) -{ - GNUNET_TRANSPORT_TESTING_log_connect (cls, - me, - other); - if (0 == memcmp (other, &ccc->p[0]->id, sizeof(struct GNUNET_PeerIdentity))) - { - p1_c_notify = GNUNET_YES; - } - if (0 == memcmp (other, &ccc->p[1]->id, sizeof(struct GNUNET_PeerIdentity))) - { - p2_c_notify = GNUNET_YES; - } - check_done (); -} - - -static void -monitor1_cb (void *cls, - const struct GNUNET_PeerIdentity *peer, - const struct GNUNET_HELLO_Address *address, - enum GNUNET_TRANSPORT_PeerState state, - struct GNUNET_TIME_Absolute state_timeout) -{ - if ((NULL == address) || (NULL == ccc->p[0])) - return; - - GNUNET_log (GNUNET_ERROR_TYPE_INFO, - "Monitor 1: %s %s %s\n", - GNUNET_i2s (&address->peer), - GNUNET_TRANSPORT_ps2s (state), - GNUNET_STRINGS_absolute_time_to_string (state_timeout)); - if ((0 == memcmp (&address->peer, &ccc->p[1]->id, sizeof(ccc->p[1]->id))) && - (GNUNET_YES == GNUNET_TRANSPORT_is_connected (state)) && - (GNUNET_NO == p1_c)) - { - p1_c = GNUNET_YES; - check_done (); - } -} - - -static void -monitor2_cb (void *cls, - const struct GNUNET_PeerIdentity *peer, - const struct GNUNET_HELLO_Address *address, - enum GNUNET_TRANSPORT_PeerState state, - struct GNUNET_TIME_Absolute state_timeout) -{ - if ((NULL == address) || (NULL == ccc->p[1])) - return; - - GNUNET_log (GNUNET_ERROR_TYPE_INFO, - "Monitor 2: %s %s %s\n", - GNUNET_i2s (&address->peer), - GNUNET_TRANSPORT_ps2s (state), - GNUNET_STRINGS_absolute_time_to_string (state_timeout)); - if ((0 == memcmp (&address->peer, &ccc->p[0]->id, sizeof(ccc->p[0]->id))) && - (GNUNET_YES == GNUNET_TRANSPORT_is_connected (state)) && - (GNUNET_NO == p2_c)) - { - p2_c = GNUNET_YES; - check_done (); - } -} - - -static void -start_monitors (void *cls) -{ - pmc_p1 = GNUNET_TRANSPORT_monitor_peers (ccc->p[0]->cfg, - NULL, - GNUNET_NO, - &monitor1_cb, - NULL); - pmc_p2 = GNUNET_TRANSPORT_monitor_peers (ccc->p[1]->cfg, - NULL, - GNUNET_NO, - &monitor2_cb, - NULL); -} - - -int -main (int argc, char *argv[]) -{ - struct GNUNET_TRANSPORT_TESTING_ConnectCheckContext my_ccc = { - .pre_connect_task = &start_monitors, - .connect_continuation = &sendtask, - .config_file = "test_transport_api_data.conf", - .rec = ¬ify_receive, - .nc = ¬ify_connect, - .nd = &GNUNET_TRANSPORT_TESTING_log_disconnect, - .shutdown_task = &custom_shutdown, - .timeout = TIMEOUT - }; - - ccc = &my_ccc; - if (GNUNET_OK != - GNUNET_TRANSPORT_TESTING_main (2, - &GNUNET_TRANSPORT_TESTING_connect_check, - ccc)) - return 1; - return 0; -} - - -/* end of test_transport_api_monitor_peers.c */ diff --git a/src/transport/test_transport_api_monitor_peers_peer1.conf b/src/transport/test_transport_api_monitor_peers_peer1.conf deleted file mode 100644 index bc9eee19b..000000000 --- a/src/transport/test_transport_api_monitor_peers_peer1.conf +++ /dev/null @@ -1,4 +0,0 @@ -@INLINE@ template_cfg_peer1.conf -[PATHS] -GNUNET_TEST_HOME = $GNUNET_TMP/test-transport/api-monitoring-p1/ - diff --git a/src/transport/test_transport_api_monitor_peers_peer2.conf b/src/transport/test_transport_api_monitor_peers_peer2.conf deleted file mode 100644 index 5225a5a87..000000000 --- a/src/transport/test_transport_api_monitor_peers_peer2.conf +++ /dev/null @@ -1,5 +0,0 @@ -@INLINE@ template_cfg_peer2.conf -[PATHS] -GNUNET_TEST_HOME = $GNUNET_TMP/test-transport/api-monitoring-p2/ - - diff --git a/src/transport/test_transport_api_monitor_validation_peer1.conf b/src/transport/test_transport_api_monitor_validation_peer1.conf deleted file mode 100644 index 02f7bc2f0..000000000 --- a/src/transport/test_transport_api_monitor_validation_peer1.conf +++ /dev/null @@ -1,6 +0,0 @@ -@INLINE@ template_cfg_peer1.conf -[PATHS] -GNUNET_TEST_HOME = $GNUNET_TMP/test-transport/api-monitoring-val-p1/ - -[transport] -PLUGINS=tcp diff --git a/src/transport/test_transport_api_monitor_validation_peer2.conf b/src/transport/test_transport_api_monitor_validation_peer2.conf deleted file mode 100644 index d4e0874f0..000000000 --- a/src/transport/test_transport_api_monitor_validation_peer2.conf +++ /dev/null @@ -1,7 +0,0 @@ -@INLINE@ template_cfg_peer2.conf -[PATHS] -GNUNET_TEST_HOME = $GNUNET_TMP/test-transport/api-monitoring-val-p2/ - -[TRANSPORT] -PLUGINS=tcp - diff --git a/src/transport/test_transport_api_multi_peer1.conf b/src/transport/test_transport_api_multi_peer1.conf deleted file mode 100644 index b7cb98777..000000000 --- a/src/transport/test_transport_api_multi_peer1.conf +++ /dev/null @@ -1,7 +0,0 @@ -@INLINE@ template_cfg_peer1.conf -[PATHS] -GNUNET_TEST_HOME = $GNUNET_TMP/test-transport/api-multi-p1/ - -[transport] -PLUGINS = tcp udp - diff --git a/src/transport/test_transport_api_multi_peer2.conf b/src/transport/test_transport_api_multi_peer2.conf deleted file mode 100644 index f69e0abd9..000000000 --- a/src/transport/test_transport_api_multi_peer2.conf +++ /dev/null @@ -1,9 +0,0 @@ -@INLINE@ template_cfg_peer2.conf -[PATHS] -GNUNET_TEST_HOME = $GNUNET_TMP/test-transport/api-multi-p2/ - -[nat] -ALLOW_NAT = NO - -[transport] -PLUGINS = tcp udp diff --git a/src/transport/test_transport_api_tcp_nat_peer1.conf b/src/transport/test_transport_api_tcp_nat_peer1.conf deleted file mode 100644 index fb2fcecc6..000000000 --- a/src/transport/test_transport_api_tcp_nat_peer1.conf +++ /dev/null @@ -1,34 +0,0 @@ -@INLINE@ template_cfg_peer1.conf -[PATHS] -GNUNET_TEST_HOME = $GNUNET_TMP/test-transport/api-tcp-nat-p1/ - -[nat] -BEHIND_NAT = YES -ENABLE_NAT_SERVER = YES -DISABLEV6 = YES - -[transport-tcp] -PORT = 0 -TIMEOUT = 5 s - -[arm] -PORT = 51204 -UNIXPATH = $GNUNET_RUNTIME_DIR/gnunet-p1-service-arm.sock - -[statistics] -PORT = 12023 -UNIXPATH = $GNUNET_RUNTIME_DIR/gnunet-p1-service-statistics.sock - -[resolver] -PORT = 12022 -UNIXPATH = $GNUNET_RUNTIME_DIR/gnunet-p1-service-resolver.sock - -[peerinfo] -PORT = 12021 -UNIXPATH = $GNUNET_RUNTIME_DIR/gnunet-p1-service-peerinfo.sock - -[transport] -PORT = 29542 -PLUGINS = tcp -UNIXPATH = $GNUNET_RUNTIME_DIR/gnunet-p1-service-transport.sock - diff --git a/src/transport/test_transport_api_tcp_nat_peer2.conf b/src/transport/test_transport_api_tcp_nat_peer2.conf deleted file mode 100644 index 1faefc195..000000000 --- a/src/transport/test_transport_api_tcp_nat_peer2.conf +++ /dev/null @@ -1,32 +0,0 @@ -@INLINE@ template_cfg_peer2.conf -[PATHS] -GNUNET_TEST_HOME = $GNUNET_TMP/test-transport/api-tcp-nat-p2/ - -[nat] -DISABLEV6 = YES -ENABLE_NAT_CLIENT = YES - -[transport-tcp] -PORT = 12030 -TIMEOUT = 5 s - -[arm] -PORT = 12034 -UNIXPATH = $GNUNET_RUNTIME_DIR/gnunet-p2-service-arm.sock - -[statistics] -PORT = 12033 -UNIXPATH = $GNUNET_RUNTIME_DIR/gnunet-p2-service-statistics.sock - -[resolver] -PORT = 12032 -UNIXPATH = $GNUNET_RUNTIME_DIR/gnunet-p2-service-resolver.sock - -[peerinfo] -PORT = 12031 -UNIXPATH = $GNUNET_RUNTIME_DIR/gnunet-p2-service-peerinfo.sock - -[transport] -PORT = 45923 -PLUGINS = tcp -UNIXPATH = $GNUNET_RUNTIME_DIR/gnunet-p2-service-transport.sock diff --git a/src/transport/test_transport_api_tcp_peer1.conf b/src/transport/test_transport_api_tcp_peer1.conf deleted file mode 100644 index eabd6b701..000000000 --- a/src/transport/test_transport_api_tcp_peer1.conf +++ /dev/null @@ -1,9 +0,0 @@ -@INLINE@ template_cfg_peer1.conf -[PATHS] -GNUNET_TEST_HOME = $GNUNET_TMP/test-transport/api-tcp-p1/ - -[transport] -PLUGINS = tcp - -#[transport] -#PREFIX = valgrind diff --git a/src/transport/test_transport_api_tcp_peer2.conf b/src/transport/test_transport_api_tcp_peer2.conf deleted file mode 100644 index 58ce0777f..000000000 --- a/src/transport/test_transport_api_tcp_peer2.conf +++ /dev/null @@ -1,9 +0,0 @@ -@INLINE@ template_cfg_peer2.conf -[PATHS] -GNUNET_TEST_HOME = $GNUNET_TMP/test-transport/api-tcp-p2/ - -[transport] -PLUGINS = tcp - -#[transport] -#PREFIX = valgrind diff --git a/src/transport/test_transport_api_udp_nat_peer1.conf b/src/transport/test_transport_api_udp_nat_peer1.conf deleted file mode 100644 index 7bbcaa628..000000000 --- a/src/transport/test_transport_api_udp_nat_peer1.conf +++ /dev/null @@ -1,34 +0,0 @@ -@INLINE@ template_cfg_peer1.conf -[PATHS] -GNUNET_TEST_HOME = $GNUNET_TMP/test-transport/api-udp-nat-p1/ - -[nat] -BEHIND_NAT = YES -ALLOW_NAT = NO -ONLY_NAT_ADDRESSES = YES - -[transport-udp] -PORT = 0 - -[arm] -PORT = 12065 -UNIXPATH = $GNUNET_RUNTIME_DIR/gnunet-p1-service-arm.sock - -[statistics] -PORT = 12064 -UNIXPATH = $GNUNET_RUNTIME_DIR/gnunet-p1-service-statistics.sock - -[resolver] -PORT = 12063 -UNIXPATH = $GNUNET_RUNTIME_DIR/gnunet-p1-service-resolver.sock - -[peerinfo] -PORT = 12062 -UNIXPATH = $GNUNET_RUNTIME_DIR/gnunet-p1-service-peerinfo.sock - -[transport] -PORT = 12061 -PLUGINS = udp -UNIXPATH = $GNUNET_RUNTIME_DIR/gnunet-p1-service-transport.sock - - diff --git a/src/transport/test_transport_api_udp_nat_peer2.conf b/src/transport/test_transport_api_udp_nat_peer2.conf deleted file mode 100644 index 8bdb8c293..000000000 --- a/src/transport/test_transport_api_udp_nat_peer2.conf +++ /dev/null @@ -1,32 +0,0 @@ -@INLINE@ template_cfg_peer2.conf -[PATHS] -GNUNET_TEST_HOME = $GNUNET_TMP/test-transport/api-udp-nat-p2/ - -[nat] -ALLOW_NAT = YES - -[transport-udp] -PORT = 12070 - -[arm] -PORT = 12075 -UNIXPATH = $GNUNET_RUNTIME_DIR/gnunet-p2-service-arm.sock - -[statistics] -PORT = 12074 -UNIXPATH = $GNUNET_RUNTIME_DIR/gnunet-p2-service-statistics.sock - -[resolver] -PORT = 12073 -UNIXPATH = $GNUNET_RUNTIME_DIR/gnunet-p2-service-resolver.sock - -[peerinfo] -PORT = 12072 -UNIXPATH = $GNUNET_RUNTIME_DIR/gnunet-p2-service-peerinfo.sock - -[transport] -PORT = 12071 -PLUGINS = udp -UNIXPATH = $GNUNET_RUNTIME_DIR/gnunet-p2-service-transport.sock - - diff --git a/src/transport/test_transport_api_udp_peer1.conf b/src/transport/test_transport_api_udp_peer1.conf deleted file mode 100644 index 4684b77f2..000000000 --- a/src/transport/test_transport_api_udp_peer1.conf +++ /dev/null @@ -1,17 +0,0 @@ -@INLINE@ template_cfg_peer1.conf -[PATHS] -GNUNET_TEST_HOME = $GNUNET_TMP/test-transport/api-udp-p1/ - -[transport-udp] -PORT = 0 -BROADCAST = NO -MAX_BPS = 50000000 -BINDTO = 127.0.0.1 -BINDTO6 = ::1 - -[transport] -PORT = 12041 -PLUGINS = udp - - - diff --git a/src/transport/test_transport_api_udp_peer2.conf b/src/transport/test_transport_api_udp_peer2.conf deleted file mode 100644 index 96706ce49..000000000 --- a/src/transport/test_transport_api_udp_peer2.conf +++ /dev/null @@ -1,15 +0,0 @@ -@INLINE@ template_cfg_peer2.conf -[PATHS] -GNUNET_TEST_HOME = $GNUNET_TMP/test-transport/api-udp-p2/ - -[transport-udp] -PORT = 12050 -BROADCAST = NO -MAX_BPS = 50000000 -BINDTO = 127.0.0.1 -BINDTO6 = ::1 - -[transport] -PLUGINS = udp - - diff --git a/src/transport/test_transport_api_unix_peer1.conf b/src/transport/test_transport_api_unix_peer1.conf deleted file mode 100644 index 005558bcc..000000000 --- a/src/transport/test_transport_api_unix_peer1.conf +++ /dev/null @@ -1,10 +0,0 @@ -@INLINE@ template_cfg_peer1.conf -[PATHS] -GNUNET_TEST_HOME = $GNUNET_TMP/test-transport/api-unix-p1/ - -[transport] -PLUGINS = unix - -[transport-unix] -PORT = 12120 - diff --git a/src/transport/test_transport_api_unix_peer2.conf b/src/transport/test_transport_api_unix_peer2.conf deleted file mode 100644 index 840ed81c7..000000000 --- a/src/transport/test_transport_api_unix_peer2.conf +++ /dev/null @@ -1,7 +0,0 @@ -@INLINE@ template_cfg_peer2.conf -[PATHS] -GNUNET_TEST_HOME = $GNUNET_TMP/test-transport/api-unix-p2/ - -[transport] -PLUGINS = unix - diff --git a/src/transport/test_transport_defaults.conf b/src/transport/test_transport_defaults.conf deleted file mode 100644 index 3aed73f0c..000000000 --- a/src/transport/test_transport_defaults.conf +++ /dev/null @@ -1,20 +0,0 @@ -@INLINE@ ../../contrib/conf/gnunet/no_forcestart.conf - -[PATHS] -GNUNET_TEST_HOME = $GNUNET_TMP/test-tng/ - -[transport-tcp] -TIMEOUT = 300 s - -[transport] -# PREFIX = valgrind - -[nat] -DISABLEV6 = NO -RETURN_LOCAL_ADDRESSES = YES -BINDTO = 127.0.0.1 -INTERNAL_ADDRESS = 127.0.0.1 -EXTERNAL_ADDRESS = 127.0.0.1 - -[transport-udp] -BROADCAST_RECEIVE = no diff --git a/src/transport/test_transport_distance_vector_circle_topo.conf b/src/transport/test_transport_distance_vector_circle_topo.conf deleted file mode 100644 index e8d694c3a..000000000 --- a/src/transport/test_transport_distance_vector_circle_topo.conf +++ /dev/null @@ -1,11 +0,0 @@ -M:1 -N:3 -X:0 -B:0 -T:libgnunet_test_transport_plugin_cmd_simple_send_dv -R:1|{tcp_port:0}|{udp_port:1} -R:2|{tcp_port:0}|{udp_port:1} -R:3|{tcp_port:0}|{udp_port:1} -P:1:1|{connect:{P:2:1:udp}} -P:2:1|{connect:{P:3:1:udp}} -P:3:1|{connect:{P:1:1:udp}} diff --git a/src/transport/test_transport_distance_vector_inverse_topo.conf b/src/transport/test_transport_distance_vector_inverse_topo.conf deleted file mode 100644 index e062b3e2e..000000000 --- a/src/transport/test_transport_distance_vector_inverse_topo.conf +++ /dev/null @@ -1,13 +0,0 @@ -M:1 -N:4 -X:0 -AC:1 -T:libgnunet_test_transport_plugin_cmd_simple_send_dv -R:1|{tcp_port:1}|{udp_port:0} -R:2|{tcp_port:1}|{udp_port:0} -R:3|{tcp_port:1}|{udp_port:0} -R:4|{tcp_port:1}|{udp_port:0} -P:1:1|{connect:{P:2:1:tcp}}|{AC:2} -P:2:1|{connect:{P:3:1:tcp}}|{AC:2} -P:3:1|{connect:{P:4:1:tcp}}|{AC:2} -P:4:1|{AC:3} \ No newline at end of file diff --git a/src/transport/test_transport_distance_vector_topo.conf b/src/transport/test_transport_distance_vector_topo.conf deleted file mode 100644 index ead3e0a0a..000000000 --- a/src/transport/test_transport_distance_vector_topo.conf +++ /dev/null @@ -1,8 +0,0 @@ -M:2 -N:2 -X:0 -T:libgnunet_test_transport_plugin_cmd_simple_send_dv -R:1|{tcp_port:1}|{udp_port:0} -R:2|{tcp_port:1}|{udp_port:0} -P:1:1|{connect:{P:2:1:tcp}} -P:2:1|{connect:{P:1:1:tcp}} diff --git a/src/transport/test_transport_just_run_topo.conf b/src/transport/test_transport_just_run_topo.conf deleted file mode 100644 index d27a2fc77..000000000 --- a/src/transport/test_transport_just_run_topo.conf +++ /dev/null @@ -1,6 +0,0 @@ -M:2 -N:1 -X:0 -T:libgnunet_test_transport_plugin_cmd_just_run -P:1:1|{connect:{P:1:2:tcp}|{P:1:2:udp}} -P:1:2|{connect:{P:1:1:tcp}|{P:1:1:udp}} \ No newline at end of file diff --git a/src/transport/test_transport_nat_icmp_tcp.sh b/src/transport/test_transport_nat_icmp_tcp.sh deleted file mode 100755 index 41cdde487..000000000 --- a/src/transport/test_transport_nat_icmp_tcp.sh +++ /dev/null @@ -1,12 +0,0 @@ -#!/bin/bash -if ! [ -d "/run/netns" ]; then - echo You have to create the directory /run/netns. -fi -if [ -f /proc/sys/kernel/unprivileged_userns_clone ]; then - if [ "$(cat /proc/sys/kernel/unprivileged_userns_clone)" != 1 ]; then - echo -e "Error during test setup: The kernel parameter kernel.unprivileged_userns_clone has to be set to 1! One has to execute\n\n sysctl kernel.unprivileged_userns_clone=1\n" - exit 78 - fi -fi -exec unshare -r -nmU bash -c "mount -t tmpfs --make-rshared tmpfs /run/netns; ./test_transport_start_with_config test_transport_nat_icmp_tcp_topo.conf" -#sudo valgrind --vgdb=yes --leak-check=full --track-origins=yes --trace-children=yes --trace-children-skip=/usr/bin/awk,/usr/bin/cut,/usr/bin/seq,/sbin/ip/sed/bash ./test_transport_start_with_config test_transport_nat_upnp_topo.conf diff --git a/src/transport/test_transport_nat_icmp_tcp_topo.conf b/src/transport/test_transport_nat_icmp_tcp_topo.conf deleted file mode 100644 index 37738c80f..000000000 --- a/src/transport/test_transport_nat_icmp_tcp_topo.conf +++ /dev/null @@ -1,7 +0,0 @@ -M:1 -N:1 -X:1 -T:libgnunet_test_transport_plugin_cmd_nat_upnp -K:1|{connect:{P:1:1:tcp_natted}} -R:1|{tcp_port:0}|{udp_port:0} -P:1:1 \ No newline at end of file diff --git a/src/transport/test_transport_nat_upnp.sh b/src/transport/test_transport_nat_upnp.sh deleted file mode 100755 index df43ef320..000000000 --- a/src/transport/test_transport_nat_upnp.sh +++ /dev/null @@ -1,12 +0,0 @@ -#!/bin/bash -if ! [ -d "/run/netns" ]; then - echo You have to create the directory /run/netns. -fi -if [ -f /proc/sys/kernel/unprivileged_userns_clone ]; then - if [ "$(cat /proc/sys/kernel/unprivileged_userns_clone)" != 1 ]; then - echo -e "Error during test setup: The kernel parameter kernel.unprivileged_userns_clone has to be set to 1! One has to execute\n\n sysctl kernel.unprivileged_userns_clone=1\n" - exit 78 - fi -fi -exec unshare -r -nmU bash -c "mount -t tmpfs --make-rshared tmpfs /run/netns; ./test_transport_start_with_config test_transport_nat_upnp_topo.conf" -#sudo valgrind --vgdb=yes --leak-check=full --track-origins=yes --trace-children=yes --trace-children-skip=/usr/bin/awk,/usr/bin/cut,/usr/bin/seq,/sbin/ip/sed/bash ./test_transport_start_with_config test_transport_nat_upnp_topo.conf diff --git a/src/transport/test_transport_nat_upnp_topo.conf b/src/transport/test_transport_nat_upnp_topo.conf deleted file mode 100644 index e02633d4b..000000000 --- a/src/transport/test_transport_nat_upnp_topo.conf +++ /dev/null @@ -1,7 +0,0 @@ -M:1 -N:1 -X:1 -T:libgnunet_test_transport_plugin_cmd_nat_upnp -K:1|{connect:{P:1:1:tcp}} -R:1|{tcp_port:0}|{udp_port:0}|{script:upnp.sh} -P:1:1|{connect:{K:1:udp}} \ No newline at end of file diff --git a/src/transport/test_transport_plugin_cmd_just_run.c b/src/transport/test_transport_plugin_cmd_just_run.c deleted file mode 100644 index df7484884..000000000 --- a/src/transport/test_transport_plugin_cmd_just_run.c +++ /dev/null @@ -1,494 +0,0 @@ -/* - This file is part of GNUnet - Copyright (C) 2021 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 . - - SPDX-License-Identifier: AGPL3.0-or-later - */ - -/** - * @file testbed/plugin_cmd_simple_send.c - * @brief a plugin to provide the API for running test cases. - * @author t3sserakt - */ -#include "platform.h" -#include "gnunet_testing_barrier.h" -#include "gnunet_testing_netjail_lib.h" -#include "gnunet_util_lib.h" -#include "gnunet_transport_application_service.h" -#include "transport-testing2.h" -#include "transport-testing-cmds.h" -#include "gnunet_testing_barrier.h" -#include "gnunet_core_service.h" - -/** - * Generic logging shortcut - */ -#define LOG(kind, ...) GNUNET_log (kind, __VA_ARGS__) - -#define BASE_DIR "testdir" - -#define TOPOLOGY_CONFIG "test_transport_simple_send_topo.conf" - -#define TIMEOUT GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS, 600) - -#define MAX_RECEIVED 1000 - -#define MESSAGE_SIZE 65000 - -static struct GNUNET_TESTING_Command block_script; - -static struct GNUNET_TESTING_Command connect_peers; - -static struct GNUNET_TESTING_Command local_prepared; - -static struct GNUNET_TESTING_Command start_peer; - -static struct GNUNET_TESTING_Interpreter *is; - -static struct GNUNET_CONTAINER_MultiPeerMap *senders; - -struct Sender -{ - /** - * Number of received messages from sender. - */ - unsigned long long num_received; - - /** - * Sample mean time the message traveled. - */ - struct GNUNET_TIME_Relative mean_time; - - /** - * Time the first message was send. - */ - struct GNUNET_TIME_Absolute time_first; -}; - -/** - * Function called to check a message of type GNUNET_TRANSPORT_TESTING_SIMPLE_MTYPE being - * received. - * - */ -static int -check_test (void *cls, - const struct - GNUNET_TRANSPORT_TESTING_PerformanceTestMessage *message) -{ - return GNUNET_OK; -} - - -struct GNUNET_TESTING_BarrierList* -get_waiting_for_barriers () -{ - struct GNUNET_TESTING_BarrierList*barriers; - struct GNUNET_TESTING_BarrierListEntry *ble; - - barriers = GNUNET_new (struct GNUNET_TESTING_BarrierList); - ble = GNUNET_new (struct GNUNET_TESTING_BarrierListEntry); - ble->barrier_name = "ready-to-connect"; - ble->expected_reaches = 1; - GNUNET_CONTAINER_DLL_insert (barriers->head, - barriers->tail, - ble); - - ble = GNUNET_new (struct GNUNET_TESTING_BarrierListEntry); - ble->barrier_name = "test-case-finished"; - ble->expected_reaches = 1; - GNUNET_CONTAINER_DLL_insert (barriers->head, - barriers->tail, - ble); - return barriers; -} - - -/** - * Callback to set the flag indicating all peers started. Will be called via the plugin api. - * - */ -static void -all_peers_started () -{ -} - - -/** - * Function called with the final result of the test. - * - * @param cls the `struct MainParams` - * @param rv #GNUNET_OK if the test passed - */ -static void -handle_result (void *cls, - enum GNUNET_GenericReturnValue rv) -{ - struct TestState *ts = cls; - - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "Local test exits with status %d\n", - rv); - - ts->finished_cb (rv); - GNUNET_free (ts->testdir); - GNUNET_free (ts->cfgname); - GNUNET_TESTING_free_topology (ts->topology); - GNUNET_free (ts); -} - - -/** - * Callback from start peer cmd for signaling a peer got connected. - * - * -static void * -notify_connect (struct GNUNET_TESTING_Interpreter *is, - const struct GNUNET_PeerIdentity *peer) -{ - const struct ConnectPeersState *cps; - const struct GNUNET_TESTING_Command *cmd; - - cmd = GNUNET_TESTING_interpreter_lookup_command (is, - "connect-peers"); - GNUNET_TRANSPORT_get_trait_connect_peer_state (cmd, - &cps); - void *ret = NULL; - - cps->notify_connect (is, - peer); - return ret; - }*/ - - -/** - * Callback to set the flag indicating all peers are prepared to finish. Will be called via the plugin api. - */ -static void -all_local_tests_prepared () -{ - const struct GNUNET_TESTING_LocalPreparedState *lfs; - - GNUNET_TESTING_get_trait_local_prepared_state (&local_prepared, - &lfs); - GNUNET_assert (NULL != &lfs->ac); - if (NULL == lfs->ac.cont) - GNUNET_TESTING_async_fail ((struct GNUNET_TESTING_AsyncContext *) &lfs->ac); - else - GNUNET_TESTING_async_finish ((struct - GNUNET_TESTING_AsyncContext *) &lfs->ac); -} - - -static void -child_completed_callback (void *cls, - enum GNUNET_OS_ProcessStatusType type, - long unsigned int exit_code) -{ - -} - - -/** - * Function called to check a message being - * received. - * - */ -static int -check_encrypted (void *cls, struct GNUNET_MessageHeader *header) -{ - return GNUNET_OK; -} - - -static void -core_receive_continue (struct GNUNET_PeerIdentity *peer) -{ - const struct GNUNET_TESTING_StartPeerState *sps; - - GNUNET_TRANSPORT_get_trait_state (&start_peer, - &sps); - - LOG (GNUNET_ERROR_TYPE_DEBUG, - "Executing core receive continue\n"); - - GNUNET_TRANSPORT_core_receive_continue (sps->th, peer); -} - - -/*static void -handle_core (void *cls, struct GNUNET_MessageHeader *header) -{ - struct GNUNET_PeerIdentity *peer = cls; - - core_receive_continue (peer); - }*/ - - -/** - * Function called to handle a message being received. - * - */ -static void -handle_encrypted (void *cls, struct GNUNET_MessageHeader *header) -{ - struct GNUNET_PeerIdentity *peer = cls; - - core_receive_continue (peer); -} - - -static void -handle_ephemeral_key (void *cls, struct GNUNET_MessageHeader *header) -{ - struct GNUNET_PeerIdentity *peer = cls; - - core_receive_continue (peer); -} - - -static void -handle_ping (void *cls, struct GNUNET_MessageHeader *header) -{ - struct GNUNET_PeerIdentity *peer = cls; - - core_receive_continue (peer); -} - - -static void -handle_pong (void *cls, struct GNUNET_MessageHeader *header) -{ - struct GNUNET_PeerIdentity *peer = cls; - - core_receive_continue (peer); -} - -/** - * Function to start a local test case. - * - * @param write_message Callback to send a message to the master loop. - * @param router_ip Global address of the network namespace. - * @param node_ip The IP address of the node. - * @param m The number of the node in a network namespace. - * @param n The number of the network namespace. - * @param local_m The number of nodes in a network namespace. - * @param topology_data A file name for the file containing the topology configuration, or a string containing - * the topology configuration. - * @param read_file If read_file is GNUNET_YES this string is the filename for the topology configuration, - * if read_file is GNUNET_NO the string contains the topology configuration. - * @param finish_cb Callback function which writes a message from the helper process running on a netjail - * node to the master process * signaling that the test case running on the netjail node finished. - * @return Returns the struct GNUNET_TESTING_Interpreter of the command loop running on this netjail node. - */ -static struct GNUNET_TESTING_Interpreter * -start_testcase (GNUNET_TESTING_cmd_helper_write_cb write_message, - const char *router_ip, - const char *node_ip, - const char *m, - const char *n, - const char *local_m, - const char *topology_data, - unsigned int *read_file, - GNUNET_TESTING_cmd_helper_finish_cb finished_cb) -{ - - unsigned int n_int; - unsigned int m_int; - unsigned int local_m_int; - unsigned int num; - struct TestState *ts = GNUNET_new (struct TestState); - struct GNUNET_TESTING_NetjailTopology *topology; - unsigned int sscanf_ret = 0; - char **argv = NULL; - int argc = 0; - - ts->finished_cb = finished_cb; - LOG (GNUNET_ERROR_TYPE_ERROR, - "n %s m %s\n", - n, - m); - - if (GNUNET_YES == *read_file) - { - LOG (GNUNET_ERROR_TYPE_DEBUG, - "read from file\n"); - topology = GNUNET_TESTING_get_topo_from_file (topology_data); - } - else - topology = GNUNET_TESTING_get_topo_from_string (topology_data); - - ts->topology = topology; - - errno = 0; - sscanf_ret = sscanf (m, "%u", &m_int); - if (errno != 0) - { - GNUNET_log_strerror (GNUNET_ERROR_TYPE_ERROR, "sscanf"); - } - GNUNET_assert (0 < sscanf_ret); - errno = 0; - sscanf_ret = sscanf (n, "%u", &n_int); - if (errno != 0) - { - GNUNET_log_strerror (GNUNET_ERROR_TYPE_ERROR, "sscanf"); - } - GNUNET_assert (0 < sscanf_ret); - errno = 0; - sscanf_ret = sscanf (local_m, "%u", &local_m_int); - if (errno != 0) - { - GNUNET_log_strerror (GNUNET_ERROR_TYPE_ERROR, "sscanf"); - } - GNUNET_assert (0 < sscanf_ret); - - if (0 == n_int) - num = m_int; - else - num = (n_int - 1) * local_m_int + m_int + topology->nodes_x; - - block_script = GNUNET_TESTING_cmd_block_until_external_trigger ( - "block-script"); - connect_peers = GNUNET_TRANSPORT_cmd_connect_peers ("connect-peers", - "start-peer", - "system-create", - num, - topology, - 0, - GNUNET_NO); - local_prepared = GNUNET_TESTING_cmd_local_test_prepared ( - "local-test-prepared", - write_message); - - - GNUNET_asprintf (&ts->cfgname, - "test_transport_api2_tcp_node1.conf"); - - LOG (GNUNET_ERROR_TYPE_DEBUG, - "plugin cfgname: %s\n", - ts->cfgname); - - LOG (GNUNET_ERROR_TYPE_DEBUG, - "node ip: %s\n", - node_ip); - - GNUNET_asprintf (&ts->testdir, - "%s%s%s", - BASE_DIR, - m, - n); - - /*struct GNUNET_MQ_MessageHandler handlers[] = { - GNUNET_MQ_hd_fixed_size (ephemeral_key, - GNUNET_MESSAGE_TYPE_CORE_EPHEMERAL_KEY, - struct EphemeralKeyMessage, - NULL), - GNUNET_MQ_hd_fixed_size (ping, - GNUNET_MESSAGE_TYPE_CORE_PING, - struct PingMessage, - NULL), - GNUNET_MQ_hd_fixed_size (pong, - GNUNET_MESSAGE_TYPE_CORE_PONG, - struct PongMessage, - NULL), - GNUNET_MQ_handler_end () - };*/ - - start_peer = GNUNET_TESTING_cmd_start_peer ("start-peer", - "system-create", - num, - node_ip, - ts->cfgname, - GNUNET_NO); - - struct GNUNET_TESTING_Command commands[] = { - GNUNET_TESTING_cmd_system_create ("system-create", - ts->testdir), - start_peer, - GNUNET_TESTING_cmd_barrier_reached ("ready-to-connect-reached", - "ready-to-connect", - GNUNET_NO, - num, - GNUNET_NO, - write_message), - connect_peers, - GNUNET_TESTING_cmd_exec_bash_script ("script", - "block.sh", - argv, - argc, - &child_completed_callback), - block_script, - GNUNET_TESTING_cmd_barrier_reached ("test-case-finished-reached", - "test-case-finished", - GNUNET_NO, - num, - GNUNET_NO, - write_message), - GNUNET_TRANSPORT_cmd_stop_peer ("stop-peer", - "start-peer"), - GNUNET_TESTING_cmd_system_destroy ("system-destroy", - "system-create"), - GNUNET_TESTING_cmd_end () - }; - - ts->write_message = write_message; - - is = GNUNET_TESTING_run (commands, - TIMEOUT, - &handle_result, - ts); - return is; -} - - -/** - * Entry point for the plugin. - * - * @param cls NULL - * @return the exported block API - */ -void * -libgnunet_test_transport_plugin_cmd_just_run_init (void *cls) -{ - struct GNUNET_TESTING_PluginFunctions *api; - - GNUNET_log_setup ("simple-send", - "DEBUG", - NULL); - - api = GNUNET_new (struct GNUNET_TESTING_PluginFunctions); - api->start_testcase = &start_testcase; - api->get_waiting_for_barriers = get_waiting_for_barriers; - return api; -} - - -/** - * Exit point from the plugin. - * - * @param cls the return value from #libgnunet_test_transport_plugin_just_run_init - * @return NULL - */ -void * -libgnunet_test_transport_plugin_cmd_just_run_done (void *cls) -{ - struct GNUNET_TESTING_PluginFunctions *api = cls; - - GNUNET_free (api); - return NULL; -} - - -/* end of plugin_cmd_simple_send.c */ diff --git a/src/transport/test_transport_plugin_cmd_nat_upnp.c b/src/transport/test_transport_plugin_cmd_nat_upnp.c deleted file mode 100644 index 9d7c5d856..000000000 --- a/src/transport/test_transport_plugin_cmd_nat_upnp.c +++ /dev/null @@ -1,368 +0,0 @@ -/* - This file is part of GNUnet - Copyright (C) 2021 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 . - - SPDX-License-Identifier: AGPL3.0-or-later - */ - -/** - * @file testbed/plugin_cmd_simple_send.c - * @brief a plugin to provide the API for running test cases. - * @author t3sserakt - */ -#include "platform.h" -#include "gnunet_testing_barrier.h" -#include "gnunet_testing_netjail_lib.h" -#include "gnunet_util_lib.h" -#include "gnunet_transport_application_service.h" -#include "transport-testing2.h" -#include "transport-testing-cmds.h" -#include "gnunet_testing_barrier.h" - -/** - * Generic logging shortcut - */ -#define LOG(kind, ...) GNUNET_log (kind, __VA_ARGS__) - -#define BASE_DIR "testdir" - -#define TOPOLOGY_CONFIG "test_transport_simple_send_topo.conf" - -#define TIMEOUT GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS, 600) - -static struct GNUNET_TESTING_Command block_send; - -static struct GNUNET_TESTING_Command block_receive; - -static struct GNUNET_TESTING_Command connect_peers; - -static struct GNUNET_TESTING_Command local_prepared; - -static struct GNUNET_TESTING_Interpreter *is; - -/** - * Function called to check a message of type GNUNET_TRANSPORT_TESTING_SIMPLE_MTYPE being - * received. - * - */ -static int -check_test (void *cls, - const struct GNUNET_TRANSPORT_TESTING_TestMessage *message) -{ - return GNUNET_OK; -} - - -/** - * Function called to handle a message of type GNUNET_TRANSPORT_TESTING_SIMPLE_MTYPE - * being received. - * - */ -static void -handle_test (void *cls, - const struct GNUNET_TRANSPORT_TESTING_TestMessage *message) -{ - struct GNUNET_TESTING_AsyncContext *ac; - - GNUNET_TESTING_get_trait_async_context (&block_receive, - &ac); - GNUNET_assert (NULL != ac); - if (NULL == ac->cont) - GNUNET_TESTING_async_fail ((struct GNUNET_TESTING_AsyncContext *) ac); - else - GNUNET_TESTING_async_finish ((struct GNUNET_TESTING_AsyncContext *) ac); -} - - -struct GNUNET_TESTING_BarrierList* -get_waiting_for_barriers () -{ - struct GNUNET_TESTING_BarrierList*barriers; - struct GNUNET_TESTING_BarrierListEntry *ble; - - barriers = GNUNET_new (struct GNUNET_TESTING_BarrierList); - ble = GNUNET_new (struct GNUNET_TESTING_BarrierListEntry); - ble->barrier_name = "ready-to-connect"; - ble->expected_reaches = 1; - GNUNET_CONTAINER_DLL_insert (barriers->head, - barriers->tail, - ble); - - ble = GNUNET_new (struct GNUNET_TESTING_BarrierListEntry); - ble->barrier_name = "test-case-finished"; - ble->expected_reaches = 1; - GNUNET_CONTAINER_DLL_insert (barriers->head, - barriers->tail, - ble); - return barriers; -} - - -/** - * Function called with the final result of the test. - * - * @param cls the `struct MainParams` - * @param rv #GNUNET_OK if the test passed - */ -static void -handle_result (void *cls, - enum GNUNET_GenericReturnValue rv) -{ - struct TestState *ts = cls; - - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "Local test exits with status %d\n", - rv); - - ts->finished_cb (rv); - GNUNET_free (ts->testdir); - GNUNET_free (ts->cfgname); - GNUNET_TESTING_free_topology (ts->topology); - GNUNET_free (ts); -} - - -/** - * Callback from start peer cmd for signaling a peer got connected. - * - */ -static void * -notify_connect (struct GNUNET_TESTING_Interpreter *is, - const struct GNUNET_PeerIdentity *peer) -{ - const struct ConnectPeersState *cps; - const struct GNUNET_TESTING_Command *cmd; - - cmd = GNUNET_TESTING_interpreter_lookup_command (is, - "connect-peers"); - GNUNET_TRANSPORT_get_trait_connect_peer_state (cmd, - &cps); - void *ret = NULL; - - cps->notify_connect (is, - peer); - return ret; -} - - -/** - * Function to start a local test case. - * - * @param write_message Callback to send a message to the master loop. - * @param router_ip Global address of the network namespace. - * @param node_ip The IP address of the node. - * @param m The number of the node in a network namespace. - * @param n The number of the network namespace. - * @param local_m The number of nodes in a network namespace. - * @param topology_data A file name for the file containing the topology configuration, or a string containing - * the topology configuration. - * @param read_file If read_file is GNUNET_YES this string is the filename for the topology configuration, - * if read_file is GNUNET_NO the string contains the topology configuration. - * @param finish_cb Callback function which writes a message from the helper process running on a netjail - * node to the master process * signaling that the test case running on the netjail node finished. - * @return Returns the struct GNUNET_TESTING_Interpreter of the command loop running on this netjail node. - */ -static struct GNUNET_TESTING_Interpreter * -start_testcase (GNUNET_TESTING_cmd_helper_write_cb write_message, - const char *router_ip, - const char *node_ip, - const char *m, - const char *n, - const char *local_m, - const char *topology_data, - unsigned int *read_file, - GNUNET_TESTING_cmd_helper_finish_cb finished_cb) -{ - - unsigned int n_int; - unsigned int m_int; - unsigned int local_m_int; - unsigned int num; - struct TestState *ts = GNUNET_new (struct TestState); - struct GNUNET_TESTING_NetjailTopology *topology; - unsigned int sscanf_ret = 0; - - ts->finished_cb = finished_cb; - LOG (GNUNET_ERROR_TYPE_ERROR, - "n %s m %s\n", - n, - m); - - if (GNUNET_YES == *read_file) - { - LOG (GNUNET_ERROR_TYPE_DEBUG, - "read from file\n"); - topology = GNUNET_TESTING_get_topo_from_file (topology_data); - } - else - topology = GNUNET_TESTING_get_topo_from_string (topology_data); - - ts->topology = topology; - - errno = 0; - sscanf_ret = sscanf (m, "%u", &m_int); - if (errno != 0) - { - GNUNET_log_strerror (GNUNET_ERROR_TYPE_ERROR, "sscanf"); - } - GNUNET_assert (0 < sscanf_ret); - errno = 0; - sscanf_ret = sscanf (n, "%u", &n_int); - if (errno != 0) - { - GNUNET_log_strerror (GNUNET_ERROR_TYPE_ERROR, "sscanf"); - } - GNUNET_assert (0 < sscanf_ret); - errno = 0; - sscanf_ret = sscanf (local_m, "%u", &local_m_int); - if (errno != 0) - { - GNUNET_log_strerror (GNUNET_ERROR_TYPE_ERROR, "sscanf"); - } - GNUNET_assert (0 < sscanf_ret); - - if (0 == n_int) - num = m_int; - else - num = (n_int - 1) * local_m_int + m_int + topology->nodes_x; - - block_send = GNUNET_TESTING_cmd_block_until_external_trigger ( - "block"); - block_receive = GNUNET_TESTING_cmd_block_until_external_trigger ( - "block-receive"); - connect_peers = GNUNET_TRANSPORT_cmd_connect_peers ("connect-peers", - "start-peer", - "system-create", - num, - topology, - 0, - GNUNET_YES); - local_prepared = GNUNET_TESTING_cmd_local_test_prepared ( - "local-test-prepared", - write_message); - - - GNUNET_asprintf (&ts->cfgname, - "test_transport_api2_tcp_node1.conf"); - - LOG (GNUNET_ERROR_TYPE_DEBUG, - "plugin cfgname: %s\n", - ts->cfgname); - - LOG (GNUNET_ERROR_TYPE_DEBUG, - "node ip: %s\n", - node_ip); - - GNUNET_asprintf (&ts->testdir, - "%s%s%s", - BASE_DIR, - m, - n); - - struct GNUNET_MQ_MessageHandler handlers[] = { - GNUNET_MQ_hd_var_size (test, - GNUNET_TRANSPORT_TESTING_SIMPLE_MTYPE, - struct GNUNET_TRANSPORT_TESTING_TestMessage, - ts), - GNUNET_MQ_handler_end () - }; - - struct GNUNET_TESTING_Command commands[] = { - GNUNET_TESTING_cmd_system_create ("system-create", - ts->testdir), - GNUNET_TRANSPORT_cmd_start_peer ("start-peer", - "system-create", - num, - node_ip, - handlers, - ts->cfgname, - notify_connect, - GNUNET_NO), - GNUNET_TESTING_cmd_barrier_reached ("ready-to-connect-reached", - "ready-to-connect", - GNUNET_NO, - num, - GNUNET_NO, - write_message), - connect_peers, - GNUNET_TRANSPORT_cmd_send_simple ("send-simple", - "start-peer", - "system-create", - num, - topology), - block_receive, - GNUNET_TESTING_cmd_barrier_reached ("test-case-finished-reached", - "test-case-finished", - GNUNET_NO, - num, - GNUNET_NO, - write_message), - GNUNET_TRANSPORT_cmd_stop_peer ("stop-peer", - "start-peer"), - GNUNET_TESTING_cmd_system_destroy ("system-destroy", - "system-create"), - GNUNET_TESTING_cmd_end () - }; - - ts->write_message = write_message; - - is = GNUNET_TESTING_run (commands, - TIMEOUT, - &handle_result, - ts); - return is; -} - - -/** - * Entry point for the plugin. - * - * @param cls NULL - * @return the exported block API - */ -void * -libgnunet_test_transport_plugin_cmd_nat_upnp_init (void *cls) -{ - struct GNUNET_TESTING_PluginFunctions *api; - - GNUNET_log_setup ("simple-send", - "DEBUG", - NULL); - - api = GNUNET_new (struct GNUNET_TESTING_PluginFunctions); - api->start_testcase = &start_testcase; - api->get_waiting_for_barriers = get_waiting_for_barriers; - return api; -} - - -/** - * Exit point from the plugin. - * - * @param cls the return value from #libgnunet_test_transport_plugin_nat_upnp_init - * @return NULL - */ -void * -libgnunet_test_transport_plugin_cmd_nat_upnp_done (void *cls) -{ - struct GNUNET_TESTING_PluginFunctions *api = cls; - - GNUNET_free (api); - return NULL; -} - - -/* end of plugin_cmd_simple_send.c */ diff --git a/src/transport/test_transport_plugin_cmd_simple_send.c b/src/transport/test_transport_plugin_cmd_simple_send.c deleted file mode 100644 index c6d75cce4..000000000 --- a/src/transport/test_transport_plugin_cmd_simple_send.c +++ /dev/null @@ -1,377 +0,0 @@ -/* - This file is part of GNUnet - Copyright (C) 2021 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 . - - SPDX-License-Identifier: AGPL3.0-or-later - */ - -/** - * @file testbed/plugin_cmd_simple_send.c - * @brief a plugin to provide the API for running test cases. - * @author t3sserakt - */ -#include "platform.h" -#include "gnunet_testing_barrier.h" -#include "gnunet_testing_netjail_lib.h" -#include "gnunet_util_lib.h" -#include "gnunet_transport_application_service.h" -#include "transport-testing2.h" -#include "transport-testing-cmds.h" -#include "gnunet_testing_barrier.h" - -/** - * Generic logging shortcut - */ -#define LOG(kind, ...) GNUNET_log (kind, __VA_ARGS__) - -#define BASE_DIR "testdir" - -#define TOPOLOGY_CONFIG "test_transport_simple_send_topo.conf" - -#define TIMEOUT GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS, 600) - -static struct GNUNET_TESTING_Command block_send; - -static struct GNUNET_TESTING_Command block_receive; - -static struct GNUNET_TESTING_Command connect_peers; - -static struct GNUNET_TESTING_Command local_prepared; - -static struct GNUNET_TESTING_Interpreter *is; - -/** - * Function called to check a message of type GNUNET_TRANSPORT_TESTING_SIMPLE_MTYPE being - * received. - * - */ -static int -check_test (void *cls, - const struct GNUNET_TRANSPORT_TESTING_TestMessage *message) -{ - return GNUNET_OK; -} - - -/** - * Function called to handle a message of type GNUNET_TRANSPORT_TESTING_SIMPLE_MTYPE - * being received. - * - */ -static void -handle_test (void *cls, - const struct GNUNET_TRANSPORT_TESTING_TestMessage *message) -{ - struct GNUNET_TESTING_AsyncContext *ac; - - GNUNET_TESTING_get_trait_async_context (&block_receive, - &ac); - GNUNET_assert (NULL != ac); - if (NULL == ac->cont) - GNUNET_TESTING_async_fail ((struct GNUNET_TESTING_AsyncContext *) ac); - else - GNUNET_TESTING_async_finish ((struct GNUNET_TESTING_AsyncContext *) ac); -} - - -struct GNUNET_TESTING_BarrierList * -get_waiting_for_barriers () -{ - // No Barrier - return GNUNET_new (struct GNUNET_TESTING_BarrierList); -} - - -/** - * Callback to set the flag indicating all peers started. Will be called via the plugin api. - * - */ -static void -all_peers_started () -{ - struct GNUNET_TESTING_AsyncContext *ac; - - GNUNET_TESTING_get_trait_async_context (&block_send, - &ac); - GNUNET_assert (NULL != ac); - if (NULL == ac->cont) - GNUNET_TESTING_async_fail ((struct GNUNET_TESTING_AsyncContext *) ac); - else - GNUNET_TESTING_async_finish ((struct GNUNET_TESTING_AsyncContext *) ac); -} - - -/** - * Function called with the final result of the test. - * - * @param cls the `struct MainParams` - * @param rv #GNUNET_OK if the test passed - */ -static void -handle_result (void *cls, - enum GNUNET_GenericReturnValue rv) -{ - struct TestState *ts = cls; - - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "Local test exits with status %d\n", - rv); - - ts->finished_cb (rv); - GNUNET_free (ts->testdir); - GNUNET_free (ts->cfgname); - GNUNET_TESTING_free_topology (ts->topology); - GNUNET_free (ts); -} - - -/** - * Callback from start peer cmd for signaling a peer got connected. - * - */ -static void * -notify_connect (struct GNUNET_TESTING_Interpreter *is, - const struct GNUNET_PeerIdentity *peer) -{ - const struct ConnectPeersState *cps; - const struct GNUNET_TESTING_Command *cmd; - - cmd = GNUNET_TESTING_interpreter_lookup_command (is, - "connect-peers"); - GNUNET_TRANSPORT_get_trait_connect_peer_state (cmd, - &cps); - void *ret = NULL; - - cps->notify_connect (is, - peer); - return ret; -} - - -/** - * Callback to set the flag indicating all peers are prepared to finish. Will be called via the plugin api. - */ -static void -all_local_tests_prepared () -{ - const struct GNUNET_TESTING_LocalPreparedState *lfs; - - GNUNET_TESTING_get_trait_local_prepared_state (&local_prepared, - &lfs); - GNUNET_assert (NULL != &lfs->ac); - if (NULL == lfs->ac.cont) - GNUNET_TESTING_async_fail ((struct GNUNET_TESTING_AsyncContext *) &lfs->ac); - else - GNUNET_TESTING_async_finish ((struct - GNUNET_TESTING_AsyncContext *) &lfs->ac); -} - - -/** - * Function to start a local test case. - * - * @param write_message Callback to send a message to the master loop. - * @param router_ip Global address of the network namespace. - * @param node_ip The IP address of the node. - * @param m The number of the node in a network namespace. - * @param n The number of the network namespace. - * @param local_m The number of nodes in a network namespace. - */ -static struct GNUNET_TESTING_Interpreter * -start_testcase (GNUNET_TESTING_cmd_helper_write_cb write_message, - const char *router_ip, - const char *node_ip, - const char *m, - const char *n, - const char *local_m, - const char *topology_data, - unsigned int *read_file, - GNUNET_TESTING_cmd_helper_finish_cb finished_cb) -{ - - unsigned int n_int; - unsigned int m_int; - unsigned int local_m_int; - unsigned int num; - struct TestState *ts = GNUNET_new (struct TestState); - struct GNUNET_TESTING_NetjailTopology *topology; - unsigned int sscanf_ret = 0; - - ts->finished_cb = finished_cb; - LOG (GNUNET_ERROR_TYPE_ERROR, - "n %s m %s\n", - n, - m); - - if (GNUNET_YES == *read_file) - { - LOG (GNUNET_ERROR_TYPE_DEBUG, - "read from file\n"); - topology = GNUNET_TESTING_get_topo_from_file (topology_data); - } - else - topology = GNUNET_TESTING_get_topo_from_string (topology_data); - - ts->topology = topology; - - errno = 0; - sscanf_ret = sscanf (m, "%u", &m_int); - if (errno != 0) - { - GNUNET_log_strerror (GNUNET_ERROR_TYPE_ERROR, "sscanf"); - } - GNUNET_assert (0 < sscanf_ret); - errno = 0; - sscanf_ret = sscanf (n, "%u", &n_int); - if (errno != 0) - { - GNUNET_log_strerror (GNUNET_ERROR_TYPE_ERROR, "sscanf"); - } - GNUNET_assert (0 < sscanf_ret); - errno = 0; - sscanf_ret = sscanf (local_m, "%u", &local_m_int); - if (errno != 0) - { - GNUNET_log_strerror (GNUNET_ERROR_TYPE_ERROR, "sscanf"); - } - GNUNET_assert (0 < sscanf_ret); - - if (0 == n_int) - num = m_int; - else - num = (n_int - 1) * local_m_int + m_int + topology->nodes_x; - - block_send = GNUNET_TESTING_cmd_block_until_external_trigger ( - "block"); - block_receive = GNUNET_TESTING_cmd_block_until_external_trigger ( - "block-receive"); - connect_peers = GNUNET_TRANSPORT_cmd_connect_peers ("connect-peers", - "start-peer", - "system-create", - num, - topology, - 0, - GNUNET_YES); - local_prepared = GNUNET_TESTING_cmd_local_test_prepared ( - "local-test-prepared", - write_message); - - - GNUNET_asprintf (&ts->cfgname, - "test_transport_api2_tcp_node1.conf"); - - LOG (GNUNET_ERROR_TYPE_DEBUG, - "plugin cfgname: %s\n", - ts->cfgname); - - LOG (GNUNET_ERROR_TYPE_DEBUG, - "node ip: %s\n", - node_ip); - - GNUNET_asprintf (&ts->testdir, - "%s%s%s", - BASE_DIR, - m, - n); - - struct GNUNET_MQ_MessageHandler handlers[] = { - GNUNET_MQ_hd_var_size (test, - GNUNET_TRANSPORT_TESTING_SIMPLE_MTYPE, - struct GNUNET_TRANSPORT_TESTING_TestMessage, - ts), - GNUNET_MQ_handler_end () - }; - - struct GNUNET_TESTING_Command commands[] = { - GNUNET_TESTING_cmd_system_create ("system-create", - ts->testdir), - GNUNET_TRANSPORT_cmd_start_peer ("start-peer", - "system-create", - num, - node_ip, - handlers, - ts->cfgname, - notify_connect, - GNUNET_NO), - GNUNET_TESTING_cmd_send_peer_ready ("send-peer-ready", - write_message), - block_send, - connect_peers, - GNUNET_TRANSPORT_cmd_send_simple ("send-simple", - "start-peer", - "system-create", - num, - topology), - block_receive, - local_prepared, - GNUNET_TRANSPORT_cmd_stop_peer ("stop-peer", - "start-peer"), - GNUNET_TESTING_cmd_system_destroy ("system-destroy", - "system-create"), - GNUNET_TESTING_cmd_end () - }; - - ts->write_message = write_message; - - is = GNUNET_TESTING_run (commands, - TIMEOUT, - &handle_result, - ts); - return is; -} - - -/** - * Entry point for the plugin. - * - * @param cls NULL - * @return the exported block API - */ -void * -libgnunet_test_transport_plugin_cmd_simple_send_init (void *cls) -{ - struct GNUNET_TESTING_PluginFunctions *api; - - GNUNET_log_setup ("simple-send", - "DEBUG", - NULL); - - api = GNUNET_new (struct GNUNET_TESTING_PluginFunctions); - api->start_testcase = &start_testcase; - api->all_peers_started = &all_peers_started; - api->all_local_tests_prepared = all_local_tests_prepared; - api->get_waiting_for_barriers = get_waiting_for_barriers; - return api; -} - - -/** - * Exit point from the plugin. - * - * @param cls the return value from #libgnunet_test_transport_plugin_block_test_init - * @return NULL - */ -void * -libgnunet_test_transport_plugin_cmd_simple_send_done (void *cls) -{ - struct GNUNET_TESTING_PluginFunctions *api = cls; - - GNUNET_free (api); - return NULL; -} - - -/* end of plugin_cmd_simple_send.c */ diff --git a/src/transport/test_transport_plugin_cmd_simple_send_broadcast.c b/src/transport/test_transport_plugin_cmd_simple_send_broadcast.c deleted file mode 100644 index d2870ab34..000000000 --- a/src/transport/test_transport_plugin_cmd_simple_send_broadcast.c +++ /dev/null @@ -1,402 +0,0 @@ -/* - This file is part of GNUnet - Copyright (C) 2021 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 . - - SPDX-License-Identifier: AGPL3.0-or-later - */ - -/** - * @file testbed/plugin_cmd_simple_send_broadcast.c - * @brief a plugin to provide the API for running test cases. - * @author t3sserakt - */ -#include "platform.h" -#include "gnunet_testing_barrier.h" -#include "gnunet_testing_netjail_lib.h" -#include "gnunet_util_lib.h" -#include "gnunet_transport_application_service.h" -#include "transport-testing2.h" -#include "transport-testing-cmds.h" -#include "gnunet_testing_barrier.h" - -/** - * Generic logging shortcut - */ -#define LOG(kind, ...) GNUNET_log (kind, __VA_ARGS__) - -#define BASE_DIR "testdir" - -#define TOPOLOGY_CONFIG "test_transport_simple_send_topo.conf" - -#define TIMEOUT GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS, 600) - -static struct GNUNET_TESTING_Command block_send; - -static struct GNUNET_TESTING_Command block_receive; - -static struct GNUNET_TESTING_Command connect_peers; - -static struct GNUNET_TESTING_Command local_prepared; - -static struct GNUNET_TESTING_Interpreter *is; - -/** - * Function called to check a message of type GNUNET_TRANSPORT_TESTING_SIMPLE_MTYPE being - * received. - * - */ -static int -check_test (void *cls, - const struct GNUNET_TRANSPORT_TESTING_TestMessage *message) -{ - return GNUNET_OK; -} - - -/** - * Function called to handle a message of type GNUNET_TRANSPORT_TESTING_SIMPLE_MTYPE - * being received. - * - */ -static void -handle_test (void *cls, - const struct GNUNET_TRANSPORT_TESTING_TestMessage *message) -{ - struct GNUNET_TESTING_AsyncContext *ac; - - GNUNET_TESTING_get_trait_async_context (&block_receive, - &ac); - GNUNET_assert (NULL != ac); - if ((GNUNET_NO == ac->finished) && (NULL == ac->cont)) - GNUNET_TESTING_async_fail ((struct GNUNET_TESTING_AsyncContext *) ac); - else if (GNUNET_NO == ac->finished) - GNUNET_TESTING_async_finish ((struct GNUNET_TESTING_AsyncContext *) ac); -} - - -struct GNUNET_TESTING_BarrierList * -get_waiting_for_barriers () -{ - // No Barrier - return GNUNET_new (struct GNUNET_TESTING_BarrierList); -} - - -/** - * Callback to set the flag indicating all peers started. Will be called via the plugin api. - * - */ -static void -all_peers_started () -{ - struct GNUNET_TESTING_AsyncContext *ac; - - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "Received message\n"); - GNUNET_TESTING_get_trait_async_context (&block_send, - &ac); - GNUNET_assert (NULL != ac); - if (NULL == ac->cont) - GNUNET_TESTING_async_fail ((struct GNUNET_TESTING_AsyncContext *) ac); - else - GNUNET_TESTING_async_finish ((struct GNUNET_TESTING_AsyncContext *) ac); -} - - -/** - * Function called with the final result of the test. - * - * @param cls the `struct MainParams` - * @param rv #GNUNET_OK if the test passed - */ -static void -handle_result (void *cls, - enum GNUNET_GenericReturnValue rv) -{ - struct TestState *ts = cls; - - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "Local test exits with status %d\n", - rv); - ts->finished_cb (rv); - GNUNET_free (ts->testdir); - GNUNET_free (ts->cfgname); - GNUNET_TESTING_free_topology (ts->topology); - GNUNET_free (ts); -} - - -/** - * Callback from start peer cmd for signaling a peer got connected. - * - */ -static void * -notify_connect (struct GNUNET_TESTING_Interpreter *is, - const struct GNUNET_PeerIdentity *peer) -{ - struct GNUNET_TESTING_AsyncContext *ac; - void *ret = NULL; - const struct GNUNET_TESTING_Command *cmd; - struct GNUNET_TESTING_BlockState *bs; - - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "notify_connect\n"); - - GNUNET_TESTING_get_trait_async_context (&connect_peers, - &ac); - - if (NULL != ac->is) - { - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "notify_connect running\n"); - GNUNET_assert (NULL != ac); - if (NULL == ac->cont) - GNUNET_TESTING_async_fail (ac); - else - GNUNET_TESTING_async_finish (ac); - } - else - { - cmd = GNUNET_TESTING_interpreter_lookup_future_command (is, - "connect-peers"); - - LOG (GNUNET_ERROR_TYPE_DEBUG, - "block state %s\n", - cmd->label); - GNUNET_TESTING_get_trait_block_state ( - cmd, - &bs); - - LOG (GNUNET_ERROR_TYPE_DEBUG, - "block state %u\n", - bs->asynchronous_finish); - bs->asynchronous_finish = GNUNET_YES; - LOG (GNUNET_ERROR_TYPE_DEBUG, - "block state %u\n", - bs->asynchronous_finish); - } - - return ret; -} - - -/** - * Callback to set the flag indicating all peers are prepared to finish. Will be called via the plugin api. - */ -static void -all_local_tests_prepared () -{ - const struct GNUNET_TESTING_LocalPreparedState *lfs; - - GNUNET_TESTING_get_trait_local_prepared_state (&local_prepared, - &lfs); - GNUNET_assert (NULL != &lfs->ac); - if (NULL == lfs->ac.cont) - GNUNET_TESTING_async_fail ((struct GNUNET_TESTING_AsyncContext *) &lfs->ac); - else - GNUNET_TESTING_async_finish ((struct - GNUNET_TESTING_AsyncContext *) &lfs->ac); -} - - -/** - * Function to start a local test case. - * - * @param write_message Callback to send a message to the master loop. - * @param router_ip Global address of the network namespace. - * @param node_ip The IP address of the node. - * @param m The number of the node in a network namespace. - * @param n The number of the network namespace. - * @param local_m The number of nodes in a network namespace. - */ -static struct GNUNET_TESTING_Interpreter * -start_testcase (GNUNET_TESTING_cmd_helper_write_cb write_message, - const char *router_ip, - const char *node_ip, - const char *m, - const char *n, - const char *local_m, - const char *topology_data, - unsigned int *read_file, - GNUNET_TESTING_cmd_helper_finish_cb finished_cb) -{ - unsigned int n_int; - unsigned int m_int; - unsigned int local_m_int; - unsigned int num; - struct TestState *ts = GNUNET_new (struct TestState); - struct GNUNET_TESTING_NetjailTopology *topology; - unsigned int sscanf_ret = 0; - - ts->finished_cb = finished_cb; - LOG (GNUNET_ERROR_TYPE_ERROR, - "n %s m %s\n", - n, - m); - - if (GNUNET_YES == *read_file) - { - LOG (GNUNET_ERROR_TYPE_DEBUG, - "read from file\n"); - topology = GNUNET_TESTING_get_topo_from_file (topology_data); - } - else - topology = GNUNET_TESTING_get_topo_from_string (topology_data); - - ts->topology = topology; - - errno = 0; - sscanf_ret = sscanf (m, "%u", &m_int); - if (errno != 0) - { - GNUNET_log_strerror (GNUNET_ERROR_TYPE_ERROR, "sscanf"); - } - GNUNET_assert (0 < sscanf_ret); - errno = 0; - sscanf_ret = sscanf (n, "%u", &n_int); - if (errno != 0) - { - GNUNET_log_strerror (GNUNET_ERROR_TYPE_ERROR, "sscanf"); - } - GNUNET_assert (0 < sscanf_ret); - errno = 0; - sscanf_ret = sscanf (local_m, "%u", &local_m_int); - if (errno != 0) - { - GNUNET_log_strerror (GNUNET_ERROR_TYPE_ERROR, "sscanf"); - } - GNUNET_assert (0 < sscanf_ret); - - if (0 == n_int) - num = m_int; - else - num = (n_int - 1) * local_m_int + m_int + topology->nodes_x; - - block_send = GNUNET_TESTING_cmd_block_until_external_trigger ("block"); - block_receive = GNUNET_TESTING_cmd_block_until_external_trigger ( - "block-receive"); - connect_peers = GNUNET_TESTING_cmd_block_until_external_trigger ( - "connect-peers"); - local_prepared = GNUNET_TESTING_cmd_local_test_prepared ( - "local-test-prepared", - write_message); - - - GNUNET_asprintf (&ts->cfgname, - "test_transport_api2_tcp_node1.conf"); - - LOG (GNUNET_ERROR_TYPE_DEBUG, - "plugin cfgname: %s\n", - ts->cfgname); - - LOG (GNUNET_ERROR_TYPE_DEBUG, - "node ip: %s\n", - node_ip); - - GNUNET_asprintf (&ts->testdir, - "%s%s%s", - BASE_DIR, - m, - n); - - struct GNUNET_MQ_MessageHandler handlers[] = { - GNUNET_MQ_hd_var_size (test, - GNUNET_TRANSPORT_TESTING_SIMPLE_MTYPE, - struct GNUNET_TRANSPORT_TESTING_TestMessage, - ts), - GNUNET_MQ_handler_end () - }; - - struct GNUNET_TESTING_Command commands[] = { - GNUNET_TESTING_cmd_system_create ("system-create", - ts->testdir), - GNUNET_TRANSPORT_cmd_start_peer ("start-peer", - "system-create", - num, - node_ip, - handlers, - ts->cfgname, - notify_connect, - GNUNET_YES), - GNUNET_TESTING_cmd_send_peer_ready ("send-peer-ready", - write_message), - block_send, - connect_peers, - GNUNET_TRANSPORT_cmd_send_simple ("send-simple", - "start-peer", - "system-create", - num, - topology), - block_receive, - local_prepared, - GNUNET_TRANSPORT_cmd_stop_peer ("stop-peer", - "start-peer"), - GNUNET_TESTING_cmd_system_destroy ("system-destroy", - "system-create"), - GNUNET_TESTING_cmd_end () - }; - - ts->write_message = write_message; - - is = GNUNET_TESTING_run (commands, - TIMEOUT, - &handle_result, - ts); - return is; -} - - -/** - * Entry point for the plugin. - * - * @param cls NULL - * @return the exported block API - */ -void * -libgnunet_test_transport_plugin_cmd_simple_send_broadcast_init (void *cls) -{ - struct GNUNET_TESTING_PluginFunctions *api; - - GNUNET_log_setup ("simple-send", - "DEBUG", - NULL); - - api = GNUNET_new (struct GNUNET_TESTING_PluginFunctions); - api->start_testcase = &start_testcase; - api->all_peers_started = &all_peers_started; - api->all_local_tests_prepared = all_local_tests_prepared; - api->get_waiting_for_barriers = get_waiting_for_barriers; - return api; -} - - -/** - * Exit point from the plugin. - * - * @param cls the return value from #libgnunet_test_transport_plugin_block_test_init - * @return NULL - */ -void * -libgnunet_test_transport_plugin_cmd_simple_send_broadcast_done (void *cls) -{ - struct GNUNET_TESTING_PluginFunctions *api = cls; - - GNUNET_free (api); - return NULL; -} - - -/* end of plugin_cmd_simple_send_broadcast.c */ diff --git a/src/transport/test_transport_plugin_cmd_simple_send_dv.c b/src/transport/test_transport_plugin_cmd_simple_send_dv.c deleted file mode 100644 index d35672cd9..000000000 --- a/src/transport/test_transport_plugin_cmd_simple_send_dv.c +++ /dev/null @@ -1,434 +0,0 @@ -/* - This file is part of GNUnet - Copyright (C) 2021 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 . - - SPDX-License-Identifier: AGPL3.0-or-later - */ - -/** - * @file testbed/plugin_cmd_simple_send_broadcast.c - * @brief a plugin to provide the API for running test cases. - * @author t3sserakt - */ -#include "platform.h" -#include "gnunet_testing_barrier.h" -#include "gnunet_testing_netjail_lib.h" -#include "gnunet_util_lib.h" -#include "gnunet_transport_application_service.h" -#include "transport-testing2.h" -#include "transport-testing-cmds.h" -#include "gnunet_testing_barrier.h" - -/** - * Generic logging shortcut - */ -#define LOG(kind, ...) GNUNET_log (kind, __VA_ARGS__) - -#define BASE_DIR "testdir" - -#define TOPOLOGY_CONFIG "test_transport_simple_send_topo.conf" - -#define TIMEOUT GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS, 600) - -/** - * The number of messages received. - */ -static unsigned int number_received; - -static struct GNUNET_TESTING_Command block_send; - -static struct GNUNET_TESTING_Command block_receive; - -static struct GNUNET_TESTING_Command connect_peers; - -static struct GNUNET_TESTING_Command local_prepared; - -static struct GNUNET_TESTING_Command start_peer; - -static struct GNUNET_TESTING_Interpreter *is; - -/** - * Function called to check a message of type GNUNET_TRANSPORT_TESTING_SIMPLE_MTYPE being - * received. - * - */ -static int -check_test (void *cls, - const struct GNUNET_TRANSPORT_TESTING_TestMessage *message) -{ - GNUNET_assert (NULL != cls); - return GNUNET_OK; -} - - -/** - * Function called to handle a message of type GNUNET_TRANSPORT_TESTING_SIMPLE_MTYPE - * being received. - * - */ -static void -handle_test (void *cls, - const struct GNUNET_TRANSPORT_TESTING_TestMessage *message) -{ - struct GNUNET_PeerIdentity *peer = cls; - struct GNUNET_TESTING_AsyncContext *ac_block; - const struct GNUNET_CONTAINER_MultiShortmap *connected_peers_map; - unsigned int connected; - struct GNUNET_TESTING_BlockState *bs; - struct GNUNET_TRANSPORT_CoreHandle *ch; - const struct GNUNET_TESTING_StartPeerState *sps; - - - GNUNET_TRANSPORT_get_trait_state (&start_peer, - &sps); - ch = sps->th; - GNUNET_TRANSPORT_get_trait_connected_peers_map (&start_peer, - &connected_peers_map); - - if (NULL != connected_peers_map) - { - connected = GNUNET_CONTAINER_multishortmap_size ( - connected_peers_map); - - number_received++; - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "Received %u test message(s) from %s, %u connected peer(s)\n", - number_received, - GNUNET_i2s (peer), - connected); - - GNUNET_TESTING_get_trait_async_context (&block_receive, - &ac_block); - - if (connected == number_received) - { - if (NULL != ac_block->is) - { - GNUNET_assert (NULL != ac_block); - if (NULL == ac_block->cont) - GNUNET_TESTING_async_fail ((struct - GNUNET_TESTING_AsyncContext *) ac_block); - else - GNUNET_TESTING_async_finish ((struct - GNUNET_TESTING_AsyncContext *) ac_block); - } - else - { - GNUNET_TESTING_get_trait_block_state ( - &block_receive, - &bs); - bs->asynchronous_finish = GNUNET_YES; - } - - } - } - GNUNET_TRANSPORT_core_receive_continue (ch, peer); -} - - -struct GNUNET_TESTING_BarrierList * -get_waiting_for_barriers () -{ - // No Barrier - return GNUNET_new (struct GNUNET_TESTING_BarrierList); -} - - -/** - * Callback to set the flag indicating all peers started. Will be called via the plugin api. - * - */ -static void -all_peers_started () -{ - struct GNUNET_TESTING_AsyncContext *ac; - - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "Received message\n"); - GNUNET_TESTING_get_trait_async_context (&block_send, - &ac); - GNUNET_assert (NULL != ac); - if (NULL == ac->cont) - GNUNET_TESTING_async_fail ((struct GNUNET_TESTING_AsyncContext *) ac); - else - GNUNET_TESTING_async_finish ((struct GNUNET_TESTING_AsyncContext *) ac); -} - - -/** - * Function called with the final result of the test. - * - * @param cls the `struct MainParams` - * @param rv #GNUNET_OK if the test passed - */ -static void -handle_result (void *cls, - enum GNUNET_GenericReturnValue rv) -{ - struct TestState *ts = cls; - - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "Local test exits with status %d\n", - rv); - ts->finished_cb (rv); - GNUNET_free (ts->testdir); - GNUNET_free (ts->cfgname); - GNUNET_TESTING_free_topology (ts->topology); - GNUNET_free (ts); -} - - -/** - * Callback from start peer cmd for signaling a peer got connected. - * - */ -static void * -notify_connect (struct GNUNET_TESTING_Interpreter *is, - const struct GNUNET_PeerIdentity *peer) -{ - const struct ConnectPeersState *cps; - const struct GNUNET_TESTING_Command *cmd; - - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "notify_connect peer %s\n", - GNUNET_i2s (peer)); - // FIXME: modifying future is a bit unclean, not easy to follow logic; - // might be better to when reaching the future command to look into - // the past... - cmd = GNUNET_TESTING_interpreter_lookup_command_all (is, - "connect-peers"); - // FIXME: check return value! - GNUNET_TRANSPORT_get_trait_connect_peer_state (cmd, - &cps); - cps->notify_connect (is, - peer); - return NULL; -} - - -/** - * Callback to set the flag indicating all peers are prepared to finish. Will be called via the plugin api. - */ -static void -all_local_tests_prepared () -{ - const struct GNUNET_TESTING_LocalPreparedState *lfs; - - GNUNET_TESTING_get_trait_local_prepared_state (&local_prepared, - &lfs); - GNUNET_assert (NULL != &lfs->ac); - if (NULL == lfs->ac.cont) - GNUNET_TESTING_async_fail ((struct GNUNET_TESTING_AsyncContext *) &lfs->ac); - else - GNUNET_TESTING_async_finish ((struct - GNUNET_TESTING_AsyncContext *) &lfs->ac); -} - - -/** - * Function to start a local test case. - * - * @param write_message Callback to send a message to the master loop. - * @param router_ip Global address of the network namespace. - * @param node_ip The IP address of the node. - * @param m The number of the node in a network namespace. - * @param n The number of the network namespace. - * @param local_m The number of nodes in a network namespace. - */ -static struct GNUNET_TESTING_Interpreter * -start_testcase (GNUNET_TESTING_cmd_helper_write_cb write_message, - const char *router_ip, - const char *node_ip, - const char *m, - const char *n, - const char *local_m, - const char *topology_data, - unsigned int *read_file, - GNUNET_TESTING_cmd_helper_finish_cb finished_cb) -{ - unsigned int n_int; - unsigned int m_int; - unsigned int local_m_int; - unsigned int num; - struct TestState *ts = GNUNET_new (struct TestState); - struct GNUNET_TESTING_NetjailTopology *topology; - struct GNUNET_MQ_MessageHandler handlers[] = { - GNUNET_MQ_hd_var_size (test, - GNUNET_TRANSPORT_TESTING_SIMPLE_MTYPE, - struct GNUNET_TRANSPORT_TESTING_TestMessage, - ts), - GNUNET_MQ_handler_end () - }; - unsigned int sscanf_ret = 0; - - ts->finished_cb = finished_cb; - LOG (GNUNET_ERROR_TYPE_ERROR, - "n %s m %s\n", - n, - m); - - if (GNUNET_YES == *read_file) - { - LOG (GNUNET_ERROR_TYPE_DEBUG, - "read from file\n"); - topology = GNUNET_TESTING_get_topo_from_file (topology_data); - } - else - topology = GNUNET_TESTING_get_topo_from_string (topology_data); - - ts->topology = topology; - - errno = 0; - sscanf_ret = sscanf (m, "%u", &m_int); - if (errno != 0) - { - GNUNET_log_strerror (GNUNET_ERROR_TYPE_ERROR, "sscanf"); - } - GNUNET_assert (0 < sscanf_ret); - errno = 0; - sscanf_ret = sscanf (n, "%u", &n_int); - if (errno != 0) - { - GNUNET_log_strerror (GNUNET_ERROR_TYPE_ERROR, "sscanf"); - } - GNUNET_assert (0 < sscanf_ret); - errno = 0; - sscanf_ret = sscanf (local_m, "%u", &local_m_int); - if (errno != 0) - { - GNUNET_log_strerror (GNUNET_ERROR_TYPE_ERROR, "sscanf"); - } - GNUNET_assert (0 < sscanf_ret); - - if (0 == n_int) - num = m_int; - else - num = (n_int - 1) * local_m_int + m_int + topology->nodes_x; - - block_send = GNUNET_TESTING_cmd_block_until_external_trigger ("block"); - block_receive = GNUNET_TESTING_cmd_block_until_external_trigger ( - "block-receive"); - connect_peers = GNUNET_TRANSPORT_cmd_connect_peers ( - "connect-peers", - "start-peer", - "system-create", - num, - topology, - topology->additional_connects, - GNUNET_YES); - local_prepared = GNUNET_TESTING_cmd_local_test_prepared ( - "local-test-prepared", - write_message); - - - GNUNET_asprintf (&ts->cfgname, - "test_transport_api2_tcp_node1.conf"); - - LOG (GNUNET_ERROR_TYPE_DEBUG, - "plugin cfgname: %s\n", - ts->cfgname); - - LOG (GNUNET_ERROR_TYPE_DEBUG, - "node ip: %s\n", - node_ip); - - GNUNET_asprintf (&ts->testdir, - "%s%s%s", - BASE_DIR, - m, - n); - - start_peer = GNUNET_TRANSPORT_cmd_start_peer ("start-peer", - "system-create", - num, - node_ip, - handlers, - ts->cfgname, - notify_connect, - GNUNET_NO); - struct GNUNET_TESTING_Command commands[] = { - GNUNET_TESTING_cmd_system_create ("system-create", - ts->testdir), - start_peer, - GNUNET_TESTING_cmd_send_peer_ready ("send-peer-ready", - write_message), - block_send, - connect_peers, - GNUNET_TRANSPORT_cmd_send_simple ("send-simple", - "start-peer", - "system-create", - num, - topology), - block_receive, - local_prepared, - GNUNET_TRANSPORT_cmd_stop_peer ("stop-peer", - "start-peer"), - GNUNET_TESTING_cmd_system_destroy ("system-destroy", - "system-create"), - GNUNET_TESTING_cmd_end () - }; - - ts->write_message = write_message; - - is = GNUNET_TESTING_run (commands, - TIMEOUT, - &handle_result, - ts); - return is; -} - - -/** - * Entry point for the plugin. - * - * @param cls NULL - * @return the exported block API - */ -void * -libgnunet_test_transport_plugin_cmd_simple_send_dv_init (void *cls) -{ - struct GNUNET_TESTING_PluginFunctions *api; - - GNUNET_log_setup ("simple-send", - "DEBUG", - NULL); - - api = GNUNET_new (struct GNUNET_TESTING_PluginFunctions); - api->start_testcase = &start_testcase; - api->all_peers_started = &all_peers_started; - api->all_local_tests_prepared = all_local_tests_prepared; - api->get_waiting_for_barriers = get_waiting_for_barriers; - return api; -} - - -/** - * Exit point from the plugin. - * - * @param cls the return value from #libgnunet_test_transport_plugin_block_test_init - * @return NULL - */ -void * -libgnunet_test_transport_plugin_cmd_simple_send_dv_done (void *cls) -{ - struct GNUNET_TESTING_PluginFunctions *api = cls; - - GNUNET_free (api); - return NULL; -} - - -/* end of plugin_cmd_simple_send_broadcast.c */ diff --git a/src/transport/test_transport_plugin_cmd_simple_send_performance.c b/src/transport/test_transport_plugin_cmd_simple_send_performance.c deleted file mode 100644 index 2baa7b5b4..000000000 --- a/src/transport/test_transport_plugin_cmd_simple_send_performance.c +++ /dev/null @@ -1,480 +0,0 @@ -/* - This file is part of GNUnet - Copyright (C) 2021 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 . - - SPDX-License-Identifier: AGPL3.0-or-later - */ - -/** - * @file testbed/plugin_cmd_simple_send.c - * @brief a plugin to provide the API for running test cases. - * @author t3sserakt - */ -#include "platform.h" -#include "gnunet_testing_barrier.h" -#include "gnunet_testing_netjail_lib.h" -#include "gnunet_util_lib.h" -#include "gnunet_transport_application_service.h" -#include "transport-testing2.h" -#include "transport-testing-cmds.h" -#include "gnunet_testing_barrier.h" - -/** - * Generic logging shortcut - */ -#define LOG(kind, ...) GNUNET_log (kind, __VA_ARGS__) - -#define BASE_DIR "testdir" - -#define TOPOLOGY_CONFIG "test_transport_simple_send_topo.conf" - -#define TIMEOUT GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS, 600) - -#define MAX_RECEIVED 1000 - -#define MESSAGE_SIZE 65000 - -static struct GNUNET_TESTING_Command block_send; - -static struct GNUNET_TESTING_Command block_receive; - -static struct GNUNET_TESTING_Command connect_peers; - -static struct GNUNET_TESTING_Command local_prepared; - -static struct GNUNET_TESTING_Command start_peer; - -static struct GNUNET_TESTING_Interpreter *is; - -static struct GNUNET_CONTAINER_MultiPeerMap *senders; - -struct Sender -{ - /** - * Number of received messages from sender. - */ - unsigned long long num_received; - - /** - * Sample mean time the message traveled. - */ - struct GNUNET_TIME_Relative mean_time; - - /** - * Time the first message was send. - */ - struct GNUNET_TIME_Absolute time_first; -}; - -/** - * Function called to check a message of type GNUNET_TRANSPORT_TESTING_SIMPLE_MTYPE being - * received. - * - */ -static int -check_test (void *cls, - const struct - GNUNET_TRANSPORT_TESTING_PerformanceTestMessage *message) -{ - return GNUNET_OK; -} - - -/** - * Function called to handle a message of type GNUNET_TRANSPORT_TESTING_SIMPLE_MTYPE - * being received. - * - */ -static void -handle_test (void *cls, - const struct - GNUNET_TRANSPORT_TESTING_PerformanceTestMessage *message) -{ - struct GNUNET_PeerIdentity *peer = cls; - struct GNUNET_TESTING_AsyncContext *ac; - struct Sender *sender; - struct GNUNET_TIME_Absolute time_send; - struct GNUNET_TIME_Absolute now; - struct GNUNET_TIME_Relative time_traveled; - uint32_t num; - struct GNUNET_TRANSPORT_CoreHandle *ch; - const struct GNUNET_TESTING_StartPeerState *sps; - - - GNUNET_TRANSPORT_get_trait_state (&start_peer, - &sps); - ch = sps->th; - num = ntohl (message->num); - GNUNET_TESTING_get_trait_async_context (&block_receive, - &ac); - GNUNET_assert (NULL != ac); - - sender = GNUNET_CONTAINER_multipeermap_get (senders, peer); - - now = GNUNET_TIME_absolute_get (); - time_send = GNUNET_TIME_absolute_ntoh (message->time_send); - - time_traveled = GNUNET_TIME_absolute_get_difference (time_send, now); - - if (NULL == sender) - { - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "time traveled init %s\n", - GNUNET_i2s (peer)); - sender = GNUNET_new (struct Sender); - sender->time_first = time_send; - sender->mean_time = GNUNET_TIME_UNIT_ZERO; - GNUNET_assert (GNUNET_OK == GNUNET_CONTAINER_multipeermap_put (senders, - peer, sender, - GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_ONLY)); - } - - if (GNUNET_TIME_UNIT_ZERO.rel_value_us == sender->mean_time.rel_value_us) - { - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "time traveld mean zero\n"); - sender->mean_time = time_traveled; - } - else - { - double factor = (double) sender->num_received - / ((double) sender->num_received + 1.0); - struct GNUNET_TIME_Relative s1; - struct GNUNET_TIME_Relative s2; - - s1 = GNUNET_TIME_relative_multiply (sender->mean_time, - factor); - s2 = GNUNET_TIME_relative_divide (time_traveled, - sender->num_received + 1); - sender->mean_time = GNUNET_TIME_relative_add (s1, s2); - } - - sender->num_received++; - - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "time traveled: %llu\n", - time_traveled.rel_value_us); - GNUNET_log (GNUNET_ERROR_TYPE_INFO, - "mean time traveled: %s %llu messages received with message number %u\n", - GNUNET_STRINGS_relative_time_to_string (sender->mean_time, - GNUNET_NO), - sender->num_received, - num); - if (floor (MAX_RECEIVED * (1 - 1.0 / 200)) < sender->num_received && NULL == - ac->cont) - { - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "time traveled failed\n"); - // GNUNET_TESTING_async_fail ((struct GNUNET_TESTING_AsyncContext *) ac); - } - else if (floor (MAX_RECEIVED * (1 - 1.0 / 200)) < sender->num_received && - GNUNET_NO == ac->finished) - { - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "time traveled finish\n"); - GNUNET_TESTING_async_finish ((struct GNUNET_TESTING_AsyncContext *) ac); - } - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "time traveled end\n"); - GNUNET_TRANSPORT_core_receive_continue (ch, peer); -} - - -struct GNUNET_TESTING_BarrierList* -get_waiting_for_barriers () -{ - struct GNUNET_TESTING_BarrierList*barriers; - struct GNUNET_TESTING_BarrierListEntry *ble; - - barriers = GNUNET_new (struct GNUNET_TESTING_BarrierList); - ble = GNUNET_new (struct GNUNET_TESTING_BarrierListEntry); - ble->barrier_name = "ready-to-connect"; - ble->expected_reaches = 1; - GNUNET_CONTAINER_DLL_insert (barriers->head, - barriers->tail, - ble); - - ble = GNUNET_new (struct GNUNET_TESTING_BarrierListEntry); - ble->barrier_name = "test-case-finished"; - ble->expected_reaches = 1; - GNUNET_CONTAINER_DLL_insert (barriers->head, - barriers->tail, - ble); - return barriers; -} - - -/** - * Function called with the final result of the test. - * - * @param cls the `struct MainParams` - * @param rv #GNUNET_OK if the test passed - */ -static void -handle_result (void *cls, - enum GNUNET_GenericReturnValue rv) -{ - struct TestState *ts = cls; - - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "Local test exits with status %d\n", - rv); - - ts->finished_cb (rv); - GNUNET_free (ts->testdir); - GNUNET_free (ts->cfgname); - GNUNET_TESTING_free_topology (ts->topology); - GNUNET_free (ts); -} - - -/** - * Callback from start peer cmd for signaling a peer got connected. - * - */ -static void * -notify_connect (struct GNUNET_TESTING_Interpreter *is, - const struct GNUNET_PeerIdentity *peer) -{ - const struct ConnectPeersState *cps; - const struct GNUNET_TESTING_Command *cmd; - - cmd = GNUNET_TESTING_interpreter_lookup_command (is, - "connect-peers"); - GNUNET_TRANSPORT_get_trait_connect_peer_state (cmd, - &cps); - void *ret = NULL; - - cps->notify_connect (is, - peer); - return ret; -} - - -/** - * Function to start a local test case. - * - * @param write_message Callback to send a message to the master loop. - * @param router_ip Global address of the network namespace. - * @param node_ip The IP address of the node. - * @param m The number of the node in a network namespace. - * @param n The number of the network namespace. - * @param local_m The number of nodes in a network namespace. - * @param topology_data A file name for the file containing the topology configuration, or a string containing - * the topology configuration. - * @param read_file If read_file is GNUNET_YES this string is the filename for the topology configuration, - * if read_file is GNUNET_NO the string contains the topology configuration. - * @param finish_cb Callback function which writes a message from the helper process running on a netjail - * node to the master process * signaling that the test case running on the netjail node finished. - * @return Returns the struct GNUNET_TESTING_Interpreter of the command loop running on this netjail node. - */ -static struct GNUNET_TESTING_Interpreter * -start_testcase (GNUNET_TESTING_cmd_helper_write_cb write_message, - const char *router_ip, - const char *node_ip, - const char *m, - const char *n, - const char *local_m, - const char *topology_data, - unsigned int *read_file, - GNUNET_TESTING_cmd_helper_finish_cb finished_cb) -{ - - unsigned int n_int; - unsigned int m_int; - unsigned int local_m_int; - unsigned int num; - struct TestState *ts = GNUNET_new (struct TestState); - struct GNUNET_TESTING_NetjailTopology *topology; - unsigned int sscanf_ret = 0; - - senders = GNUNET_CONTAINER_multipeermap_create (1, GNUNET_NO); - ts->finished_cb = finished_cb; - LOG (GNUNET_ERROR_TYPE_ERROR, - "n %s m %s\n", - n, - m); - - if (GNUNET_YES == *read_file) - { - LOG (GNUNET_ERROR_TYPE_DEBUG, - "read from file\n"); - topology = GNUNET_TESTING_get_topo_from_file (topology_data); - } - else - topology = GNUNET_TESTING_get_topo_from_string (topology_data); - - ts->topology = topology; - - errno = 0; - sscanf_ret = sscanf (m, "%u", &m_int); - if (errno != 0) - { - GNUNET_log_strerror (GNUNET_ERROR_TYPE_ERROR, "sscanf"); - } - GNUNET_assert (0 < sscanf_ret); - errno = 0; - sscanf_ret = sscanf (n, "%u", &n_int); - if (errno != 0) - { - GNUNET_log_strerror (GNUNET_ERROR_TYPE_ERROR, "sscanf"); - } - GNUNET_assert (0 < sscanf_ret); - errno = 0; - sscanf_ret = sscanf (local_m, "%u", &local_m_int); - if (errno != 0) - { - GNUNET_log_strerror (GNUNET_ERROR_TYPE_ERROR, "sscanf"); - } - GNUNET_assert (0 < sscanf_ret); - - if (0 == n_int) - num = m_int; - else - num = (n_int - 1) * local_m_int + m_int + topology->nodes_x; - - block_send = GNUNET_TESTING_cmd_block_until_external_trigger ( - "block"); - block_receive = GNUNET_TESTING_cmd_block_until_external_trigger ( - "block-receive"); - connect_peers = GNUNET_TRANSPORT_cmd_connect_peers ("connect-peers", - "start-peer", - "system-create", - num, - topology, - 0, - GNUNET_YES); - local_prepared = GNUNET_TESTING_cmd_local_test_prepared ( - "local-test-prepared", - write_message); - - - GNUNET_asprintf (&ts->cfgname, - "test_transport_api2_tcp_node1.conf"); - - LOG (GNUNET_ERROR_TYPE_DEBUG, - "plugin cfgname: %s\n", - ts->cfgname); - - LOG (GNUNET_ERROR_TYPE_DEBUG, - "node ip: %s\n", - node_ip); - - GNUNET_asprintf (&ts->testdir, - "%s%s%s", - BASE_DIR, - m, - n); - - struct GNUNET_MQ_MessageHandler handlers[] = { - GNUNET_MQ_hd_var_size (test, - GNUNET_TRANSPORT_TESTING_SIMPLE_PERFORMANCE_MTYPE, - struct - GNUNET_TRANSPORT_TESTING_PerformanceTestMessage, - ts), - GNUNET_MQ_handler_end () - }; - - start_peer = GNUNET_TRANSPORT_cmd_start_peer ("start-peer", - "system-create", - num, - node_ip, - handlers, - ts->cfgname, - notify_connect, - GNUNET_NO); - - struct GNUNET_TESTING_Command commands[] = { - GNUNET_TESTING_cmd_system_create ("system-create", - ts->testdir), - start_peer, - GNUNET_TESTING_cmd_barrier_reached ("ready-to-connect-reached", - "ready-to-connect", - GNUNET_NO, - num, - GNUNET_NO, - write_message), - connect_peers, - GNUNET_TRANSPORT_cmd_send_simple_performance ("send-simple", - "start-peer", - "system-create", - num, - MESSAGE_SIZE, - MAX_RECEIVED, - topology), - block_receive, - GNUNET_TESTING_cmd_barrier_reached ("test-case-finished-reached", - "test-case-finished", - GNUNET_NO, - num, - GNUNET_NO, - write_message), - GNUNET_TRANSPORT_cmd_stop_peer ("stop-peer", - "start-peer"), - GNUNET_TESTING_cmd_system_destroy ("system-destroy", - "system-create"), - GNUNET_TESTING_cmd_end () - }; - - ts->write_message = write_message; - - is = GNUNET_TESTING_run (commands, - TIMEOUT, - &handle_result, - ts); - return is; -} - - -/** - * Entry point for the plugin. - * - * @param cls NULL - * @return the exported block API - */ -void * -libgnunet_test_transport_plugin_cmd_simple_send_performance_init (void *cls) -{ - struct GNUNET_TESTING_PluginFunctions *api; - - GNUNET_log_setup ("simple-send", - "DEBUG", - NULL); - - api = GNUNET_new (struct GNUNET_TESTING_PluginFunctions); - api->start_testcase = &start_testcase; - api->get_waiting_for_barriers = get_waiting_for_barriers; - return api; -} - - -/** - * Exit point from the plugin. - * - * @param cls the return value from #libgnunet_test_transport_plugin_simple_send_performance_init - * @return NULL - */ -void * -libgnunet_test_transport_plugin_cmd_simple_send_performance_done (void *cls) -{ - struct GNUNET_TESTING_PluginFunctions *api = cls; - - GNUNET_free (api); - return NULL; -} - - -/* end of plugin_cmd_simple_send.c */ diff --git a/src/transport/test_transport_plugin_cmd_udp_backchannel.c b/src/transport/test_transport_plugin_cmd_udp_backchannel.c deleted file mode 100644 index 378caf7df..000000000 --- a/src/transport/test_transport_plugin_cmd_udp_backchannel.c +++ /dev/null @@ -1,371 +0,0 @@ -/* - This file is part of GNUnet - Copyright (C) 2021 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 . - - SPDX-License-Identifier: AGPL3.0-or-later - */ - -/** - * @file testbed/plugin_cmd_simple_send.c - * @brief a plugin to provide the API for running test cases. - * @author t3sserakt - */ -#include "platform.h" -#include "gnunet_testing_barrier.h" -#include "gnunet_testing_netjail_lib.h" -#include "gnunet_util_lib.h" -#include "gnunet_transport_application_service.h" -#include "transport-testing2.h" -#include "transport-testing-cmds.h" -#include "gnunet_testing_barrier.h" - -/** - * Generic logging shortcut - */ -#define LOG(kind, ...) GNUNET_log_from (kind, "udp-backchannel", __VA_ARGS__) - -#define BASE_DIR "testdir" - -#define TOPOLOGY_CONFIG "test_transport_udp_backchannel_topo.conf" - -#define TIMEOUT GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS, 600) - -static struct GNUNET_TESTING_Command block_send; - -static struct GNUNET_TESTING_Command connect_peers; - -static struct GNUNET_TESTING_Command local_prepared; - -static struct GNUNET_TESTING_Interpreter *is; - - -/** - * Function called to check a message of type GNUNET_TRANSPORT_TESTING_SIMPLE_MTYPE being - * received. - * - */ -static int -check_test (void *cls, - const struct GNUNET_TRANSPORT_TESTING_TestMessage *message) -{ - return GNUNET_OK; -} - - -/** - * Function called to handle a message of type GNUNET_TRANSPORT_TESTING_SIMPLE_MTYPE - * being received. - * - */ -static void -handle_test (void *cls, - const struct GNUNET_TRANSPORT_TESTING_TestMessage *message) -{ - // struct GNUNET_TESTING_AsyncContext *ac; - - /*GNUNET_TESTING_get_trait_async_context (&block_receive, - &ac); - if ((NULL == ac) || (NULL == ac->cont)) - GNUNET_TESTING_async_fail (ac); - else - GNUNET_TESTING_async_finish (ac);*/ -} - - -struct GNUNET_TESTING_BarrierList * -get_waiting_for_barriers () -{ - // No Barrier - return GNUNET_new (struct GNUNET_TESTING_BarrierList); -} - - -/** - * Callback to set the flag indicating all peers started. Will be called via the plugin api. - * - */ -static void -all_peers_started () -{ - struct GNUNET_TESTING_AsyncContext *ac; - - GNUNET_TESTING_get_trait_async_context (&block_send, - &ac); - GNUNET_assert (NULL != ac); - if ((NULL == ac->cont)) - GNUNET_TESTING_async_fail ((struct GNUNET_TESTING_AsyncContext *) ac); - else - GNUNET_TESTING_async_finish ((struct GNUNET_TESTING_AsyncContext *) ac); -} - - -/** -* Function called with the final result of the test. -* -* @param cls the `struct MainParams` -* @param rv #GNUNET_OK if the test passed -*/ -static void -handle_result (void *cls, - enum GNUNET_GenericReturnValue rv) -{ - struct TestState *ts = cls; - - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "Local test exits with status %d\n", - rv); - - ts->finished_cb (rv); - GNUNET_free (ts->testdir); - GNUNET_free (ts->cfgname); - GNUNET_TESTING_free_topology (ts->topology); - GNUNET_free (ts); -} - - -/** - * Callback from start peer cmd for signaling a peer got connected. - * - */ -static void * -notify_connect (struct GNUNET_TESTING_Interpreter *is, - const struct GNUNET_PeerIdentity *peer) -{ - const struct ConnectPeersState *cps; - - GNUNET_TRANSPORT_get_trait_connect_peer_state (&connect_peers, - &cps); - void *ret = NULL; - - cps->notify_connect (is, - peer); - return ret; -} - - -/** - * Callback to set the flag indicating all peers are prepared to finish. Will be called via the plugin api. - */ -static void -all_local_tests_prepared () -{ - const struct GNUNET_TESTING_LocalPreparedState *lfs; - - GNUNET_TESTING_get_trait_local_prepared_state (&local_prepared, - &lfs); - GNUNET_assert (NULL != &lfs->ac); - if (NULL == lfs->ac.cont) - GNUNET_TESTING_async_fail ((struct GNUNET_TESTING_AsyncContext *) &lfs->ac); - else - GNUNET_TESTING_async_finish ((struct - GNUNET_TESTING_AsyncContext *) &lfs->ac); -} - - -/** - * Function to start a local test case. - * - * @param write_message Callback to send a message to the master loop. - * @param router_ip Global address of the network namespace. - * @param node_ip The IP address of the node. - * @param m The number of the node in a network namespace. - * @param n The number of the network namespace. - * @param local_m The number of nodes in a network namespace. - */ -static struct GNUNET_TESTING_Interpreter * -start_testcase (GNUNET_TESTING_cmd_helper_write_cb write_message, - const char *router_ip, - const char *node_ip, - const char *m, - const char *n, - const char *local_m, - const char *topology_data, - unsigned int *read_file, - GNUNET_TESTING_cmd_helper_finish_cb finished_cb) -{ - - unsigned int n_int; - unsigned int m_int; - unsigned int local_m_int; - unsigned int num; - struct TestState *ts = GNUNET_new (struct TestState); - struct GNUNET_TESTING_NetjailTopology *topology; - unsigned int sscanf_ret = 0; - - ts->finished_cb = finished_cb; - LOG (GNUNET_ERROR_TYPE_ERROR, - "n %s m %s\n", - n, - m); - - if (GNUNET_YES == *read_file) - { - LOG (GNUNET_ERROR_TYPE_DEBUG, - "read from file\n"); - topology = GNUNET_TESTING_get_topo_from_file (topology_data); - } - else - topology = GNUNET_TESTING_get_topo_from_string (topology_data); - - ts->topology = topology; - - errno = 0; - sscanf_ret = sscanf (m, "%u", &m_int); - if (errno != 0) - { - GNUNET_log_strerror (GNUNET_ERROR_TYPE_ERROR, "sscanf"); - } - GNUNET_assert (0 < sscanf_ret); - errno = 0; - sscanf_ret = sscanf (n, "%u", &n_int); - if (errno != 0) - { - GNUNET_log_strerror (GNUNET_ERROR_TYPE_ERROR, "sscanf"); - } - GNUNET_assert (0 < sscanf_ret); - errno = 0; - sscanf_ret = sscanf (local_m, "%u", &local_m_int); - if (errno != 0) - { - GNUNET_log_strerror (GNUNET_ERROR_TYPE_ERROR, "sscanf"); - } - GNUNET_assert (0 < sscanf_ret); - - - if (0 == n_int) - num = m_int; - else - num = (n_int - 1) * local_m_int + m_int + topology->nodes_x; - - block_send = GNUNET_TESTING_cmd_block_until_external_trigger ( - "block"); - connect_peers = GNUNET_TRANSPORT_cmd_connect_peers ("connect-peers", - "start-peer", - "system-create", - num, - topology, - 0, - GNUNET_YES); - local_prepared = GNUNET_TESTING_cmd_local_test_prepared ( - "local-test-prepared", - write_message); - - GNUNET_asprintf (&ts->cfgname, - "test_transport_api2_tcp_node1.conf"); - - LOG (GNUNET_ERROR_TYPE_DEBUG, - "plugin cfgname: %s\n", - ts->cfgname); - - LOG (GNUNET_ERROR_TYPE_DEBUG, - "node ip: %s\n", - node_ip); - - GNUNET_asprintf (&ts->testdir, - "%s%s%s", - BASE_DIR, - m, - n); - - struct GNUNET_MQ_MessageHandler handlers[] = { - GNUNET_MQ_hd_var_size (test, - GNUNET_TRANSPORT_TESTING_SIMPLE_MTYPE, - struct GNUNET_TRANSPORT_TESTING_TestMessage, - NULL), - GNUNET_MQ_handler_end () - }; - - struct GNUNET_TESTING_Command commands[] = { - GNUNET_TESTING_cmd_system_create ("system-create", - ts->testdir), - GNUNET_TRANSPORT_cmd_start_peer ("start-peer", - "system-create", - num, - node_ip, - handlers, - ts->cfgname, - notify_connect, - GNUNET_NO), - GNUNET_TESTING_cmd_send_peer_ready ("send-peer-ready", - write_message), - block_send, - connect_peers, - GNUNET_TRANSPORT_cmd_backchannel_check ("backchannel-check", - "start-peer", - "system-create", - num, - m_int, - n_int, - topology), - local_prepared, - GNUNET_TRANSPORT_cmd_stop_peer ("stop-peer", - "start-peer"), - GNUNET_TESTING_cmd_system_destroy ("system-destroy", - "system-create"), - GNUNET_TESTING_cmd_end () - }; - - ts->write_message = write_message; - - is = GNUNET_TESTING_run (commands, - TIMEOUT, - &handle_result, - ts); - return is; -} - - -/** - * Entry point for the plugin. - * - * @param cls NULL - * @return the exported block API - */ -void * -libgnunet_test_transport_plugin_cmd_udp_backchannel_init (void *cls) -{ - struct GNUNET_TESTING_PluginFunctions *api; - - GNUNET_log_setup ("udp-backchannel", - "DEBUG", - "plugin.out"); - - api = GNUNET_new (struct GNUNET_TESTING_PluginFunctions); - api->start_testcase = &start_testcase; - api->all_peers_started = &all_peers_started; - api->all_local_tests_prepared = all_local_tests_prepared; - api->get_waiting_for_barriers = get_waiting_for_barriers; - return api; -} - - -/** - * Exit point from the plugin. - * - * @param cls the return value from #libgnunet_test_transport_plugin_block_test_init - * @return NULL - */ -void * -libgnunet_test_transport_plugin_cmd_udp_backchannel_done (void *cls) -{ - struct GNUNET_TESTING_PluginFunctions *api = cls; - - GNUNET_free (api); - return NULL; -} - - -/* end of plugin_cmd_simple_send.c */ diff --git a/src/transport/test_transport_simple_send.sh b/src/transport/test_transport_simple_send.sh deleted file mode 100755 index 0250070be..000000000 --- a/src/transport/test_transport_simple_send.sh +++ /dev/null @@ -1,11 +0,0 @@ -#!/bin/bash -if ! [ -d "/run/netns" ]; then - echo You have to create the directory /run/netns. -fi -if [ -f /proc/sys/kernel/unprivileged_userns_clone ]; then - if [ "$(cat /proc/sys/kernel/unprivileged_userns_clone)" != 1 ]; then - echo -e "Error during test setup: The kernel parameter kernel.unprivileged_userns_clone has to be set to 1! One has to execute\n\n sysctl kernel.unprivileged_userns_clone=1\n" - exit 78 - fi -fi -exec unshare -r -nmU bash -c "mount -t tmpfs --make-rshared tmpfs /run/netns; ./test_transport_start_with_config test_transport_simple_send_topo.conf" diff --git a/src/transport/test_transport_simple_send_broadcast.sh b/src/transport/test_transport_simple_send_broadcast.sh deleted file mode 100755 index a4801bf70..000000000 --- a/src/transport/test_transport_simple_send_broadcast.sh +++ /dev/null @@ -1,11 +0,0 @@ -#!/bin/bash -if ! [ -d "/run/netns" ]; then - echo You have to create the directory /run/netns. -fi -if [ -f /proc/sys/kernel/unprivileged_userns_clone ]; then - if [ "$(cat /proc/sys/kernel/unprivileged_userns_clone)" != 1 ]; then - echo -e "Error during test setup: The kernel parameter kernel.unprivileged_userns_clone has to be set to 1! One has to execute\n\n sysctl kernel.unprivileged_userns_clone=1\n" - exit 78 - fi -fi -exec unshare -r -nmU bash -c "mount -t tmpfs --make-rshared tmpfs /run/netns; ./test_transport_start_with_config test_transport_simple_send_broadcast_topo.conf" diff --git a/src/transport/test_transport_simple_send_broadcast_topo.conf b/src/transport/test_transport_simple_send_broadcast_topo.conf deleted file mode 100644 index aa4964f81..000000000 --- a/src/transport/test_transport_simple_send_broadcast_topo.conf +++ /dev/null @@ -1,4 +0,0 @@ -M:2 -N:1 -X:0 -T:libgnunet_test_transport_plugin_cmd_simple_send_broadcast \ No newline at end of file diff --git a/src/transport/test_transport_simple_send_dv_circle.sh b/src/transport/test_transport_simple_send_dv_circle.sh deleted file mode 100755 index bd5f00abe..000000000 --- a/src/transport/test_transport_simple_send_dv_circle.sh +++ /dev/null @@ -1,11 +0,0 @@ -#!/bin/bash -if ! [ -d "/run/netns" ]; then - echo You have to create the directory /run/netns. -fi -if [ -f /proc/sys/kernel/unprivileged_userns_clone ]; then - if [ "$(cat /proc/sys/kernel/unprivileged_userns_clone)" != 1 ]; then - echo -e "Error during test setup: The kernel parameter kernel.unprivileged_userns_clone has to be set to 1! One has to execute\n\n sysctl kernel.unprivileged_userns_clone=1\n" - exit 78 - fi -fi -exec unshare -r -nmU bash -c "mount -t tmpfs --make-rshared tmpfs /run/netns; ./test_transport_start_with_config test_transport_distance_vector_circle_topo.conf" diff --git a/src/transport/test_transport_simple_send_dv_inverse.sh b/src/transport/test_transport_simple_send_dv_inverse.sh deleted file mode 100755 index cf7b863a0..000000000 --- a/src/transport/test_transport_simple_send_dv_inverse.sh +++ /dev/null @@ -1,12 +0,0 @@ -#!/bin/bash -if ! [ -d "/run/netns" ]; then - echo You have to create the directory /run/netns. -fi -if [ -f /proc/sys/kernel/unprivileged_userns_clone ]; then - if [ "$(cat /proc/sys/kernel/unprivileged_userns_clone)" != 1 ]; then - echo -e "Error during test setup: The kernel parameter kernel.unprivileged_userns_clone has to be set to 1! One has to execute\n\n sysctl kernel.unprivileged_userns_clone=1\n" - exit 78 - fi -fi -# exec unshare -r -nmU bash -c "mount -t tmpfs --make-rshared tmpfs /run/netns; valgrind --leak-check=full --track-origins=yes --trace-children=yes --trace-children-skip=/usr/bin/awk,/usr/bin/cut,/usr/bin/seq,/sbin/ip/sed/bash ./test_transport_start_with_config test_transport_distance_vector_inverse_topo.conf" -exec unshare -r -nmU bash -c "mount -t tmpfs --make-rshared tmpfs /run/netns; ./test_transport_start_with_config test_transport_distance_vector_inverse_topo.conf" diff --git a/src/transport/test_transport_simple_send_performance.sh b/src/transport/test_transport_simple_send_performance.sh deleted file mode 100755 index 12798c2f0..000000000 --- a/src/transport/test_transport_simple_send_performance.sh +++ /dev/null @@ -1,11 +0,0 @@ -#!/bin/bash -if ! [ -d "/run/netns" ]; then - echo You have to create the directory /run/netns. -fi -if [ -f /proc/sys/kernel/unprivileged_userns_clone ]; then - if [ "$(cat /proc/sys/kernel/unprivileged_userns_clone)" != 1 ]; then - echo -e "Error during test setup: The kernel parameter kernel.unprivileged_userns_clone has to be set to 1! One has to execute\n\n sysctl kernel.unprivileged_userns_clone=1\n" - exit 78 - fi -fi -exec unshare -r -nmU bash -c "mount -t tmpfs --make-rshared tmpfs /run/netns; ./test_transport_start_with_config test_transport_simple_send_performance_topo.conf" diff --git a/src/transport/test_transport_simple_send_performance_topo.conf b/src/transport/test_transport_simple_send_performance_topo.conf deleted file mode 100644 index 9f39cd9bc..000000000 --- a/src/transport/test_transport_simple_send_performance_topo.conf +++ /dev/null @@ -1,6 +0,0 @@ -M:2 -N:1 -X:0 -T:libgnunet_test_transport_plugin_cmd_simple_send_performance -P:1:1|{connect:{P:1:2:tcp}|{P:1:2:udp}} -P:1:2|{connect:{P:1:1:tcp}|{P:1:1:udp}} \ No newline at end of file diff --git a/src/transport/test_transport_simple_send_string.sh b/src/transport/test_transport_simple_send_string.sh deleted file mode 100755 index 211abb494..000000000 --- a/src/transport/test_transport_simple_send_string.sh +++ /dev/null @@ -1,21 +0,0 @@ -#!/bin/bash -string=$(cat << EOF -M:2 -N:1 -X:0 -T:libgnunet_test_transport_plugin_cmd_simple_send -P:1:1|{connect:{P:1:2:tcp}} -P:1:2|{connect:{P:1:1:tcp}} -EOF - ) -if ! [ -d "/run/netns" ]; then - echo You have to create the directory /run/netns. -fi -if [ -f /proc/sys/kernel/unprivileged_userns_clone ]; then - if [ "$(cat /proc/sys/kernel/unprivileged_userns_clone)" != 1 ]; then - echo -e "Error during test setup: The kernel parameter kernel.unprivileged_userns_clone has to be set to 1! One has to execute\n\n sysctl kernel.unprivileged_userns_clone=1\n" - exit 78 - fi -fi - -exec unshare -r -nmU bash -c "mount -t tmpfs --make-rshared tmpfs /run/netns; ./test_transport_start_with_config -s '$string'" diff --git a/src/transport/test_transport_simple_send_topo.conf b/src/transport/test_transport_simple_send_topo.conf deleted file mode 100644 index 2c16201f5..000000000 --- a/src/transport/test_transport_simple_send_topo.conf +++ /dev/null @@ -1,6 +0,0 @@ -M:2 -N:1 -X:0 -T:libgnunet_test_transport_plugin_cmd_simple_send -P:1:1|{connect:{P:1:2:tcp}} -P:1:2|{connect:{P:1:1:tcp}} \ No newline at end of file diff --git a/src/transport/test_transport_start_testcase.sh b/src/transport/test_transport_start_testcase.sh deleted file mode 100755 index 028c8f248..000000000 --- a/src/transport/test_transport_start_testcase.sh +++ /dev/null @@ -1,12 +0,0 @@ -#!/bin/bash -read -p "Test case configuration to use:" conf -if ! [ -d "/run/netns" ]; then - echo You have to create the directory /run/netns. -fi -if [ -f /proc/sys/kernel/unprivileged_userns_clone ]; then - if [ "$(cat /proc/sys/kernel/unprivileged_userns_clone)" != 1 ]; then - echo -e "Error during test setup: The kernel parameter kernel.unprivileged_userns_clone has to be set to 1! One has to execute\n\n sysctl kernel.unprivileged_userns_clone=1\n" - exit 78 - fi -fi -exec unshare -r -nmU bash -c "mount -t tmpfs --make-rshared tmpfs /run/netns; ./test_transport_start_with_config $conf" diff --git a/src/transport/test_transport_start_with_config.c b/src/transport/test_transport_start_with_config.c deleted file mode 100644 index 349cd65a5..000000000 --- a/src/transport/test_transport_start_with_config.c +++ /dev/null @@ -1,121 +0,0 @@ -/* - This file is part of GNUnet - Copyright (C) 2021 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 . - - SPDX-License-Identifier: AGPL3.0-or-later - */ - -/** - * @file transport/test_transport_start_with_config.c - * @brief Generic program to start testcases in an configurable topology. - * @author t3sserakt - */ -#include "platform.h" -#include "gnunet_testing_ng_lib.h" -#include "gnunet_testing_netjail_lib.h" -#include "gnunet_util_lib.h" - -#define TIMEOUT GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS, 600) - - -int -main (int argc, - char *const *argv) -{ - char *topology_data; - char *topology_data_script; - struct GNUNET_TESTING_NetjailTopology *topology; - unsigned int read_file = GNUNET_YES; - int ret; - char *rest = NULL; - char *token; - size_t single_line_len; - size_t data_len; - - GNUNET_log_setup ("test-netjail", - "INFO", - NULL); - - if (0 == strcmp ("-s", argv[1])) - { - data_len = strlen (argv[2]); - topology_data = GNUNET_malloc (data_len); - topology_data_script = GNUNET_malloc (data_len); - token = strtok_r (argv[2], "\n", &rest); - while (NULL != token) - { - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "token1 %s\n", - token); - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "token2 %s\n", - token); - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "topology_data %s\n", - topology_data); - strcat (topology_data_script, token); - strcat (topology_data_script, " "); - strcat (topology_data, token); - strcat (topology_data, "\n"); - token = strtok_r (NULL, "\n", &rest); - } - single_line_len = strlen (topology_data); - topology_data_script [single_line_len - 1] = '\0'; - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "read from string\n"); - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "topology_data %s\n", - topology_data); - read_file = GNUNET_NO; - topology = GNUNET_TESTING_get_topo_from_string (topology_data); - } - else - { - topology_data = argv[1]; - topology_data_script = argv[1]; - topology = GNUNET_TESTING_get_topo_from_file (topology_data); - } - - struct GNUNET_TESTING_Command commands[] = { - GNUNET_TESTING_cmd_netjail_start ("netjail-start", - topology_data_script, - &read_file), - GNUNET_TESTING_cmd_netjail_start_cmds_helper ("netjail-start-testbed", - topology, - &read_file, - topology_data_script, - TIMEOUT), - GNUNET_TESTING_cmd_stop_cmds_helper ("stop-testbed", - "netjail-start-testbed", - topology), - GNUNET_TESTING_cmd_netjail_stop ("netjail-stop", - topology_data_script, - &read_file), - GNUNET_TESTING_cmd_end () - }; - - ret = GNUNET_TESTING_main (commands, - TIMEOUT); - - if (0 == strcmp ("-s", argv[1])) - { - GNUNET_free (topology_data_script); - GNUNET_free (topology_data); - } - GNUNET_TESTING_free_topology (topology); - - return ret; -} diff --git a/src/transport/test_transport_test_transport_address_switch_tcp_peer1.conf b/src/transport/test_transport_test_transport_address_switch_tcp_peer1.conf deleted file mode 100644 index 920e8d199..000000000 --- a/src/transport/test_transport_test_transport_address_switch_tcp_peer1.conf +++ /dev/null @@ -1,29 +0,0 @@ -@INLINE@ template_cfg_peer1.conf - -[ats] -UNSPECIFIED_QUOTA_IN = 8 KiB -UNSPECIFIED_QUOTA_OUT = 8 KiB -# LOOPBACK -LOOPBACK_QUOTA_IN = 8 KiB -LOOPBACK_QUOTA_OUT = 8 KiB -# LAN -LAN_QUOTA_IN = 8 KiB -LAN_QUOTA_OUT = 8 KiB -# WAN -WAN_QUOTA_IN = 8 KiB -WAN_QUOTA_OUT = 8 KiB -# WLAN -WLAN_QUOTA_IN = 8 KiB -WLAN_QUOTA_OUT = 8 KiB -# BLUETOOTH -BLUETOOTH_QUOTA_IN = 8 KiB -BLUETOOTH_QUOTA_OUT = 8 KiB - -[PATHS] -GNUNET_TEST_HOME = $GNUNET_TMP/test-transport/quota-tcp-p1/ - -[transport] -PLUGINS = tcp - - - diff --git a/src/transport/test_transport_test_transport_address_switch_tcp_peer2.conf b/src/transport/test_transport_test_transport_address_switch_tcp_peer2.conf deleted file mode 100644 index 6855cd00d..000000000 --- a/src/transport/test_transport_test_transport_address_switch_tcp_peer2.conf +++ /dev/null @@ -1,29 +0,0 @@ -@INLINE@ template_cfg_peer2.conf - -[ats] -UNSPECIFIED_QUOTA_IN = 8 KiB -UNSPECIFIED_QUOTA_OUT = 8 KiB -# LOOPBACK -LOOPBACK_QUOTA_IN = 8 KiB -LOOPBACK_QUOTA_OUT = 8 KiB -# LAN -LAN_QUOTA_IN = 8 KiB -LAN_QUOTA_OUT = 8 KiB -# WAN -WAN_QUOTA_IN = 8 KiB -WAN_QUOTA_OUT = 8 KiB -# WLAN -WLAN_QUOTA_IN = 8 KiB -WLAN_QUOTA_OUT = 8 KiB -# BLUETOOTH -BLUETOOTH_QUOTA_IN = 8 KiB -BLUETOOTH_QUOTA_OUT = 8 KiB - - -[PATHS] -GNUNET_TEST_HOME = $GNUNET_TMP/test-transport/api-tcp-p2/ - -[transport] -PLUGINS = tcp - - diff --git a/src/transport/test_transport_testing_startstop.c b/src/transport/test_transport_testing_startstop.c deleted file mode 100644 index 4783c1813..000000000 --- a/src/transport/test_transport_testing_startstop.c +++ /dev/null @@ -1,138 +0,0 @@ -/* - This file is part of GNUnet. - Copyright (C) 2009, 2010, 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 . - - SPDX-License-Identifier: AGPL3.0-or-later - */ -/** - * @file transport/test_transport_testing_startstop.c - * @brief test case for transport testing library: - * start the peer, get the HELLO message and stop the peer - */ -#include "platform.h" -#include "gnunet_transport_service.h" -#include "transport-testing.h" - -#define TIMEOUT GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS, 30) - - -static struct GNUNET_SCHEDULER_Task *timeout_task; - -static struct GNUNET_TRANSPORT_TESTING_PeerContext *p; - -static struct GNUNET_TRANSPORT_TESTING_Handle *tth; - -static int ret; - - -static void -end () -{ - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "Stopping peers\n"); - if (NULL != timeout_task) - GNUNET_SCHEDULER_cancel (timeout_task); - if (NULL != p) - GNUNET_TRANSPORT_TESTING_stop_peer (p); - if (NULL != tth) - GNUNET_TRANSPORT_TESTING_done (tth); -} - - -static void -end_badly () -{ - timeout_task = NULL; - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - "Timeout!\n"); - end (); - ret = GNUNET_SYSERR; -} - - -static void -start_cb (void *cls) -{ - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "Peer %u (`%s') successfully started\n", - p->no, - GNUNET_i2s (&p->id)); - ret = 0; - GNUNET_SCHEDULER_add_now (&end, - NULL); -} - - -static void -run (void *cls, - char *const *args, - const char *cfgfile, - const struct GNUNET_CONFIGURATION_Handle *cfg) -{ - ret = 1; - tth = GNUNET_TRANSPORT_TESTING_init (); - GNUNET_assert (NULL != tth); - - timeout_task = - GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_UNIT_MINUTES, - &end_badly, - NULL); - - p = GNUNET_TRANSPORT_TESTING_start_peer (tth, - cfgfile, - 1, - NULL, /* receive cb */ - NULL, /* connect cb */ - NULL, /* disconnect cb */ - NULL, /* nc/nd closure */ - &start_cb, /* startup cb */ - NULL); /* closure */ - if (NULL == p) - { - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Failed to start peer\n"); - if (timeout_task != NULL) - GNUNET_SCHEDULER_cancel (timeout_task); - timeout_task = GNUNET_SCHEDULER_add_now (&end_badly, NULL); - } -} - - -int -main (int argc, char *argv[]) -{ - char *const argv_1[] = { "test_transport_testing", - "-c", - "test_transport_api_data.conf", - NULL }; - struct GNUNET_GETOPT_CommandLineOption options[] = { - GNUNET_GETOPT_OPTION_END - }; - - GNUNET_log_setup ("test_transport_testing_startstop", - "WARNING", - NULL); - GNUNET_PROGRAM_run ((sizeof(argv_1) / sizeof(char *)) - 1, - argv_1, - "test_transport_testing_startstop", "nohelp", - options, - &run, - &ret); - - return ret; -} - - -/* end of test_transport_testing_startstop.c */ diff --git a/src/transport/test_transport_udp_backchannel.sh b/src/transport/test_transport_udp_backchannel.sh deleted file mode 100755 index 1a7c83385..000000000 --- a/src/transport/test_transport_udp_backchannel.sh +++ /dev/null @@ -1,14 +0,0 @@ -#!/bin/bash -if [ -f "test.out" ]; then - rm test.out -fi -if ! [ -d "/run/netns" ]; then - echo You have to create the directory /run/netns. -fi -if [ -f /proc/sys/kernel/unprivileged_userns_clone ]; then - if [ "$(cat /proc/sys/kernel/unprivileged_userns_clone)" != 1 ]; then - echo -e "Error during test setup: The kernel parameter kernel.unprivileged_userns_clone has to be set to 1! One has to execute\n\n sysctl kernel.unprivileged_userns_clone=1\n" - exit 78 - fi -fi -exec unshare -r -nmU bash -c "mount -t tmpfs --make-rshared tmpfs /run/netns; GNUNET_FORCE_LOG=';;;;DEBUG' GNUNET_FORCE_LOGFILE='test.out' ./test_transport_start_with_config test_transport_udp_backchannel_topo.conf" diff --git a/src/transport/test_transport_udp_backchannel_topo.conf b/src/transport/test_transport_udp_backchannel_topo.conf deleted file mode 100644 index ad35bde0a..000000000 --- a/src/transport/test_transport_udp_backchannel_topo.conf +++ /dev/null @@ -1,7 +0,0 @@ -M:1 -N:1 -X:1 -T:libgnunet_test_transport_plugin_cmd_udp_backchannel -K:1|{connect:{P:1:1:tcp}} -R:1|{tcp_port:1}|{udp_port:0} -P:1:1|{connect:{K:1:udp}} diff --git a/src/transport/transport-testing-cmds.h b/src/transport/transport-testing-cmds.h deleted file mode 100644 index 6b6fcf4f1..000000000 --- a/src/transport/transport-testing-cmds.h +++ /dev/null @@ -1,245 +0,0 @@ -/* - This file is part of GNUnet. - Copyright (C) 2021 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 . - - SPDX-License-Identifier: AGPL3.0-or-later - */ - -/** - * @file transport-testing.h - * @brief testing lib for transport service - * @author Matthias Wachs - * @author Christian Grothoff - */ -#ifndef TRANSPORT_TESTING_CMDS_H -#define TRANSPORT_TESTING_CMDS_H -#include "gnunet_testing_ng_lib.h" -#include "gnunet_testing_plugin.h" - -typedef void * -(*GNUNET_TRANSPORT_notify_connect_cb) (struct GNUNET_TESTING_Interpreter *is, - const struct GNUNET_PeerIdentity *peer); - - - -struct TestState -{ - /** - * Callback to write messages to the master loop. - * - */ - GNUNET_TESTING_cmd_helper_write_cb write_message; - - /** - * Callback to notify the helper test case has finished. - */ - GNUNET_TESTING_cmd_helper_finish_cb finished_cb; - - /** - * The name for a specific test environment directory. - * - */ - char *testdir; - - /** - * The name for the configuration file of the specific node. - * - */ - char *cfgname; - - /** - * The complete topology information. - */ - struct GNUNET_TESTING_NetjailTopology *topology; -}; - - -/** - * Create command. - * - * @param label name for command. - * @param system_label Label of the cmd to setup a test environment. - * @param no Decimal number representing the last byte of the IP address of this peer. - * @param node_ip The IP address of this node. - * @param handlers Handler for messages received by this peer. - * @param cfgname Configuration file name for this peer. - * @param notify_connect Method which will be called, when a peer connects. - * @param broadcast Flag indicating, if broadcast should be switched on. - * @return command. - */ -struct GNUNET_TESTING_Command -GNUNET_TRANSPORT_cmd_start_peer (const char *label, - const char *system_label, - uint32_t no, - const char *node_ip, - struct GNUNET_MQ_MessageHandler *handlers, - const char *cfgname, - GNUNET_TRANSPORT_notify_connect_cb - notify_connect, - unsigned int broadcast); - - -struct GNUNET_TESTING_Command -GNUNET_TRANSPORT_cmd_stop_peer (const char *label, - const char *start_label); - - -/** - * Create command - * - * @param label name for command - * @param start_peer_label Label of the cmd to start a peer. - * @param create_label Label of the cmd which started the test system. - * @param num Number globally identifying the node. - * @param topology The topology for the test setup. - * @param additional_connects Number of additional connects this cmd will wait for not triggered by this cmd. - * @return command. - */ -struct GNUNET_TESTING_Command -GNUNET_TRANSPORT_cmd_connect_peers ( - const char *label, - const char *start_peer_label, - const char *create_label, - uint32_t num, - struct GNUNET_TESTING_NetjailTopology *topology, - unsigned int additional_connects, - unsigned int wait_for_connect); - - -/** - * Create command. - * - * @param label name for command. - * @param start_peer_label Label of the cmd to start a peer. - * @param create_label Label of the cmd which started the test system. - * @param num Number globally identifying the node. - * @param topology The topology for the test setup. - * @return command. - */ -struct GNUNET_TESTING_Command -GNUNET_TRANSPORT_cmd_send_simple (const char *label, - const char *start_peer_label, - const char *create_label, - uint32_t num, - struct GNUNET_TESTING_NetjailTopology * - topology); - -/** - * - * - * @param label name for command. - * @param start_peer_label Label of the cmd to start a peer. - * @param create_label Label of the cmd which started the test system. - * @param num Number globally identifying the node. - * @param size The size of the test message to send. - * @param max_send The number of messages to send. - * @param topology The topology for the test setup. - * @return command. - */ -struct GNUNET_TESTING_Command -GNUNET_TRANSPORT_cmd_send_simple_performance (const char *label, - const char *start_peer_label, - const char *create_label, - uint32_t num, - int size, - int max_send, - struct GNUNET_TESTING_NetjailTopology * - topology); - - -/** - * Create command. - * - * @param label name for command. - * @param start_peer_label Label of the cmd to start a peer. - * @param create_label Label of the cmd to create the testing system. - * @param num Number globally identifying the node. - * @param node_n The number of the node in a network namespace. - * @param namespace_n The number of the network namespace. - * @param topology The topology for the test setup. - * @return command. - */ -struct GNUNET_TESTING_Command -GNUNET_TRANSPORT_cmd_backchannel_check (const char *label, - const char *start_peer_label, - const char *create_label, - uint32_t num, - unsigned int node_n, - unsigned int namespace_n, - struct GNUNET_TESTING_NetjailTopology * - topology); - - -/** - * Create headers for a trait with name @a name for - * statically allocated data of type @a type. - */ -#define GNUNET_TRANSPORT_MAKE_DECL_SIMPLE_TRAIT(name,type) \ - enum GNUNET_GenericReturnValue \ - GNUNET_TRANSPORT_get_trait_ ## name ( \ - const struct GNUNET_TESTING_Command *cmd, \ - type **ret); \ - struct GNUNET_TESTING_Trait \ - GNUNET_TRANSPORT_make_trait_ ## name ( \ - type * value); - - -/** - * Create C implementation for a trait with name @a name for statically - * allocated data of type @a type. - */ -#define GNUNET_TRANSPORT_MAKE_IMPL_SIMPLE_TRAIT(name,type) \ - enum GNUNET_GenericReturnValue \ - GNUNET_TRANSPORT_get_trait_ ## name ( \ - const struct GNUNET_TESTING_Command *cmd, \ - type **ret) \ - { \ - if (NULL == cmd->traits) return GNUNET_SYSERR; \ - return cmd->traits (cmd->cls, \ - (const void **) ret, \ - GNUNET_S (name), \ - 0); \ - } \ - struct GNUNET_TESTING_Trait \ - GNUNET_TRANSPORT_make_trait_ ## name ( \ - type * value) \ - { \ - struct GNUNET_TESTING_Trait ret = { \ - .trait_name = GNUNET_S (name), \ - .ptr = (const void *) value \ - }; \ - return ret; \ - } - - -/** - * Call #op on all simple traits. - */ -#define GNUNET_TRANSPORT_SIMPLE_TRAITS(op) \ - op (peer_id, const struct GNUNET_PeerIdentity) \ - op (connected_peers_map, const struct GNUNET_CONTAINER_MultiShortmap) \ - op (hello_size, const size_t) \ - op (hello, const char) \ - op (application_handle, const struct GNUNET_TRANSPORT_ApplicationHandle) \ - op (connect_peer_state, const struct ConnectPeersState) \ - op (state, const struct GNUNET_TESTING_StartPeerState) \ - op (broadcast, const enum GNUNET_GenericReturnValue) - -GNUNET_TRANSPORT_SIMPLE_TRAITS (GNUNET_TRANSPORT_MAKE_DECL_SIMPLE_TRAIT) - - -#endif -/* end of transport_testing.h */ diff --git a/src/transport/transport-testing-communicator.c b/src/transport/transport-testing-communicator.c deleted file mode 100644 index 553426b66..000000000 --- a/src/transport/transport-testing-communicator.c +++ /dev/null @@ -1,1238 +0,0 @@ -/* - This file is part of GNUnet. - Copyright (C) 2019 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 . - - SPDX-License-Identifier: AGPL3.0-or-later - */ - -/** - * @file transport/transport-testing-communicator.c - * @brief functions related to testing-tng - * @author Christian Grothoff - * @author Julius Bünger - */ -#include "platform.h" -#include "gnunet_util_lib.h" -#include "gnunet_protocols.h" -#include "gnunet_constants.h" -#include "transport-testing-communicator.h" -#include "gnunet_hello_lib.h" -#include "gnunet_signatures.h" -#include "transport.h" -#include - -#define LOG(kind, ...) GNUNET_log_from (kind, "transport-testing2", __VA_ARGS__) - -struct MyClient -{ - struct MyClient *prev; - struct MyClient *next; - /** - * @brief Handle to the client - */ - struct GNUNET_SERVICE_Client *client; - - /** - * @brief Handle to the client - */ - struct GNUNET_MQ_Handle *c_mq; - - /** - * The TCH - */ - struct GNUNET_TRANSPORT_TESTING_TransportCommunicatorHandle *tc; - -}; - -/** - * @brief Queue of a communicator and some context - */ -struct GNUNET_TRANSPORT_TESTING_TransportCommunicatorQueue -{ - /** - * @brief Handle to the TransportCommunicator - */ - struct GNUNET_TRANSPORT_TESTING_TransportCommunicatorHandle *tc_h; - - /** - * @brief Envelope to a message that requests the opening of the queue. - * - * If the client already requests queue(s), but the communicator is not yet - * connected, we cannot send the request to open the queue. Save it until the - * communicator becomes available and send it then. - */ - struct GNUNET_MQ_Envelope *open_queue_env; - - /** - * @brief Peer ID of the peer on the other side of the queue - */ - struct GNUNET_PeerIdentity peer_id; - - /** - * @brief Queue ID - */ - uint32_t qid; - - /** - * @brief Current message id - */ - uint64_t mid; - - /** - * An `enum GNUNET_NetworkType` in NBO. - */ - uint32_t nt; - - /** - * Maximum transmission unit. UINT32_MAX for unlimited. - */ - uint32_t mtu; - - /** - * Queue length. UINT64_MAX for unlimited. - */ - uint64_t q_len; - - /** - * Queue prio - */ - uint32_t priority; - - /** - * An `enum GNUNET_TRANSPORT_ConnectionStatus` in NBO. - */ - uint32_t cs; - - /** - * @brief Next element inside a DLL - */ - struct GNUNET_TRANSPORT_TESTING_TransportCommunicatorQueue *next; - - /** - * @brief Previous element inside a DLL - */ - struct GNUNET_TRANSPORT_TESTING_TransportCommunicatorQueue *prev; -}; - - -/** - * @brief Handle/Context to a single transmission - */ -struct GNUNET_TRANSPORT_TESTING_TransportCommunicatorTransmission -{ -}; - - -/** - * @brief Check whether incoming msg indicating available communicator is - * correct - * - * @param cls Closure - * @param msg Message struct - * - * @return GNUNET_YES in case message is correct - */ -static int -check_communicator_available ( - void *cls, - const struct GNUNET_TRANSPORT_CommunicatorAvailableMessage *msg) -{ - uint16_t size; - - size = ntohs (msg->header.size) - sizeof(*msg); - if (0 == size) - return GNUNET_OK; /* receive-only communicator */ - GNUNET_MQ_check_zero_termination (msg); - return GNUNET_OK; -} - - -/** - * @brief Handle new communicator - * - * Store characteristics of communicator, call respective client callback. - * - * @param cls Closure - communicator handle - * @param msg Message struct - */ -static void -handle_communicator_available ( - void *cls, - const struct GNUNET_TRANSPORT_CommunicatorAvailableMessage *msg) -{ - struct MyClient *client = cls; - struct GNUNET_TRANSPORT_TESTING_TransportCommunicatorHandle *tc_h = - client->tc; - uint16_t size; - tc_h->c_mq = client->c_mq; - - size = ntohs (msg->header.size) - sizeof(*msg); - if (0 == size) - { - GNUNET_SERVICE_client_continue (client->client); - return; /* receive-only communicator */ - } - tc_h->c_characteristics = ntohl (msg->cc); - tc_h->c_addr_prefix = GNUNET_strdup ((const char *) &msg[1]); - if (NULL != tc_h->communicator_available_cb) - { - LOG (GNUNET_ERROR_TYPE_DEBUG, "calling communicator_available_cb()\n"); - tc_h->communicator_available_cb (tc_h->cb_cls, - tc_h, - tc_h->c_characteristics, - tc_h->c_addr_prefix); - } - GNUNET_SERVICE_client_continue (client->client); - LOG (GNUNET_ERROR_TYPE_DEBUG, "finished communicator_available_cb()\n"); - -} - - -/** - * Incoming message. Test message is well-formed. - * - * @param cls the client - * @param msg the send message that was sent - * @return #GNUNET_OK if message is well-formed - */ -static int -check_communicator_backchannel (void *cls, - const struct - GNUNET_TRANSPORT_CommunicatorBackchannel *msg) -{ - // struct TransportClient *tc = cls; - - // if (CT_COMMUNICATOR != tc->type) - // { - // GNUNET_break (0); - // return GNUNET_SYSERR; - // } - // GNUNET_MQ_check_boxed_message (msg); - return GNUNET_OK; -} - - -/** - * @brief Receive an incoming message. - * - * Pass the message to the client. - * - * @param cls Closure - communicator handle - * @param bc_msg Message - */ -static void -handle_communicator_backchannel (void *cls, - const struct - GNUNET_TRANSPORT_CommunicatorBackchannel * - bc_msg) -{ - struct MyClient *client = cls; - struct GNUNET_TRANSPORT_TESTING_TransportCommunicatorHandle *tc_h = - client->tc; - struct GNUNET_TRANSPORT_TESTING_TransportCommunicatorHandle *other_tc_h; - struct GNUNET_MessageHeader *msg; - msg = (struct GNUNET_MessageHeader *) &bc_msg[1]; - uint16_t isize = ntohs (msg->size); - const char *target_communicator = ((const char *) msg) + isize; - struct GNUNET_TRANSPORT_CommunicatorBackchannelIncoming *cbi; - struct GNUNET_MQ_Envelope *env; - - LOG (GNUNET_ERROR_TYPE_DEBUG, - "Received backchannel message\n"); - if (tc_h->bc_enabled != GNUNET_YES) - { - GNUNET_SERVICE_client_continue (client->client); - return; - } - /* Find client providing this communicator */ - /* Finally, deliver backchannel message to communicator */ - LOG (GNUNET_ERROR_TYPE_DEBUG, - "Delivering backchannel message of type %u to %s\n", - ntohs (msg->type), - target_communicator); - other_tc_h = tc_h->bc_cb (tc_h, msg, (struct - GNUNET_PeerIdentity*) &bc_msg->pid); - env = GNUNET_MQ_msg_extra ( - cbi, - isize, - GNUNET_MESSAGE_TYPE_TRANSPORT_COMMUNICATOR_BACKCHANNEL_INCOMING); - cbi->pid = tc_h->peer_id; - memcpy (&cbi[1], msg, isize); - - - GNUNET_MQ_send (other_tc_h->c_mq, env); - GNUNET_SERVICE_client_continue (client->client); -} - - -/** - * Address of our peer added. Test message is well-formed. - * - * @param cls the client - * @param msg the send message that was sent - * @return #GNUNET_OK if message is well-formed - */ -static int -check_add_address (void *cls, - const struct GNUNET_TRANSPORT_AddAddressMessage *msg) -{ - // if (CT_COMMUNICATOR != tc->type) - // { - // GNUNET_break (0); - // return GNUNET_SYSERR; - // } - GNUNET_MQ_check_zero_termination (msg); - return GNUNET_OK; -} - - -/** - * @brief The communicator informs about an address. - * - * Store address and call client callback. - * - * @param cls Closure - communicator handle - * @param msg Message - */ -static void -handle_add_address (void *cls, - const struct GNUNET_TRANSPORT_AddAddressMessage *msg) -{ - struct MyClient *client = cls; - struct GNUNET_TRANSPORT_TESTING_TransportCommunicatorHandle *tc_h = - client->tc; - uint16_t size; - size = ntohs (msg->header.size) - sizeof(*msg); - LOG (GNUNET_ERROR_TYPE_DEBUG, "received add address cb %u\n", size); - if (0 == size) - return; /* receive-only communicator */ - LOG (GNUNET_ERROR_TYPE_DEBUG, "received add address cb %u\n", size); - tc_h->c_address = GNUNET_strdup ((const char *) &msg[1]); - if (NULL != tc_h->add_address_cb) - { - LOG (GNUNET_ERROR_TYPE_DEBUG, "calling add_address_cb()\n"); - tc_h->add_address_cb (tc_h->cb_cls, - tc_h, - tc_h->c_address, - GNUNET_TIME_relative_ntoh (msg->expiration), - msg->aid, - ntohl (msg->nt)); - } - GNUNET_SERVICE_client_continue (client->client); -} - - -/** - * Incoming message. Test message is well-formed. - * - * @param cls the client - * @param msg the send message that was sent - * @return #GNUNET_OK if message is well-formed - */ -static int -check_incoming_msg (void *cls, - const struct GNUNET_TRANSPORT_IncomingMessage *msg) -{ - // struct TransportClient *tc = cls; - - // if (CT_COMMUNICATOR != tc->type) - // { - // GNUNET_break (0); - // return GNUNET_SYSERR; - // } - GNUNET_MQ_check_boxed_message (msg); - return GNUNET_OK; -} - - -/** - * @brief Receive an incoming message. - * - * Pass the message to the client. - * - * @param cls Closure - communicator handle - * @param inc_msg Message - */ -static void -handle_incoming_msg (void *cls, - const struct GNUNET_TRANSPORT_IncomingMessage *inc_msg) -{ - struct MyClient *client = cls; - struct GNUNET_TRANSPORT_TESTING_TransportCommunicatorHandle *tc_h = - client->tc; - struct GNUNET_MessageHeader *msg; - msg = (struct GNUNET_MessageHeader *) &inc_msg[1]; - size_t payload_len = ntohs (msg->size) - sizeof (struct - GNUNET_MessageHeader); - if (NULL != tc_h->incoming_msg_cb) - { - tc_h->incoming_msg_cb (tc_h->cb_cls, - tc_h, - (char*) &msg[1], - payload_len); - } - else - { - LOG (GNUNET_ERROR_TYPE_WARNING, - "Incoming message from communicator but no handler!\n"); - } - if (GNUNET_YES == ntohl (inc_msg->fc_on)) - { - /* send ACK when done to communicator for flow control! */ - struct GNUNET_MQ_Envelope *env; - struct GNUNET_TRANSPORT_IncomingMessageAck *ack; - - env = GNUNET_MQ_msg (ack, GNUNET_MESSAGE_TYPE_TRANSPORT_INCOMING_MSG_ACK); - GNUNET_assert (NULL != env); - ack->reserved = htonl (0); - ack->fc_id = inc_msg->fc_id; - ack->sender = inc_msg->sender; - GNUNET_MQ_send (tc_h->c_mq, env); - } - - GNUNET_SERVICE_client_continue (client->client); -} - - -/** - * @brief Communicator informs that it tries to establish requested queue - * - * @param cls Closure - communicator handle - * @param msg Message - */ -static void -handle_queue_create_ok (void *cls, - const struct GNUNET_TRANSPORT_CreateQueueResponse *msg) -{ - struct MyClient *client = cls; - struct GNUNET_TRANSPORT_TESTING_TransportCommunicatorHandle *tc_h = - client->tc; - - if (NULL != tc_h->queue_create_reply_cb) - { - tc_h->queue_create_reply_cb (tc_h->cb_cls, tc_h, GNUNET_YES); - } - GNUNET_SERVICE_client_continue (client->client); -} - - -/** - * @brief Communicator informs that it won't try establishing requested queue. - * - * It will not do so probably because the address is bougus (see comment to - * #GNUNET_MESSAGE_TYPE_TRANSPORT_QUEUE_CREATE_FAIL) - * - * @param cls Closure - communicator handle - * @param msg Message - */ -static void -handle_queue_create_fail ( - void *cls, - const struct GNUNET_TRANSPORT_CreateQueueResponse *msg) -{ - struct MyClient *client = cls; - struct GNUNET_TRANSPORT_TESTING_TransportCommunicatorHandle *tc_h = - client->tc; - - if (NULL != tc_h->queue_create_reply_cb) - { - tc_h->queue_create_reply_cb (tc_h->cb_cls, tc_h, GNUNET_NO); - } - GNUNET_SERVICE_client_continue (client->client); -} - - -/** - * New queue became available. Check message. - * - * @param cls the client - * @param aqm the send message that was sent - */ -static int -check_add_queue_message (void *cls, - const struct GNUNET_TRANSPORT_AddQueueMessage *aqm) -{ - GNUNET_MQ_check_zero_termination (aqm); - return GNUNET_OK; -} - - -/** - * @brief Handle new queue - * - * Store context and call client callback. - * - * @param cls Closure - communicator handle - * @param msg Message struct - */ -static void -handle_add_queue_message (void *cls, - const struct GNUNET_TRANSPORT_AddQueueMessage *msg) -{ - struct MyClient *client = cls; - struct GNUNET_TRANSPORT_TESTING_TransportCommunicatorHandle *tc_h = - client->tc; - struct GNUNET_TRANSPORT_TESTING_TransportCommunicatorQueue *tc_queue; - - LOG (GNUNET_ERROR_TYPE_DEBUG, - "Got queue with ID %u\n", msg->qid); - for (tc_queue = tc_h->queue_head; NULL != tc_queue; tc_queue = tc_queue->next) - { - if (tc_queue->qid == msg->qid) - break; - } - if (NULL == tc_queue) - { - tc_queue = - GNUNET_new (struct GNUNET_TRANSPORT_TESTING_TransportCommunicatorQueue); - tc_queue->tc_h = tc_h; - tc_queue->qid = msg->qid; - tc_queue->peer_id = msg->receiver; - GNUNET_CONTAINER_DLL_insert (tc_h->queue_head, tc_h->queue_tail, tc_queue); - } - GNUNET_assert (tc_queue->qid == msg->qid); - GNUNET_assert (0 == GNUNET_memcmp (&tc_queue->peer_id, &msg->receiver)); - tc_queue->nt = msg->nt; - tc_queue->mtu = ntohl (msg->mtu); - tc_queue->cs = msg->cs; - tc_queue->priority = ntohl (msg->priority); - tc_queue->q_len = GNUNET_ntohll (msg->q_len); - if (NULL != tc_h->add_queue_cb) - { - tc_h->add_queue_cb (tc_h->cb_cls, tc_h, tc_queue, tc_queue->mtu); - } - GNUNET_SERVICE_client_continue (client->client); -} - - -/** - * @brief Handle new queue - * - * Store context and call client callback. - * - * @param cls Closure - communicator handle - * @param msg Message struct - */ -static void -handle_update_queue_message (void *cls, - const struct - GNUNET_TRANSPORT_UpdateQueueMessage *msg) -{ - struct MyClient *client = cls; - struct GNUNET_TRANSPORT_TESTING_TransportCommunicatorHandle *tc_h = - client->tc; - struct GNUNET_TRANSPORT_TESTING_TransportCommunicatorQueue *tc_queue; - - LOG (GNUNET_ERROR_TYPE_DEBUG, - "Received queue update message for %u with q_len %" PRIu64 "\n", - msg->qid, GNUNET_ntohll (msg->q_len)); - tc_queue = tc_h->queue_head; - if (NULL != tc_queue) - { - while (tc_queue->qid != msg->qid) - { - tc_queue = tc_queue->next; - } - } - if (NULL == tc_queue) - { - GNUNET_SERVICE_client_continue (client->client); - return; - } - GNUNET_assert (tc_queue->qid == msg->qid); - GNUNET_assert (0 == GNUNET_memcmp (&tc_queue->peer_id, &msg->receiver)); - tc_queue->nt = msg->nt; - tc_queue->mtu = ntohl (msg->mtu); - tc_queue->cs = msg->cs; - tc_queue->priority = ntohl (msg->priority); - // Uncomment this for alternativ 1 of backchannel functionality - tc_queue->q_len += GNUNET_ntohll (msg->q_len); - // Until here for alternativ 1 - // Uncomment this for alternativ 2 of backchannel functionality - // tc_queue->q_len = GNUNET_ntohll (msg->q_len); - // Until here for alternativ 2 - GNUNET_SERVICE_client_continue (client->client); -} - - -/** - * @brief Shut down the service - * - * @param cls Closure - Handle to the service - */ -static void -shutdown_service (void *cls) -{ - struct GNUNET_SERVICE_Handle *h = cls; - - LOG (GNUNET_ERROR_TYPE_DEBUG, - "Shutting down service!\n"); - - GNUNET_SERVICE_stop (h); -} - - -/** - * @brief Callback called when new Client (Communicator) connects - * - * @param cls Closure - TransporCommmunicator Handle - * @param client Client - * @param mq Messagequeue - * - * @return TransportCommunicator Handle - */ -static void * -connect_cb (void *cls, - struct GNUNET_SERVICE_Client *client, - struct GNUNET_MQ_Handle *mq) -{ - struct GNUNET_TRANSPORT_TESTING_TransportCommunicatorHandle *tc_h = cls; - struct MyClient *new_c; - - LOG (GNUNET_ERROR_TYPE_DEBUG, "Client %p connected to %p.\n", - client, tc_h); - new_c = GNUNET_new (struct MyClient); - new_c->client = client; - new_c->c_mq = mq; - new_c->tc = tc_h; - GNUNET_CONTAINER_DLL_insert (tc_h->client_head, - tc_h->client_tail, - new_c); - - if (NULL == tc_h->queue_head) - return new_c; - /* Iterate over queues. They are yet to be opened. Request opening. */ - for (struct - GNUNET_TRANSPORT_TESTING_TransportCommunicatorQueue *tc_queue_iter = - tc_h->queue_head; - NULL != tc_queue_iter; - tc_queue_iter = tc_queue_iter->next) - { - if (NULL == tc_queue_iter->open_queue_env) - continue; - /* Send the previously created mq envelope to request the creation of the - * queue. */ - GNUNET_MQ_send (tc_h->c_mq, - tc_queue_iter->open_queue_env); - tc_queue_iter->open_queue_env = NULL; - } - return new_c; -} - - -/** - * @brief Callback called when Client disconnects - * - * @param cls Closure - TransportCommunicator Handle - * @param client Client - * @param internal_cls TransporCommmunicator Handle - */ -static void -disconnect_cb (void *cls, - struct GNUNET_SERVICE_Client *client, - void *internal_cls) -{ - struct MyClient *cl = cls; - struct GNUNET_TRANSPORT_TESTING_TransportCommunicatorHandle *tc_h = cls; - - for (cl = tc_h->client_head; NULL != cl; cl = cl->next) - { - if (cl->client != client) - continue; - GNUNET_CONTAINER_DLL_remove (tc_h->client_head, - tc_h->client_tail, - cl); - if (cl->c_mq == tc_h->c_mq) - tc_h->c_mq = NULL; - GNUNET_free (cl); - break; - } - LOG (GNUNET_ERROR_TYPE_DEBUG, "Client disconnected.\n"); -} - - -/** - * Message was transmitted. Process the request. - * - * @param cls the client - * @param sma the send message that was sent - */ -static void -handle_send_message_ack (void *cls, - const struct GNUNET_TRANSPORT_SendMessageToAck *sma) -{ - struct MyClient *client = cls; - struct GNUNET_TRANSPORT_TESTING_TransportCommunicatorHandle *tc_h = - client->tc; - static int mtr = 0; - mtr++; - if (tc_h->cont != NULL) - tc_h->cont (tc_h->cont_cls); - GNUNET_SERVICE_client_continue (client->client); -} - - -/** - * @brief Start the communicator part of the transport service - * - * @param communicator_available Callback to be called when a new communicator - * becomes available - * @param cfg Configuration - */ -static void -transport_communicator_start ( - struct GNUNET_TRANSPORT_TESTING_TransportCommunicatorHandle *tc_h) -{ - struct GNUNET_MQ_MessageHandler mh[] = { - GNUNET_MQ_hd_var_size (communicator_available, - GNUNET_MESSAGE_TYPE_TRANSPORT_NEW_COMMUNICATOR, - struct GNUNET_TRANSPORT_CommunicatorAvailableMessage, - tc_h), - GNUNET_MQ_hd_var_size (communicator_backchannel, - GNUNET_MESSAGE_TYPE_TRANSPORT_COMMUNICATOR_BACKCHANNEL, - struct GNUNET_TRANSPORT_CommunicatorBackchannel, - tc_h), - GNUNET_MQ_hd_var_size (add_address, - GNUNET_MESSAGE_TYPE_TRANSPORT_ADD_ADDRESS, - struct GNUNET_TRANSPORT_AddAddressMessage, - tc_h), - // GNUNET_MQ_hd_fixed_size (del_address, - // GNUNET_MESSAGE_TYPE_TRANSPORT_DEL_ADDRESS, - // struct GNUNET_TRANSPORT_DelAddressMessage, - // NULL), - GNUNET_MQ_hd_var_size (incoming_msg, - GNUNET_MESSAGE_TYPE_TRANSPORT_INCOMING_MSG, - struct GNUNET_TRANSPORT_IncomingMessage, - tc_h), - GNUNET_MQ_hd_fixed_size (queue_create_ok, - GNUNET_MESSAGE_TYPE_TRANSPORT_QUEUE_CREATE_OK, - struct GNUNET_TRANSPORT_CreateQueueResponse, - tc_h), - GNUNET_MQ_hd_fixed_size (queue_create_fail, - GNUNET_MESSAGE_TYPE_TRANSPORT_QUEUE_CREATE_FAIL, - struct GNUNET_TRANSPORT_CreateQueueResponse, - tc_h), - GNUNET_MQ_hd_var_size (add_queue_message, - GNUNET_MESSAGE_TYPE_TRANSPORT_QUEUE_SETUP, - struct GNUNET_TRANSPORT_AddQueueMessage, - tc_h), - GNUNET_MQ_hd_fixed_size (update_queue_message, - GNUNET_MESSAGE_TYPE_TRANSPORT_QUEUE_UPDATE, - struct GNUNET_TRANSPORT_UpdateQueueMessage, - tc_h), - // GNUNET_MQ_hd_fixed_size (del_queue_message, - // GNUNET_MESSAGE_TYPE_TRANSPORT_QUEUE_TEARDOWN, - // struct GNUNET_TRANSPORT_DelQueueMessage, - // NULL), - GNUNET_MQ_hd_fixed_size (send_message_ack, - GNUNET_MESSAGE_TYPE_TRANSPORT_SEND_MSG_ACK, - struct GNUNET_TRANSPORT_SendMessageToAck, - tc_h), - GNUNET_MQ_handler_end () - }; - - - tc_h->sh = GNUNET_SERVICE_start ("transport", - tc_h->cfg, - &connect_cb, - &disconnect_cb, - tc_h, - mh); - GNUNET_assert (NULL != tc_h->sh); -} - - -/** - * @brief Task run at shutdown to kill communicator and clean up - * - * @param cls Closure - Process of communicator - */ -static void -shutdown_process (struct GNUNET_OS_Process *proc) -{ - if (0 != GNUNET_OS_process_kill (proc, SIGTERM)) - { - LOG (GNUNET_ERROR_TYPE_WARNING, - "Error shutting down process with SIGERM, trying SIGKILL\n"); - if (0 != GNUNET_OS_process_kill (proc, SIGKILL)) - { - LOG (GNUNET_ERROR_TYPE_ERROR, - "Error shutting down process with SIGERM and SIGKILL\n"); - } - } - GNUNET_OS_process_destroy (proc); -} - - -/** - * @brief Task run at shutdown to kill the statistics process - * - * @param cls Closure - Process of communicator - */ -static void -shutdown_statistics (void *cls) -{ - struct GNUNET_OS_Process *proc = cls; - shutdown_process (proc); -} - - -/** - * @brief Task run at shutdown to kill the peerstore process - * - * @param cls Closure - Process of communicator - */ -static void -shutdown_peerstore (void *cls) -{ - struct GNUNET_OS_Process *proc = cls; - shutdown_process (proc); -} - - -/** - * @brief Task run at shutdown to kill a communicator process - * - * @param cls Closure - Process of communicator - */ -static void -shutdown_communicator (void *cls) -{ - struct GNUNET_OS_Process *proc = cls; - shutdown_process (proc); -} - - -/** - * @brief Start the communicator - * - * @param cfgname Name of the communicator - */ -static void -communicator_start ( - struct GNUNET_TRANSPORT_TESTING_TransportCommunicatorHandle *tc_h, - const char *binary_name) -{ - char *binary; - char *loprefix; - char *section_name; - - LOG (GNUNET_ERROR_TYPE_DEBUG, "communicator_start\n"); - - section_name = strchr (binary_name, '-'); - section_name++; - - if (GNUNET_OK != GNUNET_CONFIGURATION_get_value_string (tc_h->cfg, - section_name, - "PREFIX", - &loprefix)) - loprefix = GNUNET_strdup (""); - - - binary = GNUNET_OS_get_libexec_binary_path (binary_name); - tc_h->c_proc = GNUNET_OS_start_process_s (GNUNET_OS_INHERIT_STD_OUT_AND_ERR, - NULL, - loprefix, - binary, - binary_name, - "-c", - tc_h->cfg_filename, - NULL); - if (NULL == tc_h->c_proc) - { - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Failed to start communicator!"); - return; - } - LOG (GNUNET_ERROR_TYPE_INFO, "started communicator\n"); - GNUNET_free (binary); -} - - -/** - * @brief Task run at shutdown to kill communicator and clean up - * - * @param cls Closure - Process of communicator - */ -static void -shutdown_nat (void *cls) -{ - struct GNUNET_OS_Process *proc = cls; - shutdown_process (proc); -} - - -/** - * @brief Task run at shutdown to kill the resolver process - * - * @param cls Closure - Process of communicator - */ -static void -shutdown_resolver (void *cls) -{ - struct GNUNET_OS_Process *proc = cls; - shutdown_process (proc); -} - - -/** - * @brief Start Resolver - * - */ -static void -resolver_start (struct - GNUNET_TRANSPORT_TESTING_TransportCommunicatorHandle *tc_h) -{ - char *binary; - - LOG (GNUNET_ERROR_TYPE_DEBUG, "resolver_start\n"); - binary = GNUNET_OS_get_libexec_binary_path ("gnunet-service-resolver"); - tc_h->resolver_proc = GNUNET_OS_start_process ( - GNUNET_OS_INHERIT_STD_OUT_AND_ERR - | GNUNET_OS_USE_PIPE_CONTROL, - NULL, - NULL, - NULL, - binary, - "gnunet-service-resolver", - "-c", - tc_h->cfg_filename, - NULL); - if (NULL == tc_h->resolver_proc) - { - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Failed to start resolver service!"); - return; - } - LOG (GNUNET_ERROR_TYPE_INFO, "started resolver service\n"); - GNUNET_free (binary); - -} - - -/** - * @brief Start Statistics - * - */ -static void -statistics_start ( - struct GNUNET_TRANSPORT_TESTING_TransportCommunicatorHandle *tc_h) -{ - char *binary; - - binary = GNUNET_OS_get_libexec_binary_path ("gnunet-service-statistics"); - tc_h->stat_proc = GNUNET_OS_start_process (GNUNET_OS_INHERIT_STD_OUT_AND_ERR, - NULL, - NULL, - NULL, - binary, - "gnunet-service-statistics", - "-c", - tc_h->cfg_filename, - NULL); - if (NULL == tc_h->stat_proc) - { - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Failed to start Statistics!"); - return; - } - LOG (GNUNET_ERROR_TYPE_INFO, "started Statistics\n"); - GNUNET_free (binary); -} - - -/** - * @brief Start Peerstore - * - */ -static void -peerstore_start ( - struct GNUNET_TRANSPORT_TESTING_TransportCommunicatorHandle *tc_h) -{ - char *binary; - - binary = GNUNET_OS_get_libexec_binary_path ("gnunet-service-peerstore"); - tc_h->ps_proc = GNUNET_OS_start_process (GNUNET_OS_INHERIT_STD_OUT_AND_ERR, - NULL, - NULL, - NULL, - binary, - "gnunet-service-peerstore", - "-c", - tc_h->cfg_filename, - NULL); - if (NULL == tc_h->ps_proc) - { - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Failed to start Peerstore!"); - return; - } - LOG (GNUNET_ERROR_TYPE_INFO, "started Peerstore\n"); - GNUNET_free (binary); -} - - -/** - * @brief Start NAT - * - */ -static void -nat_start ( - struct GNUNET_TRANSPORT_TESTING_TransportCommunicatorHandle *tc_h) -{ - char *binary; - - LOG (GNUNET_ERROR_TYPE_DEBUG, "nat_start\n"); - binary = GNUNET_OS_get_libexec_binary_path ("gnunet-service-nat"); - tc_h->nat_proc = GNUNET_OS_start_process (GNUNET_OS_INHERIT_STD_OUT_AND_ERR - | GNUNET_OS_USE_PIPE_CONTROL, - NULL, - NULL, - NULL, - binary, - "gnunet-service-nat", - "-c", - tc_h->cfg_filename, - NULL); - if (NULL == tc_h->nat_proc) - { - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Failed to start NAT!"); - return; - } - LOG (GNUNET_ERROR_TYPE_INFO, "started NAT\n"); - GNUNET_free (binary); -} - - -/** - * @brief Start communicator part of transport service and communicator - * - * @param service_name Name of the service - * @param cfg Configuration handle - * @param communicator_available_cb Callback that is called when a new - * @param add_address_cb Callback that is called when a new - * communicator becomes available - * @param cb_cls Closure to @a communicator_available_cb and @a - * - * @return Handle to the communicator duo - */ -struct GNUNET_TRANSPORT_TESTING_TransportCommunicatorHandle * -GNUNET_TRANSPORT_TESTING_transport_communicator_service_start ( - const char *service_name, - const char *binary_name, - const char *cfg_filename, - const struct GNUNET_PeerIdentity *peer_id, - GNUNET_TRANSPORT_TESTING_CommunicatorAvailableCallback - communicator_available_cb, - GNUNET_TRANSPORT_TESTING_AddAddressCallback add_address_cb, - GNUNET_TRANSPORT_TESTING_QueueCreateReplyCallback queue_create_reply_cb, - GNUNET_TRANSPORT_TESTING_AddQueueCallback add_queue_cb, - GNUNET_TRANSPORT_TESTING_IncomingMessageCallback incoming_message_cb, - GNUNET_TRANSPORT_TESTING_BackchannelCallback bc_cb, - void *cb_cls) -{ - struct GNUNET_TRANSPORT_TESTING_TransportCommunicatorHandle *tc_h; - - LOG (GNUNET_ERROR_TYPE_DEBUG, - "Starting new transport/communicator combo with config %s\n", - cfg_filename); - tc_h = - GNUNET_new (struct GNUNET_TRANSPORT_TESTING_TransportCommunicatorHandle); - tc_h->cfg_filename = GNUNET_strdup (cfg_filename); - tc_h->cfg = GNUNET_CONFIGURATION_create (); - if ((GNUNET_SYSERR == GNUNET_CONFIGURATION_load (tc_h->cfg, cfg_filename))) - { - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - _ ("Malformed configuration file `%s', exit ...\n"), - cfg_filename); - GNUNET_free (tc_h->cfg_filename); - GNUNET_CONFIGURATION_destroy (tc_h->cfg); - GNUNET_free (tc_h); - return NULL; - } - tc_h->bc_enabled = GNUNET_CONFIGURATION_get_value_yesno (tc_h->cfg, - "communicator-test", - "BACKCHANNEL_ENABLED"); - tc_h->communicator_available_cb = communicator_available_cb; - tc_h->add_address_cb = add_address_cb; - tc_h->queue_create_reply_cb = queue_create_reply_cb; - tc_h->add_queue_cb = add_queue_cb; - tc_h->incoming_msg_cb = incoming_message_cb; - tc_h->bc_cb = bc_cb; - tc_h->peer_id = *peer_id; - tc_h->cb_cls = cb_cls; - - /* Start communicator part of service */ - transport_communicator_start (tc_h); - /* Start NAT */ - nat_start (tc_h); - /* Start resolver service */ - resolver_start (tc_h); - /* Start peerstore service */ - peerstore_start (tc_h); - /* Start statistic service */ - statistics_start (tc_h); - /* Schedule start communicator */ - communicator_start (tc_h, - binary_name); - return tc_h; -} - - -void -GNUNET_TRANSPORT_TESTING_transport_communicator_service_stop ( - struct GNUNET_TRANSPORT_TESTING_TransportCommunicatorHandle *tc_h) -{ - shutdown_communicator (tc_h->c_proc); - shutdown_service (tc_h->sh); - shutdown_nat (tc_h->nat_proc); - shutdown_resolver (tc_h->resolver_proc); - shutdown_peerstore (tc_h->ps_proc); - shutdown_statistics (tc_h->stat_proc); - GNUNET_CONFIGURATION_destroy (tc_h->cfg); - GNUNET_free (tc_h); -} - - -/** - * @brief Instruct communicator to open a queue - * - * @param tc_h Handle to communicator which shall open queue - * @param peer_id Towards which peer - * @param address For which address - */ -void -GNUNET_TRANSPORT_TESTING_transport_communicator_open_queue ( - struct GNUNET_TRANSPORT_TESTING_TransportCommunicatorHandle *tc_h, - const struct GNUNET_PeerIdentity *peer_id, - const char *address) -{ - struct GNUNET_TRANSPORT_TESTING_TransportCommunicatorQueue *tc_queue; - static uint32_t idgen; - char *prefix; - struct GNUNET_TRANSPORT_CreateQueue *msg; - struct GNUNET_MQ_Envelope *env; - size_t alen; - - tc_queue = - GNUNET_new (struct GNUNET_TRANSPORT_TESTING_TransportCommunicatorQueue); - tc_queue->tc_h = tc_h; - prefix = GNUNET_HELLO_address_to_prefix (address); - if (NULL == prefix) - { - GNUNET_break (0); /* We got an invalid address!? */ - GNUNET_free (tc_queue); - return; - } - GNUNET_free (prefix); - alen = strlen (address) + 1; - env = - GNUNET_MQ_msg_extra (msg, alen, GNUNET_MESSAGE_TYPE_TRANSPORT_QUEUE_CREATE); - msg->request_id = htonl (idgen++); - tc_queue->qid = msg->request_id; - msg->receiver = *peer_id; - tc_queue->peer_id = *peer_id; - memcpy (&msg[1], address, alen); - if (NULL != tc_h->c_mq) - { - LOG (GNUNET_ERROR_TYPE_DEBUG, - "Sending queue create immediately\n"); - GNUNET_MQ_send (tc_h->c_mq, env); - } - else - { - tc_queue->open_queue_env = env; - } - GNUNET_CONTAINER_DLL_insert (tc_h->queue_head, tc_h->queue_tail, tc_queue); -} - - -void -GNUNET_TRANSPORT_TESTING_transport_communicator_send - (struct GNUNET_TRANSPORT_TESTING_TransportCommunicatorHandle *tc_h, - GNUNET_SCHEDULER_TaskCallback cont, - void *cont_cls, - const void *payload, - size_t payload_size) -{ - struct GNUNET_MessageHeader *mh; - struct GNUNET_TRANSPORT_SendMessageTo *msg; - struct GNUNET_MQ_Envelope *env; - size_t inbox_size; - struct GNUNET_TRANSPORT_TESTING_TransportCommunicatorQueue *tc_queue; - struct GNUNET_TRANSPORT_TESTING_TransportCommunicatorQueue *tc_queue_tmp; - static struct GNUNET_TRANSPORT_TESTING_TransportCommunicatorQueue *last_queue; - tc_queue = NULL; - - for (tc_queue_tmp = tc_h->queue_head; - NULL != tc_queue_tmp; - tc_queue_tmp = tc_queue_tmp->next) - { - if (tc_queue_tmp->q_len <= 0) - continue; - if (NULL == tc_queue) - { - LOG (GNUNET_ERROR_TYPE_DEBUG, - "Selecting queue with prio %u, len %" PRIu64 " and MTU %u\n", - tc_queue_tmp->priority, - tc_queue_tmp->q_len, - tc_queue_tmp->mtu); - tc_queue = tc_queue_tmp; - continue; - } - if (tc_queue->priority < tc_queue_tmp->priority) - { - LOG (GNUNET_ERROR_TYPE_DEBUG, - "Selecting queue with prio %u, len %" PRIu64 " and MTU %u\n", - tc_queue_tmp->priority, - tc_queue_tmp->q_len, - tc_queue_tmp->mtu); - tc_queue = tc_queue_tmp; - } - } - if (last_queue != tc_queue) - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "Selected sending queue changed to %u with length %lu and MTU %u\n", - ntohl (tc_queue->qid), (unsigned long) tc_queue->q_len, tc_queue->mtu); - GNUNET_assert (NULL != tc_queue); - last_queue = tc_queue; - // Uncomment this for alternativ 1 of backchannel functionality - if (tc_queue->q_len != GNUNET_TRANSPORT_QUEUE_LENGTH_UNLIMITED) - tc_queue->q_len--; - // Until here for alternativ 1 - static int msg_count = 0; - msg_count++; - if (msg_count % 100 == 0) - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "Sending %u-th (%lu-th for queue) message on queue %u\n", - msg_count, (unsigned long) tc_queue->mid, ntohl (tc_queue->qid)); - inbox_size = sizeof (struct GNUNET_MessageHeader) + payload_size; - env = GNUNET_MQ_msg_extra (msg, - inbox_size, - GNUNET_MESSAGE_TYPE_TRANSPORT_SEND_MSG); - GNUNET_assert (NULL != env); - msg->qid = tc_queue->qid; - msg->mid = tc_queue->mid++; - msg->receiver = tc_queue->peer_id; - mh = (struct GNUNET_MessageHeader *) &msg[1]; - mh->size = htons (inbox_size); - mh->type = GNUNET_MESSAGE_TYPE_DUMMY; - memcpy (&mh[1], - payload, - payload_size); - if (NULL != cont) - GNUNET_MQ_notify_sent (env, - cont, - cont_cls); - GNUNET_MQ_send (tc_queue->tc_h->c_mq, - env); -} diff --git a/src/transport/transport-testing-communicator.h b/src/transport/transport-testing-communicator.h deleted file mode 100644 index 7460aab8e..000000000 --- a/src/transport/transport-testing-communicator.h +++ /dev/null @@ -1,370 +0,0 @@ -/* - This file is part of GNUnet. - Copyright (C) 2019 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 . - - SPDX-License-Identifier: AGPL3.0-or-later - */ - -/** - * @file transport/transport-testing-communicator.h - * @brief functions and structures related to testing-tng - * @author Christian Grothoff - * @author Julius Bünger - */ -#include "platform.h" -#include "gnunet_util_lib.h" -#include "gnunet_transport_communication_service.h" -#include "transport.h" - -/** - * @brief Queue of a communicator and some context - */ -struct GNUNET_TRANSPORT_TESTING_TransportCommunicatorQueue; - - -/** - * @brief Handle/Context to a single transmission - */ -struct GNUNET_TRANSPORT_TESTING_TransportCommunicatorTransmission; - -/** - * @brief Function signature for callbacks that are called when new - * backchannel message arrived - * - * @param cls Closure - * @param msg Backchannel message - * @param pid Target peer - */ -typedef struct GNUNET_TRANSPORT_TESTING_TransportCommunicatorHandle * -(*GNUNET_TRANSPORT_TESTING_BackchannelCallback)(void *cls, - struct GNUNET_MessageHeader *msg, - struct GNUNET_PeerIdentity *pid); - - -/** - * @brief Function signature for callbacks that are called when new - * communicators become available - * - * @param cls Closure - * @param tc_h Communicator handle - * @param cc Characteristics of communicator - * @param address_prefix Prefix of the address - */ -typedef void -(*GNUNET_TRANSPORT_TESTING_CommunicatorAvailableCallback)(void *cls, - struct - GNUNET_TRANSPORT_TESTING_TransportCommunicatorHandle - *tc_h, - enum - GNUNET_TRANSPORT_CommunicatorCharacteristics - cc, - char *address_prefix); - - -/** - * @brief Receive information about the address of a communicator. - * - * @param cls Closure - * @param tc_h Communicator handle - * @param address Address represented as string - * @param expiration Expiration - * @param aid Aid - * @param nt Network Type - */ -typedef void -(*GNUNET_TRANSPORT_TESTING_AddAddressCallback)(void *cls, - struct - GNUNET_TRANSPORT_TESTING_TransportCommunicatorHandle - *tc_h, - const char *address, - struct GNUNET_TIME_Relative - expiration, - uint32_t aid, - enum GNUNET_NetworkType nt); - - -/** - * @brief Get informed about the success of a queue request. - * - * @param cls Closure - * @param tc_h Communicator handle - * @param will_try #GNUNET_YES if communicator will try to create queue - */ -typedef void -(*GNUNET_TRANSPORT_TESTING_QueueCreateReplyCallback)(void *cls, - struct - GNUNET_TRANSPORT_TESTING_TransportCommunicatorHandle - *tc_h, - int will_try); - - -/** - * @brief Handle opening of queue - * - * @param cls Closure - * @param tc_h Communicator handle - * @param tc_queue Handle to newly opened queue - */ -typedef void -(*GNUNET_TRANSPORT_TESTING_AddQueueCallback)(void *cls, - struct - GNUNET_TRANSPORT_TESTING_TransportCommunicatorHandle - *tc_h, - struct - GNUNET_TRANSPORT_TESTING_TransportCommunicatorQueue - *tc_queue, - size_t mtu); - - -/** - * @brief Handle an incoming message - * - * @param cls Closure - * @param tc_h Handle to the receiving communicator - * @param msg Received message - */ -typedef void -(*GNUNET_TRANSPORT_TESTING_IncomingMessageCallback)(void *cls, - struct - GNUNET_TRANSPORT_TESTING_TransportCommunicatorHandle - *tc_h, - const char*payload, - size_t payload_len); - -/** - * @brief Handle to a transport communicator - */ -struct GNUNET_TRANSPORT_TESTING_TransportCommunicatorHandle -{ - /** - * Clients - */ - struct MyClient *client_head; - struct MyClient *client_tail; - - /** - * @brief Handle to the client - */ - struct GNUNET_MQ_Handle *c_mq; - - /** - * @brief Handle to the configuration - */ - struct GNUNET_CONFIGURATION_Handle *cfg; - - /** - * @brief File name of configuration file - */ - char *cfg_filename; - - struct GNUNET_PeerIdentity peer_id; - - /** - * @brief Handle to the transport service - */ - struct GNUNET_SERVICE_Handle *tsh; - - /** - * @brief Task that will be run on shutdown to stop and clean transport - * service - */ - struct GNUNET_SCHEDULER_Task *ts_shutdown_task; - - - /** - * @brief Process of the communicator - */ - struct GNUNET_OS_Process *c_proc; - - /** - * NAT process - */ - struct GNUNET_OS_Process *nat_proc; - - /** - * resolver service process - */ - struct GNUNET_OS_Process *resolver_proc; - - /** - * statistics service process - */ - struct GNUNET_OS_Process *stat_proc; - - /** - * peerstore service process - */ - struct GNUNET_OS_Process *ps_proc; - - /** - * @brief Task that will be run on shutdown to stop and clean communicator - */ - struct GNUNET_SCHEDULER_Task *c_shutdown_task; - - /** - * @brief Characteristics of the communicator - */ - enum GNUNET_TRANSPORT_CommunicatorCharacteristics c_characteristics; - - /** - * @brief Specifies supported addresses - */ - char *c_addr_prefix; - - /** - * @brief Specifies supported addresses - */ - char *c_address; - - /** - * @brief Head of the DLL of queues associated with this communicator - */ - struct GNUNET_TRANSPORT_TESTING_TransportCommunicatorQueue *queue_head; - - /** - * @brief Tail of the DLL of queues associated with this communicator - */ - struct GNUNET_TRANSPORT_TESTING_TransportCommunicatorQueue *queue_tail; - - /* Callbacks + Closures */ - /** - * @brief Callback called when a new communicator connects - */ - GNUNET_TRANSPORT_TESTING_CommunicatorAvailableCallback - communicator_available_cb; - - /** - * @brief Callback called when a new communicator connects - */ - GNUNET_TRANSPORT_TESTING_AddAddressCallback add_address_cb; - - /** - * @brief Callback called when a new communicator connects - */ - GNUNET_TRANSPORT_TESTING_QueueCreateReplyCallback queue_create_reply_cb; - - /** - * @brief Callback called when a new communicator connects - */ - GNUNET_TRANSPORT_TESTING_AddQueueCallback add_queue_cb; - - /** - * @brief Callback called when a new communicator connects - */ - GNUNET_TRANSPORT_TESTING_IncomingMessageCallback incoming_msg_cb; - - /** - * @brief Backchannel callback - */ - GNUNET_TRANSPORT_TESTING_BackchannelCallback bc_cb; - - /** - * Our service handle - */ - struct GNUNET_SERVICE_Handle *sh; - - /** - * @brief Closure to the callback - */ - void *cb_cls; - - /** - * Callback to call when message ack received. - */ - GNUNET_SCHEDULER_TaskCallback cont; - - /** - * Closure for cont - */ - void *cont_cls; - - /** - * Backchannel supported - */ - int bc_enabled; -}; - -/** - * @brief Start communicator part of transport service and communicator - * - * @param service_name Name of the service - * @param cfg Configuration handle - * @param communicator_available Callback that is called when a new - * communicator becomes available - * @param add_address_cb Callback handling new addresses - * @param queue_create_reply_cb Callback handling success of queue requests - * @param add_queue_cb Callback handling freshly created queues - * @param incoming_message_cb Callback handling incoming messages - * @param cb_cls Closure to @p communicator_available - * - * @return Handle to the communicator duo - */ -struct GNUNET_TRANSPORT_TESTING_TransportCommunicatorHandle * -GNUNET_TRANSPORT_TESTING_transport_communicator_service_start ( - const char *service_name, - const char *binary_name, - const char *cfg_filename, - const struct GNUNET_PeerIdentity *peer_id, - GNUNET_TRANSPORT_TESTING_CommunicatorAvailableCallback - communicator_available_cb, - GNUNET_TRANSPORT_TESTING_AddAddressCallback add_address_cb, - GNUNET_TRANSPORT_TESTING_QueueCreateReplyCallback queue_create_reply_cb, - GNUNET_TRANSPORT_TESTING_AddQueueCallback add_queue_cb, - GNUNET_TRANSPORT_TESTING_IncomingMessageCallback incoming_message_cb, - GNUNET_TRANSPORT_TESTING_BackchannelCallback bc_cb, - void *cb_cls); - - -void -GNUNET_TRANSPORT_TESTING_transport_communicator_service_stop ( - struct GNUNET_TRANSPORT_TESTING_TransportCommunicatorHandle *tc_h); - - -/** - * @brief Instruct communicator to open a queue - * - * @param tc_h Handle to communicator which shall open queue - * @param peer_id Towards which peer - * @param address For which address - */ -void -GNUNET_TRANSPORT_TESTING_transport_communicator_open_queue (struct - GNUNET_TRANSPORT_TESTING_TransportCommunicatorHandle - *tc_h, - const struct - GNUNET_PeerIdentity - *peer_id, - const char *address); - - -/** - * @brief Instruct communicator to send data - * - * @param tc_queue The queue to use for sending - * @param cont function to call when done sending - * @param cont_cls closure for @a cont - * @param payload Data to send - * @param payload_size Size of the @a payload - */ -void -GNUNET_TRANSPORT_TESTING_transport_communicator_send (struct - GNUNET_TRANSPORT_TESTING_TransportCommunicatorHandle - *tc_h, - GNUNET_SCHEDULER_TaskCallback - cont, - void *cont_cls, - const void *payload, - size_t payload_size); diff --git a/src/transport/transport-testing-filenames2.c b/src/transport/transport-testing-filenames2.c deleted file mode 100644 index 59fa1ebd5..000000000 --- a/src/transport/transport-testing-filenames2.c +++ /dev/null @@ -1,203 +0,0 @@ -/* - This file is part of GNUnet. - Copyright (C) 2006, 2009, 2015, 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 . - - SPDX-License-Identifier: AGPL3.0-or-later - */ -/** - * @file transport-testing-filenames.c - * @brief convenience string manipulation functions for tests - * @author Matthias Wachs - * @author Christian Grothoff - */ -#include "platform.h" -#include "transport-testing2.h" - - -/** - * Removes all directory separators from absolute filename - * - * @param file the absolute file name, e.g. as found in argv[0] - * @return extracted file name, has to be freed by caller - */ -static char * -extract_filename (const char *file) -{ - char *pch = GNUNET_strdup (file); - char *backup = pch; - char *filename = NULL; - char *res; - - if (NULL != strstr (pch, "/")) - { - pch = strtok (pch, "/"); - while (pch != NULL) - { - pch = strtok (NULL, "/"); - if (pch != NULL) - { - filename = pch; - } - } - } - else - filename = pch; - - res = GNUNET_strdup (filename); - GNUNET_free (backup); - return res; -} - - -/** - * Extracts the test filename from an absolute file name and removes - * the extension - * - * @param file absolute file name - * @return the result - */ -char * -GNUNET_TRANSPORT_TESTING_get_test_name (const char *file) -{ - char *backup = extract_filename (file); - char *filename = backup; - char *dotexe; - char *ret; - - if (NULL == filename) - return NULL; - - /* remove "lt-" */ - filename = strstr (filename, "test"); - if (NULL == filename) - { - GNUNET_free (backup); - return NULL; - } - - /* remove ".exe" */ - if (NULL != (dotexe = strstr (filename, ".exe"))) - dotexe[0] = '\0'; - ret = GNUNET_strdup (filename); - GNUNET_free (backup); - return ret; -} - - -/** - * Extracts the filename from an absolute file name and removes the extension - * - * @param file absolute file name - * @return the result - */ -char * -GNUNET_TRANSPORT_TESTING_get_test_source_name (const char *file) -{ - char *src = extract_filename (file); - char *split; - - split = strstr (src, "."); - if (NULL != split) - split[0] = '\0'; - return src; -} - - -/** - * Extracts the plugin name from an absolute file name and the test name - * - * @param file absolute file name - * @param test test name - * @return the result - */ -char * -GNUNET_TRANSPORT_TESTING_get_test_plugin_name (const char *file, - const char *test) -{ - char *filename; - char *dotexe; - char *e = extract_filename (file); - char *t = extract_filename (test); - char *ret; - - if (NULL == e) - goto fail; - /* remove "lt-" */ - filename = strstr (e, "tes"); - if (NULL == filename) - goto fail; - /* remove ".exe" */ - if (NULL != (dotexe = strstr (filename, ".exe"))) - dotexe[0] = '\0'; - - /* find last _ */ - filename = strstr (filename, t); - if (NULL == filename) - goto fail; - /* copy plugin */ - filename += strlen (t); - if ('\0' != *filename) - filename++; - ret = GNUNET_strdup (filename); - goto suc; -fail: - ret = NULL; -suc: - GNUNET_free (t); - GNUNET_free (e); - return ret; -} - - -/** - * This function takes the filename (e.g. argv[0), removes a "lt-"-prefix and - * if existing ".exe"-prefix and adds the peer-number - * - * @param file filename of the test, e.g. argv[0] - * @param count peer number - * @return the result - */ -char * -GNUNET_TRANSPORT_TESTING_get_config_name (const char *file, - int count) -{ - char *filename = extract_filename (file); - char *backup = filename; - char *dotexe; - char *ret; - - if (NULL == filename) - return NULL; - /* remove "lt-" */ - filename = strstr (filename, "test"); - if (NULL == filename) - goto fail; - /* remove ".exe" */ - if (NULL != (dotexe = strstr (filename, ".exe"))) - dotexe[0] = '\0'; - GNUNET_asprintf (&ret, - "%s_peer%u.conf", - filename, - count); - GNUNET_free (backup); - return ret; -fail: - GNUNET_free (backup); - return NULL; -} - - -/* end of transport-testing-filenames.c */ diff --git a/src/transport/transport-testing-loggers2.c b/src/transport/transport-testing-loggers2.c deleted file mode 100644 index e6c79b78a..000000000 --- a/src/transport/transport-testing-loggers2.c +++ /dev/null @@ -1,81 +0,0 @@ -/* - This file is part of GNUnet. - Copyright (C) 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 . - - SPDX-License-Identifier: AGPL3.0-or-later - */ -/** - * @file transport-testing-loggers.c - * @brief convenience functions for logging common events in tests - * @author Christian Grothoff - */ -#include "platform.h" -#include "transport-testing2.h" - - -/** - * Log a connect event. - * - * @param cls NULL - * @param me peer that had the event - * @param other peer that connected. - */ -void -GNUNET_TRANSPORT_TESTING_log_connect (void *cls, - struct - GNUNET_TRANSPORT_TESTING_PeerContext *me, - const struct GNUNET_PeerIdentity *other) -{ - char *ps; - - ps = GNUNET_strdup (GNUNET_i2s (&me->id)); - GNUNET_log (GNUNET_ERROR_TYPE_INFO, - "Peer %s connected to %u (%s)!\n", - GNUNET_i2s (other), - me->no, - ps); - GNUNET_free (ps); -} - - -/** - * Log a disconnect event. - * - * @param cls NULL - * @param me peer that had the event - * @param other peer that disconnected. - */ -void -GNUNET_TRANSPORT_TESTING_log_disconnect (void *cls, - struct - GNUNET_TRANSPORT_TESTING_PeerContext * - me, - const struct - GNUNET_PeerIdentity *other) -{ - char *ps; - - ps = GNUNET_strdup (GNUNET_i2s (&me->id)); - GNUNET_log (GNUNET_ERROR_TYPE_INFO, - "Peer `%s' disconnected from %u (%s)!\n", - GNUNET_i2s (other), - me->no, - ps); - GNUNET_free (ps); -} - - -/* end of transport-testing-loggers.c */ diff --git a/src/transport/transport-testing-main2.c b/src/transport/transport-testing-main2.c deleted file mode 100644 index 0a1710922..000000000 --- a/src/transport/transport-testing-main2.c +++ /dev/null @@ -1,614 +0,0 @@ -/* - This file is part of GNUnet. - Copyright (C) 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 . - - SPDX-License-Identifier: AGPL3.0-or-later - */ -/** - * @file transport-testing-main.c - * @brief convenience main function for tests - * @author Christian Grothoff - */ -#include "platform.h" -#include "transport-testing2.h" - - -/** - * Closure for #connect_cb. - */ -struct GNUNET_TRANSPORT_TESTING_ConnectRequestList -{ - /** - * Stored in a DLL. - */ - struct GNUNET_TRANSPORT_TESTING_ConnectRequestList *next; - - /** - * Stored in a DLL. - */ - struct GNUNET_TRANSPORT_TESTING_ConnectRequestList *prev; - - /** - * Overall context we are in. - */ - struct GNUNET_TRANSPORT_TESTING_ConnectCheckContext *ccc; - - /** - * Connect request this is about. - */ - struct GNUNET_TRANSPORT_TESTING_ConnectRequest *cr; - - /** - * Peer being connected. - */ - struct GNUNET_TRANSPORT_TESTING_PeerContext *p1; - - /** - * Peer being connected. - */ - struct GNUNET_TRANSPORT_TESTING_PeerContext *p2; -}; - - -/** - * Shutdown function for the test. Stops all peers. - * - * @param cls our `struct GNUNET_TRANSPORT_TESTING_ConnectCheckContext *` - */ -static void -do_shutdown (void *cls) -{ - struct GNUNET_TRANSPORT_TESTING_ConnectCheckContext *ccc = cls; - struct GNUNET_TRANSPORT_TESTING_ConnectRequestList *crl; - - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "Testcase shutting down\n"); - if (NULL != ccc->shutdown_task) - ccc->shutdown_task (ccc->shutdown_task_cls); - if (NULL != ccc->timeout_task) - { - GNUNET_SCHEDULER_cancel (ccc->timeout_task); - ccc->timeout_task = NULL; - } - if (NULL != ccc->connect_task) - { - GNUNET_SCHEDULER_cancel (ccc->connect_task); - ccc->connect_task = NULL; - } - while (NULL != (crl = ccc->crl_head)) - { - GNUNET_CONTAINER_DLL_remove (ccc->crl_head, - ccc->crl_tail, - crl); - GNUNET_TRANSPORT_TESTING_connect_peers_cancel (crl->cr); - GNUNET_free (crl); - } - for (unsigned int i = 0; i < ccc->num_peers; i++) - { - if (NULL != ccc->p[i]) - { - GNUNET_TRANSPORT_TESTING_stop_peer (ccc->p[i]); - ccc->p[i] = NULL; - } - } -} - - -/** - * Testcase hit timeout, shut it down with error. - * - * @param cls our `struct GNUNET_TRANSPORT_TESTING_ConnectCheckContext *` - */ -static void -do_timeout (void *cls) -{ - struct GNUNET_TRANSPORT_TESTING_ConnectCheckContext *ccc = cls; - - ccc->timeout_task = NULL; - GNUNET_log (GNUNET_ERROR_TYPE_WARNING, - "Testcase timed out\n"); - ccc->global_ret = GNUNET_SYSERR; - GNUNET_SCHEDULER_shutdown (); -} - - -/** - * Internal data structure. Closure for - * #connect_cb, #disconnect_cb, #my_nc and #start_cb. - * Allows us to identify which peer this is about. - */ -struct GNUNET_TRANSPORT_TESTING_InternalPeerContext -{ - /** - * Overall context of the callback. - */ - struct GNUNET_TRANSPORT_TESTING_ConnectCheckContext *ccc; - - /** - * Offset of the peer this is about. - */ - unsigned int off; -}; - - -/** - * Information tracked per connected peer. - */ -struct ConnectPairInfo -{ - /** - * Peer this is about. - */ - const struct GNUNET_PeerIdentity *sender; - - /** - * Information about the receiving peer. - */ - struct GNUNET_TRANSPORT_TESTING_InternalPeerContext *ipi; -}; - - -/** - * Function called when we connected two peers. Once we have gotten - * to the clique, launch test-specific logic. - * - * @param cls our `struct GNUNET_TRANSPORT_TESTING_ConnectRequestList *` - */ -static void -connect_cb (void *cls) -{ - struct GNUNET_TRANSPORT_TESTING_ConnectRequestList *crl = cls; - struct GNUNET_TRANSPORT_TESTING_ConnectCheckContext *ccc = crl->ccc; - - GNUNET_CONTAINER_DLL_remove (ccc->crl_head, - ccc->crl_tail, - crl); - { - char *p1_c = GNUNET_strdup (GNUNET_i2s (&crl->p1->id)); - - GNUNET_log (GNUNET_ERROR_TYPE_INFO, - "Peers connected: %u (%s) <-> %u (%s)\n", - crl->p1->no, - p1_c, - crl->p2->no, - GNUNET_i2s (&crl->p2->id)); - GNUNET_free (p1_c); - GNUNET_free (crl); - } - if (NULL == ccc->crl_head) - { - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "All connections UP, launching custom test logic.\n"); - GNUNET_SCHEDULER_add_now (ccc->connect_continuation, - ccc->connect_continuation_cls); - } -} - - -/** - * Find peer by peer ID. - * - * @param ccc context to search - * @param peer peer to look for - * @return NULL if @a peer was not found - */ -struct GNUNET_TRANSPORT_TESTING_PeerContext * -GNUNET_TRANSPORT_TESTING_find_peer (struct - GNUNET_TRANSPORT_TESTING_ConnectCheckContext - *ccc, - const struct GNUNET_PeerIdentity *peer) -{ - for (unsigned int i = 0; i < ccc->num_peers; i++) - if ((NULL != ccc->p[i]) && - (0 == memcmp (peer, - &ccc->p[i]->id, - sizeof(*peer)))) - return ccc->p[i]; - return NULL; -} - - -/** - * Wrapper around peers connecting. Calls client's nc function. - * - * @param cls our `struct GNUNET_TRANSPORT_TESTING_InternalPeerContext *` - * @param peer peer we got connected to - * @param mq message queue for transmissions to @a peer - * @return closure for message handlers - */ -static void * -my_nc (void *cls, - const struct GNUNET_PeerIdentity *peer, - struct GNUNET_MQ_Handle *mq) -{ - struct GNUNET_TRANSPORT_TESTING_InternalPeerContext *ipi = cls; - struct GNUNET_TRANSPORT_TESTING_ConnectCheckContext *ccc = ipi->ccc; - struct ConnectPairInfo *cpi; - - if (NULL != ccc->nc) - ccc->nc (ccc->cls, - ccc->p[ipi->off], - peer); - cpi = GNUNET_new (struct ConnectPairInfo); - cpi->ipi = ipi; - cpi->sender = peer; /* valid until disconnect */ - return cpi; -} - - -/** - * Wrapper around peers disconnecting. Calls client's nd function. - * - * @param cls our `struct GNUNET_TRANSPORT_TESTING_InternalPeerContext *` - * @param peer peer we got disconnected from - * @param custom_cls return value from @a my_nc - */ -static void -my_nd (void *cls, - const struct GNUNET_PeerIdentity *peer, - void *custom_cls) -{ - struct GNUNET_TRANSPORT_TESTING_InternalPeerContext *ipi = cls; - struct GNUNET_TRANSPORT_TESTING_ConnectCheckContext *ccc = ipi->ccc; - struct ConnectPairInfo *cpi = custom_cls; - - if (NULL != ccc->nd) - ccc->nd (ccc->cls, - ccc->p[ipi->off], - peer); - GNUNET_free (cpi); -} - - -/** - * Wrapper around receiving data. Calls client's rec function. - * - * @param cls our `struct ConnectPairInfo *` - * @param message message we received - * @return #GNUNET_OK (all messages are fine) - */ -static int -check_test (void *cls, - const struct GNUNET_TRANSPORT_TESTING_TestMessage *message) -{ - return GNUNET_OK; -} - - -/** - * Wrapper around receiving data. Calls client's rec function. - * - * @param cls our `struct ConnectPairInfo *` - * @param message message we received - */ -static void -handle_test (void *cls, - const struct GNUNET_TRANSPORT_TESTING_TestMessage *message) -{ - struct ConnectPairInfo *cpi = cls; - struct GNUNET_TRANSPORT_TESTING_InternalPeerContext *ipi = cpi->ipi; - struct GNUNET_TRANSPORT_TESTING_ConnectCheckContext *ccc = ipi->ccc; - - if (NULL != ccc->rec) - ccc->rec (ccc->cls, - ccc->p[ipi->off], - cpi->sender, - message); -} - - -/** - * Wrapper around receiving data. Calls client's rec function. - * - * @param cls our `struct ConnectPairInfo *` - * @param message message we received - * @return #GNUNET_OK (all messages are fine) - */ -static int -check_test2 (void *cls, - const struct GNUNET_TRANSPORT_TESTING_TestMessage *message) -{ - return GNUNET_OK; -} - - -/** - * Wrapper around receiving data. Calls client's rec function. - * - * @param cls our `struct ConnectPairInfo *` - * @param message message we received - */ -static void -handle_test2 (void *cls, - const struct GNUNET_TRANSPORT_TESTING_TestMessage *message) -{ - struct ConnectPairInfo *cpi = cls; - struct GNUNET_TRANSPORT_TESTING_InternalPeerContext *ipi = cpi->ipi; - struct GNUNET_TRANSPORT_TESTING_ConnectCheckContext *ccc = ipi->ccc; - - if (NULL != ccc->rec) - ccc->rec (ccc->cls, - ccc->p[ipi->off], - cpi->sender, - message); -} - - -/** - * Connect the peers as a clique. - * - * @param cls our `struct GNUNET_TRANSPORT_TESTING_ConnectCheckContext` - */ -static void -do_connect (void *cls) -{ - struct GNUNET_TRANSPORT_TESTING_ConnectCheckContext *ccc = cls; - - ccc->connect_task = NULL; - for (unsigned int i = 0; i < ccc->num_peers; i++) - for (unsigned int j = (ccc->bi_directional ? 0 : i + 1); j < ccc->num_peers; - j++) - { - struct GNUNET_TRANSPORT_TESTING_ConnectRequestList *crl; - - if (i == j) - continue; - crl = GNUNET_new (struct GNUNET_TRANSPORT_TESTING_ConnectRequestList); - GNUNET_CONTAINER_DLL_insert (ccc->crl_head, - ccc->crl_tail, - crl); - crl->ccc = ccc; - crl->p1 = ccc->p[i]; - crl->p2 = ccc->p[j]; - { - char *sender_c = GNUNET_strdup (GNUNET_i2s (&ccc->p[0]->id)); - - GNUNET_log (GNUNET_ERROR_TYPE_INFO, - "Test tries to connect peer %u (`%s') -> peer %u (`%s')\n", - ccc->p[0]->no, - sender_c, - ccc->p[1]->no, - GNUNET_i2s (&ccc->p[1]->id)); - GNUNET_free (sender_c); - } - crl->cr = GNUNET_TRANSPORT_TESTING_connect_peers (ccc->p[i], - ccc->p[j], - &connect_cb, - crl); - } -} - - -/** - * Function called once we have successfully launched a peer. - * Once all peers have been launched, we connect all of them - * in a clique. - * - * @param cls our `struct GNUNET_TRANSPORT_TESTING_InternalPeerContext *` - */ -static void -start_cb (void *cls) -{ - struct GNUNET_TRANSPORT_TESTING_InternalPeerContext *ipi = cls; - struct GNUNET_TRANSPORT_TESTING_ConnectCheckContext *ccc = ipi->ccc; - struct GNUNET_TRANSPORT_TESTING_PeerContext *p = ccc->p[ipi->off]; - - ccc->started++; - GNUNET_log (GNUNET_ERROR_TYPE_INFO, - "Peer %u (`%s') started\n", - p->no, - GNUNET_i2s (&p->id)); - if (ccc->started != ccc->num_peers) - return; - if (NULL != ccc->pre_connect_task) - { - /* Run the custom per-connect job, then give it a second to - go into effect before we continue connecting peers. */ - ccc->pre_connect_task (ccc->pre_connect_task_cls); - ccc->connect_task = GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_UNIT_SECONDS, - &do_connect, - ccc); - } - else - { - do_connect (ccc); - } -} - - -/** - * Function run from #GNUNET_TRANSPORT_TESTING_connect_check - * once the scheduler is up. Should launch the peers and - * then in the continuations try to connect them. - * - * @param cls our `struct GNUNET_TRANSPORT_TESTING_ConnectCheckContext *` - * @param args ignored - * @param cfgfile ignored - * @param cfg configuration - */ -static void -connect_check_run (void *cls, - char *const *args, - const char *cfgfile, - const struct GNUNET_CONFIGURATION_Handle *cfg) -{ - struct GNUNET_TRANSPORT_TESTING_ConnectCheckContext *ccc = cls; - int ok; - - ccc->cfg = cfg; - ccc->timeout_task = GNUNET_SCHEDULER_add_delayed (ccc->timeout, - &do_timeout, - ccc); - GNUNET_SCHEDULER_add_shutdown (&do_shutdown, - ccc); - ok = GNUNET_OK; - for (unsigned int i = 0; i < ccc->num_peers; i++) - { - struct GNUNET_MQ_MessageHandler handlers[] = { - GNUNET_MQ_hd_var_size (test, - GNUNET_TRANSPORT_TESTING_SIMPLE_MTYPE, - struct GNUNET_TRANSPORT_TESTING_TestMessage, - NULL), - GNUNET_MQ_hd_var_size (test2, - GNUNET_TRANSPORT_TESTING_SIMPLE_MTYPE2, - struct GNUNET_TRANSPORT_TESTING_TestMessage, - NULL), - GNUNET_MQ_handler_end () - }; - ccc->p[i] = GNUNET_TRANSPORT_TESTING_start_peer (ccc->tth, - ccc->cfg_files[i], - i + 1, - handlers, - &my_nc, - &my_nd, - &ccc->ip[i], - &start_cb, - &ccc->ip[i]); - if (NULL == ccc->p[i]) - ok = GNUNET_SYSERR; - } - if (GNUNET_OK != ok) - { - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - "Fail! Could not start peers!\n"); - GNUNET_SCHEDULER_shutdown (); - } -} - - -/** - * Common implementation of the #GNUNET_TRANSPORT_TESTING_CheckCallback. - * Starts and connects the two peers, then invokes the - * `connect_continuation` from @a cls. Sets up a timeout to - * abort the test, and a shutdown handler to clean up properly - * on exit. - * - * @param cls closure of type `struct GNUNET_TRANSPORT_TESTING_ConnectCheckContext` - * @param tth_ initialized testing handle - * @param test_plugin_ name of the plugin - * @param test_name_ name of the test - * @param num_peers number of entries in the @a cfg_file array - * @param cfg_files array of names of configuration files for the peers - * @return #GNUNET_SYSERR on error - */ -int -GNUNET_TRANSPORT_TESTING_connect_check (void *cls, - struct GNUNET_TRANSPORT_TESTING_Handle * - tth_, - const char *test_plugin_, - const char *test_name_, - unsigned int num_peers, - char *cfg_files[]) -{ - static struct GNUNET_GETOPT_CommandLineOption options[] = { - GNUNET_GETOPT_OPTION_END - }; - struct GNUNET_TRANSPORT_TESTING_ConnectCheckContext *ccc = cls; - struct GNUNET_TRANSPORT_TESTING_PeerContext *p[num_peers]; - struct GNUNET_TRANSPORT_TESTING_InternalPeerContext ip[num_peers]; - char *argv[] = { - (char *) test_name_, - "-c", - (char *) ccc->config_file, - NULL - }; - - ccc->num_peers = num_peers; - ccc->cfg_files = cfg_files; - ccc->test_plugin = test_plugin_; - ccc->test_name = test_name_; - ccc->tth = tth_; - ccc->global_ret = GNUNET_OK; - ccc->p = p; - ccc->ip = ip; - for (unsigned int i = 0; i < num_peers; i++) - { - ip[i].off = i; - ip[i].ccc = ccc; - } - if (GNUNET_OK != - GNUNET_PROGRAM_run ((sizeof(argv) / sizeof(char *)) - 1, - argv, - test_name_, - "nohelp", - options, - &connect_check_run, - ccc)) - return GNUNET_SYSERR; - return ccc->global_ret; -} - - -/** - * Setup testcase. Calls @a check with the data the test needs. - * - * @param argv0 binary name (argv[0]) - * @param filename source file name (__FILE__) - * @param num_peers number of peers to start - * @param check main function to run - * @param check_cls closure for @a check - * @return #GNUNET_OK on success - */ -int -GNUNET_TRANSPORT_TESTING_main_ (const char *argv0, - const char *filename, - unsigned int num_peers, - GNUNET_TRANSPORT_TESTING_CheckCallback check, - void *check_cls) -{ - struct GNUNET_TRANSPORT_TESTING_Handle *tth; - char *test_name; - char *test_source; - char *test_plugin; - char *cfg_names[num_peers]; - int ret; - - ret = GNUNET_OK; - test_name = GNUNET_TRANSPORT_TESTING_get_test_name (argv0); - GNUNET_log_setup (test_name, - "WARNING", - NULL); - test_source = GNUNET_TRANSPORT_TESTING_get_test_source_name (filename); - test_plugin = GNUNET_TRANSPORT_TESTING_get_test_plugin_name (argv0, - test_source); - for (unsigned int i = 0; i < num_peers; i++) - cfg_names[i] = GNUNET_TRANSPORT_TESTING_get_config_name (argv0, - i + 1); - tth = GNUNET_TRANSPORT_TESTING_init (); - if (NULL == tth) - { - ret = GNUNET_SYSERR; - } - else - { - ret = check (check_cls, - tth, - test_plugin, - test_name, - num_peers, - cfg_names); - GNUNET_TRANSPORT_TESTING_done (tth); - } - for (unsigned int i = 0; i < num_peers; i++) - GNUNET_free (cfg_names[i]); - GNUNET_free (test_source); - GNUNET_free (test_plugin); - GNUNET_free (test_name); - return ret; -} - - -/* end of transport-testing-main.c */ diff --git a/src/transport/transport-testing-send2.c b/src/transport/transport-testing-send2.c deleted file mode 100644 index c48dc3a4a..000000000 --- a/src/transport/transport-testing-send2.c +++ /dev/null @@ -1,241 +0,0 @@ -/* - This file is part of GNUnet. - Copyright (C) 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 . - - SPDX-License-Identifier: AGPL3.0-or-later - */ -/** - * @file transport-testing-send.c - * @brief convenience transmission function for tests - * @author Christian Grothoff - */ -#include "platform.h" -#include "transport-testing2.h" - -/** - * Acceptable transmission delay. - */ -#define TIMEOUT_TRANSMIT GNUNET_TIME_relative_multiply ( \ - GNUNET_TIME_UNIT_SECONDS, 30) - - -/** - * Return @a cx in @a cls. - */ -static void -find_cr (void *cls, - struct GNUNET_TRANSPORT_TESTING_ConnectRequest *cx) -{ - struct GNUNET_TRANSPORT_TESTING_ConnectRequest **cr = cls; - - if (GNUNET_NO == cx->connected) - return; - *cr = cx; -} - - -/** - * Send a test message of type @a mtype and size @a msize from - * peer @a sender to peer @a receiver. The peers should be - * connected when this function is called. - * - * @param sender the sending peer - * @param receiver the receiving peer - * @param mtype message type to use - * @param msize size of the message, at least `sizeof (struct GNUNET_TRANSPORT_TESTING_TestMessage)` - * @param num unique message number - * @param cont continuation to call after transmission - * @param cont_cls closure for @a cont - * @return #GNUNET_OK if message was queued, - * #GNUNET_NO if peers are not connected - * #GNUNET_SYSERR if @a msize is illegal - */ -int -GNUNET_TRANSPORT_TESTING_send (struct - GNUNET_TRANSPORT_TESTING_PeerContext *sender, - struct GNUNET_TRANSPORT_TESTING_PeerContext * - receiver, - uint16_t mtype, - uint16_t msize, - uint32_t num, - GNUNET_SCHEDULER_TaskCallback cont, - void *cont_cls) -{ - struct GNUNET_TRANSPORT_TESTING_ConnectRequest *cr; - struct GNUNET_MQ_Envelope *env; - struct GNUNET_TRANSPORT_TESTING_TestMessage *test; - - if (msize < sizeof(struct GNUNET_TRANSPORT_TESTING_TestMessage)) - { - GNUNET_break (0); - return GNUNET_SYSERR; - } - cr = NULL; - GNUNET_TRANSPORT_TESTING_find_connecting_context (sender, - receiver, - &find_cr, - &cr); - if (NULL == cr) - GNUNET_TRANSPORT_TESTING_find_connecting_context (receiver, - sender, - &find_cr, - &cr); - if (NULL == cr) - { - GNUNET_break (0); - return GNUNET_NO; - } - if (NULL == cr->mq) - { - GNUNET_break (0); - return GNUNET_NO; - } - { - char *receiver_s = GNUNET_strdup (GNUNET_i2s (&receiver->id)); - - GNUNET_log (GNUNET_ERROR_TYPE_INFO, - "Sending message from peer %u (`%s') -> peer %u (`%s') !\n", - sender->no, - GNUNET_i2s (&sender->id), - receiver->no, - receiver_s); - GNUNET_free (receiver_s); - } - env = GNUNET_MQ_msg_extra (test, - msize - sizeof(*test), - mtype); - test->num = htonl (num); - memset (&test[1], - num, - msize - sizeof(*test)); - GNUNET_MQ_notify_sent (env, - cont, - cont_cls); - GNUNET_MQ_send (cr->mq, - env); - return GNUNET_OK; -} - - -/** - * Task that sends a test message from the - * first peer to the second peer. - * - * @param ccc context which should contain at least two peers, the - * first two of which should be currently connected - * @param size desired message size - * @param cont continuation to call after transmission - * @param cont_cls closure for @a cont - */ -static void -do_send (struct GNUNET_TRANSPORT_TESTING_ConnectCheckContext *ccc, - uint16_t size, - GNUNET_SCHEDULER_TaskCallback cont, - void *cont_cls) -{ - int ret; - - ccc->global_ret = GNUNET_SYSERR; - ret = GNUNET_TRANSPORT_TESTING_send (ccc->p[0], - ccc->p[1], - GNUNET_TRANSPORT_TESTING_SIMPLE_MTYPE, - size, - ccc->send_num_gen++, - cont, - cont_cls); - GNUNET_assert (GNUNET_SYSERR != ret); - if (GNUNET_NO == ret) - { - GNUNET_break (0); - ccc->global_ret = GNUNET_SYSERR; - GNUNET_SCHEDULER_shutdown (); - } -} - - -/** - * Task that sends a minimalistic test message from the - * first peer to the second peer. - * - * @param cls the `struct GNUNET_TRANSPORT_TESTING_ConnectCheckContext` - * which should contain at least two peers, the first two - * of which should be currently connected - */ -void -GNUNET_TRANSPORT_TESTING_simple_send (void *cls) -{ - struct GNUNET_TRANSPORT_TESTING_SendClosure *sc = cls; - int done; - size_t msize; - - if (0 < sc->num_messages) - { - sc->num_messages--; - done = (0 == sc->num_messages); - } - else - { - done = 0; /* infinite loop */ - } - msize = sizeof(struct GNUNET_TRANSPORT_TESTING_TestMessage); - if (NULL != sc->get_size_cb) - msize = sc->get_size_cb (sc->num_messages); - /* if this was the last message, call the continuation, - otherwise call this function again */ - do_send (sc->ccc, - msize, - done ? sc->cont : &GNUNET_TRANSPORT_TESTING_simple_send, - done ? sc->cont_cls : sc); -} - - -/** - * Task that sends a large test message from the - * first peer to the second peer. - * - * @param cls the `struct GNUNET_TRANSPORT_TESTING_ConnectCheckContext` - * which should contain at least two peers, the first two - * of which should be currently connected - */ -void -GNUNET_TRANSPORT_TESTING_large_send (void *cls) -{ - struct GNUNET_TRANSPORT_TESTING_SendClosure *sc = cls; - int done; - size_t msize; - - if (0 < sc->num_messages) - { - sc->num_messages--; - done = (0 == sc->num_messages); - } - else - { - done = 0; /* infinite loop */ - } - msize = 2600; - if (NULL != sc->get_size_cb) - msize = sc->get_size_cb (sc->num_messages); - /* if this was the last message, call the continuation, - otherwise call this function again */ - do_send (sc->ccc, - msize, - done ? sc->cont : &GNUNET_TRANSPORT_TESTING_large_send, - done ? sc->cont_cls : sc); -} - - -/* end of transport-testing-send.c */ diff --git a/src/transport/transport-testing2.c b/src/transport/transport-testing2.c deleted file mode 100644 index afa0b0ad4..000000000 --- a/src/transport/transport-testing2.c +++ /dev/null @@ -1,924 +0,0 @@ -/* - This file is part of GNUnet. - Copyright (C) 2006, 2009, 2015, 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 . - - SPDX-License-Identifier: AGPL3.0-or-later - */ -/** - * @file transport-testing.c - * @brief testing lib for transport service - * @author Matthias Wachs - * @author Christian Grothoff - */ -#include "platform.h" -#include "transport-testing2.h" - - -#define LOG(kind, ...) GNUNET_log_from (kind, "transport-testing", __VA_ARGS__) - - -static struct GNUNET_TRANSPORT_TESTING_PeerContext * -find_peer_context (struct GNUNET_TRANSPORT_TESTING_Handle *tth, - const struct GNUNET_PeerIdentity *peer) -{ - struct GNUNET_TRANSPORT_TESTING_PeerContext *t; - - for (t = tth->p_head; NULL != t; t = t->next) - if (0 == memcmp (&t->id, - peer, - sizeof(struct GNUNET_PeerIdentity))) - return t; - return NULL; -} - - -/** - * Find any connecting context matching the given pair of peers. - * - * @param p1 first peer - * @param p2 second peer - * @param cb function to call - * @param cb_cls closure for @a cb - */ -void -GNUNET_TRANSPORT_TESTING_find_connecting_context (struct - GNUNET_TRANSPORT_TESTING_PeerContext - *p1, - struct - GNUNET_TRANSPORT_TESTING_PeerContext - *p2, - GNUNET_TRANSPORT_TESTING_ConnectContextCallback - cb, - void *cb_cls) -{ - struct GNUNET_TRANSPORT_TESTING_Handle *tth = p1->tth; - struct GNUNET_TRANSPORT_TESTING_ConnectRequest *cc; - struct GNUNET_TRANSPORT_TESTING_ConnectRequest *ccn; - - for (cc = tth->cc_head; NULL != cc; cc = ccn) - { - ccn = cc->next; - if ((cc->p1 == p1) && - (cc->p2 == p2)) - cb (cb_cls, - cc); - } -} - - -static void -set_p1c (void *cls, - struct GNUNET_TRANSPORT_TESTING_ConnectRequest *cx) -{ - int *found = cls; - - if (NULL != found) - *found = GNUNET_YES; - cx->p1_c = GNUNET_YES; -} - - -static void -set_mq (void *cls, - struct GNUNET_TRANSPORT_TESTING_ConnectRequest *cx) -{ - struct GNUNET_MQ_Handle *mq = cls; - - cx->mq = mq; -} - - -static void -set_p2c (void *cls, - struct GNUNET_TRANSPORT_TESTING_ConnectRequest *cx) -{ - int *found = cls; - - if (NULL != found) - *found = GNUNET_YES; - cx->p2_c = GNUNET_YES; -} - - -static void -clear_p1c (void *cls, - struct GNUNET_TRANSPORT_TESTING_ConnectRequest *cx) -{ - int *found = cls; - - if (NULL != found) - *found = GNUNET_YES; - cx->p1_c = GNUNET_NO; -} - - -static void -clear_p2c (void *cls, - struct GNUNET_TRANSPORT_TESTING_ConnectRequest *cx) -{ - int *found = cls; - - if (NULL != found) - *found = GNUNET_YES; - cx->p2_c = GNUNET_NO; -} - - -static void * -notify_connect (void *cls, - const struct GNUNET_PeerIdentity *peer, - struct GNUNET_MQ_Handle *mq) -{ - struct GNUNET_TRANSPORT_TESTING_PeerContext *p = cls; - struct GNUNET_TRANSPORT_TESTING_Handle *tth = p->tth; - char *p2_s; - struct GNUNET_TRANSPORT_TESTING_PeerContext *p2; - struct GNUNET_TRANSPORT_TESTING_ConnectRequest *cc; - struct GNUNET_TRANSPORT_TESTING_ConnectRequest *ccn; - int found; - void *ret; - - p2 = find_peer_context (p->tth, - peer); - if (NULL != p->nc) - ret = p->nc (p->cb_cls, - peer, - mq); - else - ret = NULL; - - if (NULL != p2) - GNUNET_asprintf (&p2_s, - "%u (`%s')", - p2->no, - GNUNET_i2s (&p2->id)); - else - GNUNET_asprintf (&p2_s, - "`%s'", - GNUNET_i2s (peer)); - LOG (GNUNET_ERROR_TYPE_DEBUG, - "Peers %s connected to peer %u (`%s')\n", - p2_s, - p->no, - GNUNET_i2s (&p->id)); - GNUNET_free (p2_s); - /* update flags in connecting contexts */ - found = GNUNET_NO; - GNUNET_TRANSPORT_TESTING_find_connecting_context (p, - p2, - &set_p1c, - &found); - if (GNUNET_NO == found) - { - cc = GNUNET_new (struct GNUNET_TRANSPORT_TESTING_ConnectRequest); - cc->p1 = p; - cc->p2 = p2; - cc->p1_c = GNUNET_YES; - GNUNET_CONTAINER_DLL_insert (tth->cc_head, - tth->cc_tail, - cc); - } - found = GNUNET_NO; - GNUNET_TRANSPORT_TESTING_find_connecting_context (p2, - p, - &set_p2c, - &found); - if (GNUNET_NO == found) - { - cc = GNUNET_new (struct GNUNET_TRANSPORT_TESTING_ConnectRequest); - cc->p1 = p2; - cc->p2 = p; - cc->p1_c = GNUNET_YES; - GNUNET_CONTAINER_DLL_insert (tth->cc_head, - tth->cc_tail, - cc); - } - GNUNET_TRANSPORT_TESTING_find_connecting_context (p, - p2, - &set_mq, - mq); - /* update set connected flag for all requests */ - for (cc = tth->cc_head; NULL != cc; cc = cc->next) - { - if (GNUNET_YES == cc->connected) - continue; - if ((GNUNET_YES == cc->p1_c) && - (GNUNET_YES == cc->p2_c)) - { - cc->connected = GNUNET_YES; - /* stop trying to connect */ - if (NULL != cc->tct) - { - GNUNET_SCHEDULER_cancel (cc->tct); - cc->tct = NULL; - } - if (NULL != cc->ah_sh) - { - GNUNET_TRANSPORT_application_suggest_cancel (cc->ah_sh); - cc->ah_sh = NULL; - } - } - } - /* then notify application */ - for (cc = tth->cc_head; NULL != cc; cc = ccn) - { - ccn = cc->next; - if ((GNUNET_YES == cc->connected) && - (NULL != cc->cb)) - { - cc->cb (cc->cb_cls); - cc->cb = NULL; /* only notify once! */ - } - } - return ret; -} - - -/** - * Offer the current HELLO of P2 to P1. - * - * @param cls our `struct GNUNET_TRANSPORT_TESTING_ConnectRequest` - */ -static void -offer_hello (void *cls); - - -static void -notify_disconnect (void *cls, - const struct GNUNET_PeerIdentity *peer, - void *handler_cls) -{ - struct GNUNET_TRANSPORT_TESTING_PeerContext *p = cls; - struct GNUNET_TRANSPORT_TESTING_Handle *tth = p->tth; - char *p2_s; - /* Find PeerContext */ - int no = 0; - struct GNUNET_TRANSPORT_TESTING_PeerContext *p2 = NULL; - struct GNUNET_TRANSPORT_TESTING_ConnectRequest *cc; - - p2 = find_peer_context (p->tth, - peer); - no = p->no; - if (NULL != p2) - GNUNET_asprintf (&p2_s, - "%u (`%s')", - p2->no, - GNUNET_i2s (&p2->id)); - else - GNUNET_asprintf (&p2_s, - "`%s'", - GNUNET_i2s (peer)); - LOG (GNUNET_ERROR_TYPE_DEBUG, - "Peers %s disconnected from peer %u (`%s')\n", - p2_s, - no, - GNUNET_i2s (&p->id)); - GNUNET_free (p2_s); - /* notify about disconnect */ - if (NULL != p->nd) - p->nd (p->cb_cls, - peer, - handler_cls); - if (NULL == p2) - return; - /* clear MQ, it is now invalid */ - GNUNET_TRANSPORT_TESTING_find_connecting_context (p, - p2, - &set_mq, - NULL); - /* update set connected flags for all requests */ - GNUNET_TRANSPORT_TESTING_find_connecting_context (p, - p2, - &clear_p1c, - NULL); - GNUNET_TRANSPORT_TESTING_find_connecting_context (p2, - p, - &clear_p2c, - NULL); - /* resume connectivity requests as necessary */ - for (cc = tth->cc_head; NULL != cc; cc = cc->next) - { - if (GNUNET_NO == cc->connected) - continue; - if ((GNUNET_YES != cc->p1_c) || - (GNUNET_YES != cc->p2_c)) - { - cc->connected = GNUNET_NO; - /* start trying to connect */ - if (NULL == cc->tct) - cc->tct = GNUNET_SCHEDULER_add_now (&offer_hello, - cc); - if (NULL == cc->ah_sh) - cc->ah_sh = GNUNET_TRANSPORT_application_suggest (cc->p1->ah, - &p2->id, - GNUNET_MQ_PRIO_BEST_EFFORT, - GNUNET_BANDWIDTH_ZERO); - } - } -} - - -static void -retrieve_hello (void *cls); - -static void -hello_iter_cb (void *cb_cls, - const struct GNUNET_PEERSTORE_Record *record, - const char *emsg) -{ - struct GNUNET_TRANSPORT_TESTING_PeerContext *p = cb_cls; - if (NULL == record) - { - p->pic = NULL; - if (NULL != p->start_cb) - p->rh_task = GNUNET_SCHEDULER_add_now (retrieve_hello, p); - return; - } - // Check record type et al? - p->hello_size = record->value_size; - p->hello = GNUNET_malloc (p->hello_size); - memcpy (p->hello, record->value, p->hello_size); - p->hello[p->hello_size - 1] = '\0'; - - GNUNET_PEERSTORE_iterate_cancel (p->pic); - p->pic = NULL; - if (NULL != p->start_cb) - { - LOG (GNUNET_ERROR_TYPE_DEBUG, - "Peer %u (`%s') successfully started\n", - p->no, - GNUNET_i2s (&p->id)); - p->start_cb (p->start_cb_cls); - p->start_cb = NULL; - } -} - - -static void -retrieve_hello (void *cls) -{ - struct GNUNET_TRANSPORT_TESTING_PeerContext *p = cls; - p->rh_task = NULL; - p->pic = GNUNET_PEERSTORE_iterate (p->ph, - "transport", - &p->id, - GNUNET_PEERSTORE_TRANSPORT_HELLO_KEY, - hello_iter_cb, - p); - -} - - -struct GNUNET_TRANSPORT_TESTING_PeerContext * -GNUNET_TRANSPORT_TESTING_start_peer (struct - GNUNET_TRANSPORT_TESTING_Handle *tth, - const char *cfgname, - int peer_id, - const struct - GNUNET_MQ_MessageHandler *handlers, - GNUNET_TRANSPORT_NotifyConnect nc, - GNUNET_TRANSPORT_NotifyDisconnect nd, - void *cb_cls, - GNUNET_SCHEDULER_TaskCallback start_cb, - void *start_cb_cls) -{ - char *emsg = NULL; - struct GNUNET_TRANSPORT_TESTING_PeerContext *p; - struct GNUNET_PeerIdentity dummy; - unsigned int i; - - if (GNUNET_NO == GNUNET_DISK_file_test (cfgname)) - { - LOG (GNUNET_ERROR_TYPE_ERROR, - "File not found: `%s'\n", - cfgname); - return NULL; - } - - p = GNUNET_new (struct GNUNET_TRANSPORT_TESTING_PeerContext); - p->tth = tth; - p->nc = nc; - p->nd = nd; - if (NULL != handlers) - { - for (i = 0; NULL != handlers[i].cb; i++) - ; - p->handlers = GNUNET_new_array (i + 1, - struct GNUNET_MQ_MessageHandler); - GNUNET_memcpy (p->handlers, - handlers, - i * sizeof(struct GNUNET_MQ_MessageHandler)); - } - if (NULL != cb_cls) - p->cb_cls = cb_cls; - else - p->cb_cls = p; - p->start_cb = start_cb; - if (NULL != start_cb_cls) - p->start_cb_cls = start_cb_cls; - else - p->start_cb_cls = p; - GNUNET_CONTAINER_DLL_insert (tth->p_head, - tth->p_tail, - p); - - /* Create configuration and call testing lib to modify it */ - p->cfg = GNUNET_CONFIGURATION_create (); - GNUNET_assert (GNUNET_OK == - GNUNET_CONFIGURATION_load (p->cfg, cfgname)); - if (GNUNET_SYSERR == - GNUNET_TESTING_configuration_create (tth->tl_system, - p->cfg)) - { - LOG (GNUNET_ERROR_TYPE_ERROR, - "Testing library failed to create unique configuration based on `%s'\n", - cfgname); - GNUNET_CONFIGURATION_destroy (p->cfg); - GNUNET_free (p); - return NULL; - } - - p->no = peer_id; - /* Configure peer with configuration */ - p->peer = GNUNET_TESTING_peer_configure (tth->tl_system, - p->cfg, - p->no, - NULL, - &emsg); - if (NULL == p->peer) - { - LOG (GNUNET_ERROR_TYPE_ERROR, - "Testing library failed to create unique configuration based on `%s': `%s'\n", - cfgname, - emsg); - GNUNET_TRANSPORT_TESTING_stop_peer (p); - GNUNET_free (emsg); - return NULL; - } - - if (GNUNET_OK != GNUNET_TESTING_peer_start (p->peer)) - { - LOG (GNUNET_ERROR_TYPE_ERROR, - "Testing library failed to create unique configuration based on `%s'\n", - cfgname); - GNUNET_TRANSPORT_TESTING_stop_peer (p); - return NULL; - } - - memset (&dummy, - '\0', - sizeof(dummy)); - GNUNET_TESTING_peer_get_identity (p->peer, - &p->id); - if (0 == memcmp (&dummy, - &p->id, - sizeof(struct GNUNET_PeerIdentity))) - { - LOG (GNUNET_ERROR_TYPE_ERROR, - "Testing library failed to obtain peer identity for peer %u\n", - p->no); - GNUNET_TRANSPORT_TESTING_stop_peer (p); - return NULL; - } - LOG (GNUNET_ERROR_TYPE_DEBUG, - "Peer %u configured with identity `%s'\n", - p->no, - GNUNET_i2s_full (&p->id)); - p->th = GNUNET_TRANSPORT_core_connect (p->cfg, - NULL, - handlers, - p, - ¬ify_connect, - ¬ify_disconnect); - if (NULL == p->th) - { - LOG (GNUNET_ERROR_TYPE_ERROR, - "Failed to connect to transport service for peer `%s': `%s'\n", - cfgname, - emsg); - GNUNET_TRANSPORT_TESTING_stop_peer (p); - GNUNET_free (emsg); - return NULL; - } - p->ah = GNUNET_TRANSPORT_application_init (p->cfg); - if (NULL == p->ah) - { - LOG (GNUNET_ERROR_TYPE_ERROR, - "Failed to connect to TNG service for peer `%s': `%s'\n", - cfgname, - emsg); - GNUNET_TRANSPORT_TESTING_stop_peer (p); - GNUNET_free (emsg); - return NULL; - } - p->ph = GNUNET_PEERSTORE_connect (p->cfg); - // FIXME Error handling - p->rh_task = GNUNET_SCHEDULER_add_now (retrieve_hello, p); - - return p; -} - - -int -GNUNET_TRANSPORT_TESTING_restart_peer (struct - GNUNET_TRANSPORT_TESTING_PeerContext *p, - GNUNET_SCHEDULER_TaskCallback restart_cb, - void *restart_cb_cls) -{ - struct GNUNET_TRANSPORT_TESTING_ConnectRequest *cc; - struct GNUNET_TRANSPORT_TESTING_ConnectRequest *ccn; - - /* shutdown */ - LOG (GNUNET_ERROR_TYPE_DEBUG, - "Stopping peer %u (`%s')\n", - p->no, - GNUNET_i2s (&p->id)); - if (NULL != p->pic) - { - GNUNET_PEERSTORE_iterate_cancel (p->pic); - p->pic = NULL; - } - if (NULL != p->th) - { - GNUNET_TRANSPORT_core_disconnect (p->th); - p->th = NULL; - } - for (cc = p->tth->cc_head; NULL != cc; cc = ccn) - { - ccn = cc->next; - if ((cc->p1 == p) || - (cc->p2 == p)) - GNUNET_TRANSPORT_TESTING_connect_peers_cancel (cc); - } - if (NULL != p->ah) - { - GNUNET_TRANSPORT_application_done (p->ah); - p->ah = NULL; - } - if (GNUNET_SYSERR == - GNUNET_TESTING_peer_stop (p->peer)) - { - LOG (GNUNET_ERROR_TYPE_ERROR, - "Failed to stop peer %u (`%s')\n", - p->no, - GNUNET_i2s (&p->id)); - return GNUNET_SYSERR; - } - - sleep (5); // YUCK! - - LOG (GNUNET_ERROR_TYPE_DEBUG, - "Restarting peer %u (`%s')\n", - p->no, - GNUNET_i2s (&p->id)); - /* restart */ - if (GNUNET_SYSERR == GNUNET_TESTING_peer_start (p->peer)) - { - LOG (GNUNET_ERROR_TYPE_ERROR, - "Failed to restart peer %u (`%s')\n", - p->no, - GNUNET_i2s (&p->id)); - return GNUNET_SYSERR; - } - - GNUNET_assert (NULL == p->start_cb); - p->start_cb = restart_cb; - p->start_cb_cls = restart_cb_cls; - - p->th = GNUNET_TRANSPORT_core_connect (p->cfg, - NULL, - p->handlers, - p, - ¬ify_connect, - ¬ify_disconnect); - GNUNET_assert (NULL != p->th); - p->ah = GNUNET_TRANSPORT_application_init (p->cfg); - p->pic = GNUNET_PEERSTORE_iterate (p->ph, - "transport", - &p->id, - GNUNET_PEERSTORE_TRANSPORT_HELLO_KEY, - hello_iter_cb, - p); - GNUNET_assert (NULL != p->pic); - return GNUNET_OK; -} - - -/** - * Shutdown the given peer - * - * @param p the peer - */ -void -GNUNET_TRANSPORT_TESTING_stop_peer (struct - GNUNET_TRANSPORT_TESTING_PeerContext *p) -{ - struct GNUNET_TRANSPORT_TESTING_Handle *tth = p->tth; - struct GNUNET_TRANSPORT_TESTING_ConnectRequest *cc; - struct GNUNET_TRANSPORT_TESTING_ConnectRequest *ccn; - /* shutdown */ - LOG (GNUNET_ERROR_TYPE_DEBUG, - "Stopping peer %u (`%s')\n", - p->no, - GNUNET_i2s (&p->id)); - - for (cc = tth->cc_head; NULL != cc; cc = ccn) - { - ccn = cc->next; - if ((cc->p1 == p) || - (cc->p2 == p)) - GNUNET_TRANSPORT_TESTING_connect_peers_cancel (cc); - } - if (NULL != p->pic) - { - GNUNET_PEERSTORE_iterate_cancel (p->pic); - p->pic = NULL; - } - if (NULL != p->th) - { - GNUNET_TRANSPORT_core_disconnect (p->th); - p->th = NULL; - } - if (NULL != p->ah) - { - GNUNET_TRANSPORT_application_done (p->ah); - p->ah = NULL; - } - if (NULL != p->ph) - { - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "Disconnecting from PEERSTORE service\n"); - GNUNET_PEERSTORE_disconnect (p->ph, GNUNET_NO); - p->ph = NULL; - } - - if (NULL != p->peer) - { - if (GNUNET_OK != - GNUNET_TESTING_peer_stop (p->peer)) - { - LOG (GNUNET_ERROR_TYPE_DEBUG, - "Testing lib failed to stop peer %u (`%s')\n", - p->no, - GNUNET_i2s (&p->id)); - } - GNUNET_TESTING_peer_destroy (p->peer); - p->peer = NULL; - } - if (NULL != p->hello) - { - GNUNET_free (p->hello); - p->hello = NULL; - } - if (NULL != p->cfg) - { - GNUNET_CONFIGURATION_destroy (p->cfg); - p->cfg = NULL; - } - if (NULL != p->handlers) - { - GNUNET_free (p->handlers); - p->handlers = NULL; - } - GNUNET_CONTAINER_DLL_remove (tth->p_head, - tth->p_tail, - p); - LOG (GNUNET_ERROR_TYPE_DEBUG, - "Peer %u (`%s') stopped\n", - p->no, - GNUNET_i2s (&p->id)); - if (NULL != p->rh_task) - GNUNET_SCHEDULER_cancel (p->rh_task); - p->rh_task = NULL; - GNUNET_free (p); -} - - -/** - * Function called after the HELLO was passed to the - * transport service. - * FIXME maybe schedule the application_validate somehow - */ -/* - static void - hello_offered (void *cls) - { - struct GNUNET_TRANSPORT_TESTING_ConnectRequest *cc = cls; - - cc->oh = NULL; - cc->tct = GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_UNIT_SECONDS, - &offer_hello, - cc); - }*/ - - -static void -offer_hello (void *cls) -{ - struct GNUNET_TRANSPORT_TESTING_ConnectRequest *cc = cls; - struct GNUNET_TRANSPORT_TESTING_PeerContext *p1 = cc->p1; - struct GNUNET_TRANSPORT_TESTING_PeerContext *p2 = cc->p2; - struct GNUNET_TIME_Absolute t; - enum GNUNET_NetworkType nt = 0; - char *addr; - - cc->tct = NULL; - { - char *p2_s = GNUNET_strdup (GNUNET_i2s (&p2->id)); - - LOG (GNUNET_ERROR_TYPE_DEBUG, - "Asking peer %u (`%s') to connect peer %u (`%s'), providing HELLO with %s\n", - p1->no, - GNUNET_i2s (&p1->id), - p2->no, - p2_s, - p2->hello); - GNUNET_free (p2_s); - } - - addr = GNUNET_HELLO_extract_address (p2->hello, - p2->hello_size, - &p2->id, - &nt, - &t); - GNUNET_assert (NULL != addr); - GNUNET_assert (NULL != p1->hello); - GNUNET_TRANSPORT_application_validate (p1->ah, - &p2->id, - nt, - addr); - GNUNET_free (addr); -} - - -/** - * Initiate a connection from p1 to p2 by offering p1 p2's HELLO message - * - * Remarks: start_peer's notify_connect callback can be called before. - * - * @param tth transport testing handle - * @param p1 peer 1 - * @param p2 peer 2 - * @param cb the callback to call when both peers notified that they are connected - * @param cls callback cls - * @return a connect request handle - */ -struct GNUNET_TRANSPORT_TESTING_ConnectRequest * -GNUNET_TRANSPORT_TESTING_connect_peers (struct - GNUNET_TRANSPORT_TESTING_PeerContext *p1, - struct - GNUNET_TRANSPORT_TESTING_PeerContext *p2, - GNUNET_SCHEDULER_TaskCallback cb, - void *cls) -{ - struct GNUNET_TRANSPORT_TESTING_Handle *tth = p1->tth; - struct GNUNET_TRANSPORT_TESTING_ConnectRequest *cc; - struct GNUNET_TRANSPORT_TESTING_ConnectRequest *ccn; - - ccn = NULL; - for (cc = tth->cc_head; NULL != cc; cc = cc->next) - { - if ((cc->p1 == p1) && - (cc->p2 == p2)) - { - ccn = cc; - break; - } - } - - cc = GNUNET_new (struct GNUNET_TRANSPORT_TESTING_ConnectRequest); - cc->p1 = p1; - cc->p2 = p2; - cc->cb = cb; - if (NULL != cls) - cc->cb_cls = cls; - else - cc->cb_cls = cc; - if (NULL != ccn) - { - cc->p1_c = ccn->p1_c; - cc->p2_c = ccn->p2_c; - cc->connected = ccn->connected; - } - GNUNET_CONTAINER_DLL_insert (tth->cc_head, - tth->cc_tail, - cc); - cc->tct = GNUNET_SCHEDULER_add_now (&offer_hello, - cc); - cc->ah_sh = GNUNET_TRANSPORT_application_suggest (cc->p1->ah, - &p2->id, - GNUNET_MQ_PRIO_BEST_EFFORT, - GNUNET_BANDWIDTH_ZERO); - LOG (GNUNET_ERROR_TYPE_DEBUG, - "New connect request %p\n", - cc); - return cc; -} - - -void -GNUNET_TRANSPORT_TESTING_connect_peers_cancel (struct - GNUNET_TRANSPORT_TESTING_ConnectRequest - *cc) -{ - struct GNUNET_TRANSPORT_TESTING_Handle *tth = cc->p1->tth; - - LOG (GNUNET_ERROR_TYPE_DEBUG, - "Canceling connect request!\n"); - if (NULL != cc->tct) - { - GNUNET_SCHEDULER_cancel (cc->tct); - cc->tct = NULL; - } - if (NULL != cc->ah_sh) - { - GNUNET_TRANSPORT_application_suggest_cancel (cc->ah_sh); - cc->ah_sh = NULL; - } - GNUNET_CONTAINER_DLL_remove (tth->cc_head, - tth->cc_tail, - cc); - GNUNET_free (cc); -} - - -/** - * Clean up the transport testing - * - * @param tth transport testing handle - */ -void -GNUNET_TRANSPORT_TESTING_done (struct GNUNET_TRANSPORT_TESTING_Handle *tth) -{ - struct GNUNET_TRANSPORT_TESTING_ConnectRequest *cc; - struct GNUNET_TRANSPORT_TESTING_ConnectRequest *ct; - struct GNUNET_TRANSPORT_TESTING_PeerContext *p; - struct GNUNET_TRANSPORT_TESTING_PeerContext *t; - - if (NULL == tth) - return; - cc = tth->cc_head; - while (NULL != cc) - { - ct = cc->next; - LOG (GNUNET_ERROR_TYPE_ERROR, - "Developer forgot to cancel connect request!\n"); - GNUNET_TRANSPORT_TESTING_connect_peers_cancel (cc); - cc = ct; - } - p = tth->p_head; - while (NULL != p) - { - t = p->next; - LOG (GNUNET_ERROR_TYPE_ERROR, - "Developer forgot to stop peer!\n"); - GNUNET_TRANSPORT_TESTING_stop_peer (p); - p = t; - } - GNUNET_TESTING_system_destroy (tth->tl_system, - GNUNET_YES); - - GNUNET_free (tth); -} - - -/** - * Initialize the transport testing - * - * @return transport testing handle - */ -struct GNUNET_TRANSPORT_TESTING_Handle * -GNUNET_TRANSPORT_TESTING_init () -{ - struct GNUNET_TRANSPORT_TESTING_Handle *tth; - - tth = GNUNET_new (struct GNUNET_TRANSPORT_TESTING_Handle); - tth->tl_system = GNUNET_TESTING_system_create ("transport-testing", - NULL, - NULL, - NULL); - if (NULL == tth->tl_system) - { - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - "Failed to initialize testing library!\n"); - GNUNET_free (tth); - return NULL; - } - return tth; -} - - -/* end of transport-testing.c */ diff --git a/src/transport/transport-testing2.h b/src/transport/transport-testing2.h deleted file mode 100644 index 8f1071e8f..000000000 --- a/src/transport/transport-testing2.h +++ /dev/null @@ -1,941 +0,0 @@ -/* - This file is part of GNUnet. - Copyright (C) 2006, 2009, 2015, 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 . - - SPDX-License-Identifier: AGPL3.0-or-later - */ - -/** - * @file transport-testing.h - * @brief testing lib for transport service - * @author Matthias Wachs - * @author Christian Grothoff - */ -#ifndef TRANSPORT_TESTING_H -#define TRANSPORT_TESTING_H -#include "platform.h" -#include "gnunet_util_lib.h" -#include "gnunet_hello_lib.h" -#include "gnunet_peerstore_service.h" -#include "gnunet_transport_core_service.h" -#include "gnunet_transport_application_service.h" -#include "gnunet_testing_lib.h" - - -/* ************* Basic functions for starting/stopping/connecting *********** */ - -/** - * Context for a single peer - */ -struct GNUNET_TRANSPORT_TESTING_PeerContext; - -/** - * Definition for a transport testing handle - */ -struct GNUNET_TRANSPORT_TESTING_Handle; - - -/** - * Context for a single peer - */ -struct GNUNET_TRANSPORT_TESTING_PeerContext -{ - /** - * Next element in the DLL - */ - struct GNUNET_TRANSPORT_TESTING_PeerContext *next; - - /** - * Previous element in the DLL - */ - struct GNUNET_TRANSPORT_TESTING_PeerContext *prev; - - /** - * Transport testing handle this peer belongs to - */ - struct GNUNET_TRANSPORT_TESTING_Handle *tth; - - /** - * Application handle - */ - struct GNUNET_TRANSPORT_ApplicationHandle *ah; - - /** - * Peer's configuration - */ - struct GNUNET_CONFIGURATION_Handle *cfg; - - /** - * Peer's transport service handle - */ - struct GNUNET_TRANSPORT_CoreHandle *th; - - /** - * Peer's PEERSTORE Handle - */ - struct GNUNET_PEERSTORE_Handle *ph; - - /** - * Peer's transport get hello handle to retrieve peer's HELLO message - */ - struct GNUNET_PEERSTORE_IterateContext *pic; - - /** - * Hello - */ - char *hello; - - /** - * Hello size - */ - size_t hello_size; - - /** - * Peer's testing handle - */ - struct GNUNET_TESTING_Peer *peer; - - /** - * Peer identity - */ - struct GNUNET_PeerIdentity id; - - /** - * Handle for the peer's ARM process - */ - struct GNUNET_OS_Process *arm_proc; - - /** - * Receive callback - */ - struct GNUNET_MQ_MessageHandler *handlers; - - /** - * Notify connect callback - */ - GNUNET_TRANSPORT_NotifyConnect nc; - - /** - * Notify disconnect callback - */ - GNUNET_TRANSPORT_NotifyDisconnect nd; - - /** - * Startup completed callback - */ - GNUNET_SCHEDULER_TaskCallback start_cb; - - /** - * Hello get task - */ - struct GNUNET_SCHEDULER_Task *rh_task; - - /** - * Closure for the @a nc and @a nd callbacks - */ - void *cb_cls; - - /** - * Closure for @e start_cb. - */ - void *start_cb_cls; - - /** - * An unique number to identify the peer - */ - unsigned int no; -}; - - -/** - * Handle for a request to connect two peers. - */ -struct GNUNET_TRANSPORT_TESTING_ConnectRequest -{ - /** - * Kept in a DLL. - */ - struct GNUNET_TRANSPORT_TESTING_ConnectRequest *next; - - /** - * Kept in a DLL. - */ - struct GNUNET_TRANSPORT_TESTING_ConnectRequest *prev; - - /** - * Peer we want to connect. - */ - struct GNUNET_TRANSPORT_TESTING_PeerContext *p1; - - /** - * Peer we want to connect. - */ - struct GNUNET_TRANSPORT_TESTING_PeerContext *p2; - - /** - * Task by which we accomplish the connection. - */ - struct GNUNET_SCHEDULER_Task *tct; - - /** - * Handle by which we ask TNG to facilitate the connection. - */ - struct GNUNET_TRANSPORT_ApplicationSuggestHandle *ah_sh; - - /** - * Function to call upon completion. - */ - GNUNET_SCHEDULER_TaskCallback cb; - - /** - * Closure for @e cb. - */ - void *cb_cls; - - /** - * Message queue for sending from @a p1 to @a p2. - */ - struct GNUNET_MQ_Handle *mq; - - /** - * Set if peer1 says the connection is up to peer2. - */ - int p1_c; - - /** - * Set if peer2 says the connection is up to peer1. - */ - int p2_c; - - /** - * #GNUNET_YES if both @e p1_c and @e p2_c are #GNUNET_YES. - */ - int connected; -}; - - -/** - * Handle for a test run. - */ -struct GNUNET_TRANSPORT_TESTING_Handle -{ - /** - * Testing library system handle - */ - struct GNUNET_TESTING_System *tl_system; - - /** - * head DLL of connect contexts - */ - struct GNUNET_TRANSPORT_TESTING_ConnectRequest *cc_head; - - /** - * head DLL of connect contexts - */ - struct GNUNET_TRANSPORT_TESTING_ConnectRequest *cc_tail; - - /** - * head DLL of peers - */ - struct GNUNET_TRANSPORT_TESTING_PeerContext *p_head; - - /** - * tail DLL of peers - */ - struct GNUNET_TRANSPORT_TESTING_PeerContext *p_tail; -}; - - -/** - * Initialize the transport testing - * - * @return transport testing handle - */ -struct GNUNET_TRANSPORT_TESTING_Handle * -GNUNET_TRANSPORT_TESTING_init (void); - - -/** - * Clean up the transport testing - * - * @param tth transport testing handle - */ -void -GNUNET_TRANSPORT_TESTING_done (struct GNUNET_TRANSPORT_TESTING_Handle *tth); - - -/** - * Start a peer with the given configuration - * - * @param tth the testing handle - * @param cfgname configuration file - * @param peer_id an identification number for the peer - * @param handlers functions for receiving messages - * @param nc connect callback - * @param nd disconnect callback - * @param cb_cls closure for @a nc and @a nd callback - * @param start_cb start callback - * @param start_cb_cls closure for @a start_cb - * @return the peer context - */ -struct GNUNET_TRANSPORT_TESTING_PeerContext * -GNUNET_TRANSPORT_TESTING_start_peer ( - struct GNUNET_TRANSPORT_TESTING_Handle *tth, - const char *cfgname, - int peer_id, - const struct GNUNET_MQ_MessageHandler *handlers, - GNUNET_TRANSPORT_NotifyConnect nc, - GNUNET_TRANSPORT_NotifyDisconnect nd, - void *cb_cls, - GNUNET_SCHEDULER_TaskCallback start_cb, - void *start_cb_cls); - - -/** - * Shutdown the given peer - * - * @param p the peer - */ -void -GNUNET_TRANSPORT_TESTING_stop_peer ( - struct GNUNET_TRANSPORT_TESTING_PeerContext *pc); - - -/** - * Stops and restarts the given peer, sleeping (!) for 5s in between. - * - * @param p the peer - * @param restart_cb restart callback - * @param restart_cb_cls callback closure - * @return #GNUNET_OK in success otherwise #GNUNET_SYSERR - */ -int -GNUNET_TRANSPORT_TESTING_restart_peer ( - struct GNUNET_TRANSPORT_TESTING_PeerContext *p, - GNUNET_SCHEDULER_TaskCallback restart_cb, - void *restart_cb_cls); - - -/** - * Connect the given peers and call the callback when both peers - * report the inbound connection. Remarks: start_peer's notify_connect - * callback can be called before. - * - * @param p1 peer 1 - * @param p2 peer 2 - * @param cb the callback to call when both peers notified that they are - * connected - * @param cls callback cls - * @return a connect request handle - */ -struct GNUNET_TRANSPORT_TESTING_ConnectRequest * -GNUNET_TRANSPORT_TESTING_connect_peers ( - struct GNUNET_TRANSPORT_TESTING_PeerContext *p1, - struct GNUNET_TRANSPORT_TESTING_PeerContext *p2, - GNUNET_SCHEDULER_TaskCallback cb, - void *cls); - - -/** - * Cancel the request to connect two peers. You MUST cancel the - * request if you stop the peers before the peers connected - * successfully. - * - * @param cc a connect request handle - */ -void -GNUNET_TRANSPORT_TESTING_connect_peers_cancel ( - struct GNUNET_TRANSPORT_TESTING_ConnectRequest *cc); - - -/** - * Function called on matching connect requests. - * - * @param cls closure - * @param cc request matching the query - */ -typedef void (*GNUNET_TRANSPORT_TESTING_ConnectContextCallback) ( - void *cls, - struct GNUNET_TRANSPORT_TESTING_ConnectRequest *cc); - - -/** - * Find any connecting context matching the given pair of peers. - * - * @param p1 first peer - * @param p2 second peer - * @param cb function to call - * @param cb_cls closure for @a cb - */ -void -GNUNET_TRANSPORT_TESTING_find_connecting_context ( - struct GNUNET_TRANSPORT_TESTING_PeerContext *p1, - struct GNUNET_TRANSPORT_TESTING_PeerContext *p2, - GNUNET_TRANSPORT_TESTING_ConnectContextCallback cb, - void *cb_cls); - - -/* ********************** high-level process functions *************** */ - - -/** - * Function called once the peers have been launched and - * connected by #GNUNET_TRANSPORT_TESTING_connect_check(). - * - * @param cls closure - * @param num_peers size of the @a p array - * @param p the peers that were launched - */ -typedef void (*GNUNET_TRANSPORT_TESTING_ConnectContinuation) ( - void *cls, - unsigned int num_peers, - struct GNUNET_TRANSPORT_TESTING_PeerContext *p[]); - - -/** - * Internal data structure. - */ -struct GNUNET_TRANSPORT_TESTING_ConnectRequestList; - -/** - * Internal data structure. - */ -struct GNUNET_TRANSPORT_TESTING_InternalPeerContext; - - -GNUNET_NETWORK_STRUCT_BEGIN -struct GNUNET_TRANSPORT_TESTING_TestMessage -{ - /** - * Type is (usually) #GNUNET_TRANSPORT_TESTING_SIMPLE_MTYPE. - */ - struct GNUNET_MessageHeader header; - - /** - * Monotonically increasing counter throughout the test. - */ - uint32_t num GNUNET_PACKED; -}; - -struct GNUNET_TRANSPORT_TESTING_PerformanceTestMessage -{ - /** - * Type is (usually) #GNUNET_TRANSPORT_TESTING_SIMPLE_PERFORMANCE_MTYPE. - */ - struct GNUNET_MessageHeader header; - - /** - * Time this message was send via transport api. - */ - struct GNUNET_TIME_AbsoluteNBO time_send; - - /** - * Monotonically increasing counter throughout the test. - */ - uint32_t num GNUNET_PACKED; -}; - -GNUNET_NETWORK_STRUCT_END - - -/** - * Function called by the transport for each received message. - * - * @param cls closure - * @param receiver receiver of the message - * @param sender sender of the message - * @param message the message - */ -typedef void (*GNUNET_TRANSPORT_TESTING_ReceiveCallback) ( - void *cls, - struct GNUNET_TRANSPORT_TESTING_PeerContext *receiver, - const struct GNUNET_PeerIdentity *sender, - const struct GNUNET_TRANSPORT_TESTING_TestMessage *message); - - -/** - * Function called to notify transport users that another - * peer connected to us. - * - * @param cls closure - * @param me peer experiencing the event - * @param other peer that connected to @a me - */ -typedef void (*GNUNET_TRANSPORT_TESTING_NotifyConnect) ( - void *cls, - struct GNUNET_TRANSPORT_TESTING_PeerContext *me, - const struct GNUNET_PeerIdentity *other); - - -/** - * Function called to notify transport users that another - * peer disconnected from us. - * - * @param cls closure - * @param me peer experiencing the event - * @param other peer that disconnected from @a me - */ -typedef void (*GNUNET_TRANSPORT_TESTING_NotifyDisconnect) ( - void *cls, - struct GNUNET_TRANSPORT_TESTING_PeerContext *me, - const struct GNUNET_PeerIdentity *other); - - -/** - * Closure that must be passed to - * #GNUNET_TRANSPORT_TESTING_connect_check. - */ -struct GNUNET_TRANSPORT_TESTING_ConnectCheckContext -{ - /** - * How should we continue after the connect? - */ - GNUNET_SCHEDULER_TaskCallback connect_continuation; - - /** - * Closure for @e connect_continuation. - */ - void *connect_continuation_cls; - - /** - * Which configuration file should we pass to the - * #GNUNET_PROGRAM_run() of the testcase? - */ - const char *config_file; - - /** - * Receiver argument to give for peers we start. - */ - GNUNET_TRANSPORT_TESTING_ReceiveCallback rec; - - /** - * Notify connect argument to give for peers we start. - */ - GNUNET_TRANSPORT_TESTING_NotifyConnect nc; - - /** - * Notify disconnect argument to give for peers we start. - */ - GNUNET_TRANSPORT_TESTING_NotifyDisconnect nd; - - /** - * Closure for @e rec, @e nc and @e nd. - */ - void *cls; - - /** - * Custom task to run on shutdown. - */ - GNUNET_SCHEDULER_TaskCallback shutdown_task; - - /** - * Closure for @e shutdown_task. - */ - void *shutdown_task_cls; - - /** - * Custom task to run after peers were started but before we try to - * connect them. If this function is set, we wait ONE second after - * running this function until we continue with connecting the - * peers. - */ - GNUNET_SCHEDULER_TaskCallback pre_connect_task; - - /** - * Closure for @e shutdown_task. - */ - void *pre_connect_task_cls; - - /** - * When should the testcase time out? - */ - struct GNUNET_TIME_Relative timeout; - - /** - * Should we try to create connections in both directions? - */ - int bi_directional; - - /* ******* fields set by #GNUNET_TRANSPORT_TESTING_connect_check **** */ - - /** - * Number of peers involved in the test. - */ - unsigned int num_peers; - - /** - * Configuration files we have, array with @e num_peers entries. - */ - char **cfg_files; - - /** - * Array with @e num_peers entries. - */ - struct GNUNET_TRANSPORT_TESTING_PeerContext **p; - - /** - * Name of the plugin. - */ - const char *test_plugin; - - /** - * Name of the testcase. - */ - const char *test_name; - - /** - * Configuration object for the testcase. - */ - const struct GNUNET_CONFIGURATION_Handle *cfg; - - /** - * Main testing handle. - */ - struct GNUNET_TRANSPORT_TESTING_Handle *tth; - - /** - * Result from the main function, set to #GNUNET_OK on success. - * Clients should set to #GNUNET_SYSERR to indicate test failure. - */ - int global_ret; - - /** - * Generator for the `num` field in test messages. Incremented each - * time #GNUNET_TRANSPORT_TESTING_simple_send or - * #GNUNET_TRANSPORT_TESTING_large_send are used to transmit a - * message. - */ - uint32_t send_num_gen; - - /* ******* internal state, clients should not mess with this **** */ - - /** - * Task run on timeout. - */ - struct GNUNET_SCHEDULER_Task *timeout_task; - - /** - * Task run to connect peers. - */ - struct GNUNET_SCHEDULER_Task *connect_task; - - /** - * Number of peers that have been started. - */ - unsigned int started; - - /** - * DLL of active connect requests. - */ - struct GNUNET_TRANSPORT_TESTING_ConnectRequestList *crl_head; - - /** - * DLL of active connect requests. - */ - struct GNUNET_TRANSPORT_TESTING_ConnectRequestList *crl_tail; - - /** - * Array with @e num_peers entries. - */ - struct GNUNET_TRANSPORT_TESTING_InternalPeerContext *ip; -}; - - -/** - * Find peer by peer ID. - * - * @param ccc context to search - * @param peer peer to look for - * @return NULL if @a peer was not found - */ -struct GNUNET_TRANSPORT_TESTING_PeerContext * -GNUNET_TRANSPORT_TESTING_find_peer ( - struct GNUNET_TRANSPORT_TESTING_ConnectCheckContext *ccc, - const struct GNUNET_PeerIdentity *peer); - - -/** - * Common implementation of the #GNUNET_TRANSPORT_TESTING_CheckCallback. - * Starts and connects the two peers, then invokes the - * `connect_continuation` from @a cls. Sets up a timeout to - * abort the test, and a shutdown handler to clean up properly - * on exit. - * - * @param cls closure of type `struct - * GNUNET_TRANSPORT_TESTING_ConnectCheckContext` - * @param tth_ initialized testing handle - * @param test_plugin_ name of the plugin - * @param test_name_ name of the test - * @param num_peers number of entries in the @a cfg_file array - * @param cfg_files array of names of configuration files for the peers - * @return #GNUNET_SYSERR on error - */ -int -GNUNET_TRANSPORT_TESTING_connect_check ( - void *cls, - struct GNUNET_TRANSPORT_TESTING_Handle *tth_, - const char *test_plugin_, - const char *test_name_, - unsigned int num_peers, - char *cfg_files[]); - - -/** - * Main function of a testcase. Called with the initial setup data - * for the test as derived from the source name and the binary name. - * - * @param cls closure - * @param tth_ initialized testing handle - * @param test_plugin_ name of the plugin - * @param test_name_ name of the test - * @param num_peers number of entries in the @a cfg_file array - * @param cfg_files array of names of configuration files for the peers - * @return #GNUNET_SYSERR on error - */ -typedef int (*GNUNET_TRANSPORT_TESTING_CheckCallback) ( - void *cls, - struct GNUNET_TRANSPORT_TESTING_Handle *tth_, - const char *test_plugin_, - const char *test_name_, - unsigned int num_peers, - char *cfg_files[]); - - -/** - * Setup testcase. Calls @a check with the data the test needs. - * - * @param argv0 binary name (argv[0]) - * @param filename source file name (__FILE__) - * @param num_peers number of peers to start - * @param check main function to run - * @param check_cls closure for @a check - * @return #GNUNET_OK on success - */ -int -GNUNET_TRANSPORT_TESTING_main_ (const char *argv0, - const char *filename, - unsigned int num_peers, - GNUNET_TRANSPORT_TESTING_CheckCallback check, - void *check_cls); - - -/** - * Setup testcase. Calls @a check with the data the test needs. - * - * @param num_peers number of peers to start - * @param check main function to run - * @param check_cls closure for @a check - * @return #GNUNET_OK on success - */ -#define GNUNET_TRANSPORT_TESTING_main(num_peers, check, check_cls) \ - GNUNET_TRANSPORT_TESTING_main_ (argv[0], \ - __FILE__, \ - num_peers, \ - check, \ - check_cls) - -/* ***************** Convenience functions for sending ********* */ - -/** - * Send a test message of type @a mtype and size @a msize from - * peer @a sender to peer @a receiver. The peers should be - * connected when this function is called. - * - * @param sender the sending peer - * @param receiver the receiving peer - * @param mtype message type to use - * @param msize size of the message, at least `sizeof (struct - * GNUNET_TRANSPORT_TESTING_TestMessage)` - * @param num unique message number - * @param cont continuation to call after transmission - * @param cont_cls closure for @a cont - * @return #GNUNET_OK if message was queued, - * #GNUNET_NO if peers are not connected - * #GNUNET_SYSERR if @a msize is illegal - */ -int -GNUNET_TRANSPORT_TESTING_send ( - struct GNUNET_TRANSPORT_TESTING_PeerContext *sender, - struct GNUNET_TRANSPORT_TESTING_PeerContext *receiver, - uint16_t mtype, - uint16_t msize, - uint32_t num, - GNUNET_SCHEDULER_TaskCallback cont, - void *cont_cls); - - -/** - * Message type used by #GNUNET_TRANSPORT_TESTING_simple_send(). - */ -#define GNUNET_TRANSPORT_TESTING_SIMPLE_MTYPE 12345 - -/** - * Alternative message type for tests. - */ -#define GNUNET_TRANSPORT_TESTING_SIMPLE_MTYPE2 12346 - -/** - * Message type used by #(). - */ -#define GNUNET_TRANSPORT_TESTING_SIMPLE_PERFORMANCE_MTYPE 12347 - -/** - * Type of the closure argument to pass to - * #GNUNET_TRANSPORT_TESTING_simple_send() and - * #GNUNET_TRANSPORT_TESTING_large_send(). - */ -struct GNUNET_TRANSPORT_TESTING_SendClosure -{ - /** - * Context for the transmission. - */ - struct GNUNET_TRANSPORT_TESTING_ConnectCheckContext *ccc; - - /** - * Function that returns the desired message size. Overrides - * the message size, can be NULL in which case the message - * size is the default. - */ - size_t (*get_size_cb) (unsigned int n); - - /** - * Number of messages to be transmitted in a loop. - * Use zero for "forever" (until external shutdown). - */ - unsigned int num_messages; - - /** - * Function to call after all transmissions, can be NULL. - */ - GNUNET_SCHEDULER_TaskCallback cont; - - /** - * Closure for @e cont. - */ - void *cont_cls; -}; - - -/** - * Task that sends a minimalistic test message from the - * first peer to the second peer. - * - * @param cls the `struct GNUNET_TRANSPORT_TESTING_SendClosure` - * which should contain at least two peers, the first two - * of which should be currently connected - */ -void -GNUNET_TRANSPORT_TESTING_simple_send (void *cls); - -/** - * Size of a message sent with - * #GNUNET_TRANSPORT_TESTING_large_send(). Big enough - * to usually force defragmentation. - */ -#define GNUNET_TRANSPORT_TESTING_LARGE_MESSAGE_SIZE 2600 - -/** - * Task that sends a large test message from the - * first peer to the second peer. - * - * @param cls the `struct GNUNET_TRANSPORT_TESTING_SendClosure` - * which should contain at least two peers, the first two - * of which should be currently connected - */ -void -GNUNET_TRANSPORT_TESTING_large_send (void *cls); - - -/* ********************** log-only convenience functions ************* */ - - -/** - * Log a connect event. - * - * @param cls NULL - * @param me peer that had the event - * @param other peer that connected. - */ -void -GNUNET_TRANSPORT_TESTING_log_connect ( - void *cls, - struct GNUNET_TRANSPORT_TESTING_PeerContext *me, - const struct GNUNET_PeerIdentity *other); - - -/** - * Log a disconnect event. - * - * @param cls NULL - * @param me peer that had the event - * @param other peer that disconnected. - */ -void -GNUNET_TRANSPORT_TESTING_log_disconnect ( - void *cls, - struct GNUNET_TRANSPORT_TESTING_PeerContext *me, - const struct GNUNET_PeerIdentity *other); - - -/* ********************** low-level filename functions *************** */ - - -/** - * Extracts the test filename from an absolute file name and removes - * the extension. - * - * @param file absolute file name - * @return resulting test name - */ -char * -GNUNET_TRANSPORT_TESTING_get_test_name (const char *file); - - -/** - * This function takes the filename (e.g. argv[0), removes a "lt-"-prefix and - * if existing ".exe"-prefix and adds the peer-number - * - * @param file filename of the test, e.g. argv[0] - * @param count peer number - * @return configuration name to use - */ -char * -GNUNET_TRANSPORT_TESTING_get_config_name (const char *file, int count); - - -/** - * Extracts the plugin anme from an absolute file name and the test name - * @param file absolute file name - * @param test test name - * @return the plugin name - */ -char * -GNUNET_TRANSPORT_TESTING_get_test_plugin_name (const char *executable, - const char *testname); - - -/** - * Extracts the filename from an absolute file name and removes the - * extension - * - * @param file absolute file name - * @return the source name - */ -char * -GNUNET_TRANSPORT_TESTING_get_test_source_name (const char *file); - -#endif -/* end of transport_testing.h */ diff --git a/src/transport/transport.conf.in b/src/transport/transport.conf.in deleted file mode 100644 index de4855c6a..000000000 --- a/src/transport/transport.conf.in +++ /dev/null @@ -1,248 +0,0 @@ -[transport] -START_ON_DEMAND = @START_ON_DEMAND@ -@JAVAPORT@PORT = 2091 -HOSTNAME = localhost -BINARY = gnunet-service-transport -# PREFIX = valgrind - -# Maximum number of neighbours PER PLUGIN (not in total). -NEIGHBOUR_LIMIT = 50 -ACCEPT_FROM = 127.0.0.1; -ACCEPT_FROM6 = ::1; -# TCP is the only transport plugin known to work "reliably" -PLUGINS = tcp -UNIXPATH = $GNUNET_RUNTIME_DIR/gnunet-service-transport.sock -BLACKLIST_FILE = $GNUNET_CONFIG_HOME/transport/blacklist -UNIX_MATCH_UID = NO -UNIX_MATCH_GID = YES -# DISABLE_SOCKET_FORWARDING = NO -# USERNAME = -# MAXBUF = -# TIMEOUT = -# DISABLEV6 = -# BINDTO = -# REJECT_FROM = -# REJECT_FROM6 = -# PREFIX = valgrind --leak-check=full - -# Configuration settings related to traffic manipulation for testing purposes -# Distance -# MANIPULATE_DISTANCE_IN = 1 -# MANIPULATE_DISTANCE_OUT = 1 -# Delay; WARNING: to large values may lead to peers not connecting! -# MANIPULATE_DELAY_IN = 1 ms -# MANIPULATE_DELAY_OUT = 1 ms - - -[transport-unix] -UNIXPATH = $GNUNET_RUNTIME_DIR/gnunet-transport-plugin-unix.sock -TESTING_IGNORE_KEYS = ACCEPT_FROM; - -[transport-tcp] -# Use 0 to ONLY advertise as a peer behind NAT (no port binding) -PORT = 2086 - -# Obsolete option, to be replaced by HOLE_EXTERNAL (soon) -ADVERTISED_PORT = 2086 - -# If we have a manually punched NAT, what is the external IP and port? -# Can use DNS names for DynDNS-based detection of external IP. -# Can use IPv6 addresses ([fefc::]:PORT). -# Use "AUTO" for the hostname to automatically detect external IP. -# Do not set if NAT is not manually punched. -# HOLE_EXTERNAL = AUTO:2086 - -TESTING_IGNORE_KEYS = ACCEPT_FROM; - -# Maximum number of open TCP connections allowed -MAX_CONNECTIONS = 128 - -TIMEOUT = 5 s -# ACCEPT_FROM = -# ACCEPT_FROM6 = -# REJECT_FROM = -# REJECT_FROM6 = -# BINDTO = -MAX_CONNECTIONS = 128 - -# Enable TCP stealth? -TCP_STEALTH = NO - - - -[transport-xt] -# Use 0 to ONLY advertise as a peer behind NAT (no port binding) -PORT = 2087 - -# Obsolete option, to be replaced by HOLE_EXTERNAL (soon) -ADVERTISED_PORT = 2087 - -# If we have a manually punched NAT, what is the external IP and port? -# Can use DNS names for DynDNS-based detection of external IP. -# Can use IPv6 addresses ([fefc::]:PORT). -# Use "AUTO" for the hostname to automatically detect external IP. -# Do not set if NAT is not manually punched. -# HOLE_EXTERNAL = AUTO:2087 - -TESTING_IGNORE_KEYS = ACCEPT_FROM; - -# Maximum number of open TCP connections allowed -MAX_CONNECTIONS = 128 - -TIMEOUT = 5 s -# ACCEPT_FROM = -# ACCEPT_FROM6 = -# REJECT_FROM = -# REJECT_FROM6 = -# BINDTO = -MAX_CONNECTIONS = 128 - -# Enable TCP stealth? -TCP_STEALTH = NO - - -[transport-udp] -# Use PORT = 0 to autodetect a port available -PORT = 2086 -BROADCAST = YES -BROADCAST_RECEIVE = YES -BROADCAST_INTERVAL = 30 s - -# This limits UDP to 1MB/s for SENDING. Higher values are advised -# for benchmarking or well-connected systems. Note that this quota -# applies IN ADDITION to the system-wide transport-wide WAN/LAN -# quotas. -MAX_BPS = 1000000 -TESTING_IGNORE_KEYS = ACCEPT_FROM; - -# If we have a manually punched NAT, what is the external IP and port? -# Can use DNS names for DynDNS-based detection of external IP. -# Can use IPv6 addresses ([fefc::]:PORT). -# Use "AUTO" for the hostname to automatically detect external IP. -# Do not set if NAT is not manually punched. -# HOLE_EXTERNAL = AUTO:2086 - - -[transport-xu] -# Use PORT = 0 to autodetect a port available -PORT = 2087 - - -[transport-http_client] -MAX_CONNECTIONS = 128 -TESTING_IGNORE_KEYS = ACCEPT_FROM; -# Hostname or IP of proxy server -# PROXY = - -# User name for proxy server -# PROXY_USERNAME = -# User password for proxy server -# PROXY_PASSWORD = - -# Type of proxy server, -# Valid values: HTTP, SOCKS4, SOCKS5, SOCKS4A, SOCKS5_HOSTNAME -# Default: HTTP -# PROXY_TYPE = HTTP - -# Enable tunneling proxy request instead of having proxy server evaluate it -# Experimental, default: NO -# PROXY_HTTP_TUNNELING = NO - - -[transport-http_server] -#EXTERNAL_HOSTNAME = -PORT = 1080 - -# Obsolete option, to be replaced by HOLE_EXTERNAL (soon) -ADVERTISED_PORT = 1080 - -# If we have a manually punched NAT, what is the external IP and port? -# Can use DNS names for DynDNS-based detection of external IP. -# Can use IPv6 addresses ([fefc::]:PORT). -# Use "AUTO" for the hostname to automatically detect external IP. -# Do not set if NAT is not manually punched. -# HOLE_EXTERNAL = AUTO:1080 - -MAX_CONNECTIONS = 128 -TESTING_IGNORE_KEYS = ACCEPT_FROM; - -# Enable TCP stealth? -TCP_STEALTH = NO - - -[transport-https_client] -MAX_CONNECTIONS = 128 -TESTING_IGNORE_KEYS = ACCEPT_FROM; -# Hostname or IP of proxy server -# PROXY = - -# User name for proxy server -# PROXY_USERNAME = -# User password for proxy server -# PROXY_PASSWORD = - -# Type of proxy server, -# Valid values: HTTP, SOCKS4, SOCKS5, SOCKS4A, SOCKS5_HOSTNAME -# Default: HTTP -# PROXY_TYPE = HTTP - -# Enable tunneling proxy request instead of having proxy server evaluate it -# Experimental, default: NO -# PROXY_HTTP_TUNNELING = NO - - -[transport-https_server] -# EXTERNAL_HOSTNAME = -# EXTERNAL_HOSTNAME_ONLY = YES -# If you have a valid SSL certificate for your external hostname tell, -# clients to verify it -# VERIFY_EXTERNAL_HOSTNAME = YES -# Does the external hostname use the same port? -# EXTERNAL_HOSTNAME_USE_PORT = YES -PORT = 4433 - -# Obsolete option, to be replaced by HOLE_EXTERNAL (soon) -ADVERTISED_PORT = 4433 - -# If we have a manually punched NAT, what is the external IP and port? -# Can use DNS names for DynDNS-based detection of external IP. -# Can use IPv6 addresses ([fefc::]:PORT). -# Use "AUTO" for the hostname to automatically detect external IP. -# Do not set if NAT is not manually punched. -# HOLE_EXTERNAL = AUTO:4433 - -CRYPTO_INIT = NORMAL -KEY_FILE = $GNUNET_DATA_HOME/transport/https.key -CERT_FILE = $GNUNET_DATA_HOME/transport/https.cert -MAX_CONNECTIONS = 128 -TESTING_IGNORE_KEYS = ACCEPT_FROM; - -# Enable TCP stealth? -TCP_STEALTH = NO - - -[transport-wlan] -# Name of the interface in monitor mode (typically monX) -INTERFACE = mon0 -# Real hardware, no testing -TESTMODE = 0 -TESTING_IGNORE_KEYS = ACCEPT_FROM; - - -[transport-bluetooth] -# Name of the interface (typically hciX) -INTERFACE = hci0 -# Real hardware, no testing -TESTMODE = 0 -TESTING_IGNORE_KEYS = ACCEPT_FROM; - -[communicator-tcp] -#PREFIX = xterm -geometry 100x85 -T peer1 -e gdb --args -#PREFIX = valgrind --leak-check=full --track-origins=yes -BINDTO = 2086 -DISABLE_V6 = NO -BINARY = gnunet-communicator-tcp -IMMEDIATE_START = YES - -[communicator-quic] -BINDTO = 127.0.0.1 diff --git a/src/transport/transport.h b/src/transport/transport.h deleted file mode 100644 index 66f17ee5b..000000000 --- a/src/transport/transport.h +++ /dev/null @@ -1,828 +0,0 @@ -/* - This file is part of GNUnet. - Copyright (C) 2009-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 . - - SPDX-License-Identifier: AGPL3.0-or-later - */ - -/** - * @file transport/transport.h - * @brief common internal definitions for transport service - * @author Christian Grothoff - */ -#ifndef TRANSPORT_H -#define TRANSPORT_H - -#include "gnunet_util_lib.h" -#include "gnunet_time_lib.h" -#include "gnunet_constants.h" - -#define DEBUG_TRANSPORT GNUNET_EXTRA_LOGGING - - -/** - * Similar to GNUNET_TRANSPORT_NotifyDisconnect but in and out quotas are - * included here. These values are not required outside transport_api - * - * @param cls closure - * @param peer the peer that connected - * @param bandwidth_in inbound bandwidth in NBO - * @param bandwidth_out outbound bandwidth in NBO - * - */ -typedef void (*NotifyConnect) ( - void *cls, - const struct GNUNET_PeerIdentity *peer, - struct GNUNET_BANDWIDTH_Value32NBO bandwidth_in, - struct GNUNET_BANDWIDTH_Value32NBO bandwidth_out); - - -GNUNET_NETWORK_STRUCT_BEGIN - - -/** - * Message from the transport service to the library - * asking to check if both processes agree about this - * peers identity. - */ -struct StartMessage -{ - /** - * Type will be #GNUNET_MESSAGE_TYPE_TRANSPORT_START - */ - struct GNUNET_MessageHeader header; - - /** - * 0: no options - * 1: The @e self field should be checked - * 2: this client is interested in payload traffic - */ - uint32_t options; - - /** - * Identity we think we have. If it does not match, the - * receiver should print out an error message and disconnect. - */ - struct GNUNET_PeerIdentity self; -}; - - -/** - * Message from the transport service to the library - * informing about neighbors. - */ -struct ConnectInfoMessage -{ - /** - * Type will be #GNUNET_MESSAGE_TYPE_TRANSPORT_CONNECT - */ - struct GNUNET_MessageHeader header; - -#if (defined(GNUNET_TRANSPORT_COMMUNICATION_VERSION) || \ - defined(GNUNET_TRANSPORT_CORE_VERSION)) - - /** - * Always zero, for alignment. - */ - uint32_t reserved GNUNET_PACKED; -#else - /** - * Current outbound quota for this peer - */ - struct GNUNET_BANDWIDTH_Value32NBO quota_out; -#endif - - /** - * Identity of the new neighbour. - */ - struct GNUNET_PeerIdentity id; -}; - - -/** - * Message from the transport service to the library - * informing about disconnects. - */ -struct DisconnectInfoMessage -{ - /** - * Type will be #GNUNET_MESSAGE_TYPE_TRANSPORT_DISCONNECT - */ - struct GNUNET_MessageHeader header; - - /** - * Reserved, always zero. - */ - uint32_t reserved GNUNET_PACKED; - - /** - * Who got disconnected? - */ - struct GNUNET_PeerIdentity peer; -}; - - -/** - * Message used to notify the transport API about a message - * received from the network. The actual message follows. - */ -struct InboundMessage -{ - /** - * Type will be #GNUNET_MESSAGE_TYPE_TRANSPORT_RECV - */ - struct GNUNET_MessageHeader header; - - /** - * Which peer sent the message? - */ - struct GNUNET_PeerIdentity peer; -}; - - -/** - * Message used to notify the transport API that it can - * send another message to the transport service. - */ -struct SendOkMessage -{ - /** - * Type will be #GNUNET_MESSAGE_TYPE_TRANSPORT_SEND_OK - */ - struct GNUNET_MessageHeader header; - -#if (defined(GNUNET_TRANSPORT_COMMUNICATION_VERSION) || \ - defined(GNUNET_TRANSPORT_CORE_VERSION)) - - uint32_t reserved GNUNET_PACKED; -#else - /** - * #GNUNET_OK if the transmission succeeded, - * #GNUNET_SYSERR if it failed (i.e. network disconnect); - * in either case, it is now OK for this client to - * send us another message for the given peer. - */ - uint16_t success GNUNET_PACKED; - - /** - * Size of message sent - */ - uint16_t bytes_msg GNUNET_PACKED; - - /** - * Size of message sent over wire. - * Includes plugin and protocol specific overheads. - */ - uint32_t bytes_physical GNUNET_PACKED; -#endif - - /** - * Which peer can send more now? - */ - struct GNUNET_PeerIdentity peer; -}; - - -/** - * Message used to notify the transport API that it can - * send another message to the transport service. - * (Used to implement flow control.) - */ -struct RecvOkMessage -{ - /** - * Type will be #GNUNET_MESSAGE_TYPE_TRANSPORT_RECV_OK - */ - struct GNUNET_MessageHeader header; - - /** - * Number of messages by which to increase the window, greater or - * equal to one. - */ - uint32_t increase_window_delta GNUNET_PACKED; - - /** - * Which peer can CORE handle more from now? - */ - struct GNUNET_PeerIdentity peer; -}; - - -/** - * Message used to notify the transport service about a message - * to be transmitted to another peer. The actual message follows. - */ -struct OutboundMessage -{ - /** - * Type will be #GNUNET_MESSAGE_TYPE_TRANSPORT_SEND - */ - struct GNUNET_MessageHeader header; - - /** - * An `enum GNUNET_MQ_PriorityPreferences` in NBO. - */ - uint32_t priority GNUNET_PACKED; - -#if ! (defined(GNUNET_TRANSPORT_COMMUNICATION_VERSION) || \ - defined(GNUNET_TRANSPORT_CORE_VERSION)) - - /** - * Allowed delay. - */ - struct GNUNET_TIME_RelativeNBO timeout; -#endif - - /** - * Which peer should receive the message? - */ - struct GNUNET_PeerIdentity peer; -}; - - -/* *********************** TNG messages ***************** */ - -/** - * Communicator goes online. Note which addresses it can - * work with. - */ -struct GNUNET_TRANSPORT_CommunicatorAvailableMessage -{ - /** - * Type will be #GNUNET_MESSAGE_TYPE_TRANSPORT_NEW_COMMUNICATOR. - */ - struct GNUNET_MessageHeader header; - - /** - * NBO encoding of `enum GNUNET_TRANSPORT_CommunicatorCharacteristics` - */ - uint32_t cc; - - /* Followed by the address prefix of the communicator */ -}; - - -/** - * Add address to the list. - */ -struct GNUNET_TRANSPORT_AddAddressMessage -{ - /** - * Type will be #GNUNET_MESSAGE_TYPE_TRANSPORT_ADD_ADDRESS. - */ - struct GNUNET_MessageHeader header; - - /** - * Address identifier (used during deletion). - */ - uint32_t aid GNUNET_PACKED; - - /** - * When does the address expire? - */ - struct GNUNET_TIME_RelativeNBO expiration; - - /** - * An `enum GNUNET_NetworkType` in NBO. - */ - uint32_t nt; - - /* followed by UTF-8 encoded, 0-terminated human-readable address */ -}; - - -/** - * Remove address from the list. - */ -struct GNUNET_TRANSPORT_DelAddressMessage -{ - /** - * Type will be #GNUNET_MESSAGE_TYPE_TRANSPORT_DEL_ADDRESS. - */ - struct GNUNET_MessageHeader header; - - /** - * Address identifier. - */ - uint32_t aid GNUNET_PACKED; -}; - - -/** - * Inform transport about an incoming message. - */ -struct GNUNET_TRANSPORT_IncomingMessage -{ - /** - * Type will be #GNUNET_MESSAGE_TYPE_TRANSPORT_INCOMING_MSG. - */ - struct GNUNET_MessageHeader header; - - /** - * Do we use flow control or not? - */ - uint32_t fc_on GNUNET_PACKED; - - /** - * 64-bit number to identify the matching ACK. - */ - uint64_t fc_id GNUNET_PACKED; - - /** - * How long does the communicator believe the address on which - * the message was received to remain valid? - */ - struct GNUNET_TIME_RelativeNBO expected_address_validity; - - /** - * Sender identifier. - */ - struct GNUNET_PeerIdentity sender; - - /** - * Direct neighbour sender identifier. - */ - struct GNUNET_PeerIdentity neighbour_sender; - - /* followed by the message */ -}; - - -/** - * Transport informs us about being done with an incoming message. - * (only sent if fc_on was set). - */ -struct GNUNET_TRANSPORT_IncomingMessageAck -{ - /** - * Type will be #GNUNET_MESSAGE_TYPE_TRANSPORT_INCOMING_MSG_ACK. - */ - struct GNUNET_MessageHeader header; - - /** - * Reserved (0) - */ - uint32_t reserved GNUNET_PACKED; - - /** - * Which message is being ACKed? - */ - uint64_t fc_id GNUNET_PACKED; - - /** - * Sender identifier of the original message. - */ - struct GNUNET_PeerIdentity sender; -}; - - -/** - * Add queue to the transport - */ -struct GNUNET_TRANSPORT_AddQueueMessage -{ - /** - * Type will be #GNUNET_MESSAGE_TYPE_TRANSPORT_QUEUE_SETUP. - */ - struct GNUNET_MessageHeader header; - - /** - * Queue identifier (used to identify the queue). - */ - uint32_t qid GNUNET_PACKED; - - /** - * Receiver that can be addressed via the queue. - */ - struct GNUNET_PeerIdentity receiver; - - /** - * An `enum GNUNET_NetworkType` in NBO. - */ - uint32_t nt; - - /** - * Maximum transmission unit, in NBO. UINT32_MAX for unlimited. - */ - uint32_t mtu; - - /** - * Queue length, in NBO. Defines how many messages may be - * send through this queue. UINT64_MAX for unlimited. - */ - uint64_t q_len; - - /** - * Priority of the queue in relation to other queues. - */ - uint32_t priority; - - /** - * An `enum GNUNET_TRANSPORT_ConnectionStatus` in NBO. - */ - uint32_t cs; - - /* followed by UTF-8 encoded, 0-terminated human-readable address */ -}; - - -/** - * Update queue - */ -struct GNUNET_TRANSPORT_UpdateQueueMessage -{ - /** - * Type will be #GNUNET_MESSAGE_TYPE_TRANSPORT_QUEUE_SETUP. - */ - struct GNUNET_MessageHeader header; - - /** - * Queue identifier (used to identify the queue). - */ - uint32_t qid GNUNET_PACKED; - - /** - * Receiver that can be addressed via the queue. - */ - struct GNUNET_PeerIdentity receiver; - - /** - * An `enum GNUNET_NetworkType` in NBO. - */ - uint32_t nt; - - /** - * Maximum transmission unit, in NBO. UINT32_MAX for unlimited. - */ - uint32_t mtu; - - /** - * Queue length, in NBO. Defines how many messages may be - * send through this queue. UINT64_MAX for unlimited. - */ - uint64_t q_len; - - /** - * Priority of the queue in relation to other queues. - */ - uint32_t priority; - - /** - * An `enum GNUNET_TRANSPORT_ConnectionStatus` in NBO. - */ - uint32_t cs; -}; - - -/** - * Remove queue, it is no longer available. - */ -struct GNUNET_TRANSPORT_DelQueueMessage -{ - /** - * Type will be #GNUNET_MESSAGE_TYPE_TRANSPORT_QUEUE_TEARDOWN. - */ - struct GNUNET_MessageHeader header; - - /** - * Address identifier. - */ - uint32_t qid GNUNET_PACKED; - - /** - * Receiver that can be addressed via the queue. - */ - struct GNUNET_PeerIdentity receiver; -}; - - -/** - * Transport tells communicator that it wants a new queue. - */ -struct GNUNET_TRANSPORT_CreateQueue -{ - /** - * Type will be #GNUNET_MESSAGE_TYPE_TRANSPORT_QUEUE_CREATE. - */ - struct GNUNET_MessageHeader header; - - /** - * Unique ID for the request. - */ - uint32_t request_id GNUNET_PACKED; - - /** - * Receiver that can be addressed via the queue. - */ - struct GNUNET_PeerIdentity receiver; - - /* followed by UTF-8 encoded, 0-terminated human-readable address */ -}; - - -/** - * Communicator tells transport how queue creation went down. - */ -struct GNUNET_TRANSPORT_CreateQueueResponse -{ - /** - * Type will be #GNUNET_MESSAGE_TYPE_TRANSPORT_QUEUE_CREATE_OK or - * #GNUNET_MESSAGE_TYPE_TRANSPORT_QUEUE_CREATE_FAIL. - */ - struct GNUNET_MessageHeader header; - - /** - * Unique ID for the request. - */ - uint32_t request_id GNUNET_PACKED; -}; - - -/** - * Inform communicator about transport's desire to send a message. - */ -struct GNUNET_TRANSPORT_SendMessageTo -{ - /** - * Type will be #GNUNET_MESSAGE_TYPE_TRANSPORT_SEND_MSG. - */ - struct GNUNET_MessageHeader header; - - /** - * Which queue should we use? - */ - uint32_t qid GNUNET_PACKED; - - /** - * Message ID, used for flow control. - */ - uint64_t mid GNUNET_PACKED; - - /** - * Receiver identifier. - */ - struct GNUNET_PeerIdentity receiver; - - /* followed by the message */ -}; - - -/** - * Inform transport that message was sent. - */ -struct GNUNET_TRANSPORT_SendMessageToAck -{ - /** - * Type will be #GNUNET_MESSAGE_TYPE_TRANSPORT_SEND_MSG_ACK. - */ - struct GNUNET_MessageHeader header; - - /** - * Success (#GNUNET_OK), failure (#GNUNET_SYSERR). - */ - uint32_t status GNUNET_PACKED; - - /** - * Message ID of the original message. - */ - uint64_t mid GNUNET_PACKED; - - /** - * Queue ID for the queue which was used to send the message. - */ - uint32_t qid GNUNET_PACKED; - - /** - * Receiver identifier. - */ - struct GNUNET_PeerIdentity receiver; -}; - - -/** - * Message from communicator to transport service asking for - * transmission of a backchannel message with the given peer @e pid - * and communicator. - */ -struct GNUNET_TRANSPORT_CommunicatorBackchannel -{ - /** - * Type will be #GNUNET_MESSAGE_TYPE_TRANSPORT_COMMUNICATOR_BACKCHANNEL - */ - struct GNUNET_MessageHeader header; - - /** - * Always zero, for alignment. - */ - uint32_t reserved; - - /** - * Target peer. - */ - struct GNUNET_PeerIdentity pid; - - /* Followed by a `struct GNUNET_MessageHeader` with the encapsulated - message to the communicator */ - - /* Followed by the 0-terminated string specifying the desired - communicator at the target (@e pid) peer */ -}; - - -/** - * Message from transport to communicator passing along a backchannel - * message from the given peer @e pid. - */ -struct GNUNET_TRANSPORT_CommunicatorBackchannelIncoming -{ - /** - * Type will be - * #GNUNET_MESSAGE_TYPE_TRANSPORT_COMMUNICATOR_BACKCHANNEL_INCOMING - */ - struct GNUNET_MessageHeader header; - - /** - * Always zero, for alignment. - */ - uint32_t reserved; - - /** - * Origin peer. - */ - struct GNUNET_PeerIdentity pid; - - /* Followed by a `struct GNUNET_MessageHeader` with the encapsulated - message to the communicator */ -}; - - -/** - * Request to start monitoring. - */ -struct GNUNET_TRANSPORT_MonitorStart -{ - /** - * Type will be #GNUNET_MESSAGE_TYPE_TRANSPORT_MONITOR_START. - */ - struct GNUNET_MessageHeader header; - - /** - * #GNUNET_YES for one-shot montoring, #GNUNET_NO for continuous monitoring. - */ - uint32_t one_shot; - - /** - * Target identifier to monitor, all zeros for "all peers". - */ - struct GNUNET_PeerIdentity peer; -}; - - -/** - * Monitoring data. - */ -struct GNUNET_TRANSPORT_MonitorData -{ - /** - * Type will be #GNUNET_MESSAGE_TYPE_TRANSPORT_MONITOR_DATA. - */ - struct GNUNET_MessageHeader header; - - /** - * Network type (an `enum GNUNET_NetworkType` in NBO). - */ - uint32_t nt GNUNET_PACKED; - - /** - * Target identifier. - */ - struct GNUNET_PeerIdentity peer; - - /** - * @deprecated To be discussed if we keep these... - */ - struct GNUNET_TIME_AbsoluteNBO last_validation; - struct GNUNET_TIME_AbsoluteNBO valid_until; - struct GNUNET_TIME_AbsoluteNBO next_validation; - - /** - * Current round-trip time estimate. - */ - struct GNUNET_TIME_RelativeNBO rtt; - - /** - * Connection status (in NBO). - */ - uint32_t cs GNUNET_PACKED; - - /** - * Messages pending (in NBO). - */ - uint32_t num_msg_pending GNUNET_PACKED; - - /** - * Bytes pending (in NBO). - */ - uint32_t num_bytes_pending GNUNET_PACKED; - - /* Followed by 0-terminated address of the peer */ -}; - - -/** - * Request to verify address. - */ -struct GNUNET_TRANSPORT_AddressToVerify -{ - /** - * Type will be #GNUNET_MESSAGE_TYPE_TRANSPORT_ADDRESS_CONSIDER_VERIFY. - */ - struct GNUNET_MessageHeader header; - - /** - * Reserved. 0. - */ - uint32_t reserved; - - /** - * Peer the address is from. - */ - struct GNUNET_PeerIdentity peer; - - /* followed by variable-size raw address */ -}; - - -/** - * Application client to TRANSPORT service: we would like to have - * address suggestions for this peer. - */ -struct ExpressPreferenceMessage -{ - /** - * Type is #GNUNET_MESSAGE_TYPE_TRANSPORT_SUGGEST or - * #GNUNET_MESSAGE_TYPE_TRANSPORT_SUGGEST_CANCEL to stop - * suggestions. - */ - struct GNUNET_MessageHeader header; - - /** - * What type of performance preference does the client have? - * A `enum GNUNET_MQ_PreferenceKind` in NBO. - */ - uint32_t pk GNUNET_PACKED; - - /** - * Peer to get address suggestions for. - */ - struct GNUNET_PeerIdentity peer; - - /** - * How much bandwidth in bytes/second does the application expect? - */ - struct GNUNET_BANDWIDTH_Value32NBO bw; -}; - - -/** - * We got an address of another peer, TRANSPORT service - * should validate it. There is no response. - */ -struct RequestHelloValidationMessage -{ - /** - * Type is #GNUNET_MESSAGE_TYPE_TRANSPORT_REQUEST_HELLO_VALIDATION. - */ - struct GNUNET_MessageHeader header; - - /** - * What type of network does the other peer claim this is? - * A `enum GNUNET_NetworkType` in NBO. - */ - uint32_t nt GNUNET_PACKED; - - /** - * Peer to the address is presumably for. - */ - struct GNUNET_PeerIdentity peer; - - /* followed by 0-terminated address to validate */ -}; - -GNUNET_NETWORK_STRUCT_END - -/* end of transport.h */ -#endif diff --git a/src/transport/transport_api2_application.c b/src/transport/transport_api2_application.c deleted file mode 100644 index 00f5f62eb..000000000 --- a/src/transport/transport_api2_application.c +++ /dev/null @@ -1,397 +0,0 @@ -/* - This file is part of GNUnet. - Copyright (C) 2010--2019 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 . - - SPDX-License-Identifier: AGPL3.0-or-later - */ -/** - * @file transport/transport_api2_application.c - * @brief enable clients to ask TRANSPORT about establishing connections to peers - * @author Christian Grothoff - * @author Matthias Wachs - */ -#include "platform.h" -#include "gnunet_transport_application_service.h" -#include "gnunet_transport_core_service.h" -#include "transport.h" - - -#define LOG(kind, ...) \ - GNUNET_log_from (kind, "transport-application-api", __VA_ARGS__) - - -/** - * Handle for TRANSPORT address suggestion requests. - */ -struct GNUNET_TRANSPORT_ApplicationSuggestHandle -{ - /** - * ID of the peer for which address suggestion was requested. - */ - struct GNUNET_PeerIdentity id; - - /** - * Connecitivity handle this suggestion handle belongs to. - */ - struct GNUNET_TRANSPORT_ApplicationHandle *ch; - - /** - * What preference is being expressed? - */ - enum GNUNET_MQ_PriorityPreferences pk; - - /** - * How much bandwidth does the client expect? - */ - struct GNUNET_BANDWIDTH_Value32NBO bw; -}; - - -/** - * Handle to the TRANSPORT subsystem for application management. - */ -struct GNUNET_TRANSPORT_ApplicationHandle -{ - /** - * Our configuration. - */ - const struct GNUNET_CONFIGURATION_Handle *cfg; - - /** - * Map with the identities of all the peers for which we would - * like to have address suggestions. The key is the PID, the - * value is currently the `struct GNUNET_TRANSPORT_ApplicationSuggestHandle` - */ - struct GNUNET_CONTAINER_MultiPeerMap *sug_requests; - - /** - * Message queue for sending requests to the TRANSPORT service. - */ - struct GNUNET_MQ_Handle *mq; - - /** - * Task to trigger reconnect. - */ - struct GNUNET_SCHEDULER_Task *task; - - /** - * Reconnect backoff delay. - */ - struct GNUNET_TIME_Relative backoff; -}; - - -/** - * Re-establish the connection to the TRANSPORT service. - * - * @param ch handle to use to re-connect. - */ -static void -reconnect (struct GNUNET_TRANSPORT_ApplicationHandle *ch); - - -/** - * Re-establish the connection to the TRANSPORT service. - * - * @param cls handle to use to re-connect. - */ -static void -reconnect_task (void *cls) -{ - struct GNUNET_TRANSPORT_ApplicationHandle *ch = cls; - - ch->task = NULL; - reconnect (ch); -} - - -/** - * Disconnect from TRANSPORT and then reconnect. - * - * @param ch our handle - */ -static void -force_reconnect (struct GNUNET_TRANSPORT_ApplicationHandle *ch) -{ - if (NULL != ch->mq) - { - GNUNET_MQ_destroy (ch->mq); - ch->mq = NULL; - } - ch->backoff = GNUNET_TIME_STD_BACKOFF (ch->backoff); - ch->task = GNUNET_SCHEDULER_add_delayed (ch->backoff, &reconnect_task, ch); -} - - -/** - * We encountered an error handling the MQ to the - * TRANSPORT service. Reconnect. - * - * @param cls the `struct GNUNET_TRANSPORT_ApplicationHandle` - * @param error details about the error - */ -static void -error_handler (void *cls, enum GNUNET_MQ_Error error) -{ - struct GNUNET_TRANSPORT_ApplicationHandle *ch = cls; - - LOG (GNUNET_ERROR_TYPE_DEBUG, - "TRANSPORT connection died (code %d), reconnecting\n", - (int) error); - force_reconnect (ch); -} - - -/** - * Transmit request for an address suggestion. - * - * @param cls the `struct GNUNET_TRANSPORT_ApplicationHandle` - * @param peer peer to ask for an address suggestion for - * @param value the `struct GNUNET_TRANSPORT_SuggestHandle` - * @return #GNUNET_OK (continue to iterate), #GNUNET_SYSERR on - * failure (message queue no longer exists) - */ -static int -transmit_suggestion (void *cls, - const struct GNUNET_PeerIdentity *peer, - void *value) -{ - struct GNUNET_TRANSPORT_ApplicationHandle *ch = cls; - struct GNUNET_TRANSPORT_ApplicationSuggestHandle *sh = value; - struct GNUNET_MQ_Envelope *ev; - struct ExpressPreferenceMessage *m; - - if (NULL == ch->mq) - return GNUNET_SYSERR; - ev = GNUNET_MQ_msg (m, GNUNET_MESSAGE_TYPE_TRANSPORT_SUGGEST); - m->pk = htonl ((uint32_t) sh->pk); - m->bw = sh->bw; - m->peer = *peer; - GNUNET_MQ_send (ch->mq, ev); - return GNUNET_OK; -} - - -/** - * Re-establish the connection to the TRANSPORT service. - * - * @param ch handle to use to re-connect. - */ -static void -reconnect (struct GNUNET_TRANSPORT_ApplicationHandle *ch) -{ - static const struct GNUNET_MQ_MessageHandler handlers[] = { { NULL, 0, 0 } }; - - GNUNET_assert (NULL == ch->mq); - ch->mq = - GNUNET_CLIENT_connect (ch->cfg, "transport", handlers, &error_handler, ch); - if (NULL == ch->mq) - { - force_reconnect (ch); - return; - } - GNUNET_CONTAINER_multipeermap_iterate (ch->sug_requests, - &transmit_suggestion, - ch); -} - - -/** - * Initialize the TRANSPORT application suggestion client handle. - * - * @param cfg configuration to use - * @return transport application handle, NULL on error - */ -struct GNUNET_TRANSPORT_ApplicationHandle * -GNUNET_TRANSPORT_application_init ( - const struct GNUNET_CONFIGURATION_Handle *cfg) -{ - struct GNUNET_TRANSPORT_ApplicationHandle *ch; - - ch = GNUNET_new (struct GNUNET_TRANSPORT_ApplicationHandle); - ch->cfg = cfg; - ch->sug_requests = GNUNET_CONTAINER_multipeermap_create (32, GNUNET_YES); - reconnect (ch); - return ch; -} - - -/** - * Function called to free all `struct GNUNET_TRANSPORT_ApplicationSuggestHandle`s - * in the map. - * - * @param cls NULL - * @param key the key - * @param value the value to free - * @return #GNUNET_OK (continue to iterate) - */ -static int -free_sug_handle (void *cls, const struct GNUNET_PeerIdentity *key, void *value) -{ - struct GNUNET_TRANSPORT_ApplicationSuggestHandle *cur = value; - - GNUNET_free (cur); - return GNUNET_OK; -} - - -/** - * Client is done with TRANSPORT application management, release resources. - * - * @param ch handle to release - */ -void -GNUNET_TRANSPORT_application_done ( - struct GNUNET_TRANSPORT_ApplicationHandle *ch) -{ - if (NULL != ch->mq) - { - GNUNET_MQ_destroy (ch->mq); - ch->mq = NULL; - } - if (NULL != ch->task) - { - GNUNET_SCHEDULER_cancel (ch->task); - ch->task = NULL; - } - GNUNET_CONTAINER_multipeermap_iterate (ch->sug_requests, - &free_sug_handle, - NULL); - GNUNET_CONTAINER_multipeermap_destroy (ch->sug_requests); - GNUNET_free (ch); -} - - -/** - * An application would like TRANSPORT to connect to a peer. - * - * @param ch handle - * @param peer identity of the peer we need an address for - * @param pk what kind of application will the application require (can be - * #GNUNET_MQ_PRIO_BACKGROUND, we will still try to connect) - * @param bw desired bandwidth, can be zero (we will still try to connect) - * @return suggest handle, NULL if a request is already pending - */ -struct GNUNET_TRANSPORT_ApplicationSuggestHandle * -GNUNET_TRANSPORT_application_suggest ( - struct GNUNET_TRANSPORT_ApplicationHandle *ch, - const struct GNUNET_PeerIdentity *peer, - enum GNUNET_MQ_PriorityPreferences pk, - struct GNUNET_BANDWIDTH_Value32NBO bw) -{ - struct GNUNET_TRANSPORT_ApplicationSuggestHandle *s; - - s = GNUNET_new (struct GNUNET_TRANSPORT_ApplicationSuggestHandle); - s->ch = ch; - s->id = *peer; - s->pk = pk; - s->bw = bw; - (void) GNUNET_CONTAINER_multipeermap_put ( - ch->sug_requests, - &s->id, - s, - GNUNET_CONTAINER_MULTIHASHMAPOPTION_MULTIPLE); - LOG (GNUNET_ERROR_TYPE_DEBUG, - "Requesting TRANSPORT to suggest address for `%s'\n", - GNUNET_i2s (peer)); - if (NULL == ch->mq) - return s; - GNUNET_assert (GNUNET_OK == transmit_suggestion (ch, &s->id, s)); - return s; -} - - -/** - * We no longer care about being connected to a peer. - * - * @param sh handle to stop - */ -void -GNUNET_TRANSPORT_application_suggest_cancel ( - struct GNUNET_TRANSPORT_ApplicationSuggestHandle *sh) -{ - struct GNUNET_TRANSPORT_ApplicationHandle *ch = sh->ch; - struct GNUNET_MQ_Envelope *ev; - struct ExpressPreferenceMessage *m; - - LOG (GNUNET_ERROR_TYPE_DEBUG, - "Telling TRANSPORT we no longer care for an address for `%s'\n", - GNUNET_i2s (&sh->id)); - GNUNET_assert ( - GNUNET_OK == - GNUNET_CONTAINER_multipeermap_remove (ch->sug_requests, &sh->id, sh)); - if (NULL == ch->mq) - { - GNUNET_free (sh); - return; - } - ev = GNUNET_MQ_msg (m, GNUNET_MESSAGE_TYPE_TRANSPORT_SUGGEST_CANCEL); - m->pk = htonl ((uint32_t) sh->pk); - m->bw = sh->bw; - m->peer = sh->id; - GNUNET_MQ_send (ch->mq, ev); - GNUNET_free (sh); -} - - -/** - * An application (or a communicator) has received a HELLO (or other address - * data of another peer) and wants TRANSPORT to validate that the address is - * correct. The result is NOT returned, in fact TRANSPORT may do nothing - * (i.e. if it has too many active validations or recently tried this one - * already). If the @a addr validates, TRANSPORT will persist the address - * with PEERSTORE. - * - * @param ch handle - * @param peer identity of the peer we have an address for - * @param nt network type of @a addr (as claimed by the other peer); - * used by TRANSPORT to avoid trying @a addr's that really cannot work - * due to network type mismatches - * @param addr address to validate - */ -void -GNUNET_TRANSPORT_application_validate ( - struct GNUNET_TRANSPORT_ApplicationHandle *ch, - const struct GNUNET_PeerIdentity *peer, - enum GNUNET_NetworkType nt, - const char *addr) -{ - struct GNUNET_MQ_Envelope *ev; - struct RequestHelloValidationMessage *m; - size_t alen; - - if (NULL == ch->mq) - { - GNUNET_log ( - GNUNET_ERROR_TYPE_WARNING, - "Address validation for %s:%s skipped as transport is not connected\n", - GNUNET_i2s (peer), - addr); - return; - } - alen = strlen (addr) + 1; - ev = - GNUNET_MQ_msg_extra (m, - alen, - GNUNET_MESSAGE_TYPE_TRANSPORT_REQUEST_HELLO_VALIDATION); - m->peer = *peer; - m->nt = htonl ((uint32_t) nt); - memcpy (&m[1], addr, alen); - GNUNET_MQ_send (ch->mq, ev); -} - - -/* end of transport_api2_application.c */ diff --git a/src/transport/transport_api2_communication.c b/src/transport/transport_api2_communication.c deleted file mode 100644 index 0a7636843..000000000 --- a/src/transport/transport_api2_communication.c +++ /dev/null @@ -1,1114 +0,0 @@ -/* - This file is part of GNUnet. - Copyright (C) 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 . - - SPDX-License-Identifier: AGPL3.0-or-later - */ - -/** - * @file transport/transport_api2_communication.c - * @brief implementation of the gnunet_transport_communication_service.h API - * @author Christian Grothoff - */ -#include "platform.h" -#include "gnunet_util_lib.h" -#include "gnunet_protocols.h" -#include "gnunet_transport_communication_service.h" -#include "transport.h" - - -/** - * How many messages do we keep at most in the queue to the - * transport service before we start to drop (default, - * can be changed via the configuration file). - */ -#define DEFAULT_MAX_QUEUE_LENGTH 16 - - -/** - * Information we track per packet to enable flow control. - */ -struct FlowControl -{ - /** - * Kept in a DLL. - */ - struct FlowControl *next; - - /** - * Kept in a DLL. - */ - struct FlowControl *prev; - - /** - * Function to call once the message was processed. - */ - GNUNET_TRANSPORT_MessageCompletedCallback cb; - - /** - * Closure for @e cb - */ - void *cb_cls; - - /** - * Which peer is this about? - */ - struct GNUNET_PeerIdentity sender; - - /** - * More-or-less unique ID for the message. - */ - uint64_t id; -}; - - -/** - * Information we track per message to tell the transport about - * success or failures. - */ -struct AckPending -{ - /** - * Kept in a DLL. - */ - struct AckPending *next; - - /** - * Kept in a DLL. - */ - struct AckPending *prev; - - /** - * Communicator this entry belongs to. - */ - struct GNUNET_TRANSPORT_CommunicatorHandle *ch; - - /** - * Which peer is this about? - */ - struct GNUNET_PeerIdentity receiver; - - /** - * More-or-less unique ID for the message. - */ - uint64_t mid; - - /** - * Queue ID of the queue which will be used for the message. - */ - uint32_t qid; -}; - - -/** - * Opaque handle to the transport service for communicators. - */ -struct GNUNET_TRANSPORT_CommunicatorHandle -{ - /** - * Head of DLL of addresses this communicator offers to the transport service. - */ - struct GNUNET_TRANSPORT_AddressIdentifier *ai_head; - - /** - * Tail of DLL of addresses this communicator offers to the transport service. - */ - struct GNUNET_TRANSPORT_AddressIdentifier *ai_tail; - - /** - * DLL of messages awaiting flow control confirmation (ack). - */ - struct FlowControl *fc_head; - - /** - * DLL of messages awaiting flow control confirmation (ack). - */ - struct FlowControl *fc_tail; - - /** - * DLL of messages awaiting transmission confirmation (ack). - */ - struct AckPending *ap_head; - - /** - * DLL of messages awaiting transmission confirmation (ack). - */ - struct AckPending *ap_tail; - - /** - * DLL of queues we offer. - */ - struct GNUNET_TRANSPORT_QueueHandle *queue_head; - - /** - * DLL of queues we offer. - */ - struct GNUNET_TRANSPORT_QueueHandle *queue_tail; - - /** - * Our configuration. - */ - const struct GNUNET_CONFIGURATION_Handle *cfg; - - /** - * Config section to use. - */ - const char *config_section; - - /** - * Address prefix to use. - */ - const char *addr_prefix; - - /** - * Function to call when the transport service wants us to initiate - * a communication channel with another peer. - */ - GNUNET_TRANSPORT_CommunicatorMqInit mq_init; - - /** - * Closure for @e mq_init. - */ - void *mq_init_cls; - - /** - * Function to call when the transport service receives messages - * for a communicator (i.e. for NAT traversal or for non-bidirectional - * communicators). - */ - GNUNET_TRANSPORT_CommunicatorNotify notify_cb; - - /** - * Closure for @e notify_Cb. - */ - void *notify_cb_cls; - - /** - * Queue to talk to the transport service. - */ - struct GNUNET_MQ_Handle *mq; - - /** - * Maximum permissible queue length. - */ - unsigned long long max_queue_length; - - /** - * Flow-control identifier generator. - */ - uint64_t fc_gen; - - /** - * Internal UUID for the address used in communication with the - * transport service. - */ - uint32_t aid_gen; - - /** - * Queue identifier generator. - */ - uint32_t queue_gen; - - /** - * Characteristics of the communicator. - */ - enum GNUNET_TRANSPORT_CommunicatorCharacteristics cc; -}; - - -/** - * Handle returned to identify the internal data structure the transport - * API has created to manage a message queue to a particular peer. - */ -struct GNUNET_TRANSPORT_QueueHandle -{ - /** - * Kept in a DLL. - */ - struct GNUNET_TRANSPORT_QueueHandle *next; - - /** - * Kept in a DLL. - */ - struct GNUNET_TRANSPORT_QueueHandle *prev; - - /** - * Handle this queue belongs to. - */ - struct GNUNET_TRANSPORT_CommunicatorHandle *ch; - - /** - * Address used by the communication queue. - */ - char *address; - - /** - * The queue itself. - */ - struct GNUNET_MQ_Handle *mq; - - /** - * Which peer we can communciate with. - */ - struct GNUNET_PeerIdentity peer; - - /** - * Network type of the communication queue. - */ - enum GNUNET_NetworkType nt; - - /** - * Communication status of the queue. - */ - enum GNUNET_TRANSPORT_ConnectionStatus cs; - - /** - * ID for this queue when talking to the transport service. - */ - uint32_t queue_id; - - /** - * Maximum transmission unit for the queue. - */ - uint32_t mtu; - - /** - * Queue length. - */ - uint64_t q_len; - /** - * Queue priority. - */ - uint32_t priority; -}; - - -/** - * Internal representation of an address a communicator is - * currently providing for the transport service. - */ -struct GNUNET_TRANSPORT_AddressIdentifier -{ - /** - * Kept in a DLL. - */ - struct GNUNET_TRANSPORT_AddressIdentifier *next; - - /** - * Kept in a DLL. - */ - struct GNUNET_TRANSPORT_AddressIdentifier *prev; - - /** - * Transport handle where the address was added. - */ - struct GNUNET_TRANSPORT_CommunicatorHandle *ch; - - /** - * The actual address. - */ - char *address; - - /** - * When does the address expire? (Expected lifetime of the - * address.) - */ - struct GNUNET_TIME_Relative expiration; - - /** - * Internal UUID for the address used in communication with the - * transport service. - */ - uint32_t aid; - - /** - * Network type for the address. - */ - enum GNUNET_NetworkType nt; -}; - - -/** - * (re)connect our communicator to the transport service - * - * @param ch handle to reconnect - */ -static void -reconnect (struct GNUNET_TRANSPORT_CommunicatorHandle *ch); - - -/** - * Send message to the transport service about address @a ai - * being now available. - * - * @param ai address to add - */ -static void -send_add_address (struct GNUNET_TRANSPORT_AddressIdentifier *ai) -{ - struct GNUNET_MQ_Envelope *env; - struct GNUNET_TRANSPORT_AddAddressMessage *aam; - - if (NULL == ai->ch->mq) - return; - env = GNUNET_MQ_msg_extra (aam, - strlen (ai->address) + 1, - GNUNET_MESSAGE_TYPE_TRANSPORT_ADD_ADDRESS); - aam->expiration = GNUNET_TIME_relative_hton (ai->expiration); - aam->nt = htonl ((uint32_t) ai->nt); - memcpy (&aam[1], ai->address, strlen (ai->address) + 1); - GNUNET_MQ_send (ai->ch->mq, env); -} - - -/** - * Send message to the transport service about address @a ai - * being no longer available. - * - * @param ai address to delete - */ -static void -send_del_address (struct GNUNET_TRANSPORT_AddressIdentifier *ai) -{ - struct GNUNET_MQ_Envelope *env; - struct GNUNET_TRANSPORT_DelAddressMessage *dam; - - if (NULL == ai->ch->mq) - return; - env = GNUNET_MQ_msg (dam, GNUNET_MESSAGE_TYPE_TRANSPORT_DEL_ADDRESS); - dam->aid = htonl (ai->aid); - GNUNET_MQ_send (ai->ch->mq, env); -} - - -/** - * Send message to the transport service about queue @a qh - * being now available. - * - * @param qh queue to add - */ -static void -send_add_queue (struct GNUNET_TRANSPORT_QueueHandle *qh) -{ - struct GNUNET_MQ_Envelope *env; - struct GNUNET_TRANSPORT_AddQueueMessage *aqm; - - if (NULL == qh->ch->mq) - return; - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "Sending `GNUNET_MESSAGE_TYPE_TRANSPORT_QUEUE_SETUP` message\n"); - env = GNUNET_MQ_msg_extra (aqm, - strlen (qh->address) + 1, - GNUNET_MESSAGE_TYPE_TRANSPORT_QUEUE_SETUP); - aqm->qid = htonl (qh->queue_id); - aqm->receiver = qh->peer; - aqm->nt = htonl ((uint32_t) qh->nt); - aqm->mtu = htonl (qh->mtu); - aqm->q_len = GNUNET_htonll (qh->q_len); - aqm->priority = htonl (qh->priority); - aqm->cs = htonl ((uint32_t) qh->cs); - memcpy (&aqm[1], qh->address, strlen (qh->address) + 1); - GNUNET_MQ_send (qh->ch->mq, env); -} - - -/** - * Send message to the transport service about queue @a qh - * updated. - * - * @param qh queue to add - */ -static void -send_update_queue (struct GNUNET_TRANSPORT_QueueHandle *qh) -{ - struct GNUNET_MQ_Envelope *env; - struct GNUNET_TRANSPORT_UpdateQueueMessage *uqm; - - if (NULL == qh->ch->mq) - return; - env = GNUNET_MQ_msg (uqm, GNUNET_MESSAGE_TYPE_TRANSPORT_QUEUE_UPDATE); - uqm->qid = htonl (qh->queue_id); - uqm->receiver = qh->peer; - uqm->nt = htonl ((uint32_t) qh->nt); - uqm->mtu = htonl (qh->mtu); - uqm->q_len = GNUNET_htonll (qh->q_len); - uqm->priority = htonl (qh->priority); - uqm->cs = htonl ((uint32_t) qh->cs); - GNUNET_MQ_send (qh->ch->mq, env); -} - - -/** - * Send message to the transport service about queue @a qh - * being no longer available. - * - * @param qh queue to delete - */ -static void -send_del_queue (struct GNUNET_TRANSPORT_QueueHandle *qh) -{ - struct GNUNET_MQ_Envelope *env; - struct GNUNET_TRANSPORT_DelQueueMessage *dqm; - - if (NULL == qh->ch->mq) - return; - env = GNUNET_MQ_msg (dqm, GNUNET_MESSAGE_TYPE_TRANSPORT_QUEUE_TEARDOWN); - dqm->qid = htonl (qh->queue_id); - dqm->receiver = qh->peer; - GNUNET_MQ_send (qh->ch->mq, env); -} - - -/** - * Disconnect from the transport service. Purges - * all flow control entries as we will no longer receive - * the ACKs. Purges the ack pending entries as the - * transport will no longer expect the confirmations. - * - * @param ch service to disconnect from - */ -static void -disconnect (struct GNUNET_TRANSPORT_CommunicatorHandle *ch) -{ - struct FlowControl *fcn; - struct AckPending *apn; - - for (struct FlowControl *fc = ch->fc_head; NULL != fc; fc = fcn) - { - fcn = fc->next; - GNUNET_CONTAINER_DLL_remove (ch->fc_head, ch->fc_tail, fc); - fc->cb (fc->cb_cls, GNUNET_SYSERR); - GNUNET_free (fc); - } - for (struct AckPending *ap = ch->ap_head; NULL != ap; ap = apn) - { - apn = ap->next; - GNUNET_CONTAINER_DLL_remove (ch->ap_head, ch->ap_tail, ap); - GNUNET_free (ap); - } - if (NULL == ch->mq) - return; - GNUNET_MQ_destroy (ch->mq); - ch->mq = NULL; -} - - -/** - * Function called on MQ errors. - */ -static void -error_handler (void *cls, enum GNUNET_MQ_Error error) -{ - struct GNUNET_TRANSPORT_CommunicatorHandle *ch = cls; - - GNUNET_log (GNUNET_ERROR_TYPE_INFO, - "MQ failure %d, reconnecting to transport service.\n", - error); - disconnect (ch); - /* TODO: maybe do this with exponential backoff/delay */ - reconnect (ch); -} - - -/** - * Transport service acknowledged a message we gave it - * (with flow control enabled). Tell the communicator. - * - * @param cls our `struct GNUNET_TRANSPORT_CommunicatorHandle *` - * @param incoming_ack the ack - */ -static void -handle_incoming_ack ( - void *cls, - const struct GNUNET_TRANSPORT_IncomingMessageAck *incoming_ack) -{ - struct GNUNET_TRANSPORT_CommunicatorHandle *ch = cls; - - for (struct FlowControl *fc = ch->fc_head; NULL != fc; fc = fc->next) - { - if ((fc->id == incoming_ack->fc_id) && - (0 == memcmp (&fc->sender, - &incoming_ack->sender, - sizeof(struct GNUNET_PeerIdentity)))) - { - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "Done with message with flow control id %" PRIu64 - " for sender %s from sender %s\n", - incoming_ack->fc_id, - GNUNET_i2s (&fc->sender), - GNUNET_i2s (&incoming_ack->sender)); - GNUNET_CONTAINER_DLL_remove (ch->fc_head, ch->fc_tail, fc); - fc->cb (fc->cb_cls, GNUNET_OK); - GNUNET_free (fc); - return; - } - } - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "Message with flow control id %" PRIu64 - " from sender %s not found\n", - incoming_ack->fc_id, - GNUNET_i2s (&incoming_ack->sender)); - GNUNET_break (0); - disconnect (ch); - /* TODO: maybe do this with exponential backoff/delay */ - reconnect (ch); -} - - -/** - * Transport service wants us to create a queue. Check if @a cq - * is well-formed. - * - * @param cls our `struct GNUNET_TRANSPORT_CommunicatorHandle *` - * @param cq the queue creation request - * @return #GNUNET_OK if @a smt is well-formed - */ -static int -check_create_queue (void *cls, const struct GNUNET_TRANSPORT_CreateQueue *cq) -{ - (void) cls; - GNUNET_MQ_check_zero_termination (cq); - return GNUNET_OK; -} - - -/** - * Transport service wants us to create a queue. Tell the communicator. - * - * @param cls our `struct GNUNET_TRANSPORT_CommunicatorHandle *` - * @param cq the queue creation request - */ -static void -handle_create_queue (void *cls, const struct GNUNET_TRANSPORT_CreateQueue *cq) -{ - struct GNUNET_TRANSPORT_CommunicatorHandle *ch = cls; - const char *addr = (const char *) &cq[1]; - struct GNUNET_TRANSPORT_CreateQueueResponse *cqr; - struct GNUNET_MQ_Envelope *env; - - if (GNUNET_OK != ch->mq_init (ch->mq_init_cls, &cq->receiver, addr)) - { - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - "Address `%s' invalid for this communicator\n", - addr); - env = GNUNET_MQ_msg (cqr, GNUNET_MESSAGE_TYPE_TRANSPORT_QUEUE_CREATE_FAIL); - } - else - { - env = GNUNET_MQ_msg (cqr, GNUNET_MESSAGE_TYPE_TRANSPORT_QUEUE_CREATE_OK); - } - cqr->request_id = cq->request_id; - GNUNET_MQ_send (ch->mq, env); -} - - -/** - * Transport service wants us to send a message. Check if @a smt - * is well-formed. - * - * @param cls our `struct GNUNET_TRANSPORT_CommunicatorHandle *` - * @param smt the transmission request - * @return #GNUNET_OK if @a smt is well-formed - */ -static int -check_send_msg (void *cls, const struct GNUNET_TRANSPORT_SendMessageTo *smt) -{ - (void) cls; - GNUNET_MQ_check_boxed_message (smt); - return GNUNET_OK; -} - - -/** - * Notify transport service about @a status of a message with - * @a mid sent to @a receiver. - * - * @param ch handle - * @param status #GNUNET_OK on success, #GNUNET_SYSERR on failure - * @param receiver which peer was the receiver - * @param mid message that the ack is about - */ -static void -send_ack (struct GNUNET_TRANSPORT_CommunicatorHandle *ch, - int status, - const struct GNUNET_PeerIdentity *receiver, - uint64_t mid, - uint32_t qid) -{ - struct GNUNET_MQ_Envelope *env; - struct GNUNET_TRANSPORT_SendMessageToAck *ack; - - env = GNUNET_MQ_msg (ack, GNUNET_MESSAGE_TYPE_TRANSPORT_SEND_MSG_ACK); - ack->status = htonl (status); - ack->mid = mid; - ack->qid = qid; - ack->receiver = *receiver; - GNUNET_MQ_send (ch->mq, env); -} - - -/** - * Message queue transmission by communicator was successful, - * notify transport service. - * - * @param cls an `struct AckPending *` - */ -static void -send_ack_cb (void *cls) -{ - struct AckPending *ap = cls; - struct GNUNET_TRANSPORT_CommunicatorHandle *ch = ap->ch; - - GNUNET_CONTAINER_DLL_remove (ch->ap_head, ch->ap_tail, ap); - send_ack (ch, GNUNET_OK, &ap->receiver, ap->mid, ap->qid); - GNUNET_free (ap); -} - - -/** - * Transport service wants us to send a message. Tell the communicator. - * - * @param cls our `struct GNUNET_TRANSPORT_CommunicatorHandle *` - * @param smt the transmission request - */ -static void -handle_send_msg (void *cls, const struct GNUNET_TRANSPORT_SendMessageTo *smt) -{ - struct GNUNET_TRANSPORT_CommunicatorHandle *ch = cls; - const struct GNUNET_MessageHeader *mh; - struct GNUNET_MQ_Envelope *env; - struct AckPending *ap; - struct GNUNET_TRANSPORT_QueueHandle *qh; - - for (qh = ch->queue_head; NULL != qh; qh = qh->next) - if ((qh->queue_id == ntohl (smt->qid)) && - (0 == memcmp (&qh->peer, - &smt->receiver, - sizeof(struct GNUNET_PeerIdentity)))) - break; - if (NULL == qh) - { - /* queue is already gone, tell transport this one failed */ - GNUNET_log (GNUNET_ERROR_TYPE_INFO, - "Transmission failed, queue no longer exists.\n"); - send_ack (ch, GNUNET_NO, &smt->receiver, smt->mid, smt->qid); - return; - } - ap = GNUNET_new (struct AckPending); - ap->ch = ch; - ap->receiver = smt->receiver; - ap->mid = smt->mid; - ap->qid = smt->qid; - GNUNET_CONTAINER_DLL_insert (ch->ap_head, ch->ap_tail, ap); - mh = (const struct GNUNET_MessageHeader *) &smt[1]; - env = GNUNET_MQ_msg_copy (mh); - GNUNET_MQ_notify_sent (env, &send_ack_cb, ap); - GNUNET_MQ_send (qh->mq, env); -} - - -/** - * Transport service gives us backchannel message. Check if @a bi - * is well-formed. - * - * @param cls our `struct GNUNET_TRANSPORT_CommunicatorHandle *` - * @param bi the backchannel message - * @return #GNUNET_OK if @a smt is well-formed - */ -static int -check_backchannel_incoming ( - void *cls, - const struct GNUNET_TRANSPORT_CommunicatorBackchannelIncoming *bi) -{ - (void) cls; - GNUNET_MQ_check_boxed_message (bi); - return GNUNET_OK; -} - - -/** - * Transport service gives us backchannel message. Handle it. - * - * @param cls our `struct GNUNET_TRANSPORT_CommunicatorHandle *` - * @param bi the backchannel message - */ -static void -handle_backchannel_incoming ( - void *cls, - const struct GNUNET_TRANSPORT_CommunicatorBackchannelIncoming *bi) -{ - struct GNUNET_TRANSPORT_CommunicatorHandle *ch = cls; - if (NULL != ch->notify_cb) - ch->notify_cb (ch->notify_cb_cls, - &bi->pid, - (const struct GNUNET_MessageHeader *) &bi[1]); - else - GNUNET_log ( - GNUNET_ERROR_TYPE_INFO, - _ ("Dropped backchanel message: handler not provided by communicator\n")); -} - - -/** - * (re)connect our communicator to the transport service - * - * @param ch handle to reconnect - */ -static void -reconnect (struct GNUNET_TRANSPORT_CommunicatorHandle *ch) -{ - struct GNUNET_MQ_MessageHandler handlers[] = - { GNUNET_MQ_hd_fixed_size (incoming_ack, - GNUNET_MESSAGE_TYPE_TRANSPORT_INCOMING_MSG_ACK, - struct GNUNET_TRANSPORT_IncomingMessageAck, - ch), - GNUNET_MQ_hd_var_size (create_queue, - GNUNET_MESSAGE_TYPE_TRANSPORT_QUEUE_CREATE, - struct GNUNET_TRANSPORT_CreateQueue, - ch), - GNUNET_MQ_hd_var_size (send_msg, - GNUNET_MESSAGE_TYPE_TRANSPORT_SEND_MSG, - struct GNUNET_TRANSPORT_SendMessageTo, - ch), - GNUNET_MQ_hd_var_size ( - backchannel_incoming, - GNUNET_MESSAGE_TYPE_TRANSPORT_COMMUNICATOR_BACKCHANNEL_INCOMING, - struct GNUNET_TRANSPORT_CommunicatorBackchannelIncoming, - ch), - GNUNET_MQ_handler_end () }; - struct GNUNET_TRANSPORT_CommunicatorAvailableMessage *cam; - struct GNUNET_MQ_Envelope *env; - - ch->mq = - GNUNET_CLIENT_connect (ch->cfg, "transport", handlers, &error_handler, ch); - if (NULL == ch->mq) - return; - env = GNUNET_MQ_msg_extra (cam, - strlen (ch->addr_prefix) + 1, - GNUNET_MESSAGE_TYPE_TRANSPORT_NEW_COMMUNICATOR); - cam->cc = htonl ((uint32_t) ch->cc); - memcpy (&cam[1], ch->addr_prefix, strlen (ch->addr_prefix) + 1); - GNUNET_MQ_send (ch->mq, env); - for (struct GNUNET_TRANSPORT_AddressIdentifier *ai = ch->ai_head; NULL != ai; - ai = ai->next) - send_add_address (ai); - for (struct GNUNET_TRANSPORT_QueueHandle *qh = ch->queue_head; NULL != qh; - qh = qh->next) - send_add_queue (qh); -} - - -struct GNUNET_TRANSPORT_CommunicatorHandle * -GNUNET_TRANSPORT_communicator_connect ( - const struct GNUNET_CONFIGURATION_Handle *cfg, - const char *config_section, - const char *addr_prefix, - enum GNUNET_TRANSPORT_CommunicatorCharacteristics cc, - GNUNET_TRANSPORT_CommunicatorMqInit mq_init, - void *mq_init_cls, - GNUNET_TRANSPORT_CommunicatorNotify notify_cb, - void *notify_cb_cls) -{ - struct GNUNET_TRANSPORT_CommunicatorHandle *ch; - - ch = GNUNET_new (struct GNUNET_TRANSPORT_CommunicatorHandle); - ch->cfg = cfg; - ch->config_section = config_section; - ch->addr_prefix = addr_prefix; - ch->mq_init = mq_init; - ch->mq_init_cls = mq_init_cls; - ch->notify_cb = notify_cb; - ch->notify_cb_cls = notify_cb_cls; - ch->cc = cc; - reconnect (ch); - if (GNUNET_OK != - GNUNET_CONFIGURATION_get_value_number (cfg, - config_section, - "MAX_QUEUE_LENGTH", - &ch->max_queue_length)) - ch->max_queue_length = DEFAULT_MAX_QUEUE_LENGTH; - if (NULL == ch->mq) - { - GNUNET_free (ch); - return NULL; - } - return ch; -} - - -/** - * Disconnect from the transport service. - * - * @param ch handle returned from connect - */ -void -GNUNET_TRANSPORT_communicator_disconnect ( - struct GNUNET_TRANSPORT_CommunicatorHandle *ch) -{ - disconnect (ch); - while (NULL != ch->ai_head) - { - GNUNET_break (0); /* communicator forgot to remove address, warn! */ - GNUNET_TRANSPORT_communicator_address_remove (ch->ai_head); - } - GNUNET_free (ch); -} - - -/* ************************* Receiving *************************** */ - - -int -GNUNET_TRANSPORT_communicator_receive ( - struct GNUNET_TRANSPORT_CommunicatorHandle *ch, - const struct GNUNET_PeerIdentity *sender, - const struct GNUNET_MessageHeader *msg, - struct GNUNET_TIME_Relative expected_addr_validity, - GNUNET_TRANSPORT_MessageCompletedCallback cb, - void *cb_cls) -{ - struct GNUNET_MQ_Envelope *env; - struct GNUNET_TRANSPORT_IncomingMessage *im; - uint16_t msize; - - - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "communicator receive\n"); - - if (NULL == ch->mq) - return GNUNET_SYSERR; - if ((NULL == cb) && (GNUNET_MQ_get_length (ch->mq) >= ch->max_queue_length)) - { - GNUNET_log ( - GNUNET_ERROR_TYPE_WARNING, - "Dropping message: transport is too slow, queue length %llu exceeded\n", - ch->max_queue_length); - return GNUNET_NO; - } - - msize = ntohs (msg->size); - env = - GNUNET_MQ_msg_extra (im, msize, GNUNET_MESSAGE_TYPE_TRANSPORT_INCOMING_MSG); - if (NULL == env) - { - GNUNET_break (0); - return GNUNET_SYSERR; - } - im->expected_address_validity = - GNUNET_TIME_relative_hton (expected_addr_validity); - im->sender = *sender; - // FIXME: this is expensive, would be better if we would - // re-design the API to allow us to create the envelope first, - // and then have the application fill in the body so we do - // not have to memcpy() - memcpy (&im[1], msg, msize); - im->fc_on = htonl (GNUNET_NO); - if (NULL != cb) - { - struct FlowControl *fc; - - im->fc_on = htonl (GNUNET_YES); - im->fc_id = ch->fc_gen++; - fc = GNUNET_new (struct FlowControl); - fc->sender = *sender; - fc->id = im->fc_id; - fc->cb = cb; - fc->cb_cls = cb_cls; - GNUNET_CONTAINER_DLL_insert (ch->fc_head, ch->fc_tail, fc); - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "Created flow control id %" PRIu64 " for sender %s\n", - fc->id, - GNUNET_i2s (&fc->sender)); - } - GNUNET_MQ_send (ch->mq, env); - return GNUNET_OK; -} - - -/* ************************* Discovery *************************** */ - - -struct GNUNET_TRANSPORT_QueueHandle * -GNUNET_TRANSPORT_communicator_mq_add ( - struct GNUNET_TRANSPORT_CommunicatorHandle *ch, - const struct GNUNET_PeerIdentity *peer, - const char *address, - uint32_t mtu, - uint64_t q_len, - uint32_t priority, - enum GNUNET_NetworkType nt, - enum GNUNET_TRANSPORT_ConnectionStatus cs, - struct GNUNET_MQ_Handle *mq) -{ - struct GNUNET_TRANSPORT_QueueHandle *qh; - - // Do not notify the service if there is no intial capacity. - GNUNET_assert (0 < q_len); - - qh = GNUNET_new (struct GNUNET_TRANSPORT_QueueHandle); - qh->ch = ch; - qh->peer = *peer; - qh->address = GNUNET_strdup (address); - qh->nt = nt; - qh->mtu = mtu; - qh->q_len = q_len; - qh->priority = priority; - qh->cs = cs; - qh->mq = mq; - qh->queue_id = ch->queue_gen++; - GNUNET_CONTAINER_DLL_insert (ch->queue_head, ch->queue_tail, qh); - send_add_queue (qh); - return qh; -} - - -void -GNUNET_TRANSPORT_communicator_mq_update ( - struct GNUNET_TRANSPORT_CommunicatorHandle *ch, - const struct GNUNET_TRANSPORT_QueueHandle *u_qh, - uint64_t q_len, - uint32_t priority) -{ - struct GNUNET_TRANSPORT_QueueHandle *qh; - - for (qh = ch->queue_head; NULL != qh; qh = qh->next) - { - if (u_qh == qh) - break; - } - GNUNET_assert (NULL != qh); - qh->q_len = q_len; - qh->priority = priority; - send_update_queue (qh); -} - - -/** - * Notify transport service that an MQ became unavailable due to a - * disconnect or timeout. - * - * @param qh handle for the queue that must be invalidated - */ -void -GNUNET_TRANSPORT_communicator_mq_del (struct GNUNET_TRANSPORT_QueueHandle *qh) -{ - struct GNUNET_TRANSPORT_CommunicatorHandle *ch = qh->ch; - - send_del_queue (qh); - GNUNET_CONTAINER_DLL_remove (ch->queue_head, ch->queue_tail, qh); - GNUNET_MQ_destroy (qh->mq); - GNUNET_free (qh->address); - GNUNET_free (qh); -} - - -/** - * Notify transport service about an address that this communicator - * provides for this peer. - * - * @param ch connection to transport service - * @param address our address in human-readable format, 0-terminated, UTF-8 - * @param nt which network type does the address belong to? - * @param expiration when does the communicator forsee this address expiring? - */ -struct GNUNET_TRANSPORT_AddressIdentifier * -GNUNET_TRANSPORT_communicator_address_add ( - struct GNUNET_TRANSPORT_CommunicatorHandle *ch, - const char *address, - enum GNUNET_NetworkType nt, - struct GNUNET_TIME_Relative expiration) -{ - struct GNUNET_TRANSPORT_AddressIdentifier *ai; - - ai = GNUNET_new (struct GNUNET_TRANSPORT_AddressIdentifier); - ai->ch = ch; - ai->address = GNUNET_strdup (address); - ai->nt = nt; - ai->expiration = expiration; - ai->aid = ch->aid_gen++; - GNUNET_CONTAINER_DLL_insert (ch->ai_head, ch->ai_tail, ai); - send_add_address (ai); - return ai; -} - - -/** - * Notify transport service about an address that this communicator no - * longer provides for this peer. - * - * @param ai address that is no longer provided - */ -void -GNUNET_TRANSPORT_communicator_address_remove ( - struct GNUNET_TRANSPORT_AddressIdentifier *ai) -{ - struct GNUNET_TRANSPORT_CommunicatorHandle *ch = ai->ch; - - send_del_address (ai); - GNUNET_CONTAINER_DLL_remove (ch->ai_head, ch->ai_tail, ai); - GNUNET_free (ai->address); - GNUNET_free (ai); - ai = NULL; -} - - -/** - * Notify transport service that this communicator no longer provides all its addresses for this peer. - * - * @param ch The communicator handle. - */ -void -GNUNET_TRANSPORT_communicator_address_remove_all ( - struct GNUNET_TRANSPORT_CommunicatorHandle *ch) -{ - struct GNUNET_TRANSPORT_AddressIdentifier *ai = ch->ai_head; - while (NULL != ai) - { - struct GNUNET_TRANSPORT_AddressIdentifier *ai_next = ai->next; - GNUNET_TRANSPORT_communicator_address_remove (ai); - ai = ai_next; - } -} - - -/* ************************* Backchannel *************************** */ - - -void -GNUNET_TRANSPORT_communicator_notify ( - struct GNUNET_TRANSPORT_CommunicatorHandle *ch, - const struct GNUNET_PeerIdentity *pid, - const char *comm, - const struct GNUNET_MessageHeader *header) -{ - struct GNUNET_MQ_Envelope *env; - struct GNUNET_TRANSPORT_CommunicatorBackchannel *cb; - size_t slen = strlen (comm) + 1; - uint16_t mlen = ntohs (header->size); - - GNUNET_assert (mlen + slen + sizeof(*cb) < UINT16_MAX); - env = - GNUNET_MQ_msg_extra (cb, - slen + mlen, - GNUNET_MESSAGE_TYPE_TRANSPORT_COMMUNICATOR_BACKCHANNEL); - cb->pid = *pid; - memcpy (&cb[1], header, mlen); - memcpy (((char *) &cb[1]) + mlen, comm, slen); - GNUNET_MQ_send (ch->mq, env); -} - - -/* end of transport_api2_communication.c */ diff --git a/src/transport/transport_api2_core.c b/src/transport/transport_api2_core.c deleted file mode 100644 index 0d2a0ac7f..000000000 --- a/src/transport/transport_api2_core.c +++ /dev/null @@ -1,826 +0,0 @@ -/* - This file is part of GNUnet. - Copyright (C) 2009-2013, 2016, 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 . - - SPDX-License-Identifier: AGPL3.0-or-later - */ - -/** - * @file transport/transport_api_core.c - * @brief library to access the transport service for message exchange - * @author Christian Grothoff - */ -#include "platform.h" -#include "gnunet_util_lib.h" -#include "gnunet_constants.h" -#include "gnunet_arm_service.h" -#include "gnunet_hello_lib.h" -#include "gnunet_protocols.h" -#include "gnunet_transport_core_service.h" -#include "transport.h" - -#define LOG(kind, ...) GNUNET_log_from (kind, "transport-api-core", __VA_ARGS__) - -/** - * How large to start with for the hashmap of neighbours. - */ -#define STARTING_NEIGHBOURS_SIZE 16 - -/** - * Window size. How many messages to the same target do we pass - * to TRANSPORT without a SEND_OK in between? Small values limit - * thoughput, large values will increase latency. - * - * FIXME-OPTIMIZE: find out what good values are experimentally, - * maybe set adaptively (i.e. to observed available bandwidth). - */ -#define SEND_WINDOW_SIZE 4 - - -/** - * Entry in hash table of all of our current (connected) neighbours. - */ -struct Neighbour -{ - /** - * Identity of this neighbour. - */ - struct GNUNET_PeerIdentity id; - - /** - * Overall transport handle. - */ - struct GNUNET_TRANSPORT_CoreHandle *h; - - /** - * Active message queue for the peer. - */ - struct GNUNET_MQ_Handle *mq; - - /** - * Envelope with the message we are currently transmitting (or NULL). - */ - struct GNUNET_MQ_Envelope *env; - - /** - * Closure for @e mq handlers. - */ - void *handlers_cls; - - /** - * How many messages can we still send to this peer before we should - * throttle? - */ - unsigned int ready_window; - - /** - * Used to indicate our status if @e env is non-NULL. Set to - * #GNUNET_YES if we did pass a message to the MQ and are waiting - * for the call to #notify_send_done(). Set to #GNUNET_NO if the @e - * ready_window is 0 and @e env is waiting for a - * #GNUNET_MESSAGE_TYPE_TRANSPORT_RECV_OK? - */ - int16_t awaiting_done; - - /** - * Size of the message in @e env. - */ - uint16_t env_size; -}; - - -/** - * Handle for the transport service (includes all of the - * state for the transport service). - */ -struct GNUNET_TRANSPORT_CoreHandle -{ - /** - * Closure for the callbacks. - */ - void *cls; - - /** - * Functions to call for received data (template for - * new message queues). - */ - struct GNUNET_MQ_MessageHandler *handlers; - - /** - * function to call on connect events - */ - GNUNET_TRANSPORT_NotifyConnect nc_cb; - - /** - * function to call on disconnect events - */ - GNUNET_TRANSPORT_NotifyDisconnect nd_cb; - - /** - * My client connection to the transport service. - */ - struct GNUNET_MQ_Handle *mq; - - /** - * My configuration. - */ - const struct GNUNET_CONFIGURATION_Handle *cfg; - - /** - * Hash map of the current connected neighbours of this peer. - * Maps peer identities to `struct Neighbour` entries. - */ - struct GNUNET_CONTAINER_MultiPeerMap *neighbours; - - /** - * Peer identity as assumed by this process, or all zeros. - */ - struct GNUNET_PeerIdentity self; - - /** - * ID of the task trying to reconnect to the service. - */ - struct GNUNET_SCHEDULER_Task *reconnect_task; - - /** - * Delay until we try to reconnect. - */ - struct GNUNET_TIME_Relative reconnect_delay; - - /** - * Should we check that @e self matches what the service thinks? - * (if #GNUNET_NO, then @e self is all zeros!). - */ - int check_self; -}; - - -/** - * Function that will schedule the job that will try - * to connect us again to the client. - * - * @param h transport service to reconnect - */ -static void -disconnect_and_schedule_reconnect (struct GNUNET_TRANSPORT_CoreHandle *h); - - -/** - * Get the neighbour list entry for the given peer - * - * @param h our context - * @param peer peer to look up - * @return NULL if no such peer entry exists - */ -static struct Neighbour * -neighbour_find (struct GNUNET_TRANSPORT_CoreHandle *h, - const struct GNUNET_PeerIdentity *peer) -{ - return GNUNET_CONTAINER_multipeermap_get (h->neighbours, peer); -} - - -/** - * Iterator over hash map entries, for deleting state of a neighbour. - * - * @param cls the `struct GNUNET_TRANSPORT_CoreHandle *` - * @param key peer identity - * @param value value in the hash map, the neighbour entry to delete - * @return #GNUNET_YES if we should continue to - * iterate, - * #GNUNET_NO if not. - */ -static int -neighbour_delete (void *cls, const struct GNUNET_PeerIdentity *key, void *value) -{ - struct GNUNET_TRANSPORT_CoreHandle *handle = cls; - struct Neighbour *n = value; - - LOG (GNUNET_ERROR_TYPE_DEBUG, - "Dropping entry for neighbour `%s'.\n", - GNUNET_i2s (key)); - if (NULL != handle->nd_cb) - handle->nd_cb (handle->cls, &n->id, n->handlers_cls); - if (NULL != n->env) - { - GNUNET_MQ_send_cancel (n->env); - n->env = NULL; - } - GNUNET_MQ_destroy (n->mq); - GNUNET_assert (NULL == n->mq); - GNUNET_assert ( - GNUNET_YES == - GNUNET_CONTAINER_multipeermap_remove (handle->neighbours, key, n)); - GNUNET_free (n); - return GNUNET_YES; -} - - -/** - * Generic error handler, called with the appropriate - * error code and the same closure specified at the creation of - * the message queue. - * Not every message queue implementation supports an error handler. - * - * @param cls closure with the `struct GNUNET_TRANSPORT_CoreHandle *` - * @param error error code - */ -static void -mq_error_handler (void *cls, enum GNUNET_MQ_Error error) -{ - struct GNUNET_TRANSPORT_CoreHandle *h = cls; - - LOG (GNUNET_ERROR_TYPE_DEBUG, - "Error %u received from transport service, disconnecting temporarily.\n", - error); - disconnect_and_schedule_reconnect (h); -} - - -/** - * A message from the handler's message queue to a neighbour was - * transmitted. Now trigger (possibly delayed) notification of the - * neighbour's message queue that we are done and thus ready for - * the next message. Note that the MQ being ready is independent - * of the send window, as we may queue many messages and simply - * not pass them to TRANSPORT if the send window is insufficient. - * - * @param cls the `struct Neighbour` where the message was sent - */ -static void -notify_send_done (void *cls) -{ - struct Neighbour *n = cls; - - n->awaiting_done = GNUNET_NO; - n->env = NULL; - if (0 < n->ready_window) - GNUNET_MQ_impl_send_continue (n->mq); -} - - -/** - * We have an envelope waiting for transmission at @a n, and - * our transmission window is positive. Perform the transmission. - * - * @param n neighbour to perform transmission for - */ -static void -do_send (struct Neighbour *n) -{ - GNUNET_assert (0 < n->ready_window); - GNUNET_assert (NULL != n->env); - n->ready_window--; - n->awaiting_done = GNUNET_YES; - GNUNET_MQ_notify_sent (n->env, ¬ify_send_done, n); - GNUNET_MQ_send (n->h->mq, n->env); - LOG (GNUNET_ERROR_TYPE_DEBUG, - "Passed message of type %u for neighbour `%s' to TRANSPORT. ready_window %u\n", - ntohs (GNUNET_MQ_env_get_msg (n->env)->type), - GNUNET_i2s (&n->id), - n->ready_window); -} - - -/** - * Implement sending functionality of a message queue. - * Called one message at a time. Should send the @a msg - * to the transport service and then notify the queue - * once we are ready for the next one. - * - * @param mq the message queue - * @param msg the message to send - * @param impl_state state of the implementation - */ -static void -mq_send_impl (struct GNUNET_MQ_Handle *mq, - const struct GNUNET_MessageHeader *msg, - void *impl_state) -{ - struct Neighbour *n = impl_state; - struct OutboundMessage *obm; - uint16_t msize; - - msize = ntohs (msg->size); - if (msize >= GNUNET_MAX_MESSAGE_SIZE - sizeof(*obm)) - { - GNUNET_break (0); - GNUNET_MQ_impl_send_continue (mq); - return; - } - LOG (GNUNET_ERROR_TYPE_DEBUG, - "CORE requested transmission of message of type %u to neighbour `%s'.\n", - ntohs (msg->type), - GNUNET_i2s (&n->id)); - - GNUNET_assert (NULL == n->env); - n->env = - GNUNET_MQ_msg_nested_mh (obm, GNUNET_MESSAGE_TYPE_TRANSPORT_SEND, msg); - n->env_size = ntohs (msg->size); - { - struct GNUNET_MQ_Envelope *env; - - env = GNUNET_MQ_get_current_envelope (mq); - obm->priority = htonl ((uint32_t) GNUNET_MQ_env_get_options (env)); - } - obm->peer = n->id; - if (0 == n->ready_window) - { - LOG (GNUNET_ERROR_TYPE_DEBUG, - "Flow control delays transmission to CORE until we see SEND_OK.\n"); - return; /* can't send yet, need to wait for SEND_OK */ - } - do_send (n); -} - - -/** - * Handle destruction of a message queue. Implementations must not - * free @a mq, but should take care of @a impl_state. - * - * @param mq the message queue to destroy - * @param impl_state state of the implementation - */ -static void -mq_destroy_impl (struct GNUNET_MQ_Handle *mq, void *impl_state) -{ - struct Neighbour *n = impl_state; - - GNUNET_assert (mq == n->mq); - n->mq = NULL; -} - - -/** - * Implementation function that cancels the currently sent message. - * Should basically undo whatever #mq_send_impl() did. - * - * @param mq message queue - * @param impl_state state specific to the implementation - */ -static void -mq_cancel_impl (struct GNUNET_MQ_Handle *mq, void *impl_state) -{ - struct Neighbour *n = impl_state; - - n->ready_window++; - if (GNUNET_YES == n->awaiting_done) - { - GNUNET_MQ_send_cancel (n->env); - n->env = NULL; - n->awaiting_done = GNUNET_NO; - } - else - { - GNUNET_assert (0 == n->ready_window); - n->env = NULL; - } -} - - -/** - * We had an error processing a message we forwarded from a peer to - * the CORE service. We should just complain about it but otherwise - * continue processing. - * - * @param cls closure - * @param error error code - */ -static void -peer_mq_error_handler (void *cls, enum GNUNET_MQ_Error error) -{ - struct Neighbour *n = cls; - - if (GNUNET_MQ_ERROR_MALFORMED == error) - GNUNET_break_op (0); - //TODO Look into bug #7887 - - GNUNET_TRANSPORT_core_receive_continue (n->h, &n->id); -} - - -/** - * Function we use for handling incoming connect messages. - * - * @param cls closure, a `struct GNUNET_TRANSPORT_Handle *` - * @param cim message received - */ -static void -handle_connect (void *cls, const struct ConnectInfoMessage *cim) -{ - struct GNUNET_TRANSPORT_CoreHandle *h = cls; - struct Neighbour *n; - - LOG (GNUNET_ERROR_TYPE_DEBUG, - "Receiving CONNECT message for `%s'\n", - GNUNET_i2s (&cim->id)); - n = neighbour_find (h, &cim->id); - if (NULL != n) - { - GNUNET_break (0); - disconnect_and_schedule_reconnect (h); - return; - } - n = GNUNET_new (struct Neighbour); - n->id = cim->id; - n->h = h; - n->ready_window = SEND_WINDOW_SIZE; - GNUNET_assert (GNUNET_OK == - GNUNET_CONTAINER_multipeermap_put ( - h->neighbours, - &n->id, - n, - GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_ONLY)); - - n->mq = GNUNET_MQ_queue_for_callbacks (&mq_send_impl, - &mq_destroy_impl, - &mq_cancel_impl, - n, - h->handlers, - &peer_mq_error_handler, - n); - if (NULL != h->nc_cb) - { - n->handlers_cls = h->nc_cb (h->cls, &n->id, n->mq); - GNUNET_MQ_set_handlers_closure (n->mq, n->handlers_cls); - } -} - - -/** - * Function we use for handling incoming disconnect messages. - * - * @param cls closure, a `struct GNUNET_TRANSPORT_CoreHandle *` - * @param dim message received - */ -static void -handle_disconnect (void *cls, const struct DisconnectInfoMessage *dim) -{ - struct GNUNET_TRANSPORT_CoreHandle *h = cls; - struct Neighbour *n; - - GNUNET_break (ntohl (dim->reserved) == 0); - LOG (GNUNET_ERROR_TYPE_DEBUG, - "Receiving DISCONNECT message for `%s'.\n", - GNUNET_i2s (&dim->peer)); - n = neighbour_find (h, &dim->peer); - if (NULL == n) - { - GNUNET_break (0); - disconnect_and_schedule_reconnect (h); - return; - } - GNUNET_assert (GNUNET_YES == neighbour_delete (h, &dim->peer, n)); -} - - -/** - * Function we use for handling incoming send-ok messages. - * - * @param cls closure, a `struct GNUNET_TRANSPORT_CoreHandle *` - * @param okm message received - */ -static void -handle_send_ok (void *cls, const struct SendOkMessage *okm) -{ - struct GNUNET_TRANSPORT_CoreHandle *h = cls; - struct Neighbour *n; - - LOG (GNUNET_ERROR_TYPE_DEBUG, - "Receiving SEND_OK message for transmission to %s\n", - GNUNET_i2s (&okm->peer)); - - n = neighbour_find (h, &okm->peer); - - if (NULL == n) - { - /* We should never get a 'SEND_OK' for a peer that we are not - connected to */ - GNUNET_break (0); - disconnect_and_schedule_reconnect (h); - return; - } - - if ((GNUNET_NO == n->awaiting_done) && - (NULL != n->env) && - (0 == n->ready_window)) - { - n->ready_window++; - do_send (n); - return; - } - else if ((GNUNET_NO == n->awaiting_done) && - (0 == n->ready_window)) - { - n->ready_window++; - GNUNET_MQ_impl_send_continue (n->mq); - return; - } - n->ready_window++; -} - - -/** - * Function we use for checking incoming "inbound" messages. - * - * @param cls closure, a `struct GNUNET_TRANSPORT_CoreHandle *` - * @param im message received - */ -static int -check_recv (void *cls, const struct InboundMessage *im) -{ - const struct GNUNET_MessageHeader *imm; - uint16_t size; - - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "check_recv\n"); - size = ntohs (im->header.size) - sizeof(*im); - if (size < sizeof(struct GNUNET_MessageHeader)) - { - GNUNET_break (0); - return GNUNET_SYSERR; - } - imm = (const struct GNUNET_MessageHeader *) &im[1]; - if (ntohs (imm->size) != size) - { - GNUNET_break (0); - return GNUNET_SYSERR; - } - return GNUNET_OK; -} - - -/** - * Function we use for handling incoming messages. - * - * @param cls closure, a `struct GNUNET_TRANSPORT_CoreHandle *` - * @param im message received - */ -static void -handle_recv (void *cls, const struct InboundMessage *im) -{ - struct GNUNET_TRANSPORT_CoreHandle *h = cls; - const struct GNUNET_MessageHeader *imm = - (const struct GNUNET_MessageHeader *) &im[1]; - struct Neighbour *n; - - LOG (GNUNET_ERROR_TYPE_DEBUG, - "Received message of type %u with %u bytes from `%s'.\n", - (unsigned int) ntohs (imm->type), - (unsigned int) ntohs (imm->size), - GNUNET_i2s (&im->peer)); - n = neighbour_find (h, &im->peer); - if (NULL == n) - { - GNUNET_break (0); - disconnect_and_schedule_reconnect (h); - return; - } - GNUNET_MQ_inject_message (n->mq, imm); -} - - -/** - * Try again to connect to transport service. - * - * @param cls the handle to the transport service - */ -static void -reconnect (void *cls) -{ - struct GNUNET_TRANSPORT_CoreHandle *h = cls; - struct GNUNET_MQ_MessageHandler handlers[] = - { GNUNET_MQ_hd_fixed_size (connect, - GNUNET_MESSAGE_TYPE_TRANSPORT_CONNECT, - struct ConnectInfoMessage, - h), - GNUNET_MQ_hd_fixed_size (disconnect, - GNUNET_MESSAGE_TYPE_TRANSPORT_DISCONNECT, - struct DisconnectInfoMessage, - h), - GNUNET_MQ_hd_fixed_size (send_ok, - GNUNET_MESSAGE_TYPE_TRANSPORT_SEND_OK, - struct SendOkMessage, - h), - GNUNET_MQ_hd_var_size (recv, - GNUNET_MESSAGE_TYPE_TRANSPORT_RECV, - struct InboundMessage, - h), - GNUNET_MQ_handler_end () }; - struct GNUNET_MQ_Envelope *env; - struct StartMessage *s; - uint32_t options; - - h->reconnect_task = NULL; - LOG (GNUNET_ERROR_TYPE_DEBUG, "Connecting to transport service.\n"); - GNUNET_assert (NULL == h->mq); - h->mq = - GNUNET_CLIENT_connect (h->cfg, "transport", handlers, &mq_error_handler, h); - if (NULL == h->mq) - return; - env = GNUNET_MQ_msg (s, GNUNET_MESSAGE_TYPE_TRANSPORT_START); - options = 0; - if (h->check_self) - options |= 1; - if (NULL != h->handlers) - options |= 2; - s->options = htonl (options); - s->self = h->self; - GNUNET_MQ_send (h->mq, env); -} - - -/** - * Disconnect from the transport service. - * - * @param h transport service to reconnect - */ -static void -disconnect (struct GNUNET_TRANSPORT_CoreHandle *h) -{ - GNUNET_CONTAINER_multipeermap_iterate (h->neighbours, &neighbour_delete, h); - if (NULL != h->mq) - { - GNUNET_MQ_destroy (h->mq); - h->mq = NULL; - } -} - - -/** - * Function that will schedule the job that will try - * to connect us again to the client. - * - * @param h transport service to reconnect - */ -static void -disconnect_and_schedule_reconnect (struct GNUNET_TRANSPORT_CoreHandle *h) -{ - GNUNET_assert (NULL == h->reconnect_task); - disconnect (h); - LOG (GNUNET_ERROR_TYPE_DEBUG, - "Scheduling task to reconnect to transport service in %s.\n", - GNUNET_STRINGS_relative_time_to_string (h->reconnect_delay, GNUNET_YES)); - h->reconnect_task = - GNUNET_SCHEDULER_add_delayed (h->reconnect_delay, &reconnect, h); - h->reconnect_delay = GNUNET_TIME_STD_BACKOFF (h->reconnect_delay); -} - - -/** - * Checks if a given peer is connected to us and get the message queue. - * - * @param handle connection to transport service - * @param peer the peer to check - * @return NULL if disconnected, otherwise message queue for @a peer - */ -struct GNUNET_MQ_Handle * -GNUNET_TRANSPORT_core_get_mq (struct GNUNET_TRANSPORT_CoreHandle *handle, - const struct GNUNET_PeerIdentity *peer) -{ - struct Neighbour *n; - - n = neighbour_find (handle, peer); - if (NULL == n) - return NULL; - return n->mq; -} - - -/** - * Notification from the CORE service to the TRANSPORT service - * that the CORE service has finished processing a message from - * TRANSPORT (via the @code{handlers} of #GNUNET_TRANSPORT_core_connect()) - * and that it is thus now OK for TRANSPORT to send more messages - * for @a pid. - * - * Used to provide flow control, this is our equivalent to - * #GNUNET_SERVICE_client_continue() of an ordinary service. - * - * Note that due to the use of a window, TRANSPORT may send multiple - * messages destined for the same peer even without an intermediate - * call to this function. However, CORE must still call this function - * once per message received, as otherwise eventually the window will - * be full and TRANSPORT will stop providing messages to CORE for @a - * pid. - * - * @param ch core handle - * @param pid which peer was the message from that was fully processed by CORE - */ -void -GNUNET_TRANSPORT_core_receive_continue (struct GNUNET_TRANSPORT_CoreHandle *ch, - const struct GNUNET_PeerIdentity *pid) -{ - struct GNUNET_MQ_Envelope *env; - struct RecvOkMessage *rok; - - LOG (GNUNET_ERROR_TYPE_DEBUG, - "Message for %s finished CORE processing, sending RECV_OK.\n", - GNUNET_i2s (pid)); - if (NULL == ch->mq) - return; - env = GNUNET_MQ_msg (rok, GNUNET_MESSAGE_TYPE_TRANSPORT_RECV_OK); - rok->increase_window_delta = htonl (1); - rok->peer = *pid; - GNUNET_MQ_send (ch->mq, env); -} - - -/** - * Connect to the transport service. Note that the connection may - * complete (or fail) asynchronously. - * - * @param cfg configuration to use - * @param self our own identity (API should check that it matches - * the identity found by transport), or NULL (no check) - * @param cls closure for the callbacks - * @param rec receive function to call - * @param nc function to call on connect events - * @param nd function to call on disconnect events - * @return NULL on error - */ -struct GNUNET_TRANSPORT_CoreHandle * -GNUNET_TRANSPORT_core_connect (const struct GNUNET_CONFIGURATION_Handle *cfg, - const struct GNUNET_PeerIdentity *self, - const struct GNUNET_MQ_MessageHandler *handlers, - void *cls, - GNUNET_TRANSPORT_NotifyConnect nc, - GNUNET_TRANSPORT_NotifyDisconnect nd) -{ - struct GNUNET_TRANSPORT_CoreHandle *h; - unsigned int i; - - h = GNUNET_new (struct GNUNET_TRANSPORT_CoreHandle); - if (NULL != self) - { - h->self = *self; - h->check_self = GNUNET_YES; - } - h->cfg = cfg; - h->cls = cls; - h->nc_cb = nc; - h->nd_cb = nd; - h->reconnect_delay = GNUNET_TIME_UNIT_ZERO; - if (NULL != handlers) - { - for (i = 0; NULL != handlers[i].cb; i++) - ; - h->handlers = GNUNET_new_array (i + 1, struct GNUNET_MQ_MessageHandler); - GNUNET_memcpy (h->handlers, - handlers, - i * sizeof(struct GNUNET_MQ_MessageHandler)); - } - LOG (GNUNET_ERROR_TYPE_DEBUG, "Connecting to transport service\n"); - reconnect (h); - if (NULL == h->mq) - { - GNUNET_free (h->handlers); - GNUNET_free (h); - return NULL; - } - h->neighbours = - GNUNET_CONTAINER_multipeermap_create (STARTING_NEIGHBOURS_SIZE, GNUNET_YES); - return h; -} - - -/** - * Disconnect from the transport service. - * - * @param handle handle to the service as returned from - * #GNUNET_TRANSPORT_core_connect() - */ -void -GNUNET_TRANSPORT_core_disconnect (struct GNUNET_TRANSPORT_CoreHandle *handle) -{ - LOG (GNUNET_ERROR_TYPE_DEBUG, "Transport disconnect called!\n"); - /* this disconnects all neighbours... */ - disconnect (handle); - /* and now we stop trying to connect again... */ - if (NULL != handle->reconnect_task) - { - GNUNET_SCHEDULER_cancel (handle->reconnect_task); - handle->reconnect_task = NULL; - } - GNUNET_CONTAINER_multipeermap_destroy (handle->neighbours); - handle->neighbours = NULL; - GNUNET_free (handle->handlers); - handle->handlers = NULL; - GNUNET_free (handle); -} - - -/* end of transport_api_core.c */ diff --git a/src/transport/transport_api2_monitor.c b/src/transport/transport_api2_monitor.c deleted file mode 100644 index 67aa1985e..000000000 --- a/src/transport/transport_api2_monitor.c +++ /dev/null @@ -1,292 +0,0 @@ -/* - This file is part of GNUnet. - Copyright (C) 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 . - - SPDX-License-Identifier: AGPL3.0-or-later - */ - -/** - * @file transport/transport_api2_monitor.c - * @brief implementation of the gnunet_transport_monitor_service.h API - * @author Christian Grothoff - */ -#include "platform.h" -#include "gnunet_util_lib.h" -#include "gnunet_protocols.h" -#include "gnunet_transport_monitor_service.h" -#include "transport.h" - - -/** - * Opaque handle to the transport service for monitors. - */ -struct GNUNET_TRANSPORT_MonitorContext -{ - /** - * Our configuration. - */ - const struct GNUNET_CONFIGURATION_Handle *cfg; - - /** - * Queue to talk to the transport service. - */ - struct GNUNET_MQ_Handle *mq; - - /** - * Peer we monitor, all zeros for "all" - */ - struct GNUNET_PeerIdentity peer; - - /** - * #GNUNET_YES to return the current state and then end. - */ - int one_shot; - - /** - * Function to call with monitor data. - */ - GNUNET_TRANSPORT_MonitorCallback cb; - - /** - * Closure for @e cb. - */ - void *cb_cls; -}; - - -/** - * (re)connect our monitor to the transport service - * - * @param mc handle to reconnect - */ -static void -reconnect (struct GNUNET_TRANSPORT_MonitorContext *mc); - - -/** - * Send message to the transport service about our montoring - * desire. - * - * @param ai address to delete - */ -static void -send_start_monitor (struct GNUNET_TRANSPORT_MonitorContext *mc) -{ - struct GNUNET_MQ_Envelope *env; - struct GNUNET_TRANSPORT_MonitorStart *smm; - - if (NULL == mc->mq) - return; - env = GNUNET_MQ_msg (smm, GNUNET_MESSAGE_TYPE_TRANSPORT_MONITOR_START); - smm->one_shot = htonl ((uint32_t) mc->one_shot); - smm->peer = mc->peer; - GNUNET_MQ_send (mc->mq, env); -} - - -/** - * Disconnect from the transport service. - * - * @param mc service to disconnect from - */ -static void -disconnect (struct GNUNET_TRANSPORT_MonitorContext *mc) -{ - if (NULL == mc->mq) - return; - GNUNET_MQ_destroy (mc->mq); - mc->mq = NULL; -} - - -/** - * Function called on MQ errors. Reconnects to the service. - * - * @param cls our `struct GNUNET_TRANSPORT_MonitorContext *` - * @param error what error happened? - */ -static void -error_handler (void *cls, enum GNUNET_MQ_Error error) -{ - struct GNUNET_TRANSPORT_MonitorContext *mc = cls; - - GNUNET_log (GNUNET_ERROR_TYPE_INFO, - "MQ failure %d, reconnecting to transport service.\n", - error); - disconnect (mc); - /* TODO: maybe do this with exponential backoff/delay */ - reconnect (mc); -} - - -/** - * Transport service sends us information about what is going on. - * Check if @a md is well-formed. - * - * @param cls our `struct GNUNET_TRANSPORT_MonitorContext *` - * @param md the monitor data we got - * @return #GNUNET_OK if @a smt is well-formed - */ -static int -check_monitor_data (void *cls, const struct GNUNET_TRANSPORT_MonitorData *md) -{ - (void) cls; - GNUNET_MQ_check_zero_termination (md); - return GNUNET_OK; -} - - -/** - * Transport service sends us information about what is going on. - * - * @param cls our `struct GNUNET_TRANSPORT_MonitorContext *` - * @param md monitor data - */ -static void -handle_monitor_data (void *cls, const struct GNUNET_TRANSPORT_MonitorData *md) -{ - struct GNUNET_TRANSPORT_MonitorContext *mc = cls; - struct GNUNET_TRANSPORT_MonitorInformation mi; - - mi.address = (const char *) &md[1]; - mi.nt = (enum GNUNET_NetworkType) ntohl (md->nt); - mi.cs = (enum GNUNET_TRANSPORT_ConnectionStatus) ntohl (md->cs); - mi.num_msg_pending = ntohl (md->num_msg_pending); - mi.num_bytes_pending = ntohl (md->num_bytes_pending); - mi.last_validation = GNUNET_TIME_absolute_ntoh (md->last_validation); - mi.valid_until = GNUNET_TIME_absolute_ntoh (md->valid_until); - mi.next_validation = GNUNET_TIME_absolute_ntoh (md->next_validation); - mi.rtt = GNUNET_TIME_relative_ntoh (md->rtt); - mc->cb (mc->cb_cls, &md->peer, &mi); -} - - -/** - * One shot was requested, and transport service is done. - * - * @param cls our `struct GNUNET_TRANSPORT_MonitorContext *` - * @param me end message - */ -static void -handle_monitor_end (void *cls, const struct GNUNET_MessageHeader *me) -{ - struct GNUNET_TRANSPORT_MonitorContext *mc = cls; - - if (GNUNET_YES != mc->one_shot) - { - GNUNET_break (0); - disconnect (mc); - reconnect (mc); - return; - } - mc->cb (mc->cb_cls, NULL, NULL); - GNUNET_TRANSPORT_monitor_cancel (mc); -} - - -/** - * (re)connect our monitor to the transport service - * - * @param mc handle to reconnect - */ -static void -reconnect (struct GNUNET_TRANSPORT_MonitorContext *mc) -{ - struct GNUNET_MQ_MessageHandler handlers[] = - { GNUNET_MQ_hd_var_size (monitor_data, - GNUNET_MESSAGE_TYPE_TRANSPORT_MONITOR_DATA, - struct GNUNET_TRANSPORT_MonitorData, - mc), - GNUNET_MQ_hd_fixed_size (monitor_end, - GNUNET_MESSAGE_TYPE_TRANSPORT_MONITOR_END, - struct GNUNET_MessageHeader, - mc), - GNUNET_MQ_handler_end () }; - - mc->mq = - GNUNET_CLIENT_connect (mc->cfg, "transport", handlers, &error_handler, mc); - if (NULL == mc->mq) - return; - send_start_monitor (mc); -} - - -/** - * Return information about a specific peer or all peers currently known to - * transport service once or in monitoring mode. To obtain information about - * a specific peer, a peer identity can be passed. To obtain information about - * all peers currently known to transport service, NULL can be passed as peer - * identity. - * - * For each peer, the callback is called with information about the address used - * to communicate with this peer, the state this peer is currently in and the - * the current timeout for this state. - * - * Upon completion, the #GNUNET_TRANSPORT_PeerIterateCallback is called one - * more time with `NULL`. After this, the operation must no longer be - * explicitly canceled. - * - * The #GNUNET_TRANSPORT_monitor_peers_cancel call MUST not be called in the - * the peer_callback! - * - * @param cfg configuration to use - * @param peer a specific peer identity to obtain information for, - * NULL for all peers - * @param one_shot #GNUNET_YES to return the current state and then end (with NULL+NULL), - * #GNUNET_NO to monitor peers continuously - * @param cb function to call with the results - * @param cb_cls closure for @a mc - */ -struct GNUNET_TRANSPORT_MonitorContext * -GNUNET_TRANSPORT_monitor (const struct GNUNET_CONFIGURATION_Handle *cfg, - const struct GNUNET_PeerIdentity *peer, - int one_shot, - GNUNET_TRANSPORT_MonitorCallback cb, - void *cb_cls) -{ - struct GNUNET_TRANSPORT_MonitorContext *mc; - - mc = GNUNET_new (struct GNUNET_TRANSPORT_MonitorContext); - mc->cfg = cfg; - if (NULL != peer) - mc->peer = *peer; - mc->one_shot = one_shot; - mc->cb = cb; - mc->cb_cls = cb_cls; - reconnect (mc); - if (NULL == mc->mq) - { - GNUNET_free (mc); - return NULL; - } - return mc; -} - - -/** - * Cancel request to monitor peers - * - * @param pmc handle for the request to cancel - */ -void -GNUNET_TRANSPORT_monitor_cancel (struct GNUNET_TRANSPORT_MonitorContext *mc) -{ - disconnect (mc); - GNUNET_free (mc); -} - - -/* end of transport_api2_monitor.c */ diff --git a/src/transport/transport_api_cmd_backchannel_check.c b/src/transport/transport_api_cmd_backchannel_check.c deleted file mode 100644 index 68bdae69c..000000000 --- a/src/transport/transport_api_cmd_backchannel_check.c +++ /dev/null @@ -1,554 +0,0 @@ -/* - This file is part of GNUnet - Copyright (C) 2021 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 . - - SPDX-License-Identifier: AGPL3.0-or-later - */ - -/** - * @file testing_api_cmd_backchannel_check.c - * @brief cmd to start a peer. - * @author t3sserakt - */ -#include "platform.h" -#include "gnunet_common.h" -#include "gnunet_util_lib.h" -#include "gnunet_testing_ng_lib.h" -#include "gnunet_testing_netjail_lib.h" -#include "gnunet_transport_application_service.h" -#include "gnunet_hello_lib.h" -#include "transport-testing-cmds.h" - -/** - * Generic logging shortcut - */ -#define LOG(kind, ...) GNUNET_log_from (kind, "udp-backchannel",__VA_ARGS__) - -#define UDP "udp" - -/** - * Maximum length allowed for line input. - */ -#define MAX_LINE_LENGTH 1024 - -/** - * Struct to store information needed in callbacks. - * - */ -struct CheckState -{ - /** - * Context for our asynchronous completion. - */ - struct GNUNET_TESTING_AsyncContext ac; - - /** - * The number of the node in a network namespace. - */ - unsigned int node_n; - - /** - * The number of the network namespace. - */ - unsigned int namespace_n; - - /** - * The testing system of this node. - */ - const struct GNUNET_TESTING_System *tl_system; - - // Label of the cmd which started the test system. - const char *create_label; - - /** - * Number globally identifying the node. - * - */ - uint32_t num; - - /** - * Label of the cmd to start a peer. - * - */ - const char *start_peer_label; - - /** - * The topology of the test setup. - */ - struct GNUNET_TESTING_NetjailTopology *topology; - - /** - * Connections to other peers. - */ - struct GNUNET_TESTING_NodeConnection *node_connections_head; - - /** - * Number of connections. - */ - unsigned int con_num; - - /** - * Number of received backchannel messages. - */ - unsigned int received_backchannel_msgs; - - /** - * Array with search strings. - */ - char **search_string; - - /** - * File handle for log file. - */ - struct GNUNET_DISK_FileHandle *fh; - - /** - * Task which handles the reading - */ - struct GNUNET_SCHEDULER_Task *task; - - /** - * Stream to read log file lines. - */ - FILE *stream; -}; - -/** - * - * @param cls The cmd state CheckState. - */ -static void -read_from_log (void *cls) -{ - struct CheckState *cs = cls; - char line[MAX_LINE_LENGTH + 1]; - char *search_string; - - - LOG (GNUNET_ERROR_TYPE_DEBUG, - "read_from_log\n"); - - cs->fh = GNUNET_DISK_file_open ("test.out", - GNUNET_DISK_OPEN_READ, - GNUNET_DISK_PERM_USER_READ); - - cs->task = NULL; - - /* read message from line and handle it */ - cs->stream = fdopen (cs->fh->fd, "r"); - memset (line, 0, MAX_LINE_LENGTH + 1); - - // fgets (line, MAX_LINE_LENGTH, cs->stream); - // while (NULL != line && 0 != strcmp (line, ""))// '\0' != line[0]) - while (NULL != fgets (line, MAX_LINE_LENGTH, cs->stream)) - { - /*LOG (GNUNET_ERROR_TYPE_DEBUG, - "cs->received_backchannel_msgs: %u\n", - cs->received_backchannel_msgs);*/ - /*if (NULL == strstr (line, "line")) - LOG (GNUNET_ERROR_TYPE_DEBUG, - "line: %s", - line);*/ - - - for (int i = 0; i < cs->con_num; i++) - { - search_string = cs->search_string[i]; - /*LOG (GNUNET_ERROR_TYPE_DEBUG, - "search %u %u: %s %p\n", - i, - cs->con_num, - cs->search_string[i], - cs->search_string); - fprintf (stderr, - line);*/ - if (NULL != strstr (line, - search_string)) - // "Delivering backchannel message from 4TTC to F7B5 of type 1460 to udp")) - // cs->search_string[i])) - { - cs->received_backchannel_msgs++; - LOG (GNUNET_ERROR_TYPE_DEBUG, - "received_backchannel_msgs %u con_num %u\n", - cs->received_backchannel_msgs, - cs->con_num); - if (cs->received_backchannel_msgs == cs->con_num) - { - LOG (GNUNET_ERROR_TYPE_DEBUG, - "search finished %lu %lu %u\n", - strlen (cs->search_string[i]), - strlen ( - "Delivering backchannel message from 4TTC to F7B5 of type 1460 to udp"), - strcmp ( - "Delivering backchannel message from 4TTC to F7B5 of type 1460 to udp", - cs->search_string[i])); - GNUNET_TESTING_async_finish (&cs->ac); - fclose (cs->stream); - return; - } - } - } - } - LOG (GNUNET_ERROR_TYPE_DEBUG, - "read_from_log end\n"); - fclose (cs->stream); - GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_UNIT_MINUTES, - &read_from_log, - cs); - /*if (NULL == fgets (line, MAX_LINE_LENGTH, cs->stream)) - { - LOG (GNUNET_ERROR_TYPE_DEBUG, - "read null\n"); - GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_UNIT_SECONDS, - &read_from_log, - cs); - return; - }*/ - /*else { - cs->task = - GNUNET_SCHEDULER_add_read_file (GNUNET_TIME_UNIT_FOREVER_REL, - cs->fh, - &read_from_log, - cs); - - - }*/ -} - - -static enum GNUNET_GenericReturnValue -will_the_other_node_connect_via_udp ( - struct CheckState *cs, - const struct GNUNET_TESTING_NetjailNode *node) -// struct GNUNET_TESTING_NodeConnection *connection) -{ - // struct GNUNET_TESTING_NetjailTopology *topology = cs->topology; - // unsigned int node_n = connection->node_n; - // unsigned int namespace_n = connection->namespace_n; - // struct GNUNET_HashCode hc; - // struct GNUNET_ShortHashCode *key = GNUNET_new (struct GNUNET_ShortHashCode); - // struct GNUNET_HashCode hc_namespace; - /*struct GNUNET_ShortHashCode *key_namespace = GNUNET_new (struct - GNUNET_ShortHashCode);*/ - // struct GNUNET_TESTING_NetjailNode *node; - struct GNUNET_TESTING_NodeConnection *pos_connection; - struct GNUNET_TESTING_AddressPrefix *pos_prefix; - // struct GNUNET_TESTING_NetjailNamespace *namespace; - // struct GNUNET_CONTAINER_MultiShortmap *map; - - /* if (0 == connection->namespace_n) */ - /* { */ - /* map = topology->map_globals; */ - /* } */ - /* else */ - /* { */ - /* GNUNET_CRYPTO_hash (&namespace_n, sizeof(namespace_n), &hc_namespace); */ - /* memcpy (key_namespace, */ - /* &hc_namespace, */ - /* sizeof (*key_namespace)); */ - /* if (GNUNET_YES == GNUNET_CONTAINER_multishortmap_contains ( */ - /* topology->map_namespaces, */ - /* key_namespace)) */ - /* { */ - /* namespace = GNUNET_CONTAINER_multishortmap_get (topology->map_namespaces, */ - /* key_namespace); */ - /* map = namespace->nodes; */ - /* } */ - /* else */ - /* GNUNET_assert (0); */ - /* } */ - - /* GNUNET_CRYPTO_hash (&node_n, sizeof(node_n), &hc); */ - /* memcpy (key, */ - /* &hc, */ - /* sizeof (*key)); */ - /* if (GNUNET_YES == GNUNET_CONTAINER_multishortmap_contains ( */ - /* map, */ - /* key)) */ - /* { */ - /* node = GNUNET_CONTAINER_multishortmap_get (cs->topology->map_globals, */ - /* key); */ - /* for (pos_connection = node->node_connections_head; NULL != pos_connection; */ - /* pos_connection = pos_connection->next) */ - /* { */ - /* if ((node->namespace_n == pos_connection->namespace_n) && */ - /* (node->node_n == pos_connection->node_n) ) */ - /* { */ - /* for (pos_prefix = pos_connection->address_prefixes_head; NULL != */ - /* pos_prefix; */ - /* pos_prefix = */ - /* pos_prefix->next) */ - /* { */ - /* if (0 == strcmp (UDP, pos_prefix->address_prefix)) */ - /* { */ - /* return GNUNET_YES; */ - /* } */ - /* } */ - /* } */ - /* } */ - /* } */ - - for (pos_connection = node->node_connections_head; NULL != pos_connection; - pos_connection = pos_connection->next) - { - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "connect via udp %u %u %u %u\n", - node->namespace_n, - cs->namespace_n, - node->node_n, - cs->node_n); - if ((pos_connection->namespace_n == cs->namespace_n) && - (pos_connection->node_n == cs->node_n) ) - { - for (pos_prefix = pos_connection->address_prefixes_head; NULL != - pos_prefix; - pos_prefix = - pos_prefix->next) - { - if (0 == strcmp (UDP, pos_prefix->address_prefix)) - { - return GNUNET_YES; - } - } - } - } - - return GNUNET_NO; -} - - -static void -add_search_string (struct CheckState *cs, const struct - GNUNET_TESTING_NetjailNode *node) -{ - unsigned int num; - struct GNUNET_PeerIdentity *peer; - struct GNUNET_PeerIdentity *us; - char *buf; - char *part_one = "Delivering backchannel message from "; - char *part_two = " to "; - char *part_three = " of type 1460 to udp"; - char *peer_id; - char *us_id; - - if (0 == node->namespace_n) - num = node->node_n; - else - num = (node->namespace_n - 1) * cs->topology->nodes_m + node->node_n - + cs->topology->nodes_x; - - // num = GNUNET_TESTING_calculate_num (pos_connection, cs->topology); - peer = GNUNET_TESTING_get_peer (num, cs->tl_system); - LOG (GNUNET_ERROR_TYPE_DEBUG, - "peer: %s num %u\n", - GNUNET_i2s (peer), - num); - us = GNUNET_TESTING_get_peer (cs->num, cs->tl_system); - LOG (GNUNET_ERROR_TYPE_DEBUG, - "us: %s cs->num %d\n", - GNUNET_i2s (us), - cs->num); - - GNUNET_asprintf (&peer_id, - "%s", - GNUNET_i2s (peer)); - GNUNET_asprintf (&us_id, - "%s", - GNUNET_i2s (us)); - - if (0 < GNUNET_asprintf (&buf, - "%s%s%s%s%s", - part_one, - us_id, - part_two, - peer_id, - part_three)) - { - GNUNET_array_append (cs->search_string, - cs->con_num, - buf); - /*LOG (GNUNET_ERROR_TYPE_DEBUG, - "con_num: %u search: %s %p\n", - cs->con_num, - cs->search_string[cs->con_num - 1], - cs->search_string);*/ - } - else - GNUNET_assert (0); - GNUNET_free (peer); - GNUNET_free (us); -} - - -/** - * The run method of this cmd will connect to peers. - * - */ -static void -backchannel_check_run (void *cls, - struct GNUNET_TESTING_Interpreter *is) -{ - struct CheckState *cs = cls; - const struct GNUNET_TESTING_Command *system_cmd; - const struct GNUNET_TESTING_System *tl_system; - const struct GNUNET_TESTING_Command *peer1_cmd; - const struct GNUNET_TRANSPORT_ApplicationHandle *ah; - struct GNUNET_CONTAINER_MultiShortmapIterator *node_it; - struct GNUNET_CONTAINER_MultiShortmapIterator *namespace_it; - struct GNUNET_ShortHashCode node_key; - struct GNUNET_ShortHashCode namespace_key; - const struct GNUNET_TESTING_NetjailNode *node; - const struct GNUNET_TESTING_NetjailNamespace *namespace; - - LOG (GNUNET_ERROR_TYPE_DEBUG, - "check run 1\n"); - - peer1_cmd = GNUNET_TESTING_interpreter_lookup_command (is, - cs->start_peer_label); - GNUNET_TRANSPORT_get_trait_application_handle (peer1_cmd, - &ah); - - system_cmd = GNUNET_TESTING_interpreter_lookup_command (is, - cs->create_label); - GNUNET_TESTING_get_trait_test_system (system_cmd, - &tl_system); - - cs->tl_system = tl_system; - - cs->node_connections_head = GNUNET_TESTING_get_connections (cs->num, - cs->topology); - - LOG (GNUNET_ERROR_TYPE_DEBUG, - "check run 2\n"); - - - node_it = GNUNET_CONTAINER_multishortmap_iterator_create ( - cs->topology->map_globals); - - while (GNUNET_YES == GNUNET_CONTAINER_multishortmap_iterator_next (node_it, - &node_key, - (const - void**) & - node)) - { - LOG (GNUNET_ERROR_TYPE_DEBUG, - "namespace_n %u node_n %u\n", - node->namespace_n, - node->node_n); - if (GNUNET_YES == will_the_other_node_connect_via_udp (cs, node)) - { - add_search_string (cs, node); - } - } - GNUNET_free (node_it); - namespace_it = GNUNET_CONTAINER_multishortmap_iterator_create ( - cs->topology->map_namespaces); - while (GNUNET_YES == GNUNET_CONTAINER_multishortmap_iterator_next ( - namespace_it, - &namespace_key, - (const - void**) &namespace)) - { - LOG (GNUNET_ERROR_TYPE_DEBUG, - "namespace_n %u\n", - node->namespace_n); - node_it = GNUNET_CONTAINER_multishortmap_iterator_create ( - namespace->nodes); - while (GNUNET_YES == GNUNET_CONTAINER_multishortmap_iterator_next (node_it, - &node_key, - (const - void**) - &node)) - { - LOG (GNUNET_ERROR_TYPE_DEBUG, - "namespace_n %u node_n %u\n", - node->namespace_n, - node->node_n); - if (GNUNET_YES == will_the_other_node_connect_via_udp (cs, node)) - { - add_search_string (cs, node); - } - } - GNUNET_free (node_it); - } - - if (0 != cs->con_num) - { - cs->task = - GNUNET_SCHEDULER_add_now (&read_from_log, - cs); - } - else - GNUNET_TESTING_async_finish (&cs->ac); - - GNUNET_free (namespace_it); -} - - -/** - * Trait function of this cmd does nothing. - * - */ -static int -backchannel_check_traits (void *cls, - const void **ret, - const char *trait, - unsigned int index) -{ - return GNUNET_OK; -} - - -/** - * The cleanup function of this cmd frees resources the cmd allocated. - * - */ -static void -backchannel_check_cleanup (void *cls) -{ - struct ConnectPeersState *cs = cls; - - GNUNET_free (cs); -} - - -struct GNUNET_TESTING_Command -GNUNET_TRANSPORT_cmd_backchannel_check (const char *label, - const char *start_peer_label, - const char *create_label, - uint32_t num, - unsigned int node_n, - unsigned int namespace_n, - struct GNUNET_TESTING_NetjailTopology * - topology) -{ - struct CheckState *cs; - - cs = GNUNET_new (struct CheckState); - cs->start_peer_label = start_peer_label; - cs->num = num; - cs->create_label = create_label; - cs->topology = topology; - cs->node_n = node_n; - cs->namespace_n = namespace_n; - - return GNUNET_TESTING_command_new (cs, - label, - &backchannel_check_run, - &backchannel_check_cleanup, - &backchannel_check_traits, - &cs->ac); -} diff --git a/src/transport/transport_api_cmd_connecting_peers.c b/src/transport/transport_api_cmd_connecting_peers.c deleted file mode 100644 index c59c4b006..000000000 --- a/src/transport/transport_api_cmd_connecting_peers.c +++ /dev/null @@ -1,311 +0,0 @@ -/* - This file is part of GNUnet - Copyright (C) 2021 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 . - - SPDX-License-Identifier: AGPL3.0-or-later - */ - -/** - * @file testing_api_cmd_start_peer.c - * @brief cmd to start a peer. - * @author t3sserakt - */ -#include "platform.h" -#include "gnunet_util_lib.h" -#include "gnunet_testing_ng_lib.h" -#include "gnunet_testing_netjail_lib.h" -#include "gnunet_transport_application_service.h" -#include "gnunet_hello_lib.h" -#include "transport-testing-cmds.h" - -/** - * Generic logging shortcut - */ -#define LOG(kind, ...) GNUNET_log (kind, __VA_ARGS__) - -/** - * The run method of this cmd will connect to peers. - * - */ -static void -connect_peers_run (void *cls, - struct GNUNET_TESTING_Interpreter *is) -{ - struct ConnectPeersState *cps = cls; - const struct GNUNET_TESTING_Command *system_cmd; - const struct GNUNET_TESTING_System *tl_system; - - - const struct GNUNET_TESTING_Command *peer1_cmd; - const struct GNUNET_TRANSPORT_ApplicationHandle *ah; - struct GNUNET_PeerIdentity *peer; - char *addr; - char *addr_and_port; - enum GNUNET_NetworkType nt = 0; - uint32_t num; - struct GNUNET_TESTING_NodeConnection *pos_connection; - struct GNUNET_TESTING_AddressPrefix *pos_prefix; - unsigned int con_num = 0; - const enum GNUNET_GenericReturnValue *broadcast; - - cps->is = is; - peer1_cmd = GNUNET_TESTING_interpreter_lookup_command (is, - cps->start_peer_label); - if (GNUNET_YES == cps->wait_for_connect) - { - LOG (GNUNET_ERROR_TYPE_DEBUG, - "Wait for connect.\n"); - GNUNET_TRANSPORT_get_trait_application_handle (peer1_cmd, - &ah); - } - else - { - LOG (GNUNET_ERROR_TYPE_DEBUG, - "Not waiting for connect.\n"); - GNUNET_TESTING_get_trait_application_handle (peer1_cmd, - &ah); - } - - GNUNET_TRANSPORT_get_trait_broadcast (peer1_cmd, - &broadcast); - - system_cmd = GNUNET_TESTING_interpreter_lookup_command (is, - cps->create_label); - GNUNET_TESTING_get_trait_test_system (system_cmd, - &tl_system); - - cps->tl_system = tl_system; - - LOG (GNUNET_ERROR_TYPE_DEBUG, - "cps->num: %u \n", - cps->num); - - cps->node_connections_head = GNUNET_TESTING_get_connections (cps->num, - cps->topology); - - for (pos_connection = cps->node_connections_head; NULL != pos_connection; - pos_connection = pos_connection->next) - { - con_num++; - num = GNUNET_TESTING_calculate_num (pos_connection, cps->topology); - for (pos_prefix = pos_connection->address_prefixes_head; NULL != pos_prefix; - pos_prefix = - pos_prefix->next) - { - addr = GNUNET_TESTING_get_address (pos_connection, - pos_prefix->address_prefix); - if (NULL != addr) - { - char *natted_p = strstr (pos_prefix->address_prefix, "_"); - - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "0 validating peer number %s %s %s\n", - natted_p, - pos_prefix->address_prefix, - addr); - if (0 == GNUNET_memcmp (pos_prefix->address_prefix, "udp")) - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "validating memcmp\n"); - if (GNUNET_YES == *broadcast) - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "validating broadcast\n"); - if ((0 == GNUNET_memcmp (pos_prefix->address_prefix, "udp")) && - (GNUNET_YES == *broadcast) ) - GNUNET_asprintf (&addr_and_port, - "%s:2086", - addr); - else if (NULL == natted_p) - GNUNET_asprintf (&addr_and_port, - "%s:60002", - addr); - else if (NULL != natted_p) - { - char *prefix; - char *rest; - char *rest2; - char *address; - - prefix = strtok (addr, "_"); - rest = strtok (NULL, "_"); - rest2 = strtok (rest, "-"); - address = strtok (NULL, "-"); - - GNUNET_asprintf (&addr_and_port, - "%s-%s:0", - prefix, - address); - - } - peer = GNUNET_TESTING_get_peer (num, tl_system); - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "validating peer number %u with identity %s and address %s %u %s and handle %p\n", - num, - GNUNET_i2s (peer), - addr_and_port, - *broadcast, - pos_prefix->address_prefix, - ah); - GNUNET_TRANSPORT_application_validate ((struct - GNUNET_TRANSPORT_ApplicationHandle - *) ah, - peer, - nt, - addr_and_port); - GNUNET_free (peer); - GNUNET_free (addr); - GNUNET_free (addr_and_port); - } - } - } - cps->con_num = con_num; -} - - -/** - * Callback from start peer cmd for signaling a peer got connected. - * - */ -static void * -notify_connect (struct GNUNET_TESTING_Interpreter *is, - const struct GNUNET_PeerIdentity *peer) -{ - const struct GNUNET_TESTING_Command *cmd; - struct ConnectPeersState *cps; - struct GNUNET_PeerIdentity *peer_connection; - unsigned int num; - unsigned int con_num; - void *ret = NULL; - - cmd = GNUNET_TESTING_interpreter_lookup_command_all (is, - "connect-peers"); - cps = cmd->cls; // WTF? Never go directly into cls of another command! FIXME! - con_num = cps->con_num_notified; - for (struct GNUNET_TESTING_NodeConnection *pos_connection = - cps->node_connections_head; - NULL != pos_connection; - pos_connection = pos_connection->next) - { - num = GNUNET_TESTING_calculate_num (pos_connection, - cps->topology); - peer_connection = GNUNET_TESTING_get_peer (num, - cps->tl_system); - if (0 == GNUNET_memcmp (peer, - peer_connection)) - cps->con_num_notified++; - GNUNET_free (peer_connection); - } - if (cps->con_num_notified == con_num) - cps->additional_connects_notified++; - - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "con_num: %u add: %u num_notified: %u add_notified: %u peer: %s\n", - cps->con_num, - cps->additional_connects, - cps->con_num_notified, - cps->additional_connects_notified, - GNUNET_i2s (peer)); - if ((cps->con_num == cps->con_num_notified) && - (cps->additional_connects <= cps->additional_connects_notified)) - { - GNUNET_TESTING_async_finish (&cps->ac); - } - return ret; -} - - -/** - * The cleanup function of this cmd frees resources the cmd allocated. - * - */ -static void -connect_peers_cleanup (void *cls) -{ - struct ConnectPeersState *cps = cls; - - GNUNET_free (cps); -} - - -/** - * This function prepares an array with traits. - * - */ -enum GNUNET_GenericReturnValue -connect_peers_traits (void *cls, - const void **ret, - const char *trait, - unsigned int index) -{ - struct ConnectPeersState *cps = cls; - struct GNUNET_TESTING_Trait traits[] = { - GNUNET_TRANSPORT_make_trait_connect_peer_state ((const void *) cps), - GNUNET_TESTING_trait_end () - }; - return GNUNET_TESTING_get_trait (traits, - ret, - trait, - index); -} - - -struct GNUNET_TESTING_Command -GNUNET_TRANSPORT_cmd_connect_peers (const char *label, - const char *start_peer_label, - const char *create_label, - uint32_t num, - struct GNUNET_TESTING_NetjailTopology * - topology, - unsigned int additional_connects, - unsigned int wait_for_connect) -{ - struct ConnectPeersState *cps; - unsigned int node_additional_connects; - - node_additional_connects = GNUNET_TESTING_get_additional_connects (num, - topology); - - LOG (GNUNET_ERROR_TYPE_DEBUG, - "global: %u and local: %u additional_connects\n", - additional_connects, - node_additional_connects); - - if (0 != node_additional_connects) - additional_connects = node_additional_connects; - - cps = GNUNET_new (struct ConnectPeersState); - cps->start_peer_label = start_peer_label; - cps->num = num; - cps->create_label = create_label; - cps->topology = topology; - cps->notify_connect = notify_connect; - cps->additional_connects = additional_connects; - cps->wait_for_connect = wait_for_connect; - - if (GNUNET_YES == wait_for_connect) - return GNUNET_TESTING_command_new (cps, - label, - &connect_peers_run, - &connect_peers_cleanup, - &connect_peers_traits, - &cps->ac); - else - return GNUNET_TESTING_command_new (cps, - label, - &connect_peers_run, - &connect_peers_cleanup, - &connect_peers_traits, - NULL); -} diff --git a/src/transport/transport_api_cmd_send_simple.c b/src/transport/transport_api_cmd_send_simple.c deleted file mode 100644 index 2671727c0..000000000 --- a/src/transport/transport_api_cmd_send_simple.c +++ /dev/null @@ -1,162 +0,0 @@ -/* - This file is part of GNUnet - Copyright (C) 2021 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 . - - SPDX-License-Identifier: AGPL3.0-or-later - */ - -/** - * @file testing_api_cmd_start_peer.c - * @brief cmd to start a peer. - * @author t3sserakt - */ -#include "platform.h" -#include "gnunet_util_lib.h" -#include "gnunet_testing_ng_lib.h" -#include "gnunet_testing_netjail_lib.h" -#include "transport-testing2.h" -#include "transport-testing-cmds.h" - -/** - * Generic logging shortcut - */ -#define LOG(kind, ...) GNUNET_log (kind, __VA_ARGS__) - -/** - * Struct to hold information for callbacks. - * - */ -struct SendSimpleState -{ - /** - * Number globally identifying the node. - * - */ - uint32_t num; - - /** - * Label of the cmd to start a peer. - * - */ - const char *start_peer_label; - - /** - * Label of the cmd which started the test system. - * - */ - const char *create_label; - - /** - * The topology we get the connected nodes from. - */ - struct GNUNET_TESTING_NetjailTopology *topology; -}; - - -/** - * The cleanup function of this cmd frees resources the cmd allocated. - * - */ -static void -send_simple_cleanup (void *cls) -{ - struct SendSimpleState *sss = cls; - - GNUNET_free (sss); -} - - -static int -send_simple_cb (void *cls, - const struct GNUNET_ShortHashCode *key, - void *value) -{ - struct SendSimpleState *sss = cls; - struct GNUNET_MQ_Handle *mq = value; - struct GNUNET_MQ_Envelope *env; - struct GNUNET_TRANSPORT_TESTING_TestMessage *test; - - LOG (GNUNET_ERROR_TYPE_DEBUG, - "Sending simple test message with mq %p\n", - mq); - - env = GNUNET_MQ_msg_extra (test, - 1000 - sizeof(*test), - GNUNET_TRANSPORT_TESTING_SIMPLE_MTYPE); - test->num = htonl (sss->num); - memset (&test[1], - sss->num, - 1000 - sizeof(*test)); - GNUNET_MQ_send (mq, - env); - return GNUNET_OK; -} - - -/** - * The run method of this cmd will send a simple message to the connected peers. - * - */ -static void -send_simple_run (void *cls, - struct GNUNET_TESTING_Interpreter *is) -{ - struct SendSimpleState *sss = cls; - const struct GNUNET_CONTAINER_MultiShortmap *connected_peers_map; - const struct GNUNET_TESTING_Command *peer1_cmd; - const struct GNUNET_TESTING_Command *system_cmd; - const struct GNUNET_TESTING_System *tl_system; - - peer1_cmd = GNUNET_TESTING_interpreter_lookup_command (is, - sss->start_peer_label); - GNUNET_TRANSPORT_get_trait_connected_peers_map (peer1_cmd, - &connected_peers_map); - - system_cmd = GNUNET_TESTING_interpreter_lookup_command (is, - sss->create_label); - GNUNET_TESTING_get_trait_test_system (system_cmd, - &tl_system); - - GNUNET_CONTAINER_multishortmap_iterate ( - (struct GNUNET_CONTAINER_MultiShortmap *) - connected_peers_map, send_simple_cb, - sss); -} - - -struct GNUNET_TESTING_Command -GNUNET_TRANSPORT_cmd_send_simple (const char *label, - const char *start_peer_label, - const char *create_label, - uint32_t num, - struct GNUNET_TESTING_NetjailTopology * - topology) -{ - struct SendSimpleState *sss; - - sss = GNUNET_new (struct SendSimpleState); - sss->num = num; - sss->start_peer_label = start_peer_label; - sss->create_label = create_label; - sss->topology = topology; - - return GNUNET_TESTING_command_new (sss, - label, - &send_simple_run, - &send_simple_cleanup, - NULL, - NULL); -} diff --git a/src/transport/transport_api_cmd_send_simple_performance.c b/src/transport/transport_api_cmd_send_simple_performance.c deleted file mode 100644 index 7ce3b8bf7..000000000 --- a/src/transport/transport_api_cmd_send_simple_performance.c +++ /dev/null @@ -1,220 +0,0 @@ -/* - This file is part of GNUnet - Copyright (C) 2021 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 . - - SPDX-License-Identifier: AGPL3.0-or-later - */ - -/** - * @file testing_api_cmd_start_peer.c - * @brief cmd to start a peer. - * @author t3sserakt - */ -#include "platform.h" -#include "gnunet_util_lib.h" -#include "gnunet_testing_ng_lib.h" -#include "gnunet_testing_netjail_lib.h" -#include "transport-testing2.h" -#include "transport-testing-cmds.h" - -/** - * Generic logging shortcut - */ -#define LOG(kind, ...) GNUNET_log (kind, __VA_ARGS__) - -/** - * Struct to hold information for callbacks. - * - */ -struct SendSimplePerfState -{ - /** - * Context for our asynchronous completion. - */ - struct GNUNET_TESTING_AsyncContext ac; - - /** - * Label of the cmd to start a peer. - * - */ - const char *start_peer_label; - - /** - * Label of the cmd which started the test system. - * - */ - const char *create_label; - - /** - * The topology we get the connected nodes from. - */ - struct GNUNET_TESTING_NetjailTopology *topology; - - /** - * Size of the message in bytes. - */ - unsigned int size; - - /** - * Maximum number of messages per peer. - */ - unsigned int max_send; -}; - -struct MQWrapper -{ - /** - * State of the command. - */ - struct SendSimplePerfState *sss; - - /** - * Message queue for a peer. - */ - struct GNUNET_MQ_Handle *mq; - - /** - * Number of messages allready send. - */ - uint32_t num_send; -}; - -/** - * The cleanup function of this cmd frees resources the cmd allocated. - * - */ -static void -send_simple_cleanup (void *cls) -{ - struct SendSimpleState *sss = cls; - - GNUNET_free (sss); -} - - -static void -send_simple_single (void *cls) -{ - struct MQWrapper *mq_wrapper = cls; - struct GNUNET_MQ_Envelope *env; - struct GNUNET_TRANSPORT_TESTING_PerformanceTestMessage *test; - struct GNUNET_TIME_Absolute now; - - now = GNUNET_TIME_absolute_get (); - mq_wrapper->num_send++; - LOG (GNUNET_ERROR_TYPE_DEBUG, - "Sending simple test message with size %u number %u with mq %p max %u\n", - mq_wrapper->sss->size, - mq_wrapper->num_send, - mq_wrapper->mq, - mq_wrapper->sss->max_send); - - env = GNUNET_MQ_msg_extra (test, - mq_wrapper->sss->size - sizeof(*test), - GNUNET_TRANSPORT_TESTING_SIMPLE_PERFORMANCE_MTYPE); - test->num = htonl (mq_wrapper->num_send); - test->time_send = GNUNET_TIME_absolute_hton (now); - memset (&test[1], - '1', - mq_wrapper->sss->size - sizeof(*test)); - GNUNET_MQ_send (mq_wrapper->mq, - env); - if (mq_wrapper->sss->max_send > mq_wrapper->num_send) - GNUNET_SCHEDULER_add_now (&send_simple_single, mq_wrapper); - else - GNUNET_TESTING_async_finish (&mq_wrapper->sss->ac); -} - - -static int -send_simple_cb (void *cls, - const struct GNUNET_ShortHashCode *key, - void *value) -{ - struct SendSimplePerfState *sss = cls; - struct GNUNET_MQ_Handle *mq = value; - struct MQWrapper *mq_wrapper = GNUNET_new (struct MQWrapper); - - mq_wrapper->sss = sss; - mq_wrapper->mq = mq; - send_simple_single (mq_wrapper); - - return GNUNET_OK; -} - - -/** - * The run method of this cmd will send a simple message to the connected peers. - * - */ -static void -send_simple_run (void *cls, - struct GNUNET_TESTING_Interpreter *is) -{ - struct SendSimplePerfState *sss = cls; - const struct GNUNET_CONTAINER_MultiShortmap *connected_peers_map; - const struct GNUNET_TESTING_Command *peer1_cmd; - const struct GNUNET_TESTING_Command *system_cmd; - const struct GNUNET_TESTING_System *tl_system; - - - peer1_cmd = GNUNET_TESTING_interpreter_lookup_command (is, - sss->start_peer_label); - GNUNET_TRANSPORT_get_trait_connected_peers_map (peer1_cmd, - &connected_peers_map); - - system_cmd = GNUNET_TESTING_interpreter_lookup_command (is, - sss->create_label); - GNUNET_TESTING_get_trait_test_system (system_cmd, - &tl_system); - - GNUNET_CONTAINER_multishortmap_iterate ( - (struct GNUNET_CONTAINER_MultiShortmap *) - connected_peers_map, send_simple_cb, - sss); -} - - -struct GNUNET_TESTING_Command -GNUNET_TRANSPORT_cmd_send_simple_performance (const char *label, - const char *start_peer_label, - const char *create_label, - uint32_t num, - int size, - int max_send, - struct - GNUNET_TESTING_NetjailTopology * - topology) -{ - struct SendSimplePerfState *sss; - struct GNUNET_TESTING_Command cmd; - - sss = GNUNET_new (struct SendSimplePerfState); - sss->start_peer_label = start_peer_label; - sss->create_label = create_label; - sss->topology = topology; - sss->size = size; - sss->max_send = max_send; - - cmd = GNUNET_TESTING_command_new (sss, - label, - &send_simple_run, - &send_simple_cleanup, - NULL, - &sss->ac); - cmd.asynchronous_finish = GNUNET_YES; - return cmd; -} diff --git a/src/transport/transport_api_cmd_start_peer.c b/src/transport/transport_api_cmd_start_peer.c deleted file mode 100644 index 54e204a21..000000000 --- a/src/transport/transport_api_cmd_start_peer.c +++ /dev/null @@ -1,483 +0,0 @@ -/* - This file is part of GNUnet - Copyright (C) 2021 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 . - - SPDX-License-Identifier: AGPL3.0-or-later - */ - -/** - * @file testing_api_cmd_start_peer.c - * @brief cmd to start a peer. - * @author t3sserakt - */ -#include "platform.h" -#include "gnunet_util_lib.h" -#include "gnunet_testing_ng_lib.h" -#include "gnunet_testing_netjail_lib.h" -#include "gnunet_peerstore_service.h" -#include "gnunet_transport_core_service.h" -#include "gnunet_transport_application_service.h" -#include "transport-testing-cmds.h" - -/** - * Generic logging shortcut - */ -#define LOG(kind, ...) GNUNET_log (kind, __VA_ARGS__) - - -static void -retrieve_hello (void *cls); - - -/** - * Callback delivering the hello of this peer from peerstore. - * - */ -static void -hello_iter_cb (void *cb_cls, - const struct GNUNET_PEERSTORE_Record *record, - const char *emsg) -{ - struct GNUNET_TESTING_StartPeerState *sps = cb_cls; - if (NULL == record) - { - sps->pic = NULL; - sps->rh_task = GNUNET_SCHEDULER_add_now (retrieve_hello, sps); - return; - } - // Check record type et al? - sps->hello_size = record->value_size; - sps->hello = GNUNET_malloc (sps->hello_size); - memcpy (sps->hello, record->value, sps->hello_size); - sps->hello[sps->hello_size - 1] = '\0'; - - LOG (GNUNET_ERROR_TYPE_DEBUG, - "Our hello %s\n", - sps->hello); - GNUNET_PEERSTORE_iterate_cancel (sps->pic); - sps->pic = NULL; - GNUNET_TESTING_async_finish (&sps->ac); -} - - -/** - * Function to start the retrieval task to retrieve the hello of this peer - * from the peerstore. - * - */ -static void -retrieve_hello (void *cls) -{ - struct GNUNET_TESTING_StartPeerState *sps = cls; - sps->rh_task = NULL; - sps->pic = GNUNET_PEERSTORE_iterate (sps->ph, - "transport", - &sps->id, - GNUNET_PEERSTORE_TRANSPORT_HELLO_KEY, - hello_iter_cb, - sps); - -} - - -/** - * Disconnect callback for the connection to the core service. - * - */ -static void -notify_disconnect (void *cls, - const struct GNUNET_PeerIdentity *peer, - void *handler_cls) -{ - struct GNUNET_TESTING_StartPeerState *sps = cls; - - LOG (GNUNET_ERROR_TYPE_DEBUG, - "Peer %s disconnected from peer %u (`%s')\n", - GNUNET_i2s (peer), - sps->no, - GNUNET_i2s (&sps->id)); - -} - - -/** - * Connect callback for the connection to the core service. - * - */ -static void * -notify_connect (void *cls, - const struct GNUNET_PeerIdentity *peer, - struct GNUNET_MQ_Handle *mq) -{ - struct GNUNET_TESTING_StartPeerState *sps = cls; - struct GNUNET_ShortHashCode *key = GNUNET_new (struct GNUNET_ShortHashCode); - struct GNUNET_HashCode hc; - struct GNUNET_CRYPTO_EddsaPublicKey public_key = peer->public_key; - - void *ret = (struct GNUNET_PeerIdentity *) peer; - - LOG (GNUNET_ERROR_TYPE_DEBUG, - "This Peer %s \n", - GNUNET_i2s (&sps->id)); - - - GNUNET_CRYPTO_hash (&public_key, sizeof(public_key), &hc); - - LOG (GNUNET_ERROR_TYPE_DEBUG, - "Peer %s connected to peer number %u with mq %p\n", - GNUNET_i2s (peer), - sps->no, - mq); - - - memcpy (key, - &hc, - sizeof (*key)); - GNUNET_CONTAINER_multishortmap_put (sps->connected_peers_map, - key, - mq, - GNUNET_CONTAINER_MULTIHASHMAPOPTION_MULTIPLE); - - GNUNET_free (key); - - sps->notify_connect (sps->ac.is, - peer); - - return ret; -} - - -/** - * The run method of this cmd will start all services of a peer to test the transport service. - * - */ -static void -start_peer_run (void *cls, - struct GNUNET_TESTING_Interpreter *is) -{ - struct GNUNET_TESTING_StartPeerState *sps = cls; - char *emsg = NULL; - struct GNUNET_PeerIdentity dummy; - const struct GNUNET_TESTING_Command *system_cmd; - const struct GNUNET_TESTING_System *tl_system; - char *home; - char *transport_unix_path; - char *tcp_communicator_unix_path; - char *udp_communicator_unix_path; - char *bindto; - char *bindto_udp; - - if (GNUNET_NO == GNUNET_DISK_file_test (sps->cfgname)) - { - LOG (GNUNET_ERROR_TYPE_ERROR, - "File not found: `%s'\n", - sps->cfgname); - GNUNET_TESTING_interpreter_fail (is); - return; - } - - - sps->cfg = GNUNET_CONFIGURATION_create (); - GNUNET_assert (GNUNET_OK == - GNUNET_CONFIGURATION_load (sps->cfg, sps->cfgname)); - - GNUNET_asprintf (&home, - "$GNUNET_TMP/test-transport/api-tcp-p%u", - sps->no); - - GNUNET_asprintf (&transport_unix_path, - "$GNUNET_RUNTIME_DIR/tng-p%u.sock", - sps->no); - - GNUNET_asprintf (&tcp_communicator_unix_path, - "$GNUNET_RUNTIME_DIR/tcp-comm-p%u.sock", - sps->no); - - GNUNET_asprintf (&udp_communicator_unix_path, - "$GNUNET_RUNTIME_DIR/tcp-comm-p%u.sock", - sps->no); - - GNUNET_asprintf (&bindto, - "%s:60002", - sps->node_ip); - - GNUNET_asprintf (&bindto_udp, - "2086"); - - LOG (GNUNET_ERROR_TYPE_DEBUG, - "node_ip %s\n", - bindto); - - LOG (GNUNET_ERROR_TYPE_DEBUG, - "bind_udp %s\n", - GNUNET_YES == sps->broadcast ? - bindto_udp : bindto); - - GNUNET_CONFIGURATION_set_value_string (sps->cfg, "PATHS", "GNUNET_TEST_HOME", - home); - GNUNET_CONFIGURATION_set_value_string (sps->cfg, "transport", "UNIXPATH", - transport_unix_path); - GNUNET_CONFIGURATION_set_value_string (sps->cfg, "communicator-tcp", - "BINDTO", - bindto); - GNUNET_CONFIGURATION_set_value_string (sps->cfg, "communicator-udp", - "BINDTO", - GNUNET_YES == sps->broadcast ? - bindto_udp : bindto); - GNUNET_CONFIGURATION_set_value_string (sps->cfg, "communicator-tcp", - "UNIXPATH", - tcp_communicator_unix_path); - GNUNET_CONFIGURATION_set_value_string (sps->cfg, "communicator-udp", - "UNIXPATH", - udp_communicator_unix_path); - - - system_cmd = GNUNET_TESTING_interpreter_lookup_command (is, - sps->system_label); - GNUNET_TESTING_get_trait_test_system (system_cmd, - &tl_system); - - sps->tl_system = tl_system; - - LOG (GNUNET_ERROR_TYPE_DEBUG, - "Creating testing library with key number %u\n", - sps->no); - - if (GNUNET_SYSERR == - GNUNET_TESTING_configuration_create ((struct - GNUNET_TESTING_System *) tl_system, - sps->cfg)) - { - LOG (GNUNET_ERROR_TYPE_DEBUG, - "Testing library failed to create unique configuration based on `%s'\n", - sps->cfgname); - GNUNET_CONFIGURATION_destroy (sps->cfg); - GNUNET_TESTING_interpreter_fail (is); - return; - } - - sps->peer = GNUNET_TESTING_peer_configure ((struct - GNUNET_TESTING_System *) sps-> - tl_system, - sps->cfg, - sps->no, - NULL, - &emsg); - if (NULL == sps->peer) - { - LOG (GNUNET_ERROR_TYPE_ERROR, - "Testing library failed to create unique configuration based on `%s': `%s' with key number %u\n", - sps->cfgname, - emsg, - sps->no); - GNUNET_free (emsg); - GNUNET_TESTING_interpreter_fail (is); - return; - } - - if (GNUNET_OK != GNUNET_TESTING_peer_start (sps->peer)) - { - LOG (GNUNET_ERROR_TYPE_ERROR, - "Testing library failed to create unique configuration based on `%s'\n", - sps->cfgname); - GNUNET_free (emsg); - GNUNET_TESTING_interpreter_fail (is); - return; - } - - memset (&dummy, - '\0', - sizeof(dummy)); - - GNUNET_TESTING_peer_get_identity (sps->peer, - &sps->id); - - if (0 == memcmp (&dummy, - &sps->id, - sizeof(struct GNUNET_PeerIdentity))) - { - LOG (GNUNET_ERROR_TYPE_ERROR, - "Testing library failed to obtain peer identity for peer %u\n", - sps->no); - GNUNET_free (emsg); - GNUNET_TESTING_interpreter_fail (is); - return; - } - LOG (GNUNET_ERROR_TYPE_DEBUG, - "Peer %u configured with identity `%s'\n", - sps->no, - GNUNET_i2s_full (&sps->id)); - - sps->th = GNUNET_TRANSPORT_core_connect (sps->cfg, - NULL, - sps->handlers, - sps, - ¬ify_connect, - ¬ify_disconnect); - if (NULL == sps->th) - { - LOG (GNUNET_ERROR_TYPE_ERROR, - "Failed to connect to transport service for peer `%s': `%s'\n", - sps->cfgname, - emsg); - GNUNET_free (emsg); - GNUNET_TESTING_interpreter_fail (is); - return; - } - - sps->ph = GNUNET_PEERSTORE_connect (sps->cfg); - if (NULL == sps->ph) - { - LOG (GNUNET_ERROR_TYPE_ERROR, - "Failed to connect to peerstore service for peer `%s': `%s'\n", - sps->cfgname, - emsg); - GNUNET_free (emsg); - GNUNET_TESTING_interpreter_fail (is); - return; - } - - sps->ah = GNUNET_TRANSPORT_application_init (sps->cfg); - if (NULL == sps->ah) - { - LOG (GNUNET_ERROR_TYPE_ERROR, - "Failed to initialize the TRANSPORT application suggestion client handle for peer `%s': `%s'\n", - sps->cfgname, - emsg); - GNUNET_free (emsg); - GNUNET_TESTING_interpreter_fail (is); - return; - } - sps->rh_task = GNUNET_SCHEDULER_add_now (retrieve_hello, sps); - GNUNET_free (home); - GNUNET_free (transport_unix_path); - GNUNET_free (tcp_communicator_unix_path); - GNUNET_free (udp_communicator_unix_path); - GNUNET_free (bindto); - GNUNET_free (bindto_udp); -} - - -/** - * The cleanup function of this cmd frees resources the cmd allocated. - * - */ -static void -start_peer_cleanup (void *cls) -{ - struct GNUNET_TESTING_StartPeerState *sps = cls; - - if (NULL != sps->handlers) - { - GNUNET_free (sps->handlers); - sps->handlers = NULL; - } - // TODO Investigate why this caused problems during shutdown. - /*if (NULL != sps->cfg) - { - GNUNET_CONFIGURATION_destroy (sps->cfg); - sps->cfg = NULL; - }*/ - GNUNET_free (sps->cfgname); - GNUNET_free (sps->node_ip); - GNUNET_free (sps->system_label); - GNUNET_free (sps->hello); - GNUNET_free (sps->connected_peers_map); - GNUNET_free (sps); -} - - -/** - * This function prepares an array with traits. - * - */ -static int -start_peer_traits (void *cls, - const void **ret, - const char *trait, - unsigned int index) -{ - struct GNUNET_TESTING_StartPeerState *sps = cls; - struct GNUNET_TRANSPORT_ApplicationHandle *ah = sps->ah; - struct GNUNET_PeerIdentity *id = &sps->id; - struct GNUNET_CONTAINER_MultiShortmap *connected_peers_map = - sps->connected_peers_map; - char *hello = sps->hello; - size_t hello_size = sps->hello_size; - - - struct GNUNET_TESTING_Trait traits[] = { - GNUNET_TRANSPORT_make_trait_application_handle ((const void *) ah), - GNUNET_TRANSPORT_make_trait_peer_id ((const void *) id), - GNUNET_TRANSPORT_make_trait_connected_peers_map ((const - void *) - connected_peers_map), - GNUNET_TRANSPORT_make_trait_hello ((const void *) hello), - GNUNET_TRANSPORT_make_trait_hello_size ((const void *) hello_size), - GNUNET_TRANSPORT_make_trait_state ((const void *) sps), - GNUNET_TRANSPORT_make_trait_broadcast ((const void *) &sps->broadcast), - GNUNET_TESTING_trait_end () - }; - - return GNUNET_TESTING_get_trait (traits, - ret, - trait, - index); -} - - -struct GNUNET_TESTING_Command -GNUNET_TRANSPORT_cmd_start_peer (const char *label, - const char *system_label, - uint32_t no, - const char *node_ip, - struct GNUNET_MQ_MessageHandler *handlers, - const char *cfgname, - GNUNET_TRANSPORT_notify_connect_cb - notify_connect, - unsigned int broadcast) -{ - struct GNUNET_TESTING_StartPeerState *sps; - struct GNUNET_CONTAINER_MultiShortmap *connected_peers_map = - GNUNET_CONTAINER_multishortmap_create (1,GNUNET_NO); - unsigned int i; - - sps = GNUNET_new (struct GNUNET_TESTING_StartPeerState); - sps->no = no; - sps->system_label = GNUNET_strdup (system_label); - sps->connected_peers_map = connected_peers_map; - sps->cfgname = GNUNET_strdup (cfgname); - sps->node_ip = GNUNET_strdup (node_ip); - sps->notify_connect = notify_connect; - sps->broadcast = broadcast; - - if (NULL != handlers) - { - for (i = 0; NULL != handlers[i].cb; i++) - ; - sps->handlers = GNUNET_new_array (i + 1, - struct GNUNET_MQ_MessageHandler); - GNUNET_memcpy (sps->handlers, - handlers, - i * sizeof(struct GNUNET_MQ_MessageHandler)); - } - return GNUNET_TESTING_command_new (sps, - label, - &start_peer_run, - &start_peer_cleanup, - &start_peer_traits, - &sps->ac); -} diff --git a/src/transport/transport_api_cmd_stop_peer.c b/src/transport/transport_api_cmd_stop_peer.c deleted file mode 100644 index 60d48c56b..000000000 --- a/src/transport/transport_api_cmd_stop_peer.c +++ /dev/null @@ -1,154 +0,0 @@ -/* - This file is part of GNUnet - Copyright (C) 2021 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 . - - SPDX-License-Identifier: AGPL3.0-or-later - */ - -/** - * @file testing_api_cmd_stop_peer.c - * @brief cmd to stop a peer. - * @author t3sserakt - */ -#include "platform.h" -#include "gnunet_util_lib.h" -#include "gnunet_testing_ng_lib.h" -#include "gnunet_testing_netjail_lib.h" -#include "gnunet_peerstore_service.h" -#include "gnunet_transport_core_service.h" -#include "gnunet_transport_application_service.h" -#include "transport-testing-cmds.h" - -/** - * Generic logging shortcut - */ -#define LOG(kind, ...) GNUNET_log (kind, __VA_ARGS__) - - -/** - * Struct to hold information for callbacks. - * - */ -struct StopPeerState -{ - // Label of the cmd to start the peer. - const char *start_label; -}; - - -/** - * The run method of this cmd will stop all services of a peer which were used to test the transport service. - * - */ -static void -stop_peer_run (void *cls, - struct GNUNET_TESTING_Interpreter *is) -{ - struct StopPeerState *stop_ps = cls; - const struct GNUNET_TESTING_StartPeerState *sps; - const struct GNUNET_TESTING_Command *start_cmd; - - start_cmd = GNUNET_TESTING_interpreter_lookup_command (is, - stop_ps->start_label); - GNUNET_TRANSPORT_get_trait_state (start_cmd, - &sps); - - if (NULL != sps->pic) - { - GNUNET_PEERSTORE_iterate_cancel (sps->pic); - } - if (NULL != sps->th) - { - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "Disconnecting from TRANSPORT service\n"); - GNUNET_TRANSPORT_core_disconnect (sps->th); - } - if (NULL != sps->ah) - { - GNUNET_TRANSPORT_application_done (sps->ah); - } - if (NULL != sps->ph) - { - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "Disconnecting from PEERSTORE service\n"); - GNUNET_PEERSTORE_disconnect (sps->ph, GNUNET_NO); - } - if (NULL != sps->peer) - { - if (GNUNET_OK != - GNUNET_TESTING_peer_stop (sps->peer)) - { - LOG (GNUNET_ERROR_TYPE_ERROR, - "Testing lib failed to stop peer %u (`%s')\n", - sps->no, - GNUNET_i2s (&sps->id)); - } - GNUNET_TESTING_peer_destroy (sps->peer); - } - if (NULL != sps->rh_task) - GNUNET_SCHEDULER_cancel (sps->rh_task); -} - - -/** - * The cleanup function of this cmd frees resources the cmd allocated. - * - */ -static void -stop_peer_cleanup (void *cls) -{ - struct StopPeerState *sps = cls; - - GNUNET_free (sps); -} - - -/** - * Trait function of this cmd does nothing. - * - */ -static int -stop_peer_traits (void *cls, - const void **ret, - const char *trait, - unsigned int index) -{ - return GNUNET_OK; -} - - -/** - * Create command. - * - * @param label name for command. - * @param start_label Label of the cmd to start the peer. - * @return command. - */ -struct GNUNET_TESTING_Command -GNUNET_TRANSPORT_cmd_stop_peer (const char *label, - const char *start_label) -{ - struct StopPeerState *sps; - - sps = GNUNET_new (struct StopPeerState); - sps->start_label = start_label; - return GNUNET_TESTING_command_new (sps, - label, - &stop_peer_run, - &stop_peer_cleanup, - &stop_peer_traits, - NULL); -} diff --git a/src/transport/transport_api_traits.c b/src/transport/transport_api_traits.c deleted file mode 100644 index 7e66cc3d1..000000000 --- a/src/transport/transport_api_traits.c +++ /dev/null @@ -1,32 +0,0 @@ -/* - This file is part of GNUnet - Copyright (C) 2021 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 . - - SPDX-License-Identifier: AGPL3.0-or-later - */ - -/** - * @file transport/test_transport_start_with_config.c - * @brief Generic program to start testcases in an configurable topology. - * @author t3sserakt - */ -#include "platform.h" -#include "gnunet_testing_ng_lib.h" -#include "gnunet_testing_netjail_lib.h" -#include "transport-testing-cmds.h" -#include "gnunet_util_lib.h" - -GNUNET_TRANSPORT_SIMPLE_TRAITS (GNUNET_TRANSPORT_MAKE_IMPL_SIMPLE_TRAIT) diff --git a/src/transport/upnp.sh b/src/transport/upnp.sh deleted file mode 100755 index d01a1a1a1..000000000 --- a/src/transport/upnp.sh +++ /dev/null @@ -1,22 +0,0 @@ -#!/bin/bash - -if [ $2 -eq 1 ] -then - if [ ! -d /tmp/netjail_scripts ] - then - mkdir /tmp/netjail_scripts - fi - - ext_ifname=$(ip addr |grep UP|grep "@"|awk -F: '{printf $2"\n"}'|tr -d " "|awk -F@ '{printf $1" "}'|awk '{printf $1}') - listening_ip=$(ip addr |grep UP|grep "@"|awk -F: '{printf $2"\n"}'|tr -d " "|awk -F@ '{printf $1" "}'|awk '{printf $2}') - uuid=$(uuidgen) - cat miniupnpd.conf |sed 's/#ext_ifname=eth1/ext_ifname='$ext_ifname'/g'|sed 's/#listening_ip=eth0/listening_ip='$listening_ip'/g'|sed 's/uuid=73a9cb68-a00b-4d2c-8412-75fc989f0c6/uuid='$uuid'/g'|grep -v "^#"|grep -v '^$' > /tmp/netjail_scripts/gargoyle.txt - miniupnpd -d -f /tmp/netjail_scripts/gargoyle.txt -P /tmp/netjail_scripts/miniupnpd_$1.pid & -else - kill $(cat /tmp/netjail_scripts/miniupnpd_$1.pid) -fi - - - - - diff --git a/src/vpn/Makefile.am b/src/vpn/Makefile.am index 38a7e8b3f..2f102a3fd 100644 --- a/src/vpn/Makefile.am +++ b/src/vpn/Makefile.am @@ -39,7 +39,7 @@ gnunet_helper_vpn_LDADD = \ gnunet_service_vpn_SOURCES = \ gnunet-service-vpn.c gnunet_service_vpn_LDADD = \ - $(top_builddir)/src/statistics/libgnunetstatistics.la \ + $(top_builddir)/src/service/statistics/libgnunetstatistics.la \ $(top_builddir)/src/lib/util/libgnunetutil.la \ $(top_builddir)/src/cadet/libgnunetcadet.la \ $(top_builddir)/src/regex/libgnunetregex.la \ diff --git a/src/zonemaster/Makefile.am b/src/zonemaster/Makefile.am index b5fcb9d7b..5c10827b8 100644 --- a/src/zonemaster/Makefile.am +++ b/src/zonemaster/Makefile.am @@ -24,7 +24,7 @@ gnunet_service_zonemaster_LDADD = \ $(top_builddir)/src/dht/libgnunetdht.la \ $(top_builddir)/src/lib/gnsrecord/libgnunetgnsrecord.la \ $(top_builddir)/src/service/identity/libgnunetidentity.la \ - $(top_builddir)/src/statistics/libgnunetstatistics.la \ + $(top_builddir)/src/service/statistics/libgnunetstatistics.la \ $(top_builddir)/src/lib/util/libgnunetutil.la \ $(top_builddir)/src/namestore/libgnunetnamestore.la \ $(top_builddir)/src/namecache/libgnunetnamecache.la \ -- cgit v1.2.3