From 0a217a8df1657b4334b55b0e4a6c7837a8dbcfd9 Mon Sep 17 00:00:00 2001 From: Christian Grothoff Date: Fri, 29 May 2009 00:46:26 +0000 Subject: ng --- ABOUT-NLS | 1101 ++++ AUTHORS | 85 + COPYING | 674 +++ ChangeLog | 1704 ++++++ HACKING | 57 + INSTALL | 237 + Makefile.am | 14 + NEWS | 1 + RATIONALE | 246 + README | 257 + TODO | 150 + bin/grephdr.sh | 3 + bin/grepsrc.sh | 3 + bin/rename.sh | 6 + bootstrap | 7 + config.rpath | 614 +++ configure.ac | 821 +++ contrib/Makefile.am | 19 + contrib/config.scm | 1568 ++++++ contrib/coverage.sh | 13 + contrib/defaults.conf | 139 + contrib/gnunet-logo-big.png | Bin 0 -> 250489 bytes contrib/gnunet-logo-color.png | Bin 0 -> 6851 bytes contrib/hostlist.cgi | 5 + contrib/hostlist.php | 35 + contrib/report.sh | 200 + doc/man/Makefile.am | 7 + doc/man/gnunet-arm.1 | 57 + doc/man/gnunet-peerinfo.1 | 42 + doc/man/gnunet-statistics.1 | 50 + doc/man/gnunet-transport.1 | 96 + m4/Makefile.am | 1 + m4/align.m4 | 28 + po/ChangeLog | 16 + po/GNUnet.pot | 5168 ++++++++++++++++++ po/LINGUAS | 2 + po/Makefile.in.in | 403 ++ po/Makevars | 41 + po/Makevars.template | 41 + po/POTFILES.in | 0 po/Rules-quot | 47 + po/boldquot.sed | 10 + po/de.gmo | Bin 0 -> 40027 bytes po/de.po | 7387 ++++++++++++++++++++++++++ po/en@boldquot.header | 25 + po/en@quot.header | 22 + po/es.gmo | Bin 0 -> 31722 bytes po/es.po | 6921 ++++++++++++++++++++++++ po/insert-header.sin | 23 + po/quot.sed | 6 + po/remove-potcdate.sin | 19 + po/stamp-po | 1 + po/sv.gmo | Bin 0 -> 19833 bytes po/sv.po | 5885 ++++++++++++++++++++ po/update.pl | 230 + po/vi.gmo | Bin 0 -> 166871 bytes po/vi.po | 5772 ++++++++++++++++++++ pre-commit | 17 + src/Makefile.am | 17 + src/arm/Makefile.am | 58 + src/arm/arm.h | 32 + src/arm/arm_api.c | 337 ++ src/arm/gnunet-arm.c | 180 + src/arm/gnunet-service-arm.c | 712 +++ src/arm/test_arm_api.c | 140 + src/arm/test_arm_api_data.conf | 7 + src/arm/test_gnunet_arm.sh | 117 + src/core/Makefile.am | 68 + src/core/core.h | 308 ++ src/core/core_api.c | 1071 ++++ src/core/gnunet-service-core.c | 2859 ++++++++++ src/core/test_core_api.c | 368 ++ src/core/test_core_api_data.conf | 27 + src/core/test_core_api_peer1.conf | 40 + src/core/test_core_api_peer2.conf | 39 + src/core/test_core_api_start_only.c | 257 + src/datastore/plugin_datastore.h | 241 + src/fragmentation/Makefile.am | 28 + src/fragmentation/fragmentation.c | 702 +++ src/fragmentation/test_fragmentation.c | 439 ++ src/hello/Makefile.am | 28 + src/hello/hello.c | 482 ++ src/hello/test_hello.c | 185 + src/include/Makefile.am | 42 + src/include/gettext.h | 71 + src/include/gnunet_arm_service.h | 107 + src/include/gnunet_client_lib.h | 160 + src/include/gnunet_common.h | 469 ++ src/include/gnunet_configuration_lib.h | 238 + src/include/gnunet_container_lib.h | 784 +++ src/include/gnunet_core_service.h | 323 ++ src/include/gnunet_crypto_lib.h | 567 ++ src/include/gnunet_datastore_service.h | 187 + src/include/gnunet_directories.h.in | 34 + src/include/gnunet_disk_lib.h | 279 + src/include/gnunet_fragmentation_lib.h | 113 + src/include/gnunet_getopt_lib.h | 251 + src/include/gnunet_hello_lib.h | 201 + src/include/gnunet_network_lib.h | 308 ++ src/include/gnunet_os_lib.h | 158 + src/include/gnunet_peerinfo_service.h | 111 + src/include/gnunet_plugin_lib.h | 84 + src/include/gnunet_program_lib.h | 90 + src/include/gnunet_protocols.h | 319 ++ src/include/gnunet_pseudonym_lib.h | 125 + src/include/gnunet_resolver_service.h | 135 + src/include/gnunet_scheduler_lib.h | 442 ++ src/include/gnunet_server_lib.h | 498 ++ src/include/gnunet_service_lib.h | 140 + src/include/gnunet_signal_lib.h | 73 + src/include/gnunet_signatures.h | 77 + src/include/gnunet_statistics_service.h | 157 + src/include/gnunet_strings_lib.h | 139 + src/include/gnunet_time_lib.h | 246 + src/include/gnunet_transport_service.h | 241 + src/include/gnunet_upnp_service.h | 75 + src/include/gnunet_util_lib.h | 64 + src/include/platform.h | 221 + src/include/plibc.h | 582 ++ src/include/winproc.h | 216 + src/peerinfo/Makefile.am | 56 + src/peerinfo/gnunet-peerinfo.c | 152 + src/peerinfo/gnunet-service-peerinfo.c | 708 +++ src/peerinfo/peerinfo.h | 135 + src/peerinfo/peerinfo_api.c | 302 ++ src/peerinfo/test_peerinfo_api.c | 175 + src/peerinfo/test_peerinfo_api_data.conf | 5 + src/resolver/Makefile.am | 46 + src/resolver/gnunet-service-resolver.c | 483 ++ src/resolver/resolver.h | 63 + src/resolver/resolver_api.c | 468 ++ src/resolver/test_resolver_api.c | 229 + src/resolver/test_resolver_api_data.conf | 5 + src/statistics/Makefile.am | 59 + src/statistics/gnunet-service-statistics.c | 470 ++ src/statistics/gnunet-statistics.c | 179 + src/statistics/statistics.h | 94 + src/statistics/statistics_api.c | 688 +++ src/statistics/test_gnunet_statistics.sh | 177 + src/statistics/test_statistics_api.c | 177 + src/statistics/test_statistics_api_data.conf | 5 + src/template/Makefile.am | 37 + src/template/gnunet-service-template.c | 87 + src/template/gnunet-template.c | 81 + src/template/test_template_api.c | 45 + src/transport/Makefile.am | 84 + src/transport/NOTES | 46 + src/transport/gnunet-service-transport.c | 2852 ++++++++++ src/transport/gnunet-transport.c | 42 + src/transport/plugin_transport.h | 468 ++ src/transport/plugin_transport_http.c | 2085 ++++++++ src/transport/plugin_transport_smtp.c | 906 ++++ src/transport/plugin_transport_tcp.c | 1782 +++++++ src/transport/plugin_transport_template.c | 335 ++ src/transport/plugin_transport_udp.c | 592 +++ src/transport/test_transport_api.c | 305 ++ src/transport/test_transport_api_data.conf | 24 + src/transport/test_transport_api_peer1.conf | 25 + src/transport/test_transport_api_peer2.conf | 25 + src/transport/transport.h | 238 + src/transport/transport_api.c | 1863 +++++++ src/upnp/Makefile.am | 59 + src/upnp/draft-cheshire-nat-pmp.txt | 1160 ++++ src/upnp/test_upnp.c | 110 + src/upnp/upnp.c | 721 +++ src/upnp/upnp.h | 82 + src/upnp/upnp_init.c | 208 + src/upnp/upnp_ip.c | 47 + src/upnp/upnp_ip.h | 40 + src/upnp/upnp_util.c | 152 + src/upnp/upnp_util.h | 68 + src/upnp/upnp_xmlnode.c | 487 ++ src/upnp/upnp_xmlnode.h | 92 + src/util/Makefile.am | 308 ++ src/util/client.c | 526 ++ src/util/common_allocation.c | 206 + src/util/common_endian.c | 52 + src/util/common_gettext.c | 42 + src/util/common_logging.c | 401 ++ src/util/configuration.c | 848 +++ src/util/container_bloomfilter.c | 677 +++ src/util/container_heap.c | 533 ++ src/util/container_meta_data.c | 721 +++ src/util/container_multihashmap.c | 334 ++ src/util/crypto_aes.c | 148 + src/util/crypto_crc.c | 106 + src/util/crypto_hash.c | 791 +++ src/util/crypto_ksk.c | 791 +++ src/util/crypto_random.c | 136 + src/util/crypto_rsa.c | 948 ++++ src/util/disk.c | 954 ++++ src/util/getopt.c | 1077 ++++ src/util/getopt_helpers.c | 200 + src/util/network.c | 1239 +++++ src/util/os_installation.c | 443 ++ src/util/os_load.c | 653 +++ src/util/os_network.c | 286 + src/util/os_priority.c | 186 + src/util/perf_crypto_hash.c | 64 + src/util/plugin.c | 243 + src/util/program.c | 202 + src/util/pseudonym.c | 545 ++ src/util/scheduler.c | 886 +++ src/util/server.c | 1091 ++++ src/util/server_tc.c | 198 + src/util/service.c | 1451 +++++ src/util/signal.c | 76 + src/util/strings.c | 396 ++ src/util/test_client.c | 195 + src/util/test_common_allocation.c | 112 + src/util/test_common_endian.c | 42 + src/util/test_common_logging.c | 100 + src/util/test_configuration.c | 229 + src/util/test_configuration_data.conf | 30 + src/util/test_container_bloomfilter.c | 246 + src/util/test_container_heap.c | 112 + src/util/test_container_meta_data.c | 262 + src/util/test_container_meta_data_image.jpg | Bin 0 -> 136107 bytes src/util/test_container_multihashmap.c | 113 + src/util/test_crypto_aes.c | 180 + src/util/test_crypto_aes_weak.c | 203 + src/util/test_crypto_crc.c | 221 + src/util/test_crypto_hash.c | 165 + src/util/test_crypto_ksk.c | 220 + src/util/test_crypto_random.c | 71 + src/util/test_crypto_rsa.c | 335 ++ src/util/test_disk.c | 274 + src/util/test_getopt.c | 239 + src/util/test_network.c | 209 + src/util/test_network_addressing.c | 193 + src/util/test_network_receive_cancel.c | 164 + src/util/test_network_timeout.c | 144 + src/util/test_network_timeout_no_connect.c | 95 + src/util/test_network_transmit_cancel.c | 97 + src/util/test_os_load.c | 182 + src/util/test_os_network.c | 73 + src/util/test_os_priority.c | 61 + src/util/test_plugin.c | 64 + src/util/test_plugin_plug.c | 42 + src/util/test_program.c | 94 + src/util/test_program_data.conf | 0 src/util/test_pseudonym.c | 138 + src/util/test_pseudonym_data.conf | 8 + src/util/test_scheduler.c | 263 + src/util/test_scheduler_delay.c | 107 + src/util/test_server.c | 287 + src/util/test_server_disconnect.c | 241 + src/util/test_server_with_client.c | 215 + src/util/test_service.c | 337 ++ src/util/test_service_data.conf | 26 + src/util/test_strings.c | 111 + src/util/test_time.c | 122 + src/util/time.c | 286 + 253 files changed, 101835 insertions(+) create mode 100644 ABOUT-NLS create mode 100644 AUTHORS create mode 100644 COPYING create mode 100644 ChangeLog create mode 100644 HACKING create mode 100644 INSTALL create mode 100644 Makefile.am create mode 100644 NEWS create mode 100644 RATIONALE create mode 100644 README create mode 100644 TODO create mode 100755 bin/grephdr.sh create mode 100755 bin/grepsrc.sh create mode 100755 bin/rename.sh create mode 100755 bootstrap create mode 100755 config.rpath create mode 100644 configure.ac create mode 100644 contrib/Makefile.am create mode 100644 contrib/config.scm create mode 100755 contrib/coverage.sh create mode 100644 contrib/defaults.conf create mode 100644 contrib/gnunet-logo-big.png create mode 100644 contrib/gnunet-logo-color.png create mode 100644 contrib/hostlist.cgi create mode 100644 contrib/hostlist.php create mode 100755 contrib/report.sh create mode 100644 doc/man/Makefile.am create mode 100644 doc/man/gnunet-arm.1 create mode 100644 doc/man/gnunet-peerinfo.1 create mode 100644 doc/man/gnunet-statistics.1 create mode 100644 doc/man/gnunet-transport.1 create mode 100644 m4/Makefile.am create mode 100644 m4/align.m4 create mode 100644 po/ChangeLog create mode 100644 po/GNUnet.pot create mode 100644 po/LINGUAS create mode 100644 po/Makefile.in.in create mode 100644 po/Makevars create mode 100644 po/Makevars.template create mode 100644 po/POTFILES.in create mode 100644 po/Rules-quot create mode 100644 po/boldquot.sed create mode 100644 po/de.gmo create mode 100644 po/de.po create mode 100644 po/en@boldquot.header create mode 100644 po/en@quot.header create mode 100644 po/es.gmo create mode 100644 po/es.po create mode 100644 po/insert-header.sin create mode 100644 po/quot.sed create mode 100644 po/remove-potcdate.sin create mode 100644 po/stamp-po create mode 100644 po/sv.gmo create mode 100644 po/sv.po create mode 100644 po/update.pl create mode 100644 po/vi.gmo create mode 100644 po/vi.po create mode 100755 pre-commit create mode 100644 src/Makefile.am create mode 100644 src/arm/Makefile.am create mode 100644 src/arm/arm.h create mode 100644 src/arm/arm_api.c create mode 100644 src/arm/gnunet-arm.c create mode 100644 src/arm/gnunet-service-arm.c create mode 100644 src/arm/test_arm_api.c create mode 100644 src/arm/test_arm_api_data.conf create mode 100755 src/arm/test_gnunet_arm.sh create mode 100644 src/core/Makefile.am create mode 100644 src/core/core.h create mode 100644 src/core/core_api.c create mode 100644 src/core/gnunet-service-core.c create mode 100644 src/core/test_core_api.c create mode 100644 src/core/test_core_api_data.conf create mode 100644 src/core/test_core_api_peer1.conf create mode 100644 src/core/test_core_api_peer2.conf create mode 100644 src/core/test_core_api_start_only.c create mode 100644 src/datastore/plugin_datastore.h create mode 100644 src/fragmentation/Makefile.am create mode 100644 src/fragmentation/fragmentation.c create mode 100644 src/fragmentation/test_fragmentation.c create mode 100644 src/hello/Makefile.am create mode 100644 src/hello/hello.c create mode 100644 src/hello/test_hello.c create mode 100644 src/include/Makefile.am create mode 100644 src/include/gettext.h create mode 100644 src/include/gnunet_arm_service.h create mode 100644 src/include/gnunet_client_lib.h create mode 100644 src/include/gnunet_common.h create mode 100644 src/include/gnunet_configuration_lib.h create mode 100644 src/include/gnunet_container_lib.h create mode 100644 src/include/gnunet_core_service.h create mode 100644 src/include/gnunet_crypto_lib.h create mode 100644 src/include/gnunet_datastore_service.h create mode 100644 src/include/gnunet_directories.h.in create mode 100644 src/include/gnunet_disk_lib.h create mode 100644 src/include/gnunet_fragmentation_lib.h create mode 100644 src/include/gnunet_getopt_lib.h create mode 100644 src/include/gnunet_hello_lib.h create mode 100644 src/include/gnunet_network_lib.h create mode 100644 src/include/gnunet_os_lib.h create mode 100644 src/include/gnunet_peerinfo_service.h create mode 100644 src/include/gnunet_plugin_lib.h create mode 100644 src/include/gnunet_program_lib.h create mode 100644 src/include/gnunet_protocols.h create mode 100644 src/include/gnunet_pseudonym_lib.h create mode 100644 src/include/gnunet_resolver_service.h create mode 100644 src/include/gnunet_scheduler_lib.h create mode 100644 src/include/gnunet_server_lib.h create mode 100644 src/include/gnunet_service_lib.h create mode 100644 src/include/gnunet_signal_lib.h create mode 100644 src/include/gnunet_signatures.h create mode 100644 src/include/gnunet_statistics_service.h create mode 100644 src/include/gnunet_strings_lib.h create mode 100644 src/include/gnunet_time_lib.h create mode 100644 src/include/gnunet_transport_service.h create mode 100644 src/include/gnunet_upnp_service.h create mode 100644 src/include/gnunet_util_lib.h create mode 100644 src/include/platform.h create mode 100644 src/include/plibc.h create mode 100644 src/include/winproc.h create mode 100644 src/peerinfo/Makefile.am create mode 100644 src/peerinfo/gnunet-peerinfo.c create mode 100644 src/peerinfo/gnunet-service-peerinfo.c create mode 100644 src/peerinfo/peerinfo.h create mode 100644 src/peerinfo/peerinfo_api.c create mode 100644 src/peerinfo/test_peerinfo_api.c create mode 100644 src/peerinfo/test_peerinfo_api_data.conf create mode 100644 src/resolver/Makefile.am create mode 100644 src/resolver/gnunet-service-resolver.c create mode 100644 src/resolver/resolver.h create mode 100644 src/resolver/resolver_api.c create mode 100644 src/resolver/test_resolver_api.c create mode 100644 src/resolver/test_resolver_api_data.conf create mode 100644 src/statistics/Makefile.am create mode 100644 src/statistics/gnunet-service-statistics.c create mode 100644 src/statistics/gnunet-statistics.c create mode 100644 src/statistics/statistics.h create mode 100644 src/statistics/statistics_api.c create mode 100755 src/statistics/test_gnunet_statistics.sh create mode 100644 src/statistics/test_statistics_api.c create mode 100644 src/statistics/test_statistics_api_data.conf create mode 100644 src/template/Makefile.am create mode 100644 src/template/gnunet-service-template.c create mode 100644 src/template/gnunet-template.c create mode 100644 src/template/test_template_api.c create mode 100644 src/transport/Makefile.am create mode 100644 src/transport/NOTES create mode 100644 src/transport/gnunet-service-transport.c create mode 100644 src/transport/gnunet-transport.c create mode 100644 src/transport/plugin_transport.h create mode 100644 src/transport/plugin_transport_http.c create mode 100644 src/transport/plugin_transport_smtp.c create mode 100644 src/transport/plugin_transport_tcp.c create mode 100644 src/transport/plugin_transport_template.c create mode 100644 src/transport/plugin_transport_udp.c create mode 100644 src/transport/test_transport_api.c create mode 100644 src/transport/test_transport_api_data.conf create mode 100644 src/transport/test_transport_api_peer1.conf create mode 100644 src/transport/test_transport_api_peer2.conf create mode 100644 src/transport/transport.h create mode 100644 src/transport/transport_api.c create mode 100644 src/upnp/Makefile.am create mode 100644 src/upnp/draft-cheshire-nat-pmp.txt create mode 100644 src/upnp/test_upnp.c create mode 100644 src/upnp/upnp.c create mode 100644 src/upnp/upnp.h create mode 100644 src/upnp/upnp_init.c create mode 100644 src/upnp/upnp_ip.c create mode 100644 src/upnp/upnp_ip.h create mode 100644 src/upnp/upnp_util.c create mode 100644 src/upnp/upnp_util.h create mode 100644 src/upnp/upnp_xmlnode.c create mode 100644 src/upnp/upnp_xmlnode.h create mode 100644 src/util/Makefile.am create mode 100644 src/util/client.c create mode 100644 src/util/common_allocation.c create mode 100644 src/util/common_endian.c create mode 100644 src/util/common_gettext.c create mode 100644 src/util/common_logging.c create mode 100644 src/util/configuration.c create mode 100644 src/util/container_bloomfilter.c create mode 100644 src/util/container_heap.c create mode 100644 src/util/container_meta_data.c create mode 100644 src/util/container_multihashmap.c create mode 100644 src/util/crypto_aes.c create mode 100644 src/util/crypto_crc.c create mode 100644 src/util/crypto_hash.c create mode 100644 src/util/crypto_ksk.c create mode 100644 src/util/crypto_random.c create mode 100644 src/util/crypto_rsa.c create mode 100644 src/util/disk.c create mode 100644 src/util/getopt.c create mode 100644 src/util/getopt_helpers.c create mode 100644 src/util/network.c create mode 100644 src/util/os_installation.c create mode 100644 src/util/os_load.c create mode 100644 src/util/os_network.c create mode 100644 src/util/os_priority.c create mode 100644 src/util/perf_crypto_hash.c create mode 100644 src/util/plugin.c create mode 100644 src/util/program.c create mode 100644 src/util/pseudonym.c create mode 100644 src/util/scheduler.c create mode 100644 src/util/server.c create mode 100644 src/util/server_tc.c create mode 100644 src/util/service.c create mode 100644 src/util/signal.c create mode 100644 src/util/strings.c create mode 100644 src/util/test_client.c create mode 100644 src/util/test_common_allocation.c create mode 100644 src/util/test_common_endian.c create mode 100644 src/util/test_common_logging.c create mode 100644 src/util/test_configuration.c create mode 100644 src/util/test_configuration_data.conf create mode 100644 src/util/test_container_bloomfilter.c create mode 100644 src/util/test_container_heap.c create mode 100644 src/util/test_container_meta_data.c create mode 100644 src/util/test_container_meta_data_image.jpg create mode 100644 src/util/test_container_multihashmap.c create mode 100644 src/util/test_crypto_aes.c create mode 100644 src/util/test_crypto_aes_weak.c create mode 100644 src/util/test_crypto_crc.c create mode 100644 src/util/test_crypto_hash.c create mode 100644 src/util/test_crypto_ksk.c create mode 100644 src/util/test_crypto_random.c create mode 100644 src/util/test_crypto_rsa.c create mode 100644 src/util/test_disk.c create mode 100644 src/util/test_getopt.c create mode 100644 src/util/test_network.c create mode 100644 src/util/test_network_addressing.c create mode 100644 src/util/test_network_receive_cancel.c create mode 100644 src/util/test_network_timeout.c create mode 100644 src/util/test_network_timeout_no_connect.c create mode 100644 src/util/test_network_transmit_cancel.c create mode 100644 src/util/test_os_load.c create mode 100644 src/util/test_os_network.c create mode 100644 src/util/test_os_priority.c create mode 100644 src/util/test_plugin.c create mode 100644 src/util/test_plugin_plug.c create mode 100644 src/util/test_program.c create mode 100644 src/util/test_program_data.conf create mode 100644 src/util/test_pseudonym.c create mode 100644 src/util/test_pseudonym_data.conf create mode 100644 src/util/test_scheduler.c create mode 100644 src/util/test_scheduler_delay.c create mode 100644 src/util/test_server.c create mode 100644 src/util/test_server_disconnect.c create mode 100644 src/util/test_server_with_client.c create mode 100644 src/util/test_service.c create mode 100644 src/util/test_service_data.conf create mode 100644 src/util/test_strings.c create mode 100644 src/util/test_time.c create mode 100644 src/util/time.c diff --git a/ABOUT-NLS b/ABOUT-NLS new file mode 100644 index 000000000..ec20977e0 --- /dev/null +++ b/ABOUT-NLS @@ -0,0 +1,1101 @@ +1 Notes on the Free Translation Project +*************************************** + +Free software is going international! The Free Translation Project is +a way to get maintainers of free software, translators, and users all +together, so that free software will gradually become able to speak many +languages. A few packages already provide translations for their +messages. + + If you found this `ABOUT-NLS' file inside a distribution, you may +assume that the distributed package does use GNU `gettext' internally, +itself available at your nearest GNU archive site. But you do _not_ +need to install GNU `gettext' prior to configuring, installing or using +this package with messages translated. + + Installers will find here some useful hints. These notes also +explain how users should proceed for getting the programs to use the +available translations. They tell how people wanting to contribute and +work on translations can contact the appropriate team. + + When reporting bugs in the `intl/' directory or bugs which may be +related to internationalization, you should tell about the version of +`gettext' which is used. The information can be found in the +`intl/VERSION' file, in internationalized packages. + +1.1 Quick configuration advice +============================== + +If you want to exploit the full power of internationalization, you +should configure it using + + ./configure --with-included-gettext + +to force usage of internationalizing routines provided within this +package, despite the existence of internationalizing capabilities in the +operating system where this package is being installed. So far, only +the `gettext' implementation in the GNU C library version 2 provides as +many features (such as locale alias, message inheritance, automatic +charset conversion or plural form handling) as the implementation here. +It is also not possible to offer this additional functionality on top +of a `catgets' implementation. Future versions of GNU `gettext' will +very likely convey even more functionality. So it might be a good idea +to change to GNU `gettext' as soon as possible. + + So you need _not_ provide this option if you are using GNU libc 2 or +you have installed a recent copy of the GNU gettext package with the +included `libintl'. + +1.2 INSTALL Matters +=================== + +Some packages are "localizable" when properly installed; the programs +they contain can be made to speak your own native language. Most such +packages use GNU `gettext'. Other packages have their own ways to +internationalization, predating GNU `gettext'. + + By default, this package will be installed to allow translation of +messages. It will automatically detect whether the system already +provides the GNU `gettext' functions. If not, the included GNU +`gettext' library will be used. This library is wholly contained +within this package, usually in the `intl/' subdirectory, so prior +installation of the GNU `gettext' package is _not_ required. +Installers may use special options at configuration time for changing +the default behaviour. The commands: + + ./configure --with-included-gettext + ./configure --disable-nls + +will, respectively, bypass any pre-existing `gettext' to use the +internationalizing routines provided within this package, or else, +_totally_ disable translation of messages. + + When you already have GNU `gettext' installed on your system and run +configure without an option for your new package, `configure' will +probably detect the previously built and installed `libintl.a' file and +will decide to use this. This might not be desirable. You should use +the more recent version of the GNU `gettext' library. I.e. if the file +`intl/VERSION' shows that the library which comes with this package is +more recent, you should use + + ./configure --with-included-gettext + +to prevent auto-detection. + + The configuration process will not test for the `catgets' function +and therefore it will not be used. The reason is that even an +emulation of `gettext' on top of `catgets' could not provide all the +extensions of the GNU `gettext' library. + + Internationalized packages usually have many `po/LL.po' files, where +LL gives an ISO 639 two-letter code identifying the language. Unless +translations have been forbidden at `configure' time by using the +`--disable-nls' switch, all available translations are installed +together with the package. However, the environment variable `LINGUAS' +may be set, prior to configuration, to limit the installed set. +`LINGUAS' should then contain a space separated list of two-letter +codes, stating which languages are allowed. + +1.3 Using This Package +====================== + +As a user, if your language has been installed for this package, you +only have to set the `LANG' environment variable to the appropriate +`LL_CC' combination. Here `LL' is an ISO 639 two-letter language code, +and `CC' is an ISO 3166 two-letter country code. For example, let's +suppose that you speak German and live in Germany. At the shell +prompt, merely execute `setenv LANG de_DE' (in `csh'), +`export LANG; LANG=de_DE' (in `sh') or `export LANG=de_DE' (in `bash'). +This can be done from your `.login' or `.profile' file, once and for +all. + + You might think that the country code specification is redundant. +But in fact, some languages have dialects in different countries. For +example, `de_AT' is used for Austria, and `pt_BR' for Brazil. The +country code serves to distinguish the dialects. + + The locale naming convention of `LL_CC', with `LL' denoting the +language and `CC' denoting the country, is the one use on systems based +on GNU libc. On other systems, some variations of this scheme are +used, such as `LL' or `LL_CC.ENCODING'. You can get the list of +locales supported by your system for your language by running the +command `locale -a | grep '^LL''. + + Not all programs have translations for all languages. By default, an +English message is shown in place of a nonexistent translation. If you +understand other languages, you can set up a priority list of languages. +This is done through a different environment variable, called +`LANGUAGE'. GNU `gettext' gives preference to `LANGUAGE' over `LANG' +for the purpose of message handling, but you still need to have `LANG' +set to the primary language; this is required by other parts of the +system libraries. For example, some Swedish users who would rather +read translations in German than English for when Swedish is not +available, set `LANGUAGE' to `sv:de' while leaving `LANG' to `sv_SE'. + + Special advice for Norwegian users: The language code for Norwegian +bokma*l changed from `no' to `nb' recently (in 2003). During the +transition period, while some message catalogs for this language are +installed under `nb' and some older ones under `no', it's recommended +for Norwegian users to set `LANGUAGE' to `nb:no' so that both newer and +older translations are used. + + In the `LANGUAGE' environment variable, but not in the `LANG' +environment variable, `LL_CC' combinations can be abbreviated as `LL' +to denote the language's main dialect. For example, `de' is equivalent +to `de_DE' (German as spoken in Germany), and `pt' to `pt_PT' +(Portuguese as spoken in Portugal) in this context. + +1.4 Translating Teams +===================== + +For the Free Translation Project to be a success, we need interested +people who like their own language and write it well, and who are also +able to synergize with other translators speaking the same language. +Each translation team has its own mailing list. The up-to-date list of +teams can be found at the Free Translation Project's homepage, +`http://www.iro.umontreal.ca/contrib/po/HTML/', in the "National teams" +area. + + If you'd like to volunteer to _work_ at translating messages, you +should become a member of the translating team for your own language. +The subscribing address is _not_ the same as the list itself, it has +`-request' appended. For example, speakers of Swedish can send a +message to `sv-request@li.org', having this message body: + + subscribe + + Keep in mind that team members are expected to participate +_actively_ in translations, or at solving translational difficulties, +rather than merely lurking around. If your team does not exist yet and +you want to start one, or if you are unsure about what to do or how to +get started, please write to `translation@iro.umontreal.ca' to reach the +coordinator for all translator teams. + + The English team is special. It works at improving and uniformizing +the terminology in use. Proven linguistic skills are praised more than +programming skills, here. + +1.5 Available Packages +====================== + +Languages are not equally supported in all packages. The following +matrix shows the current state of internationalization, as of October +2006. The matrix shows, in regard of each package, for which languages +PO files have been submitted to translation coordination, with a +translation percentage of at least 50%. + + Ready PO files af am ar az be bg bs ca cs cy da de el en en_GB eo + +----------------------------------------------------+ + GNUnet | [] | + a2ps | [] [] [] [] [] | + aegis | () | + ant-phone | () | + anubis | [] | + ap-utils | | + aspell | [] [] [] [] [] | + bash | [] [] [] | + batchelor | [] | + bfd | | + bibshelf | [] | + binutils | [] | + bison | [] [] | + bison-runtime | | + bluez-pin | [] [] [] [] [] | + cflow | [] | + clisp | [] [] | + console-tools | [] [] | + coreutils | [] [] [] | + cpio | | + cpplib | [] [] [] | + cryptonit | [] | + darkstat | [] () [] | + dialog | [] [] [] [] [] [] | + diffutils | [] [] [] [] [] [] | + doodle | [] | + e2fsprogs | [] [] | + enscript | [] [] [] [] | + error | [] [] [] [] | + fetchmail | [] [] () [] | + fileutils | [] [] | + findutils | [] [] [] | + flex | [] [] [] | + fslint | [] | + gas | | + gawk | [] [] [] | + gbiff | [] | + gcal | [] | + gcc | [] | + gettext-examples | [] [] [] [] [] | + gettext-runtime | [] [] [] [] [] | + gettext-tools | [] [] | + gimp-print | [] [] [] [] | + gip | [] | + gliv | [] | + glunarclock | [] | + gmult | [] [] | + gnubiff | () | + gnucash | () () [] | + gnucash-glossary | [] () | + gnuedu | | + gnulib | [] [] [] [] [] [] | + gnunet-gtk | | + gnutls | | + gpe-aerial | [] [] | + gpe-beam | [] [] | + gpe-calendar | | + gpe-clock | [] [] | + gpe-conf | [] [] | + gpe-contacts | | + gpe-edit | [] | + gpe-filemanager | | + gpe-go | [] | + gpe-login | [] [] | + gpe-ownerinfo | [] [] | + gpe-package | | + gpe-sketchbook | [] [] | + gpe-su | [] [] | + gpe-taskmanager | [] [] | + gpe-timesheet | [] | + gpe-today | [] [] | + gpe-todo | | + gphoto2 | [] [] [] [] | + gprof | [] [] | + gpsdrive | () () | + gramadoir | [] [] | + grep | [] [] [] [] [] [] | + gretl | | + gsasl | | + gss | | + gst-plugins | [] [] [] [] | + gst-plugins-base | [] [] [] | + gst-plugins-good | [] [] [] [] [] [] [] | + gstreamer | [] [] [] [] [] [] [] | + gtick | () | + gtkam | [] [] [] | + gtkorphan | [] [] | + gtkspell | [] [] [] [] | + gutenprint | [] | + hello | [] [] [] [] [] | + id-utils | [] [] | + impost | | + indent | [] [] [] | + iso_3166 | [] [] | + iso_3166_2 | | + iso_4217 | [] | + iso_639 | [] [] | + jpilot | [] | + jtag | | + jwhois | | + kbd | [] [] [] [] | + keytouch | | + keytouch-editor | | + keytouch-keyboa... | | + latrine | () | + ld | [] | + leafpad | [] [] [] [] [] | + libc | [] [] [] [] [] | + libexif | [] | + libextractor | [] | + libgpewidget | [] [] [] | + libgpg-error | [] | + libgphoto2 | [] [] | + libgphoto2_port | [] [] | + libgsasl | | + libiconv | [] [] | + libidn | [] [] | + lifelines | [] () | + lilypond | [] | + lingoteach | | + lynx | [] [] [] [] | + m4 | [] [] [] [] | + mailutils | [] | + make | [] [] | + man-db | [] () [] [] | + minicom | [] [] [] | + mysecretdiary | [] [] | + nano | [] [] [] | + nano_1_0 | [] () [] [] | + opcodes | [] | + parted | | + pilot-qof | [] | + psmisc | [] | + pwdutils | | + python | | + qof | | + radius | [] | + recode | [] [] [] [] [] [] | + rpm | [] [] | + screem | | + scrollkeeper | [] [] [] [] [] [] [] [] | + sed | [] [] [] | + sh-utils | [] [] | + shared-mime-info | [] [] [] [] | + sharutils | [] [] [] [] [] [] | + shishi | | + silky | | + skencil | [] () | + sketch | [] () | + solfege | | + soundtracker | [] [] | + sp | [] | + stardict | [] | + system-tools-ba... | [] [] [] [] [] [] [] [] [] | + tar | [] | + texinfo | [] [] [] | + textutils | [] [] [] | + tin | () () | + tp-robot | [] | + tuxpaint | [] [] [] [] [] | + unicode-han-tra... | | + unicode-transla... | | + util-linux | [] [] [] [] | + vorbis-tools | [] [] [] [] | + wastesedge | () | + wdiff | [] [] [] [] | + wget | [] [] | + xchat | [] [] [] [] [] [] | + xkeyboard-config | | + xpad | [] [] | + +----------------------------------------------------+ + af am ar az be bg bs ca cs cy da de el en en_GB eo + 10 0 1 2 9 22 1 42 41 2 60 95 16 1 17 16 + + es et eu fa fi fr ga gl gu he hi hr hu id is it + +--------------------------------------------------+ + GNUnet | | + a2ps | [] [] [] () | + aegis | | + ant-phone | [] | + anubis | [] | + ap-utils | [] [] | + aspell | [] [] [] | + bash | [] [] [] | + batchelor | [] [] | + bfd | [] | + bibshelf | [] [] [] | + binutils | [] [] [] | + bison | [] [] [] [] [] [] | + bison-runtime | [] [] [] [] [] | + bluez-pin | [] [] [] [] [] | + cflow | [] | + clisp | [] [] | + console-tools | | + coreutils | [] [] [] [] [] [] | + cpio | [] [] [] | + cpplib | [] [] | + cryptonit | [] | + darkstat | [] () [] [] [] | + dialog | [] [] [] [] [] [] [] [] | + diffutils | [] [] [] [] [] [] [] [] [] | + doodle | [] [] | + e2fsprogs | [] [] [] | + enscript | [] [] [] | + error | [] [] [] [] [] | + fetchmail | [] | + fileutils | [] [] [] [] [] [] | + findutils | [] [] [] [] | + flex | [] [] [] | + fslint | [] | + gas | [] [] | + gawk | [] [] [] [] | + gbiff | [] | + gcal | [] [] | + gcc | [] | + gettext-examples | [] [] [] [] [] [] | + gettext-runtime | [] [] [] [] [] [] | + gettext-tools | [] [] [] | + gimp-print | [] [] | + gip | [] [] [] | + gliv | () | + glunarclock | [] [] [] | + gmult | [] [] [] | + gnubiff | () () | + gnucash | () () () | + gnucash-glossary | [] [] | + gnuedu | [] | + gnulib | [] [] [] [] [] [] [] [] | + gnunet-gtk | | + gnutls | | + gpe-aerial | [] [] | + gpe-beam | [] [] | + gpe-calendar | | + gpe-clock | [] [] [] [] | + gpe-conf | [] | + gpe-contacts | [] [] | + gpe-edit | [] [] [] [] | + gpe-filemanager | [] | + gpe-go | [] [] [] | + gpe-login | [] [] [] | + gpe-ownerinfo | [] [] [] [] [] | + gpe-package | [] | + gpe-sketchbook | [] [] | + gpe-su | [] [] [] [] | + gpe-taskmanager | [] [] [] | + gpe-timesheet | [] [] [] [] | + gpe-today | [] [] [] [] | + gpe-todo | [] | + gphoto2 | [] [] [] [] [] | + gprof | [] [] [] [] | + gpsdrive | () () [] () | + gramadoir | [] [] | + grep | [] [] [] [] [] [] [] [] [] [] [] [] | + gretl | [] [] [] | + gsasl | [] [] | + gss | [] | + gst-plugins | [] [] [] | + gst-plugins-base | [] [] | + gst-plugins-good | [] [] [] | + gstreamer | [] [] [] | + gtick | [] | + gtkam | [] [] [] [] | + gtkorphan | [] [] | + gtkspell | [] [] [] [] [] [] | + gutenprint | [] | + hello | [] [] [] [] [] [] [] [] [] [] [] [] [] | + id-utils | [] [] [] [] [] | + impost | [] [] | + indent | [] [] [] [] [] [] [] [] [] [] | + iso_3166 | [] [] [] | + iso_3166_2 | [] | + iso_4217 | [] [] [] [] | + iso_639 | [] [] [] [] [] | + jpilot | [] [] | + jtag | [] | + jwhois | [] [] [] [] [] | + kbd | [] [] | + keytouch | [] | + keytouch-editor | [] | + keytouch-keyboa... | [] | + latrine | [] [] [] | + ld | [] [] | + leafpad | [] [] [] [] [] [] | + libc | [] [] [] [] [] | + libexif | [] | + libextractor | [] | + libgpewidget | [] [] [] [] [] | + libgpg-error | | + libgphoto2 | [] [] [] | + libgphoto2_port | [] [] | + libgsasl | [] [] | + libiconv | [] [] | + libidn | [] [] | + lifelines | () | + lilypond | [] | + lingoteach | [] [] [] | + lynx | [] [] [] | + m4 | [] [] [] [] | + mailutils | [] [] | + make | [] [] [] [] [] [] [] [] | + man-db | () | + minicom | [] [] [] [] | + mysecretdiary | [] [] [] | + nano | [] [] [] [] [] [] | + nano_1_0 | [] [] [] [] [] | + opcodes | [] [] [] [] | + parted | [] [] [] [] | + pilot-qof | | + psmisc | [] [] [] | + pwdutils | | + python | | + qof | [] | + radius | [] [] | + recode | [] [] [] [] [] [] [] [] | + rpm | [] [] | + screem | | + scrollkeeper | [] [] [] | + sed | [] [] [] [] [] | + sh-utils | [] [] [] [] [] [] [] | + shared-mime-info | [] [] [] [] [] [] | + sharutils | [] [] [] [] [] [] [] [] | + shishi | | + silky | [] | + skencil | [] [] | + sketch | [] [] | + solfege | [] | + soundtracker | [] [] [] | + sp | [] | + stardict | [] | + system-tools-ba... | [] [] [] [] [] [] [] [] | + tar | [] [] [] [] [] [] [] | + texinfo | [] [] | + textutils | [] [] [] [] [] | + tin | [] () | + tp-robot | [] [] [] [] | + tuxpaint | [] [] | + unicode-han-tra... | | + unicode-transla... | [] [] | + util-linux | [] [] [] [] [] [] [] | + vorbis-tools | [] [] | + wastesedge | () | + wdiff | [] [] [] [] [] [] [] [] | + wget | [] [] [] [] [] [] [] [] | + xchat | [] [] [] [] [] [] [] [] | + xkeyboard-config | [] [] [] [] | + xpad | [] [] [] | + +--------------------------------------------------+ + es et eu fa fi fr ga gl gu he hi hr hu id is it + 88 22 14 2 40 115 61 14 1 8 1 6 59 31 0 52 + + ja ko ku ky lg lt lv mk mn ms mt nb ne nl nn no + +-------------------------------------------------+ + GNUnet | | + a2ps | () [] [] () | + aegis | () | + ant-phone | [] | + anubis | [] [] [] | + ap-utils | [] | + aspell | [] [] | + bash | [] | + batchelor | [] [] | + bfd | | + bibshelf | [] | + binutils | | + bison | [] [] [] | + bison-runtime | [] [] [] | + bluez-pin | [] [] [] | + cflow | | + clisp | [] | + console-tools | | + coreutils | [] | + cpio | | + cpplib | [] | + cryptonit | [] | + darkstat | [] [] | + dialog | [] [] | + diffutils | [] [] [] | + doodle | | + e2fsprogs | [] | + enscript | [] | + error | [] | + fetchmail | [] [] | + fileutils | [] [] | + findutils | [] | + flex | [] [] | + fslint | [] [] | + gas | | + gawk | [] [] | + gbiff | [] | + gcal | | + gcc | | + gettext-examples | [] [] | + gettext-runtime | [] [] [] | + gettext-tools | [] [] | + gimp-print | [] [] | + gip | [] [] | + gliv | [] | + glunarclock | [] [] | + gmult | [] [] | + gnubiff | | + gnucash | () () | + gnucash-glossary | [] | + gnuedu | | + gnulib | [] [] [] [] | + gnunet-gtk | | + gnutls | | + gpe-aerial | [] | + gpe-beam | [] | + gpe-calendar | [] | + gpe-clock | [] [] [] | + gpe-conf | [] [] | + gpe-contacts | [] | + gpe-edit | [] [] [] | + gpe-filemanager | [] [] | + gpe-go | [] [] [] | + gpe-login | [] [] [] | + gpe-ownerinfo | [] [] | + gpe-package | [] [] | + gpe-sketchbook | [] [] | + gpe-su | [] [] [] | + gpe-taskmanager | [] [] [] [] | + gpe-timesheet | [] | + gpe-today | [] [] | + gpe-todo | [] | + gphoto2 | [] [] | + gprof | | + gpsdrive | () () () | + gramadoir | () | + grep | [] [] [] [] | + gretl | | + gsasl | [] | + gss | | + gst-plugins | [] | + gst-plugins-base | | + gst-plugins-good | [] | + gstreamer | [] | + gtick | | + gtkam | [] | + gtkorphan | [] | + gtkspell | [] [] | + gutenprint | | + hello | [] [] [] [] [] [] | + id-utils | [] | + impost | | + indent | [] [] | + iso_3166 | [] | + iso_3166_2 | [] | + iso_4217 | [] [] [] | + iso_639 | [] [] | + jpilot | () () () | + jtag | | + jwhois | [] | + kbd | [] | + keytouch | [] | + keytouch-editor | | + keytouch-keyboa... | | + latrine | [] | + ld | | + leafpad | [] [] | + libc | [] [] [] [] [] | + libexif | | + libextractor | | + libgpewidget | [] | + libgpg-error | | + libgphoto2 | [] | + libgphoto2_port | [] | + libgsasl | [] | + libiconv | | + libidn | [] [] | + lifelines | [] | + lilypond | | + lingoteach | [] | + lynx | [] [] | + m4 | [] [] | + mailutils | | + make | [] [] [] | + man-db | () | + minicom | [] | + mysecretdiary | [] | + nano | [] [] [] | + nano_1_0 | [] [] [] | + opcodes | [] | + parted | [] [] | + pilot-qof | | + psmisc | [] [] [] | + pwdutils | | + python | | + qof | | + radius | | + recode | [] | + rpm | [] [] | + screem | [] | + scrollkeeper | [] [] [] [] | + sed | [] [] | + sh-utils | [] [] | + shared-mime-info | [] [] [] [] [] | + sharutils | [] [] | + shishi | | + silky | [] | + skencil | | + sketch | | + solfege | | + soundtracker | | + sp | () | + stardict | [] [] | + system-tools-ba... | [] [] [] [] | + tar | [] [] [] | + texinfo | [] [] [] | + textutils | [] [] [] | + tin | | + tp-robot | [] | + tuxpaint | [] | + unicode-han-tra... | | + unicode-transla... | | + util-linux | [] [] | + vorbis-tools | [] | + wastesedge | [] | + wdiff | [] [] | + wget | [] [] | + xchat | [] [] [] [] | + xkeyboard-config | [] | + xpad | [] [] [] | + +-------------------------------------------------+ + ja ko ku ky lg lt lv mk mn ms mt nb ne nl nn no + 52 24 2 2 1 3 0 2 3 21 0 15 1 97 5 1 + + nso or pa pl pt pt_BR rm ro ru rw sk sl sq sr sv ta + +------------------------------------------------------+ + GNUnet | | + a2ps | () [] [] [] [] [] [] | + aegis | () () | + ant-phone | [] [] | + anubis | [] [] [] | + ap-utils | () | + aspell | [] [] | + bash | [] [] [] | + batchelor | [] [] | + bfd | | + bibshelf | [] | + binutils | [] [] | + bison | [] [] [] [] [] | + bison-runtime | [] [] [] [] | + bluez-pin | [] [] [] [] [] [] [] [] [] | + cflow | [] | + clisp | [] | + console-tools | [] | + coreutils | [] [] [] [] | + cpio | [] [] [] | + cpplib | [] | + cryptonit | [] [] | + darkstat | [] [] [] [] [] [] | + dialog | [] [] [] [] [] [] [] [] [] | + diffutils | [] [] [] [] [] [] | + doodle | [] [] | + e2fsprogs | [] [] | + enscript | [] [] [] [] [] | + error | [] [] [] [] | + fetchmail | [] [] [] | + fileutils | [] [] [] [] [] | + findutils | [] [] [] [] [] [] | + flex | [] [] [] [] [] | + fslint | [] [] [] [] | + gas | | + gawk | [] [] [] [] | + gbiff | [] | + gcal | [] | + gcc | [] | + gettext-examples | [] [] [] [] [] [] [] [] | + gettext-runtime | [] [] [] [] [] [] [] [] | + gettext-tools | [] [] [] [] [] [] [] | + gimp-print | [] [] | + gip | [] [] [] [] | + gliv | [] [] [] [] | + glunarclock | [] [] [] [] [] [] | + gmult | [] [] [] [] | + gnubiff | () | + gnucash | () [] | + gnucash-glossary | [] [] [] | + gnuedu | | + gnulib | [] [] [] [] [] | + gnunet-gtk | [] | + gnutls | [] [] | + gpe-aerial | [] [] [] [] [] [] [] | + gpe-beam | [] [] [] [] [] [] [] | + gpe-calendar | [] | + gpe-clock | [] [] [] [] [] [] [] [] | + gpe-conf | [] [] [] [] [] [] [] | + gpe-contacts | [] [] [] [] [] | + gpe-edit | [] [] [] [] [] [] [] [] | + gpe-filemanager | [] [] | + gpe-go | [] [] [] [] [] [] | + gpe-login | [] [] [] [] [] [] [] [] | + gpe-ownerinfo | [] [] [] [] [] [] [] [] | + gpe-package | [] [] | + gpe-sketchbook | [] [] [] [] [] [] [] [] | + gpe-su | [] [] [] [] [] [] [] [] | + gpe-taskmanager | [] [] [] [] [] [] [] [] | + gpe-timesheet | [] [] [] [] [] [] [] [] | + gpe-today | [] [] [] [] [] [] [] [] | + gpe-todo | [] [] [] [] | + gphoto2 | [] [] [] [] [] | + gprof | [] [] [] | + gpsdrive | [] [] [] | + gramadoir | [] [] | + grep | [] [] [] [] [] [] [] [] | + gretl | [] | + gsasl | [] [] [] | + gss | [] [] [] | + gst-plugins | [] [] [] [] | + gst-plugins-base | [] | + gst-plugins-good | [] [] [] [] | + gstreamer | [] [] [] | + gtick | [] | + gtkam | [] [] [] [] | + gtkorphan | [] | + gtkspell | [] [] [] [] [] [] [] [] | + gutenprint | [] | + hello | [] [] [] [] [] [] [] [] | + id-utils | [] [] [] [] | + impost | [] | + indent | [] [] [] [] [] [] | + iso_3166 | [] [] [] [] [] [] | + iso_3166_2 | | + iso_4217 | [] [] [] [] | + iso_639 | [] [] [] [] | + jpilot | | + jtag | [] | + jwhois | [] [] [] [] | + kbd | [] [] [] | + keytouch | [] | + keytouch-editor | [] | + keytouch-keyboa... | [] | + latrine | [] [] | + ld | [] | + leafpad | [] [] [] [] [] [] | + libc | [] [] [] [] [] | + libexif | [] | + libextractor | [] [] | + libgpewidget | [] [] [] [] [] [] [] | + libgpg-error | [] [] | + libgphoto2 | [] | + libgphoto2_port | [] [] [] | + libgsasl | [] [] [] [] | + libiconv | [] [] | + libidn | [] [] () | + lifelines | [] [] | + lilypond | | + lingoteach | [] | + lynx | [] [] [] | + m4 | [] [] [] [] [] | + mailutils | [] [] [] [] | + make | [] [] [] [] | + man-db | [] [] | + minicom | [] [] [] [] [] | + mysecretdiary | [] [] [] [] | + nano | [] [] [] | + nano_1_0 | [] [] [] [] | + opcodes | [] [] | + parted | [] | + pilot-qof | [] | + psmisc | [] [] | + pwdutils | [] [] | + python | | + qof | [] [] | + radius | [] [] | + recode | [] [] [] [] [] [] [] | + rpm | [] [] [] [] | + screem | | + scrollkeeper | [] [] [] [] [] [] [] | + sed | [] [] [] [] [] [] [] [] [] | + sh-utils | [] [] [] | + shared-mime-info | [] [] [] [] [] | + sharutils | [] [] [] [] | + shishi | [] | + silky | [] | + skencil | [] [] [] | + sketch | [] [] [] | + solfege | [] | + soundtracker | [] [] | + sp | | + stardict | [] [] [] | + system-tools-ba... | [] [] [] [] [] [] [] [] [] | + tar | [] [] [] [] [] | + texinfo | [] [] [] [] | + textutils | [] [] [] | + tin | () | + tp-robot | [] | + tuxpaint | [] [] [] [] [] | + unicode-han-tra... | | + unicode-transla... | | + util-linux | [] [] [] [] | + vorbis-tools | [] [] | + wastesedge | | + wdiff | [] [] [] [] [] [] | + wget | [] [] [] [] | + xchat | [] [] [] [] [] [] [] | + xkeyboard-config | [] [] | + xpad | [] [] [] | + +------------------------------------------------------+ + nso or pa pl pt pt_BR rm ro ru rw sk sl sq sr sv ta + 0 2 3 58 30 54 5 73 72 4 40 46 11 50 128 2 + + tg th tk tr uk ven vi wa xh zh_CN zh_HK zh_TW zu + +---------------------------------------------------+ + GNUnet | [] | 2 + a2ps | [] [] [] | 19 + aegis | | 0 + ant-phone | [] [] | 6 + anubis | [] [] [] | 11 + ap-utils | () [] | 4 + aspell | [] [] [] | 15 + bash | [] | 11 + batchelor | [] [] | 9 + bfd | | 1 + bibshelf | [] | 7 + binutils | [] [] [] | 9 + bison | [] [] [] | 19 + bison-runtime | [] [] [] | 15 + bluez-pin | [] [] [] [] [] [] | 28 + cflow | [] [] | 5 + clisp | | 6 + console-tools | [] [] | 5 + coreutils | [] [] | 16 + cpio | [] [] [] | 9 + cpplib | [] [] [] [] | 11 + cryptonit | | 5 + darkstat | [] () () | 15 + dialog | [] [] [] [] [] | 30 + diffutils | [] [] [] [] | 28 + doodle | [] | 6 + e2fsprogs | [] [] | 10 + enscript | [] [] [] | 16 + error | [] [] [] [] | 18 + fetchmail | [] [] | 12 + fileutils | [] [] [] | 18 + findutils | [] [] [] | 17 + flex | [] [] | 15 + fslint | [] | 9 + gas | [] | 3 + gawk | [] [] | 15 + gbiff | [] | 5 + gcal | [] | 5 + gcc | [] [] [] | 6 + gettext-examples | [] [] [] [] [] [] | 27 + gettext-runtime | [] [] [] [] [] [] | 28 + gettext-tools | [] [] [] [] [] | 19 + gimp-print | [] [] | 12 + gip | [] [] | 12 + gliv | [] [] | 8 + glunarclock | [] [] [] | 15 + gmult | [] [] [] [] | 15 + gnubiff | [] | 1 + gnucash | () | 2 + gnucash-glossary | [] [] | 9 + gnuedu | [] | 2 + gnulib | [] [] [] [] [] | 28 + gnunet-gtk | | 1 + gnutls | | 2 + gpe-aerial | [] [] | 14 + gpe-beam | [] [] | 14 + gpe-calendar | [] | 3 + gpe-clock | [] [] [] [] | 21 + gpe-conf | [] [] | 14 + gpe-contacts | [] [] | 10 + gpe-edit | [] [] [] [] | 20 + gpe-filemanager | [] | 6 + gpe-go | [] [] | 15 + gpe-login | [] [] [] [] [] | 21 + gpe-ownerinfo | [] [] [] [] | 21 + gpe-package | [] | 6 + gpe-sketchbook | [] [] | 16 + gpe-su | [] [] [] | 20 + gpe-taskmanager | [] [] [] | 20 + gpe-timesheet | [] [] [] [] | 18 + gpe-today | [] [] [] [] [] | 21 + gpe-todo | [] | 7 + gphoto2 | [] [] [] [] | 20 + gprof | [] [] | 11 + gpsdrive | | 4 + gramadoir | [] | 7 + grep | [] [] [] [] | 34 + gretl | | 4 + gsasl | [] [] | 8 + gss | [] | 5 + gst-plugins | [] [] [] | 15 + gst-plugins-base | [] [] [] | 9 + gst-plugins-good | [] [] [] [] [] | 20 + gstreamer | [] [] [] | 17 + gtick | [] | 3 + gtkam | [] | 13 + gtkorphan | [] | 7 + gtkspell | [] [] [] [] [] [] | 26 + gutenprint | | 3 + hello | [] [] [] [] [] | 37 + id-utils | [] [] | 14 + impost | [] | 4 + indent | [] [] [] [] | 25 + iso_3166 | [] [] [] [] | 16 + iso_3166_2 | | 2 + iso_4217 | [] [] | 14 + iso_639 | [] | 14 + jpilot | [] [] [] [] | 7 + jtag | [] | 3 + jwhois | [] [] [] | 13 + kbd | [] [] | 12 + keytouch | [] | 4 + keytouch-editor | | 2 + keytouch-keyboa... | [] | 3 + latrine | [] [] | 8 + ld | [] [] [] [] | 8 + leafpad | [] [] [] [] | 23 + libc | [] [] [] | 23 + libexif | [] | 4 + libextractor | [] | 5 + libgpewidget | [] [] [] | 19 + libgpg-error | [] | 4 + libgphoto2 | [] | 8 + libgphoto2_port | [] [] [] | 11 + libgsasl | [] | 8 + libiconv | [] | 7 + libidn | [] [] | 10 + lifelines | | 4 + lilypond | | 2 + lingoteach | [] | 6 + lynx | [] [] [] | 15 + m4 | [] [] [] | 18 + mailutils | [] | 8 + make | [] [] [] | 20 + man-db | [] | 6 + minicom | [] | 14 + mysecretdiary | [] [] | 12 + nano | [] [] | 17 + nano_1_0 | [] [] [] | 18 + opcodes | [] [] | 10 + parted | [] [] [] | 10 + pilot-qof | [] | 3 + psmisc | [] | 10 + pwdutils | [] | 3 + python | | 0 + qof | [] | 4 + radius | [] | 6 + recode | [] [] [] | 25 + rpm | [] [] [] [] | 14 + screem | [] | 2 + scrollkeeper | [] [] [] [] | 26 + sed | [] [] [] | 22 + sh-utils | [] | 15 + shared-mime-info | [] [] [] [] | 24 + sharutils | [] [] [] | 23 + shishi | | 1 + silky | [] | 4 + skencil | [] | 7 + sketch | | 6 + solfege | | 2 + soundtracker | [] [] | 9 + sp | [] | 3 + stardict | [] [] [] [] | 11 + system-tools-ba... | [] [] [] [] [] [] [] | 37 + tar | [] [] [] [] | 20 + texinfo | [] [] [] | 15 + textutils | [] [] [] | 17 + tin | | 1 + tp-robot | [] [] [] | 10 + tuxpaint | [] [] [] | 16 + unicode-han-tra... | | 0 + unicode-transla... | | 2 + util-linux | [] [] [] | 20 + vorbis-tools | [] [] | 11 + wastesedge | | 1 + wdiff | [] [] | 22 + wget | [] [] [] | 19 + xchat | [] [] [] [] | 29 + xkeyboard-config | [] [] [] [] | 11 + xpad | [] [] [] | 14 + +---------------------------------------------------+ + 77 teams tg th tk tr uk ven vi wa xh zh_CN zh_HK zh_TW zu + 170 domains 0 1 1 77 39 0 136 10 1 48 5 54 0 2028 + + Some counters in the preceding matrix are higher than the number of +visible blocks let us expect. This is because a few extra PO files are +used for implementing regional variants of languages, or language +dialects. + + For a PO file in the matrix above to be effective, the package to +which it applies should also have been internationalized and +distributed as such by its maintainer. There might be an observable +lag between the mere existence a PO file and its wide availability in a +distribution. + + If October 2006 seems to be old, you may fetch a more recent copy of +this `ABOUT-NLS' file on most GNU archive sites. The most up-to-date +matrix with full percentage details can be found at +`http://www.iro.umontreal.ca/contrib/po/HTML/matrix.html'. + +1.6 Using `gettext' in new packages +=================================== + +If you are writing a freely available program and want to +internationalize it you are welcome to use GNU `gettext' in your +package. Of course you have to respect the GNU Library General Public +License which covers the use of the GNU `gettext' library. This means +in particular that even non-free programs can use `libintl' as a shared +library, whereas only free software can use `libintl' as a static +library or use modified versions of `libintl'. + + Once the sources are changed appropriately and the setup can handle +the use of `gettext' the only thing missing are the translations. The +Free Translation Project is also available for packages which are not +developed inside the GNU project. Therefore the information given above +applies also for every other Free Software Project. Contact +`translation@iro.umontreal.ca' to make the `.pot' files available to +the translation teams. + diff --git a/AUTHORS b/AUTHORS new file mode 100644 index 000000000..950320941 --- /dev/null +++ b/AUTHORS @@ -0,0 +1,85 @@ +Primary developers (0.9.x series): +Christian Grothoff +Heikki Lindholm +Nils Durner +Milan Bouchet-Valat + +Code contributions also came from: +Adam Warrington [ UPnP ] +Alex Harper [ OS X CPU load ] +Andrew McDonald [ SHA-512] +Antti Salonen +Blake Matheny +Clytie Siddall +David Kuehling +Enrico Scholz +Eric Haumant +Eric Noack +Felix von Leitner [ diet libc snprintf for win32 ] +Gerd Knorr +Glenn McGrath +Hendrik Pagenhardt +Heikki Lindholm +Igor Wronsky +Ioana Patrascu + +Jake Dust +James Blackwell +Jean-Luc Cooke [ SHA-512] +Jussi Eloranta +Jürgen Appel +Kevin Vandersloot [original code of gnome-system-monitor] +Krista Bennett Grothoff +Kyle McMartin [ SHA-512] +Larry Waldo +Ludovic Courtès +Marko Räihä +Michael John Wensley +Nathan Evans +Paul Ruth +Renaldo Ferreira +Risto Saarelma +Roman Zippel +Romain Lievin +sheda +Simo Viitanen +Tiberius Stef +Tomi Tukiainen +Tuomas Toivonen +Tzvetan Horozov +Uli Luckas +Vasil Dimov +Werner Koch [original code of libgcrypt] + +Translations (webpage, documentation, as far as known): +Chinese : Di Ma +Danish : Jens Palsberg +Deutsch : Christian Grothoff , + Nils Durner +French : Mathieu , + Eric Haumant + Milan +Japanese : Hiroshi Yamauchi +Polish : Adam Welc +Romaneste : Bogdan Carbunar +Kinyarwanda: Steven Michael Murphy +Vietnamese : Phan Vinh Thinh and Clytie Siddall +Swedish : Daniel Nylander +Spanish : Miguel Angel Arruga Vivas +Turkish : Nilgün Belma Bugüner + +Logos: +GNU in Net : Christian Muellner +GNU with Net : Christian Muellner +AFS Face : Alex Jones +new GNU in Net: Nicklas Larsson + +Maintainers: +FreeBSD : Kirill Ponomarew +Debian GNU/Linux: Daniel Baumann and + Arnaud Kyheng +OS X : Jussi Eloranta + + +If you have contributed and are not listed here, please +notify one of the maintainers in order to be added. diff --git a/COPYING b/COPYING new file mode 100644 index 000000000..94a9ed024 --- /dev/null +++ b/COPYING @@ -0,0 +1,674 @@ + GNU GENERAL PUBLIC LICENSE + Version 3, 29 June 2007 + + Copyright (C) 2007 Free Software Foundation, Inc. + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The GNU General Public License is a free, copyleft license for +software and other kinds of works. + + The licenses for most software and other practical works are designed +to take away your freedom to share and change the works. By contrast, +the GNU General Public License is intended to guarantee your freedom to +share and change all versions of a program--to make sure it remains free +software for all its users. We, the Free Software Foundation, use the +GNU General Public License for most of our software; it applies also to +any other work released this way by its authors. You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +them if you wish), that you receive source code or can get it if you +want it, that you can change the software or use pieces of it in new +free programs, and that you know you can do these things. + + To protect your rights, we need to prevent others from denying you +these rights or asking you to surrender the rights. Therefore, you have +certain responsibilities if you distribute copies of the software, or if +you modify it: responsibilities to respect the freedom of others. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must pass on to the recipients the same +freedoms that you received. You must make sure that they, too, receive +or can get the source code. And you must show them these terms so they +know their rights. + + Developers that use the GNU GPL protect your rights with two steps: +(1) assert copyright on the software, and (2) offer you this License +giving you legal permission to copy, distribute and/or modify it. + + For the developers' and authors' protection, the GPL clearly explains +that there is no warranty for this free software. For both users' and +authors' sake, the GPL requires that modified versions be marked as +changed, so that their problems will not be attributed erroneously to +authors of previous versions. + + Some devices are designed to deny users access to install or run +modified versions of the software inside them, although the manufacturer +can do so. This is fundamentally incompatible with the aim of +protecting users' freedom to change the software. The systematic +pattern of such abuse occurs in the area of products for individuals to +use, which is precisely where it is most unacceptable. Therefore, we +have designed this version of the GPL to prohibit the practice for those +products. If such problems arise substantially in other domains, we +stand ready to extend this provision to those domains in future versions +of the GPL, as needed to protect the freedom of users. + + Finally, every program is threatened constantly by software patents. +States should not allow patents to restrict development and use of +software on general-purpose computers, but in those that do, we wish to +avoid the special danger that patents applied to a free program could +make it effectively proprietary. To prevent this, the GPL assures that +patents cannot be used to render the program non-free. + + The precise terms and conditions for copying, distribution and +modification follow. + + TERMS AND CONDITIONS + + 0. Definitions. + + "This License" refers to version 3 of the GNU General Public License. + + "Copyright" also means copyright-like laws that apply to other kinds of +works, such as semiconductor masks. + + "The Program" refers to any copyrightable work licensed under this +License. Each licensee is addressed as "you". "Licensees" and +"recipients" may be individuals or organizations. + + To "modify" a work means to copy from or adapt all or part of the work +in a fashion requiring copyright permission, other than the making of an +exact copy. The resulting work is called a "modified version" of the +earlier work or a work "based on" the earlier work. + + A "covered work" means either the unmodified Program or a work based +on the Program. + + To "propagate" a work means to do anything with it that, without +permission, would make you directly or secondarily liable for +infringement under applicable copyright law, except executing it on a +computer or modifying a private copy. Propagation includes copying, +distribution (with or without modification), making available to the +public, and in some countries other activities as well. + + To "convey" a work means any kind of propagation that enables other +parties to make or receive copies. Mere interaction with a user through +a computer network, with no transfer of a copy, is not conveying. + + An interactive user interface displays "Appropriate Legal Notices" +to the extent that it includes a convenient and prominently visible +feature that (1) displays an appropriate copyright notice, and (2) +tells the user that there is no warranty for the work (except to the +extent that warranties are provided), that licensees may convey the +work under this License, and how to view a copy of this License. If +the interface presents a list of user commands or options, such as a +menu, a prominent item in the list meets this criterion. + + 1. Source Code. + + The "source code" for a work means the preferred form of the work +for making modifications to it. "Object code" means any non-source +form of a work. + + A "Standard Interface" means an interface that either is an official +standard defined by a recognized standards body, or, in the case of +interfaces specified for a particular programming language, one that +is widely used among developers working in that language. + + The "System Libraries" of an executable work include anything, other +than the work as a whole, that (a) is included in the normal form of +packaging a Major Component, but which is not part of that Major +Component, and (b) serves only to enable use of the work with that +Major Component, or to implement a Standard Interface for which an +implementation is available to the public in source code form. A +"Major Component", in this context, means a major essential component +(kernel, window system, and so on) of the specific operating system +(if any) on which the executable work runs, or a compiler used to +produce the work, or an object code interpreter used to run it. + + The "Corresponding Source" for a work in object code form means all +the source code needed to generate, install, and (for an executable +work) run the object code and to modify the work, including scripts to +control those activities. However, it does not include the work's +System Libraries, or general-purpose tools or generally available free +programs which are used unmodified in performing those activities but +which are not part of the work. For example, Corresponding Source +includes interface definition files associated with source files for +the work, and the source code for shared libraries and dynamically +linked subprograms that the work is specifically designed to require, +such as by intimate data communication or control flow between those +subprograms and other parts of the work. + + The Corresponding Source need not include anything that users +can regenerate automatically from other parts of the Corresponding +Source. + + The Corresponding Source for a work in source code form is that +same work. + + 2. Basic Permissions. + + All rights granted under this License are granted for the term of +copyright on the Program, and are irrevocable provided the stated +conditions are met. This License explicitly affirms your unlimited +permission to run the unmodified Program. The output from running a +covered work is covered by this License only if the output, given its +content, constitutes a covered work. This License acknowledges your +rights of fair use or other equivalent, as provided by copyright law. + + You may make, run and propagate covered works that you do not +convey, without conditions so long as your license otherwise remains +in force. You may convey covered works to others for the sole purpose +of having them make modifications exclusively for you, or provide you +with facilities for running those works, provided that you comply with +the terms of this License in conveying all material for which you do +not control copyright. Those thus making or running the covered works +for you must do so exclusively on your behalf, under your direction +and control, on terms that prohibit them from making any copies of +your copyrighted material outside their relationship with you. + + Conveying under any other circumstances is permitted solely under +the conditions stated below. Sublicensing is not allowed; section 10 +makes it unnecessary. + + 3. Protecting Users' Legal Rights From Anti-Circumvention Law. + + No covered work shall be deemed part of an effective technological +measure under any applicable law fulfilling obligations under article +11 of the WIPO copyright treaty adopted on 20 December 1996, or +similar laws prohibiting or restricting circumvention of such +measures. + + When you convey a covered work, you waive any legal power to forbid +circumvention of technological measures to the extent such circumvention +is effected by exercising rights under this License with respect to +the covered work, and you disclaim any intention to limit operation or +modification of the work as a means of enforcing, against the work's +users, your or third parties' legal rights to forbid circumvention of +technological measures. + + 4. Conveying Verbatim Copies. + + You may convey verbatim copies of the Program's source code as you +receive it, in any medium, provided that you conspicuously and +appropriately publish on each copy an appropriate copyright notice; +keep intact all notices stating that this License and any +non-permissive terms added in accord with section 7 apply to the code; +keep intact all notices of the absence of any warranty; and give all +recipients a copy of this License along with the Program. + + You may charge any price or no price for each copy that you convey, +and you may offer support or warranty protection for a fee. + + 5. Conveying Modified Source Versions. + + You may convey a work based on the Program, or the modifications to +produce it from the Program, in the form of source code under the +terms of section 4, provided that you also meet all of these conditions: + + a) The work must carry prominent notices stating that you modified + it, and giving a relevant date. + + b) The work must carry prominent notices stating that it is + released under this License and any conditions added under section + 7. This requirement modifies the requirement in section 4 to + "keep intact all notices". + + c) You must license the entire work, as a whole, under this + License to anyone who comes into possession of a copy. This + License will therefore apply, along with any applicable section 7 + additional terms, to the whole of the work, and all its parts, + regardless of how they are packaged. This License gives no + permission to license the work in any other way, but it does not + invalidate such permission if you have separately received it. + + d) If the work has interactive user interfaces, each must display + Appropriate Legal Notices; however, if the Program has interactive + interfaces that do not display Appropriate Legal Notices, your + work need not make them do so. + + A compilation of a covered work with other separate and independent +works, which are not by their nature extensions of the covered work, +and which are not combined with it such as to form a larger program, +in or on a volume of a storage or distribution medium, is called an +"aggregate" if the compilation and its resulting copyright are not +used to limit the access or legal rights of the compilation's users +beyond what the individual works permit. Inclusion of a covered work +in an aggregate does not cause this License to apply to the other +parts of the aggregate. + + 6. Conveying Non-Source Forms. + + You may convey a covered work in object code form under the terms +of sections 4 and 5, provided that you also convey the +machine-readable Corresponding Source under the terms of this License, +in one of these ways: + + a) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by the + Corresponding Source fixed on a durable physical medium + customarily used for software interchange. + + b) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by a + written offer, valid for at least three years and valid for as + long as you offer spare parts or customer support for that product + model, to give anyone who possesses the object code either (1) a + copy of the Corresponding Source for all the software in the + product that is covered by this License, on a durable physical + medium customarily used for software interchange, for a price no + more than your reasonable cost of physically performing this + conveying of source, or (2) access to copy the + Corresponding Source from a network server at no charge. + + c) Convey individual copies of the object code with a copy of the + written offer to provide the Corresponding Source. This + alternative is allowed only occasionally and noncommercially, and + only if you received the object code with such an offer, in accord + with subsection 6b. + + d) Convey the object code by offering access from a designated + place (gratis or for a charge), and offer equivalent access to the + Corresponding Source in the same way through the same place at no + further charge. You need not require recipients to copy the + Corresponding Source along with the object code. If the place to + copy the object code is a network server, the Corresponding Source + may be on a different server (operated by you or a third party) + that supports equivalent copying facilities, provided you maintain + clear directions next to the object code saying where to find the + Corresponding Source. Regardless of what server hosts the + Corresponding Source, you remain obligated to ensure that it is + available for as long as needed to satisfy these requirements. + + e) Convey the object code using peer-to-peer transmission, provided + you inform other peers where the object code and Corresponding + Source of the work are being offered to the general public at no + charge under subsection 6d. + + A separable portion of the object code, whose source code is excluded +from the Corresponding Source as a System Library, need not be +included in conveying the object code work. + + A "User Product" is either (1) a "consumer product", which means any +tangible personal property which is normally used for personal, family, +or household purposes, or (2) anything designed or sold for incorporation +into a dwelling. In determining whether a product is a consumer product, +doubtful cases shall be resolved in favor of coverage. For a particular +product received by a particular user, "normally used" refers to a +typical or common use of that class of product, regardless of the status +of the particular user or of the way in which the particular user +actually uses, or expects or is expected to use, the product. A product +is a consumer product regardless of whether the product has substantial +commercial, industrial or non-consumer uses, unless such uses represent +the only significant mode of use of the product. + + "Installation Information" for a User Product means any methods, +procedures, authorization keys, or other information required to install +and execute modified versions of a covered work in that User Product from +a modified version of its Corresponding Source. The information must +suffice to ensure that the continued functioning of the modified object +code is in no case prevented or interfered with solely because +modification has been made. + + If you convey an object code work under this section in, or with, or +specifically for use in, a User Product, and the conveying occurs as +part of a transaction in which the right of possession and use of the +User Product is transferred to the recipient in perpetuity or for a +fixed term (regardless of how the transaction is characterized), the +Corresponding Source conveyed under this section must be accompanied +by the Installation Information. But this requirement does not apply +if neither you nor any third party retains the ability to install +modified object code on the User Product (for example, the work has +been installed in ROM). + + The requirement to provide Installation Information does not include a +requirement to continue to provide support service, warranty, or updates +for a work that has been modified or installed by the recipient, or for +the User Product in which it has been modified or installed. Access to a +network may be denied when the modification itself materially and +adversely affects the operation of the network or violates the rules and +protocols for communication across the network. + + Corresponding Source conveyed, and Installation Information provided, +in accord with this section must be in a format that is publicly +documented (and with an implementation available to the public in +source code form), and must require no special password or key for +unpacking, reading or copying. + + 7. Additional Terms. + + "Additional permissions" are terms that supplement the terms of this +License by making exceptions from one or more of its conditions. +Additional permissions that are applicable to the entire Program shall +be treated as though they were included in this License, to the extent +that they are valid under applicable law. If additional permissions +apply only to part of the Program, that part may be used separately +under those permissions, but the entire Program remains governed by +this License without regard to the additional permissions. + + When you convey a copy of a covered work, you may at your option +remove any additional permissions from that copy, or from any part of +it. (Additional permissions may be written to require their own +removal in certain cases when you modify the work.) You may place +additional permissions on material, added by you to a covered work, +for which you have or can give appropriate copyright permission. + + Notwithstanding any other provision of this License, for material you +add to a covered work, you may (if authorized by the copyright holders of +that material) supplement the terms of this License with terms: + + a) Disclaiming warranty or limiting liability differently from the + terms of sections 15 and 16 of this License; or + + b) Requiring preservation of specified reasonable legal notices or + author attributions in that material or in the Appropriate Legal + Notices displayed by works containing it; or + + c) Prohibiting misrepresentation of the origin of that material, or + requiring that modified versions of such material be marked in + reasonable ways as different from the original version; or + + d) Limiting the use for publicity purposes of names of licensors or + authors of the material; or + + e) Declining to grant rights under trademark law for use of some + trade names, trademarks, or service marks; or + + f) Requiring indemnification of licensors and authors of that + material by anyone who conveys the material (or modified versions of + it) with contractual assumptions of liability to the recipient, for + any liability that these contractual assumptions directly impose on + those licensors and authors. + + All other non-permissive additional terms are considered "further +restrictions" within the meaning of section 10. If the Program as you +received it, or any part of it, contains a notice stating that it is +governed by this License along with a term that is a further +restriction, you may remove that term. If a license document contains +a further restriction but permits relicensing or conveying under this +License, you may add to a covered work material governed by the terms +of that license document, provided that the further restriction does +not survive such relicensing or conveying. + + If you add terms to a covered work in accord with this section, you +must place, in the relevant source files, a statement of the +additional terms that apply to those files, or a notice indicating +where to find the applicable terms. + + Additional terms, permissive or non-permissive, may be stated in the +form of a separately written license, or stated as exceptions; +the above requirements apply either way. + + 8. Termination. + + You may not propagate or modify a covered work except as expressly +provided under this License. Any attempt otherwise to propagate or +modify it is void, and will automatically terminate your rights under +this License (including any patent licenses granted under the third +paragraph of section 11). + + However, if you cease all violation of this License, then your +license from a particular copyright holder is reinstated (a) +provisionally, unless and until the copyright holder explicitly and +finally terminates your license, and (b) permanently, if the copyright +holder fails to notify you of the violation by some reasonable means +prior to 60 days after the cessation. + + Moreover, your license from a particular copyright holder is +reinstated permanently if the copyright holder notifies you of the +violation by some reasonable means, this is the first time you have +received notice of violation of this License (for any work) from that +copyright holder, and you cure the violation prior to 30 days after +your receipt of the notice. + + Termination of your rights under this section does not terminate the +licenses of parties who have received copies or rights from you under +this License. If your rights have been terminated and not permanently +reinstated, you do not qualify to receive new licenses for the same +material under section 10. + + 9. Acceptance Not Required for Having Copies. + + You are not required to accept this License in order to receive or +run a copy of the Program. Ancillary propagation of a covered work +occurring solely as a consequence of using peer-to-peer transmission +to receive a copy likewise does not require acceptance. However, +nothing other than this License grants you permission to propagate or +modify any covered work. These actions infringe copyright if you do +not accept this License. Therefore, by modifying or propagating a +covered work, you indicate your acceptance of this License to do so. + + 10. Automatic Licensing of Downstream Recipients. + + Each time you convey a covered work, the recipient automatically +receives a license from the original licensors, to run, modify and +propagate that work, subject to this License. You are not responsible +for enforcing compliance by third parties with this License. + + An "entity transaction" is a transaction transferring control of an +organization, or substantially all assets of one, or subdividing an +organization, or merging organizations. If propagation of a covered +work results from an entity transaction, each party to that +transaction who receives a copy of the work also receives whatever +licenses to the work the party's predecessor in interest had or could +give under the previous paragraph, plus a right to possession of the +Corresponding Source of the work from the predecessor in interest, if +the predecessor has it or can get it with reasonable efforts. + + You may not impose any further restrictions on the exercise of the +rights granted or affirmed under this License. For example, you may +not impose a license fee, royalty, or other charge for exercise of +rights granted under this License, and you may not initiate litigation +(including a cross-claim or counterclaim in a lawsuit) alleging that +any patent claim is infringed by making, using, selling, offering for +sale, or importing the Program or any portion of it. + + 11. Patents. + + A "contributor" is a copyright holder who authorizes use under this +License of the Program or a work on which the Program is based. The +work thus licensed is called the contributor's "contributor version". + + A contributor's "essential patent claims" are all patent claims +owned or controlled by the contributor, whether already acquired or +hereafter acquired, that would be infringed by some manner, permitted +by this License, of making, using, or selling its contributor version, +but do not include claims that would be infringed only as a +consequence of further modification of the contributor version. For +purposes of this definition, "control" includes the right to grant +patent sublicenses in a manner consistent with the requirements of +this License. + + Each contributor grants you a non-exclusive, worldwide, royalty-free +patent license under the contributor's essential patent claims, to +make, use, sell, offer for sale, import and otherwise run, modify and +propagate the contents of its contributor version. + + In the following three paragraphs, a "patent license" is any express +agreement or commitment, however denominated, not to enforce a patent +(such as an express permission to practice a patent or covenant not to +sue for patent infringement). To "grant" such a patent license to a +party means to make such an agreement or commitment not to enforce a +patent against the party. + + If you convey a covered work, knowingly relying on a patent license, +and the Corresponding Source of the work is not available for anyone +to copy, free of charge and under the terms of this License, through a +publicly available network server or other readily accessible means, +then you must either (1) cause the Corresponding Source to be so +available, or (2) arrange to deprive yourself of the benefit of the +patent license for this particular work, or (3) arrange, in a manner +consistent with the requirements of this License, to extend the patent +license to downstream recipients. "Knowingly relying" means you have +actual knowledge that, but for the patent license, your conveying the +covered work in a country, or your recipient's use of the covered work +in a country, would infringe one or more identifiable patents in that +country that you have reason to believe are valid. + + If, pursuant to or in connection with a single transaction or +arrangement, you convey, or propagate by procuring conveyance of, a +covered work, and grant a patent license to some of the parties +receiving the covered work authorizing them to use, propagate, modify +or convey a specific copy of the covered work, then the patent license +you grant is automatically extended to all recipients of the covered +work and works based on it. + + A patent license is "discriminatory" if it does not include within +the scope of its coverage, prohibits the exercise of, or is +conditioned on the non-exercise of one or more of the rights that are +specifically granted under this License. You may not convey a covered +work if you are a party to an arrangement with a third party that is +in the business of distributing software, under which you make payment +to the third party based on the extent of your activity of conveying +the work, and under which the third party grants, to any of the +parties who would receive the covered work from you, a discriminatory +patent license (a) in connection with copies of the covered work +conveyed by you (or copies made from those copies), or (b) primarily +for and in connection with specific products or compilations that +contain the covered work, unless you entered into that arrangement, +or that patent license was granted, prior to 28 March 2007. + + Nothing in this License shall be construed as excluding or limiting +any implied license or other defenses to infringement that may +otherwise be available to you under applicable patent law. + + 12. No Surrender of Others' Freedom. + + If conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot convey a +covered work so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you may +not convey it at all. For example, if you agree to terms that obligate you +to collect a royalty for further conveying from those to whom you convey +the Program, the only way you could satisfy both those terms and this +License would be to refrain entirely from conveying the Program. + + 13. Use with the GNU Affero General Public License. + + Notwithstanding any other provision of this License, you have +permission to link or combine any covered work with a work licensed +under version 3 of the GNU Affero General Public License into a single +combined work, and to convey the resulting work. The terms of this +License will continue to apply to the part which is the covered work, +but the special requirements of the GNU Affero General Public License, +section 13, concerning interaction through a network will apply to the +combination as such. + + 14. Revised Versions of this License. + + The Free Software Foundation may publish revised and/or new versions of +the GNU General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + + Each version is given a distinguishing version number. If the +Program specifies that a certain numbered version of the GNU General +Public License "or any later version" applies to it, you have the +option of following the terms and conditions either of that numbered +version or of any later version published by the Free Software +Foundation. If the Program does not specify a version number of the +GNU General Public License, you may choose any version ever published +by the Free Software Foundation. + + If the Program specifies that a proxy can decide which future +versions of the GNU General Public License can be used, that proxy's +public statement of acceptance of a version permanently authorizes you +to choose that version for the Program. + + Later license versions may give you additional or different +permissions. However, no additional obligations are imposed on any +author or copyright holder as a result of your choosing to follow a +later version. + + 15. Disclaimer of Warranty. + + THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY +APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT +HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY +OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, +THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM +IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF +ALL NECESSARY SERVICING, REPAIR OR CORRECTION. + + 16. Limitation of Liability. + + IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS +THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY +GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE +USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF +DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD +PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), +EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF +SUCH DAMAGES. + + 17. Interpretation of Sections 15 and 16. + + If the disclaimer of warranty and limitation of liability provided +above cannot be given local legal effect according to their terms, +reviewing courts shall apply local law that most closely approximates +an absolute waiver of all civil liability in connection with the +Program, unless a warranty or assumption of liability accompanies a +copy of the Program in return for a fee. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +state the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + + Copyright (C) + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program 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 General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + +Also add information on how to contact you by electronic and paper mail. + + If the program does terminal interaction, make it output a short +notice like this when it starts in an interactive mode: + + Copyright (C) + This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the appropriate +parts of the General Public License. Of course, your program's commands +might be different; for a GUI interface, you would use an "about box". + + You should also get your employer (if you work as a programmer) or school, +if any, to sign a "copyright disclaimer" for the program, if necessary. +For more information on this, and how to apply and follow the GNU GPL, see +. + + The GNU General Public License does not permit incorporating your program +into proprietary programs. If your program is a subroutine library, you +may consider it more useful to permit linking proprietary applications with +the library. If this is what you want to do, use the GNU Lesser General +Public License instead of this License. But first, please read +. diff --git a/ChangeLog b/ChangeLog new file mode 100644 index 000000000..1bf887e46 --- /dev/null +++ b/ChangeLog @@ -0,0 +1,1704 @@ +Sun Feb 1 19:51:40 MST 2009 + Fixed Mantis #1429 (struct padding could cause sizeof + to be different on AMD64, using gcc-ism "packed" to + avoid this problem). + +Sun Feb 1 00:37:16 MST 2009 + Fixed problem in DHT client protocol that could cause + PUT requests from clients to be (frequently) lost. + +Sun Nov 2 16:06:47 MST 2008 + Fixed problem with MySQL disconnects in one module + impacting MySQL correctness in another module (caused + crashes). + +Sat Aug 16 00:18:34 CEST 2008 + Improved keyword extraction + Fixed expiration time of keywords + Releasing 0.8.0b. + +Sun Jul 20 15:27:11 MDT 2008 + Releasing 0.8.0a. + +Thu Jul 10 00:59:31 MDT 2008 + Fixed bug where datastore resizing would not + result in bloomfilter update during gnunet-update. + +Sat Jun 28 23:43:00 MDT 2008 + Fixed bug preventing namespace advertisements from + being processed. + +Sat Jun 14 00:54:39 MDT 2008 + Releasing 0.8.0. + +Fri Jun 13 23:51:00 MDT 2008 + GNUnet now compiles on OpenBSD. + +Wed Jun 11 22:17:09 MDT 2008 + Made state of gnunet-auto-share persistent. + Added support for multiple directories. Improved + FSUI/ECRS APIs to reduce the number of threads needed + for probing. Increased number of concurrent probes + allowed. + +Sat Jun 7 17:54:49 MDT 2008 + Simplified namespaces a lot. Shorter timeouts for + trying to connect to gnunetd to prevent interactive + apps from blocking too long when gnunetd is not + running. + +Sun Jun 1 21:11:54 MDT 2008 + Releasing 0.8.0pre1. + +Sun Jun 1 13:41:27 MDT 2008 + Split of libgnunetpseudonym from libgnunetnamespace + (with major API extension and changes). + +Tue May 6 04:10:02 MDT 2008 + Added simple sanity check for [MODULES] values. + Added "-s" and "-q" options to gnunet-peer-info. + +Thu Apr 24 00:05:17 MDT 2008 + Simplified FSUI stop/abort/pause/resume API calls + (no need to pass FSUI context anymore). + +Mon Apr 21 21:05:20 MDT 2008 + Adding buffered IO for FSUI's serialize and + deserialize code. + +Sun Apr 20 20:01:20 MDT 2008 + Fixed bug in HTTP transport causing lonely messages + (responses to GET) not to be delivered. + +Mon Mar 24 21:15:36 MDT 2008 + Fixed various problems with downloading locally + indexed large files (downloading large files from + other peers should not have been a problem). + +Sun Mar 2 15:33:41 MST 2008 + Added option to allow user to disable IPv6 support. + Releasing 0.8.0pre0. + +Sat Mar 1 21:14:02 MST 2008 + Fixed some dstore performance issues. + Made transports alternate between trying IPv4 and IPv6 + if both are available (since one of them maybe + misconfigured). + +Mon Feb 25 23:25:48 MST 2008 + Cleanup of the DNS code: removed GNUNET_IPvXAddress + and GNUNET_PRIP; centralized all DNS lookup code in + util/network/dns.c; improved IPv6 support for + network-client code. + +Mon Feb 25 00:01:27 MST 2008 + Added asynchronous search and download methods for + ECRS library. FSUI now can do with only one thread + per search or download (until now, we had two + threads per search / download). + +Tue Feb 19 20:35:28 MST 2008 + Updated database schemata to support O(1) operations + even if there are N files under the same keywords. + This breaks database backwards compatibility (and + no migration code is provided). + +Mon Feb 18 19:47:37 MST 2008 + Added new options -u and -s to gnunet-insert. + +Sat Feb 16 21:37:33 MST 2008 + Implemented gnunet-auto-share for automatic + sharing of directories. + +Thu Feb 14 01:02:32 MST 2008 + Made HTTP transport work over IPv6 as well. + +Wed Feb 13 22:59:07 MST 2008 + Modified mySQL and SQLite datastores to return diverse + result sets during (partial) hash-based get iterations + (should result in more diverse keyword result sets). + +Tue Feb 12 23:54:34 MST 2008 + First shot at integrated hostlist HTTP server based + on libmicrohttpd. + +Sat Feb 9 22:06:23 MST 2008 + Minor changes to the ECRS/FSUI APIs for searches + (no more timeouts and/or max-results). Clients who + need these features could easily implement them + directly (and they are not really useful to begin + with). + Also, the IPv4 and IPv6 protocols are being integrated + so that we only listen to one port (running both IPv4 + and IPv6 over the same port). This break transport + compatibility, but given that we're breaking protocol + compatibility for file-sharing already, this may + actually be a bit of a good thing (since it will now + be possible to tell if peers running the new GAP code + are connected to other peers running the new GAP code). + +Sun Feb 3 13:17:09 MST 2008 + Dramatic changes to the GAP implementation (breaking + protocol compatibility). Essentially, we can save + a few bytes in each reply. More importantly, the new + code allows the searching client to specify a set of + replies that are not desired (hopefully helping to + dramatically increase the diversity of search replies + obtained over time). Note that the actual encoding + and databases are not affected (just P2P protocol). + The update is not complete yet, but should compile. + +Tue Jan 8 20:07:20 MST 2008 + Added option for testing ("make check") to use weak(er) + PRNG for key generation (thanks to Werner Koch for + pointing out how to do it). + +Thu Dec 22 20:10:37 MST 2007 + Releasing GNUnet 0.7.3. + +Sun Dec 9 14:34:32 MST 2007 + Implemented MySQL version of the dstore-module. This + means that sqLite is now again truly optional. + +Sat Dec 8 15:15:53 MST 2007 + Integrated F2F topology into main topology module + (options are used to choose between F2F-only (old F2F), + minimum number of friend connections (NEW) or entirely + arbitrary connection set (default)). + +Thu Dec 6 20:51:17 MST 2007 + The SMTP transport is working again. + +Thu Nov 22 20:49:16 MST 2007 + United all libgnunetutil_*.so libraries into one big + libgnunetutil.so library (eliminates issues for binary + packagers and reduces file size by about 20%). Also + made sure that all exported symbols start with "GNUNET_". + +Sat Sep 29 16:48:24 MDT 2007 + Improved MySQL sqstore module performance. + Releasing GNUnet 0.7.2c. + +Sat Sep 8 18:01:36 MDT 2007 + HTTP transport seems finally ready. + +Sat Aug 25 23:58:21 MDT 2007 + New and improved gnunet-setup GTK interface. + +Mon Jul 30 00:07:44 MDT 2007 + Releasing GNUnet 0.7.2b. + +Sun Jul 29 01:53:32 MDT 2007 + Fixing log rotation. + +Sat Jul 21 23:42:11 MDT 2007 + Radical changes to MySQL implementation (trying to + address certain scalability issues). + +Sat Jul 7 00:22:47 MDT 2007 + Releasing GNUnet 0.7.2a. + +Fri Jul 6 22:54:52 MDT 2007 + Fixed bugs in F2F topology code. Eliminated a few + confusing LOG messages. + +Sun Jul 1 20:35:00 MDT 2007 + Fixed issue with too many TCP connections being + created. Reduced CPU overheads by increasing + requirements for grouping of messages. Improved + various error messages. + +Sat Jun 30 01:55:34 MDT 2007 + Improved bloomfilter recomputation after quota change. + Fixed bloomfilter size computation. You must run + gnunet-update (will take a while). + +Fri Jun 29 00:56:03 MDT 2007 + Avoid re-connecting shortly after connection was shutdown. + +Thu Jun 28 01:10:01 MDT 2007 + Fixed high-latency problems for certain SQLite operations. + Also improved SQLite performance (but not scalability) a + bit. Resolved MySQL disconnect crash (gnunetd would crash + if mysqld was stopped). + +Sat Jun 23 16:56:03 MDT 2007 + Improved CPU consumption from content migration code + by querying the DB less often, using the same content + for more peers (at the expense of 1 MB extra memory + utilization). Fixed a deadlock. + +Wed Jun 20 17:10:38 MDT 2007 + Fixed bug in MySQL module. + +Sun Jun 17 00:09:13 MDT 2007 + Releasing GNUnet 0.7.2. + +Sat Jun 16 04:43:20 MDT 2007 + http transport is amost back, now needing the + new libmicrohttpd. However, the code is still + highly experimental and not ready for production + use. + +Fri Jun 8 23:44:01 MDT 2007 + Fixed bugs preventing namespace search from + working. Also extended testcase to cover + namespace search. + +Tue May 29 23:15:27 MDT 2007 + Fixed a major bug which resulted in peers without + traffic between them disconnecting (even if that + session was their only connection). + Fixed another major bug where nodes would not + properly forward HELLOs from other peers (given + certain common/default configuration options). + +Sun May 27 22:39:48 MDT 2007 + Added new API gnunet_testing_lib.h to make it + easier to write testcases that need to start + gnunetd. API is now used everywhere and the + ugly peer.conf files and directories are gone. + Fixed a bug with inserting empty directories. + Extended ECRS API to allow partial file download. + +Sat May 26 18:24:15 MDT 2007 + Fixed bugs with testcases (changes in names and + structure of options were not reflected in .conf + files for various testcases). + Fixed issues with command-line option handling (failed to + override configuration file options). + Fixed bogus nesting in GAP routing algorithm (caused + random request drops). + +Fri May 25 12:23:38 MDT 2007 + Finished extended URITRACK API. + Renamed various (minor) libraries and function calls to + get somewhat more consistent naming conventions. + +Sun Apr 15 22:15:37 MDT 2007 + Implemented heuristic for better packing of gnunet-directories. + +Sun Mar 25 13:47:08 MDT 2007 + Releasing GNUnet 0.7.1c. + +Wed Feb 28 15:29:05 MST 2007 + Enabled abortion of gnunet-peer-info with CTRL-C. + +Mon Feb 26 19:19:29 MST 2007 + Fixed handling of -d option by gnunet-download + if -f option is not given. + +Sat Feb 24 15:43:14 MST 2007 + Fixed various bugs related to content expiration. + Completed message coverage in stats implementation. + +Fri Feb 23 17:55:46 MST 2007 + Fixed potential deadlock during download shutdown sequence. + +Thu Feb 15 21:54:15 MST 2007 + Added support for IO load detection. + +Sun Feb 11 13:53:13 MST 2007 + Releasing GNUnet 0.7.1b. + +Thu Feb 8 13:21:34 MST 2007 + Updating gettext to 0.16.1. + Minor bugfix in build process (#1180, #1181). + Minor bugfix in gnunet-search (#1179). + +Sun Jan 28 02:43:37 MST 2007 + Improving support for multiple choice configuration items + in gnunet-setup. + +Sat Jan 27 16:02:02 MST 2007 + Changing $-expansion for interpretation of configuration file + (again). Also, now the base-path for gnunetd defaults to + "/var/lib/gnunet" instead of "/var/lib/GNUnet". + +Tue Jan 23 19:48:37 MST 2007 + Fixed problem with split-extractor keywords not being used + for uploads. + +Tue Jan 16 21:43:26 MST 2007 + Expanded transport APIs to avoid building messages for + transmission just to have them rejected by blocking TCPs + with full transmission queues (happened quite a bit). + +Mon Jan 8 22:21:15 MST 2007 + Making computation of send buffer permuations more + efficient (in terms of calling weak_randomi) by only + computing permuations over the actually selected messages. + +Mon Jan 8 21:34:02 MST 2007 + Forcing -O3 for crypto library (performance critical). + Enforcing message queue limit for daemon (somehow got lost + on the way to 0.7.1, was responsible for high CPU load). + Fixing cron job deletion in core (clean shutdown). + +Sun Dec 31 23:56:31 MST 2006 + ncurses may need "-lm" in order to link. + Releasing GNUnet 0.7.1a. + +Sat Dec 30 00:21:49 MST 2006 + DHT routing now seems to work (not integrated with file-sharing, + only for gnunet-dht-query). + +Fri Dec 29 09:38:53 MST 2006 + Added UPnP support to GNUnet (IPv4 only, #843). + +Thu Dec 28 21:22:10 MST 2006 + Hostname resolution with gnunet-peer-info implemented (#1076). + Proper linking of gnunet-pseudonym (#1161). + Drop group permissions when changing user (#1162). + gnunet-download support for directory files implemented (#1013). + +Thu Dec 28 20:44:45 MST 2006 + Hopefully fixing build problems on certain systems with + unusual installation of iconv. + +Tue Dec 26 20:54:03 MST 2006 + Added additional gnunet-gtk options to client configuration + specification file. + +Sun Dec 24 18:06:04 MST 2006 + Limit memory utilization by select write queue. + Improved select write buffering (fewer malloc/free + operations and less copying). + +Sat Dec 23 22:12:17 MST 2006 + Updated various man pages and some configuration documentation. + Fixed TCP transport (did not work with NAT if port was set to 0). + Fixed verbose option for gnunet-transport-check. + Updated hostlist scripts in contrib/ to reflect new protocol IDs. + Allow aborting of gnunet-transport-check with CTRL-C. + Allow testing of bi-directional transports behind NAT with + gnunet-transport-check. + +Fri Dec 22 19:21:25 MST 2006 + Added option to set maximum number of file descriptors + (convenience for OS X users where the default is too low). + Releasing GNUnet 0.7.1. + +Thu Dec 21 20:03:07 MST 2006 + Fixed gnunet-insert "-e" option. Fixed various + crashes in client code. Improved OS X portability. + +Tue Dec 19 19:03:48 MST 2006 + Allowing GNUnet (without gnunet-setup) to be build + on systems without guile. + +Sun Dec 17 16:54:33 MST 2006 + Various improvements for OS X portability. + CPU load detection fix for Solaris. + +Sat Dec 16 11:42:13 MST 2006 + Bugfixes in collection library (gnunet-pseudonym). + +Mon Dec 11 21:11:54 MST 2006 + Implemented ncurses/dialog based gnunet-setup wizard. + Releasing GNUnet 0.7.1pre2. + +Sun Dec 10 00:36:11 MST 2006 + Fixed load calculation code. More aggressive utilization + of available resources. + +Mon Dec 4 13:24:12 MST 2006 + Releasing GNUnet 0.7.1pre1. + +Wed Nov 29 22:20:26 MST 2006 + Fixed various bugs with (de)serialization of FSUI state + and closing searches with associated downloads. Extended + FSUI test suite. + +Mon Nov 27 08:51:46 MST 2006 + Added conversion of ECRS error messages to FSUI + error event messages. + +Sun Nov 19 00:20:14 MST 2006 + Improved error handling. gnunetd now can communicate + text error messages to clients (not just error codes). + +Wed Nov 15 23:17:36 MST 2006 + Fixed expiration time setting. Improved bias of + migration towards valuable content. + Releasing GNUnet 0.7.1pre0. + +Wed Nov 1 20:58:44 MST 2006 + Fixed MySQL database size underreporting. + +Wed Nov 1 13:09:53 MST 2006 + Fixed some problems with index creation in sqlite + datastore (discovered with new sqlite benchmarking + code). Performance should improve significantly + (observed was a factor of 4-10 depending on + database size and operation). + +Thu Oct 19 23:44:24 MDT 2006 + Completed huge update to FSUI API (not fully debugged). + Major changes include: + * clients can associate pointer with each action + * abort methods can cancel action (but do not stop everything) + * upload and unindex operations can now be suspended and resumed + * cleaned up naming conventions + +Thu Sep 14 23:44:17 MDT 2006 + Breaking UDP transport protocol compatibility -- some + fields have to be switched around to make it work with + the new select code. Expect to see some warnings when + interacting with 0.7.0 peers. + +Tue Sep 5 21:28:25 PDT 2006 + Switched ncurses interface of gnunet-setup to use + external dialog library (new dependency!). + +Fri Aug 18 00:01:37 PDT 2006 + Swiched http bootstrap mechanism to using libcurl + (new dependency!). + +Sat May 20 08:37:02 PDT 2006 + Releasing GNUnet 0.7.0e. + +Sun May 14 02:51:15 PDT 2006 + Major gnunet-setup GTK code cleanup (now uses libglade). + +Sat May 13 19:35:49 PDT 2006 + Made headers more C++ friendly. + +Wed May 10 22:11:17 PDT 2006 + Fixing bug that could result in hanging the UDP transport; + found by Luigi Auriemma (thanks!). + +Sat May 6 00:17:59 PDT 2006 + Releasing GNUnet 0.7.0d. + +Fri May 5 01:33:42 PDT 2006 + Fixed bug with priorities of IBlocks of uploaded content + (priority was left at zero, making those blocks be + possibly discarded rather quickly once the datastore filled + up). + +Tue May 2 00:01:25 PDT 2006 + Fixed bug in gap where the tracking of query origins for + optimizing routing paths lacked a simple assignment of the + query origin (found by code inspection). As a result, + routing performance should improve further. Also fixed + small memory leak in gap and reduced memory consumption by + fixing Mantis #1058. + +Sat Apr 22 13:50:39 PDT 2006 + Fixed bug in util/cron.c where stopCron() would wait for an + unnecessary sleep to complete. + +Sat Mar 18 12:54:11 PST 2006 + Releasing GNUnet 0.7.0c. + +Thu Mar 16 22:14:01 PST 2006 + Fixing various routing problems (resulting in better utilization + of bandwidth by routing more content and less queries). + +Wed Mar 15 00:00:14 PST 2006 + Fixing various content migration bugs (one that disabled it, + one that made it unlikely that migration was even tried, and + then various crashes in formerly dead code). + +Mon Jan 23 19:04:17 PST 2006 + Added Swedish translation. + +Sun Jan 1 21:35:59 PST 2006 + Reduced amount of hashing done to be O(n) and not O(n^2) for + n local search results (for example, for 100 results, this can + make the difference between hashing 200 MB and hashing 20 MB). + +Sat Dec 31 17:02:37 PST 2005 + Added support for using -k multiple times in gnunet-pseudonym. + +Wen Dec 28 00:22:54 CET 2005 + Fixed deadlock in http transport. + +Fri Dec 23 17:17:18 PST 2005 + Releasing GNUnet 0.7.0b. + +Sat Nov 19 16:00:16 PST 2005 + Releasing GNUnet 0.7.0a. + +Fri Oct 7 15:28:02 PDT 2005 + Fixing Mantis #946. + +Thu Sep 15 23:56:39 PDT 2005 + Fixed various gcc 4.0 warnings. + +Sun Aug 28 15:29:56 PDT 2005 + Releasing GNUnet 0.7.0. + +Sun Aug 28 01:46:26 PDT 2005 + Updated German translation. + +Tue Aug 23 00:45:57 PDT 2005 + Fixed undersynchronization that may result in segv if pending + download was cancelled. + +Mon Aug 22 00:37:11 PDT 2005 + Made "UPDATE" use a prepared statement in MySQL datastore. + +Sun Aug 21 23:08:57 PDT 2005 + Fixed bugs causing unnecessary long delays for downloads from + loopback. Also now bypassing routing table for loopback download + (faster, avoids pollution of the routing table when not needed). + +Sun Aug 21 18:42:14 PDT 2005 + Fixed memory leak in gnunet-insert -R. Improved memory utilization + by SQlite dramatically (see discussion on gnunet-developers). + +Sat Aug 20 21:04:28 PDT 2005 + Fixed (rare) segmentation fault in insertion code. + +Sat Aug 20 19:31:10 PDT 2005 + Yet another tiny bug -- but one that hangs gnunetd; fixed in pre6a. + +Sat Aug 20 13:51:01 PDT 2005 + Releasing 0.7.0pre6. + +Sat Aug 20 12:51:27 PDT 2005 + Changed sqlite data format to avoid encoding-decoding + (breaks compatibility with previous datastores, in particular + since conversion code is NOT provided). + The page size was also increased, rendering the database files + incompatible, too. + +Thu Aug 18 21:18:28 PDT 2005 + Made quotations match GNU standards. + +Wed Aug 17 20:05:19 PDT 2005 + Fixed bug that would prevent P2P messages from being + transmitted under certain (common) circumstances. + +Mon Aug 15 00:08:47 PDT 2005 + Bugfest. Fixed various bugs in MySQL, fragmentation, + core and fs (see Mantis for more details). + +Mon Jul 18 01:03:05 CEST 2005 + Alpha-rename fest. Releasing 0.7.0pre5. + +Sun Jul 17 13:49:40 CEST 2005 + Fixed problems with struct alignment on 64-bit + AMDs. Fixed recently introduced memory leak. + Fixed various compiler warnings. + +Sat Jul 16 23:55:31 CEST 2005 + Fixed various minor problems with platforms + without gettext/NLS support and for 64-bit size_ts. + Also fixed a couple of other compiler warnings. + Cleaned up connection.c code. + +Mon Jul 11 14:41:18 CEST 2005 + Fixed tiny memory leak in http_bootstrap. + +Mon Jul 11 02:22:24 CEST 2005 + Fixed long-standing problems in statuscalls wrt + network load estimates, leading to too-high bandwidth + consumption on average. Fixed minor memory leaks. + Fixed far too often session-key exchange (was done + also for sessions that were already up). + +Thu Jul 7 15:22:21 CEST 2005 + Fixed bug in identity, missing ntohs for protocol ID. + Fixed bug in topology, wrong calculation of saturation. + +Wed Jul 6 22:34:30 CEST 2005 + Fixed bugs in core with respect to possible infinite + recursion caused by fragmentation callbacks. Also + differenciated between fatal transport sends and + transient transport sends. Cleanup of some APIs. + +Sat Jul 2 17:21:07 CEST 2005 + Fixed bugs in gnunet-pseudonym (listing of namespaces), + FSUI (double-free for namespace creation), cleaned up + APIs and implemented clearing of completed downloads + (FSUI). Releasing 0.7.0pre4. + +Fri Jul 1 15:08:42 CEST 2005 + Added statistics for transports and other connection- + related functions. Prevented core from dropping + messages just because transport is (temporarily) + blocked. Made transport selection in identity random + again where necessary. + +Thu Jun 30 20:05:15 CEST 2005 + Added cache for KBlocks (can speed up insertion speed + for recursive inserts quite a bit). + +Thu Jun 30 13:52:17 CEST 2005 + Enabled IPv6 by default. Fixed various minor bugs. + +Tue Jun 28 13:41:58 UTC 2005 + Fixed various open FIXMEs, including error handling, + bad performance and some memory leaks (gnunet-tools + only, not in gnunetd). + +Mon Jun 27 17:21:09 CEST 2005 + Fixed double-free segfault. + Fixed problem with session timeout not happening. + Fixed issue with too lazy slot reclaim for reconnect. + Made reconnect scanning more incremental (1/10th of + the work every 500ms instead of full work every 5s). + Fixed memory leak in advertisement processing. + +Sun Jun 26 18:21:17 CEST 2005 + Plenty of bugfixes everywhere, gnunet-setup works + now. Releasing 0.7.0pre3. + +Sat Jun 11 11:25:24 EST 2005 + Fixed various problems with recursive upload + (thread stack size too small, wrong filenames + passed around, wrong list of filenames updated). + +Sat Apr 2 01:54:23 EST 2005 + Various bugfixes, some of them critical (segfaults, + protocol errors (resulting in failures to connect), + old version of configuration file would be + generated if configuration was missing, + gnunet-insert would not properly work with multiple + keywords (-k and -K options)). Releasing 0.7.0pre2. + +Thu Mar 31 21:38:06 EST 2005 + Releasing 0.7.0pre1. + +Tue Mar 8 14:45:55 EST 2005 + OpenSSL cannot fully support SHA-512. Eliminated as an + option (not necessary, GPL incompatible, trouble, work + to maintain -- in summary: not worth it). + +Sat Mar 5 00:33:51 EST 2005 + Changing hash algorithm to SHA-512. + Changing symcipher algorithm to AES-256. + +Sun Feb 27 21:59:31 EST 2005 + All unit testcases pass. Releasing 0.7.0pre0. + +Sat Feb 12 17:35:08 EST 2005 + Fixed mysql-test. Changed MySQL to use prepared statements, + avoids conversion (mysql_escape_string) and is faster on the + server-side, too. + +Wed Dec 15 20:47:40 EST 2004 + Removed support for password encryption of namespace keys. + Hardly used and adds hardly any security. Encrypt /home if + you need this, would be more effective anyway. + +Fri Nov 26 06:27:12 EST 2004 + Sessionkey exchange works now, at least with OpenSSL. There + seems to be some quirk in some version of libcrypt with + respect to RSA encryption. Anyway, it is a heisenbug (sometimes + RSA decryption does not result in what was originally + encrypted), but I have the feeling this problem existed + already in 0.6.5 -- and it is now detected and does not + occur all the time. + +Tue Nov 23 02:35:50 EST 2004 + The CVS may look like it's compiling, but that's just because + most of the service modules are not even attempted. + And without those, nothing will work. But in good news, + util, transport and server do compile now. + +Sun Nov 21 03:53:22 EST 2004 + Starting to make incompatible changes. Once this is commited + to CVS (which will only happen once at least some parts compile + again), this version will no longer be compatible with any + previously released version of GNUnet (and highly experimental). + Do not yet use the new code on-line, do not expect it to work + at all! + +Sun Nov 14 16:49:02 EST 2004 + Debugged collections, seems to work (not that it looks pretty, + but it basically works, the rest is UI work). Fixed deadlock + in gnunet-gtk logging. Releasing GNUnet 0.6.5. + +Thu Nov 4 09:04:48 EST 2004 + Added (highly experimental) support for collections. + +Sat Sep 25 22:29:48 EST 2004 + Releasing GNUnet 0.6.4a. + +Sun Sep 19 23:39:04 EST 2004 + Various code cleanups and minor bugfixes. + +Sun Aug 29 15:11:28 IST 2004 + Releasing GNUnet 0.6.4. + +Tue Aug 24 20:59:14 IST 2004 + Significant enhancements to namespaces. A namespace can + now be annotated with meta-data at the time that it is + created. + +Sat Aug 21 01:00:06 IST 2004 + Adding GNU gettext support to configure. + +Thu Aug 19 01:26:55 IST 2004 + Migrating gnunet-gtk to GTK 2.0 (tested with 2.4). + +Mon Aug 16 00:30:59 IST 2004 + Removed gcry. Added dependency on GNU MP library (libgmp) + for kblocks. + +Tue Aug 3 17:57:43 IST 2004 + Fixed bugs in gnunet-update. Fixed segfault in AFS startup. + Fixed warnings in gnunet-peer-info/gnunetd. Fixed packaging + error (not all of util/gcry included in distribution). + Releasing GNUnet 0.6.3a. + +Mon Aug 2 20:06:07 IST 2004 + Releasing GNUnet 0.6.3. + +Sun Aug 1 01:31:19 IST 2004 + Changed most places from HexName to the new, shorter EncName. + Pushed URIs for namespaces and keyword searches into more + widespread use. Replaced all occurences of sprintf with + SNPRINTF (to obtain extra bounds check). + +Sun Jul 11 13:54:01 IST 2004 + Moved testcases for libgnunet_util to util/ directory. + Made DB testcases in afs/module/ run for each available + database and not just the one configured in /etc/gnunet.conf. + +Fri Jun 25 21:05:59 IST 2004 + Releasing GNUnet 0.6.2b (resolves various minor bugs). + +Wed May 5 17:34:39 EST 2004 + Releasing GNUnet 0.6.2a (resolves libgcrypt and packageing + issues, improved gtk-statistics, other minor bugfixes). + +Fri Apr 30 21:36:23 EST 2004 + Fixed synchronization problem in cron (only relevant for + parallel downloading). Releasing GNUnet 0.6.2. + +Mon Apr 26 21:13:45 EST 2004 + Added support to gnunet-check to convert file index database + to new design. Removed support for pre 0.6.1b bloomfilter + conversion (would have been too messy to support both). + Added old-version check to gnunetd startup to make updates + more systematic in the future (not perfect, though). + +Mon Apr 26 19:27:29 EST 2004 + Recursive insertion of directories with gnunet-gtk + now works including the progress bar. + +Sun Apr 25 18:24:55 EST 2004 + Global changes to fix bug #698. The fix is still + incomplete with respect to pthread.h specific structs. + +Sun Apr 25 15:20:29 EST 2004 + Fixed compile error in IPv6 transports. + +Fri Apr 23 23:38:01 EST 2004 + Added recursive insertion of directories to gnunet-gtk. + Progress-bar does not work yet, also libextractor is + always used (no option to turn it off). + +Fri Apr 23 21:49:18 EST 2004 + Removed need to specify _protocol in configuration file + (shorter is better). + +Wed Apr 21 23:32:36 EST 2004 + Added code to bound priority used by clients by twice the + average priority of requests from other peers that are + routed at the moment. That prevents clients from exposing + themselves with excessively high priorities (and also + avoids giving too much credit/trust needlessly). + +Tue Apr 20 22:27:19 EST 2004 + Added possibility of using a link instead of making a copy + if gnunetd and the insert-client both run on the same machine. + +Tue Apr 20 00:00:58 EST 2004 + Added network transparency for indexing files (by copying + them to a GNUnet(d) specific directory). This breaks + unindexing (for files indexed with previous versions) and + forces the user to make a copy of the file. In the near + future we should resolve this problem by allowing the use + of a link. + +Sun Apr 18 17:24:34 EST 2004 + Adding recursive, parallel download of directories to + gnunet-download + +Sun Apr 18 01:57:32 EST 2004 + gnunet-transport-check can now be used to check + external connectivity (with other peers). + +Sat Apr 17 17:46:19 EST 2004 + Reduced memory consuption for AFS insertion and + download. + +Sat Apr 10 21:17:46 EST 2004 + Implemented statistics-plot in gnunet-gtk based on + gnome-system-monitor code. + +Fri Apr 9 22:01:51 EST 2004 + Added support for libextractor > 0.2.7. + +Fri Apr 9 0:29:20 EST 2004 + Completed --help conversion. + +Wed Mar 31 15:40:51 EST 2004 + Releasing 0.6.1d. + +Tue Mar 30 22:42:13 EST 2004 + Integrated gnunet-pseudonym-create, -delete and -list into + gnunet-pseudonym as well as gnunet-directory-list, -emptydb + and -print into gnunet-directory. + +Tue Mar 2 10:46:01 EST 2004 + Fixing bug preventing download for indexed files <= 1k. + +Sat Feb 28 03:43:34 EST 2004 + Releasing 0.6.1c. + +Sat Feb 21 06:16:47 EST 2004 + Modified requestmanager to improve performance at the end of the + download. + +Thu Feb 19 00:48:33 EST 2004 + Fixed integer underflow possibly causing slow download performance. + +Sun Feb 15 15:17:05 EST 2004 + Fixed weak key failures with libgcrypt. + +Sun Feb 15 13:14:44 EST 2004 + New routing code (untested) commited. + +Sun Feb 15 11:58:10 EST 2004 + Fixing problem with 64-bit size_t on alpha (#655). + +Sat Jan 31 04:32:33 EST 2004 + Releasing 0.6.1b. + +Fri Jan 23 00:00:09 EST 2004 + Fixed indexed content being overwritten by migrated content + +Thu Jan 22 19:27:33 EST 2004 + Fixed integer-overflow problems in statuscalls. + Fixed missing ttl/priority adjustment for NS-Queries. + Fixed bug in routing that would let very old queries survive. + +Thu Jan 15 23:14:54 EST 2004 + Fixed size of super-bloomfilter (was factor of 32 to small). + Fixed option -r of gnunet-check (would not increment bloom filters + to appropriate value). + +Wed Dec 31 17:07:40 EST 2003 + Fixed various routing and bandwidth allocation problems. + Releasing 0.6.1a. + +Wed Dec 24 00:00:00 EST 2003 + Releasing 0.6.1. + +Fri Dec 19 00:57:29 EST 2003 + NAT transport passes tests. + +Sat Dec 13 00:16:41 EST 2003 + Added NAT transport. + +Thu Oct 16 19:20:29 EST 2003 + Added greedy knapsack heuristic for use by CPU-overloaded + systems. + +Mon Oct 13 22:36:45 EST 2003 + Improved gnunet-gtk code that forks of gnunetd (some extra checking, + use fork-exec instead of system(), pass configuration file as + argument). + +Sun Oct 12 03:42:55 EST 2003 + Split bandwidth montoring into up and downstream. + Why stop sending replies merely because we *receive* traffic!? + Also reduced frequency of HELO exchange. + Added more statistics (traffic by type). + +Fri Oct 10 02:05:13 EST 2003 + Fixed bug that wasted a lot of CPU time. + Better CPU usage control by making knapsack + solving (frequency and problem size) dependent + on CPU load. + +Thu Oct 9 14:29:35 EST 2003 + Releasing 0.6.0a. + +Sun Oct 5 21:35:23 EST 2003 + GNUnet 0.6.0 released. + +Sat Oct 4 12:54:55 EST 2003 + Fixed bug in timer code (discovered in testcase). + Added extensive testcases for platform dependent code. + +Fri Sep 19 01:38:00 EST 2003 + Added code to allow namespace queries to return multiple + results. Updates are now displayed in separate tabs. + +Wed Sep 17 23:54:15 EST 2003 + Migrated libgcrypt code in util to comply with + libgcrypt 1.1.43 (and it passes the tests just fine) + +Wed Sep 17 22:59:16 EST 2003 + Fixed problems with updates in namespaces in the GTK code. + Added boolean search capability (x AND y) to GTK search. + Made namespace search frequency respect TTL delays. + +Thu Aug 21 19:42:57 EST 2003 + GNUnet 0.5.5 released. + +Tue Aug 12 20:48:39 EST 2003 + Support for namespaces with updates is there, + but requires testing. + +Sun Jul 27 14:48:19 EST 2003 + Added download summary window to gnunet-gtk. + +Fri Jul 25 14:15:37 CET 2003 + Unioned gnunet-insert-multi and gnunet-insert-sblock + into gnunet-insert. + +Thu Jun 26 23:22:12 EST 2003 + Added draft support for directories. + +Thu Jun 26 23:21:41 EST 2003 + GNUnet 0.5.4a released + +Sat Jun 14 19:21:41 EST 2003 + Added support for HTTP-PROXY to download the hostlist. + +Wed May 29 01:13:15 EST 2003 + Fixing some ugly bugs in connection.c that caused + permanent connection-drop and connection-establish + cycles (every 5 minutes). + +Sat May 24 03:12:42 EST 2003 + Migrated subset of libgcrypt into GNUnet tree to use + it instead of OpenSSL or libgcrypt. RSA in the new + code is broken at the moment. + +Sun May 18 04:10:25 EST 2003 + GNUnet 0.5.4 released. + +Sat May 10 21:39:35 EST 2003 + Only download hostlist after a while if we were + not able to connect (to reduce load on hostlist + servers). + +Fri May 2 23:20:15 EST 2003 + Added code to handle massive collisions in datastore + (thousands of files under the same keyword). + +Sun Apr 27 23:52:12 EST 2003 + GNUnet 0.5.3 released. + +Wed Apr 23 03:20:21 EST 2003 + Build system cleanup, location of mysql, gdbm, tdb and + db can now be specified. + +Mon Apr 21 01:54:24 EST 2003 + Made random content selection with pIdx faster + (do not read entire pidx files) and fully random + (added code to weigh pidx files by number of entries). + +Sun Apr 20 23:25:45 EST 2003 + Fixed double-free in TCP transport code (seems to have + only had a real chance to occur under BSD in practice). + +Sat Apr 19 14:42:37 EST 2003 + Replaced use of dlopen for plugins with the more + portable ltdl from libtool. + +Thu Apr 17 20:36:42 EST 2003 + Added synchronization to pIdx, also use truncate + to remove last n entries instead of re-writing the + entire file (much, much faster). + +Mon Apr 7 00:00:07 EST 2003 + Implemented gnunet-delete. + +Sat Apr 5 15:55:31 EST 2003 + Made directory database use 256 sub-directories + (should increase speed a lot, with the new DB + manager, this will result in at most 2048 + files per directory). + +Mon Mar 31 16:51:24 EST 2003 + gnunet-convert and gnunet-check compile again + with the new DB code. + +Sun Mar 30 13:14:52 CET 2003 + Added BSD DB (libdb) support. + +Sun Mar 23 04:04:38 EST 2003 + Big gnunet-gtk cleanup. + +Tue Mar 18 01:52:18 EST 2003 + Releasing 0.5.2a. + +Sun Mar 16 20:24:24 EST 2003 + Caching of RSA signatures for HELOs reduced CPU + usage dramatically, from an average of 31% to less + than 5% on my machine. + +Sun Mar 16 01:21:16 EST 2003 + Fixed locking problem and problem with TCP transport + (bad set of sockets in select). + +Sat Mar 15 03:02:42 EST 2003 + Fixed bug that copied wrong content (garbage) into + replies for other peers. This one was introduced + briefly before 0.5.2. Argh. + +Fri Feb 28 00:23:02 EST 2003 + Fixed segfault in gnunet-gtk when closing search + sub-window. + +Thu Feb 27 16:23:20 EST 2003 + Releasing GNUnet 0.5.2. + +Mon Feb 24 00:28:52 EST 2003 + Cleaning up the AFS routing code, also some tweaks + to improve anonymity against an active attacker. + +Thu Feb 20 23:51:30 EST 2003 + gnunet-tracekit works now nicely (and with dot). + Also, GNUnet now attempts to locate a configuration + file in /etc/gnunet.conf if ~/.gnunet/gnunet.conf + does not exist (and nothing is specified on the + command line). + +Wed Feb 12 23:21:44 EST 2003 + Fixed deadlock between pingpong and connection + module. + +Tue Feb 4 13:22:09 EST 2003 + Releasing GNUnet 0.5.1(a). + +Tue Feb 4 04:05:58 EST 2003 + RSA encryption of OpenSSL has been changed to pure + PKCS #1 to achieve compatibility with libgcrypt. + +Mon Feb 3 18:46:54 EST 2003 + RSA signature encoding has been changed from raw + to PKCS #1 compatible encoding. This should help + making the migration to libgcrypt easier in the + future. RSA & libgcrypt are still not friends, + but the GNUnet code should be mostly there. + +Fri Jan 31 10:40:01 EST 2003 + gnunet-peer-info tool was added. This tool can + prints information about all known peers and may + be useful for diagnostics. + +Mon Jan 20 17:54:51 EST 2003 + A large number of memory leaks got fixed thanks + to the new automated scripts to find them. + +Thu Jan 16 00:49:20 EST 2003 + Nearly finished removing dependencies on OpenSSL + (using libgcrypt instead), $FOO-expansion in + configuration parsing and RSA (public key crypto) + are the only missing pieces. + +Mon Jan 13 01:53:45 EST 2003 + Fixed deadlock in gnunetd (would stop processing + messages). + +Sun Dec 27 13:34:11 CET 2002 + Fixed segmentation fault that occured when number + of known peers goes over 128. + +Sat Dec 21 16:27:11 CET 2002 + Releasing GNUnet 0.5.0. + +Sat Dec 14 03:47:05 CET 2002 + Bugfixes over bugfixes. Various memory leaks + from gnunetd to gnunet-download, problems + with bloomfilters and gnunet-check, enforcing + storage limitations set by the user, fixes + in the UDP, TCP and SMTP transports; + better thread termination (avoid asynchronous + cancellation). Not to mention the new + tools gnunet-transport-check and gnunet-tbench + for the evaulation of correctness and performance + of the transport services. + +Wed Nov 6 22:49:24 EST 2002 + Added load smoothing for network and CPU load + such that we quickly adapt to increasing load, + but slowly reduce the (effective) load value + used in the policy if the load drops. This should + help avoiding 0-1 policies where we always jump + up and down between 100% load and no load, letting + in one period all traffic through and in the next + none. + +Wed Nov 6 00:31:39 EST 2002 + Added port==0 for UDP and TCP transport as an + option to indicate that the transport is to be + used only to establish connections to other + peers but not as a server-socket (that is, + if the port is 0, other GNUnet peers can not + connect to the local node's UDP or TCP port, + but the local node can still use UDP + (unidirectional) or TCP (even bi-directional) + to talk to other peers. This is useful if a + node is behind a NAT box and can not receive + inbound traffic but is able to send messages to + other peers (and in the case of TCP even receive + messages if the local peer initiated the + bi-directional connection). Currently, a peer + must always provide at least one transport under + which the peer can be "actively" reached since + otherwise other peers will not keep the public + key of that peer and thus not be able to + complete the session key exchange. For NAT-box + peers, SMTP might be a viable choice for that + "active" protocol. + +Fri Oct 11 02:16:42 EST 2002 + Added bloomfilter interface and calls to the BF from + the AFS service implementation. Missing for working + bloomfilter are calls from gnunet-insert to update + the bloom-filter (top-CHK and super-queries) and the + actual bloom-filter implementation. + +Tue Oct 8 20:35:45 EST 2002 + TCP transport now works with only one thread (using + select) instead of one thread per peer-connection. + +Fri Sep 27 02:24:52 EST 2002 + New content encoding (still only 1k blocks, but with + CHK style IBocks and GBlocks for aggressive content + migration) now seems to work. + +Mon Sep 16 01:57:55 EST 2002 + Implemented gnunet-chat, a demo-application for + how to develop p2p applications on top of GNUnet. + +Sun Sep 15 22:48:28 EST 2002 + Fixing (long standing) problem with segfaults under BSD + due to stack overflow in threads. + +Sun Sep 15 02:04:52 EST 2002 + TCP transport layer implementation seems to work now. + gnunet.conf is now documented on the web. HELO exchange + is fixed, so is the CS-connection whitelisting. + +Fri Sep 13 00:51:01 EST 2002 + Update on the progress towards 0.4.9. What is missing? + * new content encoding to facilitate: + - content migration + - discriminated routing (downloads vs. searches) + - variable blocksize (?) + - self-synchronizing stream encoding (?) + * additional transport layers (SMTP, TCP, HTTP) + * demo application (chat) for how to use the API + * documentation on new gnunet.conf + * known bugs: + - Igor reports, HELO exchange is broken + - David reports, CS-connection whitelisting is broken + - rpm.spec is outdated (add GNUnet's dynamic libraries) + + What has been resolved so far: + Major: + * transport and application layer have been abstracted + * dynamic loading of transports and applications is + implemented + * routing much faster since slots are freed earlier + * querymanager is more intelligent in where to send + queries (presumably, not tested) + + Refactoring: + * configuration can store options from commandline + * statistics is totally dynamic and can be extended + * connection buffer does callbacks on applications to + fill buffers with queries instead of noise, and + the querymanager makes use of this + + Moving: + * common/ is gone + * gnettypes.h is gone + * AFS specific code is all in AFS + * renaming is also done + +Sun Aug 25 15:30:56 EST 2002 + Moving work towards 0.4.9 to CVS. The major goals are: + * independent transport layer (UDP, TCP, SMTP, HTTP, + any of these should be possible in the future); also, + the MTU may be defined differently by any one of them. + * independent application layer. Anonymous File Sharing + (AFS) should just be one application for the GNUnet + core. + + Other minor goals and refactorings include: + * configuration - we should be able to update dynamically + options from the gnunet.conf file. E.g. by adding options + from the command line + * statistics - we should not have a hardwired fixed set + of datapoints, the statistics module should be useful + for any kind of statistics + * noise reduction - the connection buffer should allow + the use of intelligent applications that get notified + if noise would be send and then can plug in useful data + + And then there is just moving stuff: + * move "common/" to where it belongs (either applications + or server) + * gnettypes is bad if we want to seperate app specifcs + and core. The typedefs should be placed where they are + used + * database does not belong in util, it's AFS specific + * AFS: cleaner separation between database, encoding, + protocol implementation, shell-tools and GUI code. + + And finally, renaming: + * if we have TCP for peer-to-peer communications, + we can't use "TCP" vs. "UDP" to distinguish p2p from + client-server, thus: "UDP" becomes "p2p" and "TCP" + becomes "CS". At least where they were used to distinguish + node-to-node and client-server (gnunetd). + +Sun Aug 25 15:02:31 EST 2002 + Releasing GNUnet 0.4.6. + +Thu Aug 22 01:31:21 EEST 2002 + gnunet-gtk should now be able to perform multiple + search and download operations simultaneously (atleast a couple). + +Tue Aug 20 23:11:10 EEST 2002 + Fixed segfault in gnunet-search and gnunet-gtk which appeared + when there was lots of results. + Modified gnunetd to return more results at once if there + is excess bandwidth. + +Sun Aug 18 23:57:10 EST 2002 + Reworked the lookup-database. The old database got + into trouble storing data when it was 6% full. Some + simulations show that the new approach should manage + 94% and should be about as fast (more CPU, presumably + slightly fewer random disc accesses). + This change is INCOMPATIBLE with 0.4.5. You *must* + run "gnunet-check -a" before starting this new version + of gnunetd in order to migrate to the new format. For + database simulation and statistical numbers, see + also src/historical/simula.c. + +Fri Aug 16 17:07:15 EST 2002 + Releasing GNUnet 0.4.5. + +Fri Aug 16 16:43:39 EST 2002 + Added feature that will allow the specification of + multiple URLs for downloading the initial hostlist. + +Mon Aug 12 02:28:14 EST 2002 + Fixed big problem in requestmanager that would + make downloads *very* slow for no good reason + at all (ttl increment in wrong place would make + us defer the next request for more than twice + as long as needed, plus some other minor bugs). + +Mon Aug 12 01:31:03 EST 2002 + Fixed bug #347 (option "-H" to gnunet-clients for + connecting to a remote gnunetd node). + +Mon Aug 12 01:10:17 EST 2002 + Fixed bug #348 (no more scan of the entire database + on startup, instead the databases (gdbm, tdb) contain + an entry that gives the nubmer of entries). Note that + this is not done for contentdatabase_directory. + +Sun Aug 11 17:00:03 EEST 2002 + Crude file insertion capabilities added to gnunet-gtk gui. + +Thu Aug 8 19:13:34 EST 2002 + Added progress bar / printing of insertion status to + gnunet-insert (option "-V"). + +Thu Aug 8 19:02:33 EST 2002 + Cleaning up code (establishing sessions, etc.) + +Thu Aug 8 18:03:05 EST 2002 + Fixed bug #343 (overflow in stats) by moving to + long long. Let's hope that __BYTE_ORDER is + actually defined outside of netinet/in.h. + +Thu Aug 8 17:47:17 EST 2002 + Fixed gnunetd segfaulting if gnunet.conf is not + available. Also, recvfrom returning -1 with + errno == EAGAIN was fixed earlier. + +Wed Jul 31 23:38:36 EST 2002 + Released 0.4.4. + +Wed Jul 31 21:35:53 EST 2002 + Fixed bug with TCP connection that would not + free thread resources and eventually lead to + gnunetd not being able to start new threads + anymore (mantis: #339). + +Tue Jul 30 23:38:51 EST 2002 + Fixed SIGHUP exiting. The main loop was exiting + because recvfrom returned -1 with errno EINTR, + which caused us to break out of the main loop. + Also added gnunetd.pid file to simplify killing + (or re-reading configuration of) the server. + You must specify the PIDFILE in gnunet.conf! + +Tue Jul 30 20:38:47 EST 2002 + Started writing code to enable re-reading of the + configuration file whenever we receive a SIGHUP. + Works, except that after we've read the new + config file, gnunetd exits (unknown why). + +Mon Jul 29 18:52:23 EST 2002 + Added shutdown code that terminates connection when + SIGTERM is received (to avoid keeping lots of UDP + traffic for the next couple of minutes from hosts + that still believe we're up). + +Sat Jul 27 12:39:47 EST 2002 + Added code to measure network load asymmetrically (useful + for DSL users). + +Fri Jul 26 22:46:54 EST 2002 + Released 0.4.3. + +Wed Jul 24 21:13:08 EST 2002 + Fixed bug in locking and segfault (Linux tolerated, + BSD crashed, both in connection.c), added logging service. + +Tue Jul 23 00:41:17 EST 2002 + Made gnunetd fork like a nice deamon should. Also + started to add a logging service. + +Sun Jul 21 20:39:20 EST 2002 + Fixed issue with check_database (index checking too + strict if content is available in database and + on-demand encoded; also a bounds check was missing). + +Sun Jul 21 16:05:20 EST 2002 + Fixed deadlock in heloexchange (knownhost requires + recursive lock). + +Sun Jul 21 02:02:32 EST 2002 + Improved routing table. Now does more dynamic memory + allocation, reducing the fixed-size per slot from 700 + to 56 bytes. Increased number of slots from 512 to + 8092 (which is the optimal number for a 56 kbps + modem connection). + +Sat Jul 20 23:57:06 EST 2002 + Fixed bug that caused problems for files + that were indexed and that had spaces in + the filename. + +Wed Jul 17 22:50:24 EST 2002 + Fixed bug that prohibited a transitive HELO exchange + in many cases. + +Wed Jul 17 01:48:44 EST 2002 + Fixed a couple of bugs in gnunet-check and the + insertion-tools. Now gnunet-check seems to run like + a charm. Running it to detect and fix problems in + the GNUnet databases seems now recommendable :-). + Note that you must stop gnunetd before running + gnunet-check. + +Mon Jul 15 22:27:45 EST 2002 + Added return values for insertion. If we run out of + space (in particular for the fixed-size index + database), we can now communicate the problem to the + user. + gnunet-check is now pretty much feature-complete, + but it takes of course very, very long to run. There + is now also a man-page for gnunet-check. + +Thu Jul 11 11:33:34 EST 2002 + Added randomized order of blocks for the download + (patch by I. Wronsky). + +Thu Jun 20 21:13:21 EST 2002 + Added HANGUP, a message send if one side terminates the connection. + Sending the message is optional, but it is nicer to have it. Also + started on gnunet-check, a tool to check database consistency. + +Thu Jun 20 03:11:01 EST 2002 + Improved handling of collisions in the hashes in the + lookup module (uses no longer a linear scan on the + collisions file but a hashtable for the collisions!). + +Tue Jun 18 20:29:38 EST 2002 + Released GNUnet 0.4.2. + +Tue Jun 18 20:20:55 EST 2002 + Fixed bugs in blacklisting IPs. Added feature to allow specification + which networks are trusted enough to connect to the trusted TCP + port (TCP/2086 no longer needs to be firewalled!). + +Tue Jun 18 02:35:53 EST 2002 + Fixed problems with ttl (negative TTL queries were forwarded), + also better flushing of buffers and fewer SKEY exchanges due to + a now working implementation of the blacklist. A DNS lookup for + the NAT-box-IP configuration was also added. + +Sun Jun 16 04:11:01 EST 2002 + Added code to provide statistics (gnunet-stats). + Incomplete. + +Sun Jun 9 22:56:02 EST 2002 + Released GNUnet 0.4.1. + +Sat Jun 8 05:00:36 EST 2002 + Several small bugfixes (list of shared files not updated on + insertion, highest-bit of trust not interpreted as dirty, + rpm dependency on libextractor added. + Also many improvments to the build process. + +Wed Jun 5 17:01:58 EST 2002 + Made sure that HELOs don't trigger Ping-Pong if we don't + really learn anything new. + +Tue Jun 4 17:17:45 EST 2002 + The port numbers were not in network-byte order. Fixing + this is trivial, but it breaks compatibility with 0.4.0. + Thanks to Rick Kennell for reporting the endianess issue. + +Mon Jun 3 20:35:47 EST 2002 + Fixed bug with CRC not endian-converted in gnunet-gtk. + Added mimetype and filename support to gnunet-gtk. + +Sun Jun 2 03:52:18 EST 2002 + Released 0.4.0. + +Sat Jun 1 22:13:12 EST 2002 + Added three-way handshake for SKEY exchange. Not tested. + +Sat Jun 1 16:55:18 EST 2002 + Fixed potential DDoS problem where malicious hosts could + trick GNUnet servers to repeatedly probe a non-partitipating host + trying to establish a connection. I wonder how many other + p2p systems are vulnerable. (Fix: after HELO, play PING-PONG). + The endianess issues should be fixed throughout the system, but + not tested on a real machine so far. + +Sat May 25 15:55:11 EST 2002 + Moved to new CVS server. First changes towards 0.4.0: + * mostly-big-endian + * insert/index via TCP + * bugfixes (mostly performance) + +Sat May 18 16:00:06 EST 2002 + Added mimetype, filename and version number to RootNodes + (mostly incompatible change!). Added automatic generic keyword + and mime-type extraction via libextractor to gnunet-insert. + +Tue May 14 23:59:40 EST 2002 + New storage database is starting to look good. We can now store + the data in a directory (one file per block), a gdbm database, + and presumably (not tested) in a tdb database. + +Sat May 11 22:10:56 EST 2002 + Completed, deployed and tested the new TCPIO code. This changes + the gnunetd-client protocol. Essentially, we convert the TCP stream + into records with a length and type header. This should allow us + to fix bug #212 and add other features in the future. The current + change is really only changing the core TCP code and not anything + around it. We should deploy some demultiplexing code later. + +Tue May 7 20:56:40 EST 2002 + Added IP blacklisting (for virtual private networks). + +Sat May 4 14:08:23 EST 2002 + Fixed bug with bad insertion of files smaller than 1k. + +Fri May 3 23:25:16 EST 2002 + Added IP change detection (useful for dialup) [bug #272]. + Added code to ensure that a HELO is life instantly + after receiving it, fixing the 'unknown host, + refusing SKEY problem' (bug #273). Other minor + bugfixes (#274, #283). + +Sun Apr 14 22:36:03 EST 2002 + Added option to allow NAT boxes not to exchange + foreign HELOs and thus expose the private + network. Fixed bug that all other hosts should + in fact do this exchange. + +Sat Apr 13 11:02:59 EST 2002 + Make sure that files are readable before returning TRUE + in assertIsFile() + +Fri Apr 12 03:01:06 EST 2002 + Added padding messages with random if maximum + packet size is not reached. All encrypted + packets now look absolutely uniform in size + for a non-participating adversary (before, + a host that had few queries may have sent packets + that were less than the maximum size). + +Thu Apr 11 01:43:28 EST 2002 + Added sequence numbers to protocol to defend + against replay attacks (bug #185). + +Wed Apr 10 02:07:25 EST 2002 + Bugfix gnunet-insert-mp3 (endless loop). + Updated documentation. + +Mon Apr 8 00:07:15 EST 2002 + Fixed endless-loop bug when activemigration is turned + on (during startup). + +Fri Apr 5 02:03:13 EST 2002 + Released 0.3.3. + +Fri Apr 5 00:33:42 EST 2002 + gnunet-gtk, the gtk+ based GUI is now functional and + has all the features of gnunet-search and gnunet-download, + including boolean queries. In fact, it is a bit better than + gnunet-download as it shows the progress of the download. + The manpage for gnunet-gtk was updated. Minor bugfixes. + +Mon Mar 25 02:59:13 EST 2002 + Changed writeFile to use a fourth parameter which is the + desired permissions for the file. Files in data/hosts + are now world-readable. Created man pages + +Mon Mar 18 17:56:52 EST 2002 + Released 0.3.2. + +Sun Mar 17 10:47:51 EST 2002 + Got rid of far too frequent key exchange attempts. + Added expiration for very-long-dead host keys. Stopped + forwarding of expired HELOs (receiver drops anyway). + +Wed Mar 14 05:46:20 EST 2002 + Fixed some file location inconsistencies. All files are + now in $HOME/GNUnet. RPM installation points to /var/GNUnet + and the config file is ~/GNUnet/gnunet.conf + Finished port to FreeBSD. This included mostly changes + to src/util/statuscalls.c, but also several changes to + the build system. Added a stat() call in the server code + to make sure the config file exists before sending it to + the OpenSSL conf code. Added getopt to source to fix + portability issues. Changed FREEBSD defs to SOMEBSD, also + changed in configure.in. Compiles and works on at least + FreeBSD 4.5 and OpenBSD 3.0 + +Wed Mar 6 23:15:36 EST 2002 + Added code for 'fast scan' of the database for + content to discard (should be called partial + scan). This improves the startup-time dramatically. + Every source file got the GNU header. + +Tue Mar 5 22:42:36 EST 2002 + Added option for *not* participating in content + migration. Added timeout option to gnunet-search. + gnunetd now periodically (1h) scans for new content, + no restarting required. + +Mon Mar 4 23:21:51 EST 2002 + Optimized routing and content migration. Distance + of the hash to the identity of the local host is + now taken into account for routing. Hosts give + higher priority to content that is close to their + identity (priority towards keeping it). If the + network grows, this should significantly improve + the performance. Also, the choice of the hosts + for routing is now based on proximity (to the + query), the activity level of the host and its + credit ranking. + +Sun Mar 3 00:14:58 EST 2002 + Implemented binary queries ("AND") for gnunet-search. + Made sure that content is not downloaded again if + already present (if there is other content, we + overwrite). Fixed bug in gnunet-download + (uninitialzied time-to-live). + +Sat Mar 2 01:57:37 EST 2002 + Finished porting the download-code to C. Now files can + be downloaded in using shell commands. No GUI though. + Released GNUnet v0.3.1. + +Wed Feb 20 23:47:19 EST 2002 + Big CVS moving operation. + +Sat Feb 16 23:17:27 EST 2002 + Started migration to GNUnet. + +Sun Feb 3 01:20:37 EST 2002 + Extracted crc32 method from zlib and put only that method into + primitives. Reduces our memory footprint (if nobody else uses + zlib) and we no longer depend no that library (always good). + +Sat Feb 2 22:56:32 EST 2002 + Building final tarball for v0.3.0. + +Wed Jan 30 23:41:04 EST 2002 + Created mp32gnet, a tool to automaticall index mp3 files + using information from the mp3 header as keywords (title, + artist, album, comments). The mp3 parsing code comes from + the GPL tool mp3info. + +Tue Jan 29 17:36:48 EST 2002 + Fixed bug with files that were not closed under certain + circumstances and lead to an exhaustion of the number of + concurrently open files. + +Mon Jan 21 23:34:44 EST 2002 + Bumping version numbers to v0.3.0 (not yet released, needs some + more testing to be on the safe side). + +Sun Jan 20 02:09:06 EST 2002 + Content gathering now discards the least important + content in favour of new, more important content (if + there is not space for all content). The cron + management is now used everywhere. The rpm, the init-script + and the gproxy shell script have been revised. + +Sun Jan 13 23:17:58 EST 2002 + Created cron management, replacing old control + thread (allows for better timing of events). + EvaluateContent now gets the priority of the + query that the content had. + +Sun Jan 13 00:54:56 EST 2002 + Improved choice in number of hosts to forward + queries to (now dynamic, depending on priority, etc.) + Removed many, many allocations in print statements + which also caused memory leaks if active (see + debugging.h). + Added CRC checking of the root node in GProxy. Fixed + problem with CRC in IBlocks for large files. + Added dialog to GProxy to choose where to save the file. + +Sat Jan 5 14:25:31 EST 2002 + Optimized routing table. Routing table is now a hashtable + (collisions are handled by droping either the former or the + new entry). Each bucket has it's own lock, the global + semaphore is gone. The table has many more entries (we can + afford that now that there is no longer a linear search). + +Wed Dec 26 19:30:41 CET 2001 + Created v0.2.0 + +Tue Dec 25 21:24:18 CET 2001 + Fixed TTL errors that made queries loop. Added host-evaluation + to policy (drop packets from untrusted hosts under load). Found that CRC-errors + are related to policy decisions ("drop"), cosmetic fix. + +Sun Nov 25 08:10:52 EST 2001 + Added triple-hash functionality and on-demand encoding (lookup.c). + +Sat Nov 24 07:46:10 EST 2001 + Added randomized choice of hosts for the hosttable. + +Thu Nov 22 04:57:57 EST 2001 + Fixed another bunch of big bugs, tested, seems to work smoothly. Creating V0.1.0. + +Mon Nov 19 01:22:42 EST 2001: + Fixed biggest (show-stopper) bugs. Creating V0.0.3 + +Fri Oct 26 02:20:00 EST 2001: + Created ChangeLog. Report important changes here, + report small changes to CVS only. diff --git a/HACKING b/HACKING new file mode 100644 index 000000000..3c50af952 --- /dev/null +++ b/HACKING @@ -0,0 +1,57 @@ +Naming conventions: + +include files: +- _lib: library without need for a process +- _service: library that needs a service process +- _plugin: plugin definition +- _protocol: structs used in network protocol +- exceptions: + * GNUNET_config.h --- generated // FIXME: decapitalize + * platform.h --- first included + * plibc.h --- external library + * gnunet_common.h --- fundamental routines + * gnunet_directories.h --- generated + * gettext.h --- external library + + +exported symbols: +- must start with "GNUNET_modulename_" and be defined in "modulename.c" +- exceptions: those defined in gnunet_common.h + + +testcases: +- must be called "test_module-under-test_case-description.c" +- "case-description" maybe omitted if there is only one test + + +performance tests: +- must be called "perf_module-under-test_case-description.c" +- "case-description" maybe omitted if there is only one test + + + +src/ directories: +- apps: end-user applications (i.e., gnunet-search) +- connectors: libraries requiring services (i.e., libgnunetstatistics) +- libs: standalone libraries (i.e., libgnunetecrs, etc.) +- plugins: loadable plugins (i.e., TCP transport, MySQL backend) + * transports: udp/tcp/http/dv??? +- services: arm-controlled applications (i.e., gnunet-service-statistics) +- util: library for everyone + +For each directory in services, there should be one +in connectors and vice-versa. + +For each entry in apps, there should be one in libs. + + + +Minimum file-sharing system (in order of dependency): +gnunet-arm +gnunet-transport (name?) +gnunet-core (name?) +gnunet-datastore +gnunet-statistics (integrate traffic?) +gnunet-dv +gnunet-dht +gnunet-fs diff --git a/INSTALL b/INSTALL new file mode 100644 index 000000000..d3c5b40a9 --- /dev/null +++ b/INSTALL @@ -0,0 +1,237 @@ +Installation Instructions +************************* + +Copyright (C) 1994, 1995, 1996, 1999, 2000, 2001, 2002, 2004, 2005, +2006, 2007 Free Software Foundation, Inc. + +This file is free documentation; the Free Software Foundation gives +unlimited permission to copy, distribute and modify it. + +Basic Installation +================== + +Briefly, the shell commands `./configure; make; make install' should +configure, build, and install this package. The following +more-detailed instructions are generic; see the `README' file for +instructions specific to this package. + + The `configure' shell script attempts to guess correct values for +various system-dependent variables used during compilation. It uses +those values to create a `Makefile' in each directory of the package. +It may also create one or more `.h' files containing system-dependent +definitions. Finally, it creates a shell script `config.status' that +you can run in the future to recreate the current configuration, and a +file `config.log' containing compiler output (useful mainly for +debugging `configure'). + + It can also use an optional file (typically called `config.cache' +and enabled with `--cache-file=config.cache' or simply `-C') that saves +the results of its tests to speed up reconfiguring. Caching is +disabled by default to prevent problems with accidental use of stale +cache files. + + If you need to do unusual things to compile the package, please try +to figure out how `configure' could check whether to do them, and mail +diffs or instructions to the address given in the `README' so they can +be considered for the next release. If you are using the cache, and at +some point `config.cache' contains results you don't want to keep, you +may remove or edit it. + + The file `configure.ac' (or `configure.in') is used to create +`configure' by a program called `autoconf'. You need `configure.ac' if +you want to change it or regenerate `configure' using a newer version +of `autoconf'. + +The simplest way to compile this package is: + + 1. `cd' to the directory containing the package's source code and type + `./configure' to configure the package for your system. + + Running `configure' might take a while. While running, it prints + some messages telling which features it is checking for. + + 2. Type `make' to compile the package. + + 3. Optionally, type `make check' to run any self-tests that come with + the package. + + 4. Type `make install' to install the programs and any data files and + documentation. + + 5. You can remove the program binaries and object files from the + source code directory by typing `make clean'. To also remove the + files that `configure' created (so you can compile the package for + a different kind of computer), type `make distclean'. There is + also a `make maintainer-clean' target, but that is intended mainly + for the package's developers. If you use it, you may have to get + all sorts of other programs in order to regenerate files that came + with the distribution. + + 6. Often, you can also type `make uninstall' to remove the installed + files again. + +Compilers and Options +===================== + +Some systems require unusual options for compilation or linking that the +`configure' script does not know about. Run `./configure --help' for +details on some of the pertinent environment variables. + + You can give `configure' initial values for configuration parameters +by setting variables in the command line or in the environment. Here +is an example: + + ./configure CC=c99 CFLAGS=-g LIBS=-lposix + + *Note Defining Variables::, for more details. + +Compiling For Multiple Architectures +==================================== + +You can compile the package for more than one kind of computer at the +same time, by placing the object files for each architecture in their +own directory. To do this, you can use GNU `make'. `cd' to the +directory where you want the object files and executables to go and run +the `configure' script. `configure' automatically checks for the +source code in the directory that `configure' is in and in `..'. + + With a non-GNU `make', it is safer to compile the package for one +architecture at a time in the source code directory. After you have +installed the package for one architecture, use `make distclean' before +reconfiguring for another architecture. + +Installation Names +================== + +By default, `make install' installs the package's commands under +`/usr/local/bin', include files under `/usr/local/include', etc. You +can specify an installation prefix other than `/usr/local' by giving +`configure' the option `--prefix=PREFIX'. + + You can specify separate installation prefixes for +architecture-specific files and architecture-independent files. If you +pass the option `--exec-prefix=PREFIX' to `configure', the package uses +PREFIX as the prefix for installing programs and libraries. +Documentation and other data files still use the regular prefix. + + In addition, if you use an unusual directory layout you can give +options like `--bindir=DIR' to specify different values for particular +kinds of files. Run `configure --help' for a list of the directories +you can set and what kinds of files go in them. + + If the package supports it, you can cause programs to be installed +with an extra prefix or suffix on their names by giving `configure' the +option `--program-prefix=PREFIX' or `--program-suffix=SUFFIX'. + +Optional Features +================= + +Some packages pay attention to `--enable-FEATURE' options to +`configure', where FEATURE indicates an optional part of the package. +They may also pay attention to `--with-PACKAGE' options, where PACKAGE +is something like `gnu-as' or `x' (for the X Window System). The +`README' should mention any `--enable-' and `--with-' options that the +package recognizes. + + For packages that use the X Window System, `configure' can usually +find the X include and library files automatically, but if it doesn't, +you can use the `configure' options `--x-includes=DIR' and +`--x-libraries=DIR' to specify their locations. + +Specifying the System Type +========================== + +There may be some features `configure' cannot figure out automatically, +but needs to determine by the type of machine the package will run on. +Usually, assuming the package is built to be run on the _same_ +architectures, `configure' can figure that out, but if it prints a +message saying it cannot guess the machine type, give it the +`--build=TYPE' option. TYPE can either be a short name for the system +type, such as `sun4', or a canonical name which has the form: + + CPU-COMPANY-SYSTEM + +where SYSTEM can have one of these forms: + + OS KERNEL-OS + + See the file `config.sub' for the possible values of each field. If +`config.sub' isn't included in this package, then this package doesn't +need to know the machine type. + + If you are _building_ compiler tools for cross-compiling, you should +use the option `--target=TYPE' to select the type of system they will +produce code for. + + If you want to _use_ a cross compiler, that generates code for a +platform different from the build platform, you should specify the +"host" platform (i.e., that on which the generated programs will +eventually be run) with `--host=TYPE'. + +Sharing Defaults +================ + +If you want to set default values for `configure' scripts to share, you +can create a site shell script called `config.site' that gives default +values for variables like `CC', `cache_file', and `prefix'. +`configure' looks for `PREFIX/share/config.site' if it exists, then +`PREFIX/etc/config.site' if it exists. Or, you can set the +`CONFIG_SITE' environment variable to the location of the site script. +A warning: not all `configure' scripts look for a site script. + +Defining Variables +================== + +Variables not defined in a site shell script can be set in the +environment passed to `configure'. However, some packages may run +configure again during the build, and the customized values of these +variables may be lost. In order to avoid this problem, you should set +them in the `configure' command line, using `VAR=value'. For example: + + ./configure CC=/usr/local2/bin/gcc + +causes the specified `gcc' to be used as the C compiler (unless it is +overridden in the site shell script). + +Unfortunately, this technique does not work for `CONFIG_SHELL' due to +an Autoconf bug. Until the bug is fixed you can use this workaround: + + CONFIG_SHELL=/bin/bash /bin/bash ./configure CONFIG_SHELL=/bin/bash + +`configure' Invocation +====================== + +`configure' recognizes the following options to control how it operates. + +`--help' +`-h' + Print a summary of the options to `configure', and exit. + +`--version' +`-V' + Print the version of Autoconf used to generate the `configure' + script, and exit. + +`--cache-file=FILE' + Enable the cache: use and save the results of the tests in FILE, + traditionally `config.cache'. FILE defaults to `/dev/null' to + disable caching. + +`--config-cache' +`-C' + Alias for `--cache-file=config.cache'. + +`--quiet' +`--silent' +`-q' + Do not print messages saying which checks are being made. To + suppress all normal output, redirect it to `/dev/null' (any error + messages will still be shown). + +`--srcdir=DIR' + Look for the package's source code in directory DIR. Usually + `configure' can determine that directory automatically. + +`configure' also accepts some other, not widely useful, options. Run +`configure --help' for more details. + diff --git a/Makefile.am b/Makefile.am new file mode 100644 index 000000000..c736a533d --- /dev/null +++ b/Makefile.am @@ -0,0 +1,14 @@ +INCLUDES = -I$(top_srcdir)/src/include +SUBDIRS = contrib src po + +EXTRA_DIST = \ + ABOUT-NLS \ + config.rpath \ + install-sh \ + HACKING \ + UPDATING \ + PLATFORMS \ + acinclude.m4 + +ACLOCAL_AMFLAGS = -I m4 + diff --git a/NEWS b/NEWS new file mode 100644 index 000000000..898a3dab3 --- /dev/null +++ b/NEWS @@ -0,0 +1 @@ +See ChangeLog. diff --git a/RATIONALE b/RATIONALE new file mode 100644 index 000000000..e68dcb883 --- /dev/null +++ b/RATIONALE @@ -0,0 +1,246 @@ +This document is a summary of why we're moving to GNUnet NG and what +this major redesign tries to address. + +First of all, the redesign does not (intentionally) change anything +fundamental about the application-level protocols or how files are +encoded and shared. However, it is not protocol-compatible due to +other changes that do not relate to the essence of the application +protocols. + + +The redesign tries to address the following major problem groups +describing isssues that apply more or less to all GNUnet versions +prior to 0.9.x: + + +PROBLEM GROUP 1 (scalability): +* The code was modular, but bugs were not. Memory corruption + in one plugin could cause crashes in others and it was not + always easy to identify the culprit. This approach + fundamentally does not scale (in the sense of GNUnet being + a framework and a GNUnet server running hundreds of + different application protocols -- and the result still + being debuggable, secure and stable). +* The code was heavily multi-threaded resulting in complex + locking operations. GNUnet 0.8.x had over 70 different + mutexes and almost 1000 lines of lock/unlock operations. + It is challenging for even good programmers to program or + maintain good multi-threaded code with this complexity. + The excessive locking essentially prevents GNUnet from + actually doing much in parallel on multicores. +* Despite efforts like Freeway, it was virtually + impossible to contribute code to GNUnet that was not + writen in C/C++. +* Changes to the configuration almost always required restarts + of gnunetd; the existence of change-notifications does not + really change that (how many users are even aware of SIGHUP, + and how few options worked with that -- and at what expense + in code complexity!). +* Valgrinding could only be done for the entire gnunetd + process. Given that gnunetd does quite a bit of + CPU-intensive crypto, this could not be done for a system + under heavy (or even moderate) load. +* Stack overflows with threads, while rare under Linux these + days, result in really nasty and hard-to-find crashes. +* structs of function pointers in service APIs were + needlessly adding complexity, especially since in + most cases there was no polymorphism + +SOLUTION: +* Use multiple, lously-coupled processes and one big select + loop in each (supported by a powerful library to eliminate + code duplication for each process). +* Eliminate all threads, manage the processes with a + master-process (gnunet-arm, for automatic restart manager) + which also ensures that configuration changes trigger the + necessary restarts. +* Use continuations (with timeouts) as a way to unify + cron-jobs and other event-based code (such as waiting + on network IO). + => Using multiple processes ensures that memory corruption + stays localized. + => Using multiple processes will make it easy to contribute + services written in other language(s). + => Individual services can now be subjected to valgrind + => Process priorities can be used to schedule the CPU better + Note that we can not just use one process with a big + select loop because we have blocking operations (and the + blocking is outside of our control, thanks MySQL, + sqlite, gethostbyaddr, etc.). So in order to perform + reasonably well, we need some construct for parallel + execution. + + RULE: If your service contains blocking functions, it + MUST be a process by itself. +* Eliminate structs with function pointers for service APIs; + instead, provide a library (still ending in _service.h) API + that transmits the requests nicely to the respective + process (easier to use, no need to "request" service + in the first place; API can cause process to be started/stopped + via ARM if necessary). + + +PROBLEM GROUP 2 (UTIL-APIs causing bugs): +* The existing logging functions were awkward to use and + their expressive power was never really used for much. +* While we had some rules for naming functions, there + were still plenty of inconsistencies. +* Specification of default values in configuration could + result in inconsistencies between defaults in + config.scm and defaults used by the program; also, + different defaults might have been specified for the + same option in different parts of the program. +* The TIME API did not distinguish between absolute + and relative time, requiring users to know which + type of value some variable contained and to + manually convert properly. Combined with the + possibility of integer overflows this is a major + source of bugs. +* The TIME API for seconds has a theoretical problem + with a 32-bit overflow on some platforms which is + only partially fixed by the old code with some + hackery. + +SOLUTION: +* Logging was radically simplified. +* Functions are now more conistently named. +* Configuration has no more defaults; instead, + we load a global default configuration file + before the user-specific configuration (which + can be used to override defaults); the global + default configuration file will be generated + from config.scm. +* Time now distinguishes between + struct GNUNET_TIME_Absolute and + struct GNUNET_TIME_Relative. We use structs + so that the compiler won't coerce for us + (forcing the use of specific conversion + functions which have checks for overflows, etc.). + Naturally the need to use these functions makes + the code a bit more verbose, but that's a good + thing given the potential for bugs. +* There is no more TIME API function to do anything + with 32-bit seconds + + +PROBLEM GROUP 3 (statistics): +* Databases and others needed to store capacity values + similar to what stats was already doing, but + across process lifetimes ("state"-API was a partial + solution for that, but using it was clunky) +* Only gnunetd could use statistics, but other + processes in the GNUnet system might have had + good uses for it as well + +SOLUTION: +* New statistics library and service that offer + an API to inspect and modify statistics +* Statistics are distinguished by service name + in addition to the name of the value +* Statistics can be marked as persistent, in + which case they are written to disk when + the statistics service shuts down. + => One solution for existing stats uses, + application stats, database stats and + versioning information! + + +PROBLEM GROUP 4 (Testing): +* The existing structure of the code with modules + stored in places far away from the test code + resulted in tools like lcov not giving good results. +* The codebase had evolved into a complex, deeply + nested hierarchy often with directories that + then only contained a single file. Some of these + files had the same name making it hard to find + the source corresponding to a crash based on + the reported filename/line information. +* Non-trivial portions of the code lacked good testcases, + and it was not always obvious which parts of the code + were not well-tested. + +SOLUTION: +* Code that should be tested together is now + in the same directory. +* The hierarchy is now essentially flat, each + major service having on directory under src/; + naming conventions help to make sure that + files have globally-unique names +* All code added to the new repository must + come with testcases with reasonable coverage. + + +PROBLEM GROUP 5 (core/transports): +* The new DV service requires session key exchange + between DV-neighbours, but the existing + session key code can not be used to achieve this. +* The core requires certain services + (such as identity, pingpong, fragmentation, + transport, traffic, session) which makes it + meaningless to have these as modules + (especially since there is really only one + way to implement these) +* HELLO's are larger than necessary since we need + one for each transport (and hence often have + to pick a subset of our HELLOs to transmit) +* Fragmentation is done at the core level but only + required for a few transports; future versions of + these transports might want to be aware of fragments + and do things like retransmission +* Autoconfiguration is hard since we have no good + way to detect (and then use securely) our external IP address +* It is currently not possible for multiple transports + between the same pair of peers to be used concurrently + in the same direction(s) +* We're using lots of cron-based jobs to periodically + try (and fail) to build and transmit + +SOLUTION: +* Rewrite core to integrate most of these services + into one "core" service. +* Redesign HELLO to contain the addresses for + all enabled transports in one message (avoiding + having to transmit the public key and signature + many, many times) +* With discovery being part of the transport service, + it is now also possible to "learn" our external + IP address from other peers (we just add plausible + addresses to the list; other peers will discard + those addresses that don't work for them!) +* New DV will consist of a "transport" and a + high-level service (to handle encrypted DV + control- and data-messages). +* Move expiration from one field per HELLO to one + per address +* Require signature in PONG, not in HELLO (and confirm + on address at a time) +* Move fragmentation into helper library linked + against by UDP (and others that might need it) +* Link-to-link advertising of our HELLO is transport + responsibility; global advertising/bootstrap remains + responsibility of higher layers +* Change APIs to be event-based (transports pull for + transmission data instead of core pushing and failing) + + +PROBLEM GROUP 6 (FS-APIs): +* As with gnunetd, the FS-APIs are heavily threaded, + resulting in hard-to-understand code (slightly + better than gnunetd, but not much). +* GTK in particular does not like this, resulting + in complicated code to switch to the GTK event + thread when needed (which may still be causing + problems on Gnome, not sure). +* If GUIs die (or are not properly shutdown), state + of current transactions is lost (FSUI only + saves to disk on shutdown) + +SOLUTION (draft, not done yet, details missing...): +* Eliminate threads from FS-APIs + => Open question: how to best write the APIs to + allow integration with diverse event loops + of GUI libraries? +* Store FS-state always also on disk + => Open question: how to do this without + compromising state/scalability? + diff --git a/README b/README new file mode 100644 index 000000000..84330e2ab --- /dev/null +++ b/README @@ -0,0 +1,257 @@ + Welcome to GNUnet + + +What is GNUnet? +=============== + +GNUnet is peer-to-peer framework focusing on security. The first and +primary application for GNUnet is anonymous file-sharing. GNUnet is +currently developed by a worldwide group of independent free software +developers. GNUnet is a part of the GNU project (http://www.gnu.org/). + +This is a BETA release. While there are no known significant bugs, we +are still changing significant aspects of the system in any other +version. While we believe that the system is usable, quite a few +important features -- which would improve performance make the life of +users easier -- are still missing. Also, the documentation may not be +adequate for inexperienced users. + +For a longer description of the GNUnet System see our webpages +http://www.gnu.org/software/gnunet/ and http://gnunet.org/. + + +Dependencies: +============= + +For the impatient, here is the list of immediate dependencies for +running GNUnet: + +- libextractor >= 0.5.20b +- libgcrypt >= 1.2 +- libgmp >= 4.0 +- libcurl >= 7.15.4 +- libltdl >= 2.2 (part of GNU libtool) +- libguile >= 1.8 (required for gnunet-setup) +- GNU adns >= 1.0 (strongly recommended) +- mysql >= 5.0 (strongly recommended) +- sqlite >= 3.0 (alternative to MySQL) + +Certain gnunet-setup plugins would also like to have: +- GTK >= 2.6.0 +- Qt >= 4.0 +- dialog >= 1.0-20051207 +- ncurses + +Certain transports would also like to have: +- libmicrohttpd >= 0.4.0b +- libcurl >= 7.15.4 +- libesmtp >= 1.0.4 + + +Recommended autotools for compiling the SVN version are: +- autoconf >= 2.59 +- automake >= 1.9.4 +- libtool >= 1.5 +- libltdl >= 2.2.0 (only in Debian experimental) + +See also README.debian for a list of Debian packages. + + +How to install? +=============== + +The fastest way is to use a binary package if it is available for your +system. For a more detailed description, read the installation +instructions on the webpage at http://gnunet.org/documentation.php3. + + +If you install from source, you need to install libextractor +first (download from http://gnunet.org/libextractor/). Then +you can start the actual GNUnet compilation process with: + +$ ./configure --prefix=$HOME --with-extractor=$HOME +$ make +# make install +# gnunet-setup -d +# gnunetd + +This will compile and install GNUnet to ~/bin/, ~/lib/ and ~/man/. +gnunet-setup will create the daemon configuration (-d); this step is +interactive. You can run gnunet-setup as root for a system-wide +installation or as a particular user to create a personal +installation. If you do not want to run gnunetd as root, gnunet-setup +can be used to add a user "gnunet". Data will then be stored in +/var/lib/GNUnet and gnunetd will run as that user. Note that +additional, per-user configuration files (~/.gnunet/gnunet.conf) also +need to be created by each user by running gnunet-setup without the -d +option. Depending on your operating system the wizards of +gnunet-setup can also be used to create an init script that starts +gnunetd each time the system boots. For more general information +about the GNU build process read the INSTALL file. + +GNUnet requires the GNU MP library (http://www.gnu.org/software/gmp/) +and libgcrypt. You can specify the path to libgcrypt by passing +"--with-gcrypt=PATH" to configure. You will also need either sqlite +(version 3 or higher) or MySQL (version 5.0 or higher). + +If you are compiling the code from subversion, you have to run +". bootstrap" before ./configure. If you receive an error during the +running of ". bootstrap" that looks like "macro `AM_PATH_GTK' not +found in library", you may need to run aclocal by hand with the -I +option, pointing to your aclocal m4 macros, i.e. + +$ aclocal -I /usr/local/share/aclocal + + +Configuration +============= + +GNUnet uses two types of configuration files, one for the daemon +(called gnunetd.conf) and one for each user (gnunet.conf). You can +create and edit these configuration files with the gnunet-setup tool. +You need to add the option "-d" to gnunet-setup in order to edit +gnunetd.conf (by default, gnunet-setup will edit gnunet.conf). The +defaults that are created the first time you run gnunet-setup are +usually ok, you may want to adjust the limitations (space consumption, +bandwidth, etc.) though. The configuration files are human-readable; +gnunetd's configuration is typically located at "/etc/gnunetd.conf". +The per-user configuration file should be at "~/.gnunet/gnunet.conf". +A default version of the per-user configuration will automatically +be created whenever you run any tool that needs that particular +cofniguration file. + +You MUST create /etc/gnunetd.conf explicitly before starting gnunetd, +and the recommended way to do this is to run gnunet-setup -d (plus +possibly options to specify which user interface you would perfer). + +If you want to use a different configuration file, pass the name of +the configuration file as an argument with the option "-c" to any +GNUnet application. Sending a SIGHUP to the gnunetd process will +cause gnunetd to re-read the configuration file. Note that not all +options can be changed at runtime this way (e.g. to change any port +number, you must fully restart gnunetd). + +After changing certain options (or updating GNUnet) you must re-run +gnunet-update. + + +Usage +===== + +First, you must obtain an initial list of GNUnet hosts. Knowing a +single peer is sufficient since after that GNUnet propagates +information about other peers. Note that the default "gnunetd.conf" +contains URLs from where gnunetd downloads an initial hostlist +whenever it is started. If you want to create an alternative URL for +others to use, the file can be generated on any machine running +gnunetd by periodically executing + +$ cat $GNUNETD_HOME/data/hosts/* > the_file + +If the solution with the URL is not feasible for your situation, you +can also add hosts manually. The GNUnet webpage has a public +directory of hostkeys under http://gnunet.org/hosts/. You +can of course use any other source for these files. Copy the hostkeys +to "$GNUNETD_HOME/data/hosts/" (where $GNUNETD_HOME is the +directory specified in the /etc/gnunetd.conf configuration file). + +Now start the local node using "gnunetd". gnunetd should run 24/7 if +you want to maximize your anonymity. You may start it as a service +with "/etc/init.d/gnunetd start". To insert files into GNUnet, use +the "gnunet-insert" command. + +The GTK user interface is shipped separately from GNUnet. After +downloading and installing gnunet-gtk, you can invoke the GUI with: + +$ gnunet-gtk + +For Qt/KDE users, there is also a QT user interface (also shipped +separately). If you install gnunet-qt, you can invoke the GUI with: + +$ gnunet-qt + +If you want to use the shell (part of this distribution), use + +$ gnunet-search KEYWORD + +This will display a list of results to the console. Then use + +$ gnunet-download -o FILENAME GNUNET_URI + +to retrieve a file. The GNUNET_URI is printed by gnunet-search +together with a description. + +In order to share files, the easiest way is to create a directory +with the files (and directories) that you want to share and run + +$ nohup gnunet-auto-share NAME-OF-THE-DIRECTORY & + +For further documentation, see our webpage. + + +Hacking GNUnet +============== + +Contributions are welcome, please submit bugs to +https://gnunet.org/mantis/. Please make sure to run contrib/report.sh +and include the output with your bug reports. More about how to +report bugs can be found in the GNUnet FAQ on the webpage. Submit +patches via E-Mail to gnunet-developers@gnu.org. + +In order to run the unit tests with "make check", you need to +set an environment variable ("GNUNET_PREFIX") to the directory +where GNUnet is installed (usually, GNUnet will use OS specific +tricks in order to try to figure out the PREFIX, but since the +testcase binaries are not installed, that trick does not work +for them). Also, before running any testcases, you must +complete the installation first. Quick summary: + +$ ./configure --prefix=$SOMEWHERE +$ make +$ make install +$ export GNUNET_PREFIX=$SOMEWHERE +$ make check + +If any testcases fail to pass on your system, run "contrib/report.sh" +and report the output together with information about the failing +testcase to the Mantis bugtracking system at +https://gnunet.org/mantis/. + + +Running http on port 80 +======================= + +In order to hide GNUnet's HTTP traffic perfectly, you might consider +running GNUnet's HTTP transport on port 80. However, you should not +run GNUnet as root. Instead, forward port 80 to say 8080 with this +command (as root, in your startup scripts): + +# iptables -t nat -A PREROUTING -p tcp -m tcp --dport 80 -j REDIRECT --to-ports 8080 + +Then set in the HTTP section of gnunetd.conf the "ADVERTISED-PORT" +to "80" and "PORT" to 8080. You can do the same trick for the +TCP and UDP transports if you want to map them to a priviledged +port (from the point of view of the network). + + +Running the SMTP transport +========================== + +Running GNUnet over SMTP (e-mail) is a bit more involved. Note that +you do not need to run all transports (only running the NAT transport +is the only thing that will not work). If you really want to do +P2P over SMTP, read the instructions at http://gnunet.org/smtp.php3 + + +Stay tuned +========== + +* http://www.gnu.org/software/gnunet/ +* http://gnunet.org/ +* https://gnunet.org/mantis/ +* https://gnunet.org/drupal/ +* http://mail.gnu.org/mailman/listinfo/gnunet-developers +* http://mail.gnu.org/mailman/listinfo/help-gnunet +* http://mail.gnu.org/mailman/listinfo/info-gnunet +* http://mail.gnu.org/mailman/listinfo/gnunet-svn + diff --git a/TODO b/TODO new file mode 100644 index 000000000..8c97584cc --- /dev/null +++ b/TODO @@ -0,0 +1,150 @@ +PHASE #1: (Goal: settle key design questions) + +Core: +* API review: should "bpm" and "last_activity" arguments be + included in the ClientEventHandler? +* Internal: topology +* Internal: bandwidth allocation (inbound limits, trust) +* Internal: advertising (propagate other peers' HELLOs, find new ones) +* Internal: bootstrapping + => bootstrap should use plugins, possible multiple at the same time! + +Util: +* Should "server" argument be given in event callbacks? +* consider adding "get_time" to "configuration" API + + +PHASE #2: (Goal: recover basic core functionality) + +Datastores: +* implement sqlite-based sqstore/datastore service + + implement library (talks to service) + + implement service (datastore + talks to plugin) + + implement sqlite plugin (talks to DB) +* implement sqlite-based dstore services + + implement library (talks to service) + + implement service (talks to plugin) + + implement sqlite plugin (talks to DB) + +Applications: +* implement hostlist service (need to bootstrap!) +* DHT, DV +* FS / fs-libs + +Databases: +* mysql & postgres backend + +Transports: +* UDP backend (need LIBRARY to support (de)fragmentation!) +* HTTP backend +* Testing: + + Testcases for set_quota, timeouts, disconnects, transmit_ready_cancel + + Better coverage of gnunet-service-transport (hello validation) + + direct test of plugins compliance to plugin API + + +PHASE #3: (Goal: ready for pre-release) [completion-goal: end of 2009] + +* testing +* setup +* gtk, qt GUIs +* tbench +* tracekit +* vpn + + + +Minor TODO items: +* SERVER: + - inefficient memmove +* TRANSPORT: + - transport_api: support forcing disconnects through low quotas! + - API: consider having core provide priority and possibly + deadline information for each message + (likely important for DV plugin which wants to loop back!) + - implement transport API to pretty-print transport address + + transport_api extension (API extension!) + + service-transport extension (protocol extension) + - add calls to statistics in various places + - implement gnunet-transport (transport configurator / tester) + - UPnP-based IP detection + (Note: build library always, build service when libxml2/etc. are available) + - instantly filter addresses from *other* peers that + are *equal* to our own address + port (i.e., localhost:2086). We + no longer filter those for outgoing (helps with loopback testing + and keeps the code clean), but we should filter strictly *impossible* + incoming addresses! This is for efficiency, not correctness. + - We currently are happy to take any address told to us in a WELCOME + to our set of addresses; we should have some minimal threshold-based + scheme, limiting both the total number of addresses that we accept + this way as well as requiring multiple confirmations; also, we + should possibly try to confirm that the given address works for + us ourselves (loopback-style) before adding it to the list + [SECURITY issue] + - not sure current way of doing ACKs works well-enough + with unreliable transports where the ACK maybe lost; + the "is_new" check would then possibly prevent future + ACKs to be delivered, all while we're happily + receiving messages from that peer! Worse, the other + peer won't generate another ACK since it thinks we're + connected just fine... + Key questions: + + How necessary is ACKing in the first place? (alternatives?) + + Should we transmit ACKs in response to every HELLO? (would that + fully address the problem?) + - latency measurements implemented in the transport + plugins makes it only work for bi-di transports + and results in code replication + - should latency be included in the ReceiveCallback and + NotifyConnect or passed on request? +* SETUP: + - auto-generate "defaults.conf" using gnunet-setup from "config.scm" + - integrate all options into "config.scm" + - change config-file writing to exclude options set to default values +* ARM: + - implement exponential back-off for service restarts + - better tracking of which config changes actually need to cause process restarts by ARM. + - have way to specify dependencies between services (to manage ARM restarts better) +* PEERINFO: + - have gnunet-peerinfo print actual host addresses again + - add option to gnunet-peerinfo to modify trust value +* POSTGRES-DB: + - finish postgres implementation; simplify other SQLs using new stats +* HTTPS transport + - PolariSSL for MHD? + - https integration +* GAP improvements: + - active reply route caching design & implementation of service, + gap extension! + +=> PRE-RELEASE + +PHASE #4: [completion-goal: mid 2010] +* Documentation +* new webpage + + + + +Stuff to remember: +* Features eliminated from util: + - threading (goal: good riddance!) + - complex logging features [ectx-passing, target-kinds] (goal: good riddance!) + - complex configuration features [defaults, notifications] (goal: good riddance!) + - network traffic monitors (goal: eliminate) + - IPC semaphores (goal: d-bus? / eliminate?) + - second timers + - DNS lookup (goal: have async service; issue: still need synchronous resolution in places, current code may not be portable) + => code shrunk from 61 files to 34, 22k LOC to 15k LOC, 470k to 330k (with symbols) +* New features in util: + - scheduler + - service and program boot-strap code +* Major changes in util: + - more expressive server (replaces selector) +* Open questions: + - how to integrate scheduler with GTK event loop! + + + +Test coverage: +* UTIL: 75%, 4914 out of 6463 diff --git a/bin/grephdr.sh b/bin/grephdr.sh new file mode 100755 index 000000000..b0f979196 --- /dev/null +++ b/bin/grephdr.sh @@ -0,0 +1,3 @@ +#!/bin/sh +# grepsrc.sh string --- greps for string over all java files +find . -name "*.h" -print | grep -v "#" | xargs grep "$@" diff --git a/bin/grepsrc.sh b/bin/grepsrc.sh new file mode 100755 index 000000000..0c18dd7d2 --- /dev/null +++ b/bin/grepsrc.sh @@ -0,0 +1,3 @@ +#!/bin/sh +# grepsrc.sh string --- greps for string over all C files +find . -name "*.c" -print | grep -v "#" | xargs grep -n "$*" diff --git a/bin/rename.sh b/bin/rename.sh new file mode 100755 index 000000000..7d5790b41 --- /dev/null +++ b/bin/rename.sh @@ -0,0 +1,6 @@ +#!/bin/sh +for n in `find * -name "*.c"` `find * -name "*.h"` +do + cat $n | sed -e "s/$1/$2/g" > $n.new + mv $n.new $n || echo "Move failed: $n.new to $n" +done diff --git a/bootstrap b/bootstrap new file mode 100755 index 000000000..e5ddbc6a8 --- /dev/null +++ b/bootstrap @@ -0,0 +1,7 @@ +#!/bin/sh +libtoolize -ifc +aclocal -I m4 +autoheader +autoconf +automake --add-missing --copy + diff --git a/config.rpath b/config.rpath new file mode 100755 index 000000000..c492a93b6 --- /dev/null +++ b/config.rpath @@ -0,0 +1,614 @@ +#! /bin/sh +# Output a system dependent set of variables, describing how to set the +# run time search path of shared libraries in an executable. +# +# Copyright 1996-2006 Free Software Foundation, Inc. +# Taken from GNU libtool, 2001 +# Originally by Gordon Matzigkeit , 1996 +# +# This file is free software; the Free Software Foundation gives +# unlimited permission to copy and/or distribute it, with or without +# modifications, as long as this notice is preserved. +# +# The first argument passed to this file is the canonical host specification, +# CPU_TYPE-MANUFACTURER-OPERATING_SYSTEM +# or +# CPU_TYPE-MANUFACTURER-KERNEL-OPERATING_SYSTEM +# The environment variables CC, GCC, LDFLAGS, LD, with_gnu_ld +# should be set by the caller. +# +# The set of defined variables is at the end of this script. + +# Known limitations: +# - On IRIX 6.5 with CC="cc", the run time search patch must not be longer +# than 256 bytes, otherwise the compiler driver will dump core. The only +# known workaround is to choose shorter directory names for the build +# directory and/or the installation directory. + +# All known linkers require a `.a' archive for static linking (except MSVC, +# which needs '.lib'). +libext=a +shrext=.so + +host="$1" +host_cpu=`echo "$host" | sed 's/^\([^-]*\)-\([^-]*\)-\(.*\)$/\1/'` +host_vendor=`echo "$host" | sed 's/^\([^-]*\)-\([^-]*\)-\(.*\)$/\2/'` +host_os=`echo "$host" | sed 's/^\([^-]*\)-\([^-]*\)-\(.*\)$/\3/'` + +# Code taken from libtool.m4's _LT_CC_BASENAME. + +for cc_temp in $CC""; do + case $cc_temp in + compile | *[\\/]compile | ccache | *[\\/]ccache ) ;; + distcc | *[\\/]distcc | purify | *[\\/]purify ) ;; + \-*) ;; + *) break;; + esac +done +cc_basename=`echo "$cc_temp" | sed -e 's%^.*/%%'` + +# Code taken from libtool.m4's AC_LIBTOOL_PROG_COMPILER_PIC. + +wl= +if test "$GCC" = yes; then + wl='-Wl,' +else + case "$host_os" in + aix*) + wl='-Wl,' + ;; + darwin*) + case $cc_basename in + xlc*) + wl='-Wl,' + ;; + esac + ;; + mingw* | pw32* | os2*) + ;; + hpux9* | hpux10* | hpux11*) + wl='-Wl,' + ;; + irix5* | irix6* | nonstopux*) + wl='-Wl,' + ;; + newsos6) + ;; + linux*) + case $cc_basename in + icc* | ecc*) + wl='-Wl,' + ;; + pgcc | pgf77 | pgf90) + wl='-Wl,' + ;; + ccc*) + wl='-Wl,' + ;; + como) + wl='-lopt=' + ;; + *) + case `$CC -V 2>&1 | sed 5q` in + *Sun\ C*) + wl='-Wl,' + ;; + esac + ;; + esac + ;; + osf3* | osf4* | osf5*) + wl='-Wl,' + ;; + sco3.2v5*) + ;; + solaris*) + wl='-Wl,' + ;; + sunos4*) + wl='-Qoption ld ' + ;; + sysv4 | sysv4.2uw2* | sysv4.3* | sysv5*) + wl='-Wl,' + ;; + sysv4*MP*) + ;; + unicos*) + wl='-Wl,' + ;; + uts4*) + ;; + esac +fi + +# Code taken from libtool.m4's AC_LIBTOOL_PROG_LD_SHLIBS. + +hardcode_libdir_flag_spec= +hardcode_libdir_separator= +hardcode_direct=no +hardcode_minus_L=no + +case "$host_os" in + cygwin* | mingw* | pw32*) + # FIXME: the MSVC++ port hasn't been tested in a loooong time + # When not using gcc, we currently assume that we are using + # Microsoft Visual C++. + if test "$GCC" != yes; then + with_gnu_ld=no + fi + ;; + interix*) + # we just hope/assume this is gcc and not c89 (= MSVC++) + with_gnu_ld=yes + ;; + openbsd*) + with_gnu_ld=no + ;; +esac + +ld_shlibs=yes +if test "$with_gnu_ld" = yes; then + # Set some defaults for GNU ld with shared library support. These + # are reset later if shared libraries are not supported. Putting them + # here allows them to be overridden if necessary. + # Unlike libtool, we use -rpath here, not --rpath, since the documented + # option of GNU ld is called -rpath, not --rpath. + hardcode_libdir_flag_spec='${wl}-rpath ${wl}$libdir' + case "$host_os" in + aix3* | aix4* | aix5*) + # On AIX/PPC, the GNU linker is very broken + if test "$host_cpu" != ia64; then + ld_shlibs=no + fi + ;; + amigaos*) + hardcode_libdir_flag_spec='-L$libdir' + hardcode_minus_L=yes + # Samuel A. Falvo II reports + # that the semantics of dynamic libraries on AmigaOS, at least up + # to version 4, is to share data among multiple programs linked + # with the same dynamic library. Since this doesn't match the + # behavior of shared libraries on other platforms, we cannot use + # them. + ld_shlibs=no + ;; + beos*) + if $LD --help 2>&1 | grep ': supported targets:.* elf' > /dev/null; then + : + else + ld_shlibs=no + fi + ;; + cygwin* | mingw* | pw32*) + # hardcode_libdir_flag_spec is actually meaningless, as there is + # no search path for DLLs. + hardcode_libdir_flag_spec='-L$libdir' + if $LD --help 2>&1 | grep 'auto-import' > /dev/null; then + : + else + ld_shlibs=no + fi + ;; + interix3*) + hardcode_direct=no + hardcode_libdir_flag_spec='${wl}-rpath,$libdir' + ;; + linux*) + if $LD --help 2>&1 | grep ': supported targets:.* elf' > /dev/null; then + : + else + ld_shlibs=no + fi + ;; + netbsd*) + ;; + solaris*) + if $LD -v 2>&1 | grep 'BFD 2\.8' > /dev/null; then + ld_shlibs=no + elif $LD --help 2>&1 | grep ': supported targets:.* elf' > /dev/null; then + : + else + ld_shlibs=no + fi + ;; + sysv5* | sco3.2v5* | sco5v6* | unixware* | OpenUNIX*) + case `$LD -v 2>&1` in + *\ [01].* | *\ 2.[0-9].* | *\ 2.1[0-5].*) + ld_shlibs=no + ;; + *) + if $LD --help 2>&1 | grep ': supported targets:.* elf' > /dev/null; then + hardcode_libdir_flag_spec='`test -z "$SCOABSPATH" && echo ${wl}-rpath,$libdir`' + else + ld_shlibs=no + fi + ;; + esac + ;; + sunos4*) + hardcode_direct=yes + ;; + *) + if $LD --help 2>&1 | grep ': supported targets:.* elf' > /dev/null; then + : + else + ld_shlibs=no + fi + ;; + esac + if test "$ld_shlibs" = no; then + hardcode_libdir_flag_spec= + fi +else + case "$host_os" in + aix3*) + # Note: this linker hardcodes the directories in LIBPATH if there + # are no directories specified by -L. + hardcode_minus_L=yes + if test "$GCC" = yes; then + # Neither direct hardcoding nor static linking is supported with a + # broken collect2. + hardcode_direct=unsupported + fi + ;; + aix4* | aix5*) + if test "$host_cpu" = ia64; then + # On IA64, the linker does run time linking by default, so we don't + # have to do anything special. + aix_use_runtimelinking=no + else + aix_use_runtimelinking=no + # Test if we are trying to use run time linking or normal + # AIX style linking. If -brtl is somewhere in LDFLAGS, we + # need to do runtime linking. + case $host_os in aix4.[23]|aix4.[23].*|aix5*) + for ld_flag in $LDFLAGS; do + if (test $ld_flag = "-brtl" || test $ld_flag = "-Wl,-brtl"); then + aix_use_runtimelinking=yes + break + fi + done + ;; + esac + fi + hardcode_direct=yes + hardcode_libdir_separator=':' + if test "$GCC" = yes; then + case $host_os in aix4.[012]|aix4.[012].*) + collect2name=`${CC} -print-prog-name=collect2` + if test -f "$collect2name" && \ + strings "$collect2name" | grep resolve_lib_name >/dev/null + then + # We have reworked collect2 + hardcode_direct=yes + else + # We have old collect2 + hardcode_direct=unsupported + hardcode_minus_L=yes + hardcode_libdir_flag_spec='-L$libdir' + hardcode_libdir_separator= + fi + ;; + esac + fi + # Begin _LT_AC_SYS_LIBPATH_AIX. + echo 'int main () { return 0; }' > conftest.c + ${CC} ${LDFLAGS} conftest.c -o conftest + aix_libpath=`dump -H conftest 2>/dev/null | sed -n -e '/Import File Strings/,/^$/ { /^0/ { s/^0 *\(.*\)$/\1/; p; } +}'` + if test -z "$aix_libpath"; then + aix_libpath=`dump -HX64 conftest 2>/dev/null | sed -n -e '/Import File Strings/,/^$/ { /^0/ { s/^0 *\(.*\)$/\1/; p; } +}'` + fi + if test -z "$aix_libpath"; then + aix_libpath="/usr/lib:/lib" + fi + rm -f conftest.c conftest + # End _LT_AC_SYS_LIBPATH_AIX. + if test "$aix_use_runtimelinking" = yes; then + hardcode_libdir_flag_spec='${wl}-blibpath:$libdir:'"$aix_libpath" + else + if test "$host_cpu" = ia64; then + hardcode_libdir_flag_spec='${wl}-R $libdir:/usr/lib:/lib' + else + hardcode_libdir_flag_spec='${wl}-blibpath:$libdir:'"$aix_libpath" + fi + fi + ;; + amigaos*) + hardcode_libdir_flag_spec='-L$libdir' + hardcode_minus_L=yes + # see comment about different semantics on the GNU ld section + ld_shlibs=no + ;; + bsdi[45]*) + ;; + cygwin* | mingw* | pw32*) + # When not using gcc, we currently assume that we are using + # Microsoft Visual C++. + # hardcode_libdir_flag_spec is actually meaningless, as there is + # no search path for DLLs. + hardcode_libdir_flag_spec=' ' + libext=lib + ;; + darwin* | rhapsody*) + hardcode_direct=no + if test "$GCC" = yes ; then + : + else + case $cc_basename in + xlc*) + ;; + *) + ld_shlibs=no + ;; + esac + fi + ;; + dgux*) + hardcode_libdir_flag_spec='-L$libdir' + ;; + freebsd1*) + ld_shlibs=no + ;; + freebsd2.2*) + hardcode_libdir_flag_spec='-R$libdir' + hardcode_direct=yes + ;; + freebsd2*) + hardcode_direct=yes + hardcode_minus_L=yes + ;; + freebsd* | kfreebsd*-gnu | dragonfly*) + hardcode_libdir_flag_spec='-R$libdir' + hardcode_direct=yes + ;; + hpux9*) + hardcode_libdir_flag_spec='${wl}+b ${wl}$libdir' + hardcode_libdir_separator=: + hardcode_direct=yes + # hardcode_minus_L: Not really in the search PATH, + # but as the default location of the library. + hardcode_minus_L=yes + ;; + hpux10*) + if test "$with_gnu_ld" = no; then + hardcode_libdir_flag_spec='${wl}+b ${wl}$libdir' + hardcode_libdir_separator=: + hardcode_direct=yes + # hardcode_minus_L: Not really in the search PATH, + # but as the default location of the library. + hardcode_minus_L=yes + fi + ;; + hpux11*) + if test "$with_gnu_ld" = no; then + hardcode_libdir_flag_spec='${wl}+b ${wl}$libdir' + hardcode_libdir_separator=: + case $host_cpu in + hppa*64*|ia64*) + hardcode_direct=no + ;; + *) + hardcode_direct=yes + # hardcode_minus_L: Not really in the search PATH, + # but as the default location of the library. + hardcode_minus_L=yes + ;; + esac + fi + ;; + irix5* | irix6* | nonstopux*) + hardcode_libdir_flag_spec='${wl}-rpath ${wl}$libdir' + hardcode_libdir_separator=: + ;; + netbsd*) + hardcode_libdir_flag_spec='-R$libdir' + hardcode_direct=yes + ;; + newsos6) + hardcode_direct=yes + hardcode_libdir_flag_spec='${wl}-rpath ${wl}$libdir' + hardcode_libdir_separator=: + ;; + openbsd*) + hardcode_direct=yes + if test -z "`echo __ELF__ | $CC -E - | grep __ELF__`" || test "$host_os-$host_cpu" = "openbsd2.8-powerpc"; then + hardcode_libdir_flag_spec='${wl}-rpath,$libdir' + else + case "$host_os" in + openbsd[01].* | openbsd2.[0-7] | openbsd2.[0-7].*) + hardcode_libdir_flag_spec='-R$libdir' + ;; + *) + hardcode_libdir_flag_spec='${wl}-rpath,$libdir' + ;; + esac + fi + ;; + os2*) + hardcode_libdir_flag_spec='-L$libdir' + hardcode_minus_L=yes + ;; + osf3*) + hardcode_libdir_flag_spec='${wl}-rpath ${wl}$libdir' + hardcode_libdir_separator=: + ;; + osf4* | osf5*) + if test "$GCC" = yes; then + hardcode_libdir_flag_spec='${wl}-rpath ${wl}$libdir' + else + # Both cc and cxx compiler support -rpath directly + hardcode_libdir_flag_spec='-rpath $libdir' + fi + hardcode_libdir_separator=: + ;; + solaris*) + hardcode_libdir_flag_spec='-R$libdir' + ;; + sunos4*) + hardcode_libdir_flag_spec='-L$libdir' + hardcode_direct=yes + hardcode_minus_L=yes + ;; + sysv4) + case $host_vendor in + sni) + hardcode_direct=yes # is this really true??? + ;; + siemens) + hardcode_direct=no + ;; + motorola) + hardcode_direct=no #Motorola manual says yes, but my tests say they lie + ;; + esac + ;; + sysv4.3*) + ;; + sysv4*MP*) + if test -d /usr/nec; then + ld_shlibs=yes + fi + ;; + sysv4*uw2* | sysv5OpenUNIX* | sysv5UnixWare7.[01].[10]* | unixware7*) + ;; + sysv5* | sco3.2v5* | sco5v6*) + hardcode_libdir_flag_spec='`test -z "$SCOABSPATH" && echo ${wl}-R,$libdir`' + hardcode_libdir_separator=':' + ;; + uts4*) + hardcode_libdir_flag_spec='-L$libdir' + ;; + *) + ld_shlibs=no + ;; + esac +fi + +# Check dynamic linker characteristics +# Code taken from libtool.m4's AC_LIBTOOL_SYS_DYNAMIC_LINKER. +libname_spec='lib$name' +case "$host_os" in + aix3*) + ;; + aix4* | aix5*) + ;; + amigaos*) + ;; + beos*) + ;; + bsdi[45]*) + ;; + cygwin* | mingw* | pw32*) + shrext=.dll + ;; + darwin* | rhapsody*) + shrext=.dylib + ;; + dgux*) + ;; + freebsd1*) + ;; + kfreebsd*-gnu) + ;; + freebsd* | dragonfly*) + ;; + gnu*) + ;; + hpux9* | hpux10* | hpux11*) + case $host_cpu in + ia64*) + shrext=.so + ;; + hppa*64*) + shrext=.sl + ;; + *) + shrext=.sl + ;; + esac + ;; + interix3*) + ;; + irix5* | irix6* | nonstopux*) + case "$host_os" in + irix5* | nonstopux*) + libsuff= shlibsuff= + ;; + *) + case $LD in + *-32|*"-32 "|*-melf32bsmip|*"-melf32bsmip ") libsuff= shlibsuff= ;; + *-n32|*"-n32 "|*-melf32bmipn32|*"-melf32bmipn32 ") libsuff=32 shlibsuff=N32 ;; + *-64|*"-64 "|*-melf64bmip|*"-melf64bmip ") libsuff=64 shlibsuff=64 ;; + *) libsuff= shlibsuff= ;; + esac + ;; + esac + ;; + linux*oldld* | linux*aout* | linux*coff*) + ;; + linux*) + ;; + knetbsd*-gnu) + ;; + netbsd*) + ;; + newsos6) + ;; + nto-qnx*) + ;; + openbsd*) + ;; + os2*) + libname_spec='$name' + shrext=.dll + ;; + osf3* | osf4* | osf5*) + ;; + solaris*) + ;; + sunos4*) + ;; + sysv4 | sysv4.3*) + ;; + sysv4*MP*) + ;; + sysv5* | sco3.2v5* | sco5v6* | unixware* | OpenUNIX* | sysv4*uw2*) + ;; + uts4*) + ;; +esac + +sed_quote_subst='s/\(["`$\\]\)/\\\1/g' +escaped_wl=`echo "X$wl" | sed -e 's/^X//' -e "$sed_quote_subst"` +shlibext=`echo "$shrext" | sed -e 's,^\.,,'` +escaped_hardcode_libdir_flag_spec=`echo "X$hardcode_libdir_flag_spec" | sed -e 's/^X//' -e "$sed_quote_subst"` + +LC_ALL=C sed -e 's/^\([a-zA-Z0-9_]*\)=/acl_cv_\1=/' </dev/null 2>&1; +then + CFLAGS="-fno-strict-aliasing $CFLAGS" +fi + +# Use Linux interface name unless the OS has a different preference +DEFAULT_INTERFACE="\"eth0\"" + +# Check system type +case "$host_os" in +*darwin* | *rhapsody* | *macosx*) + AC_DEFINE_UNQUOTED(OSX,1,[This is an OS X system]) + CFLAGS="-no-cpp-precomp -fno-common $CFLAGS" + AC_MSG_WARN([The VPN application cannot be compiled on your OS]) + build_target="macosx" + DEFAULT_INTERFACE="\"en0\"" + LIBPREFIX= + DLLDIR=lib + ;; +linux*) + AC_DEFINE_UNQUOTED(LINUX,1,[This is a Linux system]) + build_target="linux" + LIBPREFIX= + DLLDIR=lib + AC_PATH_XTRA + ;; +freebsd*) + AC_DEFINE_UNQUOTED(SOMEBSD,1,[This is a BSD system]) + AC_DEFINE_UNQUOTED(FREEBSD,1,[This is a FreeBSD system]) + CFLAGS="-D_THREAD_SAFE $CFLAGS" + build_target="freebsd" + LIBPREFIX= + DLLDIR=lib + ;; +openbsd*) + AC_DEFINE_UNQUOTED(SOMEBSD,1,[This is a BSD system]) + AC_DEFINE_UNQUOTED(OPENBSD,1,[This is an OpenBSD system]) + LIBS=`echo $LIBS | sed -e "s/-ldl//"` + build_target="openbsd" + LIBPREFIX= + DLLDIR=lib + ;; +netbsd*) + AC_DEFINE_UNQUOTED(SOMEBSD,1,[This is a BSD system]) + AC_DEFINE_UNQUOTED(NETBSD,1,[This is a NetBSD system]) + LIBPREFIX= + DLLDIR=lib + ;; +*solaris*) + AC_DEFINE_UNQUOTED(SOLARIS,1,[This is a Solaris system]) + AC_DEFINE_UNQUOTED(_REENTRANT,1,[Need with solaris or errno doesnt work]) + AC_CHECK_LIB(resolv, res_init) + AC_CHECK_LIB(rt, nanosleep) + build_target="solaris" + LIBPREFIX= + DLLDIR=lib + ;; +*arm-linux*) + AC_DEFINE_UNQUOTED(LINUX,1,[This is a Linux system]) + CFLAGS="-D_REENTRANT -fPIC -pipe $CFLAGS" + build_target="linux" + LIBPREFIX= + DLLDIR=lib + ;; +*cygwin*) + AC_DEFINE_UNQUOTED(CYGWIN,1,[This is a Cygwin system]) + AC_DEFINE_UNQUOTED(WINDOWS,1,[This is a Windows system]) + AC_CHECK_LIB(intl, gettext) + LDFLAGS="$LDFLAGS -no-undefined" + CFLAGS="-mms-bitfields $CFLAGS" + build_target="cygwin" + LIBPREFIX=lib + DLLDIR=bin + AC_PROG_CXX + ;; +*mingw*) + AC_DEFINE_UNQUOTED(MINGW,1,[This is a MinGW system]) + AC_DEFINE_UNQUOTED(WINDOWS,1,[This is a Windows system]) + AC_DEFINE_UNQUOTED(_WIN32,1,[This is a Windows system]) + AC_CHECK_LIB(intl, gettext) + LDFLAGS="$LDFLAGS -no-undefined -Wl,--export-all-symbols" + LIBS="$LIBS -lws2_32 -lplibc" + CFLAGS="-mms-bitfields $CFLAGS" + build_target="mingw" + AC_PROG_CXX + LIBPREFIX=lib + DLLDIR=bin + ;; +*) + AC_MSG_RESULT(Unrecognised OS $host_os) + AC_DEFINE_UNQUOTED(OTHEROS,1,[Some strange OS]) +;; +esac +AC_DEFINE_UNQUOTED([GNUNET_DEFAULT_INTERFACE], $DEFAULT_INTERFACE, [This should be the default choice for the name of the first network interface]) +AC_SUBST(DEFAULT_INTERFACE) + +AC_MSG_CHECKING([for build target]) +AM_CONDITIONAL(MACOSX, test "$build_target" = "macosx") +AM_CONDITIONAL(CYGWIN, test "$build_target" = "cygwin") +AM_CONDITIONAL(MINGW, test "$build_target" = "mingw") +AM_CONDITIONAL(SOLARIS, test "$build_target" = "solaris") +AM_CONDITIONAL(XFREEBSD, test "$build_target" = "freebsd") +AM_CONDITIONAL(OPENBSD, test "$build_target" = "openbsd") +AM_CONDITIONAL(LINUX, test "$build_target" = "linux") +AC_MSG_RESULT([$build_target]) +AC_SUBST(build_target) +AM_CONDITIONAL([am__fastdepOBJC], false) +AC_UNALIGNED_64_ACCESS + +# some other checks for standard libs +AC_SEARCH_LIBS([gethostbyname], [nsl ws2_32]) +AC_CHECK_LIB(socket, socket) +AC_CHECK_LIB(m, log) +AC_CHECK_LIB(c, getloadavg, AC_DEFINE(HAVE_GETLOADAVG,1,[getloadavg supported])) + +# 'save' libs; only those libs found so far will be +# linked against _everywhere_. For the others, we +# will be more selective! +SAVE_LIBS=$LIBS + +# libgcrypt +gcrypt=0 +AM_PATH_LIBGCRYPT(1.2.0, gcrypt=1) + +if test $gcrypt = 0 +then + AC_MSG_ERROR([GNUnet needs libgcrypt]) +fi + + +# libcurl +LIBCURL_CHECK_CONFIG(,7.15.4,,AC_MSG_ERROR([GNUnet requires libcurl >= 7.15.4])) +# restore LIBS +LIBS=$SAVE_LIBS + +# restore LIBS +LIBS=$SAVE_LIBS + +# test for kvm and kstat (for CPU stats under BSD/Solaris) +AC_CHECK_LIB([kvm],[kvm_open]) +AC_CHECK_LIB([kstat],[kstat_open]) + +# test for libextractor +extractor=0 +AC_MSG_CHECKING(for libextractor) +AC_ARG_WITH(extractor, + [ --with-extractor=PFX base of libextractor installation], + [AC_MSG_RESULT([$with_extractor]) + case $with_extractor in + no) + ;; + yes) + AC_CHECK_HEADERS(extractor.h, + AC_CHECK_LIB([extractor], [EXTRACTOR_loadDefaultLibraries], + extractor=1)) + ;; + *) + LDFLAGS="-L$with_extractor/lib $LDFLAGS" + CPPFLAGS="-I$with_extractor/include $CPPFLAGS" + AC_CHECK_HEADERS(extractor.h, + AC_CHECK_LIB([extractor], [EXTRACTOR_loadDefaultLibraries], + EXT_LIB_PATH="-L$with_extractor/lib $EXT_LIB_PATH" + extractor=1)) + ;; + esac + ], + [AC_MSG_RESULT([--with-extractor not specified]) + AC_CHECK_HEADERS(extractor.h, + AC_CHECK_LIB([extractor], [EXTRACTOR_loadDefaultLibraries], + extractor=1))]) +if test "$extractor" != 1 +then + AC_MSG_ERROR([GNUnet requires libextractor]) +fi +# restore LIBS +LIBS=$SAVE_LIBS + + +# Checks for standard header files. +AC_HEADER_DIRENT +AC_HEADER_STDC + +# Check for headers that are ALWAYS required +AC_CHECK_HEADERS([fcntl.h math.h errno.h ctype.h limits.h stdio.h stdlib.h string.h unistd.h stdarg.h signal.h locale.h sys/stat.h sys/types.h],,AC_MSG_ERROR([Compiling GNUnet requires standard UNIX headers files])) + +# Checks for headers that are only required on some systems or opional (and where we do NOT abort if they are not there) +AC_CHECK_HEADERS([langinfo.h sys/param.h sys/mount.h sys/statvfs.h sys/select.h sockLib.h sys/mman.h sys/msg.h sys/vfs.h arpa/inet.h fcntl.h libintl.h netdb.h netinet/in.h sys/ioctl.h sys/socket.h sys/time.h unistd.h kstat.h sys/sysinfo.h kvm.h sys/file.h sys/resource.h iconv.h ifaddrs.h mach/mach.h stddef.h sys/timeb.h terminos.h]) + +# Check for GMP header (and abort if not present) +AC_CHECK_HEADERS([gmp.h],,AC_MSG_ERROR([Compiling GNUnet requires gmp.h (from the GNU MP library, libgmp)])) + +# test for libgmp +gmp=0 +AC_MSG_CHECKING(for libgmp) +AC_ARG_WITH(gmp, + [ --with-gmp=PFX base of libgmp installation], + [AC_MSG_RESULT([$with_gmp]) + case $with_gmp in + no) + ;; + yes) + AC_CHECK_HEADERS(gmp.h, + AC_CHECK_LIB([gmp], [__gmpz_add], + gmp=1)) + ;; + *) + LDFLAGS="-L$with_gmp/lib $LDFLAGS" + CPPFLAGS="-I$with_gmp/include $CPPFLAGS" + AC_CHECK_HEADERS(gmp.h, + AC_CHECK_LIB([gmp], [__gmpz_add], + EXT_LIB_PATH="-L$with_gmp/lib $EXT_LIB_PATH" + gmp=1)) + ;; + esac + ], + [AC_MSG_RESULT([--with-gmp not specified]) + AC_CHECK_HEADERS(gmp.h, + AC_CHECK_LIB([gmp], [__gmpz_add], + gmp=1))]) +if test "$gmp" != 1 +then + AC_MSG_ERROR([GNUnet requires libgmp]) +fi + +SAVE_LDFLAGS=$LDFLAGS +SAVE_CPPFLAGS=$CPPFLAGS + +# test for sqlite +sqlite=false +AC_MSG_CHECKING(for SQLite) +AC_ARG_WITH(sqlite, + [ --with-sqlite=PFX base of SQLite installation], + [AC_MSG_RESULT("$with_sqlite") + case $with_sqlite in + no) + ;; + yes) + AC_CHECK_HEADERS(sqlite3.h, + sqlite=true) + ;; + *) + LDFLAGS="-L$with_sqlite/lib $LDFLAGS" + CPPFLAGS="-I$with_sqlite/include $CPPFLAGS" + AC_CHECK_HEADERS(sqlite3.h, + EXT_LIB_PATH="-L$with_sqlite/lib $EXT_LIB_PATH" + SQLITE_LDFLAGS="-L$with_sqlite/lib" + SQLITE_CPPFLAGS="-I$with_sqlite/include" + sqlite=true) + LDFLAGS=$SAVE_LDFLAGS + CPPFLAGS=$SAVE_CPPFLAGS + ;; + esac + ], + [AC_MSG_RESULT([--with-sqlite not specified]) + AC_CHECK_HEADERS(sqlite3.h, sqlite=true)]) +AM_CONDITIONAL(HAVE_SQLITE, test x$sqlite = xtrue) +AC_SUBST(SQLITE_CPPFLAGS) +AC_SUBST(SQLITE_LDFLAGS) + +# test for postgres +postgres=false +AC_MSG_CHECKING(for postgres) +AC_ARG_WITH(postgres, + [ --with-postgres=PFX base of postgres installation], + [AC_MSG_RESULT("$with_postgres") + case $with_postgres in + no) + ;; + yes) + AC_CHECK_HEADERS(postgresql/libpq-fe.h, + postgres=true) + ;; + *) + LDFLAGS="-L$with_postgres/lib $LDFLAGS" + CPPFLAGS="-I$with_postgres/include $CPPFLAGS" + AC_CHECK_HEADERS(postgresql/libpq-fe.h, + EXT_LIB_PATH="-L$with_postgres/lib $EXT_LIB_PATH" + SQLITE_LDFLAGS="-L$with_postgres/lib" + SQLITE_CPPFLAGS="-I$with_postgres/include" + postgres=true) + LDFLAGS=$SAVE_LDFLAGS + CPPFLAGS=$SAVE_CPPFLAGS + ;; + esac + ], + [AC_MSG_RESULT([--with-postgres not specified]) + AC_CHECK_HEADERS(postgresql/libpq-fe.h, postgres=true)]) +AM_CONDITIONAL(HAVE_POSTGRES, test x$postgres = xtrue) +AC_SUBST(POSTGRES_CPPFLAGS) +AC_SUBST(POSTGRES_LDFLAGS) + + +# test for libz (maybe required for linking mysql) +zlib=1 +AC_CHECK_LIB(z, compress,,zlib=0) +AM_CONDITIONAL(HAVE_ZLIB, test x$zlib = x1) +if test "$zlib" != 1 +then + AC_MSG_ERROR([GNUnet requires zlib]) +fi + +# mysql & windows +AC_CHECK_TYPES([sigset_t, off_t], [], [], [#include ]) +AC_CHECK_TYPES([size_t], [], [], [#include ]) + +if test "$build_target" = "mingw" +then + CYGWIN_MYSQL_MAGIC="#include " +fi + +# test for mysql +mysql=false +mysqlfail=false +SAVE_LDFLAGS=$LDFLAGS +SAVE_CPPFLAGS=$CPPFLAGS +AC_MSG_CHECKING(for mysql) +AC_ARG_WITH(mysql, + [ --with-mysql=PFX base of MySQL installation], + [AC_MSG_RESULT("$with_mysql") + if test "$with_mysql" != "no" + then + if test "$with_mysql" != "yes" + then + LDFLAGS="-L$with_mysql/lib -L$with_mysql/lib/mysql $LDFLAGS $ZLIBS" + CPPFLAGS="-I$with_mysql/include $CPPFLAGS" + fi + AC_CHECK_HEADERS(mysql/mysql.h, + AC_CHECK_LIB(mysqlclient, mysql_init, + MYSQL_LDFLAGS="-L$with_mysql/lib -L$with_mysql/lib/mysql" + MYSQL_CPPFLAGS="-I$with_mysql/include" + + mysql=true), [], [$CYGWIN_MYSQL_MAGIC]) + fi + ], + [AC_MSG_RESULT([--with-mysql not specified]) + LDFLAGS="-L/usr/lib/mysql $LDFLAGS $ZLIBS" + AC_CHECK_LIB(mysqlclient, mysql_init, + [AC_CHECK_HEADERS(mysql/mysql.h, + MYSQL_LDFLAGS="-L/usr/lib/mysql" + mysql=true + + , [], [$CYGWIN_MYSQL_MAGIC])]) + ]) + +AC_SUBST(MYSQL_LDFLAGS) +AC_SUBST(MYSQL_CPPFLAGS) + +# additional version check for mysql +AC_ARG_ENABLE(mysql-version-check, [ --disable-mysql-version-check do not check MySQL version],, enable_mysql_version_check=yes) +if test "$mysql" = "true" -a "x$enable_mysql_version_check" = "xyes" +then + AC_MSG_CHECKING(mysql version) + AC_RUN_IFELSE([AC_LANG_PROGRAM( + [[$CYGWIN_MYSQL_MAGIC + #include ]], + [[if (MYSQL_VERSION_ID < 40100) + return(-1); + else + return(0); + ]]) + ],mysql=true,mysql=false) + if test "$mysql" = "false" + then + mysqlfail=true + AC_MSG_RESULT([fail, >= 4.1 required]) + else + AC_MSG_RESULT(ok) + fi +fi +AM_CONDITIONAL(HAVE_MYSQL, test x$mysql = xtrue) +AM_CONDITIONAL(HAVE_MYSQLE, test "0" = "1") +# restore LIBS +LIBS=$SAVE_LIBS +LDFLAGS=$SAVE_LDFLAGS +CPPFLAGS=$SAVE_CPPFLAGS + +if test "$sqlite" = 0 -a "$mysql" = 0 +then + AC_MSG_ERROR([GNUnet requires SQLite or MySQL]) +fi + +# GNUnet Setup +dialog=0 +LIBS="-lm $LIBS" +AC_CHECK_LIB([ncursesw],[wmove]) +AC_MSG_CHECKING([for dialog 1.0-20051207]) +AC_ARG_WITH(dialog, + [ --with-dialog=PFX base of dialog installation], + [AC_MSG_RESULT([$with_dialog]) + case $with_dialog in + no) + ;; + yes) + AC_CHECK_HEADERS(dialog.h, + AC_CHECK_LIB([dialog], [dlg_menu], + dialog=1)) + ;; + *) + LDFLAGS="-L$with_dialog/lib $LDFLAGS" + CPPFLAGS="-I$with_dialog/include $CPPFLAGS" + AC_CHECK_HEADERS(dialog.h, + AC_CHECK_LIB([dialog], [dlg_menu], + EXT_LIB_PATH="-L$with_dialog/lib $EXT_LIB_PATH" + dialog=1)) + ;; + esac + ], + [AC_MSG_RESULT([--with-dialog not specified]) + AC_CHECK_HEADERS(dialog.h, + AC_CHECK_LIB([dialog], [dlg_menu], + dialog=1))]) + +AM_CONDITIONAL(HAVE_DIALOG, test x$dialog = x1) +AC_DEFINE_UNQUOTED([HAVE_DIALOG], $dialog, [We have dialog]) + +cdialog=0 +AC_ARG_WITH(cdialog, + [ --with-cdialog=PFX base of cdialog installation], + [AC_MSG_RESULT([$with_cdialog]) + case $with_cdialog in + no) + ;; + yes) + AC_CHECK_HEADERS(cdialog/dialog.h, + AC_CHECK_LIB([cdialog], [dlg_menu], + cdialog=1)) + ;; + *) + LDFLAGS="-L$with_cdialog/lib $LDFLAGS" + CPPFLAGS="-I$with_cdialog/include $CPPFLAGS" + AC_CHECK_HEADERS(cdialog/dialog.h, + AC_CHECK_LIB([cdialog], [dlg_menu], + EXT_LIB_PATH="-L$with_cdialog/lib $EXT_LIB_PATH" + cdialog=1)) + ;; + esac + ], + [AC_MSG_RESULT([--with-cdialog not specified]) + AC_CHECK_HEADERS(cdialog/dialog.h, + AC_CHECK_LIB([cdialog], [dlg_menu], + cdialog=1))]) + +AM_CONDITIONAL(HAVE_CDIALOG, test x$cdialog = x1) +AC_DEFINE_UNQUOTED([HAVE_CDIALOG], $cdialog, [We have cdialog]) +# restore LIBS +LIBS=$SAVE_LIBS + +# libmicrohttpd +lmhd=0 +AC_MSG_CHECKING([for libmicrohttpd]) +AC_ARG_WITH(microhttpd, + [ --with-microhttpd=PFX base of libmicrohttpd installation], + [AC_MSG_RESULT([$with_microhttpd]) + case $with_microhttpd in + no) + ;; + yes) + AC_CHECK_HEADERS([microhttpd.h], + AC_CHECK_DECL(MHD_OPTION_PER_IP_CONNECTION_LIMIT, + AC_CHECK_LIB([microhttpd], [MHD_start_daemon], + lmhd=1), + [],[#include "src/include/platform.h" + #include ]),, + [#include "src/include/platform.h"]) + ;; + *) + LDFLAGS="-L$with_microhttpd/lib $LDFLAGS" + CPPFLAGS="-I$with_microhttpd/include $CPPFLAGS" + AC_CHECK_HEADERS(microhttpd.h, + AC_CHECK_DECL(MHD_OPTION_PER_IP_CONNECTION_LIMIT, + AC_CHECK_LIB([microhttpd], [MHD_start_daemon], + EXT_LIB_PATH="-L$with_microhttpd/lib $EXT_LIB_PATH" + lmhd=1), + [],[#include "src/include/platform.h" + #include ]),, + [#include "src/include/platform.h"]) + ;; + esac + ], + [AC_MSG_RESULT([--with-microhttpd not specified]) + AC_CHECK_HEADERS([microhttpd.h], + AC_CHECK_DECL(MHD_OPTION_PER_IP_CONNECTION_LIMIT, + AC_CHECK_LIB([microhttpd], [MHD_start_daemon], + lmhd=1), + [],[#include "src/include/platform.h" + #include ]),, + [#include "src/include/platform.h"])]) +AM_CONDITIONAL(HAVE_MHD, test x$lmhd = x1) +AC_DEFINE_UNQUOTED([HAVE_MHD], $lmhd, [We have libmicrohttpd]) +# restore LIBS +LIBS=$SAVE_LIBS + +# libesmtp +esmtp=0 +AC_MSG_CHECKING([for libesmtp]) +AC_ARG_WITH(esmtp, + [ --with-esmtp=PFX base of libesmtp installation], + [AC_MSG_RESULT([$with_esmtp]) + case $with_esmtp in + no) + ;; + yes) + AC_CHECK_HEADERS(libesmtp.h, + AC_CHECK_LIB([esmtp], [smtp_start_session], + esmtp=1)) + ;; + *) + LDFLAGS="-L$with_esmtp/lib $LDFLAGS" + CPPFLAGS="-I$with_esmtp/include $CPPFLAGS" + AC_CHECK_HEADERS(libesmtp.h, + AC_CHECK_LIB([esmtp], [smtp_start_session], + EXT_LIB_PATH="-L$with_esmtp/lib $EXT_LIB_PATH" + esmtp=1)) + ;; + esac + ], + [AC_MSG_RESULT([--with-esmtp not specified]) + AC_CHECK_HEADERS(libesmtp.h, + AC_CHECK_LIB([esmtp], [smtp_start_session], + esmtp=1))]) +AM_CONDITIONAL(HAVE_ESMTP, test x$esmtp = x1) +AC_DEFINE_UNQUOTED([HAVE_ESMTP], $esmtp, [We have libesmtp]) +# restore LIBS +LIBS=$SAVE_LIBS + +# check for gettext +AM_GNU_GETTEXT_VERSION([0.16.1]) +AM_GNU_GETTEXT([external]) + +# check for iconv +AM_ICONV + +# Checks for standard typedefs, structures, and compiler characteristics. +AC_C_CONST +AC_TYPE_PID_T +AC_TYPE_SIZE_T +AC_TYPE_MODE_T +AC_HEADER_TIME +AC_HEADER_STAT +AC_HEADER_STDBOOL +AC_STRUCT_TM + + + + +# Checks for library functions. +AC_FUNC_CLOSEDIR_VOID +AC_FUNC_FORK +AC_PROG_GCC_TRADITIONAL +AC_FUNC_MEMCMP +AC_FUNC_SELECT_ARGTYPES +AC_FUNC_CHOWN + +AC_TYPE_SIGNAL +AC_FUNC_STAT +AC_FUNC_STRFTIME +AC_FUNC_VPRINTF +AC_HEADER_SYS_WAIT +AC_TYPE_OFF_T +AC_TYPE_UID_T +AC_CHECK_FUNCS([floor gethostname memmove rmdir strncasecmp strrchr strtol atoll dup2 fdatasync ftruncate gettimeofday memset mkdir mkfifo select socket strcasecmp strchr strdup strerror strstr clock_gettime getrusage rand uname setlocale getcwd mktime gmtime_r gmtime strlcpy strlcat ftruncate stat64 sbrk mmap mremap setrlimit gethostbyaddr initgroups getifaddrs freeifaddrs getnameinfo getaddrinfo inet_ntoa localtime_r nl_langinfo putenv realpath strndup gethostbyname2 gethostbyname]) + +# restore LIBS +LIBS=$SAVE_LIBS + + + +# check for guile +guile=0 +AC_MSG_CHECKING(for guile 1.8) +AC_ARG_WITH(guile, + [ --with-guile=PFX base of guile installation], + [AC_MSG_RESULT([$with_guile]) + case $with_guile in + no) + ;; + yes) + AC_CHECK_HEADERS(libguile.h, + AC_CHECK_LIB([guile], [scm_c_define_gsubr], + guile=1, [], + -lgmp $LIBLTDL)) + ;; + *) + LDFLAGS="-L$with_guile/lib $LDFLAGS" + CPPFLAGS="-I$with_guile/include $CPPFLAGS" + AC_CHECK_HEADERS(libguile.h, + AC_CHECK_LIB([guile], [scm_c_define_gsubr], + EXT_LIB_PATH="-L$with_guile/lib $EXT_LIB_PATH" + guile=1, [], + -lgmp $LIBLTDL)) + ;; + esac + ], + [AC_MSG_RESULT([--with-guile not specified]) + AC_CHECK_HEADERS(libguile.h, + AC_CHECK_LIB([guile], [scm_c_define_gsubr], + guile=1, [], -lgmp $LIBLTDL))]) + +if test "$guile" = 1 +then + AC_CHECK_LIB([guile], [scm_init_guile], [], + AC_MSG_ERROR([Guile doesn't provide scm_init_guile(). Please report to bug-gnunet@gnu.org]), -lgmp $LIBLTDL) + AC_DEFINE_UNQUOTED([HAVE_GUILE], 1, [We have GUILE]) +else + AC_DEFINE_UNQUOTED([HAVE_GUILE], 0, [We do NOT have GUILE]) +fi +AM_CONDITIONAL(HAVE_GUILE, test x$guile = x1) +# restore LIBS +LIBS=$SAVE_LIBS + +gn_user_home_dir="~/.gnunet" +AC_ARG_WITH(user-home-dir, + AC_HELP_STRING( + [--with-user-home-dir=DIR], + [default user home directory (~/.gnunet)]), + [gn_user_home_dir=$withval]) +AC_SUBST(GN_USER_HOME_DIR, $gn_user_home_dir) +gn_daemon_home_dir="/var/lib/gnunet" +AC_ARG_WITH(daemon-home-dir, + AC_HELP_STRING( + [--with-daemon-home-dir=DIR], + [default daemon home directory (/var/lib/gnunet)]), + [gn_daemon_home_dir=$withval]) +AC_SUBST(GN_DAEMON_HOME_DIR, $gn_daemon_home_dir) +gn_daemon_config_dir="/etc" +AC_ARG_WITH(daemon-config-dir, + AC_HELP_STRING( + [--with-daemon-config-dir=DIR], + [default daemon config directory (/etc)]), + [gn_daemon_config_dir=$withval]) +AC_SUBST(GN_DAEMON_CONFIG_DIR, $gn_daemon_config_dir) +gn_daemon_pidfile="/var/run/gnunetd/pid" +AC_ARG_WITH(daemon-pidfile, + AC_HELP_STRING( + [--with-daemon-pidfile=FILE], + [default daemon pidfile (/var/run/gnunetd/pid)]), + [gn_daemon_pidfile=$withval]) +AC_SUBST(GN_DAEMON_PIDFILE, $gn_daemon_pidfile) + +GN_INTLINCL="" +GN_LIBINTL="$LTLIBINTL" +AC_ARG_ENABLE(framework, [ --enable-framework enable Mac OS X framework build helpers],enable_framework_build=$enableval) +AM_CONDITIONAL(WANT_FRAMEWORK, test x$enable_framework_build = xyes) +if test x$enable_framework_build = xyes +then + AC_DEFINE([FRAMEWORK_BUILD], 1, [Build a Mac OS X Framework]) + GN_INTLINCL='-I$(top_srcdir)/src/intlemu' + GN_LIBINTL='$(top_builddir)/src/intlemu/libintlemu.la -framework CoreFoundation' + AC_LIB_APPENDTOVAR([CPPFLAGS], [$GN_INTLINCL]) +fi + +GN_LIB_LDFLAGS="-export-dynamic -no-undefined" +GN_PLUGIN_LDFLAGS="-export-dynamic -avoid-version -module -no-undefined" +dnl TODO insert a proper check here +AC_CACHE_CHECK([whether -export-symbols-regex works], + gn_cv_export_symbols_regex_works, + [ + case "$host_os" in + mingw*) gn_cv_export_symbols_regex_works=no;; + *) gn_cv_export_symbols_regex_works=yes;; + esac + ]) +if test "x$gn_cv_export_symbols_regex_works" = "xyes" +then + GN_LIB_LDFLAGS="$GN_LIB_LDFLAGS -export-symbols-regex \"GNUNET_@<:@a-zA-Z0-9_@:>@*\"" + GN_PLUGIN_LDFLAGS="$GN_PLUGIN_LDFLAGS -export-symbols-regex \"@<:@a-zA-Z0-9_@:>@*\"" +fi +AC_SUBST(GN_LIB_LDFLAGS) +AC_SUBST(GN_PLUGIN_LDFLAGS) +AC_SUBST(GN_INTLINCL) +AC_SUBST(GN_LIBINTL) + +AC_SUBST(CPPFLAGS) +AC_SUBST(LIBS) +AC_SUBST(LDFLAGS) +AC_SUBST(EXT_LIB_PATH) +AC_SUBST(EXT_LIBS) + +AC_SUBST(LIBPREFIX) +AC_SUBST(DLLDIR) +AC_SUBST(EXT_LIB_PATH) + + +# gcov compilation +use_gcov=no +AC_ARG_ENABLE([coverage], AS_HELP_STRING([--enable-coverage], + [Compile the library with code coverage support (default is NO)]), + [use_gcov=yes], [use_gcov=no]) +AM_CONDITIONAL([USE_COVERAGE], [test "x$use_gcov" = "xyes"]) + + + +AC_CONFIG_FILES([ po/Makefile.in m4/Makefile +Makefile +contrib/Makefile +src/Makefile +src/arm/Makefile +src/core/Makefile +src/include/Makefile +src/include/gnunet_directories.h +src/util/Makefile +src/fragmentation/Makefile +src/hello/Makefile +src/peerinfo/Makefile +src/resolver/Makefile +src/statistics/Makefile +src/template/Makefile +src/transport/Makefile +]) +AC_OUTPUT + +# Finally: summary! + +# warn user if mysql found but not used due to version +if test "$mysqlfail" = "true" +then + AC_MSG_NOTICE([NOTICE: MySQL found, but too old. MySQL support will not be compiled.]) +fi + +# sqlite +if test "x$sqlite" = "x0" +then + AC_MSG_NOTICE([NOTICE: sqlite not found. sqLite support will not be compiled.]) +fi + +if test "x$dialog" != "x1" -a "x$cdialog" != "x1" +then + AC_MSG_NOTICE([NOTICE: curses based gnunet-setup frontends will not be installed.]) +fi + +if test "x$lmhd" != "x1" +then + AC_MSG_NOTICE([NOTICE: libmicrohttpd not found, http transport will not be installed.]) +fi + +AC_MSG_NOTICE([NOTICE: Database support is set to MySQL: $mysql, SQLite: $sqlite]) + +# guile +if test "x$guile" = "x0" +then + AC_MSG_NOTICE([WARNING: Guile not found, gnunet-setup will not be installed.]) +fi + +if test "$enable_framework_build" = "yes" +then + AC_MSG_NOTICE([NOTICE: Mac OS X framework build enabled.]) +fi + +AC_MSG_NOTICE([******************************************** +You can build GNUnet with + make install +now. After that, install gnunet-gtk or gnunet-qt and run + gnunet-setup -d wizard-gtk +(on graphical systems with GTK) or + gnunet-setup -d wizard-qt +(on graphical systems with QT) or + gnunet-setup -d wizard-curses +(on text-based systems with curses) in order to make important configuration +settings. If neither works for you, you can find a template configuration +file in the contrib/ subdirectory. Copy it to /etc/gnunetd.conf and modify it +using your favourite text editor. +********************************************]) diff --git a/contrib/Makefile.am b/contrib/Makefile.am new file mode 100644 index 000000000..50758e9ad --- /dev/null +++ b/contrib/Makefile.am @@ -0,0 +1,19 @@ +dist_pkgdata_DATA = \ + gnunet-logo-color.png \ + defaults.conf \ + config.scm + +EXTRA_DIST = \ + coverage.sh \ + hostlist.cgi \ + hostlist.php \ + report.sh + +# init_gnunet_redhat \ +# init_gnunet_ubuntu \ +# visualize_stats.sh \ +# gnmessage.sh \ +# junkinsert.sh \ +# junklookup.sh \ +# namespacehelper.sh + diff --git a/contrib/config.scm b/contrib/config.scm new file mode 100644 index 000000000..d7fd931fa --- /dev/null +++ b/contrib/config.scm @@ -0,0 +1,1568 @@ +;; This is not a stand-alone guile application. +;; It can only be executed from within gnunet-setup. +;; +;; GNUnet setup defines a function "build-tree-node" +;; (with arguments section, option, description, help, +;; children, visible, value and range) which is +;; used by the script to create the configuration tree. +;; +;; GNUnet setup defines a function "change-visible" +;; (with arguments context, section, option, yesno) which +;; can be used by the script to dynamically change the +;; visibility of options. +;; +;; GNUnet setup defines a function "get-option" +;; (with arguments context, section, option) which +;; can be used to query the current value of an option. +;; +;; GNUnet setup defines a function "set-option" +;; (with arguments context, section, option, value) which +;; can be used to set the value of an option. +;; +;; +;; GNUnet setup requires two functions from this script. +;; First, a function "gnunet-config-setup" which constructs the +;; configuration tree. +;; +;; Second, a function "gnunet-config-change" which is notified whenever +;; configuration options are changed; the script can then +;; change the visibility of other options. + + +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + +;; for GNU gettext +(define (_ msg) msg) + +;; common string +(define (nohelp) + (_ "No help available.") ) + +(define (nathelp) + (_ "You can use 'make check' in src/transports/upnp/ to find out if your NAT supports UPnP. You should disable this option if you are sure that you are not behind a NAT. If your NAT box does not support UPnP, having this on will not do much harm (only cost a small amount of resources).") ) + +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + +;; menu definitions + +;; meta-menu + +(define (meta-exp builder) + (builder + "Meta" + "EXPERIMENTAL" + (_ "Prompt for development and/or incomplete code") + (_ +"If EXPERIMENTAL is set to NO, options for experimental code are not shown. If in doubt, use NO. + +Some options apply to experimental code that maybe in a state of development where the functionality, stability, or the level of testing is not yet high enough for general use. These features are said to be of \"alpha\" quality. If a feature is currently in alpha, uninformed use is discouraged (since the developers then do not fancy \"Why doesn't this work?\" type messages). + +However, active testing and qualified feedback of these features is always welcome. Users should just be aware that alpha features may not meet the normal level of reliability or it may fail to work in some special cases. Bug reports are usually welcomed by the developers, but please read the documents and and use for how to report problems." ) + '() + #t + #f + #f + 'advanced) ) + +(define (meta-adv builder) + (builder + "Meta" + "ADVANCED" + (_ "Show options for advanced users") + (_ +"These are options that maybe difficult to understand for the beginner. These options typically refer to features that allow tweaking of the installation. If in a hurry, say NO." ) + '() + #t + #t + #f + 'always) ) + +(define (meta-rare builder) + (builder + "Meta" + "RARE" + (_ "Show rarely used options") + (_ +"These are options that hardly anyone actually needs. If you plan on doing development on GNUnet, you may want to look into these. If in doubt or in a hurry, say NO." ) + '() + #t + #t + #f + 'advanced) ) + +(define (meta builder) + (builder + "Meta" + "" + (_ "Meta-configuration") + (_ "Which level of configuration should be available") + (list + (meta-adv builder) + (meta-rare builder) + (meta-exp builder) + ) + #t + #f + #f + 'always) ) + +;; fundamentals + +(define (paths-home builder) + (builder + "PATHS" + "GNUNETD_HOME" + (_ "Full pathname of GNUnet HOME directory") + (_ +"This gives the root-directory of the GNUnet installation. Make sure there is some space left in that directory. :-) Users inserting or indexing files will be able to store data in this directory up to the (global) quota specified below. Having a few gigabytes of free space is recommended." ) + '() + #t + "/var/lib/gnunet" + '() + 'always) ) + +;; General menu + +(define (fs-path builder) + (builder + "FS" + "DIR" + (_ "Full pathname of GNUnet directory for file-sharing data") + (nohelp) + '() + #t + "$GNUNETD_HOME/data/fs" + '() + 'always) ) + +(define (kv-path builder) + (builder + "KEYVALUE_DATABASE" + "DIR" + (_ "Full pathname to the directory with the key-value database") + (_ "Note that the kvstore is currently not used.") + '() + #f + "$GNUNETD_HOME/kvstore/" + '() + 'never) ) + +(define (index-path builder) + (builder + "FS" + "INDEX-DIRECTORY" + (_ "Full pathname of GNUnet directory for indexed files symbolic links") + (nohelp) + '() + #t + "$GNUNETD_HOME/data/shared" + '() + 'always) ) + +(define (general-helloexpires builder) + (builder + "GNUNETD" + "HELLOEXPIRES" + (_ "How many minutes should peer advertisements last?") + (_ +"How many minutes is the current IP valid? (GNUnet will sign HELLO messages with this expiration timeline. If you are on dialup, 60 (for 1 hour) is suggested. If you have a static IP address, you may want to set this to a large value (say 14400). The default is 1440 (1 day). If your IP changes periodically, you will want to choose an expiry period smaller than the frequency with which your IP changes." ) + '() + #t + 1440 + (cons 1 14400) + 'advanced) ) + +(define (general-hostlisturl builder) + (builder + "GNUNETD" + "HOSTLISTURL" + (_ "Where can GNUnet find an initial list of peers?") + (_ +"GNUnet can automatically update the hostlist from the web. While GNUnet internally communicates which hosts are online, it is typically a good idea to get a fresh hostlist whenever gnunetd starts from the WEB. By setting this option, you can specify from which server gnunetd should try to download the hostlist. The default should be fine for now. + +The general format is a list of space-separated URLs. Each URL must have the format http://HOSTNAME/FILENAME + +If you want to setup an alternate hostlist server, you must run a permanent node and \"cat data/hosts/* > hostlist\" every few minutes to keep the list up-to-date. + +If you do not specify a HOSTLISTURL, you must copy valid hostkeys to data/hosts manually.") + '() + #t + "http://gnunet.org/hostlist.php http://gnunet.mine.nu:8081/hostlist http://vserver1236.vserver-on.de/hostlist-074" + '() + 'always) ) + +(define (general-http-proxy builder) + (builder + "GNUNETD" + "HTTP-PROXY" + (_ "HTTP Proxy Server") + (_ +"If you have to use a proxy for outbound HTTP connections, specify the proxy configuration here. Default is no proxy." ) + '() + #t + "" + '() + 'advanced) ) + + +(define (general-hosts builder) + (builder + "GNUNETD" + "HOSTS" + (_ "Name of the directory where gnunetd should store contact information about peers") + (_ +"Unless you want to share the directory directly using a webserver, the default is most likely just fine." ) + '() + #t + "$GNUNETD_HOME/data/hosts/" + '() + 'rare) ) + + +;; logging options + +(define (log-level description option builder) + (builder + "LOGGING" + option + description + (nohelp) + '() + #t + "WARNING" + (list "SC" "NOTHING" "FATAL" "ERROR" "WARNING" "INFO" "STATUS" "DEBUG") + 'always)) + +(define (log-keeplog builder) + (builder + "GNUNETD" + "KEEPLOG" + (_ "How long should logs be kept?") + (_ +"How long should logs be kept? If you specify a value greater than zero, a log is created each day with the date appended to its filename. These logs are deleted after $KEEPLOG days. To keep logs forever, set this value to 0." ) + '() + #t + 3 + (cons 0 36500) + 'advanced) ) + +(define (daemon-fdlimit builder) + (builder + "GNUNETD" + "FDLIMIT" + (_ "What maximum number of open file descriptors should be requested from the OS?") + (_ +"The default of 1024 should be fine for most systems. If your system can support more, increasing the number might help support additional clients on machines with plenty of bandwidth. For embedded systems, a smaller number might be acceptable. A value of 0 will leave the descriptor limit untouched. This option is mostly for OS X systems where the default is too low. Note that if gnunetd cannot obtain the desired number of file descriptors from the operating system, it will print a warning and try to run with what it is given." ) + '() + #t + 1024 + (cons 64 65536) + 'rare) ) + +(define (log-logfile builder) + (builder + "GNUNETD" + "LOGFILE" + (_ "Where should gnunetd write the logs?") + (nohelp) + '() + #f + "$GNUNETD_HOME/daemon-logs" + '() + 'rare) ) + +(define (log-devel builder) + (builder + "LOGGING" + "DEVELOPER" + (_ "Enable for extra-verbose logging.") + (nohelp) + '() + #f + #f + #f + 'rare) ) + +(define (logging builder) + (builder + "LOGGING" + "" + (_ "Logging") + (_ "Specify which system messages should be logged how") + (list + (log-keeplog builder) + (log-logfile builder) + (log-devel builder) + (log-level (_ "Logging of events for users") "USER-LEVEL" builder) + (log-level (_ "Logging of events for the system administrator") "ADMIN-LEVEL" builder) + ) + #t + #f + #f + 'advanced) ) + + +(define (general-pidfile builder) + (builder + "GNUNETD" + "PIDFILE" + (_ "Where should gnunetd write the PID?") + (_ "The default is no longer /var/run/gnunetd.pid since we could not delete the file on shutdown at that location." ) + '() + #f + "/var/run/gnunetd/pid" + '() + 'rare) ) + + +(define (general-username builder) + (builder + "GNUNETD" + "USER" + (_ "As which user should gnunetd run?") + (_ +"Empty means \"current user\". On computer startup, it is root/SYSTEM. Under Windows, this setting affects the creation of a new system service only.") + '() + #f + "" + '() + 'advanced) ) + + + +(define (general-autostart builder) + (builder + "GNUNETD" + "AUTOSTART" + (_ "Should gnunetd be automatically started when the system boots?") + (_ "Set to YES if gnunetd should be automatically started on boot. If this option is set, gnunet-setup will install a script to start the daemon upon completion. This option may not work on all systems.") + '() + #t + #f + #f + 'rare) ) + + +(define (general-transports builder) + (builder + "GNUNETD" + "TRANSPORTS" + (_ "Which transport mechanisms should GNUnet use?") + (_ +"Use a space-separated list of modules, e.g. \"udp smtp tcp\". The available transports are udp, tcp, http, smtp and nat. + +Loading the 'nat' and 'tcp' modules is required for peers behind NAT boxes that cannot directly be reached from the outside. Peers that are NOT behind a NAT box and that want to *allow* peers that ARE behind a NAT box to connect must ALSO load the 'nat' module. Note that the actual transfer will always be via tcp initiated by the peer behind the NAT box. The nat transport requires the use of tcp, http and/or smtp in addition to nat itself.") + '() + #t + "udp tcp http nat" + (list "MC" "udp" "tcp" "nat" "http" "smtp") + 'always) ) + + +(define (general-applications builder) + (builder + "GNUNETD" + "APPLICATIONS" + (_ "Which applications should gnunetd support?") + (_ +"Whenever this option is changed, you MUST run gnunet-update. Currently, the available applications are: + +advertising: advertises your peer to other peers. Without it, your peer will not participate in informing peers about other peers. You should always load this module. + +getoption: allows clients to query gnunetd about the values of various configuration options. Many tools need this. You should always load this module. + +stats: allows tools like gnunet-stats and gnunet-gtk to query gnunetd about various statistics. This information is usually quite useful to diagnose errors, hence it is recommended that you load this module. + +traffic: keeps track of how many messages were recently received and transmitted. This information can then be used to establish how much cover traffic is currently available. The amount of cover traffic becomes important if you want to make anonymous requests with an anonymity level that is greater than one. It is recommended that you load this module. + +fs: needed for anonymous file sharing. You should always load this module. + +hostlist: integrated hostlist HTTP server. Useful if you want to offer a hostlist and running Apache would be overkill. + +chat: broadcast chat (demo-application, ALPHA quality). Required for gnunet-chat. Note that the current implementation of chat is not considered to be secure. + +tbench: benchmark transport performance. Required for gnunet-tbench. Note that tbench allows other users to abuse your resources. + +tracekit: topology visualization toolkit. Required for gnunet-tracekit. Note that loading tracekit will make it slightly easier for an adversary to compromise your anonymity." ) + '() + #t + "advertising getoption fs stats traffic" + (list "MC" "advertising" "getoption" "fs" "hostlist" "stats" "traffic" "dht" "tracekit" "tbench" "vpn" "chat") + 'always) ) + + + +(define (tcpserver-disable builder) + (builder + "TCPSERVER" + "DISABLE" + (_ "Disable client-server connections") + (_ "This option can be used to tell gnunetd not to open the client port. When run like this, gnunetd will participate as a peer in the network but not support any user interfaces. This may be useful for headless systems that are never expected to have end-user interactions. Note that this will also prevent you from running diagnostic tools like gnunet-stats!") + '() + #t + #f + #f + 'rare) ) + + +(define (gnunetd-disable-ipv6 builder) + (builder + "GNUNETD" + "DISABLE-IPV6" + (_ "YES disables IPv6 support, NO enables IPv6 support") + (_ "This option may be useful on peers where the kernel does not support IPv6. You might also want to set this option if you do not have an IPv6 network connection.") + '() + #t + #t + #t + 'advanced) ) + + +(define (gnunetd-private-network builder) + (builder + "GNUNETD" + "PRIVATE-NETWORK" + (_ "Disable peer discovery") + (_ "The option 'PRIVATE-NETWORK' can be used to limit the connections of this peer to peers of which the hostkey has been copied by hand to data/hosts; if this option is given, GNUnet will not accept advertisements of peers that the local node does not already know about. Note that in order for this option to work, HOSTLISTURL should either not be set at all or be set to a trusted peer that only advertises the private network. Also, the option does NOT work at the moment if the NAT transport is loaded; for that, a couple of lines above would need some minor editing :-).") + '() + #t + #f + #f + 'rare) ) + +(define (network-disable-advertising builder) + (builder + "NETWORK" + "DISABLE-ADVERTISEMENTS" + (_ "Disable advertising this peer to other peers") + (nohelp) + '() + #t + #f + #f + 'rare) ) + +(define (network-disable-autoconnect builder) + (builder + "NETWORK" + "DISABLE-AUTOCONNECT" + (_ "Disable automatic establishment of connections") + (_ "If this option is enabled, GNUnet will not automatically establish connections to other peers, but instead wait for applications to specifically request connections to other peers (or for other peers to connect to us).") + '() + #t + #f + #f + 'experimental) ) + +(define (network-disable-helloexchange builder) + (builder + "NETWORK" + "HELLOEXCHANGE" + (_ "Enable advertising of other peers by this peer") + (_ "This option may be useful during testing, but turning it off is dangerous! If in any doubt, set it to YES (which is the default).") + '() + #t + #t + #t + 'experimental) ) + +(define (network-port builder) + (builder + "NETWORK" + "PORT" + (_ "Port for communication with GNUnet user interfaces") + (_ "Which is the client-server port that is used between gnunetd and the clients (TCP only). You may firewall this port for non-local machines (but you do not have to since GNUnet will perform access control and only allow connections from machines that are listed under TRUSTED).") + '() + #t + 2087 + (cons 1 65535) + 'advanced) ) + +(define (hostlist-port builder) + (builder + "HOSTLIST" + "PORT" + (_ "Port for the integrated hostlist HTTP server") + (nohelp) + '() + #t + 8080 + (cons 1 65535) + 'hostlist-loaded) ) + +(define (network-trusted builder) + (builder + "NETWORK" + "TRUSTED" + (_ "IPv4 networks allowed to use gnunetd server") + (_ "This option specifies which hosts are trusted enough to connect as clients (to the TCP port). This is useful if you run gnunetd on one host of your network and want to allow all other hosts to use this node as their server. By default, this is set to 'loopback only'. The format is IP/NETMASK where the IP is specified in dotted-decimal and the netmask either in CIDR notation (/16) or in dotted decimal (255.255.0.0). Several entries must be separated by a semicolon, spaces are not allowed.") + '() + #t + "127.0.0.0/8;" + '() + 'advanced) ) + +(define (network-trusted6 builder) + (builder + "NETWORK" + "TRUSTED6" + (_ "IPv6 networks allowed to use gnunetd server") + (_ "This option specifies which hosts are trusted enough to connect as clients (to the TCP port). This is useful if you run gnunetd on one host of your network and want to allow all other hosts to use this node as their server. By default, this is set to 'loopback only'. The format is IP/NETMASK where the IP is specified in dotted-decimal and the netmask either in CIDR notation (/16) or in dotted decimal (255.255.0.0). Several entries must be separated by a semicolon, spaces are not allowed.") + '() + #t + "::1;" + '() + 'ipv6) ) + + +(define (limit-allow builder) + (builder + "GNUNETD" + "LIMIT-ALLOW" + (_ "Limit connections to the specfied set of peers.") + (_ "If this option is not set, any peer is allowed to connect. If it is set, only the specified peers are allowed. Specify the list of peer IDs (not IPs!)") + '() + #t + "" + '() + 'rare)) + +(define (general-groupname builder) + (builder + "GNUNETD" + "GROUP" + (_ "Run gnunetd as this group.") + (_ "When started as root, gnunetd will change permissions to the given group.") + '() + #t + "gnunetd" + '() + 'advanced)) + +(define (limit-deny builder) + (builder + "GNUNETD" + "LIMIT-DENY" + (_ "Prevent the specfied set of peers from connecting.") + (_ "If this option is not set, any peer is allowed to connect. If the ID of a peer is listed here, connections from that peer will be refused. Specify the list of peer IDs (not IPs!)") + '() + #t + "" + '() + 'rare)) + +(define (advertising builder) + (builder + "ADVERTISING" + "" + (_ "Topology Maintenance") + (_ "Rarely used settings for peer advertisements and connections") + (list + (general-helloexpires builder) + (tcpserver-disable builder) + (gnunetd-private-network builder) + (network-disable-advertising builder) + (network-disable-helloexchange builder) + (network-disable-autoconnect builder) + (limit-allow builder) + (limit-deny builder) + ) + #t + #f + #f + 'rare) ) + +(define (general builder) + (builder + "GNUNETD" + "" + (_ "General settings") + (_ "Settings that change the behavior of GNUnet in general") + (list + (network-port builder) + (hostlist-port builder) + (network-trusted builder) + (general-hostlisturl builder) + (general-hosts builder) + (general-http-proxy builder) + (f2f builder) + (fs-path builder) + (index-path builder) + (daemon-fdlimit builder) + (gnunetd-disable-ipv6 builder) + (general-username builder) + (general-groupname builder) + (general-pidfile builder) + (general-autostart builder) + ) + #t + #f + #f + 'always) ) + +(define (modules builder) + (builder + "MODULES" + "" + (_ "Modules") + (_ "Settings that select specific implementations for GNUnet modules") + (list + (modules-sqstore builder) + (modules-dstore builder) + (modules-topology builder) + ) + #t + #f + #f + 'advanced) ) + + + + +(define (fundamentals builder) + (builder + "PATHS" + "" + (_ "Fundamentals") + "" + (list + (paths-home builder) + (general-applications builder) + (general-transports builder) + (modules builder) + ) + #t + #f + #f + 'always) ) + + +;; modules menu + +(define (modules-sqstore builder) + (builder + "MODULES" + "sqstore" + (_ "Which database should be used?") + (_ +"Which database should be used? The options are \"sqstore_sqlite\", \"sqstore_postgres\" and \"sqstore_mysql\". You must run gnunet-update after changing this value! + +In order to use MySQL or Postgres, you must configure the respective database, which is relatively simple. Read the file doc/README.mysql or doc/README.postgres for how to setup the respective database." ) + '() + #t + "sqstore_sqlite" + (list "SC" "sqstore_sqlite" "sqstore_postgres" "sqstore_mysql") + 'fs-loaded) ) + +(define (modules-dstore builder) + (builder + "MODULES" + "dstore" + (_ "Which topology should be used?") + (_ "Which database should be used for the temporary datastore of the DHT?" ) + '() + #t + "dstore_sqlite" + (list "SC" "dstore_sqlite" "dstore_mysql") + 'advanced) ) + + +(define (modules-topology builder) + (builder + "MODULES" + "topology" + (_ "Which topology should be used?") + (_ +"Which topology should be used? The only option at the moment is \"topology_default\"" ) + '() + #t + "topology_default" + (list "SC" "topology_default") + 'rare) ) + + +;; f2f menu + +(define (f2f-minimum builder) + (builder + "F2F" + "MINIMUM" + (_ "The minimum number of connected friends before this peer is allowed to connect to peers that are not listed as friends") + (_ "Note that this option does not guarantee that the peer will be able to connect to the specified number of friends. Also, if the peer had connected to a sufficient number of friends and then established non-friend connections, some of the friends may drop out of the network, temporarily resulting in having fewer than the specified number of friends connected while being connected to non-friends. However, it is guaranteed that the peer itself will never choose to drop a friend's connection if this would result in dropping below the specified number of friends (unless that number is higher than the overall connection target).") + '() + #t + 0 + (cons 0 1024) + 'f2fr) ) + +(define (f2f-restrict builder) + (builder + "F2F" + "FRIENDS-ONLY" + (_ "If set to YES, the peer is only allowed to connect to other peers that are explicitly specified as friends") + (_ "Use YES only if you have (trustworthy) friends that use GNUnet and are afraid of establishing (direct) connections to unknown peers") + '() + #t + #f + #f + 'advanced) ) + +(define (f2f-friends builder) + (builder + "F2F" + "FRIENDS" + (_ "List of friends for friend-to-friend topology") + (_ "Specifies the name of a file which contains a list of GNUnet peer IDs that are friends. If used with the friend-to-friend topology, this will ensure that GNUnet only connects to these peers (via any available transport).") + '() + #f + "$GNUNETD_HOME/friends" + '() + 'f2f) ) + +(define (f2f builder) + (builder + "F2F" + "" + (_ "Friend-to-Friend Topology Specification") + (_ "Settings for restricting connections to friends") + (list + (f2f-restrict builder) + (f2f-minimum builder) + (f2f-friends builder) + ) + #t + #f + #f + 'advanced) ) + +;; mysql menu + +(define (mysql-database builder) + (builder + "MYSQL" + "DATABASE" + (_ "Name of the MySQL database GNUnet should use") + (nohelp) + '() + #t + "gnunet" + '() + 'mysql) ) + +(define (mysql-config builder) + (builder + "MYSQL" + "CONFIG" + (_ "Configuration file that specifies the MySQL username and password") + (nohelp) + '() + #t + "/etc/my.cnf" + '() + 'mysql) ) + +(define (mysql builder) + (builder + "MYSQL" + "" + (_ "Configuration of the MySQL database") + (nohelp) + (list + (mysql-config builder) + (mysql-database builder) + ) + #t + #f + #f + 'mysql) ) + + + +;; applications menu + +(define (fs-quota builder) + (builder + "FS" + "QUOTA" + (_ "MB of diskspace GNUnet can use for anonymous file sharing") + (_ +"How much disk space (MB) is GNUnet allowed to use for anonymous file sharing? This does not take indexed files into account, only the space directly used by GNUnet is accounted for. GNUnet will gather content from the network if the current space-consumption is below the number given here (and if content migration is allowed below). + +Note that if you change the quota, you need to run gnunet-update afterwards.") + '() + #t + 1024 + (cons 1 1000000) + 'always)) + + +(define (fs-migration-buffer builder) + (builder + "FS" + "MIGRATIONBUFFERSIZE" + (_ "Number of entries in the migration buffer") + (_ "Each entry uses about 32k of memory. More entries can reduce disk IO and CPU usage at the expense of having gnunetd use more memory. Very large values may again increase CPU usage. A value of 0 will prevent your peer from sending unsolicited responses.") + '() + #t + 64 + (cons 0 1048576) + 'always)) + + +(define (fs-gap-tablesize builder) + (builder + "GAP" + "TABLESIZE" + (_ "Size of the routing table for anonymous routing.") + (nohelp) + '() + #t + 65536 + (cons 1024 1048576) + 'rare)) + +(define (fs-dht-tablesize builder) + (builder + "DHT" + "TABLESIZE" + (_ "Size of the routing table for DHT routing.") + (nohelp) + '() + #t + 1024 + (cons 128 1048576) + 'rare)) + + +(define (fs-activemigration builder) + (builder + "FS" + "ACTIVEMIGRATION" + (_ "Allow migrating content to this peer.") + (_ +"If you say yes here, GNUnet will migrate content to your server, and you will not be able to control what data is stored on your machine. + +If you activate it, you can claim for *all* the non-indexed (-n to gnunet-insert) content that you did not know what it was even if an adversary takes control of your machine. If you do not activate it, it is obvious that you have knowledge of all the content that is hosted on your machine and thus can be considered liable for it.") + '() + #t + #f + #f + 'advanced)) + + +(define (dstore-quota builder) + (builder + "DSTORE" + "QUOTA" + (_ "MB of diskspace GNUnet can use for caching DHT index data (the data will be stored in /tmp)") + (_ "DHT index data is inherently small and expires comparatively quickly. It is deleted whenever gnunetd is shut down. + +The size of the DSTORE QUOTA is specified in MB.") + '() + #t + 1 + (cons 1 1024) + 'rare)) + + +(define (fs builder) + (builder + "FS" + "" + (_ "Options for anonymous file sharing") + (nohelp) + (list + (fs-quota builder) + (fs-activemigration builder) + (fs-gap-tablesize builder) + (fs-dht-tablesize builder) + (dstore-quota builder) + (mysql builder) + ) + #t + #t + #f + 'fs-loaded)) + +(define (applications builder) + (builder + "" + "" + (_ "Applications") + (nohelp) + (list + (fs builder) + ) + #t + #f + #f + 'always) ) + +;; transport menus + +(define (nat builder) + (builder + "NAT" + "LIMITED" + (_ "Is this machine unreachable behind a NAT?") + (_ "Set to YES if this machine is behind a NAT that limits connections from the outside to the GNUnet port and that cannot be traversed using UPnP. Note that if you have configured your NAT box to allow direct connections from other machines to the GNUnet ports or if GNUnet can open ports using UPnP, you should set the option to NO. Set this only to YES if other peers cannot contact you directly. You can use 'make check' in src/transports/upnp/ to find out if your NAT supports UPnP. You can also use gnunet-transport-check with the '-p' option in order to determine which setting results in more connections. Use YES only if you get no connections otherwise. Set to AUTO to use YES if the local IP is belongs to a private IP network and NO otherwise.") + '() + #t + "AUTO" + (list "SC" "YES" "AUTO" "NO") + 'nat-loaded) ) + +(define (tcp-port builder) + (builder + "TCP" + "PORT" + (_ "Which port should be used by the TCP IPv4 transport?") + (nohelp) + '() + #t + 2086 + (cons 0 65535) + 'advanced)) + +(define (tcp-upnp builder) + (builder + "TCP" + "UPNP" + (_ "Should we try to determine our external IP using UPnP?") + (nathelp) + '() + #t + #t + #f + 'tcp-loaded)) + +(define (tcp-blacklist builder) + (builder + "TCP" + "BLACKLISTV4" + (_ "Which IP(v4)s are not allowed to connect?") + (nohelp) + '() + #t + "127.0.0.1;" + '() + 'advanced)) + +(define (tcp-whitelist builder) + (builder + "TCP" + "WHITELISTV4" + (_ "Which IP(v4)s are allowed to connect? Leave empty to use the IP of your primary network interface.") + (nohelp) + '() + #t + "" + '() + 'advanced)) + +(define (tcp6-blacklist builder) + (builder + "TCP" + "BLACKLISTV6" + (_ "Which IPv6s are not allowed to connect?") + (nohelp) + '() + #t + "" + '() + 'ipv6)) + +(define (tcp6-whitelist builder) + (builder + "TCP" + "WHITELISTV6" + (_ "Which IPv6s are allowed to connect? Leave empty to allow any IP to connect.") + (nohelp) + '() + #t + "" + '() + 'ipv6)) + + +(define (tcp builder) + (builder + "TCP" + "" + (_ "TCP transport") + (nohelp) + (list + (tcp-port builder) + (tcp-upnp builder) + (tcp-blacklist builder) + (tcp-whitelist builder) + (tcp6-blacklist builder) + (tcp6-whitelist builder) + ) + #t + #f + #f + 'tcp-loaded) ) + + +(define (http-port builder) + (builder + "HTTP" + "PORT" + (_ "Which port should be used by the HTTP transport?") + (nohelp) + '() + #t + 1080 + (cons 0 65535) + 'advanced)) + +(define (http-upnp builder) + (builder + "HTTP" + "UPNP" + (_ "Should we try to determine our external IP using UPnP?") + (nathelp) + '() + #t + #t + #f + 'http-port-nz)) + +(define (http-advertised-port builder) + (builder + "HTTP" + "ADVERTISED-PORT" + (_ "Which is the external port of the HTTP transport?") + (_ "Use this option if your firewall maps, say, port 80 to your real HTTP port. This can be useful in making the HTTP messages appear even more legit (without needing to run gnunetd as root due to the use of a privileged port).") + '() + #t + 80 + (cons 0 65535) + 'advanced)) + +(define (http builder) + (builder + "HTTP" + "" + (_ "HTTP transport") + (nohelp) + (list + (http-port builder) + (http-advertised-port builder) + (http-upnp builder) + ) + #t + #f + #f + 'http-loaded) ) + + + + +(define (smtp-mtu builder) + (builder + "SMTP" + "MTU" + (_ "What is the maximum transfer unit for SMTP?") + (nohelp) + '() + #t + 65528 + (cons 1200 65528) + 'smtp-loaded)) + +(define (smtp-ratelimit builder) + (builder + "SMTP" + "RATELIMIT" + (_ "What is the maximum number of e-mails that gnunetd would be allowed to send per hour?") + (_ "Use 0 for unlimited") + '() + #t + 0 + (cons 0 1048576) + 'smtp-loaded)) + +(define (smtp-email builder) + (builder + "SMTP" + "EMAIL" + (_ "Which e-mail address should be used to send e-mail to this peer?") + (_ "You must make sure that e-mail received at this address is forwarded to the PIPE which is read by gnunetd. Use the FILTER option to filter e-mail with procmail and the PIPE option to set the name of the pipe.") + '() + #t + "gnunet@localhost" + '() + 'smtp-loaded)) + +(define (smtp-filter builder) + (builder + "SMTP" + "FILTER" + (_ "Which header line should other peers include in e-mails to enable filtering?") + (_ "You can specify a header line here which can then be used by procmail to filter GNUnet e-mail from your inbox and forward it to gnunetd.") + '() + #t + "X-mailer: GNUnet" + '() + 'smtp-loaded)) + +(define (smtp-pipe builder) + (builder + "SMTP" + "PIPE" + (_ "What is the filename of the pipe where gnunetd can read its e-mail?") + (_ "Have a look at contrib/dot-procmailrc for an example .procmailrc file.") + '() + #t + "$GNUNETD_HOME/smtp-pipe" + '() + 'smtp-loaded)) + +(define (smtp-server builder) + (builder + "SMTP" + "SERVER" + (_ "What is the name and port of the server for outgoing e-mail?") + (_ "The basic format is HOSTNAME:PORT.") + '() + #t + "localhost:25" + '() + 'smtp-loaded)) + +(define (smtp builder) + (builder + "SMTP" + "" + (_ "SMTP transport") + (nohelp) + (list + (smtp-email builder) + (smtp-ratelimit builder) + (smtp-filter builder) + (smtp-pipe builder) + (smtp-server builder) + (smtp-mtu builder) + ) + #t + #f + #f + 'smtp-loaded) ) + + + + + +(define (udp-port builder) + (builder + "UDP" + "PORT" + (_ "Which port should be used by the UDP IPv4 transport?") + (nohelp) + '() + #t + 2086 + (cons 0 65535) + 'advanced)) + +(define (udp-upnp builder) + (builder + "UDP" + "UPNP" + (_ "Should we try to determine our external IP using UPnP?") + (nathelp) + '() + #t + #t + #f + 'udp-port-nz)) + +(define (udp-mtu builder) + (builder + "UDP" + "MTU" + (_ "What is the maximum transfer unit for UDP?") + (nohelp) + '() + #t + 1472 + (cons 1200 65500) + 'rare)) + +(define (udp-blacklist builder) + (builder + "UDP" + "BLACKLISTV4" + (_ "Which IPs are not allowed to connect?") + (nohelp) + '() + #t + "127.0.0.1;" + '() + 'advanced)) + +(define (udp-whitelist builder) + (builder + "UDP" + "WHITELISTV4" + (_ "Which IPs are allowed to connect? Leave empty to allow connections from any IP.") + (nohelp) + '() + #t + "" + '() + 'advanced)) + +(define (udp6-blacklist builder) + (builder + "UDP" + "BLACKLISTV6" + (_ "Which IPv6s are not allowed to connect?") + (nohelp) + '() + #t + "" + '() + 'ipv6)) + +(define (udp6-whitelist builder) + (builder + "UDP6" + "WHITELISTV6" + (_ "Which IPv6s are allowed to connect? Leave empty to allow any IP to connect.") + (nohelp) + '() + #t + "" + '() + 'ipv6)) + +(define (udp builder) + (builder + "UDP" + "" + (_ "UDP transport") + (nohelp) + (list + (udp-port builder) + (udp-upnp builder) + (udp-mtu builder) + (udp-blacklist builder) + (udp-whitelist builder) + (udp6-blacklist builder) + (udp6-whitelist builder) + ) + #t + #f + #f + 'udp-loaded) ) + + + +(define (network-interface builder) + (builder + "NETWORK" + "INTERFACE" + (_ "Network interface") + (nohelp) + '() + #t + "eth0" + '() + 'advanced) ) + +(define (network-ip builder) + (builder + "NETWORK" + "IP" + (_ "External IP address (leave empty to try auto-detection)") + (nohelp) + '() + #t + "" + '() + 'advanced) ) + +(define (network-ip6 builder) + (builder + "NETWORK" + "IP6" + (_ "External IPv6 address (leave empty to try auto-detection)") + (nohelp) + '() + #t + "" + '() + 'ipv6) ) + +(define (transports builder) + (builder + "" + "" + (_ "Transports") + (nohelp) + (list + (nat builder) + (network-interface builder) + (network-ip builder) + (tcp builder) + (udp builder) + (http builder) + (smtp builder) + ) + #t + #f + #f + 'always) ) + + + +(define (load-maxdown builder) + (builder + "LOAD" + "MAXNETDOWNBPSTOTAL" + (_ "What is the maximum number of bytes per second that we may receive?") + (nohelp) + '() + #t + 50000 + (cons 1 999999999) + 'always)) + +(define (load-maxup builder) + (builder + "LOAD" + "MAXNETUPBPSTOTAL" + (_ "What is the maximum number of bytes per second that we may send?") + (nohelp) + '() + #t + 50000 + (cons 1 999999999) + 'always)) + +(define (load-cpu builder) + (builder + "LOAD" + "MAXCPULOAD" + (_ "What is the maximum CPU load (percentage)?") + (_ "The highest tolerable CPU load. Load here always refers to the total system load, that is it includes CPU utilization by other processes. A value of 50 means that once your 1 minute-load average goes over 50% non-idle, GNUnet will try to reduce CPU consumption until the load goes under the threshold. Reasonable values are typically between 50 and 100. Multiprocessors may use values above 100." ) + '() + #t + 100 + (cons 0 10000) + 'always)) + +(define (load-io builder) + (builder + "LOAD" + "MAXIOLOAD" + (_ "What is the maximum IO load (permille)?") + (_ +"The highest tolerable IO load. Load here refers to the percentage of CPU cycles wasted waiting for IO for the entire system, that is it includes disk utilization by other processes. A value of 10 means that once the average number of cycles wasted waiting for IO is more than 10% non-idle, GNUnet will try to reduce IO until the load goes under the threshold. Reasonable values are typically between 10 and 75." ) + '() + #t + 50 + (cons 0 10000) + 'advanced)) + +(define (load-cpu-hard builder) + (builder + "LOAD" + "HARDCPULIMIT" + (_ "What is the maximum CPU load (hard limit)?") + (_ "The highest tolerable CPU load. This is the hard limit, so once it is reached, gnunetd will start to massively drop data to reduce the load. Use with caution.") + '() + #t + 0 + (cons 0 99999) + 'rare)) + +(define (load-hard-up-limit builder) + (builder + "LOAD" + "HARDUPLIMIT" + (_ "What is the maximum upstream bandwidth (hard limit)?") + (_ "The limit is given as a percentage of the MAXNETUPBPS limit. Use 100 to have MAXNETUPBPS be the hard limit. Use zero for no limit.") + '() + #t + 0 + (cons 0 999999999) + 'rare)) + + +(define (load-priority builder) + (builder + "LOAD" + "PRIORITY" + (_ "What priority should gnunetd use to run?") + (_ "You can specify priorities like NORMAL, ABOVE NORMAL, BELOW NORMAL, HIGH and IDLE or a numerical integer value (man nice). The default is IDLE, which should result in gnunetd only using resources that would otherwise be idle.") + '() + #t + "IDLE" + '() + 'always)) + + +(define (load-padding builder) + (builder + "GNUNETD-EXPERIMENTAL" + "PADDING" + (_ "Should we disable random padding (experimental option)?") + (nohelp) + '() + #t + #f + #f + 'experimental)) + +(define (load-basiclimiting builder) + (builder + "LOAD" + "BASICLIMITING" + (_ "Use basic bandwidth limitation? (YES/NO). If in doubt, say YES.") + (_ +"Basic bandwidth limitation (YES) means simply that the bandwidth limits specified apply to GNUnet and only to GNUnet. If set to YES, you simply specify the maximum bandwidth (upstream and downstream) that GNUnet is allowed to use and GNUnet will stick to those limitations. This is useful if your overall bandwidth is so large that the limit is mostly used to ensure that enough capacity is left for other applications. Even if you want to dedicate your entire connection to GNUnet you should not set the limits to values higher than what you have since GNUnet uses those limits to determine for example the number of connections to establish (and it would be inefficient if that computation yields a number that is far too high). + +While basic bandwidth limitation is simple and always works, there are some situations where it is not perfect. Suppose you are running another application which performs a larger download. During that particular time, it would be nice if GNUnet would throttle its bandwidth consumption (automatically) and resume using more bandwidth after the download is complete. This is obviously advanced magic since GNUnet will have to monitor the behavior of other applications. Another scenario is a monthly cap on bandwidth imposed by your ISP, which you would want to ensure is obeyed. Here, you may want GNUnet to monitor the traffic from other applications to ensure that the combined long-term traffic is within the pre-set bounds. Note that you should probably not set the bounds tightly since GNUnet may observe that the bounds are about to be broken but would be unable to stop other applications from continuing to use bandwidth. + +If either of these two scenarios applies, set BASICLIMITING to NO. Then set the bandwidth limits to the COMBINED amount of traffic that is acceptable for both GNUnet and other applications. GNUnet will then immediately throttle bandwidth consumption if the short-term average is above the limit, and it will also try to ensure that the long-term average is below the limit. Note however that using NO can have the effect of GNUnet (almost) ceasing operations after other applications perform high-volume downloads that are beyond the defined limits. GNUnet would reduce consumption until the long-term limits are again within bounds. + +NO only works on platforms where GNUnet can monitor the amount of traffic that the local host puts out on the network. This is only implemented for Linux and Win32. In order for the code to work, GNUnet needs to know the specific network interface that is used for the external connection (after all, the amount of traffic on loopback or on the LAN should never be counted since it is irrelevant).") + '() + #t + #t + #f + 'advanced)) + +(define (load-interfaces builder) + (builder + "LOAD" + "INTERFACES" + (_ "Network interface to monitor") + (_ "For which interfaces should we do accounting? GNUnet will evaluate the total traffic (not only the GNUnet related traffic) and adjust its bandwidth usage accordingly. You can currently only specify a single interface. GNUnet will also use this interface to determine the IP to use. Typical values are eth0, ppp0, eth1, wlan0, etc. 'ifconfig' will tell you what you have. Never use 'lo', that just won't work. Under Windows, specify the index number reported by 'gnunet-win-tool -n'.") + '() + #t + "eth0" + (list "*" "eth0" "eth1" "eth2") + 'nobasiclimit)) + +(define (load builder) + (builder + "" + "" + (_ "Load management") + (nohelp) + (list + (load-priority builder) + (load-maxdown builder) + (load-maxup builder) + (load-hard-up-limit builder) + (load-cpu builder) + (load-io builder) + (load-cpu-hard builder) + (load-basiclimiting builder) + (load-interfaces builder) + (load-padding builder) + ) + #t + #f + #f + 'always) ) + + +;; main-menu + +(define (main builder) + (builder + "Root" + "" + (_ "Root node") + (nohelp) + (list + (meta builder) + (fundamentals builder) + (general builder) + (advertising builder) + (logging builder) + (load builder) + (transports builder) + (applications builder) + ) + #t + #f + #f + 'always) ) + + + +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + +;; first main method: build tree using build-tree-node +;; The lambda expression is used to throw away the last argument, +;; which we use internally and which is not used by build-tree-node! +(define (gnunet-config-setup) + (main + (lambda (a b c d e f g h i) (build-tree-node a b c d e f g h) ) ) ) + + +;; second main method: update visibility (and values) +;; "change" uses again the tree builder but this time +;; scans the "i" tags to determine how the visibility needs to change + +(define (gnunet-config-change ctx) + (let + ( + (advanced (get-option ctx "Meta" "ADVANCED")) + (rare (get-option ctx "Meta" "RARE")) + (nobasiclimit (not (get-option ctx "LOAD" "BASICLIMITING"))) + (experimental (get-option ctx "Meta" "EXPERIMENTAL")) + (ipv6 (not (get-option ctx "GNUNETD" "DISABLE-IPV6"))) + (f2fr (not (get-option ctx "F2F" "RESTRICT") ) ) + (f2f (or (get-option ctx "F2F" "FRIENDS-ONLY") + (not (eq? (get-option ctx "F2F" "MINIMUM") 0) ) ) ) + (tcp-port-nz (eq? (get-option ctx "TCP" "PORT") 0) ) + (udp-port-nz (eq? (get-option ctx "UDP" "PORT") 0) ) + (http-port-nz (eq? (get-option ctx "HTTP" "PORT") 0) ) + (mysql (string= (get-option ctx "MODULES" "sqstore") "sqstore_mysql") ) + (fs-loaded (list? (member "fs" (string-split (get-option ctx "GNUNETD" "APPLICATIONS") #\ ) ) ) ) + (hostlist-loaded (list? (member "hostlist" (string-split (get-option ctx "GNUNETD" "APPLICATIONS") #\ ) ) ) ) + (nat-loaded (list? (member "nat" (string-split (get-option ctx "GNUNETD" "TRANSPORTS") #\ ) ) ) ) + (tcp-loaded (list? (member "tcp" (string-split (get-option ctx "GNUNETD" "TRANSPORTS") #\ ) ) ) ) + (udp-loaded (list? (member "udp" (string-split (get-option ctx "GNUNETD" "TRANSPORTS") #\ ) ) ) ) + (http-loaded (list? (member "http" (string-split (get-option ctx "GNUNETD" "TRANSPORTS") #\ ) ) ) ) + (smtp-loaded (list? (member "smtp" (string-split (get-option ctx "GNUNETD" "TRANSPORTS") #\ ) ) ) ) + ) + (begin + (main + (lambda (a b c d e f g h i) + (begin + (cond + ((eq? i 'advanced) (change-visible ctx a b advanced)) + ((eq? i 'rare) (change-visible ctx a b (and advanced rare))) + ((eq? i 'experimental) (change-visible ctx a b (and advanced experimental))) + ((eq? i 'f2f) (change-visible ctx a b f2f)) + ((eq? i 'ipv6) (change-visible ctx a b ipv6)) + ((eq? i 'f2fr) (change-visible ctx a b f2fr)) + ((eq? i 'mysql) (change-visible ctx a b mysql)) + ((eq? i 'fs-loaded) (change-visible ctx a b fs-loaded)) + ((eq? i 'hostlist-loaded) (change-visible ctx a b hostlist-loaded)) + ((eq? i 'nat-unlimited)(change-visible ctx a b nat-unlimited)) + ((eq? i 'tcp-port-nz) (change-visible ctx a b tcp-port-nz)) + ((eq? i 'udp-port-nz) (change-visible ctx a b udp-port-nz)) + ((eq? i 'nat-loaded) (change-visible ctx a b nat-loaded)) + ((eq? i 'udp-loaded) (change-visible ctx a b udp-loaded)) + ((eq? i 'tcp-loaded) (change-visible ctx a b tcp-loaded)) + ((eq? i 'http-loaded) (change-visible ctx a b http-loaded)) + ((eq? i 'smtp-loaded) (change-visible ctx a b smtp-loaded)) + ((eq? i 'nobasiclimit) (change-visible ctx a b nobasiclimit)) + (else 'nothing) + ) + ) ) ) + (change-visible ctx "NETWORK" "PORT" (and advanced (not (get-option ctx "TCPSERVER" "DISABLE")))) + (change-visible ctx "NETWORK" "TRUSTED" (and advanced (not (get-option ctx "TCPSERVER" "DISABLE")))) + ) +) ) + diff --git a/contrib/coverage.sh b/contrib/coverage.sh new file mode 100755 index 000000000..72314ae67 --- /dev/null +++ b/contrib/coverage.sh @@ -0,0 +1,13 @@ +#!/bin/sh +# make sure configure was run with coverage enabled... +lcov --directory . --zerocounters +make check +for n in `find * -name "*.gc??" | grep libs` +do + cd `dirname $n` + mv `basename $n` .. + cd - +done +lcov --directory . --capture --output-file app.info +mkdir -p doc/coverage +genhtml -o doc/coverage app.info diff --git a/contrib/defaults.conf b/contrib/defaults.conf new file mode 100644 index 000000000..d6100b77d --- /dev/null +++ b/contrib/defaults.conf @@ -0,0 +1,139 @@ +[PATHS] +SERVICEHOME = /var/lib/gnunet/ +DEFAULTCONFIG = /etc/gnunetd.conf + +[gnunetd] +HOSTKEY = $SERVICEHOME/.hostkey + +[TESTING] +WEAKRANDOM = NO + + +[tcp] +PORT = 2086 +TIMEOUT = 300000 +ALLOW_SHUTDOWN = NO +# MAXBUF = +# DISABLEV6 = +# BINDTO = +# ACCEPT_FROM = +# ACCEPT_FROM6 = +# REJECT_FROM = +# REJECT_FROM6 = + + +[arm] +PORT = 2087 +HOSTNAME = localhost +HOME = $SERVICEHOME +CONFIG = $DEFAULTCONFIG +BINARY = gnunet-service-arm +ACCEPT_FROM = 127.0.0.1; +ACCEPT_FROM6 = ::1; +ALLOW_SHUTDOWN = YES +# GLOBAL_PREFIX = +# USERNAME = +# MAXBUF = +# TIMEOUT = +# DISABLEV6 = +# BINDTO = +# REJECT_FROM = +# REJECT_FROM6 = +# PREFIX = + +[statistics] +PORT = 2088 +HOSTNAME = localhost +HOME = $SERVICEHOME +CONFIG = $DEFAULTCONFIG +BINARY = gnunet-service-statistics +ACCEPT_FROM = 127.0.0.1; +ACCEPT_FROM6 = ::1; +ALLOW_SHUTDOWN = YES +# USERNAME = +# MAXBUF = +# TIMEOUT = +# DISABLEV6 = +# BINDTO = +# REJECT_FROM = +# REJECT_FROM6 = +# PREFIX = + +[resolver] +PORT = 2089 +HOSTNAME = localhost +HOME = $SERVICEHOME +CONFIG = $DEFAULTCONFIG +BINARY = gnunet-service-resolver +ACCEPT_FROM = 127.0.0.1; +ACCEPT_FROM6 = ::1; +ALLOW_SHUTDOWN = YES +# USERNAME = +# MAXBUF = +# TIMEOUT = +# DISABLEV6 = +# BINDTO = +# REJECT_FROM = +# REJECT_FROM6 = +# PREFIX = + +[peerinfo] +PORT = 2090 +HOSTNAME = localhost +HOME = $SERVICEHOME +CONFIG = $DEFAULTCONFIG +BINARY = gnunet-service-peerinfo +ACCEPT_FROM = 127.0.0.1; +ACCEPT_FROM6 = ::1; +ALLOW_SHUTDOWN = YES +# USERNAME = +# MAXBUF = +# TIMEOUT = +# DISABLEV6 = +# BINDTO = +# REJECT_FROM = +# REJECT_FROM6 = +# PREFIX = +HOSTS = $SERVICEHOME/data/hosts/ +TRUST = $SERVICEHOME/data/credit/ + + +[transport] +PORT = 2091 +HOSTNAME = localhost +HOME = $SERVICEHOME +CONFIG = $DEFAULTCONFIG +BINARY = gnunet-service-transport +DEFAULT_QUOTA_IN = 50000 +DEFAULT_QUOTA_OUT = 50000 +NEIGHBOUR_LIMIT = 50 +ACCEPT_FROM = 127.0.0.1; +ACCEPT_FROM6 = ::1; +ALLOW_SHUTDOWN = YES +# USERNAME = +# MAXBUF = +# TIMEOUT = +# DISABLEV6 = +# BINDTO = +# REJECT_FROM = +# REJECT_FROM6 = +# PREFIX = + + +[core] +PORT = 2092 +HOSTNAME = localhost +HOME = $SERVICEHOME +CONFIG = $DEFAULTCONFIG +BINARY = gnunet-service-core +ACCEPT_FROM = 127.0.0.1; +ACCEPT_FROM6 = ::1; +ALLOW_SHUTDOWN = YES +# USERNAME = +# MAXBUF = +# TIMEOUT = +# DISABLEV6 = +# BINDTO = +# REJECT_FROM = +# REJECT_FROM6 = +# PREFIX = diff --git a/contrib/gnunet-logo-big.png b/contrib/gnunet-logo-big.png new file mode 100644 index 000000000..54c418f8f Binary files /dev/null and b/contrib/gnunet-logo-big.png differ diff --git a/contrib/gnunet-logo-color.png b/contrib/gnunet-logo-color.png new file mode 100644 index 000000000..326822ebd Binary files /dev/null and b/contrib/gnunet-logo-color.png differ diff --git a/contrib/hostlist.cgi b/contrib/hostlist.cgi new file mode 100644 index 000000000..f04246e47 --- /dev/null +++ b/contrib/hostlist.cgi @@ -0,0 +1,5 @@ +#!/bin/sh +# This is a CGI script to generate the host list on-demand. +# by Michael Wensley, with minor improvements by Christian Grothoff +echo -ne "Content-Type: application/octet-stream\r\n\r\n" +cat /var/lib/gnunet/data/hosts/*.{2,3,4,5,6,8,12,17,23,25} diff --git a/contrib/hostlist.php b/contrib/hostlist.php new file mode 100644 index 000000000..158561542 --- /dev/null +++ b/contrib/hostlist.php @@ -0,0 +1,35 @@ += 4.3.0 +// Author: "Krasko Oleksandr" <0m3r.mail@gmail.com> +// Minor improvements by Christian Grothoff +header("Content-Type: application/octet-stream\r\n\r\n"); +$extmas = array(); +$pv=$_GET['p']; +if (isset($pv)) + { + for ($ii=0;$ii<64;$ii++) + if (0 != ($pv & (1 << $ii))) + $extmas[] = $ii; + } +else + { + $extmas = array('2','3','4','5','6','8','12','17','23','25'); + } +$path = '/var/lib/gnunet/data/hosts/'; // adjust as necessary +$dir = opendir($path); +if (! $dir) + die("Cannot open directory $path.\n"); +$mas = array(); +while ($fname = readdir($dir)) { + $fn = $path . '/' . $fname; + if (is_file($fn)) { + $dpo = strpos($fname, '.') + 1; + $len = strlen($fname); + if (in_array(substr($fname, $dpo - $len), $extmas)) + $mas[] = $fn; + } +} +shuffle($mas); // randomize order +foreach ($mas as $val) + echo file_get_contents($val); +?> diff --git a/contrib/report.sh b/contrib/report.sh new file mode 100755 index 000000000..76abe5a7c --- /dev/null +++ b/contrib/report.sh @@ -0,0 +1,200 @@ +#!/bin/sh + +TEST=`type type|grep not` +if test -n "$TEST"; then + WHICH=which +else + WHICH=type +fi + +echo "On some systems, you may need to change /bin/sh to point to bash" +echo +echo "Please submit the following information with your bug report: " +echo "--------------------------------------------------------------" +OS=`uname -s 2>/dev/null` +echo "OS : $OS" +REL=`uname -r 2>/dev/null` +echo "OS RELEASE : $REL" +HW=`uname -m 2>/dev/null` +echo "HARDWARE : $HW" + +TEST=`$WHICH gcc 2>/dev/null` +if test -n "$TEST"; then + VERS=`gcc --version 2>/dev/null | head -n 1` + echo "gcc : $VERS" +else + echo "gcc : Not Found"; +fi + +TEST=`$WHICH gmake 2>/dev/null` +if test -n "$TEST" ; then + gmake --version 2>/dev/null |\ + awk -F, '{print $1}' |\ + awk '/GNU Make/{print "GNU gmake :",$NF}' +else + TEST=`make --version 2>/dev/null` + if test -n "$TEST"; then + make --version 2>/dev/null |\ + awk -F, '{print $1}' |\ + awk '/GNU Make/{print "make :",$NF}' + else + echo "make : Not Found" + fi +fi + +TEST=`$WHICH autoconf 2>/dev/null` +if test -n "$TEST"; then + autoconf --version |\ + head -n 1 |\ + awk '{\ + if (length($4) == 0) {\ + print "autoconf : "$3\ + } else {\ + print "autoconf : "$4\ + }}' +else + echo "autoconf : Not Found" +fi + +TEST=`$WHICH automake 2>/dev/null` +if test -n "$TEST"; then + automake --version 2>/dev/null |\ + head -n 1 |\ + awk '{print "automake : "$4}' +else + echo "automake : Not Found" +fi + +TEST=`$WHICH libtool 2>/dev/null` +if test -n "$TEST"; then + libtool --version 2>/dev/null |\ + head -n 1 |\ + awk '{print "libtool : "$4}' +else + echo "libtool : Not Found" +fi + +TEST=`$WHICH extract 2>/dev/null` +if test -n "$TEST"; then + extract -v 2>/dev/null |\ + head -n 1 |\ + awk '{print "libextractor : "$2}' +else + echo "libextractor : Not Found" +fi + +TEST=`$WHICH gnunetd 2>/dev/null` +if test -n "$TEST"; then + gnunetd -v | sed -e "s/v//" 2>/dev/null |\ + awk '{print "GNUnet : "$2}' +else + echo "GNUnet : Not Found" +fi + +TEST=`$WHICH libgcrypt-config 2> /dev/null` +if test -n "$TEST"; then + libgcrypt-config --version 2> /dev/null | \ + awk '{print "libgcrypt : "$1}' +else + echo "libgcrypt : Not Found" +fi + +TEST=`$WHICH mysql_config 2> /dev/null` +if test -n "$TEST"; then + mysql_config --version 2> /dev/null | \ + awk '{print "mysql : "$1}' +else + echo "mysql : Not Found" +fi + +TEST=`$WHICH pkg-config 2> /dev/null` +if test -n "$TEST"; then + pkg-config --version 2> /dev/null | \ + awk '{print "pkg-config : "$1}' +else + echo "pkg-config : Not Found" +fi + +TEST=`$WHICH pkg-config 2> /dev/null` +if test -n "$TEST"; then + pkg-config --modversion glib-2.0 2> /dev/null | \ + awk '{print "glib2 : "$1}' +else + echo "glib2 : Not Found" +fi + +TEST=`$WHICH pkg-config 2> /dev/null` +if test -n "$TEST"; then + pkg-config --modversion gtk+-2.0 2> /dev/null | \ + awk '{print "gtk2+ : "$1}' +else + echo "gtk2+ : Not Found" +fi + +TEST=`$WHICH dpkg 2> /dev/null` +if test -n "$TEST"; then + LINES=`dpkg -s libgmp3-dev | grep Version | wc -l` + if test "$LINES" = "1" + then + VERSION=`dpkg -s libgmp3-dev | grep Version | awk '{print $2}'` + echo "GMP : libgmp3-dev-$VERSION.deb" + else + echo "GMP : dpkg: libgmp3-dev not installed" + fi +else + TEST=`$WHICH rpm 2> /dev/null` + if test -n "$TEST"; then + rpm -q gmp | sed -e "s/gmp-//" 2> /dev/null | \ + awk '{print "GMP : "$1.rpm}' + else + echo "GMP : Test not available" + fi +fi + +TEST=`$WHICH gettext 2> /dev/null` +if test -n "$TEST"; then + gettext --version | head -n1 2> /dev/null | \ + awk '{print "GNU gettext : "$4}' +else + echo "GNU gettext : Not found" +fi + + +TEST=`$WHICH guile 2> /dev/null` +if test -n "$TEST"; then + guile --version | head -n1 2> /dev/null | \ + awk '{print "Guile : "$2}' +else + echo "Guile : Not found" +fi + + +TEST=`$WHICH curl-config 2> /dev/null` +if test -n "$TEST"; then + curl-config --version | head -n1 2> /dev/null | \ + awk '{print "libcurl : "$2}' +else + echo "libcurl : Not found" +fi + + +TEST=`grep "Id:" /usr/include/adns.h 2> /dev/null` +if test -n "$TEST"; then + echo "$TEST" | awk '{print "GNU adns : "$4}' +else + echo "GNU adns : Not found" +fi + +TEST=`which qmake 2> /dev/null` +if test -x "$TEST"; then + qmake --version | tail -n 1 | awk '{print "Qt : "$4}' +else + echo "Qt : Not found" +fi + + + + + + +echo "--------------------------------------------------------------" diff --git a/doc/man/Makefile.am b/doc/man/Makefile.am new file mode 100644 index 000000000..734fc9c85 --- /dev/null +++ b/doc/man/Makefile.am @@ -0,0 +1,7 @@ +man_MANS = \ + gnunet-arm.1 \ + gnunet-peerinfo.1 \ + gnunet-statistics.1 \ + gnunet-transport.c + +EXTRA_DIST = ${man_MANS} diff --git a/doc/man/gnunet-arm.1 b/doc/man/gnunet-arm.1 new file mode 100644 index 000000000..e440479c3 --- /dev/null +++ b/doc/man/gnunet-arm.1 @@ -0,0 +1,57 @@ +.TH GNUNET\-ARM 1 "Mar 7, 2009" "GNUnet" + +.SH NAME +gnunet\-arm \- control GNUnet services + +.SH SYNOPSIS +.B gnunet-arm +.RI [ options ] +.br + +.SH DESCRIPTION +\fBgnunet\-arm\fP can be used to start or stop GNUnet services, including the ARM service itself. + +.SH OPTIONS + +.TP +.IP "\-c FILENAME, \-\-config=FILENAME" +Use the configuration file FILENAME. + +.TP +.IP "\-e, \-\-end" +Shutdown all GNUnet services (including ARM). + +.TP +.IP "\-h, \-\-help" +Print short help on options. + +.TP +.IP "\-L LOGLEVEL, \-\-loglevel=LOGLEVEL" +Use LOGLEVEL for logging. Valid values are DEBUG, INFO, WARNING and ERROR. + +.TP +.IP "\-i SERVICE, \-\-init=SERVICE" +Starts the specified SERVICE if it is not already running. + +.TP +.IP "\-k SERVICE, \-\-kill=SERVICE" +Stop the specified SERVICE if it is running. + +.TP +.IP "\-s, \-\-start" +Start all GNUnet default services on this system (including ARM). + +.TP +.IP "\-t SERVICE, \-\-test=SERVICE" +Report if the specified SERVICE is running. + +.TP +.IP "\-v, \-\-version" +Print GNUnet version number. + + +.SH BUGS +Report bugs by using Mantis or by sending electronic mail to + +.SH SEE ALSO +gnunet\-service\-arm(1) diff --git a/doc/man/gnunet-peerinfo.1 b/doc/man/gnunet-peerinfo.1 new file mode 100644 index 000000000..2980d3544 --- /dev/null +++ b/doc/man/gnunet-peerinfo.1 @@ -0,0 +1,42 @@ +.TH GNUNET-PEERINFO 1 "Mar 15, 2009" "GNUnet" + +.SH NAME +gnunet\-peerinfo \- Display information about other peers. + +.SH SYNOPSIS +.B gnunet\-peerinfo +.RI [ options ] +.br + +.SH DESCRIPTION +.PP +\fBgnunet\-peerinfo\fP display the known addresses and trust of known peers. + +.SH OPTIONS +.B +.IP "\-c FILENAME, \-\-config=FILENAME" +Load config file (default: /etc/gnunet.conf) +.B +.IP "\-h, \-\-help" +Print help page +.B +.IP "\-n, \-\-numeric" +Disable resolution of IPs to hostnames +.B +.IP "\-q, \-\-quiet" +Do not print anything but the peer identities +.B +.IP "\-s, \-\-self" +Print only our own identity (together with "\-q", this is the exact line that other peers would have to put in to their friends file in order to consider this peer one of their friends in F2F mode). +.B +.IP "\-v, \-\-version" +Print the version number +.B +.IP "\-L LOGLEVEL, \-\-loglelvel=LOGLEVEL" +Set the loglevel + +.SH BUGS +Report bugs by using mantis or by sending electronic mail to + +.SH SEE ALSO +gnunet.conf(5) diff --git a/doc/man/gnunet-statistics.1 b/doc/man/gnunet-statistics.1 new file mode 100644 index 000000000..8a23e01e0 --- /dev/null +++ b/doc/man/gnunet-statistics.1 @@ -0,0 +1,50 @@ +.TH GNUNET\-STATISTICS 1 "Mar 6, 2009" "GNUnet" + +.SH NAME +gnunet\-statistics \- Display statistics about your GNUnet system + +.SH SYNOPSIS +.B gnunet-statistics +.RI [ options ] [ VALUE ] +.br + +.SH DESCRIPTION +\fBgnunet\-statistics\fP is used to display detailed information about various aspect of GNUnet's operation. This tool only works if the "statistics" service is available. +gnunet\-statistics can be used to set a value by giving the options \-n, \-s and also a VALUE. + +.SH OPTIONS + +.TP +.IP "\-c FILENAME, \-\-config=FILENAME" +Use the configuration file FILENAME. + +.TP +.IP "\-h, \-\-help" +Print short help on options. + +.TP +.IP "\-L LOGLEVEL, \-\-loglevel=LOGLEVEL" +Use LOGLEVEL for logging. Valid values are DEBUG, INFO, WARNING and ERROR. + +.TP +.IP "\-n NAME, \-\-name=NAME" +Each statistic has a name that is unique with in its subsystem. With this option, the output can be restricted to statistics that have a particular name. + +.TP +.IP "\-p, \-\-persistent" +When setting a value, make the value persistent. If the value used to be persistent and this flag is not given, it will be marked as non\-persistent. + +.TP +.IP "\-s SUBSYSTEM, \-\-subsystem=SUBSYSTEM" +Statistics are kept for various subsystems. With this option, the output can be restricted to a particular subsystem only. + +.TP +.IP "\-v, \-\-version" +Print GNUnet version number. + + +.SH BUGS +Report bugs by using Mantis or by sending electronic mail to + +.SH SEE ALSO +gnunet\-service\-statistics(1) diff --git a/doc/man/gnunet-transport.1 b/doc/man/gnunet-transport.1 new file mode 100644 index 000000000..643759395 --- /dev/null +++ b/doc/man/gnunet-transport.1 @@ -0,0 +1,96 @@ +.TH GNUNET-TRANSPORT "1" "23 Dec 2006" "GNUnet" +.SH NAME +gnunet\-transport \- a tool to test a GNUnet transport service +.SH SYNOPSIS +.B gnunet\-transport\ +[\fIOPTIONS\fR] +.SH DESCRIPTION +.PP +gnunet\-transport can be used to test or profile +a GNUnet transport service. The tool can be used to test +both the correctness of the software as well as the correctness +of the configuration. gnunet\-transport features two modes, +called loopback mode and ping mode. In loopback mode the test is limited to testing if the +transport can be used to communicate with itself (loopback). +This mode does not include communication with other peers which +may be blocked by firewalls and other general Internet connectivity +problems. The loopback mode is particularly useful to test +the SMTP transport service since this service is fairly hard to +configure correctly and most problems can be reveiled by just +testing the loopback. In ping mode the tool will attempt to download +peer advertisements from the URL specified in the configuration file +and then try to contact each of the peers. Note that it is perfectly +normal that some peers do not respond, but if no peer responds something +is likely to be wrong. The configuration is always taken +from the configuration file. Do not run gnunetd while running +gnunet\-transport since the transport services cannot +be used by two processes at the same time. +.PP +gnunet\-transport will always produce an error\-message for +the NAT transport in loopback mode. If NAT is configured in accept\-mode (as in, +accept connections from peers using network address translation), +the check will fail with the message "could not create HELO", +which is correct since the peer itself is clearly not going to +advertise itself as a NAT. If the peer is configured in NAT\-mode, +that is, the peer is behind a NAT box, the message will be +'could not connect'. For NAT, both messages are NOT errors +but exactly what is supposed to happen. +.PP +Similarly, a NAT\-ed peer should typically configure the TCP transport +to use port 0 (not listen on any port). In this case, +gnunet\-transport will print 'could not create HELO' for the +TCP transport. This is also ok. In fact, a correctly configured +peer using NAT should give just two errors (could not connect for +tcp and could not create HELO for NAT) when tested using +gnunet\-transport\. The reason is, that gnunet\-transport\ +only tests loopback connectivity, and for a NAT\-ed peer, that just +does not apply. +.PP +Note that in ping mode the HTTP download times out after 5 minutes, +so if the list of peers is very large and not all peers can be +queried within the 5 minutes the tool may abort before trying all +peers. +.TP +\fB\-c \fIFILENAME\fR, \fB\-\-config=\fIFILENAME\fR +use config file (default: /etc/gnunetd.conf) +.TP +\fB\-h\fR, \fB\-\-help\fR +print help page +.TP +\fB\-L \fILOGLEVEL\fR, \fB\-\-loglevel=\fILOGLEVEL\fR +change the loglevel. Possible values for \fILOGLEVEL\fR are NOTHING, FATAL, ERROR, FAILURE, WARNING, MESSAGE, INFO, DEBUG, CRON and EVERYTHING. +.TP +\fB\-p\fR, \fB\-\-ping\fR +use ping mode (loopback mode is default) +.TP +\fB\-r\fI COUNT \fB\-\-repeat=\fICOUNT\fR +send COUNT messages in a sequence over the same connection +.TP +\fB\-s\fI SIZE \fB\-\-size=\fISIZE\fR +test using the specified message size, default is 11 +.TP +\fB\-t\fI TRANSPORT\fR, \fB\-\-transport=\fITRANSPORT\fR +run using the specified transport, if not given the transports +configured in the configuration file are used. +.TP +\fB\-u \fIUSER\fR, \fB\-\-user=USER\fR +run as user USER (and if available as group USER). Note that to use this option, you will probably have to start gnunet-transport as +root. It is typically better to directly start gnunet-transport as that user instead. +.TP +\fB\-v\fR, \fB\-\-version\fR +print the version number +.TP +\fB\-V\fR, \fB\-\-verbose\fR +be verbose +.SH NOTES +gnunet\-transport can run for a long time, depending on +how high you have set the \fICOUNT\fR level. Run first with small numbers +for \fICOUNT\fR to get an initial estimate on the runtime. +.SH FILES +.TP +/etc/gnunetd.conf +default gnunetd configuration file +.SH "REPORTING BUGS" +Report bugs by using mantis or by sending electronic mail to +.SH "SEE ALSO" +\fBgnunetd.conf\fP(5), \fBgnunetd\fP(1) diff --git a/m4/Makefile.am b/m4/Makefile.am new file mode 100644 index 000000000..128870999 --- /dev/null +++ b/m4/Makefile.am @@ -0,0 +1 @@ +EXTRA_DIST = . diff --git a/m4/align.m4 b/m4/align.m4 new file mode 100644 index 000000000..256300e37 --- /dev/null +++ b/m4/align.m4 @@ -0,0 +1,28 @@ +# align.m4 +dnl Copyright (C) 2008 Christian Grothoff +dnl This file is free software, distributed under the terms of the GNU +dnl General Public License. As a special exception to the GNU General +dnl Public License, this file may be distributed as part of a program +dnl that contains a configuration script generated by Autoconf, under +dnl the same distribution terms as the rest of that program. + +# Define HAVE_UNALIGNED_64_ACCESS if reading a 64-bit value at a 32-bit aligned offset works +# Note that the program intentionally causes a SIGBUS (so you may +# see some message along those lines on the console). +AC_DEFUN([AC_UNALIGNED_64_ACCESS], +[AC_CACHE_CHECK([whether unaligned 64-bit access works], + ac_cv_unaligned_64_access, + [ + AC_RUN_IFELSE(AC_LANG_PROGRAM([[struct S { int a,b,c;};]], + [[struct S s = {0,0,0}; long long * p = &s.b; + long long x = *p; return (int) x*x;]]), + ac_cv_unaligned_64_access=yes, + ac_cv_unaligned_64_access=no, + ac_cv_unaligned_64_access=no) + ]) + case "$ac_cv_unaligned_64_access" in + *yes) value=1;; + *) value=0;; + esac + AC_DEFINE_UNQUOTED([HAVE_UNALIGNED_64_ACCESS], $value, [We can access-64 bit values that are only 32-bit aligned]) +]) diff --git a/po/ChangeLog b/po/ChangeLog new file mode 100644 index 000000000..7ff113c61 --- /dev/null +++ b/po/ChangeLog @@ -0,0 +1,16 @@ +2007-02-08 gettextize + + * Makefile.in.in: Upgrade to gettext-0.16.1. + * Rules-quot: Upgrade to gettext-0.16.1. + +2004-08-21 gettextize + + * Makefile.in.in: New file, from gettext-0.14. + * boldquot.sed: New file, from gettext-0.14. + * en@boldquot.header: New file, from gettext-0.14. + * en@quot.header: New file, from gettext-0.14. + * insert-header.sin: New file, from gettext-0.14. + * quot.sed: New file, from gettext-0.14. + * remove-potcdate.sin: New file, from gettext-0.14. + * Rules-quot: New file, from gettext-0.14. + diff --git a/po/GNUnet.pot b/po/GNUnet.pot new file mode 100644 index 000000000..47bf3dfae --- /dev/null +++ b/po/GNUnet.pot @@ -0,0 +1,5168 @@ +# SOME DESCRIPTIVE TITLE. +# Copyright (C) YEAR Christian Grothoff +# This file is distributed under the same license as the PACKAGE package. +# FIRST AUTHOR , YEAR. +# +#, fuzzy +msgid "" +msgstr "" +"Project-Id-Version: PACKAGE VERSION\n" +"Report-Msgid-Bugs-To: gnunet-developers@mail.gnu.org\n" +"POT-Creation-Date: 2009-01-08 10:55-0700\n" +"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" +"Last-Translator: FULL NAME \n" +"Language-Team: LANGUAGE \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=CHARSET\n" +"Content-Transfer-Encoding: 8bit\n" + +#: src/setup/ncurses/wizard_curs.c:72 src/setup/lib/wizard_util.c:155 +#: src/setup/lib/wizard_util.c:210 +msgid "Error" +msgstr "" + +#: src/setup/ncurses/wizard_curs.c:80 +msgid "Help" +msgstr "" + +#: src/setup/ncurses/wizard_curs.c:87 +msgid "Error!" +msgstr "" + +#: src/setup/ncurses/wizard_curs.c:101 src/applications/vpn/cs.c:94 +msgid "No" +msgstr "" + +#: src/setup/ncurses/wizard_curs.c:102 src/applications/vpn/cs.c:94 +msgid "Yes" +msgstr "" + +#: src/setup/ncurses/wizard_curs.c:118 src/setup/ncurses/wizard_curs.c:183 +#: src/setup/ncurses/wizard_curs.c:299 src/setup/ncurses/mconf.c:189 +#: src/setup/ncurses/mconf.c:285 src/setup/ncurses/mconf.c:365 +#: src/setup/ncurses/mconf.c:456 +msgid "Internal error! (Choice invalid?)" +msgstr "" + +#: src/setup/ncurses/wizard_curs.c:150 +msgid "Abort" +msgstr "" + +#: src/setup/ncurses/wizard_curs.c:151 +msgid "Ok" +msgstr "" + +#: src/setup/ncurses/wizard_curs.c:218 src/setup/ncurses/wizard_curs.c:284 +#: src/setup/ncurses/wizard_curs.c:425 +msgid "GNUnet configuration" +msgstr "" + +#: src/setup/ncurses/wizard_curs.c:220 +msgid "" +"Welcome to GNUnet!\n" +"\n" +"This assistant will ask you a few basic questions in order to configure " +"GNUnet.\n" +"\n" +"Please visit our homepage at\n" +"\thttp://gnunet.org/\n" +"and join our community at\n" +"\thttp://gnunet.org/drupal/\n" +"\n" +"Have a lot of fun,\n" +"\n" +"the GNUnet team" +msgstr "" + +#: src/setup/ncurses/wizard_curs.c:286 +msgid "" +"Choose the network interface that connects your computer to the internet " +"from the list below." +msgstr "" + +#: src/setup/ncurses/wizard_curs.c:304 src/setup/ncurses/wizard_curs.c:318 +msgid "" +"The \"Network interface\" is the device that connects your computer to the " +"internet. This is usually a modem, an ISDN card or a network card in case " +"you are using DSL." +msgstr "" + +#: src/setup/ncurses/wizard_curs.c:315 +msgid "Network configuration: interface" +msgstr "" + +#: src/setup/ncurses/wizard_curs.c:317 +msgid "" +"What is the name of the network interface that connects your computer to the " +"Internet?" +msgstr "" + +#: src/setup/ncurses/wizard_curs.c:328 +msgid "Network configuration: IP" +msgstr "" + +#: src/setup/ncurses/wizard_curs.c:330 +msgid "What is this computer's public IP address or hostname?" +msgstr "" + +#: src/setup/ncurses/wizard_curs.c:331 +msgid "" +"If your provider always assigns the same IP-Address to you (a \"static\" IP-" +"Address), enter it into the \"IP-Address\" field. If your IP-Address changes " +"every now and then (\"dynamic\" IP-Address) but there's a hostname that " +"always points to your actual IP-Address (\"Dynamic DNS\"), you can also " +"enter it here.\n" +"If left empty, GNUnet will try to automatically detect the IP.\n" +"You can specify a hostname, GNUnet will then use DNS to resolve it.\n" +"If in doubt, leave this empty." +msgstr "" + +#: src/setup/ncurses/wizard_curs.c:346 +msgid "Bandwidth configuration: upload" +msgstr "" + +#: src/setup/ncurses/wizard_curs.c:348 +msgid "How much upstream bandwidth (in bytes/s) may be used?" +msgstr "" + +#: src/setup/ncurses/wizard_curs.c:349 +msgid "" +"You can limit GNUnet's resource usage here.\n" +"\n" +"The \"upstream\" is the data channel through which data is *sent* to the " +"internet. The limit is the maximum amount which GNUnet is allowed to use. If " +"you have a flatrate, you can set it to the maximum speed of your internet " +"connection. You should not use a value that is higher than what your actual " +"connection allows." +msgstr "" + +#: src/setup/ncurses/wizard_curs.c:361 +msgid "Bandwidth configuration: download" +msgstr "" + +#: src/setup/ncurses/wizard_curs.c:363 +msgid "How much downstream bandwidth (in bytes/s) may be used?" +msgstr "" + +#: src/setup/ncurses/wizard_curs.c:364 +msgid "" +"You can limit GNUnet's resource usage here.\n" +"\n" +"The \"downstream\" is the data channel through which data is *received* from " +"the internet. The limit is the maximum amount which GNUnet is allowed to " +"use. If you have a flatrate, you can set it to the maximum speed of your " +"internet connection. You should not use a value that is higher than what " +"your actual connection allows." +msgstr "" + +#: src/setup/ncurses/wizard_curs.c:376 +msgid "Quota configuration" +msgstr "" + +#: src/setup/ncurses/wizard_curs.c:377 +msgid "What is the maximum size of the datastore in MB?" +msgstr "" + +#: src/setup/ncurses/wizard_curs.c:378 +msgid "" +"The GNUnet datastore contains all content that GNUnet needs to store " +"(indexed, inserted and migrated content)." +msgstr "" + +#: src/setup/ncurses/wizard_curs.c:390 +msgid "Daemon configuration: user account" +msgstr "" + +#: src/setup/ncurses/wizard_curs.c:391 +msgid "As which user should gnunetd be run?" +msgstr "" + +#: src/setup/ncurses/wizard_curs.c:393 +msgid "" +"For security reasons, it is a good idea to let this setup create a new user " +"account under which the GNUnet service is started at system startup.\n" +"\n" +"However, GNUnet may not be able to access files other than its own. This " +"includes files you want to publish in GNUnet. You'll have to grant read " +"permissions to the user specified below.\n" +"\n" +"Leave the field empty to run GNUnet with system privileges.\n" +msgstr "" + +#: src/setup/ncurses/wizard_curs.c:410 +msgid "Daemon configuration: group account" +msgstr "" + +#: src/setup/ncurses/wizard_curs.c:411 +msgid "As which group should gnunetd be run?" +msgstr "" + +#: src/setup/ncurses/wizard_curs.c:413 +msgid "" +"For security reasons, it is a good idea to let this setup create a new group " +"for the chosen user account.\n" +"\n" +"You can also specify a already existent group here.\n" +"\n" +"Only members of this group will be allowed to start and stop the the GNUnet " +"server and have access to GNUnet server data.\n" +msgstr "" + +#: src/setup/ncurses/wizard_curs.c:427 +msgid "Do you want to automatically launch GNUnet as a system service?" +msgstr "" + +#: src/setup/ncurses/wizard_curs.c:429 +msgid "" +"If you say \"yes\" here, the GNUnet background process will be automatically " +"started when you turn on your computer. If you say \"no\" here, you have to " +"launch GNUnet yourself each time you want to use it." +msgstr "" + +#: src/setup/ncurses/wizard_curs.c:452 src/setup/gtk/wizard_gtk.c:414 +#, c-format +msgid "Unable to save configuration file `%s':" +msgstr "" + +#: src/setup/ncurses/wizard_curs.c:472 +msgid "Unable to create user account for daemon." +msgstr "" + +#: src/setup/ncurses/wizard_curs.c:483 +msgid "Unable to setup autostart for daemon." +msgstr "" + +#: src/setup/ncurses/wizard_curs.c:498 +msgid "Save configuration?" +msgstr "" + +#: src/setup/ncurses/wizard_curs.c:499 +msgid "Save configuration now?" +msgstr "" + +#: src/setup/ncurses/wizard_curs.c:539 src/setup/ncurses/mconf.c:500 +msgid "GNUnet Configuration" +msgstr "" + +#: src/setup/ncurses/wizard_curs.c:543 +msgid "Back" +msgstr "" + +#: src/setup/ncurses/mconf.c:96 +msgid "Exit" +msgstr "" + +#: src/setup/ncurses/mconf.c:99 +msgid "Up" +msgstr "" + +#: src/setup/ncurses/mconf.c:102 +msgid "Cancel" +msgstr "" + +#: src/setup/ncurses/mconf.c:221 src/setup/ncurses/mconf.c:408 +msgid "Internal error! (Value invalid?)" +msgstr "" + +#: src/setup/ncurses/mconf.c:398 +msgid "Invalid input, expecting floating point value." +msgstr "" + +#: src/setup/ncurses/mconf.c:439 +msgid "Invalid input, expecting integer." +msgstr "" + +#: src/setup/ncurses/mconf.c:446 +msgid "Value is not in legal range." +msgstr "" + +#: src/setup/ncurses/mconf.c:512 src/setup/text/conf.c:569 +#, c-format +msgid "Configuration unchanged, no need to save.\n" +msgstr "" + +#: src/setup/ncurses/mconf.c:518 +msgid "Do you wish to save your new configuration?" +msgstr "" + +#: src/setup/ncurses/mconf.c:532 +#, c-format +msgid "" +"\n" +"End of configuration.\n" +msgstr "" + +#: src/setup/ncurses/mconf.c:537 +#, c-format +msgid "" +"\n" +"Your configuration changes were NOT saved.\n" +msgstr "" + +#: src/setup/gnunet-win-tool.c:52 +msgid "list all network adapters" +msgstr "" + +#: src/setup/gnunet-win-tool.c:55 +msgid "install GNUnet as Windows service" +msgstr "" + +#: src/setup/gnunet-win-tool.c:58 +msgid "uninstall GNUnet service" +msgstr "" + +#: src/setup/gnunet-win-tool.c:61 +msgid "increase the maximum number of TCP/IP connections" +msgstr "" + +#: src/setup/gnunet-win-tool.c:64 +msgid "display a file's hash value" +msgstr "" + +#: src/setup/gnunet-win-tool.c:125 +#, c-format +msgid "GNUnet service installed successfully.\n" +msgstr "" + +#: src/setup/gnunet-win-tool.c:128 src/setup/gnunet-win-tool.c:156 +#, c-format +msgid "This version of Windows doesn't support services.\n" +msgstr "" + +#: src/setup/gnunet-win-tool.c:132 src/setup/gnunet-win-tool.c:160 +#, c-format +msgid "Error: can't open Service Control Manager: %s\n" +msgstr "" + +#: src/setup/gnunet-win-tool.c:137 +#, c-format +msgid "Error: can't create service: %s\n" +msgstr "" + +#: src/setup/gnunet-win-tool.c:140 src/setup/gnunet-win-tool.c:172 +#, c-format +msgid "Unknown error.\n" +msgstr "" + +#: src/setup/gnunet-win-tool.c:153 +#, c-format +msgid "Service deleted.\n" +msgstr "" + +#: src/setup/gnunet-win-tool.c:165 +#, c-format +msgid "Error: can't access service: %s\n" +msgstr "" + +#: src/setup/gnunet-win-tool.c:169 +#, c-format +msgid "Error: can't delete service: %s\n" +msgstr "" + +#: src/setup/gtk/ngconf.c:389 +msgid "Configuration saved." +msgstr "" + +#: src/setup/gtk/ngconf.c:399 +msgid "Failed to save configuration." +msgstr "" + +#: src/setup/gtk/ngconf.c:424 +msgid "Configuration changed. Save?" +msgstr "" + +#: src/setup/gtk/ngconf.c:437 +msgid "Error saving configuration." +msgstr "" + +#: src/setup/gtk/wizard_gtk.c:141 +msgid "(unknown connection)" +msgstr "" + +#: src/setup/gtk/wizard_gtk.c:438 +msgid "Do you want to save the new configuration?" +msgstr "" + +#: src/setup/gtk/wizard_gtk.c:470 +msgid "Unable to create user account:" +msgstr "" + +#: src/setup/gtk/wizard_gtk.c:480 +msgid "Unable to change startup process:" +msgstr "" + +#: src/setup/gtk/wizard_gtk.c:495 +msgid "" +"Running gnunet-update failed.\n" +"This maybe due to insufficient permissions, please check your " +"configuration.\n" +"Finally, run gnunet-update manually." +msgstr "" + +#: src/setup/gnunet-setup.c:65 +#, c-format +msgid "Can only set one option per invocation.\n" +msgstr "" + +#: src/setup/gnunet-setup.c:73 +#, c-format +msgid "" +"Invalid syntax, argument to 'set' must have the format SECTION:" +"OPTION=VALUE.\n" +msgstr "" + +#: src/setup/gnunet-setup.c:87 +#, c-format +msgid "Can only display one option per invocation.\n" +msgstr "" + +#: src/setup/gnunet-setup.c:94 +#, c-format +msgid "" +"Invalid syntax, argument to 'get' must have the format SECTION:OPTION.\n" +msgstr "" + +#: src/setup/gnunet-setup.c:108 +msgid "generate configuration for gnunetd, the GNUnet daemon" +msgstr "" + +#: src/setup/gnunet-setup.c:111 src/setup/gnunet-setup.c:127 +#: src/server/gnunet-update.c:268 +msgid "print a value from the configuration file to stdout" +msgstr "" + +#: src/setup/gnunet-setup.c:113 src/setup/gnunet-setup.c:129 +msgid "Tool to setup GNUnet." +msgstr "" + +#: src/setup/gnunet-setup.c:115 src/setup/gnunet-setup.c:131 +msgid "update a value in the configuration file" +msgstr "" + +#: src/setup/gnunet-setup.c:338 +#, c-format +msgid "Too many arguments.\n" +msgstr "" + +#: src/setup/gnunet-setup.c:344 +msgid "No interface specified, using default.\n" +msgstr "" + +#: src/setup/gnunet-setup.c:392 +#, c-format +msgid "Configuration file `%s' must be a filename (but is a directory).\n" +msgstr "" + +#: src/setup/gnunet-setup.c:439 +#, c-format +msgid "Undefined option.\n" +msgstr "" + +#: src/setup/gnunet-setup.c:496 +#, c-format +msgid "`%s' is not available.\n" +msgstr "" + +#: src/setup/gnunet-setup.c:516 +#, c-format +msgid "Unknown operation '%s'.\n" +msgstr "" + +#: src/setup/gnunet-setup.c:517 src/util/getopt/getopt.c:1072 +#, c-format +msgid "Use --help to get a list of options.\n" +msgstr "" + +#: src/setup/text/conf.c:79 +msgid "yes" +msgstr "" + +#: src/setup/text/conf.c:80 +msgid "no" +msgstr "" + +#: src/setup/text/conf.c:106 +#, c-format +msgid "\tEnter yes (%s), no (%s) or help (%s): " +msgstr "" + +#: src/setup/text/conf.c:115 +msgid "\tPossible choices:\n" +msgstr "" + +#: src/setup/text/conf.c:123 +msgid "\tUse single space prefix to avoid conflicts with hotkeys!\n" +msgstr "" + +#: src/setup/text/conf.c:125 +#, c-format +msgid "\tEnter string (type '%s' for default value `%s'): " +msgstr "" + +#: src/setup/text/conf.c:143 +#, c-format +msgid "\t Enter choice (default is %c): " +msgstr "" + +#: src/setup/text/conf.c:147 +#, c-format +msgid "\tEnter floating point (type '%s' for default value %f): " +msgstr "" + +#: src/setup/text/conf.c:153 +#, c-format +msgid "" +"\tEnter unsigned integer in interval [%llu,%llu] (type '%s' for default " +"value %llu): " +msgstr "" + +#: src/setup/text/conf.c:187 +#, c-format +msgid "Yes\n" +msgstr "" + +#: src/setup/text/conf.c:192 +#, c-format +msgid "No\n" +msgstr "" + +#: src/setup/text/conf.c:195 src/setup/text/conf.c:236 +#: src/setup/text/conf.c:266 src/setup/text/conf.c:329 +#: src/setup/text/conf.c:387 +#, c-format +msgid "Help\n" +msgstr "" + +#: src/setup/text/conf.c:198 src/setup/text/conf.c:213 +#: src/setup/text/conf.c:276 src/setup/text/conf.c:307 +#: src/setup/text/conf.c:365 +#, c-format +msgid "Abort\n" +msgstr "" + +#: src/setup/text/conf.c:354 src/setup/text/conf.c:416 +#, c-format +msgid "" +"\n" +"Invalid entry, try again (use '?' for help): " +msgstr "" + +#: src/setup/text/conf.c:422 +#, c-format +msgid "Unknown kind %x (internal error). Skipping option.\n" +msgstr "" + +#: src/setup/text/conf.c:484 +msgid "\tDescend? (y/n/?) " +msgstr "" + +#: src/setup/text/conf.c:493 +msgid "Aborted.\n" +msgstr "" + +#: src/setup/text/conf.c:506 +msgid "Invalid entry.\n" +msgstr "" + +#: src/setup/text/conf.c:524 +#, c-format +msgid "Unknown kind %x (internal error). Aborting.\n" +msgstr "" + +#: src/setup/text/conf.c:556 +#, c-format +msgid "You can always press ENTER to keep the current value.\n" +msgstr "" + +#: src/setup/text/conf.c:557 +#, c-format +msgid "Use the '%s' key to abort.\n" +msgstr "" + +#: src/setup/text/conf.c:575 +#, c-format +msgid "" +"Save configuration? Answer 'y' for yes, 'n' for no, 'r' to repeat " +"configuration. " +msgstr "" + +#: src/setup/text/conf.c:590 +#, c-format +msgid "Configuration was unchanged, no need to save.\n" +msgstr "" + +#: src/setup/text/conf.c:599 +#, c-format +msgid "Configuration file `%s' written.\n" +msgstr "" + +#: src/setup/lib/tree.c:191 +#, c-format +msgid "" +"Internal error: entry `%s' in section `%s' not found for visibility change!\n" +msgstr "" + +#: src/setup/lib/wizard_util.c:126 +msgid "Can't open Service Control Manager" +msgstr "" + +#: src/setup/lib/wizard_util.c:132 +msgid "Can't create service" +msgstr "" + +#: src/setup/lib/wizard_util.c:136 +msgid "Error changing the permissions of the GNUnet directory" +msgstr "" + +#: src/setup/lib/wizard_util.c:141 +msgid "Cannot write to the registry" +msgstr "" + +#: src/setup/lib/wizard_util.c:144 +msgid "Can't access the service" +msgstr "" + +#: src/setup/lib/wizard_util.c:147 +msgid "Can't delete the service" +msgstr "" + +#: src/setup/lib/wizard_util.c:150 +msgid "Unknown error" +msgstr "" + +#: src/setup/lib/wizard_util.c:186 +msgid "This version of Windows does not support multiple users." +msgstr "" + +#: src/setup/lib/wizard_util.c:190 +msgid "Error creating user" +msgstr "" + +#: src/setup/lib/wizard_util.c:194 +msgid "Error accessing local security policy" +msgstr "" + +#: src/setup/lib/wizard_util.c:199 +msgid "Error granting service right to user" +msgstr "" + +#: src/setup/lib/wizard_util.c:204 +msgid "Unknown error while creating a new user" +msgstr "" + +#: src/setup/lib/gns.c:297 +#, c-format +msgid "" +"Configuration does not satisfy constraints of configuration specification " +"file `%s'!\n" +msgstr "" + +#: src/util/disk/storage.c:172 +#, c-format +msgid "`%s' failed for drive `%s': %u\n" +msgstr "" + +#: src/util/disk/storage.c:524 +#, c-format +msgid "Expected `%s' to be a directory!\n" +msgstr "" + +#: src/util/error/error.c:152 +#, c-format +msgid "Message `%.*s' repeated %u times in the last %llus\n" +msgstr "" + +#: src/util/error/error.c:254 +#, c-format +msgid "" +"\n" +"Press any key to continue\n" +msgstr "" + +#: src/util/error/error.c:336 src/util/error/error.c:371 +msgid "DEBUG" +msgstr "" + +#: src/util/error/error.c:338 src/util/error/error.c:373 +msgid "STATUS" +msgstr "" + +#: src/util/error/error.c:340 src/util/error/error.c:377 +msgid "WARNING" +msgstr "" + +#: src/util/error/error.c:342 src/util/error/error.c:379 +msgid "ERROR" +msgstr "" + +#: src/util/error/error.c:344 src/util/error/error.c:381 +msgid "FATAL" +msgstr "" + +#: src/util/error/error.c:346 src/util/error/error.c:383 +msgid "USER" +msgstr "" + +#: src/util/error/error.c:348 src/util/error/error.c:385 +msgid "ADMIN" +msgstr "" + +#: src/util/error/error.c:350 src/util/error/error.c:387 +msgid "DEVELOPER" +msgstr "" + +#: src/util/error/error.c:352 src/util/error/error.c:389 +msgid "REQUEST" +msgstr "" + +#: src/util/error/error.c:354 src/util/error/error.c:391 +msgid "BULK" +msgstr "" + +#: src/util/error/error.c:356 src/util/error/error.c:393 +msgid "IMMEDIATE" +msgstr "" + +#: src/util/error/error.c:358 +msgid "ALL" +msgstr "" + +#: src/util/error/error.c:375 +msgid "INFO" +msgstr "" + +#: src/util/error/error.c:394 +msgid "NOTHING" +msgstr "" + +#: src/util/network_client/tcpio.c:98 src/util/network_client/tcpio.c:154 +msgid "Could not find valid value for HOST in section NETWORK.\n" +msgstr "" + +#: src/util/network_client/tcpio.c:123 +#, c-format +msgid "Syntax error in configuration entry HOST in section NETWORK: `%s'\n" +msgstr "" + +#: src/util/network_client/tcpio.c:335 +#, c-format +msgid "Error connecting to %s:%u. Is the daemon running?\n" +msgstr "" + +#: src/util/network_client/tcpio.c:398 +#, c-format +msgid "Cannot connect to %s:%u: %s\n" +msgstr "" + +#: src/util/network_client/tcpio.c:636 +msgid "Reading result from gnunetd failed, reply invalid!\n" +msgstr "" + +#: src/util/getopt/setoption.c:59 +#, c-format +msgid "" +"Setting option `%s' in section `%s' to `%s' when processing command line " +"option `%s' was denied.\n" +msgstr "" + +#: src/util/getopt/setoption.c:138 src/util/getopt/setoption.c:155 +#, c-format +msgid "You must pass a number to the `%s' option.\n" +msgstr "" + +#: src/util/getopt/printhelp.c:49 +#, c-format +msgid "" +"Arguments mandatory for long options are also mandatory for short options.\n" +msgstr "" + +#: src/util/getopt/getopt.c:684 +#, c-format +msgid "%s: option `%s' is ambiguous\n" +msgstr "" + +#: src/util/getopt/getopt.c:710 +#, c-format +msgid "%s: option `--%s' does not allow an argument\n" +msgstr "" + +#: src/util/getopt/getopt.c:716 +#, c-format +msgid "%s: option `%c%s' does not allow an argument\n" +msgstr "" + +#: src/util/getopt/getopt.c:737 src/util/getopt/getopt.c:909 +#, c-format +msgid "%s: option `%s' requires an argument\n" +msgstr "" + +#: src/util/getopt/getopt.c:767 +#, c-format +msgid "%s: unrecognized option `--%s'\n" +msgstr "" + +#: src/util/getopt/getopt.c:771 +#, c-format +msgid "%s: unrecognized option `%c%s'\n" +msgstr "" + +#: src/util/getopt/getopt.c:797 +#, c-format +msgid "%s: illegal option -- %c\n" +msgstr "" + +#: src/util/getopt/getopt.c:799 +#, c-format +msgid "%s: invalid option -- %c\n" +msgstr "" + +#: src/util/getopt/getopt.c:828 src/util/getopt/getopt.c:958 +#, c-format +msgid "%s: option requires an argument -- %c\n" +msgstr "" + +#: src/util/getopt/getopt.c:876 +#, c-format +msgid "%s: option `-W %s' is ambiguous\n" +msgstr "" + +#: src/util/getopt/getopt.c:894 +#, c-format +msgid "%s: option `-W %s' does not allow an argument\n" +msgstr "" + +#: src/util/network/ip.c:96 src/util/network/ip.c:160 src/transports/ip.c:260 +#, c-format +msgid "No interface specified in section `%s' under `%s'!\n" +msgstr "" + +#: src/util/network/ip.c:127 src/util/network/ip.c:246 src/transports/ip.c:291 +#, c-format +msgid "Could not obtain IP for interface `%s' using `%s'.\n" +msgstr "" + +#: src/util/network/ip.c:216 +#, c-format +msgid "" +"Could not find interface `%s' using `%s', trying to find another interface.\n" +msgstr "" + +#: src/util/network/ip.c:295 +#, c-format +msgid "Could not find an IP address for interface `%s'.\n" +msgstr "" + +#: src/util/network/ip.c:306 +#, c-format +msgid "" +"There is more than one IP address specified for interface `%s'.\n" +"GNUnet will use %s.\n" +msgstr "" + +#: src/util/network/ip.c:330 +#, c-format +msgid "Could not resolve `%s' to determine our IP address: %s\n" +msgstr "" + +#: src/util/network/ip.c:363 +#, c-format +msgid "GNUnet now uses the IP address %s.\n" +msgstr "" + +#: src/util/network/ipcheck.c:106 src/util/network/ipcheck.c:136 +#: src/util/network/ipcheck.c:186 src/util/network/ipcheck.c:211 +#: src/util/network/ipcheck.c:219 +#, c-format +msgid "Invalid format for IP: `%s'\n" +msgstr "" + +#: src/util/network/ipcheck.c:167 +#, c-format +msgid "Invalid network notation ('/%d' is not legal in IPv4 CIDR)." +msgstr "" + +#: src/util/network/ipcheck.c:269 +#, c-format +msgid "Invalid network notation (does not end with ';': `%s')\n" +msgstr "" + +#: src/util/network/ipcheck.c:306 +#, c-format +msgid "Wrong format `%s' for netmask\n" +msgstr "" + +#: src/util/network/ipcheck.c:338 +#, c-format +msgid "Wrong format `%s' for network\n" +msgstr "" + +#: src/util/network/dns.c:472 +#, c-format +msgid "Could not resolve `%s' (%s): %s\n" +msgstr "" + +#: src/util/network/dns.c:523 src/util/network/dns.c:591 +#, c-format +msgid "Could not find IP of host `%s': %s\n" +msgstr "" + +#: src/util/network/select.c:310 +msgid "Received malformed message (too small) from connection. Closing.\n" +msgstr "" + +#: src/util/network/select.c:495 +#, c-format +msgid "select listen socket for `%s' not valid!\n" +msgstr "" + +#: src/util/config/config.c:296 +#, c-format +msgid "Syntax error in configuration file `%s' at line %d.\n" +msgstr "" + +#: src/util/config/config.c:592 +#, c-format +msgid "" +"Configuration value '%llu' for '%s' in section '%s' is out of legal bounds [%" +"llu,%llu]\n" +msgstr "" + +#: src/util/config/config.c:602 +#, c-format +msgid "Configuration value '%s' for '%s' in section '%s' should be a number\n" +msgstr "" + +#: src/util/config/config.c:688 +#, c-format +msgid "" +"Configuration value '%s' for '%s' in section '%s' is not in set of legal " +"choices\n" +msgstr "" + +#: src/util/crypto/locking_gcrypt.c:80 +#, c-format +msgid "libgcrypt has not the expected version (version %s is required).\n" +msgstr "" + +#: src/util/crypto/symcipher_gcrypt.c:46 src/util/crypto/symcipher_gcrypt.c:53 +#: src/util/crypto/hostkey_gcrypt.c:64 src/util/crypto/hostkey_gcrypt.c:71 +#: src/util/loggers/file.c:271 src/util/loggers/file.c:289 +#: src/applications/sqstore_sqlite/sqlite.c:45 +#: src/applications/sqstore_sqlite/sqlite.c:52 +#: src/applications/kvstore_sqlite/kv_sqlite.c:44 +#: src/applications/kvstore_sqlite/kv_sqlite.c:51 +#: src/applications/kvstore_mysql/kv_mysql.c:44 +#: src/applications/kvstore_mysql/kv_mysql.c:51 +#: src/applications/dstore_sqlite/dstore.c:94 +#: src/applications/dstore_sqlite/dstore.c:101 +#: src/applications/dstore_sqlite/dstore.c:222 +#: src/applications/dstore_sqlite/dstore.c:259 +#: src/applications/dstore_sqlite/dstore.c:285 +#: src/applications/dstore_sqlite/dstore.c:345 +#: src/applications/dstore_sqlite/dstore.c:366 +#: src/applications/dstore_sqlite/dstore.c:378 +#: src/applications/dstore_sqlite/dstore.c:407 +#: src/applications/dstore_sqlite/dstore.c:511 +#: src/applications/dstore_sqlite/dstore.c:555 +#: src/include/gnunet_util_error.h:249 src/include/gnunet_util_error.h:256 +#: src/include/gnunet_util_error.h:263 +#, c-format +msgid "`%s' failed at %s:%d with error: %s\n" +msgstr "" + +#: src/util/crypto/hostkey_gcrypt.c:907 +#, c-format +msgid "RSA signature verification failed at %s:%d: %s\n" +msgstr "" + +#: src/util/os/user.c:108 src/util/os/user.c:125 +#, c-format +msgid "`%s' returned with error code %u" +msgstr "" + +#: src/util/os/user.c:155 src/util/os/user.c:200 +#, c-format +msgid "Cannot obtain information about user `%s': %s\n" +msgstr "" + +#: src/util/os/user.c:156 +msgid "No such user" +msgstr "" + +#: src/util/os/user.c:171 +#, c-format +msgid "Cannot change user/group to `%s': %s\n" +msgstr "" + +#: src/util/os/semaphore.c:227 +#, c-format +msgid "Can't create semaphore: %i" +msgstr "" + +#: src/util/os/cpustatus.c:464 +msgid "Cannot query the CPU usage (Windows NT).\n" +msgstr "" + +#: src/util/os/cpustatus.c:487 +msgid "Cannot query the CPU usage (Win 9x)\n" +msgstr "" + +#: src/util/os/dso.c:59 +#, c-format +msgid "Initialization of plugin mechanism failed: %s!\n" +msgstr "" + +#: src/util/os/dso.c:120 +#, c-format +msgid "`%s' failed for library `%s' with error: %s\n" +msgstr "" + +#: src/util/os/dso.c:162 +#, c-format +msgid "`%s' failed to resolve method '%s' with error: %s\n" +msgstr "" + +#: src/util/os/statuscalls.c:197 src/util/os/statuscalls.c:342 +#, c-format +msgid "Failed to parse interface data from `%s'.\n" +msgstr "" + +#: src/util/os/statuscalls.c:390 src/util/os/statuscalls.c:400 +#, c-format +msgid "" +"No network interfaces defined in configuration section `%s' under `%s'!\n" +msgstr "" + +#: src/util/os/osconfig.c:153 +msgid "Setting open descriptor limit not supported.\n" +msgstr "" + +#: src/util/os/osconfig.c:463 src/util/os/osconfig.c:492 +#, c-format +msgid "Command `%s' failed with error code %u\n" +msgstr "" + +#: src/util/os/priority.c:78 +#, c-format +msgid "Invalid process priority `%s'\n" +msgstr "" + +#: src/util/threads/semaphore.c:168 src/util/threads/pthread.c:157 +#: src/util/threads/mutex.c:146 +#, c-format +msgid "Real-time delay violation (%llu ms) at %s:%u\n" +msgstr "" + +#: src/util/threads/pthread.c:169 src/util/threads/pthread.c:176 +#: src/util/threads/pthread.c:182 src/util/threads/pthread.c:276 +#, c-format +msgid "`%s' failed with error code %s: %s\n" +msgstr "" + +#: src/util/threads/pthread.c:188 src/util/threads/pthread.c:286 +#, c-format +msgid "`%s' failed with error code %d: %s\n" +msgstr "" + +#: src/util/threads/mutex.c:155 src/util/threads/mutex.c:201 +#, c-format +msgid "Invalid argument for `%s'.\n" +msgstr "" + +#: src/util/threads/mutex.c:160 +#, c-format +msgid "Deadlock due to `%s'.\n" +msgstr "" + +#: src/util/threads/mutex.c:187 +#, c-format +msgid "Lock acquired for too long (%llu ms) at %s:%u\n" +msgstr "" + +#: src/util/threads/mutex.c:207 +#, c-format +msgid "Permission denied for `%s'.\n" +msgstr "" + +#: src/util/boot/startup.c:259 +#, c-format +msgid "Failed to run %s: %s %d\n" +msgstr "" + +#: src/util/string/string.c:55 +msgid "ms" +msgstr "" + +#: src/util/string/string.c:61 +msgid "s" +msgstr "" + +#: src/util/string/string.c:65 +msgid "m" +msgstr "" + +#: src/util/string/string.c:69 +msgid "h" +msgstr "" + +#: src/util/string/string.c:73 +msgid " days" +msgstr "" + +#: src/util/string/string.c:89 +msgid "b" +msgstr "" + +#: src/util/string/string.c:95 +msgid "KiB" +msgstr "" + +#: src/util/string/string.c:99 +msgid "MiB" +msgstr "" + +#: src/util/string/string.c:103 +msgid "GiB" +msgstr "" + +#: src/util/string/string.c:107 +msgid "TiB" +msgstr "" + +#: src/util/string/string.c:226 +msgid "Failed to expand `$HOME': environment variable `HOME' not set" +msgstr "" + +#: src/util/loggers/file.c:229 +#, c-format +msgid "Failed to open log-file `%s': %s\n" +msgstr "" + +#: src/util/loggers/file.c:250 +msgid "GNUnet error log" +msgstr "" + +#: src/util/loggers/memory.c:72 +msgid "Out of memory (for logging)\n" +msgstr "" + +#: src/util/pseudonym/names.c:79 +msgid "no-name" +msgstr "" + +#: src/applications/datastore/datastore.c:183 +#: src/applications/datastore/datastore.c:199 +#, c-format +msgid "Availability test failed for `%s' at %s:%d.\n" +msgstr "" + +#: src/applications/datastore/datastore.c:401 +msgid "# requests filtered by bloom filter" +msgstr "" + +#: src/applications/datastore/datastore.c:403 +msgid "# bloom filter false positives" +msgstr "" + +#: src/applications/datastore/datastore.c:406 +msgid "# bytes allowed in datastore" +msgstr "" + +#: src/applications/datastore/datastore.c:423 +msgid "Failed to load state service. Trying to do without.\n" +msgstr "" + +#: src/applications/datastore/datastore.c:529 +#, c-format +msgid "Datastore conversion at approximately %u%%\n" +msgstr "" + +#: src/applications/datastore/datastore.c:576 +#, c-format +msgid "Starting datastore conversion (this may take a while).\n" +msgstr "" + +#: src/applications/datastore/datastore.c:584 +#, c-format +msgid "Completed datastore conversion.\n" +msgstr "" + +#: src/applications/datastore/datastore.c:592 +msgid "Failed to load sqstore service. Check your configuration!\n" +msgstr "" + +#: src/applications/rpc/rpc.c:339 +#, c-format +msgid "" +"%s:%d - RPC %s:%p could not be registered: another callback is already using " +"this name (%p)\n" +msgstr "" + +#: src/applications/rpc/rpc.c:398 +#, c-format +msgid "%s:%d - async RPC %s:%p could not be unregistered: not found\n" +msgstr "" + +#: src/applications/rpc/rpc.c:951 +#, c-format +msgid "`%s' registering handlers %d %d %d\n" +msgstr "" + +#: src/applications/rpc/rpc.c:972 +#, c-format +msgid "Failed to initialize `%s' service.\n" +msgstr "" + +#: src/applications/tbench/tbenchtest.c:53 +#, c-format +msgid "Using %u messages of size %u for %u times.\n" +msgstr "" + +#: src/applications/tbench/tbenchtest.c:85 +#, c-format +msgid "Times: max %16llu min %16llu mean %12.3f variance %12.3f\n" +msgstr "" + +#: src/applications/tbench/tbenchtest.c:89 +#, c-format +msgid "Loss: max %16u min %16u mean %12.3f variance %12.3f\n" +msgstr "" + +#: src/applications/tbench/tbenchtest.c:95 +#, c-format +msgid "" +"\n" +"Failed to receive reply from gnunetd.\n" +msgstr "" + +#: src/applications/tbench/tbenchtest.c:149 +#, c-format +msgid "Running benchmark...\n" +msgstr "" + +#: src/applications/tbench/tbench.c:422 +msgid "allows profiling of direct peer-to-peer connections" +msgstr "" + +#: src/applications/tbench/gnunet-tbench.c:63 +#: src/applications/tracekit/gnunet-tracekit.c:302 +msgid "Start GNUnet transport benchmarking tool." +msgstr "" + +#: src/applications/tbench/gnunet-tbench.c:65 +msgid "output in gnuplot format" +msgstr "" + +#: src/applications/tbench/gnunet-tbench.c:69 +msgid "number of iterations" +msgstr "" + +#: src/applications/tbench/gnunet-tbench.c:73 +msgid "number of messages to use per iteration" +msgstr "" + +#: src/applications/tbench/gnunet-tbench.c:76 +msgid "receiver host identifier (ENC file name)" +msgstr "" + +#: src/applications/tbench/gnunet-tbench.c:79 +msgid "message size" +msgstr "" + +#: src/applications/tbench/gnunet-tbench.c:82 +msgid "sleep for SPACE ms after each a message block" +msgstr "" + +#: src/applications/tbench/gnunet-tbench.c:85 +msgid "time to wait for the completion of an iteration (in ms)" +msgstr "" + +#: src/applications/tbench/gnunet-tbench.c:90 +msgid "number of messages in a message block" +msgstr "" + +#: src/applications/tbench/gnunet-tbench.c:126 +#: src/applications/tracekit/gnunet-tracekit.c:352 +#: src/applications/tracekit/tracekittest.c:133 +#: src/applications/template/gnunet-template.c:95 +#: src/applications/stats/gnunet-stats.c:121 +#: src/applications/vpn/gnunet-vpn.c:154 +#, c-format +msgid "Error establishing connection with gnunetd.\n" +msgstr "" + +#: src/applications/tbench/gnunet-tbench.c:142 +#, c-format +msgid "You must specify a receiver!\n" +msgstr "" + +#: src/applications/tbench/gnunet-tbench.c:152 +#, c-format +msgid "Invalid receiver peer ID specified (`%s' is not valid name).\n" +msgstr "" + +#: src/applications/tbench/gnunet-tbench.c:189 +#, c-format +msgid "Time:\n" +msgstr "" + +#: src/applications/tbench/gnunet-tbench.c:190 +#, c-format +msgid "\tmax %llums\n" +msgstr "" + +#: src/applications/tbench/gnunet-tbench.c:191 +#, c-format +msgid "\tmin %llums\n" +msgstr "" + +#: src/applications/tbench/gnunet-tbench.c:192 +#, c-format +msgid "\tmean %8.4fms\n" +msgstr "" + +#: src/applications/tbench/gnunet-tbench.c:193 +#, c-format +msgid "\tvariance %8.4fms\n" +msgstr "" + +#: src/applications/tbench/gnunet-tbench.c:195 +#, c-format +msgid "Loss:\n" +msgstr "" + +#: src/applications/tbench/gnunet-tbench.c:196 +#, c-format +msgid "\tmax %u\n" +msgstr "" + +#: src/applications/tbench/gnunet-tbench.c:197 +#, c-format +msgid "\tmin %u\n" +msgstr "" + +#: src/applications/tbench/gnunet-tbench.c:198 +#, c-format +msgid "\tmean %8.4f\n" +msgstr "" + +#: src/applications/tbench/gnunet-tbench.c:199 +#, c-format +msgid "\tvariance %8.4f\n" +msgstr "" + +#: src/applications/tbench/gnunet-tbench.c:205 +#, c-format +msgid "Output format not known, this should not happen.\n" +msgstr "" + +#: src/applications/tbench/gnunet-tbench.c:211 +#, c-format +msgid "" +"\n" +"Did not receive the message from gnunetd. Is gnunetd running?\n" +msgstr "" + +#: src/applications/traffic/traffic.c:454 +#, c-format +msgid "# bytes transmitted of type %d" +msgstr "" + +#: src/applications/traffic/traffic.c:470 +#, c-format +msgid "# bytes received of type %d" +msgstr "" + +#: src/applications/traffic/traffic.c:489 +#, c-format +msgid "# bytes received in plaintext of type %d" +msgstr "" + +#: src/applications/traffic/traffic.c:652 +msgid "tracks bandwidth utilization by gnunetd" +msgstr "" + +#: src/applications/sqstore_sqlite/sqlite.c:218 +#, c-format +msgid "Unable to initialize SQLite: %s.\n" +msgstr "" + +#: src/applications/sqstore_sqlite/sqlite.c:434 +#: src/applications/sqstore_sqlite/sqlite.c:469 +#, c-format +msgid "Invalid data in %s. Trying to fix (by deletion).\n" +msgstr "" + +#: src/applications/sqstore_sqlite/sqlite.c:435 +#: src/applications/sqstore_sqlite/sqlite.c:470 +msgid "sqlite datastore" +msgstr "" + +#: src/applications/sqstore_sqlite/sqlite.c:1474 +#: src/applications/sqstore_mysql/mysql.c:1078 +msgid "# bytes in datastore" +msgstr "" + +#: src/applications/sqstore_sqlite/sqlite.c:1476 +msgid "# bytes allocated by SQLite" +msgstr "" + +#: src/applications/sqstore_mysql/mysql.c:1085 +#: src/applications/sqstore_mysql/mysql.c:1160 +msgid "" +"Failed to load MySQL database module. Check that MySQL is running and " +"configured properly!\n" +msgstr "" + +#: src/applications/tracekit/gnunet-tracekit.c:104 +#, c-format +msgid "`%s' connected to `%s'.\n" +msgstr "" + +#: src/applications/tracekit/gnunet-tracekit.c:110 +#: src/applications/tracekit/tracekittest.c:67 +#, c-format +msgid "`%s' is not connected to any peer.\n" +msgstr "" + +#: src/applications/tracekit/gnunet-tracekit.c:295 +msgid "probe network to the given DEPTH" +msgstr "" + +#: src/applications/tracekit/gnunet-tracekit.c:299 +msgid "" +"specify output format; 0 for human readable output, 1 for dot, 2 for vcg" +msgstr "" + +#: src/applications/tracekit/gnunet-tracekit.c:306 +msgid "use PRIORITY for the priority of the trace request" +msgstr "" + +#: src/applications/tracekit/gnunet-tracekit.c:310 +msgid "wait DELAY seconds for replies" +msgstr "" + +#: src/applications/tracekit/gnunet-tracekit.c:344 +#, c-format +msgid "" +"Format specification invalid. Use 0 for user-readable, 1 for dot, 2 for " +"vcg.\n" +msgstr "" + +#: src/applications/tracekit/tracekittest.c:60 +#, c-format +msgid "`%.*s' connected to `%.*s'.\n" +msgstr "" + +#: src/applications/tracekit/tracekit.c:440 +msgid "allows mapping of the network topology" +msgstr "" + +#: src/applications/advertising/advertising_test.c:47 +#: src/applications/hostlist/hostlisttest.c:40 +#: src/applications/session/sessiontest.c:40 +#: src/applications/session/sessiontest_nat_http.c:40 +#: src/applications/session/sessiontest_nat.c:40 +#: src/applications/stats/statistics.c:247 +msgid "# of connected peers" +msgstr "" + +#: src/applications/advertising/advertising.c:194 +#, c-format +msgid "HELLO message from `%s' has an invalid signature. Dropping.\n" +msgstr "" + +#: src/applications/advertising/advertising.c:205 +msgid "HELLO message has expiration too far in the future. Dropping.\n" +msgstr "" + +#: src/applications/advertising/advertising.c:406 +msgid "Could not send HELLO+PING, ping buffer full.\n" +msgstr "" + +#: src/applications/advertising/advertising.c:429 +msgid "Failed to create an advertisement for this peer. Will not send PING.\n" +msgstr "" + +#: src/applications/advertising/advertising.c:581 +#, c-format +msgid "Advertising my transport %d to selected peers.\n" +msgstr "" + +#: src/applications/advertising/advertising.c:590 +msgid "" +"Announcing ourselves pointless: no other peers are known to us so far.\n" +msgstr "" + +#: src/applications/advertising/advertising.c:868 +msgid "# Peer advertisements received" +msgstr "" + +#: src/applications/advertising/advertising.c:871 +msgid "# Peer advertisements of type NAT received" +msgstr "" + +#: src/applications/advertising/advertising.c:874 +msgid "# Peer advertisements confirmed via PONG" +msgstr "" + +#: src/applications/advertising/advertising.c:877 +msgid "# Peer advertisements updating earlier HELLOs" +msgstr "" + +#: src/applications/advertising/advertising.c:880 +msgid "# Peer advertisements discarded due to load" +msgstr "" + +#: src/applications/advertising/advertising.c:883 +msgid "# Peer advertisements for unsupported transport" +msgstr "" + +#: src/applications/advertising/advertising.c:886 +msgid "# Peer advertisements not confirmed due to ping busy" +msgstr "" + +#: src/applications/advertising/advertising.c:889 +msgid "# Peer advertisements not confirmed due to lack of self ad" +msgstr "" + +#: src/applications/advertising/advertising.c:892 +msgid "# Peer advertisements not confirmed due to send error" +msgstr "" + +#: src/applications/advertising/advertising.c:894 +msgid "# Self advertisments transmitted" +msgstr "" + +#: src/applications/advertising/advertising.c:896 +msgid "# Foreign advertisements forwarded" +msgstr "" + +#: src/applications/advertising/advertising.c:898 +#: src/applications/pingpong/pingpong.c:528 +msgid "# plaintext PING messages sent" +msgstr "" + +#: src/applications/advertising/advertising.c:904 +#: src/applications/session/connect.c:932 +#, c-format +msgid "`%s' registering handler %d (plaintext and ciphertext)\n" +msgstr "" + +#: src/applications/advertising/advertising.c:922 +msgid "" +"ensures that this peer is known by other peers and discovers other peers" +msgstr "" + +#: src/applications/fragmentation/fragmentation.c:578 +msgid "# messages defragmented" +msgstr "" + +#: src/applications/fragmentation/fragmentation.c:580 +msgid "# messages fragmented" +msgstr "" + +#: src/applications/fragmentation/fragmentation.c:581 +msgid "# fragments discarded" +msgstr "" + +#: src/applications/fragmentation/fragmentation.c:592 +#, c-format +msgid "`%s' registering handler %d\n" +msgstr "" + +#: src/applications/topology_default/topology.c:466 +#, c-format +msgid "Could not read friends list `%s'\n" +msgstr "" + +#: src/applications/topology_default/topology.c:485 +#, c-format +msgid "Failed to read friends list from `%s'\n" +msgstr "" + +#: src/applications/topology_default/topology.c:505 +msgid "Syntax error in topology specification, skipping bytes.\n" +msgstr "" + +#: src/applications/topology_default/topology.c:523 +#, c-format +msgid "Syntax error in topology specification, skipping bytes `%s'.\n" +msgstr "" + +#: src/applications/topology_default/topology.c:535 +msgid "" +"Fewer friends specified than required by minimum friend count. Will only " +"connect to friends.\n" +msgstr "" + +#: src/applications/topology_default/topology.c:543 +msgid "" +"More friendly connections required than target total number of connections.\n" +msgstr "" + +#: src/applications/topology_default/topology.c:726 +msgid "maintains GNUnet default mesh topology" +msgstr "" + +#: src/applications/chat/tools/gnunet-chat.c:107 +msgid "anonymous" +msgstr "" + +#: src/applications/chat/tools/gnunet-chat.c:113 +#, c-format +msgid "`%s' said: %s\n" +msgstr "" + +#: src/applications/chat/tools/gnunet-chat.c:116 +#: src/applications/chat/tools/gnunet-chat.c:119 +#, c-format +msgid "`%s' said to you: %s\n" +msgstr "" + +#: src/applications/chat/tools/gnunet-chat.c:122 +#, c-format +msgid "`%s' said for sure: %s\n" +msgstr "" + +#: src/applications/chat/tools/gnunet-chat.c:125 +#, c-format +msgid "`%s' said to you for sure: %s\n" +msgstr "" + +#: src/applications/chat/tools/gnunet-chat.c:128 +#, c-format +msgid "`%s' was confirmed that you received: %s\n" +msgstr "" + +#: src/applications/chat/tools/gnunet-chat.c:131 +#, c-format +msgid "`%s' was confirmed that you and only you received: %s\n" +msgstr "" + +#: src/applications/chat/tools/gnunet-chat.c:134 +#, c-format +msgid "`%s' was confirmed that you received from him or her: %s\n" +msgstr "" + +#: src/applications/chat/tools/gnunet-chat.c:139 +#, c-format +msgid "`%s' was confirmed that you and only you received from him or her: %s\n" +msgstr "" + +#: src/applications/chat/tools/gnunet-chat.c:142 +#, c-format +msgid "`%s' said off the record: %s\n" +msgstr "" + +#: src/applications/chat/tools/gnunet-chat.c:145 +#, c-format +msgid "<%s> said using an unknown message type: %s\n" +msgstr "" + +#: src/applications/chat/tools/gnunet-chat.c:167 +#, c-format +msgid "`%s' entered the room\n" +msgstr "" + +#: src/applications/chat/tools/gnunet-chat.c:167 +#, c-format +msgid "`%s' left the room\n" +msgstr "" + +#: src/applications/chat/tools/gnunet-chat.c:239 +#: src/applications/chat/tools/gnunet-chat.c:348 +#, c-format +msgid "Failed to send message.\n" +msgstr "" + +#: src/applications/chat/tools/gnunet-chat.c:265 +#: src/applications/chat/tools/gnunet-chat.c:524 +#, c-format +msgid "Joined room `%s' as user `%s'.\n" +msgstr "" + +#: src/applications/chat/tools/gnunet-chat.c:293 +#, c-format +msgid "Changed username to `%s'.\n" +msgstr "" + +#: src/applications/chat/tools/gnunet-chat.c:301 +#, c-format +msgid "Unknown command `%s'.\n" +msgstr "" + +#: src/applications/chat/tools/gnunet-chat.c:316 +#, c-format +msgid "Syntax: /msg USERNAME MESSAGE" +msgstr "" + +#: src/applications/chat/tools/gnunet-chat.c:324 +#, c-format +msgid "Unknown user `%s'\n" +msgstr "" + +#: src/applications/chat/tools/gnunet-chat.c:339 +#, c-format +msgid "User `%s' is currently not in the room!\n" +msgstr "" + +#: src/applications/chat/tools/gnunet-chat.c:361 +#, c-format +msgid "Users in room `%s': " +msgstr "" + +#: src/applications/chat/tools/gnunet-chat.c:390 +msgid "" +"Use `/join #roomname' to join a chat room. Joining a room will cause you to " +"leave the current room" +msgstr "" + +#: src/applications/chat/tools/gnunet-chat.c:393 +msgid "" +"Use `/nick nickname' to change your nickname. This will cause you to leave " +"the current room and immediately rejoin it with the new name." +msgstr "" + +#: src/applications/chat/tools/gnunet-chat.c:396 +msgid "" +"Use `/msg nickname message' to send a private message to the specified user" +msgstr "" + +#: src/applications/chat/tools/gnunet-chat.c:398 +msgid "The `/notice' command is an alias for `/msg'" +msgstr "" + +#: src/applications/chat/tools/gnunet-chat.c:400 +msgid "The `/query' command is an alias for `/msg'" +msgstr "" + +#: src/applications/chat/tools/gnunet-chat.c:402 +msgid "Use `/quit' to terminate gnunet-chat" +msgstr "" + +#: src/applications/chat/tools/gnunet-chat.c:404 +msgid "The `/leave' command is an alias for `/quit'" +msgstr "" + +#: src/applications/chat/tools/gnunet-chat.c:407 +msgid "Use `/names' to list all of the current members in the chat room" +msgstr "" + +#: src/applications/chat/tools/gnunet-chat.c:409 +msgid "Use `/help command' to get help for a specific command" +msgstr "" + +#: src/applications/chat/tools/gnunet-chat.c:457 +msgid "Join a chat on GNUnet." +msgstr "" + +#: src/applications/chat/tools/gnunet-chat.c:461 +msgid "set the nickname to use (required)" +msgstr "" + +#: src/applications/chat/tools/gnunet-chat.c:464 +msgid "set the chat room to join" +msgstr "" + +#: src/applications/chat/tools/gnunet-chat.c:495 +#, c-format +msgid "You must specify a nickname\n" +msgstr "" + +#: src/applications/chat/tools/gnunet-chat.c:515 +#, c-format +msgid "Failed to join room `%s'\n" +msgstr "" + +#: src/applications/chat/module/chat.c:325 +#, c-format +msgid "`%s' registering CS handlers %d and %d\n" +msgstr "" + +#: src/applications/chat/module/chat.c:347 +msgid "enables P2P-chat (incomplete)" +msgstr "" + +#: src/applications/chat/lib/messaging.c:353 +#: src/applications/identity/hostkey.c:122 +#, c-format +msgid "Failed to access GNUnet home directory `%s'\n" +msgstr "" + +#: src/applications/chat/lib/messaging.c:389 +#, c-format +msgid "Existing key in file `%s' failed format check, creating new key.\n" +msgstr "" + +#: src/applications/chat/lib/messaging.c:399 +msgid "Creating new key for this nickname (this may take a while).\n" +msgstr "" + +#: src/applications/chat/lib/messaging.c:411 +msgid "Done creating key.\n" +msgstr "" + +#: src/applications/dstore_mysql/dstore_mysql.c:471 +msgid "Failed to initialize MySQL database connection for dstore.\n" +msgstr "" + +#: src/applications/dstore_mysql/dstore_mysql.c:494 +#: src/applications/dstore_sqlite/dstore.c:636 +msgid "# bytes in dstore" +msgstr "" + +#: src/applications/dstore_mysql/dstore_mysql.c:496 +#: src/applications/dstore_sqlite/dstore.c:638 +msgid "# max bytes allowed in dstore" +msgstr "" + +#: src/applications/transport/transport.c:191 +#, c-format +msgid "" +"Converting peer address to string failed, transport type %d not supported\n" +msgstr "" + +#: src/applications/transport/transport.c:246 +#, c-format +msgid "Transport connection attempt failed, transport type %d not supported\n" +msgstr "" + +#: src/applications/transport/transport.c:299 +#, c-format +msgid "" +"Transport failed to connect to peer `%s' (%u HELLOs known, none worked)\n" +msgstr "" + +#: src/applications/transport/transport.c:376 +#, c-format +msgid "Transmission attempt failed, transport type %d unknown.\n" +msgstr "" + +#: src/applications/transport/transport.c:500 +#, c-format +msgid "No transport of type %d known.\n" +msgstr "" + +#: src/applications/transport/transport.c:560 +msgid "No transport succeeded in creating a hello!\n" +msgstr "" + +#: src/applications/transport/transport.c:761 +#, c-format +msgid "Loading transports `%s'\n" +msgstr "" + +#: src/applications/transport/transport.c:781 +#, c-format +msgid "Could not load transport plugin `%s'\n" +msgstr "" + +#: src/applications/transport/transport.c:795 +#, c-format +msgid "Transport library `%s' did not provide required function '%s%s'.\n" +msgstr "" + +#: src/applications/transport/transport.c:824 +#, c-format +msgid "Loaded transport `%s'\n" +msgstr "" + +#: src/applications/transport/transport.c:836 +#: src/server/gnunet-peer-info.c:252 +#, c-format +msgid "I am peer `%s'.\n" +msgstr "" + +#: src/applications/dht/tools/dht_multipeer_test.c:80 +#: src/applications/dht/tools/dht_twopeer_test.c:47 +#: src/applications/dht/module/table.c:783 +msgid "# dht connections" +msgstr "" + +#: src/applications/dht/tools/dht-query.c:54 +msgid "Query (get KEY, put KEY VALUE) DHT table." +msgstr "" + +#: src/applications/dht/tools/dht-query.c:58 +msgid "allow TIME ms to process a GET command" +msgstr "" + +#: src/applications/dht/tools/dht-query.c:107 +#, c-format +msgid "Issuing `%s(%s,%s)' command.\n" +msgstr "" + +#: src/applications/dht/tools/dht-query.c:142 +#: src/applications/fs/tools/gnunet-auto-share.c:669 +#, c-format +msgid "Failed to connect to gnunetd.\n" +msgstr "" + +#: src/applications/dht/tools/dht-query.c:155 +#, c-format +msgid "Command `%s' requires an argument (`%s').\n" +msgstr "" + +#: src/applications/dht/tools/dht-query.c:172 +#, c-format +msgid "Command `%s' requires two arguments (`%s' and `%s').\n" +msgstr "" + +#: src/applications/dht/tools/dht-query.c:183 +#, c-format +msgid "Unsupported command `%s'. Aborting.\n" +msgstr "" + +#: src/applications/dht/module/table.c:785 +msgid "# dht discovery messages received" +msgstr "" + +#: src/applications/dht/module/table.c:787 +msgid "# dht route host lookups performed" +msgstr "" + +#: src/applications/dht/module/table.c:789 +msgid "# dht discovery messages sent" +msgstr "" + +#: src/applications/dht/module/routing.c:879 +msgid "# dht replies routed" +msgstr "" + +#: src/applications/dht/module/routing.c:881 +msgid "# dht requests routed" +msgstr "" + +#: src/applications/dht/module/routing.c:883 +msgid "# dht get requests received" +msgstr "" + +#: src/applications/dht/module/routing.c:885 +msgid "# dht put requests received" +msgstr "" + +#: src/applications/dht/module/routing.c:887 +msgid "# dht results received" +msgstr "" + +#: src/applications/dht/module/routing.c:892 +#, c-format +msgid "`%s' registering p2p handlers: %d %d %d\n" +msgstr "" + +#: src/applications/dht/module/cs.c:122 +#, c-format +msgid "`%s' failed. Terminating connection to client.\n" +msgstr "" + +#: src/applications/dht/module/cs.c:250 +#, c-format +msgid "`%s' registering client handlers: %d %d\n" +msgstr "" + +#: src/applications/dht/module/cs.c:273 +msgid "Enables efficient non-anonymous routing" +msgstr "" + +#: src/applications/identity/hostkey.c:155 +#, c-format +msgid "" +"Existing hostkey in file `%s' failed format check, creating new hostkey.\n" +msgstr "" + +#: src/applications/identity/hostkey.c:164 +msgid "Creating new hostkey (this may take a while).\n" +msgstr "" + +#: src/applications/identity/hostkey.c:176 +msgid "Done creating hostkey.\n" +msgstr "" + +#: src/applications/identity/identity.c:333 +#, c-format +msgid "" +"File `%s' in directory `%s' does not match naming convention. Removed.\n" +msgstr "" + +#: src/applications/identity/identity.c:408 +#, c-format +msgid "Still no peers found in `%s'!\n" +msgstr "" + +#: src/applications/identity/identity.c:731 +#: src/applications/identity/identity.c:757 +#, c-format +msgid "Removed file `%s' containing invalid HELLO data.\n" +msgstr "" + +#: src/applications/identity/identity.c:809 +#, c-format +msgid "Signature failed verification: peer `%s' not known.\n" +msgstr "" + +#: src/applications/identity/identity.c:819 +msgid "Signature failed verification: signature invalid.\n" +msgstr "" + +#: src/applications/identity/identity.c:935 +#: src/applications/identity/identity.c:1058 +#, c-format +msgid "Peer `%s' is currently strictly blacklisted (for another %llums).\n" +msgstr "" + +#: src/applications/identity/identity.c:1061 +#, c-format +msgid "Peer `%s' is currently blacklisted (for another %llums).\n" +msgstr "" + +#: src/applications/pingpong/pingpong.c:134 +#: src/applications/pingpong/pingpong.c:203 +#: src/applications/pingpong/pingpong.c:273 +#: src/applications/pingpong/pingpong.c:345 +#, c-format +msgid "Received malformed `%s' message. Dropping.\n" +msgstr "" + +#: src/applications/pingpong/pingpong.c:146 +msgid "Received ping for another peer. Dropping.\n" +msgstr "" + +#: src/applications/pingpong/pingpong.c:215 +#, c-format +msgid "Received PING from `%s' not destined for us!\n" +msgstr "" + +#: src/applications/pingpong/pingpong.c:315 +#: src/applications/pingpong/pingpong.c:381 +msgid "" +"Could not match PONG against any PING. Try increasing MAX_PING_PONG " +"constant.\n" +msgstr "" + +#: src/applications/pingpong/pingpong.c:425 +msgid "Cannot create PING, table full. Try increasing MAX_PING_PONG.\n" +msgstr "" + +#: src/applications/pingpong/pingpong.c:518 +msgid "# encrypted PONG messages received" +msgstr "" + +#: src/applications/pingpong/pingpong.c:520 +msgid "# plaintext PONG messages received" +msgstr "" + +#: src/applications/pingpong/pingpong.c:522 +msgid "# encrypted PING messages received" +msgstr "" + +#: src/applications/pingpong/pingpong.c:524 +msgid "# PING messages created" +msgstr "" + +#: src/applications/pingpong/pingpong.c:526 +#: src/applications/session/connect.c:926 +msgid "# encrypted PONG messages sent" +msgstr "" + +#: src/applications/pingpong/pingpong.c:530 +#: src/applications/session/connect.c:924 +msgid "# encrypted PING messages sent" +msgstr "" + +#: src/applications/pingpong/pingpong.c:532 +msgid "# plaintext PONG messages sent" +msgstr "" + +#: src/applications/pingpong/pingpong.c:536 +msgid "# plaintext PONG transmissions failed" +msgstr "" + +#: src/applications/pingpong/pingpong.c:546 +#, c-format +msgid "`%s' registering handlers %d %d (plaintext and ciphertext)\n" +msgstr "" + +#: src/applications/hostlist/hostlist.c:165 +msgid "# hostlist requests received" +msgstr "" + +#: src/applications/hostlist/hostlist.c:167 +msgid "# hostlist HELLOs returned" +msgstr "" + +#: src/applications/hostlist/hostlist.c:169 +msgid "# hostlist bytes returned" +msgstr "" + +#: src/applications/hostlist/hostlist.c:199 +msgid "integrated HTTP hostlist server" +msgstr "" + +#: src/applications/session/connect.c:238 +#, c-format +msgid "Session key from peer `%s' could not be verified.\n" +msgstr "" + +#: src/applications/session/connect.c:282 +#, c-format +msgid "Cannot encrypt sessionkey, peer `%s' not known!\n" +msgstr "" + +#: src/applications/session/connect.c:489 +#, c-format +msgid "Could not create any HELLO for myself (have transports `%s')!\n" +msgstr "" + +#: src/applications/session/connect.c:599 +#, c-format +msgid "Session key received from peer `%s' has invalid format (discarded).\n" +msgstr "" + +#: src/applications/session/connect.c:632 +#, c-format +msgid "Session key received from peer `%s' is for `%s' and not for me!\n" +msgstr "" + +#: src/applications/session/connect.c:659 +#, c-format +msgid "Invalid `%s' message received from peer `%s'.\n" +msgstr "" + +#: src/applications/session/connect.c:670 +#, c-format +msgid "setkey `%s' from `%s' fails CRC check (have: %u, want %u).\n" +msgstr "" + +#: src/applications/session/connect.c:728 +#, c-format +msgid "" +"Error parsing encrypted session key from `%s', given message part size is " +"invalid.\n" +msgstr "" + +#: src/applications/session/connect.c:741 +#, c-format +msgid "Unknown type in embedded message from `%s': %u (size: %u)\n" +msgstr "" + +#: src/applications/session/connect.c:916 +msgid "# session keys sent" +msgstr "" + +#: src/applications/session/connect.c:918 +msgid "# session keys rejected" +msgstr "" + +#: src/applications/session/connect.c:920 +msgid "# session keys accepted" +msgstr "" + +#: src/applications/session/connect.c:922 +msgid "# sessions established" +msgstr "" + +#: src/applications/fs/tools/gnunet-pseudonym.c:70 +#: src/applications/fs/tools/gnunet-auto-share.c:199 +#: src/applications/fs/tools/gnunet-insert.c:246 +#: src/applications/fs/tools/gnunet-search.c:125 +#: src/applications/fs/tools/gnunet-download.c:77 +msgid "set the desired LEVEL of sender-anonymity" +msgstr "" + +#: src/applications/fs/tools/gnunet-pseudonym.c:73 +msgid "automate creation of a namespace by starting a collection" +msgstr "" + +#: src/applications/fs/tools/gnunet-pseudonym.c:77 +msgid "create a new pseudonym under the given NICKNAME" +msgstr "" + +#: src/applications/fs/tools/gnunet-pseudonym.c:80 +msgid "delete the pseudonym with the given NICKNAME" +msgstr "" + +#: src/applications/fs/tools/gnunet-pseudonym.c:83 +msgid "end automated building of a namespace (ends collection)" +msgstr "" + +#: src/applications/fs/tools/gnunet-pseudonym.c:85 +msgid "Create new pseudonyms, delete pseudonyms or list existing pseudonyms." +msgstr "" + +#: src/applications/fs/tools/gnunet-pseudonym.c:89 +msgid "" +"use the given keyword to advertise the namespace (use when creating a new " +"pseudonym)" +msgstr "" + +#: src/applications/fs/tools/gnunet-pseudonym.c:92 +msgid "specify metadata describing the namespace or collection" +msgstr "" + +#: src/applications/fs/tools/gnunet-pseudonym.c:96 +msgid "" +"do not generate an advertisement for this namespace (use when creating a new " +"pseudonym)" +msgstr "" + +#: src/applications/fs/tools/gnunet-pseudonym.c:99 +msgid "do not list the pseudonyms from the pseudonym database" +msgstr "" + +#: src/applications/fs/tools/gnunet-pseudonym.c:103 +msgid "" +"specify IDENTIFIER to be the address of the entrypoint to content in the " +"namespace (use when creating a new pseudonym)" +msgstr "" + +#: src/applications/fs/tools/gnunet-pseudonym.c:106 +msgid "set the rating of a namespace" +msgstr "" + +#: src/applications/fs/tools/gnunet-pseudonym.c:141 +#, c-format +msgid "Namespace `%s' has rating %d.\n" +msgstr "" + +#: src/applications/fs/tools/gnunet-pseudonym.c:143 +#, c-format +msgid "Namespace `%s' (%s) has rating %d.\n" +msgstr "" + +#: src/applications/fs/tools/gnunet-pseudonym.c:175 +#, c-format +msgid "\tRating (after update): %d\n" +msgstr "" + +#: src/applications/fs/tools/gnunet-pseudonym.c:179 +#: src/applications/fs/tools/gnunet-pseudonym.c:241 +#: src/applications/fs/tools/gnunet-insert.c:101 +#, c-format +msgid "\tUnknown namespace `%s'\n" +msgstr "" + +#: src/applications/fs/tools/gnunet-pseudonym.c:217 +#, c-format +msgid "Collection stopped.\n" +msgstr "" + +#: src/applications/fs/tools/gnunet-pseudonym.c:219 +#, c-format +msgid "Failed to stop collection (not active?).\n" +msgstr "" + +#: src/applications/fs/tools/gnunet-pseudonym.c:230 +#, c-format +msgid "Pseudonym `%s' deleted.\n" +msgstr "" + +#: src/applications/fs/tools/gnunet-pseudonym.c:235 +#, c-format +msgid "Error deleting pseudonym `%s' (does not exist?).\n" +msgstr "" + +#: src/applications/fs/tools/gnunet-pseudonym.c:256 +msgid "Started collection.\n" +msgstr "" + +#: src/applications/fs/tools/gnunet-pseudonym.c:260 +msgid "Failed to start collection.\n" +msgstr "" + +#: src/applications/fs/tools/gnunet-pseudonym.c:296 +msgid "Could not create namespace.\n" +msgstr "" + +#: src/applications/fs/tools/gnunet-pseudonym.c:304 +#, c-format +msgid "Namespace `%s' created (root: %s).\n" +msgstr "" + +#: src/applications/fs/tools/gnunet-pseudonym.c:321 +#, c-format +msgid "You must specify a name for the collection (`%s' option).\n" +msgstr "" + +#: src/applications/fs/tools/gnunet-pseudonym.c:329 +#, c-format +msgid "Could not access namespace information.\n" +msgstr "" + +#: src/applications/fs/tools/gnunet-directory.c:84 +#, c-format +msgid "==> Directory `%s':\n" +msgstr "" + +#: src/applications/fs/tools/gnunet-directory.c:88 +#, c-format +msgid "=\tError reading directory.\n" +msgstr "" + +#: src/applications/fs/tools/gnunet-directory.c:118 +#, c-format +msgid "File format error (not a GNUnet directory?)\n" +msgstr "" + +#: src/applications/fs/tools/gnunet-directory.c:120 +#, c-format +msgid "%d files found in directory.\n" +msgstr "" + +#: src/applications/fs/tools/gnunet-directory.c:135 +msgid "Perform directory related operations." +msgstr "" + +#: src/applications/fs/tools/gnunet-directory.c:138 +msgid "remove all entries from the directory database and stop tracking URIs" +msgstr "" + +#: src/applications/fs/tools/gnunet-directory.c:142 +msgid "list entries from the directory database" +msgstr "" + +#: src/applications/fs/tools/gnunet-directory.c:145 +msgid "start tracking entries for the directory database" +msgstr "" + +#: src/applications/fs/tools/gnunet-directory.c:168 +#, c-format +msgid "Listed %d matching entries.\n" +msgstr "" + +#: src/applications/fs/tools/gnunet-auto-share.c:117 +#, c-format +msgid "Upload of `%s' at %llu out of %llu bytes.\n" +msgstr "" + +#: src/applications/fs/tools/gnunet-auto-share.c:130 +#, c-format +msgid "Upload of `%s' complete, URI is `%s'.\n" +msgstr "" + +#: src/applications/fs/tools/gnunet-auto-share.c:139 +#, c-format +msgid "Upload aborted.\n" +msgstr "" + +#: src/applications/fs/tools/gnunet-auto-share.c:145 +#, c-format +msgid "Error uploading file: %s\n" +msgstr "" + +#: src/applications/fs/tools/gnunet-auto-share.c:154 +#, c-format +msgid "Starting upload of `%s'.\n" +msgstr "" + +#: src/applications/fs/tools/gnunet-auto-share.c:164 +#, c-format +msgid "Uploading suspended.\n" +msgstr "" + +#: src/applications/fs/tools/gnunet-auto-share.c:179 +#, c-format +msgid "Uploading `%s' resumed.\n" +msgstr "" + +#: src/applications/fs/tools/gnunet-auto-share.c:186 +#, c-format +msgid "Unexpected event: %d\n" +msgstr "" + +#: src/applications/fs/tools/gnunet-auto-share.c:205 +msgid "" +"run in debug mode; gnunet-auto-share will not daemonize and error messages " +"will be written to stderr instead of a logfile" +msgstr "" + +#: src/applications/fs/tools/gnunet-auto-share.c:211 +#: src/applications/fs/tools/gnunet-insert.c:259 +msgid "" +"do not use libextractor to add additional references to directory entries " +"and/or the published file" +msgstr "" + +#: src/applications/fs/tools/gnunet-auto-share.c:213 +msgid "Automatically share a directory." +msgstr "" + +#: src/applications/fs/tools/gnunet-auto-share.c:216 +#: src/applications/fs/tools/gnunet-insert.c:273 +msgid "" +"add an additional keyword for all files and directories (this option can be " +"specified multiple times)" +msgstr "" + +#: src/applications/fs/tools/gnunet-auto-share.c:221 +#: src/applications/fs/tools/gnunet-insert.c:290 +msgid "specify the priority of the content" +msgstr "" + +#: src/applications/fs/tools/gnunet-auto-share.c:468 +#: src/applications/fs/tools/gnunet-auto-share.c:903 +#, c-format +msgid "Could not access `%s': %s\n" +msgstr "" + +#: src/applications/fs/tools/gnunet-auto-share.c:547 +#, c-format +msgid "Unknown keyword type `%s' in metadata configuration\n" +msgstr "" + +#: src/applications/fs/tools/gnunet-auto-share.c:652 +#, c-format +msgid "Failed to stop running gnunet-auto-share.\n" +msgstr "" + +#: src/applications/fs/tools/gnunet-auto-share.c:918 +#, c-format +msgid "Directory `%s' is already on the list of shared directories.\n" +msgstr "" + +#: src/applications/fs/tools/gnunet-auto-share.c:939 +msgid "" +"The specified directories were added to the list of shared directories.\n" +msgstr "" + +#: src/applications/fs/tools/gnunet-auto-share.c:961 +#, c-format +msgid "Could not open logfile `%s': %s\n" +msgstr "" + +#: src/applications/fs/tools/gnunet-insert.c:115 +#, c-format +msgid "Created entry `%s' in namespace `%s'\n" +msgstr "" + +#: src/applications/fs/tools/gnunet-insert.c:120 +#, c-format +msgid "Failed to add entry to namespace `%s' (does it exist?)\n" +msgstr "" + +#: src/applications/fs/tools/gnunet-insert.c:135 +#, c-format +msgid "Keywords for file `%s':\n" +msgstr "" + +#: src/applications/fs/tools/gnunet-insert.c:144 +msgid "filename" +msgstr "" + +#: src/applications/fs/tools/gnunet-insert.c:146 +msgid "mimetype" +msgstr "" + +#: src/applications/fs/tools/gnunet-insert.c:186 +#, c-format +msgid "%16llu of %16llu bytes inserted (estimating %6s to completion) - %s\n" +msgstr "" + +#: src/applications/fs/tools/gnunet-insert.c:198 +#, c-format +msgid "Upload of `%s' complete, %llu bytes took %llu seconds (%8.3f KiB/s).\n" +msgstr "" + +#: src/applications/fs/tools/gnunet-insert.c:209 +#, c-format +msgid "File `%s' has URI: %s\n" +msgstr "" + +#: src/applications/fs/tools/gnunet-insert.c:220 +#, c-format +msgid "" +"\n" +"Upload aborted.\n" +msgstr "" + +#: src/applications/fs/tools/gnunet-insert.c:225 +#, c-format +msgid "" +"\n" +"Error uploading file: %s" +msgstr "" + +#: src/applications/fs/tools/gnunet-insert.c:234 +#, c-format +msgid "" +"\n" +"Unexpected event: %d\n" +msgstr "" + +#: src/applications/fs/tools/gnunet-insert.c:250 +msgid "" +"even if gnunetd is running on the local machine, force the creation of a " +"copy instead of making a link to the GNUnet share directory" +msgstr "" + +#: src/applications/fs/tools/gnunet-insert.c:255 +msgid "disable adding the creation time to the metadata of the uploaded file" +msgstr "" + +#: src/applications/fs/tools/gnunet-insert.c:263 +msgid "" +"print list of extracted keywords that would be used, but do not perform " +"upload" +msgstr "" + +#: src/applications/fs/tools/gnunet-insert.c:265 +msgid "Make files available to GNUnet for sharing." +msgstr "" + +#: src/applications/fs/tools/gnunet-insert.c:269 +msgid "" +"add an additional keyword for the top-level file or directory (this option " +"can be specified multiple times)" +msgstr "" + +#: src/applications/fs/tools/gnunet-insert.c:278 +msgid "set the meta-data for the given TYPE to the given VALUE" +msgstr "" + +#: src/applications/fs/tools/gnunet-insert.c:281 +msgid "" +"do not index, perform full insertion (stores entire file in encrypted form " +"in GNUnet database)" +msgstr "" + +#: src/applications/fs/tools/gnunet-insert.c:286 +msgid "" +"specify ID of an updated version to be published in the future (for " +"namespace insertions only)" +msgstr "" + +#: src/applications/fs/tools/gnunet-insert.c:294 +msgid "publish the files under the pseudonym NAME (place file into namespace)" +msgstr "" + +#: src/applications/fs/tools/gnunet-insert.c:297 +msgid "" +"only simulte the process but do not do any actual publishing (useful to " +"compute URIs)" +msgstr "" + +#: src/applications/fs/tools/gnunet-insert.c:301 +msgid "" +"set the ID of this version of the publication (for namespace insertions only)" +msgstr "" + +#: src/applications/fs/tools/gnunet-insert.c:305 +msgid "" +"URI to be published (can be used instead of passing a file to add keywords " +"to the file with the respective URI)" +msgstr "" + +#: src/applications/fs/tools/gnunet-insert.c:342 +#, c-format +msgid "You must specify one and only one filename for insertion.\n" +msgstr "" + +#: src/applications/fs/tools/gnunet-insert.c:348 +#, c-format +msgid "You must NOT specify an URI and a filename.\n" +msgstr "" + +#: src/applications/fs/tools/gnunet-insert.c:354 +#, c-format +msgid "Cannot extract metadata from a URI!\n" +msgstr "" + +#: src/applications/fs/tools/gnunet-insert.c:400 +#, c-format +msgid "Could not access namespace `%s' (does not exist?).\n" +msgstr "" + +#: src/applications/fs/tools/gnunet-insert.c:408 +#, c-format +msgid "Option `%s' is required when using option `%s'.\n" +msgstr "" + +#: src/applications/fs/tools/gnunet-insert.c:419 +#: src/applications/fs/tools/gnunet-insert.c:427 +#, c-format +msgid "Option `%s' makes no sense without option `%s'.\n" +msgstr "" + +#: src/applications/fs/tools/gnunet-search.c:128 +msgid "Search GNUnet for files." +msgstr "" + +#: src/applications/fs/tools/gnunet-search.c:132 +msgid "write encountered (decrypted) search results to FILENAME" +msgstr "" + +#: src/applications/fs/tools/gnunet-search.c:169 +#, c-format +msgid "Error converting arguments to URI!\n" +msgstr "" + +#: src/applications/fs/tools/gnunet-unindex.c:61 +#, c-format +msgid "" +"%16llu of %16llu bytes unindexed (estimating %llu seconds to " +"completion) " +msgstr "" + +#: src/applications/fs/tools/gnunet-unindex.c:73 +#, c-format +msgid "" +"\n" +"Unindexing of `%s' complete, %llu bytes took %llu seconds (%8.3f KiB/s).\n" +msgstr "" + +#: src/applications/fs/tools/gnunet-unindex.c:88 +#, c-format +msgid "" +"\n" +"Error unindexing file: %s\n" +msgstr "" + +#: src/applications/fs/tools/gnunet-unindex.c:108 +msgid "Unindex files." +msgstr "" + +#: src/applications/fs/tools/gnunet-unindex.c:145 +msgid "Not enough arguments. You must specify a filename.\n" +msgstr "" + +#: src/applications/fs/tools/gnunet-unindex.c:163 +#, c-format +msgid "`%s' failed. Is `%s' a file?\n" +msgstr "" + +#: src/applications/fs/tools/gnunet-download.c:82 +msgid "" +"download a GNUnet directory that has already been downloaded. Requires that " +"a filename of an existing file is specified instead of the URI. The " +"download will only download the top-level files in the directory unless the " +"`-R' option is also specified." +msgstr "" + +#: src/applications/fs/tools/gnunet-download.c:85 +msgid "delete incomplete downloads (when aborted with CTRL-C)" +msgstr "" + +#: src/applications/fs/tools/gnunet-download.c:87 +msgid "Download files from GNUnet." +msgstr "" + +#: src/applications/fs/tools/gnunet-download.c:91 +msgid "write the file to FILENAME" +msgstr "" + +#: src/applications/fs/tools/gnunet-download.c:95 +msgid "set the maximum number of parallel downloads that are allowed" +msgstr "" + +#: src/applications/fs/tools/gnunet-download.c:98 +msgid "download a GNUnet directory recursively" +msgstr "" + +#: src/applications/fs/tools/gnunet-download.c:119 +#, c-format +msgid "Download of file `%s' at %16llu out of %16llu bytes (%8.3f KiB/s)\n" +msgstr "" + +#: src/applications/fs/tools/gnunet-download.c:133 +#, c-format +msgid "Download aborted.\n" +msgstr "" + +#: src/applications/fs/tools/gnunet-download.c:139 +#, c-format +msgid "Error downloading: %s\n" +msgstr "" + +#: src/applications/fs/tools/gnunet-download.c:145 +#, c-format +msgid "Download of file `%s' complete. Speed was %8.3f KiB per second.\n" +msgstr "" + +#: src/applications/fs/tools/gnunet-download.c:191 +msgid "no name given" +msgstr "" + +#: src/applications/fs/tools/gnunet-download.c:197 +#, c-format +msgid "Starting download `%s'\n" +msgstr "" + +#: src/applications/fs/tools/gnunet-download.c:239 +msgid "Not enough arguments. You must specify a GNUnet file URI\n" +msgstr "" + +#: src/applications/fs/tools/gnunet-download.c:257 +#, c-format +msgid "URI `%s' invalid for gnunet-download.\n" +msgstr "" + +#: src/applications/fs/tools/gnunet-download.c:300 +#, c-format +msgid "No filename specified, using `%s' instead (for now).\n" +msgstr "" + +#: src/applications/fs/tools/gnunet-download.c:342 +#, c-format +msgid "Could not access gnunet-directory file `%s'\n" +msgstr "" + +#: src/applications/fs/tools/gnunet-download.c:363 +#, c-format +msgid "Downloading %d files from directory `%s'.\n" +msgstr "" + +#: src/applications/fs/tools/gnunet-download.c:366 +#, c-format +msgid "Did not find any files in directory `%s'\n" +msgstr "" + +#: src/applications/fs/tools/gnunet-download.c:404 +#, c-format +msgid "File stored as `%s'.\n" +msgstr "" + +#: src/applications/fs/uritrack/file_info.c:98 +msgid "Collecting file identifiers disabled.\n" +msgstr "" + +#: src/applications/fs/uritrack/file_info.c:377 +#, c-format +msgid "Deleted corrupt URI database in `%s'." +msgstr "" + +#: src/applications/fs/ecrs/upload.c:158 +#, c-format +msgid "`%s' is not a file.\n" +msgstr "" + +#: src/applications/fs/ecrs/upload.c:166 +#, c-format +msgid "Cannot get size of file `%s'" +msgstr "" + +#: src/applications/fs/ecrs/upload.c:175 +msgid "Failed to connect to gnunetd." +msgstr "" + +#: src/applications/fs/ecrs/upload.c:187 +#, c-format +msgid "Cannot hash `%s'.\n" +msgstr "" + +#: src/applications/fs/ecrs/upload.c:215 +#, c-format +msgid "Initialization for indexing file `%s' failed.\n" +msgstr "" + +#: src/applications/fs/ecrs/upload.c:223 +#, c-format +msgid "Indexing file `%s' failed. Suggestion: try to insert the file.\n" +msgstr "" + +#: src/applications/fs/ecrs/upload.c:237 +#, c-format +msgid "Cannot open file `%s': `%s'" +msgstr "" + +#: src/applications/fs/ecrs/upload.c:322 +#, c-format +msgid "Indexing data of file `%s' failed at position %llu.\n" +msgstr "" + +#: src/applications/fs/ecrs/helper.c:91 +msgid "No keywords specified!\n" +msgstr "" + +#: src/applications/fs/ecrs/helper.c:99 +msgid "Number of double-quotes not balanced!\n" +msgstr "" + +#: src/applications/fs/ecrs/helper.c:398 +#, c-format +msgid "Renaming of file `%s' to `%s' failed: %s\n" +msgstr "" + +#: src/applications/fs/ecrs/helper.c:408 +#, c-format +msgid "Could not rename file `%s' to `%s': file exists\n" +msgstr "" + +#: src/applications/fs/ecrs/parser.c:165 +#, c-format +msgid "" +"Unknown metadata type in metadata option `%s'. Using metadata type " +"`unknown' instead.\n" +msgstr "" + +#: src/applications/fs/ecrs/search.c:152 +msgid "CHK URI not allowed for search.\n" +msgstr "" + +#: src/applications/fs/ecrs/search.c:207 +msgid "LOC URI not allowed for search.\n" +msgstr "" + +#: src/applications/fs/ecrs/namespace.c:365 +#, c-format +msgid "File `%s' does not contain a pseudonym.\n" +msgstr "" + +#: src/applications/fs/ecrs/namespace.c:376 +#, c-format +msgid "Format of pseudonym `%s' is invalid.\n" +msgstr "" + +#: src/applications/fs/ecrs/namespace.c:535 +#: src/applications/fs/ecrs/namespace.c:547 +#: src/applications/fs/ecrs/namespace.c:559 +#, c-format +msgid "Format of file `%s' is invalid, trying to remove.\n" +msgstr "" + +#: src/applications/fs/ecrs/download.c:599 +msgid "" +"Decrypted content does not match key. This is either a bug or a maliciously " +"inserted file. Download aborted.\n" +msgstr "" + +#: src/applications/fs/ecrs/download.c:609 +msgid "IO error." +msgstr "" + +#: src/applications/fs/collection/collection.c:559 +#: src/applications/fs/collection/collection.c:562 +#, c-format +msgid "Revision %u" +msgstr "" + +#: src/applications/fs/fsui/upload.c:330 +msgid "Application aborted." +msgstr "" + +#: src/applications/fs/fsui/upload.c:344 +msgid "Failed to create temporary directory." +msgstr "" + +#: src/applications/fs/fsui/deserialize.c:927 +#, c-format +msgid "FSUI state file `%s' had syntax error at offset %u.\n" +msgstr "" + +#: src/applications/fs/fsui/unindex.c:114 +msgid "Unindexing failed (no reason given)" +msgstr "" + +#: src/applications/fs/gap/plan.c:944 +msgid "# gap requests total sent" +msgstr "" + +#: src/applications/fs/gap/plan.c:946 +msgid "# gap content total planned" +msgstr "" + +#: src/applications/fs/gap/plan.c:948 +msgid "# gap routes succeeded" +msgstr "" + +#: src/applications/fs/gap/plan.c:949 +msgid "# trust spent" +msgstr "" + +#: src/applications/fs/gap/fs.c:157 +msgid "Datastore full.\n" +msgstr "" + +#: src/applications/fs/gap/fs.c:831 +msgid "# gap requests total received" +msgstr "" + +#: src/applications/fs/gap/fs.c:833 +msgid "# gap requests dropped due to load" +msgstr "" + +#: src/applications/fs/gap/fs.c:835 +msgid "# gap content total received" +msgstr "" + +#: src/applications/fs/gap/fs.c:837 +msgid "# gap total trust awarded" +msgstr "" + +#: src/applications/fs/gap/fs.c:865 +#, c-format +msgid "" +"`%s' registering client handlers %d %d %d %d %d %d %d %d and P2P handlers %d " +"%d\n" +msgstr "" + +#: src/applications/fs/gap/fs.c:921 +msgid "enables (anonymous) file-sharing" +msgstr "" + +#: src/applications/fs/gap/ondemand.c:173 +#, c-format +msgid "" +"Because the file `%s' has been unavailable for 3 days it got removed from " +"your share. Please unindex files before deleting them as the index now " +"contains invalid references!\n" +msgstr "" + +#: src/applications/fs/gap/ondemand.c:451 +msgid "Indexed content changed (does not match its hash).\n" +msgstr "" + +#: src/applications/fs/gap/ondemand.c:569 +#, c-format +msgid "" +"Unindexed ODB block `%s' from offset %llu already missing from datastore.\n" +msgstr "" + +#: src/applications/fs/gap/pid_table.c:177 +msgid "# distinct interned peer IDs in pid table" +msgstr "" + +#: src/applications/fs/gap/pid_table.c:180 +msgid "# total RC of interned peer IDs in pid table" +msgstr "" + +#: src/applications/fs/gap/querymanager.c:708 +msgid "# gap client queries received" +msgstr "" + +#: src/applications/fs/gap/querymanager.c:710 +msgid "# gap replies sent to clients" +msgstr "" + +#: src/applications/fs/gap/querymanager.c:712 +msgid "# gap client requests tracked" +msgstr "" + +#: src/applications/fs/gap/querymanager.c:714 +msgid "# gap client requests injected" +msgstr "" + +#: src/applications/fs/gap/querymanager.c:717 +msgid "# gap query bloomfilter resizing updates" +msgstr "" + +#: src/applications/fs/gap/migration.c:437 +msgid "# blocks migrated" +msgstr "" + +#: src/applications/fs/gap/migration.c:439 +msgid "# blocks injected for migration" +msgstr "" + +#: src/applications/fs/gap/migration.c:441 +msgid "# blocks fetched for migration" +msgstr "" + +#: src/applications/fs/gap/migration.c:443 +msgid "# on-demand fetches for migration" +msgstr "" + +#: src/applications/fs/gap/gap.c:694 +msgid "# gap queries dropped (table full)" +msgstr "" + +#: src/applications/fs/gap/gap.c:696 +msgid "# gap queries dropped (redundant)" +msgstr "" + +#: src/applications/fs/gap/gap.c:698 +msgid "# gap queries routed" +msgstr "" + +#: src/applications/fs/gap/gap.c:700 +msgid "# gap content found locally" +msgstr "" + +#: src/applications/fs/gap/gap.c:703 +msgid "# gap queries refreshed existing record" +msgstr "" + +#: src/applications/fs/gap/gap.c:704 +msgid "# trust earned" +msgstr "" + +#: src/applications/fs/gap/fs_dht.c:256 +msgid "# blocks pushed into DHT" +msgstr "" + +#: src/applications/fs/gap/anonymity.c:56 +msgid "Failed to get traffic stats.\n" +msgstr "" + +#: src/applications/testing/remote.c:68 +#, c-format +msgid "scp command is : %s \n" +msgstr "" + +#: src/applications/testing/remote.c:491 +#, c-format +msgid "Friend list of %s:%d\n" +msgstr "" + +#: src/applications/testing/remote.c:513 +#, c-format +msgid "scp command for friend file copy is : %s \n" +msgstr "" + +#: src/applications/testing/remote.c:535 +#, c-format +msgid "connecting peer %s:%d to peer %s:%d\n" +msgstr "" + +#: src/applications/testing/remotetest.c:38 +msgid "Set up multiple gnunetd daemons across multiple hosts." +msgstr "" + +#: src/applications/testing/remotetest.c:43 +msgid "set number of daemons to start" +msgstr "" + +#: src/applications/testing/testing.c:268 +#: src/applications/testing/remotetopologies.c:367 +#, c-format +msgid "Waiting for peers to connect" +msgstr "" + +#: src/applications/testing/remotetopologies.c:213 +#, c-format +msgid "Connecting nodes in 2d torus topology: %u rows %u columns\n" +msgstr "" + +#: src/applications/testing/remotetopologies.c:491 +#, c-format +msgid "Failed to establish connection with peers.\n" +msgstr "" + +#: src/applications/bootstrap_http/http.c:113 +#, c-format +msgid "Bootstrap data obtained from `%s' is invalid.\n" +msgstr "" + +#: src/applications/bootstrap_http/http.c:126 +#: src/applications/bootstrap_http/http.c:277 +#: src/applications/bootstrap_http/http.c:294 +#: src/applications/bootstrap_http/http.c:333 +#: src/applications/bootstrap_http/http.c:352 +#: src/applications/bootstrap_http/http.c:365 +#: src/applications/bootstrap_http/http.c:375 +#: src/applications/bootstrap_http/http.c:385 src/transports/upnp/upnp.c:356 +#: src/transports/upnp/upnp.c:541 src/transports/http.c:1085 +#: src/transports/http.c:1209 src/transports/http.c:1377 +#: src/transports/http.c:1777 src/transports/http.c:1827 +#, c-format +msgid "%s failed at %s:%d: `%s'\n" +msgstr "" + +#: src/applications/bootstrap_http/http.c:185 +msgid "No hostlist URL specified in configuration, will not bootstrap.\n" +msgstr "" + +#: src/applications/bootstrap_http/http.c:226 +#, c-format +msgid "Bootstrapping using `%s'.\n" +msgstr "" + +#: src/applications/bootstrap_http/http.c:254 +#, c-format +msgid "Trying to download hostlist from `%s'\n" +msgstr "" + +#: src/applications/bootstrap_http/http.c:391 +#, c-format +msgid "Downloaded %llu bytes from `%s'.\n" +msgstr "" + +#: src/applications/bootstrap_http/http.c:425 +msgid "# HELLOs downloaded via http" +msgstr "" + +#: src/applications/getoption/getoption.c:78 +#, c-format +msgid "`%s' registering client handler %d\n" +msgstr "" + +#: src/applications/getoption/getoption.c:88 +msgid "allows clients to determine gnunetd's configuration" +msgstr "" + +#: src/applications/template/template.c:70 +#, c-format +msgid "`%s' registering client handler %d and %d\n" +msgstr "" + +#: src/applications/template/gnunet-template.c:42 +msgid "Template description." +msgstr "" + +#: src/applications/stats/clientapi.c:331 +msgid "Uptime (seconds)" +msgstr "" + +#: src/applications/stats/sqstats.c:151 +msgid "# Any-Blocks" +msgstr "" + +#: src/applications/stats/sqstats.c:152 +msgid "# DBlocks" +msgstr "" + +#: src/applications/stats/sqstats.c:153 +msgid "# SBlocks" +msgstr "" + +#: src/applications/stats/sqstats.c:154 +msgid "# KBlocks" +msgstr "" + +#: src/applications/stats/sqstats.c:155 +msgid "# NBlocks" +msgstr "" + +#: src/applications/stats/sqstats.c:156 +msgid "# KNBlocks" +msgstr "" + +#: src/applications/stats/sqstats.c:157 +msgid "# OnDemand-Blocks" +msgstr "" + +#: src/applications/stats/sqstats.c:158 +msgid "# Unknown-Blocks" +msgstr "" + +#: src/applications/stats/sqstats.c:159 +msgid "# expired" +msgstr "" + +#: src/applications/stats/sqstats.c:160 +msgid "# expire in 1h" +msgstr "" + +#: src/applications/stats/sqstats.c:161 +msgid "# expire in 24h" +msgstr "" + +#: src/applications/stats/sqstats.c:162 +msgid "# expire in 1 week" +msgstr "" + +#: src/applications/stats/sqstats.c:163 +msgid "# expire in 1 month" +msgstr "" + +#: src/applications/stats/sqstats.c:164 +msgid "# zero priority" +msgstr "" + +#: src/applications/stats/sqstats.c:165 +msgid "# priority one" +msgstr "" + +#: src/applications/stats/sqstats.c:166 +msgid "# priority larger than one" +msgstr "" + +#: src/applications/stats/sqstats.c:167 +msgid "# no anonymity" +msgstr "" + +#: src/applications/stats/sqstats.c:168 +msgid "# anonymity one" +msgstr "" + +#: src/applications/stats/sqstats.c:169 +msgid "# anonymity larger than one" +msgstr "" + +#: src/applications/stats/statistics.c:238 +#, no-c-format +msgid "% of allowed network load (up)" +msgstr "" + +#: src/applications/stats/statistics.c:240 +#, no-c-format +msgid "% of allowed network load (down)" +msgstr "" + +#: src/applications/stats/statistics.c:243 +#, no-c-format +msgid "% of allowed cpu load" +msgstr "" + +#: src/applications/stats/statistics.c:246 +#, no-c-format +msgid "% of allowed io load" +msgstr "" + +#: src/applications/stats/statistics.c:249 +msgid "# bytes of noise received" +msgstr "" + +#: src/applications/stats/statistics.c:251 +msgid "# plibc handles" +msgstr "" + +#: src/applications/stats/statistics.c:441 +#, c-format +msgid "`%s' registering client handlers %d %d %d and p2p handler %d\n" +msgstr "" + +#: src/applications/stats/statistics.c:463 +msgid "keeps statistics about gnunetd's operation" +msgstr "" + +#: src/applications/stats/gnunet-stats.c:61 +#, c-format +msgid "Supported peer-to-peer messages:\n" +msgstr "" + +#: src/applications/stats/gnunet-stats.c:64 +#, c-format +msgid "Supported client-server messages:\n" +msgstr "" + +#: src/applications/stats/gnunet-stats.c:83 +#: src/applications/vpn/gnunet-vpn.c:59 +msgid "Print statistics about GNUnet operations." +msgstr "" + +#: src/applications/stats/gnunet-stats.c:87 +msgid "prints supported protocol messages" +msgstr "" + +#: src/applications/stats/gnunet-stats.c:136 +#, c-format +msgid "Error reading information from gnunetd.\n" +msgstr "" + +#: src/applications/vpn/gnunet-vpn.c:63 +msgid "Suppress display of asynchronous log messages" +msgstr "" + +#: src/applications/vpn/p2p.c:75 +msgid "VPN IP src not anonymous. drop..\n" +msgstr "" + +#: src/applications/vpn/p2p.c:83 +msgid "VPN IP not anonymous, drop.\n" +msgstr "" + +#: src/applications/vpn/p2p.c:92 +msgid "VPN Received, not anonymous, drop.\n" +msgstr "" + +#: src/applications/vpn/p2p.c:97 +#, c-format +msgid "VPN Received unknown IP version %d...\n" +msgstr "" + +#: src/applications/vpn/p2p.c:110 +#, c-format +msgid "<- GNUnet(%d) : %s\n" +msgstr "" + +#: src/applications/vpn/p2p.c:139 +msgid "Could not write the tunnelled IP to the OS... Did to setup a tunnel?\n" +msgstr "" + +#: src/applications/vpn/p2p.c:183 +msgid "Receive route request\n" +msgstr "" + +#: src/applications/vpn/p2p.c:193 +#, c-format +msgid "Prepare route announcement level %d\n" +msgstr "" + +#: src/applications/vpn/p2p.c:208 +#, c-format +msgid "Send route announcement %d with route announce\n" +msgstr "" + +#: src/applications/vpn/p2p.c:217 +#, c-format +msgid "Send outside table info %d\n" +msgstr "" + +#: src/applications/vpn/p2p.c:239 +msgid "Receive route announce.\n" +msgstr "" + +#: src/applications/vpn/p2p.c:247 +msgid "Going to try insert route into local table.\n" +msgstr "" + +#: src/applications/vpn/p2p.c:256 +#, c-format +msgid "Inserting with hops %d\n" +msgstr "" + +#: src/applications/vpn/p2p.c:273 +#, c-format +msgid "Request level %d from peer %d\n" +msgstr "" + +#: src/applications/vpn/p2p.c:300 +#, c-format +msgid "Receive table limit on peer reached %d\n" +msgstr "" + +#: src/applications/vpn/vpn.c:180 +#, c-format +msgid "Not storing route to myself from peer %d\n" +msgstr "" + +#: src/applications/vpn/vpn.c:194 +#, c-format +msgid "Duplicate route to node from peer %d, choosing minimum hops" +msgstr "" + +#: src/applications/vpn/vpn.c:230 +#, c-format +msgid "Inserting route from peer %d in route table at location %d\n" +msgstr "" + +#: src/applications/vpn/vpn.c:247 +#, c-format +msgid "RFC4193 Frame length %d is too big for GNUnet!\n" +msgstr "" + +#: src/applications/vpn/vpn.c:254 +#, c-format +msgid "RFC4193 Frame length %d too small\n" +msgstr "" + +#: src/applications/vpn/vpn.c:273 +#, c-format +msgid "RFC4193 Ethertype %x and IP version %x do not match!\n" +msgstr "" + +#: src/applications/vpn/vpn.c:289 +#, c-format +msgid "RFC4193 Going to try and make a tunnel in slot %d\n" +msgstr "" + +#: src/applications/vpn/vpn.c:295 +#, c-format +msgid "Cannot open tunnel device: %s" +msgstr "" + +#: src/applications/vpn/vpn.c:331 +#, c-format +msgid "RFC4193 Create skips gnu%d as we are already using it\n" +msgstr "" + +#: src/applications/vpn/vpn.c:346 +#, c-format +msgid "Cannot set tunnel name to %s because of %s\n" +msgstr "" + +#: src/applications/vpn/vpn.c:356 +#, c-format +msgid "Configured tunnel name to %s\n" +msgstr "" + +#: src/applications/vpn/vpn.c:398 +#, c-format +msgid "Cannot get socket flags for gnu%d because %s\n" +msgstr "" + +#: src/applications/vpn/vpn.c:408 +#, c-format +msgid "Cannot set socket flags for gnu%d because %s\n" +msgstr "" + +#: src/applications/vpn/vpn.c:418 +#, c-format +msgid "Cannot set MTU for gnu%d because %s\n" +msgstr "" + +#: src/applications/vpn/vpn.c:426 +#, c-format +msgid "Cannot get interface index for gnu%d because %s\n" +msgstr "" + +#: src/applications/vpn/vpn.c:440 +#, c-format +msgid "IPv6 ifaddr gnu%d - %x:%x:%x:%x:%x:%x:%x:%x/%d\n" +msgstr "" + +#: src/applications/vpn/vpn.c:455 +#, c-format +msgid "Cannot set interface IPv6 address for gnu%d because %s\n" +msgstr "" + +#: src/applications/vpn/vpn.c:471 +#, c-format +msgid "IPv6 route gnu%d - destination %x:%x:%x:%x:%x:%x:%x:%x/%d\n" +msgstr "" + +#: src/applications/vpn/vpn.c:485 +#, c-format +msgid "Cannot add route IPv6 address for gnu%s because %s\n" +msgstr "" + +#: src/applications/vpn/vpn.c:528 +msgid "" +"RFC4193 We have run out of memory and so I can't store a tunnel for this " +"peer.\n" +msgstr "" + +#: src/applications/vpn/vpn.c:579 +#, c-format +msgid "RFC4193 Thread running (frame %d tunnel %d f2f %d) ...\n" +msgstr "" + +#: src/applications/vpn/vpn.c:661 +#, c-format +msgid "VPN dropping connection %x\n" +msgstr "" + +#: src/applications/vpn/vpn.c:670 +#, c-format +msgid "VPN cannot drop connection %x\n" +msgstr "" + +#: src/applications/vpn/vpn.c:690 +msgid "RFC4193 Thread exiting\n" +msgstr "" + +#: src/applications/vpn/vpn.c:712 +msgid "realise alloc ram\n" +msgstr "" + +#: src/applications/vpn/vpn.c:735 +msgid "realise add routes\n" +msgstr "" + +#: src/applications/vpn/vpn.c:849 +msgid "realise copy table\n" +msgstr "" + +#: src/applications/vpn/vpn.c:898 +#, c-format +msgid "`%s' initialising RFC4913 module %d and %d\n" +msgstr "" + +#: src/applications/vpn/vpn.c:903 +#, c-format +msgid "RFC4193 my First 4 hex digits of host id are %x\n" +msgstr "" + +#: src/applications/vpn/vpn.c:942 +msgid "enables IPv6 over GNUnet (incomplete)" +msgstr "" + +#: src/applications/vpn/vpn.c:963 +msgid "RFC4193 Waiting for tun thread to end\n" +msgstr "" + +#: src/applications/vpn/vpn.c:978 +msgid "RFC4193 The tun thread has ended\n" +msgstr "" + +#: src/applications/vpn/vpn.c:996 +#, c-format +msgid "RFC4193 Closing tunnel %d fd %d\n" +msgstr "" + +#: src/server/core.c:119 src/server/core.c:318 +#, c-format +msgid "Configuration value `%s' under [MODULES] for `%s' is invalid!\n" +msgstr "" + +#: src/server/core.c:140 +#, c-format +msgid "Application module `%s' already initialized!\n" +msgstr "" + +#: src/server/core.c:194 +#, c-format +msgid "Failed to load plugin `%s' at %s:%d. Unloading plugin.\n" +msgstr "" + +#: src/server/core.c:244 +#, c-format +msgid "Could not shutdown `%s': application not loaded\n" +msgstr "" + +#: src/server/core.c:255 +#, c-format +msgid "Could not shutdown application `%s': not initialized\n" +msgstr "" + +#: src/server/core.c:265 +#, c-format +msgid "Could not find '%s%s' method in library `%s'.\n" +msgstr "" + +#: src/server/core.c:422 +#, c-format +msgid "Could not release %p: service not loaded\n" +msgstr "" + +#: src/server/core.c:531 +#, c-format +msgid "Could not properly shutdown application `%s'.\n" +msgstr "" + +#: src/server/core.c:676 +#, c-format +msgid "Could not properly unload service `%s'!\n" +msgstr "" + +#: src/server/gnunet-update.c:146 +#, c-format +msgid "Updating data for module `%s'\n" +msgstr "" + +#: src/server/gnunet-update.c:151 +#, c-format +msgid "Failed to update data for module `%s'\n" +msgstr "" + +#: src/server/gnunet-update.c:225 src/server/gnunetd.c:124 +msgid "Core initialization failed.\n" +msgstr "" + +#: src/server/gnunet-update.c:270 +msgid "Updates GNUnet datastructures after version change." +msgstr "" + +#: src/server/gnunet-update.c:274 src/server/gnunet-transport-check.c:376 +msgid "run as user LOGIN" +msgstr "" + +#: src/server/gnunet-update.c:278 +msgid "run in client mode (for getting client configuration values)" +msgstr "" + +#: src/server/version.c:125 +msgid "" +"Failed to determine filename used to store GNUnet version information!\n" +msgstr "" + +#: src/server/gnunetd.c:85 +#, c-format +msgid "`%s' startup complete.\n" +msgstr "" + +#: src/server/gnunetd.c:89 +#, c-format +msgid "`%s' is shutting down.\n" +msgstr "" + +#: src/server/gnunetd.c:179 +msgid "" +"run in debug mode; gnunetd will not daemonize and error messages will be " +"written to stderr instead of a logfile" +msgstr "" + +#: src/server/gnunetd.c:183 +msgid "Starts the gnunetd daemon." +msgstr "" + +#: src/server/gnunetd.c:186 +msgid "disable padding with random data (experimental)" +msgstr "" + +#: src/server/gnunetd.c:190 +msgid "print all log messages to the console (only works together with -d)" +msgstr "" + +#: src/server/gnunetd.c:194 +msgid "specify username as which gnunetd should run" +msgstr "" + +#: src/server/gnunetd.c:275 +#, c-format +msgid "Configuration or GNUnet version changed. You need to run `%s'!\n" +msgstr "" + +#: src/server/tcpserver.c:121 +#, c-format +msgid "The `%s' request received from client is malformed.\n" +msgstr "" + +#: src/server/tcpserver.c:409 +#, c-format +msgid "`%s' failed for port %d. Is gnunetd already running?\n" +msgstr "" + +#: src/server/tcpserver.c:487 src/server/tcpserver.c:512 +#, c-format +msgid "" +"Malformed network specification in the configuration in section `%s' for " +"entry `%s': %s\n" +msgstr "" + +#: src/server/tcpserver.c:572 +#, c-format +msgid "Registering failed, message type %d already in use.\n" +msgstr "" + +#: src/server/startup.c:219 +#, c-format +msgid "Unable to obtain filesystem information for `%s': %u\n" +msgstr "" + +#: src/server/startup.c:237 +#, c-format +msgid "" +"Filesystem `%s' of partition `%s' is unknown. Please contact gnunet-" +"developers@gnu.org!" +msgstr "" + +#: src/server/startup.c:252 +#, c-format +msgid "" +"Limiting datastore size to %llu GB, because the `%s' filesystem does not " +"support larger files. Please consider storing the database on a NTFS " +"partition!\n" +msgstr "" + +#: src/server/startup.c:291 +#, c-format +msgid "Insufficient access permissions for `%s': %s\n" +msgstr "" + +#: src/server/gnunet-peer-info.c:55 +msgid "Print information about GNUnet peers." +msgstr "" + +#: src/server/gnunet-peer-info.c:59 +msgid "don't resolve host names" +msgstr "" + +#: src/server/gnunet-peer-info.c:62 +msgid "output only the identity strings" +msgstr "" + +#: src/server/gnunet-peer-info.c:65 +msgid "output our own identity only" +msgstr "" + +#: src/server/gnunet-peer-info.c:130 src/server/gnunet-peer-info.c:164 +#, c-format +msgid "Could not get address of peer `%s'.\n" +msgstr "" + +#: src/server/gnunet-peer-info.c:143 +#, c-format +msgid "`%s' message invalid (signature invalid).\n" +msgstr "" + +#: src/server/gnunet-peer-info.c:168 +#, c-format +msgid "Peer `%s' with trust %8u\n" +msgstr "" + +#: src/server/gnunet-peer-info.c:175 +#, c-format +msgid "Peer `%s' with trust %8u and address `%s'\n" +msgstr "" + +#: src/server/connection.c:1313 +#, c-format +msgid "`%s' selected %d out of %d messages (MTU: %d).\n" +msgstr "" + +#: src/server/connection.c:1323 +#, c-format +msgid "Message details: %u: length %d, priority: %d\n" +msgstr "" + +#: src/server/connection.c:3129 +#, c-format +msgid "Message from `%s' discarded: invalid format.\n" +msgstr "" + +#: src/server/connection.c:3218 +#, c-format +msgid "Invalid sequence number %u <= %u, dropping message.\n" +msgstr "" + +#: src/server/connection.c:3240 +msgid "Message received more than one day old. Dropped.\n" +msgstr "" + +#: src/server/connection.c:3763 +msgid "# outgoing messages dropped" +msgstr "" + +#: src/server/connection.c:3766 +msgid "# bytes of outgoing messages dropped" +msgstr "" + +#: src/server/connection.c:3768 +msgid "# connections closed (HANGUP sent)" +msgstr "" + +#: src/server/connection.c:3772 +msgid "# connections closed (transport issue)" +msgstr "" + +#: src/server/connection.c:3775 +msgid "# bytes encrypted" +msgstr "" + +#: src/server/connection.c:3779 +msgid "# bytes transmitted" +msgstr "" + +#: src/server/connection.c:3783 +msgid "# bytes received" +msgstr "" + +#: src/server/connection.c:3785 +msgid "# bytes decrypted" +msgstr "" + +#: src/server/connection.c:3786 +msgid "# bytes noise sent" +msgstr "" + +#: src/server/connection.c:3789 +msgid "# total bytes per second send limit" +msgstr "" + +#: src/server/connection.c:3792 +msgid "# total bytes per second receive limit" +msgstr "" + +#: src/server/connection.c:3795 +msgid "# total number of messages in send buffers" +msgstr "" + +#: src/server/connection.c:3798 +msgid "# total number of bytes we were allowed to send but did not" +msgstr "" + +#: src/server/connection.c:3801 +msgid "# total number of bytes we were allowed to sent" +msgstr "" + +#: src/server/connection.c:3804 +msgid "# total number of bytes we are currently allowed to send" +msgstr "" + +#: src/server/connection.c:3807 +msgid "# transports switched to stream transport" +msgstr "" + +#: src/server/connection.c:3810 +msgid "# average connection lifetime (in ms)" +msgstr "" + +#: src/server/connection.c:3813 +msgid "# conn. shutdown: other peer sent too much" +msgstr "" + +#: src/server/connection.c:3816 +msgid "# conn. shutdown: we lacked bandwidth" +msgstr "" + +#: src/server/connection.c:3819 +msgid "# conn. shutdown: other peer timed out" +msgstr "" + +#: src/server/connection.c:3822 +msgid "# conn. shutdown: timed out during connect" +msgstr "" + +#: src/server/connection.c:3825 +msgid "# conn. shutdown: other peer requested it" +msgstr "" + +#: src/server/handler.c:442 +#, c-format +msgid "Received corrupt message from peer `%s' in %s:%d.\n" +msgstr "" + +#: src/server/gnunet-transport-check.c:121 +#, c-format +msgid "`%s': Could not create hello.\n" +msgstr "" + +#: src/server/gnunet-transport-check.c:129 +#, c-format +msgid "`%s': Could not connect.\n" +msgstr "" + +#: src/server/gnunet-transport-check.c:163 +#, c-format +msgid "`%s': Could not send.\n" +msgstr "" + +#: src/server/gnunet-transport-check.c:179 +#, c-format +msgid "`%s': Did not receive message within %llu ms.\n" +msgstr "" + +#: src/server/gnunet-transport-check.c:192 +#, c-format +msgid "`%s': Could not disconnect.\n" +msgstr "" + +#: src/server/gnunet-transport-check.c:200 +#, c-format +msgid "" +"`%s' transport OK. It took %ums to transmit %llu messages of %llu bytes " +"each.\n" +msgstr "" + +#: src/server/gnunet-transport-check.c:231 +#, c-format +msgid " Transport %d is not being tested\n" +msgstr "" + +#: src/server/gnunet-transport-check.c:261 +#, c-format +msgid "" +"\n" +"Contacting `%s'." +msgstr "" + +#: src/server/gnunet-transport-check.c:286 +#, c-format +msgid " Connection failed\n" +msgstr "" + +#: src/server/gnunet-transport-check.c:292 +#, c-format +msgid " Connection failed (bug?)\n" +msgstr "" + +#: src/server/gnunet-transport-check.c:330 +#, c-format +msgid "Timeout after %llums.\n" +msgstr "" + +#: src/server/gnunet-transport-check.c:332 +#, c-format +msgid "OK!\n" +msgstr "" + +#: src/server/gnunet-transport-check.c:357 +msgid "Tool to test if GNUnet transport services are operational." +msgstr "" + +#: src/server/gnunet-transport-check.c:361 +msgid "ping peers from HOSTLISTURL that match transports" +msgstr "" + +#: src/server/gnunet-transport-check.c:364 +msgid "send COUNT messages" +msgstr "" + +#: src/server/gnunet-transport-check.c:367 +msgid "send messages with SIZE bytes payload" +msgstr "" + +#: src/server/gnunet-transport-check.c:370 +msgid "specifies which TRANSPORT should be tested" +msgstr "" + +#: src/server/gnunet-transport-check.c:373 +msgid "specifies after how many MS to time-out" +msgstr "" + +#: src/server/gnunet-transport-check.c:381 +msgid "repeat each test X times" +msgstr "" + +#: src/server/gnunet-transport-check.c:449 +#, c-format +msgid "Testing transport(s) %s\n" +msgstr "" + +#: src/server/gnunet-transport-check.c:451 +#, c-format +msgid "Available transport(s): %s\n" +msgstr "" + +#: src/server/gnunet-transport-check.c:501 +#, c-format +msgid "" +"\n" +"%d out of %d peers contacted successfully (%d times transport unavailable).\n" +msgstr "" + +#: src/transports/common.c:370 +#, c-format +msgid "Port is 0, will only send using %s.\n" +msgstr "" + +#: src/transports/smtp.c:367 src/transports/udp.c:107 src/transports/tcp.c:271 +#: src/transports/tcp.c:291 +#, c-format +msgid "Received malformed message via %s. Ignored.\n" +msgstr "" + +#: src/transports/smtp.c:459 +msgid "SMTP filter string to invalid, lacks ': '\n" +msgstr "" + +#: src/transports/smtp.c:469 +#, c-format +msgid "SMTP filter string to long, capped to `%s'\n" +msgstr "" + +#: src/transports/smtp.c:564 src/transports/smtp.c:575 +#: src/transports/smtp.c:589 src/transports/smtp.c:609 +#: src/transports/smtp.c:634 src/transports/smtp.c:643 +#: src/transports/smtp.c:657 src/transports/smtp.c:669 +#, c-format +msgid "SMTP: `%s' failed: %s.\n" +msgstr "" + +#: src/transports/smtp.c:814 +msgid "No email-address specified, can not start SMTP transport.\n" +msgstr "" + +#: src/transports/smtp.c:831 +msgid "# bytes received via SMTP" +msgstr "" + +#: src/transports/smtp.c:832 +msgid "# bytes sent via SMTP" +msgstr "" + +#: src/transports/smtp.c:834 +msgid "# bytes dropped by SMTP (outgoing)" +msgstr "" + +#: src/transports/upnp/upnp.c:431 +#, c-format +msgid "%s failed for url `%s' and post-data `%s' at %s:%d: `%s'\n" +msgstr "" + +#: src/transports/upnp/upnp.c:476 +#, c-format +msgid "upnp: NAT Returned IP: %s\n" +msgstr "" + +#: src/transports/http.c:2019 src/transports/tcp.c:811 +#, c-format +msgid "" +"The UPnP service could not be loaded. To disable UPnP, set the configuration " +"option \"UPNP\" in section \"%s\" to \"NO\"\n" +msgstr "" + +#: src/transports/http.c:2028 +msgid "# bytes received via HTTP" +msgstr "" + +#: src/transports/http.c:2029 +msgid "# bytes sent via HTTP" +msgstr "" + +#: src/transports/http.c:2031 +msgid "# bytes dropped by HTTP (outgoing)" +msgstr "" + +#: src/transports/http.c:2032 +msgid "# HTTP GET issued" +msgstr "" + +#: src/transports/http.c:2034 +msgid "# HTTP GET received" +msgstr "" + +#: src/transports/http.c:2035 +msgid "# HTTP PUT issued" +msgstr "" + +#: src/transports/http.c:2037 +msgid "# HTTP PUT received" +msgstr "" + +#: src/transports/http.c:2039 +msgid "# HTTP select calls" +msgstr "" + +#: src/transports/http.c:2041 +msgid "# HTTP send calls" +msgstr "" + +#: src/transports/http.c:2044 +msgid "# HTTP curl send callbacks" +msgstr "" + +#: src/transports/http.c:2046 +msgid "# HTTP curl receive callbacks" +msgstr "" + +#: src/transports/http.c:2048 +msgid "# HTTP mhd access callbacks" +msgstr "" + +#: src/transports/http.c:2050 +msgid "# HTTP mhd read callbacks" +msgstr "" + +#: src/transports/http.c:2052 +msgid "# HTTP mhd close callbacks" +msgstr "" + +#: src/transports/http.c:2054 +msgid "# HTTP connect calls" +msgstr "" + +#: src/transports/ip.c:70 src/transports/ip.c:365 +#, c-format +msgid "Failed to obtain my (external) %s address!\n" +msgstr "" + +#: src/transports/udp.c:472 src/transports/tcp.c:728 +#, c-format +msgid "Failed to bind to %s port %d.\n" +msgstr "" + +#: src/transports/udp.c:538 +#, c-format +msgid "MTU %llu for `%s' is probably too low!\n" +msgstr "" + +#: src/transports/udp.c:562 +msgid "# bytes received via UDP" +msgstr "" + +#: src/transports/udp.c:563 +msgid "# bytes sent via UDP" +msgstr "" + +#: src/transports/udp.c:565 +msgid "# bytes dropped by UDP (outgoing)" +msgstr "" + +#: src/transports/udp.c:567 +msgid "# UDP connections (right now)" +msgstr "" + +#: src/transports/tcp.c:821 +msgid "# bytes received via TCP" +msgstr "" + +#: src/transports/tcp.c:822 +msgid "# bytes sent via TCP" +msgstr "" + +#: src/transports/tcp.c:824 +msgid "# bytes dropped by TCP (outgoing)" +msgstr "" + +#: src/include/gnunet_util_getopt.h:154 +msgid "print this help" +msgstr "" + +#: src/include/gnunet_util_getopt.h:163 +msgid "print the version number" +msgstr "" + +#: src/include/gnunet_util_getopt.h:169 +msgid "configure logging to use LOGLEVEL" +msgstr "" + +#: src/include/gnunet_util_getopt.h:175 +msgid "be verbose" +msgstr "" + +#: src/include/gnunet_util_getopt.h:181 +msgid "use configuration file FILENAME" +msgstr "" + +#: src/include/gnunet_util_getopt.h:187 +msgid "specify host on which gnunetd is running" +msgstr "" + +#: src/include/gnunet_util_error.h:219 src/include/gnunet_util_error.h:224 +#: src/include/gnunet_util_error.h:230 src/include/gnunet_util_error.h:232 +#, c-format +msgid "Internal error: assertion failed at %s:%d.\n" +msgstr "" + +#: src/include/gnunet_util_error.h:242 +#, c-format +msgid "" +"External protocol violation: assertion failed at %s:%d (no need to panic, we " +"can handle this).\n" +msgstr "" + +#: src/include/gnunet_util_error.h:270 src/include/gnunet_util_error.h:277 +#, c-format +msgid "`%s' failed on file `%s' at %s:%d with error: %s\n" +msgstr "" + +#: contrib/config-daemon.scm:39 contrib/config-client.scm:40 +msgid "No help available." +msgstr "" + +#: contrib/config-daemon.scm:42 +msgid "" +"You can use 'make check' in src/transports/upnp/ to find out if your NAT " +"supports UPnP. You should disable this option if you are sure that you are " +"not behind a NAT. If your NAT box does not support UPnP, having this on " +"will not do much harm (only cost a small amount of resources)." +msgstr "" + +#: contrib/config-daemon.scm:54 contrib/config-client.scm:53 +msgid "Prompt for development and/or incomplete code" +msgstr "" + +#: contrib/config-daemon.scm:56 contrib/config-client.scm:55 +msgid "" +"If EXPERIMENTAL is set to NO, options for experimental code are not shown. " +"If in doubt, use NO.\n" +"\n" +"Some options apply to experimental code that maybe in a state of development " +"where the functionality, stability, or the level of testing is not yet high " +"enough for general use. These features are said to be of \"alpha\" " +"quality. If a feature is currently in alpha, uninformed use is discouraged " +"(since the developers then do not fancy \"Why doesn't this work?\" type " +"messages).\n" +"\n" +"However, active testing and qualified feedback of these features is always " +"welcome. Users should just be aware that alpha features may not meet the " +"normal level of reliability or it may fail to work in some special cases. " +"Bug reports are usually welcomed by the developers, but please read the " +"documents and and use for how to report problems." +msgstr "" + +#: contrib/config-daemon.scm:71 contrib/config-client.scm:70 +msgid "Show options for advanced users" +msgstr "" + +#: contrib/config-daemon.scm:73 contrib/config-client.scm:72 +msgid "" +"These are options that maybe difficult to understand for the beginner. These " +"options typically refer to features that allow tweaking of the " +"installation. If in a hurry, say NO." +msgstr "" + +#: contrib/config-daemon.scm:84 contrib/config-client.scm:83 +msgid "Show rarely used options" +msgstr "" + +#: contrib/config-daemon.scm:86 contrib/config-client.scm:85 +msgid "" +"These are options that hardly anyone actually needs. If you plan on doing " +"development on GNUnet, you may want to look into these. If in doubt or in a " +"hurry, say NO." +msgstr "" + +#: contrib/config-daemon.scm:97 contrib/config-client.scm:96 +msgid "Meta-configuration" +msgstr "" + +#: contrib/config-daemon.scm:98 contrib/config-client.scm:97 +msgid "Which level of configuration should be available" +msgstr "" + +#: contrib/config-daemon.scm:115 +msgid "Full pathname of GNUnet HOME directory" +msgstr "" + +#: contrib/config-daemon.scm:117 +msgid "" +"This gives the root-directory of the GNUnet installation. Make sure there is " +"some space left in that directory. :-) Users inserting or indexing files " +"will be able to store data in this directory up to the (global) quota " +"specified below. Having a few gigabytes of free space is recommended." +msgstr "" + +#: contrib/config-daemon.scm:130 +msgid "Full pathname of GNUnet directory for file-sharing data" +msgstr "" + +#: contrib/config-daemon.scm:142 +msgid "Full pathname to the directory with the key-value database" +msgstr "" + +#: contrib/config-daemon.scm:143 +msgid "Note that the kvstore is currently not used." +msgstr "" + +#: contrib/config-daemon.scm:154 +msgid "Full pathname of GNUnet directory for indexed files symbolic links" +msgstr "" + +#: contrib/config-daemon.scm:166 +msgid "How many minutes should peer advertisements last?" +msgstr "" + +#: contrib/config-daemon.scm:168 +msgid "" +"How many minutes is the current IP valid? (GNUnet will sign HELLO messages " +"with this expiration timeline. If you are on dialup, 60 (for 1 hour) is " +"suggested. If you have a static IP address, you may want to set this to a " +"large value (say 14400). The default is 1440 (1 day). If your IP changes " +"periodically, you will want to choose an expiry period smaller than the " +"frequency with which your IP changes." +msgstr "" + +#: contrib/config-daemon.scm:179 +msgid "Where can GNUnet find an initial list of peers?" +msgstr "" + +#: contrib/config-daemon.scm:181 +msgid "" +"GNUnet can automatically update the hostlist from the web. While GNUnet " +"internally communicates which hosts are online, it is typically a good idea " +"to get a fresh hostlist whenever gnunetd starts from the WEB. By setting " +"this option, you can specify from which server gnunetd should try to " +"download the hostlist. The default should be fine for now.\n" +"\t\t\n" +"The general format is a list of space-separated URLs. Each URL must have " +"the format http://HOSTNAME/FILENAME\n" +"\t\t\n" +"If you want to setup an alternate hostlist server, you must run a permanent " +"node and \"cat data/hosts/* > hostlist\" every few minutes to keep the list " +"up-to-date.\n" +"\t\t\n" +"If you do not specify a HOSTLISTURL, you must copy valid hostkeys to data/" +"hosts manually." +msgstr "" + +#: contrib/config-daemon.scm:198 +msgid "HTTP Proxy Server" +msgstr "" + +#: contrib/config-daemon.scm:200 +msgid "" +"If you have to use a proxy for outbound HTTP connections, specify the proxy " +"configuration here. Default is no proxy." +msgstr "" + +#: contrib/config-daemon.scm:212 +msgid "" +"Name of the directory where gnunetd should store contact information about " +"peers" +msgstr "" + +#: contrib/config-daemon.scm:214 +msgid "" +"Unless you want to share the directory directly using a webserver, the " +"default is most likely just fine." +msgstr "" + +#: contrib/config-daemon.scm:240 contrib/config-client.scm:140 +msgid "How long should logs be kept?" +msgstr "" + +#: contrib/config-daemon.scm:242 contrib/config-client.scm:142 +msgid "" +"How long should logs be kept? If you specify a value greater than zero, a " +"log is created each day with the date appended to its filename. These logs " +"are deleted after $KEEPLOG days.\tTo keep logs forever, set this value to 0." +msgstr "" + +#: contrib/config-daemon.scm:253 +msgid "" +"What maximum number of open file descriptors should be requested from the OS?" +msgstr "" + +#: contrib/config-daemon.scm:255 +msgid "" +"The default of 1024 should be fine for most systems. If your system can " +"support more, increasing the number might help support additional clients on " +"machines with plenty of bandwidth. For embedded systems, a smaller number " +"might be acceptable. A value of 0 will leave the descriptor limit " +"untouched. This option is mostly for OS X systems where the default is too " +"low. Note that if gnunetd cannot obtain the desired number of file " +"descriptors from the operating system, it will print a warning and try to " +"run with what it is given." +msgstr "" + +#: contrib/config-daemon.scm:266 +msgid "Where should gnunetd write the logs?" +msgstr "" + +#: contrib/config-daemon.scm:278 +msgid "Enable for extra-verbose logging." +msgstr "" + +#: contrib/config-daemon.scm:290 contrib/config-client.scm:165 +msgid "Logging" +msgstr "" + +#: contrib/config-daemon.scm:291 contrib/config-client.scm:166 +msgid "Specify which system messages should be logged how" +msgstr "" + +#: contrib/config-daemon.scm:296 contrib/config-client.scm:170 +msgid "Logging of events for users" +msgstr "" + +#: contrib/config-daemon.scm:297 contrib/config-client.scm:171 +msgid "Logging of events for the system administrator" +msgstr "" + +#: contrib/config-daemon.scm:309 +msgid "Where should gnunetd write the PID?" +msgstr "" + +#: contrib/config-daemon.scm:310 +msgid "" +"The default is no longer /var/run/gnunetd.pid since we could not delete the " +"file on shutdown at that location." +msgstr "" + +#: contrib/config-daemon.scm:322 +msgid "As which user should gnunetd run?" +msgstr "" + +#: contrib/config-daemon.scm:324 +msgid "" +"Empty means \"current user\". On computer startup, it is root/SYSTEM. Under " +"Windows, this setting affects the creation of a new system service only." +msgstr "" + +#: contrib/config-daemon.scm:337 +msgid "Should gnunetd be automatically started when the system boots?" +msgstr "" + +#: contrib/config-daemon.scm:338 +msgid "" +"Set to YES if gnunetd should be automatically started on boot. If this " +"option is set, gnunet-setup will install a script to start the daemon upon " +"completion. This option may not work on all systems." +msgstr "" + +#: contrib/config-daemon.scm:350 +msgid "Which transport mechanisms should GNUnet use?" +msgstr "" + +#: contrib/config-daemon.scm:352 +msgid "" +"Use a space-separated list of modules, e.g. \"udp smtp tcp\". The " +"available transports are udp, tcp, http, smtp and nat.\n" +"\t\t\n" +"Loading the 'nat' and 'tcp' modules is required for peers behind NAT boxes " +"that cannot directly be reached from the outside. Peers that are NOT behind " +"a NAT box and that want to *allow* peers that ARE behind a NAT box to " +"connect must ALSO load the 'nat' module. Note that the actual transfer will " +"always be via tcp initiated by the peer behind the NAT box. The nat " +"transport requires the use of tcp, http and/or smtp in addition to nat " +"itself." +msgstr "" + +#: contrib/config-daemon.scm:366 +msgid "Which applications should gnunetd support?" +msgstr "" + +#: contrib/config-daemon.scm:368 +msgid "" +"Whenever this option is changed, you MUST run gnunet-update. Currently, the " +"available applications are:\n" +"\n" +"advertising: advertises your peer to other peers. Without it, your peer will " +"not participate in informing peers about other peers. You should always " +"load this module.\n" +"\n" +"getoption: allows clients to query gnunetd about the values of various " +"configuration options. Many tools need this. You should always load this " +"module.\n" +"\n" +"stats: allows tools like gnunet-stats and gnunet-gtk to query gnunetd about " +"various statistics. This information is usually quite useful to diagnose " +"errors, hence it is recommended that you load this module.\n" +"\n" +"traffic: keeps track of how many messages were recently received and " +"transmitted. This information can then be used to establish how much cover " +"traffic is currently available. The amount of cover traffic becomes " +"important if you want to make anonymous requests with an anonymity level " +"that is greater than one. It is recommended that you load this module.\n" +"\n" +"fs: needed for anonymous file sharing. You should always load this module.\n" +"\n" +"hostlist: integrated hostlist HTTP server. Useful if you want to offer a " +"hostlist and running Apache would be overkill.\n" +"\n" +"chat: broadcast chat (demo-application, ALPHA quality).\tRequired for gnunet-" +"chat. Note that the current implementation of chat is not considered to be " +"secure.\n" +"\n" +"tbench: benchmark transport performance. Required for gnunet-tbench. Note " +"that tbench allows other users to abuse your resources.\n" +"\n" +"tracekit: topology visualization toolkit. Required for gnunet-tracekit. " +"Note that loading tracekit will make it slightly easier for an adversary to " +"compromise your anonymity." +msgstr "" + +#: contrib/config-daemon.scm:399 +msgid "Disable client-server connections" +msgstr "" + +#: contrib/config-daemon.scm:400 +msgid "" +"This option can be used to tell gnunetd not to open the client port. When " +"run like this, gnunetd will participate as a peer in the network but not " +"support any user interfaces. This may be useful for headless systems that " +"are never expected to have end-user interactions. Note that this will also " +"prevent you from running diagnostic tools like gnunet-stats!" +msgstr "" + +#: contrib/config-daemon.scm:412 +msgid "YES disables IPv6 support, NO enables IPv6 support" +msgstr "" + +#: contrib/config-daemon.scm:413 +msgid "" +"This option may be useful on peers where the kernel does not support IPv6. " +"You might also want to set this option if you do not have an IPv6 network " +"connection." +msgstr "" + +#: contrib/config-daemon.scm:425 +msgid "Disable peer discovery" +msgstr "" + +#: contrib/config-daemon.scm:426 +msgid "" +"The option 'PRIVATE-NETWORK' can be used to limit the connections of this " +"peer to peers of which the hostkey has been copied by hand to data/hosts; " +"if this option is given, GNUnet will not accept advertisements of peers that " +"the local node does not already know about. Note that in order for this " +"option to work, HOSTLISTURL should either not be set at all or be set to a " +"trusted peer that only advertises the private network. Also, the option does " +"NOT work at the moment if the NAT transport is loaded; for that, a couple of " +"lines above would need some minor editing :-)." +msgstr "" + +#: contrib/config-daemon.scm:437 +msgid "Disable advertising this peer to other peers" +msgstr "" + +#: contrib/config-daemon.scm:449 +msgid "Disable automatic establishment of connections" +msgstr "" + +#: contrib/config-daemon.scm:450 +msgid "" +"If this option is enabled, GNUnet will not automatically establish " +"connections to other peers, but instead wait for applications to " +"specifically request connections to other peers (or for other peers to " +"connect to us)." +msgstr "" + +#: contrib/config-daemon.scm:461 +msgid "Enable advertising of other peers by this peer" +msgstr "" + +#: contrib/config-daemon.scm:462 +msgid "" +"This option may be useful during testing, but turning it off is dangerous! " +"If in any doubt, set it to YES (which is the default)." +msgstr "" + +#: contrib/config-daemon.scm:473 +msgid "Port for communication with GNUnet user interfaces" +msgstr "" + +#: contrib/config-daemon.scm:474 +msgid "" +"Which is the client-server port that is used between gnunetd and the clients " +"(TCP only). You may firewall this port for non-local machines (but you do " +"not have to since GNUnet will perform access control and only allow " +"connections from machines that are listed under TRUSTED)." +msgstr "" + +#: contrib/config-daemon.scm:485 +msgid "Port for the integrated hostlist HTTP server" +msgstr "" + +#: contrib/config-daemon.scm:497 +msgid "IPv4 networks allowed to use gnunetd server" +msgstr "" + +#: contrib/config-daemon.scm:498 contrib/config-daemon.scm:510 +msgid "" +"This option specifies which hosts are trusted enough to connect as clients " +"(to the TCP port). This is useful if you run gnunetd on one host of your " +"network and want to allow all other hosts to use this node as their server. " +"By default, this is set to 'loopback only'. The format is IP/NETMASK where " +"the IP is specified in dotted-decimal and the netmask either in CIDR " +"notation (/16) or in dotted decimal (255.255.0.0). Several entries must be " +"separated by a semicolon, spaces are not allowed." +msgstr "" + +#: contrib/config-daemon.scm:509 +msgid "IPv6 networks allowed to use gnunetd server" +msgstr "" + +#: contrib/config-daemon.scm:522 +msgid "Limit connections to the specfied set of peers." +msgstr "" + +#: contrib/config-daemon.scm:523 +msgid "" +"If this option is not set, any peer is allowed to connect. If it is set, " +"only the specified peers are allowed. Specify the list of peer IDs (not IPs!)" +msgstr "" + +#: contrib/config-daemon.scm:534 +msgid "Run gnunetd as this group." +msgstr "" + +#: contrib/config-daemon.scm:535 +msgid "" +"When started as root, gnunetd will change permissions to the given group." +msgstr "" + +#: contrib/config-daemon.scm:546 +msgid "Prevent the specfied set of peers from connecting." +msgstr "" + +#: contrib/config-daemon.scm:547 +msgid "" +"If this option is not set, any peer is allowed to connect. If the ID of a " +"peer is listed here, connections from that peer will be refused. Specify " +"the list of peer IDs (not IPs!)" +msgstr "" + +#: contrib/config-daemon.scm:558 +msgid "Topology Maintenance" +msgstr "" + +#: contrib/config-daemon.scm:559 +msgid "Rarely used settings for peer advertisements and connections" +msgstr "" + +#: contrib/config-daemon.scm:579 +msgid "General settings" +msgstr "" + +#: contrib/config-daemon.scm:580 +msgid "Settings that change the behavior of GNUnet in general" +msgstr "" + +#: contrib/config-daemon.scm:607 +msgid "Modules" +msgstr "" + +#: contrib/config-daemon.scm:608 +msgid "Settings that select specific implementations for GNUnet modules" +msgstr "" + +#: contrib/config-daemon.scm:626 +msgid "Fundamentals" +msgstr "" + +#: contrib/config-daemon.scm:646 +msgid "Which database should be used?" +msgstr "" + +#: contrib/config-daemon.scm:648 +msgid "" +"Which database should be used? The options are \"sqstore_sqlite\", " +"\"sqstore_postgres\" and \"sqstore_mysql\". You must run gnunet-update " +"after changing this value!\n" +"\t\t\t\n" +"In order to use MySQL or Postgres, you must configure the respective " +"database, which is relatively simple. Read the file doc/README.mysql or doc/" +"README.postgres for how to setup the respective database." +msgstr "" + +#: contrib/config-daemon.scm:661 contrib/config-daemon.scm:674 +msgid "Which topology should be used?" +msgstr "" + +#: contrib/config-daemon.scm:662 +msgid "Which database should be used for the temporary datastore of the DHT?" +msgstr "" + +#: contrib/config-daemon.scm:676 +msgid "" +"Which topology should be used? The only option at the moment is " +"\"topology_default\"" +msgstr "" + +#: contrib/config-daemon.scm:690 +msgid "" +"The minimum number of connected friends before this peer is allowed to " +"connect to peers that are not listed as friends" +msgstr "" + +#: contrib/config-daemon.scm:691 +msgid "" +"Note that this option does not guarantee that the peer will be able to " +"connect to the specified number of friends. Also, if the peer had connected " +"to a sufficient number of friends and then established non-friend " +"connections, some of the friends may drop out of the network, temporarily " +"resulting in having fewer than the specified number of friends connected " +"while being connected to non-friends. However, it is guaranteed that the " +"peer itself will never choose to drop a friend's connection if this would " +"result in dropping below the specified number of friends (unless that number " +"is higher than the overall connection target)." +msgstr "" + +#: contrib/config-daemon.scm:702 +msgid "" +"If set to YES, the peer is only allowed to connect to other peers that are " +"explicitly specified as friends" +msgstr "" + +#: contrib/config-daemon.scm:703 +msgid "" +"Use YES only if you have (trustworthy) friends that use GNUnet and are " +"afraid of establishing (direct) connections to unknown peers" +msgstr "" + +#: contrib/config-daemon.scm:714 +msgid "List of friends for friend-to-friend topology" +msgstr "" + +#: contrib/config-daemon.scm:715 +msgid "" +"Specifies the name of a file which contains a list of GNUnet peer IDs that " +"are friends. If used with the friend-to-friend topology, this will ensure " +"that GNUnet only connects to these peers (via any available transport)." +msgstr "" + +#: contrib/config-daemon.scm:726 +msgid "Friend-to-Friend Topology Specification" +msgstr "" + +#: contrib/config-daemon.scm:727 +msgid "Settings for restricting connections to friends" +msgstr "" + +#: contrib/config-daemon.scm:744 +msgid "Name of the MySQL database GNUnet should use" +msgstr "" + +#: contrib/config-daemon.scm:756 +msgid "Configuration file that specifies the MySQL username and password" +msgstr "" + +#: contrib/config-daemon.scm:768 +msgid "Configuration of the MySQL database" +msgstr "" + +#: contrib/config-daemon.scm:787 +msgid "MB of diskspace GNUnet can use for anonymous file sharing" +msgstr "" + +#: contrib/config-daemon.scm:789 +msgid "" +"How much disk space (MB) is GNUnet allowed to use for anonymous file " +"sharing? This does not take indexed files into account, only the space " +"directly used by GNUnet is accounted for. GNUnet will gather content from " +"the network if the current space-consumption is below the number given here " +"(and if content migration is allowed below).\n" +"\n" +"Note that if you change the quota, you need to run gnunet-update afterwards." +msgstr "" + +#: contrib/config-daemon.scm:803 +msgid "Number of entries in the migration buffer" +msgstr "" + +#: contrib/config-daemon.scm:804 +msgid "" +"Each entry uses about 32k of memory. More entries can reduce disk IO and " +"CPU usage at the expense of having gnunetd use more memory. Very large " +"values may again increase CPU usage. A value of 0 will prevent your peer " +"from sending unsolicited responses." +msgstr "" + +#: contrib/config-daemon.scm:816 +msgid "Size of the routing table for anonymous routing." +msgstr "" + +#: contrib/config-daemon.scm:828 +msgid "Size of the routing table for DHT routing." +msgstr "" + +#: contrib/config-daemon.scm:841 +msgid "Allow migrating content to this peer." +msgstr "" + +#: contrib/config-daemon.scm:843 +msgid "" +"If you say yes here, GNUnet will migrate content to your server, and you " +"will not be able to control what data is stored on your machine. \n" +"\t\t\t\n" +"If you activate it, you can claim for *all* the non-indexed (-n to gnunet-" +"insert) content that you did not know what it was even if an adversary takes " +"control of your machine. If you do not activate it, it is obvious that you " +"have knowledge of all the content that is hosted on your machine and thus " +"can be considered liable for it." +msgstr "" + +#: contrib/config-daemon.scm:857 +msgid "" +"MB of diskspace GNUnet can use for caching DHT index data (the data will be " +"stored in /tmp)" +msgstr "" + +#: contrib/config-daemon.scm:858 +msgid "" +"DHT index data is inherently small and expires comparatively quickly. It is " +"deleted whenever gnunetd is shut down.\n" +"\n" +"The size of the DSTORE QUOTA is specified in MB." +msgstr "" + +#: contrib/config-daemon.scm:872 +msgid "Options for anonymous file sharing" +msgstr "" + +#: contrib/config-daemon.scm:891 +msgid "Applications" +msgstr "" + +#: contrib/config-daemon.scm:907 +msgid "Is this machine unreachable behind a NAT?" +msgstr "" + +#: contrib/config-daemon.scm:908 +msgid "" +"Set to YES if this machine is behind a NAT that limits connections from the " +"outside to the GNUnet port and that cannot be traversed using UPnP. Note " +"that if you have configured your NAT box to allow direct connections from " +"other machines to the GNUnet ports or if GNUnet can open ports using UPnP, " +"you should set the option to NO. Set this only to YES if other peers cannot " +"contact you directly. You can use 'make check' in src/transports/upnp/ to " +"find out if your NAT supports UPnP. You can also use gnunet-transport-check " +"with the '-p' option in order to determine which setting results in more " +"connections. Use YES only if you get no connections otherwise. Set to AUTO " +"to use YES if the local IP is belongs to a private IP network and NO " +"otherwise." +msgstr "" + +#: contrib/config-daemon.scm:919 +msgid "Which port should be used by the TCP IPv4 transport?" +msgstr "" + +#: contrib/config-daemon.scm:931 contrib/config-daemon.scm:1024 +#: contrib/config-daemon.scm:1174 +msgid "Should we try to determine our external IP using UPnP?" +msgstr "" + +#: contrib/config-daemon.scm:943 +msgid "Which IP(v4)s are not allowed to connect?" +msgstr "" + +#: contrib/config-daemon.scm:955 +msgid "" +"Which IP(v4)s are allowed to connect? Leave empty to use the IP of your " +"primary network interface." +msgstr "" + +#: contrib/config-daemon.scm:967 contrib/config-daemon.scm:1222 +msgid "Which IPv6s are not allowed to connect?" +msgstr "" + +#: contrib/config-daemon.scm:979 contrib/config-daemon.scm:1234 +msgid "" +"Which IPv6s are allowed to connect? Leave empty to allow any IP to connect." +msgstr "" + +#: contrib/config-daemon.scm:992 +msgid "TCP transport" +msgstr "" + +#: contrib/config-daemon.scm:1012 +msgid "Which port should be used by the HTTP transport?" +msgstr "" + +#: contrib/config-daemon.scm:1036 +msgid "Which is the external port of the HTTP transport?" +msgstr "" + +#: contrib/config-daemon.scm:1037 +msgid "" +"Use this option if your firewall maps, say, port 80 to your real HTTP port. " +"This can be useful in making the HTTP messages appear even more legit " +"(without needing to run gnunetd as root due to the use of a privileged port)." +msgstr "" + +#: contrib/config-daemon.scm:1048 +msgid "HTTP transport" +msgstr "" + +#: contrib/config-daemon.scm:1067 +msgid "What is the maximum transfer unit for SMTP?" +msgstr "" + +#: contrib/config-daemon.scm:1079 +msgid "" +"What is the maximum number of e-mails that gnunetd would be allowed to send " +"per hour?" +msgstr "" + +#: contrib/config-daemon.scm:1080 +msgid "Use 0 for unlimited" +msgstr "" + +#: contrib/config-daemon.scm:1091 +msgid "Which e-mail address should be used to send e-mail to this peer?" +msgstr "" + +#: contrib/config-daemon.scm:1092 +msgid "" +"You must make sure that e-mail received at this address is forwarded to the " +"PIPE which is read by gnunetd. Use the FILTER option to filter e-mail with " +"procmail and the PIPE option to set the name of the pipe." +msgstr "" + +#: contrib/config-daemon.scm:1103 +msgid "" +"Which header line should other peers include in e-mails to enable filtering?" +msgstr "" + +#: contrib/config-daemon.scm:1104 +msgid "" +"You can specify a header line here which can then be used by procmail to " +"filter GNUnet e-mail from your inbox and forward it to gnunetd." +msgstr "" + +#: contrib/config-daemon.scm:1115 +msgid "What is the filename of the pipe where gnunetd can read its e-mail?" +msgstr "" + +#: contrib/config-daemon.scm:1116 +msgid "Have a look at contrib/dot-procmailrc for an example .procmailrc file." +msgstr "" + +#: contrib/config-daemon.scm:1127 +msgid "What is the name and port of the server for outgoing e-mail?" +msgstr "" + +#: contrib/config-daemon.scm:1128 +msgid "The basic format is HOSTNAME:PORT." +msgstr "" + +#: contrib/config-daemon.scm:1139 +msgid "SMTP transport" +msgstr "" + +#: contrib/config-daemon.scm:1162 +msgid "Which port should be used by the UDP IPv4 transport?" +msgstr "" + +#: contrib/config-daemon.scm:1186 +msgid "What is the maximum transfer unit for UDP?" +msgstr "" + +#: contrib/config-daemon.scm:1198 +msgid "Which IPs are not allowed to connect?" +msgstr "" + +#: contrib/config-daemon.scm:1210 +msgid "" +"Which IPs are allowed to connect? Leave empty to allow connections from any " +"IP." +msgstr "" + +#: contrib/config-daemon.scm:1246 +msgid "UDP transport" +msgstr "" + +#: contrib/config-daemon.scm:1268 +msgid "Network interface" +msgstr "" + +#: contrib/config-daemon.scm:1280 +msgid "External IP address (leave empty to try auto-detection)" +msgstr "" + +#: contrib/config-daemon.scm:1292 +msgid "External IPv6 address (leave empty to try auto-detection)" +msgstr "" + +#: contrib/config-daemon.scm:1304 +msgid "Transports" +msgstr "" + +#: contrib/config-daemon.scm:1326 +msgid "What is the maximum number of bytes per second that we may receive?" +msgstr "" + +#: contrib/config-daemon.scm:1338 +msgid "What is the maximum number of bytes per second that we may send?" +msgstr "" + +#: contrib/config-daemon.scm:1350 +msgid "What is the maximum CPU load (percentage)?" +msgstr "" + +#: contrib/config-daemon.scm:1351 +msgid "" +"The highest tolerable CPU load. Load here always refers to the total system " +"load, that is it includes CPU utilization by other processes. A value of 50 " +"means that once your 1 minute-load average goes over 50% non-idle, GNUnet " +"will try to reduce CPU consumption until the load goes under the threshold. " +"Reasonable values are typically between 50 and 100. Multiprocessors may use " +"values above 100." +msgstr "" + +#: contrib/config-daemon.scm:1362 +msgid "What is the maximum IO load (permille)?" +msgstr "" + +#: contrib/config-daemon.scm:1364 +msgid "" +"The highest tolerable IO load. Load here refers to the percentage of CPU " +"cycles wasted waiting for IO for the entire system, that is it includes disk " +"utilization by other processes. A value of 10 means that once the average " +"number of cycles wasted waiting for IO is more than 10% non-idle, GNUnet " +"will try to reduce IO until the load goes under the threshold. Reasonable " +"values are typically between 10 and 75." +msgstr "" + +#: contrib/config-daemon.scm:1375 +msgid "What is the maximum CPU load (hard limit)?" +msgstr "" + +#: contrib/config-daemon.scm:1376 +msgid "" +"The highest tolerable CPU load. This is the hard limit, so once it is " +"reached, gnunetd will start to massively drop data to reduce the load. Use " +"with caution." +msgstr "" + +#: contrib/config-daemon.scm:1387 +msgid "What is the maximum upstream bandwidth (hard limit)?" +msgstr "" + +#: contrib/config-daemon.scm:1388 +msgid "" +"The limit is given as a percentage of the MAXNETUPBPS limit. Use 100 to " +"have MAXNETUPBPS be the hard limit. Use zero for no limit." +msgstr "" + +#: contrib/config-daemon.scm:1400 +msgid "What priority should gnunetd use to run?" +msgstr "" + +#: contrib/config-daemon.scm:1401 +msgid "" +"You can specify priorities like NORMAL, ABOVE NORMAL, BELOW NORMAL, HIGH and " +"IDLE or a numerical integer value (man nice). The default is IDLE, which " +"should result in gnunetd only using resources that would otherwise be idle." +msgstr "" + +#: contrib/config-daemon.scm:1413 +msgid "Should we disable random padding (experimental option)?" +msgstr "" + +#: contrib/config-daemon.scm:1425 +msgid "Use basic bandwidth limitation? (YES/NO). If in doubt, say YES." +msgstr "" + +#: contrib/config-daemon.scm:1427 +msgid "" +"Basic bandwidth limitation (YES) means simply that the bandwidth limits " +"specified apply to GNUnet and only to GNUnet. If set to YES, you simply " +"specify the maximum bandwidth (upstream and downstream) that GNUnet is " +"allowed to use and GNUnet will stick to those limitations. This is useful " +"if your overall bandwidth is so large that the limit is mostly used to " +"ensure that enough capacity is left for other applications. Even if you " +"want to dedicate your entire connection to GNUnet you should not set the " +"limits to values higher than what you have since GNUnet uses those limits to " +"determine for example the number of connections to establish (and it would " +"be inefficient if that computation yields a number that is far too high). \n" +"\n" +"While basic bandwidth limitation is simple and always works, there are some " +"situations where it is not perfect. Suppose you are running another " +"application which performs a larger download. During that particular time, " +"it would be nice if GNUnet would throttle its bandwidth consumption " +"(automatically) and resume using more bandwidth after the download is " +"complete. This is obviously advanced magic since GNUnet will have to " +"monitor the behavior of other applications. Another scenario is a monthly " +"cap on bandwidth imposed by your ISP, which you would want to ensure is " +"obeyed. Here, you may want GNUnet to monitor the traffic from other " +"applications to ensure that the combined long-term traffic is within the pre-" +"set bounds. Note that you should probably not set the bounds tightly since " +"GNUnet may observe that the bounds are about to be broken but would be " +"unable to stop other applications from continuing to use bandwidth.\n" +"\n" +"If either of these two scenarios applies, set BASICLIMITING to NO. Then set " +"the bandwidth limits to the COMBINED amount of traffic that is acceptable " +"for both GNUnet and other applications. GNUnet will then immediately " +"throttle bandwidth consumption if the short-term average is above the limit, " +"and it will also try to ensure that the long-term average is below the " +"limit. Note however that using NO can have the effect of GNUnet (almost) " +"ceasing operations after other applications perform high-volume downloads " +"that are beyond the defined limits. GNUnet would reduce consumption until " +"the long-term limits are again within bounds.\n" +"\n" +"NO only works on platforms where GNUnet can monitor the amount of traffic " +"that the local host puts out on the network. This is only implemented for " +"Linux and Win32. In order for the code to work, GNUnet needs to know the " +"specific network interface that is used for the external connection (after " +"all, the amount of traffic on loopback or on the LAN should never be counted " +"since it is irrelevant)." +msgstr "" + +#: contrib/config-daemon.scm:1444 +msgid "Network interface to monitor" +msgstr "" + +#: contrib/config-daemon.scm:1445 +msgid "" +"For which interfaces should we do accounting? GNUnet will evaluate the " +"total traffic (not only the GNUnet related traffic) and adjust its bandwidth " +"usage accordingly. You can currently only specify a single interface. GNUnet " +"will also use this interface to determine the IP to use. Typical values are " +"eth0, ppp0, eth1, wlan0, etc. 'ifconfig' will tell you what you have. " +"Never use 'lo', that just won't work. Under Windows, specify the index " +"number reported by 'gnunet-win-tool -n'." +msgstr "" + +#: contrib/config-daemon.scm:1456 +msgid "Load management" +msgstr "" + +#: contrib/config-daemon.scm:1482 contrib/config-client.scm:413 +msgid "Root node" +msgstr "" + +#: contrib/config-client.scm:153 +msgid "Where should gnunet-clients write their logs?" +msgstr "" + +#: contrib/config-client.scm:185 +msgid "On which machine and port is gnunetd running (for clients)?" +msgstr "" + +#: contrib/config-client.scm:186 +msgid "This is equivalent to the -H option. The format is IP:PORT." +msgstr "" + +#: contrib/config-client.scm:197 +msgid "What is the path to the configuration file for gnunetd?" +msgstr "" + +#: contrib/config-client.scm:198 +msgid "This option is used when clients need to start gnunetd." +msgstr "" + +#: contrib/config-client.scm:210 +msgid "General options" +msgstr "" + +#: contrib/config-client.scm:227 +msgid "Do not add metadata listing the creation time for inserted content" +msgstr "" + +#: contrib/config-client.scm:239 +msgid "Which non-default extractors should GNUnet use for keyword extractors" +msgstr "" + +#: contrib/config-client.scm:240 +msgid "" +"Specify which additional extractor libraries should be used. gnunet-insert " +"uses libextractor to extract keywords from files. libextractor can be " +"dynamically extended to handle additional file formats. If you want to use " +"more than the default set of extractors, specify additional extractor " +"libraries here. The format is [[-]LIBRARYNAME[:[-]LIBRARYNAME]*].\n" +"\n" +"The default is to use filenames and to break larger words at spaces (and " +"underscores, etc.). This should be just fine for most people. The '-' " +"before a library name indicates that this should be executed last and makes " +"only sense for the split-library." +msgstr "" + +#: contrib/config-client.scm:253 +msgid "How many entries should the URI DB table have?" +msgstr "" + +#: contrib/config-client.scm:254 +msgid "" +"GNUnet uses two bytes per entry on the disk. This database is used to keep " +"track of how a particular URI has been used in the past. For example, " +"GNUnet may remember that a particular URI has been found in a search " +"previously or corresponds to a file uploaded by the user. This information " +"can then be used by user-interfaces to filter URI lists, such as search " +"results. If the database is full, older entries will be discarded. The " +"default value should be sufficient without causing undue disk utilization." +msgstr "" + +#: contrib/config-client.scm:265 +msgid "Location of the file specifying metadata for the auto-share directory" +msgstr "" + +#: contrib/config-client.scm:277 +msgid "" +"Location of the file with the PID of any running gnunet-auto-share daemon " +"process" +msgstr "" + +#: contrib/config-client.scm:289 +msgid "Location of the log file for gnunet-auto-share" +msgstr "" + +#: contrib/config-client.scm:301 +msgid "File-Sharing options" +msgstr "" + +#: contrib/config-client.scm:319 +msgid "Which plugins should be loaded by gnunet-gtk?" +msgstr "" + +#: contrib/config-client.scm:320 +msgid "" +"Load the about plugin for the about dialog. The daemon plugin allows " +"starting and stopping of gnunetd and displays information about gnunetd. " +"The fs plugin provides the file-sharing functionality. The stats plugin " +"displays various statistics about gnunetd." +msgstr "" + +#: contrib/config-client.scm:331 +msgid "How frequently (in milli-seconds) should the statistics update?" +msgstr "" + +#: contrib/config-client.scm:332 +msgid "" +"Each pixel in the stats dialog corresponds to the time interval specified " +"here." +msgstr "" + +#: contrib/config-client.scm:344 +msgid "Do not show thumbnail previews from meta-data in search results" +msgstr "" + +#: contrib/config-client.scm:345 +msgid "" +"This option is useful for people who maybe offended by some previews or use " +"gnunet-gtk at work and would like to avoid bad surprises." +msgstr "" + +#: contrib/config-client.scm:356 +msgid "Do not show search results for files that were uploaded by us" +msgstr "" + +#: contrib/config-client.scm:357 +msgid "" +"This option is useful to eliminate files that the user already has from the " +"search. Naturally, enabling this option maybe confusing because some " +"obviously expected search results would no longer show up. This option only " +"works if the URI_DB_SIZE option under FS is not zero (since the URI DB is " +"used to determine which files the user is sharing)" +msgstr "" + +#: contrib/config-client.scm:369 +msgid "To which directory should gnunet-gtk save downloads to?" +msgstr "" + +#: contrib/config-client.scm:381 +msgid "Options related to gnunet-gtk" +msgstr "" + +#: contrib/config-client.scm:401 +msgid "Full pathname of GNUnet client HOME directory" +msgstr "" + +#: contrib/config-client.scm:402 +msgid "The directory for GNUnet files that belong to the user." +msgstr "" diff --git a/po/LINGUAS b/po/LINGUAS new file mode 100644 index 000000000..07778ccfd --- /dev/null +++ b/po/LINGUAS @@ -0,0 +1,2 @@ +# set of available languages +vi de sv es diff --git a/po/Makefile.in.in b/po/Makefile.in.in new file mode 100644 index 000000000..5022b8b18 --- /dev/null +++ b/po/Makefile.in.in @@ -0,0 +1,403 @@ +# Makefile for PO directory in any package using GNU gettext. +# Copyright (C) 1995-1997, 2000-2006 by Ulrich Drepper +# +# This file can be copied and used freely without restrictions. It can +# be used in projects which are not available under the GNU General Public +# License but which still want to provide support for the GNU gettext +# functionality. +# Please note that the actual code of GNU gettext is covered by the GNU +# General Public License and is *not* in the public domain. +# +# Origin: gettext-0.16 + +PACKAGE = @PACKAGE@ +VERSION = @VERSION@ +PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@ + +SHELL = /bin/sh +@SET_MAKE@ + +srcdir = @srcdir@ +top_srcdir = @top_srcdir@ +VPATH = @srcdir@ + +prefix = @prefix@ +exec_prefix = @exec_prefix@ +datarootdir = @datarootdir@ +datadir = @datadir@ +localedir = @localedir@ +gettextsrcdir = $(datadir)/gettext/po + +INSTALL = @INSTALL@ +INSTALL_DATA = @INSTALL_DATA@ + +# We use $(mkdir_p). +# In automake <= 1.9.x, $(mkdir_p) is defined either as "mkdir -p --" or as +# "$(mkinstalldirs)" or as "$(install_sh) -d". For these automake versions, +# @install_sh@ does not start with $(SHELL), so we add it. +# In automake >= 1.10, @mkdir_p@ is derived from ${MKDIR_P}, which is defined +# either as "/path/to/mkdir -p" or ".../install-sh -c -d". For these automake +# versions, $(mkinstalldirs) and $(install_sh) are unused. +mkinstalldirs = $(SHELL) @install_sh@ -d +install_sh = $(SHELL) @install_sh@ +MKDIR_P = @MKDIR_P@ +mkdir_p = @mkdir_p@ + +GMSGFMT_ = @GMSGFMT@ +GMSGFMT_no = @GMSGFMT@ +GMSGFMT_yes = @GMSGFMT_015@ +GMSGFMT = $(GMSGFMT_$(USE_MSGCTXT)) +MSGFMT_ = @MSGFMT@ +MSGFMT_no = @MSGFMT@ +MSGFMT_yes = @MSGFMT_015@ +MSGFMT = $(MSGFMT_$(USE_MSGCTXT)) +XGETTEXT_ = @XGETTEXT@ +XGETTEXT_no = @XGETTEXT@ +XGETTEXT_yes = @XGETTEXT_015@ +XGETTEXT = $(XGETTEXT_$(USE_MSGCTXT)) +MSGMERGE = msgmerge +MSGMERGE_UPDATE = @MSGMERGE@ --update +MSGINIT = msginit +MSGCONV = msgconv +MSGFILTER = msgfilter + +POFILES = @POFILES@ +GMOFILES = @GMOFILES@ +UPDATEPOFILES = @UPDATEPOFILES@ +DUMMYPOFILES = @DUMMYPOFILES@ +DISTFILES.common = Makefile.in.in remove-potcdate.sin \ +$(DISTFILES.common.extra1) $(DISTFILES.common.extra2) $(DISTFILES.common.extra3) +DISTFILES = $(DISTFILES.common) Makevars POTFILES.in \ +$(POFILES) $(GMOFILES) \ +$(DISTFILES.extra1) $(DISTFILES.extra2) $(DISTFILES.extra3) + +POTFILES = \ + +CATALOGS = @CATALOGS@ + +# Makevars gets inserted here. (Don't remove this line!) + +.SUFFIXES: +.SUFFIXES: .po .gmo .mo .sed .sin .nop .po-create .po-update + +.po.mo: + @echo "$(MSGFMT) -c -o $@ $<"; \ + $(MSGFMT) -c -o t-$@ $< && mv t-$@ $@ + +.po.gmo: + @lang=`echo $* | sed -e 's,.*/,,'`; \ + test "$(srcdir)" = . && cdcmd="" || cdcmd="cd $(srcdir) && "; \ + echo "$${cdcmd}rm -f $${lang}.gmo && $(GMSGFMT) -c --statistics -o $${lang}.gmo $${lang}.po"; \ + cd $(srcdir) && rm -f $${lang}.gmo && $(GMSGFMT) -c --statistics -o t-$${lang}.gmo $${lang}.po && mv t-$${lang}.gmo $${lang}.gmo + +.sin.sed: + sed -e '/^#/d' $< > t-$@ + mv t-$@ $@ + + +all: all-@USE_NLS@ + +all-yes: stamp-po +all-no: + +# $(srcdir)/$(DOMAIN).pot is only created when needed. When xgettext finds no +# internationalized messages, no $(srcdir)/$(DOMAIN).pot is created (because +# we don't want to bother translators with empty POT files). We assume that +# LINGUAS is empty in this case, i.e. $(POFILES) and $(GMOFILES) are empty. +# In this case, stamp-po is a nop (i.e. a phony target). + +# stamp-po is a timestamp denoting the last time at which the CATALOGS have +# been loosely updated. Its purpose is that when a developer or translator +# checks out the package via CVS, and the $(DOMAIN).pot file is not in CVS, +# "make" will update the $(DOMAIN).pot and the $(CATALOGS), but subsequent +# invocations of "make" will do nothing. This timestamp would not be necessary +# if updating the $(CATALOGS) would always touch them; however, the rule for +# $(POFILES) has been designed to not touch files that don't need to be +# changed. +stamp-po: $(srcdir)/$(DOMAIN).pot + test ! -f $(srcdir)/$(DOMAIN).pot || \ + test -z "$(GMOFILES)" || $(MAKE) $(GMOFILES) + @test ! -f $(srcdir)/$(DOMAIN).pot || { \ + echo "touch stamp-po" && \ + echo timestamp > stamp-poT && \ + mv stamp-poT stamp-po; \ + } + +# Note: Target 'all' must not depend on target '$(DOMAIN).pot-update', +# otherwise packages like GCC can not be built if only parts of the source +# have been downloaded. + +# This target rebuilds $(DOMAIN).pot; it is an expensive operation. +# Note that $(DOMAIN).pot is not touched if it doesn't need to be changed. +$(DOMAIN).pot-update: $(POTFILES) $(srcdir)/POTFILES.in remove-potcdate.sed + if test -n '$(MSGID_BUGS_ADDRESS)' || test '$(PACKAGE_BUGREPORT)' = '@'PACKAGE_BUGREPORT'@'; then \ + msgid_bugs_address='$(MSGID_BUGS_ADDRESS)'; \ + else \ + msgid_bugs_address='$(PACKAGE_BUGREPORT)'; \ + fi; \ + $(XGETTEXT) --default-domain=$(DOMAIN) --directory=$(top_srcdir) \ + --add-comments=TRANSLATORS: $(XGETTEXT_OPTIONS) \ + --files-from=$(srcdir)/POTFILES.in \ + --copyright-holder='$(COPYRIGHT_HOLDER)' \ + --msgid-bugs-address="$$msgid_bugs_address" + test ! -f $(DOMAIN).po || { \ + if test -f $(srcdir)/$(DOMAIN).pot; then \ + sed -f remove-potcdate.sed < $(srcdir)/$(DOMAIN).pot > $(DOMAIN).1po && \ + sed -f remove-potcdate.sed < $(DOMAIN).po > $(DOMAIN).2po && \ + if cmp $(DOMAIN).1po $(DOMAIN).2po >/dev/null 2>&1; then \ + rm -f $(DOMAIN).1po $(DOMAIN).2po $(DOMAIN).po; \ + else \ + rm -f $(DOMAIN).1po $(DOMAIN).2po $(srcdir)/$(DOMAIN).pot && \ + mv $(DOMAIN).po $(srcdir)/$(DOMAIN).pot; \ + fi; \ + else \ + mv $(DOMAIN).po $(srcdir)/$(DOMAIN).pot; \ + fi; \ + } + +# This rule has no dependencies: we don't need to update $(DOMAIN).pot at +# every "make" invocation, only create it when it is missing. +# Only "make $(DOMAIN).pot-update" or "make dist" will force an update. +$(srcdir)/$(DOMAIN).pot: + $(MAKE) $(DOMAIN).pot-update + +# This target rebuilds a PO file if $(DOMAIN).pot has changed. +# Note that a PO file is not touched if it doesn't need to be changed. +$(POFILES): $(srcdir)/$(DOMAIN).pot + @lang=`echo $@ | sed -e 's,.*/,,' -e 's/\.po$$//'`; \ + if test -f "$(srcdir)/$${lang}.po"; then \ + test "$(srcdir)" = . && cdcmd="" || cdcmd="cd $(srcdir) && "; \ + echo "$${cdcmd}$(MSGMERGE_UPDATE) $${lang}.po $(DOMAIN).pot"; \ + cd $(srcdir) && $(MSGMERGE_UPDATE) $${lang}.po $(DOMAIN).pot; \ + else \ + $(MAKE) $${lang}.po-create; \ + fi + + +install: install-exec install-data +install-exec: +install-data: install-data-@USE_NLS@ + if test "$(PACKAGE)" = "gettext-tools"; then \ + $(mkdir_p) $(DESTDIR)$(gettextsrcdir); \ + for file in $(DISTFILES.common) Makevars.template; do \ + $(INSTALL_DATA) $(srcdir)/$$file \ + $(DESTDIR)$(gettextsrcdir)/$$file; \ + done; \ + for file in Makevars; do \ + rm -f $(DESTDIR)$(gettextsrcdir)/$$file; \ + done; \ + else \ + : ; \ + fi +install-data-no: all +install-data-yes: all + $(mkdir_p) $(DESTDIR)$(datadir) + @catalogs='$(CATALOGS)'; \ + for cat in $$catalogs; do \ + cat=`basename $$cat`; \ + lang=`echo $$cat | sed -e 's/\.gmo$$//'`; \ + dir=$(localedir)/$$lang/LC_MESSAGES; \ + $(mkdir_p) $(DESTDIR)$$dir; \ + if test -r $$cat; then realcat=$$cat; else realcat=$(srcdir)/$$cat; fi; \ + $(INSTALL_DATA) $$realcat $(DESTDIR)$$dir/$(DOMAIN).mo; \ + echo "installing $$realcat as $(DESTDIR)$$dir/$(DOMAIN).mo"; \ + for lc in '' $(EXTRA_LOCALE_CATEGORIES); do \ + if test -n "$$lc"; then \ + if (cd $(DESTDIR)$(localedir)/$$lang && LC_ALL=C ls -l -d $$lc 2>/dev/null) | grep ' -> ' >/dev/null; then \ + link=`cd $(DESTDIR)$(localedir)/$$lang && LC_ALL=C ls -l -d $$lc | sed -e 's/^.* -> //'`; \ + mv $(DESTDIR)$(localedir)/$$lang/$$lc $(DESTDIR)$(localedir)/$$lang/$$lc.old; \ + mkdir $(DESTDIR)$(localedir)/$$lang/$$lc; \ + (cd $(DESTDIR)$(localedir)/$$lang/$$lc.old && \ + for file in *; do \ + if test -f $$file; then \ + ln -s ../$$link/$$file $(DESTDIR)$(localedir)/$$lang/$$lc/$$file; \ + fi; \ + done); \ + rm -f $(DESTDIR)$(localedir)/$$lang/$$lc.old; \ + else \ + if test -d $(DESTDIR)$(localedir)/$$lang/$$lc; then \ + :; \ + else \ + rm -f $(DESTDIR)$(localedir)/$$lang/$$lc; \ + mkdir $(DESTDIR)$(localedir)/$$lang/$$lc; \ + fi; \ + fi; \ + rm -f $(DESTDIR)$(localedir)/$$lang/$$lc/$(DOMAIN).mo; \ + ln -s ../LC_MESSAGES/$(DOMAIN).mo $(DESTDIR)$(localedir)/$$lang/$$lc/$(DOMAIN).mo 2>/dev/null || \ + ln $(DESTDIR)$(localedir)/$$lang/LC_MESSAGES/$(DOMAIN).mo $(DESTDIR)$(localedir)/$$lang/$$lc/$(DOMAIN).mo 2>/dev/null || \ + cp -p $(DESTDIR)$(localedir)/$$lang/LC_MESSAGES/$(DOMAIN).mo $(DESTDIR)$(localedir)/$$lang/$$lc/$(DOMAIN).mo; \ + echo "installing $$realcat link as $(DESTDIR)$(localedir)/$$lang/$$lc/$(DOMAIN).mo"; \ + fi; \ + done; \ + done + +install-strip: install + +installdirs: installdirs-exec installdirs-data +installdirs-exec: +installdirs-data: installdirs-data-@USE_NLS@ + if test "$(PACKAGE)" = "gettext-tools"; then \ + $(mkdir_p) $(DESTDIR)$(gettextsrcdir); \ + else \ + : ; \ + fi +installdirs-data-no: +installdirs-data-yes: + $(mkdir_p) $(DESTDIR)$(datadir) + @catalogs='$(CATALOGS)'; \ + for cat in $$catalogs; do \ + cat=`basename $$cat`; \ + lang=`echo $$cat | sed -e 's/\.gmo$$//'`; \ + dir=$(localedir)/$$lang/LC_MESSAGES; \ + $(mkdir_p) $(DESTDIR)$$dir; \ + for lc in '' $(EXTRA_LOCALE_CATEGORIES); do \ + if test -n "$$lc"; then \ + if (cd $(DESTDIR)$(localedir)/$$lang && LC_ALL=C ls -l -d $$lc 2>/dev/null) | grep ' -> ' >/dev/null; then \ + link=`cd $(DESTDIR)$(localedir)/$$lang && LC_ALL=C ls -l -d $$lc | sed -e 's/^.* -> //'`; \ + mv $(DESTDIR)$(localedir)/$$lang/$$lc $(DESTDIR)$(localedir)/$$lang/$$lc.old; \ + mkdir $(DESTDIR)$(localedir)/$$lang/$$lc; \ + (cd $(DESTDIR)$(localedir)/$$lang/$$lc.old && \ + for file in *; do \ + if test -f $$file; then \ + ln -s ../$$link/$$file $(DESTDIR)$(localedir)/$$lang/$$lc/$$file; \ + fi; \ + done); \ + rm -f $(DESTDIR)$(localedir)/$$lang/$$lc.old; \ + else \ + if test -d $(DESTDIR)$(localedir)/$$lang/$$lc; then \ + :; \ + else \ + rm -f $(DESTDIR)$(localedir)/$$lang/$$lc; \ + mkdir $(DESTDIR)$(localedir)/$$lang/$$lc; \ + fi; \ + fi; \ + fi; \ + done; \ + done + +# Define this as empty until I found a useful application. +installcheck: + +uninstall: uninstall-exec uninstall-data +uninstall-exec: +uninstall-data: uninstall-data-@USE_NLS@ + if test "$(PACKAGE)" = "gettext-tools"; then \ + for file in $(DISTFILES.common) Makevars.template; do \ + rm -f $(DESTDIR)$(gettextsrcdir)/$$file; \ + done; \ + else \ + : ; \ + fi +uninstall-data-no: +uninstall-data-yes: + catalogs='$(CATALOGS)'; \ + for cat in $$catalogs; do \ + cat=`basename $$cat`; \ + lang=`echo $$cat | sed -e 's/\.gmo$$//'`; \ + for lc in LC_MESSAGES $(EXTRA_LOCALE_CATEGORIES); do \ + rm -f $(DESTDIR)$(localedir)/$$lang/$$lc/$(DOMAIN).mo; \ + done; \ + done + +check: all + +info dvi ps pdf html tags TAGS ctags CTAGS ID: + +mostlyclean: + rm -f remove-potcdate.sed + rm -f stamp-poT + rm -f core core.* $(DOMAIN).po $(DOMAIN).1po $(DOMAIN).2po *.new.po + rm -fr *.o + +clean: mostlyclean + +distclean: clean + rm -f Makefile Makefile.in POTFILES *.mo + +maintainer-clean: distclean + @echo "This command is intended for maintainers to use;" + @echo "it deletes files that may require special tools to rebuild." + rm -f stamp-po $(GMOFILES) + +distdir = $(top_builddir)/$(PACKAGE)-$(VERSION)/$(subdir) +dist distdir: + $(MAKE) update-po + @$(MAKE) dist2 +# This is a separate target because 'update-po' must be executed before. +dist2: stamp-po $(DISTFILES) + dists="$(DISTFILES)"; \ + if test "$(PACKAGE)" = "gettext-tools"; then \ + dists="$$dists Makevars.template"; \ + fi; \ + if test -f $(srcdir)/$(DOMAIN).pot; then \ + dists="$$dists $(DOMAIN).pot stamp-po"; \ + fi; \ + if test -f $(srcdir)/ChangeLog; then \ + dists="$$dists ChangeLog"; \ + fi; \ + for i in 0 1 2 3 4 5 6 7 8 9; do \ + if test -f $(srcdir)/ChangeLog.$$i; then \ + dists="$$dists ChangeLog.$$i"; \ + fi; \ + done; \ + if test -f $(srcdir)/LINGUAS; then dists="$$dists LINGUAS"; fi; \ + for file in $$dists; do \ + if test -f $$file; then \ + cp -p $$file $(distdir) || exit 1; \ + else \ + cp -p $(srcdir)/$$file $(distdir) || exit 1; \ + fi; \ + done + +update-po: Makefile + $(MAKE) $(DOMAIN).pot-update + test -z "$(UPDATEPOFILES)" || $(MAKE) $(UPDATEPOFILES) + $(MAKE) update-gmo + +# General rule for creating PO files. + +.nop.po-create: + @lang=`echo $@ | sed -e 's/\.po-create$$//'`; \ + echo "File $$lang.po does not exist. If you are a translator, you can create it through 'msginit'." 1>&2; \ + exit 1 + +# General rule for updating PO files. + +.nop.po-update: + @lang=`echo $@ | sed -e 's/\.po-update$$//'`; \ + if test "$(PACKAGE)" = "gettext-tools"; then PATH=`pwd`/../src:$$PATH; fi; \ + tmpdir=`pwd`; \ + echo "$$lang:"; \ + test "$(srcdir)" = . && cdcmd="" || cdcmd="cd $(srcdir) && "; \ + echo "$${cdcmd}$(MSGMERGE) $$lang.po $(DOMAIN).pot -o $$lang.new.po"; \ + cd $(srcdir); \ + if $(MSGMERGE) $$lang.po $(DOMAIN).pot -o $$tmpdir/$$lang.new.po; then \ + if cmp $$lang.po $$tmpdir/$$lang.new.po >/dev/null 2>&1; then \ + rm -f $$tmpdir/$$lang.new.po; \ + else \ + if mv -f $$tmpdir/$$lang.new.po $$lang.po; then \ + :; \ + else \ + echo "msgmerge for $$lang.po failed: cannot move $$tmpdir/$$lang.new.po to $$lang.po" 1>&2; \ + exit 1; \ + fi; \ + fi; \ + else \ + echo "msgmerge for $$lang.po failed!" 1>&2; \ + rm -f $$tmpdir/$$lang.new.po; \ + fi + +$(DUMMYPOFILES): + +update-gmo: Makefile $(GMOFILES) + @: + +Makefile: Makefile.in.in Makevars $(top_builddir)/config.status @POMAKEFILEDEPS@ + cd $(top_builddir) \ + && $(SHELL) ./config.status $(subdir)/$@.in po-directories + +force: + +# Tell versions [3.59,3.63) of GNU make not to export all variables. +# Otherwise a system limit (for SysV at least) may be exceeded. +.NOEXPORT: diff --git a/po/Makevars b/po/Makevars new file mode 100644 index 000000000..085957181 --- /dev/null +++ b/po/Makevars @@ -0,0 +1,41 @@ +# Makefile variables for PO directory in any package using GNU gettext. + +# Usually the message domain is the same as the package name. +DOMAIN = $(PACKAGE) + +# These two variables depend on the location of this directory. +subdir = po +top_builddir = .. + +# These options get passed to xgettext. +XGETTEXT_OPTIONS = --keyword=_ --keyword=N_ + +# This is the copyright holder that gets inserted into the header of the +# $(DOMAIN).pot file. Set this to the copyright holder of the surrounding +# package. (Note that the msgstr strings, extracted from the package's +# sources, belong to the copyright holder of the package.) Translators are +# expected to transfer the copyright for their translations to this person +# or entity, or to disclaim their copyright. The empty string stands for +# the public domain; in this case the translators are expected to disclaim +# their copyright. +COPYRIGHT_HOLDER = Christian Grothoff + +# This is the email address or URL to which the translators shall report +# bugs in the untranslated strings: +# - Strings which are not entire sentences, see the maintainer guidelines +# in the GNU gettext documentation, section 'Preparing Strings'. +# - Strings which use unclear terms or require additional context to be +# understood. +# - Strings which make invalid assumptions about notation of date, time or +# money. +# - Pluralisation problems. +# - Incorrect English spelling. +# - Incorrect formatting. +# It can be your email address, or a mailing list address where translators +# can write to without being subscribed, or the URL of a web page through +# which the translators can contact you. +MSGID_BUGS_ADDRESS = gnunet-developers@mail.gnu.org + +# This is the list of locale categories, beyond LC_MESSAGES, for which the +# message catalogs shall be used. It is usually empty. +EXTRA_LOCALE_CATEGORIES = diff --git a/po/Makevars.template b/po/Makevars.template new file mode 100644 index 000000000..32692ab4b --- /dev/null +++ b/po/Makevars.template @@ -0,0 +1,41 @@ +# Makefile variables for PO directory in any package using GNU gettext. + +# Usually the message domain is the same as the package name. +DOMAIN = $(PACKAGE) + +# These two variables depend on the location of this directory. +subdir = po +top_builddir = .. + +# These options get passed to xgettext. +XGETTEXT_OPTIONS = --keyword=_ --keyword=N_ + +# This is the copyright holder that gets inserted into the header of the +# $(DOMAIN).pot file. Set this to the copyright holder of the surrounding +# package. (Note that the msgstr strings, extracted from the package's +# sources, belong to the copyright holder of the package.) Translators are +# expected to transfer the copyright for their translations to this person +# or entity, or to disclaim their copyright. The empty string stands for +# the public domain; in this case the translators are expected to disclaim +# their copyright. +COPYRIGHT_HOLDER = Free Software Foundation, Inc. + +# This is the email address or URL to which the translators shall report +# bugs in the untranslated strings: +# - Strings which are not entire sentences, see the maintainer guidelines +# in the GNU gettext documentation, section 'Preparing Strings'. +# - Strings which use unclear terms or require additional context to be +# understood. +# - Strings which make invalid assumptions about notation of date, time or +# money. +# - Pluralisation problems. +# - Incorrect English spelling. +# - Incorrect formatting. +# It can be your email address, or a mailing list address where translators +# can write to without being subscribed, or the URL of a web page through +# which the translators can contact you. +MSGID_BUGS_ADDRESS = + +# This is the list of locale categories, beyond LC_MESSAGES, for which the +# message catalogs shall be used. It is usually empty. +EXTRA_LOCALE_CATEGORIES = diff --git a/po/POTFILES.in b/po/POTFILES.in new file mode 100644 index 000000000..e69de29bb diff --git a/po/Rules-quot b/po/Rules-quot new file mode 100644 index 000000000..9c2a995e3 --- /dev/null +++ b/po/Rules-quot @@ -0,0 +1,47 @@ +# Special Makefile rules for English message catalogs with quotation marks. + +DISTFILES.common.extra1 = quot.sed boldquot.sed en@quot.header en@boldquot.header insert-header.sin Rules-quot + +.SUFFIXES: .insert-header .po-update-en + +en@quot.po-create: + $(MAKE) en@quot.po-update +en@boldquot.po-create: + $(MAKE) en@boldquot.po-update + +en@quot.po-update: en@quot.po-update-en +en@boldquot.po-update: en@boldquot.po-update-en + +.insert-header.po-update-en: + @lang=`echo $@ | sed -e 's/\.po-update-en$$//'`; \ + if test "$(PACKAGE)" = "gettext"; then PATH=`pwd`/../src:$$PATH; GETTEXTLIBDIR=`cd $(top_srcdir)/src && pwd`; export GETTEXTLIBDIR; fi; \ + tmpdir=`pwd`; \ + echo "$$lang:"; \ + ll=`echo $$lang | sed -e 's/@.*//'`; \ + LC_ALL=C; export LC_ALL; \ + cd $(srcdir); \ + if $(MSGINIT) -i $(DOMAIN).pot --no-translator -l $$ll -o - 2>/dev/null | sed -f $$tmpdir/$$lang.insert-header | $(MSGCONV) -t UTF-8 | $(MSGFILTER) sed -f `echo $$lang | sed -e 's/.*@//'`.sed 2>/dev/null > $$tmpdir/$$lang.new.po; then \ + if cmp $$lang.po $$tmpdir/$$lang.new.po >/dev/null 2>&1; then \ + rm -f $$tmpdir/$$lang.new.po; \ + else \ + if mv -f $$tmpdir/$$lang.new.po $$lang.po; then \ + :; \ + else \ + echo "creation of $$lang.po failed: cannot move $$tmpdir/$$lang.new.po to $$lang.po" 1>&2; \ + exit 1; \ + fi; \ + fi; \ + else \ + echo "creation of $$lang.po failed!" 1>&2; \ + rm -f $$tmpdir/$$lang.new.po; \ + fi + +en@quot.insert-header: insert-header.sin + sed -e '/^#/d' -e 's/HEADER/en@quot.header/g' $(srcdir)/insert-header.sin > en@quot.insert-header + +en@boldquot.insert-header: insert-header.sin + sed -e '/^#/d' -e 's/HEADER/en@boldquot.header/g' $(srcdir)/insert-header.sin > en@boldquot.insert-header + +mostlyclean: mostlyclean-quot +mostlyclean-quot: + rm -f *.insert-header diff --git a/po/boldquot.sed b/po/boldquot.sed new file mode 100644 index 000000000..4b937aa51 --- /dev/null +++ b/po/boldquot.sed @@ -0,0 +1,10 @@ +s/"\([^"]*\)"/“\1â€/g +s/`\([^`']*\)'/‘\1’/g +s/ '\([^`']*\)' / ‘\1’ /g +s/ '\([^`']*\)'$/ ‘\1’/g +s/^'\([^`']*\)' /‘\1’ /g +s/“â€/""/g +s/“/“/g +s/â€/â€/g +s/‘/‘/g +s/’/’/g diff --git a/po/de.gmo b/po/de.gmo new file mode 100644 index 000000000..c3cd1fde4 Binary files /dev/null and b/po/de.gmo differ diff --git a/po/de.po b/po/de.po new file mode 100644 index 000000000..b1e089b98 --- /dev/null +++ b/po/de.po @@ -0,0 +1,7387 @@ +# German translations for GNUnet package +# German messages for GNUnet. +# Copyright (C) 2004, 2005 Christian Grothoff +# This file is distributed under the same license as the GNUnet package. +# Christian Grothoff , 2004, 2005. +msgid "" +msgstr "" +"Project-Id-Version: GNUnet 0.7.0b\n" +"Report-Msgid-Bugs-To: gnunet-developers@mail.gnu.org\n" +"POT-Creation-Date: 2009-01-08 10:55-0700\n" +"PO-Revision-Date: 2006-03-17 21:37+0100\n" +"Last-Translator: Nils Durner \n" +"Language-Team: German \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Plural-Forms: nplurals=2; plural=(n != 1);\n" + +#: src/setup/ncurses/wizard_curs.c:72 src/setup/lib/wizard_util.c:155 +#: src/setup/lib/wizard_util.c:210 +msgid "Error" +msgstr "Fehler" + +#: src/setup/ncurses/wizard_curs.c:80 +msgid "Help" +msgstr "Hilfe" + +#: src/setup/ncurses/wizard_curs.c:87 +#, fuzzy +msgid "Error!" +msgstr "Fehler" + +#: src/setup/ncurses/wizard_curs.c:101 src/applications/vpn/cs.c:94 +msgid "No" +msgstr "Nein" + +#: src/setup/ncurses/wizard_curs.c:102 src/applications/vpn/cs.c:94 +msgid "Yes" +msgstr "Ja" + +#: src/setup/ncurses/wizard_curs.c:118 src/setup/ncurses/wizard_curs.c:183 +#: src/setup/ncurses/wizard_curs.c:299 src/setup/ncurses/mconf.c:189 +#: src/setup/ncurses/mconf.c:285 src/setup/ncurses/mconf.c:365 +#: src/setup/ncurses/mconf.c:456 +msgid "Internal error! (Choice invalid?)" +msgstr "" + +#: src/setup/ncurses/wizard_curs.c:150 +#, fuzzy +msgid "Abort" +msgstr "_über" + +#: src/setup/ncurses/wizard_curs.c:151 +#, fuzzy +msgid "Ok" +msgstr "k" + +#: src/setup/ncurses/wizard_curs.c:218 src/setup/ncurses/wizard_curs.c:284 +#: src/setup/ncurses/wizard_curs.c:425 +msgid "GNUnet configuration" +msgstr "GNUnet Konfiguration" + +#: src/setup/ncurses/wizard_curs.c:220 +msgid "" +"Welcome to GNUnet!\n" +"\n" +"This assistant will ask you a few basic questions in order to configure " +"GNUnet.\n" +"\n" +"Please visit our homepage at\n" +"\thttp://gnunet.org/\n" +"and join our community at\n" +"\thttp://gnunet.org/drupal/\n" +"\n" +"Have a lot of fun,\n" +"\n" +"the GNUnet team" +msgstr "" +"Willkommen bei GNUnet!\n" +"\n" +"Dieser Assistent wird Ihnen einige grundlegende Fragen stellen, um GNUnet zu " +"konfigurieren.\n" +"\n" +"Bitte besuchen Sie unsere Homepage\n" +"\thttp://gnunet.org\n" +"und schließen Sie sich unserer Community an:\n" +"\thttps://gnunet.org/drupal/\n" +"\n" +"Viel Spaß,\n" +"\n" +"das GNUnet-Team" + +#: src/setup/ncurses/wizard_curs.c:286 +msgid "" +"Choose the network interface that connects your computer to the internet " +"from the list below." +msgstr "" +"Wählen Sie das Netzwerkgerät, das Ihren Computer mit dem Internet verbindet, " +"aus unten stehender Liste." + +#: src/setup/ncurses/wizard_curs.c:304 src/setup/ncurses/wizard_curs.c:318 +msgid "" +"The \"Network interface\" is the device that connects your computer to the " +"internet. This is usually a modem, an ISDN card or a network card in case " +"you are using DSL." +msgstr "" +"Das \"Netzwerkgerät\" ist das Gerät, das Ihren Computer mit dem Internet " +"verbindet. Dies ist üblicherweise ein Modem, eine ISDN-Karte oder eine " +"Netzwerkkarte falls Sie DSL nutzen." + +#: src/setup/ncurses/wizard_curs.c:315 +#, fuzzy +msgid "Network configuration: interface" +msgstr "Netzwerkgerät:" + +#: src/setup/ncurses/wizard_curs.c:317 +msgid "" +"What is the name of the network interface that connects your computer to the " +"Internet?" +msgstr "" +"Was ist der Name des Netzwerkgerätes, das Ihren Computer mit dem Internet " +"verbindet?" + +#: src/setup/ncurses/wizard_curs.c:328 +#, fuzzy +msgid "Network configuration: IP" +msgstr "GNUnet Konfiguration" + +#: src/setup/ncurses/wizard_curs.c:330 +#, fuzzy +msgid "What is this computer's public IP address or hostname?" +msgstr "" +"Wie heißt die öffentliche IP-Adresse oder der öffentliche Name dieses " +"Computers?\n" +"Wenn Sie nicht sicher sind, lassen Sie dieses Feld leer." + +#: src/setup/ncurses/wizard_curs.c:331 +#, fuzzy +msgid "" +"If your provider always assigns the same IP-Address to you (a \"static\" IP-" +"Address), enter it into the \"IP-Address\" field. If your IP-Address changes " +"every now and then (\"dynamic\" IP-Address) but there's a hostname that " +"always points to your actual IP-Address (\"Dynamic DNS\"), you can also " +"enter it here.\n" +"If left empty, GNUnet will try to automatically detect the IP.\n" +"You can specify a hostname, GNUnet will then use DNS to resolve it.\n" +"If in doubt, leave this empty." +msgstr "" +"Wenn Ihr Provider Ihnen immer die gleiche IP-Adresse zuweist (eine " +"\"statische\" IP-Adresse), so geben Sie diese in das \"IP-Adresse\"-Feld " +"ein. Wenn sich Ihre IP-Adresse immer wieder ändert (\"dynamische\" IP-" +"Adresse), es jedoch einen Rechnernamen gibt, der immer auf Ihre aktuelle IP-" +"Adresse verweist, so können Sie diesen ebenfalls eintragen.\n" +"Wenn Sie nicht sicher sind, so können Sie das Feld leer lassen. GNUnet wird " +"dann versuchen, Ihre IP-Adresse automatisch zu bestimmen." + +#: src/setup/ncurses/wizard_curs.c:346 +#, fuzzy +msgid "Bandwidth configuration: upload" +msgstr "GNUnet Konfiguration" + +#: src/setup/ncurses/wizard_curs.c:348 +#, fuzzy +msgid "How much upstream bandwidth (in bytes/s) may be used?" +msgstr "Wieviel Upstream (Bytes/s) darf verwendet werden?" + +#: src/setup/ncurses/wizard_curs.c:349 +#, fuzzy +msgid "" +"You can limit GNUnet's resource usage here.\n" +"\n" +"The \"upstream\" is the data channel through which data is *sent* to the " +"internet. The limit is the maximum amount which GNUnet is allowed to use. If " +"you have a flatrate, you can set it to the maximum speed of your internet " +"connection. You should not use a value that is higher than what your actual " +"connection allows." +msgstr "" +"Hier können Sie GNUnets Ressourcenverwendung einschränken.\n" +"\n" +"Der \"Upstream\" ist der Datenkanal, durch den Daten an das Internet " +"*gesendet* werden. Das Limit ist entweder das gesamte Maximum für diesem " +"Computer oder wieviel GNUnet selbst verwenden darf. Dies können Sie später " +"festlegen. Wenn Sie eine Flatrate haben, können Sie die maximale " +"Geschwindigkeit Ihrer Internetverbindung angeben." + +#: src/setup/ncurses/wizard_curs.c:361 +#, fuzzy +msgid "Bandwidth configuration: download" +msgstr "GNUnet Konfiguration" + +#: src/setup/ncurses/wizard_curs.c:363 +#, fuzzy +msgid "How much downstream bandwidth (in bytes/s) may be used?" +msgstr "Wieviel Downstream (Bytes/s) darf verwendet werden?" + +#: src/setup/ncurses/wizard_curs.c:364 +#, fuzzy +msgid "" +"You can limit GNUnet's resource usage here.\n" +"\n" +"The \"downstream\" is the data channel through which data is *received* from " +"the internet. The limit is the maximum amount which GNUnet is allowed to " +"use. If you have a flatrate, you can set it to the maximum speed of your " +"internet connection. You should not use a value that is higher than what " +"your actual connection allows." +msgstr "" +"Hier können Sie GNUnets Ressourcenverwendung einschränken.\n" +"\n" +"Der \"Downstream\" ist der Datenkanal, durch den Daten vom Internet " +"*empfangen* werden. Das Limit ist entweder das gesamte Maximum für diesem " +"Computer oder wieviel GNUnet selbst verwenden darf. Dies können Sie später " +"festlegen. Wenn Sie eine Flatrate haben, können Sie die maximale " +"Geschwindigkeit Ihrer Internetverbindung angeben." + +#: src/setup/ncurses/wizard_curs.c:376 +#, fuzzy +msgid "Quota configuration" +msgstr "GNUnet Konfiguration" + +#: src/setup/ncurses/wizard_curs.c:377 +msgid "What is the maximum size of the datastore in MB?" +msgstr "" + +#: src/setup/ncurses/wizard_curs.c:378 +#, fuzzy +msgid "" +"The GNUnet datastore contains all content that GNUnet needs to store " +"(indexed, inserted and migrated content)." +msgstr "" +"Was ist die maximale Größe des GNUnet Datenspeichers (in MB)?\n" +"Der GNUnet Datenspeicher enthält alle Daten, die GNUnet erzeugt (Indexdaten, " +"eingefügte und migrierte Inhalte)." + +#: src/setup/ncurses/wizard_curs.c:390 +#, fuzzy +msgid "Daemon configuration: user account" +msgstr "Fehler beim Anlegen des Benutzerkontos:" + +#: src/setup/ncurses/wizard_curs.c:391 +msgid "As which user should gnunetd be run?" +msgstr "" + +#: src/setup/ncurses/wizard_curs.c:393 +#, fuzzy +msgid "" +"For security reasons, it is a good idea to let this setup create a new user " +"account under which the GNUnet service is started at system startup.\n" +"\n" +"However, GNUnet may not be able to access files other than its own. This " +"includes files you want to publish in GNUnet. You'll have to grant read " +"permissions to the user specified below.\n" +"\n" +"Leave the field empty to run GNUnet with system privileges.\n" +msgstr "" +"Geben Sie den Benutzer an, dem der GNUnet Dienst gehören soll.\n" +"\n" +"Aus Sicherheitsgründen ist es eine gute Idee, dieses Setup ein neues " +"Benutzerkonto anlegen zu lassen, unter dem der GNUnet Dienst beim " +"Systemstart läuft.\n" +"\n" +"Natürlich kann GNUnet dann nur auf seine eigenen Dateien zugreifen. Dies " +"betrifft auch Dateien, die Sie im GNUnet veröffentlichen möchten. Sie müssen " +"dann dem unten angegebenen Benutzerkonto zuerst Leseberechtigungen geben.\n" +"\n" +"Lassen Sie dieses Feld leer, wenn Sie GNUnet mit Systemprivilegien laufen " +"lassen möchten.\n" +"\n" +"GNUnet Benutzer:" + +#: src/setup/ncurses/wizard_curs.c:410 +msgid "Daemon configuration: group account" +msgstr "" + +#: src/setup/ncurses/wizard_curs.c:411 +msgid "As which group should gnunetd be run?" +msgstr "" + +#: src/setup/ncurses/wizard_curs.c:413 +#, fuzzy +msgid "" +"For security reasons, it is a good idea to let this setup create a new group " +"for the chosen user account.\n" +"\n" +"You can also specify a already existent group here.\n" +"\n" +"Only members of this group will be allowed to start and stop the the GNUnet " +"server and have access to GNUnet server data.\n" +msgstr "" +"Definieren Sie eine Gruppe, der der GNUnet Dienst gehört.\n" +"\n" +"Aus Sicherheitsgründen ist es eine gute Idee, dieses Setup eine neue Gruppe " +"für das gewählte Benutzerkonto anlegen zu lassen.\n" +"\n" +"Sie können hier auch eine bestehende Gruppe angeben.\n" +"\n" +"Nur Mitglieder dieser Gruppe dürfen den GNUnet Server starten und anhalten " +"und haben Zugriff auf die Daten des GNUnet Servers.\n" +"\n" +"GNUnet Gruppe:" + +#: src/setup/ncurses/wizard_curs.c:427 +msgid "Do you want to automatically launch GNUnet as a system service?" +msgstr "" + +#: src/setup/ncurses/wizard_curs.c:429 +#, fuzzy +msgid "" +"If you say \"yes\" here, the GNUnet background process will be automatically " +"started when you turn on your computer. If you say \"no\" here, you have to " +"launch GNUnet yourself each time you want to use it." +msgstr "" +"Möchten Sie GNUnet als Systemdienst starten?\n" +"\n" +"Wenn Sie hier \"Ja\" sagen, so wird der GNUnet Hintergrundprozess jedesmal " +"automatisch gestartet, wenn Sie Ihren Computer einschalten. Wenn Sie hier " +"\"Nein\" sagen, so müssen Sie GNUnet jedesmal selbst starten, wenn Sie es " +"verwenden möchten." + +#: src/setup/ncurses/wizard_curs.c:452 src/setup/gtk/wizard_gtk.c:414 +#, c-format +msgid "Unable to save configuration file `%s':" +msgstr "Fehler beim Speichern der Konfigurationsdatei `%s':" + +#: src/setup/ncurses/wizard_curs.c:472 +#, fuzzy +msgid "Unable to create user account for daemon." +msgstr "Fehler beim Anlegen des Benutzerkontos:" + +#: src/setup/ncurses/wizard_curs.c:483 +msgid "Unable to setup autostart for daemon." +msgstr "" + +#: src/setup/ncurses/wizard_curs.c:498 +#, fuzzy +msgid "Save configuration?" +msgstr "GNUnet Konfiguration" + +#: src/setup/ncurses/wizard_curs.c:499 +#, fuzzy +msgid "Save configuration now?" +msgstr "GNUnet Konfiguration" + +#: src/setup/ncurses/wizard_curs.c:539 src/setup/ncurses/mconf.c:500 +#, fuzzy +msgid "GNUnet Configuration" +msgstr "GNUnet Konfiguration" + +#: src/setup/ncurses/wizard_curs.c:543 +msgid "Back" +msgstr "Zurück" + +#: src/setup/ncurses/mconf.c:96 +msgid "Exit" +msgstr "" + +#: src/setup/ncurses/mconf.c:99 +msgid "Up" +msgstr "Oben" + +#: src/setup/ncurses/mconf.c:102 +msgid "Cancel" +msgstr "Abbrechen" + +#: src/setup/ncurses/mconf.c:221 src/setup/ncurses/mconf.c:408 +msgid "Internal error! (Value invalid?)" +msgstr "" + +#: src/setup/ncurses/mconf.c:398 +msgid "Invalid input, expecting floating point value." +msgstr "" + +#: src/setup/ncurses/mconf.c:439 +msgid "Invalid input, expecting integer." +msgstr "" + +#: src/setup/ncurses/mconf.c:446 +msgid "Value is not in legal range." +msgstr "" + +#: src/setup/ncurses/mconf.c:512 src/setup/text/conf.c:569 +#, fuzzy, c-format +msgid "Configuration unchanged, no need to save.\n" +msgstr "" +"Konfiguration oder die GNUnet Version hat sich geändert. Sie müssen `%s' " +"ausführen!\n" + +#: src/setup/ncurses/mconf.c:518 +#, fuzzy +msgid "Do you wish to save your new configuration?" +msgstr "Möchten Sie Ihre Einstellungen speichern?" + +#: src/setup/ncurses/mconf.c:532 +#, fuzzy, c-format +msgid "" +"\n" +"End of configuration.\n" +msgstr " gconfig\tGTK Konfiguration\n" + +#: src/setup/ncurses/mconf.c:537 +#, fuzzy, c-format +msgid "" +"\n" +"Your configuration changes were NOT saved.\n" +msgstr "Konfigurationsdatei `%s' wurde erzeugt.\n" + +#: src/setup/gnunet-win-tool.c:52 +msgid "list all network adapters" +msgstr "" + +#: src/setup/gnunet-win-tool.c:55 +msgid "install GNUnet as Windows service" +msgstr "" + +#: src/setup/gnunet-win-tool.c:58 +msgid "uninstall GNUnet service" +msgstr "" + +#: src/setup/gnunet-win-tool.c:61 +msgid "increase the maximum number of TCP/IP connections" +msgstr "" + +#: src/setup/gnunet-win-tool.c:64 +msgid "display a file's hash value" +msgstr "" + +#: src/setup/gnunet-win-tool.c:125 +#, c-format +msgid "GNUnet service installed successfully.\n" +msgstr "Der GNUnet Dienst wurde erfolgreich installiert.\n" + +#: src/setup/gnunet-win-tool.c:128 src/setup/gnunet-win-tool.c:156 +#, c-format +msgid "This version of Windows doesn't support services.\n" +msgstr "Diese Version von Windows unterstützt keine Dienste.\n" + +#: src/setup/gnunet-win-tool.c:132 src/setup/gnunet-win-tool.c:160 +#, c-format +msgid "Error: can't open Service Control Manager: %s\n" +msgstr "Fehler: der Dienstemanager konnte nicht geöffnet werden: %s\n" + +#: src/setup/gnunet-win-tool.c:137 +#, c-format +msgid "Error: can't create service: %s\n" +msgstr "Fehler: Dienst konnte nicht angelegt werden: %s\n" + +#: src/setup/gnunet-win-tool.c:140 src/setup/gnunet-win-tool.c:172 +#, c-format +msgid "Unknown error.\n" +msgstr "Unbekannter Fehler.\n" + +#: src/setup/gnunet-win-tool.c:153 +#, c-format +msgid "Service deleted.\n" +msgstr "Dienst gelöscht.\n" + +#: src/setup/gnunet-win-tool.c:165 +#, c-format +msgid "Error: can't access service: %s\n" +msgstr "Fehler: auf den Dienst konnte nicht zugegriffen werden: %s\n" + +#: src/setup/gnunet-win-tool.c:169 +#, c-format +msgid "Error: can't delete service: %s\n" +msgstr "Fehler: Dienst konnte nicht gelöscht werden: %s\n" + +#: src/setup/gtk/ngconf.c:389 +#, fuzzy +msgid "Configuration saved." +msgstr "GNUnet Konfiguration" + +#: src/setup/gtk/ngconf.c:399 +#, fuzzy +msgid "Failed to save configuration." +msgstr "Fehler beim Speichern der Konfiguration!" + +#: src/setup/gtk/ngconf.c:424 +#, fuzzy +msgid "Configuration changed. Save?" +msgstr "" +"Konfiguration oder die GNUnet Version hat sich geändert. Sie müssen `%s' " +"ausführen!\n" + +#: src/setup/gtk/ngconf.c:437 +#, fuzzy +msgid "Error saving configuration." +msgstr "Fehler beim Speichern der Konfiguration!" + +#: src/setup/gtk/wizard_gtk.c:141 +#, fuzzy +msgid "(unknown connection)" +msgstr "Netzwerkverbindung" + +#: src/setup/gtk/wizard_gtk.c:438 +#, fuzzy +msgid "Do you want to save the new configuration?" +msgstr "Möchten Sie Ihre Einstellungen speichern?" + +#: src/setup/gtk/wizard_gtk.c:470 +msgid "Unable to create user account:" +msgstr "Fehler beim Anlegen des Benutzerkontos:" + +#: src/setup/gtk/wizard_gtk.c:480 +msgid "Unable to change startup process:" +msgstr "Startprozeß konnte nicht geändert werden:" + +#: src/setup/gtk/wizard_gtk.c:495 +msgid "" +"Running gnunet-update failed.\n" +"This maybe due to insufficient permissions, please check your " +"configuration.\n" +"Finally, run gnunet-update manually." +msgstr "" + +#: src/setup/gnunet-setup.c:65 +#, c-format +msgid "Can only set one option per invocation.\n" +msgstr "" + +#: src/setup/gnunet-setup.c:73 +#, c-format +msgid "" +"Invalid syntax, argument to 'set' must have the format SECTION:" +"OPTION=VALUE.\n" +msgstr "" + +#: src/setup/gnunet-setup.c:87 +#, c-format +msgid "Can only display one option per invocation.\n" +msgstr "" + +#: src/setup/gnunet-setup.c:94 +#, c-format +msgid "" +"Invalid syntax, argument to 'get' must have the format SECTION:OPTION.\n" +msgstr "" + +#: src/setup/gnunet-setup.c:108 +msgid "generate configuration for gnunetd, the GNUnet daemon" +msgstr "Generiere Konfiguration für gnunetd, den GNUnet Daemon" + +#: src/setup/gnunet-setup.c:111 src/setup/gnunet-setup.c:127 +#: src/server/gnunet-update.c:268 +msgid "print a value from the configuration file to stdout" +msgstr "" +"Einen Wert aus der Konfigurationsdatei auf der Standardausgabe ausgeben" + +#: src/setup/gnunet-setup.c:113 src/setup/gnunet-setup.c:129 +msgid "Tool to setup GNUnet." +msgstr "Werkzeug für die Einrichtung von GNUnet." + +#: src/setup/gnunet-setup.c:115 src/setup/gnunet-setup.c:131 +#, fuzzy +msgid "update a value in the configuration file" +msgstr "" +"Einen Wert aus der Konfigurationsdatei auf der Standardausgabe ausgeben" + +#: src/setup/gnunet-setup.c:338 +#, fuzzy, c-format +msgid "Too many arguments.\n" +msgstr "Ungültige Kommandozeilen Parameter.\n" + +#: src/setup/gnunet-setup.c:344 +#, fuzzy +msgid "No interface specified, using default.\n" +msgstr "Keine Oberfläche angegeben, verwende Standard\n" + +#: src/setup/gnunet-setup.c:392 +#, fuzzy, c-format +msgid "Configuration file `%s' must be a filename (but is a directory).\n" +msgstr "" +"Konfigurationsdatei `%s' nicht gefunden. Bitte führen Sie gnunet-setup aus!\n" + +#: src/setup/gnunet-setup.c:439 +#, fuzzy, c-format +msgid "Undefined option.\n" +msgstr "Weitere Einstellungen" + +#: src/setup/gnunet-setup.c:496 +#, fuzzy, c-format +msgid "`%s' is not available.\n" +msgstr "`%s' ist nicht verfügbar." + +#: src/setup/gnunet-setup.c:516 +#, fuzzy, c-format +msgid "Unknown operation '%s'.\n" +msgstr "Unbekannte Operation `%s'\n" + +#: src/setup/gnunet-setup.c:517 src/util/getopt/getopt.c:1072 +#, c-format +msgid "Use --help to get a list of options.\n" +msgstr "Verwenden Sie --help, um eine Liste der Optionen zu erhalten.\n" + +#: src/setup/text/conf.c:79 +#, fuzzy +msgid "yes" +msgstr "Ja" + +#: src/setup/text/conf.c:80 +msgid "no" +msgstr "" + +#: src/setup/text/conf.c:106 +#, c-format +msgid "\tEnter yes (%s), no (%s) or help (%s): " +msgstr "" + +#: src/setup/text/conf.c:115 +msgid "\tPossible choices:\n" +msgstr "" + +#: src/setup/text/conf.c:123 +msgid "\tUse single space prefix to avoid conflicts with hotkeys!\n" +msgstr "" + +#: src/setup/text/conf.c:125 +#, c-format +msgid "\tEnter string (type '%s' for default value `%s'): " +msgstr "" + +#: src/setup/text/conf.c:143 +#, c-format +msgid "\t Enter choice (default is %c): " +msgstr "" + +#: src/setup/text/conf.c:147 +#, c-format +msgid "\tEnter floating point (type '%s' for default value %f): " +msgstr "" + +#: src/setup/text/conf.c:153 +#, c-format +msgid "" +"\tEnter unsigned integer in interval [%llu,%llu] (type '%s' for default " +"value %llu): " +msgstr "" + +#: src/setup/text/conf.c:187 +#, fuzzy, c-format +msgid "Yes\n" +msgstr "Ja" + +#: src/setup/text/conf.c:192 +#, fuzzy, c-format +msgid "No\n" +msgstr "Nein" + +#: src/setup/text/conf.c:195 src/setup/text/conf.c:236 +#: src/setup/text/conf.c:266 src/setup/text/conf.c:329 +#: src/setup/text/conf.c:387 +#, fuzzy, c-format +msgid "Help\n" +msgstr "Hilfe" + +#: src/setup/text/conf.c:198 src/setup/text/conf.c:213 +#: src/setup/text/conf.c:276 src/setup/text/conf.c:307 +#: src/setup/text/conf.c:365 +#, fuzzy, c-format +msgid "Abort\n" +msgstr "_über" + +#: src/setup/text/conf.c:354 src/setup/text/conf.c:416 +#, c-format +msgid "" +"\n" +"Invalid entry, try again (use '?' for help): " +msgstr "" + +#: src/setup/text/conf.c:422 +#, c-format +msgid "Unknown kind %x (internal error). Skipping option.\n" +msgstr "" + +#: src/setup/text/conf.c:484 +msgid "\tDescend? (y/n/?) " +msgstr "" + +#: src/setup/text/conf.c:493 +msgid "Aborted.\n" +msgstr "" + +#: src/setup/text/conf.c:506 +#, fuzzy +msgid "Invalid entry.\n" +msgstr "Ungültiger Parameter: `%s'\n" + +#: src/setup/text/conf.c:524 +#, c-format +msgid "Unknown kind %x (internal error). Aborting.\n" +msgstr "" + +#: src/setup/text/conf.c:556 +#, c-format +msgid "You can always press ENTER to keep the current value.\n" +msgstr "" + +#: src/setup/text/conf.c:557 +#, c-format +msgid "Use the '%s' key to abort.\n" +msgstr "" + +#: src/setup/text/conf.c:575 +#, c-format +msgid "" +"Save configuration? Answer 'y' for yes, 'n' for no, 'r' to repeat " +"configuration. " +msgstr "" + +#: src/setup/text/conf.c:590 +#, fuzzy, c-format +msgid "Configuration was unchanged, no need to save.\n" +msgstr "" +"Konfiguration oder die GNUnet Version hat sich geändert. Sie müssen `%s' " +"ausführen!\n" + +#: src/setup/text/conf.c:599 +#, fuzzy, c-format +msgid "Configuration file `%s' written.\n" +msgstr "Konfigurationsdatei `%s' wurde erzeugt.\n" + +#: src/setup/lib/tree.c:191 +#, c-format +msgid "" +"Internal error: entry `%s' in section `%s' not found for visibility change!\n" +msgstr "" + +#: src/setup/lib/wizard_util.c:126 +msgid "Can't open Service Control Manager" +msgstr "Der Dienstemanager konnte nicht geöffnet werden" + +#: src/setup/lib/wizard_util.c:132 +msgid "Can't create service" +msgstr "Der Dienst konnte nicht angelegt werden" + +#: src/setup/lib/wizard_util.c:136 +msgid "Error changing the permissions of the GNUnet directory" +msgstr "Fehler beim Ändern der Berechtigungen des GNUnet Verzeichnisses" + +#: src/setup/lib/wizard_util.c:141 +#, fuzzy +msgid "Cannot write to the registry" +msgstr "Konnte nicht in die Registry schreiben" + +#: src/setup/lib/wizard_util.c:144 +msgid "Can't access the service" +msgstr "Auf den Dienst konnte nicht zugegriffen werden" + +#: src/setup/lib/wizard_util.c:147 +msgid "Can't delete the service" +msgstr "Dienst konnte nicht gelöscht werden" + +#: src/setup/lib/wizard_util.c:150 +msgid "Unknown error" +msgstr "Unbekannter Fehler" + +#: src/setup/lib/wizard_util.c:186 +msgid "This version of Windows does not support multiple users." +msgstr "Diese Version von Windows ist nicht Mehrbenutzerfähig." + +#: src/setup/lib/wizard_util.c:190 +msgid "Error creating user" +msgstr "Fehler beim Anlegen des Benutzers" + +#: src/setup/lib/wizard_util.c:194 +msgid "Error accessing local security policy" +msgstr "Fehler beim Zugriff auf die lokale Sicherheitsrichtlinie" + +#: src/setup/lib/wizard_util.c:199 +msgid "Error granting service right to user" +msgstr "Fehler beim Zuweisen des Dienstrechtes dem Benutzer" + +#: src/setup/lib/wizard_util.c:204 +msgid "Unknown error while creating a new user" +msgstr "Unbekannter Fehler beim Anlegen des neuen Benutzers" + +#: src/setup/lib/gns.c:297 +#, fuzzy, c-format +msgid "" +"Configuration does not satisfy constraints of configuration specification " +"file `%s'!\n" +msgstr "" +"Die Konfigurationsdatei muss in der Sektion `%s' unter `%s' ein Verzeichnis " +"für FS Daten angeben.\n" + +#: src/util/disk/storage.c:172 +#, fuzzy, c-format +msgid "`%s' failed for drive `%s': %u\n" +msgstr "`%s' fehlgeschlagen für Laufwerk %s: %u\n" + +#: src/util/disk/storage.c:524 +#, fuzzy, c-format +msgid "Expected `%s' to be a directory!\n" +msgstr "`%s' erwartet, dass `%s' ein Verzeichnis ist!\n" + +#: src/util/error/error.c:152 +#, c-format +msgid "Message `%.*s' repeated %u times in the last %llus\n" +msgstr "" + +#: src/util/error/error.c:254 +#, c-format +msgid "" +"\n" +"Press any key to continue\n" +msgstr "" + +#: src/util/error/error.c:336 src/util/error/error.c:371 +msgid "DEBUG" +msgstr "DEBUG" + +#: src/util/error/error.c:338 src/util/error/error.c:373 +msgid "STATUS" +msgstr "" + +#: src/util/error/error.c:340 src/util/error/error.c:377 +msgid "WARNING" +msgstr "WARNUNG" + +#: src/util/error/error.c:342 src/util/error/error.c:379 +msgid "ERROR" +msgstr "FEHLER" + +#: src/util/error/error.c:344 src/util/error/error.c:381 +msgid "FATAL" +msgstr "SCHWERWIEGEND" + +#: src/util/error/error.c:346 src/util/error/error.c:383 +msgid "USER" +msgstr "" + +#: src/util/error/error.c:348 src/util/error/error.c:385 +msgid "ADMIN" +msgstr "" + +#: src/util/error/error.c:350 src/util/error/error.c:387 +msgid "DEVELOPER" +msgstr "" + +#: src/util/error/error.c:352 src/util/error/error.c:389 +msgid "REQUEST" +msgstr "" + +#: src/util/error/error.c:354 src/util/error/error.c:391 +msgid "BULK" +msgstr "" + +#: src/util/error/error.c:356 src/util/error/error.c:393 +msgid "IMMEDIATE" +msgstr "" + +#: src/util/error/error.c:358 +msgid "ALL" +msgstr "" + +#: src/util/error/error.c:375 +msgid "INFO" +msgstr "INFO" + +#: src/util/error/error.c:394 +msgid "NOTHING" +msgstr "NICHTS" + +#: src/util/network_client/tcpio.c:98 src/util/network_client/tcpio.c:154 +msgid "Could not find valid value for HOST in section NETWORK.\n" +msgstr "" + +#: src/util/network_client/tcpio.c:123 +#, fuzzy, c-format +msgid "Syntax error in configuration entry HOST in section NETWORK: `%s'\n" +msgstr "Syntaxfehler in Konfigurationsdatei `%s' in Zeile %d.\n" + +#: src/util/network_client/tcpio.c:335 +#, fuzzy, c-format +msgid "Error connecting to %s:%u. Is the daemon running?\n" +msgstr "Verbindung zu %u.%u.%u.%u:%u fehlgeschlagen: %s\n" + +#: src/util/network_client/tcpio.c:398 +#, fuzzy, c-format +msgid "Cannot connect to %s:%u: %s\n" +msgstr "Verbindung zu %u.%u.%u.%u:%u fehlgeschlagen: %s\n" + +#: src/util/network_client/tcpio.c:636 +#, fuzzy +msgid "Reading result from gnunetd failed, reply invalid!\n" +msgstr "`%s' fehlgeschlagen, Antwort ist ungültig!\n" + +#: src/util/getopt/setoption.c:59 +#, c-format +msgid "" +"Setting option `%s' in section `%s' to `%s' when processing command line " +"option `%s' was denied.\n" +msgstr "" + +#: src/util/getopt/setoption.c:138 src/util/getopt/setoption.c:155 +#, c-format +msgid "You must pass a number to the `%s' option.\n" +msgstr "Sie müssen für die Option `%s' zusätzlich eine Zahl angeben.\n" + +#: src/util/getopt/printhelp.c:49 +#, c-format +msgid "" +"Arguments mandatory for long options are also mandatory for short options.\n" +msgstr "" +"Parameter, die für lange Optionen zwingend sind, sind auch für kurze " +"Optionen zwingend.\n" + +#: src/util/getopt/getopt.c:684 +#, c-format +msgid "%s: option `%s' is ambiguous\n" +msgstr "%s: Option `%s' ist mehrdeutig\n" + +#: src/util/getopt/getopt.c:710 +#, c-format +msgid "%s: option `--%s' does not allow an argument\n" +msgstr "%s: Option '--%s' erlaubt keinen Parameter\n" + +#: src/util/getopt/getopt.c:716 +#, c-format +msgid "%s: option `%c%s' does not allow an argument\n" +msgstr "%s: Option '%c%s' erlaubt keinen Parameter\n" + +#: src/util/getopt/getopt.c:737 src/util/getopt/getopt.c:909 +#, c-format +msgid "%s: option `%s' requires an argument\n" +msgstr "%s: Option `%s' benötigt einen Parameter\n" + +#: src/util/getopt/getopt.c:767 +#, c-format +msgid "%s: unrecognized option `--%s'\n" +msgstr "%s: unbekannte Option '--%s'\n" + +#: src/util/getopt/getopt.c:771 +#, c-format +msgid "%s: unrecognized option `%c%s'\n" +msgstr "%s: unbekannte Option '%c%s'\n" + +#: src/util/getopt/getopt.c:797 +#, c-format +msgid "%s: illegal option -- %c\n" +msgstr "%s: unerlaubte Option -- %c\n" + +#: src/util/getopt/getopt.c:799 +#, c-format +msgid "%s: invalid option -- %c\n" +msgstr "%s: ungültige Option -- %c\n" + +#: src/util/getopt/getopt.c:828 src/util/getopt/getopt.c:958 +#, c-format +msgid "%s: option requires an argument -- %c\n" +msgstr "%s: Option benötigt einen Parameter -- %c\n" + +#: src/util/getopt/getopt.c:876 +#, c-format +msgid "%s: option `-W %s' is ambiguous\n" +msgstr "%s: Option '-W %s' ist mehrdeutig\n" + +#: src/util/getopt/getopt.c:894 +#, c-format +msgid "%s: option `-W %s' does not allow an argument\n" +msgstr "%s: Option '-W %s' erlaubt keinen Parameter\n" + +#: src/util/network/ip.c:96 src/util/network/ip.c:160 src/transports/ip.c:260 +#, fuzzy, c-format +msgid "No interface specified in section `%s' under `%s'!\n" +msgstr "" +"Es sind keine Netzwerkgeräte in der Konfigurationsdatei in der Sektion `%s' " +"unter `%s' definiert!\n" + +#: src/util/network/ip.c:127 src/util/network/ip.c:246 src/transports/ip.c:291 +#, c-format +msgid "Could not obtain IP for interface `%s' using `%s'.\n" +msgstr "" +"Es konnte keine IP für das Gerät `%s' unter Verwendung von `%s' ermittelt " +"werden.\n" + +#: src/util/network/ip.c:216 +#, fuzzy, c-format +msgid "" +"Could not find interface `%s' using `%s', trying to find another interface.\n" +msgstr "" +"Gerät `%s' konnte in `%s' nicht gefunden werden, es wird versucht, ein " +"anderes Gerät zu finden.\n" + +#: src/util/network/ip.c:295 +#, c-format +msgid "Could not find an IP address for interface `%s'.\n" +msgstr "Es konnte keine IP-Adresse für das Gerät `%s' ermittelt werden.\n" + +#: src/util/network/ip.c:306 +#, fuzzy, c-format +msgid "" +"There is more than one IP address specified for interface `%s'.\n" +"GNUnet will use %s.\n" +msgstr "" +"Es ist mehr als eine IP-Adresse für das Gerät `%s' angegeben.\n" +"GNUnet wird %u.%u.%u.%u. verwenden.\n" + +#: src/util/network/ip.c:330 +#, c-format +msgid "Could not resolve `%s' to determine our IP address: %s\n" +msgstr "" +"`%s' konnte nicht aufgelöst werden, um unsere IP-Adresse zu ermitteln: %s\n" + +#: src/util/network/ip.c:363 +#, fuzzy, c-format +msgid "GNUnet now uses the IP address %s.\n" +msgstr "GNUnet verwendet nun die IP-Adresse %u.%u.%u.%u.\n" + +#: src/util/network/ipcheck.c:106 src/util/network/ipcheck.c:136 +#: src/util/network/ipcheck.c:186 src/util/network/ipcheck.c:211 +#: src/util/network/ipcheck.c:219 +#, c-format +msgid "Invalid format for IP: `%s'\n" +msgstr "Ungültiges Format für IP: `%s'\n" + +#: src/util/network/ipcheck.c:167 +#, c-format +msgid "Invalid network notation ('/%d' is not legal in IPv4 CIDR)." +msgstr "Ungültige Netzwerk Notation ('/%d ist nicht gültig in IPv4 CIDR)." + +#: src/util/network/ipcheck.c:269 +#, c-format +msgid "Invalid network notation (does not end with ';': `%s')\n" +msgstr "Ungültige Netzwerk Notation (endet nicht mit ';': `%s')\n" + +#: src/util/network/ipcheck.c:306 +#, fuzzy, c-format +msgid "Wrong format `%s' for netmask\n" +msgstr "Falsches Format `%s' für Netzmaske: %s\n" + +#: src/util/network/ipcheck.c:338 +#, fuzzy, c-format +msgid "Wrong format `%s' for network\n" +msgstr "Falsches Format `%s' für Netzwerk: %s\n" + +#: src/util/network/dns.c:472 +#, fuzzy, c-format +msgid "Could not resolve `%s' (%s): %s\n" +msgstr "`%s' konnte nicht aufgelöst werden: %s\n" + +#: src/util/network/dns.c:523 src/util/network/dns.c:591 +#, c-format +msgid "Could not find IP of host `%s': %s\n" +msgstr "IP des Hosts `%s' konnte nicht ermittelt werden: %s\n" + +#: src/util/network/select.c:310 +#, fuzzy +msgid "Received malformed message (too small) from connection. Closing.\n" +msgstr "" +"Es wurde per TCP von einem anderen Knoten eine ungültige Nachricht (Größe %" +"u) empfangen. Verbindung wird geschlossen.\n" + +#: src/util/network/select.c:495 +#, c-format +msgid "select listen socket for `%s' not valid!\n" +msgstr "" + +#: src/util/config/config.c:296 +#, c-format +msgid "Syntax error in configuration file `%s' at line %d.\n" +msgstr "Syntaxfehler in Konfigurationsdatei `%s' in Zeile %d.\n" + +#: src/util/config/config.c:592 +#, c-format +msgid "" +"Configuration value '%llu' for '%s' in section '%s' is out of legal bounds [%" +"llu,%llu]\n" +msgstr "" + +#: src/util/config/config.c:602 +#, c-format +msgid "Configuration value '%s' for '%s' in section '%s' should be a number\n" +msgstr "" + +#: src/util/config/config.c:688 +#, c-format +msgid "" +"Configuration value '%s' for '%s' in section '%s' is not in set of legal " +"choices\n" +msgstr "" + +#: src/util/crypto/locking_gcrypt.c:80 +#, c-format +msgid "libgcrypt has not the expected version (version %s is required).\n" +msgstr "" +"libgcrypt hat nicht die erwartete Version (Version %s wird vorausgesetzt).\n" + +#: src/util/crypto/symcipher_gcrypt.c:46 src/util/crypto/symcipher_gcrypt.c:53 +#: src/util/crypto/hostkey_gcrypt.c:64 src/util/crypto/hostkey_gcrypt.c:71 +#: src/util/loggers/file.c:271 src/util/loggers/file.c:289 +#: src/applications/sqstore_sqlite/sqlite.c:45 +#: src/applications/sqstore_sqlite/sqlite.c:52 +#: src/applications/kvstore_sqlite/kv_sqlite.c:44 +#: src/applications/kvstore_sqlite/kv_sqlite.c:51 +#: src/applications/kvstore_mysql/kv_mysql.c:44 +#: src/applications/kvstore_mysql/kv_mysql.c:51 +#: src/applications/dstore_sqlite/dstore.c:94 +#: src/applications/dstore_sqlite/dstore.c:101 +#: src/applications/dstore_sqlite/dstore.c:222 +#: src/applications/dstore_sqlite/dstore.c:259 +#: src/applications/dstore_sqlite/dstore.c:285 +#: src/applications/dstore_sqlite/dstore.c:345 +#: src/applications/dstore_sqlite/dstore.c:366 +#: src/applications/dstore_sqlite/dstore.c:378 +#: src/applications/dstore_sqlite/dstore.c:407 +#: src/applications/dstore_sqlite/dstore.c:511 +#: src/applications/dstore_sqlite/dstore.c:555 +#: src/include/gnunet_util_error.h:249 src/include/gnunet_util_error.h:256 +#: src/include/gnunet_util_error.h:263 +#, c-format +msgid "`%s' failed at %s:%d with error: %s\n" +msgstr "`%s' schlug bei %s:%d mit dem Fehler %s fehl\n" + +#: src/util/crypto/hostkey_gcrypt.c:907 +#, c-format +msgid "RSA signature verification failed at %s:%d: %s\n" +msgstr "RSA Signaturüberprüfung fehlgeschlagen bei %s:%d: %s\n" + +#: src/util/os/user.c:108 src/util/os/user.c:125 +#, fuzzy, c-format +msgid "`%s' returned with error code %u" +msgstr "`%s' schlug bei %s:%d mit dem Fehler %s fehl\n" + +#: src/util/os/user.c:155 src/util/os/user.c:200 +#, fuzzy, c-format +msgid "Cannot obtain information about user `%s': %s\n" +msgstr "Fehler beim Speichern der Konfigurationsdatei: `%s': %s.\n" + +#: src/util/os/user.c:156 +msgid "No such user" +msgstr "" + +#: src/util/os/user.c:171 +#, c-format +msgid "Cannot change user/group to `%s': %s\n" +msgstr "Benutzer/Gruppe kann nicht zu `%s' gewechselt werden: %s\n" + +#: src/util/os/semaphore.c:227 +#, c-format +msgid "Can't create semaphore: %i" +msgstr "Semaphore konnte nicht angelegt werden: %i" + +#: src/util/os/cpustatus.c:464 +msgid "Cannot query the CPU usage (Windows NT).\n" +msgstr "CPU Nutzung kann nicht ermittelt werden (Windows NT).\n" + +#: src/util/os/cpustatus.c:487 +msgid "Cannot query the CPU usage (Win 9x)\n" +msgstr "CPU Nutzung kann nicht ermittelt werden (Win 9x).\n" + +#: src/util/os/dso.c:59 +#, c-format +msgid "Initialization of plugin mechanism failed: %s!\n" +msgstr "Initialisierung des Plugin Mechanismuses fehlgeschlagen: %s!\n" + +#: src/util/os/dso.c:120 +#, fuzzy, c-format +msgid "`%s' failed for library `%s' with error: %s\n" +msgstr "`%s' fehlgeschlagen für die Bibliothek `%s'. Ort: %s:%d. Fehler: %s\n" + +#: src/util/os/dso.c:162 +#, fuzzy, c-format +msgid "`%s' failed to resolve method '%s' with error: %s\n" +msgstr "" +"`%s' konnte die Methode '%s%s' nicht auflösen. Ort: %s:%d. Fehler: %s\n" + +#: src/util/os/statuscalls.c:197 src/util/os/statuscalls.c:342 +#, fuzzy, c-format +msgid "Failed to parse interface data from `%s'.\n" +msgstr "Fehler beim Parsen der Gerätedaten von `%s' bei %s:%d.\n" + +#: src/util/os/statuscalls.c:390 src/util/os/statuscalls.c:400 +#, c-format +msgid "" +"No network interfaces defined in configuration section `%s' under `%s'!\n" +msgstr "" +"Es sind keine Netzwerkgeräte in der Konfigurationsdatei in der Sektion `%s' " +"unter `%s' definiert!\n" + +#: src/util/os/osconfig.c:153 +msgid "Setting open descriptor limit not supported.\n" +msgstr "" + +#: src/util/os/osconfig.c:463 src/util/os/osconfig.c:492 +#, fuzzy, c-format +msgid "Command `%s' failed with error code %u\n" +msgstr "`%s' schlug bei %s:%d mit dem Fehler %s fehl\n" + +#: src/util/os/priority.c:78 +#, fuzzy, c-format +msgid "Invalid process priority `%s'\n" +msgstr "Ungültige Antwort auf `%s'.\n" + +#: src/util/threads/semaphore.c:168 src/util/threads/pthread.c:157 +#: src/util/threads/mutex.c:146 +#, fuzzy, c-format +msgid "Real-time delay violation (%llu ms) at %s:%u\n" +msgstr "Unerwartete sehr große Allokierung (%u Bytes) bei %s:%d!\n" + +#: src/util/threads/pthread.c:169 src/util/threads/pthread.c:176 +#: src/util/threads/pthread.c:182 src/util/threads/pthread.c:276 +#, fuzzy, c-format +msgid "`%s' failed with error code %s: %s\n" +msgstr "`%s' schlug bei %s:%d mit dem Fehler %s fehl\n" + +#: src/util/threads/pthread.c:188 src/util/threads/pthread.c:286 +#, fuzzy, c-format +msgid "`%s' failed with error code %d: %s\n" +msgstr "`%s' schlug bei %s:%d mit dem Fehler %s fehl\n" + +#: src/util/threads/mutex.c:155 src/util/threads/mutex.c:201 +#, fuzzy, c-format +msgid "Invalid argument for `%s'.\n" +msgstr "Ungültiger Parameter: `%s'\n" + +#: src/util/threads/mutex.c:160 +#, fuzzy, c-format +msgid "Deadlock due to `%s'.\n" +msgstr "Durch `%s' ist ein Deadlock bei %s:%d entstanden\n" + +#: src/util/threads/mutex.c:187 +#, fuzzy, c-format +msgid "Lock acquired for too long (%llu ms) at %s:%u\n" +msgstr "Unerwartete sehr große Allokierung (%u Bytes) bei %s:%d!\n" + +#: src/util/threads/mutex.c:207 +#, fuzzy, c-format +msgid "Permission denied for `%s'.\n" +msgstr "Zugriff verweigert für `%s' bei %s:%d.\n" + +#: src/util/boot/startup.c:259 +#, fuzzy, c-format +msgid "Failed to run %s: %s %d\n" +msgstr "Es konnte keine Verbindung mit gnunetd hergestellt werden.\n" + +#: src/util/string/string.c:55 +msgid "ms" +msgstr "ms" + +#: src/util/string/string.c:61 +msgid "s" +msgstr "s" + +#: src/util/string/string.c:65 +msgid "m" +msgstr "m" + +#: src/util/string/string.c:69 +msgid "h" +msgstr "h" + +#: src/util/string/string.c:73 +msgid " days" +msgstr " Tage" + +#: src/util/string/string.c:89 +msgid "b" +msgstr "b" + +#: src/util/string/string.c:95 +msgid "KiB" +msgstr "" + +#: src/util/string/string.c:99 +msgid "MiB" +msgstr "" + +#: src/util/string/string.c:103 +msgid "GiB" +msgstr "" + +#: src/util/string/string.c:107 +msgid "TiB" +msgstr "" + +#: src/util/string/string.c:226 +msgid "Failed to expand `$HOME': environment variable `HOME' not set" +msgstr "" + +#: src/util/loggers/file.c:229 +#, fuzzy, c-format +msgid "Failed to open log-file `%s': %s\n" +msgstr "Datei wurde als `%s' gespeichert.\n" + +#: src/util/loggers/file.c:250 +#, fuzzy +msgid "GNUnet error log" +msgstr "GNUnet Netzwerk Topologie tracen." + +#: src/util/loggers/memory.c:72 +msgid "Out of memory (for logging)\n" +msgstr "" + +#: src/util/pseudonym/names.c:79 +#, fuzzy +msgid "no-name" +msgstr "Name anzeigen" + +#: src/applications/datastore/datastore.c:183 +#: src/applications/datastore/datastore.c:199 +#, c-format +msgid "Availability test failed for `%s' at %s:%d.\n" +msgstr "Verfügbarkeitstest für `%s' fehlgeschlagen bei %s:%d.\n" + +#: src/applications/datastore/datastore.c:401 +msgid "# requests filtered by bloom filter" +msgstr "" + +#: src/applications/datastore/datastore.c:403 +msgid "# bloom filter false positives" +msgstr "" + +#: src/applications/datastore/datastore.c:406 +msgid "# bytes allowed in datastore" +msgstr "# bytes erlaubt in der Datenbank" + +#: src/applications/datastore/datastore.c:423 +#, fuzzy +msgid "Failed to load state service. Trying to do without.\n" +msgstr "Der Transportdienst auf Port %d konnte nicht gestartet werden.\n" + +#: src/applications/datastore/datastore.c:529 +#, c-format +msgid "Datastore conversion at approximately %u%%\n" +msgstr "" + +#: src/applications/datastore/datastore.c:576 +#, fuzzy, c-format +msgid "Starting datastore conversion (this may take a while).\n" +msgstr "Ein neuer Hostkey wird erzeugt (dies kann eine Weile dauern).\n" + +#: src/applications/datastore/datastore.c:584 +#, c-format +msgid "Completed datastore conversion.\n" +msgstr "" + +#: src/applications/datastore/datastore.c:592 +#, fuzzy +msgid "Failed to load sqstore service. Check your configuration!\n" +msgstr "Der Transportdienst auf Port %d konnte nicht gestartet werden.\n" + +#: src/applications/rpc/rpc.c:339 +#, fuzzy, c-format +msgid "" +"%s:%d - RPC %s:%p could not be registered: another callback is already using " +"this name (%p)\n" +msgstr "" +"%s::%s - RPC %s:%p konnte nicht registriert werden: ein anderer Callback " +"verwendet bereits diesen Namen (%p)\n" + +#: src/applications/rpc/rpc.c:398 +#, fuzzy, c-format +msgid "%s:%d - async RPC %s:%p could not be unregistered: not found\n" +msgstr "" +"%s::%s - async RPC %s:%p konnte nicht unregistriert werden: nicht gefunden\n" + +#: src/applications/rpc/rpc.c:951 +#, c-format +msgid "`%s' registering handlers %d %d %d\n" +msgstr "`%s' registriert Handler %d %d %d\n" + +#: src/applications/rpc/rpc.c:972 +#, c-format +msgid "Failed to initialize `%s' service.\n" +msgstr "`%s' Dienst konnte nicht initialisiert werden.\n" + +#: src/applications/tbench/tbenchtest.c:53 +#, c-format +msgid "Using %u messages of size %u for %u times.\n" +msgstr "Verwende %u Nachrichten der Größe %u %umal.\n" + +#: src/applications/tbench/tbenchtest.c:85 +#, c-format +msgid "Times: max %16llu min %16llu mean %12.3f variance %12.3f\n" +msgstr "Zeiten: Max %16llu Min %16llu Mittel %12.3f Varianz %12.3f\n" + +#: src/applications/tbench/tbenchtest.c:89 +#, c-format +msgid "Loss: max %16u min %16u mean %12.3f variance %12.3f\n" +msgstr "Verloren: Max %16u Min %16u Mittel %12.3f Varianz %12.3f\n" + +#: src/applications/tbench/tbenchtest.c:95 +#, c-format +msgid "" +"\n" +"Failed to receive reply from gnunetd.\n" +msgstr "" +"\n" +"Fehler beim Empfangen der Antwort von gnunetd.\n" + +#: src/applications/tbench/tbenchtest.c:149 +#, c-format +msgid "Running benchmark...\n" +msgstr "Benchmark läuft...\n" + +#: src/applications/tbench/tbench.c:422 +msgid "allows profiling of direct peer-to-peer connections" +msgstr "Ermöglicht das Untersuchen direkter Peer-to-Peer Verbindungen" + +#: src/applications/tbench/gnunet-tbench.c:63 +#: src/applications/tracekit/gnunet-tracekit.c:302 +msgid "Start GNUnet transport benchmarking tool." +msgstr "GNUnet Transport Benchmarking Werkzeug starten." + +#: src/applications/tbench/gnunet-tbench.c:65 +msgid "output in gnuplot format" +msgstr "Ausgabe im gnuplot Format" + +#: src/applications/tbench/gnunet-tbench.c:69 +msgid "number of iterations" +msgstr "Anzahl an Durchläufen" + +#: src/applications/tbench/gnunet-tbench.c:73 +msgid "number of messages to use per iteration" +msgstr "Anzahl an Nachrichten, die pro Durchlauf verwendet wird" + +#: src/applications/tbench/gnunet-tbench.c:76 +msgid "receiver host identifier (ENC file name)" +msgstr "Bezeichner des Empfängerhosts (ENC Dateiname)" + +#: src/applications/tbench/gnunet-tbench.c:79 +msgid "message size" +msgstr "Nachrichtengröße" + +#: src/applications/tbench/gnunet-tbench.c:82 +msgid "sleep for SPACE ms after each a message block" +msgstr "Für SPACE ms nach jedem Nachrichtenblock pausieren" + +#: src/applications/tbench/gnunet-tbench.c:85 +msgid "time to wait for the completion of an iteration (in ms)" +msgstr "Zeit, die gewartet wird, bis der Durchlauf fertiggestellt wird (in ms)" + +#: src/applications/tbench/gnunet-tbench.c:90 +msgid "number of messages in a message block" +msgstr "Anzahl an Nachrichten in einem Nachrichtenblock" + +#: src/applications/tbench/gnunet-tbench.c:126 +#: src/applications/tracekit/gnunet-tracekit.c:352 +#: src/applications/tracekit/tracekittest.c:133 +#: src/applications/template/gnunet-template.c:95 +#: src/applications/stats/gnunet-stats.c:121 +#: src/applications/vpn/gnunet-vpn.c:154 +#, c-format +msgid "Error establishing connection with gnunetd.\n" +msgstr "Fehler beim Aufbauen einer Verbindung mit gnunetd.\n" + +#: src/applications/tbench/gnunet-tbench.c:142 +#, c-format +msgid "You must specify a receiver!\n" +msgstr "Sie müssen einen Empfänger angeben!\n" + +#: src/applications/tbench/gnunet-tbench.c:152 +#, c-format +msgid "Invalid receiver peer ID specified (`%s' is not valid name).\n" +msgstr "" +"Ungültige Empfängerknoten ID angegeben (`%s' ist kein gültiger Name).\n" + +#: src/applications/tbench/gnunet-tbench.c:189 +#, c-format +msgid "Time:\n" +msgstr "Zeit:\n" + +#: src/applications/tbench/gnunet-tbench.c:190 +#, c-format +msgid "\tmax %llums\n" +msgstr "\tMax %llums\n" + +#: src/applications/tbench/gnunet-tbench.c:191 +#, c-format +msgid "\tmin %llums\n" +msgstr "\tMin %llums\n" + +#: src/applications/tbench/gnunet-tbench.c:192 +#, c-format +msgid "\tmean %8.4fms\n" +msgstr "\tMittel %8.4fms\n" + +#: src/applications/tbench/gnunet-tbench.c:193 +#, c-format +msgid "\tvariance %8.4fms\n" +msgstr "\tVarianz %8.4fms\n" + +#: src/applications/tbench/gnunet-tbench.c:195 +#, c-format +msgid "Loss:\n" +msgstr "Verlust:\n" + +#: src/applications/tbench/gnunet-tbench.c:196 +#, c-format +msgid "\tmax %u\n" +msgstr "\tMax %u\n" + +#: src/applications/tbench/gnunet-tbench.c:197 +#, c-format +msgid "\tmin %u\n" +msgstr "\tMin %u\n" + +#: src/applications/tbench/gnunet-tbench.c:198 +#, c-format +msgid "\tmean %8.4f\n" +msgstr "\tMittel %8.4fms\n" + +#: src/applications/tbench/gnunet-tbench.c:199 +#, c-format +msgid "\tvariance %8.4f\n" +msgstr "\tVarianz %8.4f\n" + +#: src/applications/tbench/gnunet-tbench.c:205 +#, c-format +msgid "Output format not known, this should not happen.\n" +msgstr "Ausgabeformat ist unbekannt, dies sollte nicht passieren.\n" + +#: src/applications/tbench/gnunet-tbench.c:211 +#, c-format +msgid "" +"\n" +"Did not receive the message from gnunetd. Is gnunetd running?\n" +msgstr "" +"\n" +"Es wurde keine Nachricht von gnunetd empfangen. Läuft gnunetd?\n" + +#: src/applications/traffic/traffic.c:454 +#, c-format +msgid "# bytes transmitted of type %d" +msgstr "# Bytes des Typs %d übertragen" + +#: src/applications/traffic/traffic.c:470 +#, c-format +msgid "# bytes received of type %d" +msgstr "# Bytes des Typs %d empfangen" + +#: src/applications/traffic/traffic.c:489 +#, fuzzy, c-format +msgid "# bytes received in plaintext of type %d" +msgstr "# Bytes des Typs %d empfangen" + +#: src/applications/traffic/traffic.c:652 +msgid "tracks bandwidth utilization by gnunetd" +msgstr "Verfolgt die Bandbreitennutzung von gnunetd" + +#: src/applications/sqstore_sqlite/sqlite.c:218 +#, c-format +msgid "Unable to initialize SQLite: %s.\n" +msgstr "SQLite Datenbank konnte nicht initialisiert werden: %s.\n" + +#: src/applications/sqstore_sqlite/sqlite.c:434 +#: src/applications/sqstore_sqlite/sqlite.c:469 +#, c-format +msgid "Invalid data in %s. Trying to fix (by deletion).\n" +msgstr "Ungültige Daten in %s. Korrektur wird versucht (durch Löschung).\n" + +#: src/applications/sqstore_sqlite/sqlite.c:435 +#: src/applications/sqstore_sqlite/sqlite.c:470 +msgid "sqlite datastore" +msgstr "sqlite Datenspeicher" + +#: src/applications/sqstore_sqlite/sqlite.c:1474 +#: src/applications/sqstore_mysql/mysql.c:1078 +msgid "# bytes in datastore" +msgstr "# bytes in der Datenbank" + +#: src/applications/sqstore_sqlite/sqlite.c:1476 +#, fuzzy +msgid "# bytes allocated by SQLite" +msgstr "# bytes erlaubt in der Datenbank" + +#: src/applications/sqstore_mysql/mysql.c:1085 +#: src/applications/sqstore_mysql/mysql.c:1160 +msgid "" +"Failed to load MySQL database module. Check that MySQL is running and " +"configured properly!\n" +msgstr "" +"Fehler beim Laden des MySQL Datenbankmoduls. Prüfen Sie, ob MySQL läuft und " +"richtig eingerichtet ist!\n" + +#: src/applications/tracekit/gnunet-tracekit.c:104 +#, c-format +msgid "`%s' connected to `%s'.\n" +msgstr "`%s' hat sich mit `%s' verbunden.\n" + +#: src/applications/tracekit/gnunet-tracekit.c:110 +#: src/applications/tracekit/tracekittest.c:67 +#, c-format +msgid "`%s' is not connected to any peer.\n" +msgstr "`%s' ist zu keinem Knoten verbunden.\n" + +#: src/applications/tracekit/gnunet-tracekit.c:295 +msgid "probe network to the given DEPTH" +msgstr "Netzwerk bis zur angegebenen Tiefe DEPTH sondieren" + +#: src/applications/tracekit/gnunet-tracekit.c:299 +msgid "" +"specify output format; 0 for human readable output, 1 for dot, 2 for vcg" +msgstr "Gibt das Ausgabeformat an: 0 für menschen-lesbar, 1 für dot, 2 für vcg" + +#: src/applications/tracekit/gnunet-tracekit.c:306 +#, fuzzy +msgid "use PRIORITY for the priority of the trace request" +msgstr "PRIO als Priorität für die Trace Anfragen verwenden" + +#: src/applications/tracekit/gnunet-tracekit.c:310 +msgid "wait DELAY seconds for replies" +msgstr "DELAY Sekunden auf Antworten warten" + +#: src/applications/tracekit/gnunet-tracekit.c:344 +#, c-format +msgid "" +"Format specification invalid. Use 0 for user-readable, 1 for dot, 2 for " +"vcg.\n" +msgstr "" +"Formatangabe ungültig. Verwenden Sie 0 für menschen-lesbar, 1 für dot und 2 " +"für vcg.\n" + +#: src/applications/tracekit/tracekittest.c:60 +#, fuzzy, c-format +msgid "`%.*s' connected to `%.*s'.\n" +msgstr "`%s' hat sich mit `%s' verbunden.\n" + +#: src/applications/tracekit/tracekit.c:440 +msgid "allows mapping of the network topology" +msgstr "Erlaubt die Kartographierung der Netzwerktopologie" + +#: src/applications/advertising/advertising_test.c:47 +#: src/applications/hostlist/hostlisttest.c:40 +#: src/applications/session/sessiontest.c:40 +#: src/applications/session/sessiontest_nat_http.c:40 +#: src/applications/session/sessiontest_nat.c:40 +#: src/applications/stats/statistics.c:247 +msgid "# of connected peers" +msgstr "# verbundener Knoten" + +#: src/applications/advertising/advertising.c:194 +#, fuzzy, c-format +msgid "HELLO message from `%s' has an invalid signature. Dropping.\n" +msgstr "" +"Hello Nachricht von `%s' ist ungültig (Signatur ist ungültig). Nachricht " +"wurde verworfen.\n" + +#: src/applications/advertising/advertising.c:205 +#, fuzzy +msgid "HELLO message has expiration too far in the future. Dropping.\n" +msgstr "" +"Empfangene Hello Nachricht ist ungültig (Ablaufzeit über Limit). Nachricht " +"wurde verworfen.\n" + +#: src/applications/advertising/advertising.c:406 +#, fuzzy +msgid "Could not send HELLO+PING, ping buffer full.\n" +msgstr "Hellos+PING konnten nicht gesendet werden, Ping Puffer ist voll.\n" + +#: src/applications/advertising/advertising.c:429 +msgid "Failed to create an advertisement for this peer. Will not send PING.\n" +msgstr "" + +#: src/applications/advertising/advertising.c:581 +#, c-format +msgid "Advertising my transport %d to selected peers.\n" +msgstr "Transport %d wird ausgewählten anderen Knoten bekannt gegeben.\n" + +#: src/applications/advertising/advertising.c:590 +msgid "" +"Announcing ourselves pointless: no other peers are known to us so far.\n" +msgstr "" +"Bekanntmachung von uns zwecklos: bis jetzt sind uns keine anderen Knoten " +"bekannt.\n" + +#: src/applications/advertising/advertising.c:868 +msgid "# Peer advertisements received" +msgstr "# Knotenankündigungen empfangen" + +#: src/applications/advertising/advertising.c:871 +#, fuzzy +msgid "# Peer advertisements of type NAT received" +msgstr "# Knotenankündigungen empfangen" + +#: src/applications/advertising/advertising.c:874 +#, fuzzy +msgid "# Peer advertisements confirmed via PONG" +msgstr "# Knotenankündigungen empfangen" + +#: src/applications/advertising/advertising.c:877 +#, fuzzy +msgid "# Peer advertisements updating earlier HELLOs" +msgstr "# Knotenankündigungen empfangen" + +#: src/applications/advertising/advertising.c:880 +#, fuzzy +msgid "# Peer advertisements discarded due to load" +msgstr "# Knotenankündigungen empfangen" + +#: src/applications/advertising/advertising.c:883 +#, fuzzy +msgid "# Peer advertisements for unsupported transport" +msgstr "# Knotenankündigungen empfangen" + +#: src/applications/advertising/advertising.c:886 +#, fuzzy +msgid "# Peer advertisements not confirmed due to ping busy" +msgstr "# Knotenankündigungen empfangen" + +#: src/applications/advertising/advertising.c:889 +#, fuzzy +msgid "# Peer advertisements not confirmed due to lack of self ad" +msgstr "# Knotenankündigungen empfangen" + +#: src/applications/advertising/advertising.c:892 +#, fuzzy +msgid "# Peer advertisements not confirmed due to send error" +msgstr "# Knotenankündigungen empfangen" + +#: src/applications/advertising/advertising.c:894 +msgid "# Self advertisments transmitted" +msgstr "# Selbstbekanntmachungen übertragen" + +#: src/applications/advertising/advertising.c:896 +msgid "# Foreign advertisements forwarded" +msgstr "# Bekanntmachungen von anderen übertragen" + +#: src/applications/advertising/advertising.c:898 +#: src/applications/pingpong/pingpong.c:528 +msgid "# plaintext PING messages sent" +msgstr "# Klartext PING Nachrichten gesendet" + +#: src/applications/advertising/advertising.c:904 +#: src/applications/session/connect.c:932 +#, c-format +msgid "`%s' registering handler %d (plaintext and ciphertext)\n" +msgstr "`%s' registriert Handler %d (Plaintext und Ciphertext)\n" + +#: src/applications/advertising/advertising.c:922 +msgid "" +"ensures that this peer is known by other peers and discovers other peers" +msgstr "" +"Stellt sicher, dass dieser Knoten anderen Knoten bekannt ist und entdeckt " +"andere Knoten" + +#: src/applications/fragmentation/fragmentation.c:578 +msgid "# messages defragmented" +msgstr "# defragmentierter Nachrichten" + +#: src/applications/fragmentation/fragmentation.c:580 +msgid "# messages fragmented" +msgstr "# fragmentierter Nachrichten" + +#: src/applications/fragmentation/fragmentation.c:581 +msgid "# fragments discarded" +msgstr "# verworfener Nachrichten" + +#: src/applications/fragmentation/fragmentation.c:592 +#, c-format +msgid "`%s' registering handler %d\n" +msgstr "`%s' registriert Handler %d\n" + +#: src/applications/topology_default/topology.c:466 +#, fuzzy, c-format +msgid "Could not read friends list `%s'\n" +msgstr "Fehler beim Lesen der Freunde-Liste von `%s'\n" + +#: src/applications/topology_default/topology.c:485 +#, c-format +msgid "Failed to read friends list from `%s'\n" +msgstr "Fehler beim Lesen der Freunde-Liste von `%s'\n" + +#: src/applications/topology_default/topology.c:505 +msgid "Syntax error in topology specification, skipping bytes.\n" +msgstr "Syntaxfehler in Topolgieangabe, Bytes werden übersprungen.\n" + +#: src/applications/topology_default/topology.c:523 +#, c-format +msgid "Syntax error in topology specification, skipping bytes `%s'.\n" +msgstr "Syntaxfehler in Topologieangabe, überspringe Bytes `%s'.\n" + +#: src/applications/topology_default/topology.c:535 +msgid "" +"Fewer friends specified than required by minimum friend count. Will only " +"connect to friends.\n" +msgstr "" + +#: src/applications/topology_default/topology.c:543 +msgid "" +"More friendly connections required than target total number of connections.\n" +msgstr "" + +#: src/applications/topology_default/topology.c:726 +msgid "maintains GNUnet default mesh topology" +msgstr "" + +#: src/applications/chat/tools/gnunet-chat.c:107 +msgid "anonymous" +msgstr "" + +#: src/applications/chat/tools/gnunet-chat.c:113 +#, fuzzy, c-format +msgid "`%s' said: %s\n" +msgstr "`%s' %s schlug fehl: %s\n" + +#: src/applications/chat/tools/gnunet-chat.c:116 +#: src/applications/chat/tools/gnunet-chat.c:119 +#, fuzzy, c-format +msgid "`%s' said to you: %s\n" +msgstr "`%s' %s schlug fehl: %s\n" + +#: src/applications/chat/tools/gnunet-chat.c:122 +#, fuzzy, c-format +msgid "`%s' said for sure: %s\n" +msgstr "`%s' fehlgeschlagen für Laufwerk %s: %u\n" + +#: src/applications/chat/tools/gnunet-chat.c:125 +#, fuzzy, c-format +msgid "`%s' said to you for sure: %s\n" +msgstr "`%s' schlug bei %s:%d mit dem Fehler %s fehl\n" + +#: src/applications/chat/tools/gnunet-chat.c:128 +#, fuzzy, c-format +msgid "`%s' was confirmed that you received: %s\n" +msgstr "`%s' schlug bei %s:%d mit dem Fehler %s fehl\n" + +#: src/applications/chat/tools/gnunet-chat.c:131 +#, c-format +msgid "`%s' was confirmed that you and only you received: %s\n" +msgstr "" + +#: src/applications/chat/tools/gnunet-chat.c:134 +#, c-format +msgid "`%s' was confirmed that you received from him or her: %s\n" +msgstr "" + +#: src/applications/chat/tools/gnunet-chat.c:139 +#, c-format +msgid "`%s' was confirmed that you and only you received from him or her: %s\n" +msgstr "" + +#: src/applications/chat/tools/gnunet-chat.c:142 +#, fuzzy, c-format +msgid "`%s' said off the record: %s\n" +msgstr "`%s' schlug bei %s:%d mit dem Fehler %s fehl\n" + +#: src/applications/chat/tools/gnunet-chat.c:145 +#, c-format +msgid "<%s> said using an unknown message type: %s\n" +msgstr "" + +#: src/applications/chat/tools/gnunet-chat.c:167 +#, c-format +msgid "`%s' entered the room\n" +msgstr "" + +#: src/applications/chat/tools/gnunet-chat.c:167 +#, fuzzy, c-format +msgid "`%s' left the room\n" +msgstr "Fehler beim Binden an UDP Port %d.\n" + +#: src/applications/chat/tools/gnunet-chat.c:239 +#: src/applications/chat/tools/gnunet-chat.c:348 +#, fuzzy, c-format +msgid "Failed to send message.\n" +msgstr "Zustellung der Nachricht `%s' fehlgeschlagen.\n" + +#: src/applications/chat/tools/gnunet-chat.c:265 +#: src/applications/chat/tools/gnunet-chat.c:524 +#, fuzzy, c-format +msgid "Joined room `%s' as user `%s'.\n" +msgstr "Ungültige Antwort auf `%s' von Knoten `%s' empfangen.\n" + +#: src/applications/chat/tools/gnunet-chat.c:293 +#, fuzzy, c-format +msgid "Changed username to `%s'.\n" +msgstr "Benutzer/Gruppe kann nicht zu `%s' gewechselt werden: %s\n" + +#: src/applications/chat/tools/gnunet-chat.c:301 +#, fuzzy, c-format +msgid "Unknown command `%s'.\n" +msgstr "Unbekannte Operation `%s'\n" + +#: src/applications/chat/tools/gnunet-chat.c:316 +#, c-format +msgid "Syntax: /msg USERNAME MESSAGE" +msgstr "" + +#: src/applications/chat/tools/gnunet-chat.c:324 +#, fuzzy, c-format +msgid "Unknown user `%s'\n" +msgstr "Unbekannte Operation `%s'\n" + +#: src/applications/chat/tools/gnunet-chat.c:339 +#, c-format +msgid "User `%s' is currently not in the room!\n" +msgstr "" + +#: src/applications/chat/tools/gnunet-chat.c:361 +#, c-format +msgid "Users in room `%s': " +msgstr "" + +#: src/applications/chat/tools/gnunet-chat.c:390 +msgid "" +"Use `/join #roomname' to join a chat room. Joining a room will cause you to " +"leave the current room" +msgstr "" + +#: src/applications/chat/tools/gnunet-chat.c:393 +msgid "" +"Use `/nick nickname' to change your nickname. This will cause you to leave " +"the current room and immediately rejoin it with the new name." +msgstr "" + +#: src/applications/chat/tools/gnunet-chat.c:396 +msgid "" +"Use `/msg nickname message' to send a private message to the specified user" +msgstr "" + +#: src/applications/chat/tools/gnunet-chat.c:398 +msgid "The `/notice' command is an alias for `/msg'" +msgstr "" + +#: src/applications/chat/tools/gnunet-chat.c:400 +msgid "The `/query' command is an alias for `/msg'" +msgstr "" + +#: src/applications/chat/tools/gnunet-chat.c:402 +msgid "Use `/quit' to terminate gnunet-chat" +msgstr "" + +#: src/applications/chat/tools/gnunet-chat.c:404 +msgid "The `/leave' command is an alias for `/quit'" +msgstr "" + +#: src/applications/chat/tools/gnunet-chat.c:407 +msgid "Use `/names' to list all of the current members in the chat room" +msgstr "" + +#: src/applications/chat/tools/gnunet-chat.c:409 +msgid "Use `/help command' to get help for a specific command" +msgstr "" + +#: src/applications/chat/tools/gnunet-chat.c:457 +msgid "Join a chat on GNUnet." +msgstr "" + +#: src/applications/chat/tools/gnunet-chat.c:461 +msgid "set the nickname to use (required)" +msgstr "" + +#: src/applications/chat/tools/gnunet-chat.c:464 +msgid "set the chat room to join" +msgstr "" + +#: src/applications/chat/tools/gnunet-chat.c:495 +#, fuzzy, c-format +msgid "You must specify a nickname\n" +msgstr "Sie müssen einen Empfänger angeben!\n" + +#: src/applications/chat/tools/gnunet-chat.c:515 +#, fuzzy, c-format +msgid "Failed to join room `%s'\n" +msgstr "Fehler beim Binden an UDP Port %d.\n" + +#: src/applications/chat/module/chat.c:325 +#, fuzzy, c-format +msgid "`%s' registering CS handlers %d and %d\n" +msgstr "`%s' registriert Handler %d und %d\n" + +#: src/applications/chat/module/chat.c:347 +msgid "enables P2P-chat (incomplete)" +msgstr "Ermöglicht P2P-Chat" + +#: src/applications/chat/lib/messaging.c:353 +#: src/applications/identity/hostkey.c:122 +#, fuzzy, c-format +msgid "Failed to access GNUnet home directory `%s'\n" +msgstr "Dateiformat fehlerhaft (kein GNUnet Verzeichnis?)\n" + +#: src/applications/chat/lib/messaging.c:389 +#, fuzzy, c-format +msgid "Existing key in file `%s' failed format check, creating new key.\n" +msgstr "" +"Formatüberprüfung schlug für die existierende Hostkeydatei `%s' fehl, es " +"wird ein neuer Hostkey erzeugt.\n" + +#: src/applications/chat/lib/messaging.c:399 +#, fuzzy +msgid "Creating new key for this nickname (this may take a while).\n" +msgstr "Ein neuer Hostkey wird erzeugt (dies kann eine Weile dauern).\n" + +#: src/applications/chat/lib/messaging.c:411 +#, fuzzy +msgid "Done creating key.\n" +msgstr "Hostkey wurde erfolgreich erzeugt.\n" + +#: src/applications/dstore_mysql/dstore_mysql.c:471 +#, fuzzy +msgid "Failed to initialize MySQL database connection for dstore.\n" +msgstr "SQLite Datenbank konnte nicht initialisiert werden.\n" + +#: src/applications/dstore_mysql/dstore_mysql.c:494 +#: src/applications/dstore_sqlite/dstore.c:636 +#, fuzzy +msgid "# bytes in dstore" +msgstr "# bytes in der Datenbank" + +#: src/applications/dstore_mysql/dstore_mysql.c:496 +#: src/applications/dstore_sqlite/dstore.c:638 +#, fuzzy +msgid "# max bytes allowed in dstore" +msgstr "# bytes erlaubt in der Datenbank" + +#: src/applications/transport/transport.c:191 +#, c-format +msgid "" +"Converting peer address to string failed, transport type %d not supported\n" +msgstr "" + +#: src/applications/transport/transport.c:246 +#, c-format +msgid "Transport connection attempt failed, transport type %d not supported\n" +msgstr "" + +#: src/applications/transport/transport.c:299 +#, c-format +msgid "" +"Transport failed to connect to peer `%s' (%u HELLOs known, none worked)\n" +msgstr "" + +#: src/applications/transport/transport.c:376 +#, fuzzy, c-format +msgid "Transmission attempt failed, transport type %d unknown.\n" +msgstr "Kein Transport des Typs %d bekannt.\n" + +#: src/applications/transport/transport.c:500 +#, c-format +msgid "No transport of type %d known.\n" +msgstr "Kein Transport des Typs %d bekannt.\n" + +#: src/applications/transport/transport.c:560 +msgid "No transport succeeded in creating a hello!\n" +msgstr "" + +#: src/applications/transport/transport.c:761 +#, fuzzy, c-format +msgid "Loading transports `%s'\n" +msgstr "Teste Transport(e) %s\n" + +#: src/applications/transport/transport.c:781 +#, fuzzy, c-format +msgid "Could not load transport plugin `%s'\n" +msgstr "Anwendung `%s' konnte nicht initialisiert werden.\n" + +#: src/applications/transport/transport.c:795 +#, c-format +msgid "Transport library `%s' did not provide required function '%s%s'.\n" +msgstr "" +"Transport Bibliothek `%s' stellt nicht die benötigte Funktion '%s%s' zur " +"Verfügung.\n" + +#: src/applications/transport/transport.c:824 +#, fuzzy, c-format +msgid "Loaded transport `%s'\n" +msgstr "Verfügbare(r) Transport(e): %s\n" + +#: src/applications/transport/transport.c:836 +#: src/server/gnunet-peer-info.c:252 +#, c-format +msgid "I am peer `%s'.\n" +msgstr "Ich bin Peer `%s'.\n" + +#: src/applications/dht/tools/dht_multipeer_test.c:80 +#: src/applications/dht/tools/dht_twopeer_test.c:47 +#: src/applications/dht/module/table.c:783 +msgid "# dht connections" +msgstr "" + +#: src/applications/dht/tools/dht-query.c:54 +#, fuzzy +msgid "Query (get KEY, put KEY VALUE) DHT table." +msgstr "Eine DHT Tabelle abfragen (get KEY, put KEY VALUE, remove KEY VALUE)." + +#: src/applications/dht/tools/dht-query.c:58 +#, fuzzy +msgid "allow TIME ms to process a GET command" +msgstr "TIME ms erlauben, um jedes Kommando zu bearbeiten" + +#: src/applications/dht/tools/dht-query.c:107 +#, fuzzy, c-format +msgid "Issuing `%s(%s,%s)' command.\n" +msgstr "'%s(%s,%s)' fehlgeschlagen.\n" + +#: src/applications/dht/tools/dht-query.c:142 +#: src/applications/fs/tools/gnunet-auto-share.c:669 +#, c-format +msgid "Failed to connect to gnunetd.\n" +msgstr "Es konnte keine Verbindung mit gnunetd hergestellt werden.\n" + +#: src/applications/dht/tools/dht-query.c:155 +#, c-format +msgid "Command `%s' requires an argument (`%s').\n" +msgstr "Kommando `%s' benötigt eine zusätzliche Angabe (`%s').\n" + +#: src/applications/dht/tools/dht-query.c:172 +#, c-format +msgid "Command `%s' requires two arguments (`%s' and `%s').\n" +msgstr "Kommando `%s' benötigt zwei zusätzliche Angaben (`%s' und `%s').\n" + +#: src/applications/dht/tools/dht-query.c:183 +#, c-format +msgid "Unsupported command `%s'. Aborting.\n" +msgstr "Kommando `%s' wird nicht unterstützt. Vorgang wird abgebrochen.\n" + +#: src/applications/dht/module/table.c:785 +#, fuzzy +msgid "# dht discovery messages received" +msgstr "# verschlüsselter PING Nachrichten empfangen" + +#: src/applications/dht/module/table.c:787 +msgid "# dht route host lookups performed" +msgstr "" + +#: src/applications/dht/module/table.c:789 +#, fuzzy +msgid "# dht discovery messages sent" +msgstr "# verschlüsselter PING Nachrichten empfangen" + +#: src/applications/dht/module/routing.c:879 +msgid "# dht replies routed" +msgstr "" + +#: src/applications/dht/module/routing.c:881 +msgid "# dht requests routed" +msgstr "# dht Anfragen weitergeleitet" + +#: src/applications/dht/module/routing.c:883 +#, fuzzy +msgid "# dht get requests received" +msgstr "# Client Trace-Anfragen empfangen" + +#: src/applications/dht/module/routing.c:885 +#, fuzzy +msgid "# dht put requests received" +msgstr "# Client Trace-Anfragen empfangen" + +#: src/applications/dht/module/routing.c:887 +#, fuzzy +msgid "# dht results received" +msgstr "# Bytes empfangen über TCP" + +#: src/applications/dht/module/routing.c:892 +#, fuzzy, c-format +msgid "`%s' registering p2p handlers: %d %d %d\n" +msgstr "`%s' registriert Handler %d %d %d\n" + +#: src/applications/dht/module/cs.c:122 +#, c-format +msgid "`%s' failed. Terminating connection to client.\n" +msgstr "`%s' fehlgeschlagen. Beende Verbindung zu Client.\n" + +#: src/applications/dht/module/cs.c:250 +#, fuzzy, c-format +msgid "`%s' registering client handlers: %d %d\n" +msgstr "`%s' registriert Client-Handler %d\n" + +#: src/applications/dht/module/cs.c:273 +msgid "Enables efficient non-anonymous routing" +msgstr "" + +#: src/applications/identity/hostkey.c:155 +#, c-format +msgid "" +"Existing hostkey in file `%s' failed format check, creating new hostkey.\n" +msgstr "" +"Formatüberprüfung schlug für die existierende Hostkeydatei `%s' fehl, es " +"wird ein neuer Hostkey erzeugt.\n" + +#: src/applications/identity/hostkey.c:164 +msgid "Creating new hostkey (this may take a while).\n" +msgstr "Ein neuer Hostkey wird erzeugt (dies kann eine Weile dauern).\n" + +#: src/applications/identity/hostkey.c:176 +msgid "Done creating hostkey.\n" +msgstr "Hostkey wurde erfolgreich erzeugt.\n" + +#: src/applications/identity/identity.c:333 +#, c-format +msgid "" +"File `%s' in directory `%s' does not match naming convention. Removed.\n" +msgstr "" +"Die Datei `%s' im Verzeichnis `%s' entspricht nicht der Namenskonvention. " +"Datei wurde entfernt.\n" + +#: src/applications/identity/identity.c:408 +#, fuzzy, c-format +msgid "Still no peers found in `%s'!\n" +msgstr "Dienst `%s' konnte nicht ordentlich entladen werden!\n" + +#: src/applications/identity/identity.c:731 +#: src/applications/identity/identity.c:757 +#, fuzzy, c-format +msgid "Removed file `%s' containing invalid HELLO data.\n" +msgstr "Datei `%s' enthielt ungültige Hello Daten und wurde entfernt.\n" + +#: src/applications/identity/identity.c:809 +#, c-format +msgid "Signature failed verification: peer `%s' not known.\n" +msgstr "" +"Signatur kann nicht verifiziert werden, der Knoten `%s' ist uns nicht " +"bekannt!\n" + +#: src/applications/identity/identity.c:819 +msgid "Signature failed verification: signature invalid.\n" +msgstr "Verifikation einer Signatur fehlgeschlagen: Signatur ist ungültig.\n" + +#: src/applications/identity/identity.c:935 +#: src/applications/identity/identity.c:1058 +#, c-format +msgid "Peer `%s' is currently strictly blacklisted (for another %llums).\n" +msgstr "" + +#: src/applications/identity/identity.c:1061 +#, c-format +msgid "Peer `%s' is currently blacklisted (for another %llums).\n" +msgstr "" + +#: src/applications/pingpong/pingpong.c:134 +#: src/applications/pingpong/pingpong.c:203 +#: src/applications/pingpong/pingpong.c:273 +#: src/applications/pingpong/pingpong.c:345 +#, c-format +msgid "Received malformed `%s' message. Dropping.\n" +msgstr "Beschädigte `%s' Nachricht empfangen. Nachricht wird verworfen.\n" + +#: src/applications/pingpong/pingpong.c:146 +msgid "Received ping for another peer. Dropping.\n" +msgstr "Ping für einen anderen Knoten empfangen. Ping wird verworfen.\n" + +#: src/applications/pingpong/pingpong.c:215 +#, fuzzy, c-format +msgid "Received PING from `%s' not destined for us!\n" +msgstr "Empfangener PING ist nicht an uns gerichtet!\n" + +#: src/applications/pingpong/pingpong.c:315 +#: src/applications/pingpong/pingpong.c:381 +msgid "" +"Could not match PONG against any PING. Try increasing MAX_PING_PONG " +"constant.\n" +msgstr "" +"PONG konnte keinem PING zugeordnet werden. Versuchen Sie die Konstante " +"MAX_PING_PONG hochzusetzen.\n" + +#: src/applications/pingpong/pingpong.c:425 +msgid "Cannot create PING, table full. Try increasing MAX_PING_PONG.\n" +msgstr "" +"PING konnte nicht erstellt werden, da die Tabelle voll ist. Versuchen Sie, " +"die Konstante MAX_PING_PONG hochzusetzen.\n" + +#: src/applications/pingpong/pingpong.c:518 +msgid "# encrypted PONG messages received" +msgstr "# verschlüsselter PONG Nachrichten empfangen" + +#: src/applications/pingpong/pingpong.c:520 +msgid "# plaintext PONG messages received" +msgstr "# Klartext PONG Nachrichten empfangen" + +#: src/applications/pingpong/pingpong.c:522 +msgid "# encrypted PING messages received" +msgstr "# verschlüsselter PING Nachrichten empfangen" + +#: src/applications/pingpong/pingpong.c:524 +msgid "# PING messages created" +msgstr "# PING Nachrichten erstellt" + +#: src/applications/pingpong/pingpong.c:526 +#: src/applications/session/connect.c:926 +msgid "# encrypted PONG messages sent" +msgstr "# verschlüsselter PONG Nachrichten gesendet" + +#: src/applications/pingpong/pingpong.c:530 +#: src/applications/session/connect.c:924 +msgid "# encrypted PING messages sent" +msgstr "# verschlüsselter PING Nachrichten gesendet" + +#: src/applications/pingpong/pingpong.c:532 +#, fuzzy +msgid "# plaintext PONG messages sent" +msgstr "# Klartext PING Nachrichten gesendet" + +#: src/applications/pingpong/pingpong.c:536 +#, fuzzy +msgid "# plaintext PONG transmissions failed" +msgstr "# Klartext PONG Nachrichten empfangen" + +#: src/applications/pingpong/pingpong.c:546 +#, c-format +msgid "`%s' registering handlers %d %d (plaintext and ciphertext)\n" +msgstr "`%s' registriert Handler %d %d (Plaintext und Ciphertext)\n" + +#: src/applications/hostlist/hostlist.c:165 +#, fuzzy +msgid "# hostlist requests received" +msgstr "# Client Trace-Anfragen empfangen" + +#: src/applications/hostlist/hostlist.c:167 +msgid "# hostlist HELLOs returned" +msgstr "" + +#: src/applications/hostlist/hostlist.c:169 +msgid "# hostlist bytes returned" +msgstr "" + +#: src/applications/hostlist/hostlist.c:199 +msgid "integrated HTTP hostlist server" +msgstr "" + +#: src/applications/session/connect.c:238 +#, c-format +msgid "Session key from peer `%s' could not be verified.\n" +msgstr "Sitzungsschlüssel von Knoten `%s' konnte nicht überprüft werden.\n" + +#: src/applications/session/connect.c:282 +#, c-format +msgid "Cannot encrypt sessionkey, peer `%s' not known!\n" +msgstr "" +"Sitzungsschlüssel kann nicht verschlüsselt werden, Knoten `%s' ist uns nicht " +"bekannt!\n" + +#: src/applications/session/connect.c:489 +#, fuzzy, c-format +msgid "Could not create any HELLO for myself (have transports `%s')!\n" +msgstr "Hostkey konnte nicht erzeugt werden!\n" + +#: src/applications/session/connect.c:599 +#, fuzzy, c-format +msgid "Session key received from peer `%s' has invalid format (discarded).\n" +msgstr "Empfangene Nachricht ist ungültig.\n" + +#: src/applications/session/connect.c:632 +#, fuzzy, c-format +msgid "Session key received from peer `%s' is for `%s' and not for me!\n" +msgstr "Empfangene Nachricht ist ungültig.\n" + +#: src/applications/session/connect.c:659 +#, c-format +msgid "Invalid `%s' message received from peer `%s'.\n" +msgstr "Ungültige `%s' Nachricht von Knoten `%s' empfangen.\n" + +#: src/applications/session/connect.c:670 +#, c-format +msgid "setkey `%s' from `%s' fails CRC check (have: %u, want %u).\n" +msgstr "" +"setkey `%s' von `%s' hat eine ungültige CRC Prüfsumme (tatsächlich: %u, " +"erwartet: %u).\n" + +#: src/applications/session/connect.c:728 +#, fuzzy, c-format +msgid "" +"Error parsing encrypted session key from `%s', given message part size is " +"invalid.\n" +msgstr "" +"Fehler beim Parsen des Verschlüsselten Sitzungsschlüssels, gegebene " +"Nachrichtenteilgröße ist ungültig.\n" + +#: src/applications/session/connect.c:741 +#, fuzzy, c-format +msgid "Unknown type in embedded message from `%s': %u (size: %u)\n" +msgstr "Unbekannter Typ in engebetteter Nachricht: %u (%u bytes)\n" + +#: src/applications/session/connect.c:916 +msgid "# session keys sent" +msgstr "# Sitzungsschlüssel gesendet" + +#: src/applications/session/connect.c:918 +msgid "# session keys rejected" +msgstr "# Sitzungsschlüssel abgelehnt" + +#: src/applications/session/connect.c:920 +msgid "# session keys accepted" +msgstr "# Sitzungsschlüssel akzeptiert" + +#: src/applications/session/connect.c:922 +msgid "# sessions established" +msgstr "# Sitzungen aufgebaut" + +#: src/applications/fs/tools/gnunet-pseudonym.c:70 +#: src/applications/fs/tools/gnunet-auto-share.c:199 +#: src/applications/fs/tools/gnunet-insert.c:246 +#: src/applications/fs/tools/gnunet-search.c:125 +#: src/applications/fs/tools/gnunet-download.c:77 +msgid "set the desired LEVEL of sender-anonymity" +msgstr "Gewünschten Grad an Sender-Anonymität festlegen" + +#: src/applications/fs/tools/gnunet-pseudonym.c:73 +msgid "automate creation of a namespace by starting a collection" +msgstr "" +"Erstellung eines Namespaces durch das Anfangen einer Collection " +"automatisieren" + +#: src/applications/fs/tools/gnunet-pseudonym.c:77 +msgid "create a new pseudonym under the given NICKNAME" +msgstr "Neues Pseudonym unter dem angegebenen NICKNAME erstellen" + +#: src/applications/fs/tools/gnunet-pseudonym.c:80 +msgid "delete the pseudonym with the given NICKNAME" +msgstr "Pseudonym mit dem angegeben NICKNAME löschen" + +#: src/applications/fs/tools/gnunet-pseudonym.c:83 +msgid "end automated building of a namespace (ends collection)" +msgstr "" +"Automatisierte Erstellung eines Namespaces beenden (beendet Collection)" + +#: src/applications/fs/tools/gnunet-pseudonym.c:85 +msgid "Create new pseudonyms, delete pseudonyms or list existing pseudonyms." +msgstr "" +"Erstellen von neuen Pseudonymen, Löschen von Pseudonymen und Auflisten von " +"bestehenden Pseudonymen." + +#: src/applications/fs/tools/gnunet-pseudonym.c:89 +msgid "" +"use the given keyword to advertise the namespace (use when creating a new " +"pseudonym)" +msgstr "" +"Das angegebene schlüsselwort verwenden, um den Namespace bekanntzumachen (zu " +"verwenden, wenn ein neues Pseudonym erstellt wird)" + +#: src/applications/fs/tools/gnunet-pseudonym.c:92 +#, fuzzy +msgid "specify metadata describing the namespace or collection" +msgstr "" +"Automatisierte Erstellung eines Namespaces beenden (beendet Collection)" + +#: src/applications/fs/tools/gnunet-pseudonym.c:96 +msgid "" +"do not generate an advertisement for this namespace (use when creating a new " +"pseudonym)" +msgstr "" +"Keine Bekanntmachung für diesen Namespace erstellen (zu verwenden, wenn ein " +"neues Pseudonym erstellt wird)" + +#: src/applications/fs/tools/gnunet-pseudonym.c:99 +msgid "do not list the pseudonyms from the pseudonym database" +msgstr "Keine Ausgabe der Pseudonyme in der Pseudonymdatenbank" + +#: src/applications/fs/tools/gnunet-pseudonym.c:103 +msgid "" +"specify IDENTIFIER to be the address of the entrypoint to content in the " +"namespace (use when creating a new pseudonym)" +msgstr "" +"IDENTIFIER als Adresse angeben, die der Einsprungspunkt zu den Inhalten im " +"Namespace ist (zu verwenden, wenn ein neues Pseudonym erstellt wird)" + +#: src/applications/fs/tools/gnunet-pseudonym.c:106 +msgid "set the rating of a namespace" +msgstr "das Rating eines Namespaces setzen" + +#: src/applications/fs/tools/gnunet-pseudonym.c:141 +#, c-format +msgid "Namespace `%s' has rating %d.\n" +msgstr "Namespace `%s' hat das Rating %d.\n" + +#: src/applications/fs/tools/gnunet-pseudonym.c:143 +#, c-format +msgid "Namespace `%s' (%s) has rating %d.\n" +msgstr "Namespace `%s' (%s) hat das Rating %d.\n" + +#: src/applications/fs/tools/gnunet-pseudonym.c:175 +#, c-format +msgid "\tRating (after update): %d\n" +msgstr "\tRating (nach Update): %d\n" + +#: src/applications/fs/tools/gnunet-pseudonym.c:179 +#: src/applications/fs/tools/gnunet-pseudonym.c:241 +#: src/applications/fs/tools/gnunet-insert.c:101 +#, fuzzy, c-format +msgid "\tUnknown namespace `%s'\n" +msgstr "Unbekannte Operation `%s'\n" + +#: src/applications/fs/tools/gnunet-pseudonym.c:217 +#, c-format +msgid "Collection stopped.\n" +msgstr "Collection beendet.\n" + +#: src/applications/fs/tools/gnunet-pseudonym.c:219 +#, c-format +msgid "Failed to stop collection (not active?).\n" +msgstr "Fehler beim Beenden der Collection (nicht aktiv?).\n" + +#: src/applications/fs/tools/gnunet-pseudonym.c:230 +#, c-format +msgid "Pseudonym `%s' deleted.\n" +msgstr "Pseudonym `%s' wurde gelöscht.\n" + +#: src/applications/fs/tools/gnunet-pseudonym.c:235 +#, c-format +msgid "Error deleting pseudonym `%s' (does not exist?).\n" +msgstr "Fehler beim Löschen des Pseudonyms `%s' (existiert nicht?).\n" + +#: src/applications/fs/tools/gnunet-pseudonym.c:256 +#, fuzzy +msgid "Started collection.\n" +msgstr "Collection `%s' begonnen.\n" + +#: src/applications/fs/tools/gnunet-pseudonym.c:260 +msgid "Failed to start collection.\n" +msgstr "Fehler beim Starten der Collection.\n" + +#: src/applications/fs/tools/gnunet-pseudonym.c:296 +#, fuzzy +msgid "Could not create namespace.\n" +msgstr "Namespace `%s' konnte nicht erstellt werden (existiert bereits?).\n" + +#: src/applications/fs/tools/gnunet-pseudonym.c:304 +#, c-format +msgid "Namespace `%s' created (root: %s).\n" +msgstr "Namespace `%s' wurde erstellt (Root: %s).\n" + +#: src/applications/fs/tools/gnunet-pseudonym.c:321 +#, fuzzy, c-format +msgid "You must specify a name for the collection (`%s' option).\n" +msgstr "" +"Sie müssen einen Namen für die PID Datei in Sektion `%s' unter `%s' " +"angeben.\n" + +#: src/applications/fs/tools/gnunet-pseudonym.c:329 +#, c-format +msgid "Could not access namespace information.\n" +msgstr "Auf die Namespace Informationen konnte nicht zugegriffen werden.\n" + +#: src/applications/fs/tools/gnunet-directory.c:84 +#, c-format +msgid "==> Directory `%s':\n" +msgstr "==> Verzeichnis `%s':\n" + +#: src/applications/fs/tools/gnunet-directory.c:88 +#, c-format +msgid "=\tError reading directory.\n" +msgstr "=\tFehler beim Lesen des Verzeichnisses.\n" + +#: src/applications/fs/tools/gnunet-directory.c:118 +#, c-format +msgid "File format error (not a GNUnet directory?)\n" +msgstr "Dateiformat fehlerhaft (kein GNUnet Verzeichnis?)\n" + +#: src/applications/fs/tools/gnunet-directory.c:120 +#, c-format +msgid "%d files found in directory.\n" +msgstr "%d Dateien im Verzeichnis gefunden.\n" + +#: src/applications/fs/tools/gnunet-directory.c:135 +msgid "Perform directory related operations." +msgstr "Verzeichnisbezogene Operationen durchführen." + +#: src/applications/fs/tools/gnunet-directory.c:138 +msgid "remove all entries from the directory database and stop tracking URIs" +msgstr "" +"Alle Einträge aus der Verzeichnis Datenbank löschen und das Verfolgen von " +"URIs abbrechen" + +#: src/applications/fs/tools/gnunet-directory.c:142 +msgid "list entries from the directory database" +msgstr "Einträge aus der Verzeichnis Datenbank auflisten" + +#: src/applications/fs/tools/gnunet-directory.c:145 +msgid "start tracking entries for the directory database" +msgstr "Anfangen, Einträge für die Verzeichnis Datenbank zu verfolgen" + +#: src/applications/fs/tools/gnunet-directory.c:168 +#, c-format +msgid "Listed %d matching entries.\n" +msgstr "%d übereinstimmende Einträge aufgelistet.\n" + +#: src/applications/fs/tools/gnunet-auto-share.c:117 +#, fuzzy, c-format +msgid "Upload of `%s' at %llu out of %llu bytes.\n" +msgstr "Download der Datei `%s' bei %16llu von %16llu Bytes (%8.3f kbps)\n" + +#: src/applications/fs/tools/gnunet-auto-share.c:130 +#, fuzzy, c-format +msgid "Upload of `%s' complete, URI is `%s'.\n" +msgstr "" +"Upload von `%s' komplett, derzeitige durchschnittliche Geschwindigkeit " +"beträgt %8.3f KB/s.\n" + +#: src/applications/fs/tools/gnunet-auto-share.c:139 +#, fuzzy, c-format +msgid "Upload aborted.\n" +msgstr "" +"\n" +"Upload abgebrochen.\n" + +#: src/applications/fs/tools/gnunet-auto-share.c:145 +#, fuzzy, c-format +msgid "Error uploading file: %s\n" +msgstr "" +"\n" +"Fehler beim Uploaden der Datei: %s\n" + +#: src/applications/fs/tools/gnunet-auto-share.c:154 +#, fuzzy, c-format +msgid "Starting upload of `%s'.\n" +msgstr "Collection `%s' begonnen.\n" + +#: src/applications/fs/tools/gnunet-auto-share.c:164 +#, fuzzy, c-format +msgid "Uploading suspended.\n" +msgstr "Upload abgewiesen!" + +#: src/applications/fs/tools/gnunet-auto-share.c:179 +#, fuzzy, c-format +msgid "Uploading `%s' resumed.\n" +msgstr "Upload abgewiesen!" + +#: src/applications/fs/tools/gnunet-auto-share.c:186 +#, c-format +msgid "Unexpected event: %d\n" +msgstr "" + +#: src/applications/fs/tools/gnunet-auto-share.c:205 +#, fuzzy +msgid "" +"run in debug mode; gnunet-auto-share will not daemonize and error messages " +"will be written to stderr instead of a logfile" +msgstr "" +"Im Debug-Modus ausführen. gnunetd wird nicht im Hintergrund laufen und " +"Fehlermeldungen werden nicht in die Protokolldatei, sondern auf die " +"Standardfehlerausgabe (stderr) geschrieben." + +#: src/applications/fs/tools/gnunet-auto-share.c:211 +#: src/applications/fs/tools/gnunet-insert.c:259 +#, fuzzy +msgid "" +"do not use libextractor to add additional references to directory entries " +"and/or the published file" +msgstr "" +"benutze libextractor um weitere direkte Referenzen zu Dateien in " +"Verzeichnissen zu erzeugen" + +#: src/applications/fs/tools/gnunet-auto-share.c:213 +msgid "Automatically share a directory." +msgstr "" + +#: src/applications/fs/tools/gnunet-auto-share.c:216 +#: src/applications/fs/tools/gnunet-insert.c:273 +msgid "" +"add an additional keyword for all files and directories (this option can be " +"specified multiple times)" +msgstr "" +"Ein zusätzliches Schlüsselwort für alle Dateien und Verzeichnisse hinzufügen " +"(diese Option kann mehrmals angegeben werden)" + +#: src/applications/fs/tools/gnunet-auto-share.c:221 +#: src/applications/fs/tools/gnunet-insert.c:290 +msgid "specify the priority of the content" +msgstr "Die Priorität des Inhalts angeben" + +#: src/applications/fs/tools/gnunet-auto-share.c:468 +#: src/applications/fs/tools/gnunet-auto-share.c:903 +#, fuzzy, c-format +msgid "Could not access `%s': %s\n" +msgstr "`%s' konnte nicht aufgelöst werden: %s\n" + +#: src/applications/fs/tools/gnunet-auto-share.c:547 +#, c-format +msgid "Unknown keyword type `%s' in metadata configuration\n" +msgstr "" + +#: src/applications/fs/tools/gnunet-auto-share.c:652 +#, fuzzy, c-format +msgid "Failed to stop running gnunet-auto-share.\n" +msgstr "Es konnte keine Verbindung mit gnunetd hergestellt werden.\n" + +#: src/applications/fs/tools/gnunet-auto-share.c:918 +#, c-format +msgid "Directory `%s' is already on the list of shared directories.\n" +msgstr "" + +#: src/applications/fs/tools/gnunet-auto-share.c:939 +msgid "" +"The specified directories were added to the list of shared directories.\n" +msgstr "" + +#: src/applications/fs/tools/gnunet-auto-share.c:961 +#, fuzzy, c-format +msgid "Could not open logfile `%s': %s\n" +msgstr "Datei wurde als `%s' gespeichert.\n" + +#: src/applications/fs/tools/gnunet-insert.c:115 +#, c-format +msgid "Created entry `%s' in namespace `%s'\n" +msgstr "Eintag `%s' in Namespace `%s' wurde erstellt\n" + +#: src/applications/fs/tools/gnunet-insert.c:120 +#, c-format +msgid "Failed to add entry to namespace `%s' (does it exist?)\n" +msgstr "" +"Der Eintrag konnte dem Namespace `%s' nicht hinzugefügt werden (existiert " +"er?)\n" + +#: src/applications/fs/tools/gnunet-insert.c:135 +#, c-format +msgid "Keywords for file `%s':\n" +msgstr "Schlüsselwörter für Datei `%s':\n" + +#: src/applications/fs/tools/gnunet-insert.c:144 +msgid "filename" +msgstr "" + +#: src/applications/fs/tools/gnunet-insert.c:146 +msgid "mimetype" +msgstr "" + +#: src/applications/fs/tools/gnunet-insert.c:186 +#, c-format +msgid "%16llu of %16llu bytes inserted (estimating %6s to completion) - %s\n" +msgstr "" +"%16llu von %16llu Bytes eingefügt (geschätzte %6s bis Fertigstellung) - %s\n" + +#: src/applications/fs/tools/gnunet-insert.c:198 +#, c-format +msgid "Upload of `%s' complete, %llu bytes took %llu seconds (%8.3f KiB/s).\n" +msgstr "" +"Upload von `%s' komplett, %llu Bytes brauchten %llu Sekunden (%8.3f KB/s).\n" + +#: src/applications/fs/tools/gnunet-insert.c:209 +#, c-format +msgid "File `%s' has URI: %s\n" +msgstr "Datei `%s' hat URI: %s\n" + +#: src/applications/fs/tools/gnunet-insert.c:220 +#, c-format +msgid "" +"\n" +"Upload aborted.\n" +msgstr "" +"\n" +"Upload abgebrochen.\n" + +#: src/applications/fs/tools/gnunet-insert.c:225 +#, fuzzy, c-format +msgid "" +"\n" +"Error uploading file: %s" +msgstr "" +"\n" +"Fehler beim Uploaden der Datei: %s\n" + +#: src/applications/fs/tools/gnunet-insert.c:234 +#, c-format +msgid "" +"\n" +"Unexpected event: %d\n" +msgstr "" + +#: src/applications/fs/tools/gnunet-insert.c:250 +msgid "" +"even if gnunetd is running on the local machine, force the creation of a " +"copy instead of making a link to the GNUnet share directory" +msgstr "" +"Obwohl gnunetd auf der lokalen Maschiene läuft die Erstellung einer Kopie " +"anstelle der Erzeugung eines Links auf das GNUnet Share-Verzeichnis " +"erzwingen." + +#: src/applications/fs/tools/gnunet-insert.c:255 +msgid "disable adding the creation time to the metadata of the uploaded file" +msgstr "" + +#: src/applications/fs/tools/gnunet-insert.c:263 +msgid "" +"print list of extracted keywords that would be used, but do not perform " +"upload" +msgstr "" +"Liste der extrahierten Schlüsselworte, die verwendet werden würden, " +"ausgeben, aber keinen Upload durchführen" + +#: src/applications/fs/tools/gnunet-insert.c:265 +msgid "Make files available to GNUnet for sharing." +msgstr "Dateien GNUnet zum Filesharing zur Verfügung stellen." + +#: src/applications/fs/tools/gnunet-insert.c:269 +msgid "" +"add an additional keyword for the top-level file or directory (this option " +"can be specified multiple times)" +msgstr "" +"Ein zusätzliches Schlüsselwort für die Datei oder das Verzeichnis auf der " +"obersten Ebene hinzufügen (diese Option kann mehrmals angegeben werden)" + +#: src/applications/fs/tools/gnunet-insert.c:278 +msgid "set the meta-data for the given TYPE to the given VALUE" +msgstr "" +"Die Meta-Daten des angegebenen Typs TYPE auf den angegebenen Wert VALUE " +"setzen" + +#: src/applications/fs/tools/gnunet-insert.c:281 +msgid "" +"do not index, perform full insertion (stores entire file in encrypted form " +"in GNUnet database)" +msgstr "" +"Nicht indizieren, sondern komplett einfügen (speichert die gesamte Datei in " +"verschlüsselter Form in der GNUnet Datenbank)" + +#: src/applications/fs/tools/gnunet-insert.c:286 +msgid "" +"specify ID of an updated version to be published in the future (for " +"namespace insertions only)" +msgstr "" +"ID einer aktualisierten Version angeben, die in der Zukunft veröffentlich " +"werden soll. (nur für das Einfügen in Namespaces)" + +#: src/applications/fs/tools/gnunet-insert.c:294 +msgid "publish the files under the pseudonym NAME (place file into namespace)" +msgstr "" +"Die Datei unter dem Pseudonym NAME veröffentlichen (platziert die Datei in " +"einem Namespace)" + +#: src/applications/fs/tools/gnunet-insert.c:297 +msgid "" +"only simulte the process but do not do any actual publishing (useful to " +"compute URIs)" +msgstr "" + +#: src/applications/fs/tools/gnunet-insert.c:301 +msgid "" +"set the ID of this version of the publication (for namespace insertions only)" +msgstr "" +"die ID dieser Version der Veröffentlichung setzen (nur für das Einfügen in " +"Namespaces)" + +#: src/applications/fs/tools/gnunet-insert.c:305 +msgid "" +"URI to be published (can be used instead of passing a file to add keywords " +"to the file with the respective URI)" +msgstr "" + +#: src/applications/fs/tools/gnunet-insert.c:342 +#, c-format +msgid "You must specify one and only one filename for insertion.\n" +msgstr "Sie dürfen nur eine Datei zum Deindizieren angeben.\n" + +#: src/applications/fs/tools/gnunet-insert.c:348 +#, fuzzy, c-format +msgid "You must NOT specify an URI and a filename.\n" +msgstr "Sie müssen einen Empfänger angeben!\n" + +#: src/applications/fs/tools/gnunet-insert.c:354 +#, c-format +msgid "Cannot extract metadata from a URI!\n" +msgstr "" + +#: src/applications/fs/tools/gnunet-insert.c:400 +#, c-format +msgid "Could not access namespace `%s' (does not exist?).\n" +msgstr "Konnte nicht auf den Namespace `%s' zugreifen (existiert nicht?).\n" + +#: src/applications/fs/tools/gnunet-insert.c:408 +#, fuzzy, c-format +msgid "Option `%s' is required when using option `%s'.\n" +msgstr "Option `%s' macht keinen Sinn ohne die Option `%s'.\n" + +#: src/applications/fs/tools/gnunet-insert.c:419 +#: src/applications/fs/tools/gnunet-insert.c:427 +#, c-format +msgid "Option `%s' makes no sense without option `%s'.\n" +msgstr "Option `%s' macht keinen Sinn ohne die Option `%s'.\n" + +#: src/applications/fs/tools/gnunet-search.c:128 +msgid "Search GNUnet for files." +msgstr "Das GNUnet nach Dateien durchsuchen." + +#: src/applications/fs/tools/gnunet-search.c:132 +msgid "write encountered (decrypted) search results to FILENAME" +msgstr "Begegnete (entschlüsselte) Suchergebnisse in FILENAME schreiben" + +#: src/applications/fs/tools/gnunet-search.c:169 +#, c-format +msgid "Error converting arguments to URI!\n" +msgstr "Fehler beim Konvertieren von Parametern in URI!\n" + +#: src/applications/fs/tools/gnunet-unindex.c:61 +#, c-format +msgid "" +"%16llu of %16llu bytes unindexed (estimating %llu seconds to " +"completion) " +msgstr "" +"%16llu von %16llu Bytes deindiziert (schätze %llu Sekunden bis " +"Fertigstellung) " + +#: src/applications/fs/tools/gnunet-unindex.c:73 +#, c-format +msgid "" +"\n" +"Unindexing of `%s' complete, %llu bytes took %llu seconds (%8.3f KiB/s).\n" +msgstr "" +"\n" +"Deindizierung von `%s' abgeschlossen, %llu Bytes benötigten %llu Sekunden (%" +"8.3f kbps).\n" + +#: src/applications/fs/tools/gnunet-unindex.c:88 +#, c-format +msgid "" +"\n" +"Error unindexing file: %s\n" +msgstr "" +"\n" +"Fehler beim Deindizieren der Datei: %s\n" + +#: src/applications/fs/tools/gnunet-unindex.c:108 +msgid "Unindex files." +msgstr "Dateien deindizieren." + +#: src/applications/fs/tools/gnunet-unindex.c:145 +#, fuzzy +msgid "Not enough arguments. You must specify a filename.\n" +msgstr "Nicht genügend Parameter. Sie müssen eine GNUnet Datei URI angeben\n" + +#: src/applications/fs/tools/gnunet-unindex.c:163 +#, c-format +msgid "`%s' failed. Is `%s' a file?\n" +msgstr "`%s' schlug fehl. Ist `%s' eine Datei?\n" + +#: src/applications/fs/tools/gnunet-download.c:82 +msgid "" +"download a GNUnet directory that has already been downloaded. Requires that " +"a filename of an existing file is specified instead of the URI. The " +"download will only download the top-level files in the directory unless the " +"`-R' option is also specified." +msgstr "" + +#: src/applications/fs/tools/gnunet-download.c:85 +msgid "delete incomplete downloads (when aborted with CTRL-C)" +msgstr "" + +#: src/applications/fs/tools/gnunet-download.c:87 +msgid "Download files from GNUnet." +msgstr "Dateien aus dem GNUnet herunterladen." + +#: src/applications/fs/tools/gnunet-download.c:91 +msgid "write the file to FILENAME" +msgstr "Schreibe die Datei in DATEINAME" + +#: src/applications/fs/tools/gnunet-download.c:95 +msgid "set the maximum number of parallel downloads that are allowed" +msgstr "" + +#: src/applications/fs/tools/gnunet-download.c:98 +msgid "download a GNUnet directory recursively" +msgstr "Das GNUnet Verzeichnis rekursiv herunterladen" + +#: src/applications/fs/tools/gnunet-download.c:119 +#, c-format +msgid "Download of file `%s' at %16llu out of %16llu bytes (%8.3f KiB/s)\n" +msgstr "Download der Datei `%s' bei %16llu von %16llu Bytes (%8.3f kbps)\n" + +#: src/applications/fs/tools/gnunet-download.c:133 +#, c-format +msgid "Download aborted.\n" +msgstr "Download abgebrochen.\n" + +#: src/applications/fs/tools/gnunet-download.c:139 +#, c-format +msgid "Error downloading: %s\n" +msgstr "Fehler beim Download: %s\n" + +#: src/applications/fs/tools/gnunet-download.c:145 +#, c-format +msgid "Download of file `%s' complete. Speed was %8.3f KiB per second.\n" +msgstr "" +"Download der Datei `%s' abgeschlossen. Geschwindigkeit war %8.3f KB/s.\n" + +#: src/applications/fs/tools/gnunet-download.c:191 +msgid "no name given" +msgstr "" + +#: src/applications/fs/tools/gnunet-download.c:197 +#, fuzzy, c-format +msgid "Starting download `%s'\n" +msgstr "Collection `%s' begonnen.\n" + +#: src/applications/fs/tools/gnunet-download.c:239 +msgid "Not enough arguments. You must specify a GNUnet file URI\n" +msgstr "Nicht genügend Parameter. Sie müssen eine GNUnet Datei URI angeben\n" + +#: src/applications/fs/tools/gnunet-download.c:257 +#, c-format +msgid "URI `%s' invalid for gnunet-download.\n" +msgstr "URI `%s' ist ungültig für gnunet-download.\n" + +#: src/applications/fs/tools/gnunet-download.c:300 +#, fuzzy, c-format +msgid "No filename specified, using `%s' instead (for now).\n" +msgstr "Kein Tabellenname angegeben, verwende `%s'.\n" + +#: src/applications/fs/tools/gnunet-download.c:342 +#, fuzzy, c-format +msgid "Could not access gnunet-directory file `%s'\n" +msgstr "Konfigurationsdatei `%s' konnte nicht geparst werden.\n" + +#: src/applications/fs/tools/gnunet-download.c:363 +#, fuzzy, c-format +msgid "Downloading %d files from directory `%s'.\n" +msgstr "Dateien aus dem GNUnet herunterladen." + +#: src/applications/fs/tools/gnunet-download.c:366 +#, fuzzy, c-format +msgid "Did not find any files in directory `%s'\n" +msgstr "Fehler beim Lesen der Freunde-Liste von `%s'\n" + +#: src/applications/fs/tools/gnunet-download.c:404 +#, c-format +msgid "File stored as `%s'.\n" +msgstr "Datei wurde als `%s' gespeichert.\n" + +#: src/applications/fs/uritrack/file_info.c:98 +msgid "Collecting file identifiers disabled.\n" +msgstr "Einsammeln von Dateibezeichnern deaktiviert.\n" + +#: src/applications/fs/uritrack/file_info.c:377 +#, c-format +msgid "Deleted corrupt URI database in `%s'." +msgstr "" + +#: src/applications/fs/ecrs/upload.c:158 +#, c-format +msgid "`%s' is not a file.\n" +msgstr "`%s' ist keine Datei.\n" + +#: src/applications/fs/ecrs/upload.c:166 +#, c-format +msgid "Cannot get size of file `%s'" +msgstr "Die Größe der Datei `%s' konnte nicht ermittelt werden" + +#: src/applications/fs/ecrs/upload.c:175 +msgid "Failed to connect to gnunetd." +msgstr "Es konnte keine Verbindung mit gnunetd hergestellt werden." + +#: src/applications/fs/ecrs/upload.c:187 +#, c-format +msgid "Cannot hash `%s'.\n" +msgstr "`%s' konnte nicht gehashed werden.\n" + +#: src/applications/fs/ecrs/upload.c:215 +#, c-format +msgid "Initialization for indexing file `%s' failed.\n" +msgstr "Initialisierung der Indizierung der Datei `%s' fehlgeschlagen.\n" + +#: src/applications/fs/ecrs/upload.c:223 +#, fuzzy, c-format +msgid "Indexing file `%s' failed. Suggestion: try to insert the file.\n" +msgstr "Indizieren der Datei `%s' schlug fehl. Versuch Datei einzufügen...\n" + +#: src/applications/fs/ecrs/upload.c:237 +#, fuzzy, c-format +msgid "Cannot open file `%s': `%s'" +msgstr "Konfigurationsdatei `%s' konnte nicht geöffnet werden.\n" + +#: src/applications/fs/ecrs/upload.c:322 +#, fuzzy, c-format +msgid "Indexing data of file `%s' failed at position %llu.\n" +msgstr "Indizierung der Daten schlug an Position %i fehl.\n" + +#: src/applications/fs/ecrs/helper.c:91 +msgid "No keywords specified!\n" +msgstr "Keine Schlüsselwörter angegeben!\n" + +#: src/applications/fs/ecrs/helper.c:99 +msgid "Number of double-quotes not balanced!\n" +msgstr "" + +#: src/applications/fs/ecrs/helper.c:398 +#, c-format +msgid "Renaming of file `%s' to `%s' failed: %s\n" +msgstr "Umbenennen der Datei `%s' zu `%s' fehlgeschlagen: %s\n" + +#: src/applications/fs/ecrs/helper.c:408 +#, c-format +msgid "Could not rename file `%s' to `%s': file exists\n" +msgstr "" +"Datei `%s' konnte nicht zu `%s' umbenannt werden: Datei existiert bereits\n" + +#: src/applications/fs/ecrs/parser.c:165 +#, c-format +msgid "" +"Unknown metadata type in metadata option `%s'. Using metadata type " +"`unknown' instead.\n" +msgstr "" + +#: src/applications/fs/ecrs/search.c:152 +msgid "CHK URI not allowed for search.\n" +msgstr "CHK URI ist nicht erlaubt für Suchen.\n" + +#: src/applications/fs/ecrs/search.c:207 +msgid "LOC URI not allowed for search.\n" +msgstr "LOC URI ist nicht erlaubt für Suchen.\n" + +#: src/applications/fs/ecrs/namespace.c:365 +#, c-format +msgid "File `%s' does not contain a pseudonym.\n" +msgstr "Die Datei `%s' enthält kein Pseudonym.\n" + +#: src/applications/fs/ecrs/namespace.c:376 +#, c-format +msgid "Format of pseudonym `%s' is invalid.\n" +msgstr "Format des Pseudonyms `%s' ist ungültig.\n" + +#: src/applications/fs/ecrs/namespace.c:535 +#: src/applications/fs/ecrs/namespace.c:547 +#: src/applications/fs/ecrs/namespace.c:559 +#, fuzzy, c-format +msgid "Format of file `%s' is invalid, trying to remove.\n" +msgstr "Format der Datei `%s' ist ungültig.\n" + +#: src/applications/fs/ecrs/download.c:599 +msgid "" +"Decrypted content does not match key. This is either a bug or a maliciously " +"inserted file. Download aborted.\n" +msgstr "" +"Entschlüsselter Inhalt entspricht nicht dem Schlüssel. Dies ist entweder ein " +"Bug oder eine mit bösen Absichten eingefügte Datei. Download wurde " +"abgebrochen.\n" + +#: src/applications/fs/ecrs/download.c:609 +msgid "IO error." +msgstr "" + +#: src/applications/fs/collection/collection.c:559 +#: src/applications/fs/collection/collection.c:562 +#, c-format +msgid "Revision %u" +msgstr "" + +#: src/applications/fs/fsui/upload.c:330 +#, fuzzy +msgid "Application aborted." +msgstr "_Optionen" + +#: src/applications/fs/fsui/upload.c:344 +#, fuzzy +msgid "Failed to create temporary directory." +msgstr "Dateiformat fehlerhaft (kein GNUnet Verzeichnis?)\n" + +#: src/applications/fs/fsui/deserialize.c:927 +#, c-format +msgid "FSUI state file `%s' had syntax error at offset %u.\n" +msgstr "FSUI Statusdatei `%s' hatte einen Syntaxfehler bei Offset %u.\n" + +#: src/applications/fs/fsui/unindex.c:114 +msgid "Unindexing failed (no reason given)" +msgstr "" + +#: src/applications/fs/gap/plan.c:944 +#, fuzzy +msgid "# gap requests total sent" +msgstr "# gap Anfragen insgesamt empfangen" + +#: src/applications/fs/gap/plan.c:946 +#, fuzzy +msgid "# gap content total planned" +msgstr "# gap Anfragen insgesamt empfangen" + +#: src/applications/fs/gap/plan.c:948 +#, fuzzy +msgid "# gap routes succeeded" +msgstr "# gap Routing erfolgreich (insgesamt)" + +#: src/applications/fs/gap/plan.c:949 +msgid "# trust spent" +msgstr "" + +#: src/applications/fs/gap/fs.c:157 +msgid "Datastore full.\n" +msgstr "" + +#: src/applications/fs/gap/fs.c:831 +msgid "# gap requests total received" +msgstr "# gap Anfragen insgesamt empfangen" + +#: src/applications/fs/gap/fs.c:833 +#, fuzzy +msgid "# gap requests dropped due to load" +msgstr "# gap Anfragen verworfen: Kollision in RT" + +#: src/applications/fs/gap/fs.c:835 +#, fuzzy +msgid "# gap content total received" +msgstr "# gap Anfragen insgesamt empfangen" + +#: src/applications/fs/gap/fs.c:837 +msgid "# gap total trust awarded" +msgstr "" + +#: src/applications/fs/gap/fs.c:865 +#, fuzzy, c-format +msgid "" +"`%s' registering client handlers %d %d %d %d %d %d %d %d and P2P handlers %d " +"%d\n" +msgstr "`%s' registriert die Client Handler %d %d %d %d %d %d %d %d %d\n" + +#: src/applications/fs/gap/fs.c:921 +msgid "enables (anonymous) file-sharing" +msgstr "Ermöglicht (anonymes) Filesharing" + +#: src/applications/fs/gap/ondemand.c:173 +#, c-format +msgid "" +"Because the file `%s' has been unavailable for 3 days it got removed from " +"your share. Please unindex files before deleting them as the index now " +"contains invalid references!\n" +msgstr "" +"Weil die Datei `%s' nun seit 3 Tagen nicht zur Verfügung steht, wurde Sie " +"aus Ihrem Share entfernt. Bitte deindizieren Sie Dateien, bevor Sie sie " +"löschen, da Ihr Index nun ungültige Referenzen enthält!\n" + +#: src/applications/fs/gap/ondemand.c:451 +msgid "Indexed content changed (does not match its hash).\n" +msgstr "" + +#: src/applications/fs/gap/ondemand.c:569 +#, c-format +msgid "" +"Unindexed ODB block `%s' from offset %llu already missing from datastore.\n" +msgstr "" +"Deindizierter ODB Block `%s' vom Offset %llu fehlt bereits im " +"Datenspeicher.\n" + +#: src/applications/fs/gap/pid_table.c:177 +msgid "# distinct interned peer IDs in pid table" +msgstr "" + +#: src/applications/fs/gap/pid_table.c:180 +msgid "# total RC of interned peer IDs in pid table" +msgstr "" + +#: src/applications/fs/gap/querymanager.c:708 +#, fuzzy +msgid "# gap client queries received" +msgstr "# gap Anfragen insgesamt empfangen" + +#: src/applications/fs/gap/querymanager.c:710 +#, fuzzy +msgid "# gap replies sent to clients" +msgstr "# gap Anfragen insgesamt empfangen" + +#: src/applications/fs/gap/querymanager.c:712 +#, fuzzy +msgid "# gap client requests tracked" +msgstr "# gap Anfragen insgesamt empfangen" + +#: src/applications/fs/gap/querymanager.c:714 +#, fuzzy +msgid "# gap client requests injected" +msgstr "# Client Trace-Anfragen empfangen" + +#: src/applications/fs/gap/querymanager.c:717 +msgid "# gap query bloomfilter resizing updates" +msgstr "" + +#: src/applications/fs/gap/migration.c:437 +msgid "# blocks migrated" +msgstr "# Blöcke migriert" + +#: src/applications/fs/gap/migration.c:439 +#, fuzzy +msgid "# blocks injected for migration" +msgstr "# Blöcke migriert" + +#: src/applications/fs/gap/migration.c:441 +#, fuzzy +msgid "# blocks fetched for migration" +msgstr "# Blöcke migriert" + +#: src/applications/fs/gap/migration.c:443 +#, fuzzy +msgid "# on-demand fetches for migration" +msgstr "# Blöcke migriert" + +#: src/applications/fs/gap/gap.c:694 +#, fuzzy +msgid "# gap queries dropped (table full)" +msgstr "# gap falsche Antworten" + +#: src/applications/fs/gap/gap.c:696 +#, fuzzy +msgid "# gap queries dropped (redundant)" +msgstr "# gap falsche Antworten" + +#: src/applications/fs/gap/gap.c:698 +#, fuzzy +msgid "# gap queries routed" +msgstr "# dht Anfragen weitergeleitet" + +#: src/applications/fs/gap/gap.c:700 +msgid "# gap content found locally" +msgstr "" + +#: src/applications/fs/gap/gap.c:703 +msgid "# gap queries refreshed existing record" +msgstr "" + +#: src/applications/fs/gap/gap.c:704 +#, fuzzy +msgid "# trust earned" +msgstr "# dht Anfragen weitergeleitet" + +#: src/applications/fs/gap/fs_dht.c:256 +msgid "# blocks pushed into DHT" +msgstr "" + +#: src/applications/fs/gap/anonymity.c:56 +msgid "Failed to get traffic stats.\n" +msgstr "Statistiken über den Netzwerkverkehr konnten nicht ermittelt werden.\n" + +#: src/applications/testing/remote.c:68 +#, c-format +msgid "scp command is : %s \n" +msgstr "" + +#: src/applications/testing/remote.c:491 +#, fuzzy, c-format +msgid "Friend list of %s:%d\n" +msgstr "Es konnte keine Verbindung mit gnunetd hergestellt werden.\n" + +#: src/applications/testing/remote.c:513 +#, c-format +msgid "scp command for friend file copy is : %s \n" +msgstr "" + +#: src/applications/testing/remote.c:535 +#, fuzzy, c-format +msgid "connecting peer %s:%d to peer %s:%d\n" +msgstr "Verbindung zu %u.%u.%u.%u:%u fehlgeschlagen: %s\n" + +#: src/applications/testing/remotetest.c:38 +msgid "Set up multiple gnunetd daemons across multiple hosts." +msgstr "" + +#: src/applications/testing/remotetest.c:43 +#, fuzzy +msgid "set number of daemons to start" +msgstr "Anzahl an Nachrichten, die pro Durchlauf verwendet wird" + +#: src/applications/testing/testing.c:268 +#: src/applications/testing/remotetopologies.c:367 +#, fuzzy, c-format +msgid "Waiting for peers to connect" +msgstr "" +"Warte darauf, dass sich andere Knoten verbinden (%u Iterationen " +"verbleiben)...\n" + +#: src/applications/testing/remotetopologies.c:213 +#, c-format +msgid "Connecting nodes in 2d torus topology: %u rows %u columns\n" +msgstr "" + +#: src/applications/testing/remotetopologies.c:491 +#, fuzzy, c-format +msgid "Failed to establish connection with peers.\n" +msgstr "Fehler beim Aufbauen einer Verbindung mit gnunetd.\n" + +#: src/applications/bootstrap_http/http.c:113 +#, fuzzy, c-format +msgid "Bootstrap data obtained from `%s' is invalid.\n" +msgstr "Format des Pseudonyms `%s' ist ungültig.\n" + +#: src/applications/bootstrap_http/http.c:126 +#: src/applications/bootstrap_http/http.c:277 +#: src/applications/bootstrap_http/http.c:294 +#: src/applications/bootstrap_http/http.c:333 +#: src/applications/bootstrap_http/http.c:352 +#: src/applications/bootstrap_http/http.c:365 +#: src/applications/bootstrap_http/http.c:375 +#: src/applications/bootstrap_http/http.c:385 src/transports/upnp/upnp.c:356 +#: src/transports/upnp/upnp.c:541 src/transports/http.c:1085 +#: src/transports/http.c:1209 src/transports/http.c:1377 +#: src/transports/http.c:1777 src/transports/http.c:1827 +#, c-format +msgid "%s failed at %s:%d: `%s'\n" +msgstr "`%s' schlug fehl bei %s:%d mit dem Fehler: `%s'.\n" + +#: src/applications/bootstrap_http/http.c:185 +msgid "No hostlist URL specified in configuration, will not bootstrap.\n" +msgstr "" + +#: src/applications/bootstrap_http/http.c:226 +#, c-format +msgid "Bootstrapping using `%s'.\n" +msgstr "" + +#: src/applications/bootstrap_http/http.c:254 +#, fuzzy, c-format +msgid "Trying to download hostlist from `%s'\n" +msgstr "Fehler beim Lesen der Freunde-Liste von `%s'\n" + +#: src/applications/bootstrap_http/http.c:391 +#, fuzzy, c-format +msgid "Downloaded %llu bytes from `%s'.\n" +msgstr "Dateien aus dem GNUnet herunterladen." + +#: src/applications/bootstrap_http/http.c:425 +#, fuzzy +msgid "# HELLOs downloaded via http" +msgstr "# Hellos per HTTP heruntergeladen" + +#: src/applications/getoption/getoption.c:78 +#, c-format +msgid "`%s' registering client handler %d\n" +msgstr "`%s' registriert Client-Handler %d\n" + +#: src/applications/getoption/getoption.c:88 +msgid "allows clients to determine gnunetd's configuration" +msgstr "Erlaubt es Clients die Konfiguration von gnunetd abzufragen" + +#: src/applications/template/template.c:70 +#, c-format +msgid "`%s' registering client handler %d and %d\n" +msgstr "`%s' registriert Client-Handler %d and %d\n" + +#: src/applications/template/gnunet-template.c:42 +msgid "Template description." +msgstr "" + +#: src/applications/stats/clientapi.c:331 +msgid "Uptime (seconds)" +msgstr "Laufzeit (Sekunden)" + +#: src/applications/stats/sqstats.c:151 +msgid "# Any-Blocks" +msgstr "" + +#: src/applications/stats/sqstats.c:152 +msgid "# DBlocks" +msgstr "" + +#: src/applications/stats/sqstats.c:153 +msgid "# SBlocks" +msgstr "" + +#: src/applications/stats/sqstats.c:154 +msgid "# KBlocks" +msgstr "" + +#: src/applications/stats/sqstats.c:155 +msgid "# NBlocks" +msgstr "" + +#: src/applications/stats/sqstats.c:156 +msgid "# KNBlocks" +msgstr "" + +#: src/applications/stats/sqstats.c:157 +msgid "# OnDemand-Blocks" +msgstr "" + +#: src/applications/stats/sqstats.c:158 +msgid "# Unknown-Blocks" +msgstr "" + +#: src/applications/stats/sqstats.c:159 +msgid "# expired" +msgstr "" + +#: src/applications/stats/sqstats.c:160 +msgid "# expire in 1h" +msgstr "" + +#: src/applications/stats/sqstats.c:161 +msgid "# expire in 24h" +msgstr "" + +#: src/applications/stats/sqstats.c:162 +msgid "# expire in 1 week" +msgstr "" + +#: src/applications/stats/sqstats.c:163 +msgid "# expire in 1 month" +msgstr "" + +#: src/applications/stats/sqstats.c:164 +msgid "# zero priority" +msgstr "" + +#: src/applications/stats/sqstats.c:165 +msgid "# priority one" +msgstr "" + +#: src/applications/stats/sqstats.c:166 +msgid "# priority larger than one" +msgstr "" + +#: src/applications/stats/sqstats.c:167 +msgid "# no anonymity" +msgstr "" + +#: src/applications/stats/sqstats.c:168 +msgid "# anonymity one" +msgstr "" + +#: src/applications/stats/sqstats.c:169 +msgid "# anonymity larger than one" +msgstr "" + +#: src/applications/stats/statistics.c:238 +#, no-c-format +msgid "% of allowed network load (up)" +msgstr "% of allowed network load (up)" + +#: src/applications/stats/statistics.c:240 +#, no-c-format +msgid "% of allowed network load (down)" +msgstr "% of allowed network load (down)" + +#: src/applications/stats/statistics.c:243 +#, no-c-format +msgid "% of allowed cpu load" +msgstr "% of allowed cpu load" + +#: src/applications/stats/statistics.c:246 +#, fuzzy, no-c-format +msgid "% of allowed io load" +msgstr "% of allowed cpu load" + +#: src/applications/stats/statistics.c:249 +msgid "# bytes of noise received" +msgstr "# Bytes Rauschen empfangen" + +#: src/applications/stats/statistics.c:251 +msgid "# plibc handles" +msgstr "" + +#: src/applications/stats/statistics.c:441 +#, fuzzy, c-format +msgid "`%s' registering client handlers %d %d %d and p2p handler %d\n" +msgstr "`%s' registriert die Client Handler %d %d %d %d %d %d %d %d %d\n" + +#: src/applications/stats/statistics.c:463 +msgid "keeps statistics about gnunetd's operation" +msgstr "Hält Statistiken über Betrieb von gnunetd" + +#: src/applications/stats/gnunet-stats.c:61 +#, c-format +msgid "Supported peer-to-peer messages:\n" +msgstr "unterstützte Peer-to-Peer Nachrichten:\n" + +#: src/applications/stats/gnunet-stats.c:64 +#, c-format +msgid "Supported client-server messages:\n" +msgstr "unterstützte Client-Server Nachrichten:\n" + +#: src/applications/stats/gnunet-stats.c:83 +#: src/applications/vpn/gnunet-vpn.c:59 +msgid "Print statistics about GNUnet operations." +msgstr "Statistiken der GNUnet Aktivitäten ausgeben." + +#: src/applications/stats/gnunet-stats.c:87 +msgid "prints supported protocol messages" +msgstr "gibt unterstützte Protokollnachrichten aus" + +#: src/applications/stats/gnunet-stats.c:136 +#, c-format +msgid "Error reading information from gnunetd.\n" +msgstr "Fehler beim Lesen von Informationen von gnunetd.\n" + +#: src/applications/vpn/gnunet-vpn.c:63 +msgid "Suppress display of asynchronous log messages" +msgstr "" + +#: src/applications/vpn/p2p.c:75 +msgid "VPN IP src not anonymous. drop..\n" +msgstr "" + +#: src/applications/vpn/p2p.c:83 +msgid "VPN IP not anonymous, drop.\n" +msgstr "" + +#: src/applications/vpn/p2p.c:92 +msgid "VPN Received, not anonymous, drop.\n" +msgstr "" + +#: src/applications/vpn/p2p.c:97 +#, fuzzy, c-format +msgid "VPN Received unknown IP version %d...\n" +msgstr "Es wurde eine unbekannte Testbed Nachricht des Typs %u empfangen.\n" + +#: src/applications/vpn/p2p.c:110 +#, c-format +msgid "<- GNUnet(%d) : %s\n" +msgstr "" + +#: src/applications/vpn/p2p.c:139 +msgid "Could not write the tunnelled IP to the OS... Did to setup a tunnel?\n" +msgstr "" + +#: src/applications/vpn/p2p.c:183 +msgid "Receive route request\n" +msgstr "" + +#: src/applications/vpn/p2p.c:193 +#, c-format +msgid "Prepare route announcement level %d\n" +msgstr "" + +#: src/applications/vpn/p2p.c:208 +#, c-format +msgid "Send route announcement %d with route announce\n" +msgstr "" + +#: src/applications/vpn/p2p.c:217 +#, c-format +msgid "Send outside table info %d\n" +msgstr "" + +#: src/applications/vpn/p2p.c:239 +msgid "Receive route announce.\n" +msgstr "" + +#: src/applications/vpn/p2p.c:247 +msgid "Going to try insert route into local table.\n" +msgstr "" + +#: src/applications/vpn/p2p.c:256 +#, c-format +msgid "Inserting with hops %d\n" +msgstr "" + +#: src/applications/vpn/p2p.c:273 +#, c-format +msgid "Request level %d from peer %d\n" +msgstr "" + +#: src/applications/vpn/p2p.c:300 +#, c-format +msgid "Receive table limit on peer reached %d\n" +msgstr "" + +#: src/applications/vpn/vpn.c:180 +#, c-format +msgid "Not storing route to myself from peer %d\n" +msgstr "" + +#: src/applications/vpn/vpn.c:194 +#, c-format +msgid "Duplicate route to node from peer %d, choosing minimum hops" +msgstr "" + +#: src/applications/vpn/vpn.c:230 +#, c-format +msgid "Inserting route from peer %d in route table at location %d\n" +msgstr "" + +#: src/applications/vpn/vpn.c:247 +#, c-format +msgid "RFC4193 Frame length %d is too big for GNUnet!\n" +msgstr "" + +#: src/applications/vpn/vpn.c:254 +#, c-format +msgid "RFC4193 Frame length %d too small\n" +msgstr "" + +#: src/applications/vpn/vpn.c:273 +#, c-format +msgid "RFC4193 Ethertype %x and IP version %x do not match!\n" +msgstr "" + +#: src/applications/vpn/vpn.c:289 +#, c-format +msgid "RFC4193 Going to try and make a tunnel in slot %d\n" +msgstr "" + +#: src/applications/vpn/vpn.c:295 +#, fuzzy, c-format +msgid "Cannot open tunnel device: %s" +msgstr "Konfigurationsdatei `%s' konnte nicht geöffnet werden.\n" + +#: src/applications/vpn/vpn.c:331 +#, c-format +msgid "RFC4193 Create skips gnu%d as we are already using it\n" +msgstr "" + +#: src/applications/vpn/vpn.c:346 +#, c-format +msgid "Cannot set tunnel name to %s because of %s\n" +msgstr "" + +#: src/applications/vpn/vpn.c:356 +#, c-format +msgid "Configured tunnel name to %s\n" +msgstr "" + +#: src/applications/vpn/vpn.c:398 +#, c-format +msgid "Cannot get socket flags for gnu%d because %s\n" +msgstr "" + +#: src/applications/vpn/vpn.c:408 +#, c-format +msgid "Cannot set socket flags for gnu%d because %s\n" +msgstr "" + +#: src/applications/vpn/vpn.c:418 +#, c-format +msgid "Cannot set MTU for gnu%d because %s\n" +msgstr "" + +#: src/applications/vpn/vpn.c:426 +#, c-format +msgid "Cannot get interface index for gnu%d because %s\n" +msgstr "" + +#: src/applications/vpn/vpn.c:440 +#, c-format +msgid "IPv6 ifaddr gnu%d - %x:%x:%x:%x:%x:%x:%x:%x/%d\n" +msgstr "" + +#: src/applications/vpn/vpn.c:455 +#, fuzzy, c-format +msgid "Cannot set interface IPv6 address for gnu%d because %s\n" +msgstr "Es konnte keine IP-Adresse für das Gerät `%s' ermittelt werden.\n" + +#: src/applications/vpn/vpn.c:471 +#, c-format +msgid "IPv6 route gnu%d - destination %x:%x:%x:%x:%x:%x:%x:%x/%d\n" +msgstr "" + +#: src/applications/vpn/vpn.c:485 +#, fuzzy, c-format +msgid "Cannot add route IPv6 address for gnu%s because %s\n" +msgstr "Es konnte keine IP-Adresse für das Gerät `%s' ermittelt werden.\n" + +#: src/applications/vpn/vpn.c:528 +msgid "" +"RFC4193 We have run out of memory and so I can't store a tunnel for this " +"peer.\n" +msgstr "" + +#: src/applications/vpn/vpn.c:579 +#, c-format +msgid "RFC4193 Thread running (frame %d tunnel %d f2f %d) ...\n" +msgstr "" + +#: src/applications/vpn/vpn.c:661 +#, c-format +msgid "VPN dropping connection %x\n" +msgstr "" + +#: src/applications/vpn/vpn.c:670 +#, c-format +msgid "VPN cannot drop connection %x\n" +msgstr "" + +#: src/applications/vpn/vpn.c:690 +msgid "RFC4193 Thread exiting\n" +msgstr "" + +#: src/applications/vpn/vpn.c:712 +msgid "realise alloc ram\n" +msgstr "" + +#: src/applications/vpn/vpn.c:735 +msgid "realise add routes\n" +msgstr "" + +#: src/applications/vpn/vpn.c:849 +msgid "realise copy table\n" +msgstr "" + +#: src/applications/vpn/vpn.c:898 +#, fuzzy, c-format +msgid "`%s' initialising RFC4913 module %d and %d\n" +msgstr "`%s' registriert Handler %d und %d\n" + +#: src/applications/vpn/vpn.c:903 +#, c-format +msgid "RFC4193 my First 4 hex digits of host id are %x\n" +msgstr "" + +#: src/applications/vpn/vpn.c:942 +#, fuzzy +msgid "enables IPv6 over GNUnet (incomplete)" +msgstr "Ermöglicht P2P-Chat" + +#: src/applications/vpn/vpn.c:963 +msgid "RFC4193 Waiting for tun thread to end\n" +msgstr "" + +#: src/applications/vpn/vpn.c:978 +msgid "RFC4193 The tun thread has ended\n" +msgstr "" + +#: src/applications/vpn/vpn.c:996 +#, c-format +msgid "RFC4193 Closing tunnel %d fd %d\n" +msgstr "" + +#: src/server/core.c:119 src/server/core.c:318 +#, c-format +msgid "Configuration value `%s' under [MODULES] for `%s' is invalid!\n" +msgstr "" + +#: src/server/core.c:140 +#, c-format +msgid "Application module `%s' already initialized!\n" +msgstr "Anwendungsmodul `%s' ist bereits initialisiert!\n" + +#: src/server/core.c:194 +#, c-format +msgid "Failed to load plugin `%s' at %s:%d. Unloading plugin.\n" +msgstr "Fehler beim Laden des Plugins `%s' bei %s:%d. Plugin wird entladen.\n" + +#: src/server/core.c:244 +#, c-format +msgid "Could not shutdown `%s': application not loaded\n" +msgstr "`%s' kann nicht heruntergefahren werden: Anwendung ist nicht geladen\n" + +#: src/server/core.c:255 +#, c-format +msgid "Could not shutdown application `%s': not initialized\n" +msgstr "" +"Anwendung `%s' kann nicht heruntergefahren werden: sie ist nicht " +"initialisiert\n" + +#: src/server/core.c:265 +#, c-format +msgid "Could not find '%s%s' method in library `%s'.\n" +msgstr "Methode '%s%s' kann in Bibliothek `%s' nicht gefunden werden.\n" + +#: src/server/core.c:422 +#, c-format +msgid "Could not release %p: service not loaded\n" +msgstr "%p kann nicht freigegeben werden: Dienst ist nicht geladen\n" + +#: src/server/core.c:531 +#, c-format +msgid "Could not properly shutdown application `%s'.\n" +msgstr "Anwendung `%s' konnte nicht ordentlich heruntergefahren werden.\n" + +#: src/server/core.c:676 +#, c-format +msgid "Could not properly unload service `%s'!\n" +msgstr "Dienst `%s' konnte nicht ordentlich entladen werden!\n" + +#: src/server/gnunet-update.c:146 +#, c-format +msgid "Updating data for module `%s'\n" +msgstr "Daten des Moduls `%s' werden aktualisiert\n" + +#: src/server/gnunet-update.c:151 +#, c-format +msgid "Failed to update data for module `%s'\n" +msgstr "Fehler beim Aktualisieren der Daten des Moduls `%s'\n" + +#: src/server/gnunet-update.c:225 src/server/gnunetd.c:124 +#, fuzzy +msgid "Core initialization failed.\n" +msgstr " Verbindung fehlgeschlagen\n" + +#: src/server/gnunet-update.c:270 +msgid "Updates GNUnet datastructures after version change." +msgstr "GNUnet Datenstrukturen nach einer Versionsänderung aktualisieren." + +#: src/server/gnunet-update.c:274 src/server/gnunet-transport-check.c:376 +msgid "run as user LOGIN" +msgstr "als Benutzer LOGIN ausführen" + +#: src/server/gnunet-update.c:278 +msgid "run in client mode (for getting client configuration values)" +msgstr "" +"Im Benuter-Modus laufen (um benutzerspezifische Konfigurationseinstellungen " +"zu holen" + +#: src/server/version.c:125 +msgid "" +"Failed to determine filename used to store GNUnet version information!\n" +msgstr "" + +#: src/server/gnunetd.c:85 +#, c-format +msgid "`%s' startup complete.\n" +msgstr "`%s' Startvorgang abgeschlossen.\n" + +#: src/server/gnunetd.c:89 +#, c-format +msgid "`%s' is shutting down.\n" +msgstr "`%s' fährt herunter.\n" + +#: src/server/gnunetd.c:179 +msgid "" +"run in debug mode; gnunetd will not daemonize and error messages will be " +"written to stderr instead of a logfile" +msgstr "" +"Im Debug-Modus ausführen. gnunetd wird nicht im Hintergrund laufen und " +"Fehlermeldungen werden nicht in die Protokolldatei, sondern auf die " +"Standardfehlerausgabe (stderr) geschrieben." + +#: src/server/gnunetd.c:183 +msgid "Starts the gnunetd daemon." +msgstr "Startet den gnunetd Daemonen." + +#: src/server/gnunetd.c:186 +msgid "disable padding with random data (experimental)" +msgstr "" + +#: src/server/gnunetd.c:190 +msgid "print all log messages to the console (only works together with -d)" +msgstr "" + +#: src/server/gnunetd.c:194 +#, fuzzy +msgid "specify username as which gnunetd should run" +msgstr "Gibt an, auf welchem Host gnunetd läuft" + +#: src/server/gnunetd.c:275 +#, c-format +msgid "Configuration or GNUnet version changed. You need to run `%s'!\n" +msgstr "" +"Konfiguration oder die GNUnet Version hat sich geändert. Sie müssen `%s' " +"ausführen!\n" + +#: src/server/tcpserver.c:121 +#, c-format +msgid "The `%s' request received from client is malformed.\n" +msgstr "Die `%s' Anfrage, die vom Client empfangen wurde, ist beschädigt.\n" + +#: src/server/tcpserver.c:409 +#, fuzzy, c-format +msgid "`%s' failed for port %d. Is gnunetd already running?\n" +msgstr "`%s' schlug fehl für Port %d: %s. Läuft gnunetd bereits?\n" + +#: src/server/tcpserver.c:487 src/server/tcpserver.c:512 +#, c-format +msgid "" +"Malformed network specification in the configuration in section `%s' for " +"entry `%s': %s\n" +msgstr "" +"Beschädigte Netzwerkangabe in der Konfigurationsdatei in Sektion `%s' für " +"Eintrag `%s': %s\n" + +#: src/server/tcpserver.c:572 +#, fuzzy, c-format +msgid "Registering failed, message type %d already in use.\n" +msgstr "%s schlug fehl, Nachrichten Typ %d ist bereits in Verwendung.\n" + +#: src/server/startup.c:219 +#, fuzzy, c-format +msgid "Unable to obtain filesystem information for `%s': %u\n" +msgstr "Fehler beim Speichern der Konfigurationsdatei: `%s': %s.\n" + +#: src/server/startup.c:237 +#, c-format +msgid "" +"Filesystem `%s' of partition `%s' is unknown. Please contact gnunet-" +"developers@gnu.org!" +msgstr "" + +#: src/server/startup.c:252 +#, c-format +msgid "" +"Limiting datastore size to %llu GB, because the `%s' filesystem does not " +"support larger files. Please consider storing the database on a NTFS " +"partition!\n" +msgstr "" + +#: src/server/startup.c:291 +#, c-format +msgid "Insufficient access permissions for `%s': %s\n" +msgstr "" + +#: src/server/gnunet-peer-info.c:55 +msgid "Print information about GNUnet peers." +msgstr "Informationen über andere GNUnet Knoten ausgeben." + +#: src/server/gnunet-peer-info.c:59 +msgid "don't resolve host names" +msgstr "" + +#: src/server/gnunet-peer-info.c:62 +msgid "output only the identity strings" +msgstr "" + +#: src/server/gnunet-peer-info.c:65 +msgid "output our own identity only" +msgstr "" + +#: src/server/gnunet-peer-info.c:130 src/server/gnunet-peer-info.c:164 +#, c-format +msgid "Could not get address of peer `%s'.\n" +msgstr "Adresse des Knotens `%s' konnte nicht ermittelt werden.\n" + +#: src/server/gnunet-peer-info.c:143 +#, fuzzy, c-format +msgid "`%s' message invalid (signature invalid).\n" +msgstr "Hello Nachricht ist ungültig (Signatur ist ungültig).\n" + +#: src/server/gnunet-peer-info.c:168 +#, fuzzy, c-format +msgid "Peer `%s' with trust %8u\n" +msgstr "Knoten `%s' mit Vertrauen %8u und Adresse `%s'\n" + +#: src/server/gnunet-peer-info.c:175 +#, c-format +msgid "Peer `%s' with trust %8u and address `%s'\n" +msgstr "Knoten `%s' mit Vertrauen %8u und Adresse `%s'\n" + +#: src/server/connection.c:1313 +#, c-format +msgid "`%s' selected %d out of %d messages (MTU: %d).\n" +msgstr "`%s' wählte %d von %d Nachrichten aus (MTU: %d).\n" + +#: src/server/connection.c:1323 +#, c-format +msgid "Message details: %u: length %d, priority: %d\n" +msgstr "Nachrichten Details: %u: Länge %d, Priorität: %d\n" + +#: src/server/connection.c:3129 +#, c-format +msgid "Message from `%s' discarded: invalid format.\n" +msgstr "Nachricht von `%s' verworfen: ungültiges Format.\n" + +#: src/server/connection.c:3218 +#, c-format +msgid "Invalid sequence number %u <= %u, dropping message.\n" +msgstr "Ungültige Sequenznummer %u <= %u, Nachricht wird verworfen.\n" + +#: src/server/connection.c:3240 +msgid "Message received more than one day old. Dropped.\n" +msgstr "" +"Empfangene Nachricht ist mehr als ein Tag alt. Nachricht wird verworfen.\n" + +#: src/server/connection.c:3763 +msgid "# outgoing messages dropped" +msgstr "# ausgehender Nachrichten verworfen" + +#: src/server/connection.c:3766 +msgid "# bytes of outgoing messages dropped" +msgstr "# Bytes ausgehender Nachrichten verworfen" + +#: src/server/connection.c:3768 +msgid "# connections closed (HANGUP sent)" +msgstr "# geschlossener Verbindungen (HANGUP gesendet)" + +#: src/server/connection.c:3772 +#, fuzzy +msgid "# connections closed (transport issue)" +msgstr "# geschlossener Verbindungen (HANGUP gesendet)" + +#: src/server/connection.c:3775 +msgid "# bytes encrypted" +msgstr "# Bytes verschlüsselt" + +#: src/server/connection.c:3779 +#, fuzzy +msgid "# bytes transmitted" +msgstr "# Bytes des Typs %d übertragen" + +#: src/server/connection.c:3783 +#, fuzzy +msgid "# bytes received" +msgstr "# Bytes empfangen über TCP" + +#: src/server/connection.c:3785 +msgid "# bytes decrypted" +msgstr "# Bytes entschlüsselt" + +#: src/server/connection.c:3786 +msgid "# bytes noise sent" +msgstr "# Bytes Rauschen gesendet" + +#: src/server/connection.c:3789 +msgid "# total bytes per second send limit" +msgstr "" + +#: src/server/connection.c:3792 +#, fuzzy +msgid "# total bytes per second receive limit" +msgstr "# Bytes Rauschen empfangen" + +#: src/server/connection.c:3795 +#, fuzzy +msgid "# total number of messages in send buffers" +msgstr "Anzahl an Nachrichten in einem Nachrichtenblock" + +#: src/server/connection.c:3798 +#, fuzzy +msgid "# total number of bytes we were allowed to send but did not" +msgstr "Anzahl an Nachrichten in einem Nachrichtenblock" + +#: src/server/connection.c:3801 +#, fuzzy +msgid "# total number of bytes we were allowed to sent" +msgstr "Anzahl an Nachrichten in einem Nachrichtenblock" + +#: src/server/connection.c:3804 +msgid "# total number of bytes we are currently allowed to send" +msgstr "" + +#: src/server/connection.c:3807 +msgid "# transports switched to stream transport" +msgstr "" + +#: src/server/connection.c:3810 +msgid "# average connection lifetime (in ms)" +msgstr "" + +#: src/server/connection.c:3813 +msgid "# conn. shutdown: other peer sent too much" +msgstr "" + +#: src/server/connection.c:3816 +msgid "# conn. shutdown: we lacked bandwidth" +msgstr "" + +#: src/server/connection.c:3819 +msgid "# conn. shutdown: other peer timed out" +msgstr "" + +#: src/server/connection.c:3822 +msgid "# conn. shutdown: timed out during connect" +msgstr "" + +#: src/server/connection.c:3825 +msgid "# conn. shutdown: other peer requested it" +msgstr "" + +#: src/server/handler.c:442 +#, fuzzy, c-format +msgid "Received corrupt message from peer `%s' in %s:%d.\n" +msgstr "Beschädigte Nachricht von Knoten `%s' in %s:%d empfangen.\n" + +#: src/server/gnunet-transport-check.c:121 +#, c-format +msgid "`%s': Could not create hello.\n" +msgstr "`%s': Hello konnte nicht erzeugt werden.\n" + +#: src/server/gnunet-transport-check.c:129 +#, c-format +msgid "`%s': Could not connect.\n" +msgstr "`%s': Verbindung konnte nicht hergestellt werden.\n" + +#: src/server/gnunet-transport-check.c:163 +#, c-format +msgid "`%s': Could not send.\n" +msgstr "`%s': Kann nicht senden.\n" + +#: src/server/gnunet-transport-check.c:179 +#, c-format +msgid "`%s': Did not receive message within %llu ms.\n" +msgstr "`%s': Nachricht wurde nicht innerhalb %llu ms empfangen.\n" + +#: src/server/gnunet-transport-check.c:192 +#, c-format +msgid "`%s': Could not disconnect.\n" +msgstr "`%s': Verbindung konnte nicht getrennt werden.\n" + +#: src/server/gnunet-transport-check.c:200 +#, fuzzy, c-format +msgid "" +"`%s' transport OK. It took %ums to transmit %llu messages of %llu bytes " +"each.\n" +msgstr "" +"`%s' Transport funktioniert. Es dauerte %ums, um %d Nachrichten zu je %d " +"Bytes zu übertragen.\n" + +#: src/server/gnunet-transport-check.c:231 +#, fuzzy, c-format +msgid " Transport %d is not being tested\n" +msgstr " Transport %d ist nicht verfügbar\n" + +#: src/server/gnunet-transport-check.c:261 +#, c-format +msgid "" +"\n" +"Contacting `%s'." +msgstr "" +"\n" +"Kontaktiere `%s'." + +#: src/server/gnunet-transport-check.c:286 +#, c-format +msgid " Connection failed\n" +msgstr " Verbindung fehlgeschlagen\n" + +#: src/server/gnunet-transport-check.c:292 +#, c-format +msgid " Connection failed (bug?)\n" +msgstr " Verbindung fehlgeschlagen (Bug?)\n" + +#: src/server/gnunet-transport-check.c:330 +#, c-format +msgid "Timeout after %llums.\n" +msgstr "" + +#: src/server/gnunet-transport-check.c:332 +#, fuzzy, c-format +msgid "OK!\n" +msgstr "OK" + +#: src/server/gnunet-transport-check.c:357 +msgid "Tool to test if GNUnet transport services are operational." +msgstr "" +"Werkzeug, mit dem getestet werden kann, ob die GNUnet Transport Dienste " +"funktionsfähig sind." + +#: src/server/gnunet-transport-check.c:361 +msgid "ping peers from HOSTLISTURL that match transports" +msgstr "Knoten aus HOSTLISTURL anpingen, deren Transports passen" + +#: src/server/gnunet-transport-check.c:364 +msgid "send COUNT messages" +msgstr "COUNT Nachrichten versenden" + +#: src/server/gnunet-transport-check.c:367 +msgid "send messages with SIZE bytes payload" +msgstr "Nachrichten mit SIZE Bytes Nutzlast versenden" + +#: src/server/gnunet-transport-check.c:370 +msgid "specifies which TRANSPORT should be tested" +msgstr "Gibt an, welcher TRANSPORT getestet werden soll" + +#: src/server/gnunet-transport-check.c:373 +msgid "specifies after how many MS to time-out" +msgstr "Gibt an, nach wievielen MS die Zeit abgelaufen sein soll" + +#: src/server/gnunet-transport-check.c:381 +msgid "repeat each test X times" +msgstr "" + +#: src/server/gnunet-transport-check.c:449 +#, c-format +msgid "Testing transport(s) %s\n" +msgstr "Teste Transport(e) %s\n" + +#: src/server/gnunet-transport-check.c:451 +#, c-format +msgid "Available transport(s): %s\n" +msgstr "Verfügbare(r) Transport(e): %s\n" + +#: src/server/gnunet-transport-check.c:501 +#, fuzzy, c-format +msgid "" +"\n" +"%d out of %d peers contacted successfully (%d times transport unavailable).\n" +msgstr "" +"%d von %d Knoten erfolgreich kontaktiert (%d mal war der Transport nicht " +"verfügbar).\n" + +#: src/transports/common.c:370 +#, c-format +msgid "Port is 0, will only send using %s.\n" +msgstr "" + +#: src/transports/smtp.c:367 src/transports/udp.c:107 src/transports/tcp.c:271 +#: src/transports/tcp.c:291 +#, fuzzy, c-format +msgid "Received malformed message via %s. Ignored.\n" +msgstr "" +"Es wurde eine ungültige Nachricht per SMTP empfangen (ungültige Größe).\n" + +#: src/transports/smtp.c:459 +#, fuzzy +msgid "SMTP filter string to invalid, lacks ': '\n" +msgstr "SMTP Filterstring zu lang, wurde auf `%s' abgeschnitten\n" + +#: src/transports/smtp.c:469 +#, c-format +msgid "SMTP filter string to long, capped to `%s'\n" +msgstr "SMTP Filterstring zu lang, wurde auf `%s' abgeschnitten\n" + +#: src/transports/smtp.c:564 src/transports/smtp.c:575 +#: src/transports/smtp.c:589 src/transports/smtp.c:609 +#: src/transports/smtp.c:634 src/transports/smtp.c:643 +#: src/transports/smtp.c:657 src/transports/smtp.c:669 +#, fuzzy, c-format +msgid "SMTP: `%s' failed: %s.\n" +msgstr "`%s' schlug fehl: %s\n" + +#: src/transports/smtp.c:814 +#, fuzzy +msgid "No email-address specified, can not start SMTP transport.\n" +msgstr "" +"Kein Filter für E-Mail angegeben, es kann keine Bekanntmachung erstellt " +"werden.\n" + +#: src/transports/smtp.c:831 +#, fuzzy +msgid "# bytes received via SMTP" +msgstr "# Bytes empfangen über TCP" + +#: src/transports/smtp.c:832 +#, fuzzy +msgid "# bytes sent via SMTP" +msgstr "# Bytes gesendet über TCP" + +#: src/transports/smtp.c:834 +#, fuzzy +msgid "# bytes dropped by SMTP (outgoing)" +msgstr "# Bytes verworfen von TCP (ausgehend)" + +#: src/transports/upnp/upnp.c:431 +#, fuzzy, c-format +msgid "%s failed for url `%s' and post-data `%s' at %s:%d: `%s'\n" +msgstr "`%s' schlug fehl bei %s:%d mit dem Fehler: `%s'.\n" + +#: src/transports/upnp/upnp.c:476 +#, c-format +msgid "upnp: NAT Returned IP: %s\n" +msgstr "" + +#: src/transports/http.c:2019 src/transports/tcp.c:811 +#, c-format +msgid "" +"The UPnP service could not be loaded. To disable UPnP, set the configuration " +"option \"UPNP\" in section \"%s\" to \"NO\"\n" +msgstr "" + +#: src/transports/http.c:2028 +msgid "# bytes received via HTTP" +msgstr "# Bytes empfangen über HTTP" + +#: src/transports/http.c:2029 +msgid "# bytes sent via HTTP" +msgstr "# Bytes gesendet über HTTP" + +#: src/transports/http.c:2031 +msgid "# bytes dropped by HTTP (outgoing)" +msgstr "# Bytes verworfen von HTTP" + +#: src/transports/http.c:2032 +msgid "# HTTP GET issued" +msgstr "" + +#: src/transports/http.c:2034 +#, fuzzy +msgid "# HTTP GET received" +msgstr "# Bytes empfangen über TCP" + +#: src/transports/http.c:2035 +msgid "# HTTP PUT issued" +msgstr "" + +#: src/transports/http.c:2037 +#, fuzzy +msgid "# HTTP PUT received" +msgstr "# Bytes empfangen über TCP" + +#: src/transports/http.c:2039 +msgid "# HTTP select calls" +msgstr "" + +#: src/transports/http.c:2041 +msgid "# HTTP send calls" +msgstr "" + +#: src/transports/http.c:2044 +msgid "# HTTP curl send callbacks" +msgstr "" + +#: src/transports/http.c:2046 +msgid "# HTTP curl receive callbacks" +msgstr "" + +#: src/transports/http.c:2048 +msgid "# HTTP mhd access callbacks" +msgstr "" + +#: src/transports/http.c:2050 +msgid "# HTTP mhd read callbacks" +msgstr "" + +#: src/transports/http.c:2052 +msgid "# HTTP mhd close callbacks" +msgstr "" + +#: src/transports/http.c:2054 +#, fuzzy +msgid "# HTTP connect calls" +msgstr "# verbundener Knoten" + +#: src/transports/ip.c:70 src/transports/ip.c:365 +#, fuzzy, c-format +msgid "Failed to obtain my (external) %s address!\n" +msgstr "Fehler beim Bestimmen der (externen) IP-Adresse!\n" + +#: src/transports/udp.c:472 src/transports/tcp.c:728 +#, fuzzy, c-format +msgid "Failed to bind to %s port %d.\n" +msgstr "Fehler beim Binden an UDP Port %d.\n" + +#: src/transports/udp.c:538 +#, fuzzy, c-format +msgid "MTU %llu for `%s' is probably too low!\n" +msgstr "" +"MTU für `%s' ist möglicherweise zu gering (Fragmentierung ist nicht " +"implementiert!)\n" + +#: src/transports/udp.c:562 +msgid "# bytes received via UDP" +msgstr "# Bytes empfangen über UDP" + +#: src/transports/udp.c:563 +msgid "# bytes sent via UDP" +msgstr "# Bytes gesendet über UDP" + +#: src/transports/udp.c:565 +msgid "# bytes dropped by UDP (outgoing)" +msgstr "# Bytes verworfen von UDP (outgoing)" + +#: src/transports/udp.c:567 +msgid "# UDP connections (right now)" +msgstr "" + +#: src/transports/tcp.c:821 +msgid "# bytes received via TCP" +msgstr "# Bytes empfangen über TCP" + +#: src/transports/tcp.c:822 +msgid "# bytes sent via TCP" +msgstr "# Bytes gesendet über TCP" + +#: src/transports/tcp.c:824 +msgid "# bytes dropped by TCP (outgoing)" +msgstr "# Bytes verworfen von TCP (ausgehend)" + +#: src/include/gnunet_util_getopt.h:154 +msgid "print this help" +msgstr "Gibt diese Hilfe aus" + +#: src/include/gnunet_util_getopt.h:163 +msgid "print the version number" +msgstr "Versionsnummer ausgeben" + +#: src/include/gnunet_util_getopt.h:169 +msgid "configure logging to use LOGLEVEL" +msgstr "" + +#: src/include/gnunet_util_getopt.h:175 +msgid "be verbose" +msgstr "umfangreiche Meldungen ausgeben" + +#: src/include/gnunet_util_getopt.h:181 +msgid "use configuration file FILENAME" +msgstr "Konfigurationsdatei FILENAME verwenden" + +#: src/include/gnunet_util_getopt.h:187 +msgid "specify host on which gnunetd is running" +msgstr "Gibt an, auf welchem Host gnunetd läuft" + +#: src/include/gnunet_util_error.h:219 src/include/gnunet_util_error.h:224 +#: src/include/gnunet_util_error.h:230 src/include/gnunet_util_error.h:232 +#, fuzzy, c-format +msgid "Internal error: assertion failed at %s:%d.\n" +msgstr "Absicherung fehlgeschlagen bei %s:%d.\n" + +#: src/include/gnunet_util_error.h:242 +#, c-format +msgid "" +"External protocol violation: assertion failed at %s:%d (no need to panic, we " +"can handle this).\n" +msgstr "" + +#: src/include/gnunet_util_error.h:270 src/include/gnunet_util_error.h:277 +#, fuzzy, c-format +msgid "`%s' failed on file `%s' at %s:%d with error: %s\n" +msgstr "`%s' schlug bei Datei `%s' fehl. Ort: %s:%d. Fehler: %s\n" + +#: contrib/config-daemon.scm:39 contrib/config-client.scm:40 +msgid "No help available." +msgstr "Keine Hilfe verfügbar." + +#: contrib/config-daemon.scm:42 +msgid "" +"You can use 'make check' in src/transports/upnp/ to find out if your NAT " +"supports UPnP. You should disable this option if you are sure that you are " +"not behind a NAT. If your NAT box does not support UPnP, having this on " +"will not do much harm (only cost a small amount of resources)." +msgstr "" + +#: contrib/config-daemon.scm:54 contrib/config-client.scm:53 +msgid "Prompt for development and/or incomplete code" +msgstr "" + +#: contrib/config-daemon.scm:56 contrib/config-client.scm:55 +msgid "" +"If EXPERIMENTAL is set to NO, options for experimental code are not shown. " +"If in doubt, use NO.\n" +"\n" +"Some options apply to experimental code that maybe in a state of development " +"where the functionality, stability, or the level of testing is not yet high " +"enough for general use. These features are said to be of \"alpha\" " +"quality. If a feature is currently in alpha, uninformed use is discouraged " +"(since the developers then do not fancy \"Why doesn't this work?\" type " +"messages).\n" +"\n" +"However, active testing and qualified feedback of these features is always " +"welcome. Users should just be aware that alpha features may not meet the " +"normal level of reliability or it may fail to work in some special cases. " +"Bug reports are usually welcomed by the developers, but please read the " +"documents and and use for how to report problems." +msgstr "" + +#: contrib/config-daemon.scm:71 contrib/config-client.scm:70 +msgid "Show options for advanced users" +msgstr "" + +#: contrib/config-daemon.scm:73 contrib/config-client.scm:72 +msgid "" +"These are options that maybe difficult to understand for the beginner. These " +"options typically refer to features that allow tweaking of the " +"installation. If in a hurry, say NO." +msgstr "" + +#: contrib/config-daemon.scm:84 contrib/config-client.scm:83 +#, fuzzy +msgid "Show rarely used options" +msgstr "Maskierte Optionen anzeigen" + +#: contrib/config-daemon.scm:86 contrib/config-client.scm:85 +msgid "" +"These are options that hardly anyone actually needs. If you plan on doing " +"development on GNUnet, you may want to look into these. If in doubt or in a " +"hurry, say NO." +msgstr "" + +#: contrib/config-daemon.scm:97 contrib/config-client.scm:96 +#, fuzzy +msgid "Meta-configuration" +msgstr "GNUnet Konfiguration" + +#: contrib/config-daemon.scm:98 contrib/config-client.scm:97 +msgid "Which level of configuration should be available" +msgstr "" + +#: contrib/config-daemon.scm:115 +#, fuzzy +msgid "Full pathname of GNUnet HOME directory" +msgstr "Dateiformat fehlerhaft (kein GNUnet Verzeichnis?)\n" + +#: contrib/config-daemon.scm:117 +msgid "" +"This gives the root-directory of the GNUnet installation. Make sure there is " +"some space left in that directory. :-) Users inserting or indexing files " +"will be able to store data in this directory up to the (global) quota " +"specified below. Having a few gigabytes of free space is recommended." +msgstr "" + +#: contrib/config-daemon.scm:130 +#, fuzzy +msgid "Full pathname of GNUnet directory for file-sharing data" +msgstr "Dateiformat fehlerhaft (kein GNUnet Verzeichnis?)\n" + +#: contrib/config-daemon.scm:142 +#, fuzzy +msgid "Full pathname to the directory with the key-value database" +msgstr "Dateiformat fehlerhaft (kein GNUnet Verzeichnis?)\n" + +#: contrib/config-daemon.scm:143 +#, fuzzy +msgid "Note that the kvstore is currently not used." +msgstr "# gap Routingschächte im Moment in Verwendung" + +#: contrib/config-daemon.scm:154 +#, fuzzy +msgid "Full pathname of GNUnet directory for indexed files symbolic links" +msgstr "Dateiformat fehlerhaft (kein GNUnet Verzeichnis?)\n" + +#: contrib/config-daemon.scm:166 +msgid "How many minutes should peer advertisements last?" +msgstr "" + +#: contrib/config-daemon.scm:168 +msgid "" +"How many minutes is the current IP valid? (GNUnet will sign HELLO messages " +"with this expiration timeline. If you are on dialup, 60 (for 1 hour) is " +"suggested. If you have a static IP address, you may want to set this to a " +"large value (say 14400). The default is 1440 (1 day). If your IP changes " +"periodically, you will want to choose an expiry period smaller than the " +"frequency with which your IP changes." +msgstr "" + +#: contrib/config-daemon.scm:179 +msgid "Where can GNUnet find an initial list of peers?" +msgstr "" + +#: contrib/config-daemon.scm:181 +msgid "" +"GNUnet can automatically update the hostlist from the web. While GNUnet " +"internally communicates which hosts are online, it is typically a good idea " +"to get a fresh hostlist whenever gnunetd starts from the WEB. By setting " +"this option, you can specify from which server gnunetd should try to " +"download the hostlist. The default should be fine for now.\n" +"\t\t\n" +"The general format is a list of space-separated URLs. Each URL must have " +"the format http://HOSTNAME/FILENAME\n" +"\t\t\n" +"If you want to setup an alternate hostlist server, you must run a permanent " +"node and \"cat data/hosts/* > hostlist\" every few minutes to keep the list " +"up-to-date.\n" +"\t\t\n" +"If you do not specify a HOSTLISTURL, you must copy valid hostkeys to data/" +"hosts manually." +msgstr "" + +#: contrib/config-daemon.scm:198 +msgid "HTTP Proxy Server" +msgstr "" + +#: contrib/config-daemon.scm:200 +msgid "" +"If you have to use a proxy for outbound HTTP connections, specify the proxy " +"configuration here. Default is no proxy." +msgstr "" + +#: contrib/config-daemon.scm:212 +msgid "" +"Name of the directory where gnunetd should store contact information about " +"peers" +msgstr "" + +#: contrib/config-daemon.scm:214 +msgid "" +"Unless you want to share the directory directly using a webserver, the " +"default is most likely just fine." +msgstr "" + +#: contrib/config-daemon.scm:240 contrib/config-client.scm:140 +msgid "How long should logs be kept?" +msgstr "" + +#: contrib/config-daemon.scm:242 contrib/config-client.scm:142 +msgid "" +"How long should logs be kept? If you specify a value greater than zero, a " +"log is created each day with the date appended to its filename. These logs " +"are deleted after $KEEPLOG days.\tTo keep logs forever, set this value to 0." +msgstr "" + +#: contrib/config-daemon.scm:253 +msgid "" +"What maximum number of open file descriptors should be requested from the OS?" +msgstr "" + +#: contrib/config-daemon.scm:255 +msgid "" +"The default of 1024 should be fine for most systems. If your system can " +"support more, increasing the number might help support additional clients on " +"machines with plenty of bandwidth. For embedded systems, a smaller number " +"might be acceptable. A value of 0 will leave the descriptor limit " +"untouched. This option is mostly for OS X systems where the default is too " +"low. Note that if gnunetd cannot obtain the desired number of file " +"descriptors from the operating system, it will print a warning and try to " +"run with what it is given." +msgstr "" + +#: contrib/config-daemon.scm:266 +msgid "Where should gnunetd write the logs?" +msgstr "" + +#: contrib/config-daemon.scm:278 +msgid "Enable for extra-verbose logging." +msgstr "" + +#: contrib/config-daemon.scm:290 contrib/config-client.scm:165 +msgid "Logging" +msgstr "" + +#: contrib/config-daemon.scm:291 contrib/config-client.scm:166 +msgid "Specify which system messages should be logged how" +msgstr "" + +#: contrib/config-daemon.scm:296 contrib/config-client.scm:170 +msgid "Logging of events for users" +msgstr "" + +#: contrib/config-daemon.scm:297 contrib/config-client.scm:171 +msgid "Logging of events for the system administrator" +msgstr "" + +#: contrib/config-daemon.scm:309 +msgid "Where should gnunetd write the PID?" +msgstr "" + +#: contrib/config-daemon.scm:310 +msgid "" +"The default is no longer /var/run/gnunetd.pid since we could not delete the " +"file on shutdown at that location." +msgstr "" + +#: contrib/config-daemon.scm:322 +msgid "As which user should gnunetd run?" +msgstr "" + +#: contrib/config-daemon.scm:324 +msgid "" +"Empty means \"current user\". On computer startup, it is root/SYSTEM. Under " +"Windows, this setting affects the creation of a new system service only." +msgstr "" + +#: contrib/config-daemon.scm:337 +msgid "Should gnunetd be automatically started when the system boots?" +msgstr "" + +#: contrib/config-daemon.scm:338 +msgid "" +"Set to YES if gnunetd should be automatically started on boot. If this " +"option is set, gnunet-setup will install a script to start the daemon upon " +"completion. This option may not work on all systems." +msgstr "" + +#: contrib/config-daemon.scm:350 +msgid "Which transport mechanisms should GNUnet use?" +msgstr "" + +#: contrib/config-daemon.scm:352 +msgid "" +"Use a space-separated list of modules, e.g. \"udp smtp tcp\". The " +"available transports are udp, tcp, http, smtp and nat.\n" +"\t\t\n" +"Loading the 'nat' and 'tcp' modules is required for peers behind NAT boxes " +"that cannot directly be reached from the outside. Peers that are NOT behind " +"a NAT box and that want to *allow* peers that ARE behind a NAT box to " +"connect must ALSO load the 'nat' module. Note that the actual transfer will " +"always be via tcp initiated by the peer behind the NAT box. The nat " +"transport requires the use of tcp, http and/or smtp in addition to nat " +"itself." +msgstr "" + +#: contrib/config-daemon.scm:366 +msgid "Which applications should gnunetd support?" +msgstr "" + +#: contrib/config-daemon.scm:368 +msgid "" +"Whenever this option is changed, you MUST run gnunet-update. Currently, the " +"available applications are:\n" +"\n" +"advertising: advertises your peer to other peers. Without it, your peer will " +"not participate in informing peers about other peers. You should always " +"load this module.\n" +"\n" +"getoption: allows clients to query gnunetd about the values of various " +"configuration options. Many tools need this. You should always load this " +"module.\n" +"\n" +"stats: allows tools like gnunet-stats and gnunet-gtk to query gnunetd about " +"various statistics. This information is usually quite useful to diagnose " +"errors, hence it is recommended that you load this module.\n" +"\n" +"traffic: keeps track of how many messages were recently received and " +"transmitted. This information can then be used to establish how much cover " +"traffic is currently available. The amount of cover traffic becomes " +"important if you want to make anonymous requests with an anonymity level " +"that is greater than one. It is recommended that you load this module.\n" +"\n" +"fs: needed for anonymous file sharing. You should always load this module.\n" +"\n" +"hostlist: integrated hostlist HTTP server. Useful if you want to offer a " +"hostlist and running Apache would be overkill.\n" +"\n" +"chat: broadcast chat (demo-application, ALPHA quality).\tRequired for gnunet-" +"chat. Note that the current implementation of chat is not considered to be " +"secure.\n" +"\n" +"tbench: benchmark transport performance. Required for gnunet-tbench. Note " +"that tbench allows other users to abuse your resources.\n" +"\n" +"tracekit: topology visualization toolkit. Required for gnunet-tracekit. " +"Note that loading tracekit will make it slightly easier for an adversary to " +"compromise your anonymity." +msgstr "" + +#: contrib/config-daemon.scm:399 +msgid "Disable client-server connections" +msgstr "" + +#: contrib/config-daemon.scm:400 +msgid "" +"This option can be used to tell gnunetd not to open the client port. When " +"run like this, gnunetd will participate as a peer in the network but not " +"support any user interfaces. This may be useful for headless systems that " +"are never expected to have end-user interactions. Note that this will also " +"prevent you from running diagnostic tools like gnunet-stats!" +msgstr "" + +#: contrib/config-daemon.scm:412 +msgid "YES disables IPv6 support, NO enables IPv6 support" +msgstr "" + +#: contrib/config-daemon.scm:413 +msgid "" +"This option may be useful on peers where the kernel does not support IPv6. " +"You might also want to set this option if you do not have an IPv6 network " +"connection." +msgstr "" + +#: contrib/config-daemon.scm:425 +msgid "Disable peer discovery" +msgstr "" + +#: contrib/config-daemon.scm:426 +msgid "" +"The option 'PRIVATE-NETWORK' can be used to limit the connections of this " +"peer to peers of which the hostkey has been copied by hand to data/hosts; " +"if this option is given, GNUnet will not accept advertisements of peers that " +"the local node does not already know about. Note that in order for this " +"option to work, HOSTLISTURL should either not be set at all or be set to a " +"trusted peer that only advertises the private network. Also, the option does " +"NOT work at the moment if the NAT transport is loaded; for that, a couple of " +"lines above would need some minor editing :-)." +msgstr "" + +#: contrib/config-daemon.scm:437 +msgid "Disable advertising this peer to other peers" +msgstr "" + +#: contrib/config-daemon.scm:449 +msgid "Disable automatic establishment of connections" +msgstr "" + +#: contrib/config-daemon.scm:450 +msgid "" +"If this option is enabled, GNUnet will not automatically establish " +"connections to other peers, but instead wait for applications to " +"specifically request connections to other peers (or for other peers to " +"connect to us)." +msgstr "" + +#: contrib/config-daemon.scm:461 +msgid "Enable advertising of other peers by this peer" +msgstr "" + +#: contrib/config-daemon.scm:462 +msgid "" +"This option may be useful during testing, but turning it off is dangerous! " +"If in any doubt, set it to YES (which is the default)." +msgstr "" + +#: contrib/config-daemon.scm:473 +#, fuzzy +msgid "Port for communication with GNUnet user interfaces" +msgstr "Informationen über andere GNUnet Knoten ausgeben." + +#: contrib/config-daemon.scm:474 +msgid "" +"Which is the client-server port that is used between gnunetd and the clients " +"(TCP only). You may firewall this port for non-local machines (but you do " +"not have to since GNUnet will perform access control and only allow " +"connections from machines that are listed under TRUSTED)." +msgstr "" + +#: contrib/config-daemon.scm:485 +msgid "Port for the integrated hostlist HTTP server" +msgstr "" + +#: contrib/config-daemon.scm:497 +msgid "IPv4 networks allowed to use gnunetd server" +msgstr "" + +#: contrib/config-daemon.scm:498 contrib/config-daemon.scm:510 +msgid "" +"This option specifies which hosts are trusted enough to connect as clients " +"(to the TCP port). This is useful if you run gnunetd on one host of your " +"network and want to allow all other hosts to use this node as their server. " +"By default, this is set to 'loopback only'. The format is IP/NETMASK where " +"the IP is specified in dotted-decimal and the netmask either in CIDR " +"notation (/16) or in dotted decimal (255.255.0.0). Several entries must be " +"separated by a semicolon, spaces are not allowed." +msgstr "" + +#: contrib/config-daemon.scm:509 +msgid "IPv6 networks allowed to use gnunetd server" +msgstr "" + +#: contrib/config-daemon.scm:522 +msgid "Limit connections to the specfied set of peers." +msgstr "" + +#: contrib/config-daemon.scm:523 +msgid "" +"If this option is not set, any peer is allowed to connect. If it is set, " +"only the specified peers are allowed. Specify the list of peer IDs (not IPs!)" +msgstr "" + +#: contrib/config-daemon.scm:534 +#, fuzzy +msgid "Run gnunetd as this group." +msgstr "gnunet-update ausführen" + +#: contrib/config-daemon.scm:535 +msgid "" +"When started as root, gnunetd will change permissions to the given group." +msgstr "" + +#: contrib/config-daemon.scm:546 +msgid "Prevent the specfied set of peers from connecting." +msgstr "" + +#: contrib/config-daemon.scm:547 +msgid "" +"If this option is not set, any peer is allowed to connect. If the ID of a " +"peer is listed here, connections from that peer will be refused. Specify " +"the list of peer IDs (not IPs!)" +msgstr "" + +#: contrib/config-daemon.scm:558 +msgid "Topology Maintenance" +msgstr "" + +#: contrib/config-daemon.scm:559 +msgid "Rarely used settings for peer advertisements and connections" +msgstr "" + +#: contrib/config-daemon.scm:579 +#, fuzzy +msgid "General settings" +msgstr "Weitere Einstellungen" + +#: contrib/config-daemon.scm:580 +msgid "Settings that change the behavior of GNUnet in general" +msgstr "" + +#: contrib/config-daemon.scm:607 +msgid "Modules" +msgstr "" + +#: contrib/config-daemon.scm:608 +msgid "Settings that select specific implementations for GNUnet modules" +msgstr "" + +#: contrib/config-daemon.scm:626 +msgid "Fundamentals" +msgstr "" + +#: contrib/config-daemon.scm:646 +msgid "Which database should be used?" +msgstr "" + +#: contrib/config-daemon.scm:648 +msgid "" +"Which database should be used? The options are \"sqstore_sqlite\", " +"\"sqstore_postgres\" and \"sqstore_mysql\". You must run gnunet-update " +"after changing this value!\n" +"\t\t\t\n" +"In order to use MySQL or Postgres, you must configure the respective " +"database, which is relatively simple. Read the file doc/README.mysql or doc/" +"README.postgres for how to setup the respective database." +msgstr "" + +#: contrib/config-daemon.scm:661 contrib/config-daemon.scm:674 +msgid "Which topology should be used?" +msgstr "" + +#: contrib/config-daemon.scm:662 +msgid "Which database should be used for the temporary datastore of the DHT?" +msgstr "" + +#: contrib/config-daemon.scm:676 +msgid "" +"Which topology should be used? The only option at the moment is " +"\"topology_default\"" +msgstr "" + +#: contrib/config-daemon.scm:690 +msgid "" +"The minimum number of connected friends before this peer is allowed to " +"connect to peers that are not listed as friends" +msgstr "" + +#: contrib/config-daemon.scm:691 +msgid "" +"Note that this option does not guarantee that the peer will be able to " +"connect to the specified number of friends. Also, if the peer had connected " +"to a sufficient number of friends and then established non-friend " +"connections, some of the friends may drop out of the network, temporarily " +"resulting in having fewer than the specified number of friends connected " +"while being connected to non-friends. However, it is guaranteed that the " +"peer itself will never choose to drop a friend's connection if this would " +"result in dropping below the specified number of friends (unless that number " +"is higher than the overall connection target)." +msgstr "" + +#: contrib/config-daemon.scm:702 +msgid "" +"If set to YES, the peer is only allowed to connect to other peers that are " +"explicitly specified as friends" +msgstr "" + +#: contrib/config-daemon.scm:703 +msgid "" +"Use YES only if you have (trustworthy) friends that use GNUnet and are " +"afraid of establishing (direct) connections to unknown peers" +msgstr "" + +#: contrib/config-daemon.scm:714 +msgid "List of friends for friend-to-friend topology" +msgstr "" + +#: contrib/config-daemon.scm:715 +msgid "" +"Specifies the name of a file which contains a list of GNUnet peer IDs that " +"are friends. If used with the friend-to-friend topology, this will ensure " +"that GNUnet only connects to these peers (via any available transport)." +msgstr "" + +#: contrib/config-daemon.scm:726 +msgid "Friend-to-Friend Topology Specification" +msgstr "" + +#: contrib/config-daemon.scm:727 +#, fuzzy +msgid "Settings for restricting connections to friends" +msgstr "`%s' fehlgeschlagen. Beende Verbindung zu Client.\n" + +#: contrib/config-daemon.scm:744 +msgid "Name of the MySQL database GNUnet should use" +msgstr "" + +#: contrib/config-daemon.scm:756 +#, fuzzy +msgid "Configuration file that specifies the MySQL username and password" +msgstr "" +"Konfiguration oder die GNUnet Version hat sich geändert. Sie müssen `%s' " +"ausführen!\n" + +#: contrib/config-daemon.scm:768 +#, fuzzy +msgid "Configuration of the MySQL database" +msgstr "" +"Konfiguration oder die GNUnet Version hat sich geändert. Sie müssen `%s' " +"ausführen!\n" + +#: contrib/config-daemon.scm:787 +#, fuzzy +msgid "MB of diskspace GNUnet can use for anonymous file sharing" +msgstr "Ermöglicht (anonymes) Filesharing" + +#: contrib/config-daemon.scm:789 +msgid "" +"How much disk space (MB) is GNUnet allowed to use for anonymous file " +"sharing? This does not take indexed files into account, only the space " +"directly used by GNUnet is accounted for. GNUnet will gather content from " +"the network if the current space-consumption is below the number given here " +"(and if content migration is allowed below).\n" +"\n" +"Note that if you change the quota, you need to run gnunet-update afterwards." +msgstr "" + +#: contrib/config-daemon.scm:803 +#, fuzzy +msgid "Number of entries in the migration buffer" +msgstr "Anzahl an Nachrichten in einem Nachrichtenblock" + +#: contrib/config-daemon.scm:804 +msgid "" +"Each entry uses about 32k of memory. More entries can reduce disk IO and " +"CPU usage at the expense of having gnunetd use more memory. Very large " +"values may again increase CPU usage. A value of 0 will prevent your peer " +"from sending unsolicited responses." +msgstr "" + +#: contrib/config-daemon.scm:816 +msgid "Size of the routing table for anonymous routing." +msgstr "" + +#: contrib/config-daemon.scm:828 +msgid "Size of the routing table for DHT routing." +msgstr "" + +#: contrib/config-daemon.scm:841 +msgid "Allow migrating content to this peer." +msgstr "" + +#: contrib/config-daemon.scm:843 +msgid "" +"If you say yes here, GNUnet will migrate content to your server, and you " +"will not be able to control what data is stored on your machine. \n" +"\t\t\t\n" +"If you activate it, you can claim for *all* the non-indexed (-n to gnunet-" +"insert) content that you did not know what it was even if an adversary takes " +"control of your machine. If you do not activate it, it is obvious that you " +"have knowledge of all the content that is hosted on your machine and thus " +"can be considered liable for it." +msgstr "" + +#: contrib/config-daemon.scm:857 +msgid "" +"MB of diskspace GNUnet can use for caching DHT index data (the data will be " +"stored in /tmp)" +msgstr "" + +#: contrib/config-daemon.scm:858 +msgid "" +"DHT index data is inherently small and expires comparatively quickly. It is " +"deleted whenever gnunetd is shut down.\n" +"\n" +"The size of the DSTORE QUOTA is specified in MB." +msgstr "" + +#: contrib/config-daemon.scm:872 +#, fuzzy +msgid "Options for anonymous file sharing" +msgstr "Ermöglicht (anonymes) Filesharing" + +#: contrib/config-daemon.scm:891 +#, fuzzy +msgid "Applications" +msgstr "_Optionen" + +#: contrib/config-daemon.scm:907 +msgid "Is this machine unreachable behind a NAT?" +msgstr "" + +#: contrib/config-daemon.scm:908 +msgid "" +"Set to YES if this machine is behind a NAT that limits connections from the " +"outside to the GNUnet port and that cannot be traversed using UPnP. Note " +"that if you have configured your NAT box to allow direct connections from " +"other machines to the GNUnet ports or if GNUnet can open ports using UPnP, " +"you should set the option to NO. Set this only to YES if other peers cannot " +"contact you directly. You can use 'make check' in src/transports/upnp/ to " +"find out if your NAT supports UPnP. You can also use gnunet-transport-check " +"with the '-p' option in order to determine which setting results in more " +"connections. Use YES only if you get no connections otherwise. Set to AUTO " +"to use YES if the local IP is belongs to a private IP network and NO " +"otherwise." +msgstr "" + +#: contrib/config-daemon.scm:919 +msgid "Which port should be used by the TCP IPv4 transport?" +msgstr "" + +#: contrib/config-daemon.scm:931 contrib/config-daemon.scm:1024 +#: contrib/config-daemon.scm:1174 +msgid "Should we try to determine our external IP using UPnP?" +msgstr "" + +#: contrib/config-daemon.scm:943 +msgid "Which IP(v4)s are not allowed to connect?" +msgstr "" + +#: contrib/config-daemon.scm:955 +msgid "" +"Which IP(v4)s are allowed to connect? Leave empty to use the IP of your " +"primary network interface." +msgstr "" + +#: contrib/config-daemon.scm:967 contrib/config-daemon.scm:1222 +msgid "Which IPv6s are not allowed to connect?" +msgstr "" + +#: contrib/config-daemon.scm:979 contrib/config-daemon.scm:1234 +msgid "" +"Which IPv6s are allowed to connect? Leave empty to allow any IP to connect." +msgstr "" + +#: contrib/config-daemon.scm:992 +msgid "TCP transport" +msgstr "" + +#: contrib/config-daemon.scm:1012 +msgid "Which port should be used by the HTTP transport?" +msgstr "" + +#: contrib/config-daemon.scm:1036 +msgid "Which is the external port of the HTTP transport?" +msgstr "" + +#: contrib/config-daemon.scm:1037 +msgid "" +"Use this option if your firewall maps, say, port 80 to your real HTTP port. " +"This can be useful in making the HTTP messages appear even more legit " +"(without needing to run gnunetd as root due to the use of a privileged port)." +msgstr "" + +#: contrib/config-daemon.scm:1048 +msgid "HTTP transport" +msgstr "" + +#: contrib/config-daemon.scm:1067 +msgid "What is the maximum transfer unit for SMTP?" +msgstr "" + +#: contrib/config-daemon.scm:1079 +msgid "" +"What is the maximum number of e-mails that gnunetd would be allowed to send " +"per hour?" +msgstr "" + +#: contrib/config-daemon.scm:1080 +msgid "Use 0 for unlimited" +msgstr "" + +#: contrib/config-daemon.scm:1091 +msgid "Which e-mail address should be used to send e-mail to this peer?" +msgstr "" + +#: contrib/config-daemon.scm:1092 +msgid "" +"You must make sure that e-mail received at this address is forwarded to the " +"PIPE which is read by gnunetd. Use the FILTER option to filter e-mail with " +"procmail and the PIPE option to set the name of the pipe." +msgstr "" + +#: contrib/config-daemon.scm:1103 +msgid "" +"Which header line should other peers include in e-mails to enable filtering?" +msgstr "" + +#: contrib/config-daemon.scm:1104 +msgid "" +"You can specify a header line here which can then be used by procmail to " +"filter GNUnet e-mail from your inbox and forward it to gnunetd." +msgstr "" + +#: contrib/config-daemon.scm:1115 +msgid "What is the filename of the pipe where gnunetd can read its e-mail?" +msgstr "" + +#: contrib/config-daemon.scm:1116 +msgid "Have a look at contrib/dot-procmailrc for an example .procmailrc file." +msgstr "" + +#: contrib/config-daemon.scm:1127 +msgid "What is the name and port of the server for outgoing e-mail?" +msgstr "" + +#: contrib/config-daemon.scm:1128 +msgid "The basic format is HOSTNAME:PORT." +msgstr "" + +#: contrib/config-daemon.scm:1139 +msgid "SMTP transport" +msgstr "" + +#: contrib/config-daemon.scm:1162 +msgid "Which port should be used by the UDP IPv4 transport?" +msgstr "" + +#: contrib/config-daemon.scm:1186 +msgid "What is the maximum transfer unit for UDP?" +msgstr "" + +#: contrib/config-daemon.scm:1198 +msgid "Which IPs are not allowed to connect?" +msgstr "" + +#: contrib/config-daemon.scm:1210 +msgid "" +"Which IPs are allowed to connect? Leave empty to allow connections from any " +"IP." +msgstr "" + +#: contrib/config-daemon.scm:1246 +msgid "UDP transport" +msgstr "" + +#: contrib/config-daemon.scm:1268 +#, fuzzy +msgid "Network interface" +msgstr "Netzwerkgerät:" + +#: contrib/config-daemon.scm:1280 +msgid "External IP address (leave empty to try auto-detection)" +msgstr "" + +#: contrib/config-daemon.scm:1292 +msgid "External IPv6 address (leave empty to try auto-detection)" +msgstr "" + +#: contrib/config-daemon.scm:1304 +msgid "Transports" +msgstr "" + +#: contrib/config-daemon.scm:1326 +msgid "What is the maximum number of bytes per second that we may receive?" +msgstr "" + +#: contrib/config-daemon.scm:1338 +msgid "What is the maximum number of bytes per second that we may send?" +msgstr "" + +#: contrib/config-daemon.scm:1350 +msgid "What is the maximum CPU load (percentage)?" +msgstr "" + +#: contrib/config-daemon.scm:1351 +msgid "" +"The highest tolerable CPU load. Load here always refers to the total system " +"load, that is it includes CPU utilization by other processes. A value of 50 " +"means that once your 1 minute-load average goes over 50% non-idle, GNUnet " +"will try to reduce CPU consumption until the load goes under the threshold. " +"Reasonable values are typically between 50 and 100. Multiprocessors may use " +"values above 100." +msgstr "" + +#: contrib/config-daemon.scm:1362 +msgid "What is the maximum IO load (permille)?" +msgstr "" + +#: contrib/config-daemon.scm:1364 +msgid "" +"The highest tolerable IO load. Load here refers to the percentage of CPU " +"cycles wasted waiting for IO for the entire system, that is it includes disk " +"utilization by other processes. A value of 10 means that once the average " +"number of cycles wasted waiting for IO is more than 10% non-idle, GNUnet " +"will try to reduce IO until the load goes under the threshold. Reasonable " +"values are typically between 10 and 75." +msgstr "" + +#: contrib/config-daemon.scm:1375 +msgid "What is the maximum CPU load (hard limit)?" +msgstr "" + +#: contrib/config-daemon.scm:1376 +msgid "" +"The highest tolerable CPU load. This is the hard limit, so once it is " +"reached, gnunetd will start to massively drop data to reduce the load. Use " +"with caution." +msgstr "" + +#: contrib/config-daemon.scm:1387 +msgid "What is the maximum upstream bandwidth (hard limit)?" +msgstr "" + +#: contrib/config-daemon.scm:1388 +msgid "" +"The limit is given as a percentage of the MAXNETUPBPS limit. Use 100 to " +"have MAXNETUPBPS be the hard limit. Use zero for no limit." +msgstr "" + +#: contrib/config-daemon.scm:1400 +msgid "What priority should gnunetd use to run?" +msgstr "" + +#: contrib/config-daemon.scm:1401 +msgid "" +"You can specify priorities like NORMAL, ABOVE NORMAL, BELOW NORMAL, HIGH and " +"IDLE or a numerical integer value (man nice). The default is IDLE, which " +"should result in gnunetd only using resources that would otherwise be idle." +msgstr "" + +#: contrib/config-daemon.scm:1413 +msgid "Should we disable random padding (experimental option)?" +msgstr "" + +#: contrib/config-daemon.scm:1425 +msgid "Use basic bandwidth limitation? (YES/NO). If in doubt, say YES." +msgstr "" + +#: contrib/config-daemon.scm:1427 +msgid "" +"Basic bandwidth limitation (YES) means simply that the bandwidth limits " +"specified apply to GNUnet and only to GNUnet. If set to YES, you simply " +"specify the maximum bandwidth (upstream and downstream) that GNUnet is " +"allowed to use and GNUnet will stick to those limitations. This is useful " +"if your overall bandwidth is so large that the limit is mostly used to " +"ensure that enough capacity is left for other applications. Even if you " +"want to dedicate your entire connection to GNUnet you should not set the " +"limits to values higher than what you have since GNUnet uses those limits to " +"determine for example the number of connections to establish (and it would " +"be inefficient if that computation yields a number that is far too high). \n" +"\n" +"While basic bandwidth limitation is simple and always works, there are some " +"situations where it is not perfect. Suppose you are running another " +"application which performs a larger download. During that particular time, " +"it would be nice if GNUnet would throttle its bandwidth consumption " +"(automatically) and resume using more bandwidth after the download is " +"complete. This is obviously advanced magic since GNUnet will have to " +"monitor the behavior of other applications. Another scenario is a monthly " +"cap on bandwidth imposed by your ISP, which you would want to ensure is " +"obeyed. Here, you may want GNUnet to monitor the traffic from other " +"applications to ensure that the combined long-term traffic is within the pre-" +"set bounds. Note that you should probably not set the bounds tightly since " +"GNUnet may observe that the bounds are about to be broken but would be " +"unable to stop other applications from continuing to use bandwidth.\n" +"\n" +"If either of these two scenarios applies, set BASICLIMITING to NO. Then set " +"the bandwidth limits to the COMBINED amount of traffic that is acceptable " +"for both GNUnet and other applications. GNUnet will then immediately " +"throttle bandwidth consumption if the short-term average is above the limit, " +"and it will also try to ensure that the long-term average is below the " +"limit. Note however that using NO can have the effect of GNUnet (almost) " +"ceasing operations after other applications perform high-volume downloads " +"that are beyond the defined limits. GNUnet would reduce consumption until " +"the long-term limits are again within bounds.\n" +"\n" +"NO only works on platforms where GNUnet can monitor the amount of traffic " +"that the local host puts out on the network. This is only implemented for " +"Linux and Win32. In order for the code to work, GNUnet needs to know the " +"specific network interface that is used for the external connection (after " +"all, the amount of traffic on loopback or on the LAN should never be counted " +"since it is irrelevant)." +msgstr "" + +#: contrib/config-daemon.scm:1444 +#, fuzzy +msgid "Network interface to monitor" +msgstr "Netzwerkgerät:" + +#: contrib/config-daemon.scm:1445 +msgid "" +"For which interfaces should we do accounting? GNUnet will evaluate the " +"total traffic (not only the GNUnet related traffic) and adjust its bandwidth " +"usage accordingly. You can currently only specify a single interface. GNUnet " +"will also use this interface to determine the IP to use. Typical values are " +"eth0, ppp0, eth1, wlan0, etc. 'ifconfig' will tell you what you have. " +"Never use 'lo', that just won't work. Under Windows, specify the index " +"number reported by 'gnunet-win-tool -n'." +msgstr "" + +#: contrib/config-daemon.scm:1456 +#, fuzzy +msgid "Load management" +msgstr "Ungültige Kommandozeilen Parameter.\n" + +#: contrib/config-daemon.scm:1482 contrib/config-client.scm:413 +msgid "Root node" +msgstr "" + +#: contrib/config-client.scm:153 +msgid "Where should gnunet-clients write their logs?" +msgstr "" + +#: contrib/config-client.scm:185 +msgid "On which machine and port is gnunetd running (for clients)?" +msgstr "" + +#: contrib/config-client.scm:186 +#, fuzzy +msgid "This is equivalent to the -H option. The format is IP:PORT." +msgstr "Wert der Option anzeigen" + +#: contrib/config-client.scm:197 +#, fuzzy +msgid "What is the path to the configuration file for gnunetd?" +msgstr "" +"Einen Wert aus der Konfigurationsdatei auf der Standardausgabe ausgeben" + +#: contrib/config-client.scm:198 +msgid "This option is used when clients need to start gnunetd." +msgstr "" + +#: contrib/config-client.scm:210 +#, fuzzy +msgid "General options" +msgstr "Weitere Einstellungen" + +#: contrib/config-client.scm:227 +msgid "Do not add metadata listing the creation time for inserted content" +msgstr "" + +#: contrib/config-client.scm:239 +msgid "Which non-default extractors should GNUnet use for keyword extractors" +msgstr "" + +#: contrib/config-client.scm:240 +msgid "" +"Specify which additional extractor libraries should be used. gnunet-insert " +"uses libextractor to extract keywords from files. libextractor can be " +"dynamically extended to handle additional file formats. If you want to use " +"more than the default set of extractors, specify additional extractor " +"libraries here. The format is [[-]LIBRARYNAME[:[-]LIBRARYNAME]*].\n" +"\n" +"The default is to use filenames and to break larger words at spaces (and " +"underscores, etc.). This should be just fine for most people. The '-' " +"before a library name indicates that this should be executed last and makes " +"only sense for the split-library." +msgstr "" + +#: contrib/config-client.scm:253 +msgid "How many entries should the URI DB table have?" +msgstr "" + +#: contrib/config-client.scm:254 +msgid "" +"GNUnet uses two bytes per entry on the disk. This database is used to keep " +"track of how a particular URI has been used in the past. For example, " +"GNUnet may remember that a particular URI has been found in a search " +"previously or corresponds to a file uploaded by the user. This information " +"can then be used by user-interfaces to filter URI lists, such as search " +"results. If the database is full, older entries will be discarded. The " +"default value should be sufficient without causing undue disk utilization." +msgstr "" + +#: contrib/config-client.scm:265 +msgid "Location of the file specifying metadata for the auto-share directory" +msgstr "" + +#: contrib/config-client.scm:277 +msgid "" +"Location of the file with the PID of any running gnunet-auto-share daemon " +"process" +msgstr "" + +#: contrib/config-client.scm:289 +msgid "Location of the log file for gnunet-auto-share" +msgstr "" + +#: contrib/config-client.scm:301 +#, fuzzy +msgid "File-Sharing options" +msgstr "Alle Optionen anzeigen" + +#: contrib/config-client.scm:319 +msgid "Which plugins should be loaded by gnunet-gtk?" +msgstr "" + +#: contrib/config-client.scm:320 +msgid "" +"Load the about plugin for the about dialog. The daemon plugin allows " +"starting and stopping of gnunetd and displays information about gnunetd. " +"The fs plugin provides the file-sharing functionality. The stats plugin " +"displays various statistics about gnunetd." +msgstr "" + +#: contrib/config-client.scm:331 +msgid "How frequently (in milli-seconds) should the statistics update?" +msgstr "" + +#: contrib/config-client.scm:332 +msgid "" +"Each pixel in the stats dialog corresponds to the time interval specified " +"here." +msgstr "" + +#: contrib/config-client.scm:344 +msgid "Do not show thumbnail previews from meta-data in search results" +msgstr "" + +#: contrib/config-client.scm:345 +msgid "" +"This option is useful for people who maybe offended by some previews or use " +"gnunet-gtk at work and would like to avoid bad surprises." +msgstr "" + +#: contrib/config-client.scm:356 +msgid "Do not show search results for files that were uploaded by us" +msgstr "" + +#: contrib/config-client.scm:357 +msgid "" +"This option is useful to eliminate files that the user already has from the " +"search. Naturally, enabling this option maybe confusing because some " +"obviously expected search results would no longer show up. This option only " +"works if the URI_DB_SIZE option under FS is not zero (since the URI DB is " +"used to determine which files the user is sharing)" +msgstr "" + +#: contrib/config-client.scm:369 +msgid "To which directory should gnunet-gtk save downloads to?" +msgstr "" + +#: contrib/config-client.scm:381 +#, fuzzy +msgid "Options related to gnunet-gtk" +msgstr "Nicht verbunden zu gnunetd." + +#: contrib/config-client.scm:401 +#, fuzzy +msgid "Full pathname of GNUnet client HOME directory" +msgstr "Dateiformat fehlerhaft (kein GNUnet Verzeichnis?)\n" + +#: contrib/config-client.scm:402 +msgid "The directory for GNUnet files that belong to the user." +msgstr "" + +#~ msgid "Could not determine my public IPv6 address.\n" +#~ msgstr "Die öffentliche IPv6-Adresse konnte nicht ermittelt werden.\n" + +#~ msgid "`%s': unknown service: %s\n" +#~ msgstr "`%s': unbekannter Dienst: %s\n" + +#~ msgid "Failed to start transport service on port %d.\n" +#~ msgstr "Der Transportdienst auf Port %d konnte nicht gestartet werden.\n" + +#, fuzzy +#~ msgid "# bytes received via TCP6" +#~ msgstr "# Bytes empfangen über TCP" + +#, fuzzy +#~ msgid "# bytes sent via TCP6" +#~ msgstr "# Bytes gesendet über TCP" + +#, fuzzy +#~ msgid "# bytes dropped by TCP6 (outgoing)" +#~ msgstr "# Bytes verworfen von TCP (ausgehend)" + +#~ msgid "UDP6: Could not determine my public IPv6 address.\n" +#~ msgstr "UDP6: Öffentliche IPv6-Adresse konnte nicht ermittelt werden.\n" + +#, fuzzy +#~ msgid "# bytes received via UDP6" +#~ msgstr "# Bytes empfangen über UDP" + +#, fuzzy +#~ msgid "# bytes sent via UDP6" +#~ msgstr "# Bytes gesendet über UDP" + +#, fuzzy +#~ msgid "# bytes dropped by UDP6 (outgoing)" +#~ msgstr "# Bytes verworfen von UDP (outgoing)" + +#~ msgid "HTTP: Could not determine my public IP address.\n" +#~ msgstr "HTTP: öffentliche IP-Adresse konnte nicht ermittelt werden.\n" + +#, fuzzy +#~ msgid "Received malformed message instead of welcome message. Closing.\n" +#~ msgstr "" +#~ "Es wurde per TCP von einem anderen Knoten eine ungültige Nachricht (Größe " +#~ "%u) empfangen. Verbindung wird geschlossen.\n" + +#, fuzzy +#~ msgid "Received malformed message from tcp-peer connection. Closing.\n" +#~ msgstr "" +#~ "Es wurde per TCP von einem anderen Knoten eine ungültige Nachricht (Größe " +#~ "%u) empfangen. Verbindung wird geschlossen.\n" + +#, fuzzy +#~ msgid "TCP: Could not determine my public IP address.\n" +#~ msgstr "HTTP: öffentliche IP-Adresse konnte nicht ermittelt werden.\n" + +#~ msgid "Cannot connect to %u.%u.%u.%u:%u: %s\n" +#~ msgstr "Verbindung zu %u.%u.%u.%u:%u fehlgeschlagen: %s\n" + +#~ msgid "UDP: Could not determine my public IP address.\n" +#~ msgstr "UDP: öffentliche IP-Adresse konnte nicht ermittelt werden.\n" + +#~ msgid "Failed to send message of size %d via UDP to %u.%u.%u.%u:%u: %s\n" +#~ msgstr "" +#~ "Eine Nachricht der Größe %d konnte nicht per UDP an %u.%u.%u.%u:%u " +#~ "versendet werden: %s\n" + +#, fuzzy +#~ msgid "Received malformed message from udp-peer connection. Closing.\n" +#~ msgstr "" +#~ "Es wurde per TCP von einem anderen Knoten eine ungültige Nachricht (Größe " +#~ "%u) empfangen. Verbindung wird geschlossen.\n" + +#~ msgid "'%s(%s,%s)' succeeded\n" +#~ msgstr "'%s(%s,%s)' erfolgreich abgeschlossen\n" + +#~ msgid "'%s(%s,%s)' failed.\n" +#~ msgstr "'%s(%s,%s)' fehlgeschlagen.\n" + +#~ msgid "" +#~ "%s::%s - RPC %s:%p could not be unregistered: another callback registered " +#~ "under that name: %p\n" +#~ msgstr "" +#~ "%s::%s - RPC %s:%p konnte nicht unregistriert werden: ein anderer " +#~ "Callback ist unter diesem Namen registiert: %p\n" + +#~ msgid "%s::%s - RPC %s:%p could not be unregistered: not found\n" +#~ msgstr "" +#~ "%s::%s - RPC %s:%p konnte nicht unregistriert werden: nicht gefunden\n" + +#~ msgid "Invalid message of type %u received. Dropping.\n" +#~ msgstr "" +#~ "Ungültige Nachricht des Typs %u empfangen. Nachricht wird verworfen.\n" + +#~ msgid "Dropping RPC request %u: message malformed.\n" +#~ msgstr "RPC Anfrage %u wird verworfen: Nachricht ist beschädigt.\n" + +#~ msgid "`%s' called with timeout above 1 hour (bug?)\n" +#~ msgstr "`%s' mit einem Timeout von über einer Stunde aufgerufen (Fehler?)\n" + +#~ msgid "RPC not unregistered: %s:%p\n" +#~ msgstr "RPC nicht unregistriert: %s:%p\n" + +#~ msgid "RPC async reply invalid.\n" +#~ msgstr "RPC async Antwort ungültig.\n" + +#~ msgid "async RPC reply not received.\n" +#~ msgstr "async RPC Antwort nicht empfangen.\n" + +#~ msgid "GAP received invalid content from `%s'\n" +#~ msgstr "GAP hat ungültige Inhalte von `%s' empfangen.\n" + +#~ msgid "" +#~ "Cover traffic requested but traffic service not loaded. Rejecting " +#~ "request.\n" +#~ msgstr "" +#~ "Verdeckender Netzwerkverkehr angefordert, aber der Verkehrsdienst wurde " +#~ "nicht geladen. Anfrage wird abgelehnt.\n" + +#~ msgid "Cannot satisfy desired level of anonymity, ignoring request.\n" +#~ msgstr "" +#~ "Gewünschter Grad an Anonymität kann nicht erreicht werden, Anfrage wird " +#~ "ignoriert.\n" + +#~ msgid "# gap requests policy: immediate drop" +#~ msgstr "# gap Anfragen mit taktischer Entscheidung: sofortiges Verwerfen" + +#~ msgid "# gap requests policy: not routed" +#~ msgstr "# gap Anfragen mit taktischer Entscheidung: kein Routing" + +#~ msgid "# gap requests policy: not answered" +#~ msgstr "# gap Anfragen mit taktischer Entscheidung: nicht Antworten" + +#~ msgid "# gap requests processed: attempted add to RT" +#~ msgstr "# gap Anfragen verarbeitet: versucht, der RT hinzuzufügen" + +#~ msgid "# gap requests processed: local result" +#~ msgstr "# gap Anfragen verarbeitet: lokales Ergebnis" + +#~ msgid "# gap requests forwarded (counting each peer)" +#~ msgstr "#gap Anfragen weitergeleitet (jeder Knoten gezählt)" + +#~ msgid "# gap duplicate requests (pending)" +#~ msgstr "# gap doppelte Anfragen (unfertig)" + +#~ msgid "# gap duplicate requests that were re-tried" +#~ msgstr "# gap doppelte Anfragen, die wiederholt wurden" + +#~ msgid "# gap re-try ttl difference (cummulative)" +#~ msgstr "# gap Wiederholungs-TTL-Differenz (kummulativ)" + +#~ msgid "# gap reply duplicates" +#~ msgstr "#gap doppelte Antworten" + +#~ msgid "# gap routing slots currently in use" +#~ msgstr "# gap Routingschächte im Moment in Verwendung" + +#, fuzzy +#~ msgid "# gap rewards pending" +#~ msgstr "# gap doppelte Anfragen (unfertig)" + +#, fuzzy +#~ msgid "# gap response weights" +#~ msgstr "# gap falsche Antworten" + +#~ msgid "" +#~ "Traffic service failed to load; gap cannot ensure cover-traffic " +#~ "availability.\n" +#~ msgstr "" +#~ "Verkehrsdienst konnte nicht geladen werden, GAP kann keinen verdeckenden " +#~ "Netzwerkverkehr sicherstellen.\n" + +#~ msgid "`%s' registering handlers %d %d\n" +#~ msgstr "`%s' registriert Handler %d %d\n" + +#~ msgid "" +#~ "set interval for availability of updates to SECONDS (for namespace " +#~ "insertions only)" +#~ msgstr "" +#~ "Intervall der Verfügbarkeit von Updates auf SECONDS setzen (nur für das " +#~ "Einfügen in Namespaces)" + +#~ msgid "" +#~ "specifies this as an aperiodic but updated publication (for namespace " +#~ "insertions only)" +#~ msgstr "" +#~ "Dies als unregelmäßige aber aktualisierbare Veröffentlichung kennzeichnen " +#~ "(nur für das Einfügen in Namespaces)" + +#~ msgid "specify creation time for SBlock (see man-page for format)" +#~ msgstr "Erstellungszeit für den SBLOCK angeben (s. Manpage zum Format)" + +#~ msgid "" +#~ "ID of the previous version of the content (for namespace update only)" +#~ msgstr "" +#~ "ID der vorherigen Version des Inhalts (nur für das Einfügen in Namespaces)" + +#~ msgid "Parsing time failed. Use `%s' format.\n" +#~ msgstr "Das Parsen der Zeit schlug fehl. Verwenden Sie das `%s' Format.\n" + +#~ msgid "exit after receiving LIMIT results" +#~ msgstr "Abbrechen, nachdem LIMIT Ergebnisse empfangen wurden" + +#~ msgid "wait DELAY seconds for search results before aborting" +#~ msgstr "TIMEOUT Sekunden auf Suchergebnisse warten, bevor abgebrochen wird" + +#, fuzzy +#~ msgid "# FS currently tracked queries from clients" +#~ msgstr "# Client Trace-Antworten gesendet" + +#~ msgid "Indexed file disappeared, deleting block for query `%s'\n" +#~ msgstr "" +#~ "Indizierte Datei ist verschwunden, Block für Anfrage `%s' wird gelöscht\n" + +#~ msgid "" +#~ "Configuration file must specify directory for storage of FS data in " +#~ "section `%s' under `%s'.\n" +#~ msgstr "" +#~ "Die Konfigurationsdatei muss ein Verzeichnis für die Speicherung von FS " +#~ "Daten in der Sektion `%s' unter `%s' angeben.\n" + +#~ msgid "" +#~ "You must specify a postive number for `%s' in the configuration in " +#~ "section `%s'.\n" +#~ msgstr "" +#~ "Sie müssen für `%s' in der Sektion `%s' der Konfigurationsdatei eine " +#~ "positive Zahl angeben.\n" + +#, fuzzy +#~ msgid "# FS valid replies received" +#~ msgstr "# Bytes empfangen über TCP" + +#~ msgid "AND" +#~ msgstr "UND" + +#~ msgid "Error running search (no reason given)." +#~ msgstr "Das Starten der Suche schlug fehl (Ursache unbekannt)." + +#~ msgid "Download failed (no reason given)" +#~ msgstr "ECRS Download schlug fehl (Ursache unbekannt)." + +#~ msgid "Could not unlink temporary file `%s': %s\n" +#~ msgstr "Temporäre Datei `%s' konnte nicht gelöscht werden: %s\n" + +#~ msgid "Write(%d, %p, %d) failed: %s\n" +#~ msgstr "Write(%d, %p, %d) schlug fehl: %s\n" + +#~ msgid "" +#~ "Content `%s' seems to be not available on the network (tried %u times).\n" +#~ msgstr "" +#~ "Inhalt `%s' scheint im Netzwerk nicht verfügbar zu sein (%u mal " +#~ "versucht).\n" + +#~ msgid "Cannot create pseudonym `%s', file `%s' exists.\n" +#~ msgstr "" +#~ "Pseudonym `%s' kann nicht erstellt werden, da die Datei `%s' bereits " +#~ "existiert.\n" + +#~ msgid "Publication interval for periodic publication changed." +#~ msgstr "" +#~ "Veröffentlichungsintervall für periodische Veröffentlichung wurde " +#~ "geändert." + +#~ msgid "" +#~ "Publishing update for periodically updated content more than a week ahead " +#~ "of schedule.\n" +#~ msgstr "" +#~ "Veröffentlichungsdatum für periodisch aktualisierten Inhalt ist mehr als " +#~ "eine Woche früher als geplant.\n" + +#~ msgid "Message received from peer is invalid.\n" +#~ msgstr "Empfangene Nachricht ist ungültig.\n" + +#~ msgid "Message received from client is invalid\n" +#~ msgstr "Empfangene Client-Nachricht ist ungültig.\n" + +#~ msgid "Maximum number of chat clients reached.\n" +#~ msgstr "Maximale Anzahl an Chat Clients erreicht.\n" + +#~ msgid "Now %d of %d chat clients at this node.\n" +#~ msgstr "Jetzt sind %d von %d auf diesem Knoten.\n" + +#, fuzzy +#~ msgid "Invalid data in %s (NCS). Trying to fix (by deletion).\n" +#~ msgstr "Ungültige Daten in %s. Korrektur wird versucht (durch Löschung).\n" + +#, fuzzy +#~ msgid "`%s' failed at %s:%d with error: %s" +#~ msgstr "`%s' schlug bei %s:%d mit dem Fehler %s fehl\n" + +#, fuzzy +#~ msgid "`%s' failed at %s:%d with error `%s' after %llums\n" +#~ msgstr "`%s' schlug bei %s:%d mit dem Fehler %s fehl\n" + +#~ msgid "Trying to use file `%s' for MySQL configuration.\n" +#~ msgstr "Versuche, Datei `%s' für MySQL Konfiguration zu verwenden.\n" + +#~ msgid "" +#~ "Directory `%s' in directory `%s' does not match naming convention. " +#~ "Removed.\n" +#~ msgstr "" +#~ "Die Datei `%s' im Verzeichnis `%s' entspricht nicht der Namenskonvention. " +#~ "Datei wurde entfernt.\n" + +#~ msgid "Start GNUnet-testbed helper." +#~ msgstr "GNUnet-testbed Helfer starten." + +#~ msgid "Cannot connect to LOOPBACK port %d: %s\n" +#~ msgstr "Verbindung zum LOOPBACK port %d schlug fehl: %s\n" + +#~ msgid "Could not execute `%s': %s\n" +#~ msgstr "`%s' konnte nicht ausgeführt werden: %s\n" + +#~ msgid "No client service started. Trying again in 30 seconds.\n" +#~ msgstr "Kein Client Dienst gestartet. Erneuter Versuch in 30 Sekunden.\n" + +#~ msgid "" +#~ "Error (%s) binding the TCP listener to port %d. No proxy service " +#~ "started.\n" +#~ "Trying again in %d seconds...\n" +#~ msgstr "" +#~ "Fehler (%s) beim Binden des TCP Listeners an den Port %d. Der Proxy " +#~ "Dienst wurde nicht gestartet.\n" +#~ "Erneuter Versuch in %d Sekunden...\n" + +#~ msgid "Rejected unauthorized connection from %u.%u.%u.%u.\n" +#~ msgstr "Unauthorisierte Verbindung von %u.%u.%u.%u. wurde abgewiesen.\n" + +#~ msgid "Protocol violation on socket. Expected command.\n" +#~ msgstr "Protokollverletzung auf Socket. Kommando erwartet.\n" + +#~ msgid "Command `%s' not found!\n" +#~ msgstr "Kommando `%s' wurde nicht gefunden!\n" + +#~ msgid "Start GNUnet testbed controller." +#~ msgstr "GNUnet testbed Controller starten." + +#~ msgid "Malformed entry in the configuration in section %s under %s: %s\n" +#~ msgstr "" +#~ "Beschädigter Eintrag in der Konfigurationsdatei in Sektion %s unter %s: %" +#~ "s\n" + +#~ msgid "Could not send acknowledgement back to client.\n" +#~ msgstr "Bestätigung konnte nicht an Client zurück gesendet werden.\n" + +#~ msgid "Received unknown testbed message of type %u.\n" +#~ msgstr "Es wurde eine unbekannte Testbed Nachricht des Typs %u empfangen.\n" + +#~ msgid "size of `%s' message is too short. Ignoring.\n" +#~ msgstr "Größe der `%s' Nachricht ist zu kurz. Nachricht wird ignoriert.\n" + +#~ msgid "size of `%s' message is wrong. Ignoring.\n" +#~ msgstr "Größe der `%s' Nachricht ist falsch. Nachricht wird ignoriert.\n" + +#~ msgid "TESTBED could not generate hello message for protocol %u\n" +#~ msgstr "" +#~ "Das TESTBED konnte keine Hello Nachricht für das Protokoll %u erzeugen\n" + +#~ msgid "received invalid `%s' message\n" +#~ msgstr "ungültige `%s' Nachricht empfangen\n" + +#~ msgid "received invalid `%s' message (empty module name)\n" +#~ msgstr "ungültige `%s' Nachricht empfangen (leerer Modulname)\n" + +#~ msgid "loading module `%s' failed. Notifying client.\n" +#~ msgstr "das Laden von Modul `%s' schlug fehl. Client wird benachrichtigt.\n" + +#~ msgid "unloading module failed. Notifying client.\n" +#~ msgstr "das Entladen des Moduls schlug fehl. Client wird benachrichtigt.\n" + +#~ msgid "received invalid `%s' message: %s.\n" +#~ msgstr "ungültige `%s' Nachricht empfangen: %s.\n" + +#~ msgid "'..' is not allowed in file name (%s).\n" +#~ msgstr "'..' ist nicht erlaubt in einem Dateinamen (%s).\n" + +#~ msgid "Empty filename for UPLOAD_FILE message is invalid!\n" +#~ msgstr "Leerer Dateiname für UPLOAD_FILE Nachricht ist ungültig!\n" + +#~ msgid "Filename for UPLOAD_FILE message is not null-terminated (invalid!)\n" +#~ msgstr "" +#~ "Dateiname für UPLOAD_FILE Nachricht ist nicht Null-terminiert " +#~ "(ungültig!)\n" + +#~ msgid "Invalid message received at %s:%d." +#~ msgstr "Ungültige Nachricht empfangen bei %s:%d." + +#~ msgid "received invalid testbed message of size %u\n" +#~ msgstr "ungültige Testbed Nachricht der Größe %u empfangen\n" + +#~ msgid "" +#~ "Received testbed message of type %u but unexpected size %u, expected %u\n" +#~ msgstr "" +#~ "Empfangene Testbed Nachricht des Typs %u hat die unerwartete Größe %u, es " +#~ "wurde %u erwartet\n" + +#~ msgid "No testbed URL given, not registered.\n" +#~ msgstr "" +#~ "Keine Testbed URL angegeben, es wurde keine Registrierung vorgenommen.\n" + +#~ msgid "Could not resolve name of HTTP proxy `%s'.\n" +#~ msgstr "Der Name des HTTP Proxies `%s' konnte nicht aufgelöst werden.\n" + +#~ msgid "Invalid URL `%s' (must begin with `%s')\n" +#~ msgstr "Ungültige URL `%s' (muss mit `%s' beginnen)\n" + +#~ msgid "Malformed http URL: `%s' at `%s'. Testbed-client not registered.\n" +#~ msgstr "" +#~ "Beschädigte HTTP URL: `%s' bei `%s'. Testbed-Client wurde nicht " +#~ "registriert.\n" + +#~ msgid "Could not register testbed, host `%s' unknown\n" +#~ msgstr "" +#~ "Testbed konnte nicht registriert werden, Host `%s' ist nicht bekannt.\n" + +#~ msgid "Failed to send HTTP request to host `%s': %s\n" +#~ msgstr "HTTP Anfrage konnte nicht an Host `%s' gesendet werden: %s\n" + +#~ msgid "Failed so send HTTP request `%s' to host `%s': %s\n" +#~ msgstr "Fehler beim Senden der HTTP Anfrage `%s' an Host `%s': %s\n" + +#~ msgid "Exit register (error: no http response read).\n" +#~ msgstr "Abbruch der Registrierung (Fehler: keine HTTP Antwort gelesen).\n" + +#~ msgid "allows construction of a P2P-testbed (incomplete)" +#~ msgstr "Ermöglicht die Konstruktion einer P2P-Testumgebung (inkomplett)" + +#, fuzzy +#~ msgid "Failed to connect to %s:%u in %ds\n" +#~ msgstr "Es konnte keine Verbindung mit gnunetd hergestellt werden.\n" + +#, fuzzy +#~ msgid "`%s' failed at %s:%d in %s with error: %s\n" +#~ msgstr "`%s' schlug bei %s:%d mit dem Fehler %s fehl\n" + +#~ msgid "GNUnet configuration assistant" +#~ msgstr "GNUnet Konfigurationsassistent" + +#, fuzzy +#~ msgid "" +#~ "Welcome to GNUnet!\n" +#~ "\n" +#~ "This assistant will ask you a few basic questions in order to configure " +#~ "GNUnet.\n" +#~ "\n" +#~ "Please visit our homepage at\n" +#~ "\thttp://gnunet.org\n" +#~ "and join our community at\n" +#~ "\thttps://gnunet.org/drupal/\n" +#~ "\n" +#~ "Have a lot of fun,\n" +#~ "\n" +#~ "The GNUnet team" +#~ msgstr "" +#~ "Willkommen bei GNUnet!\n" +#~ "\n" +#~ "Dieser Assistent wird Ihnen einige grundlegende Fragen stellen, um GNUnet " +#~ "zu konfigurieren.\n" +#~ "\n" +#~ "Bitte besuchen Sie unsere Homepage\n" +#~ "\thttp://gnunet.org\n" +#~ "und schließen Sie sich unserer Community an:\n" +#~ "\thttps://gnunet.org/drupal/\n" +#~ "\n" +#~ "Viel Spaß,\n" +#~ "\n" +#~ "das GNUnet-Team" + +#~ msgid "Next" +#~ msgstr "Weiter" + +#~ msgid "" +#~ "Enter information about your network connection here.\n" +#~ "\n" +#~ "The \"Network interface\" is the device that connects your computer to " +#~ "the internet. This is usually a modem, an ISDN card or a network card in " +#~ "case you are using DSL.\n" +#~ "\n" +#~ "If your provider always assigns the same IP-Address to you (a \"static\" " +#~ "IP-Address), enter it into the \"IP-Address\" field. If your IP-Address " +#~ "changes every now and then (\"dynamic\" IP-Address) but there's a " +#~ "hostname that always points to your actual IP-Address (\"Dynamic DNS\"), " +#~ "you can also enter it here.\n" +#~ "If in doubt, leave the field empty. GNUnet will then try to determine " +#~ "your IP-Address.\n" +#~ "\n" +#~ "If you are connected to the internet through another computer doing SNAT, " +#~ "a router or a \"hardware firewall\" and other computers on the internet " +#~ "cannot connect to this computer, check the last option on this page. " +#~ "Leave it unchecked on direct connections through modems, ISDN cards and " +#~ "DNAT (also known as \"port forwarding\")." +#~ msgstr "" +#~ "Geben Sie hier Ihre Netzwerkinformationen ein.\n" +#~ "\n" +#~ "Das \"Netzwerkgerät\" ist das Gerät, das Ihren Computer mit dem Internet " +#~ "verbindet. Dies ist normalerweise ein Modem, eine ISDN-Karte oder eine " +#~ "Netzwerkkarte für den Fall, dass Sie DSL benutzen.\n" +#~ "\n" +#~ "Wenn Ihnen Ihr Provider immer die gleiche IP-Adresse zuweist (eine " +#~ "\"statische\" IP-Adresse), geben Sie diese in das \"IP-Adresse\"-Feld " +#~ "ein. Wenn Ihre IP-Adresse sich hin und wieder ändert (\"dynamische\" IP-" +#~ "Adresse), es jedoch einen Hostnamen gibt, der immer auf Ihre aktuelle IP-" +#~ "Adresse zeigt (\"Dynamisches DNS\"), so können Sie ihn auch eingeben.\n" +#~ "Im Zweifel lassen Sie das Feld leer. GNUnet wird dann versuchen, die IP-" +#~ "Adresse automatisch zu bestimmen.\n" +#~ "\n" +#~ "Wenn Sie nicht direkt mit dem Internet verbunden sind, sondern dies über " +#~ "einen anderen Rechner mit SNAT, einem Router oder einer \"Hardware " +#~ "Firewall\" geschieht und andere Computer im Internet keine Verbindung mit " +#~ "diesem Computer herstellen können, so aktivieren Sie die letzte Option " +#~ "auf dieser Seite. Lassen Sie sie jedoch deaktiviert, wenn Sie eine " +#~ "direkte Verbindung über ein Modem, eine ISDN-Karte oder einen anderen " +#~ "Rechner mit DNAT (auch bekannt als \"Port forwarding\") zum Internet " +#~ "haben." + +#~ msgid "Computer cannot receive inbound connections (SNAT/Firewall)" +#~ msgstr "" +#~ "Computer kann keine ankommenden Verbindungen akzeptieren (SNAT/Firewall)" + +#~ msgid "IP-Address/Hostname:" +#~ msgstr "IP-Adresse/Hostname:" + +#, fuzzy +#~ msgid "Network interface:" +#~ msgstr "Netzwerkgerät:" + +#, fuzzy +#~ msgid "Network connection" +#~ msgstr "GNUnet Konfiguration" + +#~ msgid "" +#~ "You can limit GNUnet's ressource usage here.\n" +#~ "\n" +#~ "\"Bandwidth limitation\" is how much data may be sent per second. If you " +#~ "have a flatrate you can set it to the maximum speed of your internet " +#~ "connection.\n" +#~ "\n" +#~ "The \"Max. CPU usage\" is the percentage of processor time GNUnet is " +#~ "allowed to use." +#~ msgstr "" +#~ "Hier können Sie GNUnet's Ressourcennutzung einschränken.\n" +#~ "\n" +#~ "\"Bandbreitenbeschränkung\" gibt an, wieviele Daten maximal pro Sekunde " +#~ "übetragen werden dürfen. Wenn Sie eine Flatrate haben, können Sie diese " +#~ "Werte auf die maximal erreichbare Geschwindigkeit Ihrer " +#~ "Internetverbindung setzen.\n" +#~ "\n" +#~ "Die \"Max. CPU Nutzung\" gibt den Prozentsatz an CPU-Zeit an, den GNUnet " +#~ "für sich verwenden darf." + +#~ msgid "Downstream (Bytes/s):" +#~ msgstr "Downstream (Bytes/s):" + +#~ msgid "Upstream (Bytes/s):" +#~ msgstr "Upstream (Bytes/s):" + +#~ msgid "Bandwidth limitation" +#~ msgstr "Bandbreitenbeschränkung" + +#~ msgid "Use denoted bandwidth for GNUnet" +#~ msgstr "Angegebene Bandbreite für GNUnet verwenden" + +#~ msgid "Share denoted bandwidth with other applications" +#~ msgstr "Angegebene Bandbreite mit anderen Anwendungen teilen" + +#~ msgid "Bandwidth sharing" +#~ msgstr "Aufteilung der Bandbreite" + +#~ msgid "Max. CPU usage (%):" +#~ msgstr "Max. CPU Nutzung (%):" + +#~ msgid "CPU usage" +#~ msgstr "CPU Nutzung" + +#~ msgid "Load limitation" +#~ msgstr "Lastbeschränkung" + +#~ msgid "" +#~ "GNUnet is able to store data from other peers in your datastore. This is " +#~ "useful if an adversary has access to your inserted content and you need " +#~ "to deny that the content is yours. With \"content migration\" on, the " +#~ "content could have \"migrated\" over the internet to your node without " +#~ "your knowledge.\n" +#~ "It also helps to spread popular content over different peers to enhance " +#~ "availability.\n" +#~ "\n" +#~ "The GNUnet datastore contains all data that GNUnet generates (index data, " +#~ "inserted and migrated content). Its maximum size can be specified below.\n" +#~ "\n" +#~ "If you are an experienced user, you may want to tweak your GNUnet " +#~ "installation using the enhanced configurator.\n" +#~ "\n" +#~ "After changing the configuration and/or updating GNUnet, it is sometimes " +#~ "required to run gnunet-update to update internal data structures. " +#~ "Depending on the changes made, this may take some time." +#~ msgstr "" +#~ "GNUnet ist in der Lage, Daten von anderen Knoten in Ihrem Datenspeicher " +#~ "zu speichern. Das ist nützlich, wenn ein Widersacher Zugriff auf Ihre " +#~ "eingefügten Inhalte erlangt und Sie abstreiten müssen, dass diese Daten " +#~ "Ihnen gehören. Mit \"Inhaltsmigration\" angeschaltet können die Inhalte " +#~ "über das Internet von einem anderen Knoten zu Ihrem Rechner ohne Ihr " +#~ "Wissen \"gewandert\" sein.\n" +#~ "Außerdem hilft es, beliebte Inhalte über verschiedene Netzteilnehmer zu " +#~ "verteilen, um so die Verfügbarkeit zu erhöhen.\n" +#~ "\n" +#~ "Der GNUnet Datenspeicher enthält alle Daten, die GNUnet erzeugt " +#~ "(Indexdaten, eingefügte und migrierte Inhalte). Seine maximale Größe kann " +#~ "unten angegeben werden.\n" +#~ "\n" +#~ "Wenn Sie ein fortgeschrittener Benutzer sind, möchten Sie vielleicht " +#~ "weitere Feinjustierungen an GNUnet über den \"erweiterten Konfigurator\" " +#~ "vornehmen.\n" +#~ "Nachdem die Konfiguration verändert und/oder GNUnet upgedated wurde ist " +#~ "es manchmal nötig, gnunet-update auszuführen, um interne Datenstrukturen " +#~ "zu aktualisieren. Abhängig von den gemachten Änderungen kann dies etwas " +#~ "Zeit in Anspruch nehmen." + +#~ msgid "Store migrated content" +#~ msgstr "Migrierte Inhalte speichern" + +#~ msgid "Maximum datastore size (MB):" +#~ msgstr "Maximale Größe des Datenspeichers (MB):" + +#~ msgid "Start the GNUnet background process on computer startup" +#~ msgstr "" +#~ "GNUnet Hintergrundprozeß beim Starten des Computers automatisch starten" + +#~ msgid "Open the enhanced configurator" +#~ msgstr "Erweiterten Konfigurator starten" + +#, fuzzy +#~ msgid "Run gnunet-update" +#~ msgstr "gnunet-update schlug fehlt!" + +#, fuzzy +#~ msgid "Other settings" +#~ msgstr "Weitere Einstellungen" + +#~ msgid "Finish" +#~ msgstr "Fertigstellen" + +#~ msgid "" +#~ "Define the user and the group owning the GNUnet service here.\n" +#~ "\n" +#~ "For security reasons, it is a good idea to let this setup create a new " +#~ "user account and a new group under which the GNUnet service is started at " +#~ "system startup.\n" +#~ "\n" +#~ "However, GNUnet may not be able to access files other than its own. This " +#~ "includes files you want to publish in GNUnet. You'll have to grant read " +#~ "permissions to the user specified below.\n" +#~ "\n" +#~ "Leave the fields empty to run GNUnet with system privileges." +#~ msgstr "" +#~ "Geben Sie den Benutzer und die Gruppe an, der der GNUnet Dienst gehören " +#~ "soll.\n" +#~ "\n" +#~ "Aus Sicherheitsgründen ist es eine gute Idee, dieses Setup ein neues " +#~ "Benutzerkonto und eine neue Gruppe anlegen zu lassen, unter der der " +#~ "GNUnet Dienst beim Systemstart läuft.\n" +#~ "\n" +#~ "Natürlich kann GNUnet dann nur auf seine eigenen Dateien zugreifen. Dies " +#~ "betrifft auch Dateien, die Sie im GNUnet veröffentlichen möchten. Sie " +#~ "müssen dann dem unten angegebenen Benutzerkonto zuerst Leseberechtigungen " +#~ "geben.\n" +#~ "\n" +#~ "Lassen Sie dieses Feld leer, wenn Sie GNUnet mit Systemprivilegien laufen " +#~ "lassen möchten." + +#~ msgid "User account:" +#~ msgstr "Benutzerkonto:" + +#~ msgid "Group:" +#~ msgstr "Gruppe:" + +#, fuzzy +#~ msgid "gnunet-setup" +#~ msgstr "gnunet-update ausführen" + +#, fuzzy +#~ msgid "Save configuration" +#~ msgstr "GNUnet Konfiguration" + +#, fuzzy +#~ msgid "Show copyright information for gnunet-setup." +#~ msgstr "Fehler beim Lesen von Informationen von gnunetd.\n" + +#, fuzzy +#~ msgid "About gnunet-setup" +#~ msgstr "gnunet-update ausführen" + +#, fuzzy +#~ msgid "This is the configuration tool for GNUnet." +#~ msgstr "" +#~ "Einen Wert aus der Konfigurationsdatei auf der Standardausgabe ausgeben" + +#~ msgid "Not for English ;-)" +#~ msgstr "Nils Durner and Christian Grothoff" + +#, fuzzy +#~ msgid "Description" +#~ msgstr "Frage" + +#, fuzzy +#~ msgid "Section" +#~ msgstr "Frage" + +#, fuzzy +#~ msgid "Option" +#~ msgstr "_Optionen" + +#~ msgid "Received invalid `%s' message from `%s'.\n" +#~ msgstr "Ungültige `%s' Anfrage von `%s' empfangen.\n" + +#~ msgid "TRACEKIT: routing table full, trace request dropped\n" +#~ msgstr "TRACEKIT: Routing-Tabelle ist voll, Trace-Anfrage wird verworfen\n" + +#~ msgid "TRACEKIT: received invalid `%s' message\n" +#~ msgstr "TRACEKIT: ungültige `%s' Nachricht empfangen\n" + +#~ msgid "Format specification invalid. Use 0 for user-readable, 1 for dot\n" +#~ msgstr "" +#~ "Formatangabe ungültig. Verwenden Sie 0 für menschen-lesbar und 1 für dot\n" + +#~ msgid "Peer `%s' did not report back.\n" +#~ msgstr "Knoten `%s' hat sich nicht zurückgemeldet.\n" + +#~ msgid "Could not send request to gnunetd.\n" +#~ msgstr "Anfrage konnte nicht an gnunetd gesendet werden.\n" + +#~ msgid "" +#~ "You must specify the name of a pipe for the SMTP transport in section `%" +#~ "s' under `%s'.\n" +#~ msgstr "" +#~ "Für den SMTP Transport müssen Sie den Namen einer Pipe in Sektion `%s', " +#~ "Eintrag `%s' eintragen.\n" + +#, fuzzy +#~ msgid "Failed to initialize libesmtp: %s.\n" +#~ msgstr "SQLite Datenbank konnte nicht initialisiert werden.\n" + +#~ msgid "Sending E-mail to `%s' failed.\n" +#~ msgstr "Das Senden einer E-Mail an `%s' schlug fehl.\n" + +#~ msgid "%.*s filter %s (SMTP)" +#~ msgstr "%.*s filter %s (SMTP)" + +#~ msgid "MTU for `%s' is probably too low (fragmentation not implemented!)\n" +#~ msgstr "" +#~ "MTU für `%s' ist möglicherweise zu gering (Fragmentierung ist nicht " +#~ "implementiert!)\n" + +#, fuzzy +#~ msgid "Network configuration: NAT" +#~ msgstr "GNUnet Konfiguration" + +#~ msgid "" +#~ "Is this machine behind NAT?\n" +#~ "\n" +#~ "If you are connected to the internet through another computer doing SNAT, " +#~ "a router or a \"hardware firewall\" and other computers on the internet " +#~ "cannot connect to this computer, say \"yes\" here. Answer \"no\" on " +#~ "direct connections through modems, ISDN cards and DNAT (also known as " +#~ "\"port forwarding\")." +#~ msgstr "" +#~ "Ist diese Maschine hinter NAT?\n" +#~ "\n" +#~ "Wenn Sie mit dem Internet über einen anderen Computer per SNAT, einem " +#~ "Router oder einer \"Hardware Firewall\" verbunden sind und andere " +#~ "Computer im Internet keine Verbindung zu diesem Computer herstellen " +#~ "können, so sagen Sie hier \"Ja\". Antworten Sie \"Nein\" bei direkten " +#~ "Verbindungen über Modem, ISDN-Karten und DNAT (auch bekannt als \"Port " +#~ "forwarding\")." + +#, fuzzy +#~ msgid "Configuration of the logging system" +#~ msgstr "" +#~ "Konfiguration oder die GNUnet Version hat sich geändert. Sie müssen `%s' " +#~ "ausführen!\n" + +#, fuzzy +#~ msgid "Run gnunetd as this user." +#~ msgstr "gnunet-update ausführen" + +#, fuzzy +#~ msgid "Run gnunetd during system startup?" +#~ msgstr "gnunet-update ausführen" + +#, fuzzy +#~ msgid "Path settings" +#~ msgstr "Weitere Einstellungen" + +#~ msgid "specify nickname" +#~ msgstr "Spitznamen angeben" + +#~ msgid "Start GNUnet chat client." +#~ msgstr "GNUnet chat client starten" + +#~ msgid "Could not connect to gnunetd.\n" +#~ msgstr "Verbindung zu gnunetd konnte nicht hergestellt werden.\n" + +#~ msgid "You must specify a nickname (use option `%s').\n" +#~ msgstr "" +#~ "Sie müssen einen Spitznamen angeben (verwenden Sie die Option `%s').\n" + +#~ msgid "Could not send join message to gnunetd\n" +#~ msgstr "'join' Nachricht konnte nicht an gnunetd gesendet werden.\n" + +#~ msgid "Could not send message to gnunetd\n" +#~ msgstr "Nachricht konnte nicht an gnunetd gesendet werden.\n" + +#~ msgid "mysql datastore" +#~ msgstr "mysql Datenspeicher" + +#, fuzzy +#~ msgid "" +#~ "`%s' failed at %s:%d with error: I/%s S/%s SC/%s SS/%s SSC/%s U/%s D/%s " +#~ "DG/%s\n" +#~ msgstr "`%s' schlug bei %s:%d mit dem Fehler %s fehl\n" + +#~ msgid "Database failed to delete `%s'.\n" +#~ msgstr "Die Datenbank konnte `%s' nicht löschen.\n" + +#, fuzzy +#~ msgid "Error log:\n" +#~ msgstr "Fehler" + +#, fuzzy +#~ msgid "# bytes received via TCP-OLD" +#~ msgstr "# Bytes empfangen über TCP" + +#, fuzzy +#~ msgid "# bytes sent via TCP-OLD" +#~ msgstr "# Bytes gesendet über TCP" + +#, fuzzy +#~ msgid "# bytes dropped by TCP-OLD (outgoing)" +#~ msgstr "# Bytes verworfen von TCP (ausgehend)" + +#~ msgid "hello advertisement for protocol %d received.\n" +#~ msgstr "Hello Ankündigung für Protokoll %d empfangen.\n" + +#~ msgid "`%s' failed (%d, %u). Will not send PING.\n" +#~ msgstr "`%s' fehlgeschlagen (%d, %u). PING wird nicht gesendet.\n" + +#~ msgid "Removing hello from peer `%s' (expired %ds ago).\n" +#~ msgstr "Hello von Knoten `%s' wird entfernt (lief vor %ds ab).\n" + +#~ msgid "No reply received within %llums.\n" +#~ msgstr "Keine Antwort innerhalb %llums erhalten.\n" + +#~ msgid "Waiting for gnunetd to start (%u iterations left)...\n" +#~ msgstr "Warte auf den Start von gnunetd (%u Iterationen verbleiben)...\n" + +#, fuzzy +#~ msgid "Deleting expired content. This may take a while.\n" +#~ msgstr "Ein neuer Hostkey wird erzeugt (dies kann eine Weile dauern).\n" + +#~ msgid "User `%s' not known, cannot change UID to it.\n" +#~ msgstr "" +#~ "Benutzer `%s' ist nicht bekannt, UID kann nicht gewechselt werden.\n" + +#~ msgid "" +#~ "Expected welcome on http connection, got garbage. Closing connection.\n" +#~ msgstr "" +#~ "Es wurde eine Willkommensnachricht erwartet, tatsächlich wurde jedoch " +#~ "keine gesendet. HTTP-Verbindung wird geschlossen.\n" + +#~ msgid "%s: Rejected connection from blacklisted address %u.%u.%u.%u.\n" +#~ msgstr "" +#~ "%s: Abgewiesene Verbindung von schwarzgelisteter Adresse %u.%u.%u.%u.\n" + +#~ msgid "" +#~ "Could not bind the HTTP listener to port %d. No transport service " +#~ "started.\n" +#~ msgstr "" +#~ "HTTP-Listener konnte nicht an Port %d gebunden werden. Der Transport " +#~ "Dienst wurde nicht gestartet.\n" + +#~ msgid "Could not determine my public IP address.\n" +#~ msgstr "Öffentliche IP-Adresse konnte nicht ermittelt werden.\n" + +#~ msgid "Unexpected reply to `%s' operation.\n" +#~ msgstr "Unerwartete Antwort zu `%s' Operation.\n" + +#~ msgid "join table called NAME" +#~ msgstr "Tabelle NAME anschließen" + +#~ msgid "Malformed optional field `%s' received from peer `%s'.\n" +#~ msgstr "Ungültiges optionales Feld `%s' empfangen von Knoten `%s'.\n" + +#~ msgid "Could not find peer `%s' in routing table!\n" +#~ msgstr "Knoten `%s' konnte nicht in der Routing Tabelle gefunden werden!\n" + +#~ msgid "Received malformed response to `%s' from peer `%s'.\n" +#~ msgstr "Beschädigte Antwort auf `%s' von Knoten `%s' empfangen.\n" + +#~ msgid "Invalid response to `%s'.\n" +#~ msgstr "Ungültige Antwort auf `%s'.\n" + +#~ msgid "Malformed response to `%s' on master table.\n" +#~ msgstr "Beschädigte Antwort auf `%s' in Master Tabelle.\n" + +#~ msgid "Invalid response to `%s' from `%s'\n" +#~ msgstr "Ungültige Antwort auf `%s' von `%s'\n" + +#~ msgid "Received invalid RPC `%s'.\n" +#~ msgstr "Ungültiger RPC `%s' empfangen.\n" + +#~ msgid "RPC for `%s' received for table that we do not participate in!\n" +#~ msgstr "" +#~ "RPC für `%s' empfangen für eine Tabelle, an der wir nicht beteiligt " +#~ "sind!\n" + +#~ msgid "`%s' failed. Terminating connection to client.\n" +#~ msgstr "`%s' fehlgeschlagen. Beende Verbindung zu Client.\n" + +#~ msgid "" +#~ "`%s' called with cron job not in queue, adding. This may not be what you " +#~ "want.\n" +#~ msgstr "" +#~ "`%s' aufgerufen wobei Cron Job nicht Warteschlange ist. Er wird " +#~ "hinzugefügt. Das ist möglicherweise nicht, was Sie wollen.\n" + +#~ msgid "" +#~ "Share denoted bandwidth with other applications?\n" +#~ "\n" +#~ "Say \"yes\" here, if you don't want other network traffic to interfere " +#~ "with GNUnet's operation, but still wish to constrain GNUnet's bandwidth " +#~ "usage to values entered in the previous steps, or if you can't reliably " +#~ "measure the maximum capabilities of your connection. \"No\" can be very " +#~ "useful if other applications are causing a lot of traffic on your LAN. " +#~ "In this case, you do not want to limit the traffic that GNUnet can " +#~ "inflict on your internet connection whenever your high-speed LAN gets " +#~ "used (e.g. by NFS)." +#~ msgstr "" +#~ "Angegebene Bandbreite mit anderen Anwendungen teilen?\n" +#~ "\n" +#~ "Sagen Sie hier \"Ja\", wenn Sie nicht möchten, dass anderer " +#~ "Netzwerkverkehr GNUnets Funktion stört aber dennoch GNUnets Bandbreite " +#~ "gemäß den Angaben in den vorherigen Schritten einschränken möchten oder " +#~ "Sie die maximalen Möglichkeiten Ihrer Internetverbindung nicht " +#~ "zuverlässig messen können. \"Nein\" kann nützlich sein, wenn andere " +#~ "Anwendungen viel Netzwerkverkehr in Ihrem LAN verursachen. In diesem Fall " +#~ "möchten Sie nicht GNUnets Netzwerkverkehr über die Internetverbindung " +#~ "einschränken, wann immer Ihre Hochgeschwindigkeits-LAN-Verbindung " +#~ "verwendet wird (z.B. durch NFS)." + +#~ msgid "How much CPU (in %) may be used?" +#~ msgstr "Wieviel CPU (in %) darf verwendet werden?" + +#~ msgid "" +#~ "You can limit GNUnet's resource usage here.\n" +#~ "\n" +#~ "This is the percentage of processor time GNUnet is allowed to use." +#~ msgstr "" +#~ "Hier können Sie GNUnets Ressourcenverwendung einschränken.\n" +#~ "\n" +#~ "Dies ist der Prozentsatz an Prozessorzeit, den GNUnet verwenden darf." + +#~ msgid "" +#~ "Store migrated content?\n" +#~ "\n" +#~ "GNUnet is able to store data from other peers in your datastore. This is " +#~ "useful if an adversary has access to your inserted content and you need " +#~ "to deny that the content is yours. With \"content migration\" on, the " +#~ "content could have \"migrated\" over the internet to your node without " +#~ "your knowledge.\n" +#~ "It also helps to spread popular content over different peers to enhance " +#~ "availability." +#~ msgstr "" +#~ "Sollen migrierte Inhalte gespeichert werden?\n" +#~ "GNUnet ist in der Lage, Daten von anderen Knoten in Ihrem Datenspeicher " +#~ "zu speichern. Das ist nützlich, wenn ein Widersacher Zugriff auf Ihre " +#~ "eingefügten Inhalte erlangt und Sie abstreiten müssen, dass diese Daten " +#~ "Ihnen gehören. Ist die \"Inhaltsmigration\" angeschaltet, so können die " +#~ "Inhalte über das Internet von einem anderen Knoten zu Ihrem Rechner ohne " +#~ "Ihr Wissen \"gewandert\" sein.\n" +#~ "Außerdem hilft es, beliebte Inhalte über verschiedene Netzteilnehmer zu " +#~ "verteilen, um so die Verfügbarkeit zu erhöhen." + +#~ msgid "" +#~ "If you are an experienced user, you may want to tweak your GNUnet " +#~ "installation using the enhanced configurator.\n" +#~ "\n" +#~ "Do you want to start it after saving your configuration?" +#~ msgstr "" +#~ "Wenn Sie ein erfahrener Benutzer sind, so möchten Sie vielleicht Ihre " +#~ "GNUnet Installation über den erweiterten Konfigurator optimieren.\n" +#~ "\n" +#~ "Möchten Sie ihn starten, nachdem Ihre Konfiguration gespeichert wurde?" + +#~ msgid "" +#~ "Unable to save configuration file %s: %s.\n" +#~ "\n" +#~ "Try again?" +#~ msgstr "" +#~ "Konfigurationsdatei %s kann nicht gespeichert werden: %s.\n" +#~ "\n" +#~ "Soll es nochmals versucht werden?" + +#~ msgid "Failed to send `%s'. Closing connection.\n" +#~ msgstr "Fehler beim Senden von `%s'. Verbindung wird geschlossen.\n" + +#~ msgid "Received invalid `%s' request (size %d)\n" +#~ msgstr "Ungültige Anfrage `%s' empfangen (Größe %d)\n" + +#~ msgid "Received invalid `%s' request (wrong table)\n" +#~ msgstr "Ungültige Anfrage `%s' empfangen (falsche Tabelle)\n" + +#~ msgid "Received unknown request type %d at %s:%d\n" +#~ msgstr "Unbekannte Anfrageart %d empfangen bei %s:%d\n" + +#~ msgid "This client already participates in the given DHT!\n" +#~ msgstr "Dieser Client beteiligt sich bereits an der angegebenen DHT!\n" + +#~ msgid "Cannot leave DHT: table not known!\n" +#~ msgstr "DHT kann nicht verlassen werden: Tabelle unbekannt!\n" + +#~ msgid "gnunetd signaled error in response to `%s' message\n" +#~ msgstr "gnunetd gab in Bezug auf die `%s' Nachricht einen Fehler zurück.\n" + +#~ msgid "Failed to receive response to `%s' message from gnunetd\n" +#~ msgstr "" +#~ "Fehler beim Empfangen der Antwort von gnunetd auf die `%s' Nachricht\n" + +#~ msgid "Failed to send `%s' message to gnunetd\n" +#~ msgstr "Fehler beim Senden der `%s' Nachricht an gnunetd\n" + +#~ msgid "Join a DHT." +#~ msgstr "Einer DHT anschließen." + +#~ msgid "allow SIZE bytes of memory for the local table" +#~ msgstr "SIZE bytes an Speicher für die lokale Tabelle erlauben" + +#~ msgid "Call to `%s' returns %d.\n" +#~ msgstr "Aufruf von `%s' gibt %d zurück.\n" + +#~ msgid "Call to `%s' with key `%s'.\n" +#~ msgstr "Aufruf von `%s' mit Schlüssel `%s'.\n" + +#~ msgid "Call to `%s' with value '%.*s' (%d bytes).\n" +#~ msgstr "Aufruf von `%s' mit Wert '%.*s' (%d Bytes).\n" + +#~ msgid "Error joining DHT.\n" +#~ msgstr "Fehler beim Beitreten zu der DHT.\n" + +#~ msgid "Joined DHT. Press CTRL-C to leave.\n" +#~ msgstr "Der DHT beigetreten. Drücken Sie STRG-C, um sie zu verlassen.\n" + +#~ msgid "Error leaving DHT.\n" +#~ msgstr "Fehler beim Verlassen der DHT.\n" + +#~ msgid "`%s' failed: table not found!\n" +#~ msgstr "`%s' fehlgeschlagen: Tabelle nicht gefunden!\n" + +#~ msgid "sendAck failed. Terminating connection to client.\n" +#~ msgstr "sendAck fehlgeschlagen. Beende Verbindung zu Client.\n" + +#~ msgid "Unindex failed." +#~ msgstr "Deindizierung schlug fehl." + +#, fuzzy +#~ msgid "Upload failed (consult logs)." +#~ msgstr "ECRS Download schlug fehl (siehe Protokolldateien)." + +#~ msgid "Could not resolve name of SMTP server `%s': %s" +#~ msgstr "Der Name des SMTP servers `%s' konnte nicht aufgelöst werden: %s" + +#~ msgid "SMTP server send unexpected response at %s:%d.\n" +#~ msgstr "Der SMTP server sendete eine unerwartete Antwort bei %s:%d.\n" + +#~ msgid "" +#~ "SMTP server failed to respond with 250 confirmation code to `%s' " +#~ "request.\n" +#~ msgstr "" +#~ "SMTP Server antwortete nicht mit einem 250 Bestätigungscode auf eine `%s' " +#~ "Anfrage.\n" + +#~ msgid "Failed to send `%s' request to SMTP server.\n" +#~ msgstr "Fehler beim Senden einer `%s' Anfrage an den SMTP Server.\n" + +#~ msgid "query table called NAME" +#~ msgstr "Frage Tabelle mit dem Namen NAME ab" + +#~ msgid "No commands specified.\n" +#~ msgstr "Keine Kommandos angegeben.\n" + +#~ msgid "Superflous arguments (ignored).\n" +#~ msgstr "überflüssige Parameter (werden ignoriert).\n" + +#~ msgid "Query `%s' had no results.\n" +#~ msgstr "Abfrage `%s' hatte keine Ergebnisse.\n" + +#~ msgid "FSUI persistence: error restoring download\n" +#~ msgstr "FSUI Beständigkeit: Fehler beim Wiederherstellen des Downloads\n" + +#~ msgid "ECRS download suspending." +#~ msgstr "ECRS download wird eingefroren." + +#~ msgid "Upload failed." +#~ msgstr "Upload fehlgeschlagen." + +#~ msgid "Cannot upload directory without using recursion." +#~ msgstr "" +#~ "Verzeichnis kann nicht ohne die Verwendung von Rekursion hochgeladen " +#~ "werden." + +#, fuzzy +#~ msgid "Expected `%s' to be a regular file\n" +#~ msgstr "`%s' ist keine normale Datei.\n" + +#, fuzzy +#~ msgid "expected `%s' to be a directory!\n" +#~ msgstr "`%s' erwartet, dass `%s' ein Verzeichnis ist!\n" + +#~ msgid "Sorry, no help is available for this option.\n" +#~ msgstr "Sorry, für diese Option steht keine Hilfe zur Verfügung.\n" + +#~ msgid "Failed to obtain my (external) IPv6 address!\n" +#~ msgstr "Die öffentliche IPv6-Adresse konnte nicht ermittelt werden!\n" + +#~ msgid "" +#~ "Cannot determine port to bind to. Define in configuration file in " +#~ "section `%s' under `%s' or in `%s' under %s/%s.\n" +#~ msgstr "" +#~ "Der Port, an dem Verbindungen entgegengenommen werden sollen, konnte " +#~ "nicht ermittelt werden. Bitte definieren Sie ihn in der " +#~ "Konfigurationsdatei in der Sektion `%s' unter `%s' oder in `%s' unter %s/%" +#~ "s.\n" + +#~ msgid "Failed to bind to UDP6 port %d.\n" +#~ msgstr "Fehler beim Binden an UDP6 Port %d.\n" + +#, fuzzy +#~ msgid "UDP6: select returned, but ioctl reports %d bytes available!\n" +#~ msgstr "" +#~ "UDP: select kam zurück aber ioctl berichtet, dass 0 Bytes verfügbar " +#~ "sind!\n" + +#~ msgid "Received invalid UDP6 message from %s:%d, dropping.\n" +#~ msgstr "" +#~ "Ungültige UDP6 Nachricht von %s:%d empfangen, Nachricht wird ignoriert.\n" + +#~ msgid "Packet received from %s:%d (UDP6) failed format check." +#~ msgstr "" +#~ "Die Formatüberprüfung des Pakets, das von %s:%d (UDP6) empfangen wurde, " +#~ "schlug fehl." + +#~ msgid "%s: Rejected connection from blacklisted address %s.\n" +#~ msgstr "%s: Zurückgewiesene Verbindung von schwarzgelisteter Adresse %s.\n" + +#~ msgid "" +#~ "Expected welcome message on tcp connection, got garbage (%u, %u). " +#~ "Closing.\n" +#~ msgstr "" +#~ "Es wurde eine Willkommensnachricht erwartet, über die TCP Verbindung " +#~ "wurde aber keine gesendet (%u, %u). Verbindung wird geschlossen.\n" + +#, fuzzy +#~ msgid "UDP: select returned, but ioctl reports %d bytes available!\n" +#~ msgstr "" +#~ "UDP: select kam zurück aber ioctl berichtet, dass 0 Bytes verfügbar " +#~ "sind!\n" + +#~ msgid "Received invalid UDP message from %u.%u.%u.%u:%u, dropping.\n" +#~ msgstr "" +#~ "Es wurde eine ungültige UDP Nachricht von %u.%u.%u.%u:%u empfangen, " +#~ "Nachricht wird ignoriert.\n" + +#~ msgid "Packet received from %u.%u.%u.%u:%u (UDP) failed format check.\n" +#~ msgstr "" +#~ "Ein Paket empfangen von %u.%u.%u.%u:%u (UDP) hat ein ungültiges Format.\n" + +#~ msgid "Expected welcome message on tcp connection, got garbage. Closing.\n" +#~ msgstr "" +#~ "Es wurde eine Willkommensnachricht erwartet, über die TCP Verbindung " +#~ "wurde jedoch keine gesendet. Verbindung wird geschlossen.\n" + +#~ msgid "" +#~ "Received malformed message from tcp6-peer connection. Closing " +#~ "connection.\n" +#~ msgstr "" +#~ "über die TCP6-Verbindung zu einem anderen Knoten wurde eine ungültige " +#~ "Nachricht empfangen. Verbindung wird geschlossen.\n" + +#~ msgid "Network advertisements disabled by configuration!\n" +#~ msgstr "Netzwerkbekanntmachungen wurden per Konfiguration deaktiviert!\n" + +#~ msgid "Version mismatch (`%s' vs. '%*.s'), run gnunet-update!\n" +#~ msgstr "" +#~ "Versionen stimmen nicht überein (`%s' vs. '%*.s'), lassen Sie gnunet-" +#~ "update laufen!\n" + +#~ msgid "" +#~ "Configuration file must specify directory for storing FS data in section `" +#~ "%s' under `%s'.\n" +#~ msgstr "" +#~ "Die Konfigurationsdatei muss in der Sektion `%s' unter `%s' ein " +#~ "Verzeichnis angeben, in dem FS Daten gespeichert werden.\n" + +#~ msgid "Could not initialize libgnunetutil!\n" +#~ msgstr "Konnte libgnunetutil nicht initialisieren!\n" + +#~ msgid "" +#~ "Configuration file must specify a directory for GNUnet to store per-peer " +#~ "data under %s%s\n" +#~ msgstr "" +#~ "Die Konfigurationsdatei muss unter %s%s ein Verzeichnis angeben, in dem " +#~ "GNUnet knotenspezifische Daten speichern kann.\n" + +#~ msgid "%s `%s' returned no known hosts!\n" +#~ msgstr "%s `%s' ergab keine bekannten Knoten!\n" + +#~ msgid "" +#~ "Option `%s' not set in configuration in section `%s', setting to %dm.\n" +#~ msgstr "" +#~ "Option `%s' ist in der Konfigurationsdatei in der Sektion `%s' nicht " +#~ "gesetzt, sie wird auf %dm gesetzt.\n" + +#~ msgid "" +#~ "You should specify at least one transport service under option `%s' in " +#~ "section `%s'.\n" +#~ msgstr "" +#~ "Sie sollten mindestens einen Transport Dienst unter der Option `%s' in " +#~ "der Sektion `%s' angegeben.\n" + +#~ msgid "" +#~ "specify that the contents of the namespace are of the given MIMETYPE (use " +#~ "when creating a new pseudonym)" +#~ msgstr "" +#~ "Angeben, dass die Inhalte des Namespaces vom angegebenen MIMETYOE sind " +#~ "(zu verwenden, wenn ein neues Pseudonym erstellt wird)" + +#~ msgid "" +#~ "specify NAME to be the realname of the user controlling the namespace " +#~ "(use when creating a new pseudonym)" +#~ msgstr "" +#~ "NAME als den Realnamen des Benutzers angeben, der den Namespace verwaltet " +#~ "(zu verwenden, wenn ein neues Pseudonym erstellt wird)" + +#~ msgid "" +#~ "use DESCRIPTION to describe the content of the namespace (use when " +#~ "creating a new pseudonym)" +#~ msgstr "" +#~ "DESCRIPTION als Beschreibung der Inhalte des Namespaces verwenden (zu " +#~ "verwenden, wenn ein neues Pseudonym erstellt wird)" + +#~ msgid "" +#~ "specify the given URI as an address that contains more information about " +#~ "the namespace (use when creating a new pseudonym)" +#~ msgstr "" +#~ "die angegebene URI als die Adresse angeben, die weitere Informationen " +#~ "über den Namespace enthält (zu verwenden, wenn ein neues Pseudonym " +#~ "erstellt wird)" + +#~ msgid "Invalid argument: `%s'\n" +#~ msgstr "Ungültiger Parameter: `%s'\n" + +#~ msgid "Invalid arguments. Exiting.\n" +#~ msgstr "Ungültige Parameter. Abbruch.\n" + +#~ msgid "%8u of %8u bytes deleted." +#~ msgstr "%8u von %8u Bytes gelöscht." + +#~ msgid "specify the file to delete from GNUnet (obligatory, file must exist)" +#~ msgstr "" +#~ "vom GNUnet zu löschende Datei angeben (obgligatorisch, Datei muss " +#~ "existieren)" + +#~ msgid "" +#~ "Remove file from GNUnet. The specified file is not removed\n" +#~ "from the filesystem but just from the local GNUnet datastore." +#~ msgstr "" +#~ "Datei auf GNUnet löschen. Die angegebene Datei wird nicht aus dem " +#~ "Dateisystem gelöscht, sondern aus dem lokalen GNUnet Datenspeicher." + +#~ msgid "You must specify a filename (option -f)\n" +#~ msgstr "Sie müssen eine Datei angeben (Option -f)\n" + +#~ msgid "" +#~ "Error deleting file %s.\n" +#~ "Probably a few blocks were already missing from the database.\n" +#~ msgstr "" +#~ "Fehler beim Löschen der Datei %s.\n" +#~ "Möglicherweise fehlen bereits einige wenige Datenblöcke in der " +#~ "Datenbank.\n" + +#~ msgid "gnunet-directory [OPTIONS] [FILENAMES]" +#~ msgstr "gnunet-directory [OPTIONEN] [DATEINAMEN]" + +#~ msgid "process directories recursively" +#~ msgstr "Verzeichnisse rekursiv bearbeiten" + +#~ msgid "You must pass a positive number to the `%s' option.\n" +#~ msgstr "Sie müssen eine positive Zahl zu der Option `%s' übergeben.\n" + +#~ msgid "You must specify a list of files to insert.\n" +#~ msgstr "Sie müssen eine Liste von Dateien zum Einfügen angeben.\n" + +#~ msgid "Only one file or directory can be specified at a time.\n" +#~ msgstr "Nur eine Datei oder Verzeichnis kann auf einmal angegeben werden.\n" + +#~ msgid "You must specify a file or directory to upload.\n" +#~ msgstr "Sie müssen eine Datei oder Verzeichnis für den Upload angeben.\n" + +#~ msgid "set the desired LEVEL of receiver-anonymity" +#~ msgstr "Den Grad LEVEL der gewünschten Empfänger-Anonymität setzen" + +#~ msgid "Not enough arguments. You must specify a keyword or identifier.\n" +#~ msgstr "" +#~ "Nicht genügend Parameter. Sie müssen ein Schlüsselwort oder einen " +#~ "Bezeichner angeben.\n" + +#~ msgid "LEVEL" +#~ msgstr "GRAD" + +#~ msgid "FILENAME" +#~ msgstr "DATEINAME" + +#~ msgid "This search is already pending!\n" +#~ msgstr "Diese Suche läuft bereits!\n" + +#~ msgid "" +#~ "Configuration file must specify a directory for GNUnet to store per-peer " +#~ "data under %s%s.\n" +#~ msgstr "" +#~ "Die Konfigurationsdatei muss unter %s%s ein Verzeichnis für GNUnet " +#~ "angeben, in dem Knotenbezogene Daten gespeichert werden.\n" + +#~ msgid "" +#~ "Need to have list of friends in configuration under `%s' in section `%" +#~ "s'.\n" +#~ msgstr "" +#~ "Es muss eine Liste von Freunden in der Konfigurationsdatei unter `%s' in " +#~ "der Sektion `%s' angegeben werden.\n" + +#~ msgid "helptext for -t" +#~ msgstr "Hilfetext für -t" + +#~ msgid "Template for gnunet-clients." +#~ msgstr "Vorlage für gnunet-clients." + +#~ msgid "Invalid port \"%s\" in hostlist specification, trying port %d.\n" +#~ msgstr "Ungültiger Port \"%s\" in Angabe der Hostlist, versuche Port %d.\n" + +#~ msgid "Could not download list of peer contacts, host `%s' unknown.\n" +#~ msgstr "" +#~ "Die Liste mit Knotenkontakten konnte nicht heruntergeladen werden, Host `%" +#~ "s' ist unbekannt.\n" + +#~ msgid "`%s' to `%s' failed at %s:%d with error: %s\n" +#~ msgstr "`%s' an `%s' schlug fehl bei %s:%d mit dem Fehler: %s\n" + +#~ msgid "Parsing HTTP response for URL `%s' failed.\n" +#~ msgstr "Das Parsen der HTTP Antwort für die URL `%s' schlug fehl.\n" + +#~ msgid "Parsing hello from `%s' failed.\n" +#~ msgstr "Das Parsen des Hello von `%s' schlug fehl.\n" + +#~ msgid "Could not resolve name of HTTP proxy `%s'. Trying without a proxy.\n" +#~ msgstr "" +#~ "Der Name des HTTP Proxies `%s' konnte nicht aufgelöst werden. Es wird " +#~ "ohne Proxy versucht.\n" + +#~ msgid "Failed to query gnunetd about traffic conditions.\n" +#~ msgstr "Fehler beim Abfragen der Netzwerkverkehrsbedingungen von gnunetd.\n" + +#~ msgid "Did not receive reply from gnunetd about traffic conditions.\n" +#~ msgstr "Keine Antwort von gnunetd über die Netzwerkverkehrsbedingungen.\n" + +#, fuzzy +#~ msgid "" +#~ "Configuration file must specify directory for storing data in section `%" +#~ "s' under `%s'.\n" +#~ msgstr "" +#~ "Die Konfigurationsdatei muss in der Sektion `%s' unter `%s' ein " +#~ "Verzeichnis angeben, in dem FS Daten gespeichert werden.\n" + +#~ msgid "" +#~ "Configuration file must specify a directory for GNUnet to store per-peer " +#~ "data under %s\\%s.\n" +#~ msgstr "" +#~ "Die Konfigurationsdatei muss unter %s\\%s ein Verzeichnis für GNUnet " +#~ "angeben, in dem knotenbezogene Daten gespeichert werden.\n" + +#~ msgid "Option `%s' makes no sense without option `%s'." +#~ msgstr "Option `%s' macht keinen Sinn ohne die Option `%s'." + +#~ msgid "Invalid arguments: " +#~ msgstr "Ungültige Parameter: " + +#~ msgid "" +#~ "\n" +#~ "Exiting.\n" +#~ msgstr "" +#~ "\n" +#~ "Abbruch.\n" + +#~ msgid "No applications defined in configuration!\n" +#~ msgstr "In der Konfigurationsdatei wurden keine Anwendungen definiert!\n" + +#~ msgid "Updated data for %d applications.\n" +#~ msgstr "Daten für %d Anwendungen wurden aktualisiert.\n" + +#~ msgid "Could not write PID to file `%s': %s.\n" +#~ msgstr "PID konnte nicht in Datei `%s' geschrieben werden: %s.\n" + +#~ msgid "Invalid command-line arguments:\n" +#~ msgstr "Ungültige Kommandozeilen Parameter:\n" + +#~ msgid "Argument %d: `%s'\n" +#~ msgstr "Parameter %d: `%s'\n" + +#~ msgid "`%s' starting\n" +#~ msgstr "`%s' startet\n" + +#~ msgid "FATAL: Identity plugin not found!\n" +#~ msgstr "SCHWERWIEGEND: Identity Plugin wurde nicht gefunden!\n" + +#~ msgid "You must specify a non-empty set of transports to test!\n" +#~ msgstr "" +#~ "Sie müssen eine Menge an Transporten angeben, die getestet werden " +#~ "sollen!\n" + +#~ msgid "Available MODEs:\n" +#~ msgstr "Verfügbare MODEs:\n" + +#~ msgid " config\t\ttext-based configuration\n" +#~ msgstr " config\t\ttext-basierte Konfiguration\n" + +#~ msgid " menuconfig\ttext-based menu\n" +#~ msgstr "menuconfig\ttext-basiertes Menü\n" + +#~ msgid " wizard-curses\tBasic text-based graphical configuration\n" +#~ msgstr " wizard-curses\tEinfache text-basierte grafische Konfiguration\n" + +#~ msgid "" +#~ " wizard-gtk\tBasic GTK configuration\n" +#~ "\n" +#~ msgstr "" +#~ " wizard-gtk\tEinfache GTK Konfiguration\n" +#~ "\n" + +#~ msgid "gnunet-setup must have write-access to the configuration file `%s'\n" +#~ msgstr "" +#~ "gnunet-setup benötigt Schreibberechtigungen für die Konfigurationsdatei `%" +#~ "s'\n" + +#~ msgid "" +#~ "Can only run wizard to configure gnunetd.\n" +#~ "Did you forget the `%s' option?\n" +#~ msgstr "" +#~ "Der Assistent kann nur zur Einrichtung von gnunetd gestartet werden.\n" +#~ "Haben Sie die `%s'-Option vergessen?\n" + +#~ msgid "%s: symbol value `%s' invalid for %s\n" +#~ msgstr "%s: Symbolwert `%s' ist ungültig für %s\n" + +#~ msgid "Gtk GNUnet Configurator" +#~ msgstr "Gtk GNUnet Konfigurator" + +#~ msgid "_File" +#~ msgstr "_Datei" + +#~ msgid "Load a config file" +#~ msgstr "Eine Konfigurationsdatei laden" + +#~ msgid "_Load" +#~ msgstr "_öffnen" + +#~ msgid "Save the config in .config" +#~ msgstr "Die Konfiguration in .config speichern" + +#~ msgid "_Save" +#~ msgstr "_Speichern" + +#~ msgid "_Quit" +#~ msgstr "_Beenden" + +#~ msgid "Show _name" +#~ msgstr "_Name anzeigen" + +#~ msgid "Show range (Y/M/N)" +#~ msgstr "Bereich anzeigen (Y/M/N)" + +#~ msgid "Show _range" +#~ msgstr "_Bereich anzeigen" + +#~ msgid "Show _data" +#~ msgstr "_Daten anzeigen" + +#~ msgid "Show all _options" +#~ msgstr "Alle _Optionen anzeigen" + +#~ msgid "_Help" +#~ msgstr "_Hilfe" + +#~ msgid "_Introduction" +#~ msgstr "_Einführung" + +#~ msgid "Goes up of one level (single view)" +#~ msgstr "Bewegt sich eine Ebene nach oben (einfache Ansicht)" + +#~ msgid "Load" +#~ msgstr "Laden" + +#~ msgid "Save a config file" +#~ msgstr "Konfigurationsdatei speichern" + +#~ msgid "Save" +#~ msgstr "Speichern" + +#~ msgid "Single view" +#~ msgstr "Einfache Ansicht" + +#~ msgid "Single" +#~ msgstr "Einfach" + +#~ msgid "Split view" +#~ msgstr "Geteilte Ansicht" + +#~ msgid "Split" +#~ msgstr "Geteilt" + +#~ msgid "Full view" +#~ msgstr "Volle Ansicht" + +#~ msgid "Full" +#~ msgstr "Voll" + +#~ msgid "Collapse the whole tree in the right frame" +#~ msgstr "Den gesamten Baum im rechten Frame kollabieren" + +#~ msgid "Collapse" +#~ msgstr "Kollabieren" + +#~ msgid "Expand the whole tree in the right frame" +#~ msgstr "Den gesamten Baum im rechten Frame expandieren" + +#~ msgid "Expand" +#~ msgstr "Expandieren" + +#, fuzzy +#~ msgid "Introduction" +#~ msgstr "_Einführung" + +#~ msgid "inlining configration file `%s'\n" +#~ msgstr "Binde Konfigurationsdatei `%s' ein\n" + +#~ msgid "" +#~ "Configuration file not found. Please run GNUnet Setup (Client " +#~ "Configuration) first." +#~ msgstr "" +#~ "Die Konfigurationsdatei wurde nicht gefunden. Bitte führen Sie zuerst " +#~ "GNUnet Setup (Client Konfiguration) aus." + +#~ msgid "Configuration file `%s' not found. Run `gnunet-setup -d'!\n" +#~ msgstr "" +#~ "Konfigurationsdatei `%s' nicht gefunden. Bitte führen Sie `gnunet-setup -" +#~ "d' aus!\n" + +#~ msgid "Cron stopped\n" +#~ msgstr "Cron angehalten\n" + +#~ msgid "Shutdown complete.\n" +#~ msgstr "GNUnet wurde erfolgreich heruntergefahren.\n" + +#~ msgid "Caught signal %d.\n" +#~ msgstr "Signal %d empfangen.\n" + +#~ msgid "Invalid network notation (additional characters: `%s')." +#~ msgstr "Ungültige Netzwerk Notation (zusätzliche Zeichen: `%s')." + +#~ msgid "FAILURE" +#~ msgstr "FEHLSCHLAG" + +#~ msgid "MESSAGE" +#~ msgstr "MELDUNG" + +#~ msgid "CRON" +#~ msgstr "CRON" + +#~ msgid "EVERYTHING" +#~ msgstr "ALLES" + +#~ msgid "Invalid LOGLEVEL `%s' specified.\n" +#~ msgstr "Ungültiger LOGLEVEL `%s' angegeben.\n" + +#~ msgid "Failure at %s:%d.\n" +#~ msgstr "Fehler bei %s:%d.\n" + +#~ msgid "" +#~ "Cannot determine port of gnunetd server. Define in configuration file in " +#~ "section `%s' under `%s'.\n" +#~ msgstr "" +#~ "Der Port des gnunetd Servers konnte nicht ermittelt werden. Definieren " +#~ "Sie ihn in der Sektion `%s' unter `%s'.\n" + +#~ msgid "" +#~ "Usage: %s\n" +#~ "%s\n" +#~ "\n" +#~ msgstr "" +#~ "Verwendung: %s\n" +#~ "%s\n" +#~ "\n" + +#~ msgid "Invalid argument for `%s' at %s:%d.\n" +#~ msgstr "Ungültiger Parameter für `%s' bei %s:%d.\n" + +#~ msgid "g" +#~ msgstr "g" + +#~ msgid "t" +#~ msgstr "t" + +#, fuzzy +#~ msgid "unknown" +#~ msgstr "Unbekannter Fehler" + +#~ msgid "`%s' failed, other side closed connection.\n" +#~ msgstr "" +#~ "`%s' fehlgeschlagen, die andere Seite hat die Verbindung geschlossen\n" + +#~ msgid "Attempted path to `%s' was `%s'.\n" +#~ msgstr "Versuchter Pfad für `%s' war `%s'.\n" + +#~ msgid "set verbosity to LEVEL" +#~ msgstr "Umfang der Meldungen auf LEVEL setzen" + +#~ msgid "_License" +#~ msgstr "_Lizenz" + +#~ msgid "Sorry, no help available for this option yet." +#~ msgstr "Sorry, für diese Option steht noch keine Hilfe zur Verfügung" + +#~ msgid "Couldn't find pixmap file: %s" +#~ msgstr "Pixmapdatei %s konnte nicht gefunden werden" + +#~ msgid "" +#~ "Welcome to GNUnet!\n" +#~ "\n" +#~ "This assistant will ask you a few basic questions in order to configure " +#~ "GNUnet.\n" +#~ "\n" +#~ "Please visit our homepage at\n" +#~ "\thttp://www.gnunet.org\n" +#~ "and join our community at\n" +#~ "\thttp://www.gnunet.org/drupal/\n" +#~ "\n" +#~ "Have a lot of fun,\n" +#~ "\n" +#~ "the GNUnet team" +#~ msgstr "" +#~ "Willkommen bei GNUnet!\n" +#~ "\n" +#~ "Dieser Assistent wird Ihnen einige grundlegende Fragen stellen, um GNUnet " +#~ "zu konfigurieren.\n" +#~ "\n" +#~ "Bitte besuchen Sie unsere Homepage\n" +#~ "\thttp://gnunet.org\n" +#~ "und schließen Sie sich unserer Community an:\n" +#~ "\thttps://gnunet.org/drupal/\n" +#~ "\n" +#~ "Viel Spaß,\n" +#~ "\n" +#~ "das GNUnet-Team" + +#~ msgid "" +#~ "You must specify a directory for FS files in the configuration in section " +#~ "`%s' under `%s'." +#~ msgstr "" +#~ "Sie müssen ein Verzeichnis für FS Dateien in der Konfigurationsdatei in " +#~ "der Sektion `%s' unter `%s' angeben." + +#~ msgid "Invalid data in MySQL database. Please verify integrity!\n" +#~ msgstr "" +#~ "Ungültige Daten in der MySQL Datenbank. Bitte überprüfen Sie die " +#~ "Integrität!\n" + +#~ msgid "SQL Database corrupt, ignoring result.\n" +#~ msgstr "SQL Datenbank beschädigt, Ergebnis wird ignoriert.\n" + +#~ msgid "Invalid data in database. Please verify integrity!\n" +#~ msgstr "" +#~ "Es befinden sich ungültige Daten in Datenbank. Bitte überprüfen Sie die " +#~ "Integrität!\n" + +#~ msgid "menuconfig is not available\n" +#~ msgstr " menuconfig ist nicht verfügbar\n" + +#~ msgid "wizard-curses is not available\n" +#~ msgstr "wizard-curses ist nicht verfügbar\n" + +#~ msgid "wizard-gtk is not available\n" +#~ msgstr "wizard-gtk ist nicht verfügbar\n" + +#~ msgid "gconfig is not available\n" +#~ msgstr "gconfig ist nicht verfügbar\n" + +#~ msgid "Recursive download of directory `%s' at %llu of %llu bytes.\n" +#~ msgstr "" +#~ "Rekursiver Download des Verzeichnisses `%s' bei %llu von %llu Bytes.\n" + +#~ msgid "" +#~ "Indexing file `%s' failed. Check file permissions and consult your GNUnet " +#~ "server's logs.\n" +#~ msgstr "" +#~ "Indizierung der Datei `%s' schlug fehl. Bitte prüfen Sie die " +#~ "Dateiberechtigungen und ziehen Sie die Protokolldateien des GNUnet-" +#~ "Servers zu Rate.\n" + +#~ msgid "Show _debug info" +#~ msgstr "_Debug Informationen anzeigen" + +#~ msgid "" +#~ "USAGE: gnunet-setup MODULE\n" +#~ "\n" +#~ "MODULE\n" +#~ " recreate\trecreate configuration files\n" +#~ " config\t\ttext-based configuration\n" +#~ " menuconfig\ttext-based menu\n" +#~ " gconfig\tGTK configuration\n" +#~ " wizard-curses\tBasic text-based graphical configuration\n" +#~ " wizard-gtk\tBasic GTK configuration\n" +#~ "\n" +#~ msgstr "" +#~ "Verwendung: gnunet-setup MODUL\n" +#~ "\n" +#~ "MODUL\n" +#~ " recreate\tKonfigurationsdateien neu erzeugen\n" +#~ " config\t\tText-basierte Konfiguration\n" +#~ " menuconfig\tText-basiertes Menü\n" +#~ " gconfig\tGTK Konfiguration\n" +#~ " wizard-curses\tEinfache text-basierte grafische Konfiguration\n" +#~ " wizard-gtk\tEinfache GTK Konfiguration\n" +#~ "\n" + +#~ msgid "Please specify a path where the configuration files will be stored." +#~ msgstr "" +#~ "Bitte geben Sie den Pfad an, wohin die Konfigurationsdateien gespeichert " +#~ "werden." + +#~ msgid "# p2p trace replies sent" +#~ msgstr "# p2p Trace-Antworten gesendet" + +#~ msgid "Session with peer `%s' confirmed, but I cannot connect! (bug?)\n" +#~ msgstr "" +#~ "Sitzung mit Knoten `%s' ist bestätigt es kann jedoch nicht verbunden " +#~ "werden! (Bug?)\n" + +#~ msgid "LOGLEVEL not specified, that is not ok.\n" +#~ msgstr "LOGLEVEL wurde nicht angegeben, das ist nicht in Ordnung.\n" + +#~ msgid "" +#~ "Interfaces string (%s) in configuration section `%s' under `%s' is " +#~ "malformed.\n" +#~ msgstr "" +#~ "Geräteangabe (%s) in der Konfigurationssektion `%s' unter `%s' ist " +#~ "beschädigt.\n" + +#~ msgid "" +#~ "No network interfaces specified in the configuration file in section `%s' " +#~ "under `%s'.\n" +#~ msgstr "" +#~ "Es sind keine Netzwerkgeräte in der Konfigurationsdatei in der Sektion '%" +#~ "s' unter `%s' definiert.\n" + +#~ msgid "Failed to parse interface data `%s' output at %s:%d.\n" +#~ msgstr "Fehler beim Parsen der Gerätedaten `%s' Ausgabe bei %s:%d.\n" + +#~ msgid "Could not decoding file `%s' at %s:%d.\n" +#~ msgstr "Datei `%s' konnte nicht dekodiert werden bei %s:%d.\n" + +#~ msgid "" +#~ "Configuration file must specify directory for network identities in " +#~ "section %s under %s.\n" +#~ msgstr "" +#~ "Die Konfigurationsdatei muss in Sektion %s unter %s ein Verzeichnis für " +#~ "Identitäten angeben.\n" + +#~ msgid "Sender %u.%u.%u.%u is blacklisted, dropping message.\n" +#~ msgstr "" +#~ "Sender %u.%u.%u.%u steht auf schwarzer Liste, Nachricht wird ignoriert.\n" + +#~ msgid "Sender %s is blacklisted, dropping message.\n" +#~ msgstr "Sender %s steht auf schwarzer Liste, Nachricht wird ignoriert.\n" + +#~ msgid "Removed file `%s' containing invalid peer advertisement.\n" +#~ msgstr "" +#~ "Datei `%s' enthielt eine ungültige Knotenbekanntmachung und wurde " +#~ "entfernt.\n" + +#~ msgid "Removed invalid HELO file `%s'\n" +#~ msgstr "Ungültige HELO Datei `%s' wurde entfernt.\n" + +#~ msgid "Could not determine IP address of the local machine!\n" +#~ msgstr "IP-Adresse der lokalen Maschiene konnte nicht ermittelt werden!\n" + +#~ msgid "Could not determine IP(v6) address of the local machine!\n" +#~ msgstr "" +#~ "IP(v6)-Adresse der lokalen Maschiene konnte nicht ermittelt werden!\n" + +#~ msgid "" +#~ "Could not find IP(v4) for this host. Please provide the IP in the " +#~ "configuration file.\n" +#~ msgstr "" +#~ "IP(v4) dieses Hosts konnte nicht ermittelt werden. Bitte geben Sie die IP " +#~ "in der Konfigurationsdatei an.\n" + +#~ msgid "" +#~ "Could not find IP(v6) for this host. Please provide the IP in the " +#~ "configuration file.\n" +#~ msgstr "" +#~ "IP(v6) dieses Hosts konnte nicht ermittelt werden. Bitte geben Sie die IP " +#~ "in der Konfigurationsdatei an.\n" + +#~ msgid "Save _as" +#~ msgstr "Speichern _unter" + +#~ msgid "Save the config in a file" +#~ msgstr "Die Konfiguration in einer Datei speichern" + +#~ msgid "Error: can't open Service Control Manager: %s (%i)\n" +#~ msgstr "Fehler: der Dienstemanager konnte nicht geöffnet werden: %s (%i)\n" + +#~ msgid "Error: can't create service: %s (#%i)\n" +#~ msgstr "Fehler: Dienst konnte nicht erzeugt werden: %s (#%i)\n" + +#~ msgid "Failure at at %s:%d.\n" +#~ msgstr "Fehler bei %s:%d.\n" diff --git a/po/en@boldquot.header b/po/en@boldquot.header new file mode 100644 index 000000000..fedb6a06d --- /dev/null +++ b/po/en@boldquot.header @@ -0,0 +1,25 @@ +# All this catalog "translates" are quotation characters. +# The msgids must be ASCII and therefore cannot contain real quotation +# characters, only substitutes like grave accent (0x60), apostrophe (0x27) +# and double quote (0x22). These substitutes look strange; see +# http://www.cl.cam.ac.uk/~mgk25/ucs/quotes.html +# +# This catalog translates grave accent (0x60) and apostrophe (0x27) to +# left single quotation mark (U+2018) and right single quotation mark (U+2019). +# It also translates pairs of apostrophe (0x27) to +# left single quotation mark (U+2018) and right single quotation mark (U+2019) +# and pairs of quotation mark (0x22) to +# left double quotation mark (U+201C) and right double quotation mark (U+201D). +# +# When output to an UTF-8 terminal, the quotation characters appear perfectly. +# When output to an ISO-8859-1 terminal, the single quotation marks are +# transliterated to apostrophes (by iconv in glibc 2.2 or newer) or to +# grave/acute accent (by libiconv), and the double quotation marks are +# transliterated to 0x22. +# When output to an ASCII terminal, the single quotation marks are +# transliterated to apostrophes, and the double quotation marks are +# transliterated to 0x22. +# +# This catalog furthermore displays the text between the quotation marks in +# bold face, assuming the VT100/XTerm escape sequences. +# diff --git a/po/en@quot.header b/po/en@quot.header new file mode 100644 index 000000000..a9647fc35 --- /dev/null +++ b/po/en@quot.header @@ -0,0 +1,22 @@ +# All this catalog "translates" are quotation characters. +# The msgids must be ASCII and therefore cannot contain real quotation +# characters, only substitutes like grave accent (0x60), apostrophe (0x27) +# and double quote (0x22). These substitutes look strange; see +# http://www.cl.cam.ac.uk/~mgk25/ucs/quotes.html +# +# This catalog translates grave accent (0x60) and apostrophe (0x27) to +# left single quotation mark (U+2018) and right single quotation mark (U+2019). +# It also translates pairs of apostrophe (0x27) to +# left single quotation mark (U+2018) and right single quotation mark (U+2019) +# and pairs of quotation mark (0x22) to +# left double quotation mark (U+201C) and right double quotation mark (U+201D). +# +# When output to an UTF-8 terminal, the quotation characters appear perfectly. +# When output to an ISO-8859-1 terminal, the single quotation marks are +# transliterated to apostrophes (by iconv in glibc 2.2 or newer) or to +# grave/acute accent (by libiconv), and the double quotation marks are +# transliterated to 0x22. +# When output to an ASCII terminal, the single quotation marks are +# transliterated to apostrophes, and the double quotation marks are +# transliterated to 0x22. +# diff --git a/po/es.gmo b/po/es.gmo new file mode 100644 index 000000000..219b3b52e Binary files /dev/null and b/po/es.gmo differ diff --git a/po/es.po b/po/es.po new file mode 100644 index 000000000..5620f68b9 --- /dev/null +++ b/po/es.po @@ -0,0 +1,6921 @@ +# Spanish translations for GNUnet package. +# +# This file is distributed under the same license as the GNUnet package. +# Miguel Angel Arruga Vivas , 2006. +# +msgid "" +msgstr "" +"Project-Id-Version: GNUnet 0.7.0e\n" +"Report-Msgid-Bugs-To: gnunet-developers@mail.gnu.org\n" +"POT-Creation-Date: 2009-01-08 10:55-0700\n" +"PO-Revision-Date: 2006-06-29 12:05+0200\n" +"Last-Translator: Miguel Angel Arruga \n" +"Language-Team: Spanish\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Plural-Forms: nplurals=2; plural=(n != 1);\n" + +#: src/setup/ncurses/wizard_curs.c:72 src/setup/lib/wizard_util.c:155 +#: src/setup/lib/wizard_util.c:210 +msgid "Error" +msgstr "Error" + +#: src/setup/ncurses/wizard_curs.c:80 +msgid "Help" +msgstr "Ayuda" + +#: src/setup/ncurses/wizard_curs.c:87 +msgid "Error!" +msgstr "¡Error!" + +#: src/setup/ncurses/wizard_curs.c:101 src/applications/vpn/cs.c:94 +msgid "No" +msgstr "No" + +#: src/setup/ncurses/wizard_curs.c:102 src/applications/vpn/cs.c:94 +msgid "Yes" +msgstr "Sí" + +#: src/setup/ncurses/wizard_curs.c:118 src/setup/ncurses/wizard_curs.c:183 +#: src/setup/ncurses/wizard_curs.c:299 src/setup/ncurses/mconf.c:189 +#: src/setup/ncurses/mconf.c:285 src/setup/ncurses/mconf.c:365 +#: src/setup/ncurses/mconf.c:456 +msgid "Internal error! (Choice invalid?)" +msgstr "" + +#: src/setup/ncurses/wizard_curs.c:150 +#, fuzzy +msgid "Abort" +msgstr "_Acerca de" + +#: src/setup/ncurses/wizard_curs.c:151 +#, fuzzy +msgid "Ok" +msgstr "k" + +#: src/setup/ncurses/wizard_curs.c:218 src/setup/ncurses/wizard_curs.c:284 +#: src/setup/ncurses/wizard_curs.c:425 +msgid "GNUnet configuration" +msgstr "Configuración de GNUnet" + +#: src/setup/ncurses/wizard_curs.c:220 +msgid "" +"Welcome to GNUnet!\n" +"\n" +"This assistant will ask you a few basic questions in order to configure " +"GNUnet.\n" +"\n" +"Please visit our homepage at\n" +"\thttp://gnunet.org/\n" +"and join our community at\n" +"\thttp://gnunet.org/drupal/\n" +"\n" +"Have a lot of fun,\n" +"\n" +"the GNUnet team" +msgstr "" +"¡Bienvenido a GNUnet!\n" +"\n" +"Este asistente te preguntará unas cuestiones básicas para configurar " +"GNUnet.\n" +"\n" +"Por favor, visita nuestra página en\n" +"\thttp://gnunet.org/\n" +"y únete a nuestra comunidad en \n" +"\thttp://gnunet.org/drupal/\n" +"\n" +"Diviertete,\n" +"\n" +"el equipo de GNUnet" + +#: src/setup/ncurses/wizard_curs.c:286 +msgid "" +"Choose the network interface that connects your computer to the internet " +"from the list below." +msgstr "" +"Escoge la interfaz de red que conecta tu ordenador a Internet de la lista de " +"abajo." + +#: src/setup/ncurses/wizard_curs.c:304 src/setup/ncurses/wizard_curs.c:318 +msgid "" +"The \"Network interface\" is the device that connects your computer to the " +"internet. This is usually a modem, an ISDN card or a network card in case " +"you are using DSL." +msgstr "" +"La \"Interfaz de red\" es el dispositivo que conecta tu ordenador a " +"Internet. Normalmente es un módem, una tarjeta de RDSI o una tarjeta de red " +"en el caso de los xDSL como el ADSL." + +#: src/setup/ncurses/wizard_curs.c:315 +#, fuzzy +msgid "Network configuration: interface" +msgstr "Interfaz de red:" + +#: src/setup/ncurses/wizard_curs.c:317 +msgid "" +"What is the name of the network interface that connects your computer to the " +"Internet?" +msgstr "" +"¿Cuál es el nombre de la interfaz de red que conecta tu ordenador a Internet?" + +#: src/setup/ncurses/wizard_curs.c:328 +#, fuzzy +msgid "Network configuration: IP" +msgstr "Configuración de GNUnet" + +#: src/setup/ncurses/wizard_curs.c:330 +#, fuzzy +msgid "What is this computer's public IP address or hostname?" +msgstr "" +"¿Cuál es es la dirección pública IP o el nombre del dominio de éste " +"ordenador?\n" +"\n" +"En caso de duda, dejar este campo en blanco." + +#: src/setup/ncurses/wizard_curs.c:331 +#, fuzzy +msgid "" +"If your provider always assigns the same IP-Address to you (a \"static\" IP-" +"Address), enter it into the \"IP-Address\" field. If your IP-Address changes " +"every now and then (\"dynamic\" IP-Address) but there's a hostname that " +"always points to your actual IP-Address (\"Dynamic DNS\"), you can also " +"enter it here.\n" +"If left empty, GNUnet will try to automatically detect the IP.\n" +"You can specify a hostname, GNUnet will then use DNS to resolve it.\n" +"If in doubt, leave this empty." +msgstr "" +"Si tu proveedor siempre te asigna la misma dirección IP (una IP \"estática" +"\") introducela en el campo \"Dirección IP\". Si tu dirección IP cambia pero " +"hay un nombre de dominio que siempre apunta a tu dirección IP actual (\"DNS " +"dinámica\"), puedes introducirlo allí también.\n" +"En caso de duda deja el campo en blanco. GNUnet intentará determinar tu " +"dirección IP" + +#: src/setup/ncurses/wizard_curs.c:346 +#, fuzzy +msgid "Bandwidth configuration: upload" +msgstr "Configuración de GNUnet" + +#: src/setup/ncurses/wizard_curs.c:348 +#, fuzzy +msgid "How much upstream bandwidth (in bytes/s) may be used?" +msgstr "¿Cuánta subida (Bytes/s) será usada por GNUnet?" + +#: src/setup/ncurses/wizard_curs.c:349 +#, fuzzy +msgid "" +"You can limit GNUnet's resource usage here.\n" +"\n" +"The \"upstream\" is the data channel through which data is *sent* to the " +"internet. The limit is the maximum amount which GNUnet is allowed to use. If " +"you have a flatrate, you can set it to the maximum speed of your internet " +"connection. You should not use a value that is higher than what your actual " +"connection allows." +msgstr "" +"Puedes limitar el uso de recursos de GNUnet aquí.\n" +"\n" +"La \"subida\" es el canal de datos a través del cual los datos son " +"*mandados* a Internet. El límite, o bien es el total máximo para este " +"ordenador, o bien el que se le permita usar a GNUnet. Puedes especificar " +"este valor más tarde. Si tienes una tarifa plana puedes configurarlo a la " +"máxima velocidad de tu conexión a Internet." + +#: src/setup/ncurses/wizard_curs.c:361 +#, fuzzy +msgid "Bandwidth configuration: download" +msgstr "Configuración de GNUnet" + +#: src/setup/ncurses/wizard_curs.c:363 +#, fuzzy +msgid "How much downstream bandwidth (in bytes/s) may be used?" +msgstr "¿Cuánta bajada (Bytes/s) será usada por GNUnet?" + +#: src/setup/ncurses/wizard_curs.c:364 +#, fuzzy +msgid "" +"You can limit GNUnet's resource usage here.\n" +"\n" +"The \"downstream\" is the data channel through which data is *received* from " +"the internet. The limit is the maximum amount which GNUnet is allowed to " +"use. If you have a flatrate, you can set it to the maximum speed of your " +"internet connection. You should not use a value that is higher than what " +"your actual connection allows." +msgstr "" +"Puedes limitar el uso de recursos de GNUnet aquí.\n" +"\n" +"La \"bajada\" es el canal de datos a través del cuál los datos son " +"*recibidos* de Internet. El límite, o bien es el total máximo de este " +"ordenador, o bien el que se le permita usar a GNUnet. Puedes especificar " +"este valor más tarde. Si tienes una tarifa plana puedes configurarlo a la " +"máxima velocidad de tu conexión a Internet." + +#: src/setup/ncurses/wizard_curs.c:376 +#, fuzzy +msgid "Quota configuration" +msgstr "Configuración de GNUnet" + +#: src/setup/ncurses/wizard_curs.c:377 +msgid "What is the maximum size of the datastore in MB?" +msgstr "" + +#: src/setup/ncurses/wizard_curs.c:378 +#, fuzzy +msgid "" +"The GNUnet datastore contains all content that GNUnet needs to store " +"(indexed, inserted and migrated content)." +msgstr "" +"¿Cuál es el tamaño máximo de almacenamiento en MB?\n" +"\n" +"El almacenamiento de GNUnet contiene todos los datos que GNUnet genera " +"(datos del índice, contenido insertado y migrado)." + +#: src/setup/ncurses/wizard_curs.c:390 +#, fuzzy +msgid "Daemon configuration: user account" +msgstr "Imposible crear la cuenta de usuario:" + +#: src/setup/ncurses/wizard_curs.c:391 +msgid "As which user should gnunetd be run?" +msgstr "" + +#: src/setup/ncurses/wizard_curs.c:393 +#, fuzzy +msgid "" +"For security reasons, it is a good idea to let this setup create a new user " +"account under which the GNUnet service is started at system startup.\n" +"\n" +"However, GNUnet may not be able to access files other than its own. This " +"includes files you want to publish in GNUnet. You'll have to grant read " +"permissions to the user specified below.\n" +"\n" +"Leave the field empty to run GNUnet with system privileges.\n" +msgstr "" +"Define el usuario bajo el que correrán los servicios de GNUnet.\n" +"\n" +"Por razones de seguridad, es una buena idea dejar que la configuración cree " +"una nueva cuenta de usuario bajo el cual el servicio de GNUnet es arrancado " +"al iniciar el sistema.\n" +"\n" +"Por consiguiente, GNUnet no tiene la posibilidad de acceder a otros ficheros " +"que no sean los que posee. Ésto incluye los ficheros que quieras publicar en " +"GNUnet. Tendrás que garantizar que el usuario especificado aquí posea " +"permisos de lectura.\n" +"\n" +"Deja los campos vacíos para arrancar GNUnet con privilegios de sistema.\n" +"Usuario de GNUnet:" + +#: src/setup/ncurses/wizard_curs.c:410 +msgid "Daemon configuration: group account" +msgstr "" + +#: src/setup/ncurses/wizard_curs.c:411 +msgid "As which group should gnunetd be run?" +msgstr "" + +#: src/setup/ncurses/wizard_curs.c:413 +#, fuzzy +msgid "" +"For security reasons, it is a good idea to let this setup create a new group " +"for the chosen user account.\n" +"\n" +"You can also specify a already existent group here.\n" +"\n" +"Only members of this group will be allowed to start and stop the the GNUnet " +"server and have access to GNUnet server data.\n" +msgstr "" +"Define el grupo bajo el que correrán los servicios de GNUnet aquí.\n" +"\n" +"Por razones de seguridad, es una buena idea dejar que la configuración cree " +"un nuevo grupo para la cuenta de usuario creada.\n" +"\n" +"Puedes especificar un grupo ya existente aquí.\n" +"\n" +"Sólo los miembros de este grupo esta autorizados a arrancar y parar el " +"servidor de GNUnet y tener acceso a los datos del servidor de GNUnet.\n" +"\n" +"Grupo de GNUnet:" + +#: src/setup/ncurses/wizard_curs.c:427 +msgid "Do you want to automatically launch GNUnet as a system service?" +msgstr "" + +#: src/setup/ncurses/wizard_curs.c:429 +#, fuzzy +msgid "" +"If you say \"yes\" here, the GNUnet background process will be automatically " +"started when you turn on your computer. If you say \"no\" here, you have to " +"launch GNUnet yourself each time you want to use it." +msgstr "" +"¿Quieres arrancar GNUnet como un servicio de sistema?\n" +"n\n" +"Si dices \"sí\" aquí, el proceso en segundo plano de GNUnet sera " +"automáticamente arrancado cuando enciendas tu ordenador. Si dices \"no\" " +"aquí, tendrás que ejecutar GNUnet tu mismo cada vez que quieras usarlo." + +#: src/setup/ncurses/wizard_curs.c:452 src/setup/gtk/wizard_gtk.c:414 +#, c-format +msgid "Unable to save configuration file `%s':" +msgstr "Imposible guardar el fichero de configuración '%s':" + +#: src/setup/ncurses/wizard_curs.c:472 +#, fuzzy +msgid "Unable to create user account for daemon." +msgstr "Imposible crear la cuenta de usuario:" + +#: src/setup/ncurses/wizard_curs.c:483 +msgid "Unable to setup autostart for daemon." +msgstr "" + +#: src/setup/ncurses/wizard_curs.c:498 +#, fuzzy +msgid "Save configuration?" +msgstr "Configuración de GNUnet" + +#: src/setup/ncurses/wizard_curs.c:499 +#, fuzzy +msgid "Save configuration now?" +msgstr "Configuración de GNUnet" + +#: src/setup/ncurses/wizard_curs.c:539 src/setup/ncurses/mconf.c:500 +#, fuzzy +msgid "GNUnet Configuration" +msgstr "Configuración de GNUnet" + +#: src/setup/ncurses/wizard_curs.c:543 +msgid "Back" +msgstr "Atrás" + +#: src/setup/ncurses/mconf.c:96 +msgid "Exit" +msgstr "" + +#: src/setup/ncurses/mconf.c:99 +msgid "Up" +msgstr "Arriba" + +#: src/setup/ncurses/mconf.c:102 +msgid "Cancel" +msgstr "Cancelar" + +#: src/setup/ncurses/mconf.c:221 src/setup/ncurses/mconf.c:408 +msgid "Internal error! (Value invalid?)" +msgstr "" + +#: src/setup/ncurses/mconf.c:398 +msgid "Invalid input, expecting floating point value." +msgstr "" + +#: src/setup/ncurses/mconf.c:439 +msgid "Invalid input, expecting integer." +msgstr "" + +#: src/setup/ncurses/mconf.c:446 +msgid "Value is not in legal range." +msgstr "" + +#: src/setup/ncurses/mconf.c:512 src/setup/text/conf.c:569 +#, fuzzy, c-format +msgid "Configuration unchanged, no need to save.\n" +msgstr "" +"La configuración o la versión de GNUnet cambiaron. ¡Debes ejecutar '%s'!\n" + +#: src/setup/ncurses/mconf.c:518 +#, fuzzy +msgid "Do you wish to save your new configuration?" +msgstr "¿Quieres guardar tu configuración?" + +#: src/setup/ncurses/mconf.c:532 +#, fuzzy, c-format +msgid "" +"\n" +"End of configuration.\n" +msgstr " gconfig\tConfiguración GTK\n" + +#: src/setup/ncurses/mconf.c:537 +#, fuzzy, c-format +msgid "" +"\n" +"Your configuration changes were NOT saved.\n" +msgstr "Fichero de configuración '%s' creado.\n" + +#: src/setup/gnunet-win-tool.c:52 +msgid "list all network adapters" +msgstr "" + +#: src/setup/gnunet-win-tool.c:55 +msgid "install GNUnet as Windows service" +msgstr "" + +#: src/setup/gnunet-win-tool.c:58 +msgid "uninstall GNUnet service" +msgstr "" + +#: src/setup/gnunet-win-tool.c:61 +msgid "increase the maximum number of TCP/IP connections" +msgstr "" + +#: src/setup/gnunet-win-tool.c:64 +msgid "display a file's hash value" +msgstr "" + +#: src/setup/gnunet-win-tool.c:125 +#, c-format +msgid "GNUnet service installed successfully.\n" +msgstr "Servicio de GNUnet instalado satisfactoriamente.\n" + +#: src/setup/gnunet-win-tool.c:128 src/setup/gnunet-win-tool.c:156 +#, c-format +msgid "This version of Windows doesn't support services.\n" +msgstr "Esta versión de Windows no soporta servicios.\n" + +#: src/setup/gnunet-win-tool.c:132 src/setup/gnunet-win-tool.c:160 +#, fuzzy, c-format +msgid "Error: can't open Service Control Manager: %s\n" +msgstr "Error: imposible abrir el Service Control Manager: &s\n" + +#: src/setup/gnunet-win-tool.c:137 +#, c-format +msgid "Error: can't create service: %s\n" +msgstr "Error: imposible crear el servicio: %s\n" + +#: src/setup/gnunet-win-tool.c:140 src/setup/gnunet-win-tool.c:172 +#, c-format +msgid "Unknown error.\n" +msgstr "Error desconocido.\n" + +#: src/setup/gnunet-win-tool.c:153 +#, c-format +msgid "Service deleted.\n" +msgstr "Servicio eliminado.\n" + +#: src/setup/gnunet-win-tool.c:165 +#, c-format +msgid "Error: can't access service: %s\n" +msgstr "Error: imposible acceder al servicio: %s\n" + +#: src/setup/gnunet-win-tool.c:169 +#, c-format +msgid "Error: can't delete service: %s\n" +msgstr "Error: imposible borrar el servicio: %s\n" + +#: src/setup/gtk/ngconf.c:389 +#, fuzzy +msgid "Configuration saved." +msgstr "Configuración de GNUnet" + +#: src/setup/gtk/ngconf.c:399 +#, fuzzy +msgid "Failed to save configuration." +msgstr "Imposible guardar la configuración" + +#: src/setup/gtk/ngconf.c:424 +#, fuzzy +msgid "Configuration changed. Save?" +msgstr "" +"La configuración o la versión de GNUnet cambiaron. ¡Debes ejecutar '%s'!\n" + +#: src/setup/gtk/ngconf.c:437 +#, fuzzy +msgid "Error saving configuration." +msgstr "Imposible guardar la configuración" + +#: src/setup/gtk/wizard_gtk.c:141 +#, fuzzy +msgid "(unknown connection)" +msgstr "Conexión de red" + +#: src/setup/gtk/wizard_gtk.c:438 +#, fuzzy +msgid "Do you want to save the new configuration?" +msgstr "¿Quieres guardar tu configuración?" + +#: src/setup/gtk/wizard_gtk.c:470 +msgid "Unable to create user account:" +msgstr "Imposible crear la cuenta de usuario:" + +#: src/setup/gtk/wizard_gtk.c:480 +msgid "Unable to change startup process:" +msgstr "Imposible cambiar el proceso de arranque:" + +#: src/setup/gtk/wizard_gtk.c:495 +msgid "" +"Running gnunet-update failed.\n" +"This maybe due to insufficient permissions, please check your " +"configuration.\n" +"Finally, run gnunet-update manually." +msgstr "" + +#: src/setup/gnunet-setup.c:65 +#, c-format +msgid "Can only set one option per invocation.\n" +msgstr "" + +#: src/setup/gnunet-setup.c:73 +#, c-format +msgid "" +"Invalid syntax, argument to 'set' must have the format SECTION:" +"OPTION=VALUE.\n" +msgstr "" + +#: src/setup/gnunet-setup.c:87 +#, c-format +msgid "Can only display one option per invocation.\n" +msgstr "" + +#: src/setup/gnunet-setup.c:94 +#, c-format +msgid "" +"Invalid syntax, argument to 'get' must have the format SECTION:OPTION.\n" +msgstr "" + +#: src/setup/gnunet-setup.c:108 +msgid "generate configuration for gnunetd, the GNUnet daemon" +msgstr "genera configuración para gnunetd, el demonio de GNUnet" + +#: src/setup/gnunet-setup.c:111 src/setup/gnunet-setup.c:127 +#: src/server/gnunet-update.c:268 +#, fuzzy +msgid "print a value from the configuration file to stdout" +msgstr "Imposible guardar el fichero de configuración '%s':" + +#: src/setup/gnunet-setup.c:113 src/setup/gnunet-setup.c:129 +msgid "Tool to setup GNUnet." +msgstr "Herramienta de configuración de GNUnet." + +#: src/setup/gnunet-setup.c:115 src/setup/gnunet-setup.c:131 +#, fuzzy +msgid "update a value in the configuration file" +msgstr "Imposible guardar el fichero de configuración '%s':" + +#: src/setup/gnunet-setup.c:338 +#, fuzzy, c-format +msgid "Too many arguments.\n" +msgstr "Argumentos en la linea de comandos inválidos.\n" + +#: src/setup/gnunet-setup.c:344 +#, fuzzy +msgid "No interface specified, using default.\n" +msgstr "Ninguna interfaz especificada, usando la marcada por defecto\n" + +#: src/setup/gnunet-setup.c:392 +#, fuzzy, c-format +msgid "Configuration file `%s' must be a filename (but is a directory).\n" +msgstr "" +"El fichero de configuración '%s' no ha sido encontrado. ¡Ejecute gnunet-" +"setup!\n" + +#: src/setup/gnunet-setup.c:439 +#, fuzzy, c-format +msgid "Undefined option.\n" +msgstr "Otras configuraciones" + +#: src/setup/gnunet-setup.c:496 +#, fuzzy, c-format +msgid "`%s' is not available.\n" +msgstr "'%s' no esta disponible." + +#: src/setup/gnunet-setup.c:516 +#, fuzzy, c-format +msgid "Unknown operation '%s'.\n" +msgstr "Operación desconocida '%s'\n" + +#: src/setup/gnunet-setup.c:517 src/util/getopt/getopt.c:1072 +#, c-format +msgid "Use --help to get a list of options.\n" +msgstr "Usar --help para obtener una lista de opciones.\n" + +#: src/setup/text/conf.c:79 +#, fuzzy +msgid "yes" +msgstr "Bytes" + +#: src/setup/text/conf.c:80 +msgid "no" +msgstr "" + +#: src/setup/text/conf.c:106 +#, c-format +msgid "\tEnter yes (%s), no (%s) or help (%s): " +msgstr "" + +#: src/setup/text/conf.c:115 +msgid "\tPossible choices:\n" +msgstr "" + +#: src/setup/text/conf.c:123 +msgid "\tUse single space prefix to avoid conflicts with hotkeys!\n" +msgstr "" + +#: src/setup/text/conf.c:125 +#, c-format +msgid "\tEnter string (type '%s' for default value `%s'): " +msgstr "" + +#: src/setup/text/conf.c:143 +#, c-format +msgid "\t Enter choice (default is %c): " +msgstr "" + +#: src/setup/text/conf.c:147 +#, c-format +msgid "\tEnter floating point (type '%s' for default value %f): " +msgstr "" + +#: src/setup/text/conf.c:153 +#, c-format +msgid "" +"\tEnter unsigned integer in interval [%llu,%llu] (type '%s' for default " +"value %llu): " +msgstr "" + +#: src/setup/text/conf.c:187 +#, c-format +msgid "Yes\n" +msgstr "Sí\n" + +#: src/setup/text/conf.c:192 +#, c-format +msgid "No\n" +msgstr "No\n" + +#: src/setup/text/conf.c:195 src/setup/text/conf.c:236 +#: src/setup/text/conf.c:266 src/setup/text/conf.c:329 +#: src/setup/text/conf.c:387 +#, c-format +msgid "Help\n" +msgstr "Ayuda\n" + +#: src/setup/text/conf.c:198 src/setup/text/conf.c:213 +#: src/setup/text/conf.c:276 src/setup/text/conf.c:307 +#: src/setup/text/conf.c:365 +#, fuzzy, c-format +msgid "Abort\n" +msgstr "_Acerca de" + +#: src/setup/text/conf.c:354 src/setup/text/conf.c:416 +#, c-format +msgid "" +"\n" +"Invalid entry, try again (use '?' for help): " +msgstr "" + +#: src/setup/text/conf.c:422 +#, c-format +msgid "Unknown kind %x (internal error). Skipping option.\n" +msgstr "" + +#: src/setup/text/conf.c:484 +msgid "\tDescend? (y/n/?) " +msgstr "" + +#: src/setup/text/conf.c:493 +msgid "Aborted.\n" +msgstr "" + +#: src/setup/text/conf.c:506 +#, fuzzy +msgid "Invalid entry.\n" +msgstr "Argumento no válido: '%s'\n" + +#: src/setup/text/conf.c:524 +#, c-format +msgid "Unknown kind %x (internal error). Aborting.\n" +msgstr "" + +#: src/setup/text/conf.c:556 +#, c-format +msgid "You can always press ENTER to keep the current value.\n" +msgstr "" + +#: src/setup/text/conf.c:557 +#, c-format +msgid "Use the '%s' key to abort.\n" +msgstr "" + +#: src/setup/text/conf.c:575 +#, c-format +msgid "" +"Save configuration? Answer 'y' for yes, 'n' for no, 'r' to repeat " +"configuration. " +msgstr "" + +#: src/setup/text/conf.c:590 +#, fuzzy, c-format +msgid "Configuration was unchanged, no need to save.\n" +msgstr "" +"La configuración o la versión de GNUnet cambiaron. ¡Debes ejecutar '%s'!\n" + +#: src/setup/text/conf.c:599 +#, fuzzy, c-format +msgid "Configuration file `%s' written.\n" +msgstr "Fichero de configuración '%s' creado.\n" + +#: src/setup/lib/tree.c:191 +#, c-format +msgid "" +"Internal error: entry `%s' in section `%s' not found for visibility change!\n" +msgstr "" + +#: src/setup/lib/wizard_util.c:126 +msgid "Can't open Service Control Manager" +msgstr "Imposible abrir el Service Control Manager" + +#: src/setup/lib/wizard_util.c:132 +msgid "Can't create service" +msgstr "Imposible crear el servicio" + +#: src/setup/lib/wizard_util.c:136 +msgid "Error changing the permissions of the GNUnet directory" +msgstr "Error cambiando los permisos del directorio de GNUnet" + +#: src/setup/lib/wizard_util.c:141 +#, fuzzy +msgid "Cannot write to the registry" +msgstr "Imposible escribir en el registro" + +#: src/setup/lib/wizard_util.c:144 +msgid "Can't access the service" +msgstr "Imposible acceder al servicio" + +#: src/setup/lib/wizard_util.c:147 +msgid "Can't delete the service" +msgstr "Imposible borrar el servicio" + +#: src/setup/lib/wizard_util.c:150 +msgid "Unknown error" +msgstr "Error desconocido" + +#: src/setup/lib/wizard_util.c:186 +msgid "This version of Windows does not support multiple users." +msgstr "Esta versión de Windows no permite usuarios múltiples" + +#: src/setup/lib/wizard_util.c:190 +msgid "Error creating user" +msgstr "Error creando usuario" + +#: src/setup/lib/wizard_util.c:194 +msgid "Error accessing local security policy" +msgstr "Error accediendo a la política de seguridad local" + +#: src/setup/lib/wizard_util.c:199 +msgid "Error granting service right to user" +msgstr "Error garantizando el servicio correcto al usuario" + +#: src/setup/lib/wizard_util.c:204 +msgid "Unknown error while creating a new user" +msgstr "Error desconocido mientras se creaba un nuevo usuario" + +#: src/setup/lib/gns.c:297 +#, fuzzy, c-format +msgid "" +"Configuration does not satisfy constraints of configuration specification " +"file `%s'!\n" +msgstr "" +"La configuración debe especificar un directorio para los datos FS en la " +"sección '%s' bajo'%s'.\n" + +#: src/util/disk/storage.c:172 +#, fuzzy, c-format +msgid "`%s' failed for drive `%s': %u\n" +msgstr "'%s' falló para la unidad %s: %u\n" + +#: src/util/disk/storage.c:524 +#, fuzzy, c-format +msgid "Expected `%s' to be a directory!\n" +msgstr "¡'%s' se esperaba que '%s' fuera un directorio!\n" + +#: src/util/error/error.c:152 +#, c-format +msgid "Message `%.*s' repeated %u times in the last %llus\n" +msgstr "" + +#: src/util/error/error.c:254 +#, c-format +msgid "" +"\n" +"Press any key to continue\n" +msgstr "" + +#: src/util/error/error.c:336 src/util/error/error.c:371 +msgid "DEBUG" +msgstr "DEPURACIÓN" + +#: src/util/error/error.c:338 src/util/error/error.c:373 +msgid "STATUS" +msgstr "" + +#: src/util/error/error.c:340 src/util/error/error.c:377 +msgid "WARNING" +msgstr "PELIGRO" + +#: src/util/error/error.c:342 src/util/error/error.c:379 +msgid "ERROR" +msgstr "ERROR" + +#: src/util/error/error.c:344 src/util/error/error.c:381 +msgid "FATAL" +msgstr "FATAL" + +#: src/util/error/error.c:346 src/util/error/error.c:383 +msgid "USER" +msgstr "" + +#: src/util/error/error.c:348 src/util/error/error.c:385 +msgid "ADMIN" +msgstr "" + +#: src/util/error/error.c:350 src/util/error/error.c:387 +msgid "DEVELOPER" +msgstr "" + +#: src/util/error/error.c:352 src/util/error/error.c:389 +msgid "REQUEST" +msgstr "" + +#: src/util/error/error.c:354 src/util/error/error.c:391 +msgid "BULK" +msgstr "" + +#: src/util/error/error.c:356 src/util/error/error.c:393 +msgid "IMMEDIATE" +msgstr "" + +#: src/util/error/error.c:358 +msgid "ALL" +msgstr "" + +#: src/util/error/error.c:375 +msgid "INFO" +msgstr "INFORMACIÓN" + +#: src/util/error/error.c:394 +msgid "NOTHING" +msgstr "NADA" + +#: src/util/network_client/tcpio.c:98 src/util/network_client/tcpio.c:154 +msgid "Could not find valid value for HOST in section NETWORK.\n" +msgstr "" + +#: src/util/network_client/tcpio.c:123 +#, fuzzy, c-format +msgid "Syntax error in configuration entry HOST in section NETWORK: `%s'\n" +msgstr "" +"Error de sintaxis en el fichero de configuración '%s' en la linea %d.\n" + +#: src/util/network_client/tcpio.c:335 +#, fuzzy, c-format +msgid "Error connecting to %s:%u. Is the daemon running?\n" +msgstr "Imposible conectar a %u.%u.%u.%u:%u: %s\n" + +#: src/util/network_client/tcpio.c:398 +#, fuzzy, c-format +msgid "Cannot connect to %s:%u: %s\n" +msgstr "Imposible conectar a %u.%u.%u.%u:%u: %s\n" + +#: src/util/network_client/tcpio.c:636 +#, fuzzy +msgid "Reading result from gnunetd failed, reply invalid!\n" +msgstr "¡'%s' falló, respuesta no válida!\n" + +#: src/util/getopt/setoption.c:59 +#, c-format +msgid "" +"Setting option `%s' in section `%s' to `%s' when processing command line " +"option `%s' was denied.\n" +msgstr "" + +#: src/util/getopt/setoption.c:138 src/util/getopt/setoption.c:155 +#, c-format +msgid "You must pass a number to the `%s' option.\n" +msgstr "Tienes que introducir un número en la opción '%s'.\n" + +#: src/util/getopt/printhelp.c:49 +#, c-format +msgid "" +"Arguments mandatory for long options are also mandatory for short options.\n" +msgstr "" + +#: src/util/getopt/getopt.c:684 +#, c-format +msgid "%s: option `%s' is ambiguous\n" +msgstr "%s: la opción '%s' es ambigua\n" + +#: src/util/getopt/getopt.c:710 +#, c-format +msgid "%s: option `--%s' does not allow an argument\n" +msgstr "%s: la opción '--%s' no permite un argumento\n" + +#: src/util/getopt/getopt.c:716 +#, c-format +msgid "%s: option `%c%s' does not allow an argument\n" +msgstr "%s: la opción '%c%s' no permite un argumento\n" + +#: src/util/getopt/getopt.c:737 src/util/getopt/getopt.c:909 +#, c-format +msgid "%s: option `%s' requires an argument\n" +msgstr "%s: la opción '%s' requiere un argumento\n" + +#: src/util/getopt/getopt.c:767 +#, c-format +msgid "%s: unrecognized option `--%s'\n" +msgstr "%s: opción no reconocida '--%s'\n" + +#: src/util/getopt/getopt.c:771 +#, c-format +msgid "%s: unrecognized option `%c%s'\n" +msgstr "%s: opción no reconocida '%c%s'\n" + +#: src/util/getopt/getopt.c:797 +#, c-format +msgid "%s: illegal option -- %c\n" +msgstr "%s: opción ilegal -- %c\n" + +#: src/util/getopt/getopt.c:799 +#, c-format +msgid "%s: invalid option -- %c\n" +msgstr "%s: opción no válida -- %c\n" + +#: src/util/getopt/getopt.c:828 src/util/getopt/getopt.c:958 +#, c-format +msgid "%s: option requires an argument -- %c\n" +msgstr "%s: la opción requiere un argumento --%c\n" + +#: src/util/getopt/getopt.c:876 +#, c-format +msgid "%s: option `-W %s' is ambiguous\n" +msgstr "%s: la opción '-W %s' es ambigua\n" + +#: src/util/getopt/getopt.c:894 +#, c-format +msgid "%s: option `-W %s' does not allow an argument\n" +msgstr "%s: la opción '-W %s' no permite un argumento\n" + +#: src/util/network/ip.c:96 src/util/network/ip.c:160 src/transports/ip.c:260 +#, fuzzy, c-format +msgid "No interface specified in section `%s' under `%s'!\n" +msgstr "Ninguna interfaz especificada, usando la marcada por defecto\n" + +#: src/util/network/ip.c:127 src/util/network/ip.c:246 src/transports/ip.c:291 +#, c-format +msgid "Could not obtain IP for interface `%s' using `%s'.\n" +msgstr "Imposible obtener IP para la interfaz '%s' usando '%s'.\n" + +#: src/util/network/ip.c:216 +#, fuzzy, c-format +msgid "" +"Could not find interface `%s' using `%s', trying to find another interface.\n" +msgstr "Imposible obtener IP para la interfaz '%s' usando '%s'.\n" + +#: src/util/network/ip.c:295 +#, c-format +msgid "Could not find an IP address for interface `%s'.\n" +msgstr "Imposible obtener la dirección IP para la interfaz '%s'.\n" + +#: src/util/network/ip.c:306 +#, fuzzy, c-format +msgid "" +"There is more than one IP address specified for interface `%s'.\n" +"GNUnet will use %s.\n" +msgstr "" +"Hay más de una dirección IP especificada para la interfaz '%s'.\n" +"GNUnet usará %u.%u.%u.%u.\n" + +#: src/util/network/ip.c:330 +#, c-format +msgid "Could not resolve `%s' to determine our IP address: %s\n" +msgstr "Imposible resolver '%s' para determinar nuestra dirección IP: %s\n" + +#: src/util/network/ip.c:363 +#, fuzzy, c-format +msgid "GNUnet now uses the IP address %s.\n" +msgstr "GNUnet usa ahora la dirección IP %u.%u.%u.%u.\n" + +#: src/util/network/ipcheck.c:106 src/util/network/ipcheck.c:136 +#: src/util/network/ipcheck.c:186 src/util/network/ipcheck.c:211 +#: src/util/network/ipcheck.c:219 +#, c-format +msgid "Invalid format for IP: `%s'\n" +msgstr "Formato no válido para la IP: '%s'\n" + +#: src/util/network/ipcheck.c:167 +#, c-format +msgid "Invalid network notation ('/%d' is not legal in IPv4 CIDR)." +msgstr "Notación de red no válida ('/%d' no es válido en IPv4 CIDR)." + +#: src/util/network/ipcheck.c:269 +#, c-format +msgid "Invalid network notation (does not end with ';': `%s')\n" +msgstr "Notación de red no válida (no termina con ';': '%s')\n" + +#: src/util/network/ipcheck.c:306 +#, fuzzy, c-format +msgid "Wrong format `%s' for netmask\n" +msgstr "Formato '%s' erróneo para la máscara de red: %s\n" + +#: src/util/network/ipcheck.c:338 +#, fuzzy, c-format +msgid "Wrong format `%s' for network\n" +msgstr "Formato '%s' erróneo para la red: %s\n" + +#: src/util/network/dns.c:472 +#, fuzzy, c-format +msgid "Could not resolve `%s' (%s): %s\n" +msgstr "Imposible resolver '%s': %s\n" + +#: src/util/network/dns.c:523 src/util/network/dns.c:591 +#, c-format +msgid "Could not find IP of host `%s': %s\n" +msgstr "Imposible encontrar la IP del host '%s': %s\n" + +#: src/util/network/select.c:310 +#, fuzzy +msgid "Received malformed message (too small) from connection. Closing.\n" +msgstr "Recibida respuesta anómala a'%s' del par '%s'.\n" + +#: src/util/network/select.c:495 +#, c-format +msgid "select listen socket for `%s' not valid!\n" +msgstr "" + +#: src/util/config/config.c:296 +#, c-format +msgid "Syntax error in configuration file `%s' at line %d.\n" +msgstr "" +"Error de sintaxis en el fichero de configuración '%s' en la linea %d.\n" + +#: src/util/config/config.c:592 +#, c-format +msgid "" +"Configuration value '%llu' for '%s' in section '%s' is out of legal bounds [%" +"llu,%llu]\n" +msgstr "" + +#: src/util/config/config.c:602 +#, c-format +msgid "Configuration value '%s' for '%s' in section '%s' should be a number\n" +msgstr "" + +#: src/util/config/config.c:688 +#, c-format +msgid "" +"Configuration value '%s' for '%s' in section '%s' is not in set of legal " +"choices\n" +msgstr "" + +#: src/util/crypto/locking_gcrypt.c:80 +#, c-format +msgid "libgcrypt has not the expected version (version %s is required).\n" +msgstr "" +"libgcrypt no está en la versión esperada (se necesita la versión %s).\n" + +#: src/util/crypto/symcipher_gcrypt.c:46 src/util/crypto/symcipher_gcrypt.c:53 +#: src/util/crypto/hostkey_gcrypt.c:64 src/util/crypto/hostkey_gcrypt.c:71 +#: src/util/loggers/file.c:271 src/util/loggers/file.c:289 +#: src/applications/sqstore_sqlite/sqlite.c:45 +#: src/applications/sqstore_sqlite/sqlite.c:52 +#: src/applications/kvstore_sqlite/kv_sqlite.c:44 +#: src/applications/kvstore_sqlite/kv_sqlite.c:51 +#: src/applications/kvstore_mysql/kv_mysql.c:44 +#: src/applications/kvstore_mysql/kv_mysql.c:51 +#: src/applications/dstore_sqlite/dstore.c:94 +#: src/applications/dstore_sqlite/dstore.c:101 +#: src/applications/dstore_sqlite/dstore.c:222 +#: src/applications/dstore_sqlite/dstore.c:259 +#: src/applications/dstore_sqlite/dstore.c:285 +#: src/applications/dstore_sqlite/dstore.c:345 +#: src/applications/dstore_sqlite/dstore.c:366 +#: src/applications/dstore_sqlite/dstore.c:378 +#: src/applications/dstore_sqlite/dstore.c:407 +#: src/applications/dstore_sqlite/dstore.c:511 +#: src/applications/dstore_sqlite/dstore.c:555 +#: src/include/gnunet_util_error.h:249 src/include/gnunet_util_error.h:256 +#: src/include/gnunet_util_error.h:263 +#, c-format +msgid "`%s' failed at %s:%d with error: %s\n" +msgstr "'%s' falló en %s: %d con el error: %s\n" + +#: src/util/crypto/hostkey_gcrypt.c:907 +#, c-format +msgid "RSA signature verification failed at %s:%d: %s\n" +msgstr "La verificación de la firma RSA fallo en %s: %d: %s\n" + +#: src/util/os/user.c:108 src/util/os/user.c:125 +#, fuzzy, c-format +msgid "`%s' returned with error code %u" +msgstr "'%s' falló con el código de error %s: %s" + +#: src/util/os/user.c:155 src/util/os/user.c:200 +#, fuzzy, c-format +msgid "Cannot obtain information about user `%s': %s\n" +msgstr "Imposible guardar el fichero de configuración '%s': %s.\n" + +#: src/util/os/user.c:156 +msgid "No such user" +msgstr "" + +#: src/util/os/user.c:171 +#, c-format +msgid "Cannot change user/group to `%s': %s\n" +msgstr "Imposible cambiar el usuario/grupo a '%s': %s\n" + +#: src/util/os/semaphore.c:227 +#, c-format +msgid "Can't create semaphore: %i" +msgstr "Imposible crear un semáforo: %i" + +#: src/util/os/cpustatus.c:464 +msgid "Cannot query the CPU usage (Windows NT).\n" +msgstr "" + +#: src/util/os/cpustatus.c:487 +msgid "Cannot query the CPU usage (Win 9x)\n" +msgstr "" + +#: src/util/os/dso.c:59 +#, c-format +msgid "Initialization of plugin mechanism failed: %s!\n" +msgstr "¡El mecanismo de iniciación de los plugins falló: %s!\n" + +#: src/util/os/dso.c:120 +#, fuzzy, c-format +msgid "`%s' failed for library `%s' with error: %s\n" +msgstr "'%s' falló en la biblioteca '%s' en %s:%d con un error: %s\n" + +#: src/util/os/dso.c:162 +#, fuzzy, c-format +msgid "`%s' failed to resolve method '%s' with error: %s\n" +msgstr "'%s' falló al resolver el método '%s%s' en %s:%d con un error: %s\n" + +#: src/util/os/statuscalls.c:197 src/util/os/statuscalls.c:342 +#, fuzzy, c-format +msgid "Failed to parse interface data from `%s'.\n" +msgstr "Falló al pasar los datos de la interfaz de '%s' de %s:%d.\n" + +#: src/util/os/statuscalls.c:390 src/util/os/statuscalls.c:400 +#, c-format +msgid "" +"No network interfaces defined in configuration section `%s' under `%s'!\n" +msgstr "" + +#: src/util/os/osconfig.c:153 +msgid "Setting open descriptor limit not supported.\n" +msgstr "" + +#: src/util/os/osconfig.c:463 src/util/os/osconfig.c:492 +#, fuzzy, c-format +msgid "Command `%s' failed with error code %u\n" +msgstr "'%s' falló con el código de error %s: %s" + +#: src/util/os/priority.c:78 +#, fuzzy, c-format +msgid "Invalid process priority `%s'\n" +msgstr "Respuesta inválida a '%s'.\n" + +#: src/util/threads/semaphore.c:168 src/util/threads/pthread.c:157 +#: src/util/threads/mutex.c:146 +#, c-format +msgid "Real-time delay violation (%llu ms) at %s:%u\n" +msgstr "" + +#: src/util/threads/pthread.c:169 src/util/threads/pthread.c:176 +#: src/util/threads/pthread.c:182 src/util/threads/pthread.c:276 +#, fuzzy, c-format +msgid "`%s' failed with error code %s: %s\n" +msgstr "'%s' falló con el código de error %s: %s" + +#: src/util/threads/pthread.c:188 src/util/threads/pthread.c:286 +#, fuzzy, c-format +msgid "`%s' failed with error code %d: %s\n" +msgstr "'%s' falló con el código de error %d: %s" + +#: src/util/threads/mutex.c:155 src/util/threads/mutex.c:201 +#, fuzzy, c-format +msgid "Invalid argument for `%s'.\n" +msgstr "Argumento no válido: '%s'\n" + +#: src/util/threads/mutex.c:160 +#, c-format +msgid "Deadlock due to `%s'.\n" +msgstr "" + +#: src/util/threads/mutex.c:187 +#, c-format +msgid "Lock acquired for too long (%llu ms) at %s:%u\n" +msgstr "" + +#: src/util/threads/mutex.c:207 +#, fuzzy, c-format +msgid "Permission denied for `%s'.\n" +msgstr "Permiso denegado para '%s' en %s:%d.\n" + +#: src/util/boot/startup.c:259 +#, fuzzy, c-format +msgid "Failed to run %s: %s %d\n" +msgstr "Fallo al conectar a gnunetd.\n" + +#: src/util/string/string.c:55 +msgid "ms" +msgstr "ms" + +#: src/util/string/string.c:61 +msgid "s" +msgstr "s" + +#: src/util/string/string.c:65 +msgid "m" +msgstr "m" + +#: src/util/string/string.c:69 +msgid "h" +msgstr "h" + +#: src/util/string/string.c:73 +msgid " days" +msgstr " días" + +#: src/util/string/string.c:89 +msgid "b" +msgstr "b" + +#: src/util/string/string.c:95 +msgid "KiB" +msgstr "KiB" + +#: src/util/string/string.c:99 +msgid "MiB" +msgstr "MiB" + +#: src/util/string/string.c:103 +msgid "GiB" +msgstr "GiB" + +#: src/util/string/string.c:107 +msgid "TiB" +msgstr "TiB" + +#: src/util/string/string.c:226 +msgid "Failed to expand `$HOME': environment variable `HOME' not set" +msgstr "" + +#: src/util/loggers/file.c:229 +#, fuzzy, c-format +msgid "Failed to open log-file `%s': %s\n" +msgstr "Fichero almacenado en '%s'.\n" + +#: src/util/loggers/file.c:250 +msgid "GNUnet error log" +msgstr "" + +#: src/util/loggers/memory.c:72 +msgid "Out of memory (for logging)\n" +msgstr "" + +#: src/util/pseudonym/names.c:79 +#, fuzzy +msgid "no-name" +msgstr "Mostrar el nombre" + +#: src/applications/datastore/datastore.c:183 +#: src/applications/datastore/datastore.c:199 +#, c-format +msgid "Availability test failed for `%s' at %s:%d.\n" +msgstr "Test de disponibilidad fallido para '%s' en %s:%d.\n" + +#: src/applications/datastore/datastore.c:401 +msgid "# requests filtered by bloom filter" +msgstr "" + +#: src/applications/datastore/datastore.c:403 +msgid "# bloom filter false positives" +msgstr "" + +#: src/applications/datastore/datastore.c:406 +#, fuzzy +msgid "# bytes allowed in datastore" +msgstr "# bytes en la base de datos" + +#: src/applications/datastore/datastore.c:423 +msgid "Failed to load state service. Trying to do without.\n" +msgstr "" + +#: src/applications/datastore/datastore.c:529 +#, c-format +msgid "Datastore conversion at approximately %u%%\n" +msgstr "" + +#: src/applications/datastore/datastore.c:576 +#, fuzzy, c-format +msgid "Starting datastore conversion (this may take a while).\n" +msgstr "Creando nueva clave local (esto puede llevar un tiempo).\n" + +#: src/applications/datastore/datastore.c:584 +#, c-format +msgid "Completed datastore conversion.\n" +msgstr "" + +#: src/applications/datastore/datastore.c:592 +#, fuzzy +msgid "Failed to load sqstore service. Check your configuration!\n" +msgstr "Imposible guardar la configuración" + +#: src/applications/rpc/rpc.c:339 +#, fuzzy, c-format +msgid "" +"%s:%d - RPC %s:%p could not be registered: another callback is already using " +"this name (%p)\n" +msgstr "" +"%s::%s - RPC %s:%p no pudo ser registrada: otro evento está actualmente " +"usando este nombre (%p)\n" + +#: src/applications/rpc/rpc.c:398 +#, fuzzy, c-format +msgid "%s:%d - async RPC %s:%p could not be unregistered: not found\n" +msgstr "%s::%s RPC asíncrona %s:%p no pudo ser desregistrada: no encontrada\n" + +#: src/applications/rpc/rpc.c:951 +#, c-format +msgid "`%s' registering handlers %d %d %d\n" +msgstr "'%s' registrando manejadores %d %d %d\n" + +#: src/applications/rpc/rpc.c:972 +#, c-format +msgid "Failed to initialize `%s' service.\n" +msgstr "Falló al inicializar el servicio '%s'.\n" + +#: src/applications/tbench/tbenchtest.c:53 +#, c-format +msgid "Using %u messages of size %u for %u times.\n" +msgstr "Usando %u mensajes de un tamaño de %u durante %u veces.\n" + +#: src/applications/tbench/tbenchtest.c:85 +#, c-format +msgid "Times: max %16llu min %16llu mean %12.3f variance %12.3f\n" +msgstr "" + +#: src/applications/tbench/tbenchtest.c:89 +#, c-format +msgid "Loss: max %16u min %16u mean %12.3f variance %12.3f\n" +msgstr "" + +#: src/applications/tbench/tbenchtest.c:95 +#, c-format +msgid "" +"\n" +"Failed to receive reply from gnunetd.\n" +msgstr "" +"\n" +"Fallo al recibir la respuesta de gnunetd.\n" + +#: src/applications/tbench/tbenchtest.c:149 +#, c-format +msgid "Running benchmark...\n" +msgstr "" + +#: src/applications/tbench/tbench.c:422 +msgid "allows profiling of direct peer-to-peer connections" +msgstr "" + +#: src/applications/tbench/gnunet-tbench.c:63 +#: src/applications/tracekit/gnunet-tracekit.c:302 +msgid "Start GNUnet transport benchmarking tool." +msgstr "" + +#: src/applications/tbench/gnunet-tbench.c:65 +msgid "output in gnuplot format" +msgstr "salida en formato de gnuplot" + +#: src/applications/tbench/gnunet-tbench.c:69 +msgid "number of iterations" +msgstr "número de repeticiones" + +#: src/applications/tbench/gnunet-tbench.c:73 +msgid "number of messages to use per iteration" +msgstr "número de mensajes a usar por iteración" + +#: src/applications/tbench/gnunet-tbench.c:76 +msgid "receiver host identifier (ENC file name)" +msgstr "" + +#: src/applications/tbench/gnunet-tbench.c:79 +msgid "message size" +msgstr "tamaño del mensaje" + +#: src/applications/tbench/gnunet-tbench.c:82 +msgid "sleep for SPACE ms after each a message block" +msgstr "duerme durante SPACE ms después de cada bloque de mensajes" + +#: src/applications/tbench/gnunet-tbench.c:85 +msgid "time to wait for the completion of an iteration (in ms)" +msgstr "tiempo para esperar hasta completar una iteración (en ms)" + +#: src/applications/tbench/gnunet-tbench.c:90 +msgid "number of messages in a message block" +msgstr "número de mensajes en un bloque de mensajes" + +#: src/applications/tbench/gnunet-tbench.c:126 +#: src/applications/tracekit/gnunet-tracekit.c:352 +#: src/applications/tracekit/tracekittest.c:133 +#: src/applications/template/gnunet-template.c:95 +#: src/applications/stats/gnunet-stats.c:121 +#: src/applications/vpn/gnunet-vpn.c:154 +#, c-format +msgid "Error establishing connection with gnunetd.\n" +msgstr "Se produjo un error estableciendo conexión con gnunetd.\n" + +#: src/applications/tbench/gnunet-tbench.c:142 +#, c-format +msgid "You must specify a receiver!\n" +msgstr "¡Debes especificar un receptor!\n" + +#: src/applications/tbench/gnunet-tbench.c:152 +#, c-format +msgid "Invalid receiver peer ID specified (`%s' is not valid name).\n" +msgstr "" +"ID del par receptor especificada no válida ('%s' no es un nombre válido).\n" + +#: src/applications/tbench/gnunet-tbench.c:189 +#, c-format +msgid "Time:\n" +msgstr "Tiempo:\n" + +#: src/applications/tbench/gnunet-tbench.c:190 +#, c-format +msgid "\tmax %llums\n" +msgstr "\tmax %llums\n" + +#: src/applications/tbench/gnunet-tbench.c:191 +#, c-format +msgid "\tmin %llums\n" +msgstr "\tmin %llums\n" + +#: src/applications/tbench/gnunet-tbench.c:192 +#, c-format +msgid "\tmean %8.4fms\n" +msgstr "\tmean %8.4fms\n" + +#: src/applications/tbench/gnunet-tbench.c:193 +#, c-format +msgid "\tvariance %8.4fms\n" +msgstr "\tvariance %8.4fms\n" + +#: src/applications/tbench/gnunet-tbench.c:195 +#, c-format +msgid "Loss:\n" +msgstr "Perdido:\n" + +#: src/applications/tbench/gnunet-tbench.c:196 +#, c-format +msgid "\tmax %u\n" +msgstr "\tmax %u\n" + +#: src/applications/tbench/gnunet-tbench.c:197 +#, c-format +msgid "\tmin %u\n" +msgstr "\tmin %u\n" + +#: src/applications/tbench/gnunet-tbench.c:198 +#, c-format +msgid "\tmean %8.4f\n" +msgstr "\tmean %8.4f\n" + +#: src/applications/tbench/gnunet-tbench.c:199 +#, c-format +msgid "\tvariance %8.4f\n" +msgstr "\tvariance %8.4f\n" + +#: src/applications/tbench/gnunet-tbench.c:205 +#, c-format +msgid "Output format not known, this should not happen.\n" +msgstr "El formato de salida es desconocido, ésto no debería pasar.\n" + +#: src/applications/tbench/gnunet-tbench.c:211 +#, c-format +msgid "" +"\n" +"Did not receive the message from gnunetd. Is gnunetd running?\n" +msgstr "" +"\n" +"No se recibió el mensaje de gnunetd. ¿Está gnunetd ejecutandose?\n" + +#: src/applications/traffic/traffic.c:454 +#, c-format +msgid "# bytes transmitted of type %d" +msgstr "" + +#: src/applications/traffic/traffic.c:470 +#, c-format +msgid "# bytes received of type %d" +msgstr "" + +#: src/applications/traffic/traffic.c:489 +#, fuzzy, c-format +msgid "# bytes received in plaintext of type %d" +msgstr "# bytes recibidos por TCP" + +#: src/applications/traffic/traffic.c:652 +msgid "tracks bandwidth utilization by gnunetd" +msgstr "" + +#: src/applications/sqstore_sqlite/sqlite.c:218 +#, fuzzy, c-format +msgid "Unable to initialize SQLite: %s.\n" +msgstr "Imposible inicializar SQLite.\n" + +#: src/applications/sqstore_sqlite/sqlite.c:434 +#: src/applications/sqstore_sqlite/sqlite.c:469 +#, c-format +msgid "Invalid data in %s. Trying to fix (by deletion).\n" +msgstr "Datos no válidos en %s. Intentando fijar (por borrado).\n" + +#: src/applications/sqstore_sqlite/sqlite.c:435 +#: src/applications/sqstore_sqlite/sqlite.c:470 +msgid "sqlite datastore" +msgstr "base de datos sqlite" + +#: src/applications/sqstore_sqlite/sqlite.c:1474 +#: src/applications/sqstore_mysql/mysql.c:1078 +msgid "# bytes in datastore" +msgstr "# bytes en la base de datos" + +#: src/applications/sqstore_sqlite/sqlite.c:1476 +#, fuzzy +msgid "# bytes allocated by SQLite" +msgstr "# bytes en la base de datos" + +#: src/applications/sqstore_mysql/mysql.c:1085 +#: src/applications/sqstore_mysql/mysql.c:1160 +msgid "" +"Failed to load MySQL database module. Check that MySQL is running and " +"configured properly!\n" +msgstr "" +"Falló al cargar el modulo de la base de datos MySQL. ¡Comprueba que MySQL " +"esta ejecutandose y esta configurada correctamente!\n" + +#: src/applications/tracekit/gnunet-tracekit.c:104 +#, c-format +msgid "`%s' connected to `%s'.\n" +msgstr "'%s' conectado a '%s'.\n" + +#: src/applications/tracekit/gnunet-tracekit.c:110 +#: src/applications/tracekit/tracekittest.c:67 +#, c-format +msgid "`%s' is not connected to any peer.\n" +msgstr "'%s' no esta conectado a ningún par.\n" + +#: src/applications/tracekit/gnunet-tracekit.c:295 +msgid "probe network to the given DEPTH" +msgstr "" + +#: src/applications/tracekit/gnunet-tracekit.c:299 +msgid "" +"specify output format; 0 for human readable output, 1 for dot, 2 for vcg" +msgstr "" + +#: src/applications/tracekit/gnunet-tracekit.c:306 +#, fuzzy +msgid "use PRIORITY for the priority of the trace request" +msgstr "especifica la prioridad del contenido" + +#: src/applications/tracekit/gnunet-tracekit.c:310 +msgid "wait DELAY seconds for replies" +msgstr "" + +#: src/applications/tracekit/gnunet-tracekit.c:344 +#, c-format +msgid "" +"Format specification invalid. Use 0 for user-readable, 1 for dot, 2 for " +"vcg.\n" +msgstr "" + +#: src/applications/tracekit/tracekittest.c:60 +#, fuzzy, c-format +msgid "`%.*s' connected to `%.*s'.\n" +msgstr "'%s' conectado a '%s'.\n" + +#: src/applications/tracekit/tracekit.c:440 +msgid "allows mapping of the network topology" +msgstr "" + +#: src/applications/advertising/advertising_test.c:47 +#: src/applications/hostlist/hostlisttest.c:40 +#: src/applications/session/sessiontest.c:40 +#: src/applications/session/sessiontest_nat_http.c:40 +#: src/applications/session/sessiontest_nat.c:40 +#: src/applications/stats/statistics.c:247 +msgid "# of connected peers" +msgstr "# de pares conectados" + +#: src/applications/advertising/advertising.c:194 +#, fuzzy, c-format +msgid "HELLO message from `%s' has an invalid signature. Dropping.\n" +msgstr "mensaje de saludo de '%s' inválido (firma inválida). Omitiendo.\n" + +#: src/applications/advertising/advertising.c:205 +#, fuzzy +msgid "HELLO message has expiration too far in the future. Dropping.\n" +msgstr "" +"mensaje de saludo recibido inválido (tiempo superior al límite). Omitiendo.\n" + +#: src/applications/advertising/advertising.c:406 +#, fuzzy +msgid "Could not send HELLO+PING, ping buffer full.\n" +msgstr "Imposible mandar ahora saludos+PING, buffer del ping lleno.\n" + +#: src/applications/advertising/advertising.c:429 +msgid "Failed to create an advertisement for this peer. Will not send PING.\n" +msgstr "" + +#: src/applications/advertising/advertising.c:581 +#, c-format +msgid "Advertising my transport %d to selected peers.\n" +msgstr "Anunciando mi transporte %d a los pares seleccionados.\n" + +#: src/applications/advertising/advertising.c:590 +msgid "" +"Announcing ourselves pointless: no other peers are known to us so far.\n" +msgstr "Es inútil anunciarnos: no hay más pares que nos conozcan más allá.\n" + +#: src/applications/advertising/advertising.c:868 +msgid "# Peer advertisements received" +msgstr "# Anuncios de los pares recibidos" + +#: src/applications/advertising/advertising.c:871 +#, fuzzy +msgid "# Peer advertisements of type NAT received" +msgstr "# Anuncios de los pares recibidos" + +#: src/applications/advertising/advertising.c:874 +#, fuzzy +msgid "# Peer advertisements confirmed via PONG" +msgstr "# Anuncios de los pares recibidos" + +#: src/applications/advertising/advertising.c:877 +#, fuzzy +msgid "# Peer advertisements updating earlier HELLOs" +msgstr "# Anuncios de los pares recibidos" + +#: src/applications/advertising/advertising.c:880 +#, fuzzy +msgid "# Peer advertisements discarded due to load" +msgstr "# Anuncios de los pares recibidos" + +#: src/applications/advertising/advertising.c:883 +#, fuzzy +msgid "# Peer advertisements for unsupported transport" +msgstr "# Anuncios de los pares recibidos" + +#: src/applications/advertising/advertising.c:886 +#, fuzzy +msgid "# Peer advertisements not confirmed due to ping busy" +msgstr "# Anuncios de los pares recibidos" + +#: src/applications/advertising/advertising.c:889 +#, fuzzy +msgid "# Peer advertisements not confirmed due to lack of self ad" +msgstr "# Anuncios de los pares recibidos" + +#: src/applications/advertising/advertising.c:892 +#, fuzzy +msgid "# Peer advertisements not confirmed due to send error" +msgstr "# Anuncios de los pares recibidos" + +#: src/applications/advertising/advertising.c:894 +msgid "# Self advertisments transmitted" +msgstr "# Auto-anuncios transmitidos" + +#: src/applications/advertising/advertising.c:896 +msgid "# Foreign advertisements forwarded" +msgstr "# Anuncios a extraños mandados" + +#: src/applications/advertising/advertising.c:898 +#: src/applications/pingpong/pingpong.c:528 +msgid "# plaintext PING messages sent" +msgstr "# mensajes de texto mandados por PING" + +#: src/applications/advertising/advertising.c:904 +#: src/applications/session/connect.c:932 +#, c-format +msgid "`%s' registering handler %d (plaintext and ciphertext)\n" +msgstr "'%s' registrando manejador %d (texto plano e hipertexto)\n" + +#: src/applications/advertising/advertising.c:922 +msgid "" +"ensures that this peer is known by other peers and discovers other peers" +msgstr "" +"Asegura que este par es conocido por otros pares y descubre otros pares" + +#: src/applications/fragmentation/fragmentation.c:578 +msgid "# messages defragmented" +msgstr "# mensajes defragmentados" + +#: src/applications/fragmentation/fragmentation.c:580 +msgid "# messages fragmented" +msgstr "# mensajes fragmentados" + +#: src/applications/fragmentation/fragmentation.c:581 +msgid "# fragments discarded" +msgstr "# fragmentos descartados" + +#: src/applications/fragmentation/fragmentation.c:592 +#, c-format +msgid "`%s' registering handler %d\n" +msgstr "'%s' registrando manejador %d\n" + +#: src/applications/topology_default/topology.c:466 +#, fuzzy, c-format +msgid "Could not read friends list `%s'\n" +msgstr "Imposible inicializar la aplicación '%s'\n" + +#: src/applications/topology_default/topology.c:485 +#, c-format +msgid "Failed to read friends list from `%s'\n" +msgstr "" + +#: src/applications/topology_default/topology.c:505 +msgid "Syntax error in topology specification, skipping bytes.\n" +msgstr "" + +#: src/applications/topology_default/topology.c:523 +#, c-format +msgid "Syntax error in topology specification, skipping bytes `%s'.\n" +msgstr "" + +#: src/applications/topology_default/topology.c:535 +msgid "" +"Fewer friends specified than required by minimum friend count. Will only " +"connect to friends.\n" +msgstr "" + +#: src/applications/topology_default/topology.c:543 +msgid "" +"More friendly connections required than target total number of connections.\n" +msgstr "" + +#: src/applications/topology_default/topology.c:726 +msgid "maintains GNUnet default mesh topology" +msgstr "" + +#: src/applications/chat/tools/gnunet-chat.c:107 +msgid "anonymous" +msgstr "" + +#: src/applications/chat/tools/gnunet-chat.c:113 +#, fuzzy, c-format +msgid "`%s' said: %s\n" +msgstr "'%s' %s falló: %s\n" + +#: src/applications/chat/tools/gnunet-chat.c:116 +#: src/applications/chat/tools/gnunet-chat.c:119 +#, fuzzy, c-format +msgid "`%s' said to you: %s\n" +msgstr "'%s' %s falló: %s\n" + +#: src/applications/chat/tools/gnunet-chat.c:122 +#, fuzzy, c-format +msgid "`%s' said for sure: %s\n" +msgstr "'%s' %s falló: %s\n" + +#: src/applications/chat/tools/gnunet-chat.c:125 +#, fuzzy, c-format +msgid "`%s' said to you for sure: %s\n" +msgstr "'%s' falló con el código de error %s: %s" + +#: src/applications/chat/tools/gnunet-chat.c:128 +#, fuzzy, c-format +msgid "`%s' was confirmed that you received: %s\n" +msgstr "'%s' falló con el código de error %d: %s" + +#: src/applications/chat/tools/gnunet-chat.c:131 +#, c-format +msgid "`%s' was confirmed that you and only you received: %s\n" +msgstr "" + +#: src/applications/chat/tools/gnunet-chat.c:134 +#, c-format +msgid "`%s' was confirmed that you received from him or her: %s\n" +msgstr "" + +#: src/applications/chat/tools/gnunet-chat.c:139 +#, c-format +msgid "`%s' was confirmed that you and only you received from him or her: %s\n" +msgstr "" + +#: src/applications/chat/tools/gnunet-chat.c:142 +#, fuzzy, c-format +msgid "`%s' said off the record: %s\n" +msgstr "'%s' falló con el código de error %s: %s" + +#: src/applications/chat/tools/gnunet-chat.c:145 +#, c-format +msgid "<%s> said using an unknown message type: %s\n" +msgstr "" + +#: src/applications/chat/tools/gnunet-chat.c:167 +#, c-format +msgid "`%s' entered the room\n" +msgstr "" + +#: src/applications/chat/tools/gnunet-chat.c:167 +#, c-format +msgid "`%s' left the room\n" +msgstr "" + +#: src/applications/chat/tools/gnunet-chat.c:239 +#: src/applications/chat/tools/gnunet-chat.c:348 +#, fuzzy, c-format +msgid "Failed to send message.\n" +msgstr "Falló al entregar el mensaje '%s'.\n" + +#: src/applications/chat/tools/gnunet-chat.c:265 +#: src/applications/chat/tools/gnunet-chat.c:524 +#, fuzzy, c-format +msgid "Joined room `%s' as user `%s'.\n" +msgstr "Respuesta inválida a '%s' del par '%s'.\n" + +#: src/applications/chat/tools/gnunet-chat.c:293 +#, fuzzy, c-format +msgid "Changed username to `%s'.\n" +msgstr "Imposible cambiar el usuario/grupo a '%s': %s\n" + +#: src/applications/chat/tools/gnunet-chat.c:301 +#, fuzzy, c-format +msgid "Unknown command `%s'.\n" +msgstr "Operación desconocida '%s'\n" + +#: src/applications/chat/tools/gnunet-chat.c:316 +#, c-format +msgid "Syntax: /msg USERNAME MESSAGE" +msgstr "" + +#: src/applications/chat/tools/gnunet-chat.c:324 +#, fuzzy, c-format +msgid "Unknown user `%s'\n" +msgstr "Operación desconocida '%s'\n" + +#: src/applications/chat/tools/gnunet-chat.c:339 +#, c-format +msgid "User `%s' is currently not in the room!\n" +msgstr "" + +#: src/applications/chat/tools/gnunet-chat.c:361 +#, fuzzy, c-format +msgid "Users in room `%s': " +msgstr "Fichero almacenado en '%s'.\n" + +#: src/applications/chat/tools/gnunet-chat.c:390 +msgid "" +"Use `/join #roomname' to join a chat room. Joining a room will cause you to " +"leave the current room" +msgstr "" + +#: src/applications/chat/tools/gnunet-chat.c:393 +msgid "" +"Use `/nick nickname' to change your nickname. This will cause you to leave " +"the current room and immediately rejoin it with the new name." +msgstr "" + +#: src/applications/chat/tools/gnunet-chat.c:396 +msgid "" +"Use `/msg nickname message' to send a private message to the specified user" +msgstr "" + +#: src/applications/chat/tools/gnunet-chat.c:398 +msgid "The `/notice' command is an alias for `/msg'" +msgstr "" + +#: src/applications/chat/tools/gnunet-chat.c:400 +msgid "The `/query' command is an alias for `/msg'" +msgstr "" + +#: src/applications/chat/tools/gnunet-chat.c:402 +msgid "Use `/quit' to terminate gnunet-chat" +msgstr "" + +#: src/applications/chat/tools/gnunet-chat.c:404 +msgid "The `/leave' command is an alias for `/quit'" +msgstr "" + +#: src/applications/chat/tools/gnunet-chat.c:407 +msgid "Use `/names' to list all of the current members in the chat room" +msgstr "" + +#: src/applications/chat/tools/gnunet-chat.c:409 +msgid "Use `/help command' to get help for a specific command" +msgstr "" + +#: src/applications/chat/tools/gnunet-chat.c:457 +msgid "Join a chat on GNUnet." +msgstr "" + +#: src/applications/chat/tools/gnunet-chat.c:461 +msgid "set the nickname to use (required)" +msgstr "" + +#: src/applications/chat/tools/gnunet-chat.c:464 +msgid "set the chat room to join" +msgstr "" + +#: src/applications/chat/tools/gnunet-chat.c:495 +#, fuzzy, c-format +msgid "You must specify a nickname\n" +msgstr "¡Debes especificar un receptor!\n" + +#: src/applications/chat/tools/gnunet-chat.c:515 +#, fuzzy, c-format +msgid "Failed to join room `%s'\n" +msgstr "Fichero almacenado en '%s'.\n" + +#: src/applications/chat/module/chat.c:325 +#, fuzzy, c-format +msgid "`%s' registering CS handlers %d and %d\n" +msgstr "'%s' registrando manejadores %d y %d\n" + +#: src/applications/chat/module/chat.c:347 +msgid "enables P2P-chat (incomplete)" +msgstr "Activa el chat P2P (incompleto)" + +#: src/applications/chat/lib/messaging.c:353 +#: src/applications/identity/hostkey.c:122 +#, fuzzy, c-format +msgid "Failed to access GNUnet home directory `%s'\n" +msgstr "Error en el formato del fichero (¿no es un directorio de GNUnet?)\n" + +#: src/applications/chat/lib/messaging.c:389 +#, fuzzy, c-format +msgid "Existing key in file `%s' failed format check, creating new key.\n" +msgstr "" +"Se produjo un fallo al comprobar la clave local existente en el fichero '%" +"s', creando nueva clave local.\n" + +#: src/applications/chat/lib/messaging.c:399 +#, fuzzy +msgid "Creating new key for this nickname (this may take a while).\n" +msgstr "Creando nueva clave local (esto puede llevar un tiempo).\n" + +#: src/applications/chat/lib/messaging.c:411 +#, fuzzy +msgid "Done creating key.\n" +msgstr "Correcto al crear la clave local.\n" + +#: src/applications/dstore_mysql/dstore_mysql.c:471 +#, fuzzy +msgid "Failed to initialize MySQL database connection for dstore.\n" +msgstr "Imposible inicializar SQLite.\n" + +#: src/applications/dstore_mysql/dstore_mysql.c:494 +#: src/applications/dstore_sqlite/dstore.c:636 +#, fuzzy +msgid "# bytes in dstore" +msgstr "# bytes en la base de datos" + +#: src/applications/dstore_mysql/dstore_mysql.c:496 +#: src/applications/dstore_sqlite/dstore.c:638 +#, fuzzy +msgid "# max bytes allowed in dstore" +msgstr "# bytes en la base de datos" + +#: src/applications/transport/transport.c:191 +#, c-format +msgid "" +"Converting peer address to string failed, transport type %d not supported\n" +msgstr "" + +#: src/applications/transport/transport.c:246 +#, c-format +msgid "Transport connection attempt failed, transport type %d not supported\n" +msgstr "" + +#: src/applications/transport/transport.c:299 +#, c-format +msgid "" +"Transport failed to connect to peer `%s' (%u HELLOs known, none worked)\n" +msgstr "" + +#: src/applications/transport/transport.c:376 +#, c-format +msgid "Transmission attempt failed, transport type %d unknown.\n" +msgstr "" + +#: src/applications/transport/transport.c:500 +#, c-format +msgid "No transport of type %d known.\n" +msgstr "" + +#: src/applications/transport/transport.c:560 +msgid "No transport succeeded in creating a hello!\n" +msgstr "" + +#: src/applications/transport/transport.c:761 +#, fuzzy, c-format +msgid "Loading transports `%s'\n" +msgstr "Probando transporte(s) %s\n" + +#: src/applications/transport/transport.c:781 +#, fuzzy, c-format +msgid "Could not load transport plugin `%s'\n" +msgstr "Imposible inicializar la aplicación '%s'\n" + +#: src/applications/transport/transport.c:795 +#, c-format +msgid "Transport library `%s' did not provide required function '%s%s'.\n" +msgstr "" +"La biblioteca de transporte '%s' no provee la función '%s%s' requerida.\n" + +#: src/applications/transport/transport.c:824 +#, fuzzy, c-format +msgid "Loaded transport `%s'\n" +msgstr "Transporte(s) disponible(s): %s\n" + +#: src/applications/transport/transport.c:836 +#: src/server/gnunet-peer-info.c:252 +#, c-format +msgid "I am peer `%s'.\n" +msgstr "Yo soy el par '%s'.\n" + +#: src/applications/dht/tools/dht_multipeer_test.c:80 +#: src/applications/dht/tools/dht_twopeer_test.c:47 +#: src/applications/dht/module/table.c:783 +msgid "# dht connections" +msgstr "" + +#: src/applications/dht/tools/dht-query.c:54 +#, fuzzy +msgid "Query (get KEY, put KEY VALUE) DHT table." +msgstr "Pregunta (coje KEY, pone KEY VALUE, borra KEY VALUE) a una tabla DHT." + +#: src/applications/dht/tools/dht-query.c:58 +#, fuzzy +msgid "allow TIME ms to process a GET command" +msgstr "Reserva TIME ms para procesar cada comando" + +#: src/applications/dht/tools/dht-query.c:107 +#, fuzzy, c-format +msgid "Issuing `%s(%s,%s)' command.\n" +msgstr "'%s(%s, %s)' falló.\n" + +#: src/applications/dht/tools/dht-query.c:142 +#: src/applications/fs/tools/gnunet-auto-share.c:669 +#, c-format +msgid "Failed to connect to gnunetd.\n" +msgstr "Fallo al conectar a gnunetd.\n" + +#: src/applications/dht/tools/dht-query.c:155 +#, c-format +msgid "Command `%s' requires an argument (`%s').\n" +msgstr "El comando '%s' requiere un argumento ('%s').\n" + +#: src/applications/dht/tools/dht-query.c:172 +#, c-format +msgid "Command `%s' requires two arguments (`%s' and `%s').\n" +msgstr "El comando '%s' requiere dos argumentos ('%s' y '%s').\n" + +#: src/applications/dht/tools/dht-query.c:183 +#, c-format +msgid "Unsupported command `%s'. Aborting.\n" +msgstr "Comando inesperado '%s'. Abortando.\n" + +#: src/applications/dht/module/table.c:785 +#, fuzzy +msgid "# dht discovery messages received" +msgstr "# mensajes PONG encriptados recibidos" + +#: src/applications/dht/module/table.c:787 +msgid "# dht route host lookups performed" +msgstr "" + +#: src/applications/dht/module/table.c:789 +#, fuzzy +msgid "# dht discovery messages sent" +msgstr "# mensajes PONG encriptados recibidos" + +#: src/applications/dht/module/routing.c:879 +msgid "# dht replies routed" +msgstr "" + +#: src/applications/dht/module/routing.c:881 +msgid "# dht requests routed" +msgstr "" + +#: src/applications/dht/module/routing.c:883 +#, fuzzy +msgid "# dht get requests received" +msgstr "# mensajes PONG encriptados recibidos" + +#: src/applications/dht/module/routing.c:885 +#, fuzzy +msgid "# dht put requests received" +msgstr "# bytes recibidos por TCP" + +#: src/applications/dht/module/routing.c:887 +#, fuzzy +msgid "# dht results received" +msgstr "# bytes recibidos por TCP" + +#: src/applications/dht/module/routing.c:892 +#, fuzzy, c-format +msgid "`%s' registering p2p handlers: %d %d %d\n" +msgstr "'%s' registrando manejadores %d %d %d\n" + +#: src/applications/dht/module/cs.c:122 +#, c-format +msgid "`%s' failed. Terminating connection to client.\n" +msgstr "'%s' falló. Finalizando conexión con el cleinte.\n" + +#: src/applications/dht/module/cs.c:250 +#, fuzzy, c-format +msgid "`%s' registering client handlers: %d %d\n" +msgstr "'%s' registrando un manejador de clientes %d\n" + +#: src/applications/dht/module/cs.c:273 +msgid "Enables efficient non-anonymous routing" +msgstr "" + +#: src/applications/identity/hostkey.c:155 +#, c-format +msgid "" +"Existing hostkey in file `%s' failed format check, creating new hostkey.\n" +msgstr "" +"Se produjo un fallo al comprobar la clave local existente en el fichero '%" +"s', creando nueva clave local.\n" + +#: src/applications/identity/hostkey.c:164 +msgid "Creating new hostkey (this may take a while).\n" +msgstr "Creando nueva clave local (esto puede llevar un tiempo).\n" + +#: src/applications/identity/hostkey.c:176 +msgid "Done creating hostkey.\n" +msgstr "Correcto al crear la clave local.\n" + +#: src/applications/identity/identity.c:333 +#, c-format +msgid "" +"File `%s' in directory `%s' does not match naming convention. Removed.\n" +msgstr "" +"El fichero '%s' en el directorio '%s' no sigue la convención de nombres. " +"Eliminando.\n" + +#: src/applications/identity/identity.c:408 +#, fuzzy, c-format +msgid "Still no peers found in `%s'!\n" +msgstr "¡Imposible descargar adecuadamente el servicio '%s'!\n" + +#: src/applications/identity/identity.c:731 +#: src/applications/identity/identity.c:757 +#, fuzzy, c-format +msgid "Removed file `%s' containing invalid HELLO data.\n" +msgstr "Borrando el fichero '%s' que contiene datos de saludo no válidos.\n" + +#: src/applications/identity/identity.c:809 +#, c-format +msgid "Signature failed verification: peer `%s' not known.\n" +msgstr "La verificación de la firma falló: el par '%s' no es conocido.\n" + +#: src/applications/identity/identity.c:819 +msgid "Signature failed verification: signature invalid.\n" +msgstr "Falló la verificación de la firma: firma no válida.\n" + +#: src/applications/identity/identity.c:935 +#: src/applications/identity/identity.c:1058 +#, c-format +msgid "Peer `%s' is currently strictly blacklisted (for another %llums).\n" +msgstr "" + +#: src/applications/identity/identity.c:1061 +#, c-format +msgid "Peer `%s' is currently blacklisted (for another %llums).\n" +msgstr "" + +#: src/applications/pingpong/pingpong.c:134 +#: src/applications/pingpong/pingpong.c:203 +#: src/applications/pingpong/pingpong.c:273 +#: src/applications/pingpong/pingpong.c:345 +#, c-format +msgid "Received malformed `%s' message. Dropping.\n" +msgstr "Recibido el mensaje '%s' con un mal formato. Omitiendo.\n" + +#: src/applications/pingpong/pingpong.c:146 +msgid "Received ping for another peer. Dropping.\n" +msgstr "Recibido ping de otro par. Omitiendo.\n" + +#: src/applications/pingpong/pingpong.c:215 +#, fuzzy, c-format +msgid "Received PING from `%s' not destined for us!\n" +msgstr "¡Recibido PING no destinado a nosotros!\n" + +#: src/applications/pingpong/pingpong.c:315 +#: src/applications/pingpong/pingpong.c:381 +msgid "" +"Could not match PONG against any PING. Try increasing MAX_PING_PONG " +"constant.\n" +msgstr "" + +#: src/applications/pingpong/pingpong.c:425 +msgid "Cannot create PING, table full. Try increasing MAX_PING_PONG.\n" +msgstr "" + +#: src/applications/pingpong/pingpong.c:518 +msgid "# encrypted PONG messages received" +msgstr "# mensajes PONG encriptados recibidos" + +#: src/applications/pingpong/pingpong.c:520 +msgid "# plaintext PONG messages received" +msgstr "" + +#: src/applications/pingpong/pingpong.c:522 +msgid "# encrypted PING messages received" +msgstr "" + +#: src/applications/pingpong/pingpong.c:524 +msgid "# PING messages created" +msgstr "" + +#: src/applications/pingpong/pingpong.c:526 +#: src/applications/session/connect.c:926 +msgid "# encrypted PONG messages sent" +msgstr "" + +#: src/applications/pingpong/pingpong.c:530 +#: src/applications/session/connect.c:924 +msgid "# encrypted PING messages sent" +msgstr "" + +#: src/applications/pingpong/pingpong.c:532 +#, fuzzy +msgid "# plaintext PONG messages sent" +msgstr "# mensajes de texto mandados por PING" + +#: src/applications/pingpong/pingpong.c:536 +#, fuzzy +msgid "# plaintext PONG transmissions failed" +msgstr "# mensajes de texto mandados por PING" + +#: src/applications/pingpong/pingpong.c:546 +#, c-format +msgid "`%s' registering handlers %d %d (plaintext and ciphertext)\n" +msgstr "'%s' registrando manejadores %d %d (texto plano e hipertexto)\n" + +#: src/applications/hostlist/hostlist.c:165 +#, fuzzy +msgid "# hostlist requests received" +msgstr "# mensajes PONG encriptados recibidos" + +#: src/applications/hostlist/hostlist.c:167 +msgid "# hostlist HELLOs returned" +msgstr "" + +#: src/applications/hostlist/hostlist.c:169 +msgid "# hostlist bytes returned" +msgstr "" + +#: src/applications/hostlist/hostlist.c:199 +msgid "integrated HTTP hostlist server" +msgstr "" + +#: src/applications/session/connect.c:238 +#, c-format +msgid "Session key from peer `%s' could not be verified.\n" +msgstr "La clave de sesión del par '%s' no pudo ser verificada.\n" + +#: src/applications/session/connect.c:282 +#, fuzzy, c-format +msgid "Cannot encrypt sessionkey, peer `%s' not known!\n" +msgstr "¡Imposible encriptar la clave de sesión, otro par desconocido!\n" + +#: src/applications/session/connect.c:489 +#, fuzzy, c-format +msgid "Could not create any HELLO for myself (have transports `%s')!\n" +msgstr "¡Imposible crear la clave local!\n" + +#: src/applications/session/connect.c:599 +#, fuzzy, c-format +msgid "Session key received from peer `%s' has invalid format (discarded).\n" +msgstr "El mensaje recibido del par es inválido.\n" + +#: src/applications/session/connect.c:632 +#, fuzzy, c-format +msgid "Session key received from peer `%s' is for `%s' and not for me!\n" +msgstr "El mensaje recibido del par es inválido.\n" + +#: src/applications/session/connect.c:659 +#, c-format +msgid "Invalid `%s' message received from peer `%s'.\n" +msgstr "Mensaje '%s' inválido recibido del par '%s'.\n" + +#: src/applications/session/connect.c:670 +#, c-format +msgid "setkey `%s' from `%s' fails CRC check (have: %u, want %u).\n" +msgstr "" +"el cambio de clave '%s' de '%s' falló al comprobar el CRC (dio: %u, esperado " +"%u).\n" + +#: src/applications/session/connect.c:728 +#, fuzzy, c-format +msgid "" +"Error parsing encrypted session key from `%s', given message part size is " +"invalid.\n" +msgstr "" +"Error pasando la clave de sesión encriptada, el mensaje dado del tamaño de " +"las partes es inválido.\n" + +#: src/applications/session/connect.c:741 +#, fuzzy, c-format +msgid "Unknown type in embedded message from `%s': %u (size: %u)\n" +msgstr "Tipo desconocido en el mensaje embebido: %u (tamaño: %u)\n" + +#: src/applications/session/connect.c:916 +msgid "# session keys sent" +msgstr "# claves de la sesión mandadas" + +#: src/applications/session/connect.c:918 +msgid "# session keys rejected" +msgstr "# claves de la sesión rechazadas" + +#: src/applications/session/connect.c:920 +msgid "# session keys accepted" +msgstr "# claves de la sesión aceptadas" + +#: src/applications/session/connect.c:922 +msgid "# sessions established" +msgstr "# sesiones establecidas" + +#: src/applications/fs/tools/gnunet-pseudonym.c:70 +#: src/applications/fs/tools/gnunet-auto-share.c:199 +#: src/applications/fs/tools/gnunet-insert.c:246 +#: src/applications/fs/tools/gnunet-search.c:125 +#: src/applications/fs/tools/gnunet-download.c:77 +msgid "set the desired LEVEL of sender-anonymity" +msgstr "seleccione el NIVEL deseado de anonimato al enviar" + +#: src/applications/fs/tools/gnunet-pseudonym.c:73 +msgid "automate creation of a namespace by starting a collection" +msgstr "creación automática de un espacio al empezar una colección" + +#: src/applications/fs/tools/gnunet-pseudonym.c:77 +msgid "create a new pseudonym under the given NICKNAME" +msgstr "crea un nuevo pseudónimo bajo el APODO dado" + +#: src/applications/fs/tools/gnunet-pseudonym.c:80 +msgid "delete the pseudonym with the given NICKNAME" +msgstr "borra el pseudónimo con el APODO dado" + +#: src/applications/fs/tools/gnunet-pseudonym.c:83 +msgid "end automated building of a namespace (ends collection)" +msgstr "" +"finaliza la construcción automática de un espacio (finaliza la recolección)" + +#: src/applications/fs/tools/gnunet-pseudonym.c:85 +msgid "Create new pseudonyms, delete pseudonyms or list existing pseudonyms." +msgstr "" +"Crea nuevos pseudónimos, borra pseudónimos o lista los pseudónimos " +"existentes." + +#: src/applications/fs/tools/gnunet-pseudonym.c:89 +msgid "" +"use the given keyword to advertise the namespace (use when creating a new " +"pseudonym)" +msgstr "" +"usa la clave dada para anunciar el espacio (úsalo cuando crees un nuevo " +"pseudónimo)" + +#: src/applications/fs/tools/gnunet-pseudonym.c:92 +#, fuzzy +msgid "specify metadata describing the namespace or collection" +msgstr "" +"finaliza la construcción automática de un espacio (finaliza la recolección)" + +#: src/applications/fs/tools/gnunet-pseudonym.c:96 +msgid "" +"do not generate an advertisement for this namespace (use when creating a new " +"pseudonym)" +msgstr "" +"no genera un anuncio para este espacio (úsalo cuando crees un nuevo " +"pseudónimo)" + +#: src/applications/fs/tools/gnunet-pseudonym.c:99 +msgid "do not list the pseudonyms from the pseudonym database" +msgstr "no listar los pseudónimos de la base de datos de pseudónimos" + +#: src/applications/fs/tools/gnunet-pseudonym.c:103 +msgid "" +"specify IDENTIFIER to be the address of the entrypoint to content in the " +"namespace (use when creating a new pseudonym)" +msgstr "" +"especifica el IDENTIFIER para ser la dirección del punto de entrada al " +"contenido en el espacio (úsalo cuando crees un nuevo pseudónimo)" + +#: src/applications/fs/tools/gnunet-pseudonym.c:106 +msgid "set the rating of a namespace" +msgstr "cambia la valoración de un espacio" + +#: src/applications/fs/tools/gnunet-pseudonym.c:141 +#, c-format +msgid "Namespace `%s' has rating %d.\n" +msgstr "El espacio '%s' ha sido valorado con un %d.\n" + +#: src/applications/fs/tools/gnunet-pseudonym.c:143 +#, c-format +msgid "Namespace `%s' (%s) has rating %d.\n" +msgstr "El espacio '%s' (%s) ha sido valorado con un %d.\n" + +#: src/applications/fs/tools/gnunet-pseudonym.c:175 +#, c-format +msgid "\tRating (after update): %d\n" +msgstr "\tValoración (después de la actualización): %d\n" + +#: src/applications/fs/tools/gnunet-pseudonym.c:179 +#: src/applications/fs/tools/gnunet-pseudonym.c:241 +#: src/applications/fs/tools/gnunet-insert.c:101 +#, fuzzy, c-format +msgid "\tUnknown namespace `%s'\n" +msgstr "Operación desconocida '%s'\n" + +#: src/applications/fs/tools/gnunet-pseudonym.c:217 +#, c-format +msgid "Collection stopped.\n" +msgstr "Colección detenida.\n" + +#: src/applications/fs/tools/gnunet-pseudonym.c:219 +#, c-format +msgid "Failed to stop collection (not active?).\n" +msgstr "Falló al parar la recolección (¿no está activa?).\n" + +#: src/applications/fs/tools/gnunet-pseudonym.c:230 +#, c-format +msgid "Pseudonym `%s' deleted.\n" +msgstr "El pseudónimo '%s' fue borrado.\n" + +#: src/applications/fs/tools/gnunet-pseudonym.c:235 +#, c-format +msgid "Error deleting pseudonym `%s' (does not exist?).\n" +msgstr "Error borrando el pseudónimo '%s' (¿no existe?).\n" + +#: src/applications/fs/tools/gnunet-pseudonym.c:256 +#, fuzzy +msgid "Started collection.\n" +msgstr "Iniciada colección '%s'.\n" + +#: src/applications/fs/tools/gnunet-pseudonym.c:260 +msgid "Failed to start collection.\n" +msgstr "Falló al comenzar la recolección.\n" + +#: src/applications/fs/tools/gnunet-pseudonym.c:296 +#, fuzzy +msgid "Could not create namespace.\n" +msgstr "Imposible crear el espacio '%s' (¿existe?).\n" + +#: src/applications/fs/tools/gnunet-pseudonym.c:304 +#, c-format +msgid "Namespace `%s' created (root: %s).\n" +msgstr "El espacio '%s' fue creado (root: %s).\n" + +#: src/applications/fs/tools/gnunet-pseudonym.c:321 +#, fuzzy, c-format +msgid "You must specify a name for the collection (`%s' option).\n" +msgstr "Debes especificar un apodo (use la opción '%s').\n" + +#: src/applications/fs/tools/gnunet-pseudonym.c:329 +#, c-format +msgid "Could not access namespace information.\n" +msgstr "Imposible acceder a la información del espacio.\n" + +#: src/applications/fs/tools/gnunet-directory.c:84 +#, c-format +msgid "==> Directory `%s':\n" +msgstr "==> Directorio '%s':\n" + +#: src/applications/fs/tools/gnunet-directory.c:88 +#, c-format +msgid "=\tError reading directory.\n" +msgstr "=\tError leyendo el directorio.\n" + +#: src/applications/fs/tools/gnunet-directory.c:118 +#, c-format +msgid "File format error (not a GNUnet directory?)\n" +msgstr "Error en el formato del fichero (¿no es un directorio de GNUnet?)\n" + +#: src/applications/fs/tools/gnunet-directory.c:120 +#, c-format +msgid "%d files found in directory.\n" +msgstr "%d ficheros encontrados en el directorio.\n" + +#: src/applications/fs/tools/gnunet-directory.c:135 +msgid "Perform directory related operations." +msgstr "Realiza operaciones respecto al directorio." + +#: src/applications/fs/tools/gnunet-directory.c:138 +msgid "remove all entries from the directory database and stop tracking URIs" +msgstr "" +"borra todas las entradas de la base de datos del directorio y detiene el " +"seguimiento de URIs" + +#: src/applications/fs/tools/gnunet-directory.c:142 +msgid "list entries from the directory database" +msgstr "lista las entradas de la base de datos del directorio" + +#: src/applications/fs/tools/gnunet-directory.c:145 +msgid "start tracking entries for the directory database" +msgstr "comienza a seguir las entradas de la base de datos del directorio" + +#: src/applications/fs/tools/gnunet-directory.c:168 +#, c-format +msgid "Listed %d matching entries.\n" +msgstr "%d entradas encontradas.\n" + +#: src/applications/fs/tools/gnunet-auto-share.c:117 +#, fuzzy, c-format +msgid "Upload of `%s' at %llu out of %llu bytes.\n" +msgstr "Descargado del fichero '%s' el %16llu de %16llu bytes(%8.3f kbps)\n" + +#: src/applications/fs/tools/gnunet-auto-share.c:130 +#, fuzzy, c-format +msgid "Upload of `%s' complete, URI is `%s'.\n" +msgstr "" +"Subida de '%s' completada, la velocidad media de subida actual es %8.3f " +"kbps.\n" + +#: src/applications/fs/tools/gnunet-auto-share.c:139 +#, fuzzy, c-format +msgid "Upload aborted.\n" +msgstr "Descarga abortada" + +#: src/applications/fs/tools/gnunet-auto-share.c:145 +#, fuzzy, c-format +msgid "Error uploading file: %s\n" +msgstr "" +"\n" +"Error subiendo el fichero %s\n" + +#: src/applications/fs/tools/gnunet-auto-share.c:154 +#, fuzzy, c-format +msgid "Starting upload of `%s'.\n" +msgstr "Iniciada colección '%s'.\n" + +#: src/applications/fs/tools/gnunet-auto-share.c:164 +#, fuzzy, c-format +msgid "Uploading suspended.\n" +msgstr "¡Subida rechazada!" + +#: src/applications/fs/tools/gnunet-auto-share.c:179 +#, fuzzy, c-format +msgid "Uploading `%s' resumed.\n" +msgstr "¡Subida rechazada!" + +#: src/applications/fs/tools/gnunet-auto-share.c:186 +#, c-format +msgid "Unexpected event: %d\n" +msgstr "" + +#: src/applications/fs/tools/gnunet-auto-share.c:205 +#, fuzzy +msgid "" +"run in debug mode; gnunet-auto-share will not daemonize and error messages " +"will be written to stderr instead of a logfile" +msgstr "" +"se ejecuta en modo de depuración; gnunetd no sera un demonio y los mensajes " +"de error serán escritos a través de stderr en vez de en un fichero de log" + +#: src/applications/fs/tools/gnunet-auto-share.c:211 +#: src/applications/fs/tools/gnunet-insert.c:259 +#, fuzzy +msgid "" +"do not use libextractor to add additional references to directory entries " +"and/or the published file" +msgstr "" +"usa libextractor para añadir referencias directas adicionales a las entradas " +"del directorio" + +#: src/applications/fs/tools/gnunet-auto-share.c:213 +msgid "Automatically share a directory." +msgstr "" + +#: src/applications/fs/tools/gnunet-auto-share.c:216 +#: src/applications/fs/tools/gnunet-insert.c:273 +msgid "" +"add an additional keyword for all files and directories (this option can be " +"specified multiple times)" +msgstr "" +"añade una clave adicional para todos los ficheros y directorios (esta opción " +"puede ser especificada varias veces)" + +#: src/applications/fs/tools/gnunet-auto-share.c:221 +#: src/applications/fs/tools/gnunet-insert.c:290 +msgid "specify the priority of the content" +msgstr "especifica la prioridad del contenido" + +#: src/applications/fs/tools/gnunet-auto-share.c:468 +#: src/applications/fs/tools/gnunet-auto-share.c:903 +#, fuzzy, c-format +msgid "Could not access `%s': %s\n" +msgstr "Imposible ejecutar '%s': %s\n" + +#: src/applications/fs/tools/gnunet-auto-share.c:547 +#, c-format +msgid "Unknown keyword type `%s' in metadata configuration\n" +msgstr "" + +#: src/applications/fs/tools/gnunet-auto-share.c:652 +#, fuzzy, c-format +msgid "Failed to stop running gnunet-auto-share.\n" +msgstr "Fallo al conectar a gnunetd.\n" + +#: src/applications/fs/tools/gnunet-auto-share.c:918 +#, c-format +msgid "Directory `%s' is already on the list of shared directories.\n" +msgstr "" + +#: src/applications/fs/tools/gnunet-auto-share.c:939 +msgid "" +"The specified directories were added to the list of shared directories.\n" +msgstr "" + +#: src/applications/fs/tools/gnunet-auto-share.c:961 +#, fuzzy, c-format +msgid "Could not open logfile `%s': %s\n" +msgstr "Fichero almacenado en '%s'.\n" + +#: src/applications/fs/tools/gnunet-insert.c:115 +#, c-format +msgid "Created entry `%s' in namespace `%s'\n" +msgstr "Creada la entrada '%s' en el espacio '%s'\n" + +#: src/applications/fs/tools/gnunet-insert.c:120 +#, c-format +msgid "Failed to add entry to namespace `%s' (does it exist?)\n" +msgstr "Fallo al añadir la entrada al espacio '%s' (¿existe?)\n" + +#: src/applications/fs/tools/gnunet-insert.c:135 +#, c-format +msgid "Keywords for file `%s':\n" +msgstr "Claves para los ficheros '%s':\n" + +#: src/applications/fs/tools/gnunet-insert.c:144 +msgid "filename" +msgstr "" + +#: src/applications/fs/tools/gnunet-insert.c:146 +msgid "mimetype" +msgstr "" + +#: src/applications/fs/tools/gnunet-insert.c:186 +#, fuzzy, c-format +msgid "%16llu of %16llu bytes inserted (estimating %6s to completion) - %s\n" +msgstr "%16llu de %16llu bytes insertados (%s estimado para completar)\n" + +#: src/applications/fs/tools/gnunet-insert.c:198 +#, fuzzy, c-format +msgid "Upload of `%s' complete, %llu bytes took %llu seconds (%8.3f KiB/s).\n" +msgstr "" +"Subida de '%s' completada, %llu bytes tomaron %llu segundos (%8.3f kbps).\n" + +#: src/applications/fs/tools/gnunet-insert.c:209 +#, c-format +msgid "File `%s' has URI: %s\n" +msgstr "El fichero '%s' tiene la URI: '%s'\n" + +#: src/applications/fs/tools/gnunet-insert.c:220 +#, fuzzy, c-format +msgid "" +"\n" +"Upload aborted.\n" +msgstr "Descarga abortada" + +#: src/applications/fs/tools/gnunet-insert.c:225 +#, fuzzy, c-format +msgid "" +"\n" +"Error uploading file: %s" +msgstr "" +"\n" +"Error subiendo el fichero %s\n" + +#: src/applications/fs/tools/gnunet-insert.c:234 +#, c-format +msgid "" +"\n" +"Unexpected event: %d\n" +msgstr "" + +#: src/applications/fs/tools/gnunet-insert.c:250 +msgid "" +"even if gnunetd is running on the local machine, force the creation of a " +"copy instead of making a link to the GNUnet share directory" +msgstr "" +"aunque gnunetd este corriendo en el ordenador local, fuerza la creación de " +"una copia en vez de hacer un enlace al directorio para compartir de GNUnet" + +#: src/applications/fs/tools/gnunet-insert.c:255 +msgid "disable adding the creation time to the metadata of the uploaded file" +msgstr "" + +#: src/applications/fs/tools/gnunet-insert.c:263 +msgid "" +"print list of extracted keywords that would be used, but do not perform " +"upload" +msgstr "" +"imprime la lista de las claves extraidas que podrían ser usadas, pero no " +"realiza la subida" + +#: src/applications/fs/tools/gnunet-insert.c:265 +msgid "Make files available to GNUnet for sharing." +msgstr "Permite a GNUnet disponer de los ficheros para compartirlos." + +#: src/applications/fs/tools/gnunet-insert.c:269 +msgid "" +"add an additional keyword for the top-level file or directory (this option " +"can be specified multiple times)" +msgstr "" +"añade una clave adicional para el fichero del nivel más alto o el directorio " +"(esta opción puede ser especificada varias veces)" + +#: src/applications/fs/tools/gnunet-insert.c:278 +msgid "set the meta-data for the given TYPE to the given VALUE" +msgstr "cambia el meta-dato para el TIPO dado al VALOR dado" + +#: src/applications/fs/tools/gnunet-insert.c:281 +msgid "" +"do not index, perform full insertion (stores entire file in encrypted form " +"in GNUnet database)" +msgstr "" +"no indexar, hacer inserciones totales (almacena el fichero entero de forma " +"encriptada en la base de datos de GNUnet)" + +#: src/applications/fs/tools/gnunet-insert.c:286 +msgid "" +"specify ID of an updated version to be published in the future (for " +"namespace insertions only)" +msgstr "" +"especifica la ID de una versión actualizada para ser publicada en el futuro " +"(para inserciones en el espacio únicamente)" + +#: src/applications/fs/tools/gnunet-insert.c:294 +msgid "publish the files under the pseudonym NAME (place file into namespace)" +msgstr "" +"publica los ficheros bajo el pseudónimo NOMBRE (coloca el fichero en el " +"espacio)" + +#: src/applications/fs/tools/gnunet-insert.c:297 +msgid "" +"only simulte the process but do not do any actual publishing (useful to " +"compute URIs)" +msgstr "" + +#: src/applications/fs/tools/gnunet-insert.c:301 +msgid "" +"set the ID of this version of the publication (for namespace insertions only)" +msgstr "" +"cambia la ID de esta versión de la publicación (para inserciones en el " +"espacio únicamente)" + +#: src/applications/fs/tools/gnunet-insert.c:305 +msgid "" +"URI to be published (can be used instead of passing a file to add keywords " +"to the file with the respective URI)" +msgstr "" + +#: src/applications/fs/tools/gnunet-insert.c:342 +#, fuzzy, c-format +msgid "You must specify one and only one filename for insertion.\n" +msgstr "Debes especificar uno y solo un fichero para desindexar.\n" + +#: src/applications/fs/tools/gnunet-insert.c:348 +#, fuzzy, c-format +msgid "You must NOT specify an URI and a filename.\n" +msgstr "¡Debes especificar un receptor!\n" + +#: src/applications/fs/tools/gnunet-insert.c:354 +#, c-format +msgid "Cannot extract metadata from a URI!\n" +msgstr "" + +#: src/applications/fs/tools/gnunet-insert.c:400 +#, c-format +msgid "Could not access namespace `%s' (does not exist?).\n" +msgstr "Imposible acceder al espacio '%s' (¿no existe?).\n" + +#: src/applications/fs/tools/gnunet-insert.c:408 +#, fuzzy, c-format +msgid "Option `%s' is required when using option `%s'.\n" +msgstr "La opción '%s' no tiene sentido sin la opción '%s'.\n" + +#: src/applications/fs/tools/gnunet-insert.c:419 +#: src/applications/fs/tools/gnunet-insert.c:427 +#, c-format +msgid "Option `%s' makes no sense without option `%s'.\n" +msgstr "La opción '%s' no tiene sentido sin la opción '%s'.\n" + +#: src/applications/fs/tools/gnunet-search.c:128 +msgid "Search GNUnet for files." +msgstr "Buscar GNUnet en busca de ficheros." + +#: src/applications/fs/tools/gnunet-search.c:132 +msgid "write encountered (decrypted) search results to FILENAME" +msgstr "escribe los resultados encontrados (desencriptados) a FILENAME" + +#: src/applications/fs/tools/gnunet-search.c:169 +#, c-format +msgid "Error converting arguments to URI!\n" +msgstr "¡Se produjo un error al convertir los argumentos a una URI!\n" + +#: src/applications/fs/tools/gnunet-unindex.c:61 +#, c-format +msgid "" +"%16llu of %16llu bytes unindexed (estimating %llu seconds to " +"completion) " +msgstr "" +"%16llu de %16llu bytes desindexados (estimados %llu segundos para " +"completar) " + +#: src/applications/fs/tools/gnunet-unindex.c:73 +#, fuzzy, c-format +msgid "" +"\n" +"Unindexing of `%s' complete, %llu bytes took %llu seconds (%8.3f KiB/s).\n" +msgstr "" +"\n" +"El desindexado de '%s' se completó, %llu bytes tomaron %llu segundos (%8.3f " +"kbps).\n" + +#: src/applications/fs/tools/gnunet-unindex.c:88 +#, c-format +msgid "" +"\n" +"Error unindexing file: %s\n" +msgstr "" +"\n" +"Error desindexando el fichero: %s\n" + +#: src/applications/fs/tools/gnunet-unindex.c:108 +msgid "Unindex files." +msgstr "Desindexar los ficheros." + +#: src/applications/fs/tools/gnunet-unindex.c:145 +#, fuzzy +msgid "Not enough arguments. You must specify a filename.\n" +msgstr "" +"Argumentos insuficientes. Debes especificar una URI de un fichero de GNUnet\n" + +#: src/applications/fs/tools/gnunet-unindex.c:163 +#, c-format +msgid "`%s' failed. Is `%s' a file?\n" +msgstr "'%s' falló. ¿Es '%s' un fichero?\n" + +#: src/applications/fs/tools/gnunet-download.c:82 +msgid "" +"download a GNUnet directory that has already been downloaded. Requires that " +"a filename of an existing file is specified instead of the URI. The " +"download will only download the top-level files in the directory unless the " +"`-R' option is also specified." +msgstr "" + +#: src/applications/fs/tools/gnunet-download.c:85 +msgid "delete incomplete downloads (when aborted with CTRL-C)" +msgstr "" + +#: src/applications/fs/tools/gnunet-download.c:87 +msgid "Download files from GNUnet." +msgstr "Descarga los ficheros de GNUnet" + +#: src/applications/fs/tools/gnunet-download.c:91 +msgid "write the file to FILENAME" +msgstr "escribe el fichero al FICHERO" + +#: src/applications/fs/tools/gnunet-download.c:95 +msgid "set the maximum number of parallel downloads that are allowed" +msgstr "" + +#: src/applications/fs/tools/gnunet-download.c:98 +msgid "download a GNUnet directory recursively" +msgstr "descarga un directorio de GNUnet recursivamente" + +#: src/applications/fs/tools/gnunet-download.c:119 +#, fuzzy, c-format +msgid "Download of file `%s' at %16llu out of %16llu bytes (%8.3f KiB/s)\n" +msgstr "Descargado del fichero '%s' el %16llu de %16llu bytes(%8.3f kbps)\n" + +#: src/applications/fs/tools/gnunet-download.c:133 +#, fuzzy, c-format +msgid "Download aborted.\n" +msgstr "Descarga abortada" + +#: src/applications/fs/tools/gnunet-download.c:139 +#, c-format +msgid "Error downloading: %s\n" +msgstr "Error descargando: %s\n" + +#: src/applications/fs/tools/gnunet-download.c:145 +#, fuzzy, c-format +msgid "Download of file `%s' complete. Speed was %8.3f KiB per second.\n" +msgstr "" +"Descarga del fichero '%s' completada. La velocidad media fue %8.3fkilobytes " +"por segundo.\n" + +#: src/applications/fs/tools/gnunet-download.c:191 +msgid "no name given" +msgstr "" + +#: src/applications/fs/tools/gnunet-download.c:197 +#, fuzzy, c-format +msgid "Starting download `%s'\n" +msgstr "Iniciada colección '%s'.\n" + +#: src/applications/fs/tools/gnunet-download.c:239 +msgid "Not enough arguments. You must specify a GNUnet file URI\n" +msgstr "" +"Argumentos insuficientes. Debes especificar una URI de un fichero de GNUnet\n" + +#: src/applications/fs/tools/gnunet-download.c:257 +#, c-format +msgid "URI `%s' invalid for gnunet-download.\n" +msgstr "La URI '%s' no es válida para una descarga-de-gnunet.\n" + +#: src/applications/fs/tools/gnunet-download.c:300 +#, fuzzy, c-format +msgid "No filename specified, using `%s' instead (for now).\n" +msgstr "Ningún nombre especificado para la tabla, usando '%s'.\n" + +#: src/applications/fs/tools/gnunet-download.c:342 +#, fuzzy, c-format +msgid "Could not access gnunet-directory file `%s'\n" +msgstr "Imposible pasar el fichero de configuración '%s'.\n" + +#: src/applications/fs/tools/gnunet-download.c:363 +#, fuzzy, c-format +msgid "Downloading %d files from directory `%s'.\n" +msgstr "Descarga los ficheros de GNUnet" + +#: src/applications/fs/tools/gnunet-download.c:366 +#, fuzzy, c-format +msgid "Did not find any files in directory `%s'\n" +msgstr "Falló al actualizar los datos del módulo '%s'\n" + +#: src/applications/fs/tools/gnunet-download.c:404 +#, c-format +msgid "File stored as `%s'.\n" +msgstr "Fichero almacenado en '%s'.\n" + +#: src/applications/fs/uritrack/file_info.c:98 +msgid "Collecting file identifiers disabled.\n" +msgstr "Recolección de identificadores de fichero desactivada.\n" + +#: src/applications/fs/uritrack/file_info.c:377 +#, c-format +msgid "Deleted corrupt URI database in `%s'." +msgstr "Borrada base de datos de URIs corrupta en '%s'." + +#: src/applications/fs/ecrs/upload.c:158 +#, c-format +msgid "`%s' is not a file.\n" +msgstr "'%s' no es un fichero.\n" + +#: src/applications/fs/ecrs/upload.c:166 +#, c-format +msgid "Cannot get size of file `%s'" +msgstr "Imposible determinar el tamaño del fichero '%s'" + +#: src/applications/fs/ecrs/upload.c:175 +msgid "Failed to connect to gnunetd." +msgstr "Fallo al conectarse a gnunetd" + +#: src/applications/fs/ecrs/upload.c:187 +#, c-format +msgid "Cannot hash `%s'.\n" +msgstr "Imposible hallar el hash de '%s'.\n" + +#: src/applications/fs/ecrs/upload.c:215 +#, c-format +msgid "Initialization for indexing file `%s' failed.\n" +msgstr "" +"Durante la inicialización de la indexación del fichero '%s' se produjo un " +"fallo.\n" + +#: src/applications/fs/ecrs/upload.c:223 +#, fuzzy, c-format +msgid "Indexing file `%s' failed. Suggestion: try to insert the file.\n" +msgstr "Indexación del fichero '%s' fallida. Intentando insertar fichero...\n" + +#: src/applications/fs/ecrs/upload.c:237 +#, fuzzy, c-format +msgid "Cannot open file `%s': `%s'" +msgstr "Imposible abrir el fichero de configuración '%s'.\n" + +#: src/applications/fs/ecrs/upload.c:322 +#, fuzzy, c-format +msgid "Indexing data of file `%s' failed at position %llu.\n" +msgstr "La indexación de los datos falló en la posición %i.\n" + +#: src/applications/fs/ecrs/helper.c:91 +msgid "No keywords specified!\n" +msgstr "¡Ninguna clave especificada!\n" + +#: src/applications/fs/ecrs/helper.c:99 +msgid "Number of double-quotes not balanced!\n" +msgstr "" + +#: src/applications/fs/ecrs/helper.c:398 +#, c-format +msgid "Renaming of file `%s' to `%s' failed: %s\n" +msgstr "Al renombrar el fichero '%s' a '%s' se produjo un fallo: %s\n" + +#: src/applications/fs/ecrs/helper.c:408 +#, c-format +msgid "Could not rename file `%s' to `%s': file exists\n" +msgstr "Imposible renombrar el fichero '%s' a '%s': el fichero ya existe\n" + +#: src/applications/fs/ecrs/parser.c:165 +#, c-format +msgid "" +"Unknown metadata type in metadata option `%s'. Using metadata type " +"`unknown' instead.\n" +msgstr "" +"Tipo de metadatos desconocido en la opción de metadatos '%s'. Usando el " +"tipo de metadatos 'desconocido' en su lugar.\n" + +#: src/applications/fs/ecrs/search.c:152 +msgid "CHK URI not allowed for search.\n" +msgstr "CHK URI no autorizado para buscar.\n" + +#: src/applications/fs/ecrs/search.c:207 +msgid "LOC URI not allowed for search.\n" +msgstr "LOC URI no autorizado para buscar.\n" + +#: src/applications/fs/ecrs/namespace.c:365 +#, c-format +msgid "File `%s' does not contain a pseudonym.\n" +msgstr "El fichero '%s' no contiene un pseudónimo.\n" + +#: src/applications/fs/ecrs/namespace.c:376 +#, c-format +msgid "Format of pseudonym `%s' is invalid.\n" +msgstr "Formato del pseudónimo '%s' no es válido.\n" + +#: src/applications/fs/ecrs/namespace.c:535 +#: src/applications/fs/ecrs/namespace.c:547 +#: src/applications/fs/ecrs/namespace.c:559 +#, fuzzy, c-format +msgid "Format of file `%s' is invalid, trying to remove.\n" +msgstr "El formato del fichero '%s' no es válido.\n" + +#: src/applications/fs/ecrs/download.c:599 +msgid "" +"Decrypted content does not match key. This is either a bug or a maliciously " +"inserted file. Download aborted.\n" +msgstr "" +"El contenido desencriptado no encuentra una clave. Esto puede ser un bug o " +"un fichero introducido maliciosamente. Descarga abortada.\n" + +#: src/applications/fs/ecrs/download.c:609 +msgid "IO error." +msgstr "" + +#: src/applications/fs/collection/collection.c:559 +#: src/applications/fs/collection/collection.c:562 +#, c-format +msgid "Revision %u" +msgstr "" + +#: src/applications/fs/fsui/upload.c:330 +#, fuzzy +msgid "Application aborted." +msgstr "_Opciones" + +#: src/applications/fs/fsui/upload.c:344 +#, fuzzy +msgid "Failed to create temporary directory." +msgstr "Error en el formato del fichero (¿no es un directorio de GNUnet?)\n" + +#: src/applications/fs/fsui/deserialize.c:927 +#, c-format +msgid "FSUI state file `%s' had syntax error at offset %u.\n" +msgstr "" +"Fichero del estado de FSUI '%s' tiene un error de sintáxis en la asignación %" +"u.\n" + +#: src/applications/fs/fsui/unindex.c:114 +msgid "Unindexing failed (no reason given)" +msgstr "" + +#: src/applications/fs/gap/plan.c:944 +msgid "# gap requests total sent" +msgstr "" + +#: src/applications/fs/gap/plan.c:946 +msgid "# gap content total planned" +msgstr "" + +#: src/applications/fs/gap/plan.c:948 +msgid "# gap routes succeeded" +msgstr "" + +#: src/applications/fs/gap/plan.c:949 +msgid "# trust spent" +msgstr "" + +#: src/applications/fs/gap/fs.c:157 +msgid "Datastore full.\n" +msgstr "" + +#: src/applications/fs/gap/fs.c:831 +msgid "# gap requests total received" +msgstr "" + +#: src/applications/fs/gap/fs.c:833 +#, fuzzy +msgid "# gap requests dropped due to load" +msgstr "# Anuncios de los pares recibidos" + +#: src/applications/fs/gap/fs.c:835 +msgid "# gap content total received" +msgstr "" + +#: src/applications/fs/gap/fs.c:837 +msgid "# gap total trust awarded" +msgstr "" + +#: src/applications/fs/gap/fs.c:865 +#, fuzzy, c-format +msgid "" +"`%s' registering client handlers %d %d %d %d %d %d %d %d and P2P handlers %d " +"%d\n" +msgstr "'%s' registrando manejadores de clientes %d %d %d %d %d %d %d %d %d\n" + +#: src/applications/fs/gap/fs.c:921 +msgid "enables (anonymous) file-sharing" +msgstr "activa la compartición de ficheros anónima" + +#: src/applications/fs/gap/ondemand.c:173 +#, c-format +msgid "" +"Because the file `%s' has been unavailable for 3 days it got removed from " +"your share. Please unindex files before deleting them as the index now " +"contains invalid references!\n" +msgstr "" +"El fichero '%s' se ha eliminado de tus compartidos porque no ha estado " +"disponible durante tres días. Por favor, desindexa los ficheros antes de " +"borrarlos porque eso causa que el índice tenga referencias no válidas.\n" + +#: src/applications/fs/gap/ondemand.c:451 +msgid "Indexed content changed (does not match its hash).\n" +msgstr "" + +#: src/applications/fs/gap/ondemand.c:569 +#, fuzzy, c-format +msgid "" +"Unindexed ODB block `%s' from offset %llu already missing from datastore.\n" +msgstr "" +"Bloque ODB '%s' desindexado del offset %llu perdido de la base de datos.\n" + +#: src/applications/fs/gap/pid_table.c:177 +msgid "# distinct interned peer IDs in pid table" +msgstr "" + +#: src/applications/fs/gap/pid_table.c:180 +msgid "# total RC of interned peer IDs in pid table" +msgstr "" + +#: src/applications/fs/gap/querymanager.c:708 +#, fuzzy +msgid "# gap client queries received" +msgstr "# mensajes PONG encriptados recibidos" + +#: src/applications/fs/gap/querymanager.c:710 +msgid "# gap replies sent to clients" +msgstr "" + +#: src/applications/fs/gap/querymanager.c:712 +#, fuzzy +msgid "# gap client requests tracked" +msgstr "# mensajes PONG encriptados recibidos" + +#: src/applications/fs/gap/querymanager.c:714 +#, fuzzy +msgid "# gap client requests injected" +msgstr "# mensajes PONG encriptados recibidos" + +#: src/applications/fs/gap/querymanager.c:717 +msgid "# gap query bloomfilter resizing updates" +msgstr "" + +#: src/applications/fs/gap/migration.c:437 +msgid "# blocks migrated" +msgstr "# blocks migrados" + +#: src/applications/fs/gap/migration.c:439 +#, fuzzy +msgid "# blocks injected for migration" +msgstr "# blocks migrados" + +#: src/applications/fs/gap/migration.c:441 +#, fuzzy +msgid "# blocks fetched for migration" +msgstr "# blocks migrados" + +#: src/applications/fs/gap/migration.c:443 +#, fuzzy +msgid "# on-demand fetches for migration" +msgstr "# blocks migrados" + +#: src/applications/fs/gap/gap.c:694 +msgid "# gap queries dropped (table full)" +msgstr "" + +#: src/applications/fs/gap/gap.c:696 +msgid "# gap queries dropped (redundant)" +msgstr "" + +#: src/applications/fs/gap/gap.c:698 +msgid "# gap queries routed" +msgstr "" + +#: src/applications/fs/gap/gap.c:700 +msgid "# gap content found locally" +msgstr "" + +#: src/applications/fs/gap/gap.c:703 +msgid "# gap queries refreshed existing record" +msgstr "" + +#: src/applications/fs/gap/gap.c:704 +msgid "# trust earned" +msgstr "" + +#: src/applications/fs/gap/fs_dht.c:256 +msgid "# blocks pushed into DHT" +msgstr "" + +#: src/applications/fs/gap/anonymity.c:56 +msgid "Failed to get traffic stats.\n" +msgstr "Fallo en las estadísticas del tráfico.\n" + +#: src/applications/testing/remote.c:68 +#, c-format +msgid "scp command is : %s \n" +msgstr "" + +#: src/applications/testing/remote.c:491 +#, fuzzy, c-format +msgid "Friend list of %s:%d\n" +msgstr "Fallo al conectar a gnunetd.\n" + +#: src/applications/testing/remote.c:513 +#, c-format +msgid "scp command for friend file copy is : %s \n" +msgstr "" + +#: src/applications/testing/remote.c:535 +#, fuzzy, c-format +msgid "connecting peer %s:%d to peer %s:%d\n" +msgstr "Imposible conectar a %u.%u.%u.%u:%u: %s\n" + +#: src/applications/testing/remotetest.c:38 +msgid "Set up multiple gnunetd daemons across multiple hosts." +msgstr "" + +#: src/applications/testing/remotetest.c:43 +#, fuzzy +msgid "set number of daemons to start" +msgstr "número de mensajes a usar por iteración" + +#: src/applications/testing/testing.c:268 +#: src/applications/testing/remotetopologies.c:367 +#, fuzzy, c-format +msgid "Waiting for peers to connect" +msgstr "Esperando a los pares para conectar (%u ciclos restantes)...\n" + +#: src/applications/testing/remotetopologies.c:213 +#, c-format +msgid "Connecting nodes in 2d torus topology: %u rows %u columns\n" +msgstr "" + +#: src/applications/testing/remotetopologies.c:491 +#, fuzzy, c-format +msgid "Failed to establish connection with peers.\n" +msgstr "Se produjo un error estableciendo conexión con gnunetd.\n" + +#: src/applications/bootstrap_http/http.c:113 +#, fuzzy, c-format +msgid "Bootstrap data obtained from `%s' is invalid.\n" +msgstr "Formato del pseudónimo '%s' no es válido.\n" + +#: src/applications/bootstrap_http/http.c:126 +#: src/applications/bootstrap_http/http.c:277 +#: src/applications/bootstrap_http/http.c:294 +#: src/applications/bootstrap_http/http.c:333 +#: src/applications/bootstrap_http/http.c:352 +#: src/applications/bootstrap_http/http.c:365 +#: src/applications/bootstrap_http/http.c:375 +#: src/applications/bootstrap_http/http.c:385 src/transports/upnp/upnp.c:356 +#: src/transports/upnp/upnp.c:541 src/transports/http.c:1085 +#: src/transports/http.c:1209 src/transports/http.c:1377 +#: src/transports/http.c:1777 src/transports/http.c:1827 +#, fuzzy, c-format +msgid "%s failed at %s:%d: `%s'\n" +msgstr "'%s' falló en %s: %d con error: '%s'.\n" + +#: src/applications/bootstrap_http/http.c:185 +msgid "No hostlist URL specified in configuration, will not bootstrap.\n" +msgstr "" + +#: src/applications/bootstrap_http/http.c:226 +#, c-format +msgid "Bootstrapping using `%s'.\n" +msgstr "" + +#: src/applications/bootstrap_http/http.c:254 +#, c-format +msgid "Trying to download hostlist from `%s'\n" +msgstr "" + +#: src/applications/bootstrap_http/http.c:391 +#, fuzzy, c-format +msgid "Downloaded %llu bytes from `%s'.\n" +msgstr "Descarga los ficheros de GNUnet" + +#: src/applications/bootstrap_http/http.c:425 +#, fuzzy +msgid "# HELLOs downloaded via http" +msgstr "# saludos descargados vía HTTP" + +#: src/applications/getoption/getoption.c:78 +#, c-format +msgid "`%s' registering client handler %d\n" +msgstr "'%s' registrando un manejador de clientes %d\n" + +#: src/applications/getoption/getoption.c:88 +msgid "allows clients to determine gnunetd's configuration" +msgstr "permite a los clientes determinar la configuración de gnunetd" + +#: src/applications/template/template.c:70 +#, c-format +msgid "`%s' registering client handler %d and %d\n" +msgstr "'%s' registrando manejador de clientes %d y %d\n" + +#: src/applications/template/gnunet-template.c:42 +msgid "Template description." +msgstr "" + +#: src/applications/stats/clientapi.c:331 +msgid "Uptime (seconds)" +msgstr "" + +#: src/applications/stats/sqstats.c:151 +msgid "# Any-Blocks" +msgstr "" + +#: src/applications/stats/sqstats.c:152 +msgid "# DBlocks" +msgstr "" + +#: src/applications/stats/sqstats.c:153 +msgid "# SBlocks" +msgstr "" + +#: src/applications/stats/sqstats.c:154 +msgid "# KBlocks" +msgstr "" + +#: src/applications/stats/sqstats.c:155 +msgid "# NBlocks" +msgstr "" + +#: src/applications/stats/sqstats.c:156 +msgid "# KNBlocks" +msgstr "" + +#: src/applications/stats/sqstats.c:157 +msgid "# OnDemand-Blocks" +msgstr "" + +#: src/applications/stats/sqstats.c:158 +msgid "# Unknown-Blocks" +msgstr "" + +#: src/applications/stats/sqstats.c:159 +msgid "# expired" +msgstr "" + +#: src/applications/stats/sqstats.c:160 +msgid "# expire in 1h" +msgstr "" + +#: src/applications/stats/sqstats.c:161 +msgid "# expire in 24h" +msgstr "" + +#: src/applications/stats/sqstats.c:162 +msgid "# expire in 1 week" +msgstr "" + +#: src/applications/stats/sqstats.c:163 +msgid "# expire in 1 month" +msgstr "" + +#: src/applications/stats/sqstats.c:164 +msgid "# zero priority" +msgstr "" + +#: src/applications/stats/sqstats.c:165 +msgid "# priority one" +msgstr "" + +#: src/applications/stats/sqstats.c:166 +msgid "# priority larger than one" +msgstr "" + +#: src/applications/stats/sqstats.c:167 +msgid "# no anonymity" +msgstr "" + +#: src/applications/stats/sqstats.c:168 +msgid "# anonymity one" +msgstr "" + +#: src/applications/stats/sqstats.c:169 +msgid "# anonymity larger than one" +msgstr "" + +#: src/applications/stats/statistics.c:238 +#, fuzzy, no-c-format +msgid "% of allowed network load (up)" +msgstr "% de red permitida para la subida" + +#: src/applications/stats/statistics.c:240 +#, fuzzy, no-c-format +msgid "% of allowed network load (down)" +msgstr "% de red permitida para bajada" + +#: src/applications/stats/statistics.c:243 +#, fuzzy, no-c-format +msgid "% of allowed cpu load" +msgstr "% de CPU permitida para el uso" + +#: src/applications/stats/statistics.c:246 +#, fuzzy, no-c-format +msgid "% of allowed io load" +msgstr "% de CPU permitida para el uso" + +#: src/applications/stats/statistics.c:249 +msgid "# bytes of noise received" +msgstr "# bytes de ruido recibidos" + +#: src/applications/stats/statistics.c:251 +msgid "# plibc handles" +msgstr "" + +#: src/applications/stats/statistics.c:441 +#, fuzzy, c-format +msgid "`%s' registering client handlers %d %d %d and p2p handler %d\n" +msgstr "'%s' registrando manejadores de clientes %d %d %d %d %d %d %d %d %d\n" + +#: src/applications/stats/statistics.c:463 +msgid "keeps statistics about gnunetd's operation" +msgstr "" + +#: src/applications/stats/gnunet-stats.c:61 +#, c-format +msgid "Supported peer-to-peer messages:\n" +msgstr "Mensajes P2P soportados:\n" + +#: src/applications/stats/gnunet-stats.c:64 +#, c-format +msgid "Supported client-server messages:\n" +msgstr "Mensajes cliente-servidor soportados:\n" + +#: src/applications/stats/gnunet-stats.c:83 +#: src/applications/vpn/gnunet-vpn.c:59 +msgid "Print statistics about GNUnet operations." +msgstr "Imprime estadísticas acerca de las operaciones de GNUnet." + +#: src/applications/stats/gnunet-stats.c:87 +msgid "prints supported protocol messages" +msgstr "imprime los mensajes del protocolo" + +#: src/applications/stats/gnunet-stats.c:136 +#, c-format +msgid "Error reading information from gnunetd.\n" +msgstr "Se produjo un error leyendo información de gnunetd.\n" + +#: src/applications/vpn/gnunet-vpn.c:63 +msgid "Suppress display of asynchronous log messages" +msgstr "" + +#: src/applications/vpn/p2p.c:75 +msgid "VPN IP src not anonymous. drop..\n" +msgstr "" + +#: src/applications/vpn/p2p.c:83 +msgid "VPN IP not anonymous, drop.\n" +msgstr "" + +#: src/applications/vpn/p2p.c:92 +msgid "VPN Received, not anonymous, drop.\n" +msgstr "" + +#: src/applications/vpn/p2p.c:97 +#, fuzzy, c-format +msgid "VPN Received unknown IP version %d...\n" +msgstr "Recibida petición de tipo desconocido %d en %s: %d\n" + +#: src/applications/vpn/p2p.c:110 +#, c-format +msgid "<- GNUnet(%d) : %s\n" +msgstr "" + +#: src/applications/vpn/p2p.c:139 +msgid "Could not write the tunnelled IP to the OS... Did to setup a tunnel?\n" +msgstr "" + +#: src/applications/vpn/p2p.c:183 +msgid "Receive route request\n" +msgstr "" + +#: src/applications/vpn/p2p.c:193 +#, c-format +msgid "Prepare route announcement level %d\n" +msgstr "" + +#: src/applications/vpn/p2p.c:208 +#, c-format +msgid "Send route announcement %d with route announce\n" +msgstr "" + +#: src/applications/vpn/p2p.c:217 +#, c-format +msgid "Send outside table info %d\n" +msgstr "" + +#: src/applications/vpn/p2p.c:239 +msgid "Receive route announce.\n" +msgstr "" + +#: src/applications/vpn/p2p.c:247 +msgid "Going to try insert route into local table.\n" +msgstr "" + +#: src/applications/vpn/p2p.c:256 +#, c-format +msgid "Inserting with hops %d\n" +msgstr "" + +#: src/applications/vpn/p2p.c:273 +#, c-format +msgid "Request level %d from peer %d\n" +msgstr "" + +#: src/applications/vpn/p2p.c:300 +#, c-format +msgid "Receive table limit on peer reached %d\n" +msgstr "" + +#: src/applications/vpn/vpn.c:180 +#, c-format +msgid "Not storing route to myself from peer %d\n" +msgstr "" + +#: src/applications/vpn/vpn.c:194 +#, c-format +msgid "Duplicate route to node from peer %d, choosing minimum hops" +msgstr "" + +#: src/applications/vpn/vpn.c:230 +#, c-format +msgid "Inserting route from peer %d in route table at location %d\n" +msgstr "" + +#: src/applications/vpn/vpn.c:247 +#, c-format +msgid "RFC4193 Frame length %d is too big for GNUnet!\n" +msgstr "" + +#: src/applications/vpn/vpn.c:254 +#, c-format +msgid "RFC4193 Frame length %d too small\n" +msgstr "" + +#: src/applications/vpn/vpn.c:273 +#, c-format +msgid "RFC4193 Ethertype %x and IP version %x do not match!\n" +msgstr "" + +#: src/applications/vpn/vpn.c:289 +#, c-format +msgid "RFC4193 Going to try and make a tunnel in slot %d\n" +msgstr "" + +#: src/applications/vpn/vpn.c:295 +#, fuzzy, c-format +msgid "Cannot open tunnel device: %s" +msgstr "Imposible abrir el fichero de configuración '%s'.\n" + +#: src/applications/vpn/vpn.c:331 +#, c-format +msgid "RFC4193 Create skips gnu%d as we are already using it\n" +msgstr "" + +#: src/applications/vpn/vpn.c:346 +#, c-format +msgid "Cannot set tunnel name to %s because of %s\n" +msgstr "" + +#: src/applications/vpn/vpn.c:356 +#, c-format +msgid "Configured tunnel name to %s\n" +msgstr "" + +#: src/applications/vpn/vpn.c:398 +#, c-format +msgid "Cannot get socket flags for gnu%d because %s\n" +msgstr "" + +#: src/applications/vpn/vpn.c:408 +#, c-format +msgid "Cannot set socket flags for gnu%d because %s\n" +msgstr "" + +#: src/applications/vpn/vpn.c:418 +#, c-format +msgid "Cannot set MTU for gnu%d because %s\n" +msgstr "" + +#: src/applications/vpn/vpn.c:426 +#, c-format +msgid "Cannot get interface index for gnu%d because %s\n" +msgstr "" + +#: src/applications/vpn/vpn.c:440 +#, c-format +msgid "IPv6 ifaddr gnu%d - %x:%x:%x:%x:%x:%x:%x:%x/%d\n" +msgstr "" + +#: src/applications/vpn/vpn.c:455 +#, fuzzy, c-format +msgid "Cannot set interface IPv6 address for gnu%d because %s\n" +msgstr "Imposible obtener la dirección IP para la interfaz '%s'.\n" + +#: src/applications/vpn/vpn.c:471 +#, c-format +msgid "IPv6 route gnu%d - destination %x:%x:%x:%x:%x:%x:%x:%x/%d\n" +msgstr "" + +#: src/applications/vpn/vpn.c:485 +#, fuzzy, c-format +msgid "Cannot add route IPv6 address for gnu%s because %s\n" +msgstr "Imposible obtener la dirección IP para la interfaz '%s'.\n" + +#: src/applications/vpn/vpn.c:528 +msgid "" +"RFC4193 We have run out of memory and so I can't store a tunnel for this " +"peer.\n" +msgstr "" + +#: src/applications/vpn/vpn.c:579 +#, c-format +msgid "RFC4193 Thread running (frame %d tunnel %d f2f %d) ...\n" +msgstr "" + +#: src/applications/vpn/vpn.c:661 +#, c-format +msgid "VPN dropping connection %x\n" +msgstr "" + +#: src/applications/vpn/vpn.c:670 +#, c-format +msgid "VPN cannot drop connection %x\n" +msgstr "" + +#: src/applications/vpn/vpn.c:690 +msgid "RFC4193 Thread exiting\n" +msgstr "" + +#: src/applications/vpn/vpn.c:712 +msgid "realise alloc ram\n" +msgstr "" + +#: src/applications/vpn/vpn.c:735 +msgid "realise add routes\n" +msgstr "" + +#: src/applications/vpn/vpn.c:849 +msgid "realise copy table\n" +msgstr "" + +#: src/applications/vpn/vpn.c:898 +#, fuzzy, c-format +msgid "`%s' initialising RFC4913 module %d and %d\n" +msgstr "'%s' registrando manejadores %d y %d\n" + +#: src/applications/vpn/vpn.c:903 +#, c-format +msgid "RFC4193 my First 4 hex digits of host id are %x\n" +msgstr "" + +#: src/applications/vpn/vpn.c:942 +#, fuzzy +msgid "enables IPv6 over GNUnet (incomplete)" +msgstr "Activa el chat P2P (incompleto)" + +#: src/applications/vpn/vpn.c:963 +msgid "RFC4193 Waiting for tun thread to end\n" +msgstr "" + +#: src/applications/vpn/vpn.c:978 +msgid "RFC4193 The tun thread has ended\n" +msgstr "" + +#: src/applications/vpn/vpn.c:996 +#, c-format +msgid "RFC4193 Closing tunnel %d fd %d\n" +msgstr "" + +#: src/server/core.c:119 src/server/core.c:318 +#, c-format +msgid "Configuration value `%s' under [MODULES] for `%s' is invalid!\n" +msgstr "" + +#: src/server/core.c:140 +#, c-format +msgid "Application module `%s' already initialized!\n" +msgstr "¡El módulo de la aplicación '%s' ya esta inicializado!\n" + +#: src/server/core.c:194 +#, c-format +msgid "Failed to load plugin `%s' at %s:%d. Unloading plugin.\n" +msgstr "Falló al cargar el plugin '%s' en %s:%d. Descargando plugin.\n" + +#: src/server/core.c:244 +#, c-format +msgid "Could not shutdown `%s': application not loaded\n" +msgstr "Imposible apagar '%s': aplicación no cargada\n" + +#: src/server/core.c:255 +#, c-format +msgid "Could not shutdown application `%s': not initialized\n" +msgstr "Imposible apagar la aplicación '%s': no inicializada\n" + +#: src/server/core.c:265 +#, c-format +msgid "Could not find '%s%s' method in library `%s'.\n" +msgstr "Imposible encontrar el método '%s%s' en la biblioteca '%s'.\n" + +#: src/server/core.c:422 +#, c-format +msgid "Could not release %p: service not loaded\n" +msgstr "" + +#: src/server/core.c:531 +#, c-format +msgid "Could not properly shutdown application `%s'.\n" +msgstr "Imposible apagar la aplicación '%s' adecuadamente.\n" + +#: src/server/core.c:676 +#, c-format +msgid "Could not properly unload service `%s'!\n" +msgstr "¡Imposible descargar adecuadamente el servicio '%s'!\n" + +#: src/server/gnunet-update.c:146 +#, c-format +msgid "Updating data for module `%s'\n" +msgstr "Actualizando los datos del módulo '%s'\n" + +#: src/server/gnunet-update.c:151 +#, c-format +msgid "Failed to update data for module `%s'\n" +msgstr "Falló al actualizar los datos del módulo '%s'\n" + +#: src/server/gnunet-update.c:225 src/server/gnunetd.c:124 +#, fuzzy +msgid "Core initialization failed.\n" +msgstr "Conexión fallida\n" + +#: src/server/gnunet-update.c:270 +msgid "Updates GNUnet datastructures after version change." +msgstr "" + +#: src/server/gnunet-update.c:274 src/server/gnunet-transport-check.c:376 +msgid "run as user LOGIN" +msgstr "ejecuta como el usuario LOGIN" + +#: src/server/gnunet-update.c:278 +msgid "run in client mode (for getting client configuration values)" +msgstr "" + +#: src/server/version.c:125 +msgid "" +"Failed to determine filename used to store GNUnet version information!\n" +msgstr "" + +#: src/server/gnunetd.c:85 +#, c-format +msgid "`%s' startup complete.\n" +msgstr "'%s' comienzo completo.\n" + +#: src/server/gnunetd.c:89 +#, c-format +msgid "`%s' is shutting down.\n" +msgstr "'%s' se esta cerrando.\n" + +#: src/server/gnunetd.c:179 +msgid "" +"run in debug mode; gnunetd will not daemonize and error messages will be " +"written to stderr instead of a logfile" +msgstr "" +"se ejecuta en modo de depuración; gnunetd no sera un demonio y los mensajes " +"de error serán escritos a través de stderr en vez de en un fichero de log" + +#: src/server/gnunetd.c:183 +msgid "Starts the gnunetd daemon." +msgstr "Arranca el demonio de gnunetd." + +#: src/server/gnunetd.c:186 +msgid "disable padding with random data (experimental)" +msgstr "" + +#: src/server/gnunetd.c:190 +msgid "print all log messages to the console (only works together with -d)" +msgstr "" + +#: src/server/gnunetd.c:194 +#, fuzzy +msgid "specify username as which gnunetd should run" +msgstr "especifica el host en el que gnunetd esta ejecutandose" + +#: src/server/gnunetd.c:275 +#, c-format +msgid "Configuration or GNUnet version changed. You need to run `%s'!\n" +msgstr "" +"La configuración o la versión de GNUnet cambiaron. ¡Debes ejecutar '%s'!\n" + +#: src/server/tcpserver.c:121 +#, c-format +msgid "The `%s' request received from client is malformed.\n" +msgstr "La petición '%s' recibida del cliente esta mal formada.\n" + +#: src/server/tcpserver.c:409 +#, fuzzy, c-format +msgid "`%s' failed for port %d. Is gnunetd already running?\n" +msgstr "" +"'%s' falló para el puerto %d: %s. ¿Está gnunet ejecutandose actualmente?\n" + +#: src/server/tcpserver.c:487 src/server/tcpserver.c:512 +#, c-format +msgid "" +"Malformed network specification in the configuration in section `%s' for " +"entry `%s': %s\n" +msgstr "" + +#: src/server/tcpserver.c:572 +#, fuzzy, c-format +msgid "Registering failed, message type %d already in use.\n" +msgstr "%s falló, el mensaje del tipo %d ya está en uso.\n" + +#: src/server/startup.c:219 +#, fuzzy, c-format +msgid "Unable to obtain filesystem information for `%s': %u\n" +msgstr "Imposible guardar el fichero de configuración '%s': %s.\n" + +#: src/server/startup.c:237 +#, c-format +msgid "" +"Filesystem `%s' of partition `%s' is unknown. Please contact gnunet-" +"developers@gnu.org!" +msgstr "" + +#: src/server/startup.c:252 +#, c-format +msgid "" +"Limiting datastore size to %llu GB, because the `%s' filesystem does not " +"support larger files. Please consider storing the database on a NTFS " +"partition!\n" +msgstr "" + +#: src/server/startup.c:291 +#, c-format +msgid "Insufficient access permissions for `%s': %s\n" +msgstr "" + +#: src/server/gnunet-peer-info.c:55 +msgid "Print information about GNUnet peers." +msgstr "Imprime información de los pares de GNUnet." + +#: src/server/gnunet-peer-info.c:59 +msgid "don't resolve host names" +msgstr "" + +#: src/server/gnunet-peer-info.c:62 +msgid "output only the identity strings" +msgstr "" + +#: src/server/gnunet-peer-info.c:65 +msgid "output our own identity only" +msgstr "" + +#: src/server/gnunet-peer-info.c:130 src/server/gnunet-peer-info.c:164 +#, c-format +msgid "Could not get address of peer `%s'.\n" +msgstr "Imposible obtener la dirección del par '%s'.\n" + +#: src/server/gnunet-peer-info.c:143 +#, fuzzy, c-format +msgid "`%s' message invalid (signature invalid).\n" +msgstr "mensaje de saludo inválido (firma no válida).\n" + +#: src/server/gnunet-peer-info.c:168 +#, fuzzy, c-format +msgid "Peer `%s' with trust %8u\n" +msgstr "Par '%s' con credibilidad %8u y dirección '%s'\n" + +#: src/server/gnunet-peer-info.c:175 +#, c-format +msgid "Peer `%s' with trust %8u and address `%s'\n" +msgstr "Par '%s' con credibilidad %8u y dirección '%s'\n" + +#: src/server/connection.c:1313 +#, c-format +msgid "`%s' selected %d out of %d messages (MTU: %d).\n" +msgstr "" + +#: src/server/connection.c:1323 +#, c-format +msgid "Message details: %u: length %d, priority: %d\n" +msgstr "Detalles del mensaje: %u: longitud %d, prioridad: %d\n" + +#: src/server/connection.c:3129 +#, c-format +msgid "Message from `%s' discarded: invalid format.\n" +msgstr "Mensaje de '%s' descartado: formato inválido.\n" + +#: src/server/connection.c:3218 +#, c-format +msgid "Invalid sequence number %u <= %u, dropping message.\n" +msgstr "Secuencia de números no válida %u <= %u, omitiendo mensaje.\n" + +#: src/server/connection.c:3240 +msgid "Message received more than one day old. Dropped.\n" +msgstr "Mensajes recibidos de mas de un día de antigüedad. Omitidos.\n" + +#: src/server/connection.c:3763 +msgid "# outgoing messages dropped" +msgstr "# mensajes salientes omitidos" + +#: src/server/connection.c:3766 +msgid "# bytes of outgoing messages dropped" +msgstr "# bytes de mensajes salientes omitidos" + +#: src/server/connection.c:3768 +msgid "# connections closed (HANGUP sent)" +msgstr "" + +#: src/server/connection.c:3772 +msgid "# connections closed (transport issue)" +msgstr "" + +#: src/server/connection.c:3775 +msgid "# bytes encrypted" +msgstr "# bytes encriptados" + +#: src/server/connection.c:3779 +#, fuzzy +msgid "# bytes transmitted" +msgstr "# bytes desencriptados" + +#: src/server/connection.c:3783 +#, fuzzy +msgid "# bytes received" +msgstr "# bytes recibidos por TCP" + +#: src/server/connection.c:3785 +msgid "# bytes decrypted" +msgstr "# bytes desencriptados" + +#: src/server/connection.c:3786 +msgid "# bytes noise sent" +msgstr "# \"bytes-ruido\" mandados" + +#: src/server/connection.c:3789 +msgid "# total bytes per second send limit" +msgstr "" + +#: src/server/connection.c:3792 +#, fuzzy +msgid "# total bytes per second receive limit" +msgstr "# bytes de ruido recibidos" + +#: src/server/connection.c:3795 +#, fuzzy +msgid "# total number of messages in send buffers" +msgstr "número de mensajes en un bloque de mensajes" + +#: src/server/connection.c:3798 +#, fuzzy +msgid "# total number of bytes we were allowed to send but did not" +msgstr "número de mensajes en un bloque de mensajes" + +#: src/server/connection.c:3801 +#, fuzzy +msgid "# total number of bytes we were allowed to sent" +msgstr "número de mensajes en un bloque de mensajes" + +#: src/server/connection.c:3804 +msgid "# total number of bytes we are currently allowed to send" +msgstr "" + +#: src/server/connection.c:3807 +msgid "# transports switched to stream transport" +msgstr "" + +#: src/server/connection.c:3810 +msgid "# average connection lifetime (in ms)" +msgstr "" + +#: src/server/connection.c:3813 +msgid "# conn. shutdown: other peer sent too much" +msgstr "" + +#: src/server/connection.c:3816 +msgid "# conn. shutdown: we lacked bandwidth" +msgstr "" + +#: src/server/connection.c:3819 +msgid "# conn. shutdown: other peer timed out" +msgstr "" + +#: src/server/connection.c:3822 +msgid "# conn. shutdown: timed out during connect" +msgstr "" + +#: src/server/connection.c:3825 +msgid "# conn. shutdown: other peer requested it" +msgstr "" + +#: src/server/handler.c:442 +#, fuzzy, c-format +msgid "Received corrupt message from peer `%s' in %s:%d.\n" +msgstr "Recibido mensaje corrupto del par '%s' en %s:%d.\n" + +#: src/server/gnunet-transport-check.c:121 +#, c-format +msgid "`%s': Could not create hello.\n" +msgstr "'%s': Imposible crear saludo.\n" + +#: src/server/gnunet-transport-check.c:129 +#, c-format +msgid "`%s': Could not connect.\n" +msgstr "'%s': Imposible conectar.\n" + +#: src/server/gnunet-transport-check.c:163 +#, c-format +msgid "`%s': Could not send.\n" +msgstr "'%s': Imposible mandar.\n" + +#: src/server/gnunet-transport-check.c:179 +#, c-format +msgid "`%s': Did not receive message within %llu ms.\n" +msgstr "'%s': No se recibió el mensaje en %llu ms.\n" + +#: src/server/gnunet-transport-check.c:192 +#, c-format +msgid "`%s': Could not disconnect.\n" +msgstr "'%s': Imposible desconectar.\n" + +#: src/server/gnunet-transport-check.c:200 +#, fuzzy, c-format +msgid "" +"`%s' transport OK. It took %ums to transmit %llu messages of %llu bytes " +"each.\n" +msgstr "" +"Transporte '%s' OK. Tomó %ums transmitir %d mensajes de %d bytes cada uno.\n" + +#: src/server/gnunet-transport-check.c:231 +#, c-format +msgid " Transport %d is not being tested\n" +msgstr " El transporte %d no esta siendo probado\n" + +#: src/server/gnunet-transport-check.c:261 +#, c-format +msgid "" +"\n" +"Contacting `%s'." +msgstr "" +"\n" +"Contactando '%s'." + +#: src/server/gnunet-transport-check.c:286 +#, c-format +msgid " Connection failed\n" +msgstr "Conexión fallida\n" + +#: src/server/gnunet-transport-check.c:292 +#, c-format +msgid " Connection failed (bug?)\n" +msgstr "Conexión fallida (¿bug?)\n" + +#: src/server/gnunet-transport-check.c:330 +#, c-format +msgid "Timeout after %llums.\n" +msgstr "" + +#: src/server/gnunet-transport-check.c:332 +#, fuzzy, c-format +msgid "OK!\n" +msgstr "OK" + +#: src/server/gnunet-transport-check.c:357 +msgid "Tool to test if GNUnet transport services are operational." +msgstr "" + +#: src/server/gnunet-transport-check.c:361 +msgid "ping peers from HOSTLISTURL that match transports" +msgstr "" + +#: src/server/gnunet-transport-check.c:364 +msgid "send COUNT messages" +msgstr "envia COUNT mensajes" + +#: src/server/gnunet-transport-check.c:367 +msgid "send messages with SIZE bytes payload" +msgstr "manda mensajes con SIZE bytes payload" + +#: src/server/gnunet-transport-check.c:370 +msgid "specifies which TRANSPORT should be tested" +msgstr "" + +#: src/server/gnunet-transport-check.c:373 +msgid "specifies after how many MS to time-out" +msgstr "" + +#: src/server/gnunet-transport-check.c:381 +msgid "repeat each test X times" +msgstr "" + +#: src/server/gnunet-transport-check.c:449 +#, c-format +msgid "Testing transport(s) %s\n" +msgstr "Probando transporte(s) %s\n" + +#: src/server/gnunet-transport-check.c:451 +#, c-format +msgid "Available transport(s): %s\n" +msgstr "Transporte(s) disponible(s): %s\n" + +#: src/server/gnunet-transport-check.c:501 +#, c-format +msgid "" +"\n" +"%d out of %d peers contacted successfully (%d times transport unavailable).\n" +msgstr "" + +#: src/transports/common.c:370 +#, c-format +msgid "Port is 0, will only send using %s.\n" +msgstr "" + +#: src/transports/smtp.c:367 src/transports/udp.c:107 src/transports/tcp.c:271 +#: src/transports/tcp.c:291 +#, fuzzy, c-format +msgid "Received malformed message via %s. Ignored.\n" +msgstr "Recibido el mensaje '%s' con un mal formato. Omitiendo.\n" + +#: src/transports/smtp.c:459 +msgid "SMTP filter string to invalid, lacks ': '\n" +msgstr "" + +#: src/transports/smtp.c:469 +#, c-format +msgid "SMTP filter string to long, capped to `%s'\n" +msgstr "" + +#: src/transports/smtp.c:564 src/transports/smtp.c:575 +#: src/transports/smtp.c:589 src/transports/smtp.c:609 +#: src/transports/smtp.c:634 src/transports/smtp.c:643 +#: src/transports/smtp.c:657 src/transports/smtp.c:669 +#, fuzzy, c-format +msgid "SMTP: `%s' failed: %s.\n" +msgstr "'%s' %s falló: %s\n" + +#: src/transports/smtp.c:814 +msgid "No email-address specified, can not start SMTP transport.\n" +msgstr "" + +#: src/transports/smtp.c:831 +#, fuzzy +msgid "# bytes received via SMTP" +msgstr "# bytes recibidos por TCP" + +#: src/transports/smtp.c:832 +#, fuzzy +msgid "# bytes sent via SMTP" +msgstr "# bytes enviados por TCP" + +#: src/transports/smtp.c:834 +#, fuzzy +msgid "# bytes dropped by SMTP (outgoing)" +msgstr "# bytes omitidos por TCP (salientes)" + +#: src/transports/upnp/upnp.c:431 +#, fuzzy, c-format +msgid "%s failed for url `%s' and post-data `%s' at %s:%d: `%s'\n" +msgstr "'%s' falló en %s: %d con error: '%s'.\n" + +#: src/transports/upnp/upnp.c:476 +#, c-format +msgid "upnp: NAT Returned IP: %s\n" +msgstr "" + +#: src/transports/http.c:2019 src/transports/tcp.c:811 +#, c-format +msgid "" +"The UPnP service could not be loaded. To disable UPnP, set the configuration " +"option \"UPNP\" in section \"%s\" to \"NO\"\n" +msgstr "" + +#: src/transports/http.c:2028 +msgid "# bytes received via HTTP" +msgstr "# bytes recibidos vía HTTP" + +#: src/transports/http.c:2029 +msgid "# bytes sent via HTTP" +msgstr "# bytes mandados vía HTTP" + +#: src/transports/http.c:2031 +msgid "# bytes dropped by HTTP (outgoing)" +msgstr "#bytes omitidos vía HTTP (salientes)" + +#: src/transports/http.c:2032 +msgid "# HTTP GET issued" +msgstr "" + +#: src/transports/http.c:2034 +#, fuzzy +msgid "# HTTP GET received" +msgstr "# bytes recibidos por TCP" + +#: src/transports/http.c:2035 +msgid "# HTTP PUT issued" +msgstr "" + +#: src/transports/http.c:2037 +#, fuzzy +msgid "# HTTP PUT received" +msgstr "# bytes recibidos por TCP" + +#: src/transports/http.c:2039 +msgid "# HTTP select calls" +msgstr "" + +#: src/transports/http.c:2041 +msgid "# HTTP send calls" +msgstr "" + +#: src/transports/http.c:2044 +msgid "# HTTP curl send callbacks" +msgstr "" + +#: src/transports/http.c:2046 +#, fuzzy +msgid "# HTTP curl receive callbacks" +msgstr "# bytes recibidos por TCP" + +#: src/transports/http.c:2048 +msgid "# HTTP mhd access callbacks" +msgstr "" + +#: src/transports/http.c:2050 +msgid "# HTTP mhd read callbacks" +msgstr "" + +#: src/transports/http.c:2052 +msgid "# HTTP mhd close callbacks" +msgstr "" + +#: src/transports/http.c:2054 +#, fuzzy +msgid "# HTTP connect calls" +msgstr "# de pares conectados" + +#: src/transports/ip.c:70 src/transports/ip.c:365 +#, fuzzy, c-format +msgid "Failed to obtain my (external) %s address!\n" +msgstr "¡Falló al obtener mi dirección IP (externa)!\n" + +#: src/transports/udp.c:472 src/transports/tcp.c:728 +#, fuzzy, c-format +msgid "Failed to bind to %s port %d.\n" +msgstr "Fallo al conectar a gnunetd.\n" + +#: src/transports/udp.c:538 +#, c-format +msgid "MTU %llu for `%s' is probably too low!\n" +msgstr "" + +#: src/transports/udp.c:562 +msgid "# bytes received via UDP" +msgstr "# bytes recibidos vía UDP" + +#: src/transports/udp.c:563 +msgid "# bytes sent via UDP" +msgstr "# bytes enviados vía UDP" + +#: src/transports/udp.c:565 +msgid "# bytes dropped by UDP (outgoing)" +msgstr "# bytes omitidos por UDP (salientes)" + +#: src/transports/udp.c:567 +msgid "# UDP connections (right now)" +msgstr "" + +#: src/transports/tcp.c:821 +msgid "# bytes received via TCP" +msgstr "# bytes recibidos por TCP" + +#: src/transports/tcp.c:822 +msgid "# bytes sent via TCP" +msgstr "# bytes enviados por TCP" + +#: src/transports/tcp.c:824 +msgid "# bytes dropped by TCP (outgoing)" +msgstr "# bytes omitidos por TCP (salientes)" + +#: src/include/gnunet_util_getopt.h:154 +msgid "print this help" +msgstr "imprime esta ayuda" + +#: src/include/gnunet_util_getopt.h:163 +msgid "print the version number" +msgstr "imprime el número de versión" + +#: src/include/gnunet_util_getopt.h:169 +msgid "configure logging to use LOGLEVEL" +msgstr "" + +#: src/include/gnunet_util_getopt.h:175 +msgid "be verbose" +msgstr "" + +#: src/include/gnunet_util_getopt.h:181 +#, fuzzy +msgid "use configuration file FILENAME" +msgstr "usa el fichero de configuración FILENAME" + +#: src/include/gnunet_util_getopt.h:187 +#, fuzzy +msgid "specify host on which gnunetd is running" +msgstr "especifica el host en el que gnunetd esta ejecutandose" + +#: src/include/gnunet_util_error.h:219 src/include/gnunet_util_error.h:224 +#: src/include/gnunet_util_error.h:230 src/include/gnunet_util_error.h:232 +#, fuzzy, c-format +msgid "Internal error: assertion failed at %s:%d.\n" +msgstr "La verificación de la firma RSA fallo en %s: %d: %s\n" + +#: src/include/gnunet_util_error.h:242 +#, c-format +msgid "" +"External protocol violation: assertion failed at %s:%d (no need to panic, we " +"can handle this).\n" +msgstr "" + +#: src/include/gnunet_util_error.h:270 src/include/gnunet_util_error.h:277 +#, fuzzy, c-format +msgid "`%s' failed on file `%s' at %s:%d with error: %s\n" +msgstr "'%s' falló en el fichero '%s' en %s: %d con el error: %s\n" + +#: contrib/config-daemon.scm:39 contrib/config-client.scm:40 +#, fuzzy +msgid "No help available." +msgstr "'%s' no esta disponible." + +#: contrib/config-daemon.scm:42 +msgid "" +"You can use 'make check' in src/transports/upnp/ to find out if your NAT " +"supports UPnP. You should disable this option if you are sure that you are " +"not behind a NAT. If your NAT box does not support UPnP, having this on " +"will not do much harm (only cost a small amount of resources)." +msgstr "" + +#: contrib/config-daemon.scm:54 contrib/config-client.scm:53 +msgid "Prompt for development and/or incomplete code" +msgstr "" + +#: contrib/config-daemon.scm:56 contrib/config-client.scm:55 +msgid "" +"If EXPERIMENTAL is set to NO, options for experimental code are not shown. " +"If in doubt, use NO.\n" +"\n" +"Some options apply to experimental code that maybe in a state of development " +"where the functionality, stability, or the level of testing is not yet high " +"enough for general use. These features are said to be of \"alpha\" " +"quality. If a feature is currently in alpha, uninformed use is discouraged " +"(since the developers then do not fancy \"Why doesn't this work?\" type " +"messages).\n" +"\n" +"However, active testing and qualified feedback of these features is always " +"welcome. Users should just be aware that alpha features may not meet the " +"normal level of reliability or it may fail to work in some special cases. " +"Bug reports are usually welcomed by the developers, but please read the " +"documents and and use for how to report problems." +msgstr "" + +#: contrib/config-daemon.scm:71 contrib/config-client.scm:70 +msgid "Show options for advanced users" +msgstr "" + +#: contrib/config-daemon.scm:73 contrib/config-client.scm:72 +msgid "" +"These are options that maybe difficult to understand for the beginner. These " +"options typically refer to features that allow tweaking of the " +"installation. If in a hurry, say NO." +msgstr "" + +#: contrib/config-daemon.scm:84 contrib/config-client.scm:83 +#, fuzzy +msgid "Show rarely used options" +msgstr "Mostrar todas las opciones" + +#: contrib/config-daemon.scm:86 contrib/config-client.scm:85 +msgid "" +"These are options that hardly anyone actually needs. If you plan on doing " +"development on GNUnet, you may want to look into these. If in doubt or in a " +"hurry, say NO." +msgstr "" + +#: contrib/config-daemon.scm:97 contrib/config-client.scm:96 +#, fuzzy +msgid "Meta-configuration" +msgstr "Configuración de GNUnet" + +#: contrib/config-daemon.scm:98 contrib/config-client.scm:97 +msgid "Which level of configuration should be available" +msgstr "" + +#: contrib/config-daemon.scm:115 +#, fuzzy +msgid "Full pathname of GNUnet HOME directory" +msgstr "Error en el formato del fichero (¿no es un directorio de GNUnet?)\n" + +#: contrib/config-daemon.scm:117 +msgid "" +"This gives the root-directory of the GNUnet installation. Make sure there is " +"some space left in that directory. :-) Users inserting or indexing files " +"will be able to store data in this directory up to the (global) quota " +"specified below. Having a few gigabytes of free space is recommended." +msgstr "" + +#: contrib/config-daemon.scm:130 +#, fuzzy +msgid "Full pathname of GNUnet directory for file-sharing data" +msgstr "Error en el formato del fichero (¿no es un directorio de GNUnet?)\n" + +#: contrib/config-daemon.scm:142 +#, fuzzy +msgid "Full pathname to the directory with the key-value database" +msgstr "Error en el formato del fichero (¿no es un directorio de GNUnet?)\n" + +#: contrib/config-daemon.scm:143 +msgid "Note that the kvstore is currently not used." +msgstr "" + +#: contrib/config-daemon.scm:154 +#, fuzzy +msgid "Full pathname of GNUnet directory for indexed files symbolic links" +msgstr "Error en el formato del fichero (¿no es un directorio de GNUnet?)\n" + +#: contrib/config-daemon.scm:166 +msgid "How many minutes should peer advertisements last?" +msgstr "" + +#: contrib/config-daemon.scm:168 +msgid "" +"How many minutes is the current IP valid? (GNUnet will sign HELLO messages " +"with this expiration timeline. If you are on dialup, 60 (for 1 hour) is " +"suggested. If you have a static IP address, you may want to set this to a " +"large value (say 14400). The default is 1440 (1 day). If your IP changes " +"periodically, you will want to choose an expiry period smaller than the " +"frequency with which your IP changes." +msgstr "" + +#: contrib/config-daemon.scm:179 +msgid "Where can GNUnet find an initial list of peers?" +msgstr "" + +#: contrib/config-daemon.scm:181 +msgid "" +"GNUnet can automatically update the hostlist from the web. While GNUnet " +"internally communicates which hosts are online, it is typically a good idea " +"to get a fresh hostlist whenever gnunetd starts from the WEB. By setting " +"this option, you can specify from which server gnunetd should try to " +"download the hostlist. The default should be fine for now.\n" +"\t\t\n" +"The general format is a list of space-separated URLs. Each URL must have " +"the format http://HOSTNAME/FILENAME\n" +"\t\t\n" +"If you want to setup an alternate hostlist server, you must run a permanent " +"node and \"cat data/hosts/* > hostlist\" every few minutes to keep the list " +"up-to-date.\n" +"\t\t\n" +"If you do not specify a HOSTLISTURL, you must copy valid hostkeys to data/" +"hosts manually." +msgstr "" + +#: contrib/config-daemon.scm:198 +msgid "HTTP Proxy Server" +msgstr "" + +#: contrib/config-daemon.scm:200 +msgid "" +"If you have to use a proxy for outbound HTTP connections, specify the proxy " +"configuration here. Default is no proxy." +msgstr "" + +#: contrib/config-daemon.scm:212 +msgid "" +"Name of the directory where gnunetd should store contact information about " +"peers" +msgstr "" + +#: contrib/config-daemon.scm:214 +msgid "" +"Unless you want to share the directory directly using a webserver, the " +"default is most likely just fine." +msgstr "" + +#: contrib/config-daemon.scm:240 contrib/config-client.scm:140 +msgid "How long should logs be kept?" +msgstr "" + +#: contrib/config-daemon.scm:242 contrib/config-client.scm:142 +msgid "" +"How long should logs be kept? If you specify a value greater than zero, a " +"log is created each day with the date appended to its filename. These logs " +"are deleted after $KEEPLOG days.\tTo keep logs forever, set this value to 0." +msgstr "" + +#: contrib/config-daemon.scm:253 +msgid "" +"What maximum number of open file descriptors should be requested from the OS?" +msgstr "" + +#: contrib/config-daemon.scm:255 +msgid "" +"The default of 1024 should be fine for most systems. If your system can " +"support more, increasing the number might help support additional clients on " +"machines with plenty of bandwidth. For embedded systems, a smaller number " +"might be acceptable. A value of 0 will leave the descriptor limit " +"untouched. This option is mostly for OS X systems where the default is too " +"low. Note that if gnunetd cannot obtain the desired number of file " +"descriptors from the operating system, it will print a warning and try to " +"run with what it is given." +msgstr "" + +#: contrib/config-daemon.scm:266 +msgid "Where should gnunetd write the logs?" +msgstr "" + +#: contrib/config-daemon.scm:278 +msgid "Enable for extra-verbose logging." +msgstr "" + +#: contrib/config-daemon.scm:290 contrib/config-client.scm:165 +msgid "Logging" +msgstr "" + +#: contrib/config-daemon.scm:291 contrib/config-client.scm:166 +msgid "Specify which system messages should be logged how" +msgstr "" + +#: contrib/config-daemon.scm:296 contrib/config-client.scm:170 +msgid "Logging of events for users" +msgstr "" + +#: contrib/config-daemon.scm:297 contrib/config-client.scm:171 +msgid "Logging of events for the system administrator" +msgstr "" + +#: contrib/config-daemon.scm:309 +msgid "Where should gnunetd write the PID?" +msgstr "" + +#: contrib/config-daemon.scm:310 +msgid "" +"The default is no longer /var/run/gnunetd.pid since we could not delete the " +"file on shutdown at that location." +msgstr "" + +#: contrib/config-daemon.scm:322 +msgid "As which user should gnunetd run?" +msgstr "" + +#: contrib/config-daemon.scm:324 +msgid "" +"Empty means \"current user\". On computer startup, it is root/SYSTEM. Under " +"Windows, this setting affects the creation of a new system service only." +msgstr "" + +#: contrib/config-daemon.scm:337 +msgid "Should gnunetd be automatically started when the system boots?" +msgstr "" + +#: contrib/config-daemon.scm:338 +msgid "" +"Set to YES if gnunetd should be automatically started on boot. If this " +"option is set, gnunet-setup will install a script to start the daemon upon " +"completion. This option may not work on all systems." +msgstr "" + +#: contrib/config-daemon.scm:350 +msgid "Which transport mechanisms should GNUnet use?" +msgstr "" + +#: contrib/config-daemon.scm:352 +msgid "" +"Use a space-separated list of modules, e.g. \"udp smtp tcp\". The " +"available transports are udp, tcp, http, smtp and nat.\n" +"\t\t\n" +"Loading the 'nat' and 'tcp' modules is required for peers behind NAT boxes " +"that cannot directly be reached from the outside. Peers that are NOT behind " +"a NAT box and that want to *allow* peers that ARE behind a NAT box to " +"connect must ALSO load the 'nat' module. Note that the actual transfer will " +"always be via tcp initiated by the peer behind the NAT box. The nat " +"transport requires the use of tcp, http and/or smtp in addition to nat " +"itself." +msgstr "" + +#: contrib/config-daemon.scm:366 +msgid "Which applications should gnunetd support?" +msgstr "" + +#: contrib/config-daemon.scm:368 +msgid "" +"Whenever this option is changed, you MUST run gnunet-update. Currently, the " +"available applications are:\n" +"\n" +"advertising: advertises your peer to other peers. Without it, your peer will " +"not participate in informing peers about other peers. You should always " +"load this module.\n" +"\n" +"getoption: allows clients to query gnunetd about the values of various " +"configuration options. Many tools need this. You should always load this " +"module.\n" +"\n" +"stats: allows tools like gnunet-stats and gnunet-gtk to query gnunetd about " +"various statistics. This information is usually quite useful to diagnose " +"errors, hence it is recommended that you load this module.\n" +"\n" +"traffic: keeps track of how many messages were recently received and " +"transmitted. This information can then be used to establish how much cover " +"traffic is currently available. The amount of cover traffic becomes " +"important if you want to make anonymous requests with an anonymity level " +"that is greater than one. It is recommended that you load this module.\n" +"\n" +"fs: needed for anonymous file sharing. You should always load this module.\n" +"\n" +"hostlist: integrated hostlist HTTP server. Useful if you want to offer a " +"hostlist and running Apache would be overkill.\n" +"\n" +"chat: broadcast chat (demo-application, ALPHA quality).\tRequired for gnunet-" +"chat. Note that the current implementation of chat is not considered to be " +"secure.\n" +"\n" +"tbench: benchmark transport performance. Required for gnunet-tbench. Note " +"that tbench allows other users to abuse your resources.\n" +"\n" +"tracekit: topology visualization toolkit. Required for gnunet-tracekit. " +"Note that loading tracekit will make it slightly easier for an adversary to " +"compromise your anonymity." +msgstr "" + +#: contrib/config-daemon.scm:399 +msgid "Disable client-server connections" +msgstr "" + +#: contrib/config-daemon.scm:400 +msgid "" +"This option can be used to tell gnunetd not to open the client port. When " +"run like this, gnunetd will participate as a peer in the network but not " +"support any user interfaces. This may be useful for headless systems that " +"are never expected to have end-user interactions. Note that this will also " +"prevent you from running diagnostic tools like gnunet-stats!" +msgstr "" + +#: contrib/config-daemon.scm:412 +msgid "YES disables IPv6 support, NO enables IPv6 support" +msgstr "" + +#: contrib/config-daemon.scm:413 +msgid "" +"This option may be useful on peers where the kernel does not support IPv6. " +"You might also want to set this option if you do not have an IPv6 network " +"connection." +msgstr "" + +#: contrib/config-daemon.scm:425 +msgid "Disable peer discovery" +msgstr "" + +#: contrib/config-daemon.scm:426 +msgid "" +"The option 'PRIVATE-NETWORK' can be used to limit the connections of this " +"peer to peers of which the hostkey has been copied by hand to data/hosts; " +"if this option is given, GNUnet will not accept advertisements of peers that " +"the local node does not already know about. Note that in order for this " +"option to work, HOSTLISTURL should either not be set at all or be set to a " +"trusted peer that only advertises the private network. Also, the option does " +"NOT work at the moment if the NAT transport is loaded; for that, a couple of " +"lines above would need some minor editing :-)." +msgstr "" + +#: contrib/config-daemon.scm:437 +msgid "Disable advertising this peer to other peers" +msgstr "" + +#: contrib/config-daemon.scm:449 +#, fuzzy +msgid "Disable automatic establishment of connections" +msgstr "Se produjo un error estableciendo conexión con gnunetd.\n" + +#: contrib/config-daemon.scm:450 +msgid "" +"If this option is enabled, GNUnet will not automatically establish " +"connections to other peers, but instead wait for applications to " +"specifically request connections to other peers (or for other peers to " +"connect to us)." +msgstr "" + +#: contrib/config-daemon.scm:461 +msgid "Enable advertising of other peers by this peer" +msgstr "" + +#: contrib/config-daemon.scm:462 +msgid "" +"This option may be useful during testing, but turning it off is dangerous! " +"If in any doubt, set it to YES (which is the default)." +msgstr "" + +#: contrib/config-daemon.scm:473 +#, fuzzy +msgid "Port for communication with GNUnet user interfaces" +msgstr "Imprime información de los pares de GNUnet." + +#: contrib/config-daemon.scm:474 +msgid "" +"Which is the client-server port that is used between gnunetd and the clients " +"(TCP only). You may firewall this port for non-local machines (but you do " +"not have to since GNUnet will perform access control and only allow " +"connections from machines that are listed under TRUSTED)." +msgstr "" + +#: contrib/config-daemon.scm:485 +msgid "Port for the integrated hostlist HTTP server" +msgstr "" + +#: contrib/config-daemon.scm:497 +msgid "IPv4 networks allowed to use gnunetd server" +msgstr "" + +#: contrib/config-daemon.scm:498 contrib/config-daemon.scm:510 +msgid "" +"This option specifies which hosts are trusted enough to connect as clients " +"(to the TCP port). This is useful if you run gnunetd on one host of your " +"network and want to allow all other hosts to use this node as their server. " +"By default, this is set to 'loopback only'. The format is IP/NETMASK where " +"the IP is specified in dotted-decimal and the netmask either in CIDR " +"notation (/16) or in dotted decimal (255.255.0.0). Several entries must be " +"separated by a semicolon, spaces are not allowed." +msgstr "" + +#: contrib/config-daemon.scm:509 +msgid "IPv6 networks allowed to use gnunetd server" +msgstr "" + +#: contrib/config-daemon.scm:522 +msgid "Limit connections to the specfied set of peers." +msgstr "" + +#: contrib/config-daemon.scm:523 +msgid "" +"If this option is not set, any peer is allowed to connect. If it is set, " +"only the specified peers are allowed. Specify the list of peer IDs (not IPs!)" +msgstr "" + +#: contrib/config-daemon.scm:534 +#, fuzzy +msgid "Run gnunetd as this group." +msgstr "Ejecutar gnunet-update" + +#: contrib/config-daemon.scm:535 +msgid "" +"When started as root, gnunetd will change permissions to the given group." +msgstr "" + +#: contrib/config-daemon.scm:546 +msgid "Prevent the specfied set of peers from connecting." +msgstr "" + +#: contrib/config-daemon.scm:547 +msgid "" +"If this option is not set, any peer is allowed to connect. If the ID of a " +"peer is listed here, connections from that peer will be refused. Specify " +"the list of peer IDs (not IPs!)" +msgstr "" + +#: contrib/config-daemon.scm:558 +msgid "Topology Maintenance" +msgstr "" + +#: contrib/config-daemon.scm:559 +msgid "Rarely used settings for peer advertisements and connections" +msgstr "" + +#: contrib/config-daemon.scm:579 +#, fuzzy +msgid "General settings" +msgstr "Otras configuraciones" + +#: contrib/config-daemon.scm:580 +msgid "Settings that change the behavior of GNUnet in general" +msgstr "" + +#: contrib/config-daemon.scm:607 +msgid "Modules" +msgstr "" + +#: contrib/config-daemon.scm:608 +msgid "Settings that select specific implementations for GNUnet modules" +msgstr "" + +#: contrib/config-daemon.scm:626 +msgid "Fundamentals" +msgstr "" + +#: contrib/config-daemon.scm:646 +msgid "Which database should be used?" +msgstr "" + +#: contrib/config-daemon.scm:648 +msgid "" +"Which database should be used? The options are \"sqstore_sqlite\", " +"\"sqstore_postgres\" and \"sqstore_mysql\". You must run gnunet-update " +"after changing this value!\n" +"\t\t\t\n" +"In order to use MySQL or Postgres, you must configure the respective " +"database, which is relatively simple. Read the file doc/README.mysql or doc/" +"README.postgres for how to setup the respective database." +msgstr "" + +#: contrib/config-daemon.scm:661 contrib/config-daemon.scm:674 +msgid "Which topology should be used?" +msgstr "" + +#: contrib/config-daemon.scm:662 +msgid "Which database should be used for the temporary datastore of the DHT?" +msgstr "" + +#: contrib/config-daemon.scm:676 +msgid "" +"Which topology should be used? The only option at the moment is " +"\"topology_default\"" +msgstr "" + +#: contrib/config-daemon.scm:690 +msgid "" +"The minimum number of connected friends before this peer is allowed to " +"connect to peers that are not listed as friends" +msgstr "" + +#: contrib/config-daemon.scm:691 +msgid "" +"Note that this option does not guarantee that the peer will be able to " +"connect to the specified number of friends. Also, if the peer had connected " +"to a sufficient number of friends and then established non-friend " +"connections, some of the friends may drop out of the network, temporarily " +"resulting in having fewer than the specified number of friends connected " +"while being connected to non-friends. However, it is guaranteed that the " +"peer itself will never choose to drop a friend's connection if this would " +"result in dropping below the specified number of friends (unless that number " +"is higher than the overall connection target)." +msgstr "" + +#: contrib/config-daemon.scm:702 +msgid "" +"If set to YES, the peer is only allowed to connect to other peers that are " +"explicitly specified as friends" +msgstr "" + +#: contrib/config-daemon.scm:703 +msgid "" +"Use YES only if you have (trustworthy) friends that use GNUnet and are " +"afraid of establishing (direct) connections to unknown peers" +msgstr "" + +#: contrib/config-daemon.scm:714 +msgid "List of friends for friend-to-friend topology" +msgstr "" + +#: contrib/config-daemon.scm:715 +msgid "" +"Specifies the name of a file which contains a list of GNUnet peer IDs that " +"are friends. If used with the friend-to-friend topology, this will ensure " +"that GNUnet only connects to these peers (via any available transport)." +msgstr "" + +#: contrib/config-daemon.scm:726 +msgid "Friend-to-Friend Topology Specification" +msgstr "" + +#: contrib/config-daemon.scm:727 +#, fuzzy +msgid "Settings for restricting connections to friends" +msgstr "'%s' falló. Finalizando conexión con el cleinte.\n" + +#: contrib/config-daemon.scm:744 +#, fuzzy +msgid "Name of the MySQL database GNUnet should use" +msgstr "Imposible inicializar SQLite.\n" + +#: contrib/config-daemon.scm:756 +#, fuzzy +msgid "Configuration file that specifies the MySQL username and password" +msgstr "" +"La configuración o la versión de GNUnet cambiaron. ¡Debes ejecutar '%s'!\n" + +#: contrib/config-daemon.scm:768 +#, fuzzy +msgid "Configuration of the MySQL database" +msgstr "" +"La configuración o la versión de GNUnet cambiaron. ¡Debes ejecutar '%s'!\n" + +#: contrib/config-daemon.scm:787 +#, fuzzy +msgid "MB of diskspace GNUnet can use for anonymous file sharing" +msgstr "activa la compartición de ficheros anónima" + +#: contrib/config-daemon.scm:789 +msgid "" +"How much disk space (MB) is GNUnet allowed to use for anonymous file " +"sharing? This does not take indexed files into account, only the space " +"directly used by GNUnet is accounted for. GNUnet will gather content from " +"the network if the current space-consumption is below the number given here " +"(and if content migration is allowed below).\n" +"\n" +"Note that if you change the quota, you need to run gnunet-update afterwards." +msgstr "" + +#: contrib/config-daemon.scm:803 +#, fuzzy +msgid "Number of entries in the migration buffer" +msgstr "número de mensajes en un bloque de mensajes" + +#: contrib/config-daemon.scm:804 +msgid "" +"Each entry uses about 32k of memory. More entries can reduce disk IO and " +"CPU usage at the expense of having gnunetd use more memory. Very large " +"values may again increase CPU usage. A value of 0 will prevent your peer " +"from sending unsolicited responses." +msgstr "" + +#: contrib/config-daemon.scm:816 +msgid "Size of the routing table for anonymous routing." +msgstr "" + +#: contrib/config-daemon.scm:828 +msgid "Size of the routing table for DHT routing." +msgstr "" + +#: contrib/config-daemon.scm:841 +msgid "Allow migrating content to this peer." +msgstr "" + +#: contrib/config-daemon.scm:843 +msgid "" +"If you say yes here, GNUnet will migrate content to your server, and you " +"will not be able to control what data is stored on your machine. \n" +"\t\t\t\n" +"If you activate it, you can claim for *all* the non-indexed (-n to gnunet-" +"insert) content that you did not know what it was even if an adversary takes " +"control of your machine. If you do not activate it, it is obvious that you " +"have knowledge of all the content that is hosted on your machine and thus " +"can be considered liable for it." +msgstr "" + +#: contrib/config-daemon.scm:857 +#, fuzzy +msgid "" +"MB of diskspace GNUnet can use for caching DHT index data (the data will be " +"stored in /tmp)" +msgstr "activa la compartición de ficheros anónima" + +#: contrib/config-daemon.scm:858 +msgid "" +"DHT index data is inherently small and expires comparatively quickly. It is " +"deleted whenever gnunetd is shut down.\n" +"\n" +"The size of the DSTORE QUOTA is specified in MB." +msgstr "" + +#: contrib/config-daemon.scm:872 +#, fuzzy +msgid "Options for anonymous file sharing" +msgstr "activa la compartición de ficheros anónima" + +#: contrib/config-daemon.scm:891 +#, fuzzy +msgid "Applications" +msgstr "_Opciones" + +#: contrib/config-daemon.scm:907 +msgid "Is this machine unreachable behind a NAT?" +msgstr "" + +#: contrib/config-daemon.scm:908 +msgid "" +"Set to YES if this machine is behind a NAT that limits connections from the " +"outside to the GNUnet port and that cannot be traversed using UPnP. Note " +"that if you have configured your NAT box to allow direct connections from " +"other machines to the GNUnet ports or if GNUnet can open ports using UPnP, " +"you should set the option to NO. Set this only to YES if other peers cannot " +"contact you directly. You can use 'make check' in src/transports/upnp/ to " +"find out if your NAT supports UPnP. You can also use gnunet-transport-check " +"with the '-p' option in order to determine which setting results in more " +"connections. Use YES only if you get no connections otherwise. Set to AUTO " +"to use YES if the local IP is belongs to a private IP network and NO " +"otherwise." +msgstr "" + +#: contrib/config-daemon.scm:919 +msgid "Which port should be used by the TCP IPv4 transport?" +msgstr "" + +#: contrib/config-daemon.scm:931 contrib/config-daemon.scm:1024 +#: contrib/config-daemon.scm:1174 +msgid "Should we try to determine our external IP using UPnP?" +msgstr "" + +#: contrib/config-daemon.scm:943 +msgid "Which IP(v4)s are not allowed to connect?" +msgstr "" + +#: contrib/config-daemon.scm:955 +msgid "" +"Which IP(v4)s are allowed to connect? Leave empty to use the IP of your " +"primary network interface." +msgstr "" + +#: contrib/config-daemon.scm:967 contrib/config-daemon.scm:1222 +msgid "Which IPv6s are not allowed to connect?" +msgstr "" + +#: contrib/config-daemon.scm:979 contrib/config-daemon.scm:1234 +msgid "" +"Which IPv6s are allowed to connect? Leave empty to allow any IP to connect." +msgstr "" + +#: contrib/config-daemon.scm:992 +msgid "TCP transport" +msgstr "" + +#: contrib/config-daemon.scm:1012 +msgid "Which port should be used by the HTTP transport?" +msgstr "" + +#: contrib/config-daemon.scm:1036 +msgid "Which is the external port of the HTTP transport?" +msgstr "" + +#: contrib/config-daemon.scm:1037 +msgid "" +"Use this option if your firewall maps, say, port 80 to your real HTTP port. " +"This can be useful in making the HTTP messages appear even more legit " +"(without needing to run gnunetd as root due to the use of a privileged port)." +msgstr "" + +#: contrib/config-daemon.scm:1048 +msgid "HTTP transport" +msgstr "" + +#: contrib/config-daemon.scm:1067 +msgid "What is the maximum transfer unit for SMTP?" +msgstr "" + +#: contrib/config-daemon.scm:1079 +msgid "" +"What is the maximum number of e-mails that gnunetd would be allowed to send " +"per hour?" +msgstr "" + +#: contrib/config-daemon.scm:1080 +msgid "Use 0 for unlimited" +msgstr "" + +#: contrib/config-daemon.scm:1091 +msgid "Which e-mail address should be used to send e-mail to this peer?" +msgstr "" + +#: contrib/config-daemon.scm:1092 +msgid "" +"You must make sure that e-mail received at this address is forwarded to the " +"PIPE which is read by gnunetd. Use the FILTER option to filter e-mail with " +"procmail and the PIPE option to set the name of the pipe." +msgstr "" + +#: contrib/config-daemon.scm:1103 +msgid "" +"Which header line should other peers include in e-mails to enable filtering?" +msgstr "" + +#: contrib/config-daemon.scm:1104 +msgid "" +"You can specify a header line here which can then be used by procmail to " +"filter GNUnet e-mail from your inbox and forward it to gnunetd." +msgstr "" + +#: contrib/config-daemon.scm:1115 +msgid "What is the filename of the pipe where gnunetd can read its e-mail?" +msgstr "" + +#: contrib/config-daemon.scm:1116 +msgid "Have a look at contrib/dot-procmailrc for an example .procmailrc file." +msgstr "" + +#: contrib/config-daemon.scm:1127 +msgid "What is the name and port of the server for outgoing e-mail?" +msgstr "" + +#: contrib/config-daemon.scm:1128 +msgid "The basic format is HOSTNAME:PORT." +msgstr "" + +#: contrib/config-daemon.scm:1139 +msgid "SMTP transport" +msgstr "" + +#: contrib/config-daemon.scm:1162 +msgid "Which port should be used by the UDP IPv4 transport?" +msgstr "" + +#: contrib/config-daemon.scm:1186 +msgid "What is the maximum transfer unit for UDP?" +msgstr "" + +#: contrib/config-daemon.scm:1198 +msgid "Which IPs are not allowed to connect?" +msgstr "" + +#: contrib/config-daemon.scm:1210 +msgid "" +"Which IPs are allowed to connect? Leave empty to allow connections from any " +"IP." +msgstr "" + +#: contrib/config-daemon.scm:1246 +msgid "UDP transport" +msgstr "" + +#: contrib/config-daemon.scm:1268 +#, fuzzy +msgid "Network interface" +msgstr "Interfaz de red:" + +#: contrib/config-daemon.scm:1280 +msgid "External IP address (leave empty to try auto-detection)" +msgstr "" + +#: contrib/config-daemon.scm:1292 +msgid "External IPv6 address (leave empty to try auto-detection)" +msgstr "" + +#: contrib/config-daemon.scm:1304 +msgid "Transports" +msgstr "" + +#: contrib/config-daemon.scm:1326 +msgid "What is the maximum number of bytes per second that we may receive?" +msgstr "" + +#: contrib/config-daemon.scm:1338 +msgid "What is the maximum number of bytes per second that we may send?" +msgstr "" + +#: contrib/config-daemon.scm:1350 +msgid "What is the maximum CPU load (percentage)?" +msgstr "" + +#: contrib/config-daemon.scm:1351 +msgid "" +"The highest tolerable CPU load. Load here always refers to the total system " +"load, that is it includes CPU utilization by other processes. A value of 50 " +"means that once your 1 minute-load average goes over 50% non-idle, GNUnet " +"will try to reduce CPU consumption until the load goes under the threshold. " +"Reasonable values are typically between 50 and 100. Multiprocessors may use " +"values above 100." +msgstr "" + +#: contrib/config-daemon.scm:1362 +msgid "What is the maximum IO load (permille)?" +msgstr "" + +#: contrib/config-daemon.scm:1364 +msgid "" +"The highest tolerable IO load. Load here refers to the percentage of CPU " +"cycles wasted waiting for IO for the entire system, that is it includes disk " +"utilization by other processes. A value of 10 means that once the average " +"number of cycles wasted waiting for IO is more than 10% non-idle, GNUnet " +"will try to reduce IO until the load goes under the threshold. Reasonable " +"values are typically between 10 and 75." +msgstr "" + +#: contrib/config-daemon.scm:1375 +msgid "What is the maximum CPU load (hard limit)?" +msgstr "" + +#: contrib/config-daemon.scm:1376 +msgid "" +"The highest tolerable CPU load. This is the hard limit, so once it is " +"reached, gnunetd will start to massively drop data to reduce the load. Use " +"with caution." +msgstr "" + +#: contrib/config-daemon.scm:1387 +msgid "What is the maximum upstream bandwidth (hard limit)?" +msgstr "" + +#: contrib/config-daemon.scm:1388 +msgid "" +"The limit is given as a percentage of the MAXNETUPBPS limit. Use 100 to " +"have MAXNETUPBPS be the hard limit. Use zero for no limit." +msgstr "" + +#: contrib/config-daemon.scm:1400 +msgid "What priority should gnunetd use to run?" +msgstr "" + +#: contrib/config-daemon.scm:1401 +msgid "" +"You can specify priorities like NORMAL, ABOVE NORMAL, BELOW NORMAL, HIGH and " +"IDLE or a numerical integer value (man nice). The default is IDLE, which " +"should result in gnunetd only using resources that would otherwise be idle." +msgstr "" + +#: contrib/config-daemon.scm:1413 +msgid "Should we disable random padding (experimental option)?" +msgstr "" + +#: contrib/config-daemon.scm:1425 +msgid "Use basic bandwidth limitation? (YES/NO). If in doubt, say YES." +msgstr "" + +#: contrib/config-daemon.scm:1427 +msgid "" +"Basic bandwidth limitation (YES) means simply that the bandwidth limits " +"specified apply to GNUnet and only to GNUnet. If set to YES, you simply " +"specify the maximum bandwidth (upstream and downstream) that GNUnet is " +"allowed to use and GNUnet will stick to those limitations. This is useful " +"if your overall bandwidth is so large that the limit is mostly used to " +"ensure that enough capacity is left for other applications. Even if you " +"want to dedicate your entire connection to GNUnet you should not set the " +"limits to values higher than what you have since GNUnet uses those limits to " +"determine for example the number of connections to establish (and it would " +"be inefficient if that computation yields a number that is far too high). \n" +"\n" +"While basic bandwidth limitation is simple and always works, there are some " +"situations where it is not perfect. Suppose you are running another " +"application which performs a larger download. During that particular time, " +"it would be nice if GNUnet would throttle its bandwidth consumption " +"(automatically) and resume using more bandwidth after the download is " +"complete. This is obviously advanced magic since GNUnet will have to " +"monitor the behavior of other applications. Another scenario is a monthly " +"cap on bandwidth imposed by your ISP, which you would want to ensure is " +"obeyed. Here, you may want GNUnet to monitor the traffic from other " +"applications to ensure that the combined long-term traffic is within the pre-" +"set bounds. Note that you should probably not set the bounds tightly since " +"GNUnet may observe that the bounds are about to be broken but would be " +"unable to stop other applications from continuing to use bandwidth.\n" +"\n" +"If either of these two scenarios applies, set BASICLIMITING to NO. Then set " +"the bandwidth limits to the COMBINED amount of traffic that is acceptable " +"for both GNUnet and other applications. GNUnet will then immediately " +"throttle bandwidth consumption if the short-term average is above the limit, " +"and it will also try to ensure that the long-term average is below the " +"limit. Note however that using NO can have the effect of GNUnet (almost) " +"ceasing operations after other applications perform high-volume downloads " +"that are beyond the defined limits. GNUnet would reduce consumption until " +"the long-term limits are again within bounds.\n" +"\n" +"NO only works on platforms where GNUnet can monitor the amount of traffic " +"that the local host puts out on the network. This is only implemented for " +"Linux and Win32. In order for the code to work, GNUnet needs to know the " +"specific network interface that is used for the external connection (after " +"all, the amount of traffic on loopback or on the LAN should never be counted " +"since it is irrelevant)." +msgstr "" + +#: contrib/config-daemon.scm:1444 +#, fuzzy +msgid "Network interface to monitor" +msgstr "Interfaz de red:" + +#: contrib/config-daemon.scm:1445 +msgid "" +"For which interfaces should we do accounting? GNUnet will evaluate the " +"total traffic (not only the GNUnet related traffic) and adjust its bandwidth " +"usage accordingly. You can currently only specify a single interface. GNUnet " +"will also use this interface to determine the IP to use. Typical values are " +"eth0, ppp0, eth1, wlan0, etc. 'ifconfig' will tell you what you have. " +"Never use 'lo', that just won't work. Under Windows, specify the index " +"number reported by 'gnunet-win-tool -n'." +msgstr "" + +#: contrib/config-daemon.scm:1456 +#, fuzzy +msgid "Load management" +msgstr "Argumentos en la linea de comandos inválidos.\n" + +#: contrib/config-daemon.scm:1482 contrib/config-client.scm:413 +msgid "Root node" +msgstr "" + +#: contrib/config-client.scm:153 +msgid "Where should gnunet-clients write their logs?" +msgstr "" + +#: contrib/config-client.scm:185 +msgid "On which machine and port is gnunetd running (for clients)?" +msgstr "" + +#: contrib/config-client.scm:186 +#, fuzzy +msgid "This is equivalent to the -H option. The format is IP:PORT." +msgstr "Muestra el valor de la opción" + +#: contrib/config-client.scm:197 +#, fuzzy +msgid "What is the path to the configuration file for gnunetd?" +msgstr "Esta es la herramienta de configuración de GNUnet." + +#: contrib/config-client.scm:198 +msgid "This option is used when clients need to start gnunetd." +msgstr "" + +#: contrib/config-client.scm:210 +#, fuzzy +msgid "General options" +msgstr "Otras configuraciones" + +#: contrib/config-client.scm:227 +msgid "Do not add metadata listing the creation time for inserted content" +msgstr "" + +#: contrib/config-client.scm:239 +msgid "Which non-default extractors should GNUnet use for keyword extractors" +msgstr "" + +#: contrib/config-client.scm:240 +msgid "" +"Specify which additional extractor libraries should be used. gnunet-insert " +"uses libextractor to extract keywords from files. libextractor can be " +"dynamically extended to handle additional file formats. If you want to use " +"more than the default set of extractors, specify additional extractor " +"libraries here. The format is [[-]LIBRARYNAME[:[-]LIBRARYNAME]*].\n" +"\n" +"The default is to use filenames and to break larger words at spaces (and " +"underscores, etc.). This should be just fine for most people. The '-' " +"before a library name indicates that this should be executed last and makes " +"only sense for the split-library." +msgstr "" + +#: contrib/config-client.scm:253 +msgid "How many entries should the URI DB table have?" +msgstr "" + +#: contrib/config-client.scm:254 +msgid "" +"GNUnet uses two bytes per entry on the disk. This database is used to keep " +"track of how a particular URI has been used in the past. For example, " +"GNUnet may remember that a particular URI has been found in a search " +"previously or corresponds to a file uploaded by the user. This information " +"can then be used by user-interfaces to filter URI lists, such as search " +"results. If the database is full, older entries will be discarded. The " +"default value should be sufficient without causing undue disk utilization." +msgstr "" + +#: contrib/config-client.scm:265 +msgid "Location of the file specifying metadata for the auto-share directory" +msgstr "" + +#: contrib/config-client.scm:277 +msgid "" +"Location of the file with the PID of any running gnunet-auto-share daemon " +"process" +msgstr "" + +#: contrib/config-client.scm:289 +msgid "Location of the log file for gnunet-auto-share" +msgstr "" + +#: contrib/config-client.scm:301 +msgid "File-Sharing options" +msgstr "" + +#: contrib/config-client.scm:319 +msgid "Which plugins should be loaded by gnunet-gtk?" +msgstr "" + +#: contrib/config-client.scm:320 +msgid "" +"Load the about plugin for the about dialog. The daemon plugin allows " +"starting and stopping of gnunetd and displays information about gnunetd. " +"The fs plugin provides the file-sharing functionality. The stats plugin " +"displays various statistics about gnunetd." +msgstr "" + +#: contrib/config-client.scm:331 +msgid "How frequently (in milli-seconds) should the statistics update?" +msgstr "" + +#: contrib/config-client.scm:332 +msgid "" +"Each pixel in the stats dialog corresponds to the time interval specified " +"here." +msgstr "" + +#: contrib/config-client.scm:344 +msgid "Do not show thumbnail previews from meta-data in search results" +msgstr "" + +#: contrib/config-client.scm:345 +msgid "" +"This option is useful for people who maybe offended by some previews or use " +"gnunet-gtk at work and would like to avoid bad surprises." +msgstr "" + +#: contrib/config-client.scm:356 +msgid "Do not show search results for files that were uploaded by us" +msgstr "" + +#: contrib/config-client.scm:357 +msgid "" +"This option is useful to eliminate files that the user already has from the " +"search. Naturally, enabling this option maybe confusing because some " +"obviously expected search results would no longer show up. This option only " +"works if the URI_DB_SIZE option under FS is not zero (since the URI DB is " +"used to determine which files the user is sharing)" +msgstr "" + +#: contrib/config-client.scm:369 +msgid "To which directory should gnunet-gtk save downloads to?" +msgstr "" + +#: contrib/config-client.scm:381 +msgid "Options related to gnunet-gtk" +msgstr "" + +#: contrib/config-client.scm:401 +#, fuzzy +msgid "Full pathname of GNUnet client HOME directory" +msgstr "Error en el formato del fichero (¿no es un directorio de GNUnet?)\n" + +#: contrib/config-client.scm:402 +msgid "The directory for GNUnet files that belong to the user." +msgstr "" + +#, fuzzy +#~ msgid "Failed to connect to %s:%u in %ds\n" +#~ msgstr "Fallo al conectar a gnunetd.\n" + +#, fuzzy +#~ msgid "Failed to connect to %s:%u\n" +#~ msgstr "Fallo al conectar a gnunetd.\n" + +#, fuzzy +#~ msgid "`%s' failed at %s:%d in %s with error: %s\n" +#~ msgstr "'%s' falló en %s: %d con el error: %s\n" + +#, fuzzy +#~ msgid "`%s' failed at %s:%d with error: %s" +#~ msgstr "'%s' falló en %s: %d con el error: %s\n" + +#, fuzzy +#~ msgid "`%s' failed at %s:%d with error `%s' after %llums\n" +#~ msgstr "'%s' falló en %s: %d con el error: %s\n" + +#~ msgid "Trying to use file `%s' for MySQL configuration.\n" +#~ msgstr "Intentando usar el fichero '%s' para la configuración de MySQL.\n" + +#~ msgid "'%s(%s,%s)' succeeded\n" +#~ msgstr "'%s (%s, %s)' logrado satisfactoriamente\n" + +#~ msgid "'%s(%s,%s)' failed.\n" +#~ msgstr "'%s(%s, %s)' falló.\n" + +#, fuzzy +#~ msgid "" +#~ "Directory `%s' in directory `%s' does not match naming convention. " +#~ "Removed.\n" +#~ msgstr "" +#~ "El fichero '%s' en el directorio '%s' no sigue la convención de nombres. " +#~ "Eliminando.\n" + +#, fuzzy +#~ msgid "You must specify one and only one directory for sharing.\n" +#~ msgstr "Debes especificar uno y solo un fichero para desindexar.\n" + +#~ msgid "" +#~ "set interval for availability of updates to SECONDS (for namespace " +#~ "insertions only)" +#~ msgstr "" +#~ "cambia el intervalo de disponibilidad de las actualizaciones a SEGUNDOS " +#~ "(para inserciones en el espacio solamente)" + +#~ msgid "" +#~ "specifies this as an aperiodic but updated publication (for namespace " +#~ "insertions only)" +#~ msgstr "" +#~ "especifica esto como una aperiódica pero actualizada publicación (para " +#~ "inserciones en el espacio únicamente)" + +#~ msgid "specify creation time for SBlock (see man-page for format)" +#~ msgstr "" +#~ "especifica el tiempo para la creación de un Superbloque (vea la página " +#~ "del manual para el formato)" + +#~ msgid "" +#~ "ID of the previous version of the content (for namespace update only)" +#~ msgstr "" +#~ "ID de la versión previa del contenido (para actualizaciones del espacio " +#~ "únicamente)" + +#~ msgid "Parsing time failed. Use `%s' format.\n" +#~ msgstr "Se produjo un fallo al analizar el tiempo. Use el formato '%s'.\n" + +#, fuzzy +#~ msgid "File `%s' does not contain a pseudonym, trying to remove.\n" +#~ msgstr "El fichero '%s' no contiene un pseudónimo.\n" + +#~ msgid "Format of file `%s' is invalid.\n" +#~ msgstr "El formato del fichero '%s' no es válido.\n" + +#~ msgid "Publication interval for periodic publication changed." +#~ msgstr "Intervalo de publicación periódica cambiado." + +#~ msgid "" +#~ "Publishing update for periodically updated content more than a week ahead " +#~ "of schedule.\n" +#~ msgstr "" +#~ "publicaciones actualizadas para contenido actualizado periódicamente más " +#~ "de una semana antes de la tarea planificada.\n" + +#, fuzzy +#~ msgid "Received malformed message via TCP. Closing.\n" +#~ msgstr "Recibido el mensaje '%s' con un mal formato. Omitiendo.\n" + +#~ msgid "Cannot create pseudonym `%s', file `%s' exists.\n" +#~ msgstr "Imposible crear el pseudónimo '%s', el fichero '%s' ya existe.\n" + +#, fuzzy +#~ msgid "Error writing to socket.\n" +#~ msgstr "Error creando usuario" + +#~ msgid "GNUnet configuration assistant" +#~ msgstr "Asistente de configuración de GNUnet" + +#~ msgid "" +#~ "Welcome to GNUnet!\n" +#~ "\n" +#~ "This assistant will ask you a few basic questions in order to configure " +#~ "GNUnet.\n" +#~ "\n" +#~ "Please visit our homepage at\n" +#~ "\thttp://gnunet.org\n" +#~ "and join our community at\n" +#~ "\thttps://gnunet.org/drupal/\n" +#~ "\n" +#~ "Have a lot of fun,\n" +#~ "\n" +#~ "The GNUnet team" +#~ msgstr "" +#~ "¡Bienvenido a GNUnet!\n" +#~ "\n" +#~ "Este asistente le preguntará unos datos básicos para configurar GNUnet.\n" +#~ "\n" +#~ "Por favor, visite nuestra página en\n" +#~ "\thttps://gnunet.org/drupal/\n" +#~ "\n" +#~ "Diviertase,\n" +#~ "\n" +#~ "El equipo de GNUnet" + +#~ msgid "Next" +#~ msgstr "Siguiente" + +#~ msgid "" +#~ "Enter information about your network connection here.\n" +#~ "\n" +#~ "The \"Network interface\" is the device that connects your computer to " +#~ "the internet. This is usually a modem, an ISDN card or a network card in " +#~ "case you are using DSL.\n" +#~ "\n" +#~ "If your provider always assigns the same IP-Address to you (a \"static\" " +#~ "IP-Address), enter it into the \"IP-Address\" field. If your IP-Address " +#~ "changes every now and then (\"dynamic\" IP-Address) but there's a " +#~ "hostname that always points to your actual IP-Address (\"Dynamic DNS\"), " +#~ "you can also enter it here.\n" +#~ "If in doubt, leave the field empty. GNUnet will then try to determine " +#~ "your IP-Address.\n" +#~ "\n" +#~ "If you are connected to the internet through another computer doing SNAT, " +#~ "a router or a \"hardware firewall\" and other computers on the internet " +#~ "cannot connect to this computer, check the last option on this page. " +#~ "Leave it unchecked on direct connections through modems, ISDN cards and " +#~ "DNAT (also known as \"port forwarding\")." +#~ msgstr "" +#~ "Introduce información acerca de tu conexión aquí.\n" +#~ "\n" +#~ "La \"Interfaz de red\" es el dispositivo que conecta tu ordenador a " +#~ "Internet. Normalmente es un módem, una tarjeta de RDSI, un módem ADSL o " +#~ "una tarjeta de red en el caso de un router.\n" +#~ "\n" +#~ "Si tu proveedor siempre te da la misma dirección IP (una dirección IP " +#~ "\"estática\") introducelo en el campo \"Dirección IP\". Si tu dirección " +#~ "IP cambia (una dirección IP \"dinámica\") pero hay un nombre de dominio " +#~ "que siempre apunta a tu dirección IP actual (\"DNS dinámica\") también " +#~ "puedes introducirla aquí.\n" +#~ "En caso de duda deja el campo vacío. GNUnet intentara determinar tu " +#~ "dirección IP automaticamente.\n" +#~ "\n" +#~ "Si tu estas conectado a Internet a través de otro ordenador haciendo " +#~ "SNAT, a un router o a un \"cortafuegos de hardware\" y otros ordenadores " +#~ "no pueden conectar con tu ordenador marca la última opción en ésta " +#~ "página. Déjala sin marcar en conexiones directas a través de modems, " +#~ "tarjetas de RDSI y DNAT (también conocido como \"seguimiento de puertos" +#~ "\")." + +#~ msgid "Computer cannot receive inbound connections (SNAT/Firewall)" +#~ msgstr "" +#~ "El ordenador no puede recibir conexiones entrantes (SNAT/Cortafuegos)" + +#~ msgid "IP-Address/Hostname:" +#~ msgstr "Dirección IP/Nombre del dominio" + +#, fuzzy +#~ msgid "Network interface:" +#~ msgstr "Interfaz de red:" + +#, fuzzy +#~ msgid "Network connection" +#~ msgstr "Configuración de GNUnet" + +#~ msgid "" +#~ "You can limit GNUnet's ressource usage here.\n" +#~ "\n" +#~ "\"Bandwidth limitation\" is how much data may be sent per second. If you " +#~ "have a flatrate you can set it to the maximum speed of your internet " +#~ "connection.\n" +#~ "\n" +#~ "The \"Max. CPU usage\" is the percentage of processor time GNUnet is " +#~ "allowed to use." +#~ msgstr "" +#~ "Puedes limitar el uso de recursos de GNUnet aquí.\n" +#~ "\n" +#~ "\"Limitación de ancho de banda\" es cuantos datos pueden ser mandados por " +#~ "segundo. Si tienes una tarifa plana puedes seleccionar la velocidad " +#~ "máxima de tu conexión a Internet.\n" +#~ "\n" +#~ "El \"Uso máximo de CPU\" es el porcentaje del tiempo de procesado que " +#~ "GNUnet puede usar." + +#~ msgid "Downstream (Bytes/s):" +#~ msgstr "Bajada (Bytes/s):" + +#~ msgid "Upstream (Bytes/s):" +#~ msgstr "Subida (Bytes/s):" + +#~ msgid "Bandwidth limitation" +#~ msgstr "Limitación del ancho de banda" + +#~ msgid "Use denoted bandwidth for GNUnet" +#~ msgstr "Usa el ancho de banda marcado para GNUnet" + +#~ msgid "Share denoted bandwidth with other applications" +#~ msgstr "Comparte el ancho de banda marcado con otras aplicaciones" + +#~ msgid "Bandwidth sharing" +#~ msgstr "Ancho de banda compartido" + +#~ msgid "Max. CPU usage (%):" +#~ msgstr "Uso máximo de la CPU (%):" + +#~ msgid "CPU usage" +#~ msgstr "Uso de CPU" + +#~ msgid "Load limitation" +#~ msgstr "Limitación de carga" + +#~ msgid "" +#~ "GNUnet is able to store data from other peers in your datastore. This is " +#~ "useful if an adversary has access to your inserted content and you need " +#~ "to deny that the content is yours. With \"content migration\" on, the " +#~ "content could have \"migrated\" over the internet to your node without " +#~ "your knowledge.\n" +#~ "It also helps to spread popular content over different peers to enhance " +#~ "availability.\n" +#~ "\n" +#~ "The GNUnet datastore contains all data that GNUnet generates (index data, " +#~ "inserted and migrated content). Its maximum size can be specified below.\n" +#~ "\n" +#~ "If you are an experienced user, you may want to tweak your GNUnet " +#~ "installation using the enhanced configurator.\n" +#~ "\n" +#~ "After changing the configuration and/or updating GNUnet, it is sometimes " +#~ "required to run gnunet-update to update internal data structures. " +#~ "Depending on the changes made, this may take some time." +#~ msgstr "" +#~ "GNUnet es capaz de almacenar datos de otros pares en tu base de datos. " +#~ "Ésto es útil si un adversario tiene acceso a tu contenido y tu necesitas " +#~ "negar que ese contenido sea tuyo. Con la \"migración de contenido\" " +#~ "activada, tu contenido puede haber \"migrado\" por Internet hasta tu nodo " +#~ "sin tu conocimiento.\n" +#~ "Ésto también ayuda a difundir contenidos populares por diferentes pares " +#~ "para facilitar su disponibilidad.\n" +#~ "\n" +#~ "La base de datos de GNUnet contiene todos los datos que GNUnet genera " +#~ "(índices, contenido insertado y descargado). Su máximo tamaño puede ser " +#~ "especificado debajo.\n" +#~ "\n" +#~ "Si eres un usuario experimentado, puedes desear exprimir tu instalación " +#~ "de GNUnet usando la herramienta de configuración avanzada.\n" +#~ "\n" +#~ "Después de cambiar la configuración y/o actualizar GNUnet, a veces " +#~ "esnecesario ejecutar gnunet-update para actualizar las estructuras de " +#~ "datos internas. Dependiendo de los cambios que se hayan hecho, ésto podrá " +#~ "tomar algún tiempo." + +#~ msgid "Store migrated content" +#~ msgstr "Almacena contenido migrado" + +#~ msgid "Maximum datastore size (MB):" +#~ msgstr "Máximo tamaño de almacenamiento (MB):" + +#~ msgid "Start the GNUnet background process on computer startup" +#~ msgstr "Arranca el demonio de GNUnet al encender el ordenador" + +#~ msgid "Open the enhanced configurator" +#~ msgstr "Abre el editor de configuración avanzado" + +#, fuzzy +#~ msgid "Run gnunet-update" +#~ msgstr "¡gnunet-update falló!" + +#, fuzzy +#~ msgid "Other settings" +#~ msgstr "Otras configuraciones" + +#~ msgid "Finish" +#~ msgstr "Finalizar" + +#~ msgid "" +#~ "Define the user and the group owning the GNUnet service here.\n" +#~ "\n" +#~ "For security reasons, it is a good idea to let this setup create a new " +#~ "user account and a new group under which the GNUnet service is started at " +#~ "system startup.\n" +#~ "\n" +#~ "However, GNUnet may not be able to access files other than its own. This " +#~ "includes files you want to publish in GNUnet. You'll have to grant read " +#~ "permissions to the user specified below.\n" +#~ "\n" +#~ "Leave the fields empty to run GNUnet with system privileges." +#~ msgstr "" +#~ "Define el usuario y el grupo bajo el que correrán los servicios de GNUnet " +#~ "aquí.\n" +#~ "\n" +#~ "Por razones de seguridad, es una buena idea dejar que la configuración " +#~ "cree una nueva cuenta de usuario y un nuevo grupo bajo el cual el " +#~ "servicio de GNUnet es arrancado al iniciar el sistema.\n" +#~ "\n" +#~ "Por consiguiente, GNUnet no tiene la posibilidad de acceder a otros " +#~ "ficheros que no sean los que posee. Ésto incluye los ficheros que quieras " +#~ "publicar en GNUnet. Tendrás que garantizar que el usuario especificado " +#~ "aquí posea permisos de lectura.\n" +#~ "\n" +#~ "Deja los campos vacíos para arrancar GNUnet con privilegios de sistema." + +#~ msgid "User account:" +#~ msgstr "Cuenta de usuario:" + +#~ msgid "Group:" +#~ msgstr "Grupo:" + +#~ msgid "gnunet-setup" +#~ msgstr "gnunet-setup" + +#, fuzzy +#~ msgid "Save configuration" +#~ msgstr "Configuración de GNUnet" + +#, fuzzy +#~ msgid "Show copyright information for gnunet-setup." +#~ msgstr "Se produjo un error leyendo información de gnunetd.\n" + +#, fuzzy +#~ msgid "About gnunet-setup" +#~ msgstr "gnunet-setup" + +#, fuzzy +#~ msgid "(C) 2001-2007 Christian Grothoff (and other contributing authors)" +#~ msgstr "" +#~ "C) 2001-2006 Christian Grothoff (y otros autores que han contribuido)" + +#~ msgid "This is the configuration tool for GNUnet." +#~ msgstr "Esta es la herramienta de configuración de GNUnet." + +#~ msgid "Could not unlink temporary file `%s': %s\n" +#~ msgstr "Imposible desenlazar temporalmente el fichero '%s': %s\n" + +#~ msgid "Write(%d, %p, %d) failed: %s\n" +#~ msgstr "Escribiendo(%d, %p, %d) se produjo un fallo: %s\n" + +#~ msgid "AND" +#~ msgstr "Y" + +#, fuzzy +#~ msgid "Could not find IPv6 address of host `%s': %s\n" +#~ msgstr "Imposible encontrar la IP del host '%s': %s\n" + +#, fuzzy +#~ msgid "Error running search (no reason given)." +#~ msgstr "Se produjo un fallo al comenzar la búsqueda. Consulte los logs.\n" + +#, fuzzy +#~ msgid "Download failed (no reason given)" +#~ msgstr "La descarga del ERCS falló (vea los logs)." + +#~ msgid "Message received from client is invalid\n" +#~ msgstr "El mensaje recibido del cliente es inválido\n" + +#~ msgid "Maximum number of chat clients reached.\n" +#~ msgstr "Número máximo de clientes en el chat alcanzado.\n" + +#~ msgid "Now %d of %d chat clients at this node.\n" +#~ msgstr "Ahora hay %d de %d clientes de chat en este nodo.\n" + +#, fuzzy +#~ msgid "Invalid data in %s (NCS). Trying to fix (by deletion).\n" +#~ msgstr "Datos no válidos en %s. Intentando fijar (por borrado).\n" + +#~ msgid "HTTP: Could not determine my public IP address.\n" +#~ msgstr "HTTP: Imposible determinar mi dirección IP pública.\n" + +#~ msgid "" +#~ "%s::%s - RPC %s:%p could not be unregistered: another callback registered " +#~ "under that name: %p\n" +#~ msgstr "" +#~ "%s::%s - RPC %s:%p no pudo ser desregistrada: otro evento está " +#~ "actualmente registrado con este nombre (%p)\n" + +#~ msgid "%s::%s - RPC %s:%p could not be unregistered: not found\n" +#~ msgstr "%s::%s - RPC %s:%p no pudo ser desregistrada: no encontrada\n" + +#~ msgid "Invalid message of type %u received. Dropping.\n" +#~ msgstr "Mensaje no válido del tipo %u recibido. Omitiendo.\n" + +#~ msgid "Dropping RPC request %u: message malformed.\n" +#~ msgstr "Omitiendo la petición RPC %u: mensaje mal formado.\n" + +#~ msgid "`%s' called with timeout above 1 hour (bug?)\n" +#~ msgstr "'%s' fue llamada con un retraso de una hora(¿bug?)\n" + +#~ msgid "RPC not unregistered: %s:%p\n" +#~ msgstr "No fue desregistrada RPC: %s:%p\n" + +#~ msgid "RPC async reply invalid.\n" +#~ msgstr "RPC respuesta asíncrona no válida.\n" + +#~ msgid "async RPC reply not received.\n" +#~ msgstr "respuesta RPC asíncrona no recibida.\n" + +#~ msgid "Could not determine my public IPv6 address.\n" +#~ msgstr "Imposible determinar mi dirección IPv6 pública.\n" + +#~ msgid "`%s': unknown service: %s\n" +#~ msgstr "'%s': servicio desconocido: %s\n" + +#, fuzzy +#~ msgid "# bytes received via TCP6" +#~ msgstr "# bytes recibidos por TCP" + +#, fuzzy +#~ msgid "# bytes sent via TCP6" +#~ msgstr "# bytes enviados por TCP" + +#, fuzzy +#~ msgid "# bytes dropped by TCP6 (outgoing)" +#~ msgstr "# bytes omitidos por TCP (salientes)" + +#~ msgid "UDP6: Could not determine my public IPv6 address.\n" +#~ msgstr "UDP6: Imposible determinar mi dirección IPv6 pública.\n" + +#, fuzzy +#~ msgid "# bytes received via UDP6" +#~ msgstr "# bytes recibidos vía UDP" + +#, fuzzy +#~ msgid "# bytes sent via UDP6" +#~ msgstr "# bytes enviados vía UDP" + +#, fuzzy +#~ msgid "# bytes dropped by UDP6 (outgoing)" +#~ msgstr "# bytes omitidos por UDP (salientes)" + +#, fuzzy +#~ msgid "Received malformed message instead of welcome message. Closing.\n" +#~ msgstr "Recibido el mensaje '%s' con un mal formato. Omitiendo.\n" + +#, fuzzy +#~ msgid "Received malformed message from tcp-peer connection. Closing.\n" +#~ msgstr "Recibido el mensaje '%s' con un mal formato. Omitiendo.\n" + +#, fuzzy +#~ msgid "TCP: Could not determine my public IP address.\n" +#~ msgstr "HTTP: Imposible determinar mi dirección IP pública.\n" + +#~ msgid "Cannot connect to %u.%u.%u.%u:%u: %s\n" +#~ msgstr "Imposible conectar a %u.%u.%u.%u:%u: %s\n" + +#~ msgid "UDP: Could not determine my public IP address.\n" +#~ msgstr "UDP: Imposible determinar mi dirección IP pública.\n" + +#~ msgid "Failed to send message of size %d via UDP to %u.%u.%u.%u:%u: %s\n" +#~ msgstr "" +#~ "Falló al mandar el mensaje de tamaño %d a través de UDP a %u.%u.%u.%u:%u: " +#~ "%s\n" + +#, fuzzy +#~ msgid "Received malformed message from udp-peer connection. Closing.\n" +#~ msgstr "Recibido el mensaje '%s' con un mal formato. Omitiendo.\n" + +#~ msgid "exit after receiving LIMIT results" +#~ msgstr "sale después de recibir LIMIT resultados" + +#, fuzzy +#~ msgid "wait DELAY seconds for search results before aborting" +#~ msgstr "espera TIMEOUT segundos para buscar resultados después de abortar" + +#~ msgid "Message received from peer is invalid.\n" +#~ msgstr "El mensaje recibido del par es inválido.\n" + +#~ msgid "GAP received invalid content from `%s'\n" +#~ msgstr "GAP recibido contenido no válido de '%s'\n" + +#~ msgid "myself" +#~ msgstr "yo" + +#~ msgid "" +#~ "Cover traffic requested but traffic service not loaded. Rejecting " +#~ "request.\n" +#~ msgstr "" +#~ "Solicitada la cobertura de tráfico pero el servicio de tráfico no ha sido " +#~ "cargado. Rechazando la petición.\n" + +#~ msgid "Cannot satisfy desired level of anonymity, ignoring request.\n" +#~ msgstr "" +#~ "Imposible satisfacer el nivel deseado de anonimato, ignorando la " +#~ "petición.\n" + +#~ msgid "`%s' registering handlers %d %d\n" +#~ msgstr "'%s' registrando manejadores %d %d\n" + +#, fuzzy +#~ msgid "Indexed file disappeared, deleting block for query `%s'\n" +#~ msgstr "" +#~ "Fichero indexado desaparecido, borrando el bloque para preguntas '%s'\n" + +#~ msgid "" +#~ "Configuration file must specify directory for storage of FS data in " +#~ "section `%s' under `%s'.\n" +#~ msgstr "" +#~ "El fichero de configuración debe especificar un directorio para el " +#~ "almacenamiento de los datos de FS en la sección '%s' bajo '%s'.\n" + +#~ msgid "" +#~ "You must specify a postive number for `%s' in the configuration in " +#~ "section `%s'.\n" +#~ msgstr "" +#~ "Debes especificar un número positivo para '%s' en la configuración en la " +#~ "sección '%s'.\n" + +#, fuzzy +#~ msgid "# FS valid replies received" +#~ msgstr "# bytes recibidos por TCP" + +#~ msgid "" +#~ "Content `%s' seems to be not available on the network (tried %u times).\n" +#~ msgstr "" +#~ "El contenido '%s' parece no estar disponible en la red (intentado %u " +#~ "veces).\n" + +#, fuzzy +#~ msgid "Start GNUnet-testbed helper." +#~ msgstr "Arranca el cliente de chat de GNUnet" + +#~ msgid "Cannot connect to LOOPBACK port %d: %s\n" +#~ msgstr "Imposible conectar al puerto de LOOPBACK %d: %s\n" + +#~ msgid "No client service started. Trying again in 30 seconds.\n" +#~ msgstr "" +#~ "No hay ningún servicio de cliente ejecutandose. Se volverá a intentar en " +#~ "30 segundos.\n" + +#, fuzzy +#~ msgid "" +#~ "Error (%s) binding the TCP listener to port %d. No proxy service " +#~ "started.\n" +#~ "Trying again in %d seconds...\n" +#~ msgstr "" +#~ "No hay ningún servicio de cliente ejecutandose. Se volverá a intentar en " +#~ "30 segundos.\n" + +#~ msgid "Rejected unauthorized connection from %u.%u.%u.%u.\n" +#~ msgstr "Rechazada conexión no autorizada de %u.%u.%u.%u.\n" + +#~ msgid "Protocol violation on socket. Expected command.\n" +#~ msgstr "Violación del protocolo en el socket. Esperando comando.\n" + +#~ msgid "Command `%s' not found!\n" +#~ msgstr "¡Comando '%s' no encontrado!\n" + +#, fuzzy +#~ msgid "Start GNUnet testbed controller." +#~ msgstr "Arranca el cliente de chat de GNUnet" + +#~ msgid "Malformed entry in the configuration in section %s under %s: %s\n" +#~ msgstr "" +#~ "Entrada mal formada en la configuración en la sección %s bajo %s: %s\n" + +#, fuzzy +#~ msgid "Could not send acknowledgement back to client.\n" +#~ msgstr "Imposible mandar el mensaje a gnunetd\n" + +#~ msgid "size of `%s' message is too short. Ignoring.\n" +#~ msgstr "el tamaño del '%s' mensaje es demasiado corto. Ignorandolo.\n" + +#~ msgid "size of `%s' message is wrong. Ignoring.\n" +#~ msgstr "el tamaño del mensaje '%s' es erroneo. Se ignora.\n" + +#~ msgid "TESTBED could not generate hello message for protocol %u\n" +#~ msgstr "" +#~ "TESTBED no puede generar el mensaje de saludo para el protocolo %u\n" + +#~ msgid "received invalid `%s' message\n" +#~ msgstr "recibido mensaje '%s' no válido\n" + +#~ msgid "received invalid `%s' message (empty module name)\n" +#~ msgstr "recibido mensaje '%s' no válido (nombre del módulo vacío)\n" + +#~ msgid "loading module `%s' failed. Notifying client.\n" +#~ msgstr "" +#~ "se produjo un fallo al cargar el módulo '%s'. Notificando al cliente.\n" + +#~ msgid "unloading module failed. Notifying client.\n" +#~ msgstr "" +#~ "se produjo un fallo al descargar el módulo. Notificando al cliente.\n" + +#~ msgid "received invalid `%s' message: %s.\n" +#~ msgstr "recibido mensaje '%s' no válido: %s.\n" + +#~ msgid "'..' is not allowed in file name (%s).\n" +#~ msgstr "'..' no esta permitido en el nombre del fichero(%s).\n" + +#~ msgid "Empty filename for UPLOAD_FILE message is invalid!\n" +#~ msgstr "" +#~ "¡Nombre del fichero vacío para el mensaje UPLOAD_FILE es inválido!\n" + +#~ msgid "Filename for UPLOAD_FILE message is not null-terminated (invalid!)\n" +#~ msgstr "" +#~ "Nombre del fichero para el mensaje UPLOAD_FILE no esta terminado en null " +#~ "(¡inválido!)\n" + +#~ msgid "Invalid message received at %s:%d." +#~ msgstr "Mensaje inválido recibido en %s:%d." + +#, fuzzy +#~ msgid "received invalid testbed message of size %u\n" +#~ msgstr "recibido mensaje '%s' no válido: %s.\n" + +#~ msgid "Could not resolve name of HTTP proxy `%s'.\n" +#~ msgstr "Imposible resolver el nombre del proxy HTTP '%s'.\n" + +#~ msgid "Invalid URL `%s' (must begin with `%s')\n" +#~ msgstr "URL invalida '%s' (debe comenzar por '%s')\n" + +#, fuzzy +#~ msgid "Could not register testbed, host `%s' unknown\n" +#~ msgstr "" +#~ "Imposible descargar lista de contactos, servidor '%s' desconocido.\n" + +#~ msgid "Failed to send HTTP request to host `%s': %s\n" +#~ msgstr "Falló al mandar la petición HTTP al host '%s': %s\n" + +#~ msgid "Failed so send HTTP request `%s' to host `%s': %s\n" +#~ msgstr "Falló al mandar la petición HTTP '%s' al host '%s': %s\n" + +#~ msgid "Exit register (error: no http response read).\n" +#~ msgstr "Saliendo del registro (error: no se leyó una respuesta de http).\n" + +#, fuzzy +#~ msgid "Description" +#~ msgstr "Pregunta" + +#, fuzzy +#~ msgid "Section" +#~ msgstr "Pregunta" + +#, fuzzy +#~ msgid "Option" +#~ msgstr "_Opciones" + +#, fuzzy +#~ msgid "Failed to initialize libesmtp: %s.\n" +#~ msgstr "Imposible inicializar SQLite.\n" + +#~ msgid "Sending E-mail to `%s' failed.\n" +#~ msgstr "Enviando un correo electrónico a '%s' se produjo un fallo.\n" + +#, fuzzy +#~ msgid "Network configuration: NAT" +#~ msgstr "Configuración de GNUnet" + +#~ msgid "" +#~ "Is this machine behind NAT?\n" +#~ "\n" +#~ "If you are connected to the internet through another computer doing SNAT, " +#~ "a router or a \"hardware firewall\" and other computers on the internet " +#~ "cannot connect to this computer, say \"yes\" here. Answer \"no\" on " +#~ "direct connections through modems, ISDN cards and DNAT (also known as " +#~ "\"port forwarding\")." +#~ msgstr "" +#~ "¿Tu máquina esta detrás de un NAT?\n" +#~ "\n" +#~ "Si tu estás conectado a Internet a través de otro ordenador haciendo " +#~ "SNAT, un router o un \"cortafuegos de hardware\" y otros ordenadores no " +#~ "pueden conectarse al tuyo a través de Internet directamente, responde \"si" +#~ "\" aquí. Responde \"no\" en conexiones directas a través de módems, " +#~ "tarjetas de RDSI y DNAT (también conocido como \"seguimiento de puertos" +#~ "\")." + +#, fuzzy +#~ msgid "Configuration of the logging system" +#~ msgstr "" +#~ "La configuración o la versión de GNUnet cambiaron. ¡Debes ejecutar '%" +#~ "s'!\n" + +#, fuzzy +#~ msgid "Run gnunetd as this user." +#~ msgstr "Ejecutar gnunet-update" + +#, fuzzy +#~ msgid "Run gnunetd during system startup?" +#~ msgstr "Ejecutar gnunet-update" + +#, fuzzy +#~ msgid "Path settings" +#~ msgstr "Otras configuraciones" + +#~ msgid "specify nickname" +#~ msgstr "especifica el apodo" + +#~ msgid "Could not connect to gnunetd.\n" +#~ msgstr "Imposible conectar con gnunetd.\n" + +#~ msgid "You must specify a nickname (use option `%s').\n" +#~ msgstr "Debes especificar un apodo (use la opción '%s').\n" + +#~ msgid "Could not send join message to gnunetd\n" +#~ msgstr "Imposible mandar el mensaje a gnunetd\n" + +#~ msgid "mysql datastore" +#~ msgstr "base de datos mysql" + +#, fuzzy +#~ msgid "" +#~ "`%s' failed at %s:%d with error: I/%s S/%s SC/%s SS/%s SSC/%s U/%s D/%s " +#~ "DG/%s\n" +#~ msgstr "'%s' falló en %s: %d con el error: %s\n" + +#~ msgid "Database failed to delete `%s'.\n" +#~ msgstr "Falló al borrar de la base de datos %s.\n" + +#, fuzzy +#~ msgid "Error log:\n" +#~ msgstr "Error" + +#, fuzzy +#~ msgid "# bytes received via TCP-OLD" +#~ msgstr "# bytes recibidos por TCP" + +#, fuzzy +#~ msgid "# bytes sent via TCP-OLD" +#~ msgstr "# bytes enviados por TCP" + +#, fuzzy +#~ msgid "# bytes dropped by TCP-OLD (outgoing)" +#~ msgstr "# bytes omitidos por TCP (salientes)" + +#~ msgid "hello advertisement for protocol %d received.\n" +#~ msgstr "Inicio de comunicación del protocolo %d recibido.\n" + +#~ msgid "`%s' failed (%d, %u). Will not send PING.\n" +#~ msgstr "'%s' falló (%d, %u). No se mandará el PING.\n" + +#~ msgid "Removing hello from peer `%s' (expired %ds ago).\n" +#~ msgstr "Eliminando saludo del par '%s' (expiró hace %ds).\n" + +#~ msgid "No reply received within %llums.\n" +#~ msgstr "No se ha recibido una respuesta en %llums.\n" + +#~ msgid "Waiting for gnunetd to start (%u iterations left)...\n" +#~ msgstr "Esperando a gnunetd para empezar (%u ciclos restantes)...\n" + +#, fuzzy +#~ msgid "Deleting expired content. This may take a while.\n" +#~ msgstr "Creando nueva clave local (esto puede llevar un tiempo).\n" + +#~ msgid "User `%s' not known, cannot change UID to it.\n" +#~ msgstr "Usuario '%s' desconocido, imposible cambiar la UID a él.\n" + +#~ msgid "" +#~ "Expected welcome on http connection, got garbage. Closing connection.\n" +#~ msgstr "" +#~ "Esperada bienvenida en la conexión http, encontrada basura. Cerrando la " +#~ "conexión.\n" + +#~ msgid "%s: Rejected connection from blacklisted address %u.%u.%u.%u.\n" +#~ msgstr "" +#~ "%s: Conexión rechazada de una dirección de la \"lista negra\" %u.%u.%u.%" +#~ "u.\n" + +#~ msgid "Unexpected reply to `%s' operation.\n" +#~ msgstr "Respuesta inesperada a la operación '%s'.\n" + +#~ msgid "join table called NAME" +#~ msgstr "únete a la tabla llamada NAME" + +#~ msgid "Malformed optional field `%s' received from peer `%s'.\n" +#~ msgstr "Campo opcional anómalo '%s' recibido del par '%s'.\n" + +#~ msgid "Could not find peer `%s' in routing table!\n" +#~ msgstr "¡Imposible encontrar el par '%s' en la tabla de enrutado!\n" + +#~ msgid "Received malformed response to `%s' from peer `%s'.\n" +#~ msgstr "Recibida respuesta anómala a'%s' del par '%s'.\n" + +#~ msgid "Invalid response to `%s'.\n" +#~ msgstr "Respuesta inválida a '%s'.\n" + +#~ msgid "Malformed response to `%s' on master table.\n" +#~ msgstr "Respuesta anómala a '%s' en la tabla maestra.\n" + +#~ msgid "Invalid response to `%s' from `%s'\n" +#~ msgstr "Respuesta inválida a '%s' de '%s'\n" + +#~ msgid "Received invalid RPC `%s'.\n" +#~ msgstr "Recibido RPC '%s' inválida.\n" + +#~ msgid "RPC for `%s' received for table that we do not participate in!\n" +#~ msgstr "¡RPC para '%s' recibida de la tabla en la cual no participamos!\n" + +#~ msgid "`%s' failed. Terminating connection to client.\n" +#~ msgstr "'%s' falló. Finalizando conexión con el cliente.\n" + +#~ msgid "" +#~ "Share denoted bandwidth with other applications?\n" +#~ "\n" +#~ "Say \"yes\" here, if you don't want other network traffic to interfere " +#~ "with GNUnet's operation, but still wish to constrain GNUnet's bandwidth " +#~ "usage to values entered in the previous steps, or if you can't reliably " +#~ "measure the maximum capabilities of your connection. \"No\" can be very " +#~ "useful if other applications are causing a lot of traffic on your LAN. " +#~ "In this case, you do not want to limit the traffic that GNUnet can " +#~ "inflict on your internet connection whenever your high-speed LAN gets " +#~ "used (e.g. by NFS)." +#~ msgstr "" +#~ "¿Compartir el ancho de banda marcado con otras aplicaciones?\n" +#~ "\n" +#~ "Di \"sí\" aquí si no quieres que el resto del tráfico de la red no " +#~ "interfiera con las operaciones de GNUnet, pero aun quieres mantener el " +#~ "ancho de banda usado por GNUnet introducido en los pasos previos, o no " +#~ "puedes asegurar las máximas capacidades de tu conexión. \"No\" puede ser " +#~ "muy útil si otras aplicaciones crean mucho tráfico en tu LAN. En ese " +#~ "caso no quieres limitar el tráfico que GNUnet puede crear en tu conexión " +#~ "a Internet cuando una LAN de alta velocidad es usada (por ejemplo bajo " +#~ "NFS)." + +#~ msgid "How much CPU (in %) may be used?" +#~ msgstr "¿Cuánta CPU (en %) podrá ser usada?" + +#~ msgid "" +#~ "You can limit GNUnet's resource usage here.\n" +#~ "\n" +#~ "This is the percentage of processor time GNUnet is allowed to use." +#~ msgstr "" +#~ "Tu puedes limitar el uso de recursos de GNUnet aquí.\n" +#~ "\n" +#~ "Este es el porcentaje del tiempo del procesador que GNUnet puede usar." + +#~ msgid "" +#~ "Store migrated content?\n" +#~ "\n" +#~ "GNUnet is able to store data from other peers in your datastore. This is " +#~ "useful if an adversary has access to your inserted content and you need " +#~ "to deny that the content is yours. With \"content migration\" on, the " +#~ "content could have \"migrated\" over the internet to your node without " +#~ "your knowledge.\n" +#~ "It also helps to spread popular content over different peers to enhance " +#~ "availability." +#~ msgstr "" +#~ "¿Almacena el contenido migrado?\n" +#~ "\n" +#~ "GNUnet puede almacenar datos de otros pares en tu ordenador. Esto es muy " +#~ "útil si un adversario accede a tu contenido insertado y necesitas negar " +#~ "que dicho contenido sea tuyo. Con la \"migración de contenidos\" " +#~ "activada, el contenido puede haber \"migrado\" a través de internet a tu " +#~ "nodo sin tu conocimiento.\n" +#~ "Ésto también ayuda a repartir contenido popular a través de diferentes " +#~ "pares para aumentar la disponibilidad." + +#~ msgid "" +#~ "If you are an experienced user, you may want to tweak your GNUnet " +#~ "installation using the enhanced configurator.\n" +#~ "\n" +#~ "Do you want to start it after saving your configuration?" +#~ msgstr "" +#~ "Si eres un usuario experimentado, puedes desear exprimir tu instalación " +#~ "de GNUnet usando la herramienta de configuración avanzada.\n" +#~ "\n" +#~ "¿Quieres arrancarla después de guardar tu configuración?" + +#~ msgid "" +#~ "Unable to save configuration file %s: %s.\n" +#~ "\n" +#~ "Try again?" +#~ msgstr "" +#~ "Imposible guardar el fichero de configuración %s: %s.\n" +#~ "\n" +#~ "¿Intentar de nuevo?" + +#~ msgid "Failed to send `%s'. Closing connection.\n" +#~ msgstr "Fallo al mandar '%s'. Cerrando la conexión.\n" + +#~ msgid "Received invalid `%s' request (size %d)\n" +#~ msgstr "Recibida petición '%s' no válida (tamaño %d)\n" + +#~ msgid "Received invalid `%s' request (wrong table)\n" +#~ msgstr "Recibida petición '%s' no válida (tabla errónea)\n" + +#~ msgid "This client already participates in the given DHT!\n" +#~ msgstr "Este cliente ya participa en el DHT dado\n" + +#~ msgid "Cannot leave DHT: table not known!\n" +#~ msgstr "Imposible dejar DHT: ¡tabla no conocida!\n" + +#~ msgid "gnunetd signaled error in response to `%s' message\n" +#~ msgstr "gnunetd lanzó una señal de error en respuesta al mensaje '%s'\n" + +#~ msgid "Failed to receive response to `%s' message from gnunetd\n" +#~ msgstr "Falló al recibir la respuesta al mensaje '%s' de gnunetd\n" + +#~ msgid "Failed to send `%s' message to gnunetd\n" +#~ msgstr "Falló al mandar el mensaje '%s' a gnunetd\n" + +#~ msgid "Join a DHT." +#~ msgstr "Únete a DHT" + +#~ msgid "allow SIZE bytes of memory for the local table" +#~ msgstr "Reserva SIZE bytes de memoria para la tabla local" + +#~ msgid "Call to `%s' returns %d.\n" +#~ msgstr "La llamada a '%s' devuelve %d.\n" + +#~ msgid "Call to `%s' with key `%s'.\n" +#~ msgstr "Llamada a '%s' con la clave '%s'.\n" + +#~ msgid "Call to `%s' with value '%.*s' (%d bytes).\n" +#~ msgstr "Llama a '%s' con el valor '%.*s' (%d bytes).\n" + +#~ msgid "Error joining DHT.\n" +#~ msgstr "Error uniéndose a DHT.\n" + +#~ msgid "Joined DHT. Press CTRL-C to leave.\n" +#~ msgstr "Unido a DHT. Pulsa CTRL-C para abandonarlo.\n" + +#~ msgid "Error leaving DHT.\n" +#~ msgstr "Error abandonando DHT.\n" + +#~ msgid "`%s' failed: table not found!\n" +#~ msgstr "'%s' falló: ¡tabla no encontrada!\n" + +#~ msgid "sendAck failed. Terminating connection to client.\n" +#~ msgstr "sendAck falló. Finalizando conexión con el cliente.\n" + +#~ msgid "Unindex failed." +#~ msgstr "El desindexado falló" + +#, fuzzy +#~ msgid "Upload failed (consult logs)." +#~ msgstr "La descarga del ERCS falló (vea los logs)." + +#~ msgid "Could not resolve name of SMTP server `%s': %s" +#~ msgstr "Imposible resolver el nombre del servidor de SMTP '%s': %s" + +#~ msgid "query table called NAME" +#~ msgstr "pregunta a la tabla llamada NAME" + +#~ msgid "No commands specified.\n" +#~ msgstr "Ningún comando fue especificado.\n" + +#~ msgid "Superflous arguments (ignored).\n" +#~ msgstr "Argumentos superfluos (ignorados).\n" + +#~ msgid "Query `%s' had no results.\n" +#~ msgstr "La pregunta '%s' no tuvo resultados.\n" + +#~ msgid "FSUI persistence: error restoring download\n" +#~ msgstr "FSUI persistente: error restableciendo la descarga\n" + +#~ msgid "ECRS download suspending." +#~ msgstr "La descarga del ERCS fue suspendida." + +#~ msgid "Unexpected download state." +#~ msgstr "Estado de descarga inesperado." + +#~ msgid "Upload failed." +#~ msgstr "Subida fallida" + +#~ msgid "Cannot upload directory without using recursion." +#~ msgstr "Imposible compartir un directorio sin hacerlo recursivamente." + +#, fuzzy +#~ msgid "Expected `%s' to be a regular file\n" +#~ msgstr "'%s' no es un fichero regular.\n" + +#, fuzzy +#~ msgid "expected `%s' to be a directory!\n" +#~ msgstr "¡'%s' se esperaba que '%s' fuera un directorio!\n" + +#~ msgid "Sorry, no help is available for this option.\n" +#~ msgstr "Lo siento, no hay ayuda disponible para esta opción.\n" + +#~ msgid "Failed to obtain my (external) IPv6 address!\n" +#~ msgstr "¡Falló al obtener mi dirección IPv6 (externa)!\n" + +#~ msgid "Received invalid UDP6 message from %s:%d, dropping.\n" +#~ msgstr "Recibido mensaje UDP6 inválido de %s:%d, omitiendo.\n" + +#~ msgid "%s: Rejected connection from blacklisted address %s.\n" +#~ msgstr "%s: Conexión rechazada de una dirección de la \"lista negra\" %s.\n" + +#~ msgid "Received invalid UDP message from %u.%u.%u.%u:%u, dropping.\n" +#~ msgstr "Recibido mensaje UDP no válido del %u.%u.%u.%u:%u, omitiendo.\n" + +#~ msgid "Network advertisements disabled by configuration!\n" +#~ msgstr "¡Advertencias de la red desconectadas por configuración!\n" + +#~ msgid "" +#~ "Configuration file must specify directory for storing FS data in section `" +#~ "%s' under `%s'.\n" +#~ msgstr "" +#~ "El fichero de configuración debe especificar el directorio para almacenar " +#~ "los datos FS en la sección '%s' bajo '%s'.\n" + +#~ msgid "Could not initialize libgnunetutil!\n" +#~ msgstr "¡Imposible inicializar libgnunetutil!\n" + +#~ msgid "" +#~ "Configuration file must specify a directory for GNUnet to store per-peer " +#~ "data under %s%s\n" +#~ msgstr "" +#~ "El fichero de configuración debe especificar un directorio para que " +#~ "GNUnet almacene la información por-par bajo%s%s\n" + +#~ msgid "%s `%s' returned no known hosts!\n" +#~ msgstr "¡%s '%s' devolvió hosts desconocidos!\n" + +#~ msgid "" +#~ "specify that the contents of the namespace are of the given MIMETYPE (use " +#~ "when creating a new pseudonym)" +#~ msgstr "" +#~ "especifica que los contenidos del espacio son del MIMETYPE dado (úsalo " +#~ "cuando crees un nuevo pseudónimo)" + +#~ msgid "" +#~ "specify NAME to be the realname of the user controlling the namespace " +#~ "(use when creating a new pseudonym)" +#~ msgstr "" +#~ "especifica el NAME para ser el nombre real del usuario que controla el " +#~ "espacio (úsalo cuando crees un nuevo pseudónimo)" + +#~ msgid "" +#~ "use DESCRIPTION to describe the content of the namespace (use when " +#~ "creating a new pseudonym)" +#~ msgstr "" +#~ "usa la DESCRIPTION para describir el contenido del espacio (úsalo cuando " +#~ "crees un nuevo pseudónimo)" + +#~ msgid "" +#~ "specify the given URI as an address that contains more information about " +#~ "the namespace (use when creating a new pseudonym)" +#~ msgstr "" +#~ "especifica la URI dada como dirección que contiene mas información del " +#~ "espacio (úsalo cuando crees un nuevo pseudónimo)" + +#~ msgid "Invalid argument: `%s'\n" +#~ msgstr "Argumento no válido: '%s'\n" + +#~ msgid "Invalid arguments. Exiting.\n" +#~ msgstr "Argumentos no válidos. Saliendo.\n" + +#~ msgid "%8u of %8u bytes deleted." +#~ msgstr "%8u de %8u bytes borrados." + +#~ msgid "specify the file to delete from GNUnet (obligatory, file must exist)" +#~ msgstr "" +#~ "especifica el fichero a borrar de GNUnet (el fichero debe existir " +#~ "obligatoriamente)" + +#~ msgid "" +#~ "Remove file from GNUnet. The specified file is not removed\n" +#~ "from the filesystem but just from the local GNUnet datastore." +#~ msgstr "" +#~ "Borra el fichero de GNUnet. El fichero especificado no es borrado\n" +#~ "del sistema de ficheros, solamente de la base de datos local de GNUnet." + +#~ msgid "You must specify a filename (option -f)\n" +#~ msgstr "Debes especificar un fichero (opción -f)\n" + +#~ msgid "" +#~ "Error deleting file %s.\n" +#~ "Probably a few blocks were already missing from the database.\n" +#~ msgstr "" +#~ "Error borrando el fichero %s.\n" +#~ "Probablemente algunos bloques se hayan perdido de la base de datos.\n" + +#~ msgid "gnunet-directory [OPTIONS] [FILENAMES]" +#~ msgstr "directorio-de-gnunet [OPCIONES] [FICHEROS]" + +#~ msgid "process directories recursively" +#~ msgstr "procesa los directorios recursivamente" + +#~ msgid "You must pass a positive number to the `%s' option.\n" +#~ msgstr "Debes pasar un número positivo a la opción '%s'.\n" + +#~ msgid "You must specify a list of files to insert.\n" +#~ msgstr "Debes especificar una lista de ficheros a insertar.\n" + +#~ msgid "Only one file or directory can be specified at a time.\n" +#~ msgstr "Solo un fichero o un directorio puede ser especificado cada vez.\n" + +#~ msgid "You must specify a file or directory to upload.\n" +#~ msgstr "Debes especificar un fichero o directorio para subir.\n" + +#~ msgid "set the desired LEVEL of receiver-anonymity" +#~ msgstr "seleccione el NIVEL deseado de anonimidad-del-receptor" + +#~ msgid "Not enough arguments. You must specify a keyword or identifier.\n" +#~ msgstr "" +#~ "No hay suficientes argumentos. Debes especificar una clave o un " +#~ "identificador.\n" + +#~ msgid "LEVEL" +#~ msgstr "NIVEL" + +#~ msgid "FILENAME" +#~ msgstr "FICHERO" + +#~ msgid "This search is already pending!\n" +#~ msgstr "¡Esta búsqueda está aún pendiente!\n" + +#~ msgid "" +#~ "Configuration file must specify a directory for GNUnet to store per-peer " +#~ "data under %s%s.\n" +#~ msgstr "" +#~ "El fichero de configuración debe especificar un directorio para que " +#~ "GNUnet almacene la información por-par bajo %s%s.\n" + +#~ msgid "helptext for -t" +#~ msgstr "texto de ayuda para -t" + +#~ msgid "Template for gnunet-clients." +#~ msgstr "Plantilla para clientes-de-gnunet." + +#~ msgid "Invalid port \"%s\" in hostlist specification, trying port %d.\n" +#~ msgstr "" +#~ "Puerto inválido \"%s\" en la especificación de la lista local, probando " +#~ "el puerto %d.\n" + +#~ msgid "`%s' to `%s' failed at %s:%d with error: %s\n" +#~ msgstr "'%s' a '%s' falló en %s: %d con error %s\n" + +#~ msgid "Parsing HTTP response for URL `%s' failed.\n" +#~ msgstr "Analizando HTTP de la URL '%s' se produjo un fallo.\n" + +#~ msgid "Parsing hello from `%s' failed.\n" +#~ msgstr "Analizando saludo de '%s' se produjo un fallo.\n" + +#~ msgid "Could not resolve name of HTTP proxy `%s'. Trying without a proxy.\n" +#~ msgstr "" +#~ "Imposible resolver el nombre del proxy HTTP '%s'. Intentándolo sin un " +#~ "proxy.\n" + +#, fuzzy +#~ msgid "" +#~ "Configuration file must specify directory for storing data in section `%" +#~ "s' under `%s'.\n" +#~ msgstr "" +#~ "El fichero de configuración debe especificar el directorio para almacenar " +#~ "los datos FS en la sección '%s' bajo '%s'.\n" + +#~ msgid "Option `%s' makes no sense without option `%s'." +#~ msgstr "La opción '%s' no tiene ningún sentido sin la opción '%s'." + +#~ msgid "Invalid arguments: " +#~ msgstr "Argumentos inválidos: " + +#~ msgid "" +#~ "\n" +#~ "Exiting.\n" +#~ msgstr "" +#~ "\n" +#~ "Saliendo.\n" + +#~ msgid "No applications defined in configuration!\n" +#~ msgstr "¡Ninguna aplicación definida en la configuración!\n" + +#~ msgid "Updated data for %d applications.\n" +#~ msgstr "Actualizados los datos para %d aplicaciones.\n" + +#~ msgid "Could not write PID to file `%s': %s.\n" +#~ msgstr "Imposible escribir PID al fichero '%s': %s.\n" + +#~ msgid "Invalid command-line arguments:\n" +#~ msgstr "Argumentos en la linea de comandos inválidos:\n" + +#~ msgid "Argument %d: `%s'\n" +#~ msgstr "Argumento %d: '%s'\n" + +#~ msgid "`%s' starting\n" +#~ msgstr "'%s' comenzando\n" + +#~ msgid "FATAL: Identity plugin not found!\n" +#~ msgstr "FATAL: ¡Plugin de identidad no encontrado!\n" + +#~ msgid "Available MODEs:\n" +#~ msgstr "MODOs disponibles:\n" + +#~ msgid " config\t\ttext-based configuration\n" +#~ msgstr " configuración\t\tconfiguración basada en texto\n" + +#~ msgid " menuconfig\ttext-based menu\n" +#~ msgstr " menuconfig\tmenú basado en texto\n" + +#~ msgid " wizard-curses\tBasic text-based graphical configuration\n" +#~ msgstr "" +#~ " wizard-curses\t Configuración básica en modo gráfico pero basado en " +#~ "texto\n" + +#~ msgid "" +#~ " wizard-gtk\tBasic GTK configuration\n" +#~ "\n" +#~ msgstr "" +#~ " wizard-gtk\tConfiguración GTK básica\n" +#~ "\n" + +#~ msgid "gnunet-setup must have write-access to the configuration file `%s'\n" +#~ msgstr "" +#~ "gnunet-setup debe tener acceso de escritura al fichero de configuración '%" +#~ "s'\n" + +#, fuzzy +#~ msgid "" +#~ "Can only run wizard to configure gnunetd.\n" +#~ "Did you forget the `%s' option?\n" +#~ msgstr "" +#~ "Solo se puede arrancar wizard para configurar gnunetd.\n" +#~ "¿Te olvidaste de la opción %s'?\n" + +#~ msgid "%s: symbol value `%s' invalid for %s\n" +#~ msgstr "%s: valor del símbolo '%s' no válido para %s\n" + +#~ msgid "Gtk GNUnet Configurator" +#~ msgstr "Configurador Gtk de GNUnet" + +#~ msgid "_File" +#~ msgstr "_Fichero" + +#~ msgid "Load a config file" +#~ msgstr "Cargar un fichero de configuración" + +#~ msgid "_Load" +#~ msgstr "_Cargar" + +#~ msgid "Save the config in .config" +#~ msgstr "Guardar la configuración en .config" + +#~ msgid "_Save" +#~ msgstr "_Guardar" + +#~ msgid "_Quit" +#~ msgstr "_Salir" + +#~ msgid "Show _name" +#~ msgstr "Mostrar el _nombre" + +#~ msgid "Show range (Y/M/N)" +#~ msgstr "Muestra el rango (Y/M/N)" + +#~ msgid "Show _range" +#~ msgstr "Muestra el _rango" + +#~ msgid "Show _data" +#~ msgstr "Mostrar los _datos" + +#~ msgid "Show all _options" +#~ msgstr "Mostrar todas las _opciones" + +#~ msgid "_Help" +#~ msgstr "_Ayuda" + +#~ msgid "_Introduction" +#~ msgstr "_Introducción" + +#~ msgid "Goes up of one level (single view)" +#~ msgstr "Sube un nivel (vista única)" + +#~ msgid "Load" +#~ msgstr "Cargar" + +#~ msgid "Save a config file" +#~ msgstr "Guardar un fichero de configuración" + +#~ msgid "Save" +#~ msgstr "Guardar" + +#~ msgid "Single view" +#~ msgstr "Vista única" + +#~ msgid "Single" +#~ msgstr "Única" + +#~ msgid "Split view" +#~ msgstr "Vista doble" + +#~ msgid "Split" +#~ msgstr "Doble" + +#~ msgid "Full view" +#~ msgstr "Vista completa" + +#~ msgid "Full" +#~ msgstr "Completa" + +#~ msgid "Collapse the whole tree in the right frame" +#~ msgstr "Contraer el árbol completo en el lateral izquierdo" + +#~ msgid "Collapse" +#~ msgstr "Contraer" + +#~ msgid "Expand the whole tree in the right frame" +#~ msgstr "Expandir el árbol entero en el lateral izquierdo" + +#~ msgid "Expand" +#~ msgstr "Expandir" + +#~ msgid "" +#~ "This is GNUnet's configuration interface.\n" +#~ "\n" +#~ "GNUnet's options are separated into categories. You can browse them in " +#~ "the left tree. If you click on one of the categories, its options are " +#~ "shown above. \n" +#~ "\n" +#~ "To change the value of an option, simply click on its value and enter a " +#~ "new value. To get additional information about a specific option, click " +#~ "on its description." +#~ msgstr "" +#~ "Ésta es la interfaz de configuración de GNUnet.\n" +#~ "\n" +#~ "Las opciones de GNUnet están separadas en categorías. Tu puedes " +#~ "explorarlas en el árbol de la izquierda. Si pulsas en una de las " +#~ "categorías las opciones son mostradas encima.\n" +#~ "\n" +#~ "Para cambiar el valor de una opción, simplemente pulsa en su valor e " +#~ "introduce uno nuevo. Para obtener información adicional acerca de una " +#~ "opción específica, pulsa en su descripción." + +#~ msgid "Introduction" +#~ msgstr "Introducción" + +#, fuzzy +#~ msgid "" +#~ "Welcome to GNUnet Setup.\n" +#~ "\n" +#~ "For each option, a blank box indicates the feature is disabled, and " +#~ "checked one indicates it is enabled.\n" +#~ "If you do not see an option that you believe should be present, try " +#~ "turning on Show All Options under the Options menu.\n" +#~ "\n" +#~ "Although there is no cross reference yet to help you figure out what " +#~ "other options must be enabled to support the option you are interested " +#~ "in, you can still view the help of a grayed-out option.\n" +#~ "\n" +#~ "Toggling Show Debug Info under the Options menu will show the " +#~ "dependencies, which you can then match by examining other options." +#~ msgstr "" +#~ "Bienvenido a la configuración de GNUnet.\n" +#~ "\n" +#~ "Para cada opción una caja en blanco indica que la opción está desactivada " +#~ "y una marcada indica que está activada.\n" +#~ "Si no ves una opción que crees que debería estar presente, prueba " +#~ "activando la opción \"Mostrar todas las opciones\" en el menú de " +#~ "\"Opciones\".\n" +#~ "\n" +#~ "Although·there·is·no·cross·reference·yet·to·help·you·figure·out·what·other·options·must·be·enabled·to·support·the·option·you·are·interested·in," +#~ "·you·can·still·view·the·help·of·a·grayed-out·option.\n" +#~ "\n" +#~ "Toggling·Show·Debug·Info·under·the·Options·menu·will·show·the·dependencies," +#~ "·which·you·can·then·match·by·examining·other·options." + +#~ msgid "" +#~ "Configuration file not found. Please run GNUnet Setup (Client " +#~ "Configuration) first." +#~ msgstr "" +#~ "Fichero de configuración no encontrado. Por favor, ejecuta GNUnet Setup " +#~ "(Configuración del cliente) primero." + +#~ msgid "Configuration file `%s' not found. Run `gnunet-setup -d'!\n" +#~ msgstr "" +#~ "El·fichero·de·configuración·'%s'·no·ha·sido·encontrado.··¡Ejecute·'gnunet-" +#~ "setup -d'!\n" + +#~ msgid "Stopping cron\n" +#~ msgstr "Deteniendo cron\n" + +#~ msgid "Cron stopped\n" +#~ msgstr "Cron detenido\n" + +#~ msgid "Shutdown complete.\n" +#~ msgstr "Cierre completado.\n" + +#~ msgid "Caught signal %d.\n" +#~ msgstr "Cogida señal %d.\n" + +#~ msgid "Invalid network notation (additional characters: `%s')." +#~ msgstr "Notación de red no válida (caracteres adicionales: '%s')." + +#~ msgid "FAILURE" +#~ msgstr "FALLO" + +#~ msgid "MESSAGE" +#~ msgstr "MENSAJE" + +#~ msgid "CRON" +#~ msgstr "CRON" + +#~ msgid "EVERYTHING" +#~ msgstr "TODO" + +#~ msgid "Invalid LOGLEVEL `%s' specified.\n" +#~ msgstr "LOGLEVEL inválido '%s' especificado.\n" + +#~ msgid "Failure at %s:%d.\n" +#~ msgstr "Fallo en %s:%d.\n" + +#~ msgid "" +#~ "Cannot determine port of gnunetd server. Define in configuration file in " +#~ "section `%s' under `%s'.\n" +#~ msgstr "" +#~ "Imposible determinar el puerto del servidor de gnunetd. Defínelo en el " +#~ "fichero de configuración en la sección '%s' bajo '%s'.\n" + +#~ msgid "" +#~ "Usage: %s\n" +#~ "%s\n" +#~ "\n" +#~ msgstr "" +#~ "Uso: %s\n" +#~ "%s\n" +#~ "\n" + +#~ msgid "Invalid argument for `%s' at %s:%d.\n" +#~ msgstr "Argumento no válido para '%s' en %s:%d.\n" + +#~ msgid "g" +#~ msgstr "g" + +#~ msgid "t" +#~ msgstr "t" + +#~ msgid "unknown" +#~ msgstr "desconocido" + +#~ msgid "`%s' failed, other side closed connection.\n" +#~ msgstr "'%s' falló, conexión cerrada en el otro lado.\n" + +#~ msgid "Attempted path to `%s' was `%s'.\n" +#~ msgstr "La ruta esperada a '%s' fue '%s'.\n" diff --git a/po/insert-header.sin b/po/insert-header.sin new file mode 100644 index 000000000..b26de01f6 --- /dev/null +++ b/po/insert-header.sin @@ -0,0 +1,23 @@ +# Sed script that inserts the file called HEADER before the header entry. +# +# At each occurrence of a line starting with "msgid ", we execute the following +# commands. At the first occurrence, insert the file. At the following +# occurrences, do nothing. The distinction between the first and the following +# occurrences is achieved by looking at the hold space. +/^msgid /{ +x +# Test if the hold space is empty. +s/m/m/ +ta +# Yes it was empty. First occurrence. Read the file. +r HEADER +# Output the file's contents by reading the next line. But don't lose the +# current line while doing this. +g +N +bb +:a +# The hold space was nonempty. Following occurrences. Do nothing. +x +:b +} diff --git a/po/quot.sed b/po/quot.sed new file mode 100644 index 000000000..0122c4631 --- /dev/null +++ b/po/quot.sed @@ -0,0 +1,6 @@ +s/"\([^"]*\)"/“\1â€/g +s/`\([^`']*\)'/‘\1’/g +s/ '\([^`']*\)' / ‘\1’ /g +s/ '\([^`']*\)'$/ ‘\1’/g +s/^'\([^`']*\)' /‘\1’ /g +s/“â€/""/g diff --git a/po/remove-potcdate.sin b/po/remove-potcdate.sin new file mode 100644 index 000000000..2436c49e7 --- /dev/null +++ b/po/remove-potcdate.sin @@ -0,0 +1,19 @@ +# Sed script that remove the POT-Creation-Date line in the header entry +# from a POT file. +# +# The distinction between the first and the following occurrences of the +# pattern is achieved by looking at the hold space. +/^"POT-Creation-Date: .*"$/{ +x +# Test if the hold space is empty. +s/P/P/ +ta +# Yes it was empty. First occurrence. Remove the line. +g +d +bb +:a +# The hold space was nonempty. Following occurrences. Do nothing. +x +:b +} diff --git a/po/stamp-po b/po/stamp-po new file mode 100644 index 000000000..9788f7023 --- /dev/null +++ b/po/stamp-po @@ -0,0 +1 @@ +timestamp diff --git a/po/sv.gmo b/po/sv.gmo new file mode 100644 index 000000000..19593621a Binary files /dev/null and b/po/sv.gmo differ diff --git a/po/sv.po b/po/sv.po new file mode 100644 index 000000000..93f941126 --- /dev/null +++ b/po/sv.po @@ -0,0 +1,5885 @@ +# Swedish translation of GNUnet. +# Copyright (C) 2006 Free Software Foundation, Inc. +# This file is distributed under the same license as the GNUnet package. +# Daniel Nylander , 2006. +# +msgid "" +msgstr "" +"Project-Id-Version: GNUnet 0.7.0b\n" +"Report-Msgid-Bugs-To: gnunet-developers@mail.gnu.org\n" +"POT-Creation-Date: 2009-01-08 10:55-0700\n" +"PO-Revision-Date: 2006-01-21 17:16+0100\n" +"Last-Translator: Daniel Nylander \n" +"Language-Team: Swedish \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=utf-8\n" +"Content-Transfer-Encoding: 8bit\n" + +#: src/setup/ncurses/wizard_curs.c:72 src/setup/lib/wizard_util.c:155 +#: src/setup/lib/wizard_util.c:210 +msgid "Error" +msgstr "Fel" + +#: src/setup/ncurses/wizard_curs.c:80 +msgid "Help" +msgstr "Hjälp" + +#: src/setup/ncurses/wizard_curs.c:87 +#, fuzzy +msgid "Error!" +msgstr "Fel" + +#: src/setup/ncurses/wizard_curs.c:101 src/applications/vpn/cs.c:94 +msgid "No" +msgstr "Nej" + +#: src/setup/ncurses/wizard_curs.c:102 src/applications/vpn/cs.c:94 +msgid "Yes" +msgstr "Ja" + +#: src/setup/ncurses/wizard_curs.c:118 src/setup/ncurses/wizard_curs.c:183 +#: src/setup/ncurses/wizard_curs.c:299 src/setup/ncurses/mconf.c:189 +#: src/setup/ncurses/mconf.c:285 src/setup/ncurses/mconf.c:365 +#: src/setup/ncurses/mconf.c:456 +msgid "Internal error! (Choice invalid?)" +msgstr "" + +#: src/setup/ncurses/wizard_curs.c:150 +#, fuzzy +msgid "Abort" +msgstr "_Om" + +#: src/setup/ncurses/wizard_curs.c:151 +#, fuzzy +msgid "Ok" +msgstr "k" + +#: src/setup/ncurses/wizard_curs.c:218 src/setup/ncurses/wizard_curs.c:284 +#: src/setup/ncurses/wizard_curs.c:425 +msgid "GNUnet configuration" +msgstr "GNUnet-konfiguration" + +#: src/setup/ncurses/wizard_curs.c:220 +msgid "" +"Welcome to GNUnet!\n" +"\n" +"This assistant will ask you a few basic questions in order to configure " +"GNUnet.\n" +"\n" +"Please visit our homepage at\n" +"\thttp://gnunet.org/\n" +"and join our community at\n" +"\thttp://gnunet.org/drupal/\n" +"\n" +"Have a lot of fun,\n" +"\n" +"the GNUnet team" +msgstr "" +"Välkommen till GNUnet!\n" +"\n" +"Denna assistant kommer att frÃ¥ga dig nÃ¥gra enkla frÃ¥gor för att konfigurera " +"GNUnet.\n" +"\n" +"Vänligen besök pÃ¥ webbplats pÃ¥\n" +"\thttp://gnunet.org/\n" +"och gÃ¥ med i vÃ¥r gemenskap pÃ¥\n" +"\thttp://gnunet.org/drupal/\n" +"\n" +"Ha det sÃ¥ kul,\n" +"\n" +"the GNUnet team" + +#: src/setup/ncurses/wizard_curs.c:286 +msgid "" +"Choose the network interface that connects your computer to the internet " +"from the list below." +msgstr "" + +#: src/setup/ncurses/wizard_curs.c:304 src/setup/ncurses/wizard_curs.c:318 +msgid "" +"The \"Network interface\" is the device that connects your computer to the " +"internet. This is usually a modem, an ISDN card or a network card in case " +"you are using DSL." +msgstr "" + +#: src/setup/ncurses/wizard_curs.c:315 +#, fuzzy +msgid "Network configuration: interface" +msgstr "Nätverksgränssnitt:" + +#: src/setup/ncurses/wizard_curs.c:317 +msgid "" +"What is the name of the network interface that connects your computer to the " +"Internet?" +msgstr "" + +#: src/setup/ncurses/wizard_curs.c:328 +#, fuzzy +msgid "Network configuration: IP" +msgstr "GNUnet-konfiguration" + +#: src/setup/ncurses/wizard_curs.c:330 +msgid "What is this computer's public IP address or hostname?" +msgstr "" + +#: src/setup/ncurses/wizard_curs.c:331 +msgid "" +"If your provider always assigns the same IP-Address to you (a \"static\" IP-" +"Address), enter it into the \"IP-Address\" field. If your IP-Address changes " +"every now and then (\"dynamic\" IP-Address) but there's a hostname that " +"always points to your actual IP-Address (\"Dynamic DNS\"), you can also " +"enter it here.\n" +"If left empty, GNUnet will try to automatically detect the IP.\n" +"You can specify a hostname, GNUnet will then use DNS to resolve it.\n" +"If in doubt, leave this empty." +msgstr "" + +#: src/setup/ncurses/wizard_curs.c:346 +#, fuzzy +msgid "Bandwidth configuration: upload" +msgstr "GNUnet-konfiguration" + +#: src/setup/ncurses/wizard_curs.c:348 +#, fuzzy +msgid "How much upstream bandwidth (in bytes/s) may be used?" +msgstr "Hur mycket CPU (i %) fÃ¥r användas?" + +#: src/setup/ncurses/wizard_curs.c:349 +msgid "" +"You can limit GNUnet's resource usage here.\n" +"\n" +"The \"upstream\" is the data channel through which data is *sent* to the " +"internet. The limit is the maximum amount which GNUnet is allowed to use. If " +"you have a flatrate, you can set it to the maximum speed of your internet " +"connection. You should not use a value that is higher than what your actual " +"connection allows." +msgstr "" + +#: src/setup/ncurses/wizard_curs.c:361 +#, fuzzy +msgid "Bandwidth configuration: download" +msgstr "GNUnet-konfiguration" + +#: src/setup/ncurses/wizard_curs.c:363 +#, fuzzy +msgid "How much downstream bandwidth (in bytes/s) may be used?" +msgstr "Hur mycket CPU (i %) fÃ¥r användas?" + +#: src/setup/ncurses/wizard_curs.c:364 +msgid "" +"You can limit GNUnet's resource usage here.\n" +"\n" +"The \"downstream\" is the data channel through which data is *received* from " +"the internet. The limit is the maximum amount which GNUnet is allowed to " +"use. If you have a flatrate, you can set it to the maximum speed of your " +"internet connection. You should not use a value that is higher than what " +"your actual connection allows." +msgstr "" + +#: src/setup/ncurses/wizard_curs.c:376 +#, fuzzy +msgid "Quota configuration" +msgstr "GNUnet-konfiguration" + +#: src/setup/ncurses/wizard_curs.c:377 +msgid "What is the maximum size of the datastore in MB?" +msgstr "" + +#: src/setup/ncurses/wizard_curs.c:378 +msgid "" +"The GNUnet datastore contains all content that GNUnet needs to store " +"(indexed, inserted and migrated content)." +msgstr "" + +#: src/setup/ncurses/wizard_curs.c:390 +#, fuzzy +msgid "Daemon configuration: user account" +msgstr "Kunde inte skapa användarkonto:" + +#: src/setup/ncurses/wizard_curs.c:391 +msgid "As which user should gnunetd be run?" +msgstr "" + +#: src/setup/ncurses/wizard_curs.c:393 +msgid "" +"For security reasons, it is a good idea to let this setup create a new user " +"account under which the GNUnet service is started at system startup.\n" +"\n" +"However, GNUnet may not be able to access files other than its own. This " +"includes files you want to publish in GNUnet. You'll have to grant read " +"permissions to the user specified below.\n" +"\n" +"Leave the field empty to run GNUnet with system privileges.\n" +msgstr "" + +#: src/setup/ncurses/wizard_curs.c:410 +msgid "Daemon configuration: group account" +msgstr "" + +#: src/setup/ncurses/wizard_curs.c:411 +msgid "As which group should gnunetd be run?" +msgstr "" + +#: src/setup/ncurses/wizard_curs.c:413 +msgid "" +"For security reasons, it is a good idea to let this setup create a new group " +"for the chosen user account.\n" +"\n" +"You can also specify a already existent group here.\n" +"\n" +"Only members of this group will be allowed to start and stop the the GNUnet " +"server and have access to GNUnet server data.\n" +msgstr "" + +#: src/setup/ncurses/wizard_curs.c:427 +msgid "Do you want to automatically launch GNUnet as a system service?" +msgstr "" + +#: src/setup/ncurses/wizard_curs.c:429 +msgid "" +"If you say \"yes\" here, the GNUnet background process will be automatically " +"started when you turn on your computer. If you say \"no\" here, you have to " +"launch GNUnet yourself each time you want to use it." +msgstr "" + +#: src/setup/ncurses/wizard_curs.c:452 src/setup/gtk/wizard_gtk.c:414 +#, c-format +msgid "Unable to save configuration file `%s':" +msgstr "Kunde inte spara konfigurationsfil \"%s\":" + +#: src/setup/ncurses/wizard_curs.c:472 +#, fuzzy +msgid "Unable to create user account for daemon." +msgstr "Kunde inte skapa användarkonto:" + +#: src/setup/ncurses/wizard_curs.c:483 +msgid "Unable to setup autostart for daemon." +msgstr "" + +#: src/setup/ncurses/wizard_curs.c:498 +#, fuzzy +msgid "Save configuration?" +msgstr "GNUnet-konfiguration" + +#: src/setup/ncurses/wizard_curs.c:499 +#, fuzzy +msgid "Save configuration now?" +msgstr "GNUnet-konfiguration" + +#: src/setup/ncurses/wizard_curs.c:539 src/setup/ncurses/mconf.c:500 +#, fuzzy +msgid "GNUnet Configuration" +msgstr "GNUnet-konfiguration" + +#: src/setup/ncurses/wizard_curs.c:543 +msgid "Back" +msgstr "Tillbaka" + +#: src/setup/ncurses/mconf.c:96 +msgid "Exit" +msgstr "" + +#: src/setup/ncurses/mconf.c:99 +msgid "Up" +msgstr "Upp" + +#: src/setup/ncurses/mconf.c:102 +msgid "Cancel" +msgstr "Avbryt" + +#: src/setup/ncurses/mconf.c:221 src/setup/ncurses/mconf.c:408 +msgid "Internal error! (Value invalid?)" +msgstr "" + +#: src/setup/ncurses/mconf.c:398 +msgid "Invalid input, expecting floating point value." +msgstr "" + +#: src/setup/ncurses/mconf.c:439 +msgid "Invalid input, expecting integer." +msgstr "" + +#: src/setup/ncurses/mconf.c:446 +msgid "Value is not in legal range." +msgstr "" + +#: src/setup/ncurses/mconf.c:512 src/setup/text/conf.c:569 +#, fuzzy, c-format +msgid "Configuration unchanged, no need to save.\n" +msgstr "" +"Konfiguration eller version av GNUnet ändrad. Du behöver köra \"%s\"!\n" + +#: src/setup/ncurses/mconf.c:518 +#, fuzzy +msgid "Do you wish to save your new configuration?" +msgstr "Vill du spara dina inställningar?" + +#: src/setup/ncurses/mconf.c:532 +#, fuzzy, c-format +msgid "" +"\n" +"End of configuration.\n" +msgstr " gconfig\tGTK-konfiguration\n" + +#: src/setup/ncurses/mconf.c:537 +#, c-format +msgid "" +"\n" +"Your configuration changes were NOT saved.\n" +msgstr "" + +#: src/setup/gnunet-win-tool.c:52 +msgid "list all network adapters" +msgstr "" + +#: src/setup/gnunet-win-tool.c:55 +msgid "install GNUnet as Windows service" +msgstr "" + +#: src/setup/gnunet-win-tool.c:58 +msgid "uninstall GNUnet service" +msgstr "" + +#: src/setup/gnunet-win-tool.c:61 +msgid "increase the maximum number of TCP/IP connections" +msgstr "" + +#: src/setup/gnunet-win-tool.c:64 +msgid "display a file's hash value" +msgstr "" + +#: src/setup/gnunet-win-tool.c:125 +#, c-format +msgid "GNUnet service installed successfully.\n" +msgstr "" + +#: src/setup/gnunet-win-tool.c:128 src/setup/gnunet-win-tool.c:156 +#, c-format +msgid "This version of Windows doesn't support services.\n" +msgstr "Denna version av Windows har inte stöd för tjänster.\n" + +#: src/setup/gnunet-win-tool.c:132 src/setup/gnunet-win-tool.c:160 +#, c-format +msgid "Error: can't open Service Control Manager: %s\n" +msgstr "" + +#: src/setup/gnunet-win-tool.c:137 +#, c-format +msgid "Error: can't create service: %s\n" +msgstr "Fel: kan inte skapa tjänst: %s\n" + +#: src/setup/gnunet-win-tool.c:140 src/setup/gnunet-win-tool.c:172 +#, c-format +msgid "Unknown error.\n" +msgstr "Okänt fel.\n" + +#: src/setup/gnunet-win-tool.c:153 +#, c-format +msgid "Service deleted.\n" +msgstr "Tjänst borttagen.\n" + +#: src/setup/gnunet-win-tool.c:165 +#, c-format +msgid "Error: can't access service: %s\n" +msgstr "" + +#: src/setup/gnunet-win-tool.c:169 +#, c-format +msgid "Error: can't delete service: %s\n" +msgstr "Fel: kan inte ta bort tjänst: %s\n" + +#: src/setup/gtk/ngconf.c:389 +#, fuzzy +msgid "Configuration saved." +msgstr "Konfigurationsfil \"%s\" skapad.\n" + +#: src/setup/gtk/ngconf.c:399 +#, fuzzy +msgid "Failed to save configuration." +msgstr "Kunde inte spara konfiguration!" + +#: src/setup/gtk/ngconf.c:424 +#, fuzzy +msgid "Configuration changed. Save?" +msgstr "Konfigurationsfil \"%s\" skapad.\n" + +#: src/setup/gtk/ngconf.c:437 +#, fuzzy +msgid "Error saving configuration." +msgstr "Kunde inte spara konfiguration!" + +#: src/setup/gtk/wizard_gtk.c:141 +#, fuzzy +msgid "(unknown connection)" +msgstr "Nätverksanslutning" + +#: src/setup/gtk/wizard_gtk.c:438 +#, fuzzy +msgid "Do you want to save the new configuration?" +msgstr "Vill du spara dina inställningar?" + +#: src/setup/gtk/wizard_gtk.c:470 +msgid "Unable to create user account:" +msgstr "Kunde inte skapa användarkonto:" + +#: src/setup/gtk/wizard_gtk.c:480 +msgid "Unable to change startup process:" +msgstr "" + +#: src/setup/gtk/wizard_gtk.c:495 +msgid "" +"Running gnunet-update failed.\n" +"This maybe due to insufficient permissions, please check your " +"configuration.\n" +"Finally, run gnunet-update manually." +msgstr "" + +#: src/setup/gnunet-setup.c:65 +#, c-format +msgid "Can only set one option per invocation.\n" +msgstr "" + +#: src/setup/gnunet-setup.c:73 +#, c-format +msgid "" +"Invalid syntax, argument to 'set' must have the format SECTION:" +"OPTION=VALUE.\n" +msgstr "" + +#: src/setup/gnunet-setup.c:87 +#, c-format +msgid "Can only display one option per invocation.\n" +msgstr "" + +#: src/setup/gnunet-setup.c:94 +#, c-format +msgid "" +"Invalid syntax, argument to 'get' must have the format SECTION:OPTION.\n" +msgstr "" + +#: src/setup/gnunet-setup.c:108 +msgid "generate configuration for gnunetd, the GNUnet daemon" +msgstr "" + +#: src/setup/gnunet-setup.c:111 src/setup/gnunet-setup.c:127 +#: src/server/gnunet-update.c:268 +msgid "print a value from the configuration file to stdout" +msgstr "skriv ut ett värde frÃ¥n konfigurationsfilen till standard ut" + +#: src/setup/gnunet-setup.c:113 src/setup/gnunet-setup.c:129 +msgid "Tool to setup GNUnet." +msgstr "Verktyg för att ställa in GNUnet." + +#: src/setup/gnunet-setup.c:115 src/setup/gnunet-setup.c:131 +#, fuzzy +msgid "update a value in the configuration file" +msgstr "skriv ut ett värde frÃ¥n konfigurationsfilen till standard ut" + +#: src/setup/gnunet-setup.c:338 +#, fuzzy, c-format +msgid "Too many arguments.\n" +msgstr "Ogiltiga kommandoradsargument.\n" + +#: src/setup/gnunet-setup.c:344 +#, fuzzy +msgid "No interface specified, using default.\n" +msgstr "Inget tabellnamn angivet, använder \"%s\".\n" + +#: src/setup/gnunet-setup.c:392 +#, fuzzy, c-format +msgid "Configuration file `%s' must be a filename (but is a directory).\n" +msgstr "Konfigurationsfil \"%s\" skapad.\n" + +#: src/setup/gnunet-setup.c:439 +#, c-format +msgid "Undefined option.\n" +msgstr "" + +#: src/setup/gnunet-setup.c:496 +#, fuzzy, c-format +msgid "`%s' is not available.\n" +msgstr "\"%s\" är inte tillgänglig." + +#: src/setup/gnunet-setup.c:516 +#, fuzzy, c-format +msgid "Unknown operation '%s'.\n" +msgstr "Okänd operation \"%s\"\n" + +#: src/setup/gnunet-setup.c:517 src/util/getopt/getopt.c:1072 +#, c-format +msgid "Use --help to get a list of options.\n" +msgstr "Använd --help för att fÃ¥ en lista pÃ¥ flaggor.\n" + +#: src/setup/text/conf.c:79 +#, fuzzy +msgid "yes" +msgstr "Ja" + +#: src/setup/text/conf.c:80 +msgid "no" +msgstr "" + +#: src/setup/text/conf.c:106 +#, c-format +msgid "\tEnter yes (%s), no (%s) or help (%s): " +msgstr "" + +#: src/setup/text/conf.c:115 +msgid "\tPossible choices:\n" +msgstr "" + +#: src/setup/text/conf.c:123 +msgid "\tUse single space prefix to avoid conflicts with hotkeys!\n" +msgstr "" + +#: src/setup/text/conf.c:125 +#, c-format +msgid "\tEnter string (type '%s' for default value `%s'): " +msgstr "" + +#: src/setup/text/conf.c:143 +#, c-format +msgid "\t Enter choice (default is %c): " +msgstr "" + +#: src/setup/text/conf.c:147 +#, c-format +msgid "\tEnter floating point (type '%s' for default value %f): " +msgstr "" + +#: src/setup/text/conf.c:153 +#, c-format +msgid "" +"\tEnter unsigned integer in interval [%llu,%llu] (type '%s' for default " +"value %llu): " +msgstr "" + +#: src/setup/text/conf.c:187 +#, fuzzy, c-format +msgid "Yes\n" +msgstr "Ja" + +#: src/setup/text/conf.c:192 +#, fuzzy, c-format +msgid "No\n" +msgstr "Nej" + +#: src/setup/text/conf.c:195 src/setup/text/conf.c:236 +#: src/setup/text/conf.c:266 src/setup/text/conf.c:329 +#: src/setup/text/conf.c:387 +#, fuzzy, c-format +msgid "Help\n" +msgstr "Hjälp" + +#: src/setup/text/conf.c:198 src/setup/text/conf.c:213 +#: src/setup/text/conf.c:276 src/setup/text/conf.c:307 +#: src/setup/text/conf.c:365 +#, fuzzy, c-format +msgid "Abort\n" +msgstr "_Om" + +#: src/setup/text/conf.c:354 src/setup/text/conf.c:416 +#, c-format +msgid "" +"\n" +"Invalid entry, try again (use '?' for help): " +msgstr "" + +#: src/setup/text/conf.c:422 +#, c-format +msgid "Unknown kind %x (internal error). Skipping option.\n" +msgstr "" + +#: src/setup/text/conf.c:484 +msgid "\tDescend? (y/n/?) " +msgstr "" + +#: src/setup/text/conf.c:493 +msgid "Aborted.\n" +msgstr "" + +#: src/setup/text/conf.c:506 +#, fuzzy +msgid "Invalid entry.\n" +msgstr "Ogiltiga argument: " + +#: src/setup/text/conf.c:524 +#, c-format +msgid "Unknown kind %x (internal error). Aborting.\n" +msgstr "" + +#: src/setup/text/conf.c:556 +#, c-format +msgid "You can always press ENTER to keep the current value.\n" +msgstr "" + +#: src/setup/text/conf.c:557 +#, c-format +msgid "Use the '%s' key to abort.\n" +msgstr "" + +#: src/setup/text/conf.c:575 +#, c-format +msgid "" +"Save configuration? Answer 'y' for yes, 'n' for no, 'r' to repeat " +"configuration. " +msgstr "" + +#: src/setup/text/conf.c:590 +#, fuzzy, c-format +msgid "Configuration was unchanged, no need to save.\n" +msgstr "" +"Konfiguration eller version av GNUnet ändrad. Du behöver köra \"%s\"!\n" + +#: src/setup/text/conf.c:599 +#, fuzzy, c-format +msgid "Configuration file `%s' written.\n" +msgstr "Konfigurationsfil \"%s\" skapad.\n" + +#: src/setup/lib/tree.c:191 +#, c-format +msgid "" +"Internal error: entry `%s' in section `%s' not found for visibility change!\n" +msgstr "" + +#: src/setup/lib/wizard_util.c:126 +msgid "Can't open Service Control Manager" +msgstr "" + +#: src/setup/lib/wizard_util.c:132 +msgid "Can't create service" +msgstr "Kan inte skapa tjänst" + +#: src/setup/lib/wizard_util.c:136 +msgid "Error changing the permissions of the GNUnet directory" +msgstr "Fel vid ändring av rättigheterna pÃ¥ GNUnet-katalogen" + +#: src/setup/lib/wizard_util.c:141 +#, fuzzy +msgid "Cannot write to the registry" +msgstr "Kan inte skriva till registret" + +#: src/setup/lib/wizard_util.c:144 +msgid "Can't access the service" +msgstr "Kan inte tillgÃ¥ tjänsten" + +#: src/setup/lib/wizard_util.c:147 +msgid "Can't delete the service" +msgstr "Kan inte ta bort tjänsten" + +#: src/setup/lib/wizard_util.c:150 +msgid "Unknown error" +msgstr "Okänt fel" + +#: src/setup/lib/wizard_util.c:186 +msgid "This version of Windows does not support multiple users." +msgstr "Denna version för Windows har inte stöd för flera användare." + +#: src/setup/lib/wizard_util.c:190 +msgid "Error creating user" +msgstr "Fel vid skapandet av användare" + +#: src/setup/lib/wizard_util.c:194 +msgid "Error accessing local security policy" +msgstr "Fel vid Ã¥tkomst av lokal säkerhetspolicy" + +#: src/setup/lib/wizard_util.c:199 +msgid "Error granting service right to user" +msgstr "" + +#: src/setup/lib/wizard_util.c:204 +msgid "Unknown error while creating a new user" +msgstr "Okänt fel vid skapandet av ny användare" + +#: src/setup/lib/gns.c:297 +#, c-format +msgid "" +"Configuration does not satisfy constraints of configuration specification " +"file `%s'!\n" +msgstr "" + +# drive = hard drive ? +#: src/util/disk/storage.c:172 +#, fuzzy, c-format +msgid "`%s' failed for drive `%s': %u\n" +msgstr "\"%s\" misslyckades för enhet %s: %u\n" + +#: src/util/disk/storage.c:524 +#, fuzzy, c-format +msgid "Expected `%s' to be a directory!\n" +msgstr "\"%s\" förväntade att \"%s\" skulle vara en katalog!\n" + +#: src/util/error/error.c:152 +#, c-format +msgid "Message `%.*s' repeated %u times in the last %llus\n" +msgstr "" + +#: src/util/error/error.c:254 +#, c-format +msgid "" +"\n" +"Press any key to continue\n" +msgstr "" + +#: src/util/error/error.c:336 src/util/error/error.c:371 +msgid "DEBUG" +msgstr "FELSÖKNING" + +#: src/util/error/error.c:338 src/util/error/error.c:373 +msgid "STATUS" +msgstr "" + +#: src/util/error/error.c:340 src/util/error/error.c:377 +msgid "WARNING" +msgstr "VARNING" + +#: src/util/error/error.c:342 src/util/error/error.c:379 +msgid "ERROR" +msgstr "FEL" + +#: src/util/error/error.c:344 src/util/error/error.c:381 +msgid "FATAL" +msgstr "ÖDESDIGER" + +#: src/util/error/error.c:346 src/util/error/error.c:383 +msgid "USER" +msgstr "" + +#: src/util/error/error.c:348 src/util/error/error.c:385 +msgid "ADMIN" +msgstr "" + +#: src/util/error/error.c:350 src/util/error/error.c:387 +msgid "DEVELOPER" +msgstr "" + +#: src/util/error/error.c:352 src/util/error/error.c:389 +msgid "REQUEST" +msgstr "" + +#: src/util/error/error.c:354 src/util/error/error.c:391 +msgid "BULK" +msgstr "" + +#: src/util/error/error.c:356 src/util/error/error.c:393 +msgid "IMMEDIATE" +msgstr "" + +#: src/util/error/error.c:358 +msgid "ALL" +msgstr "" + +#: src/util/error/error.c:375 +msgid "INFO" +msgstr "INFO" + +#: src/util/error/error.c:394 +msgid "NOTHING" +msgstr "INGET" + +#: src/util/network_client/tcpio.c:98 src/util/network_client/tcpio.c:154 +msgid "Could not find valid value for HOST in section NETWORK.\n" +msgstr "" + +#: src/util/network_client/tcpio.c:123 +#, fuzzy, c-format +msgid "Syntax error in configuration entry HOST in section NETWORK: `%s'\n" +msgstr "Syntaxfel i konfigurationsfil \"%s\" pÃ¥ rad %d.\n" + +#: src/util/network_client/tcpio.c:335 +#, c-format +msgid "Error connecting to %s:%u. Is the daemon running?\n" +msgstr "" + +#: src/util/network_client/tcpio.c:398 +#, fuzzy, c-format +msgid "Cannot connect to %s:%u: %s\n" +msgstr "Kan inte ansluta till %u.%u.%u.%u:%u: %s\n" + +#: src/util/network_client/tcpio.c:636 +#, fuzzy +msgid "Reading result from gnunetd failed, reply invalid!\n" +msgstr "\"%s\" misslyckades, svar ogiltigt!\n" + +#: src/util/getopt/setoption.c:59 +#, c-format +msgid "" +"Setting option `%s' in section `%s' to `%s' when processing command line " +"option `%s' was denied.\n" +msgstr "" + +#: src/util/getopt/setoption.c:138 src/util/getopt/setoption.c:155 +#, c-format +msgid "You must pass a number to the `%s' option.\n" +msgstr "Du mÃ¥ste skicka med ett nummer till flaggan \"%s\".\n" + +#: src/util/getopt/printhelp.c:49 +#, c-format +msgid "" +"Arguments mandatory for long options are also mandatory for short options.\n" +msgstr "" +"Argument som är obligatoriska för lÃ¥nga flaggor är ocksÃ¥ obligatoriska för " +"korta flaggor.\n" + +#: src/util/getopt/getopt.c:684 +#, c-format +msgid "%s: option `%s' is ambiguous\n" +msgstr "%s: flagga \"%s\" är tvetydig\n" + +#: src/util/getopt/getopt.c:710 +#, c-format +msgid "%s: option `--%s' does not allow an argument\n" +msgstr "%s: flagga \"--%s\" tillÃ¥ter inte ett argument\n" + +#: src/util/getopt/getopt.c:716 +#, c-format +msgid "%s: option `%c%s' does not allow an argument\n" +msgstr "%s: flagga \"%c%s\" tillÃ¥ter inte ett argument\n" + +#: src/util/getopt/getopt.c:737 src/util/getopt/getopt.c:909 +#, c-format +msgid "%s: option `%s' requires an argument\n" +msgstr "%s: flagga \"%s\" kräver ett argument\n" + +#: src/util/getopt/getopt.c:767 +#, c-format +msgid "%s: unrecognized option `--%s'\n" +msgstr "%s: okänd flagga \"--%s\"\n" + +#: src/util/getopt/getopt.c:771 +#, c-format +msgid "%s: unrecognized option `%c%s'\n" +msgstr "%s: okänd flagga \"%c%s\"\n" + +#: src/util/getopt/getopt.c:797 +#, c-format +msgid "%s: illegal option -- %c\n" +msgstr "%s: otillÃ¥ten flagga -- %c\n" + +#: src/util/getopt/getopt.c:799 +#, c-format +msgid "%s: invalid option -- %c\n" +msgstr "%s: ogiltig flagga -- %c\n" + +#: src/util/getopt/getopt.c:828 src/util/getopt/getopt.c:958 +#, c-format +msgid "%s: option requires an argument -- %c\n" +msgstr "%s: flagga kräver ett argument -- %c\n" + +#: src/util/getopt/getopt.c:876 +#, c-format +msgid "%s: option `-W %s' is ambiguous\n" +msgstr "%s: flagga \"-W %s\" är tvetydig\n" + +#: src/util/getopt/getopt.c:894 +#, c-format +msgid "%s: option `-W %s' does not allow an argument\n" +msgstr "%s: flagga \"-W %s\" tillÃ¥ter inte ett argument\n" + +#: src/util/network/ip.c:96 src/util/network/ip.c:160 src/transports/ip.c:260 +#, fuzzy, c-format +msgid "No interface specified in section `%s' under `%s'!\n" +msgstr "" +"Inga nätverksgränssnitt angivna i konfigurationssektionen \"%s\" under \"%s" +"\"!\n" + +#: src/util/network/ip.c:127 src/util/network/ip.c:246 src/transports/ip.c:291 +#, c-format +msgid "Could not obtain IP for interface `%s' using `%s'.\n" +msgstr "" + +#: src/util/network/ip.c:216 +#, c-format +msgid "" +"Could not find interface `%s' using `%s', trying to find another interface.\n" +msgstr "" + +#: src/util/network/ip.c:295 +#, c-format +msgid "Could not find an IP address for interface `%s'.\n" +msgstr "" + +#: src/util/network/ip.c:306 +#, c-format +msgid "" +"There is more than one IP address specified for interface `%s'.\n" +"GNUnet will use %s.\n" +msgstr "" + +#: src/util/network/ip.c:330 +#, c-format +msgid "Could not resolve `%s' to determine our IP address: %s\n" +msgstr "" + +#: src/util/network/ip.c:363 +#, c-format +msgid "GNUnet now uses the IP address %s.\n" +msgstr "" + +#: src/util/network/ipcheck.c:106 src/util/network/ipcheck.c:136 +#: src/util/network/ipcheck.c:186 src/util/network/ipcheck.c:211 +#: src/util/network/ipcheck.c:219 +#, c-format +msgid "Invalid format for IP: `%s'\n" +msgstr "Ogiltigt format för IP: \"%s\"\n" + +#: src/util/network/ipcheck.c:167 +#, c-format +msgid "Invalid network notation ('/%d' is not legal in IPv4 CIDR)." +msgstr "Ogiltig nätverksnotation (\"/%d\" är inte giltig i IPv4 CIDR)." + +#: src/util/network/ipcheck.c:269 +#, c-format +msgid "Invalid network notation (does not end with ';': `%s')\n" +msgstr "Ogiltig nätverksnotation (slutar inte med \";\": \"%s\")\n" + +#: src/util/network/ipcheck.c:306 +#, fuzzy, c-format +msgid "Wrong format `%s' for netmask\n" +msgstr "Fel format \"%s\" för nätmask: %s\n" + +#: src/util/network/ipcheck.c:338 +#, fuzzy, c-format +msgid "Wrong format `%s' for network\n" +msgstr "Fel format \"%s\" för nätverk: %s\n" + +#: src/util/network/dns.c:472 +#, fuzzy, c-format +msgid "Could not resolve `%s' (%s): %s\n" +msgstr "Kunde inte slÃ¥ upp \"%s\": %s\n" + +#: src/util/network/dns.c:523 src/util/network/dns.c:591 +#, c-format +msgid "Could not find IP of host `%s': %s\n" +msgstr "" + +#: src/util/network/select.c:310 +msgid "Received malformed message (too small) from connection. Closing.\n" +msgstr "" + +#: src/util/network/select.c:495 +#, c-format +msgid "select listen socket for `%s' not valid!\n" +msgstr "" + +#: src/util/config/config.c:296 +#, c-format +msgid "Syntax error in configuration file `%s' at line %d.\n" +msgstr "Syntaxfel i konfigurationsfil \"%s\" pÃ¥ rad %d.\n" + +#: src/util/config/config.c:592 +#, c-format +msgid "" +"Configuration value '%llu' for '%s' in section '%s' is out of legal bounds [%" +"llu,%llu]\n" +msgstr "" + +#: src/util/config/config.c:602 +#, c-format +msgid "Configuration value '%s' for '%s' in section '%s' should be a number\n" +msgstr "" + +#: src/util/config/config.c:688 +#, c-format +msgid "" +"Configuration value '%s' for '%s' in section '%s' is not in set of legal " +"choices\n" +msgstr "" + +#: src/util/crypto/locking_gcrypt.c:80 +#, c-format +msgid "libgcrypt has not the expected version (version %s is required).\n" +msgstr "libgcrypt har inte den förväntande versionen (version %s krävs).\n" + +#: src/util/crypto/symcipher_gcrypt.c:46 src/util/crypto/symcipher_gcrypt.c:53 +#: src/util/crypto/hostkey_gcrypt.c:64 src/util/crypto/hostkey_gcrypt.c:71 +#: src/util/loggers/file.c:271 src/util/loggers/file.c:289 +#: src/applications/sqstore_sqlite/sqlite.c:45 +#: src/applications/sqstore_sqlite/sqlite.c:52 +#: src/applications/kvstore_sqlite/kv_sqlite.c:44 +#: src/applications/kvstore_sqlite/kv_sqlite.c:51 +#: src/applications/kvstore_mysql/kv_mysql.c:44 +#: src/applications/kvstore_mysql/kv_mysql.c:51 +#: src/applications/dstore_sqlite/dstore.c:94 +#: src/applications/dstore_sqlite/dstore.c:101 +#: src/applications/dstore_sqlite/dstore.c:222 +#: src/applications/dstore_sqlite/dstore.c:259 +#: src/applications/dstore_sqlite/dstore.c:285 +#: src/applications/dstore_sqlite/dstore.c:345 +#: src/applications/dstore_sqlite/dstore.c:366 +#: src/applications/dstore_sqlite/dstore.c:378 +#: src/applications/dstore_sqlite/dstore.c:407 +#: src/applications/dstore_sqlite/dstore.c:511 +#: src/applications/dstore_sqlite/dstore.c:555 +#: src/include/gnunet_util_error.h:249 src/include/gnunet_util_error.h:256 +#: src/include/gnunet_util_error.h:263 +#, c-format +msgid "`%s' failed at %s:%d with error: %s\n" +msgstr "\"%s\" misslyckades vid %s:%d med fel: %s\n" + +#: src/util/crypto/hostkey_gcrypt.c:907 +#, c-format +msgid "RSA signature verification failed at %s:%d: %s\n" +msgstr "" + +#: src/util/os/user.c:108 src/util/os/user.c:125 +#, fuzzy, c-format +msgid "`%s' returned with error code %u" +msgstr "\"%s\" misslyckades vid %s:%d med fel: %s\n" + +#: src/util/os/user.c:155 src/util/os/user.c:200 +#, fuzzy, c-format +msgid "Cannot obtain information about user `%s': %s\n" +msgstr "Kan inte öppna konfigurationsfil \"%s\".\n" + +#: src/util/os/user.c:156 +msgid "No such user" +msgstr "" + +#: src/util/os/user.c:171 +#, c-format +msgid "Cannot change user/group to `%s': %s\n" +msgstr "Kan inte ändra användare/grupp till \"%s\": %s\n" + +#: src/util/os/semaphore.c:227 +#, c-format +msgid "Can't create semaphore: %i" +msgstr "Kan inte skapa semafor: %i" + +#: src/util/os/cpustatus.c:464 +msgid "Cannot query the CPU usage (Windows NT).\n" +msgstr "Kan inte frÃ¥ga efter CPU-användning (Windows NT).\n" + +#: src/util/os/cpustatus.c:487 +msgid "Cannot query the CPU usage (Win 9x)\n" +msgstr "Kan inte frÃ¥ga efter CPU-användning (Win 9x).\n" + +#: src/util/os/dso.c:59 +#, c-format +msgid "Initialization of plugin mechanism failed: %s!\n" +msgstr "Initiering av insticksmekanism misslyckades: %s!\n" + +#: src/util/os/dso.c:120 +#, fuzzy, c-format +msgid "`%s' failed for library `%s' with error: %s\n" +msgstr "\"%s\" misslyckades vid %s:%d med fel: %s\n" + +#: src/util/os/dso.c:162 +#, fuzzy, c-format +msgid "`%s' failed to resolve method '%s' with error: %s\n" +msgstr "\"%s\" misslyckades för fil \"%s\" vid %s:%d med fel: %s\n" + +#: src/util/os/statuscalls.c:197 src/util/os/statuscalls.c:342 +#, fuzzy, c-format +msgid "Failed to parse interface data from `%s'.\n" +msgstr "Misslyckades att läsa kompislista frÃ¥n \"%s\"\n" + +#: src/util/os/statuscalls.c:390 src/util/os/statuscalls.c:400 +#, c-format +msgid "" +"No network interfaces defined in configuration section `%s' under `%s'!\n" +msgstr "" +"Inga nätverksgränssnitt angivna i konfigurationssektionen \"%s\" under \"%s" +"\"!\n" + +#: src/util/os/osconfig.c:153 +msgid "Setting open descriptor limit not supported.\n" +msgstr "" + +#: src/util/os/osconfig.c:463 src/util/os/osconfig.c:492 +#, fuzzy, c-format +msgid "Command `%s' failed with error code %u\n" +msgstr "\"%s\" misslyckades vid %s:%d med fel: %s\n" + +#: src/util/os/priority.c:78 +#, fuzzy, c-format +msgid "Invalid process priority `%s'\n" +msgstr "Ogiltigt svar pÃ¥ \"%s\".\n" + +#: src/util/threads/semaphore.c:168 src/util/threads/pthread.c:157 +#: src/util/threads/mutex.c:146 +#, fuzzy, c-format +msgid "Real-time delay violation (%llu ms) at %s:%u\n" +msgstr "Icke-förväntad mycket stor allokering (%u byte) vid %s:%d!\n" + +#: src/util/threads/pthread.c:169 src/util/threads/pthread.c:176 +#: src/util/threads/pthread.c:182 src/util/threads/pthread.c:276 +#, fuzzy, c-format +msgid "`%s' failed with error code %s: %s\n" +msgstr "\"%s\" misslyckades vid %s:%d med fel: %s\n" + +#: src/util/threads/pthread.c:188 src/util/threads/pthread.c:286 +#, fuzzy, c-format +msgid "`%s' failed with error code %d: %s\n" +msgstr "\"%s\" misslyckades vid %s:%d med fel: %s\n" + +#: src/util/threads/mutex.c:155 src/util/threads/mutex.c:201 +#, fuzzy, c-format +msgid "Invalid argument for `%s'.\n" +msgstr "Ogiltigt argument: \"%s\"\n" + +#: src/util/threads/mutex.c:160 +#, c-format +msgid "Deadlock due to `%s'.\n" +msgstr "" + +#: src/util/threads/mutex.c:187 +#, c-format +msgid "Lock acquired for too long (%llu ms) at %s:%u\n" +msgstr "" + +#: src/util/threads/mutex.c:207 +#, fuzzy, c-format +msgid "Permission denied for `%s'.\n" +msgstr "Ã…tkomst nekad för \"%s\" vid %s:%d.\n" + +#: src/util/boot/startup.c:259 +#, fuzzy, c-format +msgid "Failed to run %s: %s %d\n" +msgstr "Fel vid %s:%d.\n" + +#: src/util/string/string.c:55 +msgid "ms" +msgstr "ms" + +#: src/util/string/string.c:61 +msgid "s" +msgstr "s" + +#: src/util/string/string.c:65 +msgid "m" +msgstr "m" + +#: src/util/string/string.c:69 +msgid "h" +msgstr "h" + +#: src/util/string/string.c:73 +msgid " days" +msgstr " dagar" + +#: src/util/string/string.c:89 +msgid "b" +msgstr "b" + +#: src/util/string/string.c:95 +msgid "KiB" +msgstr "" + +#: src/util/string/string.c:99 +msgid "MiB" +msgstr "" + +#: src/util/string/string.c:103 +msgid "GiB" +msgstr "" + +#: src/util/string/string.c:107 +msgid "TiB" +msgstr "" + +#: src/util/string/string.c:226 +msgid "Failed to expand `$HOME': environment variable `HOME' not set" +msgstr "" + +#: src/util/loggers/file.c:229 +#, fuzzy, c-format +msgid "Failed to open log-file `%s': %s\n" +msgstr "Misslyckades att leverera \"%s\" meddelande.\n" + +#: src/util/loggers/file.c:250 +#, fuzzy +msgid "GNUnet error log" +msgstr "SpÃ¥ra GNUnets nätverkstopologi." + +#: src/util/loggers/memory.c:72 +msgid "Out of memory (for logging)\n" +msgstr "" + +#: src/util/pseudonym/names.c:79 +#, fuzzy +msgid "no-name" +msgstr "Visa namn" + +#: src/applications/datastore/datastore.c:183 +#: src/applications/datastore/datastore.c:199 +#, c-format +msgid "Availability test failed for `%s' at %s:%d.\n" +msgstr "Tillgänglighetstest misslyckades för \"%s\" vid %s:%d.\n" + +#: src/applications/datastore/datastore.c:401 +msgid "# requests filtered by bloom filter" +msgstr "" + +#: src/applications/datastore/datastore.c:403 +msgid "# bloom filter false positives" +msgstr "" + +#: src/applications/datastore/datastore.c:406 +msgid "# bytes allowed in datastore" +msgstr "" + +#: src/applications/datastore/datastore.c:423 +msgid "Failed to load state service. Trying to do without.\n" +msgstr "" + +#: src/applications/datastore/datastore.c:529 +#, c-format +msgid "Datastore conversion at approximately %u%%\n" +msgstr "" + +#: src/applications/datastore/datastore.c:576 +#, fuzzy, c-format +msgid "Starting datastore conversion (this may take a while).\n" +msgstr "Skapar ny värdnyckel (det här kan ta en stund).\n" + +#: src/applications/datastore/datastore.c:584 +#, c-format +msgid "Completed datastore conversion.\n" +msgstr "" + +#: src/applications/datastore/datastore.c:592 +msgid "Failed to load sqstore service. Check your configuration!\n" +msgstr "" + +#: src/applications/rpc/rpc.c:339 +#, fuzzy, c-format +msgid "" +"%s:%d - RPC %s:%p could not be registered: another callback is already using " +"this name (%p)\n" +msgstr "%s::%s - RPC %s:%p kunde inte avregistreras: hittades inte\n" + +#: src/applications/rpc/rpc.c:398 +#, fuzzy, c-format +msgid "%s:%d - async RPC %s:%p could not be unregistered: not found\n" +msgstr "%s::%s - RPC %s:%p kunde inte avregistreras: hittades inte\n" + +#: src/applications/rpc/rpc.c:951 +#, c-format +msgid "`%s' registering handlers %d %d %d\n" +msgstr "\"%s\" registrerar handtag %d %d %d\n" + +#: src/applications/rpc/rpc.c:972 +#, c-format +msgid "Failed to initialize `%s' service.\n" +msgstr "Misslyckades att initiera tjänsten \"%s\".\n" + +#: src/applications/tbench/tbenchtest.c:53 +#, c-format +msgid "Using %u messages of size %u for %u times.\n" +msgstr "" + +#: src/applications/tbench/tbenchtest.c:85 +#, c-format +msgid "Times: max %16llu min %16llu mean %12.3f variance %12.3f\n" +msgstr "" + +#: src/applications/tbench/tbenchtest.c:89 +#, c-format +msgid "Loss: max %16u min %16u mean %12.3f variance %12.3f\n" +msgstr "" + +#: src/applications/tbench/tbenchtest.c:95 +#, c-format +msgid "" +"\n" +"Failed to receive reply from gnunetd.\n" +msgstr "" + +#: src/applications/tbench/tbenchtest.c:149 +#, c-format +msgid "Running benchmark...\n" +msgstr "" + +#: src/applications/tbench/tbench.c:422 +msgid "allows profiling of direct peer-to-peer connections" +msgstr "" + +#: src/applications/tbench/gnunet-tbench.c:63 +#: src/applications/tracekit/gnunet-tracekit.c:302 +msgid "Start GNUnet transport benchmarking tool." +msgstr "" + +#: src/applications/tbench/gnunet-tbench.c:65 +msgid "output in gnuplot format" +msgstr "utdata i gnuplot-format" + +#: src/applications/tbench/gnunet-tbench.c:69 +msgid "number of iterations" +msgstr "antal iterationer" + +#: src/applications/tbench/gnunet-tbench.c:73 +msgid "number of messages to use per iteration" +msgstr "antal meddelanden att använda per iteration" + +#: src/applications/tbench/gnunet-tbench.c:76 +msgid "receiver host identifier (ENC file name)" +msgstr "" + +#: src/applications/tbench/gnunet-tbench.c:79 +msgid "message size" +msgstr "meddelandestorlek" + +#: src/applications/tbench/gnunet-tbench.c:82 +msgid "sleep for SPACE ms after each a message block" +msgstr "" + +#: src/applications/tbench/gnunet-tbench.c:85 +msgid "time to wait for the completion of an iteration (in ms)" +msgstr "" + +#: src/applications/tbench/gnunet-tbench.c:90 +msgid "number of messages in a message block" +msgstr "antal meddelanden i ett meddelandeblock" + +#: src/applications/tbench/gnunet-tbench.c:126 +#: src/applications/tracekit/gnunet-tracekit.c:352 +#: src/applications/tracekit/tracekittest.c:133 +#: src/applications/template/gnunet-template.c:95 +#: src/applications/stats/gnunet-stats.c:121 +#: src/applications/vpn/gnunet-vpn.c:154 +#, c-format +msgid "Error establishing connection with gnunetd.\n" +msgstr "" + +#: src/applications/tbench/gnunet-tbench.c:142 +#, c-format +msgid "You must specify a receiver!\n" +msgstr "Du mÃ¥ste ange en mottagare!\n" + +#: src/applications/tbench/gnunet-tbench.c:152 +#, c-format +msgid "Invalid receiver peer ID specified (`%s' is not valid name).\n" +msgstr "" + +#: src/applications/tbench/gnunet-tbench.c:189 +#, c-format +msgid "Time:\n" +msgstr "Tid:\n" + +#: src/applications/tbench/gnunet-tbench.c:190 +#, c-format +msgid "\tmax %llums\n" +msgstr "\tmax %llums\n" + +#: src/applications/tbench/gnunet-tbench.c:191 +#, c-format +msgid "\tmin %llums\n" +msgstr "\tmin %llums\n" + +#: src/applications/tbench/gnunet-tbench.c:192 +#, c-format +msgid "\tmean %8.4fms\n" +msgstr "" + +#: src/applications/tbench/gnunet-tbench.c:193 +#, c-format +msgid "\tvariance %8.4fms\n" +msgstr "" + +#: src/applications/tbench/gnunet-tbench.c:195 +#, c-format +msgid "Loss:\n" +msgstr "" + +#: src/applications/tbench/gnunet-tbench.c:196 +#, c-format +msgid "\tmax %u\n" +msgstr "\tmax %u\n" + +#: src/applications/tbench/gnunet-tbench.c:197 +#, c-format +msgid "\tmin %u\n" +msgstr "\tmin %u\n" + +#: src/applications/tbench/gnunet-tbench.c:198 +#, c-format +msgid "\tmean %8.4f\n" +msgstr "" + +#: src/applications/tbench/gnunet-tbench.c:199 +#, c-format +msgid "\tvariance %8.4f\n" +msgstr "" + +#: src/applications/tbench/gnunet-tbench.c:205 +#, c-format +msgid "Output format not known, this should not happen.\n" +msgstr "Utdataformat är inte känt, detta bör inte hända.\n" + +#: src/applications/tbench/gnunet-tbench.c:211 +#, c-format +msgid "" +"\n" +"Did not receive the message from gnunetd. Is gnunetd running?\n" +msgstr "" + +#: src/applications/traffic/traffic.c:454 +#, c-format +msgid "# bytes transmitted of type %d" +msgstr "# byte skickade av typen %d" + +#: src/applications/traffic/traffic.c:470 +#, c-format +msgid "# bytes received of type %d" +msgstr "# byte mottagna av typen %d" + +#: src/applications/traffic/traffic.c:489 +#, fuzzy, c-format +msgid "# bytes received in plaintext of type %d" +msgstr "# byte mottagna av typen %d" + +#: src/applications/traffic/traffic.c:652 +msgid "tracks bandwidth utilization by gnunetd" +msgstr "" + +#: src/applications/sqstore_sqlite/sqlite.c:218 +#, fuzzy, c-format +msgid "Unable to initialize SQLite: %s.\n" +msgstr "Kunde inte initiera SQLite.\n" + +#: src/applications/sqstore_sqlite/sqlite.c:434 +#: src/applications/sqstore_sqlite/sqlite.c:469 +#, c-format +msgid "Invalid data in %s. Trying to fix (by deletion).\n" +msgstr "" + +#: src/applications/sqstore_sqlite/sqlite.c:435 +#: src/applications/sqstore_sqlite/sqlite.c:470 +msgid "sqlite datastore" +msgstr "" + +#: src/applications/sqstore_sqlite/sqlite.c:1474 +#: src/applications/sqstore_mysql/mysql.c:1078 +msgid "# bytes in datastore" +msgstr "" + +#: src/applications/sqstore_sqlite/sqlite.c:1476 +msgid "# bytes allocated by SQLite" +msgstr "" + +#: src/applications/sqstore_mysql/mysql.c:1085 +#: src/applications/sqstore_mysql/mysql.c:1160 +msgid "" +"Failed to load MySQL database module. Check that MySQL is running and " +"configured properly!\n" +msgstr "" + +#: src/applications/tracekit/gnunet-tracekit.c:104 +#, c-format +msgid "`%s' connected to `%s'.\n" +msgstr "\"%s\" ansluten till \"%s\".\n" + +#: src/applications/tracekit/gnunet-tracekit.c:110 +#: src/applications/tracekit/tracekittest.c:67 +#, c-format +msgid "`%s' is not connected to any peer.\n" +msgstr "\"%s\" är inte ansluten till nÃ¥gon ändpunkt.\n" + +#: src/applications/tracekit/gnunet-tracekit.c:295 +msgid "probe network to the given DEPTH" +msgstr "" + +#: src/applications/tracekit/gnunet-tracekit.c:299 +msgid "" +"specify output format; 0 for human readable output, 1 for dot, 2 for vcg" +msgstr "" + +#: src/applications/tracekit/gnunet-tracekit.c:306 +#, fuzzy +msgid "use PRIORITY for the priority of the trace request" +msgstr "ange prioritet för innehÃ¥llet" + +#: src/applications/tracekit/gnunet-tracekit.c:310 +msgid "wait DELAY seconds for replies" +msgstr "" + +#: src/applications/tracekit/gnunet-tracekit.c:344 +#, c-format +msgid "" +"Format specification invalid. Use 0 for user-readable, 1 for dot, 2 for " +"vcg.\n" +msgstr "" + +#: src/applications/tracekit/tracekittest.c:60 +#, fuzzy, c-format +msgid "`%.*s' connected to `%.*s'.\n" +msgstr "\"%s\" ansluten till \"%s\".\n" + +#: src/applications/tracekit/tracekit.c:440 +msgid "allows mapping of the network topology" +msgstr "tillÃ¥ter kartläggning av nätverkstopologin" + +#: src/applications/advertising/advertising_test.c:47 +#: src/applications/hostlist/hostlisttest.c:40 +#: src/applications/session/sessiontest.c:40 +#: src/applications/session/sessiontest_nat_http.c:40 +#: src/applications/session/sessiontest_nat.c:40 +#: src/applications/stats/statistics.c:247 +msgid "# of connected peers" +msgstr "# av anslutna parter" + +#: src/applications/advertising/advertising.c:194 +#, fuzzy, c-format +msgid "HELLO message from `%s' has an invalid signature. Dropping.\n" +msgstr "Meddelande frÃ¥n \"%s\" kastades bort: ogiltigt format.\n" + +#: src/applications/advertising/advertising.c:205 +msgid "HELLO message has expiration too far in the future. Dropping.\n" +msgstr "" + +#: src/applications/advertising/advertising.c:406 +msgid "Could not send HELLO+PING, ping buffer full.\n" +msgstr "" + +#: src/applications/advertising/advertising.c:429 +msgid "Failed to create an advertisement for this peer. Will not send PING.\n" +msgstr "" + +#: src/applications/advertising/advertising.c:581 +#, c-format +msgid "Advertising my transport %d to selected peers.\n" +msgstr "Annonserar min transport %d till valda ändpunkter.\n" + +#: src/applications/advertising/advertising.c:590 +msgid "" +"Announcing ourselves pointless: no other peers are known to us so far.\n" +msgstr "" + +#: src/applications/advertising/advertising.c:868 +msgid "# Peer advertisements received" +msgstr "" + +#: src/applications/advertising/advertising.c:871 +msgid "# Peer advertisements of type NAT received" +msgstr "" + +#: src/applications/advertising/advertising.c:874 +msgid "# Peer advertisements confirmed via PONG" +msgstr "" + +#: src/applications/advertising/advertising.c:877 +msgid "# Peer advertisements updating earlier HELLOs" +msgstr "" + +#: src/applications/advertising/advertising.c:880 +#, fuzzy +msgid "# Peer advertisements discarded due to load" +msgstr "Nätverksannonsering avstängd i konfigurationen!\n" + +#: src/applications/advertising/advertising.c:883 +msgid "# Peer advertisements for unsupported transport" +msgstr "" + +#: src/applications/advertising/advertising.c:886 +msgid "# Peer advertisements not confirmed due to ping busy" +msgstr "" + +#: src/applications/advertising/advertising.c:889 +msgid "# Peer advertisements not confirmed due to lack of self ad" +msgstr "" + +#: src/applications/advertising/advertising.c:892 +msgid "# Peer advertisements not confirmed due to send error" +msgstr "" + +#: src/applications/advertising/advertising.c:894 +msgid "# Self advertisments transmitted" +msgstr "" + +#: src/applications/advertising/advertising.c:896 +msgid "# Foreign advertisements forwarded" +msgstr "" + +#: src/applications/advertising/advertising.c:898 +#: src/applications/pingpong/pingpong.c:528 +msgid "# plaintext PING messages sent" +msgstr "# PING-meddelanden i klartext skickade" + +#: src/applications/advertising/advertising.c:904 +#: src/applications/session/connect.c:932 +#, c-format +msgid "`%s' registering handler %d (plaintext and ciphertext)\n" +msgstr "\"%s\" registrerar handtag %d (klartext och kryptotext)\n" + +#: src/applications/advertising/advertising.c:922 +msgid "" +"ensures that this peer is known by other peers and discovers other peers" +msgstr "" + +#: src/applications/fragmentation/fragmentation.c:578 +msgid "# messages defragmented" +msgstr "" + +#: src/applications/fragmentation/fragmentation.c:580 +msgid "# messages fragmented" +msgstr "" + +#: src/applications/fragmentation/fragmentation.c:581 +msgid "# fragments discarded" +msgstr "" + +#: src/applications/fragmentation/fragmentation.c:592 +#, c-format +msgid "`%s' registering handler %d\n" +msgstr "\"%s\" registrerar handtag %d\n" + +#: src/applications/topology_default/topology.c:466 +#, fuzzy, c-format +msgid "Could not read friends list `%s'\n" +msgstr "Misslyckades att läsa kompislista frÃ¥n \"%s\"\n" + +#: src/applications/topology_default/topology.c:485 +#, c-format +msgid "Failed to read friends list from `%s'\n" +msgstr "Misslyckades att läsa kompislista frÃ¥n \"%s\"\n" + +#: src/applications/topology_default/topology.c:505 +msgid "Syntax error in topology specification, skipping bytes.\n" +msgstr "" + +#: src/applications/topology_default/topology.c:523 +#, c-format +msgid "Syntax error in topology specification, skipping bytes `%s'.\n" +msgstr "" + +#: src/applications/topology_default/topology.c:535 +msgid "" +"Fewer friends specified than required by minimum friend count. Will only " +"connect to friends.\n" +msgstr "" + +#: src/applications/topology_default/topology.c:543 +msgid "" +"More friendly connections required than target total number of connections.\n" +msgstr "" + +#: src/applications/topology_default/topology.c:726 +msgid "maintains GNUnet default mesh topology" +msgstr "" + +#: src/applications/chat/tools/gnunet-chat.c:107 +msgid "anonymous" +msgstr "" + +#: src/applications/chat/tools/gnunet-chat.c:113 +#, fuzzy, c-format +msgid "`%s' said: %s\n" +msgstr "\"%s\" %s misslyckades: %s\n" + +#: src/applications/chat/tools/gnunet-chat.c:116 +#: src/applications/chat/tools/gnunet-chat.c:119 +#, fuzzy, c-format +msgid "`%s' said to you: %s\n" +msgstr "\"%s\" %s misslyckades: %s\n" + +# drive = hard drive ? +#: src/applications/chat/tools/gnunet-chat.c:122 +#, fuzzy, c-format +msgid "`%s' said for sure: %s\n" +msgstr "\"%s\" misslyckades för enhet %s: %u\n" + +# drive = hard drive ? +#: src/applications/chat/tools/gnunet-chat.c:125 +#, fuzzy, c-format +msgid "`%s' said to you for sure: %s\n" +msgstr "\"%s\" misslyckades för enhet %s: %u\n" + +#: src/applications/chat/tools/gnunet-chat.c:128 +#, c-format +msgid "`%s' was confirmed that you received: %s\n" +msgstr "" + +#: src/applications/chat/tools/gnunet-chat.c:131 +#, c-format +msgid "`%s' was confirmed that you and only you received: %s\n" +msgstr "" + +#: src/applications/chat/tools/gnunet-chat.c:134 +#, c-format +msgid "`%s' was confirmed that you received from him or her: %s\n" +msgstr "" + +#: src/applications/chat/tools/gnunet-chat.c:139 +#, c-format +msgid "`%s' was confirmed that you and only you received from him or her: %s\n" +msgstr "" + +#: src/applications/chat/tools/gnunet-chat.c:142 +#, fuzzy, c-format +msgid "`%s' said off the record: %s\n" +msgstr "\"%s\" misslyckades vid %s:%d med fel: %s\n" + +#: src/applications/chat/tools/gnunet-chat.c:145 +#, c-format +msgid "<%s> said using an unknown message type: %s\n" +msgstr "" + +#: src/applications/chat/tools/gnunet-chat.c:167 +#, c-format +msgid "`%s' entered the room\n" +msgstr "" + +#: src/applications/chat/tools/gnunet-chat.c:167 +#, c-format +msgid "`%s' left the room\n" +msgstr "" + +#: src/applications/chat/tools/gnunet-chat.c:239 +#: src/applications/chat/tools/gnunet-chat.c:348 +#, fuzzy, c-format +msgid "Failed to send message.\n" +msgstr "Misslyckades att leverera \"%s\" meddelande.\n" + +#: src/applications/chat/tools/gnunet-chat.c:265 +#: src/applications/chat/tools/gnunet-chat.c:524 +#, fuzzy, c-format +msgid "Joined room `%s' as user `%s'.\n" +msgstr "Ogiltigt svar till \"%s\" frÃ¥n motpart \"%s\".\n" + +#: src/applications/chat/tools/gnunet-chat.c:293 +#, fuzzy, c-format +msgid "Changed username to `%s'.\n" +msgstr "Kan inte ändra användare/grupp till \"%s\": %s\n" + +#: src/applications/chat/tools/gnunet-chat.c:301 +#, fuzzy, c-format +msgid "Unknown command `%s'.\n" +msgstr "Okänd operation \"%s\"\n" + +#: src/applications/chat/tools/gnunet-chat.c:316 +#, c-format +msgid "Syntax: /msg USERNAME MESSAGE" +msgstr "" + +#: src/applications/chat/tools/gnunet-chat.c:324 +#, fuzzy, c-format +msgid "Unknown user `%s'\n" +msgstr "Okänd operation \"%s\"\n" + +#: src/applications/chat/tools/gnunet-chat.c:339 +#, c-format +msgid "User `%s' is currently not in the room!\n" +msgstr "" + +#: src/applications/chat/tools/gnunet-chat.c:361 +#, c-format +msgid "Users in room `%s': " +msgstr "" + +#: src/applications/chat/tools/gnunet-chat.c:390 +msgid "" +"Use `/join #roomname' to join a chat room. Joining a room will cause you to " +"leave the current room" +msgstr "" + +#: src/applications/chat/tools/gnunet-chat.c:393 +msgid "" +"Use `/nick nickname' to change your nickname. This will cause you to leave " +"the current room and immediately rejoin it with the new name." +msgstr "" + +#: src/applications/chat/tools/gnunet-chat.c:396 +msgid "" +"Use `/msg nickname message' to send a private message to the specified user" +msgstr "" + +#: src/applications/chat/tools/gnunet-chat.c:398 +msgid "The `/notice' command is an alias for `/msg'" +msgstr "" + +#: src/applications/chat/tools/gnunet-chat.c:400 +msgid "The `/query' command is an alias for `/msg'" +msgstr "" + +#: src/applications/chat/tools/gnunet-chat.c:402 +msgid "Use `/quit' to terminate gnunet-chat" +msgstr "" + +#: src/applications/chat/tools/gnunet-chat.c:404 +msgid "The `/leave' command is an alias for `/quit'" +msgstr "" + +#: src/applications/chat/tools/gnunet-chat.c:407 +msgid "Use `/names' to list all of the current members in the chat room" +msgstr "" + +#: src/applications/chat/tools/gnunet-chat.c:409 +msgid "Use `/help command' to get help for a specific command" +msgstr "" + +#: src/applications/chat/tools/gnunet-chat.c:457 +msgid "Join a chat on GNUnet." +msgstr "" + +#: src/applications/chat/tools/gnunet-chat.c:461 +msgid "set the nickname to use (required)" +msgstr "" + +#: src/applications/chat/tools/gnunet-chat.c:464 +msgid "set the chat room to join" +msgstr "" + +#: src/applications/chat/tools/gnunet-chat.c:495 +#, fuzzy, c-format +msgid "You must specify a nickname\n" +msgstr "Du mÃ¥ste ange en mottagare!\n" + +#: src/applications/chat/tools/gnunet-chat.c:515 +#, fuzzy, c-format +msgid "Failed to join room `%s'\n" +msgstr "Misslyckades att läsa kompislista frÃ¥n \"%s\"\n" + +#: src/applications/chat/module/chat.c:325 +#, fuzzy, c-format +msgid "`%s' registering CS handlers %d and %d\n" +msgstr "\"%s\" registrerar handtagen %d och %d\n" + +#: src/applications/chat/module/chat.c:347 +msgid "enables P2P-chat (incomplete)" +msgstr "aktiverar P2P-chatt (ej komplett)" + +#: src/applications/chat/lib/messaging.c:353 +#: src/applications/identity/hostkey.c:122 +#, fuzzy, c-format +msgid "Failed to access GNUnet home directory `%s'\n" +msgstr "Filformatsfel (inte en GNUnet-katalog?)\n" + +#: src/applications/chat/lib/messaging.c:389 +#, fuzzy, c-format +msgid "Existing key in file `%s' failed format check, creating new key.\n" +msgstr "" +"Existerande värdnyckel i filen \"%s\" felade vid formatkontroll, skapar en " +"ny värdnyckel.\n" + +#: src/applications/chat/lib/messaging.c:399 +#, fuzzy +msgid "Creating new key for this nickname (this may take a while).\n" +msgstr "Skapar ny värdnyckel (det här kan ta en stund).\n" + +#: src/applications/chat/lib/messaging.c:411 +#, fuzzy +msgid "Done creating key.\n" +msgstr "Klar med skapandet av värdnyckel.\n" + +#: src/applications/dstore_mysql/dstore_mysql.c:471 +msgid "Failed to initialize MySQL database connection for dstore.\n" +msgstr "" + +#: src/applications/dstore_mysql/dstore_mysql.c:494 +#: src/applications/dstore_sqlite/dstore.c:636 +#, fuzzy +msgid "# bytes in dstore" +msgstr "# byte krypterade" + +#: src/applications/dstore_mysql/dstore_mysql.c:496 +#: src/applications/dstore_sqlite/dstore.c:638 +msgid "# max bytes allowed in dstore" +msgstr "" + +#: src/applications/transport/transport.c:191 +#, c-format +msgid "" +"Converting peer address to string failed, transport type %d not supported\n" +msgstr "" + +#: src/applications/transport/transport.c:246 +#, c-format +msgid "Transport connection attempt failed, transport type %d not supported\n" +msgstr "" + +#: src/applications/transport/transport.c:299 +#, c-format +msgid "" +"Transport failed to connect to peer `%s' (%u HELLOs known, none worked)\n" +msgstr "" + +#: src/applications/transport/transport.c:376 +#, fuzzy, c-format +msgid "Transmission attempt failed, transport type %d unknown.\n" +msgstr "Ingen transport av typ %d är känd.\n" + +#: src/applications/transport/transport.c:500 +#, c-format +msgid "No transport of type %d known.\n" +msgstr "Ingen transport av typ %d är känd.\n" + +#: src/applications/transport/transport.c:560 +msgid "No transport succeeded in creating a hello!\n" +msgstr "" + +#: src/applications/transport/transport.c:761 +#, fuzzy, c-format +msgid "Loading transports `%s'\n" +msgstr "Testar transport(er) %s\n" + +#: src/applications/transport/transport.c:781 +#, fuzzy, c-format +msgid "Could not load transport plugin `%s'\n" +msgstr "Kunde inte slÃ¥ upp \"%s\": %s\n" + +#: src/applications/transport/transport.c:795 +#, c-format +msgid "Transport library `%s' did not provide required function '%s%s'.\n" +msgstr "" + +#: src/applications/transport/transport.c:824 +#, fuzzy, c-format +msgid "Loaded transport `%s'\n" +msgstr "Tillgängliga transport(er): %s\n" + +#: src/applications/transport/transport.c:836 +#: src/server/gnunet-peer-info.c:252 +#, c-format +msgid "I am peer `%s'.\n" +msgstr "Jag är ändpunkt \"%s\".\n" + +#: src/applications/dht/tools/dht_multipeer_test.c:80 +#: src/applications/dht/tools/dht_twopeer_test.c:47 +#: src/applications/dht/module/table.c:783 +#, fuzzy +msgid "# dht connections" +msgstr "Nätverksanslutning" + +#: src/applications/dht/tools/dht-query.c:54 +msgid "Query (get KEY, put KEY VALUE) DHT table." +msgstr "" + +#: src/applications/dht/tools/dht-query.c:58 +#, fuzzy +msgid "allow TIME ms to process a GET command" +msgstr "tillÃ¥t TID ms för behandling av varje kommando" + +#: src/applications/dht/tools/dht-query.c:107 +#, fuzzy, c-format +msgid "Issuing `%s(%s,%s)' command.\n" +msgstr "\"%s(%s,%s)\" misslyckades.\n" + +#: src/applications/dht/tools/dht-query.c:142 +#: src/applications/fs/tools/gnunet-auto-share.c:669 +#, c-format +msgid "Failed to connect to gnunetd.\n" +msgstr "Misslyckades att ansluta till gnunetd.\n" + +#: src/applications/dht/tools/dht-query.c:155 +#, c-format +msgid "Command `%s' requires an argument (`%s').\n" +msgstr "Kommando \"%s\" kräver ett argument (\"%s\").\n" + +#: src/applications/dht/tools/dht-query.c:172 +#, c-format +msgid "Command `%s' requires two arguments (`%s' and `%s').\n" +msgstr "Kommando \"%s\" kräver tvÃ¥ argument (\"%s\" och \"%s\").\n" + +#: src/applications/dht/tools/dht-query.c:183 +#, c-format +msgid "Unsupported command `%s'. Aborting.\n" +msgstr "Kommando \"%s\" stöds ej. Avbryter.\n" + +#: src/applications/dht/module/table.c:785 +#, fuzzy +msgid "# dht discovery messages received" +msgstr "# krypterade PONG-meddelanden mottagna" + +#: src/applications/dht/module/table.c:787 +msgid "# dht route host lookups performed" +msgstr "" + +#: src/applications/dht/module/table.c:789 +#, fuzzy +msgid "# dht discovery messages sent" +msgstr "# krypterade PONG-meddelanden skickade" + +#: src/applications/dht/module/routing.c:879 +msgid "# dht replies routed" +msgstr "" + +#: src/applications/dht/module/routing.c:881 +msgid "# dht requests routed" +msgstr "" + +#: src/applications/dht/module/routing.c:883 +msgid "# dht get requests received" +msgstr "" + +#: src/applications/dht/module/routing.c:885 +msgid "# dht put requests received" +msgstr "" + +#: src/applications/dht/module/routing.c:887 +#, fuzzy +msgid "# dht results received" +msgstr "# byte mottogs via TCP" + +#: src/applications/dht/module/routing.c:892 +#, fuzzy, c-format +msgid "`%s' registering p2p handlers: %d %d %d\n" +msgstr "\"%s\" registrerar handtag %d %d %d\n" + +#: src/applications/dht/module/cs.c:122 +#, c-format +msgid "`%s' failed. Terminating connection to client.\n" +msgstr "\"%s\" misslyckades. Terminerar anslutning till klient.\n" + +#: src/applications/dht/module/cs.c:250 +#, fuzzy, c-format +msgid "`%s' registering client handlers: %d %d\n" +msgstr "\"%s\" registrerar klienthandtag %d\n" + +#: src/applications/dht/module/cs.c:273 +msgid "Enables efficient non-anonymous routing" +msgstr "" + +#: src/applications/identity/hostkey.c:155 +#, c-format +msgid "" +"Existing hostkey in file `%s' failed format check, creating new hostkey.\n" +msgstr "" +"Existerande värdnyckel i filen \"%s\" felade vid formatkontroll, skapar en " +"ny värdnyckel.\n" + +#: src/applications/identity/hostkey.c:164 +msgid "Creating new hostkey (this may take a while).\n" +msgstr "Skapar ny värdnyckel (det här kan ta en stund).\n" + +#: src/applications/identity/hostkey.c:176 +msgid "Done creating hostkey.\n" +msgstr "Klar med skapandet av värdnyckel.\n" + +#: src/applications/identity/identity.c:333 +#, c-format +msgid "" +"File `%s' in directory `%s' does not match naming convention. Removed.\n" +msgstr "" + +#: src/applications/identity/identity.c:408 +#, c-format +msgid "Still no peers found in `%s'!\n" +msgstr "" + +#: src/applications/identity/identity.c:731 +#: src/applications/identity/identity.c:757 +#, c-format +msgid "Removed file `%s' containing invalid HELLO data.\n" +msgstr "" + +#: src/applications/identity/identity.c:809 +#, c-format +msgid "Signature failed verification: peer `%s' not known.\n" +msgstr "" + +#: src/applications/identity/identity.c:819 +msgid "Signature failed verification: signature invalid.\n" +msgstr "" + +#: src/applications/identity/identity.c:935 +#: src/applications/identity/identity.c:1058 +#, c-format +msgid "Peer `%s' is currently strictly blacklisted (for another %llums).\n" +msgstr "" + +#: src/applications/identity/identity.c:1061 +#, c-format +msgid "Peer `%s' is currently blacklisted (for another %llums).\n" +msgstr "" + +#: src/applications/pingpong/pingpong.c:134 +#: src/applications/pingpong/pingpong.c:203 +#: src/applications/pingpong/pingpong.c:273 +#: src/applications/pingpong/pingpong.c:345 +#, c-format +msgid "Received malformed `%s' message. Dropping.\n" +msgstr "" + +#: src/applications/pingpong/pingpong.c:146 +msgid "Received ping for another peer. Dropping.\n" +msgstr "" + +#: src/applications/pingpong/pingpong.c:215 +#, fuzzy, c-format +msgid "Received PING from `%s' not destined for us!\n" +msgstr "Mottog PING som ej var ämnat för oss!\n" + +#: src/applications/pingpong/pingpong.c:315 +#: src/applications/pingpong/pingpong.c:381 +msgid "" +"Could not match PONG against any PING. Try increasing MAX_PING_PONG " +"constant.\n" +msgstr "" + +#: src/applications/pingpong/pingpong.c:425 +msgid "Cannot create PING, table full. Try increasing MAX_PING_PONG.\n" +msgstr "Kan inte skapa PING, tabell är full. Försök att öka MAX_PING_PONG.\n" + +#: src/applications/pingpong/pingpong.c:518 +msgid "# encrypted PONG messages received" +msgstr "# krypterade PONG-meddelanden mottagna" + +#: src/applications/pingpong/pingpong.c:520 +msgid "# plaintext PONG messages received" +msgstr "# klartext PONG-meddelanden mottagna" + +#: src/applications/pingpong/pingpong.c:522 +msgid "# encrypted PING messages received" +msgstr "# krypterade PING-meddelanden mottagna" + +#: src/applications/pingpong/pingpong.c:524 +msgid "# PING messages created" +msgstr "# PING-meddelanden skapade" + +#: src/applications/pingpong/pingpong.c:526 +#: src/applications/session/connect.c:926 +msgid "# encrypted PONG messages sent" +msgstr "# krypterade PONG-meddelanden skickade" + +#: src/applications/pingpong/pingpong.c:530 +#: src/applications/session/connect.c:924 +msgid "# encrypted PING messages sent" +msgstr "# krypterade PING-meddelanden skickade" + +#: src/applications/pingpong/pingpong.c:532 +#, fuzzy +msgid "# plaintext PONG messages sent" +msgstr "# PING-meddelanden i klartext skickade" + +#: src/applications/pingpong/pingpong.c:536 +#, fuzzy +msgid "# plaintext PONG transmissions failed" +msgstr "# klartext PONG-meddelanden mottagna" + +#: src/applications/pingpong/pingpong.c:546 +#, c-format +msgid "`%s' registering handlers %d %d (plaintext and ciphertext)\n" +msgstr "\"%s\" registrerar handtag %d %d (klartext och kryptotext)\n" + +#: src/applications/hostlist/hostlist.c:165 +msgid "# hostlist requests received" +msgstr "" + +#: src/applications/hostlist/hostlist.c:167 +msgid "# hostlist HELLOs returned" +msgstr "" + +#: src/applications/hostlist/hostlist.c:169 +msgid "# hostlist bytes returned" +msgstr "" + +#: src/applications/hostlist/hostlist.c:199 +msgid "integrated HTTP hostlist server" +msgstr "" + +#: src/applications/session/connect.c:238 +#, c-format +msgid "Session key from peer `%s' could not be verified.\n" +msgstr "" + +#: src/applications/session/connect.c:282 +#, fuzzy, c-format +msgid "Cannot encrypt sessionkey, peer `%s' not known!\n" +msgstr "Kan inte kryptera sessionsnyckel, andra parten är inte känd!\n" + +#: src/applications/session/connect.c:489 +#, c-format +msgid "Could not create any HELLO for myself (have transports `%s')!\n" +msgstr "" + +#: src/applications/session/connect.c:599 +#, fuzzy, c-format +msgid "Session key received from peer `%s' has invalid format (discarded).\n" +msgstr "Meddelande mottaget frÃ¥n motpart är ogiltig.\n" + +#: src/applications/session/connect.c:632 +#, c-format +msgid "Session key received from peer `%s' is for `%s' and not for me!\n" +msgstr "" + +#: src/applications/session/connect.c:659 +#, c-format +msgid "Invalid `%s' message received from peer `%s'.\n" +msgstr "" + +#: src/applications/session/connect.c:670 +#, c-format +msgid "setkey `%s' from `%s' fails CRC check (have: %u, want %u).\n" +msgstr "" + +#: src/applications/session/connect.c:728 +#, c-format +msgid "" +"Error parsing encrypted session key from `%s', given message part size is " +"invalid.\n" +msgstr "" + +#: src/applications/session/connect.c:741 +#, c-format +msgid "Unknown type in embedded message from `%s': %u (size: %u)\n" +msgstr "" + +#: src/applications/session/connect.c:916 +msgid "# session keys sent" +msgstr "# sessionsnycklar skickade" + +#: src/applications/session/connect.c:918 +msgid "# session keys rejected" +msgstr "# sessionnycklar vägrade" + +#: src/applications/session/connect.c:920 +msgid "# session keys accepted" +msgstr "# sessionsnycklar accepterade" + +#: src/applications/session/connect.c:922 +msgid "# sessions established" +msgstr "# sessioner etablerade" + +#: src/applications/fs/tools/gnunet-pseudonym.c:70 +#: src/applications/fs/tools/gnunet-auto-share.c:199 +#: src/applications/fs/tools/gnunet-insert.c:246 +#: src/applications/fs/tools/gnunet-search.c:125 +#: src/applications/fs/tools/gnunet-download.c:77 +msgid "set the desired LEVEL of sender-anonymity" +msgstr "" + +#: src/applications/fs/tools/gnunet-pseudonym.c:73 +msgid "automate creation of a namespace by starting a collection" +msgstr "" + +#: src/applications/fs/tools/gnunet-pseudonym.c:77 +msgid "create a new pseudonym under the given NICKNAME" +msgstr "skapa en ny pseudonym under angivet SMEKNAMN" + +#: src/applications/fs/tools/gnunet-pseudonym.c:80 +msgid "delete the pseudonym with the given NICKNAME" +msgstr "ta bort pseudonymen med angivet SMEKNAMN" + +#: src/applications/fs/tools/gnunet-pseudonym.c:83 +msgid "end automated building of a namespace (ends collection)" +msgstr "" + +#: src/applications/fs/tools/gnunet-pseudonym.c:85 +msgid "Create new pseudonyms, delete pseudonyms or list existing pseudonyms." +msgstr "" +"Skapa nya pseudonymer, ta bort pseudonymer eller lista existerande " +"pseudonymer." + +#: src/applications/fs/tools/gnunet-pseudonym.c:89 +msgid "" +"use the given keyword to advertise the namespace (use when creating a new " +"pseudonym)" +msgstr "" + +#: src/applications/fs/tools/gnunet-pseudonym.c:92 +msgid "specify metadata describing the namespace or collection" +msgstr "" + +#: src/applications/fs/tools/gnunet-pseudonym.c:96 +msgid "" +"do not generate an advertisement for this namespace (use when creating a new " +"pseudonym)" +msgstr "" + +#: src/applications/fs/tools/gnunet-pseudonym.c:99 +msgid "do not list the pseudonyms from the pseudonym database" +msgstr "" + +#: src/applications/fs/tools/gnunet-pseudonym.c:103 +msgid "" +"specify IDENTIFIER to be the address of the entrypoint to content in the " +"namespace (use when creating a new pseudonym)" +msgstr "" + +#: src/applications/fs/tools/gnunet-pseudonym.c:106 +msgid "set the rating of a namespace" +msgstr "" + +#: src/applications/fs/tools/gnunet-pseudonym.c:141 +#, c-format +msgid "Namespace `%s' has rating %d.\n" +msgstr "" + +#: src/applications/fs/tools/gnunet-pseudonym.c:143 +#, c-format +msgid "Namespace `%s' (%s) has rating %d.\n" +msgstr "" + +#: src/applications/fs/tools/gnunet-pseudonym.c:175 +#, c-format +msgid "\tRating (after update): %d\n" +msgstr "" + +#: src/applications/fs/tools/gnunet-pseudonym.c:179 +#: src/applications/fs/tools/gnunet-pseudonym.c:241 +#: src/applications/fs/tools/gnunet-insert.c:101 +#, fuzzy, c-format +msgid "\tUnknown namespace `%s'\n" +msgstr "Okänd operation \"%s\"\n" + +#: src/applications/fs/tools/gnunet-pseudonym.c:217 +#, c-format +msgid "Collection stopped.\n" +msgstr "Samling stoppad.\n" + +#: src/applications/fs/tools/gnunet-pseudonym.c:219 +#, c-format +msgid "Failed to stop collection (not active?).\n" +msgstr "Misslyckades att stoppa samling (inte aktiv?).\n" + +#: src/applications/fs/tools/gnunet-pseudonym.c:230 +#, c-format +msgid "Pseudonym `%s' deleted.\n" +msgstr "Pseudonym \"%s\" togs bort.\n" + +#: src/applications/fs/tools/gnunet-pseudonym.c:235 +#, c-format +msgid "Error deleting pseudonym `%s' (does not exist?).\n" +msgstr "Fel vid borttagning av pseudonym \"%s\" (existerar den?).\n" + +#: src/applications/fs/tools/gnunet-pseudonym.c:256 +#, fuzzy +msgid "Started collection.\n" +msgstr "Startade samling \"%s\".\n" + +#: src/applications/fs/tools/gnunet-pseudonym.c:260 +msgid "Failed to start collection.\n" +msgstr "Misslyckades att starta samling.\n" + +#: src/applications/fs/tools/gnunet-pseudonym.c:296 +#, fuzzy +msgid "Could not create namespace.\n" +msgstr "Kunde inte skapa namnrymd \"%s\" (existerar?).\n" + +#: src/applications/fs/tools/gnunet-pseudonym.c:304 +#, c-format +msgid "Namespace `%s' created (root: %s).\n" +msgstr "Namnrymd \"%s\" skapad(rot: %s).\n" + +#: src/applications/fs/tools/gnunet-pseudonym.c:321 +#, fuzzy, c-format +msgid "You must specify a name for the collection (`%s' option).\n" +msgstr "Du mÃ¥ste ange ett namn för PID-filen i sektion \"%s\" under \"%s\".\n" + +#: src/applications/fs/tools/gnunet-pseudonym.c:329 +#, c-format +msgid "Could not access namespace information.\n" +msgstr "Kunde inte komma Ã¥t namnrymdsinformation.\n" + +#: src/applications/fs/tools/gnunet-directory.c:84 +#, c-format +msgid "==> Directory `%s':\n" +msgstr "==> Katalog \"%s\":\n" + +#: src/applications/fs/tools/gnunet-directory.c:88 +#, c-format +msgid "=\tError reading directory.\n" +msgstr "=\tFel vid läsning av katalog.\n" + +#: src/applications/fs/tools/gnunet-directory.c:118 +#, c-format +msgid "File format error (not a GNUnet directory?)\n" +msgstr "Filformatsfel (inte en GNUnet-katalog?)\n" + +#: src/applications/fs/tools/gnunet-directory.c:120 +#, c-format +msgid "%d files found in directory.\n" +msgstr "%d filer hittades i katalog.\n" + +#: src/applications/fs/tools/gnunet-directory.c:135 +msgid "Perform directory related operations." +msgstr "Genomför katalogrelaterade operationer." + +#: src/applications/fs/tools/gnunet-directory.c:138 +msgid "remove all entries from the directory database and stop tracking URIs" +msgstr "" + +#: src/applications/fs/tools/gnunet-directory.c:142 +msgid "list entries from the directory database" +msgstr "" + +#: src/applications/fs/tools/gnunet-directory.c:145 +msgid "start tracking entries for the directory database" +msgstr "" + +#: src/applications/fs/tools/gnunet-directory.c:168 +#, c-format +msgid "Listed %d matching entries.\n" +msgstr "Listade %d matchande poster.\n" + +#: src/applications/fs/tools/gnunet-auto-share.c:117 +#, fuzzy, c-format +msgid "Upload of `%s' at %llu out of %llu bytes.\n" +msgstr "Hämtning av fil \"%s\" vid %16llu av %16llu byte (%8.3f kbps)\n" + +#: src/applications/fs/tools/gnunet-auto-share.c:130 +#, fuzzy, c-format +msgid "Upload of `%s' complete, URI is `%s'.\n" +msgstr "" +"Uppladdning av \"%s\" klar, aktuell genomsnittshastighet är %8.3f kbps.\n" + +#: src/applications/fs/tools/gnunet-auto-share.c:139 +#, fuzzy, c-format +msgid "Upload aborted.\n" +msgstr "Nedladdning avbruten." + +#: src/applications/fs/tools/gnunet-auto-share.c:145 +#, fuzzy, c-format +msgid "Error uploading file: %s\n" +msgstr "" +"\n" +"Fel vid uppladdning av fil: %s\n" + +#: src/applications/fs/tools/gnunet-auto-share.c:154 +#, fuzzy, c-format +msgid "Starting upload of `%s'.\n" +msgstr "Startade samling \"%s\".\n" + +#: src/applications/fs/tools/gnunet-auto-share.c:164 +#, fuzzy, c-format +msgid "Uploading suspended.\n" +msgstr "Uppladdning misslyckades.\n" + +#: src/applications/fs/tools/gnunet-auto-share.c:179 +#, fuzzy, c-format +msgid "Uploading `%s' resumed.\n" +msgstr "Uppladdning vägrades!" + +#: src/applications/fs/tools/gnunet-auto-share.c:186 +#, c-format +msgid "Unexpected event: %d\n" +msgstr "" + +#: src/applications/fs/tools/gnunet-auto-share.c:205 +msgid "" +"run in debug mode; gnunet-auto-share will not daemonize and error messages " +"will be written to stderr instead of a logfile" +msgstr "" + +#: src/applications/fs/tools/gnunet-auto-share.c:211 +#: src/applications/fs/tools/gnunet-insert.c:259 +#, fuzzy +msgid "" +"do not use libextractor to add additional references to directory entries " +"and/or the published file" +msgstr "" +"använd libextractor för att lägga till ytterligare direktreferenser till " +"katalogposter" + +#: src/applications/fs/tools/gnunet-auto-share.c:213 +msgid "Automatically share a directory." +msgstr "" + +#: src/applications/fs/tools/gnunet-auto-share.c:216 +#: src/applications/fs/tools/gnunet-insert.c:273 +msgid "" +"add an additional keyword for all files and directories (this option can be " +"specified multiple times)" +msgstr "" + +#: src/applications/fs/tools/gnunet-auto-share.c:221 +#: src/applications/fs/tools/gnunet-insert.c:290 +msgid "specify the priority of the content" +msgstr "ange prioritet för innehÃ¥llet" + +#: src/applications/fs/tools/gnunet-auto-share.c:468 +#: src/applications/fs/tools/gnunet-auto-share.c:903 +#, fuzzy, c-format +msgid "Could not access `%s': %s\n" +msgstr "Kunde inte köra \"%s\": %s\n" + +#: src/applications/fs/tools/gnunet-auto-share.c:547 +#, c-format +msgid "Unknown keyword type `%s' in metadata configuration\n" +msgstr "" + +#: src/applications/fs/tools/gnunet-auto-share.c:652 +#, fuzzy, c-format +msgid "Failed to stop running gnunet-auto-share.\n" +msgstr "Misslyckades att ansluta till gnunetd.\n" + +#: src/applications/fs/tools/gnunet-auto-share.c:918 +#, c-format +msgid "Directory `%s' is already on the list of shared directories.\n" +msgstr "" + +#: src/applications/fs/tools/gnunet-auto-share.c:939 +msgid "" +"The specified directories were added to the list of shared directories.\n" +msgstr "" + +#: src/applications/fs/tools/gnunet-auto-share.c:961 +#, fuzzy, c-format +msgid "Could not open logfile `%s': %s\n" +msgstr "Kunde inte slÃ¥ upp \"%s\": %s\n" + +#: src/applications/fs/tools/gnunet-insert.c:115 +#, c-format +msgid "Created entry `%s' in namespace `%s'\n" +msgstr "Skapade post \"%s\" i namnrymd \"%s\"\n" + +#: src/applications/fs/tools/gnunet-insert.c:120 +#, c-format +msgid "Failed to add entry to namespace `%s' (does it exist?)\n" +msgstr "" + +#: src/applications/fs/tools/gnunet-insert.c:135 +#, c-format +msgid "Keywords for file `%s':\n" +msgstr "Nyckelord för fil \"%s\":\n" + +#: src/applications/fs/tools/gnunet-insert.c:144 +msgid "filename" +msgstr "" + +#: src/applications/fs/tools/gnunet-insert.c:146 +msgid "mimetype" +msgstr "" + +#: src/applications/fs/tools/gnunet-insert.c:186 +#, fuzzy, c-format +msgid "%16llu of %16llu bytes inserted (estimating %6s to completion) - %s\n" +msgstr "%16llu av %16llu byte inlagda (uppskattar %s till färdigställd)\n" + +#: src/applications/fs/tools/gnunet-insert.c:198 +#, fuzzy, c-format +msgid "Upload of `%s' complete, %llu bytes took %llu seconds (%8.3f KiB/s).\n" +msgstr "" +"Uppladdning av \"%s\" klar, %llu byte tog %llu sekunder (%8.3f kbps).\n" + +#: src/applications/fs/tools/gnunet-insert.c:209 +#, c-format +msgid "File `%s' has URI: %s\n" +msgstr "Fil \"%s\" har URI: %s\n" + +#: src/applications/fs/tools/gnunet-insert.c:220 +#, fuzzy, c-format +msgid "" +"\n" +"Upload aborted.\n" +msgstr "Nedladdning avbruten." + +#: src/applications/fs/tools/gnunet-insert.c:225 +#, fuzzy, c-format +msgid "" +"\n" +"Error uploading file: %s" +msgstr "" +"\n" +"Fel vid uppladdning av fil: %s\n" + +#: src/applications/fs/tools/gnunet-insert.c:234 +#, c-format +msgid "" +"\n" +"Unexpected event: %d\n" +msgstr "" + +#: src/applications/fs/tools/gnunet-insert.c:250 +msgid "" +"even if gnunetd is running on the local machine, force the creation of a " +"copy instead of making a link to the GNUnet share directory" +msgstr "" +"även om gnunetd körs pÃ¥ den lokala maskinen, tvinga fram skapandet av en " +"kopia istället för att göra en länk till GNUnets utdelade katalog" + +#: src/applications/fs/tools/gnunet-insert.c:255 +msgid "disable adding the creation time to the metadata of the uploaded file" +msgstr "" + +#: src/applications/fs/tools/gnunet-insert.c:263 +msgid "" +"print list of extracted keywords that would be used, but do not perform " +"upload" +msgstr "" + +#: src/applications/fs/tools/gnunet-insert.c:265 +msgid "Make files available to GNUnet for sharing." +msgstr "Gör filer tillgängliga via utdelning till GNUnet." + +#: src/applications/fs/tools/gnunet-insert.c:269 +msgid "" +"add an additional keyword for the top-level file or directory (this option " +"can be specified multiple times)" +msgstr "" + +#: src/applications/fs/tools/gnunet-insert.c:278 +msgid "set the meta-data for the given TYPE to the given VALUE" +msgstr "" + +#: src/applications/fs/tools/gnunet-insert.c:281 +msgid "" +"do not index, perform full insertion (stores entire file in encrypted form " +"in GNUnet database)" +msgstr "" + +#: src/applications/fs/tools/gnunet-insert.c:286 +msgid "" +"specify ID of an updated version to be published in the future (for " +"namespace insertions only)" +msgstr "" + +#: src/applications/fs/tools/gnunet-insert.c:294 +msgid "publish the files under the pseudonym NAME (place file into namespace)" +msgstr "" + +#: src/applications/fs/tools/gnunet-insert.c:297 +msgid "" +"only simulte the process but do not do any actual publishing (useful to " +"compute URIs)" +msgstr "" + +#: src/applications/fs/tools/gnunet-insert.c:301 +msgid "" +"set the ID of this version of the publication (for namespace insertions only)" +msgstr "" + +#: src/applications/fs/tools/gnunet-insert.c:305 +msgid "" +"URI to be published (can be used instead of passing a file to add keywords " +"to the file with the respective URI)" +msgstr "" + +#: src/applications/fs/tools/gnunet-insert.c:342 +#, fuzzy, c-format +msgid "You must specify one and only one filename for insertion.\n" +msgstr "Du mÃ¥ste ange en och endast en fil att avindexera.\n" + +#: src/applications/fs/tools/gnunet-insert.c:348 +#, fuzzy, c-format +msgid "You must NOT specify an URI and a filename.\n" +msgstr "Du mÃ¥ste ange en och endast en fil att avindexera.\n" + +#: src/applications/fs/tools/gnunet-insert.c:354 +#, c-format +msgid "Cannot extract metadata from a URI!\n" +msgstr "" + +#: src/applications/fs/tools/gnunet-insert.c:400 +#, c-format +msgid "Could not access namespace `%s' (does not exist?).\n" +msgstr "" + +#: src/applications/fs/tools/gnunet-insert.c:408 +#, fuzzy, c-format +msgid "Option `%s' is required when using option `%s'.\n" +msgstr "Kommando \"%s\" kräver ett argument (\"%s\").\n" + +#: src/applications/fs/tools/gnunet-insert.c:419 +#: src/applications/fs/tools/gnunet-insert.c:427 +#, c-format +msgid "Option `%s' makes no sense without option `%s'.\n" +msgstr "" + +#: src/applications/fs/tools/gnunet-search.c:128 +msgid "Search GNUnet for files." +msgstr "Sök GNUnet efter filer." + +#: src/applications/fs/tools/gnunet-search.c:132 +msgid "write encountered (decrypted) search results to FILENAME" +msgstr "" + +#: src/applications/fs/tools/gnunet-search.c:169 +#, c-format +msgid "Error converting arguments to URI!\n" +msgstr "" + +#: src/applications/fs/tools/gnunet-unindex.c:61 +#, c-format +msgid "" +"%16llu of %16llu bytes unindexed (estimating %llu seconds to " +"completion) " +msgstr "" +"%16llu av %16llu byte avindexerat (uppskattar %llu sekunder till " +"färdigställd) " + +#: src/applications/fs/tools/gnunet-unindex.c:73 +#, fuzzy, c-format +msgid "" +"\n" +"Unindexing of `%s' complete, %llu bytes took %llu seconds (%8.3f KiB/s).\n" +msgstr "" +"\n" +"Avindexering av \"%s\" klar, %llu byte tog %llu sekunder (%8.3f kbps).\n" + +#: src/applications/fs/tools/gnunet-unindex.c:88 +#, c-format +msgid "" +"\n" +"Error unindexing file: %s\n" +msgstr "" +"\n" +"Fel vid avindexering av fil: %s\n" + +#: src/applications/fs/tools/gnunet-unindex.c:108 +msgid "Unindex files." +msgstr "Avindexera filer." + +#: src/applications/fs/tools/gnunet-unindex.c:145 +#, fuzzy +msgid "Not enough arguments. You must specify a filename.\n" +msgstr "" +"Inte tillräckligt med argument. Du mÃ¥ste ange en URI till en GNUnet-fil\n" + +#: src/applications/fs/tools/gnunet-unindex.c:163 +#, c-format +msgid "`%s' failed. Is `%s' a file?\n" +msgstr "\"%s\" misslyckades. Är \"%s\" en fil?\n" + +#: src/applications/fs/tools/gnunet-download.c:82 +msgid "" +"download a GNUnet directory that has already been downloaded. Requires that " +"a filename of an existing file is specified instead of the URI. The " +"download will only download the top-level files in the directory unless the " +"`-R' option is also specified." +msgstr "" + +#: src/applications/fs/tools/gnunet-download.c:85 +msgid "delete incomplete downloads (when aborted with CTRL-C)" +msgstr "" + +#: src/applications/fs/tools/gnunet-download.c:87 +msgid "Download files from GNUnet." +msgstr "Ladda ner filer frÃ¥n GNUnet." + +#: src/applications/fs/tools/gnunet-download.c:91 +msgid "write the file to FILENAME" +msgstr "skriv filen till FILNAMN" + +#: src/applications/fs/tools/gnunet-download.c:95 +msgid "set the maximum number of parallel downloads that are allowed" +msgstr "" + +#: src/applications/fs/tools/gnunet-download.c:98 +msgid "download a GNUnet directory recursively" +msgstr "hämta en GNUnet-katalog rekursivt" + +#: src/applications/fs/tools/gnunet-download.c:119 +#, fuzzy, c-format +msgid "Download of file `%s' at %16llu out of %16llu bytes (%8.3f KiB/s)\n" +msgstr "Hämtning av fil \"%s\" vid %16llu av %16llu byte (%8.3f kbps)\n" + +#: src/applications/fs/tools/gnunet-download.c:133 +#, fuzzy, c-format +msgid "Download aborted.\n" +msgstr "Nedladdning avbruten." + +#: src/applications/fs/tools/gnunet-download.c:139 +#, c-format +msgid "Error downloading: %s\n" +msgstr "Fel vid nedladdning: %s\n" + +#: src/applications/fs/tools/gnunet-download.c:145 +#, fuzzy, c-format +msgid "Download of file `%s' complete. Speed was %8.3f KiB per second.\n" +msgstr "" +"Hämtning av fil \"%s\" färdig. Hastigheten var %8.3f kilobyte per sekund.\n" + +#: src/applications/fs/tools/gnunet-download.c:191 +msgid "no name given" +msgstr "" + +#: src/applications/fs/tools/gnunet-download.c:197 +#, fuzzy, c-format +msgid "Starting download `%s'\n" +msgstr "Startade samling \"%s\".\n" + +#: src/applications/fs/tools/gnunet-download.c:239 +msgid "Not enough arguments. You must specify a GNUnet file URI\n" +msgstr "" +"Inte tillräckligt med argument. Du mÃ¥ste ange en URI till en GNUnet-fil\n" + +#: src/applications/fs/tools/gnunet-download.c:257 +#, c-format +msgid "URI `%s' invalid for gnunet-download.\n" +msgstr "URI \"%s\" ogiltig för gnunet-download.\n" + +#: src/applications/fs/tools/gnunet-download.c:300 +#, fuzzy, c-format +msgid "No filename specified, using `%s' instead (for now).\n" +msgstr "Inget tabellnamn angivet, använder \"%s\".\n" + +#: src/applications/fs/tools/gnunet-download.c:342 +#, fuzzy, c-format +msgid "Could not access gnunet-directory file `%s'\n" +msgstr "Kunde inte tolka konfigurationsfil \"%s\".\n" + +#: src/applications/fs/tools/gnunet-download.c:363 +#, fuzzy, c-format +msgid "Downloading %d files from directory `%s'.\n" +msgstr "Ladda ner filer frÃ¥n GNUnet." + +#: src/applications/fs/tools/gnunet-download.c:366 +#, fuzzy, c-format +msgid "Did not find any files in directory `%s'\n" +msgstr "%d filer hittades i katalog.\n" + +#: src/applications/fs/tools/gnunet-download.c:404 +#, c-format +msgid "File stored as `%s'.\n" +msgstr "Fil lagrad som \"%s\".\n" + +#: src/applications/fs/uritrack/file_info.c:98 +msgid "Collecting file identifiers disabled.\n" +msgstr "" + +#: src/applications/fs/uritrack/file_info.c:377 +#, c-format +msgid "Deleted corrupt URI database in `%s'." +msgstr "" + +#: src/applications/fs/ecrs/upload.c:158 +#, c-format +msgid "`%s' is not a file.\n" +msgstr "\"%s\" är inte en fil.\n" + +#: src/applications/fs/ecrs/upload.c:166 +#, c-format +msgid "Cannot get size of file `%s'" +msgstr "Kan inte hämta storlek pÃ¥ fil \"%s\"" + +#: src/applications/fs/ecrs/upload.c:175 +#, fuzzy +msgid "Failed to connect to gnunetd." +msgstr "Misslyckades att ansluta till gnunetd.\n" + +#: src/applications/fs/ecrs/upload.c:187 +#, c-format +msgid "Cannot hash `%s'.\n" +msgstr "" + +#: src/applications/fs/ecrs/upload.c:215 +#, c-format +msgid "Initialization for indexing file `%s' failed.\n" +msgstr "Initiering av indexering av fil \"%s\" misslyckades.\n" + +#: src/applications/fs/ecrs/upload.c:223 +#, fuzzy, c-format +msgid "Indexing file `%s' failed. Suggestion: try to insert the file.\n" +msgstr "Indexering av fil \"%s\" misslyckades. Försöker att infoga fil...\n" + +#: src/applications/fs/ecrs/upload.c:237 +#, fuzzy, c-format +msgid "Cannot open file `%s': `%s'" +msgstr "Kan inte öppna konfigurationsfil \"%s\".\n" + +#: src/applications/fs/ecrs/upload.c:322 +#, fuzzy, c-format +msgid "Indexing data of file `%s' failed at position %llu.\n" +msgstr "Indexering av data misslyckades vid position %i.\n" + +#: src/applications/fs/ecrs/helper.c:91 +msgid "No keywords specified!\n" +msgstr "Inga nyckelord angivna!\n" + +#: src/applications/fs/ecrs/helper.c:99 +msgid "Number of double-quotes not balanced!\n" +msgstr "" + +#: src/applications/fs/ecrs/helper.c:398 +#, c-format +msgid "Renaming of file `%s' to `%s' failed: %s\n" +msgstr "Namnbyte pÃ¥ fil \"%s\" till \"%s\" misslyckades: %s\n" + +#: src/applications/fs/ecrs/helper.c:408 +#, c-format +msgid "Could not rename file `%s' to `%s': file exists\n" +msgstr "Kunde inte byta namn pÃ¥ fil \"%s\" till \"%s\": filen existerar\n" + +#: src/applications/fs/ecrs/parser.c:165 +#, c-format +msgid "" +"Unknown metadata type in metadata option `%s'. Using metadata type " +"`unknown' instead.\n" +msgstr "" + +#: src/applications/fs/ecrs/search.c:152 +msgid "CHK URI not allowed for search.\n" +msgstr "CHK URI tillÃ¥ts inte för sökning.\n" + +#: src/applications/fs/ecrs/search.c:207 +msgid "LOC URI not allowed for search.\n" +msgstr "LOC URI tillÃ¥ts inte för sökning.\n" + +#: src/applications/fs/ecrs/namespace.c:365 +#, c-format +msgid "File `%s' does not contain a pseudonym.\n" +msgstr "Filen \"%s\" innehÃ¥ller ingen pseudonym.\n" + +#: src/applications/fs/ecrs/namespace.c:376 +#, c-format +msgid "Format of pseudonym `%s' is invalid.\n" +msgstr "Formatet pÃ¥ pseudonym \"%s\" är ogiltig.\n" + +#: src/applications/fs/ecrs/namespace.c:535 +#: src/applications/fs/ecrs/namespace.c:547 +#: src/applications/fs/ecrs/namespace.c:559 +#, fuzzy, c-format +msgid "Format of file `%s' is invalid, trying to remove.\n" +msgstr "Formatet pÃ¥ filen \"%s\" är ogiltig.\n" + +#: src/applications/fs/ecrs/download.c:599 +msgid "" +"Decrypted content does not match key. This is either a bug or a maliciously " +"inserted file. Download aborted.\n" +msgstr "" +"Dekrypterat innehÃ¥ll stämmer inte med nyckeln. Det är antingen ett fel eller " +"en fil inlagd med onda avsikter. Hämtning avbruten.\n" + +#: src/applications/fs/ecrs/download.c:609 +msgid "IO error." +msgstr "" + +#: src/applications/fs/collection/collection.c:559 +#: src/applications/fs/collection/collection.c:562 +#, c-format +msgid "Revision %u" +msgstr "" + +#: src/applications/fs/fsui/upload.c:330 +msgid "Application aborted." +msgstr "" + +#: src/applications/fs/fsui/upload.c:344 +msgid "Failed to create temporary directory." +msgstr "" + +#: src/applications/fs/fsui/deserialize.c:927 +#, c-format +msgid "FSUI state file `%s' had syntax error at offset %u.\n" +msgstr "" + +#: src/applications/fs/fsui/unindex.c:114 +msgid "Unindexing failed (no reason given)" +msgstr "" + +#: src/applications/fs/gap/plan.c:944 +msgid "# gap requests total sent" +msgstr "" + +#: src/applications/fs/gap/plan.c:946 +msgid "# gap content total planned" +msgstr "" + +#: src/applications/fs/gap/plan.c:948 +msgid "# gap routes succeeded" +msgstr "" + +#: src/applications/fs/gap/plan.c:949 +msgid "# trust spent" +msgstr "" + +#: src/applications/fs/gap/fs.c:157 +msgid "Datastore full.\n" +msgstr "" + +#: src/applications/fs/gap/fs.c:831 +msgid "# gap requests total received" +msgstr "" + +#: src/applications/fs/gap/fs.c:833 +msgid "# gap requests dropped due to load" +msgstr "" + +#: src/applications/fs/gap/fs.c:835 +msgid "# gap content total received" +msgstr "" + +#: src/applications/fs/gap/fs.c:837 +msgid "# gap total trust awarded" +msgstr "" + +#: src/applications/fs/gap/fs.c:865 +#, fuzzy, c-format +msgid "" +"`%s' registering client handlers %d %d %d %d %d %d %d %d and P2P handlers %d " +"%d\n" +msgstr "\"%s\" registrerar klienthandtagen %d och %d\n" + +#: src/applications/fs/gap/fs.c:921 +msgid "enables (anonymous) file-sharing" +msgstr "aktiverar (anonym) fildelning" + +#: src/applications/fs/gap/ondemand.c:173 +#, c-format +msgid "" +"Because the file `%s' has been unavailable for 3 days it got removed from " +"your share. Please unindex files before deleting them as the index now " +"contains invalid references!\n" +msgstr "" + +#: src/applications/fs/gap/ondemand.c:451 +msgid "Indexed content changed (does not match its hash).\n" +msgstr "" + +#: src/applications/fs/gap/ondemand.c:569 +#, c-format +msgid "" +"Unindexed ODB block `%s' from offset %llu already missing from datastore.\n" +msgstr "" + +#: src/applications/fs/gap/pid_table.c:177 +msgid "# distinct interned peer IDs in pid table" +msgstr "" + +#: src/applications/fs/gap/pid_table.c:180 +msgid "# total RC of interned peer IDs in pid table" +msgstr "" + +#: src/applications/fs/gap/querymanager.c:708 +#, fuzzy +msgid "# gap client queries received" +msgstr "# klartext PONG-meddelanden mottagna" + +#: src/applications/fs/gap/querymanager.c:710 +msgid "# gap replies sent to clients" +msgstr "" + +#: src/applications/fs/gap/querymanager.c:712 +msgid "# gap client requests tracked" +msgstr "" + +#: src/applications/fs/gap/querymanager.c:714 +msgid "# gap client requests injected" +msgstr "" + +#: src/applications/fs/gap/querymanager.c:717 +msgid "# gap query bloomfilter resizing updates" +msgstr "" + +#: src/applications/fs/gap/migration.c:437 +msgid "# blocks migrated" +msgstr "" + +#: src/applications/fs/gap/migration.c:439 +msgid "# blocks injected for migration" +msgstr "" + +#: src/applications/fs/gap/migration.c:441 +msgid "# blocks fetched for migration" +msgstr "" + +#: src/applications/fs/gap/migration.c:443 +msgid "# on-demand fetches for migration" +msgstr "" + +#: src/applications/fs/gap/gap.c:694 +msgid "# gap queries dropped (table full)" +msgstr "" + +#: src/applications/fs/gap/gap.c:696 +msgid "# gap queries dropped (redundant)" +msgstr "" + +#: src/applications/fs/gap/gap.c:698 +msgid "# gap queries routed" +msgstr "" + +#: src/applications/fs/gap/gap.c:700 +msgid "# gap content found locally" +msgstr "" + +#: src/applications/fs/gap/gap.c:703 +msgid "# gap queries refreshed existing record" +msgstr "" + +#: src/applications/fs/gap/gap.c:704 +msgid "# trust earned" +msgstr "" + +#: src/applications/fs/gap/fs_dht.c:256 +msgid "# blocks pushed into DHT" +msgstr "" + +#: src/applications/fs/gap/anonymity.c:56 +msgid "Failed to get traffic stats.\n" +msgstr "" + +#: src/applications/testing/remote.c:68 +#, c-format +msgid "scp command is : %s \n" +msgstr "" + +#: src/applications/testing/remote.c:491 +#, fuzzy, c-format +msgid "Friend list of %s:%d\n" +msgstr "Fel vid %s:%d.\n" + +#: src/applications/testing/remote.c:513 +#, c-format +msgid "scp command for friend file copy is : %s \n" +msgstr "" + +#: src/applications/testing/remote.c:535 +#, c-format +msgid "connecting peer %s:%d to peer %s:%d\n" +msgstr "" + +#: src/applications/testing/remotetest.c:38 +msgid "Set up multiple gnunetd daemons across multiple hosts." +msgstr "" + +#: src/applications/testing/remotetest.c:43 +#, fuzzy +msgid "set number of daemons to start" +msgstr "antal meddelanden att använda per iteration" + +#: src/applications/testing/testing.c:268 +#: src/applications/testing/remotetopologies.c:367 +#, fuzzy, c-format +msgid "Waiting for peers to connect" +msgstr "Väntar pÃ¥ att motparter ska ansluta (%u iterationer kvar)...\n" + +#: src/applications/testing/remotetopologies.c:213 +#, c-format +msgid "Connecting nodes in 2d torus topology: %u rows %u columns\n" +msgstr "" + +#: src/applications/testing/remotetopologies.c:491 +#, fuzzy, c-format +msgid "Failed to establish connection with peers.\n" +msgstr "Misslyckades att starta samling.\n" + +#: src/applications/bootstrap_http/http.c:113 +#, fuzzy, c-format +msgid "Bootstrap data obtained from `%s' is invalid.\n" +msgstr "Formatet pÃ¥ pseudonym \"%s\" är ogiltig.\n" + +#: src/applications/bootstrap_http/http.c:126 +#: src/applications/bootstrap_http/http.c:277 +#: src/applications/bootstrap_http/http.c:294 +#: src/applications/bootstrap_http/http.c:333 +#: src/applications/bootstrap_http/http.c:352 +#: src/applications/bootstrap_http/http.c:365 +#: src/applications/bootstrap_http/http.c:375 +#: src/applications/bootstrap_http/http.c:385 src/transports/upnp/upnp.c:356 +#: src/transports/upnp/upnp.c:541 src/transports/http.c:1085 +#: src/transports/http.c:1209 src/transports/http.c:1377 +#: src/transports/http.c:1777 src/transports/http.c:1827 +#, fuzzy, c-format +msgid "%s failed at %s:%d: `%s'\n" +msgstr "\"%s\" misslyckades vid %s:%d med fel: \"%s\".\n" + +#: src/applications/bootstrap_http/http.c:185 +msgid "No hostlist URL specified in configuration, will not bootstrap.\n" +msgstr "" + +#: src/applications/bootstrap_http/http.c:226 +#, c-format +msgid "Bootstrapping using `%s'.\n" +msgstr "" + +#: src/applications/bootstrap_http/http.c:254 +#, fuzzy, c-format +msgid "Trying to download hostlist from `%s'\n" +msgstr "Misslyckades att läsa kompislista frÃ¥n \"%s\"\n" + +#: src/applications/bootstrap_http/http.c:391 +#, fuzzy, c-format +msgid "Downloaded %llu bytes from `%s'.\n" +msgstr "Ladda ner filer frÃ¥n GNUnet." + +#: src/applications/bootstrap_http/http.c:425 +msgid "# HELLOs downloaded via http" +msgstr "" + +#: src/applications/getoption/getoption.c:78 +#, c-format +msgid "`%s' registering client handler %d\n" +msgstr "\"%s\" registrerar klienthandtag %d\n" + +#: src/applications/getoption/getoption.c:88 +msgid "allows clients to determine gnunetd's configuration" +msgstr "tillÃ¥ter klienter att fastställa gnunetds konfiguration" + +#: src/applications/template/template.c:70 +#, c-format +msgid "`%s' registering client handler %d and %d\n" +msgstr "\"%s\" registrerar klienthandtagen %d och %d\n" + +#: src/applications/template/gnunet-template.c:42 +msgid "Template description." +msgstr "" + +#: src/applications/stats/clientapi.c:331 +msgid "Uptime (seconds)" +msgstr "Upptid (sekunder)" + +#: src/applications/stats/sqstats.c:151 +msgid "# Any-Blocks" +msgstr "" + +#: src/applications/stats/sqstats.c:152 +msgid "# DBlocks" +msgstr "" + +#: src/applications/stats/sqstats.c:153 +msgid "# SBlocks" +msgstr "" + +#: src/applications/stats/sqstats.c:154 +msgid "# KBlocks" +msgstr "" + +#: src/applications/stats/sqstats.c:155 +msgid "# NBlocks" +msgstr "" + +#: src/applications/stats/sqstats.c:156 +msgid "# KNBlocks" +msgstr "" + +#: src/applications/stats/sqstats.c:157 +msgid "# OnDemand-Blocks" +msgstr "" + +#: src/applications/stats/sqstats.c:158 +msgid "# Unknown-Blocks" +msgstr "" + +#: src/applications/stats/sqstats.c:159 +msgid "# expired" +msgstr "" + +#: src/applications/stats/sqstats.c:160 +msgid "# expire in 1h" +msgstr "" + +#: src/applications/stats/sqstats.c:161 +msgid "# expire in 24h" +msgstr "" + +#: src/applications/stats/sqstats.c:162 +msgid "# expire in 1 week" +msgstr "" + +#: src/applications/stats/sqstats.c:163 +msgid "# expire in 1 month" +msgstr "" + +#: src/applications/stats/sqstats.c:164 +msgid "# zero priority" +msgstr "" + +#: src/applications/stats/sqstats.c:165 +msgid "# priority one" +msgstr "" + +#: src/applications/stats/sqstats.c:166 +msgid "# priority larger than one" +msgstr "" + +#: src/applications/stats/sqstats.c:167 +msgid "# no anonymity" +msgstr "" + +#: src/applications/stats/sqstats.c:168 +msgid "# anonymity one" +msgstr "" + +#: src/applications/stats/sqstats.c:169 +msgid "# anonymity larger than one" +msgstr "" + +#: src/applications/stats/statistics.c:238 +#, no-c-format +msgid "% of allowed network load (up)" +msgstr "" + +#: src/applications/stats/statistics.c:240 +#, no-c-format +msgid "% of allowed network load (down)" +msgstr "" + +#: src/applications/stats/statistics.c:243 +#, no-c-format +msgid "% of allowed cpu load" +msgstr "" + +#: src/applications/stats/statistics.c:246 +#, no-c-format +msgid "% of allowed io load" +msgstr "" + +#: src/applications/stats/statistics.c:249 +msgid "# bytes of noise received" +msgstr "" + +#: src/applications/stats/statistics.c:251 +msgid "# plibc handles" +msgstr "" + +#: src/applications/stats/statistics.c:441 +#, fuzzy, c-format +msgid "`%s' registering client handlers %d %d %d and p2p handler %d\n" +msgstr "\"%s\" registrerar klienthandtagen %d och %d\n" + +#: src/applications/stats/statistics.c:463 +msgid "keeps statistics about gnunetd's operation" +msgstr "behÃ¥ller statistik om gnunetds operation" + +#: src/applications/stats/gnunet-stats.c:61 +#, c-format +msgid "Supported peer-to-peer messages:\n" +msgstr "" + +#: src/applications/stats/gnunet-stats.c:64 +#, c-format +msgid "Supported client-server messages:\n" +msgstr "" + +#: src/applications/stats/gnunet-stats.c:83 +#: src/applications/vpn/gnunet-vpn.c:59 +msgid "Print statistics about GNUnet operations." +msgstr "Skriv ut statistik om GNUnet-operationer." + +#: src/applications/stats/gnunet-stats.c:87 +msgid "prints supported protocol messages" +msgstr "skriver ut stödda protokollmeddelanden" + +#: src/applications/stats/gnunet-stats.c:136 +#, c-format +msgid "Error reading information from gnunetd.\n" +msgstr "" + +#: src/applications/vpn/gnunet-vpn.c:63 +msgid "Suppress display of asynchronous log messages" +msgstr "" + +#: src/applications/vpn/p2p.c:75 +msgid "VPN IP src not anonymous. drop..\n" +msgstr "" + +#: src/applications/vpn/p2p.c:83 +msgid "VPN IP not anonymous, drop.\n" +msgstr "" + +#: src/applications/vpn/p2p.c:92 +msgid "VPN Received, not anonymous, drop.\n" +msgstr "" + +#: src/applications/vpn/p2p.c:97 +#, c-format +msgid "VPN Received unknown IP version %d...\n" +msgstr "" + +#: src/applications/vpn/p2p.c:110 +#, c-format +msgid "<- GNUnet(%d) : %s\n" +msgstr "" + +#: src/applications/vpn/p2p.c:139 +msgid "Could not write the tunnelled IP to the OS... Did to setup a tunnel?\n" +msgstr "" + +#: src/applications/vpn/p2p.c:183 +msgid "Receive route request\n" +msgstr "" + +#: src/applications/vpn/p2p.c:193 +#, c-format +msgid "Prepare route announcement level %d\n" +msgstr "" + +#: src/applications/vpn/p2p.c:208 +#, c-format +msgid "Send route announcement %d with route announce\n" +msgstr "" + +#: src/applications/vpn/p2p.c:217 +#, c-format +msgid "Send outside table info %d\n" +msgstr "" + +#: src/applications/vpn/p2p.c:239 +msgid "Receive route announce.\n" +msgstr "" + +#: src/applications/vpn/p2p.c:247 +msgid "Going to try insert route into local table.\n" +msgstr "" + +#: src/applications/vpn/p2p.c:256 +#, c-format +msgid "Inserting with hops %d\n" +msgstr "" + +#: src/applications/vpn/p2p.c:273 +#, c-format +msgid "Request level %d from peer %d\n" +msgstr "" + +#: src/applications/vpn/p2p.c:300 +#, c-format +msgid "Receive table limit on peer reached %d\n" +msgstr "" + +#: src/applications/vpn/vpn.c:180 +#, c-format +msgid "Not storing route to myself from peer %d\n" +msgstr "" + +#: src/applications/vpn/vpn.c:194 +#, c-format +msgid "Duplicate route to node from peer %d, choosing minimum hops" +msgstr "" + +#: src/applications/vpn/vpn.c:230 +#, c-format +msgid "Inserting route from peer %d in route table at location %d\n" +msgstr "" + +#: src/applications/vpn/vpn.c:247 +#, c-format +msgid "RFC4193 Frame length %d is too big for GNUnet!\n" +msgstr "" + +#: src/applications/vpn/vpn.c:254 +#, c-format +msgid "RFC4193 Frame length %d too small\n" +msgstr "" + +#: src/applications/vpn/vpn.c:273 +#, c-format +msgid "RFC4193 Ethertype %x and IP version %x do not match!\n" +msgstr "" + +#: src/applications/vpn/vpn.c:289 +#, c-format +msgid "RFC4193 Going to try and make a tunnel in slot %d\n" +msgstr "" + +#: src/applications/vpn/vpn.c:295 +#, fuzzy, c-format +msgid "Cannot open tunnel device: %s" +msgstr "Kan inte öppna konfigurationsfil \"%s\".\n" + +#: src/applications/vpn/vpn.c:331 +#, c-format +msgid "RFC4193 Create skips gnu%d as we are already using it\n" +msgstr "" + +#: src/applications/vpn/vpn.c:346 +#, c-format +msgid "Cannot set tunnel name to %s because of %s\n" +msgstr "" + +#: src/applications/vpn/vpn.c:356 +#, c-format +msgid "Configured tunnel name to %s\n" +msgstr "" + +#: src/applications/vpn/vpn.c:398 +#, c-format +msgid "Cannot get socket flags for gnu%d because %s\n" +msgstr "" + +#: src/applications/vpn/vpn.c:408 +#, c-format +msgid "Cannot set socket flags for gnu%d because %s\n" +msgstr "" + +#: src/applications/vpn/vpn.c:418 +#, c-format +msgid "Cannot set MTU for gnu%d because %s\n" +msgstr "" + +#: src/applications/vpn/vpn.c:426 +#, c-format +msgid "Cannot get interface index for gnu%d because %s\n" +msgstr "" + +#: src/applications/vpn/vpn.c:440 +#, c-format +msgid "IPv6 ifaddr gnu%d - %x:%x:%x:%x:%x:%x:%x:%x/%d\n" +msgstr "" + +#: src/applications/vpn/vpn.c:455 +#, c-format +msgid "Cannot set interface IPv6 address for gnu%d because %s\n" +msgstr "" + +#: src/applications/vpn/vpn.c:471 +#, c-format +msgid "IPv6 route gnu%d - destination %x:%x:%x:%x:%x:%x:%x:%x/%d\n" +msgstr "" + +#: src/applications/vpn/vpn.c:485 +#, c-format +msgid "Cannot add route IPv6 address for gnu%s because %s\n" +msgstr "" + +#: src/applications/vpn/vpn.c:528 +msgid "" +"RFC4193 We have run out of memory and so I can't store a tunnel for this " +"peer.\n" +msgstr "" + +#: src/applications/vpn/vpn.c:579 +#, c-format +msgid "RFC4193 Thread running (frame %d tunnel %d f2f %d) ...\n" +msgstr "" + +#: src/applications/vpn/vpn.c:661 +#, c-format +msgid "VPN dropping connection %x\n" +msgstr "" + +#: src/applications/vpn/vpn.c:670 +#, c-format +msgid "VPN cannot drop connection %x\n" +msgstr "" + +#: src/applications/vpn/vpn.c:690 +msgid "RFC4193 Thread exiting\n" +msgstr "" + +#: src/applications/vpn/vpn.c:712 +msgid "realise alloc ram\n" +msgstr "" + +#: src/applications/vpn/vpn.c:735 +msgid "realise add routes\n" +msgstr "" + +#: src/applications/vpn/vpn.c:849 +msgid "realise copy table\n" +msgstr "" + +#: src/applications/vpn/vpn.c:898 +#, fuzzy, c-format +msgid "`%s' initialising RFC4913 module %d and %d\n" +msgstr "\"%s\" registrerar handtagen %d och %d\n" + +#: src/applications/vpn/vpn.c:903 +#, c-format +msgid "RFC4193 my First 4 hex digits of host id are %x\n" +msgstr "" + +#: src/applications/vpn/vpn.c:942 +#, fuzzy +msgid "enables IPv6 over GNUnet (incomplete)" +msgstr "aktiverar P2P-chatt (ej komplett)" + +#: src/applications/vpn/vpn.c:963 +msgid "RFC4193 Waiting for tun thread to end\n" +msgstr "" + +#: src/applications/vpn/vpn.c:978 +msgid "RFC4193 The tun thread has ended\n" +msgstr "" + +#: src/applications/vpn/vpn.c:996 +#, c-format +msgid "RFC4193 Closing tunnel %d fd %d\n" +msgstr "" + +#: src/server/core.c:119 src/server/core.c:318 +#, c-format +msgid "Configuration value `%s' under [MODULES] for `%s' is invalid!\n" +msgstr "" + +#: src/server/core.c:140 +#, c-format +msgid "Application module `%s' already initialized!\n" +msgstr "" + +#: src/server/core.c:194 +#, c-format +msgid "Failed to load plugin `%s' at %s:%d. Unloading plugin.\n" +msgstr "" + +#: src/server/core.c:244 +#, c-format +msgid "Could not shutdown `%s': application not loaded\n" +msgstr "" + +#: src/server/core.c:255 +#, c-format +msgid "Could not shutdown application `%s': not initialized\n" +msgstr "" + +#: src/server/core.c:265 +#, c-format +msgid "Could not find '%s%s' method in library `%s'.\n" +msgstr "" + +#: src/server/core.c:422 +#, c-format +msgid "Could not release %p: service not loaded\n" +msgstr "" + +#: src/server/core.c:531 +#, c-format +msgid "Could not properly shutdown application `%s'.\n" +msgstr "" + +#: src/server/core.c:676 +#, c-format +msgid "Could not properly unload service `%s'!\n" +msgstr "" + +#: src/server/gnunet-update.c:146 +#, c-format +msgid "Updating data for module `%s'\n" +msgstr "Uppdaterar data för modul \"%s\"\n" + +#: src/server/gnunet-update.c:151 +#, c-format +msgid "Failed to update data for module `%s'\n" +msgstr "" + +#: src/server/gnunet-update.c:225 src/server/gnunetd.c:124 +#, fuzzy +msgid "Core initialization failed.\n" +msgstr " Anslutning misslyckades\n" + +#: src/server/gnunet-update.c:270 +msgid "Updates GNUnet datastructures after version change." +msgstr "Uppdaterar GNUnets datastruktur efter versionsändring." + +#: src/server/gnunet-update.c:274 src/server/gnunet-transport-check.c:376 +msgid "run as user LOGIN" +msgstr "kör som användare LOGIN" + +#: src/server/gnunet-update.c:278 +msgid "run in client mode (for getting client configuration values)" +msgstr "kör i klientläge (för att hämta värden frÃ¥n klientkonfiguration)" + +#: src/server/version.c:125 +msgid "" +"Failed to determine filename used to store GNUnet version information!\n" +msgstr "" + +#: src/server/gnunetd.c:85 +#, c-format +msgid "`%s' startup complete.\n" +msgstr "\"%s\" uppstart klar.\n" + +#: src/server/gnunetd.c:89 +#, c-format +msgid "`%s' is shutting down.\n" +msgstr "" + +#: src/server/gnunetd.c:179 +msgid "" +"run in debug mode; gnunetd will not daemonize and error messages will be " +"written to stderr instead of a logfile" +msgstr "" + +#: src/server/gnunetd.c:183 +msgid "Starts the gnunetd daemon." +msgstr "Startar gnunetd-demonen." + +#: src/server/gnunetd.c:186 +msgid "disable padding with random data (experimental)" +msgstr "" + +#: src/server/gnunetd.c:190 +msgid "print all log messages to the console (only works together with -d)" +msgstr "" + +#: src/server/gnunetd.c:194 +#, fuzzy +msgid "specify username as which gnunetd should run" +msgstr "ange värd pÃ¥ vilken gnunetd körs" + +#: src/server/gnunetd.c:275 +#, c-format +msgid "Configuration or GNUnet version changed. You need to run `%s'!\n" +msgstr "" +"Konfiguration eller version av GNUnet ändrad. Du behöver köra \"%s\"!\n" + +#: src/server/tcpserver.c:121 +#, c-format +msgid "The `%s' request received from client is malformed.\n" +msgstr "" + +#: src/server/tcpserver.c:409 +#, fuzzy, c-format +msgid "`%s' failed for port %d. Is gnunetd already running?\n" +msgstr "\"%s\" misslyckades för port %d: %s. Körs verkligen gnunetd?\n" + +#: src/server/tcpserver.c:487 src/server/tcpserver.c:512 +#, c-format +msgid "" +"Malformed network specification in the configuration in section `%s' for " +"entry `%s': %s\n" +msgstr "" + +#: src/server/tcpserver.c:572 +#, c-format +msgid "Registering failed, message type %d already in use.\n" +msgstr "" + +#: src/server/startup.c:219 +#, fuzzy, c-format +msgid "Unable to obtain filesystem information for `%s': %u\n" +msgstr "Kunde inte spara konfigurationsfil \"%s\":" + +#: src/server/startup.c:237 +#, c-format +msgid "" +"Filesystem `%s' of partition `%s' is unknown. Please contact gnunet-" +"developers@gnu.org!" +msgstr "" + +#: src/server/startup.c:252 +#, c-format +msgid "" +"Limiting datastore size to %llu GB, because the `%s' filesystem does not " +"support larger files. Please consider storing the database on a NTFS " +"partition!\n" +msgstr "" + +#: src/server/startup.c:291 +#, c-format +msgid "Insufficient access permissions for `%s': %s\n" +msgstr "" + +#: src/server/gnunet-peer-info.c:55 +msgid "Print information about GNUnet peers." +msgstr "Skriv ut information om GNUnets motparter." + +#: src/server/gnunet-peer-info.c:59 +msgid "don't resolve host names" +msgstr "" + +#: src/server/gnunet-peer-info.c:62 +msgid "output only the identity strings" +msgstr "" + +#: src/server/gnunet-peer-info.c:65 +msgid "output our own identity only" +msgstr "" + +#: src/server/gnunet-peer-info.c:130 src/server/gnunet-peer-info.c:164 +#, c-format +msgid "Could not get address of peer `%s'.\n" +msgstr "" + +#: src/server/gnunet-peer-info.c:143 +#, fuzzy, c-format +msgid "`%s' message invalid (signature invalid).\n" +msgstr "\"%s\" misslyckades, svar ogiltigt!\n" + +#: src/server/gnunet-peer-info.c:168 +#, fuzzy, c-format +msgid "Peer `%s' with trust %8u\n" +msgstr "Motpart \"%s\" med pÃ¥litlighet %8u och adress \"%s\"\n" + +#: src/server/gnunet-peer-info.c:175 +#, c-format +msgid "Peer `%s' with trust %8u and address `%s'\n" +msgstr "Motpart \"%s\" med pÃ¥litlighet %8u och adress \"%s\"\n" + +#: src/server/connection.c:1313 +#, c-format +msgid "`%s' selected %d out of %d messages (MTU: %d).\n" +msgstr "\"%s\" valda %d av %d meddelanden (MTU: %d).\n" + +#: src/server/connection.c:1323 +#, c-format +msgid "Message details: %u: length %d, priority: %d\n" +msgstr "Detaljer om meddelande: %u: längd %d, prioritet: %d\n" + +#: src/server/connection.c:3129 +#, c-format +msgid "Message from `%s' discarded: invalid format.\n" +msgstr "Meddelande frÃ¥n \"%s\" kastades bort: ogiltigt format.\n" + +#: src/server/connection.c:3218 +#, c-format +msgid "Invalid sequence number %u <= %u, dropping message.\n" +msgstr "" + +#: src/server/connection.c:3240 +msgid "Message received more than one day old. Dropped.\n" +msgstr "" + +#: src/server/connection.c:3763 +msgid "# outgoing messages dropped" +msgstr "" + +#: src/server/connection.c:3766 +msgid "# bytes of outgoing messages dropped" +msgstr "" + +#: src/server/connection.c:3768 +msgid "# connections closed (HANGUP sent)" +msgstr "" + +#: src/server/connection.c:3772 +msgid "# connections closed (transport issue)" +msgstr "" + +#: src/server/connection.c:3775 +msgid "# bytes encrypted" +msgstr "# byte krypterade" + +#: src/server/connection.c:3779 +#, fuzzy +msgid "# bytes transmitted" +msgstr "# byte skickade av typen %d" + +#: src/server/connection.c:3783 +#, fuzzy +msgid "# bytes received" +msgstr "# byte mottogs via TCP" + +#: src/server/connection.c:3785 +msgid "# bytes decrypted" +msgstr "# byte dekrypterade" + +#: src/server/connection.c:3786 +msgid "# bytes noise sent" +msgstr "" + +#: src/server/connection.c:3789 +msgid "# total bytes per second send limit" +msgstr "" + +#: src/server/connection.c:3792 +msgid "# total bytes per second receive limit" +msgstr "" + +#: src/server/connection.c:3795 +#, fuzzy +msgid "# total number of messages in send buffers" +msgstr "antal meddelanden i ett meddelandeblock" + +#: src/server/connection.c:3798 +msgid "# total number of bytes we were allowed to send but did not" +msgstr "" + +#: src/server/connection.c:3801 +msgid "# total number of bytes we were allowed to sent" +msgstr "" + +#: src/server/connection.c:3804 +msgid "# total number of bytes we are currently allowed to send" +msgstr "" + +#: src/server/connection.c:3807 +msgid "# transports switched to stream transport" +msgstr "" + +#: src/server/connection.c:3810 +msgid "# average connection lifetime (in ms)" +msgstr "" + +#: src/server/connection.c:3813 +msgid "# conn. shutdown: other peer sent too much" +msgstr "" + +#: src/server/connection.c:3816 +msgid "# conn. shutdown: we lacked bandwidth" +msgstr "" + +#: src/server/connection.c:3819 +msgid "# conn. shutdown: other peer timed out" +msgstr "" + +#: src/server/connection.c:3822 +msgid "# conn. shutdown: timed out during connect" +msgstr "" + +#: src/server/connection.c:3825 +msgid "# conn. shutdown: other peer requested it" +msgstr "" + +#: src/server/handler.c:442 +#, fuzzy, c-format +msgid "Received corrupt message from peer `%s' in %s:%d.\n" +msgstr "Mottog skadat meddelande frÃ¥n motpart \"%s\"i %s:%d.\n" + +#: src/server/gnunet-transport-check.c:121 +#, c-format +msgid "`%s': Could not create hello.\n" +msgstr "" + +#: src/server/gnunet-transport-check.c:129 +#, c-format +msgid "`%s': Could not connect.\n" +msgstr "\"%s\": Kunde inte ansluta.\n" + +#: src/server/gnunet-transport-check.c:163 +#, c-format +msgid "`%s': Could not send.\n" +msgstr "\"%s\": Kunde inte skicka.\n" + +#: src/server/gnunet-transport-check.c:179 +#, c-format +msgid "`%s': Did not receive message within %llu ms.\n" +msgstr "\"%s\": Mottog inte meddelande inom %llu ms.\n" + +#: src/server/gnunet-transport-check.c:192 +#, c-format +msgid "`%s': Could not disconnect.\n" +msgstr "\"%s\": Kunde inte koppla ned.\n" + +#: src/server/gnunet-transport-check.c:200 +#, fuzzy, c-format +msgid "" +"`%s' transport OK. It took %ums to transmit %llu messages of %llu bytes " +"each.\n" +msgstr "" +"\"%s\" transport OK. Det tog %ums att överföra %d meddelanden pÃ¥ %d byte " +"styck.\n" + +#: src/server/gnunet-transport-check.c:231 +#, fuzzy, c-format +msgid " Transport %d is not being tested\n" +msgstr " Transport %d ej tillgänglig\n" + +#: src/server/gnunet-transport-check.c:261 +#, c-format +msgid "" +"\n" +"Contacting `%s'." +msgstr "" +"\n" +"Kontaktar \"%s\"." + +#: src/server/gnunet-transport-check.c:286 +#, c-format +msgid " Connection failed\n" +msgstr " Anslutning misslyckades\n" + +#: src/server/gnunet-transport-check.c:292 +#, c-format +msgid " Connection failed (bug?)\n" +msgstr " Anslutning misslyckades (fel?)\n" + +#: src/server/gnunet-transport-check.c:330 +#, c-format +msgid "Timeout after %llums.\n" +msgstr "" + +#: src/server/gnunet-transport-check.c:332 +#, fuzzy, c-format +msgid "OK!\n" +msgstr "OK" + +#: src/server/gnunet-transport-check.c:357 +msgid "Tool to test if GNUnet transport services are operational." +msgstr "" + +#: src/server/gnunet-transport-check.c:361 +msgid "ping peers from HOSTLISTURL that match transports" +msgstr "" + +#: src/server/gnunet-transport-check.c:364 +msgid "send COUNT messages" +msgstr "skicka ANTAL meddelanden" + +#: src/server/gnunet-transport-check.c:367 +msgid "send messages with SIZE bytes payload" +msgstr "skicka meddelanden med STORLEK bytes nyttolast" + +#: src/server/gnunet-transport-check.c:370 +msgid "specifies which TRANSPORT should be tested" +msgstr "anger vilken TRANSPORT som ska testas" + +#: src/server/gnunet-transport-check.c:373 +msgid "specifies after how many MS to time-out" +msgstr "anger timeout efter antal MS" + +#: src/server/gnunet-transport-check.c:381 +msgid "repeat each test X times" +msgstr "" + +#: src/server/gnunet-transport-check.c:449 +#, c-format +msgid "Testing transport(s) %s\n" +msgstr "Testar transport(er) %s\n" + +#: src/server/gnunet-transport-check.c:451 +#, c-format +msgid "Available transport(s): %s\n" +msgstr "Tillgängliga transport(er): %s\n" + +#: src/server/gnunet-transport-check.c:501 +#, c-format +msgid "" +"\n" +"%d out of %d peers contacted successfully (%d times transport unavailable).\n" +msgstr "" + +#: src/transports/common.c:370 +#, c-format +msgid "Port is 0, will only send using %s.\n" +msgstr "" + +#: src/transports/smtp.c:367 src/transports/udp.c:107 src/transports/tcp.c:271 +#: src/transports/tcp.c:291 +#, fuzzy, c-format +msgid "Received malformed message via %s. Ignored.\n" +msgstr "Mottog ogiltigt \"%s\" meddelande frÃ¥n \"%s\".\n" + +# capped är inte ett bra ord IMHO +#: src/transports/smtp.c:459 +#, fuzzy +msgid "SMTP filter string to invalid, lacks ': '\n" +msgstr "SMTP-filtersträng för lÃ¥ng, kapad till \"%s\"\n" + +# capped är inte ett bra ord IMHO +#: src/transports/smtp.c:469 +#, c-format +msgid "SMTP filter string to long, capped to `%s'\n" +msgstr "SMTP-filtersträng för lÃ¥ng, kapad till \"%s\"\n" + +#: src/transports/smtp.c:564 src/transports/smtp.c:575 +#: src/transports/smtp.c:589 src/transports/smtp.c:609 +#: src/transports/smtp.c:634 src/transports/smtp.c:643 +#: src/transports/smtp.c:657 src/transports/smtp.c:669 +#, fuzzy, c-format +msgid "SMTP: `%s' failed: %s.\n" +msgstr "\"%s\" %s misslyckades: %s\n" + +#: src/transports/smtp.c:814 +msgid "No email-address specified, can not start SMTP transport.\n" +msgstr "" + +#: src/transports/smtp.c:831 +#, fuzzy +msgid "# bytes received via SMTP" +msgstr "# byte mottogs via TCP" + +#: src/transports/smtp.c:832 +#, fuzzy +msgid "# bytes sent via SMTP" +msgstr "# byte skickades via TCP" + +#: src/transports/smtp.c:834 +#, fuzzy +msgid "# bytes dropped by SMTP (outgoing)" +msgstr "# byte kastade via TCP (utgÃ¥ende)" + +#: src/transports/upnp/upnp.c:431 +#, fuzzy, c-format +msgid "%s failed for url `%s' and post-data `%s' at %s:%d: `%s'\n" +msgstr "\"%s\" misslyckades för fil \"%s\" vid %s:%d med fel: %s\n" + +#: src/transports/upnp/upnp.c:476 +#, c-format +msgid "upnp: NAT Returned IP: %s\n" +msgstr "" + +#: src/transports/http.c:2019 src/transports/tcp.c:811 +#, c-format +msgid "" +"The UPnP service could not be loaded. To disable UPnP, set the configuration " +"option \"UPNP\" in section \"%s\" to \"NO\"\n" +msgstr "" + +#: src/transports/http.c:2028 +msgid "# bytes received via HTTP" +msgstr "# byte mottagna via HTTP" + +#: src/transports/http.c:2029 +msgid "# bytes sent via HTTP" +msgstr "# byte skickade via HTTP" + +#: src/transports/http.c:2031 +msgid "# bytes dropped by HTTP (outgoing)" +msgstr "# byte kastade via HTTP (utgÃ¥ende)" + +#: src/transports/http.c:2032 +msgid "# HTTP GET issued" +msgstr "" + +#: src/transports/http.c:2034 +msgid "# HTTP GET received" +msgstr "" + +#: src/transports/http.c:2035 +msgid "# HTTP PUT issued" +msgstr "" + +#: src/transports/http.c:2037 +msgid "# HTTP PUT received" +msgstr "" + +#: src/transports/http.c:2039 +msgid "# HTTP select calls" +msgstr "" + +#: src/transports/http.c:2041 +msgid "# HTTP send calls" +msgstr "" + +#: src/transports/http.c:2044 +msgid "# HTTP curl send callbacks" +msgstr "" + +#: src/transports/http.c:2046 +msgid "# HTTP curl receive callbacks" +msgstr "" + +#: src/transports/http.c:2048 +msgid "# HTTP mhd access callbacks" +msgstr "" + +#: src/transports/http.c:2050 +msgid "# HTTP mhd read callbacks" +msgstr "" + +#: src/transports/http.c:2052 +msgid "# HTTP mhd close callbacks" +msgstr "" + +#: src/transports/http.c:2054 +#, fuzzy +msgid "# HTTP connect calls" +msgstr "# av anslutna parter" + +#: src/transports/ip.c:70 src/transports/ip.c:365 +#, c-format +msgid "Failed to obtain my (external) %s address!\n" +msgstr "" + +#: src/transports/udp.c:472 src/transports/tcp.c:728 +#, fuzzy, c-format +msgid "Failed to bind to %s port %d.\n" +msgstr "Misslyckades att binda till UDP-port %d.\n" + +#: src/transports/udp.c:538 +#, c-format +msgid "MTU %llu for `%s' is probably too low!\n" +msgstr "" + +#: src/transports/udp.c:562 +msgid "# bytes received via UDP" +msgstr "# byte mottagna via UDP" + +#: src/transports/udp.c:563 +msgid "# bytes sent via UDP" +msgstr "# byte skickade via UDP" + +#: src/transports/udp.c:565 +msgid "# bytes dropped by UDP (outgoing)" +msgstr "# byte kastade via UDP (utgÃ¥ende)" + +#: src/transports/udp.c:567 +msgid "# UDP connections (right now)" +msgstr "" + +#: src/transports/tcp.c:821 +msgid "# bytes received via TCP" +msgstr "# byte mottogs via TCP" + +#: src/transports/tcp.c:822 +msgid "# bytes sent via TCP" +msgstr "# byte skickades via TCP" + +#: src/transports/tcp.c:824 +msgid "# bytes dropped by TCP (outgoing)" +msgstr "# byte kastade via TCP (utgÃ¥ende)" + +#: src/include/gnunet_util_getopt.h:154 +msgid "print this help" +msgstr "skriv ut denna hjälp" + +#: src/include/gnunet_util_getopt.h:163 +msgid "print the version number" +msgstr "skriv ut versionsnummer" + +#: src/include/gnunet_util_getopt.h:169 +msgid "configure logging to use LOGLEVEL" +msgstr "" + +#: src/include/gnunet_util_getopt.h:175 +msgid "be verbose" +msgstr "var informativ" + +#: src/include/gnunet_util_getopt.h:181 +msgid "use configuration file FILENAME" +msgstr "använd konfigurationsfil FILNAMN" + +#: src/include/gnunet_util_getopt.h:187 +msgid "specify host on which gnunetd is running" +msgstr "ange värd pÃ¥ vilken gnunetd körs" + +#: src/include/gnunet_util_error.h:219 src/include/gnunet_util_error.h:224 +#: src/include/gnunet_util_error.h:230 src/include/gnunet_util_error.h:232 +#, c-format +msgid "Internal error: assertion failed at %s:%d.\n" +msgstr "" + +#: src/include/gnunet_util_error.h:242 +#, c-format +msgid "" +"External protocol violation: assertion failed at %s:%d (no need to panic, we " +"can handle this).\n" +msgstr "" + +#: src/include/gnunet_util_error.h:270 src/include/gnunet_util_error.h:277 +#, c-format +msgid "`%s' failed on file `%s' at %s:%d with error: %s\n" +msgstr "\"%s\" misslyckades för fil \"%s\" vid %s:%d med fel: %s\n" + +#: contrib/config-daemon.scm:39 contrib/config-client.scm:40 +#, fuzzy +msgid "No help available." +msgstr "\"%s\" är inte tillgänglig." + +#: contrib/config-daemon.scm:42 +msgid "" +"You can use 'make check' in src/transports/upnp/ to find out if your NAT " +"supports UPnP. You should disable this option if you are sure that you are " +"not behind a NAT. If your NAT box does not support UPnP, having this on " +"will not do much harm (only cost a small amount of resources)." +msgstr "" + +#: contrib/config-daemon.scm:54 contrib/config-client.scm:53 +msgid "Prompt for development and/or incomplete code" +msgstr "" + +#: contrib/config-daemon.scm:56 contrib/config-client.scm:55 +msgid "" +"If EXPERIMENTAL is set to NO, options for experimental code are not shown. " +"If in doubt, use NO.\n" +"\n" +"Some options apply to experimental code that maybe in a state of development " +"where the functionality, stability, or the level of testing is not yet high " +"enough for general use. These features are said to be of \"alpha\" " +"quality. If a feature is currently in alpha, uninformed use is discouraged " +"(since the developers then do not fancy \"Why doesn't this work?\" type " +"messages).\n" +"\n" +"However, active testing and qualified feedback of these features is always " +"welcome. Users should just be aware that alpha features may not meet the " +"normal level of reliability or it may fail to work in some special cases. " +"Bug reports are usually welcomed by the developers, but please read the " +"documents and and use for how to report problems." +msgstr "" + +#: contrib/config-daemon.scm:71 contrib/config-client.scm:70 +msgid "Show options for advanced users" +msgstr "" + +#: contrib/config-daemon.scm:73 contrib/config-client.scm:72 +msgid "" +"These are options that maybe difficult to understand for the beginner. These " +"options typically refer to features that allow tweaking of the " +"installation. If in a hurry, say NO." +msgstr "" + +#: contrib/config-daemon.scm:84 contrib/config-client.scm:83 +#, fuzzy +msgid "Show rarely used options" +msgstr "Visa alla alternativ" + +#: contrib/config-daemon.scm:86 contrib/config-client.scm:85 +msgid "" +"These are options that hardly anyone actually needs. If you plan on doing " +"development on GNUnet, you may want to look into these. If in doubt or in a " +"hurry, say NO." +msgstr "" + +#: contrib/config-daemon.scm:97 contrib/config-client.scm:96 +#, fuzzy +msgid "Meta-configuration" +msgstr "GNUnet-konfiguration" + +#: contrib/config-daemon.scm:98 contrib/config-client.scm:97 +msgid "Which level of configuration should be available" +msgstr "" + +#: contrib/config-daemon.scm:115 +#, fuzzy +msgid "Full pathname of GNUnet HOME directory" +msgstr "Filformatsfel (inte en GNUnet-katalog?)\n" + +#: contrib/config-daemon.scm:117 +msgid "" +"This gives the root-directory of the GNUnet installation. Make sure there is " +"some space left in that directory. :-) Users inserting or indexing files " +"will be able to store data in this directory up to the (global) quota " +"specified below. Having a few gigabytes of free space is recommended." +msgstr "" + +#: contrib/config-daemon.scm:130 +msgid "Full pathname of GNUnet directory for file-sharing data" +msgstr "" + +#: contrib/config-daemon.scm:142 +msgid "Full pathname to the directory with the key-value database" +msgstr "" + +#: contrib/config-daemon.scm:143 +msgid "Note that the kvstore is currently not used." +msgstr "" + +#: contrib/config-daemon.scm:154 +msgid "Full pathname of GNUnet directory for indexed files symbolic links" +msgstr "" + +#: contrib/config-daemon.scm:166 +msgid "How many minutes should peer advertisements last?" +msgstr "" + +#: contrib/config-daemon.scm:168 +msgid "" +"How many minutes is the current IP valid? (GNUnet will sign HELLO messages " +"with this expiration timeline. If you are on dialup, 60 (for 1 hour) is " +"suggested. If you have a static IP address, you may want to set this to a " +"large value (say 14400). The default is 1440 (1 day). If your IP changes " +"periodically, you will want to choose an expiry period smaller than the " +"frequency with which your IP changes." +msgstr "" + +#: contrib/config-daemon.scm:179 +msgid "Where can GNUnet find an initial list of peers?" +msgstr "" + +#: contrib/config-daemon.scm:181 +msgid "" +"GNUnet can automatically update the hostlist from the web. While GNUnet " +"internally communicates which hosts are online, it is typically a good idea " +"to get a fresh hostlist whenever gnunetd starts from the WEB. By setting " +"this option, you can specify from which server gnunetd should try to " +"download the hostlist. The default should be fine for now.\n" +"\t\t\n" +"The general format is a list of space-separated URLs. Each URL must have " +"the format http://HOSTNAME/FILENAME\n" +"\t\t\n" +"If you want to setup an alternate hostlist server, you must run a permanent " +"node and \"cat data/hosts/* > hostlist\" every few minutes to keep the list " +"up-to-date.\n" +"\t\t\n" +"If you do not specify a HOSTLISTURL, you must copy valid hostkeys to data/" +"hosts manually." +msgstr "" + +#: contrib/config-daemon.scm:198 +msgid "HTTP Proxy Server" +msgstr "" + +#: contrib/config-daemon.scm:200 +msgid "" +"If you have to use a proxy for outbound HTTP connections, specify the proxy " +"configuration here. Default is no proxy." +msgstr "" + +#: contrib/config-daemon.scm:212 +msgid "" +"Name of the directory where gnunetd should store contact information about " +"peers" +msgstr "" + +#: contrib/config-daemon.scm:214 +msgid "" +"Unless you want to share the directory directly using a webserver, the " +"default is most likely just fine." +msgstr "" + +#: contrib/config-daemon.scm:240 contrib/config-client.scm:140 +msgid "How long should logs be kept?" +msgstr "" + +#: contrib/config-daemon.scm:242 contrib/config-client.scm:142 +msgid "" +"How long should logs be kept? If you specify a value greater than zero, a " +"log is created each day with the date appended to its filename. These logs " +"are deleted after $KEEPLOG days.\tTo keep logs forever, set this value to 0." +msgstr "" + +#: contrib/config-daemon.scm:253 +msgid "" +"What maximum number of open file descriptors should be requested from the OS?" +msgstr "" + +#: contrib/config-daemon.scm:255 +msgid "" +"The default of 1024 should be fine for most systems. If your system can " +"support more, increasing the number might help support additional clients on " +"machines with plenty of bandwidth. For embedded systems, a smaller number " +"might be acceptable. A value of 0 will leave the descriptor limit " +"untouched. This option is mostly for OS X systems where the default is too " +"low. Note that if gnunetd cannot obtain the desired number of file " +"descriptors from the operating system, it will print a warning and try to " +"run with what it is given." +msgstr "" + +#: contrib/config-daemon.scm:266 +msgid "Where should gnunetd write the logs?" +msgstr "" + +#: contrib/config-daemon.scm:278 +msgid "Enable for extra-verbose logging." +msgstr "" + +#: contrib/config-daemon.scm:290 contrib/config-client.scm:165 +msgid "Logging" +msgstr "" + +#: contrib/config-daemon.scm:291 contrib/config-client.scm:166 +msgid "Specify which system messages should be logged how" +msgstr "" + +#: contrib/config-daemon.scm:296 contrib/config-client.scm:170 +msgid "Logging of events for users" +msgstr "" + +#: contrib/config-daemon.scm:297 contrib/config-client.scm:171 +msgid "Logging of events for the system administrator" +msgstr "" + +#: contrib/config-daemon.scm:309 +msgid "Where should gnunetd write the PID?" +msgstr "" + +#: contrib/config-daemon.scm:310 +msgid "" +"The default is no longer /var/run/gnunetd.pid since we could not delete the " +"file on shutdown at that location." +msgstr "" + +#: contrib/config-daemon.scm:322 +msgid "As which user should gnunetd run?" +msgstr "" + +#: contrib/config-daemon.scm:324 +msgid "" +"Empty means \"current user\". On computer startup, it is root/SYSTEM. Under " +"Windows, this setting affects the creation of a new system service only." +msgstr "" + +#: contrib/config-daemon.scm:337 +msgid "Should gnunetd be automatically started when the system boots?" +msgstr "" + +#: contrib/config-daemon.scm:338 +msgid "" +"Set to YES if gnunetd should be automatically started on boot. If this " +"option is set, gnunet-setup will install a script to start the daemon upon " +"completion. This option may not work on all systems." +msgstr "" + +#: contrib/config-daemon.scm:350 +msgid "Which transport mechanisms should GNUnet use?" +msgstr "" + +#: contrib/config-daemon.scm:352 +msgid "" +"Use a space-separated list of modules, e.g. \"udp smtp tcp\". The " +"available transports are udp, tcp, http, smtp and nat.\n" +"\t\t\n" +"Loading the 'nat' and 'tcp' modules is required for peers behind NAT boxes " +"that cannot directly be reached from the outside. Peers that are NOT behind " +"a NAT box and that want to *allow* peers that ARE behind a NAT box to " +"connect must ALSO load the 'nat' module. Note that the actual transfer will " +"always be via tcp initiated by the peer behind the NAT box. The nat " +"transport requires the use of tcp, http and/or smtp in addition to nat " +"itself." +msgstr "" + +#: contrib/config-daemon.scm:366 +msgid "Which applications should gnunetd support?" +msgstr "" + +#: contrib/config-daemon.scm:368 +msgid "" +"Whenever this option is changed, you MUST run gnunet-update. Currently, the " +"available applications are:\n" +"\n" +"advertising: advertises your peer to other peers. Without it, your peer will " +"not participate in informing peers about other peers. You should always " +"load this module.\n" +"\n" +"getoption: allows clients to query gnunetd about the values of various " +"configuration options. Many tools need this. You should always load this " +"module.\n" +"\n" +"stats: allows tools like gnunet-stats and gnunet-gtk to query gnunetd about " +"various statistics. This information is usually quite useful to diagnose " +"errors, hence it is recommended that you load this module.\n" +"\n" +"traffic: keeps track of how many messages were recently received and " +"transmitted. This information can then be used to establish how much cover " +"traffic is currently available. The amount of cover traffic becomes " +"important if you want to make anonymous requests with an anonymity level " +"that is greater than one. It is recommended that you load this module.\n" +"\n" +"fs: needed for anonymous file sharing. You should always load this module.\n" +"\n" +"hostlist: integrated hostlist HTTP server. Useful if you want to offer a " +"hostlist and running Apache would be overkill.\n" +"\n" +"chat: broadcast chat (demo-application, ALPHA quality).\tRequired for gnunet-" +"chat. Note that the current implementation of chat is not considered to be " +"secure.\n" +"\n" +"tbench: benchmark transport performance. Required for gnunet-tbench. Note " +"that tbench allows other users to abuse your resources.\n" +"\n" +"tracekit: topology visualization toolkit. Required for gnunet-tracekit. " +"Note that loading tracekit will make it slightly easier for an adversary to " +"compromise your anonymity." +msgstr "" + +#: contrib/config-daemon.scm:399 +msgid "Disable client-server connections" +msgstr "" + +#: contrib/config-daemon.scm:400 +msgid "" +"This option can be used to tell gnunetd not to open the client port. When " +"run like this, gnunetd will participate as a peer in the network but not " +"support any user interfaces. This may be useful for headless systems that " +"are never expected to have end-user interactions. Note that this will also " +"prevent you from running diagnostic tools like gnunet-stats!" +msgstr "" + +#: contrib/config-daemon.scm:412 +msgid "YES disables IPv6 support, NO enables IPv6 support" +msgstr "" + +#: contrib/config-daemon.scm:413 +msgid "" +"This option may be useful on peers where the kernel does not support IPv6. " +"You might also want to set this option if you do not have an IPv6 network " +"connection." +msgstr "" + +#: contrib/config-daemon.scm:425 +msgid "Disable peer discovery" +msgstr "" + +#: contrib/config-daemon.scm:426 +msgid "" +"The option 'PRIVATE-NETWORK' can be used to limit the connections of this " +"peer to peers of which the hostkey has been copied by hand to data/hosts; " +"if this option is given, GNUnet will not accept advertisements of peers that " +"the local node does not already know about. Note that in order for this " +"option to work, HOSTLISTURL should either not be set at all or be set to a " +"trusted peer that only advertises the private network. Also, the option does " +"NOT work at the moment if the NAT transport is loaded; for that, a couple of " +"lines above would need some minor editing :-)." +msgstr "" + +#: contrib/config-daemon.scm:437 +msgid "Disable advertising this peer to other peers" +msgstr "" + +#: contrib/config-daemon.scm:449 +msgid "Disable automatic establishment of connections" +msgstr "" + +#: contrib/config-daemon.scm:450 +msgid "" +"If this option is enabled, GNUnet will not automatically establish " +"connections to other peers, but instead wait for applications to " +"specifically request connections to other peers (or for other peers to " +"connect to us)." +msgstr "" + +#: contrib/config-daemon.scm:461 +msgid "Enable advertising of other peers by this peer" +msgstr "" + +#: contrib/config-daemon.scm:462 +msgid "" +"This option may be useful during testing, but turning it off is dangerous! " +"If in any doubt, set it to YES (which is the default)." +msgstr "" + +#: contrib/config-daemon.scm:473 +#, fuzzy +msgid "Port for communication with GNUnet user interfaces" +msgstr "Skriv ut information om GNUnets motparter." + +#: contrib/config-daemon.scm:474 +msgid "" +"Which is the client-server port that is used between gnunetd and the clients " +"(TCP only). You may firewall this port for non-local machines (but you do " +"not have to since GNUnet will perform access control and only allow " +"connections from machines that are listed under TRUSTED)." +msgstr "" + +#: contrib/config-daemon.scm:485 +msgid "Port for the integrated hostlist HTTP server" +msgstr "" + +#: contrib/config-daemon.scm:497 +msgid "IPv4 networks allowed to use gnunetd server" +msgstr "" + +#: contrib/config-daemon.scm:498 contrib/config-daemon.scm:510 +msgid "" +"This option specifies which hosts are trusted enough to connect as clients " +"(to the TCP port). This is useful if you run gnunetd on one host of your " +"network and want to allow all other hosts to use this node as their server. " +"By default, this is set to 'loopback only'. The format is IP/NETMASK where " +"the IP is specified in dotted-decimal and the netmask either in CIDR " +"notation (/16) or in dotted decimal (255.255.0.0). Several entries must be " +"separated by a semicolon, spaces are not allowed." +msgstr "" + +#: contrib/config-daemon.scm:509 +msgid "IPv6 networks allowed to use gnunetd server" +msgstr "" + +#: contrib/config-daemon.scm:522 +msgid "Limit connections to the specfied set of peers." +msgstr "" + +#: contrib/config-daemon.scm:523 +msgid "" +"If this option is not set, any peer is allowed to connect. If it is set, " +"only the specified peers are allowed. Specify the list of peer IDs (not IPs!)" +msgstr "" + +#: contrib/config-daemon.scm:534 +#, fuzzy +msgid "Run gnunetd as this group." +msgstr "Kör gnunet-update" + +#: contrib/config-daemon.scm:535 +msgid "" +"When started as root, gnunetd will change permissions to the given group." +msgstr "" + +#: contrib/config-daemon.scm:546 +msgid "Prevent the specfied set of peers from connecting." +msgstr "" + +#: contrib/config-daemon.scm:547 +msgid "" +"If this option is not set, any peer is allowed to connect. If the ID of a " +"peer is listed here, connections from that peer will be refused. Specify " +"the list of peer IDs (not IPs!)" +msgstr "" + +#: contrib/config-daemon.scm:558 +msgid "Topology Maintenance" +msgstr "" + +#: contrib/config-daemon.scm:559 +msgid "Rarely used settings for peer advertisements and connections" +msgstr "" + +#: contrib/config-daemon.scm:579 +#, fuzzy +msgid "General settings" +msgstr "Andra inställningar" + +#: contrib/config-daemon.scm:580 +msgid "Settings that change the behavior of GNUnet in general" +msgstr "" + +#: contrib/config-daemon.scm:607 +msgid "Modules" +msgstr "" + +#: contrib/config-daemon.scm:608 +msgid "Settings that select specific implementations for GNUnet modules" +msgstr "" + +#: contrib/config-daemon.scm:626 +msgid "Fundamentals" +msgstr "" + +#: contrib/config-daemon.scm:646 +msgid "Which database should be used?" +msgstr "" + +#: contrib/config-daemon.scm:648 +msgid "" +"Which database should be used? The options are \"sqstore_sqlite\", " +"\"sqstore_postgres\" and \"sqstore_mysql\". You must run gnunet-update " +"after changing this value!\n" +"\t\t\t\n" +"In order to use MySQL or Postgres, you must configure the respective " +"database, which is relatively simple. Read the file doc/README.mysql or doc/" +"README.postgres for how to setup the respective database." +msgstr "" + +#: contrib/config-daemon.scm:661 contrib/config-daemon.scm:674 +msgid "Which topology should be used?" +msgstr "" + +#: contrib/config-daemon.scm:662 +msgid "Which database should be used for the temporary datastore of the DHT?" +msgstr "" + +#: contrib/config-daemon.scm:676 +msgid "" +"Which topology should be used? The only option at the moment is " +"\"topology_default\"" +msgstr "" + +#: contrib/config-daemon.scm:690 +msgid "" +"The minimum number of connected friends before this peer is allowed to " +"connect to peers that are not listed as friends" +msgstr "" + +#: contrib/config-daemon.scm:691 +msgid "" +"Note that this option does not guarantee that the peer will be able to " +"connect to the specified number of friends. Also, if the peer had connected " +"to a sufficient number of friends and then established non-friend " +"connections, some of the friends may drop out of the network, temporarily " +"resulting in having fewer than the specified number of friends connected " +"while being connected to non-friends. However, it is guaranteed that the " +"peer itself will never choose to drop a friend's connection if this would " +"result in dropping below the specified number of friends (unless that number " +"is higher than the overall connection target)." +msgstr "" + +#: contrib/config-daemon.scm:702 +msgid "" +"If set to YES, the peer is only allowed to connect to other peers that are " +"explicitly specified as friends" +msgstr "" + +#: contrib/config-daemon.scm:703 +msgid "" +"Use YES only if you have (trustworthy) friends that use GNUnet and are " +"afraid of establishing (direct) connections to unknown peers" +msgstr "" + +#: contrib/config-daemon.scm:714 +msgid "List of friends for friend-to-friend topology" +msgstr "" + +#: contrib/config-daemon.scm:715 +msgid "" +"Specifies the name of a file which contains a list of GNUnet peer IDs that " +"are friends. If used with the friend-to-friend topology, this will ensure " +"that GNUnet only connects to these peers (via any available transport)." +msgstr "" + +#: contrib/config-daemon.scm:726 +msgid "Friend-to-Friend Topology Specification" +msgstr "" + +#: contrib/config-daemon.scm:727 +#, fuzzy +msgid "Settings for restricting connections to friends" +msgstr "\"%s\" misslyckades. Terminerar anslutning till klient.\n" + +#: contrib/config-daemon.scm:744 +msgid "Name of the MySQL database GNUnet should use" +msgstr "" + +#: contrib/config-daemon.scm:756 +msgid "Configuration file that specifies the MySQL username and password" +msgstr "" + +#: contrib/config-daemon.scm:768 +#, fuzzy +msgid "Configuration of the MySQL database" +msgstr "Konfigurationsfil \"%s\" skapad.\n" + +#: contrib/config-daemon.scm:787 +msgid "MB of diskspace GNUnet can use for anonymous file sharing" +msgstr "" + +#: contrib/config-daemon.scm:789 +msgid "" +"How much disk space (MB) is GNUnet allowed to use for anonymous file " +"sharing? This does not take indexed files into account, only the space " +"directly used by GNUnet is accounted for. GNUnet will gather content from " +"the network if the current space-consumption is below the number given here " +"(and if content migration is allowed below).\n" +"\n" +"Note that if you change the quota, you need to run gnunet-update afterwards." +msgstr "" + +#: contrib/config-daemon.scm:803 +msgid "Number of entries in the migration buffer" +msgstr "" + +#: contrib/config-daemon.scm:804 +msgid "" +"Each entry uses about 32k of memory. More entries can reduce disk IO and " +"CPU usage at the expense of having gnunetd use more memory. Very large " +"values may again increase CPU usage. A value of 0 will prevent your peer " +"from sending unsolicited responses." +msgstr "" + +#: contrib/config-daemon.scm:816 +msgid "Size of the routing table for anonymous routing." +msgstr "" + +#: contrib/config-daemon.scm:828 +msgid "Size of the routing table for DHT routing." +msgstr "" + +#: contrib/config-daemon.scm:841 +msgid "Allow migrating content to this peer." +msgstr "" + +#: contrib/config-daemon.scm:843 +msgid "" +"If you say yes here, GNUnet will migrate content to your server, and you " +"will not be able to control what data is stored on your machine. \n" +"\t\t\t\n" +"If you activate it, you can claim for *all* the non-indexed (-n to gnunet-" +"insert) content that you did not know what it was even if an adversary takes " +"control of your machine. If you do not activate it, it is obvious that you " +"have knowledge of all the content that is hosted on your machine and thus " +"can be considered liable for it." +msgstr "" + +#: contrib/config-daemon.scm:857 +msgid "" +"MB of diskspace GNUnet can use for caching DHT index data (the data will be " +"stored in /tmp)" +msgstr "" + +#: contrib/config-daemon.scm:858 +msgid "" +"DHT index data is inherently small and expires comparatively quickly. It is " +"deleted whenever gnunetd is shut down.\n" +"\n" +"The size of the DSTORE QUOTA is specified in MB." +msgstr "" + +#: contrib/config-daemon.scm:872 +#, fuzzy +msgid "Options for anonymous file sharing" +msgstr "aktiverar (anonym) fildelning" + +#: contrib/config-daemon.scm:891 +#, fuzzy +msgid "Applications" +msgstr "_Alternativ" + +#: contrib/config-daemon.scm:907 +msgid "Is this machine unreachable behind a NAT?" +msgstr "" + +#: contrib/config-daemon.scm:908 +msgid "" +"Set to YES if this machine is behind a NAT that limits connections from the " +"outside to the GNUnet port and that cannot be traversed using UPnP. Note " +"that if you have configured your NAT box to allow direct connections from " +"other machines to the GNUnet ports or if GNUnet can open ports using UPnP, " +"you should set the option to NO. Set this only to YES if other peers cannot " +"contact you directly. You can use 'make check' in src/transports/upnp/ to " +"find out if your NAT supports UPnP. You can also use gnunet-transport-check " +"with the '-p' option in order to determine which setting results in more " +"connections. Use YES only if you get no connections otherwise. Set to AUTO " +"to use YES if the local IP is belongs to a private IP network and NO " +"otherwise." +msgstr "" + +#: contrib/config-daemon.scm:919 +msgid "Which port should be used by the TCP IPv4 transport?" +msgstr "" + +#: contrib/config-daemon.scm:931 contrib/config-daemon.scm:1024 +#: contrib/config-daemon.scm:1174 +msgid "Should we try to determine our external IP using UPnP?" +msgstr "" + +#: contrib/config-daemon.scm:943 +msgid "Which IP(v4)s are not allowed to connect?" +msgstr "" + +#: contrib/config-daemon.scm:955 +msgid "" +"Which IP(v4)s are allowed to connect? Leave empty to use the IP of your " +"primary network interface." +msgstr "" + +#: contrib/config-daemon.scm:967 contrib/config-daemon.scm:1222 +msgid "Which IPv6s are not allowed to connect?" +msgstr "" + +#: contrib/config-daemon.scm:979 contrib/config-daemon.scm:1234 +msgid "" +"Which IPv6s are allowed to connect? Leave empty to allow any IP to connect." +msgstr "" + +#: contrib/config-daemon.scm:992 +msgid "TCP transport" +msgstr "" + +#: contrib/config-daemon.scm:1012 +msgid "Which port should be used by the HTTP transport?" +msgstr "" + +#: contrib/config-daemon.scm:1036 +msgid "Which is the external port of the HTTP transport?" +msgstr "" + +#: contrib/config-daemon.scm:1037 +msgid "" +"Use this option if your firewall maps, say, port 80 to your real HTTP port. " +"This can be useful in making the HTTP messages appear even more legit " +"(without needing to run gnunetd as root due to the use of a privileged port)." +msgstr "" + +#: contrib/config-daemon.scm:1048 +msgid "HTTP transport" +msgstr "" + +#: contrib/config-daemon.scm:1067 +msgid "What is the maximum transfer unit for SMTP?" +msgstr "" + +#: contrib/config-daemon.scm:1079 +msgid "" +"What is the maximum number of e-mails that gnunetd would be allowed to send " +"per hour?" +msgstr "" + +#: contrib/config-daemon.scm:1080 +msgid "Use 0 for unlimited" +msgstr "" + +#: contrib/config-daemon.scm:1091 +msgid "Which e-mail address should be used to send e-mail to this peer?" +msgstr "" + +#: contrib/config-daemon.scm:1092 +msgid "" +"You must make sure that e-mail received at this address is forwarded to the " +"PIPE which is read by gnunetd. Use the FILTER option to filter e-mail with " +"procmail and the PIPE option to set the name of the pipe." +msgstr "" + +#: contrib/config-daemon.scm:1103 +msgid "" +"Which header line should other peers include in e-mails to enable filtering?" +msgstr "" + +#: contrib/config-daemon.scm:1104 +msgid "" +"You can specify a header line here which can then be used by procmail to " +"filter GNUnet e-mail from your inbox and forward it to gnunetd." +msgstr "" + +#: contrib/config-daemon.scm:1115 +msgid "What is the filename of the pipe where gnunetd can read its e-mail?" +msgstr "" + +#: contrib/config-daemon.scm:1116 +msgid "Have a look at contrib/dot-procmailrc for an example .procmailrc file." +msgstr "" + +#: contrib/config-daemon.scm:1127 +msgid "What is the name and port of the server for outgoing e-mail?" +msgstr "" + +#: contrib/config-daemon.scm:1128 +msgid "The basic format is HOSTNAME:PORT." +msgstr "" + +#: contrib/config-daemon.scm:1139 +msgid "SMTP transport" +msgstr "" + +#: contrib/config-daemon.scm:1162 +msgid "Which port should be used by the UDP IPv4 transport?" +msgstr "" + +#: contrib/config-daemon.scm:1186 +msgid "What is the maximum transfer unit for UDP?" +msgstr "" + +#: contrib/config-daemon.scm:1198 +msgid "Which IPs are not allowed to connect?" +msgstr "" + +#: contrib/config-daemon.scm:1210 +msgid "" +"Which IPs are allowed to connect? Leave empty to allow connections from any " +"IP." +msgstr "" + +#: contrib/config-daemon.scm:1246 +msgid "UDP transport" +msgstr "" + +#: contrib/config-daemon.scm:1268 +#, fuzzy +msgid "Network interface" +msgstr "Nätverksgränssnitt:" + +#: contrib/config-daemon.scm:1280 +msgid "External IP address (leave empty to try auto-detection)" +msgstr "" + +#: contrib/config-daemon.scm:1292 +msgid "External IPv6 address (leave empty to try auto-detection)" +msgstr "" + +#: contrib/config-daemon.scm:1304 +msgid "Transports" +msgstr "" + +#: contrib/config-daemon.scm:1326 +msgid "What is the maximum number of bytes per second that we may receive?" +msgstr "" + +#: contrib/config-daemon.scm:1338 +msgid "What is the maximum number of bytes per second that we may send?" +msgstr "" + +#: contrib/config-daemon.scm:1350 +msgid "What is the maximum CPU load (percentage)?" +msgstr "" + +#: contrib/config-daemon.scm:1351 +msgid "" +"The highest tolerable CPU load. Load here always refers to the total system " +"load, that is it includes CPU utilization by other processes. A value of 50 " +"means that once your 1 minute-load average goes over 50% non-idle, GNUnet " +"will try to reduce CPU consumption until the load goes under the threshold. " +"Reasonable values are typically between 50 and 100. Multiprocessors may use " +"values above 100." +msgstr "" + +#: contrib/config-daemon.scm:1362 +msgid "What is the maximum IO load (permille)?" +msgstr "" + +#: contrib/config-daemon.scm:1364 +msgid "" +"The highest tolerable IO load. Load here refers to the percentage of CPU " +"cycles wasted waiting for IO for the entire system, that is it includes disk " +"utilization by other processes. A value of 10 means that once the average " +"number of cycles wasted waiting for IO is more than 10% non-idle, GNUnet " +"will try to reduce IO until the load goes under the threshold. Reasonable " +"values are typically between 10 and 75." +msgstr "" + +#: contrib/config-daemon.scm:1375 +msgid "What is the maximum CPU load (hard limit)?" +msgstr "" + +#: contrib/config-daemon.scm:1376 +msgid "" +"The highest tolerable CPU load. This is the hard limit, so once it is " +"reached, gnunetd will start to massively drop data to reduce the load. Use " +"with caution." +msgstr "" + +#: contrib/config-daemon.scm:1387 +msgid "What is the maximum upstream bandwidth (hard limit)?" +msgstr "" + +#: contrib/config-daemon.scm:1388 +msgid "" +"The limit is given as a percentage of the MAXNETUPBPS limit. Use 100 to " +"have MAXNETUPBPS be the hard limit. Use zero for no limit." +msgstr "" + +#: contrib/config-daemon.scm:1400 +msgid "What priority should gnunetd use to run?" +msgstr "" + +#: contrib/config-daemon.scm:1401 +msgid "" +"You can specify priorities like NORMAL, ABOVE NORMAL, BELOW NORMAL, HIGH and " +"IDLE or a numerical integer value (man nice). The default is IDLE, which " +"should result in gnunetd only using resources that would otherwise be idle." +msgstr "" + +#: contrib/config-daemon.scm:1413 +msgid "Should we disable random padding (experimental option)?" +msgstr "" + +#: contrib/config-daemon.scm:1425 +msgid "Use basic bandwidth limitation? (YES/NO). If in doubt, say YES." +msgstr "" + +#: contrib/config-daemon.scm:1427 +msgid "" +"Basic bandwidth limitation (YES) means simply that the bandwidth limits " +"specified apply to GNUnet and only to GNUnet. If set to YES, you simply " +"specify the maximum bandwidth (upstream and downstream) that GNUnet is " +"allowed to use and GNUnet will stick to those limitations. This is useful " +"if your overall bandwidth is so large that the limit is mostly used to " +"ensure that enough capacity is left for other applications. Even if you " +"want to dedicate your entire connection to GNUnet you should not set the " +"limits to values higher than what you have since GNUnet uses those limits to " +"determine for example the number of connections to establish (and it would " +"be inefficient if that computation yields a number that is far too high). \n" +"\n" +"While basic bandwidth limitation is simple and always works, there are some " +"situations where it is not perfect. Suppose you are running another " +"application which performs a larger download. During that particular time, " +"it would be nice if GNUnet would throttle its bandwidth consumption " +"(automatically) and resume using more bandwidth after the download is " +"complete. This is obviously advanced magic since GNUnet will have to " +"monitor the behavior of other applications. Another scenario is a monthly " +"cap on bandwidth imposed by your ISP, which you would want to ensure is " +"obeyed. Here, you may want GNUnet to monitor the traffic from other " +"applications to ensure that the combined long-term traffic is within the pre-" +"set bounds. Note that you should probably not set the bounds tightly since " +"GNUnet may observe that the bounds are about to be broken but would be " +"unable to stop other applications from continuing to use bandwidth.\n" +"\n" +"If either of these two scenarios applies, set BASICLIMITING to NO. Then set " +"the bandwidth limits to the COMBINED amount of traffic that is acceptable " +"for both GNUnet and other applications. GNUnet will then immediately " +"throttle bandwidth consumption if the short-term average is above the limit, " +"and it will also try to ensure that the long-term average is below the " +"limit. Note however that using NO can have the effect of GNUnet (almost) " +"ceasing operations after other applications perform high-volume downloads " +"that are beyond the defined limits. GNUnet would reduce consumption until " +"the long-term limits are again within bounds.\n" +"\n" +"NO only works on platforms where GNUnet can monitor the amount of traffic " +"that the local host puts out on the network. This is only implemented for " +"Linux and Win32. In order for the code to work, GNUnet needs to know the " +"specific network interface that is used for the external connection (after " +"all, the amount of traffic on loopback or on the LAN should never be counted " +"since it is irrelevant)." +msgstr "" + +#: contrib/config-daemon.scm:1444 +#, fuzzy +msgid "Network interface to monitor" +msgstr "Nätverksgränssnitt:" + +#: contrib/config-daemon.scm:1445 +msgid "" +"For which interfaces should we do accounting? GNUnet will evaluate the " +"total traffic (not only the GNUnet related traffic) and adjust its bandwidth " +"usage accordingly. You can currently only specify a single interface. GNUnet " +"will also use this interface to determine the IP to use. Typical values are " +"eth0, ppp0, eth1, wlan0, etc. 'ifconfig' will tell you what you have. " +"Never use 'lo', that just won't work. Under Windows, specify the index " +"number reported by 'gnunet-win-tool -n'." +msgstr "" + +#: contrib/config-daemon.scm:1456 +msgid "Load management" +msgstr "" + +#: contrib/config-daemon.scm:1482 contrib/config-client.scm:413 +msgid "Root node" +msgstr "" + +#: contrib/config-client.scm:153 +msgid "Where should gnunet-clients write their logs?" +msgstr "" + +#: contrib/config-client.scm:185 +msgid "On which machine and port is gnunetd running (for clients)?" +msgstr "" + +#: contrib/config-client.scm:186 +msgid "This is equivalent to the -H option. The format is IP:PORT." +msgstr "" + +#: contrib/config-client.scm:197 +#, fuzzy +msgid "What is the path to the configuration file for gnunetd?" +msgstr "skriv ut ett värde frÃ¥n konfigurationsfilen till standard ut" + +#: contrib/config-client.scm:198 +msgid "This option is used when clients need to start gnunetd." +msgstr "" + +#: contrib/config-client.scm:210 +#, fuzzy +msgid "General options" +msgstr "Visa alla alternativ" + +#: contrib/config-client.scm:227 +msgid "Do not add metadata listing the creation time for inserted content" +msgstr "" + +#: contrib/config-client.scm:239 +msgid "Which non-default extractors should GNUnet use for keyword extractors" +msgstr "" + +#: contrib/config-client.scm:240 +msgid "" +"Specify which additional extractor libraries should be used. gnunet-insert " +"uses libextractor to extract keywords from files. libextractor can be " +"dynamically extended to handle additional file formats. If you want to use " +"more than the default set of extractors, specify additional extractor " +"libraries here. The format is [[-]LIBRARYNAME[:[-]LIBRARYNAME]*].\n" +"\n" +"The default is to use filenames and to break larger words at spaces (and " +"underscores, etc.). This should be just fine for most people. The '-' " +"before a library name indicates that this should be executed last and makes " +"only sense for the split-library." +msgstr "" + +#: contrib/config-client.scm:253 +msgid "How many entries should the URI DB table have?" +msgstr "" + +#: contrib/config-client.scm:254 +msgid "" +"GNUnet uses two bytes per entry on the disk. This database is used to keep " +"track of how a particular URI has been used in the past. For example, " +"GNUnet may remember that a particular URI has been found in a search " +"previously or corresponds to a file uploaded by the user. This information " +"can then be used by user-interfaces to filter URI lists, such as search " +"results. If the database is full, older entries will be discarded. The " +"default value should be sufficient without causing undue disk utilization." +msgstr "" + +#: contrib/config-client.scm:265 +msgid "Location of the file specifying metadata for the auto-share directory" +msgstr "" + +#: contrib/config-client.scm:277 +msgid "" +"Location of the file with the PID of any running gnunet-auto-share daemon " +"process" +msgstr "" + +#: contrib/config-client.scm:289 +msgid "Location of the log file for gnunet-auto-share" +msgstr "" + +#: contrib/config-client.scm:301 +#, fuzzy +msgid "File-Sharing options" +msgstr "Visa alla alternativ" + +#: contrib/config-client.scm:319 +msgid "Which plugins should be loaded by gnunet-gtk?" +msgstr "" + +#: contrib/config-client.scm:320 +msgid "" +"Load the about plugin for the about dialog. The daemon plugin allows " +"starting and stopping of gnunetd and displays information about gnunetd. " +"The fs plugin provides the file-sharing functionality. The stats plugin " +"displays various statistics about gnunetd." +msgstr "" + +#: contrib/config-client.scm:331 +msgid "How frequently (in milli-seconds) should the statistics update?" +msgstr "" + +#: contrib/config-client.scm:332 +msgid "" +"Each pixel in the stats dialog corresponds to the time interval specified " +"here." +msgstr "" + +#: contrib/config-client.scm:344 +msgid "Do not show thumbnail previews from meta-data in search results" +msgstr "" + +#: contrib/config-client.scm:345 +msgid "" +"This option is useful for people who maybe offended by some previews or use " +"gnunet-gtk at work and would like to avoid bad surprises." +msgstr "" + +#: contrib/config-client.scm:356 +msgid "Do not show search results for files that were uploaded by us" +msgstr "" + +#: contrib/config-client.scm:357 +msgid "" +"This option is useful to eliminate files that the user already has from the " +"search. Naturally, enabling this option maybe confusing because some " +"obviously expected search results would no longer show up. This option only " +"works if the URI_DB_SIZE option under FS is not zero (since the URI DB is " +"used to determine which files the user is sharing)" +msgstr "" + +#: contrib/config-client.scm:369 +msgid "To which directory should gnunet-gtk save downloads to?" +msgstr "" + +#: contrib/config-client.scm:381 +#, fuzzy +msgid "Options related to gnunet-gtk" +msgstr "Ej ansluten till gnunetd." + +#: contrib/config-client.scm:401 +msgid "Full pathname of GNUnet client HOME directory" +msgstr "" + +#: contrib/config-client.scm:402 +msgid "The directory for GNUnet files that belong to the user." +msgstr "" + +#~ msgid "`%s' failed (%d, %u). Will not send PING.\n" +#~ msgstr "\"%s\" misslyckades (%d, %u). Kommer inte skicka PING.\n" + +#~ msgid "Invalid URL `%s' (must begin with `%s')\n" +#~ msgstr "Ogiltig URL \"%s\" (mÃ¥ste börja med \"%s\")\n" + +#~ msgid "`%s' to `%s' failed at %s:%d with error: %s\n" +#~ msgstr "\"%s\" till \"%s\" misslyckades vid %s:%d med fel: %s\n" + +#~ msgid "Parsing HTTP response for URL `%s' failed.\n" +#~ msgstr "Tolkning av HTTP-svar för URL \"%s\" misslyckades.\n" + +#~ msgid "Could not resolve name of HTTP proxy `%s'. Trying without a proxy.\n" +#~ msgstr "" +#~ "Kunde inte slÃ¥ upp namn för HTTP-proxy \"%s\". Försöker utan proxy.\n" + +#~ msgid "Message received from client is invalid\n" +#~ msgstr "Meddelande mottaget frÃ¥n klient är ogiltig.\n" + +#~ msgid "Maximum number of chat clients reached.\n" +#~ msgstr "Maximalt antal chattklienter uppnÃ¥tt.\n" + +#~ msgid "Now %d of %d chat clients at this node.\n" +#~ msgstr "Nu är det %d av %d chattklienter pÃ¥ den här noden.\n" + +#~ msgid "specify nickname" +#~ msgstr "ange smeknamn" + +#~ msgid "Start GNUnet chat client." +#~ msgstr "Starta GNUnets chattklient." + +#~ msgid "Could not connect to gnunetd.\n" +#~ msgstr "Kunde inte ansluta till gnunetd.\n" + +#~ msgid "You must specify a nickname (use option `%s').\n" +#~ msgstr "Du mÃ¥ste ange ett smeknamn (använd flagga \"%s\").\n" + +#~ msgid "Could not send message to gnunetd\n" +#~ msgstr "Kunde inte skicka meddelande till gnunetd\n" + +#~ msgid "Database failed to delete `%s'.\n" +#~ msgstr "Databas misslyckades att ta bort \"%s\".\n" + +#~ msgid "`%s' failed: table not found!\n" +#~ msgstr "\"%s\" misslyckades: tabell hittades inte!\n" + +#~ msgid "`%s' failed. Terminating connection to client.\n" +#~ msgstr "\"%s\" misslyckades. Terminerar anslutning till klient.\n" + +#~ msgid "sendAck failed. Terminating connection to client.\n" +#~ msgstr "sendAck misslyckades. Terminerar anslutning till klient.\n" + +#~ msgid "Could not find peer `%s' in routing table!\n" +#~ msgstr "Kunde inte hitta motpart \"%s\" i routingtabell!\n" + +#~ msgid "`%s' called with timeout above 1 hour (bug?)\n" +#~ msgstr "\"%s\" anropad med timeout över 1 timma (fel?)\n" + +#~ msgid "Invalid response to `%s' from `%s'\n" +#~ msgstr "Ogiltigt svar till \"%s\" frÃ¥n \"%s\"\n" + +#~ msgid "Received invalid RPC `%s'.\n" +#~ msgstr "Mottog ogiltig RPC \"%s\".\n" + +#~ msgid "allow SIZE bytes of memory for the local table" +#~ msgstr "tillÃ¥t STORLEK byte av minne för lokaltabellen" + +#~ msgid "Superflous arguments (ignored).\n" +#~ msgstr "Onödiga argument (ignorerade).\n" + +#~ msgid "Call to `%s' returns %d.\n" +#~ msgstr "Anrop till \"%s\" returnerade %d.\n" + +#~ msgid "Call to `%s' with key `%s'.\n" +#~ msgstr "Anrop till \"%s\" med nyckel \"%s\".\n" + +#~ msgid "Call to `%s' with value '%.*s' (%d bytes).\n" +#~ msgstr "Anrop till \"%s\" med värde \"%.*s\" (%d byte).\n" + +#~ msgid "Error leaving DHT.\n" +#~ msgstr "Fel vid lämning av DHT.\n" + +#~ msgid "query table called NAME" +#~ msgstr "frÃ¥ga tabell kallad NAME" + +#~ msgid "No commands specified.\n" +#~ msgstr "Inga kommandon angivna.\n" + +#~ msgid "'%s(%s,%s)' succeeded\n" +#~ msgstr "\"%s(%s,%s)\" lyckades\n" + +#~ msgid "Failed to send `%s'. Closing connection.\n" +#~ msgstr "Misslyckades att skicka \"%s\". Stänger anslutning.\n" + +#~ msgid "Received invalid `%s' request (size %d)\n" +#~ msgstr "Mottog ogiltig \"%s\" begäran (storlek %d)\n" + +#~ msgid "Received invalid `%s' request (wrong table)\n" +#~ msgstr "Mottog ogiltig \"%s\" begäran (fel tabell)\n" + +#~ msgid "Received unknown request type %d at %s:%d\n" +#~ msgstr "Mottog okänd typ av begäran %d vid %s:%d\n" + +#~ msgid "gnunetd signaled error in response to `%s' message\n" +#~ msgstr "gnunetd signalerade fel i svar till \"%s\" meddelande\n" + +#~ msgid "Failed to receive response to `%s' message from gnunetd\n" +#~ msgstr "Misslyckades att ta emot svar till \"%s\" meddelande frÃ¥n gnunetd\n" + +#~ msgid "Failed to send `%s' message to gnunetd\n" +#~ msgstr "Misslyckades att skicka \"%s\" meddelande till gnunetd\n" + +#~ msgid "Unexpected reply to `%s' operation.\n" +#~ msgstr "Oväntat svar till \"%s\" operation.\n" + +#~ msgid "Waiting for gnunetd to start (%u iterations left)...\n" +#~ msgstr "Väntar pÃ¥ att gnunetd ska starta (%u iterationer kvar)...\n" + +#~ msgid "Write(%d, %p, %d) failed: %s\n" +#~ msgstr "Skrivning(%d, %p, %d) misslyckades: %s\n" + +#~ msgid "Cannot create pseudonym `%s', file `%s' exists.\n" +#~ msgstr "Kan inte skapa pseudonym \"%s\", filen \"%s\" existerar.\n" + +#~ msgid "AND" +#~ msgstr "OCH" + +#~ msgid "Publication interval for periodic publication changed." +#~ msgstr "Publiseringsintervall för periodisk publisering har ändrats." + +#~ msgid "Unindex failed." +#~ msgstr "Avindexering misslyckades." + +#~ msgid "" +#~ "You must specify a postive number for `%s' in the configuration in " +#~ "section `%s'.\n" +#~ msgstr "" +#~ "Du mÃ¥ste ange ett positivt nummer för \"%s\" i konfigurationen i sektion " +#~ "\"%s\".\n" + +#~ msgid "Indexed file disappeared, deleting block for query `%s'\n" +#~ msgstr "Indexerad fil försvann, tar bort block för frÃ¥ga \"%s\"\n" + +#~ msgid "%8u of %8u bytes deleted." +#~ msgstr "%8u av %8u byte togs bort." + +#~ msgid "specify the file to delete from GNUnet (obligatory, file must exist)" +#~ msgstr "" +#~ "ange filen att ta bort frÃ¥n GNUnet (obligatorisk, filen mÃ¥ste existera)" + +#~ msgid "" +#~ "Remove file from GNUnet. The specified file is not removed\n" +#~ "from the filesystem but just from the local GNUnet datastore." +#~ msgstr "" +#~ "Ta bort fil frÃ¥n GNUnet. Den angivna filen tas inte bort frÃ¥n\n" +#~ "filsystemet utan bara frÃ¥n det lokala GNUnet-datalagret." + +#~ msgid "You must specify a filename (option -f)\n" +#~ msgstr "Du mÃ¥ste ange ett filnamn (flagga -f)\n" + +#~ msgid "gnunet-directory [OPTIONS] [FILENAMES]" +#~ msgstr "gnunet-directory [FLAGGOR] [FILNAMN]" + +#~ msgid "LEVEL" +#~ msgstr "NIVÃ…" + +#~ msgid "FILENAME" +#~ msgstr "FILNAMN" + +#~ msgid "process directories recursively" +#~ msgstr "bearbeta kataloger rekursivt" + +#~ msgid "You must specify a list of files to insert.\n" +#~ msgstr "Du mÃ¥ste ange en lista av filer att lägga in.\n" + +#~ msgid "Only one file or directory can be specified at a time.\n" +#~ msgstr "Endast en fil eller katalog kan anges samtidigt.\n" + +#~ msgid "You must specify a file or directory to upload.\n" +#~ msgstr "Du mÃ¥ste ange en fil eller katalog att ladda upp.\n" + +#~ msgid "Invalid arguments. Exiting.\n" +#~ msgstr "Ogiltiga argument. Avslutar.\n" + +#~ msgid "Not enough arguments. You must specify a keyword or identifier.\n" +#~ msgstr "" +#~ "Inte tillräckligt med argument. Du mÃ¥ste ange ett nyckelord eller " +#~ "identifierare.\n" + +#~ msgid "`%s' registering handlers %d %d\n" +#~ msgstr "\"%s\" registrerar handtag %d %d\n" + +#~ msgid "" +#~ "Configuration file must specify a directory for GNUnet to store per-peer " +#~ "data under %s%s\n" +#~ msgstr "" +#~ "Konfigurationsfil mÃ¥ste ange en katalog för GNUnet att lagra motpartsdata " +#~ "under %s%s\n" + +#~ msgid "Could not create hostkey!\n" +#~ msgstr "Kunde inte skapa värdnyckel!\n" + +#~ msgid "%s `%s' returned no known hosts!\n" +#~ msgstr "%s \"%s\" returnerade inga kända värdar!\n" + +#~ msgid "Trying to use file `%s' for MySQL configuration.\n" +#~ msgstr "Försöker använda fil \"%s\" för MySQL-konfiguration.\n" + +#~ msgid "Could not initialize libgnunetutil!\n" +#~ msgstr "Kunde inte initiera libgnunetutil!\n" + +#~ msgid "helptext for -t" +#~ msgstr "hjälptext för -t" + +#~ msgid "Template for gnunet-clients." +#~ msgstr "Mall för gnunet-clients." + +#~ msgid "Start GNUnet-testbed helper." +#~ msgstr "Starta hjälpprogrammet GNUnet-testbed." + +#~ msgid "Command `%s' not found!\n" +#~ msgstr "Kommando \"%s\" hittades inte!\n" + +#~ msgid "size of `%s' message is too short. Ignoring.\n" +#~ msgstr "storlek pÃ¥ \"%s\" meddelande är för litet. Ignorerar.\n" + +#~ msgid "size of `%s' message is wrong. Ignoring.\n" +#~ msgstr "storlek pÃ¥ \"%s\" meddelande är fel. Ignorerar.\n" + +#~ msgid "received invalid `%s' message\n" +#~ msgstr "mottog ogiltigt \"%s\" meddelande\n" + +#~ msgid "received invalid `%s' message: %s.\n" +#~ msgstr "mottog ogiltigt \"%s\" meddelande: %s.\n" + +#~ msgid "'..' is not allowed in file name (%s).\n" +#~ msgstr "\"..\" tillÃ¥ts inte i filnamn (%s).\n" + +#~ msgid "Invalid message received at %s:%d." +#~ msgstr "Ogiltigt meddelande mottogs den %s:%d." + +#~ msgid "Could not resolve name of HTTP proxy `%s'.\n" +#~ msgstr "Kunde inte slÃ¥ upp namn för HTTP-proxy \"%s\".\n" + +#~ msgid "Failed to send HTTP request to host `%s': %s\n" +#~ msgstr "Misslyckades att skicka HTTP-begäran till värd \"%s\": %s\n" + +#~ msgid "Failed so send HTTP request `%s' to host `%s': %s\n" +#~ msgstr "Misslyckades att skicka HTTP-begäran \"%s\" till värd \"%s\": %s\n" + +#~ msgid "Peer `%s' did not report back.\n" +#~ msgstr "Motpart \"%s\" rapporterade inte tillbaka.\n" + +#~ msgid "Could not send request to gnunetd.\n" +#~ msgstr "Kunde inte skicka begäran till gnunetd.\n" + +#~ msgid "%s: symbol value `%s' invalid for %s\n" +#~ msgstr "%s: symbolvärde \"%s\" ogiltigt för %s\n" + +#~ msgid "Sorry, no help is available for this option.\n" +#~ msgstr "Tyvärr, ingen hjälp är tillgänglig för den här flaggan.\n" + +#~ msgid "_File" +#~ msgstr "_Fil" + +#~ msgid "_Load" +#~ msgstr "_Läs in" + +#~ msgid "Load a config file" +#~ msgstr "Läs in en konfigurationsfil" + +#~ msgid "_Save" +#~ msgstr "_Spara" + +#~ msgid "Save the config in .config" +#~ msgstr "Spara konfigurationen i .config" + +#~ msgid "_Quit" +#~ msgstr "_Avsluta" + +#~ msgid "Show _name" +#~ msgstr "Visa _namn" + +#~ msgid "Show _range" +#~ msgstr "Visa _omfÃ¥ng" + +#~ msgid "Show range (Y/M/N)" +#~ msgstr "Visa omfÃ¥ng (Y/M/N)" + +#~ msgid "Show _data" +#~ msgstr "Visa _data" + +#~ msgid "Show value of the option" +#~ msgstr "Visa värde av alternativet" + +#~ msgid "Show all _options" +#~ msgstr "Visa alla a_lternativ" + +#~ msgid "_Help" +#~ msgstr "_Hjälp" + +#~ msgid "_Introduction" +#~ msgstr "_Introduktion" + +#~ msgid "_License" +#~ msgstr "_Licens" + +#~ msgid "Goes up of one level (single view)" +#~ msgstr "GÃ¥r upp en nivÃ¥ (enkel vy)" + +#~ msgid "Load" +#~ msgstr "Läs in" + +#~ msgid "Save a config file" +#~ msgstr "Spara en konfigurationsfil" + +#~ msgid "Save" +#~ msgstr "Spara" + +#~ msgid "Single view" +#~ msgstr "Enkel vy" + +#~ msgid "Single" +#~ msgstr "Enkel" + +#~ msgid "Split view" +#~ msgstr "Dela vy" + +#~ msgid "Split" +#~ msgstr "Dela" + +#~ msgid "Full view" +#~ msgstr "Full vy" + +#~ msgid "Full" +#~ msgstr "Full" + +#~ msgid "Collapse" +#~ msgstr "Fäll in" + +#~ msgid "Expand" +#~ msgstr "Expandera" + +#~ msgid "Sorry, no help available for this option yet." +#~ msgstr "Tyvärr, ingen hjälp tillgänglig för den här flaggan ännu." + +#~ msgid "Couldn't find pixmap file: %s" +#~ msgstr "Kunde inte htta bildfil: %s" + +#~ msgid "Available MODEs:\n" +#~ msgstr "Tillgängliga lägen:\n" + +#~ msgid " config\t\ttext-based configuration\n" +#~ msgstr " config\t\ttextbaserad konfiguration\n" + +#~ msgid " menuconfig\ttext-based menu\n" +#~ msgstr " menuconfig\ttextbaserad meny\n" + +#~ msgid " wizard-curses\tBasic text-based graphical configuration\n" +#~ msgstr " wizard-curses\tEnkel textbaserad grafisk konfiguration\n" + +#~ msgid "" +#~ " wizard-gtk\tBasic GTK configuration\n" +#~ "\n" +#~ msgstr "" +#~ " wizard-gtk\tEnkel GTK-konfiguration\n" +#~ "\n" + +#~ msgid "gnunet-setup must have write-access to the configuration file `%s'\n" +#~ msgstr "" +#~ "gnunet-setup mÃ¥ste ha skrivrättighet till konfigurationsfilen \"%s\"\n" + +#~ msgid "" +#~ "Can only run wizard to configure gnunetd.\n" +#~ "Did you forget the `%s' option?\n" +#~ msgstr "" +#~ "Kan endast köra vägvisaren för att konfigurera gnunetd.\n" +#~ "Glömde du flaggan \"%s\"?\n" + +#~ msgid "" +#~ "You can limit GNUnet's resource usage here.\n" +#~ "\n" +#~ "This is the percentage of processor time GNUnet is allowed to use." +#~ msgstr "" +#~ "Du kan begränsa GNUnets resursanvändning här.\n" +#~ "\n" +#~ "Det här är det procenttal som GNUnet tillÃ¥ts använda av processortiden." + +#~ msgid "" +#~ "Welcome to GNUnet!\n" +#~ "\n" +#~ "This assistant will ask you a few basic questions in order to configure " +#~ "GNUnet.\n" +#~ "\n" +#~ "Please visit our homepage at\n" +#~ "\thttp://www.gnunet.org\n" +#~ "and join our community at\n" +#~ "\thttp://www.gnunet.org/drupal/\n" +#~ "\n" +#~ "Have a lot of fun,\n" +#~ "\n" +#~ "the GNUnet team" +#~ msgstr "" +#~ "Välkommen till GNUnet!\n" +#~ "\n" +#~ "Denna assistant kommer att frÃ¥ga dig nÃ¥gra enkla frÃ¥gor för att " +#~ "konfigurera GNUnet.\n" +#~ "\n" +#~ "Besök pÃ¥ webbplats pÃ¥\n" +#~ "\thttp://gnunet.org/\n" +#~ "och gÃ¥ med i vÃ¥r gemenskap pÃ¥\n" +#~ "\thttp://www.gnunet.org/drupal/\n" +#~ "\n" +#~ "Ha det sÃ¥ kul,\n" +#~ "\n" +#~ "GNUnet-laget" + +#~ msgid "Next" +#~ msgstr "Nästa" + +#~ msgid "IP-Address/Hostname:" +#~ msgstr "IP-adress/Värdnamn:" + +#~ msgid "Bandwidth limitation" +#~ msgstr "Bandbreddsbegränsning" + +#~ msgid "Bandwidth sharing" +#~ msgstr "Bandbreddsdelning" + +#~ msgid "Max. CPU usage (%):" +#~ msgstr "Max. CPU-användning (%):" + +#~ msgid "CPU usage" +#~ msgstr "CPU-användning" + +#~ msgid "Finish" +#~ msgstr "Slutför" + +#~ msgid "Question" +#~ msgstr "FrÃ¥ga" + +#~ msgid "Group:" +#~ msgstr "Grupp:" + +#~ msgid "User account:" +#~ msgstr "Användarkonto:" + +#~ msgid "gnunet-update failed!" +#~ msgstr "gnunet-update misslyckades!" + +#~ msgid "No applications defined in configuration!\n" +#~ msgstr "Inga applikationer definierade i konfiguration!\n" + +#~ msgid "No reply received within %llums.\n" +#~ msgstr "Inget svar mottaget inom %llums.\n" + +#~ msgid "You must specify a non-empty set of transports to test!\n" +#~ msgstr "Du mÃ¥ste ange en icke-tom uppsättning av transporter att testa!\n" + +#~ msgid "" +#~ "\n" +#~ "Exiting.\n" +#~ msgstr "" +#~ "\n" +#~ "Avslutar.\n" + +#~ msgid "Updated data for %d applications.\n" +#~ msgstr "Uppdaterade data för %d program.\n" + +#~ msgid "`%s' starting\n" +#~ msgstr "\"%s\" startar\n" + +#~ msgid "Invalid command-line arguments:\n" +#~ msgstr "Ogiltiga kommandoradsargument:\n" + +#~ msgid "Argument %d: `%s'\n" +#~ msgstr "Argument %d: \"%s\"\n" + +#~ msgid "%s: Rejected connection from blacklisted address %u.%u.%u.%u.\n" +#~ msgstr "%s: Vägrade anslutning frÃ¥n svartlistad adress %u.%u.%u.%u.\n" + +#~ msgid "HTTP: Could not determine my public IP address.\n" +#~ msgstr "HTTP: Kunde inte fastställa min publika IP-adress.\n" + +#~ msgid "Could not resolve name of SMTP server `%s': %s" +#~ msgstr "Kunde inte slÃ¥ upp namnet för SMTP-server \"%s\": %s" + +#~ msgid "" +#~ "You must specify the name of a pipe for the SMTP transport in section `%" +#~ "s' under `%s'.\n" +#~ msgstr "" +#~ "Du mÃ¥ste ange ett namn pÃ¥ röret för SMTP-transporten i sektion \"%s\" " +#~ "under \"%s\".\n" + +#~ msgid "Sending E-mail to `%s' failed.\n" +#~ msgstr "Sändning av e-post till \"%s\" misslyckades.\n" + +#~ msgid "%.*s filter %s (SMTP)" +#~ msgstr "%.*s filter %s (SMTP)" + +#~ msgid "Could not determine my public IPv6 address.\n" +#~ msgstr "Kunde inte fastställa min publika IPv6-adress.\n" + +#~ msgid "`%s': unknown service: %s\n" +#~ msgstr "\"%s\": okänd tjänst: %s\n" + +#~ msgid "Failed to bind to UDP6 port %d.\n" +#~ msgstr "Misslyckades att binda till UDP6-port %d.\n" + +#~ msgid "Configuration file `%s' not found. Run gnunet-setup!\n" +#~ msgstr "Konfigurationsfil \"%s\" hittades inte. Kör gnunet-setup!\n" + +#~ msgid "" +#~ "Configuration file not found. Please run GNUnet Setup (Client " +#~ "Configuration) first." +#~ msgstr "" +#~ "Konfigurationsfil hittades inte. Vänligen kör konfigurationen för GNUnet " +#~ "(klientkonfiguration) först." + +#~ msgid "Configuration file `%s' not found. Run `gnunet-setup -d'!\n" +#~ msgstr "Konfigurationsfil \"%s\" hittades inte. Kör \"gnunet-setup -d\"!\n" + +#~ msgid "Cron stopped\n" +#~ msgstr "Cron stoppad\n" + +#~ msgid "Shutdown complete.\n" +#~ msgstr "Nedstängning klar.\n" + +#~ msgid "Caught signal %d.\n" +#~ msgstr "FÃ¥ngade signal %d.\n" + +#~ msgid "Invalid network notation (additional characters: `%s')." +#~ msgstr "Ogiltig nätverksnotation (ytterligare tecken: \"%s\")." + +#~ msgid "FAILURE" +#~ msgstr "MISSLYCKANDE" + +#~ msgid "MESSAGE" +#~ msgstr "MEDDELANDE" + +#~ msgid "CRON" +#~ msgstr "CRON" + +#~ msgid "EVERYTHING" +#~ msgstr "ALLT" + +#~ msgid "Invalid LOGLEVEL `%s' specified.\n" +#~ msgstr "Ogiltig LOGGNIVÃ… \"%s\" angiven.\n" + +#~ msgid "" +#~ "Usage: %s\n" +#~ "%s\n" +#~ "\n" +#~ msgstr "" +#~ "Användning: %s\n" +#~ "%s\n" +#~ "\n" + +#~ msgid "" +#~ "Configuration file must specify a directory for GNUnet to store per-peer " +#~ "data under %s\\%s.\n" +#~ msgstr "" +#~ "Konfigurationsfil mÃ¥ste ange en katalog för GNUnet att lagra motpartsdata " +#~ "under %s\\%s.\n" + +#~ msgid "`%s' is not a regular file.\n" +#~ msgstr "\"%s\" är inte en vanlig fil.\n" + +#~ msgid "g" +#~ msgstr "g" + +#~ msgid "t" +#~ msgstr "t" + +#~ msgid "`%s' failed, other side closed connection.\n" +#~ msgstr "\"%s\" misslyckades, andra sidan har stängt anslutningen.\n" + +#~ msgid "Attempted path to `%s' was `%s'.\n" +#~ msgstr "Försökt sökväg till \"%s\" var \"%s\".\n" + +#~ msgid "set verbosity to LEVEL" +#~ msgstr "sätt informationsnivÃ¥ till NIVÃ…" diff --git a/po/update.pl b/po/update.pl new file mode 100644 index 000000000..c7c34b612 --- /dev/null +++ b/po/update.pl @@ -0,0 +1,230 @@ +#!/usr/bin/perl -w + +# GNOME po update utility. +# (C) 2000 The Free Software Foundation +# +# Author(s): Kenneth Christiansen +# Patches: Björn Voigt + + +$VERSION = "1.2.5 beta 2"; +$LANG = $ARGV[0]; +$PACKAGE = "GNUnet"; + +if (! $LANG){ + print "update.pl: missing file arguments\n"; + print "Try `update.pl --help' for more information.\n"; + exit; +} + +if ($LANG=~/^-(.)*/){ + + if ("$LANG" eq "--version" || "$LANG" eq "-V"){ + print "GNOME PO Updater $VERSION\n"; + print "Written by Kenneth Christiansen , 2000.\n\n"; + print "Copyright (C) 2000 Free Software Foundation, Inc.\n"; + print "This is free software; see the source for copying conditions. There is NO\n"; + print "warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n"; + exit; + } + + + elsif ($LANG eq "--help" || "$LANG" eq "-H"){ + print "Usage: ./update.pl [OPTIONS] ...LANGCODE\n"; + print "Updates pot files and merge them with the translations.\n\n"; + print " -V, --version shows the version\n"; + print " -H, --help shows this help page\n"; + print " -P, --pot only generates the potfile\n"; + print " -M, --maintain search for missing files in POTFILES.in\n"; + print "\nExamples of use:\n"; + print "update.sh --pot just creates a new pot file from the source\n"; + print "update.sh da created new pot file and updated the da.po file\n\n"; + print "Report bugs to .\n"; + exit; + } + + elsif($LANG eq "--pot" || "$LANG" eq "-P"){ + + print "Building the $PACKAGE.pot ..."; + + $b="xgettext --default-domain\=$PACKAGE --directory\=\.\." + ." --add-comments --keyword\=\_ --keyword\=N\_" + ." --files-from\=\.\/POTFILES\.in "; + $b1="test \! -f $PACKAGE\.po \|\| \( rm -f \.\/$PACKAGE\.pot " + ."&& mv $PACKAGE\.po \.\/$PACKAGE\.pot \)"; + if(($ret=system($b . " && " . $b1))==0) { + print "...done\n"; + } + else { + print "...failed\n"; + } + + exit $ret; + } + + elsif ($LANG eq "--maintain" || "$LANG" eq "-M"){ + + $a="find ../ -path ../intl -prune -o -print | egrep '.*\\.(c|y|cc|c++|h|gob)\$' "; + + open(BUF2, "POTFILES.in") || die "update.pl: there's not POTFILES.in!!!\n"; + print "Searching for missing _(\" \") entries and for deleted files...\n"; + open(BUF1, "$a|"); + + + @buf2 = ; + @buf1 = ; + + if (-s "POTFILES.ignore") { + open FILE, "POTFILES.ignore"; + while () { + if ($_=~/^[^#]/o) { + push @bup, $_; + } + } + print "POTFILES.ignore found! Ignoring files...\n"; + @buf2 = (@bup, @buf2); + } + + foreach my $file (@buf1) { + $cmd="xgettext -o - --omit-header --keyword=_ " . + "--keyword=N_ " . $file . " |"; + open XGET, $cmd; + if() { + $file = unpack("x3 A*",$file) . "\n"; + push @buff1, $file; + } + close XGET; +# open FILE, "<$file"; +# while () { +# if ($_=~/_\(\"/o || $_=~/ngettext\(\"/o){ +# $file = unpack("x3 A*",$file) . "\n"; +# push @buff1, $file; +# last; +# } +# } + } + + @bufff1 = sort (@buff1); + + @bufff2 = sort (@buf2); + + my %in2; + foreach (@bufff2) { + chomp; + $in2{$_} = 1; + } + + my %in1; + foreach (@bufff1) { + chomp; + $in1{$_} = 1; + } + + foreach (@bufff1) { + chomp; + if (!exists($in2{$_})) { + push @result, $_ . "\n"; + } + } + + foreach (@bufff2) { + chomp; + if (! -f "../" . $_) { + push @deletedfiles, $_ . "\n"; + } + } + + foreach (@bufff2) { + if (!exists($in1{$_})) { + push @noi18nfiles, $_ . "\n"; + } + } + + if(@result){ + open OUT, ">POTFILES.in.missing"; + print OUT @result; + print "\nHere are the missing files:\n\n", @result, "\n"; + print "File POTFILES.in.missing is being placed in directory...\n"; + print "Please add the files that should be ignored in POTFILES.ignore\n"; + } + else{ + unlink("POTFILES.in.missing"); + } + + if(@deletedfiles){ + open OUT, ">POTFILES.in.deleted"; + print OUT @deletedfiles; + print "\nHere are the deleted files:\n\n", @deletedfiles, "\n"; + print "File POTFILES.in.deleted is being placed in directory...\n"; + print "Please delete the files from POTFILES.in or POTFILES.ignore\n"; + } + else{ + unlink("POTFILES.in.deleted"); + } + + if(@noi18nfiles){ + open OUT, ">POTFILES.in.noi18n"; + print OUT @noi18nfiles; + print "\nHere are the files which currently have no i18n strings:\n\n", + @noi18nfiles, "\n"; + print "File POTFILES.in.noi18n is being placed in directory...\n"; + print "Please delete the files from POTFILES.in or POTFILES.ignore\n"; + print "or ignore the files.\n"; + } + else{ + unlink("POTFILES.in.noi18n"); + } + + if( ! @result && ! @deletedfiles ) { + print "\nWell, it's all perfect! Congratulation!\n"; + } + } + + + else{ + print "update.pl: invalid option -- $LANG\n"; + print "Try `update.pl --help' for more information.\n"; + } + exit; + } + +elsif(-s "$LANG.po"){ + + print "Building the $PACKAGE.pot ..."; + + $c="xgettext --default-domain\=$PACKAGE --directory\=\.\." + ." --add-comments --keyword\=\_ --keyword\=N\_" + ." --files-from\=\.\/POTFILES\.in "; + $c1="test \! -f $PACKAGE\.po \|\| \( rm -f \.\/$PACKAGE\.pot " + ."&& mv $PACKAGE\.po \.\/$PACKAGE\.pot \)"; + + if(($ret=system($c . " && " . $c1))==0) { + print "...done\n"; + } + else { + print "...failed\n"; + } + + if($ret==0) { + print "\nNow merging $LANG.po with $PACKAGE.pot, and creating an updated $LANG.po ...\n"; + + + $d="if msgmerge $LANG.po $PACKAGE.pot -o $LANG.new.po; then " . + " mv -f $LANG.new.po $LANG.po; " . + " msgfmt --statistics -c -v -o /dev/null $LANG.po; " . + "else " . + " echo \"msgmerge failed!\"; " . + " rm -f $LANG.new.po; ". + "fi"; + + $ret=system($d); + + exit $ret; + } +} + +else{ + print "update.pl: sorry $LANG.po does not exist!\n"; + print "Try `update.pl --help' for more information.\n"; + exit; +} diff --git a/po/vi.gmo b/po/vi.gmo new file mode 100644 index 000000000..9bd0b0088 Binary files /dev/null and b/po/vi.gmo differ diff --git a/po/vi.po b/po/vi.po new file mode 100644 index 000000000..2d9c3c3fc --- /dev/null +++ b/po/vi.po @@ -0,0 +1,5772 @@ +# Vietnamese translation for GNUnet. +# Copyright © 2008 Christian Grothoff (msgids). +# This file is distributed under the same license as the gnunet package. +# Phan Vinh Thinh , 2005. +# Clytie Siddall , 2008. +# +msgid "" +msgstr "" +"Project-Id-Version: gnunet 0.8.0a\n" +"Report-Msgid-Bugs-To: gnunet-developers@mail.gnu.org\n" +"POT-Creation-Date: 2009-01-08 10:55-0700\n" +"PO-Revision-Date: 2008-09-10 22:05+0930\n" +"Last-Translator: Clytie Siddall \n" +"Language-Team: Vietnamese \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Plural-Forms: nplurals=1; plural=0;\n" +"X-Generator: LocFactoryEditor 1.7b3\n" + +#: src/setup/ncurses/wizard_curs.c:72 src/setup/lib/wizard_util.c:155 +#: src/setup/lib/wizard_util.c:210 +msgid "Error" +msgstr "Lá»—i" + +#: src/setup/ncurses/wizard_curs.c:80 +msgid "Help" +msgstr "Trợ giúp" + +#: src/setup/ncurses/wizard_curs.c:87 +msgid "Error!" +msgstr "Lá»—i !" + +#: src/setup/ncurses/wizard_curs.c:101 src/applications/vpn/cs.c:94 +msgid "No" +msgstr "Không" + +#: src/setup/ncurses/wizard_curs.c:102 src/applications/vpn/cs.c:94 +msgid "Yes" +msgstr "Có" + +#: src/setup/ncurses/wizard_curs.c:118 src/setup/ncurses/wizard_curs.c:183 +#: src/setup/ncurses/wizard_curs.c:299 src/setup/ncurses/mconf.c:189 +#: src/setup/ncurses/mconf.c:285 src/setup/ncurses/mconf.c:365 +#: src/setup/ncurses/mconf.c:456 +msgid "Internal error! (Choice invalid?)" +msgstr "Lá»—i ná»™i bá»™ (sai chá»n ?)" + +#: src/setup/ncurses/wizard_curs.c:150 +msgid "Abort" +msgstr "Hủy" + +#: src/setup/ncurses/wizard_curs.c:151 +msgid "Ok" +msgstr "OK" + +#: src/setup/ncurses/wizard_curs.c:218 src/setup/ncurses/wizard_curs.c:284 +#: src/setup/ncurses/wizard_curs.c:425 +msgid "GNUnet configuration" +msgstr "Cấu hình GNUnet" + +#: src/setup/ncurses/wizard_curs.c:220 +msgid "" +"Welcome to GNUnet!\n" +"\n" +"This assistant will ask you a few basic questions in order to configure " +"GNUnet.\n" +"\n" +"Please visit our homepage at\n" +"\thttp://gnunet.org/\n" +"and join our community at\n" +"\thttp://gnunet.org/drupal/\n" +"\n" +"Have a lot of fun,\n" +"\n" +"the GNUnet team" +msgstr "" +"Xin chào mừng tá»›i GNUnet !\n" +"\n" +"Trình giúp đỡ này sẽ há»i bạn vài câu cÆ¡ bản để cấu hình GNUnet.\n" +"\n" +"Xin hãy thăm trang chủ của chúng tôi tại\n" +"\thttp://gnunet.org\n" +"và tham gia cá»™ng đồng tại\n" +"\thttp://www.gnunet.org/drupal/\n" +"\n" +"Chúc có nhiá»u niá»m vui,\n" +"\n" +"Ä‘á»™i GNUnet" + +#: src/setup/ncurses/wizard_curs.c:286 +msgid "" +"Choose the network interface that connects your computer to the internet " +"from the list below." +msgstr "" +"Chá»n giao diện mạng kết nối máy tính tá»›i Internet từ danh sách dÆ°á»›i đây." + +#: src/setup/ncurses/wizard_curs.c:304 src/setup/ncurses/wizard_curs.c:318 +msgid "" +"The \"Network interface\" is the device that connects your computer to the " +"internet. This is usually a modem, an ISDN card or a network card in case " +"you are using DSL." +msgstr "" +"« Giao diện mạng » là thiết bị kết nối máy tính vá»›i Internet. Äây thÆ°á»ng là " +"má»™t bá»™ Ä‘iá»u giải, má»™t bo mạch ISDN hay má»™t bo mạch mạng nếu dùng DSL." + +#: src/setup/ncurses/wizard_curs.c:315 +msgid "Network configuration: interface" +msgstr "Cấu hình mạng: giao diện" + +#: src/setup/ncurses/wizard_curs.c:317 +msgid "" +"What is the name of the network interface that connects your computer to the " +"Internet?" +msgstr "Máy tính này kết nối tá»›i Internet qua giao diện mạng tên nào?" + +#: src/setup/ncurses/wizard_curs.c:328 +msgid "Network configuration: IP" +msgstr "Cấu hình mạng: IP" + +#: src/setup/ncurses/wizard_curs.c:330 +msgid "What is this computer's public IP address or hostname?" +msgstr "Äịa chỉ IP công cá»™ng hoặc tên của máy tính này là gì?" + +#: src/setup/ncurses/wizard_curs.c:331 +msgid "" +"If your provider always assigns the same IP-Address to you (a \"static\" IP-" +"Address), enter it into the \"IP-Address\" field. If your IP-Address changes " +"every now and then (\"dynamic\" IP-Address) but there's a hostname that " +"always points to your actual IP-Address (\"Dynamic DNS\"), you can also " +"enter it here.\n" +"If left empty, GNUnet will try to automatically detect the IP.\n" +"You can specify a hostname, GNUnet will then use DNS to resolve it.\n" +"If in doubt, leave this empty." +msgstr "" +"Nếu nhà cung cấp luôn luôn gán cùng má»™t địa chỉ IP (má»™t địa chỉ IP « tÄ©nh »), " +"thì hãy nhập địa chỉ đó vào vùng « Äịa chỉ IP ». Nếu địa chỉ IP thay đổi sau " +"má»—i lần kết nối (địa chỉ IP « Ä‘á»™ng ») nhÆ°ng có má»™t tên máy luôn luôn chỉ tá»›i " +"địa chỉ IP thá»±c của bạn (« DNS Ä‘á»™ng »), thì cÅ©ng có thể nhập nó ở đây.\n" +"Nếu không biết phải làm gì, thì hãy để trống. GNUnet sẽ thá»­ tá»± Ä‘á»™ng phát " +"hiện địa chỉ IP.\n" +"Bạn cÅ©ng có thể ghi rõ tên máy. Vì thế GNUnet sẽ dùng dịch vụ DNS để giải " +"quyết nó." + +#: src/setup/ncurses/wizard_curs.c:346 +msgid "Bandwidth configuration: upload" +msgstr "Cấu hình dải thông: tải lên" + +#: src/setup/ncurses/wizard_curs.c:348 +msgid "How much upstream bandwidth (in bytes/s) may be used?" +msgstr "Dòng ra có thể dùng bao nhiêu byte?" + +#: src/setup/ncurses/wizard_curs.c:349 +msgid "" +"You can limit GNUnet's resource usage here.\n" +"\n" +"The \"upstream\" is the data channel through which data is *sent* to the " +"internet. The limit is the maximum amount which GNUnet is allowed to use. If " +"you have a flatrate, you can set it to the maximum speed of your internet " +"connection. You should not use a value that is higher than what your actual " +"connection allows." +msgstr "" +"Có thể giá»›i hạn sá»­ dụng tài nguyên của GNUnet ở đây.\n" +"\n" +"« Dòng ra » (upstream) là kênh dữ liệu qua đó _gá»­i_ dữ liệu tá»›i Internet. " +"Giá»›i hạn là số tối Ä‘a được gán cho GNUnet. Nếu có tốc Ä‘á»™ Ä‘á»u, thì có thể đặt " +"thành tốc Ä‘á»™ kết nối Internet lá»›n nhất. Không nên đặt giá trị lá»›n hÆ¡n số " +"được gán cho kết nối Internet của bạn." + +#: src/setup/ncurses/wizard_curs.c:361 +msgid "Bandwidth configuration: download" +msgstr "Cấu hình dải thông: tải xuống" + +#: src/setup/ncurses/wizard_curs.c:363 +msgid "How much downstream bandwidth (in bytes/s) may be used?" +msgstr "Dòng vào có thể dùng bao nhiêu byte?" + +#: src/setup/ncurses/wizard_curs.c:364 +msgid "" +"You can limit GNUnet's resource usage here.\n" +"\n" +"The \"downstream\" is the data channel through which data is *received* from " +"the internet. The limit is the maximum amount which GNUnet is allowed to " +"use. If you have a flatrate, you can set it to the maximum speed of your " +"internet connection. You should not use a value that is higher than what " +"your actual connection allows." +msgstr "" +"Có thể giá»›i hạn sá»­ dụng tài nguyên của GNUnet ở đây.\n" +"\n" +"« Dòng vào » (downstream) là kênh dữ liệu qua đó _nhận_ dữ liệu từ Internet. " +"Giá»›i hạn là số tối Ä‘a được gán cho GNUnet. Nếu có tốc Ä‘á»™ Ä‘á»u, thì có thể đặt " +"thành tốc Ä‘á»™ kết nối Internet lá»›n nhất. Không nên đặt giá trị lá»›n hÆ¡n số " +"được gán cho kết nối Internet của bạn." + +#: src/setup/ncurses/wizard_curs.c:376 +msgid "Quota configuration" +msgstr "Cấu hình hạn ngạch" + +#: src/setup/ncurses/wizard_curs.c:377 +msgid "What is the maximum size of the datastore in MB?" +msgstr "Kho dữ liệu có kích cỡ tối Ä‘a (theo MB)?" + +#: src/setup/ncurses/wizard_curs.c:378 +msgid "" +"The GNUnet datastore contains all content that GNUnet needs to store " +"(indexed, inserted and migrated content)." +msgstr "" +"Kho dữ liệu GNUnet chứa tất cả các dữ liệu GNUnet cần cất giữ (dữ liệu chỉ " +"mục, ná»™i dung chèn và nhập vào)." + +#: src/setup/ncurses/wizard_curs.c:390 +msgid "Daemon configuration: user account" +msgstr "Cấu hình trình ná»n: tài khoản ngÆ°á»i dùng" + +#: src/setup/ncurses/wizard_curs.c:391 +msgid "As which user should gnunetd be run?" +msgstr "Trình ná»n gnunetd nên chạy vá»›i tÆ° cách ngÆ°á»i dùng nào?" + +#: src/setup/ncurses/wizard_curs.c:393 +msgid "" +"For security reasons, it is a good idea to let this setup create a new user " +"account under which the GNUnet service is started at system startup.\n" +"\n" +"However, GNUnet may not be able to access files other than its own. This " +"includes files you want to publish in GNUnet. You'll have to grant read " +"permissions to the user specified below.\n" +"\n" +"Leave the field empty to run GNUnet with system privileges.\n" +msgstr "" +"Vì lý do bảo mật, nên cho phép thiết lập này tạo má»™t tài khoản ngÆ°á»i dùng " +"má»›i sở hữu dịch vụ GNUnet chạy má»—i khi khởi Ä‘á»™ng máy tính.\n" +"\n" +"Tuy nhiên, GNUnet có thể không truy cập được tá»›i các tập tin mà nó không sở " +"hữu, bao gồm các tập tin ngÆ°á»i dùng muốn Ä‘Æ°a ra chia sẻ trong GNUnet. Sẽ cần " +"cho phép ngÆ°á»i dùng chỉ ra dÆ°á»›i đây quyá»n Ä‘á»c chúng.\n" +"\n" +"Äể trống để chạy GNUnet vá»›i quyá»n hệ thống.\n" + +#: src/setup/ncurses/wizard_curs.c:410 +msgid "Daemon configuration: group account" +msgstr "Cấu hình trình ná»n: tài khoản nhóm" + +#: src/setup/ncurses/wizard_curs.c:411 +msgid "As which group should gnunetd be run?" +msgstr "gnunetd nên chạy nhÆ° nhóm nào?" + +#: src/setup/ncurses/wizard_curs.c:413 +msgid "" +"For security reasons, it is a good idea to let this setup create a new group " +"for the chosen user account.\n" +"\n" +"You can also specify a already existent group here.\n" +"\n" +"Only members of this group will be allowed to start and stop the the GNUnet " +"server and have access to GNUnet server data.\n" +msgstr "" +"Vì lý do bảo mật, nên để thiết lập này tạo má»™t nhóm má»›i cho tài khoản ngÆ°á»i " +"dùng đã chá»n\n" +"\n" +"CÅ©ng có thể chỉ ra má»™t nhóm đã có.\n" +"\n" +"Chỉ thành viên của nhóm này má»›i có quyá»n chạy và dừng trình phục vụ GNUnet " +"và có truy cập tá»›i dữ liệu của trình phục vụ GNUnet.\n" + +#: src/setup/ncurses/wizard_curs.c:427 +msgid "Do you want to automatically launch GNUnet as a system service?" +msgstr "Bạn có muốn tá»± Ä‘á»™ng khởi chạy GNUnet nhÆ° là dịch vụ hệ thống không?" + +#: src/setup/ncurses/wizard_curs.c:429 +msgid "" +"If you say \"yes\" here, the GNUnet background process will be automatically " +"started when you turn on your computer. If you say \"no\" here, you have to " +"launch GNUnet yourself each time you want to use it." +msgstr "" +"Bật tùy chá»n này thì tiến trình GNUnet ná»n được tá»± Ä‘á»™ng khởi chạy má»—i lần mở " +"máy tính. Không thì bạn cần phải tá»± khởi chạy GNUnet má»—i lần để sá»­ dụng nó." + +#: src/setup/ncurses/wizard_curs.c:452 src/setup/gtk/wizard_gtk.c:414 +#, c-format +msgid "Unable to save configuration file `%s':" +msgstr "Không thể lÆ°u tập tin cấu hình « %s »:" + +#: src/setup/ncurses/wizard_curs.c:472 +msgid "Unable to create user account for daemon." +msgstr "Không thể tạo tài khoản ngÆ°á»i dùng cho trình ná»n." + +#: src/setup/ncurses/wizard_curs.c:483 +msgid "Unable to setup autostart for daemon." +msgstr "Không thể thiết lập chức năng tá»± Ä‘á»™ng khởi chạy cho trình ná»n." + +#: src/setup/ncurses/wizard_curs.c:498 +msgid "Save configuration?" +msgstr "LÆ°u cấu hình không?" + +#: src/setup/ncurses/wizard_curs.c:499 +msgid "Save configuration now?" +msgstr "LÆ°u cấu hình ngay bây giá» không?" + +#: src/setup/ncurses/wizard_curs.c:539 src/setup/ncurses/mconf.c:500 +msgid "GNUnet Configuration" +msgstr "Cấu hình GNUnet" + +#: src/setup/ncurses/wizard_curs.c:543 +msgid "Back" +msgstr "Lùi" + +#: src/setup/ncurses/mconf.c:96 +msgid "Exit" +msgstr "Thoát" + +#: src/setup/ncurses/mconf.c:99 +msgid "Up" +msgstr "Lên" + +#: src/setup/ncurses/mconf.c:102 +msgid "Cancel" +msgstr "Thôi" + +#: src/setup/ncurses/mconf.c:221 src/setup/ncurses/mconf.c:408 +msgid "Internal error! (Value invalid?)" +msgstr "Lá»—i ná»™i bá»™ (giá trị sai ?)" + +#: src/setup/ncurses/mconf.c:398 +msgid "Invalid input, expecting floating point value." +msgstr "Dữ liệu nhập sai, mong đợi giá trị chấm Ä‘á»™ng." + +#: src/setup/ncurses/mconf.c:439 +msgid "Invalid input, expecting integer." +msgstr "Dữ liệu nhập sai, mong đợi số nguyên." + +#: src/setup/ncurses/mconf.c:446 +msgid "Value is not in legal range." +msgstr "Giá trị không nằm trong phạm vi được phép." + +#: src/setup/ncurses/mconf.c:512 src/setup/text/conf.c:569 +#, c-format +msgid "Configuration unchanged, no need to save.\n" +msgstr "Cấu hình chÆ°a thay đổi thì không cần lÆ°u lại.\n" + +#: src/setup/ncurses/mconf.c:518 +msgid "Do you wish to save your new configuration?" +msgstr "Bạn có muốn lÆ°u cấu hình má»›i không?" + +#: src/setup/ncurses/mconf.c:532 +#, c-format +msgid "" +"\n" +"End of configuration.\n" +msgstr "" +"\n" +"Kết thúc cấu hình.\n" + +#: src/setup/ncurses/mconf.c:537 +#, c-format +msgid "" +"\n" +"Your configuration changes were NOT saved.\n" +msgstr "" +"\n" +"CHƯA lÆ°u các thay đổi trong cấu hình.\n" + +#: src/setup/gnunet-win-tool.c:52 +msgid "list all network adapters" +msgstr "liệt kê má»i bá»™ tiếp hợp mạng" + +#: src/setup/gnunet-win-tool.c:55 +msgid "install GNUnet as Windows service" +msgstr "cài đặt GNUnet nhÆ° là má»™t dịch vụ Windows" + +#: src/setup/gnunet-win-tool.c:58 +msgid "uninstall GNUnet service" +msgstr "hủy cài đặt dịch vụ GNUnet" + +#: src/setup/gnunet-win-tool.c:61 +msgid "increase the maximum number of TCP/IP connections" +msgstr "tăng sổ tối Ä‘a các kết nối TCP/IP" + +#: src/setup/gnunet-win-tool.c:64 +msgid "display a file's hash value" +msgstr "hiển thị giá trị tổng kiểm của tập tin" + +#: src/setup/gnunet-win-tool.c:125 +#, c-format +msgid "GNUnet service installed successfully.\n" +msgstr "Dịch vụ GNUnet đã được cài đặt.\n" + +#: src/setup/gnunet-win-tool.c:128 src/setup/gnunet-win-tool.c:156 +#, c-format +msgid "This version of Windows doesn't support services.\n" +msgstr "Phiên bản Windows này không há»— trợ dịch vụ.\n" + +#: src/setup/gnunet-win-tool.c:132 src/setup/gnunet-win-tool.c:160 +#, c-format +msgid "Error: can't open Service Control Manager: %s\n" +msgstr "Lá»—i: không thể mở Bá»™ Quản lý Äiá»u khiển Dịch vụ : %s\n" + +#: src/setup/gnunet-win-tool.c:137 +#, c-format +msgid "Error: can't create service: %s\n" +msgstr "Lá»—i: không thể tạo dịch vụ : %s\n" + +#: src/setup/gnunet-win-tool.c:140 src/setup/gnunet-win-tool.c:172 +#, c-format +msgid "Unknown error.\n" +msgstr "Lá»—i không rõ.\n" + +#: src/setup/gnunet-win-tool.c:153 +#, c-format +msgid "Service deleted.\n" +msgstr "Dịch vụ đã bị xoá.\n" + +#: src/setup/gnunet-win-tool.c:165 +#, c-format +msgid "Error: can't access service: %s\n" +msgstr "Lá»—i: không thể truy cập đến dịch vụ : %s\n" + +#: src/setup/gnunet-win-tool.c:169 +#, c-format +msgid "Error: can't delete service: %s\n" +msgstr "Lá»—i: không thể xoá dịch vụ : %s\n" + +#: src/setup/gtk/ngconf.c:389 +msgid "Configuration saved." +msgstr "Cấu hình đã được lÆ°u." + +#: src/setup/gtk/ngconf.c:399 +msgid "Failed to save configuration." +msgstr "Lá»—i lÆ°u cấu hình." + +#: src/setup/gtk/ngconf.c:424 +msgid "Configuration changed. Save?" +msgstr "Cấu hình bị thay đổi. LÆ°u ?" + +#: src/setup/gtk/ngconf.c:437 +msgid "Error saving configuration." +msgstr "Gặp lá»—i khi lÆ°u cấu hình." + +#: src/setup/gtk/wizard_gtk.c:141 +msgid "(unknown connection)" +msgstr "(không rõ kết nối)" + +#: src/setup/gtk/wizard_gtk.c:438 +msgid "Do you want to save the new configuration?" +msgstr "Bạn có muốn lÆ°u cấu hình má»›i này không?" + +#: src/setup/gtk/wizard_gtk.c:470 +msgid "Unable to create user account:" +msgstr "Không thể tạo tài khoản ngÆ°á»i dùng:" + +#: src/setup/gtk/wizard_gtk.c:480 +msgid "Unable to change startup process:" +msgstr "Không thể thay đổi tiến trình khởi chạy:" + +#: src/setup/gtk/wizard_gtk.c:495 +msgid "" +"Running gnunet-update failed.\n" +"This maybe due to insufficient permissions, please check your " +"configuration.\n" +"Finally, run gnunet-update manually." +msgstr "" +"Lá»—i chạy tiến trình cập nhật gnunet-update.\n" +"Äây có thể do không đủ quyá»n, hãy kiểm tra cấu hình.\n" +"Cuối cùng, chạy gnunet-update thủ công." + +#: src/setup/gnunet-setup.c:65 +#, c-format +msgid "Can only set one option per invocation.\n" +msgstr "Chỉ có thể đặt má»™t tùy chá»n trong má»—i cuá»™c gá»i.\n" + +#: src/setup/gnunet-setup.c:73 +#, c-format +msgid "" +"Invalid syntax, argument to 'set' must have the format SECTION:" +"OPTION=VALUE.\n" +msgstr "" +"Cú pháp sai, đối số tá»›i « set » phải theo định dạng:\n" +"PHẦN:TÙY_CHỌN­=GIÃ_TRỊ\n" + +#: src/setup/gnunet-setup.c:87 +#, c-format +msgid "Can only display one option per invocation.\n" +msgstr "Chỉ có thể hiển thị má»™t tùy chá»n trong má»—i cuá»™c gá»i.\n" + +#: src/setup/gnunet-setup.c:94 +#, c-format +msgid "" +"Invalid syntax, argument to 'get' must have the format SECTION:OPTION.\n" +msgstr "" +"Cú pháp sai, đối số tá»›i « get » phải theo định dạng:\n" +"PHẦN:TÙY_CHỌN\n" + +#: src/setup/gnunet-setup.c:108 +msgid "generate configuration for gnunetd, the GNUnet daemon" +msgstr "tạo ra cấu hình cho gnunetd, trình ná»n GNUnet" + +#: src/setup/gnunet-setup.c:111 src/setup/gnunet-setup.c:127 +#: src/server/gnunet-update.c:268 +msgid "print a value from the configuration file to stdout" +msgstr "in ra đầu ra tiêu chuẩn má»™t giá trị từ tập tin cấu hình" + +#: src/setup/gnunet-setup.c:113 src/setup/gnunet-setup.c:129 +msgid "Tool to setup GNUnet." +msgstr "Công cụ để thiết lập GNUnet." + +#: src/setup/gnunet-setup.c:115 src/setup/gnunet-setup.c:131 +msgid "update a value in the configuration file" +msgstr "cập nhật má»™t giá trị trong tập tin cấu hình" + +#: src/setup/gnunet-setup.c:338 +#, c-format +msgid "Too many arguments.\n" +msgstr "Quá nhiá»u đối số.\n" + +#: src/setup/gnunet-setup.c:344 +msgid "No interface specified, using default.\n" +msgstr "ChÆ°a xác định giao diện nên dùng mặc định.\n" + +#: src/setup/gnunet-setup.c:392 +#, c-format +msgid "Configuration file `%s' must be a filename (but is a directory).\n" +msgstr "" + +#: src/setup/gnunet-setup.c:439 +#, c-format +msgid "Undefined option.\n" +msgstr "Tùy chá»n không xác định.\n" + +#: src/setup/gnunet-setup.c:496 +#, c-format +msgid "`%s' is not available.\n" +msgstr "« %s » không sẵn sàng.\n" + +#: src/setup/gnunet-setup.c:516 +#, c-format +msgid "Unknown operation '%s'.\n" +msgstr "Không rõ thao tác « %s ».\n" + +#: src/setup/gnunet-setup.c:517 src/util/getopt/getopt.c:1072 +#, c-format +msgid "Use --help to get a list of options.\n" +msgstr "" +"Hãy sá»­ dụng câu lệnh trợ giúp « --help » để xem danh sách các tùy chá»n.\n" + +#: src/setup/text/conf.c:79 +msgid "yes" +msgstr "có" + +#: src/setup/text/conf.c:80 +msgid "no" +msgstr "không" + +#: src/setup/text/conf.c:106 +#, c-format +msgid "\tEnter yes (%s), no (%s) or help (%s): " +msgstr "\tNhập có (%s), không (%s) hoặc trợ giúp (%s): " + +#: src/setup/text/conf.c:115 +msgid "\tPossible choices:\n" +msgstr "\tLá»±a chá»n có thể:\n" + +#: src/setup/text/conf.c:123 +msgid "\tUse single space prefix to avoid conflicts with hotkeys!\n" +msgstr "\tHãy dùng tiá»n tố má»™t dấu cách để tránh xung Ä‘á»™t vá»›i phím nóng.\n" + +#: src/setup/text/conf.c:125 +#, c-format +msgid "\tEnter string (type '%s' for default value `%s'): " +msgstr "\tNhập chuá»—i (gõ « %s » cho giá trị mặc định « %s »): " + +#: src/setup/text/conf.c:143 +#, c-format +msgid "\t Enter choice (default is %c): " +msgstr "\t Nhập sá»± chá»n (mặc định là %c):" + +#: src/setup/text/conf.c:147 +#, c-format +msgid "\tEnter floating point (type '%s' for default value %f): " +msgstr "\tNhập chấm Ä‘á»™ng (gõ « %s » cho giá trị mặc định %f): " + +#: src/setup/text/conf.c:153 +#, c-format +msgid "" +"\tEnter unsigned integer in interval [%llu,%llu] (type '%s' for default " +"value %llu): " +msgstr "" +"\tNhập số nguyên không có dấu trong khoảng [%llu,%llu] (gõ « %s » cho giá trị " +"mặc định %llu): " + +#: src/setup/text/conf.c:187 +#, c-format +msgid "Yes\n" +msgstr "Có\n" + +#: src/setup/text/conf.c:192 +#, c-format +msgid "No\n" +msgstr "Không\n" + +#: src/setup/text/conf.c:195 src/setup/text/conf.c:236 +#: src/setup/text/conf.c:266 src/setup/text/conf.c:329 +#: src/setup/text/conf.c:387 +#, c-format +msgid "Help\n" +msgstr "Trợ giúp\n" + +#: src/setup/text/conf.c:198 src/setup/text/conf.c:213 +#: src/setup/text/conf.c:276 src/setup/text/conf.c:307 +#: src/setup/text/conf.c:365 +#, c-format +msgid "Abort\n" +msgstr "Hủy bá»\n" + +#: src/setup/text/conf.c:354 src/setup/text/conf.c:416 +#, c-format +msgid "" +"\n" +"Invalid entry, try again (use '?' for help): " +msgstr "" +"\n" +"Dữ liệu nhập sai. Hãy thá»­ lại (dùng « ? » để xem trợ giúp): " + +#: src/setup/text/conf.c:422 +#, c-format +msgid "Unknown kind %x (internal error). Skipping option.\n" +msgstr "Kiểu không rõ %x (lá»—i ná»™i bá»™). Äang bá» qua tùy chá»n.\n" + +#: src/setup/text/conf.c:484 +msgid "\tDescend? (y/n/?) " +msgstr "\tGiảm dần ? (c/k?)" + +#: src/setup/text/conf.c:493 +msgid "Aborted.\n" +msgstr "Bị hủy bá».\n" + +#: src/setup/text/conf.c:506 +msgid "Invalid entry.\n" +msgstr "Dữ liệu nhập không hợp lệ.\n" + +#: src/setup/text/conf.c:524 +#, c-format +msgid "Unknown kind %x (internal error). Aborting.\n" +msgstr "Kiểu không rõ %x (lá»—i ná»™i bá»™). Äang hủy bá».\n" + +#: src/setup/text/conf.c:556 +#, c-format +msgid "You can always press ENTER to keep the current value.\n" +msgstr "Lúc nào bạn cÅ©ng có thể bấm phím Enter để giữ lại giá trị hiện có.\n" + +#: src/setup/text/conf.c:557 +#, c-format +msgid "Use the '%s' key to abort.\n" +msgstr "Dùng phím « %s » để hủy bá».\n" + +#: src/setup/text/conf.c:575 +#, c-format +msgid "" +"Save configuration? Answer 'y' for yes, 'n' for no, 'r' to repeat " +"configuration. " +msgstr "" +"LÆ°u lại cấu hình không?\n" +" • y\t\tcó\n" +" • n\t\tkhông\n" +" • r\t\tlặp lại cấu hình." + +#: src/setup/text/conf.c:590 +#, c-format +msgid "Configuration was unchanged, no need to save.\n" +msgstr "Cấu hình chÆ°a thay đổi thì không cần lÆ°u lại.\n" + +#: src/setup/text/conf.c:599 +#, c-format +msgid "Configuration file `%s' written.\n" +msgstr "Tập tin cấu hình « %s » đã được ghi.\n" + +#: src/setup/lib/tree.c:191 +#, c-format +msgid "" +"Internal error: entry `%s' in section `%s' not found for visibility change!\n" +msgstr "" +"Lá»—i ná»™i bá»™ : mục nhập « %s » trong phần « %s » không tìm thấy để thay đổi tình " +"trạng hiển rõ.\n" + +#: src/setup/lib/wizard_util.c:126 +msgid "Can't open Service Control Manager" +msgstr "Không thể mở Bá»™ Quản lý Äiá»u khiển Dịch vụ" + +#: src/setup/lib/wizard_util.c:132 +msgid "Can't create service" +msgstr "Không thể tạo dịch vụ" + +#: src/setup/lib/wizard_util.c:136 +msgid "Error changing the permissions of the GNUnet directory" +msgstr "Gặp lá»—i khi thay đổi quyá»n hạn của thÆ° mục GNUnet" + +#: src/setup/lib/wizard_util.c:141 +msgid "Cannot write to the registry" +msgstr "Không thể ghi vào sổ đăng ký" + +#: src/setup/lib/wizard_util.c:144 +msgid "Can't access the service" +msgstr "Không thể truy cập đến dịch vụ" + +#: src/setup/lib/wizard_util.c:147 +msgid "Can't delete the service" +msgstr "Không thể xoá dịch vụ" + +#: src/setup/lib/wizard_util.c:150 +msgid "Unknown error" +msgstr "Lá»—i không rõ" + +#: src/setup/lib/wizard_util.c:186 +msgid "This version of Windows does not support multiple users." +msgstr "Phiên bản Windows này không có há»— trợ Ä‘a ngÆ°á»i dùng." + +#: src/setup/lib/wizard_util.c:190 +msgid "Error creating user" +msgstr "Gặp lá»—i khi tạo ngÆ°á»i dùng" + +#: src/setup/lib/wizard_util.c:194 +msgid "Error accessing local security policy" +msgstr "Gặp lá»—i khi truy cập đến chính sách bảo mật cục bá»™" + +#: src/setup/lib/wizard_util.c:199 +msgid "Error granting service right to user" +msgstr "Gặp lá»—i khi cấp quyá»n dịch vụ cho ngÆ°á»i dùng" + +#: src/setup/lib/wizard_util.c:204 +msgid "Unknown error while creating a new user" +msgstr "Gặp lá»—i không rõ trong khi tạo má»™t ngÆ°á»i dùng má»›i" + +#: src/setup/lib/gns.c:297 +#, c-format +msgid "" +"Configuration does not satisfy constraints of configuration specification " +"file `%s'!\n" +msgstr "" +"Cấu hình không thá»a mãn các ràng buá»™c của tập tin đặc tả cấu hình « %s ».\n" + +#: src/util/disk/storage.c:172 +#, c-format +msgid "`%s' failed for drive `%s': %u\n" +msgstr "« %s » thất bại cho ổ Ä‘Ä©a « %s »: %u\n" + +#: src/util/disk/storage.c:524 +#, c-format +msgid "Expected `%s' to be a directory!\n" +msgstr "Mong đợi « %s » là má»™t thÆ° mục.\n" + +#: src/util/error/error.c:152 +#, c-format +msgid "Message `%.*s' repeated %u times in the last %llus\n" +msgstr "Thông Ä‘iệp « %.*s » đã lặp lại %u lần trong %llu giây trÆ°á»›c\n" + +#: src/util/error/error.c:254 +#, c-format +msgid "" +"\n" +"Press any key to continue\n" +msgstr "" +"\n" +"Hãy bấm bất cứ phím nào để tiếp tục.\n" + +#: src/util/error/error.c:336 src/util/error/error.c:371 +msgid "DEBUG" +msgstr "Gá»  Lá»–I" + +#: src/util/error/error.c:338 src/util/error/error.c:373 +msgid "STATUS" +msgstr "TRẠNG THÃI" + +#: src/util/error/error.c:340 src/util/error/error.c:377 +msgid "WARNING" +msgstr "CẢNH BÃO" + +#: src/util/error/error.c:342 src/util/error/error.c:379 +msgid "ERROR" +msgstr "Lá»–I" + +#: src/util/error/error.c:344 src/util/error/error.c:381 +msgid "FATAL" +msgstr "NGHIÊM TRỌNG" + +#: src/util/error/error.c:346 src/util/error/error.c:383 +msgid "USER" +msgstr "NGƯỜI DÙNG" + +#: src/util/error/error.c:348 src/util/error/error.c:385 +msgid "ADMIN" +msgstr "QUẢN TRỊ" + +#: src/util/error/error.c:350 src/util/error/error.c:387 +msgid "DEVELOPER" +msgstr "NHÀ PHÃT TRIỂN" + +#: src/util/error/error.c:352 src/util/error/error.c:389 +msgid "REQUEST" +msgstr "YÊU CẦU" + +#: src/util/error/error.c:354 src/util/error/error.c:391 +msgid "BULK" +msgstr "HÀNG LOẠT" + +#: src/util/error/error.c:356 src/util/error/error.c:393 +msgid "IMMEDIATE" +msgstr "NGAY" + +#: src/util/error/error.c:358 +msgid "ALL" +msgstr "TẤT CẢ" + +#: src/util/error/error.c:375 +msgid "INFO" +msgstr "TIN" + +#: src/util/error/error.c:394 +msgid "NOTHING" +msgstr "KHÔNG GÃŒ" + +#: src/util/network_client/tcpio.c:98 src/util/network_client/tcpio.c:154 +#, fuzzy +msgid "Could not find valid value for HOST in section NETWORK.\n" +msgstr "Không tìm thấy giá trị đúng cho MÃY trong phần MẠNG." + +#: src/util/network_client/tcpio.c:123 +#, fuzzy, c-format +msgid "Syntax error in configuration entry HOST in section NETWORK: `%s'\n" +msgstr "Gặp lá»—i cú pháp trong mục nhập cấu hình MÃY trong phần MẠNG: « %s »" + +#: src/util/network_client/tcpio.c:335 +#, c-format +msgid "Error connecting to %s:%u. Is the daemon running?\n" +msgstr "Gặp lá»—i khi kết nối tá»›i %s:%u. Trình ná»n Ä‘ang chạy không?\n" + +#: src/util/network_client/tcpio.c:398 +#, c-format +msgid "Cannot connect to %s:%u: %s\n" +msgstr "Không thể kết nối tá»›i %s:%u: %s\n" + +#: src/util/network_client/tcpio.c:636 +msgid "Reading result from gnunetd failed, reply invalid!\n" +msgstr "Lá»—i Ä‘á»c kết quả từ gnunetd, đáp ứng không hợp lệ.\n" + +#: src/util/getopt/setoption.c:59 +#, c-format +msgid "" +"Setting option `%s' in section `%s' to `%s' when processing command line " +"option `%s' was denied.\n" +msgstr "" +"Bị từ chối đặt tùy chá»n « %s » trong phần « %s » thành « %s » khi xá»­ lý tùy " +"chá»n dòng lệnh « %s ».\n" + +#: src/util/getopt/setoption.c:138 src/util/getopt/setoption.c:155 +#, c-format +msgid "You must pass a number to the `%s' option.\n" +msgstr "Phải gá»­i má»™t con số cho tùy chá»n « %s ».\n" + +#: src/util/getopt/printhelp.c:49 +#, c-format +msgid "" +"Arguments mandatory for long options are also mandatory for short options.\n" +msgstr "" +"Má»i đối số bắt buá»™c phải sá»­ dụng vá»›i tùy chá»n dài cÅ©ng bắt buá»™c vá»›i tùy chá»n " +"ngắn.\n" + +#: src/util/getopt/getopt.c:684 +#, c-format +msgid "%s: option `%s' is ambiguous\n" +msgstr "%s: tùy chá»n « %s » là mÆ¡ hồ\n" + +#: src/util/getopt/getopt.c:710 +#, c-format +msgid "%s: option `--%s' does not allow an argument\n" +msgstr "%s: tùy chá»n « --%s » không cho phép đối số\n" + +#: src/util/getopt/getopt.c:716 +#, c-format +msgid "%s: option `%c%s' does not allow an argument\n" +msgstr "%s: tùy chá»n « %c%s » không cho phép đối số\n" + +#: src/util/getopt/getopt.c:737 src/util/getopt/getopt.c:909 +#, c-format +msgid "%s: option `%s' requires an argument\n" +msgstr "%s: tùy chá»n « %s » cần thiết đối số\n" + +#: src/util/getopt/getopt.c:767 +#, c-format +msgid "%s: unrecognized option `--%s'\n" +msgstr "%s: không nhận ra tùy chá»n « --%s »\n" + +#: src/util/getopt/getopt.c:771 +#, c-format +msgid "%s: unrecognized option `%c%s'\n" +msgstr "%s: không nhận ra tùy chá»n « %c%s »\n" + +#: src/util/getopt/getopt.c:797 +#, c-format +msgid "%s: illegal option -- %c\n" +msgstr "%s: tùy chá»n không được phép -- %c\n" + +#: src/util/getopt/getopt.c:799 +#, c-format +msgid "%s: invalid option -- %c\n" +msgstr "%s: tùy chá»n không hợp lệ -- %c\n" + +#: src/util/getopt/getopt.c:828 src/util/getopt/getopt.c:958 +#, c-format +msgid "%s: option requires an argument -- %c\n" +msgstr "%s: tùy chá»n cần thiết đối số -- %c\n" + +#: src/util/getopt/getopt.c:876 +#, c-format +msgid "%s: option `-W %s' is ambiguous\n" +msgstr "%s: tùy chá»n « -W %s » là mÆ¡ hồ\n" + +#: src/util/getopt/getopt.c:894 +#, c-format +msgid "%s: option `-W %s' does not allow an argument\n" +msgstr "%s: tùy chá»n « -W %s » không cho phép đối số\n" + +#: src/util/network/ip.c:96 src/util/network/ip.c:160 src/transports/ip.c:260 +#, c-format +msgid "No interface specified in section `%s' under `%s'!\n" +msgstr "" +"Không có giao diện mạng được xác định trong cấu hình phần « %s » dÆ°á»›i « %s ».\n" + +#: src/util/network/ip.c:127 src/util/network/ip.c:246 src/transports/ip.c:291 +#, c-format +msgid "Could not obtain IP for interface `%s' using `%s'.\n" +msgstr "Không thể lấy địa chỉ IP cho giao diện « %s », dùng « %s ».\n" + +#: src/util/network/ip.c:216 +#, c-format +msgid "" +"Could not find interface `%s' using `%s', trying to find another interface.\n" +msgstr "" +"Không tìm thấy giao diện « %s » trong « %s », Ä‘ang thá»­ tìm giao diện khác.\n" + +#: src/util/network/ip.c:295 +#, c-format +msgid "Could not find an IP address for interface `%s'.\n" +msgstr "Không tìm thấy má»™t địa chỉ IP cho giao diện « %s ».\n" + +#: src/util/network/ip.c:306 +#, c-format +msgid "" +"There is more than one IP address specified for interface `%s'.\n" +"GNUnet will use %s.\n" +msgstr "" +"Có vài địa chỉ IP chỉ ra cho giao diện « %s ».\n" +"GNUnet sẽ dùng %s.\n" + +#: src/util/network/ip.c:330 +#, c-format +msgid "Could not resolve `%s' to determine our IP address: %s\n" +msgstr "Không thể giải quyết « %s » để quyết định địa chỉ IP của chúng ta: %s\n" + +#: src/util/network/ip.c:363 +#, c-format +msgid "GNUnet now uses the IP address %s.\n" +msgstr "GNUnet bây giá» sá»­ dụng địa chỉ IP %s.\n" + +#: src/util/network/ipcheck.c:106 src/util/network/ipcheck.c:136 +#: src/util/network/ipcheck.c:186 src/util/network/ipcheck.c:211 +#: src/util/network/ipcheck.c:219 +#, c-format +msgid "Invalid format for IP: `%s'\n" +msgstr "Äịa chỉ IP định dạng sai: %s\n" + +#: src/util/network/ipcheck.c:167 +#, c-format +msgid "Invalid network notation ('/%d' is not legal in IPv4 CIDR)." +msgstr "Ký hiệu mạng sai (« /%d » không hợp lệ trong CIDR IPv4)." + +#: src/util/network/ipcheck.c:269 +#, c-format +msgid "Invalid network notation (does not end with ';': `%s')\n" +msgstr "Ký hiệu mạng sai (không kết thúc vá»›i « ; »: « %s »)\n" + +#: src/util/network/ipcheck.c:306 +#, fuzzy, c-format +msgid "Wrong format `%s' for netmask\n" +msgstr "Mặt nạ mạng có định dạng sai « %s »: %s\n" + +#: src/util/network/ipcheck.c:338 +#, fuzzy, c-format +msgid "Wrong format `%s' for network\n" +msgstr "Mạng có định dạng sai « %s »: %s\n" + +#: src/util/network/dns.c:472 +#, c-format +msgid "Could not resolve `%s' (%s): %s\n" +msgstr "Không thể giải quyết « %s » (%s): %s\n" + +#: src/util/network/dns.c:523 src/util/network/dns.c:591 +#, c-format +msgid "Could not find IP of host `%s': %s\n" +msgstr "Không tìm thấy địa chỉ IP của máy « %s »: %s\n" + +#: src/util/network/select.c:310 +msgid "Received malformed message (too small) from connection. Closing.\n" +msgstr "Nhận được thông báo bị há»ng (quá nhá») từ kết nối. Äang đóng.\n" + +#: src/util/network/select.c:495 +#, c-format +msgid "select listen socket for `%s' not valid!\n" +msgstr "sai chá»n ổ cắm lắng nghe cho « %s »\n" + +#: src/util/config/config.c:296 +#, c-format +msgid "Syntax error in configuration file `%s' at line %d.\n" +msgstr "Gặp lá»—i cú pháp trong tập tin cấu hình « %s » tại dòng %d.\n" + +#: src/util/config/config.c:592 +#, c-format +msgid "" +"Configuration value '%llu' for '%s' in section '%s' is out of legal bounds [%" +"llu,%llu]\n" +msgstr "" +"Giá tri cấu hình « %llu » cho « %s » trong phần « %s » ở ngoại phạm vi được " +"phép [%llu,%llu]\n" + +#: src/util/config/config.c:602 +#, c-format +msgid "Configuration value '%s' for '%s' in section '%s' should be a number\n" +msgstr "Giá trị cấu hình « %s » cho « %s » trong phần « %s » nên là con số\n" + +#: src/util/config/config.c:688 +#, c-format +msgid "" +"Configuration value '%s' for '%s' in section '%s' is not in set of legal " +"choices\n" +msgstr "" +"Giá trị cấu hình « %s » cho « %s » trong phần « %s » không phải nằm trong tập " +"hợp các sá»± chá»n được phép\n" + +#: src/util/crypto/locking_gcrypt.c:80 +#, c-format +msgid "libgcrypt has not the expected version (version %s is required).\n" +msgstr "libgcrypt không có phiên bản mong đợi (yêu cầu phiên bản %s).\n" + +#: src/util/crypto/symcipher_gcrypt.c:46 src/util/crypto/symcipher_gcrypt.c:53 +#: src/util/crypto/hostkey_gcrypt.c:64 src/util/crypto/hostkey_gcrypt.c:71 +#: src/util/loggers/file.c:271 src/util/loggers/file.c:289 +#: src/applications/sqstore_sqlite/sqlite.c:45 +#: src/applications/sqstore_sqlite/sqlite.c:52 +#: src/applications/kvstore_sqlite/kv_sqlite.c:44 +#: src/applications/kvstore_sqlite/kv_sqlite.c:51 +#: src/applications/kvstore_mysql/kv_mysql.c:44 +#: src/applications/kvstore_mysql/kv_mysql.c:51 +#: src/applications/dstore_sqlite/dstore.c:94 +#: src/applications/dstore_sqlite/dstore.c:101 +#: src/applications/dstore_sqlite/dstore.c:222 +#: src/applications/dstore_sqlite/dstore.c:259 +#: src/applications/dstore_sqlite/dstore.c:285 +#: src/applications/dstore_sqlite/dstore.c:345 +#: src/applications/dstore_sqlite/dstore.c:366 +#: src/applications/dstore_sqlite/dstore.c:378 +#: src/applications/dstore_sqlite/dstore.c:407 +#: src/applications/dstore_sqlite/dstore.c:511 +#: src/applications/dstore_sqlite/dstore.c:555 +#: src/include/gnunet_util_error.h:249 src/include/gnunet_util_error.h:256 +#: src/include/gnunet_util_error.h:263 +#, c-format +msgid "`%s' failed at %s:%d with error: %s\n" +msgstr "« %s » bị lá»—i tại %s:%d vá»›i lá»—i: %s\n" + +#: src/util/crypto/hostkey_gcrypt.c:907 +#, c-format +msgid "RSA signature verification failed at %s:%d: %s\n" +msgstr "Lá»—i thẩm tra chữ ký RSA tại %s:%d: %s\n" + +#: src/util/os/user.c:108 src/util/os/user.c:125 +#, c-format +msgid "`%s' returned with error code %u" +msgstr "« %s » trả lại vá»›i mã lá»—i %u" + +#: src/util/os/user.c:155 src/util/os/user.c:200 +#, c-format +msgid "Cannot obtain information about user `%s': %s\n" +msgstr "Không thể lấy thông tin vá» ngÆ°á»i dùng « %s »: %s\n" + +#: src/util/os/user.c:156 +msgid "No such user" +msgstr "Không có ngÆ°á»i dùng nhÆ° vậy" + +#: src/util/os/user.c:171 +#, c-format +msgid "Cannot change user/group to `%s': %s\n" +msgstr "Không thể thay đổi ngÆ°á»i dùng/nhóm thành « %s »: %s\n" + +#: src/util/os/semaphore.c:227 +#, c-format +msgid "Can't create semaphore: %i" +msgstr "Không thể tạo cá» hiệu : %i" + +#: src/util/os/cpustatus.c:464 +msgid "Cannot query the CPU usage (Windows NT).\n" +msgstr "Không há»i được sá»± sá»­ dụng CPU (Windows NT).\n" + +#: src/util/os/cpustatus.c:487 +msgid "Cannot query the CPU usage (Win 9x)\n" +msgstr "Không há»i được sá»± sá»­ dụng CPU (Windows 9x).\n" + +#: src/util/os/dso.c:59 +#, c-format +msgid "Initialization of plugin mechanism failed: %s!\n" +msgstr "Lá»—i sÆ¡ khởi cÆ¡ chế phần bổ sung: %s\n" + +#: src/util/os/dso.c:120 +#, c-format +msgid "`%s' failed for library `%s' with error: %s\n" +msgstr "« %s » thất bại cho thÆ° viện « %s » vá»›i lá»—i: %s\n" + +#: src/util/os/dso.c:162 +#, c-format +msgid "`%s' failed to resolve method '%s' with error: %s\n" +msgstr "« %s » không giải quyết được phÆ°Æ¡ng pháp « %s », vá»›i lá»—i: %s\n" + +#: src/util/os/statuscalls.c:197 src/util/os/statuscalls.c:342 +#, c-format +msgid "Failed to parse interface data from `%s'.\n" +msgstr "Lá»—i phân tích dữ liệu giao diện từ « %s ».\n" + +#: src/util/os/statuscalls.c:390 src/util/os/statuscalls.c:400 +#, c-format +msgid "" +"No network interfaces defined in configuration section `%s' under `%s'!\n" +msgstr "" +"Không có giao diện mạng được xác định trong cấu hình phần « %s » dÆ°á»›i « %s ».\n" + +#: src/util/os/osconfig.c:153 +msgid "Setting open descriptor limit not supported.\n" +msgstr "Không há»— trợ chức năng đặt giá»›i hạn bá»™ mô tả còn mở.\n" + +#: src/util/os/osconfig.c:463 src/util/os/osconfig.c:492 +#, c-format +msgid "Command `%s' failed with error code %u\n" +msgstr "Câu lệnh « %s » đã thất bại vá»›i mã lá»—i %u\n" + +#: src/util/os/priority.c:78 +#, c-format +msgid "Invalid process priority `%s'\n" +msgstr "Mức Æ°u tiên tiến trình không hợp lê « %s ».\n" + +#: src/util/threads/semaphore.c:168 src/util/threads/pthread.c:157 +#: src/util/threads/mutex.c:146 +#, c-format +msgid "Real-time delay violation (%llu ms) at %s:%u\n" +msgstr "Xâm phạm khoảng đợi thá»i gian thá»±c (%llu miligiây) tại %s:%u\n" + +#: src/util/threads/pthread.c:169 src/util/threads/pthread.c:176 +#: src/util/threads/pthread.c:182 src/util/threads/pthread.c:276 +#, c-format +msgid "`%s' failed with error code %s: %s\n" +msgstr "« %s » thất bại vá»›i mã lá»—i %s: %s\n" + +#: src/util/threads/pthread.c:188 src/util/threads/pthread.c:286 +#, c-format +msgid "`%s' failed with error code %d: %s\n" +msgstr "« %s » thất bại vá»›i mã lá»—i %d: %s\n" + +#: src/util/threads/mutex.c:155 src/util/threads/mutex.c:201 +#, c-format +msgid "Invalid argument for `%s'.\n" +msgstr "Äối số không hợp lệ cho « %s ».\n" + +#: src/util/threads/mutex.c:160 +#, c-format +msgid "Deadlock due to `%s'.\n" +msgstr "Bế tắc do « %s ».\n" + +#: src/util/threads/mutex.c:187 +#, c-format +msgid "Lock acquired for too long (%llu ms) at %s:%u\n" +msgstr "Khoá đặt được quá lâu (%llu miligiây) tại %s:%u\n" + +#: src/util/threads/mutex.c:207 +#, c-format +msgid "Permission denied for `%s'.\n" +msgstr "Không đủ quyá»n cho « %s ».\n" + +#: src/util/boot/startup.c:259 +#, c-format +msgid "Failed to run %s: %s %d\n" +msgstr "Lá»—i chạy %s: %s %d\n" + +#: src/util/string/string.c:55 +msgid "ms" +msgstr "mg" + +#: src/util/string/string.c:61 +msgid "s" +msgstr "g" + +#: src/util/string/string.c:65 +msgid "m" +msgstr "p" + +#: src/util/string/string.c:69 +msgid "h" +msgstr "g" + +#: src/util/string/string.c:73 +msgid " days" +msgstr " ngày" + +#: src/util/string/string.c:89 +msgid "b" +msgstr "b" + +#: src/util/string/string.c:95 +msgid "KiB" +msgstr "KiB" + +#: src/util/string/string.c:99 +msgid "MiB" +msgstr "MiB" + +#: src/util/string/string.c:103 +msgid "GiB" +msgstr "GiB" + +#: src/util/string/string.c:107 +msgid "TiB" +msgstr "TiB" + +#: src/util/string/string.c:226 +msgid "Failed to expand `$HOME': environment variable `HOME' not set" +msgstr "Lá»—i mở rá»™ng biến môi trÆ°á»ng « $HOME »: chÆ°a đặt biến môi trÆ°á»ng « HOME »" + +#: src/util/loggers/file.c:229 +#, c-format +msgid "Failed to open log-file `%s': %s\n" +msgstr "Lá»—i mở tập tin ghi sá»± kiện « %s »: %s\n" + +#: src/util/loggers/file.c:250 +msgid "GNUnet error log" +msgstr "Bản ghi lá»—i GNUnet" + +#: src/util/loggers/memory.c:72 +msgid "Out of memory (for logging)\n" +msgstr "Tràn bá»™ nhá»› (để ghi sá»± kiện)\n" + +#: src/util/pseudonym/names.c:79 +msgid "no-name" +msgstr "không-tên" + +#: src/applications/datastore/datastore.c:183 +#: src/applications/datastore/datastore.c:199 +#, c-format +msgid "Availability test failed for `%s' at %s:%d.\n" +msgstr "Lá»—i kiểm tra tình trạng sẵn sàng cho « %s » tại %s:%d.\n" + +#: src/applications/datastore/datastore.c:401 +msgid "# requests filtered by bloom filter" +msgstr "# các yêu cầu được lá»c theo bá»™ lá»c bloom" + +#: src/applications/datastore/datastore.c:403 +msgid "# bloom filter false positives" +msgstr "# các dÆ°Æ¡ng giả của bá»™ lá»c bloom" + +#: src/applications/datastore/datastore.c:406 +msgid "# bytes allowed in datastore" +msgstr "# các byte được phép trong kho dữ liệu" + +#: src/applications/datastore/datastore.c:423 +msgid "Failed to load state service. Trying to do without.\n" +msgstr "Lá»—i nạp dịch vụ tình trạng. Không có nhÆ°ng vẫn Ä‘ang thá»­ tiếp tục.\n" + +#: src/applications/datastore/datastore.c:529 +#, c-format +msgid "Datastore conversion at approximately %u%%\n" +msgstr "Chuyển đổi kho dữ liệu theo xấp xỉ %u%%\n" + +#: src/applications/datastore/datastore.c:576 +#, c-format +msgid "Starting datastore conversion (this may take a while).\n" +msgstr "Äang bắt đầu chuyển đổi kho dữ liệu (có thể hÆ¡i lâu).\n" + +#: src/applications/datastore/datastore.c:584 +#, c-format +msgid "Completed datastore conversion.\n" +msgstr "Hoàn tất chuyển đổi kho dữ liệu.\n" + +#: src/applications/datastore/datastore.c:592 +msgid "Failed to load sqstore service. Check your configuration!\n" +msgstr "Lá»—i nạp dịch vụ sqstore. Hãy kiểm tra lại cấu hình.\n" + +#: src/applications/rpc/rpc.c:339 +#, c-format +msgid "" +"%s:%d - RPC %s:%p could not be registered: another callback is already using " +"this name (%p)\n" +msgstr "" +"%s:%d - RPC %s:%p không đăng ký được: má»™t cuá»™c gá»i ngược lại khác Ä‘ang dùng " +"tên này (%p)\n" + +#: src/applications/rpc/rpc.c:398 +#, c-format +msgid "%s:%d - async RPC %s:%p could not be unregistered: not found\n" +msgstr "" +"%s:%d - RPC không đồng bá»™ %s:%p không hủy đăng ký được: không tìm thấy\n" + +#: src/applications/rpc/rpc.c:951 +#, c-format +msgid "`%s' registering handlers %d %d %d\n" +msgstr "« %s » Ä‘ang đăng ký các trình Ä‘iá»u khiển %d %d %d\n" + +#: src/applications/rpc/rpc.c:972 +#, c-format +msgid "Failed to initialize `%s' service.\n" +msgstr "Lá»—i sÆ¡ khởi dịch vụ « %s ».\n" + +#: src/applications/tbench/tbenchtest.c:53 +#, c-format +msgid "Using %u messages of size %u for %u times.\n" +msgstr "Sá»­ dụng %u thông báo vá»›i kích cỡ %u trong %u lần.\n" + +#: src/applications/tbench/tbenchtest.c:85 +#, c-format +msgid "Times: max %16llu min %16llu mean %12.3f variance %12.3f\n" +msgstr "Thá»i gian: đại %16llu tiểu %16llu t.bình %12.3f ph.sai %12.3f\n" + +#: src/applications/tbench/tbenchtest.c:89 +#, c-format +msgid "Loss: max %16u min %16u mean %12.3f variance %12.3f\n" +msgstr "Mất: đại %16u tiểu %16u t.bình %12.3f ph.sai %12.3f\n" + +#: src/applications/tbench/tbenchtest.c:95 +#, c-format +msgid "" +"\n" +"Failed to receive reply from gnunetd.\n" +msgstr "" +"\n" +"Không nhận được đáp ứng từ gnunetd.\n" + +#: src/applications/tbench/tbenchtest.c:149 +#, c-format +msgid "Running benchmark...\n" +msgstr "Äang chạy tiến trình kiểm chuẩn...\n" + +#: src/applications/tbench/tbench.c:422 +msgid "allows profiling of direct peer-to-peer connections" +msgstr "cho phép Ä‘o hiệu năng sá»­ dụng của kết nối đồng đẳng trá»±c tiếp" + +#: src/applications/tbench/gnunet-tbench.c:63 +#: src/applications/tracekit/gnunet-tracekit.c:302 +msgid "Start GNUnet transport benchmarking tool." +msgstr "Khởi chạy công cụ kiểm chuẩn truyá»n tải của GNUnet." + +#: src/applications/tbench/gnunet-tbench.c:65 +msgid "output in gnuplot format" +msgstr "kết xuất theo định dạng gnuplot" + +#: src/applications/tbench/gnunet-tbench.c:69 +msgid "number of iterations" +msgstr "số lần lặp lại" + +#: src/applications/tbench/gnunet-tbench.c:73 +msgid "number of messages to use per iteration" +msgstr "số tin nhắn cần dùng má»—i lần lặp" + +#: src/applications/tbench/gnunet-tbench.c:76 +msgid "receiver host identifier (ENC file name)" +msgstr "đồ nhận diện máy nhận (tên tập tin mã hoá)" + +#: src/applications/tbench/gnunet-tbench.c:79 +msgid "message size" +msgstr "kích cỡ tin nhắn" + +#: src/applications/tbench/gnunet-tbench.c:82 +msgid "sleep for SPACE ms after each a message block" +msgstr "ngủ KHOẢNG miligiây sau má»—i khối tin nhắn" + +#: src/applications/tbench/gnunet-tbench.c:85 +msgid "time to wait for the completion of an iteration (in ms)" +msgstr "thá»i gian chá» sá»± hoàn thành của má»™t lần lặp (theo miligiây)" + +#: src/applications/tbench/gnunet-tbench.c:90 +msgid "number of messages in a message block" +msgstr "số tin nhắn trong má»™t khối tin nhắn" + +#: src/applications/tbench/gnunet-tbench.c:126 +#: src/applications/tracekit/gnunet-tracekit.c:352 +#: src/applications/tracekit/tracekittest.c:133 +#: src/applications/template/gnunet-template.c:95 +#: src/applications/stats/gnunet-stats.c:121 +#: src/applications/vpn/gnunet-vpn.c:154 +#, c-format +msgid "Error establishing connection with gnunetd.\n" +msgstr "Lá»—i thiết lập kết nối đến gnunetd.\n" + +#: src/applications/tbench/gnunet-tbench.c:142 +#, c-format +msgid "You must specify a receiver!\n" +msgstr "Phải ghi rõ má»™t máy nhận.\n" + +#: src/applications/tbench/gnunet-tbench.c:152 +#, c-format +msgid "Invalid receiver peer ID specified (`%s' is not valid name).\n" +msgstr "Chỉ ra mã số đồng đẳng nhận sai (« %s » không phải là má»™t tên đúng).\n" + +#: src/applications/tbench/gnunet-tbench.c:189 +#, c-format +msgid "Time:\n" +msgstr "Thá»i gian:\n" + +#: src/applications/tbench/gnunet-tbench.c:190 +#, c-format +msgid "\tmax %llums\n" +msgstr "\tđại %llu mg\n" + +#: src/applications/tbench/gnunet-tbench.c:191 +#, c-format +msgid "\tmin %llums\n" +msgstr "\ttiểu %llu mg\n" + +#: src/applications/tbench/gnunet-tbench.c:192 +#, c-format +msgid "\tmean %8.4fms\n" +msgstr "\ttrung bình %8.4f mg\n" + +#: src/applications/tbench/gnunet-tbench.c:193 +#, c-format +msgid "\tvariance %8.4fms\n" +msgstr "\tphÆ°Æ¡ng sai %8.4f mg\n" + +#: src/applications/tbench/gnunet-tbench.c:195 +#, c-format +msgid "Loss:\n" +msgstr "Mất:\n" + +#: src/applications/tbench/gnunet-tbench.c:196 +#, c-format +msgid "\tmax %u\n" +msgstr "\tđại %u\n" + +#: src/applications/tbench/gnunet-tbench.c:197 +#, c-format +msgid "\tmin %u\n" +msgstr "\ttiểu %u\n" + +#: src/applications/tbench/gnunet-tbench.c:198 +#, c-format +msgid "\tmean %8.4f\n" +msgstr "\tt.bình %8.4f\n" + +#: src/applications/tbench/gnunet-tbench.c:199 +#, c-format +msgid "\tvariance %8.4f\n" +msgstr "\tph.sai %8.4f\n" + +#: src/applications/tbench/gnunet-tbench.c:205 +#, c-format +msgid "Output format not known, this should not happen.\n" +msgstr "Äịnh dạng kết xuất không rõ, Ä‘iá»u này không nên xảy ra.\n" + +#: src/applications/tbench/gnunet-tbench.c:211 +#, c-format +msgid "" +"\n" +"Did not receive the message from gnunetd. Is gnunetd running?\n" +msgstr "" +"\n" +"Không nhận được thông báo từ gnunetd. Trình ná»n gnunetd Ä‘ang chạy không?\n" + +#: src/applications/traffic/traffic.c:454 +#, c-format +msgid "# bytes transmitted of type %d" +msgstr "# các byte kiểu %d được gá»­i " + +#: src/applications/traffic/traffic.c:470 +#, c-format +msgid "# bytes received of type %d" +msgstr "# các byte kiểu %d được nhận" + +#: src/applications/traffic/traffic.c:489 +#, c-format +msgid "# bytes received in plaintext of type %d" +msgstr "# các byte nhập thô kiểu %d được nhận" + +#: src/applications/traffic/traffic.c:652 +msgid "tracks bandwidth utilization by gnunetd" +msgstr "theo dõi gnunetd sá»­ dụng dải thông" + +#: src/applications/sqstore_sqlite/sqlite.c:218 +#, c-format +msgid "Unable to initialize SQLite: %s.\n" +msgstr "Không thể sÆ¡ khởi SQLite: %s.\n" + +#: src/applications/sqstore_sqlite/sqlite.c:434 +#: src/applications/sqstore_sqlite/sqlite.c:469 +#, c-format +msgid "Invalid data in %s. Trying to fix (by deletion).\n" +msgstr "Dữ liệu sai trong %s. Äang thá»­ sá»­a chữa (bằng cách xoá).\n" + +#: src/applications/sqstore_sqlite/sqlite.c:435 +#: src/applications/sqstore_sqlite/sqlite.c:470 +msgid "sqlite datastore" +msgstr "kho dữ liệu sqlite" + +#: src/applications/sqstore_sqlite/sqlite.c:1474 +#: src/applications/sqstore_mysql/mysql.c:1078 +msgid "# bytes in datastore" +msgstr "# các byte trong kho dữ liệu" + +#: src/applications/sqstore_sqlite/sqlite.c:1476 +#, fuzzy +msgid "# bytes allocated by SQLite" +msgstr "# các byte được phép trong kho dữ liệu" + +#: src/applications/sqstore_mysql/mysql.c:1085 +#: src/applications/sqstore_mysql/mysql.c:1160 +msgid "" +"Failed to load MySQL database module. Check that MySQL is running and " +"configured properly!\n" +msgstr "" +"Lá»—i nạp mô-Ä‘un cÆ¡ sở dữ liệu MySQL. Hãy kiểm tra lại MySQL Ä‘ang chạy và có " +"cấu hình đúng.\n" + +#: src/applications/tracekit/gnunet-tracekit.c:104 +#, c-format +msgid "`%s' connected to `%s'.\n" +msgstr "« %s » được kết nối tá»›i « %s ».\n" + +#: src/applications/tracekit/gnunet-tracekit.c:110 +#: src/applications/tracekit/tracekittest.c:67 +#, c-format +msgid "`%s' is not connected to any peer.\n" +msgstr "« %s » không phải được kết nối tá»›i đồng đẳng nào.\n" + +#: src/applications/tracekit/gnunet-tracekit.c:295 +msgid "probe network to the given DEPTH" +msgstr "dò mạng tá»›i Ä‘á»™ sâu SÂU Ä‘Æ°a ra" + +#: src/applications/tracekit/gnunet-tracekit.c:299 +msgid "" +"specify output format; 0 for human readable output, 1 for dot, 2 for vcg" +msgstr "" +"chỉ ra định dạng kết quả;\n" +" • 0\t\tkết xuất cho ngÆ°á»i Ä‘á»c được\n" +" • 1\t\tdấu chấm\n" +" • 2\t\tvcg" + +#: src/applications/tracekit/gnunet-tracekit.c:306 +msgid "use PRIORITY for the priority of the trace request" +msgstr "dùng ƯU_TIÊN làm Æ°u tiên của yêu cầu tìm Ä‘Æ°á»ng" + +#: src/applications/tracekit/gnunet-tracekit.c:310 +msgid "wait DELAY seconds for replies" +msgstr "đợi đáp ứng TRỄ giây" + +#: src/applications/tracekit/gnunet-tracekit.c:344 +#, c-format +msgid "" +"Format specification invalid. Use 0 for user-readable, 1 for dot, 2 for " +"vcg.\n" +msgstr "" +"Äặt tả định dạng sai. Dùng:\n" +" • 0\t\tkết xuất cho ngÆ°á»i Ä‘á»c được\n" +" • 1\t\tdấu chấm\n" +" • 2\t\tvcg\n" + +#: src/applications/tracekit/tracekittest.c:60 +#, c-format +msgid "`%.*s' connected to `%.*s'.\n" +msgstr "« %.*s » được kết nối tá»›i « %.*s ».\n" + +#: src/applications/tracekit/tracekit.c:440 +msgid "allows mapping of the network topology" +msgstr "cho phép ánh xạ địa hình của mạng" + +#: src/applications/advertising/advertising_test.c:47 +#: src/applications/hostlist/hostlisttest.c:40 +#: src/applications/session/sessiontest.c:40 +#: src/applications/session/sessiontest_nat_http.c:40 +#: src/applications/session/sessiontest_nat.c:40 +#: src/applications/stats/statistics.c:247 +msgid "# of connected peers" +msgstr "# của các đồng đẳng đã kết nối" + +#: src/applications/advertising/advertising.c:194 +#, c-format +msgid "HELLO message from `%s' has an invalid signature. Dropping.\n" +msgstr "Thông báo HELLO từ « %s » có chữ ký sai. Äang bá» Ä‘i.\n" + +#: src/applications/advertising/advertising.c:205 +msgid "HELLO message has expiration too far in the future. Dropping.\n" +msgstr "Thông báo HELLO hết hạn trong tÆ°Æ¡ng lai quá nhiá»u. Äang bá» Ä‘i.\n" + +#: src/applications/advertising/advertising.c:406 +msgid "Could not send HELLO+PING, ping buffer full.\n" +msgstr "Không gá»­i được tín hiệu HELLO+PING, đầy bá»™ đệm ping.\n" + +#: src/applications/advertising/advertising.c:429 +msgid "Failed to create an advertisement for this peer. Will not send PING.\n" +msgstr "" +"Không tạo được quảng cáo cho đồng đẳng này. Sẽ không gá»­i tín hiệu PING.\n" + +#: src/applications/advertising/advertising.c:581 +#, c-format +msgid "Advertising my transport %d to selected peers.\n" +msgstr "Äang quảng cáo truyá»n tải %d của mình tá»›i các đồng đẳng đã chá»n.\n" + +#: src/applications/advertising/advertising.c:590 +msgid "" +"Announcing ourselves pointless: no other peers are known to us so far.\n" +msgstr "Không có nghÄ©a khi tá»± thông báo : chÆ°a biết đồng đẳng khác.\n" + +#: src/applications/advertising/advertising.c:868 +msgid "# Peer advertisements received" +msgstr "# Các quảng cáo đồng đẳng được nhận" + +#: src/applications/advertising/advertising.c:871 +msgid "# Peer advertisements of type NAT received" +msgstr "# Các quảng cáo đồng đẳng kiểu NAT được nhận" + +#: src/applications/advertising/advertising.c:874 +msgid "# Peer advertisements confirmed via PONG" +msgstr "# Các quảng cáo đồng đẳng được xác nhận qua PONG" + +#: src/applications/advertising/advertising.c:877 +msgid "# Peer advertisements updating earlier HELLOs" +msgstr "# Các quảng cáo đồng đẳng cập nhật tin hiệu HELLO trÆ°á»›c" + +#: src/applications/advertising/advertising.c:880 +msgid "# Peer advertisements discarded due to load" +msgstr "# Các quảng cáo đồng đẳng bị hủy do trá»ng tải" + +#: src/applications/advertising/advertising.c:883 +msgid "# Peer advertisements for unsupported transport" +msgstr "# Các quảng cáo đồng đẳng cho truyá»n tải không được há»— trợ" + +#: src/applications/advertising/advertising.c:886 +msgid "# Peer advertisements not confirmed due to ping busy" +msgstr "# Các quảng cáo đồng đẳng không được xác nhận do ping Ä‘ang bận" + +#: src/applications/advertising/advertising.c:889 +msgid "# Peer advertisements not confirmed due to lack of self ad" +msgstr "# Các quảng cáo đồng đẳng không được xác nhận do không tá»± quảng cáo" + +#: src/applications/advertising/advertising.c:892 +msgid "# Peer advertisements not confirmed due to send error" +msgstr "# Các quảng cáo đồng đẳng không được xác nhận do lá»—i gá»­i" + +#: src/applications/advertising/advertising.c:894 +msgid "# Self advertisments transmitted" +msgstr "# Các tá»± quảng cáo được truyá»n" + +#: src/applications/advertising/advertising.c:896 +msgid "# Foreign advertisements forwarded" +msgstr "# Các quảng cáo ngoại được chuyển tiếp" + +#: src/applications/advertising/advertising.c:898 +#: src/applications/pingpong/pingpong.c:528 +msgid "# plaintext PING messages sent" +msgstr "# các thông báo PING nhập thô được gá»­i" + +#: src/applications/advertising/advertising.c:904 +#: src/applications/session/connect.c:932 +#, c-format +msgid "`%s' registering handler %d (plaintext and ciphertext)\n" +msgstr "« %s » Ä‘ang đăng ký trình Ä‘iá»u khiển %d (nhập thô và văn bản mã hóa)\n" + +#: src/applications/advertising/advertising.c:922 +msgid "" +"ensures that this peer is known by other peers and discovers other peers" +msgstr "đảm bảo là đồng đẳng này được biết bởi và phát hiện các đồng đẳng khác" + +#: src/applications/fragmentation/fragmentation.c:578 +msgid "# messages defragmented" +msgstr "# các thông báo được chắp liá»n" + +#: src/applications/fragmentation/fragmentation.c:580 +msgid "# messages fragmented" +msgstr "# các thông báo bị tế phân" + +#: src/applications/fragmentation/fragmentation.c:581 +msgid "# fragments discarded" +msgstr "# các mảnh bị loại bá»" + +#: src/applications/fragmentation/fragmentation.c:592 +#, c-format +msgid "`%s' registering handler %d\n" +msgstr "« %s » Ä‘ang đăng ký trình Ä‘iá»u khiển %d\n" + +#: src/applications/topology_default/topology.c:466 +#, c-format +msgid "Could not read friends list `%s'\n" +msgstr "Không thể Ä‘á»c danh sách bạn bè « %s »\n" + +#: src/applications/topology_default/topology.c:485 +#, c-format +msgid "Failed to read friends list from `%s'\n" +msgstr "Lá»—i Ä‘á»c danh sách bạn bè từ « %s »\n" + +#: src/applications/topology_default/topology.c:505 +msgid "Syntax error in topology specification, skipping bytes.\n" +msgstr "Lá»—i cú pháp trong sá»± xác định địa hình há»c, Ä‘ang bá» qua các byte.\n" + +#: src/applications/topology_default/topology.c:523 +#, c-format +msgid "Syntax error in topology specification, skipping bytes `%s'.\n" +msgstr "" +"Lá»—i cú pháp trong sá»± xác định địa hình há»c, Ä‘ang bá» qua các byte « %s ».\n" + +#: src/applications/topology_default/topology.c:535 +msgid "" +"Fewer friends specified than required by minimum friend count. Will only " +"connect to friends.\n" +msgstr "" +"Xác định quá ít bạn bè (dÆ°á»›i số tối thiểu). Sẽ chỉ kết nối tá»›i bạn bè.\n" + +#: src/applications/topology_default/topology.c:543 +msgid "" +"More friendly connections required than target total number of connections.\n" +msgstr "Cần thiết nhiá»u kết nối bạn bè hÆ¡n tổng số kết nối đích.\n" + +#: src/applications/topology_default/topology.c:726 +msgid "maintains GNUnet default mesh topology" +msgstr "bảo quản định hình mắc lÆ°á»›i mặc định của GNUnet" + +#: src/applications/chat/tools/gnunet-chat.c:107 +msgid "anonymous" +msgstr "nặc danh" + +#: src/applications/chat/tools/gnunet-chat.c:113 +#, c-format +msgid "`%s' said: %s\n" +msgstr "« %s » nói: %s\n" + +#: src/applications/chat/tools/gnunet-chat.c:116 +#: src/applications/chat/tools/gnunet-chat.c:119 +#, c-format +msgid "`%s' said to you: %s\n" +msgstr "« %s » nói cho bạn: %s\n" + +#: src/applications/chat/tools/gnunet-chat.c:122 +#, c-format +msgid "`%s' said for sure: %s\n" +msgstr "« %s » nói thật: %s\n" + +#: src/applications/chat/tools/gnunet-chat.c:125 +#, c-format +msgid "`%s' said to you for sure: %s\n" +msgstr "« %s » nói thật cho bạn: %s\n" + +#: src/applications/chat/tools/gnunet-chat.c:128 +#, c-format +msgid "`%s' was confirmed that you received: %s\n" +msgstr "« %s » xác nhận bạn đã nhận được: %s\n" + +#: src/applications/chat/tools/gnunet-chat.c:131 +#, c-format +msgid "`%s' was confirmed that you and only you received: %s\n" +msgstr "« %s » xác nhận mà chỉ bạn đã nhận được: %s\n" + +#: src/applications/chat/tools/gnunet-chat.c:134 +#, c-format +msgid "`%s' was confirmed that you received from him or her: %s\n" +msgstr "« %s » xác nhận mà bạn đã nhận được từ há» : %s\n" + +#: src/applications/chat/tools/gnunet-chat.c:139 +#, c-format +msgid "`%s' was confirmed that you and only you received from him or her: %s\n" +msgstr "« %s » xác nhận mà chỉ bạn Ä‘a nhận được từ há» : %s\n" + +#: src/applications/chat/tools/gnunet-chat.c:142 +#, c-format +msgid "`%s' said off the record: %s\n" +msgstr "« %s » nói không chính thức: %s\n" + +#: src/applications/chat/tools/gnunet-chat.c:145 +#, c-format +msgid "<%s> said using an unknown message type: %s\n" +msgstr "<%s> đã nói bằng má»™t kiểu tin nhẳn không rõ : %s\n" + +#: src/applications/chat/tools/gnunet-chat.c:167 +#, c-format +msgid "`%s' entered the room\n" +msgstr "« %s » vào phòng\n" + +#: src/applications/chat/tools/gnunet-chat.c:167 +#, c-format +msgid "`%s' left the room\n" +msgstr "« %s » rá»i phòng\n" + +#: src/applications/chat/tools/gnunet-chat.c:239 +#: src/applications/chat/tools/gnunet-chat.c:348 +#, c-format +msgid "Failed to send message.\n" +msgstr "Lá»—i gá»­i tin nhẳn.\n" + +#: src/applications/chat/tools/gnunet-chat.c:265 +#: src/applications/chat/tools/gnunet-chat.c:524 +#, c-format +msgid "Joined room `%s' as user `%s'.\n" +msgstr "Äã vào phòng « %s » là ngÆ°á»i dùng « %s ».\n" + +#: src/applications/chat/tools/gnunet-chat.c:293 +#, c-format +msgid "Changed username to `%s'.\n" +msgstr "Äã thay đổi tên ngÆ°á»i dùng thành « %s ».\n" + +#: src/applications/chat/tools/gnunet-chat.c:301 +#, c-format +msgid "Unknown command `%s'.\n" +msgstr "Không rõ câu lệnh « %s ».\n" + +#: src/applications/chat/tools/gnunet-chat.c:316 +#, c-format +msgid "Syntax: /msg USERNAME MESSAGE" +msgstr "Cú pháp: /msg TÊN_NGƯỜI_DÙNG TIN_NHẲN" + +#: src/applications/chat/tools/gnunet-chat.c:324 +#, c-format +msgid "Unknown user `%s'\n" +msgstr "Không rõ ngÆ°á»i dùng « %s »\n" + +#: src/applications/chat/tools/gnunet-chat.c:339 +#, c-format +msgid "User `%s' is currently not in the room!\n" +msgstr "NgÆ°á»i dùng « %s » hiện thá»i không có trong phòng này.\n" + +#: src/applications/chat/tools/gnunet-chat.c:361 +#, c-format +msgid "Users in room `%s': " +msgstr "NgÆ°á»i dùng trong phòng « %s »:" + +#: src/applications/chat/tools/gnunet-chat.c:390 +msgid "" +"Use `/join #roomname' to join a chat room. Joining a room will cause you to " +"leave the current room" +msgstr "" +"Gõ chuá»—i « /join #tên_phòng » để vào má»™t phòng trò chuyện nào đó (việc này " +"cÅ©ng gây ra bạn ra khá»i phòng hiện tại)" + +#: src/applications/chat/tools/gnunet-chat.c:393 +msgid "" +"Use `/nick nickname' to change your nickname. This will cause you to leave " +"the current room and immediately rejoin it with the new name." +msgstr "" +"Gõ chuá»—i « /nick tên_hiệu » để thay đổi tên hiệu của mình (việc này cÅ©ng gây " +"ra bạn ra khá»i phòng hiện tại, sau đó vào lại ngay vá»›i tên má»›i)" + +#: src/applications/chat/tools/gnunet-chat.c:396 +msgid "" +"Use `/msg nickname message' to send a private message to the specified user" +msgstr "" +"Gõ chuá»—i « /msg tên_hiệu tin_nhẳn » để gá»­i má»™t tin nhẳn riêng cho ngÆ°á»i dùng " +"có tên đó" + +#: src/applications/chat/tools/gnunet-chat.c:398 +msgid "The `/notice' command is an alias for `/msg'" +msgstr "Lệnh « /notice » là má»™t biệt hiệu cho « /msg »" + +#: src/applications/chat/tools/gnunet-chat.c:400 +msgid "The `/query' command is an alias for `/msg'" +msgstr "Lệnh « /query » là má»™t biệt hiệu cho « /msg »" + +#: src/applications/chat/tools/gnunet-chat.c:402 +msgid "Use `/quit' to terminate gnunet-chat" +msgstr "Gõ chuá»—i « /quit » để thoát khá»i trình gnunet-chat" + +#: src/applications/chat/tools/gnunet-chat.c:404 +msgid "The `/leave' command is an alias for `/quit'" +msgstr "Lệnh « /leave » là má»™t biệt hiệu cho « /quit »" + +#: src/applications/chat/tools/gnunet-chat.c:407 +msgid "Use `/names' to list all of the current members in the chat room" +msgstr "" +"Gõ chuá»—i « /names » để liệt kê tất cả các thành viên hiện thá»i trong phòng " +"trò chuyện đó" + +#: src/applications/chat/tools/gnunet-chat.c:409 +msgid "Use `/help command' to get help for a specific command" +msgstr "Gõ chuá»—i « /help LỆNH » để xem trợ giúp vá» lệnh đó" + +#: src/applications/chat/tools/gnunet-chat.c:457 +msgid "Join a chat on GNUnet." +msgstr "Vào phòng trò chuyện trên GNUnet." + +#: src/applications/chat/tools/gnunet-chat.c:461 +msgid "set the nickname to use (required)" +msgstr "đặt tên hiệu cần dùng (cần thiết)" + +#: src/applications/chat/tools/gnunet-chat.c:464 +msgid "set the chat room to join" +msgstr "đặt phòng trò chuyện cần vào" + +#: src/applications/chat/tools/gnunet-chat.c:495 +#, c-format +msgid "You must specify a nickname\n" +msgstr "Phải ghi rõ tên hiệu\n" + +#: src/applications/chat/tools/gnunet-chat.c:515 +#, c-format +msgid "Failed to join room `%s'\n" +msgstr "Lá»—i vào phòng « %s »\n" + +#: src/applications/chat/module/chat.c:325 +#, c-format +msgid "`%s' registering CS handlers %d and %d\n" +msgstr "« %s » Ä‘ang đăng ký các trình Ä‘iá»u khiển CS %d và %d\n" + +#: src/applications/chat/module/chat.c:347 +msgid "enables P2P-chat (incomplete)" +msgstr "hiệu lá»±c trò chuyện giữa các đồng đẳng (chÆ°a hoàn tất)" + +#: src/applications/chat/lib/messaging.c:353 +#: src/applications/identity/hostkey.c:122 +#, c-format +msgid "Failed to access GNUnet home directory `%s'\n" +msgstr "Lá»—i truy cập đến thÆ° mục nhà GNUnet « %s »\n" + +#: src/applications/chat/lib/messaging.c:389 +#, c-format +msgid "Existing key in file `%s' failed format check, creating new key.\n" +msgstr "" +"Kiểm tra định dạng của chìa khóa đã có trong tập tin « %s » không thành công, " +"Ä‘ang tạo chìa khóa máy má»›i.\n" + +#: src/applications/chat/lib/messaging.c:399 +msgid "Creating new key for this nickname (this may take a while).\n" +msgstr "Äang tạo khoá má»›i cho tên hiệu này (có thể hÆ¡i lâu).\n" + +#: src/applications/chat/lib/messaging.c:411 +msgid "Done creating key.\n" +msgstr "Hoàn thành tạo khoá.\n" + +#: src/applications/dstore_mysql/dstore_mysql.c:471 +msgid "Failed to initialize MySQL database connection for dstore.\n" +msgstr "Lá»—i sÆ¡ khởi kết nối cÆ¡ sở dữ liệu MySQL cho kho dữ liệu.\n" + +#: src/applications/dstore_mysql/dstore_mysql.c:494 +#: src/applications/dstore_sqlite/dstore.c:636 +msgid "# bytes in dstore" +msgstr "# các byte trong kho dữ liệu" + +#: src/applications/dstore_mysql/dstore_mysql.c:496 +#: src/applications/dstore_sqlite/dstore.c:638 +msgid "# max bytes allowed in dstore" +msgstr "# các byte được phép trong kho dữ liệu dstore" + +#: src/applications/transport/transport.c:191 +#, c-format +msgid "" +"Converting peer address to string failed, transport type %d not supported\n" +msgstr "" +"Lá»—i chuyển đổi địa chỉ đồng đẳng sang chuá»—i, kiểu truyá»n tải %d không được " +"há»— trợ\n" + +#: src/applications/transport/transport.c:246 +#, c-format +msgid "Transport connection attempt failed, transport type %d not supported\n" +msgstr "Lá»—i thá»­ kết nối truyá»n tải, kiểu cÆ¡ chế truyá»n %d không được há»— trợ\n" + +#: src/applications/transport/transport.c:299 +#, c-format +msgid "" +"Transport failed to connect to peer `%s' (%u HELLOs known, none worked)\n" +msgstr "" +"Truyá»n tải không kết nối được tá»›i đồng đẳng « %s » (đã biết %u tín hiệu " +"HELLO, mà không có tín hiệu nào hoạt Ä‘á»™ng được)\n" + +#: src/applications/transport/transport.c:376 +#, c-format +msgid "Transmission attempt failed, transport type %d unknown.\n" +msgstr "Lá»—i thá»­ gá»­i, kiểu truyá»n tải %d không được há»— trợ\n" + +#: src/applications/transport/transport.c:500 +#, c-format +msgid "No transport of type %d known.\n" +msgstr "Không biết truyá»n tải nào kiểu %d.\n" + +#: src/applications/transport/transport.c:560 +msgid "No transport succeeded in creating a hello!\n" +msgstr "Không có truyá»n tải nào đã tạo được má»™t tín hiệu HELLO.\n" + +#: src/applications/transport/transport.c:761 +#, c-format +msgid "Loading transports `%s'\n" +msgstr "Äang nạp các truyá»n tải « %s »\n" + +#: src/applications/transport/transport.c:781 +#, c-format +msgid "Could not load transport plugin `%s'\n" +msgstr "Không thể nạp phần bổ sung truyá»n tải « %s »\n" + +#: src/applications/transport/transport.c:795 +#, c-format +msgid "Transport library `%s' did not provide required function '%s%s'.\n" +msgstr "ThÆ° viện truyá»n tải « %s » không cung cấp chức năng yêu cầu « %s%s ».\n" + +#: src/applications/transport/transport.c:824 +#, c-format +msgid "Loaded transport `%s'\n" +msgstr "Äã nạp truyá»n tải « %s »\n" + +#: src/applications/transport/transport.c:836 +#: src/server/gnunet-peer-info.c:252 +#, c-format +msgid "I am peer `%s'.\n" +msgstr "Tôi là đồng đẳng « %s ».\n" + +#: src/applications/dht/tools/dht_multipeer_test.c:80 +#: src/applications/dht/tools/dht_twopeer_test.c:47 +#: src/applications/dht/module/table.c:783 +msgid "# dht connections" +msgstr "# các kết nối dht" + +#: src/applications/dht/tools/dht-query.c:54 +msgid "Query (get KEY, put KEY VALUE) DHT table." +msgstr "Há»i (nhận KHOÃ, gá»­i GIÃ_TRỊ_KHOÃ) bảng DHT." + +#: src/applications/dht/tools/dht-query.c:58 +msgid "allow TIME ms to process a GET command" +msgstr "cho phép THỜI_GIAN mili giây để xá»­ lý má»—i câu lệnh GET (lấy)" + +#: src/applications/dht/tools/dht-query.c:107 +#, c-format +msgid "Issuing `%s(%s,%s)' command.\n" +msgstr "Äang cấp câu lệnh « %s(%s,%s) ».\n" + +#: src/applications/dht/tools/dht-query.c:142 +#: src/applications/fs/tools/gnunet-auto-share.c:669 +#, c-format +msgid "Failed to connect to gnunetd.\n" +msgstr "Lá»—i kết nối đến gnunetd.\n" + +#: src/applications/dht/tools/dht-query.c:155 +#, c-format +msgid "Command `%s' requires an argument (`%s').\n" +msgstr "Câu lệnh « %s » cần đến má»™t đối số (« %s »).\n" + +#: src/applications/dht/tools/dht-query.c:172 +#, c-format +msgid "Command `%s' requires two arguments (`%s' and `%s').\n" +msgstr "Câu lệnh « %s » cần đến hai đối số (« %s » và « %s »).\n" + +#: src/applications/dht/tools/dht-query.c:183 +#, c-format +msgid "Unsupported command `%s'. Aborting.\n" +msgstr "Lệnh không được há»— trợ « %s ». Äang hủy bá».\n" + +#: src/applications/dht/module/table.c:785 +msgid "# dht discovery messages received" +msgstr "# các thông báo phát hiện dht được nhận" + +#: src/applications/dht/module/table.c:787 +msgid "# dht route host lookups performed" +msgstr "# các việc tra tìm Ä‘Æ°á»ng tá»›i máy dht được làm" + +#: src/applications/dht/module/table.c:789 +msgid "# dht discovery messages sent" +msgstr "# các thông báo phát hiện dht được gá»­i" + +#: src/applications/dht/module/routing.c:879 +msgid "# dht replies routed" +msgstr "# các đáp ứng dht được định tuyến" + +#: src/applications/dht/module/routing.c:881 +msgid "# dht requests routed" +msgstr "# các yêu cầu dht được định tuyến" + +#: src/applications/dht/module/routing.c:883 +msgid "# dht get requests received" +msgstr "# các yêu cầu get (lấy) dht được nhận" + +#: src/applications/dht/module/routing.c:885 +msgid "# dht put requests received" +msgstr "# các yêu cầu put (gá»­i) dht được nhận" + +#: src/applications/dht/module/routing.c:887 +msgid "# dht results received" +msgstr "# các kết quả dht được nhận" + +#: src/applications/dht/module/routing.c:892 +#, c-format +msgid "`%s' registering p2p handlers: %d %d %d\n" +msgstr "« %s » Ä‘ang đăng ký các trình Ä‘iá»u khiển p2p: %d %d %d\n" + +#: src/applications/dht/module/cs.c:122 +#, c-format +msgid "`%s' failed. Terminating connection to client.\n" +msgstr "« %s » bị lá»—i. Äang chấm dứt kết nối tá»›i máy khách.\n" + +#: src/applications/dht/module/cs.c:250 +#, c-format +msgid "`%s' registering client handlers: %d %d\n" +msgstr "« %s » Ä‘ang đăng ký các trình Ä‘iá»u khiển máy khách: %d %d\n" + +#: src/applications/dht/module/cs.c:273 +msgid "Enables efficient non-anonymous routing" +msgstr "Hiệu lá»±c định tuyến khác nặc danh hiệu dụng" + +#: src/applications/identity/hostkey.c:155 +#, c-format +msgid "" +"Existing hostkey in file `%s' failed format check, creating new hostkey.\n" +msgstr "" +"Kiểm tra định dạng của chìa khóa máy trong tập tin « %s » không thành công, " +"Ä‘ang tạo chìa khóa máy má»›i.\n" + +#: src/applications/identity/hostkey.c:164 +msgid "Creating new hostkey (this may take a while).\n" +msgstr "Äang tạo khoá máy má»›i (có thể hÆ¡i lâu).\n" + +#: src/applications/identity/hostkey.c:176 +msgid "Done creating hostkey.\n" +msgstr "Hoàn thành tạo khoá máy.\n" + +#: src/applications/identity/identity.c:333 +#, c-format +msgid "" +"File `%s' in directory `%s' does not match naming convention. Removed.\n" +msgstr "" +"Tập tin « %s » trong thÆ° mục « %s » không tùy theo quy Æ°á»›c đặt tên. Bị gỡ bá».\n" + +#: src/applications/identity/identity.c:408 +#, c-format +msgid "Still no peers found in `%s'!\n" +msgstr "Vẫn còn không tìm thấy đồng đẳng trong « %s ».\n" + +#: src/applications/identity/identity.c:731 +#: src/applications/identity/identity.c:757 +#, c-format +msgid "Removed file `%s' containing invalid HELLO data.\n" +msgstr "Äã gỡ bá» tập tin « %s » chứa dữ liệu HELLO sai.\n" + +#: src/applications/identity/identity.c:809 +#, c-format +msgid "Signature failed verification: peer `%s' not known.\n" +msgstr "Không thẩm tra được chữ ký: không rõ đồng đẳng « %s ».\n" + +#: src/applications/identity/identity.c:819 +msgid "Signature failed verification: signature invalid.\n" +msgstr "Không thẩm tra được chữ ký: chữ ký sai.\n" + +#: src/applications/identity/identity.c:935 +#: src/applications/identity/identity.c:1058 +#, c-format +msgid "Peer `%s' is currently strictly blacklisted (for another %llums).\n" +msgstr "" +"Äồng đẳng « %s » hiện thá»i bị cấm hoàn toàn (trong %llu miligiây sau).\n" + +#: src/applications/identity/identity.c:1061 +#, c-format +msgid "Peer `%s' is currently blacklisted (for another %llums).\n" +msgstr "Äồng đẳng « %s » hiện thá»i bị cấm (trong %llu miligiây sau).\n" + +#: src/applications/pingpong/pingpong.c:134 +#: src/applications/pingpong/pingpong.c:203 +#: src/applications/pingpong/pingpong.c:273 +#: src/applications/pingpong/pingpong.c:345 +#, c-format +msgid "Received malformed `%s' message. Dropping.\n" +msgstr "Nhận được thông báo « %s » bị há»ng. Äang bá» Ä‘i.\n" + +#: src/applications/pingpong/pingpong.c:146 +msgid "Received ping for another peer. Dropping.\n" +msgstr "Nhận được tin hiệu ping cho đồng đẳng khác. Äang bá» Ä‘i.\n" + +#: src/applications/pingpong/pingpong.c:215 +#, c-format +msgid "Received PING from `%s' not destined for us!\n" +msgstr "Nhận được PING « %s » không dành cho chúng ta.\n" + +#: src/applications/pingpong/pingpong.c:315 +#: src/applications/pingpong/pingpong.c:381 +msgid "" +"Could not match PONG against any PING. Try increasing MAX_PING_PONG " +"constant.\n" +msgstr "" +"Không tÆ°Æ¡ng ứng được PONG đối vá»›i bất kỳ PING. Hãy thá»­ tăng hằng số " +"MAX_PING_PONG.\n" + +#: src/applications/pingpong/pingpong.c:425 +msgid "Cannot create PING, table full. Try increasing MAX_PING_PONG.\n" +msgstr "Không tạo được PING, bảng đầy. Hãy thá»­ tăng MAX_PING_PONG.\n" + +#: src/applications/pingpong/pingpong.c:518 +msgid "# encrypted PONG messages received" +msgstr "# các thông báo PONG đã mật mã được nhận" + +#: src/applications/pingpong/pingpong.c:520 +msgid "# plaintext PONG messages received" +msgstr "# các thông báo PONG nhập thô được nhận" + +#: src/applications/pingpong/pingpong.c:522 +msgid "# encrypted PING messages received" +msgstr "# các thông báo PING đã mật mã được nhận" + +#: src/applications/pingpong/pingpong.c:524 +msgid "# PING messages created" +msgstr "# các thông báo PING được tạo" + +#: src/applications/pingpong/pingpong.c:526 +#: src/applications/session/connect.c:926 +msgid "# encrypted PONG messages sent" +msgstr "# các thông báo PONG đã mật mã được gá»­i" + +#: src/applications/pingpong/pingpong.c:530 +#: src/applications/session/connect.c:924 +msgid "# encrypted PING messages sent" +msgstr "# các thông báo PING đã mật mã được gá»­i" + +#: src/applications/pingpong/pingpong.c:532 +msgid "# plaintext PONG messages sent" +msgstr "# các thông báo PONG nhập thô được gá»­i" + +#: src/applications/pingpong/pingpong.c:536 +msgid "# plaintext PONG transmissions failed" +msgstr "# các sá»± truyá»n PONG bị lá»—i" + +#: src/applications/pingpong/pingpong.c:546 +#, c-format +msgid "`%s' registering handlers %d %d (plaintext and ciphertext)\n" +msgstr "" +"« %s » Ä‘ang đăng ký các trình Ä‘iá»u khiển %d %d (nhập thô và văn bản mã hóa)\n" + +#: src/applications/hostlist/hostlist.c:165 +msgid "# hostlist requests received" +msgstr "# các yêu cầu danh sách máy được nhận" + +#: src/applications/hostlist/hostlist.c:167 +msgid "# hostlist HELLOs returned" +msgstr "# các lá»i chào mừng HELLO danh sách máy được trả vá»" + +#: src/applications/hostlist/hostlist.c:169 +msgid "# hostlist bytes returned" +msgstr "# các byte danh sách máy được trả vá»" + +#: src/applications/hostlist/hostlist.c:199 +msgid "integrated HTTP hostlist server" +msgstr "trình phục vụ danh sách máy HTTP hợp nhất" + +#: src/applications/session/connect.c:238 +#, c-format +msgid "Session key from peer `%s' could not be verified.\n" +msgstr "Khoá phiên chạy từ đồng đẳng « %s » không thể được thẩm tra.\n" + +#: src/applications/session/connect.c:282 +#, c-format +msgid "Cannot encrypt sessionkey, peer `%s' not known!\n" +msgstr "Không thể mã hoá khoá phiên chạy, không rõ đồng đẳng « %s ».\n" + +#: src/applications/session/connect.c:489 +#, c-format +msgid "Could not create any HELLO for myself (have transports `%s')!\n" +msgstr "Không thể tạo tín hiệu HELLO nào cho mình (có truyá»n tải « %s »).\n" + +#: src/applications/session/connect.c:599 +#, c-format +msgid "Session key received from peer `%s' has invalid format (discarded).\n" +msgstr "" +"Khoá phiên chạy được nhận từ đồng đẳng « %s » có định dạng sai (bị hủy).\n" + +#: src/applications/session/connect.c:632 +#, c-format +msgid "Session key received from peer `%s' is for `%s' and not for me!\n" +msgstr "" +"Khoá phiên chạy được nhận từ đồng đẳng « %s » dành cho « %s », không phải cho " +"tôi.\n" + +#: src/applications/session/connect.c:659 +#, c-format +msgid "Invalid `%s' message received from peer `%s'.\n" +msgstr "Nhận được thông báo « %s » sai từ đồng đẳng « %s ».\n" + +#: src/applications/session/connect.c:670 +#, c-format +msgid "setkey `%s' from `%s' fails CRC check (have: %u, want %u).\n" +msgstr "" +"Kiểm tra CRC setkey « %s » từ « %s » không thành công (có %u, còn muốn %u).\n" + +#: src/applications/session/connect.c:728 +#, c-format +msgid "" +"Error parsing encrypted session key from `%s', given message part size is " +"invalid.\n" +msgstr "" +"Lá»—i phân tích chìa khóa phiên chạy đã mã hóa từ « %s », kích cỡ của phần " +"thông báo Ä‘Æ°a ra là sai.\n" + +#: src/applications/session/connect.c:741 +#, c-format +msgid "Unknown type in embedded message from `%s': %u (size: %u)\n" +msgstr "Gặp kiểu không rõ trong thông báo nhúng từ « %s »: %u (kích cỡ : %u)\n" + +#: src/applications/session/connect.c:916 +msgid "# session keys sent" +msgstr "# các khoá phiên chạy được gá»­i" + +#: src/applications/session/connect.c:918 +msgid "# session keys rejected" +msgstr "# các khoá phiên chạy bị từ chối" + +#: src/applications/session/connect.c:920 +msgid "# session keys accepted" +msgstr "# các khoá phiên chạy được chấp nhận" + +#: src/applications/session/connect.c:922 +msgid "# sessions established" +msgstr "# các phiên chạy được thiết lập" + +#: src/applications/fs/tools/gnunet-pseudonym.c:70 +#: src/applications/fs/tools/gnunet-auto-share.c:199 +#: src/applications/fs/tools/gnunet-insert.c:246 +#: src/applications/fs/tools/gnunet-search.c:125 +#: src/applications/fs/tools/gnunet-download.c:77 +msgid "set the desired LEVEL of sender-anonymity" +msgstr "đặt CẤP mong muốn của tình trạng nặc danh của ngÆ°á»i gá»­i" + +#: src/applications/fs/tools/gnunet-pseudonym.c:73 +msgid "automate creation of a namespace by starting a collection" +msgstr "tá»± Ä‘á»™ng tạo má»™t không gian tên bằng cách bắt đầu má»™t thu thập" + +#: src/applications/fs/tools/gnunet-pseudonym.c:77 +msgid "create a new pseudonym under the given NICKNAME" +msgstr "tạo má»™t biệt hiệu má»›i dÆ°á»›i TÊN_HIỆU Ä‘Æ°a ra" + +#: src/applications/fs/tools/gnunet-pseudonym.c:80 +msgid "delete the pseudonym with the given NICKNAME" +msgstr "xoá biệt hiệu có TÊN_HIỆU đã cho" + +#: src/applications/fs/tools/gnunet-pseudonym.c:83 +msgid "end automated building of a namespace (ends collection)" +msgstr "kết thúc việc tá»± Ä‘á»™ng xây dá»±ng má»™t không gian tên (kết thúc thu thập)" + +#: src/applications/fs/tools/gnunet-pseudonym.c:85 +msgid "Create new pseudonyms, delete pseudonyms or list existing pseudonyms." +msgstr "Tạo biệt hiệu má»›i, xóa biệt hiệu hoặc liệt kê các biệt hiệu có." + +#: src/applications/fs/tools/gnunet-pseudonym.c:89 +msgid "" +"use the given keyword to advertise the namespace (use when creating a new " +"pseudonym)" +msgstr "" +"sá»­ dụng từ khóa Ä‘Æ°a ra để quảng cáo không gian tên (dùng khi tạo má»™t biệt " +"hiệu má»›i)" + +#: src/applications/fs/tools/gnunet-pseudonym.c:92 +msgid "specify metadata describing the namespace or collection" +msgstr "ghi rõ siêu dữ liệu mô tả không gian tên (hoặc thu thập)" + +#: src/applications/fs/tools/gnunet-pseudonym.c:96 +msgid "" +"do not generate an advertisement for this namespace (use when creating a new " +"pseudonym)" +msgstr "" +"đừng tạo ra má»™t quảng cáo cho không gian tên này (dùng khi tạo má»™t biệt hiệu " +"má»›i)" + +#: src/applications/fs/tools/gnunet-pseudonym.c:99 +msgid "do not list the pseudonyms from the pseudonym database" +msgstr "không liệt kê các biệt hiệu từ cÆ¡ sở dữ liệu biệt hiệu" + +#: src/applications/fs/tools/gnunet-pseudonym.c:103 +msgid "" +"specify IDENTIFIER to be the address of the entrypoint to content in the " +"namespace (use when creating a new pseudonym)" +msgstr "" +"ghi rõ BỘ_NHẬN_DIỆN là địa chỉ của Ä‘iểm vào ná»™i dung trong không gian tên " +"(dùng khi tạo má»™t biệt hiệu má»›i)" + +#: src/applications/fs/tools/gnunet-pseudonym.c:106 +msgid "set the rating of a namespace" +msgstr "đặt đánh giá của má»™t không gian tên" + +#: src/applications/fs/tools/gnunet-pseudonym.c:141 +#, c-format +msgid "Namespace `%s' has rating %d.\n" +msgstr "Không gian tên « %s » có đánh giá %d.\n" + +#: src/applications/fs/tools/gnunet-pseudonym.c:143 +#, c-format +msgid "Namespace `%s' (%s) has rating %d.\n" +msgstr "Không gian tên « %s » (%s) có đánh giá %d.\n" + +#: src/applications/fs/tools/gnunet-pseudonym.c:175 +#, c-format +msgid "\tRating (after update): %d\n" +msgstr "\tÄánh giá (sau khi cập nhật): %d\n" + +#: src/applications/fs/tools/gnunet-pseudonym.c:179 +#: src/applications/fs/tools/gnunet-pseudonym.c:241 +#: src/applications/fs/tools/gnunet-insert.c:101 +#, c-format +msgid "\tUnknown namespace `%s'\n" +msgstr "\tKhông rõ miá»n tên « %s »\n" + +#: src/applications/fs/tools/gnunet-pseudonym.c:217 +#, c-format +msgid "Collection stopped.\n" +msgstr "Thu thập bị dừng.\n" + +#: src/applications/fs/tools/gnunet-pseudonym.c:219 +#, c-format +msgid "Failed to stop collection (not active?).\n" +msgstr "Lá»—i dừng thu thập (không hoạt Ä‘á»™ng ?).\n" + +#: src/applications/fs/tools/gnunet-pseudonym.c:230 +#, c-format +msgid "Pseudonym `%s' deleted.\n" +msgstr "Biệt hiệu « %s » bị xoá.\n" + +#: src/applications/fs/tools/gnunet-pseudonym.c:235 +#, c-format +msgid "Error deleting pseudonym `%s' (does not exist?).\n" +msgstr "Lá»— xoá biệt hiệu « %s » (không tồn tại ?).\n" + +#: src/applications/fs/tools/gnunet-pseudonym.c:256 +msgid "Started collection.\n" +msgstr "Äã bắt đầu thu thập.\n" + +#: src/applications/fs/tools/gnunet-pseudonym.c:260 +msgid "Failed to start collection.\n" +msgstr "Lá»—i bắt đầu thu thập.\n" + +#: src/applications/fs/tools/gnunet-pseudonym.c:296 +msgid "Could not create namespace.\n" +msgstr "Không thể tạo miá»n tên.\n" + +#: src/applications/fs/tools/gnunet-pseudonym.c:304 +#, c-format +msgid "Namespace `%s' created (root: %s).\n" +msgstr "Äã tạo không gian tên « %s » (gốc: %s).\n" + +#: src/applications/fs/tools/gnunet-pseudonym.c:321 +#, c-format +msgid "You must specify a name for the collection (`%s' option).\n" +msgstr "Bạn phải ghi rõ má»™t tên cho thu thập (tùy chá»n « %s »).\n" + +#: src/applications/fs/tools/gnunet-pseudonym.c:329 +#, c-format +msgid "Could not access namespace information.\n" +msgstr "Không thể truy cập đến thông tin vá» không gian tên.\n" + +#: src/applications/fs/tools/gnunet-directory.c:84 +#, c-format +msgid "==> Directory `%s':\n" +msgstr "==> ThÆ° mục « %s »:\n" + +#: src/applications/fs/tools/gnunet-directory.c:88 +#, c-format +msgid "=\tError reading directory.\n" +msgstr "=\tLá»—i Ä‘á»c thÆ° mục.\n" + +#: src/applications/fs/tools/gnunet-directory.c:118 +#, c-format +msgid "File format error (not a GNUnet directory?)\n" +msgstr "Lá»—i định dạng tập tin (không phải là thÆ° mục GNUnet ?)\n" + +#: src/applications/fs/tools/gnunet-directory.c:120 +#, c-format +msgid "%d files found in directory.\n" +msgstr "Tìm thấy %d tập tin trong thÆ° mục.\n" + +#: src/applications/fs/tools/gnunet-directory.c:135 +msgid "Perform directory related operations." +msgstr "Thá»±c hiện các thao tác liên quan đến thÆ° mục." + +#: src/applications/fs/tools/gnunet-directory.c:138 +msgid "remove all entries from the directory database and stop tracking URIs" +msgstr "" +"gỡ bá» má»i mục nhập khá»i cÆ¡ sở dữ liệu thÆ° mục, và dừng theo dõi các địa chỉ " +"URI" + +#: src/applications/fs/tools/gnunet-directory.c:142 +msgid "list entries from the directory database" +msgstr "liệt kê các mục nhập từ cÆ¡ sở dữ liệu thÆ° mục" + +#: src/applications/fs/tools/gnunet-directory.c:145 +msgid "start tracking entries for the directory database" +msgstr "bắt đầu theo dõi các mục nhập cho cÆ¡ sở dữ liệu thÆ° mục" + +#: src/applications/fs/tools/gnunet-directory.c:168 +#, c-format +msgid "Listed %d matching entries.\n" +msgstr "Äã liệt kê %d mục nhập tÆ°Æ¡ng ứng.\n" + +#: src/applications/fs/tools/gnunet-auto-share.c:117 +#, c-format +msgid "Upload of `%s' at %llu out of %llu bytes.\n" +msgstr "Tải lên tập tin « %s » tại %llu trên %llu byte.\n" + +#: src/applications/fs/tools/gnunet-auto-share.c:130 +#, c-format +msgid "Upload of `%s' complete, URI is `%s'.\n" +msgstr "Tải lên « %s » hoàn thành, địa chỉ URI là « %s ».\n" + +#: src/applications/fs/tools/gnunet-auto-share.c:139 +#, c-format +msgid "Upload aborted.\n" +msgstr "Tải lên bị hủy bá».\n" + +#: src/applications/fs/tools/gnunet-auto-share.c:145 +#, c-format +msgid "Error uploading file: %s\n" +msgstr "Gặp lá»—i khi tải lên tập tin: %s\n" + +#: src/applications/fs/tools/gnunet-auto-share.c:154 +#, c-format +msgid "Starting upload of `%s'.\n" +msgstr "Äang bắt đầu tài lên « %s ».\n" + +#: src/applications/fs/tools/gnunet-auto-share.c:164 +#, c-format +msgid "Uploading suspended.\n" +msgstr "Tiến trình tải lên bị ngÆ°ng.\n" + +#: src/applications/fs/tools/gnunet-auto-share.c:179 +#, c-format +msgid "Uploading `%s' resumed.\n" +msgstr "Tiến trình tải lên « %s » đã tiếp tục lại.\n" + +#: src/applications/fs/tools/gnunet-auto-share.c:186 +#, c-format +msgid "Unexpected event: %d\n" +msgstr "Gặp sá»± kiện bất thÆ°á»ng: %d\n" + +#: src/applications/fs/tools/gnunet-auto-share.c:205 +msgid "" +"run in debug mode; gnunet-auto-share will not daemonize and error messages " +"will be written to stderr instead of a logfile" +msgstr "" +"chạy trong chế Ä‘á»™ tìm sá»­a lá»—i; gnunet-auto-share sẽ không trở thành trình " +"ná»n và sẽ ghi thông báo lá»—i ra đầu lá»—i tiêu chuẩn thay vì vào má»™t tập tin " +"ghi sá»± kiện." + +#: src/applications/fs/tools/gnunet-auto-share.c:211 +#: src/applications/fs/tools/gnunet-insert.c:259 +msgid "" +"do not use libextractor to add additional references to directory entries " +"and/or the published file" +msgstr "" +"đừng dùng libextractor để thêm các tham chiếu bổ sung vào mục nhập thÆ° mục " +"và/hay tập tin công bố" + +#: src/applications/fs/tools/gnunet-auto-share.c:213 +msgid "Automatically share a directory." +msgstr "Tá»± Ä‘á»™ng chia sẻ má»™t thÆ° mục." + +#: src/applications/fs/tools/gnunet-auto-share.c:216 +#: src/applications/fs/tools/gnunet-insert.c:273 +msgid "" +"add an additional keyword for all files and directories (this option can be " +"specified multiple times)" +msgstr "" +"thêm má»™t từ khóa bổ sung cho tất cả tập tin và thÆ° mục (có thể chỉ ra tùy " +"chá»n này nhiá»u lần)" + +#: src/applications/fs/tools/gnunet-auto-share.c:221 +#: src/applications/fs/tools/gnunet-insert.c:290 +msgid "specify the priority of the content" +msgstr "xác định mức Æ°u tiên của ná»™i dung" + +#: src/applications/fs/tools/gnunet-auto-share.c:468 +#: src/applications/fs/tools/gnunet-auto-share.c:903 +#, c-format +msgid "Could not access `%s': %s\n" +msgstr "Không thể truy cập đến « %s »: %s\n" + +#: src/applications/fs/tools/gnunet-auto-share.c:547 +#, c-format +msgid "Unknown keyword type `%s' in metadata configuration\n" +msgstr "Không rõ kiểu từ khoá « %s » trong cấu hình siêu dữ liệu\n" + +#: src/applications/fs/tools/gnunet-auto-share.c:652 +#, c-format +msgid "Failed to stop running gnunet-auto-share.\n" +msgstr "Lá»—i dừng chạy gnunet-auto-share.\n" + +#: src/applications/fs/tools/gnunet-auto-share.c:918 +#, c-format +msgid "Directory `%s' is already on the list of shared directories.\n" +msgstr "ThÆ° mục « %s » đã có trong danh sách các thÆ° mục dùng chung.\n" + +#: src/applications/fs/tools/gnunet-auto-share.c:939 +msgid "" +"The specified directories were added to the list of shared directories.\n" +msgstr "" +"Những thÆ° mục Ä‘Æ°a ra đã được thêm vào danh sách các thÆ° mục dùng chung.\n" + +#: src/applications/fs/tools/gnunet-auto-share.c:961 +#, c-format +msgid "Could not open logfile `%s': %s\n" +msgstr "Lá»—i mở tập tin theo dõi « %s »: %s\n" + +#: src/applications/fs/tools/gnunet-insert.c:115 +#, c-format +msgid "Created entry `%s' in namespace `%s'\n" +msgstr "Äã tạo mục nhập « %s » trong không gian tên « %s »\n" + +#: src/applications/fs/tools/gnunet-insert.c:120 +#, c-format +msgid "Failed to add entry to namespace `%s' (does it exist?)\n" +msgstr "Lá»—i thêm mục nhập vào không gian tên « %s » (có chÆ°a ?)\n" + +#: src/applications/fs/tools/gnunet-insert.c:135 +#, c-format +msgid "Keywords for file `%s':\n" +msgstr "Từ khoá cho tập tin « %s »:\n" + +#: src/applications/fs/tools/gnunet-insert.c:144 +msgid "filename" +msgstr "tên tập tin" + +#: src/applications/fs/tools/gnunet-insert.c:146 +msgid "mimetype" +msgstr "kiểu MIME" + +#: src/applications/fs/tools/gnunet-insert.c:186 +#, c-format +msgid "%16llu of %16llu bytes inserted (estimating %6s to completion) - %s\n" +msgstr "" +"đã chèn %16llu trên %16llu byte (sẽ hoàn thành trong khoảng %6s giây) - %s\n" + +#: src/applications/fs/tools/gnunet-insert.c:198 +#, c-format +msgid "Upload of `%s' complete, %llu bytes took %llu seconds (%8.3f KiB/s).\n" +msgstr "" +"Hoàn thành tải lên « %s », %llu byte trong %llu giây (%8.3f KiB/giây).\n" + +#: src/applications/fs/tools/gnunet-insert.c:209 +#, c-format +msgid "File `%s' has URI: %s\n" +msgstr "Tập tin « %s » có URI: %s\n" + +#: src/applications/fs/tools/gnunet-insert.c:220 +#, c-format +msgid "" +"\n" +"Upload aborted.\n" +msgstr "" +"\n" +"Tải lên bị hủy bá».\n" + +#: src/applications/fs/tools/gnunet-insert.c:225 +#, c-format +msgid "" +"\n" +"Error uploading file: %s" +msgstr "" +"\n" +"Gặp lá»—i khi tải lên tập tin: %s" + +#: src/applications/fs/tools/gnunet-insert.c:234 +#, c-format +msgid "" +"\n" +"Unexpected event: %d\n" +msgstr "" +"\n" +"Gặp sá»± kiện bất thÆ°á»ng: %d\n" + +#: src/applications/fs/tools/gnunet-insert.c:250 +msgid "" +"even if gnunetd is running on the local machine, force the creation of a " +"copy instead of making a link to the GNUnet share directory" +msgstr "" +"thậm chí nếu gnunetd Ä‘ang chạy trên máy cục bá»™, bắt buá»™c tạo má»™t bản sao " +"thay vì tạo má»™t liên kết đến thÆ° mục chia sẻ của GNUnet" + +#: src/applications/fs/tools/gnunet-insert.c:255 +msgid "disable adding the creation time to the metadata of the uploaded file" +msgstr "tắt thêm giá» tạo vào siêu dữ liệu của tập tin đã tải lên" + +#: src/applications/fs/tools/gnunet-insert.c:263 +msgid "" +"print list of extracted keywords that would be used, but do not perform " +"upload" +msgstr "" +"in ra danh sách các từ khóa đã giải phóng cần sá»­ dụng, nhÆ°ng không thá»±c hiện " +"tải lên" + +#: src/applications/fs/tools/gnunet-insert.c:265 +msgid "Make files available to GNUnet for sharing." +msgstr "Làm cho các tập tin sẵn sàng qua GNUnet để chia sẻ." + +#: src/applications/fs/tools/gnunet-insert.c:269 +msgid "" +"add an additional keyword for the top-level file or directory (this option " +"can be specified multiple times)" +msgstr "" +"thêm má»™t từ khoá bổ sung cho tập tin hoặc thÆ° mục ở cấp đầu (có thể chỉ ra " +"tùy chá»n này nhiá»u lần)" + +#: src/applications/fs/tools/gnunet-insert.c:278 +msgid "set the meta-data for the given TYPE to the given VALUE" +msgstr "đặt siêu dữ liệu cho KIỂU Ä‘Æ°a ra thành GIÃ_TRỊ chỉ ra" + +#: src/applications/fs/tools/gnunet-insert.c:281 +msgid "" +"do not index, perform full insertion (stores entire file in encrypted form " +"in GNUnet database)" +msgstr "" +"không đánh chỉ mục, thá»±c hiện việc chèn đầy đủ (chứa toàn bá»™ tập tin ở dạng " +"mã hóa trong cÆ¡ sở dữ liệu GNUnet)" + +#: src/applications/fs/tools/gnunet-insert.c:286 +msgid "" +"specify ID of an updated version to be published in the future (for " +"namespace insertions only)" +msgstr "" +"chỉ ra mã số của má»™t phiên bản đã cập nhật để công bố trong tÆ°Æ¡ng lai (chỉ " +"cho sá»± chèn không gian tên)" + +#: src/applications/fs/tools/gnunet-insert.c:294 +msgid "publish the files under the pseudonym NAME (place file into namespace)" +msgstr "" +"công bố các tập tin dÆ°á»›i biệt hiệu TÊN (đặt tập tin vào không gian tên)" + +#: src/applications/fs/tools/gnunet-insert.c:297 +msgid "" +"only simulte the process but do not do any actual publishing (useful to " +"compute URIs)" +msgstr "" +"chỉ mô phá»ng tiến trình, không thật công bố (có ích để tính địa chỉ URI)" + +#: src/applications/fs/tools/gnunet-insert.c:301 +msgid "" +"set the ID of this version of the publication (for namespace insertions only)" +msgstr "" +"đặt mã số của phiên bản này của sá»± công bố (chỉ cho chèn không gian tên)" + +#: src/applications/fs/tools/gnunet-insert.c:305 +msgid "" +"URI to be published (can be used instead of passing a file to add keywords " +"to the file with the respective URI)" +msgstr "" +"Äịa chỉ URI cần công bố (có thể được dùng thay vào gá»­i má»™t tập tin để thêm " +"từ khoá vào tập tin có địa chỉ URI tÆ°Æ¡ng ứng)" + +#: src/applications/fs/tools/gnunet-insert.c:342 +#, c-format +msgid "You must specify one and only one filename for insertion.\n" +msgstr "Phải ghi rõ chỉ má»™t tên tập tin để chèn.\n" + +#: src/applications/fs/tools/gnunet-insert.c:348 +#, c-format +msgid "You must NOT specify an URI and a filename.\n" +msgstr "KHÔNG cho phép ghi rõ cả hai địa chỉ URI và tên tập tin.\n" + +#: src/applications/fs/tools/gnunet-insert.c:354 +#, c-format +msgid "Cannot extract metadata from a URI!\n" +msgstr "Không thể trích siêu dữ liệu ra má»™t địa chỉ URI.\n" + +#: src/applications/fs/tools/gnunet-insert.c:400 +#, c-format +msgid "Could not access namespace `%s' (does not exist?).\n" +msgstr "Không thể truy cập đến không gian tên « %s » (không tồn tại ?).\n" + +#: src/applications/fs/tools/gnunet-insert.c:408 +#, c-format +msgid "Option `%s' is required when using option `%s'.\n" +msgstr "Tùy chá»n « %s » cần thiết khi dùng tùy chá»n « %s ».\n" + +#: src/applications/fs/tools/gnunet-insert.c:419 +#: src/applications/fs/tools/gnunet-insert.c:427 +#, c-format +msgid "Option `%s' makes no sense without option `%s'.\n" +msgstr "Tùy chá»n « %s » không có nghÄ©a khi không có tùy chá»n « %s ».\n" + +#: src/applications/fs/tools/gnunet-search.c:128 +msgid "Search GNUnet for files." +msgstr "Tìm tập tin trong GNUnet." + +#: src/applications/fs/tools/gnunet-search.c:132 +msgid "write encountered (decrypted) search results to FILENAME" +msgstr "ghi kết quả tìm kiếm tìm thấy (đã giải mã) vào tập tin TÊN_TẬP_TIN" + +#: src/applications/fs/tools/gnunet-search.c:169 +#, c-format +msgid "Error converting arguments to URI!\n" +msgstr "Gặp lá»—i khi chuyển đổi các đối số sang URI.\n" + +#: src/applications/fs/tools/gnunet-unindex.c:61 +#, c-format +msgid "" +"%16llu of %16llu bytes unindexed (estimating %llu seconds to " +"completion) " +msgstr "" +"Äã bá» chỉ mục %16llu trên %16llu byte (sẽ hoàn thành sau khoảng %llu " +"giây) " + +#: src/applications/fs/tools/gnunet-unindex.c:73 +#, c-format +msgid "" +"\n" +"Unindexing of `%s' complete, %llu bytes took %llu seconds (%8.3f KiB/s).\n" +msgstr "" +"\n" +"Hoàn thành bá» chỉ mục của « %s », %llu byte sau %llu giây (%8.3f KiB/giây).\n" + +#: src/applications/fs/tools/gnunet-unindex.c:88 +#, c-format +msgid "" +"\n" +"Error unindexing file: %s\n" +msgstr "" +"\n" +"Gặp lá»—i khi bá» chỉ mục tập tin: %s\n" + +#: src/applications/fs/tools/gnunet-unindex.c:108 +msgid "Unindex files." +msgstr "Bá» chỉ mục tập tin." + +#: src/applications/fs/tools/gnunet-unindex.c:145 +msgid "Not enough arguments. You must specify a filename.\n" +msgstr "Không đủ đối số. Phải xác định má»™t tên tập tin.\n" + +#: src/applications/fs/tools/gnunet-unindex.c:163 +#, c-format +msgid "`%s' failed. Is `%s' a file?\n" +msgstr "« %s » bị lá»—i. « %s » là má»™t tập tin phải không?\n" + +#: src/applications/fs/tools/gnunet-download.c:82 +msgid "" +"download a GNUnet directory that has already been downloaded. Requires that " +"a filename of an existing file is specified instead of the URI. The " +"download will only download the top-level files in the directory unless the " +"`-R' option is also specified." +msgstr "" +"tải xuống má»™t thÆ° mục GNUnet đã được tải vá» trÆ°á»›c. Cần thiết ghi rõ tên tập " +"tin của má»™t tập tin đã có, thay cho địa chỉ URI. Việc tải vá» sẽ chỉ tải vá» " +"các tập tin cấp đầu của thÆ° mục, nếu không cÅ©ng Ä‘Æ°a ra tùy chá»n « -R »." + +#: src/applications/fs/tools/gnunet-download.c:85 +msgid "delete incomplete downloads (when aborted with CTRL-C)" +msgstr "xoá việc tải vá» không hoàn thành (khi hủy bở dùng CTRL-C)" + +#: src/applications/fs/tools/gnunet-download.c:87 +msgid "Download files from GNUnet." +msgstr "Tải tập tin xuống GNUnet." + +#: src/applications/fs/tools/gnunet-download.c:91 +msgid "write the file to FILENAME" +msgstr "ghi tập tin vào TÊN_TẬP_TIN" + +#: src/applications/fs/tools/gnunet-download.c:95 +msgid "set the maximum number of parallel downloads that are allowed" +msgstr "đặt số tối Ä‘a các việc tải xuống đồng thá»i được phép" + +#: src/applications/fs/tools/gnunet-download.c:98 +msgid "download a GNUnet directory recursively" +msgstr "tải xuống đệ quy má»™t thÆ° mục GNUnet" + +#: src/applications/fs/tools/gnunet-download.c:119 +#, c-format +msgid "Download of file `%s' at %16llu out of %16llu bytes (%8.3f KiB/s)\n" +msgstr "Tải xuống tập tin « %s » tại %16llu trên %16llu byte (%8.3f KiB/giây)\n" + +#: src/applications/fs/tools/gnunet-download.c:133 +#, c-format +msgid "Download aborted.\n" +msgstr "Tiến trình tải xuống bị hủy bá».\n" + +#: src/applications/fs/tools/gnunet-download.c:139 +#, c-format +msgid "Error downloading: %s\n" +msgstr "Gặp lá»—i khi tải xuống: %s\n" + +#: src/applications/fs/tools/gnunet-download.c:145 +#, c-format +msgid "Download of file `%s' complete. Speed was %8.3f KiB per second.\n" +msgstr "Hoàn thành tải xuống tập tin « %s ». Tốc Ä‘á»™ là %8.3f KiB má»™t giây.\n" + +#: src/applications/fs/tools/gnunet-download.c:191 +msgid "no name given" +msgstr "chÆ°a Ä‘Æ°a ra tên" + +#: src/applications/fs/tools/gnunet-download.c:197 +#, c-format +msgid "Starting download `%s'\n" +msgstr "Äang bắt đầu tài vỠ« %s »\n" + +#: src/applications/fs/tools/gnunet-download.c:239 +msgid "Not enough arguments. You must specify a GNUnet file URI\n" +msgstr "Không đủ đối số. Phải ghi rõ má»™t địa chỉ URI tập tin GNUnet\n" + +#: src/applications/fs/tools/gnunet-download.c:257 +#, c-format +msgid "URI `%s' invalid for gnunet-download.\n" +msgstr "Sai URI « %s » cho gnunet-download.\n" + +#: src/applications/fs/tools/gnunet-download.c:300 +#, c-format +msgid "No filename specified, using `%s' instead (for now).\n" +msgstr "ChÆ°a ghi rõ tên tập tin, Ä‘ang dùng « %s » để thay thế (lúc này).\n" + +#: src/applications/fs/tools/gnunet-download.c:342 +#, c-format +msgid "Could not access gnunet-directory file `%s'\n" +msgstr "Không thể truy cập đến tập tin gnunet-directory « %s »\n" + +#: src/applications/fs/tools/gnunet-download.c:363 +#, c-format +msgid "Downloading %d files from directory `%s'.\n" +msgstr "Äang tải %d tập tin xuống thÆ° mục « %s ».\n" + +#: src/applications/fs/tools/gnunet-download.c:366 +#, c-format +msgid "Did not find any files in directory `%s'\n" +msgstr "Không tìm thấy tập tin nào trong thÆ° mục « %s »\n" + +#: src/applications/fs/tools/gnunet-download.c:404 +#, c-format +msgid "File stored as `%s'.\n" +msgstr "Tập tin được lÆ°u dạng « %s ».\n" + +#: src/applications/fs/uritrack/file_info.c:98 +msgid "Collecting file identifiers disabled.\n" +msgstr "Chức năng thu thập các đồ nhận diện tập tin đã bị tắt.\n" + +#: src/applications/fs/uritrack/file_info.c:377 +#, c-format +msgid "Deleted corrupt URI database in `%s'." +msgstr "Äã xoá cÆ¡ sở dữ liệu địa chỉ URI bị há»ng trong « %s »." + +#: src/applications/fs/ecrs/upload.c:158 +#, c-format +msgid "`%s' is not a file.\n" +msgstr "« %s » không phải là má»™t tập tin.\n" + +#: src/applications/fs/ecrs/upload.c:166 +#, c-format +msgid "Cannot get size of file `%s'" +msgstr "Không thể lấy kích cỡ của tập tin « %s »" + +#: src/applications/fs/ecrs/upload.c:175 +msgid "Failed to connect to gnunetd." +msgstr "Không kết nối được đến trình ná»n gnunetd." + +#: src/applications/fs/ecrs/upload.c:187 +#, c-format +msgid "Cannot hash `%s'.\n" +msgstr "Không thể tạo mẫu duy nhất đại diện cho « %s ».\n" + +#: src/applications/fs/ecrs/upload.c:215 +#, c-format +msgid "Initialization for indexing file `%s' failed.\n" +msgstr "Lá»—i sÆ¡ khởi tập tin đánh chỉ mục « %s ».\n" + +#: src/applications/fs/ecrs/upload.c:223 +#, c-format +msgid "Indexing file `%s' failed. Suggestion: try to insert the file.\n" +msgstr "Lá»—i đánh chỉ mục tập tin « %s ». Äá» nghị: thá»­ chèn tập tin.\n" + +#: src/applications/fs/ecrs/upload.c:237 +#, c-format +msgid "Cannot open file `%s': `%s'" +msgstr "Không thể mở tập tin « %s »: « %s »" + +#: src/applications/fs/ecrs/upload.c:322 +#, c-format +msgid "Indexing data of file `%s' failed at position %llu.\n" +msgstr "Äánh chỉ mục dữ liệu của tập tin « %s » bị lá»—i tại vị tri %llu.\n" + +#: src/applications/fs/ecrs/helper.c:91 +msgid "No keywords specified!\n" +msgstr "ChÆ°a ghi rõ từ khoá.\n" + +#: src/applications/fs/ecrs/helper.c:99 +msgid "Number of double-quotes not balanced!\n" +msgstr "Có dấu nháy kép thừa hay thiếu.\n" + +#: src/applications/fs/ecrs/helper.c:398 +#, c-format +msgid "Renaming of file `%s' to `%s' failed: %s\n" +msgstr "Lá»—i thay đổi tên của tập tin « %s » thành « %s »: %s\n" + +#: src/applications/fs/ecrs/helper.c:408 +#, c-format +msgid "Could not rename file `%s' to `%s': file exists\n" +msgstr "Không thể thay đổi tên tập tin « %s » thành « %s »: tập tin đã có\n" + +#: src/applications/fs/ecrs/parser.c:165 +#, c-format +msgid "" +"Unknown metadata type in metadata option `%s'. Using metadata type " +"`unknown' instead.\n" +msgstr "" +"Không rõ kiểu siêu dữ liệu trong tùy chá»n siêu dữ liệu « %s ». Äang ùng kiểu " +"siêu dữ liệu « không rõ » (unknown) để thay thế.\n" + +#: src/applications/fs/ecrs/search.c:152 +msgid "CHK URI not allowed for search.\n" +msgstr "Không cho phép địa chỉ URI CHK khi tìm kiếm.\n" + +#: src/applications/fs/ecrs/search.c:207 +msgid "LOC URI not allowed for search.\n" +msgstr "Không cho phép địa chỉ URI LOC khi tìm kiếm.\n" + +#: src/applications/fs/ecrs/namespace.c:365 +#, c-format +msgid "File `%s' does not contain a pseudonym.\n" +msgstr "Tập tin « %s » không chứa biệt hiệu.\n" + +#: src/applications/fs/ecrs/namespace.c:376 +#, c-format +msgid "Format of pseudonym `%s' is invalid.\n" +msgstr "Äịnh dạng của biệt hiệu « %s » là không hợp lệ.\n" + +#: src/applications/fs/ecrs/namespace.c:535 +#: src/applications/fs/ecrs/namespace.c:547 +#: src/applications/fs/ecrs/namespace.c:559 +#, c-format +msgid "Format of file `%s' is invalid, trying to remove.\n" +msgstr "Äịnh dạng của tập tin « %s » là không hợp lệ nên thá»­ gỡ bá».\n" + +#: src/applications/fs/ecrs/download.c:599 +msgid "" +"Decrypted content does not match key. This is either a bug or a maliciously " +"inserted file. Download aborted.\n" +msgstr "" +"Ná»™i dung đã giải mã không tÆ°Æ¡ng ứng chìa khóa. Äây là má»™t lá»—i hoặc má»™t tập " +"tin chèn vào vá»›i ý xấu. Tiến trình tải xuống bị hủy bá».\n" + +#: src/applications/fs/ecrs/download.c:609 +msgid "IO error." +msgstr "Lá»—i VR." + +#: src/applications/fs/collection/collection.c:559 +#: src/applications/fs/collection/collection.c:562 +#, c-format +msgid "Revision %u" +msgstr "Bản sá»­a đổi %u" + +#: src/applications/fs/fsui/upload.c:330 +msgid "Application aborted." +msgstr "Ứng dụng bị hủy bá»." + +#: src/applications/fs/fsui/upload.c:344 +msgid "Failed to create temporary directory." +msgstr "Lá»—i tạo thÆ° mục tạm thá»i." + +#: src/applications/fs/fsui/deserialize.c:927 +#, c-format +msgid "FSUI state file `%s' had syntax error at offset %u.\n" +msgstr "Tập tin tình trạng FSUI « %s » có lá»—i cú pháp tại khoảng bù %u.\n" + +#: src/applications/fs/fsui/unindex.c:114 +msgid "Unindexing failed (no reason given)" +msgstr "Lá»—i bá» chỉ mục (không Ä‘Æ°a ra lý do)." + +#: src/applications/fs/gap/plan.c:944 +msgid "# gap requests total sent" +msgstr "# tổng số yêu cầu lá»— hổng được gá»­i" + +#: src/applications/fs/gap/plan.c:946 +msgid "# gap content total planned" +msgstr "# tổng số ná»™i dung lá»— hổng dá»± định" + +#: src/applications/fs/gap/plan.c:948 +msgid "# gap routes succeeded" +msgstr "# tổng số định tuyến lá»— hổng thành công" + +#: src/applications/fs/gap/plan.c:949 +msgid "# trust spent" +msgstr "# Ä‘á»™ tin cậy được tiêu phí" + +#: src/applications/fs/gap/fs.c:157 +msgid "Datastore full.\n" +msgstr "Kho dữ liệu đầy.\n" + +#: src/applications/fs/gap/fs.c:831 +msgid "# gap requests total received" +msgstr "# tổng số yêu cầu lá»— hổng được nhận" + +#: src/applications/fs/gap/fs.c:833 +msgid "# gap requests dropped due to load" +msgstr "# các yêu cầu lá»— hổng bị bá» do trá»ng tải" + +#: src/applications/fs/gap/fs.c:835 +msgid "# gap content total received" +msgstr "# tổng số ná»™i dung lá»— hổng được nhận" + +#: src/applications/fs/gap/fs.c:837 +msgid "# gap total trust awarded" +msgstr "# tổng số tin cậy lá»— hổng được cấp" + +#: src/applications/fs/gap/fs.c:865 +#, c-format +msgid "" +"`%s' registering client handlers %d %d %d %d %d %d %d %d and P2P handlers %d " +"%d\n" +msgstr "" +"« %s » Ä‘ang đăng ký các trình Ä‘iá»u khiển kiểu ứng dụng khách %d %d %d %d %d %" +"d %d %d và kiểu P2P %d %d\n" + +#: src/applications/fs/gap/fs.c:921 +msgid "enables (anonymous) file-sharing" +msgstr "hiệu lá»±c chia sẻ tập tin (nặc danh)" + +#: src/applications/fs/gap/ondemand.c:173 +#, c-format +msgid "" +"Because the file `%s' has been unavailable for 3 days it got removed from " +"your share. Please unindex files before deleting them as the index now " +"contains invalid references!\n" +msgstr "" +"Vì không có tập tin « %s » trong 3 ngày nên đã xóa nó khá»i chia sẻ của bạn. " +"Xin hãy bá» chỉ mục các tập tin trÆ°á»›c khi xoá chúng vì chỉ mục hiện thá»i chứa " +"các tham chiếu sai.\n" + +#: src/applications/fs/gap/ondemand.c:451 +msgid "Indexed content changed (does not match its hash).\n" +msgstr "Ná»™i dung đánh chỉ mục bị thay đổi (không tÆ°Æ¡ng ứng vá»›i tổng kiểm).\n" + +#: src/applications/fs/gap/ondemand.c:569 +#, c-format +msgid "" +"Unindexed ODB block `%s' from offset %llu already missing from datastore.\n" +msgstr "" +"Khối ODB đã bá» chỉ mục « %s » từ khoảng bù %llu đã không có trong kho dữ " +"liệu.\n" + +#: src/applications/fs/gap/pid_table.c:177 +msgid "# distinct interned peer IDs in pid table" +msgstr "# các mã số đồng đẳng bị giam giữ riêng biệt trong bảng PID" + +#: src/applications/fs/gap/pid_table.c:180 +msgid "# total RC of interned peer IDs in pid table" +msgstr "# tổng số RC của mã số đồng đẳng bị giam giữ trong bảng PID" + +#: src/applications/fs/gap/querymanager.c:708 +msgid "# gap client queries received" +msgstr "# các yêu cầu khách lá»— hổng được nhận" + +#: src/applications/fs/gap/querymanager.c:710 +msgid "# gap replies sent to clients" +msgstr "# các đáp ứng lá»— hổng được gá»­i cho trình/máy khách" + +#: src/applications/fs/gap/querymanager.c:712 +msgid "# gap client requests tracked" +msgstr "# các yêu cầu máy/trình khách lá»— hổng được theo dõi" + +#: src/applications/fs/gap/querymanager.c:714 +msgid "# gap client requests injected" +msgstr "# các yêu cầu máy/trình khách lá»— hổng được phun vào" + +#: src/applications/fs/gap/querymanager.c:717 +msgid "# gap query bloomfilter resizing updates" +msgstr "" +"# các bản cập nhật thay đổi kích cỡ bá»™ lá»c bloomfilter truy vấn lá»— hổng" + +#: src/applications/fs/gap/migration.c:437 +msgid "# blocks migrated" +msgstr "# các khối được nâng cấp" + +#: src/applications/fs/gap/migration.c:439 +msgid "# blocks injected for migration" +msgstr "# các khối được phun vào để nâng cấp" + +#: src/applications/fs/gap/migration.c:441 +msgid "# blocks fetched for migration" +msgstr "# các khối được lấy để nâng cấp" + +#: src/applications/fs/gap/migration.c:443 +msgid "# on-demand fetches for migration" +msgstr "# các lần lấy theo yêu cầu để nâng cấp" + +#: src/applications/fs/gap/gap.c:694 +msgid "# gap queries dropped (table full)" +msgstr "# các truy vấn lá»— hổng bị bá» (bảng đầy)" + +#: src/applications/fs/gap/gap.c:696 +msgid "# gap queries dropped (redundant)" +msgstr "# các truy vấn lá»— hổng bị bá» (thừa)" + +#: src/applications/fs/gap/gap.c:698 +msgid "# gap queries routed" +msgstr "# các truy vấn lá»— hổng được định tuyến" + +#: src/applications/fs/gap/gap.c:700 +msgid "# gap content found locally" +msgstr "# ná»™i dung lá»— hổng được tìm cục bá»™" + +#: src/applications/fs/gap/gap.c:703 +msgid "# gap queries refreshed existing record" +msgstr "# các truy vấn lá»— hổng đã cập nhật mục ghi đã có" + +#: src/applications/fs/gap/gap.c:704 +msgid "# trust earned" +msgstr "# Ä‘á»™ tin cậy giành được" + +#: src/applications/fs/gap/fs_dht.c:256 +msgid "# blocks pushed into DHT" +msgstr "# các khối được đẩy vào DHT" + +#: src/applications/fs/gap/anonymity.c:56 +msgid "Failed to get traffic stats.\n" +msgstr "Lá»—i lấy thông kê vá» truyá»n tải.\n" + +#: src/applications/testing/remote.c:68 +#, c-format +msgid "scp command is : %s \n" +msgstr "Câu lệnh scp là: %s\n" + +#: src/applications/testing/remote.c:491 +#, c-format +msgid "Friend list of %s:%d\n" +msgstr "Danh sách bạn bè của %s:%d\n" + +#: src/applications/testing/remote.c:513 +#, c-format +msgid "scp command for friend file copy is : %s \n" +msgstr "Câu lệnh scp cho bản sao tập tin bạn bè là: %s\n" + +#: src/applications/testing/remote.c:535 +#, c-format +msgid "connecting peer %s:%d to peer %s:%d\n" +msgstr "Ä‘ang kết nối đồng đẳng %s:%d tá»›i đồng đẳng %s:%d\n" + +#: src/applications/testing/remotetest.c:38 +msgid "Set up multiple gnunetd daemons across multiple hosts." +msgstr "Thiết lập nhiá»u trình ná»n gnunetd qua nhiá»u máy khác nhau." + +#: src/applications/testing/remotetest.c:43 +msgid "set number of daemons to start" +msgstr "đặt số trình ná»n cần khởi chạy" + +#: src/applications/testing/testing.c:268 +#: src/applications/testing/remotetopologies.c:367 +#, c-format +msgid "Waiting for peers to connect" +msgstr "Äang đợi các đồng đẳng kết nối" + +#: src/applications/testing/remotetopologies.c:213 +#, c-format +msgid "Connecting nodes in 2d torus topology: %u rows %u columns\n" +msgstr "" +"Äang kết nối các Ä‘iểm nút theo địa hình há»c hình xuyến hai chiá»u: %u hàng, %" +"u cá»™t\n" + +#: src/applications/testing/remotetopologies.c:491 +#, c-format +msgid "Failed to establish connection with peers.\n" +msgstr "Lá»—i thiết lập kết nối vá»›i đồng đẳng.\n" + +#: src/applications/bootstrap_http/http.c:113 +#, c-format +msgid "Bootstrap data obtained from `%s' is invalid.\n" +msgstr "Sai dữ liệu nạp và khởi Ä‘á»™ng được lấy từ « %s ».\n" + +#: src/applications/bootstrap_http/http.c:126 +#: src/applications/bootstrap_http/http.c:277 +#: src/applications/bootstrap_http/http.c:294 +#: src/applications/bootstrap_http/http.c:333 +#: src/applications/bootstrap_http/http.c:352 +#: src/applications/bootstrap_http/http.c:365 +#: src/applications/bootstrap_http/http.c:375 +#: src/applications/bootstrap_http/http.c:385 src/transports/upnp/upnp.c:356 +#: src/transports/upnp/upnp.c:541 src/transports/http.c:1085 +#: src/transports/http.c:1209 src/transports/http.c:1377 +#: src/transports/http.c:1777 src/transports/http.c:1827 +#, c-format +msgid "%s failed at %s:%d: `%s'\n" +msgstr "%s bị lá»—i tại %s:%d: « %s »\n" + +#: src/applications/bootstrap_http/http.c:185 +msgid "No hostlist URL specified in configuration, will not bootstrap.\n" +msgstr "" +"ChÆ°a ghi rõ địa chỉ URL của danh sách các máy nên không nạp và khởi Ä‘á»™ng.\n" + +#: src/applications/bootstrap_http/http.c:226 +#, c-format +msgid "Bootstrapping using `%s'.\n" +msgstr "Äang nạp và khởi Ä‘á»™ng dùng « %s ».\n" + +#: src/applications/bootstrap_http/http.c:254 +#, c-format +msgid "Trying to download hostlist from `%s'\n" +msgstr "Äang thá»­ tải danh sách các máy xuống « %s »\n" + +#: src/applications/bootstrap_http/http.c:391 +#, c-format +msgid "Downloaded %llu bytes from `%s'.\n" +msgstr "Äã tải %llu byte xuống « %s ».\n" + +#: src/applications/bootstrap_http/http.c:425 +msgid "# HELLOs downloaded via http" +msgstr "# các HELLO tải xuống qua HTTP" + +#: src/applications/getoption/getoption.c:78 +#, c-format +msgid "`%s' registering client handler %d\n" +msgstr "« %s » Ä‘ang đăng ký trình Ä‘iá»u khiển máy khách %d\n" + +#: src/applications/getoption/getoption.c:88 +msgid "allows clients to determine gnunetd's configuration" +msgstr "cho phép máy khách quyết định cấu hình của trình ná»n gnunetd." + +#: src/applications/template/template.c:70 +#, c-format +msgid "`%s' registering client handler %d and %d\n" +msgstr "« %s » Ä‘ang đăng ký trình Ä‘iá»u khiển máy khách %d và %d\n" + +#: src/applications/template/gnunet-template.c:42 +msgid "Template description." +msgstr "Mô tả mẫu." + +#: src/applications/stats/clientapi.c:331 +msgid "Uptime (seconds)" +msgstr "Thá»i gian chạy (giây)" + +#: src/applications/stats/sqstats.c:151 +msgid "# Any-Blocks" +msgstr "# Khối bất kỳ" + +#: src/applications/stats/sqstats.c:152 +msgid "# DBlocks" +msgstr "# Khối D" + +#: src/applications/stats/sqstats.c:153 +msgid "# SBlocks" +msgstr "# Khối S" + +#: src/applications/stats/sqstats.c:154 +msgid "# KBlocks" +msgstr "# Khối K" + +#: src/applications/stats/sqstats.c:155 +msgid "# NBlocks" +msgstr "# Khối N" + +#: src/applications/stats/sqstats.c:156 +msgid "# KNBlocks" +msgstr "# Khối KN" + +#: src/applications/stats/sqstats.c:157 +msgid "# OnDemand-Blocks" +msgstr "# Khối theo yêu cầu" + +#: src/applications/stats/sqstats.c:158 +msgid "# Unknown-Blocks" +msgstr "# Khối không rõ" + +#: src/applications/stats/sqstats.c:159 +msgid "# expired" +msgstr "# đã hết hạn" + +#: src/applications/stats/sqstats.c:160 +msgid "# expire in 1h" +msgstr "# hết hạn trong 1 giá»" + +#: src/applications/stats/sqstats.c:161 +msgid "# expire in 24h" +msgstr "# hết hạn trong 24 giá»" + +#: src/applications/stats/sqstats.c:162 +msgid "# expire in 1 week" +msgstr "# hết hạn trong 1 tuần" + +#: src/applications/stats/sqstats.c:163 +msgid "# expire in 1 month" +msgstr "# hết hạn trong 1 tháng" + +#: src/applications/stats/sqstats.c:164 +msgid "# zero priority" +msgstr "# Æ°u tiên 0" + +#: src/applications/stats/sqstats.c:165 +msgid "# priority one" +msgstr "# Æ°u tiên 1" + +#: src/applications/stats/sqstats.c:166 +msgid "# priority larger than one" +msgstr "# Æ°u tiên >1" + +#: src/applications/stats/sqstats.c:167 +msgid "# no anonymity" +msgstr "# nặc danh 0" + +#: src/applications/stats/sqstats.c:168 +msgid "# anonymity one" +msgstr "# nặc danh 1" + +#: src/applications/stats/sqstats.c:169 +msgid "# anonymity larger than one" +msgstr "# nặc danh >1" + +#: src/applications/stats/statistics.c:238 +#, no-c-format +msgid "% of allowed network load (up)" +msgstr "% trá»ng tải mạng được phép (chạy)" + +#: src/applications/stats/statistics.c:240 +#, no-c-format +msgid "% of allowed network load (down)" +msgstr "% trá»ng tải mạng được phép (không chạy)" + +#: src/applications/stats/statistics.c:243 +#, no-c-format +msgid "% of allowed cpu load" +msgstr "% trá»ng tải CPU được phép" + +#: src/applications/stats/statistics.c:246 +#, no-c-format +msgid "% of allowed io load" +msgstr "% trá»ng tải V/R được phép" + +#: src/applications/stats/statistics.c:249 +msgid "# bytes of noise received" +msgstr "# các byte nhiá»…u được nhận" + +#: src/applications/stats/statistics.c:251 +msgid "# plibc handles" +msgstr "# các bá»™ xá»­ lý plibc" + +#: src/applications/stats/statistics.c:441 +#, c-format +msgid "`%s' registering client handlers %d %d %d and p2p handler %d\n" +msgstr "" +"« %s » Ä‘ang đăng ký các trình Ä‘iá»u khiển máy khách %d %d %d và trình Ä‘iá»u " +"khiển đồng đẳng %d\n" + +#: src/applications/stats/statistics.c:463 +msgid "keeps statistics about gnunetd's operation" +msgstr "tính thống kê vá» thao tác gnunetd" + +#: src/applications/stats/gnunet-stats.c:61 +#, c-format +msgid "Supported peer-to-peer messages:\n" +msgstr "Thông báo đồng đẳng được há»— trợ :\n" + +#: src/applications/stats/gnunet-stats.c:64 +#, c-format +msgid "Supported client-server messages:\n" +msgstr "Thông báo máy khách-chủ được há»— trợ :\n" + +#: src/applications/stats/gnunet-stats.c:83 +#: src/applications/vpn/gnunet-vpn.c:59 +msgid "Print statistics about GNUnet operations." +msgstr "In ra thống kê vá» các thao tác GNUnet." + +#: src/applications/stats/gnunet-stats.c:87 +msgid "prints supported protocol messages" +msgstr "in ra các thông báo giao thức được há»— trợ" + +#: src/applications/stats/gnunet-stats.c:136 +#, c-format +msgid "Error reading information from gnunetd.\n" +msgstr "Gặp lá»—i khi Ä‘á»c thông tin từ gnunetd.\n" + +#: src/applications/vpn/gnunet-vpn.c:63 +msgid "Suppress display of asynchronous log messages" +msgstr "Thu hồi hiển thị các thông Ä‘iệp ghi sá»± kiện không đồng bá»™" + +#: src/applications/vpn/p2p.c:75 +msgid "VPN IP src not anonymous. drop..\n" +msgstr "VPN Nguồn địa chỉ IP không phải nặc danh, bá» Ä‘i.\n" + +#: src/applications/vpn/p2p.c:83 +msgid "VPN IP not anonymous, drop.\n" +msgstr "VPN Äịa chỉ IP không phải nặc danh, bá» Ä‘i.\n" + +#: src/applications/vpn/p2p.c:92 +msgid "VPN Received, not anonymous, drop.\n" +msgstr "VPN Nhận được, không phải nặc danh, bá» Ä‘i.\n" + +#: src/applications/vpn/p2p.c:97 +#, c-format +msgid "VPN Received unknown IP version %d...\n" +msgstr "VPN Nhận được phiên bản IP không rõ %d...\n" + +#: src/applications/vpn/p2p.c:110 +#, c-format +msgid "<- GNUnet(%d) : %s\n" +msgstr "<- GNUnet(%d) : %s\n" + +#: src/applications/vpn/p2p.c:139 +msgid "Could not write the tunnelled IP to the OS... Did to setup a tunnel?\n" +msgstr "" +"Không thể ghi vào HÄH địa chỉ IP theo Ä‘Æ°á»ng hầm... Äã làm để thiết lập má»™t " +"Ä‘Æ°á»ng hầm ?\n" + +#: src/applications/vpn/p2p.c:183 +msgid "Receive route request\n" +msgstr "Nhận yêu cầu định tuyến\n" + +#: src/applications/vpn/p2p.c:193 +#, c-format +msgid "Prepare route announcement level %d\n" +msgstr "Chuẩn bị thông cáo định tuyến cấp %d\n" + +#: src/applications/vpn/p2p.c:208 +#, c-format +msgid "Send route announcement %d with route announce\n" +msgstr "Gá»­i thông cáo định tuyến %d vá»›i Ä‘Æ°á»ng đã báo\n" + +#: src/applications/vpn/p2p.c:217 +#, c-format +msgid "Send outside table info %d\n" +msgstr "Gá»­i thông tin bảng bên ngoài %d\n" + +#: src/applications/vpn/p2p.c:239 +msgid "Receive route announce.\n" +msgstr "Nhận thông cáo định tuyến.\n" + +#: src/applications/vpn/p2p.c:247 +msgid "Going to try insert route into local table.\n" +msgstr "Sẽ thá»­ chèn Ä‘Æ°á»ng vào bảng cục bá»™.\n" + +#: src/applications/vpn/p2p.c:256 +#, c-format +msgid "Inserting with hops %d\n" +msgstr "Äang chèn vá»›i số bÆ°á»›c nhảy %d\n" + +#: src/applications/vpn/p2p.c:273 +#, c-format +msgid "Request level %d from peer %d\n" +msgstr "Yêu cầu cấp %d từ đồng đẳng %d\n" + +#: src/applications/vpn/p2p.c:300 +#, c-format +msgid "Receive table limit on peer reached %d\n" +msgstr "Giá»›i hạn bảng nhận trên đồng đẳng đã tá»›i %d\n" + +#: src/applications/vpn/vpn.c:180 +#, c-format +msgid "Not storing route to myself from peer %d\n" +msgstr "Không phải lÆ°u lại Ä‘Æ°á»ng đến máy này từ đồng đẳng %d\n" + +#: src/applications/vpn/vpn.c:194 +#, c-format +msgid "Duplicate route to node from peer %d, choosing minimum hops" +msgstr "" +"ÄÆ°á»ng trùng đến nút từ đồng đẳng %d, Ä‘ang chá»n Ä‘Æ°á»ng có ít bÆ°á»›c nhảy hÆ¡n" + +#: src/applications/vpn/vpn.c:230 +#, c-format +msgid "Inserting route from peer %d in route table at location %d\n" +msgstr "Äang chèn Ä‘Æ°á»ng từ đồng đẳng %d vào bảng định tuyến tại vị trí %d\n" + +#: src/applications/vpn/vpn.c:247 +#, c-format +msgid "RFC4193 Frame length %d is too big for GNUnet!\n" +msgstr "RFC4193 Chiá»u dài khung %d quá lá»›n cho GNUnet!\n" + +#: src/applications/vpn/vpn.c:254 +#, c-format +msgid "RFC4193 Frame length %d too small\n" +msgstr "RFC4193 Chiá»u dài khung %d quá nhá»\n" + +#: src/applications/vpn/vpn.c:273 +#, c-format +msgid "RFC4193 Ethertype %x and IP version %x do not match!\n" +msgstr "RFC4193 Ethertype %x và phiên bản IP %x không tÆ°Æ¡ng ứng.\n" + +#: src/applications/vpn/vpn.c:289 +#, c-format +msgid "RFC4193 Going to try and make a tunnel in slot %d\n" +msgstr "RFC4193 Sẽ thá»­ tạo má»™t Ä‘Æ°á»ng hầm trong khoảng %d\n" + +#: src/applications/vpn/vpn.c:295 +#, fuzzy, c-format +msgid "Cannot open tunnel device: %s" +msgstr "Không thể mở thiết bị Ä‘Æ°á»ng hầm do %s" + +#: src/applications/vpn/vpn.c:331 +#, c-format +msgid "RFC4193 Create skips gnu%d as we are already using it\n" +msgstr "RFC4193 Chức năng tạo sẽ ná» qua gnu%d vì chúng ta Ä‘ang dùng nó\n" + +#: src/applications/vpn/vpn.c:346 +#, c-format +msgid "Cannot set tunnel name to %s because of %s\n" +msgstr "không thể đặt tên Ä‘Æ°á»ng hầm thành %s do %s\n" + +#: src/applications/vpn/vpn.c:356 +#, c-format +msgid "Configured tunnel name to %s\n" +msgstr "Tên Ä‘Æ°á»ng hầm đã được cấu hình thành %s\n" + +#: src/applications/vpn/vpn.c:398 +#, c-format +msgid "Cannot get socket flags for gnu%d because %s\n" +msgstr "Không thể lấy các cỠổ cắm cho gnu%d do %s\n" + +#: src/applications/vpn/vpn.c:408 +#, c-format +msgid "Cannot set socket flags for gnu%d because %s\n" +msgstr "Không thể đặt các cỠổ cắm cho gnu%d do %s\n" + +#: src/applications/vpn/vpn.c:418 +#, c-format +msgid "Cannot set MTU for gnu%d because %s\n" +msgstr "Không thể đặt MTU cho gnu%d do %s\n" + +#: src/applications/vpn/vpn.c:426 +#, c-format +msgid "Cannot get interface index for gnu%d because %s\n" +msgstr "Không thể lấy chủ mục giao diện cho gnu%d do %s\n" + +#: src/applications/vpn/vpn.c:440 +#, c-format +msgid "IPv6 ifaddr gnu%d - %x:%x:%x:%x:%x:%x:%x:%x/%d\n" +msgstr "IPv6 ifaddr gnu%d - %x:%x:%x:%x:%x:%x:%x:%x/%d\n" + +#: src/applications/vpn/vpn.c:455 +#, c-format +msgid "Cannot set interface IPv6 address for gnu%d because %s\n" +msgstr "Không thể đặt địa chỉ IPv6 của giao diện cho gnu%d do %s\n" + +#: src/applications/vpn/vpn.c:471 +#, c-format +msgid "IPv6 route gnu%d - destination %x:%x:%x:%x:%x:%x:%x:%x/%d\n" +msgstr "ÄÆ°á»ng IPv6 gnu%d - đích đến %x:%x:%x:%x:%x:%x:%x:%x/%d\n" + +#: src/applications/vpn/vpn.c:485 +#, c-format +msgid "Cannot add route IPv6 address for gnu%s because %s\n" +msgstr "Không thể thêm địa chỉ IPv6 của Ä‘Æ°á»ng cho gnu%s do %s\n" + +#: src/applications/vpn/vpn.c:528 +msgid "" +"RFC4193 We have run out of memory and so I can't store a tunnel for this " +"peer.\n" +msgstr "" +"RFC4193 Tràn bá»™ nhá»› thì không thể cất giữ má»™t Ä‘Æ°á»ng hầm cho đồng đẳng này.\n" + +#: src/applications/vpn/vpn.c:579 +#, c-format +msgid "RFC4193 Thread running (frame %d tunnel %d f2f %d) ...\n" +msgstr "RFC4193 Mạch Ä‘ang chạy (khung %d Ä‘Æ°á»ng hầm %d f2f %d) ...\n" + +#: src/applications/vpn/vpn.c:661 +#, c-format +msgid "VPN dropping connection %x\n" +msgstr "VPN Ä‘ang bá» Ä‘i kết nối %x\n" + +#: src/applications/vpn/vpn.c:670 +#, c-format +msgid "VPN cannot drop connection %x\n" +msgstr "VPN không thể bá» Ä‘i kết nối %x\n" + +#: src/applications/vpn/vpn.c:690 +msgid "RFC4193 Thread exiting\n" +msgstr "RFC4193 Mạch Ä‘ang thoát\n" + +#: src/applications/vpn/vpn.c:712 +msgid "realise alloc ram\n" +msgstr "realise cấp phát bá»™ nhá»› RAM\n" + +#: src/applications/vpn/vpn.c:735 +msgid "realise add routes\n" +msgstr "realise thêm Ä‘Æ°á»ng\n" + +#: src/applications/vpn/vpn.c:849 +msgid "realise copy table\n" +msgstr "realise chép bảng\n" + +#: src/applications/vpn/vpn.c:898 +#, c-format +msgid "`%s' initialising RFC4913 module %d and %d\n" +msgstr "« %s » Ä‘ang sÆ¡ khởi mô-Ä‘un RFC4913 %d và %d\n" + +#: src/applications/vpn/vpn.c:903 +#, c-format +msgid "RFC4193 my First 4 hex digits of host id are %x\n" +msgstr "RFC4193 my 4 chữ số thập lúc đầu tiên của mã số máy là %x\n" + +#: src/applications/vpn/vpn.c:942 +msgid "enables IPv6 over GNUnet (incomplete)" +msgstr "hiệu lá»±c IPv6 qua GNUnet (chÆ°a hoàn tất)" + +#: src/applications/vpn/vpn.c:963 +msgid "RFC4193 Waiting for tun thread to end\n" +msgstr "RFC4193 Äang đợi kết thúc mạch tun\n" + +#: src/applications/vpn/vpn.c:978 +msgid "RFC4193 The tun thread has ended\n" +msgstr "RFC4193 Mạch tun đã kết thúc\n" + +#: src/applications/vpn/vpn.c:996 +#, c-format +msgid "RFC4193 Closing tunnel %d fd %d\n" +msgstr "RFC4193 Äang đóng Ä‘Æ°á»ng hầm %d fd %d\n" + +#: src/server/core.c:119 src/server/core.c:318 +#, c-format +msgid "Configuration value `%s' under [MODULES] for `%s' is invalid!\n" +msgstr "Sai giá trị cấu hình « %s » dÆ°á»›i [MÔ-ÄUN] cho « %s ».\n" + +#: src/server/core.c:140 +#, c-format +msgid "Application module `%s' already initialized!\n" +msgstr "Mô-Ä‘un ứng dụng « %s » đã được sở khởi.\n" + +#: src/server/core.c:194 +#, c-format +msgid "Failed to load plugin `%s' at %s:%d. Unloading plugin.\n" +msgstr "Lá»—i nạp phần bổ sung « %s » tại %s:%d. Äang hủy nạp phần bổ sung.\n" + +#: src/server/core.c:244 +#, c-format +msgid "Could not shutdown `%s': application not loaded\n" +msgstr "Không thể tắt « %s »: chÆ°a nạp ứng dụng\n" + +#: src/server/core.c:255 +#, c-format +msgid "Could not shutdown application `%s': not initialized\n" +msgstr "Không thể tắt ứng dụng « %s »: chÆ°a được sÆ¡ khởi\n" + +#: src/server/core.c:265 +#, c-format +msgid "Could not find '%s%s' method in library `%s'.\n" +msgstr "Không tìm thấy phÆ°Æ¡ng pháp « %s%s » trong thÆ° viện « %s ».\n" + +#: src/server/core.c:422 +#, c-format +msgid "Could not release %p: service not loaded\n" +msgstr "Không thể giải phóng %p: chÆ°a nạp dịch vụ\n" + +#: src/server/core.c:531 +#, c-format +msgid "Could not properly shutdown application `%s'.\n" +msgstr "Không thể tắt đúng ứng dụng « %s ».\n" + +#: src/server/core.c:676 +#, c-format +msgid "Could not properly unload service `%s'!\n" +msgstr "Không thể hủy nạp đúng dịch vụ « %s ».\n" + +#: src/server/gnunet-update.c:146 +#, c-format +msgid "Updating data for module `%s'\n" +msgstr "Äang cập nhật dữ liệu cho mô-Ä‘un « %s »\n" + +#: src/server/gnunet-update.c:151 +#, c-format +msgid "Failed to update data for module `%s'\n" +msgstr "Lá»—i cập nhật dữ liệu cho mô-Ä‘un « %s »\n" + +#: src/server/gnunet-update.c:225 src/server/gnunetd.c:124 +msgid "Core initialization failed.\n" +msgstr "Lá»—i sÆ¡ khởi lõi.\n" + +#: src/server/gnunet-update.c:270 +msgid "Updates GNUnet datastructures after version change." +msgstr "Cập nhật cấu trúc dữ liệu GNUnet sau khi thay đổi phiên bản." + +#: src/server/gnunet-update.c:274 src/server/gnunet-transport-check.c:376 +msgid "run as user LOGIN" +msgstr "chạy dÆ°á»›i ngÆ°á»i dùng ÄÄ‚NG_NHẬP" + +#: src/server/gnunet-update.c:278 +msgid "run in client mode (for getting client configuration values)" +msgstr "chạy trong chế Ä‘á»™ khách (để lấy các giá trị cấu hình của máy khách)" + +#: src/server/version.c:125 +msgid "" +"Failed to determine filename used to store GNUnet version information!\n" +msgstr "" +"Lá»—i quyết định tên tập tin được dùng để chứa thông tin vá» phiên bản GNUnet.\n" + +#: src/server/gnunetd.c:85 +#, c-format +msgid "`%s' startup complete.\n" +msgstr "Hoàn thành khởi chạy « %s ».\n" + +#: src/server/gnunetd.c:89 +#, c-format +msgid "`%s' is shutting down.\n" +msgstr "« %s » Ä‘ang tắt.\n" + +#: src/server/gnunetd.c:179 +msgid "" +"run in debug mode; gnunetd will not daemonize and error messages will be " +"written to stderr instead of a logfile" +msgstr "" +"chạy trong chế Ä‘á»™ tìm sá»­a lá»—i; gnunetd sẽ không trở thành trình ná»n và sẽ " +"ghi thông báo lá»—i ra đầu lá»—i tiêu chuẩn thay vì vào má»™t tập tin ghi sá»± kiện." + +#: src/server/gnunetd.c:183 +msgid "Starts the gnunetd daemon." +msgstr "Khởi chạy trình ná»n gnunetd." + +#: src/server/gnunetd.c:186 +msgid "disable padding with random data (experimental)" +msgstr "tắt đệm lót vá»›i dữ liệu ngẫu nhiên (vẫn thá»±c nghiệm !)" + +#: src/server/gnunetd.c:190 +msgid "print all log messages to the console (only works together with -d)" +msgstr "" +"in má»i thông Ä‘iệp ghi sá»± kiện ra bàn giao tiếp (chỉ hoạt Ä‘á»™ng tổ hợp vá»›i « -d " +"»)" + +#: src/server/gnunetd.c:194 +msgid "specify username as which gnunetd should run" +msgstr "chỉ ra tên ngÆ°á»i dùng dÆ°á»›i đó cần chạy gnunetd" + +#: src/server/gnunetd.c:275 +#, c-format +msgid "Configuration or GNUnet version changed. You need to run `%s'!\n" +msgstr "" +"Cấu hình hoặc phiên bản GNUnet bị thay đổi. NgÆ°á»i dùng cần chạy « %s ».\n" + +#: src/server/tcpserver.c:121 +#, c-format +msgid "The `%s' request received from client is malformed.\n" +msgstr "Nhận được yêu cầu « %s » dạng sai từ máy khách.\n" + +#: src/server/tcpserver.c:409 +#, c-format +msgid "`%s' failed for port %d. Is gnunetd already running?\n" +msgstr "« %s » bị lá»—i cho cổng %d. Trình gnunetd có chạy chÆ°a?\n" + +#: src/server/tcpserver.c:487 src/server/tcpserver.c:512 +#, c-format +msgid "" +"Malformed network specification in the configuration in section `%s' for " +"entry `%s': %s\n" +msgstr "" +"Äặc tả mạng dạng sai trong cấu hình phần « %s » cho mục nhập « %s »: %s\n" + +#: src/server/tcpserver.c:572 +#, c-format +msgid "Registering failed, message type %d already in use.\n" +msgstr "Lá»—i đăng ký, thông Ä‘iệp kiểu %d Ä‘ang được dùng.\n" + +#: src/server/startup.c:219 +#, c-format +msgid "Unable to obtain filesystem information for `%s': %u\n" +msgstr "Không thể đại được thông tin vá» hệ thống tập tin cho « %s »: %u\n" + +#: src/server/startup.c:237 +#, c-format +msgid "" +"Filesystem `%s' of partition `%s' is unknown. Please contact gnunet-" +"developers@gnu.org!" +msgstr "" +"Hệ thống tập tin « %s » của phân vùng « %s » không rõ. Hãy liên lạc vá»›i các " +"nhà phát triển gnunet ở địa chỉ « gnunet-developers@gnu.org »." + +#: src/server/startup.c:252 +#, c-format +msgid "" +"Limiting datastore size to %llu GB, because the `%s' filesystem does not " +"support larger files. Please consider storing the database on a NTFS " +"partition!\n" +msgstr "" +"Äang hạn chế kích cỡ kho dữ liệu thành %llu GB, vì hệ thống tập tin « %s » " +"không há»— trợ tập tin lá»›n hÆ¡n đó. Khuyên bạn cất giữ cÆ¡ sở dữ liệu trong má»™t " +"phiên bản kiểu NTFS.\n" + +#: src/server/startup.c:291 +#, c-format +msgid "Insufficient access permissions for `%s': %s\n" +msgstr "Không đủ quyá»n truy cập cho « %s »: %s\n" + +#: src/server/gnunet-peer-info.c:55 +msgid "Print information about GNUnet peers." +msgstr "In ra thông tin vá» các đồng đẳng GNUnet." + +#: src/server/gnunet-peer-info.c:59 +msgid "don't resolve host names" +msgstr "không quyết định các tên máy" + +#: src/server/gnunet-peer-info.c:62 +msgid "output only the identity strings" +msgstr "chỉ xuất những chuá»—i nhận diện" + +#: src/server/gnunet-peer-info.c:65 +msgid "output our own identity only" +msgstr "chỉ xuất nhận diện mình" + +#: src/server/gnunet-peer-info.c:130 src/server/gnunet-peer-info.c:164 +#, c-format +msgid "Could not get address of peer `%s'.\n" +msgstr "Không thể lấy địa chỉ của đồng đẳng « %s ».\n" + +#: src/server/gnunet-peer-info.c:143 +#, c-format +msgid "`%s' message invalid (signature invalid).\n" +msgstr "Thông báo « %s » không hợp lệ (chữ ký sai).\n" + +#: src/server/gnunet-peer-info.c:168 +#, c-format +msgid "Peer `%s' with trust %8u\n" +msgstr "Äồng đẳng « %s » có mức tin cậy %8u\n" + +#: src/server/gnunet-peer-info.c:175 +#, c-format +msgid "Peer `%s' with trust %8u and address `%s'\n" +msgstr "Äồng đẳng « %s » có mức tin cậy %8u và địa chỉ « %s »\n" + +#: src/server/connection.c:1313 +#, c-format +msgid "`%s' selected %d out of %d messages (MTU: %d).\n" +msgstr "« %s » đã chá»n %d trên %d thông báo (MTU: %d).\n" + +#: src/server/connection.c:1323 +#, c-format +msgid "Message details: %u: length %d, priority: %d\n" +msgstr "Chi tiết vá» thông báo: %u: dài %d, Æ°u tiên: %d\n" + +#: src/server/connection.c:3129 +#, c-format +msgid "Message from `%s' discarded: invalid format.\n" +msgstr "Thông báo từ « %s » bị hủy: định dạng sai.\n" + +#: src/server/connection.c:3218 +#, c-format +msgid "Invalid sequence number %u <= %u, dropping message.\n" +msgstr "Sai số thứ tá»± dãy trong thông báo %u ≤ %u, Ä‘ang loại bá» thông báo.\n" + +#: src/server/connection.c:3240 +msgid "Message received more than one day old. Dropped.\n" +msgstr "Thông báo nhận được cÅ© hÆ¡n má»™t ngày. Äã loại bá».\n" + +#: src/server/connection.c:3763 +msgid "# outgoing messages dropped" +msgstr "# các thông báo gá»­i Ä‘i bị loại bá»" + +#: src/server/connection.c:3766 +msgid "# bytes of outgoing messages dropped" +msgstr "# các byte thông báo gá»­i Ä‘i bị loại bá»" + +#: src/server/connection.c:3768 +msgid "# connections closed (HANGUP sent)" +msgstr "# các kết nối bị đóng (gá»­i tín hiệu ngừng nói HANGUP)" + +#: src/server/connection.c:3772 +msgid "# connections closed (transport issue)" +msgstr "# các kết nối bị đóng (vấn Ä‘á» truyá»n tải)" + +#: src/server/connection.c:3775 +msgid "# bytes encrypted" +msgstr "# các byte đã mã hoá" + +#: src/server/connection.c:3779 +msgid "# bytes transmitted" +msgstr "# các byte được gá»­i" + +#: src/server/connection.c:3783 +msgid "# bytes received" +msgstr "# các byte được nhận" + +#: src/server/connection.c:3785 +msgid "# bytes decrypted" +msgstr "# các byte đã giải mã" + +#: src/server/connection.c:3786 +msgid "# bytes noise sent" +msgstr "# các byte nhiá»…u được gá»­i" + +#: src/server/connection.c:3789 +msgid "# total bytes per second send limit" +msgstr "# giá»›i hạn tổng số byte má»—i giây" + +#: src/server/connection.c:3792 +msgid "# total bytes per second receive limit" +msgstr "# giá»›i hạn nhận tổng số byte má»—i giây" + +#: src/server/connection.c:3795 +msgid "# total number of messages in send buffers" +msgstr "# tổng số thông báo trong bá»™ đệm gá»­i" + +#: src/server/connection.c:3798 +msgid "# total number of bytes we were allowed to send but did not" +msgstr "# tổng số byte được phép gá»­i mà chÆ°a" + +#: src/server/connection.c:3801 +msgid "# total number of bytes we were allowed to sent" +msgstr "# tổng số byte được phép gá»­i" + +#: src/server/connection.c:3804 +msgid "# total number of bytes we are currently allowed to send" +msgstr "# tổng số byte hiện thá»i được phép gá»­i" + +#: src/server/connection.c:3807 +msgid "# transports switched to stream transport" +msgstr "# các truyá»n tải được chuyển sang truyá»n tải luồng" + +#: src/server/connection.c:3810 +msgid "# average connection lifetime (in ms)" +msgstr "# thá»i gian trung bình còn kết nối (theo miligiây)" + +#: src/server/connection.c:3813 +msgid "# conn. shutdown: other peer sent too much" +msgstr "# kết nối bị ngắt: đồng đẳng khác đã gá»­i quá nhiá»u" + +#: src/server/connection.c:3816 +msgid "# conn. shutdown: we lacked bandwidth" +msgstr "# kết nối bị ngắt: không đủ băng thông." + +#: src/server/connection.c:3819 +msgid "# conn. shutdown: other peer timed out" +msgstr "# kết nối bị ngắt: đồng đẳng khác quá hạn" + +#: src/server/connection.c:3822 +msgid "# conn. shutdown: timed out during connect" +msgstr "# kết nối bị ngắt: quá hạn trong khi thiết lập kết nối" + +#: src/server/connection.c:3825 +msgid "# conn. shutdown: other peer requested it" +msgstr "# kết nối bị ngắt: theo yêu cầu của đồng đẳng khác" + +#: src/server/handler.c:442 +#, c-format +msgid "Received corrupt message from peer `%s' in %s:%d.\n" +msgstr "Nhận được thông báo bị há»ng từ đồng đẳng « %s » trong %s:%d.\n" + +#: src/server/gnunet-transport-check.c:121 +#, c-format +msgid "`%s': Could not create hello.\n" +msgstr "« %s »: Không tạo được tín hiệu xin chào (hello).\n" + +#: src/server/gnunet-transport-check.c:129 +#, c-format +msgid "`%s': Could not connect.\n" +msgstr "« %s »: Không thể kết nối.\n" + +#: src/server/gnunet-transport-check.c:163 +#, c-format +msgid "`%s': Could not send.\n" +msgstr "« %s »: Không thể gá»­i.\n" + +#: src/server/gnunet-transport-check.c:179 +#, c-format +msgid "`%s': Did not receive message within %llu ms.\n" +msgstr "« %s »: ChÆ°a nhận thông báo sau %llu miligiây.\n" + +#: src/server/gnunet-transport-check.c:192 +#, c-format +msgid "`%s': Could not disconnect.\n" +msgstr "« %s »: Không thể ngắt kết nối.\n" + +#: src/server/gnunet-transport-check.c:200 +#, c-format +msgid "" +"`%s' transport OK. It took %ums to transmit %llu messages of %llu bytes " +"each.\n" +msgstr "" +"« %s » truyá»n tải OK. Cần %u miligiây để truyá»n Ä‘i %llu thông báo vá»›i %llu " +"byte má»—i cái.\n" + +#: src/server/gnunet-transport-check.c:231 +#, c-format +msgid " Transport %d is not being tested\n" +msgstr " Truyá»n tải %d không Ä‘ang thá»­\n" + +#: src/server/gnunet-transport-check.c:261 +#, c-format +msgid "" +"\n" +"Contacting `%s'." +msgstr "" +"\n" +"Äang liên lạc vá»›i « %s »." + +#: src/server/gnunet-transport-check.c:286 +#, c-format +msgid " Connection failed\n" +msgstr " Lá»—i kết nối\n" + +#: src/server/gnunet-transport-check.c:292 +#, c-format +msgid " Connection failed (bug?)\n" +msgstr " Không kết nối được (lá»—i ?)\n" + +#: src/server/gnunet-transport-check.c:330 +#, c-format +msgid "Timeout after %llums.\n" +msgstr "Quá hạn sau %llu miligiây.\n" + +#: src/server/gnunet-transport-check.c:332 +#, c-format +msgid "OK!\n" +msgstr "OK!\n" + +#: src/server/gnunet-transport-check.c:357 +msgid "Tool to test if GNUnet transport services are operational." +msgstr "" +"Công cụ để thá»­ nghiệm xem dịch vụ truyá»n tải GNUnet có hoạt Ä‘á»™ng không." + +#: src/server/gnunet-transport-check.c:361 +msgid "ping peers from HOSTLISTURL that match transports" +msgstr "" +"gá»­i tín hiệu ping cho các đồng đẳng từ HOSTLISTURL (danh sách máy theo URL) " +"tÆ°Æ¡ng ứng vá»›i truyá»n tải" + +#: src/server/gnunet-transport-check.c:364 +msgid "send COUNT messages" +msgstr "gá»­i ÄẾM thông báo" + +#: src/server/gnunet-transport-check.c:367 +msgid "send messages with SIZE bytes payload" +msgstr "gá»­i thông báo có KÃCH_Cá»  byte tải trá»ng" + +#: src/server/gnunet-transport-check.c:370 +msgid "specifies which TRANSPORT should be tested" +msgstr "chỉ ra TRUYỀN_TẢI nào cần được thá»­ nghiệm" + +#: src/server/gnunet-transport-check.c:373 +msgid "specifies after how many MS to time-out" +msgstr "chỉ ra hết thá»i gian chá» sau bao nhiêu miligiay" + +#: src/server/gnunet-transport-check.c:381 +msgid "repeat each test X times" +msgstr "lặp lại má»—i hàm thá»­ X lần" + +#: src/server/gnunet-transport-check.c:449 +#, c-format +msgid "Testing transport(s) %s\n" +msgstr "Äang thá»­ nghiệm (các) truyá»n tải %s\n" + +#: src/server/gnunet-transport-check.c:451 +#, c-format +msgid "Available transport(s): %s\n" +msgstr "Các truyá»n tải sẵn sàng: %s\n" + +#: src/server/gnunet-transport-check.c:501 +#, c-format +msgid "" +"\n" +"%d out of %d peers contacted successfully (%d times transport unavailable).\n" +msgstr "" +"\n" +"Äã liên lạc thành công vá»›i %d trên %d đồng đẳng (truyá»n tải không sẵn sàng %" +"d lần).\n" + +#: src/transports/common.c:370 +#, c-format +msgid "Port is 0, will only send using %s.\n" +msgstr "Cổng là 0, chỉ sẽ gá»­i dùng %s.\n" + +#: src/transports/smtp.c:367 src/transports/udp.c:107 src/transports/tcp.c:271 +#: src/transports/tcp.c:291 +#, c-format +msgid "Received malformed message via %s. Ignored.\n" +msgstr "Nhận được thông báo dạng sai qua %s. Bị bá» qua.\n" + +#: src/transports/smtp.c:459 +msgid "SMTP filter string to invalid, lacks ': '\n" +msgstr "Chuá»—i lá»c vào SMTP không hợp lệ, còn thiếu « : »\n" + +#: src/transports/smtp.c:469 +#, c-format +msgid "SMTP filter string to long, capped to `%s'\n" +msgstr "Chuá»—i lá»c vào SMTP quá dài, tối Ä‘a « %s »\n" + +#: src/transports/smtp.c:564 src/transports/smtp.c:575 +#: src/transports/smtp.c:589 src/transports/smtp.c:609 +#: src/transports/smtp.c:634 src/transports/smtp.c:643 +#: src/transports/smtp.c:657 src/transports/smtp.c:669 +#, c-format +msgid "SMTP: `%s' failed: %s.\n" +msgstr "SMTP: « %s » bị lá»—i: %s\n" + +#: src/transports/smtp.c:814 +msgid "No email-address specified, can not start SMTP transport.\n" +msgstr "ChÆ°a ghi rõ địa chỉ thÆ° Ä‘iện tá»­ nên không tạo được truyá»n tải SMTP.\n" + +#: src/transports/smtp.c:831 +msgid "# bytes received via SMTP" +msgstr "# các byte đã nhận qua SMTP" + +#: src/transports/smtp.c:832 +msgid "# bytes sent via SMTP" +msgstr "# các byte đã gá»­i qua SMTP" + +#: src/transports/smtp.c:834 +msgid "# bytes dropped by SMTP (outgoing)" +msgstr "# các byte loại Ä‘i bởi SMTP (Ä‘i ra)" + +#: src/transports/upnp/upnp.c:431 +#, c-format +msgid "%s failed for url `%s' and post-data `%s' at %s:%d: `%s'\n" +msgstr "" +"%s bị lá»—i đối vá»›i địa chỉ URL « %s » và dữ liệu cuối « %s » tại %s:%d: « %s »\n" + +#: src/transports/upnp/upnp.c:476 +#, c-format +msgid "upnp: NAT Returned IP: %s\n" +msgstr "upnp: địa chỉ IP được NAT trả vá»: %s\n" + +#: src/transports/http.c:2019 src/transports/tcp.c:811 +#, c-format +msgid "" +"The UPnP service could not be loaded. To disable UPnP, set the configuration " +"option \"UPNP\" in section \"%s\" to \"NO\"\n" +msgstr "" +"Dịch vụ UPnP không thể nạp được. Äể tắt UPnP, đặt tùy chá»n cấu hình « UPNP » " +"trong phần « %s » thành « NO » (không).\n" + +#: src/transports/http.c:2028 +msgid "# bytes received via HTTP" +msgstr "# các byte đã nhận qua HTTP" + +#: src/transports/http.c:2029 +msgid "# bytes sent via HTTP" +msgstr "# các byte đã gá»­i qua HTTP" + +#: src/transports/http.c:2031 +msgid "# bytes dropped by HTTP (outgoing)" +msgstr "# các byte loại bá» bởi HTTP (Ä‘i ra)" + +#: src/transports/http.c:2032 +msgid "# HTTP GET issued" +msgstr "# Tín hiệu HTTP GET được gá»­i" + +#: src/transports/http.c:2034 +msgid "# HTTP GET received" +msgstr "# Tín hiệu HTTP GET được nhận" + +#: src/transports/http.c:2035 +msgid "# HTTP PUT issued" +msgstr "# Tín hiệu HTTP PUT được gá»­i" + +#: src/transports/http.c:2037 +msgid "# HTTP PUT received" +msgstr "# Tín hiệu HTTP PUT được nhận" + +#: src/transports/http.c:2039 +msgid "# HTTP select calls" +msgstr "# các cuá»™c gá»i HTTP select (lá»±a chá»n)" + +#: src/transports/http.c:2041 +msgid "# HTTP send calls" +msgstr "# các cuá»™c gá»i HTTP send (gá»­i)" + +#: src/transports/http.c:2044 +msgid "# HTTP curl send callbacks" +msgstr "# các cuá»™c gá»i ngược HTTP curl send (hàm curl gá»­i)" + +#: src/transports/http.c:2046 +msgid "# HTTP curl receive callbacks" +msgstr "# các cuá»™c gá»i ngược HTTP curl receive (hàm curl nhận)" + +#: src/transports/http.c:2048 +msgid "# HTTP mhd access callbacks" +msgstr "# các cuá»™c gá»i ngược HTTP mhd access (hàm mhd truy cập)" + +#: src/transports/http.c:2050 +msgid "# HTTP mhd read callbacks" +msgstr "# các cuá»™c gá»i ngược HTTP mhd read (hàm mhd Ä‘á»c)" + +#: src/transports/http.c:2052 +msgid "# HTTP mhd close callbacks" +msgstr "# các cuá»™c gá»i ngược HTTP mhd close (hàm mhd đóng)" + +#: src/transports/http.c:2054 +msgid "# HTTP connect calls" +msgstr "# các cuá»™c gá»i HTTP connect (kết nối)" + +#: src/transports/ip.c:70 src/transports/ip.c:365 +#, c-format +msgid "Failed to obtain my (external) %s address!\n" +msgstr "Lá»—i lấy địa chỉ %s (bên ngoài) của mình.\n" + +#: src/transports/udp.c:472 src/transports/tcp.c:728 +#, c-format +msgid "Failed to bind to %s port %d.\n" +msgstr "Lá»—i đóng kết đến cổng %s %d.\n" + +#: src/transports/udp.c:538 +#, c-format +msgid "MTU %llu for `%s' is probably too low!\n" +msgstr "MTU %llu cho « %s » rất có thể quá thấp.\n" + +#: src/transports/udp.c:562 +msgid "# bytes received via UDP" +msgstr "# các byte đã nhận qua UDP" + +#: src/transports/udp.c:563 +msgid "# bytes sent via UDP" +msgstr "# các byte đã gá»­i qua UDP" + +#: src/transports/udp.c:565 +msgid "# bytes dropped by UDP (outgoing)" +msgstr "# các byte loại bá» bởi UDP (Ä‘i ra)" + +#: src/transports/udp.c:567 +msgid "# UDP connections (right now)" +msgstr "# các kết nối UDP (lúc này)" + +#: src/transports/tcp.c:821 +msgid "# bytes received via TCP" +msgstr "# các byte đã nhận qua TCP" + +#: src/transports/tcp.c:822 +msgid "# bytes sent via TCP" +msgstr "# các byte đã gừi qua TCP" + +#: src/transports/tcp.c:824 +msgid "# bytes dropped by TCP (outgoing)" +msgstr "# các byte loại Ä‘i bởi TCP (Ä‘i ra)" + +#: src/include/gnunet_util_getopt.h:154 +msgid "print this help" +msgstr "hiển thị trợ giúp này" + +#: src/include/gnunet_util_getopt.h:163 +msgid "print the version number" +msgstr "hiển thị số thứ tá»± phiên bản" + +#: src/include/gnunet_util_getopt.h:169 +msgid "configure logging to use LOGLEVEL" +msgstr "cấu hình chức năng ghi sá»± kiện để dùng CẤP_GHI_LƯU" + +#: src/include/gnunet_util_getopt.h:175 +msgid "be verbose" +msgstr "xuất chi tiết" + +#: src/include/gnunet_util_getopt.h:181 +msgid "use configuration file FILENAME" +msgstr "dùng tập tin cấu hình TÊN_TẬP_TIN" + +#: src/include/gnunet_util_getopt.h:187 +msgid "specify host on which gnunetd is running" +msgstr "ghi rõ máy trên đó chạy trình ná»n gnunetd" + +#: src/include/gnunet_util_error.h:219 src/include/gnunet_util_error.h:224 +#: src/include/gnunet_util_error.h:230 src/include/gnunet_util_error.h:232 +#, c-format +msgid "Internal error: assertion failed at %s:%d.\n" +msgstr "Lá»—i ná»™i bá»™ : khẳng định không thành công tại %s:%d.\n" + +#: src/include/gnunet_util_error.h:242 +#, c-format +msgid "" +"External protocol violation: assertion failed at %s:%d (no need to panic, we " +"can handle this).\n" +msgstr "" +"Xâm phạm giao thức bên ngoài: khẳng định không thành công tại %s:%d (đừng " +"lo, vẫn còn có thể sá»­a chữa).\n" + +#: src/include/gnunet_util_error.h:270 src/include/gnunet_util_error.h:277 +#, c-format +msgid "`%s' failed on file `%s' at %s:%d with error: %s\n" +msgstr "« %s » thất bại ở tập tin « %s » tại %s:%d vá»›i lá»—i: %s\n" + +#: contrib/config-daemon.scm:39 contrib/config-client.scm:40 +msgid "No help available." +msgstr "Không có trợ giúp sẵn sàng." + +#: contrib/config-daemon.scm:42 +msgid "" +"You can use 'make check' in src/transports/upnp/ to find out if your NAT " +"supports UPnP. You should disable this option if you are sure that you are " +"not behind a NAT. If your NAT box does not support UPnP, having this on " +"will not do much harm (only cost a small amount of resources)." +msgstr "" +"Bạn có thể dùng câu lệnh « make check » trong « src/transports/upnp » để tìm " +"biết nếu NAT đó há»— trợ UPnP không. ChÆ°a chắc nếu máy này nằm sau má»™t NAT thì " +"bạn nên tắt tùy chá»n này. Nếu máy NAT đó không há»— trợ UPnP, việc bật tùy " +"chá»n này sẽ không rất vấn Ä‘á» gì (chỉ chiếm má»™t ít tài nguyên)." + +#: contrib/config-daemon.scm:54 contrib/config-client.scm:53 +msgid "Prompt for development and/or incomplete code" +msgstr "Nhắc vá»›i mã vẫn phát triển và/hay mã chÆ°a hoàn toàn" + +#: contrib/config-daemon.scm:56 contrib/config-client.scm:55 +msgid "" +"If EXPERIMENTAL is set to NO, options for experimental code are not shown. " +"If in doubt, use NO.\n" +"\n" +"Some options apply to experimental code that maybe in a state of development " +"where the functionality, stability, or the level of testing is not yet high " +"enough for general use. These features are said to be of \"alpha\" " +"quality. If a feature is currently in alpha, uninformed use is discouraged " +"(since the developers then do not fancy \"Why doesn't this work?\" type " +"messages).\n" +"\n" +"However, active testing and qualified feedback of these features is always " +"welcome. Users should just be aware that alpha features may not meet the " +"normal level of reliability or it may fail to work in some special cases. " +"Bug reports are usually welcomed by the developers, but please read the " +"documents and and use for how to report problems." +msgstr "" +"Nếu EXPERIMENTAL (thá»±c nghiệm) được đặt thành NO (không) thì không hiển thị " +"tùy chá»n vá» mã vẫn thá»±c nghiệm. ChÆ°a chắc thì đặt NO.\n" +"\n" +"Má»™t số tùy chá»n nào đó có áp dụng cho mã thá»±c nghiệm, mà có thể vẫn còn được " +"phát triển thì chÆ°a đầy đủ chức năng, ổn định hay đủ thá»­ để sá»­ dụng má»™t cách " +"chung. Các tính năng này được nhãn là « alpha » (a). Nếu má»™t tính năng vẫn là " +"alpha, khuyên ngÆ°á»i dùng bình thÆ°á»ng không dùng nó.\n" +"\n" +"Tùy nhiên, nếu bạn quen vá»›i tiến trình thá»­ và phản hồi, má»i bạn tham gia " +"(miá»…n là bạn hiểu được rằng phần má»m này có thể chÆ°a sẵn sàng sá»­ dụng vá»›i dữ " +"liệu quan trá»ng). Nhà phát triển má»i bạn gá»­i báo cáo lá»—i: xin hãy Ä‘á»c tài " +"liệu và , sau đó dùng địa chỉ " +" để thông báo vấn Ä‘á»." + +#: contrib/config-daemon.scm:71 contrib/config-client.scm:70 +msgid "Show options for advanced users" +msgstr "Hiện tùy chá»n cấp cao" + +#: contrib/config-daemon.scm:73 contrib/config-client.scm:72 +msgid "" +"These are options that maybe difficult to understand for the beginner. These " +"options typically refer to features that allow tweaking of the " +"installation. If in a hurry, say NO." +msgstr "" +"Tùy chá»n cấp này có thể khó hiểu cho ngÆ°á»i dùng bắt đầu sá»­ dụng máy tính. " +"Tùy chá»n kiểu này thÆ°á»ng tham chiếu đến tính năng cho phép Ä‘iá»u chỉnh bản " +"cài đặt. Vá»™i thì đặt NO (không)." + +#: contrib/config-daemon.scm:84 contrib/config-client.scm:83 +msgid "Show rarely used options" +msgstr "Hiện tùy chá»n ít dùng" + +#: contrib/config-daemon.scm:86 contrib/config-client.scm:85 +msgid "" +"These are options that hardly anyone actually needs. If you plan on doing " +"development on GNUnet, you may want to look into these. If in doubt or in a " +"hurry, say NO." +msgstr "" +"Tùy chá»n này cần bởi rất ít ngÆ°á»i. Nếu bạn định phát triển qua GNUnet, có lẽ " +"tùy chá»n này có ích. Nếu không hay vá»™i, hãy đặt NO (không)." + +#: contrib/config-daemon.scm:97 contrib/config-client.scm:96 +msgid "Meta-configuration" +msgstr "Siêu cấu hình" + +#: contrib/config-daemon.scm:98 contrib/config-client.scm:97 +msgid "Which level of configuration should be available" +msgstr "Cấp cấu hình nên sẵn sàng" + +#: contrib/config-daemon.scm:115 +msgid "Full pathname of GNUnet HOME directory" +msgstr "Tên Ä‘Æ°á»ng dẫn đầy đủ đến thÆ° mục HOME GNUnet" + +#: contrib/config-daemon.scm:117 +msgid "" +"This gives the root-directory of the GNUnet installation. Make sure there is " +"some space left in that directory. :-) Users inserting or indexing files " +"will be able to store data in this directory up to the (global) quota " +"specified below. Having a few gigabytes of free space is recommended." +msgstr "" +"Äây Ä‘Æ°a ra thÆ° mục gốc của bản cài đặt GNUnet. Hãy kiểm tra xem vẫn còn có " +"sức chứa còn lại trong thÆ° mục đó. :-)\n" +"NgÆ°á»i dùng cần chèn hay phụ lục tập tin thì có thể lÆ°u dữ liệu vào thÆ° mục " +"này đến hạn ngạch (toàn cục) được ghi rõ dÆ°á»›i đây. Khuyên có vài GB sức chứa " +"còn rảnh." + +#: contrib/config-daemon.scm:130 +msgid "Full pathname of GNUnet directory for file-sharing data" +msgstr "" +"Tên Ä‘Æ°á»ng dẫn đầy đủ đến thÆ° mục GNUnet sẽ chứa dữ liệu chia sẻ tập tin" + +#: contrib/config-daemon.scm:142 +msgid "Full pathname to the directory with the key-value database" +msgstr "Tên Ä‘Æ°á»ng dẫn đầy đủ đến thÆ° mục có cÆ¡ sở dữ liệu khoá-giá_trị " + +#: contrib/config-daemon.scm:143 +msgid "Note that the kvstore is currently not used." +msgstr "Ghi chú rằng hiện thá»i không dùng kvstore. " + +#: contrib/config-daemon.scm:154 +msgid "Full pathname of GNUnet directory for indexed files symbolic links" +msgstr "" +"Tên Ä‘Æ°á»ng dẫn đầy đủ đến thÆ° mục GNUnet sẽ chứa các liên kết tÆ°Æ¡ng trÆ°ng đến " +"tập tin phụ lục" + +#: contrib/config-daemon.scm:166 +msgid "How many minutes should peer advertisements last?" +msgstr "Có nên quảng cáo đồng đẳnh trong mấy phút?" + +#: contrib/config-daemon.scm:168 +msgid "" +"How many minutes is the current IP valid? (GNUnet will sign HELLO messages " +"with this expiration timeline. If you are on dialup, 60 (for 1 hour) is " +"suggested. If you have a static IP address, you may want to set this to a " +"large value (say 14400). The default is 1440 (1 day). If your IP changes " +"periodically, you will want to choose an expiry period smaller than the " +"frequency with which your IP changes." +msgstr "" +"Äịa chỉ IP hiện thá»i vẫn hợp lệ trong bao nhiêu phút? (GNUnet sẽ ký má»—i " +"thông Ä‘iệp HELLO dùng thá»i hạn này. Nếu bạn quay số để kết nối, khuyên dùng " +"60 (1 giá»). Nếu máy này có má»™t địa chỉ IP tÄ©nh, má»™t giá trị lá»›n hÆ¡n (v.d. " +"14400) có ích. Mặc định là 1440 (1 ngày). Nếu máy này có má»™t địa chỉ IP thay " +"đổi theo định kỳ, khuyên bạn chá»n má»™t thá»i hạn ngắn hÆ¡n khoảng thá»i gian " +"giữa hai lần thay đổi địa chỉ IP." + +#: contrib/config-daemon.scm:179 +msgid "Where can GNUnet find an initial list of peers?" +msgstr "GNUnet có thể tìm má»™t danh sách các đồng đẳng đầu tiên ở đâu?" + +#: contrib/config-daemon.scm:181 +msgid "" +"GNUnet can automatically update the hostlist from the web. While GNUnet " +"internally communicates which hosts are online, it is typically a good idea " +"to get a fresh hostlist whenever gnunetd starts from the WEB. By setting " +"this option, you can specify from which server gnunetd should try to " +"download the hostlist. The default should be fine for now.\n" +"\t\t\n" +"The general format is a list of space-separated URLs. Each URL must have " +"the format http://HOSTNAME/FILENAME\n" +"\t\t\n" +"If you want to setup an alternate hostlist server, you must run a permanent " +"node and \"cat data/hosts/* > hostlist\" every few minutes to keep the list " +"up-to-date.\n" +"\t\t\n" +"If you do not specify a HOSTLISTURL, you must copy valid hostkeys to data/" +"hosts manually." +msgstr "" +"GNUnet có khả năng tá»± Ä‘á»™ng cập nhật danh sách các máy chủ từ Web. Dù GNUnet " +"liên lạc ná»™i bá»™ những máy này hiện thá»i trá»±c tuyến, thÆ°á»ng có ích để lấy má»™t " +"danh sách máy má»›i từ Web khi nào trình ná»n gnunetd khởi chạy. Bằng cách đặt " +"tùy chá»n này, bạn có thể ghi rõ gnunetd nên thá»­ tải danh sách máy xuống máy " +"phục vụ nào. Bây giá» giá trị mặc định vẫn ổn.\n" +"\n" +"Äịnh dạng chung là má»™t danh sách các địa chỉ URL định giá»›i bằng dấu cách. " +"Má»—i địa chỉ URL nên có dạng « http://tên_máy/tên_tập_tin ».\n" +"\n" +"Muốn thiết lập má»™t máy phục vụ danh sách máy xen kẽ thì bạn cần phải chạy " +"má»™t nút thông tin (node) bá»n bỉ, và chạy câu lệnh « cat data/hosts/* > " +"hostlist » sau má»—i vài phút để cập nhật danh sách.\n" +"\n" +"Nếu bạn không ghi rõ má»™t HOSTLISTURL (địa chỉ URL của danh sách máy), bạn " +"cÅ©ng cần phải tá»± sao chép má»—i khoá máy hợp lệ vào « data/hosts »." + +#: contrib/config-daemon.scm:198 +msgid "HTTP Proxy Server" +msgstr "Máy phục vụ ủy nhiệm HTTP" + +#: contrib/config-daemon.scm:200 +msgid "" +"If you have to use a proxy for outbound HTTP connections, specify the proxy " +"configuration here. Default is no proxy." +msgstr "" +"Nếu bạn cần dùng má»™t máy phục vụ ủy nhiệm cho kết nối HTTP Ä‘i ra, hãy ghi rõ " +"cấu hình ủy nhiệm ở đây. Mặc định là không dùng ủy nhiệm." + +#: contrib/config-daemon.scm:212 +msgid "" +"Name of the directory where gnunetd should store contact information about " +"peers" +msgstr "Tên của thÆ° mục vào đó gnunetd nên lÆ°u thông tin liên lạc vỠđồng đẳng" + +#: contrib/config-daemon.scm:214 +msgid "" +"Unless you want to share the directory directly using a webserver, the " +"default is most likely just fine." +msgstr "" +"Nếu bạn không muốn chia sẻ thÆ° mục má»™t cách trá»±c tiếp dùng má»™t máy phục vụ " +"Web, giá trị mặc định rất có thể ổn." + +#: contrib/config-daemon.scm:240 contrib/config-client.scm:140 +msgid "How long should logs be kept?" +msgstr "Bao lâu nên giữ bản theo dõi?" + +#: contrib/config-daemon.scm:242 contrib/config-client.scm:142 +msgid "" +"How long should logs be kept? If you specify a value greater than zero, a " +"log is created each day with the date appended to its filename. These logs " +"are deleted after $KEEPLOG days.\tTo keep logs forever, set this value to 0." +msgstr "" +"Bao lâu nên giữ sổ theo dõi (log)? Nếu bạn ghi rõ má»™t giá trị hÆ¡n số không, " +"má»™t sổ theo dõi được tạo hàng ngày vá»›i ngày tháng được phụ thêm vào tên tập " +"tin. Sổ theo dõi này bị xoá sau $KEEPLOG ngày.\tÄể không bao giá» xoá sổ theo " +"dõi, hãy đặt giá trị này thành 0." + +#: contrib/config-daemon.scm:253 +msgid "" +"What maximum number of open file descriptors should be requested from the OS?" +msgstr "" +"Có nên yêu cầu từ hệ Ä‘iá»u hành bao nhiêu bá»™ mô tả tập tin còn mở (tối Ä‘a)?" + +#: contrib/config-daemon.scm:255 +msgid "" +"The default of 1024 should be fine for most systems. If your system can " +"support more, increasing the number might help support additional clients on " +"machines with plenty of bandwidth. For embedded systems, a smaller number " +"might be acceptable. A value of 0 will leave the descriptor limit " +"untouched. This option is mostly for OS X systems where the default is too " +"low. Note that if gnunetd cannot obtain the desired number of file " +"descriptors from the operating system, it will print a warning and try to " +"run with what it is given." +msgstr "" +"Giá trị mặc định 1024 nên ổn cho phần lá»›n hệ thống. Nếu hệ thống này có khả " +"năng há»— trợ nhiá»u bá»™ mô tả tập tin hÆ¡n, má»™t số lá»›n hÆ¡n có thể giúp há»— trợ " +"các ứng dụng khách bổ sung trên máy có rất nhiá»u bảng thông. Äối vá»›i hệ " +"thống nhúng, má»™t số nhá» hÆ¡n có thể ổn. Giá trị 0 sẽ không thay đổi giá»›i hạn. " +"Tùy chá»n này thÆ°á»ng có ích trên hệ thống OSX (có giá trị mặc định quá thấp). " +"Ghi chú rằng nếu gnunetd không thể lấy số các bá»™ mô tả tập tin yêu cầu từ hệ " +"Ä‘iá»u hành, thì nó sẽ in ra má»™t cảnh báo và thá»­ chạy vá»›i số Ä‘Æ°a ra." + +#: contrib/config-daemon.scm:266 +msgid "Where should gnunetd write the logs?" +msgstr "gnunetd nên ghi sổ theo dõi vào đâu?" + +#: contrib/config-daemon.scm:278 +msgid "Enable for extra-verbose logging." +msgstr "Bật để theo dõi rất chi tiết hÆ¡n." + +#: contrib/config-daemon.scm:290 contrib/config-client.scm:165 +msgid "Logging" +msgstr "Theo dõi" + +#: contrib/config-daemon.scm:291 contrib/config-client.scm:166 +msgid "Specify which system messages should be logged how" +msgstr "Ghi rõ những thông Ä‘iệp hệ thống nào nên được theo dõi nhÆ° thế nào" + +#: contrib/config-daemon.scm:296 contrib/config-client.scm:170 +msgid "Logging of events for users" +msgstr "Theo dõi sá»± kiện cho ngÆ°á»i dùng" + +#: contrib/config-daemon.scm:297 contrib/config-client.scm:171 +msgid "Logging of events for the system administrator" +msgstr "Theo dõi sá»± kiện cho quản trị hệ thống" + +#: contrib/config-daemon.scm:309 +msgid "Where should gnunetd write the PID?" +msgstr "gnunetd nên ghi PID vào đâu?" + +#: contrib/config-daemon.scm:310 +msgid "" +"The default is no longer /var/run/gnunetd.pid since we could not delete the " +"file on shutdown at that location." +msgstr "" +"Giá trị mặc định không còn là « /var/run/gnunetd.pid » lại, vì không thể xoá " +"tập tin ở vị trí đó khi tắt máy." + +#: contrib/config-daemon.scm:322 +msgid "As which user should gnunetd run?" +msgstr "gnunetd nên chạy dÆ°á»›i ngÆ°á»i dùng nào?" + +#: contrib/config-daemon.scm:324 +msgid "" +"Empty means \"current user\". On computer startup, it is root/SYSTEM. Under " +"Windows, this setting affects the creation of a new system service only." +msgstr "" +"Trống có nghÄ©a là « ngÆ°á»i dùng hiện thá»i ». Khi máy khởi Ä‘á»™ng, nó là « root/" +"SYSTEM ». DÆ°á»›i Windows, thiết lập này chỉ ảnh hưởng đến việc tạo má»™t dịch vụ " +"hệ thống." + +#: contrib/config-daemon.scm:337 +msgid "Should gnunetd be automatically started when the system boots?" +msgstr "Khi hệ thống khởi Ä‘á»™ng, có nên tá»± Ä‘á»™ng khởi chạy gnunetd không?" + +#: contrib/config-daemon.scm:338 +msgid "" +"Set to YES if gnunetd should be automatically started on boot. If this " +"option is set, gnunet-setup will install a script to start the daemon upon " +"completion. This option may not work on all systems." +msgstr "" +"Äặt thành YES (có) nếu trình ná»n gnunetd nên tá»± Ä‘á»™ng được khởi chạy khi hệ " +"thống khởi Ä‘á»™ng. Nếu đặt tùy chá»n này thì tiến trình thiết lập gnunet-setup " +"sẽ cài đặt má»™t văn lệnh để khởi chạy trình ná»n này má»™t khi hoàn tất. Tùy " +"chá»n này có thể không hoạt Ä‘á»™ng đúng trên má»i hệ thống." + +#: contrib/config-daemon.scm:350 +msgid "Which transport mechanisms should GNUnet use?" +msgstr "GNUnet nên dùng những cÆ¡ chế truyá»n nào?" + +#: contrib/config-daemon.scm:352 +msgid "" +"Use a space-separated list of modules, e.g. \"udp smtp tcp\". The " +"available transports are udp, tcp, http, smtp and nat.\n" +"\t\t\n" +"Loading the 'nat' and 'tcp' modules is required for peers behind NAT boxes " +"that cannot directly be reached from the outside. Peers that are NOT behind " +"a NAT box and that want to *allow* peers that ARE behind a NAT box to " +"connect must ALSO load the 'nat' module. Note that the actual transfer will " +"always be via tcp initiated by the peer behind the NAT box. The nat " +"transport requires the use of tcp, http and/or smtp in addition to nat " +"itself." +msgstr "" +"Dùng má»™t danh sách các mô-Ä‘un định giá»›i bằng dấu cách, v.d. « udp smtp tcp ». " +"Những cÆ¡ chế truyá»n sẵn sàng: udp, tcp, http, smtp, nat.\n" +"\n" +"Nạp hai mô-Ä‘un « nat » và « tcp » cần thiết cho đồng đẳng nằm sau máy NAT mà " +"không tá»›i được từ bên ngoài. Äồng đẳng KHÔNG phải nằm sau máy NAT và muốn " +"_cho phép_ kết nối từ đồng đẳng mà CÓ phải nằm sau NAT thì CŨNG cần phải nạp " +"mô-Ä‘un « nat ». Ghi chú rằng việc truyá»n thật lúc nào cÅ©ng chạy qua tcp, được " +"sÆ¡ khởi bởi đồng đẳng nằm sau máy NAT. CÆ¡ chế truyá»n nat cÅ©ng cần thiết tcp, " +"http và/hay smtp thêm vào nat chính nó." + +#: contrib/config-daemon.scm:366 +msgid "Which applications should gnunetd support?" +msgstr "gnunetd nên há»— trợ những ứng dụng nào?" + +#: contrib/config-daemon.scm:368 +msgid "" +"Whenever this option is changed, you MUST run gnunet-update. Currently, the " +"available applications are:\n" +"\n" +"advertising: advertises your peer to other peers. Without it, your peer will " +"not participate in informing peers about other peers. You should always " +"load this module.\n" +"\n" +"getoption: allows clients to query gnunetd about the values of various " +"configuration options. Many tools need this. You should always load this " +"module.\n" +"\n" +"stats: allows tools like gnunet-stats and gnunet-gtk to query gnunetd about " +"various statistics. This information is usually quite useful to diagnose " +"errors, hence it is recommended that you load this module.\n" +"\n" +"traffic: keeps track of how many messages were recently received and " +"transmitted. This information can then be used to establish how much cover " +"traffic is currently available. The amount of cover traffic becomes " +"important if you want to make anonymous requests with an anonymity level " +"that is greater than one. It is recommended that you load this module.\n" +"\n" +"fs: needed for anonymous file sharing. You should always load this module.\n" +"\n" +"hostlist: integrated hostlist HTTP server. Useful if you want to offer a " +"hostlist and running Apache would be overkill.\n" +"\n" +"chat: broadcast chat (demo-application, ALPHA quality).\tRequired for gnunet-" +"chat. Note that the current implementation of chat is not considered to be " +"secure.\n" +"\n" +"tbench: benchmark transport performance. Required for gnunet-tbench. Note " +"that tbench allows other users to abuse your resources.\n" +"\n" +"tracekit: topology visualization toolkit. Required for gnunet-tracekit. " +"Note that loading tracekit will make it slightly easier for an adversary to " +"compromise your anonymity." +msgstr "" +"Khi nào tùy chá»n này bị thay đổi, bạn CẦN PHẢI chạy tiến trình cập nhật " +"gnunet-update. Hiện thá»i có những ứng dụng sẵn sàng này:\n" +"\n" +" • advertising\tquảng cáo đồng đẳng này cho các đồng đẳng khác. Không có ứng " +"dụng này thì đồng đẳng này sẽ không tham gia hoạt Ä‘á»™ng cho đồng đẳng biết vá» " +"đồng đẳng khác. Lúc nào cÅ©ng nên nạp mô-Ä‘un này.\n" +"\n" +" • getoption\tcho phép ứng dụng khách há»i gnunetd vá» giá trị của má»™t số tùy " +"chá»n cấu hình nào đó. Rất nhiá»u công cụ cần chức năng này. Lúc nào cÅ©ng nên " +"nạp mô-Ä‘un này.\n" +"\n" +" • stats\tcho phép công cụ nhÆ° gnunet-stats và gnunet-gtk há»i gnunetd vá» má»™t " +"số thống kê nào đó. Thông tin này thÆ°á»ng hÆ¡i hữu ích để chẩn Ä‘oán lá»—i, do đó " +"khuyên bạn nạp mô-Ä‘un này.\n" +"\n" +" • traffic\ttheo dõi bao nhiêu tin nhẳn vừa nhận và gá»­i. Thông tin này thì " +"có thể được dùng để tính bao nhiêu giao thông trải ra hiện thá»i sẵn sàng. " +"Lượng giao thông trải ra trở thành quan trá»ng nếu bạn muốn gá»­i yêu cầu nặc " +"danh vá»›i má»™t cấp nặc danh hÆ¡n má»™t. Khuyên bạn nạp mô-Ä‘un này.\n" +"\n" +" • fs\tcần để chia sẻ tập tin má»™t cách nặc danh. Lúc nào cÅ©ng nên nạp mô-Ä‘un " +"này.\n" +"\n" +" • hostlist\ttrình phục vụ HTTP danh sách máy thống nhất. Có ích nếu bạn " +"muốn cung cấp má»™t danh sách máy, nhÆ°ng không muốn chạy Apache cho má»™t công " +"việc rất nhá» nhÆ° vậy.\n" +"\n" +" • chat\ttrò chuyện quảng bá (ứng dụng minh hoạ, vẫn ALPHA). Cần thiết cho " +"ứng dụng trò chuyện gnunet-chat. Ghi chú rằng bản thá»±c hiện chat hiện thá»i " +"chÆ°a được xem là bảo mật.\n" +"\n" +" • tbench\tkiểm chuẩn hiệu suất truyá»n. Cần thiết cho gnunet-tbench. Ghi chú " +"rằng tbench cho phép ngÆ°á»i dùng khác lạm dụng các tài nguyên của bạn.\n" +"\n" +" • tracekit\tbá»™ công cụ mÆ°á»ng tượng địa hình. Cần thiết cho gnunet-tracekit. " +"Ghi chú rằng việc nạp tracekit sẽ làm cho má»™t ít dá»… hÆ¡n khi ngÆ°á»i dùng muốn " +"xâm nhập tình trạng nặc danh của bạn." + +#: contrib/config-daemon.scm:399 +msgid "Disable client-server connections" +msgstr "Tắt má»i kết nối kiểu khách-máy_phục_vụ" + +#: contrib/config-daemon.scm:400 +msgid "" +"This option can be used to tell gnunetd not to open the client port. When " +"run like this, gnunetd will participate as a peer in the network but not " +"support any user interfaces. This may be useful for headless systems that " +"are never expected to have end-user interactions. Note that this will also " +"prevent you from running diagnostic tools like gnunet-stats!" +msgstr "" +"Tùy chá»n này có thể được dùng để báo trình ná»n gnunetd không mở cổng cho ứng " +"dụng khách. Khi chạy bằng cách này, gnunetd sẽ tham gia là má»™t đồng đẳng " +"trên mạng, còn không há»— trợ giao diện ngÆ°á»i dùng nào. Có thể hữu ích cho hệ " +"thống không cần màn hình mà không bao giá» nên tÆ°Æ¡ng tác vá»›i ngÆ°á»i dùng cuối " +"cùng. Ghi chú rằng tùy chá»n này cÅ©ng ngăn cản bạn chạy công cụ chẩn Ä‘oán nhÆ° " +"tiến trình thống kê gnunet-stats !" + +#: contrib/config-daemon.scm:412 +msgid "YES disables IPv6 support, NO enables IPv6 support" +msgstr "YES hay NO thì tắt hay bật há»— trợ IPv6" + +#: contrib/config-daemon.scm:413 +msgid "" +"This option may be useful on peers where the kernel does not support IPv6. " +"You might also want to set this option if you do not have an IPv6 network " +"connection." +msgstr "" +"Tùy chá»n này có thể hữu ích trên đồng đẳng không có hạt nhân há»— trợ IPv6. " +"CÅ©ng có thể đặt tùy chá»n này nếu không có kết nối mạng kiểu IPv6." + +#: contrib/config-daemon.scm:425 +msgid "Disable peer discovery" +msgstr "Tắt khám phá đồng đẳng" + +#: contrib/config-daemon.scm:426 +msgid "" +"The option 'PRIVATE-NETWORK' can be used to limit the connections of this " +"peer to peers of which the hostkey has been copied by hand to data/hosts; " +"if this option is given, GNUnet will not accept advertisements of peers that " +"the local node does not already know about. Note that in order for this " +"option to work, HOSTLISTURL should either not be set at all or be set to a " +"trusted peer that only advertises the private network. Also, the option does " +"NOT work at the moment if the NAT transport is loaded; for that, a couple of " +"lines above would need some minor editing :-)." +msgstr "" +"Tùy chá»n « PRIVATE-NETWORK » có thể được dùng để hạn chế kết nối của đồng " +"đẳng này thành những đồng đẳng có khoá máy đã được sao chép bằng tay vào « " +"data/hosts »; nếu bật tùy chá»n này thì GNUnet sẽ không chấp nhận quảng cáo " +"vỠđồng đẳng không được nút cục bá»™ nhận ra. Ghi chú rằng tùy chá»n này cÅ©ng " +"cần HOSTLISTURL không đặt bằng cách nào cả hay có đặt thành má»™t đồng đẳng " +"tin cậy mà chỉ quảng cáo mạng riêng. HÆ¡n nữa, tùy chá»n này KHÔNG hoạt Ä‘á»™ng " +"khi nào cÆ¡ chế trung NAT được nạp: để tránh trÆ°á»ng hợp đó, cÅ©ng cần phải " +"chỉnh sá»­a vài dòng bên trên. :-)" + +#: contrib/config-daemon.scm:437 +msgid "Disable advertising this peer to other peers" +msgstr "Tắt quảng cáo máy này cho đồng đẳng khác" + +#: contrib/config-daemon.scm:449 +msgid "Disable automatic establishment of connections" +msgstr "Tắt tá»± Ä‘á»™ng thiết lập kết nối" + +#: contrib/config-daemon.scm:450 +msgid "" +"If this option is enabled, GNUnet will not automatically establish " +"connections to other peers, but instead wait for applications to " +"specifically request connections to other peers (or for other peers to " +"connect to us)." +msgstr "" +"Bật tùy chá»n này thì GNUnet sẽ không tá»± Ä‘á»™ng thiết lập kết nối tá»›i đồng đẳng " +"khác, thay vào đó nó đợi ứng đụng yêu cầu dứt khoát kết nối tá»›i đồng đẳng " +"khác (hoặc đợi đồng đẳng khác kết nối đến máy này)." + +#: contrib/config-daemon.scm:461 +msgid "Enable advertising of other peers by this peer" +msgstr "Bật máy này quảng cáo đồng đẳng khác" + +#: contrib/config-daemon.scm:462 +msgid "" +"This option may be useful during testing, but turning it off is dangerous! " +"If in any doubt, set it to YES (which is the default)." +msgstr "" +"Tùy chá»n này có thể hữu ích trong khi thá»­: trong má»i trÆ°á»ng hợp Ä‘á»u không " +"nên tắt nó ! ChÆ°a chắc thì đặt nó thành YES (có) mà mặc định." + +#: contrib/config-daemon.scm:473 +msgid "Port for communication with GNUnet user interfaces" +msgstr "Cổng để liên lạc vá»›i giao diện ngÆ°á»i dùng GNUnet" + +#: contrib/config-daemon.scm:474 +msgid "" +"Which is the client-server port that is used between gnunetd and the clients " +"(TCP only). You may firewall this port for non-local machines (but you do " +"not have to since GNUnet will perform access control and only allow " +"connections from machines that are listed under TRUSTED)." +msgstr "" +"Trình ná»n gnunetd và các ứng dụng khách liên lạc (chỉ TCP) qua cổng khách-" +"phục_vụ nào? Bạn cÅ©ng có thể đặt bức tÆ°á»ng lá»­a giữa cổng này và các máy khác " +"cục bá»™, nhÆ°ng không cần phải vì GNUnet sẽ Ä‘iá»u khiển truy cập và chỉ cho " +"phép kết nối từ máy được liệt kê dÆ°á»›i TRUSTED (tin cậy). " + +#: contrib/config-daemon.scm:485 +msgid "Port for the integrated hostlist HTTP server" +msgstr "Cổng cho trình phục vụ HTTP danh sách máy chủ thống nhất" + +#: contrib/config-daemon.scm:497 +msgid "IPv4 networks allowed to use gnunetd server" +msgstr "Các mạng IPV4 được phép dùng trình phục vụ gnunetd" + +#: contrib/config-daemon.scm:498 contrib/config-daemon.scm:510 +msgid "" +"This option specifies which hosts are trusted enough to connect as clients " +"(to the TCP port). This is useful if you run gnunetd on one host of your " +"network and want to allow all other hosts to use this node as their server. " +"By default, this is set to 'loopback only'. The format is IP/NETMASK where " +"the IP is specified in dotted-decimal and the netmask either in CIDR " +"notation (/16) or in dotted decimal (255.255.0.0). Several entries must be " +"separated by a semicolon, spaces are not allowed." +msgstr "" +"Tùy chá»n này ghi rõ những máy nào đủ tin cậy để kết nối là ứng dụng khách " +"(đến cổng TCP). Có ích nếu bạn chạy trình nên gnunetd trên má»™t máy của mạng " +"cục bá»™, cÅ©ng muốn các máy khác cùng mạng dùng nút này làm máy phục vụ. Mặc " +"định là tùy chá»n này được đặt thành « loopback only » (chỉ mạch ná»™i bá»™). Äịnh " +"dạng là IP/MẶT_NẠ_MẠNG mà địa chỉ IP được ghi rõ theo thập phân đánh chấm (v." +"d. 255.255.0.0), và mặt nạ mạng (netmask) theo hoặc cách ghi CIDR (v.d. " +"1/16) hoặc thập phân đánh chấm. Vài mục nhập nên định giá»›i bằng dấu hai " +"chấm, cÅ©ng không cho phép khoảng cách." + +#: contrib/config-daemon.scm:509 +msgid "IPv6 networks allowed to use gnunetd server" +msgstr "Các mạng IPv6 được phép dùng trình phục vụ gnunetd" + +#: contrib/config-daemon.scm:522 +msgid "Limit connections to the specfied set of peers." +msgstr "Hạn chế kết nối thành tập hợp đồng đẳng được ghi rõ." + +#: contrib/config-daemon.scm:523 +msgid "" +"If this option is not set, any peer is allowed to connect. If it is set, " +"only the specified peers are allowed. Specify the list of peer IDs (not IPs!)" +msgstr "" +"Không bật tùy chá»n này thì má»i đồng đẳng được phép kết nối. Nếu bật nó thì " +"chỉ cho phép những đồng đẳng Ä‘Æ°a ra. Hãy ghi rõ danh sách các mã số đồng " +"đẳng, không phải địa chỉ IP !" + +#: contrib/config-daemon.scm:534 +msgid "Run gnunetd as this group." +msgstr "Chạy gnunetd dÆ°á»›i nhóm này." + +#: contrib/config-daemon.scm:535 +msgid "" +"When started as root, gnunetd will change permissions to the given group." +msgstr "" +"Khi khởi chạy bởi ngÆ°á»i chủ, trình ná»n gnunetd sẽ chuyển đổi quyá»n truy cập " +"sang nhóm Ä‘Æ°a ra." + +#: contrib/config-daemon.scm:546 +msgid "Prevent the specfied set of peers from connecting." +msgstr "Chặn kết nối từ tập hợp đồng đẳng được ghi rõ." + +#: contrib/config-daemon.scm:547 +msgid "" +"If this option is not set, any peer is allowed to connect. If the ID of a " +"peer is listed here, connections from that peer will be refused. Specify " +"the list of peer IDs (not IPs!)" +msgstr "" +"Không bật tùy chá»n này thì má»i đồng đẳng Ä‘á»u được phép kết nối. Nếu mã số " +"của má»™t đồng đẳng nào đó được liệt kê ở đây, thì kết nối từ máy đó bị từ " +"chối. Hãy ghi rõ danh sách các mã số đồng đẳng, không phải địa chỉ IP !" + +#: contrib/config-daemon.scm:558 +msgid "Topology Maintenance" +msgstr "Duy trì địa hình" + +#: contrib/config-daemon.scm:559 +msgid "Rarely used settings for peer advertisements and connections" +msgstr "Thiết lập ít dùng vá» quảng cáo và kết nối của đồng đẳng" + +#: contrib/config-daemon.scm:579 +msgid "General settings" +msgstr "Thiết lập chung" + +#: contrib/config-daemon.scm:580 +msgid "Settings that change the behavior of GNUnet in general" +msgstr "Thiết lập mà sá»­a đổi ứng xá»­ chung của GNUnet" + +#: contrib/config-daemon.scm:607 +msgid "Modules" +msgstr "Mô-Ä‘un" + +#: contrib/config-daemon.scm:608 +msgid "Settings that select specific implementations for GNUnet modules" +msgstr "Thiết lập mà chá»n bản thá»±c hiện riêng cho mô-Ä‘un GNUnet" + +#: contrib/config-daemon.scm:626 +msgid "Fundamentals" +msgstr "CÆ¡ bản" + +#: contrib/config-daemon.scm:646 +msgid "Which database should be used?" +msgstr "Có nên dùng cÆ¡ sở dữ liệu nào?" + +#: contrib/config-daemon.scm:648 +#, fuzzy +msgid "" +"Which database should be used? The options are \"sqstore_sqlite\", " +"\"sqstore_postgres\" and \"sqstore_mysql\". You must run gnunet-update " +"after changing this value!\n" +"\t\t\t\n" +"In order to use MySQL or Postgres, you must configure the respective " +"database, which is relatively simple. Read the file doc/README.mysql or doc/" +"README.postgres for how to setup the respective database." +msgstr "" +"Có nên dùng cÆ¡ sở dữ liệu nào? Hai tùy chá»n là « sqstore_sqlite » và « " +"sqstore_mysql ». Sau khi thay đổi giá trị này thì cÅ©ng cần phải chạy tiến " +"trình cập nhật gnunet-update !\n" +"\n" +"Äể sá»­ dụng sqstore_mysql, bạn cÅ©ng cần phải cấu hình cÆ¡ sở dữ liệu MySQL: " +"má»™t công việc hÆ¡i Ä‘Æ¡n giản. Hãy Ä‘á»c tài liệu Äá»c Äi « doc/README.mysql » để " +"tìm biết thiết lập MySQL nhÆ° thế nào." + +#: contrib/config-daemon.scm:661 contrib/config-daemon.scm:674 +msgid "Which topology should be used?" +msgstr "Có nên địa hình nào?" + +#: contrib/config-daemon.scm:662 +msgid "Which database should be used for the temporary datastore of the DHT?" +msgstr "Có nên dùng cÆ¡ sở dữ liệu nào làm kho dữ liệu tạm thá»i của DHT?" + +#: contrib/config-daemon.scm:676 +msgid "" +"Which topology should be used? The only option at the moment is " +"\"topology_default\"" +msgstr "" +"Có nên địa hình nào? Tùy chá»n duy nhất hiện thá»i là « topology_default »." + +#: contrib/config-daemon.scm:690 +msgid "" +"The minimum number of connected friends before this peer is allowed to " +"connect to peers that are not listed as friends" +msgstr "" +"Số tối thiểu các bạn bè có kết nối trÆ°á»›c khi đồng đẳng nay được phép kết nối " +"tá»›i đồng đẳng lạ (không phải được liệt kê là bạn bè)" + +#: contrib/config-daemon.scm:691 +msgid "" +"Note that this option does not guarantee that the peer will be able to " +"connect to the specified number of friends. Also, if the peer had connected " +"to a sufficient number of friends and then established non-friend " +"connections, some of the friends may drop out of the network, temporarily " +"resulting in having fewer than the specified number of friends connected " +"while being connected to non-friends. However, it is guaranteed that the " +"peer itself will never choose to drop a friend's connection if this would " +"result in dropping below the specified number of friends (unless that number " +"is higher than the overall connection target)." +msgstr "" +"Ghi chú rằng tùy chá»n này không đảm bảo rằng đồng đẳng sẽ kết nối được tá»›i " +"số các bạn bè Ä‘Æ°a ra. HÆ¡n nữa, nếu đồng đẳng có kết nối tá»›i đủ bạn bè, sau " +"đó thiết lập kết nối khác bạn bè, thì má»™t số bạn bè có thể dứt khá»i mạng, " +"tạm thá»i có kết quả là quá ít bạn bè có kết nối trong khi kết nối tá»›i khác " +"bạn bè. Tuy nhiên đảm bảo rằng đồng đẳng chính nó không bao giá» chá»n ngắt " +"kết nối tá»›i bạn bè nếu việc đó có kết quả là quá ít bán bè có kết nối (nếu " +"số đã đặt không phải lá»›n hÆ¡n đích kết nối toàn bá»™)." + +#: contrib/config-daemon.scm:702 +msgid "" +"If set to YES, the peer is only allowed to connect to other peers that are " +"explicitly specified as friends" +msgstr "" +"Bật tùy chá»n này (đặt YES) thì đồng đẳng chỉ được phép tá»›i đồng đẳng được " +"ghi rõ dứt khoát là bạn bè" + +#: contrib/config-daemon.scm:703 +msgid "" +"Use YES only if you have (trustworthy) friends that use GNUnet and are " +"afraid of establishing (direct) connections to unknown peers" +msgstr "" +"Hãy đặt YES chỉ nếu bạn có bạn bè tin cậy có sá»­ dụng GNUnet và không muốn " +"thiết lập kết nối trá»±c tiếp tá»›i đồng đẳng lạ" + +#: contrib/config-daemon.scm:714 +msgid "List of friends for friend-to-friend topology" +msgstr "Danh sách bạn bè cho địa hình bạn-đến-bạn" + +#: contrib/config-daemon.scm:715 +msgid "" +"Specifies the name of a file which contains a list of GNUnet peer IDs that " +"are friends. If used with the friend-to-friend topology, this will ensure " +"that GNUnet only connects to these peers (via any available transport)." +msgstr "" +"Ghi rõ tên của má»™t tập tin chứa danh sách các mã số đồng đẳng GNUnet cÅ©ng là " +"bạn bè. Nếu dùng vá»›i địa hình bạn-đến-bạn, thì tùy chá»n này sẽ đảm bảo rằng " +"GNUnet chỉ kết nối tá»›i những đồng đẳng đó (qua bất cứ cÆ¡ chế truyá»n hoạt " +"Ä‘á»™ng nào)." + +#: contrib/config-daemon.scm:726 +msgid "Friend-to-Friend Topology Specification" +msgstr "Äặc tả địa hình bạn-đến-bạn" + +#: contrib/config-daemon.scm:727 +msgid "Settings for restricting connections to friends" +msgstr "Thiết lập để hạn chế kết nối thành bạn bè" + +#: contrib/config-daemon.scm:744 +msgid "Name of the MySQL database GNUnet should use" +msgstr "Tên của cÆ¡ sở dữ liệu MySQL mà GNUnet nên dùng" + +#: contrib/config-daemon.scm:756 +msgid "Configuration file that specifies the MySQL username and password" +msgstr "Tập tin cấu hình mà ghi rõ tên ngÆ°á»i dùng và mật khẩu MySQL" + +#: contrib/config-daemon.scm:768 +msgid "Configuration of the MySQL database" +msgstr "Cấu hình của cÆ¡ sở dữ liệu MySQL" + +#: contrib/config-daemon.scm:787 +msgid "MB of diskspace GNUnet can use for anonymous file sharing" +msgstr "MB sức chứa trên Ä‘Ä©a cho GNUnet dùng để chia sẻ tập tin nặc danh" + +#: contrib/config-daemon.scm:789 +msgid "" +"How much disk space (MB) is GNUnet allowed to use for anonymous file " +"sharing? This does not take indexed files into account, only the space " +"directly used by GNUnet is accounted for. GNUnet will gather content from " +"the network if the current space-consumption is below the number given here " +"(and if content migration is allowed below).\n" +"\n" +"Note that if you change the quota, you need to run gnunet-update afterwards." +msgstr "" +"Bao nhiêu sức chứa trên Ä‘Ä©a (theo MB) cho phép GNUnet dùng để chia sẻ tập " +"tin má»™t cách nặc danh? Giá trị này không tính tập tin phụ lục, chỉ tính sức " +"chứa được GNUnet dùng trá»±c tiếp. GNUnet sẽ tập hợp ná»™i dung từ mạng nếu tiêu " +"thụ sức chứa hiện thá»i nhá» hÆ¡n số Ä‘Æ°a ra ở đây (và nếu chức năng nâng cấp " +"ná»™i dung được phép bên dÆ°á»›i).\n" +"\n" +"Ghi chú rằng nếu bạn sá»­a đổi hạn ngạch này, sau đó cÅ©ng cần phải chạy tiến " +"trình cập nhật gnunet-update." + +#: contrib/config-daemon.scm:803 +msgid "Number of entries in the migration buffer" +msgstr "Số các mục nhập trong vùng đệm nâng cấp" + +#: contrib/config-daemon.scm:804 +msgid "" +"Each entry uses about 32k of memory. More entries can reduce disk IO and " +"CPU usage at the expense of having gnunetd use more memory. Very large " +"values may again increase CPU usage. A value of 0 will prevent your peer " +"from sending unsolicited responses." +msgstr "" +"Má»—i mục nhập chiếm khoảng 32k trong vùng nhá»›. Nhiá»u mục nhập hÆ¡n thì có thể " +"giảm VR của Ä‘Ä©a và sá»­ dụng CPU, còn gnunetd chiếm vùng nhá»› lá»›n hÆ¡n. Giá trị " +"rất lá»›n cÅ©ng có thể tăng sá»­ dụng CPU. Giá trị 0 sẽ ngăn cản máy này đáp ứng " +"mà không yêu cầu." + +#: contrib/config-daemon.scm:816 +msgid "Size of the routing table for anonymous routing." +msgstr "Kích cỡ của bảng định tuyến đối vá»›i định tuyến nặc danh." + +#: contrib/config-daemon.scm:828 +msgid "Size of the routing table for DHT routing." +msgstr "Kích cỡ của bảng định tuyến đối vá»›i định tuyến DHT." + +#: contrib/config-daemon.scm:841 +msgid "Allow migrating content to this peer." +msgstr "Cho phép nâng cấp ná»™i dung lên máy này." + +#: contrib/config-daemon.scm:843 +msgid "" +"If you say yes here, GNUnet will migrate content to your server, and you " +"will not be able to control what data is stored on your machine. \n" +"\t\t\t\n" +"If you activate it, you can claim for *all* the non-indexed (-n to gnunet-" +"insert) content that you did not know what it was even if an adversary takes " +"control of your machine. If you do not activate it, it is obvious that you " +"have knowledge of all the content that is hosted on your machine and thus " +"can be considered liable for it." +msgstr "" +"Bật tùy chá»n này thì GNUnet sẽ nâng cấp ná»™i dung đến máy phục vụ này, và bạn " +"không thể Ä‘iá»u khiển dữ liệu nào được giữ trên máy này.\n" +"\n" +"Tùy chá»n này cho phép ngÆ°á»i dùng từ chối trách nhiệm vá» ná»™i dung không phụ " +"lục (-n tá»›i gnunet-insert) nằm trong máy của mình, thậm chí nếu ngÆ°á»i khác " +"xâm nhập hệ thống. Không bật tùy chá»n này thì ngÆ°á»i dùng chịu trách nhiệm vá» " +"tất cả các ná»™i dung trên máy của mình." + +#: contrib/config-daemon.scm:857 +msgid "" +"MB of diskspace GNUnet can use for caching DHT index data (the data will be " +"stored in /tmp)" +msgstr "" +"MB sức chứa trên Ä‘Ä©a cho GNUnet dùng để lÆ°u tạm dữ liệu phụ lục DHT (dữ liệu " +"được giữ trong /tmp)" + +#: contrib/config-daemon.scm:858 +msgid "" +"DHT index data is inherently small and expires comparatively quickly. It is " +"deleted whenever gnunetd is shut down.\n" +"\n" +"The size of the DSTORE QUOTA is specified in MB." +msgstr "" +"Dữ liệu phụ lục DHT vốn đã nhá» và hết hạn hÆ¡i nhanh. Nó bị xoá khi nào " +"gnunetd bị thoát.\n" +"\n" +"Kích cỡ của hạn ngạch kho dữ liệu (DSTORE QUOTA) được ghi rõ theo MB." + +#: contrib/config-daemon.scm:872 +msgid "Options for anonymous file sharing" +msgstr "Tùy chá»n vá» chia sẻ tập tin nặc danh" + +#: contrib/config-daemon.scm:891 +msgid "Applications" +msgstr "Ứng dụng" + +#: contrib/config-daemon.scm:907 +msgid "Is this machine unreachable behind a NAT?" +msgstr "Máy này không tá»›i được ở sau NAT không?" + +#: contrib/config-daemon.scm:908 +msgid "" +"Set to YES if this machine is behind a NAT that limits connections from the " +"outside to the GNUnet port and that cannot be traversed using UPnP. Note " +"that if you have configured your NAT box to allow direct connections from " +"other machines to the GNUnet ports or if GNUnet can open ports using UPnP, " +"you should set the option to NO. Set this only to YES if other peers cannot " +"contact you directly. You can use 'make check' in src/transports/upnp/ to " +"find out if your NAT supports UPnP. You can also use gnunet-transport-check " +"with the '-p' option in order to determine which setting results in more " +"connections. Use YES only if you get no connections otherwise. Set to AUTO " +"to use YES if the local IP is belongs to a private IP network and NO " +"otherwise." +msgstr "" +"Äặt thành YES (có) nếu máy này nằm sau má»™t máy NAT mà hạn chế kết nối từ bên " +"ngoài đến cổng GNUnet và không thể Ä‘i qua được dùng UPnP. Ghi chú rằng nếu " +"bạn đã cấu hình máy NAT để cho phép kết nối trá»±c tiếp từ máy khác đến cổng " +"GNUnet, hoặc nếu GNUnet có quyá»n mở cổng dùng UPnP, thì bạn nên đặt tùy chá»n " +"này thành NO (không). Chỉ đặt thành YES nếu đồng đẳng khác không thể liên " +"lạc vá»›i máy này má»™t cách trá»±c tiếp. Bạn cÅ©ng có thể sá»­ dụng câu lệnh « make " +"check » (trong /src/transports/upnp) để tìm biết nếu NAT đó há»— trợ UPnP " +"không. Bạn cÅ©ng có thể sá»­ dụng công cụ gnunet-transport-check vá»›i tùy chá»n « -" +"p » để quyết định thiết lập nào gây ra nhiá»u kết nối hÆ¡n. Äặt YES chỉ nếu " +"bạn không nhận kết nối bằng cách khác. Äặt thành AUTO (tá»± Ä‘á»™ng) để sá»­ dụng " +"YES nếu địa chỉ IP cục bá»™ thuá»™c vá» má»™t mạng IP riêng, không thì NO." + +#: contrib/config-daemon.scm:919 +msgid "Which port should be used by the TCP IPv4 transport?" +msgstr "CÆ¡ chế truyá»n IPv4 TCP nên dùng cổng nào?" + +#: contrib/config-daemon.scm:931 contrib/config-daemon.scm:1024 +#: contrib/config-daemon.scm:1174 +msgid "Should we try to determine our external IP using UPnP?" +msgstr "Có nên thá»­ quyết định địa chỉ IP bên ngoài của mình dùng UPnP không?" + +#: contrib/config-daemon.scm:943 +msgid "Which IP(v4)s are not allowed to connect?" +msgstr "Những địa chỉ IP(v4) nào không có quyá»n kết nối?" + +#: contrib/config-daemon.scm:955 +msgid "" +"Which IP(v4)s are allowed to connect? Leave empty to use the IP of your " +"primary network interface." +msgstr "" +"Những địa chỉ IP(v4) nào có quyá»n kết nối? Bá» trống để sá»­ dụng địa chỉ IP " +"của giao diện mạng chính." + +#: contrib/config-daemon.scm:967 contrib/config-daemon.scm:1222 +msgid "Which IPv6s are not allowed to connect?" +msgstr "Những địa chỉ IPv6 nào không có quyá»n kết nối?" + +#: contrib/config-daemon.scm:979 contrib/config-daemon.scm:1234 +msgid "" +"Which IPv6s are allowed to connect? Leave empty to allow any IP to connect." +msgstr "" +"Những địa chỉ IPv6 nào có quyá»n kết nối? Bá» trống để cho phép má»i địa chỉ IP " +"kết nối." + +#: contrib/config-daemon.scm:992 +msgid "TCP transport" +msgstr "CÆ¡ chế truyá»n TCP" + +#: contrib/config-daemon.scm:1012 +msgid "Which port should be used by the HTTP transport?" +msgstr "CÆ¡ chế truyá»n HTTP nên dùng cổng nào?" + +#: contrib/config-daemon.scm:1036 +msgid "Which is the external port of the HTTP transport?" +msgstr "CÆ¡ chế truyá»n HTTP dùng cổng bên ngoài nào?" + +#: contrib/config-daemon.scm:1037 +msgid "" +"Use this option if your firewall maps, say, port 80 to your real HTTP port. " +"This can be useful in making the HTTP messages appear even more legit " +"(without needing to run gnunetd as root due to the use of a privileged port)." +msgstr "" +"Hãy bật tùy chá»n này nếu bức tÆ°á»ng lá»­a ánh xạ, v.d. cổng 80 vá»›i cổng HTTP " +"thật. Có thể hữu ích để làm cho các thông Ä‘iệp HTTP hình nhÆ° ngay cả chính " +"đáng hÆ¡n (mà không cần chạy gnunetd dÆ°á»›i ngÆ°á»i chủ do dùng má»™t cổng có quyá»n " +"đặc biệt)." + +#: contrib/config-daemon.scm:1048 +msgid "HTTP transport" +msgstr "CÆ¡ chế truyá»n HTTP" + +#: contrib/config-daemon.scm:1067 +msgid "What is the maximum transfer unit for SMTP?" +msgstr "Äối vá»›i SMTP, Ä‘Æ¡n vị truyá»n tối Ä‘a là gì?" + +#: contrib/config-daemon.scm:1079 +msgid "" +"What is the maximum number of e-mails that gnunetd would be allowed to send " +"per hour?" +msgstr "gnunetd có quyá»n gá»­i má»—i giá» bao nhiêu thÆ° Ä‘iện tá»­ (tối Ä‘a)?" + +#: contrib/config-daemon.scm:1080 +msgid "Use 0 for unlimited" +msgstr "0 = vô hạn" + +#: contrib/config-daemon.scm:1091 +msgid "Which e-mail address should be used to send e-mail to this peer?" +msgstr "Äể gá»­i thÆ° cho đồng đẳng này, có nên dùng địa chỉ thÆ° Ä‘iện tá»­ nào?" + +#: contrib/config-daemon.scm:1092 +msgid "" +"You must make sure that e-mail received at this address is forwarded to the " +"PIPE which is read by gnunetd. Use the FILTER option to filter e-mail with " +"procmail and the PIPE option to set the name of the pipe." +msgstr "" +"Bạn cần phải đảm bảo rằng thÆ° Ä‘iện tá»­ được nhận ở địa chỉ này thật được " +"chuyển tiếp tá»›i PIPE (ống dẫn) được gnunetd Ä‘á»c. Hãy dùng tùy chá»n FILTER " +"(lá»c) để lá»c thÆ° bằng procmail, và tùy chá»n PIPE để đặt tên của ống dẫn." + +#: contrib/config-daemon.scm:1103 +msgid "" +"Which header line should other peers include in e-mails to enable filtering?" +msgstr "Äể hiệu lá»±c lá»c thÆ°, đồng đẳng khác nên ghi dòng đầu nào?" + +#: contrib/config-daemon.scm:1104 +msgid "" +"You can specify a header line here which can then be used by procmail to " +"filter GNUnet e-mail from your inbox and forward it to gnunetd." +msgstr "" +"Ở đây thì bạn có dịp ghi rõ má»™t dòng đầu cho procmail dùng để lá»c thÆ° GNUnet " +"từ há»™p ThÆ° Äến của bạn và chuyển tiếp nó tá»›i gnunetd." + +#: contrib/config-daemon.scm:1115 +msgid "What is the filename of the pipe where gnunetd can read its e-mail?" +msgstr "gnunetd có thể Ä‘á»c thÆ° qua ống dẫn có tên tập tin nào?" + +#: contrib/config-daemon.scm:1116 +msgid "Have a look at contrib/dot-procmailrc for an example .procmailrc file." +msgstr "Xem « contrib/dot-procmailrc » để tìm má»™t tập tin .procmailrc ví dụ." + +#: contrib/config-daemon.scm:1127 +msgid "What is the name and port of the server for outgoing e-mail?" +msgstr "Máy phục vụ thÆ° gá»­i ra có tên và cổng nào?" + +#: contrib/config-daemon.scm:1128 +msgid "The basic format is HOSTNAME:PORT." +msgstr "Äịnh dạng cÆ¡ bản là « TÊN_MÃY:Cá»”NG »." + +#: contrib/config-daemon.scm:1139 +msgid "SMTP transport" +msgstr "CÆ¡ chế SMTP" + +#: contrib/config-daemon.scm:1162 +msgid "Which port should be used by the UDP IPv4 transport?" +msgstr "CÆ¡ chế truyá»n IPv4 UDP nên dùng cổng nào?" + +#: contrib/config-daemon.scm:1186 +msgid "What is the maximum transfer unit for UDP?" +msgstr "Äối vá»›i UDP, Ä‘Æ¡n vị truyá»n tối Ä‘a là gì?" + +#: contrib/config-daemon.scm:1198 +msgid "Which IPs are not allowed to connect?" +msgstr "Những địa chỉ IP nào không có quyá»n kết nối?" + +#: contrib/config-daemon.scm:1210 +msgid "" +"Which IPs are allowed to connect? Leave empty to allow connections from any " +"IP." +msgstr "" +"Những địa chỉ IP nào có quyá»n kết nối? Bá» trống để cho phép kết nối từ má»i " +"địa chỉ IP." + +#: contrib/config-daemon.scm:1246 +msgid "UDP transport" +msgstr "CÆ¡ chế truyá»n UDP" + +#: contrib/config-daemon.scm:1268 +msgid "Network interface" +msgstr "Giao diện mạng" + +#: contrib/config-daemon.scm:1280 +msgid "External IP address (leave empty to try auto-detection)" +msgstr "Äịa chỉ IP bên ngoài (bá» trống để thá»­ tá»± Ä‘á»™ng phát hiện)" + +#: contrib/config-daemon.scm:1292 +msgid "External IPv6 address (leave empty to try auto-detection)" +msgstr "Äịa chỉ IPv6 bên ngoài (bá» trống để thá»­ tá»± Ä‘á»™ng phát hiện)" + +#: contrib/config-daemon.scm:1304 +msgid "Transports" +msgstr "CÆ¡ chế truyá»n" + +#: contrib/config-daemon.scm:1326 +msgid "What is the maximum number of bytes per second that we may receive?" +msgstr "Có thể nhận bao nhiêu byte má»—i giây (tối Ä‘a)?" + +#: contrib/config-daemon.scm:1338 +msgid "What is the maximum number of bytes per second that we may send?" +msgstr "Có thể gá»­i bao nhiêu byte má»—i giây (tối Ä‘a)?" + +#: contrib/config-daemon.scm:1350 +msgid "What is the maximum CPU load (percentage)?" +msgstr "Tải trá»ng CPU tối Ä‘a là gì (theo phần trăm)?" + +#: contrib/config-daemon.scm:1351 +msgid "" +"The highest tolerable CPU load. Load here always refers to the total system " +"load, that is it includes CPU utilization by other processes. A value of 50 " +"means that once your 1 minute-load average goes over 50% non-idle, GNUnet " +"will try to reduce CPU consumption until the load goes under the threshold. " +"Reasonable values are typically between 50 and 100. Multiprocessors may use " +"values above 100." +msgstr "" +"Tải trá»ng CPU cao nhất có thể. Ở đây thì tải trá»ng tham chiếu đến tổng số " +"tải trá»ng hệ thống, tức là nó cÅ©ng tính sá»­ dụng CPU của các tiến trình khác. " +"Giá trị 50 có nghÄ©a là má»™t khi tải trá»ng trung bình má»—i phút vượt quá 50% " +"không nghỉ, thì GNUnet thá»­ giảm sá»­ dụng CPU đến khi tải trá»ng nhá» hÆ¡n " +"ngưỡng. Giá trị hợp lý thÆ°á»ng nằm giữa 50 và 100. Máy Ä‘a bá»™ xá»­ lý có khả " +"năng sá»­ dụng giá trị hÆ¡n 100." + +#: contrib/config-daemon.scm:1362 +msgid "What is the maximum IO load (permille)?" +msgstr "Tải trá»ng VR tối Ä‘a là gì (theo phần nghìn)?" + +#: contrib/config-daemon.scm:1364 +msgid "" +"The highest tolerable IO load. Load here refers to the percentage of CPU " +"cycles wasted waiting for IO for the entire system, that is it includes disk " +"utilization by other processes. A value of 10 means that once the average " +"number of cycles wasted waiting for IO is more than 10% non-idle, GNUnet " +"will try to reduce IO until the load goes under the threshold. Reasonable " +"values are typically between 10 and 75." +msgstr "" +"Tải trá»ng VR cao nhất có thể tha thứ được. Ở đây thì tải trá»ng tham chiếu " +"đến tá»· lệ phần trăm chu kỳ CPU bị phí khi đợi VR đối vá»›i toàn hệ thống, tức " +"là nó cÅ©ng tính sá»­ dụng Ä‘Ä©a của các tiến trình khác. Giá trị 10 có nghÄ©a là " +"má»™t khi số các chu kỳ trung bình bị phí khi đợi VR hÆ¡n 10% không nghỉ, " +"GNUnet sẽ thá»­ giảm VR đến khi tải trá»ng nhá» hÆ¡n ngưỡng. Giá trị hợp lý " +"thÆ°á»ng nằm giữa 10 và 75." + +#: contrib/config-daemon.scm:1375 +msgid "What is the maximum CPU load (hard limit)?" +msgstr "Tải trá»ng CPU tối Ä‘a là gì (giá»›i hạn cứng)?" + +#: contrib/config-daemon.scm:1376 +msgid "" +"The highest tolerable CPU load. This is the hard limit, so once it is " +"reached, gnunetd will start to massively drop data to reduce the load. Use " +"with caution." +msgstr "" +"Tải trá»ng CPU cao nhất có thể tha thứ được. Äây là giá»›i hạn cứng, do đó má»™t " +"khi tá»›i được, gnunetd sẽ bắt đầu bá» rất nhiá»u dữ liệu để giảm tải trá»ng. Hãy " +"sá»­ dụng cẩn thận." + +#: contrib/config-daemon.scm:1387 +msgid "What is the maximum upstream bandwidth (hard limit)?" +msgstr "Băng thông dòng ra tối Ä‘a là gì (giá»›i hạn cứng)?" + +#: contrib/config-daemon.scm:1388 +msgid "" +"The limit is given as a percentage of the MAXNETUPBPS limit. Use 100 to " +"have MAXNETUPBPS be the hard limit. Use zero for no limit." +msgstr "" +"Giá»›i hạn được ghi rõ theo phần trăm của giá»›i hạn MAXNETUPBPS. Dùng 100 để " +"đặt MAXNETUPBPS là giá»›i hạn cứng. 0 = vô hạn." + +#: contrib/config-daemon.scm:1400 +#, fuzzy +msgid "What priority should gnunetd use to run?" +msgstr "gnunetd nên chạy nhÆ° nhóm nào?" + +#: contrib/config-daemon.scm:1401 +msgid "" +"You can specify priorities like NORMAL, ABOVE NORMAL, BELOW NORMAL, HIGH and " +"IDLE or a numerical integer value (man nice). The default is IDLE, which " +"should result in gnunetd only using resources that would otherwise be idle." +msgstr "" + +#: contrib/config-daemon.scm:1413 +msgid "Should we disable random padding (experimental option)?" +msgstr "Có nên tắt đệm ngẫu nhiên (tùy chá»n vẫn thá»±c nghiệm) không?" + +#: contrib/config-daemon.scm:1425 +msgid "Use basic bandwidth limitation? (YES/NO). If in doubt, say YES." +msgstr "" +"Äặt giá»›i hạn băng thông cÆ¡ bản không? (YES/NO) ChÆ°a chắc thì đặt YES (có)." + +#: contrib/config-daemon.scm:1427 +msgid "" +"Basic bandwidth limitation (YES) means simply that the bandwidth limits " +"specified apply to GNUnet and only to GNUnet. If set to YES, you simply " +"specify the maximum bandwidth (upstream and downstream) that GNUnet is " +"allowed to use and GNUnet will stick to those limitations. This is useful " +"if your overall bandwidth is so large that the limit is mostly used to " +"ensure that enough capacity is left for other applications. Even if you " +"want to dedicate your entire connection to GNUnet you should not set the " +"limits to values higher than what you have since GNUnet uses those limits to " +"determine for example the number of connections to establish (and it would " +"be inefficient if that computation yields a number that is far too high). \n" +"\n" +"While basic bandwidth limitation is simple and always works, there are some " +"situations where it is not perfect. Suppose you are running another " +"application which performs a larger download. During that particular time, " +"it would be nice if GNUnet would throttle its bandwidth consumption " +"(automatically) and resume using more bandwidth after the download is " +"complete. This is obviously advanced magic since GNUnet will have to " +"monitor the behavior of other applications. Another scenario is a monthly " +"cap on bandwidth imposed by your ISP, which you would want to ensure is " +"obeyed. Here, you may want GNUnet to monitor the traffic from other " +"applications to ensure that the combined long-term traffic is within the pre-" +"set bounds. Note that you should probably not set the bounds tightly since " +"GNUnet may observe that the bounds are about to be broken but would be " +"unable to stop other applications from continuing to use bandwidth.\n" +"\n" +"If either of these two scenarios applies, set BASICLIMITING to NO. Then set " +"the bandwidth limits to the COMBINED amount of traffic that is acceptable " +"for both GNUnet and other applications. GNUnet will then immediately " +"throttle bandwidth consumption if the short-term average is above the limit, " +"and it will also try to ensure that the long-term average is below the " +"limit. Note however that using NO can have the effect of GNUnet (almost) " +"ceasing operations after other applications perform high-volume downloads " +"that are beyond the defined limits. GNUnet would reduce consumption until " +"the long-term limits are again within bounds.\n" +"\n" +"NO only works on platforms where GNUnet can monitor the amount of traffic " +"that the local host puts out on the network. This is only implemented for " +"Linux and Win32. In order for the code to work, GNUnet needs to know the " +"specific network interface that is used for the external connection (after " +"all, the amount of traffic on loopback or on the LAN should never be counted " +"since it is irrelevant)." +msgstr "" +"Giá»›i hạn băng thông cÆ¡ bản (YES) có nghÄ©a Ä‘Æ¡n giản là má»—i giá»›i hạn băng " +"thông Ä‘Æ°a ra chỉ áp dụng cho GNUnet. Nếu đặt thành YES (có) thì bạn Ä‘Æ¡n giản " +"chỉ ghi rõ băng thông tối Ä‘a (dòng ra và dòng vào) cho phép GNUnet dùng, và " +"GNUnet sẽ tuân theo những giá»›i hạn đó. Có ích nếu băng thông toàn bá»™ lá»›n quá " +"đến mức là giá»›i hạn thÆ°á»ng dùng để đảm bảo đủ khả năng còn lại cho các ứng " +"dụng khác. Thậm chí nếu bạn muốn dành kết nối hoàn toàn cho GNUnet, không " +"nên đặt giá»›i hạn thành giá trị cao hÆ¡n khả năng, vì GNUnet dùng giá»›i hạn đó " +"để quyết định, ví dụ, số các kết nối nên thiết lập (không có hiệu quả nếu " +"phép tính có kết quả là quá lá»›n).\n" +"\n" +"Dù chức năng hạn chế băng thông cÆ¡ bản vẫn Ä‘Æ¡n giản và lúc nào cÅ©ng hoạt " +"Ä‘á»™ng được, trong má»™t số trÆ°á»ng hợp nào đó nó không phải hoàn toàn. Giả sá»­ " +"bạn Ä‘ang chạy má»™t ứng dụng khác mà thá»±c hiện má»™t công việc tải xuống lá»›n " +"hÆ¡n. Trong khi làm công việc đó, có ích nếu GNUnet giảm tiêu thụ băng thông " +"(má»™t cách tá»± Ä‘á»™ng), cÅ©ng tăng lại má»™t khi công việc tải xuống đó đã chạy " +"xong. Chức năng này thá»±c sá»± phức tạp, vì GNUnet phải theo dõi ứng xá»­ của các " +"ứng dụng khác. Má»™t trÆ°á»ng hợp khác là má»™t ngưỡng băng thông hàng tháng được " +"nhà cung cấp dịch vụ Internet (ISP) Ä‘iá»u khiển: bạn muốn đảm bảo tuân theo " +"nó. Trong trÆ°á»ng hợp đó, bạn có thể muốn theo dõi tải cho mạng từ các ứng " +"dụng khác để đảm bảo rằng tổng số tải trá»ng lâu dài nằm dÆ°á»›i ngưỡng định " +"sẵn. Ghi chú rằng không nên đặt ngưỡng quá chặt chẽ vì GNUnet có thể phát " +"hiện ngưỡng sắp bị vượt quá nhÆ°ng không thể ngăn cản ứng dụng khác tiếp tục " +"chiếm băng thông.\n" +"\n" +"Nếu có má»™t của hai trÆ°á»ng hợp này, hãy đặt BASICLIMITING thành NO (không). " +"Sau đó thì đặt giá»›i hạn băng thông thành Tá»”NG Sá» tải trá»ng thoả đáng cho cả " +"hai GNUnet và ứng dụng khác. GNUnet thì giảm ngay tiêu thụ băng thông nếu " +"trung bình ngắn kỳ hÆ¡n giá»›i hạn, nó cÅ©ng thá»­ đảm bảo rằng trung bình lâu dài " +"dÆ°á»›i giá»›i hạn. Tuy nhiên, ghi chú rằng việc đặt NO (không) có kết quả có thể " +"là GNUnet gần dừng hoạt Ä‘á»™ng sau khi ứng dụng khác chạy công việc lá»›n mà " +"vượt quá giá»›i hạn đã xác định. Lúc đó, GNUnet sẽ giảm tiêu thụ đến khi giá»›i " +"hạn lâu dài lại nằm trong phạm vi Ä‘Æ°a ra.\n" +"\n" +"Giá trị NO chỉ hoạt Ä‘á»™ng được trên ná»n tảng thích hợp vá»›i GNUnet theo dõi " +"tải cho mạng được máy cục bá»™ gá»­i. Chức năng này chỉ được thá»±c hiện cho Linux " +"và Win32. Äể mà mã hoạt Ä‘á»™ng được, GNUnet cần biết giao diện mạng dứt khoát " +"dùng cho kết nối bên ngoài (thôi, lượng tải trá»ng trên mạch ná»™i bá»™ hay LAN " +"không bao giá» nên được đếm vì nó không thích đáng). " + +#: contrib/config-daemon.scm:1444 +msgid "Network interface to monitor" +msgstr "Giao diện mạng cần theo dõi" + +#: contrib/config-daemon.scm:1445 +msgid "" +"For which interfaces should we do accounting? GNUnet will evaluate the " +"total traffic (not only the GNUnet related traffic) and adjust its bandwidth " +"usage accordingly. You can currently only specify a single interface. GNUnet " +"will also use this interface to determine the IP to use. Typical values are " +"eth0, ppp0, eth1, wlan0, etc. 'ifconfig' will tell you what you have. " +"Never use 'lo', that just won't work. Under Windows, specify the index " +"number reported by 'gnunet-win-tool -n'." +msgstr "" +"Có nên tính đến những giao diện nào? GNUnet sẽ định giá tổng số tải trá»ng " +"(không phải chỉ tải trá»ng liên quan đến GNUnet) và Ä‘iá»u chỉnh sá»­ dụng băng " +"thông má»™t cách thích hợp. Hiện thá»i ngÆ°á»i dùng chỉ có thể ghi rõ má»™t giao " +"diện riêng lẻ. GNUnet sẽ cÅ©ng sá»­ dụng giao doện này để quyết định địa chỉ IP " +"cần dùng. Giá trị thÆ°á»ng dùng: eth0, ppp0, eth1, wlan0, v.v. Lệnh « ifconfig " +"» sẽ hiển thị những giao diện sẵn sàng. Không bao giá» nên dùng « lo » vì nó " +"không hoạt Ä‘á»™ng được. DÆ°á»›i Windows, hãy ghi rõ số chỉ mục trả lại bởi câu " +"lệnh « gnunet-win-tool -n »." + +#: contrib/config-daemon.scm:1456 +msgid "Load management" +msgstr "Quản lý tải trá»ng" + +#: contrib/config-daemon.scm:1482 contrib/config-client.scm:413 +msgid "Root node" +msgstr "Nút gốc" + +#: contrib/config-client.scm:153 +msgid "Where should gnunet-clients write their logs?" +msgstr "Ứng dụng khách gnunet-clients nên ghi sổ theo dõi vào đâu?" + +#: contrib/config-client.scm:185 +msgid "On which machine and port is gnunetd running (for clients)?" +msgstr "gnunetd Ä‘ang chạy trên máy và cổng nào (cho ứng dụng khách)?" + +#: contrib/config-client.scm:186 +msgid "This is equivalent to the -H option. The format is IP:PORT." +msgstr "Äây tÆ°Æ¡ng Ä‘Æ°Æ¡ng vá»›i tùy chá»n « -H ». Äịnh dạng là « IP:Cá»”NG »." + +#: contrib/config-client.scm:197 +msgid "What is the path to the configuration file for gnunetd?" +msgstr "Tập tin cấu hình gnunetd có Ä‘Æ°á»ng dẫn nào?" + +#: contrib/config-client.scm:198 +msgid "This option is used when clients need to start gnunetd." +msgstr "Tùy chá»n này dùng khi ứng dụng khách cần khởi chạy gnunetd." + +#: contrib/config-client.scm:210 +msgid "General options" +msgstr "Tùy chá»n chung" + +#: contrib/config-client.scm:227 +msgid "Do not add metadata listing the creation time for inserted content" +msgstr "" +"Không nên thêm siêu dữ liệu mà ghi nhá»› giá» tạo của ná»™i dung được chèn vào" + +#: contrib/config-client.scm:239 +msgid "Which non-default extractors should GNUnet use for keyword extractors" +msgstr "" +"Äối vá»›i bá»™ trích khác từ khoá, GNUnet nên dùng những bá»™ trích khác mặc định " +"nào?" + +#: contrib/config-client.scm:240 +msgid "" +"Specify which additional extractor libraries should be used. gnunet-insert " +"uses libextractor to extract keywords from files. libextractor can be " +"dynamically extended to handle additional file formats. If you want to use " +"more than the default set of extractors, specify additional extractor " +"libraries here. The format is [[-]LIBRARYNAME[:[-]LIBRARYNAME]*].\n" +"\n" +"The default is to use filenames and to break larger words at spaces (and " +"underscores, etc.). This should be just fine for most people. The '-' " +"before a library name indicates that this should be executed last and makes " +"only sense for the split-library." +msgstr "" +"Ghi rõ những thÆ° viện trích bổ sung nào nên dùng. Tiến trình chèn gnunet-" +"insert sá»­ dụng libextractor để trích các từ khoá khá»i tập tin. libextractor " +"cÅ©ng có thể mở rá»™ng Ä‘á»™ng để quản lý định dạng tập tin bổ sung. Muốn sá»­ dụng " +"bá»™ trích bên ngoài tập hợp mặc định thì ghi rõ ở đây những thÆ° viện trích bổ " +"sung. Äịnh dạng là « [[-]tên_thÆ°_viện[:[-]tên_thÆ°_viện]*] ».\n" +"\n" +"Mặc định là dùng tên tập tin, và ngắt từ lá»›n ở dấu cách, dấu gạch dÆ°á»›i v.v. " +"Äây nên ổn cho phần lá»›n ngÆ°á»i dùng. Dấu gạch nối « - » đằng trÆ°á»›c má»™t tên thÆ° " +"viện có ngụ ý nó nên được thá»±c hiện cuối cùng, thích hợp chỉ cho split-" +"library." + +#: contrib/config-client.scm:253 +msgid "How many entries should the URI DB table have?" +msgstr "Bảng DB URI nên chứa bao nhiêu mục nhập?" + +#: contrib/config-client.scm:254 +msgid "" +"GNUnet uses two bytes per entry on the disk. This database is used to keep " +"track of how a particular URI has been used in the past. For example, " +"GNUnet may remember that a particular URI has been found in a search " +"previously or corresponds to a file uploaded by the user. This information " +"can then be used by user-interfaces to filter URI lists, such as search " +"results. If the database is full, older entries will be discarded. The " +"default value should be sufficient without causing undue disk utilization." +msgstr "" +"GNUnet dùng hai byte cho má»—i mục nhập trên Ä‘Ä©a. CÆ¡ sở dữ liệu này dùng để " +"theo dõi má»™t URI nào đó đã được sá»­ dụng nhÆ° thế nào. Ví dụ, GNUnet có thể " +"nhá»› rằng má»™t URI nào đó đã được phát hiện bằng má»™t công việc tìm kiếm trÆ°á»›c, " +"hay tÆ°Æ¡ng ứng vá»›i má»™t tập tin được ngÆ°á»i dùng tải lên. Thông tin này thì có " +"thể được dùng bởi giao diện ngÆ°á»i dùng để lá»c danh sách các URI, nhÆ° kết quả " +"tìm kiếm. Nếu cÆ¡ sở dữ liệu bị đầy thì hủy các mục nhập cÅ©. Giá trị mặc định " +"nên ổn mà không gây ra sá»­ dụng Ä‘Ä©a má»™t cách quá chừng." + +#: contrib/config-client.scm:265 +msgid "Location of the file specifying metadata for the auto-share directory" +msgstr "Vị trí của tập tin ghi rõ siêu dữ liệu cho thÆ° mục tá»± Ä‘á»™ng chia sẻ" + +#: contrib/config-client.scm:277 +msgid "" +"Location of the file with the PID of any running gnunet-auto-share daemon " +"process" +msgstr "" +"Vị trí của tập tin chứa PID của bất kỳ tiến trình ná»n gnunet-auto-share" + +#: contrib/config-client.scm:289 +msgid "Location of the log file for gnunet-auto-share" +msgstr "Vị trí của tập tin theo dõi cho gnunet-auto-share" + +#: contrib/config-client.scm:301 +msgid "File-Sharing options" +msgstr "Tùy chá»n chia sẻ tập tin" + +#: contrib/config-client.scm:319 +msgid "Which plugins should be loaded by gnunet-gtk?" +msgstr "gnunet-gtk nên nạp những phần bổ sung nào?" + +#: contrib/config-client.scm:320 +msgid "" +"Load the about plugin for the about dialog. The daemon plugin allows " +"starting and stopping of gnunetd and displays information about gnunetd. " +"The fs plugin provides the file-sharing functionality. The stats plugin " +"displays various statistics about gnunetd." +msgstr "" +"Nạp phần bổ sung about cho há»™p thoại giá»›i thiệu. Phần bổ sung daemon cho " +"phép khởi chạy và dừng chạy trình ná»n gnunetd, và hiển thị thông tin vá» " +"gnunetd. Phần bổ sung fs cung cấp chức năng chia sẻ tập tin. Phần bổ sung " +"stats hiển thị thống kê khác nhau vá» gnunetd." + +#: contrib/config-client.scm:331 +msgid "How frequently (in milli-seconds) should the statistics update?" +msgstr "Thống kê có nên cập nhật thÆ°á»ng xuyên cỡ nào (theo mili-giây)?" + +#: contrib/config-client.scm:332 +msgid "" +"Each pixel in the stats dialog corresponds to the time interval specified " +"here." +msgstr "" +"Má»—i Ä‘iểm ảnh trong há»™p thoại thống kê thì tÆ°Æ¡ng ứng vá»›i khoảng thá»i gian Ä‘Æ°a " +"ra ở đây." + +#: contrib/config-client.scm:344 +msgid "Do not show thumbnail previews from meta-data in search results" +msgstr "" +"Không hiển thị ô xem thá»­ ảnh mẫu từ siêu dữ liệu trong kết quả tìm kiếm" + +#: contrib/config-client.scm:345 +msgid "" +"This option is useful for people who maybe offended by some previews or use " +"gnunet-gtk at work and would like to avoid bad surprises." +msgstr "" +"Tùy chá»n này có ích cho ngÆ°á»i dùng không thích má»™t số ô xem thá»­, hoặc sá»­ " +"dụng gnunet-gtk ở chá»— làm và muốn tránh sá»± ngạc nhiên xấu." + +#: contrib/config-client.scm:356 +msgid "Do not show search results for files that were uploaded by us" +msgstr "Không hiển thị kết quả tìm kiếm cho tập tin được chúng ta tải lên" + +#: contrib/config-client.scm:357 +msgid "" +"This option is useful to eliminate files that the user already has from the " +"search. Naturally, enabling this option maybe confusing because some " +"obviously expected search results would no longer show up. This option only " +"works if the URI_DB_SIZE option under FS is not zero (since the URI DB is " +"used to determine which files the user is sharing)" +msgstr "" +"Tùy chá»n này có ích để loại ra việc tìm kiếm những tập tin đã có trên máy " +"đó. Tất nhiên, bật tùy chá»n có thể gây ra bối rối vì má»™t số kết quả tìm kiếm " +"mong đợi sẽ không được hiển thị. Tùy chá»n này chỉ hoạt Ä‘á»™ng được nếu tùy " +"chá»n URI_DB_SIZE (kích cỡ cÆ¡ sở dữ liệu URI) dÆ°á»›i FS khác số không (vì cÆ¡ sở " +"dữ liệu URI được dùng để quyết định những tập tin được ngÆ°á»i dùng chia sẻ)" + +#: contrib/config-client.scm:369 +msgid "To which directory should gnunet-gtk save downloads to?" +msgstr "gnunet-gtk nên lÆ°u tập tin tải vá» vào thÆ° mục nào?" + +#: contrib/config-client.scm:381 +msgid "Options related to gnunet-gtk" +msgstr "Tùy chá»n liên quan đến gnunet-gtk" + +#: contrib/config-client.scm:401 +msgid "Full pathname of GNUnet client HOME directory" +msgstr "Tên Ä‘Æ°á»ng dẫn đầy đủ đến thÆ° mục HOME khách GNUnet" + +#: contrib/config-client.scm:402 +msgid "The directory for GNUnet files that belong to the user." +msgstr "ThÆ° mục chứa những tập tin GNUnet mà thuá»™c vá» ngÆ°á»i dùng." + +#~ msgid "Setting option `%s' in section `%s' to value `%s' was refused.\n" +#~ msgstr "Bị từ chối đặt tùy chá»n « %s » trong phần « %s » thành « %s ».\n" + +#~ msgid "`%s' failed at %s:%d with error: %s" +#~ msgstr "« %s » bị lá»—i tại %s:%d vá»›i lá»—i: %s" + +#~ msgid "`%s' failed at %s:%d with error `%s' after %llums\n" +#~ msgstr "« %s » bị lá»—i tại %s:%d vá»›i lá»—i:« %s » đẳng sau %llu miligiây\n" + +#~ msgid "Trying to use file `%s' for MySQL configuration.\n" +#~ msgstr "Äang thá»­ dùng tập tin « %s » cho cấu hình MySQL.\n" + +#~ msgid "" +#~ "Directory `%s' in directory `%s' does not match naming convention. " +#~ "Removed.\n" +#~ msgstr "" +#~ "ThÆ° mục « %s » trong thÆ° mục « %s » không tùy theo quy Æ°á»›c đặt tên. Bị gỡ " +#~ "bá».\n" + +#~ msgid "File `%s' does not contain a pseudonym, trying to remove.\n" +#~ msgstr "Tập tin « %s » không chứa biệt hiệu nên thá»­ gỡ bá».\n" + +#~ msgid "Format of file `%s' is invalid.\n" +#~ msgstr "Äịnh dạng của tập tin « %s » là không hợp lệ.\n" diff --git a/pre-commit b/pre-commit new file mode 100755 index 000000000..b733c44fa --- /dev/null +++ b/pre-commit @@ -0,0 +1,17 @@ +#!/bin/sh +# NOTE: "touch mydir/no-indent" to make the tree starting at mydir untouchable +l="" +for n in `find . -name "no-indent"` +do + n=`dirname "$n"` + l="-and -not -path \"$n/*\" $l" +done +indent -nut `echo $l | xargs find . -name "*.c"` +indent -nut `echo $l | xargs find . -name "*.h"` +if test -x "`which 'dos2unix'`" +then + dos2unix -ko `echo $l | xargs find . -name "*.c"` + dos2unix -ko `echo $l | xargs find . -name "*.h"` + rm `echo $l | xargs find . -name "*.?~"` +fi + diff --git a/src/Makefile.am b/src/Makefile.am new file mode 100644 index 000000000..c8852ad04 --- /dev/null +++ b/src/Makefile.am @@ -0,0 +1,17 @@ +#if WANT_FRAMEWORK +# INTLEMU_SUBDIRS = intlemu +#endif + +SUBDIRS = \ + include $(INTLEMU_SUBDIRS) \ + util \ + arm \ + fragmentation \ + hello \ + peerinfo \ + resolver \ + statistics \ + template \ + transport \ + core + diff --git a/src/arm/Makefile.am b/src/arm/Makefile.am new file mode 100644 index 000000000..65fa145ee --- /dev/null +++ b/src/arm/Makefile.am @@ -0,0 +1,58 @@ +INCLUDES = -I$(top_srcdir)/src/include + +if MINGW + WINFLAGS = -Wl,--no-undefined -Wl,--export-all-symbols +endif + +if USE_COVERAGE + AM_CFLAGS = -fprofile-arcs -ftest-coverage +endif + +lib_LTLIBRARIES = libgnunetarm.la + +libgnunetarm_la_SOURCES = \ + arm_api.c arm.h +libgnunetarm_la_LIBADD = \ + $(top_builddir)/src/util/libgnunetutil.la \ + $(GN_LIBINTL) +libgnunetarm_la_LDFLAGS = \ + $(GN_LIB_LDFLAGS) $(WINFLAGS) \ + -version-info 0:0:0 + + +bin_PROGRAMS = \ + gnunet-arm \ + gnunet-service-arm + +gnunet_arm_SOURCES = \ + gnunet-arm.c +gnunet_arm_LDADD = \ + $(top_builddir)/src/arm/libgnunetarm.la \ + $(top_builddir)/src/util/libgnunetutil.la \ + $(GN_LIBINTL) + +gnunet_service_arm_SOURCES = \ + gnunet-service-arm.c +gnunet_service_arm_LDADD = \ + $(top_builddir)/src/util/libgnunetutil.la \ + $(GN_LIBINTL) + + +check_PROGRAMS = \ + test_arm_api + +check_SCRIPTS = \ + test_gnunet_arm.sh + +TESTS = $(check_PROGRAMS) $(check_SCRIPTS) + +test_arm_api_SOURCES = \ + test_arm_api.c +test_arm_api_LDADD = \ + $(top_builddir)/src/arm/libgnunetarm.la \ + $(top_builddir)/src/resolver/libgnunetresolver.la \ + $(top_builddir)/src/util/libgnunetutil.la + + +EXTRA_DIST = \ + test_arm_api_data.conf diff --git a/src/arm/arm.h b/src/arm/arm.h new file mode 100644 index 000000000..2293ec036 --- /dev/null +++ b/src/arm/arm.h @@ -0,0 +1,32 @@ +/* + This file is part of GNUnet. + (C) 2009 Christian Grothoff (and other contributing authors) + + GNUnet is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published + by the Free Software Foundation; either version 2, 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 + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with GNUnet; see the file COPYING. If not, write to the + Free Software Foundation, Inc., 59 Temple Place - Suite 330, + Boston, MA 02111-1307, USA. +*/ + +/** + * @author Christian Grothoff + * @file arm/arm.h + */ +#ifndef ARM_H +#define ARM_H + +#include "gnunet_common.h" + +#define DEBUG_ARM GNUNET_NO + +#endif diff --git a/src/arm/arm_api.c b/src/arm/arm_api.c new file mode 100644 index 000000000..c5bb15fd3 --- /dev/null +++ b/src/arm/arm_api.c @@ -0,0 +1,337 @@ +/* + This file is part of GNUnet. + (C) 2009 Christian Grothoff (and other contributing authors) + + GNUnet is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published + by the Free Software Foundation; either version 2, 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 + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with GNUnet; see the file COPYING. If not, write to the + Free Software Foundation, Inc., 59 Temple Place - Suite 330, + Boston, MA 02111-1307, USA. +*/ + +/** + * @file arm/arm_api.c + * @brief API for accessing the ARM service + * @author Christian Grothoff + */ +#include "platform.h" +#include "gnunet_arm_service.h" +#include "gnunet_client_lib.h" +#include "gnunet_getopt_lib.h" +#include "gnunet_os_lib.h" +#include "gnunet_protocols.h" +#include "gnunet_server_lib.h" +#include "arm.h" + + +struct ArmContext +{ + GNUNET_ARM_Callback callback; + void *cls; + char *service_name; + struct GNUNET_CLIENT_Connection *client; + struct GNUNET_CONFIGURATION_Handle *cfg; + struct GNUNET_TIME_Absolute timeout; + uint16_t type; +}; + + +static void +arm_service_report (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc) +{ + struct ArmContext *pos = cls; + pid_t pid; + char *binary; + char *config; + + if (0 != (tc->reason & GNUNET_SCHEDULER_REASON_PREREQ_DONE)) + { + if (pos->callback != NULL) + pos->callback (pos->cls, GNUNET_YES); + GNUNET_free (pos); + return; + } + binary = NULL; + config = NULL; + /* start service */ + if ((GNUNET_OK != + GNUNET_CONFIGURATION_get_value_filename (pos->cfg, + "arm", + "BINARY", + &binary)) || + (GNUNET_OK != + GNUNET_CONFIGURATION_get_value_filename (pos->cfg, + "arm", "CONFIG", &config))) + { + GNUNET_log (GNUNET_ERROR_TYPE_WARNING, + _("Configuration file or binary for ARM not known!\n")); + if (pos->callback != NULL) + pos->callback (pos->cls, GNUNET_SYSERR); + GNUNET_free_non_null (binary); + GNUNET_free (pos); + return; + } + pid = GNUNET_OS_start_process (binary, binary, "-d", "-c", config, +#if DEBUG_ARM + "-L", "DEBUG", +#endif + NULL); + GNUNET_free (binary); + GNUNET_free (config); + if (pid == -1) + { + if (pos->callback != NULL) + pos->callback (pos->cls, GNUNET_SYSERR); + GNUNET_free (pos); + return; + } + /* FIXME: consider checking again to see if it worked!? */ + if (pos->callback != NULL) + pos->callback (pos->cls, GNUNET_YES); + GNUNET_free (pos); +} + + +static void +handle_response (void *cls, const struct GNUNET_MessageHeader *msg) +{ + struct ArmContext *sc = cls; + int ret; + + if (msg == NULL) + { + GNUNET_log (GNUNET_ERROR_TYPE_WARNING, + _("Error receiving response from ARM service\n")); + GNUNET_CLIENT_disconnect (sc->client); + if (sc->callback != NULL) + sc->callback (sc->cls, GNUNET_SYSERR); + GNUNET_free (sc); + return; + } +#if DEBUG_ARM + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + _("Received response from ARM service\n")); +#endif + switch (ntohs (msg->type)) + { + case GNUNET_MESSAGE_TYPE_ARM_IS_UP: + ret = GNUNET_YES; + break; + case GNUNET_MESSAGE_TYPE_ARM_IS_DOWN: + ret = GNUNET_NO; + break; + default: + GNUNET_break (0); + ret = GNUNET_SYSERR; + } + GNUNET_CLIENT_disconnect (sc->client); + if (sc->callback != NULL) + sc->callback (sc->cls, ret); + GNUNET_free (sc); +} + + +static size_t +send_service_msg (void *cls, size_t size, void *buf) +{ + struct ArmContext *sctx = cls; + struct GNUNET_MessageHeader *msg; + size_t slen; + + if (buf == NULL) + { + GNUNET_log (GNUNET_ERROR_TYPE_WARNING, + _("Error while trying to transmit to ARM service\n")); + GNUNET_CLIENT_disconnect (sctx->client); + if (sctx->callback != NULL) + sctx->callback (sctx->cls, GNUNET_SYSERR); + GNUNET_free (sctx->service_name); + GNUNET_free (sctx); + return 0; + } +#if DEBUG_ARM + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + _("Transmitting service request to ARM.\n")); +#endif + slen = strlen (sctx->service_name) + 1; + GNUNET_assert (size >= slen); + msg = buf; + msg->size = htons (sizeof (struct GNUNET_MessageHeader) + slen); + msg->type = htons (sctx->type); + memcpy (&msg[1], sctx->service_name, slen); + GNUNET_free (sctx->service_name); + sctx->service_name = NULL; + GNUNET_CLIENT_receive (sctx->client, + &handle_response, + sctx, + GNUNET_TIME_absolute_get_remaining (sctx->timeout)); + return slen + sizeof (struct GNUNET_MessageHeader); +} + + +/** + * Start or stop a service. + * + * @param service_name name of the service + * @param cfg configuration to use (needed to contact ARM; + * the ARM service may internally use a different + * configuration to determine how to start the service). + * @param sched scheduler to use + * @param timeout how long to wait before failing for good + * @param cb callback to invoke when service is ready + * @param cb_cls closure for callback + */ +static void +change_service (const char *service_name, + struct GNUNET_CONFIGURATION_Handle *cfg, + struct GNUNET_SCHEDULER_Handle *sched, + struct GNUNET_TIME_Relative timeout, + GNUNET_ARM_Callback cb, void *cb_cls, uint16_t type) +{ + struct GNUNET_CLIENT_Connection *client; + struct ArmContext *sctx; + size_t slen; + + slen = strlen (service_name) + 1; + if (slen + sizeof (struct GNUNET_MessageHeader) > + GNUNET_SERVER_MAX_MESSAGE_SIZE) + { + GNUNET_break (0); + if (cb != NULL) + cb (cb_cls, GNUNET_NO); + return; + } + client = GNUNET_CLIENT_connect (sched, "arm", cfg); + if (client == NULL) + { + GNUNET_log (GNUNET_ERROR_TYPE_WARNING, + _("Failed to connect to ARM service\n")); + if (cb != NULL) + cb (cb_cls, GNUNET_SYSERR); + return; + } +#if DEBUG_ARM + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + _("ARM requests starting of service `%s'.\n"), service_name); +#endif + sctx = GNUNET_malloc (sizeof (struct ArmContext)); + sctx->callback = cb; + sctx->cls = cb_cls; + sctx->client = client; + sctx->service_name = GNUNET_strdup (service_name); + sctx->timeout = GNUNET_TIME_relative_to_absolute (timeout); + sctx->type = type; + if (NULL == + GNUNET_CLIENT_notify_transmit_ready (client, + slen + + sizeof (struct + GNUNET_MessageHeader), + timeout, &send_service_msg, sctx)) + { + GNUNET_log (GNUNET_ERROR_TYPE_WARNING, + _("Failed to transmit request to ARM service\n")); + GNUNET_free (sctx->service_name); + GNUNET_free (sctx); + if (cb != NULL) + cb (cb_cls, GNUNET_SYSERR); + GNUNET_CLIENT_disconnect (client); + return; + } +} + + +/** + * Start a service. + * + * @param service_name name of the service + * @param cfg configuration to use (needed to contact ARM; + * the ARM service may internally use a different + * configuration to determine how to start the service). + * @param sched scheduler to use + * @param timeout how long to wait before failing for good + * @param cb callback to invoke when service is ready + * @param cb_cls closure for callback + */ +void +GNUNET_ARM_start_service (const char *service_name, + struct GNUNET_CONFIGURATION_Handle *cfg, + struct GNUNET_SCHEDULER_Handle *sched, + struct GNUNET_TIME_Relative timeout, + GNUNET_ARM_Callback cb, void *cb_cls) +{ + struct ArmContext *sctx; + + GNUNET_log (GNUNET_ERROR_TYPE_INFO, + _("Starting service `%s'\n"), service_name); + if (0 == strcmp ("arm", service_name)) + { + sctx = GNUNET_malloc (sizeof (struct ArmContext)); + sctx->callback = cb; + sctx->cls = cb_cls; + sctx->cfg = cfg; + GNUNET_CLIENT_service_test (sched, + "arm", + cfg, timeout, &arm_service_report, sctx); + return; + } + change_service (service_name, + cfg, + sched, timeout, cb, cb_cls, GNUNET_MESSAGE_TYPE_ARM_START); +} + + + + +/** + * Stop a service. + * + * @param service_name name of the service + * @param cfg configuration to use (needed to contact ARM; + * the ARM service may internally use a different + * configuration to determine how to start the service). + * @param sched scheduler to use + * @param timeout how long to wait before failing for good + * @param cb callback to invoke when service is ready + * @param cb_cls closure for callback + */ +void +GNUNET_ARM_stop_service (const char *service_name, + struct GNUNET_CONFIGURATION_Handle *cfg, + struct GNUNET_SCHEDULER_Handle *sched, + struct GNUNET_TIME_Relative timeout, + GNUNET_ARM_Callback cb, void *cb_cls) +{ + struct GNUNET_CLIENT_Connection *client; + + GNUNET_log (GNUNET_ERROR_TYPE_INFO, + _("Stopping service `%s'\n"), service_name); + if (0 == strcmp ("arm", service_name)) + { + client = GNUNET_CLIENT_connect (sched, "arm", cfg); + if (client == NULL) + { + if (cb != NULL) + cb (cb_cls, GNUNET_SYSERR); + return; + } + GNUNET_CLIENT_service_shutdown (client); + GNUNET_CLIENT_disconnect (client); + if (cb != NULL) + cb (cb_cls, GNUNET_NO); + return; + } + change_service (service_name, + cfg, + sched, timeout, cb, cb_cls, GNUNET_MESSAGE_TYPE_ARM_STOP); +} + +/* end of arm_api.c */ diff --git a/src/arm/gnunet-arm.c b/src/arm/gnunet-arm.c new file mode 100644 index 000000000..f8f5bc20f --- /dev/null +++ b/src/arm/gnunet-arm.c @@ -0,0 +1,180 @@ +/* + This file is part of GNUnet. + (C) 2009 Christian Grothoff (and other contributing authors) + + GNUnet is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published + by the Free Software Foundation; either version 2, 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 + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with GNUnet; see the file COPYING. If not, write to the + Free Software Foundation, Inc., 59 Temple Place - Suite 330, + Boston, MA 02111-1307, USA. +*/ + +/** + * @file arm/gnunet-arm.c + * @brief arm for writing a tool + * @author Christian Grothoff + */ +#include "platform.h" +#include "gnunet_arm_service.h" +#include "gnunet_client_lib.h" +#include "gnunet_getopt_lib.h" +#include "gnunet_program_lib.h" +#include "gnunet_time_lib.h" + +/** + * Timeout for all operations. + */ +#define TIMEOUT GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS, 5) + +/** + * Set if we are to shutdown all services (including ARM). + */ +static int end; + +/** + * Set if we are to start default services (including ARM). + */ +static int start; + +/** + * Set to the name of a service to start. + */ +static char *init; + +/** + * Set to the name of a service to kill. + */ +static char *term; + +/** + * Set to the name of a service to test. + */ +static char *test; + +/** + * Final status code. + */ +static int ret; + + +static void +confirm_cb (void *cls, int success) +{ + const char *service = cls; + switch (success) + { + case GNUNET_OK: + fprintf (stdout, _("Service `%s' is now running.\n"), service); + break; + case GNUNET_NO: + fprintf (stdout, _("Service `%s' is not running.\n"), service); + break; + case GNUNET_SYSERR: + fprintf (stdout, + _("Error updating service `%s': ARM not running\n"), service); + break; + } +} + + +static void +confirm_task (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc) +{ + const char *service = cls; + + if (0 != (tc->reason & GNUNET_SCHEDULER_REASON_PREREQ_DONE)) + fprintf (stdout, _("Service `%s' is running.\n"), service); + else + fprintf (stdout, _("Service `%s' is not running.\n"), service); +} + + +/** + * Main function that will be run by the scheduler. + * + * @param cls closure + * @param sched the scheduler to use + * @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, + struct GNUNET_SCHEDULER_Handle *sched, + char *const *args, + const char *cfgfile, struct GNUNET_CONFIGURATION_Handle *cfg) +{ + if (term != NULL) + { + GNUNET_ARM_stop_service (term, cfg, sched, TIMEOUT, &confirm_cb, term); + } + if (end) + { + GNUNET_ARM_stop_service ("arm", + cfg, sched, TIMEOUT, &confirm_cb, "arm"); + } + if (start) + { + GNUNET_ARM_start_service ("arm", + cfg, sched, TIMEOUT, &confirm_cb, "arm"); + } + if (init != NULL) + { + GNUNET_ARM_start_service (init, cfg, sched, TIMEOUT, &confirm_cb, init); + } + if (test != NULL) + { + GNUNET_CLIENT_service_test (sched, + test, cfg, TIMEOUT, &confirm_task, test); + } +} + + +/** + * gnunet-arm command line options + */ +static struct GNUNET_GETOPT_CommandLineOption options[] = { + {'e', "end", NULL, gettext_noop ("stop all GNUnet services"), + GNUNET_NO, &GNUNET_GETOPT_set_one, &end}, + {'i', "init", "SERVICE", gettext_noop ("start a particular service"), + GNUNET_YES, &GNUNET_GETOPT_set_string, &init}, + {'k', "kill", "SERVICE", gettext_noop ("stop a particular service"), + GNUNET_YES, &GNUNET_GETOPT_set_string, &term}, + {'s', "start", NULL, gettext_noop ("start all GNUnet default services"), + GNUNET_NO, &GNUNET_GETOPT_set_one, &start}, + {'t', "test", "SERVICE", + gettext_noop ("test if a particular service is running"), + GNUNET_YES, &GNUNET_GETOPT_set_string, &test}, + GNUNET_GETOPT_OPTION_END +}; + + +/** + * The main function to obtain arm from gnunetd. + * + * @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) +{ + return (GNUNET_OK == + GNUNET_PROGRAM_run (argc, + argv, + "gnunet-arm", + gettext_noop + ("Control services and the Automated Restart Manager (ARM)"), + options, &run, NULL)) ? ret : 1; +} + +/* end of gnunet-arm.c */ diff --git a/src/arm/gnunet-service-arm.c b/src/arm/gnunet-service-arm.c new file mode 100644 index 000000000..97d507890 --- /dev/null +++ b/src/arm/gnunet-service-arm.c @@ -0,0 +1,712 @@ +/* + This file is part of GNUnet. + (C) 2009 Christian Grothoff (and other contributing authors) + + GNUnet is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published + by the Free Software Foundation; either version 2, 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 + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with GNUnet; see the file COPYING. If not, write to the + Free Software Foundation, Inc., 59 Temple Place - Suite 330, + Boston, MA 02111-1307, USA. +*/ + +/** + * @file arm/gnunet-service-arm.c + * @brief the automated restart manager service + * @author Christian Grothoff + * + * TODO: + * - multiple start-stop requests with RC>1 can result + * in UP/DOWN signals based on "pending" that are inaccurate... + * => have list of clients waiting for a resolution instead of + * giving instant (but incorrect) replies + * - code could go into restart-loop for a service + * if service crashes instantly -- need exponential back-off + * - need to test auto-restart code on configuration changes; + * - should refine restart code to check if *relevant* parts of the + * configuration were changed (anything in the section for the service) + * - should have a way to specify dependencies between services and + * manage restarts of groups of services + */ +#include "platform.h" +#include "gnunet_client_lib.h" +#include "gnunet_getopt_lib.h" +#include "gnunet_os_lib.h" +#include "gnunet_protocols.h" +#include "gnunet_service_lib.h" +#include "arm.h" + + +/** + * Run maintenance every second. + */ +#define MAINT_FREQUENCY GNUNET_TIME_UNIT_SECONDS + +/** + * How long do we wait until we decide that a service + * did not start? + */ +#define CHECK_TIMEOUT GNUNET_TIME_UNIT_MINUTES + +struct ServiceList; + +typedef void (*CleanCallback) (void *cls, struct ServiceList * pos); + +/** + * List of our services. + */ +struct ServiceList +{ + /** + * This is a linked list. + */ + struct ServiceList *next; + + /** + * Name of the service. + */ + char *name; + + /** + * Name of the binary used. + */ + char *binary; + + /** + * Name of the configuration file used. + */ + char *config; + + /** + * Function to call upon kill completion (waitpid), NULL + * if we should simply restart the process. + */ + CleanCallback kill_continuation; + + /** + * Closure for kill_continuation. + */ + void *kill_continuation_cls; + + /** + * Process ID of the child. + */ + pid_t pid; + + /** + * Last time the config of this service was + * modified. + */ + time_t mtime; + + /** + * Reference counter (counts how many times we've been + * asked to start the service). We only actually stop + * it once rc hits zero. + */ + unsigned int rc; + +}; + +/** + * List of running services. + */ +static struct ServiceList *running; + +/** + * Our configuration + */ +static struct GNUNET_CONFIGURATION_Handle *cfg; + +/** + * Our scheduler. + */ +static struct GNUNET_SCHEDULER_Handle *sched; + +/** + * Command to prepend to each actual command. + */ +static char *prefix_command; + + +static size_t +write_result (void *cls, size_t size, void *buf) +{ + uint16_t *res = cls; + struct GNUNET_MessageHeader *msg; + + if (buf == NULL) + return 0; /* error, not much we can do */ + GNUNET_assert (size >= sizeof (struct GNUNET_MessageHeader)); + msg = buf; + msg->size = htons (sizeof (struct GNUNET_MessageHeader)); + msg->type = htons (*res); + GNUNET_free (res); + return sizeof (struct GNUNET_MessageHeader); +} + + + +/** + * Signal our client that we will start or stop the + * service. + * + * @return NULL if it was not found + */ +static void +signal_result (struct GNUNET_SERVER_Client *client, + const char *name, uint16_t result) +{ + uint16_t *res; + +#if DEBUG_ARM + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Telling client that service `%s' is now %s\n", + name, + result == GNUNET_MESSAGE_TYPE_ARM_IS_DOWN ? "down" : "up"); +#endif + res = GNUNET_malloc (sizeof (uint16_t)); + *res = result; + GNUNET_SERVER_notify_transmit_ready (client, + sizeof (struct GNUNET_MessageHeader), + GNUNET_TIME_UNIT_FOREVER_REL, + &write_result, res); +} + + +/** + * Find the process with the given PID in the + * given list. + * + * @return NULL if it was not found + */ +static struct ServiceList * +find_pid (pid_t pid) +{ + struct ServiceList *pos; + + pos = running; + while (pos != NULL) + { + if (pos->pid == pid) + return pos; + pos = pos->next; + } + return NULL; +} + + +/** + * Find the process with the given service + * name in the given list, remove it and return it. + * + * @return NULL if it was not found + */ +static struct ServiceList * +find_name (const char *name) +{ + struct ServiceList *pos; + struct ServiceList *prev; + + pos = running; + prev = NULL; + while (pos != NULL) + { + if (0 == strcmp (pos->name, name)) + { + if (prev == NULL) + running = pos->next; + else + prev->next = pos->next; + pos->next = NULL; + return pos; + } + prev = pos; + pos = pos->next; + } + return NULL; +} + + +static void +free_entry (struct ServiceList *pos) +{ + GNUNET_free_non_null (pos->config); + GNUNET_free_non_null (pos->binary); + GNUNET_free (pos->name); + GNUNET_free (pos); +} + + + + +/** + * Actually start the process for the given service. + * + * @param sl identifies service to start + */ +static void +start_process (struct ServiceList *sl) +{ + char *loprefix; + char **argv; + unsigned int argv_size; + char *lopos; + const char *firstarg; + int use_debug; + + /* start service */ + if (GNUNET_OK != + GNUNET_CONFIGURATION_get_value_string (cfg, + sl->name, "PREFIX", &loprefix)) + loprefix = GNUNET_strdup (prefix_command); + use_debug = GNUNET_CONFIGURATION_get_value_yesno (cfg, sl->name, "DEBUG"); + + GNUNET_log (GNUNET_ERROR_TYPE_INFO, _("Starting service `%s'\n"), sl->name); +#if DEBUG_ARM + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Starting service `%s' using binary `%s' and configuration `%s'\n", + sl->name, sl->binary, sl->config); +#endif + argv_size = 5; + if (use_debug) + argv_size += 2; + lopos = loprefix; + while ('\0' != *lopos) + { + if (*lopos == ' ') + argv_size++; + lopos++; + } + firstarg = NULL; + argv = GNUNET_malloc (argv_size * sizeof (char *)); + argv_size = 0; + lopos = loprefix; + while ('\0' != *lopos) + { + while (*lopos == ' ') + lopos++; + if (*lopos == '\0') + continue; + if (argv_size == 0) + firstarg = lopos; + argv[argv_size++] = lopos; + while (('\0' != *lopos) && (' ' != *lopos)) + lopos++; + if ('\0' == *lopos) + continue; + *lopos = '\0'; + lopos++; + } + if (argv_size == 0) + firstarg = sl->binary; + argv[argv_size++] = sl->binary; + argv[argv_size++] = "-c"; + argv[argv_size++] = sl->config; + if (GNUNET_YES == use_debug) + { + argv[argv_size++] = "-L"; + argv[argv_size++] = "DEBUG"; + } + argv[argv_size++] = NULL; + sl->pid = GNUNET_OS_start_process_v (firstarg, argv); + GNUNET_free (argv); + GNUNET_free (loprefix); +} + + +/** + * Start the specified service. + */ +static void +start_service (struct GNUNET_SERVER_Handle *server, + struct GNUNET_SERVER_Client *client, const char *servicename) +{ + struct ServiceList *sl; + char *binary; + char *config; + struct stat sbuf; + sl = find_name (servicename); + if (sl != NULL) + { + /* already running, just increment RC */ + GNUNET_log (GNUNET_ERROR_TYPE_INFO, + _("Service `%s' already running.\n"), servicename); + sl->rc++; + sl->next = running; + running = sl; + signal_result (client, servicename, GNUNET_MESSAGE_TYPE_ARM_IS_UP); + return; + } + if (GNUNET_OK != + GNUNET_CONFIGURATION_get_value_string (cfg, + servicename, "BINARY", &binary)) + { + GNUNET_log (GNUNET_ERROR_TYPE_WARNING, + _("Binary implementing service `%s' not known!\n"), + servicename); + signal_result (client, servicename, GNUNET_MESSAGE_TYPE_ARM_IS_DOWN); + return; + } + if ((GNUNET_OK != + GNUNET_CONFIGURATION_get_value_filename (cfg, + servicename, + "CONFIG", + &config)) || + (0 != STAT (config, &sbuf))) + { + GNUNET_log (GNUNET_ERROR_TYPE_WARNING, + _("Configuration file `%s' for service `%s' not known!\n"), + config, servicename); + signal_result (client, servicename, GNUNET_MESSAGE_TYPE_ARM_IS_DOWN); + GNUNET_free (binary); + return; + } + GNUNET_log (GNUNET_ERROR_TYPE_INFO, + _("Preparing to start `%s'\n"), servicename); + sl = GNUNET_malloc (sizeof (struct ServiceList)); + sl->name = GNUNET_strdup (servicename); + sl->next = running; + sl->rc = 1; + sl->binary = binary; + sl->config = config; + sl->mtime = sbuf.st_mtime; + running = sl; + start_process (sl); + signal_result (client, servicename, GNUNET_MESSAGE_TYPE_ARM_IS_UP); +} + + +static void +free_and_signal (void *cls, struct ServiceList *pos) +{ + struct GNUNET_SERVER_Client *client = cls; + /* find_name will remove "pos" from the list! */ + GNUNET_assert (pos == find_name (pos->name)); + GNUNET_log (GNUNET_ERROR_TYPE_INFO, "Service `%s' stopped\n", pos->name); + signal_result (client, pos->name, GNUNET_MESSAGE_TYPE_ARM_IS_DOWN); + GNUNET_SERVER_receive_done (client, GNUNET_OK); + GNUNET_SERVER_client_drop (client); + free_entry (pos); +} + + +/** + * Stop the specified service. + */ +static void +stop_service (struct GNUNET_SERVER_Handle *server, + struct GNUNET_SERVER_Client *client, const char *servicename) +{ + struct ServiceList *pos; + struct GNUNET_CLIENT_Connection *sc; + + GNUNET_log (GNUNET_ERROR_TYPE_INFO, + "Preparing to stop `%s'\n", servicename); + pos = find_name (servicename); + if (pos->kill_continuation != NULL) + { + /* killing already in progress */ + signal_result (client, servicename, GNUNET_MESSAGE_TYPE_ARM_IS_DOWN); + return; + } + if ((pos != NULL) && (pos->rc > 1)) + { + /* RC>1, just decrement RC */ + pos->rc--; + pos->next = running; + running = pos; + signal_result (client, servicename, GNUNET_MESSAGE_TYPE_ARM_IS_UP); + GNUNET_SERVER_receive_done (client, GNUNET_OK); + return; + } + if (pos != NULL) + { + if (0 != PLIBC_KILL (pos->pid, SIGTERM)) + GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING, "kill"); + pos->next = running; + running = pos; + pos->kill_continuation = &free_and_signal; + pos->kill_continuation_cls = client; + GNUNET_SERVER_client_keep (client); + } + else + { + sc = GNUNET_CLIENT_connect (sched, servicename, cfg); + GNUNET_CLIENT_service_shutdown (sc); + GNUNET_CLIENT_disconnect (sc); + signal_result (client, servicename, GNUNET_MESSAGE_TYPE_ARM_IS_DOWN); + GNUNET_SERVER_receive_done (client, GNUNET_OK); + } +} + + +/** + * Handle START-message. + * + * @param cls closure (always NULL) + * @param server the server handling the message + * @param client identification of the client + * @param message the actual message + * @return GNUNET_OK to keep the connection open, + * GNUNET_SYSERR to close it (signal serious error) + */ +static void +handle_start (void *cls, + struct GNUNET_SERVER_Handle *server, + struct GNUNET_SERVER_Client *client, + const struct GNUNET_MessageHeader *message) +{ + const char *servicename; + uint16_t size; + + size = ntohs (message->size); + size -= sizeof (struct GNUNET_MessageHeader); + servicename = (const char *) &message[1]; + if ((size == 0) || (servicename[size - 1] != '\0')) + { + GNUNET_break (0); + GNUNET_SERVER_receive_done (client, GNUNET_SYSERR); + return; + } + start_service (server, client, servicename); + GNUNET_SERVER_receive_done (client, GNUNET_OK); +} + + +/** + * Handle STOP-message. + * + * @param cls closure (always NULL) + * @param server the server handling the message + * @param client identification of the client + * @param message the actual message + * @return GNUNET_OK to keep the connection open, + * GNUNET_SYSERR to close it (signal serious error) + */ +static void +handle_stop (void *cls, + struct GNUNET_SERVER_Handle *server, + struct GNUNET_SERVER_Client *client, + const struct GNUNET_MessageHeader *message) +{ + const char *servicename; + uint16_t size; + + size = ntohs (message->size); + size -= sizeof (struct GNUNET_MessageHeader); + servicename = (const char *) &message[1]; + if ((size == 0) || (servicename[size - 1] != '\0')) + { + GNUNET_break (0); + GNUNET_SERVER_receive_done (client, GNUNET_SYSERR); + return; + } + stop_service (server, client, servicename); +} + + + +/** + * Background task doing maintenance. + * + * @param cls closure + * @param tc context + */ +static void +maint (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc) +{ + struct ServiceList *pos; + pid_t pid; + int status; + const char *statstr; + int statcode; + struct stat sbuf; + + if (0 != (tc->reason & GNUNET_SCHEDULER_REASON_SHUTDOWN)) + { + GNUNET_log (GNUNET_ERROR_TYPE_INFO, _("Stopping all services\n")); + while (NULL != (pos = running)) + { + running = pos->next; + if (0 != PLIBC_KILL (pos->pid, SIGTERM)) + GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING, "kill"); + if (pos->pid != waitpid (pos->pid, NULL, 0)) + GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING, "waitpid"); + free_entry (pos); + } + return; + } + GNUNET_SCHEDULER_add_delayed (tc->sched, + GNUNET_YES, + GNUNET_SCHEDULER_PRIORITY_IDLE, + GNUNET_SCHEDULER_NO_PREREQUISITE_TASK, + MAINT_FREQUENCY, &maint, cfg); + + /* check for services that died (WAITPID) */ + while (0 < (pid = waitpid (0, &status, WNOHANG))) + { + if (WIFSTOPPED (status) || WIFCONTINUED (status)) + continue; + pos = find_pid (pid); + if (pos == NULL) + { + /* we killed the service */ + continue; + } + if (WIFEXITED (status)) + { + statstr = _( /* process termination method */ "exit"); + statcode = WEXITSTATUS (status); + } + else if (WTERMSIG (status)) + { + statstr = _( /* process termination method */ "signal"); + statcode = WTERMSIG (status); + } + else + { + statstr = _( /* process termination method */ "unknown"); + statcode = 0; + } + if (NULL != pos->kill_continuation) + { + pos->kill_continuation (pos->kill_continuation_cls, pos); + } + else + { + GNUNET_log (GNUNET_ERROR_TYPE_WARNING, + _ + ("Service `%s' terminated with status %s/%d, will try to restart it!\n"), + pos->name, statstr, statcode); + /* schedule restart */ + pos->pid = 0; + } + } + + /* check for services that need to be restarted due to + configuration changes or because the last restart failed */ + pos = running; + while (pos != NULL) + { + if ((0 == STAT (pos->config, &sbuf)) && (pos->mtime < sbuf.st_mtime)) + { + GNUNET_log (GNUNET_ERROR_TYPE_INFO, + _ + ("Restarting service `%s' due to configuration file change.\n")); + if (0 != PLIBC_KILL (pos->pid, SIGTERM)) + GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING, "kill"); + } + if (pos->pid == 0) + { + GNUNET_log (GNUNET_ERROR_TYPE_INFO, + _("Restarting service `%s'.\n"), pos->name); + /* FIXME: should have some exponentially + increasing timer to avoid tight restart loops */ + start_process (pos); + } + pos = pos->next; + } +} + + +/** + * List of handlers for the messages understood by this + * service. + */ +static struct GNUNET_SERVER_MessageHandler handlers[] = { + {&handle_start, NULL, GNUNET_MESSAGE_TYPE_ARM_START, 0}, + {&handle_stop, NULL, GNUNET_MESSAGE_TYPE_ARM_STOP, 0}, + {NULL, NULL, 0, 0} +}; + + +/** + * Process arm requests. + * + * @param cls closure + * @param s scheduler to use + * @param server the initialized server + * @param c configuration to use + */ +static void +run (void *cls, + struct GNUNET_SCHEDULER_Handle *s, + struct GNUNET_SERVER_Handle *server, + struct GNUNET_CONFIGURATION_Handle *c) +{ + char *defaultservices; + char *pos; + + GNUNET_log (GNUNET_ERROR_TYPE_INFO, "Starting...\n"); + cfg = c; + sched = s; + if (GNUNET_OK != + GNUNET_CONFIGURATION_get_value_string (cfg, + "ARM", + "GLOBAL_PREFIX", + &prefix_command)) + prefix_command = GNUNET_strdup (""); + /* start default services... */ + if (GNUNET_OK == + GNUNET_CONFIGURATION_get_value_string (cfg, + "ARM", + "DEFAULTSERVICES", + &defaultservices)) + { +#if DEBUG_ARM + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Starting default services `%s'\n", defaultservices); +#endif + pos = strtok (defaultservices, " "); + while (pos != NULL) + { + start_service (server, NULL, pos); + pos = strtok (NULL, " "); + } + GNUNET_free (defaultservices); + } + else + { +#if DEBUG_ARM + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "No default services configured.\n"); +#endif + } + + /* process client requests */ + GNUNET_SERVER_add_handlers (server, handlers); + + /* manage services */ + GNUNET_SCHEDULER_add_delayed (sched, + GNUNET_YES, + GNUNET_SCHEDULER_PRIORITY_IDLE, + GNUNET_SCHEDULER_NO_PREREQUISITE_TASK, + MAINT_FREQUENCY, &maint, NULL); +} + + +/** + * The main function for the arm service. + * + * @param argc number of arguments from the command line + * @param argv command line arguments + * @return 0 ok, 1 on error + */ +int +main (int argc, char *const *argv) +{ + return (GNUNET_OK == + GNUNET_SERVICE_run (argc, + argv, "arm", &run, NULL, NULL, NULL)) ? 0 : 1; +} + +/* end of gnunet-service-arm.c */ diff --git a/src/arm/test_arm_api.c b/src/arm/test_arm_api.c new file mode 100644 index 000000000..89f63d3ec --- /dev/null +++ b/src/arm/test_arm_api.c @@ -0,0 +1,140 @@ +/* + This file is part of GNUnet. + (C) 2009 Christian Grothoff (and other contributing authors) + + GNUnet is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published + by the Free Software Foundation; either version 2, 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 + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with GNUnet; see the file COPYING. If not, write to the + Free Software Foundation, Inc., 59 Temple Place - Suite 330, + Boston, MA 02111-1307, USA. +*/ +/** + * @file arm/test_arm_api.c + * @brief testcase for arm_api.c + */ +#include "platform.h" +#include "gnunet_common.h" +#include "gnunet_arm_service.h" +#include "gnunet_client_lib.h" +#include "gnunet_configuration_lib.h" +#include "gnunet_getopt_lib.h" +#include "gnunet_program_lib.h" +#include "gnunet_resolver_service.h" + +#define VERBOSE GNUNET_NO + +#define START_ARM GNUNET_YES + +#define TIMEOUT GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS, 10) + +static struct GNUNET_SCHEDULER_Handle *sched; + +static struct GNUNET_CONFIGURATION_Handle *cfg; + +static int ok = 1; + +static void +dns_notify (void *cls, const struct sockaddr *addr, socklen_t addrlen) +{ + if (addr == NULL) + { + GNUNET_assert (ok == 0); +#if START_ARM + GNUNET_ARM_stop_service ("arm", cfg, sched, TIMEOUT, NULL, NULL); +#endif + return; + } + GNUNET_assert (addr != NULL); + ok = 0; +} + + +static void +resolver_notify (void *cls, int success) +{ + GNUNET_assert (success == GNUNET_YES); + sleep (1); /* FIXME: that we need to do this is a problem... */ + GNUNET_RESOLVER_ip_get (sched, + cfg, + "localhost", AF_INET, TIMEOUT, &dns_notify, NULL); +} + +static void +arm_notify (void *cls, int success) +{ + GNUNET_assert (success == GNUNET_YES); +#if START_ARM + sleep (1); /* FIXME: that we need to do this is a problem... */ +#endif + GNUNET_ARM_start_service ("resolver", + cfg, sched, TIMEOUT, &resolver_notify, NULL); +} + + +static void +task (void *cls, + struct GNUNET_SCHEDULER_Handle *s, + char *const *args, + const char *cfgfile, struct GNUNET_CONFIGURATION_Handle *c) +{ + cfg = c; + sched = s; +#if START_ARM + GNUNET_ARM_start_service ("arm", cfg, sched, TIMEOUT, &arm_notify, NULL); +#else + arm_notify (NULL, GNUNET_YES); +#endif +} + + + +static int +check () +{ + char *const argv[] = { + "test-arm-api", + "-c", "test_arm_api_data.conf", +#if VERBOSE + "-L", "DEBUG", +#endif + NULL + }; + struct GNUNET_GETOPT_CommandLineOption options[] = { + GNUNET_GETOPT_OPTION_END + }; + GNUNET_assert (GNUNET_OK == + GNUNET_PROGRAM_run ((sizeof (argv) / sizeof (char *)) - 1, + argv, + "test-arm-api", + "nohelp", options, &task, NULL)); + return ok; +} + +int +main (int argc, char *argv[]) +{ + int ret; + + + GNUNET_log_setup ("test-arm-api", +#if VERBOSE + "DEBUG", +#else + "WARNING", +#endif + NULL); + ret = check (); + + return ret; +} + +/* end of test_arm_api.c */ diff --git a/src/arm/test_arm_api_data.conf b/src/arm/test_arm_api_data.conf new file mode 100644 index 000000000..b25924917 --- /dev/null +++ b/src/arm/test_arm_api_data.conf @@ -0,0 +1,7 @@ +[PATHS] +SERVICEHOME = /tmp/test-gnunetd-arm/ +DEFAULTCONFIG = test_arm_api_data.conf + +[arm] +PORT = 23354 + diff --git a/src/arm/test_gnunet_arm.sh b/src/arm/test_gnunet_arm.sh new file mode 100755 index 000000000..72f7a176b --- /dev/null +++ b/src/arm/test_gnunet_arm.sh @@ -0,0 +1,117 @@ +#!/bin/bash + +rm -rf /tmp/test-gnunetd-arm/ +exe="./gnunet-arm -c test_arm_api_data.conf" +base=/tmp/gnunet-test-arm +#DEBUG="-L DEBUG" + +# ---------------------------------------------------------------------------------- +echo -n "TEST: Bad argument checking... " + +if $exe -x 2> /dev/null; then + echo "FAIL: error running $exe" + exit 1 +fi +echo "PASS" + +# ---------------------------------------------------------------------------------- +echo -n "TEST: Start ARM... " + +if ! $exe $DEBUG -s > /dev/null ; then + echo "FAIL: error running $exe" + exit 1 +fi +LINES=`ps ax | grep gnunet-service-arm | grep -v grep | wc -l` +if test $LINES -eq 0; then + echo "FAIL: found $LINES gnunet-service-arm processes" + exit 1 +fi +echo "PASS" + +# ---------------------------------------------------------------------------------- +echo -n "TEST: Start another service... " + +if ! $exe $DEBUG -i resolver > /dev/null ; then + echo "FAIL: error running $exe" + kill %% + exit 1 +fi +sleep 1 +LINES=`ps ax | grep gnunet-service-resolver | grep -v grep | wc -l` +if test $LINES -ne 1; then + echo "FAIL: unexpected output (got $LINES lines, wanted 1)" + $exe -e + exit 1 +fi +echo "PASS" + +# ---------------------------------------------------------------------------------- +echo -n "TEST: Test -t on running service... " + +if ! $exe $DEBUG -t resolver > $base.out; then + echo "FAIL: error running $exe" + exit 1 +fi +LINES=`cat $base.out | grep resolver | grep not | wc -l` +if test $LINES -ne 0; then + echo "FAIL: unexpected output" + $exe -e + exit 1 +fi +LINES=`cat $base.out | grep resolver | grep -v not | wc -l` +if test $LINES -ne 1; then + echo "FAIL: unexpected output" + $exe -e + exit 1 +fi +echo "PASS" + +# ---------------------------------------------------------------------------------- +echo -n "TEST: Stop a service... " + +if ! $exe $DEBUG -k resolver > /dev/null; then + echo "FAIL: error running $exe" + $exe -e + exit 1 +fi +sleep 1 +LINES=`ps ax | grep gnunet-service-resolver | grep -v grep | wc -l` +if test $LINES -ne 0; then + echo "FAIL: unexpected output" + $exe -e + exit 1 +fi +echo "PASS" + +# ---------------------------------------------------------------------------------- +echo -n "TEST: Test -t on stopped service... " + +if ! $exe $DEBUG -t resolver > $base.out; then + echo "FAIL: error running $exe" + $exe -e + exit 1 +fi +LINES=`cat $base.out | grep resolver | grep not | wc -l` +if test $LINES -ne 1; then + echo "FAIL: unexpected output" + $exe -e + exit 1 +fi +echo "PASS" + +# ---------------------------------------------------------------------------------- +echo -n "TEST: Stop ARM... " + +if ! $exe $DEBUG -e > /dev/null; then + echo "FAIL: error running $exe" + exit 1 +fi +LINES=`ps ax | grep gnunet-service-arm | grep -v grep | wc -l` +if test $LINES -ne 0; then + echo "FAIL: unexpected output" + exit 1 +fi +echo "PASS" + +rm -rf /tmp/test-gnunetd-arm/ +rm -f $base.out diff --git a/src/core/Makefile.am b/src/core/Makefile.am new file mode 100644 index 000000000..55ef757c2 --- /dev/null +++ b/src/core/Makefile.am @@ -0,0 +1,68 @@ +INCLUDES = -I$(top_srcdir)/src/include + +plugindir = $(libdir)/gnunet + +if MINGW + WINFLAGS = -Wl,--no-undefined -Wl,--export-all-symbols +endif + +if USE_COVERAGE + AM_CFLAGS = -fprofile-arcs -ftest-coverage +endif + + +lib_LTLIBRARIES = \ + libgnunetcore.la + +libgnunetcore_la_SOURCES = \ + core_api.c core.h +libgnunetcore_la_LIBADD = \ + $(top_builddir)/src/arm/libgnunetarm.la \ + $(top_builddir)/src/util/libgnunetutil.la \ + $(GN_LIBINTL) +libgnunetcore_la_LDFLAGS = \ + $(GN_LIB_LDFLAGS) $(WINFLAGS) \ + -version-info 0:0:0 + + +bin_PROGRAMS = \ + gnunet-service-core + +gnunet_service_core_SOURCES = \ + gnunet-service-core.c +gnunet_service_core_LDADD = \ + $(top_builddir)/src/hello/libgnunethello.la \ + $(top_builddir)/src/peerinfo/libgnunetpeerinfo.la \ + $(top_builddir)/src/transport/libgnunettransport.la \ + $(top_builddir)/src/util/libgnunetutil.la \ + $(GN_LIBINTL) + + +check_PROGRAMS = \ + test_core_api_start_only \ + test_core_api + +TESTS = $(check_PROGRAMS) + +test_core_api_SOURCES = \ + test_core_api.c +test_core_api_LDADD = \ + $(top_builddir)/src/arm/libgnunetarm.la \ + $(top_builddir)/src/core/libgnunetcore.la \ + $(top_builddir)/src/transport/libgnunettransport.la \ + $(top_builddir)/src/util/libgnunetutil.la + + +test_core_api_start_only_SOURCES = \ + test_core_api_start_only.c +test_core_api_start_only_LDADD = \ + $(top_builddir)/src/arm/libgnunetarm.la \ + $(top_builddir)/src/core/libgnunetcore.la \ + $(top_builddir)/src/util/libgnunetutil.la + + +EXTRA_DIST = \ + test_core_api_data.conf \ + test_core_api_peer1.conf \ + test_core_api_peer2.conf + diff --git a/src/core/core.h b/src/core/core.h new file mode 100644 index 000000000..840c7e143 --- /dev/null +++ b/src/core/core.h @@ -0,0 +1,308 @@ +/* + This file is part of GNUnet. + (C) 2009 Christian Grothoff (and other contributing authors) + + GNUnet is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published + by the Free Software Foundation; either version 2, 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 + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with GNUnet; see the file COPYING. If not, write to the + Free Software Foundation, Inc., 59 Temple Place - Suite 330, + Boston, MA 02111-1307, USA. +*/ + +/** + * @file core/core.h + * @brief common internal definitions for core service + * @author Christian Grothoff + */ +#include "gnunet_crypto_lib.h" +#include "gnunet_time_lib.h" + +#define DEBUG_CORE GNUNET_NO + +/** + * Definition of bits in the InitMessage's options field that specify + * which events this client cares about. Note that inbound messages + * for handlers that were specifically registered are always + * transmitted to the client. + */ +#define GNUNET_CORE_OPTION_NOTHING 0 +#define GNUNET_CORE_OPTION_SEND_CONNECT 1 +#define GNUNET_CORE_OPTION_SEND_DISCONNECT 2 +#define GNUNET_CORE_OPTION_SEND_BFC 4 +#define GNUNET_CORE_OPTION_SEND_FULL_INBOUND 8 +#define GNUNET_CORE_OPTION_SEND_HDR_INBOUND 16 +#define GNUNET_CORE_OPTION_SEND_FULL_OUTBOUND 32 +#define GNUNET_CORE_OPTION_SEND_HDR_OUTBOUND 64 + + +/** + * Message transmitted core clients to gnunet-service-core + * to start the interaction. This header is followed by + * uint16_t type values specifying which messages this + * client is interested in. + */ +struct InitMessage +{ + + /** + * Header with type GNUNET_MESSAGE_TYPE_CORE_INIT. + */ + struct GNUNET_MessageHeader header; + + /** + * Options, see GNUNET_CORE_OPTION_ values. + */ + uint32_t options GNUNET_PACKED; + +}; + + +/** + * Message transmitted by the gnunet-service-core process + * to its clients in response to an INIT message. + */ +struct InitReplyMessage +{ + + /** + * Header with type GNUNET_MESSAGE_TYPE_CORE_INIT_REPLY + */ + struct GNUNET_MessageHeader header; + + /** + * Always zero. + */ + uint32_t reserved GNUNET_PACKED; + + /** + * Public key of the local peer. + */ + struct GNUNET_CRYPTO_RsaPublicKeyBinaryEncoded publicKey; + +}; + + +/** + * Message sent by the service to clients to notify them + * about a peer connecting or disconnecting. + */ +struct ConnectNotifyMessage +{ + /** + * Header with type GNUNET_MESSAGE_TYPE_CORE_NOTIFY_CONNECT + * or GNUNET_MESSAGE_TYPE_CORE_NOTIFY_DISCONNECT. + */ + struct GNUNET_MessageHeader header; + + /** + * Available bandwidth to this peer; zero for disconnect. + * [TODO: currently set to hard-coded constant and hence + * not really useful, right?] + */ + uint32_t bpm_available GNUNET_PACKED; + + /** + * Identity of the connecting peer. + */ + struct GNUNET_PeerIdentity peer; + + /** + * Time of our last interaction with the peer; close + * to "now" for connect messages. + * [TODO: is this useful?] + */ + struct GNUNET_TIME_AbsoluteNBO last_activity; + +}; + + + +/** + * Message sent by the service to clients to notify them about + * messages being received or transmitted. This overall message is + * followed by the real message, or just the header of the real + * message (depending on the client's preferences). The receiver can + * tell if he got the full message or only a partial message by + * looking at the size field in the header of NotifyTrafficMessage and + * checking it with the size field in the message that follows. + */ +struct NotifyTrafficMessage +{ + /** + * Header with type GNUNET_MESSAGE_TYPE_CORE_NOTIFY_INBOUND + * or GNUNET_MESSAGE_TYPE_CORE_NOTIFY_OUTBOUND. + */ + struct GNUNET_MessageHeader header; + + /** + * Always zero. + */ + uint32_t reserved GNUNET_PACKED; + + /** + * Identity of the receiver or sender. + */ + struct GNUNET_PeerIdentity peer; + +}; + + +/** + * Message sent to the core asking for configuration + * information and possibly preference changes. + */ +struct RequestConfigureMessage +{ + /** + * Header with type GNUNET_MESSAGE_TYPE_CORE_REQUEST_CONFIGURE + */ + struct GNUNET_MessageHeader header; + + /** + * Always zero. + */ + uint32_t reserved GNUNET_PACKED; + + /** + * Limit the number of bytes of outbound traffic to this + * peer to at most the specified amount (naturally, the + * amount is also limited by the receiving peer). + */ + uint32_t limit_outbound_bpm GNUNET_PACKED; + + /** + * Number of bytes of inbound traffic to reserve, can + * be negative (to unreserve). NBO. + */ + int32_t reserve_inbound GNUNET_PACKED; + + /** + * Increment the current traffic preference for the given peer by + * the specified amont. The traffic preference is used to determine + * the share of bandwidth this peer will typcially be assigned. + */ + double preference_change GNUNET_PACKED; + + /** + * Identity of the peer being configured. + */ + struct GNUNET_PeerIdentity peer; + +}; + + +/** + * Response from the core to a "RequestConfigureMessage" + * providing traffic status information for a peer. + */ +struct ConfigurationInfoMessage +{ + /** + * Header with type GNUNET_MESSAGE_TYPE_CORE_CONFIGURATION_INFO + */ + struct GNUNET_MessageHeader header; + + /** + * Amount of traffic (inbound number of bytes) that was reserved in + * response to the configuration change request. Negative for + * "unreserved" bytes. + */ + int32_t reserved_amount GNUNET_PACKED; + + /** + * Available bandwidth in (in bytes per minute) for this peer. + * 0 if we have been disconnected. + */ + uint32_t bpm_in GNUNET_PACKED; + + /** + * Available bandwidth out (in bytes per minute) for this peer, + * 0 if we have been disconnected. + */ + uint32_t bpm_out GNUNET_PACKED; + + /** + * Latest transport latency estimate for the peer. + * FOREVER if we have been disconnected. + */ + struct GNUNET_TIME_RelativeNBO latency; + + /** + * Current traffic preference for the peer. + * 0 if we have been disconnected. + */ + double preference; + + /** + * Identity of the receiver or sender. + */ + struct GNUNET_PeerIdentity peer; + +}; + + +/** + * Core asking a client to generate traffic for a particular + * target. + */ +struct SolicitTrafficMessage +{ + /** + * Header with type GNUNET_MESSAGE_TYPE_CORE_SOLICIT_TRAFFIC + * or GNUNET_MESSAGE_TYPE_CORE_RECV_OK + */ + struct GNUNET_MessageHeader header; + + /** + * Number of bytes of traffic being solicited. + */ + uint32_t solicit_size GNUNET_PACKED; + + /** + * Identity of the receiver or sender. + */ + struct GNUNET_PeerIdentity peer; + +}; + + +/** + * Client asking core to transmit a particular message to + * a particular target. Does NOT have to be solicited. + */ +struct SendMessage +{ + /** + * Header with type GNUNET_MESSAGE_TYPE_CORE_SEND + */ + struct GNUNET_MessageHeader header; + + /** + * How important is this message? + */ + uint32_t priority GNUNET_PACKED; + + /** + * By what time would the sender really like to see this + * message transmitted? + */ + struct GNUNET_TIME_AbsoluteNBO deadline; + + /** + * Identity of the receiver or sender. + */ + struct GNUNET_PeerIdentity peer; + +}; + + +/* end of core.h */ diff --git a/src/core/core_api.c b/src/core/core_api.c new file mode 100644 index 000000000..10fa0ccdd --- /dev/null +++ b/src/core/core_api.c @@ -0,0 +1,1071 @@ +/* + This file is part of GNUnet. + (C) 2009 Christian Grothoff (and other contributing authors) + + GNUnet is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published + by the Free Software Foundation; either version 2, 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 + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with GNUnet; see the file COPYING. If not, write to the + Free Software Foundation, Inc., 59 Temple Place - Suite 330, + Boston, MA 02111-1307, USA. +*/ + +/** + * @file core/core_api.c + * @brief core service; this is the main API for encrypted P2P + * communications + * @author Christian Grothoff + */ +#include "platform.h" +#include "gnunet_core_service.h" +#include "core.h" + + +/** + * Context for the core service connection. + */ +struct GNUNET_CORE_Handle +{ + + /** + * Our scheduler. + */ + struct GNUNET_SCHEDULER_Handle *sched; + + /** + * Configuration we're using. + */ + struct GNUNET_CONFIGURATION_Handle *cfg; + + /** + * Closure for the various callbacks. + */ + void *cls; + + /** + * Function to call once we've handshaked with the core service. + */ + GNUNET_CORE_StartupCallback init; + + /** + * Function to call whenever we're notified about a peer connecting. + */ + GNUNET_CORE_ClientEventHandler connects; + + /** + * Function to call whenever we're notified about a peer disconnecting. + */ + GNUNET_CORE_ClientEventHandler disconnects; + + /** + * Function to call whenever we're asked to generate traffic + * (data provided to be transmitted back to the service). + */ + GNUNET_CORE_BufferFillCallback bfc; + + /** + * Function to call whenever we receive an inbound message. + */ + GNUNET_CORE_MessageCallback inbound_notify; + + /** + * Function to call whenever we receive an outbound message. + */ + GNUNET_CORE_MessageCallback outbound_notify; + + /** + * Function handlers for messages of particular type. + */ + const struct GNUNET_CORE_MessageHandler *handlers; + + /** + * Our connection to the service. + */ + struct GNUNET_CLIENT_Connection *client; + + /** + * Handle for our current transmission request. + */ + struct GNUNET_NETWORK_TransmitHandle *th; + + /** + * Head of doubly-linked list of pending requests. + */ + struct GNUNET_CORE_TransmitHandle *pending_head; + + /** + * Tail of doubly-linked list of pending requests. + */ + struct GNUNET_CORE_TransmitHandle *pending_tail; + + /** + * Currently submitted request (or NULL) + */ + struct GNUNET_CORE_TransmitHandle *submitted; + + /** + * How long to wait until we time out the connection attempt? + */ + struct GNUNET_TIME_Absolute startup_timeout; + + /** + * ID of reconnect task (if any). + */ + GNUNET_SCHEDULER_TaskIdentifier reconnect_task; + + /** + * Number of entries in the handlers array. + */ + unsigned int hcnt; + + /** + * For inbound notifications without a specific handler, do + * we expect to only receive headers? + */ + int inbound_hdr_only; + + /** + * For outbound notifications without a specific handler, do + * we expect to only receive headers? + */ + int outbound_hdr_only; + + /** + * Are we currently disconnected and hence unable to forward + * requests? + */ + int currently_down; +}; + + +/** + * Handle for a transmission request. + */ +struct GNUNET_CORE_TransmitHandle +{ + + /** + * We keep active transmit handles in a doubly-linked list. + */ + struct GNUNET_CORE_TransmitHandle *next; + + /** + * We keep active transmit handles in a doubly-linked list. + */ + struct GNUNET_CORE_TransmitHandle *prev; + + /** + * Corresponding core handle. + */ + struct GNUNET_CORE_Handle *ch; + + /** + * Function that will be called to get the actual request + * (once we are ready to transmit this request to the core). + * The function will be called with a NULL buffer to signal + * timeout. + */ + GNUNET_NETWORK_TransmitReadyNotify get_message; + + /** + * Closure for get_message. + */ + void *get_message_cls; + + /** + * If this entry is for a configuration request, pointer + * to the information callback; otherwise NULL. + */ + GNUNET_CORE_PeerConfigurationInfoCallback info; + + /** + * Closure for info. + */ + void *info_cls; + + /** + * If this entry is for a transmission request, pointer + * to the notify callback; otherwise NULL. + */ + GNUNET_NETWORK_TransmitReadyNotify notify; + + /** + * Closure for notify. + */ + void *notify_cls; + + /** + * Peer the request is about. + */ + struct GNUNET_PeerIdentity peer; + + /** + * Timeout for this handle. + */ + struct GNUNET_TIME_Absolute timeout; + + /** + * ID of timeout task. + */ + GNUNET_SCHEDULER_TaskIdentifier timeout_task; + + /** + * How important is this message? + */ + uint32_t priority; + + /** + * Size of this request. + */ + uint16_t msize; + + +}; + + +/** + * Function called when we are ready to transmit our + * "START" message (or when this operation timed out). + * + * @param cls closure + * @param size number of bytes available in buf + * @param buf where the callee should write the message + * @return number of bytes written to buf + */ +static size_t transmit_start (void *cls, size_t size, void *buf); + + +/** + * Our current client connection went down. Clean it up + * and try to reconnect! + */ +static void +reconnect (struct GNUNET_CORE_Handle *h) +{ + GNUNET_CLIENT_disconnect (h->client); + h->currently_down = GNUNET_YES; + h->client = GNUNET_CLIENT_connect (h->sched, "core", h->cfg); + h->th = GNUNET_CLIENT_notify_transmit_ready (h->client, + sizeof (struct InitMessage) + + sizeof (uint16_t) * h->hcnt, + GNUNET_TIME_UNIT_SECONDS, + &transmit_start, h); +} + + +/** + * The given request hit its timeout. Remove from the + * doubly-linked list and call the respective continuation. + * + * @param cls the transmit handle of the request that timed out + * @param tc context, can be NULL (!) + */ +static void +timeout_request (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc) +{ + struct GNUNET_CORE_TransmitHandle *th = cls; + struct GNUNET_CORE_Handle *h; + + h = th->ch; + th->timeout_task = GNUNET_SCHEDULER_NO_PREREQUISITE_TASK; + GNUNET_assert (0 == th->get_message (th->get_message_cls, 0, NULL)); + GNUNET_CORE_notify_transmit_ready_cancel (th); +} + + +/** + * Function called when we are ready to transmit a request from our + * request list (or when this operation timed out). + * + * @param cls closure + * @param size number of bytes available in buf + * @param buf where the callee should write the message + * @return number of bytes written to buf + */ +static size_t +request_start (void *cls, size_t size, void *buf) +{ + struct GNUNET_CORE_Handle *h = cls; + struct GNUNET_CORE_TransmitHandle *th; + size_t ret; + + h->th = NULL; + th = h->pending_head; + if (buf == NULL) + { + timeout_request (th, NULL); + return 0; + } + /* create new timeout task (in case core takes too long to respond!) */ + th->timeout_task = GNUNET_SCHEDULER_add_delayed (h->sched, + GNUNET_NO, + GNUNET_SCHEDULER_PRIORITY_KEEP, + GNUNET_SCHEDULER_NO_PREREQUISITE_TASK, + GNUNET_TIME_absolute_get_remaining + (th->timeout), + &timeout_request, th); + /* remove th from doubly-linked pending list, move to submitted */ + GNUNET_assert (th->prev == NULL); + h->pending_head = th->next; + if (th->next == NULL) + h->pending_tail = NULL; + else + th->next->prev = NULL; + GNUNET_assert (h->submitted == NULL); + h->submitted = th; + GNUNET_assert (size >= th->msize); + ret = th->get_message (th->get_message_cls, size, buf); + GNUNET_assert (ret <= size); + return ret; +} + + +/** + * Check the list of pending requests, send the next + * one to the core. + */ +static void +trigger_next_request (struct GNUNET_CORE_Handle *h) +{ + struct GNUNET_CORE_TransmitHandle *th; + if (h->currently_down) + return; /* connection temporarily down */ + if (NULL == (th = h->pending_head)) + return; /* no requests pending */ + GNUNET_assert (NULL == h->th); + GNUNET_SCHEDULER_cancel (h->sched, th->timeout_task); + th->timeout_task = GNUNET_SCHEDULER_NO_PREREQUISITE_TASK; + h->th = GNUNET_CLIENT_notify_transmit_ready (h->client, + th->msize, + GNUNET_TIME_absolute_get_remaining + (th->timeout), &request_start, + h); +} + + +/** + * cls is a pointer to a 32 bit number followed by that + * amount of data. If possible, copy to buf and return + * number of bytes copied. Always free the buffer. + */ +static size_t +copy_and_free (void *cls, size_t size, void *buf) +{ + char *cbuf = cls; + uint32_t have; + + memcpy (&have, cbuf, sizeof (uint32_t)); + if (have > size) + { + /* timeout / error case */ + GNUNET_free (cbuf); + return 0; + } + memcpy (buf, cbuf + sizeof (uint32_t), have); + GNUNET_free (cbuf); + return have; +} + + +/** + * Call bfc callback to solicit traffic for the given peer. + */ +static void +solicit_traffic (struct GNUNET_CORE_Handle *h, + const struct GNUNET_PeerIdentity *peer, uint32_t amount) +{ + char buf[amount]; + size_t have; + char *cbuf; + + have = h->bfc (h->cls, peer, buf, amount); + if (have == 0) + return; + GNUNET_assert (have >= sizeof (struct GNUNET_MessageHeader)); + cbuf = GNUNET_malloc (have + sizeof (uint32_t)); + memcpy (cbuf, &have, sizeof (uint32_t)); + memcpy (cbuf + sizeof (uint32_t), buf, have); + GNUNET_CORE_notify_transmit_ready (h, + 0, + GNUNET_TIME_UNIT_SECONDS, + peer, have, ©_and_free, cbuf); +} + + +/** + * Handler for most messages received from the core. + */ +static void +main_handler (void *cls, const struct GNUNET_MessageHeader *msg) +{ + struct GNUNET_CORE_Handle *h = cls; + unsigned int hpos; + const struct ConnectNotifyMessage *cnm; + const struct NotifyTrafficMessage *ntm; + const struct ConfigurationInfoMessage *cim; + const struct SolicitTrafficMessage *stm; + const struct GNUNET_MessageHeader *em; + uint16_t msize; + uint16_t et; + uint32_t ss; + const struct GNUNET_CORE_MessageHandler *mh; + + if (msg == NULL) + { + GNUNET_log (GNUNET_ERROR_TYPE_WARNING, + _ + ("Client was disconnected from core service, trying to reconnect.\n")); + reconnect (h); + return; + } + msize = ntohs (msg->size); + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Processing message of type %u and size %u from core service\n", + ntohs (msg->type), msize); + switch (ntohs (msg->type)) + { + case GNUNET_MESSAGE_TYPE_CORE_NOTIFY_CONNECT: + if (NULL == h->connects) + { + GNUNET_break (0); + break; + } + if (msize != sizeof (struct ConnectNotifyMessage)) + { + GNUNET_break (0); + break; + } + cnm = (const struct ConnectNotifyMessage *) msg; + break; + case GNUNET_MESSAGE_TYPE_CORE_NOTIFY_DISCONNECT: + if (NULL == h->disconnects) + { + GNUNET_break (0); + break; + } + if (msize != sizeof (struct ConnectNotifyMessage)) + { + GNUNET_break (0); + break; + } + cnm = (const struct ConnectNotifyMessage *) msg; + break; + case GNUNET_MESSAGE_TYPE_CORE_NOTIFY_INBOUND: + if (msize < + sizeof (struct NotifyTrafficMessage) + + sizeof (struct GNUNET_MessageHeader)) + { + GNUNET_break (0); + break; + } + ntm = (const struct NotifyTrafficMessage *) msg; + em = (const struct GNUNET_MessageHeader *) &ntm[1]; + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Received message of type %u from peer `%4s'\n", + ntohs (em->type), GNUNET_i2s (&ntm->peer)); + if ((GNUNET_NO == h->inbound_hdr_only) && + (msize != ntohs (em->size) + sizeof (struct NotifyTrafficMessage))) + { + GNUNET_break (0); + break; + } + et = ntohs (em->type); + for (hpos = 0; hpos < h->hcnt; hpos++) + { + mh = &h->handlers[hpos]; + if (mh->type != et) + continue; + if ((mh->expected_size != ntohs (em->size)) && + (mh->expected_size != 0)) + { + GNUNET_break (0); + continue; + } + if (GNUNET_OK != + h->handlers[hpos].callback (h->cls, &ntm->peer, em)) + { + /* error in processing, disconnect ! */ + reconnect (h); + return; + } + } + if (NULL != h->inbound_notify) + h->inbound_notify (h->cls, &ntm->peer, em); + break; + case GNUNET_MESSAGE_TYPE_CORE_NOTIFY_OUTBOUND: + if (msize < + sizeof (struct NotifyTrafficMessage) + + sizeof (struct GNUNET_MessageHeader)) + { + GNUNET_break (0); + break; + } + ntm = (const struct NotifyTrafficMessage *) msg; + em = (const struct GNUNET_MessageHeader *) &ntm[1]; + if ((GNUNET_NO == h->outbound_hdr_only) && + (msize != ntohs (em->size) + sizeof (struct NotifyTrafficMessage))) + { + GNUNET_break (0); + break; + } + if (NULL == h->outbound_notify) + { + GNUNET_break (0); + break; + } + h->outbound_notify (h->cls, &ntm->peer, em); + break; + case GNUNET_MESSAGE_TYPE_CORE_CONFIGURATION_INFO: + if (msize != sizeof (struct ConfigurationInfoMessage)) + { + GNUNET_break (0); + break; + } + if (NULL == h->submitted) + break; + cim = (const struct ConfigurationInfoMessage *) msg; + + /* process configuration data */ + if (h->submitted->info != NULL) + h->submitted->info (h->submitted->info_cls, + &h->submitted->peer, + ntohl (cim->bpm_in), + ntohl (cim->bpm_out), + GNUNET_TIME_relative_ntoh (cim->latency), + (int) ntohl (cim->reserved_amount), + cim->preference); + /* done, clean up! */ + GNUNET_CORE_notify_transmit_ready_cancel (h->submitted); + trigger_next_request (h); + break; + case GNUNET_MESSAGE_TYPE_CORE_SOLICIT_TRAFFIC: + if (msize != sizeof (struct SolicitTrafficMessage)) + { + GNUNET_break (0); + break; + } + stm = (const struct SolicitTrafficMessage *) msg; + if (NULL == h->bfc) + { + GNUNET_break (0); + break; + } + ss = ntohl (stm->solicit_size); + if ((ss > GNUNET_SERVER_MAX_MESSAGE_SIZE) || + (ss + sizeof (struct SendMessage) > GNUNET_SERVER_MAX_MESSAGE_SIZE)) + { + GNUNET_break (0); + break; + } + solicit_traffic (h, &stm->peer, ss); + break; + default: + GNUNET_break (0); + break; + } + GNUNET_CLIENT_receive (h->client, + &main_handler, h, GNUNET_TIME_UNIT_FOREVER_REL); +} + + + +/** + * Function called when we are ready to transmit our + * "START" message (or when this operation timed out). + * + * @param cls closure + * @param size number of bytes available in buf + * @param buf where the callee should write the message + * @return number of bytes written to buf + */ +static size_t transmit_start (void *cls, size_t size, void *buf); + + +/** + * Function called on the first message received from + * the service (contains our public key, etc.). + * Should trigger calling the init callback + * and then start our regular message processing. + * + * @param cls closure + * @param msg message received, NULL on timeout or fatal error + */ +static void +init_reply_handler (void *cls, const struct GNUNET_MessageHeader *msg) +{ + struct GNUNET_CORE_Handle *h = cls; + const struct InitReplyMessage *m; + GNUNET_CORE_StartupCallback init; + struct GNUNET_PeerIdentity my_identity; + + if ((msg == NULL) || + (ntohs (msg->size) != sizeof (struct InitReplyMessage)) || + (ntohs (msg->type) != GNUNET_MESSAGE_TYPE_CORE_INIT_REPLY)) + { + GNUNET_log (GNUNET_ERROR_TYPE_INFO, + _ + ("Error connecting to core service (failed to receive `%s' message).\n"), + "INIT_REPLY"); + GNUNET_break (msg == NULL); + transmit_start (h, 0, NULL); + return; + } + m = (const struct InitReplyMessage *) msg; + /* start our message processing loop */ + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + _ + ("Successfully connected to core service, starting processing loop.\n")); + h->currently_down = GNUNET_NO; + trigger_next_request (h); + GNUNET_CLIENT_receive (h->client, + &main_handler, h, GNUNET_TIME_UNIT_FOREVER_REL); + if (NULL != (init = h->init)) + { + /* mark so we don't call init on reconnect */ + h->init = NULL; + GNUNET_log (GNUNET_ERROR_TYPE_INFO, + _("Successfully connected to core service.\n")); + GNUNET_CRYPTO_hash (&m->publicKey, + sizeof (struct + GNUNET_CRYPTO_RsaPublicKeyBinaryEncoded), + &my_identity.hashPubKey); + init (h->cls, h, &my_identity, &m->publicKey); + } +} + + +static void +reconnect_task (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc) +{ + struct GNUNET_CORE_Handle *h = cls; + h->reconnect_task = GNUNET_SCHEDULER_NO_PREREQUISITE_TASK; + reconnect (h); +} + + +/** + * Function called when we are ready to transmit our + * "START" message (or when this operation timed out). + * + * @param cls closure + * @param size number of bytes available in buf + * @param buf where the callee should write the message + * @return number of bytes written to buf + */ +static size_t +transmit_start (void *cls, size_t size, void *buf) +{ + struct GNUNET_CORE_Handle *h = cls; + struct InitMessage *init; + uint16_t *ts; + uint16_t msize; + uint32_t opt; + unsigned int hpos; + struct GNUNET_TIME_Relative delay; + + h->th = NULL; + if (size == 0) + { + if ((h->init == NULL) || + (GNUNET_TIME_absolute_get ().value < h->startup_timeout.value)) + { + GNUNET_log (GNUNET_ERROR_TYPE_INFO, + _("Failed to connect to core service, retrying.\n")); + delay = GNUNET_TIME_absolute_get_remaining (h->startup_timeout); + if ((h->init == NULL) || (delay.value > 1000)) + delay = GNUNET_TIME_UNIT_SECONDS; + if (h->init == NULL) + h->startup_timeout = + GNUNET_TIME_relative_to_absolute (GNUNET_TIME_UNIT_MINUTES); + h->reconnect_task = + GNUNET_SCHEDULER_add_delayed (h->sched, GNUNET_NO, + GNUNET_SCHEDULER_PRIORITY_IDLE, + GNUNET_SCHEDULER_NO_PREREQUISITE_TASK, + delay, &reconnect_task, h); + return 0; + } + /* timeout on initial connect */ + GNUNET_log (GNUNET_ERROR_TYPE_WARNING, + _("Failed to connect to core service, giving up.\n")); + h->init (h->cls, NULL, NULL, NULL); + GNUNET_CORE_disconnect (h); + return 0; + } + msize = h->hcnt * sizeof (uint16_t) + sizeof (struct InitMessage); + GNUNET_assert (size >= msize); + init = buf; + init->header.type = htons (GNUNET_MESSAGE_TYPE_CORE_INIT); + init->header.size = htons (msize); + opt = GNUNET_CORE_OPTION_NOTHING; + if (h->connects != NULL) + opt |= GNUNET_CORE_OPTION_SEND_CONNECT; + if (h->disconnects != NULL) + opt |= GNUNET_CORE_OPTION_SEND_DISCONNECT; + if (h->bfc != NULL) + opt |= GNUNET_CORE_OPTION_SEND_BFC; + if (h->inbound_notify != NULL) + { + if (h->inbound_hdr_only) + opt |= GNUNET_CORE_OPTION_SEND_HDR_INBOUND; + else + opt |= GNUNET_CORE_OPTION_SEND_FULL_INBOUND; + } + if (h->outbound_notify != NULL) + { + if (h->outbound_hdr_only) + opt |= GNUNET_CORE_OPTION_SEND_HDR_OUTBOUND; + else + opt |= GNUNET_CORE_OPTION_SEND_FULL_OUTBOUND; + } + init->options = htonl (opt); + ts = (uint16_t *) & init[1]; + for (hpos = 0; hpos < h->hcnt; hpos++) + ts[hpos] = htons (h->handlers[hpos].type); + GNUNET_CLIENT_receive (h->client, + &init_reply_handler, + h, + GNUNET_TIME_absolute_get_remaining (h-> + startup_timeout)); + return sizeof (struct InitMessage) + h->hcnt * sizeof (uint16_t); +} + + +/** + * Connect to the core service. Note that the connection may + * complete (or fail) asynchronously. + * + * @param sched scheduler to use + * @param cfg configuration to use + * @param timeout after how long should we give up trying to connect to the core service? + * @param cls closure for the various callbacks that follow (including handlers in the handlers array) + * @param init callback to call on timeout or once we have successfully + * connected to the core service + * @param connects function to call on peer connect, can be NULL + * @param disconnects function to call on peer disconnect / timeout, can be NULL + * @param bfc function to call to fill up spare bandwidth, can be NULL + * @param inbound_notify function to call for all inbound messages, can be NULL + * @param inbound_hdr_only set to GNUNET_YES if inbound_notify will only read the + * GNUNET_MessageHeader and hence we do not need to give it the full message; + * can be used to improve efficiency, ignored if inbound_notify is NULLL + * @param outbound_notify function to call for all outbound messages, can be NULL + * @param outbound_hdr_only set to GNUNET_YES if outbound_notify will only read the + * GNUNET_MessageHeader and hence we do not need to give it the full message + * can be used to improve efficiency, ignored if outbound_notify is NULLL + * @param handlers callbacks for messages we care about, NULL-terminated + */ +void +GNUNET_CORE_connect (struct GNUNET_SCHEDULER_Handle *sched, + struct GNUNET_CONFIGURATION_Handle *cfg, + struct GNUNET_TIME_Relative timeout, + void *cls, + GNUNET_CORE_StartupCallback init, + GNUNET_CORE_ClientEventHandler connects, + GNUNET_CORE_ClientEventHandler disconnects, + GNUNET_CORE_BufferFillCallback bfc, + GNUNET_CORE_MessageCallback inbound_notify, + int inbound_hdr_only, + GNUNET_CORE_MessageCallback outbound_notify, + int outbound_hdr_only, + const struct GNUNET_CORE_MessageHandler *handlers) +{ + struct GNUNET_CORE_Handle *h; + + GNUNET_assert (init != NULL); + h = GNUNET_malloc (sizeof (struct GNUNET_CORE_Handle)); + h->sched = sched; + h->cfg = cfg; + h->cls = cls; + h->init = init; + h->connects = connects; + h->disconnects = disconnects; + h->bfc = bfc; + h->inbound_notify = inbound_notify; + h->outbound_notify = outbound_notify; + h->inbound_hdr_only = inbound_hdr_only; + h->outbound_hdr_only = outbound_hdr_only; + h->handlers = handlers; + h->client = GNUNET_CLIENT_connect (sched, "core", cfg); + h->startup_timeout = GNUNET_TIME_relative_to_absolute (timeout); + h->hcnt = 0; + while (handlers[h->hcnt].callback != NULL) + h->hcnt++; + GNUNET_assert (h->hcnt < + (GNUNET_SERVER_MAX_MESSAGE_SIZE - + sizeof (struct InitMessage)) / sizeof (uint16_t)); + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Trying to connect to core service in next %llu ms.\n", + timeout.value); + h->th = + GNUNET_CLIENT_notify_transmit_ready (h->client, + sizeof (struct InitMessage) + + sizeof (uint16_t) * h->hcnt, timeout, + &transmit_start, h); +} + + +/** + * Disconnect from the core service. + * + * @param handle connection to core to disconnect + */ +void +GNUNET_CORE_disconnect (struct GNUNET_CORE_Handle *handle) +{ + if (handle->th != NULL) + GNUNET_NETWORK_notify_transmit_ready_cancel (handle->th); + if (handle->reconnect_task != GNUNET_SCHEDULER_NO_PREREQUISITE_TASK) + GNUNET_SCHEDULER_cancel (handle->sched, handle->reconnect_task); + GNUNET_CLIENT_disconnect (handle->client); + GNUNET_free (handle); +} + + +/** + * Build the configure message. + */ +static size_t +produce_configure_message (void *cls, size_t size, void *buf) +{ + struct GNUNET_CORE_TransmitHandle *th = cls; + struct GNUNET_CORE_Handle *ch = th->ch; + + if (buf == NULL) + { + /* communicate handle timeout/error! */ + if (th->info != NULL) + th->info (th->info_cls, NULL, 0, 0, GNUNET_TIME_UNIT_ZERO, 0, 0.0); + if (th->timeout_task != GNUNET_SCHEDULER_NO_PREREQUISITE_TASK) + GNUNET_CORE_notify_transmit_ready_cancel (th); + if (ch->submitted == th) + ch->submitted = NULL; + trigger_next_request (ch); + return 0; + } + GNUNET_assert (size >= sizeof (struct RequestConfigureMessage)); + memcpy (buf, &th[1], sizeof (struct RequestConfigureMessage)); + if (th->prev == NULL) + ch->pending_head = th->next; + else + th->prev->next = th->next; + if (th->next == NULL) + ch->pending_tail = th->prev; + else + th->next->prev = th->prev; + GNUNET_assert (ch->submitted == NULL); + ch->submitted = th; + return sizeof (struct RequestConfigureMessage); +} + + +/** + * Obtain statistics and/or change preferences for the given peer. + * + * @param handle connection to core to use + * @param peer identifies the peer + * @param timeout after how long should we give up (and call "info" with NULL + * for "peer" to signal an error)? + * @param bpm_out set to the current bandwidth limit (sending) for this peer, + * caller should set "bpm_out" to "-1" to avoid changing + * the current value; otherwise "bpm_out" will be lowered to + * the specified value; passing a pointer to "0" can be used to force + * us to disconnect from the peer; "bpm_out" might not increase + * as specified since the upper bound is generally + * determined by the other peer! + * @param amount reserve N bytes for receiving, negative + * amounts can be used to undo a (recent) reservation; + * @param preference increase incoming traffic share preference by this amount; + * in the absence of "amount" reservations, we use this + * preference value to assign proportional bandwidth shares + * to all connected peers + * @param info function to call with the resulting configuration information + * @param info_cls closure for info + */ +void +GNUNET_CORE_peer_configure (struct GNUNET_CORE_Handle *handle, + const struct GNUNET_PeerIdentity *peer, + struct GNUNET_TIME_Relative timeout, + unsigned int bpm_out, + int amount, + double preference, + GNUNET_CORE_PeerConfigurationInfoCallback info, + void *info_cls) +{ + struct RequestConfigureMessage *rcm; + struct GNUNET_CORE_TransmitHandle *th; + + th = GNUNET_malloc (sizeof (struct GNUNET_CORE_TransmitHandle) + + sizeof (struct RequestConfigureMessage)); + /* append to list */ + th->prev = handle->pending_tail; + if (handle->pending_tail == NULL) + handle->pending_head = th; + else + handle->pending_tail->next = th; + th->ch = handle; + th->get_message = &produce_configure_message; + th->get_message_cls = th; + th->info = info; + th->info_cls = info_cls; + th->timeout = GNUNET_TIME_relative_to_absolute (timeout); + th->timeout_task = GNUNET_SCHEDULER_add_delayed (handle->sched, + GNUNET_NO, + GNUNET_SCHEDULER_PRIORITY_KEEP, + GNUNET_SCHEDULER_NO_PREREQUISITE_TASK, + timeout, + &timeout_request, th); + th->msize = sizeof (struct RequestConfigureMessage); + rcm = (struct RequestConfigureMessage *) &th[1]; + rcm->header.size = htons (sizeof (struct RequestConfigureMessage)); + rcm->header.type = htons (GNUNET_MESSAGE_TYPE_CORE_REQUEST_CONFIGURE); + rcm->reserved = htonl (0); + rcm->limit_outbound_bpm = htonl (bpm_out); + rcm->reserve_inbound = htonl (amount); + rcm->preference_change = preference; + rcm->peer = *peer; + if (handle->pending_head == th) + trigger_next_request (handle); +} + + +/** + * Build the message requesting data transmission. + */ +static size_t +produce_send (void *cls, size_t size, void *buf) +{ + struct GNUNET_CORE_TransmitHandle *th = cls; + struct GNUNET_CORE_Handle *h; + struct SendMessage *sm; + size_t dt; + GNUNET_NETWORK_TransmitReadyNotify notify; + void *notify_cls; + + h = th->ch; + if (buf == NULL) + { + /* timeout or error */ + GNUNET_assert (0 == th->notify (th->notify_cls, 0, NULL)); + if (th->timeout_task != GNUNET_SCHEDULER_NO_PREREQUISITE_TASK) + GNUNET_CORE_notify_transmit_ready_cancel (th); + trigger_next_request (h); + return 0; + } + GNUNET_assert (th->timeout_task != GNUNET_SCHEDULER_NO_PREREQUISITE_TASK); + sm = (struct SendMessage *) buf; + sm->header.type = htons (GNUNET_MESSAGE_TYPE_CORE_SEND); + sm->priority = htonl (th->priority); + sm->deadline = GNUNET_TIME_absolute_hton (th->timeout); + sm->peer = th->peer; + notify = th->notify; + notify_cls = th->notify_cls; + GNUNET_CORE_notify_transmit_ready_cancel (th); + trigger_next_request (h); + GNUNET_assert (size >= sizeof (struct SendMessage)); + dt = notify (notify_cls, size - sizeof (struct SendMessage), &sm[1]); + sm->header.size = htons (dt + sizeof (struct SendMessage)); + GNUNET_assert (dt + sizeof (struct SendMessage) < size); + return dt + sizeof (struct SendMessage); +} + + +/** + * Ask the core to call "notify" once it is ready to transmit the + * given number of bytes to the specified "target". If we are not yet + * connected to the specified peer, a call to this function will cause + * us to try to establish a connection. + * + * @param handle connection to core service + * @param priority how important is the message? + * @param maxdelay how long can the message wait? + * @param target who should receive the message, + * use NULL for this peer (loopback) + * @param notify_size how many bytes of buffer space does notify want? + * @param notify function to call when buffer space is available + * @param notify_cls closure for notify + * @return non-NULL if the notify callback was queued, + * NULL if we can not even queue the request (insufficient + * memory); if NULL is returned, "notify" will NOT be called. + */ +struct GNUNET_CORE_TransmitHandle * +GNUNET_CORE_notify_transmit_ready (struct GNUNET_CORE_Handle *handle, + unsigned int priority, + struct GNUNET_TIME_Relative maxdelay, + const struct GNUNET_PeerIdentity *target, + size_t notify_size, + GNUNET_NETWORK_TransmitReadyNotify notify, + void *notify_cls) +{ + struct GNUNET_CORE_TransmitHandle *th; + + GNUNET_assert (notify_size + sizeof (struct SendMessage) < + GNUNET_SERVER_MAX_MESSAGE_SIZE); + th = GNUNET_malloc (sizeof (struct GNUNET_CORE_TransmitHandle)); + th->ch = handle; + /* append to list */ + th->prev = handle->pending_tail; + if (handle->pending_tail == NULL) + handle->pending_head = th; + else + handle->pending_tail->next = th; + th->get_message = &produce_send; + th->get_message_cls = th; + th->notify = notify; + th->notify_cls = notify_cls; + th->peer = *target; + th->timeout = GNUNET_TIME_relative_to_absolute (maxdelay); + th->timeout_task = GNUNET_SCHEDULER_add_delayed (handle->sched, + GNUNET_NO, + GNUNET_SCHEDULER_PRIORITY_KEEP, + GNUNET_SCHEDULER_NO_PREREQUISITE_TASK, + maxdelay, + &timeout_request, th); + th->priority = priority; + th->msize = sizeof (struct SendMessage) + notify_size; + /* was the request queue previously empty? */ + if (handle->pending_head == th) + trigger_next_request (handle); + return NULL; +} + + +/** + * Cancel the specified transmission-ready notification. + * + * @param h handle that was returned by "notify_transmit_ready". + */ +void +GNUNET_CORE_notify_transmit_ready_cancel (struct GNUNET_CORE_TransmitHandle + *h) +{ + struct GNUNET_CORE_Handle *handle = h->ch; + + if (handle->submitted == h) + { + handle->submitted = NULL; + } + else + { + if (h->prev == NULL) + handle->pending_head = h->next; + else + h->prev->next = h->next; + if (h->next == NULL) + handle->pending_tail = h->prev; + else + h->next->prev = h->prev; + } + if (h->timeout_task != GNUNET_SCHEDULER_NO_PREREQUISITE_TASK) + GNUNET_SCHEDULER_cancel (handle->sched, h->timeout_task); + GNUNET_free (h); +} + + +/* end of core_api.c */ diff --git a/src/core/gnunet-service-core.c b/src/core/gnunet-service-core.c new file mode 100644 index 000000000..e9e076bcc --- /dev/null +++ b/src/core/gnunet-service-core.c @@ -0,0 +1,2859 @@ +/* + This file is part of GNUnet. + (C) 2009 Christian Grothoff (and other contributing authors) + + GNUnet is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published + by the Free Software Foundation; either version 2, 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 + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with GNUnet; see the file COPYING. If not, write to the + Free Software Foundation, Inc., 59 Temple Place - Suite 330, + Boston, MA 02111-1307, USA. +*/ + +/** + * @file core/gnunet-service-core.c + * @brief high-level P2P messaging + * @author Christian Grothoff + * + * TODO: + * TESTING: + * - write test for basic core functions: + * + connect to peer + * + transmit (encrypted) message [with handshake] + * + receive (encrypted) message, forward plaintext to clients + * POST-TESTING: + * - revisit API (which arguments are used, needed)? + * - add code to bound queue size when handling client's SEND message + * - add code to bound message queue size when passing messages to clients + * - add code to discard_expired_messages + * - add code to re-transmit key if first attempt failed + * + timeout on connect / key exchange, etc. + * + timeout for automatic re-try, etc. + * - add code to give up re-transmission of key if many attempts fail + * - add code to send PINGs if we are about to time-out otherwise + * ? add heuristic to do another send_key in "handle_set_key" + * in case previous attempt failed / didn't work / persist + * (but don't do it always to avoid storm of SET_KEY's going + * back and forth!) --- alternatively, add "status" field + * of the other peer to the set key message, that way we'd + * know for sure! + * - check that hostkey used by transport (for HELLOs) is the + * same as the hostkey that we are using! + * - free list of clients on exit + * - topology management: + * + bootstrapping (transport offer hello, plugins) + * + internal neighbour selection + * + update bandwidth usage statistics + * + bandwidth allocation (transport set quota) + * - optimize lookup (many O(n) list traversals + * could ideally be changed to O(1) hash map lookups) + */ +#include "platform.h" +#include "gnunet_util_lib.h" +#include "gnunet_hello_lib.h" +#include "gnunet_peerinfo_service.h" +#include "gnunet_protocols.h" +#include "gnunet_signatures.h" +#include "gnunet_transport_service.h" +#include "core.h" + + +/** + * Receive and send buffer windows grow over time. For + * how long can 'unused' bandwidth accumulate before we + * need to cap it? (specified in ms). + */ +#define MAX_WINDOW_TIME (5 * 60 * 1000) + + +/** + * Amount of bytes per minute (in/out) to assume initially + * (before either peer has communicated any particular + * preference). Should be rather low. + */ +#define DEFAULT_BPM_IN_OUT 2048 + + +/** + * What is the maximum delay for a SET_KEY message? + */ +#define MAX_SET_KEY_DELAY GNUNET_TIME_UNIT_SECONDS + + +/** + * What how long do we wait for SET_KEY confirmation initially? + */ +#define INITIAL_SET_KEY_RETRY_FREQUENCY GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS, 3) + + +/** + * What is the maximum delay for a PING message? + */ +#define MAX_PING_DELAY GNUNET_TIME_UNIT_SECONDS + + +/** + * What is the maximum delay for a PONG message? + */ +#define MAX_PONG_DELAY GNUNET_TIME_UNIT_SECONDS + + +/** + * What is the priority for a SET_KEY message? + */ +#define SET_KEY_PRIORITY 0xFFFFFF + + +/** + * What is the priority for a PING message? + */ +#define PING_PRIORITY 0xFFFFFF + + +/** + * What is the priority for a PONG message? + */ +#define PONG_PRIORITY 0xFFFFFF + + +/** + * What is the maximum age of a message for us to consider + * processing it? Note that this looks at the timestamp used + * by the other peer, so clock skew between machines does + * come into play here. So this should be picked high enough + * so that a little bit of clock skew does not prevent peers + * from connecting to us. + */ +#define MAX_MESSAGE_AGE GNUNET_TIME_UNIT_DAYS + + +/** + * What is the maximum size for encrypted messages? Note that this + * number imposes a clear limit on the maximum size of any message. + * Set to a value close to 64k but not so close that transports will + * have trouble with their headers. + */ +#define MAX_ENCRYPTED_MESSAGE_SIZE (63 * 1024) + + +/** + * State machine for our P2P encryption handshake. Everyone starts in + * "DOWN", if we receive the other peer's key (other peer initiated) + * we start in state RECEIVED (since we will immediately send our + * own); otherwise we start in SENT. If we get back a PONG from + * within either state, we move up to CONFIRMED (the PONG will always + * be sent back encrypted with the key we sent to the other peer). + */ +enum PeerStateMachine +{ + PEER_STATE_DOWN, + PEER_STATE_KEY_SENT, + PEER_STATE_KEY_RECEIVED, + PEER_STATE_KEY_CONFIRMED +}; + + +/** + * Number of bytes (at the beginning) of "struct EncryptedMessage" + * that are NOT encrypted. + */ +#define ENCRYPTED_HEADER_SIZE (sizeof(struct GNUNET_MessageHeader) + sizeof(uint32_t) + sizeof(GNUNET_HashCode)) + +/** + * Encapsulation for encrypted messages exchanged between + * peers. Followed by the actual encrypted data. + */ +struct EncryptedMessage +{ + /** + * Message type is either CORE_ENCRYPTED_MESSAGE. + */ + struct GNUNET_MessageHeader header; + + /** + * Always zero. + */ + uint32_t reserved GNUNET_PACKED; + + /** + * Hash of the plaintext, used to verify message integrity; + * ALSO used as the IV for the symmetric cipher! Everything + * after this hash will be encrypted. ENCRYPTED_HEADER_SIZE + * must be set to the offset of the next field. + */ + GNUNET_HashCode plaintext_hash; + + /** + * Sequence number, in network byte order. This field + * must be the first encrypted/decrypted field and the + * first byte that is hashed for the plaintext hash. + */ + uint32_t sequence_number GNUNET_PACKED; + + /** + * Desired bandwidth (how much we should send to this + * peer / how much is the sender willing to receive), + * in bytes per minute. + */ + uint32_t inbound_bpm_limit GNUNET_PACKED; + + /** + * Timestamp. Used to prevent reply of ancient messages + * (recent messages are caught with the sequence number). + */ + struct GNUNET_TIME_AbsoluteNBO timestamp; + +}; + +/** + * We're sending an (encrypted) PING to the other peer to check if he + * can decrypt. The other peer should respond with a PONG with the + * same content, except this time encrypted with the receiver's key. + */ +struct PingMessage +{ + /** + * Message type is either CORE_PING or CORE_PONG. + */ + struct GNUNET_MessageHeader header; + + /** + * Random number chosen to make reply harder. + */ + uint32_t challenge GNUNET_PACKED; + + /** + * Intended target of the PING, used primarily to check + * that decryption actually worked. + */ + struct GNUNET_PeerIdentity target; +}; + + +/** + * Message transmitted to set (or update) a session key. + */ +struct SetKeyMessage +{ + + /** + * Message type is either CORE_SET_KEY. + */ + struct GNUNET_MessageHeader header; + + /** + * Status of the sender (should be in "enum PeerStateMachine"), nbo. + */ + int32_t sender_status GNUNET_PACKED; + + /** + * Purpose of the signature, will be + * GNUNET_SIGNATURE_PURPOSE_SET_KEY. + */ + struct GNUNET_CRYPTO_RsaSignaturePurpose purpose; + + /** + * At what time was this key created? + */ + struct GNUNET_TIME_AbsoluteNBO creation_time; + + /** + * The encrypted session key. + */ + struct GNUNET_CRYPTO_RsaEncryptedData encrypted_key; + + /** + * Who is the intended recipient? + */ + struct GNUNET_PeerIdentity target; + + /** + * Signature of the stuff above (starting at purpose). + */ + struct GNUNET_CRYPTO_RsaSignature signature; + +}; + + +/** + * Message waiting for transmission. This struct + * is followed by the actual content of the message. + */ +struct MessageEntry +{ + + /** + * We keep messages in a linked list (for now). + */ + struct MessageEntry *next; + + /** + * By when are we supposed to transmit this message? + */ + struct GNUNET_TIME_Absolute deadline; + + /** + * How important is this message to us? + */ + unsigned int priority; + + /** + * How long is the message? (number of bytes following + * the "struct MessageEntry", but not including the + * size of "struct MessageEntry" itself!) + */ + uint16_t size; + + /** + * Was this message selected for transmission in the + * current round? GNUNET_YES or GNUNET_NO. + */ + int16_t do_transmit; + +}; + + +struct Neighbour +{ + /** + * We keep neighbours in a linked list (for now). + */ + struct Neighbour *next; + + /** + * Unencrypted messages destined for this peer. + */ + struct MessageEntry *messages; + + /** + * Head of the batched, encrypted message queue (already ordered, + * transmit starting with the head). + */ + struct MessageEntry *encrypted_head; + + /** + * Tail of the batched, encrypted message queue (already ordered, + * append new messages to tail) + */ + struct MessageEntry *encrypted_tail; + + /** + * Handle for pending requests for transmission to this peer + * with the transport service. NULL if no request is pending. + */ + struct GNUNET_TRANSPORT_TransmitHandle *th; + + /** + * Public key of the neighbour, NULL if we don't have it yet. + */ + struct GNUNET_CRYPTO_RsaPublicKeyBinaryEncoded *public_key; + + /** + * We received a PING message before we got the "public_key" + * (or the SET_KEY). We keep it here until we have a key + * to decrypt it. NULL if no PING is pending. + */ + struct PingMessage *pending_ping; + + /** + * Identity of the neighbour. + */ + struct GNUNET_PeerIdentity peer; + + /** + * Key we use to encrypt our messages for the other peer + * (initialized by us when we do the handshake). + */ + struct GNUNET_CRYPTO_AesSessionKey encrypt_key; + + /** + * Key we use to decrypt messages from the other peer + * (given to us by the other peer during the handshake). + */ + struct GNUNET_CRYPTO_AesSessionKey decrypt_key; + + /** + * ID of task used for re-trying plaintext scheduling. + */ + GNUNET_SCHEDULER_TaskIdentifier retry_plaintext_task; + + /** + * ID of task used for re-trying SET_KEY and PING message. + */ + GNUNET_SCHEDULER_TaskIdentifier retry_set_key_task; + + /** + * At what time did we generate our encryption key? + */ + struct GNUNET_TIME_Absolute encrypt_key_created; + + /** + * At what time did the other peer generate the decryption key? + */ + struct GNUNET_TIME_Absolute decrypt_key_created; + + /** + * At what time did we initially establish (as in, complete session + * key handshake) this connection? Should be zero if status != KEY_CONFIRMED. + */ + struct GNUNET_TIME_Absolute time_established; + + /** + * At what time did we last receive an encrypted message from the + * other peer? Should be zero if status != KEY_CONFIRMED. + */ + struct GNUNET_TIME_Absolute last_activity; + + /** + * Last latency observed from this peer. + */ + struct GNUNET_TIME_Relative last_latency; + + /** + * At what frequency are we currently re-trying SET KEY messages? + */ + struct GNUNET_TIME_Relative set_key_retry_frequency; + + /** + * Time of our last update to the "available_send_window". + */ + struct GNUNET_TIME_Absolute last_asw_update; + + /** + * Time of our last update to the "available_recv_window". + */ + struct GNUNET_TIME_Absolute last_arw_update; + + /** + * Number of bytes that we are eligible to transmit to this + * peer at this point. Incremented every minute by max_out_bpm, + * bounded by max_bpm (no back-log larger than MAX_BUF_FACT minutes, + * bandwidth-hogs are sampled at a frequency of about 78s!); + * may get negative if we have VERY high priority content. + */ + long long available_send_window; + + /** + * How much downstream capacity of this peer has been reserved for + * our traffic? (Our clients can request that a certain amount of + * bandwidth is available for replies to them; this value is used to + * make sure that this reserved amount of bandwidth is actually + * available). + */ + long long available_recv_window; + + /** + * How valueable were the messages of this peer recently? + */ + double current_preference; + + /** + * Bit map indicating which of the 32 sequence numbers before the last + * were received (good for accepting out-of-order packets and + * estimating reliability of the connection) + */ + unsigned int last_packets_bitmap; + + /** + * Number of messages in the message queue for this peer. + */ + unsigned int message_queue_size; + + /** + * last sequence number received on this connection (highest) + */ + uint32_t last_sequence_number_received; + + /** + * last sequence number transmitted + */ + uint32_t last_sequence_number_sent; + + /** + * Available bandwidth in for this peer (current target). + */ + uint32_t bpm_in; + + /** + * Available bandwidth out for this peer (current target). + */ + uint32_t bpm_out; + + /** + * Internal bandwidth limit set for this peer (initially + * typcially set to "-1"). "bpm_out" is MAX of + * "bpm_out_internal_limit" and "bpm_out_external_limit". + */ + uint32_t bpm_out_internal_limit; + + /** + * External bandwidth limit set for this peer by the + * peer that we are communicating with. "bpm_out" is MAX of + * "bpm_out_internal_limit" and "bpm_out_external_limit". + */ + uint32_t bpm_out_external_limit; + + /** + * What was our PING challenge number? + */ + uint32_t ping_challenge; + + /** + * What is our connection status? + */ + enum PeerStateMachine status; + +}; + + +/** + * Events are messages for clients. The struct + * itself is followed by the actual message. + */ +struct Event +{ + /** + * This is a linked list. + */ + struct Event *next; + + /** + * Size of the message. + */ + size_t size; + + /** + * Could this event be dropped if this queue + * is getting too large? (NOT YET USED!) + */ + int can_drop; + +}; + + +/** + * Data structure for each client connected to the core service. + */ +struct Client +{ + /** + * Clients are kept in a linked list. + */ + struct Client *next; + + /** + * Handle for the client with the server API. + */ + struct GNUNET_SERVER_Client *client_handle; + + /** + * Linked list of messages we still need to deliver to + * this client. + */ + struct Event *event_head; + + /** + * Tail of the linked list of events. + */ + struct Event *event_tail; + + /** + * Current transmit handle, NULL if no transmission request + * is pending. + */ + struct GNUNET_NETWORK_TransmitHandle *th; + + /** + * Array of the types of messages this peer cares + * about (with "tcnt" entries). Allocated as part + * of this client struct, do not free! + */ + uint16_t *types; + + /** + * Options for messages this client cares about, + * see GNUNET_CORE_OPTION_ values. + */ + uint32_t options; + + /** + * Number of types of incoming messages this client + * specifically cares about. Size of the "types" array. + */ + unsigned int tcnt; + +}; + + +/** + * Our public key. + */ +static struct GNUNET_CRYPTO_RsaPublicKeyBinaryEncoded my_public_key; + +/** + * Our identity. + */ +static struct GNUNET_PeerIdentity my_identity; + +/** + * Our private key. + */ +static struct GNUNET_CRYPTO_RsaPrivateKey *my_private_key; + +/** + * Our scheduler. + */ +struct GNUNET_SCHEDULER_Handle *sched; + +/** + * Our configuration. + */ +struct GNUNET_CONFIGURATION_Handle *cfg; + +/** + * Our server. + */ +static struct GNUNET_SERVER_Handle *server; + +/** + * Transport service. + */ +static struct GNUNET_TRANSPORT_Handle *transport; + +/** + * We keep neighbours in a linked list (for now). + */ +static struct Neighbour *neighbours; + +/** + * Linked list of our clients. + */ +static struct Client *clients; + + +/** + * Recalculate the number of bytes we expect to + * receive or transmit in a given window. + * + * @param window pointer to the byte counter (updated) + * @param ts pointer to the timestamp (updated) + * @param bpm number of bytes per minute that should + * be added to the window. + */ +static void +update_window (long long *window, + struct GNUNET_TIME_Absolute *ts, unsigned int bpm) +{ + struct GNUNET_TIME_Relative since; + + since = GNUNET_TIME_absolute_get_duration (*ts); + if (since.value < 60 * 1000) + return; /* not even a minute has passed */ + *ts = GNUNET_TIME_absolute_get (); + *window += (bpm * since.value) / 60 / 1000; + if (*window > MAX_WINDOW_TIME * bpm) + *window = MAX_WINDOW_TIME * bpm; +} + + +/** + * Find the entry for the given neighbour. + * + * @param peer identity of the neighbour + * @return NULL if we are not connected, otherwise the + * neighbour's entry. + */ +static struct Neighbour * +find_neighbour (const struct GNUNET_PeerIdentity *peer) +{ + struct Neighbour *ret; + + ret = neighbours; + while ((ret != NULL) && + (0 != memcmp (&ret->peer, + peer, sizeof (struct GNUNET_PeerIdentity)))) + ret = ret->next; + return ret; +} + + +/** + * Find the entry for the given client. + * + * @param client handle for the client + * @return NULL if we are not connected, otherwise the + * client's struct. + */ +static struct Client * +find_client (const struct GNUNET_SERVER_Client *client) +{ + struct Client *ret; + + ret = clients; + while ((ret != NULL) && (client != ret->client_handle)) + ret = ret->next; + return ret; +} + + +/** + * If necessary, initiate a request with the server to + * transmit messages from the queue of the given client. + * @param client who to transfer messages to + */ +static void request_transmit (struct Client *client); + + +/** + * Client is ready to receive data, provide it. + * @param cls closure + * @param size number of bytes available in buf + * @param buf where the callee should write the message + * @return number of bytes written to buf + */ +static size_t +do_client_transmit (void *cls, size_t size, void *buf) +{ + struct Client *client = cls; + struct Event *e; + char *tgt; + size_t ret; + + client->th = NULL; + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Client ready to receive %u bytes.\n", size); + if (buf == NULL) + { + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Failed to transmit data to client (disconnect)?\n"); + return 0; /* we'll surely get a disconnect soon... */ + } + tgt = buf; + ret = 0; + while ((NULL != (e = client->event_head)) && (e->size <= size)) + { + memcpy (&tgt[ret], &e[1], e->size); + size -= e->size; + ret += e->size; + client->event_head = e->next; + GNUNET_free (e); + } + GNUNET_assert (ret > 0); + if (client->event_head == NULL) + client->event_tail = NULL; + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Transmitting %u bytes to client\n", ret); + request_transmit (client); + return ret; +} + + +/** + * If necessary, initiate a request with the server to + * transmit messages from the queue of the given client. + * @param client who to transfer messages to + */ +static void +request_transmit (struct Client *client) +{ + + if (NULL != client->th) + return; /* already pending */ + if (NULL == client->event_head) + return; /* no more events pending */ + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Asking server to transmit %u bytes to client\n", + client->event_head->size); + client->th + = GNUNET_SERVER_notify_transmit_ready (client->client_handle, + client->event_head->size, + GNUNET_TIME_UNIT_FOREVER_REL, + &do_client_transmit, client); +} + + +/** + * Send a message to one of our clients. + * @param client target for the message + * @param msg message to transmit + * @param can_drop could this message be dropped if the + * client's queue is getting too large? + */ +static void +send_to_client (struct Client *client, + const struct GNUNET_MessageHeader *msg, int can_drop) +{ + struct Event *e; + uint16_t msize; + + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Preparing to send message of type %u to client.\n", + ntohs (msg->type)); + msize = ntohs (msg->size); + e = GNUNET_malloc (sizeof (struct Event) + msize); + /* append */ + if (client->event_tail != NULL) + client->event_tail->next = e; + else + client->event_head = e; + client->event_tail = e; + e->can_drop = can_drop; + e->size = msize; + memcpy (&e[1], msg, msize); + request_transmit (client); +} + + +/** + * Send a message to all of our current clients. + */ +static void +send_to_all_clients (const struct GNUNET_MessageHeader *msg, int can_drop) +{ + struct Client *c; + + c = clients; + while (c != NULL) + { + send_to_client (c, msg, can_drop); + c = c->next; + } +} + + +/** + * Handle CORE_INIT request. + */ +static void +handle_client_init (void *cls, + struct GNUNET_SERVER_Handle *server, + struct GNUNET_SERVER_Client *client, + const struct GNUNET_MessageHeader *message) +{ + const struct InitMessage *im; + struct InitReplyMessage irm; + struct Client *c; + uint16_t msize; + const uint16_t *types; + struct Neighbour *n; + struct ConnectNotifyMessage cnm; + + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Client connecting to core service with `%s' message\n", + "INIT"); + /* check that we don't have an entry already */ + c = clients; + while (c != NULL) + { + if (client == c->client_handle) + { + GNUNET_break (0); + GNUNET_SERVER_receive_done (client, GNUNET_SYSERR); + return; + } + c = c->next; + } + msize = ntohs (message->size); + if (msize < sizeof (struct InitMessage)) + { + GNUNET_break (0); + GNUNET_SERVER_receive_done (client, GNUNET_SYSERR); + return; + } + im = (const struct InitMessage *) message; + types = (const uint16_t *) &im[1]; + msize -= sizeof (struct InitMessage); + c = GNUNET_malloc (sizeof (struct Client) + msize); + c->client_handle = client; + c->next = clients; + clients = c; + memcpy (&c[1], types, msize); + c->types = (uint16_t *) & c[1]; + c->options = ntohl (im->options); + c->tcnt = msize / sizeof (uint16_t); + /* send init reply message */ + irm.header.size = htons (sizeof (struct InitReplyMessage)); + irm.header.type = htons (GNUNET_MESSAGE_TYPE_CORE_INIT_REPLY); + irm.reserved = htonl (0); + memcpy (&irm.publicKey, + &my_public_key, + sizeof (struct GNUNET_CRYPTO_RsaPublicKeyBinaryEncoded)); + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Sending `%s' message to client.\n", "INIT_REPLY"); + send_to_client (c, &irm.header, GNUNET_NO); + /* notify new client about existing neighbours */ + cnm.header.size = htons (sizeof (struct ConnectNotifyMessage)); + cnm.header.type = htons (GNUNET_MESSAGE_TYPE_CORE_NOTIFY_CONNECT); + n = neighbours; + while (n != NULL) + { + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Sending `%s' message to client.\n", "NOTIFY_CONNECT"); + cnm.bpm_available = htonl (n->bpm_out); + cnm.last_activity = GNUNET_TIME_absolute_hton (n->last_activity); + cnm.peer = n->peer; + send_to_client (c, &cnm.header, GNUNET_NO); + n = n->next; + } + GNUNET_SERVER_receive_done (client, GNUNET_OK); +} + + +/** + * A client disconnected, clean up. + * + * @param cls closure + * @param client identification of the client + */ +static void +handle_client_disconnect (void *cls, struct GNUNET_SERVER_Client *client) +{ + struct Client *pos; + struct Client *prev; + + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Client has disconnected from core service.\n"); + prev = NULL; + pos = clients; + while (pos != NULL) + { + if (client == pos->client_handle) + { + if (prev == NULL) + clients = pos->next; + else + prev->next = pos->next; + if (pos->th != NULL) + GNUNET_NETWORK_notify_transmit_ready_cancel (pos->th); + GNUNET_free (pos); + return; + } + prev = pos; + pos = pos->next; + } + /* client never sent INIT */ +} + + +/** + * Handle REQUEST_CONFIGURE request. + */ +static void +handle_client_request_configure (void *cls, + struct GNUNET_SERVER_Handle *server, + struct GNUNET_SERVER_Client *client, + const struct GNUNET_MessageHeader *message) +{ + const struct RequestConfigureMessage *rcm; + struct Neighbour *n; + struct ConfigurationInfoMessage cim; + struct Client *c; + int reserv; + + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Core service receives `%s' request.\n", "CONFIGURE"); + rcm = (const struct RequestConfigureMessage *) message; + n = find_neighbour (&rcm->peer); + memset (&cim, 0, sizeof (cim)); + if ((n != NULL) && (n->status == PEER_STATE_KEY_CONFIRMED)) + { + n->bpm_out_internal_limit = ntohl (rcm->limit_outbound_bpm); + n->bpm_out = GNUNET_MAX (n->bpm_out_internal_limit, + n->bpm_out_external_limit); + reserv = ntohl (rcm->reserve_inbound); + if (reserv < 0) + { + n->available_recv_window += reserv; + } + else if (reserv > 0) + { + update_window (&n->available_recv_window, + &n->last_arw_update, n->bpm_in); + if (n->available_recv_window < reserv) + reserv = n->available_recv_window; + n->available_recv_window -= reserv; + } + n->current_preference += rcm->preference_change; + if (n->current_preference < 0) + n->current_preference = 0; + cim.reserved_amount = htonl (reserv); + cim.bpm_in = htonl (n->bpm_in); + cim.bpm_out = htonl (n->bpm_out); + cim.latency = GNUNET_TIME_relative_hton (n->last_latency); + cim.preference = n->current_preference; + } + cim.header.size = htons (sizeof (struct ConfigurationInfoMessage)); + cim.header.type = htons (GNUNET_MESSAGE_TYPE_CORE_CONFIGURATION_INFO); + cim.peer = rcm->peer; + c = find_client (client); + if (c == NULL) + { + GNUNET_break (0); + return; + } + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Sending `%s' message to client.\n", "CONFIGURATION_INFO"); + send_to_client (c, &cim.header, GNUNET_NO); +} + + +/** + * Check if we have encrypted messages for the specified neighbour + * pending, and if so, check with the transport about sending them + * out. + * + * @param n neighbour to check. + */ +static void process_encrypted_neighbour_queue (struct Neighbour *n); + + +/** + * Function called when the transport service is ready to + * receive an encrypted message for the respective peer + * + * @param cls neighbour to use message from + * @param size number of bytes we can transmit + * @param buf where to copy the message + * @return number of bytes transmitted + */ +static size_t +notify_encrypted_transmit_ready (void *cls, size_t size, void *buf) +{ + struct Neighbour *n = cls; + struct MessageEntry *m; + size_t ret; + char *cbuf; + + n->th = NULL; + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Transport ready to receive %u bytes for `%4s'\n", + size, GNUNET_i2s (&n->peer)); + GNUNET_assert (NULL != (m = n->encrypted_head)); + n->encrypted_head = m->next; + if (m->next == NULL) + n->encrypted_tail = NULL; + ret = 0; + cbuf = buf; + if (buf != NULL) + { + GNUNET_assert (size >= m->size); + memcpy (cbuf, &m[1], m->size); + ret = m->size; + process_encrypted_neighbour_queue (n); + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Copied message of type %u and size %u into transport buffer for `%4s'\n", + ntohs (((struct GNUNET_MessageHeader *) &m[1])->type), + ret, GNUNET_i2s (&n->peer)); + } + else + { + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Transmission for message of type %u and size %u failed\n", + ntohs (((struct GNUNET_MessageHeader *) &m[1])->type), + m->size); + } + GNUNET_free (m); + return ret; +} + + +/** + * Check if we have plaintext messages for the specified neighbour + * pending, and if so, consider batching and encrypting them (and + * then trigger processing of the encrypted queue if needed). + * + * @param n neighbour to check. + */ +static void process_plaintext_neighbour_queue (struct Neighbour *n); + + +/** + * Check if we have encrypted messages for the specified neighbour + * pending, and if so, check with the transport about sending them + * out. + * + * @param n neighbour to check. + */ +static void +process_encrypted_neighbour_queue (struct Neighbour *n) +{ + if (n->th != NULL) + { + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Asked to process encrypted queue, but request already pending.\n"); + return; /* already pending */ + } + if (n->encrypted_head == NULL) + { + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Encrypted queue empty, trying plaintext queue instead.\n"); + process_plaintext_neighbour_queue (n); + return; + } + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Asking transport for transmission of %u bytes to `%4s' in next %llu ms\n", + n->encrypted_head->size, + GNUNET_i2s (&n->peer), + GNUNET_TIME_absolute_get_remaining (n->encrypted_head-> + deadline).value); + n->th = + GNUNET_TRANSPORT_notify_transmit_ready (transport, &n->peer, + n->encrypted_head->size, + GNUNET_TIME_absolute_get_remaining + (n->encrypted_head->deadline), + ¬ify_encrypted_transmit_ready, + n); + if (n->th == NULL) + { + /* message request too large (oops) */ + GNUNET_break (0); + /* FIXME: handle error somehow! */ + } +} + + +/** + * Decrypt size bytes from in and write the result to out. Use the + * key for inbound traffic of the given neighbour. This function does + * NOT do any integrity-checks on the result. + * + * @param n neighbour we are receiving from + * @param iv initialization vector to use + * @param in ciphertext + * @param out plaintext + * @param size size of in/out + * @return GNUNET_OK on success + */ +static int +do_decrypt (struct Neighbour *n, + const GNUNET_HashCode * iv, + const void *in, void *out, size_t size) +{ + if (size != (uint16_t) size) + { + GNUNET_break (0); + return GNUNET_NO; + } + if ((n->status != PEER_STATE_KEY_RECEIVED) && + (n->status != PEER_STATE_KEY_CONFIRMED)) + { + GNUNET_break_op (0); + return GNUNET_SYSERR; + } + if (size != + GNUNET_CRYPTO_aes_decrypt (&n->decrypt_key, + in, + (uint16_t) size, + (const struct + GNUNET_CRYPTO_AesInitializationVector *) iv, + out)) + { + GNUNET_break (0); + return GNUNET_SYSERR; + } + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Decrypted %u bytes from `%4s' using key %u\n", + size, GNUNET_i2s (&n->peer), n->decrypt_key.crc32); + return GNUNET_OK; +} + + +/** + * Encrypt size bytes from in and write the result to out. Use the + * key for outbound traffic of the given neighbour. + * + * @param n neighbour we are sending to + * @param iv initialization vector to use + * @param in ciphertext + * @param out plaintext + * @param size size of in/out + * @return GNUNET_OK on success + */ +static int +do_encrypt (struct Neighbour *n, + const GNUNET_HashCode * iv, + const void *in, void *out, size_t size) +{ + if (size != (uint16_t) size) + { + GNUNET_break (0); + return GNUNET_NO; + } + GNUNET_assert (size == + GNUNET_CRYPTO_aes_encrypt (in, + (uint16_t) size, + &n->encrypt_key, + (const struct + GNUNET_CRYPTO_AesInitializationVector + *) iv, out)); + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Encrypted %u bytes for `%4s' using key %u\n", size, + GNUNET_i2s (&n->peer), n->encrypt_key.crc32); + return GNUNET_OK; +} + + +/** + * Select messages for transmission. This heuristic uses a combination + * of earliest deadline first (EDF) scheduling (with bounded horizon) + * and priority-based discard (in case no feasible schedule exist) and + * speculative optimization (defer any kind of transmission until + * we either create a batch of significant size, 25% of max, or until + * we are close to a deadline). Furthermore, when scheduling the + * heuristic also packs as many messages into the batch as possible, + * starting with those with the earliest deadline. Yes, this is fun. + * + * @param n neighbour to select messages from + * @param size number of bytes to select for transmission + * @param retry_time set to the time when we should try again + * (only valid if this function returns zero) + * @return number of bytes selected, or 0 if we decided to + * defer scheduling overall; in that case, retry_time is set. + */ +static size_t +select_messages (struct Neighbour *n, + size_t size, struct GNUNET_TIME_Relative *retry_time) +{ + struct MessageEntry *pos; + struct MessageEntry *min; + struct MessageEntry *last; + unsigned int min_prio; + struct GNUNET_TIME_Absolute t; + struct GNUNET_TIME_Absolute now; + uint64_t delta; + uint64_t avail; + unsigned long long slack; /* how long could we wait before missing deadlines? */ + size_t off; + int discard_low_prio; + + GNUNET_assert (NULL != n->messages); + now = GNUNET_TIME_absolute_get (); + /* last entry in linked list of messages processed */ + last = NULL; + /* should we remove the entry with the lowest + priority from consideration for scheduling at the + end of the loop? */ + discard_low_prio = GNUNET_YES; + while (GNUNET_YES == discard_low_prio) + { + min = NULL; + min_prio = -1; + discard_low_prio = GNUNET_NO; + /* number of bytes available for transmission at time "t" */ + avail = n->available_send_window; + t = n->last_asw_update; + /* how many bytes have we (hyptothetically) scheduled so far */ + off = 0; + /* maximum time we can wait before transmitting anything + and still make all of our deadlines */ + slack = -1; + + pos = n->messages; + /* note that we use "*2" here because we want to look + a bit further into the future; much more makes no + sense since new message might be scheduled in the + meantime... */ + while ((pos != NULL) && (off < size * 2)) + { + if (pos->do_transmit == GNUNET_YES) + { + /* already removed from consideration */ + pos = pos->next; + continue; + } + if (discard_low_prio == GNUNET_NO) + { + delta = pos->deadline.value; + if (delta < t.value) + delta = 0; + else + delta = t.value - delta; + avail += delta * n->bpm_out / 1000 / 60; + if (avail < pos->size) + { + discard_low_prio = GNUNET_YES; /* we could not schedule this one! */ + } + else + { + avail -= pos->size; + /* update slack, considering both its absolute deadline + and relative deadlines caused by other messages + with their respective load */ + slack = GNUNET_MIN (slack, avail / n->bpm_out); + if (pos->deadline.value < now.value) + slack = 0; + else + slack = + GNUNET_MIN (slack, pos->deadline.value - now.value); + } + } + off += pos->size; + t.value = GNUNET_MAX (pos->deadline.value, t.value); + if (pos->priority <= min_prio) + { + /* update min for discard */ + min_prio = pos->priority; + min = pos; + } + pos = pos->next; + } + if (discard_low_prio) + { + /* remove lowest-priority entry from consideration */ + min->do_transmit = GNUNET_YES; /* means: discard (for now) */ + } + last = pos; + } + /* guard against sending "tiny" messages with large headers without + urgent deadlines */ + if ((slack > 1000) && (size > 4 * off)) + { + /* less than 25% of message would be filled with + deadlines still being met if we delay by one + second or more; so just wait for more data */ + retry_time->value = slack / 2; + /* reset do_transmit values for next time */ + while (pos != last) + { + pos->do_transmit = GNUNET_NO; + pos = pos->next; + } + return 0; + } + /* select marked messages (up to size) for transmission */ + off = 0; + pos = n->messages; + while (pos != last) + { + if ((pos->size <= size) && (pos->do_transmit == GNUNET_NO)) + { + pos->do_transmit = GNUNET_YES; /* mark for transmission */ + off += pos->size; + size -= pos->size; + } + else + pos->do_transmit = GNUNET_NO; /* mark for not transmitting! */ + pos = pos->next; + } + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Selected %u bytes of plaintext messages for transmission to `%4s'.\n", + off, GNUNET_i2s (&n->peer)); + return off; +} + + +/** + * Batch multiple messages into a larger buffer. + * + * @param n neighbour to take messages from + * @param buf target buffer + * @param size size of buf + * @param deadline set to transmission deadline for the result + * @param retry_time set to the time when we should try again + * (only valid if this function returns zero) + * @param priority set to the priority of the batch + * @return number of bytes written to buf (can be zero) + */ +static size_t +batch_message (struct Neighbour *n, + char *buf, + size_t size, + struct GNUNET_TIME_Absolute *deadline, + struct GNUNET_TIME_Relative *retry_time, + unsigned int *priority) +{ + struct MessageEntry *pos; + struct MessageEntry *prev; + struct MessageEntry *next; + size_t ret; + + ret = 0; + *priority = 0; + *deadline = GNUNET_TIME_UNIT_FOREVER_ABS; + *retry_time = GNUNET_TIME_UNIT_FOREVER_REL; + if (0 == select_messages (n, size, retry_time)) + { + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "No messages selected, will try again in %llu ms\n", + retry_time->value); + return 0; + } + pos = n->messages; + prev = NULL; + while ((pos != NULL) && (size >= sizeof (struct GNUNET_MessageHeader))) + { + next = pos->next; + if (GNUNET_YES == pos->do_transmit) + { + GNUNET_assert (pos->size <= size); + memcpy (&buf[ret], &pos[1], pos->size); + ret += pos->size; + size -= pos->size; + *priority += pos->priority; + deadline->value = GNUNET_MIN (deadline->value, pos->deadline.value); + GNUNET_free (pos); + if (prev == NULL) + n->messages = next; + else + prev->next = next; + } + else + { + prev = pos; + } + pos = next; + } + return ret; +} + + +/** + * Remove messages with deadlines that have long expired from + * the queue. + * + * @param n neighbour to inspect + */ +static void +discard_expired_messages (struct Neighbour *n) +{ + /* FIXME */ +} + + +/** + * Signature of the main function of a task. + * + * @param cls closure + * @param tc context information (why was this task triggered now) + */ +static void +retry_plaintext_processing (void *cls, + const struct GNUNET_SCHEDULER_TaskContext *tc) +{ + struct Neighbour *n = cls; + + n->retry_plaintext_task = GNUNET_SCHEDULER_NO_PREREQUISITE_TASK; + process_plaintext_neighbour_queue (n); +} + + +/** + * Send our key (and encrypted PING) to the other peer. + * + * @param n the other peer + */ +static void send_key (struct Neighbour *n); + + +/** + * Check if we have plaintext messages for the specified neighbour + * pending, and if so, consider batching and encrypting them (and + * then trigger processing of the encrypted queue if needed). + * + * @param n neighbour to check. + */ +static void +process_plaintext_neighbour_queue (struct Neighbour *n) +{ + char pbuf[MAX_ENCRYPTED_MESSAGE_SIZE]; /* plaintext */ + size_t used; + size_t esize; + struct EncryptedMessage *em; /* encrypted message */ + struct EncryptedMessage *ph; /* plaintext header */ + struct MessageEntry *me; + unsigned int priority; + struct GNUNET_TIME_Absolute deadline; + struct GNUNET_TIME_Relative retry_time; + + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Processing plaintext message queue for `%4s', scheduling messages.\n", + GNUNET_i2s (&n->peer)); + if (n->retry_plaintext_task != GNUNET_SCHEDULER_NO_PREREQUISITE_TASK) + { + GNUNET_SCHEDULER_cancel (sched, n->retry_plaintext_task); + n->retry_plaintext_task = GNUNET_SCHEDULER_NO_PREREQUISITE_TASK; + } + switch (n->status) + { + case PEER_STATE_DOWN: + send_key (n); + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Not yet connected, deferring processing of plaintext messages.\n"); + return; + case PEER_STATE_KEY_SENT: + GNUNET_assert (n->retry_set_key_task != + GNUNET_SCHEDULER_NO_PREREQUISITE_TASK); + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Not yet connected, deferring processing of plaintext messages.\n"); + return; + case PEER_STATE_KEY_RECEIVED: + GNUNET_assert (n->retry_set_key_task != + GNUNET_SCHEDULER_NO_PREREQUISITE_TASK); + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Not yet connected, deferring processing of plaintext messages.\n"); + return; + case PEER_STATE_KEY_CONFIRMED: + /* ready to continue */ + break; + } + if (n->messages == NULL) + { + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Plaintext message queue is empty.\n"); + return; /* no pending messages */ + } + discard_expired_messages (n); + if (n->encrypted_head != NULL) + { + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Encrypted message queue is still full, delaying plaintext processing.\n"); + return; /* wait for messages already encrypted to be + processed first! */ + } + ph = (struct EncryptedMessage *) pbuf; + deadline = GNUNET_TIME_UNIT_FOREVER_ABS; + priority = 0; + used = sizeof (struct EncryptedMessage); + + used += batch_message (n, + &pbuf[used], + MAX_ENCRYPTED_MESSAGE_SIZE - used, + &deadline, &retry_time, &priority); + if (used == sizeof (struct EncryptedMessage)) + { + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "No messages selected for processing at this time, will try again later.\n"); + /* no messages selected for sending, try again later... */ + n->retry_plaintext_task = + GNUNET_SCHEDULER_add_delayed (sched, + GNUNET_NO, + GNUNET_SCHEDULER_PRIORITY_IDLE, + GNUNET_SCHEDULER_NO_PREREQUISITE_TASK, + retry_time, + &retry_plaintext_processing, n); + return; + } + + ph->sequence_number = htonl (++n->last_sequence_number_sent); + ph->inbound_bpm_limit = htonl (n->bpm_in); + ph->timestamp = GNUNET_TIME_absolute_hton (GNUNET_TIME_absolute_get ()); + + /* setup encryption message header */ + me = GNUNET_malloc (sizeof (struct MessageEntry) + used); + me->deadline = deadline; + me->priority = priority; + me->size = used; + em = (struct EncryptedMessage *) &me[1]; + em->header.size = htons (used); + em->header.type = htons (GNUNET_MESSAGE_TYPE_CORE_ENCRYPTED_MESSAGE); + em->reserved = htonl (0); + esize = used - ENCRYPTED_HEADER_SIZE; + GNUNET_CRYPTO_hash (&ph->sequence_number, esize, &em->plaintext_hash); + /* encrypt */ + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Encrypting plaintext messages for transmission.\n"); + GNUNET_assert (GNUNET_OK == + do_encrypt (n, + &em->plaintext_hash, + &ph->sequence_number, + &em->sequence_number, esize)); + /* append to transmission list */ + if (n->encrypted_tail == NULL) + n->encrypted_head = me; + else + n->encrypted_tail->next = me; + n->encrypted_tail = me; + process_encrypted_neighbour_queue (n); +} + + +/** + * Handle CORE_SEND request. + */ +static void +handle_client_send (void *cls, + struct GNUNET_SERVER_Handle *server, + struct GNUNET_SERVER_Client *client, + const struct GNUNET_MessageHeader *message); + + +/** + * Function called to notify us that we either succeeded + * or failed to connect (at the transport level) to another + * peer. We should either free the message we were asked + * to transmit or re-try adding it to the queue. + * + * @param cls closure + * @param size number of bytes available in buf + * @param buf where the callee should write the message + * @return number of bytes written to buf + */ +static size_t +send_connect_continuation (void *cls, size_t size, void *buf) +{ + struct SendMessage *sm = cls; + + if (buf == NULL) + { + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Asked to send message to disconnected peer `%4s' and connection failed. Discarding message.\n", + GNUNET_i2s (&sm->peer)); + GNUNET_free (sm); + return 0; + } + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Connection to peer `%4s' succeeded, retrying original send request\n", + GNUNET_i2s (&sm->peer)); + handle_client_send (NULL, NULL, NULL, &sm->header); + GNUNET_free (sm); + return 0; +} + + +/** + * Handle CORE_SEND request. + */ +static void +handle_client_send (void *cls, + struct GNUNET_SERVER_Handle *server, + struct GNUNET_SERVER_Client *client, + const struct GNUNET_MessageHeader *message) +{ + const struct SendMessage *sm; + struct SendMessage *smc; + const struct GNUNET_MessageHeader *mh; + struct Neighbour *n; + struct MessageEntry *pred; + struct MessageEntry *pos; + struct MessageEntry *e; + uint16_t msize; + + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Core service receives `%s' request.\n", "SEND"); + msize = ntohs (message->size); + if (msize < + sizeof (struct SendMessage) + sizeof (struct GNUNET_MessageHeader)) + { + GNUNET_break (0); + if (client != NULL) + GNUNET_SERVER_receive_done (client, GNUNET_SYSERR); + return; + } + sm = (const struct SendMessage *) message; + msize -= sizeof (struct SendMessage); + mh = (const struct GNUNET_MessageHeader *) &sm[1]; + if (msize != ntohs (mh->size)) + { + GNUNET_break (0); + if (client != NULL) + GNUNET_SERVER_receive_done (client, GNUNET_SYSERR); + return; + } + n = find_neighbour (&sm->peer); + if (n == NULL) + { + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Not yet connected to `%4s', will try to establish connection\n", + GNUNET_i2s (&sm->peer)); + msize += sizeof (struct SendMessage); + /* ask transport to connect to the peer */ + /* FIXME: this code does not handle the + case where we get multiple SendMessages before + transport responds to this request; + => need to track pending requests! */ + smc = GNUNET_malloc (msize); + memcpy (smc, sm, msize); + GNUNET_TRANSPORT_notify_transmit_ready (transport, + &sm->peer, + 0, + GNUNET_TIME_absolute_get_remaining + (GNUNET_TIME_absolute_ntoh + (sm->deadline)), + &send_connect_continuation, + smc); + if (client != NULL) + GNUNET_SERVER_receive_done (client, GNUNET_OK); + return; + } + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Core queues %u bytes of plaintext data for transmission to `%4s'.\n", + msize, GNUNET_i2s (&sm->peer)); + /* FIXME: consider bounding queue size */ + e = GNUNET_malloc (sizeof (struct MessageEntry) + msize); + e->deadline = GNUNET_TIME_absolute_ntoh (sm->deadline); + e->priority = ntohl (sm->priority); + e->size = msize; + memcpy (&e[1], mh, msize); + + /* insert, keep list sorted by deadline */ + pred = NULL; + pos = n->messages; + while ((pos != NULL) && (pos->deadline.value < e->deadline.value)) + { + pred = pos; + pos = pos->next; + } + if (pred == NULL) + n->messages = e; + else + pred->next = e; + e->next = pos; + + /* consider scheduling now */ + process_plaintext_neighbour_queue (n); + if (client != NULL) + GNUNET_SERVER_receive_done (client, GNUNET_OK); +} + + +/** + * List of handlers for the messages understood by this + * service. + */ +static struct GNUNET_SERVER_MessageHandler handlers[] = { + {&handle_client_init, NULL, + GNUNET_MESSAGE_TYPE_CORE_INIT, 0}, + {&handle_client_request_configure, NULL, + GNUNET_MESSAGE_TYPE_CORE_REQUEST_CONFIGURE, + sizeof (struct RequestConfigureMessage)}, + {&handle_client_send, NULL, + GNUNET_MESSAGE_TYPE_CORE_SEND, 0}, + {NULL, NULL, 0, 0} +}; + + +/** + * PEERINFO is giving us a HELLO for a peer. Add the + * public key to the neighbour's struct and retry + * send_key. Or, if we did not get a HELLO, just do + * nothing. + * + * @param cls NULL + * @param peer the peer for which this is the HELLO + * @param hello HELLO message of that peer + * @param trust amount of trust we currently have in that peer + */ +static void +process_hello_retry_send_key (void *cls, + const struct GNUNET_PeerIdentity *peer, + const struct GNUNET_HELLO_Message *hello, + uint32_t trust) +{ + struct Neighbour *n; + + if (peer == NULL) + return; + n = find_neighbour (peer); + if (n == NULL) + return; + if (n->public_key != NULL) + return; + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Received new HELLO for `%4s', initiating key exchange.\n", + GNUNET_i2s (peer)); + n->public_key = + GNUNET_malloc (sizeof (struct GNUNET_CRYPTO_RsaPublicKeyBinaryEncoded)); + if (GNUNET_OK != GNUNET_HELLO_get_key (hello, n->public_key)) + { + GNUNET_free (n->public_key); + n->public_key = NULL; + return; + } + send_key (n); +} + + +/** + * Task that will retry "send_key" if our previous attempt failed + * to yield a PONG. + */ +static void +set_key_retry_task (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc) +{ + struct Neighbour *n = cls; + + GNUNET_assert (n->status != PEER_STATE_KEY_CONFIRMED); + n->retry_set_key_task = GNUNET_SCHEDULER_NO_PREREQUISITE_TASK; + n->set_key_retry_frequency = + GNUNET_TIME_relative_multiply (n->set_key_retry_frequency, 2); + send_key (n); +} + + +/** + * Send our key (and encrypted PING) to the other peer. + * + * @param n the other peer + */ +static void +send_key (struct Neighbour *n) +{ + struct SetKeyMessage *sm; + struct MessageEntry *me; + struct PingMessage pp; + struct PingMessage *pm; + + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Asked to perform key exchange with `%4s'.\n", + GNUNET_i2s (&n->peer)); + if (n->public_key == NULL) + { + /* lookup n's public key, then try again */ + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Lacking public key for `%4s', trying to obtain one.\n", + GNUNET_i2s (&n->peer)); + GNUNET_PEERINFO_for_all (cfg, + sched, + &n->peer, + 0, + GNUNET_TIME_UNIT_MINUTES, + &process_hello_retry_send_key, NULL); + return; + } + /* first, set key message */ + me = GNUNET_malloc (sizeof (struct MessageEntry) + + sizeof (struct SetKeyMessage)); + me->deadline = GNUNET_TIME_relative_to_absolute (MAX_SET_KEY_DELAY); + me->priority = SET_KEY_PRIORITY; + me->size = sizeof (struct SetKeyMessage); + if (n->encrypted_head == NULL) + n->encrypted_head = me; + else + n->encrypted_tail->next = me; + n->encrypted_tail = me; + sm = (struct SetKeyMessage *) &me[1]; + sm->header.size = htons (sizeof (struct SetKeyMessage)); + sm->header.type = htons (GNUNET_MESSAGE_TYPE_CORE_SET_KEY); + sm->sender_status = htonl ((int32_t) ((n->status == PEER_STATE_DOWN) ? + PEER_STATE_KEY_SENT : n->status)); + sm->purpose.size = + htonl (sizeof (struct GNUNET_CRYPTO_RsaSignaturePurpose) + + sizeof (struct GNUNET_TIME_AbsoluteNBO) + + sizeof (struct GNUNET_CRYPTO_RsaEncryptedData) + + sizeof (struct GNUNET_PeerIdentity)); + sm->purpose.purpose = htonl (GNUNET_SIGNATURE_PURPOSE_SET_KEY); + sm->creation_time = GNUNET_TIME_absolute_hton (n->encrypt_key_created); + sm->target = n->peer; + GNUNET_assert (GNUNET_OK == + GNUNET_CRYPTO_rsa_encrypt (&n->encrypt_key, + sizeof (struct + GNUNET_CRYPTO_AesSessionKey), + n->public_key, + &sm->encrypted_key)); + GNUNET_assert (GNUNET_OK == + GNUNET_CRYPTO_rsa_sign (my_private_key, &sm->purpose, + &sm->signature)); + + /* second, encrypted PING message */ + me = GNUNET_malloc (sizeof (struct MessageEntry) + + sizeof (struct PingMessage)); + me->deadline = GNUNET_TIME_relative_to_absolute (MAX_PING_DELAY); + me->priority = PING_PRIORITY; + me->size = sizeof (struct PingMessage); + n->encrypted_tail->next = me; + n->encrypted_tail = me; + pm = (struct PingMessage *) &me[1]; + pm->header.size = htons (sizeof (struct PingMessage)); + pm->header.type = htons (GNUNET_MESSAGE_TYPE_CORE_PING); + pp.challenge = htonl (n->ping_challenge); + pp.target = n->peer; + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Encrypting `%s' and `%s' messages for `%4s'.\n", + "SET_KEY", "PING", GNUNET_i2s (&n->peer)); + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Sending `%s' to `%4s' with challenge %u encrypted using key %u\n", + "PING", + GNUNET_i2s (&n->peer), n->ping_challenge, n->encrypt_key.crc32); + do_encrypt (n, + &n->peer.hashPubKey, + &pp.challenge, + &pm->challenge, + sizeof (struct PingMessage) - + sizeof (struct GNUNET_MessageHeader)); + /* update status */ + switch (n->status) + { + case PEER_STATE_DOWN: + n->status = PEER_STATE_KEY_SENT; + break; + case PEER_STATE_KEY_SENT: + break; + case PEER_STATE_KEY_RECEIVED: + break; + case PEER_STATE_KEY_CONFIRMED: + GNUNET_break (0); + break; + default: + GNUNET_break (0); + break; + } + /* trigger queue processing */ + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Triggering processing of encrypted message queue.\n"); + process_encrypted_neighbour_queue (n); + if (n->status != PEER_STATE_KEY_CONFIRMED) + n->retry_set_key_task + = GNUNET_SCHEDULER_add_delayed (sched, + GNUNET_NO, + GNUNET_SCHEDULER_PRIORITY_KEEP, + GNUNET_SCHEDULER_NO_PREREQUISITE_TASK, + n->set_key_retry_frequency, + &set_key_retry_task, n); +} + + +/** + * We received a SET_KEY message. Validate and update + * our key material and status. + * + * @param n the neighbour from which we received message m + * @param m the set key message we received + */ +static void +handle_set_key (struct Neighbour *n, const struct SetKeyMessage *m); + + +/** + * PEERINFO is giving us a HELLO for a peer. Add the public key to + * the neighbour's struct and retry handling the set_key message. Or, + * if we did not get a HELLO, just free the set key message. + * + * @param cls pointer to the set key message + * @param peer the peer for which this is the HELLO + * @param hello HELLO message of that peer + * @param trust amount of trust we currently have in that peer + */ +static void +process_hello_retry_handle_set_key (void *cls, + const struct GNUNET_PeerIdentity *peer, + const struct GNUNET_HELLO_Message *hello, + uint32_t trust) +{ + struct SetKeyMessage *sm = cls; + struct Neighbour *n; + + if (peer == NULL) + { + GNUNET_free (sm); + return; + } + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Looking for peer `%4s' to handle `%s' message.\n", + GNUNET_i2s (peer), "SET_KEY"); + n = find_neighbour (peer); + if (n == NULL) + { + GNUNET_break (0); + return; + } + if (n->public_key != NULL) + return; /* multiple HELLOs match!? */ + n->public_key = + GNUNET_malloc (sizeof (struct GNUNET_CRYPTO_RsaPublicKeyBinaryEncoded)); + if (GNUNET_OK != GNUNET_HELLO_get_key (hello, n->public_key)) + { + GNUNET_free (n->public_key); + n->public_key = NULL; + return; + } + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Received `%s' for `%4s', continuing processing of `%s' message.\n", + "HELLO", GNUNET_i2s (peer), "SET_KEY"); + handle_set_key (n, sm); +} + + +/** + * We received a PING message. Validate and transmit + * PONG. + * + * @param n sender of the PING + * @param m the encrypted PING message itself + */ +static void +handle_ping (struct Neighbour *n, const struct PingMessage *m) +{ + struct PingMessage t; + struct PingMessage *tp; + struct MessageEntry *me; + + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Core service receives `%s' request from `%4s'.\n", + "PING", GNUNET_i2s (&n->peer)); + if (GNUNET_OK != + do_decrypt (n, + &my_identity.hashPubKey, + &m->challenge, + &t.challenge, + sizeof (struct PingMessage) - + sizeof (struct GNUNET_MessageHeader))) + return; + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Decrypted `%s' to `%4s' with challenge %u decrypted using key %u\n", + "PING", + GNUNET_i2s (&t.target), + ntohl (t.challenge), n->decrypt_key.crc32); + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Target of `%s' request is `%4s'.\n", + "PING", GNUNET_i2s (&t.target)); + if (0 != memcmp (&t.target, + &my_identity, sizeof (struct GNUNET_PeerIdentity))) + { + GNUNET_break_op (0); + return; + } + me = GNUNET_malloc (sizeof (struct MessageEntry) + + sizeof (struct PingMessage)); + if (n->encrypted_tail != NULL) + n->encrypted_tail->next = me; + else + { + n->encrypted_tail = me; + n->encrypted_head = me; + } + me->deadline = GNUNET_TIME_relative_to_absolute (MAX_PONG_DELAY); + me->priority = PONG_PRIORITY; + me->size = sizeof (struct PingMessage); + tp = (struct PingMessage *) &me[1]; + tp->header.type = htons (GNUNET_MESSAGE_TYPE_CORE_PONG); + tp->header.size = htons (sizeof (struct PingMessage)); + do_encrypt (n, + &my_identity.hashPubKey, + &t.challenge, + &tp->challenge, + sizeof (struct PingMessage) - + sizeof (struct GNUNET_MessageHeader)); + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Encrypting `%s' with challenge %u using key %u\n", "PONG", + ntohl (t.challenge), n->encrypt_key.crc32); + /* trigger queue processing */ + process_encrypted_neighbour_queue (n); +} + + +/** + * We received a SET_KEY message. Validate and update + * our key material and status. + * + * @param n the neighbour from which we received message m + * @param m the set key message we received + */ +static void +handle_set_key (struct Neighbour *n, const struct SetKeyMessage *m) +{ + struct SetKeyMessage *m_cpy; + struct GNUNET_TIME_Absolute t; + struct GNUNET_CRYPTO_AesSessionKey k; + struct PingMessage *ping; + enum PeerStateMachine sender_status; + + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Core service receives `%s' request from `%4s'.\n", + "SET_KEY", GNUNET_i2s (&n->peer)); + if (n->public_key == NULL) + { + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Lacking public key for peer, trying to obtain one.\n"); + m_cpy = GNUNET_malloc (sizeof (struct SetKeyMessage)); + memcpy (m_cpy, m, sizeof (struct SetKeyMessage)); + /* lookup n's public key, then try again */ + GNUNET_PEERINFO_for_all (cfg, + sched, + &n->peer, + 0, + GNUNET_TIME_UNIT_MINUTES, + &process_hello_retry_handle_set_key, m_cpy); + return; + } + if ((ntohl (m->purpose.size) != + sizeof (struct GNUNET_CRYPTO_RsaSignaturePurpose) + + sizeof (struct GNUNET_TIME_AbsoluteNBO) + + sizeof (struct GNUNET_CRYPTO_RsaEncryptedData) + + sizeof (struct GNUNET_PeerIdentity)) || + (GNUNET_OK != + GNUNET_CRYPTO_rsa_verify (GNUNET_SIGNATURE_PURPOSE_SET_KEY, + &m->purpose, &m->signature, n->public_key))) + { + /* invalid signature */ + GNUNET_break_op (0); + return; + } + t = GNUNET_TIME_absolute_ntoh (m->creation_time); + if (((n->status == PEER_STATE_KEY_RECEIVED) || + (n->status == PEER_STATE_KEY_CONFIRMED)) && + (t.value < n->decrypt_key_created.value)) + { + /* this could rarely happen due to massive re-ordering of + messages on the network level, but is most likely either + a bug or some adversary messing with us. Report. */ + GNUNET_break_op (0); + return; + } + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Decrypting key material.\n"); + if ((GNUNET_CRYPTO_rsa_decrypt (my_private_key, + &m->encrypted_key, + &k, + sizeof (struct GNUNET_CRYPTO_AesSessionKey)) + != sizeof (struct GNUNET_CRYPTO_AesSessionKey)) || + (GNUNET_OK != GNUNET_CRYPTO_aes_check_session_key (&k))) + { + /* failed to decrypt !? */ + GNUNET_break_op (0); + return; + } + + n->decrypt_key = k; + if (n->decrypt_key_created.value != t.value) + { + /* fresh key, reset sequence numbers */ + n->last_sequence_number_received = 0; + n->last_packets_bitmap = 0; + n->decrypt_key_created = t; + } + sender_status = (enum PeerStateMachine) ntohl (m->sender_status); + switch (n->status) + { + case PEER_STATE_DOWN: + n->status = PEER_STATE_KEY_RECEIVED; + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Responding to `%s' with my own key.\n", "SET_KEY"); + send_key (n); + break; + case PEER_STATE_KEY_SENT: + case PEER_STATE_KEY_RECEIVED: + n->status = PEER_STATE_KEY_RECEIVED; + if ((sender_status != PEER_STATE_KEY_RECEIVED) && + (sender_status != PEER_STATE_KEY_CONFIRMED)) + { + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Responding to `%s' with my own key (other peer has status %u).\n", + "SET_KEY", sender_status); + send_key (n); + } + break; + case PEER_STATE_KEY_CONFIRMED: + if ((sender_status != PEER_STATE_KEY_RECEIVED) && + (sender_status != PEER_STATE_KEY_CONFIRMED)) + { + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Responding to `%s' with my own key (other peer has status %u), I was already fully up.\n", + "SET_KEY", sender_status); + send_key (n); + } + break; + default: + GNUNET_break (0); + break; + } + if (n->pending_ping != NULL) + { + ping = n->pending_ping; + n->pending_ping = NULL; + handle_ping (n, ping); + GNUNET_free (ping); + } +} + + +/** + * We received a PONG message. Validate and update + * our status. + * + * @param n sender of the PONG + * @param m the encrypted PONG message itself + */ +static void +handle_pong (struct Neighbour *n, const struct PingMessage *m) +{ + struct PingMessage t; + + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Core service receives `%s' request from `%4s'.\n", + "PONG", GNUNET_i2s (&n->peer)); + if (GNUNET_OK != + do_decrypt (n, + &n->peer.hashPubKey, + &m->challenge, + &t.challenge, + sizeof (struct PingMessage) - + sizeof (struct GNUNET_MessageHeader))) + return; + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Decrypted `%s' from `%4s' with challenge %u using key %u\n", + "PONG", + GNUNET_i2s (&t.target), + ntohl (t.challenge), n->decrypt_key.crc32); + if ((0 != memcmp (&t.target, + &n->peer, + sizeof (struct GNUNET_PeerIdentity))) || + (n->ping_challenge != ntohl (t.challenge))) + { + /* PONG malformed */ + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Received malfromed `%s' wanted sender `%4s' with challenge %u\n", + "PONG", GNUNET_i2s (&n->peer), n->ping_challenge); + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Received malfromed `%s' received from `%4s' with challenge %u\n", + "PONG", GNUNET_i2s (&t.target), ntohl (t.challenge)); + GNUNET_break_op (0); + return; + } + switch (n->status) + { + case PEER_STATE_DOWN: + GNUNET_break (0); /* should be impossible */ + return; + case PEER_STATE_KEY_SENT: + GNUNET_break (0); /* should be impossible, how did we decrypt? */ + return; + case PEER_STATE_KEY_RECEIVED: + n->status = PEER_STATE_KEY_CONFIRMED; + if (n->retry_set_key_task != GNUNET_SCHEDULER_NO_PREREQUISITE_TASK) + { + GNUNET_SCHEDULER_cancel (sched, n->retry_set_key_task); + n->retry_set_key_task = GNUNET_SCHEDULER_NO_PREREQUISITE_TASK; + } + process_encrypted_neighbour_queue (n); + break; + case PEER_STATE_KEY_CONFIRMED: + /* duplicate PONG? */ + break; + default: + GNUNET_break (0); + break; + } +} + + +/** + * Send a P2P message to a client. + * + * @param sender who sent us the message? + * @param client who should we give the message to? + * @param m contains the message to transmit + * @param msize number of bytes in buf to transmit + */ +static void +send_p2p_message_to_client (struct Neighbour *sender, + struct Client *client, + const void *m, size_t msize) +{ + char buf[msize + sizeof (struct NotifyTrafficMessage)]; + struct NotifyTrafficMessage *ntm; + + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Core service passes P2P message of type %u to client.\n", + ntohs (((const struct GNUNET_MessageHeader *) m)->type)); + ntm = (struct NotifyTrafficMessage *) buf; + ntm->header.size = htons (msize + sizeof (struct NotifyTrafficMessage)); + ntm->header.type = htons (GNUNET_MESSAGE_TYPE_CORE_NOTIFY_INBOUND); + ntm->reserved = htonl (0); + ntm->peer = sender->peer; + memcpy (&ntm[1], m, msize); + send_to_client (client, &ntm->header, GNUNET_YES); +} + + +/** + * Deliver P2P message to interested clients. + * + * @param sender who sent us the message? + * @param m the message + * @param msize size of the message (including header) + */ +static void +deliver_message (struct Neighbour *sender, + const struct GNUNET_MessageHeader *m, size_t msize) +{ + struct Client *cpos; + uint16_t type; + unsigned int tpos; + int deliver_full; + + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Passing decrypted P2P message to interested clients.\n"); + type = ntohs (m->type); + cpos = clients; + while (cpos != NULL) + { + deliver_full = GNUNET_NO; + if (cpos->options & GNUNET_CORE_OPTION_SEND_FULL_INBOUND) + deliver_full = GNUNET_YES; + else + { + for (tpos = 0; tpos < cpos->tcnt; tpos++) + { + if (type != cpos->types[tpos]) + continue; + deliver_full = GNUNET_YES; + break; + } + } + if (GNUNET_YES == deliver_full) + send_p2p_message_to_client (sender, cpos, m, msize); + else if (cpos->options & GNUNET_CORE_OPTION_SEND_HDR_INBOUND) + send_p2p_message_to_client (sender, cpos, m, + sizeof (struct GNUNET_MessageHeader)); + cpos = cpos->next; + } +} + + +/** + * Align P2P message and then deliver to interested clients. + * + * @param sender who sent us the message? + * @param buffer unaligned (!) buffer containing message + * @param msize size of the message (including header) + */ +static void +align_and_deliver (struct Neighbour *sender, const char *buffer, size_t msize) +{ + char abuf[msize]; + + /* TODO: call to statistics? */ + memcpy (abuf, buffer, msize); + deliver_message (sender, (const struct GNUNET_MessageHeader *) abuf, msize); +} + + +/** + * Deliver P2P messages to interested clients. + * + * @param sender who sent us the message? + * @param buffer buffer containing messages, can be modified + * @param buffer_size size of the buffer (overall) + * @param offset offset where messages in the buffer start + */ +static void +deliver_messages (struct Neighbour *sender, + const char *buffer, size_t buffer_size, size_t offset) +{ + struct GNUNET_MessageHeader *mhp; + struct GNUNET_MessageHeader mh; + uint16_t msize; + int need_align; + + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Delivering %u bytes of plaintext to interested clients.\n", + buffer_size); + while (offset + sizeof (struct GNUNET_MessageHeader) <= buffer_size) + { + if (0 != offset % sizeof (uint16_t)) + { + /* outch, need to copy to access header */ + memcpy (&mh, &buffer[offset], sizeof (struct GNUNET_MessageHeader)); + mhp = &mh; + } + else + { + /* can access header directly */ + mhp = (struct GNUNET_MessageHeader *) &buffer[offset]; + } + msize = ntohs (mhp->size); + if (msize + offset > buffer_size) + { + /* malformed message, header says it is larger than what + would fit into the overall buffer */ + GNUNET_break_op (0); + break; + } +#if HAVE_UNALIGNED_64_ACCESS + need_align = (0 != offset % 4) ? GNUNET_YES : GNUNET_NO; +#else + need_align = (0 != offset % 8) ? GNUNET_YES : GNUNET_NO; +#endif + if (GNUNET_YES == need_align) + align_and_deliver (sender, &buffer[offset], msize); + else + deliver_message (sender, + (const struct GNUNET_MessageHeader *) + &buffer[offset], msize); + offset += msize; + } +} + + +/** + * We received an encrypted message. Decrypt, validate and + * pass on to the appropriate clients. + */ +static void +handle_encrypted_message (struct Neighbour *n, + const struct EncryptedMessage *m) +{ + size_t size = ntohs (m->header.size); + char buf[size]; + struct EncryptedMessage *pt; /* plaintext */ + GNUNET_HashCode ph; + size_t off; + uint32_t snum; + struct GNUNET_TIME_Absolute t; + + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Core service receives `%s' request from `%4s'.\n", + "ENCRYPTED_MESSAGE", GNUNET_i2s (&n->peer)); + /* decrypt */ + if (GNUNET_OK != + do_decrypt (n, + &m->plaintext_hash, + &m->sequence_number, + &buf[ENCRYPTED_HEADER_SIZE], size - ENCRYPTED_HEADER_SIZE)) + return; + pt = (struct EncryptedMessage *) buf; + + /* validate hash */ + GNUNET_CRYPTO_hash (&pt->sequence_number, + size - ENCRYPTED_HEADER_SIZE, &ph); + if (0 != memcmp (&ph, &m->plaintext_hash, sizeof (GNUNET_HashCode))) + { + /* checksum failed */ + GNUNET_break_op (0); + return; + } + + /* validate sequence number */ + snum = ntohl (pt->sequence_number); + if (n->last_sequence_number_received == snum) + { + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Received duplicate message, ignoring.\n"); + /* duplicate, ignore */ + return; + } + if ((n->last_sequence_number_received > snum) && + (n->last_sequence_number_received - snum > 32)) + { + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Received ancient out of sequence message, ignoring.\n"); + /* ancient out of sequence, ignore */ + return; + } + if (n->last_sequence_number_received > snum) + { + unsigned int rotbit = + 1 << (n->last_sequence_number_received - snum - 1); + if ((n->last_packets_bitmap & rotbit) != 0) + { + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Received duplicate message, ignoring.\n"); + /* duplicate, ignore */ + return; + } + n->last_packets_bitmap |= rotbit; + } + if (n->last_sequence_number_received < snum) + { + n->last_packets_bitmap <<= (snum - n->last_sequence_number_received); + n->last_sequence_number_received = snum; + } + + /* check timestamp */ + t = GNUNET_TIME_absolute_ntoh (pt->timestamp); + if (GNUNET_TIME_absolute_get_duration (t).value > MAX_MESSAGE_AGE.value) + { + GNUNET_log (GNUNET_ERROR_TYPE_INFO, + _ + ("Message received far too old (%llu ms). Content ignored.\n"), + GNUNET_TIME_absolute_get_duration (t).value); + return; + } + + /* process decrypted message(s) */ + n->bpm_out_external_limit = ntohl (pt->inbound_bpm_limit); + n->bpm_out = GNUNET_MAX (n->bpm_out_external_limit, + n->bpm_out_internal_limit); + n->last_activity = GNUNET_TIME_absolute_get (); + off = sizeof (struct EncryptedMessage); + deliver_messages (n, buf, size, off); +} + + +/** + * Function called by the transport for each received message. + * + * @param cls closure + * @param latency estimated latency for communicating with the + * given peer + * @param peer (claimed) identity of the other peer + * @param message the message + */ +static void +handle_transport_receive (void *cls, + struct GNUNET_TIME_Relative latency, + const struct GNUNET_PeerIdentity *peer, + const struct GNUNET_MessageHeader *message) +{ + struct Neighbour *n; + struct GNUNET_TIME_Absolute now; + int up; + uint16_t type; + uint16_t size; + + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Received message of type %u from `%4s', demultiplexing.\n", + ntohs (message->type), GNUNET_i2s (peer)); + n = find_neighbour (peer); + if (n == NULL) + { + GNUNET_break (0); + return; + } + n->last_latency = latency; + up = n->status == PEER_STATE_KEY_CONFIRMED; + type = ntohs (message->type); + size = ntohs (message->size); + switch (type) + { + case GNUNET_MESSAGE_TYPE_CORE_SET_KEY: + if (size != sizeof (struct SetKeyMessage)) + { + GNUNET_break_op (0); + return; + } + handle_set_key (n, (const struct SetKeyMessage *) message); + break; + case GNUNET_MESSAGE_TYPE_CORE_ENCRYPTED_MESSAGE: + if (size < sizeof (struct EncryptedMessage) + + sizeof (struct GNUNET_MessageHeader)) + { + GNUNET_break_op (0); + return; + } + if ((n->status != PEER_STATE_KEY_RECEIVED) && + (n->status != PEER_STATE_KEY_CONFIRMED)) + { + GNUNET_break_op (0); + return; + } + handle_encrypted_message (n, (const struct EncryptedMessage *) message); + break; + case GNUNET_MESSAGE_TYPE_CORE_PING: + if (size != sizeof (struct PingMessage)) + { + GNUNET_break_op (0); + return; + } + if ((n->status != PEER_STATE_KEY_RECEIVED) && + (n->status != PEER_STATE_KEY_CONFIRMED)) + { + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Core service receives `%s' request from `%4s' but have not processed key; marking as pending.\n", + "PING", GNUNET_i2s (&n->peer)); + GNUNET_free_non_null (n->pending_ping); + n->pending_ping = GNUNET_malloc (sizeof (struct PingMessage)); + memcpy (n->pending_ping, message, sizeof (struct PingMessage)); + return; + } + handle_ping (n, (const struct PingMessage *) message); + break; + case GNUNET_MESSAGE_TYPE_CORE_PONG: + if (size != sizeof (struct PingMessage)) + { + GNUNET_break_op (0); + return; + } + if ((n->status != PEER_STATE_KEY_SENT) && + (n->status != PEER_STATE_KEY_RECEIVED) && + (n->status != PEER_STATE_KEY_CONFIRMED)) + { + /* could not decrypt pong, oops! */ + GNUNET_break_op (0); + return; + } + handle_pong (n, (const struct PingMessage *) message); + break; + default: + GNUNET_log (GNUNET_ERROR_TYPE_WARNING, + _("Unsupported message of type %u received.\n"), type); + return; + } + if (n->status == PEER_STATE_KEY_CONFIRMED) + { + now = GNUNET_TIME_absolute_get (); + n->last_activity = now; + if (!up) + n->time_established = now; + } +} + + +/** + * Function called by transport to notify us that + * a peer connected to us (on the network level). + * + * @param cls closure + * @param peer the peer that connected + * @param latency current latency of the connection + */ +static void +handle_transport_notify_connect (void *cls, + const struct GNUNET_PeerIdentity *peer, + struct GNUNET_TIME_Relative latency) +{ + struct Neighbour *n; + struct GNUNET_TIME_Absolute now; + struct ConnectNotifyMessage cnm; + + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Received connection from `%4s'.\n", GNUNET_i2s (peer)); + n = find_neighbour (peer); + if (n != NULL) + { + /* duplicate connect notification!? */ + GNUNET_break (0); + return; + } + now = GNUNET_TIME_absolute_get (); + n = GNUNET_malloc (sizeof (struct Neighbour)); + n->next = neighbours; + neighbours = n; + n->peer = *peer; + n->last_latency = latency; + GNUNET_CRYPTO_aes_create_session_key (&n->encrypt_key); + n->encrypt_key_created = now; + n->set_key_retry_frequency = INITIAL_SET_KEY_RETRY_FREQUENCY; + n->last_asw_update = now; + n->last_arw_update = now; + n->bpm_in = DEFAULT_BPM_IN_OUT; + n->bpm_out = DEFAULT_BPM_IN_OUT; + n->bpm_out_internal_limit = (uint32_t) - 1; + n->bpm_out_external_limit = DEFAULT_BPM_IN_OUT; + n->ping_challenge = GNUNET_CRYPTO_random_u32 (GNUNET_CRYPTO_QUALITY_WEAK, + (uint32_t) - 1); + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Created entry for new neighbour `%4s'.\n", + GNUNET_i2s (&n->peer)); + cnm.header.size = htons (sizeof (struct ConnectNotifyMessage)); + cnm.header.type = htons (GNUNET_MESSAGE_TYPE_CORE_NOTIFY_CONNECT); + cnm.bpm_available = htonl (DEFAULT_BPM_IN_OUT); + cnm.peer = *peer; + cnm.last_activity = GNUNET_TIME_absolute_hton (now); + send_to_all_clients (&cnm.header, GNUNET_YES); +} + + +/** + * Free the given entry for the neighbour (it has + * already been removed from the list at this point). + * @param n neighbour to free + */ +static void +free_neighbour (struct Neighbour *n) +{ + struct MessageEntry *m; + + while (NULL != (m = n->messages)) + { + n->messages = m->next; + GNUNET_free (m); + } + while (NULL != (m = n->encrypted_head)) + { + n->encrypted_head = m->next; + GNUNET_free (m); + } + if (NULL != n->th) + GNUNET_TRANSPORT_notify_transmit_ready_cancel (n->th); + if (n->retry_plaintext_task != GNUNET_SCHEDULER_NO_PREREQUISITE_TASK) + GNUNET_SCHEDULER_cancel (sched, n->retry_plaintext_task); + if (n->retry_set_key_task != GNUNET_SCHEDULER_NO_PREREQUISITE_TASK) + GNUNET_SCHEDULER_cancel (sched, n->retry_set_key_task); + GNUNET_free_non_null (n->public_key); + GNUNET_free_non_null (n->pending_ping); + GNUNET_free (n); +} + + +/** + * Function called by transport telling us that a peer + * disconnected. + * + * @param cls closure + * @param peer the peer that disconnected + */ +static void +handle_transport_notify_disconnect (void *cls, + const struct GNUNET_PeerIdentity *peer) +{ + struct ConnectNotifyMessage cnm; + struct Neighbour *n; + struct Neighbour *p; + + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Peer `%4s' disconnected from us.\n", GNUNET_i2s (peer)); + p = NULL; + n = neighbours; + while ((n != NULL) && + (0 != memcmp (&n->peer, peer, sizeof (struct GNUNET_PeerIdentity)))) + { + p = n; + n = n->next; + } + if (n == NULL) + { + GNUNET_break (0); + return; + } + if (p == NULL) + neighbours = n->next; + else + p->next = n->next; + cnm.header.size = htons (sizeof (struct ConnectNotifyMessage)); + cnm.header.type = htons (GNUNET_MESSAGE_TYPE_CORE_NOTIFY_DISCONNECT); + cnm.bpm_available = htonl (0); + cnm.peer = *peer; + cnm.last_activity = GNUNET_TIME_absolute_hton (n->last_activity); + send_to_all_clients (&cnm.header, GNUNET_YES); + free_neighbour (n); +} + + +/** + * Last task run during shutdown. Disconnects us from + * the transport. + */ +static void +cleaning_task (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc) +{ + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Core service shutting down.\n"); + GNUNET_assert (transport != NULL); + GNUNET_TRANSPORT_disconnect (transport); + transport = NULL; +} + + +/** + * Initiate core service. + * + * @param cls closure + * @param s scheduler to use + * @param serv the initialized server + * @param c configuration to use + */ +static void +run (void *cls, + struct GNUNET_SCHEDULER_Handle *s, + struct GNUNET_SERVER_Handle *serv, struct GNUNET_CONFIGURATION_Handle *c) +{ +#if 0 + unsigned long long qin; + unsigned long long qout; + unsigned long long tneigh; +#endif + char *keyfile; + + sched = s; + cfg = c; + /* parse configuration */ + if ( +#if 0 + (GNUNET_OK != + GNUNET_CONFIGURATION_get_value_number (c, + "CORE", + "XX", + &qin)) || + (GNUNET_OK != + GNUNET_CONFIGURATION_get_value_number (c, + "CORE", + "YY", + &qout)) || + (GNUNET_OK != + GNUNET_CONFIGURATION_get_value_number (c, + "CORE", + "ZZ_LIMIT", &tneigh)) || +#endif + (GNUNET_OK != + GNUNET_CONFIGURATION_get_value_filename (c, + "GNUNETD", + "HOSTKEY", &keyfile))) + { + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + _ + ("Core service is lacking key configuration settings. Exiting.\n")); + GNUNET_SCHEDULER_shutdown (s); + return; + } + my_private_key = GNUNET_CRYPTO_rsa_key_create_from_file (keyfile); + GNUNET_free (keyfile); + if (my_private_key == NULL) + { + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + _("Core service could not access hostkey. Exiting.\n")); + GNUNET_SCHEDULER_shutdown (s); + return; + } + GNUNET_CRYPTO_rsa_key_get_public (my_private_key, &my_public_key); + GNUNET_CRYPTO_hash (&my_public_key, + sizeof (my_public_key), &my_identity.hashPubKey); + /* setup notification */ + server = serv; + GNUNET_SERVER_disconnect_notify (server, &handle_client_disconnect, NULL); + /* setup transport connection */ + transport = GNUNET_TRANSPORT_connect (sched, + cfg, + NULL, + &handle_transport_receive, + &handle_transport_notify_connect, + &handle_transport_notify_disconnect); + GNUNET_assert (NULL != transport); + GNUNET_SCHEDULER_add_delayed (sched, + GNUNET_YES, + GNUNET_SCHEDULER_PRIORITY_IDLE, + GNUNET_SCHEDULER_NO_PREREQUISITE_TASK, + GNUNET_TIME_UNIT_FOREVER_REL, + &cleaning_task, NULL); + /* process client requests */ + GNUNET_SERVER_add_handlers (server, handlers); + GNUNET_log (GNUNET_ERROR_TYPE_INFO, + _("Core service of `%4s' ready.\n"), GNUNET_i2s (&my_identity)); +} + + +/** + * Function called during shutdown. Clean up our state. + */ +static void +cleanup (void *cls, struct GNUNET_CONFIGURATION_Handle *cfg) +{ + struct Neighbour *n; + + if (my_private_key != NULL) + GNUNET_CRYPTO_rsa_key_free (my_private_key); + while (NULL != (n = neighbours)) + { + neighbours = n->next; + free_neighbour (n); + } + /* + FIXME: + - free clients + */ +} + + +/** + * The main function for the transport service. + * + * @param argc number of arguments from the command line + * @param argv command line arguments + * @return 0 ok, 1 on error + */ +int +main (int argc, char *const *argv) +{ + return (GNUNET_OK == + GNUNET_SERVICE_run (argc, + argv, + "core", &run, NULL, &cleanup, NULL)) ? 0 : 1; +} + +/* end of gnunet-service-core.c */ diff --git a/src/core/test_core_api.c b/src/core/test_core_api.c new file mode 100644 index 000000000..e1b3dcd0d --- /dev/null +++ b/src/core/test_core_api.c @@ -0,0 +1,368 @@ +/* + This file is part of GNUnet. + (C) 2009 Christian Grothoff (and other contributing authors) + + GNUnet is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published + by the Free Software Foundation; either version 2, 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 + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with GNUnet; see the file COPYING. If not, write to the + Free Software Foundation, Inc., 59 Temple Place - Suite 330, + Boston, MA 02111-1307, USA. +*/ +/** + * @file transport/test_core_api.c + * @brief testcase for core_api.c + * + * FIXME: + * - make sure connect callback is invoked properly as well! + */ +#include "platform.h" +#include "gnunet_common.h" +#include "gnunet_arm_service.h" +#include "gnunet_core_service.h" +#include "gnunet_getopt_lib.h" +#include "gnunet_os_lib.h" +#include "gnunet_program_lib.h" +#include "gnunet_scheduler_lib.h" +#include "gnunet_transport_service.h" + +#define VERBOSE GNUNET_NO + +#define START_ARM GNUNET_YES + + +/** + * How long until we give up on transmitting the message? + */ +#define TIMEOUT GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS, 15) + +#define MTYPE 12345 + +struct PeerContext +{ + struct GNUNET_CONFIGURATION_Handle *cfg; + struct GNUNET_CORE_Handle *ch; + struct GNUNET_PeerIdentity id; /* FIXME: this is always all-zeros! */ + struct GNUNET_TRANSPORT_Handle *th; + struct GNUNET_MessageHeader *hello; +#if START_ARM + pid_t arm_pid; +#endif +}; + +static struct PeerContext p1; + +static struct PeerContext p2; + +static struct GNUNET_SCHEDULER_Handle *sched; + +static int ok; + +#if VERBOSE +#define OKPP do { ok++; fprintf (stderr, "Now at stage %u at %s:%u\n", ok, __FILE__, __LINE__); } while (0) +#else +#define OKPP do { ok++; } while (0) +#endif + + + +static void +terminate_task (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc) +{ + GNUNET_assert (ok == 6); + GNUNET_CORE_disconnect (p1.ch); + GNUNET_CORE_disconnect (p2.ch); + GNUNET_TRANSPORT_disconnect (p1.th); + GNUNET_TRANSPORT_disconnect (p2.th); + GNUNET_ARM_stop_service ("core", p1.cfg, sched, TIMEOUT, NULL, NULL); + GNUNET_ARM_stop_service ("core", p2.cfg, sched, TIMEOUT, NULL, NULL); + ok = 0; +} + + +static void +connect_notify (void *cls, + const struct GNUNET_PeerIdentity *peer, + unsigned int bpm, struct GNUNET_TIME_Absolute last_activity) +{ + GNUNET_assert ((ok == 5) || (ok == 6)); + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Encrypted connection established to peer `%4s' (%u bpm)\n", + GNUNET_i2s (peer), bpm); +} + + +static void +disconnect_notify (void *cls, + const struct GNUNET_PeerIdentity *peer, + unsigned int bpm, + struct GNUNET_TIME_Absolute last_activity) +{ + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Encrypted connection to `%4s' cut\n", GNUNET_i2s (peer)); +} + + +static unsigned int +bfc_callback (void *cls, + const struct GNUNET_PeerIdentity *receiver, + void *position, unsigned int padding) +{ + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Core requests data for `%4s', I have none.\n", + GNUNET_i2s (receiver)); + return 0; +} + + +static int +inbound_notify (void *cls, + const struct GNUNET_PeerIdentity *other, + const struct GNUNET_MessageHeader *message) +{ + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Core provides inbound data from `%4s'.\n", GNUNET_i2s (other)); + return GNUNET_OK; +} + + +static int +outbound_notify (void *cls, + const struct GNUNET_PeerIdentity *other, + const struct GNUNET_MessageHeader *message) +{ + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Core notifies about outbound data for `%4s'.\n", + GNUNET_i2s (other)); + return GNUNET_OK; +} + + +static int +process_mtype (void *cls, + const struct GNUNET_PeerIdentity *peer, + const struct GNUNET_MessageHeader *message) +{ + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Receiving message from `%4s'.\n", GNUNET_i2s (peer)); + GNUNET_assert (ok == 5); + OKPP; + GNUNET_SCHEDULER_add_delayed (sched, + GNUNET_NO, + GNUNET_SCHEDULER_PRIORITY_KEEP, + GNUNET_SCHEDULER_NO_PREREQUISITE_TASK, + GNUNET_TIME_UNIT_ZERO, &terminate_task, NULL); + return GNUNET_OK; +} + + +static struct GNUNET_CORE_MessageHandler handlers[] = { + {&process_mtype, MTYPE, sizeof (struct GNUNET_MessageHeader)}, + {NULL, 0, 0} +}; + + +static size_t +transmit_ready (void *cls, size_t size, void *buf) +{ + struct PeerContext *p = cls; + struct GNUNET_MessageHeader *m; + + GNUNET_assert (ok == 4); + OKPP; + GNUNET_assert (p == &p1); + GNUNET_assert (buf != NULL); + m = (struct GNUNET_MessageHeader *) buf; + m->type = htons (MTYPE); + m->size = htons (sizeof (struct GNUNET_MessageHeader)); + return sizeof (struct GNUNET_MessageHeader); +} + + + +static void +init_notify (void *cls, + struct GNUNET_CORE_Handle *server, + const struct GNUNET_PeerIdentity *my_identity, + const struct GNUNET_CRYPTO_RsaPublicKeyBinaryEncoded *publicKey) +{ + struct PeerContext *p = cls; + + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Core connection to `%4s' established\n", + GNUNET_i2s (my_identity)); + GNUNET_assert (server != NULL); + p->id = *my_identity; + p->ch = server; + if (cls == &p1) + { + GNUNET_assert (ok == 2); + OKPP; + /* connect p2 */ + GNUNET_CORE_connect (sched, + p2.cfg, + TIMEOUT, + &p2, + &init_notify, + &connect_notify, + &disconnect_notify, + &bfc_callback, + &inbound_notify, + GNUNET_YES, + &outbound_notify, GNUNET_YES, handlers); + } + else + { + GNUNET_assert (ok == 3); + OKPP; + GNUNET_assert (cls == &p2); + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Asking core (1) for transmission to peer `%4s'\n", + GNUNET_i2s (&p2.id)); + GNUNET_CORE_notify_transmit_ready (p1.ch, + 0, + TIMEOUT, + &p2.id, + sizeof (struct GNUNET_MessageHeader), + &transmit_ready, &p1); + + } +} + + +static void +process_hello (void *cls, + struct GNUNET_TIME_Relative latency, + const struct GNUNET_PeerIdentity *peer, + const struct GNUNET_MessageHeader *message) +{ + struct PeerContext *p = cls; + + GNUNET_assert (peer != NULL); + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Received (my) `%s' from transport service of `%4s'\n", + "HELLO", GNUNET_i2s (peer)); + GNUNET_assert (message != NULL); + p->hello = GNUNET_malloc (ntohs (message->size)); + memcpy (p->hello, message, ntohs (message->size)); + if ((p == &p1) && (p2.th != NULL)) + GNUNET_TRANSPORT_offer_hello (p2.th, message); + if ((p == &p2) && (p1.th != NULL)) + GNUNET_TRANSPORT_offer_hello (p1.th, message); + + if ((p == &p1) && (p2.hello != NULL)) + GNUNET_TRANSPORT_offer_hello (p1.th, p2.hello); + if ((p == &p2) && (p1.hello != NULL)) + GNUNET_TRANSPORT_offer_hello (p2.th, p1.hello); +} + + + +static void +setup_peer (struct PeerContext *p, const char *cfgname) +{ + p->cfg = GNUNET_CONFIGURATION_create (); +#if START_ARM + p->arm_pid = GNUNET_OS_start_process ("gnunet-service-arm", + "gnunet-service-arm", +#if VERBOSE + "-L", "DEBUG", +#endif + "-c", cfgname, NULL); + sleep (1); /* allow ARM to start */ +#endif + GNUNET_assert (GNUNET_OK == GNUNET_CONFIGURATION_load (p->cfg, cfgname)); + GNUNET_ARM_start_service ("core", p->cfg, sched, TIMEOUT, NULL, NULL); + p->th = GNUNET_TRANSPORT_connect (sched, p->cfg, p, NULL, NULL, NULL); + GNUNET_assert (p->th != NULL); + GNUNET_TRANSPORT_get_hello (p->th, TIMEOUT, &process_hello, p); +} + + +static void +run (void *cls, + struct GNUNET_SCHEDULER_Handle *s, + char *const *args, + const char *cfgfile, struct GNUNET_CONFIGURATION_Handle *cfg) +{ + GNUNET_assert (ok == 1); + OKPP; + sched = s; + setup_peer (&p1, "test_core_api_peer1.conf"); + setup_peer (&p2, "test_core_api_peer2.conf"); + GNUNET_CORE_connect (sched, + p1.cfg, + TIMEOUT, + &p1, + &init_notify, + &connect_notify, + &disconnect_notify, + &bfc_callback, + &inbound_notify, + GNUNET_YES, &outbound_notify, GNUNET_YES, handlers); +} + + +static void +stop_arm (struct PeerContext *p) +{ +#if START_ARM + if (0 != PLIBC_KILL (p->arm_pid, SIGTERM)) + GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING, "kill"); + if (p->arm_pid != waitpid (p->arm_pid, NULL, 0)) + GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING, "waitpid"); + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "ARM process %u stopped\n", p->arm_pid); +#endif + GNUNET_CONFIGURATION_destroy (p->cfg); +} + +static int +check () +{ + char *const argv[] = { "test-core-api", + "-c", + "test_core_api_data.conf", +#if VERBOSE + "-L", "DEBUG", +#endif + NULL + }; + struct GNUNET_GETOPT_CommandLineOption options[] = { + GNUNET_GETOPT_OPTION_END + }; + sleep (1); + ok = 1; + GNUNET_PROGRAM_run ((sizeof (argv) / sizeof (char *)) - 1, + argv, "test-core-api", "nohelp", options, &run, &ok); + stop_arm (&p1); + stop_arm (&p2); + return ok; +} + +int +main (int argc, char *argv[]) +{ + int ret; + + GNUNET_log_setup ("test-core-api", +#if VERBOSE + "DEBUG", +#else + "WARNING", +#endif + NULL); + ret = check (); + + return ret; +} + +/* end of test_core_api.c */ diff --git a/src/core/test_core_api_data.conf b/src/core/test_core_api_data.conf new file mode 100644 index 000000000..7a35faaf0 --- /dev/null +++ b/src/core/test_core_api_data.conf @@ -0,0 +1,27 @@ +[PATHS] +SERVICEHOME = /tmp/test-gnunetd-core-master/ + +[resolver] +PORT = 2464 + +[transport] +PORT = 2465 +PLUGINS = tcp + +[arm] +PORT = 2466 + +[statistics] +PORT = 2467 + +[tcp] +PORT = 2468 + +[peerinfo] +PORT = 2469 + +[core] +PORT = 2470 + +[testing] +WEAKRANDOM = YES diff --git a/src/core/test_core_api_peer1.conf b/src/core/test_core_api_peer1.conf new file mode 100644 index 000000000..fb060e3e6 --- /dev/null +++ b/src/core/test_core_api_peer1.conf @@ -0,0 +1,40 @@ +[PATHS] +SERVICEHOME = /tmp/test-gnunetd-core-peer-1/ +DEFAULTCONFIG = test_core_api_peer1.conf + +[resolver] +PORT = 12464 +#PREFIX = xterm -T resolver1 -e valgrind --tool=memcheck + +[transport] +PORT = 12465 +PLUGINS = tcp +#PREFIX = xterm -T transport1 -e +#PREFIX = xterm -T transport1 -e valgrind --tool=memcheck +#DEBUG = YES + +[arm] +PORT = 12466 +#GLOBAL_PREFIX = xterm -e valgrind --tool=memcheck + +[statistics] +PORT = 12467 +#PREFIX = xterm -T statistics1 -e valgrind --tool=memcheck + +[tcp] +PORT = 12468 + +[peerinfo] +PORT = 12469 +#PREFIX = xterm -T peerinfo1 -e valgrind --tool=memcheck +#PREFIX = xterm -T peerinfo1 -e + +[core] +PORT = 12470 +#PREFIX = xterm -T core1 -e valgrind --tool=memcheck +#PREFIX = xterm -T core1 -e gdb -x cmd --args +#PREFIX = xterm -T core1 -e +#DEBUG = YES + +[testing] +WEAKRANDOM = YES diff --git a/src/core/test_core_api_peer2.conf b/src/core/test_core_api_peer2.conf new file mode 100644 index 000000000..e3862da0f --- /dev/null +++ b/src/core/test_core_api_peer2.conf @@ -0,0 +1,39 @@ +[PATHS] +SERVICEHOME = /tmp/test-gnunetd-core-peer-2/ +DEFAULTCONFIG = test_core_api_peer2.conf + +[resolver] +PORT = 22464 +#PREFIX = xterm -T resolver2 -e valgrind --tool=memcheck + +[transport] +PORT = 22465 +PLUGINS = tcp +#PREFIX = xterm -T transport2 -e +#PREFIX = xterm -T transport2 -e valgrind --tool=memcheck +#DEBUG = YES + +[arm] +PORT = 22466 +#GLOBAL_PREFIX = xterm -e valgrind --tool=memcheck + +[statistics] +PORT = 22467 +#PREFIX = xterm -T statistics2 -e valgrind --tool=memcheck + +[tcp] +PORT = 22468 + +[peerinfo] +PORT = 22469 +#PREFIX = xterm -T peerinfo2 -e valgrind --tool=memcheck +#PREFIX = xterm -T peerinfo2 -e + +[core] +PORT = 22470 +#PREFIX = xterm -T core2 -e +#PREFIX = xterm -T core2 -e valgrind --tool=memcheck +#DEBUG = YES + +[testing] +WEAKRANDOM = YES diff --git a/src/core/test_core_api_start_only.c b/src/core/test_core_api_start_only.c new file mode 100644 index 000000000..dc4bfea98 --- /dev/null +++ b/src/core/test_core_api_start_only.c @@ -0,0 +1,257 @@ +/* + This file is part of GNUnet. + (C) 2009 Christian Grothoff (and other contributing authors) + + GNUnet is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published + by the Free Software Foundation; either version 2, 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 + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with GNUnet; see the file COPYING. If not, write to the + Free Software Foundation, Inc., 59 Temple Place - Suite 330, + Boston, MA 02111-1307, USA. +*/ +/** + * @file transport/test_core_api_start_only.c + * @brief testcase for core_api.c that only starts two peers, + * connects to the core service and shuts down again + */ +#include "platform.h" +#include "gnunet_common.h" +#include "gnunet_arm_service.h" +#include "gnunet_core_service.h" +#include "gnunet_getopt_lib.h" +#include "gnunet_os_lib.h" +#include "gnunet_program_lib.h" +#include "gnunet_scheduler_lib.h" + +#define VERBOSE GNUNET_NO + +#define START_ARM GNUNET_YES + + +/** + * How long until we give up on transmitting the message? + */ +#define TIMEOUT GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS, 30) + +#define MTYPE 12345 + +struct PeerContext +{ + struct GNUNET_CONFIGURATION_Handle *cfg; + struct GNUNET_CORE_Handle *ch; + struct GNUNET_PeerIdentity id; +#if START_ARM + pid_t arm_pid; +#endif +}; + +static struct PeerContext p1; + +static struct PeerContext p2; + +static struct GNUNET_SCHEDULER_Handle *sched; + +static int ok; + +#if VERBOSE +#define OKPP do { ok++; fprintf (stderr, "Now at stage %u at %s:%u\n", ok, __FILE__, __LINE__); } while (0) +#else +#define OKPP do { ok++; } while (0) +#endif + + + +static void +connect_notify (void *cls, + const struct GNUNET_PeerIdentity *peer, + unsigned int bpm, struct GNUNET_TIME_Absolute last_activity) +{ +} + + +static void +disconnect_notify (void *cls, + const struct GNUNET_PeerIdentity *peer, + unsigned int bpm, + struct GNUNET_TIME_Absolute last_activity) +{ +} + + +static unsigned int +bfc_callback (void *cls, + const struct GNUNET_PeerIdentity *receiver, + void *position, unsigned int padding) +{ + return 0; +} + + +static int +inbound_notify (void *cls, + const struct GNUNET_PeerIdentity *other, + const struct GNUNET_MessageHeader *message) +{ + return GNUNET_OK; +} + + +static int +outbound_notify (void *cls, + const struct GNUNET_PeerIdentity *other, + const struct GNUNET_MessageHeader *message) +{ + return GNUNET_OK; +} + + +static struct GNUNET_CORE_MessageHandler handlers[] = { + {NULL, 0, 0} +}; + + + +static void +init_notify (void *cls, + struct GNUNET_CORE_Handle *server, + const struct GNUNET_PeerIdentity *my_identity, + const struct GNUNET_CRYPTO_RsaPublicKeyBinaryEncoded *publicKey) +{ + struct PeerContext *p = cls; + + GNUNET_assert (server != NULL); + p->ch = server; + if (cls == &p1) + { + /* connect p2 */ + GNUNET_CORE_connect (sched, + p2.cfg, + TIMEOUT, + &p2, + &init_notify, + &connect_notify, + &disconnect_notify, + &bfc_callback, + &inbound_notify, + GNUNET_YES, + &outbound_notify, GNUNET_YES, handlers); + } + else + { + GNUNET_assert (cls == &p2); + GNUNET_CORE_disconnect (p1.ch); + GNUNET_CORE_disconnect (p2.ch); + GNUNET_ARM_stop_service ("core", p1.cfg, sched, TIMEOUT, NULL, NULL); + GNUNET_ARM_stop_service ("core", p2.cfg, sched, TIMEOUT, NULL, NULL); + + ok = 0; + } +} + + +static void +setup_peer (struct PeerContext *p, const char *cfgname) +{ + p->cfg = GNUNET_CONFIGURATION_create (); +#if START_ARM + p->arm_pid = GNUNET_OS_start_process ("gnunet-service-arm", + "gnunet-service-arm", +#if VERBOSE + "-L", "DEBUG", +#endif + "-c", cfgname, NULL); + sleep (1); /* allow ARM to start */ +#endif + GNUNET_assert (GNUNET_OK == GNUNET_CONFIGURATION_load (p->cfg, cfgname)); + GNUNET_ARM_start_service ("core", p->cfg, sched, TIMEOUT, NULL, NULL); +} + + +static void +run (void *cls, + struct GNUNET_SCHEDULER_Handle *s, + char *const *args, + const char *cfgfile, struct GNUNET_CONFIGURATION_Handle *cfg) +{ + GNUNET_assert (ok == 1); + OKPP; + sched = s; + setup_peer (&p1, "test_core_api_peer1.conf"); + setup_peer (&p2, "test_core_api_peer2.conf"); + GNUNET_CORE_connect (sched, + p1.cfg, + TIMEOUT, + &p1, + &init_notify, + &connect_notify, + &disconnect_notify, + &bfc_callback, + &inbound_notify, + GNUNET_YES, &outbound_notify, GNUNET_YES, handlers); +} + + +static void +stop_arm (struct PeerContext *p) +{ +#if START_ARM + if (0 != PLIBC_KILL (p->arm_pid, SIGTERM)) + GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING, "kill"); + if (p->arm_pid != waitpid (p->arm_pid, NULL, 0)) + GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING, "waitpid"); + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "ARM process %u stopped\n", p->arm_pid); +#endif + GNUNET_CONFIGURATION_destroy (p->cfg); +} + + +static int +check () +{ + char *const argv[] = { "test-core-api", + "-c", + "test_core_api_data.conf", +#if VERBOSE + "-L", "DEBUG", +#endif + NULL + }; + struct GNUNET_GETOPT_CommandLineOption options[] = { + GNUNET_GETOPT_OPTION_END + }; + + ok = 1; + GNUNET_PROGRAM_run ((sizeof (argv) / sizeof (char *)) - 1, + argv, "test-core-api", "nohelp", options, &run, &ok); + stop_arm (&p1); + stop_arm (&p2); + return ok; +} + +int +main (int argc, char *argv[]) +{ + int ret; + + GNUNET_log_setup ("test-core-api", +#if VERBOSE + "DEBUG", +#else + "WARNING", +#endif + NULL); + ret = check (); + + return ret; +} + +/* end of test_core_api_start_only.c */ diff --git a/src/datastore/plugin_datastore.h b/src/datastore/plugin_datastore.h new file mode 100644 index 000000000..b4dc87f9d --- /dev/null +++ b/src/datastore/plugin_datastore.h @@ -0,0 +1,241 @@ +/* + This file is part of GNUnet + (C) 2009 Christian Grothoff (and other contributing authors) + + GNUnet is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published + by the Free Software Foundation; either version 2, 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 + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with GNUnet; see the file COPYING. If not, write to the + Free Software Foundation, Inc., 59 Temple Place - Suite 330, + Boston, MA 02111-1307, USA. +*/ + +/** + * @file datastore/plugin_datastore.h + * @brief API for the database backend plugins. + * @author Christian Grothoff + * + * TODO: + * - consider defining enumeration or at least typedef + * for the type of "type" (instead of using uint32_t) + */ +#ifndef PLUGIN_DATASTORE_H +#define PLUGIN_DATASTORE_H + +#include "gnunet_configuration_lib.h" +#include "gnunet_scheduler_lib.h" +#include "gnunet_datastore_service.h" + +/** + * The datastore service will pass a pointer to a struct + * of this type as the first and only argument to the + * entry point of each datastore plugin. + */ +struct GNUNET_DATASTORE_PluginEnvironment +{ + /** + * Configuration to use. + */ + struct GNUNET_CONFIGURATION_Handle *cfg; + + /** + * Scheduler to use. + */ + struct GNUNET_SCHEDULER_Handle *sched; + +}; + + +/** + * Get an estimate of how much space the database is + * currently using. + * @return number of bytes used on disk + */ +typedef unsigned long long (*GNUNET_DATASTORE_GetSize) (void *cls); + + +/** + * Store an item in the datastore. + * + * @param cls closure + * @param key key for the item + * @param size number of bytes in data + * @param data content stored + * @param type type of the content + * @param priority priority of the content + * @param anonymity anonymity-level for the content + * @param expiration expiration time for the content + */ +typedef void + (*GNUNET_DATASTORE_Put) (void *cls, + const GNUNET_HashCode * key, + uint32_t size, + const void *data, + unit32_t type, + uint32_t priority, + uint32_t anonymity, + struct GNUNET_TIME_Absolute expiration); + + +/** + * Iterate over the results for a particular key + * in the datastore. + * + * @param cls closure + * @param key maybe NULL (to match all entries) + * @param vhash hash of the value, maybe NULL (to + * match all values that have the right key). + * Note that for DBlocks there is no difference + * betwen key and vhash, but for other blocks + * there may be! + * @param type entries of which type are relevant? + * Use 0 for any type. + * @param iter function to call on each matching value; + * will be called once with a NULL value at the end + * @param iter_cls closure for iter + */ +typedef void + (*GNUNET_DATASTORE_Get) (void *cls, + const GNUNET_HashCode * key, + const GNUNET_HashCode * vhash, + uint32_t type, + GNUNET_DATASTORE_Iterator iter, void *iter_cls); + + +/** + * Update the priority for a particular key in the datastore. If + * the expiration time in value is different than the time found in + * the datastore, the higher value should be kept. For the + * anonymity level, the lower value is to be used. The specified + * priority should be added to the existing priority, ignoring the + * priority in value. + * + * Note that it is possible for multiple values to match this put. + * In that case, all of the respective values are updated. + * + * @param uid unique identifier of the datum + * @param delta by how much should the priority + * change? If priority + delta < 0 the + * priority should be set to 0 (never go + * negative). + * @param expire new expiration time should be the + * MAX of any existing expiration time and + * this value + */ +typedef void + (*GNUNET_DATASTORE_Update) (void *cls, + unsigned long long uid, + int delta, struct GNUNET_TIME_Absolute expire); + + +/** + * Select a subset of the items in the datastore and call + * the given iterator for each of them. + * + * @param type entries of which type should be considered? + * Use 0 for any type. + * @param iter function to call on each matching value; + * will be called once with a NULL value at the end + * @param iter_cls closure for iter + */ +typedef void + (*GNUNET_DATASTORE_Selector) (void *cls, + uint32_t type, + GNUNET_DATASTORE_Iterator iter, + void *iter_cls); + +/** + * Drop database. + */ +typedef void (*GNUNET_DATASTORE_Drop) (void *cls); + + + +/** + * Each plugin is required to return a pointer to a struct of this + * type as the return value from its entry point. + */ +struct GNUNET_DATASTORE_PluginFunctions +{ + + /** + * Closure to use for all of the following callbacks. + */ + void *cls; + + /** + * Get the current on-disk size of the SQ store. Estimates are + * fine, if that's the only thing available. + */ + GNUNET_DATASTORE_GetSize size; + + /** + * Function to store an item in the datastore. + */ + GNUNET_DATASTORE_Put put; + + /** + * Function to iterate over the results for a particular key + * in the datastore. + */ + GNUNET_DATASTORE_Get get; + + /** + * Update the priority for a particular key in the datastore. If + * the expiration time in value is different than the time found in + * the datastore, the higher value should be kept. For the + * anonymity level, the lower value is to be used. The specified + * priority should be added to the existing priority, ignoring the + * priority in value. + */ + GNUNET_DATASTORE_Update update; + + /** + * Iterate over the items in the datastore in ascending + * order of priority. + */ + GNUNET_DATASTORE_Selector iter_low_priority; + + /** + * Iterate over content with anonymity zero. + */ + GNUNET_DATASTORE_Selector iter_zero_anonymity; + + /** + * Iterate over the items in the datastore in ascending + * order of expiration time. + */ + GNUNET_DATSTORE_Selector iter_ascending_expiration; + + /** + * Iterate over the items in the datastore in migration + * order. + */ + GNUNET_DATASTORE_Selector iter_migration_order; + + /** + * Iterate over all the items in the datastore + * as fast as possible in a single transaction + * (can lock datastore while this happens, focus + * is on doing it fast). + */ + GNUNET_DATASTORE_Selector iter_all_now; + + /** + * Delete the database. The next operation is + * guaranteed to be unloading of the module. + */ + GNUNET_DATASTORE_Drop drop; + +}; + + +#endif diff --git a/src/fragmentation/Makefile.am b/src/fragmentation/Makefile.am new file mode 100644 index 000000000..d3a47d920 --- /dev/null +++ b/src/fragmentation/Makefile.am @@ -0,0 +1,28 @@ +INCLUDES = -I$(top_srcdir)/src/include + +if MINGW + WINFLAGS = -Wl,--no-undefined -Wl,--export-all-symbols +endif + +if USE_COVERAGE + AM_CFLAGS = --coverage +endif + +lib_LTLIBRARIES = libgnunetfragmentation.la + +libgnunetfragmentation_la_SOURCES = \ + fragmentation.c +libgnunetfragmentation_la_LIBADD = \ + $(top_builddir)/src/util/libgnunetutil.la + +check_PROGRAMS = \ + test_fragmentation + +TESTS = $(check_PROGRAMS) + +test_fragmentation_SOURCES = \ + test_fragmentation.c +test_fragmentation_LDADD = \ + $(top_builddir)/src/fragmentation/libgnunetfragmentation.la \ + $(top_builddir)/src/util/libgnunetutil.la + diff --git a/src/fragmentation/fragmentation.c b/src/fragmentation/fragmentation.c new file mode 100644 index 000000000..9550663c7 --- /dev/null +++ b/src/fragmentation/fragmentation.c @@ -0,0 +1,702 @@ +/* + This file is part of GNUnet + (C) 2004, 2006, 2009 Christian Grothoff (and other contributing authors) + + GNUnet is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published + by the Free Software Foundation; either version 2, 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 + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with GNUnet; see the file COPYING. If not, write to the + Free Software Foundation, Inc., 59 Temple Place - Suite 330, + Boston, MA 02111-1307, USA. +*/ +/** + * @file fragmentation/fragmentation.c + * @brief fragmentation and defragmentation, this code allows + * sending and receiving messages that are larger than + * the MTU of the transport. Messages are still limited + * to a maximum size of 65535 bytes, which is a good + * idea because otherwise we may need ungainly fragmentation + * buffers. Each connected peer can have at most one + * fragmented packet at any given point in time (prevents + * DoS attacks). Fragmented messages that have not been + * completed after a certain amount of time are discarded. + * @author Christian Grothoff + */ + +#include "platform.h" +#include "gnunet_fragmentation_lib.h" + +/** + * Message fragment. This header is followed + * by the actual data of the fragment. + */ +struct Fragment +{ + + struct GNUNET_MessageHeader header; + + /** + * Fragment offset. + */ + uint32_t off GNUNET_PACKED; + + /** + * "unique" id for the fragment + */ + uint64_t id GNUNET_PACKED; + +}; + + +/** + * Defragmentation context. + */ +struct GNUNET_FRAGMENT_Context +{ +}; + + +/** + * Fragment an over-sized message. + * + * @param msg the message to fragment + * @param mtu the maximum message size + * @param proc function to call for each fragment + * @param proc_cls closure for proc + */ +void +GNUNET_FRAGMENT_fragment (const struct GNUNET_MessageHeader *msg, + uint16_t mtu, + GNUNET_FRAGMENT_MessageProcessor proc, + void *proc_cls) +{ + GNUNET_assert (0); +} + + +/** + * Create a defragmentation context. + * + * @param stats statistics context + * @param proc function to call with defragmented messages + * @param proc_cls closure for proc + * @return the defragmentation context + */ +struct GNUNET_FRAGMENT_Context * +GNUNET_FRAGMENT_context_create (struct GNUNET_STATISTICS_Handle *stats, + GNUNET_FRAGMENT_MessageProcessor proc, + void *proc_cls) +{ + return NULL; +} + + +/** + * Destroy the given defragmentation context. + */ +void +GNUNET_FRAGMENT_context_destroy (struct GNUNET_FRAGMENT_Context *ctx) +{ + GNUNET_assert (0); +} + + +/** + * We have received a fragment. Process it. + * + * @param ctx the context + * @param sender who transmitted the fragment + * @param msg the message that was received + */ +void +GNUNET_FRAGMENT_process (struct GNUNET_FRAGMENT_Context *ctx, + const struct GNUNET_PeerIdentity *sender, + const struct GNUNET_MessageHeader *msg) +{ + GNUNET_assert (0); +} + + + +#if 0 + +/** + * How many buckets does the fragment hash table + * have? + */ +#define DEFRAG_BUCKET_COUNT 16 + +/** + * After how long do fragments time out? + */ +#ifndef DEFRAGMENTATION_TIMEOUT +#define DEFRAGMENTATION_TIMEOUT (3 * GNUNET_CRON_MINUTES) +#endif + +/** + * Entry in the linked list of fragments. + */ +typedef struct FL +{ + struct FL *link; + P2P_fragmentation_MESSAGE *frag; +} FL; + +/** + * Entry in the GNUNET_hash table of fragments. + */ +typedef struct FC +{ + struct FC *next; + FL *head; + GNUNET_PeerIdentity sender; + int id; + GNUNET_CronTime ttl; +} FC; + +#define FRAGSIZE(fl) ((ntohs(fl->frag->header.size)-sizeof(P2P_fragmentation_MESSAGE))) + +static GNUNET_CoreAPIForPlugins *coreAPI; + +static GNUNET_Stats_ServiceAPI *stats; + +static int stat_defragmented; + +static int stat_fragmented; + +static int stat_discarded; + +/** + * Hashtable *with* collision management! + */ +static FC *defragmentationCache[DEFRAG_BUCKET_COUNT]; + +/** + * Lock for the defragmentation cache. + */ +static struct GNUNET_Mutex *defragCacheLock; + +static void +freeFL (FL * fl, int c) +{ + while (fl != NULL) + { + FL *link = fl->link; + if (stats != NULL) + stats->change (stat_discarded, c); + GNUNET_free (fl->frag); + GNUNET_free (fl); + fl = link; + } +} + +/** + * This cron job ensures that we purge buffers of fragments + * that have timed out. It can run in much longer intervals + * than the defragmentationCron, e.g. every 60s. + *

+ * This method goes through the hashtable, finds entries that + * have timed out and removes them (and all the fragments that + * belong to the entry). It's a bit more complicated as the + * collision list is also collapsed. + */ +static void +defragmentationPurgeCron (void *unused) +{ + int i; + FC *smf; + FC *next; + FC *last; + + GNUNET_mutex_lock (defragCacheLock); + for (i = 0; i < DEFRAG_BUCKET_COUNT; i++) + { + last = NULL; + smf = defragmentationCache[i]; + while (smf != NULL) + { + if (smf->ttl < GNUNET_get_time ()) + { + /* free linked list of fragments */ + freeFL (smf->head, 1); + next = smf->next; + GNUNET_free (smf); + if (last == NULL) + defragmentationCache[i] = next; + else + last->next = next; + smf = next; + } + else + { + last = smf; + smf = smf->next; + } + } /* while smf != NULL */ + } /* for all buckets */ + GNUNET_mutex_unlock (defragCacheLock); +} + +/** + * Check if this fragment-list is complete. If yes, put it together, + * process and free all buffers. Does not free the pep + * itself (but sets the TTL to 0 to have the cron free it + * in the next iteration). + * + * @param pep the entry in the GNUNET_hash table + */ +static void +checkComplete (FC * pep) +{ + FL *pos; + unsigned short off; + unsigned short len; + char *msg; + + GNUNET_GE_ASSERT (NULL, pep != NULL); + pos = pep->head; + if (pos == NULL) + return; + len = ntohs (pos->frag->len); + if (len == 0) + goto CLEANUP; /* really bad error! */ + off = 0; + while ((pos != NULL) && (ntohs (pos->frag->off) <= off)) + { + if (off >= off + FRAGSIZE (pos)) + goto CLEANUP; /* error! */ + if (ntohs (pos->frag->off) + FRAGSIZE (pos) > off) + off = ntohs (pos->frag->off) + FRAGSIZE (pos); + else + goto CLEANUP; /* error! */ + pos = pos->link; + } + if (off < len) + return; /* some fragment is still missing */ + + msg = GNUNET_malloc (len); + pos = pep->head; + while (pos != NULL) + { + memcpy (&msg[ntohs (pos->frag->off)], &pos->frag[1], FRAGSIZE (pos)); + pos = pos->link; + } + if (stats != NULL) + stats->change (stat_defragmented, 1); +#if 0 + printf ("Finished defragmentation!\n"); +#endif + /* handle message! */ + coreAPI->loopback_send (&pep->sender, msg, len, GNUNET_YES, NULL); + GNUNET_free (msg); +CLEANUP: + /* free fragment buffers */ + freeFL (pep->head, 0); + pep->head = NULL; + pep->ttl = 0; +} + +/** + * See if the new fragment is a part of this entry and join them if + * yes. Return GNUNET_SYSERR if the fragments do not match. Return GNUNET_OK if + * the fragments do match and the fragment has been processed. The + * defragCacheLock is already acquired by the caller whenever this + * method is called.

+ * + * @param entry the entry in the cache + * @param pep the new entry + * @param packet the ip part in the new entry + */ +static int +tryJoin (FC * entry, + const GNUNET_PeerIdentity * sender, + const P2P_fragmentation_MESSAGE * packet) +{ + /* frame before ours; may end in the middle of + our frame or before it starts; NULL if we are + the earliest position we have received so far */ + FL *before; + /* frame after ours; may start in the middle of + our frame or after it; NULL if we are the last + fragment we have received so far */ + FL *after; + /* current position in the frame-list */ + FL *pos; + /* the new entry that we're inserting */ + FL *pep; + FL *tmp; + unsigned short end; + + GNUNET_GE_ASSERT (NULL, entry != NULL); + if (0 != memcmp (sender, &entry->sender, sizeof (GNUNET_PeerIdentity))) + return GNUNET_SYSERR; /* wrong fragment list, try another! */ + if (ntohl (packet->id) != entry->id) + return GNUNET_SYSERR; /* wrong fragment list, try another! */ +#if 0 + printf ("Received fragment %u from %u to %u\n", + ntohl (packet->id), + ntohs (packet->off), + ntohs (packet->off) + ntohs (packet->header.size) - + sizeof (P2P_fragmentation_MESSAGE)); +#endif + pos = entry->head; + if ((pos != NULL) && (packet->len != pos->frag->len)) + return GNUNET_SYSERR; /* wrong fragment size */ + + before = NULL; + /* find the before-frame */ + while ((pos != NULL) && (ntohs (pos->frag->off) < ntohs (packet->off))) + { + before = pos; + pos = pos->link; + } + + /* find the after-frame */ + end = + ntohs (packet->off) + ntohs (packet->header.size) - + sizeof (P2P_fragmentation_MESSAGE); + if (end <= ntohs (packet->off)) + { + GNUNET_GE_LOG (NULL, + GNUNET_GE_DEVELOPER | GNUNET_GE_DEBUG | GNUNET_GE_BULK, + "Received invalid fragment at %s:%d\n", __FILE__, + __LINE__); + return GNUNET_SYSERR; /* yuck! integer overflow! */ + } + + if (before != NULL) + after = before; + else + after = entry->head; + while ((after != NULL) && (ntohs (after->frag->off) < end)) + after = after->link; + + if ((before != NULL) && (before == after)) + { + /* this implies after or before != NULL and thereby the new + fragment is redundant as it is fully enclosed in an earlier + fragment */ + if (stats != NULL) + stats->change (stat_defragmented, 1); + return GNUNET_OK; /* drop, there is a packet that spans our range! */ + } + + if ((before != NULL) && + (after != NULL) && + ((htons (before->frag->off) + + FRAGSIZE (before)) >= htons (after->frag->off))) + { + /* this implies that the fragment that starts before us and the + fragment that comes after this one leave no space in the middle + or even overlap; thus we can drop this redundant piece */ + if (stats != NULL) + stats->change (stat_defragmented, 1); + return GNUNET_OK; + } + + /* allocate pep */ + pep = GNUNET_malloc (sizeof (FC)); + pep->frag = GNUNET_malloc (ntohs (packet->header.size)); + memcpy (pep->frag, packet, ntohs (packet->header.size)); + pep->link = NULL; + + if (before == NULL) + { + pep->link = after; + pos = entry->head; + while (pos != after) + { + tmp = pos->link; + GNUNET_free (pos->frag); + GNUNET_free (pos); + pos = tmp; + } + entry->head = pep; + goto FINISH; + /* end of insert first */ + } + + if (after == NULL) + { + /* insert last: find the end, free everything after it */ + freeFL (before->link, 1); + before->link = pep; + goto FINISH; + } + + /* ok, we are filling the middle between two fragments; insert. If + there is anything else in the middle, it can be dropped as we're + bigger & cover that area as well */ + /* free everything between before and after */ + pos = before->link; + while (pos != after) + { + tmp = pos->link; + GNUNET_free (pos->frag); + GNUNET_free (pos); + pos = tmp; + } + before->link = pep; + pep->link = after; + +FINISH: + entry->ttl = GNUNET_get_time () + DEFRAGMENTATION_TIMEOUT; + checkComplete (entry); + return GNUNET_OK; +} + +/** + * Defragment the given fragment and pass to handler once + * defragmentation is complete. + * + * @param frag the packet to defragment + * @return GNUNET_SYSERR if the fragment is invalid + */ +static int +processFragment (const GNUNET_PeerIdentity * sender, + const GNUNET_MessageHeader * frag) +{ + unsigned int hash; + FC *smf; + + if (ntohs (frag->size) < sizeof (P2P_fragmentation_MESSAGE)) + return GNUNET_SYSERR; + + GNUNET_mutex_lock (defragCacheLock); + hash = sender->hashPubKey.bits[0] % DEFRAG_BUCKET_COUNT; + smf = defragmentationCache[hash]; + while (smf != NULL) + { + if (GNUNET_OK == + tryJoin (smf, sender, (P2P_fragmentation_MESSAGE *) frag)) + { + GNUNET_mutex_unlock (defragCacheLock); + return GNUNET_OK; + } + if (0 == memcmp (sender, &smf->sender, sizeof (GNUNET_PeerIdentity))) + { + freeFL (smf->head, 1); + break; + } + smf = smf->next; + } + if (smf == NULL) + { + smf = GNUNET_malloc (sizeof (FC)); + smf->next = defragmentationCache[hash]; + defragmentationCache[hash] = smf; + smf->ttl = GNUNET_get_time () + DEFRAGMENTATION_TIMEOUT; + smf->sender = *sender; + } + smf->id = ntohl (((P2P_fragmentation_MESSAGE *) frag)->id); + smf->head = GNUNET_malloc (sizeof (FL)); + smf->head->link = NULL; + smf->head->frag = GNUNET_malloc (ntohs (frag->size)); + memcpy (smf->head->frag, frag, ntohs (frag->size)); + + GNUNET_mutex_unlock (defragCacheLock); + return GNUNET_OK; +} + +typedef struct +{ + GNUNET_PeerIdentity sender; + /* maximums size of each fragment */ + unsigned short mtu; + /** how long is this message part expected to be? */ + unsigned short len; + /** when did we intend to transmit? */ + GNUNET_CronTime transmissionTime; +} FragmentBMC; + +/** + * Send a message that had to be fragmented (right now!). First grabs + * the first part of the message (obtained from ctx->se) and stores + * that in a P2P_fragmentation_MESSAGE envelope. The remaining fragments are + * added to the send queue with GNUNET_EXTREME_PRIORITY (to ensure that they + * will be transmitted next). The logic here is that if the priority + * for the first fragment was sufficiently high, the priority should + * also have been sufficiently high for all of the other fragments (at + * this time) since they have the same priority. And we want to make + * sure that we send all of them since just sending the first fragment + * and then going to other messages of equal priority would not be + * such a great idea (i.e. would just waste bandwidth). + */ +static int +fragmentBMC (void *buf, void *cls, unsigned short len) +{ + FragmentBMC *ctx = cls; + static int idGen = 0; + P2P_fragmentation_MESSAGE *frag; + unsigned int pos; + int id; + unsigned short mlen; + + if ((len < ctx->mtu) || (buf == NULL)) + { + GNUNET_free (ctx); + return GNUNET_SYSERR; + } + if (stats != NULL) + stats->change (stat_fragmented, 1); + id = (idGen++) + GNUNET_random_u32 (GNUNET_RANDOM_QUALITY_WEAK, 512); + /* write first fragment to buf */ + frag = (P2P_fragmentation_MESSAGE *) buf; + frag->header.size = htons (len); + frag->header.type = htons (GNUNET_P2P_PROTO_MESSAGE_FRAGMENT); + frag->id = id; + frag->off = htons (0); + frag->len = htons (ctx->len); + memcpy (&frag[1], &ctx[1], len - sizeof (P2P_fragmentation_MESSAGE)); + + /* create remaining fragments, add to queue! */ + pos = len - sizeof (P2P_fragmentation_MESSAGE); + frag = GNUNET_malloc (ctx->mtu); + while (pos < ctx->len) + { + mlen = sizeof (P2P_fragmentation_MESSAGE) + ctx->len - pos; + if (mlen > ctx->mtu) + mlen = ctx->mtu; + GNUNET_GE_ASSERT (NULL, mlen > sizeof (P2P_fragmentation_MESSAGE)); + frag->header.size = htons (mlen); + frag->header.type = htons (GNUNET_P2P_PROTO_MESSAGE_FRAGMENT); + frag->id = id; + frag->off = htons (pos); + frag->len = htons (ctx->len); + memcpy (&frag[1], + &((char *) (&ctx[1]))[pos], + mlen - sizeof (P2P_fragmentation_MESSAGE)); + coreAPI->ciphertext_send (&ctx->sender, + &frag->header, + GNUNET_EXTREME_PRIORITY, + ctx->transmissionTime - GNUNET_get_time ()); + pos += mlen - sizeof (P2P_fragmentation_MESSAGE); + } + GNUNET_GE_ASSERT (NULL, pos == ctx->len); + GNUNET_free (frag); + GNUNET_free (ctx); + return GNUNET_OK; +} + +/** + * The given message must be fragmented. Produce a placeholder that + * corresponds to the first fragment. Once that fragment is scheduled + * for transmission, the placeholder should automatically add all of + * the other fragments (with very high priority). + */ +void +fragment (const GNUNET_PeerIdentity * peer, + unsigned int mtu, + unsigned int prio, + unsigned int targetTime, + unsigned int len, GNUNET_BuildMessageCallback bmc, void *bmcClosure) +{ + FragmentBMC *fbmc; + int xlen; + + GNUNET_GE_ASSERT (NULL, len > mtu); + GNUNET_GE_ASSERT (NULL, mtu > sizeof (P2P_fragmentation_MESSAGE)); + fbmc = GNUNET_malloc (sizeof (FragmentBMC) + len); + fbmc->mtu = mtu; + fbmc->sender = *peer; + fbmc->transmissionTime = targetTime; + fbmc->len = len; + if (bmc == NULL) + { + memcpy (&fbmc[1], bmcClosure, len); + GNUNET_free (bmcClosure); + } + else + { + if (GNUNET_SYSERR == bmc (&fbmc[1], bmcClosure, len)) + { + GNUNET_free (fbmc); + return; + } + } + xlen = mtu - sizeof (P2P_fragmentation_MESSAGE); + coreAPI->ciphertext_send_with_callback (peer, &fragmentBMC, fbmc, mtu, prio * xlen / len, /* compute new priority */ + targetTime); +} + +/** + * Initialize Fragmentation module. + */ +GNUNET_Fragmentation_ServiceAPI * +provide_module_fragmentation (GNUNET_CoreAPIForPlugins * capi) +{ + static GNUNET_Fragmentation_ServiceAPI ret; + int i; + + coreAPI = capi; + stats = coreAPI->service_request ("stats"); + if (stats != NULL) + { + stat_defragmented = + stats->create (gettext_noop ("# messages defragmented")); + stat_fragmented = + stats->create (gettext_noop ("# messages fragmented")); + stat_discarded = stats->create (gettext_noop ("# fragments discarded")); + } + for (i = 0; i < DEFRAG_BUCKET_COUNT; i++) + defragmentationCache[i] = NULL; + defragCacheLock = GNUNET_mutex_create (GNUNET_NO); + GNUNET_cron_add_job (coreAPI->cron, + &defragmentationPurgeCron, + 60 * GNUNET_CRON_SECONDS, 60 * GNUNET_CRON_SECONDS, + NULL); + GNUNET_GE_LOG (capi->ectx, + GNUNET_GE_INFO | GNUNET_GE_USER | GNUNET_GE_REQUEST, + _("`%s' registering handler %d\n"), "fragmentation", + GNUNET_P2P_PROTO_MESSAGE_FRAGMENT); + capi->p2p_ciphertext_handler_register (GNUNET_P2P_PROTO_MESSAGE_FRAGMENT, + &processFragment); + + ret.fragment = &fragment; + return &ret; +} + +/** + * Shutdown fragmentation. + */ +void +release_module_fragmentation () +{ + int i; + + coreAPI->p2p_ciphertext_handler_unregister + (GNUNET_P2P_PROTO_MESSAGE_FRAGMENT, &processFragment); + GNUNET_cron_del_job (coreAPI->cron, &defragmentationPurgeCron, + 60 * GNUNET_CRON_SECONDS, NULL); + for (i = 0; i < DEFRAG_BUCKET_COUNT; i++) + { + FC *pos = defragmentationCache[i]; + while (pos != NULL) + { + FC *next = pos->next; + freeFL (pos->head, 1); + GNUNET_free (pos); + pos = next; + } + } + if (stats != NULL) + { + coreAPI->service_release (stats); + stats = NULL; + } + GNUNET_mutex_destroy (defragCacheLock); + defragCacheLock = NULL; + coreAPI = NULL; +} + +#endif + +/* end of fragmentation.c */ diff --git a/src/fragmentation/test_fragmentation.c b/src/fragmentation/test_fragmentation.c new file mode 100644 index 000000000..e6e3e5d22 --- /dev/null +++ b/src/fragmentation/test_fragmentation.c @@ -0,0 +1,439 @@ +/* + This file is part of GNUnet + (C) 2004, 2009 Christian Grothoff (and other contributing authors) + + GNUnet is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published + by the Free Software Foundation; either version 2, 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 + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with GNUnet; see the file COPYING. If not, write to the + Free Software Foundation, Inc., 59 Temple Place - Suite 330, + Boston, MA 02111-1307, USA. +*/ +/** + * @file fragmentation/test_fragmentation.c + * @brief test for fragmentation.c + * @author Christian Grothoff + */ + +/** + * Testcase for defragmentation code. + * We have testcases for: + * - 2 fragments, aligned, [0,16),[16,32) + * - n (50) fragments, [i*16,(i+1)*16) + * - n (50) fragments, [0,i*16) + [50*16,51*16) + * - n (100) fragments, inserted in interleaved order (holes in sequence) + * - holes in sequence + * - other overlaps + * - timeouts + * - multiple entries in GNUNET_hash-list + * - id collisions in GNUNET_hash-list + */ + +#include "platform.h" +#include "gnunet_fragmentation_lib.h" + +#if 0 + +/* -- to speed up the testcases -- */ +#define DEFRAGMENTATION_TIMEOUT (1 * GNUNET_CRON_SECONDS) + + +static GNUNET_PeerIdentity mySender; +static char *myMsg; +static unsigned short myMsgLen; + +/* static buffers to avoid lots of malloc/free */ +static char masterBuffer[65536]; +static char resultBuffer[65536]; + +static void +handleHelper (const GNUNET_PeerIdentity * sender, + const char *msg, + const unsigned int len, int wasEncrypted, GNUNET_TSession * ts) +{ + GNUNET_GE_ASSERT (NULL, + 0 == memcmp (sender, &mySender, + sizeof (GNUNET_PeerIdentity))); + myMsg = resultBuffer; + memcpy (resultBuffer, msg, len); + myMsgLen = len; +} + +/** + * Wait long enough to force all fragments to timeout. + */ +static void +makeTimeout () +{ + GNUNET_thread_sleep (DEFRAGMENTATION_TIMEOUT * 2); + defragmentationPurgeCron (NULL); +} + +/** + * Create a fragment. The data-portion will be filled + * with a sequence of numbers from start+id to start+len-1+id. + * + * @param pep pointer to the ethernet frame/buffer + * @param ip pointer to the ip-header + * @param start starting-offset + * @param length of the data portion + * @param id the identity of the fragment + */ +static GNUNET_MessageHeader * +makeFragment (unsigned short start, + unsigned short size, unsigned short tot, int id) +{ + P2P_fragmentation_MESSAGE *frag; + int i; + + frag = (P2P_fragmentation_MESSAGE *) masterBuffer; + frag->id = htonl (id); + frag->off = htons (start); + frag->len = htons (tot); + frag->header.size = htons (sizeof (P2P_fragmentation_MESSAGE) + size); + + for (i = 0; i < size; i++) + ((char *) &frag[1])[i] = (char) i + id + start; + return &frag->header; +} + +/** + * Check that the packet received is what we expected to + * get. + * @param id the expected id + * @param len the expected length + */ +static void +checkPacket (int id, unsigned int len) +{ + int i; + + GNUNET_GE_ASSERT (NULL, myMsg != NULL); + GNUNET_GE_ASSERT (NULL, myMsgLen == len); + for (i = 0; i < len; i++) + GNUNET_GE_ASSERT (NULL, myMsg[i] == (char) (i + id)); + myMsgLen = 0; + myMsg = NULL; +} + + +/* **************** actual testcases ***************** */ + +static void +testSimpleFragment () +{ + GNUNET_MessageHeader *pep; + + pep = makeFragment (0, 16, 32, 42); + processFragment (&mySender, pep); + GNUNET_GE_ASSERT (NULL, myMsg == NULL); + pep = makeFragment (16, 16, 32, 42); + processFragment (&mySender, pep); + checkPacket (42, 32); +} + +static void +testSimpleFragmentTimeout () +{ + GNUNET_MessageHeader *pep; + + pep = makeFragment (0, 16, 32, 42); + processFragment (&mySender, pep); + GNUNET_GE_ASSERT (NULL, myMsg == NULL); + makeTimeout (); + pep = makeFragment (16, 16, 32, 42); + processFragment (&mySender, pep); + GNUNET_GE_ASSERT (NULL, myMsg == NULL); + pep = makeFragment (0, 16, 32, 42); + processFragment (&mySender, pep); + checkPacket (42, 32); +} + +static void +testSimpleFragmentReverse () +{ + GNUNET_MessageHeader *pep; + + pep = makeFragment (16, 16, 32, 42); + processFragment (&mySender, pep); + GNUNET_GE_ASSERT (NULL, myMsg == NULL); + pep = makeFragment (0, 16, 32, 42); + processFragment (&mySender, pep); + checkPacket (42, 32); +} + +static void +testManyFragments () +{ + GNUNET_MessageHeader *pep; + int i; + + for (i = 0; i < 50; i++) + { + pep = makeFragment (i * 16, 16, 51 * 16, 42); + processFragment (&mySender, pep); + GNUNET_GE_ASSERT (NULL, myMsg == NULL); + } + pep = makeFragment (50 * 16, 16, 51 * 16, 42); + processFragment (&mySender, pep); + checkPacket (42, 51 * 16); +} + +static void +testManyFragmentsMegaLarge () +{ + GNUNET_MessageHeader *pep; + int i; + + for (i = 0; i < 4000; i++) + { + pep = makeFragment (i * 16, 16, 4001 * 16, 42); + processFragment (&mySender, pep); + GNUNET_GE_ASSERT (NULL, myMsg == NULL); + } + pep = makeFragment (4000 * 16, 16, 4001 * 16, 42); + processFragment (&mySender, pep); + checkPacket (42, 4001 * 16); +} + +static void +testLastFragmentEarly () +{ + GNUNET_MessageHeader *pep; + int i; + + for (i = 0; i < 5; i++) + { + pep = makeFragment (i * 16, 8, 6 * 16 + 8, 42); + processFragment (&mySender, pep); + GNUNET_GE_ASSERT (NULL, myMsg == NULL); + } + pep = makeFragment (5 * 16, 24, 6 * 16 + 8, 42); + processFragment (&mySender, pep); + for (i = 0; i < 5; i++) + { + pep = makeFragment (i * 16 + 8, 8, 6 * 16 + 8, 42); + processFragment (&mySender, pep); + } + checkPacket (42, 6 * 16 + 8); +} + +static void +testManyInterleavedFragments () +{ + GNUNET_MessageHeader *pep; + int i; + + for (i = 0; i < 50; i++) + { + pep = makeFragment (i * 16, 8, 51 * 16 + 8, 42); + processFragment (&mySender, pep); + GNUNET_GE_ASSERT (NULL, myMsg == NULL); + } + for (i = 0; i < 50; i++) + { + pep = makeFragment (i * 16 + 8, 8, 51 * 16 + 8, 42); + processFragment (&mySender, pep); + GNUNET_GE_ASSERT (NULL, myMsg == NULL); + } + pep = makeFragment (50 * 16, 24, 51 * 16 + 8, 42); + processFragment (&mySender, pep); + checkPacket (42, 51 * 16 + 8); +} + +static void +testManyInterleavedOverlappingFragments () +{ + GNUNET_MessageHeader *pep; + int i; + + for (i = 0; i < 50; i++) + { + pep = makeFragment (i * 32, 16, 51 * 32, 42); + processFragment (&mySender, pep); + GNUNET_GE_ASSERT (NULL, myMsg == NULL); + } + for (i = 0; i < 50; i++) + { + pep = makeFragment (i * 32 + 8, 24, 51 * 32, 42); + processFragment (&mySender, pep); + GNUNET_GE_ASSERT (NULL, myMsg == NULL); + } + pep = makeFragment (50 * 32, 32, 51 * 32, 42); + processFragment (&mySender, pep); + checkPacket (42, 51 * 32); +} + +static void +testManyOverlappingFragments () +{ + GNUNET_MessageHeader *pep; + int i; + + for (i = 0; i < 50; i++) + { + pep = makeFragment (0, i * 16 + 16, 51 * 16, 42); + processFragment (&mySender, pep); + GNUNET_GE_ASSERT (NULL, myMsg == NULL); + } + pep = makeFragment (50 * 16, 16, 51 * 16, 42); + processFragment (&mySender, pep); + checkPacket (42, 51 * 16); +} + +static void +testManyOverlappingFragmentsTimeout () +{ + GNUNET_MessageHeader *pep; + int i; + + for (i = 0; i < 50; i++) + { + pep = makeFragment (0, i * 16 + 16, 51 * 16 + 8, 42); + processFragment (&mySender, pep); + GNUNET_GE_ASSERT (NULL, myMsg == NULL); + } + makeTimeout (); + pep = makeFragment (50 * 16, 24, 51 * 16 + 8, 42); + processFragment (&mySender, pep); + GNUNET_GE_ASSERT (NULL, myMsg == NULL); + for (i = 0; i < 50; i++) + { + pep = makeFragment (0, i * 16 + 16, 51 * 16 + 8, 42); + processFragment (&mySender, pep); + } + checkPacket (42, 51 * 16 + 8); +} + +static void +testManyFragmentsMultiId () +{ + GNUNET_MessageHeader *pep; + int i; + int id; + + for (i = 0; i < 50; i++) + { + for (id = 0; id < DEFRAG_BUCKET_COUNT; id++) + { + pep = makeFragment (i * 16, 16, 51 * 16, id + 5); + mySender.hashPubKey.bits[0] = id; + processFragment (&mySender, pep); + GNUNET_GE_ASSERT (NULL, myMsg == NULL); + } + } + for (id = 0; id < DEFRAG_BUCKET_COUNT; id++) + { + pep = makeFragment (50 * 16, 16, 51 * 16, id + 5); + mySender.hashPubKey.bits[0] = id; + processFragment (&mySender, pep); + checkPacket (id + 5, 51 * 16); + } +} + +static void +testManyFragmentsMultiIdCollisions () +{ + GNUNET_MessageHeader *pep; + int i; + int id; + + for (i = 0; i < 5; i++) + { + for (id = 0; id < DEFRAG_BUCKET_COUNT * 4; id++) + { + pep = makeFragment (i * 16, 16, 6 * 16, id + 5); + mySender.hashPubKey.bits[0] = id; + processFragment (&mySender, pep); + GNUNET_GE_ASSERT (NULL, myMsg == NULL); + } + } + for (id = 0; id < DEFRAG_BUCKET_COUNT * 4; id++) + { + pep = makeFragment (5 * 16, 16, 6 * 16, id + 5); + mySender.hashPubKey.bits[0] = id; + processFragment (&mySender, pep); + checkPacket (id + 5, 6 * 16); + } +} + +/* ************* driver ****************** */ + +static int +p2p_register_handler (const unsigned short type, + GNUNET_P2PRequestHandler callback) +{ + return GNUNET_OK; +} + +static int +p2p_unregister_handler (const unsigned short type, + GNUNET_P2PRequestHandler callback) +{ + return GNUNET_OK; +} + + +static void * +request_service (const char *name) +{ + return NULL; +} + +#endif + +int +main (int argc, char *argv[]) +{ + fprintf (stderr, "WARNING: testcase not yet ported to new API.\n"); +#if 0 + GNUNET_CoreAPIForPlugins capi; + + memset (&capi, 0, sizeof (GNUNET_CoreAPIForPlugins)); + capi.cron = GNUNET_cron_create (NULL); + capi.loopback_send = &handleHelper; + capi.service_request = &request_service; + capi.p2p_ciphertext_handler_register = &p2p_register_handler; + capi.p2p_ciphertext_handler_unregister = &p2p_unregister_handler; + provide_module_fragmentation (&capi); + + fprintf (stderr, "."); + testSimpleFragment (); + fprintf (stderr, "."); + testSimpleFragmentTimeout (); + fprintf (stderr, "."); + testSimpleFragmentReverse (); + fprintf (stderr, "."); + testManyFragments (); + fprintf (stderr, "."); + testManyFragmentsMegaLarge (); + fprintf (stderr, "."); + testManyFragmentsMultiId (); + fprintf (stderr, "."); + + testManyInterleavedFragments (); + fprintf (stderr, "."); + testManyInterleavedOverlappingFragments (); + fprintf (stderr, "."); + testManyOverlappingFragments (); + fprintf (stderr, "."); + testManyOverlappingFragmentsTimeout (); + fprintf (stderr, "."); + testLastFragmentEarly (); + fprintf (stderr, "."); + testManyFragmentsMultiIdCollisions (); + fprintf (stderr, "."); + release_module_fragmentation (); + fprintf (stderr, "\n"); + GNUNET_cron_destroy (capi.cron); +#endif + return 0; /* testcase passed */ +} diff --git a/src/hello/Makefile.am b/src/hello/Makefile.am new file mode 100644 index 000000000..cffb7201c --- /dev/null +++ b/src/hello/Makefile.am @@ -0,0 +1,28 @@ +INCLUDES = -I$(top_srcdir)/src/include + +if MINGW + WINFLAGS = -Wl,--no-undefined -Wl,--export-all-symbols +endif + +if USE_COVERAGE + AM_CFLAGS = --coverage +endif + +lib_LTLIBRARIES = libgnunethello.la + +libgnunethello_la_SOURCES = \ + hello.c +libgnunethello_la_LIBADD = \ + $(top_builddir)/src/util/libgnunetutil.la + +check_PROGRAMS = \ + test_hello + +TESTS = $(check_PROGRAMS) + +test_hello_SOURCES = \ + test_hello.c +test_hello_LDADD = \ + $(top_builddir)/src/hello/libgnunethello.la \ + $(top_builddir)/src/util/libgnunetutil.la + diff --git a/src/hello/hello.c b/src/hello/hello.c new file mode 100644 index 000000000..5a21bd41f --- /dev/null +++ b/src/hello/hello.c @@ -0,0 +1,482 @@ +/* + This file is part of GNUnet. + (C) 2009 Christian Grothoff (and other contributing authors) + + GNUnet is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published + by the Free Software Foundation; either version 2, 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 + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with GNUnet; see the file COPYING. If not, write to the + Free Software Foundation, Inc., 59 Temple Place - Suite 330, + Boston, MA 02111-1307, USA. +*/ + +/** + * @file hello/hello.c + * @brief helper library for handling HELLOs + * @author Christian Grothoff + */ +#include "platform.h" +#include "gnunet_hello_lib.h" +#include "gnunet_protocols.h" +#include "gnunet_server_lib.h" + +/** + * A HELLO message is used to exchange information about + * transports with other peers. This struct is always + * followed by the actual network addresses which have + * the format: + * + * 1) transport-name (0-terminated) + * 2) address-length (uint32_t, network byte order; possibly + * unaligned!) + * 3) address expiration (GNUNET_TIME_AbsoluteNBO); possibly + * unaligned!) + * 4) address (address-length bytes; possibly unaligned!) + */ +struct GNUNET_HELLO_Message +{ + /** + * Type will be GNUNET_MESSAGE_TYPE_HELLO. + */ + struct GNUNET_MessageHeader header; + + /** + * Always zero (for alignment). + */ + uint32_t reserved GNUNET_PACKED; + + /** + * The public key of the peer. + */ + struct GNUNET_CRYPTO_RsaPublicKeyBinaryEncoded publicKey; + +}; + + +/** + * Copy the given address information into + * the given buffer using the format of HELLOs. + * + * @param tname name of the transport plugin + * @param expiration expiration for the address + * @param addr the address + * @param addr_len length of the address in bytes + * @param target where to copy the address + * @param max maximum number of bytes to copy to target + * @return number of bytes copied, 0 if + * the target buffer was not big enough. + */ +size_t +GNUNET_HELLO_add_address (const char *tname, + struct GNUNET_TIME_Absolute expiration, + const void *addr, + size_t addr_len, char *target, size_t max) +{ + uint32_t alen; + size_t slen; + struct GNUNET_TIME_AbsoluteNBO exp; + + slen = strlen (tname) + 1; + if (slen + sizeof (uint32_t) + sizeof (struct GNUNET_TIME_AbsoluteNBO) + + addr_len > max) + return 0; + exp = GNUNET_TIME_absolute_hton (expiration); + alen = htonl ((uint32_t) addr_len); + memcpy (target, tname, slen); + memcpy (&target[slen], &alen, sizeof (uint32_t)); + slen += sizeof (uint32_t); + memcpy (&target[slen], &exp, sizeof (struct GNUNET_TIME_AbsoluteNBO)); + slen += sizeof (struct GNUNET_TIME_AbsoluteNBO); + memcpy (&target[slen], addr, addr_len); + slen += addr_len; + return slen; +} + + +/** + * Get the size of an address entry in a HELLO message. + * + * @param buf pointer to the start of the address entry + * @param max maximum size of the entry (end of buf) + * @param ralen set to the address length + * @return size of the entry, or 0 if max is not large enough + */ +static size_t +get_hello_address_size (const char *buf, size_t max, uint32_t * ralen) +{ + const char *pos; + uint32_t alen; + size_t left; + size_t slen; + + left = max; + pos = buf; + slen = 1; + while ((left > 0) && ('\0' != *pos)) + { + left--; + pos++; + slen++; + } + if ('\0' != *pos) + { + /* 0-termination not found */ + GNUNET_break_op (0); + return 0; + } + pos++; + if (left < sizeof (uint32_t) + sizeof (struct GNUNET_TIME_AbsoluteNBO)) + { + /* not enough space for addrlen */ + GNUNET_break_op (0); + return 0; + } + memcpy (&alen, pos, sizeof (uint32_t)); + alen = ntohl (alen); + *ralen = alen; + slen += alen + sizeof (uint32_t) + sizeof (struct GNUNET_TIME_AbsoluteNBO); + if (max < slen) + { + /* not enough space for addr */ + GNUNET_break_op (0); + return 0; + } + return slen; +} + + +/** + * Construct a HELLO message given the public key, + * expiration time and an iterator that spews the + * transport addresses. + * + * @return the hello message + */ +struct GNUNET_HELLO_Message * +GNUNET_HELLO_create (const struct GNUNET_CRYPTO_RsaPublicKeyBinaryEncoded + *publicKey, + GNUNET_HELLO_GenerateAddressListCallback addrgen, + void *addrgen_cls) +{ + char buffer[GNUNET_SERVER_MAX_MESSAGE_SIZE - 256 - + sizeof (struct GNUNET_HELLO_Message)]; + size_t max; + size_t used; + size_t ret; + struct GNUNET_HELLO_Message *hello; + + max = sizeof (buffer); + used = 0; + while (0 != (ret = addrgen (addrgen_cls, max, &buffer[used]))) + { + max -= ret; + used += ret; + } + hello = GNUNET_malloc (sizeof (struct GNUNET_HELLO_Message) + used); + hello->header.type = htons (GNUNET_MESSAGE_TYPE_HELLO); + hello->header.size = htons (sizeof (struct GNUNET_HELLO_Message) + used); + memcpy (&hello->publicKey, publicKey, + sizeof (struct GNUNET_CRYPTO_RsaPublicKeyBinaryEncoded)); + memcpy (&hello[1], buffer, used); + return hello; +} + + +/** + * Iterate over all of the addresses in the HELLO. + * + * @param msg HELLO to iterate over + * @param return_modified if a modified copy should be returned, + * otherwise NULL will be returned + * @param it iterator to call on each address + * @param it_cls closure for it + */ +struct GNUNET_HELLO_Message * +GNUNET_HELLO_iterate_addresses (const struct GNUNET_HELLO_Message *msg, + int return_modified, + GNUNET_HELLO_AddressIterator it, void *it_cls) +{ + uint16_t msize; + struct GNUNET_HELLO_Message *ret; + const char *inptr; + size_t insize; + size_t esize; + size_t wpos; + char *woff; + uint32_t alen; + struct GNUNET_TIME_AbsoluteNBO expire; + int iret; + + msize = GNUNET_HELLO_size (msg); + if ((msize < sizeof (struct GNUNET_HELLO_Message)) || + (ntohs (msg->header.type) != GNUNET_MESSAGE_TYPE_HELLO)) + return NULL; + ret = NULL; + if (return_modified) + { + ret = GNUNET_malloc (msize); + memcpy (ret, msg, msize); + } + inptr = (const char *) &msg[1]; + insize = msize - sizeof (struct GNUNET_HELLO_Message); + wpos = 0; + woff = (ret != NULL) ? (char *) &ret[1] : NULL; + while (insize > 0) + { + esize = get_hello_address_size (inptr, insize, &alen); + if (esize == 0) + { + GNUNET_break (0); + GNUNET_free_non_null (ret); + return NULL; + } + memcpy (&expire, + &inptr[esize - alen - sizeof (struct GNUNET_TIME_AbsoluteNBO)], + sizeof (struct GNUNET_TIME_AbsoluteNBO)); + iret = it (it_cls, + inptr, + GNUNET_TIME_absolute_ntoh (expire), + &inptr[esize - alen], alen); + if (iret == GNUNET_SYSERR) + { + if (ret != NULL) + ret->header.size = + ntohs (sizeof (struct GNUNET_HELLO_Message) + wpos); + return ret; + } + if ((iret == GNUNET_OK) && (ret != NULL)) + { + memcpy (woff, inptr, esize); + woff += esize; + wpos += esize; + } + insize -= esize; + inptr += esize; + } + if (ret != NULL) + ret->header.size = ntohs (sizeof (struct GNUNET_HELLO_Message) + wpos); + return ret; +} + + +struct ExpireContext +{ + const void *addr; + size_t addrlen; + int found; + struct GNUNET_TIME_Absolute expiration; +}; + + +static int +get_match_exp (void *cls, + const char *tname, + struct GNUNET_TIME_Absolute expiration, + const void *addr, size_t addrlen) +{ + struct ExpireContext *ec = cls; + + if ((addrlen == ec->addrlen) && (0 == memcmp (addr, ec->addr, addrlen))) + { + ec->found = GNUNET_YES; + ec->expiration = expiration; + return GNUNET_SYSERR; /* done here */ + } + return GNUNET_OK; +} + + +struct MergeContext +{ + const struct GNUNET_HELLO_Message *h1; + const struct GNUNET_HELLO_Message *h2; + const struct GNUNET_HELLO_Message *other; + char *buf; + size_t max; + size_t ret; + int take_equal; + +}; + + +static int +copy_latest (void *cls, + const char *tname, + struct GNUNET_TIME_Absolute expiration, + const void *addr, size_t addrlen) +{ + struct MergeContext *mc = cls; + struct ExpireContext ec; + + ec.addr = addr; + ec.addrlen = addrlen; + ec.found = GNUNET_NO; + GNUNET_HELLO_iterate_addresses (mc->other, GNUNET_NO, &get_match_exp, &ec); + if ((ec.found == GNUNET_NO) || + ((ec.expiration.value < expiration.value) || + ((ec.expiration.value == expiration.value) && + (mc->take_equal == GNUNET_YES)))) + mc->ret += GNUNET_HELLO_add_address (tname, + expiration, + addr, + addrlen, + &mc->buf[mc->ret], + mc->max - mc->ret); + return GNUNET_OK; +} + + +static size_t +merge_addr (void *cls, size_t max, void *buf) +{ + struct MergeContext *mc = cls; + + if (mc->h1 == NULL) + return 0; + mc->ret = 0; + mc->max = max; + mc->buf = buf; + mc->take_equal = GNUNET_NO; + mc->other = mc->h2; + GNUNET_HELLO_iterate_addresses (mc->h1, GNUNET_NO, ©_latest, mc); + mc->take_equal = GNUNET_YES; + mc->other = mc->h1; + GNUNET_HELLO_iterate_addresses (mc->h2, GNUNET_NO, ©_latest, mc); + mc->h1 = NULL; + return mc->ret; +} + + +/** + * Construct a HELLO message by merging the + * addresses in two existing HELLOs (which + * must be for the same peer). + * + * @param h1 first HELLO message + * @param h2 the second HELLO message + * @return the combined hello message + */ +struct GNUNET_HELLO_Message * +GNUNET_HELLO_merge (const struct GNUNET_HELLO_Message *h1, + const struct GNUNET_HELLO_Message *h2) +{ + struct MergeContext mc = { h1, h2, NULL, NULL, 0, 0, 0 }; + + return GNUNET_HELLO_create (&h1->publicKey, &merge_addr, &mc); +} + + +struct DeltaContext +{ + struct GNUNET_TIME_Absolute expiration_limit; + + GNUNET_HELLO_AddressIterator it; + + void *it_cls; + + const struct GNUNET_HELLO_Message *old_hello; +}; + + +static int +delta_match (void *cls, + const char *tname, + struct GNUNET_TIME_Absolute expiration, + const void *addr, size_t addrlen) +{ + struct DeltaContext *dc = cls; + int ret; + struct ExpireContext ec; + + ec.addr = addr; + ec.addrlen = addrlen; + ec.found = GNUNET_NO; + GNUNET_HELLO_iterate_addresses (dc->old_hello, + GNUNET_NO, &get_match_exp, &ec); + if ((ec.found == GNUNET_YES) && + ((ec.expiration.value > expiration.value) || + (ec.expiration.value >= dc->expiration_limit.value))) + return GNUNET_YES; /* skip */ + ret = dc->it (dc->it_cls, tname, expiration, addr, addrlen); + return ret; +} + + +/** + * Iterate over addresses in "new_hello" that + * are NOT already present in "old_hello". + * + * @param new_hello a HELLO message + * @param old_hello a HELLO message + * @param expiration_limit ignore addresses in old_hello + * that expired before the given time stamp + * @param it iterator to call on each address + * @param it_cls closure for it + */ +void +GNUNET_HELLO_iterate_new_addresses (const struct GNUNET_HELLO_Message + *new_hello, + const struct GNUNET_HELLO_Message + *old_hello, + struct GNUNET_TIME_Absolute + expiration_limit, + GNUNET_HELLO_AddressIterator it, + void *it_cls) +{ + struct DeltaContext dc; + + dc.expiration_limit = expiration_limit; + dc.it = it; + dc.it_cls = it_cls; + dc.old_hello = old_hello; + GNUNET_HELLO_iterate_addresses (new_hello, GNUNET_NO, &delta_match, &dc); +} + + +/** + * Return the size of the given HELLO message. + * @param hello to inspect + * @return the size, 0 if HELLO is invalid + */ +uint16_t +GNUNET_HELLO_size (const struct GNUNET_HELLO_Message *hello) +{ + uint16_t ret = ntohs (hello->header.size); + if ((ret < sizeof (struct GNUNET_HELLO_Message)) || + (ntohs (hello->header.type) != GNUNET_MESSAGE_TYPE_HELLO)) + return 0; + return ret; +} + + +/** + * Get the public key from a HELLO message. + * + * @param hello the hello message + * @param publicKey where to copy the public key information, can be NULL + * @return GNUNET_SYSERR if the HELLO was malformed + */ +int +GNUNET_HELLO_get_key (const struct GNUNET_HELLO_Message *hello, + struct GNUNET_CRYPTO_RsaPublicKeyBinaryEncoded + *publicKey) +{ + uint16_t ret = ntohs (hello->header.size); + if ((ret < sizeof (struct GNUNET_HELLO_Message)) || + (ntohs (hello->header.type) != GNUNET_MESSAGE_TYPE_HELLO)) + return GNUNET_SYSERR; + *publicKey = hello->publicKey; + return GNUNET_OK; +} + + + +/* end of hello.c */ diff --git a/src/hello/test_hello.c b/src/hello/test_hello.c new file mode 100644 index 000000000..728afd63f --- /dev/null +++ b/src/hello/test_hello.c @@ -0,0 +1,185 @@ +/* + This file is part of GNUnet + (C) 2009 Christian Grothoff (and other contributing authors) + + GNUnet is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published + by the Free Software Foundation; either version 2, 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 + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with GNUnet; see the file COPYING. If not, write to the + Free Software Foundation, Inc., 59 Temple Place - Suite 330, + Boston, MA 02111-1307, USA. +*/ +/** + * @file hello/test_hello.c + * @brief test for hello.c + * @author Christian Grothoff + */ + +/** + * Testcase for HELLO code. + */ +#include "platform.h" +#include "gnunet_hello_lib.h" + +#define DEBUG 0 + +static size_t +my_addr_gen (void *cls, size_t max, void *buf) +{ + unsigned int *i = cls; + size_t ret; + +#if DEBUG + fprintf (stderr, "DEBUG: my_addr_gen called with i = %d\n", *i); +#endif + if (0 == *i) + return 0; + ret = GNUNET_HELLO_add_address ("test", + GNUNET_TIME_absolute_get (), + "address_information", *i, buf, max); + (*i)--; + return ret; +} + + +static int +check_addr (void *cls, + const char *tname, + struct GNUNET_TIME_Absolute expiration, + const void *addr, size_t addrlen) +{ + unsigned int *i = cls; + +#if DEBUG + fprintf (stderr, "DEBUG: check_addr called with i = %d and addrlen = %u\n", + *i, addrlen); +#endif + GNUNET_assert (addrlen > 0); + GNUNET_assert (*i & (1 << (addrlen - 1))); + *i -= (1 << (addrlen - 1)); + GNUNET_assert (0 == strncmp ("address_information", addr, addrlen)); + GNUNET_assert (0 == strcmp ("test", tname)); + return GNUNET_OK; +} + + +static int +remove_some (void *cls, + const char *tname, + struct GNUNET_TIME_Absolute expiration, + const void *addr, size_t addrlen) +{ + unsigned int *i = cls; + +#if DEBUG + fprintf (stderr, "DEBUG: remove_some called with i = %d and addrlen = %u\n", + *i, addrlen); +#endif + GNUNET_assert (addrlen > 0); + if (*i & (1 << (addrlen - 1))) + { + *i -= (1 << (addrlen - 1)); + return GNUNET_NO; + } + return GNUNET_OK; +} + + +int +main (int argc, char *argv[]) +{ + struct GNUNET_HELLO_Message *msg1; + struct GNUNET_HELLO_Message *msg2; + struct GNUNET_HELLO_Message *msg3; + struct GNUNET_CRYPTO_RsaPublicKeyBinaryEncoded publicKey; + struct GNUNET_CRYPTO_RsaPublicKeyBinaryEncoded pk; + struct GNUNET_TIME_Absolute startup_time; + int ok; + unsigned int i; + + GNUNET_log_setup ("test-hello", "DEBUG", NULL); + startup_time = GNUNET_TIME_absolute_get (); + ok = 0; + memset (&publicKey, 42, sizeof (publicKey)); + fprintf (stderr, "Testing HELLO creation (without addresses)...\n"); + i = 0; + msg1 = GNUNET_HELLO_create (&publicKey, &my_addr_gen, &i); + GNUNET_assert (msg1 != NULL); + GNUNET_assert (0 < GNUNET_HELLO_size (msg1)); + + fprintf (stderr, "Testing address iteration (empty set)...\n"); + GNUNET_assert (NULL == + GNUNET_HELLO_iterate_addresses (msg1, + GNUNET_NO, &check_addr, &i)); + + fprintf (stderr, "Testing HELLO creation (with one address)...\n"); + i = 1; + msg2 = GNUNET_HELLO_create (&publicKey, &my_addr_gen, &i); + GNUNET_assert (msg2 != NULL); + GNUNET_assert (GNUNET_HELLO_size (msg1) < GNUNET_HELLO_size (msg2)); + + fprintf (stderr, "Testing address iteration (one address)...\n"); + i = 1; + GNUNET_assert (NULL == + GNUNET_HELLO_iterate_addresses (msg2, + GNUNET_NO, &check_addr, &i)); + GNUNET_assert (i == 0); + + fprintf (stderr, "Testing get_key from HELLO...\n"); + GNUNET_assert (GNUNET_OK == GNUNET_HELLO_get_key (msg2, &pk)); + GNUNET_assert (0 == memcmp (&publicKey, &pk, sizeof (pk))); + GNUNET_free (msg1); + + fprintf (stderr, "Testing HELLO creation (with two addresses)...\n"); + i = 2; + msg3 = GNUNET_HELLO_create (&publicKey, &my_addr_gen, &i); + GNUNET_assert (msg3 != NULL); + GNUNET_assert (GNUNET_HELLO_size (msg2) < GNUNET_HELLO_size (msg3)); + + fprintf (stderr, "Testing address iteration (two addresses)...\n"); + i = 3; + GNUNET_assert (NULL == + GNUNET_HELLO_iterate_addresses (msg3, + GNUNET_NO, &check_addr, &i)); + GNUNET_assert (i == 0); + + fprintf (stderr, "Testing HELLO merge...\n"); + msg1 = GNUNET_HELLO_merge (msg2, msg3); + GNUNET_assert (GNUNET_HELLO_size (msg1) == GNUNET_HELLO_size (msg3)); + + i = 3; + GNUNET_assert (NULL == + GNUNET_HELLO_iterate_addresses (msg1, + GNUNET_NO, &check_addr, &i)); + GNUNET_assert (i == 0); + GNUNET_free (msg1); + + fprintf (stderr, "Testing address iteration to copy HELLO...\n"); + i = 2; + msg1 = GNUNET_HELLO_iterate_addresses (msg3, GNUNET_YES, &remove_some, &i); + GNUNET_assert (msg1 != NULL); + GNUNET_assert (i == 0); + i = 1; + GNUNET_assert (NULL == + GNUNET_HELLO_iterate_addresses (msg1, + GNUNET_NO, &check_addr, &i)); + GNUNET_assert (i == 0); + GNUNET_free (msg1); + + fprintf (stderr, "Testing delta address iteration...\n"); + i = 2; + GNUNET_HELLO_iterate_new_addresses (msg3, + msg2, startup_time, &check_addr, &i); + GNUNET_assert (i == 0); + GNUNET_free (msg2); + GNUNET_free (msg3); + return 0; /* testcase passed */ +} diff --git a/src/include/Makefile.am b/src/include/Makefile.am new file mode 100644 index 000000000..8c49690e7 --- /dev/null +++ b/src/include/Makefile.am @@ -0,0 +1,42 @@ +SUBDIRS = . + +EXTRA_DIST = \ + gettext.h \ + platform.h \ + plibc.h \ + winproc.h + +gnunetincludedir = $(includedir)/GNUnet + +nodist_gnunetinclude_HEADERS = \ + gnunet_directories.h + +gnunetinclude_HEADERS = \ + gnunet_arm_service.h \ + gnunet_client_lib.h \ + gnunet_common.h \ + gnunet_configuration_lib.h \ + gnunet_container_lib.h \ + gnunet_core_service.h \ + gnunet_crypto_lib.h \ + gnunet_disk_lib.h \ + gnunet_fragmentation_lib.h \ + gnunet_getopt_lib.h \ + gnunet_hello_lib.h \ + gnunet_network_lib.h \ + gnunet_peerinfo_service.h \ + gnunet_program_lib.h \ + gnunet_protocols.h \ + gnunet_pseudonym_lib.h \ + gnunet_resolver_service.h \ + gnunet_scheduler_lib.h \ + gnunet_server_lib.h \ + gnunet_service_lib.h \ + gnunet_signal_lib.h \ + gnunet_signatures.h \ + gnunet_statistics_service.h \ + gnunet_strings_lib.h \ + gnunet_time_lib.h \ + gnunet_transport_service.h \ + gnunet_upnp_service.h \ + gnunet_util_lib.h diff --git a/src/include/gettext.h b/src/include/gettext.h new file mode 100644 index 000000000..c89197cfc --- /dev/null +++ b/src/include/gettext.h @@ -0,0 +1,71 @@ +/* Convenience header for conditional use of GNU . + Copyright (C) 1995-1998, 2000-2002 Free Software Foundation, Inc. + + This program is free software; you can redistribute it and/or modify it + under the terms of the GNU Library General Public License as published + by the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program 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 + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, + USA. */ + +#ifndef _LIBGETTEXT_H +#define _LIBGETTEXT_H 1 + +/* NLS can be disabled through the configure --disable-nls option. */ +#if ENABLE_NLS + +/* Get declarations of GNU message catalog functions. */ +# include + +#else + +/* Solaris /usr/include/locale.h includes /usr/include/libintl.h, which + chokes if dcgettext is defined as a macro. So include it now, to make + later inclusions of a NOP. We don't include + as well because people using "gettext.h" will not include , + and also including would fail on SunOS 4, whereas + is GNUNET_OK. */ +#if defined(__sun) +# include +#endif + +/* Disabled NLS. + The casts to 'const char *' serve the purpose of producing warnings + for invalid uses of the value returned from these functions. + On pre-ANSI systems without 'const', the config.h file is supposed to + contain "#define const". */ +# define gettext(Msgid) ((const char *) (Msgid)) +# define dgettext(Domainname, Msgid) ((const char *) (Msgid)) +# define dcgettext(Domainname, Msgid, Category) ((const char *) (Msgid)) +# define ngettext(Msgid1, Msgid2, N) \ + ((N) == 1 ? (const char *) (Msgid1) : (const char *) (Msgid2)) +# define dngettext(Domainname, Msgid1, Msgid2, N) \ + ((N) == 1 ? (const char *) (Msgid1) : (const char *) (Msgid2)) +# define dcngettext(Domainname, Msgid1, Msgid2, N, Category) \ + ((N) == 1 ? (const char *) (Msgid1) : (const char *) (Msgid2)) +/* slight modification here to avoid warnings: generate GNUNET_NO code, + not even the cast... */ +# define textdomain(Domainname) +# define bindtextdomain(Domainname, Dirname) +# define bind_textdomain_codeset(Domainname, Codeset) ((const char *) (Codeset)) + +#endif + +/* A pseudo function call that serves as a marker for the automated + extraction of messages, but does not call gettext(). The run-time + translation is done at a different place in the code. + The argument, String, should be a literal string. Concatenated strings + and other string expressions won't work. + The macro's expansion is not parenthesized, so that it is suitable as + initializer for static 'char[]' or 'const char[]' variables. */ +#define gettext_noop(String) String + +#endif /* _LIBGETTEXT_H */ diff --git a/src/include/gnunet_arm_service.h b/src/include/gnunet_arm_service.h new file mode 100644 index 000000000..012c27877 --- /dev/null +++ b/src/include/gnunet_arm_service.h @@ -0,0 +1,107 @@ +/* + This file is part of GNUnet + (C) 2009 Christian Grothoff (and other contributing authors) + + GNUnet is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published + by the Free Software Foundation; either version 2, 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 + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with GNUnet; see the file COPYING. If not, write to the + Free Software Foundation, Inc., 59 Temple Place - Suite 330, + Boston, MA 02111-1307, USA. + */ + +/** + * @file include/gnunet_arm_service.h + * @brief API to access gnunet-arm + * @author Christian Grothoff + */ + +#ifndef GNUNET_ARM_SERVICE_H +#define GNUNET_ARM_SERVICE_H + +#ifdef __cplusplus +extern "C" +{ +#if 0 /* keep Emacsens' auto-indent happy */ +} +#endif +#endif + +#include "gnunet_configuration_lib.h" +#include "gnunet_scheduler_lib.h" +#include "gnunet_time_lib.h" + +/** + * Version of the arm API. + */ +#define GNUNET_ARM_VERSION 0x00000000 + + +/** + * Callback function invoked when operation is complete. + * + * @param cls closure + * @param success GNUNET_YES if we think the service is running + * GNUNET_NO if we think the service is stopped + * GNUNET_SYSERR if we think ARM was not running + */ +typedef void (*GNUNET_ARM_Callback) (void *cls, int success); + + +/** + * Start a service. + * + * @param service_name name of the service + * @param cfg configuration to use (needed to contact ARM; + * the ARM service may internally use a different + * configuration to determine how to start the service). + * @param sched scheduler to use + * @param timeout how long to wait before failing for good + * @param cb callback to invoke when service is ready + * @param cb_cls closure for callback + */ +void +GNUNET_ARM_start_service (const char *service_name, + struct GNUNET_CONFIGURATION_Handle *cfg, + struct GNUNET_SCHEDULER_Handle *sched, + struct GNUNET_TIME_Relative timeout, + GNUNET_ARM_Callback cb, void *cb_cls); + + +/** + * Stop a service. + * + * @param service_name name of the service + * @param cfg configuration to use (needed to contact ARM; + * the ARM service may internally use a different + * configuration to determine how to start the service). + * @param sched scheduler to use + * @param timeout how long to wait before failing for good + * @param cb callback to invoke when service is ready + * @param cb_cls closure for callback + */ +void +GNUNET_ARM_stop_service (const char *service_name, + struct GNUNET_CONFIGURATION_Handle *cfg, + struct GNUNET_SCHEDULER_Handle *sched, + struct GNUNET_TIME_Relative timeout, + GNUNET_ARM_Callback cb, void *cb_cls); + + + +#if 0 /* keep Emacsens' auto-indent happy */ +{ +#endif +#ifdef __cplusplus +} +#endif + +#endif diff --git a/src/include/gnunet_client_lib.h b/src/include/gnunet_client_lib.h new file mode 100644 index 000000000..6a77aa391 --- /dev/null +++ b/src/include/gnunet_client_lib.h @@ -0,0 +1,160 @@ +/* + This file is part of GNUnet. + (C) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2009 Christian Grothoff (and other contributing authors) + + GNUnet is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published + by the Free Software Foundation; either version 2, 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 + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with GNUnet; see the file COPYING. If not, write to the + Free Software Foundation, Inc., 59 Temple Place - Suite 330, + Boston, MA 02111-1307, USA. +*/ + +/** + * @file include/gnunet_client_lib.h + * @brief functions related to accessing services + * @author Christian Grothoff + */ + +#ifndef GNUNET_CLIENT_LIB_H +#define GNUNET_CLIENT_LIB_H + +#ifdef __cplusplus +extern "C" +{ +#if 0 /* keep Emacsens' auto-indent happy */ +} +#endif +#endif + +#include "gnunet_common.h" +#include "gnunet_configuration_lib.h" +#include "gnunet_network_lib.h" +#include "gnunet_scheduler_lib.h" +#include "gnunet_time_lib.h" + +/** + * Opaque handle for a connection to a service. + */ +struct GNUNET_CLIENT_Connection; + +/** + * Get a connection with a service. + * + * @param sched scheduler to use + * @param service_name name of the service + * @param cfg configuration to use + * @return NULL on error (service unknown to configuration) + */ +struct GNUNET_CLIENT_Connection *GNUNET_CLIENT_connect (struct + GNUNET_SCHEDULER_Handle + *sched, + const char + *service_name, + struct + GNUNET_CONFIGURATION_Handle + *cfg); + +/** + * Destroy connection with the service. This will + * automatically cancel any pending "receive" request + * (however, the handler will *NOT* be called, not + * even with a NULL message). + */ +void GNUNET_CLIENT_disconnect (struct GNUNET_CLIENT_Connection *sock); + +/** + * Type of a function to call when we receive a message + * from the service. + * + * @param cls closure + * @param msg message received, NULL on timeout or fatal error + */ +typedef void (*GNUNET_CLIENT_MessageHandler) (void *cls, + const struct + GNUNET_MessageHeader * msg); + +/** + * Read from the service. + * + * @param sched scheduler to use + * @param sock the service + * @param handler function to call with the message + * @param cls closure for handler + * @param timeout how long to wait until timing out + */ +void GNUNET_CLIENT_receive (struct GNUNET_CLIENT_Connection *sock, + GNUNET_CLIENT_MessageHandler handler, + void *cls, struct GNUNET_TIME_Relative timeout); + + +/** + * Ask the client to call us once the specified number of bytes + * are free in the transmission buffer. May call the notify + * method immediately if enough space is available. + * + * @param client connection to the service + * @param size number of bytes to send + * @param timeout after how long should we give up (and call + * notify with buf NULL and size 0)? + * @param notify function to call + * @param notify_cls closure for notify + * @return NULL if someone else is already waiting to be notified + * non-NULL if the notify callback was queued (can be used to cancel + * using GNUNET_NETWORK_notify_transmit_ready_cancel) + */ +struct GNUNET_NETWORK_TransmitHandle + *GNUNET_CLIENT_notify_transmit_ready (struct GNUNET_CLIENT_Connection *sock, + size_t size, + struct GNUNET_TIME_Relative timeout, + GNUNET_NETWORK_TransmitReadyNotify + notify, void *notify_cls); + + +/** + * Request that the service should shutdown. + * Afterwards, the connection should be disconnected. + * + * @param sched scheduler to use + * @param sock the socket connected to the service + */ +void GNUNET_CLIENT_service_shutdown (struct GNUNET_CLIENT_Connection *sock); + + +/** + * Wait until the service is running. + * + * @param sched scheduler to use + * @param service name of the service to wait for + * @param cfg configuration to use + * @param timeout how long to wait at most in ms + * @param task task to run if service is running + * (reason will be "PREREQ_DONE" (service running) + * or "TIMEOUT" (service not known to be running)) + * @param task_cls closure for task + */ +void GNUNET_CLIENT_service_test (struct GNUNET_SCHEDULER_Handle *sched, + const char *service, + struct GNUNET_CONFIGURATION_Handle *cfg, + struct GNUNET_TIME_Relative timeout, + GNUNET_SCHEDULER_Task task, void *task_cls); + + +#if 0 /* keep Emacsens' auto-indent happy */ +{ +#endif +#ifdef __cplusplus +} +#endif + +/* ifndef GNUNET_CLIENT_LIB_H */ +#endif +/* end of gnunet_client_lib.h */ diff --git a/src/include/gnunet_common.h b/src/include/gnunet_common.h new file mode 100644 index 000000000..61b572eb8 --- /dev/null +++ b/src/include/gnunet_common.h @@ -0,0 +1,469 @@ +/* + This file is part of GNUnet. + (C) 2006 Christian Grothoff (and other contributing authors) + + GNUnet is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published + by the Free Software Foundation; either version 2, 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 + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with GNUnet; see the file COPYING. If not, write to the + Free Software Foundation, Inc., 59 Temple Place - Suite 330, + Boston, MA 02111-1307, USA. +*/ + +/** + * @file include/gnunet_common.h + * @brief commonly used definitions; globals in this file + * are exempt from the rule that the module name ("common") + * must be part of the symbol name. + * + * @author Christian Grothoff + * @author Nils Durner + */ +#ifndef GNUNET_COMMON_H +#define GNUNET_COMMON_H + +/** + * Version of the API (for entire gnunetutil.so library). + */ +#define GNUNET_UTIL_VERSION 0x00000000 + +/** + * Name used for "services" that are actually command-line + * programs invoked by the end user. + */ +#define GNUNET_CLIENT_SERVICE_NAME "client" + +/** + * Named constants for return values. The following + * invariants hold: "GNUNET_NO == 0" (to allow "if (GNUNET_NO)") + * "GNUNET_OK != GNUNET_SYSERR", "GNUNET_OK != GNUNET_NO", "GNUNET_NO != GNUNET_SYSERR" + * and finally "GNUNET_YES != GNUNET_NO". + */ +#define GNUNET_OK 1 +#define GNUNET_SYSERR -1 +#define GNUNET_YES 1 +#define GNUNET_NO 0 + +#define GNUNET_MIN(a,b) (((a) < (b)) ? (a) : (b)) + +#define GNUNET_MAX(a,b) (((a) > (b)) ? (a) : (b)) + +/** + * gcc-ism to get packed structs. + */ +#define GNUNET_PACKED __attribute__((packed)) + + +/* ************************ super-general types *********************** */ + +/** + * Header for all communications. + */ +struct GNUNET_MessageHeader +{ + + /** + * The length of the struct (in bytes, including the length field itself) + */ + uint16_t size GNUNET_PACKED; + + /** + * The type of the message (XX_CS_PROTO_XXXX) + */ + uint16_t type GNUNET_PACKED; + +}; + + +/** + * @brief 512-bit hashcode + */ +typedef struct +{ + uint32_t bits[512 / 8 / sizeof (uint32_t)]; /* = 16 */ +} +GNUNET_HashCode; + + +/** + * The identity of the host (basically the SHA-512 hashcode of + * it's public key). + */ +struct GNUNET_PeerIdentity +{ + GNUNET_HashCode hashPubKey GNUNET_PACKED; +}; + + +/** + * Function called with a filename. + * + * @param filename complete filename (absolute path) + * @param cls closure + * @return GNUNET_OK to continue to iterate, + * GNUNET_SYSERR to abort iteration with error! + */ +typedef int (*GNUNET_FileNameCallback) (void *cls, const char *filename); + + +/* ****************************** logging ***************************** */ + +/** + * Types of errors. + */ +enum GNUNET_ErrorType +{ + GNUNET_ERROR_TYPE_ERROR = 1, + GNUNET_ERROR_TYPE_WARNING = 2, + GNUNET_ERROR_TYPE_INFO = 4, + GNUNET_ERROR_TYPE_DEBUG = 8, + GNUNET_ERROR_TYPE_INVALID = 16, + GNUNET_ERROR_TYPE_BULK = 32 +}; + +/** + * User-defined handler for log messages. + * + * @param cls closure + * @param kind severeity + * @param component what component is issuing the message? + * @param date when was the message logged? + * @param message what is the message + */ +typedef void (*GNUNET_Logger) (void *cls, + enum GNUNET_ErrorType kind, + const char *component, + const char *date, const char *message); + +/** + * Main log function. + * + * @param kind how serious is the error? + * @param message what is the message (format string) + * @param ... arguments for format string + */ +void GNUNET_log (enum GNUNET_ErrorType kind, const char *message, ...); + + + +/** + * Log function that specifies an alternative component. + * This function should be used by plugins. + * + * @param kind how serious is the error? + * @param comp component responsible for generating the message + * @param message what is the message (format string) + * @param ... arguments for format string + */ +void +GNUNET_log_from (enum GNUNET_ErrorType kind, + const char *comp, const char *message, ...); + + +/** + * Ignore the next n calls to the log function. + * + * @param n number of log calls to ignore, use 0 to + * assert that the log skip counter is currently zero. + */ +void GNUNET_log_skip (unsigned int n); + + +/** + * Setup logging. + * + * @param component default component to use + * @param loglevel what types of messages should be logged + * @param logfile change logging to logfile (use NULL to keep stderr) + * @return GNUNET_OK on success, GNUNET_SYSERR if logfile could not be opened + */ +int +GNUNET_log_setup (const char *component, + const char *loglevel, const char *logfile); + +/** + * Add a custom logger. + * + * @param logger log function + * @param logger_cls closure for logger + */ +void GNUNET_logger_add (GNUNET_Logger logger, void *logger_cls); + +/** + * Remove a custom logger. + * + * @param logger log function + * @param logger_cls closure for logger + */ +void GNUNET_logger_remove (GNUNET_Logger logger, void *logger_cls); + + +/** + * Convert a peer identity to a string (for printing debug messages). + * This is one of the very few calls in the entire API that is + * NOT reentrant! + * + * @param pid the peer identity + * @return string form of the pid; will be overwritten by next + * call to GNUNET_i2s. + */ +const char *GNUNET_i2s (const struct GNUNET_PeerIdentity *pid); + +/** + * Convert error type to string. + * + * @param kind type to convert + * @return string corresponding to the type + */ +const char *GNUNET_error_type_to_string (enum GNUNET_ErrorType kind); + +/** + * Use this for fatal errors that cannot be handled + */ +#define GNUNET_assert(cond) do { if (! (cond)) { GNUNET_log(GNUNET_ERROR_TYPE_ERROR, _("Assertion failed at %s:%d.\n"), __FILE__, __LINE__); abort(); } } while(0) + +/** + * Use this for fatal errors that cannot be handled + */ +#define GNUNET_assert_at(cond, f, l) do { if (! (cond)) { GNUNET_log(GNUNET_ERROR_TYPE_ERROR, _("Assertion failed at %s:%d.\n"), f, l); abort(); } } while(0) + +/** + * Use this for internal assertion violations that are + * not fatal (can be handled) but should not occur. + */ +#define GNUNET_break(cond) do { if (! (cond)) { GNUNET_log(GNUNET_ERROR_TYPE_ERROR, _("Assertion failed at %s:%d.\n"), __FILE__, __LINE__); } } while(0) + +/** + * Use this for assertion violations caused by other + * peers (i.e. protocol violations). We do not want to + * confuse end-users (say, some other peer runs an + * older, broken or incompatible GNUnet version), but + * we still want to see these problems during + * development and testing. "OP == other peer". + */ +#define GNUNET_break_op(cond) do { if (! (cond)) { GNUNET_log(GNUNET_ERROR_TYPE_WARNING, _("External protocol violation detected at %s:%d.\n"), __FILE__, __LINE__); } } while(0) + +/** + * Log an error message at log-level 'level' that indicates + * a failure of the command 'cmd' with the message given + * by strerror(errno). + */ +#define GNUNET_log_strerror(level, cmd) do { GNUNET_log(level, _("`%s' failed at %s:%d with error: %s\n"), cmd, __FILE__, __LINE__, STRERROR(errno)); } while(0) + +/** + * Log an error message at log-level 'level' that indicates + * a failure of the command 'cmd' with the message given + * by strerror(errno). + */ +#define GNUNET_log_strerror_file(level, cmd, filename) do { GNUNET_log(level, _("`%s' failed on file `%s' at %s:%d with error: %s\n"), cmd, filename,__FILE__, __LINE__, STRERROR(errno)); } while(0) + +/* ************************* endianess conversion ****************** */ + +/** + * Convert a long-long to host-byte-order. + * @param n the value in network byte order + * @return the same value in host byte order + */ +unsigned long long GNUNET_ntohll (unsigned long long n); + +/** + * Convert a long long to network-byte-order. + * @param n the value in host byte order + * @return the same value in network byte order + */ +unsigned long long GNUNET_htonll (unsigned long long n); + + +/* ************************* allocation functions ****************** */ + +/** + * Maximum allocation with GNUNET_malloc macro. + */ +#define GNUNET_MAX_GNUNET_MALLOC_CHECKED (1024 * 1024 * 40) + +/** + * Wrapper around malloc. Allocates size bytes of memory. + * The memory will be zero'ed out. + * + * @param size the number of bytes to allocate, must be + * smaller than 40 MB. + * @return pointer to size bytes of memory + */ +#define GNUNET_malloc(size) GNUNET_xmalloc_(size, __FILE__, __LINE__) + +/** + * Wrapper around malloc. Allocates size bytes of memory. + * The memory will be zero'ed out. + * + * @param size the number of bytes to allocate + * @return pointer to size bytes of memory + */ +#define GNUNET_malloc_large(size) GNUNET_xmalloc_unchecked_(size, __FILE__, __LINE__) + +/** + * Wrapper around realloc. Rellocates size bytes of memory. + * + * @param ptr the pointer to reallocate + * @param size the number of bytes to reallocate + * @return pointer to size bytes of memory + */ +#define GNUNET_realloc(ptr, size) GNUNET_xrealloc_(ptr, size, __FILE__, __LINE__) + +/** + * Wrapper around free. Frees the memory referred to by ptr. + * Note that is is generally better to free memory that was + * allocated with GNUNET_array_grow using GNUNET_array_grow(mem, size, 0) instead of GNUNET_free. + * + * @param ptr location where to free the memory. ptr must have + * been returned by GNUNET_strdup, GNUNET_malloc or GNUNET_array_grow earlier. + */ +#define GNUNET_free(ptr) GNUNET_xfree_(ptr, __FILE__, __LINE__) + +/** + * Free the memory pointed to by ptr if ptr is not NULL. + * Equivalent to if (ptr!=null)GNUNET_free(ptr). + * + * @param ptr the location in memory to free + */ +#define GNUNET_free_non_null(ptr) do { void * __x__ = ptr; if (__x__ != NULL) { GNUNET_free(__x__); } } while(0) + +/** + * Wrapper around GNUNET_strdup. Makes a copy of the zero-terminated string + * pointed to by a. + * + * @param a pointer to a zero-terminated string + * @return a copy of the string including zero-termination + */ +#define GNUNET_strdup(a) GNUNET_xstrdup_(a,__FILE__,__LINE__) + +/** + * Grow a well-typed (!) array. This is a convenience + * method to grow a vector arr of size size + * to the new (target) size tsize. + *

+ * + * Example (simple, well-typed stack): + * + *

+ * static struct foo * myVector = NULL;
+ * static int myVecLen = 0;
+ *
+ * static void push(struct foo * elem) {
+ *   GNUNET_array_grow(myVector, myVecLen, myVecLen+1);
+ *   memcpy(&myVector[myVecLen-1], elem, sizeof(struct foo));
+ * }
+ *
+ * static void pop(struct foo * elem) {
+ *   if (myVecLen == 0) die();
+ *   memcpy(elem, myVector[myVecLen-1], sizeof(struct foo));
+ *   GNUNET_array_grow(myVector, myVecLen, myVecLen-1);
+ * }
+ * 
+ * + * @param arr base-pointer of the vector, may be NULL if size is 0; + * will be updated to reflect the new address. The TYPE of + * arr is important since size is the number of elements and + * not the size in bytes + * @param size the number of elements in the existing vector (number + * of elements to copy over) + * @param tsize the target size for the resulting vector, use 0 to + * free the vector (then, arr will be NULL afterwards). + */ +#define GNUNET_array_grow(arr,size,tsize) GNUNET_xgrow_((void**)&arr, sizeof(arr[0]), &size, tsize, __FILE__, __LINE__) + +/** + * Append an element to a list (growing the + * list by one). + */ +#define GNUNET_array_append(arr,size,element) do { GNUNET_array_grow(arr,size,size+1); arr[size-1] = element; } while(0) + +/** + * Like snprintf, just aborts if the buffer is of insufficient size. + */ +int GNUNET_snprintf (char *buf, size_t size, const char *format, ...); + +/** + * Like asprintf, just portable. + */ +int GNUNET_asprintf (char **buf, const char *format, ...); + + +/* ************** internal implementations, use macros above! ************** */ + +/** + * Allocate memory. Checks the return value, aborts if no more + * memory is available. Don't use GNUNET_xmalloc_ directly. Use the + * GNUNET_malloc macro. + * The memory will be zero'ed out. + */ +void *GNUNET_xmalloc_ (size_t size, const char *filename, int linenumber); + +/** + * Allocate memory. This function does not check if the + * allocation request is within reasonable bounds, allowing + * allocations larger than 40 MB. If you don't expect the + * possibility of very large allocations, use GNUNET_malloc instead. + * The memory will be zero'ed out. + */ +void *GNUNET_xmalloc_unchecked_ (size_t size, + const char *filename, int linenumber); + +/** + * Reallocate memory. Checks the return value, aborts if no more + * memory is available. + */ +void *GNUNET_xrealloc_ (void *ptr, + const size_t n, const char *filename, int linenumber); + +/** + * Free memory. Merely a wrapper for the case that we + * want to keep track of allocations. Don't use GNUNET_xfree_ + * directly. Use the GNUNET_free macro. + */ +void GNUNET_xfree_ (void *ptr, const char *filename, int linenumber); + + +/** + * Dup a string. Don't call GNUNET_xstrdup_ directly. Use the GNUNET_strdup macro. + */ +char *GNUNET_xstrdup_ (const char *str, const char *filename, int linenumber); + +/** + * Grow an array, the new elements are zeroed out. + * Grows old by (*oldCount-newCount)*elementSize + * bytes and sets *oldCount to newCount. + * + * Don't call GNUNET_xgrow_ directly. Use the GNUNET_array_grow macro. + * + * @param old address of the pointer to the array + * *old may be NULL + * @param elementSize the size of the elements of the array + * @param oldCount address of the number of elements in the *old array + * @param newCount number of elements in the new array, may be 0 (then *old will be NULL afterwards) + */ +void GNUNET_xgrow_ (void **old, + size_t elementSize, + unsigned int *oldCount, + unsigned int newCount, + const char *filename, int linenumber); + + + + +#if __STDC_VERSION__ < 199901L +# if __GNUC__ >= 2 +# define __func__ __FUNCTION__ +# else +# define __func__ "" +# endif +#endif + +#endif /*GNUNET_COMMON_H_ */ diff --git a/src/include/gnunet_configuration_lib.h b/src/include/gnunet_configuration_lib.h new file mode 100644 index 000000000..fefc6884a --- /dev/null +++ b/src/include/gnunet_configuration_lib.h @@ -0,0 +1,238 @@ +/* + This file is part of GNUnet. + (C) 2006, 2008, 2009 Christian Grothoff (and other contributing authors) + + GNUnet is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published + by the Free Software Foundation; either version 2, 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 + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with GNUnet; see the file COPYING. If not, write to the + Free Software Foundation, Inc., 59 Temple Place - Suite 330, + Boston, MA 02111-1307, USA. +*/ + +/** + * @file include/gnunet_configuration_lib.h + * @brief configuration API + * + * @author Christian Grothoff + */ + +#ifndef GNUNET_CONFIGURATION_LIB_H +#define GNUNET_CONFIGURATION_LIB_H + + +#ifdef __cplusplus +extern "C" +{ +#if 0 /* keep Emacsens' auto-indent happy */ +} +#endif +#endif + +#include "gnunet_common.h" + +/** + * A configuration object. + */ +struct GNUNET_CONFIGURATION_Handle; + +/** + * Create a new configuration object. + * + * @param component name of responsible component + */ +struct GNUNET_CONFIGURATION_Handle *GNUNET_CONFIGURATION_create (void); + +/** + * Destroy configuration object. + */ +void GNUNET_CONFIGURATION_destroy (struct GNUNET_CONFIGURATION_Handle *cfg); + +/** + * Load configuration. This function will first parse the + * defaults and then parse the specific configuration file + * to overwrite the defaults. + * + * @param filename name of the configuration file + * @return GNUNET_OK on success, GNUNET_SYSERR on error + */ +int GNUNET_CONFIGURATION_load (struct GNUNET_CONFIGURATION_Handle *cfg, + const char *filename); + +/** + * Parse a configuration file, add all of the options in the + * file to the configuration environment. + * @return GNUNET_OK on success, GNUNET_SYSERR on error + */ +int GNUNET_CONFIGURATION_parse (struct GNUNET_CONFIGURATION_Handle *cfg, + const char *filename); + +/** + * Write configuration file. + * @return GNUNET_OK on success, GNUNET_SYSERR on error + */ +int GNUNET_CONFIGURATION_write (struct GNUNET_CONFIGURATION_Handle *cfg, + const char *filename); + +/** + * Test if there are configuration options that were + * changed since the last save. + * @return GNUNET_NO if clean, GNUNET_YES if dirty, GNUNET_SYSERR on error (i.e. last save failed) + */ +int GNUNET_CONFIGURATION_is_dirty (struct GNUNET_CONFIGURATION_Handle *cfg); + +/** + * Get a configuration value that should be a number. + * @return GNUNET_OK on success, GNUNET_SYSERR on error + */ +int GNUNET_CONFIGURATION_get_value_number (struct GNUNET_CONFIGURATION_Handle + *cfg, const char *section, + const char *option, + unsigned long long *number); + +/** + * Test if we have a value for a particular option + * @return GNUNET_YES if so, GNUNET_NO if not. + */ +int GNUNET_CONFIGURATION_have_value (struct GNUNET_CONFIGURATION_Handle *cfg, + const char *section, const char *option); + +/** + * Get a configuration value that should be a string. + * @param value will be set to a freshly allocated configuration + * value, or NULL if option is not specified + * @return GNUNET_OK on success, GNUNET_SYSERR on error + */ +int GNUNET_CONFIGURATION_get_value_string (struct GNUNET_CONFIGURATION_Handle + *cfg, const char *section, + const char *option, char **value); + +/** + * Get a configuration value that should be the name of a file + * or directory. + * + * @param value will be set to a freshly allocated configuration + * value, or NULL if option is not specified + * @return GNUNET_OK on success, GNUNET_SYSERR on error + */ +int GNUNET_CONFIGURATION_get_value_filename (struct + GNUNET_CONFIGURATION_Handle *cfg, + const char *section, + const char *option, + char **value); + +/** + * Iterate over the set of filenames stored in a configuration value. + * + * @return number of filenames iterated over, -1 on error + */ +int GNUNET_CONFIGURATION_iterate_value_filenames (struct + GNUNET_CONFIGURATION_Handle + *cfg, + const char *section, + const char *option, + GNUNET_FileNameCallback + cb, void *cls); + +/** + * Get a configuration value that should be in a set of + * predefined strings + * + * @param choices NULL-terminated list of legal values + * @param value will be set to an entry in the legal list, + * or NULL if option is not specified and no default given + * @return GNUNET_OK on success, GNUNET_SYSERR on error + */ +int GNUNET_CONFIGURATION_get_value_choice (struct GNUNET_CONFIGURATION_Handle + *cfg, const char *section, + const char *option, + const char **choices, + const char **value); + +/** + * Get a configuration value that should be in a set of + * "YES" or "NO". + * + * @return GNUNET_YES, GNUNET_NO or if option has no valid value, GNUNET_SYSERR + */ +int GNUNET_CONFIGURATION_get_value_yesno (struct GNUNET_CONFIGURATION_Handle + *cfg, const char *section, + const char *option); + +/** + * Expand an expression of the form "$FOO/BAR" to "DIRECTORY/BAR" + * where either in the "PATHS" section or the environtment + * "FOO" is set to "DIRECTORY". + + * @param old string to $-expand (will be freed!) + * @return $-expanded string + */ +char *GNUNET_CONFIGURATION_expand_dollar (struct GNUNET_CONFIGURATION_Handle + *cfg, char *old); + +/** + * Set a configuration value that should be a number. + */ +void +GNUNET_CONFIGURATION_set_value_number (struct GNUNET_CONFIGURATION_Handle + *cfg, + const char *section, + const char *option, + unsigned long long number); + + +/** + * Set a configuration value that should be a string. + * @param value + */ +void +GNUNET_CONFIGURATION_set_value_string (struct GNUNET_CONFIGURATION_Handle + *cfg, + const char *section, + const char *option, const char *value); + +/** + * Remove a filename from a configuration value that + * represents a list of filenames + * + * @param value filename to remove + * @return GNUNET_OK on success, + * GNUNET_SYSERR if the filename is not in the list + */ +int GNUNET_CONFIGURATION_remove_value_filename (struct + GNUNET_CONFIGURATION_Handle + *cfg, + const char *section, + const char *option, + const char *value); + +/** + * Append a filename to a configuration value that + * represents a list of filenames + * + * @param value filename to append + * @return GNUNET_OK on success, + * GNUNET_SYSERR if the filename already in the list + */ +int GNUNET_CONFIGURATION_append_value_filename (struct + GNUNET_CONFIGURATION_Handle + *cfg, const char *section, + const char *option, + const char *value); + +#if 0 /* keep Emacsens' auto-indent happy */ +{ +#endif +#ifdef __cplusplus +} +#endif + +#endif diff --git a/src/include/gnunet_container_lib.h b/src/include/gnunet_container_lib.h new file mode 100644 index 000000000..255f68a89 --- /dev/null +++ b/src/include/gnunet_container_lib.h @@ -0,0 +1,784 @@ +/* + This file is part of GNUnet. + (C) 2001, 2002, 2003, 2004, 2005, 2006, 2008 Christian Grothoff (and other contributing authors) + + GNUnet is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published + by the Free Software Foundation; either version 2, 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 + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with GNUnet; see the file COPYING. If not, write to the + Free Software Foundation, Inc., 59 Temple Place - Suite 330, + Boston, MA 02111-1307, USA. +*/ + +/** + * @file include/gnunet_container_lib.h + * @brief container classes for GNUnet + * + * @author Christian Grothoff + * @author Nils Durner + */ + +#ifndef GNUNET_CONTAINER_LIB_H +#define GNUNET_CONTAINER_LIB_H + +/* add error and config prototypes */ +#include "gnunet_crypto_lib.h" +#include + +#ifdef __cplusplus +extern "C" +{ +#if 0 /* keep Emacsens' auto-indent happy */ +} +#endif +#endif + + +/* ******************* bloomfilter ***************** */ + +/** + * @brief bloomfilter representation (opaque) + */ +struct GNUNET_CONTAINER_BloomFilter; + +/** + * Iterator over HashCodes. + * + * @return GNUNET_YES if next was updated + * GNUNET_NO if there are no more entries + */ +typedef int (*GNUNET_HashCodeIterator) (GNUNET_HashCode * next, void *arg); + +/** + * Load a bloom-filter from a file. + * @param filename the name of the file (or the prefix) + * @param size the size of the bloom-filter (number of + * bytes of storage space to use) + * @param k the number of GNUNET_CRYPTO_hash-functions to apply per + * element (number of bits set per element in the set) + * @return the bloomfilter + */ +struct GNUNET_CONTAINER_BloomFilter *GNUNET_CONTAINER_bloomfilter_load (const + char + *filename, + unsigned + int + size, + unsigned + int + k); + +/** + * Create a bloom filter from raw bits. + * + * @param data the raw bits in memory (maybe NULL, + * in which case all bits should be considered + * to be zero). + * @param size the size of the bloom-filter (number of + * bytes of storage space to use); also size of data + * -- unless data is NULL. Must be a power of 2. + * @param k the number of GNUNET_CRYPTO_hash-functions to apply per + * element (number of bits set per element in the set) + * @return the bloomfilter + */ +struct GNUNET_CONTAINER_BloomFilter *GNUNET_CONTAINER_bloomfilter_init (const + char + *data, + unsigned + int + size, + unsigned + int + k); + +/** + * Copy the raw data of this bloomfilter into + * the given data array. + * + * @param data where to write the data + * @param size the size of the given data array + * @return GNUNET_SYSERR if the data array of the wrong size + */ +int GNUNET_CONTAINER_bloomfilter_get_raw_data (struct + GNUNET_CONTAINER_BloomFilter + *bf, char *data, + unsigned int size); + +/** + * Test if an element is in the filter. + * @param e the element + * @param bf the filter + * @return GNUNET_YES if the element is in the filter, GNUNET_NO if not + */ +int GNUNET_CONTAINER_bloomfilter_test (struct GNUNET_CONTAINER_BloomFilter + *bf, const GNUNET_HashCode * e); + +/** + * Add an element to the filter + * @param bf the filter + * @param e the element + */ +void GNUNET_CONTAINER_bloomfilter_add (struct GNUNET_CONTAINER_BloomFilter + *bf, const GNUNET_HashCode * e); + +/** + * Remove an element from the filter. + * @param bf the filter + * @param e the element to remove + */ +void GNUNET_CONTAINER_bloomfilter_remove (struct GNUNET_CONTAINER_BloomFilter + *bf, const GNUNET_HashCode * e); + +/** + * Free the space associcated with a filter + * in memory, flush to drive if needed (do not + * free the space on the drive) + * @param bf the filter + */ +void GNUNET_CONTAINER_bloomfilter_free (struct GNUNET_CONTAINER_BloomFilter + *bf); + +/** + * Reset a bloom filter to empty. + * @param bf the filter + */ +void GNUNET_CONTAINER_bloomfilter_clear (struct GNUNET_CONTAINER_BloomFilter + *bf); + +/** + * Or the entries of the given raw data array with the + * data of the given bloom filter. Assumes that + * the size of the data array and the current filter + * match. + * @param bf the filter + */ +int GNUNET_CONTAINER_bloomfilter_or (struct GNUNET_CONTAINER_BloomFilter *bf, + const char *data, unsigned int size); + +/** + * Resize a bloom filter. Note that this operation + * is pretty costly. Essentially, the bloom filter + * needs to be completely re-build. + * + * @param bf the filter + * @param iterator an iterator over all elements stored in the BF + * @param iterator_arg argument to the iterator function + * @param size the new size for the filter + * @param k the new number of GNUNET_CRYPTO_hash-function to apply per element + */ +void GNUNET_CONTAINER_bloomfilter_resize (struct GNUNET_CONTAINER_BloomFilter + *bf, + GNUNET_HashCodeIterator iterator, + void *iterator_arg, + unsigned int size, unsigned int k); + +/* ****************** metadata ******************* */ + +/** + * Meta data to associate with a file, directory or namespace. + */ +struct GNUNET_CONTAINER_MetaData; + +/** + * Iterator over meta data. + * @return GNUNET_OK to continue to iterate, GNUNET_SYSERR to abort + */ +typedef int (*GNUNET_CONTAINER_MetaDataProcessor) (EXTRACTOR_KeywordType type, + const char *data, + void *closure); + +/** + * Create a fresh MetaData token. + */ +struct GNUNET_CONTAINER_MetaData *GNUNET_CONTAINER_meta_data_create (void); + +/** + * Duplicate a MetaData token. + */ +struct GNUNET_CONTAINER_MetaData *GNUNET_CONTAINER_meta_data_duplicate (const + struct + GNUNET_CONTAINER_MetaData + *meta); + +/** + * Free meta data. + */ +void GNUNET_CONTAINER_meta_data_destroy (struct GNUNET_CONTAINER_MetaData + *md); + +/** + * Test if two MDs are equal. + */ +int GNUNET_CONTAINER_meta_data_test_equal (const struct + GNUNET_CONTAINER_MetaData *md1, + const struct + GNUNET_CONTAINER_MetaData *md2); + + +/** + * Extend metadata. + * @return GNUNET_OK on success, GNUNET_SYSERR if this entry already exists + */ +int GNUNET_CONTAINER_meta_data_insert (struct GNUNET_CONTAINER_MetaData *md, + EXTRACTOR_KeywordType type, + const char *data); + +/** + * Remove an item. + * @return GNUNET_OK on success, GNUNET_SYSERR if the item does not exist in md + */ +int GNUNET_CONTAINER_meta_data_delete (struct GNUNET_CONTAINER_MetaData *md, + EXTRACTOR_KeywordType type, + const char *data); + +/** + * Add the current time as the publication date + * to the meta-data. + */ +void GNUNET_CONTAINER_meta_data_add_publication_date (struct + GNUNET_CONTAINER_MetaData + *md); + +/** + * Iterate over MD entries, excluding thumbnails. + * + * @return number of entries + */ +int GNUNET_CONTAINER_meta_data_get_contents (const struct + GNUNET_CONTAINER_MetaData *md, + GNUNET_CONTAINER_MetaDataProcessor + iterator, void *closure); + +/** + * Get the first MD entry of the given type. + * @return NULL if we do not have any such entry, + * otherwise client is responsible for freeing the value! + */ +char *GNUNET_CONTAINER_meta_data_get_by_type (const struct + GNUNET_CONTAINER_MetaData *md, + EXTRACTOR_KeywordType type); + +/** + * Get the first matching MD entry of the given types. + * @paarm ... -1-terminated list of types + * @return NULL if we do not have any such entry, + * otherwise client is responsible for freeing the value! + */ +char *GNUNET_CONTAINER_meta_data_get_first_by_types (const struct + GNUNET_CONTAINER_MetaData + *md, ...); + +/** + * Get a thumbnail from the meta-data (if present). + * + * @param thumb will be set to the thumbnail data. Must be + * freed by the caller! + * @return number of bytes in thumbnail, 0 if not available + */ +size_t GNUNET_CONTAINER_meta_data_get_thumbnail (const struct + GNUNET_CONTAINER_MetaData + *md, unsigned char **thumb); + +/** + * Extract meta-data from a file. + * + * @return GNUNET_SYSERR on error, otherwise the number + * of meta-data items obtained + */ +int GNUNET_CONTAINER_meta_data_extract_from_file (struct + GNUNET_CONTAINER_MetaData + *md, const char *filename, + EXTRACTOR_ExtractorList * + extractors); + +enum GNUNET_CONTAINER_MetaDataSerializationOptions +{ + GNUNET_CONTAINER_META_DATA_SERIALIZE_FULL = GNUNET_NO, + GNUNET_CONTAINER_META_DATA_SERIALIZE_PART = GNUNET_YES, + GNUNET_CONTAINER_META_DATA_SERIALIZE_NO_COMPRESS = 2 +}; + + + +/** + * Serialize meta-data to target. + * + * @param size maximum number of bytes available + * @param opt is it ok to just write SOME of the + * meta-data to match the size constraint, + * possibly discarding some data? + * @return number of bytes written on success, + * GNUNET_SYSERR on error (typically: not enough + * space) + */ +int GNUNET_CONTAINER_meta_data_serialize (const struct + GNUNET_CONTAINER_MetaData *md, + char *target, unsigned int size, + enum + GNUNET_CONTAINER_MetaDataSerializationOptions + opt); + +/** + * Compute size of the meta-data in + * serialized form. + * @param opt is it ok to just write SOME of the + * meta-data to match the size constraint, + * possibly discarding some data? + */ +unsigned int GNUNET_CONTAINER_meta_data_get_serialized_size (const struct + GNUNET_CONTAINER_MetaData + *md, + enum + GNUNET_CONTAINER_MetaDataSerializationOptions + opt); + +/** + * Deserialize meta-data. Initializes md. + * @param size number of bytes available + * @return MD on success, NULL on error (i.e. + * bad format) + */ +struct GNUNET_CONTAINER_MetaData + *GNUNET_CONTAINER_meta_data_deserialize (const char *input, + unsigned int size); + +/** + * Does the meta-data claim that this is a directory? + * Checks if the mime-type is that of a GNUnet directory. + * + * @return GNUNET_YES if it is, GNUNET_NO if it is not, GNUNET_SYSERR if + * we have no mime-type information (treat as 'GNUNET_NO') + */ +int GNUNET_CONTAINER_meta_data_test_for_directory (const struct + GNUNET_CONTAINER_MetaData + *md); + + +/* ******************************* HashMap **************************** */ + +/** + * Opaque handle for a HashMap. + */ +struct GNUNET_CONTAINER_MultiHashMap; + +/** + * Options for storing values in the HashMap. + */ +enum GNUNET_CONTAINER_MultiHashMapOption +{ + /** + * If a value with the given key exists, replace it. + * Note that the old value would NOT be freed + * by replace (the application has to make sure that + * this happens if required). + */ + GNUNET_CONTAINER_MULTIHASHMAPOPTION_REPLACE, + + /** + * Allow multiple values with the same key. + */ + GNUNET_CONTAINER_MULTIHASHMAPOPTION_MULTIPLE, + + /** + * There must only be one value per key; storing + * a value should fail if a value under the same + * key already exists. + */ + GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_ONLY, + + /** + * There must only be one value per key, but don't + * bother checking if a value already exists + * (faster than UNIQUE_ONLY; implemented just like + * MULTIPLE but this option documents better what + * is intended if UNIQUE is what is desired). + */ + GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_FAST +}; + +/** + * Iterator over HashCodes. + * + * @param key current key code + * @param value value in the hash map + * @param cls client-defined argument + * @return GNUNET_YES if we should continue to + * iterate, + * GNUNET_NO if not. + */ +typedef int (*GNUNET_CONTAINER_HashMapIterator) (const GNUNET_HashCode * key, + void *value, void *cls); + + +/** + * Create a multi hash map. + * + * @param map the map + * @param len initial size (map will grow as needed) + * @return NULL on error + */ +struct GNUNET_CONTAINER_MultiHashMap + *GNUNET_CONTAINER_multihashmap_create (unsigned int len); + +/** + * Destroy a hash map. Will not free any values + * stored in the hash map! + * + * @param map the map + */ +void GNUNET_CONTAINER_multihashmap_destroy (struct + GNUNET_CONTAINER_MultiHashMap + *map); + +/** + * Given a key find a value in the + * map matching the key. + * + * @param map the map + * @param key what to look for + * @return NULL if no value was found; note that + * this is indistinguishable from values that just + * happen to be NULL; use "contains" to test for + * key-value pairs with value NULL + */ +void *GNUNET_CONTAINER_multihashmap_get (const struct + GNUNET_CONTAINER_MultiHashMap *map, + const GNUNET_HashCode * key); + +/** + * Remove the given key-value pair from the map. + * Note that if the key-value pair is in the map + * multiple times, only one of the pairs will be + * removed. + * + * @param map the map + * @param key key of the key-value pair + * @param value value of the key-value pair + * @return GNUNET_YES on success, GNUNET_NO if the key-value pair + * is not in the map + */ +int GNUNET_CONTAINER_multihashmap_remove (struct GNUNET_CONTAINER_MultiHashMap + *map, const GNUNET_HashCode * key, + void *value); + +/** + * Remove all entries for the given key from the map. + * Note that the values would not be "freed". + * + * @param map the map + * @param key identifies values to be removed + * @return number of values removed + */ +int GNUNET_CONTAINER_multihashmap_remove_all (struct + GNUNET_CONTAINER_MultiHashMap + *map, + const GNUNET_HashCode * key); + +/** + * Check if the map contains any value under the given + * key (including values that are NULL). + * + * @param map the map + * @param key the key to test if a value exists for it + * @return GNUNET_YES if such a value exists, + * GNUNET_NO if not + */ +int GNUNET_CONTAINER_multihashmap_contains (const struct + GNUNET_CONTAINER_MultiHashMap + *map, + const GNUNET_HashCode * key); + +/** + * Store a key-value pair in the map. + * + * @param map the map + * @param key key to use + * @param value value to use + * @param opt options for put + * @return GNUNET_OK on success, + * GNUNET_NO if a value was replaced (with REPLACE) + * GNUNET_SYSERR if UNIQUE_ONLY was the option and the + * value already exists + */ +int GNUNET_CONTAINER_multihashmap_put (struct GNUNET_CONTAINER_MultiHashMap + *map, const GNUNET_HashCode * key, + void *value, + enum + GNUNET_CONTAINER_MultiHashMapOption + opt); + +/** + * Get the number of key-value pairs in the map. + * + * @param map the map + * @return the number of key value pairs + */ +unsigned int GNUNET_CONTAINER_multihashmap_size (const struct + GNUNET_CONTAINER_MultiHashMap + *map); + + +/** + * Iterate over all entries in the map. + * + * @param map the map + * @param iterator function to call on each entry + * @param cls extra argument to it + * @return the number of key value pairs processed, + * GNUNET_SYSERR if it aborted iteration + */ +int GNUNET_CONTAINER_multihashmap_iterate (const struct + GNUNET_CONTAINER_MultiHashMap *map, + GNUNET_CONTAINER_HashMapIterator + iterator, void *cls); + +/** + * Iterate over all entries in the map + * that match a particular key. + * + * @param map the map + * @param key key that the entries must correspond to + * @param iterator function to call on each entry + * @param cls extra argument to it + * @return the number of key value pairs processed, + * GNUNET_SYSERR if it aborted iteration + */ +int GNUNET_CONTAINER_multihashmap_get_multiple (const struct + GNUNET_CONTAINER_MultiHashMap + *map, + const GNUNET_HashCode * key, + GNUNET_CONTAINER_HashMapIterator + iterator, void *cls); +/** + * Returns the stored value of a random non-null entry + * in the hash table. Returns only the first value, does + * not go inside bucket linked list (yet). Runs with a + * worst case time of N, so it's not efficient in any way + * shape or form!!!!. + */ +void *GNUNET_CONTAINER_multihashmap_get_random (const struct + GNUNET_CONTAINER_MultiHashMap + *map); + + + + +/* ******************** doubly-linked list *************** */ + +/** + * Insert an element into a DLL. Assumes + * that head, tail and element are structs + * with prev and next fields. + */ +#define GNUNET_CONTAINER_DLL_insert(head,tail,element) \ + (element)->next = (head); \ + (element)->prev = NULL; \ + if ((tail) == NULL) \ + (tail) = element; \ + else \ + (head)->prev = element; \ + (head) = (element); + +/** + * Insert an element into a DLL after the given other + * element. Insert at the head if the other + * element is NULL. + */ +#define GNUNET_CONTAINER_DLL_insert_after(head,tail,other,element) \ + (element)->prev = (other); \ + if (NULL == other) \ + { \ + (element)->next = (head); \ + (head) = (element); \ + } \ + else \ + { \ + (element)->next = (other)->next; \ + (other)->next = (element); \ + } \ + if (NULL == (element)->next) \ + (tail) = (element); \ + else \ + (element)->next->prev = (element); + + + + +/** + * Remove an element from a DLL. Assumes + * that head, tail and element are structs + * with prev and next fields. + */ +#define GNUNET_CONTAINER_DLL_remove(head,tail,element) \ + if ((element)->prev == NULL) \ + (head) = (element)->next; \ + else \ + (element)->prev->next = (element)->next; \ + if ((element)->next == NULL) \ + (tail) = (element)->prev; \ + else \ + (element)->next->prev = (element)->prev; + + + +/* ******************** Heap *************** */ + + +/** + * Cost by which elements in a heap can be ordered. + */ +typedef unsigned int GNUNET_CONTAINER_HeapCost; + +/* + * Heap type, either max or min. Hopefully makes the + * implementation more useful. + */ +enum GNUNET_CONTAINER_HeapOrder +{ + /** + * Heap with the maximum cost at the root. + */ + GNUNET_CONTAINER_HEAP_ORDER_MAX, + + /** + * Heap with the minimum cost at the root. + */ + GNUNET_CONTAINER_HEAP_ORDER_MIN +}; + +/** + * Handle to a Heap. + */ +struct GNUNET_CONTAINER_Heap; + +/** + * Create a new heap. + * + * @param type should the minimum or the maximum element be the root + * @return NULL on error, otherwise a fresh heap + */ +struct GNUNET_CONTAINER_Heap *GNUNET_CONTAINER_heap_create (enum + GNUNET_CONTAINER_HeapOrder + type); + +/** + * Free a heap + * + * @param h heap to free. + */ +void GNUNET_CONTAINER_heap_destroy (struct GNUNET_CONTAINER_Heap *h); + +/** + * Function called on elements of a heap. + * + * @param cls closure + * @param element obj stored in heap + * @param cost cost of the element + * @return GNUNET_YES if we should continue to iterate, + * GNUNET_NO if not. + */ +typedef int (*GNUNET_CONTAINER_HeapIterator) (void *cls, + void *element, + GNUNET_CONTAINER_HeapCost cost); +/** + * Iterate over all entries in the map. + * + * @param heap the heap + * @param iterator function to call on each entry + * @param iterator_cls closure for iterator + * @return number of items handled + * GNUNET_SYSERR if iteration was aborted by iterator + */ +int GNUNET_CONTAINER_heap_iterate (struct GNUNET_CONTAINER_Heap *heap, + GNUNET_CONTAINER_HeapIterator iterator, + void *iterator_cls); + + +/** + * Inserts a new item into the heap, item is always neighbor now. + * @param heap the heap + */ +int +GNUNET_CONTAINER_heap_insert (struct GNUNET_CONTAINER_Heap *heap, + void *element, GNUNET_CONTAINER_HeapCost cost); + +/** + * Removes root of the tree, is remove max if a max heap and remove min + * if a min heap, returns the data stored at the node. + * + * @param heap the heap + * @return NULL if the heap is empty + */ +void *GNUNET_CONTAINER_heap_remove_root (struct GNUNET_CONTAINER_Heap *heap); + +/** + * Returns element stored at root of tree, doesn't effect anything + * + * @param heap the heap + * @return NULL if the heap is empty + */ +void *GNUNET_CONTAINER_heap_peek (struct GNUNET_CONTAINER_Heap *heap); + +/** + * Removes any node from the tree based on the neighbor given, does + * not traverse the tree (backpointers) but may take more time due to + * percolation of nodes. + * @param heap the heap + */ +void *GNUNET_CONTAINER_heap_remove_node (struct GNUNET_CONTAINER_Heap *heap, + void *element); + +/** + * Updates the cost of any node in the tree + * + * @param heap the heap + * @param element the element for which the cost is updated + * @param new_cost new cost for the element + * @return WHAT? + */ +int +GNUNET_CONTAINER_heap_update_cost (struct GNUNET_CONTAINER_Heap *heap, + void *element, + GNUNET_CONTAINER_HeapCost new_cost); + +/** + * Random walk of the tree, returns the data stored at the next random node + * in the walk. Calls callee with the data, or NULL if the tree is empty + * or some other problem crops up. + * + * @param heap the heap + * @return the next element from the random walk + */ +void *GNUNET_CONTAINER_heap_walk_get_next (struct GNUNET_CONTAINER_Heap + *heap); + +/** + * Returns the current size of the heap + * + * @param heap the heap to get the size of + * @return number of elements in the heap + */ +unsigned int +GNUNET_CONTAINER_heap_get_size (struct GNUNET_CONTAINER_Heap *heap); + + + +#if 0 /* keep Emacsens' auto-indent happy */ +{ +#endif +#ifdef __cplusplus +} +#endif + + +/* ifndef GNUNET_CONTAINER_LIB_H */ +#endif +/* end of gnunet_container_lib.h */ diff --git a/src/include/gnunet_core_service.h b/src/include/gnunet_core_service.h new file mode 100644 index 000000000..8b75271b9 --- /dev/null +++ b/src/include/gnunet_core_service.h @@ -0,0 +1,323 @@ +/* + This file is part of GNUnet. + (C) 2009 Christian Grothoff (and other contributing authors) + + GNUnet is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published + by the Free Software Foundation; either version 2, 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 + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with GNUnet; see the file COPYING. If not, write to the + Free Software Foundation, Inc., 59 Temple Place - Suite 330, + Boston, MA 02111-1307, USA. +*/ + +/** + * @file include/gnunet_core_service.h + * @brief core service; this is the main API for encrypted P2P + * communications + * @author Christian Grothoff + */ + +#ifndef GNUNET_CORE_SERVICE_H +#define GNUNET_CORE_SERVICE_H + +#ifdef __cplusplus +extern "C" +{ +#if 0 /* keep Emacsens' auto-indent happy */ +} +#endif +#endif + +#include "gnunet_util_lib.h" + +/** + * Version number of GNUnet-core API. + */ +#define GNUNET_CORE_VERSION 0x00000000 + + +/** + * Opaque handle to the service. + */ +struct GNUNET_CORE_Handle; + + +/** + * Method called whenever a given peer either connects or + * disconnects (or list of connections was requested). + * + * @param cls closure + * @param peer peer identity this notification is about + * @param bpm how much bandwidth is available + * (for sending) to this peer + * @param last_activity when did we last + * receive anything from this peer? + */ +typedef void (*GNUNET_CORE_ClientEventHandler) (void *cls, + const struct + GNUNET_PeerIdentity * peer, + unsigned int bpm, + struct GNUNET_TIME_Absolute + last_activity); + + +/** + * Type of a send callback to fill up buffers. + * + * @param receiver the receiver of the message + * @param position is the reference to the + * first unused position in the buffer where GNUnet is building + * the message + * @param padding is the number of bytes left in that buffer. + * @return the number of bytes written to + * that buffer (must be a positive number). + */ +typedef unsigned int + (*GNUNET_CORE_BufferFillCallback) (void *cls, + const struct GNUNET_PeerIdentity * + receiver, + void *position, unsigned int padding); + + +/** + * Functions with this signature are called whenever a message is + * received or transmitted. + * + * @param cls closure + * @param peer the other peer involved (sender or receiver, NULL + * for loopback messages where we are both sender and receiver) + * @param message the actual message + * @return GNUNET_OK to keep the connection open, + * GNUNET_SYSERR to close it (signal serious error) + */ +typedef int + (*GNUNET_CORE_MessageCallback) (void *cls, + const struct GNUNET_PeerIdentity * other, + const struct GNUNET_MessageHeader * + message); + + +/** + * Message handler. Each struct specifies how to handle on particular + * type of message received. + */ +struct GNUNET_CORE_MessageHandler +{ + /** + * Function to call for messages of "type". + */ + GNUNET_CORE_MessageCallback callback; + + /** + * Type of the message this handler covers. + */ + uint16_t type; + + /** + * Expected size of messages of this type. Use 0 for variable-size. + * If non-zero, messages of the given type will be discarded if they + * do not have the right size. + */ + uint16_t expected_size; + +}; + + +/** + * Function called after GNUNET_CORE_connect has succeeded + * (or failed for good). Note that the private key of the + * peer is intentionally not exposed here; if you need it, + * your process should try to read the private key file + * directly (which should work if you are authorized...). + * + * @param cls closure + * @param server handle to the server, NULL if we failed + * @param my_identity ID of this peer, NULL if we failed + * @param publicKey public key of this peer, NULL if we failed + */ +typedef void + (*GNUNET_CORE_StartupCallback) (void *cls, + struct GNUNET_CORE_Handle * server, + const struct GNUNET_PeerIdentity * + my_identity, + const struct + GNUNET_CRYPTO_RsaPublicKeyBinaryEncoded * + publicKey); + + +/** + * Connect to the core service. Note that the connection may + * complete (or fail) asynchronously. + * + * @param sched scheduler to use + * @param cfg configuration to use + * @param timeout after how long should we give up trying to connect to the core service? + * @param cls closure for the various callbacks that follow (including handlers in the handlers array) + * @param init callback to call on timeout or once we have successfully + * connected to the core service + * @param connects function to call on peer connect, can be NULL + * @param disconnects function to call on peer disconnect / timeout, can be NULL + * @param bfc function to call to fill up spare bandwidth, can be NULL + * @param inbound_notify function to call for all inbound messages, can be NULL + * @param inbound_hdr_only set to GNUNET_YES if inbound_notify will only read the + * GNUNET_MessageHeader and hence we do not need to give it the full message; + * can be used to improve efficiency, ignored if inbound_notify is NULLL + * @param outbound_notify function to call for all outbound messages, can be NULL + * @param outbound_hdr_only set to GNUNET_YES if outbound_notify will only read the + * GNUNET_MessageHeader and hence we do not need to give it the full message + * can be used to improve efficiency, ignored if outbound_notify is NULLL + * @param handlers callbacks for messages we care about, NULL-terminated + */ +void +GNUNET_CORE_connect (struct GNUNET_SCHEDULER_Handle *sched, + struct GNUNET_CONFIGURATION_Handle *cfg, + struct GNUNET_TIME_Relative timeout, + void *cls, + GNUNET_CORE_StartupCallback init, + GNUNET_CORE_ClientEventHandler connects, + GNUNET_CORE_ClientEventHandler disconnects, + GNUNET_CORE_BufferFillCallback bfc, + GNUNET_CORE_MessageCallback inbound_notify, + int inbound_hdr_only, + GNUNET_CORE_MessageCallback outbound_notify, + int outbound_hdr_only, + const struct GNUNET_CORE_MessageHandler *handlers); + + +/** + * Disconnect from the core service. + * + * @param handle connection to core to disconnect + */ +void GNUNET_CORE_disconnect (struct GNUNET_CORE_Handle *handle); + + +/** + * Function called with statistics about the given peer. + * + * @param peer identifies the peer + * @param latency current latency estimate, "FOREVER" if we have been + * disconnected + * @param bpm_in set to the current bandwidth limit (receiving) for this peer + * @param bpm_out set to the current bandwidth limit (sending) for this peer + * @param amount set to the amount that was actually reserved or unreserved + * @param preference current traffic preference for the given peer + */ +typedef void + (*GNUNET_CORE_PeerConfigurationInfoCallback) (void *cls, + const struct + GNUNET_PeerIdentity * peer, + unsigned int bpm_in, + unsigned int bpm_out, + struct GNUNET_TIME_Relative + latency, int amount, + double preference); + + +/** + * Obtain statistics and/or change preferences for the given peer. + * + * @param handle connection to core to use + * @param peer identifies the peer + * @param timeout after how long should we give up (and call "info" with NULL + * for "peer" to signal an error)? + * @param bpm_out set to the current bandwidth limit (sending) for this peer, + * caller should set "bpm_out" to "-1" to avoid changing + * the current value; otherwise "bpm_out" will be lowered to + * the specified value; passing a pointer to "0" can be used to force + * us to disconnect from the peer; "bpm_out" might not increase + * as specified since the upper bound is generally + * determined by the other peer! + * @param amount reserve N bytes for receiving, negative + * amounts can be used to undo a (recent) reservation; + * @param preference increase incoming traffic share preference by this amount; + * in the absence of "amount" reservations, we use this + * preference value to assign proportional bandwidth shares + * to all connected peers + * @param info function to call with the resulting configuration information + * @param info_cls closure for info + */ +void +GNUNET_CORE_peer_configure (struct GNUNET_CORE_Handle *handle, + const struct GNUNET_PeerIdentity *peer, + struct GNUNET_TIME_Relative timeout, + unsigned int bpm_out, + int amount, + double preference, + GNUNET_CORE_PeerConfigurationInfoCallback info, + void *info_cls); + + +/** + * Handle for a transmission request. + */ +struct GNUNET_CORE_TransmitHandle; + + +/** + * Ask the core to call "notify" once it is ready to transmit the + * given number of bytes to the specified "target". If we are not yet + * connected to the specified peer, a call to this function will cause + * us to try to establish a connection. + * + * @param handle connection to core service + * @param priority how important is the message? + * @param maxdelay how long can the message wait? + * @param target who should receive the message, + * use NULL for this peer (loopback) + * @param notify_size how many bytes of buffer space does notify want? + * @param notify function to call when buffer space is available + * @param notify_cls closure for notify + * @return non-NULL if the notify callback was queued, + * NULL if we can not even queue the request (insufficient + * memory); if NULL is returned, "notify" will NOT be called. + */ +struct GNUNET_CORE_TransmitHandle *GNUNET_CORE_notify_transmit_ready (struct + GNUNET_CORE_Handle + *handle, + unsigned + int + priority, + struct + GNUNET_TIME_Relative + maxdelay, + const + struct + GNUNET_PeerIdentity + *target, + size_t + notify_size, + GNUNET_NETWORK_TransmitReadyNotify + notify, + void + *notify_cls); + + +/** + * Cancel the specified transmission-ready notification. + * + * @param h handle that was returned by "notify_transmit_ready". + */ +void +GNUNET_CORE_notify_transmit_ready_cancel (struct GNUNET_CORE_TransmitHandle + *h); + + +#if 0 /* keep Emacsens' auto-indent happy */ +{ +#endif +#ifdef __cplusplus +} +#endif + +/* ifndef GNUNET_CORE_SERVICE_H */ +#endif +/* end of gnunet_core_service.h */ diff --git a/src/include/gnunet_crypto_lib.h b/src/include/gnunet_crypto_lib.h new file mode 100644 index 000000000..361d244e2 --- /dev/null +++ b/src/include/gnunet_crypto_lib.h @@ -0,0 +1,567 @@ +/* + This file is part of GNUnet. + (C) 2001, 2002, 2003, 2004, 2005, 2006, 2007 Christian Grothoff (and other contributing authors) + + GNUnet is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published + by the Free Software Foundation; either version 2, 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 + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with GNUnet; see the file COPYING. If not, write to the + Free Software Foundation, Inc., 59 Temple Place - Suite 330, + Boston, MA 02111-1307, USA. +*/ + +/** + * @file include/gnunet_crypto_lib.h + * @brief cryptographic primitives for GNUnet + * + * @author Christian Grothoff + * @author Krista Bennett + * @author Gerd Knorr + * @author Ioana Patrascu + * @author Tzvetan Horozov + */ + +#ifndef GNUNET_CRYPTO_LIB_H +#define GNUNET_CRYPTO_LIB_H + +#ifdef __cplusplus +extern "C" +{ +#if 0 /* keep Emacsens' auto-indent happy */ +} +#endif +#endif + +#include "gnunet_common.h" +#include "gnunet_scheduler_lib.h" + + +enum GNUNET_CRYPTO_Quality +{ + GNUNET_CRYPTO_QUALITY_WEAK, + GNUNET_CRYPTO_QUALITY_STRONG +}; + + +/** + * @brief length of the sessionkey in bytes (256 BIT sessionkey) + */ +#define GNUNET_CRYPTO_AES_KEY_LENGTH (256/8) + + +/** + * @brief Length of RSA encrypted data (2048 bit) + * + * We currently do not handle encryption of data + * that can not be done in a single call to the + * RSA methods (read: large chunks of data). + * We should never need that, as we can use + * the GNUNET_CRYPTO_hash for larger pieces of data for signing, + * and for encryption, we only need to encode sessionkeys! + */ +#define GNUNET_CRYPTO_RSA_DATA_ENCODING_LENGTH 256 + + +/** + * Length of an RSA KEY (d,e,len), 2048 bit (=256 octests) key d, 2 byte e + */ +#define GNUNET_CRYPTO_RSA_KEY_LENGTH 258 + + +/** + * The private information of an RSA key pair. + */ +struct GNUNET_CRYPTO_RsaPrivateKey; + + +/** + * @brief 0-terminated ASCII encoding of a GNUNET_HashCode. + */ +struct GNUNET_CRYPTO_HashAsciiEncoded +{ + unsigned char encoding[104]; +}; + + + +/** + * @brief an RSA signature + */ +struct GNUNET_CRYPTO_RsaSignature +{ + unsigned char sig[GNUNET_CRYPTO_RSA_DATA_ENCODING_LENGTH]; +}; + + +/** + * @brief header of what an RSA signature signs + * this must be followed by "size - 8" bytes of + * the actual signed data + */ +struct GNUNET_CRYPTO_RsaSignaturePurpose +{ + /** + * How many bytes does this signature sign? + * (including this purpose header); in network + * byte order (!). + */ + uint32_t size GNUNET_PACKED; + + /** + * What does this signature vouch for? This + * must contain a GNUNET_SIGNATURE_PURPOSE_XXX + * constant (from gnunet_signatures.h). In + * network byte order! + */ + uint32_t purpose GNUNET_PACKED; + +}; + + +/** + * @brief A public key. + */ +struct GNUNET_CRYPTO_RsaPublicKeyBinaryEncoded +{ + /** + * In big-endian, must be GNUNET_CRYPTO_RSA_KEY_LENGTH+4 + */ + uint16_t len GNUNET_PACKED; + + /** + * Size of n in key; in big-endian! + */ + uint16_t sizen GNUNET_PACKED; + + /** + * The key itself, contains n followed by e. + */ + unsigned char key[GNUNET_CRYPTO_RSA_KEY_LENGTH]; + + /** + * Padding (must be 0) + */ + uint16_t padding GNUNET_PACKED; +}; + + +/** + * RSA Encrypted data. + */ +struct GNUNET_CRYPTO_RsaEncryptedData +{ + unsigned char encoding[GNUNET_CRYPTO_RSA_DATA_ENCODING_LENGTH]; +}; + + +/** + * @brief type for session keys + */ +struct GNUNET_CRYPTO_AesSessionKey +{ + /** + * Actual key. + */ + unsigned char key[GNUNET_CRYPTO_AES_KEY_LENGTH]; + + /** + * checksum! + */ + uint32_t crc32 GNUNET_PACKED; +}; + + +/** + * @brief IV for sym cipher + * + * NOTE: must be smaller (!) in size than the + * GNUNET_HashCode. + */ +struct GNUNET_CRYPTO_AesInitializationVector +{ + unsigned char iv[GNUNET_CRYPTO_AES_KEY_LENGTH / 2]; +}; + + +/* **************** Functions and Macros ************* */ + + +/** + * Compute the CRC32 checksum for the first len + * bytes of the buffer. + * + * @param buf the data over which we're taking the CRC + * @param len the length of the buffer in bytes + * @return the resulting CRC32 checksum + */ +int GNUNET_CRYPTO_crc32_n (const void *buf, unsigned int len); + + +/** + * Produce a random value. + * + * @param i the upper limit (exclusive) for the random number + * @return a random value in the interval [0,i[. + */ +unsigned int GNUNET_CRYPTO_random_u32 (enum GNUNET_CRYPTO_Quality, + unsigned int i); + + +/** + * Random on unsigned 64-bit values. We break them down into signed + * 32-bit values and reassemble the 64-bit random value bit-wise. + */ +unsigned long long GNUNET_CRYPTO_random_u64 (enum GNUNET_CRYPTO_Quality mode, + unsigned long long u); + + +/** + * Get an array with a random permutation of the + * numbers 0...n-1. + * @param mode GNUNET_CRYPTO_QUALITY_STRONG if the strong (but expensive) PRNG should be used, GNUNET_CRYPTO_QUALITY_WEAK otherwise + * @param n the size of the array + * @return the permutation array (allocated from heap) + */ +unsigned int *GNUNET_CRYPTO_random_permute (enum GNUNET_CRYPTO_Quality mode, + unsigned int n); + + +/** + * Create a new Session key. + */ +void GNUNET_CRYPTO_aes_create_session_key (struct GNUNET_CRYPTO_AesSessionKey + *key); + + +/** + * Check that a new session key is well-formed. + * + * @return GNUNET_OK if the key is valid + */ +int GNUNET_CRYPTO_aes_check_session_key (const struct + GNUNET_CRYPTO_AesSessionKey *key); + + +/** + * Encrypt a block with the public key of another + * host that uses the same cyper. + * + * @param block the block to encrypt + * @param len the size of the block + * @param sessionkey the key used to encrypt + * @param iv the initialization vector to use, use INITVALUE + * for streams. + * @returns the size of the encrypted block, -1 for errors + */ +int GNUNET_CRYPTO_aes_encrypt (const void *block, + uint16_t len, + const struct GNUNET_CRYPTO_AesSessionKey + *sessionkey, + const struct + GNUNET_CRYPTO_AesInitializationVector *iv, + void *result); + + +/** + * Decrypt a given block with the sessionkey. + * + * @param sessionkey the key used to decrypt + * @param block the data to decrypt, encoded as returned by encrypt + * @param size how big is the block? + * @param iv the initialization vector to use + * @param result address to store the result at + * @return -1 on failure, size of decrypted block on success + */ +int GNUNET_CRYPTO_aes_decrypt (const struct GNUNET_CRYPTO_AesSessionKey + *sessionkey, const void *block, uint16_t size, + const struct + GNUNET_CRYPTO_AesInitializationVector *iv, + void *result); + + +/** + * Convert GNUNET_CRYPTO_hash to ASCII encoding. + * @param block the GNUNET_CRYPTO_hash code + * @param result where to store the encoding (struct GNUNET_CRYPTO_HashAsciiEncoded can be + * safely cast to char*, a '\0' termination is set). + */ +void GNUNET_CRYPTO_hash_to_enc (const GNUNET_HashCode * block, + struct GNUNET_CRYPTO_HashAsciiEncoded + *result); + + +/** + * Convert ASCII encoding back to GNUNET_CRYPTO_hash + * @param enc the encoding + * @param result where to store the GNUNET_CRYPTO_hash code + * @return GNUNET_OK on success, GNUNET_SYSERR if result has the wrong encoding + */ +int GNUNET_CRYPTO_hash_from_string (const char *enc, + GNUNET_HashCode * result); + + +/** + * Compute the distance between 2 hashcodes. + * The computation must be fast, not involve + * a.a or a.e (they're used elsewhere), and + * be somewhat consistent. And of course, the + * result should be a positive number. + * @return number between 0 and 65536 + */ +unsigned int GNUNET_CRYPTO_hash_distance_u32 (const GNUNET_HashCode * a, + const GNUNET_HashCode * b); + + +/** + * Hash block of given size. + * @param block the data to GNUNET_CRYPTO_hash, length is given as a second argument + * @param ret pointer to where to write the hashcode + */ +void GNUNET_CRYPTO_hash (const void *block, unsigned int size, + GNUNET_HashCode * ret); + + +/** + * Function called once the hash computation over the + * specified file has completed. + * + * @param cls closure + * @param res resulting hash, NULL on error + */ +typedef void (*GNUNET_CRYPTO_HashCompletedCallback) (void *cls, + const GNUNET_HashCode * + res); + + +/** + * Compute the hash of an entire file. + * + * @param sched scheduler to use + * @param priority scheduling priority to use + * @param run_on_shutdown should we complete even on shutdown? + * @param filename name of file to hash + * @param blocksize number of bytes to process in one task + * @param callback function to call upon completion + * @param callback_cls closure for callback + */ +void GNUNET_CRYPTO_hash_file (struct GNUNET_SCHEDULER_Handle *sched, + enum GNUNET_SCHEDULER_Priority priority, + int run_on_shutdown, + const char *filename, + size_t blocksize, + GNUNET_CRYPTO_HashCompletedCallback callback, + void *callback_cls); + + +/** + * Create a random hash code. + */ +void GNUNET_CRYPTO_hash_create_random (GNUNET_HashCode * result); + + +/** + * compute result(delta) = b - a + */ +void GNUNET_CRYPTO_hash_difference (const GNUNET_HashCode * a, + const GNUNET_HashCode * b, + GNUNET_HashCode * result); + + +/** + * compute result(b) = a + delta + */ +void GNUNET_CRYPTO_hash_sum (const GNUNET_HashCode * a, + const GNUNET_HashCode * delta, + GNUNET_HashCode * result); + + +/** + * compute result = a ^ b + */ +void GNUNET_CRYPTO_hash_xor (const GNUNET_HashCode * a, + const GNUNET_HashCode * b, + GNUNET_HashCode * result); + + +/** + * Convert a hashcode into a key. + */ +void GNUNET_CRYPTO_hash_to_AES_key (const GNUNET_HashCode * hc, + struct GNUNET_CRYPTO_AesSessionKey *skey, + struct + GNUNET_CRYPTO_AesInitializationVector + *iv); + + +/** + * Obtain a bit from a hashcode. + * @param code the GNUNET_CRYPTO_hash to index bit-wise + * @param bit index into the hashcode, [0...159] + * @return Bit \a bit from hashcode \a code, -1 for invalid index + */ +int GNUNET_CRYPTO_hash_get_bit (const GNUNET_HashCode * code, + unsigned int bit); + + +/** + * Compare function for HashCodes, producing a total ordering + * of all hashcodes. + * @return 1 if h1 > h2, -1 if h1 < h2 and 0 if h1 == h2. + */ +int GNUNET_CRYPTO_hash_cmp (const GNUNET_HashCode * h1, + const GNUNET_HashCode * h2); + + +/** + * Find out which of the two GNUNET_CRYPTO_hash codes is closer to target + * in the XOR metric (Kademlia). + * @return -1 if h1 is closer, 1 if h2 is closer and 0 if h1==h2. + */ +int GNUNET_CRYPTO_hash_xorcmp (const GNUNET_HashCode * h1, + const GNUNET_HashCode * h2, + const GNUNET_HashCode * target); + + +/** + * Create a new private key. Caller must free return value. + */ +struct GNUNET_CRYPTO_RsaPrivateKey *GNUNET_CRYPTO_rsa_key_create (void); + + +/** + * Create a new private key by reading it from a file. If the + * files does not exist, create a new key and write it to the + * file. Caller must free return value. Note that this function + * can not guarantee that another process might not be trying + * the same operation on the same file at the same time. The + * caller must somehow know that the file either already exists + * with a valid key OR be sure that no other process is calling + * this function at the same time. If the contents of the file + * are invalid the old file is deleted and a fresh key is + * created. + * + * @return new private key, NULL on error (for example, + * permission denied) + */ +struct GNUNET_CRYPTO_RsaPrivateKey + *GNUNET_CRYPTO_rsa_key_create_from_file (const char *filename); + + +/** + * Deterministically (!) create a private key using only the + * given HashCode as input to the PRNG. + */ +struct GNUNET_CRYPTO_RsaPrivateKey + *GNUNET_CRYPTO_rsa_key_create_from_hash (const GNUNET_HashCode * input); + + +/** + * Free memory occupied by the private key. + * @param hostkey pointer to the memory to free + */ +void GNUNET_CRYPTO_rsa_key_free (struct GNUNET_CRYPTO_RsaPrivateKey *hostkey); + + +/** + * Extract the public key of the host. + * @param result where to write the result. + */ +void GNUNET_CRYPTO_rsa_key_get_public (const struct + GNUNET_CRYPTO_RsaPrivateKey *hostkey, + struct + GNUNET_CRYPTO_RsaPublicKeyBinaryEncoded + *result); + + +/** + * Encrypt a block with the public key of another host that uses the + * same cyper. + * + * @param block the block to encrypt + * @param size the size of block + * @param publicKey the encoded public key used to encrypt + * @param target where to store the encrypted block + * @returns GNUNET_SYSERR on error, GNUNET_OK if ok + */ +int GNUNET_CRYPTO_rsa_encrypt (const void *block, + uint16_t size, + const struct + GNUNET_CRYPTO_RsaPublicKeyBinaryEncoded + *publicKey, + struct GNUNET_CRYPTO_RsaEncryptedData *target); + + +/** + * Decrypt a given block with the hostkey. + * + * @param key the key to use + * @param block the data to decrypt, encoded as returned by encrypt, not consumed + * @param result pointer to a location where the result can be stored + * @param size how many bytes of a result are expected? Must be exact. + * @returns the size of the decrypted block (that is, size) or -1 on error + */ +int GNUNET_CRYPTO_rsa_decrypt (const struct GNUNET_CRYPTO_RsaPrivateKey *key, + const struct GNUNET_CRYPTO_RsaEncryptedData + *block, void *result, uint16_t size); + + +/** + * Sign a given block. + * + * @param key private key to use for the signing + * @param purpose what to sign (size, purpose) + * @param result where to write the signature + * @return GNUNET_SYSERR on error, GNUNET_OK on success + */ +int GNUNET_CRYPTO_rsa_sign (const struct GNUNET_CRYPTO_RsaPrivateKey *key, + const struct GNUNET_CRYPTO_RsaSignaturePurpose + *purpose, + struct GNUNET_CRYPTO_RsaSignature *result); + + +/** + * Verify signature. Note that the caller MUST have already + * checked that "validate->size" bytes are actually available. + * + * @param purpose what is the purpose that validate should have? + * @param validate block to validate (size, purpose, data) + * @param sig signature that is being validated + * @param publicKey public key of the signer + * @returns GNUNET_OK if ok, GNUNET_SYSERR if invalid + */ +int GNUNET_CRYPTO_rsa_verify (uint32_t purpose, + const struct GNUNET_CRYPTO_RsaSignaturePurpose + *validate, + const struct GNUNET_CRYPTO_RsaSignature *sig, + const struct + GNUNET_CRYPTO_RsaPublicKeyBinaryEncoded + *publicKey); + + + +/** + * This function should only be called in testcases + * where strong entropy gathering is not desired + * (for example, for hostkey generation). + */ +void GNUNET_CRYPTO_random_disable_entropy_gathering (void); + +#if 0 /* keep Emacsens' auto-indent happy */ +{ +#endif +#ifdef __cplusplus +} +#endif + + +/* ifndef GNUNET_CRYPTO_LIB_H */ +#endif +/* end of gnunet_crypto_lib.h */ diff --git a/src/include/gnunet_datastore_service.h b/src/include/gnunet_datastore_service.h new file mode 100644 index 000000000..b20c6b100 --- /dev/null +++ b/src/include/gnunet_datastore_service.h @@ -0,0 +1,187 @@ +/* + This file is part of GNUnet + (C) 2004, 2005, 2006, 2007, 2009 Christian Grothoff (and other contributing authors) + + GNUnet is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published + by the Free Software Foundation; either version 2, 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 + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with GNUnet; see the file COPYING. If not, write to the + Free Software Foundation, Inc., 59 Temple Place - Suite 330, + Boston, MA 02111-1307, USA. +*/ + +/** + * @file include/gnunet_datastore_service.h + * @brief API that can be used manage the + * datastore for files stored on a GNUnet node; + * note that the datastore is NOT responsible for + * on-demand encoding, that is achieved using + * a special kind of entry. + * @author Christian Grothoff + */ + +#ifndef GNUNET_DATASTORE_SERVICE_H +#define GNUNET_DATASTORE_SERVICE_H + +#include "gnunet_core.h" + +#ifdef __cplusplus +extern "C" +{ +#if 0 /* keep Emacsens' auto-indent happy */ +} +#endif +#endif + + +/** + * Handle to the datastore service. + */ +struct GNUNET_DATASTORE_Handle; + + +/** + * An iterator over a set of items stored in the datastore. + * + * @param cls closure + * @param key key for the content + * @param size number of bytes in data + * @param data content stored + * @param type type of the content + * @param priority priority of the content + * @param anonymity anonymity-level for the content + * @param expiration expiration time for the content + * @param uid unique identifier for the datum; + * maybe 0 if no unique identifier is available + * + * @return GNUNET_SYSERR to abort the iteration, GNUNET_OK to continue, + * GNUNET_NO to delete the item and continue (if supported) + */ +typedef int (*GNUNET_DATASTORE_Iterator) (void *cls, + const GNUNET_HashCode * key, + uint32_t size, + const void *data, + uint32_t type, + uint32_t priority, + uint32_t anonymity, + struct GNUNET_TIME_Absolute + expiration, unsigned long long uid); + +/** + * Connect to the datastore service. + * + * @param cfg configuration to use + * @param sched scheduler to use + * @return handle to use to access the service + */ +struct GNUNET_DATASTORE_Handle *GNUNET_DATASTORE_connect (struct + GNUNET_CONFIGURATION_Handle + *cfg, + struct + GNUNET_SCHEDULER_Handle + *sched); + + +/** + * Disconnect from the datastore service (and free + * associated resources). + * @param h handle to the datastore + */ +void GNUNET_DATASTORE_disconnect (struct GNUNET_DATASTORE_Handle *h); + + +/** + * Get the current on-disk size of the datastore. + * @param h handle to the datastore + * @return size estimate, -1 if datastore is not available (yet) + */ +unsigned long long GNUNET_DATASTORE_size (struct GNUNET_DATASTORE_Handle *h); + + +/** + * Store an item in the datastore. If the item is already present, + * the priorities are summed up and the higher expiration time and + * lower anonymity level is used. + * + * @param h handle to the datastore + * @param key key for the value + * @param size number of bytes in data + * @param data content stored + * @param type type of the content + * @param priority priority of the content + * @param anonymity anonymity-level for the content + * @param expiration expiration time for the content + */ +void +GNUNET_DATASTORE_put (struct GNUNET_DATASTORE_Handle *h, + const GNUNET_HashCode * key, + uint32_t size, + const void *data, + unit32_t type, + uint32_t priority, + uint32_t anonymity, + struct GNUNET_TIME_Absolute expiration); + +/** + * Iterate over the results for a particular key + * in the datastore. + * + * @param h handle to the datastore + * @param key maybe NULL (to match all entries) + * @param type desired type, 0 for any + * @param iter function to call on each matching value; + * will be called once with a NULL value at the end + * @param iter_cls closure for iter + */ +void +GNUNET_DATASTORE_get (struct GNUNET_DATASTORE_Handle *h, + const GNUNET_HashCode * key, + uint32_t type, + GNUNET_DATASTORE_Iterator iter, void *iter_cls); + + +/** + * Get a random value from the datastore. + * + * @param h handle to the datastore + * @param iter function to call on each matching value; + * will be called exactly once; if no values + * are available, the value will be NULL. + * @param iter_cls closure for iter + */ +void +GNUNET_DATASTORE_get_random (struct GNUNET_DATASTORE_Handle *h, + GNUNET_DATASTORE_Iterator iter, void *iter_cls); + + +/** + * Explicitly remove some content from the database. + * + * @param h handle to the datastore + * @param key key for the value + * @param size number of bytes in data + * @param data content stored + */ +void +GNUNET_DATASTORE_remove (struct GNUNET_DATASTORE_Handle *h, + const GNUNET_HashCode * key, + uint32_t size, const void *data); + + +#if 0 /* keep Emacsens' auto-indent happy */ +{ +#endif +#ifdef __cplusplus +} +#endif + +/* end of gnunet_datastore_service.h */ +#endif diff --git a/src/include/gnunet_directories.h.in b/src/include/gnunet_directories.h.in new file mode 100644 index 000000000..b05b6d9ce --- /dev/null +++ b/src/include/gnunet_directories.h.in @@ -0,0 +1,34 @@ +/* + This file is part of GNUnet. + (C) 2001, 2002, 2003, 2004, 2005, 2006 Christian Grothoff (and other contributing authors) + + GNUnet is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published + by the Free Software Foundation; either version 2, 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 + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with GNUnet; see the file COPYING. If not, write to the + Free Software Foundation, Inc., 59 Temple Place - Suite 330, + Boston, MA 02111-1307, USA. +*/ + +/** + * @file include/gnunet_directories.h + * @brief directories and files in GNUnet (default locations) + * + * @author Christian Grothoff + */ + +#ifndef GNUNET_DIRECTORIES +#define GNUNET_DIRECTORIES + +#define GNUNET_DEFAULT_CLIENT_CONFIG_FILE "@GN_USER_HOME_DIR@/gnunet.conf" +#define GNUNET_DEFAULT_DAEMON_CONFIG_FILE "@GN_DAEMON_CONFIG_DIR@/gnunetd.conf" + +#endif diff --git a/src/include/gnunet_disk_lib.h b/src/include/gnunet_disk_lib.h new file mode 100644 index 000000000..3886be7c9 --- /dev/null +++ b/src/include/gnunet_disk_lib.h @@ -0,0 +1,279 @@ +/* + This file is part of GNUnet. + (C) 2001, 2002, 2003, 2004, 2005, 2006 Christian Grothoff (and other contributing authors) + + GNUnet is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published + by the Free Software Foundation; either version 2, 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 + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with GNUnet; see the file COPYING. If not, write to the + Free Software Foundation, Inc., 59 Temple Place - Suite 330, + Boston, MA 02111-1307, USA. +*/ + +/** + * @file include/gnunet_disk_lib.h + * @brief disk IO apis + */ + +#ifndef GNUNET_DISK_LIB_H +#define GNUNET_DISK_LIB_H + +#include "gnunet_configuration_lib.h" +#include "gnunet_scheduler_lib.h" + +/* we need size_t, and since it can be both unsigned int + or unsigned long long, this IS platform dependent; + but "stdlib.h" should be portable 'enough' to be + unconditionally available... */ +#include + +#ifdef __cplusplus +extern "C" +{ +#if 0 /* keep Emacsens' auto-indent happy */ +} +#endif +#endif + +/** + * Get the number of blocks that are left on the partition that + * contains the given file (for normal users). + * + * @param part a file on the partition to check + * @return -1 on errors, otherwise the number of free blocks + */ +long GNUNET_DISK_get_blocks_available (const char *part); + + +/** + * Check that fil corresponds to a filename + * (of a file that exists and that is not a directory). + * + * @returns GNUNET_YES if yes, GNUNET_NO if not a file, GNUNET_SYSERR if something + * else (will print an error message in that case, too). + */ +int GNUNET_DISK_file_test (const char *fil); + + +/** + * Get the size of the file (or directory) + * of the given file (in bytes). + * + * @param includeSymLinks should symbolic links be + * included? + * + * @return GNUNET_OK on success, GNUNET_SYSERR on error + */ +int GNUNET_DISK_file_size (const char *filename, + unsigned long long *size, int includeSymLinks); + + +/** + * Wrapper around "open()". Opens a file. + * + * @return file handle, -1 on error + */ +int GNUNET_DISK_file_open (const char *filename, int oflag, ...); + + +/** + * Wrapper around "close()". Closes a file. + */ +void GNUNET_DISK_file_close (const char *filename, int fd); + + +/** + * Read the contents of a binary file into a buffer. + * @param fileName the name of the file, not freed, + * must already be expanded! + * @param len the maximum number of bytes to read + * @param result the buffer to write the result to + * @return the number of bytes read on success, -1 on failure + */ +int GNUNET_DISK_file_read (const char *fileName, int len, void *result); + + +/** + * Write a buffer to a file. + * @param fileName the name of the file, NOT freed! + * @param buffer the data to write + * @param n number of bytes to write + * @param mode the mode for file permissions + * @return GNUNET_OK on success, GNUNET_SYSERR on error + */ +int GNUNET_DISK_file_write (const char *fileName, + const void *buffer, unsigned int n, + const char *mode); + + +/** + * Copy a file. + * @return GNUNET_OK on success, GNUNET_SYSERR on error + */ +int GNUNET_DISK_file_copy (const char *src, const char *dst); + + +/** + * Scan a directory for files. The name of the directory + * must be expanded first (!). + * + * @param dirName the name of the directory + * @param callback the method to call for each file + * @param data argument to pass to callback + * @return the number of files found, -1 on error + */ +int GNUNET_DISK_directory_scan (const char *dirName, + GNUNET_FileNameCallback callback, void *data); + + +/** + * Opaque handle used for iterating over a directory. + */ +struct GNUNET_DISK_DirectoryIterator; + + +/** + * Function called to iterate over a directory. + * + * @param cls closure + * @param di argument to pass to "GNUNET_DISK_directory_iterator_next" to + * get called on the next entry (or finish cleanly) + * @param filename complete filename (absolute path) + * @param dirname directory name (absolute path) + */ +typedef void (*GNUNET_DISK_DirectoryIteratorCallback) (void *cls, + struct + GNUNET_DISK_DirectoryIterator + * di, + const char *filename, + const char *dirname); + + +/** + * This function must be called during the DiskIteratorCallback + * (exactly once) to schedule the task to process the next + * filename in the directory (if there is one). + * + * @param iter opaque handle for the iterator + * @param can set to GNUNET_YES to terminate the iteration early + * @return GNUNET_YES if iteration will continue, + * GNUNET_NO if this was the last entry (and iteration is complete), + * GNUNET_SYSERR if "can" was YES + */ +int GNUNET_DISK_directory_iterator_next (struct GNUNET_DISK_DirectoryIterator + *iter, int can); + + +/** + * Scan a directory for files using the scheduler to run a task for + * each entry. The name of the directory must be expanded first (!). + * If a scheduler does not need to be used, GNUNET_DISK_directory_scan + * may provide a simpler API. + * + * @param sched scheduler to use + * @param prio priority to use + * @param dirName the name of the directory + * @param callback the method to call for each file + * @param callback_cls closure for callback + */ +void GNUNET_DISK_directory_iterator_start (struct GNUNET_SCHEDULER_Handle + *sched, + enum GNUNET_SCHEDULER_Priority + prio, const char *dirName, + GNUNET_DISK_DirectoryIteratorCallback + callback, void *callback_cls); + + +/** + * Create the directory structure for storing + * a file. + * + * @param filename name of a file in the directory + * @returns GNUNET_OK on success, GNUNET_SYSERR on failure, + * GNUNET_NO if directory exists but is not writeable + */ +int GNUNET_DISK_directory_create_for_file (const char *filename); + + +/** + * Test if fil is a directory that can be accessed. + * Will not print an error message if the directory + * does not exist. Will log errors if GNUNET_SYSERR is + * returned. + * + * @return GNUNET_YES if yes, GNUNET_NO if does not exist, GNUNET_SYSERR + * on any error and if exists but not directory + */ +int GNUNET_DISK_directory_test (const char *fil); + + +/** + * Remove all files in a directory (rm -rf). Call with + * caution. + * + * @param fileName the file to remove + * @return GNUNET_OK on success, GNUNET_SYSERR on error + */ +int GNUNET_DISK_directory_remove (const char *fileName); + + +/** + * Implementation of "mkdir -p" + * + * @param dir the directory to create + * @returns GNUNET_SYSERR on failure, GNUNET_OK otherwise + */ +int GNUNET_DISK_directory_create (const char *dir); + + +/** + * @brief Removes special characters as ':' from a filename. + * @param fn the filename to canonicalize + */ +void GNUNET_DISK_filename_canonicalize (char *fn); + + +/** + * @brief Change owner of a file + * @param filename file to change + * @param user new owner of the file + * @return GNUNET_OK on success, GNUNET_SYSERR on failure + */ +int GNUNET_DISK_file_change_owner (const char *filename, const char *user); + + +/** + * Construct full path to a file inside of the private + * directory used by GNUnet. Also creates the corresponding + * directory. If the resulting name is supposed to be + * a directory, end the last argument in '/' (or pass + * DIR_SEPARATOR_STR as the last argument before NULL). + * + * @param serviceName name of the service asking + * @param varargs is NULL-terminated list of + * path components to append to the + * private directory name. + * @return the constructed filename + */ +char *GNUNET_DISK_get_home_filename (struct GNUNET_CONFIGURATION_Handle *cfg, + const char *serviceName, ...); + +#if 0 /* keep Emacsens' auto-indent happy */ +{ +#endif +#ifdef __cplusplus +} +#endif + + +/* ifndef GNUNET_DISK_LIB_H */ +#endif +/* end of gnunet_disk_lib.h */ diff --git a/src/include/gnunet_fragmentation_lib.h b/src/include/gnunet_fragmentation_lib.h new file mode 100644 index 000000000..5089fae6a --- /dev/null +++ b/src/include/gnunet_fragmentation_lib.h @@ -0,0 +1,113 @@ +/* + This file is part of GNUnet + (C) 2009 Christian Grothoff (and other contributing authors) + + GNUnet is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published + by the Free Software Foundation; either version 2, 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 + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with GNUnet; see the file COPYING. If not, write to the + Free Software Foundation, Inc., 59 Temple Place - Suite 330, + Boston, MA 02111-1307, USA. +*/ +/** + * @file include/gnunet_fragmentation_lib.h + * @brief library to help fragment messages + * @author Christian Grothoff + */ + +#ifndef GNUNET_FRAGMENTATION_LIB_H +#define GNUNET_FRAGMENTATION_LIB_H + +#include "gnunet_common.h" +#include "gnunet_statistics_service.h" + +#ifdef __cplusplus +extern "C" +{ +#if 0 /* keep Emacsens' auto-indent happy */ +} +#endif +#endif + +/** + * Function that is called with messages + * created by the fragmentation module. + * + * @param cls closure + * @param msg the message that was created + */ +typedef void (*GNUNET_FRAGMENT_MessageProcessor) (void *cls, + const struct + GNUNET_MessageHeader * msg); + + +/** + * Fragment an over-sized message. + * + * @param msg the message to fragment + * @param mtu the maximum message size + * @param proc function to call for each fragment + * @param proc_cls closure for proc + */ +void GNUNET_FRAGMENT_fragment (const struct GNUNET_MessageHeader *msg, + uint16_t mtu, + GNUNET_FRAGMENT_MessageProcessor proc, + void *proc_cls); + +/** + * Defragmentation context. + */ +struct GNUNET_FRAGMENT_Context; + +/** + * Create a defragmentation context. + * + * @param stats statistics context + * @param proc function to call with defragmented messages + * @param proc_cls closure for proc + * @return the defragmentation context + */ +struct GNUNET_FRAGMENT_Context *GNUNET_FRAGMENT_context_create (struct + GNUNET_STATISTICS_Handle + *stats, + GNUNET_FRAGMENT_MessageProcessor + proc, + void + *proc_cls); + + +/** + * Destroy the given defragmentation context. + */ +void GNUNET_FRAGMENT_context_destroy (struct GNUNET_FRAGMENT_Context *ctx); + + +/** + * We have received a fragment. Process it. + * + * @param ctx the context + * @param sender who transmitted the fragment + * @param msg the message that was received + */ +void GNUNET_FRAGMENT_process (struct GNUNET_FRAGMENT_Context *ctx, + const struct GNUNET_PeerIdentity *sender, + const struct GNUNET_MessageHeader *msg); + + +#if 0 /* keep Emacsens' auto-indent happy */ +{ +#endif +#ifdef __cplusplus +} +#endif + +/* end of gnunet_fragmentation_lib.h */ +#endif diff --git a/src/include/gnunet_getopt_lib.h b/src/include/gnunet_getopt_lib.h new file mode 100644 index 000000000..722155568 --- /dev/null +++ b/src/include/gnunet_getopt_lib.h @@ -0,0 +1,251 @@ +/* + This file is part of GNUnet. + (C) 2001, 2002, 2003, 2004, 2005, 2006, 2009 Christian Grothoff (and other contributing authors) + + GNUnet is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published + by the Free Software Foundation; either version 2, 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 + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with GNUnet; see the file COPYING. If not, write to the + Free Software Foundation, Inc., 59 Temple Place - Suite 330, + Boston, MA 02111-1307, USA. +*/ + +/** + * @file include/gnunet_getopt_lib.h + * @brief command line parsing and --help formatting + * + * @author Christian Grothoff + */ + +#ifndef GNUNET_GETOPT_LIB_H +#define GNUNET_GETOPT_LIB_H + +#ifdef __cplusplus +extern "C" +{ +#if 0 /* keep Emacsens' auto-indent happy */ +} +#endif +#endif + +#include "gnunet_configuration_lib.h" + +/** + * @brief General context for command line processors. + */ +struct GNUNET_GETOPT_CommandLineProcessorContext +{ + + /** + * Name of the application + */ + const char *binaryName; + + /** + * Name of application with option summary + */ + const char *binaryOptions; + + /** + * Array with all command line options. + */ + const struct GNUNET_GETOPT_CommandLineOption *allOptions; + + /** + * For configuration + */ + struct GNUNET_CONFIGURATION_Handle *cfg; + + /** + * Original command line + */ + char *const *argv; + + /** + * Total number of argv's. + */ + unsigned int argc; + + /** + * Current argument. + */ + unsigned int currentArgument; + +}; + +/** + * @brief Process a command line option + * + * @param ctx context for all options + * @param scls specific closure (for this processor) + * @param option long name of the option (i.e. "config" for --config) + * @param value argument, NULL if none was given + * @return GNUNET_OK to continue processing other options, GNUNET_SYSERR to abort + */ +typedef + int (*GNUNET_GETOPT_CommandLineOptionProcessor) (struct + GNUNET_GETOPT_CommandLineProcessorContext + * ctx, void *scls, + const char *option, + const char *value); + +/** + * @brief Definition of a command line option. + */ +struct GNUNET_GETOPT_CommandLineOption +{ + + /** + * Short name of the option (use '\0' for none). + */ + const char shortName; + + /** + * Long name of the option (may not be NULL) + */ + const char *name; + + /** + * Name of the argument for the user in help text + */ + const char *argumentHelp; + + /** + * Help text for the option (description) + */ + const char *description; + + /** + * Is an argument required? 0: GNUNET_NO (includes optional), 1: GNUNET_YES. + */ + int require_argument; + + /** + * Handler for the option. + */ + GNUNET_GETOPT_CommandLineOptionProcessor processor; + + /** + * Specific closure to pass to the processor. + */ + void *scls; + +}; + +/** + * Macro defining the option to print the command line + * help text. + * + * @param about string with brief description of the application + */ +#define GNUNET_GETOPT_OPTION_HELP(about) \ + { 'h', "help", (const char *) NULL, gettext_noop("print this help"), 0, &GNUNET_GETOPT_format_help_, (void *) about } + +/** + * Macro defining the option to print the version of + * the application + * + * @param version string with the version number + */ +#define GNUNET_GETOPT_OPTION_VERSION(version) \ + { 'v', "version", (const char *) NULL, gettext_noop("print the version number"), 0, &GNUNET_GETOPT_print_version_, (void *) version } + +/** + * Get the log level + */ +#define GNUNET_GETOPT_OPTION_LOGFILE(logfn) \ + { 'l', "logfile", "LOGFILE", gettext_noop("configure logging to write logs to LOGFILE"), 1, &GNUNET_GETOPT_set_string, (void *) logfn } + +/** + * Set the configuration option for logging. + */ +#define GNUNET_GETOPT_OPTION_LOGLEVEL(loglev) \ + { 'L', "log", "LOGLEVEL", gettext_noop("configure logging to use LOGLEVEL"), 1, &GNUNET_GETOPT_set_string, (void *) loglev } + +/** + * Get number of verbose flags + */ +#define GNUNET_GETOPT_OPTION_VERBOSE(level) \ + { 'V', "verbose", (const char *) NULL, gettext_noop("be verbose"), 0, &GNUNET_GETOPT_increment_value, (void *) level } + +/** + * Get configuration file name + */ +#define GNUNET_GETOPT_OPTION_CFG_FILE(fn) \ + { 'c', "config", "FILENAME", gettext_noop("use configuration file FILENAME"), 1, &GNUNET_GETOPT_set_string, (void *) fn } + +/** + * Marker to end the list of options. + */ +#define GNUNET_GETOPT_OPTION_END \ + { '\0', NULL, NULL, NULL, 0, NULL, NULL } + +/** + * Parse the command line. + * + * @param binaryName name of the binary / application with options + * @param cfg for storing/accessing configuration data + * @param allOptions defined options and handlers + * @param argc number of arguments + * @param argv actual arguments + * @return index into argv with first non-option + * argument, or GNUNET_SYSERR on error + */ +int GNUNET_GETOPT_run (const char *binaryName, + struct GNUNET_CONFIGURATION_Handle *cfg, + const struct GNUNET_GETOPT_CommandLineOption + *allOptions, unsigned int argc, char *const *argv); + +int GNUNET_GETOPT_set_ulong (struct GNUNET_GETOPT_CommandLineProcessorContext + *ctx, void *scls, const char *option, + const char *value); + +int GNUNET_GETOPT_set_uint (struct GNUNET_GETOPT_CommandLineProcessorContext + *ctx, void *scls, const char *option, + const char *value); + +int GNUNET_GETOPT_set_one (struct GNUNET_GETOPT_CommandLineProcessorContext + *ctx, void *scls, const char *option, + const char *value); + +int GNUNET_GETOPT_set_string (struct GNUNET_GETOPT_CommandLineProcessorContext + *ctx, void *scls, const char *option, + const char *value); + +int +GNUNET_GETOPT_increment_value (struct + GNUNET_GETOPT_CommandLineProcessorContext *ctx, + void *scls, const char *option, + const char *value); + +/* *************** internal prototypes - use macros above! ************* */ + +int GNUNET_GETOPT_format_help_ (struct + GNUNET_GETOPT_CommandLineProcessorContext + *ctx, void *scls, const char *option, + const char *value); + +int GNUNET_GETOPT_print_version_ (struct + GNUNET_GETOPT_CommandLineProcessorContext + *ctx, void *scls, const char *option, + const char *value); + +#if 0 /* keep Emacsens' auto-indent happy */ +{ +#endif +#ifdef __cplusplus +} +#endif + + +/* ifndef GNUNET_GETOPT_LIB_H */ +#endif +/* end of gnunet_getopt_lib.h */ diff --git a/src/include/gnunet_hello_lib.h b/src/include/gnunet_hello_lib.h new file mode 100644 index 000000000..2ba6df6ab --- /dev/null +++ b/src/include/gnunet_hello_lib.h @@ -0,0 +1,201 @@ +/* + This file is part of GNUnet. + (C) 2001, 2002, 2003, 2004, 2005, 2006 Christian Grothoff (and other contributing authors) + + GNUnet is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published + by the Free Software Foundation; either version 2, 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 + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with GNUnet; see the file COPYING. If not, write to the + Free Software Foundation, Inc., 59 Temple Place - Suite 330, + Boston, MA 02111-1307, USA. +*/ + +/** + * @file include/gnunet_hello_lib.h + * @brief helper library for handling HELLOs + * @author Christian Grothoff + */ + +#ifndef GNUNET_HELLO_LIB_H +#define GNUNET_HELLO_LIB_H + +#ifdef __cplusplus +extern "C" +{ +#if 0 /* keep Emacsens' auto-indent happy */ +} +#endif +#endif + +#include "gnunet_common.h" +#include "gnunet_crypto_lib.h" + +/** + * A HELLO message is used to exchange information about + * transports with other peers. This struct is guaranteed + * to start with a "GNUNET_MessageHeader", everything else + * should be internal to the HELLO library. + */ +struct GNUNET_HELLO_Message; + + +/** + * Copy the given address information into + * the given buffer using the format of HELLOs. + * + * @param tname name of the transport plugin + * @param expiration expiration for the address + * @param addr the address + * @param addr_len length of the address in bytes + * @param target where to copy the address + * @param max maximum number of bytes to copy to target + * @return number of bytes copied, 0 if + * the target buffer was not big enough. + */ +size_t +GNUNET_HELLO_add_address (const char *tname, + struct GNUNET_TIME_Absolute expiration, + const void *addr, + size_t addr_len, char *target, size_t max); + + +/** + * Callback function used to fill a buffer of max bytes with a list of + * addresses in the format used by HELLOs. Should use + * "GNUNET_HELLO_add_address" as a helper function. + * + * @param cls closure + * @param max maximum number of bytes that can be written to buf + * @param buf where to write the address information + * @return number of bytes written, 0 to signal the + * end of the iteration. + */ +typedef size_t + (*GNUNET_HELLO_GenerateAddressListCallback) (void *cls, + size_t max, void *buf); + + +/** + * Construct a HELLO message given the public key, + * expiration time and an iterator that spews the + * transport addresses. + * + * @return the hello message + */ +struct GNUNET_HELLO_Message *GNUNET_HELLO_create (const struct + GNUNET_CRYPTO_RsaPublicKeyBinaryEncoded + *publicKey, + GNUNET_HELLO_GenerateAddressListCallback + addrgen, void *addrgen_cls); + + +/** + * Return the size of the given HELLO message. + * @param hello to inspect + * @return the size, 0 if HELLO is invalid + */ +uint16_t GNUNET_HELLO_size (const struct GNUNET_HELLO_Message *hello); + + +/** + * Construct a HELLO message by merging the + * addresses in two existing HELLOs (which + * must be for the same peer). + * + * @param h1 first HELLO message + * @param h2 the second HELLO message + * @return the combined hello message + */ +struct GNUNET_HELLO_Message *GNUNET_HELLO_merge (const struct + GNUNET_HELLO_Message *h1, + const struct + GNUNET_HELLO_Message *h2); + + +/** + * Iterator callback to go over all addresses. + * + * @param cls closure + * @param tname name of the transport + * @param expiration expiration time + * @param addr the address + * @param addrlen length of the address + * @return GNUNET_OK to keep the address, + * GNUNET_NO to delete it from the HELLO + * GNUNET_SYSERR to stop iterating (but keep current address) + */ +typedef int + (*GNUNET_HELLO_AddressIterator) (void *cls, + const char *tname, + struct GNUNET_TIME_Absolute expiration, + const void *addr, size_t addrlen); + + +/** + * Iterate over all of the addresses in the HELLO. + * + * @param msg HELLO to iterate over; client does not need to + * have verified that msg is well-formed (beyond starting + * with a GNUNET_MessageHeader of the right type). + * @param return_modified if a modified copy should be returned, + * otherwise NULL will be returned + * @param it iterator to call on each address + * @param it_cls closure for it + */ +struct GNUNET_HELLO_Message *GNUNET_HELLO_iterate_addresses (const struct + GNUNET_HELLO_Message + *msg, + int + return_modified, + GNUNET_HELLO_AddressIterator + it, + void *it_cls); + + +/** + * Iterate over addresses in "new_hello" that + * are NOT already present in "old_hello". + * + * @param new_hello a HELLO message + * @param old_hello a HELLO message + * @param expiration_limit ignore addresses in old_hello + * that expired before the given time stamp + * @param it iterator to call on each address + * @param it_cls closure for it + */ +void +GNUNET_HELLO_iterate_new_addresses (const struct GNUNET_HELLO_Message + *new_hello, + const struct GNUNET_HELLO_Message + *old_hello, + struct GNUNET_TIME_Absolute + expiration_limit, + GNUNET_HELLO_AddressIterator it, + void *it_cls); + + +/** + * Get the public key from a HELLO message. + * + * @param hello the hello message + * @param publicKey where to copy the public key information, can be NULL + * @return GNUNET_SYSERR if the HELLO was malformed + */ +int +GNUNET_HELLO_get_key (const struct GNUNET_HELLO_Message *hello, + struct GNUNET_CRYPTO_RsaPublicKeyBinaryEncoded + *publicKey); + + + +/* ifndef GNUNET_HELLO_LIB_H */ +#endif +/* end of gnunet_hello_lib.h */ diff --git a/src/include/gnunet_network_lib.h b/src/include/gnunet_network_lib.h new file mode 100644 index 000000000..8731eacec --- /dev/null +++ b/src/include/gnunet_network_lib.h @@ -0,0 +1,308 @@ +/* + This file is part of GNUnet. + (C) 2009 Christian Grothoff (and other contributing authors) + + GNUnet is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published + by the Free Software Foundation; either version 2, 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 + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with GNUnet; see the file COPYING. If not, write to the + Free Software Foundation, Inc., 59 Temple Place - Suite 330, + Boston, MA 02111-1307, USA. +*/ + +/** + * @file include/gnunet_network_lib.h + * @brief basic, low-level TCP networking interface + * @author Christian Grothoff + */ +#ifndef GNUNET_NETWORK_LIB_H +#define GNUNET_NETWORK_LIB_H + +#ifdef __cplusplus +extern "C" +{ +#if 0 /* keep Emacsens' auto-indent happy */ +} +#endif +#endif + +#include "gnunet_scheduler_lib.h" +#include "gnunet_time_lib.h" + +/** + * Timeout we use on TCP connect before trying another + * result from the DNS resolver. 5s. + */ +#define GNUNET_NETWORK_CONNECT_RETRY_TIMEOUT GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS, 5) + +/** + * @brief handle for a network socket + */ +struct GNUNET_NETWORK_SocketHandle; + + +/** + * Function to call for access control checks. + * + * @param cls closure + * @param addr address + * @param addrlen length of address + * @return GNUNET_YES to allow, GNUNET_NO to deny, GNUNET_SYSERR + * for unknown address family (will be denied). + */ +typedef int (*GNUNET_NETWORK_AccessCheck) (void *cls, + const struct sockaddr * addr, + socklen_t addrlen); + + +/** + * Callback function for data received from the network. Note that + * both "available" and "err" would be 0 if the read simply timed out. + * + * @param cls closure + * @param buf pointer to received data + * @param available number of bytes availabe in "buf", + * possibly 0 (on errors) + * @param addr address of the sender + * @param addrlen size of addr + * @param errCode value of errno (on errors receiving) + */ +typedef void (*GNUNET_NETWORK_Receiver) (void *cls, + const void *buf, + size_t available, + const struct sockaddr * addr, + socklen_t addrlen, int errCode); + + +/** + * Create a socket handle by boxing an existing OS socket. The OS + * socket should henceforth be no longer used directly. + * GNUNET_socket_destroy will close it. + * + * @param sched scheduler to use + * @param osSocket existing socket to box + * @param maxbuf maximum write buffer size for the socket (use + * 0 for sockets that need no write buffers, such as listen sockets) + * @return the boxed socket handle + */ +struct GNUNET_NETWORK_SocketHandle + *GNUNET_NETWORK_socket_create_from_existing (struct GNUNET_SCHEDULER_Handle + *sched, int osSocket, + size_t maxbuf); + + +/** + * Create a socket handle by accepting on a listen socket. This + * function may block if the listen socket has no connection ready. + * + * @param sched scheduler to use + * @param access function to use to check if access is allowed + * @param access_cls closure for access + * @param lsock listen socket + * @param maxbuf maximum write buffer size for the socket (use + * 0 for sockets that need no write buffers, such as listen sockets) + * @return the socket handle, NULL on error (for example, access refused) + */ +struct GNUNET_NETWORK_SocketHandle + *GNUNET_NETWORK_socket_create_from_accept (struct GNUNET_SCHEDULER_Handle + *sched, + GNUNET_NETWORK_AccessCheck + access, void *access_cls, + int lsock, size_t maxbuf); + + +/** + * Create a socket handle by (asynchronously) connecting to a host. + * This function returns immediately, even if the connection has not + * yet been established. This function only creates TCP connections. + * + * @param sched scheduler to use + * @param hostname name of the host to connect to + * @param port port to connect to + * @param maxbuf maximum write buffer size for the socket (use + * 0 for sockets that need no write buffers, such as listen sockets) + * @return the socket handle + */ +struct GNUNET_NETWORK_SocketHandle + *GNUNET_NETWORK_socket_create_from_connect (struct GNUNET_SCHEDULER_Handle + *sched, const char *hostname, + uint16_t port, size_t maxbuf); + + + +/** + * Create a socket handle by (asynchronously) connecting to a host. + * This function returns immediately, even if the connection has not + * yet been established. This function only creates TCP connections. + * + * @param sched scheduler to use + * @param af_family address family to use + * @param serv_addr server address + * @param addrlen length of server address + * @param maxbuf maximum write buffer size for the socket (use + * 0 for sockets that need no write buffers, such as listen sockets) + * @return the socket handle + */ +struct GNUNET_NETWORK_SocketHandle + *GNUNET_NETWORK_socket_create_from_sockaddr (struct GNUNET_SCHEDULER_Handle + *sched, int af_family, + const struct sockaddr + *serv_addr, socklen_t addrlen, + size_t maxbuf); + +/** + * Check if socket is valid (no fatal errors have happened so far). + * Note that a socket that is still trying to connect is considered + * valid. + * + * @param sock socket to check + * @return GNUNET_YES if valid, GNUNET_NO otherwise + */ +int GNUNET_NETWORK_socket_check (struct GNUNET_NETWORK_SocketHandle *sock); + + +/** + * Obtain the network address of the other party. + * + * @param sock the client to get the address for + * @param addr where to store the address + * @param addrlen where to store the length of the address + * @return GNUNET_OK on success + */ +int GNUNET_NETWORK_socket_get_address (struct GNUNET_NETWORK_SocketHandle + *sock, void **addr, size_t * addrlen); + +/** + * Close the socket and free associated resources. Pending + * transmissions are simply dropped. A pending receive call will be + * called with an error code of "EPIPE". + * + * @param sock socket to destroy + */ +void GNUNET_NETWORK_socket_destroy (struct GNUNET_NETWORK_SocketHandle *sock); + + +/** + * Receive data from the given socket. Note that this function will + * call "receiver" asynchronously using the scheduler. It will + * "immediately" return. Note that there MUST only be one active + * receive call per socket at any given point in time (so do not + * call receive again until the receiver callback has been invoked). + * + * @param sock socket handle + * @param max maximum number of bytes to read + * @param timeout maximum amount of time to wait + * @param receiver function to call with received data + * @param receiver_cls closure for receiver + * @return scheduler task ID used for receiving, GNUNET_SCHEDULER_NO_PREREQUISITE_TASK on error + */ +GNUNET_SCHEDULER_TaskIdentifier +GNUNET_NETWORK_receive (struct GNUNET_NETWORK_SocketHandle *sock, + size_t max, + struct GNUNET_TIME_Relative timeout, + GNUNET_NETWORK_Receiver receiver, void *receiver_cls); + + +/** + * Cancel receive job on the given socket. Note that the + * receiver callback must not have been called yet in order + * for the cancellation to be valid. + * + * @param sock socket handle + * @param task task identifier returned from the receive call + * @return closure of the original receiver callback + */ +void *GNUNET_NETWORK_receive_cancel (struct GNUNET_NETWORK_SocketHandle *sock, + GNUNET_SCHEDULER_TaskIdentifier task); + + +/** + * Function called to notify a client about the socket + * begin ready to queue more data. "buf" will be + * NULL and "size" zero if the socket was closed for + * writing in the meantime. + * + * @param cls closure + * @param size number of bytes available in buf + * @param buf where the callee should write the message + * @return number of bytes written to buf + */ +typedef size_t (*GNUNET_NETWORK_TransmitReadyNotify) (void *cls, + size_t size, void *buf); + + +/** + * Opaque handle that can be used to cancel + * a transmit-ready notification. + */ +struct GNUNET_NETWORK_TransmitHandle; + +/** + * Ask the socket to call us once the specified number of bytes + * are free in the transmission buffer. May call the notify + * method immediately if enough space is available. Note that + * this function will abort if "size" is greater than + * "maxbuf" (as specified when the socket handle was created). + * + * Note that "notify" will be called either when enough + * buffer space is available OR when the socket is destroyed. + * The size parameter given to notify is guaranteed to be + * larger or equal to size if the buffer is ready, or zero + * if the socket was destroyed (or at least closed for + * writing). Finally, any time before 'notify' is called, a + * client may call "notify_transmit_ready_cancel" to cancel + * the transmission request. + * + * Only one transmission request can be scheduled at the same + * time. Notify will be run with the same scheduler priority + * as that of the caller. + * + * @param sock socket + * @param size number of bytes to send + * @param timeout after how long should we give up (and call + * notify with buf NULL and size 0)? + * @param notify function to call when buffer space is available + * @param notify_cls closure for notify + * @return non-NULL if the notify callback was queued, + * NULL if we are already going to notify someone else (busy) + */ +struct GNUNET_NETWORK_TransmitHandle + *GNUNET_NETWORK_notify_transmit_ready (struct GNUNET_NETWORK_SocketHandle + *sock, size_t size, + struct GNUNET_TIME_Relative timeout, + GNUNET_NETWORK_TransmitReadyNotify + notify, void *notify_cls); + + +/** + * Cancel the specified transmission-ready + * notification. + * + * @param h handle for notification to cancel + */ +void +GNUNET_NETWORK_notify_transmit_ready_cancel (struct + GNUNET_NETWORK_TransmitHandle + *h); + + + +#if 0 /* keep Emacsens' auto-indent happy */ +{ +#endif +#ifdef __cplusplus +} +#endif + + +/* ifndef GNUNET_NETWORK_LIB_H */ +#endif +/* end of gnunet_network_lib.h */ diff --git a/src/include/gnunet_os_lib.h b/src/include/gnunet_os_lib.h new file mode 100644 index 000000000..dfab4a747 --- /dev/null +++ b/src/include/gnunet_os_lib.h @@ -0,0 +1,158 @@ +/* + This file is part of GNUnet. + (C) 2001, 2002, 2003, 2004, 2005, 2006 Christian Grothoff (and other contributing authors) + + GNUnet is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published + by the Free Software Foundation; either version 2, 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 + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with GNUnet; see the file COPYING. If not, write to the + Free Software Foundation, Inc., 59 Temple Place - Suite 330, + Boston, MA 02111-1307, USA. +*/ + +/** + * @file include/gnunet_os_lib.h + * @brief low level process routines + * @author Christian Grothoff + * @author Krista Bennett + * @author Gerd Knorr + * @author Ioana Patrascu + * @author Tzvetan Horozov + * @author Milan + */ + +#ifndef GNUNET_OS_LIB_H +#define GNUNET_OS_LIB_H + +#ifdef __cplusplus +extern "C" +{ +#if 0 /* keep Emacsens' auto-indent happy */ +} +#endif +#endif + +#include "gnunet_common.h" +#include "gnunet_configuration_lib.h" +#include "gnunet_scheduler_lib.h" + + +/** + * Possible installation paths to request + */ +enum GNUNET_OS_InstallationPathKind +{ + GNUNET_OS_IPK_PREFIX, + GNUNET_OS_IPK_BINDIR, + GNUNET_OS_IPK_LIBDIR, + GNUNET_OS_IPK_DATADIR, + GNUNET_OS_IPK_LOCALEDIR, + GNUNET_OS_IPK_SELF_PREFIX +}; + + +/** + * Get the path to a specific GNUnet installation directory or, with + * GNUNET_OS_IPK_SELF_PREFIX, the current running apps installation + * directory. + * + * @param dirkind what kind of directory is desired? + * @return a pointer to the dir path (to be freed by the caller) + */ +char *GNUNET_OS_installation_get_path (enum GNUNET_OS_InstallationPathKind + dirkind); + + +/** + * Callback function invoked for each interface found. + * + * @param cls closure + * @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 addrlen length of the address + * @return GNUNET_OK to continue iteration, GNUNET_SYSERR to abort + */ +typedef int (*GNUNET_OS_NetworkInterfaceProcessor) (void *cls, + const char *name, + int isDefault, + const struct sockaddr * + addr, socklen_t addrlen); + + +/** + * @brief Enumerate all network interfaces + * @param callback the callback function + */ +void GNUNET_OS_network_interfaces_list (GNUNET_OS_NetworkInterfaceProcessor + proc, void *cls); + +/** + * Get the current CPU load. + * + * @param cfg to determine acceptable load level (LOAD::MAXCPULOAD) + * @return -1 on error, otherwise load value (between 0 and 100, + * (100 is equivalent to full load for one CPU) + */ +int GNUNET_OS_load_cpu_get (struct GNUNET_CONFIGURATION_Handle *cfg); + +/** + * Get the current IO load. + * + * @param cfg to determine acceptable load level (LOAD::MAXIOLOAD) + * @return -1 on error, otherwise load value (between 0 and 100, + * 100 means that we spend all of our cycles waiting for + * the disk) + */ +int GNUNET_OS_load_disk_get (struct GNUNET_CONFIGURATION_Handle *cfg); + + +/** + * Set process priority + * + * @param proc id of the process + * @param prio priority value + * @return GNUNET_OK on success, GNUNET_SYSERR on error + */ +int GNUNET_OS_set_process_priority (pid_t proc, + enum GNUNET_SCHEDULER_Priority prio); + + +/** + * Start a process. + * + * @param filename name of the binary + * @param ... NULL-terminated list of arguments to the process + * @return process ID of the new process, -1 on error + */ +pid_t GNUNET_OS_start_process (const char *filename, ...); + +/** + * Start a process. + * + * @param filename name of the binary + * @param argv NULL-terminated list of arguments to the process, + * including the process name as the first argument + * @return process ID of the new process, -1 on error + */ +pid_t GNUNET_OS_start_process_v (const char *filename, char *const argv[]); + +#if 0 /* keep Emacsens' auto-indent happy */ +{ +#endif +#ifdef __cplusplus +} +#endif + + +/* ifndef GNUNET_OS_LIB_H */ +#endif +/* end of gnunet_os_lib.h */ diff --git a/src/include/gnunet_peerinfo_service.h b/src/include/gnunet_peerinfo_service.h new file mode 100644 index 000000000..45c52c447 --- /dev/null +++ b/src/include/gnunet_peerinfo_service.h @@ -0,0 +1,111 @@ +/* + This file is part of GNUnet + (C) 2009 Christian Grothoff (and other contributing authors) + + GNUnet is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published + by the Free Software Foundation; either version 2, 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 + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with GNUnet; see the file COPYING. If not, write to the + Free Software Foundation, Inc., 59 Temple Place - Suite 330, + Boston, MA 02111-1307, USA. +*/ +/** + * @file include/gnunet_peerinfo_service.h + * @brief Code to maintain the list of currently known hosts + * (in memory structure of data/hosts) and their trust ratings + * (in memory structure of data/trust) + * @author Christian Grothoff + */ + +#ifndef GNUNET_PEERINFO_SERVICE_H +#define GNUNET_PEERINFO_SERVICE_H + +#include "gnunet_common.h" +#include "gnunet_configuration_lib.h" +#include "gnunet_crypto_lib.h" +#include "gnunet_hello_lib.h" + +#ifdef __cplusplus +extern "C" +{ +#if 0 /* keep Emacsens' auto-indent happy */ +} +#endif +#endif + + +/** + * Add a host to the persistent list. + * + * @param cfg configuration to use + * @param sched scheduler to use + * @param peer identity of the peer + * @param hello the verified (!) HELLO message + */ +void +GNUNET_PEERINFO_add_peer (struct GNUNET_CONFIGURATION_Handle *cfg, + struct GNUNET_SCHEDULER_Handle *sched, + const struct GNUNET_PeerIdentity *peer, + const struct GNUNET_HELLO_Message *hello); + +/** + * Type of an iterator over the hosts. Note that each + * host will be called with each available protocol. + * + * @param cls closure + * @param peer id of the peer, NULL for last call + * @param hello hello message for the peer (can be NULL) + * @param trust amount of trust we have in the peer + */ +typedef void + (*GNUNET_PEERINFO_Processor) (void *cls, + const struct GNUNET_PeerIdentity * peer, + const struct GNUNET_HELLO_Message * hello, + uint32_t trust); + + +/** + * Call a method for each known matching host and change + * its trust value. The method will be invoked once for + * each host and then finally once with a NULL pointer. + * Note that the last call can be triggered by timeout or + * by simply being done; however, the trust argument will + * be set to zero if we are done, 1 if we timed out and + * 2 for fatal error. + * + * @param cfg configuration to use + * @param sched scheduler to use + * @param peer restrict iteration to this peer only (can be NULL) + * @param trust_delta how much to change the trust in all matching peers + * @param timeout how long to wait until timing out + * @param callback the method to call for each peer + * @param callback_cls closure for callback + */ +void +GNUNET_PEERINFO_for_all (struct GNUNET_CONFIGURATION_Handle *cfg, + struct GNUNET_SCHEDULER_Handle *sched, + const struct GNUNET_PeerIdentity *peer, + int trust_delta, + struct GNUNET_TIME_Relative timeout, + GNUNET_PEERINFO_Processor callback, + void *callback_cls); + + +#if 0 /* keep Emacsens' auto-indent happy */ +{ +#endif +#ifdef __cplusplus +} +#endif + + +/* end of gnunet_peerinfo_service.h */ +#endif diff --git a/src/include/gnunet_plugin_lib.h b/src/include/gnunet_plugin_lib.h new file mode 100644 index 000000000..e4f603c4e --- /dev/null +++ b/src/include/gnunet_plugin_lib.h @@ -0,0 +1,84 @@ +/* + This file is part of GNUnet. + (C) 2001, 2002, 2003, 2004, 2005, 2006 Christian Grothoff (and other contributing authors) + + GNUnet is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published + by the Free Software Foundation; either version 2, 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 + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with GNUnet; see the file COPYING. If not, write to the + Free Software Foundation, Inc., 59 Temple Place - Suite 330, + Boston, MA 02111-1307, USA. +*/ + +/** + * @file include/gnunet_plugin_lib.h + * @brief plugin loading and unloading + * @author Christian Grothoff + */ + +#ifndef GNUNET_PLUGIN_LIB_H +#define GNUNET_PLUGIN_LIB_H + +#ifdef __cplusplus +extern "C" +{ +#if 0 /* keep Emacsens' auto-indent happy */ +} +#endif +#endif + +#include "gnunet_common.h" +#include "gnunet_configuration_lib.h" + + +/** + * Signature of any function exported by a plugin. + */ +typedef void *(*GNUNET_PLUGIN_Callback) (void *arg); + + +/** + * Setup plugin (runs the "init" callback and returns whatever "init" + * returned). If "init" returns NULL, the plugin is unloaded. + * + * Note that the library must export symbols called + * "library_name_init" and "library_name_done". These will be called + * when the library is loaded and unloaded respectively. + * + * @param library_name name of the plugin to load + * @param arg argument to the plugin initialization function + * @return whatever the initialization function returned + */ +void *GNUNET_PLUGIN_load (const char *library_name, void *arg); + + +/** + * Unload plugin (runs the "done" callback and returns whatever "done" + * returned). The plugin is then unloaded. + * + * @param library_name name of the plugin to unload + * @param arg argument to the plugin shutdown function + * @return whatever the shutdown function returned + */ +void *GNUNET_PLUGIN_unload (const char *library_name, void *arg); + + +#if 0 /* keep Emacsens' auto-indent happy */ +{ +#endif +#ifdef __cplusplus +} +#endif + + +/* ifndef GNUNET_PLUGIN_LIB_H */ +#endif +/* end of gnunet_plugin_lib.h */ diff --git a/src/include/gnunet_program_lib.h b/src/include/gnunet_program_lib.h new file mode 100644 index 000000000..fcd00b8ef --- /dev/null +++ b/src/include/gnunet_program_lib.h @@ -0,0 +1,90 @@ +/* + This file is part of GNUnet. + (C) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2009 Christian Grothoff (and other contributing authors) + + GNUnet is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published + by the Free Software Foundation; either version 2, 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 + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with GNUnet; see the file COPYING. If not, write to the + Free Software Foundation, Inc., 59 Temple Place - Suite 330, + Boston, MA 02111-1307, USA. +*/ + +/** + * @file include/gnunet_program_lib.h + * @brief functions related to starting programs + * @author Christian Grothoff + */ + +#ifndef GNUNET_PROGRAM_LIB_H +#define GNUNET_PROGRAM_LIB_H + +#ifdef __cplusplus +extern "C" +{ +#if 0 /* keep Emacsens' auto-indent happy */ +} +#endif +#endif + +#include "gnunet_configuration_lib.h" +#include "gnunet_getopt_lib.h" +#include "gnunet_scheduler_lib.h" + +/** + * Main function that will be run. + * + * @param cls closure + * @param sched the scheduler to use + * @param args remaining command-line arguments + * @param cfgfile name of the configuration file used (for saving, can be NULL!) + * @param cfg configuration + */ +typedef void (*GNUNET_PROGRAM_Main) (void *cls, + struct GNUNET_SCHEDULER_Handle * sched, + char *const *args, + const char *cfgfile, + struct GNUNET_CONFIGURATION_Handle * + cfg); + + +/** + * Run a standard GNUnet command startup sequence (initialize loggers + * and configuration, parse options). + * + * @param argc number of command line arguments + * @param argv command line arguments + * @param binaryName our expected name + * @param binaryHelp helptext for "-h" option (about the app) + * @param options command line options + * @param task main function to run + * @param task_cls closure for task + * @return GNUNET_SYSERR on error, GNUNET_OK on success + */ +int GNUNET_PROGRAM_run (int argc, + char *const *argv, + const char *binaryName, + const char *binaryHelp, + const struct GNUNET_GETOPT_CommandLineOption *options, + GNUNET_PROGRAM_Main task, void *task_cls); + + + +#if 0 /* keep Emacsens' auto-indent happy */ +{ +#endif +#ifdef __cplusplus +} +#endif + +/* ifndef GNUNET_PROGRAM_LIB_H */ +#endif +/* end of gnunet_program_lib.h */ diff --git a/src/include/gnunet_protocols.h b/src/include/gnunet_protocols.h new file mode 100644 index 000000000..c86ad83ce --- /dev/null +++ b/src/include/gnunet_protocols.h @@ -0,0 +1,319 @@ +/* + This file is part of GNUnet. + (C) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2009 Christian Grothoff (and other contributing authors) + + GNUnet is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published + by the Free Software Foundation; either version 2, 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 + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with GNUnet; see the file COPYING. If not, write to the + Free Software Foundation, Inc., 59 Temple Place - Suite 330, + Boston, MA 02111-1307, USA. +*/ + +/** + * @file include/gnunet_protocols.h + * @brief constants for network protocols + * @author Christian Grothoff + */ + +#ifndef GNUNET_PROTOCOLS_H +#define GNUNET_PROTOCOLS_H + +#ifdef __cplusplus +extern "C" +{ +#if 0 /* keep Emacsens' auto-indent happy */ +} +#endif +#endif + +/** + * Test if service is online. + */ +#define GNUNET_MESSAGE_TYPE_TEST 0 + +/** + * Request service shutdown. + */ +#define GNUNET_MESSAGE_TYPE_SHUTDOWN 1 + + +/** + * Request DNS resolution. + */ +#define GNUNET_MESSAGE_TYPE_RESOLVER_REQUEST 2 + +/** + * Response to a DNS resolution request. + */ +#define GNUNET_MESSAGE_TYPE_RESOLVER_RESPONSE 3 + + +/** + * Set a statistical value. + */ +#define GNUNET_MESSAGE_TYPE_STATISTICS_SET 4 + +/** + * Get a statistical value(s). + */ +#define GNUNET_MESSAGE_TYPE_STATISTICS_GET 5 + +/** + * Response to a STATISTICS_GET message (with value). + */ +#define GNUNET_MESSAGE_TYPE_STATISTICS_VALUE 6 + +/** + * Response to a STATISTICS_GET message (end of value stream). + */ +#define GNUNET_MESSAGE_TYPE_STATISTICS_END 7 + + +/** + * Request to ARM to start a service. + */ +#define GNUNET_MESSAGE_TYPE_ARM_START 8 + +/** + * Request to ARM to stop a service. + */ +#define GNUNET_MESSAGE_TYPE_ARM_STOP 9 + +/** + * Response from ARM: service is now up. + */ +#define GNUNET_MESSAGE_TYPE_ARM_IS_UP 10 + +/** + * Response from ARM: service is now down. + * (failed to start it). + */ +#define GNUNET_MESSAGE_TYPE_ARM_IS_DOWN 11 + + +/** + * HELLO message used for communicating peer addresses. + * Managed by libgnunethello. + */ +#define GNUNET_MESSAGE_TYPE_HELLO 16 + +/** + * FRAGMENT of a larger message. + * Managed by libgnunetfragment. + */ +#define GNUNET_MESSAGE_TYPE_FRAGMENT 18 + + +/** + * Message from the core saying that the transport + * server should start giving it messages. This + * should automatically trigger the transmission of + * a HELLO message. + */ +#define GNUNET_MESSAGE_TYPE_TRANSPORT_START 20 + +/** + * Message from TRANSPORT notifying about a + * client that connected to us. + */ +#define GNUNET_MESSAGE_TYPE_TRANSPORT_CONNECT 21 + +/** + * Message from TRANSPORT notifying about a + * client that disconnected from us. + */ +#define GNUNET_MESSAGE_TYPE_TRANSPORT_DISCONNECT 22 + +/** + * Request to TRANSPORT to transmit a message. + */ +#define GNUNET_MESSAGE_TYPE_TRANSPORT_SEND 23 + +/** + * Confirmation from TRANSPORT that message for + * transmission has been queued (and that the next + * message to this peer can now be passed to the + * service). Note that this confirmation does NOT + * imply that the message was fully transmitted. + */ +#define GNUNET_MESSAGE_TYPE_TRANSPORT_SEND_OK 24 + +/** + * Message from TRANSPORT notifying about a + * message that was received. + */ +#define GNUNET_MESSAGE_TYPE_TRANSPORT_RECV 25 + +/** + * Message telling transport to limit its receive rate. + */ +#define GNUNET_MESSAGE_TYPE_TRANSPORT_SET_QUOTA 26 + +/** + * Message telling transport to try to connect to the + * given peer. + */ +#define GNUNET_MESSAGE_TYPE_TRANSPORT_TRY_CONNECT 27 + +/** + * Request to other peer to confirm receipt. + */ +#define GNUNET_MESSAGE_TYPE_TRANSPORT_PING 28 + +/** + * Message from other peer confirming receipt. + */ +#define GNUNET_MESSAGE_TYPE_TRANSPORT_PONG 29 + +/** + * Response to another peer confirming that communication was + * established. + */ +#define GNUNET_MESSAGE_TYPE_TRANSPORT_ACK 30 + + +/** + * Request addition of a HELLO + */ +#define GNUNET_MESSAGE_TYPE_PEERINFO_ADD 32 + +/** + * Request update and listing of a peer. + */ +#define GNUNET_MESSAGE_TYPE_PEERINFO_GET 33 + +/** + * Request update and listing of all peers. + */ +#define GNUNET_MESSAGE_TYPE_PEERINFO_GET_ALL 34 + +/** + * Information about one of the peers. + */ +#define GNUNET_MESSAGE_TYPE_PEERINFO_INFO 35 + +/** + * End of information about other peers. + */ +#define GNUNET_MESSAGE_TYPE_PEERINFO_INFO_END 36 + + +/** + * Welcome message between TCP transports. + */ +#define GNUNET_MESSAGE_TYPE_TRANSPORT_TCP_WELCOME 40 + +/** + * Data message between TCP transports. + */ +#define GNUNET_MESSAGE_TYPE_TRANSPORT_TCP_DATA 41 + + +/** + * Initial setup message from core client to core. + */ +#define GNUNET_MESSAGE_TYPE_CORE_INIT 64 + +/** + * Response from core to core client to INIT message. + */ +#define GNUNET_MESSAGE_TYPE_CORE_INIT_REPLY 65 + +/** + * Notify clients about new peer-to-peer connections. + */ +#define GNUNET_MESSAGE_TYPE_CORE_NOTIFY_CONNECT 66 + +/** + * Notify clients about peer disconnecting. + */ +#define GNUNET_MESSAGE_TYPE_CORE_NOTIFY_DISCONNECT 67 + +/** + * Notify clients about incoming P2P messages. + */ +#define GNUNET_MESSAGE_TYPE_CORE_NOTIFY_INBOUND 68 + +/** + * Notify clients about outgoing P2P transmissions. + */ +#define GNUNET_MESSAGE_TYPE_CORE_NOTIFY_OUTBOUND 69 + +/** + * Request from client to "configure" P2P connection. + */ +#define GNUNET_MESSAGE_TYPE_CORE_REQUEST_CONFIGURE 70 + +/** + * Response from server about (possibly updated) P2P + * connection configuration. + */ +#define GNUNET_MESSAGE_TYPE_CORE_CONFIGURATION_INFO 71 + +/** + * Solicitation from server for transmission (may have + * been requested or also be transmitted without + * client's request). + */ +#define GNUNET_MESSAGE_TYPE_CORE_SOLICIT_TRAFFIC 72 + +/** + * Response from client with message to transmit. + */ +#define GNUNET_MESSAGE_TYPE_CORE_SEND 73 + + +/** + * Session key exchange between peers. + */ +#define GNUNET_MESSAGE_TYPE_CORE_SET_KEY 80 + +/** + * Encapsulation for an encrypted message between peers. + */ +#define GNUNET_MESSAGE_TYPE_CORE_ENCRYPTED_MESSAGE 81 + +/** + * Check that other peer is alife (challenge). + */ +#define GNUNET_MESSAGE_TYPE_CORE_PING 82 + +/** + * Confirmation that other peer is alife. + */ +#define GNUNET_MESSAGE_TYPE_CORE_PONG 83 + +/** + * Request by the other peer to terminate the connection. + */ +#define GNUNET_MESSAGE_TYPE_CORE_HANGUP 84 + + +/* + TODO: + - DV + - DHT + - datastores + - applications (FS, VPN, CHAT, TRACEKIT, TBENCH) +*/ + + +#if 0 /* keep Emacsens' auto-indent happy */ +{ +#endif +#ifdef __cplusplus +} +#endif + +/* ifndef GNUNET_PROTOCOLS_H */ +#endif +/* end of gnunet_protocols.h */ diff --git a/src/include/gnunet_pseudonym_lib.h b/src/include/gnunet_pseudonym_lib.h new file mode 100644 index 000000000..0ee9ce7ac --- /dev/null +++ b/src/include/gnunet_pseudonym_lib.h @@ -0,0 +1,125 @@ +/* + This file is part of GNUnet. + (C) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2009 Christian Grothoff (and other contributing authors) + + GNUnet is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published + by the Free Software Foundation; either version 2, 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 + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with GNUnet; see the file COPYING. If not, write to the + Free Software Foundation, Inc., 59 Temple Place - Suite 330, + Boston, MA 02111-1307, USA. +*/ + +/** + * @file include/gnunet_pseudonym_lib.h + * @brief functions related to pseudonyms + * @author Christian Grothoff + */ + +#ifndef GNUNET_PSEUDONYM_LIB_H +#define GNUNET_PSEUDONYM_LIB_H + +#ifdef __cplusplus +extern "C" +{ +#if 0 /* keep Emacsens' auto-indent happy */ +} +#endif +#endif + +#include "gnunet_common.h" +#include "gnunet_configuration_lib.h" +#include "gnunet_container_lib.h" + +/** + * Iterator over all known pseudonyms. + * + * @param rating the local rating of the pseudonym + * @return GNUNET_OK to continue iteration, GNUNET_SYSERR to abort + */ +typedef int (*GNUNET_PSEUDONYM_Iterator) (void *cls, + const GNUNET_HashCode * + pseudonym, + const struct + GNUNET_CONTAINER_MetaData * md, + int rating); + +/** + * Change the ranking of a pseudonym. + * + * @param pseudonym id of the pseudonym + * @param delta by how much should the rating be changed? + * @return new rating of the namespace + */ +int GNUNET_PSEUDONYM_rank (struct GNUNET_CONFIGURATION_Handle *cfg, + const GNUNET_HashCode * pseudonym, int delta); + +/** + * Add a pseudonym to the set of known pseudonyms. + * + * @param pseudonym the pseudonym's identifier + */ +void GNUNET_PSEUDONYM_add (struct GNUNET_CONFIGURATION_Handle *cfg, + const GNUNET_HashCode * pseudo, + const struct GNUNET_CONTAINER_MetaData *meta); + + +/** + * List all known pseudonyms. + */ +int GNUNET_PSEUDONYM_list_all (struct GNUNET_CONFIGURATION_Handle *cfg, + GNUNET_PSEUDONYM_Iterator iterator, + void *closure); + +/** + * Register callback to be invoked whenever we discover + * a new pseudonym. + */ +int GNUNET_PSEUDONYM_discovery_callback_register (struct + GNUNET_CONFIGURATION_Handle + *cfg, + GNUNET_PSEUDONYM_Iterator + iterator, void *closure); + +/** + * Unregister namespace discovery callback. + */ +int +GNUNET_PSEUDONYM_discovery_callback_unregister (GNUNET_PSEUDONYM_Iterator + iterator, void *closure); + +/** + * Return the unique, human readable name for the given pseudonym. + * + * @return NULL on failure (should never happen) + */ +char *GNUNET_PSEUDONYM_id_to_name (struct GNUNET_CONFIGURATION_Handle *cfg, + const GNUNET_HashCode * pseudo); + +/** + * Get the pseudonym ID belonging to the given human readable name. + * + * @return GNUNET_OK on success + */ +int GNUNET_PSEUDONYM_name_to_id (struct GNUNET_CONFIGURATION_Handle *cfg, + const char *hname, GNUNET_HashCode * psid); + + +#if 0 /* keep Emacsens' auto-indent happy */ +{ +#endif +#ifdef __cplusplus +} +#endif + +/* ifndef GNUNET_PSEUDONYM_LIB_H */ +#endif +/* end of gnunet_pseudonym_lib.h */ diff --git a/src/include/gnunet_resolver_service.h b/src/include/gnunet_resolver_service.h new file mode 100644 index 000000000..ac7aebcc7 --- /dev/null +++ b/src/include/gnunet_resolver_service.h @@ -0,0 +1,135 @@ +/* + This file is part of GNUnet. + (C) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2009 Christian Grothoff (and other contributing authors) + + GNUnet is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published + by the Free Software Foundation; either version 2, 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 + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with GNUnet; see the file COPYING. If not, write to the + Free Software Foundation, Inc., 59 Temple Place - Suite 330, + Boston, MA 02111-1307, USA. +*/ + +/** + * @file include/gnunet_resolver_service.h + * @brief functions related to doing DNS lookups + * @author Christian Grothoff + */ + +#ifndef GNUNET_RESOLVER_SERVICE_H +#define GNUNET_RESOLVER_SERVICE_H + +#ifdef __cplusplus +extern "C" +{ +#if 0 /* keep Emacsens' auto-indent happy */ +} +#endif +#endif + +#include "gnunet_configuration_lib.h" +#include "gnunet_scheduler_lib.h" +#include "gnunet_time_lib.h" + + +/** + * Function called by the resolver for each address obtained from DNS. + * + * @param cls closure + * @param addr one of the addresses of the host, NULL for the last address + * @param addrlen length of the address + */ +typedef void (*GNUNET_RESOLVER_AddressCallback) (void *cls, + const struct sockaddr * addr, + socklen_t addrlen); + + +/** + * Convert a string to one or more IP addresses. + * + * @param sched scheduler to use + * @param cfg configuration to use + * @param hostname the hostname to resolve + * @param domain AF_INET or AF_INET6; use AF_UNSPEC for "any" + * @param callback function to call with addresses + * @param cls closure for callback + * @param timeout how long to try resolving + */ +void +GNUNET_RESOLVER_ip_get (struct GNUNET_SCHEDULER_Handle *sched, + struct GNUNET_CONFIGURATION_Handle *cfg, + const char *hostname, + int domain, + struct GNUNET_TIME_Relative timeout, + GNUNET_RESOLVER_AddressCallback callback, void *cls); + + +/** + * Resolve our hostname to an IP address. + * + * @param sched scheduler to use + * @param cfg configuration to use + * @param domain AF_INET or AF_INET6; use AF_UNSPEC for "any" + * @param callback function to call with addresses + * @param cls closure for callback + * @param timeout how long to try resolving + */ +void +GNUNET_RESOLVER_hostname_resolve (struct GNUNET_SCHEDULER_Handle *sched, + struct GNUNET_CONFIGURATION_Handle *cfg, + int domain, + struct GNUNET_TIME_Relative timeout, + GNUNET_RESOLVER_AddressCallback callback, + void *cls); + + +/** + * Function called by the resolver for each hostname obtained from DNS. + * + * @param cls closure + * @param hostname one of the names for the host, NULL + * on the last call to the callback + */ +typedef void (*GNUNET_RESOLVER_HostnameCallback) (void *cls, + const char *hostname); + + +/** + * Get an IP address as a string. + * + * @param sched scheduler to use + * @param cfg configuration to use + * @param sa host address + * @param salen length of host address + * @param do_resolve use GNUNET_NO to return numeric hostname + * @param timeout how long to try resolving + * @param callback function to call with hostnames + * @param cls closure for callback + */ +void GNUNET_RESOLVER_hostname_get (struct GNUNET_SCHEDULER_Handle *sched, + struct GNUNET_CONFIGURATION_Handle *cfg, + const struct sockaddr *sa, + socklen_t salen, + int do_resolve, + struct GNUNET_TIME_Relative timeout, + GNUNET_RESOLVER_HostnameCallback callback, + void *cls); + +#if 0 /* keep Emacsens' auto-indent happy */ +{ +#endif +#ifdef __cplusplus +} +#endif + +/* ifndef GNUNET_RESOLVER_SERVICE_H */ +#endif +/* end of gnunet_resolver_service.h */ diff --git a/src/include/gnunet_scheduler_lib.h b/src/include/gnunet_scheduler_lib.h new file mode 100644 index 000000000..aa5830942 --- /dev/null +++ b/src/include/gnunet_scheduler_lib.h @@ -0,0 +1,442 @@ +/* + This file is part of GNUnet + (C) 2009 Christian Grothoff (and other contributing authors) + + GNUnet is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published + by the Free Software Foundation; either version 2, 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 + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with GNUnet; see the file COPYING. If not, write to the + Free Software Foundation, Inc., 59 Temple Place - Suite 330, + Boston, MA 02111-1307, USA. + */ + +/** + * @file include/gnunet_scheduler_lib.h + * @brief API to schedule computations using continuation passing style + * @author Christian Grothoff + */ + +#ifndef GNUNET_SCHEDULER_LIB_H +#define GNUNET_SCHEDULER_LIB_H + +#ifdef __cplusplus +extern "C" +{ +#if 0 /* keep Emacsens' auto-indent happy */ +} +#endif +#endif + +#include "gnunet_time_lib.h" + + +/** + * Opaque handle for the scheduling service. + */ +struct GNUNET_SCHEDULER_Handle; + + +/** + * Opaque reference to a task. + */ +typedef unsigned long long GNUNET_SCHEDULER_TaskIdentifier; + + +/** + * Constant used to indicate that the scheduled + * task has no others as prerequisites. + */ +#define GNUNET_SCHEDULER_NO_PREREQUISITE_TASK ((GNUNET_SCHEDULER_TaskIdentifier) 0) + +/** + * Reasons why the schedule may have triggered + * the task now. + */ +enum GNUNET_SCHEDULER_Reason +{ + /** + * This is the very first task run during startup. + */ + GNUNET_SCHEDULER_REASON_STARTUP = 0, + + /** + * We are shutting down and are running all shutdown-related tasks + * (regardless of timeout, etc.). + */ + GNUNET_SCHEDULER_REASON_SHUTDOWN = 1, + + /** + * The specified timeout has expired. + * (also set if the delay given was 0). + */ + GNUNET_SCHEDULER_REASON_TIMEOUT = 2, + + /** + * The reading socket is ready. + */ + GNUNET_SCHEDULER_REASON_READ_READY = 4, + + /** + * The writing socket is ready. + */ + GNUNET_SCHEDULER_REASON_WRITE_READY = 8, + + /** + * The prerequisite task is done. + */ + GNUNET_SCHEDULER_REASON_PREREQ_DONE = 16 +}; + + +/** + * Valid task priorities. Use these, do not + * pass random integers! + */ +enum GNUNET_SCHEDULER_Priority +{ + /** + * Run with the same priority as the current job. + */ + GNUNET_SCHEDULER_PRIORITY_KEEP = 0, + + /** + * Run when otherwise idle. + */ + GNUNET_SCHEDULER_PRIORITY_IDLE = 1, + + /** + * Run as background job (higher than idle, + * lower than default). + */ + GNUNET_SCHEDULER_PRIORITY_BACKGROUND = 2, + + /** + * Run with the default priority (normal + * P2P operations). Higher than BACKGROUND. + */ + GNUNET_SCHEDULER_PRIORITY_DEFAULT = 3, + + /** + * Run with high priority (important requests). + * Higher than DEFAULT. + */ + GNUNET_SCHEDULER_PRIORITY_HIGH = 4, + + /** + * Run with priority for interactive tasks. + * Higher than "HIGH". + */ + GNUNET_SCHEDULER_PRIORITY_UI = 5, + + /** + * Run with priority for urgent tasks. Use + * for things like aborts and shutdowns that + * need to preempt "UI"-level tasks. + * Higher than "UI". + */ + GNUNET_SCHEDULER_PRIORITY_URGENT = 6, + + /** + * Number of priorities (must be the last priority). + * This priority must not be used by clients. + */ + GNUNET_SCHEDULER_PRIORITY_COUNT = 7 +}; + + +/** + * Context information passed to each scheduler task. + */ +struct GNUNET_SCHEDULER_TaskContext +{ + + /** + * Scheduler running the task + */ + struct GNUNET_SCHEDULER_Handle *sched; + + /** + * Reason why the task is run now + */ + enum GNUNET_SCHEDULER_Reason reason; + + /** + * Set of file descriptors ready for reading; + * note that additional bits may be set + * that were not in the original request + */ + const fd_set *read_ready; + + /** + * Set of file descriptors ready for writing; + * note that additional bits may be set + * that were not in the original request. + */ + const fd_set *write_ready; + +}; + + +/** + * Signature of the main function of a task. + * + * @param cls closure + * @param tc context information (why was this task triggered now) + */ +typedef void (*GNUNET_SCHEDULER_Task) (void *cls, + const struct + GNUNET_SCHEDULER_TaskContext * tc); + + +/** + * Initialize and run scheduler. This function will return when + * either a shutdown was initiated (via signal) and all tasks marked + * to "run_on_shutdown" have been completed or when all tasks in + * general have been completed. + * + * @param task task to run immediately + * @param cls closure of task + */ +void GNUNET_SCHEDULER_run (GNUNET_SCHEDULER_Task task, void *cls); + + +/** + * Request the shutdown of a scheduler. This function can be used to + * stop a scheduler, for example from within the signal + * handler for signals causing shutdowns. + */ +void GNUNET_SCHEDULER_shutdown (struct GNUNET_SCHEDULER_Handle *sched); + + +/** + * Get information about the current load of this scheduler. Use this + * function to determine if an elective task should be added or simply + * dropped (if the decision should be made based on the number of + * tasks ready to run). + * + * @param sched scheduler to query + * @param p priority-level to query, use KEEP to query the level + * of the current task, use COUNT to get the sum over + * all priority levels + * @return number of tasks pending right now + */ +unsigned int GNUNET_SCHEDULER_get_load (struct GNUNET_SCHEDULER_Handle *sched, + enum GNUNET_SCHEDULER_Priority p); + + +/** + * Cancel the task with the specified identifier. + * The task must not yet have run. + * + * @param sched scheduler to use + * @param task id of the task to cancel + * @return the closure of the callback of the cancelled task + */ +void *GNUNET_SCHEDULER_cancel (struct GNUNET_SCHEDULER_Handle *sched, + GNUNET_SCHEDULER_TaskIdentifier task); + + +/** + * Continue the current execution with the given function. This is + * similar to the other "add" functions except that there is no delay + * and the reason code can be specified. + * + * @param sched scheduler to use + * @param main main function of the task + * @param cls closure of task + * @param reason reason for task invocation + */ +void +GNUNET_SCHEDULER_add_continuation (struct GNUNET_SCHEDULER_Handle *sched, + int run_on_shutdown, + GNUNET_SCHEDULER_Task main, + void *cls, + enum GNUNET_SCHEDULER_Reason reason); + + +/** + * Schedule a new task to be run after the specified + * prerequisite task has completed. + * + * @param sched scheduler to use + * @param run_on_shutdown run on shutdown? + * @param prio how important is this task? + * @param prerequisite_task run this task after the task with the given + * task identifier completes (and any of our other + * conditions, such as delay, read or write-readyness + * are satisfied). Use GNUNET_SCHEDULER_NO_PREREQUISITE_TASK to not have any dependency + * on completion of other tasks. + * @param main main function of the task + * @param cls closure of task + * @return unique task identifier for the job + * only valid until "main" is started! + */ +GNUNET_SCHEDULER_TaskIdentifier +GNUNET_SCHEDULER_add_after (struct GNUNET_SCHEDULER_Handle *sched, + int run_on_shutdown, + enum GNUNET_SCHEDULER_Priority prio, + GNUNET_SCHEDULER_TaskIdentifier prerequisite_task, + GNUNET_SCHEDULER_Task main, void *cls); + + +/** + * Schedule a new task to be run with a specified delay. The task + * will be scheduled for execution once the delay has expired and the + * prerequisite task has completed. + * + * @param sched scheduler to use + * @param run_on_shutdown run on shutdown? You can use this + * argument to run a function only during shutdown + * by setting delay to -1. Set this + * argument to GNUNET_NO to skip this task if + * the user requested process termination. + * @param prio how important is this task? + * @param prerequisite_task run this task after the task with the given + * task identifier completes (and any of our other + * conditions, such as delay, read or write-readyness + * are satisfied). Use GNUNET_SCHEDULER_NO_PREREQUISITE_TASK to not have any dependency + * on completion of other tasks. + * @param delay how long should we wait? Use GNUNET_TIME_UNIT_FOREVER_REL for "forever" + * @param main main function of the task + * @param cls closure of task + * @return unique task identifier for the job + * only valid until "main" is started! + */ +GNUNET_SCHEDULER_TaskIdentifier +GNUNET_SCHEDULER_add_delayed (struct GNUNET_SCHEDULER_Handle *sched, + int run_on_shutdown, + enum GNUNET_SCHEDULER_Priority prio, + GNUNET_SCHEDULER_TaskIdentifier + prerequisite_task, + struct GNUNET_TIME_Relative delay, + GNUNET_SCHEDULER_Task main, void *cls); + + +/** + * Schedule a new task to be run with a specified delay or when the + * specified file descriptor is ready for reading. The delay can be + * used as a timeout on the socket being ready. The task will be + * scheduled for execution once either the delay has expired or the + * socket operation is ready. + * + * @param sched scheduler to use + * @param run_on_shutdown run on shutdown? Set this + * argument to GNUNET_NO to skip this task if + * the user requested process termination. + * @param prio how important is this task? + * @param prerequisite_task run this task after the task with the given + * task identifier completes (and any of our other + * conditions, such as delay, read or write-readyness + * are satisfied). Use GNUNET_SCHEDULER_NO_PREREQUISITE_TASK to not have any dependency + * on completion of other tasks. + * @param delay how long should we wait? Use GNUNET_TIME_UNIT_FOREVER_REL for "forever" + * @param rfd read file-descriptor + * @param main main function of the task + * @param cls closure of task + * @return unique task identifier for the job + * only valid until "main" is started! + */ +GNUNET_SCHEDULER_TaskIdentifier +GNUNET_SCHEDULER_add_read (struct GNUNET_SCHEDULER_Handle *sched, + int run_on_shutdown, + enum GNUNET_SCHEDULER_Priority prio, + GNUNET_SCHEDULER_TaskIdentifier prerequisite_task, + struct GNUNET_TIME_Relative delay, + int rfd, GNUNET_SCHEDULER_Task main, void *cls); + + +/** + * Schedule a new task to be run with a specified delay or when the + * specified file descriptor is ready for writing. The delay can be + * used as a timeout on the socket being ready. The task will be + * scheduled for execution once either the delay has expired or the + * socket operation is ready. + * + * @param sched scheduler to use + * @param run_on_shutdown run on shutdown? Set this + * argument to GNUNET_NO to skip this task if + * the user requested process termination. + * @param prio how important is this task? + * @param prerequisite_task run this task after the task with the given + * task identifier completes (and any of our other + * conditions, such as delay, read or write-readyness + * are satisfied). Use GNUNET_SCHEDULER_NO_PREREQUISITE_TASK to not have any dependency + * on completion of other tasks. + * @param delay how long should we wait? Use GNUNET_TIME_UNIT_FOREVER_REL for "forever" + * @param wfd write file-descriptor + * @param main main function of the task + * @param cls closure of task + * @return unique task identifier for the job + * only valid until "main" is started! + */ +GNUNET_SCHEDULER_TaskIdentifier +GNUNET_SCHEDULER_add_write (struct GNUNET_SCHEDULER_Handle *sched, + int run_on_shutdown, + enum GNUNET_SCHEDULER_Priority prio, + GNUNET_SCHEDULER_TaskIdentifier prerequisite_task, + struct GNUNET_TIME_Relative delay, + int wfd, GNUNET_SCHEDULER_Task main, void *cls); + + +/** + * Schedule a new task to be run with a specified delay or when any of + * the specified file descriptor sets is ready. The delay can be used + * as a timeout on the socket(s) being ready. The task will be + * scheduled for execution once either the delay has expired or any of + * the socket operations is ready. This is the most general + * function of the "add" family. Note that the "prerequisite_task" + * must be satisfied in addition to any of the other conditions. In + * other words, the task will be started when + * + * (prerequisite-run) + * && (delay-ready + * || any-rs-ready + * || any-ws-ready + * || (shutdown-active && run-on-shutdown) ) + * + * + * @param sched scheduler to use + * @param run_on_shutdown run on shutdown? Set this + * argument to GNUNET_NO to skip this task if + * the user requested process termination. + * @param prio how important is this task? + * @param prerequisite_task run this task after the task with the given + * task identifier completes (and any of our other + * conditions, such as delay, read or write-readyness + * are satisfied). Use GNUNET_SCHEDULER_NO_PREREQUISITE_TASK to not have any dependency + * on completion of other tasks. + * @param delay how long should we wait? Use GNUNET_TIME_UNIT_FOREVER_REL for "forever" + * @param nfds highest-numbered file descriptor in any of the two sets plus one + * @param rs set of file descriptors we want to read (can be NULL) + * @param ws set of file descriptors we want to write (can be NULL) + * @param main main function of the task + * @param cls closure of task + * @return unique task identifier for the job + * only valid until "main" is started! + */ +GNUNET_SCHEDULER_TaskIdentifier +GNUNET_SCHEDULER_add_select (struct GNUNET_SCHEDULER_Handle *sched, + int run_on_shutdown, + enum GNUNET_SCHEDULER_Priority prio, + GNUNET_SCHEDULER_TaskIdentifier + prerequisite_task, + struct GNUNET_TIME_Relative delay, + int nfds, const fd_set * rs, const fd_set * ws, + GNUNET_SCHEDULER_Task main, void *cls); + +#if 0 /* keep Emacsens' auto-indent happy */ +{ +#endif +#ifdef __cplusplus +} +#endif + +#endif diff --git a/src/include/gnunet_server_lib.h b/src/include/gnunet_server_lib.h new file mode 100644 index 000000000..4e4f35e26 --- /dev/null +++ b/src/include/gnunet_server_lib.h @@ -0,0 +1,498 @@ +/* + This file is part of GNUnet. + (C) 2009 Christian Grothoff (and other contributing authors) + + GNUnet is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published + by the Free Software Foundation; either version 2, 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 + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with GNUnet; see the file COPYING. If not, write to the + Free Software Foundation, Inc., 59 Temple Place - Suite 330, + Boston, MA 02111-1307, USA. +*/ + +/** + * @file include/gnunet_server_lib.h + * @brief library for building GNUnet network servers + * + * @author Christian Grothoff + */ + +#ifndef GNUNET_SERVER_LIB_H +#define GNUNET_SERVER_LIB_H + +#ifdef __cplusplus +extern "C" +{ +#if 0 /* keep Emacsens' auto-indent happy */ +} +#endif +#endif + +#include "gnunet_common.h" +#include "gnunet_network_lib.h" +#include "gnunet_scheduler_lib.h" + + +/** + * Largest supported message. + */ +#define GNUNET_SERVER_MAX_MESSAGE_SIZE 65536 + + +/** + * @brief handle for a server + */ +struct GNUNET_SERVER_Handle; + + +/** + * @brief opaque handle for a client of the server + */ +struct GNUNET_SERVER_Client; + + +/** + * Functions with this signature are called whenever a message is + * received. + * + * @param cls closure + * @param server the server handling the message + * @param client identification of the client + * @param message the actual message + */ +typedef void (*GNUNET_SERVER_MessageCallback) (void *cls, + struct GNUNET_SERVER_Handle * + server, + struct GNUNET_SERVER_Client * + client, + const struct + GNUNET_MessageHeader * + message); + + + +/** + * Message handler. Each struct specifies how to handle on particular + * type of message received. + */ +struct GNUNET_SERVER_MessageHandler +{ + /** + * Function to call for messages of "type". + */ + GNUNET_SERVER_MessageCallback callback; + + /** + * Closure argument for "callback". + */ + void *callback_cls; + + /** + * Type of the message this handler covers. + */ + uint16_t type; + + /** + * Expected size of messages of this type. Use 0 for + * variable-size. If non-zero, messages of the given + * type will be discarded (and the connection closed) + * if they do not have the right size. + */ + uint16_t expected_size; + +}; + + +/** + * Create a new server. + * + * @param sched scheduler to use + * @param access function for access control + * @param access_cls closure for access + * @param serverAddr address to listen on (including port), use NULL + * for internal server (no listening) + * @param socklen length of serverAddr + * @param maxbuf maximum write buffer size for accepted sockets + * @param idle_timeout after how long should we timeout idle connections? + * @param require_found if YES, connections sending messages of unknown type + * will be closed + * @return handle for the new server, NULL on error + * (typically, "port" already in use) + */ +struct GNUNET_SERVER_Handle *GNUNET_SERVER_create (struct + GNUNET_SCHEDULER_Handle + *sched, + GNUNET_NETWORK_AccessCheck + access, void *access_cls, + const struct sockaddr + *serverAddr, + socklen_t socklen, + size_t maxbuf, + struct GNUNET_TIME_Relative + idle_timeout, + int require_found); + + +/** + * Free resources held by this server. + */ +void GNUNET_SERVER_destroy (struct GNUNET_SERVER_Handle *s); + + +/** + * Add additional handlers to an existing server. + * + * @param server the server to add handlers to + * @param handlers array of message handlers for + * incoming messages; the last entry must + * have "NULL" for the "callback"; multiple + * entries for the same type are allowed, + * they will be called in order of occurence. + * These handlers can be removed later; + * the handlers array must exist until removed + * (or server is destroyed). + */ +void +GNUNET_SERVER_add_handlers (struct GNUNET_SERVER_Handle *server, + const struct GNUNET_SERVER_MessageHandler + *handlers); + + +/** + * Notify us when the server has enough space to transmit + * a message of the given size to the given client. + * + * @param client client to transmit message to + * @param size requested amount of buffer space + * @param timeout after how long should we give up (and call + * notify with buf NULL and size 0)? + * @param callback function to call when space is available + * @param callback_cls closure for callback + * @return non-NULL if the notify callback was queued; can be used + * to cancel the request using + * GNUNET_NETWORK_notify_transmit_ready_cancel. + * NULL if we are already going to notify someone else (busy) + */ +struct GNUNET_NETWORK_TransmitHandle + *GNUNET_SERVER_notify_transmit_ready (struct GNUNET_SERVER_Client *client, + size_t size, + struct GNUNET_TIME_Relative timeout, + GNUNET_NETWORK_TransmitReadyNotify + callback, void *callback_cls); + + +/** + * Resume receiving from this client, we are done processing the + * current request. This function must be called from within each + * GNUNET_SERVER_MessageCallback (or its respective continuations). + * + * @param client client we were processing a message of + * @param success GNUNET_OK to keep the connection open and + * continue to receive + * GNUNET_SYSERR to close the connection (signal + * serious error) + */ +void +GNUNET_SERVER_receive_done (struct GNUNET_SERVER_Client *client, int success); + + +/** + * Inject a message into the server, pretend it came + * from the specified client. Delivery of the message + * will happen instantly (if a handler is installed; + * otherwise the call does nothing). + * + * @param server the server receiving the message + * @param sender the "pretended" sender of the message + * can be NULL! + * @param message message to transmit + * @return GNUNET_OK if the message was OK and the + * connection can stay open + * GNUNET_SYSERR if the connection to the + * client should be shut down + */ +int +GNUNET_SERVER_inject (struct GNUNET_SERVER_Handle *server, + struct GNUNET_SERVER_Client *sender, + const struct GNUNET_MessageHeader *message); + + +/** + * Add a TCP socket-based connection to the set of handles managed by + * this server. Use this function for outgoing (P2P) connections that + * we initiated (and where this server should process incoming + * messages). + * + * @param server the server to use + * @param connection the connection to manage (client must + * stop using this connection from now on) + * @return the client handle (client should call + * "client_drop" on the return value eventually) + */ +struct GNUNET_SERVER_Client *GNUNET_SERVER_connect_socket (struct + GNUNET_SERVER_Handle + *server, + struct + GNUNET_NETWORK_SocketHandle + *connection); + + +/** + * Receive data from the given connection. This function should call + * "receiver" asynchronously using the scheduler. It must return + * "immediately". + * + * @param cls closure + * @param sched scheduler to use + * @param max maximum number of bytes to read + * @param timeout maximum amount of time to wait (use -1 for "forever") + * @param receiver function to call with received data + * @param receiver_cls closure for receiver + * @return task identifier that can be used to cancel the receive, + * GNUNET_SCHEDULER_NO_PREREQUISITE_TASK should be returned + * if the receiver function was already called + */ +typedef GNUNET_SCHEDULER_TaskIdentifier + (*GNUNET_SERVER_ReceiveCallback) (void *cls, + size_t max, + struct GNUNET_TIME_Relative timeout, + GNUNET_NETWORK_Receiver + receiver, void *receiver_cls); + + +/** + * Cancel receive request. + * + * @param cls closure + * @param ti task identifier from the receive callback + */ +typedef void (*GNUNET_SERVER_ReceiveCancelCallback) (void *cls, + GNUNET_SCHEDULER_TaskIdentifier + ti); + + +/** + * Notify us when the connection is ready to transmit size bytes. + * + * @param cls closure + * @param size number of bytes to be ready for sending + * @param timeout after how long should we give up (and call + * notify with buf NULL and size 0)? + * @param notify function to call + * @param notify_cls closure for notify + * @return a handle that can be used to cancel + * the transmission request or NULL if + * queueing a transmission request failed + */ +typedef void *(*GNUNET_SERVER_TransmitReadyCallback) (void *cls, + size_t size, + struct + GNUNET_TIME_Relative + timeout, + GNUNET_NETWORK_TransmitReadyNotify + notify, + void *notify_cls); + + +/** + * Cancel an earlier transmit notification request. + * + * @param cls closure + * @param ctx handle that was returned by the TransmitReadyCallback + */ +typedef void (*GNUNET_SERVER_TransmitReadyCancelCallback) (void *cls, + void *ctx); + + +/** + * Check if connection is still valid (no fatal errors have happened so far). + * + * @param cls closure + * @return GNUNET_YES if valid, GNUNET_NO otherwise + */ +typedef int (*GNUNET_SERVER_CheckCallback) (void *cls); + + +/** + * Destroy this connection (free resources). + * + * @param cls closure + */ +typedef void (*GNUNET_SERVER_DestroyCallback) (void *cls); + + +/** + * Add an arbitrary connection to the set of handles managed by this + * server. This can be used if a sending and receiving does not + * really go over the network (internal transmission) or for servers + * using UDP. + * + * @param server the server to use + * @param chandle opaque handle for the connection + * @param creceive receive function for the connection + * @param creceive_cancel cancel receive function for the connection + * @param cnotify transmit notification function for the connection + * @param cnotify_cancel transmit notification cancellation function for the connection + * @param ccheck function to test if the connection is still up + * @param cdestroy function to close and free the connection + * @return the client handle (client should call + * "client_drop" on the return value eventually) + */ +struct GNUNET_SERVER_Client *GNUNET_SERVER_connect_callback (struct + GNUNET_SERVER_Handle + *server, + void *chandle, + GNUNET_SERVER_ReceiveCallback + creceive, + GNUNET_SERVER_ReceiveCancelCallback + ccancel, + GNUNET_SERVER_TransmitReadyCallback + cnotify, + GNUNET_SERVER_TransmitReadyCancelCallback + cnotify_cancel, + GNUNET_SERVER_CheckCallback + ccheck, + GNUNET_SERVER_DestroyCallback + cdestroy); + + +/** + * Notify the server that the given client handle should + * be kept (keeps the connection up if possible, increments + * the internal reference counter). + * + * @param client the client to keep + */ +void GNUNET_SERVER_client_keep (struct GNUNET_SERVER_Client *client); + + +/** + * Notify the server that the given client handle is no + * longer required. Decrements the reference counter. If + * that counter reaches zero an inactive connection maybe + * closed. + * + * @param client the client to drop + */ +void GNUNET_SERVER_client_drop (struct GNUNET_SERVER_Client *client); + + +/** + * Obtain the network address of the other party. + * + * @param client the client to get the address for + * @param addr where to store the address + * @param addrlen where to store the length of the address + * @return GNUNET_OK on success + */ +int GNUNET_SERVER_client_get_address (struct GNUNET_SERVER_Client *client, + void **addr, size_t * addrlen); + + +/** + * Functions with this signature are called whenever a client + * is disconnected on the network level. + * + * @param cls closure + * @param client identification of the client + */ +typedef void (*GNUNET_SERVER_DisconnectCallback) (void *cls, + struct GNUNET_SERVER_Client + * client); + + +/** + * Ask the server to notify us whenever a client disconnects. + * This function is called whenever the actual network connection + * is closed; the reference count may be zero or larger than zero + * at this point. + * + * @param server the server manageing the clients + * @param callback function to call on disconnect + * @param callback_cls closure for callback + */ +void GNUNET_SERVER_disconnect_notify (struct GNUNET_SERVER_Handle *server, + GNUNET_SERVER_DisconnectCallback + callback, void *callback_cls); + + +/** + * Ask the server to disconnect from the given client. + * This is the same as returning GNUNET_SYSERR from a message + * handler, except that it allows dropping of a client even + * when not handling a message from that client. + * + * @param client the client to disconnect from + */ +void GNUNET_SERVER_client_disconnect (struct GNUNET_SERVER_Client *client); + + +/** + * The tansmit context is the key datastructure for a conveniance API + * used for transmission of complex results to the client followed + * ONLY by signaling receive_done with success or error + */ +struct GNUNET_SERVER_TransmitContext; + + +/** + * Create a new transmission context for the + * given client. + * + * @param client client to create the context for. + * @return NULL on error + */ +struct GNUNET_SERVER_TransmitContext + *GNUNET_SERVER_transmit_context_create (struct GNUNET_SERVER_Client + *client); + + +/** + * Append a message to the transmission context. + * All messages in the context will be sent by + * the transmit_context_run method. + * + * @param tc context to use + * @param data what to append to the result message + * @param length length of data + * @param type type of the message + */ +void +GNUNET_SERVER_transmit_context_append (struct GNUNET_SERVER_TransmitContext + *tc, const void *data, size_t length, + uint16_t type); + +/** + * Execute a transmission context. If there is + * an error in the transmission, the receive_done + * method will be called with an error code (GNUNET_SYSERR), + * otherwise with GNUNET_OK. + * + * @param tc transmission context to use + * @param timeout when to time out and abort the transmission + */ +void +GNUNET_SERVER_transmit_context_run (struct GNUNET_SERVER_TransmitContext *tc, + struct GNUNET_TIME_Relative timeout); + + + +#if 0 /* keep Emacsens' auto-indent happy */ +{ +#endif +#ifdef __cplusplus +} +#endif + + +/* ifndef GNUNET_SERVER_LIB_H */ +#endif +/* end of gnunet_server_lib.h */ diff --git a/src/include/gnunet_service_lib.h b/src/include/gnunet_service_lib.h new file mode 100644 index 000000000..dbfb2588b --- /dev/null +++ b/src/include/gnunet_service_lib.h @@ -0,0 +1,140 @@ +/* + This file is part of GNUnet. + (C) 2009 Christian Grothoff (and other contributing authors) + + GNUnet is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published + by the Free Software Foundation; either version 2, 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 + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with GNUnet; see the file COPYING. If not, write to the + Free Software Foundation, Inc., 59 Temple Place - Suite 330, + Boston, MA 02111-1307, USA. +*/ + +/** + * @file include/gnunet_service_lib.h + * @brief functions related to starting services + * @author Christian Grothoff + */ + +#ifndef GNUNET_SERVICE_LIB_H +#define GNUNET_SERVICE_LIB_H + +#ifdef __cplusplus +extern "C" +{ +#if 0 /* keep Emacsens' auto-indent happy */ +} +#endif +#endif + +#include "gnunet_configuration_lib.h" +#include "gnunet_server_lib.h" + +/** + * Function called by the service's run + * method to run service-specific setup code. + * + * @param cls closure + * @param sched scheduler to use + * @param server the initialized server + * @param cfg configuration to use + */ +typedef void (*GNUNET_SERVICE_Main) (void *cls, + struct GNUNET_SCHEDULER_Handle * sched, + struct GNUNET_SERVER_Handle * server, + struct GNUNET_CONFIGURATION_Handle * + cfg); + + +/** + * Function called when the service shuts + * down to run service-specific teardown code. + * + * @param cls closure + * @param cfg configuration to use + */ +typedef void (*GNUNET_SERVICE_Term) (void *cls, + struct GNUNET_CONFIGURATION_Handle * + cfg); + + +/** + * Run a standard GNUnet service startup sequence (initialize loggers + * and configuration, parse options). + * + * @param argc number of command line arguments + * @param argv command line arguments + * @param serviceName our service name + * @param task main task of the service + * @param task_cls closure for task + * @param term termination task of the service + * @param term_cls closure for term + * @return GNUNET_SYSERR on error, GNUNET_OK + * if we shutdown nicely + */ +int GNUNET_SERVICE_run (int argc, + char *const *argv, + const char *serviceName, + GNUNET_SERVICE_Main task, + void *task_cls, + GNUNET_SERVICE_Term term, void *term_cls); + + +struct GNUNET_SERVICE_Context; + +/** + * Run a service startup sequence within an existing + * initialized system. + * + * @param serviceName our service name + * @param sched scheduler to use + * @param cfg configuration to use + * @return NULL on error, service handle + */ +struct GNUNET_SERVICE_Context *GNUNET_SERVICE_start (const char *serviceName, + struct + GNUNET_SCHEDULER_Handle + *sched, + struct + GNUNET_CONFIGURATION_Handle + *cfg); + + +/** + * Obtain the server used by a service. Note that the server must NOT + * be destroyed by the caller. + * + * @param ctx the service context returned from the start function + * @return handle to the server for this service, NULL if there is none + */ +struct GNUNET_SERVER_Handle *GNUNET_SERVICE_get_server (struct + GNUNET_SERVICE_Context + *ctx); + + +/** + * Stop a service that was started with "GNUNET_SERVICE_start". + * + * @param ctx the service context returned from the start function + */ +void GNUNET_SERVICE_stop (struct GNUNET_SERVICE_Context *ctx); + + +#if 0 /* keep Emacsens' auto-indent happy */ +{ +#endif +#ifdef __cplusplus +} +#endif + +/* ifndef GNUNET_SERVICE_LIB_H */ +#endif +/* end of gnunet_service_lib.h */ diff --git a/src/include/gnunet_signal_lib.h b/src/include/gnunet_signal_lib.h new file mode 100644 index 000000000..af1ec0de4 --- /dev/null +++ b/src/include/gnunet_signal_lib.h @@ -0,0 +1,73 @@ +/* + This file is part of GNUnet. + (C) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2009 Christian Grothoff (and other contributing authors) + + GNUnet is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published + by the Free Software Foundation; either version 2, 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 + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with GNUnet; see the file COPYING. If not, write to the + Free Software Foundation, Inc., 59 Temple Place - Suite 330, + Boston, MA 02111-1307, USA. +*/ + +/** + * @file include/gnunet_signal_lib.h + * @brief functions related to signals + * @author Christian Grothoff + */ + +#ifndef GNUNET_SIGNAL_LIB_H +#define GNUNET_SIGNAL_LIB_H + +#ifdef __cplusplus +extern "C" +{ +#if 0 /* keep Emacsens' auto-indent happy */ +} +#endif +#endif + +struct GNUNET_SIGNAL_Context; + +/** + * A signal handler. Since different OSes have different signatures + * for their handlers, the API only gives the most restrictive + * signature -- no arguments, no return value. Note that this will + * work even if the OS expects a function with arguments. However, + * the implementation must guarantee that this handler is not called + * for signals other than the one that it has been registered for. + */ +typedef void (*GNUNET_SIGNAL_Handler) (void); + +/** + * Install a signal handler that will be run if the + * given signal is received. + */ +struct GNUNET_SIGNAL_Context *GNUNET_SIGNAL_handler_install (int signal, + GNUNET_SIGNAL_Handler + handler); + +/** + * Uninstall a previously installed signal hander. + */ +void GNUNET_SIGNAL_handler_uninstall (struct GNUNET_SIGNAL_Context *ctx); + + +#if 0 /* keep Emacsens' auto-indent happy */ +{ +#endif +#ifdef __cplusplus +} +#endif + +/* ifndef GNUNET_SIGNAL_LIB_H */ +#endif +/* end of gnunet_signal_lib.h */ diff --git a/src/include/gnunet_signatures.h b/src/include/gnunet_signatures.h new file mode 100644 index 000000000..adb00c1ac --- /dev/null +++ b/src/include/gnunet_signatures.h @@ -0,0 +1,77 @@ +/* + This file is part of GNUnet. + (C) 2009 Christian Grothoff (and other contributing authors) + + GNUnet is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published + by the Free Software Foundation; either version 2, 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 + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with GNUnet; see the file COPYING. If not, write to the + Free Software Foundation, Inc., 59 Temple Place - Suite 330, + Boston, MA 02111-1307, USA. +*/ + +/** + * @file include/gnunet_signatures.h + * @brief constants for network signatures + * @author Christian Grothoff + */ + +#ifndef GNUNET_SIGNATURES_H +#define GNUNET_SIGNATURES_H + +#ifdef __cplusplus +extern "C" +{ +#if 0 /* keep Emacsens' auto-indent happy */ +} +#endif +#endif + +/** + * Test signature, not valid for anything other than writing + * a test. (Note that the signature verification code will + * accept this value). + */ +#define GNUNET_SIGNATURE_PURPOSE_TEST 0 + +/** + * Signature for confirming HELLOs. + */ +#define GNUNET_SIGNATURE_PURPOSE_TRANSPORT_HELLO 1 + + +/** + * Purpose is to set a session key. + */ +#define GNUNET_SIGNATURE_PURPOSE_SET_KEY 2 + +/** + * Signature for a namespace/pseudonym advertisement (by + * the namespace owner). + */ +#define GNUNET_SIGNATURE_PURPOSE_NAMESPACE_ADVERTISEMENT 3 + +/** + * + */ +#define GNUNET_SIGNATURE_PURPOSE_RESOLVER_RESPONSE 3 + + +#if 0 /* keep Emacsens' auto-indent happy */ +{ +#endif +#ifdef __cplusplus +} +#endif + +/* ifndef GNUNET_SIGNATURES_H */ +#endif +/* end of gnunet_signatures.h */ diff --git a/src/include/gnunet_statistics_service.h b/src/include/gnunet_statistics_service.h new file mode 100644 index 000000000..8dd11094c --- /dev/null +++ b/src/include/gnunet_statistics_service.h @@ -0,0 +1,157 @@ +/* + This file is part of GNUnet + (C) 2009 Christian Grothoff (and other contributing authors) + + GNUnet is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published + by the Free Software Foundation; either version 2, 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 + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with GNUnet; see the file COPYING. If not, write to the + Free Software Foundation, Inc., 59 Temple Place - Suite 330, + Boston, MA 02111-1307, USA. + */ + +/** + * @file include/gnunet_statistics_service.h + * @brief API to create, modify and access statistics about + * the operation of GNUnet; all statistical values + * must be of type "unsigned long long". + * @author Christian Grothoff + */ + +#ifndef GNUNET_STATISTICS_SERVICE_H +#define GNUNET_STATISTICS_SERVICE_H + +#ifdef __cplusplus +extern "C" +{ +#if 0 /* keep Emacsens' auto-indent happy */ +} +#endif +#endif + +#include "gnunet_common.h" +#include "gnunet_configuration_lib.h" +#include "gnunet_scheduler_lib.h" + +/** + * Version of the statistics API. + */ +#define GNUNET_STATISTICS_VERSION 0x00000000 + +/** + * Opaque handle for the statistics service. + */ +struct GNUNET_STATISTICS_Handle; + +/** + * 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 + */ +typedef int (*GNUNET_STATISTICS_Iterator) (void *cls, + const char *subsystem, + const char *name, + unsigned long long value, + int is_persistent); + +/** + * Get handle for the statistics service. + * + * @param sched scheduler to use + * @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 (struct GNUNET_SCHEDULER_Handle *sched, + const char *subsystem, + struct GNUNET_CONFIGURATION_Handle *cfg); + + +/** + * Destroy a handle (free all state associated with + * it). + */ +void GNUNET_STATISTICS_destroy (struct GNUNET_STATISTICS_Handle *handle); + + +/** + * Continuation called by the "get_all" and "get" functions. + * + * @param cls closure + * @param success GNUNET_OK if statistics were + * successfully obtained, GNUNET_SYSERR if not. + */ +typedef void (*GNUNET_STATISTICS_Callback) (void *cls, int success); + +/** + * Get statistic from the peer. + * + * @param handle identification of the statistics service + * @param subsystem limit to the specified subsystem, NULL for our subsystem + * @param name name of the statistic value, NULL for all values + * @param timeout after how long should we give up (and call + * notify with buf NULL and size 0)? + * @param cont continuation to call when done (can be NULL) + * @param proc function to call on each value + * @param cls closure for proc and cont + */ +void +GNUNET_STATISTICS_get (struct GNUNET_STATISTICS_Handle *handle, + const char *subsystem, + const char *name, + struct GNUNET_TIME_Relative timeout, + GNUNET_STATISTICS_Callback cont, + GNUNET_STATISTICS_Iterator proc, void *cls); + +/** + * Set statistic value for the peer. Will always use our + * subsystem (the argument used when "handle" was created). + * + * @param handle identification of the statistics service + * @param name name of the statistic value + * @param value new value to set + * @param make_persistent should the value be kept across restarts? + */ +void +GNUNET_STATISTICS_set (struct GNUNET_STATISTICS_Handle *handle, + const char *name, + unsigned long long value, int make_persistent); + +/** + * Set statistic value for the peer. Will always use our + * subsystem (the argument used when "handle" was created). + * + * @param handle identification of the statistics service + * @param name name of the statistic value + * @param delta change in value (added to existing value) + * @param make_persistent should the value be kept across restarts? + */ +void +GNUNET_STATISTICS_update (struct GNUNET_STATISTICS_Handle *handle, + const char *name, + long long delta, int make_persistent); + + + +#if 0 /* keep Emacsens' auto-indent happy */ +{ +#endif +#ifdef __cplusplus +} +#endif + +#endif diff --git a/src/include/gnunet_strings_lib.h b/src/include/gnunet_strings_lib.h new file mode 100644 index 000000000..98d2ed396 --- /dev/null +++ b/src/include/gnunet_strings_lib.h @@ -0,0 +1,139 @@ +/* + This file is part of GNUnet. + (C) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2009 Christian Grothoff (and other contributing authors) + + GNUnet is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published + by the Free Software Foundation; either version 2, 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 + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with GNUnet; see the file COPYING. If not, write to the + Free Software Foundation, Inc., 59 Temple Place - Suite 330, + Boston, MA 02111-1307, USA. +*/ + +/** + * @file include/gnunet_strings_lib.h + * @brief strings and string handling functions (including malloc + * and string tokenizing) + * + * @author Christian Grothoff + * @author Krista Bennett + * @author Gerd Knorr + * @author Ioana Patrascu + * @author Tzvetan Horozov + */ + +#ifndef GNUNET_STRINGS_LIB_H +#define GNUNET_STRINGS_LIB_H + +/* we need size_t, and since it can be both unsigned int + or unsigned long long, this IS platform dependent; + but "stdlib.h" should be portable 'enough' to be + unconditionally available... */ +#include + +#ifdef __cplusplus +extern "C" +{ +#if 0 /* keep Emacsens' auto-indent happy */ +} +#endif +#endif + +#include "gnunet_time_lib.h" + + +/** + * Convert a given filesize into a fancy human-readable format. + */ +char *GNUNET_STRINGS_byte_size_fancy (unsigned long long size); + +/** + * Convert the len characters long character sequence + * given in input that is in the given charset + * to UTF-8. + * + * @return the converted string (0-terminated) + */ +char *GNUNET_STRINGS_to_utf8 (const char *input, + size_t len, const char *charset); + +/** + * Complete filename (a la shell) from abbrevition. + * + * @param fil the name of the file, may contain ~/ or + * be relative to the current directory + * @returns the full file name, + * NULL is returned on error + */ +char *GNUNET_STRINGS_filename_expand (const char *fil); + +/** + * Fill a buffer of the given size with + * count 0-terminated strings (given as varargs). + * If "buffer" is NULL, only compute the amount of + * space required (sum of "strlen(arg)+1"). + * + * Unlike using "snprintf" with "%s", this function + * will add 0-terminators after each string. The + * "GNUNET_string_buffer_tokenize" function can be + * used to parse the buffer back into individual + * strings. + * + * @return number of bytes written to the buffer + * (or number of bytes that would have been written) + */ +unsigned int GNUNET_STRINGS_buffer_fill (char *buffer, + unsigned int size, + unsigned int count, ...); + +/** + * Given a buffer of a given size, find "count" + * 0-terminated strings in the buffer and assign + * the count (varargs) of type "const char**" to the + * locations of the respective strings in the + * buffer. + * + * @param buffer the buffer to parse + * @param size size of the buffer + * @param count number of strings to locate + * @return offset of the character after the last 0-termination + * in the buffer, or 0 on error. + */ +unsigned int GNUNET_STRINGS_buffer_tokenize (const char *buffer, + unsigned int size, + unsigned int count, ...); + + + +/** + * "man ctime_r", except for GNUnet time; also, unlike ctime, the + * return value does not include the newline character. + */ +char *GNUNET_STRINGS_absolute_time_to_string (struct GNUNET_TIME_Absolute t); + + +/** + * Give relative time in human-readable fancy format. + * @param delta time in milli seconds + */ +char *GNUNET_STRINGS_relative_time_to_string (struct GNUNET_TIME_Relative + delta); +#if 0 /* keep Emacsens' auto-indent happy */ +{ +#endif +#ifdef __cplusplus +} +#endif + + +/* ifndef GNUNET_UTIL_STRING_H */ +#endif +/* end of gnunet_util_string.h */ diff --git a/src/include/gnunet_time_lib.h b/src/include/gnunet_time_lib.h new file mode 100644 index 000000000..4ce1e3f1e --- /dev/null +++ b/src/include/gnunet_time_lib.h @@ -0,0 +1,246 @@ +/* + This file is part of GNUnet. + (C) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2009 Christian Grothoff (and other contributing authors) + + GNUnet is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published + by the Free Software Foundation; either version 2, 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 + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with GNUnet; see the file COPYING. If not, write to the + Free Software Foundation, Inc., 59 Temple Place - Suite 330, + Boston, MA 02111-1307, USA. +*/ + +/** + * @file include/gnunet_time_lib.h + * @brief functions related to time + * + * @author Christian Grothoff + */ + +#ifndef GNUNET_TIME_LIB_H +#define GNUNET_TIME_LIB_H + +#ifdef __cplusplus +extern "C" +{ +#if 0 /* keep Emacsens' auto-indent happy */ +} +#endif +#endif + +#include "gnunet_common.h" + +/** + * Time for absolute times used by GNUnet, in milliseconds. + */ +struct GNUNET_TIME_Absolute +{ + uint64_t value; +}; + +/** + * Time for relative time used by GNUnet, in milliseconds. + * Always positive, so we can only refer to future time. + */ +struct GNUNET_TIME_Relative +{ + uint64_t value; +}; + + +/** + * Time for relative time used by GNUnet, in milliseconds and in network byte order. + */ +struct GNUNET_TIME_RelativeNBO +{ + uint64_t value GNUNET_PACKED; +}; + + +/** + * Time for absolute time used by GNUnet, in milliseconds and in network byte order. + */ +struct GNUNET_TIME_AbsoluteNBO +{ + uint64_t value GNUNET_PACKED; +}; + +/** + * @brief constants to specify time + */ +#define GNUNET_TIME_UNIT_ZERO GNUNET_TIME_relative_get_zero() +#define GNUNET_TIME_UNIT_MILLISECONDS GNUNET_TIME_relative_get_unit() +#define GNUNET_TIME_UNIT_SECONDS GNUNET_TIME_relative_multiply(GNUNET_TIME_UNIT_MILLISECONDS, 1000) +#define GNUNET_TIME_UNIT_MINUTES GNUNET_TIME_relative_multiply(GNUNET_TIME_UNIT_SECONDS, 60) +#define GNUNET_TIME_UNIT_HOURS GNUNET_TIME_relative_multiply(GNUNET_TIME_UNIT_MINUTES, 60) +#define GNUNET_TIME_UNIT_DAYS GNUNET_TIME_relative_multiply(GNUNET_TIME_UNIT_HOURS, 24) +#define GNUNET_TIME_UNIT_WEEKS GNUNET_TIME_relative_multiply(GNUNET_TIME_UNIT_DAYS, 7) +#define GNUNET_TIME_UNIT_MONTHS GNUNET_TIME_relative_multiply(GNUNET_TIME_UNIT_DAYS, 30) +#define GNUNET_TIME_UNIT_YEARS GNUNET_TIME_relative_multiply(GNUNET_TIME_UNIT_DAYS, 365) + +/** + * Constant used to specify "forever". This constant + * will be treated specially in all time operations. + */ +#define GNUNET_TIME_UNIT_FOREVER_REL GNUNET_TIME_relative_get_forever () + +/** + * Constant used to specify "forever". This constant + * will be treated specially in all time operations. + */ +#define GNUNET_TIME_UNIT_FOREVER_ABS GNUNET_TIME_absolute_get_forever () + +/** + * Return relative time of 0ms. + */ +struct GNUNET_TIME_Relative GNUNET_TIME_relative_get_zero (void); + +/** + * Return relative time of 1ms. + */ +struct GNUNET_TIME_Relative GNUNET_TIME_relative_get_unit (void); + +/** + * Return "forever". + */ +struct GNUNET_TIME_Relative GNUNET_TIME_relative_get_forever (void); + +/** + * Return "forever". + */ +struct GNUNET_TIME_Absolute GNUNET_TIME_absolute_get_forever (void); + +/** + * Get the current time. + * + * @return the current time + */ +struct GNUNET_TIME_Absolute GNUNET_TIME_absolute_get (void); + +/** + * Convert relative time to an absolute time in the + * future. + * + * @return timestamp that is "rel" in the future, or FOREVER if rel==FOREVER (or if we would overflow) + */ +struct GNUNET_TIME_Absolute GNUNET_TIME_relative_to_absolute (struct + GNUNET_TIME_Relative + rel); + +/** + * Given a timestamp in the future, how much time + * remains until then? + * + * @return future - now, or 0 if now >= future, or FOREVER if future==FOREVER. + */ +struct GNUNET_TIME_Relative GNUNET_TIME_absolute_get_remaining (struct + GNUNET_TIME_Absolute + future); + +/** + * Compute the time difference between the given start and end times. + * Use this function instead of actual subtraction to ensure that + * "FOREVER" and overflows are handeled correctly. + * + * @return 0 if start >= end; FOREVER if end==FOREVER; otherwise end - start + */ +struct GNUNET_TIME_Relative GNUNET_TIME_absolute_get_difference (struct + GNUNET_TIME_Absolute + start, + struct + GNUNET_TIME_Absolute + end); + +/** + * Get the duration of an operation as the + * difference of the current time and the given start time "hence". + * + * @return aborts if hence==FOREVER, 0 if hence > now, otherwise now-hence. + */ +struct GNUNET_TIME_Relative GNUNET_TIME_absolute_get_duration (struct + GNUNET_TIME_Absolute + hence); + + +/** + * Add a given relative duration to the + * given start time. + * + * @return FOREVER if either argument is FOREVER or on overflow; start+duration otherwise + */ +struct GNUNET_TIME_Absolute GNUNET_TIME_absolute_add (struct + GNUNET_TIME_Absolute + start, + struct + GNUNET_TIME_Relative + duration); + +/** + * Multiply relative time by a given factor. + * + * @return FOREVER if rel=FOREVER or on overflow; otherwise rel*factor + */ +struct GNUNET_TIME_Relative GNUNET_TIME_relative_multiply (struct + GNUNET_TIME_Relative + rel, + unsigned int + factor); + +/** + * Add relative times together. + * + * @return FOREVER if either argument is FOREVER or on overflow; a1+a2 otherwise + */ +struct GNUNET_TIME_Relative GNUNET_TIME_relative_add (struct + GNUNET_TIME_Relative a1, + struct + GNUNET_TIME_Relative + a2); + + +/** + * Convert relative time to network byte order. + */ +struct GNUNET_TIME_RelativeNBO GNUNET_TIME_relative_hton (struct + GNUNET_TIME_Relative + a); + +/** + * Convert relative time from network byte order. + */ +struct GNUNET_TIME_Relative GNUNET_TIME_relative_ntoh (struct + GNUNET_TIME_RelativeNBO + a); + +/** + * Convert relative time to network byte order. + */ +struct GNUNET_TIME_AbsoluteNBO GNUNET_TIME_absolute_hton (struct + GNUNET_TIME_Absolute + a); + +/** + * Convert relative time from network byte order. + */ +struct GNUNET_TIME_Absolute GNUNET_TIME_absolute_ntoh (struct + GNUNET_TIME_AbsoluteNBO + a); + +#if 0 /* keep Emacsens' auto-indent happy */ +{ +#endif +#ifdef __cplusplus +} +#endif + +/* ifndef GNUNET_TIME_LIB_H */ +#endif +/* end of gnunet_time_lib.h */ diff --git a/src/include/gnunet_transport_service.h b/src/include/gnunet_transport_service.h new file mode 100644 index 000000000..25ca388a7 --- /dev/null +++ b/src/include/gnunet_transport_service.h @@ -0,0 +1,241 @@ +/* + This file is part of GNUnet. + (C) 2009 Christian Grothoff (and other contributing authors) + + GNUnet is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published + by the Free Software Foundation; either version 2, 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 + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with GNUnet; see the file COPYING. If not, write to the + Free Software Foundation, Inc., 59 Temple Place - Suite 330, + Boston, MA 02111-1307, USA. +*/ + +/** + * @file include/gnunet_transport_service.h + * @brief low-level P2P IO + * @author Christian Grothoff + */ + +#ifndef GNUNET_TRANSPORT_SERVICE_H +#define GNUNET_TRANSPORT_SERVICE_H + +#ifdef __cplusplus +extern "C" +{ +#if 0 /* keep Emacsens' auto-indent happy */ +} +#endif +#endif + +#include "gnunet_configuration_lib.h" +#include "gnunet_crypto_lib.h" +#include "gnunet_network_lib.h" +#include "gnunet_scheduler_lib.h" +#include "gnunet_time_lib.h" + +/** + * Version number of the transport API. + */ +#define GNUNET_TRANSPORT_VERSION 0x00000000 + +/** + * Function called by the transport for each received message. + * + * @param cls closure + * @param latency estimated latency for communicating with the + * given peer + * @param peer (claimed) identity of the other peer + * @param message the message + */ +typedef void (*GNUNET_TRANSPORT_ReceiveCallback) (void *cls, + struct GNUNET_TIME_Relative + latency, + const struct + GNUNET_PeerIdentity * peer, + const struct + GNUNET_MessageHeader * + message); + + +/** + * Opaque handle to the service. + */ +struct GNUNET_TRANSPORT_Handle; + + +/** + * Function called to notify transport users that another + * peer connected to us. + * + * @param cls closure + * @param peer the peer that connected + * @param latency current latency of the connection + */ +typedef void + (*GNUNET_TRANSPORT_NotifyConnect) (void *cls, + const struct GNUNET_PeerIdentity * peer, + struct GNUNET_TIME_Relative latency); + +/** + * Function called to notify transport users that another + * peer disconnected from us. + * + * @param cls closure + * @param peer the peer that disconnected + */ +typedef void + (*GNUNET_TRANSPORT_NotifyDisconnect) (void *cls, + const struct GNUNET_PeerIdentity * + peer); + + +/** + * Connect to the transport service. Note that the connection may + * complete (or fail) asynchronously. + * + * @param sched scheduler to use + * @param cfg configuration to use + * @param cls closure for the callbacks + * @param rec receive function to call + * @param nc function to call on connect events + * @param dc function to call on disconnect events + */ +struct GNUNET_TRANSPORT_Handle *GNUNET_TRANSPORT_connect (struct + GNUNET_SCHEDULER_Handle + *sched, + struct + GNUNET_CONFIGURATION_Handle + *cfg, void *cls, + GNUNET_TRANSPORT_ReceiveCallback + rec, + GNUNET_TRANSPORT_NotifyConnect + nc, + GNUNET_TRANSPORT_NotifyDisconnect + nd); + + +/** + * Disconnect from the transport service. + */ +void GNUNET_TRANSPORT_disconnect (struct GNUNET_TRANSPORT_Handle *handle); + + +/** + * Set the share of incoming/outgoing bandwidth for the given + * peer to the specified amount. + * + * @param handle connection to transport service + * @param target who's bandwidth quota is being changed + * @param quota_in incoming bandwidth quota in bytes per ms; 0 can + * be used to force all traffic to be discarded + * @param quota_out outgoing bandwidth quota in bytes per ms; 0 can + * be used to force all traffic to be discarded + * @param timeout how long to wait until signaling failure if + * we can not communicate the quota change + * @param cont continuation to call when done, will be called + * either with reason "TIMEOUT" or with reason "PREREQ_DONE" + * @param cont_cls closure for continuation + */ +void +GNUNET_TRANSPORT_set_quota (struct GNUNET_TRANSPORT_Handle *handle, + const struct GNUNET_PeerIdentity *target, + uint32_t quota_in, + uint32_t quota_out, + struct GNUNET_TIME_Relative timeout, + GNUNET_SCHEDULER_Task cont, void *cont_cls); + + +/** + * Opaque handle for a transmission-ready request. + */ +struct GNUNET_TRANSPORT_TransmitHandle; + + +/** + * Check if we could queue a message of the given size for + * transmission. The transport service will take both its + * internal buffers and bandwidth limits imposed by the + * other peer into consideration when answering this query. + * + * @param handle connection to transport service + * @param target who should receive the message + * @param size how big is the message we want to transmit? + * @param timeout after how long should we give up (and call + * notify with buf NULL and size 0)? + * @param notify function to call when we are ready to + * send such a message + * @param notify_cls closure for notify + * @return NULL if someone else is already waiting to be notified + * non-NULL if the notify callback was queued (can be used to cancel + * using GNUNET_TRANSPORT_notify_transmit_ready_cancel) + */ +struct GNUNET_TRANSPORT_TransmitHandle + *GNUNET_TRANSPORT_notify_transmit_ready (struct GNUNET_TRANSPORT_Handle + *handle, + const struct GNUNET_PeerIdentity + *target, size_t size, + struct GNUNET_TIME_Relative + timeout, + GNUNET_NETWORK_TransmitReadyNotify + notify, void *notify_cls); + + +/** + * Cancel the specified transmission-ready + * notification. + */ +void +GNUNET_TRANSPORT_notify_transmit_ready_cancel (struct + GNUNET_TRANSPORT_TransmitHandle + *h); + + +/** + * Obtain the HELLO message for this peer. + * + * @param handle connection to transport service + * @param timeout how long to wait for the HELLO + * @param rec function to call with the HELLO, sender will be our peer + * identity; message and sender will be NULL on timeout + * (handshake with transport service pending/failed). + * cost estimate will be 0. + * @param rec_cls closure for rec + */ +void +GNUNET_TRANSPORT_get_hello (struct GNUNET_TRANSPORT_Handle *handle, + struct GNUNET_TIME_Relative timeout, + GNUNET_TRANSPORT_ReceiveCallback rec, + void *rec_cls); + + +/** + * Offer the transport service the HELLO of another peer. Note that + * the transport service may just ignore this message if the HELLO is + * malformed or useless due to our local configuration. + * + * @param handle connection to transport service + * @param hello the hello message + */ +void +GNUNET_TRANSPORT_offer_hello (struct GNUNET_TRANSPORT_Handle *handle, + const struct GNUNET_MessageHeader *hello); + + +#if 0 /* keep Emacsens' auto-indent happy */ +{ +#endif +#ifdef __cplusplus +} +#endif + +/* ifndef GNUNET_TRANSPORT_SERVICE_H */ +#endif +/* end of gnunet_transport_service.h */ diff --git a/src/include/gnunet_upnp_service.h b/src/include/gnunet_upnp_service.h new file mode 100644 index 000000000..758d5c598 --- /dev/null +++ b/src/include/gnunet_upnp_service.h @@ -0,0 +1,75 @@ +/* + This file is part of GNUnet + (C) 2006 Christian Grothoff (and other contributing authors) + + GNUnet is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published + by the Free Software Foundation; either version 2, 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 + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with GNUnet; see the file COPYING. If not, write to the + Free Software Foundation, Inc., 59 Temple Place - Suite 330, + Boston, MA 02111-1307, USA. +*/ + +/** + * @file include/gnunet_upnp_service.h + * @brief API for UPnP access + * @author Christian Grothoff + */ + +#ifndef GNUNET_UPNP_SERVICE_H +#define GNUNET_UPNP_SERVICE_H + +#include "gnunet_resolver_service.h" + +#ifdef __cplusplus +extern "C" +{ +#if 0 /* keep Emacsens' auto-indent happy */ +} +#endif +#endif + + +/** + * Get the external IP address for the local machine and + * install a port mapping if possible. The external port + * will be returned as part of the address. + * + * @param sched scheduler to use + * @param cfg configuration to use + * @param domain communication domain (i.e. PF_INET or PF_INET6) + * @param type communication semantics (SOCK_STREAM, SOCK_DGRAM) + * @param protocol protocol to use, 0 for default (see protocols(5)) + * @param port port to map + * @param timeout after how long should we give up (and call + * notify with buf NULL and size 0)? + * @param callback function to call with the external address; + * function will be called with NULL on error + * @param cls closure for callback + */ +int GNUNET_UPNP_get_ip (struct GNUNET_SCHEDULER_Handle *sched, + struct GNUNET_CONFIGURATION_Handle *cfg, + int domain, + int type, + int protocol, + uint16_t port, + struct GNUNET_TIME_Relative timeout, + GNUNET_RESOLVER_AddressCallback callback, void *cls); + + +#if 0 /* keep Emacsens' auto-indent happy */ +{ +#endif +#ifdef __cplusplus +} +#endif + +#endif diff --git a/src/include/gnunet_util_lib.h b/src/include/gnunet_util_lib.h new file mode 100644 index 000000000..c152377c3 --- /dev/null +++ b/src/include/gnunet_util_lib.h @@ -0,0 +1,64 @@ +/* + This file is part of GNUnet + (C) 2009 Christian Grothoff (and other contributing authors) + + GNUnet is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published + by the Free Software Foundation; either version 2, 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 + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with GNUnet; see the file COPYING. If not, write to the + Free Software Foundation, Inc., 59 Temple Place - Suite 330, + Boston, MA 02111-1307, USA. + */ + +/** + * @file include/gnunet_util_lib.h + * @brief convenience header including all headers of subsystems in + * gnunet_util library + * @author Christian Grothoff + */ + +#ifndef GNUNET_UTIL_LIB_H +#define GNUNET_UTIL_LIB_H + +#ifdef __cplusplus +extern "C" +{ +#if 0 /* keep Emacsens' auto-indent happy */ +} +#endif +#endif + +#include "gnunet_common.h" +#include "gnunet_client_lib.h" +#include "gnunet_configuration_lib.h" +#include "gnunet_container_lib.h" +#include "gnunet_crypto_lib.h" +#include "gnunet_disk_lib.h" +#include "gnunet_getopt_lib.h" +#include "gnunet_network_lib.h" +#include "gnunet_program_lib.h" +#include "gnunet_protocols.h" +#include "gnunet_pseudonym_lib.h" +#include "gnunet_scheduler_lib.h" +#include "gnunet_server_lib.h" +#include "gnunet_service_lib.h" +#include "gnunet_signal_lib.h" +#include "gnunet_strings_lib.h" +#include "gnunet_time_lib.h" + +#if 0 /* keep Emacsens' auto-indent happy */ +{ +#endif +#ifdef __cplusplus +} +#endif + +#endif diff --git a/src/include/platform.h b/src/include/platform.h new file mode 100644 index 000000000..07fbe0ee7 --- /dev/null +++ b/src/include/platform.h @@ -0,0 +1,221 @@ +/* + This file is part of GNUnet. + (C) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2009 Christian Grothoff (and other contributing authors) + + GNUnet is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published + by the Free Software Foundation; either version 2, 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 + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with GNUnet; see the file COPYING. If not, write to the + Free Software Foundation, Inc., 59 Temple Place - Suite 330, + Boston, MA 02111-1307, USA. +*/ + +/** + * @file include/platform.h + * @brief plaform specifics + * + * @author Nils Durner + * + * This file should never be included by installed + * header files (thos starting with "gnunet_"). + */ + +#ifndef PLATFORM_H +#define PLATFORM_H + +#ifndef HAVE_USED_CONFIG_H +#define HAVE_USED_CONFIG_H +#if HAVE_CONFIG_H +#include "gnunet_config.h" +#endif +#endif + +#ifdef WINDOWS +#define BREAKPOINT asm("int $3;"); +#else +#define BREAKPOINT +#endif + +#ifdef HAVE_SYS_TYPES_H +#include +#endif + +#define ALLOW_EXTRA_CHECKS GNUNET_NO + +/** + * For strptime (glibc2 needs this). + */ +#ifndef _XOPEN_SOURCE +#define _XOPEN_SOURCE +#endif + +#ifndef _REENTRANT +#define _REENTRANT +#endif + +/* configuration options */ + +#define VERBOSE_STATS 0 + +#ifdef CYGWIN +#include +#define _REENT_ONLY +#endif + +#ifdef CYGWIN +#undef _REENT_ONLY +#endif + +#ifdef _MSC_VER +#include +#else +#ifndef MINGW +#include +#include +#include +#include +#include +#include +#include +#include +#include +#else +#include "winproc.h" +#endif +#endif + +#include +#include +#include +#include +#include +#include +#ifndef _MSC_VER +#include /* KLB_FIX */ +#endif +#include +#include +#ifndef _MSC_VER +#include /* KLB_FIX */ +#endif +#include +#include +#if HAVE_SYS_PARAM_H +#include +#endif +#if TIME_WITH_SYS_TIME +#include +#include +#else +#if HAVE_SYS_TIME_H +#include +#else +#include +#endif +#endif + +#ifdef SOMEBSD +#include +#endif +#ifdef GNUNET_freeBSD +#include +#endif +#ifdef OSX +#include +#include +#include +#endif +#ifdef LINUX +#include +#endif +#ifdef SOLARIS +#include +#include +#include +#endif +#ifdef CYGWIN +#include +#include +#endif +#if HAVE_IFADDRS_H +#include +#endif +#include +#include + +#if HAVE_CTYPE_H +#include +#endif +#if HAVE_SYS_RESOURCE_H +#include +#endif + +#include "plibc.h" + +#include +#include +#ifndef FRAMEWORK_BUILD +#include "gettext.h" +/** + * GNU gettext support macro. + */ +#define _(String) dgettext("gnunet",String) +#define LIBEXTRACTOR_GETTEXT_DOMAIN "libextractor" +#else +#include "libintlemu.h" +#define _(String) dgettext("org.gnunet.gnunet",String) +#define LIBEXTRACTOR_GETTEXT_DOMAIN "org.gnunet.libextractor" +#endif + +#ifdef CYGWIN +#define SIOCGIFCONF _IOW('s', 100, struct ifconf) /* get if list */ +#define SIOCGIFFLAGS _IOW('s', 101, struct ifreq) /* Get if flags */ +#define SIOCGIFADDR _IOW('s', 102, struct ifreq) /* Get if addr */ +#endif + +#ifndef MINGW +#include +#endif + +#ifdef FREEBSD +#define __BYTE_ORDER BYTE_ORDER +#define __BIG_ENDIAN BIG_ENDIAN +#endif + +#ifdef OSX +#define socklen_t unsigned int +#define __BYTE_ORDER BYTE_ORDER +#define __BIG_ENDIAN BIG_ENDIAN + /* not available on OS X, override configure */ +#undef HAVE_STAT64 +#undef HAVE_MREMAP +#endif + + +#if !HAVE_ATOLL +long long atoll (const char *nptr); +#endif + +#if ENABLE_NLS +#include "langinfo.h" +#endif + +#ifndef O_LARGEFILE +#define O_LARGEFILE 0 +#endif + +#if defined(__sparc__) +#define MAKE_UNALIGNED(val) ({ __typeof__((val)) __tmp; memmove(&__tmp, &(val), sizeof((val))); __tmp; }) +#else +#define MAKE_UNALIGNED(val) val +#endif + +#endif diff --git a/src/include/plibc.h b/src/include/plibc.h new file mode 100644 index 000000000..93ca012e1 --- /dev/null +++ b/src/include/plibc.h @@ -0,0 +1,582 @@ +/* + This file is part of PlibC. + (C) 2005, 2006, 2007, 2008 Nils Durner (and other contributing authors) + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ + +/** + * @file include/plibc.h + * @brief PlibC header + * @attention This file is usually not installed under Unix, + * so ship it with your application + * @version $Revision: 1.46 $ + */ + +#ifndef _PLIBC_H_ +#define _PLIBC_H_ + +#ifndef SIGALRM +#define SIGALRM 14 +#endif /* */ + +#ifdef __cplusplus +extern "C" +{ + +#endif /* */ + +#ifdef Q_OS_WIN32 +#define WINDOWS 1 +#endif /* */ + +#define HAVE_PLIBC_FD 0 + +#ifdef WINDOWS + +#if ENABLE_NLS +#include "langinfo.h" +#endif /* */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define __BYTE_ORDER BYTE_ORDER +#define __BIG_ENDIAN BIG_ENDIAN + +/* Conflicts with our definitions */ +#define __G_WIN32_H__ + +/* Convert LARGE_INTEGER to double */ +#define Li2Double(x) ((double)((x).HighPart) * 4.294967296E9 + \ + (double) ((x).LowPart)) +#define socklen_t int +#define ssize_t int +#define off_t int +#define int64_t long long +#define int32_t long + struct stat64 + { + _dev_t st_dev; + _ino_t st_ino; + _mode_t st_mode; + short st_nlink; + short st_uid; + short st_gid; + _dev_t st_rdev; + __int64 st_size; + __time64_t st_atime; + __time64_t st_mtime; + __time64_t st_ctime; + }; + +#ifndef pid_t +#define pid_t int +#endif /* */ + +#ifndef WEXITSTATUS +#define WEXITSTATUS(status) (((status) & 0xff00) >> 8) +#endif /* */ + +/* Thanks to the Cygwin project */ +#define ENOCSI 43 /* No CSI structure available */ +#define EL2HLT 44 /* Level 2 halted */ +#ifndef EDEADLK +#define EDEADLK 45 /* Deadlock condition */ +#endif /* */ +#ifndef ENOLCK +#define ENOLCK 46 /* No record locks available */ +#endif /* */ +#define EBADE 50 /* Invalid exchange */ +#define EBADR 51 /* Invalid request descriptor */ +#define EXFULL 52 /* Exchange full */ +#define ENOANO 53 /* No anode */ +#define EBADRQC 54 /* Invalid request code */ +#define EBADSLT 55 /* Invalid slot */ +#ifndef EDEADLOCK +#define EDEADLOCK EDEADLK /* File locking deadlock error */ +#endif /* */ +#define EBFONT 57 /* Bad font file fmt */ +#define ENOSTR 60 /* Device not a stream */ +#define ENODATA 61 /* No data (for no delay io) */ +#define ETIME 62 /* Timer expired */ +#define ENOSR 63 /* Out of streams resources */ +#define ENONET 64 /* Machine is not on the network */ +#define ENOPKG 65 /* Package not installed */ +#define EREMOTE 66 /* The object is remote */ +#define ENOLINK 67 /* The link has been severed */ +#define EADV 68 /* Advertise error */ +#define ESRMNT 69 /* Srmount error */ +#define ECOMM 70 /* Communication error on send */ +#define EPROTO 71 /* Protocol error */ +#define EMULTIHOP 74 /* Multihop attempted */ +#define ELBIN 75 /* Inode is remote (not really error) */ +#define EDOTDOT 76 /* Cross mount point (not really error) */ +#define EBADMSG 77 /* Trying to read unreadable message */ +#define ENOTUNIQ 80 /* Given log. name not unique */ +#define EBADFD 81 /* f.d. invalid for this operation */ +#define EREMCHG 82 /* Remote address changed */ +#define ELIBACC 83 /* Can't access a needed shared lib */ +#define ELIBBAD 84 /* Accessing a corrupted shared lib */ +#define ELIBSCN 85 /* .lib section in a.out corrupted */ +#define ELIBMAX 86 /* Attempting to link in too many libs */ +#define ELIBEXEC 87 /* Attempting to exec a shared library */ +#ifndef ENOSYS +#define ENOSYS 88 /* Function not implemented */ +#endif /* */ +#define ENMFILE 89 /* No more files */ +#ifndef ENOTEMPTY +#define ENOTEMPTY 90 /* Directory not empty */ +#endif /* */ +#ifndef ENAMETOOLONG +#define ENAMETOOLONG 91 /* File or path name too long */ +#endif /* */ +#define ELOOP 92 /* Too many symbolic links */ +#define EOPNOTSUPP 95 /* Operation not supported on transport endpoint */ +#define EPFNOSUPPORT 96 /* Protocol family not supported */ +#define ECONNRESET 104 /* Connection reset by peer */ +#define ENOBUFS 105 /* No buffer space available */ +#define EAFNOSUPPORT 106 /* Address family not supported by protocol family */ +#define EPROTOTYPE 107 /* Protocol wrong type for socket */ +#define ENOTSOCK 108 /* Socket operation on non-socket */ +#define ENOPROTOOPT 109 /* Protocol not available */ +#define ESHUTDOWN 110 /* Can't send after socket shutdown */ +#define ECONNREFUSED 111 /* Connection refused */ +#define EADDRINUSE 112 /* Address already in use */ +#define ECONNABORTED 113 /* Connection aborted */ +#define ENETUNREACH 114 /* Network is unreachable */ +#define ENETDOWN 115 /* Network interface is not configured */ +#ifndef ETIMEDOUT +#define ETIMEDOUT 116 /* Connection timed out */ +#endif /* */ +#define EHOSTDOWN 117 /* Host is down */ +#define EHOSTUNREACH 118 /* Host is unreachable */ +#define EINPROGRESS 119 /* Connection already in progress */ +#define EALREADY 120 /* Socket already connected */ +#define EDESTADDRREQ 121 /* Destination address required */ +#define EMSGSIZE 122 /* Message too long */ +#define EPROTONOSUPPORT 123 /* Unknown protocol */ +#define ESOCKTNOSUPPORT 124 /* Socket type not supported */ +#define EADDRNOTAVAIL 125 /* Address not available */ +#define ENETRESET 126 /* Connection aborted by network */ +#define EISCONN 127 /* Socket is already connected */ +#define ENOTCONN 128 /* Socket is not connected */ +#define ETOOMANYREFS 129 /* Too many references: cannot splice */ +#define EPROCLIM 130 /* Too many processes */ +#define EUSERS 131 /* Too many users */ +#define EDQUOT 132 /* Disk quota exceeded */ +#define ESTALE 133 /* Unknown error */ +#ifndef ENOTSUP +#define ENOTSUP 134 /* Not supported */ +#endif /* */ +#define ENOMEDIUM 135 /* No medium (in tape drive) */ +#define ENOSHARE 136 /* No such host or network path */ +#define ECASECLASH 137 /* Filename exists with different case */ +#define EWOULDBLOCK EAGAIN /* Operation would block */ +#define EOVERFLOW 139 /* Value too large for defined data type */ + +#undef HOST_NOT_FOUND +#define HOST_NOT_FOUND 1 +#undef TRY_AGAIN +#define TRY_AGAIN 2 +#undef NO_RECOVERY +#define NO_RECOVERY 3 +#undef NO_ADDRESS +#define NO_ADDRESS 4 + +#define PROT_READ 0x1 +#define PROT_WRITE 0x2 +#define MAP_SHARED 0x1 +#define MAP_PRIVATE 0x2 /* unsupported */ +#define MAP_FIXED 0x10 +#define MAP_FAILED ((void *)-1) + struct statfs + { + long f_type; /* type of filesystem (see below) */ + long f_bsize; /* optimal transfer block size */ + long f_blocks; /* total data blocks in file system */ + long f_bfree; /* free blocks in fs */ + long f_bavail; /* free blocks avail to non-superuser */ + long f_files; /* total file nodes in file system */ + long f_ffree; /* free file nodes in fs */ + long f_fsid; /* file system id */ + long f_namelen; /* maximum length of filenames */ + long f_spare[6]; /* spare for later */ + }; + extern const struct in6_addr in6addr_any; /* :: */ + extern const struct in6_addr in6addr_loopback; /* ::1 */ + +/* Taken from the Wine project + /wine/include/winternl.h */ + enum SYSTEM_INFORMATION_CLASS + { SystemBasicInformation = 0, Unknown1, SystemPerformanceInformation = 2, SystemTimeOfDayInformation = 3, /* was SystemTimeInformation */ + Unknown4, SystemProcessInformation = + 5, Unknown6, Unknown7, SystemProcessorPerformanceInformation = + 8, Unknown9, Unknown10, SystemDriverInformation, Unknown12, + Unknown13, Unknown14, Unknown15, SystemHandleList, Unknown17, + Unknown18, Unknown19, Unknown20, SystemCacheInformation, + Unknown22, SystemInterruptInformation = + 23, SystemExceptionInformation = + 33, SystemRegistryQuotaInformation = 37, SystemLookasideInformation = 45 + }; + typedef struct + { + LARGE_INTEGER IdleTime; + LARGE_INTEGER KernelTime; + LARGE_INTEGER UserTime; + LARGE_INTEGER Reserved1[2]; + ULONG Reserved2; + } SYSTEM_PROCESSOR_PERFORMANCE_INFORMATION; + +#define sleep(secs) (Sleep(secs * 1000)) + +/*********************** statfs *****************************/ +/* fake block size */ +#define FAKED_BLOCK_SIZE 512 + +/* linux-compatible values for fs type */ +#define MSDOS_SUPER_MAGIC 0x4d44 +#define NTFS_SUPER_MAGIC 0x5346544E + +/*********************** End of statfs ***********************/ + +#define SHUT_RDWR SD_BOTH + +/* Operations for flock() */ +#define LOCK_SH 1 /* shared lock */ +#define LOCK_EX 2 /* exclusive lock */ +#define LOCK_NB 4 /* or'd with one of the above to prevent + blocking */ +#define LOCK_UN 8 /* remove lock */ + +/* Not supported under MinGW */ +#define S_IRGRP 0 +#define S_IWGRP 0 +#define S_IROTH 0 +#define S_IXGRP 0 +#define S_IWOTH 0 +#define S_IXOTH 0 +#define S_ISUID 0 +#define S_ISGID 0 +#define S_ISVTX 0 +#define S_IRWXG 0 +#define S_IRWXO 0 + +#define SHUT_WR SD_SEND +#define SHUT_RD SD_RECEIVE +#define SHUT_RDWR SD_BOTH + +#define SIGKILL 9 +#define SIGTERM 15 + +#define SetErrnoFromWinError(e) _SetErrnoFromWinError(e, __FILE__, __LINE__) + BOOL _plibc_CreateShortcut (const char *pszSrc, const char *pszDest); + BOOL _plibc_DereferenceShortcut (char *pszShortcut); + char *plibc_ChooseDir (char *pszTitle, unsigned long ulFlags); + char *plibc_ChooseFile (char *pszTitle, unsigned long ulFlags); + long QueryRegistry (HKEY hMainKey, char *pszKey, char *pszSubKey, + char *pszBuffer, long *pdLength); + BOOL __win_IsHandleMarkedAsBlocking (SOCKET hHandle); + void __win_SetHandleBlockingMode (SOCKET s, BOOL bBlocking); + void __win_DiscardHandleBlockingMode (SOCKET s); + int _win_isSocketValid (int s); + int plibc_conv_to_win_path (const char *pszUnix, char *pszWindows); + unsigned plibc_get_handle_count (); + typedef void (*TPanicProc) (int, char *); + void plibc_set_panic_proc (TPanicProc proc); + int flock (int fd, int operation); + int fsync (int fildes); + int inet_pton (int af, const char *src, void *dst); + int inet_pton4 (const char *src, u_char * dst, int pton); + +#if USE_IPV6 + int inet_pton6 (const char *src, u_char * dst); + +#endif /* */ + int truncate (const char *fname, int distance); + int statfs (const char *path, struct statfs *buf); + const char *hstrerror (int err); + void gettimeofday (struct timeval *tp, void *tzp); + int mkstemp (char *tmplate); + char *strptime (const char *buf, const char *format, struct tm *tm); + char *ctime (const time_t * clock); + char *ctime_r (const time_t * clock, char *buf); + const char *inet_ntop (int af, const void *src, char *dst, size_t size); + int plibc_init (char *pszOrg, char *pszApp); + void plibc_shutdown (); + int plibc_initialized (); + int plibc_conv_to_win_path_ex (const char *pszUnix, char *pszWindows, + int derefLinks); + void _SetErrnoFromWinError (long lWinError, char *pszCaller, int iLine); + void SetErrnoFromWinsockError (long lWinError); + void SetHErrnoFromWinError (long lWinError); + void SetErrnoFromHRESULT (HRESULT hRes); + FILE *_win_fopen (const char *filename, const char *mode); + DIR *_win_opendir (const char *dirname); + int _win_open (const char *filename, int oflag, ...); + +#ifdef ENABLE_NLS + char *_win_bindtextdomain (const char *domainname, const char *dirname); + +#endif /* */ + int _win_chdir (const char *path); + int _win_close (int fd); + int _win_creat (const char *path, mode_t mode); + int _win_fstat (int handle, struct stat *buffer); + int _win_ftruncate (int fildes, off_t length); + int _win_kill (pid_t pid, int sig); + int _win_pipe (int *phandles); + int _win_rmdir (const char *path); + int _win_access (const char *path, int mode); + int _win_chmod (const char *filename, int pmode); + char *realpath (const char *file_name, char *resolved_name); + long _win_random (void); + int _win_remove (const char *path); + int _win_rename (const char *oldname, const char *newname); + int _win_stat (const char *path, struct stat *buffer); + int _win_stat64 (const char *path, struct stat64 *buffer); + int _win_unlink (const char *filename); + int _win_write (int fildes, const void *buf, size_t nbyte); + int _win_read (int fildes, void *buf, size_t nbyte); + size_t _win_fwrite (const void *buffer, size_t size, size_t count, + FILE * stream); + size_t _win_fread (void *buffer, size_t size, size_t count, FILE * stream); + int _win_symlink (const char *path1, const char *path2); + void *_win_mmap (void *start, size_t len, int access, int flags, int fd, + unsigned long long offset); + int _win_munmap (void *start, size_t length); + int _win_lstat (const char *path, struct stat *buf); + int _win_lstat64 (const char *path, struct stat64 *buf); + int _win_readlink (const char *path, char *buf, size_t bufsize); + int _win_accept (SOCKET s, struct sockaddr *addr, int *addrlen); + int _win_printf (const char *format, ...); + int _win_fprintf (FILE * f, const char *format, ...); + int _win_vprintf (const char *format, va_list ap); + int _win_vfprintf (FILE * stream, const char *format, va_list arg_ptr); + int _win_vsprintf (char *dest, const char *format, va_list arg_ptr); + int _win_vsnprintf (char *str, size_t size, const char *format, + va_list arg_ptr); + int _win_snprintf (char *str, size_t size, const char *format, ...); + int _win_sprintf (char *dest, const char *format, ...); + int _win_vsscanf (const char *str, const char *format, va_list arg_ptr); + int _win_sscanf (const char *str, const char *format, ...); + int _win_vfscanf (FILE * stream, const char *format, va_list arg_ptr); + int _win_vscanf (const char *format, va_list arg_ptr); + int _win_scanf (const char *format, ...); + int _win_fscanf (FILE * stream, const char *format, ...); + pid_t _win_waitpid (pid_t pid, int *stat_loc, int options); + int _win_bind (SOCKET s, const struct sockaddr *name, int namelen); + int _win_connect (SOCKET s, const struct sockaddr *name, int namelen); + int _win_getpeername (SOCKET s, struct sockaddr *name, int *namelen); + int _win_getsockname (SOCKET s, struct sockaddr *name, int *namelen); + int _win_getsockopt (SOCKET s, int level, int optname, char *optval, + int *optlen); + int _win_listen (SOCKET s, int backlog); + int _win_recv (SOCKET s, char *buf, int len, int flags); + int _win_recvfrom (SOCKET s, void *buf, int len, int flags, + struct sockaddr *from, int *fromlen); + int _win_select (int max_fd, fd_set * rfds, fd_set * wfds, fd_set * efds, + const struct timeval *tv); + int _win_send (SOCKET s, const char *buf, int len, int flags); + int _win_sendto (SOCKET s, const char *buf, int len, int flags, + const struct sockaddr *to, int tolen); + int _win_setsockopt (SOCKET s, int level, int optname, const void *optval, + int optlen); + int _win_shutdown (SOCKET s, int how); + SOCKET _win_socket (int af, int type, int protocol); + struct hostent *_win_gethostbyaddr (const char *addr, int len, int type); + struct hostent *_win_gethostbyname (const char *name); + struct hostent *gethostbyname2 (const char *name, int af); + char *_win_strerror (int errnum); + int IsWinNT (); + char *index (const char *s, int c); + +#if !HAVE_STRNDUP + char *strndup (const char *s, size_t n); + +#endif /* */ +#if !HAVE_STRNLEN + size_t strnlen (const char *str, size_t maxlen); + +#endif /* */ + +#define strcasecmp(a, b) stricmp(a, b) +#define strncasecmp(a, b, c) strnicmp(a, b, c) + +#endif /* WINDOWS */ + +#ifndef WINDOWS +#define DIR_SEPARATOR '/' +#define DIR_SEPARATOR_STR "/" +#define PATH_SEPARATOR ';' +#define PATH_SEPARATOR_STR ";" +#define NEWLINE "\n" + +#ifdef ENABLE_NLS +#define BINDTEXTDOMAIN(d, n) bindtextdomain(d, n) +#endif /* */ +#define CREAT(p, m) creat(p, m) +#undef FOPEN +#define FOPEN(f, m) fopen(f, m) +#define FTRUNCATE(f, l) ftruncate(f, l) +#define OPENDIR(d) opendir(d) +#define OPEN open +#define CHDIR(d) chdir(d) +#define CLOSE(f) close(f) +#define LSEEK(f, o, w) lseek(f, o, w) +#define RMDIR(f) rmdir(f) +#define ACCESS(p, m) access(p, m) +#define CHMOD(f, p) chmod(f, p) +#define FSTAT(h, b) fstat(h, b) +#define PLIBC_KILL(p, s) kill(p, s) +#define PIPE(h) pipe(h) +#define REMOVE(p) remove(p) +#define RENAME(o, n) rename(o, n) +#define STAT(p, b) stat(p, b) +#define STAT64(p, b) stat64(p, b) +#define UNLINK(f) unlink(f) +#define WRITE(f, b, n) write(f, b, n) +#define READ(f, b, n) read(f, b, n) +#define GN_FREAD(b, s, c, f) fread(b, s, c, f) +#define GN_FWRITE(b, s, c, f) fwrite(b, s, c, f) +#define SYMLINK(a, b) symlink(a, b) +#define MMAP(s, l, p, f, d, o) mmap(s, l, p, f, d, o) +#define MUNMAP(s, l) munmap(s, l) +#define STRERROR(i) strerror(i) +#define RANDOM() random() +#define READLINK(p, b, s) readlink(p, b, s) +#define LSTAT(p, b) lstat(p, b) +#define LSTAT64(p, b) lstat64(p, b) +#define PRINTF printf +#define FPRINTF fprintf +#define VPRINTF(f, a) vprintf(f, a) +#define VFPRINTF(s, f, a) vfprintf(s, f, a) +#define VSPRINTF(d, f, a) vsprintf(d, f, a) +#define VSNPRINTF(str, size, fmt, a) vsnprintf(str, size, fmt, a) +#define _REAL_SNPRINTF snprintf +#define SPRINTF sprintf +#define VSSCANF(s, f, a) vsscanf(s, f, a) +#define SSCANF sscanf +#define VFSCANF(s, f, a) vfscanf(s, f, a) +#define VSCANF(f, a) vscanf(f, a) +#define SCANF scanf +#define FSCANF fscanf +#define WAITPID(p, s, o) waitpid(p, s, o) +#define ACCEPT(s, a, l) accept(s, a, l) +#define BIND(s, n, l) bind(s, n, l) +#define CONNECT(s, n, l) connect(s, n, l) +#define GETPEERNAME(s, n, l) getpeername(s, n, l) +#define GETSOCKNAME(s, n, l) getsockname(s, n, l) +#define GETSOCKOPT(s, l, o, v, p) getsockopt(s, l, o, v, p) +#define LISTEN(s, b) listen(s, b) +#define RECV(s, b, l, f) recv(s, b, l, f) +#define RECVFROM(s, b, l, f, r, o) recvfrom(s, b, l, f, r, o) +#define SELECT(n, r, w, e, t) select(n, r, w, e, t) +#define SEND(s, b, l, f) send(s, b, l, f) +#define SENDTO(s, b, l, f, o, n) sendto(s, b, l, f, o, n) +#define SETSOCKOPT(s, l, o, v, n) setsockopt(s, l, o, v, n) +#define SHUTDOWN(s, h) shutdown(s, h) +#define SOCKET(a, t, p) socket(a, t, p) +#define GETHOSTBYADDR(a, l, t) gethostbyname(a, l, t) +#define GETHOSTBYNAME(n) gethostbyname(n) +#else /* */ +#define DIR_SEPARATOR '\\' +#define DIR_SEPARATOR_STR "\\" +#define PATH_SEPARATOR ':' +#define PATH_SEPARATOR_STR ":" +#define NEWLINE "\r\n" + +#ifdef ENABLE_NLS +#define BINDTEXTDOMAIN(d, n) _win_bindtextdomain(d, n) +#endif /* */ +#define CREAT(p, m) _win_creat(p, m) +#define FOPEN(f, m) _win_fopen(f, m) +#define FTRUNCATE(f, l) _win_ftruncate(f, l) +#define OPENDIR(d) _win_opendir(d) +#define OPEN _win_open +#define CHDIR(d) _win_chdir(d) +#define CLOSE(f) _win_close(f) +#define PLIBC_KILL(p, s) _win_kill(p, s) +#define LSEEK(f, o, w) _win_lseek(f, o, w) +#define FSTAT(h, b) _win_fstat(h, b) +#define RMDIR(f) _win_rmdir(f) +#define ACCESS(p, m) _win_access(p, m) +#define CHMOD(f, p) _win_chmod(f, p) +#define PIPE(h) _win_pipe(h) +#define RANDOM() _win_random() +#define REMOVE(p) _win_remove(p) +#define RENAME(o, n) _win_rename(o, n) +#define STAT(p, b) _win_stat(p, b) +#define STAT64(p, b) _win_stat64(p, b) +#define UNLINK(f) _win_unlink(f) +#define WRITE(f, b, n) _win_write(f, b, n) +#define READ(f, b, n) _win_read(f, b, n) +#define GN_FREAD(b, s, c, f) _win_fread(b, s, c, f) +#define GN_FWRITE(b, s, c, f) _win_fwrite(b, s, c, f) +#define SYMLINK(a, b) _win_symlink(a, b) +#define MMAP(s, l, p, f, d, o) _win_mmap(s, l, p, f, d, o) +#define MUNMAP(s, l) _win_munmap(s, l) +#define STRERROR(i) _win_strerror(i) +#define READLINK(p, b, s) _win_readlink(p, b, s) +#define LSTAT(p, b) _win_lstat(p, b) +#define LSTAT64(p, b) _win_lstat64(p, b) +#define PRINTF(f, ...) _win_printf(f , __VA_ARGS__) +#define FPRINTF(fil, fmt, ...) _win_fprintf(fil, fmt, __VA_ARGS__) +#define VPRINTF(f, a) _win_vprintf(f, a) +#define VFPRINTF(s, f, a) _win_vfprintf(s, f, a) +#define VSPRINTF(d, f, a) _win_vsprintf(d, f, a) +#define VSNPRINTF(str, size, fmt, a) _win_vsnprintf(str, size, fmt, a) +#define _REAL_SNPRINTF(str, size, fmt, ...) _win_snprintf(str, size, fmt, __VA_ARGS__) +#define SPRINTF(d, f, ...) _win_sprintf(d, f, __VA_ARGS__) +#define VSSCANF(s, f, a) _win_vsscanf(s, f, a) +#define SSCANF(s, f, ...) _win_sscanf(s, f, __VA_ARGS__) +#define VFSCANF(s, f, a) _win_vfscanf(s, f, a) +#define VSCANF(f, a) _win_vscanf(f, a) +#define SCANF(f, ...) _win_scanf(f, __VA_ARGS__) +#define FSCANF(s, f, ...) _win_fscanf(s, f, __VA_ARGS__) +#define WAITPID(p, s, o) _win_waitpid(p, s, o) +#define ACCEPT(s, a, l) _win_accept(s, a, l) +#define BIND(s, n, l) _win_bind(s, n, l) +#define CONNECT(s, n, l) _win_connect(s, n, l) +#define GETPEERNAME(s, n, l) _win_getpeername(s, n, l) +#define GETSOCKNAME(s, n, l) _win_getsockname(s, n, l) +#define GETSOCKOPT(s, l, o, v, p) _win_getsockopt(s, l, o, v, p) +#define LISTEN(s, b) _win_listen(s, b) +#define RECV(s, b, l, f) _win_recv(s, b, l, f) +#define RECVFROM(s, b, l, f, r, o) _win_recvfrom(s, b, l, f, r, o) +#define SELECT(n, r, w, e, t) _win_select(n, r, w, e, t) +#define SEND(s, b, l, f) _win_send(s, b, l, f) +#define SENDTO(s, b, l, f, o, n) _win_sendto(s, b, l, f, o, n) +#define SETSOCKOPT(s, l, o, v, n) _win_setsockopt(s, l, o, v, n) +#define SHUTDOWN(s, h) _win_shutdown(s, h) +#define SOCKET(a, t, p) _win_socket(a, t, p) +#define GETHOSTBYADDR(a, l, t) _win_gethostbyname(a, l, t) +#define GETHOSTBYNAME(n) _win_gethostbyname(n) +#endif /* */ + +#ifdef __cplusplus +} +#endif /* */ + +#endif //_PLIBC_H_ + +/* end of plibc.h */ diff --git a/src/include/winproc.h b/src/include/winproc.h new file mode 100644 index 000000000..a4a173a13 --- /dev/null +++ b/src/include/winproc.h @@ -0,0 +1,216 @@ +/* + This file is part of GNUnet. + (C) 2001, 2002, 2003, 2004, 2005 Christian Grothoff (and other contributing authors) + + GNUnet is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published + by the Free Software Foundation; either version 2, 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 + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with GNUnet; see the file COPYING. If not, write to the + Free Software Foundation, Inc., 59 Temple Place - Suite 330, + Boston, MA 02111-1307, USA. +*/ + +/** + * @file util/winproc.h + * @brief Definitions for MS Windows + * @author Nils Durner + **/ + +#ifndef _WINPROC_H +#define _WINPROC_H + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include /* #define BYTE_ORDER */ +#include +#include +#include +#include "gnunet_util.h" +#include "platform.h" + +#ifdef __cplusplus +extern "C" +{ +#endif + +#ifndef MAX_NAME_LENGTH +#define MAX_NAME_LENGTH 25 +#endif + + typedef DWORD WINAPI (*TNtQuerySystemInformation) (int, PVOID, ULONG, + PULONG); + typedef DWORD WINAPI (*TGetIfEntry) (PMIB_IFROW pIfRow); + typedef DWORD WINAPI (*TGetIpAddrTable) (PMIB_IPADDRTABLE pIpAddrTable, + PULONG pdwSize, BOOL bOrder); + typedef DWORD WINAPI (*TGetIfTable) (PMIB_IFTABLE pIfTable, PULONG pdwSize, + BOOL bOrder); + typedef DWORD WINAPI (*TCreateHardLink) (LPCTSTR lpFileName, + LPCTSTR lpExistingFileName, + LPSECURITY_ATTRIBUTES + lpSecurityAttributes); + typedef SC_HANDLE WINAPI (*TOpenSCManager) (LPCTSTR lpMachineName, + LPCTSTR lpDatabaseName, + DWORD dwDesiredAccess); + typedef SC_HANDLE WINAPI (*TCreateService) (SC_HANDLE hSCManager, + LPCTSTR lpServiceName, + LPCTSTR lpDisplayName, + DWORD dwDesiredAccess, + DWORD dwServiceType, + DWORD dwStartType, + DWORD dwErrorControl, + LPCTSTR lpBinaryPathName, + LPCTSTR lpLoadOrderGroup, + LPDWORD lpdwTagId, + LPCTSTR lpDependencies, + LPCTSTR lpServiceStartName, + LPCTSTR lpPassword); + typedef BOOL WINAPI (*TCloseServiceHandle) (SC_HANDLE hSCObject); + typedef BOOL WINAPI (*TDeleteService) (SC_HANDLE hService); + typedef SERVICE_STATUS_HANDLE WINAPI (*TRegisterServiceCtrlHandler) (LPCTSTR + lpServiceName, + LPHANDLER_FUNCTION + lpHandlerProc); + typedef BOOL WINAPI (*TSetServiceStatus) (SERVICE_STATUS_HANDLE + hServiceStatus, + LPSERVICE_STATUS lpServiceStatus); + typedef BOOL WINAPI (*TStartServiceCtrlDispatcher) (const + LPSERVICE_TABLE_ENTRY + lpServiceTable); + typedef BOOL WINAPI (*TControlService) (SC_HANDLE hService, DWORD dwControl, + LPSERVICE_STATUS lpServiceStatus); + typedef SC_HANDLE WINAPI (*TOpenService) (SC_HANDLE hSCManager, + LPCTSTR lpServiceName, + DWORD dwDesiredAccess); + typedef DWORD WINAPI (*TGetBestInterface) (IPAddr dwDestAddr, + PDWORD pdwBestIfIndex); + typedef DWORD WINAPI (*TGetAdaptersInfo) (PIP_ADAPTER_INFO pAdapterInfo, + PULONG pOutBufLen); + typedef NET_API_STATUS WINAPI (*TNetUserAdd) (LPCWSTR, DWORD, PBYTE, + PDWORD); + typedef NET_API_STATUS WINAPI (*TNetUserSetInfo) (LPCWSTR servername, + LPCWSTR username, + DWORD level, LPBYTE buf, + LPDWORD parm_err); + typedef NTSTATUS NTAPI (*TLsaOpenPolicy) (PLSA_UNICODE_STRING, + PLSA_OBJECT_ATTRIBUTES, + ACCESS_MASK, PLSA_HANDLE); + typedef NTSTATUS NTAPI (*TLsaAddAccountRights) (LSA_HANDLE, PSID, + PLSA_UNICODE_STRING, ULONG); + typedef NTSTATUS NTAPI (*TLsaRemoveAccountRights) (LSA_HANDLE, PSID, + BOOLEAN, + PLSA_UNICODE_STRING, + ULONG); + typedef NTSTATUS NTAPI (*TLsaClose) (LSA_HANDLE); + typedef BOOL WINAPI (*TLookupAccountName) (LPCTSTR lpSystemName, + LPCTSTR lpAccountName, PSID Sid, + LPDWORD cbSid, + LPTSTR ReferencedDomainName, + LPDWORD cchReferencedDomainName, + PSID_NAME_USE peUse); + + typedef BOOL WINAPI (*TGetFileSecurity) (LPCTSTR lpFileName, + SECURITY_INFORMATION + RequestedInformation, + PSECURITY_DESCRIPTOR + pSecurityDescriptor, DWORD nLength, + LPDWORD lpnLengthNeeded); + typedef BOOL WINAPI (*TInitializeSecurityDescriptor) (PSECURITY_DESCRIPTOR + pSecurityDescriptor, + DWORD dwRevision); + typedef BOOL WINAPI (*TGetSecurityDescriptorDacl) (PSECURITY_DESCRIPTOR + pSecurityDescriptor, + LPBOOL lpbDaclPresent, + PACL * pDacl, + LPBOOL lpbDaclDefaulted); + typedef BOOL WINAPI (*TGetAclInformation) (PACL pAcl, + LPVOID pAclInformation, + DWORD nAclInformationLength, + ACL_INFORMATION_CLASS + dwAclInformationClass); + typedef BOOL WINAPI (*TInitializeAcl) (PACL pAcl, DWORD nAclLength, + DWORD dwAclRevision); + typedef BOOL WINAPI (*TGetAce) (PACL pAcl, DWORD dwAceIndex, LPVOID * pAce); + typedef BOOL WINAPI (*TEqualSid) (PSID pSid1, PSID pSid2); + typedef BOOL WINAPI (*TAddAce) (PACL pAcl, DWORD dwAceRevision, + DWORD dwStartingAceIndex, LPVOID pAceList, + DWORD nAceListLength); + typedef BOOL WINAPI (*TAddAccessAllowedAce) (PACL pAcl, DWORD dwAceRevision, + DWORD AccessMask, PSID pSid); + typedef BOOL WINAPI (*TSetNamedSecurityInfo) (LPTSTR pObjectName, + SE_OBJECT_TYPE ObjectType, + SECURITY_INFORMATION + SecurityInfo, PSID psidOwner, + PSID psidGroup, PACL pDacl, + PACL pSacl); + + extern TNtQuerySystemInformation GNNtQuerySystemInformation; + extern TGetIfEntry GNGetIfEntry; + extern TGetIpAddrTable GNGetIpAddrTable; + extern TGetIfTable GNGetIfTable; + extern TCreateHardLink GNCreateHardLink; + extern TOpenSCManager GNOpenSCManager; + extern TCreateService GNCreateService; + extern TCloseServiceHandle GNCloseServiceHandle; + extern TDeleteService GNDeleteService; + extern TRegisterServiceCtrlHandler GNRegisterServiceCtrlHandler; + extern TSetServiceStatus GNSetServiceStatus; + extern TStartServiceCtrlDispatcher GNStartServiceCtrlDispatcher; + extern TControlService GNControlService; + extern TOpenService GNOpenService; + extern TGetBestInterface GNGetBestInterface; + extern TGetAdaptersInfo GGetAdaptersInfo; + extern TNetUserAdd GNNetUserAdd; + extern TNetUserSetInfo GNNetUserSetInfo; + extern TLsaOpenPolicy GNLsaOpenPolicy; + extern TLsaAddAccountRights GNLsaAddAccountRights; + extern TLsaRemoveAccountRights GNLsaRemoveAccountRights; + extern TLsaClose GNLsaClose; + extern TLookupAccountName GNLookupAccountName; + extern TGetFileSecurity GNGetFileSecurity; + extern TInitializeSecurityDescriptor GNInitializeSecurityDescriptor; + extern TGetSecurityDescriptorDacl GNGetSecurityDescriptorDacl; + extern TGetAclInformation GNGetAclInformation; + extern TInitializeAcl GNInitializeAcl; + extern TGetAce GNGetAce; + extern TEqualSid GNEqualSid; + extern TAddAce GNAddAce; + extern TAddAccessAllowedAce GNAddAccessAllowedAce; + extern TSetNamedSecurityInfo GNSetNamedSecurityInfo; + + + BOOL CreateShortcut (const char *pszSrc, const char *pszDest); + BOOL DereferenceShortcut (char *pszShortcut); + long QueryRegistry (HKEY hMainKey, char *pszKey, char *pszSubKey, + char *pszBuffer, long *pdLength); + int ListNICs (void (*callback) (const char *, int, void *), void *cls); + BOOL AddPathAccessRights (char *lpszFileName, char *lpszAccountName, + DWORD dwAccessMask); + char *winErrorStr (const char *prefix, int dwErr); + + void GNInitWinEnv (); + void GNShutdownWinEnv (); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/src/peerinfo/Makefile.am b/src/peerinfo/Makefile.am new file mode 100644 index 000000000..294ff5cca --- /dev/null +++ b/src/peerinfo/Makefile.am @@ -0,0 +1,56 @@ +INCLUDES = -I$(top_srcdir)/src/include + +if MINGW + WINFLAGS = -Wl,--no-undefined -Wl,--export-all-symbols -lole32 -lshell32 -luuid -liconv -lstdc++ -lcomdlg32 -lgdi32 +endif + +if USE_COVERAGE + AM_CFLAGS = -fprofile-arcs -ftest-coverage +endif + +lib_LTLIBRARIES = libgnunetpeerinfo.la + +libgnunetpeerinfo_la_SOURCES = \ + peerinfo_api.c peerinfo.h +libgnunetpeerinfo_la_LIBADD = \ + $(top_builddir)/src/hello/libgnunethello.la \ + $(top_builddir)/src/util/libgnunetutil.la +libgnunetpeerinfo_la_LDFLAGS = \ + $(GN_LIB_LDFLAGS) $(WINFLAGS) \ + -version-info 0:0:0 + + +bin_PROGRAMS = \ + gnunet-peerinfo \ + gnunet-service-peerinfo + +gnunet_peerinfo_SOURCES = \ + gnunet-peerinfo.c +gnunet_peerinfo_LDADD = \ + $(top_builddir)/src/peerinfo/libgnunetpeerinfo.la \ + $(top_builddir)/src/util/libgnunetutil.la + +gnunet_service_peerinfo_SOURCES = \ + gnunet-service-peerinfo.c +gnunet_service_peerinfo_LDADD = \ + $(top_builddir)/src/hello/libgnunethello.la \ + $(top_builddir)/src/util/libgnunetutil.la + + +check_PROGRAMS = \ + test_peerinfo_api + +TESTS = $(check_PROGRAMS) # $(check_SCRIPTS) + +test_peerinfo_api_SOURCES = \ + test_peerinfo_api.c +test_peerinfo_api_LDADD = \ + $(top_builddir)/src/hello/libgnunethello.la \ + $(top_builddir)/src/peerinfo/libgnunetpeerinfo.la \ + $(top_builddir)/src/util/libgnunetutil.la + +EXTRA_DIST = \ + test_peerinfo_api_data.conf + +#check_SCRIPTS = \ +# test_gnunet_peerinfo.sh diff --git a/src/peerinfo/gnunet-peerinfo.c b/src/peerinfo/gnunet-peerinfo.c new file mode 100644 index 000000000..6c737c88a --- /dev/null +++ b/src/peerinfo/gnunet-peerinfo.c @@ -0,0 +1,152 @@ +/* + This file is part of GNUnet. + (C) 2001, 2002, 2003, 2004, 2006, 2009 Christian Grothoff (and other contributing authors) + + GNUnet is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published + by the Free Software Foundation; either version 2, 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 + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with GNUnet; see the file COPYING. If not, write to the + Free Software Foundation, Inc., 59 Temple Place - Suite 330, + Boston, MA 02111-1307, USA. +*/ + +/** + * @file peerinfo/gnunet-peerinfo.c + * @brief Print information about other known peers. + * @author Christian Grothoff + */ +#include "platform.h" +#include "gnunet_crypto_lib.h" +#include "gnunet_configuration_lib.h" +#include "gnunet_getopt_lib.h" +#include "gnunet_peerinfo_service.h" +#include "gnunet_program_lib.h" + +static int no_resolve; + +static int be_quiet; + +static int get_self; + +/** + * Print information about the peer. + * Currently prints the GNUNET_PeerIdentity, trust and the IP. + * Could of course do more (e.g. resolve via DNS). + */ +static void +print_peer_info (void *cls, + const struct GNUNET_PeerIdentity *peer, + const struct GNUNET_HELLO_Message *hello, uint32_t trust) +{ + struct GNUNET_CRYPTO_HashAsciiEncoded enc; + + /* FIXME: add printing of address information! + => need extended transport API! */ + GNUNET_CRYPTO_hash_to_enc (&peer->hashPubKey, &enc); + if (be_quiet) + printf ("%s\n", (const char *) &enc); + else + printf (_("Peer `%s' with trust %8u\n"), (const char *) &enc, trust); +} + +/** + * Main function that will be run by the scheduler. + * + * @param cls closure + * @param sched the scheduler to use + * @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, + struct GNUNET_SCHEDULER_Handle *sched, + char *const *args, + const char *cfgfile, struct GNUNET_CONFIGURATION_Handle *cfg) +{ + struct GNUNET_CRYPTO_RsaPrivateKey *priv; + struct GNUNET_CRYPTO_RsaPublicKeyBinaryEncoded pub; + struct GNUNET_PeerIdentity pid; + struct GNUNET_CRYPTO_HashAsciiEncoded enc; + char *fn; + + if (get_self != GNUNET_YES) + { + GNUNET_PEERINFO_for_all (cfg, + sched, + NULL, + 0, + GNUNET_TIME_relative_multiply + (GNUNET_TIME_UNIT_SECONDS, 30), + &print_peer_info, NULL); + } + else + { + if (GNUNET_OK != + GNUNET_CONFIGURATION_get_value_filename (cfg, + "GNUNET", + "HOSTKEYFILE", &fn)) + return; + priv = GNUNET_CRYPTO_rsa_key_create_from_file (fn); + if (priv == NULL) + { + fprintf (stderr, _("Loading hostkey from `%s' failed.\n"), fn); + GNUNET_free (fn); + return; + } + GNUNET_free (fn); + GNUNET_CRYPTO_rsa_key_get_public (priv, &pub); + GNUNET_CRYPTO_rsa_key_free (priv); + GNUNET_CRYPTO_hash (&pub, sizeof (pub), &pid.hashPubKey); + GNUNET_CRYPTO_hash_to_enc (&pid.hashPubKey, &enc); + if (be_quiet) + printf ("%s\n", (char *) &enc); + else + printf (_("I am peer `%s'.\n"), (const char *) &enc); + } +} + + +/** + * gnunet-peerinfo command line options + */ +static struct GNUNET_GETOPT_CommandLineOption options[] = { + {'n', "numeric", NULL, + gettext_noop ("don't resolve host names"), + 0, &GNUNET_GETOPT_set_one, &no_resolve}, + {'q', "quiet", NULL, + gettext_noop ("output only the identity strings"), + 0, &GNUNET_GETOPT_set_one, &be_quiet}, + {'s', "self", NULL, + gettext_noop ("output our own identity only"), + 0, &GNUNET_GETOPT_set_one, &get_self}, + GNUNET_GETOPT_OPTION_END +}; + +/** + * The main function to obtain peer information. + * + * @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) +{ + return (GNUNET_OK == + GNUNET_PROGRAM_run (argc, + argv, + "gnunet-peerinfo", + gettext_noop ("Print information about peers."), + options, &run, NULL)) ? 0 : 1; +} + +/* end of gnunet-peerinfo.c */ diff --git a/src/peerinfo/gnunet-service-peerinfo.c b/src/peerinfo/gnunet-service-peerinfo.c new file mode 100644 index 000000000..b81c7b6ee --- /dev/null +++ b/src/peerinfo/gnunet-service-peerinfo.c @@ -0,0 +1,708 @@ +/* + This file is part of GNUnet. + (C) 2001, 2002, 2004, 2005, 2007, 2009 Christian Grothoff (and other contributing authors) + + GNUnet is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published + by the Free Software Foundation; either version 2, 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 + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with GNUnet; see the file COPYING. If not, write to the + Free Software Foundation, Inc., 59 Temple Place - Suite 330, + Boston, MA 02111-1307, USA. +*/ + +/** + * @file peerinfo/gnunet-service-peerinfo.c + * @brief maintains list of known peers + * + * Code to maintain the list of currently known hosts (in memory + * structure of data/hosts/ and data/credit/). + * + * @author Christian Grothoff + */ + +#include "platform.h" +#include "gnunet_crypto_lib.h" +#include "gnunet_disk_lib.h" +#include "gnunet_hello_lib.h" +#include "gnunet_protocols.h" +#include "gnunet_service_lib.h" +#include "peerinfo.h" + +/** + * How often do we scan the HOST_DIR for new entries? + */ +#define DATA_HOST_FREQ GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_MINUTES, 15) + +/** + * How often do we flush trust values to disk? + */ +#define TRUST_FLUSH_FREQ GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_MINUTES, 5) + +/** + * How often do we discard old entries in data/hosts/? + */ +#define DATA_HOST_CLEAN_FREQ GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_MINUTES, 60) + +/** + * In-memory cache of known hosts. + */ +struct HostEntry +{ + + /** + * This is a linked list. + */ + struct HostEntry *next; + + /** + * Identity of the peer. + */ + struct GNUNET_PeerIdentity identity; + + /** + * Hello for the peer (can be NULL) + */ + struct GNUNET_HELLO_Message *hello; + + /** + * Trust rating for this peer + */ + uint32_t trust; + + /** + * Trust rating for this peer on disk. + */ + uint32_t disk_trust; + +}; + +/** + * The in-memory list of known hosts. + */ +static struct HostEntry *hosts; + +/** + * Directory where the hellos are stored in (data/hosts) + */ +static char *networkIdDirectory; + +/** + * Where do we store trust information? + */ +static char *trustDirectory; + + +/** + * Address iterator that causes expired entries to be discarded. + * + * @param cls pointer to the current time + * @return GNUNET_NO if expiration smaller than the current time + */ +static int +discard_expired (void *cls, + const char *tname, + struct GNUNET_TIME_Absolute expiration, + const void *addr, size_t addrlen) +{ + const struct GNUNET_TIME_Absolute *now = cls; + if (now->value > expiration.value) + return GNUNET_NO; + return GNUNET_OK; +} + + +/** + * Get the filename under which we would store the GNUNET_HELLO_Message + * for the given host and protocol. + * @return filename of the form DIRECTORY/HOSTID + */ +static char * +get_host_filename (const struct GNUNET_PeerIdentity *id) +{ + struct GNUNET_CRYPTO_HashAsciiEncoded fil; + char *fn; + + GNUNET_CRYPTO_hash_to_enc (&id->hashPubKey, &fil); + GNUNET_asprintf (&fn, + "%s%s%s", networkIdDirectory, DIR_SEPARATOR_STR, &fil); + return fn; +} + + +/** + * Get the filename under which we would store the GNUNET_HELLO_Message + * for the given host and protocol. + * @return filename of the form DIRECTORY/HOSTID + */ +static char * +get_trust_filename (const struct GNUNET_PeerIdentity *id) +{ + struct GNUNET_CRYPTO_HashAsciiEncoded fil; + char *fn; + + GNUNET_CRYPTO_hash_to_enc (&id->hashPubKey, &fil); + GNUNET_asprintf (&fn, "%s%s%s", trustDirectory, DIR_SEPARATOR_STR, &fil); + return fn; +} + +/** + * Find the host entry for the given peer. Call + * only when synchronized! + * @return NULL if not found + */ +static struct HostEntry * +lookup_host_entry (const struct GNUNET_PeerIdentity *id) +{ + struct HostEntry *pos; + + pos = hosts; + while ((pos != NULL) && + (0 != + memcmp (id, &pos->identity, sizeof (struct GNUNET_PeerIdentity)))) + pos = pos->next; + return pos; +} + + +/** + * Add a host to the list. + * + * @param identity the identity of the host + * @param protocol the protocol for the host + */ +static void +add_host_to_known_hosts (const struct GNUNET_PeerIdentity *identity) +{ + struct HostEntry *entry; + char *fn; + uint32_t trust; + char buffer[GNUNET_SERVER_MAX_MESSAGE_SIZE]; + const struct GNUNET_HELLO_Message *hello; + struct GNUNET_HELLO_Message *hello_clean; + int size; + struct GNUNET_TIME_Absolute now; + + entry = lookup_host_entry (identity); + if (entry != NULL) + return; + entry = GNUNET_malloc (sizeof (struct HostEntry)); + entry->identity = *identity; + fn = get_trust_filename (identity); + if ((GNUNET_DISK_file_test (fn) == GNUNET_YES) && + (sizeof (trust) == GNUNET_DISK_file_read (fn, sizeof (trust), &trust))) + entry->disk_trust = entry->trust = ntohl (trust); + GNUNET_free (fn); + + fn = get_host_filename (identity); + if (GNUNET_DISK_file_test (fn) == GNUNET_YES) + { + size = GNUNET_DISK_file_read (fn, sizeof (buffer), buffer); + hello = (const struct GNUNET_HELLO_Message *) buffer; + now = GNUNET_TIME_absolute_get (); + hello_clean = GNUNET_HELLO_iterate_addresses (hello, + GNUNET_YES, + &discard_expired, &now); + entry->hello = hello_clean; + } + GNUNET_free (fn); + entry->next = hosts; + hosts = entry; +} + + +/** + * Increase the host credit by a value. + * + * @param hostId is the identity of the host + * @param value is the int value by which the + * host credit is to be increased or decreased + * @returns the actual change in trust (positive or negative) + */ +static int +change_host_trust (const struct GNUNET_PeerIdentity *hostId, int value) +{ + struct HostEntry *host; + + if (value == 0) + return 0; + host = lookup_host_entry (hostId); + if (host == NULL) + { + add_host_to_known_hosts (hostId); + host = lookup_host_entry (hostId); + } + GNUNET_assert (host != NULL); + if (value > 0) + { + if (host->trust + value < host->trust) + { + value = ((uint32_t) - 1) - host->trust; + host->trust = (uint32_t) - 1; /* maximized */ + } + else + host->trust += value; + } + else + { + if (host->trust < -value) + { + value = -host->trust; + host->trust = 0; + } + else + host->trust += value; + } + return value; +} + + +/** + * Remove a file that should not be there. LOG + * success or failure. + */ +static void +remove_garbage (const char *fullname) +{ + if (0 == UNLINK (fullname)) + GNUNET_log (GNUNET_ERROR_TYPE_WARNING | GNUNET_ERROR_TYPE_BULK, + _ + ("File `%s' in directory `%s' does not match naming convention. " + "Removed.\n"), fullname, networkIdDirectory); + else + GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_ERROR | + GNUNET_ERROR_TYPE_BULK, "unlink", fullname); +} + + +static int +hosts_directory_scan_callback (void *cls, const char *fullname) +{ + unsigned int *matched = cls; + struct GNUNET_PeerIdentity identity; + const char *filename; + + if (GNUNET_DISK_file_test (fullname) != GNUNET_YES) + return GNUNET_OK; /* ignore non-files */ + if (strlen (fullname) < sizeof (struct GNUNET_CRYPTO_HashAsciiEncoded)) + { + remove_garbage (fullname); + return GNUNET_OK; + } + filename = + &fullname[strlen (fullname) - + sizeof (struct GNUNET_CRYPTO_HashAsciiEncoded) + 1]; + if (filename[-1] != DIR_SEPARATOR) + { + remove_garbage (fullname); + return GNUNET_OK; + } + if (GNUNET_OK != GNUNET_CRYPTO_hash_from_string (filename, + &identity.hashPubKey)) + { + remove_garbage (fullname); + return GNUNET_OK; + } + (*matched)++; + add_host_to_known_hosts (&identity); + return GNUNET_OK; +} + + +/** + * Call this method periodically to scan data/hosts for new hosts. + */ +static void +cron_scan_directory_data_hosts (void *cls, + const struct GNUNET_SCHEDULER_TaskContext *tc) +{ + static unsigned int retries; + unsigned int count; + + count = 0; + GNUNET_DISK_directory_scan (networkIdDirectory, + &hosts_directory_scan_callback, &count); + if ((0 == count) && (0 == (++retries & 31))) + GNUNET_log (GNUNET_ERROR_TYPE_WARNING | + GNUNET_ERROR_TYPE_BULK, + _("Still no peers found in `%s'!\n"), networkIdDirectory); + GNUNET_SCHEDULER_add_delayed (tc->sched, + GNUNET_NO, + GNUNET_SCHEDULER_PRIORITY_KEEP, + GNUNET_SCHEDULER_NO_PREREQUISITE_TASK, + DATA_HOST_FREQ, + &cron_scan_directory_data_hosts, NULL); +} + + +/** + * Bind a host address (hello) to a hostId. + * + * @param peer the peer for which this is a hello + * @param hello the verified (!) hello message + */ +static void +bind_address (const struct GNUNET_PeerIdentity *peer, + const struct GNUNET_HELLO_Message *hello) +{ + char *fn; + struct HostEntry *host; + struct GNUNET_HELLO_Message *mrg; + + add_host_to_known_hosts (peer); + host = lookup_host_entry (peer); + GNUNET_assert (host != NULL); + if (host->hello == NULL) + { + host->hello = GNUNET_malloc (GNUNET_HELLO_size (hello)); + memcpy (host->hello, hello, GNUNET_HELLO_size (hello)); + } + else + { + mrg = GNUNET_HELLO_merge (host->hello, hello); + GNUNET_free (host->hello); + host->hello = mrg; + } + fn = get_host_filename (peer); + GNUNET_DISK_file_write (fn, host->hello, GNUNET_HELLO_size (hello), "644"); + GNUNET_free (fn); +} + + +/** + * Do transmit info either for only the host matching the given + * argument or for all known hosts and change their trust values by + * the given delta. + * + * @param only NULL to hit all hosts + */ +static void +send_to_each_host (const struct GNUNET_PeerIdentity *only, + int trust_change, + struct GNUNET_SERVER_Client *client, + struct GNUNET_SERVER_Handle *server) +{ + struct HostEntry *pos; + struct InfoMessage *im; + const struct GNUNET_MessageHeader *end; + uint16_t hs; + char buf[GNUNET_SERVER_MAX_MESSAGE_SIZE]; + struct GNUNET_SERVER_TransmitContext *tc; + + tc = GNUNET_SERVER_transmit_context_create (client); + pos = hosts; + while (pos != NULL) + { + if ((only == NULL) || + (0 == + memcmp (only, &pos->identity, + sizeof (struct GNUNET_PeerIdentity)))) + { + change_host_trust (&pos->identity, trust_change); + hs = 0; + im = (struct InfoMessage *) buf; + if (pos->hello != NULL) + { + hs = GNUNET_HELLO_size (pos->hello); + GNUNET_assert (hs < + GNUNET_SERVER_MAX_MESSAGE_SIZE - + sizeof (struct InfoMessage)); + memcpy (&im[1], pos->hello, hs); + } + im->trust = htonl (pos->trust); + im->peer = pos->identity; + end = &im->header; + GNUNET_SERVER_transmit_context_append (tc, + &end[1], + hs + + sizeof (struct InfoMessage) - + sizeof (struct + GNUNET_MessageHeader), + GNUNET_MESSAGE_TYPE_PEERINFO_INFO); + } + pos = pos->next; + } + GNUNET_SERVER_transmit_context_append (tc, NULL, 0, + GNUNET_MESSAGE_TYPE_PEERINFO_INFO_END); + GNUNET_SERVER_transmit_context_run (tc, GNUNET_TIME_UNIT_FOREVER_REL); +} + + +/** + * Write host-trust information to a file - flush the buffer entry! + * Assumes synchronized access. + */ +static void +flush_trust (struct HostEntry *host) +{ + char *fn; + uint32_t trust; + + if (host->trust == host->disk_trust) + return; /* unchanged */ + fn = get_trust_filename (&host->identity); + if (host->trust == 0) + { + if ((0 != UNLINK (fn)) && (errno != ENOENT)) + GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING | + GNUNET_ERROR_TYPE_BULK, "unlink", fn); + } + else + { + trust = htonl (host->trust); + if (GNUNET_OK == + GNUNET_DISK_file_write (fn, &trust, sizeof (uint32_t), "644")) + host->disk_trust = host->trust; + } + GNUNET_free (fn); +} + +/** + * Call this method periodically to scan data/hosts for new hosts. + */ +static void +cron_flush_trust (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc) +{ + struct HostEntry *pos; + + pos = hosts; + while (pos != NULL) + { + flush_trust (pos); + pos = pos->next; + } + if (0 == (tc->reason & GNUNET_SCHEDULER_REASON_SHUTDOWN)) + GNUNET_SCHEDULER_add_delayed (tc->sched, + GNUNET_YES, + GNUNET_SCHEDULER_PRIORITY_KEEP, + GNUNET_SCHEDULER_NO_PREREQUISITE_TASK, + TRUST_FLUSH_FREQ, &cron_flush_trust, NULL); +} + + +/** + * @brief delete expired HELLO entries in data/hosts/ + */ +static int +discard_hosts_helper (void *cls, const char *fn) +{ + struct GNUNET_TIME_Absolute *now = cls; + char buffer[GNUNET_SERVER_MAX_MESSAGE_SIZE]; + const struct GNUNET_HELLO_Message *hello; + struct GNUNET_HELLO_Message *new_hello; + int size; + + size = GNUNET_DISK_file_read (fn, sizeof (buffer), buffer); + if ((size < sizeof (struct GNUNET_MessageHeader)) && (0 != UNLINK (fn))) + { + GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING | + GNUNET_ERROR_TYPE_BULK, "unlink", fn); + return GNUNET_OK; + } + hello = (const struct GNUNET_HELLO_Message *) buffer; + new_hello = GNUNET_HELLO_iterate_addresses (hello, + GNUNET_YES, + &discard_expired, now); + if ((new_hello == NULL) && (0 != UNLINK (fn))) + GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING | + GNUNET_ERROR_TYPE_BULK, "unlink", fn); + if (new_hello != NULL) + { + GNUNET_DISK_file_write (fn, + new_hello, + GNUNET_HELLO_size (new_hello), "644"); + GNUNET_free (new_hello); + } + return GNUNET_OK; +} + + +/** + * Call this method periodically to scan data/hosts for new hosts. + */ +static void +cron_clean_data_hosts (void *cls, + const struct GNUNET_SCHEDULER_TaskContext *tc) +{ + struct GNUNET_TIME_Absolute now; + + now = GNUNET_TIME_absolute_get (); + GNUNET_DISK_directory_scan (networkIdDirectory, + &discard_hosts_helper, &now); + + GNUNET_SCHEDULER_add_delayed (tc->sched, + GNUNET_NO, + GNUNET_SCHEDULER_PRIORITY_KEEP, + GNUNET_SCHEDULER_NO_PREREQUISITE_TASK, + DATA_HOST_CLEAN_FREQ, + &cron_clean_data_hosts, NULL); +} + + +/** + * Handle ADD-message. + * + * @param cls closure + * @param server the server handling the message + * @param client identification of the client + * @param message the actual message + */ +static void +handle_add (void *cls, + struct GNUNET_SERVER_Handle *server, + struct GNUNET_SERVER_Client *client, + const struct GNUNET_MessageHeader *message) +{ + const struct PeerAddMessage *pam; + const struct GNUNET_MessageHeader *hello; + uint16_t size; + + size = ntohs (message->size); + if (size < + sizeof (struct PeerAddMessage) + sizeof (struct GNUNET_MessageHeader)) + { + GNUNET_break (0); + GNUNET_SERVER_receive_done (client, GNUNET_SYSERR); + return; + } + pam = (const struct PeerAddMessage *) message; + hello = (const struct GNUNET_MessageHeader *) &pam[1]; + if (size != sizeof (struct PeerAddMessage) + ntohs (hello->size)) + { + GNUNET_break (0); + GNUNET_SERVER_receive_done (client, GNUNET_SYSERR); + return; + } + bind_address (&pam->peer, (const struct GNUNET_HELLO_Message *) hello); + GNUNET_SERVER_receive_done (client, GNUNET_OK); +} + + +/** + * Handle GET-message. + * + * @param cls closure + * @param server the server handling the message + * @param client identification of the client + * @param message the actual message + */ +static void +handle_get (void *cls, + struct GNUNET_SERVER_Handle *server, + struct GNUNET_SERVER_Client *client, + const struct GNUNET_MessageHeader *message) +{ + const struct ListPeerMessage *lpm; + + lpm = (const struct ListPeerMessage *) message; + send_to_each_host (&lpm->peer, ntohl (lpm->trust_change), client, server); +} + + +/** + * Handle GET-ALL-message. + * + * @param cls closure + * @param server the server handling the message + * @param client identification of the client + * @param message the actual message + */ +static void +handle_get_all (void *cls, + struct GNUNET_SERVER_Handle *server, + struct GNUNET_SERVER_Client *client, + const struct GNUNET_MessageHeader *message) +{ + const struct ListAllPeersMessage *lpm; + + lpm = (const struct ListAllPeersMessage *) message; + send_to_each_host (NULL, ntohl (lpm->trust_change), client, server); +} + + +/** + * List of handlers for the messages understood by this + * service. + */ +static struct GNUNET_SERVER_MessageHandler handlers[] = { + {&handle_add, NULL, GNUNET_MESSAGE_TYPE_PEERINFO_ADD, 0}, + {&handle_get, NULL, GNUNET_MESSAGE_TYPE_PEERINFO_GET, + sizeof (struct ListPeerMessage)}, + {&handle_get_all, NULL, GNUNET_MESSAGE_TYPE_PEERINFO_GET_ALL, + sizeof (struct ListAllPeersMessage)}, + {NULL, NULL, 0, 0} +}; + + +/** + * Process statistics requests. + * + * @param cls closure + * @param sched scheduler to use + * @param server the initialized server + * @param cfg configuration to use + */ +static void +run (void *cls, + struct GNUNET_SCHEDULER_Handle *sched, + struct GNUNET_SERVER_Handle *server, + struct GNUNET_CONFIGURATION_Handle *cfg) +{ + GNUNET_assert (GNUNET_OK == + GNUNET_CONFIGURATION_get_value_filename (cfg, + "peerinfo", + "HOSTS", + &networkIdDirectory)); + GNUNET_assert (GNUNET_OK == + GNUNET_CONFIGURATION_get_value_filename (cfg, + "peerinfo", + "TRUST", + &trustDirectory)); + GNUNET_DISK_directory_create (networkIdDirectory); + GNUNET_DISK_directory_create (trustDirectory); + GNUNET_SCHEDULER_add_delayed (sched, + GNUNET_NO, + GNUNET_SCHEDULER_PRIORITY_IDLE, + GNUNET_SCHEDULER_NO_PREREQUISITE_TASK, + GNUNET_TIME_UNIT_MILLISECONDS, + &cron_scan_directory_data_hosts, NULL); + GNUNET_SCHEDULER_add_delayed (sched, + GNUNET_YES, + GNUNET_SCHEDULER_PRIORITY_HIGH, + GNUNET_SCHEDULER_NO_PREREQUISITE_TASK, + TRUST_FLUSH_FREQ, &cron_flush_trust, NULL); + GNUNET_SCHEDULER_add_delayed (sched, + GNUNET_NO, + GNUNET_SCHEDULER_PRIORITY_IDLE, + GNUNET_SCHEDULER_NO_PREREQUISITE_TASK, + DATA_HOST_CLEAN_FREQ, + &cron_clean_data_hosts, NULL); + GNUNET_SERVER_add_handlers (server, handlers); +} + + +/** + * The main function for the statistics service. + * + * @param argc number of arguments from the command line + * @param argv command line arguments + * @return 0 ok, 1 on error + */ +int +main (int argc, char *const *argv) +{ + return (GNUNET_OK == + GNUNET_SERVICE_run (argc, + argv, + "peerinfo", &run, NULL, NULL, NULL)) ? 0 : 1; +} + + +/* end of gnunet-service-peerinfo.c */ diff --git a/src/peerinfo/peerinfo.h b/src/peerinfo/peerinfo.h new file mode 100644 index 000000000..040b084e4 --- /dev/null +++ b/src/peerinfo/peerinfo.h @@ -0,0 +1,135 @@ +/* + This file is part of GNUnet. + (C) 2009 Christian Grothoff (and other contributing authors) + + GNUnet is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published + by the Free Software Foundation; either version 2, 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 + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with GNUnet; see the file COPYING. If not, write to the + Free Software Foundation, Inc., 59 Temple Place - Suite 330, + Boston, MA 02111-1307, USA. +*/ + +/** + * @file peerinfo/peerinfo.h + * @brief common internal definitions for peerinfo service + * @author Christian Grothoff + */ +#include "gnunet_crypto_lib.h" +#include "gnunet_time_lib.h" +#include "gnunet_peerinfo_service.h" + + +/** + * Add the given peer to the list. This message + * is always followed by a verified HELLO message. + */ +struct PeerAddMessage +{ + + /** + * Type will be GNUNET_MESSAGE_TYPE_PEERINFO_ADD + */ + struct GNUNET_MessageHeader header; + + /** + * Always zero. + */ + uint32_t reserved GNUNET_PACKED; + + /** + * For which peer do we provide a HELLO message here? + */ + struct GNUNET_PeerIdentity peer; + +}; + + +/** + * Message requesting a listing of all known peers, + * possibly modified by the specified trust value + * and restricted to the specified peer identity. + */ +struct ListPeerMessage +{ + + /** + * Type will be GNUNET_MESSAGE_TYPE_PEERINFO_GET + */ + struct GNUNET_MessageHeader header; + + /** + * How much to change the trust in each returned peer, + * in network byte order. + */ + int32_t trust_change GNUNET_PACKED; + + /** + * Restrict to peers with this identity (optional + * field, check header.size!). + */ + struct GNUNET_PeerIdentity peer; + +}; + + +/** + * Message requesting a listing of all known peers, + * possibly modified by the specified trust value + * and restricted to the specified peer identity. + */ +struct ListAllPeersMessage +{ + + /** + * Type will be GNUNET_MESSAGE_TYPE_PEERINFO_GET + */ + struct GNUNET_MessageHeader header; + + /** + * How much to change the trust in each returned peer, + * in network byte order. + */ + int32_t trust_change GNUNET_PACKED; + +}; + + +/** + * Message used to inform the client about + * a particular peer; this message is optionally followed + * by a HELLO message for the respective peer (if available). + * Check the header.size field to see if a HELLO is + * present. + */ +struct InfoMessage +{ + + /** + * Type will be GNUNET_MESSAGE_TYPE_PEERINFO_INFO + */ + struct GNUNET_MessageHeader header; + + /** + * Amount of trust we now have in the peer, + * in network byte order. + */ + uint32_t trust GNUNET_PACKED; + + /** + * About which peer are we talking here? + */ + struct GNUNET_PeerIdentity peer; + +}; + + +/* end of peerinfo.h */ diff --git a/src/peerinfo/peerinfo_api.c b/src/peerinfo/peerinfo_api.c new file mode 100644 index 000000000..ba5ade199 --- /dev/null +++ b/src/peerinfo/peerinfo_api.c @@ -0,0 +1,302 @@ +/* + This file is part of GNUnet. + (C) 2001, 2002, 2004, 2005, 2007, 2009 Christian Grothoff (and other contributing authors) + + GNUnet is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published + by the Free Software Foundation; either version 2, 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 + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with GNUnet; see the file COPYING. If not, write to the + Free Software Foundation, Inc., 59 Temple Place - Suite 330, + Boston, MA 02111-1307, USA. +*/ + +/** + * @file peerinfo/peerinfo_api.c + * @brief API to access peerinfo service + * @author Christian Grothoff + */ +#include "platform.h" +#include "gnunet_client_lib.h" +#include "gnunet_peerinfo_service.h" +#include "gnunet_protocols.h" +#include "gnunet_time_lib.h" +#include "peerinfo.h" + +#define ADD_PEER_TIMEOUT GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS, 30) + + +struct CAFContext +{ + struct GNUNET_CLIENT_Connection *client; + struct GNUNET_MessageHeader *msg; +}; + + +static size_t +copy_and_free (void *cls, size_t size, void *buf) +{ + struct CAFContext *cc = cls; + struct GNUNET_MessageHeader *msg = cc->msg; + uint16_t msize; + + if (buf == NULL) + { + GNUNET_log (GNUNET_ERROR_TYPE_WARNING, + _ + ("Failed to transmit message of type %u to `%s' service.\n"), + ntohs (msg->type), "peerinfo"); + GNUNET_free (msg); + GNUNET_CLIENT_disconnect (cc->client); + GNUNET_free (cc); + return 0; + } + msize = ntohs (msg->size); + GNUNET_assert (size >= msize); + memcpy (buf, msg, msize); + GNUNET_free (msg); + GNUNET_CLIENT_disconnect (cc->client); + GNUNET_free (cc); + return msize; +} + + + +/** + * Add a host to the persistent list. + * + * @param cfg configuration to use + * @param sched scheduler to use + * @param peer identity of the peer + * @param hello the verified (!) HELLO message + * @param expiration when the HELLO will expire + */ +void +GNUNET_PEERINFO_add_peer (struct GNUNET_CONFIGURATION_Handle *cfg, + struct GNUNET_SCHEDULER_Handle *sched, + const struct GNUNET_PeerIdentity *peer, + const struct GNUNET_HELLO_Message *hello) +{ + struct GNUNET_CLIENT_Connection *client; + struct PeerAddMessage *pam; + uint16_t hs; + struct CAFContext *cc; + + client = GNUNET_CLIENT_connect (sched, "peerinfo", cfg); + if (client == NULL) + { + GNUNET_log (GNUNET_ERROR_TYPE_WARNING, + _("Could not connect to `%s' service.\n"), "peerinfo"); + return; + } + hs = GNUNET_HELLO_size (hello); + pam = GNUNET_malloc (sizeof (struct PeerAddMessage) + hs); + pam->header.size = htons (hs + sizeof (struct PeerAddMessage)); + pam->header.type = htons (GNUNET_MESSAGE_TYPE_PEERINFO_ADD); + memcpy (&pam->peer, peer, sizeof (struct GNUNET_PeerIdentity)); + memcpy (&pam[1], hello, hs); + cc = GNUNET_malloc (sizeof (struct CAFContext)); + cc->client = client; + cc->msg = &pam->header; + GNUNET_CLIENT_notify_transmit_ready (client, + ntohs (pam->header.size), + ADD_PEER_TIMEOUT, ©_and_free, cc); +} + + +/** + * Context for the info handler. + */ +struct InfoContext +{ + + /** + * Our connection to the PEERINFO service. + */ + struct GNUNET_CLIENT_Connection *client; + + /** + * Function to call with information. + */ + GNUNET_PEERINFO_Processor callback; + + /** + * Closure for callback. + */ + void *callback_cls; + + /** + * When should we time out? + */ + struct GNUNET_TIME_Absolute timeout; + +}; + + +/** + * Type of a function to call when we receive a message + * from the service. + * + * @param cls closure + * @param msg message received, NULL on timeout or fatal error + */ +static void +info_handler (void *cls, const struct GNUNET_MessageHeader *msg) +{ + struct InfoContext *ic = cls; + const struct InfoMessage *im; + const struct GNUNET_HELLO_Message *hello; + uint16_t ms; + + if (msg == NULL) + { + GNUNET_log (GNUNET_ERROR_TYPE_WARNING, + _("Failed to receive response from `%s' service.\n"), + "peerinfo"); + ic->callback (ic->callback_cls, NULL, NULL, 1); + GNUNET_CLIENT_disconnect (ic->client); + GNUNET_free (ic); + return; + } + if (ntohs (msg->type) == GNUNET_MESSAGE_TYPE_PEERINFO_INFO_END) + { + ic->callback (ic->callback_cls, NULL, NULL, 0); + GNUNET_CLIENT_disconnect (ic->client); + GNUNET_free (ic); + return; + } + ms = ntohs (msg->size); + if ((ms < sizeof (struct InfoMessage)) || + (ntohs (msg->type) != GNUNET_MESSAGE_TYPE_PEERINFO_INFO)) + { + GNUNET_break (0); + ic->callback (ic->callback_cls, NULL, NULL, 2); + GNUNET_CLIENT_disconnect (ic->client); + GNUNET_free (ic); + return; + } + im = (const struct InfoMessage *) msg; + hello = NULL; + if (ms > sizeof (struct InfoMessage) + sizeof (struct GNUNET_MessageHeader)) + { + hello = (const struct GNUNET_HELLO_Message *) &im[1]; + if (ms != sizeof (struct InfoMessage) + GNUNET_HELLO_size (hello)) + { + GNUNET_break (0); + ic->callback (ic->callback_cls, NULL, NULL, 2); + GNUNET_CLIENT_disconnect (ic->client); + GNUNET_free (ic); + return; + } + } + ic->callback (ic->callback_cls, &im->peer, hello, ntohl (im->trust)); + GNUNET_CLIENT_receive (ic->client, + &info_handler, + ic, + GNUNET_TIME_absolute_get_remaining (ic->timeout)); +} + + +static size_t +copy_then_receive (void *cls, size_t size, void *buf) +{ + struct InfoContext *ic = cls; + const struct GNUNET_MessageHeader *msg = + (const struct GNUNET_MessageHeader *) &ic[1]; + uint16_t msize; + + if (buf == NULL) + { + GNUNET_log (GNUNET_ERROR_TYPE_WARNING, + _ + ("Failed to transmit message of type %u to `%s' service.\n"), + ntohs (msg->type), "peerinfo"); + ic->callback (ic->callback_cls, NULL, NULL, 1); + GNUNET_CLIENT_disconnect (ic->client); + GNUNET_free (ic); + return 0; + } + msize = ntohs (msg->size); + GNUNET_assert (size >= msize); + memcpy (buf, msg, msize); + GNUNET_CLIENT_receive (ic->client, + &info_handler, + ic, + GNUNET_TIME_absolute_get_remaining (ic->timeout)); + return msize; +} + + +/** + * Call a method for each known matching host and change + * its trust value. The method will be invoked once for + * each host and then finally once with a NULL pointer. + * Note that the last call can be triggered by timeout or + * by simply being done; however, the trust argument will + * be set to zero if we are done and to 1 if we timed out. + * + * @param cfg configuration to use + * @param sched scheduler to use + * @param peer restrict iteration to this peer only (can be NULL) + * @param trust_delta how much to change the trust in all matching peers + * @param timeout how long to wait until timing out + * @param callback the method to call for each peer + * @param callback_cls closure for callback + */ +void +GNUNET_PEERINFO_for_all (struct GNUNET_CONFIGURATION_Handle *cfg, + struct GNUNET_SCHEDULER_Handle *sched, + const struct GNUNET_PeerIdentity *peer, + int trust_delta, + struct GNUNET_TIME_Relative timeout, + GNUNET_PEERINFO_Processor callback, + void *callback_cls) +{ + struct GNUNET_CLIENT_Connection *client; + struct ListAllPeersMessage *lapm; + struct ListPeerMessage *lpm; + size_t hs; + struct InfoContext *ihc; + + client = GNUNET_CLIENT_connect (sched, "peerinfo", cfg); + if (client == NULL) + { + GNUNET_log (GNUNET_ERROR_TYPE_WARNING, + _("Could not connect to `%s' service.\n"), "peerinfo"); + callback (callback_cls, NULL, NULL, 2); + return; + } + ihc = GNUNET_malloc (sizeof (struct InfoContext) + + sizeof (struct ListPeerMessage)); + ihc->client = client; + ihc->callback = callback; + ihc->callback_cls = callback_cls; + ihc->timeout = GNUNET_TIME_relative_to_absolute (timeout); + hs = 0; + if (peer == NULL) + { + lapm = (struct ListAllPeersMessage *) &ihc[1]; + lapm->header.size = htons (hs = sizeof (struct ListAllPeersMessage)); + lapm->header.type = htons (GNUNET_MESSAGE_TYPE_PEERINFO_GET_ALL); + lapm->trust_change = htonl (trust_delta); + } + else + { + lpm = (struct ListPeerMessage *) &ihc[1]; + lpm->header.size = htons (hs = sizeof (struct ListPeerMessage)); + lpm->header.type = htons (GNUNET_MESSAGE_TYPE_PEERINFO_GET); + lpm->trust_change = htonl (trust_delta); + memcpy (&lpm->peer, peer, sizeof (struct GNUNET_PeerIdentity)); + } + GNUNET_CLIENT_notify_transmit_ready (client, + hs, timeout, ©_then_receive, ihc); +} + +/* end of peerinfo_api.c */ diff --git a/src/peerinfo/test_peerinfo_api.c b/src/peerinfo/test_peerinfo_api.c new file mode 100644 index 000000000..e7d632fd5 --- /dev/null +++ b/src/peerinfo/test_peerinfo_api.c @@ -0,0 +1,175 @@ +/* + This file is part of GNUnet. + (C) 2004, 2009 Christian Grothoff (and other contributing authors) + + GNUnet is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published + by the Free Software Foundation; either version 2, 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 + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with GNUnet; see the file COPYING. If not, write to the + Free Software Foundation, Inc., 59 Temple Place - Suite 330, + Boston, MA 02111-1307, USA. +*/ + +/** + * @file peerinfo/test_peerinfo_api.c + * @brief testcase for peerinfo_api.c + * @author Christian Grothoff + * + * TODO: + * - test merging of HELLOs (add same peer twice...) + */ + +#include "platform.h" +#include "gnunet_hello_lib.h" +#include "gnunet_getopt_lib.h" +#include "gnunet_os_lib.h" +#include "gnunet_peerinfo_service.h" +#include "gnunet_program_lib.h" +#include "gnunet_time_lib.h" + + +static int +check_it (void *cls, + const char *tname, + struct GNUNET_TIME_Absolute expiration, + const void *addr, size_t addrlen) +{ + unsigned int *agc = cls; + + if (addrlen > 0) + { + GNUNET_assert (0 == strcmp ("peerinfotest", tname)); + GNUNET_assert (0 == strncmp ("Address", addr, addrlen)); + (*agc) -= (1 << (addrlen - 1)); + } + return GNUNET_OK; +} + + +static void +process (void *cls, + const struct GNUNET_PeerIdentity *peer, + const struct GNUNET_HELLO_Message *hello, uint32_t trust) +{ + int *ok = cls; + unsigned int agc; + + if (peer == NULL) + { + GNUNET_assert (peer == NULL); + GNUNET_assert (2 == *ok); + GNUNET_assert (trust == 0); + *ok = 0; + return; + } + + if (hello != NULL) + { + GNUNET_assert (3 == *ok); + agc = 3; + GNUNET_HELLO_iterate_addresses (hello, GNUNET_NO, &check_it, &agc); + GNUNET_assert (agc == 0); + *ok = 2; + } +} + + +static size_t +address_generator (void *cls, size_t max, void *buf) +{ + size_t *agc = cls; + size_t ret; + + if (0 == *agc) + return 0; + ret = GNUNET_HELLO_add_address ("peerinfotest", + GNUNET_TIME_relative_to_absolute + (GNUNET_TIME_UNIT_HOURS), "Address", *agc, + buf, max); + (*agc)--; + return ret; +} + + +static void +run (void *cls, + struct GNUNET_SCHEDULER_Handle *sched, + char *const *args, + const char *cfgfile, struct GNUNET_CONFIGURATION_Handle *cfg) +{ + struct GNUNET_HELLO_Message *hello; + struct GNUNET_CRYPTO_RsaPublicKeyBinaryEncoded pkey; + size_t agc; + struct GNUNET_PeerIdentity pid; + + memset (&pkey, 32, sizeof (pkey)); + GNUNET_CRYPTO_hash (&pkey, sizeof (pkey), &pid.hashPubKey); + agc = 2; + hello = GNUNET_HELLO_create (&pkey, &address_generator, &agc); + GNUNET_assert (hello != NULL); + GNUNET_PEERINFO_add_peer (cfg, sched, &pid, hello); + GNUNET_PEERINFO_for_all (cfg, + sched, + NULL, + 0, + GNUNET_TIME_relative_multiply + (GNUNET_TIME_UNIT_SECONDS, 15), &process, cls); + GNUNET_free (hello); +} + + +static int +check () +{ + int ok = 3; + pid_t pid; + char *const argv[] = { "test-peerinfo-api", + "-c", + "test_peerinfo_api_data.conf", +#if DEBUG_PEERINFO + "-L", "DEBUG", +#endif + NULL + }; + struct GNUNET_GETOPT_CommandLineOption options[] = { + GNUNET_GETOPT_OPTION_END + }; + pid = GNUNET_OS_start_process ("gnunet-service-peerinfo", + "gnunet-service-peerinfo", +#if DEBUG_PEERINFO + "-L", "DEBUG", +#endif + "-c", "test_peerinfo_api_data.conf", NULL); + sleep (1); + GNUNET_PROGRAM_run ((sizeof (argv) / sizeof (char *)) - 1, + argv, "test-peerinfo-api", "nohelp", + options, &run, &ok); + if (0 != PLIBC_KILL (pid, SIGTERM)) + { + GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING, "kill"); + ok = 1; + } + waitpid (pid, NULL, 0); + return ok; +} + + +int +main (int argc, char *argv[]) +{ + int ret = 0; + + ret = check (); + + return ret; +} + +/* end of test_peerinfo_api.c */ diff --git a/src/peerinfo/test_peerinfo_api_data.conf b/src/peerinfo/test_peerinfo_api_data.conf new file mode 100644 index 000000000..a81ffccb9 --- /dev/null +++ b/src/peerinfo/test_peerinfo_api_data.conf @@ -0,0 +1,5 @@ +[PATHS] +SERVICEHOME = /tmp/test-gnunetd-peerinfo/ + +[peerinfo] +PORT = 22354 diff --git a/src/resolver/Makefile.am b/src/resolver/Makefile.am new file mode 100644 index 000000000..20c07f4da --- /dev/null +++ b/src/resolver/Makefile.am @@ -0,0 +1,46 @@ +INCLUDES = -I$(top_srcdir)/src/include + +if MINGW + WINFLAGS = -Wl,--no-undefined -Wl,--export-all-symbols +endif + +if USE_COVERAGE + AM_CFLAGS = -fprofile-arcs -ftest-coverage +endif + + +lib_LTLIBRARIES = libgnunetresolver.la + +libgnunetresolver_la_SOURCES = \ + resolver_api.c resolver.h +libgnunetresolver_la_LIBADD = \ + $(top_builddir)/src/util/libgnunetutil.la \ + $(GN_LIBINTL) +libgnunetresolver_la_LDFLAGS = \ + $(GN_LIB_LDFLAGS) $(WINFLAGS) \ + -version-info 0:0:0 + + +bin_PROGRAMS = \ + gnunet-service-resolver + +gnunet_service_resolver_SOURCES = \ + gnunet-service-resolver.c +gnunet_service_resolver_LDADD = \ + $(top_builddir)/src/util/libgnunetutil.la \ + $(GN_LIBINTL) + + +check_PROGRAMS = \ + test_resolver_api + +TESTS = $(check_PROGRAMS) + +test_resolver_api_SOURCES = \ + test_resolver_api.c +test_resolver_api_LDADD = \ + $(top_builddir)/src/resolver/libgnunetresolver.la \ + $(top_builddir)/src/util/libgnunetutil.la + +EXTRA_DIST = \ + test_resolver_api_data.conf diff --git a/src/resolver/gnunet-service-resolver.c b/src/resolver/gnunet-service-resolver.c new file mode 100644 index 000000000..dbdecdfe5 --- /dev/null +++ b/src/resolver/gnunet-service-resolver.c @@ -0,0 +1,483 @@ +/* + This file is part of GNUnet. + (C) 2007, 2008, 2009 Christian Grothoff (and other contributing authors) + + GNUnet is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published + by the Free Software Foundation; either version 2, 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 + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with GNUnet; see the file COPYING. If not, write to the + Free Software Foundation, Inc., 59 Temple Place - Suite 330, + Boston, MA 02111-1307, USA. +*/ + +/** + * @file resolver/gnunet-service-resolver.c + * @brief code to do DNS resolution + * @author Christian Grothoff + */ + +#include +#include "platform.h" +#include "gnunet_disk_lib.h" +#include "gnunet_getopt_lib.h" +#include "gnunet_protocols.h" +#include "gnunet_service_lib.h" +#include "gnunet_statistics_service.h" +#include "gnunet_strings_lib.h" +#include "gnunet_time_lib.h" +#include "resolver.h" + + +struct IPCache +{ + struct IPCache *next; + char *addr; + struct sockaddr *sa; + struct GNUNET_TIME_Absolute last_refresh; + struct GNUNET_TIME_Absolute last_request; + unsigned int salen; +}; + + +static struct IPCache *head; + + + + +#if HAVE_GETNAMEINFO +static void +getnameinfo_resolve (struct IPCache *cache) +{ + char hostname[256]; + + if (0 == getnameinfo (cache->sa, cache->salen, hostname, 255, NULL, 0, 0)) + cache->addr = GNUNET_strdup (hostname); +} +#endif + + +#if HAVE_GETHOSTBYADDR +static void +gethostbyaddr_resolve (struct IPCache *cache) +{ + struct hostent *ent; + + switch (cache->sa->sa_family) + { + case AF_INET: + ent = gethostbyaddr (&((struct sockaddr_in *) cache->sa)->sin_addr, + sizeof (struct in_addr), AF_INET); + break; + case AF_INET6: + ent = gethostbyaddr (&((struct sockaddr_in6 *) cache->sa)->sin6_addr, + sizeof (struct in6_addr), AF_INET6); + break; + default: + ent = NULL; + } + if (ent != NULL) + cache->addr = GNUNET_strdup (ent->h_name); +} +#endif + + +static void +cache_resolve (struct IPCache *cache) +{ +#if HAVE_GETNAMEINFO + if (cache->addr == NULL) + getnameinfo_resolve (cache); +#endif +#if HAVE_GETHOSTBYADDR + if (cache->addr == NULL) + gethostbyaddr_resolve (cache); +#endif +} + + + +/** + * Get an IP address as a string (works for both IPv4 and IPv6). Note + * that the resolution happens asynchronously and that the first call + * may not immediately result in the FQN (but instead in a + * human-readable IP address). + * + * @param sa should be of type "struct sockaddr*" + */ +static void +get_ip_as_string (struct GNUNET_SERVER_Client *client, + const struct sockaddr *sav, socklen_t salen) +{ + struct IPCache *cache; + struct IPCache *prev; + struct GNUNET_TIME_Absolute now; + struct GNUNET_SERVER_TransmitContext *tc; + + if (salen < sizeof (struct sockaddr)) + { + GNUNET_break (0); + return; + } + now = GNUNET_TIME_absolute_get (); + cache = head; + prev = NULL; + while ((cache != NULL) && + ((cache->salen != salen) || (0 != memcmp (cache->sa, sav, salen)))) + { + if (GNUNET_TIME_absolute_get_duration (cache->last_request).value < + 60 * 60 * 1000) + { + if (prev != NULL) + { + prev->next = cache->next; + GNUNET_free_non_null (cache->addr); + GNUNET_free (cache->sa); + GNUNET_free (cache); + cache = prev->next; + } + else + { + head = cache->next; + GNUNET_free_non_null (cache->addr); + GNUNET_free (cache->sa); + GNUNET_free (cache); + cache = head; + } + continue; + } + prev = cache; + cache = cache->next; + } + if (cache != NULL) + { + cache->last_request = now; + if (GNUNET_TIME_absolute_get_duration (cache->last_request).value < + 60 * 60 * 1000) + { + GNUNET_free_non_null (cache->addr); + cache->addr = NULL; + cache->salen = 0; + cache_resolve (cache); + } + } + else + { + cache = GNUNET_malloc (sizeof (struct IPCache)); + cache->next = head; + cache->salen = salen; + cache->sa = GNUNET_malloc (salen); + memcpy (cache->sa, sav, salen); + cache->last_request = GNUNET_TIME_absolute_get (); + cache->last_refresh = GNUNET_TIME_absolute_get (); + cache->addr = NULL; + cache_resolve (cache); + head = cache; + } + tc = GNUNET_SERVER_transmit_context_create (client); + if (cache->addr != NULL) + GNUNET_SERVER_transmit_context_append (tc, + cache->addr, + strlen (cache->addr) + 1, + GNUNET_MESSAGE_TYPE_RESOLVER_RESPONSE); + GNUNET_SERVER_transmit_context_append (tc, NULL, 0, + GNUNET_MESSAGE_TYPE_RESOLVER_RESPONSE); + GNUNET_SERVER_transmit_context_run (tc, GNUNET_TIME_UNIT_FOREVER_REL); +} + + +#if HAVE_GETADDRINFO +static int +getaddrinfo_resolve (struct GNUNET_SERVER_TransmitContext *tc, + const char *hostname, int domain) +{ + int s; + struct addrinfo hints; + struct addrinfo *result; + struct addrinfo *pos; + + memset (&hints, 0, sizeof (struct addrinfo)); +// FIXME in PlibC +#ifndef MINGW + hints.ai_family = domain; +#else + hints.ai_family = AF_INET; +#endif + hints.ai_socktype = SOCK_STREAM; /* go for TCP */ + + if (0 != (s = getaddrinfo (hostname, NULL, &hints, &result))) + { + GNUNET_log (GNUNET_ERROR_TYPE_INFO, + _("Could not resolve `%s' (%s): %s\n"), hostname, + (domain == + AF_INET) ? "IPv4" : ((domain == + AF_INET6) ? "IPv6" : "any"), + gai_strerror (s)); + if ((s == EAI_BADFLAGS) || (s == EAI_MEMORY) || (s == EAI_SYSTEM)) + return GNUNET_NO; /* other function may still succeed */ + return GNUNET_SYSERR; + } + if (result == NULL) + return GNUNET_SYSERR; + pos = result; + while (pos != NULL) + { + GNUNET_SERVER_transmit_context_append (tc, + result->ai_addr, + result->ai_addrlen, + GNUNET_MESSAGE_TYPE_RESOLVER_RESPONSE); + pos = pos->ai_next; + } + freeaddrinfo (result); + return GNUNET_OK; +} +#endif + +#if HAVE_GETHOSTBYNAME2 +static int +gethostbyname2_resolve (struct GNUNET_SERVER_TransmitContext *tc, + const char *hostname, int domain) +{ + struct hostent *hp; + struct sockaddr_in a4; + struct sockaddr_in6 a6; + int ret1; + int ret2; + + if (domain == AF_UNSPEC) + { + ret1 = gethostbyname2_resolve (tc, hostname, AF_INET); + ret2 = gethostbyname2_resolve (tc, hostname, AF_INET6); + if ((ret1 == GNUNET_OK) || (ret2 == GNUNET_OK)) + return GNUNET_OK; + if ((ret1 == GNUNET_SYSERR) || (ret2 == GNUNET_SYSERR)) + return GNUNET_SYSERR; + return GNUNET_NO; + } + hp = gethostbyname2 (hostname, domain); + if (hp == NULL) + { + GNUNET_log (GNUNET_ERROR_TYPE_INFO, + _("Could not find IP of host `%s': %s\n"), + hostname, hstrerror (h_errno)); + return GNUNET_SYSERR; + } + GNUNET_assert (hp->h_addrtype == domain); + if (domain == AF_INET) + { + GNUNET_assert (hp->h_length == sizeof (struct in_addr)); + memset (&a4, 0, sizeof (a4)); + a4.sin_family = AF_INET; + memcpy (&a4.sin_addr, hp->h_addr_list[0], hp->h_length); + GNUNET_SERVER_transmit_context_append (tc, + &a4, + sizeof (a4), + GNUNET_MESSAGE_TYPE_RESOLVER_RESPONSE); + } + else + { + GNUNET_assert (hp->h_length == sizeof (struct in6_addr)); + memset (&a6, 0, sizeof (a6)); + a6.sin6_family = AF_INET6; + memcpy (&a6.sin6_addr, hp->h_addr_list[0], hp->h_length); + GNUNET_SERVER_transmit_context_append (tc, + &a6, + sizeof (a6), + GNUNET_MESSAGE_TYPE_RESOLVER_RESPONSE); + } + return GNUNET_OK; +} +#endif + +#if HAVE_GETHOSTBYNAME +static int +gethostbyname_resolve (struct GNUNET_SERVER_TransmitContext *tc, + const char *hostname) +{ + struct hostent *hp; + struct sockaddr_in addr; + + hp = GETHOSTBYNAME (hostname); + if (hp == NULL) + { + GNUNET_log (GNUNET_ERROR_TYPE_INFO, + _("Could not find IP of host `%s': %s\n"), + hostname, hstrerror (h_errno)); + return GNUNET_SYSERR; + } + if (hp->h_addrtype != AF_INET) + { + GNUNET_break (0); + return GNUNET_SYSERR; + } + GNUNET_assert (hp->h_length == sizeof (struct in_addr)); + memset (&addr, 0, sizeof (addr)); + addr.sin_family = AF_INET; + memcpy (&addr.sin_addr, hp->h_addr_list[0], hp->h_length); + GNUNET_SERVER_transmit_context_append (tc, + &addr, + sizeof (addr), + GNUNET_MESSAGE_TYPE_RESOLVER_RESPONSE); + return GNUNET_OK; +} +#endif + + +/** + * Convert a string to an IP address. + * + * @param client where to send the IP address + * @param hostname the hostname to resolve + * @param domain AF_INET or AF_INET6; use AF_UNSPEC for "any" + */ +static void +get_ip_from_hostname (struct GNUNET_SERVER_Client *client, + const char *hostname, int domain) +{ + int ret; + struct GNUNET_SERVER_TransmitContext *tc; + + tc = GNUNET_SERVER_transmit_context_create (client); + ret = GNUNET_NO; +#if HAVE_GETADDRINFO + if (ret == GNUNET_NO) + ret = getaddrinfo_resolve (tc, hostname, domain); +#endif +#if HAVE_GETHOSTBYNAME2 + if (ret == GNUNET_NO) + ret = gethostbyname2_resolve (tc, hostname, domain); +#endif +#if HAVE_GETHOSTBYNAME + if ((ret == GNUNET_NO) && ((domain == AF_UNSPEC) || (domain == PF_INET))) + gethostbyname_resolve (tc, hostname); +#endif + GNUNET_SERVER_transmit_context_append (tc, NULL, 0, + GNUNET_MESSAGE_TYPE_RESOLVER_RESPONSE); + GNUNET_SERVER_transmit_context_run (tc, GNUNET_TIME_UNIT_FOREVER_REL); +} + + +/** + * Handle GET-message. + * + * @param cls closure + * @param server the server handling the message + * @param client identification of the client + * @param message the actual message + */ +static void +handle_get (void *cls, + struct GNUNET_SERVER_Handle *server, + struct GNUNET_SERVER_Client *client, + const struct GNUNET_MessageHeader *message) +{ + uint16_t msize; + const struct GNUNET_RESOLVER_GetMessage *msg; + const char *hostname; + uint16_t size; + int direction; + int domain; + + msize = ntohs (message->size); + if (msize < sizeof (struct GNUNET_RESOLVER_GetMessage)) + { + GNUNET_break (0); + GNUNET_SERVER_receive_done (client, GNUNET_SYSERR); + return; + } + msg = (const struct GNUNET_RESOLVER_GetMessage *) message; + size = msize - sizeof (struct GNUNET_RESOLVER_GetMessage); + direction = ntohl (msg->direction); + domain = ntohl (msg->domain); + if (direction == GNUNET_NO) + { + /* IP from hostname */ + hostname = (const char *) &msg[1]; + if (hostname[size - 1] != '\0') + { + GNUNET_break (0); + GNUNET_SERVER_receive_done (client, GNUNET_SYSERR); + return; + } +#if DEBUG_RESOLVER + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + _("Resolver asked to look up `%s'.\n"), hostname); +#endif + get_ip_from_hostname (client, hostname, domain); + } + else + { +#if DEBUG_RESOLVER + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + _("Resolver asked to look up IP address.\n")); +#endif + get_ip_as_string (client, (const struct sockaddr *) &msg[1], size); + } +} + + +/** + * List of handlers for the messages understood by this + * service. + */ +static struct GNUNET_SERVER_MessageHandler handlers[] = { + {&handle_get, NULL, GNUNET_MESSAGE_TYPE_RESOLVER_REQUEST, 0}, + {NULL, NULL, 0, 0} +}; + + +/** + * Process statistics requests. + * + * @param cls closure + * @param sched scheduler to use + * @param server the initialized server + * @param cfg configuration to use + */ +static void +run (void *cls, + struct GNUNET_SCHEDULER_Handle *sched, + struct GNUNET_SERVER_Handle *server, + struct GNUNET_CONFIGURATION_Handle *cfg) +{ + GNUNET_SERVER_add_handlers (server, handlers); +} + + +/** + * The main function for the resolver service. + * + * @param argc number of arguments from the command line + * @param argv command line arguments + * @return 0 ok, 1 on error + */ +int +main (int argc, char *const *argv) +{ + int ret; + struct IPCache *pos; + + ret = (GNUNET_OK == + GNUNET_SERVICE_run (argc, + argv, + "resolver", &run, NULL, NULL, NULL)) ? 0 : 1; + + while (head != NULL) + { + pos = head->next; + GNUNET_free_non_null (head->addr); + GNUNET_free (head->sa); + GNUNET_free (head); + head = pos; + } + return ret; +} + +/* end of gnunet-service-resolver.c */ diff --git a/src/resolver/resolver.h b/src/resolver/resolver.h new file mode 100644 index 000000000..31637f01d --- /dev/null +++ b/src/resolver/resolver.h @@ -0,0 +1,63 @@ +/* + This file is part of GNUnet. + (C) 2009 Christian Grothoff (and other contributing authors) + + GNUnet is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published + by the Free Software Foundation; either version 2, 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 + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with GNUnet; see the file COPYING. If not, write to the + Free Software Foundation, Inc., 59 Temple Place - Suite 330, + Boston, MA 02111-1307, USA. +*/ + +/** + * @author Christian Grothoff + * @file resolver/resolver.h + */ +#ifndef RESOLVER_H +#define RESOLVER_H + +#include "gnunet_common.h" + +#define DEBUG_RESOLVER GNUNET_NO + +/** + * Request for the resolver. Followed by either + * the "struct sockaddr" or the 0-terminated hostname. + * + * The response will be one or more messages of type + * RESOLVER_RESPONSE, each with the message header + * immediately followed by the requested data + * (hostname or struct sockaddr, depending on direction). + * The last RESOLVER_RESPONSE will just be a header + * without any data (used to indicate the end of the list). + */ +struct GNUNET_RESOLVER_GetMessage +{ + /** + * Type: GNUNET_MESSAGE_TYPE_STATISTICS_VALUE + */ + struct GNUNET_MessageHeader header; + + /** + * GNUNET_YES to get hostname from IP, + * GNUNET_NO to get IP from hostname. + */ + int32_t direction GNUNET_PACKED; + + /** + * Domain to use (AF_INET, AF_INET6 or AF_UNSPEC). + */ + int32_t domain GNUNET_PACKED; + +}; + +#endif diff --git a/src/resolver/resolver_api.c b/src/resolver/resolver_api.c new file mode 100644 index 000000000..27358996a --- /dev/null +++ b/src/resolver/resolver_api.c @@ -0,0 +1,468 @@ +/* + This file is part of GNUnet. + (C) 2009 Christian Grothoff (and other contributing authors) + + GNUnet is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published + by the Free Software Foundation; either version 2, 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 + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with GNUnet; see the file COPYING. If not, write to the + Free Software Foundation, Inc., 59 Temple Place - Suite 330, + Boston, MA 02111-1307, USA. +*/ + +/** + * @file resolver/resolver_api.c + * @brief resolver for writing a tool + * @author Christian Grothoff + */ +#include "platform.h" +#include "gnunet_getopt_lib.h" +#include "gnunet_client_lib.h" +#include "gnunet_protocols.h" +#include "gnunet_resolver_service.h" +#include "gnunet_server_lib.h" +#include "resolver.h" + + +struct GetAddressContext +{ + GNUNET_RESOLVER_AddressCallback callback; + void *cls; + struct GNUNET_RESOLVER_GetMessage *msg; + struct GNUNET_CLIENT_Connection *client; + struct GNUNET_TIME_Absolute timeout; +}; + + + +/** + * Convert IP address to string without DNS resolution. + */ +static char * +no_resolve (const struct sockaddr *sa, socklen_t salen) +{ + char *ret; + char inet4[INET_ADDRSTRLEN]; + char inet6[INET6_ADDRSTRLEN]; + + if (salen < sizeof (struct sockaddr)) + return NULL; + switch (sa->sa_family) + { + case AF_INET: + if (salen != sizeof (struct sockaddr_in)) + return NULL; + inet_ntop (AF_INET, + &((struct sockaddr_in *) sa)->sin_addr, + inet4, INET_ADDRSTRLEN); + ret = GNUNET_strdup (inet4); + break; + case AF_INET6: + if (salen != sizeof (struct sockaddr_in6)) + return NULL; + inet_ntop (AF_INET6, + &((struct sockaddr_in6 *) sa)->sin6_addr, + inet6, INET6_ADDRSTRLEN); + ret = GNUNET_strdup (inet6); + break; + default: + ret = NULL; + break; + } + return ret; +} + + +static void +handle_address_response (void *cls, const struct GNUNET_MessageHeader *msg) +{ + struct GetAddressContext *gac = cls; + uint16_t size; + const struct sockaddr *sa; + socklen_t salen; + + + if (msg == NULL) + { + GNUNET_log (GNUNET_ERROR_TYPE_INFO, + _("Timeout trying to resolve hostname.\n")); + gac->callback (gac->cls, NULL, 0); + GNUNET_CLIENT_disconnect (gac->client); + GNUNET_free (gac); + return; + } + if (GNUNET_MESSAGE_TYPE_RESOLVER_RESPONSE != ntohs (msg->type)) + { + GNUNET_break (0); + gac->callback (gac->cls, NULL, 0); + GNUNET_CLIENT_disconnect (gac->client); + GNUNET_free (gac); + return; + } + + size = ntohs (msg->size); + if (size == sizeof (struct GNUNET_MessageHeader)) + { +#if DEBUG_RESOLVER + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + _("Received end message resolving hostname.\n")); +#endif + gac->callback (gac->cls, NULL, 0); + GNUNET_CLIENT_disconnect (gac->client); + GNUNET_free (gac); + return; + } + sa = (const struct sockaddr *) &msg[1]; + salen = size - sizeof (struct GNUNET_MessageHeader); + if (salen < sizeof (struct sockaddr)) + { + GNUNET_break (0); + gac->callback (gac->cls, NULL, 0); + GNUNET_CLIENT_disconnect (gac->client); + GNUNET_free (gac); + return; + } +#if DEBUG_RESOLVER + { + char *ips = no_resolve (sa, salen); + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, _("Resolver returns `%s'.\n"), ips); + GNUNET_free (ips); + } +#endif + gac->callback (gac->cls, sa, salen); + GNUNET_CLIENT_receive (gac->client, + &handle_address_response, + gac, + GNUNET_TIME_absolute_get_remaining (gac->timeout)); +} + + +static size_t +transmit_get_ip (void *cls, size_t size, void *buf) +{ + struct GetAddressContext *actx = cls; + uint16_t ms; + + if (buf == NULL) + { + /* timeout / error */ + GNUNET_free (actx->msg); + actx->callback (actx->cls, NULL, 0); + GNUNET_CLIENT_disconnect (actx->client); + GNUNET_free (actx); + return 0; + } + ms = ntohs (actx->msg->header.size); + GNUNET_assert (size >= ms); + memcpy (buf, actx->msg, ms); + GNUNET_free (actx->msg); + actx->msg = NULL; + GNUNET_CLIENT_receive (actx->client, + &handle_address_response, + actx, + GNUNET_TIME_absolute_get_remaining (actx->timeout)); + return ms; +} + + + +/** + * Convert a string to one or more IP addresses. + * + * @param sched scheduler to use + * @param cfg configuration to use + * @param hostname the hostname to resolve + * @param domain AF_INET or AF_INET6; use AF_UNSPEC for "any" + * @param callback function to call with addresses + * @param cls closure for callback + * @param timeout how long to try resolving + */ +void +GNUNET_RESOLVER_ip_get (struct GNUNET_SCHEDULER_Handle *sched, + struct GNUNET_CONFIGURATION_Handle *cfg, + const char *hostname, + int domain, + struct GNUNET_TIME_Relative timeout, + GNUNET_RESOLVER_AddressCallback callback, void *cls) +{ + struct GNUNET_CLIENT_Connection *client; + struct GNUNET_RESOLVER_GetMessage *msg; + struct GetAddressContext *actx; + size_t slen; + + slen = strlen (hostname) + 1; + if (slen + sizeof (struct GNUNET_RESOLVER_GetMessage) > + GNUNET_SERVER_MAX_MESSAGE_SIZE) + { + GNUNET_break (0); + callback (cls, NULL, 0); + return; + } + client = GNUNET_CLIENT_connect (sched, "resolver", cfg); + if (client == NULL) + { + callback (cls, NULL, 0); + return; + } + msg = GNUNET_malloc (sizeof (struct GNUNET_RESOLVER_GetMessage) + slen); + msg->header.size = + htons (sizeof (struct GNUNET_RESOLVER_GetMessage) + slen); + msg->header.type = htons (GNUNET_MESSAGE_TYPE_RESOLVER_REQUEST); + msg->direction = htonl (GNUNET_NO); + msg->domain = htonl (domain); + memcpy (&msg[1], hostname, slen); + actx = GNUNET_malloc (sizeof (struct GetAddressContext)); + actx->callback = callback; + actx->cls = cls; + actx->client = client; + actx->timeout = GNUNET_TIME_relative_to_absolute (timeout); + actx->msg = msg; + +#if DEBUG_RESOLVER + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + _("Resolver requests DNS resolution of hostname `%s'.\n"), + hostname); +#endif + if (NULL == + GNUNET_CLIENT_notify_transmit_ready (client, + slen + + sizeof (struct + GNUNET_RESOLVER_GetMessage), + timeout, &transmit_get_ip, actx)) + { + GNUNET_free (msg); + GNUNET_free (actx); + callback (cls, NULL, 0); + GNUNET_CLIENT_disconnect (client); + return; + } +} + + +struct GetHostnameContext +{ + GNUNET_RESOLVER_HostnameCallback callback; + void *cls; + struct GNUNET_RESOLVER_GetMessage *msg; + struct GNUNET_CLIENT_Connection *client; + struct GNUNET_TIME_Absolute timeout; +}; + + +static void +handle_hostname_response (void *cls, const struct GNUNET_MessageHeader *msg) +{ + struct GetHostnameContext *ghc = cls; + uint16_t size; + const char *hostname; + + if (msg == NULL) + { + GNUNET_log (GNUNET_ERROR_TYPE_INFO, + _("Timeout trying to resolve IP address.\n")); + ghc->callback (ghc->cls, NULL); + GNUNET_CLIENT_disconnect (ghc->client); + GNUNET_free (ghc); + return; + } + size = ntohs (msg->size); + if (size == sizeof (struct GNUNET_MessageHeader)) + { +#if DEBUG_RESOLVER + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + _("Received end message resolving IP address.\n")); +#endif + ghc->callback (ghc->cls, NULL); + GNUNET_CLIENT_disconnect (ghc->client); + GNUNET_free (ghc); + return; + } + hostname = (const char *) &msg[1]; + if (hostname[size - sizeof (struct GNUNET_MessageHeader) - 1] != '\0') + { + GNUNET_break (0); + ghc->callback (ghc->cls, NULL); + GNUNET_CLIENT_disconnect (ghc->client); + GNUNET_free (ghc); + return; + } +#if DEBUG_RESOLVER + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + _("Resolver returns `%s'.\n"), hostname); +#endif + ghc->callback (ghc->cls, hostname); + GNUNET_CLIENT_receive (ghc->client, + &handle_hostname_response, + ghc, + GNUNET_TIME_absolute_get_remaining (ghc->timeout)); +} + + +static size_t +transmit_get_hostname (void *cls, size_t size, void *buf) +{ + struct GetHostnameContext *hctx = cls; + uint16_t msize; + + if (buf == NULL) + { + GNUNET_free (hctx->msg); + hctx->callback (hctx->cls, NULL); + GNUNET_CLIENT_disconnect (hctx->client); + GNUNET_free (hctx); + return 0; + } + msize = ntohs (hctx->msg->header.size); + GNUNET_assert (size >= msize); + memcpy (buf, hctx->msg, msize); + GNUNET_free (hctx->msg); + hctx->msg = NULL; + GNUNET_CLIENT_receive (hctx->client, + &handle_hostname_response, + hctx, + GNUNET_TIME_absolute_get_remaining (hctx->timeout)); + return msize; +} + + + + +/** + * Get an IP address as a string. + * + * @param sched scheduler to use + * @param cfg configuration to use + * @param sa host address + * @param salen length of host address + * @param do_resolve use GNUNET_NO to return numeric hostname + * @param timeout how long to try resolving + * @param callback function to call with hostnames + * @param cls closure for callback + */ +void +GNUNET_RESOLVER_hostname_get (struct GNUNET_SCHEDULER_Handle *sched, + struct GNUNET_CONFIGURATION_Handle *cfg, + const struct sockaddr *sa, + socklen_t salen, + int do_resolve, + struct GNUNET_TIME_Relative timeout, + GNUNET_RESOLVER_HostnameCallback callback, + void *cls) +{ + char *result; + struct GNUNET_CLIENT_Connection *client; + struct GNUNET_RESOLVER_GetMessage *msg; + struct GetHostnameContext *hctx; + + if (GNUNET_NO == do_resolve) + { + result = no_resolve (sa, salen); +#if DEBUG_RESOLVER + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + _("Resolver returns `%s'.\n"), result); +#endif + callback (cls, result); + if (result != NULL) + { + GNUNET_free (result); + callback (cls, NULL); + } + return; + } + if (salen + sizeof (struct GNUNET_RESOLVER_GetMessage) > + GNUNET_SERVER_MAX_MESSAGE_SIZE) + { + GNUNET_break (0); + callback (cls, NULL); + return; + } + client = GNUNET_CLIENT_connect (sched, "resolver", cfg); + if (client == NULL) + { + callback (cls, NULL); + return; + } + msg = GNUNET_malloc (sizeof (struct GNUNET_RESOLVER_GetMessage) + salen); + msg->header.size = + htons (sizeof (struct GNUNET_RESOLVER_GetMessage) + salen); + msg->header.type = htons (GNUNET_MESSAGE_TYPE_RESOLVER_REQUEST); + msg->direction = htonl (GNUNET_YES); + msg->domain = htonl (sa->sa_family); + memcpy (&msg[1], sa, salen); +#if DEBUG_RESOLVER + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + _("Resolver requests DNS resolution of IP address.\n")); +#endif + hctx = GNUNET_malloc (sizeof (struct GetHostnameContext)); + hctx->callback = callback; + hctx->cls = cls; + hctx->client = client; + hctx->timeout = GNUNET_TIME_relative_to_absolute (timeout); + hctx->msg = msg; + if (NULL == + GNUNET_CLIENT_notify_transmit_ready (client, + sizeof (struct + GNUNET_RESOLVER_GetMessage) + + salen, timeout, + &transmit_get_hostname, hctx)) + { + GNUNET_free (msg); + callback (cls, NULL); + GNUNET_CLIENT_disconnect (client); + GNUNET_free (hctx); + } +} + +/** + * Maximum supported length of hostname + */ +#define MAX_HOSTNAME 1024 + + +/** + * Resolve our hostname to an IP address. + * + * @param sched scheduler to use + * @param cfg configuration to use + * @param domain AF_INET or AF_INET6; use AF_UNSPEC for "any" + * @param callback function to call with addresses + * @param cls closure for callback + * @param timeout how long to try resolving + */ +void +GNUNET_RESOLVER_hostname_resolve (struct GNUNET_SCHEDULER_Handle *sched, + struct GNUNET_CONFIGURATION_Handle *cfg, + int domain, + struct GNUNET_TIME_Relative timeout, + GNUNET_RESOLVER_AddressCallback callback, + void *cls) +{ + char hostname[MAX_HOSTNAME]; + + if (0 != gethostname (hostname, sizeof (hostname) - 1)) + { + GNUNET_log_strerror (GNUNET_ERROR_TYPE_ERROR | + GNUNET_ERROR_TYPE_BULK, "gethostname"); + callback (cls, NULL, 0); + return; + } +#if DEBUG_RESOLVER + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + _("Resolving our hostname `%s'\n"), hostname); +#endif + GNUNET_RESOLVER_ip_get (sched, + cfg, hostname, domain, timeout, callback, cls); +} + + + + +/* end of resolver_api.c */ diff --git a/src/resolver/test_resolver_api.c b/src/resolver/test_resolver_api.c new file mode 100644 index 000000000..240879d73 --- /dev/null +++ b/src/resolver/test_resolver_api.c @@ -0,0 +1,229 @@ +/* + This file is part of GNUnet. + (C) 2009 Christian Grothoff (and other contributing authors) + + GNUnet is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published + by the Free Software Foundation; either version 2, 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 + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with GNUnet; see the file COPYING. If not, write to the + Free Software Foundation, Inc., 59 Temple Place - Suite 330, + Boston, MA 02111-1307, USA. +*/ +/** + * @file resolver/test_resolver_api.c + * @brief testcase for resolver_api.c + */ +#include "platform.h" +#include "gnunet_common.h" +#include "gnunet_getopt_lib.h" +#include "gnunet_os_lib.h" +#include "gnunet_program_lib.h" +#include "gnunet_scheduler_lib.h" +#include "gnunet_resolver_service.h" +#include "resolver.h" + +#define VERBOSE GNUNET_NO + + +static void +check_hostname (void *cls, const struct sockaddr *sa, socklen_t salen) +{ + char buf[INET6_ADDRSTRLEN]; + int *ok = cls; + + if (salen == 0) + { + (*ok) &= ~8; + return; + } + if (salen == sizeof (struct sockaddr_in)) + { + struct sockaddr_in *in = (struct sockaddr_in *) sa; + GNUNET_log (GNUNET_ERROR_TYPE_INFO, + _("Got IP address `%s' for our host.\n"), + inet_ntop (AF_INET, &in->sin_addr, buf, sizeof (buf))); + } + else if (salen == sizeof (struct sockaddr_in6)) + { + struct sockaddr_in6 *in6 = (struct sockaddr_in6 *) sa; + GNUNET_log (GNUNET_ERROR_TYPE_INFO, + _("Got IP address `%s' for our host.\n"), + inet_ntop (AF_INET6, &in6->sin6_addr, buf, sizeof (buf))); + } + else + { + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + _("Got address of bogus length %u\n"), salen); + GNUNET_assert (0); + } +} + + +static void +check_localhost_num (void *cls, const char *hostname) +{ + int *ok = cls; + if (hostname == NULL) + return; + if (0 == strcmp (hostname, "127.0.0.1")) + { +#if DEBUG_RESOLVER + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Received correct hostname `%s'.\n", hostname); +#endif + (*ok) &= ~4; + } + else + { +#if DEBUG_RESOLVER + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Received invalid hostname `%s'.\n", hostname); +#endif + GNUNET_break (0); + } +} + +static void +check_localhost (void *cls, const char *hostname) +{ + int *ok = cls; + if (hostname == NULL) + return; + if (0 == strcmp (hostname, "localhost")) + { +#if DEBUG_RESOLVER + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Received correct hostname `%s'.\n", hostname); +#endif + (*ok) &= ~2; + } + else + { +#if DEBUG_RESOLVER + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Received invalid hostname `%s'.\n", hostname); +#endif + GNUNET_break (0); + } +} + +static void +check_127 (void *cls, const struct sockaddr *sa, socklen_t salen) +{ + int *ok = cls; + const struct sockaddr_in *sai = (const struct sockaddr_in *) sa; + + if (sa == NULL) + return; + GNUNET_assert (sizeof (struct sockaddr_in) == salen); + if (sai->sin_addr.s_addr == htonl (INADDR_LOOPBACK)) + { +#if DEBUG_RESOLVER + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Received correct address.\n"); +#endif + (*ok) &= ~1; + } + else + { +#if DEBUG_RESOLVER + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Received incorrect address.\n"); +#endif + GNUNET_break (0); + } +} + +static void +run (void *cls, + struct GNUNET_SCHEDULER_Handle *sched, + char *const *args, + const char *cfgfile, struct GNUNET_CONFIGURATION_Handle *cfg) +{ + struct sockaddr_in sa; + struct GNUNET_TIME_Relative timeout = + GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_MILLISECONDS, + 2500); + memset (&sa, 0, sizeof (sa)); + sa.sin_family = AF_INET; + sa.sin_addr.s_addr = htonl (INADDR_LOOPBACK); + GNUNET_RESOLVER_ip_get (sched, + cfg, + "localhost", AF_INET, timeout, &check_127, cls); + GNUNET_RESOLVER_hostname_get (sched, + cfg, + (const struct sockaddr *) &sa, + sizeof (struct sockaddr), + GNUNET_YES, timeout, &check_localhost, cls); + GNUNET_RESOLVER_hostname_get (sched, + cfg, + (const struct sockaddr *) &sa, + sizeof (struct sockaddr), + GNUNET_NO, + timeout, &check_localhost_num, cls); + GNUNET_RESOLVER_hostname_resolve (sched, + cfg, + AF_UNSPEC, timeout, &check_hostname, cls); +} + +static int +check () +{ + int ok = 1 + 2 + 4 + 8; + pid_t pid; + char *const argv[] = { "test-resolver-api", + "-c", + "test_resolver_api_data.conf", +#if VERBOSE + "-L", "DEBUG", +#endif + NULL + }; + struct GNUNET_GETOPT_CommandLineOption options[] = { + GNUNET_GETOPT_OPTION_END + }; + pid = GNUNET_OS_start_process ("gnunet-service-resolver", + "gnunet-service-resolver", +#if VERBOSE + "-L", "DEBUG", +#endif + "-c", "test_resolver_api_data.conf", NULL); + sleep (1); + GNUNET_PROGRAM_run ((sizeof (argv) / sizeof (char *)) - 1, + argv, "test-resolver-api", "nohelp", + options, &run, &ok); + if (0 != PLIBC_KILL (pid, SIGTERM)) + { + GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING, "kill"); + ok = 1; + } + waitpid (pid, NULL, 0); + if (ok != 0) + fprintf (stderr, "Missed some resolutions: %u\n", ok); + return ok; +} + +int +main (int argc, char *argv[]) +{ + int ret; + + GNUNET_log_setup ("test-resolver-api", +#if VERBOSE + "DEBUG", +#else + "WARNING", +#endif + NULL); + ret = check (); + + return ret; +} + +/* end of test_resolver_api.c */ diff --git a/src/resolver/test_resolver_api_data.conf b/src/resolver/test_resolver_api_data.conf new file mode 100644 index 000000000..c31668117 --- /dev/null +++ b/src/resolver/test_resolver_api_data.conf @@ -0,0 +1,5 @@ +[PATHS] +SERVICEHOME = /tmp/test-gnunetd-statistics/ + +[resolver] +PORT = 22354 diff --git a/src/statistics/Makefile.am b/src/statistics/Makefile.am new file mode 100644 index 000000000..2ae254cbf --- /dev/null +++ b/src/statistics/Makefile.am @@ -0,0 +1,59 @@ +INCLUDES = -I$(top_srcdir)/src/include + +if MINGW + WINFLAGS = -Wl,--no-undefined -Wl,--export-all-symbols +endif + +if USE_COVERAGE + AM_CFLAGS = -fprofile-arcs -ftest-coverage +endif + + +lib_LTLIBRARIES = libgnunetstatistics.la + +libgnunetstatistics_la_SOURCES = \ + statistics_api.c statistics.h +libgnunetstatistics_la_LIBADD = \ + $(top_builddir)/src/util/libgnunetutil.la \ + $(GN_LIBINTL) +libgnunetstatistics_la_LDFLAGS = \ + $(GN_LIB_LDFLAGS) $(WINFLAGS) \ + -version-info 0:0:0 + + +bin_PROGRAMS = \ + gnunet-statistics \ + gnunet-service-statistics + +gnunet_statistics_SOURCES = \ + gnunet-statistics.c +gnunet_statistics_LDADD = \ + $(top_builddir)/src/statistics/libgnunetstatistics.la \ + $(top_builddir)/src/util/libgnunetutil.la \ + $(GN_LIBINTL) + +gnunet_service_statistics_SOURCES = \ + gnunet-service-statistics.c +gnunet_service_statistics_LDADD = \ + $(top_builddir)/src/statistics/libgnunetstatistics.la \ + $(top_builddir)/src/util/libgnunetutil.la \ + $(GN_LIBINTL) + + +check_PROGRAMS = \ + test_statistics_api + +TESTS = $(check_PROGRAMS) $(check_SCRIPTS) + +test_statistics_api_SOURCES = \ + test_statistics_api.c +test_statistics_api_LDADD = \ + $(top_builddir)/src/statistics/libgnunetstatistics.la \ + $(top_builddir)/src/util/libgnunetutil.la + +EXTRA_DIST = \ + test_statistics_api_data.conf + +check_SCRIPTS = \ + test_gnunet_statistics.sh + diff --git a/src/statistics/gnunet-service-statistics.c b/src/statistics/gnunet-service-statistics.c new file mode 100644 index 000000000..0e7b4853d --- /dev/null +++ b/src/statistics/gnunet-service-statistics.c @@ -0,0 +1,470 @@ +/* + This file is part of GNUnet. + (C) 2009 Christian Grothoff (and other contributing authors) + + GNUnet is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published + by the Free Software Foundation; either version 2, 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 + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with GNUnet; see the file COPYING. If not, write to the + Free Software Foundation, Inc., 59 Temple Place - Suite 330, + Boston, MA 02111-1307, USA. +*/ + +/** + * @file statistics/gnunet-service-statistics.c + * @brief program that tracks statistics + * @author Christian Grothoff + */ +#include "platform.h" +#include "gnunet_disk_lib.h" +#include "gnunet_getopt_lib.h" +#include "gnunet_protocols.h" +#include "gnunet_service_lib.h" +#include "gnunet_statistics_service.h" +#include "gnunet_strings_lib.h" +#include "gnunet_time_lib.h" +#include "statistics.h" + +/** + * Entry in the statistics list. + */ +struct StatsEntry +{ + /** + * This is a linked list. + */ + struct StatsEntry *next; + + /** + * Name of the service, points into the + * middle of msg. + */ + const char *service; + + /** + * Name for the value, points into + * the middle of msg. + */ + const char *name; + + /** + * Message that can be used to set this value, + * stored at the end of the memory used by + * this struct. + */ + struct GNUNET_STATISTICS_SetMessage *msg; + + /** + * Our value. + */ + uint64_t value; + + /** + * Unique ID. + */ + uint32_t uid; + + /** + * Is this value persistent? + */ + int persistent; + +}; + +/** + * Linked list of our active statistics. + */ +static struct StatsEntry *start; + +/** + * Counter used to generate unique values. + */ +static uint32_t uidgen; + +/** + * 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 (struct GNUNET_SERVER_Handle *server, + struct GNUNET_CONFIGURATION_Handle *cfg) +{ + char *fn; + int fd; + struct stat sb; + char *buf; + size_t off; + const struct GNUNET_MessageHeader *msg; + + fn = GNUNET_DISK_get_home_filename (cfg, + "statistics", "statistics.data", NULL); + if (fn == NULL) + return; + if ((0 != stat (fn, &sb)) || (sb.st_size == 0)) + { + GNUNET_free (fn); + return; + } + fd = GNUNET_DISK_file_open (fn, O_RDONLY); + if (fd == -1) + { + GNUNET_free (fn); + return; + } + buf = mmap (NULL, sb.st_size, PROT_READ, MAP_SHARED, fd, 0); + if (MAP_FAILED == buf) + { + GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING, "mmap", fn); + GNUNET_break (0 == CLOSE (fd)); + GNUNET_free (fn); + return; + } + GNUNET_log (GNUNET_ERROR_TYPE_INFO, + _("Loading %llu bytes of statistics from `%s'\n"), + (unsigned long long) sb.st_size, fn); + off = 0; + while (off + sizeof (struct GNUNET_MessageHeader) < sb.st_size) + { + msg = (const struct GNUNET_MessageHeader *) &buf[off]; + if ((ntohs (msg->size) + off > sb.st_size) || + (GNUNET_OK != GNUNET_SERVER_inject (server, NULL, msg))) + { + GNUNET_break (0); + break; + } + off += ntohs (msg->size); + } + GNUNET_break (0 == munmap (buf, sb.st_size)); + GNUNET_break (0 == CLOSE (fd)); + GNUNET_free (fn); +} + + +/** + * Write persistent statistics to disk. + * + * @param cls closure + * @param cfg configuration to use + */ +static void +save (void *cls, struct GNUNET_CONFIGURATION_Handle *cfg) +{ + struct StatsEntry *pos; + char *fn; + int fd; + uint16_t size; + unsigned long long total; + + fd = -1; + fn = GNUNET_DISK_get_home_filename (cfg, + "statistics", "statistics.data", NULL); + if (fn != NULL) + fd = + GNUNET_DISK_file_open (fn, O_WRONLY | O_CREAT | O_TRUNC, + S_IRUSR | S_IWUSR); + total = 0; + while (NULL != (pos = start)) + { + start = pos->next; + if ((pos->persistent) && (fd != -1)) + { + size = htons (pos->msg->header.size); + if (size != WRITE (fd, pos->msg, size)) + { + GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING, + "write", fn); + GNUNET_DISK_file_close (fn, fd); + fd = -1; + } + else + total += size; + } + GNUNET_free (pos); + } + if (fd != -1) + { + GNUNET_DISK_file_close (fn, fd); + if (total == 0) + GNUNET_break (0 == UNLINK (fn)); + else + GNUNET_log (GNUNET_ERROR_TYPE_INFO, + _("Wrote %llu bytes of statistics to `%s'\n"), total, fn); + } + GNUNET_free_non_null (fn); +} + + +/** + * Transmit the given stats value. + */ +static void +transmit (struct GNUNET_SERVER_TransmitContext *tc, + const struct StatsEntry *e) +{ + struct GNUNET_STATISTICS_ReplyMessage *m; + struct GNUNET_MessageHeader *h; + size_t size; + uint16_t msize; + + size = + sizeof (struct GNUNET_STATISTICS_ReplyMessage) + strlen (e->service) + 1 + + strlen (e->name) + 1; + GNUNET_assert (size < GNUNET_SERVER_MAX_MESSAGE_SIZE); + msize = size - sizeof (struct GNUNET_MessageHeader); + m = GNUNET_malloc (size); + m->uid = htonl (e->uid); + if (e->persistent) + m->uid |= htonl (GNUNET_STATISTICS_PERSIST_BIT); + m->value = GNUNET_htonll (e->value); + size -= sizeof (struct GNUNET_STATISTICS_ReplyMessage); + GNUNET_assert (size == GNUNET_STRINGS_buffer_fill ((char *) &m[1], + size, + 2, e->service, e->name)); +#if DEBUG_STATISTICS + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Transmitting value for `%s:%s': %llu\n", + e->service, e->name, e->value); +#endif + h = &m->header; + GNUNET_SERVER_transmit_context_append (tc, + &h[1], + msize, + GNUNET_MESSAGE_TYPE_STATISTICS_VALUE); + GNUNET_free (m); +} + + +/** + * Does this entry match the request? + */ +static int +matches (const struct StatsEntry *e, const char *service, const char *name) +{ + return ((0 == strlen (service)) || + (0 == strcmp (service, e->service))) + && ((0 == strlen (name)) || (0 == strcmp (name, e->name))); +} + + +/** + * Handle GET-message. + * + * @param cls closure + * @param server the server handling the message + * @param client identification of the client + * @param message the actual message + * @return GNUNET_OK to keep the connection open, + * GNUNET_SYSERR to close it (signal serious error) + */ +static void +handle_get (void *cls, + struct GNUNET_SERVER_Handle *server, + struct GNUNET_SERVER_Client *client, + const struct GNUNET_MessageHeader *message) +{ + char *service; + char *name; + struct StatsEntry *pos; + struct GNUNET_SERVER_TransmitContext *tc; + 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); + GNUNET_SERVER_receive_done (client, GNUNET_SYSERR); + return; + } +#if DEBUG_STATISTICS + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Received request for statistics on `%s:%s'\n", + strlen (service) ? service : "*", strlen (name) ? name : "*"); +#endif + tc = GNUNET_SERVER_transmit_context_create (client); + pos = start; + while (pos != NULL) + { + if (matches (pos, service, name)) + transmit (tc, pos); + pos = pos->next; + } + GNUNET_SERVER_transmit_context_append (tc, NULL, 0, + GNUNET_MESSAGE_TYPE_STATISTICS_END); + GNUNET_SERVER_transmit_context_run (tc, GNUNET_TIME_UNIT_FOREVER_REL); +} + + +/** + * Handle SET-message. + * + * @param cls closure + * @param server the server handling the message + * @param client identification of the client + * @param message the actual message + */ +static void +handle_set (void *cls, + struct GNUNET_SERVER_Handle *server, + struct GNUNET_SERVER_Client *client, + const struct GNUNET_MessageHeader *message) +{ + char *service; + char *name; + uint16_t msize; + uint16_t size; + const struct GNUNET_STATISTICS_SetMessage *msg; + struct StatsEntry *pos; + struct StatsEntry *prev; + uint32_t flags; + uint64_t value; + int64_t delta; + + msize = ntohs (message->size); + if (msize < sizeof (struct GNUNET_STATISTICS_SetMessage)) + { + GNUNET_break (0); + GNUNET_SERVER_receive_done (client, GNUNET_SYSERR); + return; + } + size = msize - sizeof (struct GNUNET_STATISTICS_SetMessage); + msg = (const struct GNUNET_STATISTICS_SetMessage *) message; + + if (size != GNUNET_STRINGS_buffer_tokenize ((const char *) &msg[1], + size, 2, &service, &name)) + { + GNUNET_break (0); + GNUNET_SERVER_receive_done (client, GNUNET_SYSERR); + return; + } +#if DEBUG_STATISTICS + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Received request to update statistic on `%s:%s'\n", + service, name); +#endif + flags = ntohl (msg->flags); + value = GNUNET_ntohll (msg->value); + pos = start; + prev = NULL; + while (pos != NULL) + { + if (matches (pos, service, name)) + { + if ((flags & GNUNET_STATISTICS_SETFLAG_RELATIVE) == 0) + { + pos->value = value; + } + else + { + delta = (int64_t) value; + if ((delta < 0) && (pos->value < -delta)) + { + pos->value = 0; + } + else + { + GNUNET_break ((delta <= 0) || + (pos->value + delta > pos->value)); + pos->value += delta; + } + } + pos->msg->value = GNUNET_htonll (pos->value); + pos->msg->flags = msg->flags; + pos->persistent = + (0 != (flags & GNUNET_STATISTICS_SETFLAG_PERSISTENT)); + if (prev != NULL) + { + /* move to front for faster setting next time! */ + prev->next = pos->next; + pos->next = start; + start = pos; + } +#if DEBUG_STATISTICS + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Statistic `%s:%s' updated to value %llu.\n", + service, name, pos->value); +#endif + GNUNET_SERVER_receive_done (client, GNUNET_OK); + return; + } + prev = pos; + pos = pos->next; + } + pos = GNUNET_malloc (sizeof (struct StatsEntry) + msize); + pos->next = start; + if (((flags & GNUNET_STATISTICS_SETFLAG_RELATIVE) == 0) || + (0 < (int64_t) GNUNET_ntohll (msg->value))) + pos->value = GNUNET_ntohll (msg->value); + pos->uid = uidgen++; + pos->persistent = (0 != (flags & GNUNET_STATISTICS_SETFLAG_PERSISTENT)); + pos->msg = (void *) &pos[1]; + memcpy (pos->msg, message, ntohs (message->size)); + pos->service = (const char *) &pos->msg[1]; + pos->name = &pos->service[strlen (pos->service) + 1]; + + start = pos; +#if DEBUG_STATISTICS + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "New statistic on `%s:%s' with value %llu created.\n", + service, name, pos->value); +#endif + GNUNET_SERVER_receive_done (client, GNUNET_OK); +} + + +/** + * List of handlers for the messages understood by this + * service. + */ +static struct GNUNET_SERVER_MessageHandler handlers[] = { + {&handle_set, NULL, GNUNET_MESSAGE_TYPE_STATISTICS_SET, 0}, + {&handle_get, NULL, GNUNET_MESSAGE_TYPE_STATISTICS_GET, 0}, + {NULL, NULL, 0, 0} +}; + + +/** + * Process statistics requests. + * + * @param cls closure + * @param sched scheduler to use + * @param server the initialized server + * @param cfg configuration to use + */ +static void +run (void *cls, + struct GNUNET_SCHEDULER_Handle *sched, + struct GNUNET_SERVER_Handle *server, + struct GNUNET_CONFIGURATION_Handle *cfg) +{ + GNUNET_SERVER_add_handlers (server, handlers); + load (server, cfg); +} + + +/** + * The main function for the statistics service. + * + * @param argc number of arguments from the command line + * @param argv command line arguments + * @return 0 ok, 1 on error + */ +int +main (int argc, char *const *argv) +{ + return (GNUNET_OK == + GNUNET_SERVICE_run (argc, + argv, + "statistics", &run, NULL, &save, NULL)) ? 0 : 1; +} + +/* end of gnunet-service-statistics.c */ diff --git a/src/statistics/gnunet-statistics.c b/src/statistics/gnunet-statistics.c new file mode 100644 index 000000000..bafb77c66 --- /dev/null +++ b/src/statistics/gnunet-statistics.c @@ -0,0 +1,179 @@ +/* + This file is part of GNUnet. + (C) 2001, 2002, 2004, 2005, 2006, 2007, 2009 Christian Grothoff (and other contributing authors) + + GNUnet is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published + by the Free Software Foundation; either version 2, 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 + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with GNUnet; see the file COPYING. If not, write to the + Free Software Foundation, Inc., 59 Temple Place - Suite 330, + Boston, MA 02111-1307, USA. +*/ + +/** + * @file statistics/gnunet-statistics.c + * @brief tool to obtain statistics + * @author Christian Grothoff + * @author Igor Wronsky + */ +#include "platform.h" +#include "gnunet_getopt_lib.h" +#include "gnunet_program_lib.h" +#include "gnunet_statistics_service.h" +#include "statistics.h" + +#define GET_TIMEOUT GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS, 15) + +/** + * Final status code. + */ +static int ret; + +/** + * Set to subsystem that we're going to get stats for (or NULL for all). + */ +static char *subsystem; + +/** + * 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; + +/** + * 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 (void *cls, + const char *subsystem, + const char *sname, unsigned long long value, int is_persistent) +{ + FPRINTF (stdout, + "%s%-20s %-40s: %16llu\n", + is_persistent ? "!" : " ", subsystem, _(sname), value); + return GNUNET_OK; +} + + +/** + * 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) +{ + struct GNUNET_STATISTICS_Handle *h = cls; + + if (success != GNUNET_OK) + ret = 1; + if (h != NULL) + GNUNET_STATISTICS_destroy (h); +} + + +/** + * Main function that will be run by the scheduler. + * + * @param cls closure + * @param sched the scheduler to use + * @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, + struct GNUNET_SCHEDULER_Handle *sched, + char *const *args, + const char *cfgfile, struct GNUNET_CONFIGURATION_Handle *cfg) +{ + struct GNUNET_STATISTICS_Handle *h; + unsigned long long val; + + if (args[0] != NULL) + { + if ((1 != SSCANF (args[0], "%llu", &val)) || + (subsystem == NULL) || (name == NULL)) + { + FPRINTF (stderr, _("Invalid argument `%s'\n"), args[0]); + ret = 1; + return; + } + h = GNUNET_STATISTICS_create (sched, subsystem, cfg); + if (h == NULL) + { + ret = 1; + return; + } + GNUNET_STATISTICS_set (h, name, val, persistent); + GNUNET_STATISTICS_destroy (h); + return; + } + h = GNUNET_STATISTICS_create (sched, "gnunet-statistics", cfg); + if (h == NULL) + { + ret = 1; + return; + } + GNUNET_STATISTICS_get (h, + subsystem, name, GET_TIMEOUT, &cleanup, &printer, h); +} + +/** + * gnunet-statistics command line options + */ +static struct GNUNET_GETOPT_CommandLineOption options[] = { + {'n', "name", "NAME", + gettext_noop ("limit output to statistcs for the given NAME"), 1, + &GNUNET_GETOPT_set_string, &name}, + {'p', "persistent", NULL, + gettext_noop ("make the value being set persistent"), 0, + &GNUNET_GETOPT_set_one, &persistent}, + {'s', "subsystem", "SUBSYSTEM", + gettext_noop ("limit output to the given SUBSYSTEM"), 1, + &GNUNET_GETOPT_set_string, &subsystem}, + GNUNET_GETOPT_OPTION_END +}; + + +/** + * 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) +{ + return (GNUNET_OK == + GNUNET_PROGRAM_run (argc, + argv, + "gnunet-statistics", + gettext_noop + ("Print statistics about GNUnet operations."), + options, &run, NULL)) ? ret : 1; +} + +/* end of gnunet-statistics.c */ diff --git a/src/statistics/statistics.h b/src/statistics/statistics.h new file mode 100644 index 000000000..6eedd4d34 --- /dev/null +++ b/src/statistics/statistics.h @@ -0,0 +1,94 @@ +/* + This file is part of GNUnet. + (C) 2001, 2002, 2003, 2004, 2009 Christian Grothoff (and other contributing authors) + + GNUnet is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published + by the Free Software Foundation; either version 2, 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 + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with GNUnet; see the file COPYING. If not, write to the + Free Software Foundation, Inc., 59 Temple Place - Suite 330, + Boston, MA 02111-1307, USA. +*/ + +/** + * @author Christian Grothoff + * @file statistics/statistics.h + */ +#ifndef STATISTICS_H +#define STATISTICS_H + +#include "gnunet_common.h" + +#define DEBUG_STATISTICS 0 + +/** + * 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. + */ + uint32_t uid GNUNET_PACKED; + + /** + * The value. + */ + uint64_t value GNUNET_PACKED; + +}; + +#define GNUNET_STATISTICS_PERSIST_BIT (1<<31) + +#define GNUNET_STATISTICS_SETFLAG_ABSOLUTE 0 + +#define GNUNET_STATISTICS_SETFLAG_RELATIVE 1 + +#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; + +}; + +#endif diff --git a/src/statistics/statistics_api.c b/src/statistics/statistics_api.c new file mode 100644 index 000000000..f68cf9396 --- /dev/null +++ b/src/statistics/statistics_api.c @@ -0,0 +1,688 @@ +/* + This file is part of GNUnet. + (C) 2009 Christian Grothoff (and other contributing authors) + + GNUnet is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published + by the Free Software Foundation; either version 2, 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 + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with GNUnet; see the file COPYING. If not, write to the + Free Software Foundation, Inc., 59 Temple Place - Suite 330, + Boston, MA 02111-1307, USA. +*/ + +/** + * @file statistics/statistics_api.c + * @brief API of the statistics service + * @author Christian Grothoff + */ +#include "platform.h" +#include "gnunet_client_lib.h" +#include "gnunet_protocols.h" +#include "gnunet_server_lib.h" +#include "gnunet_statistics_service.h" +#include "gnunet_strings_lib.h" +#include "statistics.h" + +#define SET_TRANSMIT_TIMEOUT GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS, 30) + + +/** + * Types of actions. + */ +enum ActionType +{ + ACTION_GET, + ACTION_SET, + ACTION_UPDATE +}; + + +/** + * Linked list of things we still need to do. + */ +struct ActionItem +{ + /** + * This is a linked list. + */ + struct ActionItem *next; + + /** + * 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 proc and cont. + */ + void *cls; + + /** + * Timeout for this action. + */ + struct GNUNET_TIME_Absolute timeout; + + /** + * Associated value. + */ + unsigned long long value; + + /** + * Flag for SET/UPDATE actions. + */ + int make_persistent; + + /** + * Has the current iteration been aborted; for GET actions. + */ + int aborted; + + /** + * Is this a GET, SET or UPDATE? + */ + enum ActionType type; + + /** + * Size of the message that we will be transmitting. + */ + uint16_t msize; + +}; + + +/** + * Handle for the service. + */ +struct GNUNET_STATISTICS_Handle +{ + /** + * Our scheduler. + */ + struct GNUNET_SCHEDULER_Handle *sched; + + /** + * Name of our subsystem. + */ + char *subsystem; + + /** + * Configuration to use. + */ + struct GNUNET_CONFIGURATION_Handle *cfg; + + /** + * Socket (if available). + */ + struct GNUNET_CLIENT_Connection *client; + + /** + * Head of the linked list of pending actions (first action + * to be performed). + */ + struct ActionItem *action_head; + + /** + * Tail of the linked list of actions (for fast append). + */ + struct ActionItem *action_tail; + + /** + * Action we are currently busy with (action request has been + * transmitted, we're now receiving the response from the + * service). + */ + struct ActionItem *current; + + /** + * Should this handle be destroyed once we've processed + * all actions? + */ + int do_destroy; + +}; + + +/** + * Try to (re)connect to the statistics service. + * + * @return GNUNET_YES on success, GNUNET_NO on failure. + */ +static int +try_connect (struct GNUNET_STATISTICS_Handle *ret) +{ + if (ret->client != NULL) + return GNUNET_OK; + ret->client = GNUNET_CLIENT_connect (ret->sched, "statistics", ret->cfg); + if (ret->client != NULL) + return GNUNET_YES; + GNUNET_log (GNUNET_ERROR_TYPE_WARNING, + _("Failed to connect to statistics service!\n")); + return GNUNET_NO; +} + + +/** + * Free memory associated with the given action item. + */ +static void +free_action_item (struct ActionItem *ai) +{ + GNUNET_free_non_null (ai->subsystem); + GNUNET_free_non_null (ai->name); + GNUNET_free (ai); +} + + +/** + * 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 (struct GNUNET_SCHEDULER_Handle *sched, + const char *subsystem, + struct GNUNET_CONFIGURATION_Handle *cfg) +{ + struct GNUNET_STATISTICS_Handle *ret; + + GNUNET_assert (subsystem != NULL); + GNUNET_assert (sched != NULL); + GNUNET_assert (cfg != NULL); + ret = GNUNET_malloc (sizeof (struct GNUNET_STATISTICS_Handle)); + ret->sched = sched; + ret->cfg = cfg; + ret->subsystem = GNUNET_strdup (subsystem); + try_connect (ret); + return ret; +} + + +/** + * Actually free the handle. + */ +static void +do_destroy (struct GNUNET_STATISTICS_Handle *h) +{ + GNUNET_assert (h->action_head == NULL); + GNUNET_assert (h->current == NULL); + if (h->client != NULL) + { + GNUNET_CLIENT_disconnect (h->client); + h->client = NULL; + } + GNUNET_free (h->subsystem); + GNUNET_free (h); +} + + +/** + * Destroy a handle (free all state associated with + * it). + */ +void +GNUNET_STATISTICS_destroy (struct GNUNET_STATISTICS_Handle *handle) +{ + GNUNET_assert (handle->do_destroy == GNUNET_NO); + if ((handle->action_head != NULL) || (handle->current != NULL)) + { + handle->do_destroy = GNUNET_YES; + return; + } + do_destroy (handle); +} + + +/** + * Process the message. + * + * @return GNUNET_OK if the message was well-formed + */ +static int +process_message (struct GNUNET_STATISTICS_Handle *h, + const struct GNUNET_MessageHeader *msg) +{ + char *service; + char *name; + const struct GNUNET_STATISTICS_ReplyMessage *smsg; + uint16_t size; + + if (h->current->aborted) + return GNUNET_OK; /* don't bother */ + size = ntohs (msg->size); + if (size < sizeof (struct GNUNET_STATISTICS_ReplyMessage)) + { + GNUNET_break (0); + return GNUNET_SYSERR; + } + smsg = (const struct GNUNET_STATISTICS_ReplyMessage *) msg; + 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; + } +#if DEBUG_STATISTICS + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Received valid statistic on `%s:%s': %llu\n", + service, name, GNUNET_ntohll (smsg->value)); +#endif + if (GNUNET_OK != + h->current->proc (h->current->cls, + service, + name, + GNUNET_ntohll (smsg->value), + 0 != + (ntohl (smsg->uid) & GNUNET_STATISTICS_PERSIST_BIT))) + { +#if DEBUG_STATISTICS + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Processing of remaining statistics aborted by client.\n"); +#endif + h->current->aborted = GNUNET_YES; + } + return GNUNET_OK; +} + + + +/** + * Schedule the next action to be performed. + */ +static void schedule_action (struct GNUNET_STATISTICS_Handle *h); + + +/** + * GET processing is complete, tell client about it. + */ +static void +finish (struct GNUNET_STATISTICS_Handle *h, int code) +{ + struct ActionItem *pos = h->current; + h->current = NULL; + schedule_action (h); + if (pos->cont != NULL) + pos->cont (pos->cls, code); + free_action_item (pos); +} + + +/** + * Function called with messages from stats service. + * + * @param cls closure + * @param msg message received, NULL on timeout or fatal error + */ +static void +receive_stats (void *cls, const struct GNUNET_MessageHeader *msg) +{ + struct GNUNET_STATISTICS_Handle *h = cls; + + if (msg == NULL) + { + GNUNET_CLIENT_disconnect (h->client); + h->client = NULL; + GNUNET_log (GNUNET_ERROR_TYPE_WARNING | GNUNET_ERROR_TYPE_BULK, + _ + ("Error receiving statistics from service, is the service running?\n")); + finish (h, GNUNET_SYSERR); + return; + } + switch (ntohs (msg->type)) + { + case GNUNET_MESSAGE_TYPE_STATISTICS_END: +#if DEBUG_STATISTICS + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Received end of statistics marker\n"); +#endif + finish (h, GNUNET_OK); + return; + case GNUNET_MESSAGE_TYPE_STATISTICS_VALUE: + if (GNUNET_OK == process_message (h, msg)) + { + /* finally, look for more! */ + GNUNET_CLIENT_receive (h->client, + &receive_stats, + h, + GNUNET_TIME_absolute_get_remaining (h-> + current-> + timeout)); + return; + } + GNUNET_break (0); + break; + default: + GNUNET_break (0); + break; + } + GNUNET_CLIENT_disconnect (h->client); + h->client = NULL; + finish (h, GNUNET_SYSERR); +} + + +/** + * Transmit a GET request (and if successful, start to receive + * the response). + */ +static size_t +transmit_get (struct GNUNET_STATISTICS_Handle *handle, size_t size, void *buf) +{ + struct GNUNET_MessageHeader *hdr; + size_t slen1; + size_t slen2; + uint16_t msize; + + if (buf == NULL) + { + /* timeout / error */ + GNUNET_log (GNUNET_ERROR_TYPE_WARNING, + _("Transmission of request for statistics failed!\n")); + finish (handle, GNUNET_SYSERR); + return 0; + } + slen1 = strlen (handle->current->subsystem) + 1; + slen2 = strlen (handle->current->name) + 1; + msize = slen1 + slen2 + sizeof (struct GNUNET_MessageHeader); + GNUNET_assert (msize <= size); + hdr = (struct GNUNET_MessageHeader *) buf; + hdr->size = htons (msize); + hdr->type = htons (GNUNET_MESSAGE_TYPE_STATISTICS_GET); + GNUNET_assert (slen1 + slen2 == + GNUNET_STRINGS_buffer_fill ((char *) &hdr[1], + slen1 + slen2, + 2, + handle->current->subsystem, + handle->current->name)); + GNUNET_CLIENT_receive (handle->client, + &receive_stats, + handle, + GNUNET_TIME_absolute_get_remaining (handle->current-> + timeout)); + return msize; +} + + + +/** + * Transmit a SET/UPDATE request. + */ +static size_t +transmit_set (struct GNUNET_STATISTICS_Handle *handle, size_t size, void *buf) +{ + struct GNUNET_STATISTICS_SetMessage *r; + size_t slen; + size_t nlen; + size_t nsize; + + if (NULL == buf) + { + finish (handle, GNUNET_SYSERR); + return 0; + } + + slen = strlen (handle->current->subsystem) + 1; + nlen = strlen (handle->current->name) + 1; + nsize = sizeof (struct GNUNET_STATISTICS_SetMessage) + slen + nlen; + if (size < nsize) + { + GNUNET_break (0); + finish (handle, GNUNET_SYSERR); + return 0; + } + r = buf; + r->header.size = htons (nsize); + r->header.type = htons (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)); + finish (handle, GNUNET_OK); + return nsize; +} + + +static size_t +transmit_action (void *cls, size_t size, void *buf) +{ + struct GNUNET_STATISTICS_Handle *handle = cls; + size_t ret; + + switch (handle->current->type) + { + case ACTION_GET: + ret = transmit_get (handle, size, buf); + break; + case ACTION_SET: + case ACTION_UPDATE: + ret = transmit_set (handle, size, buf); + break; + } + return ret; +} + + +/** + * Schedule the next action to be performed. + */ +static void +schedule_action (struct GNUNET_STATISTICS_Handle *h) +{ + struct GNUNET_TIME_Relative timeout; + + if (h->current != NULL) + return; /* action already pending */ + if (GNUNET_YES != try_connect (h)) + { + finish (h, GNUNET_SYSERR); + return; + } + + /* schedule next action */ + h->current = h->action_head; + if (NULL == h->current) + { + /* no pending network action, check destroy! */ + if (h->do_destroy != GNUNET_YES) + return; + do_destroy (h); + return; + } + h->action_head = h->action_head->next; + if (NULL == h->action_head) + h->action_tail = NULL; + h->current->next = NULL; + + timeout = GNUNET_TIME_absolute_get_remaining (h->current->timeout); + if (NULL == + GNUNET_CLIENT_notify_transmit_ready (h->client, + h->current->msize, + timeout, &transmit_action, h)) + { + GNUNET_log (GNUNET_ERROR_TYPE_WARNING, + "Failed to transmit request to statistics service.\n"); + finish (h, GNUNET_SYSERR); + } +} + + +static void +insert_ai (struct GNUNET_STATISTICS_Handle *h, struct ActionItem *ai) +{ + if (h->action_tail == NULL) + { + h->action_head = ai; + h->action_tail = ai; + schedule_action (h); + } + else + { + h->action_tail->next = ai; + h->action_tail = ai; + } +} + + +/** + * Get statistic from the peer. + * + * @param handle identification of the statistics service + * @param subsystem limit to the specified subsystem, NULL for our subsystem + * @param name name of the statistic value, NULL for all values + * @param timeout after how long should we give up (and call + * cont with an error code)? + * @param cont continuation to call when done (can be NULL) + * @param proc function to call on each value + * @param cls closure for cont and proc + */ +void +GNUNET_STATISTICS_get (struct GNUNET_STATISTICS_Handle *handle, + const char *subsystem, + const char *name, + struct GNUNET_TIME_Relative timeout, + GNUNET_STATISTICS_Callback cont, + GNUNET_STATISTICS_Iterator proc, void *cls) +{ + size_t slen1; + size_t slen2; + struct ActionItem *ai; + + GNUNET_assert (handle != NULL); + GNUNET_assert (proc != NULL); + if (GNUNET_YES != try_connect (handle)) + { + GNUNET_log (GNUNET_ERROR_TYPE_WARNING, + "Failed to connect to statistics service, can not get value `%s:%s'.\n", + strlen (subsystem) ? subsystem : "*", + strlen (name) ? name : "*"); + cont (cls, GNUNET_SYSERR); + return; + } + if (subsystem == NULL) + subsystem = ""; + if (name == NULL) + name = ""; + slen1 = strlen (subsystem); + slen2 = strlen (name); + GNUNET_assert (slen1 + slen2 + sizeof (struct GNUNET_MessageHeader) < + GNUNET_SERVER_MAX_MESSAGE_SIZE); + ai = GNUNET_malloc (sizeof (struct ActionItem)); + ai->subsystem = GNUNET_strdup (subsystem); + ai->name = GNUNET_strdup (name); + ai->cont = cont; + ai->proc = proc; + ai->cls = cls; + ai->timeout = GNUNET_TIME_relative_to_absolute (timeout); + ai->type = ACTION_GET; + ai->msize = slen1 + slen2 + sizeof (struct GNUNET_MessageHeader); + insert_ai (handle, ai); +} + + +static void +add_setter_action (struct GNUNET_STATISTICS_Handle *h, + const char *name, + int make_persistent, + unsigned long long value, enum ActionType type) +{ + struct ActionItem *ai; + size_t slen; + size_t nlen; + size_t nsize; + + GNUNET_assert (h != NULL); + GNUNET_assert (name != NULL); + if (GNUNET_YES != try_connect (h)) + return; + slen = strlen (h->subsystem) + 1; + nlen = strlen (name) + 1; + nsize = sizeof (struct GNUNET_STATISTICS_SetMessage) + slen + nlen; + if (nsize >= GNUNET_SERVER_MAX_MESSAGE_SIZE) + { + GNUNET_break (0); + return; + } + ai = GNUNET_malloc (sizeof (struct ActionItem)); + 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; + insert_ai (h, ai); + schedule_action (h); +} + + +/** + * Set statistic value for the peer. Will always use our + * subsystem (the argument used when "handle" was created). + * + * @param handle identification of the statistics service + * @param name name of the statistic value + * @param value new value to set + * @param make_persistent should the value be kept across restarts? + */ +void +GNUNET_STATISTICS_set (struct GNUNET_STATISTICS_Handle *handle, + const char *name, + unsigned long long value, int make_persistent) +{ + add_setter_action (handle, name, make_persistent, value, ACTION_SET); +} + + +/** + * Set statistic value for the peer. Will always use our + * subsystem (the argument used when "handle" was created). + * + * @param handle identification of the statistics service + * @param name name of the statistic value + * @param delta change in value (added to existing value) + * @param make_persistent should the value be kept across restarts? + */ +void +GNUNET_STATISTICS_update (struct GNUNET_STATISTICS_Handle *handle, + const char *name, + long long delta, int make_persistent) +{ + add_setter_action (handle, name, make_persistent, + (unsigned long long) delta, ACTION_UPDATE); +} + + +/* end of statistics_api.c */ diff --git a/src/statistics/test_gnunet_statistics.sh b/src/statistics/test_gnunet_statistics.sh new file mode 100755 index 000000000..43f629f36 --- /dev/null +++ b/src/statistics/test_gnunet_statistics.sh @@ -0,0 +1,177 @@ +#!/bin/bash + +rm -rf /tmp/test-gnunetd-statistics/ +exe="./gnunet-statistics -c test_statistics_api_data.conf" +base=/tmp/gnunet-test-statistics +#DEBUG="-L DEBUG" +# ----------------------------------- +echo -n "Preparing: Starting service..." +./gnunet-service-statistics $DEBUG -c test_statistics_api_data.conf & +sleep 1 +echo "DONE" + +# ---------------------------------------------------------------------------------- +echo -n "TEST: Bad argument checking..." + +if $exe -x 2> /dev/null; then + echo "FAIL: error running $exe" + kill %% + exit 1 +fi +echo "PASS" + +# ---------------------------------------------------------------------------------- +echo -n "TEST: Set value..." + +if ! $exe $DEBUG -n test -s subsystem 42 ; then + echo "FAIL: error running $exe" + kill %% + exit 1 +fi +echo "PASS" + +# ---------------------------------------------------------------------------------- +echo -n "TEST: Set another value..." + +if ! $exe $DEBUG -n other -s osystem 43 ; then + echo "FAIL: error running $exe" + kill %% + exit 1 +fi +echo "PASS" + +# ---------------------------------------------------------------------------------- +echo -n "TEST: viewing all stats..." + +if ! $exe $DEBUG > $base.out; then + echo "FAIL: error running $exe" + kill %% + exit 1 +fi +LINES=`cat $base.out | wc -l` +if test $LINES -ne 2; then + echo "FAIL: unexpected output" + kill %% + exit 1 +fi +echo "PASS" + +# ---------------------------------------------------------------------------------- +echo -n "TEST: viewing stats by name..." + +if ! $exe $DEBUG -n other > $base.out; then + echo "FAIL: error running $exe" + kill %% + exit 1 +fi +LINES=`cat $base.out | grep 43 | wc -l` +if test $LINES -ne 1; then + echo "FAIL: unexpected output" + kill %% + exit 1 +fi +echo "PASS" + +# ---------------------------------------------------------------------------------- +echo -n "TEST: viewing stats by subsystem..." + +if ! $exe $DEBUG -s subsystem > $base.out; then + echo "FAIL: error running $exe" + kill %% + exit 1 +fi +LINES=`cat $base.out | grep 42 | wc -l` +if test $LINES -ne 1; then + echo "FAIL: unexpected output" + kill %% + exit 1 +fi +echo "PASS" + + +# ---------------------------------------------------------------------------------- +echo -n "TEST: Set persistent value..." + +if ! $exe $DEBUG -n lasting -s subsystem 40 -p; then + echo "FAIL: error running $exe" + kill %% + exit 1 +fi +echo "PASS" + +# ----------------------------------- +echo -n "Restarting service..." +sleep 1 +if ! kill %%; +then + echo "FAIL: could not kill service" + kill %% + exit 1 +fi +sleep 1 +./gnunet-service-statistics $DEBUG -c test_statistics_api_data.conf & +sleep 1 + + +# ---------------------------------------------------------------------------------- +echo -n "TEST: checking persistence..." + +if ! $exe $DEBUG > $base.out; then + echo "FAIL: error running $exe" + kill %% + exit 1 +fi +LINES=`cat $base.out | grep 40 | wc -l` +if test $LINES -ne 1; then + echo "FAIL: unexpected output" + kill %% + exit 1 +fi +echo "PASS" + + + +# ---------------------------------------------------------------------------------- +echo -n "TEST: Removing persistence..." + +if ! $exe $DEBUG -n lasting -s subsystem 40; then + echo "FAIL: error running $exe" + kill %% + exit 1 +fi +echo "PASS" + + +# ----------------------------------- +echo -n "Restarting service..." +sleep 1 +if ! kill %%; +then + echo "FAIL: could not kill service" + kill %% + exit 1 +fi +sleep 1 +./gnunet-service-statistics $DEBUG -c test_statistics_api_data.conf & +sleep 1 + + +# ---------------------------------------------------------------------------------- +echo -n "TEST: checking removed persistence..." + +if ! $exe $DEBUG > $base.out; then + echo "FAIL: error running $exe" + kill %% + exit 1 +fi +LINES=`cat $base.out | grep 40 | wc -l` +if test $LINES -ne 0; then + echo "FAIL: unexpected output" + kill %% + exit 1 +fi +echo "PASS" + +kill %% +rm -f $base.out +rm -rf /tmp/test-gnunetd-statistics/ diff --git a/src/statistics/test_statistics_api.c b/src/statistics/test_statistics_api.c new file mode 100644 index 000000000..7a39f54b6 --- /dev/null +++ b/src/statistics/test_statistics_api.c @@ -0,0 +1,177 @@ +/* + This file is part of GNUnet. + (C) 2009 Christian Grothoff (and other contributing authors) + + GNUnet is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published + by the Free Software Foundation; either version 2, 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 + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with GNUnet; see the file COPYING. If not, write to the + Free Software Foundation, Inc., 59 Temple Place - Suite 330, + Boston, MA 02111-1307, USA. +*/ +/** + * @file statistics/test_statistics_api.c + * @brief testcase for statistics_api.c + */ +#include "platform.h" +#include "gnunet_common.h" +#include "gnunet_getopt_lib.h" +#include "gnunet_os_lib.h" +#include "gnunet_program_lib.h" +#include "gnunet_scheduler_lib.h" +#include "gnunet_statistics_service.h" + +#define VERBOSE GNUNET_NO + +static int +check_1 (void *cls, + const char *subsystem, + const char *name, unsigned long long value, int is_persistent) +{ + 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, unsigned long long value, int is_persistent) +{ + 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, unsigned long long value, int is_persistent) +{ + 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 struct GNUNET_STATISTICS_Handle *h; + +static void +next_fin (void *cls, int success) +{ + int *ok = cls; + + GNUNET_STATISTICS_destroy (h); + GNUNET_assert (success == GNUNET_OK); + *ok = 0; +} + +static void +next (void *cls, int success) +{ + GNUNET_assert (success == GNUNET_OK); + GNUNET_STATISTICS_get (h, NULL, "test-2", + GNUNET_TIME_UNIT_SECONDS, &next_fin, &check_2, cls); +} + +static void +run (void *cls, + struct GNUNET_SCHEDULER_Handle *sched, + char *const *args, + const char *cfgfile, struct GNUNET_CONFIGURATION_Handle *cfg) +{ + + h = GNUNET_STATISTICS_create (sched, "test-statistics-api", cfg); + 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_STATISTICS_get (h, NULL, "test-1", + GNUNET_TIME_UNIT_SECONDS, &next, &check_1, cls); +} + +static void +run_more (void *cls, + struct GNUNET_SCHEDULER_Handle *sched, + char *const *args, + const char *cfgfile, struct GNUNET_CONFIGURATION_Handle *cfg) +{ + h = GNUNET_STATISTICS_create (sched, "test-statistics-api", cfg); + GNUNET_STATISTICS_get (h, NULL, "test-3", + GNUNET_TIME_UNIT_SECONDS, &next_fin, &check_3, cls); +} + +static int +check () +{ + int ok = 1; + pid_t pid; + char *const argv[] = { "test-statistics-api", + "-c", + "test_statistics_api_data.conf", + NULL + }; + struct GNUNET_GETOPT_CommandLineOption options[] = { + GNUNET_GETOPT_OPTION_END + }; + pid = GNUNET_OS_start_process ("gnunet-service-statistics", + "gnunet-service-statistics", +#if DEBUG_STATISTICS + "-L", "DEBUG", +#endif + "-c", "test_statistics_api_data.conf", NULL); + sleep (1); + GNUNET_PROGRAM_run (3, argv, "test-statistics-api", "nohelp", + options, &run, &ok); + if (0 != PLIBC_KILL (pid, SIGTERM)) + { + GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING, "kill"); + ok = 1; + } + waitpid (pid, NULL, 0); + if (ok != 0) + return ok; + ok = 1; + /* restart to check persistence! */ + pid = GNUNET_OS_start_process ("gnunet-service-statistics", + "gnunet-service-statistics", +#if DEBUG_STATISTICS + "-L", "DEBUG", +#endif + "-c", "test_statistics_api_data.conf", NULL); + sleep (1); + GNUNET_PROGRAM_run (3, argv, "test-statistics-api", "nohelp", + options, &run_more, &ok); + if (0 != PLIBC_KILL (pid, SIGTERM)) + { + GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING, "kill"); + ok = 1; + } + waitpid (pid, NULL, 0); + return ok; +} + +int +main (int argc, char *argv[]) +{ + int ret; + + ret = check (); + + return ret; +} + +/* end of test_statistics_api.c */ diff --git a/src/statistics/test_statistics_api_data.conf b/src/statistics/test_statistics_api_data.conf new file mode 100644 index 000000000..571a9b3e4 --- /dev/null +++ b/src/statistics/test_statistics_api_data.conf @@ -0,0 +1,5 @@ +[PATHS] +SERVICEHOME = /tmp/test-gnunetd-statistics/ + +[statistics] +PORT = 22353 diff --git a/src/template/Makefile.am b/src/template/Makefile.am new file mode 100644 index 000000000..47d6b4165 --- /dev/null +++ b/src/template/Makefile.am @@ -0,0 +1,37 @@ +INCLUDES = -I$(top_srcdir)/src/include + +if MINGW + WINFLAGS = -Wl,--no-undefined -Wl,--export-all-symbols +endif + +if USE_COVERAGE + AM_CFLAGS = -fprofile-arcs -ftest-coverage +endif + +bin_PROGRAMS = \ + gnunet-template \ + gnunet-service-template + +gnunet_template_SOURCES = \ + gnunet-template.c +gnunet_template_LDADD = \ + $(top_builddir)/src/util/libgnunetutil.la \ + $(GN_LIBINTL) + +gnunet_service_template_SOURCES = \ + gnunet-service-template.c +gnunet_service_template_LDADD = \ + $(top_builddir)/src/util/libgnunetutil.la \ + $(GN_LIBINTL) + + +check_PROGRAMS = \ + test_template_api + +TESTS = $(check_PROGRAMS) + +test_template_api_SOURCES = \ + test_template_api.c +test_template_api_LDADD = \ + $(top_builddir)/src/util/libgnunetutil.la + diff --git a/src/template/gnunet-service-template.c b/src/template/gnunet-service-template.c new file mode 100644 index 000000000..c43d681d7 --- /dev/null +++ b/src/template/gnunet-service-template.c @@ -0,0 +1,87 @@ +/* + This file is part of GNUnet. + (C) 2009 Christian Grothoff (and other contributing authors) + + GNUnet is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published + by the Free Software Foundation; either version 2, 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 + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with GNUnet; see the file COPYING. If not, write to the + Free Software Foundation, Inc., 59 Temple Place - Suite 330, + Boston, MA 02111-1307, USA. +*/ + +/** + * @file template/gnunet-service-template.c + * @brief program that tracks template + * @author Christian Grothoff + */ +#include "platform.h" +#include "gnunet_getopt_lib.h" +#include "gnunet_service_lib.h" + +/** + * Do cleanup here. + * + * @param cls closure + * @param cfg configuration to use + */ +static void +finish (void *cls, struct GNUNET_CONFIGURATION_Handle *cfg) +{ + /* FIXME */ +} + + +/** + * List of handlers for the messages understood by this + * service. + */ +static struct GNUNET_SERVER_MessageHandler handlers[] = { + /* FIXME: add handlers here! */ + {NULL, NULL, 0, 0} +}; + +/** + * Process template requests. + * + * @param cls closure + * @param sched scheduler to use + * @param server the initialized server + * @param cfg configuration to use + */ +static void +run (void *cls, + struct GNUNET_SCHEDULER_Handle *sched, + struct GNUNET_SERVER_Handle *server, + struct GNUNET_CONFIGURATION_Handle *cfg) +{ + /* FIXME: do setup here */ + GNUNET_SERVER_add_handlers (server, handlers); +} + + +/** + * The main function for the template service. + * + * @param argc number of arguments from the command line + * @param argv command line arguments + * @return 0 ok, 1 on error + */ +int +main (int argc, char *const *argv) +{ + return (GNUNET_OK == + GNUNET_SERVICE_run (argc, + argv, + "template", &run, NULL, &finish, NULL)) ? 0 : 1; +} + +/* end of gnunet-service-template.c */ diff --git a/src/template/gnunet-template.c b/src/template/gnunet-template.c new file mode 100644 index 000000000..ea47c7f45 --- /dev/null +++ b/src/template/gnunet-template.c @@ -0,0 +1,81 @@ +/* + This file is part of GNUnet. + (C) 2001, 2002, 2004, 2005, 2006, 2007, 2009 Christian Grothoff (and other contributing authors) + + GNUnet is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published + by the Free Software Foundation; either version 2, 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 + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with GNUnet; see the file COPYING. If not, write to the + Free Software Foundation, Inc., 59 Temple Place - Suite 330, + Boston, MA 02111-1307, USA. +*/ + +/** + * @file template/gnunet-template.c + * @brief template for writing a tool + * @author Christian Grothoff + */ +#include "platform.h" +#include "gnunet_getopt_lib.h" +#include "gnunet_program_lib.h" +/* #include "gnunet_template_service.h" */ + +/** + * Final status code. + */ +static int ret; + +/** + * Main function that will be run by the scheduler. + * + * @param cls closure + * @param sched the scheduler to use + * @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, + struct GNUNET_SCHEDULER_Handle *sched, + char *const *args, + const char *cfgfile, struct GNUNET_CONFIGURATION_Handle *cfg) +{ + /* main code here */ +} + +/** + * gnunet-template command line options + */ +static struct GNUNET_GETOPT_CommandLineOption options[] = { + /* FIMXE: add options here */ + GNUNET_GETOPT_OPTION_END +}; + + +/** + * The main function to obtain template from gnunetd. + * + * @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) +{ + return (GNUNET_OK == + GNUNET_PROGRAM_run (argc, + argv, + "gnunet-template", + gettext_noop ("help text"), + options, &run, NULL)) ? ret : 1; +} + +/* end of gnunet-template.c */ diff --git a/src/template/test_template_api.c b/src/template/test_template_api.c new file mode 100644 index 000000000..8d53a15da --- /dev/null +++ b/src/template/test_template_api.c @@ -0,0 +1,45 @@ +/* + This file is part of GNUnet. + (C) 2009 Christian Grothoff (and other contributing authors) + + GNUnet is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published + by the Free Software Foundation; either version 2, 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 + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with GNUnet; see the file COPYING. If not, write to the + Free Software Foundation, Inc., 59 Temple Place - Suite 330, + Boston, MA 02111-1307, USA. +*/ +/** + * @file template/test_template.c + * @brief testcase for template.c + */ +#include "platform.h" +#include "gnunet_common.h" + +#define VERBOSE GNUNET_NO + +static int +check () +{ + return 0; +} + +int +main (int argc, char *argv[]) +{ + int ret; + + ret = check (); + + return ret; +} + +/* end of test_template.c */ diff --git a/src/transport/Makefile.am b/src/transport/Makefile.am new file mode 100644 index 000000000..236dec7c4 --- /dev/null +++ b/src/transport/Makefile.am @@ -0,0 +1,84 @@ +INCLUDES = -I$(top_srcdir)/src/include + +plugindir = $(libdir)/gnunet + +if MINGW + WINFLAGS = -Wl,--no-undefined -Wl,--export-all-symbols +endif + +if USE_COVERAGE + AM_CFLAGS = -fprofile-arcs -ftest-coverage +endif + + +lib_LTLIBRARIES = \ + libgnunettransport.la + +libgnunettransport_la_SOURCES = \ + transport_api.c transport.h +libgnunettransport_la_LIBADD = \ + $(top_builddir)/src/arm/libgnunetarm.la \ + $(top_builddir)/src/hello/libgnunethello.la \ + $(top_builddir)/src/util/libgnunetutil.la \ + $(GN_LIBINTL) +libgnunettransport_la_LDFLAGS = \ + $(GN_LIB_LDFLAGS) $(WINFLAGS) \ + -version-info 0:0:0 + + +bin_PROGRAMS = \ + gnunet-transport \ + gnunet-service-transport + +gnunet_transport_SOURCES = \ + gnunet-transport.c +gnunet_transport_LDADD = \ + $(top_builddir)/src/transport/libgnunettransport.la \ + $(top_builddir)/src/util/libgnunetutil.la \ + $(GN_LIBINTL) + +gnunet_service_transport_SOURCES = \ + gnunet-service-transport.c +gnunet_service_transport_LDADD = \ + $(top_builddir)/src/peerinfo/libgnunetpeerinfo.la \ + $(top_builddir)/src/util/libgnunetutil.la \ + $(GN_LIBINTL) + + + +plugin_LTLIBRARIES = \ + libgnunet_plugin_transport_tcp.la \ + libgnunet_plugin_transport_template.la +# TODO: add udp, http, nat, etc. + +libgnunet_plugin_transport_tcp_la_SOURCES = \ + plugin_transport_tcp.c +libgnunet_plugin_transport_tcp_la_LIBADD = \ + $(top_builddir)/src/resolver/libgnunetresolver.la \ + $(top_builddir)/src/util/libgnunetutil.la +libgnunet_plugin_transport_tcp_la_LDFLAGS = \ + $(GN_PLUGIN_LDFLAGS) + +libgnunet_plugin_transport_template_la_SOURCES = \ + plugin_transport_template.c +libgnunet_plugin_transport_template_la_LDFLAGS = \ + $(GN_PLUGIN_LDFLAGS) + + +check_PROGRAMS = \ + test_transport_api +# TODO: add tests for tcp, udp, http, nat, etc. + +TESTS = $(check_PROGRAMS) + +test_transport_api_SOURCES = \ + test_transport_api.c +test_transport_api_LDADD = \ + $(top_builddir)/src/transport/libgnunettransport.la \ + $(top_builddir)/src/util/libgnunetutil.la + + +EXTRA_DIST = \ + test_transport_api_data.conf \ + test_transport_api_peer1.conf \ + test_transport_api_peer2.conf diff --git a/src/transport/NOTES b/src/transport/NOTES new file mode 100644 index 000000000..41404e1f9 --- /dev/null +++ b/src/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/transport/gnunet-service-transport.c b/src/transport/gnunet-service-transport.c new file mode 100644 index 000000000..08745c378 --- /dev/null +++ b/src/transport/gnunet-service-transport.c @@ -0,0 +1,2852 @@ +/* + This file is part of GNUnet. + (C) 2009 Christian Grothoff (and other contributing authors) + + GNUnet is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published + by the Free Software Foundation; either version 2, 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 + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with GNUnet; see the file COPYING. If not, write to the + Free Software Foundation, Inc., 59 Temple Place - Suite 330, + Boston, MA 02111-1307, USA. +*/ + +/** + * @file transport/gnunet-service-transport.c + * @brief low-level P2P messaging + * @author Christian Grothoff + * + * TODO: + * - if we do not receive an ACK in response to our + * HELLO, retransmit HELLO! + */ +#include "platform.h" +#include "gnunet_client_lib.h" +#include "gnunet_getopt_lib.h" +#include "gnunet_hello_lib.h" +#include "gnunet_os_lib.h" +#include "gnunet_peerinfo_service.h" +#include "gnunet_plugin_lib.h" +#include "gnunet_protocols.h" +#include "gnunet_service_lib.h" +#include "gnunet_signatures.h" +#include "plugin_transport.h" +#include "transport.h" + +/** + * How many messages can we have pending for a given client process + * before we start to drop incoming messages? We typically should + * have only one client and so this would be the primary buffer for + * messages, so the number should be chosen rather generously. + * + * The expectation here is that most of the time the queue is large + * enough so that a drop is virtually never required. + */ +#define MAX_PENDING 128 + +/** + * How often should we try to reconnect to a peer using a particular + * transport plugin before giving up? Note that the plugin may be + * added back to the list after PLUGIN_RETRY_FREQUENCY expires. + */ +#define MAX_CONNECT_RETRY 3 + +/** + * How often must a peer violate bandwidth quotas before we start + * to simply drop its messages? + */ +#define QUOTA_VIOLATION_DROP_THRESHOLD 100 + +/** + * How long until a HELLO verification attempt should time out? + */ +#define HELLO_VERIFICATION_TIMEOUT GNUNET_TIME_UNIT_MINUTES + +/** + * How often do we re-add (cheaper) plugins to our list of plugins + * to try for a given connected peer? + */ +#define PLUGIN_RETRY_FREQUENCY GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_MINUTES, 15) + +/** + * After how long do we expire an address in a HELLO + * that we just validated? This value is also used + * for our own addresses when we create a HELLO. + */ +#define HELLO_ADDRESS_EXPIRATION GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_HOURS, 12) + +/** + * After how long do we consider a connection to a peer dead + * if we don't receive messages from the peer? + */ +#define IDLE_CONNECTION_TIMEOUT GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_MINUTES, 5) + + +/** + * Entry in linked list of network addresses. + */ +struct AddressList +{ + /** + * This is a linked list. + */ + struct AddressList *next; + + /** + * The address, actually a pointer to the end + * of this struct. Do not free! + */ + void *addr; + + /** + * How long until we auto-expire this address (unless it is + * re-confirmed by the transport)? + */ + struct GNUNET_TIME_Absolute expires; + + /** + * Length of addr. + */ + size_t addrlen; + +}; + + +/** + * Entry in linked list of all of our plugins. + */ +struct TransportPlugin +{ + + /** + * This is a linked list. + */ + struct TransportPlugin *next; + + /** + * API of the transport as returned by the plugin's + * initialization function. + */ + struct GNUNET_TRANSPORT_PluginFunctions *api; + + /** + * Short name for the plugin (i.e. "tcp"). + */ + char *short_name; + + /** + * Name of the library (i.e. "gnunet_plugin_transport_tcp"). + */ + char *lib_name; + + /** + * List of our known addresses for this transport. + */ + struct AddressList *addresses; + + /** + * Environment this transport service is using + * for this plugin. + */ + struct GNUNET_TRANSPORT_PluginEnvironment env; + + /** + * ID of task that is used to clean up expired addresses. + */ + GNUNET_SCHEDULER_TaskIdentifier address_update_task; + + + /** + * Set to GNUNET_YES if we need to scrap the existing + * list of "addresses" and start fresh when we receive + * the next address update from a transport. Set to + * GNUNET_NO if we should just add the new address + * to the list and wait for the commit call. + */ + int rebuild; +}; + +struct NeighbourList; + +/** + * For each neighbour we keep a list of messages + * that we still want to transmit to the neighbour. + */ +struct MessageQueue +{ + + /** + * This is a linked list. + */ + struct MessageQueue *next; + + /** + * The message we want to transmit. + */ + struct GNUNET_MessageHeader *message; + + /** + * Client responsible for queueing the message; + * used to check that a client has not two messages + * pending for the same target. Can be NULL. + */ + struct TransportClient *client; + + /** + * Neighbour this entry belongs to. + */ + struct NeighbourList *neighbour; + + /** + * Plugin that we used for the transmission. + * NULL until we scheduled a transmission. + */ + struct TransportPlugin *plugin; + + /** + * Internal message of the transport system that should not be + * included in the usual SEND-SEND_OK transmission confirmation + * traffic management scheme. Typically, "internal_msg" will + * be set whenever "client" is NULL (but it is not strictly + * required). + */ + int internal_msg; + +}; + + +/** + * For a given Neighbour, which plugins are available + * to talk to this peer and what are their costs? + */ +struct ReadyList +{ + + /** + * This is a linked list. + */ + struct ReadyList *next; + + /** + * Which of our transport plugins does this entry + * represent? + */ + struct TransportPlugin *plugin; + + /** + * Neighbour this entry belongs to. + */ + struct NeighbourList *neighbour; + + /** + * Opaque handle (specific to the plugin) for the + * connection to our target; can be NULL. + */ + void *plugin_handle; + + /** + * What was the last latency observed for this plugin + * and peer? Invalid if connected is GNUNET_NO. + */ + struct GNUNET_TIME_Relative latency; + + /** + * If we did not successfully transmit a message to the + * given peer via this connection during the specified + * time, we should consider the connection to be dead. + * This is used in the case that a TCP transport simply + * stalls writing to the stream but does not formerly + * get a signal that the other peer died. + */ + struct GNUNET_TIME_Absolute timeout; + + /** + * Is this plugin currently connected? The first time + * we transmit or send data to a peer via a particular + * plugin, we set this to GNUNET_YES. If we later get + * an error (disconnect notification or transmission + * failure), we set it back to GNUNET_NO. Each time the + * value is set to GNUNET_YES, we increment the + * "connect_attempts" counter. If that one reaches a + * particular threshold, we consider the plugin to not + * be working properly at this time for the given peer + * and remove it from the eligible list. + */ + int connected; + + /** + * How often have we tried to connect using this plugin? + */ + unsigned int connect_attempts; + + /** + * Is this plugin ready to transmit to the specific + * target? GNUNET_NO if not. Initially, all plugins + * are marked ready. If a transmission is in progress, + * "transmit_ready" is set to GNUNET_NO. + */ + int transmit_ready; + +}; + + +/** + * Entry in linked list of all of our current neighbours. + */ +struct NeighbourList +{ + + /** + * This is a linked list. + */ + struct NeighbourList *next; + + /** + * Which of our transports is connected to this peer + * and what is their status? + */ + struct ReadyList *plugins; + + /** + * List of messages we would like to send to this peer; + * must contain at most one message per client. + */ + struct MessageQueue *messages; + + /** + * Identity of this neighbour. + */ + struct GNUNET_PeerIdentity id; + + /** + * ID of task scheduled to run when this peer is about to + * time out (will free resources associated with the peer). + */ + GNUNET_SCHEDULER_TaskIdentifier timeout_task; + + /** + * How long until we should consider this peer dead + * (if we don't receive another message in the + * meantime)? + */ + struct GNUNET_TIME_Absolute peer_timeout; + + /** + * At what time did we reset last_received last? + */ + struct GNUNET_TIME_Absolute last_quota_update; + + /** + * At what time should we try to again add plugins to + * our ready list? + */ + struct GNUNET_TIME_Absolute retry_plugins_time; + + /** + * How many bytes have we received since the "last_quota_update" + * timestamp? + */ + uint64_t last_received; + + /** + * Global quota for outbound traffic for the neighbour in bytes/ms. + */ + uint32_t quota_in; + + /** + * What is the latest version of our HELLO that we have + * sent to this neighbour? + */ + unsigned int hello_version_sent; + + /** + * How often has the other peer (recently) violated the + * inbound traffic limit? Incremented by 10 per violation, + * decremented by 1 per non-violation (for each + * time interval). + */ + unsigned int quota_violation_count; + + /** + * Have we seen an ACK from this neighbour in the past? + * (used to make up a fake ACK for clients connecting after + * the neighbour connected to us). + */ + int saw_ack; + +}; + + +/** + * Linked list of messages to be transmitted to + * the client. Each entry is followed by the + * actual message. + */ +struct ClientMessageQueueEntry +{ + /** + * This is a linked list. + */ + struct ClientMessageQueueEntry *next; +}; + + +/** + * Client connected to the transport service. + */ +struct TransportClient +{ + + /** + * This is a linked list. + */ + struct TransportClient *next; + + /** + * Handle to the client. + */ + struct GNUNET_SERVER_Client *client; + + /** + * Linked list of messages yet to be transmitted to + * the client. + */ + struct ClientMessageQueueEntry *message_queue_head; + + /** + * Tail of linked list of messages yet to be transmitted to the + * client. + */ + struct ClientMessageQueueEntry *message_queue_tail; + + /** + * Is a call to "transmit_send_continuation" pending? If so, we + * must not free this struct (even if the corresponding client + * disconnects) and instead only remove it from the linked list and + * set the "client" field to NULL. + */ + int tcs_pending; + + /** + * Length of the list of messages pending for this client. + */ + unsigned int message_count; + +}; + + +/** + * Message used to ask a peer to validate receipt (to check an address + * from a HELLO). Followed by the address used. Note that the + * recipients response does not affirm that he has this address, + * only that he got the challenge message. + */ +struct ValidationChallengeMessage +{ + + /** + * Type will be GNUNET_MESSAGE_TYPE_TRANSPORT_PING + */ + struct GNUNET_MessageHeader header; + + /** + * What are we signing and why? + */ + struct GNUNET_CRYPTO_RsaSignaturePurpose purpose; + + /** + * Random challenge number (in network byte order). + */ + uint32_t challenge GNUNET_PACKED; + + /** + * Who is the intended recipient? + */ + struct GNUNET_PeerIdentity target; +}; + + +/** + * Message used to validate a HELLO. If this was + * the right recipient, the response is a signature + * of the original validation request. The + * challenge is included in the confirmation to make + * matching of replies to requests possible. + */ +struct ValidationChallengeResponse +{ + + /** + * Type will be GNUNET_MESSAGE_TYPE_TRANSPORT_PONG + */ + struct GNUNET_MessageHeader header; + + /** + * Random challenge number (in network byte order). + */ + uint32_t challenge GNUNET_PACKED; + + /** + * Who signed this message? + */ + struct GNUNET_PeerIdentity sender; + + /** + * Signature. + */ + struct GNUNET_CRYPTO_RsaSignature signature; + +}; + + +/** + * For each HELLO, we may have to validate multiple addresses; + * each address gets its own request entry. + */ +struct ValidationAddress +{ + /** + * This is a linked list. + */ + struct ValidationAddress *next; + + /** + * Our challenge message. Points to after this + * struct, so this field should not be freed. + */ + struct ValidationChallengeMessage *msg; + + /** + * Name of the transport. + */ + char *transport_name; + + /** + * When should this validated address expire? + */ + struct GNUNET_TIME_Absolute expiration; + + /** + * Length of the address we are validating. + */ + size_t addr_len; + + /** + * Set to GNUNET_YES if the challenge was met, + * GNUNET_SYSERR if we know it failed, GNUNET_NO + * if we are waiting on a response. + */ + int ok; +}; + + +/** + * Entry in linked list of all HELLOs awaiting validation. + */ +struct ValidationList +{ + + /** + * This is a linked list. + */ + struct ValidationList *next; + + /** + * Linked list with one entry per address from the HELLO + * that needs to be validated. + */ + struct ValidationAddress *addresses; + + /** + * The public key of the peer. + */ + struct GNUNET_CRYPTO_RsaPublicKeyBinaryEncoded publicKey; + + /** + * When does this record time-out? (assuming the + * challenge goes unanswered) + */ + struct GNUNET_TIME_Absolute timeout; + +}; + + +/** + * HELLOs awaiting validation. + */ +static struct ValidationList *pending_validations; + +/** + * Our HELLO message. + */ +static struct GNUNET_HELLO_Message *our_hello; + +/** + * "version" of "our_hello". Used to see if a given + * neighbour has already been sent the latest version + * of our HELLO message. + */ +static unsigned int our_hello_version; + +/** + * Our public key. + */ +static struct GNUNET_CRYPTO_RsaPublicKeyBinaryEncoded my_public_key; + +/** + * Our identity. + */ +static struct GNUNET_PeerIdentity my_identity; + +/** + * Our private key. + */ +static struct GNUNET_CRYPTO_RsaPrivateKey *my_private_key; + +/** + * Our scheduler. + */ +struct GNUNET_SCHEDULER_Handle *sched; + +/** + * Our configuration. + */ +struct GNUNET_CONFIGURATION_Handle *cfg; + +/** + * Linked list of all clients to this service. + */ +static struct TransportClient *clients; + +/** + * All loaded plugins. + */ +static struct TransportPlugin *plugins; + +/** + * Our server. + */ +static struct GNUNET_SERVER_Handle *server; + +/** + * All known neighbours and their HELLOs. + */ +static struct NeighbourList *neighbours; + +/** + * Default bandwidth quota for receiving for new peers in bytes/ms. + */ +static uint32_t default_quota_in; + +/** + * Default bandwidth quota for sending for new peers in bytes/ms. + */ +static uint32_t default_quota_out; + +/** + * Number of neighbours we'd like to have. + */ +static uint32_t max_connect_per_transport; + + +/** + * Find an entry in the neighbour list for a particular peer. + * + * @return NULL if not found. + */ +static struct NeighbourList * +find_neighbour (const struct GNUNET_PeerIdentity *key) +{ + struct NeighbourList *head = neighbours; + while ((head != NULL) && + (0 != memcmp (key, &head->id, sizeof (struct GNUNET_PeerIdentity)))) + head = head->next; + return head; +} + + +/** + * Find an entry in the transport list for a particular transport. + * + * @return NULL if not found. + */ +static struct TransportPlugin * +find_transport (const char *short_name) +{ + struct TransportPlugin *head = plugins; + while ((head != NULL) && (0 != strcmp (short_name, head->short_name))) + head = head->next; + return head; +} + + +/** + * Update the quota values for the given neighbour now. + */ +static void +update_quota (struct NeighbourList *n) +{ + struct GNUNET_TIME_Relative delta; + uint64_t allowed; + uint64_t remaining; + + delta = GNUNET_TIME_absolute_get_duration (n->last_quota_update); + if (delta.value < MIN_QUOTA_REFRESH_TIME) + return; /* not enough time passed for doing quota update */ + allowed = delta.value * n->quota_in; + if (n->last_received < allowed) + { + remaining = allowed - n->last_received; + if (n->quota_in > 0) + remaining /= n->quota_in; + else + remaining = 0; + if (remaining > MAX_BANDWIDTH_CARRY) + remaining = MAX_BANDWIDTH_CARRY; + n->last_received = 0; + n->last_quota_update = GNUNET_TIME_absolute_get (); + n->last_quota_update.value -= remaining; + if (n->quota_violation_count > 0) + n->quota_violation_count--; + } + else + { + n->last_received -= allowed; + n->last_quota_update = GNUNET_TIME_absolute_get (); + if (n->last_received > allowed) + { + /* more than twice the allowed rate! */ + n->quota_violation_count += 10; + } + } +} + + +/** + * Function called to notify a client about the socket + * being ready to queue more data. "buf" will be + * NULL and "size" zero if the socket was closed for + * writing in the meantime. + * + * @param cls closure + * @param size number of bytes available in buf + * @param buf where the callee should write the message + * @return number of bytes written to buf + */ +static size_t +transmit_to_client_callback (void *cls, size_t size, void *buf) +{ + struct TransportClient *client = cls; + struct ClientMessageQueueEntry *q; + uint16_t msize; + size_t tsize; + const struct GNUNET_MessageHeader *msg; + struct GNUNET_NETWORK_TransmitHandle *th; + char *cbuf; + + if (buf == NULL) + { + /* fatal error with client, free message queue! */ + while (NULL != (q = client->message_queue_head)) + { + client->message_queue_head = q->next; + GNUNET_free (q); + } + client->message_queue_tail = NULL; + client->message_count = 0; + return 0; + } + cbuf = buf; + tsize = 0; + while (NULL != (q = client->message_queue_head)) + { + msg = (const struct GNUNET_MessageHeader *) &q[1]; + msize = ntohs (msg->size); + if (msize + tsize > size) + break; + client->message_queue_head = q->next; + if (q->next == NULL) + client->message_queue_tail = NULL; + memcpy (&cbuf[tsize], msg, msize); + tsize += msize; + GNUNET_free (q); + client->message_count--; + } + GNUNET_assert (tsize > 0); + if (NULL != q) + { + th = GNUNET_SERVER_notify_transmit_ready (client->client, + msize, + GNUNET_TIME_UNIT_FOREVER_REL, + &transmit_to_client_callback, + client); + GNUNET_assert (th != NULL); + } + return tsize; +} + + +/** + * Send the specified message to the specified client. Since multiple + * messages may be pending for the same client at a time, this code + * makes sure that no message is lost. + * + * @param client client to transmit the message to + * @param msg the message to send + * @param may_drop can this message be dropped if the + * message queue for this client is getting far too large? + */ +static void +transmit_to_client (struct TransportClient *client, + const struct GNUNET_MessageHeader *msg, int may_drop) +{ + struct ClientMessageQueueEntry *q; + uint16_t msize; + struct GNUNET_NETWORK_TransmitHandle *th; + + if ((client->message_count >= MAX_PENDING) && (GNUNET_YES == may_drop)) + { + GNUNET_log (GNUNET_ERROR_TYPE_INFO, + _ + ("Dropping message, have %u messages pending (%u is the soft limit)\n"), + client->message_count, MAX_PENDING); + /* TODO: call to statistics... */ + return; + } + client->message_count++; + msize = ntohs (msg->size); + q = GNUNET_malloc (sizeof (struct ClientMessageQueueEntry) + msize); + memcpy (&q[1], msg, msize); + /* append to message queue */ + if (client->message_queue_tail == NULL) + { + client->message_queue_tail = q; + } + else + { + client->message_queue_tail->next = q; + client->message_queue_tail = q; + } + if (client->message_queue_head == NULL) + { + client->message_queue_head = q; + th = GNUNET_SERVER_notify_transmit_ready (client->client, + msize, + GNUNET_TIME_UNIT_FOREVER_REL, + &transmit_to_client_callback, + client); + GNUNET_assert (th != NULL); + } +} + + +/** + * Find alternative plugins for communication. + * + * @param neighbour for which neighbour should we try to find + * more plugins? + */ +static void +try_alternative_plugins (struct NeighbourList *neighbour) +{ + struct ReadyList *rl; + + if ((neighbour->plugins != NULL) && + (neighbour->retry_plugins_time.value > + GNUNET_TIME_absolute_get ().value)) + return; /* don't try right now */ + neighbour->retry_plugins_time + = GNUNET_TIME_relative_to_absolute (PLUGIN_RETRY_FREQUENCY); + + rl = neighbour->plugins; + while (rl != NULL) + { + if (rl->connect_attempts > 0) + rl->connect_attempts--; /* amnesty */ + rl = rl->next; + } + +} + + +/** + * Check the ready list for the given neighbour and + * if a plugin is ready for transmission (and if we + * have a message), do so! + * + * @param neighbour target peer for which to check the plugins + */ +static void try_transmission_to_peer (struct NeighbourList *neighbour); + + +/** + * Function called by the GNUNET_TRANSPORT_TransmitFunction + * upon "completion" of a send request. This tells the API + * that it is now legal to send another message to the given + * peer. + * + * @param cls closure, identifies the entry on the + * message queue that was transmitted and the + * client responsible for queueing the message + * @param rl identifies plugin used for the transmission for + * this neighbour; needs to be re-enabled for + * future transmissions + * @param target the peer receiving the message + * @param result GNUNET_OK on success, if the transmission + * failed, we should not tell the client to transmit + * more messages + */ +static void +transmit_send_continuation (void *cls, + struct ReadyList *rl, + const struct GNUNET_PeerIdentity *target, + int result) +{ + struct MessageQueue *mq = cls; + struct SendOkMessage send_ok_msg; + struct NeighbourList *n; + + GNUNET_assert (mq != NULL); + n = mq->neighbour; + GNUNET_assert (0 == + memcmp (&n->id, target, + sizeof (struct GNUNET_PeerIdentity))); + if (rl == NULL) + { + rl = n->plugins; + while ((rl != NULL) && (rl->plugin != mq->plugin)) + rl = rl->next; + GNUNET_assert (rl != NULL); + } + if (result == GNUNET_OK) + rl->timeout = GNUNET_TIME_relative_to_absolute (IDLE_CONNECTION_TIMEOUT); + else + rl->connected = GNUNET_NO; + if (!mq->internal_msg) + rl->transmit_ready = GNUNET_YES; + if (mq->client != NULL) + { + send_ok_msg.header.size = htons (sizeof (send_ok_msg)); + send_ok_msg.header.type = htons (GNUNET_MESSAGE_TYPE_TRANSPORT_SEND_OK); + send_ok_msg.success = htonl (result); + send_ok_msg.peer = n->id; + transmit_to_client (mq->client, &send_ok_msg.header, GNUNET_NO); + } + GNUNET_free (mq->message); + GNUNET_free (mq); + /* one plugin just became ready again, try transmitting + another message (if available) */ + try_transmission_to_peer (n); +} + + + + +/** + * We could not use an existing (or validated) connection to + * talk to a peer. Try addresses that have not yet been + * validated. + * + * @param n neighbour we want to communicate with + * @return plugin ready to talk, or NULL if none is available + */ +static struct ReadyList * +try_unvalidated_addresses (struct NeighbourList *n) +{ + struct ValidationList *vl; + struct ValidationAddress *va; + struct GNUNET_PeerIdentity id; + struct GNUNET_TIME_Absolute now; + unsigned int total; + unsigned int cnt; + struct ReadyList *rl; + struct TransportPlugin *plugin; + +#if DEBUG_TRANSPORT + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Trying to connect to `%4s' using unvalidated addresses\n", + GNUNET_i2s (&n->id)); +#endif + /* NOTE: this function needs to not only identify the + plugin but also setup "plugin_handle", binding it to the + right address using the plugin's "send_to" API */ + now = GNUNET_TIME_absolute_get (); + vl = pending_validations; + while (vl != NULL) + { + GNUNET_CRYPTO_hash (&vl->publicKey, + sizeof (struct + GNUNET_CRYPTO_RsaPublicKeyBinaryEncoded), + &id.hashPubKey); + if (0 == memcmp (&id, &n->id, sizeof (struct GNUNET_PeerIdentity))) + break; + vl = vl->next; + } + if (vl == NULL) + { +#if DEBUG_TRANSPORT + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "No unvalidated address found for peer `%4s'\n", + GNUNET_i2s (&n->id)); +#endif + return NULL; + } + total = 0; + cnt = 0; + va = vl->addresses; + while (va != NULL) + { + cnt++; + if (va->expiration.value > now.value) + total++; + va = va->next; + } + if (total == 0) + { +#if DEBUG_TRANSPORT + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "All %u unvalidated addresses for peer have expired\n", + cnt); +#endif + return NULL; + } + total = GNUNET_CRYPTO_random_u32 (GNUNET_CRYPTO_QUALITY_WEAK, total); + for (va = vl->addresses; va != NULL; va = va->next) + { + if (va->expiration.value <= now.value) + continue; + if (total > 0) + { + total--; + continue; + } +#if DEBUG_TRANSPORT + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG | GNUNET_ERROR_TYPE_BULK, + "Trying unvalidated address of `%s' transport\n", + va->transport_name); +#endif + plugin = find_transport (va->transport_name); + if (plugin == NULL) + { + GNUNET_break (0); + break; + } + rl = GNUNET_malloc (sizeof (struct ReadyList)); + rl->next = n->plugins; + n->plugins = rl; + rl->plugin = plugin; + rl->plugin_handle = plugin->api->send_to (plugin->api->cls, + &n->id, + NULL, + NULL, + GNUNET_TIME_UNIT_ZERO, + &va->msg[1], va->addr_len); + rl->transmit_ready = GNUNET_YES; + return rl; + } + return NULL; +} + + +/** + * Check the ready list for the given neighbour and + * if a plugin is ready for transmission (and if we + * have a message), do so! + */ +static void +try_transmission_to_peer (struct NeighbourList *neighbour) +{ + struct ReadyList *pos; + struct GNUNET_TIME_Relative min_latency; + struct ReadyList *rl; + struct MessageQueue *mq; + struct GNUNET_TIME_Absolute now; + + if (neighbour->messages == NULL) + return; /* nothing to do */ + try_alternative_plugins (neighbour); + min_latency = GNUNET_TIME_UNIT_FOREVER_REL; + rl = NULL; + mq = neighbour->messages; + now = GNUNET_TIME_absolute_get (); + pos = neighbour->plugins; + while (pos != NULL) + { + /* set plugins that are inactive for a long time back to disconnected */ + if ((pos->timeout.value < now.value) && (pos->connected == GNUNET_YES)) + { +#if DEBUG_TRANSPORT + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Marking long-time inactive connection to `%4s' as down.\n", + GNUNET_i2s (&neighbour->id)); +#endif + pos->connected = GNUNET_NO; + } + if (((GNUNET_YES == pos->transmit_ready) || + (mq->internal_msg)) && + (pos->connect_attempts < MAX_CONNECT_RETRY) && + ((rl == NULL) || (min_latency.value > pos->latency.value))) + { + rl = pos; + min_latency = pos->latency; + } + pos = pos->next; + } + if (rl == NULL) + rl = try_unvalidated_addresses (neighbour); + if (rl == NULL) + { +#if DEBUG_TRANSPORT + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "No plugin ready to transmit message\n"); +#endif + return; /* nobody ready */ + } + if (GNUNET_NO == rl->connected) + { + rl->connect_attempts++; + rl->connected = GNUNET_YES; + } + neighbour->messages = mq->next; + mq->plugin = rl->plugin; + if (!mq->internal_msg) + rl->transmit_ready = GNUNET_NO; +#if DEBUG_TRANSPORT + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Giving message of type `%u' for `%4s' to plugin `%s'\n", + ntohs (mq->message->type), + GNUNET_i2s (&neighbour->id), rl->plugin->short_name); +#endif + rl->plugin_handle + = rl->plugin->api->send (rl->plugin->api->cls, + rl->plugin_handle, + rl, + &neighbour->id, + mq->message, + IDLE_CONNECTION_TIMEOUT, + &transmit_send_continuation, mq); +} + + +/** + * Send the specified message to the specified peer. + * + * @param client source of the transmission request (can be NULL) + * @param msg message to send + * @param is_internal is this an internal message + * @param neighbour handle to the neighbour for transmission + */ +static void +transmit_to_peer (struct TransportClient *client, + const struct GNUNET_MessageHeader *msg, + int is_internal, struct NeighbourList *neighbour) +{ + struct MessageQueue *mq; + struct MessageQueue *mqe; + struct GNUNET_MessageHeader *m; + +#if DEBUG_TRANSPORT + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + _("Sending message of type %u to peer `%4s'\n"), + ntohs (msg->type), GNUNET_i2s (&neighbour->id)); +#endif + if (client != NULL) + { + /* check for duplicate submission */ + mq = neighbour->messages; + while (NULL != mq) + { + if (mq->client == client) + { + /* client transmitted to same peer twice + before getting SendOk! */ + GNUNET_break (0); + return; + } + mq = mq->next; + } + } + mq = GNUNET_malloc (sizeof (struct MessageQueue)); + mq->client = client; + m = GNUNET_malloc (ntohs (msg->size)); + memcpy (m, msg, ntohs (msg->size)); + mq->message = m; + mq->neighbour = neighbour; + mq->internal_msg = is_internal; + + /* find tail */ + mqe = neighbour->messages; + if (mqe != NULL) + while (mqe->next != NULL) + mqe = mqe->next; + if (mqe == NULL) + { + /* new head */ + neighbour->messages = mq; + try_transmission_to_peer (neighbour); + } + else + { + /* append */ + mqe->next = mq; + } +} + + +struct GeneratorContext +{ + struct TransportPlugin *plug_pos; + struct AddressList *addr_pos; + struct GNUNET_TIME_Absolute expiration; +}; + + +static size_t +address_generator (void *cls, size_t max, void *buf) +{ + struct GeneratorContext *gc = cls; + size_t ret; + + while ((gc->addr_pos == NULL) && (gc->plug_pos != NULL)) + { + gc->plug_pos = gc->plug_pos->next; + gc->addr_pos = (gc->plug_pos != NULL) ? gc->plug_pos->addresses : NULL; + } + if (NULL == gc->plug_pos) + return 0; + ret = GNUNET_HELLO_add_address (gc->plug_pos->short_name, + gc->expiration, + gc->addr_pos->addr, + gc->addr_pos->addrlen, buf, max); + gc->addr_pos = gc->addr_pos->next; + return ret; +} + + +/** + * Construct our HELLO message from all of the addresses of + * all of the transports. + */ +static void +refresh_hello () +{ + struct GNUNET_HELLO_Message *hello; + struct TransportClient *cpos; + struct NeighbourList *npos; + struct GeneratorContext gc; + +#if DEBUG_TRANSPORT + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG | GNUNET_ERROR_TYPE_BULK, + "Refreshing my HELLO\n"); +#endif + gc.plug_pos = plugins; + gc.addr_pos = plugins != NULL ? plugins->addresses : NULL; + gc.expiration = GNUNET_TIME_relative_to_absolute (HELLO_ADDRESS_EXPIRATION); + hello = GNUNET_HELLO_create (&my_public_key, &address_generator, &gc); + cpos = clients; + while (cpos != NULL) + { + transmit_to_client (cpos, + (const struct GNUNET_MessageHeader *) hello, + GNUNET_NO); + cpos = cpos->next; + } + + GNUNET_free_non_null (our_hello); + our_hello = hello; + our_hello_version++; + npos = neighbours; + while (npos != NULL) + { + transmit_to_peer (NULL, + (const struct GNUNET_MessageHeader *) our_hello, + GNUNET_YES, npos); + npos = npos->next; + } +} + + +/** + * Task used to clean up expired addresses for a plugin. + * + * @param cls closure + * @param tc context + */ +static void +expire_address_task (void *cls, + const struct GNUNET_SCHEDULER_TaskContext *tc); + + +/** + * Update the list of addresses for this plugin, + * expiring those that are past their expiration date. + * + * @param plugin addresses of which plugin should be recomputed? + * @param fresh set to GNUNET_YES if a new address was added + * and we need to regenerate the HELLO even if nobody + * expired + */ +static void +update_addresses (struct TransportPlugin *plugin, int fresh) +{ + struct GNUNET_TIME_Relative min_remaining; + struct GNUNET_TIME_Relative remaining; + struct GNUNET_TIME_Absolute now; + struct AddressList *pos; + struct AddressList *prev; + struct AddressList *next; + int expired; + + if (plugin->address_update_task != GNUNET_SCHEDULER_NO_PREREQUISITE_TASK) + GNUNET_SCHEDULER_cancel (plugin->env.sched, plugin->address_update_task); + plugin->address_update_task = GNUNET_SCHEDULER_NO_PREREQUISITE_TASK; + now = GNUNET_TIME_absolute_get (); + min_remaining = GNUNET_TIME_UNIT_FOREVER_REL; + expired = GNUNET_NO; + prev = NULL; + pos = plugin->addresses; + while (pos != NULL) + { + next = pos->next; + if (pos->expires.value < now.value) + { + expired = GNUNET_YES; + if (prev == NULL) + plugin->addresses = pos->next; + else + prev->next = pos->next; + GNUNET_free (pos); + } + else + { + remaining = GNUNET_TIME_absolute_get_remaining (pos->expires); + if (remaining.value < min_remaining.value) + min_remaining = remaining; + prev = pos; + } + pos = next; + } + + if (expired || fresh) + refresh_hello (); + if (min_remaining.value < GNUNET_TIME_UNIT_FOREVER_REL.value) + plugin->address_update_task + = GNUNET_SCHEDULER_add_delayed (plugin->env.sched, + GNUNET_NO, + GNUNET_SCHEDULER_PRIORITY_IDLE, + GNUNET_SCHEDULER_NO_PREREQUISITE_TASK, + min_remaining, + &expire_address_task, plugin); + +} + + +/** + * Task used to clean up expired addresses for a plugin. + * + * @param cls closure + * @param tc context + */ +static void +expire_address_task (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc) +{ + struct TransportPlugin *plugin = cls; + plugin->address_update_task = GNUNET_SCHEDULER_NO_PREREQUISITE_TASK; + update_addresses (plugin, GNUNET_NO); +} + + +/** + * Function that must be called by each plugin to notify the + * transport service about the addresses under which the transport + * provided by the plugin can be reached. + * + * @param cls closure + * @param name name of the transport that generated the address + * @param addr one of the addresses of the host, NULL for the last address + * the specific address format depends on the transport + * @param addrlen length of the address + * @param expires when should this address automatically expire? + */ +static void +plugin_env_notify_address (void *cls, + const char *name, + const void *addr, + size_t addrlen, + struct GNUNET_TIME_Relative expires) +{ + struct TransportPlugin *p = cls; + struct AddressList *al; + struct GNUNET_TIME_Absolute abex; + +#if DEBUG_TRANSPORT + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Plugin `%s' informs us about a new address\n", name); +#endif + abex = GNUNET_TIME_relative_to_absolute (expires); + GNUNET_assert (p == find_transport (name)); + + al = p->addresses; + while (al != NULL) + { + if ((addrlen == al->addrlen) && (0 == memcmp (addr, &al[1], addrlen))) + { + if (al->expires.value < abex.value) + al->expires = abex; + return; + } + al = al->next; + } + al = GNUNET_malloc (sizeof (struct AddressList) + addrlen); + al->addr = &al[1]; + al->next = p->addresses; + p->addresses = al; + al->expires = abex; + al->addrlen = addrlen; + memcpy (&al[1], addr, addrlen); + update_addresses (p, GNUNET_YES); +} + + +struct LookupHelloContext +{ + GNUNET_TRANSPORT_AddressCallback iterator; + + void *iterator_cls; +}; + + +static int +lookup_address_callback (void *cls, + const char *tname, + struct GNUNET_TIME_Absolute expiration, + const void *addr, size_t addrlen) +{ + struct LookupHelloContext *lhc = cls; + lhc->iterator (lhc->iterator_cls, tname, addr, addrlen); + return GNUNET_OK; +} + + +static void +lookup_hello_callback (void *cls, + const struct GNUNET_PeerIdentity *peer, + const struct GNUNET_HELLO_Message *h, uint32_t trust) +{ + struct LookupHelloContext *lhc = cls; + + if (peer == NULL) + { + lhc->iterator (lhc->iterator_cls, NULL, NULL, 0); + GNUNET_free (lhc); + return; + } + if (h == NULL) + return; + GNUNET_HELLO_iterate_addresses (h, + GNUNET_NO, &lookup_address_callback, lhc); +} + + +/** + * Function that allows a transport to query the known + * network addresses for a given peer. + * + * @param cls closure + * @param timeout after how long should we time out? + * @param target which peer are we looking for? + * @param iter function to call for each known address + * @param iter_cls closure for iter + */ +static void +plugin_env_lookup_address (void *cls, + struct GNUNET_TIME_Relative timeout, + const struct GNUNET_PeerIdentity *target, + GNUNET_TRANSPORT_AddressCallback iter, + void *iter_cls) +{ + struct LookupHelloContext *lhc; + + lhc = GNUNET_malloc (sizeof (struct LookupHelloContext)); + lhc->iterator = iter; + lhc->iterator_cls = iter_cls; + GNUNET_PEERINFO_for_all (cfg, + sched, + target, 0, timeout, &lookup_hello_callback, &lhc); +} + + +/** + * Notify all of our clients about a peer connecting. + */ +static void +notify_clients_connect (const struct GNUNET_PeerIdentity *peer, + struct GNUNET_TIME_Relative latency) +{ + struct ConnectInfoMessage cim; + struct TransportClient *cpos; + +#if DEBUG_TRANSPORT + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Informing clients about peer `%4s' connecting to us\n", + GNUNET_i2s (peer)); +#endif + cim.header.size = htons (sizeof (struct ConnectInfoMessage)); + cim.header.type = htons (GNUNET_MESSAGE_TYPE_TRANSPORT_CONNECT); + cim.quota_out = htonl (default_quota_out); + cim.latency = GNUNET_TIME_relative_hton (latency); + memcpy (&cim.id, peer, sizeof (struct GNUNET_PeerIdentity)); + cpos = clients; + while (cpos != NULL) + { + transmit_to_client (cpos, &cim.header, GNUNET_NO); + cpos = cpos->next; + } +} + + +/** + * Notify all of our clients about a peer disconnecting. + */ +static void +notify_clients_disconnect (const struct GNUNET_PeerIdentity *peer) +{ + struct DisconnectInfoMessage dim; + struct TransportClient *cpos; + +#if DEBUG_TRANSPORT + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Informing clients about peer `%4s' disconnecting\n", + GNUNET_i2s (peer)); +#endif + dim.header.size = htons (sizeof (struct DisconnectInfoMessage)); + dim.header.type = htons (GNUNET_MESSAGE_TYPE_TRANSPORT_DISCONNECT); + dim.reserved = htonl (0); + memcpy (&dim.peer, peer, sizeof (struct GNUNET_PeerIdentity)); + cpos = clients; + while (cpos != NULL) + { + transmit_to_client (cpos, &dim.header, GNUNET_NO); + cpos = cpos->next; + } +} + + +/** + * Copy any validated addresses to buf. + * + * @return 0 once all addresses have been + * returned + */ +static size_t +list_validated_addresses (void *cls, size_t max, void *buf) +{ + struct ValidationAddress **va = cls; + size_t ret; + + while ((NULL != *va) && ((*va)->ok != GNUNET_YES)) + *va = (*va)->next; + if (NULL == *va) + return 0; + ret = GNUNET_HELLO_add_address ((*va)->transport_name, + (*va)->expiration, + &(*va)->msg[1], (*va)->addr_len, buf, max); + *va = (*va)->next; + return ret; +} + + +/** + * HELLO validation cleanup task. + */ +static void +cleanup_validation (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc) +{ + struct ValidationAddress *va; + struct ValidationList *pos; + struct ValidationList *prev; + struct GNUNET_TIME_Absolute now; + struct GNUNET_HELLO_Message *hello; + struct GNUNET_PeerIdentity pid; + +#if DEBUG_TRANSPORT + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG | GNUNET_ERROR_TYPE_BULK, + "HELLO validation cleanup background task running...\n"); +#endif + now = GNUNET_TIME_absolute_get (); + prev = NULL; + pos = pending_validations; + while (pos != NULL) + { + if (pos->timeout.value < now.value) + { + if (prev == NULL) + pending_validations = pos->next; + else + prev->next = pos->next; + va = pos->addresses; + hello = GNUNET_HELLO_create (&pos->publicKey, + &list_validated_addresses, &va); + GNUNET_CRYPTO_hash (&pos->publicKey, + sizeof (struct + GNUNET_CRYPTO_RsaPublicKeyBinaryEncoded), + &pid.hashPubKey); +#if DEBUG_TRANSPORT + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Creating persistent `%s' message for peer `%4s' based on confirmed addresses.\n", + "HELLO", GNUNET_i2s (&pid)); +#endif + GNUNET_PEERINFO_add_peer (cfg, sched, &pid, hello); + GNUNET_free (hello); + while (NULL != (va = pos->addresses)) + { + pos->addresses = va->next; + GNUNET_free (va->transport_name); + GNUNET_free (va); + } + GNUNET_free (pos); + if (prev == NULL) + pos = pending_validations; + else + pos = prev->next; + continue; + } + prev = pos; + pos = pos->next; + } + + /* finally, reschedule cleanup if needed; list is + ordered by timeout, so we need the last element... */ + pos = pending_validations; + while ((pos != NULL) && (pos->next != NULL)) + pos = pos->next; + if (NULL != pos) + GNUNET_SCHEDULER_add_delayed (sched, + GNUNET_NO, + GNUNET_SCHEDULER_PRIORITY_IDLE, + GNUNET_SCHEDULER_NO_PREREQUISITE_TASK, + GNUNET_TIME_absolute_get_remaining (pos-> + timeout), + &cleanup_validation, NULL); +} + + +struct CheckHelloValidatedContext +{ + /** + * Plugin for which we are validating. + */ + struct TransportPlugin *plugin; + + /** + * Hello that we are validating. + */ + struct GNUNET_HELLO_Message *hello; + + /** + * Validation list being build. + */ + struct ValidationList *e; +}; + + +/** + * Append the given address to the list of entries + * that need to be validated. + */ +static int +run_validation (void *cls, + const char *tname, + struct GNUNET_TIME_Absolute expiration, + const void *addr, size_t addrlen) +{ + struct ValidationList *e = cls; + struct TransportPlugin *tp; + struct ValidationAddress *va; + struct ValidationChallengeMessage *vcm; + + tp = find_transport (tname); + if (tp == NULL) + { + GNUNET_log (GNUNET_ERROR_TYPE_INFO | + GNUNET_ERROR_TYPE_BULK, + _ + ("Transport `%s' not loaded, will not try to validate peer address using this transport.\n"), + tname); + return GNUNET_OK; + } + va = GNUNET_malloc (sizeof (struct ValidationAddress) + + sizeof (struct ValidationChallengeMessage) + addrlen); + va->next = e->addresses; + e->addresses = va; + vcm = (struct ValidationChallengeMessage *) &va[1]; + va->msg = vcm; + va->transport_name = GNUNET_strdup (tname); + va->addr_len = addrlen; + vcm->header.size = + htons (sizeof (struct ValidationChallengeMessage) + addrlen); + vcm->header.type = htons (GNUNET_MESSAGE_TYPE_TRANSPORT_PING); + vcm->purpose.size = + htonl (sizeof (struct ValidationChallengeMessage) + addrlen - + sizeof (struct GNUNET_MessageHeader)); + vcm->purpose.purpose = htonl (GNUNET_SIGNATURE_PURPOSE_TRANSPORT_HELLO); + vcm->challenge = GNUNET_CRYPTO_random_u32 (GNUNET_CRYPTO_QUALITY_WEAK, + (unsigned int) -1); + /* Note: vcm->target is set in check_hello_validated */ + memcpy (&vcm[1], addr, addrlen); + return GNUNET_OK; +} + + +/** + * Check if addresses in validated hello "h" overlap with + * those in "chvc->hello" and update "chvc->hello" accordingly, + * removing those addresses that have already been validated. + */ +static void +check_hello_validated (void *cls, + const struct GNUNET_PeerIdentity *peer, + const struct GNUNET_HELLO_Message *h, uint32_t trust) +{ + struct CheckHelloValidatedContext *chvc = cls; + struct ValidationAddress *va; + struct TransportPlugin *tp; + int first_call; + + first_call = GNUNET_NO; + if (chvc->e == NULL) + { + first_call = GNUNET_YES; + chvc->e = GNUNET_malloc (sizeof (struct ValidationList)); + GNUNET_HELLO_get_key (h != NULL ? h : chvc->hello, &chvc->e->publicKey); + chvc->e->timeout = + GNUNET_TIME_relative_to_absolute (HELLO_VERIFICATION_TIMEOUT); + chvc->e->next = pending_validations; + pending_validations = chvc->e; + } + if (h != NULL) + { + GNUNET_HELLO_iterate_new_addresses (chvc->hello, + h, + GNUNET_TIME_absolute_get (), + &run_validation, chvc->e); + } + else if (GNUNET_YES == first_call) + { + /* no existing HELLO, all addresses are new */ + GNUNET_HELLO_iterate_addresses (chvc->hello, + GNUNET_NO, &run_validation, chvc->e); + } + if (h != NULL) + return; /* wait for next call */ + /* finally, transmit validation attempts */ + va = chvc->e->addresses; + while (va != NULL) + { + GNUNET_CRYPTO_hash (&chvc->e->publicKey, + sizeof (struct + GNUNET_CRYPTO_RsaPublicKeyBinaryEncoded), + &va->msg->target.hashPubKey); +#if DEBUG_TRANSPORT + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Establishing `%s' connection to validate `%s' of `%4s' (sending our `%s')\n", + va->transport_name, + "HELLO", GNUNET_i2s (&va->msg->target), "HELLO"); +#endif + tp = find_transport (va->transport_name); + GNUNET_assert (tp != NULL); + if (NULL == + tp->api->send_to (tp->api->cls, + &va->msg->target, + (const struct GNUNET_MessageHeader *) our_hello, + &va->msg->header, + HELLO_VERIFICATION_TIMEOUT, + &va->msg[1], va->addr_len)) + va->ok = GNUNET_SYSERR; + va = va->next; + } + if (chvc->e->next == NULL) + GNUNET_SCHEDULER_add_delayed (sched, + GNUNET_NO, + GNUNET_SCHEDULER_PRIORITY_IDLE, + GNUNET_SCHEDULER_NO_PREREQUISITE_TASK, + GNUNET_TIME_absolute_get_remaining (chvc-> + e-> + timeout), + &cleanup_validation, NULL); + GNUNET_free (chvc); +} + + +/** + * Process HELLO-message. + * + * @param plugin transport involved, may be NULL + * @param message the actual message + * @return GNUNET_OK if the HELLO was well-formed, GNUNET_SYSERR otherwise + */ +static int +process_hello (struct TransportPlugin *plugin, + const struct GNUNET_MessageHeader *message) +{ + struct ValidationList *e; + uint16_t hsize; + struct GNUNET_PeerIdentity target; + const struct GNUNET_HELLO_Message *hello; + struct CheckHelloValidatedContext *chvc; + struct GNUNET_CRYPTO_RsaPublicKeyBinaryEncoded publicKey; + + hsize = ntohs (message->size); + if ((ntohs (message->type) != GNUNET_MESSAGE_TYPE_HELLO) || + (hsize < sizeof (struct GNUNET_MessageHeader))) + { + GNUNET_break (0); + return GNUNET_SYSERR; + } + /* first, check if load is too high */ + if (GNUNET_OS_load_cpu_get (cfg) > 100) + { + /* TODO: call to stats? */ + return GNUNET_OK; + } + hello = (const struct GNUNET_HELLO_Message *) message; + if (GNUNET_OK != GNUNET_HELLO_get_key (hello, &publicKey)) + { + GNUNET_break_op (0); + return GNUNET_SYSERR; + } + GNUNET_CRYPTO_hash (&publicKey, + sizeof (struct GNUNET_CRYPTO_RsaPublicKeyBinaryEncoded), + &target.hashPubKey); +#if DEBUG_TRANSPORT + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Processing `%s' message for `%4s'\n", + "HELLO", GNUNET_i2s (&target)); +#endif + /* check if a HELLO for this peer is already on the validation list */ + e = pending_validations; + while (e != NULL) + { + if (0 == memcmp (&e->publicKey, + &publicKey, + sizeof (struct + GNUNET_CRYPTO_RsaPublicKeyBinaryEncoded))) + { + /* TODO: call to stats? */ + return GNUNET_OK; + } + e = e->next; + } + chvc = GNUNET_malloc (sizeof (struct CheckHelloValidatedContext) + hsize); + chvc->plugin = plugin; + chvc->hello = (struct GNUNET_HELLO_Message *) &chvc[1]; + memcpy (chvc->hello, hello, hsize); + /* finally, check if HELLO was previously validated + (continuation will then schedule actual validation) */ + GNUNET_PEERINFO_for_all (cfg, + sched, + &target, + 0, + HELLO_VERIFICATION_TIMEOUT, + &check_hello_validated, chvc); + return GNUNET_OK; +} + + +/** + * Handle PING-message. If the plugin that gave us the message is + * able to queue the PONG immediately, we only queue one PONG. + * Otherwise we send at most TWO PONG messages, one via an unconfirmed + * transport and one via a confirmed transport. Both addresses are + * selected randomly among those available. + * + * @param plugin plugin that gave us the message + * @param sender claimed sender of the PING + * @param plugin_context context that might be used to send response + * @param message the actual message + */ +static void +process_ping (struct TransportPlugin *plugin, + const struct GNUNET_PeerIdentity *sender, + void *plugin_context, + const struct GNUNET_MessageHeader *message) +{ + const struct ValidationChallengeMessage *vcm; + struct ValidationChallengeResponse vcr; + uint16_t msize; + struct NeighbourList *n; + +#if DEBUG_TRANSPORT + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG | GNUNET_ERROR_TYPE_BULK, + "Processing PING\n"); +#endif + msize = ntohs (message->size); + if (msize < sizeof (struct ValidationChallengeMessage)) + { + GNUNET_break_op (0); + return; + } + vcm = (const struct ValidationChallengeMessage *) message; + if (0 != memcmp (&vcm->target, + &my_identity, sizeof (struct GNUNET_PeerIdentity))) + { + GNUNET_log (GNUNET_ERROR_TYPE_WARNING, + _("Received `%s' message not destined for me!\n"), "PING"); + /* TODO: call statistics */ + return; + } + if ((ntohl (vcm->purpose.size) != + msize - sizeof (struct GNUNET_MessageHeader)) + || (ntohl (vcm->purpose.purpose) != + GNUNET_SIGNATURE_PURPOSE_TRANSPORT_HELLO)) + { + GNUNET_break_op (0); + return; + } + msize -= sizeof (struct ValidationChallengeMessage); + if (GNUNET_OK != + plugin->api->address_suggested (plugin->api->cls, &vcm[1], msize)) + { + GNUNET_break_op (0); + return; + } + vcr.header.size = htons (sizeof (struct ValidationChallengeResponse)); + vcr.header.type = htons (GNUNET_MESSAGE_TYPE_TRANSPORT_PONG); + vcr.challenge = vcm->challenge; + vcr.sender = my_identity; + GNUNET_assert (GNUNET_OK == + GNUNET_CRYPTO_rsa_sign (my_private_key, + &vcm->purpose, &vcr.signature)); +#if EXTRA_CHECKS + GNUNET_assert (GNUNET_OK == + GNUNET_CRYPTO_rsa_verify + (GNUNET_SIGNATURE_PURPOSE_TRANSPORT_HELLO, &vcm->purpose, + &vcr.signature, &my_public_key)); +#endif +#if DEBUG_TRANSPORT + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG | GNUNET_ERROR_TYPE_BULK, + "Trying to transmit PONG using inbound connection\n"); +#endif + n = find_neighbour (sender); + transmit_to_peer (NULL, &vcr.header, GNUNET_YES, n); +} + + +/** + * Handle PONG-message. + * + * @param message the actual message + */ +static void +process_pong (struct TransportPlugin *plugin, + const struct GNUNET_MessageHeader *message) +{ + const struct ValidationChallengeResponse *vcr; + struct ValidationList *pos; + struct GNUNET_PeerIdentity peer; + struct ValidationAddress *va; + int all_done; + int matched; + +#if DEBUG_TRANSPORT + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG | GNUNET_ERROR_TYPE_BULK, + "Processing PONG\n"); +#endif + vcr = (const struct ValidationChallengeResponse *) message; + pos = pending_validations; + while (pos != NULL) + { + GNUNET_CRYPTO_hash (&pos->publicKey, + sizeof (struct + GNUNET_CRYPTO_RsaPublicKeyBinaryEncoded), + &peer.hashPubKey); + if (0 == + memcmp (&peer, &vcr->sender, sizeof (struct GNUNET_PeerIdentity))) + break; + pos = pos->next; + } + if (pos == NULL) + { + /* TODO: call statistics (unmatched PONG) */ + GNUNET_log (GNUNET_ERROR_TYPE_INFO, + _ + ("Received `%s' message but have no record of a matching `%s' message. Ignoring.\n"), + "PONG", "PING"); + return; + } + all_done = GNUNET_YES; + matched = GNUNET_NO; + va = pos->addresses; + while (va != NULL) + { + if (va->msg->challenge == vcr->challenge) + { + if (GNUNET_OK != + GNUNET_CRYPTO_rsa_verify + (GNUNET_SIGNATURE_PURPOSE_TRANSPORT_HELLO, &va->msg->purpose, + &vcr->signature, &pos->publicKey)) + { + /* this could rarely happen if we used the same + challenge number for the peer for two different + transports / addresses, but the likelihood is + very small... */ + GNUNET_break_op (0); + } + else + { +#if DEBUG_TRANSPORT + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Confirmed validity of peer address.\n"); +#endif + va->ok = GNUNET_YES; + va->expiration = + GNUNET_TIME_relative_to_absolute (HELLO_ADDRESS_EXPIRATION); + matched = GNUNET_YES; + } + } + if (va->ok != GNUNET_YES) + all_done = GNUNET_NO; + va = va->next; + } + if (GNUNET_NO == matched) + { + /* TODO: call statistics (unmatched PONG) */ + GNUNET_log (GNUNET_ERROR_TYPE_INFO, + _ + ("Received `%s' message but have no record of a matching `%s' message. Ignoring.\n"), + "PONG", "PING"); + } + if (GNUNET_YES == all_done) + { + pos->timeout.value = 0; + GNUNET_SCHEDULER_add_delayed (sched, + GNUNET_NO, + GNUNET_SCHEDULER_PRIORITY_IDLE, + GNUNET_SCHEDULER_NO_PREREQUISITE_TASK, + GNUNET_TIME_UNIT_ZERO, + &cleanup_validation, NULL); + } +} + + +/** + * The peer specified by the given neighbour has timed-out. Update + * our state and do the necessary notifications. Also notifies + * our clients that the neighbour is now officially gone. + * + * @param n the neighbour list entry for the peer + */ +static void +disconnect_neighbour (struct NeighbourList *n) +{ + struct ReadyList *rpos; + struct NeighbourList *npos; + struct NeighbourList *nprev; + struct MessageQueue *mq; + +#if DEBUG_TRANSPORT + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG | GNUNET_ERROR_TYPE_BULK, + "Disconnecting from neighbour\n"); +#endif + /* remove n from neighbours list */ + nprev = NULL; + npos = neighbours; + while ((npos != NULL) && (npos != n)) + { + nprev = npos; + npos = npos->next; + } + GNUNET_assert (npos != NULL); + if (nprev == NULL) + neighbours = n->next; + else + nprev->next = n->next; + + /* notify all clients about disconnect */ + notify_clients_disconnect (&n->id); + + /* clean up all plugins, cancel connections & pending transmissions */ + while (NULL != (rpos = n->plugins)) + { + n->plugins = rpos->next; + GNUNET_assert (rpos->neighbour == n); + rpos->plugin->api->cancel (rpos->plugin->api->cls, + rpos->plugin_handle, rpos, &n->id); + GNUNET_free (rpos); + } + + /* free all messages on the queue */ + while (NULL != (mq = n->messages)) + { + n->messages = mq->next; + GNUNET_assert (mq->neighbour == n); + GNUNET_free (mq); + } + + /* finally, free n itself */ + GNUNET_free (n); +} + + +/** + * Add an entry for each of our transport plugins + * (that are able to send) to the list of plugins + * for this neighbour. + * + * @param neighbour to initialize + */ +static void +add_plugins (struct NeighbourList *neighbour) +{ + struct TransportPlugin *tp; + struct ReadyList *rl; + + neighbour->retry_plugins_time + = GNUNET_TIME_relative_to_absolute (PLUGIN_RETRY_FREQUENCY); + tp = plugins; + while (tp != NULL) + { + if (tp->api->send != NULL) + { + rl = GNUNET_malloc (sizeof (struct ReadyList)); + rl->next = neighbour->plugins; + neighbour->plugins = rl; + rl->plugin = tp; + rl->neighbour = neighbour; + rl->transmit_ready = GNUNET_YES; + } + tp = tp->next; + } +} + + +static void +neighbour_timeout_task (void *cls, + const struct GNUNET_SCHEDULER_TaskContext *tc) +{ + struct NeighbourList *n = cls; + +#if DEBUG_TRANSPORT + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG | GNUNET_ERROR_TYPE_BULK, + "Neighbour has timed out!\n"); +#endif + n->timeout_task = GNUNET_SCHEDULER_NO_PREREQUISITE_TASK; + disconnect_neighbour (n); +} + + + +/** + * Create a fresh entry in our neighbour list for the given peer. + * Will try to transmit our current HELLO to the new neighbour. Also + * notifies our clients about the new "connection". + * + * @param peer the peer for which we create the entry + * @return the new neighbour list entry + */ +static struct NeighbourList * +setup_new_neighbour (const struct GNUNET_PeerIdentity *peer) +{ + struct NeighbourList *n; + +#if DEBUG_TRANSPORT + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG | GNUNET_ERROR_TYPE_BULK, + "Setting up new neighbour `%4s', sending our HELLO to introduce ourselves\n", + GNUNET_i2s (peer)); +#endif + GNUNET_assert (our_hello != NULL); + n = GNUNET_malloc (sizeof (struct NeighbourList)); + n->next = neighbours; + neighbours = n; + n->id = *peer; + n->last_quota_update = GNUNET_TIME_absolute_get (); + n->peer_timeout = + GNUNET_TIME_relative_to_absolute (IDLE_CONNECTION_TIMEOUT); + n->quota_in = default_quota_in; + add_plugins (n); + n->hello_version_sent = our_hello_version; + n->timeout_task = GNUNET_SCHEDULER_add_delayed (sched, + GNUNET_NO, + GNUNET_SCHEDULER_PRIORITY_IDLE, + GNUNET_SCHEDULER_NO_PREREQUISITE_TASK, + IDLE_CONNECTION_TIMEOUT, + &neighbour_timeout_task, n); + transmit_to_peer (NULL, + (const struct GNUNET_MessageHeader *) our_hello, + GNUNET_YES, n); + notify_clients_connect (peer, GNUNET_TIME_UNIT_FOREVER_REL); + return n; +} + + +/** + * Function called by the plugin for each received message. + * Update data volumes, possibly notify plugins about + * reducing the rate at which they read from the socket + * and generally forward to our receive callback. + * + * @param plugin_context value to pass to this plugin + * to respond to the given peer (use is optional, + * but may speed up processing) + * @param service_context value passed to the transport-service + * to identify the neighbour; will be NULL on the first + * call for a given peer + * @param latency estimated latency for communicating with the + * given peer + * @param peer (claimed) identity of the other peer + * @param message the message, NULL if peer was disconnected + * @return the new service_context that the plugin should use + * for future receive calls for messages from this + * particular peer + */ +static struct ReadyList * +plugin_env_receive (void *cls, + void *plugin_context, + struct ReadyList *service_context, + struct GNUNET_TIME_Relative latency, + const struct GNUNET_PeerIdentity *peer, + const struct GNUNET_MessageHeader *message) +{ + const struct GNUNET_MessageHeader ack = { + htons (sizeof (struct GNUNET_MessageHeader)), + htons (GNUNET_MESSAGE_TYPE_TRANSPORT_ACK) + }; + struct TransportPlugin *plugin = cls; + struct TransportClient *cpos; + struct InboundMessage *im; + uint16_t msize; + struct NeighbourList *n; + + if (service_context != NULL) + { + n = service_context->neighbour; + GNUNET_assert (n != NULL); + } + else + { + n = find_neighbour (peer); + if (n == NULL) + { + if (message == NULL) + return NULL; /* disconnect of peer already marked down */ + n = setup_new_neighbour (peer); + } + service_context = n->plugins; + while ((service_context != NULL) && (plugin != service_context->plugin)) + service_context = service_context->next; + GNUNET_assert ((plugin->api->send == NULL) || + (service_context != NULL)); + } + if (message == NULL) + { + if ((service_context != NULL) && + (service_context->plugin_handle == plugin_context)) + { + service_context->connected = GNUNET_NO; + service_context->plugin_handle = NULL; + } + /* TODO: call stats */ + return NULL; + } +#if DEBUG_TRANSPORT + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG | GNUNET_ERROR_TYPE_BULK, + "Processing message of type `%u' received by plugin...\n", + ntohs (message->type)); +#endif + if (service_context != NULL) + { + if (service_context->connected == GNUNET_NO) + { + service_context->connected = GNUNET_YES; + service_context->transmit_ready = GNUNET_YES; + service_context->connect_attempts++; + } + service_context->timeout + = GNUNET_TIME_relative_to_absolute (IDLE_CONNECTION_TIMEOUT); + service_context->plugin_handle = plugin_context; + service_context->latency = latency; + } + /* update traffic received amount ... */ + msize = ntohs (message->size); + n->last_received += msize; + GNUNET_SCHEDULER_cancel (sched, n->timeout_task); + n->peer_timeout = + GNUNET_TIME_relative_to_absolute (IDLE_CONNECTION_TIMEOUT); + n->timeout_task = + GNUNET_SCHEDULER_add_delayed (sched, GNUNET_NO, + GNUNET_SCHEDULER_PRIORITY_IDLE, + GNUNET_SCHEDULER_NO_PREREQUISITE_TASK, + IDLE_CONNECTION_TIMEOUT, + &neighbour_timeout_task, n); + update_quota (n); + if (n->quota_violation_count > QUOTA_VIOLATION_DROP_THRESHOLD) + { + /* dropping message due to frequent inbound volume violations! */ + GNUNET_log (GNUNET_ERROR_TYPE_WARNING | + GNUNET_ERROR_TYPE_BULK, + _ + ("Dropping incoming message due to repeated bandwidth quota violations.\n")); + /* TODO: call stats */ + return service_context; + } + switch (ntohs (message->type)) + { + case GNUNET_MESSAGE_TYPE_HELLO: +#if DEBUG_TRANSPORT + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Receiving `%s' message from other peer.\n", "HELLO"); +#endif + process_hello (plugin, message); +#if DEBUG_TRANSPORT + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Sending `%s' message to connecting peer.\n", "ACK"); +#endif + transmit_to_peer (NULL, &ack, GNUNET_YES, n); + break; + case GNUNET_MESSAGE_TYPE_TRANSPORT_PING: + process_ping (plugin, peer, plugin_context, message); + break; + case GNUNET_MESSAGE_TYPE_TRANSPORT_PONG: + process_pong (plugin, message); + break; + case GNUNET_MESSAGE_TYPE_TRANSPORT_ACK: + n->saw_ack = GNUNET_YES; + /* intentional fall-through! */ + default: +#if DEBUG_TRANSPORT + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Received message of type %u from other peer, sending to all clients.\n", + ntohs (message->type)); +#endif + /* transmit message to all clients */ + im = GNUNET_malloc (sizeof (struct InboundMessage) + msize); + im->header.size = htons (sizeof (struct InboundMessage) + msize); + im->header.type = htons (GNUNET_MESSAGE_TYPE_TRANSPORT_RECV); + im->latency = GNUNET_TIME_relative_hton (latency); + im->peer = *peer; + memcpy (&im[1], message, msize); + + cpos = clients; + while (cpos != NULL) + { + transmit_to_client (cpos, &im->header, GNUNET_YES); + cpos = cpos->next; + } + GNUNET_free (im); + } + return service_context; +} + + +/** + * Handle START-message. This is the first message sent to us + * by any client which causes us to add it to our list. + * + * @param cls closure (always NULL) + * @param server the server handling the message + * @param client identification of the client + * @param message the actual message + */ +static void +handle_start (void *cls, + struct GNUNET_SERVER_Handle *server, + struct GNUNET_SERVER_Client *client, + const struct GNUNET_MessageHeader *message) +{ + struct TransportClient *c; + struct ConnectInfoMessage cim; + struct NeighbourList *n; + struct InboundMessage *im; + struct GNUNET_MessageHeader *ack; + +#if DEBUG_TRANSPORT + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Received `%s' request from client\n", "START"); +#endif + c = clients; + while (c != NULL) + { + if (c->client == client) + { + /* client already on our list! */ + GNUNET_break (0); + GNUNET_SERVER_receive_done (client, GNUNET_SYSERR); + return; + } + c = c->next; + } + c = GNUNET_malloc (sizeof (struct TransportClient)); + c->next = clients; + clients = c; + c->client = client; + if (our_hello != NULL) + { +#if DEBUG_TRANSPORT + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Sending our own HELLO to new client\n"); +#endif + transmit_to_client (c, + (const struct GNUNET_MessageHeader *) our_hello, + GNUNET_NO); + /* tell new client about all existing connections */ + cim.header.size = htons (sizeof (struct ConnectInfoMessage)); + cim.header.type = htons (GNUNET_MESSAGE_TYPE_TRANSPORT_CONNECT); + cim.quota_out = htonl (default_quota_out); + cim.latency = GNUNET_TIME_relative_hton (GNUNET_TIME_UNIT_ZERO); /* FIXME? */ + im = GNUNET_malloc (sizeof (struct InboundMessage) + + sizeof (struct GNUNET_MessageHeader)); + im->header.size = htons (sizeof (struct InboundMessage) + + sizeof (struct GNUNET_MessageHeader)); + im->header.type = htons (GNUNET_MESSAGE_TYPE_TRANSPORT_RECV); + im->latency = GNUNET_TIME_relative_hton (GNUNET_TIME_UNIT_ZERO); /* FIXME? */ + ack = (struct GNUNET_MessageHeader *) &im[1]; + ack->size = htons (sizeof (struct GNUNET_MessageHeader)); + ack->type = htons (GNUNET_MESSAGE_TYPE_TRANSPORT_ACK); + for (n = neighbours; n != NULL; n = n->next) + { + cim.id = n->id; + transmit_to_client (c, &cim.header, GNUNET_NO); + if (n->saw_ack) + { + im->peer = n->id; + transmit_to_client (c, &im->header, GNUNET_NO); + } + } + GNUNET_free (im); + } + GNUNET_SERVER_receive_done (client, GNUNET_OK); +} + + +/** + * Handle HELLO-message. + * + * @param cls closure (always NULL) + * @param server the server handling the message + * @param client identification of the client + * @param message the actual message + */ +static void +handle_hello (void *cls, + struct GNUNET_SERVER_Handle *server, + struct GNUNET_SERVER_Client *client, + const struct GNUNET_MessageHeader *message) +{ + int ret; + +#if DEBUG_TRANSPORT + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Received `%s' request from client\n", "HELLO"); +#endif + ret = process_hello (NULL, message); + GNUNET_SERVER_receive_done (client, ret); +} + + +/** + * Handle SEND-message. + * + * @param cls closure (always NULL) + * @param server the server handling the message + * @param client identification of the client + * @param message the actual message + */ +static void +handle_send (void *cls, + struct GNUNET_SERVER_Handle *server, + struct GNUNET_SERVER_Client *client, + const struct GNUNET_MessageHeader *message) +{ + struct TransportClient *tc; + struct NeighbourList *n; + const struct OutboundMessage *obm; + const struct GNUNET_MessageHeader *obmm; + uint16_t size; + uint16_t msize; + + size = ntohs (message->size); + if (size < + sizeof (struct OutboundMessage) + sizeof (struct GNUNET_MessageHeader)) + { + GNUNET_break (0); + GNUNET_SERVER_receive_done (client, GNUNET_SYSERR); + return; + } + obm = (const struct OutboundMessage *) message; +#if DEBUG_TRANSPORT + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Received `%s' request from client with target `%4s'\n", + "SEND", GNUNET_i2s (&obm->peer)); +#endif + obmm = (const struct GNUNET_MessageHeader *) &obm[1]; + msize = ntohs (obmm->size); + if (size != msize + sizeof (struct OutboundMessage)) + { + GNUNET_break (0); + GNUNET_SERVER_receive_done (client, GNUNET_SYSERR); + return; + } + n = find_neighbour (&obm->peer); + if (n == NULL) + n = setup_new_neighbour (&obm->peer); + tc = clients; + while ((tc != NULL) && (tc->client != client)) + tc = tc->next; + +#if DEBUG_TRANSPORT + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Client asked to transmit %u-byte message of type %u to `%4s'\n", + ntohs (obmm->size), + ntohs (obmm->type), GNUNET_i2s (&obm->peer)); +#endif + transmit_to_peer (tc, obmm, GNUNET_NO, n); + GNUNET_SERVER_receive_done (client, GNUNET_OK); +} + + +/** + * Handle SET_QUOTA-message. + * + * @param cls closure (always NULL) + * @param server the server handling the message + * @param client identification of the client + * @param message the actual message + */ +static void +handle_set_quota (void *cls, + struct GNUNET_SERVER_Handle *server, + struct GNUNET_SERVER_Client *client, + const struct GNUNET_MessageHeader *message) +{ + const struct QuotaSetMessage *qsm = + (const struct QuotaSetMessage *) message; + struct NeighbourList *n; + struct TransportPlugin *p; + struct ReadyList *rl; + +#if DEBUG_TRANSPORT + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Received `%s' request from client for peer `%4s'\n", + "SET_QUOTA", GNUNET_i2s (&qsm->peer)); +#endif + n = find_neighbour (&qsm->peer); + if (n == NULL) + { + GNUNET_SERVER_receive_done (client, GNUNET_OK); + return; + } + update_quota (n); + if (n->quota_in < ntohl (qsm->quota_in)) + n->last_quota_update = GNUNET_TIME_absolute_get (); + n->quota_in = ntohl (qsm->quota_in); + rl = n->plugins; + while (rl != NULL) + { + p = rl->plugin; + p->api->set_receive_quota (p->api->cls, + &qsm->peer, ntohl (qsm->quota_in)); + rl = rl->next; + } + GNUNET_SERVER_receive_done (client, GNUNET_OK); +} + + +/** + * Handle TRY_CONNECT-message. + * + * @param cls closure (always NULL) + * @param server the server handling the message + * @param client identification of the client + * @param message the actual message + */ +static void +handle_try_connect (void *cls, + struct GNUNET_SERVER_Handle *server, + struct GNUNET_SERVER_Client *client, + const struct GNUNET_MessageHeader *message) +{ + const struct TryConnectMessage *tcm; + + tcm = (const struct TryConnectMessage *) message; +#if DEBUG_TRANSPORT + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Received `%s' request from client asking to connect to `%4s'\n", + "TRY_CONNECT", GNUNET_i2s (&tcm->peer)); +#endif + if (NULL == find_neighbour (&tcm->peer)) + setup_new_neighbour (&tcm->peer); + GNUNET_SERVER_receive_done (client, GNUNET_OK); +} + + +/** + * List of handlers for the messages understood by this + * service. + */ +static struct GNUNET_SERVER_MessageHandler handlers[] = { + {&handle_start, NULL, + GNUNET_MESSAGE_TYPE_TRANSPORT_START, 0}, + {&handle_hello, NULL, + GNUNET_MESSAGE_TYPE_HELLO, 0}, + {&handle_send, NULL, + GNUNET_MESSAGE_TYPE_TRANSPORT_SEND, 0}, + {&handle_set_quota, NULL, + GNUNET_MESSAGE_TYPE_TRANSPORT_SET_QUOTA, sizeof (struct QuotaSetMessage)}, + {&handle_try_connect, NULL, + GNUNET_MESSAGE_TYPE_TRANSPORT_TRY_CONNECT, + sizeof (struct TryConnectMessage)}, + {NULL, NULL, 0, 0} +}; + + +/** + * Setup the environment for this plugin. + */ +static void +create_environment (struct TransportPlugin *plug) +{ + plug->env.cfg = cfg; + plug->env.sched = sched; + plug->env.my_public_key = &my_public_key; + plug->env.cls = plug; + plug->env.receive = &plugin_env_receive; + plug->env.lookup = &plugin_env_lookup_address; + plug->env.notify_address = &plugin_env_notify_address; + plug->env.default_quota_in = default_quota_in; + plug->env.max_connections = max_connect_per_transport; +} + + +/** + * Start the specified transport (load the plugin). + */ +static void +start_transport (struct GNUNET_SERVER_Handle *server, const char *name) +{ + struct TransportPlugin *plug; + char *libname; + + GNUNET_log (GNUNET_ERROR_TYPE_INFO, + _("Loading `%s' transport plugin\n"), name); + GNUNET_asprintf (&libname, "libgnunet_plugin_transport_%s", name); + plug = GNUNET_malloc (sizeof (struct TransportPlugin)); + create_environment (plug); + plug->short_name = GNUNET_strdup (name); + plug->lib_name = libname; + plug->next = plugins; + plugins = plug; + plug->api = GNUNET_PLUGIN_load (libname, &plug->env); + if (plug->api == NULL) + { + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + _("Failed to load transport plugin for `%s'\n"), name); + GNUNET_free (plug->short_name); + plugins = plug->next; + GNUNET_free (libname); + GNUNET_free (plug); + } +} + + +/** + * Called whenever a client is disconnected. Frees our + * resources associated with that client. + * + * @param cls closure + * @param client identification of the client + */ +static void +client_disconnect_notification (void *cls, + struct GNUNET_SERVER_Client *client) +{ + struct TransportClient *pos; + struct TransportClient *prev; + struct ClientMessageQueueEntry *mqe; + +#if DEBUG_TRANSPORT + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG | GNUNET_ERROR_TYPE_BULK, + "Client disconnected, cleaning up.\n"); +#endif + prev = NULL; + pos = clients; + while ((pos != NULL) && (pos->client != client)) + { + prev = pos; + pos = pos->next; + } + if (pos == NULL) + return; + while (NULL != (mqe = pos->message_queue_head)) + { + pos->message_queue_head = mqe->next; + GNUNET_free (mqe); + } + pos->message_queue_head = NULL; + if (prev == NULL) + clients = pos->next; + else + prev->next = pos->next; + if (GNUNET_YES == pos->tcs_pending) + { + pos->client = NULL; + return; + } + GNUNET_free (pos); +} + + +/** + * Initiate transport service. + * + * @param cls closure + * @param s scheduler to use + * @param serv the initialized server + * @param c configuration to use + */ +static void +run (void *cls, + struct GNUNET_SCHEDULER_Handle *s, + struct GNUNET_SERVER_Handle *serv, struct GNUNET_CONFIGURATION_Handle *c) +{ + char *plugs; + char *pos; + int no_transports; + unsigned long long qin; + unsigned long long qout; + unsigned long long tneigh; + char *keyfile; + + sched = s; + cfg = c; + /* parse configuration */ + if ((GNUNET_OK != + GNUNET_CONFIGURATION_get_value_number (c, + "TRANSPORT", + "DEFAULT_QUOTA_IN", + &qin)) || + (GNUNET_OK != + GNUNET_CONFIGURATION_get_value_number (c, + "TRANSPORT", + "DEFAULT_QUOTA_OUT", + &qout)) || + (GNUNET_OK != + GNUNET_CONFIGURATION_get_value_number (c, + "TRANSPORT", + "NEIGHBOUR_LIMIT", + &tneigh)) || + (GNUNET_OK != + GNUNET_CONFIGURATION_get_value_filename (c, + "GNUNETD", + "HOSTKEY", &keyfile))) + { + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + _ + ("Transport service is lacking key configuration settings. Exiting.\n")); + GNUNET_SCHEDULER_shutdown (s); + return; + } + max_connect_per_transport = (uint32_t) tneigh; + default_quota_in = (uint32_t) qin; + default_quota_out = (uint32_t) qout; + my_private_key = GNUNET_CRYPTO_rsa_key_create_from_file (keyfile); + GNUNET_free (keyfile); + if (my_private_key == NULL) + { + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + _ + ("Transport service could not access hostkey. Exiting.\n")); + GNUNET_SCHEDULER_shutdown (s); + return; + } + GNUNET_CRYPTO_rsa_key_get_public (my_private_key, &my_public_key); + GNUNET_CRYPTO_hash (&my_public_key, + sizeof (my_public_key), &my_identity.hashPubKey); + /* setup notification */ + server = serv; + GNUNET_SERVER_disconnect_notify (server, + &client_disconnect_notification, NULL); + /* load plugins... */ + no_transports = 1; + if (GNUNET_OK == + GNUNET_CONFIGURATION_get_value_string (c, + "TRANSPORT", "PLUGINS", &plugs)) + { + GNUNET_log (GNUNET_ERROR_TYPE_INFO, + _("Starting transport plugins `%s'\n"), plugs); + pos = strtok (plugs, " "); + while (pos != NULL) + { + start_transport (server, pos); + no_transports = 0; + pos = strtok (NULL, " "); + } + GNUNET_free (plugs); + } + if (no_transports) + refresh_hello (); + GNUNET_log (GNUNET_ERROR_TYPE_INFO, _("Transport service ready.\n")); + /* process client requests */ + GNUNET_SERVER_add_handlers (server, handlers); +} + + +/** + * Function called when the service shuts + * down. Unloads our plugins. + * + * @param cls closure + * @param cfg configuration to use + */ +static void +unload_plugins (void *cls, struct GNUNET_CONFIGURATION_Handle *cfg) +{ + struct TransportPlugin *plug; + struct AddressList *al; + +#if DEBUG_TRANSPORT + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Transport service is unloading plugins...\n"); +#endif + while (NULL != (plug = plugins)) + { + plugins = plug->next; + GNUNET_break (NULL == GNUNET_PLUGIN_unload (plug->lib_name, plug->api)); + GNUNET_free (plug->lib_name); + GNUNET_free (plug->short_name); + while (NULL != (al = plug->addresses)) + { + plug->addresses = al->next; + GNUNET_free (al); + } + GNUNET_free (plug); + } + if (my_private_key != NULL) + GNUNET_CRYPTO_rsa_key_free (my_private_key); +} + + +/** + * The main function for the transport service. + * + * @param argc number of arguments from the command line + * @param argv command line arguments + * @return 0 ok, 1 on error + */ +int +main (int argc, char *const *argv) +{ + return (GNUNET_OK == + GNUNET_SERVICE_run (argc, + argv, + "transport", + &run, NULL, &unload_plugins, NULL)) ? 0 : 1; +} + +/* end of gnunet-service-transport.c */ diff --git a/src/transport/gnunet-transport.c b/src/transport/gnunet-transport.c new file mode 100644 index 000000000..52475e3eb --- /dev/null +++ b/src/transport/gnunet-transport.c @@ -0,0 +1,42 @@ +/* + This file is part of GNUnet. + (C) 2009 Christian Grothoff (and other contributing authors) + + GNUnet is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published + by the Free Software Foundation; either version 2, 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 + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with GNUnet; see the file COPYING. If not, write to the + Free Software Foundation, Inc., 59 Temple Place - Suite 330, + Boston, MA 02111-1307, USA. +*/ + +/** + * @file server/gnunet-transport.c + * @brief Tool to help configure the transports. + * @author Christian Grothoff + * + * This utility can be used to test if a transport mechanism for + * GNUnet is properly configured. + */ + +#include "platform.h" +#include "gnunet_program_lib.h" +#include "gnunet_protocols.h" +#include "gnunet_transport_service.h" + +int +main (int argc, char *const *argv) +{ + return 0; +} + + +/* end of gnunet-transport.c */ diff --git a/src/transport/plugin_transport.h b/src/transport/plugin_transport.h new file mode 100644 index 000000000..57df9affe --- /dev/null +++ b/src/transport/plugin_transport.h @@ -0,0 +1,468 @@ +/* + This file is part of GNUnet + (C) 2009 Christian Grothoff (and other contributing authors) + + GNUnet is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published + by the Free Software Foundation; either version 2, 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 + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with GNUnet; see the file COPYING. If not, write to the + Free Software Foundation, Inc., 59 Temple Place - Suite 330, + Boston, MA 02111-1307, USA. +*/ + +/** + * @file transport/plugin_transport.h + * @brief API for the transport services. This header + * specifies the struct that is given to the plugin's entry + * method and the other struct that must be returned. + * Note that the destructors of transport plugins will + * be given the value returned by the constructor + * and is expected to return a NULL pointer. + * + * TODO: + * - consider moving DATA message (latency measurement) + * to service; avoids encapsulation overheads and + * would enable latency measurements for non-bidi + * transports. + * - + * + * @author Christian Grothoff + */ +#ifndef PLUGIN_TRANSPORT_H +#define PLUGIN_TRANSPORT_H + +#include "gnunet_configuration_lib.h" +#include "gnunet_scheduler_lib.h" +#include "gnunet_transport_service.h" + +/** + * Opaque internal context for a particular peer of the transport + * service. Plugins will be given a pointer to this type and, if + * cheaply possible, should pass this pointer back to the transport + * service whenever additional messages from the same peer are + * received. + */ +struct ReadyList; + +/** + * Function called by the transport for each received message. + * This function should also be called with "NULL" for the + * message to signal that the other peer disconnected. + * + * @param cls closure + * @param plugin_context value to pass to this plugin + * to respond to the given peer (use is optional, + * but may speed up processing) + * @param service_context value passed to the transport-service + * to identify the neighbour; will be NULL on the first + * call for a given peer + * @param latency estimated latency for communicating with the + * given peer; should be set to GNUNET_TIME_UNIT_FOREVER_REL + * until the transport has seen messages transmitted in + * BOTH directions (and hence been able to do an actual + * round-trip observation); a non-FOREVER latency is also used + * by the transport to know that communication in both directions + * using this one plugin actually works + * @param peer (claimed) identity of the other peer + * @param message the message, NULL if peer was disconnected + * @return the new service_context that the plugin should use + * for future receive calls for messages from this + * particular peer + */ +typedef struct ReadyList * + (*GNUNET_TRANSPORT_PluginReceiveCallback) (void *cls, + void *plugin_context, + struct ReadyList * + service_context, + struct GNUNET_TIME_Relative + latency, + const struct GNUNET_PeerIdentity + * peer, + const struct GNUNET_MessageHeader + * message); + + +/** + * Function that will be called for each address the transport + * is aware that it might be reachable under. + * + * @param cls closure + * @param name name of the transport that generated the address + * @param addr one of the addresses of the host, NULL for the last address + * the specific address format depends on the transport + * @param addrlen length of the address + * @param expires when should this address automatically expire? + */ +typedef void (*GNUNET_TRANSPORT_AddressNotification) (void *cls, + const char *name, + const void *addr, + size_t addrlen, + struct + GNUNET_TIME_Relative + expires); + + +/** + * Function that will be called for each address obtained from the HELLO. + * + * @param cls closure + * @param name name of the transport that generated the address + * @param addr one of the addresses of the host, NULL for the last address + * the specific address format depends on the transport + * @param addrlen length of the address + */ +typedef void (*GNUNET_TRANSPORT_AddressCallback) (void *cls, + const char *name, + const void *addr, + size_t addrlen); + + +/** + * Function that allows a transport to query the known + * network addresses for a given peer. + * + * @param cls closure + * @param timeout after how long should we time out? + * @param target which peer are we looking for? + * @param iter function to call for each known address + * @param iter_cls closure for iter + */ +typedef void (*GNUNET_TRANSPORT_LookupAddress) (void *cls, + struct GNUNET_TIME_Relative + timeout, + const struct + GNUNET_PeerIdentity * target, + GNUNET_TRANSPORT_AddressCallback + iter, void *iter_cls); + + +/** + * The transport service will pass a pointer to a struct + * of this type as the first and only argument to the + * entry point of each transport plugin. + */ +struct GNUNET_TRANSPORT_PluginEnvironment +{ + /** + * Configuration to use. + */ + struct GNUNET_CONFIGURATION_Handle *cfg; + + /** + * Scheduler to use. + */ + struct GNUNET_SCHEDULER_Handle *sched; + + /** + * Our public key. + */ + struct GNUNET_CRYPTO_RsaPublicKeyBinaryEncoded *my_public_key; + + /** + * Closure for the various callbacks. + */ + void *cls; + + /** + * Function that should be called by the transport plugin + * whenever a message is received. + */ + GNUNET_TRANSPORT_PluginReceiveCallback receive; + + /** + * Address lookup function. + */ + GNUNET_TRANSPORT_LookupAddress lookup; + + /** + * Function that must be called by each plugin to notify the + * transport service about the addresses under which the transport + * provided by the plugin can be reached. + */ + GNUNET_TRANSPORT_AddressNotification notify_address; + + /** + * What is the default quota (in terms of incoming bytes per + * ms) for new connections? + */ + uint32_t default_quota_in; + + /** + * What is the maximum number of connections that this transport + * should allow? Transports that do not have sessions (such as + * UDP) can ignore this value. + */ + uint32_t max_connections; + +}; + + +/** + * Function that can be used by the transport service to transmit + * a message using the plugin using a fresh connection (even if + * we already have a connection to this peer, this function is + * required to establish a new one). + * + * @param cls closure + * @param target who should receive this message + * @param msg1 first message to transmit + * @param msg2 second message to transmit (can be NULL) + * @param timeout how long should we try to transmit these? + * @param addrlen length of the address + * @param addr the address + * @return session instance if the transmission has been scheduled + * NULL if the address format is invalid + */ +typedef void * + (*GNUNET_TRANSPORT_TransmitToAddressFunction) (void *cls, + const struct + GNUNET_PeerIdentity * target, + const struct + GNUNET_MessageHeader * msg1, + const struct + GNUNET_MessageHeader * msg2, + struct GNUNET_TIME_Relative + timeout, const void *addr, + size_t addrlen); + +/** + * Function called by the GNUNET_TRANSPORT_TransmitFunction + * upon "completion". + * + * @param cls closure + * @param service_context value passed to the transport-service + * to identify the neighbour + * @param target who was the recipient of the message? + * @param result GNUNET_OK on success + * GNUNET_SYSERR if the target disconnected; + * disconnect will ALSO be signalled using + * the ReceiveCallback. + */ +typedef void + (*GNUNET_TRANSPORT_TransmitContinuation) (void *cls, + struct ReadyList * + service_context, + const struct GNUNET_PeerIdentity * + target, int result); + +/** + * Function that can be used by the transport service to transmit + * a message using the plugin. Note that in the case of a + * peer disconnecting, the continuation MUST be called + * prior to the disconnect notification itself. This function + * will be called with this peer's HELLO message to initiate + * a fresh connection to another peer. + * + * @param cls closure + * @param plugin_context value we were asked to pass to this plugin + * to respond to the given peer (use is optional, + * but may speed up processing), can be NULL + * @param service_context value passed to the transport-service + * to identify the neighbour; NULL is used to indicate + * an urgent message. If the urgent message can not be + * scheduled for immediate transmission, the plugin is to + * call the continuation with failure immediately + * @param target who should receive this message + * @param msg the message to transmit + * @param timeout how long to wait at most for the transmission + * @param cont continuation to call once the message has + * been transmitted (or if the transport is ready + * for the next transmission call; or if the + * peer disconnected...); can be NULL + * @param cont_cls closure for cont + * @return plugin_context that should be used next time for + * sending messages to the specified peer + */ +typedef void * + (*GNUNET_TRANSPORT_TransmitFunction) (void *cls, + void *plugin_context, + struct ReadyList * service_context, + const struct GNUNET_PeerIdentity * + target, + const struct GNUNET_MessageHeader * + msg, + struct GNUNET_TIME_Relative timeout, + GNUNET_TRANSPORT_TransmitContinuation + cont, void *cont_cls); + + +/** + * Function that can be called to force a disconnect from the + * specified neighbour. This should also cancel all previously + * scheduled transmissions. Obviously the transmission may have been + * partially completed already, which is OK. The plugin is supposed + * to close the connection (if applicable) and no longer call the + * transmit continuation(s). + * + * Finally, plugin MUST NOT call the services's receive function to + * notify the service that the connection to the specified target was + * closed after a getting this call. + * + * @param cls closure + * @param plugin_context value we were asked to pass to this plugin + * to respond to the given peer (use is optional, + * but may speed up processing), can be NULL (if + * NULL was returned from the transmit function) + * @param service_context must correspond to the service context + * of the corresponding Transmit call; the plugin should + * not cancel a send call made with a different service + * context pointer! Never NULL. + * @param target peer for which the last transmission is + * to be cancelled + */ +typedef void + (*GNUNET_TRANSPORT_CancelFunction) (void *cls, + void *plugin_context, + struct ReadyList * service_context, + const struct GNUNET_PeerIdentity * + target); + + +/** + * Function called by the pretty printer for the resolved address for + * each human-readable address obtained. + * + * @param cls closure + * @param hostname one of the names for the host, NULL + * on the last call to the callback + */ +typedef void (*GNUNET_TRANSPORT_AddressStringCallback) (void *cls, + const char *address); + + +/** + * Convert the transports address to a nice, human-readable + * format. + * + * @param cls closure + * @param name name of the transport that generated the address + * @param addr one of the addresses of the host, NULL for the last address + * the specific address format depends on the transport + * @param addrlen length of the address + * @param numeric should (IP) addresses be displayed in numeric form? + * @param timeout after how long should we give up? + * @param asc function to call on each string + * @param asc_cls closure for asc + */ +typedef void + (*GNUNET_TRANSPORT_AddressPrettyPrinter) (void *cls, + const char *type, + const void *addr, + size_t addrlen, + int numeric, + struct GNUNET_TIME_Relative + timeout, + GNUNET_TRANSPORT_AddressStringCallback + asc, void *asc_cls); + + +/** + * Set a quota for receiving data from the given peer; this is a + * per-transport limit. The transport should limit its read/select + * calls to stay below the quota (in terms of incoming data). + * + * @param cls closure + * @param peer the peer for whom the quota is given + * @param quota_in quota for receiving/sending data in bytes per ms + */ +typedef void + (*GNUNET_TRANSPORT_SetQuota) (void *cls, + const struct GNUNET_PeerIdentity * target, + uint32_t quota_in); + + +/** + * Another peer has suggested an address for this + * peer and transport plugin. Check that this could be a valid + * address. If so, consider adding it to the list + * of addresses. + * + * @param addr pointer to the address + * @param addrlen length of addr + * @return GNUNET_OK if this is a plausible address for this peer + * and transport + */ +typedef int + (*GNUNET_TRANSPORT_SuggestAddress) (void *cls, + const void *addr, size_t addrlen); + +/** + * Each plugin is required to return a pointer to a struct of this + * type as the return value from its entry point. + */ +struct GNUNET_TRANSPORT_PluginFunctions +{ + + /** + * Closure for all of the callbacks. + */ + void *cls; + + /** + * Function used to send a single message to a particular + * peer using the specified address. Used to validate + * HELLOs. + */ + GNUNET_TRANSPORT_TransmitToAddressFunction send_to; + + /** + * Function that the transport service will use to transmit data to + * another peer. May be null for plugins that only support + * receiving data. After this call, the plugin call the specified + * continuation with success or error before notifying us about the + * target having disconnected. + */ + GNUNET_TRANSPORT_TransmitFunction send; + + /** + * Function that can be used to force the plugin to disconnect + * from the given peer and cancel all previous transmissions + * (and their continuationc). + */ + GNUNET_TRANSPORT_CancelFunction cancel; + + /** + * Function to pretty-print addresses. NOTE: this function is not + * yet used by transport-service, but will be used in the future + * once the transport-API has been completed. + */ + GNUNET_TRANSPORT_AddressPrettyPrinter address_pretty_printer; + + /** + * Function that the transport service can use to try to enforce a + * quota for the number of bytes received via this transport. + * Transports that can not refuse incoming data (such as UDP) + * are free to ignore these calls. + */ + GNUNET_TRANSPORT_SetQuota set_receive_quota; + + /** + * Function that will be called if another peer suggested that + * we should use a particular address (since he is reaching + * us at that address) for this transport. + */ + GNUNET_TRANSPORT_SuggestAddress address_suggested; + + /** + * Relative cost of this transport compared to others. This + * is supposed to be a static cost estimate which determines + * which plugins should not even be attempted if other, + * cheaper transports are already working. The idea is that + * the costs have roughly this relationship: + *
+   * TCP < UDP < HTTP == HTTPS < SMTP
+   * 
+ */ + unsigned int cost_estimate; +}; + + +#endif diff --git a/src/transport/plugin_transport_http.c b/src/transport/plugin_transport_http.c new file mode 100644 index 000000000..eb69f4386 --- /dev/null +++ b/src/transport/plugin_transport_http.c @@ -0,0 +1,2085 @@ +/* + This file is part of GNUnet + (C) 2003, 2004, 2005, 2006, 2007, 2008 Christian Grothoff (and other contributing authors) + + GNUnet is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published + by the Free Software Foundation; either version 2, 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 + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with GNUnet; see the file COPYING. If not, write to the + Free Software Foundation, Inc., 59 Temple Place - Suite 330, + Boston, MA 02111-1307, USA. +*/ + +/** + * @file transports/http.c + * @brief Implementation of the HTTP transport service + * @author Christian Grothoff + */ + +#include "platform.h" +#include "gnunet_util.h" +#include "gnunet_protocols.h" +#include "gnunet_transport.h" +#include "gnunet_stats_service.h" +#include "gnunet_upnp_service.h" +#include +#include +#include +#include "ip.h" + +#define DEBUG_HTTP GNUNET_NO + +/** + * Disable GET (for debugging only!). Must be GNUNET_YES + * in production use! + */ +#define DO_GET GNUNET_YES + +/** + * After how much time of the core not being associated with a http + * connection anymore do we close it? + * + * Needs to be larger than SECONDS_INACTIVE_DROP in + * core's connection.s + */ +#define HTTP_TIMEOUT (600 * GNUNET_CRON_SECONDS) + +/** + * How often do we re-issue GET requests? + */ +#define HTTP_GET_REFRESH (5 * GNUNET_CRON_SECONDS) + +/** + * Default maximum size of the HTTP read and write buffer. + */ +#define HTTP_BUF_SIZE (64 * 1024) + +/** + * Text of the response sent back after the last bytes of a PUT + * request have been received (just to formally obey the HTTP + * protocol). + */ +#define HTTP_PUT_RESPONSE "Thank you!" + +#define MY_TRANSPORT_NAME "HTTP" +#include "common.c" + +/** + * Client-side data per PUT request. + */ +struct HTTPPutData +{ + /** + * This is a linked list. + */ + struct HTTPPutData *next; + + /** + * Handle to our CURL request. + */ + CURL *curl_put; + + /** + * Last time we made progress with the PUT. + */ + GNUNET_CronTime last_activity; + + /** + * The message we are sending. + */ + char *msg; + + /** + * Size of msg. + */ + unsigned int size; + + /** + * Current position in msg. + */ + unsigned int pos; + + /** + * Are we done sending? Set to 1 after we + * completed sending and started to receive + * a response ("Thank you!") or once the + * timeout has been reached. + */ + int done; + +}; + +/** + * Server-side data per PUT request. + */ +struct MHDPutData +{ + /** + * This is a linked list. + */ + struct MHDPutData *next; + + /** + * MHD connection handle for this request. + */ + struct MHD_Connection *session; + + /** + * Last time we received data on this PUT + * connection. + */ + GNUNET_CronTime last_activity; + + /** + * Read buffer for the header (from PUT) + */ + char rbuff1[sizeof (GNUNET_MessageHeader)]; + + /** + * The read buffer (used only receiving PUT data). + */ + char *rbuff2; + + /** + * Number of valid bytes in rbuff1 + */ + unsigned int rpos1; + + /** + * Number of valid bytes in rbuff2 + */ + unsigned int rpos2; + + + /** + * Size of the rbuff2 buffer. + */ + unsigned int rsize2; + + /** + * Should we sent a response for this PUT yet? + */ + int ready; + + /** + * Have we sent a response for this PUT yet? + */ + int done; + +}; + +/** + * Server-side data for a GET request. + */ +struct MHDGetData +{ + + /** + * This is a linked list. + */ + struct MHDGetData *next; + + /** + * MHD connection handle for this request. + */ + struct MHD_Connection *session; + + /** + * GET session response handle + */ + struct MHD_Response *get; + + /** + * My HTTP session. + */ + struct HTTPSession *httpsession; + + /** + * The write buffer (for sending GET response) + */ + char *wbuff; + + /** + * What was the last time we were able to + * transmit data using the current get handle? + */ + GNUNET_CronTime last_get_activity; + + /** + * Current write position in wbuff + */ + unsigned int woff; + + /** + * Number of valid bytes in wbuff (starting at woff) + */ + unsigned int wpos; + + /** + * Size of the write buffer. + */ + unsigned int wsize; + +}; + +/** + * Transport Session handle. + */ +typedef struct HTTPSession +{ + + /** + * GNUNET_TSession for this session. + */ + GNUNET_TSession *tsession; + + /** + * To whom are we talking to. + */ + GNUNET_PeerIdentity sender; + + /** + * number of users of this session + */ + unsigned int users; + + /** + * Has this session been destroyed? + */ + int destroyed; + + /** + * Are we client or server? Determines which of the + * structs in the union below is being used for this + * connection! + */ + int is_client; + + /** + * Is MHD still using this session handle? + */ + int is_mhd_active; + + /** + * Data maintained for the http client-server connection + * (depends on if we are client or server). + */ + union + { + + struct + { + /** + * Active PUT requests (linked list). + */ + struct MHDPutData *puts; + +#if DO_GET + /** + * Active GET requests (linked list; most + * recent received GET is the head of the list). + */ + struct MHDGetData *gets; +#endif + + } server; + + struct + { + + /** + * Address of the other peer. + */ + HostAddress address; + +#if DO_GET + /** + * Last time the GET was active. + */ + GNUNET_CronTime last_get_activity; + + /** + * What was the last time we were able to + * transmit data using the current get handle? + */ + GNUNET_CronTime last_get_initiated; + + /** + * GET operation + */ + CURL *get; + + /** + * Read buffer for the header (from GET). + */ + char rbuff1[sizeof (GNUNET_MessageHeader)]; + + /** + * The read buffer (used only receiving GET data). + */ + char *rbuff2; + + /** + * Number of valid bytes in rbuff1 + */ + unsigned int rpos1; + + /** + * Number of valid bytes in rbuff2 + */ + unsigned int rpos2; + + /** + * Current size of the read buffer rbuff2. + */ + unsigned int rsize2; +#endif + + /** + * URL of the get and put operations. + */ + char *url; + + /** + * Linked list of PUT operations. + */ + struct HTTPPutData *puts; + + } client; + + } cs; + +} HTTPSession; + +/* *********** globals ************* */ + +static int stat_bytesReceived; + +static int stat_bytesSent; + +static int stat_bytesDropped; + +static int stat_get_issued; + +static int stat_get_received; + +static int stat_put_issued; + +static int stat_put_received; + +static int stat_select_calls; + +static int stat_send_calls; + +static int stat_connect_calls; + +static int stat_curl_send_callbacks; + +static int stat_curl_receive_callbacks; + +static int stat_mhd_access_callbacks; + +static int stat_mhd_read_callbacks; + +static int stat_mhd_close_callbacks; + +static int stat_connect_calls; + +/** + * How many requests do we have currently pending + * (with libcurl)? + */ +static unsigned int http_requests_pending; + +static int signal_pipe[2]; + +static char *proxy; + +/** + * Daemon for listening for new connections. + */ +static struct MHD_Daemon *mhd_daemon; + +/** + * Curl multi for managing client operations. + */ +static CURLM *curl_multi; + +/** + * Set to GNUNET_YES while the transport is running. + */ +static int http_running; + +/** + * Thread running libcurl activities. + */ +static struct GNUNET_ThreadHandle *curl_thread; + +/** + * Array of currently active HTTP sessions. + */ +static GNUNET_TSession **tsessions; + +/** + * Number of valid entries in tsessions. + */ +static unsigned int tsessionCount; + +/** + * Sie of the tsessions array. + */ +static unsigned int tsessionArrayLength; + +/** + * Lock for concurrent access to all structures used + * by http, including CURL. + */ +static struct GNUNET_Mutex *lock; + + +/** + * Signal select thread that its selector + * set may have changed. + */ +static void +signal_select () +{ + static char c; + WRITE (signal_pipe[1], &c, sizeof (c)); +} + +/** + * Check if we are allowed to connect to the given IP. + */ +static int +acceptPolicyCallback (void *cls, + const struct sockaddr *addr, socklen_t addr_len) +{ + if (GNUNET_NO != is_rejected_tester (addr, addr_len)) + return MHD_NO; + return MHD_YES; +} + +/** + * Disconnect from a remote node. May only be called + * on sessions that were acquired by the caller first. + * For the core, aquiration means to call associate or + * connect. The number of disconnects must match the + * number of calls to connect+associate. + * + * Sessions are actually discarded in cleanup_connections. + * + * + * @param tsession the session that is closed + * @return GNUNET_OK on success, GNUNET_SYSERR if the operation failed + */ +static int +httpDisconnect (GNUNET_TSession * tsession) +{ + HTTPSession *httpsession = tsession->internal; + if (httpsession == NULL) + { + GNUNET_free (tsession); + return GNUNET_OK; + } + GNUNET_mutex_lock (lock); + httpsession->users--; + GNUNET_mutex_unlock (lock); + return GNUNET_OK; +} + +static void +destroy_tsession (GNUNET_TSession * tsession) +{ + HTTPSession *httpsession = tsession->internal; + struct HTTPPutData *pos; + struct HTTPPutData *next; +#if DO_GET + struct MHDGetData *gpos; + struct MHDGetData *gnext; +#endif + struct MHD_Response *r; + int i; + + GNUNET_mutex_lock (lock); + for (i = 0; i < tsessionCount; i++) + { + if (tsessions[i] == tsession) + { + tsessions[i] = tsessions[--tsessionCount]; + break; + } + } + if (httpsession->is_client) + { +#if DO_GET + curl_multi_remove_handle (curl_multi, httpsession->cs.client.get); + http_requests_pending--; + signal_select (); + curl_easy_cleanup (httpsession->cs.client.get); + GNUNET_array_grow (httpsession->cs.client.rbuff2, + httpsession->cs.client.rsize2, 0); +#endif + GNUNET_free_non_null (httpsession->cs.client.url); + pos = httpsession->cs.client.puts; + while (pos != NULL) + { + next = pos->next; + curl_multi_remove_handle (curl_multi, pos->curl_put); + http_requests_pending--; + signal_select (); + curl_easy_cleanup (pos->curl_put); + GNUNET_free (pos->msg); + GNUNET_free (pos); + pos = next; + } + GNUNET_free (httpsession); + GNUNET_free (tsession); + } + else + { + httpsession->destroyed = GNUNET_YES; + GNUNET_GE_BREAK (NULL, httpsession->cs.server.puts == NULL); +#if DO_GET + gpos = httpsession->cs.server.gets; + while (gpos != NULL) + { + GNUNET_array_grow (gpos->wbuff, gpos->wsize, 0); + r = gpos->get; + gpos->get = NULL; + gnext = gpos->next; + MHD_destroy_response (r); + gpos = gnext; + } + httpsession->cs.server.gets = NULL; +#endif + GNUNET_free (httpsession->tsession); + GNUNET_free (httpsession); + } + GNUNET_mutex_unlock (lock); +} + +/** + * MHD is done handling a request. Cleanup + * the respective transport state. + */ +static void +requestCompletedCallback (void *unused, + struct MHD_Connection *session, + void **httpSessionCache) +{ + HTTPSession *httpsession = *httpSessionCache; + struct MHDPutData *pprev; + struct MHDPutData *ppos; +#if DO_GET + struct MHDGetData *gprev; + struct MHDGetData *gpos; +#endif + + if (stats != NULL) + stats->change (stat_mhd_close_callbacks, 1); + if (httpsession == NULL) + return; /* oops */ + GNUNET_GE_ASSERT (NULL, !httpsession->is_client); + pprev = NULL; + ppos = httpsession->cs.server.puts; + while (ppos != NULL) + { + if (ppos->session == session) + { + ppos->last_activity = 0; + signal_select (); + return; + } + pprev = ppos; + ppos = ppos->next; + } +#if DO_GET + gprev = NULL; + gpos = httpsession->cs.server.gets; + while (gpos != NULL) + { + if (gpos->session == session) + { + gpos->last_get_activity = 0; + signal_select (); + return; + } + gprev = gpos; + gpos = gpos->next; + } +#endif + httpsession->is_mhd_active--; +} + +/** + * A (core) Session is to be associated with a transport session. The + * transport service may want to know in order to call back on the + * core if the connection is being closed. Associate can also be + * called to test if it would be possible to associate the session + * later, in this case the argument session is NULL. This can be used + * to test if the connection must be closed by the core or if the core + * can assume that it is going to be self-managed (if associate + * returns GNUNET_OK and session was NULL, the transport layer is responsible + * for eventually freeing resources associated with the tesession). If + * session is not NULL, the core takes responsbility for eventually + * calling disconnect. + * + * @param tsession the session handle passed along + * from the call to receive that was made by the transport + * layer + * @return GNUNET_OK if the session could be associated, + * GNUNET_SYSERR if not. + */ +static int +httpAssociate (GNUNET_TSession * tsession) +{ + HTTPSession *httpSession; + + if (tsession == NULL) + { + GNUNET_GE_BREAK (NULL, 0); + return GNUNET_SYSERR; + } + httpSession = tsession->internal; + GNUNET_mutex_lock (lock); + if (httpSession->destroyed == GNUNET_YES) + { + GNUNET_mutex_unlock (lock); + return GNUNET_SYSERR; + } + httpSession->users++; + GNUNET_mutex_unlock (lock); + return GNUNET_OK; +} + +/** + * Add a new session to the array watched by the select thread. Grows + * the array if needed. If the caller wants to do anything useful + * with the return value, it must have the lock before + * calling. It is ok to call this function without holding lock if + * the return value is ignored. + */ +static unsigned int +addTSession (GNUNET_TSession * tsession) +{ + unsigned int i; + + GNUNET_mutex_lock (lock); + if (tsessionCount == tsessionArrayLength) + GNUNET_array_grow (tsessions, tsessionArrayLength, + tsessionArrayLength * 2); + i = tsessionCount; + tsessions[tsessionCount++] = tsession; + GNUNET_mutex_unlock (lock); + return i; +} + +#if DO_GET +/** + * Callback for processing GET requests if our side is the + * MHD HTTP server. + * + * @param cls the HTTP session + * @param pos read-offset in the stream + * @param buf where to write the data + * @param max how much data to write (at most) + * @return number of bytes written, 0 is allowed! + */ +static int +contentReaderCallback (void *cls, uint64_t pos, char *buf, int max) +{ + struct MHDGetData *mgd = cls; + + if (stats != NULL) + stats->change (stat_mhd_read_callbacks, 1); + GNUNET_mutex_lock (lock); + if (mgd->wpos < max) + max = mgd->wpos; + memcpy (buf, &mgd->wbuff[mgd->woff], max); + mgd->wpos -= max; + mgd->woff += max; + if (max > 0) + mgd->last_get_activity = GNUNET_get_time (); + if (mgd->wpos == 0) + mgd->woff = 0; + GNUNET_mutex_unlock (lock); +#if DEBUG_HTTP + GNUNET_GE_LOG (coreAPI->ectx, + GNUNET_GE_DEBUG | GNUNET_GE_REQUEST | GNUNET_GE_USER, + "HTTP returns %u bytes in MHD's GET handler.\n", max); +#endif + if (stats != NULL) + stats->change (stat_bytesSent, max); + if ((max == 0) && (mgd->httpsession->cs.server.gets != mgd)) + return -1; /* end of response (another GET replaces this one) */ + return max; +} +#endif + +#if DO_GET +/** + * Notification that libmicrohttpd no longer needs the + * response object. + */ +static void +contentReaderFreeCallback (void *cls) +{ + struct MHDGetData *mgd = cls; + + GNUNET_GE_ASSERT (NULL, mgd->get == NULL); + GNUNET_array_grow (mgd->wbuff, mgd->wsize, 0); + GNUNET_free (mgd); +} +#endif + +/** + * Process GET or PUT request received via MHD. For + * GET, queue response that will send back our pending + * messages. For PUT, process incoming data and send + * to GNUnet core. In either case, check if a session + * already exists and create a new one if not. + */ +static int +accessHandlerCallback (void *cls, + struct MHD_Connection *session, + const char *url, + const char *method, + const char *version, + const char *upload_data, + size_t * upload_data_size, void **httpSessionCache) +{ + GNUNET_TSession *tsession; + struct MHDPutData *put; + struct MHDGetData *get; + HTTPSession *httpSession; + struct MHD_Response *response; + GNUNET_HashCode client; + int i; + unsigned int have; + GNUNET_MessageHeader *hdr; + GNUNET_TransportPacket *mp; + unsigned int cpy; + unsigned int poff; + + if (stats != NULL) + stats->change (stat_mhd_access_callbacks, 1); +#if DEBUG_HTTP + GNUNET_GE_LOG (coreAPI->ectx, + GNUNET_GE_DEBUG | GNUNET_GE_REQUEST | GNUNET_GE_USER, + "HTTP/MHD receives `%s' request.\n", method); +#endif + /* convert URL to sender peer id */ + if ((strlen (url) < 2) + || (GNUNET_OK != GNUNET_enc_to_hash (&url[1], &client))) + { + /* invalid request */ + /* GNUNET_GE_BREAK_OP (NULL, 0); -- this happens a lot, most likely + somebody scanning for MyDoom.X-opened backdoors */ + return MHD_NO; + } + + /* check if we already have a session for this */ + httpSession = *httpSessionCache; + if (httpSession == NULL) + { + /* new http connection */ + if (stats != NULL) + { + if (0 == strcasecmp (MHD_HTTP_METHOD_PUT, method)) + stats->change (stat_put_received, 1); + else + stats->change (stat_get_received, 1); + } + GNUNET_mutex_lock (lock); + for (i = 0; i < tsessionCount; i++) + { + tsession = tsessions[i]; + httpSession = tsession->internal; + if ((0 == + memcmp (&httpSession->sender, &client, + sizeof (GNUNET_HashCode))) + && (httpSession->is_client == GNUNET_NO)) + break; + tsession = NULL; + httpSession = NULL; + } + GNUNET_mutex_unlock (lock); + } + /* create new session if necessary */ + if (httpSession == NULL) + { +#if DEBUG_HTTP + GNUNET_GE_LOG (coreAPI->ectx, + GNUNET_GE_DEBUG | GNUNET_GE_REQUEST | GNUNET_GE_USER, + "HTTP/MHD creates new session for request from `%s'.\n", + &url[1]); +#endif + httpSession = GNUNET_malloc (sizeof (HTTPSession)); + memset (httpSession, 0, sizeof (HTTPSession)); + httpSession->sender.hashPubKey = client; + httpSession->users = 0; /* MHD */ + tsession = GNUNET_malloc (sizeof (GNUNET_TSession)); + memset (tsession, 0, sizeof (GNUNET_TSession)); + tsession->ttype = GNUNET_TRANSPORT_PROTOCOL_NUMBER_HTTP; + tsession->internal = httpSession; + tsession->peer.hashPubKey = client; + httpSession->tsession = tsession; + addTSession (tsession); + } + if (*httpSessionCache == NULL) + { + httpSession->is_mhd_active++; + *httpSessionCache = httpSession; + } + GNUNET_mutex_lock (lock); +#if DO_GET + if (0 == strcasecmp (MHD_HTTP_METHOD_GET, method)) + { +#if DEBUG_HTTP + GNUNET_GE_LOG (coreAPI->ectx, + GNUNET_GE_DEBUG | GNUNET_GE_REQUEST | GNUNET_GE_USER, + "HTTP/MHD receives GET request from `%s'.\n", &url[1]); +#endif + + /* handle get; create response object if we do not + have one already */ + get = GNUNET_malloc (sizeof (struct MHDGetData)); + memset (get, 0, sizeof (struct MHDGetData)); + get->next = httpSession->cs.server.gets; + httpSession->cs.server.gets = get; + get->session = session; + get->httpsession = httpSession; + get->last_get_activity = GNUNET_get_time (); + get->get = MHD_create_response_from_callback (MHD_SIZE_UNKNOWN, + 64 * 1024, + contentReaderCallback, + get, + contentReaderFreeCallback); + MHD_queue_response (session, MHD_HTTP_OK, get->get); + GNUNET_mutex_unlock (lock); + return MHD_YES; + } +#endif + if (0 == strcasecmp (MHD_HTTP_METHOD_PUT, method)) + { +#if DEBUG_HTTP + GNUNET_GE_LOG (coreAPI->ectx, + GNUNET_GE_DEBUG | GNUNET_GE_REQUEST | GNUNET_GE_USER, + "HTTP/MHD receives PUT request from `%s' with %u bytes.\n", + &url[1], *upload_data_size); +#endif + put = httpSession->cs.server.puts; + while ((put != NULL) && (put->session != session)) + put = put->next; + if (put == NULL) + { + put = GNUNET_malloc (sizeof (struct MHDPutData)); + memset (put, 0, sizeof (struct MHDPutData)); + put->next = httpSession->cs.server.puts; + httpSession->cs.server.puts = put; + put->session = session; + } + put->last_activity = GNUNET_get_time (); + + /* handle put (upload_data!) */ + poff = 0; + have = *upload_data_size; + if (stats != NULL) + stats->change (stat_bytesReceived, have); + *upload_data_size = 0; /* we will always process everything */ + if ((have == 0) && (put->done == GNUNET_NO) + && (put->ready == GNUNET_YES)) + { + put->done = GNUNET_YES; + /* end of upload, send response! */ +#if DEBUG_HTTP + GNUNET_GE_LOG (coreAPI->ectx, + GNUNET_GE_DEBUG | GNUNET_GE_REQUEST | GNUNET_GE_USER, + "HTTP/MHD queues dummy response to completed PUT request.\n"); +#endif + response = + MHD_create_response_from_data (strlen (HTTP_PUT_RESPONSE), + HTTP_PUT_RESPONSE, MHD_NO, MHD_NO); + MHD_queue_response (session, MHD_HTTP_OK, response); + MHD_destroy_response (response); + GNUNET_mutex_unlock (lock); + return MHD_YES; + } + while (have > 0) + { + put->ready = GNUNET_NO; + if (put->rpos1 < sizeof (GNUNET_MessageHeader)) + { + cpy = sizeof (GNUNET_MessageHeader) - put->rpos1; + if (cpy > have) + cpy = have; + memcpy (&put->rbuff1[put->rpos1], &upload_data[poff], cpy); + put->rpos1 += cpy; + have -= cpy; + poff += cpy; + put->rpos2 = 0; + } + if (put->rpos1 < sizeof (GNUNET_MessageHeader)) + break; + hdr = (GNUNET_MessageHeader *) put->rbuff1; + GNUNET_array_grow (put->rbuff2, + put->rsize2, + ntohs (hdr->size) - + sizeof (GNUNET_MessageHeader)); + if (put->rpos2 < ntohs (hdr->size) - sizeof (GNUNET_MessageHeader)) + { + cpy = + ntohs (hdr->size) - sizeof (GNUNET_MessageHeader) - + put->rpos2; + if (cpy > have) + cpy = have; + memcpy (&put->rbuff2[put->rpos2], &upload_data[poff], cpy); + have -= cpy; + poff += cpy; + put->rpos2 += cpy; + } + if (put->rpos2 < ntohs (hdr->size) - sizeof (GNUNET_MessageHeader)) + break; + mp = GNUNET_malloc (sizeof (GNUNET_TransportPacket)); + mp->msg = put->rbuff2; + mp->sender = httpSession->sender; + mp->tsession = httpSession->tsession; + mp->size = ntohs (hdr->size) - sizeof (GNUNET_MessageHeader); +#if DEBUG_HTTP + GNUNET_GE_LOG (coreAPI->ectx, + GNUNET_GE_DEBUG | GNUNET_GE_REQUEST | GNUNET_GE_USER, + "HTTP/MHD passes %u bytes to core (received via PUT request).\n", + mp->size); +#endif + coreAPI->receive (mp); + put->rbuff2 = NULL; + put->rpos2 = 0; + put->rsize2 = 0; + put->rpos1 = 0; + put->ready = GNUNET_YES; + } + GNUNET_mutex_unlock (lock); + return MHD_YES; + } + GNUNET_mutex_unlock (lock); + GNUNET_GE_BREAK_OP (NULL, 0); /* invalid request */ + return MHD_NO; +} + +#if DO_GET +/** + * Process downloaded bits (from GET via CURL). + */ +static size_t +receiveContentCallback (void *ptr, size_t size, size_t nmemb, void *ctx) +{ + HTTPSession *httpSession = ctx; + const char *inbuf = ptr; + size_t have = size * nmemb; + size_t poff = 0; + size_t cpy; + GNUNET_MessageHeader *hdr; + GNUNET_TransportPacket *mp; + + if (stats != NULL) + stats->change (stat_curl_receive_callbacks, 1); + httpSession->cs.client.last_get_activity = GNUNET_get_time (); +#if DEBUG_HTTP + GNUNET_GE_LOG (coreAPI->ectx, + GNUNET_GE_DEBUG | GNUNET_GE_REQUEST | GNUNET_GE_USER, + "HTTP/CURL receives %u bytes as response to GET.\n", + size * nmemb); +#endif + while (have > 0) + { + if (httpSession->cs.client.rpos1 < sizeof (GNUNET_MessageHeader)) + { + cpy = sizeof (GNUNET_MessageHeader) - httpSession->cs.client.rpos1; + if (cpy > have) + cpy = have; + memcpy (&httpSession->cs.client. + rbuff1[httpSession->cs.client.rpos1], &inbuf[poff], cpy); + httpSession->cs.client.rpos1 += cpy; + have -= cpy; + poff += cpy; + httpSession->cs.client.rpos2 = 0; + } + if (httpSession->cs.client.rpos1 < sizeof (GNUNET_MessageHeader)) + break; + hdr = (GNUNET_MessageHeader *) httpSession->cs.client.rbuff1; + GNUNET_array_grow (httpSession->cs.client.rbuff2, + httpSession->cs.client.rsize2, + ntohs (hdr->size) - sizeof (GNUNET_MessageHeader)); + if (httpSession->cs.client.rpos2 < + ntohs (hdr->size) - sizeof (GNUNET_MessageHeader)) + { + cpy = + ntohs (hdr->size) - sizeof (GNUNET_MessageHeader) - + httpSession->cs.client.rpos2; + if (cpy > have) + cpy = have; + memcpy (&httpSession->cs.client. + rbuff2[httpSession->cs.client.rpos2], &inbuf[poff], cpy); + have -= cpy; + poff += cpy; + httpSession->cs.client.rpos2 += cpy; + } + if (httpSession->cs.client.rpos2 < + ntohs (hdr->size) - sizeof (GNUNET_MessageHeader)) + break; + mp = GNUNET_malloc (sizeof (GNUNET_TransportPacket)); + mp->msg = httpSession->cs.client.rbuff2; + mp->sender = httpSession->sender; + mp->tsession = httpSession->tsession; + mp->size = ntohs (hdr->size) - sizeof (GNUNET_MessageHeader); + coreAPI->receive (mp); + httpSession->cs.client.rbuff2 = NULL; + httpSession->cs.client.rpos2 = 0; + httpSession->cs.client.rsize2 = 0; + httpSession->cs.client.rpos1 = 0; + } + if (stats != NULL) + stats->change (stat_bytesReceived, size * nmemb); + return size * nmemb; +} +#endif + +/** + * Provide bits for upload: we're using CURL for a PUT request + * and now need to provide data from the message we are transmitting. + */ +static size_t +sendContentCallback (void *ptr, size_t size, size_t nmemb, void *ctx) +{ + struct HTTPPutData *put = ctx; + size_t max = size * nmemb; + + if (stats != NULL) + stats->change (stat_curl_send_callbacks, 1); + put->last_activity = GNUNET_get_time (); + if (max > put->size - put->pos) + max = put->size - put->pos; + memcpy (ptr, &put->msg[put->pos], max); + put->pos += max; +#if DEBUG_HTTP + GNUNET_GE_LOG (coreAPI->ectx, + GNUNET_GE_DEBUG | GNUNET_GE_REQUEST | GNUNET_GE_USER, + "HTTP/CURL sends %u bytes in PUT request.\n", max); +#endif + if (stats != NULL) + stats->change (stat_bytesSent, max); + return max; +} + +#define CURL_EASY_SETOPT(c, a, b) do { ret = curl_easy_setopt(c, a, b); if (ret != CURLE_OK) GNUNET_GE_LOG(coreAPI->ectx, GNUNET_GE_WARNING | GNUNET_GE_USER | GNUNET_GE_BULK, _("%s failed at %s:%d: `%s'\n"), "curl_easy_setopt", __FILE__, __LINE__, curl_easy_strerror(ret)); } while (0); +#define IP_BUF_LEN 128 + +static void +create_session_url (HTTPSession * httpSession) +{ + char buf[IP_BUF_LEN]; + char *url; + GNUNET_EncName enc; + unsigned short available; + const char *obr; + const char *cbr; + const HostAddress *haddr = + (const HostAddress *) &httpSession->cs.client.address; + + url = httpSession->cs.client.url; + if (url == NULL) + { + GNUNET_hash_to_enc (&coreAPI->my_identity->hashPubKey, &enc); + available = ntohs (haddr->availability) & available_protocols; + if (available == (VERSION_AVAILABLE_IPV4 | VERSION_AVAILABLE_IPV6)) + { + if (GNUNET_random_u32 (GNUNET_RANDOM_QUALITY_WEAK, 2) == 0) + available = VERSION_AVAILABLE_IPV4; + else + available = VERSION_AVAILABLE_IPV6; + } + if ((available & VERSION_AVAILABLE_IPV4) > 0) + { + if (NULL == inet_ntop (AF_INET, &haddr->ipv4, buf, IP_BUF_LEN)) + { + /* log? */ + return; + } + obr = ""; + cbr = ""; + } + else if ((available & VERSION_AVAILABLE_IPV6) > 0) + { + if (NULL == inet_ntop (AF_INET6, &haddr->ipv6, buf, IP_BUF_LEN)) + { + /* log? */ + return; + } + obr = "["; + cbr = "]"; + } + else + return; /* error */ + url = GNUNET_malloc (64 + sizeof (GNUNET_EncName) + strlen (buf)); + GNUNET_snprintf (url, + 64 + sizeof (GNUNET_EncName), + "http://%s%s%s:%u/%s", obr, buf, cbr, + ntohs (haddr->port), &enc); + httpSession->cs.client.url = url; + } +} + +#if DO_GET +/** + * Try to do a GET on the other peer of the given + * http session. + * + * @return GNUNET_OK on success, GNUNET_SYSERR on error + */ +static int +create_curl_get (HTTPSession * httpSession) +{ + CURL *curl_get; + CURLcode ret; + CURLMcode mret; + GNUNET_CronTime now; + + if (httpSession->cs.client.url == NULL) + return GNUNET_SYSERR; + curl_get = httpSession->cs.client.get; + if (curl_get != NULL) + { + GNUNET_mutex_lock (lock); + curl_multi_remove_handle (curl_multi, curl_get); + http_requests_pending--; + signal_select (); + curl_easy_cleanup (curl_get); + GNUNET_mutex_unlock (lock); + httpSession->cs.client.get = NULL; + } + curl_get = curl_easy_init (); + if (curl_get == NULL) + return GNUNET_SYSERR; + /* create GET */ + CURL_EASY_SETOPT (curl_get, CURLOPT_FAILONERROR, 1); + CURL_EASY_SETOPT (curl_get, CURLOPT_URL, httpSession->cs.client.url); + if (strlen (proxy) > 0) + CURL_EASY_SETOPT (curl_get, CURLOPT_PROXY, proxy); + CURL_EASY_SETOPT (curl_get, CURLOPT_BUFFERSIZE, 32 * 1024); + if (0 == strncmp (httpSession->cs.client.url, "http", 4)) + CURL_EASY_SETOPT (curl_get, CURLOPT_USERAGENT, "GNUnet-http"); +#if 0 + CURL_EASY_SETOPT (curl_get, CURLOPT_VERBOSE, 1); +#endif + CURL_EASY_SETOPT (curl_get, CURLOPT_CONNECTTIMEOUT, 150L); + /* NOTE: use of CONNECTTIMEOUT without also + setting NOSIGNAL results in really weird + crashes on my system! */ + CURL_EASY_SETOPT (curl_get, CURLOPT_NOSIGNAL, 1); + CURL_EASY_SETOPT (curl_get, CURLOPT_TIMEOUT, 150L); + CURL_EASY_SETOPT (curl_get, CURLOPT_WRITEFUNCTION, &receiveContentCallback); + CURL_EASY_SETOPT (curl_get, CURLOPT_WRITEDATA, httpSession); + CURL_EASY_SETOPT (curl_get, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_1); + if (ret != CURLE_OK) + { + curl_easy_cleanup (curl_get); + return GNUNET_SYSERR; + } + GNUNET_mutex_lock (lock); + mret = curl_multi_add_handle (curl_multi, curl_get); + http_requests_pending++; + GNUNET_mutex_unlock (lock); + if (stats != NULL) + stats->change (stat_get_issued, 1); + if (mret != CURLM_OK) + { + GNUNET_GE_LOG (coreAPI->ectx, + GNUNET_GE_ERROR | GNUNET_GE_ADMIN | GNUNET_GE_USER | + GNUNET_GE_BULK, _("%s failed at %s:%d: `%s'\n"), + "curl_multi_add_handle", __FILE__, __LINE__, + curl_multi_strerror (mret)); + curl_easy_cleanup (curl_get); + return GNUNET_SYSERR; + } + signal_select (); + now = GNUNET_get_time (); + httpSession->cs.client.last_get_activity = now; + httpSession->cs.client.get = curl_get; + httpSession->cs.client.last_get_initiated = now; +#if DEBUG_HTTP + GNUNET_GE_LOG (coreAPI->ectx, + GNUNET_GE_DEBUG | GNUNET_GE_REQUEST | GNUNET_GE_USER, + "HTTP/CURL initiated GET request.\n"); +#endif + return GNUNET_OK; +} +#endif + +/** + * Establish a connection to a remote node. + * + * @param hello the hello-Message for the target node + * @param tsessionPtr the session handle that is set + * @return GNUNET_OK on success, GNUNET_SYSERR if the operation failed + */ +static int +httpConnect (const GNUNET_MessageHello * hello, + GNUNET_TSession ** tsessionPtr, int may_reuse) +{ + const HostAddress *haddr = (const HostAddress *) &hello[1]; + GNUNET_TSession *tsession; + HTTPSession *httpSession; + int i; + + if (stats != NULL) + stats->change (stat_connect_calls, 1); + /* check if we have a session pending for this peer */ + tsession = NULL; + if (may_reuse) + { + GNUNET_mutex_lock (lock); + for (i = 0; i < tsessionCount; i++) + { + if (0 == memcmp (&hello->senderIdentity, + &tsessions[i]->peer, sizeof (GNUNET_PeerIdentity))) + { + tsession = tsessions[i]; + break; + } + } + if ((tsession != NULL) && (GNUNET_OK == httpAssociate (tsession))) + { + *tsessionPtr = tsession; + GNUNET_mutex_unlock (lock); + return GNUNET_OK; + } + GNUNET_mutex_unlock (lock); + } + /* no session pending, initiate a new one! */ + httpSession = GNUNET_malloc (sizeof (HTTPSession)); + memset (httpSession, 0, sizeof (HTTPSession)); + httpSession->sender = hello->senderIdentity; + httpSession->users = 1; /* us only, core has not seen this tsession! */ + httpSession->is_client = GNUNET_YES; + httpSession->cs.client.address = *haddr; + tsession = GNUNET_malloc (sizeof (GNUNET_TSession)); + memset (tsession, 0, sizeof (GNUNET_TSession)); + httpSession->tsession = tsession; + tsession->ttype = GNUNET_TRANSPORT_PROTOCOL_NUMBER_HTTP; + tsession->internal = httpSession; + tsession->peer = hello->senderIdentity; + create_session_url (httpSession); +#if DO_GET + if (GNUNET_OK != create_curl_get (httpSession)) + { + GNUNET_free (tsession); + GNUNET_free (httpSession); + return GNUNET_SYSERR; + } +#endif + /* PUTs will be created as needed */ + addTSession (tsession); + *tsessionPtr = tsession; +#if DEBUG_HTTP + GNUNET_GE_LOG (coreAPI->ectx, + GNUNET_GE_DEBUG | GNUNET_GE_REQUEST | GNUNET_GE_USER, + "HTTP/CURL initiated connection to `%s'.\n", + httpSession->cs.client.url); +#endif + return GNUNET_OK; +} + +/** + * We received the "Thank you!" response to a PUT. + * Discard the data (not useful) and mark the PUT + * operation as completed. + */ +static size_t +discardContentCallback (void *data, size_t size, size_t nmemb, void *put_cls) +{ + struct HTTPPutData *put = put_cls; + /* this condition should pretty much always be + true; just checking here in case the PUT + response comes early somehow */ + if (put->pos == put->size) + put->done = GNUNET_YES; + return size * nmemb; +} + +/** + * Create a new PUT request for the given PUT data. + */ +static int +create_curl_put (HTTPSession * httpSession, struct HTTPPutData *put) +{ + CURL *curl_put; + CURLcode ret; + CURLMcode mret; + long size; + + /* we should have initiated a GET earlier, + so URL must not be NULL here */ + if (httpSession->cs.client.url == NULL) + return GNUNET_SYSERR; + curl_put = curl_easy_init (); + if (curl_put == NULL) + return GNUNET_SYSERR; + CURL_EASY_SETOPT (curl_put, CURLOPT_FAILONERROR, 1); + CURL_EASY_SETOPT (curl_put, CURLOPT_URL, httpSession->cs.client.url); + if (strlen (proxy) > 0) + CURL_EASY_SETOPT (curl_put, CURLOPT_PROXY, proxy); + CURL_EASY_SETOPT (curl_put, CURLOPT_BUFFERSIZE, put->size); + if (0 == strncmp (httpSession->cs.client.url, "http", 4)) + CURL_EASY_SETOPT (curl_put, CURLOPT_USERAGENT, "GNUnet-http"); + CURL_EASY_SETOPT (curl_put, CURLOPT_UPLOAD, 1); +#if 0 + CURL_EASY_SETOPT (curl_put, CURLOPT_VERBOSE, 1); +#endif + CURL_EASY_SETOPT (curl_put, CURLOPT_CONNECTTIMEOUT, 150L); + /* NOTE: use of CONNECTTIMEOUT without also + setting NOSIGNAL results in really weird + crashes on my system! */ + CURL_EASY_SETOPT (curl_put, CURLOPT_NOSIGNAL, 1); + CURL_EASY_SETOPT (curl_put, CURLOPT_TIMEOUT, 150L); + size = put->size; + CURL_EASY_SETOPT (curl_put, CURLOPT_INFILESIZE, size); + CURL_EASY_SETOPT (curl_put, CURLOPT_READFUNCTION, &sendContentCallback); + CURL_EASY_SETOPT (curl_put, CURLOPT_READDATA, put); + CURL_EASY_SETOPT (curl_put, CURLOPT_WRITEFUNCTION, &discardContentCallback); + CURL_EASY_SETOPT (curl_put, CURLOPT_WRITEDATA, put); + CURL_EASY_SETOPT (curl_put, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_1); + if (ret != CURLE_OK) + { + curl_easy_cleanup (curl_put); + return GNUNET_SYSERR; + } + GNUNET_mutex_lock (lock); + mret = curl_multi_add_handle (curl_multi, curl_put); + http_requests_pending++; + GNUNET_mutex_unlock (lock); + if (stats != NULL) + stats->change (stat_put_issued, 1); + if (mret != CURLM_OK) + { + GNUNET_GE_LOG (coreAPI->ectx, + GNUNET_GE_ERROR | GNUNET_GE_ADMIN | GNUNET_GE_USER | + GNUNET_GE_BULK, _("%s failed at %s:%d: `%s'\n"), + "curl_multi_add_handle", __FILE__, __LINE__, + curl_multi_strerror (mret)); + return GNUNET_SYSERR; + } + signal_select (); + put->curl_put = curl_put; +#if DEBUG_HTTP + GNUNET_GE_LOG (coreAPI->ectx, + GNUNET_GE_DEBUG | GNUNET_GE_REQUEST | GNUNET_GE_USER, + "HTTP/CURL initiated PUT request to `%s'.\n", + httpSession->cs.client.url); +#endif + return GNUNET_OK; +} + + +/** + * Test if the transport would even try to send + * a message of the given size and importance + * for the given session.
+ * This function is used to check if the core should + * even bother to construct (and encrypt) this kind + * of message. + * + * @return GNUNET_YES if the transport would try (i.e. queue + * the message or call the OS to send), + * GNUNET_NO if the transport would just drop the message, + * GNUNET_SYSERR if the size/session is invalid + */ +static int +httpTestWouldTry (GNUNET_TSession * tsession, const unsigned int size, + int important) +{ + HTTPSession *httpSession = tsession->internal; + struct MHDGetData *get; + int ret; + + if (size >= GNUNET_MAX_BUFFER_SIZE - sizeof (GNUNET_MessageHeader)) + { + GNUNET_GE_BREAK (coreAPI->ectx, 0); + return GNUNET_SYSERR; + } + if (size == 0) + { + GNUNET_GE_BREAK (coreAPI->ectx, 0); + return GNUNET_SYSERR; + } + if (httpSession->is_client) + { + /* client */ + if ((important != GNUNET_YES) && (httpSession->cs.client.puts != NULL)) + return GNUNET_NO; + return GNUNET_YES; + } + else + { + /* server */ + GNUNET_mutex_lock (lock); + get = httpSession->cs.server.gets; + if (get == NULL) + ret = GNUNET_NO; + else + { + if (get->wsize == 0) + ret = GNUNET_YES; + else if ((get->wpos + size > get->wsize) + && (important != GNUNET_YES)) + ret = GNUNET_NO; + else + ret = GNUNET_YES; + } + GNUNET_mutex_unlock (lock); + return ret; + } +} + + +/** + * Send a message to the specified remote node. + * + * @param tsession the GNUNET_MessageHello identifying the remote node + * @param msg the message + * @param size the size of the message + * @return GNUNET_SYSERR on error, GNUNET_OK on success, GNUNET_NO if queue is full + */ +static int +httpSend (GNUNET_TSession * tsession, + const void *msg, unsigned int size, int important) +{ + HTTPSession *httpSession = tsession->internal; + struct HTTPPutData *putData; + GNUNET_MessageHeader *hdr; +#if DO_GET + struct MHDGetData *getData; + char *tmp; +#endif + + if (stats != NULL) + stats->change (stat_send_calls, 1); + if (httpSession->is_client) + { + /* we need to do a PUT (we are the client) */ + if (size >= GNUNET_MAX_BUFFER_SIZE) + return GNUNET_SYSERR; + if (size == 0) + { + GNUNET_GE_BREAK (NULL, 0); + return GNUNET_SYSERR; + } + if (important != GNUNET_YES) + { + GNUNET_mutex_lock (lock); + if (httpSession->cs.client.puts != NULL) + { + /* do not queue more than one unimportant PUT at a time */ + signal_select (); /* do clean up now! */ + GNUNET_mutex_unlock (lock); + if (stats != NULL) + stats->change (stat_bytesDropped, size); + + return GNUNET_NO; + } + GNUNET_mutex_unlock (lock); + } + putData = GNUNET_malloc (sizeof (struct HTTPPutData)); + memset (putData, 0, sizeof (struct HTTPPutData)); + putData->msg = GNUNET_malloc (size + sizeof (GNUNET_MessageHeader)); + hdr = (GNUNET_MessageHeader *) putData->msg; + hdr->size = htons (size + sizeof (GNUNET_MessageHeader)); + hdr->type = htons (0); + memcpy (&putData->msg[sizeof (GNUNET_MessageHeader)], msg, size); + putData->size = size + sizeof (GNUNET_MessageHeader); + putData->last_activity = GNUNET_get_time (); + if (GNUNET_OK != create_curl_put (httpSession, putData)) + { + GNUNET_free (putData->msg); + GNUNET_free (putData); + return GNUNET_SYSERR; + } + GNUNET_mutex_lock (lock); + putData->next = httpSession->cs.client.puts; + httpSession->cs.client.puts = putData; + GNUNET_mutex_unlock (lock); + return GNUNET_OK; + } + + /* httpSession->isClient == false, respond to a GET (we + hopefully have one or will have one soon) */ +#if DEBUG_HTTP + GNUNET_GE_LOG (coreAPI->ectx, + GNUNET_GE_DEBUG | GNUNET_GE_REQUEST | GNUNET_GE_USER, + "HTTP/MHD queues %u bytes to be sent as response to GET as soon as possible.\n", + size); +#endif +#if DO_GET + GNUNET_mutex_lock (lock); + getData = httpSession->cs.server.gets; + if (getData == NULL) + { + GNUNET_mutex_unlock (lock); + return GNUNET_SYSERR; + } + if (getData->wsize == 0) + GNUNET_array_grow (getData->wbuff, getData->wsize, HTTP_BUF_SIZE); + size += sizeof (GNUNET_MessageHeader); + if (getData->wpos + size > getData->wsize) + { + /* need to grow or discard */ + if (!important) + { + GNUNET_mutex_unlock (lock); + return GNUNET_NO; + } + tmp = GNUNET_malloc (getData->wpos + size); + memcpy (tmp, &getData->wbuff[getData->woff], getData->wpos); + hdr = (GNUNET_MessageHeader *) & tmp[getData->wpos]; + hdr->type = htons (0); + hdr->size = htons (size); + memcpy (&hdr[1], msg, size - sizeof (GNUNET_MessageHeader)); + GNUNET_free (getData->wbuff); + getData->wbuff = tmp; + getData->wsize = getData->wpos + size; + getData->woff = 0; + getData->wpos = getData->wpos + size; + } + else + { + /* fits without growing */ + if (getData->wpos + getData->woff + size > getData->wsize) + { + /* need to compact first */ + memmove (getData->wbuff, + &getData->wbuff[getData->woff], getData->wpos); + getData->woff = 0; + } + /* append */ + hdr = + (GNUNET_MessageHeader *) & getData->wbuff[getData->woff + + getData->wpos]; + hdr->size = htons (size); + hdr->type = htons (0); + memcpy (&hdr[1], msg, size - sizeof (GNUNET_MessageHeader)); + getData->wpos += size; + } + signal_select (); + GNUNET_mutex_unlock (lock); +#endif + return GNUNET_OK; +} + +/** + * Function called to cleanup dead connections + * (completed PUTs, GETs that have timed out, + * etc.). Also re-vives GETs that have timed out + * if we are still interested in the connection. + */ +static void +cleanup_connections () +{ + int i; + HTTPSession *s; + struct HTTPPutData *prev; + struct HTTPPutData *pos; + struct MHDPutData *mpos; + struct MHDPutData *mprev; +#if DO_GET + struct MHD_Response *r; + struct MHDGetData *gpos; + struct MHDGetData *gnext; +#endif + GNUNET_CronTime now; + + GNUNET_mutex_lock (lock); + now = GNUNET_get_time (); + for (i = 0; i < tsessionCount; i++) + { + s = tsessions[i]->internal; + if (s->is_client) + { + if ((s->cs.client.puts == NULL) && (s->users == 0) +#if DO_GET + && (s->cs.client.last_get_activity + HTTP_TIMEOUT < now) +#endif + ) + { +#if DO_GET +#if DEBUG_HTTP + GNUNET_GE_LOG (coreAPI->ectx, + GNUNET_GE_DEBUG | GNUNET_GE_REQUEST | + GNUNET_GE_USER, + "HTTP transport destroys old (%llu ms) unused client session\n", + now - s->cs.client.last_get_activity); +#endif +#endif + destroy_tsession (tsessions[i]); + i--; + continue; + } + + prev = NULL; + pos = s->cs.client.puts; + while (pos != NULL) + { + if (pos->last_activity + HTTP_TIMEOUT < now) + pos->done = GNUNET_YES; + if (pos->done) + { + if (prev == NULL) + s->cs.client.puts = pos->next; + else + prev->next = pos->next; + GNUNET_free (pos->msg); + curl_multi_remove_handle (curl_multi, pos->curl_put); + http_requests_pending--; + signal_select (); + curl_easy_cleanup (pos->curl_put); + GNUNET_free (pos); + if (prev == NULL) + pos = s->cs.client.puts; + else + pos = prev->next; + continue; + } + prev = pos; + pos = pos->next; + } +#if DO_GET + if ((s->cs.client.last_get_activity + HTTP_TIMEOUT < now) && + ((s->users > 0) || (s->cs.client.puts != NULL)) && + ((s->cs.client.last_get_initiated + HTTP_GET_REFRESH > now) || + (s->cs.client.get == NULL)) && + ((s->cs.client.get == NULL) || + (s->cs.client.last_get_activity + HTTP_GET_REFRESH / 2 < now))) + create_curl_get (s); +#endif + } + else + { + mpos = s->cs.server.puts; + mprev = NULL; + while (mpos != NULL) + { + if (mpos->last_activity == 0) + { + if (mprev == NULL) + s->cs.server.puts = mpos->next; + else + mprev->next = mpos->next; + GNUNET_array_grow (mpos->rbuff2, mpos->rsize2, 0); + GNUNET_free (mpos); + if (mprev == NULL) + mpos = s->cs.server.puts; + else + mpos = mprev->next; + continue; + } + mprev = mpos; + mpos = mpos->next; + } + + /* ! s->is_client */ +#if DO_GET + gpos = s->cs.server.gets; + while (gpos != NULL) + { + gnext = gpos->next; + gpos->next = NULL; + if ((gpos->last_get_activity + HTTP_TIMEOUT < now) || + (gpos != s->cs.server.gets)) + { + if (gpos == s->cs.server.gets) + s->cs.server.gets = NULL; + r = gpos->get; + gpos->get = NULL; + MHD_destroy_response (r); + } + gpos = gnext; + } +#endif + if ( +#if DO_GET + (s->cs.server.gets == NULL) && +#endif + (s->is_mhd_active == 0) && (s->users == 0)) + { +#if DO_GET +#if DEBUG_HTTP + GNUNET_GE_LOG (coreAPI->ectx, + GNUNET_GE_DEBUG | GNUNET_GE_REQUEST | + GNUNET_GE_USER, + "HTTP transport destroys unused server session\n"); +#endif +#endif + destroy_tsession (tsessions[i]); + i--; + continue; + } + } + } + GNUNET_mutex_unlock (lock); +} + +/** + * Thread that runs the CURL and MHD requests. + */ +static void * +curl_runner (void *unused) +{ + CURLMcode mret; + fd_set rs; + fd_set ws; + fd_set es; + int max; + struct timeval tv; + int running; + unsigned long long timeout; + long ms; + int have_tv; + char buf[128]; /* for reading from pipe */ + int ret; + +#if DEBUG_HTTP + GNUNET_GE_LOG (coreAPI->ectx, + GNUNET_GE_DEBUG | GNUNET_GE_REQUEST | GNUNET_GE_USER, + "HTTP transport select thread started\n"); +#endif + while (GNUNET_YES == http_running) + { + max = 0; + FD_ZERO (&rs); + FD_ZERO (&ws); + FD_ZERO (&es); + GNUNET_mutex_lock (lock); + mret = curl_multi_fdset (curl_multi, &rs, &ws, &es, &max); + GNUNET_mutex_unlock (lock); + if (mret != CURLM_OK) + { + GNUNET_GE_LOG (coreAPI->ectx, + GNUNET_GE_ERROR | GNUNET_GE_ADMIN | GNUNET_GE_USER | + GNUNET_GE_BULK, _("%s failed at %s:%d: `%s'\n"), + "curl_multi_fdset", __FILE__, __LINE__, + curl_multi_strerror (mret)); + break; + } + if (mhd_daemon != NULL) + MHD_get_fdset (mhd_daemon, &rs, &ws, &es, &max); + timeout = 0; + have_tv = MHD_NO; + if (mhd_daemon != NULL) + have_tv = MHD_get_timeout (mhd_daemon, &timeout); + GNUNET_mutex_lock (lock); + if ((CURLM_OK == curl_multi_timeout (curl_multi, &ms)) && + (ms != -1) && ((ms < timeout) || (have_tv == MHD_NO))) + { + timeout = ms; + have_tv = MHD_YES; + } + GNUNET_mutex_unlock (lock); + FD_SET (signal_pipe[0], &rs); + if (max < signal_pipe[0]) + max = signal_pipe[0]; + tv.tv_sec = timeout / 1000; + tv.tv_usec = (timeout % 1000) * 1000; + if (stats != NULL) + stats->change (stat_select_calls, 1); + ret = + SELECT (max + 1, &rs, &ws, &es, (have_tv == MHD_YES) ? &tv : NULL); + if (ret == -1) + { + GNUNET_GE_LOG_STRERROR (coreAPI->ectx, + GNUNET_GE_ERROR | GNUNET_GE_ADMIN | + GNUNET_GE_DEVELOPER, "select"); + } + if (GNUNET_YES != http_running) + break; + running = 0; + do + { + GNUNET_mutex_lock (lock); + mret = curl_multi_perform (curl_multi, &running); + GNUNET_mutex_unlock (lock); + } + while ((mret == CURLM_CALL_MULTI_PERFORM) + && (http_running == GNUNET_YES)); + if (FD_ISSET (signal_pipe[0], &rs)) + read (signal_pipe[0], buf, sizeof (buf)); + if ((mret != CURLM_OK) && (mret != CURLM_CALL_MULTI_PERFORM)) + GNUNET_GE_LOG (coreAPI->ectx, + GNUNET_GE_ERROR | GNUNET_GE_ADMIN | GNUNET_GE_USER | + GNUNET_GE_BULK, _("%s failed at %s:%d: `%s'\n"), + "curl_multi_perform", __FILE__, __LINE__, + curl_multi_strerror (mret)); + if (mhd_daemon != NULL) + MHD_run (mhd_daemon); + cleanup_connections (); + } +#if DEBUG_HTTP + GNUNET_GE_LOG (coreAPI->ectx, + GNUNET_GE_DEBUG | GNUNET_GE_REQUEST | GNUNET_GE_USER, + "HTTP transport select thread exits.\n"); +#endif + return NULL; +} + + +/** + * Start the server process to receive inbound traffic. + * @return GNUNET_OK on success, GNUNET_SYSERR if the operation failed + */ +static int +startTransportServer () +{ + unsigned short port; + + if ((curl_multi != NULL) || (http_running == GNUNET_YES)) + return GNUNET_SYSERR; + curl_multi = curl_multi_init (); + if (curl_multi == NULL) + return GNUNET_SYSERR; + port = get_port (); + if ((mhd_daemon == NULL) && (port != 0)) + { + if (GNUNET_YES != + GNUNET_GC_get_configuration_value_yesno (cfg, "GNUNETD", + "DISABLE-IPV6", + GNUNET_YES)) + { + mhd_daemon = MHD_start_daemon (MHD_USE_IPv6, + port, + &acceptPolicyCallback, + NULL, &accessHandlerCallback, NULL, + MHD_OPTION_CONNECTION_TIMEOUT, + (unsigned int) HTTP_TIMEOUT, + MHD_OPTION_CONNECTION_MEMORY_LIMIT, + (unsigned int) 1024 * 128, + MHD_OPTION_CONNECTION_LIMIT, + (unsigned int) 128, + MHD_OPTION_PER_IP_CONNECTION_LIMIT, + (unsigned int) 8, + MHD_OPTION_NOTIFY_COMPLETED, + &requestCompletedCallback, NULL, + MHD_OPTION_END); + } + if (mhd_daemon == NULL) + { + /* try without IPv6 */ + mhd_daemon = MHD_start_daemon (MHD_NO_FLAG, + port, + &acceptPolicyCallback, + NULL, &accessHandlerCallback, NULL, + MHD_OPTION_CONNECTION_TIMEOUT, + (unsigned int) HTTP_TIMEOUT, + MHD_OPTION_CONNECTION_MEMORY_LIMIT, + (unsigned int) 1024 * 128, + MHD_OPTION_CONNECTION_LIMIT, + (unsigned int) 128, + MHD_OPTION_PER_IP_CONNECTION_LIMIT, + (unsigned int) 8, + MHD_OPTION_NOTIFY_COMPLETED, + &requestCompletedCallback, NULL, + MHD_OPTION_END); + } + else + { + available_protocols |= VERSION_AVAILABLE_IPV6; + } + if (mhd_daemon != NULL) + available_protocols |= VERSION_AVAILABLE_IPV4; + } + if (port == 0) + { + /* NAT */ + available_protocols |= VERSION_AVAILABLE_IPV4; + if (GNUNET_YES != + GNUNET_GC_get_configuration_value_yesno (cfg, "GNUNETD", + "DISABLE-IPV6", + GNUNET_YES)) + available_protocols |= VERSION_AVAILABLE_IPV6; + } + if (0 != PIPE (signal_pipe)) + { + MHD_stop_daemon (mhd_daemon); + curl_multi_cleanup (curl_multi); + curl_multi = NULL; + mhd_daemon = NULL; + return GNUNET_SYSERR; + } + GNUNET_pipe_make_nonblocking (coreAPI->ectx, signal_pipe[0]); + GNUNET_pipe_make_nonblocking (coreAPI->ectx, signal_pipe[1]); + http_running = GNUNET_YES; + curl_thread = GNUNET_thread_create (&curl_runner, NULL, 32 * 1024); + if (curl_thread == NULL) + GNUNET_GE_DIE_STRERROR (coreAPI->ectx, + GNUNET_GE_FATAL | GNUNET_GE_ADMIN | + GNUNET_GE_IMMEDIATE, "pthread_create"); + return GNUNET_OK; +} + +/** + * Shutdown the server process (stop receiving inbound + * traffic). May be restarted later! + */ +static int +stopTransportServer () +{ + void *unused; + int i; + HTTPSession *s; + + if ((http_running == GNUNET_NO) || (curl_multi == NULL)) + return GNUNET_SYSERR; + http_running = GNUNET_NO; + signal_select (); + GNUNET_thread_stop_sleep (curl_thread); + GNUNET_thread_join (curl_thread, &unused); + CLOSE (signal_pipe[0]); + CLOSE (signal_pipe[1]); + if (mhd_daemon != NULL) + { + MHD_stop_daemon (mhd_daemon); + mhd_daemon = NULL; + } + cleanup_connections (); + for (i = 0; i < tsessionCount; i++) + { + s = tsessions[i]->internal; + if (s->users == 0) + { + destroy_tsession (tsessions[i]); + i--; + } + } + curl_multi_cleanup (curl_multi); + curl_multi = NULL; + return GNUNET_OK; +} + +/* ******************** public API ******************** */ + +/** + * The exported method. Makes the core api available + * via a global and returns the udp transport API. + */ +GNUNET_TransportAPI * +inittransport_http (GNUNET_CoreAPIForTransport * core) +{ + GNUNET_GE_ASSERT (coreAPI->ectx, sizeof (HostAddress) == 24); + coreAPI = core; + cfg = coreAPI->cfg; + lock = GNUNET_mutex_create (GNUNET_YES); + if (0 != GNUNET_GC_attach_change_listener (coreAPI->cfg, + &reload_configuration, NULL)) + { + GNUNET_mutex_destroy (lock); + lock = NULL; + return NULL; + } + if (0 != curl_global_init (CURL_GLOBAL_WIN32)) + { + GNUNET_GE_BREAK (NULL, 0); + GNUNET_GC_detach_change_listener (coreAPI->cfg, &reload_configuration, + NULL); + GNUNET_mutex_destroy (lock); + lock = NULL; + return NULL; + } + tsessionCount = 0; + tsessionArrayLength = 0; + GNUNET_array_grow (tsessions, tsessionArrayLength, 32); + if (GNUNET_GC_get_configuration_value_yesno (coreAPI->cfg, + "HTTP", "UPNP", + GNUNET_YES) == GNUNET_YES) + { + upnp = coreAPI->service_request ("upnp"); + + if (upnp == NULL) + { + GNUNET_GE_LOG (coreAPI->ectx, + GNUNET_GE_ERROR | GNUNET_GE_USER | + GNUNET_GE_IMMEDIATE, + _ + ("The UPnP service could not be loaded. To disable UPnP, set the " + "configuration option \"UPNP\" in section \"%s\" to \"NO\"\n"), + "HTTP"); + } + } + stats = coreAPI->service_request ("stats"); + if (stats != NULL) + { + stat_bytesReceived + = stats->create (gettext_noop ("# bytes received via HTTP")); + stat_bytesSent = stats->create (gettext_noop ("# bytes sent via HTTP")); + stat_bytesDropped + = stats->create (gettext_noop ("# bytes dropped by HTTP (outgoing)")); + stat_get_issued = stats->create (gettext_noop ("# HTTP GET issued")); + stat_get_received + = stats->create (gettext_noop ("# HTTP GET received")); + stat_put_issued = stats->create (gettext_noop ("# HTTP PUT issued")); + stat_put_received + = stats->create (gettext_noop ("# HTTP PUT received")); + stat_select_calls + = stats->create (gettext_noop ("# HTTP select calls")); + + stat_send_calls = stats->create (gettext_noop ("# HTTP send calls")); + + stat_curl_send_callbacks + = stats->create (gettext_noop ("# HTTP curl send callbacks")); + stat_curl_receive_callbacks + = stats->create (gettext_noop ("# HTTP curl receive callbacks")); + stat_mhd_access_callbacks + = stats->create (gettext_noop ("# HTTP mhd access callbacks")); + stat_mhd_read_callbacks + = stats->create (gettext_noop ("# HTTP mhd read callbacks")); + stat_mhd_close_callbacks + = stats->create (gettext_noop ("# HTTP mhd close callbacks")); + stat_connect_calls + = stats->create (gettext_noop ("# HTTP connect calls")); + } + GNUNET_GC_get_configuration_value_string (coreAPI->cfg, + "GNUNETD", "HTTP-PROXY", "", + &proxy); + + myAPI.protocol_number = GNUNET_TRANSPORT_PROTOCOL_NUMBER_HTTP; + myAPI.mtu = 0; + myAPI.cost = 20000; /* about equal to udp */ + myAPI.hello_verify = &verify_hello; + myAPI.hello_create = &create_hello; + myAPI.connect = &httpConnect; + myAPI.associate = &httpAssociate; + myAPI.send = &httpSend; + myAPI.disconnect = &httpDisconnect; + myAPI.server_start = &startTransportServer; + myAPI.server_stop = &stopTransportServer; + myAPI.hello_to_address = &hello_to_address; + myAPI.send_now_test = &httpTestWouldTry; + + return &myAPI; +} + +void +donetransport_http () +{ + curl_global_cleanup (); + GNUNET_free_non_null (proxy); + proxy = NULL; + GNUNET_array_grow (tsessions, tsessionArrayLength, 0); + do_shutdown (); +} + +/* end of http.c */ diff --git a/src/transport/plugin_transport_smtp.c b/src/transport/plugin_transport_smtp.c new file mode 100644 index 000000000..f7cc530e4 --- /dev/null +++ b/src/transport/plugin_transport_smtp.c @@ -0,0 +1,906 @@ +/* + This file is part of GNUnet + (C) 2003, 2004, 2005, 2006, 2007 Christian Grothoff (and other contributing authors) + + GNUnet is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published + by the Free Software Foundation; either version 2, 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 + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with GNUnet; see the file COPYING. If not, write to the + Free Software Foundation, Inc., 59 Temple Place - Suite 330, + Boston, MA 02111-1307, USA. +*/ + +/** + * @file transports/smtp.c + * @brief Implementation of the SMTP transport service + * @author Christian Grothoff + * @author Renaldo Ferreira + */ + +#include "platform.h" +#include "gnunet_util.h" +#include "gnunet_directories.h" +#include "gnunet_protocols.h" +#include "gnunet_transport.h" +#include "gnunet_stats_service.h" +#include +#include + + +/** + * The default maximum size of each outbound SMTP message. + */ +#define SMTP_MESSAGE_SIZE 65528 + +#define DEBUG_SMTP GNUNET_NO + +#define FILTER_STRING_SIZE 64 + +/* how long can a line in base64 encoded + mime text be? (in characters, excluding "\n") */ +#define MAX_CHAR_PER_LINE 76 + +#define EBUF_LEN 128 + +/** + * Host-Address in a SMTP network. + */ +typedef struct +{ + + /** + * Filter line that every sender must include in the E-mails such + * that the receiver can effectively filter out the GNUnet traffic + * from the E-mail. + */ + char filter[FILTER_STRING_SIZE]; + + /** + * Claimed E-mail address of the sender. + * Format is "foo@bar.com" with null termination, padded to be + * of a multiple of 8 bytes long. + */ + char senderAddress[0]; + +} EmailAddress; + +/** + * Encapsulation of a GNUnet message in the SMTP mail body (before + * base64 encoding). + */ +typedef struct +{ + GNUNET_MessageHeader header; + + /** + * What is the identity of the sender (GNUNET_hash of public key) + */ + GNUNET_PeerIdentity sender; + +} SMTPMessage; + +/* *********** globals ************* */ + +/** + * apis (our advertised API and the core api ) + */ +static GNUNET_CoreAPIForTransport *coreAPI; + +static struct GNUNET_GE_Context *ectx; + +/** + * Thread that listens for inbound messages + */ +static struct GNUNET_ThreadHandle *dispatchThread; + +/** + * Flag to indicate that server has been shut down. + */ +static int smtp_shutdown = GNUNET_YES; + +/** + * Set to the SMTP server hostname (and port) for outgoing messages. + */ +static char *smtp_server_name; + +static char *pipename; + +/** + * Lock for uses of libesmtp (not thread-safe). + */ +static struct GNUNET_Mutex *lock; + +/** + * Old handler for SIGPIPE (kept to be able to restore). + */ +static struct sigaction old_handler; + +static char *email; + +static GNUNET_TransportAPI smtpAPI; + +static GNUNET_Stats_ServiceAPI *stats; + +static int stat_bytesReceived; + +static int stat_bytesSent; + +static int stat_bytesDropped; + +/** + * How many e-mails are we allowed to send per hour? + */ +static unsigned long long rate_limit; + +static GNUNET_CronTime last_transmission; + +/** ******************** Base64 encoding ***********/ + +#define FILLCHAR '=' +static char *cvt = "ABCDEFGHIJKLMNOPQRSTUVWXYZ" + "abcdefghijklmnopqrstuvwxyz" "0123456789+/"; + +/** + * Encode into Base64. + * + * @param data the data to encode + * @param len the length of the input + * @param output where to write the output (*output should be NULL, + * is allocated) + * @return the size of the output + */ +static unsigned int +base64_encode (const char *data, unsigned int len, char **output) +{ + unsigned int i; + char c; + unsigned int ret; + char *opt; + +/* (*output)[ret++] = '\r'; \*/ +#define CHECKLINE \ + if ( (ret % MAX_CHAR_PER_LINE) == 0) { \ + (*output)[ret++] = '\n'; \ + } + ret = 0; + opt = GNUNET_malloc (2 + (((len * 4 / 3) + 8) * (MAX_CHAR_PER_LINE + 2)) / + MAX_CHAR_PER_LINE); + /* message must start with \r\n for libesmtp */ + *output = opt; + opt[0] = '\r'; + opt[1] = '\n'; + ret += 2; + for (i = 0; i < len; ++i) + { + c = (data[i] >> 2) & 0x3f; + opt[ret++] = cvt[(int) c]; + CHECKLINE; + c = (data[i] << 4) & 0x3f; + if (++i < len) + c |= (data[i] >> 4) & 0x0f; + opt[ret++] = cvt[(int) c]; + CHECKLINE; + if (i < len) + { + c = (data[i] << 2) & 0x3f; + if (++i < len) + c |= (data[i] >> 6) & 0x03; + opt[ret++] = cvt[(int) c]; + CHECKLINE; + } + else + { + ++i; + opt[ret++] = FILLCHAR; + CHECKLINE; + } + if (i < len) + { + c = data[i] & 0x3f; + opt[ret++] = cvt[(int) c]; + CHECKLINE; + } + else + { + opt[ret++] = FILLCHAR; + CHECKLINE; + } + } + opt[ret++] = FILLCHAR; + return ret; +} + +#define cvtfind(a)( (((a) >= 'A')&&((a) <= 'Z'))? (a)-'A'\ + :(((a)>='a')&&((a)<='z')) ? (a)-'a'+26\ + :(((a)>='0')&&((a)<='9')) ? (a)-'0'+52\ + :((a) == '+') ? 62\ + :((a) == '/') ? 63 : -1) +/** + * Decode from Base64. + * + * @param data the data to encode + * @param len the length of the input + * @param output where to write the output (*output should be NULL, + * is allocated) + * @return the size of the output + */ +static unsigned int +base64_decode (const char *data, unsigned int len, char **output) +{ + unsigned int i; + char c; + char c1; + unsigned int ret = 0; + +#define CHECK_CRLF while (data[i] == '\r' || data[i] == '\n') {\ + GNUNET_GE_LOG(ectx, GNUNET_GE_DEBUG | GNUNET_GE_REQUEST | GNUNET_GE_USER, "ignoring CR/LF\n"); \ + i++; \ + if (i >= len) goto END; \ + } + + *output = GNUNET_malloc ((len * 3 / 4) + 8); +#if DEBUG_SMTP + GNUNET_GE_LOG (ectx, GNUNET_GE_DEBUG | GNUNET_GE_REQUEST | GNUNET_GE_USER, + "base64_decode decoding len=%d\n", len); +#endif + for (i = 0; i < len; ++i) + { + CHECK_CRLF; + if (data[i] == FILLCHAR) + break; + c = (char) cvtfind (data[i]); + ++i; + CHECK_CRLF; + c1 = (char) cvtfind (data[i]); + c = (c << 2) | ((c1 >> 4) & 0x3); + (*output)[ret++] = c; + if (++i < len) + { + CHECK_CRLF; + c = data[i]; + if (FILLCHAR == c) + break; + c = (char) cvtfind (c); + c1 = ((c1 << 4) & 0xf0) | ((c >> 2) & 0xf); + (*output)[ret++] = c1; + } + if (++i < len) + { + CHECK_CRLF; + c1 = data[i]; + if (FILLCHAR == c1) + break; + + c1 = (char) cvtfind (c1); + c = ((c << 6) & 0xc0) | c1; + (*output)[ret++] = c; + } + } +END: + return ret; +} + +/* ********************* the real stuff ******************* */ + +#define strAUTOncmp(a,b) strncmp(a,b,strlen(b)) + +/** + * Listen to the pipe, decode messages and send to core. + */ +static void * +listenAndDistribute (void *unused) +{ + char *line; + unsigned int linesize; + SMTPMessage *mp; + FILE *fdes; + char *retl; + char *out; + unsigned int size; + GNUNET_TransportPacket *coreMP; + int fd; + unsigned int pos; + + linesize = ((GNUNET_MAX_BUFFER_SIZE * 4 / 3) + 8) * (MAX_CHAR_PER_LINE + 2) / MAX_CHAR_PER_LINE; /* maximum size of a line supported */ + line = GNUNET_malloc (linesize + 2); /* 2 bytes for off-by-one errors, just to be safe... */ + +#define READLINE(l,limit) \ + do { retl = fgets(l, (limit), fdes); \ + if ( (retl == NULL) || (smtp_shutdown == GNUNET_YES)) {\ + goto END; \ + }\ + if (coreAPI->load_monitor != NULL) \ + GNUNET_network_monitor_notify_transmission(coreAPI->load_monitor, GNUNET_ND_DOWNLOAD, strlen(retl)); \ + } while (0) + + + while (smtp_shutdown == GNUNET_NO) + { + fd = OPEN (pipename, O_RDONLY | O_ASYNC); + if (fd == -1) + { + if (smtp_shutdown == GNUNET_NO) + GNUNET_thread_sleep (5 * GNUNET_CRON_SECONDS); + continue; + } + fdes = fdopen (fd, "r"); + while (smtp_shutdown == GNUNET_NO) + { + /* skip until end of header */ + do + { + READLINE (line, linesize); + } + while ((line[0] != '\r') && (line[0] != '\n')); /* expect newline */ + READLINE (line, linesize); /* read base64 encoded message; decode, process */ + pos = 0; + while (1) + { + pos = strlen (line) - 1; /* ignore new line */ + READLINE (&line[pos], linesize - pos); /* read base64 encoded message; decode, process */ + if ((line[pos] == '\r') || (line[pos] == '\n')) + break; /* empty line => end of message! */ + } + size = base64_decode (line, pos, &out); + if (size < sizeof (SMTPMessage)) + { + GNUNET_GE_BREAK (ectx, 0); + GNUNET_free (out); + goto END; + } + + mp = (SMTPMessage *) & out[size - sizeof (SMTPMessage)]; + if (ntohs (mp->header.size) != size) + { + GNUNET_GE_LOG (ectx, + GNUNET_GE_WARNING | GNUNET_GE_BULK | + GNUNET_GE_USER, + _ + ("Received malformed message via %s. Ignored.\n"), + "SMTP"); +#if DEBUG_SMTP + GNUNET_GE_LOG (ectx, + GNUNET_GE_DEBUG | GNUNET_GE_REQUEST | + GNUNET_GE_USER, + "Size returned by base64=%d, in the msg=%d.\n", + size, ntohl (mp->size)); +#endif + GNUNET_free (out); + goto END; + } + if (stats != NULL) + stats->change (stat_bytesReceived, size); + coreMP = GNUNET_malloc (sizeof (GNUNET_TransportPacket)); + coreMP->msg = out; + coreMP->size = size - sizeof (SMTPMessage); + coreMP->tsession = NULL; + coreMP->sender = mp->sender; +#if DEBUG_SMTP + GNUNET_GE_LOG (ectx, + GNUNET_GE_DEBUG | GNUNET_GE_REQUEST | GNUNET_GE_USER, + "SMTP message passed to the core.\n"); +#endif + + coreAPI->receive (coreMP); + } + END: +#if DEBUG_SMTP + GNUNET_GE_LOG (ectx, + GNUNET_GE_DEBUG | GNUNET_GE_REQUEST | GNUNET_GE_USER, + "SMTP message processed.\n"); +#endif + if (fdes != NULL) + fclose (fdes); + } + GNUNET_free (line); + return NULL; +} + +/* *************** API implementation *************** */ + +/** + * Verify that a hello-Message is correct (a node is reachable at that + * address). Since the reply will be asynchronous, a method must be + * called on success. + * + * @param hello the hello message to verify + * (the signature/crc have been verified before) + * @return GNUNET_OK on success, GNUNET_SYSERR on error + */ +static int +api_verify_hello (const GNUNET_MessageHello * hello) +{ + const EmailAddress *maddr; + + maddr = (const EmailAddress *) &hello[1]; + if ((ntohs (hello->header.size) != + sizeof (GNUNET_MessageHello) + ntohs (hello->senderAddressSize)) || + (maddr->senderAddress[ntohs (hello->senderAddressSize) - 1 - + FILTER_STRING_SIZE] != '\0')) + { + GNUNET_GE_BREAK (ectx, 0); + return GNUNET_SYSERR; /* obviously invalid */ + } + if (NULL == strstr (maddr->filter, ": ")) + return GNUNET_SYSERR; + return GNUNET_OK; +} + +/** + * Create a hello-Message for the current node. The hello is created + * without signature and without a timestamp. The GNUnet core will + * GNUNET_RSA_sign the message and add an expiration time. + * + * @return hello on success, NULL on error + */ +static GNUNET_MessageHello * +api_create_hello () +{ + GNUNET_MessageHello *msg; + char *filter; + EmailAddress *haddr; + int i; + + GNUNET_GC_get_configuration_value_string (coreAPI->cfg, + "SMTP", "FILTER", + "X-mailer: GNUnet", &filter); + if (NULL == strstr (filter, ": ")) + { + GNUNET_GE_LOG (ectx, + GNUNET_GE_WARNING | GNUNET_GE_BULK | GNUNET_GE_USER, + _("SMTP filter string to invalid, lacks ': '\n")); + GNUNET_free (filter); + return NULL; + } + + if (strlen (filter) > FILTER_STRING_SIZE) + { + filter[FILTER_STRING_SIZE] = '\0'; + GNUNET_GE_LOG (ectx, + GNUNET_GE_WARNING | GNUNET_GE_BULK | GNUNET_GE_USER, + _("SMTP filter string to long, capped to `%s'\n"), + filter); + } + i = (strlen (email) + 8) & (~7); /* make multiple of 8 */ + msg = + GNUNET_malloc (sizeof (GNUNET_MessageHello) + sizeof (EmailAddress) + i); + memset (msg, 0, sizeof (GNUNET_MessageHello) + sizeof (EmailAddress) + i); + haddr = (EmailAddress *) & msg[1]; + memset (&haddr->filter[0], 0, FILTER_STRING_SIZE); + strcpy (&haddr->filter[0], filter); + memcpy (&haddr->senderAddress[0], email, strlen (email) + 1); + msg->senderAddressSize = htons (strlen (email) + 1 + sizeof (EmailAddress)); + msg->protocol = htons (GNUNET_TRANSPORT_PROTOCOL_NUMBER_SMTP); + msg->MTU = htonl (smtpAPI.mtu); + msg->header.size = htons (GNUNET_sizeof_hello (msg)); + if (api_verify_hello (msg) == GNUNET_SYSERR) + GNUNET_GE_ASSERT (ectx, 0); + GNUNET_free (filter); + return msg; +} + +struct GetMessageClosure +{ + unsigned int esize; + unsigned int pos; + char *ebody; +}; + +static const char * +get_message (void **buf, int *len, void *cls) +{ + struct GetMessageClosure *gmc = cls; + + *buf = NULL; + if (len == NULL) + { + gmc->pos = 0; + return NULL; + } + if (gmc->pos == gmc->esize) + return NULL; /* done */ + *len = gmc->esize; + gmc->pos = gmc->esize; + return gmc->ebody; +} + +/** + * Send a message to the specified remote node. + * + * @param tsession the GNUNET_MessageHello identifying the remote node + * @param message what to send + * @param size the size of the message + * @return GNUNET_SYSERR on error, GNUNET_OK on success + */ +static int +api_send (GNUNET_TSession * tsession, + const void *msg, const unsigned int size, int important) +{ + const GNUNET_MessageHello *hello; + const EmailAddress *haddr; + char *m; + char *filter; + char *fvalue; + SMTPMessage *mp; + struct GetMessageClosure gm_cls; + smtp_session_t session; + smtp_message_t message; + smtp_recipient_t recipient; +#define EBUF_LEN 128 + char ebuf[EBUF_LEN]; + GNUNET_CronTime now; + + if (smtp_shutdown == GNUNET_YES) + return GNUNET_SYSERR; + if ((size == 0) || (size > smtpAPI.mtu)) + { + GNUNET_GE_BREAK (ectx, 0); + return GNUNET_SYSERR; + } + now = GNUNET_get_time (); + if ((important != GNUNET_YES) && + ((now - last_transmission) * rate_limit) < GNUNET_CRON_HOURS) + return GNUNET_NO; /* rate too high */ + last_transmission = now; + + hello = (const GNUNET_MessageHello *) tsession->internal; + if (hello == NULL) + return GNUNET_SYSERR; + GNUNET_mutex_lock (lock); + session = smtp_create_session (); + if (session == NULL) + { + GNUNET_GE_LOG (ectx, + GNUNET_GE_ERROR | GNUNET_GE_ADMIN | GNUNET_GE_USER | + GNUNET_GE_IMMEDIATE, + _("SMTP: `%s' failed: %s.\n"), + "smtp_create_session", + smtp_strerror (smtp_errno (), ebuf, EBUF_LEN)); + GNUNET_mutex_unlock (lock); + return GNUNET_SYSERR; + } + if (0 == smtp_set_server (session, smtp_server_name)) + { + GNUNET_GE_LOG (ectx, + GNUNET_GE_ERROR | GNUNET_GE_ADMIN | GNUNET_GE_USER | + GNUNET_GE_IMMEDIATE, + _("SMTP: `%s' failed: %s.\n"), + "smtp_set_server", + smtp_strerror (smtp_errno (), ebuf, EBUF_LEN)); + smtp_destroy_session (session); + GNUNET_mutex_unlock (lock); + return GNUNET_SYSERR; + } + haddr = (const EmailAddress *) &hello[1]; + message = smtp_add_message (session); + if (message == NULL) + { + GNUNET_GE_LOG (ectx, + GNUNET_GE_WARNING | GNUNET_GE_ADMIN | GNUNET_GE_USER | + GNUNET_GE_BULK, + _("SMTP: `%s' failed: %s.\n"), + "smtp_add_message", + smtp_strerror (smtp_errno (), ebuf, EBUF_LEN)); + smtp_destroy_session (session); + GNUNET_mutex_unlock (lock); + return GNUNET_SYSERR; + } + smtp_set_header (message, "To", NULL, haddr->senderAddress); + smtp_set_header (message, "From", NULL, email); + + filter = GNUNET_strdup (haddr->filter); + fvalue = strstr (filter, ": "); + GNUNET_GE_ASSERT (NULL, NULL != fvalue); + fvalue[0] = '\0'; + fvalue += 2; + if (0 == smtp_set_header (message, filter, fvalue)) + { + GNUNET_GE_LOG (ectx, + GNUNET_GE_WARNING | GNUNET_GE_ADMIN | GNUNET_GE_USER | + GNUNET_GE_BULK, + _("SMTP: `%s' failed: %s.\n"), + "smtp_set_header", + smtp_strerror (smtp_errno (), ebuf, EBUF_LEN)); + smtp_destroy_session (session); + GNUNET_mutex_unlock (lock); + GNUNET_free (filter); + return GNUNET_SYSERR; + } + GNUNET_free (filter); + m = GNUNET_malloc (size + sizeof (SMTPMessage)); + memcpy (m, msg, size); + mp = (SMTPMessage *) & m[size]; + mp->header.size = htons (size + sizeof (SMTPMessage)); + mp->header.type = htons (0); + mp->sender = *coreAPI->my_identity; + gm_cls.ebody = NULL; + gm_cls.pos = 0; + gm_cls.esize = + base64_encode (m, size + sizeof (SMTPMessage), &gm_cls.ebody); + GNUNET_free (m); + if (0 == smtp_size_set_estimate (message, gm_cls.esize)) + { + GNUNET_GE_LOG (ectx, + GNUNET_GE_WARNING | GNUNET_GE_ADMIN | GNUNET_GE_USER | + GNUNET_GE_BULK, + _("SMTP: `%s' failed: %s.\n"), + "smtp_size_set_estimate", + smtp_strerror (smtp_errno (), ebuf, EBUF_LEN)); + } + if (0 == smtp_set_messagecb (message, &get_message, &gm_cls)) + { + GNUNET_GE_LOG (ectx, + GNUNET_GE_WARNING | GNUNET_GE_ADMIN | GNUNET_GE_USER | + GNUNET_GE_BULK, + _("SMTP: `%s' failed: %s.\n"), + "smtp_set_messagecb", + smtp_strerror (smtp_errno (), ebuf, EBUF_LEN)); + smtp_destroy_session (session); + GNUNET_mutex_unlock (lock); + GNUNET_free (gm_cls.ebody); + return GNUNET_SYSERR; + } + recipient = smtp_add_recipient (message, haddr->senderAddress); + if (recipient == NULL) + { + GNUNET_GE_LOG (ectx, + GNUNET_GE_WARNING | GNUNET_GE_ADMIN | GNUNET_GE_USER | + GNUNET_GE_BULK, + _("SMTP: `%s' failed: %s.\n"), + "smtp_add_recipient", + smtp_strerror (smtp_errno (), ebuf, EBUF_LEN)); + smtp_destroy_session (session); + GNUNET_mutex_unlock (lock); + return GNUNET_SYSERR; + } + if (0 == smtp_start_session (session)) + { + GNUNET_GE_LOG (ectx, + GNUNET_GE_WARNING | GNUNET_GE_ADMIN | GNUNET_GE_USER | + GNUNET_GE_BULK, + _("SMTP: `%s' failed: %s.\n"), + "smtp_start_session", + smtp_strerror (smtp_errno (), ebuf, EBUF_LEN)); + smtp_destroy_session (session); + GNUNET_mutex_unlock (lock); + GNUNET_free (gm_cls.ebody); + return GNUNET_SYSERR; + } + if (stats != NULL) + stats->change (stat_bytesSent, size); + if (coreAPI->load_monitor != NULL) + GNUNET_network_monitor_notify_transmission (coreAPI->load_monitor, + GNUNET_ND_UPLOAD, + gm_cls.esize); + smtp_message_reset_status (message); /* this is needed to plug a 28-byte/message memory leak in libesmtp */ + smtp_destroy_session (session); + GNUNET_mutex_unlock (lock); + GNUNET_free (gm_cls.ebody); + return GNUNET_OK; +} + +/** + * Establish a connection to a remote node. + * @param helo the hello-Message for the target node + * @param tsessionPtr the session handle that is to be set + * @return GNUNET_OK on success, GNUNET_SYSERR if the operation failed + */ +static int +api_connect (const GNUNET_MessageHello * hello, + GNUNET_TSession ** tsessionPtr, int may_reuse) +{ + GNUNET_TSession *tsession; + + tsession = GNUNET_malloc (sizeof (GNUNET_TSession)); + tsession->internal = GNUNET_malloc (GNUNET_sizeof_hello (hello)); + tsession->peer = hello->senderIdentity; + memcpy (tsession->internal, hello, GNUNET_sizeof_hello (hello)); + tsession->ttype = smtpAPI.protocol_number; + (*tsessionPtr) = tsession; + return GNUNET_OK; +} + +/** + * Disconnect from a remote node. + * + * @param tsession the session that is closed + * @return GNUNET_OK on success, GNUNET_SYSERR if the operation failed + */ +static int +api_disconnect (GNUNET_TSession * tsession) +{ + if (tsession != NULL) + { + if (tsession->internal != NULL) + GNUNET_free (tsession->internal); + GNUNET_free (tsession); + } + return GNUNET_OK; +} + +/** + * Start the server process to receive inbound traffic. + * @return GNUNET_OK on success, GNUNET_SYSERR if the operation failed + */ +static int +api_start_transport_server () +{ + smtp_shutdown = GNUNET_NO; + /* initialize SMTP network */ + dispatchThread = + GNUNET_thread_create (&listenAndDistribute, NULL, 1024 * 4); + if (dispatchThread == NULL) + { + GNUNET_GE_DIE_STRERROR (ectx, + GNUNET_GE_ADMIN | GNUNET_GE_BULK | + GNUNET_GE_FATAL, "pthread_create"); + return GNUNET_SYSERR; + } + return GNUNET_OK; +} + +/** + * Shutdown the server process (stop receiving inbound traffic). Maybe + * restarted later! + */ +static int +api_stop_transport_server () +{ + void *unused; + + smtp_shutdown = GNUNET_YES; + GNUNET_thread_stop_sleep (dispatchThread); + GNUNET_thread_join (dispatchThread, &unused); + return GNUNET_OK; +} + +/** + * Convert SMTP hello to an IP address (always fails). + */ +static int +api_hello_to_address (const GNUNET_MessageHello * hello, + void **sa, unsigned int *sa_len) +{ + return GNUNET_SYSERR; +} + +/** + * Always fails. + */ +static int +api_associate (GNUNET_TSession * tsession) +{ + return GNUNET_SYSERR; /* SMTP connections can never be associated */ +} + +/** + * Always succeeds (for now; we should look at adding + * frequency limits to SMTP in the future!). + */ +static int +api_test_would_try (GNUNET_TSession * tsession, const unsigned int size, + int important) +{ + return GNUNET_OK; /* we always try... */ +} + +/** + * The exported method. Makes the core api available via a global and + * returns the smtp transport API. + */ +GNUNET_TransportAPI * +inittransport_smtp (GNUNET_CoreAPIForTransport * core) +{ + + + unsigned long long mtu; + struct sigaction sa; + + coreAPI = core; + ectx = core->ectx; + if (!GNUNET_GC_have_configuration_value (coreAPI->cfg, "SMTP", "EMAIL")) + { + GNUNET_GE_LOG (ectx, + GNUNET_GE_ERROR | GNUNET_GE_BULK | GNUNET_GE_USER, + _ + ("No email-address specified, can not start SMTP transport.\n")); + return NULL; + } + GNUNET_GC_get_configuration_value_number (coreAPI->cfg, + "SMTP", + "MTU", + 1200, + SMTP_MESSAGE_SIZE, + SMTP_MESSAGE_SIZE, &mtu); + GNUNET_GC_get_configuration_value_number (coreAPI->cfg, + "SMTP", + "RATELIMIT", + 0, 0, 1024 * 1024, &rate_limit); + stats = coreAPI->service_request ("stats"); + if (stats != NULL) + { + stat_bytesReceived + = stats->create (gettext_noop ("# bytes received via SMTP")); + stat_bytesSent = stats->create (gettext_noop ("# bytes sent via SMTP")); + stat_bytesDropped + = stats->create (gettext_noop ("# bytes dropped by SMTP (outgoing)")); + } + GNUNET_GC_get_configuration_value_filename (coreAPI->cfg, + "SMTP", + "PIPE", + GNUNET_DEFAULT_DAEMON_VAR_DIRECTORY + "/smtp-pipe", &pipename); + UNLINK (pipename); + if (0 != mkfifo (pipename, S_IWUSR | S_IRUSR | S_IWGRP | S_IWOTH)) + { + GNUNET_GE_LOG_STRERROR (ectx, + GNUNET_GE_ADMIN | GNUNET_GE_BULK | + GNUNET_GE_FATAL, "mkfifo"); + GNUNET_free (pipename); + coreAPI->service_release (stats); + stats = NULL; + return NULL; + } + /* we need to allow the mailer program to send us messages; + easiest done by giving it write permissions (see Mantis #1142) */ + if (0 != chmod (pipename, S_IWUSR | S_IRUSR | S_IWGRP | S_IWOTH)) + GNUNET_GE_LOG_STRERROR (ectx, + GNUNET_GE_ADMIN | GNUNET_GE_BULK | + GNUNET_GE_WARNING, "chmod"); + GNUNET_GC_get_configuration_value_string (coreAPI->cfg, + "SMTP", "EMAIL", NULL, &email); + lock = GNUNET_mutex_create (GNUNET_NO); + GNUNET_GC_get_configuration_value_string (coreAPI->cfg, + "SMTP", + "SERVER", + "localhost:25", + &smtp_server_name); + sa.sa_handler = SIG_IGN; + sigemptyset (&sa.sa_mask); + sa.sa_flags = 0; + sigaction (SIGPIPE, &sa, &old_handler); + + smtpAPI.protocol_number = GNUNET_TRANSPORT_PROTOCOL_NUMBER_SMTP; + smtpAPI.mtu = mtu - sizeof (SMTPMessage); + smtpAPI.cost = 50; + smtpAPI.hello_verify = &api_verify_hello; + smtpAPI.hello_create = &api_create_hello; + smtpAPI.connect = &api_connect; + smtpAPI.send = &api_send; + smtpAPI.associate = &api_associate; + smtpAPI.disconnect = &api_disconnect; + smtpAPI.server_start = &api_start_transport_server; + smtpAPI.server_stop = &api_stop_transport_server; + smtpAPI.hello_to_address = &api_hello_to_address; + smtpAPI.send_now_test = &api_test_would_try; + return &smtpAPI; +} + +void +donetransport_smtp () +{ + sigaction (SIGPIPE, &old_handler, NULL); + GNUNET_free (smtp_server_name); + if (stats != NULL) + { + coreAPI->service_release (stats); + stats = NULL; + } + GNUNET_mutex_destroy (lock); + lock = NULL; + UNLINK (pipename); + GNUNET_free (pipename); + pipename = NULL; + GNUNET_free (email); + email = NULL; +} + +/* end of smtp.c */ diff --git a/src/transport/plugin_transport_tcp.c b/src/transport/plugin_transport_tcp.c new file mode 100644 index 000000000..c87056e71 --- /dev/null +++ b/src/transport/plugin_transport_tcp.c @@ -0,0 +1,1782 @@ +/* + This file is part of GNUnet + (C) 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009 Christian Grothoff (and other contributing authors) + + GNUnet is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published + by the Free Software Foundation; either version 2, 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 + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with GNUnet; see the file COPYING. If not, write to the + Free Software Foundation, Inc., 59 Temple Place - Suite 330, + Boston, MA 02111-1307, USA. +*/ + +/** + * @file transport/plugin_transport_tcp.c + * @brief Implementation of the TCP transport service + * @author Christian Grothoff + */ + +#include "platform.h" +#include "gnunet_hello_lib.h" +#include "gnunet_network_lib.h" +#include "gnunet_os_lib.h" +#include "gnunet_peerinfo_service.h" +#include "gnunet_protocols.h" +#include "gnunet_resolver_service.h" +#include "gnunet_server_lib.h" +#include "gnunet_service_lib.h" +#include "gnunet_statistics_service.h" +#include "gnunet_transport_service.h" +#include "plugin_transport.h" +#include "transport.h" + +#define DEBUG_TCP GNUNET_NO + +/** + * After how long do we expire an address that we + * learned from another peer if it is not reconfirmed + * by anyone? + */ +#define LEARNED_ADDRESS_EXPIRATION GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_HOURS, 6) + +/** + * How long until we give up on transmitting the welcome message? + */ +#define WELCOME_TIMEOUT GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS, 30) + +/** + * How long until we give up on transmitting the welcome message? + */ +#define HOSTNAME_RESOLVE_TIMEOUT GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS, 5) + +/** + * For how many messages back to we keep transmission times? + */ +#define ACK_LOG_SIZE 32 + +/** + * Initial handshake message for a session. This header + * is followed by the address that the other peer used to + * connect to us (so that we may learn it) or the address + * that the other peer got from the accept call. + */ +struct WelcomeMessage +{ + struct GNUNET_MessageHeader header; + + /** + * Identity of the node connecting (TCP client) + */ + struct GNUNET_PeerIdentity clientIdentity; + +}; + + +/** + * Encapsulation for normal TCP traffic. + */ +struct DataMessage +{ + struct GNUNET_MessageHeader header; + + /** + * For alignment. + */ + uint32_t reserved GNUNET_PACKED; + + /** + * Number of the last message that was received from the other peer. + */ + uint64_t ack_in GNUNET_PACKED; + + /** + * Number of this outgoing message. + */ + uint64_t ack_out GNUNET_PACKED; + + /** + * How long was sending this ack delayed by the other peer + * (estimate). The receiver of this message can use the delay + * between sending his message number 'ack' and receiving this ack + * minus the delay as an estimate of the round-trip time. + */ + struct GNUNET_TIME_RelativeNBO delay; + +}; + + +/** + * Encapsulation of all of the state of the plugin. + */ +struct Plugin; + + +/** + * Information kept for each message that is yet to + * be transmitted. + */ +struct PendingMessage +{ + + /** + * This is a linked list. + */ + struct PendingMessage *next; + + /** + * The pending message, pointer to the end + * of this struct, do not free! + */ + struct GNUNET_MessageHeader *msg; + + + /** + * Continuation function to call once the message + * has been sent. Can be NULL if there is no + * continuation to call. + */ + GNUNET_TRANSPORT_TransmitContinuation transmit_cont; + + /** + * Closure for transmit_cont. + */ + void *transmit_cont_cls; + + /** + * Timeout value for the pending message. + */ + struct GNUNET_TIME_Absolute timeout; + + /** + * GNUNET_YES if this is a welcome message; + * otherwise this should be a DATA message. + */ + int is_welcome; + +}; + + +/** + * Session handle for TCP connections. + */ +struct Session +{ + + /** + * Stored in a linked list. + */ + struct Session *next; + + /** + * Pointer to the global plugin struct. + */ + struct Plugin *plugin; + + /** + * The client (used to identify this connection) + */ + struct GNUNET_SERVER_Client *client; + + /** + * gnunet-service-transport context for this connection. + */ + struct ReadyList *service_context; + + /** + * Messages currently pending for transmission + * to this peer, if any. + */ + struct PendingMessage *pending_messages; + + /** + * Handle for pending transmission request. + */ + struct GNUNET_NETWORK_TransmitHandle *transmit_handle; + + /** + * To whom are we talking to (set to our identity + * if we are still waiting for the welcome message) + */ + struct GNUNET_PeerIdentity target; + + /** + * At what time did we reset last_received last? + */ + struct GNUNET_TIME_Absolute last_quota_update; + + /** + * Address of the other peer if WE initiated the connection + * (and hence can be sure what it is), otherwise NULL. + */ + void *connect_addr; + + /** + * How many bytes have we received since the "last_quota_update" + * timestamp? + */ + uint64_t last_received; + + /** + * Our current latency estimate (in ms). + */ + double latency_estimate; + + /** + * Time when we generated the last ACK_LOG_SIZE acks. + * (the "last" refers to the "out_msg_counter" here) + */ + struct GNUNET_TIME_Absolute gen_time[ACK_LOG_SIZE]; + + /** + * Our current sequence number. + */ + uint64_t out_msg_counter; + + /** + * Highest received incoming sequence number. + */ + uint64_t max_in_msg_counter; + + /** + * Number of bytes per ms that this peer is allowed + * to send to us. + */ + uint32_t quota_in; + + /** + * Length of connect_addr, can be 0. + */ + size_t connect_alen; + + /** + * Are we still expecting the welcome message? (GNUNET_YES/GNUNET_NO) + */ + int expecting_welcome; + + /** + * Are we still trying to connect? + */ + int still_connecting; + +}; + + +/** + * Encapsulation of all of the state of the plugin. + */ +struct Plugin +{ + /** + * Our environment. + */ + struct GNUNET_TRANSPORT_PluginEnvironment *env; + + /** + * The listen socket. + */ + struct GNUNET_NETWORK_SocketHandle *lsock; + + /** + * List of open TCP sessions. + */ + struct Session *sessions; + + /** + * Handle for the statistics service. + */ + struct GNUNET_STATISTICS_Handle *statistics; + + /** + * Handle to the network service. + */ + struct GNUNET_SERVICE_Context *service; + + /** + * Handle to the server for this service. + */ + struct GNUNET_SERVER_Handle *server; + + /** + * Copy of the handler array where the closures are + * set to this struct's instance. + */ + struct GNUNET_SERVER_MessageHandler *handlers; + + /** + * ID of task used to update our addresses when one expires. + */ + GNUNET_SCHEDULER_TaskIdentifier address_update_task; + + /** + * Port that we are actually listening on. + */ + uint16_t open_port; + + /** + * Port that the user said we would have visible to the + * rest of the world. + */ + uint16_t adv_port; + +}; + + +/** + * Find the session handle for the given peer. + */ +static struct Session * +find_session_by_target (struct Plugin *plugin, + const struct GNUNET_PeerIdentity *target) +{ + struct Session *ret; + + ret = plugin->sessions; + while ((ret != NULL) && + (0 != memcmp (target, + &ret->target, sizeof (struct GNUNET_PeerIdentity)))) + ret = ret->next; + return ret; +} + + +/** + * Find the session handle for the given peer. + */ +static struct Session * +find_session_by_client (struct Plugin *plugin, + const struct GNUNET_SERVER_Client *client) +{ + struct Session *ret; + + ret = plugin->sessions; + while ((ret != NULL) && (client != ret->client)) + ret = ret->next; + return ret; +} + + +/** + * Create a welcome message. + */ +static struct PendingMessage * +create_welcome (size_t addrlen, const void *addr, struct Plugin *plugin) +{ + struct PendingMessage *pm; + struct WelcomeMessage *welcome; + + pm = GNUNET_malloc (sizeof (struct PendingMessage) + + sizeof (struct WelcomeMessage) + addrlen); + pm->msg = (struct GNUNET_MessageHeader *) &pm[1]; + welcome = (struct WelcomeMessage *) &pm[1]; + welcome->header.size = htons (sizeof (struct WelcomeMessage) + addrlen); + welcome->header.type = htons (GNUNET_MESSAGE_TYPE_TRANSPORT_TCP_WELCOME); + GNUNET_CRYPTO_hash (plugin->env->my_public_key, + sizeof (struct GNUNET_CRYPTO_RsaPublicKeyBinaryEncoded), + &welcome->clientIdentity.hashPubKey); + memcpy (&welcome[1], addr, addrlen); + pm->timeout = GNUNET_TIME_relative_to_absolute (WELCOME_TIMEOUT); + pm->is_welcome = GNUNET_YES; + return pm; +} + + +/** + * Create a new session using the specified address + * for the welcome message. + * + * @param plugin us + * @param target peer to connect to + * @param client client to use + * @param addrlen IPv4 or IPv6 + * @param addr either struct sockaddr_in or struct sockaddr_in6 + * @return NULL connection failed / invalid address + */ +static struct Session * +create_session (struct Plugin *plugin, + const struct GNUNET_PeerIdentity *target, + struct GNUNET_SERVER_Client *client, + const void *addr, size_t addrlen) +{ + struct Session *ret; + + ret = GNUNET_malloc (sizeof (struct Session)); + ret->plugin = plugin; + ret->next = plugin->sessions; + plugin->sessions = ret; + ret->client = client; + ret->target = *target; + ret->last_quota_update = GNUNET_TIME_absolute_get (); + ret->quota_in = plugin->env->default_quota_in; + ret->expecting_welcome = GNUNET_YES; + ret->pending_messages = create_welcome (addrlen, addr, plugin); + return ret; +} + + +/** + * Create a new session connecting to the specified + * target at the specified address. + * + * @param plugin us + * @param target peer to connect to + * @param addrlen IPv4 or IPv6 + * @param addr either struct sockaddr_in or struct sockaddr_in6 + * @return NULL connection failed / invalid address + */ +static struct Session * +connect_and_create_session (struct Plugin *plugin, + const struct GNUNET_PeerIdentity *target, + const void *addr, size_t addrlen) +{ + struct GNUNET_SERVER_Client *client; + struct GNUNET_NETWORK_SocketHandle *conn; + struct Session *session; + int af; + char buf[INET6_ADDRSTRLEN]; + uint16_t port; + + session = plugin->sessions; + while (session != NULL) + { + if ((0 == memcmp (target, + &session->target, + sizeof (struct GNUNET_PeerIdentity))) && + (session->connect_alen == addrlen) && + (0 == memcmp (session->connect_addr, addr, addrlen))) + return session; /* already exists! */ + session = session->next; + } + + if (addrlen == sizeof (struct sockaddr_in)) + { + af = AF_INET; + inet_ntop (af, + &((struct sockaddr_in *) addr)->sin_addr, buf, sizeof (buf)); + port = ntohs (((struct sockaddr_in *) addr)->sin_port); + } + else if (addrlen == sizeof (struct sockaddr_in6)) + { + af = AF_INET6; + inet_ntop (af, + &((struct sockaddr_in6 *) addr)->sin6_addr, + buf, sizeof (buf)); + port = ntohs (((struct sockaddr_in6 *) addr)->sin6_port); + } + else + { + GNUNET_break_op (0); + return NULL; /* invalid address */ + } + conn = GNUNET_NETWORK_socket_create_from_sockaddr (plugin->env->sched, + af, + addr, + addrlen, + GNUNET_SERVER_MAX_MESSAGE_SIZE); + if (conn == NULL) + { +#if DEBUG_TCP + GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, + "tcp", + "Failed to create connection to peer at `%s:%u'.\n", + buf, port); +#endif + return NULL; + } + client = GNUNET_SERVER_connect_socket (plugin->server, conn); + GNUNET_assert (client != NULL); + session = create_session (plugin, target, client, addr, addrlen); + session->connect_alen = addrlen; + session->connect_addr = GNUNET_malloc (addrlen); + memcpy (session->connect_addr, addr, addrlen); +#if DEBUG_TCP + GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, + "tcp", + "Creating new session %p with `%s:%u' based on `%s' request.\n", + session, buf, port, "send_to"); +#endif + return session; +} + + +/** + * If we have pending messages, ask the server to + * transmit them (schedule the respective tasks, etc.) + * + * @param session for which session should we do this + */ +static void process_pending_messages (struct Session *session); + + +/** + * Function called to notify a client about the socket + * begin ready to queue more data. "buf" will be + * NULL and "size" zero if the socket was closed for + * writing in the meantime. + * + * @param cls closure + * @param size number of bytes available in buf + * @param buf where the callee should write the message + * @return number of bytes written to buf + */ +static size_t +do_transmit (void *cls, size_t size, void *buf) +{ + struct Session *session = cls; + struct PendingMessage *pm; + char *cbuf; + uint16_t msize; + size_t ret; + struct DataMessage *dm; + + session->transmit_handle = NULL; + if (buf == NULL) + { +#if DEBUG_TCP + GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, + "tcp", "Timeout trying to transmit\n"); +#endif + /* timeout */ + while (NULL != (pm = session->pending_messages)) + { + session->pending_messages = pm->next; + if (pm->transmit_cont != NULL) + pm->transmit_cont (pm->transmit_cont_cls, + session->service_context, + &session->target, GNUNET_SYSERR); + GNUNET_free (pm); + } + return 0; + } + ret = 0; + cbuf = buf; + while (NULL != (pm = session->pending_messages)) + { + if (pm->is_welcome) + { + if (size < (msize = htons (pm->msg->size))) + break; + memcpy (cbuf, pm->msg, msize); + cbuf += msize; + ret += msize; + size -= msize; + } + else + { + if (size < + sizeof (struct DataMessage) + (msize = htons (pm->msg->size))) + break; + dm = (struct DataMessage *) cbuf; + dm->header.size = htons (sizeof (struct DataMessage) + msize); + dm->header.type = htons (GNUNET_MESSAGE_TYPE_TRANSPORT_TCP_DATA); + dm->ack_out = GNUNET_htonll (++session->out_msg_counter); + dm->ack_in = GNUNET_htonll (session->max_in_msg_counter); + cbuf += sizeof (struct DataMessage); + ret += sizeof (struct DataMessage); + size -= sizeof (struct DataMessage); + memcpy (cbuf, pm->msg, msize); + cbuf += msize; + ret += msize; + size -= msize; + } + session->pending_messages = pm->next; + if (pm->transmit_cont != NULL) + pm->transmit_cont (pm->transmit_cont_cls, + session->service_context, + &session->target, GNUNET_OK); + GNUNET_free (pm); + session->gen_time[session->out_msg_counter % ACK_LOG_SIZE] + = GNUNET_TIME_absolute_get (); + } + process_pending_messages (session); +#if DEBUG_TCP || 1 + GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, + "tcp", "Transmitting %u bytes\n", ret); +#endif + return ret; +} + + +/** + * If we have pending messages, ask the server to + * transmit them (schedule the respective tasks, etc.) + * + * @param session for which session should we do this + */ +static void +process_pending_messages (struct Session *session) +{ + GNUNET_assert (session->client != NULL); + if (session->pending_messages == NULL) + return; + if (session->transmit_handle != NULL) + return; + session->transmit_handle + = GNUNET_SERVER_notify_transmit_ready (session->client, + htons (session->pending_messages-> + msg->size) + + (session->pending_messages-> + is_welcome ? 0 : sizeof (struct + DataMessage)), + GNUNET_TIME_absolute_get_remaining + (session->pending_messages[0]. + timeout), &do_transmit, session); +} + + +/** + * Function that can be used by the transport service to transmit + * a message using the plugin using a fresh connection (even if + * we already have a connection to this peer, this function is + * required to establish a new one). + * + * @param cls closure + * @param target who should receive this message + * @param msg1 first message to transmit + * @param msg2 second message to transmit (can be NULL) + * @param timeout how long should we try to transmit these? + * @param addrlen length of the address + * @param addr the address + * @return session if the transmission has been scheduled + * NULL if the address format is invalid + */ +static void * +tcp_plugin_send_to (void *cls, + const struct GNUNET_PeerIdentity *target, + const struct GNUNET_MessageHeader *msg1, + const struct GNUNET_MessageHeader *msg2, + struct GNUNET_TIME_Relative timeout, + const void *addr, size_t addrlen) +{ + struct Plugin *plugin = cls; + struct Session *session; + struct PendingMessage *pl; + struct PendingMessage *pm; + + session = connect_and_create_session (plugin, target, addr, addrlen); + if (session == NULL) + { +#if DEBUG_TCP + GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, + "tcp", "Failed to create fresh session.\n"); +#endif + return NULL; + } + pl = NULL; + if (msg2 != NULL) + { + pm = GNUNET_malloc (sizeof (struct PendingMessage) + + ntohs (msg2->size)); + pm->msg = (struct GNUNET_MessageHeader *) &pm[1]; + memcpy (pm->msg, msg2, ntohs (msg2->size)); + pm->timeout = GNUNET_TIME_relative_to_absolute (timeout); + pm->is_welcome = GNUNET_NO; + pl = pm; + } + if (msg1 != NULL) + { + pm = GNUNET_malloc (sizeof (struct PendingMessage) + + ntohs (msg1->size)); + pm->msg = (struct GNUNET_MessageHeader *) &pm[1]; + memcpy (pm->msg, msg1, ntohs (msg1->size)); + pm->timeout = GNUNET_TIME_relative_to_absolute (timeout); + pm->is_welcome = GNUNET_NO; + pm->next = pl; + pl = pm; + } + /* append */ + if (session->pending_messages != NULL) + { + pm = session->pending_messages; + while (pm->next != NULL) + pm = pm->next; + pm->next = pl; + } + else + { + session->pending_messages = pl; + } + process_pending_messages (session); + return session; +} + + +/** + * Functions with this signature are called whenever we need + * to close a session due to a disconnect or failure to + * establish a connection. + * + * @param session session to close down + */ +static void +disconnect_session (struct Session *session) +{ + struct Session *prev; + struct Session *pos; + struct PendingMessage *pm; + +#if DEBUG_TCP + GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, + "tcp", + "Disconnecting from other peer (session %p).\n", session); +#endif + /* remove from session list */ + prev = NULL; + pos = session->plugin->sessions; + while (pos != session) + { + prev = pos; + pos = pos->next; + } + if (prev == NULL) + session->plugin->sessions = session->next; + else + prev->next = session->next; + /* clean up state */ + if (session->client != NULL) + { +#if DEBUG_TCP + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Disconnecting from client address %p\n", session->client); +#endif + GNUNET_SERVER_client_drop (session->client); + session->client = NULL; + } + if (session->transmit_handle != NULL) + { + GNUNET_NETWORK_notify_transmit_ready_cancel (session->transmit_handle); + session->transmit_handle = NULL; + } + while (NULL != (pm = session->pending_messages)) + { + session->pending_messages = pm->next; + if (NULL != pm->transmit_cont) + pm->transmit_cont (pm->transmit_cont_cls, + session->service_context, + &session->target, GNUNET_SYSERR); + GNUNET_free (pm); + } + /* notify transport service about disconnect */ + session->plugin->env->receive (session->plugin->env->cls, + session, + session->service_context, + GNUNET_TIME_UNIT_ZERO, + &session->target, NULL); + GNUNET_free_non_null (session->connect_addr); + GNUNET_free (session); +} + + +/** + * Iterator callback to go over all addresses. If we get + * a TCP address, increment the counter + * + * @param cls closure, points to the counter + * @param tname name of the transport + * @param expiration expiration time + * @param addr the address + * @param addrlen length of the address + * @return GNUNET_OK to keep the address, + * GNUNET_NO to delete it from the HELLO + * GNUNET_SYSERR to stop iterating (but keep current address) + */ +static int +count_tcp_addresses (void *cls, + const char *tname, + struct GNUNET_TIME_Absolute expiration, + const void *addr, size_t addrlen) +{ + unsigned int *counter = cls; + + if (0 != strcmp (tname, "tcp")) + return GNUNET_OK; /* not one of ours */ + (*counter)++; + return GNUNET_OK; /* failed to connect */ +} + + +struct ConnectContext +{ + struct Plugin *plugin; + + struct GNUNET_NETWORK_SocketHandle *sa; + + struct PendingMessage *welcome; + + unsigned int pos; +}; + + +/** + * Iterator callback to go over all addresses. If we get + * the "pos" TCP address, try to connect to it. + * + * @param cls closure + * @param tname name of the transport + * @param expiration expiration time + * @param addrlen length of the address + * @param addr the address + * @return GNUNET_OK to keep the address, + * GNUNET_NO to delete it from the HELLO + * GNUNET_SYSERR to stop iterating (but keep current address) + */ +static int +try_connect_to_address (void *cls, + const char *tname, + struct GNUNET_TIME_Absolute expiration, + const void *addr, size_t addrlen) +{ + struct ConnectContext *cc = cls; + int af; + + if (0 != strcmp (tname, "tcp")) + return GNUNET_OK; /* not one of ours */ + if (sizeof (struct sockaddr_in) == addrlen) + af = AF_INET; + else if (sizeof (struct sockaddr_in6) == addrlen) + af = AF_INET6; + else + { + /* not a valid address */ + GNUNET_break (0); + return GNUNET_NO; + } + if (0 == cc->pos--) + { + cc->welcome = create_welcome (addrlen, addr, cc->plugin); + cc->sa = + GNUNET_NETWORK_socket_create_from_sockaddr (cc->plugin->env->sched, + af, addr, addrlen, + GNUNET_SERVER_MAX_MESSAGE_SIZE); +#if DEBUG_TCP + GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, + "tcp", "Connected to other peer.\n"); +#endif + return GNUNET_SYSERR; + } + return GNUNET_OK; /* failed to connect */ +} + + +/** + * Type of an iterator over the hosts. Note that each + * host will be called with each available protocol. + * + * @param cls closure + * @param peer id of the peer, NULL for last call + * @param hello hello message for the peer (can be NULL) + * @param trust amount of trust we have in the peer + */ +static void +session_try_connect (void *cls, + const struct GNUNET_PeerIdentity *peer, + const struct GNUNET_HELLO_Message *hello, uint32_t trust) +{ + struct Session *session = cls; + unsigned int count; + struct ConnectContext cctx; + struct PendingMessage *pm; + + if (peer == NULL) + { + /* last call, destroy session if we are still not + connected */ + if (session->still_connecting == GNUNET_NO) + { +#if DEBUG_TCP + GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, + "tcp", + "Connected to other peer, now processing messages.\n"); +#endif + process_pending_messages (session); + } + else + { +#if DEBUG_TCP + GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, + "tcp", + "Failed to connect to other peer, now closing session.\n"); +#endif + disconnect_session (session); + } + return; + } + if ((hello == NULL) || (session->client != NULL)) + { + GNUNET_break (0); /* should this ever happen!? */ + return; + } + count = 0; + GNUNET_HELLO_iterate_addresses (hello, + GNUNET_NO, &count_tcp_addresses, &count); + if (count == 0) + { +#if DEBUG_TCP + GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, + "tcp", + "Asked to connect, but have no addresses to try.\n"); +#endif + return; + } + cctx.plugin = session->plugin; + cctx.sa = NULL; + cctx.welcome = NULL; + cctx.pos = GNUNET_CRYPTO_random_u32 (GNUNET_CRYPTO_QUALITY_WEAK, count); + GNUNET_HELLO_iterate_addresses (hello, + GNUNET_NO, &try_connect_to_address, &cctx); + if (cctx.sa == NULL) + { +#if DEBUG_TCP + GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, + "tcp", + "Asked to connect, but all addresses failed.\n"); +#endif + GNUNET_free_non_null (cctx.welcome); + return; + } + session->client = GNUNET_SERVER_connect_socket (session->plugin->server, + cctx.sa); +#if DEBUG_TCP + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Connected getting client address %p\n", session->client); +#endif + if (session->client == NULL) + { + GNUNET_break (0); /* how could this happen? */ + GNUNET_free_non_null (cctx.welcome); + return; + } + pm = cctx.welcome; + /* prepend (!) */ + pm->next = session->pending_messages; + session->pending_messages = pm; + session->still_connecting = GNUNET_NO; +#if DEBUG_TCP + GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, + "tcp", + "Connected to other peer, now sending `%s' message.\n", + "WELCOME"); +#endif +} + + +/** + * Function that can be used by the transport service to transmit + * a message using the plugin. + * + * @param cls closure + * @param plugin_context value we were asked to pass to this plugin + * to respond to the given peer (use is optional, + * but may speed up processing), can be NULL + * @param service_context value passed to the transport-service + * to identify the neighbour + * @param target who should receive this message + * @param msg the message to transmit + * @param cont continuation to call once the message has + * been transmitted (or if the transport is ready + * for the next transmission call; or if the + * peer disconnected...) + * @param cont_cls closure for cont + * @return plugin_context that should be used next time for + * sending messages to the specified peer + */ +static void * +tcp_plugin_send (void *cls, + void *plugin_context, + struct ReadyList *service_context, + const struct GNUNET_PeerIdentity *target, + const struct GNUNET_MessageHeader *msg, + struct GNUNET_TIME_Relative timeout, + GNUNET_TRANSPORT_TransmitContinuation cont, void *cont_cls) +{ + struct Plugin *plugin = cls; + struct Session *session = plugin_context; + struct PendingMessage *pm; + struct PendingMessage *pme; + + if (session == NULL) + session = find_session_by_target (plugin, target); + pm = GNUNET_malloc (sizeof (struct PendingMessage) + ntohs (msg->size)); + pm->msg = (struct GNUNET_MessageHeader *) &pm[1]; + memcpy (pm->msg, msg, ntohs (msg->size)); + pm->timeout = GNUNET_TIME_relative_to_absolute (timeout); + pm->transmit_cont = cont; + pm->transmit_cont_cls = cont_cls; + if (session == NULL) + { + session = GNUNET_malloc (sizeof (struct Session)); +#if DEBUG_TCP + GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, + "tcp", + "Asked to transmit, creating fresh session %p.\n", + session); +#endif + session->next = plugin->sessions; + plugin->sessions = session; + session->plugin = plugin; + session->target = *target; + session->last_quota_update = GNUNET_TIME_absolute_get (); + session->quota_in = plugin->env->default_quota_in; + session->expecting_welcome = GNUNET_YES; + session->still_connecting = GNUNET_YES; + session->pending_messages = pm; + GNUNET_PEERINFO_for_all (plugin->env->cfg, + plugin->env->sched, + target, + 0, timeout, &session_try_connect, session); + return session; + } + GNUNET_assert (session != NULL); + GNUNET_assert (session->still_connecting == GNUNET_NO); + /* append pm to pending_messages list */ + pme = session->pending_messages; + if (pme == NULL) + { + session->pending_messages = pm; + } + else + { + while (NULL != pme->next) + pme = pme->next; + pme->next = pm; + } +#if DEBUG_TCP + GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, + "tcp", "Asked to transmit, added message to list.\n"); +#endif + process_pending_messages (session); + return session; +} + + + +/** + * Function that can be called to force a disconnect from the + * specified neighbour. This should also cancel all previously + * scheduled transmissions. Obviously the transmission may have been + * partially completed already, which is OK. The plugin is supposed + * to close the connection (if applicable) and no longer call the + * transmit continuation(s). + * + * Finally, plugin MUST NOT call the services's receive function to + * notify the service that the connection to the specified target was + * closed after a getting this call. + * + * @param cls closure + * @param plugin_context value we were asked to pass to this plugin + * to respond to the given peer (use is optional, + * but may speed up processing), can be NULL (if + * NULL was returned from the transmit function) + * @param service_context must correspond to the service context + * of the corresponding Transmit call; the plugin should + * not cancel a send call made with a different service + * context pointer! Never NULL. + * @param target peer for which the last transmission is + * to be cancelled + */ +static void +tcp_plugin_cancel (void *cls, + void *plugin_context, + struct ReadyList *service_context, + const struct GNUNET_PeerIdentity *target) +{ + struct Plugin *plugin = cls; + struct PendingMessage *pm; + struct Session *session; + struct Session *next; + + session = plugin->sessions; + while (session != NULL) + { + next = session->next; + if (0 == memcmp (target, + &session->target, sizeof (struct GNUNET_PeerIdentity))) + { + pm = session->pending_messages; + while (pm != NULL) + { + pm->transmit_cont = NULL; + pm->transmit_cont_cls = NULL; + pm = pm->next; + } + session->service_context = NULL; + GNUNET_SERVER_client_disconnect (session->client); + /* rest of the clean-up of the session will be done as part of + disconnect_notify which should be triggered any time now */ + } + session = next; + } +} + + +struct PrettyPrinterContext +{ + GNUNET_TRANSPORT_AddressStringCallback asc; + void *asc_cls; + uint16_t port; +}; + + +/** + * Append our port and forward the result. + */ +static void +append_port (void *cls, const char *hostname) +{ + struct PrettyPrinterContext *ppc = cls; + char *ret; + + if (hostname == NULL) + { + ppc->asc (ppc->asc_cls, NULL); + GNUNET_free (ppc); + return; + } + GNUNET_asprintf (&ret, "%s:%d", hostname, ppc->port); + ppc->asc (ppc->asc_cls, ret); + GNUNET_free (ret); +} + + +/** + * Convert the transports address to a nice, human-readable + * format. + * + * @param cls closure + * @param name name of the transport that generated the address + * @param addr one of the addresses of the host, NULL for the last address + * the specific address format depends on the transport + * @param addrlen length of the address + * @param numeric should (IP) addresses be displayed in numeric form? + * @param timeout after how long should we give up? + * @param asc function to call on each string + * @param asc_cls closure for asc + */ +static void +tcp_plugin_address_pretty_printer (void *cls, + const char *type, + const void *addr, + size_t addrlen, + int numeric, + struct GNUNET_TIME_Relative timeout, + GNUNET_TRANSPORT_AddressStringCallback asc, + void *asc_cls) +{ + struct Plugin *plugin = cls; + const struct sockaddr_in *v4; + const struct sockaddr_in6 *v6; + struct PrettyPrinterContext *ppc; + + if ((addrlen != sizeof (struct sockaddr_in)) && + (addrlen != sizeof (struct sockaddr_in6))) + { + /* invalid address */ + GNUNET_break_op (0); + asc (asc_cls, NULL); + return; + } + ppc = GNUNET_malloc (sizeof (struct PrettyPrinterContext)); + ppc->asc = asc; + ppc->asc_cls = asc_cls; + if (addrlen == sizeof (struct sockaddr_in)) + { + v4 = (const struct sockaddr_in *) addr; + ppc->port = ntohs (v4->sin_port); + } + else + { + v6 = (const struct sockaddr_in6 *) addr; + ppc->port = ntohs (v6->sin6_port); + + } + GNUNET_RESOLVER_hostname_get (plugin->env->sched, + plugin->env->cfg, + addr, + addrlen, + !numeric, timeout, &append_port, ppc); +} + + +/** + * Update the last-received and bandwidth quota values + * for this session. + * + * @param session session to update + * @param force set to GNUNET_YES if we should update even + * though the minimum refresh time has not yet expired + */ +static void +update_quota (struct Session *session, int force) +{ + struct GNUNET_TIME_Absolute now; + unsigned long long delta; + unsigned long long total_allowed; + unsigned long long total_remaining; + + now = GNUNET_TIME_absolute_get (); + delta = now.value - session->last_quota_update.value; + if ((delta < MIN_QUOTA_REFRESH_TIME) && (!force)) + return; /* too early, not enough data */ + + total_allowed = session->quota_in * delta; + if (total_allowed > session->last_received) + { + /* got less than acceptable */ + total_remaining = total_allowed - session->last_received; + session->last_received = 0; + delta = total_remaining / session->quota_in; /* bonus seconds */ + if (delta > MAX_BANDWIDTH_CARRY) + delta = MAX_BANDWIDTH_CARRY; /* limit amount of carry-over */ + } + else + { + /* got more than acceptable */ + total_remaining = 0; + session->last_received -= total_allowed; + delta = 0; + } + session->last_quota_update.value = now.value - delta; +} + + +/** + * Set a quota for receiving data from the given peer; this is a + * per-transport limit. The transport should limit its read/select + * calls to stay below the quota (in terms of incoming data). + * + * @param cls closure + * @param peer the peer for whom the quota is given + * @param quota_in quota for receiving/sending data in bytes per ms + */ +static void +tcp_plugin_set_receive_quota (void *cls, + const struct GNUNET_PeerIdentity *target, + uint32_t quota_in) +{ + struct Plugin *plugin = cls; + struct Session *session; + + session = find_session_by_target (plugin, target); + if (session->quota_in != quota_in) + { + update_quota (session, GNUNET_YES); + if (session->quota_in > quota_in) + session->last_quota_update = GNUNET_TIME_absolute_get (); + session->quota_in = quota_in; + } +} + + +/** + * Check if the given port is plausible (must be either + * our listen port or our advertised port). If it is + * neither, we return one of these two ports at random. + * + * @return either in_port or a more plausible port + */ +static uint16_t +check_port (struct Plugin *plugin, uint16_t in_port) +{ + if ((in_port == plugin->adv_port) || (in_port == plugin->open_port)) + return in_port; + return (GNUNET_CRYPTO_random_u32 (GNUNET_CRYPTO_QUALITY_WEAK, + 2) == 0) + ? plugin->open_port : plugin->adv_port; +} + + +/** + * Another peer has suggested an address for this + * peer and transport plugin. Check that this could be a valid + * address. If so, consider adding it to the list + * of addresses. + * + * @param cls closure + * @param addr pointer to the address + * @param addrlen length of addr + * @return GNUNET_OK if this is a plausible address for this peer + * and transport + */ +static int +tcp_plugin_address_suggested (void *cls, const void *addr, size_t addrlen) +{ + struct Plugin *plugin = cls; + char buf[sizeof (struct sockaddr_in6)]; + struct sockaddr_in *v4; + struct sockaddr_in6 *v6; + char dst[INET6_ADDRSTRLEN]; + uint16_t port; + + if ((addrlen != sizeof (struct sockaddr_in)) && + (addrlen != sizeof (struct sockaddr_in6))) + { + GNUNET_break_op (0); + return GNUNET_SYSERR; + } + memcpy (buf, addr, sizeof (struct sockaddr_in6)); + if (addrlen == sizeof (struct sockaddr_in)) + { + v4 = (struct sockaddr_in *) buf; + v4->sin_port = htons (check_port (plugin, ntohs (v4->sin_port))); + inet_ntop (AF_INET, &v4->sin_addr, dst, sizeof (dst)); + port = ntohs (v4->sin_port); + } + else + { + v6 = (struct sockaddr_in6 *) buf; + v6->sin6_port = htons (check_port (plugin, ntohs (v6->sin6_port))); + inet_ntop (AF_INET6, &v6->sin6_addr, dst, sizeof (dst)); + port = ntohs (v6->sin6_port); + } +#if DEBUG_TCP + GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, + "tcp", + "Informing transport service about my address `%s:%u'.\n", + dst, port); +#endif + plugin->env->notify_address (plugin->env->cls, + "tcp", + buf, addrlen, LEARNED_ADDRESS_EXPIRATION); + return GNUNET_OK; +} + + +/** + * We've received a welcome from this peer via TCP. + * Possibly create a fresh client record and send back + * our welcome. + * + * @param cls closure + * @param server the server handling the message + * @param client identification of the client + * @param message the actual message + */ +static void +handle_tcp_welcome (void *cls, + struct GNUNET_SERVER_Handle *server, + struct GNUNET_SERVER_Client *client, + const struct GNUNET_MessageHeader *message) +{ + struct Plugin *plugin = cls; + struct Session *session_c; + const struct WelcomeMessage *wm; + uint16_t msize; + uint32_t addrlen; + size_t alen; + void *vaddr; + const struct sockaddr *addr; + +#if DEBUG_TCP + GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, + "tcp", + "Received `%s' message from %p.\n", "WELCOME", client); +#endif + msize = ntohs (message->size); + if (msize < sizeof (struct WelcomeMessage)) + { + GNUNET_break_op (0); + GNUNET_SERVER_receive_done (client, GNUNET_SYSERR); + return; + } + wm = (const struct WelcomeMessage *) message; + session_c = find_session_by_client (plugin, client); + if (session_c == NULL) + { + vaddr = NULL; + GNUNET_SERVER_client_get_address (client, &vaddr, &alen); + GNUNET_SERVER_client_keep (client); + session_c = create_session (plugin, + &wm->clientIdentity, client, vaddr, alen); +#if DEBUG_TCP + GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, + "tcp", + "Creating new session %p for incoming `%s' message.\n", + session_c, "WELCOME"); +#endif + GNUNET_free_non_null (vaddr); + process_pending_messages (session_c); + } + session_c->expecting_welcome = GNUNET_NO; + if (0 < (addrlen = msize - sizeof (struct WelcomeMessage))) + { + addr = (const struct sockaddr *) &wm[1]; + tcp_plugin_address_suggested (plugin, addr, addrlen); + } + GNUNET_SERVER_receive_done (client, GNUNET_OK); +} + + +/** + * Calculate how long we should delay reading from the TCP socket to + * ensure that we stay within our bandwidth limits (push back). + * + * @param session for which client should this be calculated + */ +static struct GNUNET_TIME_Relative +calculate_throttle_delay (struct Session *session) +{ + struct GNUNET_TIME_Relative ret; + struct GNUNET_TIME_Absolute now; + uint64_t del; + uint64_t avail; + uint64_t excess; + + now = GNUNET_TIME_absolute_get (); + del = now.value - session->last_quota_update.value; + if (del > MAX_BANDWIDTH_CARRY) + { + update_quota (session, GNUNET_YES); + del = now.value - session->last_quota_update.value; + GNUNET_assert (del <= MAX_BANDWIDTH_CARRY); + } + if (session->quota_in == 0) + session->quota_in = 1; /* avoid divison by zero */ + avail = del * session->quota_in; + if (avail > session->last_received) + return GNUNET_TIME_UNIT_ZERO; /* can receive right now */ + excess = session->last_received - avail; + ret.value = excess / session->quota_in; + return ret; +} + + +/** + * Task to signal the server that we can continue + * receiving from the TCP client now. + */ +static void +delayed_done (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc) +{ + struct Session *session = cls; + GNUNET_SERVER_receive_done (session->client, GNUNET_OK); +} + + +/** + * We've received data for this peer via TCP. Unbox, + * compute latency and forward. + * + * @param cls closure + * @param server the server handling the message + * @param client identification of the client + * @param message the actual message + */ +static void +handle_tcp_data (void *cls, + struct GNUNET_SERVER_Handle *server, + struct GNUNET_SERVER_Client *client, + const struct GNUNET_MessageHeader *message) +{ + struct Plugin *plugin = cls; + struct Session *session; + const struct DataMessage *dm; + uint16_t msize; + const struct GNUNET_MessageHeader *msg; + struct GNUNET_TIME_Relative latency; + struct GNUNET_TIME_Absolute ttime; + struct GNUNET_TIME_Absolute now; + struct GNUNET_TIME_Relative delay; + uint64_t ack_in; + +#if DEBUG_TCP + GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, + "tcp", "Receiving data from other peer.\n"); +#endif + msize = ntohs (message->size); + if ((msize < + sizeof (struct DataMessage) + sizeof (struct GNUNET_MessageHeader))) + { + GNUNET_break_op (0); + GNUNET_SERVER_receive_done (client, GNUNET_SYSERR); + return; + } + session = find_session_by_client (plugin, client); + if ((NULL == session) || (GNUNET_YES == session->expecting_welcome)) + { + GNUNET_break_op (0); + GNUNET_SERVER_receive_done (client, GNUNET_SYSERR); + return; + } + dm = (const struct DataMessage *) message; + session->max_in_msg_counter = GNUNET_MAX (session->max_in_msg_counter, + GNUNET_ntohll (dm->ack_out)); + msg = (const struct GNUNET_MessageHeader *) &dm[1]; + if (msize != sizeof (struct DataMessage) + ntohs (msg->size)) + { + GNUNET_break_op (0); + GNUNET_SERVER_receive_done (client, GNUNET_SYSERR); + return; + } + /* estimate latency */ + ack_in = GNUNET_ntohll (dm->ack_in); + if ((ack_in <= session->out_msg_counter) && + (session->out_msg_counter - ack_in < ACK_LOG_SIZE)) + { + delay = GNUNET_TIME_relative_ntoh (dm->delay); + ttime = session->gen_time[ack_in % ACK_LOG_SIZE]; + now = GNUNET_TIME_absolute_get (); + if (delay.value > now.value - ttime.value) + delay.value = 0; /* not plausible */ + /* update (round-trip) latency using ageing; we + use 7:1 so that we can reasonably quickly react + to changes, but not so fast that latency is largely + jitter... */ + session->latency_estimate + = ((7 * session->latency_estimate) + + (now.value - ttime.value - delay.value)) / 8; + } + latency.value = (uint64_t) session->latency_estimate; + /* deliver on */ +#if DEBUG_TCP + GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, + "tcp", + "Forwarding data of type %u to transport service.\n", + ntohs (msg->type)); +#endif + session->service_context + = plugin->env->receive (plugin->env->cls, + session, + session->service_context, + latency, &session->target, msg); + /* update bandwidth used */ + session->last_received += msize; + update_quota (session, GNUNET_NO); + + delay = calculate_throttle_delay (session); + if (delay.value == 0) + GNUNET_SERVER_receive_done (client, GNUNET_OK); + else + GNUNET_SCHEDULER_add_delayed (session->plugin->env->sched, + GNUNET_NO, + GNUNET_SCHEDULER_PRIORITY_HIGH, + GNUNET_SCHEDULER_NO_PREREQUISITE_TASK, + delay, &delayed_done, session); +} + + +/** + * Handlers for the various TCP messages. + */ +static struct GNUNET_SERVER_MessageHandler my_handlers[] = { + {&handle_tcp_welcome, NULL, GNUNET_MESSAGE_TYPE_TRANSPORT_TCP_WELCOME, 0}, + {&handle_tcp_data, NULL, GNUNET_MESSAGE_TYPE_TRANSPORT_TCP_DATA, 0}, + {NULL, NULL, 0, 0} +}; + + +static void +create_tcp_handlers (struct Plugin *plugin) +{ + unsigned int i; + plugin->handlers = GNUNET_malloc (sizeof (my_handlers)); + memcpy (plugin->handlers, my_handlers, sizeof (my_handlers)); + for (i = 0; + i < + sizeof (my_handlers) / sizeof (struct GNUNET_SERVER_MessageHandler); + i++) + plugin->handlers[i].callback_cls = plugin; + GNUNET_SERVER_add_handlers (plugin->server, plugin->handlers); +} + + +/** + * Functions with this signature are called whenever a peer + * is disconnected on the network level. + * + * @param cls closure + * @param client identification of the client + */ +static void +disconnect_notify (void *cls, struct GNUNET_SERVER_Client *client) +{ + struct Plugin *plugin = cls; + struct Session *session; + +#if DEBUG_TCP + GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, + "tcp", + "Notified about network-level disconnect of client %p.\n", + client); +#endif + session = find_session_by_client (plugin, client); + if (session == NULL) + return; /* unknown, nothing to do */ +#if DEBUG_TCP + GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, + "tcp", "Will now destroy session %p.\n", session); +#endif + disconnect_session (session); +} + + +/** + * Add the IP of our network interface to the list of + * our external IP addresses. + */ +static int +process_interfaces (void *cls, + const char *name, + int isDefault, + const struct sockaddr *addr, socklen_t addrlen) +{ + struct Plugin *plugin = cls; + char dst[INET6_ADDRSTRLEN]; + int af; + struct sockaddr_in *v4; + struct sockaddr_in6 *v6; + + af = addr->sa_family; + if (af == AF_INET) + { + v4 = (struct sockaddr_in *) addr; + inet_ntop (AF_INET, &v4->sin_addr, dst, sizeof (dst)); + v4->sin_port = htons (plugin->adv_port); + } + else + { + GNUNET_assert (af == AF_INET6); + v6 = (struct sockaddr_in6 *) addr; + inet_ntop (AF_INET6, &v6->sin6_addr, dst, sizeof (dst)); + v6->sin6_port = htons (plugin->adv_port); + } + GNUNET_log_from (GNUNET_ERROR_TYPE_INFO | + GNUNET_ERROR_TYPE_BULK, + "tcp", _("Found address `%s' (%s)\n"), dst, name); + plugin->env->notify_address (plugin->env->cls, + "tcp", + addr, addrlen, GNUNET_TIME_UNIT_FOREVER_REL); + return GNUNET_OK; +} + + +/** + * Function called by the resolver for each address obtained from DNS + * for our own hostname. Add the addresses to the list of our + * external IP addresses. + * + * @param cls closure + * @param addr one of the addresses of the host, NULL for the last address + * @param addrlen length of the address + */ +static void +process_hostname_ips (void *cls, + const struct sockaddr *addr, socklen_t addrlen) +{ + struct Plugin *plugin = cls; + + if (addr == NULL) + return; + plugin->env->notify_address (plugin->env->cls, + "tcp", + addr, addrlen, GNUNET_TIME_UNIT_FOREVER_REL); +} + + +/** + * Entry point for the plugin. + */ +void * +libgnunet_plugin_transport_tcp_init (void *cls) +{ + struct GNUNET_TRANSPORT_PluginEnvironment *env = cls; + struct GNUNET_TRANSPORT_PluginFunctions *api; + struct Plugin *plugin; + struct GNUNET_SERVICE_Context *service; + unsigned long long aport; + unsigned long long bport; + + service = GNUNET_SERVICE_start ("tcp", env->sched, env->cfg); + if (service == NULL) + { + GNUNET_log_from (GNUNET_ERROR_TYPE_WARNING, + "tcp", + _ + ("Failed to start service for `%s' transport plugin.\n"), + "tcp"); + return NULL; + } + aport = 0; + if ((GNUNET_OK != + GNUNET_CONFIGURATION_get_value_number (env->cfg, + "tcp", + "PORT", + &bport)) || + (bport > 65535) || + ((GNUNET_OK == + GNUNET_CONFIGURATION_get_value_number (env->cfg, + "tcp", + "ADVERTISED-PORT", + &aport)) && (aport > 65535))) + { + GNUNET_log_from (GNUNET_ERROR_TYPE_ERROR, + "tcp", + _ + ("Require valid port number for service `%s' in configuration!\n"), + "tcp"); + GNUNET_SERVICE_stop (service); + return NULL; + } + if (aport == 0) + aport = bport; + plugin = GNUNET_malloc (sizeof (struct Plugin)); + plugin->open_port = bport; + plugin->adv_port = aport; + plugin->env = env; + plugin->lsock = NULL; + plugin->statistics = NULL; + api = GNUNET_malloc (sizeof (struct GNUNET_TRANSPORT_PluginFunctions)); + api->cls = plugin; + api->send_to = &tcp_plugin_send_to; + api->send = &tcp_plugin_send; + api->cancel = &tcp_plugin_cancel; + api->address_pretty_printer = &tcp_plugin_address_pretty_printer; + api->set_receive_quota = &tcp_plugin_set_receive_quota; + api->address_suggested = &tcp_plugin_address_suggested; + api->cost_estimate = 42; /* TODO: ATS */ + plugin->service = service; + plugin->server = GNUNET_SERVICE_get_server (service); + create_tcp_handlers (plugin); + GNUNET_log_from (GNUNET_ERROR_TYPE_INFO, + "tcp", _("TCP transport listening on port %u\n"), bport); + if (aport != bport) + GNUNET_log_from (GNUNET_ERROR_TYPE_INFO, + "tcp", + _ + ("TCP transport advertises itself as being on port %u\n"), + aport); + GNUNET_SERVER_disconnect_notify (plugin->server, &disconnect_notify, + plugin); + GNUNET_OS_network_interfaces_list (&process_interfaces, plugin); + GNUNET_RESOLVER_hostname_resolve (env->sched, + env->cfg, + AF_UNSPEC, + HOSTNAME_RESOLVE_TIMEOUT, + &process_hostname_ips, plugin); + return api; +} + + +/** + * Exit point from the plugin. + */ +void * +libgnunet_plugin_transport_tcp_done (void *cls) +{ + struct GNUNET_TRANSPORT_PluginFunctions *api = cls; + struct Plugin *plugin = api->cls; + + GNUNET_SERVICE_stop (plugin->service); + GNUNET_free (plugin->handlers); + GNUNET_free (plugin); + GNUNET_free (api); + return NULL; +} + +/* end of plugin_transport_tcp.c */ diff --git a/src/transport/plugin_transport_template.c b/src/transport/plugin_transport_template.c new file mode 100644 index 000000000..1c8b06c61 --- /dev/null +++ b/src/transport/plugin_transport_template.c @@ -0,0 +1,335 @@ +/* + This file is part of GNUnet + (C) 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009 Christian Grothoff (and other contributing authors) + + GNUnet is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published + by the Free Software Foundation; either version 2, 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 + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with GNUnet; see the file COPYING. If not, write to the + Free Software Foundation, Inc., 59 Temple Place - Suite 330, + Boston, MA 02111-1307, USA. +*/ + +/** + * @file transport/plugin_transport_template.c + * @brief template for a new transport service + * @author Christian Grothoff + */ + +#include "platform.h" +#include "gnunet_protocols.h" +#include "gnunet_network_lib.h" +#include "gnunet_server_lib.h" +#include "gnunet_service_lib.h" +#include "gnunet_statistics_service.h" +#include "gnunet_transport_service.h" +#include "plugin_transport.h" + +#define DEBUG_TEMPLATE GNUNET_NO + +/** + * After how long do we expire an address that we + * learned from another peer if it is not reconfirmed + * by anyone? + */ +#define LEARNED_ADDRESS_EXPIRATION GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_HOURS, 6) + + +/** + * Encapsulation of all of the state of the plugin. + */ +struct Plugin; + + +/** + * Session handle for connections. + */ +struct Session +{ + + /** + * Stored in a linked list. + */ + struct Session *next; + + /** + * Pointer to the global plugin struct. + */ + struct Plugin *plugin; + + /** + * The client (used to identify this connection) + */ + /* void *client; */ + + /** + * Continuation function to call once the transmission buffer + * has again space available. NULL if there is no + * continuation to call. + */ + GNUNET_TRANSPORT_TransmitContinuation transmit_cont; + + /** + * Closure for transmit_cont. + */ + void *transmit_cont_cls; + + /** + * To whom are we talking to (set to our identity + * if we are still waiting for the welcome message) + */ + struct GNUNET_PeerIdentity sender; + + /** + * At what time did we reset last_received last? + */ + struct GNUNET_TIME_Absolute last_quota_update; + + /** + * How many bytes have we received since the "last_quota_update" + * timestamp? + */ + uint64_t last_received; + + /** + * Number of bytes per ms that this peer is allowed + * to send to us. + */ + uint32_t quota; + +}; + +/** + * Encapsulation of all of the state of the plugin. + */ +struct Plugin +{ + /** + * Our environment. + */ + struct GNUNET_TRANSPORT_PluginEnvironment *env; + + /** + * List of open sessions. + */ + struct Session *sessions; + + /** + * Handle for the statistics service. + */ + struct GNUNET_STATISTICS_Handle *statistics; + +}; + + + +/** + * Function that can be used by the transport service to transmit + * a message using the plugin using a fresh connection (even if + * we already have a connection to this peer, this function is + * required to establish a new one). + * + * @param cls closure + * @param target who should receive this message + * @param msg1 first message to transmit + * @param msg2 second message to transmit (can be NULL) + * @param timeout how long until we give up? + * @param addr the address + * @param addrlen length of the address + * @return non-null session if the transmission has been scheduled + * NULL if the address format is invalid + */ +static void * +template_plugin_send_to (void *cls, + const struct GNUNET_PeerIdentity *target, + const struct GNUNET_MessageHeader *msg1, + const struct GNUNET_MessageHeader *msg2, + struct GNUNET_TIME_Relative timeout, + const void *addr, size_t addrlen) +{ + // FIXME + return NULL; +} + + +/** + * Function that can be used by the transport service to transmit + * a message using the plugin. + * + * @param cls closure + * @param plugin_context value we were asked to pass to this plugin + * to respond to the given peer (use is optional, + * but may speed up processing), can be NULL + * @param service_context value passed to the transport-service + * to identify the neighbour + * @param target who should receive this message + * @param msg the message to transmit + * @param cont continuation to call once the message has + * been transmitted (or if the transport is ready + * for the next transmission call; or if the + * peer disconnected...) + * @param cont_cls closure for cont + * @return plugin_context that should be used next time for + * sending messages to the specified peer + */ +static void * +template_plugin_send (void *cls, + void *plugin_context, + struct ReadyList *service_context, + const struct GNUNET_PeerIdentity *target, + const struct GNUNET_MessageHeader *msg, + struct GNUNET_TIME_Relative timeout, + GNUNET_TRANSPORT_TransmitContinuation cont, + void *cont_cls) +{ + // struct Plugin *plugin = cls; + return NULL; +} + + + +/** + * + * @param cls closure + * @param plugin_context value we were asked to pass to this plugin + * to respond to the given peer (use is optional, + * but may speed up processing), can be NULL (if + * NULL was returned from the transmit function) + * @param service_context must correspond to the service context + * of the corresponding Transmit call; the plugin should + * not cancel a send call made with a different service + * context pointer! Never NULL. + * @param target peer for which the last transmission is + * to be cancelled + */ +static void +template_plugin_cancel (void *cls, + void *plugin_context, + struct ReadyList *service_context, + const struct GNUNET_PeerIdentity *target) +{ + // struct Plugin *plugin = cls; + // FIXME +} + + +/** + * Convert the transports address to a nice, human-readable + * format. + * + * @param cls closure + * @param name name of the transport that generated the address + * @param addr one of the addresses of the host, NULL for the last address + * the specific address format depends on the transport + * @param addrlen length of the address + * @param numeric should (IP) addresses be displayed in numeric form? + * @param timeout after how long should we give up? + * @param asc function to call on each string + * @param asc_cls closure for asc + */ +static void +template_plugin_address_pretty_printer (void *cls, + const char *type, + const void *addr, + size_t addrlen, + int numeric, + struct GNUNET_TIME_Relative timeout, + GNUNET_TRANSPORT_AddressStringCallback + asc, void *asc_cls) +{ + asc (asc_cls, NULL); +} + +/** + * Set a quota for receiving data from the given peer; this is a + * per-transport limit. The transport should limit its read/select + * calls to stay below the quota (in terms of incoming data). + * + * @param cls closure + * @param peer the peer for whom the quota is given + * @param quota_in quota for receiving/sending data in bytes per ms + */ +static void +template_plugin_set_receive_quota (void *cls, + const struct GNUNET_PeerIdentity *target, + uint32_t quota_in) +{ + // struct Plugin *plugin = cls; + // FIXME! +} + + +/** + * Another peer has suggested an address for this + * peer and transport plugin. Check that this could be a valid + * address. If so, consider adding it to the list + * of addresses. + * + * @param cls closure + * @param addr pointer to the address + * @param addrlen length of addr + * @return GNUNET_OK if this is a plausible address for this peer + * and transport + */ +static int +template_plugin_address_suggested (void *cls, + const void *addr, size_t addrlen) +{ + // struct Plugin *plugin = cls; + + /* check if the address is plausible; if so, + add it to our list! */ + // FIXME! + return GNUNET_OK; +} + + +/** + * Entry point for the plugin. + */ +void * +gnunet_plugin_transport_template_init (void *cls) +{ + struct GNUNET_TRANSPORT_PluginEnvironment *env = cls; + struct GNUNET_TRANSPORT_PluginFunctions *api; + struct Plugin *plugin; + + plugin = GNUNET_malloc (sizeof (struct Plugin)); + plugin->env = env; + plugin->statistics = NULL; + api = GNUNET_malloc (sizeof (struct GNUNET_TRANSPORT_PluginFunctions)); + api->cls = plugin; + api->send_to = &template_plugin_send_to; + api->send = &template_plugin_send; + api->cancel = &template_plugin_cancel; + api->address_pretty_printer = &template_plugin_address_pretty_printer; + api->set_receive_quota = &template_plugin_set_receive_quota; + api->address_suggested = &template_plugin_address_suggested; + api->cost_estimate = 42; // FIXME + return api; +} + + +/** + * Exit point from the plugin. + */ +void * +gnunet_plugin_transport_template_done (void *cls) +{ + struct GNUNET_TRANSPORT_PluginFunctions *api = cls; + struct Plugin *plugin = api->cls; + + GNUNET_free (plugin); + GNUNET_free (api); + return NULL; +} + +/* end of plugin_transport_template.c */ diff --git a/src/transport/plugin_transport_udp.c b/src/transport/plugin_transport_udp.c new file mode 100644 index 000000000..ccaf9fbd1 --- /dev/null +++ b/src/transport/plugin_transport_udp.c @@ -0,0 +1,592 @@ +/* + This file is part of GNUnet + (C) 2001, 2002, 2003, 2004, 2005, 2008 Christian Grothoff (and other contributing authors) + + GNUnet is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published + by the Free Software Foundation; either version 2, 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 + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with GNUnet; see the file COPYING. If not, write to the + Free Software Foundation, Inc., 59 Temple Place - Suite 330, + Boston, MA 02111-1307, USA. +*/ + +/** + * @file transports/udp.c + * @brief Implementation of the UDP transport service + * @author Christian Grothoff + */ + +#include "platform.h" +#include "gnunet_util.h" +#include "gnunet_protocols.h" +#include "gnunet_transport.h" +#include "gnunet_stats_service.h" +#include "gnunet_upnp_service.h" +#include "ip.h" + +#define DEBUG_UDP GNUNET_YES + +/** + * The default maximum size of each outbound UDP message, + * optimal value for Ethernet (10 or 100 MBit). + */ +#define MESSAGE_SIZE 1472 + +/** + * Message-Packet header. + */ +typedef struct +{ + /** + * size of the message, in bytes, including this header. + */ + GNUNET_MessageHeader header; + + /** + * What is the identity of the sender (GNUNET_hash of public key) + */ + GNUNET_PeerIdentity sender; + +} UDPMessage; + +#define MY_TRANSPORT_NAME "UDP" +#include "common.c" + +/* *********** globals ************* */ + +static int stat_bytesReceived; + +static int stat_bytesSent; + +static int stat_bytesDropped; + +static int stat_udpConnected; + +/** + * thread that listens for inbound messages + */ +static struct GNUNET_SelectHandle *selector; + +/** + * the socket that we transmit all data with + */ +static struct GNUNET_SocketHandle *udp_sock; + +static struct GNUNET_LoadMonitor *load_monitor; + + +/** + * The socket of session has data waiting, process! + * + * This function may only be called if the tcplock is + * already held by the caller. + */ +static int +select_message_handler (void *mh_cls, + struct GNUNET_SelectHandle *sh, + struct GNUNET_SocketHandle *sock, + void *sock_ctx, const GNUNET_MessageHeader * msg) +{ + unsigned int len; + GNUNET_TransportPacket *mp; + const UDPMessage *um; + + len = ntohs (msg->size); + if (len <= sizeof (UDPMessage)) + { + GNUNET_GE_LOG (coreAPI->ectx, + GNUNET_GE_WARNING | GNUNET_GE_USER | GNUNET_GE_BULK, + _("Received malformed message via %s. Ignored.\n"), + "UDP"); + return GNUNET_SYSERR; + } + um = (const UDPMessage *) msg; + mp = GNUNET_malloc (sizeof (GNUNET_TransportPacket)); + mp->msg = GNUNET_malloc (len - sizeof (UDPMessage)); + memcpy (mp->msg, &um[1], len - sizeof (UDPMessage)); + mp->sender = um->sender; + mp->size = len - sizeof (UDPMessage); + mp->tsession = NULL; + coreAPI->receive (mp); + if (stats != NULL) + stats->change (stat_bytesReceived, len); + return GNUNET_OK; +} + +static void * +select_accept_handler (void *ah_cls, + struct GNUNET_SelectHandle *sh, + struct GNUNET_SocketHandle *sock, + const void *addr, unsigned int addr_len) +{ + static int nonnullpointer; + + if (GNUNET_NO != is_rejected_tester (addr, addr_len)) + return NULL; + return &nonnullpointer; +} + +/** + * Select has been forced to close a connection. + * Free the associated context. + */ +static void +select_close_handler (void *ch_cls, + struct GNUNET_SelectHandle *sh, + struct GNUNET_SocketHandle *sock, void *sock_ctx) +{ + /* do nothing */ +} + +/** + * Establish a connection to a remote node. + * + * @param hello the hello-Message for the target node + * @param tsessionPtr the session handle that is to be set + * @return GNUNET_OK on success, GNUNET_SYSERR if the operation failed + */ +static int +udp_connect (const GNUNET_MessageHello * hello, + GNUNET_TSession ** tsessionPtr, int may_reuse) +{ + GNUNET_TSession *tsession; + + tsession = GNUNET_malloc (sizeof (GNUNET_TSession)); + memset (tsession, 0, sizeof (GNUNET_TSession)); + tsession->internal = GNUNET_malloc (GNUNET_sizeof_hello (hello)); + memcpy (tsession->internal, hello, GNUNET_sizeof_hello (hello)); + tsession->ttype = myAPI.protocol_number; + tsession->peer = hello->senderIdentity; + *tsessionPtr = tsession; + if (stats != NULL) + stats->change (stat_udpConnected, 1); + return GNUNET_OK; +} + +/** + * A (core) Session is to be associated with a transport session. The + * transport service may want to know in order to call back on the + * core if the connection is being closed. + * + * @param tsession the session handle passed along + * from the call to receive that was made by the transport + * layer + * @return GNUNET_OK if the session could be associated, + * GNUNET_SYSERR if not. + */ +int +udp_associate (GNUNET_TSession * tsession) +{ + return GNUNET_SYSERR; /* UDP connections can never be associated */ +} + +/** + * Disconnect from a remote node. + * + * @param tsession the session that is closed + * @return GNUNET_OK on success, GNUNET_SYSERR if the operation failed + */ +static int +udp_disconnect (GNUNET_TSession * tsession) +{ + if (tsession != NULL) + { + if (tsession->internal != NULL) + GNUNET_free (tsession->internal); + GNUNET_free (tsession); + if (stats != NULL) + stats->change (stat_udpConnected, -1); + } + return GNUNET_OK; +} + +/** + * Shutdown the server process (stop receiving inbound traffic). Maybe + * restarted later! + */ +static int +udp_transport_server_stop () +{ + GNUNET_GE_ASSERT (coreAPI->ectx, udp_sock != NULL); + if (selector != NULL) + { + GNUNET_select_destroy (selector); + selector = NULL; + } + GNUNET_socket_destroy (udp_sock); + udp_sock = NULL; + return GNUNET_OK; +} + +/** + * Test if the transport would even try to send + * a message of the given size and importance + * for the given session.
+ * This function is used to check if the core should + * even bother to construct (and encrypt) this kind + * of message. + * + * @return GNUNET_YES if the transport would try (i.e. queue + * the message or call the OS to send), + * GNUNET_NO if the transport would just drop the message, + * GNUNET_SYSERR if the size/session is invalid + */ +static int +udp_test_would_try (GNUNET_TSession * tsession, unsigned int size, + int important) +{ + const GNUNET_MessageHello *hello; + + if (udp_sock == NULL) + return GNUNET_SYSERR; + if (size == 0) + { + GNUNET_GE_BREAK (coreAPI->ectx, 0); + return GNUNET_SYSERR; + } + if (size > myAPI.mtu) + { + GNUNET_GE_BREAK (coreAPI->ectx, 0); + return GNUNET_SYSERR; + } + hello = (const GNUNET_MessageHello *) tsession->internal; + if (hello == NULL) + return GNUNET_SYSERR; + return GNUNET_YES; +} + +/** + * Create a UDP socket. If possible, use IPv6, otherwise + * try IPv4. Update available_protocols accordingly. + */ +static int +udp_create_socket () +{ + int s; + + available_protocols = VERSION_AVAILABLE_NONE; + s = -1; + if (GNUNET_YES != + GNUNET_GC_get_configuration_value_yesno (cfg, "GNUNETD", "DISABLE-IPV6", + GNUNET_YES)) + { +#ifndef MINGW + s = SOCKET (PF_INET6, SOCK_DGRAM, 17); +#else + s = win_ols_socket (PF_INET6, SOCK_DGRAM, 17); +#endif + } + if (s < 0) + { +#ifndef MINGW + s = SOCKET (PF_INET, SOCK_DGRAM, 17); +#else + s = win_ols_socket (PF_INET, SOCK_DGRAM, 17); +#endif + if (s < 0) + { + GNUNET_GE_LOG_STRERROR (coreAPI->ectx, + GNUNET_GE_ERROR | GNUNET_GE_ADMIN | + GNUNET_GE_BULK, "socket"); + return GNUNET_SYSERR; + } + available_protocols = VERSION_AVAILABLE_IPV4; + } + else + { + available_protocols = VERSION_AVAILABLE_IPV6 | VERSION_AVAILABLE_IPV4; + } + return s; +} + +/** + * Send a message to the specified remote node. + * + * @param tsession the GNUNET_MessageHello identifying the remote node + * @param message what to send + * @param size the size of the message + * @return GNUNET_SYSERR on error, GNUNET_OK on success + */ +static int +udp_send (GNUNET_TSession * tsession, + const void *message, const unsigned int size, int important) +{ + const GNUNET_MessageHello *hello; + const HostAddress *haddr; + UDPMessage *mp; + struct sockaddr_in serverAddrv4; + struct sockaddr_in6 serverAddrv6; + struct sockaddr *serverAddr; + socklen_t addrlen; + unsigned short available; + int ok; + int ssize; + size_t sent; + + GNUNET_GE_ASSERT (NULL, tsession != NULL); + if (udp_sock == NULL) + return GNUNET_SYSERR; + if (size == 0) + { + GNUNET_GE_BREAK (coreAPI->ectx, 0); + return GNUNET_SYSERR; + } + if (size > myAPI.mtu) + { + GNUNET_GE_BREAK (coreAPI->ectx, 0); + return GNUNET_SYSERR; + } + hello = (const GNUNET_MessageHello *) tsession->internal; + if (hello == NULL) + return GNUNET_SYSERR; + + haddr = (const HostAddress *) &hello[1]; + available = ntohs (haddr->availability) & available_protocols; + if (available == VERSION_AVAILABLE_NONE) + return GNUNET_SYSERR; + if (available == (VERSION_AVAILABLE_IPV4 | VERSION_AVAILABLE_IPV6)) + { + if (GNUNET_random_u32 (GNUNET_RANDOM_QUALITY_WEAK, 2) == 0) + available = VERSION_AVAILABLE_IPV4; + else + available = VERSION_AVAILABLE_IPV6; + } + ssize = size + sizeof (UDPMessage); + mp = GNUNET_malloc (ssize); + mp->header.size = htons (ssize); + mp->header.type = 0; + mp->sender = *(coreAPI->my_identity); + memcpy (&mp[1], message, size); + ok = GNUNET_SYSERR; + + if ((available & VERSION_AVAILABLE_IPV4) > 0) + { + memset (&serverAddrv4, 0, sizeof (serverAddrv4)); + serverAddrv4.sin_family = AF_INET; + serverAddrv4.sin_port = haddr->port; + memcpy (&serverAddrv4.sin_addr, &haddr->ipv4, sizeof (struct in_addr)); + addrlen = sizeof (serverAddrv4); + serverAddr = (struct sockaddr *) &serverAddrv4; + } + else + { + memset (&serverAddrv6, 0, sizeof (serverAddrv6)); + serverAddrv6.sin6_family = AF_INET; + serverAddrv6.sin6_port = haddr->port; + memcpy (&serverAddrv6.sin6_addr, &haddr->ipv6, + sizeof (struct in6_addr)); + addrlen = sizeof (serverAddrv6); + serverAddr = (struct sockaddr *) &serverAddrv6; + } +#ifndef MINGW + if (GNUNET_YES == GNUNET_socket_send_to (udp_sock, + GNUNET_NC_NONBLOCKING, + mp, + ssize, &sent, + (const char *) serverAddr, + addrlen)) +#else + sent = + win_ols_sendto (udp_sock, mp, ssize, (const char *) serverAddr, addrlen); + if (sent != SOCKET_ERROR) +#endif + { + ok = GNUNET_OK; + if (stats != NULL) + stats->change (stat_bytesSent, sent); + } + else + { + if (stats != NULL) + stats->change (stat_bytesDropped, ssize); + } + GNUNET_free (mp); + return ok; +} + +/** + * Start the server process to receive inbound traffic. + * + * @return GNUNET_OK on success, GNUNET_SYSERR if the operation failed + */ +static int +udp_transport_server_start () +{ + struct sockaddr_in serverAddrv4; + struct sockaddr_in6 serverAddrv6; + struct sockaddr *serverAddr; + socklen_t addrlen; + int sock; + const int on = 1; + unsigned short port; + + GNUNET_GE_ASSERT (coreAPI->ectx, selector == NULL); + /* initialize UDP network */ + port = get_port (); + if (port != 0) + { + sock = udp_create_socket (); + if (sock < 0) + return GNUNET_SYSERR; + if (SETSOCKOPT (sock, SOL_SOCKET, SO_REUSEADDR, &on, sizeof (on)) < 0) + { + GNUNET_GE_DIE_STRERROR (coreAPI->ectx, + GNUNET_GE_FATAL | GNUNET_GE_ADMIN | + GNUNET_GE_IMMEDIATE, "setsockopt"); + return GNUNET_SYSERR; + } + if (available_protocols == VERSION_AVAILABLE_IPV4) + { + memset (&serverAddrv4, 0, sizeof (serverAddrv4)); + serverAddrv4.sin_family = AF_INET; + serverAddrv4.sin_addr.s_addr = INADDR_ANY; + serverAddrv4.sin_port = htons (port); + addrlen = sizeof (serverAddrv4); + serverAddr = (struct sockaddr *) &serverAddrv4; + } + else + { + memset (&serverAddrv6, 0, sizeof (serverAddrv6)); + serverAddrv6.sin6_family = AF_INET6; + serverAddrv6.sin6_addr = in6addr_any; + serverAddrv6.sin6_port = htons (port); + addrlen = sizeof (serverAddrv6); + serverAddr = (struct sockaddr *) &serverAddrv6; + } + if (BIND (sock, serverAddr, addrlen) < 0) + { + GNUNET_GE_LOG_STRERROR (coreAPI->ectx, + GNUNET_GE_FATAL | GNUNET_GE_ADMIN | + GNUNET_GE_IMMEDIATE, "bind"); + GNUNET_GE_LOG (coreAPI->ectx, + GNUNET_GE_FATAL | GNUNET_GE_ADMIN | + GNUNET_GE_IMMEDIATE, + _("Failed to bind to %s port %d.\n"), + MY_TRANSPORT_NAME, port); + if (0 != CLOSE (sock)) + GNUNET_GE_LOG_STRERROR (coreAPI->ectx, + GNUNET_GE_ERROR | GNUNET_GE_USER | + GNUNET_GE_ADMIN | GNUNET_GE_BULK, + "close"); + return GNUNET_SYSERR; + } + selector = GNUNET_select_create ("udp", GNUNET_YES, coreAPI->ectx, load_monitor, sock, addrlen, 0, /* timeout */ + &select_message_handler, + NULL, + &select_accept_handler, + NULL, + &select_close_handler, + NULL, 64 * 1024, + 16 /* max sockets */ ); + if (selector == NULL) + return GNUNET_SYSERR; + } + sock = udp_create_socket (); + if (sock == -1) + { + GNUNET_GE_LOG_STRERROR (coreAPI->ectx, + GNUNET_GE_ERROR | GNUNET_GE_ADMIN | + GNUNET_GE_BULK, "socket"); + GNUNET_select_destroy (selector); + selector = NULL; + return GNUNET_SYSERR; + } + udp_sock = GNUNET_socket_create (coreAPI->ectx, load_monitor, sock); + GNUNET_GE_ASSERT (coreAPI->ectx, udp_sock != NULL); + return GNUNET_OK; +} + +/** + * The exported method. Makes the core api available via a global and + * returns the udp transport API. + */ +GNUNET_TransportAPI * +inittransport_udp (GNUNET_CoreAPIForTransport * core) +{ + unsigned long long mtu; + + cfg = core->cfg; + load_monitor = core->load_monitor; + GNUNET_GE_ASSERT (coreAPI->ectx, sizeof (UDPMessage) == 68); + GNUNET_GE_ASSERT (coreAPI->ectx, sizeof (HostAddress) == 24); + coreAPI = core; + if (-1 == GNUNET_GC_get_configuration_value_number (cfg, + "UDP", + "MTU", + sizeof (UDPMessage) + + + GNUNET_P2P_MESSAGE_OVERHEAD + + + sizeof + (GNUNET_MessageHeader) + + 32, 65500, + MESSAGE_SIZE, &mtu)) + { + return NULL; + } + if (mtu < 1200) + GNUNET_GE_LOG (coreAPI->ectx, + GNUNET_GE_ERROR | GNUNET_GE_USER | GNUNET_GE_IMMEDIATE, + _("MTU %llu for `%s' is probably too low!\n"), mtu, "UDP"); + lock = GNUNET_mutex_create (GNUNET_NO); + if (0 != + GNUNET_GC_attach_change_listener (cfg, &reload_configuration, NULL)) + { + GNUNET_mutex_destroy (lock); + lock = NULL; + return NULL; + } + if (GNUNET_GC_get_configuration_value_yesno (cfg, "UDP", "UPNP", GNUNET_YES) + == GNUNET_YES) + { + upnp = coreAPI->service_request ("upnp"); + if (upnp == NULL) + GNUNET_GE_LOG (coreAPI->ectx, + GNUNET_GE_ERROR | GNUNET_GE_USER | GNUNET_GE_IMMEDIATE, + "The UPnP service could not be loaded. To disable UPnP, set the " + "configuration option \"UPNP\" in section \"%s\" to \"NO\"\n", + "UDP"); + } + stats = coreAPI->service_request ("stats"); + if (stats != NULL) + { + stat_bytesReceived + = stats->create (gettext_noop ("# bytes received via UDP")); + stat_bytesSent = stats->create (gettext_noop ("# bytes sent via UDP")); + stat_bytesDropped + = stats->create (gettext_noop ("# bytes dropped by UDP (outgoing)")); + stat_udpConnected + = stats->create (gettext_noop ("# UDP connections (right now)")); + } + myAPI.protocol_number = GNUNET_TRANSPORT_PROTOCOL_NUMBER_UDP; + myAPI.mtu = mtu - sizeof (UDPMessage); + myAPI.cost = 20000; + myAPI.hello_verify = &verify_hello; + myAPI.hello_create = &create_hello; + myAPI.connect = &udp_connect; + myAPI.send = &udp_send; + myAPI.associate = &udp_associate; + myAPI.disconnect = &udp_disconnect; + myAPI.server_start = &udp_transport_server_start; + myAPI.server_stop = &udp_transport_server_stop; + myAPI.hello_to_address = &hello_to_address; + myAPI.send_now_test = &udp_test_would_try; + + return &myAPI; +} + +void +donetransport_udp () +{ + do_shutdown (); +} + +/* end of udp.c */ diff --git a/src/transport/test_transport_api.c b/src/transport/test_transport_api.c new file mode 100644 index 000000000..02c28b09c --- /dev/null +++ b/src/transport/test_transport_api.c @@ -0,0 +1,305 @@ +/* + This file is part of GNUnet. + (C) 2009 Christian Grothoff (and other contributing authors) + + GNUnet is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published + by the Free Software Foundation; either version 2, 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 + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with GNUnet; see the file COPYING. If not, write to the + Free Software Foundation, Inc., 59 Temple Place - Suite 330, + Boston, MA 02111-1307, USA. +*/ +/** + * @file transport/test_transport_api.c + * @brief testcase for transport_api.c + */ +#include "platform.h" +#include "gnunet_common.h" +#include "gnunet_hello_lib.h" +#include "gnunet_getopt_lib.h" +#include "gnunet_os_lib.h" +#include "gnunet_program_lib.h" +#include "gnunet_scheduler_lib.h" +#include "gnunet_transport_service.h" +#include "transport.h" + +#define VERBOSE GNUNET_NO + +#define START_ARM GNUNET_YES + +/** + * How long until we give up on transmitting the message? + */ +#define TIMEOUT GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS, 30) + +#define MTYPE 12345 + +struct PeerContext +{ + struct GNUNET_CONFIGURATION_Handle *cfg; + struct GNUNET_TRANSPORT_Handle *th; + struct GNUNET_PeerIdentity id; +#if START_ARM + pid_t arm_pid; +#endif +}; + +static struct PeerContext p1; + +static struct PeerContext p2; + +static struct GNUNET_SCHEDULER_Handle *sched; + +static int ok; + +#if VERBOSE +#define OKPP do { ok++; fprintf (stderr, "Now at stage %u at %s:%u\n", ok, __FILE__, __LINE__); } while (0) +#else +#define OKPP do { ok++; } while (0) +#endif + + +static void +end () +{ + /* do work here */ + GNUNET_assert (ok == 8); + GNUNET_TRANSPORT_disconnect (p1.th); + GNUNET_TRANSPORT_disconnect (p2.th); + ok = 0; +} + + +/** + * Function called by the transport for each received message. + * + * @param cls closure + * @param latency estimated latency for communicating with the + * given peer + * @param peer (claimed) identity of the other peer + * @param message the message + */ +static void +notify_receive (void *cls, + struct GNUNET_TIME_Relative latency, + const struct GNUNET_PeerIdentity *peer, + const struct GNUNET_MessageHeader *message) +{ + GNUNET_assert (ok == 7); + OKPP; + GNUNET_assert (MTYPE == ntohs (message->type)); + GNUNET_assert (sizeof (struct GNUNET_MessageHeader) == + ntohs (message->size)); + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Received message from peer (%p)!\n", + cls); + end (); +} + + +/** + * Function called to notify transport users that another + * peer connected to us. + * + * @param cls closure + * @param transport the transport service handle + * @param peer the peer that disconnected + * @param latency current latency of the connection + */ +static void +notify_connect (void *cls, + const struct GNUNET_PeerIdentity *peer, + struct GNUNET_TIME_Relative latency) +{ + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Peer connected to us (%p)!\n", cls); + GNUNET_assert ((ok >= 1) && (ok <= 6)); + OKPP; +} + + +/** + * Function called to notify transport users that another + * peer disconnected from us. + * + * @param cls closure + * @param transport the transport service handle + * @param peer the peer that disconnected + */ +static void +notify_disconnect (void *cls, const struct GNUNET_PeerIdentity *peer) +{ + GNUNET_assert (0); +} + + +static void +setup_peer (struct PeerContext *p, const char *cfgname) +{ + p->cfg = GNUNET_CONFIGURATION_create (); +#if START_ARM + p->arm_pid = GNUNET_OS_start_process ("gnunet-service-arm", + "gnunet-service-arm", +#if VERBOSE + "-L", "DEBUG", +#endif + "-c", cfgname, NULL); + sleep (1); /* allow ARM to start */ +#endif + GNUNET_assert (GNUNET_OK == GNUNET_CONFIGURATION_load (p->cfg, cfgname)); + p->th = GNUNET_TRANSPORT_connect (sched, p->cfg, + p, + ¬ify_receive, + ¬ify_connect, ¬ify_disconnect); + GNUNET_assert (p->th != NULL); +} + + +static size_t +notify_ready (void *cls, size_t size, void *buf) +{ + struct GNUNET_MessageHeader *hdr; + + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Transmitting message to peer (%p) - %u!\n", cls, size); + GNUNET_assert (size >= 256); + GNUNET_assert ((ok >= 5) && (ok <= 6)); + OKPP; + hdr = buf; + hdr->size = htons (sizeof (struct GNUNET_MessageHeader)); + hdr->type = htons (MTYPE); + return sizeof (struct GNUNET_MessageHeader); +} + + +static void +exchange_hello_last (void *cls, + struct GNUNET_TIME_Relative latency, + const struct GNUNET_PeerIdentity *peer, + const struct GNUNET_MessageHeader *message) +{ + struct PeerContext *me = cls; + struct GNUNET_CRYPTO_RsaPublicKeyBinaryEncoded pk; + + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Exchanging HELLO with peer (%p)!\n", cls); + GNUNET_assert (ok >= 3); + OKPP; + GNUNET_assert (message != NULL); + GNUNET_assert (GNUNET_OK == + GNUNET_HELLO_get_key ((const struct GNUNET_HELLO_Message *) + message, &pk)); + GNUNET_CRYPTO_hash (&pk, + sizeof (struct GNUNET_CRYPTO_RsaPublicKeyBinaryEncoded), + &me->id.hashPubKey); + GNUNET_TRANSPORT_offer_hello (p1.th, message); + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Finished exchanging HELLOs, now waiting for transmission!\n"); + /* both HELLOs exchanged, get ready to test transmission! */ + GNUNET_TRANSPORT_notify_transmit_ready (p1.th, + &p2.id, + 256, TIMEOUT, ¬ify_ready, &p1); +} + + +static void +exchange_hello (void *cls, + struct GNUNET_TIME_Relative latency, + const struct GNUNET_PeerIdentity *peer, + const struct GNUNET_MessageHeader *message) +{ + struct PeerContext *me = cls; + struct GNUNET_CRYPTO_RsaPublicKeyBinaryEncoded pk; + + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Exchanging HELLO with peer (%p)!\n", cls); + GNUNET_assert (ok >= 2); + OKPP; + GNUNET_assert (message != NULL); + GNUNET_assert (GNUNET_OK == + GNUNET_HELLO_get_key ((const struct GNUNET_HELLO_Message *) + message, &pk)); + GNUNET_CRYPTO_hash (&pk, + sizeof (struct GNUNET_CRYPTO_RsaPublicKeyBinaryEncoded), + &me->id.hashPubKey); + GNUNET_TRANSPORT_get_hello (p2.th, GNUNET_TIME_UNIT_MINUTES, + &exchange_hello_last, &p2); +} + + +static void +run (void *cls, + struct GNUNET_SCHEDULER_Handle *s, + char *const *args, + const char *cfgfile, struct GNUNET_CONFIGURATION_Handle *cfg) +{ + GNUNET_assert (ok == 1); + OKPP; + sched = s; + setup_peer (&p1, "test_transport_api_peer1.conf"); + setup_peer (&p2, "test_transport_api_peer2.conf"); + GNUNET_TRANSPORT_get_hello (p1.th, + GNUNET_TIME_UNIT_MINUTES, &exchange_hello, &p1); +} + + +static void +stop_arm (struct PeerContext *p) +{ +#if START_ARM + if (0 != PLIBC_KILL (p->arm_pid, SIGTERM)) + GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING, "kill"); + waitpid (p->arm_pid, NULL, 0); +#endif + GNUNET_CONFIGURATION_destroy (p->cfg); +} + + +static int +check () +{ + char *const argv[] = { "test-transport-api", + "-c", + "test_transport_api_data.conf", +#if VERBOSE + "-L", "DEBUG", +#endif + NULL + }; + struct GNUNET_GETOPT_CommandLineOption options[] = { + GNUNET_GETOPT_OPTION_END + }; + + ok = 1; + GNUNET_PROGRAM_run ((sizeof (argv) / sizeof (char *)) - 1, + argv, "test-transport-api", "nohelp", + options, &run, &ok); + stop_arm (&p1); + stop_arm (&p2); + return ok; +} + +int +main (int argc, char *argv[]) +{ + int ret; + + GNUNET_log_setup ("test-transport-api", +#if VERBOSE + "DEBUG", +#else + "WARNING", +#endif + NULL); + ret = check (); + + return ret; +} + +/* end of test_transport_api.c */ diff --git a/src/transport/test_transport_api_data.conf b/src/transport/test_transport_api_data.conf new file mode 100644 index 000000000..0fa611350 --- /dev/null +++ b/src/transport/test_transport_api_data.conf @@ -0,0 +1,24 @@ +[PATHS] +SERVICEHOME = /tmp/test-gnunetd-transport-master/ + +[resolver] +PORT = 2364 + +[transport] +PORT = 2365 +PLUGINS = tcp + +[arm] +PORT = 2366 + +[statistics] +PORT = 2367 + +[tcp] +PORT = 2368 + +[peerinfo] +PORT = 2369 + +[testing] +WEAKRANDOM = YES diff --git a/src/transport/test_transport_api_peer1.conf b/src/transport/test_transport_api_peer1.conf new file mode 100644 index 000000000..dcc0ab4cf --- /dev/null +++ b/src/transport/test_transport_api_peer1.conf @@ -0,0 +1,25 @@ +[PATHS] +SERVICEHOME = /tmp/test-gnunetd-transport-peer-1/ +DEFAULTCONFIG = test_transport_api_peer1.conf + +[resolver] +PORT = 12364 + +[transport] +PORT = 12365 +PLUGINS = tcp + +[arm] +PORT = 12366 + +[statistics] +PORT = 12367 + +[tcp] +PORT = 12368 + +[peerinfo] +PORT = 12369 + +[testing] +WEAKRANDOM = YES diff --git a/src/transport/test_transport_api_peer2.conf b/src/transport/test_transport_api_peer2.conf new file mode 100644 index 000000000..8567c6ac8 --- /dev/null +++ b/src/transport/test_transport_api_peer2.conf @@ -0,0 +1,25 @@ +[PATHS] +SERVICEHOME = /tmp/test-gnunetd-transport-peer-2/ +DEFAULTCONFIG = test_transport_api_peer2.conf + +[resolver] +PORT = 22364 + +[transport] +PORT = 22365 +PLUGINS = tcp + +[arm] +PORT = 22366 + +[statistics] +PORT = 22367 + +[tcp] +PORT = 22368 + +[peerinfo] +PORT = 22369 + +[testing] +WEAKRANDOM = YES diff --git a/src/transport/transport.h b/src/transport/transport.h new file mode 100644 index 000000000..8e1291005 --- /dev/null +++ b/src/transport/transport.h @@ -0,0 +1,238 @@ +/* + This file is part of GNUnet. + (C) 2009 Christian Grothoff (and other contributing authors) + + GNUnet is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published + by the Free Software Foundation; either version 2, 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 + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with GNUnet; see the file COPYING. If not, write to the + Free Software Foundation, Inc., 59 Temple Place - Suite 330, + Boston, MA 02111-1307, USA. +*/ + +/** + * @file transport/transport.h + * @brief common internal definitions for transport service + * @author Christian Grothoff + */ +#include "gnunet_crypto_lib.h" +#include "gnunet_time_lib.h" +#include "gnunet_transport_service.h" + +#define DEBUG_TRANSPORT GNUNET_NO + +/** + * For how long do we allow unused bandwidth + * from the past to carry over into the future? (in ms) + */ +#define MAX_BANDWIDTH_CARRY 5000 + +/** + * How often do we (at most) do a full quota + * recalculation? (in ms) + */ +#define MIN_QUOTA_REFRESH_TIME 2000 + +/** + * 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; + + /** + * Current quota for outbound traffic in bytes/ms. + * (should be equal to system default) + */ + uint32_t quota_out GNUNET_PACKED; + + /** + * Latency estimate. + */ + struct GNUNET_TIME_RelativeNBO latency; + + /** + * 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 set a particular bandwidth quota. Send + * TO the service to set an incoming quota, send FROM the + * service to update an outgoing quota. + */ +struct QuotaSetMessage +{ + + /** + * Type will be GNUNET_MESSAGE_TYPE_TRANSPORT_NEIGHBOUR_INFO + */ + struct GNUNET_MessageHeader header; + + /** + * Quota in bytes per ms, 0 to drop everything; + * in network byte order. + */ + uint32_t quota_in GNUNET_PACKED; + + /** + * About which peer are we talking here? + */ + struct GNUNET_PeerIdentity peer; + +}; + + +/** + * Message used to ask the transport service to connect + * to a particular peer. + */ +struct TryConnectMessage +{ + + /** + * Type will be GNUNET_MESSAGE_TYPE_TRANSPORT_TRY_CONNECT. + */ + struct GNUNET_MessageHeader header; + + /** + * Always zero. + */ + uint32_t reserved GNUNET_PACKED; + + /** + * About which peer are we talking here? + */ + 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; + + /** + * Always zero. + */ + uint32_t reserved GNUNET_PACKED; + + /** + * Latency estimate. + */ + struct GNUNET_TIME_RelativeNBO latency; + + /** + * 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; + + /** + * 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. + */ + uint32_t success GNUNET_PACKED; + + /** + * Which peer can send more 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; + + /** + * Always zero. + */ + uint32_t reserved GNUNET_PACKED; + + /** + * Which peer should receive the message? + */ + struct GNUNET_PeerIdentity peer; + +}; + + + + + + +/* end of transport.h */ diff --git a/src/transport/transport_api.c b/src/transport/transport_api.c new file mode 100644 index 000000000..d6d4e2a96 --- /dev/null +++ b/src/transport/transport_api.c @@ -0,0 +1,1863 @@ +/* + This file is part of GNUnet. + (C) 2009 Christian Grothoff (and other contributing authors) + + GNUnet is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published + by the Free Software Foundation; either version 2, 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 + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with GNUnet; see the file COPYING. If not, write to the + Free Software Foundation, Inc., 59 Temple Place - Suite 330, + Boston, MA 02111-1307, USA. +*/ + +/** + * @file transport/transport_api.c + * @brief library to access the low-level P2P IO service + * @author Christian Grothoff + * + * TODO: + * - set_quota with low bandwidth should cause peer + * disconnects (currently never does that) (MINOR) + */ +#include "platform.h" +#include "gnunet_client_lib.h" +#include "gnunet_arm_service.h" +#include "gnunet_hello_lib.h" +#include "gnunet_protocols.h" +#include "gnunet_server_lib.h" +#include "gnunet_time_lib.h" +#include "gnunet_transport_service.h" +#include "transport.h" + +/** + * After how long do we give up on transmitting a HELLO + * to the service? + */ +#define OFFER_HELLO_TIMEOUT GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS, 30) + +/** + * How long should ARM wait when starting up the + * transport service before reporting back? + */ +#define START_SERVICE_TIMEOUT GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS, 5) + +/** + * How long should ARM wait when stopping the + * transport service before reporting back? + */ +#define STOP_SERVICE_TIMEOUT GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS, 5) + +/** + * Entry in linked list of all of our current neighbours. + */ +struct NeighbourList +{ + + /** + * This is a linked list. + */ + struct NeighbourList *next; + + /** + * Active transmit handle, can be NULL. Used to move + * from ready to wait list on disconnect and to block + * two transmissions to the same peer from being scheduled + * at the same time. + */ + struct GNUNET_TRANSPORT_TransmitHandle *transmit_handle; + + + /** + * Identity of this neighbour. + */ + struct GNUNET_PeerIdentity id; + + /** + * At what time did we reset last_sent last? + */ + struct GNUNET_TIME_Absolute last_quota_update; + + /** + * How many bytes have we sent since the "last_quota_update" + * timestamp? + */ + uint64_t last_sent; + + /** + * Global quota for outbound traffic to the neighbour in bytes/ms. + */ + uint32_t quota_out; + + /** + * Set to GNUNET_YES if we are currently allowed to + * transmit a message to the transport service for this + * peer, GNUNET_NO otherwise. + */ + int transmit_ok; + + /** + * Set to GNUNET_YES if we have received an ACK for the + * given peer. Peers that receive our HELLO always respond + * with an ACK to let us know that we are successfully + * communicating. Note that a PING can not be used for this + * since PINGs are only send if a HELLO address requires + * confirmation (and also, PINGs are not passed to the + * transport API itself). + */ + int received_ack; + +}; + + +/** + * Linked list of requests from clients for our HELLO + * that were deferred. + */ +struct HelloWaitList +{ + + /** + * This is a linked list. + */ + struct HelloWaitList *next; + + /** + * Reference back to our transport handle. + */ + struct GNUNET_TRANSPORT_Handle *handle; + + /** + * Callback to call once we got our HELLO. + */ + GNUNET_TRANSPORT_ReceiveCallback rec; + + /** + * Closure for rec. + */ + void *rec_cls; + + /** + * When to time out (call rec with NULL). + */ + struct GNUNET_TIME_Absolute timeout; + + /** + * Timeout task (used to trigger timeout, + * cancel if we get the HELLO in time). + */ + GNUNET_SCHEDULER_TaskIdentifier task; + + +}; + + +/** + * Opaque handle for a transmission-ready request. + */ +struct GNUNET_TRANSPORT_TransmitHandle +{ + + /** + * We keep the transmit handles that are waiting for + * a transport-level connection in a doubly linked list. + */ + struct GNUNET_TRANSPORT_TransmitHandle *next; + + /** + * We keep the transmit handles that are waiting for + * a transport-level connection in a doubly linked list. + */ + struct GNUNET_TRANSPORT_TransmitHandle *prev; + + /** + * Handle of the main transport data structure. + */ + struct GNUNET_TRANSPORT_Handle *handle; + + /** + * Neighbour for this handle, can be NULL if the service + * is not yet connected to the target. + */ + struct NeighbourList *neighbour; + + /** + * Which peer is this transmission going to be for? All + * zeros if it is control-traffic to the service. + */ + struct GNUNET_PeerIdentity target; + + /** + * Function to call when notify_size bytes are available + * for transmission. + */ + GNUNET_NETWORK_TransmitReadyNotify notify; + + /** + * Closure for notify. + */ + void *notify_cls; + + /** + * transmit_ready task Id. The task is used to introduce + * the artificial delay that may be required to maintain + * the bandwidth limits. + */ + GNUNET_SCHEDULER_TaskIdentifier notify_delay_task; + + /** + * Timeout for this request. + */ + struct GNUNET_TIME_Absolute timeout; + + /** + * How many bytes is our notify callback waiting for? + */ + size_t notify_size; + +}; + + +/** + * Handle for the transport service (includes all of the + * state for the transport service). + */ +struct GNUNET_TRANSPORT_Handle +{ + + /** + * Closure for the callbacks. + */ + void *cls; + + /** + * Function to call for received data. + */ + GNUNET_TRANSPORT_ReceiveCallback rec; + + /** + * function to call on connect events + */ + GNUNET_TRANSPORT_NotifyConnect nc_cb; + + /** + * function to call on disconnect events + */ + GNUNET_TRANSPORT_NotifyDisconnect nd_cb; + + /** + * The current HELLO message for this peer. Updated + * whenever transports change their addresses. + */ + struct GNUNET_HELLO_Message *my_hello; + + /** + * My client connection to the transport service. + */ + struct GNUNET_CLIENT_Connection *client; + + /** + * Handle to our registration with the client for notification. + */ + struct GNUNET_NETWORK_TransmitHandle *network_handle; + + /** + * Linked list of transmit handles that are waiting for the + * transport to connect to the respective peer. When we + * receive notification that the transport connected to a + * peer, we go over this list and check if someone has already + * requested a transmission to the new peer; if so, we trigger + * the next step. + */ + struct GNUNET_TRANSPORT_TransmitHandle *connect_wait_head; + + /** + * Linked list of transmit handles that are waiting for the + * transport to be ready for transmission to the respective + * peer. When we + * receive notification that the transport disconnected from + * a peer, we go over this list and move the entry back to + * the connect_wait list. + */ + struct GNUNET_TRANSPORT_TransmitHandle *connect_ready_head; + + /** + * Linked list of pending requests for our HELLO. + */ + struct HelloWaitList *hwl_head; + + /** + * My scheduler. + */ + struct GNUNET_SCHEDULER_Handle *sched; + + /** + * My configuration. + */ + struct GNUNET_CONFIGURATION_Handle *cfg; + + /** + * Linked list of the current neighbours of this peer. + */ + struct NeighbourList *neighbours; + + /** + * ID of the task trying to reconnect to the + * service. + */ + GNUNET_SCHEDULER_TaskIdentifier reconnect_task; + + /** + * Delay until we try to reconnect. + */ + struct GNUNET_TIME_Relative reconnect_delay; + + /** + * Do we currently have a transmission pending? + * (schedule transmission was called but has not + * yet succeeded)? + */ + int transmission_scheduled; +}; + + +static struct NeighbourList * +find_neighbour (struct GNUNET_TRANSPORT_Handle *h, + const struct GNUNET_PeerIdentity *peer) +{ + struct NeighbourList *pos; + + pos = h->neighbours; + while ((pos != NULL) && + (0 != memcmp (peer, &pos->id, sizeof (struct GNUNET_PeerIdentity)))) + pos = pos->next; + return pos; +} + + +/** + * Schedule the task to send one message from the + * connect_ready list to the service. + */ +static void schedule_transmission (struct GNUNET_TRANSPORT_Handle *h); + + +/** + * Transmit message to client... + */ +static size_t +transport_notify_ready (void *cls, size_t size, void *buf) +{ + struct GNUNET_TRANSPORT_Handle *h = cls; + struct GNUNET_TRANSPORT_TransmitHandle *th; + struct NeighbourList *n; + size_t ret; + char *cbuf; + +#if DEBUG_TRANSPORT + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Ready to transmit %u bytes to transport service\n", size); +#endif + h->network_handle = NULL; + h->transmission_scheduled = GNUNET_NO; + if (buf == NULL) + { + th = h->connect_ready_head; + if (th->next != NULL) + th->next->prev = NULL; + h->connect_ready_head = th->next; + if (NULL != (n = th->neighbour)) + { + GNUNET_assert (n->transmit_handle == th); + n->transmit_handle = NULL; + } + GNUNET_assert (0 == th->notify (th->notify_cls, 0, NULL)); + GNUNET_free (th); + return 0; + } + cbuf = buf; + ret = 0; + h->network_handle = NULL; + h->transmission_scheduled = GNUNET_NO; + do + { + th = h->connect_ready_head; + GNUNET_assert (th->notify_size <= size); + if (th->next != NULL) + th->next->prev = NULL; + h->connect_ready_head = th->next; + if (NULL != (n = th->neighbour)) + { + GNUNET_assert (n->transmit_handle == th); + n->transmit_handle = NULL; + } + ret += th->notify (th->notify_cls, size, &cbuf[ret]); + GNUNET_free (th); + if (n != NULL) + n->last_sent += ret; + size -= ret; + } + while ((h->connect_ready_head != NULL) && + (h->connect_ready_head->notify_size <= size)); + if (h->connect_ready_head != NULL) + schedule_transmission (h); +#if DEBUG_TRANSPORT + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Transmitting %u bytes to transport service\n", ret); +#endif + return ret; +} + + +/** + * Schedule the task to send one message from the + * connect_ready list to the service. + */ +static void +schedule_transmission (struct GNUNET_TRANSPORT_Handle *h) +{ + struct GNUNET_TRANSPORT_TransmitHandle *th; + + GNUNET_assert (NULL == h->network_handle); + if (h->client == NULL) + { +#if DEBUG_TRANSPORT + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Not yet connected to transport service, need to wait.\n"); +#endif + return; + } + th = h->connect_ready_head; + if (th == NULL) + { +#if DEBUG_TRANSPORT + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Schedule transmission called, but no request is pending.\n"); +#endif + return; + } + h->transmission_scheduled = GNUNET_YES; +#if DEBUG_TRANSPORT + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Asking client API for transmission of %u bytes\n", + th->notify_size); +#endif + h->network_handle = GNUNET_CLIENT_notify_transmit_ready (h->client, + th->notify_size, + GNUNET_TIME_absolute_get_remaining + (th->timeout), + &transport_notify_ready, + h); + GNUNET_assert (NULL != h->network_handle); +} + + +/** + * Insert the given transmit handle in the given sorted + * doubly linked list based on timeout. + * + * @param head pointer to the head of the linked list + * @param th element to insert into the list + */ +static void +insert_transmit_handle (struct GNUNET_TRANSPORT_TransmitHandle **head, + struct GNUNET_TRANSPORT_TransmitHandle *th) +{ + struct GNUNET_TRANSPORT_TransmitHandle *pos; + struct GNUNET_TRANSPORT_TransmitHandle *prev; + + pos = *head; + prev = NULL; + while ((pos != NULL) && (pos->timeout.value < th->timeout.value)) + { + prev = pos; + pos = pos->next; + } + if (prev == NULL) + { + th->next = *head; + if (th->next != NULL) + th->next->prev = th; + *head = th; + } + else + { + th->next = pos; + th->prev = prev; + prev->next = th; + if (pos != NULL) + pos->prev = th; + } +} + + +/** + * Queue control request for transmission to the transport + * service. + * + * @param size number of bytes to be transmitted + * @param at_head request must be added to the head of the queue + * (otherwise request will be appended) + * @param timeout how long this transmission can wait (at most) + * @param notify function to call to get the content + * @param notify_cls closure for notify + */ +static void +schedule_control_transmit (struct GNUNET_TRANSPORT_Handle *h, + size_t size, + int at_head, + struct GNUNET_TIME_Relative timeout, + GNUNET_NETWORK_TransmitReadyNotify notify, + void *notify_cls) +{ + struct GNUNET_TRANSPORT_TransmitHandle *th; +#if DEBUG_TRANSPORT + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Queueing %u bytes control transmission request.\n", size); +#endif + th = GNUNET_malloc (sizeof (struct GNUNET_TRANSPORT_TransmitHandle)); + th->handle = h; + th->notify = notify; + th->notify_cls = notify_cls; + th->timeout = GNUNET_TIME_relative_to_absolute (timeout); + th->notify_size = size; + if (at_head) + { + th->next = h->connect_ready_head; + h->connect_ready_head = th; + if (th->next != NULL) + th->next->prev = th; + } + else + { + insert_transmit_handle (&h->connect_ready_head, th); + } + if (GNUNET_NO == h->transmission_scheduled) + schedule_transmission (h); +} + + +/** + * Update the quota values for the given neighbour now. + */ +static void +update_quota (struct NeighbourList *n) +{ + struct GNUNET_TIME_Relative delta; + uint64_t allowed; + uint64_t remaining; + + delta = GNUNET_TIME_absolute_get_duration (n->last_quota_update); + allowed = delta.value * n->quota_out; + if (n->last_sent < allowed) + { + remaining = allowed - n->last_sent; + if (n->quota_out > 0) + remaining /= n->quota_out; + else + remaining = 0; + if (remaining > MAX_BANDWIDTH_CARRY) + remaining = MAX_BANDWIDTH_CARRY; + n->last_sent = 0; + n->last_quota_update = GNUNET_TIME_absolute_get (); + n->last_quota_update.value -= remaining; + } + else + { + n->last_sent -= allowed; + n->last_quota_update = GNUNET_TIME_absolute_get (); + } +} + + +struct SetQuotaContext +{ + struct GNUNET_TRANSPORT_Handle *handle; + + struct GNUNET_PeerIdentity target; + + GNUNET_SCHEDULER_Task cont; + + void *cont_cls; + + struct GNUNET_TIME_Absolute timeout; + + uint32_t quota_in; +}; + + +static size_t +send_set_quota (void *cls, size_t size, void *buf) +{ + struct SetQuotaContext *sqc = cls; + struct QuotaSetMessage *msg; + + if (buf == NULL) + { + GNUNET_SCHEDULER_add_continuation (sqc->handle->sched, + GNUNET_NO, + sqc->cont, + sqc->cont_cls, + GNUNET_SCHEDULER_REASON_TIMEOUT); + GNUNET_free (sqc); + return 0; + } +#if DEBUG_TRANSPORT + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Transmitting `%s' request with respect to `%4s'.\n", + "SET_QUOTA", GNUNET_i2s (&sqc->target)); +#endif + GNUNET_assert (size >= sizeof (struct QuotaSetMessage)); + msg = buf; + msg->header.size = htons (sizeof (struct QuotaSetMessage)); + msg->header.type = htons (GNUNET_MESSAGE_TYPE_TRANSPORT_SET_QUOTA); + msg->quota_in = htonl (sqc->quota_in); + memcpy (&msg->peer, &sqc->target, sizeof (struct GNUNET_PeerIdentity)); + if (sqc->cont != NULL) + GNUNET_SCHEDULER_add_continuation (sqc->handle->sched, + GNUNET_NO, + sqc->cont, + sqc->cont_cls, + GNUNET_SCHEDULER_REASON_PREREQ_DONE); + GNUNET_free (sqc); + return sizeof (struct QuotaSetMessage); +} + + +/** + * Set the share of incoming bandwidth for the given + * peer to the specified amount. + * + * @param handle connection to transport service + * @param target who's bandwidth quota is being changed + * @param quota_in incoming bandwidth quota in bytes per ms; 0 can + * be used to force all traffic to be discarded + * @param quota_out outgoing bandwidth quota in bytes per ms; 0 can + * be used to force all traffic to be discarded + * @param timeout how long to wait until signaling failure if + * we can not communicate the quota change + * @param cont continuation to call when done, will be called + * either with reason "TIMEOUT" or with reason "PREREQ_DONE" + * @param cont_cls closure for continuation + */ +void +GNUNET_TRANSPORT_set_quota (struct GNUNET_TRANSPORT_Handle *handle, + const struct GNUNET_PeerIdentity *target, + uint32_t quota_in, + uint32_t quota_out, + struct GNUNET_TIME_Relative timeout, + GNUNET_SCHEDULER_Task cont, void *cont_cls) +{ + struct NeighbourList *n; + struct SetQuotaContext *sqc; + + n = find_neighbour (handle, target); + if (n != NULL) + { + update_quota (n); + if (n->quota_out < quota_out) + n->last_quota_update = GNUNET_TIME_absolute_get (); + n->quota_out = quota_out; + } + sqc = GNUNET_malloc (sizeof (struct SetQuotaContext)); + sqc->handle = handle; + sqc->target = *target; + sqc->cont = cont; + sqc->cont_cls = cont_cls; + sqc->timeout = GNUNET_TIME_relative_to_absolute (timeout); + sqc->quota_in = quota_in; + schedule_control_transmit (handle, + sizeof (struct QuotaSetMessage), + GNUNET_NO, timeout, &send_set_quota, sqc); +} + + +/** + * A "get_hello" request has timed out. Signal the client + * and clean up. + */ +static void +hello_wait_timeout (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc) +{ + struct HelloWaitList *hwl = cls; + struct HelloWaitList *pos; + struct HelloWaitList *prev; + + prev = NULL; + pos = hwl->handle->hwl_head; + while (pos != hwl) + { + GNUNET_assert (pos != NULL); + prev = pos; + pos = pos->next; + } + if (prev == NULL) + hwl->handle->hwl_head = hwl->next; + else + prev->next = hwl->next; + GNUNET_log (GNUNET_ERROR_TYPE_WARNING, + _("Timeout trying to obtain `%s' from transport service.\n"), + "HELLO"); + /* signal timeout */ + if (hwl->rec != NULL) + hwl->rec (hwl->rec_cls, GNUNET_TIME_UNIT_ZERO, NULL, NULL); + GNUNET_free (hwl); +} + + +/** + * Obtain the HELLO message for this peer. + * + * @param handle connection to transport service + * @param timeout how long to wait for the HELLO + * @param rec function to call with the HELLO, sender will be our peer + * identity; message and sender will be NULL on timeout + * (handshake with transport service pending/failed). + * cost estimate will be 0. + * @param rec_cls closure for rec + */ +void +GNUNET_TRANSPORT_get_hello (struct GNUNET_TRANSPORT_Handle *handle, + struct GNUNET_TIME_Relative timeout, + GNUNET_TRANSPORT_ReceiveCallback rec, + void *rec_cls) +{ + struct GNUNET_CRYPTO_RsaPublicKeyBinaryEncoded pk; + struct GNUNET_PeerIdentity me; + struct HelloWaitList *hwl; + + if (handle->my_hello == NULL) + { + hwl = GNUNET_malloc (sizeof (struct HelloWaitList)); + hwl->next = handle->hwl_head; + handle->hwl_head = hwl; + hwl->handle = handle; + hwl->rec = rec; + hwl->rec_cls = rec_cls; + hwl->timeout = GNUNET_TIME_relative_to_absolute (timeout); + hwl->task = GNUNET_SCHEDULER_add_delayed (handle->sched, + GNUNET_YES, + GNUNET_SCHEDULER_PRIORITY_KEEP, + GNUNET_SCHEDULER_NO_PREREQUISITE_TASK, + timeout, + &hello_wait_timeout, hwl); + return; + } + GNUNET_assert (GNUNET_OK == GNUNET_HELLO_get_key (handle->my_hello, &pk)); + GNUNET_CRYPTO_hash (&pk, + sizeof (struct GNUNET_CRYPTO_RsaPublicKeyBinaryEncoded), + &me.hashPubKey); + + rec (rec_cls, + GNUNET_TIME_UNIT_ZERO, + &me, (const struct GNUNET_MessageHeader *) handle->my_hello); +} + + +static size_t +send_hello (void *cls, size_t size, void *buf) +{ + struct GNUNET_MessageHeader *hello = cls; + uint16_t msize; + + if (buf == NULL) + { +#if DEBUG_TRANSPORT + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Timeout while trying to transmit `%s' request.\n", + "HELLO"); +#endif + GNUNET_free (hello); + return 0; + } +#if DEBUG_TRANSPORT + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Transmitting `%s' request.\n", "HELLO"); +#endif + msize = ntohs (hello->size); + GNUNET_assert (size >= msize); + memcpy (buf, hello, msize); + GNUNET_free (hello); + return msize; +} + + +/** + * Offer the transport service the HELLO of another peer. Note that + * the transport service may just ignore this message if the HELLO is + * malformed or useless due to our local configuration. + * + * @param handle connection to transport service + * @param hello the hello message + */ +void +GNUNET_TRANSPORT_offer_hello (struct GNUNET_TRANSPORT_Handle *handle, + const struct GNUNET_MessageHeader *hello) +{ + struct GNUNET_MessageHeader *hc; + uint16_t size; + + if (handle->client == NULL) + { +#if DEBUG_TRANSPORT + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Not connected to transport service, dropping offered HELLO\n"); +#endif + return; + } + GNUNET_break (ntohs (hello->type) == GNUNET_MESSAGE_TYPE_HELLO); + size = ntohs (hello->size); + GNUNET_break (size >= sizeof (struct GNUNET_MessageHeader)); + hc = GNUNET_malloc (size); + memcpy (hc, hello, size); + schedule_control_transmit (handle, + size, + GNUNET_NO, OFFER_HELLO_TIMEOUT, &send_hello, hc); +} + + +/** + * Function we use for handling incoming messages. + */ +static void demultiplexer (void *cls, const struct GNUNET_MessageHeader *msg); + + +static size_t +send_start (void *cls, size_t size, void *buf) +{ + struct GNUNET_MessageHeader *s = buf; + + if (buf == NULL) + return 0; +#if DEBUG_TRANSPORT + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Transmitting `%s' request.\n", "START"); +#endif + GNUNET_assert (size >= sizeof (struct GNUNET_MessageHeader)); + s->size = htons (sizeof (struct GNUNET_MessageHeader)); + s->type = htons (GNUNET_MESSAGE_TYPE_TRANSPORT_START); + return sizeof (struct GNUNET_MessageHeader); +} + + +/** + * Try again to connect to transport service. + */ +static void +reconnect (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc) +{ + struct GNUNET_TRANSPORT_Handle *h = cls; + struct GNUNET_TRANSPORT_TransmitHandle *pos; + struct NeighbourList *n; + + while (NULL != (n = h->neighbours)) + { + h->neighbours = n->next; + pos = n->transmit_handle; + if (pos != NULL) + { + pos->neighbour = NULL; + pos->next = h->connect_wait_head; + h->connect_wait_head = pos; + if (pos->next != NULL) + pos->next->prev = pos; + pos->prev = NULL; + } + GNUNET_free (n); + } + h->connect_ready_head = NULL; +#if DEBUG_TRANSPORT + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Connecting to transport service.\n"); +#endif + GNUNET_assert (h->client == NULL); + h->reconnect_task = GNUNET_SCHEDULER_NO_PREREQUISITE_TASK; + h->client = GNUNET_CLIENT_connect (h->sched, "transport", h->cfg); + GNUNET_assert (h->client != NULL); + /* make sure we don't send "START" twice, + remove existing entry from queue (if present) */ + pos = h->connect_ready_head; + while (pos != NULL) + { + if (pos->notify == &send_start) + { + if (pos->prev == NULL) + h->connect_ready_head = pos->next; + else + pos->prev->next = pos->next; + if (pos->next != NULL) + pos->next->prev = pos->prev; + GNUNET_assert (pos->neighbour == NULL); + GNUNET_free (pos); + break; + } + pos = pos->next; + } + schedule_control_transmit (h, + sizeof (struct GNUNET_MessageHeader), + GNUNET_YES, + GNUNET_TIME_UNIT_FOREVER_REL, &send_start, NULL); + GNUNET_CLIENT_receive (h->client, + &demultiplexer, h, GNUNET_TIME_UNIT_FOREVER_REL); +} + + +/** + * Function that will schedule the job that will try + * to connect us again to the client. + */ +static void +schedule_reconnect (struct GNUNET_TRANSPORT_Handle *h) +{ +#if DEBUG_TRANSPORT + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Scheduling task to reconnect to transport service in %llu ms.\n", + h->reconnect_delay.value); +#endif + GNUNET_assert (h->client == NULL); + GNUNET_assert (h->reconnect_task == GNUNET_SCHEDULER_NO_PREREQUISITE_TASK); + h->reconnect_task + = GNUNET_SCHEDULER_add_delayed (h->sched, + GNUNET_NO, + GNUNET_SCHEDULER_PRIORITY_DEFAULT, + GNUNET_SCHEDULER_NO_PREREQUISITE_TASK, + h->reconnect_delay, &reconnect, h); + h->reconnect_delay = GNUNET_TIME_UNIT_SECONDS; +} + + +/** + * Remove the given transmit handle from the wait list. Does NOT free + * it. + */ +static void +remove_from_wait_list (struct GNUNET_TRANSPORT_TransmitHandle *th) +{ + if (th->prev == NULL) + th->handle->connect_wait_head = th->next; + else + th->prev->next = th->next; + if (th->next != NULL) + th->next->prev = th->prev; +} + + +/** + * We are connected to the respective peer, check the + * bandwidth limits and schedule the transmission. + */ +static void schedule_request (struct GNUNET_TRANSPORT_TransmitHandle *th); + + +/** + * Function called by the scheduler when the timeout + * for bandwidth availablility for the target + * neighbour is reached. + */ +static void +transmit_ready (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc) +{ + struct GNUNET_TRANSPORT_TransmitHandle *th = cls; + + th->notify_delay_task = GNUNET_SCHEDULER_NO_PREREQUISITE_TASK; + schedule_request (th); +} + + +/** + * Called when our transmit request timed out before any transport + * reported success connecting to the desired peer or before the + * transport was ready to receive. Signal error and free + * TransmitHandle. + */ +static void +transmit_timeout (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc) +{ + struct GNUNET_TRANSPORT_TransmitHandle *th = cls; + + if (th->neighbour != NULL) + th->neighbour->transmit_handle = NULL; +#if DEBUG_TRANSPORT + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Transmission request timed out.\n"); +#endif + th->notify_delay_task = GNUNET_SCHEDULER_NO_PREREQUISITE_TASK; + remove_from_wait_list (th); + th->notify (th->notify_cls, 0, NULL); + GNUNET_free (th); +} + + +/** + * We are connected to the respective peer, check the + * bandwidth limits and schedule the transmission. + */ +static void +schedule_request (struct GNUNET_TRANSPORT_TransmitHandle *th) +{ + struct GNUNET_TRANSPORT_Handle *h; + struct GNUNET_TIME_Relative duration; + struct NeighbourList *n; + uint64_t available; + + h = th->handle; + n = th->neighbour; + if (th->notify_delay_task != GNUNET_SCHEDULER_NO_PREREQUISITE_TASK) + { + GNUNET_SCHEDULER_cancel (h->sched, th->notify_delay_task); + th->notify_delay_task = GNUNET_SCHEDULER_NO_PREREQUISITE_TASK; + } + /* check outgoing quota */ + duration = GNUNET_TIME_absolute_get_duration (n->last_quota_update); + if (duration.value > MIN_QUOTA_REFRESH_TIME) + { + update_quota (n); + duration = GNUNET_TIME_absolute_get_duration (n->last_quota_update); + } + available = duration.value * n->quota_out; + if (available < n->last_sent + th->notify_size) + { + /* calculate how much bandwidth we'd still need to + accumulate and based on that how long we'll have + to wait... */ + available = n->last_sent + th->notify_size - available; + duration = GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_MILLISECONDS, + available / n->quota_out); + if (th->timeout.value < + GNUNET_TIME_relative_to_absolute (duration).value) + { + /* signal timeout! */ +#if DEBUG_TRANSPORT + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Would need %llu ms before bandwidth is available for delivery, that is too long. Signaling timeout.\n", + duration.value); +#endif + remove_from_wait_list (th); + th->notify (th->notify_cls, 0, NULL); + GNUNET_free (th); + return; + } +#if DEBUG_TRANSPORT + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Need more bandwidth, delaying delivery by %llu ms\n", + duration.value); +#endif + th->notify_delay_task + = GNUNET_SCHEDULER_add_delayed (h->sched, + GNUNET_NO, + GNUNET_SCHEDULER_PRIORITY_KEEP, + GNUNET_SCHEDULER_NO_PREREQUISITE_TASK, + duration, &transmit_ready, th); + return; + } +#if DEBUG_TRANSPORT + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Bandwidth available for transmission to `%4s'\n", + GNUNET_i2s (&n->id)); +#endif + if (GNUNET_NO == n->transmit_ok) + { + /* we may be ready, but transport service is not; + wait for SendOkMessage or timeout */ +#if DEBUG_TRANSPORT + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Need to wait for transport service `%s' message\n", + "SEND_OK"); +#endif + th->notify_delay_task + = GNUNET_SCHEDULER_add_delayed (h->sched, + GNUNET_NO, + GNUNET_SCHEDULER_PRIORITY_KEEP, + GNUNET_SCHEDULER_NO_PREREQUISITE_TASK, + GNUNET_TIME_absolute_get_remaining + (th->timeout), &transmit_timeout, th); + return; + } + n->transmit_ok = GNUNET_NO; + remove_from_wait_list (th); +#if DEBUG_TRANSPORT + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Moving message to ready list\n"); +#endif + insert_transmit_handle (&h->connect_ready_head, th); + if (GNUNET_NO == h->transmission_scheduled) + schedule_transmission (h); +} + + +/** + * Add neighbour to our list + */ +static void +add_neighbour (struct GNUNET_TRANSPORT_Handle *h, + uint32_t quota_out, + struct GNUNET_TIME_Relative latency, + const struct GNUNET_PeerIdentity *pid) +{ + struct NeighbourList *n; + struct GNUNET_TRANSPORT_TransmitHandle *prev; + struct GNUNET_TRANSPORT_TransmitHandle *pos; + struct GNUNET_TRANSPORT_TransmitHandle *next; + + /* check for duplicates */ + if (NULL != find_neighbour (h, pid)) + { + GNUNET_break (0); + return; + } +#if DEBUG_TRANSPORT + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Creating entry for new neighbour `%4s'.\n", GNUNET_i2s (pid)); +#endif + n = GNUNET_malloc (sizeof (struct NeighbourList)); + n->id = *pid; + n->last_quota_update = GNUNET_TIME_absolute_get (); + n->quota_out = quota_out; + n->next = h->neighbours; + n->transmit_ok = GNUNET_YES; + h->neighbours = n; + if (h->nc_cb != NULL) + h->nc_cb (h->cls, &n->id, latency); + prev = NULL; + pos = h->connect_wait_head; + while (pos != NULL) + { + next = pos->next; +#if DEBUG_TRANSPORT + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Found entry in connect_wait_head for `%4s'.\n", + GNUNET_i2s (&pos->target)); +#endif + if (0 == memcmp (pid, + &pos->target, sizeof (struct GNUNET_PeerIdentity))) + { +#if DEBUG_TRANSPORT + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Found pending request for new connection, will trigger now.\n"); +#endif + pos->neighbour = n; + if (pos->notify_delay_task != GNUNET_SCHEDULER_NO_PREREQUISITE_TASK) + { + GNUNET_SCHEDULER_cancel (h->sched, pos->notify_delay_task); + pos->notify_delay_task = GNUNET_SCHEDULER_NO_PREREQUISITE_TASK; + } + GNUNET_assert (NULL == n->transmit_handle); + n->transmit_handle = pos; + if (GNUNET_YES == n->received_ack) + { +#if DEBUG_TRANSPORT + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "`%s' already received, scheduling request\n", + "ACK"); +#endif + schedule_request (pos); + } + else + { +#if DEBUG_TRANSPORT + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Still need to wait to receive `%s' message\n", + "ACK"); +#endif + pos->notify_delay_task + = GNUNET_SCHEDULER_add_delayed (h->sched, + GNUNET_NO, + GNUNET_SCHEDULER_PRIORITY_KEEP, + GNUNET_SCHEDULER_NO_PREREQUISITE_TASK, + GNUNET_TIME_absolute_get_remaining + (pos->timeout), + &transmit_timeout, pos); + } + if (prev == NULL) + h->connect_wait_head = next; + else + prev->next = next; + break; + } + prev = pos; + pos = next; + } +} + + +/** + * Connect to the transport service. Note that the connection may + * complete (or fail) asynchronously. + * + + * @param sched scheduler to use + * @param cfg configuration to use + * @param cls closure for the callbacks + * @param rec receive function to call + * @param nc function to call on connect events + * @param dc function to call on disconnect events + */ +struct GNUNET_TRANSPORT_Handle * +GNUNET_TRANSPORT_connect (struct GNUNET_SCHEDULER_Handle *sched, + struct GNUNET_CONFIGURATION_Handle *cfg, + void *cls, + GNUNET_TRANSPORT_ReceiveCallback rec, + GNUNET_TRANSPORT_NotifyConnect nc, + GNUNET_TRANSPORT_NotifyDisconnect nd) +{ + struct GNUNET_TRANSPORT_Handle *ret; + + GNUNET_ARM_start_service ("peerinfo", + cfg, sched, START_SERVICE_TIMEOUT, NULL, NULL); + GNUNET_ARM_start_service ("transport", + cfg, sched, START_SERVICE_TIMEOUT, NULL, NULL); + ret = GNUNET_malloc (sizeof (struct GNUNET_TRANSPORT_Handle)); + ret->sched = sched; + ret->cfg = cfg; + ret->cls = cls; + ret->rec = rec; + ret->nc_cb = nc; + ret->nd_cb = nd; + ret->reconnect_delay = GNUNET_TIME_UNIT_ZERO; + schedule_reconnect (ret); + return ret; +} + + +/** + * These stop activities must be run in a fresh + * scheduler that is NOT in shutdown mode. + */ +static void +stop_task (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc) +{ + struct GNUNET_TRANSPORT_Handle *handle = cls; + GNUNET_ARM_stop_service ("transport", + handle->cfg, + tc->sched, STOP_SERVICE_TIMEOUT, NULL, NULL); + GNUNET_ARM_stop_service ("peerinfo", + handle->cfg, + tc->sched, STOP_SERVICE_TIMEOUT, NULL, NULL); +} + + +/** + * Disconnect from the transport service. + */ +void +GNUNET_TRANSPORT_disconnect (struct GNUNET_TRANSPORT_Handle *handle) +{ + struct GNUNET_TRANSPORT_TransmitHandle *th; + struct NeighbourList *n; + struct HelloWaitList *hwl; + struct GNUNET_CLIENT_Connection *client; + +#if DEBUG_TRANSPORT + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Transport disconnect called!\n"); +#endif + while (NULL != (th = handle->connect_ready_head)) + { + handle->connect_ready_head = th->next; + th->notify (th->notify_cls, 0, NULL); + GNUNET_free (th); + } + + while (NULL != (th = handle->connect_wait_head)) + { + handle->connect_wait_head = th->next; + if (th->notify_delay_task != GNUNET_SCHEDULER_NO_PREREQUISITE_TASK) + { + GNUNET_SCHEDULER_cancel (handle->sched, th->notify_delay_task); + th->notify_delay_task = GNUNET_SCHEDULER_NO_PREREQUISITE_TASK; + } + th->notify (th->notify_cls, 0, NULL); + GNUNET_free (th); + } + while (NULL != (n = handle->neighbours)) + { + handle->neighbours = n->next; + GNUNET_free (n); + } + while (NULL != (hwl = handle->hwl_head)) + { + handle->hwl_head = hwl->next; + GNUNET_SCHEDULER_cancel (handle->sched, hwl->task); + GNUNET_log (GNUNET_ERROR_TYPE_WARNING, + _ + ("Disconnect while trying to obtain HELLO from transport service.\n")); + if (hwl->rec != NULL) + hwl->rec (hwl->rec_cls, GNUNET_TIME_UNIT_ZERO, NULL, NULL); + GNUNET_free (hwl); + } + if (handle->reconnect_task != GNUNET_SCHEDULER_NO_PREREQUISITE_TASK) + { + GNUNET_SCHEDULER_cancel (handle->sched, handle->reconnect_task); + handle->reconnect_task = GNUNET_SCHEDULER_NO_PREREQUISITE_TASK; + } + GNUNET_free_non_null (handle->my_hello); + handle->my_hello = NULL; + GNUNET_SCHEDULER_run (&stop_task, handle); + if (NULL != (client = handle->client)) + { +#if DEBUG_TRANSPORT + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Disconnecting from transport service for good.\n"); +#endif + handle->client = NULL; + GNUNET_CLIENT_disconnect (client); + } + if (client == NULL) + GNUNET_free (handle); +} + + +/** + * We're ready to transmit the request that the transport service + * should connect to a new peer. In addition to sending the + * request, schedule the next phase for the transmission processing + * that caused the connect request in the first place. + */ +static size_t +request_connect (void *cls, size_t size, void *buf) +{ + struct GNUNET_TRANSPORT_TransmitHandle *th = cls; + struct TryConnectMessage *tcm; + struct GNUNET_TRANSPORT_Handle *h; + + h = th->handle; + if (buf == NULL) + { +#if DEBUG_TRANSPORT + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Failed to transmit connect request to service.\n"); +#endif + th->notify (th->notify_cls, 0, NULL); + GNUNET_free (th); + return 0; + } +#if DEBUG_TRANSPORT + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Transmitting `%s' message for `%4s'.\n", + "TRY_CONNECT", GNUNET_i2s (&th->target)); +#endif + GNUNET_assert (size >= sizeof (struct TryConnectMessage)); + tcm = buf; + tcm->header.size = htons (sizeof (struct TryConnectMessage)); + tcm->header.type = htons (GNUNET_MESSAGE_TYPE_TRANSPORT_TRY_CONNECT); + tcm->reserved = htonl (0); + memcpy (&tcm->peer, &th->target, sizeof (struct GNUNET_PeerIdentity)); + th->notify_delay_task + = GNUNET_SCHEDULER_add_delayed (h->sched, + GNUNET_NO, + GNUNET_SCHEDULER_PRIORITY_KEEP, + GNUNET_SCHEDULER_NO_PREREQUISITE_TASK, + GNUNET_TIME_absolute_get_remaining (th-> + timeout), + &transmit_timeout, th); + insert_transmit_handle (&h->connect_wait_head, th); + return sizeof (struct TryConnectMessage); +} + + +/** + * Schedule a request to connect to the given + * neighbour (and if successful, add the specified + * handle to the wait list). + */ +static void +try_connect (struct GNUNET_TRANSPORT_TransmitHandle *th) +{ + schedule_control_transmit (th->handle, + sizeof (struct TryConnectMessage), + GNUNET_NO, + GNUNET_TIME_absolute_get_remaining (th->timeout), + &request_connect, th); +} + + +/** + * Cancel a pending notify transmit task + * and also remove the given transmit handle + * from whatever list is on. + */ +static void +remove_from_any_list (struct GNUNET_TRANSPORT_TransmitHandle *th) +{ + struct GNUNET_TRANSPORT_Handle *h; + + h = th->handle; + if (th->notify_delay_task != GNUNET_SCHEDULER_NO_PREREQUISITE_TASK) + { + GNUNET_SCHEDULER_cancel (h->sched, th->notify_delay_task); + th->notify_delay_task = GNUNET_SCHEDULER_NO_PREREQUISITE_TASK; + } + if (th->prev == NULL) + { + if (th == h->connect_wait_head) + h->connect_wait_head = th->next; + else + h->connect_ready_head = th->next; + } + else + th->prev->next = th->next; + if (th->next != NULL) + th->next->prev = th->prev; +} + + +/** + * Remove neighbour from our list + */ +static void +remove_neighbour (struct GNUNET_TRANSPORT_Handle *h, + const struct GNUNET_PeerIdentity *peer) +{ + struct NeighbourList *prev; + struct NeighbourList *pos; + struct GNUNET_TRANSPORT_TransmitHandle *th; + + prev = NULL; + pos = h->neighbours; + while ((pos != NULL) && + (0 != memcmp (peer, &pos->id, sizeof (struct GNUNET_PeerIdentity)))) + { + prev = pos; + pos = pos->next; + } + if (pos == NULL) + { + GNUNET_break (0); + return; + } + if (prev == NULL) + h->neighbours = pos->next; + else + prev->next = pos->next; + if (NULL != (th = pos->transmit_handle)) + { + pos->transmit_handle = NULL; + th->neighbour = NULL; + remove_from_any_list (th); + try_connect (th); + } + if (h->nc_cb != NULL) + h->nd_cb (h->cls, peer); + GNUNET_free (pos); +} + + +/** + * Type of a function to call when we receive a message + * from the service. + * + * @param cls closure + * @param msg message received, NULL on timeout or fatal error + */ +static void +demultiplexer (void *cls, const struct GNUNET_MessageHeader *msg) +{ + struct GNUNET_TRANSPORT_Handle *h = cls; + const struct DisconnectInfoMessage *dim; + const struct ConnectInfoMessage *cim; + const struct InboundMessage *im; + const struct GNUNET_MessageHeader *imm; + const struct SendOkMessage *okm; + struct HelloWaitList *hwl; + struct NeighbourList *n; + struct GNUNET_CRYPTO_RsaPublicKeyBinaryEncoded pkey; + struct GNUNET_PeerIdentity me; + uint16_t size; + + if ((msg == NULL) || (h->client == NULL)) + { + if (h->client != NULL) + { +#if DEBUG_TRANSPORT + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Error receiving from transport service, disconnecting temporarily.\n"); +#endif + if (h->network_handle != NULL) + { + GNUNET_NETWORK_notify_transmit_ready_cancel (h->network_handle); + h->network_handle = NULL; + h->transmission_scheduled = GNUNET_NO; + } + GNUNET_CLIENT_disconnect (h->client); + h->client = NULL; + schedule_reconnect (h); + } + else + { + /* shutdown initiated from 'GNUNET_TRANSPORT_disconnect', + finish clean up work! */ + GNUNET_free (h); + } + return; + } + GNUNET_CLIENT_receive (h->client, + &demultiplexer, h, GNUNET_TIME_UNIT_FOREVER_REL); + size = ntohs (msg->size); + switch (ntohs (msg->type)) + { + case GNUNET_MESSAGE_TYPE_HELLO: + if (GNUNET_OK != + GNUNET_HELLO_get_key ((const struct GNUNET_HELLO_Message *) msg, + &pkey)) + { + GNUNET_break (0); + break; + } + GNUNET_CRYPTO_hash (&pkey, + sizeof (struct + GNUNET_CRYPTO_RsaPublicKeyBinaryEncoded), + &me.hashPubKey); +#if DEBUG_TRANSPORT + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Receiving (my own) `%s' message, I am `%4s'.\n", + "HELLO", GNUNET_i2s (&me)); +#endif + GNUNET_free_non_null (h->my_hello); + h->my_hello = NULL; + if (size < sizeof (struct GNUNET_MessageHeader)) + { + GNUNET_break (0); + break; + } + h->my_hello = GNUNET_malloc (size); + memcpy (h->my_hello, msg, size); + while (NULL != (hwl = h->hwl_head)) + { + h->hwl_head = hwl->next; + GNUNET_SCHEDULER_cancel (h->sched, hwl->task); + GNUNET_TRANSPORT_get_hello (h, + GNUNET_TIME_UNIT_ZERO, + hwl->rec, hwl->rec_cls); + GNUNET_free (hwl); + } + break; + case GNUNET_MESSAGE_TYPE_TRANSPORT_CONNECT: + if (size != sizeof (struct ConnectInfoMessage)) + { + GNUNET_break (0); + break; + } + cim = (const struct ConnectInfoMessage *) msg; +#if DEBUG_TRANSPORT + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Receiving `%s' message for `%4s'.\n", + "CONNECT", GNUNET_i2s (&cim->id)); +#endif + add_neighbour (h, + ntohl (cim->quota_out), + GNUNET_TIME_relative_ntoh (cim->latency), &cim->id); + break; + case GNUNET_MESSAGE_TYPE_TRANSPORT_DISCONNECT: + if (size != sizeof (struct DisconnectInfoMessage)) + { + GNUNET_break (0); + break; + } + dim = (const struct DisconnectInfoMessage *) msg; +#if DEBUG_TRANSPORT + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Receiving `%s' message for `%4s'.\n", + "DISCONNECT", GNUNET_i2s (&dim->peer)); +#endif + remove_neighbour (h, &dim->peer); + break; + case GNUNET_MESSAGE_TYPE_TRANSPORT_SEND_OK: +#if DEBUG_TRANSPORT + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Receiving `%s' message.\n", "SEND_OK"); +#endif + if (size != sizeof (struct SendOkMessage)) + { + GNUNET_break (0); + break; + } + okm = (const struct SendOkMessage *) msg; + n = find_neighbour (h, &okm->peer); + GNUNET_assert (n != NULL); + n->transmit_ok = GNUNET_YES; + if (n->transmit_handle != NULL) + { +#if DEBUG_TRANSPORT + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Processing pending message\n"); +#endif + GNUNET_SCHEDULER_cancel (h->sched, + n->transmit_handle->notify_delay_task); + n->transmit_handle->notify_delay_task = + GNUNET_SCHEDULER_NO_PREREQUISITE_TASK; + GNUNET_assert (GNUNET_YES == n->received_ack); + schedule_request (n->transmit_handle); + } + break; + case GNUNET_MESSAGE_TYPE_TRANSPORT_RECV: +#if DEBUG_TRANSPORT + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Receiving `%s' message.\n", "RECV"); +#endif + if (size < + sizeof (struct InboundMessage) + + sizeof (struct GNUNET_MessageHeader)) + { + GNUNET_break (0); + break; + } + im = (const struct InboundMessage *) msg; + imm = (const struct GNUNET_MessageHeader *) &im[1]; + if (ntohs (imm->size) + sizeof (struct InboundMessage) != size) + { + GNUNET_break (0); + break; + } + switch (ntohs (imm->type)) + { + case GNUNET_MESSAGE_TYPE_TRANSPORT_ACK: +#if DEBUG_TRANSPORT + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Receiving `%s' message from `%4s'.\n", + "ACK", GNUNET_i2s (&im->peer)); +#endif + n = find_neighbour (h, &im->peer); + if (n == NULL) + { + GNUNET_break (0); + break; + } + if (n->received_ack == GNUNET_NO) + { + n->received_ack = GNUNET_YES; + if (NULL != n->transmit_handle) + { +#if DEBUG_TRANSPORT + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Peer connected, scheduling delayed message for deliverery now.\n"); +#endif + schedule_request (n->transmit_handle); + } + } + break; + default: +#if DEBUG_TRANSPORT + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Received message of type %u from `%4s'.\n", + ntohs (imm->type), GNUNET_i2s (&im->peer)); +#endif + if (h->rec != NULL) + h->rec (h->cls, + GNUNET_TIME_relative_ntoh (im->latency), &im->peer, imm); + break; + } + break; + default: + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + _ + ("Received unexpected message of type %u from `%4s' in %s:%u\n"), + ntohs (msg->type), GNUNET_i2s (&im->peer), __FILE__, + __LINE__); + GNUNET_break (0); + break; + } +} + + +struct ClientTransmitWrapper +{ + GNUNET_NETWORK_TransmitReadyNotify notify; + void *notify_cls; + struct GNUNET_TRANSPORT_TransmitHandle *th; +}; + + +/** + * Transmit message of a client destined for another + * peer to the service. + */ +static size_t +client_notify_wrapper (void *cls, size_t size, void *buf) +{ + struct ClientTransmitWrapper *ctw = cls; + struct OutboundMessage *obm; + struct GNUNET_MessageHeader *hdr; + size_t ret; + + if (size == 0) + { +#if DEBUG_TRANSPORT + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Transmission request could not be satisfied.\n"); +#endif + ret = ctw->notify (ctw->notify_cls, 0, NULL); + GNUNET_assert (ret == 0); + GNUNET_free (ctw); + return 0; + } + GNUNET_assert (size >= sizeof (struct OutboundMessage)); + obm = buf; + ret = ctw->notify (ctw->notify_cls, + size - sizeof (struct OutboundMessage), + (void *) &obm[1]); + if (ret == 0) + { + /* Need to reset flag, no SEND means no SEND_OK! */ + ctw->th->neighbour->transmit_ok = GNUNET_YES; + GNUNET_free (ctw); + return 0; + } + GNUNET_assert (ret >= sizeof (struct GNUNET_MessageHeader)); + hdr = (struct GNUNET_MessageHeader *) &obm[1]; + GNUNET_assert (ntohs (hdr->size) == ret); + GNUNET_assert (ret + sizeof (struct OutboundMessage) < + GNUNET_SERVER_MAX_MESSAGE_SIZE); +#if DEBUG_TRANSPORT + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Transmitting `%s' message with data for `%4s'\n", + "SEND", GNUNET_i2s (&ctw->th->target)); +#endif + ret += sizeof (struct OutboundMessage); + obm->header.type = htons (GNUNET_MESSAGE_TYPE_TRANSPORT_SEND); + obm->header.size = htons (ret); + obm->reserved = htonl (0); + obm->peer = ctw->th->target; + GNUNET_free (ctw); + return ret; +} + + + +/** + * Check if we could queue a message of the given size for + * transmission. The transport service will take both its + * internal buffers and bandwidth limits imposed by the + * other peer into consideration when answering this query. + * + * @param handle connection to transport service + * @param target who should receive the message + * @param size how big is the message we want to transmit? + * @param timeout after how long should we give up (and call + * notify with buf NULL and size 0)? + * @param notify function to call when we are ready to + * send such a message + * @param notify_cls closure for notify + * @return NULL if someone else is already waiting to be notified + * non-NULL if the notify callback was queued (can be used to cancel + * using GNUNET_TRANSPORT_notify_transmit_ready_cancel) + */ +struct GNUNET_TRANSPORT_TransmitHandle * +GNUNET_TRANSPORT_notify_transmit_ready (struct GNUNET_TRANSPORT_Handle + *handle, + const struct GNUNET_PeerIdentity + *target, size_t size, + struct GNUNET_TIME_Relative timeout, + GNUNET_NETWORK_TransmitReadyNotify + notify, void *notify_cls) +{ + struct GNUNET_TRANSPORT_TransmitHandle *pos; + struct GNUNET_TRANSPORT_TransmitHandle *th; + struct NeighbourList *n; + struct ClientTransmitWrapper *ctw; + + if (size + sizeof (struct OutboundMessage) >= + GNUNET_SERVER_MAX_MESSAGE_SIZE) + return NULL; +#if DEBUG_TRANSPORT + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Asking transport service for transmission of %u bytes to peer `%4s'.\n", + size, GNUNET_i2s (target)); +#endif + n = find_neighbour (handle, target); + ctw = GNUNET_malloc (sizeof (struct ClientTransmitWrapper)); + th = GNUNET_malloc (sizeof (struct GNUNET_TRANSPORT_TransmitHandle)); + ctw->notify = notify; + ctw->notify_cls = notify_cls; + ctw->th = th; + th->handle = handle; + th->target = *target; + th->notify = &client_notify_wrapper; + th->notify_cls = ctw; + th->notify_size = size + sizeof (struct OutboundMessage); + th->timeout = GNUNET_TIME_relative_to_absolute (timeout); + th->neighbour = n; + if (NULL == n) + { +#if DEBUG_TRANSPORT + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Transmission request could not be satisfied (not yet connected), adding it to pending request list.\n"); +#endif + pos = handle->connect_wait_head; + while (pos != NULL) + { + GNUNET_assert (0 != memcmp (target, + &pos->target, + sizeof (struct GNUNET_PeerIdentity))); + pos = pos->next; + } +#if DEBUG_TRANSPORT + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Will now try to connect to `%4s'.\n", GNUNET_i2s (target)); +#endif + try_connect (th); + } + else + { +#if DEBUG_TRANSPORT + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Transmission request queued for transmission to transport service.\n"); +#endif + GNUNET_assert (NULL == n->transmit_handle); + n->transmit_handle = th; + if (GNUNET_YES == n->received_ack) + { +#if DEBUG_TRANSPORT + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Peer `%4s' is connected, scheduling for delivery now.\n", + GNUNET_i2s (target)); +#endif + schedule_request (th); + } + else + { +#if DEBUG_TRANSPORT + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Connection to `%4s' is not yet confirmed connected, scheduling timeout (%llums) only.\n", + GNUNET_i2s (target), timeout.value); +#endif + th->notify_delay_task + = GNUNET_SCHEDULER_add_delayed (handle->sched, + GNUNET_NO, + GNUNET_SCHEDULER_PRIORITY_KEEP, + GNUNET_SCHEDULER_NO_PREREQUISITE_TASK, + timeout, &transmit_timeout, th); + } + } + return th; +} + + +/** + * Cancel the specified transmission-ready + * notification. + */ +void +GNUNET_TRANSPORT_notify_transmit_ready_cancel (struct + GNUNET_TRANSPORT_TransmitHandle + *th) +{ + struct GNUNET_TRANSPORT_Handle *h; + + GNUNET_assert (th->notify == &client_notify_wrapper); + remove_from_any_list (th); + h = th->handle; + if ((h->connect_ready_head == NULL) && (h->network_handle != NULL)) + { + GNUNET_NETWORK_notify_transmit_ready_cancel (h->network_handle); + h->network_handle = NULL; + h->transmission_scheduled = GNUNET_NO; + } + GNUNET_free (th->notify_cls); + GNUNET_free (th); +} + + +/* end of transport_api.c */ diff --git a/src/upnp/Makefile.am b/src/upnp/Makefile.am new file mode 100644 index 000000000..02a2b139d --- /dev/null +++ b/src/upnp/Makefile.am @@ -0,0 +1,59 @@ +INCLUDES = -I$(top_srcdir)/src/include + +if MINGW + WINFLAGS = -Wl,--no-undefined -Wl,--export-all-symbols +endif + +if USE_COVERAGE + AM_CFLAGS = -fprofile-arcs -ftest-coverage +endif + +lib_LTLIBRARIES = libgnunetupnp.la + +libgnunetupnp_la_SOURCES = \ + upnp.c upnp.h \ + upnp_ip.c upnp_ip.h \ + upnp_util.c upnp_util.h \ + upnp_xmlnode.c upnp_xmlnode.h +libgnunetupnp_la_CFLAGS = \ + -I$(top_scrdir)/include \ + @LIBCURL_CPPFLAGS@ @XML_CPPFLAGS@ +libgnunetupnp_la_LIBADD = \ + $(top_builddir)/src/util/libgnunetutil.la \ + $(GN_LIBINTL) @EXT_LIBS@ @XML_LIBS@ @LIBCURL@ +libgnunetupnp_la_LDFLAGS = \ + $(GN_LIB_LDFLAGS) $(WINFLAGS) \ + -version-info 0:0:0 + + +bin_PROGRAMS = \ + gnunet-upnp \ + gnunet-service-upnp + +gnunet_upnp_SOURCES = \ + gnunet-upnp.c +gnunet_upnp_LDADD = \ + $(top_builddir)/src/upnp/libgnunetupnp.la \ + $(top_builddir)/src/util/libgnunetutil.la \ + $(GN_LIBINTL) + +gnunet_service_upnp_SOURCES = \ + gnunet-service-upnp.c +gnunet_service_upnp_LDADD = \ + $(top_builddir)/src/util/libgnunetutil.la \ + $(GN_LIBINTL) + + +check_PROGRAMS = \ + test_upnp + +TESTS = $(check_PROGRAMS) + +test_upnp_SOURCES = \ + test_upnp.c +test_upnp_LDADD = \ + $(top_builddir)/src/transport/libgnunetupnp.la \ + $(top_builddir)/src/util/libgnunetutil.la + + +# EXTRA_DIST = test_upnp_data.conf diff --git a/src/upnp/draft-cheshire-nat-pmp.txt b/src/upnp/draft-cheshire-nat-pmp.txt new file mode 100644 index 000000000..727b5fad7 --- /dev/null +++ b/src/upnp/draft-cheshire-nat-pmp.txt @@ -0,0 +1,1160 @@ +Document: draft-cheshire-nat-pmp-02.txt Stuart Cheshire +Internet-Draft Marc Krochmal +Category: Standards Track Apple Computer, Inc. +Expires 14th March 2007 Kiren Sekar + Sharpcast, Inc. + 14th September 2006 + + NAT Port Mapping Protocol (NAT-PMP) + + + +Status of this Memo + + By submitting this Internet-Draft, each author represents that any + applicable patent or other IPR claims of which he or she is aware + have been or will be disclosed, and any of which he or she becomes + aware will be disclosed, in accordance with Section 6 of BCP 79. + For the purposes of this document, the term "BCP 79" refers + exclusively to RFC 3979, "Intellectual Property Rights in IETF + Technology", published March 2005. + + Internet-Drafts are working documents of the Internet Engineering + Task Force (IETF), its areas, and its working groups. Note that + other groups may also distribute working documents as Internet- + Drafts. + + Internet-Drafts are draft documents valid for a maximum of six months + and may be updated, replaced, or obsoleted by other documents at any + time. It is inappropriate to use Internet-Drafts as reference + material or to cite them other than as "work in progress." + + The list of current Internet-Drafts can be accessed at + http://www.ietf.org/1id-abstracts.html + + The list of Internet-Draft Shadow Directories can be accessed at + http://www.ietf.org/shadow.html + + +Abstract + + This document describes a protocol for automating the process of + creating Network Address Translation (NAT) port mappings. Included + in the protocol is a method for retrieving the public IP address of + a NAT gateway, thus allowing a client to make this public IP address + and port number known to peers that may wish to communicate with it. + This protocol is implemented in current Apple products including + Mac OS X, Bonjour for Windows, and AirPort wireless base stations. + + + + + + + + + + +Expires 14th March 2007 Cheshire, et al. [Page 1] + +Internet Draft NAT Port Mapping Protocol 14th September 2006 + + +1. Introduction + + Network Address Translation (NAT) is a method of sharing one public + internet address with a number of devices. This document is focused + on what "IP Network Address Translator (NAT) Terminology and + Considerations" [RFC 2663] calls "NAPTs" (Network Address/Port + Translators). A full description of NAT is beyond the scope of this + document. The following brief overview will cover the aspects + relevant to this port mapping protocol. For more information on + NAT, see "Traditional IP Network Address Translator" [RFC 3022]. + + NATs have one or more public IP addresses. A private network is set + up behind the NAT. Devices behind the NAT are assigned private + addresses and the private address of the NAT device is used as the + gateway. + + When a packet from any device behind the NAT is sent to an address on + the public internet, the packet first passes through the NAT box. The + NAT box looks at the source port and address. In some cases, a NAT + will also keep track of the destination port and address. The NAT + then creates a mapping from the private address and private port to a + public address and public port if a mapping does not already exist. + The NAT box replaces the private address and port number in the + packet with the public entries from the mapping and sends the packet + on to the next gateway. + + When a packet from any address on the internet is received on the + NAT's public side, the NAT will look up the destination port (public + port) in the list of mappings. If an entry is found, it will contain + the private address and port that the packet should be sent to. The + NAT gateway will then rewrite the destination address and port with + those from the mapping. The packet will then be forwarded to the new + destination addresses. If the packet did not match any mapping, the + packet will most likely be dropped. Various NATs implement different + strategies to handle this. The important thing to note is that if + there is no mapping, the NAT does not know which private address the + packet should be sent to. + + Mappings are usually created automatically as a result of observing + outbound traffic. There are a few exceptions. Some NATs may allow + manually-created permanent mappings that map a public port to a + specific private IP address and port. Such a mapping allows incoming + connections to the device with that private address. Some NATs also + implement a default mapping where any inbound traffic that does not + match a mapping will always be forwarded to a specific private + address. Both types of mappings are usually set up manually through + some configuration tool. + + Without these manually-created inbound port mappings, clients behind + the NAT would be unable to receive inbound connections, which + represents a loss of connectivity when compared to the original + + +Expires 14th March 2007 Cheshire, et al. [Page 2] + +Internet Draft NAT Port Mapping Protocol 14th September 2006 + + + Internet architecture [ETEAISD]. For those who view this loss of + connectivity as a bad thing, NAT-PMP allows clients to operate much + more like a host directly connected to the unrestricted public + Internet, with an unrestricted public IP address. NAT-PMP allows + client hosts to communicate with the NAT gateway to request the + creation of inbound mappings on demand. Having created a NAT mapping + to allow inbound connections, the client can then record its public + IP address and public port number in a public registry (e.g. the + world-wide Domain Name System) or otherwise make it accessible to + peers that wish to communicate with it. + + +2. Conventions and Terminology Used in this Document + + The key words "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT", + "SHOULD", "SHOULD NOT", "RECOMMENDED", "MAY", and "OPTIONAL" in this + document are to be interpreted as described in "Key words for use in + RFCs to Indicate Requirement Levels" [RFC 2119]. + + +3. Protocol and Packet Format + + NAT Port Mapping Protocol runs over UDP. Every packet starts with an + 8 bit version followed by an 8 bit operation code. + + This document specifies version 0 of the protocol. Any NAT-PMP + gateway implementing this version of the protocol, receiving a + packet with a version number other than 0, MUST return result code 1 + (Unsupported Version). + + Opcodes between 0 and 127 are client requests. Opcodes from 128 to + 255 are server responses. Responses always contain a 16 bit result + code in network byte order. A result code of zero indicates success. + Responses also contain a 32 bit unsigned integer corresponding to the + number of seconds since the NAT gateway was rebooted or since its + port mapping state was reset. + + This protocol SHOULD only be used when the client determines that + its primary IPv4 address is in one of the private IP address ranges + defined in "Address Allocation for Private Internets" [RFC 1918]. + This includes the address ranges 10/8, 172.16/12, and 192.168/16. + + Clients always send their Port Mapping Protocol requests to their + default gateway, as learned via DHCP [RFC 2131], or similar means. + This protocol is designed for small home networks, with a single + logical link (subnet) where the client's default gateway is also the + NAT translator for that network. For more complicated networks where + the NAT translator is some device other than the client's default + gateway, this protocol is not appropriate. + + + + +Expires 14th March 2007 Cheshire, et al. [Page 3] + +Internet Draft NAT Port Mapping Protocol 14th September 2006 + + +3.1 Requests and Responses + + NAT gateways are often low-cost devices, with limited memory and + CPU speed. For this reason, to avoid making excessive demands on + the NAT gateway, clients machines SHOULD NOT issue multiple requests + simultaneously in parallel. If a client needs to perform multiple + requests (e.g. on boot, wake from sleep, network connection, etc.) + it SHOULD queue them and issue them serially one at a time. Once the + NAT gateway responds to one request the client machine may issue the + next. In the case of a fast NAT gateway, the client may be able to + complete requests at a rate of hundreds per second. In the case of + a slow NAT gateway that takes perhaps half a second to respond to + a NAT-PMP request, the client SHOULD respect this and allow the + NAT gateway to operate at the pace it can manage, and not overload + it by issuing requests faster than the rate it's answering them. + + To determine the puclic IP address or request a port mapping, + a NAT-PMP client sends its request packet to port 5351 of its + configured gateway address, and waits 250ms for a response. If no + NAT-PMP response is received from the gateway after 250ms, the client + retransmits its request and waits 500ms. The client SHOULD repeat + this process with the interval between attempts doubling each time. + If, after sending its 9th attempt (and then waiting for 64 seconds), + the client has still received no response, then it SHOULD conclude + that this gateway does not support NAT Port Mapping Protocol and + MAY log an error message indicating this fact. In addition, if the + NAT-PMP client receives an "ICMP Port Unreachable" message from the + gateway for port 5351 then it can skip any remaining retransmissions + and conclude immediately that the gateway does not support NAT-PMP. + + As a performance optimization the client MAY record this information + and use it to suppress further attempts to use NAT-PMP, but the + client should not retain this information for too long. In + particular, any event that may indicate a potential change of gateway + or a change in gateway configuration (hardware link change + indication, change of gateway MAC address, acquisition of new DHCP + lease, receipt of NAT-PMP announcement packet from gateway, etc.) + should cause the client to discard its previous information regarding + the gateway's lack of NAT-PMP support, and send its next NAT-PMP + request packet normally. + + +3.2 Determining the Public Address + + To determine the public address, the client behind the NAT sends the + following UDP payload to port 5351 of the configured gateway address: + + 0 1 + 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | Vers = 0 | OP = 0 | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + +Expires 14th March 2007 Cheshire, et al. [Page 4] + +Internet Draft NAT Port Mapping Protocol 14th September 2006 + + + A compatible NAT gateway MUST generate a response with the following + format: + + 0 1 2 3 + 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | Vers = 0 | OP = 128 + 0 | Result Code | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | Seconds Since Start of Epoch | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | Public IP Address (a.b.c.d) | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + + This response indicates that the NAT gateway implements this version + of the protocol and returns the public IP address of the NAT gateway. + If the result code is non-zero, the value of Public IP Address is + undefined (MUST be set to zero on transmission, and MUST be ignored + on reception). + + The NAT gateway MUST fill in the "Seconds Since Start of Epoch" field + with the time elapsed since its port mapping table was initialized on + startup or reset for any other reason (see Section 3.6 "Seconds Since + Start of Epoch"). + + Upon receiving the response packet, the client MUST check the source + IP address, and silently discard the packet if the address is not the + address of the gateway to which the request was sent. + + +3.2.1 Announcing Address Changes + + When the public IP address of the NAT changes, the NAT gateway MUST + send a gratuitous response to the link-local multicast address + 224.0.0.1, port 5351 with the packet format above to notify clients + of the new public IP address. To accommodate packet loss, the + NAT gateway SHOULD multicast 10 address change notifications. + The interval between the first two notifications SHOULD be 250ms, + and the interval between each subsequent notification SHOULD double. + + Upon receiving a gratuitous address change announcement packet, + the client MUST check the source IP address, and silently discard + the packet if the address is not the address of the client's + current configured gateway. This is to guard against inadvertent + misconfigurations where there may be more than one NAT gateway + active on the network. + + + + + + + + +Expires 14th March 2007 Cheshire, et al. [Page 5] + +Internet Draft NAT Port Mapping Protocol 14th September 2006 + + +3.3 Creating a Mapping + + To create a mapping, the client sends a UDP packet to port 5351 + of the gateway's private IP address with the following format: + + 0 1 2 3 + 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | Vers = 0 | OP = x | Reserved (MUST be zero) | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | Private Port | Requested Public Port | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | Requested Port Mapping Lifetime in Seconds | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + + Opcodes supported: + 1 - Map UDP + 2 - Map TCP + + The Reserved field MUST be set to zero on transmission and MUST + be ignored on reception. + + The Private Port is set to the local port on which the client is + listening. + + The Requested Public Port SHOULD usually be set to the same value as + the local Private Port, or zero if the client has no preference for + what port is assigned. However, the gateway is not obliged to assign + the port requested, and may choose not to, either for policy reasons + (e.g. port 80 is reserved and clients may not request it) or because + that port has already been assigned to some other client. Because + of this, some product developers have questioned the value of having + the Requested Public Port field at all. The reason is for failure + recovery. Most low-cost home NAT gateways do not record temporary + port mappings in persistent storage, so if the gateway crashes or is + rebooted, all the mappings are lost. A renewal packet is formatted + identically to an initial mapping request packet, except that for + renewals the client sets the Requested Public Port field to the + port the gateway actually assigned, rather than the port the client + originally wanted. When a freshly-rebooted NAT gateway receives a + renewal packet from a client, it appears to the gateway just like + an ordinary initial request for a port mapping, except that in this + case the Requested Public Port is likely to be one that the NAT + gateway *is* willing to allocate (it allocated it to this client + right before the reboot, so it should presumably be willing to + allocate it again). + + The RECOMMENDED Port Mapping Lifetime is 3600 seconds. + + + + + +Expires 14th March 2007 Cheshire, et al. [Page 6] + +Internet Draft NAT Port Mapping Protocol 14th September 2006 + + + After sending the port mapping request, the client then waits for the + NAT gateway to respond. If after 250ms, the gateway doesn't respond, + the client SHOULD re-issue its request as described above in Section + 3.1 "Requests and Responses". + + The NAT gateway responds with the following packet format: + + 0 1 2 3 + 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | Vers = 0 | OP = 128 + x | Result Code | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | Seconds Since Start of Epoch | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | Private Port | Mapped Public Port | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | Port Mapping Lifetime in Seconds | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + + The 'x' in the OP field MUST match what the client requested. Some + NAT gateways are incapable of creating a UDP port mapping without + also creating a corresponding TCP port mapping, and vice versa, and + these gateways MUST NOT implement NAT Port Mapping Protocol until + this deficiency is fixed. A NAT gateway which implements this + protocol MUST be able to create TCP-only and UDP-only port mappings. + + If a NAT gateway silently creates a pair of mappings for a client + that only requested one mapping, then it may expose that client to + receiving inbound UDP packets or inbound TCP connection requests + that it did not ask for and does not want. + + While a NAT gateway MUST NOT automatically create mappings for TCP + when the client requests UDP, and vice versa, the NAT gateway MUST + reserve the companion port so the same client can choose to map it + in the future. For example, if a client requests to map TCP port 80, + as long as the client maintains the lease for that TCP port mapping, + another client with a different IP address MUST NOT be able to + successfully acquire the mapping for UDP port 80. + + The client normally requests the public port matching the private + port. If that public port is not available, the NAT gateway MUST + return a public port that is available or return an error code if + no ports are available. + + The source address of the packet MUST be used for the private address + in the mapping. This protocol is not intended to facilitate one + device behind a NAT creating mappings for other devices. If there + are legacy devices that require inbound mappings, permanent mappings + can be created manually by the administrator, just as they are today. + + + + +Expires 14th March 2007 Cheshire, et al. [Page 7] + +Internet Draft NAT Port Mapping Protocol 14th September 2006 + + + If a mapping already exists for a given private port on a given local + client (whether that mapping was created explicitly using NAT-PMP, + implicitly as a result of an outgoing TCP SYN packet, or manually by + a human administrator) and that client requests another mapping for + the same private port (possibly requesting a different public port) + then the mapping request should succeed, returning the already- + assigned public port. This is necessary to handle the case where + a client requests a mapping with requested public port X, and is + granted a mapping with actual public port Y, but the acknowledgement + packet gets lost. When the client retransmits its mapping request, + it should get back the same positive acknowledgement as was sent (and + lost) the first time. + + The NAT gateway SHOULD NOT accept mapping requests destined to the + NAT gateway's public IP address or received on its public network + interface. Only packets received on the private interface(s) with + a destination address matching the private address(es) of the NAT + gateway should be allowed. + + The NAT gateway MUST fill in the "Seconds Since Start of Epoch" field + with the time elapsed since its port mapping table was initialized on + startup or reset for any other reason (see Section 3.6 "Seconds Since + Start of Epoch"). + + The Port Mapping Lifetime is an unsigned integer in seconds. The NAT + gateway MAY reduce the lifetime from what the client requested. The + NAT gateway SHOULD NOT offer a lease lifetime greater than that + requested by the client. + + Upon receiving the response packet, the client MUST check the source + IP address, and silently discard the packet if the address is not the + address of the gateway to which the request was sent. + + The client SHOULD begin trying to renew the mapping halfway to expiry + time, like DHCP. The renewal packet should look exactly the same as + a request packet, except that the client SHOULD set the requested + public port to what the NAT gateway previously mapped, not what the + client originally requested. As described above, this enables the + gateway to automatically recover its mapping state after a crash or + reboot. + + +3.4 Destroying a Mapping + + A mapping may be destroyed in a variety of ways. If a client fails + to renew a mapping, then when its lifetime expires the mapping MUST + be automatically deleted. In the common case where the gateway + device is a combined DHCP server and NAT gateway, when a client's + DHCP address lease expires, the gateway device MAY automatically + delete any mappings belonging to that client. Otherwise a new client + being assigned the same IP address could receive unexpected inbound + + +Expires 14th March 2007 Cheshire, et al. [Page 8] + +Internet Draft NAT Port Mapping Protocol 14th September 2006 + + + UDP packets or inbound TCP connection requests that it did not ask + for and does not want. + + A client MAY also send an explicit packet to request deletion of a + mapping that is no longer needed. A client requests explicit + deletion of a mapping by sending a message to the NAT gateway + requesting the mapping, with the Requested Lifetime in Seconds set + to 0. The requested public port MUST be set to zero by the client + on sending, and MUST be ignored by the gateway on reception. + + When a mapping is destroyed successfully as a result of the client + explicitly requesting the deletion, the NAT gateway MUST send a + response packet which is formatted as defined in section 3.3 + "Creating a Mapping". The response MUST contain a result code of 0, + the private port as indicated in the deletion request, a public port + of 0, and a lifetime of 0. The NAT gateway MUST respond to a request + to destroy a mapping that does not exist as if the request were + successful. This is because of the case where the acknowledgement is + lost, and the client retransmits its request to delete the mapping. + In this case the second request to delete the mapping MUST return the + same response packet as the first request. + + If the deletion request was unsuccessful, the response MUST contain a + non-zero result code and the requested mapping; the lifetime is + undefined (MUST be set to zero on transmission, and MUST be ignored + on reception). If the client attempts to delete a port mapping which + was manually assigned by some kind of configuration tool, the NAT + gateway MUST respond with a 'Not Authorized' error, result code 2. + + When a mapping is destroyed as a result of its lifetime expiring or + for any other reason, if the NAT gateway's internal state indicates + that there are still active TCP connections traversing that now- + defunct mapping, then the NAT gateway SHOULD send appropriately- + constructed TCP RST (reset) packets both to the local client and to + the remote peer on the Internet to terminate that TCP connection. + + A client can request the explicit deletion of all its UDP or TCP + mappings by sending the same deletion request to the NAT gateway + with public port, private port, and lifetime set to 0. A client MAY + choose to do this when it first acquires a new IP address in order to + protect itself from port mappings that were performed by a previous + owner of the IP address. After receiving such a deletion request, + the gateway MUST delete all its UDP or TCP port mappings (depending + on the opcode). The gateway responds to such a deletion request with + a response as described above, with the private port set to zero. If + the gateway is unable to delete a port mapping, for example, because + the mapping was manually configured by the administrator, the gateway + MUST still delete as many port mappings as possible, but respond with + a non-zero result code. The exact result code to return depends on + the cause of the failure. If the gateway is able to successfully + delete all port mappings as requested, it MUST respond with a result + code of 0. + +Expires 14th March 2007 Cheshire, et al. [Page 9] + +Internet Draft NAT Port Mapping Protocol 14th September 2006 + + +3.5 Result Codes + + Currently defined result codes: + 0 - Success + 1 - Unsupported Version + 2 - Not Authorized/Refused + (e.g. box supports mapping, but user has turned feature off) + 3 - Network Failure + (e.g. NAT box itself has not obtained a DHCP lease) + 4 - Out of resources + (NAT box cannot create any more mappings at this time) + 5 - Unsupported opcode + + If the result code is non-zero, the format of the packet following + the result code may be truncated. For example, if the client sends a + request to the server with an opcode of 17 and the server does not + recognize that opcode, the server SHOULD respond with a message where + the opcode is 17 + 128 and the result code is 5 (opcode not + supported). Since the server does not understand the format of + opcode 17, it may not know what to place after the result code. In + some cases, relevant data may follow the opcode to identify the + operation that failed. For example, a client may request a mapping + but that mapping may fail due to resource exhaustion. The server + SHOULD respond with the result code to indicate resource exhaustion + (4) followed by the requested port mapping so the client may identify + which operation failed. + + Clients MUST be able to properly handle result codes not defined in + this document. Undefined results codes MUST be treated as fatal + errors of the request. + + +3.6 Seconds Since Start of Epoch + + Every packet sent by the NAT gateway includes a "Seconds since start + of epoch" field (SSSOE). If the NAT gateway resets or loses the + state of its port mapping table, due to reboot, power failure, or any + other reason, it MUST reset its epoch time and begin counting SSSOE + from 0 again. Whenever a client receives any packet from the NAT + gateway, either gratuitously or in response to a client request, the + client computes its own conservative estimate of the expected SSSOE + value by taking the SSSOE value in the last packet it received from + the gateway and adding 7/8 (87.5%) of the time elapsed since that + packet was received. If the SSSOE in the newly received packet is + less than the client's conservative estimate by more than one second, + then the client concludes that the NAT gateway has undergone a reboot + or other loss of port mapping state, and the client MUST immediately + renew all its active port mapping leases as described in Section 3.7 + "Recreating Mappings On NAT Gateway Reboot". + + + + +Expires 14th March 2007 Cheshire, et al. [Page 10] + +Internet Draft NAT Port Mapping Protocol 14th September 2006 + + +3.7 Recreating Mappings On NAT Gateway Reboot + + The NAT gateway MAY store mappings in persistent storage so when it + is powered off or rebooted, it remembers the port mapping state of + the network. + + However, maintaining this state is not essential for correct + operation. When the NAT gateway powers on or clears its port mapping + state as the result of a configuration change, it MUST reset the + epoch time and re-announce its IP address as described in Section + 3.2.1 "Announcing Address Changes". Reception of this packet where + time has apparently gone backwards serves as a hint to clients + on the network that they SHOULD immediately send renewal packets + (to immediately recreate their mappings) instead of waiting until + the originally scheduled time for those renewals. Clients who miss + receiving those gateway announcement packets for any reason will + still renew their mappings at the originally scheduled time and cause + their mappings to be recreated; it will just take a little longer for + these clients. + + A mapping renewal packet is formatted identically to an original + mapping request; from the point of view of the client it is a + renewal of an existing mapping, but from the point of view of the + freshly-rebooted NAT gateway it appears as a new mapping request. + + This self-healing property of the protocol is very important. + + The remarkable reliability of the Internet as a whole derives + in large part from the fact that important state is held in the + endpoints, not in the network itself [ETEAISD]. Power-cycling an + Ethernet switch results only in a brief interruption in the flow + of packets; established TCP connections through that switch are not + broken, merely delayed for a few seconds. Indeed, an old Ethernet + switch can even be replaced with a new one, and as long as the cables + are transferred over reasonably quickly, after the upgrade all the + TCP connections that were previously going though the old switch will + be unbroken and now going through the new one. The same is true of + IP routers, wireless base stations, etc. The one exception is NAT + gateways. Because the port mapping state is required for the NAT + gateway to know where to forward inbound packets, loss of that state + breaks connectivity through the NAT gateway. By allowing clients to + detect when this loss of NAT gateway state has occurred, and recreate + it on demand, we turn hard state in the network into soft state, and + allow it to be recovered automatically when needed. + + Without this automatic recreation of soft state in the NAT gateway, + reliable long-term networking would not be achieved. As mentioned + above, the reliability of the Internet does not come from trying + to build a perfect network in which errors never happen, but from + accepting that in any sufficiently large system there will always be + some component somewhere that's failing, and designing mechanisms + + +Expires 14th March 2007 Cheshire, et al. [Page 11] + +Internet Draft NAT Port Mapping Protocol 14th September 2006 + + + that can handle those failures and recover. To illustrate this point + with an example, consider the following scenario: Imagine a network + security camera that has a web interface and accepts incoming + connections from web browser clients. Imagine this network security + camera uses NAT-PMP or a similar protocol to set up an inbound + port mapping in the NAT gateway so that it can receive incoming + connections from clients the other side of the NAT gateway. + Now, this camera may well operate for weeks, months, or even years. + During that time it's possible that the NAT gateway could experience + a power failure or be rebooted. The user could upgrade the NAT + gateway's firmware, or even replace the entire NAT gateway device + with a newer model. The general point is that if the camera operates + for a long enough period of time, some kind of disruption to the NAT + gateway becomes inevitable. The question is not whether the NAT + gateway will lose its port mappings, but when, and how often. + If the network camera and devices like it on the network can detect + when the NAT gateway has lost its port mappings, and recreate them + automatically, then these disruptions are self-correcting and + invisible to the end user. If, on the other hand, the disruptions are + not self-correcting, and after a NAT gateway reboot the user has to + manually reset or reboot all the other devices on the network too, + then these disruptions are *very* visible to the end user. This + aspect of the design is what makes the difference between a protocol + that keeps on working indefinitely over a time scale of months or + years, and a protocol that works in brief testing, but in the real + world is continually failing and requiring manual intervention to get + it going again. + + When a client renews its port mappings as the result of receiving + a packet where the "Seconds since start of epoch" field (SSSOE) + indicates that a reboot or similar loss of state has occurred, + the client MUST first delay by a random amount of time selected + with uniform random distribution in the range 0 to 5 seconds, and + then send its first port mapping request. After that request is + acknowledged by the gateway, the client may then send its second + request, and so on, as rapidly as the gateway allows. The requests + SHOULD be issued serially, one at a time; the client SHOULD NOT issue + multiple requests simultaneously in parallel. + + The discussion in this section focusses on recreating inbound port + mappings after loss of NAT gateway state, because that is the more + serious problem. Losing port mappings for outgoing connections + destroys those currently active connections, but does not prevent + clients from establishing new outgoing connections. In contrast, + losing inbound port mappings not only destroys all existing inbound + connections, but also prevents the reception of any new inbound + connections until the port mapping is recreated. Accordingly, + we consider recovery of inbound port mappings the more important + priority. However, clients that want outgoing connections to survive + a NAT gateway reboot can also achieve that using NAT-PMP. After + initiating an outbound TCP connection (which will cause the NAT + + +Expires 14th March 2007 Cheshire, et al. [Page 12] + +Internet Draft NAT Port Mapping Protocol 14th September 2006 + + + gateway to establish an implicit port mapping) the client should send + the NAT gateway a port mapping request for the source port of its TCP + connection, which will cause the NAT gateway to send a response + giving the public port it allocated for that mapping. The client can + then store this information, and use later to recreate the mapping + if it determines that the NAT gateway has lost its mapping state. + + +3.8 NAT Gateways with NAT Function Disabled + + Note that *only* devices currently acting in the role of NAT gateway + should participate in NAT-PMP protocol exchanges with clients. + A network device that is capable of NAT (and NAT-PMP), but is + currently configured not to perform that function, (e.g. it is + acting as a traditional IP router, forwarding packets without + modifying them), MUST NOT respond to NAT-PMP requests from clients, + or send spontaneous NAT-PMP address-change announcements. + + In particular, a network device not currently acting in the role of + NAT gateway should not even respond to NAT-PMP requests by returning + an error code such as "2 - Not Authorized/Refused", since to do so + is misleading to clients -- it suggests that NAT port mapping is + necessary on this network for the client to successfully receive + inbound connections, but is not available because the administrator + has chosen to disable that functionality. + + Clients should also be careful to avoid making unfounded assumptions, + such as the assumption that if the client has an IPv4 address in + one of the RFC 1918 private IP address ranges then that means + NAT necessarily must be in use. Net 10/8 has enough addresses + to build a private network with millions of hosts and thousands + of interconnected subnets, all without any use of NAT. Many + organizations have built such private networks that benefit from + using standard TCP/IP technology, but by choice do not connect + to the public Internet. The purpose of NAT-PMP is to mitigate some + of the damage caused by NAT. It would be an ironic and unwanted + side-effect of this protocol if it were to lead well-meaning but + misguided developers to create products that refuse to work on a + private network *unless* they can find a NAT gateway to talk to. + Consequently, a client finding that NAT-PMP is not available on its + network should not give up, but should proceed on the assumption + that the network may be a traditional routed IP network, with no + address translation being used. This assumption may not always be + true, but it is better than the alternative of falsely assuming + the worst and not even trying to use normal (non-NAT) IP networking. + + If a network device not currently acting in the role of NAT gateway + receives UDP packets addressed to port 5351, it SHOULD respond + immediately with an "ICMP Port Unreachable" message to tell the + client that it needn't continue with timeouts and retransmissions, + and it should assume that NAT-PMP is not available and not needed + on this network. + +Expires 14th March 2007 Cheshire, et al. [Page 13] + +Internet Draft NAT Port Mapping Protocol 14th September 2006 + + +4. UNSAF Considerations + + The document "IAB Considerations for UNSAF Across NAT" [RFC 3424] + covers a number of issues when working with NATs. RFC 3424 outlines + some requirements for any document that attempts to work around + problems associated with NATs. This section addresses those + requirements. + + +4.1 Scope + + This protocol addresses the needs of TCP and UDP transport peers that + are separated from the public internet by exactly one NAT. Such + peers must have access to some form of directory server for + registering the public IP address and port at which they can be + reached. + + +4.2 Transition Plan + + Any client making use of this protocol SHOULD implement IPv6 support. + If a client supports IPv6 and is running on a device with a global + IPv6 address, that IPv6 address SHOULD be preferred to the IPv4 + public address using this NAT mapping protocol. In case other + clients do not have IPv6 connectivity, both the IPv4 and IPv6 + addresses SHOULD be registered with whatever form of directory server + is used. Preference SHOULD be given to IPv6 addresses when + available. By implementing support for IPv6 and using this protocol + for IPv4, vendors can ship products today that will work under both + scenarios. As IPv6 is more widely deployed, clients of this protocol + following these recommendations will transparently make use of IPv6. + + +4.3 Failure Cases + + Aside from NATs that do not implement this protocol, there are a + number of situations where this protocol may not work. + + +4.3.1 NAT Behind NAT + + Some people's primary IP address, assigned by their ISP, may itself + be a NAT address. In addition, some people may have a public IP + address, but may then double NAT themselves, perhaps by choice or + perhaps by accident. Although it might be possible in principle for + one NAT gateway to recursively request a mapping from the next one, + this document does not advocate that and does not try to prescribe + how it would be done. + + It would be a lot of work to implement nested NAT port mapping + correctly, and there are a number of reasons why the end result might + + +Expires 14th March 2007 Cheshire, et al. [Page 14] + +Internet Draft NAT Port Mapping Protocol 14th September 2006 + + + not be as useful as we might hope. Consider the case of an ISP that + offers each of its customers only a single NAT address. This ISP + could instead have chosen to provide each customer with a single + public IP address, or, if the ISP insists on running NAT, it could + have chosen to allow each customer a reasonable number of addresses, + enough for each customer device to have its own NAT address directly + from the ISP. If instead this ISP chooses to allow each customer + just one and only one NAT address, forcing said customer to run + nested NAT in order to use more than one device, it seems unlikely + that such an ISP would be so obliging as to provide a NAT service + that supports NAT Port Mapping Protocol. Supposing that such an ISP + did wish to offer its customers NAT service with NAT-PMP so as to + give them the ability to receive inbound connections, this ISP could + easily choose to allow each client to request a reasonable number of + DHCP addresses from that gateway. Remember that Net 10/8 [RFC 1918] + allows for over 16 million addresses, so NAT addresses are not in any + way in short supply. A single NAT gateway with 16 million available + addresses is likely to run out of packet forwarding capacity before + it runs out of private addresses to hand out. In this way the ISP + could offer single-level NAT with NAT-PMP, obviating the need to + support nested NAT-PMP. In addition, an ISP that is motivated to + provide their customers with unhindered access to the Internet by + allowing incoming as well as outgoing connections has better ways + to offer this service. Such an ISP could offer its customers real + public IP addresses instead of NAT addresses, or could even choose + to offer its customers full IPv6 connectivity, where no mapping or + translation is required at all. + + +4.3.2 NATs with Multiple Public IP Addresses + + If a NAT maps private addresses to multiple public addresses, + then it SHOULD pick one of those addresses as the one it will + support for inbound connections, and for the purposes of this + protocol it SHOULD act as if that address were its only address. + + +4.3.3 NATs and Routed Private Networks + + In some cases, a large network may be subnetted. Some sites + may install a NAT gateway and subnet the private network. + Such subnetting breaks this protocol because the router address + is not necessarily the address of the device performing NAT. + + Addressing this problem is not a high priority. Any site with the + resources to set up such a configuration should have the resources to + add manual mappings or attain a range of globally unique addresses. + + Not all NATs will support this protocol. In the case where a client + is run behind a NAT that does not support this protocol, the software + relying on the functionality of this protocol may be unusable. + + +Expires 14th March 2007 Cheshire, et al. [Page 15] + +Internet Draft NAT Port Mapping Protocol 14th September 2006 + + +4.3.4 Communication Between Hosts Behind the Same NAT + + NAT gateways supporting NAT-PMP should also implement "hairpin + translation". Hairpin translation means supporting communication + between two local clients being served by the same NAT gateway. + + Suppose device A is listening on private address and port 10.0.0.2:80 + for incoming connections. Using NAT-PMP, device A has obtained a + mapping to public address and port x.x.x.x:80, and has recorded this + public address and port in a public directory of some kind. For + example, it could have created a DNS SRV record containing this + information, and recorded it, using DNS Dynamic Update [RFC 3007], in + a publicly accessible DNS server. Suppose then that device B, behind + the same NAT gateway as device A, but unknowing or uncaring of this + fact, retrieves device A's DNS SRV record and attempts to open a TCP + connection to x.x.x.x:80. The outgoing packets addressed to this + public Internet address will be sent to the NAT gateway for + translation and forwarding. Having translated the source address and + port number on the outgoing packet, the NAT gateway needs to be smart + enough to recognize that the destination address is in fact itself, + and then feed this packet back into its packet reception engine, to + perform the destination port mapping lookup to translate and forward + this packet to device A at address and port 10.0.0.2:80. + +4.3.5 Non UDP/TCP Transport Traffic + + Any communication over transport protocols other than TCP and UDP + will not be served by this protocol. Examples are Generic Routing + Encapsulation (GRE), Authentication Header (AH) and Encapsulating + Security Payload (ESP). + +4.4 Long Term Solution + + As IPv6 is deployed, clients of this protocol supporting IPv6 will be + able to bypass this protocol and the NAT when communicating with + other IPv6 devices. In order to ensure this transition, any client + implementing this protocol SHOULD also implement IPv6 and use this + solution only when IPv6 is not available to both peers. + +4.5 Existing Deployed NATs + + Existing deployed NATs will not support this protocol. This protocol + will only work with NATs that are upgraded to support it. + + + + + + + + + + +Expires 14th March 2007 Cheshire, et al. [Page 16] + +Internet Draft NAT Port Mapping Protocol 14th September 2006 + + +5. Security Considerations + + As discussed in section 3.2 "Determining the Public Address", only + clients on the private side of the NAT may create port mappings, and + only on behalf of themselves. By using IP address spoofing, it's + possible for one client to delete the port mappings of another + client. It's also possible for one client to create port mappings on + behalf of another client. The best way to deal with this + vulnerability is to use IPSec [RFC 2401]. + + Since allowing incoming connections is often a policy decision, any + NAT gateway implementing this protocol SHOULD have an administrative + mechanism to disable it. + + Some people view the property that NATs block inbound connections as + a security benefit which is undermined by this protocol. The authors + of this document have a different point of view. In the days before + NAT, all hosts had unique public IP addresses, and had unhindered + ability to communicate with any other host on the Internet. When NAT + came along it broke this unhindered connectivity, relegating many + hosts to second-class status, unable to receive inbound connections. + This protocol goes some way to undo some of that damage. The purpose + of a NAT gateway should be to allow several hosts to share a single + address, not to simultaneously impede those host's ability to + communicate freely. Security is most properly provided by end-to-end + cryptographic security, and/or by explicit firewall functionality, as + appropriate. Blocking of certain connections should occur only as a + result of explicit and intentional firewall policy, not as an + accidental side-effect of some other technology. + + +6. IANA Considerations + + No IANA services are required by this document. + + +7. Acknowledgments + + The concepts described in this document have been explored, developed + and implemented with help from Bob Bradley, Josh Graessley, Rob + Newberry, Roger Pantos, John Saxton, and James Woodyatt. + + +8. Deployment History + + NAT-PMP client software first became available to the public + through Apple's Darwin Open Source code in August 2004. + NAT-PMP implementations began shipping to end users in large + volumes (i.e. millions) with the launch of Mac OS X 10.4 Tiger + and Bonjour for Windows 1.0 in April 2005. + + + +Expires 14th March 2007 Cheshire, et al. [Page 17] + +Internet Draft NAT Port Mapping Protocol 14th September 2006 + + + The NAT-PMP client in Mac OS X 10.4 Tiger and Bonjour for Windows + exists as part of the mDNSResponder system service. When a client + advertises a service using Wide Area Bonjour [DNS-SD], and the + machine is behind a NAT-PMP-capable NAT gateway, then if the machine + is so configured, the mDNSResponder system service automatically uses + NAT-PMP to set up an inbound port mapping, and then records the + public IP address and port in the global DNS. Existing client + software using the existing Bonjour programming APIs [Bonjour] + gets this functionality automatically. The logic is that if client + software publishes its information into the global DNS via Wide Area + Bonjour service advertising, then it's reasonable to infer an + expectation that this information should be usable by the peers + retrieving it. Generally speaking, recording a private IP address + like 10.0.0.2 in the public DNS is completely pointless because that + address is not reachable from clients on the other side of the NAT + gateway. In the case of a home user with a single computer directly + connected to their Cable or DSL modem, with a single global IPv4 + address and no NAT gateway (a surprisingly common configuration), + publishing that IP address into the global DNS is useful because that + IP address is reachable. In contrast, a home user using a NAT gateway + to share a single global IPv4 address between several computers loses + this ability to receive inbound connections easily. This breaks many + peer-to-peer collaborative applications, like the multi-user text + editor SubEthaEdit [SEE]. Automatically creating the necessary + inbound port mappings helps remedy this unintended side-effect of + NAT. + + The server side of the NAT-PMP protocol is implemented in Apple's + "AirPort Extreme" and "AirPort Express" wireless base stations. + + +9. Copyright Notice + + Copyright (C) The Internet Society (2006). + + This document is subject to the rights, licenses and restrictions + contained in BCP 78, and except as set forth therein, the authors + retain all their rights. For the purposes of this document, + the term "BCP 78" refers exclusively to RFC 3978, "IETF Rights + in Contributions", published March 2005. + + This document and the information contained herein are provided on + an "AS IS" basis and THE CONTRIBUTOR, THE ORGANIZATION HE/SHE + REPRESENTS OR IS SPONSORED BY (IF ANY), THE INTERNET SOCIETY AND THE + INTERNET ENGINEERING TASK FORCE DISCLAIM ALL WARRANTIES, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTY THAT THE USE OF + THE INFORMATION HEREIN WILL NOT INFRINGE ANY RIGHTS OR ANY IMPLIED + WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. + + + + + +Expires 14th March 2007 Cheshire, et al. [Page 18] + +Internet Draft NAT Port Mapping Protocol 14th September 2006 + + +10. Normative References + + [RFC 1918] Y. Rekhter et.al., "Address Allocation for Private + Internets", RFC 1918, February 1996. + + [RFC 2119] RFC 2119 - Key words for use in RFCs to Indicate + Requirement Levels + + +11. Informative References + + [Bonjour] Apple "Bonjour" + + [ETEAISD] J. Saltzer, D. Reed and D. Clark: "End-to-end arguments in + system design", ACM Trans. Comp. Sys., 2(4):277-88, Nov. + 1984 + + [DNS-SD] Cheshire, S., and M. Krochmal, "DNS-Based Service + Discovery", Internet-Draft (work in progress), + draft-cheshire-dnsext-dns-sd-04.txt, August 2006. + + [mDNS] Cheshire, S., and M. Krochmal, "Multicast DNS", + Internet-Draft (work in progress), + draft-cheshire-dnsext-multicastdns-06.txt, August 2006. + + [RFC 2131] R. Droms, "Dynamic Host Configuration Protocol", RFC 2131, + March 1997. + + [RFC 2401] Atkinson, R. and S. Kent, "Security Architecture for the + Internet Protocol", RFC 2401, November 1998. + + [RFC 2663] Srisuresh, P. and M. Holdrege, "IP Network Address + Translator (NAT) Terminology and Considerations", RFC + 2663, August 1999. + + [RFC 3007] Wellington, B., "Simple Secure Domain Name System + (DNS) Dynamic Update", RFC 3007, November 2000. + + [SEE] + + [RFC 3022] RFC 3022 - Network Address Translator + + [RFC 3424] RFC 3424 - IAB Considerations for UNilateral Self-Address + Fixing (UNSAF) Across Network Address Translation + + + + + + + + + +Expires 14th March 2007 Cheshire, et al. [Page 19] + +Internet Draft NAT Port Mapping Protocol 14th September 2006 + + +12. Authors' Addresses + + Stuart Cheshire + Apple Computer, Inc. + 1 Infinite Loop + Cupertino + California 95014 + USA + + Phone: +1 408 974 3207 + EMail: rfc [at] stuartcheshire [dot] org + + + Marc Krochmal + Apple Computer, Inc. + 1 Infinite Loop + Cupertino + California 95014 + USA + + Phone: +1 408 974 4368 + EMail: marc [at] apple [dot] com + + + Kiren Sekar + Sharpcast, Inc. + 250 Cambridge Ave, Suite 101 + Palo Alto + California 94306 + USA + + Phone: +1 650 323 1960 + EMail: ksekar [at] sharpcast [dot] com + + + + + + + + + + + + + + + + + + + + +Expires 14th March 2007 Cheshire, et al. [Page 20] diff --git a/src/upnp/test_upnp.c b/src/upnp/test_upnp.c new file mode 100644 index 000000000..628b40d7c --- /dev/null +++ b/src/upnp/test_upnp.c @@ -0,0 +1,110 @@ +/* + This file is part of GNUnet. + (C) 2006 Christian Grothoff (and other contributing authors) + + GNUnet is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published + by the Free Software Foundation; either version 2, 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 + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with GNUnet; see the file COPYING. If not, write to the + Free Software Foundation, Inc., 59 Temple Place - Suite 330, + Boston, MA 02111-1307, USA. +*/ +/** + * @file src/transports/upnp/upnptest.c + * @brief Testcase for UPnP + * @author Christian Grothoff + */ + +#include "gnunet_util.h" +#include "gnunet_upnp_service.h" +#include "gnunet_core.h" +#include "platform.h" + + + +int +main (int argc, const char *argv[]) +{ + static GNUNET_CoreAPIForPlugins capi; + struct GNUNET_GE_Context *ectx; + struct GNUNET_GC_Configuration *cfg; + struct in_addr addr; + int i; + GNUNET_UPnP_ServiceAPI *upnp; + struct GNUNET_PluginHandle *plug; + GNUNET_ServicePluginInitializationMethod init; + GNUNET_ServicePluginShutdownMethod done; + char ntop_buf[INET_ADDRSTRLEN]; + + ectx = GNUNET_GE_create_context_stderr (GNUNET_NO, + GNUNET_GE_WARNING | GNUNET_GE_ERROR + | GNUNET_GE_FATAL | GNUNET_GE_USER | + GNUNET_GE_ADMIN | + GNUNET_GE_DEVELOPER | + GNUNET_GE_IMMEDIATE | + GNUNET_GE_BULK); + GNUNET_GE_setDefaultContext (ectx); + cfg = GNUNET_GC_create (); + GNUNET_GE_ASSERT (ectx, cfg != NULL); + GNUNET_os_init (ectx); + capi.ectx = ectx; + capi.cfg = cfg; + plug = GNUNET_plugin_load (ectx, "libgnunet", "module_upnp"); + if (plug == NULL) + { + GNUNET_GC_free (cfg); + GNUNET_GE_free_context (ectx); + return 1; + } + init = GNUNET_plugin_resolve_function (plug, "provide_", GNUNET_YES); + if (init == NULL) + { + GNUNET_plugin_unload (plug); + GNUNET_GC_free (cfg); + GNUNET_GE_free_context (ectx); + return 1; + } + upnp = init (&capi); + if (upnp == NULL) + { + GNUNET_plugin_unload (plug); + GNUNET_GC_free (cfg); + GNUNET_GE_free_context (ectx); + return 1; + } + for (i = 0; i < 10; i++) + { + if (GNUNET_shutdown_test () != GNUNET_NO) + break; + if (GNUNET_OK == upnp->get_ip (2086, "TCP", &addr)) + { + printf ("UPnP returned external IP %s\n", + inet_ntop (AF_INET, &addr, ntop_buf, INET_ADDRSTRLEN)); + } + else + { + /* we cannot be sure that there is a UPnP-capable + NAT-box out there, so test should not fail + just because of this! */ + printf ("No UPnP response (yet).\n"); + } + GNUNET_thread_sleep (2 * GNUNET_CRON_SECONDS); + } + done = GNUNET_plugin_resolve_function (plug, "release_", GNUNET_YES); + if (done != NULL) + done (); + GNUNET_plugin_unload (plug); + GNUNET_GC_free (cfg); + GNUNET_GE_free_context (ectx); + return 0; +} + +/* end of upnptest.c */ diff --git a/src/upnp/upnp.c b/src/upnp/upnp.c new file mode 100644 index 000000000..6dc4338a0 --- /dev/null +++ b/src/upnp/upnp.c @@ -0,0 +1,721 @@ +/** + * @file upnp.c UPnP Implementation + * @ingroup core + * + * gaim + * + * Gaim is the legal property of its developers, whose names are too numerous + * to list here. Please refer to the COPYRIGHT file distributed with this + * source distribution. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include "platform.h" +#include "upnp_xmlnode.h" +#include "upnp_util.h" +#include "upnp.h" + +#include + +#define TRUE GNUNET_YES +#define FALSE GNUNET_NO +#define g_return_if_fail(a) if(!(a)) return; +#define g_return_val_if_fail(a, val) if(!(a)) return (val); + +#define HTTP_OK "200 OK" +#define NUM_UDP_ATTEMPTS 2 +#define HTTPMU_HOST_ADDRESS "239.255.255.250" +#define HTTPMU_HOST_PORT 1900 +#define SEARCH_REQUEST_DEVICE "urn:schemas-upnp-org:service:%s" +#define SEARCH_REQUEST_STRING \ + "M-SEARCH * HTTP/1.1\r\n" \ + "MX: 2\r\n" \ + "HOST: 239.255.255.250:1900\r\n" \ + "MAN: \"ssdp:discover\"\r\n" \ + "ST: urn:schemas-upnp-org:service:%s\r\n" \ + "\r\n" +#define WAN_IP_CONN_SERVICE "WANIPConnection:1" +#define WAN_PPP_CONN_SERVICE "WANPPPConnection:1" +#define HTTP_POST_SOAP_HEADER \ + "SOAPACTION: \"urn:schemas-upnp-org:service:%s#%s\"" +#define HTTP_POST_SIZE_HEADER "CONTENT-LENGTH: %u" +#define SOAP_ACTION \ + "\r\n" \ + "\r\n" \ + "\r\n" \ + "\r\n" \ + "%s" \ + "\r\n" \ + "\r\n" \ + "" +#define PORT_MAPPING_LEASE_TIME "0" +#define PORT_MAPPING_DESCRIPTION "GNUNET_UPNP_PORT_FORWARD" +#define ADD_PORT_MAPPING_PARAMS \ + "\r\n" \ + "%i\r\n" \ + "%s\r\n" \ + "%i\r\n" \ + "%s\r\n" \ + "1\r\n" \ + "" \ + PORT_MAPPING_DESCRIPTION \ + "\r\n" \ + "" \ + PORT_MAPPING_LEASE_TIME \ + "\r\n" +#define DELETE_PORT_MAPPING_PARAMS \ + "\r\n" \ + "%i\r\n" \ + "%s\r\n" + +typedef enum +{ + GAIM_UPNP_STATUS_UNDISCOVERED = -1, + GAIM_UPNP_STATUS_UNABLE_TO_DISCOVER, + GAIM_UPNP_STATUS_DISCOVERING, + GAIM_UPNP_STATUS_DISCOVERED +} GaimUPnPStatus; + +typedef struct +{ + GaimUPnPStatus status; + char *control_url; + const char *service_type; + char publicip[16]; +} GaimUPnPControlInfo; + +typedef struct +{ + const char *service_type; + char *full_url; + char *buf; + unsigned int buf_len; + int sock; +} UPnPDiscoveryData; + +static GaimUPnPControlInfo control_info = { + GAIM_UPNP_STATUS_UNDISCOVERED, + NULL, + NULL, + "", +}; + +/** + * This is the signature used for functions that act as a callback + * to CURL. + */ +typedef size_t (*GaimUtilFetchUrlCallback) (void *url_data, + size_t size, + size_t nmemb, void *user_data); + + + +static char * +g_strstr_len (const char *haystack, int haystack_len, const char *needle) +{ + int i; + + g_return_val_if_fail (haystack != NULL, NULL); + g_return_val_if_fail (needle != NULL, NULL); + + if (haystack_len < 0) + return strstr (haystack, needle); + else + { + const char *p = haystack; + int needle_len = strlen (needle); + const char *end = haystack + haystack_len - needle_len; + + if (needle_len == 0) + return (char *) haystack; + + while (*p && p <= end) + { + for (i = 0; i < needle_len; i++) + if (p[i] != needle[i]) + goto next; + + return (char *) p; + + next: + p++; + } + } + + return NULL; +} + +static int +gaim_upnp_compare_device (const xmlnode * device, const char *deviceType) +{ + xmlnode *deviceTypeNode = xmlnode_get_child (device, "deviceType"); + char *tmp; + int ret; + + if (deviceTypeNode == NULL) + return FALSE; + tmp = xmlnode_get_data (deviceTypeNode); + ret = !strcasecmp (tmp, deviceType); + GNUNET_free (tmp); + return ret; +} + +static int +gaim_upnp_compare_service (const xmlnode * service, const char *serviceType) +{ + xmlnode *serviceTypeNode; + char *tmp; + int ret; + + if (service == NULL) + return FALSE; + serviceTypeNode = xmlnode_get_child (service, "serviceType"); + if (serviceTypeNode == NULL) + return FALSE; + tmp = xmlnode_get_data (serviceTypeNode); + ret = !strcasecmp (tmp, serviceType); + GNUNET_free (tmp); + return ret; +} + +static char * +gaim_upnp_parse_description_response (const char *httpResponse, + size_t len, + const char *httpURL, + const char *serviceType) +{ + char *xmlRoot, *baseURL, *controlURL, *service; + xmlnode *xmlRootNode, *serviceTypeNode, *controlURLNode, *baseURLNode; + char *tmp; + + /* find the root of the xml document */ + xmlRoot = g_strstr_len (httpResponse, len, " 0) + CURL_EASY_SETOPT (curl, CURLOPT_PROXY, proxy); + CURL_EASY_SETOPT (curl, CURLOPT_BUFFERSIZE, 1024); /* a bit more than one HELLO */ + CURL_EASY_SETOPT (curl, CURLOPT_CONNECTTIMEOUT, 150L); + /* NOTE: use of CONNECTTIMEOUT without also + setting NOSIGNAL results in really weird + crashes on my system! */ + CURL_EASY_SETOPT (curl, CURLOPT_NOSIGNAL, 1); + return GNUNET_OK; +} + +static int +gaim_upnp_generate_action_message_and_send (const char *proxy, + const char *actionName, + const char *actionParams, + GaimUtilFetchUrlCallback cb, + void *cb_data) +{ + CURL *curl; + int ret; + char *soapHeader; + char *sizeHeader; + char *soapMessage; + struct curl_slist *headers = NULL; + + GNUNET_assert (cb != NULL); + if (0 != curl_global_init (CURL_GLOBAL_WIN32)) + return GNUNET_SYSERR; + /* set the soap message */ + soapMessage = g_strdup_printf (SOAP_ACTION, + actionName, + control_info.service_type, + actionParams, actionName); + soapHeader = g_strdup_printf (HTTP_POST_SOAP_HEADER, + control_info.service_type, actionName); + sizeHeader = g_strdup_printf (HTTP_POST_SIZE_HEADER, strlen (soapMessage)); + curl = curl_easy_init (); + setup_curl (proxy, curl); + CURL_EASY_SETOPT (curl, CURLOPT_URL, control_info.control_url); + CURL_EASY_SETOPT (curl, CURLOPT_WRITEFUNCTION, cb); + CURL_EASY_SETOPT (curl, CURLOPT_WRITEDATA, cb_data); + CURL_EASY_SETOPT (curl, CURLOPT_POST, 1); + headers = curl_slist_append (headers, + "CONTENT-TYPE: text/xml ; charset=\"utf-8\""); + headers = curl_slist_append (headers, soapHeader); + headers = curl_slist_append (headers, sizeHeader); + CURL_EASY_SETOPT (curl, CURLOPT_HTTPHEADER, headers); + CURL_EASY_SETOPT (curl, CURLOPT_POSTFIELDS, soapMessage); + CURL_EASY_SETOPT (curl, CURLOPT_POSTFIELDSIZE, strlen (soapMessage)); + CURL_EASY_SETOPT (curl, CURLOPT_MAXREDIRS, 1L); + CURL_EASY_SETOPT (curl, CURLOPT_CONNECTTIMEOUT, 1L); + CURL_EASY_SETOPT (curl, CURLOPT_TIMEOUT, 2L); + /* NOTE: use of CONNECTTIMEOUT without also + setting NOSIGNAL results in really weird + crashes on my system! */ + CURL_EASY_SETOPT (curl, CURLOPT_NOSIGNAL, 1); + if (ret == CURLE_OK) + ret = curl_easy_perform (curl); +#if 0 + if (ret != CURLE_OK) + GNUNET_log (GNUNET_ERROR_TYPE_ERROR | GNUNET_ERROR_TYPE_BULK, + _ + ("%s failed for url `%s' and post-data `%s' at %s:%d: `%s'\n"), + "curl_easy_perform", control_info.control_url, soapMessage, + __FILE__, __LINE__, curl_easy_strerror (ret)); +#endif + curl_slist_free_all (headers); + curl_easy_cleanup (curl); + curl_global_cleanup (); + GNUNET_free (sizeHeader); + GNUNET_free (soapMessage); + GNUNET_free (soapHeader); + if (ret != CURLE_OK) + return GNUNET_SYSERR; + return GNUNET_OK; +} + + +static size_t +looked_up_public_ip_cb (void *url_data, + size_t size, size_t nmemb, void *user_data) +{ + UPnPDiscoveryData *dd = user_data; + size_t len = size * nmemb; + const char *temp; + const char *temp2; + + if (len + dd->buf_len > 1024 * 1024 * 4) + return 0; /* refuse to process - too big! */ + GNUNET_array_grow (dd->buf, dd->buf_len, dd->buf_len + len); + memcpy (&dd->buf[dd->buf_len - len], url_data, len); + if (dd->buf_len == 0) + return len; + /* extract the ip, or see if there is an error */ + if ((temp = g_strstr_len (dd->buf, + dd->buf_len, "buf_len - (temp - dd->buf), ">"))) + return len; + if (!(temp2 = g_strstr_len (temp, dd->buf_len - (temp - dd->buf), "<"))) + return len; + memset (control_info.publicip, 0, sizeof (control_info.publicip)); + if (temp2 - temp >= sizeof (control_info.publicip)) + temp2 = temp + sizeof (control_info.publicip) - 1; + memcpy (control_info.publicip, temp + 1, temp2 - (temp + 1)); + GNUNET_log (GNUNET_ERROR_TYPE_INFO | GNUNET_ERROR_TYPE_BULK, + _("upnp: NAT Returned IP: %s\n"), control_info.publicip); + return len; +} + + +static size_t +ignore_response (void *url_data, size_t size, size_t nmemb, void *user_data) +{ + return size * nmemb; +} + +/** + * Process downloaded bits of service description. + */ +static size_t +upnp_parse_description_cb (void *httpResponse, + size_t size, size_t nmemb, void *user_data) +{ + UPnPDiscoveryData *dd = user_data; + size_t len = size * nmemb; + char *control_url = NULL; + + if (len + dd->buf_len > 1024 * 1024 * 4) + return len; /* refuse to process - too big! */ + GNUNET_array_grow (dd->buf, dd->buf_len, dd->buf_len + len); + memcpy (&dd->buf[dd->buf_len - len], httpResponse, len); + if (dd->buf_len > 0) + control_url = gaim_upnp_parse_description_response (dd->buf, + dd->buf_len, + dd->full_url, + dd->service_type); + control_info.status = control_url ? GAIM_UPNP_STATUS_DISCOVERED + : GAIM_UPNP_STATUS_UNABLE_TO_DISCOVER; + GNUNET_free_non_null (control_info.control_url); + control_info.control_url = control_url; + control_info.service_type = dd->service_type; + return len; +} + +static int +gaim_upnp_parse_description (char *proxy, UPnPDiscoveryData * dd) +{ + CURL *curl; + int ret; + + if (0 != curl_global_init (CURL_GLOBAL_WIN32)) + return GNUNET_SYSERR; + curl = curl_easy_init (); + setup_curl (proxy, curl); + ret = CURLE_OK; + CURL_EASY_SETOPT (curl, CURLOPT_URL, dd->full_url); + CURL_EASY_SETOPT (curl, CURLOPT_WRITEFUNCTION, &upnp_parse_description_cb); + CURL_EASY_SETOPT (curl, CURLOPT_WRITEDATA, dd); + CURL_EASY_SETOPT (curl, CURLOPT_MAXREDIRS, 1L); + CURL_EASY_SETOPT (curl, CURLOPT_CONNECTTIMEOUT, 1L); + CURL_EASY_SETOPT (curl, CURLOPT_TIMEOUT, 2L); + + /* NOTE: use of CONNECTTIMEOUT without also + setting NOSIGNAL results in really weird + crashes on my system! */ + CURL_EASY_SETOPT (curl, CURLOPT_NOSIGNAL, 1); + ret = curl_easy_perform (curl); + if (ret != CURLE_OK) + GNUNET_log (GNUNET_ERROR_TYPE_ERROR | GNUNET_ERROR_TYPE_BULK, + _("%s failed at %s:%d: `%s'\n"), + "curl_easy_perform", __FILE__, __LINE__, + curl_easy_strerror (ret)); + curl_easy_cleanup (curl); + curl_global_cleanup (); + if (control_info.control_url == NULL) + return GNUNET_SYSERR; + return GNUNET_OK; +} + +int +gaim_upnp_discover (struct GNUNET_CONFIGURATION_Handle *cfg, int sock) +{ + char *proxy; + socklen_t avail; + struct sockaddr_in server; + int retry_count; + char *sendMessage; + size_t totalSize; + int sentSuccess; + char buf[65536]; + int buf_len; + const char *startDescURL; + const char *endDescURL; + int ret; + UPnPDiscoveryData dd; + struct sockaddr *sa; + + memset (&dd, 0, sizeof (UPnPDiscoveryData)); + if (control_info.status == GAIM_UPNP_STATUS_DISCOVERING) + return GNUNET_NO; + dd.sock = sock; + memset (&server, 0, sizeof (struct sockaddr_in)); + server.sin_family = AF_INET; + avail = sizeof (struct sockaddr_in); + sa = (struct sockaddr *) &server; + if (GNUNET_OK != + GNUNET_get_ip_from_hostname (HTTPMU_HOST_ADDRESS, AF_INET, &sa, &avail)) + { + return GNUNET_SYSERR; + } + server.sin_port = htons (HTTPMU_HOST_PORT); + control_info.status = GAIM_UPNP_STATUS_DISCOVERING; + + /* because we are sending over UDP, if there is a failure + we should retry the send NUM_UDP_ATTEMPTS times. Also, + try different requests for WANIPConnection and WANPPPConnection */ + for (retry_count = 0; retry_count < NUM_UDP_ATTEMPTS; retry_count++) + { + sentSuccess = FALSE; + if ((retry_count % 2) == 0) + dd.service_type = WAN_IP_CONN_SERVICE; + else + dd.service_type = WAN_PPP_CONN_SERVICE; + sendMessage = g_strdup_printf (SEARCH_REQUEST_STRING, dd.service_type); + totalSize = strlen (sendMessage); + do + { + if (SENDTO (dd.sock, + sendMessage, + totalSize, + 0, + (struct sockaddr *) &server, + sizeof (struct sockaddr_in)) == totalSize) + { + sentSuccess = TRUE; + break; + } + } + while (((errno == EINTR) || (errno == EAGAIN)) && + (GNUNET_shutdown_test () == GNUNET_NO)); + GNUNET_free (sendMessage); + if (sentSuccess) + break; + } + if (sentSuccess == FALSE) + return GNUNET_SYSERR; + + /* try to read response */ + do + { + buf_len = recv (dd.sock, buf, sizeof (buf) - 1, 0); + if (buf_len > 0) + { + buf[buf_len] = '\0'; + break; + } + else if (errno != EINTR) + { + continue; + } + } + while ((errno == EINTR) && (GNUNET_shutdown_test () == GNUNET_NO)); + + /* parse the response, and see if it was a success */ + if (g_strstr_len (buf, buf_len, HTTP_OK) == NULL) + return GNUNET_SYSERR; + if ((startDescURL = g_strstr_len (buf, buf_len, "http://")) == NULL) + return GNUNET_SYSERR; + + endDescURL = g_strstr_len (startDescURL, + buf_len - (startDescURL - buf), "\r"); + if (endDescURL == NULL) + endDescURL = g_strstr_len (startDescURL, + buf_len - (startDescURL - buf), "\n"); + if (endDescURL == NULL) + return GNUNET_SYSERR; + if (endDescURL == startDescURL) + return GNUNET_SYSERR; + dd.full_url = GNUNET_strdup (startDescURL); + dd.full_url[endDescURL - startDescURL] = '\0'; + proxy = NULL; + GNUNET_CONFIGURATION_get_value_string (cfg, + "GNUNETD", "HTTP-PROXY", &proxy); + ret = gaim_upnp_parse_description (proxy, &dd); + GNUNET_free (dd.full_url); + GNUNET_array_grow (dd.buf, dd.buf_len, 0); + if (ret == GNUNET_OK) + { + ret = gaim_upnp_generate_action_message_and_send (proxy, + "GetExternalIPAddress", + "", + looked_up_public_ip_cb, + &dd); + GNUNET_array_grow (dd.buf, dd.buf_len, 0); + } + GNUNET_free (proxy); + return ret; +} + +const char * +gaim_upnp_get_public_ip () +{ + if ((control_info.status == GAIM_UPNP_STATUS_DISCOVERED) + && (strlen (control_info.publicip) > 0)) + return control_info.publicip; + return NULL; +} + +int +gaim_upnp_change_port_mapping (struct GNUNET_CONFIGURATION_Handle *cfg, + int do_add, + unsigned short portmap, const char *protocol) +{ + const char *action_name; + char *action_params; + char *internal_ip; + char *proxy; + int ret; + + if (control_info.status != GAIM_UPNP_STATUS_DISCOVERED) + return GNUNET_NO; + if (do_add) + { + internal_ip = GNUNET_upnp_get_internal_ip (cfg); + if (internal_ip == NULL) + { + gaim_debug_error ("upnp", + "gaim_upnp_set_port_mapping(): couldn't get local ip\n"); + return GNUNET_NO; + } + action_name = "AddPortMapping"; + action_params = g_strdup_printf (ADD_PORT_MAPPING_PARAMS, + portmap, + protocol, portmap, internal_ip); + GNUNET_free (internal_ip); + } + else + { + action_name = "DeletePortMapping"; + action_params = g_strdup_printf (DELETE_PORT_MAPPING_PARAMS, + portmap, protocol); + } + proxy = NULL; + GNUNET_CONFIGURATION_get_value_string (cfg, + "GNUNETD", "HTTP-PROXY", &proxy); + ret = + gaim_upnp_generate_action_message_and_send (proxy, action_name, + action_params, + &ignore_response, NULL); + + GNUNET_free (action_params); + GNUNET_free (proxy); + return ret; +} + +/* end of upnp.c */ diff --git a/src/upnp/upnp.h b/src/upnp/upnp.h new file mode 100644 index 000000000..1f99c5352 --- /dev/null +++ b/src/upnp/upnp.h @@ -0,0 +1,82 @@ +/** + * @file upnp.h Universal Plug N Play API + * @ingroup core + * + * gaim + * + * Gaim is the legal property of its developers, whose names are too numerous + * to list here. Please refer to the COPYRIGHT file distributed with this + * source distribution. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef _GAIM_UPNP_H_ +#define _GAIM_UPNP_H_ + +#include +#include +#include "gnunet_util_lib.h" + +#ifdef __cplusplus +extern "C" +{ +#if 0 /* keep Emacsens' auto-indent happy */ +} +#endif +#endif + +/** + * Sends a discovery request to search for a UPnP enabled IGD that + * contains the WANIPConnection service that will allow us to receive the + * public IP address of the IGD, and control it for forwarding ports. + * The result will be cached for further use. + */ +int gaim_upnp_discover (struct GNUNET_CONFIGURATION_Handle *cfg, int sock); + +/** + * Gets the IP address from a UPnP enabled IGD that sits on the local + * network, so when getting the network IP, instead of returning the + * local network IP, the public IP is retrieved. This is a cached value from + * the time of the UPnP discovery. + * + * @return The IP address of the network, or NULL if something went wrong + */ +const char *gaim_upnp_get_public_ip (void); + +/** + * Maps Ports in a UPnP enabled IGD that sits on the local network to + * this gaim client. Essentially, this function takes care of the port + * forwarding so things like file transfers can work behind NAT firewalls + * + * @param portmap The port to map to this client + * @param protocol The protocol to map, either "TCP" or "UDP" + * @param do_add TRUE/GNUNET_YES to add, FALSE/GNUNET_NO to remove + * @param cb an optional callback function to be notified when the mapping + * addition is complete + * @param cb_data Extra data to be passed to the callback + */ +int gaim_upnp_change_port_mapping (struct GNUNET_CONFIGURATION_Handle *cfg, + int do_add, + uint16_t portmap, const char *protocol); + +#if 0 /* keep Emacsens' auto-indent happy */ +{ +#endif +#ifdef __cplusplus +} +#endif + +#endif /* _GAIM_UPNP_H_ */ diff --git a/src/upnp/upnp_init.c b/src/upnp/upnp_init.c new file mode 100644 index 000000000..3dc0d2b7c --- /dev/null +++ b/src/upnp/upnp_init.c @@ -0,0 +1,208 @@ +/* + This file is part of GNUnet + (C) 2006 Christian Grothoff (and other contributing authors) + + GNUnet is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published + by the Free Software Foundation; either version 2, 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 + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with GNUnet; see the file COPYING. If not, write to the + Free Software Foundation, Inc., 59 Temple Place - Suite 330, + Boston, MA 02111-1307, USA. +*/ + +/** + * @file src/transports/upnp/init.c + * @brief API for UPnP access + * @author Christian Grothoff + */ + +#include "platform.h" +#include "gnunet_util.h" +#include "upnp.h" +#include "gnunet_upnp_service.h" +#include "gnunet_core.h" + +static struct GNUNET_GE_Context *ectx; + +static struct GNUNET_GC_Configuration *cfg; + +static struct GNUNET_CronManager *cron; + +static struct GNUNET_Mutex *lock; + +typedef struct +{ + unsigned short port; + const char *proto; +} PMap; + +static PMap *maps; + +static unsigned int maps_size; + +static struct GNUNET_ThreadHandle *discovery; + +static int discovery_socket; + +/** + * Obtain the public/external IP address. + * + * @return GNUNET_SYSERR on error, GNUNET_OK on success + */ +static int +gnunet_upnp_get_public_ip (struct in_addr *address) +{ + const char *ip; + socklen_t socklen; + struct sockaddr *sa; + struct sockaddr_in s4; + int ret; + + ip = gaim_upnp_get_public_ip (); + if (ip == NULL) + return GNUNET_SYSERR; + socklen = sizeof (struct sockaddr_in); + sa = (struct sockaddr *) &s4; + ret = GNUNET_get_ip_from_hostname (NULL, ip, AF_INET, &sa, &socklen); + if (ret == GNUNET_OK) + *address = s4.sin_addr; + return ret; +} + +static void +kill_discovery () +{ + void *unused; + + if (discovery != NULL) + { + SHUTDOWN (discovery_socket, SHUT_RDWR); + CLOSE (discovery_socket); + GNUNET_thread_join (discovery, &unused); + discovery = NULL; + } +} + +static void * +discover_thread () +{ + gaim_upnp_discover (ectx, cfg, discovery_socket); + return NULL; +} + +/** + * Periodically try to (re)discover UPnP access points. + */ +static void +discover (void *unused) +{ + kill_discovery (); + discovery_socket = SOCKET (PF_INET, SOCK_DGRAM, 0); + if (discovery_socket == -1) + return; + discovery = GNUNET_thread_create (&discover_thread, NULL, 1024 * 128); +} + +/** + * Periodically repeat our requests for port mappings. + */ +static void +portmap (void *unused) +{ + unsigned int i; + + GNUNET_mutex_lock (lock); + for (i = 0; i < maps_size; i++) + gaim_upnp_change_port_mapping (ectx, + cfg, GNUNET_NO, maps[i].port, + maps[i].proto); + GNUNET_mutex_unlock (lock); +} + + +/** + * Get the external IP address for the local machine. + * + * @return GNUNET_SYSERR on error, GNUNET_OK on success + */ +static int +gnunet_upnp_get_ip (unsigned short port, + const char *protocol, struct in_addr *address) +{ + unsigned int i; + + GNUNET_mutex_lock (lock); + for (i = 0; i < maps_size; i++) + if ((0 == strcmp (maps[i].proto, protocol)) && (maps[i].port == port)) + break; + if (i == maps_size) + { + /* new entry! */ + GNUNET_array_grow (maps, maps_size, maps_size + 1); + maps[i].proto = protocol; + maps[i].port = port; + gaim_upnp_change_port_mapping (ectx, cfg, GNUNET_YES, port, protocol); + } + GNUNET_mutex_unlock (lock); + return gnunet_upnp_get_public_ip (address); +} + + +/** + * Get the external IP address for the local machine. + */ +GNUNET_UPnP_ServiceAPI * +provide_module_upnp (GNUNET_CoreAPIForPlugins * capi) +{ + static GNUNET_UPnP_ServiceAPI api; + + ectx = capi->ectx; + cfg = capi->cfg; + cron = GNUNET_cron_create (ectx); + lock = GNUNET_mutex_create (GNUNET_NO); + GNUNET_cron_start (cron); + GNUNET_cron_add_job (cron, &discover, 0, 5 * GNUNET_CRON_MINUTES, NULL); + GNUNET_cron_add_job (cron, &portmap, 150 * GNUNET_CRON_SECONDS, + 5 * GNUNET_CRON_MINUTES, NULL); + api.get_ip = gnunet_upnp_get_ip; + return &api; +} + +/** + * Shutdown UPNP. + */ +int +release_module_upnp () +{ + unsigned int i; + + if (cron == NULL) + return GNUNET_SYSERR; /* not loaded! */ + for (i = 0; i < maps_size; i++) + gaim_upnp_change_port_mapping (ectx, + cfg, GNUNET_NO, maps[i].port, + maps[i].proto); + GNUNET_cron_stop (cron); + GNUNET_cron_del_job (cron, &discover, 5 * GNUNET_CRON_MINUTES, NULL); + GNUNET_cron_del_job (cron, &portmap, 5 * GNUNET_CRON_MINUTES, NULL); + GNUNET_cron_destroy (cron); + kill_discovery (); + cron = NULL; + GNUNET_mutex_destroy (lock); + lock = NULL; + GNUNET_array_grow (maps, maps_size, 0); + ectx = NULL; + cfg = NULL; + return GNUNET_OK; +} + + +/* end of init.c */ diff --git a/src/upnp/upnp_ip.c b/src/upnp/upnp_ip.c new file mode 100644 index 000000000..3716fc671 --- /dev/null +++ b/src/upnp/upnp_ip.c @@ -0,0 +1,47 @@ +/* + This file is part of GNUnet. + (C) 2006, 2007 Christian Grothoff (and other contributing authors) + + GNUnet is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published + by the Free Software Foundation; either version 2, 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 + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with GNUnet; see the file COPYING. If not, write to the + Free Software Foundation, Inc., 59 Temple Place - Suite 330, + Boston, MA 02111-1307, USA. +*/ + +/** + * @file transports/upnp/ip.c + * @brief code to determine the IP of the local machine + * + * @author Christian Grothoff + */ + +#include +#include "platform.h" +#include "gnunet_util.h" +#include "ip.h" + +/** + * Get the IP address for the local machine. + * @return NULL on error + */ +char * +GNUNET_upnp_get_internal_ip (struct GNUNET_GC_Configuration *cfg, + struct GNUNET_GE_Context *ectx) +{ + struct in_addr address; + + return GNUNET_get_local_ip (cfg, ectx, &address); +} + + +/* end of ip.c */ diff --git a/src/upnp/upnp_ip.h b/src/upnp/upnp_ip.h new file mode 100644 index 000000000..adc06bbd5 --- /dev/null +++ b/src/upnp/upnp_ip.h @@ -0,0 +1,40 @@ +/* + This file is part of GNUnet. + (C) 2001, 2002, 2003, 2004, 2005 Christian Grothoff (and other contributing authors) + + GNUnet is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published + by the Free Software Foundation; either version 2, 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 + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with GNUnet; see the file COPYING. If not, write to the + Free Software Foundation, Inc., 59 Temple Place - Suite 330, + Boston, MA 02111-1307, USA. +*/ + +/** + * @file transports/upnp/ip.h + * @brief + * + * @author Christian Grothoff + */ + +#ifndef IP_H +#define IP_H + + +/** + * Get the IP address for the local machine. + * @return NULL on error + */ +char *GNUNET_upnp_get_internal_ip (struct GNUNET_GC_Configuration *cfg, + struct GNUNET_GE_Context *ectx); + + +#endif diff --git a/src/upnp/upnp_util.c b/src/upnp/upnp_util.c new file mode 100644 index 000000000..cef40b578 --- /dev/null +++ b/src/upnp/upnp_util.c @@ -0,0 +1,152 @@ +/* + * @file util.h Utility Functions + * @ingroup core + * + * Gaim is the legal property of its developers, whose names are too numerous + * to list here. Please refer to the COPYRIGHT file distributed with this + * source distribution. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include "platform.h" +#include "util.h" +#include "gnunet_util.h" + +/* Returns a NULL-terminated string after unescaping an entity + * (eg. &, < & etc.) starting at s. Returns NULL on failure.*/ +static char * +detect_entity (const char *text, int *length) +{ + const char *pln; + int len; + int pound; + char b[7]; + char *buf; + + if (!text || *text != '&') + return NULL; + +#define IS_ENTITY(s) (!strncasecmp(text, s, (len = sizeof(s) - 1))) + + if (IS_ENTITY ("&")) + pln = "&"; + else if (IS_ENTITY ("<")) + pln = "<"; + else if (IS_ENTITY (">")) + pln = ">"; + else if (IS_ENTITY (" ")) + pln = " "; + else if (IS_ENTITY ("©")) + pln = "\302\251"; /* or use g_unichar_to_utf8(0xa9); */ + else if (IS_ENTITY (""")) + pln = "\""; + else if (IS_ENTITY ("®")) + pln = "\302\256"; /* or use g_unichar_to_utf8(0xae); */ + else if (IS_ENTITY ("'")) + pln = "\'"; + else if (*(text + 1) == '#' && (sscanf (text, "&#%u;", £) == 1) && + pound != 0 && *(text + 3 + (int) log10 (pound)) == ';') + { + buf = GNUNET_convert_string_to_utf8 (NULL, + (const char *) £, + 2, "UNICODE"); + if (strlen (buf) > 6) + buf[6] = '\0'; + strcpy (b, buf); + pln = b; + GNUNET_free (buf); + len = 2; + while (isdigit ((int) text[len])) + len++; + if (text[len] == ';') + len++; + } + else + return NULL; + + if (length) + *length = len; + return GNUNET_strdup (pln); +} + +char * +g_strdup_printf (const char *fmt, ...) +{ + size_t size; + char *buf; + va_list va; + + va_start (va, fmt); + size = VSNPRINTF (NULL, 0, fmt, va) + 1; + va_end (va); + buf = GNUNET_malloc (size); + va_start (va, fmt); + VSNPRINTF (buf, size, fmt, va); + va_end (va); + return buf; +} + +char * +gaim_unescape_html (const char *html) +{ + if (html != NULL) + { + const char *c = html; + char *ret = GNUNET_strdup (""); + char *app; + while (*c) + { + int len; + char *ent; + + if ((ent = detect_entity (c, &len)) != NULL) + { + app = g_strdup_printf ("%s%s", ret, ent); + GNUNET_free (ret); + ret = app; + c += len; + GNUNET_free (ent); + } + else if (!strncmp (c, "
", 4)) + { + app = g_strdup_printf ("%s%s", ret, "\n"); + GNUNET_free (ret); + ret = app; + c += 4; + } + else + { + app = g_strdup_printf ("%s%c", ret, *c); + GNUNET_free (ret); + ret = app; + c++; + } + } + return ret; + } + return NULL; +} + + +int +gaim_str_has_prefix (const char *s, const char *p) +{ + if ((s == NULL) || (p == NULL)) + return 0; + return !strncmp (s, p, strlen (p)); +} + +/* end of util.c */ diff --git a/src/upnp/upnp_util.h b/src/upnp/upnp_util.h new file mode 100644 index 000000000..89a8d71cf --- /dev/null +++ b/src/upnp/upnp_util.h @@ -0,0 +1,68 @@ +/** + * @file util.h Utility Functions + * @ingroup core + * + * gaim + * + * Gaim is the legal property of its developers, whose names are too numerous + * to list here. Please refer to the COPYRIGHT file distributed with this + * source distribution. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * @todo Rename the functions so that they live somewhere in the gaim + * namespace. + */ +#ifndef _GAIM_UTIL_H_ +#define _GAIM_UTIL_H_ + +#include +#include + +#ifdef __cplusplus +extern "C" +{ +#endif + +/** + * Unescapes HTML entities to their literal characters. + * For example "&" is replaced by '&' and so on. + * Actually only "&", """, "<" and ">" are currently + * supported. + * + * @param html The string in which to unescape any HTML entities + * + * @return the text with HTML entities literalized + */ + char *gaim_unescape_html (const char *html); + +/** + * Compares two strings to see if the first contains the second as + * a proper prefix. + * + * @param s The string to check. + * @param p The prefix in question. + * + * @return TRUE if p is a prefix of s, otherwise FALSE. + */ + int gaim_str_has_prefix (const char *s, const char *p); + + char *g_strdup_printf (const char *fmt, ...); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/src/upnp/upnp_xmlnode.c b/src/upnp/upnp_xmlnode.c new file mode 100644 index 000000000..b37528f00 --- /dev/null +++ b/src/upnp/upnp_xmlnode.c @@ -0,0 +1,487 @@ +/** + * @file xmlnode.c XML DOM functions + * + * gaim + * + * Gaim is the legal property of its developers, whose names are too numerous + * to list here. Please refer to the COPYRIGHT file distributed with this + * source distribution. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +/* A lot of this code at least resembles the code in libxode, but since + * libxode uses memory pools that we simply have no need for, I decided to + * write my own stuff. Also, re-writing this lets me be as lightweight + * as I want to be. Thank you libxode for giving me a good starting point */ + +#include "platform.h" + +#include "util.h" +#include "gnunet_util.h" +#include "xmlnode.h" + +#include +#include + + +#ifdef _WIN32 +# define NEWLINE_S "\r\n" +#else +# define NEWLINE_S "\n" +#endif + +#define TRUE GNUNET_YES +#define FALSE GNUNET_NO + +#define g_return_if_fail(a) if(!(a)) return; +#define g_return_val_if_fail(a, val) if(!(a)) return (val); + +/** + * The valid types for an xmlnode + */ +typedef enum _XMLNodeType +{ + XMLNODE_TYPE_TAG, /**< Just a tag */ + XMLNODE_TYPE_ATTRIB, /**< Has attributes */ + XMLNODE_TYPE_DATA /**< Has data */ +} XMLNodeType; + +typedef struct +{ + xmlnode *current; + xmlnode **nodes; + unsigned int pos; + unsigned int size; +} XMLNodePool; + +struct _xmlnode +{ + char *name; /**< The name of the node. */ + char *xmlns; /**< The namespace of the node */ + XMLNodeType type; /**< The type of the node. */ + char *data; /**< The data for the node. */ + size_t data_sz; /**< The size of the data. */ + struct _xmlnode *parent; /**< The parent node or @c NULL.*/ + struct _xmlnode *child; /**< The child node or @c NULL.*/ + struct _xmlnode *lastchild; /**< The last child node or @c NULL.*/ + struct _xmlnode *next; /**< The next node or @c NULL. */ + XMLNodePool *pool; + int free_pool; /* set to GNUNET_YES for the root node, which must free the pool */ +}; + + +static void * +g_memdup (const void *data, size_t s) +{ + void *ret; + + ret = GNUNET_malloc (s); + memcpy (ret, data, s); + return ret; +} + +static char * +g_string_append_len (char *prefix, const void *data, size_t s) +{ + char *ret; + + ret = g_strdup_printf ("%s%.*s", prefix, s, data); + GNUNET_free (prefix); + return ret; +} + +static xmlnode * +new_node (const char *name, XMLNodeType type, void *user_data) +{ + xmlnode *node = GNUNET_malloc (sizeof (xmlnode)); + + node->name = name == NULL ? NULL : GNUNET_strdup (name); + node->type = type; + node->pool = user_data; + if (node->pool->size == node->pool->pos) + GNUNET_array_grow (node->pool->nodes, node->pool->size, + node->pool->size * 2 + 64); + node->pool->nodes[node->pool->pos++] = node; + node->free_pool = 0; + return node; +} + +static xmlnode * +xmlnode_new (const char *name, void *user_data) +{ + g_return_val_if_fail (name != NULL, NULL); + return new_node (name, XMLNODE_TYPE_TAG, user_data); +} + +static void +xmlnode_insert_child (xmlnode * parent, xmlnode * child) +{ + g_return_if_fail (parent != NULL); + g_return_if_fail (child != NULL); + + child->parent = parent; + if (parent->lastchild) + parent->lastchild->next = child; + else + parent->child = child; + parent->lastchild = child; +} + +static xmlnode * +xmlnode_new_child (xmlnode * parent, const char *name, void *user_data) +{ + xmlnode *node; + + g_return_val_if_fail (parent != NULL, NULL); + g_return_val_if_fail (name != NULL, NULL); + node = new_node (name, XMLNODE_TYPE_TAG, user_data); + xmlnode_insert_child (parent, node); + return node; +} + +static void +xmlnode_insert_data (xmlnode * node, + const char *data, int size, void *user_data) +{ + xmlnode *child; + size_t real_size; + + g_return_if_fail (node != NULL); + g_return_if_fail (data != NULL); + g_return_if_fail (size != 0); + real_size = size == -1 ? strlen (data) : size; + child = new_node (NULL, XMLNODE_TYPE_DATA, user_data); + child->data = g_memdup (data, real_size); + child->data_sz = real_size; + xmlnode_insert_child (node, child); +} + +static void +xmlnode_remove_attrib (xmlnode * node, const char *attr) +{ + xmlnode *attr_node, *sibling = NULL; + + g_return_if_fail (node != NULL); + g_return_if_fail (attr != NULL); + + for (attr_node = node->child; attr_node; attr_node = attr_node->next) + { + if (attr_node->type == XMLNODE_TYPE_ATTRIB && + !strcmp (attr_node->name, attr)) + { + if (node->child == attr_node) + { + node->child = attr_node->next; + } + else + { + sibling->next = attr_node->next; + } + if (node->lastchild == attr_node) + { + node->lastchild = sibling; + } + xmlnode_free (attr_node); + return; + } + sibling = attr_node; + } +} + +static void +xmlnode_set_attrib (xmlnode * node, + const char *attr, const char *value, void *user_data) +{ + xmlnode *attrib_node; + + g_return_if_fail (node != NULL); + g_return_if_fail (attr != NULL); + g_return_if_fail (value != NULL); + xmlnode_remove_attrib (node, attr); + attrib_node = new_node (attr, XMLNODE_TYPE_ATTRIB, user_data); + attrib_node->data = GNUNET_strdup (value); + xmlnode_insert_child (node, attrib_node); +} + +static void +xmlnode_set_namespace (xmlnode * node, const char *xmlns) +{ + g_return_if_fail (node != NULL); + GNUNET_free_non_null (node->xmlns); + node->xmlns = GNUNET_strdup (xmlns); +} + +static const char * +xmlnode_get_namespace (xmlnode * node) +{ + g_return_val_if_fail (node != NULL, NULL); + return node->xmlns; +} + +static void +freePool (XMLNodePool * pool) +{ + unsigned int i; + xmlnode *x; + + for (i = 0; i < pool->pos; i++) + { + x = pool->nodes[i]; + GNUNET_free_non_null (x->name); + GNUNET_free_non_null (x->data); + GNUNET_free_non_null (x->xmlns); + GNUNET_free (x); + } + GNUNET_array_grow (pool->nodes, pool->size, 0); + GNUNET_free (pool); +} + +void +xmlnode_free (xmlnode * node) +{ + g_return_if_fail (node != NULL); + if (node->free_pool != GNUNET_YES) + return; + freePool (node->pool); +} + +static xmlnode * +xmlnode_get_child_with_namespace (const xmlnode * parent, + const char *name, const char *ns) +{ + xmlnode *x; + xmlnode *ret = NULL; + char *parent_name; + char *child_name; + + if (parent == NULL) + return NULL; + if (name == NULL) + return NULL; + + parent_name = GNUNET_strdup (name); + child_name = strstr (parent_name, "/"); + if (child_name != NULL) + { + child_name[0] = '\0'; + child_name++; + } + + for (x = parent->child; x; x = x->next) + { + const char *xmlns = NULL; + if (ns) + xmlns = xmlnode_get_namespace (x); + + if (x->type == XMLNODE_TYPE_TAG && name + && !strcmp (parent_name, x->name) && (!ns + || (xmlns + && !strcmp (ns, xmlns)))) + { + ret = x; + break; + } + } + + if (child_name && ret) + ret = xmlnode_get_child (ret, child_name); + + GNUNET_free (parent_name); + return ret; +} + +xmlnode * +xmlnode_get_child (const xmlnode * parent, const char *name) +{ + return xmlnode_get_child_with_namespace (parent, name, NULL); +} + +char * +xmlnode_get_data (xmlnode * node) +{ + char *str = NULL; + xmlnode *c; + + if (node == NULL) + return NULL; + for (c = node->child; c; c = c->next) + { + if (c->type == XMLNODE_TYPE_DATA) + { + if (!str) + str = GNUNET_strdup (""); + str = g_string_append_len (str, c->data, c->data_sz); + } + } + if (str == NULL) + return NULL; + + return str; +} + +static void +xmlnode_parser_element_start_libxml (void *user_data, + const xmlChar * element_name, + const xmlChar * prefix, + const xmlChar * xmlns, + int nb_namespaces, + const xmlChar ** namespaces, + int nb_attributes, + int nb_defaulted, + const xmlChar ** attributes) +{ + XMLNodePool *xpd = user_data; + xmlnode *node; + int i; + + if (!element_name) + return; + if (xpd->current) + node = + xmlnode_new_child (xpd->current, (const char *) element_name, + user_data); + else + node = xmlnode_new ((const char *) element_name, user_data); + + xmlnode_set_namespace (node, (const char *) xmlns); + + for (i = 0; i < nb_attributes * 5; i += 5) + { + char *txt; + int attrib_len = attributes[i + 4] - attributes[i + 3]; + char *attrib = GNUNET_malloc (attrib_len + 1); + memcpy (attrib, attributes[i + 3], attrib_len); + attrib[attrib_len] = '\0'; + txt = attrib; + attrib = gaim_unescape_html (txt); + GNUNET_free (txt); + xmlnode_set_attrib (node, (const char *) attributes[i], attrib, + user_data); + GNUNET_free (attrib); + } + xpd->current = node; +} + +static void +xmlnode_parser_element_end_libxml (void *user_data, + const xmlChar * element_name, + const xmlChar * prefix, + const xmlChar * xmlns) +{ + XMLNodePool *xpd = user_data; + + if (!element_name || !xpd->current) + return; + if (xpd->current->parent) + { + if (!xmlStrcmp ((xmlChar *) xpd->current->name, element_name)) + xpd->current = xpd->current->parent; + } +} + +static void +xmlnode_parser_element_text_libxml (void *user_data, + const xmlChar * text, int text_len) +{ + XMLNodePool *xpd = user_data; + + if (!xpd->current || !text || !text_len) + return; + xmlnode_insert_data (xpd->current, + (const char *) text, text_len, user_data); +} + +static xmlSAXHandler xmlnode_parser_libxml = { + .internalSubset = NULL, + .isStandalone = NULL, + .hasInternalSubset = NULL, + .hasExternalSubset = NULL, + .resolveEntity = NULL, + .getEntity = NULL, + .entityDecl = NULL, + .notationDecl = NULL, + .attributeDecl = NULL, + .elementDecl = NULL, + .unparsedEntityDecl = NULL, + .setDocumentLocator = NULL, + .startDocument = NULL, + .endDocument = NULL, + .startElement = NULL, + .endElement = NULL, + .reference = NULL, + .characters = xmlnode_parser_element_text_libxml, + .ignorableWhitespace = NULL, + .processingInstruction = NULL, + .comment = NULL, + .warning = NULL, + .error = NULL, + .fatalError = NULL, + .getParameterEntity = NULL, + .cdataBlock = NULL, + .externalSubset = NULL, + .initialized = XML_SAX2_MAGIC, + ._private = NULL, + .startElementNs = xmlnode_parser_element_start_libxml, + .endElementNs = xmlnode_parser_element_end_libxml, + .serror = NULL +}; + +xmlnode * +xmlnode_from_str (const char *str, int size) +{ + XMLNodePool *xpd; + xmlnode *ret; + size_t real_size; + + g_return_val_if_fail (str != NULL, NULL); + + real_size = size < 0 ? strlen (str) : size; + xpd = GNUNET_malloc (sizeof (XMLNodePool)); + memset (xpd, 0, sizeof (XMLNodePool)); + if (xmlSAXUserParseMemory (&xmlnode_parser_libxml, xpd, str, real_size) < 0) + { + freePool (xpd); + return NULL; + } + ret = xpd->current; + ret->free_pool = GNUNET_YES; + return ret; +} + +xmlnode * +xmlnode_get_next_twin (xmlnode * node) +{ + xmlnode *sibling; + const char *ns = xmlnode_get_namespace (node); + + g_return_val_if_fail (node != NULL, NULL); + g_return_val_if_fail (node->type == XMLNODE_TYPE_TAG, NULL); + + for (sibling = node->next; sibling; sibling = sibling->next) + { + const char *xmlns = NULL; + if (ns) + xmlns = xmlnode_get_namespace (sibling); + + if (sibling->type == XMLNODE_TYPE_TAG + && !strcmp (node->name, sibling->name) && (!ns + || (xmlns + && !strcmp (ns, + xmlns)))) + return sibling; + } + return NULL; +} diff --git a/src/upnp/upnp_xmlnode.h b/src/upnp/upnp_xmlnode.h new file mode 100644 index 000000000..199975e37 --- /dev/null +++ b/src/upnp/upnp_xmlnode.h @@ -0,0 +1,92 @@ +/** + * @file xmlnode.h XML DOM functions + * @ingroup core + * + * gaim + * + * Gaim is the legal property of its developers, whose names are too numerous + * to list here. Please refer to the COPYRIGHT file distributed with this + * source distribution. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ +#ifndef _GGAIM_XMLNODE_H_ +#define _GGAIM_XMLNODE_H_ + + +#ifdef __cplusplus +extern "C" +{ +#endif + +/** + * An xmlnode. + */ + typedef struct _xmlnode xmlnode; + +/** + * Gets a child node named name. + * + * @param parent The parent node. + * @param name The child's name. + * + * @return The child or NULL. + */ + xmlnode *xmlnode_get_child (const xmlnode * parent, const char *name); + +/** + * Gets the next node with the same name as node. + * + * @param node The node of a twin to find. + * + * @return The twin of node or NULL. + */ + xmlnode *xmlnode_get_next_twin (xmlnode * node); + +/** + * Gets data from a node. + * + * @param node The node to get data from. + * + * @return The data from the node. You must g_free + * this string when finished using it. + */ + char *xmlnode_get_data (xmlnode * node); + +/** + * Creates a node from a string of XML. Calling this on the + * root node of an XML document will parse the entire document + * into a tree of nodes, and return the xmlnode of the root. + * + * @param str The string of xml. + * @param size The size of the string, or -1 if @a str is + * NUL-terminated. + * + * @return The new node. + */ + xmlnode *xmlnode_from_str (const char *str, int size); + +/** + * Frees a node and all of it's children. + * + * @param node The node to free. + */ + void xmlnode_free (xmlnode * node); + +#ifdef __cplusplus +} +#endif + +#endif /* _GAIM_XMLNODE_H_ */ diff --git a/src/util/Makefile.am b/src/util/Makefile.am new file mode 100644 index 000000000..a90b2b793 --- /dev/null +++ b/src/util/Makefile.am @@ -0,0 +1,308 @@ +INCLUDES = -I$(top_srcdir)/src/include + +plugindir = $(libdir)/gnunet + +if MINGW + WINFLAGS = -Wl,--no-undefined -Wl,--export-all-symbols -lole32 -lshell32 -luuid -liconv -lstdc++ -lcomdlg32 -lgdi32 +endif + +if USE_COVERAGE + AM_CFLAGS = --coverage +endif + +lib_LTLIBRARIES = libgnunetutil.la + +libgnunetutil_la_SOURCES = \ + client.c \ + common_allocation.c \ + common_endian.c \ + common_gettext.c \ + common_logging.c \ + configuration.c \ + container_bloomfilter.c \ + container_meta_data.c \ + container_multihashmap.c \ + crypto_aes.c \ + crypto_crc.c \ + crypto_hash.c \ + crypto_ksk.c \ + crypto_random.c \ + crypto_rsa.c \ + disk.c \ + getopt.c \ + getopt_helpers.c \ + network.c \ + os_installation.c \ + os_load.c \ + os_network.c \ + os_priority.c \ + plugin.c \ + program.c \ + pseudonym.c \ + scheduler.c \ + server.c \ + server_tc.c \ + service.c \ + signal.c \ + strings.c \ + time.c + + +libgnunetutil_la_LIBADD = \ + $(GCLIBADD) \ + $(LIBGCRYPT_LIBS) \ + -lgmp -lltdl -lz -lextractor + +libgnunetutil_la_LDFLAGS = \ + $(GN_LIB_LDFLAGS) $(WINFLAGS) \ + -version-info 4:0:0 + + +plugin_LTLIBRARIES = \ + libgnunet_plugin_test.la + +libgnunet_plugin_test_la_SOURCES = \ + test_plugin_plug.c +libgnunet_plugin_test_la_LDFLAGS = \ + $(GN_PLUGIN_LDFLAGS) + + +check_PROGRAMS = \ + test_client \ + test_common_allocation \ + test_common_endian \ + test_common_logging \ + test_configuration \ + test_container_bloomfilter \ + test_container_meta_data \ + test_container_multihashmap \ + test_crypto_aes \ + test_crypto_aes_weak \ + test_crypto_crc \ + test_crypto_hash \ + test_crypto_ksk \ + test_crypto_random \ + test_crypto_rsa \ + test_disk \ + test_getopt \ + test_network \ + test_network_addressing \ + test_network_receive_cancel \ + test_network_timeout \ + test_network_timeout_no_connect \ + test_network_transmit_cancel \ + test_os_load \ + test_os_network \ + test_os_priority \ + test_plugin \ + test_program \ + test_pseudonym \ + test_scheduler \ + test_scheduler_delay \ + test_server \ + test_server_disconnect \ + test_server_with_client \ + test_service \ + test_strings \ + test_time \ + perf_crypto_hash + +TESTS = $(check_PROGRAMS) + +test_client_SOURCES = \ + test_client.c +test_client_LDADD = \ + $(top_builddir)/src/util/libgnunetutil.la + +test_common_allocation_SOURCES = \ + test_common_allocation.c +test_common_allocation_LDADD = \ + $(top_builddir)/src/util/libgnunetutil.la + +test_common_endian_SOURCES = \ + test_common_endian.c +test_common_endian_LDADD = \ + $(top_builddir)/src/util/libgnunetutil.la + +test_common_logging_SOURCES = \ + test_common_logging.c +test_common_logging_LDADD = \ + $(top_builddir)/src/util/libgnunetutil.la + +test_configuration_SOURCES = \ + test_configuration.c +test_configuration_LDADD = \ + $(top_builddir)/src/util/libgnunetutil.la + +test_container_bloomfilter_SOURCES = \ + test_container_bloomfilter.c +test_container_bloomfilter_LDADD = \ + $(top_builddir)/src/util/libgnunetutil.la + +test_container_meta_data_SOURCES = \ + test_container_meta_data.c +test_container_meta_data_LDADD = \ + $(top_builddir)/src/util/libgnunetutil.la + +test_container_multihashmap_SOURCES = \ + test_container_multihashmap.c +test_container_multihashmap_LDADD = \ + $(top_builddir)/src/util/libgnunetutil.la + +test_crypto_aes_SOURCES = \ + test_crypto_aes.c +test_crypto_aes_LDADD = \ + $(top_builddir)/src/util/libgnunetutil.la + +test_crypto_aes_weak_SOURCES = \ + test_crypto_aes_weak.c +test_crypto_aes_weak_LDADD = \ + $(top_builddir)/src/util/libgnunetutil.la + +test_crypto_crc_SOURCES = \ + test_crypto_crc.c +test_crypto_crc_LDADD = \ + $(top_builddir)/src/util/libgnunetutil.la + +test_crypto_hash_SOURCES = \ + test_crypto_hash.c +test_crypto_hash_LDADD = \ + $(top_builddir)/src/util/libgnunetutil.la + +test_crypto_ksk_SOURCES = \ + test_crypto_ksk.c +test_crypto_ksk_LDADD = \ + $(top_builddir)/src/util/libgnunetutil.la + +test_crypto_random_SOURCES = \ + test_crypto_random.c +test_crypto_random_LDADD = \ + $(top_builddir)/src/util/libgnunetutil.la + +test_crypto_rsa_SOURCES = \ + test_crypto_rsa.c +test_crypto_rsa_LDADD = \ + $(top_builddir)/src/util/libgnunetutil.la + +test_disk_SOURCES = \ + test_disk.c +test_disk_LDADD = \ + $(top_builddir)/src/util/libgnunetutil.la + +test_getopt_SOURCES = \ + test_getopt.c +test_getopt_LDADD = \ + $(top_builddir)/src/util/libgnunetutil.la + +test_network_SOURCES = \ + test_network.c +test_network_LDADD = \ + $(top_builddir)/src/util/libgnunetutil.la + +test_network_addressing_SOURCES = \ + test_network_addressing.c +test_network_addressing_LDADD = \ + $(top_builddir)/src/util/libgnunetutil.la + +test_network_receive_cancel_SOURCES = \ + test_network_receive_cancel.c +test_network_receive_cancel_LDADD = \ + $(top_builddir)/src/util/libgnunetutil.la + +test_network_timeout_SOURCES = \ + test_network_timeout.c +test_network_timeout_LDADD = \ + $(top_builddir)/src/util/libgnunetutil.la + +test_network_timeout_no_connect_SOURCES = \ + test_network_timeout_no_connect.c +test_network_timeout_no_connect_LDADD = \ + $(top_builddir)/src/util/libgnunetutil.la + +test_network_transmit_cancel_SOURCES = \ + test_network_transmit_cancel.c +test_network_transmit_cancel_LDADD = \ + $(top_builddir)/src/util/libgnunetutil.la + +test_os_load_SOURCES = \ + test_os_load.c +test_os_load_LDADD = \ + $(top_builddir)/src/util/libgnunetutil.la + +test_os_network_SOURCES = \ + test_os_network.c +test_os_network_LDADD = \ + $(top_builddir)/src/util/libgnunetutil.la + +test_os_priority_SOURCES = \ + test_os_priority.c +test_os_priority_LDADD = \ + $(top_builddir)/src/util/libgnunetutil.la + +test_plugin_SOURCES = \ + test_plugin.c +test_plugin_LDADD = \ + $(top_builddir)/src/util/libgnunetutil.la + +test_program_SOURCES = \ + test_program.c +test_program_LDADD = \ + $(top_builddir)/src/util/libgnunetutil.la + +test_pseudonym_SOURCES = \ + test_pseudonym.c +test_pseudonym_LDADD = \ + $(top_builddir)/src/util/libgnunetutil.la + +test_scheduler_SOURCES = \ + test_scheduler.c +test_scheduler_LDADD = \ + $(top_builddir)/src/util/libgnunetutil.la + +test_scheduler_delay_SOURCES = \ + test_scheduler_delay.c +test_scheduler_delay_LDADD = \ + $(top_builddir)/src/util/libgnunetutil.la + +test_server_SOURCES = \ + test_server.c +test_server_LDADD = \ + $(top_builddir)/src/util/libgnunetutil.la + +test_server_disconnect_SOURCES = \ + test_server_disconnect.c +test_server_disconnect_LDADD = \ + $(top_builddir)/src/util/libgnunetutil.la + +test_server_with_client_SOURCES = \ + test_server_with_client.c +test_server_with_client_LDADD = \ + $(top_builddir)/src/util/libgnunetutil.la + +test_service_SOURCES = \ + test_service.c +test_service_LDADD = \ + $(top_builddir)/src/util/libgnunetutil.la + +test_strings_SOURCES = \ + test_strings.c +test_strings_LDADD = \ + $(top_builddir)/src/util/libgnunetutil.la + +test_time_SOURCES = \ + test_time.c +test_time_LDADD = \ + $(top_builddir)/src/util/libgnunetutil.la + +perf_crypto_hash_SOURCES = \ + perf_crypto_hash.c +perf_crypto_hash_LDADD = \ + $(top_builddir)/src/util/libgnunetutil.la + + +EXTRA_DIST = \ + test_configuration_data.conf \ + test_container_meta_data_image.jpg \ + test_program_data.conf \ + test_pseudonym_data.conf \ + test_service_data.conf diff --git a/src/util/client.c b/src/util/client.c new file mode 100644 index 000000000..9dd70f266 --- /dev/null +++ b/src/util/client.c @@ -0,0 +1,526 @@ +/* + This file is part of GNUnet. + (C) 2001, 2002, 2006, 2008, 2009 Christian Grothoff (and other contributing authors) + + GNUnet is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published + by the Free Software Foundation; either version 2, 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 + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with GNUnet; see the file COPYING. If not, write to the + Free Software Foundation, Inc., 59 Temple Place - Suite 330, + Boston, MA 02111-1307, USA. +*/ + +/** + * @file util/client.c + * @brief code for access to services + * @author Christian Grothoff + * + * Generic TCP code for reliable, record-oriented TCP + * connections between clients and service providers. + */ + +#include "platform.h" +#include "gnunet_common.h" +#include "gnunet_client_lib.h" +#include "gnunet_protocols.h" +#include "gnunet_server_lib.h" +#include "gnunet_scheduler_lib.h" + +#define DEBUG_CLIENT GNUNET_NO + +/** + * Struct to refer to a GNUnet TCP connection. + * This is more than just a socket because if the server + * drops the connection, the client automatically tries + * to reconnect (and for that needs connection information). + */ +struct GNUNET_CLIENT_Connection +{ + + /** + * the socket handle, NULL if not live + */ + struct GNUNET_NETWORK_SocketHandle *sock; + + /** + * Our scheduler. + */ + struct GNUNET_SCHEDULER_Handle *sched; + + /** + * Name of the service we interact with. + */ + char *service_name; + + /** + * ID of task used for receiving. + */ + GNUNET_SCHEDULER_TaskIdentifier receiver_task; + + /** + * Handler for current receiver task. + */ + GNUNET_CLIENT_MessageHandler receiver_handler; + + /** + * Closure for receiver_handler. + */ + void *receiver_handler_cls; + + /** + * Handler for service test completion (NULL unless in service_test) + */ + GNUNET_SCHEDULER_Task test_cb; + + /** + * Closure for test_cb (NULL unless in service_test) + */ + void *test_cb_cls; + + /** + * Buffer for received message. + */ + char *received_buf; + + /** + * Timeout for receiving a response (absolute time). + */ + struct GNUNET_TIME_Absolute receive_timeout; + + /** + * Number of bytes in received_buf that are valid. + */ + size_t received_pos; + + /** + * Size of received_buf. + */ + size_t received_size; + + /** + * Do we have a complete response in received_buf? + */ + int msg_complete; + +}; + + +/** + * Get a connection with a service. + * + * @param sched scheduler to use + * @param service_name name of the service + * @param cfg configuration to use + * @return NULL on error (service unknown to configuration) + */ +struct GNUNET_CLIENT_Connection * +GNUNET_CLIENT_connect (struct GNUNET_SCHEDULER_Handle *sched, + const char *service_name, + struct GNUNET_CONFIGURATION_Handle *cfg) +{ + struct GNUNET_CLIENT_Connection *ret; + struct GNUNET_NETWORK_SocketHandle *sock; + char *hostname; + unsigned long long port; + + if ((GNUNET_OK != + GNUNET_CONFIGURATION_get_value_number (cfg, + service_name, + "PORT", + &port)) || + (port > 65535) || + (GNUNET_OK != + GNUNET_CONFIGURATION_get_value_string (cfg, + service_name, + "HOSTNAME", &hostname))) + { + GNUNET_log (GNUNET_ERROR_TYPE_WARNING, + "Could not determine valid hostname and port for service `%s' from configuration.\n", + service_name); + return NULL; + } + sock = GNUNET_NETWORK_socket_create_from_connect (sched, + hostname, + port, + GNUNET_SERVER_MAX_MESSAGE_SIZE); + GNUNET_free (hostname); + if (sock == NULL) + return NULL; + ret = GNUNET_malloc (sizeof (struct GNUNET_CLIENT_Connection)); + ret->sock = sock; + ret->sched = sched; + ret->service_name = GNUNET_strdup (service_name); + return ret; +} + + +/** + * Receiver task has completed, free rest of client + * data structures. + */ +static void +finish_cleanup (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc) +{ + struct GNUNET_CLIENT_Connection *sock = cls; + + GNUNET_array_grow (sock->received_buf, sock->received_size, 0); + GNUNET_free (sock->service_name); + GNUNET_free (sock); +} + + +/** + * Destroy connection with the service. + */ +void +GNUNET_CLIENT_disconnect (struct GNUNET_CLIENT_Connection *sock) +{ + GNUNET_assert (sock->sock != NULL); + GNUNET_NETWORK_socket_destroy (sock->sock); + sock->sock = NULL; + sock->receiver_handler = NULL; + GNUNET_SCHEDULER_add_after (sock->sched, + GNUNET_YES, + GNUNET_SCHEDULER_PRIORITY_KEEP, + sock->receiver_task, &finish_cleanup, sock); +} + + +/** + * check if message is complete + */ +static void +check_complete (struct GNUNET_CLIENT_Connection *conn) +{ + if ((conn->received_pos >= sizeof (struct GNUNET_MessageHeader)) && + (conn->received_pos >= + ntohs (((const struct GNUNET_MessageHeader *) conn->received_buf)-> + size))) + conn->msg_complete = GNUNET_YES; +} + + +/** + * Callback function for data received from the network. Note that + * both "available" and "err" would be 0 if the read simply timed out. + * + * @param cls closure + * @param buf pointer to received data + * @param available number of bytes availabe in "buf", + * possibly 0 (on errors) + * @param addr address of the sender + * @param addrlen size of addr + * @param errCode value of errno (on errors receiving) + */ +static void +receive_helper (void *cls, + const void *buf, + size_t available, + const struct sockaddr *addr, socklen_t addrlen, int errCode) +{ + struct GNUNET_CLIENT_Connection *conn = cls; + struct GNUNET_TIME_Relative remaining; + + GNUNET_assert (conn->msg_complete == GNUNET_NO); + conn->receiver_task = GNUNET_SCHEDULER_NO_PREREQUISITE_TASK; + + if ((available == 0) || (conn->sock == NULL) || (errCode != 0)) + { + /* signal timeout! */ + if (conn->receiver_handler != NULL) + { + conn->receiver_handler (conn->receiver_handler_cls, NULL); + conn->receiver_handler = NULL; + } + return; + } + + /* FIXME: optimize for common fast case where buf contains the + entire message and we need no copying... */ + + + /* slow path: append to array */ + if (conn->received_size < conn->received_pos + available) + GNUNET_array_grow (conn->received_buf, + conn->received_size, conn->received_pos + available); + memcpy (&conn->received_buf[conn->received_pos], buf, available); + conn->received_pos += available; + check_complete (conn); + /* check for timeout */ + remaining = GNUNET_TIME_absolute_get_remaining (conn->receive_timeout); + if (remaining.value == 0) + { + /* signal timeout! */ + conn->receiver_handler (conn->receiver_handler_cls, NULL); + return; + } + /* back to receive -- either for more data or to call callback! */ + GNUNET_CLIENT_receive (conn, + conn->receiver_handler, + conn->receiver_handler_cls, remaining); +} + + +/** + * Continuation to call the receive callback. + */ +static void +receive_task (void *scls, const struct GNUNET_SCHEDULER_TaskContext *tc) +{ + struct GNUNET_CLIENT_Connection *sock = scls; + const struct GNUNET_MessageHeader *cmsg; + struct GNUNET_MessageHeader *msg; + GNUNET_CLIENT_MessageHandler handler = sock->receiver_handler; + void *cls = sock->receiver_handler_cls; + uint16_t msize; + + GNUNET_assert (GNUNET_YES == sock->msg_complete); + sock->receiver_task = GNUNET_SCHEDULER_NO_PREREQUISITE_TASK; + cmsg = (const struct GNUNET_MessageHeader *) sock->received_buf; + msize = ntohs (cmsg->size); + GNUNET_assert (sock->received_pos >= msize); + msg = GNUNET_malloc (msize); + memcpy (msg, cmsg, msize); + memmove (sock->received_buf, + &sock->received_buf[msize], sock->received_pos - msize); + sock->received_pos -= msize; + sock->msg_complete = GNUNET_NO; + sock->receiver_handler = NULL; + check_complete (sock); + handler (cls, msg); + GNUNET_free (msg); +} + + +/** + * Read from the service. + * + * @param sched scheduler to use + * @param sock the service + * @param handler function to call with the message + * @param cls closure for handler + * @param timeout how long to wait until timing out + */ +void +GNUNET_CLIENT_receive (struct GNUNET_CLIENT_Connection *sock, + GNUNET_CLIENT_MessageHandler handler, + void *cls, struct GNUNET_TIME_Relative timeout) +{ + if (sock->sock == NULL) + { + /* already disconnected, fail instantly! */ + GNUNET_break (0); /* this should not happen in well-written code! */ + handler (cls, NULL); + return; + } + GNUNET_assert (sock->receiver_task == + GNUNET_SCHEDULER_NO_PREREQUISITE_TASK); + sock->receiver_handler = handler; + sock->receiver_handler_cls = cls; + sock->receive_timeout = GNUNET_TIME_relative_to_absolute (timeout); + if (GNUNET_YES == sock->msg_complete) + sock->receiver_task = GNUNET_SCHEDULER_add_after (sock->sched, + GNUNET_YES, + GNUNET_SCHEDULER_PRIORITY_KEEP, + GNUNET_SCHEDULER_NO_PREREQUISITE_TASK, + &receive_task, sock); + else + sock->receiver_task = GNUNET_NETWORK_receive (sock->sock, + GNUNET_SERVER_MAX_MESSAGE_SIZE, + timeout, + &receive_helper, sock); +} + + +static size_t +write_shutdown (void *cls, size_t size, void *buf) +{ + struct GNUNET_MessageHeader *msg; + + if (size < sizeof (struct GNUNET_MessageHeader)) + return 0; /* client disconnected */ + msg = (struct GNUNET_MessageHeader *) buf; + msg->type = htons (GNUNET_MESSAGE_TYPE_SHUTDOWN); + msg->size = htons (sizeof (struct GNUNET_MessageHeader)); + return sizeof (struct GNUNET_MessageHeader); +} + + +/** + * Request that the service should shutdown. + * Afterwards, the connection should be disconnected. + * + * @param sched scheduler to use + * @param sock the socket connected to the service + */ +void +GNUNET_CLIENT_service_shutdown (struct GNUNET_CLIENT_Connection *sock) +{ + GNUNET_NETWORK_notify_transmit_ready (sock->sock, + sizeof (struct GNUNET_MessageHeader), + GNUNET_TIME_UNIT_FOREVER_REL, + &write_shutdown, NULL); +} + + +/** + * Report service unavailable. + */ +static void +service_test_error (struct GNUNET_SCHEDULER_Handle *s, + GNUNET_SCHEDULER_Task task, void *task_cls) +{ + GNUNET_SCHEDULER_add_continuation (s, + GNUNET_YES, + task, + task_cls, + GNUNET_SCHEDULER_REASON_TIMEOUT); +} + + +/** + * Receive confirmation from test, service is up. + * + * @param cls closure + * @param msg message received, NULL on timeout or fatal error + */ +static void +confirm_handler (void *cls, const struct GNUNET_MessageHeader *msg) +{ + struct GNUNET_CLIENT_Connection *conn = cls; + /* We may want to consider looking at the reply in more + detail in the future, for example, is this the + correct service? FIXME! */ + if (msg != NULL) + { +#if DEBUG_CLIENT + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Received confirmation that service is running.\n"); +#endif + GNUNET_SCHEDULER_add_continuation (conn->sched, + GNUNET_YES, + conn->test_cb, + conn->test_cb_cls, + GNUNET_SCHEDULER_REASON_PREREQ_DONE); + } + else + { + service_test_error (conn->sched, conn->test_cb, conn->test_cb_cls); + } + GNUNET_CLIENT_disconnect (conn); +} + + +static size_t +write_test (void *cls, size_t size, void *buf) +{ + struct GNUNET_MessageHeader *msg; + + if (size < sizeof (struct GNUNET_MessageHeader)) + { +#if DEBUG_CLIENT + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + _("Failure to transmit TEST request.\n")); +#endif + return 0; /* client disconnected */ + } +#if DEBUG_CLIENT + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, _("Transmitting TEST request.\n")); +#endif + msg = (struct GNUNET_MessageHeader *) buf; + msg->type = htons (GNUNET_MESSAGE_TYPE_TEST); + msg->size = htons (sizeof (struct GNUNET_MessageHeader)); + return sizeof (struct GNUNET_MessageHeader); +} + + +/** + * Wait until the service is running. + * + * @param sched scheduler to use + * @param service name of the service to wait for + * @param cfg configuration to use + * @param timeout how long to wait at most in ms + * @param task task to run if service is running + * (reason will be "PREREQ_DONE" (service running) + * or "TIMEOUT" (service not known to be running)) + * @param task_cls closure for task + */ +void +GNUNET_CLIENT_service_test (struct GNUNET_SCHEDULER_Handle *sched, + const char *service, + struct GNUNET_CONFIGURATION_Handle *cfg, + struct GNUNET_TIME_Relative timeout, + GNUNET_SCHEDULER_Task task, void *task_cls) +{ + struct GNUNET_CLIENT_Connection *conn; + +#if DEBUG_CLIENT + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Testing if service `%s' is running.\n", service); +#endif + conn = GNUNET_CLIENT_connect (sched, service, cfg); + if (conn == NULL) + { + GNUNET_log (GNUNET_ERROR_TYPE_INFO, + _ + ("Could not connect to service `%s', must not be running.\n"), + service); + service_test_error (sched, task, task_cls); + return; + } + conn->test_cb = task; + conn->test_cb_cls = task_cls; + if (NULL == + GNUNET_NETWORK_notify_transmit_ready (conn->sock, + sizeof (struct + GNUNET_MessageHeader), + timeout, &write_test, NULL)) + { + GNUNET_log (GNUNET_ERROR_TYPE_WARNING, + _("Failure to transmit request to service `%s'\n"), + service); + service_test_error (sched, task, task_cls); + GNUNET_CLIENT_disconnect (conn); + return; + } + GNUNET_CLIENT_receive (conn, &confirm_handler, conn, timeout); +} + + +/** + * Ask the client to call us once the specified number of bytes + * are free in the transmission buffer. May call the notify + * method immediately if enough space is available. + * + * @param client connection to the service + * @param size number of bytes to send + * @param timeout after how long should we give up (and call + * notify with buf NULL and size 0)? + * @param notify function to call + * @param notify_cls closure for notify + * @return NULL if our buffer will never hold size bytes, + * a handle if the notify callback was queued (can be used to cancel) + */ +struct GNUNET_NETWORK_TransmitHandle * +GNUNET_CLIENT_notify_transmit_ready (struct GNUNET_CLIENT_Connection *sock, + size_t size, + struct GNUNET_TIME_Relative timeout, + GNUNET_NETWORK_TransmitReadyNotify + notify, void *notify_cls) +{ + return GNUNET_NETWORK_notify_transmit_ready (sock->sock, + size, + timeout, notify, notify_cls); +} + + +/* end of client.c */ diff --git a/src/util/common_allocation.c b/src/util/common_allocation.c new file mode 100644 index 000000000..9fabb3a32 --- /dev/null +++ b/src/util/common_allocation.c @@ -0,0 +1,206 @@ +/* + This file is part of GNUnet. + (C) 2001, 2002, 2003, 2005, 2006 Christian Grothoff (and other contributing authors) + + GNUnet is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published + by the Free Software Foundation; either version 2, 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 + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with GNUnet; see the file COPYING. If not, write to the + Free Software Foundation, Inc., 59 Temple Place - Suite 330, + Boston, MA 02111-1307, USA. +*/ + +/** + * @file util/memory/common_allocation.c + * @brief wrapper around malloc/free + * @author Christian Grothoff + */ + +#include "platform.h" +#include "gnunet_common.h" + +#ifndef INT_MAX +#define INT_MAX 0x7FFFFFFF +#endif + +/** + * Allocate memory. Checks the return value, aborts if no more + * memory is available. + * + * @param size how many bytes of memory to allocate, do NOT use + * this function (or GNUNET_malloc) to allocate more than several MB + * of memory, if you are possibly needing a very large chunk use + * GNUNET_xmalloc_unchecked_ instead. + * @param filename where in the code was the call to GNUNET_array_grow + * @param linenumber where in the code was the call to GNUNET_array_grow + * @return pointer to size bytes of memory + */ +void * +GNUNET_xmalloc_ (size_t size, const char *filename, int linenumber) +{ + /* As a security precaution, we generally do not allow very large + allocations using the default 'GNUNET_malloc' macro */ + GNUNET_assert_at (size <= GNUNET_MAX_GNUNET_MALLOC_CHECKED, filename, + linenumber); + return GNUNET_xmalloc_unchecked_ (size, filename, linenumber); +} + +void * +GNUNET_xmalloc_unchecked_ (size_t size, const char *filename, int linenumber) +{ + void *result; + + GNUNET_assert_at (size < INT_MAX, filename, linenumber); + result = malloc (size); + if (result == NULL) + { + GNUNET_log_strerror (GNUNET_ERROR_TYPE_ERROR, "malloc"); + abort (); + } + memset (result, 0, size); + return result; +} + +/** + * Reallocate memory. Checks the return value, aborts if no more + * memory is available. + * + * @ptr the pointer to reallocate + * @param size how many bytes of memory to allocate, do NOT use + * this function (or GNUNET_malloc) to allocate more than several MB + * of memory + * @param filename where in the code was the call to GNUNET_realloc + * @param linenumber where in the code was the call to GNUNET_realloc + * @return pointer to size bytes of memory + */ +void * +GNUNET_xrealloc_ (void *ptr, + const size_t n, const char *filename, int linenumber) +{ + ptr = realloc (ptr, n); + if (!ptr) + { + GNUNET_log_strerror (GNUNET_ERROR_TYPE_ERROR, "realloc"); + abort (); + } + return ptr; +} + +/** + * Free memory. Merely a wrapper for the case that we + * want to keep track of allocations. + * + * @param ptr the pointer to free + * @param filename where in the code was the call to GNUNET_array_grow + * @param linenumber where in the code was the call to GNUNET_array_grow + */ +void +GNUNET_xfree_ (void *ptr, const char *filename, int linenumber) +{ + GNUNET_assert_at (ptr != NULL, filename, linenumber); + free (ptr); +} + +/** + * Dup a string (same semantics as strdup). + * + * @param str the string to dup + * @param filename where in the code was the call to GNUNET_array_grow + * @param linenumber where in the code was the call to GNUNET_array_grow + * @return strdup(str) + */ +char * +GNUNET_xstrdup_ (const char *str, const char *filename, int linenumber) +{ + char *res; + + GNUNET_assert_at (str != NULL, filename, linenumber); + res = GNUNET_xmalloc_ (strlen (str) + 1, filename, linenumber); + memcpy (res, str, strlen (str) + 1); + return res; +} + +/** + * Grow an array. Grows old by (*oldCount-newCount)*elementSize bytes + * and sets *oldCount to newCount. + * + * @param old address of the pointer to the array + * *old may be NULL + * @param elementSize the size of the elements of the array + * @param oldCount address of the number of elements in the *old array + * @param newCount number of elements in the new array, may be 0 + * @param filename where in the code was the call to GNUNET_array_grow + * @param linenumber where in the code was the call to GNUNET_array_grow + */ +void +GNUNET_xgrow_ (void **old, + size_t elementSize, + unsigned int *oldCount, + unsigned int newCount, const char *filename, int linenumber) +{ + void *tmp; + size_t size; + + GNUNET_assert_at (INT_MAX / elementSize > newCount, filename, linenumber); + size = newCount * elementSize; + if (size == 0) + { + tmp = NULL; + } + else + { + tmp = GNUNET_xmalloc_ (size, filename, linenumber); + memset (tmp, 0, size); /* client code should not rely on this, though... */ + if (*oldCount > newCount) + *oldCount = newCount; /* shrink is also allowed! */ + memcpy (tmp, *old, elementSize * (*oldCount)); + } + + if (*old != NULL) + { + GNUNET_xfree_ (*old, filename, linenumber); + } + *old = tmp; + *oldCount = newCount; +} + + +int +GNUNET_asprintf (char **buf, const char *format, ...) +{ + int ret; + va_list args; + + va_start (args, format); + ret = VSNPRINTF (NULL, 0, format, args); + va_end (args); + *buf = GNUNET_malloc (ret + 1); + va_start (args, format); + ret = VSPRINTF (*buf, format, args); + va_end (args); + return ret; +} + +int +GNUNET_snprintf (char *buf, size_t size, const char *format, ...) +{ + int ret; + va_list args; + + va_start (args, format); + ret = VSNPRINTF (buf, size, format, args); + va_end (args); + GNUNET_assert (ret <= size); + return ret; +} + + +/* end of common_allocation.c */ diff --git a/src/util/common_endian.c b/src/util/common_endian.c new file mode 100644 index 000000000..223144c40 --- /dev/null +++ b/src/util/common_endian.c @@ -0,0 +1,52 @@ +/* + This file is part of GNUnet. + (C) 2001, 2002, 2003, 2004, 2006 Christian Grothoff (and other contributing authors) + + GNUnet is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published + by the Free Software Foundation; either version 2, 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 + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with GNUnet; see the file COPYING. If not, write to the + Free Software Foundation, Inc., 59 Temple Place - Suite 330, + Boston, MA 02111-1307, USA. +*/ + +/** + * @file util/common_endian.c + * @brief endian conversion helpers + * @author Christian Grothoff + */ + +#include "platform.h" +#include "gnunet_common.h" + +unsigned long long +GNUNET_ntohll (unsigned long long n) +{ +#if __BYTE_ORDER == __BIG_ENDIAN + return n; +#else + return (((unsigned long long) ntohl (n)) << 32) + ntohl (n >> 32); +#endif +} + +unsigned long long +GNUNET_htonll (unsigned long long n) +{ +#if __BYTE_ORDER == __BIG_ENDIAN + return n; +#else + return (((unsigned long long) htonl (n)) << 32) + htonl (n >> 32); +#endif +} + + + +/* end of common_endian.c */ diff --git a/src/util/common_gettext.c b/src/util/common_gettext.c new file mode 100644 index 000000000..ad347e160 --- /dev/null +++ b/src/util/common_gettext.c @@ -0,0 +1,42 @@ +/* + This file is part of GNUnet. + (C) 2008 Christian Grothoff (and other contributing authors) + + GNUnet is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published + by the Free Software Foundation; either version 2, 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 + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with GNUnet; see the file COPYING. If not, write to the + Free Software Foundation, Inc., 59 Temple Place - Suite 330, + Boston, MA 02111-1307, USA. +*/ + +/** + * @file util/common_gettext.c + * @brief gettext init routine + * @author Heikki Lindholm + */ +#include "platform.h" +#include "gnunet_common.h" +#include "gnunet_os_lib.h" + +void __attribute__ ((constructor)) GNUNET_util_generic_ltdl_init () +{ +#if ENABLE_NLS + char *path; + + path = GNUNET_OS_installation_get_path (GNUNET_OS_IPK_LOCALEDIR); + if (path != NULL) + { + BINDTEXTDOMAIN ("GNUnet", path); + GNUNET_free (path); + } +#endif +} diff --git a/src/util/common_logging.c b/src/util/common_logging.c new file mode 100644 index 000000000..4068ed94b --- /dev/null +++ b/src/util/common_logging.c @@ -0,0 +1,401 @@ +/* + This file is part of GNUnet. + (C) 2006, 2008, 2009 Christian Grothoff (and other contributing authors) + + GNUnet is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published + by the Free Software Foundation; either version 2, 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 + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with GNUnet; see the file COPYING. If not, write to the + Free Software Foundation, Inc., 59 Temple Place - Suite 330, + Boston, MA 02111-1307, USA. +*/ + +/** + * @file util/common_logging.c + * @brief error handling API + * + * @author Christian Grothoff + */ +#include "platform.h" +#include "gnunet_common.h" +#include "gnunet_crypto_lib.h" +#include "gnunet_strings_lib.h" +#include "gnunet_time_lib.h" + +/** + * After how many seconds do we always print + * that "message X was repeated N times"? Use 12h. + */ +#define BULK_DELAY_THRESHOLD (12 * 60 * 60 * 1000) + +/** + * After how many repetitions do we always print + * that "message X was repeated N times"? (even if + * we have not yet reached the delay threshold) + */ +#define BULK_REPEAT_THRESHOLD 1000 + +/** + * How many characters do we use for matching of + * bulk messages? + */ +#define BULK_TRACK_SIZE 256 + +/** + * How many characters can a date/time string + * be at most? + */ +#define DATE_STR_SIZE 64 + +/** + * Linked list of active loggers. + */ +struct CustomLogger +{ + /** + * This is a linked list. + */ + struct CustomLogger *next; + + /** + * Log function. + */ + GNUNET_Logger logger; + + /** + * Closure for logger. + */ + void *logger_cls; +}; + +/** + * The last "bulk" error message that we have been logging. + * Note that this message maybe truncated to the first BULK_TRACK_SIZE + * characters, in which case it is NOT 0-terminated! + */ +static char last_bulk[BULK_TRACK_SIZE]; + +/** + * Type of the last bulk message. + */ +static enum GNUNET_ErrorType last_bulk_kind; + +/** + * Time of the last bulk error message (0 for none) + */ +static struct GNUNET_TIME_Absolute last_bulk_time; + +/** + * Number of times that bulk message has been repeated since. + */ +static unsigned int last_bulk_repeat; + +/** + * Component when the last bulk was logged. + */ +static const char *last_bulk_comp; + +/** + * Running component. + */ +static const char *component; + +/** + * Minimum log level. + */ +static enum GNUNET_ErrorType min_level; + +/** + * Linked list of our custom loggres. + */ +static struct CustomLogger *loggers; + +/** + * Number of log calls to ignore. + */ +static unsigned int skip_log; + +/** + * Convert a textual description of a loglevel + * to the respective GNUNET_GE_KIND. + * @returns GNUNET_GE_INVALID if log does not parse + */ +static enum GNUNET_ErrorType +get_type (const char *log) +{ + if (0 == strcasecmp (log, _("DEBUG"))) + return GNUNET_ERROR_TYPE_DEBUG; + if (0 == strcasecmp (log, _("INFO"))) + return GNUNET_ERROR_TYPE_INFO; + if (0 == strcasecmp (log, _("WARNING"))) + return GNUNET_ERROR_TYPE_WARNING; + if (0 == strcasecmp (log, _("ERROR"))) + return GNUNET_ERROR_TYPE_ERROR; + return GNUNET_ERROR_TYPE_INVALID; +} + +/** + * Setup logging. + * + * @param comp default component to use + * @param loglevel what types of messages should be logged + */ +int +GNUNET_log_setup (const char *comp, const char *loglevel, const char *logfile) +{ + FILE *altlog; + + component = comp; + min_level = get_type (loglevel); + if (logfile == NULL) + return GNUNET_OK; + altlog = fopen (logfile, "a"); + if (altlog == NULL) + { + GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_ERROR, "fopen", logfile); + return GNUNET_SYSERR; + } + if (stderr != NULL) + fclose (stderr); + stderr = altlog; + return GNUNET_OK; +} + +/** + * Add a custom logger. + * + * @param logger log function + * @param logger_cls closure for logger + */ +void +GNUNET_logger_add (GNUNET_Logger logger, void *logger_cls) +{ + struct CustomLogger *entry; + + entry = GNUNET_malloc (sizeof (struct CustomLogger)); + entry->logger = logger; + entry->logger_cls = logger_cls; + entry->next = loggers; + loggers = entry; +} + +/** + * Remove a custom logger. + * + * @param logger log function + * @param logger_cls closure for logger + */ +void +GNUNET_logger_remove (GNUNET_Logger logger, void *logger_cls) +{ + struct CustomLogger *pos; + struct CustomLogger *prev; + + prev = NULL; + pos = loggers; + while ((pos != NULL) && + ((pos->logger != logger) || (pos->logger_cls != logger_cls))) + { + prev = pos; + pos = pos->next; + } + GNUNET_assert (pos != NULL); + if (prev == NULL) + loggers = pos->next; + else + prev->next = pos->next; + GNUNET_free (pos); +} + +static void +output_message (enum GNUNET_ErrorType kind, + const char *comp, const char *datestr, const char *msg) +{ + struct CustomLogger *pos; + if (stderr != NULL) + fprintf (stderr, "%s %s %s %s", datestr, comp, + GNUNET_error_type_to_string (kind), msg); + pos = loggers; + while (pos != NULL) + { + pos->logger (pos->logger_cls, kind, comp, datestr, msg); + pos = pos->next; + } +} + +static void +flush_bulk (const char *datestr) +{ + char msg[DATE_STR_SIZE + BULK_TRACK_SIZE + 256]; + int rev; + char *last; + char *ft; + + if ((last_bulk_time.value == 0) || (last_bulk_repeat == 0)) + return; + rev = 0; + last = memchr (last_bulk, '\0', BULK_TRACK_SIZE); + if (last == NULL) + last = &last_bulk[BULK_TRACK_SIZE - 1]; + else if (last != last_bulk) + last--; + if (last[0] == '\n') + { + rev = 1; + last[0] = '\0'; + } + ft = + GNUNET_STRINGS_relative_time_to_string (GNUNET_TIME_absolute_get_duration + (last_bulk_time)); + snprintf (msg, sizeof (msg), + _("Message `%.*s' repeated %u times in the last %s\n"), + BULK_TRACK_SIZE, last_bulk, last_bulk_repeat, ft); + GNUNET_free (ft); + if (rev == 1) + last[0] = '\n'; + output_message (last_bulk_kind, last_bulk_comp, datestr, msg); + last_bulk_time = GNUNET_TIME_absolute_get (); + last_bulk_repeat = 0; +} + + +/** + * Ignore the next n calls to the log function. + * + * @param n number of log calls to ignore + */ +void +GNUNET_log_skip (unsigned int n) +{ + int ok; + + if (n == 0) + { + ok = (0 == skip_log); + skip_log = 0; + GNUNET_assert (ok); + } + skip_log += n; +} + + +static void +mylog (enum GNUNET_ErrorType kind, + const char *comp, const char *message, va_list va) +{ + char date[DATE_STR_SIZE]; + time_t timetmp; + struct tm *tmptr; + size_t size; + char *buf; + va_list vacp; + + if (skip_log > 0) + { + skip_log--; + return; + } + if ((kind & (~GNUNET_ERROR_TYPE_BULK)) > min_level) + return; + va_copy (vacp, va); + size = VSNPRINTF (NULL, 0, message, vacp) + 1; + va_end (vacp); + buf = malloc (size); + if (buf == NULL) + return; /* oops */ + VSNPRINTF (buf, size, message, va); + time (&timetmp); + memset (date, 0, DATE_STR_SIZE); + tmptr = localtime (&timetmp); + strftime (date, DATE_STR_SIZE, "%b %d %H:%M:%S", tmptr); + if ((0 != (kind & GNUNET_ERROR_TYPE_BULK)) && + (last_bulk_time.value != 0) && + (0 == strncmp (buf, last_bulk, sizeof (last_bulk)))) + { + last_bulk_repeat++; + if ((GNUNET_TIME_absolute_get_duration (last_bulk_time).value > + BULK_DELAY_THRESHOLD) + || (last_bulk_repeat > BULK_REPEAT_THRESHOLD)) + flush_bulk (date); + free (buf); + return; + } + flush_bulk (date); + strncpy (last_bulk, buf, sizeof (last_bulk)); + last_bulk_repeat = 0; + last_bulk_kind = kind; + last_bulk_time = GNUNET_TIME_absolute_get (); + last_bulk_comp = comp; + output_message (kind, comp, date, buf); + free (buf); +} + + +void +GNUNET_log (enum GNUNET_ErrorType kind, const char *message, ...) +{ + va_list va; + va_start (va, message); + mylog (kind, component, message, va); + va_end (va); +} + + +void +GNUNET_log_from (enum GNUNET_ErrorType kind, + const char *comp, const char *message, ...) +{ + va_list va; + va_start (va, message); + mylog (kind, comp, message, va); + va_end (va); +} + + +/** + * Convert KIND to String + */ +const char * +GNUNET_error_type_to_string (enum GNUNET_ErrorType kind) +{ + if ((kind & GNUNET_ERROR_TYPE_ERROR) > 0) + return _("ERROR"); + if ((kind & GNUNET_ERROR_TYPE_WARNING) > 0) + return _("WARNING"); + if ((kind & GNUNET_ERROR_TYPE_INFO) > 0) + return _("INFO"); + if ((kind & GNUNET_ERROR_TYPE_DEBUG) > 0) + return _("DEBUG"); + return _("INVALID"); +} + + +/** + * Convert a peer identity to a string (for printing debug messages). + * This is one of the very few calls in the entire API that is + * NOT reentrant! + * + * @param pid the peer identity + * @return string form of the pid; will be overwritten by next + * call to GNUNET_i2s. + */ +const char * +GNUNET_i2s (const struct GNUNET_PeerIdentity *pid) +{ + static struct GNUNET_CRYPTO_HashAsciiEncoded ret; + GNUNET_CRYPTO_hash_to_enc (&pid->hashPubKey, &ret); + ret.encoding[4] = '\0'; + return (const char *) ret.encoding; +} + + + +/* end of common_logging.c */ diff --git a/src/util/configuration.c b/src/util/configuration.c new file mode 100644 index 000000000..4413b0377 --- /dev/null +++ b/src/util/configuration.c @@ -0,0 +1,848 @@ +/* + This file is part of GNUnet. + (C) 2006, 2007, 2008, 2009 Christian Grothoff (and other contributing authors) + + GNUnet is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published + by the Free Software Foundation; either version 2, 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 + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with GNUnet; see the file COPYING. If not, write to the + Free Software Foundation, Inc., 59 Temple Place - Suite 330, + Boston, MA 02111-1307, USA. +*/ + +/** + * @file src/util/configuration.c + * @brief configuration management + * + * @author Christian Grothoff + */ + +#include "platform.h" +#include "gnunet_common.h" +#include "gnunet_configuration_lib.h" +#include "gnunet_crypto_lib.h" +#include "gnunet_disk_lib.h" +#include "gnunet_os_lib.h" +#include "gnunet_strings_lib.h" + +/** + * @brief configuration entry + */ +struct ConfigEntry +{ + + /** + * This is a linked list. + */ + struct ConfigEntry *next; + + /** + * key for this entry + */ + char *key; + + /** + * current, commited value + */ + char *val; +}; + +/** + * @brief configuration section + */ +struct ConfigSection +{ + /** + * This is a linked list. + */ + struct ConfigSection *next; + + /** + * entries in the section + */ + struct ConfigEntry *entries; + + /** + * name of the section + */ + char *name; +}; + +/** + * @brief configuration data + */ +struct GNUNET_CONFIGURATION_Handle +{ + /** + * Configuration sections. + */ + struct ConfigSection *sections; + + /** + * Modification indication since last save + * GNUNET_NO if clean, GNUNET_YES if dirty, + * GNUNET_SYSERR on error (i.e. last save failed) + */ + int dirty; + +}; + +/** + * Create a GNUNET_CONFIGURATION_Configuration. + */ +struct GNUNET_CONFIGURATION_Handle * +GNUNET_CONFIGURATION_create () +{ + return GNUNET_malloc (sizeof (struct GNUNET_CONFIGURATION_Handle)); +} + +void +GNUNET_CONFIGURATION_destroy (struct GNUNET_CONFIGURATION_Handle *cfg) +{ + struct ConfigSection *sec; + struct ConfigEntry *ent; + + while (NULL != (sec = cfg->sections)) + { + cfg->sections = sec->next; + while (NULL != (ent = sec->entries)) + { + sec->entries = ent->next; + GNUNET_free (ent->key); + GNUNET_free_non_null (ent->val); + GNUNET_free (ent); + } + GNUNET_free (sec->name); + GNUNET_free (sec); + } + GNUNET_free (cfg); +} + +int +GNUNET_CONFIGURATION_parse (struct GNUNET_CONFIGURATION_Handle *cfg, + const char *filename) +{ + int dirty; + char line[256]; + char tag[64]; + char value[192]; + FILE *fp; + unsigned int nr; + int i; + int emptyline; + int ret; + char *section; + char *fn; + + fn = GNUNET_STRINGS_filename_expand (filename); + dirty = cfg->dirty; /* back up value! */ + if (NULL == (fp = FOPEN (fn, "r"))) + { + GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING, "fopen", fn); + GNUNET_free (fn); + return GNUNET_SYSERR; + } + GNUNET_free (fn); + ret = GNUNET_OK; + section = GNUNET_strdup (""); + memset (line, 0, 256); + nr = 0; + while (NULL != fgets (line, 255, fp)) + { + nr++; + for (i = 0; i < 255; i++) + if (line[i] == '\t') + line[i] = ' '; + if (line[0] == '\n' || line[0] == '#' || line[0] == '%' || + line[0] == '\r') + continue; + emptyline = 1; + for (i = 0; (i < 255 && line[i] != 0); i++) + if (line[i] != ' ' && line[i] != '\n' && line[i] != '\r') + emptyline = 0; + if (emptyline == 1) + continue; + /* remove tailing whitespace */ + for (i = strlen (line) - 1; (i >= 0) && (isspace (line[i])); i--) + line[i] = '\0'; + if (1 == sscanf (line, "@INLINE@ %191[^\n]", value)) + { + /* @INLINE@ value */ + if (0 != GNUNET_CONFIGURATION_parse (cfg, value)) + ret = GNUNET_SYSERR; /* failed to parse included config */ + } + else if (1 == sscanf (line, "[%99[^]]]", value)) + { + /* [value] */ + GNUNET_free (section); + section = GNUNET_strdup (value); + } + else if (2 == sscanf (line, " %63[^= ] = %191[^\n]", tag, value)) + { + /* tag = value */ + /* Strip LF */ + i = strlen (value) - 1; + while ((i >= 0) && (isspace (value[i]))) + value[i--] = '\0'; + /* remove quotes */ + i = 0; + if (value[0] == '"') + { + i = 1; + while ((value[i] != '\0') && (value[i] != '"')) + i++; + if (value[i] == '"') + { + value[i] = '\0'; + i = 1; + } + else + i = 0; + } + GNUNET_CONFIGURATION_set_value_string (cfg, + section, tag, &value[i]); + } + else if (1 == sscanf (line, " %63[^= ] =[^\n]", tag)) + { + /* tag = */ + GNUNET_CONFIGURATION_set_value_string (cfg, section, tag, ""); + } + else + { + /* parse error */ + GNUNET_log (GNUNET_ERROR_TYPE_WARNING, + _ + ("Syntax error in configuration file `%s' at line %u.\n"), + filename, nr); + ret = GNUNET_SYSERR; + break; + } + } + GNUNET_assert (0 == fclose (fp)); + /* restore dirty flag - anything we set in the meantime + came from disk */ + cfg->dirty = dirty; + GNUNET_free (section); + return ret; +} + +int +GNUNET_CONFIGURATION_test_dirty (struct GNUNET_CONFIGURATION_Handle *cfg) +{ + return cfg->dirty; +} + +int +GNUNET_CONFIGURATION_write (struct GNUNET_CONFIGURATION_Handle *data, + const char *filename) +{ + struct ConfigSection *sec; + struct ConfigEntry *ent; + FILE *fp; + int error; + char *fn; + char *val; + char *pos; + + fn = GNUNET_STRINGS_filename_expand (filename); + GNUNET_DISK_directory_create_for_file (fn); + if (NULL == (fp = FOPEN (fn, "w"))) + { + GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING, "fopen", fn); + GNUNET_free (fn); + return GNUNET_SYSERR; + } + GNUNET_free (fn); + error = 0; + sec = data->sections; + while (sec != NULL) + { + if (0 > fprintf (fp, "[%s]\n", sec->name)) + { + error = 1; + break; + } + ent = sec->entries; + while (ent != NULL) + { + if (ent->val != NULL) + { + val = GNUNET_malloc (strlen (ent->val) * 2 + 1); + strcpy (val, ent->val); + while (NULL != (pos = strstr (val, "\n"))) + { + memmove (&pos[2], &pos[1], strlen (&pos[1])); + pos[0] = '\\'; + pos[1] = 'n'; + } + if (0 > fprintf (fp, "%s = %s\n", ent->key, val)) + { + error = 1; + GNUNET_free (val); + break; + } + GNUNET_free (val); + } + ent = ent->next; + } + if (error != 0) + break; + if (0 > fprintf (fp, "\n")) + { + error = 1; + break; + } + sec = sec->next; + } + if (error != 0) + GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING, "fprintf", filename); + GNUNET_assert (0 == fclose (fp)); + if (error != 0) + { + data->dirty = GNUNET_SYSERR; /* last write failed */ + return GNUNET_SYSERR; + } + data->dirty = GNUNET_NO; /* last write succeeded */ + return GNUNET_OK; +} + + +static struct ConfigSection * +findSection (struct GNUNET_CONFIGURATION_Handle *data, const char *section) +{ + struct ConfigSection *pos; + + pos = data->sections; + while ((pos != NULL) && (0 != strcasecmp (section, pos->name))) + pos = pos->next; + return pos; +} + + +static struct ConfigEntry * +findEntry (struct GNUNET_CONFIGURATION_Handle *data, + const char *section, const char *key) +{ + struct ConfigSection *sec; + struct ConfigEntry *pos; + + sec = findSection (data, section); + if (sec == NULL) + return NULL; + pos = sec->entries; + while ((pos != NULL) && (0 != strcasecmp (key, pos->key))) + pos = pos->next; + return pos; +} + +void +GNUNET_CONFIGURATION_set_value_string (struct GNUNET_CONFIGURATION_Handle + *data, + const char *section, + const char *option, const char *value) +{ + struct ConfigSection *sec; + struct ConfigEntry *e; + + e = findEntry (data, section, option); + if (e != NULL) + { + GNUNET_free_non_null (e->val); + e->val = GNUNET_strdup (value); + return; + } + sec = findSection (data, section); + if (sec == NULL) + { + sec = GNUNET_malloc (sizeof (struct ConfigSection)); + sec->name = GNUNET_strdup (section); + sec->next = data->sections; + data->sections = sec; + } + e = GNUNET_malloc (sizeof (struct ConfigEntry)); + e->key = GNUNET_strdup (option); + e->val = GNUNET_strdup (value); + e->next = sec->entries; + sec->entries = e; +} + +void +GNUNET_CONFIGURATION_set_value_number (struct GNUNET_CONFIGURATION_Handle + *cfg, const char *section, + const char *option, + unsigned long long number) +{ + char s[64]; + GNUNET_snprintf (s, 64, "%llu", number); + GNUNET_CONFIGURATION_set_value_string (cfg, section, option, s); +} + +int +GNUNET_CONFIGURATION_get_value_number (struct GNUNET_CONFIGURATION_Handle + *cfg, const char *section, + const char *option, + unsigned long long *number) +{ + struct ConfigEntry *e; + + e = findEntry (cfg, section, option); + if (e == NULL) + return GNUNET_SYSERR; + if (1 != SSCANF (e->val, "%llu", number)) + return GNUNET_SYSERR; + return GNUNET_OK; +} + +int +GNUNET_CONFIGURATION_get_value_string (struct GNUNET_CONFIGURATION_Handle + *cfg, const char *section, + const char *option, char **value) +{ + struct ConfigEntry *e; + + e = findEntry (cfg, section, option); + if ((e == NULL) || (e->val == NULL)) + { + *value = NULL; + return GNUNET_SYSERR; + } + *value = GNUNET_strdup (e->val); + return GNUNET_OK; +} + +int +GNUNET_CONFIGURATION_get_value_choice (struct GNUNET_CONFIGURATION_Handle + *cfg, const char *section, + const char *option, + const char **choices, + const char **value) +{ + struct ConfigEntry *e; + int i; + + e = findEntry (cfg, section, option); + if (e == NULL) + return GNUNET_SYSERR; + i = 0; + while (choices[i] != NULL) + { + if (0 == strcasecmp (choices[i], e->val)) + break; + i++; + } + if (choices[i] == NULL) + { + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + _("Configuration value '%s' for '%s'" + " in section '%s' is not in set of legal choices\n"), + e->val, option, section); + return GNUNET_SYSERR; + } + *value = choices[i]; + return GNUNET_OK; +} + +/** + * Test if we have a value for a particular option + * @return GNUNET_YES if so, GNUNET_NO if not. + */ +int +GNUNET_CONFIGURATION_have_value (struct GNUNET_CONFIGURATION_Handle *cfg, + const char *section, const char *option) +{ + struct ConfigEntry *e; + if ((NULL == (e = findEntry (cfg, section, option))) || (e->val == NULL)) + return GNUNET_NO; + return GNUNET_YES; +} + +/** + * Expand an expression of the form "$FOO/BAR" to "DIRECTORY/BAR" + * where either in the "PATHS" section or the environtment + * "FOO" is set to "DIRECTORY". + * + * @param old string to $-expand (will be freed!) + * @return $-expanded string + */ +char * +GNUNET_CONFIGURATION_expand_dollar (struct GNUNET_CONFIGURATION_Handle *cfg, + char *orig) +{ + int i; + char *prefix; + char *result; + const char *post; + const char *env; + + if (orig[0] != '$') + return orig; + i = 0; + while ((orig[i] != '/') && (orig[i] != '\\') && (orig[i] != '\0')) + i++; + if (orig[i] == '\0') + { + post = ""; + } + else + { + orig[i] = '\0'; + post = &orig[i + 1]; + } + if (GNUNET_OK != GNUNET_CONFIGURATION_get_value_string (cfg, + "PATHS", + &orig[1], &prefix)) + { + if (NULL == (env = getenv (&orig[1]))) + { + orig[i] = DIR_SEPARATOR; + return orig; + } + prefix = GNUNET_strdup (env); + } + result = GNUNET_malloc (strlen (prefix) + strlen (post) + 2); + strcpy (result, prefix); + if ((strlen (prefix) == 0) || + ((prefix[strlen (prefix) - 1] != DIR_SEPARATOR) && (strlen (post) > 0))) + strcat (result, DIR_SEPARATOR_STR); + strcat (result, post); + GNUNET_free (prefix); + GNUNET_free (orig); + return result; +} + +/** + * Get a configuration value that should be a string. + * @param value will be set to a freshly allocated configuration + * value, or NULL if option is not specified + * @return GNUNET_OK on success, GNUNET_SYSERR on error + */ +int +GNUNET_CONFIGURATION_get_value_filename (struct GNUNET_CONFIGURATION_Handle + *data, const char *section, + const char *option, char **value) +{ + int ret; + char *tmp; + + tmp = NULL; + ret = GNUNET_CONFIGURATION_get_value_string (data, section, option, &tmp); + if (ret == GNUNET_SYSERR) + return ret; + if (tmp != NULL) + { + tmp = GNUNET_CONFIGURATION_expand_dollar (data, tmp); + *value = GNUNET_STRINGS_filename_expand (tmp); + GNUNET_free (tmp); + } + else + { + *value = NULL; + } + return ret; +} + +/** + * Get a configuration value that should be in a set of + * "GNUNET_YES" or "GNUNET_NO". + * + * @return GNUNET_YES, GNUNET_NO or GNUNET_SYSERR + */ +int +GNUNET_CONFIGURATION_get_value_yesno (struct GNUNET_CONFIGURATION_Handle *cfg, + const char *section, const char *option) +{ + static const char *yesno[] = { "YES", "NO", NULL }; + const char *val; + int ret; + + ret = GNUNET_CONFIGURATION_get_value_choice (cfg, + section, option, yesno, &val); + if (ret == GNUNET_SYSERR) + return ret; + if (val == yesno[0]) + return GNUNET_YES; + return GNUNET_NO; +} + + +/** + * Iterate over the set of filenames stored in a configuration value. + * + * @return number of filenames iterated over, -1 on error + */ +int +GNUNET_CONFIGURATION_iterate_value_filenames (struct + GNUNET_CONFIGURATION_Handle + *cfg, const char *section, + const char *option, + GNUNET_FileNameCallback cb, + void *cls) +{ + char *list; + char *pos; + char *end; + char old; + int ret; + + if (GNUNET_OK != + GNUNET_CONFIGURATION_get_value_string (cfg, section, option, &list)) + return 0; + GNUNET_assert (list != NULL); + ret = 0; + pos = list; + while (1) + { + while (pos[0] == ' ') + pos++; + if (strlen (pos) == 0) + break; + end = pos + 1; + while ((end[0] != ' ') && (end[0] != '\0')) + { + if (end[0] == '\\') + { + switch (end[1]) + { + case '\\': + case ' ': + memmove (end, &end[1], strlen (&end[1]) + 1); + case '\0': + /* illegal, but just keep it */ + break; + default: + /* illegal, but just ignore that there was a '/' */ + break; + } + } + end++; + } + old = end[0]; + end[0] = '\0'; + if (strlen (pos) > 0) + { + ret++; + if ((cb != NULL) && (GNUNET_OK != cb (cls, pos))) + { + ret = GNUNET_SYSERR; + break; + } + } + if (old == '\0') + break; + pos = end + 1; + } + GNUNET_free (list); + return ret; +} + +static char * +escape_name (const char *value) +{ + char *escaped; + const char *rpos; + char *wpos; + + escaped = GNUNET_malloc (strlen (value) * 2 + 1); + memset (escaped, 0, strlen (value) * 2 + 1); + rpos = value; + wpos = escaped; + while (rpos[0] != '\0') + { + switch (rpos[0]) + { + case '\\': + case ' ': + wpos[0] = '\\'; + wpos[1] = rpos[0]; + wpos += 2; + break; + default: + wpos[0] = rpos[0]; + wpos++; + } + rpos++; + } + return escaped; +} + +static int +test_match (void *cls, const char *fn) +{ + const char *of = cls; + return (0 == strcmp (of, fn)) ? GNUNET_SYSERR : GNUNET_OK; +} + +/** + * Append a filename to a configuration value that + * represents a list of filenames + * + * @param value filename to append + * @return GNUNET_OK on success, + * GNUNET_NO if the filename already in the list + * GNUNET_SYSERR on error + */ +int +GNUNET_CONFIGURATION_append_value_filename (struct GNUNET_CONFIGURATION_Handle + *cfg, + const char *section, + const char *option, + const char *value) +{ + char *escaped; + char *old; + char *nw; + + if (GNUNET_SYSERR + == GNUNET_CONFIGURATION_iterate_value_filenames (cfg, + section, + option, + &test_match, + (void *) value)) + return GNUNET_NO; /* already exists */ + if (GNUNET_OK != + GNUNET_CONFIGURATION_get_value_string (cfg, section, option, &old)) + old = GNUNET_strdup (""); + escaped = escape_name (value); + nw = GNUNET_malloc (strlen (old) + strlen (escaped) + 2); + strcpy (nw, old); + strcat (nw, " "); + strcat (nw, escaped); + GNUNET_CONFIGURATION_set_value_string (cfg, section, option, nw); + GNUNET_free (old); + GNUNET_free (nw); + GNUNET_free (escaped); + return GNUNET_OK; +} + + +/** + * Remove a filename from a configuration value that + * represents a list of filenames + * + * @param value filename to remove + * @return GNUNET_OK on success, + * GNUNET_NO if the filename is not in the list, + * GNUNET_SYSERR on error + */ +int +GNUNET_CONFIGURATION_remove_value_filename (struct GNUNET_CONFIGURATION_Handle + *cfg, + const char *section, + const char *option, + const char *value) +{ + char *list; + char *pos; + char *end; + char *match; + char old; + int ret; + + if (GNUNET_OK != + GNUNET_CONFIGURATION_get_value_string (cfg, section, option, &list)) + return GNUNET_NO; + match = escape_name (value); + ret = 0; + pos = list; + while (1) + { + while (pos[0] == ' ') + pos++; + if (strlen (pos) == 0) + break; + end = pos + 1; + while ((end[0] != ' ') && (end[0] != '\0')) + { + if (end[0] == '\\') + { + switch (end[1]) + { + case '\\': + case ' ': + end++; + break; + case '\0': + /* illegal, but just keep it */ + break; + default: + /* illegal, but just ignore that there was a '/' */ + break; + } + } + end++; + } + old = end[0]; + end[0] = '\0'; + if (strlen (pos) > 0) + { + if (0 == strcmp (pos, match)) + { + memmove (pos, &end[1], strlen (&end[1]) + 1); + + if (pos != list) + pos[-1] = ' '; /* previously changed to "\0" */ + GNUNET_CONFIGURATION_set_value_string (cfg, + section, option, list); + GNUNET_free (list); + GNUNET_free (match); + return GNUNET_OK; + } + } + if (old == '\0') + break; + pos = end + 1; + } + GNUNET_free (list); + GNUNET_free (match); + return GNUNET_NO; +} + + +/** + * Load configuration (starts with defaults, then loads + * system-specific configuration). + */ +int +GNUNET_CONFIGURATION_load (struct GNUNET_CONFIGURATION_Handle *cfg, + const char *cfgfn) +{ + char *baseconfig; + char *ipath; + + ipath = GNUNET_OS_installation_get_path (GNUNET_OS_IPK_DATADIR); + if (ipath == NULL) + return GNUNET_SYSERR; + baseconfig = NULL; + GNUNET_asprintf (&baseconfig, + "%s%s%s", ipath, DIR_SEPARATOR_STR, "defaults.conf"); + GNUNET_free (ipath); + if ((GNUNET_OK != + GNUNET_CONFIGURATION_parse (cfg, baseconfig)) || + (!((cfgfn == NULL) || + (GNUNET_OK == GNUNET_CONFIGURATION_parse (cfg, cfgfn))))) + { + GNUNET_free (baseconfig); + return GNUNET_SYSERR; + } + GNUNET_free (baseconfig); + if ((GNUNET_YES == GNUNET_CONFIGURATION_have_value (cfg, + "TESTING", + "WEAKRANDOM")) && + (GNUNET_YES == GNUNET_CONFIGURATION_get_value_yesno (cfg, + "TESTING", + "WEAKRANDOM"))) + GNUNET_CRYPTO_random_disable_entropy_gathering (); + return GNUNET_OK; +} + + + +/* end of configuration.c */ diff --git a/src/util/container_bloomfilter.c b/src/util/container_bloomfilter.c new file mode 100644 index 000000000..8b76ef8dc --- /dev/null +++ b/src/util/container_bloomfilter.c @@ -0,0 +1,677 @@ +/* + This file is part of GNUnet. + (C) 2001, 2002, 2003, 2004, 2006, 2008 Christian Grothoff (and other contributing authors) + + GNUnet is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published + by the Free Software Foundation; either version 2, 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 + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with GNUnet; see the file COPYING. If not, write to the + Free Software Foundation, Inc., 59 Temple Place - Suite 330, + Boston, MA 02111-1307, USA. +*/ +/** + * @file util/container_bloomfilter.c + * @brief data structure used to reduce disk accesses. + * + * The idea basically: Create a signature for each element in the + * database. Add those signatures to a bit array. When doing a lookup, + * check if the bit array matches the signature of the requested + * element. If yes, address the disk, otherwise return 'not found'. + * + * A property of the bloom filter is that sometimes we will have + * a match even if the element is not on the disk (then we do + * an unnecessary disk access), but what's most important is that + * we never get a single "false negative". + * + * To be able to delete entries from the bloom filter, we maintain + * a 4 bit counter in the file on the drive (we still use only one + * bit in memory). + * + * @author Igor Wronsky + * @author Christian Grothoff + */ + +#include "platform.h" +#include "gnunet_common.h" +#include "gnunet_container_lib.h" +#include "gnunet_disk_lib.h" + +struct GNUNET_CONTAINER_BloomFilter +{ + + /** + * The actual bloomfilter bit array + */ + char *bitArray; + + /** + * Filename of the filter + */ + char *filename; + + /** + * The bit counter file on disk + */ + int fd; + + /** + * How many bits we set for each stored element + */ + unsigned int addressesPerElement; + + /** + * Size of bitArray in bytes + */ + unsigned int bitArraySize; + +}; + + +/** + * Sets a bit active in the bitArray. Increment bit-specific + * usage counter on disk only if below 4bit max (==15). + * + * @param bitArray memory area to set the bit in + * @param bitIdx which bit to set + */ +static void +setBit (char *bitArray, unsigned int bitIdx) +{ + unsigned int arraySlot; + unsigned int targetBit; + + arraySlot = bitIdx / 8; + targetBit = (1L << (bitIdx % 8)); + bitArray[arraySlot] |= targetBit; +} + +/** + * Clears a bit from bitArray. Bit is cleared from the array + * only if the respective usage counter on the disk hits/is zero. + * + * @param bitArray memory area to set the bit in + * @param bitIdx which bit to unset + */ +static void +clearBit (char *bitArray, unsigned int bitIdx) +{ + unsigned int slot; + unsigned int targetBit; + + slot = bitIdx / 8; + targetBit = (1L << (bitIdx % 8)); + bitArray[slot] = bitArray[slot] & (~targetBit); +} + +/** + * Checks if a bit is active in the bitArray + * + * @param bitArray memory area to set the bit in + * @param bitIdx which bit to test + * @return GNUNET_YES if the bit is set, GNUNET_NO if not. + */ +static int +testBit (char *bitArray, unsigned int bitIdx) +{ + unsigned int slot; + unsigned int targetBit; + + slot = bitIdx / 8; + targetBit = (1L << (bitIdx % 8)); + if (bitArray[slot] & targetBit) + return GNUNET_YES; + else + return GNUNET_NO; +} + +/** + * Sets a bit active in the bitArray and increments + * bit-specific usage counter on disk (but only if + * the counter was below 4 bit max (==15)). + * + * @param bitArray memory area to set the bit in + * @param bitIdx which bit to test + * @param fd A file to keep the 4 bit address usage counters in + */ +static void +incrementBit (char *bitArray, unsigned int bitIdx, int fd) +{ + unsigned int fileSlot; + unsigned char value; + unsigned int high; + unsigned int low; + unsigned int targetLoc; + + setBit (bitArray, bitIdx); + if (fd == -1) + return; + /* Update the counter file on disk */ + fileSlot = bitIdx / 2; + targetLoc = bitIdx % 2; + + GNUNET_assert (fileSlot == (unsigned int) LSEEK (fd, fileSlot, SEEK_SET)); + if (1 != READ (fd, &value, 1)) + value = 0; + low = value & 0xF; + high = (value & (~0xF)) >> 4; + + if (targetLoc == 0) + { + if (low < 0xF) + low++; + } + else + { + if (high < 0xF) + high++; + } + value = ((high << 4) | low); + GNUNET_assert (fileSlot == (unsigned int) LSEEK (fd, fileSlot, SEEK_SET)); + GNUNET_assert (1 == WRITE (fd, &value, 1)); +} + +/** + * Clears a bit from bitArray if the respective usage + * counter on the disk hits/is zero. + * + * @param bitArray memory area to set the bit in + * @param bitIdx which bit to test + * @param fd A file to keep the 4bit address usage counters in + */ +static void +decrementBit (char *bitArray, unsigned int bitIdx, int fd) +{ + unsigned int fileSlot; + unsigned char value; + unsigned int high; + unsigned int low; + unsigned int targetLoc; + + if (fd == -1) + return; /* cannot decrement! */ + /* Each char slot in the counter file holds two 4 bit counters */ + fileSlot = bitIdx / 2; + targetLoc = bitIdx % 2; + LSEEK (fd, fileSlot, SEEK_SET); + if (1 != READ (fd, &value, 1)) + value = 0; + low = value & 0xF; + high = (value & 0xF0) >> 4; + + /* decrement, but once we have reached the max, never go back! */ + if (targetLoc == 0) + { + if ((low > 0) && (low < 0xF)) + low--; + if (low == 0) + { + clearBit (bitArray, bitIdx); + } + } + else + { + if ((high > 0) && (high < 0xF)) + high--; + if (high == 0) + { + clearBit (bitArray, bitIdx); + } + } + value = ((high << 4) | low); + LSEEK (fd, fileSlot, SEEK_SET); + GNUNET_assert (1 == WRITE (fd, &value, 1)); +} + +#define BUFFSIZE 65536 + +/** + * Creates a file filled with zeroes + * + * @param fd the file handle + * @param size the size of the file + * @return GNUNET_OK if created ok, GNUNET_SYSERR otherwise + */ +static int +makeEmptyFile (int fd, unsigned int size) +{ + char *buffer; + unsigned int bytesleft = size; + int res = 0; + + if (fd == -1) + return GNUNET_SYSERR; + buffer = GNUNET_malloc (BUFFSIZE); + memset (buffer, 0, BUFFSIZE); + LSEEK (fd, 0, SEEK_SET); + + while (bytesleft > 0) + { + if (bytesleft > BUFFSIZE) + { + res = WRITE (fd, buffer, BUFFSIZE); + bytesleft -= BUFFSIZE; + } + else + { + res = WRITE (fd, buffer, bytesleft); + bytesleft = 0; + } + GNUNET_assert (res != -1); + } + GNUNET_free (buffer); + return GNUNET_OK; +} + +/* ************** GNUNET_CONTAINER_BloomFilter GNUNET_CRYPTO_hash iterator ********* */ + +/** + * Iterator (callback) method to be called by the + * bloomfilter iterator on each bit that is to be + * set or tested for the key. + * + * @param bf the filter to manipulate + * @param bit the current bit + * @param additional context specific argument + */ +typedef void (*BitIterator) (struct GNUNET_CONTAINER_BloomFilter * bf, + unsigned int bit, void *arg); + +/** + * Call an iterator for each bit that the bloomfilter + * must test or set for this element. + * + * @param bf the filter + * @param callback the method to call + * @param arg extra argument to callback + * @param key the key for which we iterate over the BF bits + */ +static void +iterateBits (struct GNUNET_CONTAINER_BloomFilter *bf, + BitIterator callback, void *arg, const GNUNET_HashCode * key) +{ + GNUNET_HashCode tmp[2]; + int bitCount; + int round; + unsigned int slot = 0; + + bitCount = bf->addressesPerElement; + memcpy (&tmp[0], key, sizeof (GNUNET_HashCode)); + round = 0; + while (bitCount > 0) + { + while (slot < (sizeof (GNUNET_HashCode) / sizeof (unsigned int))) + { + callback (bf, + (((unsigned int *) &tmp[round & 1])[slot]) & + ((bf->bitArraySize * 8) - 1), arg); + slot++; + bitCount--; + if (bitCount == 0) + break; + } + if (bitCount > 0) + { + GNUNET_CRYPTO_hash (&tmp[round & 1], sizeof (GNUNET_HashCode), + &tmp[(round + 1) & 1]); + round++; + slot = 0; + } + } +} + +/** + * Callback: increment bit + * + * @param bf the filter to manipulate + * @param bit the bit to increment + * @param arg not used + */ +static void +incrementBitCallback (struct GNUNET_CONTAINER_BloomFilter *bf, + unsigned int bit, void *arg) +{ + incrementBit (bf->bitArray, bit, bf->fd); +} + +/** + * Callback: decrement bit + * + * @param bf the filter to manipulate + * @param bit the bit to decrement + * @param arg not used + */ +static void +decrementBitCallback (struct GNUNET_CONTAINER_BloomFilter *bf, + unsigned int bit, void *arg) +{ + decrementBit (bf->bitArray, bit, bf->fd); +} + +/** + * Callback: test if all bits are set + * + * @param bf the filter + * @param bit the bit to test + * @param arg pointer set to GNUNET_NO if bit is not set + */ +static void +testBitCallback (struct GNUNET_CONTAINER_BloomFilter *bf, unsigned int bit, + void *cls) +{ + int *arg = cls; + if (GNUNET_NO == testBit (bf->bitArray, bit)) + *arg = GNUNET_NO; +} + +/* *********************** INTERFACE **************** */ + +/** + * Load a bloom-filter from a file. + * + * @param filename the name of the file (or the prefix) + * @param size the size of the bloom-filter (number of + * bytes of storage space to use) + * @param k the number of GNUNET_CRYPTO_hash-functions to apply per + * element (number of bits set per element in the set) + * @return the bloomfilter + */ +struct GNUNET_CONTAINER_BloomFilter * +GNUNET_CONTAINER_bloomfilter_load (const char *filename, unsigned int size, + unsigned int k) +{ + struct GNUNET_CONTAINER_BloomFilter *bf; + char *rbuff; + unsigned int pos; + int i; + unsigned int ui; + + if ((k == 0) || (size == 0)) + return NULL; + if (size < BUFFSIZE) + size = BUFFSIZE; + ui = 1; + while (ui < size) + ui *= 2; + size = ui; /* make sure it's a power of 2 */ + + bf = GNUNET_malloc (sizeof (struct GNUNET_CONTAINER_BloomFilter)); + /* Try to open a bloomfilter file */ + if (filename != NULL) + { +#ifndef _MSC_VER + bf->fd = GNUNET_DISK_file_open (filename, O_RDWR | O_CREAT, + S_IRUSR | S_IWUSR); +#else + bf->fd = GNUNET_DISK_file_open (filename, + O_WRONLY | O_CREAT, S_IREAD | S_IWRITE); +#endif + if (-1 == bf->fd) + { + GNUNET_free (bf); + return NULL; + } + bf->filename = GNUNET_strdup (filename); + } + else + { + bf->fd = -1; + bf->filename = NULL; + } + /* Alloc block */ + bf->bitArray = GNUNET_malloc_large (size); + bf->bitArraySize = size; + bf->addressesPerElement = k; + memset (bf->bitArray, 0, bf->bitArraySize); + + if (bf->fd != -1) + { + /* Read from the file what bits we can */ + rbuff = GNUNET_malloc (BUFFSIZE); + pos = 0; + while (pos < size * 8) + { + int res; + + res = READ (bf->fd, rbuff, BUFFSIZE); + if (res == 0) + break; /* is ok! we just did not use that many bits yet */ + for (i = 0; i < res; i++) + { + if ((rbuff[i] & 0x0F) != 0) + setBit (bf->bitArray, pos + i * 2); + if ((rbuff[i] & 0xF0) != 0) + setBit (bf->bitArray, pos + i * 2 + 1); + } + if (res < BUFFSIZE) + break; + pos += BUFFSIZE * 2; /* 2 bits per byte in the buffer */ + } + GNUNET_free (rbuff); + } + return bf; +} + + +/** + * Create a bloom filter from raw bits. + * + * @param data the raw bits in memory (maybe NULL, + * in which case all bits should be considered + * to be zero). + * @param size the size of the bloom-filter (number of + * bytes of storage space to use); also size of data + * -- unless data is NULL + * @param k the number of GNUNET_CRYPTO_hash-functions to apply per + * element (number of bits set per element in the set) + * @return the bloomfilter + */ +struct GNUNET_CONTAINER_BloomFilter * +GNUNET_CONTAINER_bloomfilter_init (const char *data, unsigned int size, + unsigned int k) +{ + struct GNUNET_CONTAINER_BloomFilter *bf; + unsigned int ui; + + if ((k == 0) || (size == 0)) + return NULL; + ui = 1; + while (ui < size) + ui *= 2; + if (size != ui) + { + GNUNET_break (0); + return NULL; + } + bf = GNUNET_malloc (sizeof (struct GNUNET_CONTAINER_BloomFilter)); + bf->fd = -1; + bf->filename = NULL; + bf->bitArray = GNUNET_malloc_large (size); + bf->bitArraySize = size; + bf->addressesPerElement = k; + if (data != NULL) + memcpy (bf->bitArray, data, size); + else + memset (bf->bitArray, 0, bf->bitArraySize); + return bf; +} + + +/** + * Copy the raw data of this bloomfilter into + * the given data array. + * + * @param data where to write the data + * @param size the size of the given data array + * @return GNUNET_SYSERR if the data array is not big enough + */ +int +GNUNET_CONTAINER_bloomfilter_get_raw_data (struct GNUNET_CONTAINER_BloomFilter + *bf, char *data, unsigned int size) +{ + if (NULL == bf) + return GNUNET_SYSERR; + + if (bf->bitArraySize != size) + return GNUNET_SYSERR; + memcpy (data, bf->bitArray, size); + return GNUNET_OK; +} + +/** + * Free the space associated with a filter + * in memory, flush to drive if needed (do not + * free the space on the drive) + * + * @param bf the filter + */ +void +GNUNET_CONTAINER_bloomfilter_free (struct GNUNET_CONTAINER_BloomFilter *bf) +{ + if (NULL == bf) + return; + if (bf->fd != -1) + GNUNET_DISK_file_close (bf->filename, bf->fd); + GNUNET_free_non_null (bf->filename); + GNUNET_free (bf->bitArray); + GNUNET_free (bf); +} + +/** + * Reset a bloom filter to empty. Clears the file on disk. + * + * @param bf the filter + */ +void +GNUNET_CONTAINER_bloomfilter_clear (struct GNUNET_CONTAINER_BloomFilter *bf) +{ + if (NULL == bf) + return; + + memset (bf->bitArray, 0, bf->bitArraySize); + if (bf->fd != -1) + makeEmptyFile (bf->fd, bf->bitArraySize * 4); +} + + +/** + * Test if an element is in the filter. + * + * @param e the element + * @param bf the filter + * @return GNUNET_YES if the element is in the filter, GNUNET_NO if not + */ +int +GNUNET_CONTAINER_bloomfilter_test (struct GNUNET_CONTAINER_BloomFilter *bf, + const GNUNET_HashCode * e) +{ + int res; + + if (NULL == bf) + return GNUNET_YES; + res = GNUNET_YES; + iterateBits (bf, &testBitCallback, &res, e); + return res; +} + +/** + * Add an element to the filter + * + * @param bf the filter + * @param e the element + */ +void +GNUNET_CONTAINER_bloomfilter_add (struct GNUNET_CONTAINER_BloomFilter *bf, + const GNUNET_HashCode * e) +{ + + if (NULL == bf) + return; + iterateBits (bf, &incrementBitCallback, NULL, e); +} + + +/** + * Or the entries of the given raw data array with the + * data of the given bloom filter. Assumes that + * the size of the data array and the current filter + * match. + * @param bf the filter + */ +int +GNUNET_CONTAINER_bloomfilter_or (struct GNUNET_CONTAINER_BloomFilter *bf, + const char *data, unsigned int size) +{ + unsigned int i; + + if (NULL == bf) + return GNUNET_YES; + if (bf->bitArraySize != size) + return GNUNET_SYSERR; + /* FIXME: we could do this 4-8x faster by + going over int/long arrays */ + for (i = 0; i < size; i++) + bf->bitArray[i] |= data[i]; + return GNUNET_OK; +} + +/** + * Remove an element from the filter. + * + * @param bf the filter + * @param e the element to remove + */ +void +GNUNET_CONTAINER_bloomfilter_remove (struct GNUNET_CONTAINER_BloomFilter *bf, + const GNUNET_HashCode * e) +{ + if (NULL == bf) + return; + if (bf->fd == -1) + return; + iterateBits (bf, &decrementBitCallback, NULL, e); +} + +/** + * Resize a bloom filter. Note that this operation + * is pretty costly. Essentially, the bloom filter + * needs to be completely re-build. + * + * @param bf the filter + * @param iterator an iterator over all elements stored in the BF + * @param iterator_arg argument to the iterator function + * @param size the new size for the filter + * @param k the new number of GNUNET_CRYPTO_hash-function to apply per element + */ +void +GNUNET_CONTAINER_bloomfilter_resize (struct GNUNET_CONTAINER_BloomFilter *bf, + GNUNET_HashCodeIterator iterator, + void *iterator_arg, unsigned int size, + unsigned int k) +{ + GNUNET_HashCode hc; + unsigned int i; + + GNUNET_free (bf->bitArray); + i = 1; + while (i < size) + i *= 2; + size = i; /* make sure it's a power of 2 */ + + bf->bitArraySize = size; + bf->bitArray = GNUNET_malloc (size); + memset (bf->bitArray, 0, bf->bitArraySize); + if (bf->fd != -1) + makeEmptyFile (bf->fd, bf->bitArraySize * 4); + while (GNUNET_YES == iterator (&hc, iterator_arg)) + GNUNET_CONTAINER_bloomfilter_add (bf, &hc); +} + +/* end of container_bloomfilter.c */ diff --git a/src/util/container_heap.c b/src/util/container_heap.c new file mode 100644 index 000000000..1e7077c80 --- /dev/null +++ b/src/util/container_heap.c @@ -0,0 +1,533 @@ +/* + This file is part of GNUnet. + (C) 2008 Christian Grothoff (and other contributing authors) + + GNUnet is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published + by the Free Software Foundation; either version 2, 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 + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with GNUnet; see the file COPYING. If not, write to the + Free Software Foundation, Inc., 59 Temple Place - Suite 330, + Boston, MA 02111-1307, USA. + */ + +/** + * @author Nathan Evans + * @file util/container_heap.c + * @brief Implementation of heap operations + */ + +#include "platform.h" +#include "gnunet_protocols.h" +#include "gnunet_util.h" +#include "gnunet_util_containers.h" + +/* + * Struct that is stored in hashmap, pointers to + * locations in min_heap and max_heap. + */ +struct GNUNET_CONTAINER_heap_info +{ + struct GNUNET_CONTAINER_heap_node *min_loc; + + struct GNUNET_CONTAINER_heap_node *max_loc; + +}; + +/* + * Generic heap node structure, contains pointer to parent + * left child, right child, and actual neighbor. + */ +struct GNUNET_CONTAINER_heap_node +{ + struct GNUNET_CONTAINER_heap_node *parent; + + struct GNUNET_CONTAINER_heap_node *left_child; + + struct GNUNET_CONTAINER_heap_node *right_child; + + GNUNET_CONTAINER_HeapCost cost; + + void *element; + +}; + +struct GNUNET_CONTAINER_Heap +{ + unsigned int size; + + unsigned int max_size; + + enum type; + + struct GNUNET_CONTAINER_heap_node *root; + + struct GNUNET_CONTAINER_heap_node *traversal_pos; + +}; + +void +internal_print (struct GNUNET_CONTAINER_heap_node *root) +{ + fprintf (stdout, "%d\n", root->cost); + if (root->left_child != NULL) + { + fprintf (stdout, "LEFT of %d\n", root->cost); + internal_print (root->left_child); + } + if (root->right_child != NULL) + { + fprintf (stdout, "RIGHT of %d\n", root->cost); + internal_print (root->right_child); + } +} + +void +printTree (struct GNUNET_CONTAINER_Heap *root) +{ + internal_print (root->root); +} + +struct GNUNET_CONTAINER_Heap * +GNUNET_CONTAINER_heap_create (enum type) +{ + struct GNUNET_CONTAINER_Heap *heap; + heap = malloc (sizeof (struct GNUNET_CONTAINER_Heap)); + heap->max_size = -1; + heap->type = type; + heap->root = NULL; + heap->traversal_pos = NULL; + heap->size = 0; + + return heap; +} + +void +GNUNET_CONTAINER_heap_destroy (struct GNUNET_CONTAINER_Heap *heap) +{ + void *unused; + while (heap->size > 0) + { + unused = GNUNET_CONTAINER_heap_remove_root (heap); + } + + GNUNET_free (heap); + return; +} + +struct GNUNET_CONTAINER_heap_node * +find_element (struct GNUNET_CONTAINER_heap_node *node, void *element) +{ + struct GNUNET_CONTAINER_heap_node *ret; + ret = NULL; + if ((node != NULL) && (node->element == element)) + { + ret = node; + } + + if ((ret == NULL) && (node->left_child != NULL)) + { + ret = find_element (node->left_child, element); + } + + if ((ret == NULL) && (node->right_child != NULL)) + { + ret = find_element (node->right_child, element); + } + + return ret; +} + +static struct GNUNET_CONTAINER_heap_node * +getNextPos (struct GNUNET_CONTAINER_Heap *root) +{ + struct GNUNET_CONTAINER_heap_node *ret; + struct GNUNET_CONTAINER_heap_node *parent; + int pos; + int depth; + int i; + + ret = GNUNET_malloc (sizeof (struct GNUNET_CONTAINER_heap_node)); + pos = root->size + 1; + depth = (int) log2 (pos); + ret->left_child = NULL; + ret->right_child = NULL; + + if (depth == 0) + { + ret->parent = NULL; + root->root = ret; + } + else + { + parent = root->root; + for (i = depth; i > 1; i--) + { + if (((pos / (1 << (i - 1))) % 2) == 0) + parent = parent->left_child; + else + parent = parent->right_child; + } + + ret->parent = parent; + if ((pos % 2) == 0) + parent->left_child = ret; + else + parent->right_child = ret; + + } + + return ret; + +} + +static struct GNUNET_CONTAINER_heap_node * +getPos (struct GNUNET_CONTAINER_Heap *root, unsigned int pos) +{ + struct GNUNET_CONTAINER_heap_node *ret; + + int depth; + int i; + + depth = (int) log2 (pos); + ret = NULL; + if (pos > root->size) + { + return ret; + } + else + { + ret = root->root; + for (i = depth; i > 0; i--) + { + if (((pos / (1 << (i - 1))) % 2) == 0) + ret = ret->left_child; + else + ret = ret->right_child; + } + } + + return ret; + +} + +void +swapNodes (struct GNUNET_CONTAINER_heap_node *first, + struct GNUNET_CONTAINER_heap_node *second, + struct GNUNET_CONTAINER_Heap *root) +{ + void *temp_element; + GNUNET_CONTAINER_HeapCost temp_cost; + + temp_element = first->element; + temp_cost = first->cost; + first->element = second->element; + first->cost = second->cost; + second->element = temp_element; + second->cost = temp_cost; + +/* + * I still worry that there is some good reason for + * elements being location aware... but it eludes me + * for the moment... + if ((root->type == GNUNET_DV_MAX_HEAP)) + { + first->neighbor->max_loc = first; + second->neighbor->max_loc = second; + } + else if ((root->type == GNUNET_DV_MIN_HEAP)) + { + first->neighbor->min_loc = first; + second->neighbor->min_loc = second; + } +*/ + return; +} + +void +percolateHeap (struct GNUNET_CONTAINER_heap_node *pos, + struct GNUNET_CONTAINER_Heap *root) +{ + + while ((pos->parent != NULL) && + (((root->type == GNUNET_CONTAINER_HEAP_ORDER_MAX) + && (pos->parent->cost < pos->cost)) + || ((root->type == GNUNET_CONTAINER_HEAP_ORDER_MIN) + && (pos->parent->cost > pos->cost)))) + { + swapNodes (pos, pos->parent, root); + pos = pos->parent; + } + + return; +} + + + +void +percolateDownHeap (struct GNUNET_CONTAINER_heap_node *pos, + struct GNUNET_CONTAINER_Heap *root) +{ + struct GNUNET_CONTAINER_heap_node *switchNeighbor; + + switchNeighbor = pos; + + if ((root->type == GNUNET_CONTAINER_HEAP_ORDER_MAX)) + { + if ((pos->left_child != NULL) + && (pos->left_child->cost > switchNeighbor->cost)) + { + switchNeighbor = pos->left_child; + } + + if ((pos->right_child != NULL) + && (pos->right_child->cost > switchNeighbor->cost)) + { + switchNeighbor = pos->right_child; + } + } + else if ((root->type == GNUNET_CONTAINER_HEAP_ORDER_MIN)) + { + if ((pos->left_child != NULL) + && (pos->left_child->cost < switchNeighbor->cost)) + { + switchNeighbor = pos->left_child; + } + + if ((pos->right_child != NULL) + && (pos->right_child->cost < switchNeighbor->cost)) + { + switchNeighbor = pos->right_child; + } + } + + if (switchNeighbor != pos) + { + swapNodes (switchNeighbor, pos, root); + percolateDownHeap (switchNeighbor, root); + } + + return; +} + +void * +GNUNET_CONTAINER_heap_remove_node (struct GNUNET_CONTAINER_Heap *root, + void *element) +{ + void *ret; + struct GNUNET_CONTAINER_heap_node *del_node; + struct GNUNET_CONTAINER_heap_node *last; + GNUNET_CONTAINER_HeapCost old_cost; + + del_node = NULL; + del_node = find_element (root->root, element); + + if (del_node == NULL) + return NULL; + + ret = del_node->element; + last = getPos (root, root->size); + + old_cost = del_node->cost; + del_node->element = last->element; + del_node->cost = last->cost; + + if (last->parent->left_child == last) + last->parent->left_child = NULL; + if (last->parent->right_child == last) + last->parent->right_child = NULL; + + if (root->traversal_pos == last) + { + root->traversal_pos = root->root; + } + GNUNET_free (last); + root->size--; + + if (del_node->cost > old_cost) + { + if (root->type == GNUNET_CONTAINER_HEAP_ORDER_MAX) + percolateHeap (del_node, root); + else if (root->type == GNUNET_CONTAINER_HEAP_ORDER_MIN) + percolateDownHeap (del_node, root); + } + else if (del_node->cost < old_cost) + { + if (root->type == GNUNET_CONTAINER_HEAP_ORDER_MAX) + percolateDownHeap (del_node, root); + else if (root->type == GNUNET_CONTAINER_HEAP_ORDER_MIN) + percolateHeap (del_node, root); + } + + return ret; +} + +int +GNUNET_CONTAINER_heap_insert (struct GNUNET_CONTAINER_Heap *root, + void *element, GNUNET_CONTAINER_HeapCost cost) +{ + struct GNUNET_CONTAINER_heap_node *new_pos; + int ret; + ret = GNUNET_YES; + + if (root->max_size > root->size) + { + new_pos = getNextPos (root); + new_pos->element = element; + new_pos->cost = cost; + root->size++; + /*We no longer can tolerate pointers between heaps :( */ + /*if (root->type == GNUNET_DV_MIN_HEAP) + new_pos->neighbor->min_loc = new_pos; + else if (root->type == GNUNET_DV_MAX_HEAP) + new_pos->neighbor->max_loc = new_pos; */ + + percolateHeap (new_pos, root); + } + else + { + ret = GNUNET_NO; + } + + return ret; +} + +void * +GNUNET_CONTAINER_heap_remove_root (struct GNUNET_CONTAINER_Heap *root) +{ + void *ret; + struct GNUNET_CONTAINER_heap_node *root_node; + struct GNUNET_CONTAINER_heap_node *last; + + root_node = root->root; + ret = root_node->element; + last = getPos (root, root->size); + + if (last->parent->left_child == last) + last->parent->left_child = NULL; + else if (last->parent->right_child == last) + last->parent->right_child = NULL; + + root_node->element = last->element; + root_node->cost = last->cost; + + if (root->traversal_pos == last) + { + root->traversal_pos = root->root; + } + + GNUNET_free (last); + root->size--; + percolateDownHeap (root->root, root); + return ret; +} + +static int +updatedCost (struct GNUNET_CONTAINER_Heap *root, + struct GNUNET_CONTAINER_heap_node *node) +{ + struct GNUNET_CONTAINER_heap_node *parent; + + if (node == NULL) + return GNUNET_SYSERR; + + parent = node->parent; + + if ((root->type == GNUNET_CONTAINER_HEAP_ORDER_MAX) && (parent != NULL) + && (node->cost > parent->cost)) + percolateHeap (node, root); + else if ((root->type == GNUNET_CONTAINER_HEAP_ORDER_MIN) && (parent != NULL) + && (node->cost < parent->cost)) + percolateHeap (node, root); + else if (root->type == GNUNET_CONTAINER_HEAP_ORDER_MAX) + percolateDownHeap (node, root); + else if (root->type == GNUNET_CONTAINER_HEAP_ORDER_MIN) + percolateDownHeap (node, root); + + return GNUNET_YES; +} + + +int +GNUNET_CONTAINER_heap_update_cost (struct GNUNET_CONTAINER_Heap *root, + void *element, + GNUNET_CONTAINER_HeapCost new_cost) +{ + struct GNUNET_CONTAINER_heap_node *node; + int ret = GNUNET_YES; + node = find_element (root->root, element); + if (node == NULL) + return GNUNET_NO; + + node->cost = new_cost; + ret = updatedCost (root, node); + return ret; +} + +void +internal_iterator (struct GNUNET_CONTAINER_Heap *root, + struct GNUNET_CONTAINER_heap_node *node, + GNUNET_CONTAINER_HeapIterator iterator, void *cls) +{ + if (node == NULL) + return; + internal_iterator (root, node->left_child, iterator, cls); + internal_iterator (root, node->right_child, iterator, cls); + iterator (node->element, node->cost, root, cls); +} + +int +GNUNET_CONTAINER_heap_iterate (struct GNUNET_CONTAINER_Heap *heap, + GNUNET_CONTAINER_HeapIterator iterator, + void *cls) +{ + internal_iterator (heap, heap->root, iterator, cls); + return GNUNET_OK; +} + +void * +GNUNET_CONTAINER_heap_walk_get_next (struct GNUNET_CONTAINER_Heap *root) +{ + unsigned int choice; + void *element; + + if ((root->traversal_pos == NULL) && (root->root != NULL)) + { + root->traversal_pos = root->root; + } + + if (root->traversal_pos == NULL) + return NULL; + + element = root->traversal_pos->element; + + choice = GNUNET_random_u32 (GNUNET_RANDOM_QUALITY_WEAK, 2); + + switch (choice) + { + case 1: + root->traversal_pos = root->traversal_pos->right_child; + break; + case 0: + root->traversal_pos = root->traversal_pos->left_child; + break; + } + + return element; + +} + +unsigned int +GNUNET_CONTAINER_heap_get_size (struct GNUNET_CONTAINER_Heap *heap) +{ + return heap->size; +} + +/* end of heap.c */ diff --git a/src/util/container_meta_data.c b/src/util/container_meta_data.c new file mode 100644 index 000000000..b79de57d2 --- /dev/null +++ b/src/util/container_meta_data.c @@ -0,0 +1,721 @@ +/* + This file is part of GNUnet. + (C) 2003, 2004, 2005, 2006, 2008, 2009 Christian Grothoff (and other contributing authors) + + GNUnet is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published + by the Free Software Foundation; either version 2, 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 + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with GNUnet; see the file COPYING. If not, write to the + Free Software Foundation, Inc., 59 Temple Place - Suite 330, + Boston, MA 02111-1307, USA. +*/ + +/** + * @file util/container_meta_data.c + * @brief Storing of meta data + * @author Christian Grothoff + */ + +#include "platform.h" +#include "gnunet_common.h" +#include "gnunet_container_lib.h" +#include "gnunet_strings_lib.h" +#include "gnunet_time_lib.h" +#include +#include + +#define EXTRA_CHECKS ALLOW_EXTRA_CHECKS + +struct Item +{ + EXTRACTOR_KeywordType type; + char *data; +}; + +/** + * Meta data to associate with a file, directory or namespace. + */ +struct GNUNET_CONTAINER_MetaData +{ + uint32_t itemCount; + struct Item *items; +}; + +/** + * Create a fresh struct CONTAINER_MetaData token. + */ +struct GNUNET_CONTAINER_MetaData * +GNUNET_CONTAINER_meta_data_create () +{ + struct GNUNET_CONTAINER_MetaData *ret; + ret = GNUNET_malloc (sizeof (struct GNUNET_CONTAINER_MetaData)); + ret->items = NULL; + ret->itemCount = 0; + return ret; +} + +/** + * Free meta data. + */ +void +GNUNET_CONTAINER_meta_data_destroy (struct GNUNET_CONTAINER_MetaData *md) +{ + int i; + + if (md == NULL) + return; + for (i = 0; i < md->itemCount; i++) + GNUNET_free (md->items[i].data); + GNUNET_array_grow (md->items, md->itemCount, 0); + GNUNET_free (md); +} + +/** + * Add the current time as the publication date + * to the meta-data. + */ +void +GNUNET_CONTAINER_meta_data_add_publication_date (struct + GNUNET_CONTAINER_MetaData + *md) +{ + char *dat; + struct GNUNET_TIME_Absolute t; + + t = GNUNET_TIME_absolute_get (); + GNUNET_CONTAINER_meta_data_delete (md, EXTRACTOR_PUBLICATION_DATE, NULL); + dat = GNUNET_STRINGS_absolute_time_to_string (t); + GNUNET_CONTAINER_meta_data_insert (md, EXTRACTOR_PUBLICATION_DATE, dat); + GNUNET_free (dat); +} + +/** + * Extend metadata. + * @return GNUNET_OK on success, GNUNET_SYSERR if this entry already exists + */ +int +GNUNET_CONTAINER_meta_data_insert (struct GNUNET_CONTAINER_MetaData *md, + EXTRACTOR_KeywordType type, + const char *data) +{ + uint32_t idx; + char *p; + + GNUNET_assert (data != NULL); + for (idx = 0; idx < md->itemCount; idx++) + { + if ((md->items[idx].type == type) && + (0 == strcmp (md->items[idx].data, data))) + return GNUNET_SYSERR; + } + idx = md->itemCount; + GNUNET_array_grow (md->items, md->itemCount, md->itemCount + 1); + md->items[idx].type = type; + md->items[idx].data = p = GNUNET_strdup (data); + + /* change OS native dir separators to unix '/' and others to '_' */ + if (type == EXTRACTOR_FILENAME) + { + while (*p != '\0') + { + if (*p == DIR_SEPARATOR) + *p = '/'; + else if (*p == '\\') + *p = '_'; + p++; + } + } + + return GNUNET_OK; +} + +/** + * Remove an item. + * @return GNUNET_OK on success, GNUNET_SYSERR if the item does not exist in md + */ +int +GNUNET_CONTAINER_meta_data_delete (struct GNUNET_CONTAINER_MetaData *md, + EXTRACTOR_KeywordType type, + const char *data) +{ + uint32_t idx; + int ret = GNUNET_SYSERR; + for (idx = 0; idx < md->itemCount; idx++) + { + if ((md->items[idx].type == type) && + ((data == NULL) || (0 == strcmp (md->items[idx].data, data)))) + { + GNUNET_free (md->items[idx].data); + md->items[idx] = md->items[md->itemCount - 1]; + GNUNET_array_grow (md->items, md->itemCount, md->itemCount - 1); + if (data == NULL) + { + ret = GNUNET_OK; + continue; + } + return GNUNET_OK; + } + } + return ret; +} + +/** + * Iterate over MD entries, excluding thumbnails. + * + * @return number of entries + */ +int +GNUNET_CONTAINER_meta_data_get_contents (const struct + GNUNET_CONTAINER_MetaData *md, + GNUNET_CONTAINER_MetaDataProcessor + iterator, void *closure) +{ + uint32_t i; + uint32_t sub; + + sub = 0; + for (i = 0; i < md->itemCount; i++) + { + if (!EXTRACTOR_isBinaryType (md->items[i].type)) + { + if ((iterator != NULL) && + (GNUNET_OK != iterator (md->items[i].type, + md->items[i].data, closure))) + return GNUNET_SYSERR; + } + else + sub++; + } + return (int) (md->itemCount - sub); +} + +/** + * Iterate over MD entries + * + * @return number of entries + */ +char * +GNUNET_CONTAINER_meta_data_get_by_type (const struct GNUNET_CONTAINER_MetaData + *md, EXTRACTOR_KeywordType type) +{ + uint32_t i; + + for (i = 0; i < md->itemCount; i++) + if (type == md->items[i].type) + return GNUNET_strdup (md->items[i].data); + return NULL; +} + +/** + * Iterate over MD entries + * + * @return number of entries + */ +char * +GNUNET_CONTAINER_meta_data_get_first_by_types (const struct + GNUNET_CONTAINER_MetaData *md, + ...) +{ + char *ret; + va_list args; + EXTRACTOR_KeywordType type; + + ret = NULL; + va_start (args, md); + while (1) + { + type = va_arg (args, EXTRACTOR_KeywordType); + if (type == -1) + break; + ret = GNUNET_CONTAINER_meta_data_get_by_type (md, type); + if (ret != NULL) + break; + } + va_end (args); + return ret; +} + +/** + * Get a thumbnail from the meta-data (if present). + * + * @param thumb will be set to the thumbnail data. Must be + * freed by the caller! + * @return number of bytes in thumbnail, 0 if not available + */ +size_t +GNUNET_CONTAINER_meta_data_get_thumbnail (const struct + GNUNET_CONTAINER_MetaData * md, + unsigned char **thumb) +{ + char *encoded; + int ret; + size_t size; + + encoded = + GNUNET_CONTAINER_meta_data_get_by_type (md, EXTRACTOR_THUMBNAIL_DATA); + if (encoded == NULL) + return 0; + if (strlen (encoded) == 0) + { + GNUNET_free (encoded); + return 0; /* invalid */ + } + *thumb = NULL; + ret = EXTRACTOR_binaryDecode (encoded, thumb, &size); + GNUNET_free (encoded); + if (ret != 0) + return 0; + return size; +} + +/** + * Duplicate struct GNUNET_CONTAINER_MetaData. + */ +struct GNUNET_CONTAINER_MetaData * +GNUNET_CONTAINER_meta_data_duplicate (const struct GNUNET_CONTAINER_MetaData + *md) +{ + uint32_t i; + struct GNUNET_CONTAINER_MetaData *ret; + + if (md == NULL) + return NULL; + ret = GNUNET_CONTAINER_meta_data_create (); + for (i = 0; i < md->itemCount; i++) + GNUNET_CONTAINER_meta_data_insert (ret, md->items[i].type, + md->items[i].data); + return ret; +} + +/** + * Extract meta-data from a file. + * + * @return GNUNET_SYSERR on error, otherwise the number + * of meta-data items obtained + */ +int +GNUNET_CONTAINER_meta_data_extract_from_file (struct GNUNET_CONTAINER_MetaData + *md, const char *filename, + EXTRACTOR_ExtractorList * + extractors) +{ + EXTRACTOR_KeywordList *head; + EXTRACTOR_KeywordList *pos; + int ret; + + if (filename == NULL) + return GNUNET_SYSERR; + if (extractors == NULL) + return 0; + head = EXTRACTOR_getKeywords (extractors, filename); + head = EXTRACTOR_removeDuplicateKeywords (head, + EXTRACTOR_DUPLICATES_REMOVE_UNKNOWN); + pos = head; + ret = 0; + while (pos != NULL) + { + if (GNUNET_OK == + GNUNET_CONTAINER_meta_data_insert (md, pos->keywordType, + pos->keyword)) + ret++; + pos = pos->next; + } + EXTRACTOR_freeKeywords (head); + return ret; +} + +static unsigned int +tryCompression (char *data, unsigned int oldSize) +{ + char *tmp; + uLongf dlen; + +#ifdef compressBound + dlen = compressBound (oldSize); +#else + dlen = oldSize + (oldSize / 100) + 20; + /* documentation says 100.1% oldSize + 12 bytes, but we + should be able to overshoot by more to be safe */ +#endif + tmp = GNUNET_malloc (dlen); + if (Z_OK == compress2 ((Bytef *) tmp, + &dlen, (const Bytef *) data, oldSize, 9)) + { + if (dlen < oldSize) + { + memcpy (data, tmp, dlen); + GNUNET_free (tmp); + return dlen; + } + } + GNUNET_free (tmp); + return oldSize; +} + +/** + * Decompress input, return the decompressed data + * as output, set outputSize to the number of bytes + * that were found. + * + * @return NULL on error + */ +static char * +decompress (const char *input, + unsigned int inputSize, unsigned int outputSize) +{ + char *output; + uLongf olen; + + olen = outputSize; + output = GNUNET_malloc (olen); + if (Z_OK == uncompress ((Bytef *) output, + &olen, (const Bytef *) input, inputSize)) + { + return output; + } + else + { + GNUNET_free (output); + return NULL; + } +} + +/** + * Flag in 'version' that indicates compressed meta-data. + */ +#define HEADER_COMPRESSED 0x80000000 + +/** + * Bits in 'version' that give the version number. + */ +#define HEADER_VERSION_MASK 0x7FFFFFFF + +struct MetaDataHeader +{ + /** + * The version of the MD serialization. + * The highest bit is used to indicate + * compression. + * + * Version 0 is the current version; + * Version is 1 for a NULL pointer. + * Other version numbers are not yet defined. + */ + uint32_t version; + + /** + * How many MD entries are there? + */ + uint32_t entries; + + /** + * Size of the MD (decompressed) + */ + uint32_t size; + + /** + * This is followed by 'entries' values of type 'unsigned int' that + * correspond to EXTRACTOR_KeywordTypes. After that, the meta-data + * keywords follow (0-terminated). The MD block always ends with + * 0-termination, padding with 0 until a multiple of 8 bytes. + */ + +}; + +/** + * Serialize meta-data to target. + * + * @param size maximum number of bytes available + * @param part is it ok to just write SOME of the + * meta-data to match the size constraint, + * possibly discarding some data? + * @return number of bytes written on success, + * GNUNET_SYSERR on error (typically: not enough + * space) + */ +int +GNUNET_CONTAINER_meta_data_serialize (const struct GNUNET_CONTAINER_MetaData + *md, char *target, unsigned int max, + enum + GNUNET_CONTAINER_MetaDataSerializationOptions + part) +{ + struct MetaDataHeader *hdr; + size_t size; + size_t pos; + uint32_t i; + size_t len; + uint32_t ic; + + if (max < sizeof (struct MetaDataHeader)) + return GNUNET_SYSERR; /* far too small */ + ic = md ? md->itemCount : 0; + hdr = NULL; + while (1) + { + size = sizeof (struct MetaDataHeader); + size += sizeof (unsigned int) * ic; + for (i = 0; i < ic; i++) + size += 1 + strlen (md->items[i].data); + while (size % 8 != 0) + size++; + hdr = GNUNET_malloc (size); + hdr->version = htonl (md == NULL ? 1 : 0); + hdr->entries = htonl (ic); + for (i = 0; i < ic; i++) + ((unsigned int *) &hdr[1])[i] = + htonl ((unsigned int) md->items[i].type); + pos = sizeof (struct MetaDataHeader); + pos += sizeof (unsigned int) * ic; + for (i = 0; i < ic; i++) + { + len = strlen (md->items[i].data) + 1; + memcpy (&((char *) hdr)[pos], md->items[i].data, len); + pos += len; + } + + hdr->size = htonl (size); + if ((part & GNUNET_CONTAINER_META_DATA_SERIALIZE_NO_COMPRESS) == 0) + { + pos = tryCompression ((char *) &hdr[1], + size - sizeof (struct MetaDataHeader)); + } + else + { + pos = size - sizeof (struct MetaDataHeader); + } + if (pos < size - sizeof (struct MetaDataHeader)) + { + hdr->version = htonl (HEADER_COMPRESSED); + size = pos + sizeof (struct MetaDataHeader); + } + if (size <= max) + break; + GNUNET_free (hdr); + hdr = NULL; + + if ((part & GNUNET_CONTAINER_META_DATA_SERIALIZE_PART) == 0) + { + return GNUNET_SYSERR; /* does not fit! */ + } + /* partial serialization ok, try again with less meta-data */ + if (size > 2 * max) + ic = ic * 2 / 3; /* still far too big, make big reductions */ + else + ic--; /* small steps, we're close */ + } + GNUNET_assert (size <= max); + memcpy (target, hdr, size); + GNUNET_free (hdr); + /* extra check: deserialize! */ +#if EXTRA_CHECKS + { + struct GNUNET_CONTAINER_MetaData *mdx; + mdx = GNUNET_CONTAINER_meta_data_deserialize (target, size); + GNUNET_assert (NULL != mdx); + GNUNET_CONTAINER_meta_data_destroy (mdx); + } +#endif + return size; +} + +/** + * Estimate (!) the size of the meta-data in + * serialized form. The estimate MAY be higher + * than what is strictly needed. + */ +unsigned int +GNUNET_CONTAINER_meta_data_get_serialized_size (const struct + GNUNET_CONTAINER_MetaData *md, + enum + GNUNET_CONTAINER_MetaDataSerializationOptions + part) +{ + struct MetaDataHeader *hdr; + size_t size; + size_t pos; + uint32_t i; + size_t len; + uint32_t ic; + + ic = md ? md->itemCount : 0; + size = sizeof (struct MetaDataHeader); + size += sizeof (unsigned int) * ic; + for (i = 0; i < ic; i++) + size += 1 + strlen (md->items[i].data); + while (size % 8 != 0) + size++; + hdr = GNUNET_malloc (size); + hdr->version = htonl (md == NULL ? 1 : 0); + hdr->entries = htonl (ic); + for (i = 0; i < ic; i++) + ((unsigned int *) &hdr[1])[i] = htonl ((unsigned int) md->items[i].type); + pos = sizeof (struct MetaDataHeader); + pos += sizeof (unsigned int) * ic; + for (i = 0; i < ic; i++) + { + len = strlen (md->items[i].data) + 1; + memcpy (&((char *) hdr)[pos], md->items[i].data, len); + pos += len; + } + if ((part & GNUNET_CONTAINER_META_DATA_SERIALIZE_NO_COMPRESS) == 0) + { + pos = + tryCompression ((char *) &hdr[1], + size - sizeof (struct MetaDataHeader)); + } + else + { + pos = size - sizeof (struct MetaDataHeader); + } + if (pos < size - sizeof (struct MetaDataHeader)) + size = pos + sizeof (struct MetaDataHeader); + GNUNET_free (hdr); + return size; +} + +/** + * Deserialize meta-data. Initializes md. + * @param size number of bytes available + * @return MD on success, NULL on error (i.e. + * bad format) + */ +struct GNUNET_CONTAINER_MetaData * +GNUNET_CONTAINER_meta_data_deserialize (const char *input, unsigned int size) +{ + struct GNUNET_CONTAINER_MetaData *md; + const struct MetaDataHeader *hdr; + uint32_t ic; + char *data; + const char *cdata; + uint32_t dataSize; + int compressed; + int i; + unsigned int pos; + int len; + uint32_t version; + + if (size < sizeof (struct MetaDataHeader)) + return NULL; + hdr = (const struct MetaDataHeader *) input; + version = ntohl (MAKE_UNALIGNED (hdr->version)) & HEADER_VERSION_MASK; + if (version == 1) + return NULL; /* null pointer */ + if (version != 0) + { + GNUNET_break_op (0); /* unsupported version */ + return NULL; + } + ic = ntohl (MAKE_UNALIGNED (hdr->entries)); + compressed = + (ntohl (MAKE_UNALIGNED (hdr->version)) & HEADER_COMPRESSED) != 0; + if (compressed) + { + dataSize = + ntohl (MAKE_UNALIGNED (hdr->size)) - sizeof (struct MetaDataHeader); + if (dataSize > 2 * 1042 * 1024) + { + GNUNET_break (0); + return NULL; /* only 2 MB allowed [to make sure we don't blow + our memory limit because of a mal-formed + message... ] */ + } + data = + decompress ((const char *) &input[sizeof (struct MetaDataHeader)], + size - sizeof (struct MetaDataHeader), dataSize); + if (data == NULL) + { + GNUNET_break_op (0); + return NULL; + } + cdata = data; + } + else + { + data = NULL; + cdata = (const char *) &hdr[1]; + dataSize = size - sizeof (struct MetaDataHeader); + if (size != ntohl (MAKE_UNALIGNED (hdr->size))) + { + GNUNET_break (0); + return NULL; + } + } + + if ((sizeof (unsigned int) * ic + ic) > dataSize) + { + GNUNET_break (0); + goto FAILURE; + } + if ((ic > 0) && (cdata[dataSize - 1] != '\0')) + { + GNUNET_break (0); + goto FAILURE; + } + + md = GNUNET_CONTAINER_meta_data_create (); + GNUNET_array_grow (md->items, md->itemCount, ic); + i = 0; + pos = sizeof (unsigned int) * ic; + while ((pos < dataSize) && (i < ic)) + { + len = strlen (&cdata[pos]) + 1; + md->items[i].type = (EXTRACTOR_KeywordType) + ntohl (MAKE_UNALIGNED (((const unsigned int *) cdata)[i])); + md->items[i].data = GNUNET_strdup (&cdata[pos]); + pos += len; + i++; + } + if (i < ic) + { /* oops */ + GNUNET_CONTAINER_meta_data_destroy (md); + goto FAILURE; + } + GNUNET_free_non_null (data); + return md; +FAILURE: + GNUNET_free_non_null (data); + return NULL; /* size too small */ +} + +/** + * Test if two MDs are equal. + */ +int +GNUNET_CONTAINER_meta_data_test_equal (const struct GNUNET_CONTAINER_MetaData + *md1, + const struct GNUNET_CONTAINER_MetaData + *md2) +{ + uint32_t i; + uint32_t j; + int found; + + if (md1->itemCount != md2->itemCount) + return GNUNET_NO; + for (i = 0; i < md1->itemCount; i++) + { + found = GNUNET_NO; + for (j = 0; j < md2->itemCount; j++) + if ((md1->items[i].type == md2->items[j].type) && + (0 == strcmp (md1->items[i].data, md2->items[j].data))) + { + found = GNUNET_YES; + break; + } + if (found == GNUNET_NO) + return GNUNET_NO; + } + return GNUNET_YES; +} + + +/* end of container_meta_data.c */ diff --git a/src/util/container_multihashmap.c b/src/util/container_multihashmap.c new file mode 100644 index 000000000..a84955eb3 --- /dev/null +++ b/src/util/container_multihashmap.c @@ -0,0 +1,334 @@ +/* + This file is part of GNUnet. + (C) 2008 Christian Grothoff (and other contributing authors) + + GNUnet is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published + by the Free Software Foundation; either version 2, 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 + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with GNUnet; see the file COPYING. If not, write to the + Free Software Foundation, Inc., 59 Temple Place - Suite 330, + Boston, MA 02111-1307, USA. +*/ +/** + * @file util/container_multihashmap.c + * @brief hash map where the same key maybe present multiple times + * @author Christian Grothoff + */ + +#include "platform.h" +#include "gnunet_common.h" +#include "gnunet_container_lib.h" +#include "gnunet_crypto_lib.h" + +struct MapEntry +{ + GNUNET_HashCode key; + void *value; + struct MapEntry *next; +}; + +struct GNUNET_CONTAINER_MultiHashMap +{ + + struct MapEntry **map; + + unsigned int size; + + unsigned int map_length; +}; + +struct GNUNET_CONTAINER_MultiHashMap * +GNUNET_CONTAINER_multihashmap_create (unsigned int len) +{ + struct GNUNET_CONTAINER_MultiHashMap *ret; + + ret = GNUNET_malloc (sizeof (struct GNUNET_CONTAINER_MultiHashMap)); + ret->size = 0; + ret->map = GNUNET_malloc (len * sizeof (struct MapEntry *)); + memset (ret->map, 0, len * sizeof (struct MapEntry *)); + ret->map_length = len; + return ret; +} + +void +GNUNET_CONTAINER_multihashmap_destroy (struct GNUNET_CONTAINER_MultiHashMap + *map) +{ + unsigned int i; + struct MapEntry *e; + + for (i = 0; i < map->map_length; i++) + { + while (NULL != (e = map->map[i])) + { + map->map[i] = e->next; + GNUNET_free (e); + } + } + GNUNET_free (map->map); + GNUNET_free (map); +} + +static unsigned int +idx_of (const struct GNUNET_CONTAINER_MultiHashMap *m, + const GNUNET_HashCode * key) +{ + return (*(unsigned int *) key) % m->map_length; +} + +unsigned int +GNUNET_CONTAINER_multihashmap_size (const struct GNUNET_CONTAINER_MultiHashMap + *map) +{ + return map->size; +} + +void * +GNUNET_CONTAINER_multihashmap_get (const struct GNUNET_CONTAINER_MultiHashMap + *map, const GNUNET_HashCode * key) +{ + struct MapEntry *e; + + e = map->map[idx_of (map, key)]; + while (e != NULL) + { + if (0 == memcmp (key, &e->key, sizeof (GNUNET_HashCode))) + return e->value; + e = e->next; + } + return NULL; +} + +int +GNUNET_CONTAINER_multihashmap_iterate (const struct + GNUNET_CONTAINER_MultiHashMap *map, + GNUNET_CONTAINER_HashMapIterator it, + void *cls) +{ + int count; + unsigned int i; + struct MapEntry *e; + + count = 0; + for (i = 0; i < map->map_length; i++) + { + e = map->map[i]; + while (e != NULL) + { + if ((NULL != it) && (GNUNET_OK != it (&e->key, e->value, cls))) + return GNUNET_SYSERR; + count++; + e = e->next; + } + } + return count; +} + +int +GNUNET_CONTAINER_multihashmap_remove (struct GNUNET_CONTAINER_MultiHashMap + *map, const GNUNET_HashCode * key, + void *value) +{ + struct MapEntry *e; + struct MapEntry *p; + unsigned int i; + + i = idx_of (map, key); + p = NULL; + e = map->map[i]; + while (e != NULL) + { + if ((0 == memcmp (key, &e->key, sizeof (GNUNET_HashCode))) && + (value == e->value)) + { + if (p == NULL) + map->map[i] = e->next; + else + p->next = e->next; + GNUNET_free (e); + map->size--; + return GNUNET_YES; + } + p = e; + e = e->next; + } + return GNUNET_NO; +} + +int +GNUNET_CONTAINER_multihashmap_remove_all (struct GNUNET_CONTAINER_MultiHashMap + *map, const GNUNET_HashCode * key) +{ + struct MapEntry *e; + struct MapEntry *p; + unsigned int i; + int ret; + + ret = 0; + i = idx_of (map, key); + p = NULL; + e = map->map[i]; + while (e != NULL) + { + if (0 == memcmp (key, &e->key, sizeof (GNUNET_HashCode))) + { + if (p == NULL) + map->map[i] = e->next; + else + p->next = e->next; + GNUNET_free (e); + map->size--; + if (p == NULL) + e = map->map[i]; + else + e = p->next; + ret++; + } + else + { + p = e; + e = e->next; + } + } + return ret; +} + +int +GNUNET_CONTAINER_multihashmap_contains (const struct + GNUNET_CONTAINER_MultiHashMap *map, + const GNUNET_HashCode * key) +{ + struct MapEntry *e; + unsigned int i; + + i = idx_of (map, key); + e = map->map[i]; + while (e != NULL) + { + if (0 == memcmp (key, &e->key, sizeof (GNUNET_HashCode))) + return GNUNET_YES; + e = e->next; + } + return GNUNET_NO; +} + +static void +grow (struct GNUNET_CONTAINER_MultiHashMap *map) +{ + struct MapEntry **old; + struct MapEntry *e; + unsigned int i; + unsigned int l; + + old = map->map; + l = map->map_length; + map->map_length *= 2; + map->map = GNUNET_malloc (sizeof (struct MapEntry *) * map->map_length); + memset (map->map, 0, sizeof (struct MapEntry *) * map->map_length); + for (i = 0; i < l; i++) + { + while (NULL != (e = old[i])) + { + old[i] = e->next; + e->next = map->map[idx_of (map, &e->key)]; + map->map[idx_of (map, &e->key)] = e; + } + } + GNUNET_free (old); +} + +int +GNUNET_CONTAINER_multihashmap_put (struct GNUNET_CONTAINER_MultiHashMap *map, + const GNUNET_HashCode * key, + void *value, + enum GNUNET_CONTAINER_MultiHashMapOption + opt) +{ + struct MapEntry *e; + unsigned int i; + + i = idx_of (map, key); + if ((opt != GNUNET_CONTAINER_MULTIHASHMAPOPTION_MULTIPLE) && + (opt != GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_FAST)) + { + e = map->map[i]; + while (e != NULL) + { + if ((0 == memcmp (key, &e->key, sizeof (GNUNET_HashCode))) && + (value == e->value)) + { + if (opt == GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_ONLY) + return GNUNET_SYSERR; + e->value = value; + return GNUNET_NO; + } + e = e->next; + } + } + if (map->size / 3 > map->map_length / 4) + grow (map); + e = GNUNET_malloc (sizeof (struct MapEntry)); + e->key = *key; + e->value = value; + e->next = map->map[i]; + map->map[i] = e; + map->size++; + return GNUNET_OK; +} + +int +GNUNET_CONTAINER_multihashmap_get_multiple (const struct + GNUNET_CONTAINER_MultiHashMap + *map, const GNUNET_HashCode * key, + GNUNET_CONTAINER_HashMapIterator + it, void *cls) +{ + int count; + struct MapEntry *e; + + count = 0; + e = map->map[idx_of (map, key)]; + while (e != NULL) + { + if (0 == memcmp (key, &e->key, sizeof (GNUNET_HashCode))) + { + if ((it != NULL) && (GNUNET_OK != it (&e->key, e->value, cls))) + return GNUNET_SYSERR; + count++; + } + e = e->next; + } + return count; +} + +void * +GNUNET_CONTAINER_multihashmap_get_random (const struct + GNUNET_CONTAINER_MultiHashMap *map) +{ + unsigned int rand; + struct MapEntry *e; + e = NULL; + + if (map->size == 0) + return NULL; + + while (e == NULL) + { + rand = + GNUNET_CRYPTO_random_u32 (GNUNET_CRYPTO_QUALITY_WEAK, + map->map_length); + e = map->map[rand]; + } + + return e->value; +} + +/* end of multihashmap.c */ diff --git a/src/util/crypto_aes.c b/src/util/crypto_aes.c new file mode 100644 index 000000000..28a65dfca --- /dev/null +++ b/src/util/crypto_aes.c @@ -0,0 +1,148 @@ +/* + This file is part of GNUnet. + (C) 2001, 2002, 2003, 2004, 2005, 2006 Christian Grothoff (and other contributing authors) + + GNUnet is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published + by the Free Software Foundation; either version 2, 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 + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with GNUnet; see the file COPYING. If not, write to the + Free Software Foundation, Inc., 59 Temple Place - Suite 330, + Boston, MA 02111-1307, USA. +*/ + +/** + * @file util/crypto_aes.c + * @brief Symmetric encryption services. + * @author Christian Grothoff + * @author Ioana Patrascu + */ + +#include "platform.h" +#include "gnunet_common.h" +#include "gnunet_crypto_lib.h" +#include + +/** + * Create a new SessionKey (for AES-256). + */ +void +GNUNET_CRYPTO_aes_create_session_key (struct GNUNET_CRYPTO_AesSessionKey *key) +{ + gcry_randomize (&key->key[0], GNUNET_CRYPTO_AES_KEY_LENGTH, + GCRY_STRONG_RANDOM); + key->crc32 = + htonl (GNUNET_CRYPTO_crc32_n (key, GNUNET_CRYPTO_AES_KEY_LENGTH)); +} + +/** + * Check that a new session key is well-formed. + * + * @return GNUNET_OK if the key is valid + */ +int +GNUNET_CRYPTO_aes_check_session_key (const struct GNUNET_CRYPTO_AesSessionKey + *key) +{ + uint32_t crc; + + crc = GNUNET_CRYPTO_crc32_n (key, GNUNET_CRYPTO_AES_KEY_LENGTH); + if (ntohl (key->crc32) == crc) + return GNUNET_OK; + GNUNET_break (0); + return GNUNET_SYSERR; +} + + +/** + * Encrypt a block with the public key of another + * host that uses the same cyper. + * @param block the block to encrypt + * @param len the size of the block + * @param sessionkey the key used to encrypt + * @param iv the initialization vector to use, use INITVALUE + * for streams. + * @param result the output parameter in which to store the encrypted result + * @returns the size of the encrypted block, -1 for errors + */ +int +GNUNET_CRYPTO_aes_encrypt (const void *block, + uint16_t len, + const struct GNUNET_CRYPTO_AesSessionKey + *sessionkey, + const struct GNUNET_CRYPTO_AesInitializationVector + *iv, void *result) +{ + gcry_cipher_hd_t handle; + int rc; + + if (sessionkey->crc32 != + htonl (GNUNET_CRYPTO_crc32_n + (sessionkey, GNUNET_CRYPTO_AES_KEY_LENGTH))) + { + GNUNET_break (0); + return GNUNET_SYSERR; + } + GNUNET_assert (0 == gcry_cipher_open (&handle, + GCRY_CIPHER_AES256, + GCRY_CIPHER_MODE_CFB, 0)); + rc = gcry_cipher_setkey (handle, sessionkey, GNUNET_CRYPTO_AES_KEY_LENGTH); + GNUNET_assert ((0 == rc) || ((char) rc == GPG_ERR_WEAK_KEY)); + rc = + gcry_cipher_setiv (handle, iv, + sizeof (struct GNUNET_CRYPTO_AesInitializationVector)); + GNUNET_assert ((0 == rc) || ((char) rc == GPG_ERR_WEAK_KEY)); + GNUNET_assert (0 == gcry_cipher_encrypt (handle, result, len, block, len)); + gcry_cipher_close (handle); + return len; +} + +/** + * Decrypt a given block with the sessionkey. + * @param sessionkey the key used to decrypt + * @param block the data to decrypt, encoded as returned by encrypt + * @param size the size of the block to decrypt + * @param iv the initialization vector to use, use INITVALUE + * for streams. + * @param result address to store the result at + * @return -1 on failure, size of decrypted block on success + */ +int +GNUNET_CRYPTO_aes_decrypt (const struct GNUNET_CRYPTO_AesSessionKey + *sessionkey, const void *block, uint16_t size, + const struct GNUNET_CRYPTO_AesInitializationVector + *iv, void *result) +{ + gcry_cipher_hd_t handle; + int rc; + + if (sessionkey->crc32 != + htonl (GNUNET_CRYPTO_crc32_n + (sessionkey, GNUNET_CRYPTO_AES_KEY_LENGTH))) + { + GNUNET_break (0); + return GNUNET_SYSERR; + } + GNUNET_assert (0 == gcry_cipher_open (&handle, + GCRY_CIPHER_AES256, + GCRY_CIPHER_MODE_CFB, 0)); + rc = gcry_cipher_setkey (handle, sessionkey, GNUNET_CRYPTO_AES_KEY_LENGTH); + GNUNET_assert ((0 == rc) || ((char) rc == GPG_ERR_WEAK_KEY)); + rc = + gcry_cipher_setiv (handle, iv, + sizeof (struct GNUNET_CRYPTO_AesInitializationVector)); + GNUNET_assert ((0 == rc) || ((char) rc == GPG_ERR_WEAK_KEY)); + GNUNET_assert (0 == + gcry_cipher_decrypt (handle, result, size, block, size)); + gcry_cipher_close (handle); + return size; +} + +/* end of crypto_aes.c */ diff --git a/src/util/crypto_crc.c b/src/util/crypto_crc.c new file mode 100644 index 000000000..35d1e2576 --- /dev/null +++ b/src/util/crypto_crc.c @@ -0,0 +1,106 @@ +/* + This file is part of GNUnet. + (C) 2001, 2002, 2003, 2004, 2006 Christian Grothoff (and other contributing authors) + + GNUnet is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published + by the Free Software Foundation; either version 2, 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 + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with GNUnet; see the file COPYING. If not, write to the + Free Software Foundation, Inc., 59 Temple Place - Suite 330, + Boston, MA 02111-1307, USA. + + For the actual CRC code: + Copyright abandoned; this code is in the public domain. + Provided to GNUnet by peter@horizon.com +*/ + +/** + * @file util/crypto_crc.c + * @brief implementation of CRC32 + * @author Christian Grothoff + */ + +#include "platform.h" +#include "gnunet_common.h" +#include "gnunet_crypto_lib.h" + +/* Avoid wasting space on 8-byte longs. */ +#if UINT_MAX >= 0xffffffff +typedef unsigned int uLong; +#elif ULONG_MAX >= 0xffffffff +typedef unsigned long uLong; +#else +#error This compiler is not ANSI-compliant! +#endif + +#define Z_NULL 0 + + +#define POLYNOMIAL (uLong)0xedb88320 +static uLong crc_table[256]; + +/* + * This routine writes each crc_table entry exactly once, + * with the ccorrect final value. Thus, it is safe to call + * even on a table that someone else is using concurrently. + */ +void __attribute__ ((constructor)) GNUNET_CRYPTO_crc_init () +{ + unsigned int i, j; + uLong h = 1; + crc_table[0] = 0; + for (i = 128; i; i >>= 1) + { + h = (h >> 1) ^ ((h & 1) ? POLYNOMIAL : 0); + /* h is now crc_table[i] */ + for (j = 0; j < 256; j += 2 * i) + crc_table[i + j] = crc_table[j] ^ h; + } +} + +/* + * This computes the standard preset and inverted CRC, as used + * by most networking standards. Start by passing in an initial + * chaining value of 0, and then pass in the return value from the + * previous crc32() call. The final return value is the CRC. + * Note that this is a little-endian CRC, which is best used with + * data transmitted lsbit-first, and it should, itself, be appended + * to data in little-endian byte and bit order to preserve the + * property of detecting all burst errors of length 32 bits or less. + */ +static uLong +crc32 (uLong crc, const char *buf, size_t len) +{ + GNUNET_assert (crc_table[255] != 0); + crc ^= 0xffffffff; + while (len--) + crc = (crc >> 8) ^ crc_table[(crc ^ *buf++) & 0xff]; + return crc ^ 0xffffffff; +} + + +/** + * Compute the CRC32 checksum for the first len bytes of the buffer. + * + * @param buf the data over which we're taking the CRC + * @param len the length of the buffer + * @return the resulting CRC32 checksum + */ +int +GNUNET_CRYPTO_crc32_n (const void *buf, unsigned int len) +{ + uLong crc; + crc = crc32 (0L, Z_NULL, 0); + crc = crc32 (crc, (char *) buf, len); + return crc; +} + +/* end of crc32.c */ diff --git a/src/util/crypto_hash.c b/src/util/crypto_hash.c new file mode 100644 index 000000000..139496eac --- /dev/null +++ b/src/util/crypto_hash.c @@ -0,0 +1,791 @@ +/* + This file is part of GNUnet. + (C) 2001, 2002, 2003, 2004, 2005, 2006, 2009 Christian Grothoff (and other contributing authors) + + GNUnet is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published + by the Free Software Foundation; either version 2, 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 + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with GNUnet; see the file COPYING. If not, write to the + Free Software Foundation, Inc., 59 Temple Place - Suite 330, + Boston, MA 02111-1307, USA. + + SHA-512 code by Jean-Luc Cooke + + Copyright (c) Jean-Luc Cooke + Copyright (c) Andrew McDonald + Copyright (c) 2003 Kyle McMartin +*/ + +/** + * @file util/crypto_hash.c + * @brief SHA-512 GNUNET_CRYPTO_hash related functions + * @author Christian Grothoff + */ + +#include "platform.h" +#include "gnunet_common.h" +#include "gnunet_crypto_lib.h" +#include "gnunet_disk_lib.h" + +#define SHA512_DIGEST_SIZE 64 +#define SHA512_HMAC_BLOCK_SIZE 128 + +struct sha512_ctx +{ + unsigned long long state[8]; + unsigned int count[4]; + unsigned char buf[128]; +}; + +static unsigned long long +Ch (unsigned long long x, unsigned long long y, unsigned long long z) +{ + return z ^ (x & (y ^ z)); +} + +static unsigned long long +Maj (unsigned long long x, unsigned long long y, unsigned long long z) +{ + return (x & y) | (z & (x | y)); +} + +static unsigned long long +RORu64 (unsigned long long x, unsigned long long y) +{ + return (x >> y) | (x << (64 - y)); +} + +const unsigned long long sha512_K[80] = { + 0x428a2f98d728ae22ULL, 0x7137449123ef65cdULL, 0xb5c0fbcfec4d3b2fULL, + 0xe9b5dba58189dbbcULL, 0x3956c25bf348b538ULL, 0x59f111f1b605d019ULL, + 0x923f82a4af194f9bULL, 0xab1c5ed5da6d8118ULL, 0xd807aa98a3030242ULL, + 0x12835b0145706fbeULL, 0x243185be4ee4b28cULL, 0x550c7dc3d5ffb4e2ULL, + 0x72be5d74f27b896fULL, 0x80deb1fe3b1696b1ULL, 0x9bdc06a725c71235ULL, + 0xc19bf174cf692694ULL, 0xe49b69c19ef14ad2ULL, 0xefbe4786384f25e3ULL, + 0x0fc19dc68b8cd5b5ULL, 0x240ca1cc77ac9c65ULL, 0x2de92c6f592b0275ULL, + 0x4a7484aa6ea6e483ULL, 0x5cb0a9dcbd41fbd4ULL, 0x76f988da831153b5ULL, + 0x983e5152ee66dfabULL, 0xa831c66d2db43210ULL, 0xb00327c898fb213fULL, + 0xbf597fc7beef0ee4ULL, 0xc6e00bf33da88fc2ULL, 0xd5a79147930aa725ULL, + 0x06ca6351e003826fULL, 0x142929670a0e6e70ULL, 0x27b70a8546d22ffcULL, + 0x2e1b21385c26c926ULL, 0x4d2c6dfc5ac42aedULL, 0x53380d139d95b3dfULL, + 0x650a73548baf63deULL, 0x766a0abb3c77b2a8ULL, 0x81c2c92e47edaee6ULL, + 0x92722c851482353bULL, 0xa2bfe8a14cf10364ULL, 0xa81a664bbc423001ULL, + 0xc24b8b70d0f89791ULL, 0xc76c51a30654be30ULL, 0xd192e819d6ef5218ULL, + 0xd69906245565a910ULL, 0xf40e35855771202aULL, 0x106aa07032bbd1b8ULL, + 0x19a4c116b8d2d0c8ULL, 0x1e376c085141ab53ULL, 0x2748774cdf8eeb99ULL, + 0x34b0bcb5e19b48a8ULL, 0x391c0cb3c5c95a63ULL, 0x4ed8aa4ae3418acbULL, + 0x5b9cca4f7763e373ULL, 0x682e6ff3d6b2b8a3ULL, 0x748f82ee5defb2fcULL, + 0x78a5636f43172f60ULL, 0x84c87814a1f0ab72ULL, 0x8cc702081a6439ecULL, + 0x90befffa23631e28ULL, 0xa4506cebde82bde9ULL, 0xbef9a3f7b2c67915ULL, + 0xc67178f2e372532bULL, 0xca273eceea26619cULL, 0xd186b8c721c0c207ULL, + 0xeada7dd6cde0eb1eULL, 0xf57d4f7fee6ed178ULL, 0x06f067aa72176fbaULL, + 0x0a637dc5a2c898a6ULL, 0x113f9804bef90daeULL, 0x1b710b35131c471bULL, + 0x28db77f523047d84ULL, 0x32caab7b40c72493ULL, 0x3c9ebe0a15c9bebcULL, + 0x431d67c49c100d4cULL, 0x4cc5d4becb3e42b6ULL, 0x597f299cfc657e2aULL, + 0x5fcb6fab3ad6faecULL, 0x6c44198c4a475817ULL, +}; + +#define e0(x) (RORu64(x,28) ^ RORu64(x,34) ^ RORu64(x,39)) +#define e1(x) (RORu64(x,14) ^ RORu64(x,18) ^ RORu64(x,41)) +#define s0(x) (RORu64(x, 1) ^ RORu64(x, 8) ^ (x >> 7)) +#define s1(x) (RORu64(x,19) ^ RORu64(x,61) ^ (x >> 6)) + +/* H* initial state for SHA-512 */ +#define H0 0x6a09e667f3bcc908ULL +#define H1 0xbb67ae8584caa73bULL +#define H2 0x3c6ef372fe94f82bULL +#define H3 0xa54ff53a5f1d36f1ULL +#define H4 0x510e527fade682d1ULL +#define H5 0x9b05688c2b3e6c1fULL +#define H6 0x1f83d9abfb41bd6bULL +#define H7 0x5be0cd19137e2179ULL + +/* H'* initial state for SHA-384 */ +#define HP0 0xcbbb9d5dc1059ed8ULL +#define HP1 0x629a292a367cd507ULL +#define HP2 0x9159015a3070dd17ULL +#define HP3 0x152fecd8f70e5939ULL +#define HP4 0x67332667ffc00b31ULL +#define HP5 0x8eb44a8768581511ULL +#define HP6 0xdb0c2e0d64f98fa7ULL +#define HP7 0x47b5481dbefa4fa4ULL + +#define LOAD_OP(t1, I, W, input) \ + t1 = input[(8*I) ] & 0xff;\ + t1 <<= 8;\ + t1 |= input[(8*I)+1] & 0xff;\ + t1 <<= 8;\ + t1 |= input[(8*I)+2] & 0xff;\ + t1 <<= 8;\ + t1 |= input[(8*I)+3] & 0xff;\ + t1 <<= 8;\ + t1 |= input[(8*I)+4] & 0xff;\ + t1 <<= 8;\ + t1 |= input[(8*I)+5] & 0xff;\ + t1 <<= 8;\ + t1 |= input[(8*I)+6] & 0xff;\ + t1 <<= 8;\ + t1 |= input[(8*I)+7] & 0xff;\ + W[I] = t1; + + +#define BLEND_OP(I, W) \ + W[I] = s1(W[I-2]) + W[I-7] + s0(W[I-15]) + W[I-16]; + +static void +sha512_transform (unsigned long long *state, const unsigned char *input) +{ + unsigned long long a, b, c, d, e, f, g, h, t1, t2; + unsigned long long W[80]; + unsigned long long t0; + int i; + + /* load the input */ + for (i = 0; i < 16; i++) + { + LOAD_OP (t0, i, W, input); + } + + for (i = 16; i < 80; i++) + { + BLEND_OP (i, W); + } + + /* load the state into our registers */ + a = state[0]; + b = state[1]; + c = state[2]; + d = state[3]; + e = state[4]; + f = state[5]; + g = state[6]; + h = state[7]; + + /* now iterate */ + for (i = 0; i < 80; i += 8) + { + t1 = h + e1 (e) + Ch (e, f, g) + sha512_K[i] + W[i]; + t2 = e0 (a) + Maj (a, b, c); + d += t1; + h = t1 + t2; + t1 = g + e1 (d) + Ch (d, e, f) + sha512_K[i + 1] + W[i + 1]; + t2 = e0 (h) + Maj (h, a, b); + c += t1; + g = t1 + t2; + t1 = f + e1 (c) + Ch (c, d, e) + sha512_K[i + 2] + W[i + 2]; + t2 = e0 (g) + Maj (g, h, a); + b += t1; + f = t1 + t2; + t1 = e + e1 (b) + Ch (b, c, d) + sha512_K[i + 3] + W[i + 3]; + t2 = e0 (f) + Maj (f, g, h); + a += t1; + e = t1 + t2; + t1 = d + e1 (a) + Ch (a, b, c) + sha512_K[i + 4] + W[i + 4]; + t2 = e0 (e) + Maj (e, f, g); + h += t1; + d = t1 + t2; + t1 = c + e1 (h) + Ch (h, a, b) + sha512_K[i + 5] + W[i + 5]; + t2 = e0 (d) + Maj (d, e, f); + g += t1; + c = t1 + t2; + t1 = b + e1 (g) + Ch (g, h, a) + sha512_K[i + 6] + W[i + 6]; + t2 = e0 (c) + Maj (c, d, e); + f += t1; + b = t1 + t2; + t1 = a + e1 (f) + Ch (f, g, h) + sha512_K[i + 7] + W[i + 7]; + t2 = e0 (b) + Maj (b, c, d); + e += t1; + a = t1 + t2; + } + + state[0] += a; + state[1] += b; + state[2] += c; + state[3] += d; + state[4] += e; + state[5] += f; + state[6] += g; + state[7] += h; + + /* erase our data */ + a = b = c = d = e = f = g = h = t1 = t2 = 0; + memset (W, 0, 80 * sizeof (unsigned long long)); +} + +static void +sha512_init (struct sha512_ctx *sctx) +{ + sctx->state[0] = H0; + sctx->state[1] = H1; + sctx->state[2] = H2; + sctx->state[3] = H3; + sctx->state[4] = H4; + sctx->state[5] = H5; + sctx->state[6] = H6; + sctx->state[7] = H7; + sctx->count[0] = sctx->count[1] = sctx->count[2] = sctx->count[3] = 0; + memset (sctx->buf, 0, sizeof (sctx->buf)); +} + +static void +sha512_update (struct sha512_ctx *sctx, + const unsigned char *data, unsigned int len) +{ + unsigned int i, index, part_len; + + /* Compute number of bytes mod 128 */ + index = (unsigned int) ((sctx->count[0] >> 3) & 0x7F); + + /* Update number of bits */ + if ((sctx->count[0] += (len << 3)) < (len << 3)) + { + if ((sctx->count[1] += 1) < 1) + if ((sctx->count[2] += 1) < 1) + sctx->count[3]++; + sctx->count[1] += (len >> 29); + } + + part_len = 128 - index; + + /* Transform as many times as possible. */ + if (len >= part_len) + { + memcpy (&sctx->buf[index], data, part_len); + sha512_transform (sctx->state, sctx->buf); + + for (i = part_len; i + 127 < len; i += 128) + sha512_transform (sctx->state, &data[i]); + + index = 0; + } + else + { + i = 0; + } + + /* Buffer remaining input */ + memcpy (&sctx->buf[index], &data[i], len - i); +} + +static void +sha512_final (struct sha512_ctx *sctx, unsigned char *hash) +{ + static unsigned char padding[128] = { 0x80, }; + + unsigned int t; + unsigned long long t2; + unsigned char bits[128]; + unsigned int index, pad_len; + int i, j; + + index = pad_len = t = i = j = 0; + t2 = 0; + + /* Save number of bits */ + t = sctx->count[0]; + bits[15] = t; + t >>= 8; + bits[14] = t; + t >>= 8; + bits[13] = t; + t >>= 8; + bits[12] = t; + t = sctx->count[1]; + bits[11] = t; + t >>= 8; + bits[10] = t; + t >>= 8; + bits[9] = t; + t >>= 8; + bits[8] = t; + t = sctx->count[2]; + bits[7] = t; + t >>= 8; + bits[6] = t; + t >>= 8; + bits[5] = t; + t >>= 8; + bits[4] = t; + t = sctx->count[3]; + bits[3] = t; + t >>= 8; + bits[2] = t; + t >>= 8; + bits[1] = t; + t >>= 8; + bits[0] = t; + + /* Pad out to 112 mod 128. */ + index = (sctx->count[0] >> 3) & 0x7f; + pad_len = (index < 112) ? (112 - index) : ((128 + 112) - index); + sha512_update (sctx, padding, pad_len); + + /* Append length (before padding) */ + sha512_update (sctx, bits, 16); + + /* Store state in digest */ + for (i = j = 0; i < 8; i++, j += 8) + { + t2 = sctx->state[i]; + hash[j + 7] = (char) t2 & 0xff; + t2 >>= 8; + hash[j + 6] = (char) t2 & 0xff; + t2 >>= 8; + hash[j + 5] = (char) t2 & 0xff; + t2 >>= 8; + hash[j + 4] = (char) t2 & 0xff; + t2 >>= 8; + hash[j + 3] = (char) t2 & 0xff; + t2 >>= 8; + hash[j + 2] = (char) t2 & 0xff; + t2 >>= 8; + hash[j + 1] = (char) t2 & 0xff; + t2 >>= 8; + hash[j] = (char) t2 & 0xff; + } + + /* Zeroize sensitive information. */ + memset (sctx, 0, sizeof (struct sha512_ctx)); +} + +/** + * Hash block of given size. + * + * @param block the data to GNUNET_CRYPTO_hash, length is given as a second argument + * @param size the length of the data to GNUNET_CRYPTO_hash + * @param ret pointer to where to write the hashcode + */ +void +GNUNET_CRYPTO_hash (const void *block, unsigned int size, + GNUNET_HashCode * ret) +{ + struct sha512_ctx ctx; + + sha512_init (&ctx); + sha512_update (&ctx, block, size); + sha512_final (&ctx, (unsigned char *) ret); +} + + +/** + * Context used when hashing a file. + */ +struct FileHashContext +{ + + /** + * Function to call upon completion. + */ + GNUNET_CRYPTO_HashCompletedCallback callback; + + /** + * Closure for callback. + */ + void *callback_cls; + + /** + * IO buffer. + */ + unsigned char *buffer; + + /** + * Name of the file we are hashing. + */ + char *filename; + + /** + * Cummulated hash. + */ + struct sha512_ctx hctx; + + /** + * Blocksize. + */ + size_t bsize; + + /** + * Size of the file. + */ + unsigned long long fsize; + + /** + * Current offset. + */ + unsigned long long offset; + + /** + * Run on shutdown? + */ + int run_on_shutdown; + + /** + * File descriptor. + */ + int fd; + +}; + + +/** + * Report result of hash computation to callback + * and free associated resources. + */ +static void +file_hash_finish (struct FileHashContext *fhc, const GNUNET_HashCode * res) +{ + fhc->callback (fhc->callback_cls, res); + GNUNET_free (fhc->filename); + if (fhc->fd != -1) + GNUNET_break (0 == CLOSE (fhc->fd)); + GNUNET_free (fhc); /* also frees fhc->buffer */ +} + + +/** + * File hashing task. + * + * @param cls closure + * @param tc context + */ +static void +file_hash_task (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc) +{ + struct FileHashContext *fhc = cls; + GNUNET_HashCode res; + size_t delta; + + GNUNET_assert (fhc->offset < fhc->fsize); + delta = fhc->bsize; + if (fhc->fsize - fhc->offset < delta) + delta = fhc->fsize - fhc->offset; + if (delta != READ (fhc->fd, fhc->buffer, delta)) + { + GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING, + "read", fhc->filename); + file_hash_finish (fhc, NULL); + return; + } + sha512_update (&fhc->hctx, fhc->buffer, delta); + fhc->offset += delta; + if (fhc->offset == fhc->fsize) + { + sha512_final (&fhc->hctx, (unsigned char *) &res); + file_hash_finish (fhc, &res); + return; + } + GNUNET_SCHEDULER_add_after (tc->sched, + fhc->run_on_shutdown, + GNUNET_SCHEDULER_PRIORITY_KEEP, + GNUNET_SCHEDULER_NO_PREREQUISITE_TASK, + &file_hash_task, fhc); +} + + +/** + * Compute the hash of an entire file. + * + * @param sched scheduler to use + * @param priority scheduling priority to use + * @param run_on_shutdown should we complete even on shutdown? + * @param filename name of file to hash + * @param blocksize number of bytes to process in one task + * @param callback function to call upon completion + * @param callback_cls closure for callback + */ +void +GNUNET_CRYPTO_hash_file (struct GNUNET_SCHEDULER_Handle *sched, + enum GNUNET_SCHEDULER_Priority priority, + int run_on_shutdown, + const char *filename, + size_t blocksize, + GNUNET_CRYPTO_HashCompletedCallback callback, + void *callback_cls) +{ + struct FileHashContext *fhc; + + GNUNET_assert (blocksize > 0); + fhc = GNUNET_malloc (sizeof (struct FileHashContext) + blocksize); + fhc->callback = callback; + fhc->callback_cls = callback_cls; + fhc->buffer = (unsigned char *) &fhc[1]; + fhc->filename = GNUNET_strdup (filename); + fhc->fd = -1; + sha512_init (&fhc->hctx); + fhc->bsize = blocksize; + if (GNUNET_OK != GNUNET_DISK_file_size (filename, &fhc->fsize, GNUNET_NO)) + { + file_hash_finish (fhc, NULL); + return; + } + fhc->run_on_shutdown = run_on_shutdown; + fhc->fd = GNUNET_DISK_file_open (filename, O_RDONLY | O_LARGEFILE); + if (fhc->fd == -1) + { + file_hash_finish (fhc, NULL); + return; + } + GNUNET_SCHEDULER_add_after (sched, + run_on_shutdown, + priority, + GNUNET_SCHEDULER_NO_PREREQUISITE_TASK, + &file_hash_task, fhc); +} + + +/* ***************** binary-ASCII encoding *************** */ + +/** + * 32 characters for encoding (GNUNET_CRYPTO_hash => 32 characters) + */ +static char *encTable__ = "0123456789ABCDEFGHIJKLMNOPQRSTUV"; + +static unsigned int +getValue__ (unsigned char a) +{ + if ((a >= '0') && (a <= '9')) + return a - '0'; + if ((a >= 'A') && (a <= 'V')) + return (a - 'A' + 10); + return -1; +} + +/** + * Convert GNUNET_CRYPTO_hash to ASCII encoding. The ASCII encoding is rather + * GNUnet specific. It was chosen such that it only uses characters + * in [0-9A-V], can be produced without complex arithmetics and uses a + * small number of characters. The GNUnet encoding uses 102 + * characters plus a null terminator. + * + * @param block the GNUNET_CRYPTO_hash code + * @param result where to store the encoding (struct GNUNET_CRYPTO_HashAsciiEncoded can be + * safely cast to char*, a '\0' termination is set). + */ +void +GNUNET_CRYPTO_hash_to_enc (const GNUNET_HashCode * block, + struct GNUNET_CRYPTO_HashAsciiEncoded *result) +{ + unsigned int wpos; + unsigned int rpos; + unsigned int bits; + unsigned int vbit; + + GNUNET_assert (block != NULL); + GNUNET_assert (result != NULL); + vbit = 0; + wpos = 0; + rpos = 0; + bits = 0; + while ((rpos < sizeof (GNUNET_HashCode)) || (vbit > 0)) + { + if ((rpos < sizeof (GNUNET_HashCode)) && (vbit < 5)) + { + bits = (bits << 8) | ((unsigned char *) block)[rpos++]; /* eat 8 more bits */ + vbit += 8; + } + if (vbit < 5) + { + bits <<= (5 - vbit); /* zero-padding */ + GNUNET_assert (vbit == 2); /* padding by 3: 512+3 mod 5 == 0 */ + vbit = 5; + } + GNUNET_assert (wpos < + sizeof (struct GNUNET_CRYPTO_HashAsciiEncoded) - 1); + result->encoding[wpos++] = encTable__[(bits >> (vbit - 5)) & 31]; + vbit -= 5; + } + GNUNET_assert (wpos == sizeof (struct GNUNET_CRYPTO_HashAsciiEncoded) - 1); + GNUNET_assert (vbit == 0); + result->encoding[wpos] = '\0'; +} + +/** + * Convert ASCII encoding back to GNUNET_CRYPTO_hash + * + * @param enc the encoding + * @param result where to store the GNUNET_CRYPTO_hash code + * @return GNUNET_OK on success, GNUNET_SYSERR if result has the wrong encoding + */ +int +GNUNET_CRYPTO_hash_from_string (const char *enc, GNUNET_HashCode * result) +{ + unsigned int rpos; + unsigned int wpos; + unsigned int bits; + unsigned int vbit; + + if (strlen (enc) != sizeof (struct GNUNET_CRYPTO_HashAsciiEncoded) - 1) + return GNUNET_SYSERR; + + vbit = 2; /* padding! */ + wpos = sizeof (GNUNET_HashCode); + rpos = sizeof (struct GNUNET_CRYPTO_HashAsciiEncoded) - 1; + bits = getValue__ (enc[--rpos]) >> 3; + while (wpos > 0) + { + GNUNET_assert (rpos > 0); + bits = (getValue__ (enc[--rpos]) << vbit) | bits; + vbit += 5; + if (vbit >= 8) + { + ((unsigned char *) result)[--wpos] = (unsigned char) bits; + bits >>= 8; + vbit -= 8; + } + } + GNUNET_assert (rpos == 0); + GNUNET_assert (vbit == 0); + return GNUNET_OK; +} + +/** + * Compute the distance between 2 hashcodes. The computation must be + * fast, not involve bits[0] or bits[4] (they're used elsewhere), and be + * somewhat consistent. And of course, the result should be a positive + * number. + * + * @returns a positive number which is a measure for + * hashcode proximity. + */ +unsigned int +GNUNET_CRYPTO_hash_distance_u32 (const GNUNET_HashCode * a, + const GNUNET_HashCode * b) +{ + unsigned int x1 = (a->bits[1] - b->bits[1]) >> 16; + unsigned int x2 = (b->bits[1] - a->bits[1]) >> 16; + return (x1 * x2); +} + +void +GNUNET_CRYPTO_hash_create_random (GNUNET_HashCode * result) +{ + int i; + for (i = (sizeof (GNUNET_HashCode) / sizeof (unsigned int)) - 1; i >= 0; + i--) + result->bits[i] = rand (); +} + +void +GNUNET_CRYPTO_hash_difference (const GNUNET_HashCode * a, + const GNUNET_HashCode * b, + GNUNET_HashCode * result) +{ + int i; + for (i = (sizeof (GNUNET_HashCode) / sizeof (unsigned int)) - 1; i >= 0; + i--) + result->bits[i] = b->bits[i] - a->bits[i]; +} + +void +GNUNET_CRYPTO_hash_sum (const GNUNET_HashCode * a, + const GNUNET_HashCode * delta, + GNUNET_HashCode * result) +{ + int i; + for (i = (sizeof (GNUNET_HashCode) / sizeof (unsigned int)) - 1; i >= 0; + i--) + result->bits[i] = delta->bits[i] + a->bits[i]; +} + +void +GNUNET_CRYPTO_hash_xor (const GNUNET_HashCode * a, + const GNUNET_HashCode * b, GNUNET_HashCode * result) +{ + int i; + for (i = (sizeof (GNUNET_HashCode) / sizeof (unsigned int)) - 1; i >= 0; + i--) + result->bits[i] = a->bits[i] ^ b->bits[i]; +} + +/** + * Convert a hashcode into a key. + */ +void +GNUNET_CRYPTO_hash_to_AES_key (const GNUNET_HashCode * hc, + struct GNUNET_CRYPTO_AesSessionKey *skey, + struct GNUNET_CRYPTO_AesInitializationVector + *iv) +{ + GNUNET_assert (sizeof (GNUNET_HashCode) >= + GNUNET_CRYPTO_AES_KEY_LENGTH + + sizeof (struct GNUNET_CRYPTO_AesInitializationVector)); + memcpy (skey, hc, GNUNET_CRYPTO_AES_KEY_LENGTH); + skey->crc32 = + htonl (GNUNET_CRYPTO_crc32_n (skey, GNUNET_CRYPTO_AES_KEY_LENGTH)); + memcpy (iv, &((char *) hc)[GNUNET_CRYPTO_AES_KEY_LENGTH], + sizeof (struct GNUNET_CRYPTO_AesInitializationVector)); +} + +/** + * Obtain a bit from a hashcode. + * @param code the GNUNET_CRYPTO_hash to index bit-wise + * @param bit index into the hashcode, [0...511] + * @return Bit \a bit from hashcode \a code, -1 for invalid index + */ +int +GNUNET_CRYPTO_hash_get_bit (const GNUNET_HashCode * code, unsigned int bit) +{ + GNUNET_assert (bit < 8 * sizeof (GNUNET_HashCode)); + return (((unsigned char *) code)[bit >> 3] & (1 << (bit & 7))) > 0; +} + +/** + * Compare function for HashCodes, producing a total ordering + * of all hashcodes. + * @return 1 if h1 > h2, -1 if h1 < h2 and 0 if h1 == h2. + */ +int +GNUNET_CRYPTO_hash_cmp (const GNUNET_HashCode * h1, + const GNUNET_HashCode * h2) +{ + unsigned int *i1; + unsigned int *i2; + int i; + + i1 = (unsigned int *) h1; + i2 = (unsigned int *) h2; + for (i = (sizeof (GNUNET_HashCode) / sizeof (unsigned int)) - 1; i >= 0; + i--) + { + if (i1[i] > i2[i]) + return 1; + if (i1[i] < i2[i]) + return -1; + } + return 0; +} + +/** + * Find out which of the two GNUNET_CRYPTO_hash codes is closer to target + * in the XOR metric (Kademlia). + * @return -1 if h1 is closer, 1 if h2 is closer and 0 if h1==h2. + */ +int +GNUNET_CRYPTO_hash_xorcmp (const GNUNET_HashCode * h1, + const GNUNET_HashCode * h2, + const GNUNET_HashCode * target) +{ + int i; + unsigned int d1; + unsigned int d2; + + for (i = sizeof (GNUNET_HashCode) / sizeof (unsigned int) - 1; i >= 0; i--) + { + d1 = ((unsigned int *) h1)[i] ^ ((unsigned int *) target)[i]; + d2 = ((unsigned int *) h2)[i] ^ ((unsigned int *) target)[i]; + if (d1 > d2) + return 1; + else if (d1 < d2) + return -1; + } + return 0; +} + +/* end of hashing.c */ diff --git a/src/util/crypto_ksk.c b/src/util/crypto_ksk.c new file mode 100644 index 000000000..c3461ae61 --- /dev/null +++ b/src/util/crypto_ksk.c @@ -0,0 +1,791 @@ +/* + This file is part of GNUnet. + Copyright (C) 1994, 1996, 1998, 2001, 2002, 2003 Free Software Foundation, Inc. + Copyright (C) 2004, 2005, 2006 Christian Grothoff (and other contributing authors) + + GNUnet is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published + by the Free Software Foundation; either version 2, 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 + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with GNUnet; see the file COPYING. If not, write to the + Free Software Foundation, Inc., 59 Temple Place - Suite 330, + Boston, MA 02111-1307, USA. + + Note: This code is based on code from libgcrypt + The code was adapted for GNUnet to support RSA-key generation + based on weak, pseudo-random keys. Do NOT use to generate + ordinary RSA keys! +*/ + + +/** + * @file util/crypto_ksk.c + * @brief implementation of RSA-Key generation for KBlocks + * (do NOT use for pseudonyms or hostkeys!) + * @author Christian Grothoff + */ + +#include "platform.h" +#include "gnunet_common.h" +#include "gnunet_crypto_lib.h" +#include +#include + +/** + * Log an error message at log-level 'level' that indicates + * a failure of the command 'cmd' with the message given + * by gcry_strerror(rc). + */ +#define LOG_GCRY(level, cmd, rc) do { GNUNET_log(level, _("`%s' failed at %s:%d with error: %s\n"), cmd, __FILE__, __LINE__, gcry_strerror(rc)); } while(0); + + +typedef struct +{ + mpz_t n; /* public modulus */ + mpz_t e; /* public exponent */ + mpz_t d; /* exponent */ + mpz_t p; /* prime p. */ + mpz_t q; /* prime q. */ + mpz_t u; /* inverse of p mod q. */ +} KBlock_secret_key; + +/** + * The private information of an RSA key pair. + * NOTE: this must match the definition in crypto_rsa.c + */ +struct GNUNET_CRYPTO_RsaPrivateKey +{ + gcry_sexp_t sexp; +}; + + +/* Note: 2 is not included because it can be tested more easily by + looking at bit 0. The last entry in this list is marked by a zero */ +static uint16_t small_prime_numbers[] = { + 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, 43, + 47, 53, 59, 61, 67, 71, 73, 79, 83, 89, 97, 101, + 103, 107, 109, 113, 127, 131, 137, 139, 149, 151, + 157, 163, 167, 173, 179, 181, 191, 193, 197, 199, + 211, 223, 227, 229, 233, 239, 241, 251, 257, 263, + 269, 271, 277, 281, 283, 293, 307, 311, 313, 317, + 331, 337, 347, 349, 353, 359, 367, 373, 379, 383, + 389, 397, 401, 409, 419, 421, 431, 433, 439, 443, + 449, 457, 461, 463, 467, 479, 487, 491, 499, 503, + 509, 521, 523, 541, 547, 557, 563, 569, 571, 577, + 587, 593, 599, 601, 607, 613, 617, 619, 631, 641, + 643, 647, 653, 659, 661, 673, 677, 683, 691, 701, + 709, 719, 727, 733, 739, 743, 751, 757, 761, 769, + 773, 787, 797, 809, 811, 821, 823, 827, 829, 839, + 853, 857, 859, 863, 877, 881, 883, 887, 907, 911, + 919, 929, 937, 941, 947, 953, 967, 971, 977, 983, + 991, 997, 1009, 1013, 1019, 1021, 1031, 1033, + 1039, 1049, 1051, 1061, 1063, 1069, 1087, 1091, + 1093, 1097, 1103, 1109, 1117, 1123, 1129, 1151, + 1153, 1163, 1171, 1181, 1187, 1193, 1201, 1213, + 1217, 1223, 1229, 1231, 1237, 1249, 1259, 1277, + 1279, 1283, 1289, 1291, 1297, 1301, 1303, 1307, + 1319, 1321, 1327, 1361, 1367, 1373, 1381, 1399, + 1409, 1423, 1427, 1429, 1433, 1439, 1447, 1451, + 1453, 1459, 1471, 1481, 1483, 1487, 1489, 1493, + 1499, 1511, 1523, 1531, 1543, 1549, 1553, 1559, + 1567, 1571, 1579, 1583, 1597, 1601, 1607, 1609, + 1613, 1619, 1621, 1627, 1637, 1657, 1663, 1667, + 1669, 1693, 1697, 1699, 1709, 1721, 1723, 1733, + 1741, 1747, 1753, 1759, 1777, 1783, 1787, 1789, + 1801, 1811, 1823, 1831, 1847, 1861, 1867, 1871, + 1873, 1877, 1879, 1889, 1901, 1907, 1913, 1931, + 1933, 1949, 1951, 1973, 1979, 1987, 1993, 1997, + 1999, 2003, 2011, 2017, 2027, 2029, 2039, 2053, + 2063, 2069, 2081, 2083, 2087, 2089, 2099, 2111, + 2113, 2129, 2131, 2137, 2141, 2143, 2153, 2161, + 2179, 2203, 2207, 2213, 2221, 2237, 2239, 2243, + 2251, 2267, 2269, 2273, 2281, 2287, 2293, 2297, + 2309, 2311, 2333, 2339, 2341, 2347, 2351, 2357, + 2371, 2377, 2381, 2383, 2389, 2393, 2399, 2411, + 2417, 2423, 2437, 2441, 2447, 2459, 2467, 2473, + 2477, 2503, 2521, 2531, 2539, 2543, 2549, 2551, + 2557, 2579, 2591, 2593, 2609, 2617, 2621, 2633, + 2647, 2657, 2659, 2663, 2671, 2677, 2683, 2687, + 2689, 2693, 2699, 2707, 2711, 2713, 2719, 2729, + 2731, 2741, 2749, 2753, 2767, 2777, 2789, 2791, + 2797, 2801, 2803, 2819, 2833, 2837, 2843, 2851, + 2857, 2861, 2879, 2887, 2897, 2903, 2909, 2917, + 2927, 2939, 2953, 2957, 2963, 2969, 2971, 2999, + 3001, 3011, 3019, 3023, 3037, 3041, 3049, 3061, + 3067, 3079, 3083, 3089, 3109, 3119, 3121, 3137, + 3163, 3167, 3169, 3181, 3187, 3191, 3203, 3209, + 3217, 3221, 3229, 3251, 3253, 3257, 3259, 3271, + 3299, 3301, 3307, 3313, 3319, 3323, 3329, 3331, + 3343, 3347, 3359, 3361, 3371, 3373, 3389, 3391, + 3407, 3413, 3433, 3449, 3457, 3461, 3463, 3467, + 3469, 3491, 3499, 3511, 3517, 3527, 3529, 3533, + 3539, 3541, 3547, 3557, 3559, 3571, 3581, 3583, + 3593, 3607, 3613, 3617, 3623, 3631, 3637, 3643, + 3659, 3671, 3673, 3677, 3691, 3697, 3701, 3709, + 3719, 3727, 3733, 3739, 3761, 3767, 3769, 3779, + 3793, 3797, 3803, 3821, 3823, 3833, 3847, 3851, + 3853, 3863, 3877, 3881, 3889, 3907, 3911, 3917, + 3919, 3923, 3929, 3931, 3943, 3947, 3967, 3989, + 4001, 4003, 4007, 4013, 4019, 4021, 4027, 4049, + 4051, 4057, 4073, 4079, 4091, 4093, 4099, 4111, + 4127, 4129, 4133, 4139, 4153, 4157, 4159, 4177, + 4201, 4211, 4217, 4219, 4229, 4231, 4241, 4243, + 4253, 4259, 4261, 4271, 4273, 4283, 4289, 4297, + 4327, 4337, 4339, 4349, 4357, 4363, 4373, 4391, + 4397, 4409, 4421, 4423, 4441, 4447, 4451, 4457, + 4463, 4481, 4483, 4493, 4507, 4513, 4517, 4519, + 4523, 4547, 4549, 4561, 4567, 4583, 4591, 4597, + 4603, 4621, 4637, 4639, 4643, 4649, 4651, 4657, + 4663, 4673, 4679, 4691, 4703, 4721, 4723, 4729, + 4733, 4751, 4759, 4783, 4787, 4789, 4793, 4799, + 4801, 4813, 4817, 4831, 4861, 4871, 4877, 4889, + 4903, 4909, 4919, 4931, 4933, 4937, 4943, 4951, + 4957, 4967, 4969, 4973, 4987, 4993, 4999, + 0 +}; + +#define DIM(v) (sizeof(v)/sizeof((v)[0])) +static int no_of_small_prime_numbers = DIM (small_prime_numbers) - 1; + + + static unsigned int + get_nbits (mpz_t a) +{ + return mpz_sizeinbase (a, 2); +} + +/** + * Count the number of zerobits at the low end of A + */ +static unsigned int +get_trailing_zeros (mpz_t a) +{ + unsigned int count = 0; + unsigned int nbits = get_nbits (a); + + while ((mpz_tstbit (a, count)) && (count < nbits)) + count++; + return count; +} + +/** + * Set bit N of A. and clear all bits above + */ +static void +set_highbit (mpz_t a, unsigned int n) +{ + unsigned int nbits; + + nbits = get_nbits (a); + while (nbits > n) + mpz_clrbit (a, nbits--); + mpz_setbit (a, n); +} + +static void +mpz_randomize (mpz_t n, unsigned int nbits, GNUNET_HashCode * rnd) +{ + GNUNET_HashCode *tmp; + int cnt; + int i; + + cnt = (nbits / sizeof (GNUNET_HashCode) / 8) + 1; + tmp = GNUNET_malloc (sizeof (GNUNET_HashCode) * cnt); + + tmp[0] = *rnd; + for (i = 0; i < cnt - 1; i++) + { + GNUNET_CRYPTO_hash (&tmp[i], sizeof (GNUNET_HashCode), &tmp[i + 1]); + } + *rnd = tmp[cnt - 1]; + mpz_import (n, cnt * sizeof (GNUNET_HashCode) / sizeof (unsigned int), + 1, sizeof (unsigned int), 1, 0, tmp); + GNUNET_free (tmp); + i = get_nbits (n); + while (i > nbits) + mpz_clrbit (n, i--); +} + +/** + * Return true if n is probably a prime + */ +static int +is_prime (mpz_t n, int steps, GNUNET_HashCode * hc) +{ + mpz_t x; + mpz_t y; + mpz_t z; + mpz_t nminus1; + mpz_t a2; + mpz_t q; + unsigned int i, j, k; + int rc = 0; + unsigned int nbits; + + mpz_init (x); + mpz_init (y); + mpz_init (z); + mpz_init (nminus1); + mpz_init_set_ui (a2, 2); + nbits = get_nbits (n); + mpz_sub_ui (nminus1, n, 1); + + /* Find q and k, so that n = 1 + 2^k * q . */ + mpz_init_set (q, nminus1); + k = get_trailing_zeros (q); + mpz_tdiv_q_2exp (q, q, k); + + for (i = 0; i < steps; i++) + { + if (!i) + { + mpz_set_ui (x, 2); + } + else + { + mpz_randomize (x, nbits, hc); + + /* Make sure that the number is smaller than the prime and + keep the randomness of the high bit. */ + if (mpz_tstbit (x, nbits - 2)) + { + set_highbit (x, nbits - 2); /* Clear all higher bits. */ + } + else + { + set_highbit (x, nbits - 2); + mpz_clrbit (x, nbits - 2); + } + GNUNET_assert (mpz_cmp (x, nminus1) < 0 && mpz_cmp_ui (x, 1) > 0); + } + mpz_powm (y, x, q, n); + if (mpz_cmp_ui (y, 1) && mpz_cmp (y, nminus1)) + { + for (j = 1; j < k && mpz_cmp (y, nminus1); j++) + { + mpz_powm (y, y, a2, n); + if (!mpz_cmp_ui (y, 1)) + goto leave; /* Not a prime. */ + } + if (mpz_cmp (y, nminus1)) + goto leave; /* Not a prime. */ + } + } + rc = 1; /* May be a prime. */ + +leave: + mpz_clear (x); + mpz_clear (y); + mpz_clear (z); + mpz_clear (nminus1); + mpz_clear (q); + mpz_clear (a2); + + return rc; +} + +static void +gen_prime (mpz_t ptest, unsigned int nbits, GNUNET_HashCode * hc) +{ + mpz_t prime, pminus1, val_2, val_3, result; + int i; + unsigned x, step; + int *mods; + mpz_t tmp; + + GNUNET_assert (nbits >= 16); + + mods = GNUNET_malloc (no_of_small_prime_numbers * sizeof (*mods)); + /* Make nbits fit into mpz_t implementation. */ + mpz_init_set_ui (val_2, 2); + mpz_init_set_ui (val_3, 3); + mpz_init (prime); + mpz_init (result); + mpz_init (pminus1); + mpz_init (ptest); + while (1) + { + /* generate a random number */ + mpz_randomize (prime, nbits, hc); + /* Set high order bit to 1, set low order bit to 1. If we are + generating a secret prime we are most probably doing that + for RSA, to make sure that the modulus does have the + requested key size we set the 2 high order bits. */ + set_highbit (prime, nbits - 1); + mpz_setbit (prime, nbits - 2); + mpz_setbit (prime, 0); + + /* Calculate all remainders. */ + mpz_init (tmp); + for (i = 0; (x = small_prime_numbers[i]); i++) + mods[i] = mpz_fdiv_r_ui (tmp, prime, x); + mpz_clear (tmp); + /* Now try some primes starting with prime. */ + for (step = 0; step < 20000; step += 2) + { + /* Check against all the small primes we have in mods. */ + for (i = 0; (x = small_prime_numbers[i]); i++) + { + while (mods[i] + step >= x) + mods[i] -= x; + if (!(mods[i] + step)) + break; + } + if (x) + continue; /* Found a multiple of an already known prime. */ + + mpz_add_ui (ptest, prime, step); + if (!mpz_tstbit (ptest, nbits - 2)) + break; + + /* Do a fast Fermat test now. */ + mpz_sub_ui (pminus1, ptest, 1); + mpz_powm (result, val_2, pminus1, ptest); + if ((!mpz_cmp_ui (result, 1)) && (is_prime (ptest, 5, hc))) + { + /* Got it. */ + mpz_clear (val_2); + mpz_clear (val_3); + mpz_clear (result); + mpz_clear (pminus1); + mpz_clear (prime); + GNUNET_free (mods); + return; + } + } + } +} + +/** + * Find the greatest common divisor G of A and B. + * Return: 1 if this 1, 0 in all other cases + */ +static int +test_gcd (mpz_t g, mpz_t xa, mpz_t xb) +{ + mpz_t a, b; + + mpz_init_set (a, xa); + mpz_init_set (b, xb); + + /* TAOCP Vol II, 4.5.2, Algorithm A */ + while (mpz_cmp_ui (b, 0)) + { + mpz_fdiv_r (g, a, b); /* g used as temorary variable */ + mpz_set (a, b); + mpz_set (b, g); + } + mpz_set (g, a); + + mpz_clear (a); + mpz_clear (b); + return (0 == mpz_cmp_ui (g, 1)); +} + +/** + * Generate a key pair with a key of size NBITS. + * @param sk where to store the key + * @param nbits the number of bits to use + * @param hc the HC to use for PRNG (modified!) + */ +static void +generate_kblock_key (KBlock_secret_key * sk, + unsigned int nbits, GNUNET_HashCode * hc) +{ + mpz_t t1, t2; + mpz_t phi; /* helper: (p-1)(q-1) */ + mpz_t g; + mpz_t f; + + /* make sure that nbits is even so that we generate p, q of equal size */ + if ((nbits & 1)) + nbits++; + + mpz_init_set_ui (sk->e, 257); + mpz_init (sk->n); + mpz_init (sk->p); + mpz_init (sk->q); + mpz_init (sk->d); + mpz_init (sk->u); + + mpz_init (t1); + mpz_init (t2); + mpz_init (phi); + mpz_init (g); + mpz_init (f); + + do + { + do + { + mpz_clear (sk->p); + mpz_clear (sk->q); + gen_prime (sk->p, nbits / 2, hc); + gen_prime (sk->q, nbits / 2, hc); + + if (mpz_cmp (sk->p, sk->q) > 0) /* p shall be smaller than q (for calc of u) */ + mpz_swap (sk->p, sk->q); + /* calculate the modulus */ + mpz_mul (sk->n, sk->p, sk->q); + } + while (get_nbits (sk->n) != nbits); + + /* calculate Euler totient: phi = (p-1)(q-1) */ + mpz_sub_ui (t1, sk->p, 1); + mpz_sub_ui (t2, sk->q, 1); + mpz_mul (phi, t1, t2); + mpz_gcd (g, t1, t2); + mpz_fdiv_q (f, phi, g); + + while (0 == test_gcd (t1, sk->e, phi)) + { /* (while gcd is not 1) */ + mpz_add_ui (sk->e, sk->e, 2); + } + + /* calculate the secret key d = e^1 mod phi */ + } + while ((0 == mpz_invert (sk->d, sk->e, f)) || + (0 == mpz_invert (sk->u, sk->p, sk->q))); + + mpz_clear (t1); + mpz_clear (t2); + mpz_clear (phi); + mpz_clear (f); + mpz_clear (g); +} + + +/** + * Internal representation of the private key. + */ +struct KskRsaPrivateKeyBinaryEncoded +{ + /** + * Total size of the structure, in bytes, in big-endian! + */ + uint16_t len GNUNET_PACKED; + uint16_t sizen GNUNET_PACKED; /* in big-endian! */ + uint16_t sizee GNUNET_PACKED; /* in big-endian! */ + uint16_t sized GNUNET_PACKED; /* in big-endian! */ + uint16_t sizep GNUNET_PACKED; /* in big-endian! */ + uint16_t sizeq GNUNET_PACKED; /* in big-endian! */ + uint16_t sizedmp1 GNUNET_PACKED; /* in big-endian! */ + uint16_t sizedmq1 GNUNET_PACKED; /* in big-endian! */ + /* followed by the actual values */ +}; + + +/** + * Deterministically (!) create a hostkey using only the + * given HashCode as input to the PRNG. + */ +static struct KskRsaPrivateKeyBinaryEncoded * +makeKblockKeyInternal (const GNUNET_HashCode * hc) +{ + KBlock_secret_key sk; + GNUNET_HashCode hx; + void *pbu[6]; + mpz_t *pkv[6]; + size_t sizes[6]; + struct KskRsaPrivateKeyBinaryEncoded *retval; + int i; + size_t size; + + hx = *hc; + generate_kblock_key (&sk, 1024, /* at least 10x as fast than 2048 bits + -- we simply cannot afford 2048 bits + even on modern hardware, and especially + not since clearly a dictionary attack + will still be much cheaper + than breaking a 1024 bit RSA key. + If an adversary can spend the time to + break a 1024 bit RSA key just to forge + a signature -- SO BE IT. [ CG, 6/2005 ] */ + &hx); + pkv[0] = &sk.n; + pkv[1] = &sk.e; + pkv[2] = &sk.d; + pkv[3] = &sk.p; + pkv[4] = &sk.q; + pkv[5] = &sk.u; + size = sizeof (struct KskRsaPrivateKeyBinaryEncoded); + for (i = 0; i < 6; i++) + { + pbu[i] = mpz_export (NULL, &sizes[i], 1, /* most significant word first */ + 1, /* unit is bytes */ + 1, /* big endian */ + 0, /* nails */ + *pkv[i]); + size += sizes[i]; + } + GNUNET_assert (size < 65536); + retval = GNUNET_malloc (size); + retval->len = htons (size); + i = 0; + retval->sizen = htons (sizes[0]); + memcpy (&((char *) &retval[1])[i], pbu[0], sizes[0]); + i += sizes[0]; + retval->sizee = htons (sizes[1]); + memcpy (&((char *) &retval[1])[i], pbu[1], sizes[1]); + i += sizes[1]; + retval->sized = htons (sizes[2]); + memcpy (&((char *) &retval[1])[i], pbu[2], sizes[2]); + i += sizes[2]; + /* swap p and q! */ + retval->sizep = htons (sizes[4]); + memcpy (&((char *) &retval[1])[i], pbu[4], sizes[4]); + i += sizes[4]; + retval->sizeq = htons (sizes[3]); + memcpy (&((char *) &retval[1])[i], pbu[3], sizes[3]); + i += sizes[3]; + retval->sizedmp1 = htons (0); + retval->sizedmq1 = htons (0); + memcpy (&((char *) &retval[1])[i], pbu[5], sizes[5]); + for (i = 0; i < 6; i++) + { + mpz_clear (*pkv[i]); + free (pbu[i]); + } + return retval; +} + + +/** + * Decode the internal format into the format used + * by libgcrypt. + */ +static struct GNUNET_CRYPTO_RsaPrivateKey * +ksk_decode_key (const struct KskRsaPrivateKeyBinaryEncoded *encoding) +{ + struct GNUNET_CRYPTO_RsaPrivateKey *ret; + gcry_sexp_t res; + gcry_mpi_t n, e, d, p, q, u; + int rc; + size_t size; + int pos; + + pos = 0; + size = ntohs (encoding->sizen); + rc = gcry_mpi_scan (&n, + GCRYMPI_FMT_USG, + &((const unsigned char *) (&encoding[1]))[pos], + size, &size); + pos += ntohs (encoding->sizen); + if (rc) + { + LOG_GCRY (GNUNET_ERROR_TYPE_ERROR, "gcry_mpi_scan", rc); + return NULL; + } + size = ntohs (encoding->sizee); + rc = gcry_mpi_scan (&e, + GCRYMPI_FMT_USG, + &((const unsigned char *) (&encoding[1]))[pos], + size, &size); + pos += ntohs (encoding->sizee); + if (rc) + { + LOG_GCRY (GNUNET_ERROR_TYPE_ERROR, "gcry_mpi_scan", rc); + gcry_mpi_release (n); + return NULL; + } + size = ntohs (encoding->sized); + rc = gcry_mpi_scan (&d, + GCRYMPI_FMT_USG, + &((const unsigned char *) (&encoding[1]))[pos], + size, &size); + pos += ntohs (encoding->sized); + if (rc) + { + LOG_GCRY (GNUNET_ERROR_TYPE_ERROR, "gcry_mpi_scan", rc); + gcry_mpi_release (n); + gcry_mpi_release (e); + return NULL; + } + /* swap p and q! */ + size = ntohs (encoding->sizep); + if (size > 0) + { + rc = gcry_mpi_scan (&q, + GCRYMPI_FMT_USG, + &((const unsigned char *) (&encoding[1]))[pos], + size, &size); + pos += ntohs (encoding->sizep); + if (rc) + { + LOG_GCRY (GNUNET_ERROR_TYPE_ERROR, "gcry_mpi_scan", rc); + gcry_mpi_release (n); + gcry_mpi_release (e); + gcry_mpi_release (d); + return NULL; + } + } + else + q = NULL; + size = ntohs (encoding->sizeq); + if (size > 0) + { + rc = gcry_mpi_scan (&p, + GCRYMPI_FMT_USG, + &((const unsigned char *) (&encoding[1]))[pos], + size, &size); + pos += ntohs (encoding->sizeq); + if (rc) + { + LOG_GCRY (GNUNET_ERROR_TYPE_ERROR, "gcry_mpi_scan", rc); + gcry_mpi_release (n); + gcry_mpi_release (e); + gcry_mpi_release (d); + if (q != NULL) + gcry_mpi_release (q); + return NULL; + } + } + else + p = NULL; + pos += ntohs (encoding->sizedmp1); + pos += ntohs (encoding->sizedmq1); + size = + ntohs (encoding->len) - sizeof (struct KskRsaPrivateKeyBinaryEncoded) - + pos; + if (size > 0) + { + rc = gcry_mpi_scan (&u, + GCRYMPI_FMT_USG, + &((const unsigned char *) (&encoding[1]))[pos], + size, &size); + if (rc) + { + LOG_GCRY (GNUNET_ERROR_TYPE_ERROR, "gcry_mpi_scan", rc); + gcry_mpi_release (n); + gcry_mpi_release (e); + gcry_mpi_release (d); + if (p != NULL) + gcry_mpi_release (p); + if (q != NULL) + gcry_mpi_release (q); + return NULL; + } + } + else + u = NULL; + + if ((p != NULL) && (q != NULL) && (u != NULL)) + { + rc = gcry_sexp_build (&res, &size, /* erroff */ + "(private-key(rsa(n %m)(e %m)(d %m)(p %m)(q %m)(u %m)))", + n, e, d, p, q, u); + } + else + { + if ((p != NULL) && (q != NULL)) + { + rc = gcry_sexp_build (&res, &size, /* erroff */ + "(private-key(rsa(n %m)(e %m)(d %m)(p %m)(q %m)))", + n, e, d, p, q); + } + else + { + rc = gcry_sexp_build (&res, &size, /* erroff */ + "(private-key(rsa(n %m)(e %m)(d %m)))", + n, e, d); + } + } + gcry_mpi_release (n); + gcry_mpi_release (e); + gcry_mpi_release (d); + if (p != NULL) + gcry_mpi_release (p); + if (q != NULL) + gcry_mpi_release (q); + if (u != NULL) + gcry_mpi_release (u); + + if (rc) + LOG_GCRY (GNUNET_ERROR_TYPE_ERROR, "gcry_sexp_build", rc); +#if EXTRA_CHECKS + if (gcry_pk_testkey (res)) + { + LOG_GCRY (GNUNET_ERROR_TYPE_ERROR, "gcry_pk_testkey", rc); + return NULL; + } +#endif + ret = GNUNET_malloc (sizeof (struct GNUNET_CRYPTO_RsaPrivateKey)); + ret->sexp = res; + return ret; +} + + + + +typedef struct +{ + GNUNET_HashCode hc; + struct KskRsaPrivateKeyBinaryEncoded *pke; +} KBlockKeyCacheLine; + +static KBlockKeyCacheLine **cache; +static unsigned int cacheSize; + +/** + * Deterministically (!) create a hostkey using only the + * given HashCode as input to the PRNG. + */ +struct GNUNET_CRYPTO_RsaPrivateKey * +GNUNET_CRYPTO_rsa_key_create_from_hash (const GNUNET_HashCode * hc) +{ + struct GNUNET_CRYPTO_RsaPrivateKey *ret; + KBlockKeyCacheLine *line; + int i; + + for (i = 0; i < cacheSize; i++) + { + if (0 == memcmp (hc, &cache[i]->hc, sizeof (GNUNET_HashCode))) + { + ret = ksk_decode_key (cache[i]->pke); + return ret; + } + } + + line = GNUNET_malloc (sizeof (KBlockKeyCacheLine)); + line->hc = *hc; + line->pke = makeKblockKeyInternal (hc); + GNUNET_array_grow (cache, cacheSize, cacheSize + 1); + cache[cacheSize - 1] = line; + return ksk_decode_key (line->pke); +} + +void __attribute__ ((constructor)) GNUNET_CRYPTO_ksk_init () +{ + gcry_control (GCRYCTL_DISABLE_SECMEM, 0); + if (!gcry_check_version (GCRYPT_VERSION)) + { + fprintf (stderr, + _ + ("libgcrypt has not the expected version (version %s is required).\n"), + GCRYPT_VERSION); + abort (); + } +#ifdef gcry_fast_random_poll + gcry_fast_random_poll (); +#endif +} + +void __attribute__ ((destructor)) GNUNET_CRYPTO_ksk_fini () +{ + int i; + for (i = 0; i < cacheSize; i++) + { + GNUNET_free (cache[i]->pke); + GNUNET_free (cache[i]); + } + GNUNET_array_grow (cache, cacheSize, 0); +} + +/* end of kblockkey.c */ diff --git a/src/util/crypto_random.c b/src/util/crypto_random.c new file mode 100644 index 000000000..3f7ac4dd3 --- /dev/null +++ b/src/util/crypto_random.c @@ -0,0 +1,136 @@ +/* + This file is part of GNUnet. + (C) 2001, 2002, 2003, 2004, 2005, 2006 Christian Grothoff (and other contributing authors) + + GNUnet is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published + by the Free Software Foundation; either version 2, 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 + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with GNUnet; see the file COPYING. If not, write to the + Free Software Foundation, Inc., 59 Temple Place - Suite 330, + Boston, MA 02111-1307, USA. + +*/ + +/** + * @file util/crypto_random.c + * @brief functions to gather random numbers + * @author Christian Grothoff + */ +#include "platform.h" +#include "gnunet_common.h" +#include "gnunet_crypto_lib.h" +#include + +/** + * @return a random value in the interval [0,i[. + */ +unsigned int +GNUNET_CRYPTO_random_u32 (enum GNUNET_CRYPTO_Quality mode, unsigned int i) +{ +#ifdef gcry_fast_random_poll + static unsigned int invokeCount; +#endif + unsigned int ret; + + GNUNET_assert (i > 0); + + if (mode == GNUNET_CRYPTO_QUALITY_STRONG) + { + /* see http://lists.gnupg.org/pipermail/gcrypt-devel/2004-May/000613.html */ +#ifdef gcry_fast_random_poll + if ((invokeCount++ % 256) == 0) + gcry_fast_random_poll (); +#endif + ret = rand (); /* in case gcry_randomize fails, + we at least get a pseudo- + random number this way */ + gcry_randomize ((unsigned char *) &ret, + sizeof (unsigned int), GCRY_STRONG_RANDOM); + return ret % i; + } + else + { + ret = i * ((double) RANDOM () / RAND_MAX); + if (ret >= i) + ret = i - 1; + return ret; + } +} + + +/** + * Get an array with a random permutation of the + * numbers 0...n-1. + * @param mode GNUNET_RANDOM_QUALITY_STRONG if the strong (but expensive) + * PRNG should be used, GNUNET_RANDOM_QUALITY_WEAK otherwise + * @param n the size of the array + * @return the permutation array (allocated from heap) + */ +unsigned int * +GNUNET_CRYPTO_random_permute (enum GNUNET_CRYPTO_Quality mode, unsigned int n) +{ + unsigned int *ret; + unsigned int i; + unsigned int tmp; + unsigned int x; + + GNUNET_assert (n > 0); + ret = GNUNET_malloc (n * sizeof (int)); + for (i = 0; i < n; i++) + ret[i] = i; + for (i = 0; i < n; i++) + { + x = GNUNET_CRYPTO_random_u32 (mode, n); + tmp = ret[x]; + ret[x] = ret[i]; + ret[i] = tmp; + } + return ret; +} + +/** + * Random on unsigned 64-bit values. + */ +unsigned long long +GNUNET_CRYPTO_random_u64 (enum GNUNET_CRYPTO_Quality mode, + unsigned long long u) +{ + unsigned long long ret; + + GNUNET_assert (u > 0); + if (mode == GNUNET_CRYPTO_QUALITY_STRONG) + { + gcry_randomize ((unsigned char *) &ret, + sizeof (unsigned long long), GCRY_STRONG_RANDOM); + return ret % u; + } + else + { + ret = u * ((double) RANDOM () / RAND_MAX); + if (ret >= u) + ret = u - 1; + return ret; + } +} + +/** + * This function should only be called in testcases + * where strong entropy gathering is not desired + * (for example, for hostkey generation). + */ +void +GNUNET_CRYPTO_random_disable_entropy_gathering () +{ + gcry_control (GCRYCTL_ENABLE_QUICK_RANDOM, 0); +} + + +/* end of crypto_random.c */ diff --git a/src/util/crypto_rsa.c b/src/util/crypto_rsa.c new file mode 100644 index 000000000..b61729e06 --- /dev/null +++ b/src/util/crypto_rsa.c @@ -0,0 +1,948 @@ +/* + This file is part of GNUnet. + (C) 2001, 2002, 2003, 2004, 2005, 2006, 2009 Christian Grothoff (and other contributing authors) + + GNUnet is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published + by the Free Software Foundation; either version 2, 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 + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with GNUnet; see the file COPYING. If not, write to the + Free Software Foundation, Inc., 59 Temple Place - Suite 330, + Boston, MA 02111-1307, USA. +*/ + +/** + * @file util/crypto_rsa.c + * @brief public key cryptography (RSA) with libgcrypt + * @author Christian Grothoff + * + * Note that the code locks often needlessly on the gcrypt-locking api. + * One would think that simple MPI operations should not require locking + * (since only global operations on the random pool must be locked, + * strictly speaking). But libgcrypt does sometimes require locking in + * unexpected places, so the safe solution is to always lock even if it + * is not required. The performance impact is minimal anyway. + */ + +#include "platform.h" +#include +#include "gnunet_common.h" +#include "gnunet_crypto_lib.h" +#include "gnunet_disk_lib.h" + +/** + * The private information of an RSA key pair. + * NOTE: this must match the definition in crypto_ksk.c + */ +struct GNUNET_CRYPTO_RsaPrivateKey +{ + gcry_sexp_t sexp; +}; + + +/** + * GNUnet mandates a certain format for the encoding + * of private RSA key information that is provided + * by the RSA implementations. This format is used + * to serialize a private RSA key (typically when + * writing it to disk). + */ +struct RsaPrivateKeyBinaryEncoded +{ + /** + * Total size of the structure, in bytes, in big-endian! + */ + uint16_t len GNUNET_PACKED; + uint16_t sizen GNUNET_PACKED; /* in big-endian! */ + uint16_t sizee GNUNET_PACKED; /* in big-endian! */ + uint16_t sized GNUNET_PACKED; /* in big-endian! */ + uint16_t sizep GNUNET_PACKED; /* in big-endian! */ + uint16_t sizeq GNUNET_PACKED; /* in big-endian! */ + uint16_t sizedmp1 GNUNET_PACKED; /* in big-endian! */ + uint16_t sizedmq1 GNUNET_PACKED; /* in big-endian! */ + /* followed by the actual values */ +}; + + +#define HOSTKEY_LEN 2048 + +#define EXTRA_CHECKS ALLOW_EXTRA_CHECKS + + +/** + * Log an error message at log-level 'level' that indicates + * a failure of the command 'cmd' with the message given + * by gcry_strerror(rc). + */ +#define LOG_GCRY(level, cmd, rc) do { GNUNET_log(level, _("`%s' failed at %s:%d with error: %s\n"), cmd, __FILE__, __LINE__, gcry_strerror(rc)); } while(0); + +/** + * If target != size, move target bytes to the + * end of the size-sized buffer and zero out the + * first target-size bytes. + */ +static void +adjust (unsigned char *buf, size_t size, size_t target) +{ + if (size < target) + { + memmove (&buf[target - size], buf, size); + memset (buf, 0, target - size); + } +} + +/** + * This HostKey implementation uses RSA. + */ +struct GNUNET_CRYPTO_RsaPrivateKey * +GNUNET_CRYPTO_rsa_key_create () +{ + struct GNUNET_CRYPTO_RsaPrivateKey *ret; + gcry_sexp_t s_key; + gcry_sexp_t s_keyparam; + + GNUNET_assert (0 == gcry_sexp_build (&s_keyparam, + NULL, + "(genkey(rsa(nbits %d)(rsa-use-e 3:257)))", + HOSTKEY_LEN)); + GNUNET_assert (0 == gcry_pk_genkey (&s_key, s_keyparam)); + gcry_sexp_release (s_keyparam); +#if EXTRA_CHECKS + GNUNET_assert (0 == gcry_pk_testkey (s_key)); +#endif + ret = GNUNET_malloc (sizeof (struct GNUNET_CRYPTO_RsaPrivateKey)); + ret->sexp = s_key; + return ret; +} + +/** + * Free memory occupied by hostkey + */ +void +GNUNET_CRYPTO_rsa_key_free (struct GNUNET_CRYPTO_RsaPrivateKey *hostkey) +{ + gcry_sexp_release (hostkey->sexp); + GNUNET_free (hostkey); +} + +static int +key_from_sexp (gcry_mpi_t * array, + gcry_sexp_t sexp, const char *topname, const char *elems) +{ + gcry_sexp_t list, l2; + const char *s; + int i, idx; + + list = gcry_sexp_find_token (sexp, topname, 0); + if (!list) + { + return 1; + } + l2 = gcry_sexp_cadr (list); + gcry_sexp_release (list); + list = l2; + if (!list) + { + return 2; + } + + idx = 0; + for (s = elems; *s; s++, idx++) + { + l2 = gcry_sexp_find_token (list, s, 1); + if (!l2) + { + for (i = 0; i < idx; i++) + { + gcry_free (array[i]); + array[i] = NULL; + } + gcry_sexp_release (list); + return 3; /* required parameter not found */ + } + array[idx] = gcry_sexp_nth_mpi (l2, 1, GCRYMPI_FMT_USG); + gcry_sexp_release (l2); + if (!array[idx]) + { + for (i = 0; i < idx; i++) + { + gcry_free (array[i]); + array[i] = NULL; + } + gcry_sexp_release (list); + return 4; /* required parameter is invalid */ + } + } + gcry_sexp_release (list); + return 0; +} + +/** + * Extract the public key of the host. + * @param hostkey the hostkey to extract into the result. + * @param result where to write the result. + */ +void +GNUNET_CRYPTO_rsa_key_get_public (const struct GNUNET_CRYPTO_RsaPrivateKey + *hostkey, + struct + GNUNET_CRYPTO_RsaPublicKeyBinaryEncoded + *result) +{ + gcry_mpi_t skey[2]; + size_t size; + int rc; + + rc = key_from_sexp (skey, hostkey->sexp, "public-key", "ne"); + if (rc) + rc = key_from_sexp (skey, hostkey->sexp, "private-key", "ne"); + if (rc) + rc = key_from_sexp (skey, hostkey->sexp, "rsa", "ne"); + GNUNET_assert (0 == rc); + result->len = + htons (sizeof (struct GNUNET_CRYPTO_RsaPublicKeyBinaryEncoded) - + sizeof (result->padding)); + result->sizen = htons (GNUNET_CRYPTO_RSA_DATA_ENCODING_LENGTH); + result->padding = 0; + size = GNUNET_CRYPTO_RSA_DATA_ENCODING_LENGTH; + GNUNET_assert (0 == gcry_mpi_print (GCRYMPI_FMT_USG, + &result->key[0], size, &size, skey[0])); + adjust (&result->key[0], size, GNUNET_CRYPTO_RSA_DATA_ENCODING_LENGTH); + size = + GNUNET_CRYPTO_RSA_KEY_LENGTH - GNUNET_CRYPTO_RSA_DATA_ENCODING_LENGTH; + GNUNET_assert (0 == + gcry_mpi_print (GCRYMPI_FMT_USG, + &result-> + key[GNUNET_CRYPTO_RSA_DATA_ENCODING_LENGTH], + size, &size, skey[1])); + adjust (&result->key[GNUNET_CRYPTO_RSA_DATA_ENCODING_LENGTH], size, + GNUNET_CRYPTO_RSA_KEY_LENGTH - + GNUNET_CRYPTO_RSA_DATA_ENCODING_LENGTH); + gcry_mpi_release (skey[0]); + gcry_mpi_release (skey[1]); +} + + +/** + * Internal: publicKey => RSA-Key. + * + * Note that the return type is not actually a private + * key but rather an sexpression for the public key! + */ +static struct GNUNET_CRYPTO_RsaPrivateKey * +public2PrivateKey (const struct GNUNET_CRYPTO_RsaPublicKeyBinaryEncoded + *publicKey) +{ + struct GNUNET_CRYPTO_RsaPrivateKey *ret; + gcry_sexp_t result; + gcry_mpi_t n; + gcry_mpi_t e; + size_t size; + size_t erroff; + int rc; + + if ((ntohs (publicKey->sizen) != GNUNET_CRYPTO_RSA_DATA_ENCODING_LENGTH) || + (ntohs (publicKey->len) != + sizeof (struct GNUNET_CRYPTO_RsaPublicKeyBinaryEncoded) - + sizeof (publicKey->padding))) + { + GNUNET_break (0); + return NULL; + } + size = GNUNET_CRYPTO_RSA_DATA_ENCODING_LENGTH; + rc = gcry_mpi_scan (&n, GCRYMPI_FMT_USG, &publicKey->key[0], size, &size); + if (rc) + { + LOG_GCRY (GNUNET_ERROR_TYPE_ERROR, "gcry_mpi_scan", rc); + return NULL; + } + size = + GNUNET_CRYPTO_RSA_KEY_LENGTH - GNUNET_CRYPTO_RSA_DATA_ENCODING_LENGTH; + rc = + gcry_mpi_scan (&e, GCRYMPI_FMT_USG, + &publicKey->key[GNUNET_CRYPTO_RSA_DATA_ENCODING_LENGTH], + size, &size); + if (rc) + { + LOG_GCRY (GNUNET_ERROR_TYPE_ERROR, "gcry_mpi_scan", rc); + gcry_mpi_release (n); + return NULL; + } + rc = gcry_sexp_build (&result, + &erroff, "(public-key(rsa(n %m)(e %m)))", n, e); + gcry_mpi_release (n); + gcry_mpi_release (e); + if (rc) + { + LOG_GCRY (GNUNET_ERROR_TYPE_ERROR, "gcry_sexp_build", rc); /* erroff gives more info */ + return NULL; + } + ret = GNUNET_malloc (sizeof (struct GNUNET_CRYPTO_RsaPrivateKey)); + ret->sexp = result; + return ret; +} + + +/** + * Encode the private key in a format suitable for + * storing it into a file. + * @returns encoding of the private key. + * The first 4 bytes give the size of the array, as usual. + */ +static struct RsaPrivateKeyBinaryEncoded * +rsa_encode_key (const struct GNUNET_CRYPTO_RsaPrivateKey *hostkey) +{ + struct RsaPrivateKeyBinaryEncoded *retval; + gcry_mpi_t pkv[6]; + void *pbu[6]; + size_t sizes[6]; + int rc; + int i; + int size; + +#if EXTRA_CHECKS + if (gcry_pk_testkey (hostkey->sexp)) + { + GNUNET_break (0); + return NULL; + } +#endif + + memset (pkv, 0, sizeof (gcry_mpi_t) * 6); + rc = key_from_sexp (pkv, hostkey->sexp, "private-key", "nedpqu"); + if (rc) + rc = key_from_sexp (pkv, hostkey->sexp, "rsa", "nedpqu"); + if (rc) + rc = key_from_sexp (pkv, hostkey->sexp, "private-key", "nedpq"); + if (rc) + rc = key_from_sexp (pkv, hostkey->sexp, "rsa", "nedpq"); + if (rc) + rc = key_from_sexp (pkv, hostkey->sexp, "private-key", "ned"); + if (rc) + rc = key_from_sexp (pkv, hostkey->sexp, "rsa", "ned"); + GNUNET_assert (0 == rc); + size = sizeof (struct RsaPrivateKeyBinaryEncoded); + for (i = 0; i < 6; i++) + { + if (pkv[i] != NULL) + { + GNUNET_assert (0 == gcry_mpi_aprint (GCRYMPI_FMT_USG, + (unsigned char **) &pbu[i], + &sizes[i], pkv[i])); + size += sizes[i]; + } + else + { + pbu[i] = NULL; + sizes[i] = 0; + } + } + GNUNET_assert (size < 65536); + retval = GNUNET_malloc (size); + retval->len = htons (size); + i = 0; + retval->sizen = htons (sizes[0]); + memcpy (&((char *) (&retval[1]))[i], pbu[0], sizes[0]); + i += sizes[0]; + retval->sizee = htons (sizes[1]); + memcpy (&((char *) (&retval[1]))[i], pbu[1], sizes[1]); + i += sizes[1]; + retval->sized = htons (sizes[2]); + memcpy (&((char *) (&retval[1]))[i], pbu[2], sizes[2]); + i += sizes[2]; + /* swap p and q! */ + retval->sizep = htons (sizes[4]); + memcpy (&((char *) (&retval[1]))[i], pbu[4], sizes[4]); + i += sizes[4]; + retval->sizeq = htons (sizes[3]); + memcpy (&((char *) (&retval[1]))[i], pbu[3], sizes[3]); + i += sizes[3]; + retval->sizedmp1 = htons (0); + retval->sizedmq1 = htons (0); + memcpy (&((char *) (&retval[1]))[i], pbu[5], sizes[5]); + for (i = 0; i < 6; i++) + { + if (pkv[i] != NULL) + gcry_mpi_release (pkv[i]); + if (pbu[i] != NULL) + free (pbu[i]); + } + return retval; +} + +/** + * Decode the private key from the file-format back + * to the "normal", internal format. + */ +static struct GNUNET_CRYPTO_RsaPrivateKey * +rsa_decode_key (const struct RsaPrivateKeyBinaryEncoded *encoding) +{ + struct GNUNET_CRYPTO_RsaPrivateKey *ret; + gcry_sexp_t res; + gcry_mpi_t n, e, d, p, q, u; + int rc; + size_t size; + int pos; + + pos = 0; + size = ntohs (encoding->sizen); + rc = gcry_mpi_scan (&n, + GCRYMPI_FMT_USG, + &((const unsigned char *) (&encoding[1]))[pos], + size, &size); + pos += ntohs (encoding->sizen); + if (rc) + { + LOG_GCRY (GNUNET_ERROR_TYPE_ERROR, "gcry_mpi_scan", rc); + return NULL; + } + size = ntohs (encoding->sizee); + rc = gcry_mpi_scan (&e, + GCRYMPI_FMT_USG, + &((const unsigned char *) (&encoding[1]))[pos], + size, &size); + pos += ntohs (encoding->sizee); + if (rc) + { + LOG_GCRY (GNUNET_ERROR_TYPE_ERROR, "gcry_mpi_scan", rc); + gcry_mpi_release (n); + return NULL; + } + size = ntohs (encoding->sized); + rc = gcry_mpi_scan (&d, + GCRYMPI_FMT_USG, + &((const unsigned char *) (&encoding[1]))[pos], + size, &size); + pos += ntohs (encoding->sized); + if (rc) + { + LOG_GCRY (GNUNET_ERROR_TYPE_ERROR, "gcry_mpi_scan", rc); + gcry_mpi_release (n); + gcry_mpi_release (e); + return NULL; + } + /* swap p and q! */ + size = ntohs (encoding->sizep); + if (size > 0) + { + rc = gcry_mpi_scan (&q, + GCRYMPI_FMT_USG, + &((const unsigned char *) (&encoding[1]))[pos], + size, &size); + pos += ntohs (encoding->sizep); + if (rc) + { + LOG_GCRY (GNUNET_ERROR_TYPE_ERROR, "gcry_mpi_scan", rc); + gcry_mpi_release (n); + gcry_mpi_release (e); + gcry_mpi_release (d); + return NULL; + } + } + else + q = NULL; + size = ntohs (encoding->sizeq); + if (size > 0) + { + rc = gcry_mpi_scan (&p, + GCRYMPI_FMT_USG, + &((const unsigned char *) (&encoding[1]))[pos], + size, &size); + pos += ntohs (encoding->sizeq); + if (rc) + { + LOG_GCRY (GNUNET_ERROR_TYPE_ERROR, "gcry_mpi_scan", rc); + gcry_mpi_release (n); + gcry_mpi_release (e); + gcry_mpi_release (d); + if (q != NULL) + gcry_mpi_release (q); + return NULL; + } + } + else + p = NULL; + pos += ntohs (encoding->sizedmp1); + pos += ntohs (encoding->sizedmq1); + size = + ntohs (encoding->len) - sizeof (struct RsaPrivateKeyBinaryEncoded) - pos; + if (size > 0) + { + rc = gcry_mpi_scan (&u, + GCRYMPI_FMT_USG, + &((const unsigned char *) (&encoding[1]))[pos], + size, &size); + if (rc) + { + LOG_GCRY (GNUNET_ERROR_TYPE_ERROR, "gcry_mpi_scan", rc); + gcry_mpi_release (n); + gcry_mpi_release (e); + gcry_mpi_release (d); + if (p != NULL) + gcry_mpi_release (p); + if (q != NULL) + gcry_mpi_release (q); + return NULL; + } + } + else + u = NULL; + + if ((p != NULL) && (q != NULL) && (u != NULL)) + { + rc = gcry_sexp_build (&res, &size, /* erroff */ + "(private-key(rsa(n %m)(e %m)(d %m)(p %m)(q %m)(u %m)))", + n, e, d, p, q, u); + } + else + { + if ((p != NULL) && (q != NULL)) + { + rc = gcry_sexp_build (&res, &size, /* erroff */ + "(private-key(rsa(n %m)(e %m)(d %m)(p %m)(q %m)))", + n, e, d, p, q); + } + else + { + rc = gcry_sexp_build (&res, &size, /* erroff */ + "(private-key(rsa(n %m)(e %m)(d %m)))", + n, e, d); + } + } + gcry_mpi_release (n); + gcry_mpi_release (e); + gcry_mpi_release (d); + if (p != NULL) + gcry_mpi_release (p); + if (q != NULL) + gcry_mpi_release (q); + if (u != NULL) + gcry_mpi_release (u); + + if (rc) + LOG_GCRY (GNUNET_ERROR_TYPE_ERROR, "gcry_sexp_build", rc); +#if EXTRA_CHECKS + if (gcry_pk_testkey (res)) + { + LOG_GCRY (GNUNET_ERROR_TYPE_ERROR, "gcry_pk_testkey", rc); + return NULL; + } +#endif + ret = GNUNET_malloc (sizeof (struct GNUNET_CRYPTO_RsaPrivateKey)); + ret->sexp = res; + return ret; +} + + +/** + * Create a new private key by reading it from a file. If the + * files does not exist, create a new key and write it to the + * file. Caller must free return value. Note that this function + * can not guarantee that another process might not be trying + * the same operation on the same file at the same time. The + * caller must somehow know that the file either already exists + * with a valid key OR be sure that no other process is calling + * this function at the same time. If the contents of the file + * are invalid the old file is deleted and a fresh key is + * created. + * + * @return new private key, NULL on error (for example, + * permission denied) + */ +struct GNUNET_CRYPTO_RsaPrivateKey * +GNUNET_CRYPTO_rsa_key_create_from_file (const char *filename) +{ + struct flock fl; + struct GNUNET_CRYPTO_RsaPrivateKey *ret; + struct RsaPrivateKeyBinaryEncoded *enc; + struct stat sbuf; + uint16_t len; + int fd; + unsigned int cnt; + int ec; + + if (GNUNET_SYSERR == GNUNET_DISK_directory_create_for_file (filename)) + return NULL; + while (0 != STAT (filename, &sbuf)) + { + fd = open (filename, O_WRONLY | O_CREAT | O_EXCL, S_IRUSR | S_IWUSR); + if (-1 == fd) + { + if (errno == EEXIST) + continue; + GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_ERROR, + "open", filename); + return NULL; + } + memset (&fl, 0, sizeof (struct flock)); + fl.l_type = F_WRLCK; + fl.l_whence = SEEK_SET; + fl.l_len = sizeof (struct RsaPrivateKeyBinaryEncoded); + cnt = 0; + while (0 != fcntl (fd, F_SETLK, &fl)) + { + sleep (1); + if (0 == ++cnt % 10) + { + ec = errno; + fl.l_type = F_GETLK; + fcntl (fd, F_GETLK, &fl); + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + _ + ("Could not aquire lock on file `%s' due to process %u: %s...\n"), + filename, fl.l_pid, STRERROR (errno)); + } + memset (&fl, 0, sizeof (struct flock)); + fl.l_type = F_WRLCK; + fl.l_whence = SEEK_SET; + fl.l_len = sizeof (struct RsaPrivateKeyBinaryEncoded); + } + GNUNET_log (GNUNET_ERROR_TYPE_INFO, + _("Creating a new private key. This may take a while.\n")); + ret = GNUNET_CRYPTO_rsa_key_create (); + GNUNET_assert (ret != NULL); + enc = rsa_encode_key (ret); + GNUNET_assert (enc != NULL); + GNUNET_assert (ntohs (enc->len) == WRITE (fd, enc, ntohs (enc->len))); + GNUNET_free (enc); + fdatasync (fd); + memset (&fl, 0, sizeof (struct flock)); + fl.l_type = F_UNLCK; + fl.l_whence = SEEK_SET; + fl.l_len = sizeof (struct RsaPrivateKeyBinaryEncoded); + cnt = 0; + if (0 != fcntl (fd, F_SETLK, &fl)) + GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING, + "fcntl", filename); + GNUNET_assert (0 == CLOSE (fd)); + GNUNET_log (GNUNET_ERROR_TYPE_INFO, + _("Stored new private key in `%s'.\n"), filename); + return ret; + } + /* hostkey file exists already, read it! */ + fd = open (filename, O_RDONLY); + if (-1 == fd) + { + GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_ERROR, "open", filename); + return NULL; + } + cnt = 0; + while (1) + { + memset (&fl, 0, sizeof (struct flock)); + fl.l_type = F_RDLCK; + fl.l_whence = SEEK_SET; + fl.l_len = sizeof (struct RsaPrivateKeyBinaryEncoded); + if (0 != fcntl (fd, F_SETLK, &fl)) + { + if (0 == ++cnt % 10) + { + ec = errno; + fl.l_type = F_GETLK; + fcntl (fd, F_GETLK, &fl); + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + _ + ("Could not aquire lock on file `%s' due to process %u: %s...\n"), + filename, fl.l_pid, STRERROR (errno)); + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + _ + ("This may be ok if someone is currently generating a hostkey.\n")); + } + sleep (1); + continue; + } + if (0 != STAT (filename, &sbuf)) + { + /* eh, what!? File we opened is now gone!? */ + GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_ERROR, + "stat", filename); + memset (&fl, 0, sizeof (struct flock)); + fl.l_type = F_UNLCK; + fl.l_whence = SEEK_SET; + fl.l_len = sizeof (struct RsaPrivateKeyBinaryEncoded); + if (0 != fcntl (fd, F_SETLK, &fl)) + GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING, + "fcntl", filename); + GNUNET_assert (0 == CLOSE (fd)); + return NULL; + } + if (sbuf.st_size < sizeof (struct RsaPrivateKeyBinaryEncoded)) + { + /* maybe we got the read lock before the hostkey generating + process had a chance to get the write lock; give it up! */ + memset (&fl, 0, sizeof (struct flock)); + fl.l_type = F_UNLCK; + fl.l_whence = SEEK_SET; + fl.l_len = sizeof (struct RsaPrivateKeyBinaryEncoded); + if (0 != fcntl (fd, F_SETLK, &fl)) + GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING, + "fcntl", filename); + if (0 == ++cnt % 10) + { + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + _ + ("When trying to read hostkey file `%s' I found %u bytes but I need at least %u.\n"), + filename, (unsigned int) sbuf.st_size, + (unsigned int) sizeof (struct + RsaPrivateKeyBinaryEncoded)); + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + _ + ("This may be ok if someone is currently generating a hostkey.\n")); + } + sleep (2); /* wait a bit longer! */ + continue; + } + break; + } + enc = GNUNET_malloc (sbuf.st_size); + GNUNET_assert (sbuf.st_size == READ (fd, enc, sbuf.st_size)); + len = ntohs (enc->len); + if ((len != sbuf.st_size) || (NULL == (ret = rsa_decode_key (enc)))) + { + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + _ + ("File `%s' does not contain a valid private key. You should delete it.\n"), + filename); + GNUNET_free (enc); + } + memset (&fl, 0, sizeof (struct flock)); + fl.l_type = F_UNLCK; + fl.l_whence = SEEK_SET; + fl.l_len = sizeof (struct RsaPrivateKeyBinaryEncoded); + if (0 != fcntl (fd, F_SETLK, &fl)) + GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING, "fcntl", filename); + GNUNET_assert (0 == CLOSE (fd)); + return ret; +} + + +/** + * Encrypt a block with the public key of another host that uses the + * same cyper. + * + * @param block the block to encrypt + * @param size the size of block + * @param publicKey the encoded public key used to encrypt + * @param target where to store the encrypted block + * @returns GNUNET_SYSERR on error, GNUNET_OK if ok + */ +int +GNUNET_CRYPTO_rsa_encrypt (const void *block, + uint16_t size, + const struct + GNUNET_CRYPTO_RsaPublicKeyBinaryEncoded *publicKey, + struct GNUNET_CRYPTO_RsaEncryptedData *target) +{ + gcry_sexp_t result; + gcry_sexp_t data; + struct GNUNET_CRYPTO_RsaPrivateKey *pubkey; + gcry_mpi_t val; + gcry_mpi_t rval; + size_t isize; + size_t erroff; + + GNUNET_assert (size <= sizeof (GNUNET_HashCode)); + pubkey = public2PrivateKey (publicKey); + isize = size; + GNUNET_assert (0 == + gcry_mpi_scan (&val, GCRYMPI_FMT_USG, block, isize, &isize)); + GNUNET_assert (0 == + gcry_sexp_build (&data, &erroff, + "(data (flags pkcs1)(value %m))", val)); + gcry_mpi_release (val); + GNUNET_assert (0 == gcry_pk_encrypt (&result, data, pubkey->sexp)); + gcry_sexp_release (data); + GNUNET_CRYPTO_rsa_key_free (pubkey); + + GNUNET_assert (0 == key_from_sexp (&rval, result, "rsa", "a")); + gcry_sexp_release (result); + isize = sizeof (struct GNUNET_CRYPTO_RsaEncryptedData); + GNUNET_assert (0 == gcry_mpi_print (GCRYMPI_FMT_USG, + (unsigned char *) target, isize, &isize, + rval)); + gcry_mpi_release (rval); + adjust (&target->encoding[0], isize, + sizeof (struct GNUNET_CRYPTO_RsaEncryptedData)); + return GNUNET_OK; +} + +/** + * Decrypt a given block with the hostkey. + * + * @param hostkey the hostkey with which to decrypt this block + * @param block the data to decrypt, encoded as returned by encrypt + * @param result pointer to a location where the result can be stored + * @param max the maximum number of bits to store for the result, if + * the decrypted block is bigger, an error is returned + * @returns the size of the decrypted block, -1 on error + */ +int +GNUNET_CRYPTO_rsa_decrypt (const struct GNUNET_CRYPTO_RsaPrivateKey *hostkey, + const struct GNUNET_CRYPTO_RsaEncryptedData *block, + void *result, uint16_t max) +{ + gcry_sexp_t resultsexp; + gcry_sexp_t data; + size_t erroff; + size_t size; + gcry_mpi_t val; + unsigned char *endp; + unsigned char *tmp; + +#if EXTRA_CHECKS + GNUNET_assert (0 == gcry_pk_testkey (hostkey->sexp)); +#endif + size = sizeof (struct GNUNET_CRYPTO_RsaEncryptedData); + GNUNET_assert (0 == gcry_mpi_scan (&val, + GCRYMPI_FMT_USG, &block->encoding[0], + size, &size)); + GNUNET_assert (0 == + gcry_sexp_build (&data, &erroff, + "(enc-val(flags)(rsa(a %m)))", val)); + gcry_mpi_release (val); + GNUNET_assert (0 == gcry_pk_decrypt (&resultsexp, data, hostkey->sexp)); + gcry_sexp_release (data); + /* resultsexp has format "(value %m)" */ + GNUNET_assert (NULL != + (val = gcry_sexp_nth_mpi (resultsexp, 1, GCRYMPI_FMT_USG))); + gcry_sexp_release (resultsexp); + tmp = GNUNET_malloc (max + HOSTKEY_LEN / 8); + size = max + HOSTKEY_LEN / 8; + GNUNET_assert (0 == + gcry_mpi_print (GCRYMPI_FMT_USG, tmp, size, &size, val)); + gcry_mpi_release (val); + endp = tmp; + endp += (size - max); + size = max; + memcpy (result, endp, size); + GNUNET_free (tmp); + return size; +} + + +/** + * Sign a given block. + * + * @param hostkey private key to use for the signing + * @param purpose what to sign (size, purpose) + * @param result where to write the signature + * @return GNUNET_SYSERR on error, GNUNET_OK on success + */ +int +GNUNET_CRYPTO_rsa_sign (const struct GNUNET_CRYPTO_RsaPrivateKey *hostkey, + const struct GNUNET_CRYPTO_RsaSignaturePurpose + *purpose, struct GNUNET_CRYPTO_RsaSignature *sig) +{ + gcry_sexp_t result; + gcry_sexp_t data; + size_t ssize; + gcry_mpi_t rval; + GNUNET_HashCode hc; + char *buff; + int bufSize; + + GNUNET_CRYPTO_hash (purpose, ntohl (purpose->size), &hc); +#define FORMATSTRING "(4:data(5:flags5:pkcs1)(4:hash6:sha51264:0123456789012345678901234567890123456789012345678901234567890123))" + bufSize = strlen (FORMATSTRING) + 1; + buff = GNUNET_malloc (bufSize); + memcpy (buff, FORMATSTRING, bufSize); + memcpy (&buff + [bufSize - + strlen + ("0123456789012345678901234567890123456789012345678901234567890123))") + - 1], &hc, sizeof (GNUNET_HashCode)); + GNUNET_assert (0 == gcry_sexp_new (&data, buff, bufSize, 0)); + GNUNET_free (buff); + GNUNET_assert (0 == gcry_pk_sign (&result, data, hostkey->sexp)); + gcry_sexp_release (data); + GNUNET_assert (0 == key_from_sexp (&rval, result, "rsa", "s")); + gcry_sexp_release (result); + ssize = sizeof (struct GNUNET_CRYPTO_RsaSignature); + GNUNET_assert (0 == gcry_mpi_print (GCRYMPI_FMT_USG, + (unsigned char *) sig, ssize, &ssize, + rval)); + gcry_mpi_release (rval); + adjust (sig->sig, ssize, sizeof (struct GNUNET_CRYPTO_RsaSignature)); + return GNUNET_OK; +} + + +/** + * Verify signature. + * + * @param purpose what is the purpose that the signature should have? + * @param validate block to validate (size, purpose, data) + * @param sig signature that is being validated + * @param publicKey public key of the signer + * @returns GNUNET_OK if ok, GNUNET_SYSERR if invalid + */ +int +GNUNET_CRYPTO_rsa_verify (uint32_t purpose, + const struct GNUNET_CRYPTO_RsaSignaturePurpose + *validate, + const struct GNUNET_CRYPTO_RsaSignature *sig, + const struct GNUNET_CRYPTO_RsaPublicKeyBinaryEncoded + *publicKey) +{ + gcry_sexp_t data; + gcry_sexp_t sigdata; + size_t size; + gcry_mpi_t val; + struct GNUNET_CRYPTO_RsaPrivateKey *hostkey; + GNUNET_HashCode hc; + char *buff; + int bufSize; + size_t erroff; + int rc; + + if (purpose != ntohl (validate->purpose)) + return GNUNET_SYSERR; /* purpose mismatch */ + GNUNET_CRYPTO_hash (validate, ntohl (validate->size), &hc); + size = sizeof (struct GNUNET_CRYPTO_RsaSignature); + GNUNET_assert (0 == gcry_mpi_scan (&val, + GCRYMPI_FMT_USG, + (const unsigned char *) sig, size, + &size)); + GNUNET_assert (0 == + gcry_sexp_build (&sigdata, &erroff, "(sig-val(rsa(s %m)))", + val)); + gcry_mpi_release (val); + bufSize = strlen (FORMATSTRING) + 1; + buff = GNUNET_malloc (bufSize); + memcpy (buff, FORMATSTRING, bufSize); + memcpy (&buff[strlen (FORMATSTRING) - + strlen + ("0123456789012345678901234567890123456789012345678901234567890123))")], + &hc, sizeof (GNUNET_HashCode)); + GNUNET_assert (0 == gcry_sexp_new (&data, buff, bufSize, 0)); + GNUNET_free (buff); + hostkey = public2PrivateKey (publicKey); + if (hostkey == NULL) + { + gcry_sexp_release (data); + gcry_sexp_release (sigdata); + return GNUNET_SYSERR; + } + rc = gcry_pk_verify (sigdata, data, hostkey->sexp); + GNUNET_CRYPTO_rsa_key_free (hostkey); + gcry_sexp_release (data); + gcry_sexp_release (sigdata); + if (rc) + { + GNUNET_log (GNUNET_ERROR_TYPE_WARNING, + _("RSA signature verification failed at %s:%d: %s\n"), + __FILE__, __LINE__, gcry_strerror (rc)); + return GNUNET_SYSERR; + } + return GNUNET_OK; +} + + +/* end of crypto_rsa.c */ diff --git a/src/util/disk.c b/src/util/disk.c new file mode 100644 index 000000000..f0fe9341b --- /dev/null +++ b/src/util/disk.c @@ -0,0 +1,954 @@ +/* + This file is part of GNUnet. + (C) 2001, 2002, 2005, 2006 Christian Grothoff (and other contributing authors) + + GNUnet is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published + by the Free Software Foundation; either version 2, 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 + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with GNUnet; see the file COPYING. If not, write to the + Free Software Foundation, Inc., 59 Temple Place - Suite 330, + Boston, MA 02111-1307, USA. +*/ + +/** + * @file util/disk.c + * @brief disk IO convenience methods + * @author Christian Grothoff + */ + +#include "platform.h" +#include "gnunet_common.h" +#include "gnunet_directories.h" +#include "gnunet_disk_lib.h" +#include "gnunet_scheduler_lib.h" +#include "gnunet_strings_lib.h" + + +#if LINUX || CYGWIN +#include +#else +#ifdef SOMEBSD +#include +#include +#else +#ifdef OSX +#include +#include +#else +#ifdef SOLARIS +#include +#include +#else +#ifdef MINGW +#define _IFMT 0170000 /* type of file */ +#define _IFLNK 0120000 /* symbolic link */ +#define S_ISLNK(m) (((m)&_IFMT) == _IFLNK) +#else +#error PORT-ME: need to port statfs (how much space is left on the drive?) +#endif +#endif +#endif +#endif +#endif + +#ifndef SOMEBSD +#ifndef WINDOWS +#ifndef OSX +#include +#endif +#endif +#endif + +typedef struct +{ + unsigned long long total; + int include_sym_links; +} GetFileSizeData; + +static int +getSizeRec (void *ptr, const char *fn) +{ + GetFileSizeData *gfsd = ptr; +#ifdef HAVE_STAT64 + struct stat64 buf; +#else + struct stat buf; +#endif + +#ifdef HAVE_STAT64 + if (0 != STAT64 (fn, &buf)) + { + GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING, "stat64", fn); + return GNUNET_SYSERR; + } +#else + if (0 != STAT (fn, &buf)) + { + GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING, "stat", fn); + return GNUNET_SYSERR; + } +#endif + if ((!S_ISLNK (buf.st_mode)) || (gfsd->include_sym_links == GNUNET_YES)) + gfsd->total += buf.st_size; + if ((S_ISDIR (buf.st_mode)) && + (0 == ACCESS (fn, X_OK)) && + ((!S_ISLNK (buf.st_mode)) || (gfsd->include_sym_links == GNUNET_YES))) + { + if (GNUNET_SYSERR == GNUNET_DISK_directory_scan (fn, &getSizeRec, gfsd)) + return GNUNET_SYSERR; + } + return GNUNET_OK; +} + +/** + * Get the size of the file (or directory) + * of the given file (in bytes). + * + * @return GNUNET_SYSERR on error, GNUNET_OK on success + */ +int +GNUNET_DISK_file_size (const char *filename, + unsigned long long *size, int includeSymLinks) +{ + GetFileSizeData gfsd; + int ret; + + GNUNET_assert (size != NULL); + gfsd.total = 0; + gfsd.include_sym_links = includeSymLinks; + ret = getSizeRec (&gfsd, filename); + *size = gfsd.total; + return ret; +} + +/** + * Get the number of blocks that are left on the partition that + * contains the given file (for normal users). + * + * @param part a file on the partition to check + * @return -1 on errors, otherwise the number of free blocks + */ +long +GNUNET_DISK_get_blocks_available (const char *part) +{ +#ifdef SOLARIS + struct statvfs buf; + + if (0 != statvfs (part, &buf)) + { + GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING, "statfs", part); + return -1; + } + return buf.f_bavail; +#elif MINGW + DWORD dwDummy; + DWORD dwBlocks; + char szDrive[4]; + + memcpy (szDrive, part, 3); + szDrive[3] = 0; + if (!GetDiskFreeSpace (szDrive, &dwDummy, &dwDummy, &dwBlocks, &dwDummy)) + { + GNUNET_GE_LOG (GNUNET_ERROR_TYPE_WARNING, + _("`%s' failed for drive `%s': %u\n"), + "GetDiskFreeSpace", szDrive, GetLastError ()); + + return -1; + } + return dwBlocks; +#else + struct statfs s; + if (0 != statfs (part, &s)) + { + GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING, "statfs", part); + return -1; + } + return s.f_bavail; +#endif +} + +/** + * Test if fil is a directory. + * + * @return GNUNET_YES if yes, GNUNET_NO if not, GNUNET_SYSERR if it + * does not exist + */ +int +GNUNET_DISK_directory_test (const char *fil) +{ + struct stat filestat; + int ret; + + ret = STAT (fil, &filestat); + if (ret != 0) + { + if (errno != ENOENT) + { + GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING, "stat", fil); + return GNUNET_SYSERR; + } + return GNUNET_NO; + } + if (!S_ISDIR (filestat.st_mode)) + return GNUNET_NO; + if (ACCESS (fil, R_OK | X_OK) < 0) + { + GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING, "access", fil); + return GNUNET_SYSERR; + } + return GNUNET_YES; +} + +/** + * Check that fil corresponds to a filename + * (of a file that exists and that is not a directory). + * @returns GNUNET_YES if yes, GNUNET_NO if not a file, GNUNET_SYSERR if something + * else (will print an error message in that case, too). + */ +int +GNUNET_DISK_file_test (const char *fil) +{ + struct stat filestat; + int ret; + char *rdir; + + rdir = GNUNET_STRINGS_filename_expand (fil); + if (rdir == NULL) + return GNUNET_SYSERR; + + ret = STAT (rdir, &filestat); + if (ret != 0) + { + if (errno != ENOENT) + { + GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING, "stat", rdir); + GNUNET_free (rdir); + return GNUNET_SYSERR; + } + GNUNET_free (rdir); + return GNUNET_NO; + } + if (!S_ISREG (filestat.st_mode)) + { + GNUNET_free (rdir); + return GNUNET_NO; + } + if (ACCESS (rdir, R_OK) < 0) + { + GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING, "access", rdir); + GNUNET_free (rdir); + return GNUNET_SYSERR; + } + GNUNET_free (rdir); + return GNUNET_YES; +} + +/** + * Implementation of "mkdir -p" + * @param dir the directory to create + * @returns GNUNET_OK on success, GNUNET_SYSERR on failure + */ +int +GNUNET_DISK_directory_create (const char *dir) +{ + char *rdir; + int len; + int pos; + int ret = GNUNET_OK; + + rdir = GNUNET_STRINGS_filename_expand (dir); + if (rdir == NULL) + return GNUNET_SYSERR; + + len = strlen (rdir); +#ifndef MINGW + pos = 1; /* skip heading '/' */ +#else + /* Local or Network path? */ + if (strncmp (rdir, "\\\\", 2) == 0) + { + pos = 2; + while (rdir[pos]) + { + if (rdir[pos] == '\\') + { + pos++; + break; + } + pos++; + } + } + else + { + pos = 3; /* strlen("C:\\") */ + } +#endif + while (pos <= len) + { + if ((rdir[pos] == DIR_SEPARATOR) || (pos == len)) + { + rdir[pos] = '\0'; + ret = GNUNET_DISK_directory_test (rdir); + if (ret == GNUNET_SYSERR) + { + GNUNET_free (rdir); + return GNUNET_SYSERR; + } + if (ret == GNUNET_NO) + { +#ifndef MINGW + ret = mkdir (rdir, S_IRUSR | S_IWUSR | S_IXUSR | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH); /* 755 */ +#else + ret = mkdir (rdir); +#endif + if ((ret != 0) && (errno != EEXIST)) + { + GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_ERROR, "mkdir", + rdir); + GNUNET_free (rdir); + return GNUNET_SYSERR; + } + } + rdir[pos] = DIR_SEPARATOR; + } + pos++; + } + GNUNET_free (rdir); + return GNUNET_OK; +} + + +/** + * Create the directory structure for storing + * a file. + * + * @param filename name of a file in the directory + * @returns GNUNET_OK on success, + * GNUNET_SYSERR on failure, + * GNUNET_NO if the directory + * exists but is not writeable for us + */ +int +GNUNET_DISK_directory_create_for_file (const char *dir) +{ + char *rdir; + int len; + int ret; + + rdir = GNUNET_STRINGS_filename_expand (dir); + if (rdir == NULL) + return GNUNET_SYSERR; + len = strlen (rdir); + while ((len > 0) && (rdir[len] != DIR_SEPARATOR)) + len--; + rdir[len] = '\0'; + ret = GNUNET_DISK_directory_create (rdir); + if ((ret == GNUNET_OK) && (0 != ACCESS (rdir, W_OK))) + ret = GNUNET_NO; + GNUNET_free (rdir); + return ret; +} + +/** + * Read the contents of a binary file into a buffer. + * @param fileName the name of the file, not freed, + * must already be expanded! + * @param len the maximum number of bytes to read + * @param result the buffer to write the result to + * @return the number of bytes read on success, -1 on failure + */ +int +GNUNET_DISK_file_read (const char *fileName, int len, void *result) +{ + /* open file, must exist, open read only */ + int handle; + int size; + + GNUNET_assert (fileName != NULL); + GNUNET_assert (len > 0); + if (len == 0) + return 0; + GNUNET_assert (result != NULL); + handle = GNUNET_DISK_file_open (fileName, O_RDONLY, S_IRUSR); + if (handle < 0) + return -1; + size = READ (handle, result, len); + GNUNET_DISK_file_close (fileName, handle); + return size; +} + + +/** + * Convert string to value ('755' for chmod-call) + */ +static int +atoo (const char *s) +{ + int n = 0; + + while (('0' <= *s) && (*s < '8')) + { + n <<= 3; + n += *s++ - '0'; + } + return n; +} + +/** + * Write a buffer to a file. + * @param fileName the name of the file, NOT freed! + * @param buffer the data to write + * @param n number of bytes to write + * @param mode permissions to set on the file + * @return GNUNET_OK on success, GNUNET_SYSERR on error + */ +int +GNUNET_DISK_file_write (const char *fileName, + const void *buffer, unsigned int n, const char *mode) +{ + int handle; + char *fn; + + /* open file, open with 600, create if not + present, otherwise overwrite */ + GNUNET_assert (fileName != NULL); + fn = GNUNET_STRINGS_filename_expand (fileName); + handle = GNUNET_DISK_file_open (fn, O_CREAT | O_WRONLY, S_IRUSR | S_IWUSR); + if (handle == -1) + { + GNUNET_free (fn); + return GNUNET_SYSERR; + } + GNUNET_assert ((n == 0) || (buffer != NULL)); + /* write the buffer take length from the beginning */ + if (n != WRITE (handle, buffer, n)) + { + GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING, "write", fn); + GNUNET_DISK_file_close (fn, handle); + GNUNET_free (fn); + return GNUNET_SYSERR; + } + GNUNET_DISK_file_close (fn, handle); + if (0 != CHMOD (fn, atoo (mode))) + { + GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING, "chmod", fn); + } + GNUNET_free (fn); + return GNUNET_OK; +} + +/** + * Scan a directory for files. The name of the directory + * must be expanded first (!). + * @param dirName the name of the directory + * @param callback the method to call for each file, + * can be NULL, in that case, we only count + * @param data argument to pass to callback + * @return the number of files found, GNUNET_SYSERR on error or + * ieration aborted by callback returning GNUNET_SYSERR + */ +int +GNUNET_DISK_directory_scan (const char *dirName, + GNUNET_FileNameCallback callback, void *data) +{ + DIR *dinfo; + struct dirent *finfo; + struct stat istat; + int count = 0; + char *name; + char *dname; + unsigned int name_len; + unsigned int n_size; + + GNUNET_assert (dirName != NULL); + dname = GNUNET_STRINGS_filename_expand (dirName); + while ((strlen (dname) > 0) && (dname[strlen (dname) - 1] == DIR_SEPARATOR)) + dname[strlen (dname) - 1] = '\0'; + if (0 != STAT (dname, &istat)) + { + GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING, "stat", dname); + GNUNET_free (dname); + return GNUNET_SYSERR; + } + if (!S_ISDIR (istat.st_mode)) + { + GNUNET_log (GNUNET_ERROR_TYPE_WARNING, + _("Expected `%s' to be a directory!\n"), dirName); + GNUNET_free (dname); + return GNUNET_SYSERR; + } + errno = 0; + dinfo = OPENDIR (dname); + if ((errno == EACCES) || (dinfo == NULL)) + { + GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING, "opendir", dname); + if (dinfo != NULL) + closedir (dinfo); + GNUNET_free (dname); + return GNUNET_SYSERR; + } + name_len = 256; + n_size = strlen (dname) + name_len + 2; + name = GNUNET_malloc (n_size); + while ((finfo = readdir (dinfo)) != NULL) + { + if ((0 == strcmp (finfo->d_name, ".")) || + (0 == strcmp (finfo->d_name, ".."))) + continue; + if (callback != NULL) + { + if (name_len < strlen (finfo->d_name)) + { + GNUNET_free (name); + name_len = strlen (finfo->d_name); + n_size = strlen (dname) + name_len + 2; + name = GNUNET_malloc (n_size); + } + /* dname can end in "/" only if dname == "/"; + if dname does not end in "/", we need to add + a "/" (otherwise, we must not!) */ + GNUNET_snprintf (name, + n_size, + "%s%s%s", + dname, + (strcmp (dname, DIR_SEPARATOR_STR) == + 0) ? "" : DIR_SEPARATOR_STR, finfo->d_name); + if (GNUNET_OK != callback (data, name)) + { + closedir (dinfo); + GNUNET_free (name); + GNUNET_free (dname); + return GNUNET_SYSERR; + } + } + count++; + } + closedir (dinfo); + GNUNET_free (name); + GNUNET_free (dname); + return count; +} + + +/** + * Opaque handle used for iterating over a directory. + */ +struct GNUNET_DISK_DirectoryIterator +{ + /** + * Our scheduler. + */ + struct GNUNET_SCHEDULER_Handle *sched; + + /** + * Function to call on directory entries. + */ + GNUNET_DISK_DirectoryIteratorCallback callback; + + /** + * Closure for callback. + */ + void *callback_cls; + + /** + * Reference to directory. + */ + DIR *directory; + + /** + * Directory name. + */ + char *dirname; + + /** + * Next filename to process. + */ + char *next_name; + + /** + * Our priority. + */ + enum GNUNET_SCHEDULER_Priority priority; + +}; + + +/** + * Task used by the directory iterator. + */ +static void +directory_iterator_task (void *cls, + const struct GNUNET_SCHEDULER_TaskContext *tc) +{ + struct GNUNET_DISK_DirectoryIterator *iter = cls; + char *name; + + name = iter->next_name; + GNUNET_assert (name != NULL); + iter->next_name = NULL; + iter->callback (iter->callback_cls, iter, name, iter->dirname); + GNUNET_free (name); +} + + +/** + * This function must be called during the DiskIteratorCallback + * (exactly once) to schedule the task to process the next + * filename in the directory (if there is one). + * + * @param iter opaque handle for the iterator + * @param can set to GNUNET_YES to terminate the iteration early + * @return GNUNET_YES if iteration will continue, + * GNUNET_NO if this was the last entry (and iteration is complete), + * GNUNET_SYSERR if abort was YES + */ +int +GNUNET_DISK_directory_iterator_next (struct GNUNET_DISK_DirectoryIterator + *iter, int can) +{ + struct dirent *finfo; + + GNUNET_assert (iter->next_name == NULL); + if (can == GNUNET_YES) + { + closedir (iter->directory); + GNUNET_free (iter->dirname); + GNUNET_free (iter); + return GNUNET_SYSERR; + } + while (NULL != (finfo = readdir (iter->directory))) + { + if ((0 == strcmp (finfo->d_name, ".")) || + (0 == strcmp (finfo->d_name, ".."))) + continue; + GNUNET_asprintf (&iter->next_name, + "%s%s%s", + iter->dirname, DIR_SEPARATOR_STR, finfo->d_name); + break; + } + if (finfo == NULL) + { + GNUNET_DISK_directory_iterator_next (iter, GNUNET_YES); + return GNUNET_NO; + } + GNUNET_SCHEDULER_add_after (iter->sched, + GNUNET_YES, + iter->priority, + GNUNET_SCHEDULER_NO_PREREQUISITE_TASK, + &directory_iterator_task, iter); + return GNUNET_YES; +} + + +/** + * Scan a directory for files using the scheduler to run a task for + * each entry. The name of the directory must be expanded first (!). + * If a scheduler does not need to be used, GNUNET_DISK_directory_scan + * may provide a simpler API. + * + * @param sched scheduler to use + * @param prio priority to use + * @param dirName the name of the directory + * @param callback the method to call for each file + * @param callback_cls closure for callback + */ +void +GNUNET_DISK_directory_iterator_start (struct GNUNET_SCHEDULER_Handle *sched, + enum GNUNET_SCHEDULER_Priority prio, + const char *dirName, + GNUNET_DISK_DirectoryIteratorCallback + callback, void *callback_cls) +{ + struct GNUNET_DISK_DirectoryIterator *di; + + di = GNUNET_malloc (sizeof (struct GNUNET_DISK_DirectoryIterator)); + di->sched = sched; + di->callback = callback; + di->callback_cls = callback_cls; + di->directory = OPENDIR (dirName); + di->dirname = GNUNET_strdup (dirName); + di->priority = prio; + GNUNET_DISK_directory_iterator_next (di, GNUNET_NO); +} + + +static int +remove_helper (void *unused, const char *fn) +{ + GNUNET_DISK_directory_remove (fn); + return GNUNET_OK; +} + +/** + * Remove all files in a directory (rm -rf). Call with + * caution. + * + * + * @param fileName the file to remove + * @return GNUNET_OK on success, GNUNET_SYSERR on error + */ +int +GNUNET_DISK_directory_remove (const char *fileName) +{ + struct stat istat; + + if (0 != LSTAT (fileName, &istat)) + return GNUNET_NO; /* file may not exist... */ + if (UNLINK (fileName) == 0) + return GNUNET_OK; + if ((errno != EISDIR) && + /* EISDIR is not sufficient in all cases, e.g. + sticky /tmp directory may result in EPERM on BSD. + So we also explicitly check "isDirectory" */ + (GNUNET_YES != GNUNET_DISK_directory_test (fileName))) + { + GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING, "rmdir", fileName); + return GNUNET_SYSERR; + } + if (GNUNET_SYSERR == + GNUNET_DISK_directory_scan (fileName, remove_helper, NULL)) + return GNUNET_SYSERR; + if (0 != RMDIR (fileName)) + { + GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING, "rmdir", fileName); + return GNUNET_SYSERR; + } + return GNUNET_OK; +} + +void +GNUNET_DISK_file_close (const char *filename, int fd) +{ + if (0 != CLOSE (fd)) + GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING, "close", filename); +} + +int +GNUNET_DISK_file_open (const char *filename, int oflag, ...) +{ + char *fn; + int mode; + int ret; +#ifdef MINGW + char szFile[_MAX_PATH + 1]; + long lRet; + + if ((lRet = plibc_conv_to_win_path (filename, szFile)) != ERROR_SUCCESS) + { + errno = ENOENT; + SetLastError (lRet); + GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING, + "plibc_conv_to_win_path", filename); + return -1; + } + fn = GNUNET_strdup (szFile); +#else + fn = GNUNET_STRINGS_filename_expand (filename); +#endif + if (oflag & O_CREAT) + { + va_list arg; + va_start (arg, oflag); + mode = va_arg (arg, int); + va_end (arg); + } + else + { + mode = 0; + } +#ifdef MINGW + /* set binary mode */ + oflag |= O_BINARY; +#endif + ret = OPEN (fn, oflag, mode); + if (ret == -1) + GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING, "open", fn); + GNUNET_free (fn); + return ret; +} + +#define COPY_BLK_SIZE 65536 + +/** + * Copy a file. + * @return GNUNET_OK on success, GNUNET_SYSERR on error + */ +int +GNUNET_DISK_file_copy (const char *src, const char *dst) +{ + char *buf; + unsigned long long pos; + unsigned long long size; + unsigned long long len; + int in; + int out; + + if (GNUNET_OK != GNUNET_DISK_file_size (src, &size, GNUNET_YES)) + return GNUNET_SYSERR; + pos = 0; + in = GNUNET_DISK_file_open (src, O_RDONLY | O_LARGEFILE); + if (in == -1) + return GNUNET_SYSERR; + out = GNUNET_DISK_file_open (dst, + O_LARGEFILE | O_WRONLY | O_CREAT | O_EXCL, + S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP); + if (out == -1) + { + GNUNET_DISK_file_close (src, in); + return GNUNET_SYSERR; + } + buf = GNUNET_malloc (COPY_BLK_SIZE); + while (pos < size) + { + len = COPY_BLK_SIZE; + if (len > size - pos) + len = size - pos; + if (len != READ (in, buf, len)) + goto FAIL; + if (len != WRITE (out, buf, len)) + goto FAIL; + pos += len; + } + GNUNET_free (buf); + GNUNET_DISK_file_close (src, in); + GNUNET_DISK_file_close (dst, out); + return GNUNET_OK; +FAIL: + GNUNET_free (buf); + GNUNET_DISK_file_close (src, in); + GNUNET_DISK_file_close (dst, out); + return GNUNET_SYSERR; +} + + +/** + * @brief Removes special characters as ':' from a filename. + * @param fn the filename to canonicalize + */ +void +GNUNET_DISK_filename_canonicalize (char *fn) +{ + char *idx; + char c; + + idx = fn; + while (*idx) + { + c = *idx; + + if (c == '/' || c == '\\' || c == ':' || c == '*' || c == '?' || + c == '"' || c == '<' || c == '>' || c == '|') + { + *idx = '_'; + } + + idx++; + } +} + + + +/** + * @brief Change owner of a file + */ +int +GNUNET_DISK_file_change_owner (const char *filename, const char *user) +{ +#ifndef MINGW + struct passwd *pws; + + pws = getpwnam (user); + if (pws == NULL) + { + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + _("Cannot obtain information about user `%s': %s\n"), + user, STRERROR (errno)); + return GNUNET_SYSERR; + } + if (0 != chown (filename, pws->pw_uid, pws->pw_gid)) + GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING, "chown", filename); +#endif + return GNUNET_OK; +} + + +/** + * Construct full path to a file inside of the private + * directory used by GNUnet. Also creates the corresponding + * directory. If the resulting name is supposed to be + * a directory, end the last argument in '/' (or pass + * DIR_SEPARATOR_STR as the last argument before NULL). + * + * @param serviceName name of the service + * @param varargs is NULL-terminated list of + * path components to append to the + * private directory name. + * @return the constructed filename + */ +char * +GNUNET_DISK_get_home_filename (struct GNUNET_CONFIGURATION_Handle *cfg, + const char *serviceName, ...) +{ + const char *c; + char *pfx; + char *ret; + va_list ap; + unsigned int needed; + + if (GNUNET_OK != + GNUNET_CONFIGURATION_get_value_filename (cfg, + serviceName, "HOME", &pfx)) + return NULL; + if (pfx == NULL) + { + GNUNET_log (GNUNET_ERROR_TYPE_WARNING, + _("No `%s' specified for service `%s' in configuration.\n"), + "HOME", serviceName); + return NULL; + } + needed = strlen (pfx) + 2; + if ((pfx[strlen (pfx) - 1] != '/') && (pfx[strlen (pfx) - 1] != '\\')) + needed++; + va_start (ap, serviceName); + while (1) + { + c = va_arg (ap, const char *); + if (c == NULL) + break; + needed += strlen (c); + if ((c[strlen (c) - 1] != '/') && (c[strlen (c) - 1] != '\\')) + needed++; + } + va_end (ap); + ret = GNUNET_malloc (needed); + strcpy (ret, pfx); + GNUNET_free (pfx); + va_start (ap, serviceName); + while (1) + { + c = va_arg (ap, const char *); + if (c == NULL) + break; + if ((c[strlen (c) - 1] != '/') && (c[strlen (c) - 1] != '\\')) + strcat (ret, DIR_SEPARATOR_STR); + strcat (ret, c); + } + va_end (ap); + if ((ret[strlen (ret) - 1] != '/') && (ret[strlen (ret) - 1] != '\\')) + GNUNET_DISK_directory_create_for_file (ret); + else + GNUNET_DISK_directory_create (ret); + return ret; +} + + + +/* end of disk.c */ diff --git a/src/util/getopt.c b/src/util/getopt.c new file mode 100644 index 000000000..e069e76f7 --- /dev/null +++ b/src/util/getopt.c @@ -0,0 +1,1077 @@ +/* Getopt for GNU. + NOTE: getopt is now part of the C library, so if you don't know what + "Keep this file name-space clean" means, talk to roland@gnu.ai.mit.edu + before changing it! + + Copyright (C) 1987, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97 + Free Software Foundation, Inc. + +NOTE: The canonical source of this file is maintained with the GNU C Library. +Bugs can be reported to bug-glibc@prep.ai.mit.edu. + +This program is free software; you can redistribute it and/or modify it +under the terms of the GNU General Public License as published by the +Free Software Foundation; either version 2, or (at your option) any +later version. + +This program 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 General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, +USA. + + +This code was heavily modified for GNUnet. +Copyright (C) 2006 Christian Grothoff +*/ + +/** + * @file util/getopt.c + * @brief GNU style option parsing + * + * TODO: get rid of statics (make reentrant) and + * replace main GNU getopt parser with one that + * actually fits our API. + */ + +#include "platform.h" +#include "gnunet_common.h" +#include "gnunet_getopt_lib.h" + +#ifdef VMS +# include +# if HAVE_STRING_H - 0 +# include +# endif +#endif + +#if defined (WIN32) && !defined (__CYGWIN32__) +/* It's not Unix, really. See? Capital letters. */ +# include +# define getpid() GetCurrentProcessId() +#endif + +#ifndef _ +/* This is for other GNU distributions with internationalized messages. + When compiling libc, the _ macro is predefined. */ +# ifdef HAVE_LIBINTL_H +# include +# define _(msgid) gettext (msgid) +# else +# define _(msgid) (msgid) +# endif +#endif + +/* Describe the long-named options requested by the application. + The LONG_OPTIONS argument to getopt_long or getopt_long_only is a vector + of `struct GNoption' terminated by an element containing a name which is + zero. + + The field `has_arg' is: + no_argument (or 0) if the option does not take an argument, + required_argument (or 1) if the option requires an argument, + optional_argument (or 2) if the option takes an optional argument. + + If the field `flag' is not NULL, it points to a variable that is set + to the value given in the field `val' when the option is found, but + left unchanged if the option is not found. + + To have a long-named option do something other than set an `int' to + a compiled-in constant, such as set a value from `GNoptarg', set the + option's `flag' field to zero and its `val' field to a nonzero + value (the equivalent single-letter option character, if there is + one). For long options that have a zero `flag' field, `getopt' + returns the contents of the `val' field. */ + +struct GNoption +{ + const char *name; + /* has_arg can't be an enum because some compilers complain about + type mismatches in all the code that assumes it is an int. */ + int has_arg; + int *flag; + int val; +}; + + +/* This version of `getopt' appears to the caller like standard Unix `getopt' + but it behaves differently for the user, since it allows the user + to intersperse the options with the other arguments. + + As `getopt' works, it permutes the elements of ARGV so that, + when it is done, all the options precede everything else. Thus + all application programs are extended to handle flexible argument order. + + Setting the environment variable POSIXLY_CORRECT disables permutation. + Then the behavior is completely standard. + + GNU application programs can use a third alternative mode in which + they can distinguish the relative order of options and other arguments. */ + +/* For communication from `getopt' to the caller. + When `getopt' finds an option that takes an argument, + the argument value is returned here. + Also, when `ordering' is RETURN_IN_ORDER, + each non-option ARGV-element is returned here. */ + +static char *GNoptarg = NULL; + +/* Index in ARGV of the next element to be scanned. + This is used for communication to and from the caller + and for communication between successive calls to `getopt'. + + On entry to `getopt', zero means this is the first call; initialize. + + When `getopt' returns -1, this is the index of the first of the + non-option elements that the caller should itself scan. + + Otherwise, `GNoptind' communicates from one call to the next + how much of ARGV has been scanned so far. */ + +/* 1003.2 says this must be 1 before any call. */ +static int GNoptind = 1; + +/* Formerly, initialization of getopt depended on GNoptind==0, which + causes problems with re-calling getopt as programs generally don't + know that. */ + +static int __getopt_initialized = 0; + +/* The next char to be scanned in the option-element + in which the last option character we returned was found. + This allows us to pick up the scan where we left off. + + If this is zero, or a null string, it means resume the scan + by advancing to the next ARGV-element. */ + +static char *nextchar; + +/* Callers store zero here to inhibit the error message + for unrecognized options. */ + +static int GNopterr = 1; + +/* Set to an option character which was unrecognized. + This must be initialized on some systems to avoid linking in the + system's own getopt implementation. */ + +static int GNoptopt = '?'; + +/* Describe how to deal with options that follow non-option ARGV-elements. + + If the caller did not specify anything, + the default is REQUIRE_ORDER if the environment variable + POSIXLY_CORRECT is defined, PERMUTE otherwise. + + REQUIRE_ORDER means don't recognize them as options; + stop option processing when the first non-option is seen. + This is what Unix does. + This mode of operation is selected by either setting the environment + variable POSIXLY_CORRECT, or using `+' as the first character + of the list of option characters. + + PERMUTE is the default. We GNUNET_CRYPTO_random_permute the contents of ARGV as we scan, + so that eventually all the non-options are at the end. This allows options + to be given in any order, even with programs that were not written to + expect this. + + RETURN_IN_ORDER is an option available to programs that were written + to expect GNoptions and other ARGV-elements in any order and that care about + the ordering of the two. We describe each non-option ARGV-element + as if it were the argument of an option with character code 1. + Using `-' as the first character of the list of option characters + selects this mode of operation. + + The special argument `--' forces an end of option-scanning regardless + of the value of `ordering'. In the case of RETURN_IN_ORDER, only + `--' can cause `getopt' to return -1 with `GNoptind' != ARGC. */ + +static enum +{ + REQUIRE_ORDER, PERMUTE, RETURN_IN_ORDER +} ordering; + +/* Value of POSIXLY_CORRECT environment variable. */ +static char *posixly_correct; + +#ifdef __GNU_LIBRARY__ +/* We want to avoid inclusion of string.h with non-GNU libraries + because there are many ways it can cause trouble. + On some systems, it contains special magic macros that don't work + in GCC. */ +#include +#define my_index strchr +#else + +/* Avoid depending on library functions or files + whose names are inconsistent. */ + +char *getenv (); + +static char * +my_index (str, chr) + const char *str; + int chr; +{ + while (*str) + { + if (*str == chr) + return (char *) str; + str++; + } + return 0; +} + +/* If using GCC, we can safely declare strlen this way. + If not using GCC, it is ok not to declare it. */ +#ifdef __GNUC__ +/* Note that Motorola Delta 68k R3V7 comes with GCC but not stddef.h. + That was relevant to code that was here before. */ +#if !defined (__STDC__) || !__STDC__ +/* gcc with -traditional declares the built-in strlen to return int, + and has done so at least since version 2.4.5. -- rms. */ +extern int strlen (const char *); +#endif /* not __STDC__ */ +#endif /* __GNUC__ */ + +#endif /* not __GNU_LIBRARY__ */ + +/* Handle permutation of arguments. */ + +/* Describe the part of ARGV that contains non-options that have + been skipped. `first_nonopt' is the index in ARGV of the first of them; + `last_nonopt' is the index after the last of them. */ + +static int first_nonopt; +static int last_nonopt; + +#ifdef _LIBC +/* Bash 2.0 gives us an environment variable containing flags + indicating ARGV elements that should not be considered arguments. */ + +/* Defined in getopt_init.c */ +extern char *__getopt_nonoption_flags; + +static int nonoption_flags_max_len; +static int nonoption_flags_len; + +static int original_argc; +static char *const *original_argv; + +extern pid_t __libc_pid; + +/* Make sure the environment variable bash 2.0 puts in the environment + is valid for the getopt call we must make sure that the ARGV passed + to getopt is that one passed to the process. */ +static void + __attribute__ ((unused)) store_args_and_env (int argc, char *const *argv) +{ + /* XXX This is no good solution. We should rather copy the args so + that we can compare them later. But we must not use malloc(3). */ + original_argc = argc; + original_argv = argv; +} + +text_set_element (__libc_subinit, store_args_and_env); + +# define SWAP_FLAGS(ch1, ch2) \ + if (nonoption_flags_len > 0) \ + { \ + char __tmp = __getopt_nonoption_flags[ch1]; \ + __getopt_nonoption_flags[ch1] = __getopt_nonoption_flags[ch2]; \ + __getopt_nonoption_flags[ch2] = __tmp; \ + } +#else /* !_LIBC */ +# define SWAP_FLAGS(ch1, ch2) +#endif /* _LIBC */ + +/* Exchange two adjacent subsequences of ARGV. + One subsequence is elements [first_nonopt,last_nonopt) + which contains all the non-options that have been skipped so far. + The other is elements [last_nonopt,GNoptind), which contains all + the options processed since those non-options were skipped. + + `first_nonopt' and `last_nonopt' are relocated so that they describe + the new indices of the non-options in ARGV after they are moved. */ + +#if defined (__STDC__) && __STDC__ +static void exchange (char **); +#endif + +static void +exchange (argv) + char **argv; +{ + int bottom = first_nonopt; + int middle = last_nonopt; + int top = GNoptind; + char *tem; + + /* Exchange the shorter segment with the far end of the longer segment. + That puts the shorter segment into the right place. + It leaves the longer segment in the right place overall, + but it consists of two parts that need to be swapped next. */ + +#ifdef _LIBC + /* First make sure the handling of the `__getopt_nonoption_flags' + string can work normally. Our top argument must be in the range + of the string. */ + if (nonoption_flags_len > 0 && top >= nonoption_flags_max_len) + { + /* We must extend the array. The user plays games with us and + presents new arguments. */ + char *new_str = malloc (top + 1); + if (new_str == NULL) + nonoption_flags_len = nonoption_flags_max_len = 0; + else + { + memcpy (new_str, __getopt_nonoption_flags, nonoption_flags_max_len); + memset (&new_str[nonoption_flags_max_len], '\0', + top + 1 - nonoption_flags_max_len); + nonoption_flags_max_len = top + 1; + __getopt_nonoption_flags = new_str; + } + } +#endif + + while (top > middle && middle > bottom) + { + if (top - middle > middle - bottom) + { + /* Bottom segment is the short one. */ + int len = middle - bottom; + register int i; + + /* Swap it with the top part of the top segment. */ + for (i = 0; i < len; i++) + { + tem = argv[bottom + i]; + argv[bottom + i] = argv[top - (middle - bottom) + i]; + argv[top - (middle - bottom) + i] = tem; + SWAP_FLAGS (bottom + i, top - (middle - bottom) + i); + } + /* Exclude the moved bottom segment from further swapping. */ + top -= len; + } + else + { + /* Top segment is the short one. */ + int len = top - middle; + register int i; + + /* Swap it with the bottom part of the bottom segment. */ + for (i = 0; i < len; i++) + { + tem = argv[bottom + i]; + argv[bottom + i] = argv[middle + i]; + argv[middle + i] = tem; + SWAP_FLAGS (bottom + i, middle + i); + } + /* Exclude the moved top segment from further swapping. */ + bottom += len; + } + } + + /* Update records for the slots the non-options now occupy. */ + + first_nonopt += (GNoptind - last_nonopt); + last_nonopt = GNoptind; +} + +/* Initialize the internal data when the first call is made. */ + +#if defined (__STDC__) && __STDC__ +static const char *_getopt_initialize (int, char *const *, const char *); +#endif +static const char * +_getopt_initialize (argc, argv, optstring) + int argc; + char *const *argv; + const char *optstring; +{ + /* Start processing options with ARGV-element 1 (since ARGV-element 0 + is the program name); the sequence of previously skipped + non-option ARGV-elements is empty. */ + + first_nonopt = last_nonopt = GNoptind; + + nextchar = NULL; + + posixly_correct = getenv ("POSIXLY_CORRECT"); + + /* Determine how to handle the ordering of options and nonoptions. */ + + if (optstring[0] == '-') + { + ordering = RETURN_IN_ORDER; + ++optstring; + } + else if (optstring[0] == '+') + { + ordering = REQUIRE_ORDER; + ++optstring; + } + else if (posixly_correct != NULL) + ordering = REQUIRE_ORDER; + else + ordering = PERMUTE; + +#ifdef _LIBC + if (posixly_correct == NULL + && argc == original_argc && argv == original_argv) + { + if (nonoption_flags_max_len == 0) + { + if (__getopt_nonoption_flags == NULL + || __getopt_nonoption_flags[0] == '\0') + nonoption_flags_max_len = -1; + else + { + const char *orig_str = __getopt_nonoption_flags; + int len = nonoption_flags_max_len = strlen (orig_str); + if (nonoption_flags_max_len < argc) + nonoption_flags_max_len = argc; + __getopt_nonoption_flags = + (char *) malloc (nonoption_flags_max_len); + if (__getopt_nonoption_flags == NULL) + nonoption_flags_max_len = -1; + else + { + memcpy (__getopt_nonoption_flags, orig_str, len); + memset (&__getopt_nonoption_flags[len], '\0', + nonoption_flags_max_len - len); + } + } + } + nonoption_flags_len = nonoption_flags_max_len; + } + else + nonoption_flags_len = 0; +#endif + + return optstring; +} + +/* Scan elements of ARGV (whose length is ARGC) for option characters + given in OPTSTRING. + + If an element of ARGV starts with '-', and is not exactly "-" or "--", + then it is an option element. The characters of this element + (aside from the initial '-') are option characters. If `getopt' + is called repeatedly, it returns successively each of the option characters + from each of the option elements. + + If `getopt' finds another option character, it returns that character, + updating `GNoptind' and `nextchar' so that the next call to `getopt' can + resume the scan with the following option character or ARGV-element. + + If there are no more option characters, `getopt' returns -1. + Then `GNoptind' is the index in ARGV of the first ARGV-element + that is not an option. (The ARGV-elements have been permuted + so that those that are not options now come last.) + + OPTSTRING is a string containing the legitimate option characters. + If an option character is seen that is not listed in OPTSTRING, + return '?' after printing an error message. If you set `GNopterr' to + zero, the error message is suppressed but we still return '?'. + + If a char in OPTSTRING is followed by a colon, that means it wants an arg, + so the following text in the same ARGV-element, or the text of the following + ARGV-element, is returned in `GNoptarg'. Two colons mean an option that + wants an optional arg; if there is text in the current ARGV-element, + it is returned in `GNoptarg', otherwise `GNoptarg' is set to zero. + + If OPTSTRING starts with `-' or `+', it requests different methods of + handling the non-option ARGV-elements. + See the comments about RETURN_IN_ORDER and REQUIRE_ORDER, above. + + Long-named options begin with `--' instead of `-'. + Their names may be abbreviated as long as the abbreviation is unique + or is an exact match for some defined option. If they have an + argument, it follows the option name in the same ARGV-element, separated + from the option name by a `=', or else the in next ARGV-element. + When `getopt' finds a long-named option, it returns 0 if that option's + `flag' field is nonzero, the value of the option's `val' field + if the `flag' field is zero. + + The elements of ARGV aren't really const, because we GNUNET_CRYPTO_random_permute them. + But we pretend they're const in the prototype to be compatible + with other systems. + + LONGOPTS is a vector of `struct GNoption' terminated by an + element containing a name which is zero. + + LONGIND returns the index in LONGOPT of the long-named option found. + It is only valid when a long-named option has been found by the most + recent call. + + If LONG_ONLY is nonzero, '-' as well as '--' can introduce + long-named options. */ + +static int +GN_getopt_internal (argc, argv, optstring, longopts, longind, long_only) + int argc; + char *const *argv; + const char *optstring; + const struct GNoption *longopts; + int *longind; + int long_only; +{ + GNoptarg = NULL; + + if (GNoptind == 0 || !__getopt_initialized) + { + if (GNoptind == 0) + GNoptind = 1; /* Don't scan ARGV[0], the program name. */ + optstring = _getopt_initialize (argc, argv, optstring); + __getopt_initialized = 1; + } + + /* Test whether ARGV[GNoptind] points to a non-option argument. + Either it does not have option syntax, or there is an environment flag + from the shell indicating it is not an option. The later information + is only used when the used in the GNU libc. */ +#ifdef _LIBC +#define NONOPTION_P (argv[GNoptind][0] != '-' || argv[GNoptind][1] == '\0' \ + || (GNoptind < nonoption_flags_len \ + && __getopt_nonoption_flags[GNoptind] == '1')) +#else +#define NONOPTION_P (argv[GNoptind][0] != '-' || argv[GNoptind][1] == '\0') +#endif + + if (nextchar == NULL || *nextchar == '\0') + { + /* Advance to the next ARGV-element. */ + + /* Give FIRST_NONOPT & LAST_NONOPT rational values if GNoptind has been + moved back by the user (who may also have changed the arguments). */ + if (last_nonopt > GNoptind) + last_nonopt = GNoptind; + if (first_nonopt > GNoptind) + first_nonopt = GNoptind; + + if (ordering == PERMUTE) + { + /* If we have just processed some options following some non-options, + exchange them so that the options come first. */ + + if (first_nonopt != last_nonopt && last_nonopt != GNoptind) + exchange ((char **) argv); + else if (last_nonopt != GNoptind) + first_nonopt = GNoptind; + + /* Skip any additional non-options + and extend the range of non-options previously skipped. */ + + while (GNoptind < argc && NONOPTION_P) + GNoptind++; + last_nonopt = GNoptind; + } + + /* The special ARGV-element `--' means premature end of options. + Skip it like a null option, + then exchange with previous non-options as if it were an option, + then skip everything else like a non-option. */ + if (GNoptind != argc && !strcmp (argv[GNoptind], "--")) + { + GNoptind++; + + if (first_nonopt != last_nonopt && last_nonopt != GNoptind) + exchange ((char **) argv); + else if (first_nonopt == last_nonopt) + first_nonopt = GNoptind; + last_nonopt = argc; + + GNoptind = argc; + } + + /* If we have done all the ARGV-elements, stop the scan + and back over any non-options that we skipped and permuted. */ + + if (GNoptind == argc) + { + /* Set the next-arg-index to point at the non-options + that we previously skipped, so the caller will digest them. */ + if (first_nonopt != last_nonopt) + GNoptind = first_nonopt; + return -1; + } + + /* If we have come to a non-option and did not permute it, + either stop the scan or describe it to the caller and pass it by. */ + + if (NONOPTION_P) + { + if (ordering == REQUIRE_ORDER) + return -1; + GNoptarg = argv[GNoptind++]; + return 1; + } + + /* We have found another option-ARGV-element. + Skip the initial punctuation. */ + + nextchar = (argv[GNoptind] + 1 + + (longopts != NULL && argv[GNoptind][1] == '-')); + } + + /* Decode the current option-ARGV-element. */ + + /* Check whether the ARGV-element is a long option. + + If long_only and the ARGV-element has the form "-f", where f is + a valid short option, don't consider it an abbreviated form of + a long option that starts with f. Otherwise there would be no + way to give the -f short option. + + On the other hand, if there's a long option "fubar" and + the ARGV-element is "-fu", do consider that an abbreviation of + the long option, just like "--fu", and not "-f" with arg "u". + + This distinction seems to be the most useful approach. */ + + if (longopts != NULL + && (argv[GNoptind][1] == '-' + || (long_only + && (argv[GNoptind][2] + || !my_index (optstring, argv[GNoptind][1]))))) + { + char *nameend; + const struct GNoption *p; + const struct GNoption *pfound = NULL; + int exact = 0; + int ambig = 0; + int indfound = -1; + int option_index; + + for (nameend = nextchar; *nameend && *nameend != '='; nameend++) + /* Do nothing. */ ; + + /* Test all long options for either exact match + or abbreviated matches. */ + for (p = longopts, option_index = 0; p->name; p++, option_index++) + if (!strncmp (p->name, nextchar, nameend - nextchar)) + { + if ((unsigned int) (nameend - nextchar) + == (unsigned int) strlen (p->name)) + { + /* Exact match found. */ + pfound = p; + indfound = option_index; + exact = 1; + break; + } + else if (pfound == NULL) + { + /* First nonexact match found. */ + pfound = p; + indfound = option_index; + } + else + /* Second or later nonexact match found. */ + ambig = 1; + } + + if (ambig && !exact) + { + if (GNopterr) + fprintf (stderr, _("%s: option `%s' is ambiguous\n"), + argv[0], argv[GNoptind]); + nextchar += strlen (nextchar); + GNoptind++; + GNoptopt = 0; + return '?'; + } + + if (pfound != NULL) + { + option_index = indfound; + GNoptind++; + if (*nameend) + { + /* Don't test has_arg with >, because some C compilers don't + allow it to be used on enums. */ + if (pfound->has_arg) + GNoptarg = nameend + 1; + else + { + if (GNopterr) + { + if (argv[GNoptind - 1][1] == '-') + /* --option */ + fprintf (stderr, + _ + ("%s: option `--%s' does not allow an argument\n"), + argv[0], pfound->name); + else + /* +option or -option */ + fprintf (stderr, + _ + ("%s: option `%c%s' does not allow an argument\n"), + argv[0], argv[GNoptind - 1][0], + pfound->name); + } + nextchar += strlen (nextchar); + + GNoptopt = pfound->val; + return '?'; + } + } + else if (pfound->has_arg == 1) + { + if (GNoptind < argc) + { + GNoptarg = argv[GNoptind++]; + } + else + { + if (GNopterr) + { + fprintf (stderr, + _("%s: option `%s' requires an argument\n"), + argv[0], argv[GNoptind - 1]); + } + nextchar += strlen (nextchar); + GNoptopt = pfound->val; + return (optstring[0] == ':') ? ':' : '?'; + } + } + nextchar += strlen (nextchar); + if (longind != NULL) + *longind = option_index; + if (pfound->flag) + { + *(pfound->flag) = pfound->val; + return 0; + } + return pfound->val; + } + + /* Can't find it as a long option. If this is not getopt_long_only, + or the option starts with '--' or is not a valid short + option, then it's an error. + Otherwise interpret it as a short option. */ + if (!long_only || argv[GNoptind][1] == '-' + || my_index (optstring, *nextchar) == NULL) + { + if (GNopterr) + { + if (argv[GNoptind][1] == '-') + /* --option */ + fprintf (stderr, _("%s: unrecognized option `--%s'\n"), + argv[0], nextchar); + else + /* +option or -option */ + fprintf (stderr, _("%s: unrecognized option `%c%s'\n"), + argv[0], argv[GNoptind][0], nextchar); + } + nextchar = (char *) ""; + GNoptind++; + GNoptopt = 0; + return '?'; + } + } + + /* Look at and handle the next short option-character. */ + + { + char c = *nextchar++; + char *temp = my_index (optstring, c); + + /* Increment `GNoptind' when we start to process its last character. */ + if (*nextchar == '\0') + ++GNoptind; + + if (temp == NULL || c == ':') + { + if (GNopterr) + { + if (posixly_correct) + /* 1003.2 specifies the format of this message. */ + fprintf (stderr, _("%s: illegal option -- %c\n"), argv[0], c); + else + fprintf (stderr, _("%s: invalid option -- %c\n"), argv[0], c); + } + GNoptopt = c; + return '?'; + } + /* Convenience. Treat POSIX -W foo same as long option --foo */ + if (temp[0] == 'W' && temp[1] == ';') + { + char *nameend; + const struct GNoption *p; + const struct GNoption *pfound = NULL; + int exact = 0; + int ambig = 0; + int indfound = 0; + int option_index; + + /* This is an option that requires an argument. */ + if (*nextchar != '\0') + { + GNoptarg = nextchar; + /* If we end this ARGV-element by taking the rest as an arg, + we must advance to the next element now. */ + GNoptind++; + } + else if (GNoptind == argc) + { + if (GNopterr) + { + /* 1003.2 specifies the format of this message. */ + fprintf (stderr, _("%s: option requires an argument -- %c\n"), + argv[0], c); + } + GNoptopt = c; + if (optstring[0] == ':') + c = ':'; + else + c = '?'; + return c; + } + else + /* We already incremented `GNoptind' once; + increment it again when taking next ARGV-elt as argument. */ + GNoptarg = argv[GNoptind++]; + + /* GNoptarg is now the argument, see if it's in the + table of longopts. */ + + for (nextchar = nameend = GNoptarg; *nameend && *nameend != '='; + nameend++) + /* Do nothing. */ ; + + /* Test all long options for either exact match + or abbreviated matches. */ + for (p = longopts, option_index = 0; p->name; p++, option_index++) + if (!strncmp (p->name, nextchar, nameend - nextchar)) + { + if ((unsigned int) (nameend - nextchar) == strlen (p->name)) + { + /* Exact match found. */ + pfound = p; + indfound = option_index; + exact = 1; + break; + } + else if (pfound == NULL) + { + /* First nonexact match found. */ + pfound = p; + indfound = option_index; + } + else + /* Second or later nonexact match found. */ + ambig = 1; + } + if (ambig && !exact) + { + if (GNopterr) + fprintf (stderr, _("%s: option `-W %s' is ambiguous\n"), + argv[0], argv[GNoptind]); + nextchar += strlen (nextchar); + GNoptind++; + return '?'; + } + if (pfound != NULL) + { + option_index = indfound; + if (*nameend) + { + /* Don't test has_arg with >, because some C compilers don't + allow it to be used on enums. */ + if (pfound->has_arg) + GNoptarg = nameend + 1; + else + { + if (GNopterr) + fprintf (stderr, _("\ +%s: option `-W %s' does not allow an argument\n"), argv[0], pfound->name); + + nextchar += strlen (nextchar); + return '?'; + } + } + else if (pfound->has_arg == 1) + { + if (GNoptind < argc) + GNoptarg = argv[GNoptind++]; + else + { + if (GNopterr) + fprintf (stderr, + _("%s: option `%s' requires an argument\n"), + argv[0], argv[GNoptind - 1]); + nextchar += strlen (nextchar); + return optstring[0] == ':' ? ':' : '?'; + } + } + nextchar += strlen (nextchar); + if (longind != NULL) + *longind = option_index; + if (pfound->flag) + { + *(pfound->flag) = pfound->val; + return 0; + } + return pfound->val; + } + nextchar = NULL; + return 'W'; /* Let the application handle it. */ + } + if (temp[1] == ':') + { + if (temp[2] == ':') + { + /* This is an option that accepts an argument optionally. */ + if (*nextchar != '\0') + { + GNoptarg = nextchar; + GNoptind++; + } + else + GNoptarg = NULL; + nextchar = NULL; + } + else + { + /* This is an option that requires an argument. */ + if (*nextchar != '\0') + { + GNoptarg = nextchar; + /* If we end this ARGV-element by taking the rest as an arg, + we must advance to the next element now. */ + GNoptind++; + } + else if (GNoptind == argc) + { + if (GNopterr) + { + /* 1003.2 specifies the format of this message. */ + fprintf (stderr, + _("%s: option requires an argument -- %c\n"), + argv[0], c); + } + GNoptopt = c; + if (optstring[0] == ':') + c = ':'; + else + c = '?'; + } + else + /* We already incremented `GNoptind' once; + increment it again when taking next ARGV-elt as argument. */ + GNoptarg = argv[GNoptind++]; + nextchar = NULL; + } + } + return c; + } +} + +static int +GNgetopt_long (int argc, + char *const *argv, + const char *options, + const struct GNoption *long_options, int *opt_index) +{ + return GN_getopt_internal (argc, argv, options, long_options, opt_index, 0); +} + +/* ******************** now the GNUnet specific modifications... ********************* */ + +/** + * Parse the command line. + * + * @param binaryName name of the binary / application with options + * @param cfg for storing/accessing configuration data + * @param allOptions defined options and handlers + * @param argc number of arguments + * @param argv actual arguments + * @return index into argv with first non-option + * argument, or -1 on error + */ +int +GNUNET_GETOPT_run (const char *binaryOptions, + struct GNUNET_CONFIGURATION_Handle *cfg, + const struct GNUNET_GETOPT_CommandLineOption *allOptions, + unsigned int argc, char *const *argv) +{ + struct GNoption *long_options; + struct GNUNET_GETOPT_CommandLineProcessorContext clpc; + int count; + int i; + char *shorts; + int spos; + int cont; + int c; + + GNUNET_assert (argc > 0); + GNoptind = 0; + clpc.binaryName = argv[0]; + clpc.binaryOptions = binaryOptions; + clpc.allOptions = allOptions; + clpc.argv = argv; + clpc.argc = argc; + clpc.cfg = cfg; + count = 0; + while (allOptions[count].name != NULL) + count++; + long_options = GNUNET_malloc (sizeof (struct GNoption) * (count + 1)); + shorts = GNUNET_malloc (count * 2 + 1); + spos = 0; + for (i = 0; i < count; i++) + { + long_options[i].name = allOptions[i].name; + long_options[i].has_arg = allOptions[i].require_argument; + long_options[i].flag = NULL; + long_options[i].val = allOptions[i].shortName; + shorts[spos++] = allOptions[i].shortName; + if (allOptions[i].require_argument != 0) + shorts[spos++] = ':'; + } + long_options[count].name = NULL; + long_options[count].has_arg = 0; + long_options[count].flag = NULL; + long_options[count].val = '\0'; + shorts[spos++] = '\0'; + cont = GNUNET_OK; + /* main getopt loop */ + while (cont == GNUNET_OK) + { + int option_index = 0; + c = GNgetopt_long (argc, argv, shorts, long_options, &option_index); + + if (c == GNUNET_SYSERR) + break; /* No more flags to process */ + + for (i = 0; i < count; i++) + { + clpc.currentArgument = GNoptind - 1; + if ((char) c == allOptions[i].shortName) + { + cont = allOptions[i].processor (&clpc, + allOptions[i].scls, + allOptions[i].name, GNoptarg); + break; + } + } + if (i == count) + { + fprintf (stderr, _("Use --help to get a list of options.\n")); + cont = GNUNET_SYSERR; + } + } + + GNUNET_free (shorts); + GNUNET_free (long_options); + if (cont == GNUNET_SYSERR) + return GNUNET_SYSERR; + return GNoptind; +} + +/* end of getopt.c */ diff --git a/src/util/getopt_helpers.c b/src/util/getopt_helpers.c new file mode 100644 index 000000000..3aea5d102 --- /dev/null +++ b/src/util/getopt_helpers.c @@ -0,0 +1,200 @@ +/* + This file is part of GNUnet + (C) 2006 Christian Grothoff (and other contributing authors) + + GNUnet is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published + by the Free Software Foundation; either version 2, 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 + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with GNUnet; see the file COPYING. If not, write to the + Free Software Foundation, Inc., 59 Temple Place - Suite 330, + Boston, MA 02111-1307, USA. +*/ + +/** + * @file src/util/getopt_helpers.c + * @brief implements command line that sets option + * @author Christian Grothoff + */ + +#include "platform.h" +#include "gnunet_common.h" +#include "gnunet_getopt_lib.h" + + +int +GNUNET_GETOPT_print_version_ (struct GNUNET_GETOPT_CommandLineProcessorContext + *ctx, void *scls, const char *option, + const char *value) +{ + const char *version = scls; + + printf ("%s v%s\n", ctx->binaryName, version); + return GNUNET_SYSERR; +} + + + +#define BORDER 29 + +int +GNUNET_GETOPT_format_help_ (struct GNUNET_GETOPT_CommandLineProcessorContext + *ctx, void *scls, const char *option, + const char *value) +{ + const char *about = scls; + int slen; + int i; + int j; + int ml; + int p; + char *scp; + const char *trans; + const struct GNUNET_GETOPT_CommandLineOption *opt; + + printf ("%s\n%s\n", ctx->binaryOptions, gettext (about)); + printf (_ + ("Arguments mandatory for long options are also mandatory for short options.\n")); + slen = 0; + i = 0; + opt = ctx->allOptions; + while (opt[i].description != NULL) + { + if (opt[i].shortName == '\0') + printf (" "); + else + printf (" -%c, ", opt[i].shortName); + printf ("--%s", opt[i].name); + slen = 8 + strlen (opt[i].name); + if (opt[i].argumentHelp != NULL) + { + printf ("=%s", opt[i].argumentHelp); + slen += 1 + strlen (opt[i].argumentHelp); + } + if (slen > BORDER) + { + printf ("\n%*s", BORDER, ""); + slen = BORDER; + } + if (slen < BORDER) + { + printf ("%*s", BORDER - slen, ""); + slen = BORDER; + } + trans = gettext (opt[i].description); + ml = strlen (trans); + p = 0; + OUTER: + while (ml - p > 78 - slen) + { + for (j = p + 78 - slen; j > p; j--) + { + if (isspace (trans[j])) + { + scp = GNUNET_malloc (j - p + 1); + memcpy (scp, &trans[p], j - p); + scp[j - p] = '\0'; + printf ("%s\n%*s", scp, BORDER + 2, ""); + GNUNET_free (scp); + p = j + 1; + slen = BORDER + 2; + goto OUTER; + } + } + /* could not find space to break line */ + scp = GNUNET_malloc (78 - slen + 1); + memcpy (scp, &trans[p], 78 - slen); + scp[78 - slen] = '\0'; + printf ("%s\n%*s", scp, BORDER + 2, ""); + GNUNET_free (scp); + slen = BORDER + 2; + p = p + 78 - slen; + } + /* print rest */ + if (p < ml) + printf ("%s\n", &trans[p]); + if (strlen (trans) == 0) + printf ("\n"); + i++; + } + printf ("Report bugs to gnunet-developers@gnu.org.\n" + "GNUnet home page: http://www.gnu.org/software/gnunet/\n" + "General help using GNU software: http://www.gnu.org/gethelp/\n"); + return GNUNET_SYSERR; +} + + +int +GNUNET_GETOPT_increment_value (struct + GNUNET_GETOPT_CommandLineProcessorContext *ctx, + void *scls, const char *cmdLineOption, + const char *value) +{ + int *val = scls; + (*val)++; + return GNUNET_OK; +} + +int +GNUNET_GETOPT_set_one (struct GNUNET_GETOPT_CommandLineProcessorContext *ctx, + void *scls, const char *option, const char *value) +{ + int *val = scls; + *val = 1; + return GNUNET_OK; +} + +int +GNUNET_GETOPT_set_string (struct GNUNET_GETOPT_CommandLineProcessorContext + *ctx, void *scls, const char *option, + const char *value) +{ + char **val = scls; + + GNUNET_assert (value != NULL); + if (NULL != *val) + GNUNET_free (*val); + *val = GNUNET_strdup (value); + return GNUNET_OK; +} + +int +GNUNET_GETOPT_set_ulong (struct GNUNET_GETOPT_CommandLineProcessorContext + *ctx, void *scls, const char *option, + const char *value) +{ + unsigned long long *val = scls; + if (1 != SSCANF (value, "%llu", val)) + { + fprintf (stderr, + _("You must pass a number to the `%s' option.\n"), "-X"); + return GNUNET_SYSERR; + } + return GNUNET_OK; +} + + +int +GNUNET_GETOPT_set_uint (struct GNUNET_GETOPT_CommandLineProcessorContext *ctx, + void *scls, const char *option, const char *value) +{ + unsigned int *val = scls; + + if (1 != SSCANF (value, "%u", val)) + { + fprintf (stderr, + _("You must pass a number to the `%s' option.\n"), "-X"); + return GNUNET_SYSERR; + } + return GNUNET_OK; +} + + +/* end of getopt_helpers.c */ diff --git a/src/util/network.c b/src/util/network.c new file mode 100644 index 000000000..22c1ca632 --- /dev/null +++ b/src/util/network.c @@ -0,0 +1,1239 @@ +/* + This file is part of GNUnet. + (C) 2009 Christian Grothoff (and other contributing authors) + + GNUnet is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published + by the Free Software Foundation; either version 2, 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 + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with GNUnet; see the file COPYING. If not, write to the + Free Software Foundation, Inc., 59 Temple Place - Suite 330, + Boston, MA 02111-1307, USA. +*/ + +/** + * @file util/network/network.c + * @brief basic, low-level TCP networking interface + * @author Christian Grothoff + * + * This code is rather complex. Only modify it if you + * 1) Have a NEW testcase showing that the new code + * is needed and correct + * 2) All EXISTING testcases pass with the new code + * These rules should apply in general, but for this + * module they are VERY, VERY important. + * + * TODO: + * - can we merge receive_ready and receive_again? + * - can we integrate the nth.timeout_task with the write_task's timeout? + */ + +#include "platform.h" +#include "gnunet_common.h" +#include "gnunet_network_lib.h" +#include "gnunet_scheduler_lib.h" + +#define DEBUG_NETWORK GNUNET_NO + +struct GNUNET_NETWORK_TransmitHandle +{ + + /** + * Function to call if the send buffer has notify_size + * bytes available. + */ + GNUNET_NETWORK_TransmitReadyNotify notify_ready; + + /** + * Closure for notify_ready. + */ + void *notify_ready_cls; + + /** + * Our socket handle. + */ + struct GNUNET_NETWORK_SocketHandle *sh; + + /** + * Timeout for receiving (in absolute time). + */ + struct GNUNET_TIME_Absolute transmit_timeout; + + /** + * Task called on timeout. + */ + GNUNET_SCHEDULER_TaskIdentifier timeout_task; + + /** + * At what number of bytes available in the + * write buffer should the notify method be called? + */ + size_t notify_size; + +}; + +/** + * @brief handle for a network socket + */ +struct GNUNET_NETWORK_SocketHandle +{ + + /** + * Scheduler that was used for the connect task. + */ + struct GNUNET_SCHEDULER_Handle *sched; + + /** + * Address information for connect (may be NULL). + */ + struct addrinfo *ai; + + /** + * Index for the next struct addrinfo for connect attempts (may be NULL) + */ + struct addrinfo *ai_pos; + + /** + * Network address of the other end-point, may be NULL. + */ + struct sockaddr *addr; + + /** + * Pointer to our write buffer. + */ + char *write_buffer; + + /** + * Size of our write buffer. + */ + size_t write_buffer_size; + + /** + * Current write-offset in write buffer (where + * would we write next). + */ + size_t write_buffer_off; + + /** + * Current read-offset in write buffer (how many + * bytes have already been send). + */ + size_t write_buffer_pos; + + /** + * Length of addr. + */ + socklen_t addrlen; + + /** + * Connect task that we may need to wait for. + */ + GNUNET_SCHEDULER_TaskIdentifier connect_task; + + /** + * Read task that we may need to wait for. + */ + GNUNET_SCHEDULER_TaskIdentifier read_task; + + /** + * Write task that we may need to wait for. + */ + GNUNET_SCHEDULER_TaskIdentifier write_task; + + /** + * The handle we return for GNUNET_NETWORK_notify_transmit_ready. + */ + struct GNUNET_NETWORK_TransmitHandle nth; + + /** + * Underlying OS's socket, set to -1 after fatal errors. + */ + int sock; + + /** + * Port to connect to. + */ + uint16_t port; + + /** + * Function to call on data received, NULL + * if no receive is pending. + */ + GNUNET_NETWORK_Receiver receiver; + + /** + * Closure for receiver. + */ + void *receiver_cls; + + /** + * Timeout for receiving (in absolute time). + */ + struct GNUNET_TIME_Absolute receive_timeout; + + /** + * Maximum number of bytes to read + * (for receiving). + */ + size_t max; + +}; + + +/** + * Create a socket handle by boxing an existing OS socket. The OS + * socket should henceforth be no longer used directly. + * GNUNET_socket_destroy will close it. + * + * @param sched scheduler to use + * @param osSocket existing socket to box + * @param maxbuf maximum write buffer size for the socket (use + * 0 for sockets that need no write buffers, such as listen sockets) + * @return the boxed socket handle + */ +struct GNUNET_NETWORK_SocketHandle * +GNUNET_NETWORK_socket_create_from_existing (struct GNUNET_SCHEDULER_Handle + *sched, int osSocket, + size_t maxbuf) +{ + struct GNUNET_NETWORK_SocketHandle *ret; + ret = GNUNET_malloc (sizeof (struct GNUNET_NETWORK_SocketHandle) + maxbuf); + ret->write_buffer = (char *) &ret[1]; + ret->write_buffer_size = maxbuf; + ret->sock = osSocket; + ret->sched = sched; + return ret; +} + + +/** + * Create a socket handle by accepting on a listen socket. This + * function may block if the listen socket has no connection ready. + * + * @param sched scheduler to use + * @param access function to use to check if access is allowed + * @param access_cls closure for access + * @param lsock listen socket + * @param maxbuf maximum write buffer size for the socket (use + * 0 for sockets that need no write buffers, such as listen sockets) + * @return the socket handle, NULL on error + */ +struct GNUNET_NETWORK_SocketHandle * +GNUNET_NETWORK_socket_create_from_accept (struct GNUNET_SCHEDULER_Handle + *sched, + GNUNET_NETWORK_AccessCheck access, + void *access_cls, int lsock, + size_t maxbuf) +{ + struct GNUNET_NETWORK_SocketHandle *ret; + char addr[32]; + char msg[INET6_ADDRSTRLEN]; + socklen_t addrlen; + int fam; + int fd; + int aret; + struct sockaddr_in *v4; + struct sockaddr_in6 *v6; + struct sockaddr *sa; + void *uaddr; + + addrlen = sizeof (addr); + fd = accept (lsock, (struct sockaddr *) &addr, &addrlen); + if (fd == -1) + { + GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING, "accept"); + return NULL; + } + if (0 != fcntl (fd, F_SETFD, fcntl (fd, F_GETFD) | FD_CLOEXEC)) + GNUNET_log_strerror (GNUNET_ERROR_TYPE_ERROR | GNUNET_ERROR_TYPE_BULK, + "fcntl"); + if (addrlen > sizeof (addr)) + { + GNUNET_break (0); + GNUNET_break (0 == CLOSE (fd)); + return NULL; + } + + sa = (struct sockaddr *) addr; + v6 = (struct sockaddr_in6 *) addr; + if ((sa->sa_family == AF_INET6) && (IN6_IS_ADDR_V4MAPPED (&v6->sin6_addr))) + { + /* convert to V4 address */ + v4 = GNUNET_malloc (sizeof (struct sockaddr_in)); + memset (v4, 0, sizeof (struct sockaddr_in)); + v4->sin_family = AF_INET; + memcpy (&v4->sin_addr, + &((char *) &v6->sin6_addr)[sizeof (struct in6_addr) - + sizeof (struct in_addr)], + sizeof (struct in_addr)); + v4->sin_port = v6->sin6_port; + uaddr = v4; + addrlen = sizeof (struct sockaddr_in); + } + else + { + uaddr = GNUNET_malloc (addrlen); + memcpy (uaddr, addr, addrlen); + } + + if ((access != NULL) && + (GNUNET_YES != (aret = access (access_cls, uaddr, addrlen)))) + { + if (aret == GNUNET_NO) + { + fam = ((struct sockaddr *) addr)->sa_family; + GNUNET_log (GNUNET_ERROR_TYPE_INFO, + _("Access denied to `%s'\n"), + inet_ntop (fam, + (fam == AF_INET6) + ? (const void *) &((struct sockaddr_in6 *) + &addr)-> + sin6_addr : (const void *) + &((struct sockaddr_in *) &addr)->sin_addr, + msg, sizeof (msg))); + } + GNUNET_break (0 == SHUTDOWN (fd, SHUT_RDWR)); + GNUNET_break (0 == CLOSE (fd)); + GNUNET_free (uaddr); + return NULL; + } + ret = GNUNET_malloc (sizeof (struct GNUNET_NETWORK_SocketHandle) + maxbuf); + ret->write_buffer = (char *) &ret[1]; + ret->write_buffer_size = maxbuf; + ret->addr = uaddr; + ret->addrlen = addrlen; + ret->sock = fd; + ret->sched = sched; + return ret; +} + +/** + * Obtain the network address of the other party. + * + * @param sock the client to get the address for + * @param addr where to store the address + * @param addrlen where to store the length of the address + * @return GNUNET_OK on success + */ +int +GNUNET_NETWORK_socket_get_address (struct GNUNET_NETWORK_SocketHandle *sock, + void **addr, size_t * addrlen) +{ + if ((sock->addr == NULL) || (sock->addrlen == 0)) + return GNUNET_NO; + *addr = GNUNET_malloc (sock->addrlen); + memcpy (*addr, sock->addr, sock->addrlen); + *addrlen = sock->addrlen; + return GNUNET_OK; +} + + +/** + * Set if a socket should use blocking or non-blocking IO. + * + * @return GNUNET_OK on success, GNUNET_SYSERR on error + */ +static int +socket_set_blocking (int handle, int doBlock) +{ +#if MINGW + u_long mode; + mode = !doBlock; +#if HAVE_PLIBC_FD + if (ioctlsocket (plibc_fd_get_handle (handle), FIONBIO, &mode) == + SOCKET_ERROR) + { + SetErrnoFromWinsockError (WSAGetLastError ()); + GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING, "ioctlsocket"); + return GNUNET_SYSERR; + } +#else + if (ioctlsocket (handle, FIONBIO, &mode) == SOCKET_ERROR) + { + SetErrnoFromWinsockError (WSAGetLastError ()); + GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING, "ioctlsocket"); + return GNUNET_SYSERR; + } +#endif + /* store the blocking mode */ +#if HAVE_PLIBC_FD + plibc_fd_set_blocking (handle, doBlock); +#else + __win_SetHandleBlockingMode (handle, doBlock); +#endif + return GNUNET_OK; + +#else + /* not MINGW */ + int flags = fcntl (handle, F_GETFL); + if (flags == -1) + { + GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING, "fcntl"); + return GNUNET_SYSERR; + } + if (doBlock) + flags &= ~O_NONBLOCK; + else + flags |= O_NONBLOCK; + if (0 != fcntl (handle, F_SETFL, flags)) + { + GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING, "fcntl"); + return GNUNET_SYSERR; + } + return GNUNET_OK; +#endif +} + + +/** + * Initiate asynchronous TCP connect request. + * + * @param sock what socket to connect + * @return GNUNET_SYSERR error (no more addresses to try) + */ +static int +try_connect (struct GNUNET_NETWORK_SocketHandle *sock) +{ + int s; + + if (sock->addr != NULL) + { + GNUNET_free (sock->addr); + sock->addr = NULL; + sock->addrlen = 0; + } + while (1) + { + if (sock->ai_pos == NULL) + { + /* no more addresses to try, fatal! */ + return GNUNET_SYSERR; + } + switch (sock->ai_pos->ai_family) + { + case AF_INET: + ((struct sockaddr_in *) sock->ai_pos->ai_addr)->sin_port = + htons (sock->port); + break; + case AF_INET6: + ((struct sockaddr_in6 *) sock->ai_pos->ai_addr)->sin6_port = + htons (sock->port); + break; + default: + sock->ai_pos = sock->ai_pos->ai_next; + continue; + } + s = SOCKET (sock->ai_pos->ai_family, SOCK_STREAM, 0); + if (s == -1) + { + /* maybe unsupported address family, try next */ + GNUNET_log_strerror (GNUNET_ERROR_TYPE_INFO, "socket"); + sock->ai_pos = sock->ai_pos->ai_next; + continue; + } + if (0 != fcntl (s, F_SETFD, fcntl (s, F_GETFD) | FD_CLOEXEC)) + GNUNET_log_strerror (GNUNET_ERROR_TYPE_ERROR | GNUNET_ERROR_TYPE_BULK, + "fcntl"); + if (GNUNET_SYSERR == socket_set_blocking (s, GNUNET_NO)) + { + /* we'll treat this one as fatal */ + GNUNET_break (0 == CLOSE (s)); + return GNUNET_SYSERR; + } + if ((0 != CONNECT (s, + sock->ai_pos->ai_addr, + sock->ai_pos->ai_addrlen)) && (errno != EINPROGRESS)) + { + /* maybe refused / unsupported address, try next */ + GNUNET_log_strerror (GNUNET_ERROR_TYPE_INFO, "connect"); + GNUNET_break (0 == CLOSE (s)); + continue; + } + break; + } + /* got one! copy address information! */ + sock->addrlen = sock->ai_pos->ai_addrlen; + sock->addr = GNUNET_malloc (sock->addrlen); + memcpy (sock->addr, sock->ai_pos->ai_addr, sock->addrlen); + sock->ai_pos = sock->ai_pos->ai_next; + sock->sock = s; + return GNUNET_OK; +} + + +/** + * Scheduler let us know that we're either ready to + * write on the socket OR connect timed out. Do the + * right thing. + */ +static void +connect_continuation (void *cls, + const struct GNUNET_SCHEDULER_TaskContext *tc) +{ + struct GNUNET_NETWORK_SocketHandle *sock = cls; + unsigned int len; + int error; + + /* nobody needs to wait for us anymore... */ + sock->connect_task = GNUNET_SCHEDULER_NO_PREREQUISITE_TASK; + /* Note: write-ready does NOT mean connect succeeded, + we need to use getsockopt to be sure */ + len = sizeof (error); + errno = 0; + error = 0; + if ((0 == (tc->reason & GNUNET_SCHEDULER_REASON_WRITE_READY)) || + (0 != getsockopt (sock->sock, SOL_SOCKET, SO_ERROR, &error, &len)) || + (error != 0) || (errno != 0)) + { + /* connect failed / timed out */ + GNUNET_break (0 == CLOSE (sock->sock)); + sock->sock = -1; + if (GNUNET_SYSERR == try_connect (sock)) + { + /* failed for good */ + GNUNET_break (sock->ai_pos == NULL); + freeaddrinfo (sock->ai); + sock->ai = NULL; + return; + } + sock->connect_task = GNUNET_SCHEDULER_add_write (tc->sched, GNUNET_NO, /* abort on shutdown */ + GNUNET_SCHEDULER_PRIORITY_KEEP, + GNUNET_SCHEDULER_NO_PREREQUISITE_TASK, + GNUNET_NETWORK_CONNECT_RETRY_TIMEOUT, + sock->sock, + &connect_continuation, + sock); + return; + } + /* connect succeeded! clean up "ai" */ +#if DEBUG_NETWORK + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Connection succeeded!\n"); +#endif + freeaddrinfo (sock->ai); + sock->ai_pos = NULL; + sock->ai = NULL; +} + + +/** + * Create a socket handle by (asynchronously) connecting to a host. + * This function returns immediately, even if the connection has not + * yet been established. This function only creates TCP connections. + * + * @param sched scheduler to use + * @param hostname name of the host to connect to + * @param port port to connect to + * @param maxbuf maximum write buffer size for the socket (use + * 0 for sockets that need no write buffers, such as listen sockets) + * @return the socket handle + */ +struct GNUNET_NETWORK_SocketHandle * +GNUNET_NETWORK_socket_create_from_connect (struct GNUNET_SCHEDULER_Handle + *sched, const char *hostname, + uint16_t port, size_t maxbuf) +{ + struct GNUNET_NETWORK_SocketHandle *ret; + struct addrinfo hints; + int ec; + + ret = GNUNET_malloc (sizeof (struct GNUNET_NETWORK_SocketHandle) + maxbuf); + ret->sock = -1; + ret->sched = sched; + ret->write_buffer = (char *) &ret[1]; + ret->write_buffer_size = maxbuf; + ret->port = port; + memset (&hints, 0, sizeof (hints)); + hints.ai_family = AF_UNSPEC; + hints.ai_socktype = SOCK_STREAM; + if (0 != (ec = getaddrinfo (hostname, NULL, &hints, &ret->ai))) + { + GNUNET_log (GNUNET_ERROR_TYPE_INFO | GNUNET_ERROR_TYPE_BULK, + "`%s' failed for hostname `%s': %s\n", + "getaddrinfo", hostname, gai_strerror (ec)); + GNUNET_free (ret); + return NULL; + } + ret->ai_pos = ret->ai; + if (GNUNET_SYSERR == try_connect (ret)) + { + freeaddrinfo (ret->ai); + GNUNET_free (ret); + return NULL; + } + ret->connect_task = GNUNET_SCHEDULER_add_write (sched, GNUNET_NO, /* abort on shutdown */ + GNUNET_SCHEDULER_PRIORITY_KEEP, + GNUNET_SCHEDULER_NO_PREREQUISITE_TASK, + GNUNET_NETWORK_CONNECT_RETRY_TIMEOUT, + ret->sock, + &connect_continuation, ret); + return ret; + +} + + +/** + * Create a socket handle by (asynchronously) connecting to a host. + * This function returns immediately, even if the connection has not + * yet been established. This function only creates TCP connections. + * + * @param sched scheduler to use + * @param af_family address family to use + * @param serv_addr server address + * @param addrlen length of server address + * @param maxbuf maximum write buffer size for the socket (use + * 0 for sockets that need no write buffers, such as listen sockets) + * @return the socket handle + */ +struct GNUNET_NETWORK_SocketHandle * +GNUNET_NETWORK_socket_create_from_sockaddr (struct GNUNET_SCHEDULER_Handle + *sched, int af_family, + const struct sockaddr *serv_addr, + socklen_t addrlen, size_t maxbuf) +{ + int s; + + s = SOCKET (af_family, SOCK_STREAM, 0); + if (s == -1) + { + GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING | + GNUNET_ERROR_TYPE_BULK, "socket"); + return NULL; + } + if (0 != fcntl (s, F_SETFD, fcntl (s, F_GETFD) | FD_CLOEXEC)) + GNUNET_log_strerror (GNUNET_ERROR_TYPE_ERROR | GNUNET_ERROR_TYPE_BULK, + "fcntl"); + if (GNUNET_SYSERR == socket_set_blocking (s, GNUNET_NO)) + { + /* we'll treat this one as fatal */ + GNUNET_break (0 == CLOSE (s)); + return NULL; + } + if ((0 != CONNECT (s, serv_addr, addrlen)) && (errno != EINPROGRESS)) + { + /* maybe refused / unsupported address, try next */ + GNUNET_log_strerror (GNUNET_ERROR_TYPE_INFO, "connect"); + GNUNET_break (0 == CLOSE (s)); + return NULL; + } + return GNUNET_NETWORK_socket_create_from_existing (sched, s, maxbuf); +} + + +/** + * Check if socket is valid (no fatal errors have happened so far). + * Note that a socket that is still trying to connect is considered + * valid. + * + * @param sock socket to check + * @return GNUNET_YES if valid, GNUNET_NO otherwise + */ +int +GNUNET_NETWORK_socket_check (struct GNUNET_NETWORK_SocketHandle *sock) +{ + if (sock->ai != NULL) + return GNUNET_YES; /* still trying to connect */ + return (sock->sock == -1) ? GNUNET_NO : GNUNET_YES; +} + + +/** + * Scheduler let us know that the connect task is finished (or was + * cancelled due to shutdown). Now really clean up. + */ +static void +destroy_continuation (void *cls, + const struct GNUNET_SCHEDULER_TaskContext *tc) +{ + struct GNUNET_NETWORK_SocketHandle *sock = cls; + GNUNET_NETWORK_TransmitReadyNotify notify; + + if (sock->write_task != GNUNET_SCHEDULER_NO_PREREQUISITE_TASK) + { +#if DEBUG_NETWORK + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Destroy code waiting for writes to complete.\n"); +#endif + GNUNET_SCHEDULER_add_after (sock->sched, + GNUNET_YES, + GNUNET_SCHEDULER_PRIORITY_KEEP, + sock->write_task, + &destroy_continuation, sock); + return; + } + if (sock->sock != -1) + { +#if DEBUG_NETWORK + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Shutting down socket.\n"); +#endif + SHUTDOWN (sock->sock, SHUT_RDWR); + } + if (sock->read_task != GNUNET_SCHEDULER_NO_PREREQUISITE_TASK) + { +#if DEBUG_NETWORK + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Destroy code waiting for receive to complete.\n"); +#endif + GNUNET_SCHEDULER_add_after (sock->sched, + GNUNET_YES, + GNUNET_SCHEDULER_PRIORITY_KEEP, + sock->read_task, + &destroy_continuation, sock); + return; + } + if (NULL != (notify = sock->nth.notify_ready)) + { + sock->nth.notify_ready = NULL; + notify (sock->nth.notify_ready_cls, 0, NULL); + if (sock->nth.timeout_task != GNUNET_SCHEDULER_NO_PREREQUISITE_TASK) + { + GNUNET_SCHEDULER_cancel (sock->sched, sock->nth.timeout_task); + sock->nth.timeout_task = GNUNET_SCHEDULER_NO_PREREQUISITE_TASK; + } + } + if (sock->sock != -1) + { +#if DEBUG_NETWORK + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Closing socket.\n"); +#endif + GNUNET_break (0 == CLOSE (sock->sock)); + } + GNUNET_free_non_null (sock->addr); + if (sock->ai != NULL) + freeaddrinfo (sock->ai); + GNUNET_free (sock); +} + + +/** + * Close the socket and free associated resources. Pending + * transmissions are simply dropped. A pending receive call will be + * called with an error code of "EPIPE". + * + * @param sock socket to destroy + */ +void +GNUNET_NETWORK_socket_destroy (struct GNUNET_NETWORK_SocketHandle *sock) +{ +#if DEBUG_NETWORK + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Network asked to destroy socket %p\n", sock); +#endif + if (sock->write_buffer_off == 0) + sock->ai_pos = NULL; /* if we're still trying to connect and have + no message pending, stop trying! */ + GNUNET_assert (sock->sched != NULL); + GNUNET_SCHEDULER_add_after (sock->sched, + GNUNET_YES, + GNUNET_SCHEDULER_PRIORITY_KEEP, + sock->connect_task, + &destroy_continuation, sock); +} + +/** + * Tell the receiver callback that a timeout was reached. + */ +static void +signal_timeout (struct GNUNET_NETWORK_SocketHandle *sh) +{ + GNUNET_NETWORK_Receiver receiver; + +#if DEBUG_NETWORK + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Network signals timeout to receiver!\n"); +#endif + GNUNET_assert (NULL != (receiver = sh->receiver)); + sh->receiver = NULL; + receiver (sh->receiver_cls, NULL, 0, NULL, 0, 0); +} + + +/** + * Tell the receiver callback that we had an IO error. + */ +static void +signal_error (struct GNUNET_NETWORK_SocketHandle *sh, int errcode) +{ + GNUNET_NETWORK_Receiver receiver; + GNUNET_assert (NULL != (receiver = sh->receiver)); + sh->receiver = NULL; + receiver (sh->receiver_cls, NULL, 0, sh->addr, sh->addrlen, errcode); +} + + +/** + * This function is called once we either timeout + * or have data ready to read. + */ +static void +receive_ready (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc) +{ + struct GNUNET_NETWORK_SocketHandle *sh = cls; + struct GNUNET_TIME_Absolute now; + char buffer[sh->max]; + ssize_t ret; + GNUNET_NETWORK_Receiver receiver; + + sh->read_task = GNUNET_SCHEDULER_NO_PREREQUISITE_TASK; + now = GNUNET_TIME_absolute_get (); + if ((now.value > sh->receive_timeout.value) || + (0 != (tc->reason & GNUNET_SCHEDULER_REASON_TIMEOUT)) || + (0 != (tc->reason & GNUNET_SCHEDULER_REASON_SHUTDOWN))) + { +#if DEBUG_NETWORK + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Receive encounters error: timeout...\n"); +#endif + signal_timeout (sh); + return; + } + if (sh->sock == -1) + { + /* connect failed for good */ +#if DEBUG_NETWORK + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Receive encounters error, socket closed...\n"); +#endif + signal_error (sh, ECONNREFUSED); + return; + } + GNUNET_assert (FD_ISSET (sh->sock, tc->read_ready)); +RETRY: + ret = RECV (sh->sock, buffer, sh->max, MSG_DONTWAIT); + if (ret == -1) + { + if (errno == EINTR) + goto RETRY; +#if DEBUG_NETWORK + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Error receiving: %s\n", STRERROR (errno)); +#endif + signal_error (sh, errno); + return; + } +#if DEBUG_NETWORK + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Receive got %d bytes from OS!\n", ret); +#endif + GNUNET_assert (NULL != (receiver = sh->receiver)); + sh->receiver = NULL; + receiver (sh->receiver_cls, buffer, ret, sh->addr, sh->addrlen, 0); +} + + +/** + * This function is called after establishing a connection either has + * succeeded or timed out. Note that it is possible that the attempt + * timed out and that we're immediately retrying. If we are retrying, + * we need to wait again (or timeout); if we succeeded, we need to + * wait for data (or timeout). + */ +static void +receive_again (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc) +{ + struct GNUNET_NETWORK_SocketHandle *sh = cls; + struct GNUNET_TIME_Absolute now; + + sh->read_task = GNUNET_SCHEDULER_NO_PREREQUISITE_TASK; + if ((sh->sock == -1) && + (sh->connect_task == GNUNET_SCHEDULER_NO_PREREQUISITE_TASK)) + { + /* not connected and no longer trying */ +#if DEBUG_NETWORK + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Receive encounters error, socket closed...\n"); +#endif + signal_error (sh, ECONNREFUSED); + return; + } + now = GNUNET_TIME_absolute_get (); + if ((now.value > sh->receive_timeout.value) || + (0 != (tc->reason & GNUNET_SCHEDULER_REASON_SHUTDOWN))) + { +#if DEBUG_NETWORK + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Receive encounters error: timeout...\n"); +#endif + signal_timeout (sh); + return; + } + if (sh->connect_task != GNUNET_SCHEDULER_NO_PREREQUISITE_TASK) + { + /* connect was retried */ +#if DEBUG_NETWORK + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Receive still waits on connect...\n"); +#endif + sh->read_task = GNUNET_SCHEDULER_add_after (tc->sched, + GNUNET_YES, + GNUNET_SCHEDULER_PRIORITY_KEEP, + sh->connect_task, + &receive_again, sh); + } + else + { + /* connect succeeded, wait for data! */ +#if DEBUG_NETWORK + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Receive now waits for socket...\n"); +#endif + sh->read_task = GNUNET_SCHEDULER_add_read (tc->sched, + GNUNET_YES, + GNUNET_SCHEDULER_PRIORITY_KEEP, + sh->connect_task, + GNUNET_TIME_absolute_get_remaining + (sh->receive_timeout), + sh->sock, &receive_ready, + sh); + } +} + + +/** + * Receive data from the given socket. Note that this function will + * call "receiver" asynchronously using the scheduler. It will + * "immediately" return. Note that there MUST only be one active + * receive call per socket at any given point in time (so do not + * call receive again until the receiver callback has been invoked). + * + * @param sched scheduler to use + * @param sock socket handle + * @param max maximum number of bytes to read + * @param timeout maximum amount of time to wait (use -1 for "forever") + * @param receiver function to call with received data + * @param receiver_cls closure for receiver + * @return scheduler task ID used for receiving, GNUNET_SCHEDULER_NO_PREREQUISITE_TASK on error + */ +GNUNET_SCHEDULER_TaskIdentifier +GNUNET_NETWORK_receive (struct GNUNET_NETWORK_SocketHandle *sock, + size_t max, + struct GNUNET_TIME_Relative timeout, + GNUNET_NETWORK_Receiver receiver, void *receiver_cls) +{ + struct GNUNET_SCHEDULER_TaskContext tc; +#if DEBUG_NETWORK + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Network asked to receive from socket...\n"); +#endif + GNUNET_assert ((sock->read_task == GNUNET_SCHEDULER_NO_PREREQUISITE_TASK) && + (sock->receiver == NULL)); + sock->receiver = receiver; + sock->receiver_cls = receiver_cls; + sock->receive_timeout = GNUNET_TIME_relative_to_absolute (timeout); + sock->max = max; + memset (&tc, 0, sizeof (tc)); + tc.sched = sock->sched; + tc.reason = GNUNET_SCHEDULER_REASON_PREREQ_DONE; + receive_again (sock, &tc); + return sock->read_task; +} + + +/** + * Cancel receive job on the given socket. Note that the + * receiver callback must not have been called yet in order + * for the cancellation to be valid. + * + * @param sock socket handle + * @param task task identifier returned from the receive call + * @return closure of the original receiver callback + */ +void * +GNUNET_NETWORK_receive_cancel (struct GNUNET_NETWORK_SocketHandle *sock, + GNUNET_SCHEDULER_TaskIdentifier task) +{ + GNUNET_assert (sock->read_task == task); + GNUNET_assert (sock == GNUNET_SCHEDULER_cancel (sock->sched, task)); + sock->read_task = GNUNET_SCHEDULER_NO_PREREQUISITE_TASK; + sock->receiver = NULL; + return sock->receiver_cls; +} + + +/** + * Try to call the transmit notify method (check if we do + * have enough space available first)! + * + * @param sock socket for which we should do this processing + * @return GNUNET_YES if we were able to call notify + */ +static int +process_notify (struct GNUNET_NETWORK_SocketHandle *sock) +{ + size_t used; + size_t avail; + size_t size; + GNUNET_NETWORK_TransmitReadyNotify notify; + + GNUNET_assert (sock->write_task == GNUNET_SCHEDULER_NO_PREREQUISITE_TASK); + if (NULL == (notify = sock->nth.notify_ready)) + return GNUNET_NO; + used = sock->write_buffer_off - sock->write_buffer_pos; + avail = sock->write_buffer_size - used; + size = sock->nth.notify_size; + if (sock->nth.notify_size > avail) + return GNUNET_NO; + sock->nth.notify_ready = NULL; + if (sock->nth.timeout_task != GNUNET_SCHEDULER_NO_PREREQUISITE_TASK) + { + GNUNET_SCHEDULER_cancel (sock->sched, sock->nth.timeout_task); + sock->nth.timeout_task = GNUNET_SCHEDULER_NO_PREREQUISITE_TASK; + } + if (sock->write_buffer_size - sock->write_buffer_off < size) + { + /* need to compact */ + memmove (sock->write_buffer, + &sock->write_buffer[sock->write_buffer_pos], used); + sock->write_buffer_off -= sock->write_buffer_pos; + sock->write_buffer_pos = 0; + } + GNUNET_assert (sock->write_buffer_size - sock->write_buffer_off >= size); + size = notify (sock->nth.notify_ready_cls, + sock->write_buffer_size - sock->write_buffer_off, + &sock->write_buffer[sock->write_buffer_off]); + sock->write_buffer_off += size; + return GNUNET_YES; +} + + +/** + * Task invoked by the scheduler when a call to transmit + * is timing out (we never got enough buffer space to call + * the callback function before the specified timeout + * expired). + * + * This task notifies the client about the timeout. + */ +static void +transmit_timeout (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc) +{ + struct GNUNET_NETWORK_SocketHandle *sock = cls; + GNUNET_NETWORK_TransmitReadyNotify notify; + +#if DEBUG_NETWORK + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Transmit fails, time out reached.\n"); +#endif + notify = sock->nth.notify_ready; + sock->nth.notify_ready = NULL; + notify (sock->nth.notify_ready_cls, 0, NULL); +} + + +static void +transmit_error (struct GNUNET_NETWORK_SocketHandle *sock) +{ + if (sock->nth.notify_ready == NULL) + return; /* nobody to tell about it */ + if (sock->nth.timeout_task != GNUNET_SCHEDULER_NO_PREREQUISITE_TASK) + { + GNUNET_SCHEDULER_cancel (sock->sched, sock->nth.timeout_task); + sock->nth.timeout_task = GNUNET_SCHEDULER_NO_PREREQUISITE_TASK; + } + transmit_timeout (sock, NULL); +} + + +/** + * See if we are now connected. If not, wait longer for + * connect to succeed. If connected, we should be able + * to write now as well, unless we timed out. + */ +static void +transmit_ready (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc) +{ + struct GNUNET_NETWORK_SocketHandle *sock = cls; + ssize_t ret; + size_t have; + +#if DEBUG_NETWORK + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Transmit ready called --- will try to send\n"); +#endif + GNUNET_assert (sock->write_task != GNUNET_SCHEDULER_NO_PREREQUISITE_TASK); + sock->write_task = GNUNET_SCHEDULER_NO_PREREQUISITE_TASK; + if (sock->connect_task != GNUNET_SCHEDULER_NO_PREREQUISITE_TASK) + { + /* still waiting for connect */ +#if DEBUG_NETWORK + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Transmission still waiting for connect...\n"); +#endif + GNUNET_assert (sock->write_task == + GNUNET_SCHEDULER_NO_PREREQUISITE_TASK); + sock->write_task = + GNUNET_SCHEDULER_add_delayed (tc->sched, GNUNET_NO, + GNUNET_SCHEDULER_PRIORITY_KEEP, + sock->connect_task, + GNUNET_TIME_UNIT_ZERO, &transmit_ready, + sock); + return; + } + if (sock->sock == -1) + { + GNUNET_log (GNUNET_ERROR_TYPE_INFO, + _ + ("Did not transmit request, socket closed or connect failed.\n")); + transmit_error (sock); + return; /* connect failed for good, we're finished */ + } + if ((tc->write_ready == NULL) || (!FD_ISSET (sock->sock, tc->write_ready))) + { +#if DEBUG_NETWORK + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Socket not yet ready for writing, will wait for that.\n"); +#endif + goto SCHEDULE_WRITE; + } + GNUNET_assert (sock->write_buffer_off >= sock->write_buffer_pos); + process_notify (sock); + have = sock->write_buffer_off - sock->write_buffer_pos; + if (have == 0) + { +#if DEBUG_NETWORK + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "No data ready for writing.\n"); +#endif + return; + } +RETRY: + ret = SEND (sock->sock, + &sock->write_buffer[sock->write_buffer_pos], + have, MSG_DONTWAIT | MSG_NOSIGNAL); + if (ret == -1) + { + if (errno == EINTR) + goto RETRY; +#if DEBUG_NETWORK + GNUNET_log_strerror (GNUNET_ERROR_TYPE_DEBUG, "send"); +#endif + SHUTDOWN (sock->sock, SHUT_RDWR); + GNUNET_break (0 == CLOSE (sock->sock)); + sock->sock = -1; + transmit_error (sock); + return; + } +#if DEBUG_NETWORK + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Transmitted %d bytes to OS\n", ret); +#endif + sock->write_buffer_pos += ret; + if (sock->write_buffer_pos == sock->write_buffer_off) + { + /* transmitted all pending data */ + sock->write_buffer_pos = 0; + sock->write_buffer_off = 0; +#if DEBUG_NETWORK + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Transmission buffer now empty.\n", ret); +#endif + } + if ((sock->write_buffer_off == 0) && (NULL == sock->nth.notify_ready)) + return; /* all data sent! */ + /* not done writing, schedule more */ +#if DEBUG_NETWORK + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "More data ready for transmission, scheduling task again!\n"); +#endif +SCHEDULE_WRITE: + if (sock->write_task == GNUNET_SCHEDULER_NO_PREREQUISITE_TASK) + sock->write_task = + GNUNET_SCHEDULER_add_write (tc->sched, + GNUNET_NO, + GNUNET_SCHEDULER_PRIORITY_KEEP, + GNUNET_SCHEDULER_NO_PREREQUISITE_TASK, + GNUNET_TIME_UNIT_FOREVER_REL, + sock->sock, &transmit_ready, sock); +} + + +/** + * Ask the socket to call us once the specified number of bytes + * are free in the transmission buffer. May call the notify + * method immediately if enough space is available. + * + * @param sock socket + * @param size number of bytes to send + * @param timeout after how long should we give up (and call + * notify with buf NULL and size 0)? + * @param notify function to call + * @param notify_cls closure for notify + * @return non-NULL if the notify callback was queued, + * NULL if we are already going to notify someone else (busy) + */ +struct GNUNET_NETWORK_TransmitHandle * +GNUNET_NETWORK_notify_transmit_ready (struct GNUNET_NETWORK_SocketHandle + *sock, size_t size, + struct GNUNET_TIME_Relative timeout, + GNUNET_NETWORK_TransmitReadyNotify + notify, void *notify_cls) +{ + if (sock->nth.notify_ready != NULL) + return NULL; + GNUNET_assert (notify != NULL); + GNUNET_assert (sock->write_buffer_size >= size); + + if ((sock->sock == -1) && + (sock->connect_task == GNUNET_SCHEDULER_NO_PREREQUISITE_TASK)) + { +#if DEBUG_NETWORK + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Transmit fails, connection failed.\n"); +#endif + notify (notify_cls, 0, NULL); + return &sock->nth; + } + GNUNET_assert (sock->write_buffer_off <= sock->write_buffer_size); + GNUNET_assert (sock->write_buffer_pos <= sock->write_buffer_size); + GNUNET_assert (sock->write_buffer_pos <= sock->write_buffer_off); + sock->nth.notify_ready = notify; + sock->nth.notify_ready_cls = notify_cls; + sock->nth.sh = sock; + sock->nth.notify_size = size; + sock->nth.transmit_timeout = GNUNET_TIME_relative_to_absolute (timeout); + sock->nth.timeout_task = GNUNET_SCHEDULER_add_delayed (sock->sched, + GNUNET_NO, + GNUNET_SCHEDULER_PRIORITY_KEEP, + GNUNET_SCHEDULER_NO_PREREQUISITE_TASK, + timeout, + &transmit_timeout, + sock); +#if DEBUG_NETWORK + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Scheduling asynchronous transmission once connect is done...\n"); +#endif + if (sock->write_task == GNUNET_SCHEDULER_NO_PREREQUISITE_TASK) + sock->write_task = GNUNET_SCHEDULER_add_delayed (sock->sched, + GNUNET_NO, + GNUNET_SCHEDULER_PRIORITY_KEEP, + sock->connect_task, + GNUNET_TIME_UNIT_ZERO, + &transmit_ready, sock); + return &sock->nth; +} + + +/** + * Cancel the specified transmission-ready + * notification. + */ +void +GNUNET_NETWORK_notify_transmit_ready_cancel (struct + GNUNET_NETWORK_TransmitHandle *h) +{ + GNUNET_assert (h->notify_ready != NULL); + GNUNET_SCHEDULER_cancel (h->sh->sched, h->timeout_task); + h->timeout_task = GNUNET_SCHEDULER_NO_PREREQUISITE_TASK; + h->notify_ready = NULL; +} + + +#if 0 /* keep Emacsens' auto-indent happy */ +{ +#endif +#ifdef __cplusplus +} +#endif diff --git a/src/util/os_installation.c b/src/util/os_installation.c new file mode 100644 index 000000000..201095544 --- /dev/null +++ b/src/util/os_installation.c @@ -0,0 +1,443 @@ +/* + This file is part of GNUnet. + (C) 2006 Christian Grothoff (and other contributing authors) + + GNUnet is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published + by the Free Software Foundation; either version 2, 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 + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with GNUnet; see the file COPYING. If not, write to the + Free Software Foundation, Inc., 59 Temple Place - Suite 330, + Boston, MA 02111-1307, USA. +*/ + +/** + * @file src/util/os_installation.c + * @brief get paths used by the program + * @author Milan + */ + +#ifdef __cplusplus +extern "C" +{ +#if 0 /* keep Emacsens' auto-indent happy */ +} +#endif +#endif + +#include +#include +#include +#include + +#include "platform.h" +#include "gnunet_common.h" +#include "gnunet_configuration_lib.h" +#include "gnunet_disk_lib.h" +#include "gnunet_os_lib.h" +#if OSX +#include +#include +#endif + +#if LINUX +/** + * Try to determine path by reading /proc/PID/exe + */ +static char * +get_path_from_proc_maps () +{ + char fn[64]; + char *line; + char *dir; + FILE *f; + + GNUNET_snprintf (fn, 64, "/proc/%u/maps", getpid ()); + line = GNUNET_malloc (1024); + dir = GNUNET_malloc (1024); + f = fopen (fn, "r"); + if (f != NULL) + { + while (NULL != fgets (line, 1024, f)) + { + if ((1 == sscanf (line, + "%*x-%*x %*c%*c%*c%*c %*x %*2u:%*2u %*u%*[ ]%s", + dir)) && (NULL != strstr (dir, "libgnunetutil"))) + { + strstr (dir, "libgnunetutil")[0] = '\0'; + fclose (f); + GNUNET_free (line); + return dir; + } + } + fclose (f); + } + GNUNET_free (dir); + GNUNET_free (line); + return NULL; +} + +/** + * Try to determine path by reading /proc/PID/exe + */ +static char * +get_path_from_proc_exe () +{ + char fn[64]; + char *lnk; + size_t size; + + GNUNET_snprintf (fn, 64, "/proc/%u/exe", getpid ()); + lnk = GNUNET_malloc (1024); + size = readlink (fn, lnk, 1023); + if ((size == 0) || (size >= 1024)) + { + GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_ERROR, "readlink", fn); + GNUNET_free (lnk); + return NULL; + } + lnk[size] = '\0'; + while ((lnk[size] != '/') && (size > 0)) + size--; + if ((size < 4) || (lnk[size - 4] != '/')) + { + /* not installed in "/bin/" -- binary path probably useless */ + GNUNET_free (lnk); + return NULL; + } + lnk[size] = '\0'; + return lnk; +} +#endif + +#if WINDOWS +/** + * Try to determine path with win32-specific function + */ +static char * +get_path_from_module_filename () +{ + char *path; + char *idx; + + path = GNUNET_malloc (4097); + GetModuleFileName (NULL, path, 4096); + idx = path + strlen (path); + while ((idx > path) && (*idx != '\\') && (*idx != '/')) + idx--; + *idx = '\0'; + return path; +} +#endif + +#if OSX +typedef int (*MyNSGetExecutablePathProto) (char *buf, size_t * bufsize); + +static char * +get_path_from_NSGetExecutablePath () +{ + static char zero = '\0'; + char *path; + size_t len; + MyNSGetExecutablePathProto func; + int ret; + + path = NULL; + func = + (MyNSGetExecutablePathProto) dlsym (RTLD_DEFAULT, "_NSGetExecutablePath"); + if (!func) + return NULL; + path = &zero; + len = 0; + /* get the path len, including the trailing \0 */ + func (path, &len); + if (len == 0) + return NULL; + path = GNUNET_malloc (len); + ret = func (path, &len); + if (ret != 0) + { + GNUNET_free (path); + return NULL; + } + len = strlen (path); + while ((path[len] != '/') && (len > 0)) + len--; + path[len] = '\0'; + return path; +} + +static char * +get_path_from_dyld_image () +{ + const char *path; + char *p, *s; + int i; + int c; + + p = NULL; + c = _dyld_image_count (); + for (i = 0; i < c; i++) + { + if (_dyld_get_image_header (i) == &_mh_dylib_header) + { + path = _dyld_get_image_name (i); + if (path != NULL && strlen (path) > 0) + { + p = strdup (path); + s = p + strlen (p); + while ((s > p) && (*s != '/')) + s--; + s++; + *s = '\0'; + } + break; + } + } + return p; +} +#endif + +static char * +get_path_from_PATH () +{ + char *path; + char *pos; + char *end; + char *buf; + const char *p; + size_t size; + + p = getenv ("PATH"); + if (p == NULL) + return NULL; + path = GNUNET_strdup (p); /* because we write on it */ + buf = GNUNET_malloc (strlen (path) + 20); + size = strlen (path); + pos = path; + + while (NULL != (end = strchr (pos, ':'))) + { + *end = '\0'; + sprintf (buf, "%s/%s", pos, "gnunetd"); + if (GNUNET_DISK_file_test (buf) == GNUNET_YES) + { + pos = GNUNET_strdup (pos); + GNUNET_free (buf); + GNUNET_free (path); + return pos; + } + pos = end + 1; + } + sprintf (buf, "%s/%s", pos, "gnunetd"); + if (GNUNET_DISK_file_test (buf) == GNUNET_YES) + { + pos = GNUNET_strdup (pos); + GNUNET_free (buf); + GNUNET_free (path); + return pos; + } + GNUNET_free (buf); + GNUNET_free (path); + return NULL; +} + +static char * +get_path_from_GNUNET_PREFIX () +{ + const char *p; + + p = getenv ("GNUNET_PREFIX"); + if (p != NULL) + return GNUNET_strdup (p); + return NULL; +} + +/* + * @brief get the path to GNUnet bin/ or lib/, prefering the lib/ path + * @author Milan + * + * @return a pointer to the executable path, or NULL on error + */ +static char * +os_get_gnunet_path () +{ + char *ret; + + ret = get_path_from_GNUNET_PREFIX (); + if (ret != NULL) + return ret; +#if LINUX + ret = get_path_from_proc_maps (); + if (ret != NULL) + return ret; + ret = get_path_from_proc_exe (); + if (ret != NULL) + return ret; +#endif +#if WINDOWS + ret = get_path_from_module_filename (); + if (ret != NULL) + return ret; +#endif +#if OSX + ret = get_path_from_dyld_image (); + if (ret != NULL) + return ret; + ret = get_path_from_NSGetExecutablePath (); + if (ret != NULL) + return ret; +#endif + ret = get_path_from_PATH (); + if (ret != NULL) + return ret; + /* other attempts here */ + return NULL; +} + +/* + * @brief get the path to current app's bin/ + * @author Milan + * + * @return a pointer to the executable path, or NULL on error + */ +static char * +os_get_exec_path () +{ + char *ret; + +#if LINUX + ret = get_path_from_proc_exe (); + if (ret != NULL) + return ret; +#endif +#if WINDOWS + ret = get_path_from_module_filename (); + if (ret != NULL) + return ret; +#endif +#if OSX + ret = get_path_from_NSGetExecutablePath (); + if (ret != NULL) + return ret; +#endif + /* other attempts here */ + return NULL; +} + + + +/** + * @brief get the path to a specific GNUnet installation directory or, + * with GNUNET_IPK_SELF_PREFIX, the current running apps installation directory + * @author Milan + * @return a pointer to the dir path (to be freed by the caller) + */ +char * +GNUNET_OS_installation_get_path (enum GNUNET_OS_InstallationPathKind dirkind) +{ + size_t n; + const char *dirname; + char *execpath = NULL; + char *tmp; + int isbasedir; + + /* if wanted, try to get the current app's bin/ */ + if (dirkind == GNUNET_OS_IPK_SELF_PREFIX) + execpath = os_get_exec_path (); + + /* try to get GNUnet's bin/ or lib/, or if previous was unsuccessful some + * guess for the current app */ + if (execpath == NULL) + execpath = os_get_gnunet_path (); + + if (execpath == NULL) + return NULL; + + n = strlen (execpath); + if (n == 0) + { + /* should never happen, but better safe than sorry */ + GNUNET_free (execpath); + return NULL; + } + /* remove filename itself */ + while ((n > 1) && (execpath[n - 1] == DIR_SEPARATOR)) + execpath[--n] = '\0'; + + isbasedir = 1; + if ((n > 5) && + ((0 == strcasecmp (&execpath[n - 5], "lib32")) || + (0 == strcasecmp (&execpath[n - 5], "lib64")))) + { + if (dirkind != GNUNET_OS_IPK_LIBDIR) + { + /* strip '/lib32' or '/lib64' */ + execpath[n - 5] = '\0'; + n -= 5; + } + else + isbasedir = 0; + } + else if ((n > 3) && + ((0 == strcasecmp (&execpath[n - 3], "bin")) || + (0 == strcasecmp (&execpath[n - 3], "lib")))) + { + /* strip '/bin' or '/lib' */ + execpath[n - 3] = '\0'; + n -= 3; + } + /* in case this was a directory named foo-bin, remove "foo-" */ + while ((n > 1) && (execpath[n - 1] == DIR_SEPARATOR)) + execpath[--n] = '\0'; + switch (dirkind) + { + case GNUNET_OS_IPK_PREFIX: + case GNUNET_OS_IPK_SELF_PREFIX: + dirname = DIR_SEPARATOR_STR; + break; + case GNUNET_OS_IPK_BINDIR: + dirname = DIR_SEPARATOR_STR "bin" DIR_SEPARATOR_STR; + break; + case GNUNET_OS_IPK_LIBDIR: + if (isbasedir) + dirname = + DIR_SEPARATOR_STR "lib" DIR_SEPARATOR_STR "gnunet" + DIR_SEPARATOR_STR; + else + dirname = DIR_SEPARATOR_STR "gnunet" DIR_SEPARATOR_STR; + break; + case GNUNET_OS_IPK_DATADIR: + dirname = + DIR_SEPARATOR_STR "share" DIR_SEPARATOR_STR "gnunet" + DIR_SEPARATOR_STR; + break; + case GNUNET_OS_IPK_LOCALEDIR: + dirname = + DIR_SEPARATOR_STR "share" DIR_SEPARATOR_STR "locale" + DIR_SEPARATOR_STR; + break; + default: + GNUNET_free (execpath); + return NULL; + } + tmp = GNUNET_malloc (strlen (execpath) + strlen (dirname) + 1); + sprintf (tmp, "%s%s", execpath, dirname); + GNUNET_free (execpath); + return tmp; +} + +#if 0 /* keep Emacsens' auto-indent happy */ +{ +#endif +#ifdef __cplusplus +} +#endif +/* end of installpath.c */ diff --git a/src/util/os_load.c b/src/util/os_load.c new file mode 100644 index 000000000..d1115e33f --- /dev/null +++ b/src/util/os_load.c @@ -0,0 +1,653 @@ +/* + This file is part of GNUnet. + (C) 2001, 2002, 2003, 2005, 2006 Christian Grothoff (and other contributing authors) + + GNUnet is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published + by the Free Software Foundation; either version 2, 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 + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with GNUnet; see the file COPYING. If not, write to the + Free Software Foundation, Inc., 59 Temple Place - Suite 330, + Boston, MA 02111-1307, USA. +*/ + +/** + * @file util/os_load_cpu.c + * @brief calls to determine current CPU load + * @author Tzvetan Horozov + * @author Christian Grothoff + * @author Igor Wronsky + * @author Alex Harper (OS X portion) + */ + +#include "platform.h" +#include "gnunet_common.h" +#include "gnunet_os_lib.h" +#include "gnunet_strings_lib.h" + +#if SOLARIS +#if HAVE_KSTAT_H +#include +#endif +#if HAVE_SYS_SYSINFO_H +#include +#endif +#if HAVE_KVM_H +#include +#endif +#endif +#if SOMEBSD +#if HAVE_KVM_H +#include +#endif +#endif + +#ifdef OSX +#include + +static processor_cpu_load_info_t prev_cpu_load; +#endif + +#define DEBUG_STATUSCALLS GNUNET_NO + +#ifdef LINUX +static FILE *proc_stat; +#endif + +/** + * Current CPU load, as percentage of CPU cycles not idle or + * blocked on IO. + */ +static int currentCPULoad; + +static double agedCPULoad = -1; + +/** + * Current IO load, as percentage of CPU cycles blocked on IO. + */ +static int currentIOLoad; + +static double agedIOLoad = -1; + +#ifdef OSX +static int +initMachCpuStats () +{ + unsigned int cpu_count; + processor_cpu_load_info_t cpu_load; + mach_msg_type_number_t cpu_msg_count; + kern_return_t kret; + int i, j; + + kret = host_processor_info (mach_host_self (), + PROCESSOR_CPU_LOAD_INFO, + &cpu_count, + (processor_info_array_t *) & cpu_load, + &cpu_msg_count); + if (kret != KERN_SUCCESS) + { + GNUNET_log (GNUNET_ERROR_TYPE_ERROR | GNUNET_ERROR_TYPE_BULK, + "host_processor_info failed."); + return GNUNET_SYSERR; + } + prev_cpu_load = GNUNET_malloc (cpu_count * sizeof (*prev_cpu_load)); + for (i = 0; i < cpu_count; i++) + { + for (j = 0; j < CPU_STATE_MAX; j++) + { + prev_cpu_load[i].cpu_ticks[j] = cpu_load[i].cpu_ticks[j]; + } + } + vm_deallocate (mach_task_self (), + (vm_address_t) cpu_load, + (vm_size_t) (cpu_msg_count * sizeof (*cpu_load))); + return GNUNET_OK; +} +#endif + +/** + * Update the currentCPU and currentIO load values. + * + * Before its first invocation the method initStatusCalls() must be called. + * If there is an error the method returns -1. + */ +static int +updateUsage () +{ + currentIOLoad = -1; + currentCPULoad = -1; +#ifdef LINUX + /* under linux, first try %idle/usage using /proc/stat; + if that does not work, disable /proc/stat for the future + by closing the file and use the next-best method. */ + if (proc_stat != NULL) + { + static unsigned long long last_cpu_results[5] = { 0, 0, 0, 0, 0 }; + static int have_last_cpu = GNUNET_NO; + int ret; + char line[256]; + unsigned long long user_read, system_read, nice_read, idle_read, + iowait_read; + unsigned long long user, system, nice, idle, iowait; + unsigned long long usage_time = 0, total_time = 1; + + /* Get the first line with the data */ + rewind (proc_stat); + fflush (proc_stat); + if (NULL == fgets (line, 256, proc_stat)) + { + GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_ERROR | + GNUNET_ERROR_TYPE_BULK, + "fgets", "/proc/stat"); + fclose (proc_stat); + proc_stat = NULL; /* don't try again */ + } + else + { + iowait_read = 0; + ret = sscanf (line, "%*s %llu %llu %llu %llu %llu", + &user_read, + &system_read, &nice_read, &idle_read, &iowait_read); + if (ret < 4) + { + GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_ERROR | + GNUNET_ERROR_TYPE_BULK, + "fgets-sscanf", "/proc/stat"); + fclose (proc_stat); + proc_stat = NULL; /* don't try again */ + have_last_cpu = GNUNET_NO; + } + else + { + /* Store the current usage */ + user = user_read - last_cpu_results[0]; + system = system_read - last_cpu_results[1]; + nice = nice_read - last_cpu_results[2]; + idle = idle_read - last_cpu_results[3]; + iowait = iowait_read - last_cpu_results[4]; + /* Calculate the % usage */ + usage_time = user + system + nice; + total_time = usage_time + idle + iowait; + if ((total_time > 0) && (have_last_cpu == GNUNET_YES)) + { + currentCPULoad = (int) (100L * usage_time / total_time); + if (ret > 4) + currentIOLoad = (int) (100L * iowait / total_time); + else + currentIOLoad = -1; /* 2.4 kernel */ + } + /* Store the values for the next calculation */ + last_cpu_results[0] = user_read; + last_cpu_results[1] = system_read; + last_cpu_results[2] = nice_read; + last_cpu_results[3] = idle_read; + last_cpu_results[4] = iowait_read; + have_last_cpu = GNUNET_YES; + return GNUNET_OK; + } + } + } +#endif + +#ifdef OSX + { + unsigned int cpu_count; + processor_cpu_load_info_t cpu_load; + mach_msg_type_number_t cpu_msg_count; + unsigned long long t_sys, t_user, t_nice, t_idle, t_total; + unsigned long long t_idle_all, t_total_all; + kern_return_t kret; + int i, j; + + t_idle_all = t_total_all = 0; + kret = host_processor_info (mach_host_self (), PROCESSOR_CPU_LOAD_INFO, + &cpu_count, + (processor_info_array_t *) & cpu_load, + &cpu_msg_count); + if (kret == KERN_SUCCESS) + { + for (i = 0; i < cpu_count; i++) + { + if (cpu_load[i].cpu_ticks[CPU_STATE_SYSTEM] >= + prev_cpu_load[i].cpu_ticks[CPU_STATE_SYSTEM]) + { + t_sys = cpu_load[i].cpu_ticks[CPU_STATE_SYSTEM] - + prev_cpu_load[i].cpu_ticks[CPU_STATE_SYSTEM]; + } + else + { + t_sys = cpu_load[i].cpu_ticks[CPU_STATE_SYSTEM] + + (ULONG_MAX - prev_cpu_load[i].cpu_ticks[CPU_STATE_SYSTEM] + + 1); + } + + if (cpu_load[i].cpu_ticks[CPU_STATE_USER] >= + prev_cpu_load[i].cpu_ticks[CPU_STATE_USER]) + { + t_user = cpu_load[i].cpu_ticks[CPU_STATE_USER] - + prev_cpu_load[i].cpu_ticks[CPU_STATE_USER]; + } + else + { + t_user = cpu_load[i].cpu_ticks[CPU_STATE_USER] + + (ULONG_MAX - prev_cpu_load[i].cpu_ticks[CPU_STATE_USER] + + 1); + } + + if (cpu_load[i].cpu_ticks[CPU_STATE_NICE] >= + prev_cpu_load[i].cpu_ticks[CPU_STATE_NICE]) + { + t_nice = cpu_load[i].cpu_ticks[CPU_STATE_NICE] - + prev_cpu_load[i].cpu_ticks[CPU_STATE_NICE]; + } + else + { + t_nice = cpu_load[i].cpu_ticks[CPU_STATE_NICE] + + (ULONG_MAX - prev_cpu_load[i].cpu_ticks[CPU_STATE_NICE] + + 1); + } + + if (cpu_load[i].cpu_ticks[CPU_STATE_IDLE] >= + prev_cpu_load[i].cpu_ticks[CPU_STATE_IDLE]) + { + t_idle = cpu_load[i].cpu_ticks[CPU_STATE_IDLE] - + prev_cpu_load[i].cpu_ticks[CPU_STATE_IDLE]; + } + else + { + t_idle = cpu_load[i].cpu_ticks[CPU_STATE_IDLE] + + (ULONG_MAX - prev_cpu_load[i].cpu_ticks[CPU_STATE_IDLE] + + 1); + } + t_total = t_sys + t_user + t_nice + t_idle; + t_idle_all += t_idle; + t_total_all += t_total; + } + for (i = 0; i < cpu_count; i++) + { + for (j = 0; j < CPU_STATE_MAX; j++) + { + prev_cpu_load[i].cpu_ticks[j] = cpu_load[i].cpu_ticks[j]; + } + } + if (t_total_all > 0) + currentCPULoad = 100 - (100 * t_idle_all) / t_total_all; + else + currentCPULoad = -1; + vm_deallocate (mach_task_self (), + (vm_address_t) cpu_load, + (vm_size_t) (cpu_msg_count * sizeof (*cpu_load))); + currentIOLoad = -1; /* FIXME-OSX! */ + return GNUNET_OK; + } + else + { + GNUNET_log (GNUNET_ERROR_TYPE_ERROR | GNUNET_ERROR_TYPE_BULK, + "host_processor_info failed."); + return GNUNET_SYSERR; + } + } +#endif + /* try kstat (Solaris only) */ +#if SOLARIS && HAVE_KSTAT_H && HAVE_SYS_SYSINFO_H + { + static long long last_idlecount; + static long long last_totalcount; + static int kstat_once; /* if open fails, don't keep + trying */ + kstat_ctl_t *kc; + kstat_t *khelper; + long long idlecount; + long long totalcount; + long long deltaidle; + long long deltatotal; + + if (kstat_once == 1) + goto ABORT_KSTAT; + kc = kstat_open (); + if (kc == NULL) + { + GNUNET_log_strerror (GNUNET_ERROR_TYPE_ERROR | GNUNET_ERROR_TYPE_BULK, + "kstat_open"); + goto ABORT_KSTAT; + } + + idlecount = 0; + totalcount = 0; + for (khelper = kc->kc_chain; khelper != NULL; khelper = khelper->ks_next) + { + cpu_stat_t stats; + + if (0 != strncmp (khelper->ks_name, "cpu_stat", strlen ("cpu_stat"))) + continue; + if (khelper->ks_data_size > sizeof (cpu_stat_t)) + continue; /* better save then sorry! */ + if (-1 != kstat_read (kc, khelper, &stats)) + { + idlecount += stats.cpu_sysinfo.cpu[CPU_IDLE]; + totalcount + += stats.cpu_sysinfo.cpu[CPU_IDLE] + + stats.cpu_sysinfo.cpu[CPU_USER] + + stats.cpu_sysinfo.cpu[CPU_KERNEL] + + stats.cpu_sysinfo.cpu[CPU_WAIT]; + } + } + if (0 != kstat_close (kc)) + GNUNET_log_strerror (GNUNET_ERROR_TYPE_ERROR | GNUNET_ERROR_TYPE_BULK, + "kstat_close"); + if ((idlecount == 0) && (totalcount == 0)) + goto ABORT_KSTAT; /* no stats found => abort */ + deltaidle = idlecount - last_idlecount; + deltatotal = totalcount - last_totalcount; + if ((deltatotal > 0) && (last_totalcount > 0)) + { + currentCPULoad = (unsigned int) (100.0 * deltaidle / deltatotal); + if (currentCPULoad > 100) + currentCPULoad = 100; /* odd */ + if (currentCPULoad < 0) + currentCPULoad = 0; /* odd */ + currentCPULoad = 100 - currentCPULoad; /* computed idle-load before! */ + } + else + currentCPULoad = -1; + currentIOLoad = -1; /* FIXME-SOLARIS! */ + last_idlecount = idlecount; + last_totalcount = totalcount; + return GNUNET_OK; + ABORT_KSTAT: + kstat_once = 1; /* failed, don't try again */ + return GNUNET_SYSERR; + } +#endif + + /* insert methods better than getloadavg for + other platforms HERE! */ + + /* ok, maybe we have getloadavg on this platform */ +#if HAVE_GETLOADAVG + { + static int warnOnce = 0; + double loadavg; + if (1 != getloadavg (&loadavg, 1)) + { + /* only warn once, if there is a problem with + getloadavg, we're going to hit it frequently... */ + if (warnOnce == 0) + { + warnOnce = 1; + GNUNET_log_strerror (GNUNET_ERROR_TYPE_ERROR, "getloadavg"); + } + return GNUNET_SYSERR; + } + else + { + /* success with getloadavg */ + currentCPULoad = (int) (100 * loadavg); + currentIOLoad = -1; /* FIXME */ + return GNUNET_OK; + } + } +#endif + +#if MINGW + /* Win NT? */ + if (GNNtQuerySystemInformation) + { + static double dLastKernel; + static double dLastIdle; + static double dLastUser; + double dKernel; + double dIdle; + double dUser; + double dDiffKernel; + double dDiffIdle; + double dDiffUser; + SYSTEM_PROCESSOR_PERFORMANCE_INFORMATION theInfo; + + if (GNNtQuerySystemInformation (SystemProcessorPerformanceInformation, + &theInfo, + sizeof (theInfo), NULL) == NO_ERROR) + { + /* PORT-ME MINGW: Multi-processor? */ + dKernel = Li2Double (theInfo.KernelTime); + dIdle = Li2Double (theInfo.IdleTime); + dUser = Li2Double (theInfo.UserTime); + dDiffKernel = dKernel - dLastKernel; + dDiffIdle = dIdle - dLastIdle; + dDiffUser = dUser - dLastUser; + + if (((dDiffKernel + dDiffUser) > 0) && + (dLastIdle + dLastKernel + dLastUser > 0)) + currentCPULoad = + 100.0 - (dDiffIdle / (dDiffKernel + dDiffUser)) * 100.0; + else + currentCPULoad = -1; /* don't know (yet) */ + + dLastKernel = dKernel; + dLastIdle = dIdle; + dLastUser = dUser; + + currentIOLoad = -1; /* FIXME-MINGW */ + return GNUNET_OK; + } + else + { + /* only warn once, if there is a problem with + NtQuery..., we're going to hit it frequently... */ + static int once; + if (once == 0) + { + once = 1; + GNUNET_log (GNUNET_ERROR_TYPE_ERROR | GNUNET_ERROR_TYPE_BULK, + _("Cannot query the CPU usage (Windows NT).\n")); + } + return GNUNET_SYSERR; + } + } + else + { /* Win 9x */ + HKEY hKey; + DWORD dwDataSize, dwType, dwDummy; + + /* Start query */ + if (RegOpenKeyEx (HKEY_DYN_DATA, + "PerfStats\\StartSrv", + 0, KEY_ALL_ACCESS, &hKey) != ERROR_SUCCESS) + { + /* only warn once */ + static int once = 0; + if (once == 0) + { + once = 1; + GNUNET_log (GNUNET_ERROR_TYPE_ERROR | GNUNET_ERROR_TYPE_BULK, + _("Cannot query the CPU usage (Win 9x)\n")); + } + } + + RegOpenKeyEx (HKEY_DYN_DATA, + "PerfStats\\StartStat", 0, KEY_ALL_ACCESS, &hKey); + dwDataSize = sizeof (dwDummy); + RegQueryValueEx (hKey, + "KERNEL\\CPUUsage", + NULL, &dwType, (LPBYTE) & dwDummy, &dwDataSize); + RegCloseKey (hKey); + + /* Get CPU usage */ + RegOpenKeyEx (HKEY_DYN_DATA, + "PerfStats\\StatData", 0, KEY_ALL_ACCESS, &hKey); + dwDataSize = sizeof (currentCPULoad); + RegQueryValueEx (hKey, + "KERNEL\\CPUUsage", + NULL, &dwType, (LPBYTE) & currentCPULoad, &dwDataSize); + RegCloseKey (hKey); + currentIOLoad = -1; /* FIXME-MINGW! */ + + /* Stop query */ + RegOpenKeyEx (HKEY_DYN_DATA, + "PerfStats\\StopStat", 0, KEY_ALL_ACCESS, &hKey); + RegOpenKeyEx (HKEY_DYN_DATA, + "PerfStats\\StopSrv", 0, KEY_ALL_ACCESS, &hKey); + dwDataSize = sizeof (dwDummy); + RegQueryValueEx (hKey, + "KERNEL\\CPUUsage", + NULL, &dwType, (LPBYTE) & dwDummy, &dwDataSize); + RegCloseKey (hKey); + + return GNUNET_OK; + } +#endif + + /* loadaverage not defined and no platform + specific alternative defined + => default: error + */ + return GNUNET_SYSERR; +} + +/** + * Update load values (if enough time has expired), + * including computation of averages. Code assumes + * that lock has already been obtained. + */ +static void +updateAgedLoad (struct GNUNET_CONFIGURATION_Handle *cfg) +{ + static struct GNUNET_TIME_Absolute lastCall; + + if ((agedCPULoad == -1) + || (GNUNET_TIME_absolute_get_duration (lastCall).value > 500)) + { + /* use smoothing, but do NOT update lastRet at frequencies higher + than 500ms; this makes the smoothing (mostly) independent from + the frequency at which getCPULoad is called (and we don't spend + more time measuring CPU than actually computing something). */ + lastCall = GNUNET_TIME_absolute_get (); + updateUsage (); + if (currentCPULoad == -1) + { + agedCPULoad = -1; + } + else + { + if (agedCPULoad == -1) + { + agedCPULoad = currentCPULoad; + } + else + { + /* for CPU, we don't do the 'fast increase' since CPU is much + more jitterish to begin with */ + agedCPULoad = (agedCPULoad * 31 + currentCPULoad) / 32; + } + } + if (currentIOLoad == -1) + { + agedIOLoad = -1; + } + else + { + if (agedIOLoad == -1) + { + agedIOLoad = currentIOLoad; + } + else + { + /* for IO, we don't do the 'fast increase' since IO is much + more jitterish to begin with */ + agedIOLoad = (agedIOLoad * 31 + currentIOLoad) / 32; + } + } + } +} + +/** + * Get the load of the CPU relative to what is allowed. + * @return the CPU load as a percentage of allowed + * (100 is equivalent to full load) + */ +int +GNUNET_OS_load_cpu_get (struct GNUNET_CONFIGURATION_Handle *cfg) +{ + unsigned long long maxCPULoad; + int ret; + + updateAgedLoad (cfg); + ret = agedCPULoad; + if (ret == -1) + return -1; + if (GNUNET_OK != + GNUNET_CONFIGURATION_get_value_number (cfg, "LOAD", "MAXCPULOAD", + &maxCPULoad)) + return GNUNET_SYSERR; + return (100 * ret) / maxCPULoad; +} + + +/** + * Get the load of the CPU relative to what is allowed. + * @return the CPU load as a percentage of allowed + * (100 is equivalent to full load) + */ +int +GNUNET_OS_load_disk_get (struct GNUNET_CONFIGURATION_Handle *cfg) +{ + unsigned long long maxIOLoad; + int ret; + + updateAgedLoad (cfg); + ret = agedIOLoad; + if (ret == -1) + return -1; + if (-1 == + GNUNET_CONFIGURATION_get_value_number (cfg, "LOAD", "MAXIOLOAD", + &maxIOLoad)) + return GNUNET_SYSERR; + return (100 * ret) / maxIOLoad; +} + +/** + * The following method is called in order to initialize the status calls + * routines. After that it is safe to call each of the status calls separately + * @return GNUNET_OK on success and GNUNET_SYSERR on error (or calls errexit). + */ +void __attribute__ ((constructor)) GNUNET_cpustats_ltdl_init () +{ +#ifdef LINUX + proc_stat = fopen ("/proc/stat", "r"); + if (NULL == proc_stat) + GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_ERROR, "fopen", "/proc/stat"); +#elif OSX + initMachCpuStats (); +#elif MINGW + InitWinEnv (NULL); +#endif + updateUsage (); /* initialize */ +} + +/** + * Shutdown the status calls module. + */ +void __attribute__ ((destructor)) GNUNET_cpustats_ltdl_fini () +{ +#ifdef LINUX + if (proc_stat != NULL) + { + fclose (proc_stat); + proc_stat = NULL; + } +#elif OSX + GNUNET_free_non_null (prev_cpu_load); +#elif MINGW + ShutdownWinEnv (); +#endif +} + + +/* end of os_load_cpu.c */ diff --git a/src/util/os_network.c b/src/util/os_network.c new file mode 100644 index 000000000..cb5ccb12a --- /dev/null +++ b/src/util/os_network.c @@ -0,0 +1,286 @@ +/* + This file is part of GNUnet. + (C) 2004, 2005, 2006 Christian Grothoff (and other contributing authors) + + GNUnet is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published + by the Free Software Foundation; either version 2, 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 + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with GNUnet; see the file COPYING. If not, write to the + Free Software Foundation, Inc., 59 Temple Place - Suite 330, + Boston, MA 02111-1307, USA. + +*/ + +/** + * @file util/os_network.c + * @brief function to determine available network interfaces + * @author Nils Durner + * @author Heikki Lindholm + * @author Jake Dust + */ + +#include "platform.h" +#include "gnunet_common.h" +#include "gnunet_os_lib.h" + +/** + * @brief Enumerate all network interfaces + * @param callback the callback function + */ +void +GNUNET_OS_network_interfaces_list (GNUNET_OS_NetworkInterfaceProcessor proc, + void *cls) +{ +#ifdef MINGW + PMIB_IFTABLE pTable; + PMIB_IPADDRTABLE pAddrTable; + DWORD dwIfIdx, dwExternalNIC; + IPAddr theIP; + + /* Determine our external NIC */ + theIP = inet_addr ("192.0.34.166"); /* www.example.com */ + if ((!GNGetBestInterface) || + (GNGetBestInterface (theIP, &dwExternalNIC) != NO_ERROR)) + { + dwExternalNIC = 0; + } + + /* Enumerate NICs */ + EnumNICs (&pTable, &pAddrTable); + + if (pTable) + { + for (dwIfIdx = 0; dwIfIdx <= pTable->dwNumEntries; dwIfIdx++) + { + char szEntry[1001]; + DWORD dwIP = 0; + int iItm; + PIP_ADAPTER_INFO pAdapterInfo; + PIP_ADAPTER_INFO pAdapter = NULL; + DWORD dwRetVal = 0; + + /* Get IP-Address */ + int i; + for (i = 0; i < pAddrTable->dwNumEntries; i++) + { + if (pAddrTable->table[i].dwIndex == + pTable->table[dwIfIdx].dwIndex) + { + dwIP = pAddrTable->table[i].dwAddr; + break; + } + } + + if (dwIP) + { + BYTE bPhysAddr[MAXLEN_PHYSADDR]; + char *pszIfName = NULL; + char dst[INET_ADDRSTRLEN]; + + /* Get friendly interface name */ + pAdapterInfo = + (IP_ADAPTER_INFO *) malloc (sizeof (IP_ADAPTER_INFO)); + ULONG ulOutBufLen = sizeof (IP_ADAPTER_INFO); + + /* Make an initial call to GetAdaptersInfo to get + the necessary size into the ulOutBufLen variable */ + if (GGetAdaptersInfo (pAdapterInfo, &ulOutBufLen) == + ERROR_BUFFER_OVERFLOW) + { + free (pAdapterInfo); + pAdapterInfo = (IP_ADAPTER_INFO *) malloc (ulOutBufLen); + } + + if ((dwRetVal = + GGetAdaptersInfo (pAdapterInfo, &ulOutBufLen)) == NO_ERROR) + { + pAdapter = pAdapterInfo; + while (pAdapter) + { + if (pTable->table[dwIfIdx].dwIndex == pAdapter->Index) + { + char szKey[251]; + long lLen = 250; + + sprintf (szKey, + "SYSTEM\\CurrentControlSet\\Control\\Network\\" + "{4D36E972-E325-11CE-BFC1-08002BE10318}\\%s\\Connection", + pAdapter->AdapterName); + pszIfName = (char *) malloc (251); + if (QueryRegistry + (HKEY_LOCAL_MACHINE, szKey, "Name", pszIfName, + &lLen) != ERROR_SUCCESS) + { + free (pszIfName); + pszIfName = NULL; + } + } + pAdapter = pAdapter->Next; + } + } + free (pAdapterInfo); + + /* Set entry */ + memset (bPhysAddr, 0, MAXLEN_PHYSADDR); + memcpy (bPhysAddr, + pTable->table[dwIfIdx].bPhysAddr, + pTable->table[dwIfIdx].dwPhysAddrLen); + + snprintf (szEntry, 1000, "%s (%s - %I64u)", + pszIfName ? pszIfName : (char *) pTable-> + table[dwIfIdx].bDescr, inet_ntop (AF_INET, &dwIP, dst, + INET_ADDRSTRLEN), + *((unsigned long long *) bPhysAddr)); + szEntry[1000] = 0; + + if (pszIfName) + free (pszIfName); + + if (GNUNET_OK != + proc (cls, + szEntry, + pAddrTable->table[dwIfIdx].dwIndex == dwExternalNIC, + NULL /* FIXME: pass actual IP address! */ , + 0)) + break; + } + } + GlobalFree (pAddrTable); + GlobalFree (pTable); + } + + return GNUNET_YES; + +#elif HAVE_GETIFADDRS && HAVE_FREEIFADDRS + + struct ifaddrs *ifa_first; + socklen_t alen; + + if (getifaddrs (&ifa_first) == 0) + { + struct ifaddrs *ifa_ptr; + + ifa_ptr = ifa_first; + for (ifa_ptr = ifa_first; ifa_ptr != NULL; ifa_ptr = ifa_ptr->ifa_next) + { + if (ifa_ptr->ifa_name != NULL && + ifa_ptr->ifa_addr != NULL && (ifa_ptr->ifa_flags & IFF_UP) != 0) + { + if ((ifa_ptr->ifa_addr->sa_family != AF_INET) && + (ifa_ptr->ifa_addr->sa_family != AF_INET6)) + continue; + if (ifa_ptr->ifa_addr->sa_family == AF_INET) + alen = sizeof (struct sockaddr_in); + else + alen = sizeof (struct sockaddr_in6); + if (GNUNET_OK != proc (cls, + ifa_ptr->ifa_name, + 0 == strcmp (ifa_ptr->ifa_name, + GNUNET_DEFAULT_INTERFACE), + ifa_ptr->ifa_addr, alen)) + break; + } + } + freeifaddrs (ifa_first); + } +#else + char line[1024]; + const char *start; + char ifc[12]; + char addrstr[128]; + FILE *f; + int have_ifc; + struct sockaddr_in a4; + struct sockaddr_in6 a6; + struct in_addr v4; + struct in6_addr v6; + + if (system ("ifconfig -a > /dev/null 2> /dev/null")) + if (system ("/sbin/ifconfig -a > /dev/null 2> /dev/null") == 0) + f = popen ("/sbin/ifconfig -a 2> /dev/null", "r"); + else + f = NULL; + else + f = popen ("ifconfig -a 2> /dev/null", "r"); + if (!f) + { + GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING | + GNUNET_ERROR_TYPE_BULK, "popen", "ifconfig"); + return; + } + + have_ifc = GNUNET_NO; + ifc[11] = '\0'; + while (NULL != fgets (line, sizeof (line), f)) + { + if (strlen (line) == 0) + { + have_ifc = GNUNET_NO; + continue; + } + if (!isspace (line[0])) + { + have_ifc = + (1 == SSCANF (line, "%11s", ifc)) ? GNUNET_YES : GNUNET_NO; + /* would end with ':' on OSX, fix it! */ + if (ifc[strlen (ifc) - 1] == ':') + ifc[strlen (ifc) - 1] = '\0'; + continue; + } + if (!have_ifc) + continue; /* strange input, hope for the best */ + start = line; + while (('\0' != *start) && (isspace (*start))) + start++; + if ( /* Linux */ + (1 == SSCANF (start, "inet addr:%127s", addrstr)) || + (1 == SSCANF (start, "inet6 addr:%127s", addrstr)) || + /* Solaris, OS X */ + (1 == SSCANF (start, "inet %127s", addrstr)) || + (1 == SSCANF (start, "inet6 %127s", addrstr))) + { + /* IPv4 */ + if (1 == inet_pton (AF_INET, addrstr, &v4)) + { + memset (&a4, 0, sizeof (a4)); + a4.sin_family = AF_INET; + a4.sin_addr = v4; + if (GNUNET_OK != + proc (cls, + ifc, + 0 == strcmp (ifc, GNUNET_DEFAULT_INTERFACE), + (const struct sockaddr *) &a4, sizeof (a4))) + break; + continue; + } + /* IPv6 */ + if (1 == inet_pton (AF_INET6, addrstr, &v6)) + { + memset (&a6, 0, sizeof (a6)); + a6.sin6_family = AF_INET6; + a6.sin6_addr = v6; + fprintf (stderr, "procing %s\n", addrstr); + if (GNUNET_OK != + proc (cls, + ifc, + 0 == strcmp (ifc, GNUNET_DEFAULT_INTERFACE), + (const struct sockaddr *) &a6, sizeof (a6))) + break; + continue; + } + } + } + pclose (f); +#endif +} + + +/* end of os_network.c */ diff --git a/src/util/os_priority.c b/src/util/os_priority.c new file mode 100644 index 000000000..7862d68e9 --- /dev/null +++ b/src/util/os_priority.c @@ -0,0 +1,186 @@ +/* + This file is part of GNUnet + (C) 2002, 2003, 2004, 2005, 2006 Christian Grothoff (and other contributing authors) + + GNUnet is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published + by the Free Software Foundation; either version 2, 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 + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with GNUnet; see the file COPYING. If not, write to the + Free Software Foundation, Inc., 59 Temple Place - Suite 330, + Boston, MA 02111-1307, USA. +*/ + +/** + * @file util/os/priority.c + * @brief Methods to set process priority + * @author Nils Durner + */ + +#include "platform.h" +#include "gnunet_common.h" +#include "gnunet_os_lib.h" + +/** + * Set our process priority + */ +int +GNUNET_OS_set_process_priority (pid_t proc, + enum GNUNET_SCHEDULER_Priority eprio) +{ + int prio = 0; + + GNUNET_assert (eprio < GNUNET_SCHEDULER_PRIORITY_COUNT); + if (eprio == GNUNET_SCHEDULER_PRIORITY_KEEP) + return GNUNET_OK; + /* convert to MINGW/Unix values */ + switch (eprio) + { + case GNUNET_SCHEDULER_PRIORITY_DEFAULT: +#ifdef MINGW + prio = NORMAL_PRIORITY_CLASS; +#else + prio = 0; +#endif + break; + case GNUNET_SCHEDULER_PRIORITY_HIGH: +#ifdef MINGW + prio = ABOVE_NORMAL_PRIORITY_CLASS; +#else + prio = -5; +#endif + break; + case GNUNET_SCHEDULER_PRIORITY_BACKGROUND: +#ifdef MINGW + prio = BELOW_NORMAL_PRIORITY_CLASS; +#else + prio = 10; +#endif + break; + case GNUNET_SCHEDULER_PRIORITY_UI: + case GNUNET_SCHEDULER_PRIORITY_URGENT: +#ifdef MINGW + prio = HIGH_PRIORITY_CLASS; +#else + prio = -10; +#endif + break; + case GNUNET_SCHEDULER_PRIORITY_IDLE: +#ifdef MINGW + prio = IDLE_PRIORITY_CLASS; +#else + prio = 19; +#endif + break; + default: + GNUNET_assert (0); + return GNUNET_SYSERR; + } + /* Set process priority */ +#ifdef MINGW + SetPriorityClass (GetCurrentProcess (), prio); +#else + if (proc == getpid ()) + { + errno = 0; + if ((-1 == nice (prio)) && (errno != 0)) + { + GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING | + GNUNET_ERROR_TYPE_BULK, "nice"); + return GNUNET_SYSERR; + } + } + else + { + if (0 != setpriority (PRIO_PROCESS, proc, prio)) + + { + GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING | + GNUNET_ERROR_TYPE_BULK, "setpriority"); + return GNUNET_SYSERR; + } + } +#endif + return GNUNET_OK; +} + + + +/** + * Start a process. + * + * @param filename name of the binary + * @param ... NULL-terminated list of arguments to the process + * @return process ID of the new process, -1 on error + */ +pid_t +GNUNET_OS_start_process (const char *filename, ...) +{ + pid_t ret; + char **argv; + va_list ap; + int argc; + + ret = fork (); + if (ret != 0) + { + if (ret == -1) + GNUNET_log_strerror (GNUNET_ERROR_TYPE_ERROR, "fork"); + return ret; + } + argc = 0; + va_start (ap, filename); + while (NULL != va_arg (ap, char *)) + argc++; + va_end (ap); + argv = GNUNET_malloc (sizeof (char *) * (argc + 1)); + argc = 0; + va_start (ap, filename); + while (NULL != (argv[argc] = va_arg (ap, char *))) + argc++; + va_end (ap); + execvp (filename, argv); + GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_ERROR, "execvp", filename); + exit (1); +} + + + + +/** + * Start a process. + * + * @param filename name of the binary + * @param argv NULL-terminated list of arguments to the process + * @return process ID of the new process, -1 on error + */ +pid_t +GNUNET_OS_start_process_v (const char *filename, char *const argv[]) +{ + pid_t ret; + + ret = fork (); + if (ret != 0) + { + if (ret == -1) + GNUNET_log_strerror (GNUNET_ERROR_TYPE_ERROR, "fork"); + return ret; + } + execvp (filename, argv); + GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_ERROR, "execvp", filename); + exit (1); +} + + + + + + +/* end of os_priority.c */ diff --git a/src/util/perf_crypto_hash.c b/src/util/perf_crypto_hash.c new file mode 100644 index 000000000..a6f6ba340 --- /dev/null +++ b/src/util/perf_crypto_hash.c @@ -0,0 +1,64 @@ +/* + This file is part of GNUnet. + (C) 2002, 2003, 2004, 2006 Christian Grothoff (and other contributing authors) + + GNUnet is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published + by the Free Software Foundation; either version 2, 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 + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with GNUnet; see the file COPYING. If not, write to the + Free Software Foundation, Inc., 59 Temple Place - Suite 330, + Boston, MA 02111-1307, USA. +*/ + +/** + * @author Christian Grothoff + * @file util/perf_crypto_hash.c + * @brief measure performance of hash function + */ +#include "platform.h" +#include "gnunet_common.h" +#include "gnunet_crypto_lib.h" +#include "gnunet_time_lib.h" + +static void +perfHash () +{ + GNUNET_HashCode hc1; + GNUNET_HashCode hc2; + GNUNET_HashCode hc3; + int i; + char *buf; + + buf = GNUNET_malloc (1024 * 64); + memset (buf, 1, 1024 * 64); + GNUNET_CRYPTO_hash ("foo", 3, &hc1); + for (i = 0; i < 1024; i++) + { + GNUNET_CRYPTO_hash (&hc1, sizeof (GNUNET_HashCode), &hc2); + GNUNET_CRYPTO_hash (&hc2, sizeof (GNUNET_HashCode), &hc1); + GNUNET_CRYPTO_hash (buf, 1024 * 64, &hc3); + } + GNUNET_free (buf); +} + +int +main (int argc, char *argv[]) +{ + struct GNUNET_TIME_Absolute start; + + start = GNUNET_TIME_absolute_get (); + perfHash (); + printf ("Hash perf took %llu ms\n", + GNUNET_TIME_absolute_get_duration (start).value); + return 0; +} + +/* end of hashperf.c */ diff --git a/src/util/plugin.c b/src/util/plugin.c new file mode 100644 index 000000000..d26a343c7 --- /dev/null +++ b/src/util/plugin.c @@ -0,0 +1,243 @@ +/* + This file is part of GNUnet + (C) 2002, 2003, 2004, 2005, 2006, 2009 Christian Grothoff (and other contributing authors) + + GNUnet is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published + by the Free Software Foundation; either version 2, 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 + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with GNUnet; see the file COPYING. If not, write to the + Free Software Foundation, Inc., 59 Temple Place - Suite 330, + Boston, MA 02111-1307, USA. +*/ + +/** + * @file util/plugin.c + * @brief Methods to access plugins + * @author Christian Grothoff + */ + +#include "platform.h" +#include +#include "gnunet_common.h" +#include "gnunet_os_lib.h" +#include "gnunet_plugin_lib.h" + +/** + * Linked list of active plugins. + */ +struct PluginList +{ + /** + * This is a linked list. + */ + struct PluginList *next; + + /** + * Name of the library. + */ + char *name; + + /** + * System handle. + */ + void *handle; +}; + + +/** + * Libtool search path before we started. + */ +static char *old_dlsearchpath; + + +/** + * List of plugins we have loaded. + */ +static struct PluginList *plugins; + + +/** + * Setup libtool paths. + */ +void __attribute__ ((constructor)) GNUNET_PLUGIN_init () +{ + int err; + const char *opath; + char *path; + char *cpath; + +#ifdef MINGW + InitWinEnv (NULL); +#endif + + err = lt_dlinit (); + if (err > 0) + { + fprintf (stderr, + _("Initialization of plugin mechanism failed: %s!\n"), + lt_dlerror ()); + return; + } + opath = lt_dlgetsearchpath (); + if (opath != NULL) + old_dlsearchpath = GNUNET_strdup (opath); + path = GNUNET_OS_installation_get_path (GNUNET_OS_IPK_LIBDIR); + if (path != NULL) + { + if (opath != NULL) + { + cpath = GNUNET_malloc (strlen (path) + strlen (opath) + 4); + strcpy (cpath, opath); + strcat (cpath, ":"); + strcat (cpath, path); + lt_dlsetsearchpath (cpath); + GNUNET_free (path); + GNUNET_free (cpath); + } + else + { + lt_dlsetsearchpath (path); + GNUNET_free (path); + } + } +} + + +/** + * Shutdown libtool. + */ +void __attribute__ ((destructor)) GNUNET_PLUGIN_fini () +{ + lt_dlsetsearchpath (old_dlsearchpath); + if (old_dlsearchpath != NULL) + { + GNUNET_free (old_dlsearchpath); + old_dlsearchpath = NULL; + } + +#ifdef MINGW + ShutdownWinEnv (); +#endif + + lt_dlexit (); +} + + +/** + * Lookup a function in the plugin. + */ +static GNUNET_PLUGIN_Callback +resolve_function (struct PluginList *plug, const char *name) +{ + char *initName; + void *mptr; + + GNUNET_asprintf (&initName, "_%s_%s", plug->name, name); + mptr = lt_dlsym (plug->handle, &initName[1]); + if (mptr == NULL) + mptr = lt_dlsym (plug->handle, initName); + if (mptr == NULL) + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + _("`%s' failed to resolve method '%s' with error: %s\n"), + "lt_dlsym", &initName[1], lt_dlerror ()); + GNUNET_free (initName); + return mptr; +} + + +/** + * Setup plugin (runs the "init" callback and returns whatever "init" + * returned). If "init" returns NULL, the plugin is unloaded. + * + * Note that the library must export symbols called + * "library_name_init" and "library_name_done". These will be called + * when the library is loaded and unloaded respectively. + * + * @param library_name name of the plugin to load + * @param arg argument to the plugin initialization function + * @return whatever the initialization function returned + */ +void * +GNUNET_PLUGIN_load (const char *library_name, void *arg) +{ + void *libhandle; + struct PluginList *plug; + GNUNET_PLUGIN_Callback init; + void *ret; + + libhandle = lt_dlopenext (library_name); + if (libhandle == NULL) + { + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + _("`%s' failed for library `%s' with error: %s\n"), + "lt_dlopenext", library_name, lt_dlerror ()); + return NULL; + } + plug = GNUNET_malloc (sizeof (struct PluginList)); + plug->handle = libhandle; + plug->name = GNUNET_strdup (library_name); + plug->next = plugins; + plugins = plug; + init = resolve_function (plug, "init"); + if ((init == NULL) || (NULL == (ret = init (arg)))) + { + GNUNET_free (plug->name); + plugins = plug->next; + GNUNET_free (plug); + return NULL; + } + return ret; +} + + +/** + * Unload plugin (runs the "done" callback and returns whatever "done" + * returned). The plugin is then unloaded. + * + * @param library_name name of the plugin to unload + * @param arg argument to the plugin shutdown function + * @return whatever the shutdown function returned + */ +void * +GNUNET_PLUGIN_unload (const char *library_name, void *arg) +{ + struct PluginList *pos; + struct PluginList *prev; + GNUNET_PLUGIN_Callback done; + void *ret; + + prev = NULL; + pos = plugins; + while ((pos != NULL) && (0 != strcmp (pos->name, library_name))) + { + prev = pos; + pos = pos->next; + } + if (pos == NULL) + return NULL; + + done = resolve_function (pos, "done"); + ret = NULL; + if (done != NULL) + ret = done (arg); + if (prev == NULL) + plugins = pos->next; + else + prev->next = pos->next; + // lt_dlclose (pos->handle); + GNUNET_free (pos->name); + GNUNET_free (pos); + return ret; +} + + + +/* end of plugin.c */ diff --git a/src/util/program.c b/src/util/program.c new file mode 100644 index 000000000..c8ebfc4eb --- /dev/null +++ b/src/util/program.c @@ -0,0 +1,202 @@ +/* + This file is part of GNUnet. + (C) 2009 Christian Grothoff (and other contributing authors) + + GNUnet is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published + by the Free Software Foundation; either version 2, 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 + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with GNUnet; see the file COPYING. If not, write to the + Free Software Foundation, Inc., 59 Temple Place - Suite 330, + Boston, MA 02111-1307, USA. +*/ + +/** + * @file util/program.c + * @brief standard code for GNUnet startup and shutdown + * @author Christian Grothoff + */ + +#include "platform.h" +#include "gnunet_common.h" +#include "gnunet_configuration_lib.h" +#include "gnunet_crypto_lib.h" +#include "gnunet_directories.h" +#include "gnunet_getopt_lib.h" +#include "gnunet_os_lib.h" +#include "gnunet_program_lib.h" +#include "gnunet_scheduler_lib.h" +#include + +/** + * Context for the command. + */ +struct CommandContext +{ + /** + * Argv argument. + */ + char *const *args; + + /** + * Name of the configuration file used, can be NULL! + */ + char *cfgfile; + + /** + * Main function to run. + */ + GNUNET_PROGRAM_Main task; + + /** + * Closure for task. + */ + void *task_cls; + + /** + * Configuration to use. + */ + struct GNUNET_CONFIGURATION_Handle *cfg; + +}; + + +/** + * Initial task called by the scheduler for each + * program. Runs the program-specific main task. + */ +static void +program_main (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc) +{ + struct CommandContext *cc = cls; + + cc->task (cc->task_cls, tc->sched, cc->args, cc->cfgfile, cc->cfg); +} + + +/** + * Compare function for 'qsort' to sort command-line arguments by the + * short option. + */ +static int +cmd_sorter (const void *a1, const void *a2) +{ + const struct GNUNET_GETOPT_CommandLineOption *c1 = a1; + const struct GNUNET_GETOPT_CommandLineOption *c2 = a2; + if (toupper (c1->shortName) > toupper (c2->shortName)) + return 1; + if (toupper (c1->shortName) < toupper (c2->shortName)) + return -1; + if (c1->shortName > c2->shortName) + return 1; + if (c1->shortName < c2->shortName) + return -1; + return 0; +} + + +/** + * Run a standard GNUnet command startup sequence (initialize loggers + * and configuration, parse options). + * + * @param argc number of command line arguments + * @param argv command line arguments + * @param binaryName our expected name + * @param options command line options + * @param task main function to run + * @param task_cls closure for task + * @return GNUNET_SYSERR on error, GNUNET_OK on success + */ +int +GNUNET_PROGRAM_run (int argc, + char *const *argv, + const char *binaryName, + const char *binaryHelp, + const struct GNUNET_GETOPT_CommandLineOption *options, + GNUNET_PROGRAM_Main task, void *task_cls) +{ + struct CommandContext cc; + char *path; + char *loglev; + int ret; + unsigned int cnt; + struct GNUNET_GETOPT_CommandLineOption defoptions[] = { + GNUNET_GETOPT_OPTION_CFG_FILE (&cc.cfgfile), + GNUNET_GETOPT_OPTION_HELP (binaryHelp), + GNUNET_GETOPT_OPTION_LOGLEVEL (&loglev), + GNUNET_GETOPT_OPTION_VERSION (PACKAGE_VERSION) + }; + struct GNUNET_GETOPT_CommandLineOption *allopts; + + memset (&cc, 0, sizeof (cc)); + loglev = NULL; + cc.task = task; + cc.task_cls = task_cls; + cc.cfg = GNUNET_CONFIGURATION_create (); + + /* prepare */ +#if ENABLE_NLS + setlocale (LC_ALL, ""); + path = GNUNET_OS_installation_get_path (GNUNET_OS_IPK_LOCALEDIR); + if (path != NULL) + { + BINDTEXTDOMAIN ("GNUnet", path); + GNUNET_free (path); + } + textdomain ("GNUnet"); +#endif + cnt = 0; + while (options[cnt].name != NULL) + cnt++; + allopts = + GNUNET_malloc ((cnt + + 1) * sizeof (struct GNUNET_GETOPT_CommandLineOption) + + sizeof (defoptions)); + memcpy (allopts, defoptions, sizeof (defoptions)); + memcpy (&allopts + [sizeof (defoptions) / + sizeof (struct GNUNET_GETOPT_CommandLineOption)], options, + (cnt + 1) * sizeof (struct GNUNET_GETOPT_CommandLineOption)); + cnt += + sizeof (defoptions) / sizeof (struct GNUNET_GETOPT_CommandLineOption); + qsort (allopts, cnt, sizeof (struct GNUNET_GETOPT_CommandLineOption), + &cmd_sorter); + loglev = GNUNET_strdup ("WARNING"); + if ((-1 == (ret = GNUNET_GETOPT_run (binaryName, + cc.cfg, + allopts, + (unsigned int) argc, argv))) || + ((GNUNET_OK != + GNUNET_log_setup (binaryName, + loglev, + NULL)) || + (GNUNET_OK != GNUNET_CONFIGURATION_load (cc.cfg, cc.cfgfile)))) + + { + GNUNET_free_non_null (cc.cfgfile); + GNUNET_free (loglev); + GNUNET_free (allopts); + return GNUNET_SYSERR; + } + GNUNET_free (allopts); + + /* run */ + cc.args = &argv[ret]; + GNUNET_SCHEDULER_run (&program_main, &cc); + + /* clean up */ + GNUNET_CONFIGURATION_destroy (cc.cfg); + GNUNET_free_non_null (cc.cfgfile); + GNUNET_free (loglev); + return GNUNET_OK; +} + + +/* end of program.c */ diff --git a/src/util/pseudonym.c b/src/util/pseudonym.c new file mode 100644 index 000000000..6d9146613 --- /dev/null +++ b/src/util/pseudonym.c @@ -0,0 +1,545 @@ +/* + This file is part of GNUnet + (C) 2003, 2004, 2005, 2006, 2007, 2008 Christian Grothoff (and other contributing authors) + + GNUnet is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published + by the Free Software Foundation; either version 2, 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 + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with GNUnet; see the file COPYING. If not, write to the + Free Software Foundation, Inc., 59 Temple Place - Suite 330, + Boston, MA 02111-1307, USA. +*/ + +/** + * @file util/pseudonym.c + * @brief helper functions + * @author Christian Grothoff + */ + +#include "platform.h" +#include "gnunet_common.h" +#include "gnunet_container_lib.h" +#include "gnunet_disk_lib.h" +#include "gnunet_pseudonym_lib.h" + +#define PS_METADATA_DIR DIR_SEPARATOR_STR "data" DIR_SEPARATOR_STR "pseudonyms/metadata" DIR_SEPARATOR_STR +#define PS_NAMES_DIR DIR_SEPARATOR_STR "data" DIR_SEPARATOR_STR "pseudonyms/names" DIR_SEPARATOR_STR + +struct DiscoveryCallback +{ + struct DiscoveryCallback *next; + GNUNET_PSEUDONYM_Iterator callback; + void *closure; +}; + +static struct DiscoveryCallback *head; + +/** + * Internal notification about new tracked URI. + */ +static void +internal_notify (const GNUNET_HashCode * id, + const struct GNUNET_CONTAINER_MetaData *md, int rating) +{ + struct DiscoveryCallback *pos; + + pos = head; + while (pos != NULL) + { + pos->callback (pos->closure, id, md, rating); + pos = pos->next; + } +} + +/** + * Register callback to be invoked whenever we discover + * a new pseudonym. + */ +int +GNUNET_PSEUDONYM_discovery_callback_register (struct + GNUNET_CONFIGURATION_Handle + *cfg, + GNUNET_PSEUDONYM_Iterator + iterator, void *closure) +{ + struct DiscoveryCallback *list; + + list = GNUNET_malloc (sizeof (struct DiscoveryCallback)); + list->callback = iterator; + list->closure = closure; + list->next = head; + head = list; + GNUNET_PSEUDONYM_list_all (cfg, iterator, closure); + return GNUNET_OK; +} + +/** + * Unregister pseudonym discovery callback. + */ +int +GNUNET_PSEUDONYM_discovery_callback_unregister (GNUNET_PSEUDONYM_Iterator + iterator, void *closure) +{ + struct DiscoveryCallback *prev; + struct DiscoveryCallback *pos; + + prev = NULL; + pos = head; + while ((pos != NULL) && + ((pos->callback != iterator) || (pos->closure != closure))) + { + prev = pos; + pos = pos->next; + } + if (pos == NULL) + return GNUNET_SYSERR; + if (prev == NULL) + head = pos->next; + else + prev->next = pos->next; + GNUNET_free (pos); + return GNUNET_OK; +} + + +/** + * Get the filename (or directory name) for the given + * pseudonym identifier and directory prefix. + */ +static char * +get_data_filename (struct GNUNET_CONFIGURATION_Handle + *cfg, const char *prefix, const GNUNET_HashCode * psid) +{ + struct GNUNET_CRYPTO_HashAsciiEncoded enc; + + if (psid != NULL) + GNUNET_CRYPTO_hash_to_enc (psid, &enc); + return GNUNET_DISK_get_home_filename (cfg, + GNUNET_CLIENT_SERVICE_NAME, + prefix, + (psid == + NULL) ? NULL : (const char *) &enc, + NULL); +} + +static void +write_pseudonym_info (struct GNUNET_CONFIGURATION_Handle *cfg, + const GNUNET_HashCode * nsid, + const struct GNUNET_CONTAINER_MetaData *meta, + int32_t ranking, const char *ns_name) +{ + unsigned int size; + unsigned int tag; + unsigned int off; + char *buf; + char *fn; + + fn = get_data_filename (cfg, PS_METADATA_DIR, nsid); + GNUNET_assert (fn != NULL); + size = GNUNET_CONTAINER_meta_data_get_serialized_size (meta, + GNUNET_CONTAINER_META_DATA_SERIALIZE_FULL); + tag = size + sizeof (int) + 1; + off = 0; + if (ns_name != NULL) + { + off = strlen (ns_name); + tag += off; + } + buf = GNUNET_malloc (tag); + ((int *) buf)[0] = htonl (ranking); /* ranking */ + if (ns_name != NULL) + { + memcpy (&buf[sizeof (int)], ns_name, off + 1); + } + else + { + buf[sizeof (int)] = '\0'; + } + GNUNET_assert + (size == GNUNET_CONTAINER_meta_data_serialize (meta, + &buf[sizeof + (int) + + off + 1], + size, + GNUNET_CONTAINER_META_DATA_SERIALIZE_FULL)); + GNUNET_DISK_file_write (fn, buf, tag, "660"); + GNUNET_free (fn); + GNUNET_free (buf); + /* create entry for pseudonym name in names */ + GNUNET_free_non_null (GNUNET_PSEUDONYM_id_to_name (cfg, nsid)); +} + +static int +read_info (struct GNUNET_CONFIGURATION_Handle *cfg, + const GNUNET_HashCode * nsid, + struct GNUNET_CONTAINER_MetaData **meta, + int32_t * ranking, char **ns_name) +{ + unsigned long long len; + unsigned int size; + unsigned int zend; + struct stat sbuf; + char *buf; + char *fn; + + if (meta != NULL) + *meta = NULL; + if (ns_name != NULL) + *ns_name = NULL; + fn = get_data_filename (cfg, PS_METADATA_DIR, nsid); + GNUNET_assert (fn != NULL); + + if ((0 != STAT (fn, &sbuf)) + || (GNUNET_OK != GNUNET_DISK_file_size (fn, &len, GNUNET_YES))) + { + GNUNET_free (fn); + return GNUNET_SYSERR; + } + if (len <= sizeof (int) + 1) + { + GNUNET_free (fn); + return GNUNET_SYSERR; + } + if (len > 16 * 1024 * 1024) + { + /* too big, must be invalid! remove! */ + GNUNET_break (0); + if (0 != UNLINK (fn)) + GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING, "unlink", fn); + GNUNET_free (fn); + return GNUNET_SYSERR; + } + buf = GNUNET_malloc (len); + if (len != GNUNET_DISK_file_read (fn, len, buf)) + { + GNUNET_free (buf); + GNUNET_free (fn); + return GNUNET_SYSERR; + } + if (ranking != NULL) + *ranking = ntohl (((int *) buf)[0]); + zend = sizeof (int); + while ((zend < len) && (buf[zend] != '\0')) + zend++; + if (zend == len) + { + GNUNET_free (buf); + GNUNET_free (fn); + return GNUNET_SYSERR; + } + if (ns_name != NULL) + { + if (zend != sizeof (int)) + *ns_name = GNUNET_strdup (&buf[sizeof (int)]); + else + *ns_name = NULL; + } + zend++; + size = len - zend; + if (meta != NULL) + { + *meta = GNUNET_CONTAINER_meta_data_deserialize (&buf[zend], size); + if ((*meta) == NULL) + { + /* invalid data! remove! */ + GNUNET_break (0); + if (0 != UNLINK (fn)) + GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING, + "unlink", fn); + GNUNET_free (buf); + GNUNET_free (fn); + return GNUNET_SYSERR; + } + } + GNUNET_free (fn); + GNUNET_free (buf); + return GNUNET_OK; +} + + + +/** + * Return the unique, human readable name for the given namespace. + * + * @return NULL on failure (should never happen) + */ +char * +GNUNET_PSEUDONYM_id_to_name (struct GNUNET_CONFIGURATION_Handle *cfg, + const GNUNET_HashCode * nsid) +{ + struct GNUNET_CONTAINER_MetaData *meta; + char *name; + GNUNET_HashCode nh; + char *fn; + unsigned long long len; + int fd; + unsigned int i; + unsigned int idx; + char *ret; + struct stat sbuf; + + meta = NULL; + name = NULL; + if (GNUNET_OK == read_info (cfg, nsid, &meta, NULL, &name)) + { + if ((meta != NULL) && (name == NULL)) + name = GNUNET_CONTAINER_meta_data_get_first_by_types (meta, + EXTRACTOR_TITLE, + EXTRACTOR_FILENAME, + EXTRACTOR_DESCRIPTION, + EXTRACTOR_SUBJECT, + EXTRACTOR_PUBLISHER, + EXTRACTOR_AUTHOR, + EXTRACTOR_COMMENT, + EXTRACTOR_SUMMARY, + EXTRACTOR_OWNER, + -1); + if (meta != NULL) + { + GNUNET_CONTAINER_meta_data_destroy (meta); + meta = NULL; + } + } + if (name == NULL) + name = GNUNET_strdup (_("no-name")); + GNUNET_CRYPTO_hash (name, strlen (name), &nh); + fn = get_data_filename (cfg, PS_NAMES_DIR, &nh); + GNUNET_assert (fn != NULL); + + len = 0; + if (0 == STAT (fn, &sbuf)) + GNUNET_DISK_file_size (fn, &len, GNUNET_YES); + fd = GNUNET_DISK_file_open (fn, O_CREAT | O_RDWR, S_IRUSR | S_IWUSR); + i = 0; + idx = -1; + while ((len >= sizeof (GNUNET_HashCode)) && + (sizeof (GNUNET_HashCode) + == READ (fd, &nh, sizeof (GNUNET_HashCode)))) + { + if (0 == memcmp (&nh, nsid, sizeof (GNUNET_HashCode))) + { + idx = i; + break; + } + i++; + len -= sizeof (GNUNET_HashCode); + } + if (idx == -1) + { + idx = i; + if (sizeof (GNUNET_HashCode) != + WRITE (fd, nsid, sizeof (GNUNET_HashCode))) + GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING, "write", fn); + } + CLOSE (fd); + ret = GNUNET_malloc (strlen (name) + 32); + GNUNET_snprintf (ret, strlen (name) + 32, "%s-%u", name, idx); + GNUNET_free (name); + GNUNET_free (fn); + return ret; +} + +/** + * Get the namespace ID belonging to the given namespace name. + * + * @return GNUNET_OK on success + */ +int +GNUNET_PSEUDONYM_name_to_id (struct GNUNET_CONFIGURATION_Handle *cfg, + const char *ns_uname, GNUNET_HashCode * nsid) +{ + size_t slen; + unsigned long long len; + unsigned int idx; + char *name; + GNUNET_HashCode nh; + char *fn; + int fd; + + idx = -1; + slen = strlen (ns_uname); + while ((slen > 0) && (1 != sscanf (&ns_uname[slen - 1], "-%u", &idx))) + slen--; + if (slen == 0) + return GNUNET_SYSERR; + name = GNUNET_strdup (ns_uname); + name[slen - 1] = '\0'; + GNUNET_CRYPTO_hash (name, strlen (name), &nh); + GNUNET_free (name); + fn = get_data_filename (cfg, PS_NAMES_DIR, &nh); + GNUNET_assert (fn != NULL); + + if ((GNUNET_OK != GNUNET_DISK_file_test (fn) || + (GNUNET_OK != GNUNET_DISK_file_size (fn, &len, GNUNET_YES))) || + ((idx + 1) * sizeof (GNUNET_HashCode) > len)) + { + GNUNET_free (fn); + return GNUNET_SYSERR; + } + fd = GNUNET_DISK_file_open (fn, O_CREAT | O_RDWR, S_IRUSR | S_IWUSR); + GNUNET_free (fn); + LSEEK (fd, idx * sizeof (GNUNET_HashCode), SEEK_SET); + if (sizeof (GNUNET_HashCode) != READ (fd, nsid, sizeof (GNUNET_HashCode))) + { + CLOSE (fd); + return GNUNET_SYSERR; + } + CLOSE (fd); + return GNUNET_OK; +} + + + + +struct ListPseudonymClosure +{ + GNUNET_PSEUDONYM_Iterator iterator; + void *closure; + struct GNUNET_CONFIGURATION_Handle *cfg; +}; + +static int +list_pseudonym_helper (void *cls, const char *fullname) +{ + struct ListPseudonymClosure *c = cls; + int ret; + GNUNET_HashCode id; + int rating; + struct GNUNET_CONTAINER_MetaData *meta; + const char *fn; + + if (strlen (fullname) < sizeof (struct GNUNET_CRYPTO_HashAsciiEncoded)) + return GNUNET_OK; + fn = + &fullname[strlen (fullname) + 1 - + sizeof (struct GNUNET_CRYPTO_HashAsciiEncoded)]; + if (fn[-1] != DIR_SEPARATOR) + return GNUNET_OK; + ret = GNUNET_OK; + if (GNUNET_OK != GNUNET_CRYPTO_hash_from_string (fn, &id)) + return GNUNET_OK; /* invalid name */ + if (GNUNET_OK != read_info (c->cfg, &id, &meta, &rating, NULL)) + return GNUNET_OK; /* ignore entry */ + if (c->iterator != NULL) + ret = c->iterator (c->closure, &id, meta, rating); + GNUNET_CONTAINER_meta_data_destroy (meta); + return ret; +} + +/** + * List all available pseudonyms. + */ +int +GNUNET_PSEUDONYM_list_all (struct GNUNET_CONFIGURATION_Handle *cfg, + GNUNET_PSEUDONYM_Iterator iterator, void *closure) +{ + struct ListPseudonymClosure cls; + char *fn; + int ret; + + cls.iterator = iterator; + cls.closure = closure; + cls.cfg = cfg; + fn = get_data_filename (cfg, PS_METADATA_DIR, NULL); + GNUNET_assert (fn != NULL); + GNUNET_DISK_directory_create (fn); + ret = GNUNET_DISK_directory_scan (fn, &list_pseudonym_helper, &cls); + GNUNET_free (fn); + return ret; +} + +/** + * Change the ranking of a pseudonym. + * + * @param nsid id of the pseudonym + * @param delta by how much should the rating be + * changed? + * @return new rating of the pseudonym + */ +int +GNUNET_PSEUDONYM_rank (struct GNUNET_CONFIGURATION_Handle *cfg, + const GNUNET_HashCode * nsid, int delta) +{ + struct GNUNET_CONTAINER_MetaData *meta; + int ret; + int32_t ranking; + char *name; + + name = NULL; + ret = read_info (cfg, nsid, &meta, &ranking, &name); + if (ret == GNUNET_SYSERR) + { + ranking = 0; + meta = GNUNET_CONTAINER_meta_data_create (); + } + ranking += delta; + write_pseudonym_info (cfg, nsid, meta, ranking, name); + GNUNET_CONTAINER_meta_data_destroy (meta); + GNUNET_free_non_null (name); + return ranking; +} + +/** + * Insert metadata into existing MD record (passed as cls). + */ +static int +merge_meta_helper (EXTRACTOR_KeywordType type, const char *data, void *cls) +{ + struct GNUNET_CONTAINER_MetaData *meta = cls; + GNUNET_CONTAINER_meta_data_insert (meta, type, data); + return GNUNET_OK; +} + + + +/** + * Add a pseudonym to the set of known pseudonyms. + * For all pseudonym advertisements that we discover + * FSUI should automatically call this function. + * + * @param id the pseudonym identifier + */ +void +GNUNET_PSEUDONYM_add (struct GNUNET_CONFIGURATION_Handle *cfg, + const GNUNET_HashCode * id, + const struct GNUNET_CONTAINER_MetaData *meta) +{ + char *name; + int32_t ranking; + struct GNUNET_CONTAINER_MetaData *old; + char *fn; + struct stat sbuf; + + ranking = 0; + fn = get_data_filename (cfg, PS_METADATA_DIR, id); + GNUNET_assert (fn != NULL); + + if ((0 == STAT (fn, &sbuf)) && + (GNUNET_OK == read_info (cfg, id, &old, &ranking, &name))) + { + GNUNET_CONTAINER_meta_data_get_contents (meta, &merge_meta_helper, old); + write_pseudonym_info (cfg, id, old, ranking, name); + GNUNET_CONTAINER_meta_data_destroy (old); + GNUNET_free_non_null (name); + } + else + { + write_pseudonym_info (cfg, id, meta, ranking, NULL); + } + GNUNET_free (fn); + internal_notify (id, meta, ranking); +} + + + + + +/* end of pseudonym.c */ diff --git a/src/util/scheduler.c b/src/util/scheduler.c new file mode 100644 index 000000000..0d30910e0 --- /dev/null +++ b/src/util/scheduler.c @@ -0,0 +1,886 @@ +/* + This file is part of GNUnet + (C) 2009 Christian Grothoff (and other contributing authors) + + GNUnet is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published + by the Free Software Foundation; either version 2, 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 + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with GNUnet; see the file COPYING. If not, write to the + Free Software Foundation, Inc., 59 Temple Place - Suite 330, + Boston, MA 02111-1307, USA. + */ + +/** + * @file util/scheduler/scheduler.c + * @brief schedule computations using continuation passing style + * @author Christian Grothoff + */ +#include "platform.h" +#include "gnunet_common.h" +#include "gnunet_scheduler_lib.h" +#include "gnunet_signal_lib.h" +#include "gnunet_time_lib.h" + +/** + * Linked list of pending tasks. + */ +struct Task +{ + /** + * This is a linked list. + */ + struct Task *next; + + /** + * Function to run when ready. + */ + GNUNET_SCHEDULER_Task callback; + + /** + * Closure for the callback. + */ + void *callback_cls; + + /** + * Set of file descriptors this task is waiting + * for for reading. Once ready, this is updated + * to reflect the set of file descriptors ready + * for operation. + */ + fd_set read_set; + + /** + * Set of file descriptors this task is waiting + * for for writing. Once ready, this is updated + * to reflect the set of file descriptors ready + * for operation. + */ + fd_set write_set; + + /** + * Unique task identifier. + */ + GNUNET_SCHEDULER_TaskIdentifier id; + + /** + * Identifier of a prerequisite task. + */ + GNUNET_SCHEDULER_TaskIdentifier prereq_id; + + /** + * Absolute timeout value for the task, or + * GNUNET_TIME_UNIT_FOREVER_ABS for "no timeout". + */ + struct GNUNET_TIME_Absolute timeout; + + /** + * Why is the task ready? Set after task is added to ready queue. + * Initially set to zero. All reasons that have already been + * satisfied (i.e. read or write ready) will be set over time. + */ + enum GNUNET_SCHEDULER_Reason reason; + + /** + * Task priority. + */ + enum GNUNET_SCHEDULER_Priority priority; + + /** + * highest-numbered file descriptor in read_set or write_set plus one + */ + int nfds; + + /** + * Should this task be run on shutdown? + */ + int run_on_shutdown; + +}; + + +/** + * Handle for the scheduling service. + */ +struct GNUNET_SCHEDULER_Handle +{ + + /** + * List of tasks waiting for an event. + */ + struct Task *pending; + + /** + * List of tasks ready to run right now, + * grouped by importance. + */ + struct Task *ready[GNUNET_SCHEDULER_PRIORITY_COUNT]; + + /** + * Identity of the last task queued. Incremented for each task to + * generate a unique task ID (it is virtually impossible to start + * more than 2^64 tasks during the lifetime of a process). + */ + GNUNET_SCHEDULER_TaskIdentifier last_id; + + /** + * Highest number so that all tasks with smaller identifiers + * have already completed. Also the lowest number of a task + * still waiting to be executed. + */ + GNUNET_SCHEDULER_TaskIdentifier lowest_pending_id; + + /** + * GNUNET_NO if we are running normally, + * GNUNET_YES if we are in shutdown mode. + */ + int shutdown; + + /** + * Number of tasks on the ready list. + */ + unsigned int ready_count; + + /** + * Priority of the task running right now. Only + * valid while a task is running. + */ + enum GNUNET_SCHEDULER_Priority current_priority; + +}; + + +/** + * Check that the given priority is legal (and return it). + */ +static enum GNUNET_SCHEDULER_Priority +check_priority (enum GNUNET_SCHEDULER_Priority p) +{ + if ((p >= 0) && (p < GNUNET_SCHEDULER_PRIORITY_COUNT)) + return p; + GNUNET_assert (0); + return 0; /* make compiler happy */ +} + + +/** + * Update the timeout value so that it is smaller than min. + */ +static void +update_timeout (struct timeval *tv, struct GNUNET_TIME_Relative min) +{ + if (((tv->tv_sec * 1000) + (tv->tv_usec / 1000)) > min.value) + { + tv->tv_sec = min.value / 1000; + tv->tv_usec = (min.value - tv->tv_sec * 1000) * 1000; + } +} + + +/** + * Set the given file descriptor bit in the given set and update max + * to the maximum of the existing max and fd+1. + */ +static void +set_fd (int fd, int *max, fd_set * set) +{ + if (*max <= fd) + *max = fd + 1; + FD_SET (fd, set); +} + + +/** + * Is a task with this identifier still pending? Also updates + * "lowest_pending_id" as a side-effect (for faster checks in the + * future), but only if the return value is "GNUNET_NO" (and + * the "lowest_pending_id" check failed). + * + * @return GNUNET_YES if so, GNUNET_NO if not + */ +static int +is_pending (struct GNUNET_SCHEDULER_Handle *sched, + GNUNET_SCHEDULER_TaskIdentifier id) +{ + struct Task *pos; + enum GNUNET_SCHEDULER_Priority p; + GNUNET_SCHEDULER_TaskIdentifier min; + + if (id < sched->lowest_pending_id) + return GNUNET_NO; + min = -1; /* maximum value */ + pos = sched->pending; + while (pos != NULL) + { + if (pos->id == id) + return GNUNET_YES; + if (pos->id < min) + min = pos->id; + pos = pos->next; + } + for (p = 0; p < GNUNET_SCHEDULER_PRIORITY_COUNT; p++) + { + pos = sched->ready[p]; + while (pos != NULL) + { + if (pos->id == id) + return GNUNET_YES; + if (pos->id < min) + min = pos->id; + pos = pos->next; + } + } + sched->lowest_pending_id = min; + return GNUNET_NO; +} + + +/** + * Update all sets and timeout for select. + */ +static void +update_sets (struct GNUNET_SCHEDULER_Handle *sched, + int *max, fd_set * rs, fd_set * ws, struct timeval *tv) +{ + int i; + struct Task *pos; + + pos = sched->pending; + while (pos != NULL) + { + if ((pos->prereq_id != GNUNET_SCHEDULER_NO_PREREQUISITE_TASK) && + (GNUNET_YES == is_pending (sched, pos->prereq_id))) + { + pos = pos->next; + continue; + } + + if (pos->timeout.value != GNUNET_TIME_UNIT_FOREVER_ABS.value) + update_timeout (tv, + GNUNET_TIME_absolute_get_remaining (pos->timeout)); + for (i = 0; i < pos->nfds; i++) + { + if (FD_ISSET (i, &pos->read_set)) + set_fd (i, max, rs); + if (FD_ISSET (i, &pos->write_set)) + set_fd (i, max, ws); + } + pos = pos->next; + } +} + + +/** + * Check if the ready set overlaps with the set we want to have ready. + * If so, update the want set (set all FDs that are ready). If not, + * return GNUNET_NO. + * + * @param maxfd highest FD that needs to be checked. + * @return GNUNET_YES if there was some overlap + */ +static int +set_overlaps (const fd_set * ready, fd_set * want, int maxfd) +{ + int i; + + for (i = 0; i < maxfd; i++) + if (FD_ISSET (i, want) && FD_ISSET (i, ready)) + { + /* copy all over (yes, there maybe unrelated bits, + but this should not hurt well-written clients) */ + memcpy (want, ready, sizeof (fd_set)); + return GNUNET_YES; + } + return GNUNET_NO; +} + + +/** + * Check if the given task is eligible to run now. + * Also set the reason why it is eligible. + * + * @return GNUNET_YES if we can run it, GNUNET_NO if not. + */ +static int +is_ready (struct GNUNET_SCHEDULER_Handle *sched, + struct Task *task, + struct GNUNET_TIME_Absolute now, + const fd_set * rs, const fd_set * ws) +{ + if ((GNUNET_NO == task->run_on_shutdown) && (GNUNET_YES == sched->shutdown)) + return GNUNET_NO; + if ((GNUNET_YES == task->run_on_shutdown) && + (GNUNET_YES == sched->shutdown)) + task->reason |= GNUNET_SCHEDULER_REASON_SHUTDOWN; + if (now.value >= task->timeout.value) + task->reason |= GNUNET_SCHEDULER_REASON_TIMEOUT; + if ((0 == (task->reason & GNUNET_SCHEDULER_REASON_READ_READY)) && + (rs != NULL) && (set_overlaps (rs, &task->read_set, task->nfds))) + task->reason |= GNUNET_SCHEDULER_REASON_READ_READY; + if ((0 == (task->reason & GNUNET_SCHEDULER_REASON_WRITE_READY)) && + (ws != NULL) && (set_overlaps (ws, &task->write_set, task->nfds))) + task->reason |= GNUNET_SCHEDULER_REASON_WRITE_READY; + if (task->reason == 0) + return GNUNET_NO; /* not ready */ + if (task->prereq_id != GNUNET_SCHEDULER_NO_PREREQUISITE_TASK) + { + if (GNUNET_YES == is_pending (sched, task->prereq_id)) + return GNUNET_NO; /* prereq waiting */ + task->reason |= GNUNET_SCHEDULER_REASON_PREREQ_DONE; + } + return GNUNET_YES; +} + + +/** + * Put a task that is ready for execution into the ready queue. + */ +static void +queue_ready_task (struct GNUNET_SCHEDULER_Handle *handle, struct Task *task) +{ + task->next = handle->ready[check_priority (task->priority)]; + handle->ready[check_priority (task->priority)] = task; + handle->ready_count++; +} + + +/** + * Check which tasks are ready and move them + * to the respective ready queue. + */ +static void +check_ready (struct GNUNET_SCHEDULER_Handle *handle, + const fd_set * rs, const fd_set * ws) +{ + struct Task *pos; + struct Task *prev; + struct Task *next; + struct GNUNET_TIME_Absolute now; + + now = GNUNET_TIME_absolute_get (); + prev = NULL; + pos = handle->pending; + while (pos != NULL) + { + next = pos->next; + if (GNUNET_YES == is_ready (handle, pos, now, rs, ws)) + { + if (prev == NULL) + handle->pending = next; + else + prev->next = next; + queue_ready_task (handle, pos); + pos = next; + continue; + } + prev = pos; + pos = next; + } +} + + +/** + * Run at least one task in the highest-priority queue that is not + * empty. Keep running tasks until we are either no longer running + * "URGENT" tasks or until we have at least one "pending" task (which + * may become ready, hence we should select on it). Naturally, if + * there are no more ready tasks, we also return. + */ +static void +run_ready (struct GNUNET_SCHEDULER_Handle *sched) +{ + enum GNUNET_SCHEDULER_Priority p; + struct Task *pos; + struct GNUNET_SCHEDULER_TaskContext tc; + + do + { + if (sched->ready_count == 0) + return; + GNUNET_assert (sched->ready[GNUNET_SCHEDULER_PRIORITY_KEEP] == NULL); + /* yes, p>0 is correct, 0 is "KEEP" which should + always be an empty queue (see assertion)! */ + for (p = GNUNET_SCHEDULER_PRIORITY_COUNT - 1; p > 0; p--) + { + pos = sched->ready[p]; + if (pos != NULL) + break; + } + GNUNET_assert (pos != NULL); /* ready_count wrong? */ + sched->ready[p] = pos->next; + sched->ready_count--; + sched->current_priority = p; + GNUNET_assert (pos->priority == p); + tc.sched = sched; + tc.reason = pos->reason; + tc.read_ready = &pos->read_set; + tc.write_ready = &pos->write_set; + pos->callback (pos->callback_cls, &tc); + GNUNET_free (pos); + } + while ((sched->pending == NULL) || (p == GNUNET_SCHEDULER_PRIORITY_URGENT)); +} + + +/** + * Have we (ever) received a SIGINT/TERM/QUIT/HUP? + */ +static volatile int sig_shutdown; + + +/** + * Signal handler called for signals that should cause us to shutdown. + */ +static void +sighandler_shutdown () +{ + sig_shutdown = 1; +} + + +/** + * Initialize a scheduler using this thread. This function will + * return when either a shutdown was initiated (via signal) and all + * tasks marked to "run_on_shutdown" have been completed or when all + * tasks in general have been completed. + * + * @param task task to run immediately + * @param cls closure of task + */ +void +GNUNET_SCHEDULER_run (GNUNET_SCHEDULER_Task task, void *cls) +{ + struct GNUNET_SCHEDULER_Handle sched; + fd_set rs; + fd_set ws; + int max; + struct timeval tv; + int ret; + struct GNUNET_SIGNAL_Context *shc_int; + struct GNUNET_SIGNAL_Context *shc_term; + struct GNUNET_SIGNAL_Context *shc_quit; + struct GNUNET_SIGNAL_Context *shc_hup; + struct Task *tpos; + + sig_shutdown = 0; + shc_int = GNUNET_SIGNAL_handler_install (SIGINT, &sighandler_shutdown); + shc_term = GNUNET_SIGNAL_handler_install (SIGTERM, &sighandler_shutdown); + shc_quit = GNUNET_SIGNAL_handler_install (SIGQUIT, &sighandler_shutdown); + shc_hup = GNUNET_SIGNAL_handler_install (SIGHUP, &sighandler_shutdown); + memset (&sched, 0, sizeof (sched)); + sched.current_priority = GNUNET_SCHEDULER_PRIORITY_DEFAULT; + GNUNET_SCHEDULER_add_continuation (&sched, + GNUNET_YES, + task, + cls, GNUNET_SCHEDULER_REASON_STARTUP); + while ((GNUNET_NO == sched.shutdown) && + (!sig_shutdown) && + ((sched.pending != NULL) || (sched.ready_count > 0))) + { + FD_ZERO (&rs); + FD_ZERO (&ws); + max = 0; + tv.tv_sec = 0x7FFFFFFF; + tv.tv_usec = 0; + if (sched.ready_count > 0) + { + /* no blocking, more work already ready! */ + tv.tv_sec = 0; + tv.tv_usec = 0; + } + update_sets (&sched, &max, &rs, &ws, &tv); + ret = SELECT (max, &rs, &ws, NULL, &tv); + if (ret == -1) + { + if (errno == EINTR) + continue; + GNUNET_log_strerror (GNUNET_ERROR_TYPE_ERROR, "select"); + break; + } + check_ready (&sched, &rs, &ws); + run_ready (&sched); + } + if (sig_shutdown) + sched.shutdown = GNUNET_YES; + GNUNET_SIGNAL_handler_uninstall (shc_int); + GNUNET_SIGNAL_handler_uninstall (shc_term); + GNUNET_SIGNAL_handler_uninstall (shc_quit); + GNUNET_SIGNAL_handler_uninstall (shc_hup); + do + { + run_ready (&sched); + check_ready (&sched, NULL, NULL); + } + while (sched.ready_count > 0); + while (NULL != (tpos = sched.pending)) + { + sched.pending = tpos->next; + GNUNET_free (tpos); + } +} + + +/** + * Request the shutdown of a scheduler. This function can be used to + * stop a scheduling thread when created with the + * "GNUNET_SCHEDULER_init_thread" function or from within the signal + * handler for signals causing shutdowns. + */ +void +GNUNET_SCHEDULER_shutdown (struct GNUNET_SCHEDULER_Handle *sched) +{ + sched->shutdown = GNUNET_YES; +} + + +/** + * Get information about the current load of this scheduler. Use this + * function to determine if an elective task should be added or simply + * dropped (if the decision should be made based on the number of + * tasks ready to run). + * + * @param sched scheduler to query + * @return number of tasks pending right now + */ +unsigned int +GNUNET_SCHEDULER_get_load (struct GNUNET_SCHEDULER_Handle *sched, + enum GNUNET_SCHEDULER_Priority p) +{ + struct Task *pos; + unsigned int ret; + + if (p == GNUNET_SCHEDULER_PRIORITY_COUNT) + return sched->ready_count; + if (p == GNUNET_SCHEDULER_PRIORITY_KEEP) + p = sched->current_priority; + ret = 0; + pos = sched->ready[p]; + while (pos != NULL) + { + pos = pos->next; + ret++; + } + return ret; +} + + +/** + * Cancel the task with the specified identifier. + * The task must not yet have run. + * + * @param sched scheduler to use + * @param task id of the task to cancel + */ +void * +GNUNET_SCHEDULER_cancel (struct GNUNET_SCHEDULER_Handle *sched, + GNUNET_SCHEDULER_TaskIdentifier task) +{ + struct Task *t; + struct Task *prev; + enum GNUNET_SCHEDULER_Priority p; + void *ret; + + prev = NULL; + t = sched->pending; + while (t != NULL) + { + if (t->id == task) + break; + prev = t; + t = t->next; + } + p = 0; + while (t == NULL) + { + p++; + GNUNET_assert (p < GNUNET_SCHEDULER_PRIORITY_COUNT); + prev = NULL; + t = sched->ready[p]; + while (t != NULL) + { + if (t->id == task) + { + sched->ready_count--; + break; + } + prev = t; + t = t->next; + } + } + if (prev == NULL) + { + if (p == 0) + sched->pending = t->next; + else + sched->ready[p] = t->next; + } + else + prev->next = t->next; + ret = t->callback_cls; + GNUNET_free (t); + return ret; +} + + +/** + * Continue the current execution with the given function. This is + * similar to the other "add" functions except that there is no delay + * and the reason code can be specified. + * + * @param sched scheduler to use + * @param main main function of the task + * @param cls closure of task + * @param reason reason for task invocation + */ +void +GNUNET_SCHEDULER_add_continuation (struct GNUNET_SCHEDULER_Handle *sched, + int run_on_shutdown, + GNUNET_SCHEDULER_Task main, + void *cls, + enum GNUNET_SCHEDULER_Reason reason) +{ + struct Task *task; + + task = GNUNET_malloc (sizeof (struct Task)); + task->callback = main; + task->callback_cls = cls; + task->id = ++sched->last_id; + task->reason = reason; + task->priority = sched->current_priority; + task->run_on_shutdown = run_on_shutdown; + queue_ready_task (sched, task); +} + + +/** + * Schedule a new task to be run after the specified + * prerequisite task has completed. + * + * @param sched scheduler to use + * @param run_on_shutdown run on shutdown? + * @param prio how important is this task? + * @param prerequisite_task run this task after the task with the given + * task identifier completes (and any of our other + * conditions, such as delay, read or write-readyness + * are satisfied). Use GNUNET_SCHEDULER_NO_PREREQUISITE_TASK to not have any dependency + * on completion of other tasks. + * @param main main function of the task + * @param cls closure of task + * @return unique task identifier for the job + * only valid until "main" is started! + */ +GNUNET_SCHEDULER_TaskIdentifier +GNUNET_SCHEDULER_add_after (struct GNUNET_SCHEDULER_Handle *sched, + int run_on_shutdown, + enum GNUNET_SCHEDULER_Priority prio, + GNUNET_SCHEDULER_TaskIdentifier prerequisite_task, + GNUNET_SCHEDULER_Task main, void *cls) +{ + return GNUNET_SCHEDULER_add_select (sched, run_on_shutdown, prio, + prerequisite_task, + GNUNET_TIME_UNIT_ZERO, + 0, NULL, NULL, main, cls); +} + + +/** + * Schedule a new task to be run with a specified delay. The task + * will be scheduled for execution once the delay has expired and the + * prerequisite task has completed. + * + * @param sched scheduler to use + * @param run_on_shutdown run on shutdown? You can use this + * argument to run a function only during shutdown + * by setting delay to -1. Set this + * argument to GNUNET_NO to skip this task if + * the user requested process termination. + * @param prio how important is this task? + * @param prerequisite_task run this task after the task with the given + * task identifier completes (and any of our other + * conditions, such as delay, read or write-readyness + * are satisfied). Use GNUNET_SCHEDULER_NO_PREREQUISITE_TASK to not have any dependency + * on completion of other tasks. + * @param delay how long should we wait? Use GNUNET_TIME_UNIT_FOREVER_REL for "forever" + * @param main main function of the task + * @param cls closure of task + * @return unique task identifier for the job + * only valid until "main" is started! + */ +GNUNET_SCHEDULER_TaskIdentifier +GNUNET_SCHEDULER_add_delayed (struct GNUNET_SCHEDULER_Handle * sched, + int run_on_shutdown, + enum GNUNET_SCHEDULER_Priority prio, + GNUNET_SCHEDULER_TaskIdentifier + prerequisite_task, + struct GNUNET_TIME_Relative delay, + GNUNET_SCHEDULER_Task main, void *cls) +{ + return GNUNET_SCHEDULER_add_select (sched, run_on_shutdown, prio, + prerequisite_task, delay, + 0, NULL, NULL, main, cls); +} + + +/** + * Schedule a new task to be run with a specified delay or when the + * specified file descriptor is ready for reading. The delay can be + * used as a timeout on the socket being ready. The task will be + * scheduled for execution once either the delay has expired or the + * socket operation is ready. + * + * @param sched scheduler to use + * @param run_on_shutdown run on shutdown? Set this + * argument to GNUNET_NO to skip this task if + * the user requested process termination. + * @param prio how important is this task? + * @param prerequisite_task run this task after the task with the given + * task identifier completes (and any of our other + * conditions, such as delay, read or write-readyness + * are satisfied). Use GNUNET_SCHEDULER_NO_PREREQUISITE_TASK to not have any dependency + * on completion of other tasks. + * @param delay how long should we wait? Use GNUNET_TIME_UNIT_FOREVER_REL for "forever" + * @param rfd read file-descriptor + * @param main main function of the task + * @param cls closure of task + * @return unique task identifier for the job + * only valid until "main" is started! + */ +GNUNET_SCHEDULER_TaskIdentifier +GNUNET_SCHEDULER_add_read (struct GNUNET_SCHEDULER_Handle * sched, + int run_on_shutdown, + enum GNUNET_SCHEDULER_Priority prio, + GNUNET_SCHEDULER_TaskIdentifier prerequisite_task, + struct GNUNET_TIME_Relative delay, + int rfd, GNUNET_SCHEDULER_Task main, void *cls) +{ + fd_set rs; + + GNUNET_assert (rfd >= 0); + FD_ZERO (&rs); + FD_SET (rfd, &rs); + return GNUNET_SCHEDULER_add_select (sched, run_on_shutdown, prio, + prerequisite_task, delay, + rfd + 1, &rs, NULL, main, cls); +} + + +/** + * Schedule a new task to be run with a specified delay or when the + * specified file descriptor is ready for writing. The delay can be + * used as a timeout on the socket being ready. The task will be + * scheduled for execution once either the delay has expired or the + * socket operation is ready. + * + * @param sched scheduler to use + * @param run_on_shutdown run on shutdown? Set this + * argument to GNUNET_NO to skip this task if + * the user requested process termination. + * @param prio how important is this task? + * @param prerequisite_task run this task after the task with the given + * task identifier completes (and any of our other + * conditions, such as delay, read or write-readyness + * are satisfied). Use GNUNET_SCHEDULER_NO_PREREQUISITE_TASK to not have any dependency + * on completion of other tasks. + * @param delay how long should we wait? Use GNUNET_TIME_UNIT_FOREVER_REL for "forever" + * @param wfd write file-descriptor + * @param main main function of the task + * @param cls closure of task + * @return unique task identifier for the job + * only valid until "main" is started! + */ +GNUNET_SCHEDULER_TaskIdentifier +GNUNET_SCHEDULER_add_write (struct GNUNET_SCHEDULER_Handle * sched, + int run_on_shutdown, + enum GNUNET_SCHEDULER_Priority prio, + GNUNET_SCHEDULER_TaskIdentifier prerequisite_task, + struct GNUNET_TIME_Relative delay, + int wfd, GNUNET_SCHEDULER_Task main, void *cls) +{ + fd_set ws; + + GNUNET_assert (wfd >= 0); + FD_ZERO (&ws); + FD_SET (wfd, &ws); + return GNUNET_SCHEDULER_add_select (sched, run_on_shutdown, prio, + prerequisite_task, delay, + wfd + 1, NULL, &ws, main, cls); +} + + +/** + * Schedule a new task to be run with a specified delay or when any of + * the specified file descriptor sets is ready. The delay can be used + * as a timeout on the socket(s) being ready. The task will be + * scheduled for execution once either the delay has expired or any of + * the socket operations is ready. This is the most general + * function of the "add" family. Note that the "prerequisite_task" + * must be satisfied in addition to any of the other conditions. In + * other words, the task will be started when + * + * (prerequisite-run) + * && (delay-ready + * || any-rs-ready + * || any-ws-ready + * || (shutdown-active && run-on-shutdown) ) + * + * + * @param sched scheduler to use + * @param run_on_shutdown run on shutdown? Set this + * argument to GNUNET_NO to skip this task if + * the user requested process termination. + * @param prio how important is this task? + * @param prerequisite_task run this task after the task with the given + * task identifier completes (and any of our other + * conditions, such as delay, read or write-readyness + * are satisfied). Use GNUNET_SCHEDULER_NO_PREREQUISITE_TASK to not have any dependency + * on completion of other tasks. + * @param delay how long should we wait? Use GNUNET_TIME_UNIT_FOREVER_REL for "forever" + * @param nfds highest-numbered file descriptor in any of the two sets plus one + * @param rs set of file descriptors we want to read (can be NULL) + * @param ws set of file descriptors we want to write (can be NULL) + * @param main main function of the task + * @param cls closure of task + * @return unique task identifier for the job + * only valid until "main" is started! + */ +GNUNET_SCHEDULER_TaskIdentifier +GNUNET_SCHEDULER_add_select (struct GNUNET_SCHEDULER_Handle * sched, + int run_on_shutdown, + enum GNUNET_SCHEDULER_Priority prio, + GNUNET_SCHEDULER_TaskIdentifier + prerequisite_task, + struct GNUNET_TIME_Relative delay, + int nfds, const fd_set * rs, const fd_set * ws, + GNUNET_SCHEDULER_Task main, void *cls) +{ + struct Task *task; + + task = GNUNET_malloc (sizeof (struct Task)); + task->callback = main; + task->callback_cls = cls; + if ((rs != NULL) && (nfds > 0)) + memcpy (&task->read_set, rs, sizeof (fd_set)); + if ((ws != NULL) && (nfds > 0)) + memcpy (&task->write_set, ws, sizeof (fd_set)); + task->id = ++sched->last_id; + task->prereq_id = prerequisite_task; + task->timeout = GNUNET_TIME_relative_to_absolute (delay); + task->priority = + check_priority ((prio == + GNUNET_SCHEDULER_PRIORITY_KEEP) ? sched-> + current_priority : prio); + task->nfds = nfds; + task->run_on_shutdown = run_on_shutdown; + task->next = sched->pending; + sched->pending = task; + return task->id; +} + +/* end of scheduler.c */ diff --git a/src/util/server.c b/src/util/server.c new file mode 100644 index 000000000..91bc8cc7a --- /dev/null +++ b/src/util/server.c @@ -0,0 +1,1091 @@ +/* + This file is part of GNUnet. + (C) 2009 Christian Grothoff (and other contributing authors) + + GNUnet is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published + by the Free Software Foundation; either version 2, 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 + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with GNUnet; see the file COPYING. If not, write to the + Free Software Foundation, Inc., 59 Temple Place - Suite 330, + Boston, MA 02111-1307, USA. +*/ + +/** + * @file util/server.c + * @brief library for building GNUnet network servers + * @author Christian Grothoff + * + * TODO: + * - fix inefficient memmove in message processing + */ + +#include "platform.h" +#include "gnunet_common.h" +#include "gnunet_network_lib.h" +#include "gnunet_scheduler_lib.h" +#include "gnunet_server_lib.h" +#include "gnunet_time_lib.h" + +/** + * List of arrays of message handlers. + */ +struct HandlerList +{ + /** + * This is a linked list. + */ + struct HandlerList *next; + + /** + * NULL-terminated array of handlers. + */ + const struct GNUNET_SERVER_MessageHandler *handlers; +}; + + +/** + * List of arrays of message handlers. + */ +struct NotifyList +{ + /** + * This is a linked list. + */ + struct NotifyList *next; + + /** + * Function to call. + */ + GNUNET_SERVER_DisconnectCallback callback; + + /** + * Closure for callback. + */ + void *callback_cls; +}; + + +/** + * @brief handle for a server + */ +struct GNUNET_SERVER_Handle +{ + /** + * My scheduler. + */ + struct GNUNET_SCHEDULER_Handle *sched; + + /** + * List of handlers for incoming messages. + */ + struct HandlerList *handlers; + + /** + * List of our current clients. + */ + struct GNUNET_SERVER_Client *clients; + + /** + * Linked list of functions to call on disconnects by clients. + */ + struct NotifyList *disconnect_notify_list; + + /** + * Function to call for access control. + */ + GNUNET_NETWORK_AccessCheck access; + + /** + * Closure for access. + */ + void *access_cls; + + /** + * After how long should an idle connection time + * out (on write). + */ + struct GNUNET_TIME_Relative idle_timeout; + + /** + * maximum write buffer size for accepted sockets + */ + size_t maxbuf; + + /** + * Pipe used to signal shutdown of the server. + */ + int shutpipe[2]; + + /** + * Socket used to listen for new connections. Set to + * "-1" by GNUNET_SERVER_destroy to initiate shutdown. + */ + int listen_socket; + + /** + * Set to GNUNET_YES if we are shutting down. + */ + int do_shutdown; + + /** + * Do we ignore messages of types that we do not + * understand or do we require that a handler + * is found (and if not kill the connection)? + */ + int require_found; + +}; + + +/** + * @brief handle for a client of the server + */ +struct GNUNET_SERVER_Client +{ + + /** + * Size of the buffer for incoming data. Should be + * first so we get nice alignment. + */ + char incoming_buffer[GNUNET_SERVER_MAX_MESSAGE_SIZE]; + + /** + * This is a linked list. + */ + struct GNUNET_SERVER_Client *next; + + /** + * Server that this client belongs to. + */ + struct GNUNET_SERVER_Handle *server; + + /** + * Client closure for callbacks. + */ + void *client_closure; + + /** + * Callback to receive from client. + */ + GNUNET_SERVER_ReceiveCallback receive; + + /** + * Callback to cancel receive from client. + */ + GNUNET_SERVER_ReceiveCancelCallback receive_cancel; + + /** + * Callback to ask about transmit-ready notification. + */ + GNUNET_SERVER_TransmitReadyCallback notify_transmit_ready; + + /** + * Callback to ask about transmit-ready notification. + */ + GNUNET_SERVER_TransmitReadyCancelCallback notify_transmit_ready_cancel; + + /** + * Callback to check if client is still valid. + */ + GNUNET_SERVER_CheckCallback check; + + /** + * Callback to destroy client. + */ + GNUNET_SERVER_DestroyCallback destroy; + + /** + * Side-buffer for incoming data used when processing + * is suspended. + */ + char *side_buf; + + /** + * Number of bytes in the side buffer. + */ + size_t side_buf_size; + + /** + * Last activity on this socket (used to time it out + * if reference_count == 0). + */ + struct GNUNET_TIME_Absolute last_activity; + + /** + * Current task identifier for the receive call + * (or GNUNET_SCHEDULER_NO_PREREQUISITE_TASK for none). + */ + GNUNET_SCHEDULER_TaskIdentifier my_receive; + + /** + * How many bytes in the "incoming_buffer" are currently + * valid? (starting at offset 0). + */ + size_t receive_pos; + + /** + * Number of external entities with a reference to + * this client object. + */ + unsigned int reference_count; + + /** + * Was processing if incoming messages suspended while + * we were still processing data already received? + * This is a counter saying how often processing was + * suspended (once per handler invoked). + */ + unsigned int suspended; + + /** + * Are we currently in the "process_client_buffer" function (and + * will hence restart the receive job on exit if suspended == 0 once + * we are done?). If this is set, then "receive_done" will + * essentially only decrement suspended; if this is not set, then + * "receive_done" may need to restart the receive process (either + * from the side-buffer or via select/recv). + */ + int in_process_client_buffer; + + /** + * We're about to close down this client due to some serious + * error. + */ + int shutdown_now; + +}; + + +/** + * Server has been asked to shutdown, free resources. + */ +static void +destroy_server (struct GNUNET_SERVER_Handle *server) +{ + struct GNUNET_SERVER_Client *pos; + struct HandlerList *hpos; + struct NotifyList *npos; + + GNUNET_assert (server->listen_socket == -1); + GNUNET_break (0 == CLOSE (server->shutpipe[0])); + GNUNET_break (0 == CLOSE (server->shutpipe[1])); + while (server->clients != NULL) + { + pos = server->clients; + server->clients = pos->next; + pos->server = NULL; + } + while (NULL != (hpos = server->handlers)) + { + server->handlers = hpos->next; + GNUNET_free (hpos); + } + while (NULL != (npos = server->disconnect_notify_list)) + { + server->disconnect_notify_list = npos->next; + GNUNET_free (npos); + } + GNUNET_free (server); +} + + +/** + * Scheduler says our listen socket is ready. + * Process it! + */ +static void +process_listen_socket (void *cls, + const struct GNUNET_SCHEDULER_TaskContext *tc) +{ + struct GNUNET_SERVER_Handle *server = cls; + struct GNUNET_NETWORK_SocketHandle *sock; + struct GNUNET_SERVER_Client *client; + fd_set r; + + if ((server->do_shutdown) || + ((tc->reason & GNUNET_SCHEDULER_REASON_SHUTDOWN) != 0)) + { + /* shutdown was initiated */ + GNUNET_assert (server->listen_socket != -1); + GNUNET_break (0 == CLOSE (server->listen_socket)); + server->listen_socket = -1; + if (server->do_shutdown) + destroy_server (server); + return; + } + GNUNET_assert (FD_ISSET (server->listen_socket, tc->read_ready)); + GNUNET_assert (!FD_ISSET (server->shutpipe[0], tc->read_ready)); + sock = GNUNET_NETWORK_socket_create_from_accept (tc->sched, + server->access, + server->access_cls, + server->listen_socket, + server->maxbuf); + if (sock != NULL) + { + client = GNUNET_SERVER_connect_socket (server, sock); + /* decrement reference count, we don't keep "client" alive */ + GNUNET_SERVER_client_drop (client); + } + /* listen for more! */ + FD_ZERO (&r); + FD_SET (server->listen_socket, &r); + FD_SET (server->shutpipe[0], &r); + GNUNET_SCHEDULER_add_select (server->sched, + GNUNET_YES, + GNUNET_SCHEDULER_PRIORITY_HIGH, + GNUNET_SCHEDULER_NO_PREREQUISITE_TASK, + GNUNET_TIME_UNIT_FOREVER_REL, + GNUNET_MAX (server->listen_socket, + server->shutpipe[0]) + 1, &r, NULL, + &process_listen_socket, server); +} + + +/** + * Create and initialize a listen socket for the server. + * + * @return -1 on error, otherwise the listen socket + */ +static int +open_listen_socket (const struct sockaddr *serverAddr, socklen_t socklen) +{ + const static int on = 1; + int fd; + uint16_t port; + + switch (serverAddr->sa_family) + { + case AF_INET: + port = ntohs (((const struct sockaddr_in *) serverAddr)->sin_port); + break; + case AF_INET6: + port = ntohs (((const struct sockaddr_in6 *) serverAddr)->sin6_port); + break; + default: + GNUNET_break (0); + return -1; + } + fd = SOCKET (serverAddr->sa_family, SOCK_STREAM, 0); + if (fd < 0) + { + GNUNET_log_strerror (GNUNET_ERROR_TYPE_ERROR, "socket"); + return -1; + } + if (0 != fcntl (fd, F_SETFD, fcntl (fd, F_GETFD) | FD_CLOEXEC)) + GNUNET_log_strerror (GNUNET_ERROR_TYPE_ERROR | GNUNET_ERROR_TYPE_BULK, + "fcntl"); + if (SETSOCKOPT (fd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof (on)) < 0) + GNUNET_log_strerror (GNUNET_ERROR_TYPE_ERROR | GNUNET_ERROR_TYPE_BULK, + "setsockopt"); + /* bind the socket */ + if (BIND (fd, serverAddr, socklen) < 0) + { + GNUNET_log_strerror (GNUNET_ERROR_TYPE_ERROR, "bind"); + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + _ + ("`%s' failed for port %d. Is the service already running?\n"), + "bind", port); + GNUNET_break (0 == CLOSE (fd)); + return -1; + } + if (0 != LISTEN (fd, 5)) + { + GNUNET_log_strerror (GNUNET_ERROR_TYPE_ERROR, "listen"); + GNUNET_break (0 == CLOSE (fd)); + return -1; + } + return fd; +} + + +/** + * Create a new server. + * + * @param sched scheduler to use + * @param access function for access control + * @param access_cls closure for access + * @param serverAddr address to listen on (including port), use NULL + * for internal server (no listening) + * @param socklen length of serverAddr + * @param maxbuf maximum write buffer size for accepted sockets + * @param idle_timeout after how long should we timeout idle connections? + * @param require_found if YES, connections sending messages of unknown type + * will be closed + * @return handle for the new server, NULL on error + * (typically, "port" already in use) + */ +struct GNUNET_SERVER_Handle * +GNUNET_SERVER_create (struct GNUNET_SCHEDULER_Handle *sched, + GNUNET_NETWORK_AccessCheck access, + void *access_cls, + const struct sockaddr *serverAddr, + socklen_t socklen, + size_t maxbuf, + struct GNUNET_TIME_Relative + idle_timeout, int require_found) +{ + struct GNUNET_SERVER_Handle *ret; + int lsock; + fd_set r; + + lsock = -2; + if (serverAddr != NULL) + { + lsock = open_listen_socket (serverAddr, socklen); + if (lsock == -1) + return NULL; + } + ret = GNUNET_malloc (sizeof (struct GNUNET_SERVER_Handle)); + if (0 != PIPE (ret->shutpipe)) + { + GNUNET_break (0 == CLOSE (lsock)); + GNUNET_free (ret); + return NULL; + } + ret->sched = sched; + ret->maxbuf = maxbuf; + ret->idle_timeout = idle_timeout; + ret->listen_socket = lsock; + ret->access = access; + ret->access_cls = access_cls; + ret->require_found = require_found; + if (lsock >= 0) + { + FD_ZERO (&r); + FD_SET (ret->listen_socket, &r); + FD_SET (ret->shutpipe[0], &r); + GNUNET_SCHEDULER_add_select (sched, + GNUNET_YES, + GNUNET_SCHEDULER_PRIORITY_HIGH, + GNUNET_SCHEDULER_NO_PREREQUISITE_TASK, + GNUNET_TIME_UNIT_FOREVER_REL, + GNUNET_MAX (ret->listen_socket, + ret->shutpipe[0]) + 1, &r, + NULL, &process_listen_socket, ret); + } + return ret; +} + + +/** + * Free resources held by this server. + */ +void +GNUNET_SERVER_destroy (struct GNUNET_SERVER_Handle *s) +{ + static char c; + + GNUNET_assert (s->do_shutdown == GNUNET_NO); + s->do_shutdown = GNUNET_YES; + if (s->listen_socket == -1) + destroy_server (s); + else + GNUNET_break (1 == WRITE (s->shutpipe[1], &c, 1)); +} + + +/** + * Add additional handlers to an existing server. + * + * @param server the server to add handlers to + * @param handlers array of message handlers for + * incoming messages; the last entry must + * have "NULL" for the "callback"; multiple + * entries for the same type are allowed, + * they will be called in order of occurence. + * These handlers can be removed later; + * the handlers array must exist until removed + * (or server is destroyed). + */ +void +GNUNET_SERVER_add_handlers (struct GNUNET_SERVER_Handle *server, + const struct GNUNET_SERVER_MessageHandler + *handlers) +{ + struct HandlerList *p; + + p = GNUNET_malloc (sizeof (struct HandlerList)); + p->handlers = handlers; + p->next = server->handlers; + server->handlers = p; +} + + +/** + * Inject a message into the server, pretend it came + * from the specified client. Delivery of the message + * will happen instantly (if a handler is installed; + * otherwise the call does nothing). + * + * @param server the server receiving the message + * @param sender the "pretended" sender of the message + * can be NULL! + * @param message message to transmit + * @return GNUNET_OK if the message was OK and the + * connection can stay open + * GNUNET_SYSERR if the connection to the + * client should be shut down + */ +int +GNUNET_SERVER_inject (struct GNUNET_SERVER_Handle *server, + struct GNUNET_SERVER_Client *sender, + const struct GNUNET_MessageHeader *message) +{ + struct HandlerList *pos; + const struct GNUNET_SERVER_MessageHandler *mh; + unsigned int i; + uint16_t type; + uint16_t size; + int found; + + type = ntohs (message->type); + size = ntohs (message->size); + pos = server->handlers; + found = GNUNET_NO; + while (pos != NULL) + { + i = 0; + while (pos->handlers[i].callback != NULL) + { + mh = &pos->handlers[i]; + if (mh->type == type) + { + if ((mh->expected_size != 0) && (mh->expected_size != size)) + { + GNUNET_break_op (0); + return GNUNET_SYSERR; + } + if (sender != NULL) + sender->suspended++; + mh->callback (mh->callback_cls, server, sender, message); + found = GNUNET_YES; + } + i++; + } + pos = pos->next; + } + if (found == GNUNET_NO) + { + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG | GNUNET_ERROR_TYPE_BULK, + _("Received message of unknown type %d\n"), type); + if (server->require_found == GNUNET_YES) + return GNUNET_SYSERR; + } + return GNUNET_OK; +} + + +/** + * We're finished with this client and especially its input + * processing. If the RC is zero, free all resources otherwise wait + * until RC hits zero to do so. + */ +static void +shutdown_incoming_processing (struct GNUNET_SERVER_Client *client) +{ + struct GNUNET_SERVER_Client *prev; + struct GNUNET_SERVER_Client *pos; + struct GNUNET_SERVER_Handle *server; + struct NotifyList *n; + unsigned int rc; + + GNUNET_assert (client->my_receive == GNUNET_SCHEDULER_NO_PREREQUISITE_TASK); + rc = client->reference_count; + if (client->server != NULL) + { + server = client->server; + client->server = NULL; + prev = NULL; + pos = server->clients; + while ((pos != NULL) && (pos != client)) + { + prev = pos; + pos = pos->next; + } + GNUNET_assert (pos != NULL); + if (prev == NULL) + server->clients = pos->next; + else + prev->next = pos->next; + n = server->disconnect_notify_list; + while (n != NULL) + { + n->callback (n->callback_cls, client); + n = n->next; + } + } + /* wait for RC to hit zero, then free */ + if (rc > 0) + return; + client->destroy (client->client_closure); + GNUNET_free (client); +} + + +static void +process_client_buffer (struct GNUNET_SERVER_Client *client) +{ + struct GNUNET_SERVER_Handle *server; + const struct GNUNET_MessageHeader *hdr; + size_t msize; + + client->in_process_client_buffer = GNUNET_YES; + server = client->server; + while ((client->receive_pos >= sizeof (struct GNUNET_MessageHeader)) && + (0 == client->suspended) && (GNUNET_YES != client->shutdown_now)) + { + hdr = (const struct GNUNET_MessageHeader *) &client->incoming_buffer; + msize = ntohs (hdr->size); + if (msize > client->receive_pos) + break; + if ((msize < sizeof (struct GNUNET_MessageHeader)) || + (GNUNET_OK != GNUNET_SERVER_inject (server, client, hdr))) + { + client->in_process_client_buffer = GNUNET_NO; + shutdown_incoming_processing (client); + return; + } + /* FIXME: this is highly inefficient; we should + try to avoid this if the new base address is + already nicely aligned. See old handler code... */ + memmove (client->incoming_buffer, + &client->incoming_buffer[msize], client->receive_pos - msize); + client->receive_pos -= msize; + } + client->in_process_client_buffer = GNUNET_NO; + if (GNUNET_YES == client->shutdown_now) + shutdown_incoming_processing (client); +} + + +/** + * We are receiving an incoming message. Process it. + */ +static void +process_incoming (void *cls, + const void *buf, + size_t available, + const struct sockaddr *addr, socklen_t addrlen, int errCode) +{ + struct GNUNET_SERVER_Client *client = cls; + struct GNUNET_SERVER_Handle *server = client->server; + const char *cbuf = buf; + size_t maxcpy; + + client->my_receive = GNUNET_SCHEDULER_NO_PREREQUISITE_TASK; + if ((buf == NULL) || + (available == 0) || + (errCode != 0) || + (server == NULL) || + (client->shutdown_now == GNUNET_YES) || + (GNUNET_YES != client->check (client->client_closure))) + { + /* other side closed connection, error connecting, etc. */ + shutdown_incoming_processing (client); + return; + } + GNUNET_SERVER_client_keep (client); + client->last_activity = GNUNET_TIME_absolute_get (); + /* process data (if available) */ + while (available > 0) + { + maxcpy = available; + if (maxcpy > sizeof (client->incoming_buffer) - client->receive_pos) + maxcpy = sizeof (client->incoming_buffer) - client->receive_pos; + memcpy (&client->incoming_buffer[client->receive_pos], cbuf, maxcpy); + client->receive_pos += maxcpy; + cbuf += maxcpy; + available -= maxcpy; + if (0 < client->suspended) + { + if (available > 0) + { + client->side_buf_size = available; + client->side_buf = GNUNET_malloc (available); + memcpy (client->side_buf, cbuf, available); + available = 0; + } + break; /* do not run next client iteration! */ + } + process_client_buffer (client); + } + GNUNET_assert (available == 0); + if ((client->suspended == 0) && + (GNUNET_YES != client->shutdown_now) && (client->server != NULL)) + { + /* Finally, keep receiving! */ + client->my_receive = client->receive (client->client_closure, + GNUNET_SERVER_MAX_MESSAGE_SIZE, + server->idle_timeout, + &process_incoming, client); + } + if (GNUNET_YES == client->shutdown_now) + shutdown_incoming_processing (client); + GNUNET_SERVER_client_drop (client); +} + + +static void +restart_processing (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc) +{ + struct GNUNET_SERVER_Client *client = cls; + + process_client_buffer (client); + if (0 == client->suspended) + client->my_receive = client->receive (client->client_closure, + GNUNET_SERVER_MAX_MESSAGE_SIZE, + client->server->idle_timeout, + &process_incoming, client); +} + + +/** + * Add a client to the set of our clients and + * start receiving. + */ +static void +add_client (struct GNUNET_SERVER_Handle *server, + struct GNUNET_SERVER_Client *client) +{ + client->server = server; + client->last_activity = GNUNET_TIME_absolute_get (); + client->next = server->clients; + server->clients = client; + client->my_receive = client->receive (client->client_closure, + GNUNET_SERVER_MAX_MESSAGE_SIZE, + server->idle_timeout, + &process_incoming, client); +} + +static GNUNET_SCHEDULER_TaskIdentifier +sock_receive (void *cls, + size_t max, + struct GNUNET_TIME_Relative timeout, + GNUNET_NETWORK_Receiver receiver, void *receiver_cls) +{ + return GNUNET_NETWORK_receive (cls, max, timeout, receiver, receiver_cls); +} + +static void +sock_receive_cancel (void *cls, GNUNET_SCHEDULER_TaskIdentifier ti) +{ + GNUNET_NETWORK_receive_cancel (cls, ti); +} + + +static void * +sock_notify_transmit_ready (void *cls, + size_t size, + struct GNUNET_TIME_Relative timeout, + GNUNET_NETWORK_TransmitReadyNotify notify, + void *notify_cls) +{ + return GNUNET_NETWORK_notify_transmit_ready (cls, size, timeout, notify, + notify_cls); +} + + +static void +sock_notify_transmit_ready_cancel (void *cls, void *h) +{ + GNUNET_NETWORK_notify_transmit_ready_cancel (h); +} + + +/** + * Check if socket is still valid (no fatal errors have happened so far). + * + * @param cls the socket + * @return GNUNET_YES if valid, GNUNET_NO otherwise + */ +static int +sock_check (void *cls) +{ + return GNUNET_NETWORK_socket_check (cls); +} + + +/** + * Destroy this socket (free resources). + * + * @param cls the socket + */ +static void +sock_destroy (void *cls) +{ + GNUNET_NETWORK_socket_destroy (cls); +} + + +/** + * Add a TCP socket-based connection to the set of handles managed by + * this server. Use this function for outgoing (P2P) connections that + * we initiated (and where this server should process incoming + * messages). + * + * @param server the server to use + * @param connection the connection to manage (client must + * stop using this connection from now on) + * @return the client handle (client should call + * "client_drop" on the return value eventually) + */ +struct GNUNET_SERVER_Client * +GNUNET_SERVER_connect_socket (struct + GNUNET_SERVER_Handle + *server, + struct GNUNET_NETWORK_SocketHandle *connection) +{ + struct GNUNET_SERVER_Client *client; + + client = GNUNET_malloc (sizeof (struct GNUNET_SERVER_Client)); + client->client_closure = connection; + client->receive = &sock_receive; + client->receive_cancel = &sock_receive_cancel; + client->notify_transmit_ready = &sock_notify_transmit_ready; + client->notify_transmit_ready_cancel = &sock_notify_transmit_ready_cancel; + client->check = &sock_check; + client->destroy = &sock_destroy; + client->reference_count = 1; + add_client (server, client); + return client; +} + + +/** + * Add an arbitrary connection to the set of handles managed by this + * server. This can be used if a sending and receiving does not + * really go over the network (internal transmission) or for servers + * using UDP. + * + * @param server the server to use + * @param chandle opaque handle for the connection + * @param creceive receive function for the connection + * @param ccancel cancel receive function for the connection + * @param cnotify transmit notification function for the connection + * @param cnotify_cancel transmit notification cancellation function for the connection + * @param ccheck function to test if the connection is still up + * @param cdestroy function to close and free the connection + * @return the client handle (client should call + * "client_drop" on the return value eventually) + */ +struct GNUNET_SERVER_Client * +GNUNET_SERVER_connect_callback (struct + GNUNET_SERVER_Handle + *server, + void *chandle, + GNUNET_SERVER_ReceiveCallback + creceive, + GNUNET_SERVER_ReceiveCancelCallback + ccancel, + GNUNET_SERVER_TransmitReadyCallback + cnotify, + GNUNET_SERVER_TransmitReadyCancelCallback + cnotify_cancel, + GNUNET_SERVER_CheckCallback + ccheck, + GNUNET_SERVER_DestroyCallback cdestroy) +{ + struct GNUNET_SERVER_Client *client; + + client = GNUNET_malloc (sizeof (struct GNUNET_SERVER_Client)); + client->client_closure = chandle; + client->receive = creceive; + client->receive_cancel = ccancel; + client->notify_transmit_ready = cnotify; + client->notify_transmit_ready_cancel = cnotify_cancel; + client->check = ccheck; + client->destroy = cdestroy; + client->reference_count = 1; + add_client (server, client); + return client; +} + + +/** + * Notify the server that the given client handle should + * be kept (keeps the connection up if possible, increments + * the internal reference counter). + * + * @param client the client to keep + */ +void +GNUNET_SERVER_client_keep (struct GNUNET_SERVER_Client *client) +{ + client->reference_count++; +} + + +/** + * Notify the server that the given client handle is no + * longer required. Decrements the reference counter. If + * that counter reaches zero an inactive connection maybe + * closed. + * + * @param client the client to drop + */ +void +GNUNET_SERVER_client_drop (struct GNUNET_SERVER_Client *client) +{ + GNUNET_assert (client->reference_count > 0); + client->reference_count--; + if ((client->server == NULL) && (client->reference_count == 0)) + shutdown_incoming_processing (client); +} + + +/** + * Obtain the network address of the other party. + * + * @param client the client to get the address for + * @param addr where to store the address + * @param addrlen where to store the length of the address + * @return GNUNET_OK on success + */ +int +GNUNET_SERVER_client_get_address (struct GNUNET_SERVER_Client *client, + void **addr, size_t * addrlen) +{ + if (client->receive != &sock_receive) + return GNUNET_SYSERR; /* not a network client */ + return GNUNET_NETWORK_socket_get_address (client->client_closure, + addr, addrlen); +} + + +/** + * Ask the server to notify us whenever a client disconnects. + * This function is called whenever the actual network connection + * is closed; the reference count may be zero or larger than zero + * at this point. + * + * @param server the server manageing the clients + * @param callback function to call on disconnect + * @param callback_cls closure for callback + */ +void +GNUNET_SERVER_disconnect_notify (struct GNUNET_SERVER_Handle *server, + GNUNET_SERVER_DisconnectCallback callback, + void *callback_cls) +{ + struct NotifyList *n; + + n = GNUNET_malloc (sizeof (struct NotifyList)); + n->callback = callback; + n->callback_cls = callback_cls; + n->next = server->disconnect_notify_list; + server->disconnect_notify_list = n; +} + + +/** + * Ask the server to disconnect from the given client. + * This is the same as returning GNUNET_SYSERR from a message + * handler, except that it allows dropping of a client even + * when not handling a message from that client. + * + * @param client the client to disconnect from + */ +void +GNUNET_SERVER_client_disconnect (struct GNUNET_SERVER_Client *client) +{ + if (client->server == NULL) + return; /* already disconnected */ + GNUNET_assert (client->my_receive != GNUNET_SCHEDULER_NO_PREREQUISITE_TASK); + client->receive_cancel (client->client_closure, client->my_receive); + client->my_receive = GNUNET_SCHEDULER_NO_PREREQUISITE_TASK; + shutdown_incoming_processing (client); +} + + +/** + * Notify us when the server has enough space to transmit + * a message of the given size to the given client. + * + * @param server the server to use + * @param client client to transmit message to + * @param size requested amount of buffer space + * @param timeout after how long should we give up (and call + * notify with buf NULL and size 0)? + * @param callback function to call when space is available + * @param callback_cls closure for callback + * @return non-NULL if the notify callback was queued; can be used + * to cancel the request using + * GNUNET_NETWORK_notify_transmit_ready_cancel. + * NULL if we are already going to notify someone else (busy) + */ +struct GNUNET_NETWORK_TransmitHandle * +GNUNET_SERVER_notify_transmit_ready (struct GNUNET_SERVER_Client *client, + size_t size, + struct GNUNET_TIME_Relative timeout, + GNUNET_NETWORK_TransmitReadyNotify + callback, void *callback_cls) +{ + return client->notify_transmit_ready (client->client_closure, + size, + timeout, callback, callback_cls); +} + + +/** + * Resume receiving from this client, we are done processing the + * current request. This function must be called from within each + * GNUNET_SERVER_MessageCallback (or its respective continuations). + * + * @param client client we were processing a message of + * @param success GNUNET_OK to keep the connection open and + * continue to receive + * GNUNET_SYSERR to close the connection (signal + * serious error) + */ +void +GNUNET_SERVER_receive_done (struct GNUNET_SERVER_Client *client, int success) +{ + char *sb; + + if (client == NULL) + return; + GNUNET_assert (client->suspended > 0); + client->suspended--; + if (success != GNUNET_OK) + client->shutdown_now = GNUNET_YES; + if (client->suspended > 0) + return; + if (client->in_process_client_buffer == GNUNET_YES) + return; + if (client->side_buf_size > 0) + { + /* resume processing from side-buf */ + sb = client->side_buf; + client->side_buf = NULL; + /* this will also resume the receive job */ + if (GNUNET_YES != client->shutdown_now) + process_incoming (client, sb, client->side_buf_size, NULL, 0, 0); + else + shutdown_incoming_processing (client); + /* finally, free the side-buf */ + GNUNET_free (sb); + return; + } + /* resume receive job */ + if (GNUNET_YES != client->shutdown_now) + { + GNUNET_SCHEDULER_add_continuation (client->server->sched, + GNUNET_NO, + &restart_processing, + client, + GNUNET_SCHEDULER_REASON_PREREQ_DONE); + return; + } + shutdown_incoming_processing (client); +} + + +/* end of server.c */ diff --git a/src/util/server_tc.c b/src/util/server_tc.c new file mode 100644 index 000000000..dc51e1433 --- /dev/null +++ b/src/util/server_tc.c @@ -0,0 +1,198 @@ +/* + This file is part of GNUnet. + (C) 2009 Christian Grothoff (and other contributing authors) + + GNUnet is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published + by the Free Software Foundation; either version 2, 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 + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with GNUnet; see the file COPYING. If not, write to the + Free Software Foundation, Inc., 59 Temple Place - Suite 330, + Boston, MA 02111-1307, USA. +*/ + +/** + * @file util/server_tc.c + * @brief convenience functions for transmission of + * complex responses as a server + * @author Christian Grothoff + */ + +#include "platform.h" +#include "gnunet_common.h" +#include "gnunet_network_lib.h" +#include "gnunet_scheduler_lib.h" +#include "gnunet_server_lib.h" +#include "gnunet_time_lib.h" + + + +/** + * How much buffer space do we want to have at least + * before transmitting another increment? + */ +#define MIN_BLOCK_SIZE 128 + + + +struct GNUNET_SERVER_TransmitContext +{ + /** + * Which client are we transmitting to? + */ + struct GNUNET_SERVER_Client *client; + + /** + * Transmission buffer. (current offset for writing). + */ + char *buf; + + /** + * Number of bytes in buf. + */ + size_t total; + + /** + * Offset for writing in buf. + */ + size_t off; + + /** + * Timeout for this request. + */ + struct GNUNET_TIME_Absolute timeout; +}; + + +/** + * Helper function for incremental transmission of the response. + */ +static size_t +transmit_response (void *cls, size_t size, void *buf) +{ + struct GNUNET_SERVER_TransmitContext *tc = cls; + size_t msize; + if (buf == NULL) + { + GNUNET_SERVER_receive_done (tc->client, GNUNET_SYSERR); + GNUNET_free_non_null (tc->buf); + GNUNET_free (tc); + return 0; + } + if (tc->total - tc->off > size) + msize = size; + else + msize = tc->total - tc->off; + memcpy (buf, &tc->buf[tc->off], msize); + tc->off += msize; + if (tc->total == tc->off) + { + GNUNET_SERVER_receive_done (tc->client, GNUNET_OK); + GNUNET_free_non_null (tc->buf); + GNUNET_free (tc); + } + else + { + if (NULL == GNUNET_SERVER_notify_transmit_ready (tc->client, + GNUNET_MIN + (MIN_BLOCK_SIZE, + tc->total - tc->off), + GNUNET_TIME_absolute_get_remaining + (tc->timeout), + &transmit_response, + tc)) + { + GNUNET_break (0); + GNUNET_SERVER_receive_done (tc->client, GNUNET_SYSERR); + GNUNET_free_non_null (tc->buf); + GNUNET_free (tc); + } + } + return msize; +} + + +/** + * Create a new transmission context for the + * given client. + * + * @param client client to create the context for. + * @return NULL on error + */ +struct GNUNET_SERVER_TransmitContext * +GNUNET_SERVER_transmit_context_create (struct GNUNET_SERVER_Client *client) +{ + struct GNUNET_SERVER_TransmitContext *tc; + + GNUNET_assert (client != NULL); + tc = GNUNET_malloc (sizeof (struct GNUNET_SERVER_TransmitContext)); + tc->client = client; + return tc; +} + + +/** + * Append a message to the transmission context. + * All messages in the context will be sent by + * the transmit_context_run method. + * + * @param tc context to use + * @param data what to append to the result message + * @param length length of data + * @param type type of the message + */ +void +GNUNET_SERVER_transmit_context_append (struct GNUNET_SERVER_TransmitContext + *tc, const void *data, size_t length, + uint16_t type) +{ + struct GNUNET_MessageHeader *msg; + size_t size; + + GNUNET_assert (length < GNUNET_SERVER_MAX_MESSAGE_SIZE); + size = length + sizeof (struct GNUNET_MessageHeader); + GNUNET_assert (size > length); + tc->buf = GNUNET_realloc (tc->buf, tc->total + size); + msg = (struct GNUNET_MessageHeader *) &tc->buf[tc->total]; + tc->total += size; + msg->size = htons (size); + msg->type = htons (type); + memcpy (&msg[1], data, length); +} + + +/** + * Execute a transmission context. If there is + * an error in the transmission, the receive_done + * method will be called with an error code (GNUNET_SYSERR), + * otherwise with GNUNET_OK. + * + * @param tc transmission context to use + * @param timeout when to time out and abort the transmission + */ +void +GNUNET_SERVER_transmit_context_run (struct GNUNET_SERVER_TransmitContext *tc, + struct GNUNET_TIME_Relative timeout) +{ + tc->timeout = GNUNET_TIME_relative_to_absolute (timeout); + if (NULL == + GNUNET_SERVER_notify_transmit_ready (tc->client, + GNUNET_MIN (MIN_BLOCK_SIZE, + tc->total), timeout, + &transmit_response, tc)) + { + GNUNET_break (0); + GNUNET_SERVER_receive_done (tc->client, GNUNET_SYSERR); + GNUNET_free_non_null (tc->buf); + GNUNET_free (tc); + } +} + +/* end of server_tc.c */ diff --git a/src/util/service.c b/src/util/service.c new file mode 100644 index 000000000..af71db692 --- /dev/null +++ b/src/util/service.c @@ -0,0 +1,1451 @@ +/* + This file is part of GNUnet. + (C) 2009 Christian Grothoff (and other contributing authors) + + GNUnet is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published + by the Free Software Foundation; either version 2, 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 + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with GNUnet; see the file COPYING. If not, write to the + Free Software Foundation, Inc., 59 Temple Place - Suite 330, + Boston, MA 02111-1307, USA. +*/ + +/** + * @file util/service.c + * @brief functions related to starting services + * @author Christian Grothoff + */ +#include "platform.h" +#include "gnunet_common.h" +#include "gnunet_configuration_lib.h" +#include "gnunet_crypto_lib.h" +#include "gnunet_directories.h" +#include "gnunet_disk_lib.h" +#include "gnunet_getopt_lib.h" +#include "gnunet_os_lib.h" +#include "gnunet_protocols.h" +#include "gnunet_server_lib.h" +#include "gnunet_service_lib.h" + +/* ******************* access control ******************** */ + +/** + * @brief IPV4 network in CIDR notation. + */ +struct IPv4NetworkSet +{ + struct in_addr network; + struct in_addr netmask; +}; + +/** + * @brief network in CIDR notation for IPV6. + */ +struct IPv6NetworkSet +{ + struct in6_addr network; + struct in6_addr netmask; +}; + + +/** + * Parse a network specification. The argument specifies + * a list of networks. The format is + * [network/netmask;]* (no whitespace, must be terminated + * with a semicolon). The network must be given in dotted-decimal + * notation. The netmask can be given in CIDR notation (/16) or + * in dotted-decimal (/255.255.0.0). + *

+ * @param routeList a string specifying the forbidden networks + * @return the converted list, NULL if the synatx is flawed + */ +static struct IPv4NetworkSet * +parse_ipv4_specification (const char *routeList) +{ + unsigned int count; + unsigned int i; + unsigned int j; + unsigned int len; + int cnt; + unsigned int pos; + unsigned int temps[8]; + int slash; + struct IPv4NetworkSet *result; + + if (routeList == NULL) + return NULL; + len = strlen (routeList); + if (len == 0) + return NULL; + count = 0; + for (i = 0; i < len; i++) + if (routeList[i] == ';') + count++; + result = GNUNET_malloc (sizeof (struct IPv4NetworkSet) * (count + 1)); + /* add termination */ + memset (result, 0, sizeof (struct IPv4NetworkSet) * (count + 1)); + i = 0; + pos = 0; + while (i < count) + { + cnt = sscanf (&routeList[pos], + "%u.%u.%u.%u/%u.%u.%u.%u;", + &temps[0], + &temps[1], + &temps[2], + &temps[3], &temps[4], &temps[5], &temps[6], &temps[7]); + if (cnt == 8) + { + for (j = 0; j < 8; j++) + if (temps[j] > 0xFF) + { + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + _("Invalid format for IP: `%s'\n"), + &routeList[pos]); + GNUNET_free (result); + return NULL; + } + result[i].network.s_addr + = + htonl ((temps[0] << 24) + (temps[1] << 16) + (temps[2] << 8) + + temps[3]); + result[i].netmask.s_addr = + htonl ((temps[4] << 24) + (temps[5] << 16) + (temps[6] << 8) + + temps[7]); + while (routeList[pos] != ';') + pos++; + pos++; + i++; + continue; + } + /* try second notation */ + cnt = sscanf (&routeList[pos], + "%u.%u.%u.%u/%u;", + &temps[0], &temps[1], &temps[2], &temps[3], &slash); + if (cnt == 5) + { + for (j = 0; j < 4; j++) + if (temps[j] > 0xFF) + { + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + _("Invalid format for IP: `%s'\n"), + &routeList[pos]); + GNUNET_free (result); + return NULL; + } + result[i].network.s_addr + = + htonl ((temps[0] << 24) + (temps[1] << 16) + (temps[2] << 8) + + temps[3]); + if ((slash <= 32) && (slash >= 0)) + { + result[i].netmask.s_addr = 0; + while (slash > 0) + { + result[i].netmask.s_addr + = (result[i].netmask.s_addr >> 1) + 0x80000000; + slash--; + } + result[i].netmask.s_addr = htonl (result[i].netmask.s_addr); + while (routeList[pos] != ';') + pos++; + pos++; + i++; + continue; + } + else + { + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + _ + ("Invalid network notation ('/%d' is not legal in IPv4 CIDR)."), + slash); + GNUNET_free (result); + return NULL; /* error */ + } + } + /* try third notation */ + slash = 32; + cnt = sscanf (&routeList[pos], + "%u.%u.%u.%u;", + &temps[0], &temps[1], &temps[2], &temps[3]); + if (cnt == 4) + { + for (j = 0; j < 4; j++) + if (temps[j] > 0xFF) + { + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + _("Invalid format for IP: `%s'\n"), + &routeList[pos]); + GNUNET_free (result); + return NULL; + } + result[i].network.s_addr + = + htonl ((temps[0] << 24) + (temps[1] << 16) + (temps[2] << 8) + + temps[3]); + result[i].netmask.s_addr = 0; + while (slash > 0) + { + result[i].netmask.s_addr + = (result[i].netmask.s_addr >> 1) + 0x80000000; + slash--; + } + result[i].netmask.s_addr = htonl (result[i].netmask.s_addr); + while (routeList[pos] != ';') + pos++; + pos++; + i++; + continue; + } + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + _("Invalid format for IP: `%s'\n"), &routeList[pos]); + GNUNET_free (result); + return NULL; /* error */ + } + if (pos < strlen (routeList)) + { + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + _("Invalid format for IP: `%s'\n"), &routeList[pos]); + GNUNET_free (result); + return NULL; /* oops */ + } + return result; /* ok */ +} + + +/** + * Parse a network specification. The argument specifies + * a list of networks. The format is + * [network/netmask;]* (no whitespace, must be terminated + * with a semicolon). The network must be given in colon-hex + * notation. The netmask must be given in CIDR notation (/16) or + * can be omitted to specify a single host. + *

+ * @param routeList a string specifying the forbidden networks + * @return the converted list, NULL if the synatx is flawed + */ +static struct IPv6NetworkSet * +parse_ipv6_specification (const char *routeListX) +{ + unsigned int count; + unsigned int i; + unsigned int len; + unsigned int pos; + int start; + int slash; + int ret; + char *routeList; + struct IPv6NetworkSet *result; + unsigned int bits; + unsigned int off; + int save; + + if (routeListX == NULL) + return NULL; + len = strlen (routeListX); + if (len == 0) + return NULL; + routeList = GNUNET_strdup (routeListX); + count = 0; + for (i = 0; i < len; i++) + if (routeList[i] == ';') + count++; + if (routeList[len - 1] != ';') + { + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + _ + ("Invalid network notation (does not end with ';': `%s')\n"), + routeList); + GNUNET_free (routeList); + return NULL; + } + + result = GNUNET_malloc (sizeof (struct IPv6NetworkSet) * (count + 1)); + memset (result, 0, sizeof (struct IPv6NetworkSet) * (count + 1)); + i = 0; + pos = 0; + while (i < count) + { + start = pos; + while (routeList[pos] != ';') + pos++; + slash = pos; + while ((slash >= start) && (routeList[slash] != '/')) + slash--; + if (slash < start) + { + memset (&result[i].netmask, 0xFF, sizeof (struct in6_addr)); + slash = pos; + } + else + { + routeList[pos] = '\0'; + ret = inet_pton (AF_INET6, + &routeList[slash + 1], &result[i].netmask); + if (ret <= 0) + { + save = errno; + if ((1 != SSCANF (&routeList[slash + 1], + "%u", &bits)) || (bits >= 128)) + { + if (ret == 0) + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + _("Wrong format `%s' for netmask\n"), + &routeList[slash + 1]); + else + { + errno = save; + GNUNET_log_strerror (GNUNET_ERROR_TYPE_ERROR, + "inet_pton"); + } + GNUNET_free (result); + GNUNET_free (routeList); + return NULL; + } + off = 0; + while (bits > 8) + { + result[i].netmask.s6_addr[off++] = 0xFF; + bits -= 8; + } + while (bits > 0) + { + result[i].netmask.s6_addr[off] + = (result[i].netmask.s6_addr[off] >> 1) + 0x80; + bits--; + } + } + } + routeList[slash] = '\0'; + ret = inet_pton (AF_INET6, &routeList[start], &result[i].network); + if (ret <= 0) + { + if (ret == 0) + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + _("Wrong format `%s' for network\n"), + &routeList[slash + 1]); + else + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "inet_pton"); + GNUNET_free (result); + GNUNET_free (routeList); + return NULL; + } + pos++; + i++; + } + GNUNET_free (routeList); + return result; +} + + +/** + * Check if the given IP address is in the list of IP addresses. + * + * @param list a list of networks + * @param ip the IP to check (in network byte order) + * @return GNUNET_NO if the IP is not in the list, GNUNET_YES if it it is + */ +static int +check_ipv4_listed (const struct IPv4NetworkSet *list, + const struct in_addr *add) +{ + int i; + + i = 0; + if (list == NULL) + return GNUNET_NO; + + while ((list[i].network.s_addr != 0) || (list[i].netmask.s_addr != 0)) + { + if ((add->s_addr & list[i].netmask.s_addr) == + (list[i].network.s_addr & list[i].netmask.s_addr)) + return GNUNET_YES; + i++; + } + return GNUNET_NO; +} + +/** + * Check if the given IP address is in the list of IP addresses. + * + * @param list a list of networks + * @param ip the IP to check (in network byte order) + * @return GNUNET_NO if the IP is not in the list, GNUNET_YES if it it is + */ +static int +check_ipv6_listed (const struct IPv6NetworkSet *list, + const struct in6_addr *ip) +{ + unsigned int i; + unsigned int j; + struct in6_addr zero; + + if (list == NULL) + return GNUNET_NO; + + memset (&zero, 0, sizeof (struct in6_addr)); + i = 0; +NEXT: + while (memcmp (&zero, &list[i].network, sizeof (struct in6_addr)) != 0) + { + for (j = 0; j < sizeof (struct in6_addr) / sizeof (int); j++) + if (((((int *) ip)[j] & ((int *) &list[i].netmask)[j])) != + (((int *) &list[i].network)[j] & ((int *) &list[i].netmask)[j])) + { + i++; + goto NEXT; + } + return GNUNET_YES; + } + return GNUNET_NO; +} + + +/* ****************** service struct ****************** */ + + +/** + * Context for "service_task". + */ +struct GNUNET_SERVICE_Context +{ + /** + * Our configuration. + */ + struct GNUNET_CONFIGURATION_Handle *cfg; + + /** + * Handle for the server. + */ + struct GNUNET_SERVER_Handle *server; + + /** + * Scheduler for the server. + */ + struct GNUNET_SCHEDULER_Handle *sched; + + /** + * Address to bind to. + */ + struct sockaddr *addr; + + /** + * Name of our service. + */ + const char *serviceName; + + /** + * Main service-specific task to run. + */ + GNUNET_SERVICE_Main task; + + /** + * Closure for task. + */ + void *task_cls; + + /** + * IPv4 addresses that are not allowed to connect. + */ + struct IPv4NetworkSet *v4_denied; + + /** + * IPv6 addresses that are not allowed to connect. + */ + struct IPv6NetworkSet *v6_denied; + + /** + * IPv4 addresses that are allowed to connect (if not + * set, all are allowed). + */ + struct IPv4NetworkSet *v4_allowed; + + /** + * IPv6 addresses that are allowed to connect (if not + * set, all are allowed). + */ + struct IPv6NetworkSet *v6_allowed; + + /** + * My (default) message handlers. Adjusted copy + * of "defhandlers". + */ + struct GNUNET_SERVER_MessageHandler *my_handlers; + + /** + * Idle timeout for server. + */ + struct GNUNET_TIME_Relative timeout; + + /** + * Maximum buffer size for the server. + */ + size_t maxbuf; + + /** + * Overall success/failure of the service start. + */ + int ret; + + /** + * If we are daemonizing, this FD is set to the + * pipe to the parent. Send '.' if we started + * ok, '!' if not. -1 if we are not daemonizing. + */ + int ready_confirm_fd; + + /** + * Do we close connections if we receive messages + * for which we have no handler? + */ + int require_found; + + /** + * Can clients ask us to initiate a shutdown? + */ + int allow_shutdown; + + /** + * Length of addr. + */ + socklen_t addrlen; + +}; + + +/* ****************** message handlers ****************** */ + +static size_t +write_test (void *cls, size_t size, void *buf) +{ + struct GNUNET_SERVER_Client *client = cls; + struct GNUNET_MessageHeader *msg; + + if (size < sizeof (struct GNUNET_MessageHeader)) + { + GNUNET_SERVER_receive_done (client, GNUNET_SYSERR); + return 0; /* client disconnected */ + } + msg = (struct GNUNET_MessageHeader *) buf; + msg->type = htons (GNUNET_MESSAGE_TYPE_TEST); + msg->size = htons (sizeof (struct GNUNET_MessageHeader)); + GNUNET_SERVER_receive_done (client, GNUNET_OK); + return sizeof (struct GNUNET_MessageHeader); +} + +/** + * Handler for TEST message. + * + * @param cls closure (refers to service) + * @param server the server handling the message + * @param client identification of the client + * @param message the actual message + */ +static void +handle_test (void *cls, + struct GNUNET_SERVER_Handle *server, + struct GNUNET_SERVER_Client *client, + const struct GNUNET_MessageHeader *message) +{ + /* simply bounce message back to acknowledge */ + if (NULL == GNUNET_SERVER_notify_transmit_ready (client, + sizeof (struct + GNUNET_MessageHeader), + GNUNET_TIME_UNIT_FOREVER_REL, + &write_test, client)) + GNUNET_SERVER_receive_done (client, GNUNET_SYSERR); +} + + +/** + * Handler for SHUTDOWN message. + * + * @param cls closure (refers to service) + * @param server the server handling the message + * @param client identification of the client + * @param message the actual message + */ +static void +handle_shutdown (void *cls, + struct GNUNET_SERVER_Handle *server, + struct GNUNET_SERVER_Client *client, + const struct GNUNET_MessageHeader *message) +{ + struct GNUNET_SERVICE_Context *service = cls; + if (!service->allow_shutdown) + { + GNUNET_log (GNUNET_ERROR_TYPE_WARNING, + _ + ("Received shutdown request, but configured to ignore!\n")); + GNUNET_SERVER_receive_done (client, GNUNET_SYSERR); + return; + } + GNUNET_log (GNUNET_ERROR_TYPE_INFO, + _("Initiating shutdown as requested by client.\n")); + GNUNET_assert (service->sched != NULL); + GNUNET_SCHEDULER_shutdown (service->sched); + GNUNET_SERVER_receive_done (client, GNUNET_OK); +} + + +/** + * Default handlers for all services. Will be copied and the + * "callback_cls" fields will be replaced with the specific service + * struct. + */ +static const struct GNUNET_SERVER_MessageHandler defhandlers[] = { + {&handle_test, NULL, GNUNET_MESSAGE_TYPE_TEST, + sizeof (struct GNUNET_MessageHeader)}, + {&handle_shutdown, NULL, GNUNET_MESSAGE_TYPE_SHUTDOWN, + sizeof (struct GNUNET_MessageHeader)}, + {NULL, NULL, 0, 0} +}; + + + +/* ****************** service core routines ************** */ + + +/** + * Check if access to the service is allowed from the given address. + */ +static int +check_access (void *cls, const struct sockaddr *addr, socklen_t addrlen) +{ + struct GNUNET_SERVICE_Context *sctx = cls; + const struct sockaddr_in *i4; + const struct sockaddr_in6 *i6; + int ret; + char buf[INET6_ADDRSTRLEN]; + uint16_t port; + + switch (addr->sa_family) + { + case AF_INET: + GNUNET_assert (addrlen == sizeof (struct sockaddr_in)); + i4 = (const struct sockaddr_in *) addr; + port = ntohs (i4->sin_port); + ret = ((sctx->v4_allowed == NULL) || + (check_ipv4_listed (sctx->v4_allowed, + &i4->sin_addr))) + && ((sctx->v4_denied == NULL) || + (!check_ipv4_listed (sctx->v4_denied, &i4->sin_addr))); + if (ret != GNUNET_OK) + inet_ntop (AF_INET, &i4->sin_addr, buf, sizeof (buf)); + break; + case AF_INET6: + GNUNET_assert (addrlen == sizeof (struct sockaddr_in6)); + i6 = (const struct sockaddr_in6 *) addr; + port = ntohs (i6->sin6_port); + ret = ((sctx->v6_allowed == NULL) || + (check_ipv6_listed (sctx->v6_allowed, + &i6->sin6_addr))) + && ((sctx->v6_denied == NULL) || + (!check_ipv6_listed (sctx->v6_denied, &i6->sin6_addr))); + if (ret != GNUNET_OK) + inet_ntop (AF_INET6, &i6->sin6_addr, buf, sizeof (buf)); + break; + default: + GNUNET_log (GNUNET_ERROR_TYPE_WARNING, + _("Unknown address family %d\n"), addr->sa_family); + return GNUNET_SYSERR; + } + if (ret != GNUNET_OK) + { + GNUNET_log (GNUNET_ERROR_TYPE_WARNING, + _("Access from `%s:%u' denied to service `%s'\n"), + buf, port, sctx->serviceName); + } + return ret; +} + + +/** + * Get the name of the file where we will + * write the PID of the service. + */ +static char * +get_pid_file_name (struct GNUNET_SERVICE_Context *sctx) +{ + + char *pif; + + if (GNUNET_OK != + GNUNET_CONFIGURATION_get_value_filename (sctx->cfg, + sctx->serviceName, + "PIDFILE", &pif)) + return NULL; + return pif; +} + + +/** + * Parse an IPv4 access control list. + */ +static int +process_acl4 (struct IPv4NetworkSet **ret, + struct GNUNET_SERVICE_Context *sctx, const char *option) +{ + char *opt; + + if (!GNUNET_CONFIGURATION_have_value (sctx->cfg, sctx->serviceName, option)) + return GNUNET_OK; + GNUNET_break (GNUNET_OK == + GNUNET_CONFIGURATION_get_value_string (sctx->cfg, + sctx->serviceName, + option, &opt)); + if (NULL == (*ret = parse_ipv4_specification (opt))) + { + GNUNET_log (GNUNET_ERROR_TYPE_WARNING, + _ + ("Could not parse IPv4 network specification `%s' for `%s:%s'\n"), + opt, sctx->serviceName, option); + GNUNET_free (opt); + return GNUNET_SYSERR; + } + GNUNET_free (opt); + return GNUNET_OK; +} + + +/** + * Parse an IPv4 access control list. + */ +static int +process_acl6 (struct IPv6NetworkSet **ret, + struct GNUNET_SERVICE_Context *sctx, const char *option) +{ + char *opt; + if (!GNUNET_CONFIGURATION_have_value (sctx->cfg, sctx->serviceName, option)) + return GNUNET_OK; + GNUNET_break (GNUNET_OK == + GNUNET_CONFIGURATION_get_value_string (sctx->cfg, + sctx->serviceName, + option, &opt)); + if (NULL == (*ret = parse_ipv6_specification (opt))) + { + GNUNET_log (GNUNET_ERROR_TYPE_WARNING, + _ + ("Could not parse IPv6 network specification `%s' for `%s:%s'\n"), + opt, sctx->serviceName, option); + GNUNET_free (opt); + return GNUNET_SYSERR; + } + GNUNET_free (opt); + return GNUNET_OK; +} + + +/** + * Setup addr, addrlen, maxbuf, idle_timeout + * based on configuration! + * + * Configuration must specify a "PORT". It may + * specify: + * - TIMEOUT (after how many ms does an inactive service timeout); + * - MAXBUF (maximum incoming message size supported) + * - DISABLEV6 (disable support for IPv6, otherwise we use dual-stack) + * - ALLOW_SHUTDOWN (allow clients to shutdown this service) + * - BINDTO (hostname or IP address to bind to, otherwise we take everything) + * - ACCEPT_FROM (only allow connections from specified IPv4 subnets) + * - ACCEPT_FROM6 (only allow connections from specified IPv6 subnets) + * - REJECT_FROM (disallow allow connections from specified IPv4 subnets) + * - REJECT_FROM6 (disallow allow connections from specified IPv6 subnets) + * + * @return GNUNET_OK if configuration succeeded + */ +static int +setup_service (struct GNUNET_SERVICE_Context *sctx) +{ + unsigned long long maxbuf; + unsigned long long idleout; + char *hostname; + unsigned long long port; + int disablev6; + struct addrinfo hints; + struct addrinfo *res; + struct addrinfo *pos; + int ret; + int tolerant; + + if (GNUNET_CONFIGURATION_have_value (sctx->cfg, + sctx->serviceName, "TIMEOUT")) + { + if (GNUNET_OK != + GNUNET_CONFIGURATION_get_value_number (sctx->cfg, + sctx->serviceName, + "TIMEOUT", &idleout)) + return GNUNET_SYSERR; + + sctx->timeout.value = idleout; + } + else + sctx->timeout = GNUNET_TIME_UNIT_FOREVER_REL; + if (GNUNET_CONFIGURATION_have_value (sctx->cfg, + sctx->serviceName, "MAXBUF")) + { + if (GNUNET_OK != + GNUNET_CONFIGURATION_get_value_number (sctx->cfg, + sctx->serviceName, + "MAXBUF", &maxbuf)) + return GNUNET_SYSERR; + } + else + maxbuf = GNUNET_SERVER_MAX_MESSAGE_SIZE; + if (GNUNET_CONFIGURATION_have_value (sctx->cfg, + sctx->serviceName, "DISABLEV6")) + { + if (GNUNET_SYSERR == + (disablev6 = GNUNET_CONFIGURATION_get_value_yesno (sctx->cfg, + sctx-> + serviceName, + "DISABLEV6"))) + return GNUNET_SYSERR; + } + else + disablev6 = GNUNET_NO; + if (GNUNET_CONFIGURATION_have_value (sctx->cfg, + sctx->serviceName, "ALLOW_SHUTDOWN")) + { + if (GNUNET_SYSERR == + (sctx->allow_shutdown = + GNUNET_CONFIGURATION_get_value_yesno (sctx->cfg, sctx->serviceName, + "ALLOW_SHUTDOWN"))) + return GNUNET_SYSERR; + } + else + sctx->allow_shutdown = GNUNET_NO; + + if (!disablev6) + { + /* probe IPv6 support */ + ret = SOCKET (PF_INET6, SOCK_STREAM, 0); + if (ret == -1) + { + if ((errno == ENOBUFS) || + (errno == ENOMEM) || (errno == ENFILE) || (errno == EACCES)) + { + GNUNET_log_strerror (GNUNET_ERROR_TYPE_ERROR, "socket"); + return GNUNET_SYSERR; + } + ret = SOCKET (PF_INET, SOCK_STREAM, 0); + if (ret != -1) + { + GNUNET_log (GNUNET_ERROR_TYPE_INFO, + _ + ("Disabling IPv6 support for service `%s', failed to create IPv6 socket: %s\n"), + sctx->serviceName, strerror (errno)); + disablev6 = GNUNET_YES; + } + } + if (ret != -1) + GNUNET_break (0 == CLOSE (ret)); + } + + + + if (GNUNET_CONFIGURATION_have_value (sctx->cfg, + sctx->serviceName, "TOLERANT")) + { + if (GNUNET_SYSERR == + (tolerant = GNUNET_CONFIGURATION_get_value_yesno (sctx->cfg, + sctx->serviceName, + "TOLERANT"))) + return GNUNET_SYSERR; + } + else + tolerant = GNUNET_NO; + sctx->require_found = tolerant ? GNUNET_NO : GNUNET_YES; + + + if ((GNUNET_OK != + GNUNET_CONFIGURATION_get_value_number (sctx->cfg, + sctx->serviceName, + "PORT", + &port)) || (port > 65535)) + { + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + _ + ("Require valid port number for service `%s' in configuration!\n"), + sctx->serviceName); + return GNUNET_SYSERR; + } + if (GNUNET_CONFIGURATION_have_value (sctx->cfg, + sctx->serviceName, "BINDTO")) + { + GNUNET_break (GNUNET_OK == + GNUNET_CONFIGURATION_get_value_string (sctx->cfg, + sctx->serviceName, + "BINDTO", + &hostname)); + } + else + hostname = NULL; + + if (hostname != NULL) + { + memset (&hints, 0, sizeof (struct addrinfo)); + if (disablev6) + hints.ai_family = AF_INET; + if ((0 != (ret = getaddrinfo (hostname, + NULL, &hints, &res))) || (res == NULL)) + { + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + _("Failed to resolve `%s': %s\n"), + hostname, gai_strerror (ret)); + GNUNET_free (hostname); + return GNUNET_SYSERR; + } + pos = res; + while ((NULL != pos) && + (((disablev6) && + (pos->ai_family != AF_INET)) || + ((pos->ai_family != AF_INET) && (pos->ai_family != AF_INET6)))) + pos = pos->ai_next; + if (pos == NULL) + { + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + _("Failed to find IPv4 address for `%s'.\n"), hostname); + freeaddrinfo (res); + GNUNET_free (hostname); + return GNUNET_SYSERR; + } + GNUNET_free (hostname); + if (pos->ai_family == AF_INET) + { + GNUNET_assert (pos->ai_addrlen == sizeof (struct sockaddr_in)); + sctx->addrlen = pos->ai_addrlen; + sctx->addr = GNUNET_malloc (sctx->addrlen); + memcpy (sctx->addr, res->ai_addr, sctx->addrlen); + ((struct sockaddr_in *) sctx->addr)->sin_port = htons (port); + GNUNET_log (GNUNET_ERROR_TYPE_INFO, + _ + ("Configured to bind to %s address; %s connections to this service will fail!\n"), + "IPv4", "IPv6"); + } + else + { + GNUNET_assert (pos->ai_family == AF_INET6); + GNUNET_assert (pos->ai_addrlen == sizeof (struct sockaddr_in6)); + sctx->addrlen = pos->ai_addrlen; + sctx->addr = GNUNET_malloc (sctx->addrlen); + GNUNET_log (GNUNET_ERROR_TYPE_INFO, + _ + ("Configured to bind to %s address; %s connections to this service will fail!\n"), + "IPv6", "IPv4"); + ((struct sockaddr_in6 *) sctx->addr)->sin6_port = htons (port); + } + freeaddrinfo (res); + } + else + { + /* will bind against everything, just set port */ + if (disablev6) + { + /* V4-only */ + sctx->addrlen = sizeof (struct sockaddr_in6); + sctx->addr = GNUNET_malloc (sctx->addrlen); + ((struct sockaddr_in *) sctx->addr)->sin_family = AF_INET; + ((struct sockaddr_in *) sctx->addr)->sin_port = htons (port); + GNUNET_log (GNUNET_ERROR_TYPE_INFO, + _ + ("Configured to bind to %s address; %s connections to this service will fail!\n"), + "IPv4", "IPv6"); + } + else + { + /* dual stack */ + sctx->addrlen = sizeof (struct sockaddr_in6); + sctx->addr = GNUNET_malloc (sctx->addrlen); + ((struct sockaddr_in6 *) sctx->addr)->sin6_family = AF_INET6; + ((struct sockaddr_in6 *) sctx->addr)->sin6_port = htons (port); + } + } + sctx->maxbuf = (size_t) maxbuf; + if (sctx->maxbuf != maxbuf) + { + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + _ + ("Value in configuration for `%s' and service `%s' too large!\n"), + "MAXBUF", sctx->serviceName); + return GNUNET_SYSERR; + } + + + if ((GNUNET_OK != + process_acl4 (&sctx->v4_denied, + sctx, + "REJECT_FROM")) || + (GNUNET_OK != + process_acl4 (&sctx->v4_allowed, + sctx, + "ACCEPT_FROM")) || + (GNUNET_OK != + process_acl6 (&sctx->v6_denied, + sctx, + "REJECT_FROM6")) || + (GNUNET_OK != process_acl6 (&sctx->v6_allowed, sctx, "ACCEPT_FROM6"))) + return GNUNET_SYSERR; + return GNUNET_OK; +} + + +/** + * Get the name of the user that'll be used + * to provide the service. + */ +static char * +get_user_name (struct GNUNET_SERVICE_Context *sctx) +{ + + char *un; + + if (GNUNET_OK != + GNUNET_CONFIGURATION_get_value_filename (sctx->cfg, + sctx->serviceName, + "USERNAME", &un)) + return NULL; + return un; +} + +/** + * Write PID file. + */ +static int +write_pid_file (struct GNUNET_SERVICE_Context *sctx, pid_t pid) +{ + FILE *pidfd; + char *pif; + char *user; + char *rdir; + int len; + + if (NULL == (pif = get_pid_file_name (sctx))) + return GNUNET_OK; /* no file desired */ + user = get_user_name (sctx); + rdir = GNUNET_strdup (pif); + len = strlen (rdir); + while ((len > 0) && (rdir[len] != DIR_SEPARATOR)) + len--; + rdir[len] = '\0'; + if (0 != ACCESS (rdir, F_OK)) + { + /* we get to create a directory -- and claim it + as ours! */ + GNUNET_DISK_directory_create (rdir); + if ((user != NULL) && (0 < strlen (user))) + GNUNET_DISK_file_change_owner (rdir, user); + } + if (0 != ACCESS (rdir, W_OK | X_OK)) + { + GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_ERROR, "access", rdir); + GNUNET_free (rdir); + GNUNET_free_non_null (user); + GNUNET_free (pif); + return GNUNET_SYSERR; + } + GNUNET_free (rdir); + pidfd = FOPEN (pif, "w"); + if (pidfd == NULL) + { + GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_ERROR, "fopen", pif); + GNUNET_free (pif); + GNUNET_free_non_null (user); + return GNUNET_SYSERR; + } + if (0 > FPRINTF (pidfd, "%u", pid)) + GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING, "fprintf", pif); + GNUNET_break (0 == fclose (pidfd)); + if ((user != NULL) && (0 < strlen (user))) + GNUNET_DISK_file_change_owner (pif, user); + GNUNET_free_non_null (user); + GNUNET_free (pif); + return GNUNET_OK; +} + + +/** + * Initial task for the service. + */ +static void +service_task (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc) +{ + struct GNUNET_SERVICE_Context *sctx = cls; + unsigned int i; + + sctx->sched = tc->sched; + sctx->server = GNUNET_SERVER_create (tc->sched, + &check_access, + sctx, + sctx->addr, + sctx->addrlen, + sctx->maxbuf, + sctx->timeout, sctx->require_found); + if (sctx->server == NULL) + { + sctx->ret = GNUNET_SYSERR; + return; + } + sctx->my_handlers = GNUNET_malloc (sizeof (defhandlers)); + memcpy (sctx->my_handlers, defhandlers, sizeof (defhandlers)); + i = 0; + while ((sctx->my_handlers[i].callback != NULL)) + sctx->my_handlers[i++].callback_cls = sctx; + GNUNET_SERVER_add_handlers (sctx->server, sctx->my_handlers); + if (sctx->ready_confirm_fd != -1) + { + GNUNET_break (1 == WRITE (sctx->ready_confirm_fd, ".", 1)); + GNUNET_break (0 == CLOSE (sctx->ready_confirm_fd)); + sctx->ready_confirm_fd = -1; + write_pid_file (sctx, getpid ()); + } + + sctx->task (sctx->task_cls, tc->sched, sctx->server, sctx->cfg); +} + + +/** + * Detach from terminal. + */ +static int +detach_terminal (struct GNUNET_SERVICE_Context *sctx) +{ + pid_t pid; + int nullfd; + int filedes[2]; + +#ifndef MINGW + if (0 != PIPE (filedes)) + { + GNUNET_log_strerror (GNUNET_ERROR_TYPE_ERROR, "pipe"); + return GNUNET_SYSERR; + } + pid = fork (); + if (pid < 0) + { + GNUNET_log_strerror (GNUNET_ERROR_TYPE_ERROR, "fork"); + return GNUNET_SYSERR; + } + if (pid != 0) + { + /* Parent */ + char c; + + GNUNET_break (0 == CLOSE (filedes[1])); + c = 'X'; + if (1 != READ (filedes[0], &c, sizeof (char))) + GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING, "read"); + fflush (stdout); + switch (c) + { + case '.': + exit (0); + case 'I': + GNUNET_log (GNUNET_ERROR_TYPE_INFO, + _("Service process failed to initialize\n")); + break; + case 'S': + GNUNET_log (GNUNET_ERROR_TYPE_INFO, + _ + ("Service process could not initialize server function\n")); + break; + case 'X': + GNUNET_log (GNUNET_ERROR_TYPE_INFO, + _("Service process failed to report status\n")); + break; + } + exit (1); /* child reported error */ + } + GNUNET_break (0 == CLOSE (0)); + GNUNET_break (0 == CLOSE (1)); + GNUNET_break (0 == CLOSE (filedes[0])); + nullfd = GNUNET_DISK_file_open ("/dev/null", O_RDWR | O_APPEND); + if (nullfd < 0) + return GNUNET_SYSERR; + /* set stdin/stdout to /dev/null */ + if ((dup2 (nullfd, 0) < 0) || (dup2 (nullfd, 1) < 0)) + { + GNUNET_log_strerror (GNUNET_ERROR_TYPE_ERROR, "dup2"); + return GNUNET_SYSERR; + } + /* Detach from controlling terminal */ + pid = setsid (); + if (pid == -1) + GNUNET_log_strerror (GNUNET_ERROR_TYPE_ERROR, "setsid"); + sctx->ready_confirm_fd = filedes[1]; +#else + /* FIXME: we probably need to do something else + elsewhere in order to fork the process itself... */ + FreeConsole (); +#endif + return GNUNET_OK; +} + + +/** + * Set user ID. + */ +static int +set_user_id (struct GNUNET_SERVICE_Context *sctx) +{ + char *user; + + if (NULL == (user = get_user_name (sctx))) + return GNUNET_OK; /* keep */ +#ifndef MINGW + struct passwd *pws; + + errno = 0; + pws = getpwnam (user); + if (pws == NULL) + { + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + _("Cannot obtain information about user `%s': %s\n"), + user, errno == 0 ? _("No such user") : STRERROR (errno)); + GNUNET_free (user); + return GNUNET_SYSERR; + } + if ((0 != setgid (pws->pw_gid)) || (0 != setegid (pws->pw_gid)) || +#if HAVE_INITGROUPS + (0 != initgroups (user, pws->pw_gid)) || +#endif + (0 != setuid (pws->pw_uid)) || (0 != seteuid (pws->pw_uid))) + { + if ((0 != setregid (pws->pw_gid, pws->pw_gid)) || + (0 != setreuid (pws->pw_uid, pws->pw_uid))) + { + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + _("Cannot change user/group to `%s': %s\n"), user, + STRERROR (errno)); + GNUNET_free (user); + return GNUNET_SYSERR; + } + } +#endif + GNUNET_free (user); + return GNUNET_OK; +} + + +/** + * Delete the PID file that was created by our parent. + */ +static void +pid_file_delete (struct GNUNET_SERVICE_Context *sctx) +{ + char *pif = get_pid_file_name (sctx); + if (pif == NULL) + return; /* no PID file */ + if (0 != UNLINK (pif)) + GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING, "unlink", pif); + GNUNET_free (pif); +} + +/** + * Run a standard GNUnet service startup sequence (initialize loggers + * and configuration, parse options). + * + * @param argc number of command line arguments + * @param argv command line arguments + * @param serviceName our service name + * @param task main task of the service + * @param task_cls closure for task + * @param term termination task of the service + * @param term_cls closure for term + * @return GNUNET_SYSERR on error, GNUNET_OK + * if we shutdown nicely + */ +int +GNUNET_SERVICE_run (int argc, + char *const *argv, + const char *serviceName, + GNUNET_SERVICE_Main task, + void *task_cls, GNUNET_SERVICE_Term term, void *term_cls) +{ + char *cfg_fn; + char *loglev; + char *logfile; + int do_daemonize; + struct GNUNET_SERVICE_Context sctx; + struct GNUNET_GETOPT_CommandLineOption service_options[] = { + GNUNET_GETOPT_OPTION_CFG_FILE (&cfg_fn), + {'d', "daemonize", NULL, + gettext_noop ("do daemonize (detach from terminal)"), 0, + GNUNET_GETOPT_set_one, &do_daemonize}, + GNUNET_GETOPT_OPTION_HELP (serviceName), + GNUNET_GETOPT_OPTION_LOGLEVEL (&loglev), + GNUNET_GETOPT_OPTION_LOGFILE (&logfile), + GNUNET_GETOPT_OPTION_VERSION (PACKAGE_VERSION), + GNUNET_GETOPT_OPTION_END + }; + do_daemonize = 0; + logfile = NULL; + loglev = GNUNET_strdup ("WARNING"); + cfg_fn = GNUNET_strdup (GNUNET_DEFAULT_DAEMON_CONFIG_FILE); + memset (&sctx, 0, sizeof (sctx)); + sctx.ready_confirm_fd = -1; + sctx.ret = GNUNET_OK; + sctx.timeout = GNUNET_TIME_UNIT_FOREVER_REL; + sctx.maxbuf = GNUNET_SERVER_MAX_MESSAGE_SIZE; + sctx.task = task; + sctx.serviceName = serviceName; + sctx.cfg = GNUNET_CONFIGURATION_create (); + /* setup subsystems */ + if ((GNUNET_SYSERR == + GNUNET_GETOPT_run (serviceName, + sctx.cfg, + service_options, + argc, + argv)) || + (GNUNET_OK != + GNUNET_log_setup (serviceName, loglev, logfile)) || + (GNUNET_OK != + GNUNET_CONFIGURATION_load (sctx.cfg, cfg_fn)) || + (GNUNET_OK != + setup_service (&sctx)) || + ((do_daemonize == 1) && + (GNUNET_OK != detach_terminal (&sctx))) || + (GNUNET_OK != set_user_id (&sctx))) + { + if (sctx.ready_confirm_fd != -1) + { + if (1 != WRITE (sctx.ready_confirm_fd, "I", 1)) + GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING, "write"); + GNUNET_break (0 == CLOSE (sctx.ready_confirm_fd)); + } + GNUNET_CONFIGURATION_destroy (sctx.cfg); + GNUNET_free_non_null (sctx.addr); + GNUNET_free_non_null (logfile); + GNUNET_free (loglev); + GNUNET_free (cfg_fn); + GNUNET_free_non_null (sctx.v4_denied); + GNUNET_free_non_null (sctx.v6_denied); + GNUNET_free_non_null (sctx.v4_allowed); + GNUNET_free_non_null (sctx.v6_allowed); + return GNUNET_SYSERR; + } + + /* actually run service */ + GNUNET_SCHEDULER_run (&service_task, &sctx); + if (sctx.ready_confirm_fd != -1) + { + if (1 != WRITE (sctx.ready_confirm_fd, "S", 1)) + GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING, "write"); + GNUNET_break (0 == CLOSE (sctx.ready_confirm_fd)); + } + + /* shutdown */ + if (term != NULL) + term (term_cls, sctx.cfg); + if ((do_daemonize == 1) && (sctx.server != NULL)) + pid_file_delete (&sctx); + if (sctx.server != NULL) + GNUNET_SERVER_destroy (sctx.server); + GNUNET_free_non_null (sctx.my_handlers); + GNUNET_CONFIGURATION_destroy (sctx.cfg); + GNUNET_free_non_null (sctx.addr); + GNUNET_free_non_null (logfile); + GNUNET_free (loglev); + GNUNET_free (cfg_fn); + GNUNET_free_non_null (sctx.v4_denied); + GNUNET_free_non_null (sctx.v6_denied); + GNUNET_free_non_null (sctx.v4_allowed); + GNUNET_free_non_null (sctx.v6_allowed); + return sctx.ret; +} + + +/** + * Run a service startup sequence within an existing + * initialized system. + * + * @param serviceName our service name + * @param sched scheduler to use + * @param cfg configuration to use + * @return NULL on error, service handle + */ +struct GNUNET_SERVICE_Context * +GNUNET_SERVICE_start (const char *serviceName, + struct GNUNET_SCHEDULER_Handle *sched, + struct GNUNET_CONFIGURATION_Handle *cfg) +{ + int i; + struct GNUNET_SERVICE_Context *sctx; + + sctx = GNUNET_malloc (sizeof (struct GNUNET_SERVICE_Context)); + sctx->ready_confirm_fd = -1; /* no daemonizing */ + sctx->ret = GNUNET_OK; + sctx->timeout = GNUNET_TIME_UNIT_FOREVER_REL; + sctx->maxbuf = GNUNET_SERVER_MAX_MESSAGE_SIZE; + sctx->serviceName = serviceName; + sctx->cfg = cfg; + sctx->sched = sched; + + /* setup subsystems */ + if ((GNUNET_OK != setup_service (sctx)) || + (NULL == (sctx->server = GNUNET_SERVER_create (sched, + &check_access, + sctx, + sctx->addr, + sctx->addrlen, + sctx->maxbuf, + sctx->timeout, + sctx->require_found)))) + { + GNUNET_SERVICE_stop (sctx); + return NULL; + } + sctx->my_handlers = GNUNET_malloc (sizeof (defhandlers)); + memcpy (sctx->my_handlers, defhandlers, sizeof (defhandlers)); + i = 0; + while ((sctx->my_handlers[i].callback != NULL)) + sctx->my_handlers[i++].callback_cls = sctx; + GNUNET_SERVER_add_handlers (sctx->server, sctx->my_handlers); + + + return sctx; +} + +/** + * Obtain the server used by a service. Note that the server must NOT + * be destroyed by the caller. + * + * @param ctx the service context returned from the start function + * @return handle to the server for this service, NULL if there is none + */ +struct GNUNET_SERVER_Handle * +GNUNET_SERVICE_get_server (struct GNUNET_SERVICE_Context *ctx) +{ + return ctx->server; +} + + +/** + * Stop a service that was started with "GNUNET_SERVICE_start". + * + * @param ctx the service context returned from the start function + */ +void +GNUNET_SERVICE_stop (struct GNUNET_SERVICE_Context *sctx) +{ + if (NULL != sctx->server) + GNUNET_SERVER_destroy (sctx->server); + GNUNET_free_non_null (sctx->my_handlers); + GNUNET_free_non_null (sctx->addr); + GNUNET_free_non_null (sctx->v4_denied); + GNUNET_free_non_null (sctx->v6_denied); + GNUNET_free_non_null (sctx->v4_allowed); + GNUNET_free_non_null (sctx->v6_allowed); + GNUNET_free (sctx); +} + + +/* end of service.c */ diff --git a/src/util/signal.c b/src/util/signal.c new file mode 100644 index 000000000..478551ca2 --- /dev/null +++ b/src/util/signal.c @@ -0,0 +1,76 @@ +/* + This file is part of GNUnet. + (C) 2001, 2002, 2006 Christian Grothoff (and other contributing authors) + + GNUnet is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published + by the Free Software Foundation; either version 2, 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 + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with GNUnet; see the file COPYING. If not, write to the + Free Software Foundation, Inc., 59 Temple Place - Suite 330, + Boston, MA 02111-1307, USA. +*/ + +/** + * @file util/signal.c + * @brief code for installing and uninstalling signal handlers + * @author Christian Grothoff + */ + +#include "platform.h" +#include "gnunet_common.h" +#include "gnunet_signal_lib.h" + +struct GNUNET_SIGNAL_Context +{ + int sig; + + GNUNET_SIGNAL_Handler method; + +#ifndef MINGW + struct sigaction oldsig; +#endif +}; + +struct GNUNET_SIGNAL_Context * +GNUNET_SIGNAL_handler_install (int signal, GNUNET_SIGNAL_Handler handler) +{ + struct GNUNET_SIGNAL_Context *ret; +#ifndef MINGW + struct sigaction sig; +#endif + + ret = GNUNET_malloc (sizeof (struct GNUNET_SIGNAL_Context)); + ret->sig = signal; + ret->method = handler; +#ifndef MINGW + sig.sa_handler = (void *) handler; + sigemptyset (&sig.sa_mask); +#ifdef SA_INTERRUPT + sig.sa_flags = SA_INTERRUPT; /* SunOS */ +#else + sig.sa_flags = SA_RESTART; +#endif + sigaction (signal, &sig, &ret->oldsig); +#endif + return ret; +} + +void +GNUNET_SIGNAL_handler_uninstall (struct GNUNET_SIGNAL_Context *ctx) +{ +#ifndef MINGW + struct sigaction sig; + + sigemptyset (&sig.sa_mask); + sigaction (ctx->sig, &ctx->oldsig, &sig); +#endif + GNUNET_free (ctx); +} diff --git a/src/util/strings.c b/src/util/strings.c new file mode 100644 index 000000000..18f6582e8 --- /dev/null +++ b/src/util/strings.c @@ -0,0 +1,396 @@ +/* + This file is part of GNUnet. + (C) 2005, 2006 Christian Grothoff (and other contributing authors) + + GNUnet is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published + by the Free Software Foundation; either version 2, 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 + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with GNUnet; see the file COPYING. If not, write to the + Free Software Foundation, Inc., 59 Temple Place - Suite 330, + Boston, MA 02111-1307, USA. +*/ + +/** + * @file util/strings.c + * @brief string functions + * @author Nils Durner + * @author Christian Grothoff + */ + +#include "platform.h" +#if HAVE_ICONV_H +#include +#endif +#include "gnunet_common.h" +#include "gnunet_strings_lib.h" + + +/** + * Fill a buffer of the given size with + * count 0-terminated strings (given as varargs). + * If "buffer" is NULL, only compute the amount of + * space required (sum of "strlen(arg)+1"). + * + * Unlike using "snprintf" with "%s", this function + * will add 0-terminators after each string. The + * "GNUNET_string_buffer_tokenize" function can be + * used to parse the buffer back into individual + * strings. + * + * @return number of bytes written to the buffer + * (or number of bytes that would have been written) + */ +unsigned int +GNUNET_STRINGS_buffer_fill (char *buffer, + unsigned int size, unsigned int count, ...) +{ + unsigned int needed; + unsigned int slen; + const char *s; + va_list ap; + + needed = 0; + va_start (ap, count); + while (count > 0) + { + s = va_arg (ap, const char *); + slen = strlen (s) + 1; + if (buffer != NULL) + { + GNUNET_assert (needed + slen <= size); + memcpy (&buffer[needed], s, slen); + } + needed += slen; + count--; + } + va_end (ap); + return needed; +} + + +/** + * Given a buffer of a given size, find "count" + * 0-terminated strings in the buffer and assign + * the count (varargs) of type "const char**" to the + * locations of the respective strings in the + * buffer. + * + * @param buffer the buffer to parse + * @param size size of the buffer + * @param count number of strings to locate + * @return offset of the character after the last 0-termination + * in the buffer, or 0 on error. + */ +unsigned int +GNUNET_STRINGS_buffer_tokenize (const char *buffer, + unsigned int size, unsigned int count, ...) +{ + unsigned int start; + unsigned int needed; + const char **r; + va_list ap; + + needed = 0; + va_start (ap, count); + while (count > 0) + { + r = va_arg (ap, const char **); + start = needed; + while ((needed < size) && (buffer[needed] != '\0')) + needed++; + if (needed == size) + { + va_end (ap); + return 0; /* error */ + } + *r = &buffer[start]; + needed++; /* skip 0-termination */ + count--; + } + va_end (ap); + return needed; +} + + +/** + * Convert a given filesize into a fancy human-readable format. + */ +char * +GNUNET_STRINGS_byte_size_fancy (unsigned long long size) +{ + const char *unit = _( /* size unit */ "b"); + char *ret; + + if (size > 5 * 1024) + { + size = size / 1024; + unit = _( /* size unit */ "KiB"); + if (size > 5 * 1024) + { + size = size / 1024; + unit = _( /* size unit */ "MiB"); + if (size > 5 * 1024) + { + size = size / 1024; + unit = _( /* size unit */ "GiB"); + if (size > 5 * 1024) + { + size = size / 1024; + unit = _( /* size unit */ "TiB"); + } + } + } + } + ret = GNUNET_malloc (32); + GNUNET_snprintf (ret, 32, "%llu%s", size, unit); + return ret; +} + + +/** + * Convert the len characters long character sequence + * given in input that is in the given charset + * to UTF-8. + * @return the converted string (0-terminated), + * if conversion fails, a copy of the orignal + * string is returned. + */ +char * +GNUNET_STRINGS_to_utf8 (const char *input, size_t len, const char *charset) +{ + char *ret; +#if ENABLE_NLS && HAVE_ICONV + size_t tmpSize; + size_t finSize; + char *tmp; + char *itmp; + iconv_t cd; + + cd = iconv_open ("UTF-8", charset); + if (cd == (iconv_t) - 1) + { + GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING, "iconv_open"); + GNUNET_log (GNUNET_ERROR_TYPE_WARNING, + _("Character set requested was `%s'\n"), charset); + ret = GNUNET_malloc (len + 1); + memcpy (ret, input, len); + ret[len] = '\0'; + return ret; + } + tmpSize = 3 * len + 4; + tmp = GNUNET_malloc (tmpSize); + itmp = tmp; + finSize = tmpSize; + if (iconv (cd, (char **) &input, &len, &itmp, &finSize) == (size_t) - 1) + { + GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING, "iconv"); + iconv_close (cd); + GNUNET_free (tmp); + ret = GNUNET_malloc (len + 1); + memcpy (ret, input, len); + ret[len] = '\0'; + return ret; + } + ret = GNUNET_malloc (tmpSize - finSize + 1); + memcpy (ret, tmp, tmpSize - finSize); + ret[tmpSize - finSize] = '\0'; + GNUNET_free (tmp); + if (0 != iconv_close (cd)) + GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING, "iconv_close"); + return ret; +#else + ret = GNUNET_malloc (len + 1); + memcpy (ret, input, len); + ret[len] = '\0'; + return ret; +#endif +} + + +/** + * Complete filename (a la shell) from abbrevition. + * @param fil the name of the file, may contain ~/ or + * be relative to the current directory + * @returns the full file name, + * NULL is returned on error + */ +char * +GNUNET_STRINGS_filename_expand (const char *fil) +{ + char *buffer; +#ifndef MINGW + size_t len; + size_t n; + char *fm; + const char *fil_ptr; +#else + char *fn; + long lRet; +#endif + + if (fil == NULL) + return NULL; + +#ifndef MINGW + if (fil[0] == DIR_SEPARATOR) + /* absolute path, just copy */ + return GNUNET_strdup (fil); + if (fil[0] == '~') + { + fm = getenv ("HOME"); + if (fm == NULL) + { + GNUNET_log (GNUNET_ERROR_TYPE_WARNING, + _ + ("Failed to expand `$HOME': environment variable `HOME' not set")); + return NULL; + } + fm = GNUNET_strdup (fm); + /* do not copy '~' */ + fil_ptr = fil + 1; + + /* skip over dir seperator to be consistent */ + if (fil_ptr[0] == DIR_SEPARATOR) + fil_ptr++; + } + else + { + /* relative path */ + fil_ptr = fil; + len = 512; + fm = NULL; + while (1) + { + buffer = GNUNET_malloc (len); + if (getcwd (buffer, len) != NULL) + { + fm = buffer; + break; + } + if ((errno == ERANGE) && (len < 1024 * 1024 * 4)) + { + len *= 2; + GNUNET_free (buffer); + continue; + } + GNUNET_free (buffer); + break; + } + if (fm == NULL) + { + GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING, "getcwd"); + buffer = getenv ("PWD"); /* alternative */ + if (buffer != NULL) + fm = GNUNET_strdup (buffer); + } + if (fm == NULL) + fm = GNUNET_strdup ("./"); /* give up */ + } + n = strlen (fm) + 1 + strlen (fil_ptr) + 1; + buffer = GNUNET_malloc (n); + GNUNET_snprintf (buffer, n, "%s%s%s", + fm, + (fm[strlen (fm) - 1] == + DIR_SEPARATOR) ? "" : DIR_SEPARATOR_STR, fil_ptr); + GNUNET_free (fm); + return buffer; +#else + fn = GNUNET_malloc (MAX_PATH + 1); + + if ((lRet = plibc_conv_to_win_path (fil, fn)) != ERROR_SUCCESS) + { + SetErrnoFromWinError (lRet); + GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING, + "plibc_conv_to_win_path"); + return NULL; + } + /* is the path relative? */ + if ((strncmp (fn + 1, ":\\", 2) != 0) && (strncmp (fn, "\\\\", 2) != 0)) + { + char szCurDir[MAX_PATH + 1]; + lRet = GetCurrentDirectory (MAX_PATH + 1, szCurDir); + if (lRet + strlen (fn) + 1 > (MAX_PATH + 1)) + { + SetErrnoFromWinError (ERROR_BUFFER_OVERFLOW); + GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING, + "GetCurrentDirectory"); + return NULL; + } + buffer = GNUNET_malloc (MAX_PATH + 1); + GNUNET_snprintf (buffer, MAX_PATH + 1, "%s\\%s", szCurDir, fn); + GNUNET_free (fn); + fn = buffer; + } + + return fn; +#endif +} + + +/** + * Give relative time in human-readable fancy format. + * @param delta time in milli seconds + */ +char * +GNUNET_STRINGS_relative_time_to_string (struct GNUNET_TIME_Relative del) +{ + const char *unit = _( /* time unit */ "ms"); + char *ret; + uint64_t delta = del.value; + + if (delta > 5 * 1000) + { + delta = delta / 1000; + unit = _( /* time unit */ "s"); + if (delta > 5 * 60) + { + delta = delta / 60; + unit = _( /* time unit */ "m"); + if (delta > 5 * 60) + { + delta = delta / 60; + unit = _( /* time unit */ "h"); + if (delta > 5 * 24) + { + delta = delta / 24; + unit = _( /* time unit */ " days"); + } + } + } + } + GNUNET_asprintf (&ret, "%llu%s", delta, unit); + return ret; +} + + +/** + * "man ctime_r", except for GNUnet time; also, unlike ctime, the + * return value does not include the newline character. + */ +char * +GNUNET_STRINGS_absolute_time_to_string (struct GNUNET_TIME_Absolute t) +{ + time_t tt; + char *ret; + + tt = t.value / 1000; +#ifdef ctime_r + ret = ctime_r (&tt, GNUNET_malloc (32)); +#else + ret = GNUNET_strdup (ctime (&tt)); +#endif + ret[strlen (ret) - 1] = '\0'; + return ret; +} + + + +/* end of strings.c */ diff --git a/src/util/test_client.c b/src/util/test_client.c new file mode 100644 index 000000000..5c2e552e9 --- /dev/null +++ b/src/util/test_client.c @@ -0,0 +1,195 @@ +/* + This file is part of GNUnet. + (C) 2009 Christian Grothoff (and other contributing authors) + + GNUnet is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published + by the Free Software Foundation; either version 2, 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 + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with GNUnet; see the file COPYING. If not, write to the + Free Software Foundation, Inc., 59 Temple Place - Suite 330, + Boston, MA 02111-1307, USA. +*/ +/** + * @file util/test_client.c + * @brief tests for client.c + */ +#include "platform.h" +#include "gnunet_common.h" +#include "gnunet_client_lib.h" +#include "gnunet_configuration_lib.h" +#include "gnunet_scheduler_lib.h" +#include "gnunet_server_lib.h" +#include "gnunet_time_lib.h" + +#define VERBOSE GNUNET_NO + +#define PORT 14325 + +#define MYNAME "test_client" + +static struct GNUNET_CLIENT_Connection *client; + +static struct GNUNET_SERVER_Handle *server; + +static struct GNUNET_CONFIGURATION_Handle *cfg; + +#define MY_TYPE 130 + +struct CopyContext +{ + struct GNUNET_SERVER_Client *client; + struct GNUNET_MessageHeader *cpy; +}; + +static size_t +copy_msg (void *cls, size_t size, void *buf) +{ + struct CopyContext *ctx = cls; + struct GNUNET_MessageHeader *cpy = ctx->cpy; + + GNUNET_assert (sizeof (struct GNUNET_MessageHeader) == ntohs (cpy->size)); + GNUNET_assert (size >= ntohs (cpy->size)); + memcpy (buf, cpy, ntohs (cpy->size)); + GNUNET_SERVER_receive_done (ctx->client, GNUNET_OK); + GNUNET_free (cpy); + GNUNET_free (ctx); + return sizeof (struct GNUNET_MessageHeader); +} + + +/** + * Callback that just bounces the message back to the sender. + */ +static void +echo_cb (void *cls, + struct GNUNET_SERVER_Handle *server, + struct GNUNET_SERVER_Client *client, + const struct GNUNET_MessageHeader *message) +{ + struct CopyContext *cc; + struct GNUNET_MessageHeader *cpy; + + GNUNET_assert (sizeof (struct GNUNET_MessageHeader) == + ntohs (message->size)); + cc = GNUNET_malloc (sizeof (struct CopyContext)); + cc->client = client; + cpy = GNUNET_malloc (ntohs (message->size)); + memcpy (cpy, message, ntohs (message->size)); + cc->cpy = cpy; + GNUNET_assert (NULL != + GNUNET_SERVER_notify_transmit_ready (client, + ntohs (message->size), + GNUNET_TIME_UNIT_SECONDS, + ©_msg, cc)); +} + + +static struct GNUNET_SERVER_MessageHandler handlers[] = { + {&echo_cb, NULL, MY_TYPE, sizeof (struct GNUNET_MessageHeader)}, + {NULL, NULL, 0, 0} +}; + + +static void +recv_bounce (void *cls, const struct GNUNET_MessageHeader *got) +{ + int *ok = cls; + struct GNUNET_MessageHeader msg; + + GNUNET_assert (got != NULL); /* timeout */ + msg.type = htons (MY_TYPE); + msg.size = htons (sizeof (msg)); + GNUNET_assert (0 == memcmp (got, &msg, sizeof (msg))); + GNUNET_CLIENT_disconnect (client); + client = NULL; + GNUNET_SERVER_destroy (server); + server = NULL; + *ok = 0; +} + + +static size_t +make_msg (void *cls, size_t size, void *buf) +{ + struct GNUNET_MessageHeader *msg = buf; + GNUNET_assert (size >= sizeof (struct GNUNET_MessageHeader)); + msg->type = htons (MY_TYPE); + msg->size = htons (sizeof (msg)); + return sizeof (struct GNUNET_MessageHeader); +} + + +static void +task (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc) +{ + struct sockaddr_in sa; + + memset (&sa, 0, sizeof (sa)); + sa.sin_family = AF_INET; + sa.sin_port = htons (PORT); + server = GNUNET_SERVER_create (tc->sched, + NULL, + NULL, + (const struct sockaddr *) &sa, + sizeof (sa), + 1024, + GNUNET_TIME_relative_multiply + (GNUNET_TIME_UNIT_MILLISECONDS, 250), + GNUNET_NO); + GNUNET_assert (server != NULL); + handlers[0].callback_cls = cls; + handlers[1].callback_cls = cls; + GNUNET_SERVER_add_handlers (server, handlers); + client = GNUNET_CLIENT_connect (tc->sched, MYNAME, cfg); + GNUNET_assert (client != NULL); + GNUNET_assert (NULL != + GNUNET_CLIENT_notify_transmit_ready (client, + sizeof (struct + GNUNET_MessageHeader), + GNUNET_TIME_UNIT_SECONDS, + &make_msg, NULL)); + GNUNET_CLIENT_receive (client, &recv_bounce, cls, + GNUNET_TIME_relative_multiply + (GNUNET_TIME_UNIT_MILLISECONDS, 250)); +} + + +/** + * Main method, starts scheduler with task1, + * checks that "ok" is correct at the end. + */ +static int +check () +{ + int ok; + + cfg = GNUNET_CONFIGURATION_create (); + GNUNET_CONFIGURATION_set_value_number (cfg, MYNAME, "PORT", PORT); + GNUNET_CONFIGURATION_set_value_string (cfg, + MYNAME, "HOSTNAME", "localhost"); + ok = 1; + GNUNET_SCHEDULER_run (&task, &ok); + GNUNET_CONFIGURATION_destroy (cfg); + return ok; +} + +int +main (int argc, char *argv[]) +{ + int ret = 0; + + GNUNET_log_setup ("test_client", "WARNING", NULL); + ret += check (); + + return ret; +} + +/* end of test_client.c */ diff --git a/src/util/test_common_allocation.c b/src/util/test_common_allocation.c new file mode 100644 index 000000000..76c7dc8a3 --- /dev/null +++ b/src/util/test_common_allocation.c @@ -0,0 +1,112 @@ +/* + This file is part of GNUnet. + (C) 2001, 2002, 2003, 2005, 2006 Christian Grothoff (and other contributing authors) + + GNUnet is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published + by the Free Software Foundation; either version 2, 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 + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with GNUnet; see the file COPYING. If not, write to the + Free Software Foundation, Inc., 59 Temple Place - Suite 330, + Boston, MA 02111-1307, USA. +*/ + +/** + * @file util/test_common_allocation.c + * @brief testcase for common_allocation.c + */ +#include "platform.h" +#include "gnunet_common.h" + +static int +check () +{ +#define MAX_TESTVAL 1024 + char *ptrs[MAX_TESTVAL]; + int i; + int j; + int k; + unsigned int ui; + + /* GNUNET_malloc/GNUNET_free test */ + k = 352; /* random start value */ + for (i = 1; i < MAX_TESTVAL; i++) + { + ptrs[i] = GNUNET_malloc (i); + for (j = 0; j < i; j++) + ptrs[i][j] = k++; + } + + for (i = MAX_TESTVAL - 1; i >= 1; i--) + { + for (j = i - 1; j >= 0; j--) + if (ptrs[i][j] != (char) --k) + return 1; + GNUNET_free (ptrs[i]); + } + + /* GNUNET_free_non_null test */ + GNUNET_free_non_null (NULL); + GNUNET_free_non_null (GNUNET_malloc (4)); + + /* GNUNET_strdup tests */ + ptrs[0] = GNUNET_strdup ("bar"); + if (0 != strcmp (ptrs[0], "bar")) + return 3; + /* now realloc */ + ptrs[0] = GNUNET_realloc (ptrs[0], 12); + strcpy (ptrs[0], "Hello World"); + + GNUNET_free (ptrs[0]); + GNUNET_asprintf (&ptrs[0], "%s %s", "Hello", "World"); + GNUNET_assert (strlen (ptrs[0]) == 11); + GNUNET_free (ptrs[0]); + + /* GNUNET_array_grow tests */ + ptrs[0] = NULL; + ui = 0; + GNUNET_array_grow (ptrs[0], ui, 42); + if (ui != 42) + return 4; + GNUNET_array_grow (ptrs[0], ui, 22); + if (ui != 22) + return 5; + for (j = 0; j < 22; j++) + ptrs[0][j] = j; + GNUNET_array_grow (ptrs[0], ui, 32); + for (j = 0; j < 22; j++) + if (ptrs[0][j] != j) + return 6; + for (j = 22; j < 32; j++) + if (ptrs[0][j] != 0) + return 7; + GNUNET_array_grow (ptrs[0], ui, 0); + if (i != 0) + return 8; + if (ptrs[0] != NULL) + return 9; + + + return 0; +} + +int +main (int argc, char *argv[]) +{ + int ret; + + GNUNET_log_setup ("test-common-allocation", "WARNING", NULL); + ret = check (); + if (ret != 0) + fprintf (stderr, "ERROR %d.\n", ret); + return ret; +} + +/* end of test_common_allocation.c */ diff --git a/src/util/test_common_endian.c b/src/util/test_common_endian.c new file mode 100644 index 000000000..43f163902 --- /dev/null +++ b/src/util/test_common_endian.c @@ -0,0 +1,42 @@ +/* + This file is part of GNUnet. + (C) 2009 Christian Grothoff (and other contributing authors) + + GNUnet is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published + by the Free Software Foundation; either version 2, 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 + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with GNUnet; see the file COPYING. If not, write to the + Free Software Foundation, Inc., 59 Temple Place - Suite 330, + Boston, MA 02111-1307, USA. +*/ +/** + * @file util/test_common_endian.c + * @brief testcase for common_endian.c + */ +#include "platform.h" +#include "gnunet_common.h" + +#define CHECK(n) if (n != GNUNET_htonll(GNUNET_ntohll(n))) return 1; + +int +main (int argc, char *argv[]) +{ + GNUNET_log_setup ("test-common-endian", "WARNING", NULL); + CHECK (1); + CHECK (0x12345678); + CHECK (123456789012345LL); + if ((0x1234567890ABCDEFLL != + GNUNET_htonll (0xEFCDAB9078563412LL)) && 42 != htonl (42)) + return 1; + return 0; +} + +/* end of test_common_endian.c */ diff --git a/src/util/test_common_logging.c b/src/util/test_common_logging.c new file mode 100644 index 000000000..eb337aac4 --- /dev/null +++ b/src/util/test_common_logging.c @@ -0,0 +1,100 @@ +/* + This file is part of GNUnet. + (C) 2008 Christian Grothoff (and other contributing authors) + + GNUnet is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published + by the Free Software Foundation; either version 2, 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 + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with GNUnet; see the file COPYING. If not, write to the + Free Software Foundation, Inc., 59 Temple Place - Suite 330, + Boston, MA 02111-1307, USA. +*/ + +/** + * @file util/test_common_logging.c + * @brief testcase for the logging module + * @author Christian Grothoff + */ +#include "platform.h" +#include "gnunet_common.h" + +static void +my_log (void *ctx, enum GNUNET_ErrorType kind, + const char *component, const char *date, const char *msg) +{ + unsigned int *c = ctx; + (*c)++; +} + + + +int +main (int argc, char *argv[]) +{ + unsigned int failureCount = 0; + unsigned int logs = 0; + + fclose (stderr); + stderr = NULL; + GNUNET_logger_add (&my_log, &logs); + GNUNET_logger_add (&my_log, &logs); + GNUNET_log (GNUNET_ERROR_TYPE_BULK, "Testing...\n"); + GNUNET_log (GNUNET_ERROR_TYPE_BULK, "Testing...\n"); + GNUNET_log (GNUNET_ERROR_TYPE_BULK, "Testing...\n"); + GNUNET_log (GNUNET_ERROR_TYPE_BULK, "Testing...\n"); + GNUNET_log (GNUNET_ERROR_TYPE_BULK, "Testing...\n"); + GNUNET_log (GNUNET_ERROR_TYPE_BULK, "Testing...\n"); + GNUNET_logger_remove (&my_log, &logs); + GNUNET_log (GNUNET_ERROR_TYPE_BULK, "Flusher...\n"); + /* the last 6 calls should be merged (repated bulk messages!) */ + GNUNET_logger_remove (&my_log, &logs); + if (logs != 4) + { + fprintf (stdout, "Expected 4 log calls, got %u\n", logs); + failureCount++; + } + GNUNET_break (0 == + strcmp (_("ERROR"), + GNUNET_error_type_to_string + (GNUNET_ERROR_TYPE_ERROR))); + GNUNET_break (0 == + strcmp (_("WARNING"), + GNUNET_error_type_to_string + (GNUNET_ERROR_TYPE_WARNING))); + GNUNET_break (0 == + strcmp (_("INFO"), + GNUNET_error_type_to_string + (GNUNET_ERROR_TYPE_INFO))); + GNUNET_break (0 == + strcmp (_("DEBUG"), + GNUNET_error_type_to_string + (GNUNET_ERROR_TYPE_DEBUG))); + GNUNET_log_setup ("test_common_logging", "WARNING", "/dev/null"); + logs = 0; + GNUNET_logger_add (&my_log, &logs); + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Checker...\n"); + GNUNET_log (GNUNET_ERROR_TYPE_INFO, "Drop me...\n"); + GNUNET_logger_remove (&my_log, &logs); + if (logs != 1) + { + fprintf (stdout, "Expected 1 log call, got %u\n", logs); + failureCount++; + } + + if (failureCount != 0) + { + fprintf (stdout, "%u TESTS FAILED!\n", failureCount); + return -1; + } + return 0; +} /* end of main */ + +/* end of test_common_logging.c */ diff --git a/src/util/test_configuration.c b/src/util/test_configuration.c new file mode 100644 index 000000000..2f37c684c --- /dev/null +++ b/src/util/test_configuration.c @@ -0,0 +1,229 @@ +/* + This file is part of GNUnet. + (C) 2003, 2004, 2005, 2006, 2007 Christian Grothoff (and other contributing authors) + + GNUnet is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published + by the Free Software Foundation; either version 2, 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 + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with GNUnet; see the file COPYING. If not, write to the + Free Software Foundation, Inc., 59 Temple Place - Suite 330, + Boston, MA 02111-1307, USA. +*/ +/** + * @file util/test_configuration.c + * @brief Test that the configuration module works. + * @author Christian Grothoff + */ + +#include "platform.h" +#include "gnunet_common.h" +#include "gnunet_configuration_lib.h" + +static struct GNUNET_CONFIGURATION_Handle *cfg; + +static int +testConfig () +{ + char *c; + unsigned long long l; + + if (GNUNET_OK != + GNUNET_CONFIGURATION_get_value_string (cfg, "test", "b", &c)) + return 1; + if (0 != strcmp ("b", c)) + { + fprintf (stderr, "Got `%s'\n", c); + GNUNET_free (c); + return 2; + } + GNUNET_free (c); + if (GNUNET_OK != GNUNET_CONFIGURATION_get_value_number (cfg, + "test", "five", &l)) + return 3; + if (5 != l) + return 4; + GNUNET_CONFIGURATION_set_value_string (cfg, "more", "c", "YES"); + if (GNUNET_NO == GNUNET_CONFIGURATION_get_value_yesno (cfg, "more", "c")) + return 5; + GNUNET_CONFIGURATION_set_value_number (cfg, "NUMBERS", "TEN", 10); + if (GNUNET_OK != + GNUNET_CONFIGURATION_get_value_string (cfg, "NUMBERS", "TEN", &c)) + return 6; + if (0 != strcmp (c, "10")) + { + GNUNET_free (c); + return 7; + } + GNUNET_free (c); + + if (GNUNET_OK != + GNUNET_CONFIGURATION_get_value_filename (cfg, "last", "test", &c)) + return 8; + if (0 != strcmp (c, "/hello/world")) + { + GNUNET_free (c); + return 9; + } + GNUNET_free (c); + + return 0; +} + +static const char *want[] = { + "/Hello", + "/File Name", + "/World", + NULL, + NULL, +}; + +static int +check (void *data, const char *fn) +{ + int *idx = data; + + if (0 == strcmp (want[*idx], fn)) + { + (*idx)++; + return GNUNET_OK; + } + return GNUNET_SYSERR; +} + +static int +testConfigFilenames () +{ + int idx; + + idx = 0; + if (3 != GNUNET_CONFIGURATION_iterate_value_filenames (cfg, + "FILENAMES", + "test", + &check, &idx)) + return 8; + if (idx != 3) + return 16; + if (GNUNET_OK != + GNUNET_CONFIGURATION_remove_value_filename (cfg, + "FILENAMES", + "test", "/File Name")) + return 24; + + if (GNUNET_NO != + GNUNET_CONFIGURATION_remove_value_filename (cfg, + "FILENAMES", + "test", "/File Name")) + return 32; + if (GNUNET_NO != + GNUNET_CONFIGURATION_remove_value_filename (cfg, + "FILENAMES", + "test", "Stuff")) + return 40; + + if (GNUNET_NO != + GNUNET_CONFIGURATION_append_value_filename (cfg, + "FILENAMES", + "test", "/Hello")) + return 48; + if (GNUNET_NO != + GNUNET_CONFIGURATION_append_value_filename (cfg, + "FILENAMES", + "test", "/World")) + return 56; + + if (GNUNET_YES != + GNUNET_CONFIGURATION_append_value_filename (cfg, + "FILENAMES", + "test", "/File 1")) + return 64; + + if (GNUNET_YES != + GNUNET_CONFIGURATION_append_value_filename (cfg, + "FILENAMES", + "test", "/File 2")) + return 72; + + idx = 0; + want[1] = "/World"; + want[2] = "/File 1"; + want[3] = "/File 2"; + if (4 != GNUNET_CONFIGURATION_iterate_value_filenames (cfg, + "FILENAMES", + "test", + &check, &idx)) + return 80; + if (idx != 4) + return 88; + return 0; +} + +int +main (int argc, char *argv[]) +{ + int failureCount = 0; + char *c; + + GNUNET_log_setup ("test_configuration", "WARNING", NULL); + cfg = GNUNET_CONFIGURATION_create (); + GNUNET_assert (cfg != NULL); + if (GNUNET_OK != + GNUNET_CONFIGURATION_parse (cfg, "test_configuration_data.conf")) + { + fprintf (stderr, "Failed to parse configuration file\n"); + GNUNET_CONFIGURATION_destroy (cfg); + return 1; + } + failureCount += testConfig (); + failureCount += 2 * testConfigFilenames (); + + if (GNUNET_OK != GNUNET_CONFIGURATION_write (cfg, "/tmp/gnunet-test.conf")) + { + fprintf (stderr, "Failed to write configuration file\n"); + GNUNET_CONFIGURATION_destroy (cfg); + return 1; + } + GNUNET_CONFIGURATION_destroy (cfg); + GNUNET_assert (0 == UNLINK ("/tmp/gnunet-test.conf")); + + cfg = GNUNET_CONFIGURATION_create (); + if (GNUNET_OK != + GNUNET_CONFIGURATION_load (cfg, "test_configuration_data.conf")) + { + GNUNET_break (0); + GNUNET_CONFIGURATION_destroy (cfg); + return 1; + } + if ((GNUNET_OK != + GNUNET_CONFIGURATION_get_value_string (cfg, "TESTING", "WEAKRANDOM", + &c)) + || (0 != strcmp (c, "YES"))) + { + GNUNET_CONFIGURATION_destroy (cfg); + return 1; + } + GNUNET_free (c); + if ((GNUNET_OK != + GNUNET_CONFIGURATION_get_value_string (cfg, "PATHS", "SERVICEHOME", + &c)) + || (0 != strcmp (c, "/var/lib/gnunet/"))) + { + GNUNET_CONFIGURATION_destroy (cfg); + return 1; + } + GNUNET_free (c); + GNUNET_CONFIGURATION_destroy (cfg); + if (failureCount != 0) + { + fprintf (stderr, "Test failed: %u\n", failureCount); + return 1; + } + return 0; +} diff --git a/src/util/test_configuration_data.conf b/src/util/test_configuration_data.conf new file mode 100644 index 000000000..52b6b8220 --- /dev/null +++ b/src/util/test_configuration_data.conf @@ -0,0 +1,30 @@ +[PATHS] +SUBST=/hello + +[GNUNET] +SUBST=hello +GNUNET_HOME=/tmp + +[test] +a=a +b=b +five=5 + +[more] +c=c +five=42 + + +[last] +test = $SUBST/world +boom = "1 2 3 testing" +trailing = YES + +[FILENAMES] +test = "/Hello /File\ Name /World" + + +[TESTING] +WEAKRANDOM = YES + + diff --git a/src/util/test_container_bloomfilter.c b/src/util/test_container_bloomfilter.c new file mode 100644 index 000000000..9beb11298 --- /dev/null +++ b/src/util/test_container_bloomfilter.c @@ -0,0 +1,246 @@ +/* + This file is part of GNUnet. + (C) 2004, 2009 Christian Grothoff (and other contributing authors) + + GNUnet is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published + by the Free Software Foundation; either version 2, 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 + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with GNUnet; see the file COPYING. If not, write to the + Free Software Foundation, Inc., 59 Temple Place - Suite 330, + Boston, MA 02111-1307, USA. +*/ +/** + * @file util/test_container_bloomfilter.c + * @brief Testcase for the bloomfilter. + * @author Christian Grothoff + * @author Igor Wronsky + */ + +#include "platform.h" +#include "gnunet_common.h" +#include "gnunet_container_lib.h" + +#define K 4 +#define SIZE 65536 +#define TESTFILE "/tmp/bloomtest.dat" + +/** + * Generate a random hashcode. + */ +static void +nextHC (GNUNET_HashCode * hc) +{ + GNUNET_CRYPTO_hash_create_random (hc); +} + +static int +add_iterator (GNUNET_HashCode * next, void *arg) +{ + int *ret = arg; + GNUNET_HashCode pos; + + if (0 == (*ret)--) + return GNUNET_NO; + nextHC (&pos); + *next = pos; + return GNUNET_YES; +} + +int +main (int argc, char *argv[]) +{ + struct GNUNET_CONTAINER_BloomFilter *bf; + struct GNUNET_CONTAINER_BloomFilter *bfi; + GNUNET_HashCode tmp; + int i; + int ok1; + int ok2; + int falseok; + char buf[SIZE]; + struct stat sbuf; + + GNUNET_log_setup ("test-container-bloomfilter", "WARNING", NULL); + srand (1); + if (0 == stat (TESTFILE, &sbuf)) + if (0 != UNLINK (TESTFILE)) + GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_ERROR, "unlink", TESTFILE); + bf = GNUNET_CONTAINER_bloomfilter_load (TESTFILE, SIZE, K); + + for (i = 0; i < 200; i++) + { + nextHC (&tmp); + GNUNET_CONTAINER_bloomfilter_add (bf, &tmp); + } + srand (1); + ok1 = 0; + for (i = 0; i < 200; i++) + { + nextHC (&tmp); + if (GNUNET_CONTAINER_bloomfilter_test (bf, &tmp) == GNUNET_YES) + ok1++; + } + if (ok1 != 200) + { + printf ("Got %d elements out of" + "200 expected after insertion.\n", ok1); + GNUNET_CONTAINER_bloomfilter_free (bf); + return -1; + } + if (GNUNET_OK != GNUNET_CONTAINER_bloomfilter_get_raw_data (bf, buf, SIZE)) + { + GNUNET_CONTAINER_bloomfilter_free (bf); + return -1; + } + + GNUNET_CONTAINER_bloomfilter_free (bf); + + bf = GNUNET_CONTAINER_bloomfilter_load (TESTFILE, SIZE, K); + GNUNET_assert (bf != NULL); + bfi = GNUNET_CONTAINER_bloomfilter_init (buf, SIZE, K); + GNUNET_assert (bfi != NULL); + + srand (1); + ok1 = 0; + ok2 = 0; + for (i = 0; i < 200; i++) + { + nextHC (&tmp); + if (GNUNET_CONTAINER_bloomfilter_test (bf, &tmp) == GNUNET_YES) + ok1++; + if (GNUNET_CONTAINER_bloomfilter_test (bfi, &tmp) == GNUNET_YES) + ok2++; + } + if (ok1 != 200) + { + printf ("Got %d elements out of 200 " + "expected after reloading.\n", ok1); + GNUNET_CONTAINER_bloomfilter_free (bf); + GNUNET_CONTAINER_bloomfilter_free (bfi); + return -1; + } + + if (ok2 != 200) + { + printf ("Got %d elements out of 200 " + "expected after initialization.\n", ok2); + GNUNET_CONTAINER_bloomfilter_free (bf); + GNUNET_CONTAINER_bloomfilter_free (bfi); + return -1; + } + + srand (1); + for (i = 0; i < 100; i++) + { + nextHC (&tmp); + GNUNET_CONTAINER_bloomfilter_remove (bf, &tmp); + GNUNET_CONTAINER_bloomfilter_remove (bfi, &tmp); + } + + srand (1); + + ok1 = 0; + ok2 = 0; + for (i = 0; i < 200; i++) + { + nextHC (&tmp); + if (GNUNET_CONTAINER_bloomfilter_test (bf, &tmp) == GNUNET_YES) + ok1++; + if (GNUNET_CONTAINER_bloomfilter_test (bfi, &tmp) == GNUNET_YES) + ok2++; + } + + if (ok1 != 100) + { + printf ("Expected 100 elements in loaded filter" + " after adding 200 and deleting 100, got %d\n", ok1); + GNUNET_CONTAINER_bloomfilter_free (bf); + GNUNET_CONTAINER_bloomfilter_free (bfi); + return -1; + } + if (ok2 != 200) + { + printf ("Expected 200 elements in initialized filter" + " after adding 200 and deleting 100 " + "(which should do nothing for a filter not backed by a file), got %d\n", + ok2); + GNUNET_CONTAINER_bloomfilter_free (bf); + GNUNET_CONTAINER_bloomfilter_free (bfi); + return -1; + } + + srand (3); + + GNUNET_CONTAINER_bloomfilter_clear (bf); + falseok = 0; + for (i = 0; i < 1000; i++) + { + nextHC (&tmp); + if (GNUNET_CONTAINER_bloomfilter_test (bf, &tmp) == GNUNET_YES) + falseok++; + } + if (falseok > 0) + { + GNUNET_CONTAINER_bloomfilter_free (bf); + GNUNET_CONTAINER_bloomfilter_free (bfi); + return -1; + } + + if (GNUNET_OK != GNUNET_CONTAINER_bloomfilter_or (bf, buf, SIZE)) + { + GNUNET_CONTAINER_bloomfilter_free (bf); + GNUNET_CONTAINER_bloomfilter_free (bfi); + return -1; + } + + srand (2); + i = 20; + GNUNET_CONTAINER_bloomfilter_resize (bfi, &add_iterator, &i, SIZE * 2, K); + + srand (2); + i = 20; + GNUNET_CONTAINER_bloomfilter_resize (bf, &add_iterator, &i, SIZE * 2, K); + srand (2); + + ok1 = 0; + ok2 = 0; + for (i = 0; i < 20; i++) + { + nextHC (&tmp); + if (GNUNET_CONTAINER_bloomfilter_test (bf, &tmp) == GNUNET_YES) + ok1++; + if (GNUNET_CONTAINER_bloomfilter_test (bfi, &tmp) == GNUNET_YES) + ok2++; + } + + if (ok1 != 20) + { + printf ("Expected 20 elements in resized file-backed filter" + " after adding 20, got %d\n", ok1); + GNUNET_CONTAINER_bloomfilter_free (bf); + GNUNET_CONTAINER_bloomfilter_free (bfi); + return -1; + } + if (ok2 != 20) + { + printf ("Expected 20 elements in resized filter" + " after adding 20, got %d\n", ok2); + GNUNET_CONTAINER_bloomfilter_free (bf); + GNUNET_CONTAINER_bloomfilter_free (bfi); + return -1; + } + + + GNUNET_CONTAINER_bloomfilter_free (bf); + GNUNET_CONTAINER_bloomfilter_free (bfi); + + GNUNET_break (0 == UNLINK (TESTFILE)); + return 0; +} diff --git a/src/util/test_container_heap.c b/src/util/test_container_heap.c new file mode 100644 index 000000000..511589af5 --- /dev/null +++ b/src/util/test_container_heap.c @@ -0,0 +1,112 @@ +/* + This file is part of GNUnet. + (C) 2008 Christian Grothoff (and other contributing authors) + + GNUnet is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published + by the Free Software Foundation; either version 2, 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 + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with GNUnet; see the file COPYING. If not, write to the + Free Software Foundation, Inc., 59 Temple Place - Suite 330, + Boston, MA 02111-1307, USA. + */ + +/** + * @author Nathan Evans + * @file util/containers/heaptest.c + * @brief Test of heap operations + */ + +#include "gnunet_util.h" +#include "gnunet_util_containers.h" +#include "dv.h" + +static int +iterator_callback (void *element, GNUNET_CONTAINER_HeapCost cost, + struct GNUNET_CONTAINER_Heap *root, void *cls) +{ + struct GNUNET_dv_neighbor *node; + node = (struct GNUNET_dv_neighbor *) element; + fprintf (stdout, "%d\n", node->cost); + //fprintf (stdout, "%d\n", ((struct GNUNET_dv_neighbor *)element)->cost); + + return GNUNET_OK; +} + + +int +main (int argc, char **argv) +{ + struct GNUNET_CONTAINER_Heap *myHeap; + struct GNUNET_dv_neighbor *neighbor1; + struct GNUNET_dv_neighbor *neighbor2; + struct GNUNET_dv_neighbor *neighbor3; + struct GNUNET_dv_neighbor *neighbor4; + struct GNUNET_dv_neighbor *neighbor5; + struct GNUNET_dv_neighbor *neighbor6; + + GNUNET_log_setup ("test-container-heap", "WARNING", NULL); + + myHeap = GNUNET_CONTAINER_heap_create (GNUNET_CONTAINER_HEAP_ORDER_MAX); + + neighbor1 = malloc (sizeof (struct GNUNET_dv_neighbor)); + neighbor2 = malloc (sizeof (struct GNUNET_dv_neighbor)); + neighbor3 = malloc (sizeof (struct GNUNET_dv_neighbor)); + neighbor4 = malloc (sizeof (struct GNUNET_dv_neighbor)); + neighbor5 = malloc (sizeof (struct GNUNET_dv_neighbor)); + neighbor6 = malloc (sizeof (struct GNUNET_dv_neighbor)); + + neighbor1->cost = 60; + neighbor2->cost = 50; + neighbor3->cost = 70; + neighbor4->cost = 120; + neighbor5->cost = 100; + neighbor6->cost = 30; + + GNUNET_CONTAINER_heap_insert (myHeap, neighbor1, neighbor1->cost); + GNUNET_CONTAINER_heap_iterate (myHeap, iterator_callback, NULL); + + fprintf (stdout, "\n"); + GNUNET_CONTAINER_heap_insert (myHeap, neighbor2, neighbor2->cost); + + GNUNET_CONTAINER_heap_iterate (myHeap, iterator_callback, NULL); + fprintf (stdout, "\n"); + GNUNET_CONTAINER_heap_insert (myHeap, neighbor3, neighbor3->cost); + + GNUNET_CONTAINER_heap_iterate (myHeap, iterator_callback, NULL); + fprintf (stdout, "\n"); + GNUNET_CONTAINER_heap_insert (myHeap, neighbor4, neighbor4->cost); + + GNUNET_CONTAINER_heap_iterate (myHeap, iterator_callback, NULL); + fprintf (stdout, "\n"); + GNUNET_CONTAINER_heap_insert (myHeap, neighbor5, neighbor5->cost); + + GNUNET_CONTAINER_heap_iterate (myHeap, iterator_callback, NULL); + fprintf (stdout, "\n"); + GNUNET_CONTAINER_heap_insert (myHeap, neighbor6, neighbor6->cost); + + GNUNET_CONTAINER_heap_iterate (myHeap, iterator_callback, NULL); + fprintf (stdout, "\n"); + GNUNET_CONTAINER_heap_remove_node (myHeap, neighbor5); + + GNUNET_CONTAINER_heap_iterate (myHeap, iterator_callback, NULL); + fprintf (stdout, "\n"); + GNUNET_CONTAINER_heap_remove_root (myHeap); + + GNUNET_CONTAINER_heap_iterate (myHeap, iterator_callback, NULL); + fprintf (stdout, "\n"); + GNUNET_CONTAINER_heap_update_cost (myHeap, neighbor6, 200); + + GNUNET_CONTAINER_heap_iterate (myHeap, iterator_callback, NULL); + fprintf (stdout, "\n"); + return 0; +} + +/* end of heaptest.c */ diff --git a/src/util/test_container_meta_data.c b/src/util/test_container_meta_data.c new file mode 100644 index 000000000..17ef79161 --- /dev/null +++ b/src/util/test_container_meta_data.c @@ -0,0 +1,262 @@ +/* + This file is part of GNUnet. + (C) 2003, 2004, 2006, 2009 Christian Grothoff (and other contributing authors) + + GNUnet is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published + by the Free Software Foundation; either version 2, 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 + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with GNUnet; see the file COPYING. If not, write to the + Free Software Foundation, Inc., 59 Temple Place - Suite 330, + Boston, MA 02111-1307, USA. +*/ + +/** + * @file util/test_container_meta_data.c + * @brief Test for container_meta_data.c + * @author Christian Grothoff + */ + +#include "platform.h" +#include "gnunet_common.h" +#include "gnunet_container_lib.h" + +#define ABORT(m) { fprintf(stderr, "Error at %s:%d\n", __FILE__, __LINE__); if (m != NULL) GNUNET_CONTAINER_meta_data_destroy(m); return 1; } + +static int +testMeta (int i) +{ + struct GNUNET_CONTAINER_MetaData *m; + char *val; + int j; + unsigned int size; + + m = GNUNET_CONTAINER_meta_data_create (); + if (GNUNET_OK != + GNUNET_CONTAINER_meta_data_insert (m, EXTRACTOR_TITLE, "TestTitle")) + ABORT (m); + if (GNUNET_OK != + GNUNET_CONTAINER_meta_data_insert (m, EXTRACTOR_AUTHOR, "TestTitle")) + ABORT (m); + if (GNUNET_OK == GNUNET_CONTAINER_meta_data_insert (m, EXTRACTOR_TITLE, "TestTitle")) /* dup! */ + ABORT (m); + if (GNUNET_OK == GNUNET_CONTAINER_meta_data_insert (m, EXTRACTOR_AUTHOR, "TestTitle")) /* dup! */ + ABORT (m); + if (2 != GNUNET_CONTAINER_meta_data_get_contents (m, NULL, NULL)) + ABORT (m); + if (GNUNET_OK != + GNUNET_CONTAINER_meta_data_delete (m, EXTRACTOR_AUTHOR, "TestTitle")) + ABORT (m); + if (GNUNET_OK == GNUNET_CONTAINER_meta_data_delete (m, EXTRACTOR_AUTHOR, "TestTitle")) /* already gone */ + ABORT (m); + if (1 != GNUNET_CONTAINER_meta_data_get_contents (m, NULL, NULL)) + ABORT (m); + if (GNUNET_OK != + GNUNET_CONTAINER_meta_data_delete (m, EXTRACTOR_TITLE, "TestTitle")) + ABORT (m); + if (GNUNET_OK == GNUNET_CONTAINER_meta_data_delete (m, EXTRACTOR_TITLE, "TestTitle")) /* already gone */ + ABORT (m); + if (0 != GNUNET_CONTAINER_meta_data_get_contents (m, NULL, NULL)) + ABORT (m); + val = GNUNET_malloc (256); + for (j = 0; j < i; j++) + { + GNUNET_snprintf (val, 256, "%s.%d", + "A teststring that should compress well.", j); + if (GNUNET_OK != + GNUNET_CONTAINER_meta_data_insert (m, EXTRACTOR_UNKNOWN, val)) + { + GNUNET_free (val); + ABORT (m); + } + } + GNUNET_free (val); + if (i != GNUNET_CONTAINER_meta_data_get_contents (m, NULL, NULL)) + ABORT (m); + + size = + GNUNET_CONTAINER_meta_data_get_serialized_size (m, + GNUNET_CONTAINER_META_DATA_SERIALIZE_FULL); + val = GNUNET_malloc (size); + if (size != GNUNET_CONTAINER_meta_data_serialize (m, val, size, + GNUNET_CONTAINER_META_DATA_SERIALIZE_FULL)) + { + GNUNET_free (val); + ABORT (m); + } + GNUNET_CONTAINER_meta_data_destroy (m); + m = GNUNET_CONTAINER_meta_data_deserialize (val, size); + GNUNET_free (val); + if (m == NULL) + ABORT (m); + val = GNUNET_malloc (256); + for (j = 0; j < i; j++) + { + GNUNET_snprintf (val, 256, "%s.%d", + "A teststring that should compress well.", j); + if (GNUNET_OK != + GNUNET_CONTAINER_meta_data_delete (m, EXTRACTOR_UNKNOWN, val)) + { + GNUNET_free (val); + ABORT (m); + } + } + GNUNET_free (val); + if (0 != GNUNET_CONTAINER_meta_data_get_contents (m, NULL, NULL)) + { + ABORT (m); + } + GNUNET_CONTAINER_meta_data_destroy (m); + return 0; +} + +int +testMetaMore (int i) +{ + struct GNUNET_CONTAINER_MetaData *meta; + int q; + char txt[128]; + char *data; + unsigned long long size; + + meta = GNUNET_CONTAINER_meta_data_create (); + for (q = 0; q <= i; q++) + { + GNUNET_snprintf (txt, 128, "%u -- %u\n", i, q); + GNUNET_CONTAINER_meta_data_insert (meta, + q % + EXTRACTOR_getHighestKeywordTypeNumber + (), txt); + } + size = + GNUNET_CONTAINER_meta_data_get_serialized_size (meta, + GNUNET_CONTAINER_META_DATA_SERIALIZE_FULL); + data = GNUNET_malloc (size * 4); + if (size != GNUNET_CONTAINER_meta_data_serialize (meta, + data, size * 4, + GNUNET_CONTAINER_META_DATA_SERIALIZE_FULL)) + { + GNUNET_free (data); + ABORT (meta); + } + GNUNET_CONTAINER_meta_data_destroy (meta); + GNUNET_free (data); + return 0; +} + +static int +testMetaLink () +{ + struct GNUNET_CONTAINER_MetaData *m; + char *val; + unsigned int size; + + m = GNUNET_CONTAINER_meta_data_create (); + if (GNUNET_OK != + GNUNET_CONTAINER_meta_data_insert (m, EXTRACTOR_UNKNOWN, "link")) + ABORT (m); + if (GNUNET_OK != + GNUNET_CONTAINER_meta_data_insert (m, EXTRACTOR_FILENAME, + "lib-link.m4")) + ABORT (m); + size = + GNUNET_CONTAINER_meta_data_get_serialized_size (m, + GNUNET_CONTAINER_META_DATA_SERIALIZE_FULL); + val = GNUNET_malloc (size); + if (size != GNUNET_CONTAINER_meta_data_serialize (m, val, size, + GNUNET_CONTAINER_META_DATA_SERIALIZE_FULL)) + { + GNUNET_free (val); + ABORT (m); + } + GNUNET_CONTAINER_meta_data_destroy (m); + m = GNUNET_CONTAINER_meta_data_deserialize (val, size); + GNUNET_free (val); + if (m == NULL) + ABORT (m); + GNUNET_CONTAINER_meta_data_destroy (m); + return 0; +} + + +static int +testThumbnail () +{ + struct GNUNET_CONTAINER_MetaData *m; + struct GNUNET_CONTAINER_MetaData *d; + EXTRACTOR_ExtractorList *ex; + unsigned char *thumb; + size_t size; + char *date; + + ex = EXTRACTOR_loadConfigLibraries (NULL, "libextractor_thumbnail"); + if (ex == NULL) + { + fprintf (stderr, + "Test incomplete, have no thumbnail extractor available.\n"); + return 0; /* can not test, no thumbnailer */ + } + ex = EXTRACTOR_loadConfigLibraries (ex, "libextractor_mime"); + m = GNUNET_CONTAINER_meta_data_create (); + if (3 != GNUNET_CONTAINER_meta_data_extract_from_file (m, + "test_container_meta_data_image.jpg", + ex)) + { + GNUNET_break (0); + EXTRACTOR_removeAll (ex); + GNUNET_CONTAINER_meta_data_destroy (m); + return 1; + } + EXTRACTOR_removeAll (ex); + d = GNUNET_CONTAINER_meta_data_duplicate (m); + GNUNET_CONTAINER_meta_data_destroy (m); + size = GNUNET_CONTAINER_meta_data_get_thumbnail (d, &thumb); + if (size == 0) + { + GNUNET_break (0); + GNUNET_CONTAINER_meta_data_destroy (d); + return 1; + } + GNUNET_free (thumb); + GNUNET_CONTAINER_meta_data_add_publication_date (d); + date = GNUNET_CONTAINER_meta_data_get_by_type (d, + EXTRACTOR_PUBLICATION_DATE); + if (date == NULL) + { + GNUNET_break (0); + GNUNET_CONTAINER_meta_data_destroy (d); + return 1; + } + GNUNET_free (date); + GNUNET_CONTAINER_meta_data_destroy (d); + return 0; +} + + +int +main (int argc, char *argv[]) +{ + int failureCount = 0; + int i; + + GNUNET_log_setup ("test-container-meta-data", "WARNING", NULL); + for (i = 0; i < 255; i++) + failureCount += testMeta (i); + for (i = 1; i < 255; i++) + failureCount += testMetaMore (i); + failureCount += testMetaLink (); + failureCount += testThumbnail (); + + if (failureCount != 0) + return 1; + return 0; +} + +/* end of metatest.c */ diff --git a/src/util/test_container_meta_data_image.jpg b/src/util/test_container_meta_data_image.jpg new file mode 100644 index 000000000..3d1ba3307 Binary files /dev/null and b/src/util/test_container_meta_data_image.jpg differ diff --git a/src/util/test_container_multihashmap.c b/src/util/test_container_multihashmap.c new file mode 100644 index 000000000..e92ab49ba --- /dev/null +++ b/src/util/test_container_multihashmap.c @@ -0,0 +1,113 @@ +/* + This file is part of GNUnet. + (C) 2008 Christian Grothoff (and other contributing authors) + + GNUnet is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published + by the Free Software Foundation; either version 2, 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 + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with GNUnet; see the file COPYING. If not, write to the + Free Software Foundation, Inc., 59 Temple Place - Suite 330, + Boston, MA 02111-1307, USA. +*/ + +/** + * @file util/test_container_multihashmap.c + * @brief Test for container_multihashmap.c + * @author Christian Grothoff + */ + +#include "platform.h" +#include "gnunet_common.h" +#include "gnunet_container_lib.h" + +#define ABORT() { fprintf(stderr, "Error at %s:%d\n", __FILE__, __LINE__); if (m != NULL) GNUNET_CONTAINER_multihashmap_destroy(m); return 1; } +#define CHECK(c) { if (! (c)) ABORT(); } + +static int +testMap (int i) +{ + struct GNUNET_CONTAINER_MultiHashMap *m; + GNUNET_HashCode k1; + GNUNET_HashCode k2; + int j; + void *r; + + CHECK (NULL != (m = GNUNET_CONTAINER_multihashmap_create (i))); + memset (&k1, 0, sizeof (k1)); + memset (&k2, 1, sizeof (k2)); + CHECK (GNUNET_NO == GNUNET_CONTAINER_multihashmap_contains (m, &k1)); + CHECK (GNUNET_NO == GNUNET_CONTAINER_multihashmap_contains (m, &k2)); + CHECK (GNUNET_NO == GNUNET_CONTAINER_multihashmap_remove (m, &k1, NULL)); + CHECK (GNUNET_NO == GNUNET_CONTAINER_multihashmap_remove (m, &k2, NULL)); + CHECK (NULL == GNUNET_CONTAINER_multihashmap_get (m, &k1)); + CHECK (NULL == GNUNET_CONTAINER_multihashmap_get (m, &k2)); + CHECK (0 == GNUNET_CONTAINER_multihashmap_remove_all (m, &k1)); + CHECK (0 == GNUNET_CONTAINER_multihashmap_size (m)); + CHECK (0 == GNUNET_CONTAINER_multihashmap_iterate (m, NULL, NULL)); + CHECK (0 == + GNUNET_CONTAINER_multihashmap_get_multiple (m, &k1, NULL, NULL)); + + CHECK (GNUNET_OK == GNUNET_CONTAINER_multihashmap_put (m, + &k1, + "v1", + GNUNET_CONTAINER_MULTIHASHMAPOPTION_REPLACE)); + CHECK (1 == GNUNET_CONTAINER_multihashmap_size (m)); + CHECK (0 == strcmp ("v1", GNUNET_CONTAINER_multihashmap_get (m, &k1))); + CHECK (GNUNET_NO == GNUNET_CONTAINER_multihashmap_put (m, + &k1, + "v1", + GNUNET_CONTAINER_MULTIHASHMAPOPTION_REPLACE)); + CHECK (1 == GNUNET_CONTAINER_multihashmap_size (m)); + CHECK (GNUNET_OK == GNUNET_CONTAINER_multihashmap_put (m, + &k1, + "v2", + GNUNET_CONTAINER_MULTIHASHMAPOPTION_MULTIPLE)); + CHECK (GNUNET_OK == GNUNET_CONTAINER_multihashmap_put (m, + &k1, + "v3", + GNUNET_CONTAINER_MULTIHASHMAPOPTION_MULTIPLE)); + CHECK (3 == GNUNET_CONTAINER_multihashmap_size (m)); + CHECK (GNUNET_OK == GNUNET_CONTAINER_multihashmap_remove (m, &k1, "v3")); + CHECK (2 == GNUNET_CONTAINER_multihashmap_size (m)); + CHECK (GNUNET_YES == GNUNET_CONTAINER_multihashmap_contains (m, &k1)); + CHECK (GNUNET_NO == GNUNET_CONTAINER_multihashmap_contains (m, &k2)); + CHECK (2 == + GNUNET_CONTAINER_multihashmap_get_multiple (m, &k1, NULL, NULL)); + CHECK (0 == + GNUNET_CONTAINER_multihashmap_get_multiple (m, &k2, NULL, NULL)); + CHECK (2 == GNUNET_CONTAINER_multihashmap_iterate (m, NULL, NULL)); + r = GNUNET_CONTAINER_multihashmap_get_random (m); + CHECK (0 == strcmp (r, "v1") || 0 == strcmp (r, "v2")); + CHECK (2 == GNUNET_CONTAINER_multihashmap_remove_all (m, &k1)); + for (j = 0; j < 1024; j++) + CHECK (GNUNET_OK == GNUNET_CONTAINER_multihashmap_put (m, + &k1, + "v2", + GNUNET_CONTAINER_MULTIHASHMAPOPTION_MULTIPLE)); + GNUNET_CONTAINER_multihashmap_destroy (m); + return 0; +} + +int +main (int argc, char *argv[]) +{ + int failureCount = 0; + int i; + + GNUNET_log_setup ("test-container-multihashmap", "WARNING", NULL); + for (i = 1; i < 255; i++) + failureCount += testMap (i); + if (failureCount != 0) + return 1; + return 0; +} + +/* end of maptest.c */ diff --git a/src/util/test_crypto_aes.c b/src/util/test_crypto_aes.c new file mode 100644 index 000000000..cdae243e0 --- /dev/null +++ b/src/util/test_crypto_aes.c @@ -0,0 +1,180 @@ +/* + This file is part of GNUnet. + (C) 2002, 2003, 2004, 2006 Christian Grothoff (and other contributing authors) + + GNUnet is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published + by the Free Software Foundation; either version 2, 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 + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with GNUnet; see the file COPYING. If not, write to the + Free Software Foundation, Inc., 59 Temple Place - Suite 330, + Boston, MA 02111-1307, USA. + +*/ +/** + * @author Christian Grothoff + * @file util/test_crypto_aes.c + * @brief test for AES ciphers + */ +#include "platform.h" +#include "gnunet_common.h" +#include "gnunet_crypto_lib.h" + +#define TESTSTRING "Hello World!" +#define INITVALUE "InitializationVectorValue" + +static int +testSymcipher () +{ + struct GNUNET_CRYPTO_AesSessionKey key; + char result[100]; + int size; + char res[100]; + + GNUNET_CRYPTO_aes_create_session_key (&key); + size = GNUNET_CRYPTO_aes_encrypt (TESTSTRING, + strlen (TESTSTRING) + 1, + &key, + (const struct + GNUNET_CRYPTO_AesInitializationVector *) + INITVALUE, result); + if (size == -1) + { + printf ("symciphertest failed: encryptBlock returned %d\n", size); + return 1; + } + size = GNUNET_CRYPTO_aes_decrypt (&key, + result, size, + (const struct + GNUNET_CRYPTO_AesInitializationVector *) + INITVALUE, res); + if (strlen (TESTSTRING) + 1 != size) + { + printf ("symciphertest failed: decryptBlock returned %d\n", size); + return 1; + } + if (0 != strcmp (res, TESTSTRING)) + { + printf ("symciphertest failed: %s != %s\n", res, TESTSTRING); + return 1; + } + else + return 0; +} + +int +verifyCrypto () +{ + struct GNUNET_CRYPTO_AesSessionKey key; + char result[GNUNET_CRYPTO_AES_KEY_LENGTH]; + char *res; + int ret; + + unsigned char plain[] = + { 29, 128, 192, 253, 74, 171, 38, 187, 84, 219, 76, 76, 209, 118, 33, 249, + 172, 124, 96, 9, 157, 110, 8, 215, 200, 63, 69, 230, 157, 104, 247, 164 + }; + unsigned char raw_key[] = + { 106, 74, 209, 88, 145, 55, 189, 135, 125, 180, 225, 108, 183, 54, 25, + 169, 129, 188, 131, 75, 227, 245, 105, 10, 225, 15, 115, 159, 148, 184, + 34, 191 + }; + unsigned char encrresult[] = + { 167, 102, 230, 233, 127, 195, 176, 107, 17, 91, 199, 127, 96, 113, 75, + 195, 245, 217, 61, 236, 159, 165, 103, 121, 203, 99, 202, 41, 23, 222, 25, + 102, 1 + }; + + res = NULL; + ret = 0; + + memcpy (key.key, raw_key, GNUNET_CRYPTO_AES_KEY_LENGTH); + key.crc32 = + htonl (GNUNET_CRYPTO_crc32_n (&key, GNUNET_CRYPTO_AES_KEY_LENGTH)); + + if (ntohl (key.crc32) != (unsigned int) 38125195LL) + { + printf ("Static key has different CRC: %u - %u\n", + ntohl (key.crc32), key.crc32); + + ret = 1; + goto error; + } + + if (GNUNET_CRYPTO_AES_KEY_LENGTH != + GNUNET_CRYPTO_aes_encrypt (plain, + GNUNET_CRYPTO_AES_KEY_LENGTH, + &key, + (const struct + GNUNET_CRYPTO_AesInitializationVector *) + "testtesttesttest", result)) + { + printf ("Wrong return value from encrypt block.\n"); + ret = 1; + goto error; + } + + if (memcmp (encrresult, result, GNUNET_CRYPTO_AES_KEY_LENGTH) != 0) + { + printf ("Encrypted result wrong.\n"); + ret = 1; + goto error; + } + + res = GNUNET_malloc (GNUNET_CRYPTO_AES_KEY_LENGTH); + + if (GNUNET_CRYPTO_AES_KEY_LENGTH != + GNUNET_CRYPTO_aes_decrypt (&key, + result, + GNUNET_CRYPTO_AES_KEY_LENGTH, + (const struct + GNUNET_CRYPTO_AesInitializationVector *) + "testtesttesttest", res)) + { + printf ("Wrong return value from decrypt block.\n"); + ret = 1; + goto error; + } + + if (memcmp (res, plain, GNUNET_CRYPTO_AES_KEY_LENGTH) != 0) + { + printf ("Decrypted result does not match input.\n"); + + ret = 1; + } + +error: + + GNUNET_free_non_null (res); + + return ret; +} + +int +main (int argc, char *argv[]) +{ + int failureCount = 0; + + GNUNET_log_setup ("test-crypto-aes", "WARNING", NULL); + GNUNET_CRYPTO_random_disable_entropy_gathering (); + GNUNET_assert (strlen (INITVALUE) > + sizeof (struct GNUNET_CRYPTO_AesInitializationVector)); + failureCount += testSymcipher (); + failureCount += verifyCrypto (); + + if (failureCount != 0) + { + printf ("%d TESTS FAILED!\n", failureCount); + return -1; + } + return 0; +} + +/* end of test_crypto_aes.c */ diff --git a/src/util/test_crypto_aes_weak.c b/src/util/test_crypto_aes_weak.c new file mode 100644 index 000000000..27ee57968 --- /dev/null +++ b/src/util/test_crypto_aes_weak.c @@ -0,0 +1,203 @@ +/* + This file is part of GNUnet. + (C) 2003, 2004, 2005, 2006 Christian Grothoff (and other contributing authors) + + GNUnet is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published + by the Free Software Foundation; either version 2, 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 + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with GNUnet; see the file COPYING. If not, write to the + Free Software Foundation, Inc., 59 Temple Place - Suite 330, + Boston, MA 02111-1307, USA. + +*/ + +/** + * @author Krista Bennett + * @author Christian Grothoff + * @file util/test_crypto_aes_weak.c + * @brief AES weak key test. + */ +#include "platform.h" +#include "gnunet_common.h" +#include "gnunet_crypto_lib.h" +#include + +#define MAX_WEAK_KEY_TRIALS 100000 +#define GENERATE_WEAK_KEYS GNUNET_NO +#define WEAK_KEY_TESTSTRING "I hate weak keys." + +static void +printWeakKey (struct GNUNET_CRYPTO_AesSessionKey *key) +{ + int i; + for (i = 0; i < GNUNET_CRYPTO_AES_KEY_LENGTH; i++) + { + printf ("%x ", (int) (key->key[i])); + } +} + +static int +testWeakKey () +{ + char result[100]; + char res[100]; + int size; + struct GNUNET_CRYPTO_AesSessionKey weak_key; + struct GNUNET_CRYPTO_AesInitializationVector INITVALUE; + + memset (&INITVALUE, 42, + sizeof (struct GNUNET_CRYPTO_AesInitializationVector)); + /* sorry, this is not a weak key -- I don't have + any at the moment! */ + weak_key.key[0] = (char) (0x4c); + weak_key.key[1] = (char) (0x31); + weak_key.key[2] = (char) (0xc6); + weak_key.key[3] = (char) (0x2b); + weak_key.key[4] = (char) (0xc1); + weak_key.key[5] = (char) (0x5f); + weak_key.key[6] = (char) (0x4d); + weak_key.key[7] = (char) (0x1f); + weak_key.key[8] = (char) (0x31); + weak_key.key[9] = (char) (0xaa); + weak_key.key[10] = (char) (0x12); + weak_key.key[11] = (char) (0x2e); + weak_key.key[12] = (char) (0xb7); + weak_key.key[13] = (char) (0x82); + weak_key.key[14] = (char) (0xc0); + weak_key.key[15] = (char) (0xb6); + weak_key.key[16] = (char) (0x4d); + weak_key.key[17] = (char) (0x1f); + weak_key.key[18] = (char) (0x31); + weak_key.key[19] = (char) (0xaa); + weak_key.key[20] = (char) (0x4c); + weak_key.key[21] = (char) (0x31); + weak_key.key[22] = (char) (0xc6); + weak_key.key[23] = (char) (0x2b); + weak_key.key[24] = (char) (0xc1); + weak_key.key[25] = (char) (0x5f); + weak_key.key[26] = (char) (0x4d); + weak_key.key[27] = (char) (0x1f); + weak_key.key[28] = (char) (0x31); + weak_key.key[29] = (char) (0xaa); + weak_key.key[30] = (char) (0xaa); + weak_key.key[31] = (char) (0xaa); + /* memset(&weak_key, 0, 32); */ + weak_key.crc32 = + htonl (GNUNET_CRYPTO_crc32_n (&weak_key, GNUNET_CRYPTO_AES_KEY_LENGTH)); + + size = GNUNET_CRYPTO_aes_encrypt (WEAK_KEY_TESTSTRING, + strlen (WEAK_KEY_TESTSTRING) + 1, + &weak_key, &INITVALUE, result); + + if (size == -1) + { + GNUNET_break (0); + return 1; + } + + size = GNUNET_CRYPTO_aes_decrypt (&weak_key, result, size, &INITVALUE, res); + + if ((strlen (WEAK_KEY_TESTSTRING) + 1) != size) + { + GNUNET_break (0); + return 1; + } + if (0 != strcmp (res, WEAK_KEY_TESTSTRING)) + { + GNUNET_break (0); + return 1; + } + else + return 0; +} + +static int +getWeakKeys () +{ + struct GNUNET_CRYPTO_AesSessionKey sessionkey; + int number_of_weak_keys = 0; + int number_of_runs; + + gcry_cipher_hd_t handle; + int rc; + + for (number_of_runs = 0; number_of_runs < MAX_WEAK_KEY_TRIALS; + number_of_runs++) + { + + if (number_of_runs % 1000 == 0) + fprintf (stderr, "."); + /*printf("Got to run number %d.\n", number_of_runs); */ + GNUNET_CRYPTO_aes_create_session_key (&sessionkey); + + rc = gcry_cipher_open (&handle, + GCRY_CIPHER_AES256, GCRY_CIPHER_MODE_CFB, 0); + + if (rc) + { + printf ("testweakkey: gcry_cipher_open failed on trial %d. %s\n", + number_of_runs, gcry_strerror (rc)); + rc = 0; + continue; + } + + rc = + gcry_cipher_setkey (handle, &sessionkey, + GNUNET_CRYPTO_AES_KEY_LENGTH); + + if ((char) rc == GPG_ERR_WEAK_KEY) + { + printf ("\nWeak key (in hex): "); + printWeakKey (&sessionkey); + printf ("\n"); + number_of_weak_keys++; + } + else if (rc) + { + printf ("\nUnexpected error generating keys. Error is %s\n", + gcry_strerror (rc)); + } + + gcry_cipher_close (handle); + + } + + return number_of_weak_keys; +} + +int +main (int argc, char *argv[]) +{ + int weak_keys; + + GNUNET_log_setup ("test-crypto-aes-weak", "WARNING", NULL); + GNUNET_CRYPTO_random_disable_entropy_gathering (); + if (GENERATE_WEAK_KEYS) + { + weak_keys = getWeakKeys (); + + if (weak_keys == 0) + { + printf ("\nNo weak keys found in %d runs.\n", MAX_WEAK_KEY_TRIALS); + } + else + { + printf ("\n%d weak keys found in %d runs.\n", + weak_keys, MAX_WEAK_KEY_TRIALS); + } + } + + if (testWeakKey () != 0) + return -1; + return 0; +} + +/* end of weakkeytest.c */ diff --git a/src/util/test_crypto_crc.c b/src/util/test_crypto_crc.c new file mode 100644 index 000000000..d1505516b --- /dev/null +++ b/src/util/test_crypto_crc.c @@ -0,0 +1,221 @@ +/* + This file is part of GNUnet. + (C) 2001, 2002, 2003, 2004, 2006 Christian Grothoff (and other contributing authors) + + GNUnet is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published + by the Free Software Foundation; either version 2, 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 + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with GNUnet; see the file COPYING. If not, write to the + Free Software Foundation, Inc., 59 Temple Place - Suite 330, + Boston, MA 02111-1307, USA. + + For the actual CRC code: + Copyright abandoned; this code is in the public domain. + Provided to GNUnet by peter@horizon.com +*/ + +/** + * @file util/test_crypto_crc.c + * @brief testcase for crypto_crc.c + */ +#include "platform.h" +#include "gnunet_common.h" +#include "gnunet_crypto_lib.h" + +static int expected[] = { + -1223996378, 929797997, -1048047323, 1791081351, -425765913, 2138425902, + 82584863, 1939615314, 1806463044, -1505003452, 1878277636, -997353517, + 201238705, 1723258694, -1107452366, -344562561, -1102247383, 1973035265, + 715213337, -1886586005, 2021214515, -1387332962, 593019378, -571088044, + 1412577760, 412164558, -1626111170, 1556494863, -289796528, -850404775, + 2066714587, -911838105, -1426027382, 499684507, -835420055, 1817119454, + -1221795958, 1516966784, -1038806877, -2115880691, 532627620, 1984437415, + -396341583, -1345366324, -590766745, -1801923449, 1752427988, -386896390, + 453906317, 1552589433, -858925718, 1160445643, -740188079, -486609040, + 1102529269, -515846212, -1614217202, 1572162207, 943558923, -467330358, + -1870764193, 1477005328, -793029208, -888983175, -696956020, 842706021, + 1642390067, -805889494, 1284862057, 1562545388, 2091626273, 1852404553, + -2076508101, 370903003, 1186422975, 1936085227, 769358463, 180401058, + 2032612572, -105461719, -1119935472, 617249831, 1169304728, 1771205256, + -2042554284, 653270859, -918610713, 336081663, -913685370, 1962213744, + -505406126, -838622649, -1141518710, 893143582, -1330296611, 122119483, + 1111564496, 688811976, 1016241049, -1803438473, 359630107, 1034798954, + -581359286, 1590946527, -389997034, 2020318460, 1695967527, -464069727, + -862641495, -1405012109, -771244841, 738226150, -1035328134, -933945474, + 1254965774, 1661863830, -884127998, 1800460481, 814702567, -1214068102, + -541120421, 1898656429, -236825530, 1505866267, 1252462132, -981007520, + 1502096471, -2134644056, 483221797, 1276403836, 541133290, -1234093967, + 350748780, 257941070, 1030457090, 434988890, -1098135432, -1000556640, + -577128022, 644806294, -787536281, -1288346343, 998079404, 1259353935, + 955771631, -958377466, 1746756252, 451579658, 1913409243, -952026299, + -1556035958, -830279881, 834744289, -1878491428, 700000962, -1027245802, + 1393574384, -1260409147, -841420884, 892132797, 1494730226, -1649181766, + 1651097838, -1041807403, -1916675721, -1324525963, 157405899, -655788033, + -1943555237, -79747022, 339721623, -138341083, 1111902411, -435322914, + -533294200, -190220608, -1718346014, -1631301894, 1706265243, 745533899, + 1351941230, 1803009594, -1218191958, 1467751062, 84368433, -711251880, + 1699423788, -768792716, 846639904, 2103267723, -2095288070, -440571408, + -362144485, 2020468971, 352105963, -849211036, -1272592429, 1743440467, + 2020667861, -1649992312, 172682343, 816705364, -1990206923, 902689869, + -298510060, 164207498, 190378213, 242531543, 113383268, 304810777, + -1081099373, 819221134, -1100982926, -855941239, 1091308887, -934548124, + 520508733, -1381763773, -491593287, -2143492665, 700894653, -2049034808, + -160942046, -2009323577, 1464245054, 1584746011, -768646852, -993282698, + 1265838699, -1873820824, 575704373, -986682955, 1270688416, 88587481, + -1723991633, -409928242, 866669946, -483811323, -181759253, -963525431, + -1686612238, -1663460076, -1128449775, -1368922329, 122318131, 795862385, + 528576131, -19927090, 1369299478, 1285665642, -738964611, 1328292127, + 552041252, -1431494354, -1205275362, 42768297, -1329537238, -449177266, + 943925221, 987016465, -945138414, -270064876, 1650366626, -369252552, + 582030210, -1229235374, 147901387, -517510506, -1609742888, -1086838308, + 1391998445, -313975512, -613392078, 855706229, 1475706341, -1112105406, + 2032001400, 1565777625, 2030937777, 435522421, 1823527907, -691390605, + -827253664, 1057171580, -314146639, -630099999, -1347514552, 478716232, + -1533658804, -1425371979, 761987780, 1560243817, -1945893959, 1205759225, + -959343783, -576742354, -154125407, -1158108776, 1183788580, 1354198127, + -1534207721, -823991517, -170534462, -912524170, 1858513573, 467072185, + 2091040157, -1765027018, -1659401643, -1173890143, -1912754057, -84568053, + 2010781784, -921970156, 944508352, -922040609, 1055102010, 1018688871, + -1186761311, -2012263648, 1311654161, 277659086, 2029602288, 1127061510, + 1029452642, 285677123, -188521091, -641039012, 653836416, -805916340, + -1644860596, 1352872213, 691634876, -1477113308, -748430369, 1030697363, + -2007864449, -1196662616, 1313997192, 177342476, -566676450, -1118618118, + 1697953104, 344671484, -1489783116, -889507873, 1259591310, -716567168, + 2116447062, 324368527, 1789366816, 1558930442, 1950250221, -785460151, + 1174714258, -430047304, -859487565, -580633932, 607732845, -1128150220, + 1544355315, 1460298016, -1771194297, 1215703690, 277231808, -416020628, + -418936577, -1724839216, 404731389, 1058730508, -1508366681, 229883053, + -572310243, 1883189553, 931286849, 1659300867, -94236383, -241524462, + 548020458, -302406981, 579986475, 73468197, -984957614, 1554382245, + 2084807492, -1456802798, -1105192593, 629440327, -16313961, -2102585261, + 1873675206, 161035128, 1497033351, 1990150811, -499405222, 304019482, + 41935663, -805987182, -571699268, 1748462913, 2096239823, -116359807, + -1871127553, -1074832534, -1558866192, 231353861, 2122854560, -2102323721, + -281462361, -343403210, -673268171, 1776058383, 1581561150, 2059580579, + 768848632, 1347190372, -1701705879, 245282007, -563267886, -592558289, + 1662399958, 1390406821, -1522485580, -706446863, 2069516289, -301855859, + -778346387, -1454093198, 1249083752, -1760506745, 262193320, 630751125, + -1495939124, -29980580, -1989626563, 659039376, -329477132, -1003507166, + -1322549020, 358606508, -2052572059, 1848014133, 1826958586, -1004948862, + -1775370541, 2134177912, -1739214473, 1892700918, 926629675, -1042761322, + 2020075900, 606370962, -1256609305, 117577265, -586848924, 191368285, + 1653535275, -1329269701, -375879127, -1089901406, 1206489978, 534223924, + -1042752982, -1178316881, -445594741, -1501682065, -1598136839, + -467688289, 750784023, 1781080461, 1729380226, 16906088, 862168532, + -2037752683, 1455274138, -1491220107, 1058323960, 1711530558, 1355062750, + 227640096, 396568027, -173579098, -408975801, -993618329, -1470751562, + 371076647, 209563718, 2015405719, -723460281, -1423934420, -2089643958, + 353260489, 2084264341, -792676687, 701391030, -1440658244, 1479321011, + 1907822880, 1232524257, -256712289, 401077577, 621808069, 868263613, + 1244930119, 2020996902, 117483907, 1341376744, -1936988014, -445200547, + -843751811, -435291191, 1041695743, 476132726, -1226874735, -1436046747, + -297047422, 1739645396, 1948680937, -718144374, 1141983978, 1673650568, + -197244350, 1604464002, 1424069853, -485626505, 1708710014, -849136541, + 1573778103, 530360999, 1777767203, 1376958336, -1088364352, 1826167753, + 742735448, -1386211659, -1991323164, -444115655, -443055378, -1586901006, + -1741686587, 1925818034, -2118916824, 803890920, -1481793154, 992278937, + 1302616410, 444517030, 1393144770, -2025632978, 1902300505, -1683582981, + 800654133, 873850324, -619580878, -2002070410, -2024936385, 1978986634, + 2012024264, 675768872, 389435615, -867217540, 231209167, -303917385, + 1445676969, -1385982721, 1310476490, 580273453, -160600202, -1330895874, + 487110497, 1124384798, 227637416, -1829783306, 1014818058, -1336870683, + -1042199518, -468525587, -1186267363, -472843891, 1215617600, -2056648329, + -873216891, 156780951, -1883246047, -842549253, -717684332, 760531638, + 1074787431, 786267513, 814031289, -561255343, -110302255, -1837376592, + 989669060, -81350614, 546038730, 222899882, 1298746805, 1791615733, + 1565630269, 1516024174, 421691479, 1860326051, -1973359550, 1854393443, + -1401468528, -158562295, 1509929255, -124024738, -462937489, 259890715, + -1515121317, -289511197, -913738664, 698079062, -1631229382, -507275144, + 1897739663, -1118192766, -1687033399, 61405556, -1913606579, -473308896, + -259107170, -576944609, -1689355510, 322156799, 545090192, 127425176, + -1815211748, -2070235628, -1172529316, 599259550, -910906653, 1797380363, + -938649427, 142991392, 504559631, 1208867355, -807699247, -616021271, + -254935281, -57151221, -1095534993, 1998380318, 1772459584, 713271407, + -1197898266, 808881935, -308133481, -1314455137, 284321772, -743117625, + -1622364240, -1667535152, 118713606, 1053615347, -2072876023, -178189072, + -828319551, 2047304928, -1311435786, -1970672907, -747972100, 86806159, + -436088421, 1464645587, 735840899, 32600466, -190473426, -735703440, + 482872155, 475662392, -713681085, 1424078728, -150668609, -1137197868, + -1682762563, -48035649, 1143959866, -1542015129, 284920371, -1587695586, + -625236551, -753893357, -433976266, -1329796037, -1636712478, 1686783454, + 27839146, 1748631474, -879528256, 2057796026, 773734654, 112269667, + -2011541314, 1517797297, -1943171794, 268166111, -1037010413, -1945824504, + -1672323792, 306260758, -692968628, -701704965, -462980996, 939188824, + 553289792, 1790245000, 2093793129, -658085781, -186055037, -2130433650, + -1013235433, 1190870089, -2126586963, -1509655742, -1291895256, + -1427857845, 309538950, 388316741, 259659733, -1895092434, 110126220, + -170175575, -419430224, -696234084, -832170948, -353431720, -797675726, + -1644136054, 715163272, -1305904349, -145786463, -99586244, -695450446, + -871327102, -725496060, 952863853, -688441983, -1729929460, -103732092, + 1059054528, 568873585, -982665223, -128672783, 2099418320, 1508239336, + -2089480835, -390935727, 664306522, -1607364342, -163246802, -1121295140, + -128375779, -615694409, -2079391797, 760542037, 677761593, -750117849, + -1060525080, 2128437080, 525250908, 1987657172, 2032530557, -2011247936, + 1942775263, 1681562788, 688229491, -803856505, 684707948, 1308988965, + 1455480037, 790659611, 1557968784, -383203149, -361510986, -742575828, + 558837193, -1214977424, 1253274105, -119513513, -993964385, -33438767, + -177452803, 1186928041, -2073533871, 1188528559, 1896514695, 1200128512, + 1930588755, -1914141443, 1534656032, -1192989829, -1848274656, -220848455, + 1001806509, 1298797392, 1533031884, -1912322446, 1705583815, 1568094347, + -1397640627, 807828512, -1852996497, -1529733505, -1575634185, + -1280270160, -1567624159, -1861904922, 1276738579, 1163432999, 626879833, + 316942006, -1871138342, 1341039701, 1595907877, 1950911580, 1634717748, + 1071476055, -809354290, -1161553341, -2081621710, -2085557943, 19360224, + 322135580, -698485151, 1267663094, -233890834, -126361189, -1426257522, + 1094007921, 500179855, -283548002, -1678987343, 1946999943, 1489410849, + 2089571262, 1430799093, 1961848046, -99462663, -552833264, 1168700661, + -1783882181, 2089196401, 1092839657, 914488673, 80263859, -2140947098, + -726384741, -1022448237, 2113887675, 1485770846, -112922517, 1995461466, + 774613726, 944068011, 1521975359, 289086919, -386920759, -1960513175, + 358460021, -238698524, -1913640563, -1000324864, 1731755224, -1271586254, + -1917469655, 2134162829, -828097534, -1089292503, -1514835999, 1682931514, + -482307169, 2110243841, 115744834, -2038340170, 65889188, -539445712, + -1713206408, -1842396726, -1659545588, -909558923, 860164922, 1328713040, + 1044007120, -2103807103, -1073990344, -1312783785, -884980824, -705318011, + -1263408788, -2032228692, -1732844111, -1813827156, 1462566279, + 1179250845, 1732421772, 604429013, -92284336, -1192166516, 304654351, + 1998552034, -1802461575, -1802704071, -1704833934, -976264396, 1005840702, + 2108843914, 1363909309, 843040834, -1039625241, 1285007226, 91610001, + 418426329, 678422358, -945360697, -440008081, -1053091357, 425719777, + -1372778676, 591912153, 1229089037, -56663158, 2140251400, 830257037, + 763914157, 175610373, -2105655963, -1040826150, 1174443038, 339290593, + 346618443, -180504100, -1363190515, 210620018, 1028894425, 573529714, + 698460117, 136999397, 1015621712, -1401813739, -297990684, -1820934845, + -1299093313, 1299361369, -366522415, 91527707, 1113466178, -956229484, + 22204763, -1394374195, -1912666711, -1453789804, 1613408399, -169509567, + 1350520309, 540761213, -2086682848, 1095131491, -812787911, 1860108594, + -1121378737, -1667252487, -486084366, 166519760, 1609891237, 728218405, + 291075010, 646168382, 108462277, -1616661910, 1016600360, 2099958568, + 27934736, 183821196, 13660496, -805589719, 936068730, -439037934, + 1414622584, 215845485, -1352304469, -1817427526, -1318710977, -110207199, + 228524335, 1704746590, 998293651, -1521016702, -641956531, -2089808167, + 2094404052, -1446381065, -662186492, 1670154584, 9637833, 493925511, + 660047318, 1197537103, 1696017374, -204994399, -1104145601, -852330465, + -1936369658, -829716674, -1255255217, 1264013799, 1642611772, -652520861, + 777247164, 2028895987, -1424241853, -54367829, -1940161761, -1802831079, + -449405299, 838242661, -323055438, 794295411, -136989378, -446686673, + -421252799, -16777216, +}; + +int +main (int argc, char *argv[]) +{ + char buf[1024]; + int i; + + GNUNET_log_setup ("test-crypto-crc", "WARNING", NULL); + for (i = 0; i < 1024; i++) + buf[i] = (char) i; + for (i = 0; i < 1024; i++) + if (expected[i] != GNUNET_CRYPTO_crc32_n (&buf[i], 1024 - i)) + return 1; + return 0; +} diff --git a/src/util/test_crypto_hash.c b/src/util/test_crypto_hash.c new file mode 100644 index 000000000..a22a0e16a --- /dev/null +++ b/src/util/test_crypto_hash.c @@ -0,0 +1,165 @@ +/* + This file is part of GNUnet. + (C) 2002, 2003, 2004, 2006, 2009 Christian Grothoff (and other contributing authors) + + GNUnet is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published + by the Free Software Foundation; either version 2, 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 + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with GNUnet; see the file COPYING. If not, write to the + Free Software Foundation, Inc., 59 Temple Place - Suite 330, + Boston, MA 02111-1307, USA. +*/ + +/** + * @author Christian Grothoff + * @file util/test_crypto_hash.c + * @brief Test for crypto_hash.c + */ +#include "platform.h" +#include "gnunet_common.h" +#include "gnunet_crypto_lib.h" +#include "gnunet_scheduler_lib.h" + +static char block[65536]; + +#define FILENAME "testblock.dat" + +static int +test (int number) +{ + GNUNET_HashCode h1; + GNUNET_HashCode h2; + struct GNUNET_CRYPTO_HashAsciiEncoded enc; + + memset (&h1, number, sizeof (GNUNET_HashCode)); + GNUNET_CRYPTO_hash_to_enc (&h1, &enc); + if (GNUNET_OK != GNUNET_CRYPTO_hash_from_string ((char *) &enc, &h2)) + { + printf ("enc2hash failed!\n"); + return 1; + } + if (0 != memcmp (&h1, &h2, sizeof (GNUNET_HashCode))) + return 1; + return 0; +} + +static int +testEncoding () +{ + int i; + for (i = 0; i < 255; i++) + if (0 != test (i)) + return 1; + return 0; +} + +static int +testArithmetic () +{ + static struct GNUNET_CRYPTO_AesSessionKey zskey; + static struct GNUNET_CRYPTO_AesInitializationVector ziv; + GNUNET_HashCode h1; + GNUNET_HashCode h2; + GNUNET_HashCode d; + GNUNET_HashCode s; + struct GNUNET_CRYPTO_AesSessionKey skey; + struct GNUNET_CRYPTO_AesInitializationVector iv; + + GNUNET_CRYPTO_hash_create_random (&h1); + GNUNET_CRYPTO_hash_create_random (&h2); + if (GNUNET_CRYPTO_hash_distance_u32 (&h1, &h2) != + GNUNET_CRYPTO_hash_distance_u32 (&h2, &h1)) + return 1; + GNUNET_CRYPTO_hash_difference (&h1, &h2, &d); + GNUNET_CRYPTO_hash_sum (&h1, &d, &s); + if (0 != GNUNET_CRYPTO_hash_cmp (&s, &h2)) + return 1; + GNUNET_CRYPTO_hash_xor (&h1, &h2, &d); + GNUNET_CRYPTO_hash_xor (&h1, &d, &s); + if (0 != GNUNET_CRYPTO_hash_cmp (&s, &h2)) + return 1; + if (0 != GNUNET_CRYPTO_hash_xorcmp (&s, &h2, &h1)) + return 1; + if (-1 != GNUNET_CRYPTO_hash_xorcmp (&h1, &h2, &h1)) + return 1; + if (1 != GNUNET_CRYPTO_hash_xorcmp (&h1, &h2, &h2)) + return 1; + memset (&d, 0xF0, sizeof (d)); + if (0 != GNUNET_CRYPTO_hash_get_bit (&d, 3)) + return 1; + if (1 != GNUNET_CRYPTO_hash_get_bit (&d, 6)) + return 1; + memset (&d, 0, sizeof (d)); + GNUNET_CRYPTO_hash_to_AES_key (&d, &skey, &iv); + if ((0 != memcmp (&skey, &zskey, sizeof (skey) - sizeof (unsigned int))) || + (0 != memcmp (&iv, &ziv, sizeof (iv)))) + return 1; + return 0; +} + +static void +finished_task (void *cls, const GNUNET_HashCode * res) +{ + int *ret = cls; + GNUNET_HashCode want; + + GNUNET_CRYPTO_hash (block, sizeof (block), &want); + if (0 != memcmp (res, &want, sizeof (want))) + *ret = 2; + else + *ret = 0; +} + + +static void +file_hasher (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc) +{ + GNUNET_CRYPTO_hash_file (tc->sched, + GNUNET_SCHEDULER_PRIORITY_DEFAULT, + GNUNET_NO, FILENAME, 1024, &finished_task, cls); +} + + +static int +testFileHash () +{ + int ret; + FILE *f; + + memset (block, 42, sizeof (block) / 2); + memset (&block[sizeof (block) / 2], 43, sizeof (block) / 2); + GNUNET_assert (NULL != (f = fopen (FILENAME, "w+"))); + GNUNET_break (sizeof (block) == fwrite (block, 1, sizeof (block), f)); + GNUNET_break (0 == fclose (f)); + ret = 1; + GNUNET_SCHEDULER_run (&file_hasher, &ret); + GNUNET_break (0 == UNLINK (FILENAME)); + return ret; +} + + +int +main (int argc, char *argv[]) +{ + int failureCount = 0; + int i; + + GNUNET_log_setup ("test-crypto-hash", "WARNING", NULL); + for (i = 0; i < 10; i++) + failureCount += testEncoding (); + failureCount += testArithmetic (); + failureCount += testFileHash (); + if (failureCount != 0) + return 1; + return 0; +} + +/* end of hashingtest.c */ diff --git a/src/util/test_crypto_ksk.c b/src/util/test_crypto_ksk.c new file mode 100644 index 000000000..c8555b5de --- /dev/null +++ b/src/util/test_crypto_ksk.c @@ -0,0 +1,220 @@ +/* + This file is part of GNUnet. + Copyright (C) 2004, 2005, 2006 Christian Grothoff (and other contributing authors) + + GNUnet is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published + by the Free Software Foundation; either version 2, 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 + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with GNUnet; see the file COPYING. If not, write to the + Free Software Foundation, Inc., 59 Temple Place - Suite 330, + Boston, MA 02111-1307, USA. +*/ + +/** + * @file util/test_crypto_ksk.c + * @brief testcase for util/crypto_ksk.c + * @author Christian Grothoff + */ +#include "platform.h" +#include "gnunet_common.h" +#include "gnunet_crypto_lib.h" +#include "gnunet_signatures.h" +#include "gnunet_time_lib.h" + +#define TESTSTRING "Hello World\0" +#define MAX_TESTVAL 20 +#define UNIQUE_ITER 6 +#define ITER 25 + + +static int +testMultiKey (const char *word) +{ + GNUNET_HashCode in; + struct GNUNET_CRYPTO_RsaPrivateKey *hostkey; + struct GNUNET_CRYPTO_RsaPublicKeyBinaryEncoded pkey; + struct GNUNET_CRYPTO_RsaPublicKeyBinaryEncoded pkey1; + int i; + + fprintf (stderr, "Testing KBlock key uniqueness (%s) ", word); + GNUNET_CRYPTO_hash (word, strlen (word), &in); + hostkey = GNUNET_CRYPTO_rsa_key_create_from_hash (&in); + if (hostkey == NULL) + { + GNUNET_break (0); + return GNUNET_SYSERR; + } + GNUNET_CRYPTO_rsa_key_get_public (hostkey, &pkey); + /* + for (i=0;i (buf[i] = GNUNET_CRYPTO_random_u32 (mode, 1024))); + for (i = 0; i < 10; i++) + { + b2 = GNUNET_CRYPTO_random_permute (mode, 1024); + if (0 == memcmp (b2, buf, sizeof (buf))) + { + fprintf (stderr, "!"); + GNUNET_free (b2); + continue; + } + GNUNET_free (b2); + break; + } + if (i == 10) + return 1; /* virtually impossible... */ + + for (n = 10; n < 1024LL * 1024LL * 1024LL; n *= 10) + GNUNET_break (n > GNUNET_CRYPTO_random_u64 (mode, n)); + return 0; +} + +int +main (int argc, char *argv[]) +{ + GNUNET_log_setup ("test-crypto-random", "WARNING", NULL); + if (0 != test (GNUNET_CRYPTO_QUALITY_WEAK)) + return 1; + if (0 != test (GNUNET_CRYPTO_QUALITY_STRONG)) + return 1; + + return 0; +} diff --git a/src/util/test_crypto_rsa.c b/src/util/test_crypto_rsa.c new file mode 100644 index 000000000..e71decca8 --- /dev/null +++ b/src/util/test_crypto_rsa.c @@ -0,0 +1,335 @@ +/* + This file is part of GNUnet. + (C) 2002, 2003, 2004, 2006, 2009 Christian Grothoff (and other contributing authors) + + GNUnet is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published + by the Free Software Foundation; either version 2, 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 + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with GNUnet; see the file COPYING. If not, write to the + Free Software Foundation, Inc., 59 Temple Place - Suite 330, + Boston, MA 02111-1307, USA. + +*/ +/** + * @file util/test_crypto_rsa.c + * @brief testcase for RSA public key crypto + * @author Christian Grothoff + */ +#include "platform.h" +#include "gnunet_common.h" +#include "gnunet_crypto_lib.h" +#include "gnunet_signatures.h" +#include "gnunet_time_lib.h" + +#define TESTSTRING "Hello World\0" +#define MAX_TESTVAL sizeof(struct GNUNET_CRYPTO_AesSessionKey) +#define ITER 25 +#define KEYFILE "/tmp/test-gnunet-crypto-rsa.key" + +#define PERF GNUNET_YES + +static int +testEncryptDecrypt () +{ + struct GNUNET_CRYPTO_RsaPrivateKey *hostkey; + struct GNUNET_CRYPTO_RsaPublicKeyBinaryEncoded pkey; + struct GNUNET_CRYPTO_RsaEncryptedData target; + char result[MAX_TESTVAL]; + int i; + struct GNUNET_TIME_Absolute start; + int ok; + + fprintf (stderr, "W"); + hostkey = GNUNET_CRYPTO_rsa_key_create (); + GNUNET_CRYPTO_rsa_key_get_public (hostkey, &pkey); + + ok = 0; + start = GNUNET_TIME_absolute_get (); + for (i = 0; i < ITER; i++) + { + fprintf (stderr, "."); + if (GNUNET_SYSERR == GNUNET_CRYPTO_rsa_encrypt (TESTSTRING, + strlen (TESTSTRING) + 1, + &pkey, &target)) + { + fprintf (stderr, "GNUNET_CRYPTO_rsa_encrypt returned SYSERR\n"); + ok++; + continue; + } + if (-1 == GNUNET_CRYPTO_rsa_decrypt (hostkey, + &target, result, + strlen (TESTSTRING) + 1)) + { + fprintf (stderr, "GNUNET_CRYPTO_rsa_decrypt returned SYSERR\n"); + ok++; + continue; + + } + if (strncmp (TESTSTRING, result, strlen (TESTSTRING)) != 0) + { + printf ("%s != %.*s - testEncryptDecrypt failed!\n", + TESTSTRING, MAX_TESTVAL, result); + ok++; + continue; + } + } + printf ("%d RSA encrypt/decrypt operations %llums (%d failures)\n", + ITER, + (unsigned long long) GNUNET_TIME_absolute_get_duration (start). + value, ok); + GNUNET_CRYPTO_rsa_key_free (hostkey); + if (ok == 0) + return GNUNET_OK; + else + return GNUNET_SYSERR; +} + +#if PERF +static int +testEncryptPerformance () +{ + struct GNUNET_CRYPTO_RsaPrivateKey *hostkey; + struct GNUNET_CRYPTO_RsaPublicKeyBinaryEncoded pkey; + struct GNUNET_CRYPTO_RsaEncryptedData target; + int i; + struct GNUNET_TIME_Absolute start; + int ok; + + fprintf (stderr, "W"); + hostkey = GNUNET_CRYPTO_rsa_key_create (); + GNUNET_CRYPTO_rsa_key_get_public (hostkey, &pkey); + + ok = 0; + start = GNUNET_TIME_absolute_get (); + for (i = 0; i < ITER; i++) + { + fprintf (stderr, "."); + if (GNUNET_SYSERR == GNUNET_CRYPTO_rsa_encrypt (TESTSTRING, + strlen (TESTSTRING) + 1, + &pkey, &target)) + { + fprintf (stderr, "GNUNET_CRYPTO_rsa_encrypt returned SYSERR\n"); + ok++; + continue; + } + } + printf ("%d RSA encrypt operations %llu ms (%d failures)\n", + ITER, + (unsigned long long) GNUNET_TIME_absolute_get_duration (start). + value, ok); + GNUNET_CRYPTO_rsa_key_free (hostkey); + if (ok != 0) + return GNUNET_SYSERR; + return GNUNET_OK; +} +#endif + +static int +testEncryptDecryptSK () +{ + struct GNUNET_CRYPTO_RsaPrivateKey *hostkey; + struct GNUNET_CRYPTO_RsaPublicKeyBinaryEncoded pkey; + struct GNUNET_CRYPTO_RsaEncryptedData target; + struct GNUNET_CRYPTO_AesSessionKey insk; + struct GNUNET_CRYPTO_AesSessionKey outsk; + int i; + struct GNUNET_TIME_Absolute start; + int ok; + + fprintf (stderr, "W"); + hostkey = GNUNET_CRYPTO_rsa_key_create (); + GNUNET_CRYPTO_rsa_key_get_public (hostkey, &pkey); + + ok = 0; + start = GNUNET_TIME_absolute_get (); + for (i = 0; i < ITER; i++) + { + fprintf (stderr, "."); + GNUNET_CRYPTO_aes_create_session_key (&insk); + if (GNUNET_SYSERR == GNUNET_CRYPTO_rsa_encrypt (&insk, + sizeof (struct + GNUNET_CRYPTO_AesSessionKey), + &pkey, &target)) + { + fprintf (stderr, "GNUNET_CRYPTO_rsa_encrypt returned SYSERR\n"); + ok++; + continue; + } + if (-1 == GNUNET_CRYPTO_rsa_decrypt (hostkey, + &target, &outsk, + sizeof (struct + GNUNET_CRYPTO_AesSessionKey))) + { + fprintf (stderr, "GNUNET_CRYPTO_rsa_decrypt returned SYSERR\n"); + ok++; + continue; + } + if (0 != + memcmp (&insk, &outsk, sizeof (struct GNUNET_CRYPTO_AesSessionKey))) + { + printf ("testEncryptDecryptSK failed!\n"); + ok++; + continue; + } + } + printf ("%d RSA encrypt/decrypt SK operations %llus (%d failures)\n", + ITER, + (unsigned long long) GNUNET_TIME_absolute_get_duration (start). + value, ok); + GNUNET_CRYPTO_rsa_key_free (hostkey); + if (ok != 0) + return GNUNET_SYSERR; + return GNUNET_OK; +} + + +static int +testSignVerify () +{ + struct GNUNET_CRYPTO_RsaPrivateKey *hostkey; + struct GNUNET_CRYPTO_RsaSignature sig; + struct GNUNET_CRYPTO_RsaSignaturePurpose purp; + struct GNUNET_CRYPTO_RsaPublicKeyBinaryEncoded pkey; + int i; + struct GNUNET_TIME_Absolute start; + int ok = GNUNET_OK; + + fprintf (stderr, "W"); + hostkey = GNUNET_CRYPTO_rsa_key_create (); + GNUNET_CRYPTO_rsa_key_get_public (hostkey, &pkey); + start = GNUNET_TIME_absolute_get (); + purp.size = htonl (sizeof (struct GNUNET_CRYPTO_RsaSignaturePurpose)); + purp.purpose = htonl (GNUNET_SIGNATURE_PURPOSE_TEST); + + for (i = 0; i < ITER; i++) + { + fprintf (stderr, "."); + if (GNUNET_SYSERR == GNUNET_CRYPTO_rsa_sign (hostkey, &purp, &sig)) + { + fprintf (stderr, "GNUNET_CRYPTO_rsa_sign returned SYSERR\n"); + ok = GNUNET_SYSERR; + continue; + } + if (GNUNET_SYSERR == + GNUNET_CRYPTO_rsa_verify (GNUNET_SIGNATURE_PURPOSE_TEST, + &purp, &sig, &pkey)) + { + printf ("GNUNET_CRYPTO_rsa_verify failed!\n"); + ok = GNUNET_SYSERR; + continue; + } + if (GNUNET_SYSERR != + GNUNET_CRYPTO_rsa_verify (GNUNET_SIGNATURE_PURPOSE_TRANSPORT_HELLO, + &purp, &sig, &pkey)) + { + printf ("GNUNET_CRYPTO_rsa_verify failed to fail!\n"); + ok = GNUNET_SYSERR; + continue; + } + } + printf ("%d RSA sign/verify operations %llums\n", + ITER, + (unsigned long long) GNUNET_TIME_absolute_get_duration (start). + value); + GNUNET_CRYPTO_rsa_key_free (hostkey); + return ok; +} + + +#if PERF +static int +testSignPerformance () +{ + struct GNUNET_CRYPTO_RsaPrivateKey *hostkey; + struct GNUNET_CRYPTO_RsaSignaturePurpose purp; + struct GNUNET_CRYPTO_RsaSignature sig; + struct GNUNET_CRYPTO_RsaPublicKeyBinaryEncoded pkey; + int i; + struct GNUNET_TIME_Absolute start; + int ok = GNUNET_OK; + + purp.size = htonl (sizeof (struct GNUNET_CRYPTO_RsaSignaturePurpose)); + purp.purpose = htonl (GNUNET_SIGNATURE_PURPOSE_TEST); + fprintf (stderr, "W"); + hostkey = GNUNET_CRYPTO_rsa_key_create (); + GNUNET_CRYPTO_rsa_key_get_public (hostkey, &pkey); + start = GNUNET_TIME_absolute_get (); + for (i = 0; i < ITER; i++) + { + fprintf (stderr, "."); + if (GNUNET_SYSERR == GNUNET_CRYPTO_rsa_sign (hostkey, &purp, &sig)) + { + fprintf (stderr, "GNUNET_CRYPTO_rsa_sign returned SYSERR\n"); + ok = GNUNET_SYSERR; + continue; + } + } + printf ("%d RSA sign operations %llu ms\n", ITER, + GNUNET_TIME_absolute_get_duration (start).value); + GNUNET_CRYPTO_rsa_key_free (hostkey); + return ok; +} +#endif + + +static int +testCreateFromFile () +{ + struct GNUNET_CRYPTO_RsaPrivateKey *key; + struct GNUNET_CRYPTO_RsaPublicKeyBinaryEncoded p1; + struct GNUNET_CRYPTO_RsaPublicKeyBinaryEncoded p2; + + key = GNUNET_CRYPTO_rsa_key_create_from_file (KEYFILE); + GNUNET_CRYPTO_rsa_key_get_public (key, &p1); + GNUNET_CRYPTO_rsa_key_free (key); + key = GNUNET_CRYPTO_rsa_key_create_from_file (KEYFILE); + GNUNET_CRYPTO_rsa_key_get_public (key, &p2); + GNUNET_assert (0 == memcmp (&p1, &p2, sizeof (p1))); + GNUNET_CRYPTO_rsa_key_free (key); + GNUNET_assert (0 == UNLINK (KEYFILE)); + key = GNUNET_CRYPTO_rsa_key_create_from_file (KEYFILE); + GNUNET_CRYPTO_rsa_key_get_public (key, &p2); + GNUNET_assert (0 != memcmp (&p1, &p2, sizeof (p1))); + GNUNET_CRYPTO_rsa_key_free (key); + GNUNET_assert (0 == UNLINK (KEYFILE)); + return GNUNET_OK; +} + + +int +main (int argc, char *argv[]) +{ + int failureCount = 0; + + GNUNET_log_setup ("test-crypto-rsa", "WARNING", NULL); + GNUNET_CRYPTO_random_disable_entropy_gathering (); + if (GNUNET_OK != testCreateFromFile ()) + failureCount++; +#if PERF + if (GNUNET_OK != testEncryptPerformance ()) + failureCount++; + if (GNUNET_OK != testSignPerformance ()) + failureCount++; +#endif + if (GNUNET_OK != testEncryptDecryptSK ()) + failureCount++; + if (GNUNET_OK != testEncryptDecrypt ()) + failureCount++; + if (GNUNET_OK != testSignVerify ()) + failureCount++; + + if (failureCount != 0) + { + printf ("\n\n%d TESTS FAILED!\n\n", failureCount); + return -1; + } + return 0; +} /* end of main */ diff --git a/src/util/test_disk.c b/src/util/test_disk.c new file mode 100644 index 000000000..0d385afa7 --- /dev/null +++ b/src/util/test_disk.c @@ -0,0 +1,274 @@ +/* + This file is part of GNUnet. + (C) 2001, 2002, 2003, 2005, 2006 Christian Grothoff (and other contributing authors) + + GNUnet is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published + by the Free Software Foundation; either version 2, 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 + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with GNUnet; see the file COPYING. If not, write to the + Free Software Foundation, Inc., 59 Temple Place - Suite 330, + Boston, MA 02111-1307, USA. +*/ + +/** + * @file util/test_disk.c + * @brief testcase for the storage module + * @author Christian Grothoff + */ +#include "platform.h" +#include "gnunet_common.h" +#include "gnunet_disk_lib.h" +#include "gnunet_scheduler_lib.h" + +#define TESTSTRING "Hello World\0" + +static int +testReadWrite () +{ + char tmp[100 + 1]; + int ret; + + if (GNUNET_OK != + GNUNET_DISK_file_write (".testfile", TESTSTRING, strlen (TESTSTRING), + "644")) + return 1; + if (GNUNET_OK != GNUNET_DISK_file_test (".testfile")) + return 1; + ret = GNUNET_DISK_file_read (".testfile", sizeof (tmp) - 1, tmp); + if (ret < 0) + { + fprintf (stderr, + "Error reading file `%s' in testReadWrite\n", ".testfile"); + return 1; + } + tmp[ret] = '\0'; + if (0 != memcmp (tmp, TESTSTRING, strlen (TESTSTRING) + 1)) + { + fprintf (stderr, + "Error in testReadWrite: *%s* != *%s* for file %s\n", + tmp, TESTSTRING, ".testfile"); + return 1; + } + GNUNET_DISK_file_copy (".testfile", ".testfile2"); + memset (tmp, 0, sizeof (tmp)); + ret = GNUNET_DISK_file_read (".testfile2", sizeof (tmp) - 1, tmp); + if (ret < 0) + { + fprintf (stderr, + "Error reading file `%s' in testReadWrite\n", ".testfile2"); + return 1; + } + tmp[ret] = '\0'; + if (0 != memcmp (tmp, TESTSTRING, strlen (TESTSTRING) + 1)) + { + fprintf (stderr, + "Error in testReadWrite: *%s* != *%s* for file %s\n", + tmp, TESTSTRING, ".testfile2"); + return 1; + } + + GNUNET_break (0 == UNLINK (".testfile")); + GNUNET_break (0 == UNLINK (".testfile2")); + if (GNUNET_NO != GNUNET_DISK_file_test (".testfile")) + return 1; + + return 0; +} + +static int +testOpenClose () +{ + int fd; + unsigned long long size; + long avail; + + fd = GNUNET_DISK_file_open (".testfile", + O_RDWR | O_CREAT, S_IWUSR | S_IRUSR); + GNUNET_assert (-1 != fd); + GNUNET_break (5 == WRITE (fd, "Hello", 5)); + GNUNET_DISK_file_close (".testfile", fd); + GNUNET_break (GNUNET_OK == + GNUNET_DISK_file_size (".testfile", &size, GNUNET_NO)); + if (size != 5) + return 1; + GNUNET_break (0 == UNLINK (".testfile")); + + /* test that avail goes down as we fill the disk... */ + GNUNET_log_skip (1); + avail = GNUNET_DISK_get_blocks_available (".testfile"); + GNUNET_log_skip (0); + fd = GNUNET_DISK_file_open (".testfile", + O_RDWR | O_CREAT, S_IWUSR | S_IRUSR); + GNUNET_assert (-1 != fd); + while ((avail == GNUNET_DISK_get_blocks_available (".testfile")) && + (avail != -1)) + if (16 != WRITE (fd, "HelloWorld123456", 16)) + { + GNUNET_DISK_file_close (".testfile", fd); + GNUNET_break (0 == UNLINK (".testfile")); + return 1; + } + GNUNET_DISK_file_close (".testfile", fd); + GNUNET_break (0 == UNLINK (".testfile")); + + return 0; +} + +static int ok; + +static int +scan_callback (void *want, const char *filename) +{ + if (NULL != strstr (filename, want)) + ok++; + return GNUNET_OK; +} + +static int +testDirScan () +{ + if (GNUNET_OK != GNUNET_DISK_directory_create ("test/entry")) + return 1; + if (GNUNET_OK != GNUNET_DISK_directory_create ("test/entry_more")) + return 1; + GNUNET_DISK_directory_scan ("test", &scan_callback, "test/entry"); + if (GNUNET_OK != GNUNET_DISK_directory_remove ("test")) + return 1; + if (ok < 2) + return 1; + return 0; +} + +static void +iter_callback (void *cls, + struct GNUNET_DISK_DirectoryIterator *di, + const char *filename, const char *dirname) +{ + int *i = cls; + (*i)++; + GNUNET_DISK_directory_iterator_next (di, GNUNET_NO); +} + +static void +iter_task (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc) +{ + GNUNET_DISK_directory_iterator_start (tc->sched, + GNUNET_SCHEDULER_PRIORITY_DEFAULT, + "test", &iter_callback, cls); +} + +static int +testDirIter () +{ + int i; + + i = 0; + if (GNUNET_OK != GNUNET_DISK_directory_create ("test/entry")) + return 1; + if (GNUNET_OK != GNUNET_DISK_directory_create ("test/entry_many")) + return 1; + if (GNUNET_OK != GNUNET_DISK_directory_create ("test/entry_more")) + return 1; + GNUNET_SCHEDULER_run (&iter_task, &i); + if (GNUNET_OK != GNUNET_DISK_directory_remove ("test")) + return 1; + if (i < 3) + return 1; + return 0; +} + + +static int +testGetHome () +{ + struct GNUNET_CONFIGURATION_Handle *cfg; + char *fn; + int ret; + + cfg = GNUNET_CONFIGURATION_create (); + GNUNET_assert (cfg != NULL); + GNUNET_CONFIGURATION_set_value_string (cfg, "service", "HOME", + "/tmp/a/b/c"); + fn = GNUNET_DISK_get_home_filename (cfg, "service", "d", "e", NULL); + GNUNET_assert (fn != NULL); + GNUNET_CONFIGURATION_destroy (cfg); + ret = strcmp ("/tmp/a/b/c/d/e", fn); + GNUNET_free (fn); + return ret; +} + +static int +testCanonicalize () +{ + char *fn = GNUNET_strdup ("ab?><|cd*ef:/g\""); + GNUNET_DISK_filename_canonicalize (fn); + if (0 != strcmp (fn, "ab____cd_ef__g_")) + { + GNUNET_free (fn); + return 1; + } + GNUNET_free (fn); + return 0; +} + +static int +testChangeOwner () +{ + GNUNET_log_skip (1); + if (GNUNET_OK == GNUNET_DISK_file_change_owner ("/dev/null", "unknownuser")) + return 1; + return 0; +} + +static int +testDirMani () +{ + if (GNUNET_OK != GNUNET_DISK_directory_create_for_file ("test/ing")) + return 1; + if (GNUNET_NO != GNUNET_DISK_file_test ("test")) + return 1; + if (GNUNET_NO != GNUNET_DISK_file_test ("test/ing")) + return 1; + if (GNUNET_OK != GNUNET_DISK_directory_remove ("test")) + return 1; + if (GNUNET_OK != GNUNET_DISK_directory_create ("test")) + return 1; + if (GNUNET_YES != GNUNET_DISK_directory_test ("test")) + return 1; + if (GNUNET_OK != GNUNET_DISK_directory_remove ("test")) + return 1; + + + return 0; +} + + +int +main (int argc, char *argv[]) +{ + unsigned int failureCount = 0; + + GNUNET_log_setup ("test-disk", "WARNING", NULL); + failureCount += testReadWrite (); + failureCount += testOpenClose (); + failureCount += testDirScan (); + failureCount += testDirIter (); + failureCount += testGetHome (); + failureCount += testCanonicalize (); + failureCount += testChangeOwner (); + failureCount += testDirMani (); + if (failureCount != 0) + { + fprintf (stderr, "\n%u TESTS FAILED!\n", failureCount); + return -1; + } + return 0; +} /* end of main */ diff --git a/src/util/test_getopt.c b/src/util/test_getopt.c new file mode 100644 index 000000000..89e7be863 --- /dev/null +++ b/src/util/test_getopt.c @@ -0,0 +1,239 @@ +/* + This file is part of GNUnet. + (C) 2003, 2004, 2005, 2006, 2009 Christian Grothoff (and other contributing authors) + + GNUnet is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published + by the Free Software Foundation; either version 2, 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 + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with GNUnet; see the file COPYING. If not, write to the + Free Software Foundation, Inc., 59 Temple Place - Suite 330, + Boston, MA 02111-1307, USA. +*/ +/** + * @file util/test_getopt.c + * @brief testcase for util/getopt.c + */ +#include "platform.h" +#include "gnunet_common.h" +#include "gnunet_configuration_lib.h" +#include "gnunet_getopt_lib.h" + +#define VERBOSE 0 + +static int +testMinimal () +{ + struct GNUNET_CONFIGURATION_Handle *cfg; + char *const emptyargv[] = { + "test", + NULL + }; + const struct GNUNET_GETOPT_CommandLineOption emptyoptionlist[] = { + GNUNET_GETOPT_OPTION_END + }; + + cfg = GNUNET_CONFIGURATION_create (); + if (1 != GNUNET_GETOPT_run ("test", cfg, emptyoptionlist, 1, emptyargv)) + { + GNUNET_CONFIGURATION_destroy (cfg); + return 1; + } + GNUNET_CONFIGURATION_destroy (cfg); + + return 0; +} + +static int +testVerbose () +{ + struct GNUNET_CONFIGURATION_Handle *cfg; + char *const myargv[] = { + "test", + "-V", + "-V", + "more", + NULL + }; + unsigned int vflags = 0; + const struct GNUNET_GETOPT_CommandLineOption verboseoptionlist[] = { + GNUNET_GETOPT_OPTION_VERBOSE (&vflags), + GNUNET_GETOPT_OPTION_END + }; + + cfg = GNUNET_CONFIGURATION_create (); + if (3 != GNUNET_GETOPT_run ("test", cfg, verboseoptionlist, 4, myargv)) + { + GNUNET_break (0); + GNUNET_CONFIGURATION_destroy (cfg); + return 1; + } + GNUNET_CONFIGURATION_destroy (cfg); + if (vflags != 2) + { + GNUNET_break (0); + return 1; + } + return 0; +} + +static int +testVersion () +{ + struct GNUNET_CONFIGURATION_Handle *cfg; + char *const myargv[] = { + "test_getopt", + "-v", + NULL + }; + const struct GNUNET_GETOPT_CommandLineOption versionoptionlist[] = { + GNUNET_GETOPT_OPTION_VERSION (PACKAGE_VERSION), + GNUNET_GETOPT_OPTION_END + }; + + cfg = GNUNET_CONFIGURATION_create (); + if (-1 != GNUNET_GETOPT_run ("test_getopt", + cfg, versionoptionlist, 2, myargv)) + { + GNUNET_break (0); + GNUNET_CONFIGURATION_destroy (cfg); + return 1; + } + GNUNET_CONFIGURATION_destroy (cfg); + return 0; +} + +static int +testAbout () +{ + struct GNUNET_CONFIGURATION_Handle *cfg; + char *const myargv[] = { + "test_getopt", + "-h", + NULL + }; + const struct GNUNET_GETOPT_CommandLineOption aboutoptionlist[] = { + GNUNET_GETOPT_OPTION_HELP ("Testing"), + GNUNET_GETOPT_OPTION_END + }; + + cfg = GNUNET_CONFIGURATION_create (); + if (-1 != GNUNET_GETOPT_run ("test_getopt", + cfg, aboutoptionlist, 2, myargv)) + { + GNUNET_break (0); + GNUNET_CONFIGURATION_destroy (cfg); + return 1; + } + GNUNET_CONFIGURATION_destroy (cfg); + return 0; +} + +static int +testLogOpts () +{ + struct GNUNET_CONFIGURATION_Handle *cfg; + char *const myargv[] = { + "test_getopt", + "-l", "filename", + "-L", "WARNING", + NULL + }; + char *level = GNUNET_strdup ("stuff"); + char *fn = NULL; + const struct GNUNET_GETOPT_CommandLineOption logoptionlist[] = { + GNUNET_GETOPT_OPTION_LOGFILE (&fn), + GNUNET_GETOPT_OPTION_LOGLEVEL (&level), + GNUNET_GETOPT_OPTION_END + }; + + cfg = GNUNET_CONFIGURATION_create (); + if (5 != GNUNET_GETOPT_run ("test_getopt", cfg, logoptionlist, 5, myargv)) + { + GNUNET_break (0); + GNUNET_CONFIGURATION_destroy (cfg); + return 1; + } + GNUNET_assert (fn != NULL); + GNUNET_CONFIGURATION_destroy (cfg); + if ((0 != strcmp (level, "WARNING")) || (0 != strcmp (fn, "filename"))) + { + GNUNET_break (0); + GNUNET_free (level); + GNUNET_free (fn); + return 1; + } + GNUNET_free (level); + GNUNET_free (fn); + return 0; +} + +static int +testFlagNum () +{ + struct GNUNET_CONFIGURATION_Handle *cfg; + char *const myargv[] = { + "test_getopt", + "-f", + "-n", "42", + "-N", "42", + NULL + }; + int flag = 0; + unsigned int num = 0; + unsigned long long lnum = 0; + const struct GNUNET_GETOPT_CommandLineOption logoptionlist[] = { + {'f', "--flag", NULL, "helptext", 0, &GNUNET_GETOPT_set_one, + (void *) &flag}, + {'n', "--num", "ARG", "helptext", 1, &GNUNET_GETOPT_set_uint, + (void *) &num}, + {'N', "--lnum", "ARG", "helptext", 1, &GNUNET_GETOPT_set_ulong, + (void *) &lnum}, + GNUNET_GETOPT_OPTION_END + }; + + cfg = GNUNET_CONFIGURATION_create (); + if (6 != GNUNET_GETOPT_run ("test_getopt", cfg, logoptionlist, 6, myargv)) + { + GNUNET_break (0); + GNUNET_CONFIGURATION_destroy (cfg); + return 1; + } + GNUNET_CONFIGURATION_destroy (cfg); + if ((1 != flag) || (42 != num) || (42 != lnum)) + { + GNUNET_break (0); + return 1; + } + return 0; +} + +int +main (int argc, char *argv[]) +{ + int errCnt = 0; + + GNUNET_log_setup ("test_getopt", "WARNING", NULL); + /* suppress output from -h, -v options */ + GNUNET_break (0 == CLOSE (1)); + if (0 != testMinimal ()) + errCnt++; + if (0 != testVerbose ()) + errCnt++; + if (0 != testVersion ()) + errCnt++; + if (0 != testAbout ()) + errCnt++; + if (0 != testLogOpts ()) + errCnt++; + if (0 != testFlagNum ()) + errCnt++; + return errCnt; +} diff --git a/src/util/test_network.c b/src/util/test_network.c new file mode 100644 index 000000000..377ec836f --- /dev/null +++ b/src/util/test_network.c @@ -0,0 +1,209 @@ +/* + This file is part of GNUnet. + (C) 2009 Christian Grothoff (and other contributing authors) + + GNUnet is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published + by the Free Software Foundation; either version 2, 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 + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with GNUnet; see the file COPYING. If not, write to the + Free Software Foundation, Inc., 59 Temple Place - Suite 330, + Boston, MA 02111-1307, USA. +*/ +/** + * @file util/test_network.c + * @brief tests for network.c + */ +#include "platform.h" +#include "gnunet_common.h" +#include "gnunet_network_lib.h" +#include "gnunet_scheduler_lib.h" +#include "gnunet_time_lib.h" + +#define VERBOSE GNUNET_NO + +#define PORT 12435 + + +static struct GNUNET_NETWORK_SocketHandle *csock; + +static struct GNUNET_NETWORK_SocketHandle *asock; + +static struct GNUNET_NETWORK_SocketHandle *lsock; + +static size_t sofar; + +static int ls; + + + +/** + * Create and initialize a listen socket for the server. + * + * @return -1 on error, otherwise the listen socket + */ +static int +open_listen_socket () +{ + const static int on = 1; + struct sockaddr_in sa; + int fd; + + memset (&sa, 0, sizeof (sa)); + sa.sin_port = htons (PORT); + fd = SOCKET (AF_INET, SOCK_STREAM, 0); + GNUNET_assert (fd >= 0); + if (SETSOCKOPT (fd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof (on)) < 0) + GNUNET_log (GNUNET_ERROR_TYPE_ERROR | GNUNET_ERROR_TYPE_BULK, + "setsockopt"); + GNUNET_assert (BIND (fd, &sa, sizeof (sa)) >= 0); + LISTEN (fd, 5); + return fd; +} + +static void +receive_check (void *cls, + const void *buf, + size_t available, + const struct sockaddr *addr, socklen_t addrlen, int errCode) +{ + int *ok = cls; + +#if VERBOSE + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Receive validates incoming data\n"); +#endif + GNUNET_assert (buf != NULL); /* no timeout */ + if (0 == memcmp (&"Hello World"[sofar], buf, available)) + sofar += available; + if (sofar < 12) + { +#if VERBOSE + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Receive needs more data\n"); +#endif + GNUNET_NETWORK_receive (asock, + 1024, + GNUNET_TIME_relative_multiply + (GNUNET_TIME_UNIT_SECONDS, 5), &receive_check, + cls); + } + else + { +#if VERBOSE + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Receive closes accepted socket\n"); +#endif + *ok = 0; + GNUNET_NETWORK_socket_destroy (asock); + } +} + + +static void +run_accept (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc) +{ +#if VERBOSE + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Test accepts connection\n"); +#endif + asock = GNUNET_NETWORK_socket_create_from_accept (tc->sched, + NULL, NULL, ls, 1024); + GNUNET_assert (asock != NULL); + GNUNET_assert (GNUNET_YES == GNUNET_NETWORK_socket_check (asock)); +#if VERBOSE + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Test destroys listen socket\n"); +#endif + GNUNET_NETWORK_socket_destroy (lsock); +#if VERBOSE + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Test asks to receive on accepted socket\n"); +#endif + GNUNET_NETWORK_receive (asock, + 1024, + GNUNET_TIME_relative_multiply + (GNUNET_TIME_UNIT_SECONDS, 5), &receive_check, cls); +} + +static size_t +make_hello (void *cls, size_t size, void *buf) +{ +#if VERBOSE + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Test prepares to transmit on connect socket\n"); +#endif + GNUNET_assert (size >= 12); + strcpy ((char *) buf, "Hello World"); + return 12; +} + +static void +task (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc) +{ + ls = open_listen_socket (); + lsock = GNUNET_NETWORK_socket_create_from_existing (tc->sched, ls, 0); + GNUNET_assert (lsock != NULL); + csock = GNUNET_NETWORK_socket_create_from_connect (tc->sched, + "localhost", PORT, 1024); + GNUNET_assert (csock != NULL); +#if VERBOSE + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Test asks for write notification\n"); +#endif + GNUNET_assert (NULL != + GNUNET_NETWORK_notify_transmit_ready (csock, + 12, + GNUNET_TIME_UNIT_SECONDS, + &make_hello, NULL)); +#if VERBOSE + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Test destroys client socket\n"); +#endif + GNUNET_NETWORK_socket_destroy (csock); +#if VERBOSE + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Test prepares to accept\n"); +#endif + GNUNET_SCHEDULER_add_read (tc->sched, + GNUNET_NO, + GNUNET_SCHEDULER_PRIORITY_HIGH, + GNUNET_SCHEDULER_NO_PREREQUISITE_TASK, + GNUNET_TIME_UNIT_FOREVER_REL, + ls, &run_accept, cls); +} + + +/** + * Main method, starts scheduler with task , + * checks that "ok" is correct at the end. + */ +static int +check () +{ + int ok; + + ok = 1; + GNUNET_SCHEDULER_run (&task, &ok); + return ok; +} + + + +int +main (int argc, char *argv[]) +{ + int ret = 0; + + GNUNET_log_setup ("test_network", +#if VERBOSE + "DEBUG", +#else + "WARNING", +#endif + NULL); + ret += check (); + return ret; +} + +/* end of test_network.c */ diff --git a/src/util/test_network_addressing.c b/src/util/test_network_addressing.c new file mode 100644 index 000000000..8312b721f --- /dev/null +++ b/src/util/test_network_addressing.c @@ -0,0 +1,193 @@ +/* + This file is part of GNUnet. + (C) 2009 Christian Grothoff (and other contributing authors) + + GNUnet is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published + by the Free Software Foundation; either version 2, 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 + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with GNUnet; see the file COPYING. If not, write to the + Free Software Foundation, Inc., 59 Temple Place - Suite 330, + Boston, MA 02111-1307, USA. +*/ +/** + * @file util/test_network_addressing.c + * @brief tests for network.c + */ +#include "platform.h" +#include "gnunet_common.h" +#include "gnunet_network_lib.h" +#include "gnunet_scheduler_lib.h" +#include "gnunet_time_lib.h" + +#define VERBOSE GNUNET_NO + +#define PORT 12435 + + +static struct GNUNET_NETWORK_SocketHandle *csock; + +static struct GNUNET_NETWORK_SocketHandle *asock; + +static struct GNUNET_NETWORK_SocketHandle *lsock; + +static size_t sofar; + +static int ls; + + + +/** + * Create and initialize a listen socket for the server. + * + * @return -1 on error, otherwise the listen socket + */ +static int +open_listen_socket () +{ + const static int on = 1; + struct sockaddr_in sa; + int fd; + + memset (&sa, 0, sizeof (sa)); + sa.sin_port = htons (PORT); + fd = SOCKET (AF_INET, SOCK_STREAM, 0); + GNUNET_assert (fd >= 0); + if (SETSOCKOPT (fd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof (on)) < 0) + GNUNET_log (GNUNET_ERROR_TYPE_ERROR | GNUNET_ERROR_TYPE_BULK, + "setsockopt"); + GNUNET_assert (BIND (fd, &sa, sizeof (sa)) >= 0); + LISTEN (fd, 5); + return fd; +} + + +static void +receive_check (void *cls, + const void *buf, + size_t available, + const struct sockaddr *addr, socklen_t addrlen, int errCode) +{ + int *ok = cls; + + GNUNET_assert (buf != NULL); /* no timeout */ + if (0 == memcmp (&"Hello World"[sofar], buf, available)) + sofar += available; + if (sofar < 12) + { + GNUNET_NETWORK_receive (asock, + 1024, + GNUNET_TIME_relative_multiply + (GNUNET_TIME_UNIT_SECONDS, 5), &receive_check, + cls); + } + else + { + *ok = 0; + GNUNET_NETWORK_socket_destroy (asock); + } +} + + +static void +run_accept (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc) +{ + void *addr; + size_t alen; + struct sockaddr_in *v4; + struct sockaddr_in expect; + + asock = GNUNET_NETWORK_socket_create_from_accept (tc->sched, + NULL, NULL, ls, 1024); + GNUNET_assert (asock != NULL); + GNUNET_assert (GNUNET_YES == GNUNET_NETWORK_socket_check (asock)); + GNUNET_assert (GNUNET_OK == + GNUNET_NETWORK_socket_get_address (asock, &addr, &alen)); + GNUNET_assert (alen == sizeof (struct sockaddr_in)); + v4 = addr; + memset (&expect, 0, sizeof (expect)); + expect.sin_family = AF_INET; + expect.sin_port = v4->sin_port; + expect.sin_addr.s_addr = htonl (INADDR_LOOPBACK); + GNUNET_assert (0 == memcmp (&expect, v4, alen)); + GNUNET_NETWORK_socket_destroy (lsock); + GNUNET_NETWORK_receive (asock, + 1024, + GNUNET_TIME_relative_multiply + (GNUNET_TIME_UNIT_SECONDS, 5), &receive_check, cls); +} + +static size_t +make_hello (void *cls, size_t size, void *buf) +{ + GNUNET_assert (size >= 12); + strcpy ((char *) buf, "Hello World"); + return 12; +} + +static void +task (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc) +{ + struct sockaddr_in v4; + ls = open_listen_socket (); + lsock = GNUNET_NETWORK_socket_create_from_existing (tc->sched, ls, 0); + GNUNET_assert (lsock != NULL); + + v4.sin_family = AF_INET; + v4.sin_port = htons (PORT); + v4.sin_addr.s_addr = htonl (INADDR_LOOPBACK); + csock = GNUNET_NETWORK_socket_create_from_sockaddr (tc->sched, + AF_INET, + (const struct sockaddr + *) &v4, sizeof (v4), + 1024); + GNUNET_assert (csock != NULL); + GNUNET_assert (NULL != + GNUNET_NETWORK_notify_transmit_ready (csock, + 12, + GNUNET_TIME_UNIT_SECONDS, + &make_hello, NULL)); + GNUNET_NETWORK_socket_destroy (csock); + GNUNET_SCHEDULER_add_read (tc->sched, + GNUNET_NO, + GNUNET_SCHEDULER_PRIORITY_HIGH, + GNUNET_SCHEDULER_NO_PREREQUISITE_TASK, + GNUNET_TIME_UNIT_FOREVER_REL, + ls, &run_accept, cls); +} + + +/** + * Main method, starts scheduler with task , + * checks that "ok" is correct at the end. + */ +static int +check () +{ + int ok; + + ok = 1; + GNUNET_SCHEDULER_run (&task, &ok); + return ok; +} + + + +int +main (int argc, char *argv[]) +{ + int ret = 0; + + GNUNET_log_setup ("test_network_addressing", "WARNING", NULL); + ret += check (); + return ret; +} + +/* end of test_network_addressing.c */ diff --git a/src/util/test_network_receive_cancel.c b/src/util/test_network_receive_cancel.c new file mode 100644 index 000000000..e22e24d8c --- /dev/null +++ b/src/util/test_network_receive_cancel.c @@ -0,0 +1,164 @@ +/* + This file is part of GNUnet. + (C) 2009 Christian Grothoff (and other contributing authors) + + GNUnet is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published + by the Free Software Foundation; either version 2, 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 + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with GNUnet; see the file COPYING. If not, write to the + Free Software Foundation, Inc., 59 Temple Place - Suite 330, + Boston, MA 02111-1307, USA. +*/ +/** + * @file util/test_network_receive_cancel.c + * @brief tests for network.c + */ +#include "platform.h" +#include "gnunet_common.h" +#include "gnunet_network_lib.h" +#include "gnunet_scheduler_lib.h" +#include "gnunet_time_lib.h" + +#define VERBOSE GNUNET_NO + +#define PORT 12435 + + +static struct GNUNET_NETWORK_SocketHandle *csock; + +static struct GNUNET_NETWORK_SocketHandle *asock; + +static struct GNUNET_NETWORK_SocketHandle *lsock; + +static int ls; + +static GNUNET_SCHEDULER_TaskIdentifier receive_task; + + + + +/** + * Create and initialize a listen socket for the server. + * + * @return -1 on error, otherwise the listen socket + */ +static int +open_listen_socket () +{ + const static int on = 1; + struct sockaddr_in sa; + int fd; + + memset (&sa, 0, sizeof (sa)); + sa.sin_port = htons (PORT); + fd = SOCKET (AF_INET, SOCK_STREAM, 0); + GNUNET_assert (fd >= 0); + if (SETSOCKOPT (fd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof (on)) < 0) + GNUNET_log (GNUNET_ERROR_TYPE_ERROR | GNUNET_ERROR_TYPE_BULK, + "setsockopt"); + GNUNET_assert (BIND (fd, &sa, sizeof (sa)) >= 0); + LISTEN (fd, 5); + return fd; +} + + + +static void +dead_receive (void *cls, + const void *buf, + size_t available, + const struct sockaddr *addr, socklen_t addrlen, int errCode) +{ + GNUNET_assert (0); +} + + +static void +run_accept_cancel (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc) +{ + + asock = GNUNET_NETWORK_socket_create_from_accept (tc->sched, + NULL, NULL, ls, 1024); + GNUNET_assert (asock != NULL); + GNUNET_assert (GNUNET_YES == GNUNET_NETWORK_socket_check (asock)); + GNUNET_NETWORK_socket_destroy (lsock); + receive_task + = GNUNET_NETWORK_receive (asock, + 1024, + GNUNET_TIME_relative_multiply + (GNUNET_TIME_UNIT_SECONDS, 5), &dead_receive, + cls); +} + + +static void +receive_cancel_task (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc) +{ + int *ok = cls; + GNUNET_NETWORK_receive_cancel (asock, receive_task); + GNUNET_NETWORK_socket_destroy (csock); + GNUNET_NETWORK_socket_destroy (asock); + *ok = 0; +} + + + +static void +task_receive_cancel (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc) +{ + ls = open_listen_socket (); + lsock = GNUNET_NETWORK_socket_create_from_existing (tc->sched, ls, 0); + GNUNET_assert (lsock != NULL); + csock = GNUNET_NETWORK_socket_create_from_connect (tc->sched, + "localhost", PORT, 1024); + GNUNET_assert (csock != NULL); + GNUNET_SCHEDULER_add_read (tc->sched, + GNUNET_NO, + GNUNET_SCHEDULER_PRIORITY_HIGH, + GNUNET_SCHEDULER_NO_PREREQUISITE_TASK, + GNUNET_TIME_UNIT_FOREVER_REL, + ls, &run_accept_cancel, cls); + GNUNET_SCHEDULER_add_delayed (tc->sched, + GNUNET_NO, + GNUNET_SCHEDULER_PRIORITY_KEEP, + GNUNET_SCHEDULER_NO_PREREQUISITE_TASK, + GNUNET_TIME_UNIT_SECONDS, + &receive_cancel_task, cls); +} + + + +/** + * Main method, starts scheduler with task_timeout. + */ +static int +check_receive_cancel () +{ + int ok; + + ok = 1; + GNUNET_SCHEDULER_run (&task_receive_cancel, &ok); + return ok; +} + + +int +main (int argc, char *argv[]) +{ + int ret = 0; + + GNUNET_log_setup ("test_network_receive_cancel", "WARNING", NULL); + ret += check_receive_cancel (); + + return ret; +} + +/* end of test_network.c */ diff --git a/src/util/test_network_timeout.c b/src/util/test_network_timeout.c new file mode 100644 index 000000000..2c2f6f9f2 --- /dev/null +++ b/src/util/test_network_timeout.c @@ -0,0 +1,144 @@ +/* + This file is part of GNUnet. + (C) 2009 Christian Grothoff (and other contributing authors) + + GNUnet is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published + by the Free Software Foundation; either version 2, 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 + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with GNUnet; see the file COPYING. If not, write to the + Free Software Foundation, Inc., 59 Temple Place - Suite 330, + Boston, MA 02111-1307, USA. +*/ +/** + * @file util/test_network_timeout.c + * @brief tests for network.c + */ +#include "platform.h" +#include "gnunet_common.h" +#include "gnunet_network_lib.h" +#include "gnunet_scheduler_lib.h" +#include "gnunet_time_lib.h" + +#define VERBOSE GNUNET_NO + +#define PORT 12435 + +static struct GNUNET_NETWORK_SocketHandle *csock; + +static struct GNUNET_NETWORK_SocketHandle *lsock; + +static int ls; + + +/** + * Create and initialize a listen socket for the server. + * + * @return -1 on error, otherwise the listen socket + */ +static int +open_listen_socket () +{ + const static int on = 1; + struct sockaddr_in sa; + int fd; + + memset (&sa, 0, sizeof (sa)); + sa.sin_port = htons (PORT); + fd = SOCKET (AF_INET, SOCK_STREAM, 0); + GNUNET_assert (fd >= 0); + if (SETSOCKOPT (fd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof (on)) < 0) + GNUNET_log (GNUNET_ERROR_TYPE_ERROR | GNUNET_ERROR_TYPE_BULK, + "setsockopt"); + GNUNET_assert (BIND (fd, &sa, sizeof (sa)) >= 0); + LISTEN (fd, 5); + return fd; +} + + +static size_t +send_kilo (void *cls, size_t size, void *buf) +{ + int *ok = cls; + if (size == 0) + { +#if VERBOSE + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Got the desired timeout!\n"); +#endif + GNUNET_assert (buf == NULL); + *ok = 0; + GNUNET_NETWORK_socket_destroy (lsock); + GNUNET_NETWORK_socket_destroy (csock); + return 0; + } +#if VERBOSE + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Sending kilo to fill buffer.\n"); +#endif + GNUNET_assert (size >= 1024); + memset (buf, 42, 1024); + + GNUNET_assert (NULL != + GNUNET_NETWORK_notify_transmit_ready (csock, + 1024, + GNUNET_TIME_UNIT_SECONDS, + &send_kilo, cls)); + return 1024; +} + + +static void +task_timeout (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc) +{ + + ls = open_listen_socket (); + lsock = GNUNET_NETWORK_socket_create_from_existing (tc->sched, ls, 0); + GNUNET_assert (lsock != NULL); + csock = GNUNET_NETWORK_socket_create_from_connect (tc->sched, + "localhost", PORT, 1024); + GNUNET_assert (csock != NULL); + GNUNET_assert (NULL != + GNUNET_NETWORK_notify_transmit_ready (csock, + 1024, + GNUNET_TIME_UNIT_SECONDS, + &send_kilo, cls)); +} + + + +/** + * Main method, starts scheduler with task_timeout. + */ +static int +check_timeout () +{ + int ok; + + ok = 1; + GNUNET_SCHEDULER_run (&task_timeout, &ok); + return ok; +} + +int +main (int argc, char *argv[]) +{ + int ret = 0; + + GNUNET_log_setup ("test_network_timeout", +#if VERBOSE + "DEBUG", +#else + "WARNING", +#endif + NULL); + ret += check_timeout (); + return ret; +} + +/* end of test_network_timeout.c */ diff --git a/src/util/test_network_timeout_no_connect.c b/src/util/test_network_timeout_no_connect.c new file mode 100644 index 000000000..fd5e82dcd --- /dev/null +++ b/src/util/test_network_timeout_no_connect.c @@ -0,0 +1,95 @@ +/* + This file is part of GNUnet. + (C) 2009 Christian Grothoff (and other contributing authors) + + GNUnet is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published + by the Free Software Foundation; either version 2, 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 + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with GNUnet; see the file COPYING. If not, write to the + Free Software Foundation, Inc., 59 Temple Place - Suite 330, + Boston, MA 02111-1307, USA. +*/ +/** + * @file util/test_network_timeout.c + * @brief tests for network.c, doing timeout which connect failure + */ +#include "platform.h" +#include "gnunet_common.h" +#include "gnunet_network_lib.h" +#include "gnunet_scheduler_lib.h" +#include "gnunet_time_lib.h" + +#define VERBOSE GNUNET_NO + +#define PORT 13425 + +static struct GNUNET_NETWORK_SocketHandle *csock; + +static size_t +handle_timeout (void *cls, size_t size, void *buf) +{ + int *ok = cls; +#if VERBOSE + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Received timeout signal.\n"); +#endif + + GNUNET_assert (size == 0); + GNUNET_assert (buf == NULL); + *ok = 0; + return 0; +} + + +static void +task_timeout (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc) +{ + csock = GNUNET_NETWORK_socket_create_from_connect (tc->sched, + "localhost", PORT, 1024); + GNUNET_assert (csock != NULL); + GNUNET_assert (NULL != + GNUNET_NETWORK_notify_transmit_ready (csock, + 1024, + GNUNET_TIME_UNIT_SECONDS, + &handle_timeout, cls)); +} + + + +/** + * Main method, starts scheduler with task_timeout. + */ +static int +check_timeout () +{ + int ok; + + ok = 1; + GNUNET_SCHEDULER_run (&task_timeout, &ok); + return ok; +} + +int +main (int argc, char *argv[]) +{ + int ret = 0; + + GNUNET_log_setup ("test_network_timeout_no_connect", +#if VERBOSE + "DEBUG", +#else + "WARNING", +#endif + NULL); + ret += check_timeout (); + return ret; +} + +/* end of test_network_timeout_no_connect.c */ diff --git a/src/util/test_network_transmit_cancel.c b/src/util/test_network_transmit_cancel.c new file mode 100644 index 000000000..29732d202 --- /dev/null +++ b/src/util/test_network_transmit_cancel.c @@ -0,0 +1,97 @@ +/* + This file is part of GNUnet. + (C) 2009 Christian Grothoff (and other contributing authors) + + GNUnet is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published + by the Free Software Foundation; either version 2, 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 + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with GNUnet; see the file COPYING. If not, write to the + Free Software Foundation, Inc., 59 Temple Place - Suite 330, + Boston, MA 02111-1307, USA. +*/ +/** + * @file util/test_network_transmit_cancel.c + * @brief tests for network.c + */ +#include "platform.h" +#include "gnunet_common.h" +#include "gnunet_network_lib.h" +#include "gnunet_scheduler_lib.h" +#include "gnunet_time_lib.h" + +#define VERBOSE GNUNET_YES + +#define PORT 12435 + + +static size_t +not_run (void *cls, size_t size, void *buf) +{ + GNUNET_assert (0); + return 0; +} + + +static void +task_transmit_cancel (void *cls, + const struct GNUNET_SCHEDULER_TaskContext *tc) +{ + int *ok = cls; + struct GNUNET_NETWORK_TransmitHandle *th; + struct GNUNET_NETWORK_SocketHandle *csock; + + csock = GNUNET_NETWORK_socket_create_from_connect (tc->sched, + "localhost", PORT, 1024); + GNUNET_assert (csock != NULL); + th = GNUNET_NETWORK_notify_transmit_ready (csock, + 12, + GNUNET_TIME_UNIT_MINUTES, + ¬_run, cls); + GNUNET_NETWORK_notify_transmit_ready_cancel (th); + GNUNET_NETWORK_socket_destroy (csock); + *ok = 0; +} + + + + +/** + * Main method, starts scheduler with task_timeout. + */ +static int +check_transmit_cancel () +{ + int ok; + + ok = 1; + GNUNET_SCHEDULER_run (&task_transmit_cancel, &ok); + return ok; +} + + +int +main (int argc, char *argv[]) +{ + int ret = 0; + + GNUNET_log_setup ("test_network_transmit_cancel", +#if VERBOSE + "DEBUG", +#else + "WARNING", +#endif + NULL); + ret += check_transmit_cancel (); + + return ret; +} + +/* end of test_network_transmit_cancel.c */ diff --git a/src/util/test_os_load.c b/src/util/test_os_load.c new file mode 100644 index 000000000..2f82f60c7 --- /dev/null +++ b/src/util/test_os_load.c @@ -0,0 +1,182 @@ +/* + This file is part of GNUnet. + (C) 2003, 2004, 2005, 2006, 2009 Christian Grothoff (and other contributing authors) + + GNUnet is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published + by the Free Software Foundation; either version 2, 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 + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with GNUnet; see the file COPYING. If not, write to the + Free Software Foundation, Inc., 59 Temple Place - Suite 330, + Boston, MA 02111-1307, USA. +*/ +/** + * @file util/test_os_load.c + * @brief testcase for util/os_load.c + */ +#include "platform.h" +#include "gnunet_common.h" +#include "gnunet_configuration_lib.h" +#include "gnunet_crypto_lib.h" +#include "gnunet_disk_lib.h" +#include "gnunet_os_lib.h" +#include "gnunet_time_lib.h" + +#define VERBOSE 0 + +static int +testcpu () +{ + static long k; + int ret; + struct GNUNET_TIME_Absolute start; + struct GNUNET_CONFIGURATION_Handle *cfg; + + fprintf (stderr, "CPU load test, this may take a while."); + cfg = GNUNET_CONFIGURATION_create (); + GNUNET_assert (cfg != NULL); + /* need to run each phase for more than 10s since + statuscalls only refreshes that often... */ + GNUNET_CONFIGURATION_set_value_number (cfg, "LOAD", "MAXCPULOAD", 100); + GNUNET_OS_load_cpu_get (cfg); + start = GNUNET_TIME_absolute_get (); + while ((GNUNET_TIME_absolute_get_duration (start).value < 120 * 1000) && + (0 != GNUNET_OS_load_cpu_get (cfg))) + sleep (1); + start = GNUNET_TIME_absolute_get (); + ret = GNUNET_OS_load_cpu_get (cfg); + if (ret > 10) + { + fprintf (stderr, + "\nWARNING: base load too high (%d) to run CPU load test.\n", + ret); + GNUNET_CONFIGURATION_destroy (cfg); + return 0; + } + if (ret == -1) + { + fprintf (stderr, "\nWARNING: CPU load determination not supported.\n"); + GNUNET_CONFIGURATION_destroy (cfg); + return 0; + } + while (GNUNET_TIME_absolute_get_duration (start).value < 60 * 1000) + { + k++; /* do some processing to drive load up */ + if (ret < GNUNET_OS_load_cpu_get (cfg)) + break; + } + if (ret >= GNUNET_OS_load_cpu_get (cfg)) + { + fprintf (stderr, + "\nbusy loop failed to increase CPU load: %d >= %d.", + ret, GNUNET_OS_load_cpu_get (cfg)); + ret = 1; + } + else + { +#if VERBOSE + fprintf (stderr, + "\nbusy loop increased CPU load: %d < %d.", + ret, GNUNET_OS_load_cpu_get (cfg)); +#endif + ret = 0; + } + fprintf (stderr, "\n"); + + + GNUNET_CONFIGURATION_destroy (cfg); + return ret; +} + +static int +testdisk () +{ + int ret; + int fd; + char buf[65536]; + struct GNUNET_TIME_Absolute start; + struct GNUNET_CONFIGURATION_Handle *cfg; + + fprintf (stderr, "IO load test, this may take a while."); + cfg = GNUNET_CONFIGURATION_create (); + GNUNET_assert (cfg != NULL); + /* need to run each phase for more than 10s since + statuscalls only refreshes that often... */ + GNUNET_CONFIGURATION_set_value_number (cfg, "LOAD", "MAXIOLOAD", 100); + GNUNET_OS_load_disk_get (cfg); + start = GNUNET_TIME_absolute_get (); + while ((GNUNET_TIME_absolute_get_duration (start).value < 12 * 1000) && + (0 != GNUNET_OS_load_disk_get (cfg))) + sleep (1); + start = GNUNET_TIME_absolute_get (); + ret = GNUNET_OS_load_disk_get (cfg); + if (ret > 10) + { + fprintf (stderr, + "WARNING: base load too high (%d) to run IO load test.\n", + ret); + GNUNET_CONFIGURATION_destroy (cfg); + return 0; + } + if (ret == -1) + { + fprintf (stderr, "WARNING: IO load determination not supported.\n"); + GNUNET_CONFIGURATION_destroy (cfg); + return 0; + } + memset (buf, 42, sizeof (buf)); + fd = + GNUNET_DISK_file_open (".loadfile", O_WRONLY | O_CREAT, + S_IRUSR | S_IWUSR); + GNUNET_assert (fd != -1); + while (GNUNET_TIME_absolute_get_duration (start).value < 60 * 1000) + { + LSEEK (fd, GNUNET_CRYPTO_random_u64 (GNUNET_CRYPTO_QUALITY_WEAK, + 1024 * 1024 * 1024), SEEK_SET); + GNUNET_assert (sizeof (buf) == WRITE (fd, buf, sizeof (buf))); + fsync (fd); + if (ret < GNUNET_OS_load_disk_get (cfg)) + break; + } + GNUNET_break (0 == CLOSE (fd)); + GNUNET_break (0 == UNLINK (".loadfile")); + if (ret >= GNUNET_OS_load_disk_get (cfg)) + { + fprintf (stderr, + "\nbusy loop failed to increase IO load: %d >= %d.", + ret, GNUNET_OS_load_disk_get (cfg)); + ret = 1; + } + else + { +#if VERBOSE + fprintf (stderr, + "\nbusy loop increased disk load: %d < %d.", + ret, GNUNET_OS_load_disk_get (cfg)); +#endif + ret = 0; + } + fprintf (stderr, "\n"); + GNUNET_CONFIGURATION_destroy (cfg); + return 0; +} + +int +main (int argc, char *argv[]) +{ + int errCnt = 0; + + GNUNET_log_setup ("test-os-load", "WARNING", NULL); + if (0 != testcpu ()) + errCnt++; + if (0 != testdisk ()) + errCnt++; + return errCnt; +} diff --git a/src/util/test_os_network.c b/src/util/test_os_network.c new file mode 100644 index 000000000..ca57765da --- /dev/null +++ b/src/util/test_os_network.c @@ -0,0 +1,73 @@ +/* + This file is part of GNUnet. + (C) 2003, 2004, 2005, 2006, 2009 Christian Grothoff (and other contributing authors) + + GNUnet is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published + by the Free Software Foundation; either version 2, 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 + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with GNUnet; see the file COPYING. If not, write to the + Free Software Foundation, Inc., 59 Temple Place - Suite 330, + Boston, MA 02111-1307, USA. +*/ +/** + * @file util/test_os_network.c + * @brief testcase for util/os_network.c + */ +#include "platform.h" +#include "gnunet_common.h" +#include "gnunet_configuration_lib.h" +#include "gnunet_os_lib.h" + +#define VERBOSE 0 + +/** + * Check if the address we got is IPv4 or IPv6 loopback (which should + * be present on all systems at all times); if so, set ok to 0 + * (success). + */ +static int +proc (void *cls, + const char *name, + int isDefault, const struct sockaddr *addr, socklen_t addrlen) +{ + int *ok = cls; + char buf[INET6_ADDRSTRLEN]; + + inet_ntop (addr->sa_family, + (addr->sa_family == AF_INET) ? + (void *) &((struct sockaddr_in *) addr)->sin_addr : + (void *) &((struct sockaddr_in6 *) addr)->sin6_addr, + buf, sizeof (buf)); + if ((0 == strcmp ("::1", buf)) || (0 == strcmp ("127.0.0.1", buf))) + *ok = 0; + return GNUNET_OK; +} + +static int +testifcs () +{ + int ret; + + ret = 1; + GNUNET_OS_network_interfaces_list (&proc, &ret); + return ret; +} + +int +main (int argc, char *argv[]) +{ + int errCnt = 0; + + GNUNET_log_setup ("test-os-network", "WARNING", NULL); + if (0 != testifcs ()) + errCnt++; + return errCnt; +} diff --git a/src/util/test_os_priority.c b/src/util/test_os_priority.c new file mode 100644 index 000000000..8284af5b8 --- /dev/null +++ b/src/util/test_os_priority.c @@ -0,0 +1,61 @@ +/* + This file is part of GNUnet. + (C) 2003, 2004, 2005, 2006, 2009 Christian Grothoff (and other contributing authors) + + GNUnet is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published + by the Free Software Foundation; either version 2, 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 + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with GNUnet; see the file COPYING. If not, write to the + Free Software Foundation, Inc., 59 Temple Place - Suite 330, + Boston, MA 02111-1307, USA. +*/ +/** + * @file util/test_os_priority.c + * @brief testcase for util/os_priority.c + */ +#include "platform.h" +#include "gnunet_common.h" +#include "gnunet_os_lib.h" + +#define VERBOSE 0 + +static int +testprio () +{ + pid_t child; + if (GNUNET_OK != + GNUNET_OS_set_process_priority (getpid (), + GNUNET_SCHEDULER_PRIORITY_DEFAULT)) + return 1; +#ifndef MINGW + child = fork (); + if (child == 0) + { + sleep (10); + exit (0); + } + if (GNUNET_OK != + GNUNET_OS_set_process_priority (child, GNUNET_SCHEDULER_PRIORITY_IDLE)) + return 1; +#endif + return 0; +} + +int +main (int argc, char *argv[]) +{ + int errCnt = 0; + + GNUNET_log_setup ("test_os_priority", "WARNING", NULL); + if (0 != testprio ()) + errCnt++; + return errCnt; +} diff --git a/src/util/test_plugin.c b/src/util/test_plugin.c new file mode 100644 index 000000000..22bf78b92 --- /dev/null +++ b/src/util/test_plugin.c @@ -0,0 +1,64 @@ +/* + This file is part of GNUnet. + (C) 2009 Christian Grothoff (and other contributing authors) + + GNUnet is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published + by the Free Software Foundation; either version 2, 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 + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with GNUnet; see the file COPYING. If not, write to the + Free Software Foundation, Inc., 59 Temple Place - Suite 330, + Boston, MA 02111-1307, USA. +*/ +/** + * @file util/test_plugin.c + * @brief testcase for plugin.c + */ +#include "platform.h" +#include "gnunet_plugin_lib.h" + +#define VERBOSE GNUNET_NO + +static int +check () +{ + void *ret; + + GNUNET_log_skip (1); + ret = GNUNET_PLUGIN_load ("libgnunet_plugin_missing", NULL); + GNUNET_log_skip (0); + if (ret != NULL) + return 1; + ret = GNUNET_PLUGIN_load ("libgnunet_plugin_test", "in"); + if (ret == NULL) + return 1; + if (0 != strcmp (ret, "Hello")) + return 2; + ret = GNUNET_PLUGIN_unload ("libgnunet_plugin_test", "out"); + if (ret == NULL) + return 3; + if (0 != strcmp (ret, "World")) + return 4; + free (ret); + return 0; +} + +int +main (int argc, char *argv[]) +{ + int ret; + + GNUNET_log_setup ("test-plugin", "WARNING", NULL); + ret = check (); + + return ret; +} + +/* end of test_plugin.c */ diff --git a/src/util/test_plugin_plug.c b/src/util/test_plugin_plug.c new file mode 100644 index 000000000..ca4bef277 --- /dev/null +++ b/src/util/test_plugin_plug.c @@ -0,0 +1,42 @@ +/* + This file is part of GNUnet. + (C) 2009 Christian Grothoff (and other contributing authors) + + GNUnet is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published + by the Free Software Foundation; either version 2, 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 + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with GNUnet; see the file COPYING. If not, write to the + Free Software Foundation, Inc., 59 Temple Place - Suite 330, + Boston, MA 02111-1307, USA. +*/ +/** + * @file util/test_plugin_plug.c + * @brief plugin for testing + */ +#include "platform.h" + +void * +libgnunet_plugin_test_init (void *arg) +{ + if (0 == strcmp (arg, "in")) + return "Hello"; + return NULL; +} + +void * +libgnunet_plugin_test_done (void *arg) +{ + if (0 == strcmp (arg, "out")) + return strdup ("World"); + return NULL; +} + +/* end of test_plugin_plug.c */ diff --git a/src/util/test_program.c b/src/util/test_program.c new file mode 100644 index 000000000..dee602e2a --- /dev/null +++ b/src/util/test_program.c @@ -0,0 +1,94 @@ +/* + This file is part of GNUnet. + (C) 2009 Christian Grothoff (and other contributing authors) + + GNUnet is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published + by the Free Software Foundation; either version 2, 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 + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with GNUnet; see the file COPYING. If not, write to the + Free Software Foundation, Inc., 59 Temple Place - Suite 330, + Boston, MA 02111-1307, USA. +*/ +/** + * @file util/test_program.c + * @brief tests for program.c + */ +#include "platform.h" +#include "gnunet_common.h" +#include "gnunet_program_lib.h" +#include "gnunet_scheduler_lib.h" +#include "gnunet_time_lib.h" + +static int setme; + +static struct GNUNET_GETOPT_CommandLineOption options[] = { + {'n', "name", NULL, "description", 0, &GNUNET_GETOPT_set_one, &setme}, + GNUNET_GETOPT_OPTION_END +}; + +/** + * Main function that will be run. + */ +static void +runner (void *cls, + struct GNUNET_SCHEDULER_Handle *sched, + char *const *args, + const char *cfgfile, struct GNUNET_CONFIGURATION_Handle *cfg) +{ + int *ok = cls; + GNUNET_assert (setme == 1); + GNUNET_assert (sched != NULL); + GNUNET_assert (0 == strcmp (args[0], "extra")); + GNUNET_assert (args[1] == NULL); + GNUNET_assert (0 == strcmp (cfgfile, "test_program_data.conf")); + + *ok = 0; +} + + +/** + * Main method, starts scheduler with task1, + * checks that "ok" is correct at the end. + */ +static int +check () +{ + int ok = 1; + char *const argv[] = { + "test_program", + "-c", + "test_program_data.conf", + "-L", + "WARNING", + "-n", + "extra", + NULL + }; + GNUNET_assert (GNUNET_OK == + GNUNET_PROGRAM_run (7, + argv, + "test_program", + "A test", options, &runner, &ok)); + return ok; +} + +int +main (int argc, char *argv[]) +{ + int ret = 0; + + GNUNET_log_setup ("test_program", "WARNING", NULL); + ret += check (); + + return ret; +} + +/* end of test_program.c */ diff --git a/src/util/test_program_data.conf b/src/util/test_program_data.conf new file mode 100644 index 000000000..e69de29bb diff --git a/src/util/test_pseudonym.c b/src/util/test_pseudonym.c new file mode 100644 index 000000000..6d31c8a07 --- /dev/null +++ b/src/util/test_pseudonym.c @@ -0,0 +1,138 @@ +/* + This file is part of GNUnet. + (C) 2005, 2006, 2008, 2009 Christian Grothoff (and other contributing authors) + + GNUnet is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published + by the Free Software Foundation; either version 2, 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 + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with GNUnet; see the file COPYING. If not, write to the + Free Software Foundation, Inc., 59 Temple Place - Suite 330, + Boston, MA 02111-1307, USA. +*/ + +/** + * @file util/test_pseudonym.c + * @brief testcase for pseudonym.c + * @author Christian Grothoff + */ + +#include "platform.h" +#include "gnunet_common.h" +#include "gnunet_container_lib.h" +#include "gnunet_crypto_lib.h" +#include "gnunet_disk_lib.h" +#include "gnunet_pseudonym_lib.h" + +#define CHECK(a) if (!(a)) { ok = GNUNET_NO; GNUNET_break(0); goto FAILURE; } + +static struct GNUNET_CONTAINER_MetaData *meta; + +static GNUNET_HashCode id1; + +static int +iter (void *cls, + const GNUNET_HashCode * + pseudonym, const struct GNUNET_CONTAINER_MetaData *md, int rating) +{ + int *ok = cls; + + if ((0 == memcmp (pseudonym, + &id1, + sizeof (GNUNET_HashCode))) && + (!GNUNET_CONTAINER_meta_data_test_equal (md, meta))) + { + *ok = GNUNET_NO; + GNUNET_break (0); + } + return GNUNET_OK; +} + +static int +noti_callback (void *cls, + const GNUNET_HashCode * + pseudonym, + const struct GNUNET_CONTAINER_MetaData *md, int rating) +{ + int *ret = cls; + (*ret)++; + return GNUNET_OK; +} + + +int +main (int argc, char *argv[]) +{ + int ok; + GNUNET_HashCode rid1; + GNUNET_HashCode id2; + GNUNET_HashCode rid2; + int old; + int newVal; + struct GNUNET_CONFIGURATION_Handle *cfg; + char *name1; + char *name2; + int notiCount; + + GNUNET_log_setup ("test-psuedonym", "WARNING", NULL); + ok = GNUNET_YES; + GNUNET_CRYPTO_random_disable_entropy_gathering (); + GNUNET_DISK_directory_remove ("/tmp/gnunet-pseudonym-test"); + cfg = GNUNET_CONFIGURATION_create (); + if (-1 == GNUNET_CONFIGURATION_parse (cfg, "test_pseudonym_data.conf")) + { + GNUNET_CONFIGURATION_destroy (cfg); + GNUNET_break (0); + return -1; + } + notiCount = 0; + GNUNET_PSEUDONYM_discovery_callback_register (cfg, + ¬i_callback, ¬iCount); + /* ACTUAL TEST CODE */ + old = GNUNET_PSEUDONYM_list_all (cfg, NULL, NULL); + meta = GNUNET_CONTAINER_meta_data_create (); + GNUNET_CONTAINER_meta_data_insert (meta, EXTRACTOR_TITLE, "test"); + GNUNET_CRYPTO_hash_create_random (&id1); + GNUNET_PSEUDONYM_add (cfg, &id1, meta); + CHECK (notiCount == 1); + GNUNET_PSEUDONYM_add (cfg, &id1, meta); + CHECK (notiCount == 2); + newVal = GNUNET_PSEUDONYM_list_all (cfg, &iter, &ok); + CHECK (old < newVal); + old = newVal; + GNUNET_CRYPTO_hash_create_random (&id2); + GNUNET_PSEUDONYM_add (cfg, &id2, meta); + CHECK (notiCount == 3); + newVal = GNUNET_PSEUDONYM_list_all (cfg, &iter, &ok); + CHECK (old < newVal); + name2 = GNUNET_PSEUDONYM_id_to_name (cfg, &id2); + CHECK (name2 != NULL); + name1 = GNUNET_PSEUDONYM_id_to_name (cfg, &id1); + CHECK (name1 != NULL); + CHECK (0 != strcmp (name1, name2)); + CHECK (GNUNET_OK == GNUNET_PSEUDONYM_name_to_id (cfg, name2, &rid2)); + CHECK (GNUNET_OK == GNUNET_PSEUDONYM_name_to_id (cfg, name1, &rid1)); + CHECK (0 == memcmp (&id1, &rid1, sizeof (GNUNET_HashCode))); + CHECK (0 == memcmp (&id2, &rid2, sizeof (GNUNET_HashCode))); + CHECK (0 == GNUNET_PSEUDONYM_rank (cfg, &id1, 0)); + CHECK (5 == GNUNET_PSEUDONYM_rank (cfg, &id1, 5)); + CHECK (-5 == GNUNET_PSEUDONYM_rank (cfg, &id1, -10)); + CHECK (0 == GNUNET_PSEUDONYM_rank (cfg, &id1, 5)); + GNUNET_free (name1); + GNUNET_free (name2); + /* END OF TEST CODE */ +FAILURE: + GNUNET_PSEUDONYM_discovery_callback_unregister (¬i_callback, ¬iCount); + GNUNET_CONTAINER_meta_data_destroy (meta); + GNUNET_CONFIGURATION_destroy (cfg); + return (ok == GNUNET_YES) ? 0 : 1; +} + +/* end of test_pseudoynm.c */ diff --git a/src/util/test_pseudonym_data.conf b/src/util/test_pseudonym_data.conf new file mode 100644 index 000000000..fd2048586 --- /dev/null +++ b/src/util/test_pseudonym_data.conf @@ -0,0 +1,8 @@ +# General settings +[client] +HOME = "/tmp/gnunet-pseudonym-test" + +[TESTING] +WEAKRANDOM = YES + + diff --git a/src/util/test_scheduler.c b/src/util/test_scheduler.c new file mode 100644 index 000000000..fe15e987b --- /dev/null +++ b/src/util/test_scheduler.c @@ -0,0 +1,263 @@ +/* + This file is part of GNUnet. + (C) 2009 Christian Grothoff (and other contributing authors) + + GNUnet is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published + by the Free Software Foundation; either version 2, 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 + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with GNUnet; see the file COPYING. If not, write to the + Free Software Foundation, Inc., 59 Temple Place - Suite 330, + Boston, MA 02111-1307, USA. +*/ +/** + * @file util/test_scheduler.c + * @brief tests for the scheduler + */ +#include "platform.h" +#include "gnunet_common.h" +#include "gnunet_scheduler_lib.h" +#include "gnunet_time_lib.h" + +#define VERBOSE GNUNET_NO + +static void +task2 (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc) +{ + int *ok = cls; + GNUNET_assert (2 == *ok); + (*ok) = 3; +} + +static void +task3 (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc) +{ + int *ok = cls; + /* t4 should be ready (albeit with lower priority) */ + GNUNET_assert (1 == GNUNET_SCHEDULER_get_load (tc->sched, + GNUNET_SCHEDULER_PRIORITY_COUNT)); + GNUNET_assert (3 == *ok); + (*ok) = 4; +} + +static void +task4 (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc) +{ + int *ok = cls; + GNUNET_assert (4 == *ok); + (*ok) = 5; +} + +static int fds[2]; + + +static void +taskWrt (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc) +{ + static char c; + int *ok = cls; + GNUNET_assert (6 == *ok); + GNUNET_assert (FD_ISSET (fds[1], tc->write_ready)); + (*ok) = 7; + GNUNET_assert (1 == WRITE (fds[1], &c, 1)); + GNUNET_break (0 == CLOSE (fds[1])); +} + + +static void +taskNeverRun (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc) +{ + GNUNET_assert (0); +} + +static void +taskLast (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc) +{ + int *ok = cls; + /* t4 should be ready (albeit with lower priority) */ + GNUNET_assert (8 == *ok); + (*ok) = 0; +} + +static void +taskRd (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc) +{ + static char c; + int *ok = cls; + GNUNET_assert (7 == *ok); + GNUNET_assert (FD_ISSET (fds[0], tc->read_ready)); + GNUNET_assert (1 == READ (fds[0], &c, 1)); + GNUNET_break (0 == CLOSE (fds[0])); + (*ok) = 8; + GNUNET_SCHEDULER_add_after (tc->sched, + GNUNET_NO, + GNUNET_SCHEDULER_PRIORITY_UI, + 0, &taskNeverRun, NULL); + GNUNET_SCHEDULER_add_after (tc->sched, + GNUNET_YES, + GNUNET_SCHEDULER_PRIORITY_IDLE, + 0, &taskLast, cls); + GNUNET_SCHEDULER_shutdown (tc->sched); +} + + +static void +task5 (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc) +{ + int *ok = cls; + GNUNET_assert (5 == *ok); + (*ok) = 6; + GNUNET_assert (0 == pipe (fds)); + GNUNET_SCHEDULER_add_read (tc->sched, + GNUNET_NO, + GNUNET_SCHEDULER_PRIORITY_DEFAULT, + GNUNET_SCHEDULER_NO_PREREQUISITE_TASK, + GNUNET_TIME_UNIT_FOREVER_REL, + fds[0], &taskRd, cls); + GNUNET_SCHEDULER_add_write (tc->sched, + GNUNET_NO, + GNUNET_SCHEDULER_PRIORITY_DEFAULT, + GNUNET_SCHEDULER_NO_PREREQUISITE_TASK, + GNUNET_TIME_UNIT_FOREVER_REL, + fds[1], &taskWrt, cls); +} + + +static void +task1 (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc) +{ + int *ok = cls; + GNUNET_SCHEDULER_TaskIdentifier t2; + GNUNET_SCHEDULER_TaskIdentifier t3; + GNUNET_SCHEDULER_TaskIdentifier t4; + + GNUNET_assert (1 == *ok); + (*ok) = 2; + /* t2 will go first -- prereq for all */ + t2 = GNUNET_SCHEDULER_add_after (tc->sched, + GNUNET_NO, + GNUNET_SCHEDULER_PRIORITY_IDLE, + GNUNET_SCHEDULER_NO_PREREQUISITE_TASK, + &task2, cls); + /* t3 will go before t4: higher priority */ + t4 = GNUNET_SCHEDULER_add_after (tc->sched, + GNUNET_NO, + GNUNET_SCHEDULER_PRIORITY_IDLE, + t2, &task4, cls); + t3 = GNUNET_SCHEDULER_add_delayed (tc->sched, + GNUNET_NO, + GNUNET_SCHEDULER_PRIORITY_DEFAULT, + t2, + GNUNET_TIME_relative_get_zero (), + &task3, cls); + /* t4 will go first: lower prio, but prereq! */ + GNUNET_SCHEDULER_add_after (tc->sched, + GNUNET_NO, + GNUNET_SCHEDULER_PRIORITY_UI, t4, &task5, cls); +} + + + +/** + * Main method, starts scheduler with task1, + * checks that "ok" is correct at the end. + */ +static int +check () +{ + int ok; + + ok = 1; + GNUNET_SCHEDULER_run (&task1, &ok); + return ok; +} + + +static void +taskSig (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc) +{ + int *ok = cls; + GNUNET_assert (1 == *ok); + *ok = 8; + GNUNET_SCHEDULER_add_after (tc->sched, + GNUNET_NO, + GNUNET_SCHEDULER_PRIORITY_UI, + 0, &taskNeverRun, NULL); + GNUNET_SCHEDULER_add_after (tc->sched, + GNUNET_YES, + GNUNET_SCHEDULER_PRIORITY_UI, + 0, &taskLast, cls); + GNUNET_break (0 == PLIBC_KILL (getpid (), SIGTERM)); +} + + +/** + * Main method, starts scheduler with task1, + * checks that "ok" is correct at the end. + */ +static int +checkSignal () +{ + int ok; + + ok = 1; + GNUNET_SCHEDULER_run (&taskSig, &ok); + return ok; +} + + + + +static void +taskCancel (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc) +{ + int *ok = cls; + + GNUNET_assert (1 == *ok); + *ok = 0; + GNUNET_SCHEDULER_cancel (tc->sched, + GNUNET_SCHEDULER_add_after (tc->sched, + GNUNET_NO, + GNUNET_SCHEDULER_PRIORITY_UI, + 0, + &taskNeverRun, NULL)); +} + + +/** + * Main method, starts scheduler with task1, + * checks that "ok" is correct at the end. + */ +static int +checkCancel () +{ + int ok; + + ok = 1; + GNUNET_SCHEDULER_run (&taskCancel, &ok); + return ok; +} + + + +int +main (int argc, char *argv[]) +{ + int ret = 0; + + GNUNET_log_setup ("test_scheduler", "WARNING", NULL); + ret += check (); + ret += checkSignal (); + ret += checkCancel (); + + return ret; +} + +/* end of test_scheduler.c */ diff --git a/src/util/test_scheduler_delay.c b/src/util/test_scheduler_delay.c new file mode 100644 index 000000000..76cbf94d8 --- /dev/null +++ b/src/util/test_scheduler_delay.c @@ -0,0 +1,107 @@ +/* + This file is part of GNUnet. + (C) 2001, 2002, 2003, 2004, 2006 Christian Grothoff (and other contributing authors) + + GNUnet is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published + by the Free Software Foundation; either version 2, 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 + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with GNUnet; see the file COPYING. If not, write to the + Free Software Foundation, Inc., 59 Temple Place - Suite 330, + Boston, MA 02111-1307, USA. +*/ +/** + * @file util/test_scheduler_delay.c + * @brief testcase for delay of scheduler, measures how + * precise the timers are. Expect values between 10 and 20 ms on + * modern machines. + */ +#include "platform.h" +#include "gnunet_common.h" +#include "gnunet_scheduler_lib.h" +#include "gnunet_time_lib.h" + +#define VERBOSE GNUNET_NO + +static struct GNUNET_TIME_Absolute target; + +static int i; + +static unsigned long long cumDelta; + +#define INCR 47 + +#define MAXV 1500 + +/** + * Signature of the main function of a task. + * + * @param cls closure + * @param tc context + */ +static void +test_task (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc) +{ + struct GNUNET_TIME_Absolute now; + + now = GNUNET_TIME_absolute_get (); + if (now.value > target.value) + cumDelta += (now.value - target.value); + else + cumDelta += (target.value - now.value); + target = + GNUNET_TIME_relative_to_absolute (GNUNET_TIME_relative_multiply + (GNUNET_TIME_UNIT_MILLISECONDS, i)); + fprintf (stderr, "."); + if (i > MAXV) + { + fprintf (stderr, "\n"); + return; + } + GNUNET_SCHEDULER_add_delayed (tc->sched, + GNUNET_NO, + GNUNET_SCHEDULER_PRIORITY_DEFAULT, + GNUNET_SCHEDULER_NO_PREREQUISITE_TASK, + GNUNET_TIME_relative_multiply + (GNUNET_TIME_UNIT_MILLISECONDS, i), + &test_task, NULL); + i += INCR; +} + +static int +check () +{ + target = GNUNET_TIME_absolute_get (); + GNUNET_SCHEDULER_run (&test_task, NULL); + FPRINTF (stdout, + "Sleep precision: %llu ms. ", cumDelta / 1000 / (MAXV / INCR)); + if (cumDelta <= 10 * MAXV / INCR) + fprintf (stdout, "Timer precision is excellent.\n"); + else if (cumDelta <= 50 * MAXV / INCR) /* 50 ms average deviation */ + fprintf (stdout, "Timer precision is good.\n"); + else if (cumDelta > 250 * MAXV / INCR) + fprintf (stdout, "Timer precision is awful.\n"); + else + fprintf (stdout, "Timer precision is acceptable.\n"); + return 0; +} + +int +main (int argc, char *argv[]) +{ + int ret; + + GNUNET_log_setup ("test-scheduler-delay", "WARNING", NULL); + ret = check (); + + return ret; +} + +/* end of test_scheduler_delay.c */ diff --git a/src/util/test_server.c b/src/util/test_server.c new file mode 100644 index 000000000..e3df0b456 --- /dev/null +++ b/src/util/test_server.c @@ -0,0 +1,287 @@ +/* + This file is part of GNUnet. + (C) 2009 Christian Grothoff (and other contributing authors) + + GNUnet is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published + by the Free Software Foundation; either version 2, 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 + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with GNUnet; see the file COPYING. If not, write to the + Free Software Foundation, Inc., 59 Temple Place - Suite 330, + Boston, MA 02111-1307, USA. +*/ +/** + * @file util/test_server.c + * @brief tests for server.c + */ +#include "platform.h" +#include "gnunet_common.h" +#include "gnunet_scheduler_lib.h" +#include "gnunet_server_lib.h" +#include "gnunet_time_lib.h" + +#define VERBOSE GNUNET_NO + +#define PORT 12435 + +#define MY_TYPE 128 +#define MY_TYPE2 129 + +static struct GNUNET_SERVER_Handle *server; + +static struct GNUNET_SCHEDULER_Handle *sched; + +static void +recv_fin_cb (void *cls, + struct GNUNET_SERVER_Handle *server, + struct GNUNET_SERVER_Client *client, + const struct GNUNET_MessageHeader *message) +{ + int *ok = cls; + GNUNET_assert (2 == *ok); + GNUNET_SERVER_receive_done (client, GNUNET_OK); + *ok = 3; +} + +struct SignalTimeoutContext +{ + GNUNET_NETWORK_Receiver cb; + void *cb_cls; +}; + + +static void +signal_timeout (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc) +{ + struct SignalTimeoutContext *stctx = cls; + + stctx->cb (stctx->cb_cls, NULL, 0, NULL, 0, 0); + GNUNET_free (stctx); +} + + +static GNUNET_SCHEDULER_TaskIdentifier +my_receive (void *cls, + size_t max, + struct GNUNET_TIME_Relative timeout, + GNUNET_NETWORK_Receiver receiver, void *receiver_cls) +{ + int *ok = cls; + struct GNUNET_MessageHeader msg; + struct SignalTimeoutContext *stctx; + GNUNET_SCHEDULER_TaskIdentifier ret; + + ret = GNUNET_SCHEDULER_NO_PREREQUISITE_TASK; + switch (*ok) + { + case 1: + *ok = 2; /* report success */ + msg.type = htons (MY_TYPE2); + msg.size = htons (sizeof (msg)); + receiver (receiver_cls, &msg, sizeof (msg), NULL, 0, 0); + break; + case 3: + /* called after first receive instantly + produced a reply; + schedule receiver call with timeout + after timeout expires! */ + *ok = 4; + stctx = GNUNET_malloc (sizeof (struct SignalTimeoutContext)); + stctx->cb = receiver; + stctx->cb_cls = receiver_cls; + ret = GNUNET_SCHEDULER_add_delayed (sched, + GNUNET_NO, + GNUNET_SCHEDULER_PRIORITY_KEEP, + GNUNET_SCHEDULER_NO_PREREQUISITE_TASK, + timeout, &signal_timeout, stctx); + break; + default: + GNUNET_assert (0); + } + return ret; +} + + +static void +my_cancel (void *cls, GNUNET_SCHEDULER_TaskIdentifier ti) +{ + GNUNET_SCHEDULER_cancel (sched, ti); +} + +static void * +my_transmit_ready_cb (void *cls, + size_t size, + struct GNUNET_TIME_Relative timeout, + GNUNET_NETWORK_TransmitReadyNotify notify, + void *notify_cls) +{ + static int non_null_addr; + int *ok = cls; + char buf[size]; + struct GNUNET_MessageHeader msg; + + GNUNET_assert (4 == *ok); + GNUNET_assert (size == sizeof (struct GNUNET_MessageHeader)); + notify (notify_cls, size, buf); + msg.type = htons (MY_TYPE); + msg.size = htons (sizeof (msg)); + GNUNET_assert (0 == memcmp (&msg, buf, size)); + *ok = 5; /* report success */ + return &non_null_addr; +} + + +static void +my_transmit_ready_cancel_cb (void *cls, void *ctx) +{ + GNUNET_assert (0); +} + + +static int +my_check (void *cls) +{ + return GNUNET_YES; +} + + +static void my_destroy (void *cls); + + +struct CopyContext +{ + struct GNUNET_SERVER_Client *client; + struct GNUNET_MessageHeader *cpy; +}; + +static size_t +copy_msg (void *cls, size_t size, void *buf) +{ + struct CopyContext *ctx = cls; + struct GNUNET_MessageHeader *cpy = ctx->cpy; + GNUNET_assert (sizeof (struct GNUNET_MessageHeader) == ntohs (cpy->size)); + GNUNET_assert (size >= ntohs (cpy->size)); + memcpy (buf, cpy, ntohs (cpy->size)); + GNUNET_free (cpy); + GNUNET_free (ctx); + return sizeof (struct GNUNET_MessageHeader); +} + + +static void +recv_cb (void *cls, + struct GNUNET_SERVER_Handle *server, + struct GNUNET_SERVER_Client *argclient, + const struct GNUNET_MessageHeader *message) +{ + struct GNUNET_SERVER_Client *client; + struct CopyContext *cc; + struct GNUNET_MessageHeader *cpy; + + GNUNET_assert (argclient == NULL); + GNUNET_assert (sizeof (struct GNUNET_MessageHeader) == + ntohs (message->size)); + GNUNET_assert (MY_TYPE == ntohs (message->type)); + client = GNUNET_SERVER_connect_callback (server, + cls, + &my_receive, + &my_cancel, + &my_transmit_ready_cb, + &my_transmit_ready_cancel_cb, + &my_check, &my_destroy); + cc = GNUNET_malloc (sizeof (struct CopyContext)); + cc->client = client; + cpy = GNUNET_malloc (ntohs (message->size)); + memcpy (cpy, message, ntohs (message->size)); + cc->cpy = cpy; + GNUNET_assert (NULL != + GNUNET_SERVER_notify_transmit_ready (client, + ntohs (message->size), + GNUNET_TIME_UNIT_SECONDS, + ©_msg, cc)); + GNUNET_SERVER_client_drop (client); +} + + +static struct GNUNET_SERVER_MessageHandler handlers[] = { + {&recv_cb, NULL, MY_TYPE, sizeof (struct GNUNET_MessageHeader)}, + {&recv_fin_cb, NULL, MY_TYPE2, sizeof (struct GNUNET_MessageHeader)}, + {NULL, NULL, 0, 0} +}; + + +static void +my_destroy (void *cls) +{ + int *ok = cls; + GNUNET_assert (5 == *ok); + *ok = 0; /* report success */ + /* this will cause us to terminate */ + GNUNET_SERVER_destroy (server); +} + + +static void +task (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc) +{ + struct sockaddr_in sa; + struct GNUNET_MessageHeader msg; + + sched = tc->sched; + memset (&sa, 0, sizeof (sa)); + sa.sin_family = AF_INET; + sa.sin_port = htons (PORT); + server = GNUNET_SERVER_create (tc->sched, + NULL, + NULL, + (const struct sockaddr *) &sa, + sizeof (sa), + 1024, + GNUNET_TIME_relative_multiply + (GNUNET_TIME_UNIT_MILLISECONDS, 250), + GNUNET_NO); + GNUNET_assert (server != NULL); + handlers[0].callback_cls = cls; + handlers[1].callback_cls = cls; + GNUNET_SERVER_add_handlers (server, handlers); + msg.type = htons (MY_TYPE); + msg.size = htons (sizeof (msg)); + GNUNET_SERVER_inject (server, NULL, &msg); + memset (&msg, 0, sizeof (msg)); +} + + +/** + * Main method, starts scheduler with task1, + * checks that "ok" is correct at the end. + */ +static int +check () +{ + int ok; + + ok = 1; + GNUNET_SCHEDULER_run (&task, &ok); + return ok; +} + + +int +main (int argc, char *argv[]) +{ + int ret = 0; + + GNUNET_log_setup ("test_server", "WARNING", NULL); + ret += check (); + + return ret; +} + +/* end of test_server.c */ diff --git a/src/util/test_server_disconnect.c b/src/util/test_server_disconnect.c new file mode 100644 index 000000000..60604b35c --- /dev/null +++ b/src/util/test_server_disconnect.c @@ -0,0 +1,241 @@ +/* + This file is part of GNUnet. + (C) 2009 Christian Grothoff (and other contributing authors) + + GNUnet is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published + by the Free Software Foundation; either version 2, 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 + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with GNUnet; see the file COPYING. If not, write to the + Free Software Foundation, Inc., 59 Temple Place - Suite 330, + Boston, MA 02111-1307, USA. +*/ +/** + * @file util/test_server_disconnect.c + * @brief tests for server.c and client.c, + * specifically client_disconnect + */ +#include "platform.h" +#include "gnunet_common.h" +#include "gnunet_scheduler_lib.h" +#include "gnunet_client_lib.h" +#include "gnunet_server_lib.h" +#include "gnunet_time_lib.h" + +#define VERBOSE GNUNET_NO + +#define PORT 22335 + +#define MY_TYPE 128 + + +static struct GNUNET_SERVER_Handle *server; + +static struct GNUNET_CLIENT_Connection *client; + +static struct GNUNET_SCHEDULER_Handle *sched; + +static struct GNUNET_CONFIGURATION_Handle *cfg; + +static int ok; + +static void +send_done (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc) +{ + struct GNUNET_SERVER_Client *argclient = cls; + GNUNET_assert (ok == 3); + ok++; + GNUNET_SERVER_receive_done (argclient, GNUNET_OK); +} + +static void +server_disconnect (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc) +{ + struct GNUNET_SERVER_Client *argclient = cls; + GNUNET_assert (ok == 5); + ok++; + GNUNET_SERVER_client_disconnect (argclient); +} + + +static void +recv_cb (void *cls, + struct GNUNET_SERVER_Handle *server, + struct GNUNET_SERVER_Client *argclient, + const struct GNUNET_MessageHeader *message) +{ + void *addr; + size_t addrlen; + struct sockaddr_in sa; + struct sockaddr_in *have; + + GNUNET_assert (GNUNET_OK == + GNUNET_SERVER_client_get_address (argclient, + &addr, &addrlen)); + + GNUNET_assert (addrlen == sizeof (struct sockaddr_in)); + have = addr; + memset (&sa, 0, sizeof (sa)); + sa.sin_family = AF_INET; + sa.sin_port = have->sin_port; + sa.sin_addr.s_addr = htonl (INADDR_LOOPBACK); + GNUNET_assert (0 == memcmp (&sa, addr, addrlen)); + GNUNET_free (addr); + switch (ok) + { + case 2: + ok++; + GNUNET_SCHEDULER_add_delayed (sched, + GNUNET_YES, + GNUNET_SCHEDULER_PRIORITY_KEEP, + GNUNET_SCHEDULER_NO_PREREQUISITE_TASK, + GNUNET_TIME_relative_multiply + (GNUNET_TIME_UNIT_MILLISECONDS, 50), + &send_done, argclient); + break; + case 4: + ok++; + GNUNET_SCHEDULER_add_delayed (sched, + GNUNET_YES, + GNUNET_SCHEDULER_PRIORITY_KEEP, + GNUNET_SCHEDULER_NO_PREREQUISITE_TASK, + GNUNET_TIME_relative_multiply + (GNUNET_TIME_UNIT_MILLISECONDS, 50), + &server_disconnect, argclient); + GNUNET_SERVER_receive_done (argclient, GNUNET_OK); + break; + default: + GNUNET_assert (0); + } + +} + +static void +disconnect_notify (void *cls, const struct GNUNET_MessageHeader *msg) +{ + GNUNET_assert (msg == NULL); + GNUNET_assert (ok == 7); + ok = 0; + GNUNET_CLIENT_disconnect (client); + GNUNET_SCHEDULER_shutdown (sched); + GNUNET_CONFIGURATION_destroy (cfg); +} + + +/** + * Functions with this signature are called whenever a client + * is disconnected on the network level. + * + * @param cls closure + * @param client identification of the client + */ +static void +notify_disconnect (void *cls, struct GNUNET_SERVER_Client *clientarg) +{ + GNUNET_assert (ok == 6); + ok++; + GNUNET_CLIENT_receive (client, + &disconnect_notify, + NULL, GNUNET_TIME_UNIT_FOREVER_REL); +} + + +static size_t +notify_ready (void *cls, size_t size, void *buf) +{ + struct GNUNET_MessageHeader *msg; + + GNUNET_assert (size >= 256); + GNUNET_assert (1 == ok); + ok++; + msg = buf; + msg->type = htons (MY_TYPE); + msg->size = htons (sizeof (struct GNUNET_MessageHeader)); + msg++; + msg->type = htons (MY_TYPE); + msg->size = htons (sizeof (struct GNUNET_MessageHeader)); + return 2 * sizeof (struct GNUNET_MessageHeader); +} + + +static struct GNUNET_SERVER_MessageHandler handlers[] = { + {&recv_cb, NULL, MY_TYPE, sizeof (struct GNUNET_MessageHeader)}, + {NULL, NULL, 0, 0} +}; + + +static void +task (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc) +{ + struct sockaddr_in sa; + + sched = tc->sched; + memset (&sa, 0, sizeof (sa)); + sa.sin_family = AF_INET; + sa.sin_port = htons (PORT); + server = GNUNET_SERVER_create (tc->sched, + NULL, + NULL, + (const struct sockaddr *) &sa, + sizeof (sa), + 1024, + GNUNET_TIME_relative_multiply + (GNUNET_TIME_UNIT_MILLISECONDS, 250), + GNUNET_NO); + GNUNET_assert (server != NULL); + handlers[0].callback_cls = cls; + GNUNET_SERVER_add_handlers (server, handlers); + GNUNET_SERVER_disconnect_notify (server, ¬ify_disconnect, cls); + cfg = GNUNET_CONFIGURATION_create (); + GNUNET_CONFIGURATION_set_value_number (cfg, "test", "PORT", PORT); + GNUNET_CONFIGURATION_set_value_string (cfg, "test", "HOSTNAME", + "localhost"); + client = GNUNET_CLIENT_connect (tc->sched, "test", cfg); + GNUNET_assert (client != NULL); + GNUNET_CLIENT_notify_transmit_ready (client, + 256, + GNUNET_TIME_relative_multiply + (GNUNET_TIME_UNIT_MILLISECONDS, 250), + ¬ify_ready, NULL); +} + + +/** + * Main method, starts scheduler with task1, + * checks that "ok" is correct at the end. + */ +static int +check () +{ + + ok = 1; + GNUNET_SCHEDULER_run (&task, NULL); + return ok; +} + + +int +main (int argc, char *argv[]) +{ + int ret = 0; + + GNUNET_log_setup ("test_server_disconnect", +#if VERBOSE + "DEBUG", +#else + "WARNING", +#endif + NULL); + ret += check (); + + return ret; +} + +/* end of test_server_disconnect.c */ diff --git a/src/util/test_server_with_client.c b/src/util/test_server_with_client.c new file mode 100644 index 000000000..4ca2b5bc3 --- /dev/null +++ b/src/util/test_server_with_client.c @@ -0,0 +1,215 @@ +/* + This file is part of GNUnet. + (C) 2009 Christian Grothoff (and other contributing authors) + + GNUnet is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published + by the Free Software Foundation; either version 2, 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 + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with GNUnet; see the file COPYING. If not, write to the + Free Software Foundation, Inc., 59 Temple Place - Suite 330, + Boston, MA 02111-1307, USA. +*/ +/** + * @file util/test_server_with_client.c + * @brief tests for server.c and client.c, + * specifically disconnect_notify, + * client_get_address and receive_done (resume processing) + */ +#include "platform.h" +#include "gnunet_common.h" +#include "gnunet_scheduler_lib.h" +#include "gnunet_client_lib.h" +#include "gnunet_server_lib.h" +#include "gnunet_time_lib.h" + +#define VERBOSE GNUNET_NO + +#define PORT 22335 + +#define MY_TYPE 128 + + +static struct GNUNET_SERVER_Handle *server; + +static struct GNUNET_CLIENT_Connection *client; + +static struct GNUNET_SCHEDULER_Handle *sched; + +static struct GNUNET_CONFIGURATION_Handle *cfg; + +static int ok; + +static void +send_done (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc) +{ + struct GNUNET_SERVER_Client *argclient = cls; + GNUNET_assert (ok == 3); + ok++; + GNUNET_SERVER_receive_done (argclient, GNUNET_OK); +} + + +static void +recv_cb (void *cls, + struct GNUNET_SERVER_Handle *server, + struct GNUNET_SERVER_Client *argclient, + const struct GNUNET_MessageHeader *message) +{ + void *addr; + size_t addrlen; + struct sockaddr_in sa; + struct sockaddr_in *have; + + GNUNET_assert (GNUNET_OK == + GNUNET_SERVER_client_get_address (argclient, + &addr, &addrlen)); + + GNUNET_assert (addrlen == sizeof (struct sockaddr_in)); + have = addr; + memset (&sa, 0, sizeof (sa)); + sa.sin_family = AF_INET; + sa.sin_port = have->sin_port; + sa.sin_addr.s_addr = htonl (INADDR_LOOPBACK); + GNUNET_assert (0 == memcmp (&sa, addr, addrlen)); + GNUNET_free (addr); + switch (ok) + { + case 2: + ok++; + GNUNET_SCHEDULER_add_delayed (sched, + GNUNET_YES, + GNUNET_SCHEDULER_PRIORITY_KEEP, + GNUNET_SCHEDULER_NO_PREREQUISITE_TASK, + GNUNET_TIME_relative_multiply + (GNUNET_TIME_UNIT_MILLISECONDS, 50), + &send_done, argclient); + break; + case 4: + ok++; + GNUNET_CLIENT_disconnect (client); + GNUNET_SERVER_receive_done (argclient, GNUNET_OK); + break; + default: + GNUNET_assert (0); + } + +} + + +/** + * Functions with this signature are called whenever a client + * is disconnected on the network level. + * + * @param cls closure + * @param client identification of the client + */ +static void +notify_disconnect (void *cls, struct GNUNET_SERVER_Client *client) +{ + GNUNET_assert (ok == 5); + ok = 0; + GNUNET_SCHEDULER_shutdown (sched); + GNUNET_CONFIGURATION_destroy (cfg); +} + + +static size_t +notify_ready (void *cls, size_t size, void *buf) +{ + struct GNUNET_MessageHeader *msg; + + GNUNET_assert (size >= 256); + GNUNET_assert (1 == ok); + ok++; + msg = buf; + msg->type = htons (MY_TYPE); + msg->size = htons (sizeof (struct GNUNET_MessageHeader)); + msg++; + msg->type = htons (MY_TYPE); + msg->size = htons (sizeof (struct GNUNET_MessageHeader)); + return 2 * sizeof (struct GNUNET_MessageHeader); +} + + +static struct GNUNET_SERVER_MessageHandler handlers[] = { + {&recv_cb, NULL, MY_TYPE, sizeof (struct GNUNET_MessageHeader)}, + {NULL, NULL, 0, 0} +}; + + +static void +task (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc) +{ + struct sockaddr_in sa; + + sched = tc->sched; + memset (&sa, 0, sizeof (sa)); + sa.sin_family = AF_INET; + sa.sin_port = htons (PORT); + server = GNUNET_SERVER_create (tc->sched, + NULL, + NULL, + (const struct sockaddr *) &sa, + sizeof (sa), + 1024, + GNUNET_TIME_relative_multiply + (GNUNET_TIME_UNIT_MILLISECONDS, 250), + GNUNET_NO); + GNUNET_assert (server != NULL); + handlers[0].callback_cls = cls; + GNUNET_SERVER_add_handlers (server, handlers); + GNUNET_SERVER_disconnect_notify (server, ¬ify_disconnect, cls); + cfg = GNUNET_CONFIGURATION_create (); + GNUNET_CONFIGURATION_set_value_number (cfg, "test", "PORT", PORT); + GNUNET_CONFIGURATION_set_value_string (cfg, "test", "HOSTNAME", + "localhost"); + client = GNUNET_CLIENT_connect (tc->sched, "test", cfg); + GNUNET_assert (client != NULL); + GNUNET_CLIENT_notify_transmit_ready (client, + 256, + GNUNET_TIME_relative_multiply + (GNUNET_TIME_UNIT_MILLISECONDS, 250), + ¬ify_ready, NULL); +} + + +/** + * Main method, starts scheduler with task1, + * checks that "ok" is correct at the end. + */ +static int +check () +{ + + ok = 1; + GNUNET_SCHEDULER_run (&task, NULL); + return ok; +} + + +int +main (int argc, char *argv[]) +{ + int ret = 0; + + GNUNET_log_setup ("test_server_with_client", +#if VERBOSE + "DEBUG", +#else + "WARNING", +#endif + NULL); + ret += check (); + + return ret; +} + +/* end of test_server_with_client.c */ diff --git a/src/util/test_service.c b/src/util/test_service.c new file mode 100644 index 000000000..b5cf0805e --- /dev/null +++ b/src/util/test_service.c @@ -0,0 +1,337 @@ +/* + This file is part of GNUnet. + (C) 2009 Christian Grothoff (and other contributing authors) + + GNUnet is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published + by the Free Software Foundation; either version 2, 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 + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with GNUnet; see the file COPYING. If not, write to the + Free Software Foundation, Inc., 59 Temple Place - Suite 330, + Boston, MA 02111-1307, USA. +*/ +/** + * @file util/test_service.c + * @brief tests for service.c + */ +#include "platform.h" +#include "gnunet_common.h" +#include "gnunet_client_lib.h" +#include "gnunet_getopt_lib.h" +#include "gnunet_program_lib.h" +#include "gnunet_service_lib.h" +#include "gnunet_scheduler_lib.h" +#include "gnunet_time_lib.h" + +#define VERBOSE GNUNET_NO + +#define PORT 12435 + +#define MY_TYPE 256 + +static struct GNUNET_SCHEDULER_Handle *sched; + +static struct GNUNET_SERVICE_Context *sctx; + +static void +end_it (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc) +{ + struct GNUNET_CLIENT_Connection *client = cls; + + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Shutting down service\n"); + GNUNET_CLIENT_service_shutdown (client); + GNUNET_CLIENT_disconnect (client); + if (sctx != NULL) + GNUNET_SERVICE_stop (sctx); +} + + +static size_t +build_msg (void *cls, size_t size, void *buf) +{ + struct GNUNET_CLIENT_Connection *client = cls; + struct GNUNET_MessageHeader *msg = buf; + + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Client connected, transmitting\n"); + GNUNET_assert (size >= sizeof (struct GNUNET_MessageHeader)); + msg->type = htons (MY_TYPE); + msg->size = htons (sizeof (msg)); + GNUNET_SCHEDULER_add_continuation (sched, + GNUNET_YES, + &end_it, + client, + GNUNET_SCHEDULER_REASON_PREREQ_DONE); + return sizeof (struct GNUNET_MessageHeader); +} + +static void +ready (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc) +{ + struct GNUNET_CONFIGURATION_Handle *cfg = cls; + struct GNUNET_CLIENT_Connection *client; + + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Service confirmed running\n"); + sched = tc->sched; + GNUNET_assert (0 != (tc->reason & GNUNET_SCHEDULER_REASON_PREREQ_DONE)); + client = GNUNET_CLIENT_connect (tc->sched, "test_service", cfg); + GNUNET_assert (client != NULL); + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Client connecting, waiting to transmit\n"); + GNUNET_CLIENT_notify_transmit_ready (client, + sizeof (struct GNUNET_MessageHeader), + GNUNET_TIME_UNIT_SECONDS, + &build_msg, client); +} + +static void +recv_cb (void *cls, + struct GNUNET_SERVER_Handle *server, + struct GNUNET_SERVER_Client *client, + const struct GNUNET_MessageHeader *message) +{ + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Receiving client message...\n"); + GNUNET_SERVER_receive_done (client, GNUNET_OK); +} + +static struct GNUNET_SERVER_MessageHandler myhandlers[] = { + {&recv_cb, NULL, MY_TYPE, sizeof (struct GNUNET_MessageHeader)}, + {NULL, NULL, 0, 0} +}; + +static void +runner (void *cls, + struct GNUNET_SCHEDULER_Handle *sched, + struct GNUNET_SERVER_Handle *server, + struct GNUNET_CONFIGURATION_Handle *cfg) +{ + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Service initializing\n"); + GNUNET_SERVER_add_handlers (server, myhandlers); + GNUNET_CLIENT_service_test (sched, + "test_service", + cfg, GNUNET_TIME_UNIT_SECONDS, &ready, cfg); +} + +static void +term (void *cls, struct GNUNET_CONFIGURATION_Handle *cfg) +{ + int *ok = cls; + *ok = 0; +} + +/** + * Main method, starts scheduler with task1, + * checks that "ok" is correct at the end. + */ +static int +check () +{ + int ok = 1; + char *const argv[] = { + "test_service", + "-c", + "test_service_data.conf", + "-L", +#if VERBOSE + "DEBUG", +#else + "WARNING", +#endif + NULL + }; + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Starting service\n"); + GNUNET_assert (GNUNET_OK == + GNUNET_SERVICE_run (5, + argv, + "test_service", + &runner, &ok, &term, &ok)); + GNUNET_assert (0 == ok); + return ok; +} + +static void +ready6 (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc) +{ + struct GNUNET_CONFIGURATION_Handle *cfg = cls; + struct GNUNET_CLIENT_Connection *client; + + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "V6 ready\n"); + sched = tc->sched; + GNUNET_assert (0 != (tc->reason & GNUNET_SCHEDULER_REASON_PREREQ_DONE)); + client = GNUNET_CLIENT_connect (tc->sched, "test_service6", cfg); + GNUNET_assert (client != NULL); + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "V6 client connected\n"); + GNUNET_CLIENT_notify_transmit_ready (client, + sizeof (struct GNUNET_MessageHeader), + GNUNET_TIME_UNIT_SECONDS, + &build_msg, client); +} + +static void +runner6 (void *cls, + struct GNUNET_SCHEDULER_Handle *sched, + struct GNUNET_SERVER_Handle *server, + struct GNUNET_CONFIGURATION_Handle *cfg) +{ + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Initializing v6 service\n"); + GNUNET_SERVER_add_handlers (server, myhandlers); + GNUNET_CLIENT_service_test (sched, + "test_service6", + cfg, GNUNET_TIME_UNIT_SECONDS, &ready6, cfg); +} + +/** + * Main method, starts scheduler with task1, + * checks that "ok" is correct at the end. + */ +static int +check6 () +{ + int ok = 1; + char *const argv[] = { + "test_service6", + "-c", + "test_service_data.conf", + "-L", +#if VERBOSE + "DEBUG", +#else + "WARNING", +#endif + NULL + }; + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Starting v6 service\n"); + GNUNET_assert (GNUNET_OK == + GNUNET_SERVICE_run (5, + argv, + "test_service6", + &runner6, &ok, &term, &ok)); + GNUNET_assert (0 == ok); + return ok; +} + + +/** + * Main method, starts scheduler with task1, + * checks that "ok" is correct at the end. + */ +static int +check6d () +{ + int ok = 1; + char *const argv[] = { + "test_service6", + "-c", + "test_service_data.conf", + "-L", +#if VERBOSE + "DEBUG", +#else + "WARNING", +#endif + "-d", + NULL + }; + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Starting V6 as daemon\n"); + GNUNET_assert (GNUNET_OK == + GNUNET_SERVICE_run (6, + argv, + "test_service6", + &runner6, &ok, &term, &ok)); + GNUNET_break (0 == ok); + return ok; +} + + +static void +start_stop_main (void *cls, + struct GNUNET_SCHEDULER_Handle *sched, + char *const *args, + const char *cfgfile, struct GNUNET_CONFIGURATION_Handle *cfg) +{ + int *ret = cls; + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Starting service using start method\n"); + sctx = GNUNET_SERVICE_start ("test_service", sched, cfg); + runner (cls, sched, GNUNET_SERVICE_get_server (sctx), cfg); + *ret = 0; +} + + +static int +check_start_stop () +{ + char *const argv[] = { + "test-service-program", + "-c", + "test_service_data.conf", + "-L", +#if VERBOSE + "DEBUG", +#else + "WARNING", +#endif + NULL + }; + const struct GNUNET_GETOPT_CommandLineOption options[] = { + GNUNET_GETOPT_OPTION_END + }; + int ret = 1; + GNUNET_assert (GNUNET_OK == + GNUNET_PROGRAM_run (5, + argv, + "test-service-program", + "no help", + options, &start_stop_main, &ret)); + + GNUNET_break (0 == ret); + return ret; +} + + +int +main (int argc, char *argv[]) +{ + int ret = 0; + int s; + + GNUNET_log_setup ("test-service", +#if VERBOSE + "DEBUG", +#else + "WARNING", +#endif + NULL); + ret += check (); + ret += check (); + s = SOCKET (PF_INET6, SOCK_STREAM, 0); + if (s == -1) + { + if ((errno == ENOBUFS) || + (errno == ENOMEM) || (errno == ENFILE) || (errno == EACCES)) + { + GNUNET_log_strerror (GNUNET_ERROR_TYPE_ERROR, "socket"); + return 1; + } + fprintf (stderr, + "IPv6 support seems to not be available (%s), not testing it!\n", + strerror (errno)); + } + else + { + GNUNET_break (0 == CLOSE (s)); + ret += check6 (); + ret += check6d (); /* with daemonization */ + } + ret += check_start_stop (); + + return ret; +} + +/* end of test_service.c */ diff --git a/src/util/test_service_data.conf b/src/util/test_service_data.conf new file mode 100644 index 000000000..92c723619 --- /dev/null +++ b/src/util/test_service_data.conf @@ -0,0 +1,26 @@ +[test_service] +PORT=12435 +BINDTO=localhost +PIDFILE=/tmp/test-service.pid +TIMEOUT=30000 +MAXBUF=1024 +DISABLEV6=NO +ACCEPT_FROM=127.0.0.1; +REJECT_FROM=1.2.3.0/15;4.5.0.0/8;6.7.8.9/255.255.255.0; +ACCEPT_FROM6=::1; +REJECT_FROM6=AB:CD:EF::00;AB:CD::00/40; +HOSTNAME=localhost +ALLOW_SHUTDOWN=YES + +[test_service6] +PORT=12435 +PIDFILE=/tmp/test-service.pid +TIMEOUT=30000 +MAXBUF=1024 +DISABLEV6=NO +ACCEPT_FROM=127.0.0.1; +REJECT_FROM=1.2.3.0/15;4.5.0.0/8;6.7.8.9/255.255.255.0; +ACCEPT_FROM6=::1; +REJECT_FROM6=AB:CD:EF::00;AB:CD::00/40; +HOSTNAME=::1 +ALLOW_SHUTDOWN=YES diff --git a/src/util/test_strings.c b/src/util/test_strings.c new file mode 100644 index 000000000..be166e629 --- /dev/null +++ b/src/util/test_strings.c @@ -0,0 +1,111 @@ +/* + This file is part of GNUnet. + (C) 2009 Christian Grothoff (and other contributing authors) + + GNUnet is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published + by the Free Software Foundation; either version 2, 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 + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with GNUnet; see the file COPYING. If not, write to the + Free Software Foundation, Inc., 59 Temple Place - Suite 330, + Boston, MA 02111-1307, USA. +*/ +/** + * @file util/test_strings.c + * @brief testcase for strings.c + */ +#include "platform.h" +#include "gnunet_common.h" +#include "gnunet_strings_lib.h" + +#define VERBOSE GNUNET_NO + +#define WANT(a,b) if (0 != strcmp(a,b)) { fprintf(stderr, "Got `%s', wanted `%s'\n", b, a); GNUNET_free(b); GNUNET_break(0); return 1;} else { GNUNET_free (b); } +#define WANTB(a,b,l) if (0 != memcmp(a,b,l)) { GNUNET_break(0); return 1;} else { } + +static int +check () +{ + char buf[128]; + char *r; + char *b; + struct GNUNET_TIME_Absolute at; + + sprintf (buf, "4%s", _( /* size unit */ "b")); + b = GNUNET_STRINGS_byte_size_fancy (4); + WANT (buf, b); + sprintf (buf, "10%s", _( /* size unit */ "KiB")); + b = GNUNET_STRINGS_byte_size_fancy (10240); + WANT (buf, b); + sprintf (buf, "10%s", _( /* size unit */ "TiB")); + b = GNUNET_STRINGS_byte_size_fancy (10240LL * 1024LL * 1024LL * 1024LL); + WANT (buf, b); + sprintf (buf, "4%s", _( /* time unit */ "ms")); + b = + GNUNET_STRINGS_relative_time_to_string (GNUNET_TIME_relative_multiply + (GNUNET_TIME_UNIT_MILLISECONDS, + 4)); + WANT (buf, b); + sprintf (buf, "7%s", _( /* time unit */ "s")); + b = + GNUNET_STRINGS_relative_time_to_string (GNUNET_TIME_relative_multiply + (GNUNET_TIME_UNIT_MILLISECONDS, + 7 * 1000)); + WANT (buf, b); + sprintf (buf, "7%s", _( /* time unit */ "h")); + b = + GNUNET_STRINGS_relative_time_to_string (GNUNET_TIME_relative_multiply + (GNUNET_TIME_UNIT_MILLISECONDS, + 7 * 60 * 60 * 1000)); + WANT (buf, b); + sprintf (buf, "%s%s", getenv ("HOME"), DIR_SEPARATOR_STR); + b = GNUNET_STRINGS_filename_expand ("~"); + WANT (buf, b); + GNUNET_STRINGS_buffer_fill (buf, sizeof (buf), 3, "a", "btx", "c"); + WANTB ("a\0btx\0c", buf, 8); + if (6 != GNUNET_STRINGS_buffer_tokenize (buf, sizeof (buf), 2, &r, &b)) + return 1; + r = GNUNET_strdup (r); + WANT ("a", r); + b = GNUNET_strdup (b); + WANT ("btx", b); + if (0 != GNUNET_STRINGS_buffer_tokenize (buf, 2, 2, &r, &b)) + return 1; + at.value = 5000; + r = GNUNET_STRINGS_absolute_time_to_string (at); + /* r should be something like "Wed Dec 31 17:00:05 1969" + where the details of the day and hour depend on the timezone; + however, the "0:05 19" should always be there; hence: */ + if (NULL == strstr (r, "0:05 19")) + { + fprintf (stderr, "Got %s\n", r); + GNUNET_break (0); + GNUNET_free (r); + return 1; + } + b = GNUNET_STRINGS_to_utf8 ("TEST", 4, "ASCII"); + WANT ("TEST", b); + b = GNUNET_STRINGS_to_utf8 ("TEST", 4, "unknown"); + WANT ("TEST", b); + GNUNET_free (r); + return 0; +} + +int +main (int argc, char *argv[]) +{ + int ret; + + GNUNET_log_setup ("test_strings", "ERROR", NULL); + ret = check (); + return ret; +} + +/* end of test_strings.c */ diff --git a/src/util/test_time.c b/src/util/test_time.c new file mode 100644 index 000000000..9505952c2 --- /dev/null +++ b/src/util/test_time.c @@ -0,0 +1,122 @@ +/* + This file is part of GNUnet. + (C) 2001, 2002, 2003, 2004, 2006, 2009 Christian Grothoff (and other contributing authors) + + GNUnet is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published + by the Free Software Foundation; either version 2, 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 + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with GNUnet; see the file COPYING. If not, write to the + Free Software Foundation, Inc., 59 Temple Place - Suite 330, + Boston, MA 02111-1307, USA. +*/ +/** + * @file util/test_time.c + * @brief testcase for time.c + */ +#include "platform.h" +#include "gnunet_common.h" +#include "gnunet_time_lib.h" + +#define VERBOSE GNUNET_NO + +static int +check () +{ + struct GNUNET_TIME_Absolute now; + struct GNUNET_TIME_AbsoluteNBO nown; + struct GNUNET_TIME_Absolute future; + struct GNUNET_TIME_Absolute past; + struct GNUNET_TIME_Absolute last; + struct GNUNET_TIME_Relative rel; + struct GNUNET_TIME_RelativeNBO reln; + unsigned int i; + + last = now = GNUNET_TIME_absolute_get (); + while (now.value == last.value) + now = GNUNET_TIME_absolute_get (); + GNUNET_assert (now.value > last.value); + + /* test overflow checking in multiply */ + rel = GNUNET_TIME_UNIT_SECONDS; + GNUNET_log_skip (1); + for (i = 0; i < 55; i++) + rel = GNUNET_TIME_relative_multiply (rel, 2); + GNUNET_log_skip (0); + GNUNET_assert (rel.value == GNUNET_TIME_UNIT_FOREVER_REL.value); + + /* test infinity-check for relative to absolute */ + last = GNUNET_TIME_relative_to_absolute (rel); + GNUNET_assert (last.value == GNUNET_TIME_UNIT_FOREVER_ABS.value); + + /* check overflow for r2a */ + rel.value = ((uint64_t) - 1LL) - 1024; + GNUNET_log_skip (1); + last = GNUNET_TIME_relative_to_absolute (rel); + GNUNET_log_skip (0); + GNUNET_assert (last.value == GNUNET_TIME_UNIT_FOREVER_ABS.value); + + /* check overflow for relative add */ + GNUNET_log_skip (1); + rel = GNUNET_TIME_relative_add (rel, rel); + GNUNET_log_skip (0); + GNUNET_assert (rel.value == GNUNET_TIME_UNIT_FOREVER_REL.value); + + /* check relation check in get_duration */ + future.value = now.value + 1000000; + GNUNET_assert (GNUNET_TIME_absolute_get_difference (now, future).value == + 1000000); + GNUNET_assert (GNUNET_TIME_absolute_get_difference (future, now).value == + 0); + + past.value = now.value - 1000000; + rel = GNUNET_TIME_absolute_get_duration (future); + GNUNET_assert (rel.value == 0); + rel = GNUNET_TIME_absolute_get_duration (past); + GNUNET_assert (rel.value >= 1000000); + + /* check get remaining */ + rel = GNUNET_TIME_absolute_get_remaining (now); + GNUNET_assert (rel.value == 0); + rel = GNUNET_TIME_absolute_get_remaining (past); + GNUNET_assert (rel.value == 0); + rel = GNUNET_TIME_absolute_get_remaining (future); + GNUNET_assert (rel.value > 0); + GNUNET_assert (rel.value <= 1000000); + + /* check endianess */ + reln = GNUNET_TIME_relative_hton (rel); + GNUNET_assert (rel.value == GNUNET_TIME_relative_ntoh (reln).value); + nown = GNUNET_TIME_absolute_hton (now); + GNUNET_assert (now.value == GNUNET_TIME_absolute_ntoh (nown).value); + + /* check absolute addition */ + future = GNUNET_TIME_absolute_add (now, GNUNET_TIME_UNIT_SECONDS); + GNUNET_assert (future.value == now.value + 1000); + + /* check zero */ + future = GNUNET_TIME_absolute_add (now, GNUNET_TIME_UNIT_ZERO); + GNUNET_assert (future.value == now.value); + + return 0; +} + +int +main (int argc, char *argv[]) +{ + int ret; + + GNUNET_log_setup ("test-time", "WARNING", NULL); + ret = check (); + + return ret; +} + +/* end of test_time.c */ diff --git a/src/util/time.c b/src/util/time.c new file mode 100644 index 000000000..3ae472561 --- /dev/null +++ b/src/util/time.c @@ -0,0 +1,286 @@ +/* + This file is part of GNUnet. + (C) 2001, 2002, 2006, 2009 Christian Grothoff (and other contributing authors) + + GNUnet is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published + by the Free Software Foundation; either version 2, 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 + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with GNUnet; see the file COPYING. If not, write to the + Free Software Foundation, Inc., 59 Temple Place - Suite 330, + Boston, MA 02111-1307, USA. +*/ + +/** + * @file util/time.c + * @author Christian Grothoff + * @brief functions for handling time and time arithmetic + */ +#include "platform.h" +#include "gnunet_time_lib.h" + + +/** + * Get the current time (works just as "time", just that we use the + * unit of time that the cron-jobs use (and is 64 bit)). + * + * @return the current time + */ +struct GNUNET_TIME_Absolute +GNUNET_TIME_absolute_get () +{ + struct GNUNET_TIME_Absolute ret; + struct timeval tv; + + gettimeofday (&tv, NULL); + ret.value = tv.tv_sec * 1000 + tv.tv_usec / 1000; + return ret; +} + + +/** + * Return relative time of 0ms. + */ +struct GNUNET_TIME_Relative +GNUNET_TIME_relative_get_zero () +{ + static struct GNUNET_TIME_Relative zero; + return zero; +} + +/** + * Return relative time of 1ms. + */ +struct GNUNET_TIME_Relative +GNUNET_TIME_relative_get_unit () +{ + static struct GNUNET_TIME_Relative one = { 1 }; + return one; +} + +/** + * Return "forever". + */ +struct GNUNET_TIME_Relative +GNUNET_TIME_relative_get_forever () +{ + static struct GNUNET_TIME_Relative forever = { (uint64_t) - 1LL }; + return forever; +} + +/** + * Return "forever". + */ +struct GNUNET_TIME_Absolute +GNUNET_TIME_absolute_get_forever () +{ + static struct GNUNET_TIME_Absolute forever = { (uint64_t) - 1LL }; + return forever; +} + +/** + * Convert relative time to an absolute time in the + * future. + * + * @return timestamp that is "rel" in the future, or FOREVER if rel==FOREVER (or if we would overflow) + */ +struct GNUNET_TIME_Absolute +GNUNET_TIME_relative_to_absolute (struct GNUNET_TIME_Relative rel) +{ + struct GNUNET_TIME_Absolute ret; + if (rel.value == (uint64_t) - 1LL) + return GNUNET_TIME_absolute_get_forever (); + struct GNUNET_TIME_Absolute now = GNUNET_TIME_absolute_get (); + if (rel.value + now.value < rel.value) + { + GNUNET_break (0); /* overflow... */ + return GNUNET_TIME_absolute_get_forever (); + } + ret.value = rel.value + now.value; + return ret; +} + +/** + * Given a timestamp in the future, how much time + * remains until then? + * + * @return future - now, or 0 if now >= future, or FOREVER if future==FOREVER. + */ +struct GNUNET_TIME_Relative +GNUNET_TIME_absolute_get_remaining (struct GNUNET_TIME_Absolute future) +{ + struct GNUNET_TIME_Relative ret; + if (future.value == (uint64_t) - 1LL) + return GNUNET_TIME_relative_get_forever (); + struct GNUNET_TIME_Absolute now = GNUNET_TIME_absolute_get (); + if (now.value > future.value) + return GNUNET_TIME_relative_get_zero (); + ret.value = future.value - now.value; + return ret; +} + +/** + * Compute the time difference between the given start and end times. + * Use this function instead of actual subtraction to ensure that + * "FOREVER" and overflows are handeled correctly. + * + * @return 0 if start >= end; FOREVER if end==FOREVER; otherwise end - start + */ +struct GNUNET_TIME_Relative +GNUNET_TIME_absolute_get_difference (struct GNUNET_TIME_Absolute start, + struct GNUNET_TIME_Absolute end) +{ + struct GNUNET_TIME_Relative ret; + if (end.value == (uint64_t) - 1LL) + return GNUNET_TIME_relative_get_forever (); + if (end.value < start.value) + return GNUNET_TIME_relative_get_zero (); + ret.value = end.value - start.value; + return ret; +} + +/** + * Get the duration of an operation as the + * difference of the current time and the given start time "hence". + * + * @return aborts if hence==FOREVER, 0 if hence > now, otherwise now-hence. + */ +struct GNUNET_TIME_Relative +GNUNET_TIME_absolute_get_duration (struct GNUNET_TIME_Absolute hence) +{ + struct GNUNET_TIME_Absolute now; + struct GNUNET_TIME_Relative ret; + + now = GNUNET_TIME_absolute_get (); + GNUNET_assert (hence.value != (uint64_t) - 1LL); + if (hence.value > now.value) + return GNUNET_TIME_relative_get_zero (); + ret.value = now.value - hence.value; + return ret; +} + + +/** + * Add a given relative duration to the + * given start time. + * + * @return FOREVER if either argument is FOREVER or on overflow; start+duration otherwise + */ +struct GNUNET_TIME_Absolute +GNUNET_TIME_absolute_add (struct GNUNET_TIME_Absolute start, + struct GNUNET_TIME_Relative duration) +{ + struct GNUNET_TIME_Absolute ret; + + if ((start.value == (uint64_t) - 1LL) || + (duration.value == (uint64_t) - 1LL)) + return GNUNET_TIME_absolute_get_forever (); + if (start.value + duration.value < start.value) + { + GNUNET_break (0); + return GNUNET_TIME_absolute_get_forever (); + } + ret.value = start.value + duration.value; + return ret; +} + +/** + * Multiply relative time by a given factor. + * + * @return FOREVER if rel=FOREVER or on overflow; otherwise rel*factor + */ +struct GNUNET_TIME_Relative +GNUNET_TIME_relative_multiply (struct GNUNET_TIME_Relative rel, + unsigned int factor) +{ + struct GNUNET_TIME_Relative ret; + if (factor == 0) + return GNUNET_TIME_relative_get_zero (); + ret.value = rel.value * factor; + if (ret.value / factor != rel.value) + { + GNUNET_break (0); + return GNUNET_TIME_relative_get_forever (); + } + return ret; +} + +/** + * Add relative times together. + * + * @return FOREVER if either argument is FOREVER or on overflow; a1+a2 otherwise + */ +struct GNUNET_TIME_Relative +GNUNET_TIME_relative_add (struct GNUNET_TIME_Relative a1, + struct GNUNET_TIME_Relative a2) +{ + struct GNUNET_TIME_Relative ret; + + if ((a1.value == (uint64_t) - 1LL) || (a2.value == (uint64_t) - 1LL)) + return GNUNET_TIME_relative_get_forever (); + if (a1.value + a2.value < a1.value) + { + GNUNET_break (0); + return GNUNET_TIME_relative_get_forever (); + } + ret.value = a1.value + a2.value; + return ret; +} + + +/** + * Convert relative time to network byte order. + */ +struct GNUNET_TIME_RelativeNBO +GNUNET_TIME_relative_hton (struct GNUNET_TIME_Relative a) +{ + struct GNUNET_TIME_RelativeNBO ret; + ret.value = GNUNET_htonll (a.value); + return ret; +} + +/** + * Convert relative time from network byte order. + */ +struct GNUNET_TIME_Relative +GNUNET_TIME_relative_ntoh (struct GNUNET_TIME_RelativeNBO a) +{ + struct GNUNET_TIME_Relative ret; + ret.value = GNUNET_ntohll (a.value); + return ret; + +} + +/** + * Convert absolute time to network byte order. + */ +struct GNUNET_TIME_AbsoluteNBO +GNUNET_TIME_absolute_hton (struct GNUNET_TIME_Absolute a) +{ + struct GNUNET_TIME_AbsoluteNBO ret; + ret.value = GNUNET_htonll (a.value); + return ret; +} + +/** + * Convert absolute time from network byte order. + */ +struct GNUNET_TIME_Absolute +GNUNET_TIME_absolute_ntoh (struct GNUNET_TIME_AbsoluteNBO a) +{ + struct GNUNET_TIME_Absolute ret; + ret.value = GNUNET_ntohll (a.value); + return ret; + +} + + + +/* end of time.c */ -- cgit v1.2.3